pax_global_header00006660000000000000000000000064140410531100014477gustar00rootroot0000000000000052 comment=3e79cbcbea4480a43dc6052fb0fc118c356fb404 muse-4.0.0/000077500000000000000000000000001404105311000124515ustar00rootroot00000000000000muse-4.0.0/.github/000077500000000000000000000000001404105311000140115ustar00rootroot00000000000000muse-4.0.0/.github/workflows/000077500000000000000000000000001404105311000160465ustar00rootroot00000000000000muse-4.0.0/.github/workflows/appimage_latest.yml000066400000000000000000000141731404105311000217360ustar00rootroot00000000000000name: Create AppImage latest #on: workflow_dispatch on: push: branches: - master #on: # schedule: # - cron: '0 7 * * MON' env: BUILD_TYPE: Release jobs: build: runs-on: ubuntu-16.04 steps: - uses: actions/checkout@v2 - name: Install Deps run: | sudo add-apt-repository ppa:beineri/opt-qt-5.15.2-xenial sudo apt-get update sudo apt-get install libsndfile1-dev libsamplerate0-dev libjack-jackd2-dev ladspa-sdk liblo-dev dssi-dev \ libsamplerate0-dev libsndfile1-dev libfluidsynth-dev libgtkmm-2.4-dev \ libinstpatch-dev liblash-compat-dev librubberband-dev python3-dev # librtaudio-dev # liblilv-dev lv2-dev liblrdf0-dev # sudo apt-get install qt5-default qttools5-dev qttools5-dev-tools libqt5svg5-dev sudo apt-get install mesa-common-dev libglu1-mesa-dev sudo apt-get install qt515base qt515tools qt515svg sudo apt-get remove libsord-0-0 libserd-0-0 wget http://mirrors.kernel.org/ubuntu/pool/universe/r/rtaudio/librtaudio6_5.0.0~ds-2_amd64.deb wget http://mirrors.kernel.org/ubuntu/pool/universe/r/rtaudio/librtaudio-dev_5.0.0~ds-2_amd64.deb sudo apt-get install ./librtaudio6_5.0.0~ds-2_amd64.deb ./librtaudio-dev_5.0.0~ds-2_amd64.deb - name: Build lv2 run: | mkdir ${{runner.workspace}}/lv2_build cd ${{runner.workspace}}/lv2_build git clone --depth 1 --branch v1.18.0 https://gitlab.com/lv2/lv2.git . git submodule update --init --recursive ./waf configure ./waf sudo ./waf install - name: Build serd run: | mkdir ${{runner.workspace}}/serd_build cd ${{runner.workspace}}/serd_build git clone --depth 1 --branch v0.30.6 https://gitlab.com/drobilla/serd.git . git submodule update --init --recursive ./waf configure ./waf sudo ./waf install - name: Build sord run: | mkdir ${{runner.workspace}}/sord_build cd ${{runner.workspace}}/sord_build git clone --depth 1 --branch v0.16.6 https://gitlab.com/drobilla/sord.git . git submodule update --init --recursive ./waf configure ./waf sudo ./waf install - name: Build sratom run: | mkdir ${{runner.workspace}}/sratom_build cd ${{runner.workspace}}/sratom_build git clone --depth 1 --branch v0.6.6 https://gitlab.com/lv2/sratom.git . git submodule update --init --recursive ./waf configure ./waf sudo ./waf install - name: Build lilv run: | mkdir ${{runner.workspace}}/lilv_build cd ${{runner.workspace}}/lilv_build git clone --depth 1 --branch v0.24.10 https://gitlab.com/lv2/lilv.git . git submodule update --init --recursive ./waf configure ./waf sudo ./waf install - name: Check Qt Installation run: | #cat /opt/qt515/bin/qt515-env.sh QT_BASE_DIR=/opt/qt515 export QTDIR=$QT_BASE_DIR export PATH=$QT_BASE_DIR/bin:$PATH export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib:$LD_LIBRARY_PATH export PKG_CONFIG_PATH=$QT_BASE_DIR/lib/pkgconfig:$PKG_CONFIG_PATH echo "$PATH" >> $GITHUB_PATH echo "QT_BASE_DIR=$QT_BASE_DIR" >> $GITHUB_ENV echo "QTDIR=$QT_BASE_DIR" >> $GITHUB_ENV echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $GITHUB_ENV echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH" >> $GITHUB_ENV - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure CMake working-directory: ${{runner.workspace}}/build run: | REPO_PATH=$GITHUB_WORKSPACE/src cmake $REPO_PATH -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_INSTALL_PREFIX=/usr -DMODULES_BUILD_STATIC=ON - name: Build working-directory: ${{runner.workspace}}/build run: cmake --build . --config $BUILD_TYPE -j3 - name: Create AppImage working-directory: ${{runner.workspace}}/build run: | make DESTDIR=appdir install ; find appdir/ find appdir \( -name "moc_*" -or -name "*.o" -or -name "qrc_*" -or -name "Makefile*" -or -name "*.a" \) -exec rm {} \; wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage wget https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage chmod +x linuxdeploy*.AppImage export MUSEDIR=$(basename -a ${{runner.workspace}}/build/appdir/usr/lib/muse-*) LIB_MODULES=${{runner.workspace}}/build/appdir/usr/lib/$MUSEDIR/modules LIB_PLUGINS=${{runner.workspace}}/build/appdir/usr/lib/$MUSEDIR/plugins LIB_SYNTHI=${{runner.workspace}}/build/appdir/usr/lib/$MUSEDIR/synthi LIB_CONV=${{runner.workspace}}/build/appdir/usr/lib/$MUSEDIR/converters export LD_LIBRARY_PATH=$LIB_MODULES:$LIB_PLUGINS:$LIB_SYNTHI:$LIB_CONV:$LD_LIBRARY_PATH ./linuxdeploy-x86_64.AppImage --appdir appdir --plugin qt echo '#! /usr/bin/env bash' > appdir/AppRun echo 'set -e' >> appdir/AppRun echo 'this_dir=$(readlink -f $(dirname "$0"))' >> appdir/AppRun echo 'export LD_LIBRARY_PATH="$this_dir"/usr/lib:$LD_LIBRARY_PATH' >> appdir/AppRun echo 'exec "$this_dir"/AppRun.wrapped "$@"' >> appdir/AppRun export VERSION=master #linuxdeployqt uses this for naming the file ./linuxdeploy-plugin-appimage-x86_64.AppImage --appdir appdir - name: Upload AppImage uses: "marvinpinto/action-automatic-releases@latest" with: repo_token: "${{ secrets.GITHUB_TOKEN }}" automatic_release_tag: "latest" prerelease: true title: "Current master (AppImage)" files: ${{runner.workspace}}/build/MusE*.AppImage muse-4.0.0/.github/workflows/appimage_release.yml000066400000000000000000000137461404105311000220670ustar00rootroot00000000000000name: Create AppImage for release on: workflow_dispatch: inputs: tag: description: 'Release tag' default: '4.0.0' required: true #on: push # push: # tags: # - 'muse_*' # - '!muse_*_pre*' env: BUILD_TYPE: Release TAG: ${{ github.event.inputs.tag }} jobs: build: runs-on: ubuntu-16.04 steps: - uses: actions/checkout@v2 with: ref: ${{ env.TAG }} - name: Install Deps run: | sudo add-apt-repository ppa:beineri/opt-qt-5.15.2-xenial sudo apt-get update sudo apt-get install libsndfile1-dev libsamplerate0-dev libjack-jackd2-dev ladspa-sdk liblo-dev dssi-dev \ libsamplerate0-dev libsndfile1-dev libfluidsynth-dev libgtkmm-2.4-dev \ libinstpatch-dev liblash-compat-dev librubberband-dev python3-dev # librtaudio-dev # liblilv-dev lv2-dev liblrdf0-dev # sudo apt-get install qt5-default qttools5-dev qttools5-dev-tools libqt5svg5-dev sudo apt-get install mesa-common-dev libglu1-mesa-dev sudo apt-get install qt515base qt515tools qt515svg sudo apt-get remove libsord-0-0 libserd-0-0 wget http://mirrors.kernel.org/ubuntu/pool/universe/r/rtaudio/librtaudio6_5.0.0~ds-2_amd64.deb wget http://mirrors.kernel.org/ubuntu/pool/universe/r/rtaudio/librtaudio-dev_5.0.0~ds-2_amd64.deb sudo apt-get install ./librtaudio6_5.0.0~ds-2_amd64.deb ./librtaudio-dev_5.0.0~ds-2_amd64.deb - name: Build lv2 run: | mkdir ${{runner.workspace}}/lv2_build cd ${{runner.workspace}}/lv2_build git clone --depth 1 --branch v1.18.0 https://gitlab.com/lv2/lv2.git . git submodule update --init --recursive ./waf configure ./waf sudo ./waf install - name: Build serd run: | mkdir ${{runner.workspace}}/serd_build cd ${{runner.workspace}}/serd_build git clone --depth 1 --branch v0.30.6 https://gitlab.com/drobilla/serd.git . git submodule update --init --recursive ./waf configure ./waf sudo ./waf install - name: Build sord run: | mkdir ${{runner.workspace}}/sord_build cd ${{runner.workspace}}/sord_build git clone --depth 1 --branch v0.16.6 https://gitlab.com/drobilla/sord.git . git submodule update --init --recursive ./waf configure ./waf sudo ./waf install - name: Build sratom run: | mkdir ${{runner.workspace}}/sratom_build cd ${{runner.workspace}}/sratom_build git clone --depth 1 --branch v0.6.6 https://gitlab.com/lv2/sratom.git . git submodule update --init --recursive ./waf configure ./waf sudo ./waf install - name: Build lilv run: | mkdir ${{runner.workspace}}/lilv_build cd ${{runner.workspace}}/lilv_build git clone --depth 1 --branch v0.24.10 https://gitlab.com/lv2/lilv.git . git submodule update --init --recursive ./waf configure ./waf sudo ./waf install - name: Check Qt Installation run: | #cat /opt/qt515/bin/qt515-env.sh QT_BASE_DIR=/opt/qt515 export QTDIR=$QT_BASE_DIR export PATH=$QT_BASE_DIR/bin:$PATH export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib:$LD_LIBRARY_PATH export PKG_CONFIG_PATH=$QT_BASE_DIR/lib/pkgconfig:$PKG_CONFIG_PATH echo "$PATH" >> $GITHUB_PATH echo "QT_BASE_DIR=$QT_BASE_DIR" >> $GITHUB_ENV echo "QTDIR=$QT_BASE_DIR" >> $GITHUB_ENV echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $GITHUB_ENV echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH" >> $GITHUB_ENV - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure CMake working-directory: ${{runner.workspace}}/build run: | REPO_PATH=$GITHUB_WORKSPACE/src cmake $REPO_PATH -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_INSTALL_PREFIX=/usr -DMODULES_BUILD_STATIC=ON - name: Build working-directory: ${{runner.workspace}}/build run: cmake --build . --config $BUILD_TYPE -j3 - name: Create AppImage working-directory: ${{runner.workspace}}/build run: | make DESTDIR=appdir install ; find appdir/ wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage wget https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage chmod +x linuxdeploy*.AppImage export MUSEDIR=$(basename -a ${{runner.workspace}}/build/appdir/usr/lib/muse-*) LIB_MODULES=${{runner.workspace}}/build/appdir/usr/lib/$MUSEDIR/modules LIB_PLUGINS=${{runner.workspace}}/build/appdir/usr/lib/$MUSEDIR/plugins LIB_SYNTHI=${{runner.workspace}}/build/appdir/usr/lib/$MUSEDIR/synthi LIB_CONV=${{runner.workspace}}/build/appdir/usr/lib/$MUSEDIR/converters export LD_LIBRARY_PATH=$LIB_MODULES:$LIB_PLUGINS:$LIB_SYNTHI:$LIB_CONV:$LD_LIBRARY_PATH ./linuxdeploy-x86_64.AppImage --appdir appdir --plugin qt echo '#! /usr/bin/env bash' > appdir/AppRun echo 'set -e' >> appdir/AppRun echo 'this_dir=$(readlink -f $(dirname "$0"))' >> appdir/AppRun echo 'export LD_LIBRARY_PATH="$this_dir"/usr/lib:$LD_LIBRARY_PATH' >> appdir/AppRun echo 'exec "$this_dir"/AppRun.wrapped "$@"' >> appdir/AppRun export VERSION=${{ env.TAG }} ./linuxdeploy-plugin-appimage-x86_64.AppImage --appdir appdir - name: Upload AppImage uses: actions/upload-artifact@v2 with: name: AppImage release path: ${{runner.workspace}}/build/MusE-*-x86_64.AppImage muse-4.0.0/.github/workflows/compatibility_build.yml000066400000000000000000000045521404105311000226270ustar00rootroot00000000000000name: Compatibility build #on: workflow_dispatch on: push: branches: - master env: BUILD_TYPE: Release jobs: build: runs-on: ubuntu-16.04 steps: - uses: actions/checkout@v2 - name: Install Deps run: | sudo add-apt-repository ppa:beineri/opt-qt571-xenial sudo apt-get update sudo apt-get install libsndfile1-dev libsamplerate0-dev libjack-jackd2-dev ladspa-sdk liblo-dev dssi-dev \ libsamplerate0-dev libsndfile1-dev libfluidsynth-dev libgtkmm-2.4-dev \ libinstpatch-dev liblash-compat-dev librubberband-dev python3-dev \ liblilv-dev lv2-dev liblrdf0-dev sudo apt-get install mesa-common-dev libglu1-mesa-dev sudo apt-get install qt57base qt57tools qt57svg wget http://mirrors.kernel.org/ubuntu/pool/universe/r/rtaudio/librtaudio6_5.0.0~ds-2_amd64.deb wget http://mirrors.kernel.org/ubuntu/pool/universe/r/rtaudio/librtaudio-dev_5.0.0~ds-2_amd64.deb sudo apt-get install ./librtaudio6_5.0.0~ds-2_amd64.deb ./librtaudio-dev_5.0.0~ds-2_amd64.deb - name: Check Qt Installation run: | #cat /opt/qt57/bin/qt57-env.sh QT_BASE_DIR=/opt/qt57 export QTDIR=$QT_BASE_DIR export PATH=$QT_BASE_DIR/bin:$PATH export LD_LIBRARY_PATH=$QT_BASE_DIR/lib/x86_64-linux-gnu:$QT_BASE_DIR/lib:$LD_LIBRARY_PATH export PKG_CONFIG_PATH=$QT_BASE_DIR/lib/pkgconfig:$PKG_CONFIG_PATH echo "$PATH" >> $GITHUB_PATH echo "QT_BASE_DIR=$QT_BASE_DIR" >> $GITHUB_ENV echo "QTDIR=$QT_BASE_DIR" >> $GITHUB_ENV echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $GITHUB_ENV echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH" >> $GITHUB_ENV echo $PKG_CONFIG_PATH pkg-config --variable pc_path pkg-config ls /usr/lib/x86_64-linux-gnu/pkgconfig - name: Create Build Environment run: cmake -E make_directory ${{runner.workspace}}/build - name: Configure CMake working-directory: ${{runner.workspace}}/build run: | REPO_PATH=$GITHUB_WORKSPACE/src cmake $REPO_PATH -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_SHARED_LINKER_FLAGS=-Wl,--no-undefined -DMODULES_BUILD_STATIC=ON - name: Build working-directory: ${{runner.workspace}}/build run: cmake --build . --config $BUILD_TYPE -j3 muse-4.0.0/.github/workflows/stale_issues.yml000066400000000000000000000040011404105311000212670ustar00rootroot00000000000000name: Mark and close stale issues on: #workflow_dispatch schedule: - cron: "0 12 * * SAT" jobs: stale: runs-on: ubuntu-latest steps: - uses: actions/stale@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} # The message to post on the issue when tagging it. If none provided, will not mark issues stale. stale-issue-message: 'This issue is stale because it has been inactive for two years. Remove Stale label or write a comment, otherwise it will be closed in 30 days.' # The message to post on the issue when closing it. If none provided, will not comment when closing an issue. close-issue-message: 'Issue has been closed automatically after two years of inactivity. Feel free to reopen if the issue is still relevant for current MusE version.' # The number of days old an issue can be before marking it stale. Set to -1 to never mark issues or pull requests as stale automatically. days-before-stale: 720 # optional, default is 60 # The number of days to wait to close an issue or pull request after it being marked stale. Set to -1 to never close stale issues. days-before-close: 30 # optional, default is 7 # The label to apply when an issue is stale. #stale-issue-label: # optional, default is Stale # The label to apply when an issue is closed. # close-issue-label: 'Closed-inactive' # optional # The labels to apply when an issue is exempt from being marked stale. Separate multiple labels with commas (eg. "label1,label2") #exempt-issue-labels: # optional, default is # Remove stale labels from issues when they are updated or commented on. #remove-stale-when-updated: # optional, default is true # Run the processor in debug mode without actually performing any operations on live issues. #debug-only: true # optional ascending: true # Skip adding stale message when marking an issue as stale. #skip-stale-issue-message: # optional muse-4.0.0/.gitignore000066400000000000000000000001371404105311000144420ustar00rootroot00000000000000*~ src/build* src/*.kdev4 src/CMakeLists.txt.user build* *.code-workspace src/*.code-workspace muse-4.0.0/README.md000066400000000000000000000020631404105311000137310ustar00rootroot00000000000000![Alt tag](muse.png) MusE ==== MusE is a MIDI/Audio sequencer with recording and editing capabilities written originally by Werner Schweer now developed and maintained by the MusE development team. MusE aims to be a complete multitrack virtual studio for Linux. It is published under the GNU General Public License. Visit the MusE web site at: https://muse-sequencer.github.io/ Most Linux distributions include MusE ready to install, check your package manager. Installation from source code: ------------------------------ Stable source code releases are [here](https://github.com/muse-sequencer/muse/releases). Or one of the git branches can be cloned, built, and installed. Installation instructions are in the [README](src/README) file. Documentation: -------------- [LICENSE](src/COPYING) [AUTHORS](src/AUTHORS) These and other important documents, READMEs, and addendums are in the [src](src) directory. The official MusE Manual (work in progress) has migrated to the wiki and can be found [here](https://github.com/muse-sequencer/muse/wiki/Documentation). muse-4.0.0/muse.png000066400000000000000000024242071404105311000141430ustar00rootroot00000000000000PNG  IHDR VD(sBITOtEXtSoftwaregnome-screenshot> IDATxw@SW?sBB-SR *8Vkvvi>}:tX[kժv׺ѺphUp^ ۘ0F}͹'B!By<^WDөTbյ,77%D"̬O(f@uu xh!xh!xh!xh wP"ZZBTwԵ5U˯vē2]ܣX7Nݝ['fS#(v~ctr7qꂜ?(+ګ|''p\zO~`Rk~5EUUD݁' 8~خn'dʋ| TPUi{3 @;yᝥoθ6ŗRj5e?҅ۊ |+FQwvgJZ A$ |+`!e.jml hפq~r.(~N㟑1 Vo0~Ψ@[ Avh 3MBNΐNlf;zax|^le:hOen*;*!Ӧ63roz&!}{ vvhwcѱ!hWxxPDѣG/h4Ν|rW-r¿7b0n6O`-gڬw[eшZgk+7>0#<1$i%zޓ&{.c!3J YME^vqCJ*oYMeލ 噉er1! k+%'tPtɖ?vƻ'/KW7< <`J} Ra@ЧO-,l썗Vs'ʕ?Nx¬N{e;tG厖5Źn"axt1ֶ6Ls6eDêGaؔ{llXZn(q^=7P@6kӫo3-c=r7/qŸR[nj;o@OG 3ZQY\p;;̾3:R[s͜xu!4_jB1;^& ꚲsnIg VsᣇE;XH]EsɇsD o 2:4w 02O@]Sw;ͯk򀡣FD rwo]pW/k~{ܱeW^Fo[iN]$ a>|iԽ#|CXuLE!0BPRqKJebb]ūQuZu^*Lf&s |vڃHY<̊G!FFN_iۯ'dlLudep"2## =ǽlv5 VVn $fmLVXKR"=cRFM*_M8,D9 =z 5dLo>Xs8a_Z&@+jQV(/( \*ݠ2A/|![Uç6BDZ|KW]U|>i'}nqT Kc/!΍=w"$YHq9_!J|4u\kbah}Frbm_k:R8L;$ʍN˯1 V~73c&ht">grkϔ !BAs_u9w>wꑋ"v<#XH{1#9vR]NְizBoW}{F #ކ)5]>g {ucOI^q5Wlq:憷cB5{A)[c a6:wGMӨKjK tzyci#DZYYEu&F[1?(bGZ1芏y\Vk>YYIo9:XTW9m(cl=mk-R͙4ʮGϠ1 !8tRǪՄX$B~LXGy[4^2}xoiLa}(fE# 0;yqG!dɗ su%e&ﺮJB]YQa ߔ%'xªn￿O3#vTZUN[M!T]ڐ)7 j*Db,Z)ο"'c=_ھݗU(k'AkgFϸp.adꃰ=I,iz"6ů5e俄 Mk&Ͽ/YU?3ӛOˀ wbf~ VWVg7 %Fj;[ }ƣGrLlg#trџ;E+¢oȬ-ª}r'{e_ڹzgCr<<-"ۧRMI56uuo+o̓#GfvRa-A$ *RB+uJ} c;?ƉG[}f|!L]-MR̐ERluk^q1g~fLO=?J/7t{.K[YmՍmܓirFnIKYTxQ"-gcc+o|^--eTBe<yj`̬!%gi|Hjg%lkC9S_5 ?҇ tkr|رƵ]՞27噻7$h5 !9{Nx^[ oeͪؒ]'+XBI'^!eٕRBXe݉KGB x89B+{f^ұ0[ܣTbZ>*R~XQsqdpʋ%miŘK$oի-f{^ESqի?fROQlmii-ۻEl]_[ZBo.u,mvz&Qs ::"gmN֍z˜dtoiׂ;d[=K`ck\&j`b}TXUr%O_r}C-[lߟy0u^}b+#qqݷN;Obf[:B#2V7b9F}u!芎Lmb&2,!Ƌý=ә >{fS8bctrc|r5k+3Bao8ƻ{a@[tHW0|A/o2&+J.ivˮ]jF0p4<ѽ|U杺`1+;Mwc S'&g̽b6Ns C[`A9y%UuخgpBXլ\ [zЋ58ذO+֟m}P]O`Lx{Ͱ*I*`!_[ !χ[13c֨yB=jG¤ c>V S[PYX;Xڢ_nf;_0ڞGxvC|0FaĿ*=}C) >HlGe^Seo_G/ږLjionu7h=߇Θofa-h%XEw-Qp !D{5G+ 3VXeF(s6|7_o ǷU@KԕVm/읓_'>7Y0fV.A.T}͙暣-ؿco/w316'[0[CSx=\䖒}G'v ljVSՍ6>Gr3l^4-?W~ow1ž_|fɶk]5s.QUg<ߚ)oX6c熺Ҷ0< F#Nd+oxsAB}\e"F[_TT̾vT4QWzfϖzx9X[DRT^K?/MEϪ7?ya2[WY|ʅC{/4 6{c@5Y>Gdc)dJEUyQ^N3Jt;3`䘘!nbZQYq ;n㟽~ʔ9Hl0'359%[{o~aBoEfⷿ5Qvh6?0[#9~n|;5>YsG!R~]0neemƮ*soK3v}C8!oJ]K$٣U !TggAUU^ @0t]@3:pa~ak]x)+.o[hܱ5ʋoSvb60#qk Rvd!ʩsiWSKZ:x\$VքUMZQUwZ$#0#_o`{lեm  88v>)"ah?9[U[ik^8]],Gm@GA$ B$ B$ B$ B$ B$ B$ TntT < < < < < < J$@UMzȠ3f|е(6 xRĕΝ/,ZHб ^b7?lڴݷ -kq믿5̄"n=ܼ]Ѿx<^;% y9,,,mmm̚^uۣYl0 ="9 | !faaW_m۶'0 B.X%2(tw!&^~En{w3L&k>6lXBBB\\\߾}s [{@hz~o ͛G gڴiO>d+|?c+0\.;u!3999---n˗6ٳKKK;=ƚ|X1⩧2]ݺuŋ7o4~ԃuuuuu3gάol7|jժC=lٲ[n+:5,XsΝYfq =<<\\\!QQQ۶mkEcgΜ9}tBȻ{|j-S775k֐V]邂>CB|QBHTT!$44T"(h#'::zΜ9 Yo322ZAOO[YY_S' &hZݿKB'|rn@:uSNUUU%%%uv7PT999/_KB'L`ooߵmn|XW_x"H{5vX''ŋWUUq+?]IJJ={60C 9qD똛0r̙:*w3:R\駟?x<++Jk}}ŋ-]ձqqqReYaBCC0 IDAT,stwNy󦯯o'|0OOOBRˣKuu{YZZ>3?W\hEp=bK{F o -U'jS^n[uf'(TWWWWW_vmǎ>q\]]|%K>[|BBBzjmmETۻ;a &Bv%'OwfqㆯhϑDuذaE{F'^z5BX}bbbx<^AAArrrRRRbԨQᶶJƍ2^wРAM6=<<_ϝ;wŎnv첲NVvZkkDGG?~N.Z[[7n۷sOO@///'''XLHOO?|#F "={RՄaf͚^j* 80 @Tnn>|8$$D(FFF68!C!MAllU*͛7<έP(~J${e͛7뿓̙39uT=666􇌌)S޽x~'xD?{1Dj0!.04h!$77ɓ3зv pvvfY?1cFtt :hRRҍ7dECprrtǎKIIi}o=[2 u%?ѳ.!O>R0 C#aK^zҩ+++op'((h"ܹs jDߪ;eTjrЫaNNΑ#G蒁FGG 76@ M|՘1cz-jjj222S\\g~t7o_E} xEEEW\1|w~Ϸ]?GVVV|>ڵkIIIٝrxtj$lii9uTBȅ bѢEZ;::zȑ+WlkEa˖- eR"#F߿ ѣݑ]vqoMg$6qovǎ y׹88p`jj~$X4֙/Jܒqm߾}Æ Κ5NjЪ#G&%%}'8q3$0AAAON36BH`` B#6v5mڴɓ'ڵK}ڴiseffVUUGx<޲e!gΜ9uTmmСC:loLNwu'''GGG5꧟~jpy H}GGGFxE@пC.[lɒ%miaB!=9|>(++G&'%%UVV=Z",Y?͝;999WYYMDDGLR)`zO@@~l+ ɽR\\M%{B!KKKMVp}]f͚oAAAo@ ۺukAA̙3-Z/{QSSƗV|J1@PP|~]]]bbիW5L&cp[>VTTرeY{{Czxx1zstg sUA4h8@,?bĈΝ;mo)x ݳgϞM7ڦ#Gl۶kbdK'OlpekkkB:d0.\_Hz5kֈD>[ꫯ Egl!$55Քo :GGGڵKO% 9s]}} ҽs}駞ӦM=P($I^#a.ի9F/^i$̲lMM1~x\ý.`]UUl$G 6O>wc2 S\\̕5'&&FGGEDDP۱,rJnG+AAAFjtڵkAAA0M?N#~ BHLJRUUoʳ߿;9ro\.vqqSN?PwON>`&^4ŋ `> ?28tJ!==Bu)bƍ?<0gώ߳ga0/@ u… `YYYiii_P(0a_[6b7|>_QQh"Z$D]dHi^ig_~_=zt~FmAS9Exx<#O=T+6Eּv7\0>O:gHRфD. EN1k"GGGn޼y㝶Qqq1M'l޼Y?n ///BȖ-[0²W_}T*ipV}f-NNN\?к1>Sv7Mouu~x<n-i,Cs娨(hR^^n\RSS;pmmmiRWBΫg\5 A !:`'1LIB088rܹf{eB&O5NrtbS"aBȭ[ `Y8dfff?x _pQRm(9rIGٺcEh37D"ʼn'Ξ=RD"Q߾}гz>q^b1bat:ݚ5k f[0('-#KI#aF#ݹ}!?w"+iΝ+V(++#888̞=ロ?jt^܁R\\|QBH#ow?Yfž4pƌ ~ϴ,~ދoמpaa9#U / $#Iϭ۷˗!B0~Ν;WWW׎B=Qkwe (n޽{)P(h-`cHϟj ü{ӟQDE͛7i/&$$qPMWXXHG EB(JM̙33&˲4Obccj\bc !g-.ZNr|XX0tlULֳgO<88X(,_OA,--ɽk42V o\h 58pT*eYB>}Z???:#/7/>ӳHQRΜ9Cf9 4m tHp ӢK=Nh4...5\V'|!:R˻ۙIg}vժU])W?~< Nw5 ё:Z|ڞN>}!VТmOOn{t9pѣGlX[[/'N8qסEꫯ ʒ p~A͛K.g!Bah8D***ɉn њTGGGL !֭[|s=ףGw}p޽oˇracZ>XlT*}f͚uݻw7; 4'''wͮ5yd.F:t'  c[YY^:x @0|7҅ԩSƕl7llj|~߾}i;}VׯK.lmg &%%N8̙3 ܀H$MlƆ̻繑^|P_3 3b:JvK<0ޠc2 B6l!:ȑ#Rח/^lҷw%ęb[t (--v횏O߾} gSRRaaaa@Vs1bbbHHHLLLxx_~yʕݻw?~ӆQՇ:t萛c=6l0ss{N޽;/x͛77KKKnNcy֖R^^?tc !~ =<<&}K?@{F텫Yj-`m#e!}͇ 7ǣ_gk 333BZnt9]iܷw|?4gΜiӦ[N.DlUMĝ /}ixӧO{{{O6mРA/l+--mݓ&MٳիWyIR͝;هwtN MϴM_s.RYYIѣu\9鳫贒֡ߞteggBfN5֊/H$6+}S]v5PVVaÆ+ a&M s=iUJڵk /@;m\uc<==ɽ11_~e۞>}aHD#Gŋo&K$!CBcJR iYjZ(?::Bg V*.]b&22rРAιD,#aB 0hgK#aV;0tT/_P3..{ƦTn *]XWWgxvBYPPPll,!Nٓ͡C=cǷPPP@KKonn~Vce4W^2ɓ >|DŽT:bĈe;z^4ˍpGJ|2!qR|:3-XVv0_HӑwwiVK/L,c2jiU[;7=LJ+FigΜi[=sZ#;e?׏~y^enݢqMϟ?)xkkkz(tvB Sm*џRΝ;Go={6~|oZ>\MJM{ΜJD] B|||)U*_bE~kb.ڼy3˲|~!gjgeeB6d2__vݕV:v˲|>rSҾ&۷pB;;;OH-?2xsr%M}Yp!!֭[\]]CCC_S{{{:}oIII+Tj IDAT6:֖Rj;soF|M?\(:Tfsss:&j]]ȓC SlB۷ŋ !42>gΜlLILL#$%%uɷB Q/Ӈܶm!y5y</88<!d饥tɓ'juhhs}}Aq[`qe[~4}[/5770aiH_VVF_^zI.HԧOja̙C{a`h>sV+H>裕+WK,ټyqoW|3-FDD_:XW77+VHRe xu\&LDW޽{ڵk !VZhݻᆱT*|r\"ϛ7oY?͛7~v>0775qŝ;wW*~wt/_^dIIk޽;!!!$$deeejZ*\-aww޽{ĔT*\.?3:gmݿɓ'd29߹s>h]8hРYf)ʼ;z('H /<3EEEB靼SH8--mƍӧOЯ=:qDaaYE"7VBϢ]veggϚ5+88ښeق:jΞ=zٳg[ZZRe:ڵk&O,Jy EvvvY__tI&M0ښWp_V{M<64tuq嘭h-]ʛ7onܸ ŅUj/^LLL4qY333 ~];w~w`;##C&9;;seyW\oN/,>>RhqN8Q,gff6=Qqڹs x<ٳg[:Q322q///L%駟6.>x ,ۆ]iZyf 7}L_z' daaaƣ+ZT'OƆ888ҽcǎϙ3ʊVo޼ip @o߾={l,ի̙֭3j̬j2%%%**ҁMTQQp©S;V*VA9ٳFc=zg385+{ܹuA;„B3soڴIT*~'7V?[qD""aektNNN*J>>AAA={D.\~g/]maguȐ!B_)...)) EEE[l.'\]]xbT*10{b[XYYI$Z]UUD >!Jo߾mawwwkkkeJKKWalmmy<^yyynnZ_u=uuuׯ_o{%suu533STyyy[G XZZxBѺI$\cӦM?Ӗ-[\]]5MQhUVV5X+UVW>ry=bR7I% {iee Eaa`GGG{{{@RKJJ8rQݝt:]nnnaaacC.x?FZW+i%dyxZ͎f?yGV۷@ MNNV( ØLWX,v8Č P_<裷rKOOWys5Bc?mZZZ*999`0B!B!B #p|^;|ᇇy䑐!n@oJpMpHxǂB!B!U_8!tck׮WTUUU#<F]wݕn4VT*---ەJQgwB!B!DF͏~ !/R]]< !м,//߳gOrs ˨B!B!n)$F歷:kb!l{fEWdSRRf|ӗ 1}{og vm<wck:nG]]]ZVPxޑ .=zjg SbNƸ8B!B! F?/|'֯Bh᫯Q,P?(B!B!еZx|!B!B!B!PL=B!B!B!B!aB!B!B!naH!B!B!B![F!B!B!BB!B!B!B躅!aB!B!B!n 7c@F{r-= 4/AbbDY0!!A*tT nEQcy+W,JOKޠJjmȲԔ(uLfN\$s=JBe1n38z`-IlK10&$IPl2[rG.wdvz8;۔Q|d2!a2,Aal6*M.ɲqH Ԝ<8w="BP.! |ı0PGu&mam?DK\f(!B0 bUTT@vv6aHCChLLLpQݾ|򲲲tEUTTm =$hW7lP2mk' cp|jm,Q~(9aױ>wWfz$X~&1ݥ.dސD>bI.jkI$˖yf#CBJB!Ltv p\!%%%>Ax###*jfkS3wB!|w^ckkǫ"\?r[bELƛ?Gyf555a~yLE( (Ecg:!a 9h0ŲeSݨX}U%Q$B! SR*`iW{yw.Y`zd,#=+(YHXc9r,G8cvtյ~Vx5rՙ$s2W,Z_ Jq555jBBHH<oOMK"B膦`nMH؛SPǝZ%OɓT2#Qtuz y6W! HP3}سgSO=UQQ$}8[o喧z7O~bi<%/lB!t#&ZHzɌd2J5_III9y͛sRiCCիc=Bv>ɳ/YjFz|d䛹ƒYo|E!O%nio.ΎIT>g"#ݾZi}?&-Y|Bvom3@[P|wr,vMddl6Zv}b8 >{`0T','=? <* G=1^ĉRכTiB(G߸13/o>6ժrNX$RT䔌 F3_FE>s?V?ZY9f@0 /ɗI?##0WlkNEq|".2@!?0/ڋ^¸~I3<} 8C¥YYYT|f˖->hcc#}v=4E!Bڸ]hQ<³ +**b:˝y[VNϞ|R]vK|eCzjzkxzv)@SM Q "eF22IQPksT\J@H]d.mh(Ig+~Xy>WMh4l) wVglF`pppݺu%%%~h$j/uAfbَWW/\.&|h3Op: ,C"˲QqM/&@\ntqVkǓ l6JfQej!4g@.#,_){$)iѣ;}j +#m8S4Ȅ0p'yJϲg.wMԇmO?O^]v# |~0/33_җo!BILرfO-X,uTxp`ڭ4#A ]|g`2Lp={D[u+WP IBj~#Ô HF$>^,G} LD1Jx(5~ZrʤPjS$'G<2%)Lp?G3E>ƣGOo~FwnU iСC9%b'>{@((/9{O 3522_cgiZ4:wpGu{c\&7+gbdd'$h- -1K.eeebSIikkZccŋ#pH;ȤjjL==kF:6  <SIkyx݈е(vgٳ^    +KE2)p7Ө߲(&N}DW~k,y5~ǹO=M7t'x"(*330!D m6.ձ*B!Ba\Y< !1ퟺb?t 06~iy ڄ.6Ş)))r9ǏܹS Fm)InRQ_iV$iXv$F7ݾxpҥ&|&\z{ѷK\Je݆ %%kO@c{\cn֯-L ,sB<Te/wtoشxxxsdddknX,'+"ON{~x?G>%ޞ9.EIRVTNZQ[[ _8z !ttt {Y8I===/_Yj# ;wnppog---֭vMNwٹt|ׯOIIzMjjjŋ;RSS,//r;u8U  Țnc[MBN$x#( E8XQeR`p`2E8 ?6!a&}a#O=l`C%jVеmذ[/~^ax r5>$ /8Zsw}wHΪpB!iB0Æ:qI<]+Q+VbGn"副?q,U( #HG>HHFTx,aSgn|3{0qoӧ7oLM Yia282^Y,cD4QaWW׋xe˥?d_P:/1%'W|j "o}uu޽Zqy"",)V*88RVTTן;w.33s>BHo__3t[V6p3c?Hdg6{u@: .:rs9N_RRP(f]|/4-RI$QX,6L4PaVw Nj6̙3xisԩKLVDeuFJ$ɥZܸ mt:e68 Uw-*׍}xRHjxsȫVY)F,M'WϿ3cX#]]$&& *Es@& +8$$ #KͧTи 3_8E۷oM&ӳ>~Wߟc4MYbڵkkjjx1`B!v (*|…e˖p Gwn}wh5:RL6X&EQO>~7~4'dYwill\r629wWz}h'0LuwƜr|>(R-Rdh*MBRҢ$0s >)莬c'l۶\a^|R7o IDAT˸wVceb8#,dž9QYlEAA˲===}}} h Xװs?[#pmyOZq}4TB /Q%KPx ۹\'NL&BFyMM [TN<\p8f+JDbۻV-[x5{n`HW! C"rWUU-sH"K\Cv }8ԝZ( -xR}iAMϧ$ag>EjXY&h4Φ@3"() -]n6{.GgN3bs$8q\E8P,$4*,'ab>aW_˻_b?!HT[!M6r¸-0P}v> X;v_7Ϳg> ⥗^i:PGﯩ#C!BhL\,]~LL{^xݛ5UUU}ğQ%.{+1+qr[7Of9WZyGeqqqggg}}F%:Z{?w?Ȃ&2Dc ꛀی auuu]ptM=22r98E-hs aH$Q,T*cIa)))`FÇ׬Y I qH֯?*>44zʕކ`)R4fQ\ GӛcXO5[OjNjxC[;!xNgee|&,^h83lo-N[xO܎Ԣ~k{,)0lH`>^/+!`2nj"\JE"\3SƧK%xAoI3gv9C~X- 嵵[n}?' 8G?QZZZpkm۶bkf2B!,,ݻw5:::!UV}KDƨk.50d7x&oEj[Y +-ZVV/|뭷;wNP̨KI˿JO.6"`RiQ0wùn=>}~0x2޷y8%9)/7GR)qn{xddxxhxc%Ғ˗cҋKbjXA0WGյg4Һˋl`JN((Ô^мfMQ  %c~h3)Jeggg{{{qqq9wTV+hx,3pl.˓kp:JoU=NqWkΝi?Vk\$Mvkjj$f޴~g'%%d2!D:thڵa_+##al|ŋA21uDD lN!%%E*z<555fy[ -SOȢ=\H*fY|ƇLz3dV$ zB^~Ej>$a7oab9+F69fJN|,+X22 -q=Lqqbз TGr EHXN[_y'ja9+w(M1=@>W !fVDz>@N#Gwԩ_l6WM6+sǗ"]'ci6B!B4MHxbǚ8wڽ{ݻ>677s0a>a:(` %WzԬp`ҥK_y;ɓvvÞ%o6x<߉4u @tQ %D"ՙrp4YH\wB$\9ʭ[5\a2ipelhV(EEzǑBHH Ƙ1 sy$%عsӑ#RM0AF)>_8|+좛}+9j˳zzhB Be%w휭u_osXp,BHbx.i]AAAGGGssP(, 0ꢲ{zLSoS!jye>,/G7lAB?rϷA0=l+"SX|`JI&'1BaTMɎO+ZVrKy?UTTxޛo9{}H46o0B!%xBg\&i$8$]0x1p׿uh֭[}s޳gϴiQBVנ|=-?t:]{wz;hB, '[%ZnrĢ ' ϝFr-˚2rL 0ZxAD**Ʋ|Z*a7S"l+Sxi{?DvݕQ O:3˭%[ ˧ma"IEsEЅokZ){vT'^tm'հ,caZXβ6e13J;#'k Or~moZZG]LB,.K8z{{m6˲AB„?{oSSSFFOx0!ou!HƾN!BZ4MH{sT'.Aq d2y7d/L*6z9=&0\i[zX{]vM][QT~'xǃx&Z\6+o@2<σFZi0ֹӒd|@qiZT*Կ8Q9FǣW3wToiӑ#CVo-[WkZC ^8ɇQHx޽j:Jq~pDZFgoIXc9²y`K Eu#7=h,:~ :###]Kv`_-.ȯ;YEkYG L[3b>Х3Z ߃ߺu5_3j0 Ԁ^!̓d5BJ!a%pZZ1o[ /`! 祵nh(] ቏^pCs0VI"8Uq8ߗ'Vݭqf2@R[/aa?D6ڪ ,&Xps"eV ׶~L_#"+c+̗HD ,sJk\AN$,^h'1vk:MOe-IW12/Q$I!M̳W m_Զx|(M󕕕粄{lhڝZ:o?)t"E&ZÝVʊBM6ܢm;"tCVc%*grwjO tW2ys}TV&HږŶFnt/a듘5G t+Q$R5F@:vvt&^٬S*:tH$Zнny0iB}sEBX$H)JA*T S`zW!{VWVm&"TWW߿ؾMHXT&BH?b !ww~a"&D!BhJ%|m E`0fT() *4Mg')9o~s``ɓ'm6EABG~|.oo\c d.z )Ј@+MIG#$%+V\v]n70~_&e)Ҫ+QLL&Ic UPÐ[sUS_fy`ыj1AR LvT3zjh4ʗ hc'7dcO-]ZPPpС: v!w,vsdץW\Bd(e{/,#c-R}yђrm=zR~ p476i.hNugqHZuwwX6F iL"W'6ٻzvRSSz}°SSSb=ɷ.++猓.))I.O<|kaŲlٲi4͡Xtc5`} -mbiՄni->m'P+nCS_fX}dҁհ^CŧŁy ҽ9?+VxKȫ{rRj_{Rv=ȯܩ%0|t~ -<Ʌڶ#C=w8M2j3___q)CМ?F4㛹 %Ѹ{c3MoO??Sk|~EHCيB6Xb^K}h3?`|Rsٞ/h Ƨ *bTȫ,ޕsoщŋ].r<&_\D_ϝ3&k l dXBeU!v =fх:FFF@70!{f HFdя#B!4iBsV8$''QSJ,Y³k$<'̵flir4gH%=P,vB7EQ>jkkg 0 Bg,*1y:93]Ai(uJRA\BK hZH'6Js$'EAA^.fd6X,VYDr,1)lQB1}`>=-uxd4v턣rdAEq=M&wvfX  &ݝZccҥK")jv.\Z}˜~YM{WM A\\Y\\<<<ѡ*[]`ƫ -)[,8VMI .q7EHT|ͧNb>%ԕӘp U SJj޽{ǏɎT*xv&6y@4zb`"(Fyaɷ^lYqq1W\w\MMMf[ 4D" ;\xl6WUƜba f<Sss"L P3-Y˥.ED2WfyHHؙ(4Ko7  xƕwɃ7k[0^n]O~FïH=N,8F~T> BkV߭m"ɧN7/ٯLT 4VpYuJ4EUcUp./thdc kvk:F. |n6T~++VnNR.^~fbseGOR~'_ +nG'^*crnU?qeteiqɄac^|9##|#kĬUr ?~GpIKKဌB!B(f Om۶"$~`~gE(W&m@OCu^ok1tr롡SNT% svWKHIرlh/Xr_N&>Kpz,}Y~y0IiΩɫWᑑNx}~ozV3KNJJ$nwt7Mr7'ڹsñcIBɺMcw[9>)* w{} V %di;886YB㱎/mcqsuFV7];7qvԂMOD[nmll\IjE+} Wl۽ [GΜ<8Xo9|T*X,s􄍳d2YLS5L&,tr,KeeD"@k;weRiVk63ӭ|&}jd?޴SnFխ ȇ/|@qfM}x#U_)WCRߙl".R]G5N~'`]Bˇ ʾ]vUڊ6:Q4xIMa_5 ׭[7٦t:J> .x:eѢE 8p(B!B1kn<|aD Xa'Nz~טwO;쎿^>^E:=BSꔞ/۷APN\'+5:3QX] c\U,_B0=9-Ҍ 'Z _Nu~5~efլ]t\jx'O쎓lX699i@R*؅iɈDm[wDXIvټO wF!F` ?00i4<rTbv} xB7],vvvRͫ6 ?6 E\RVsNc>NN$Iټ}3yW$yYW-;!arNT.W롪}k@|?_j|CIۂ̫GDq5ӋD8qpGO-0̲3ʙx2跆*`,IpV] oj7O‚޾L*+ /'Q9FnL5^PTrMOݳ\كQu;vV˲|H$ |0p{{hdYvMH쾃֑-B2%q,w%(/..d-kD@òd0L /( P~ٯLOJQ^eNQ_1V!&v9a &YF"̙3Z'3=G*]֏'W2nDlv' C{\5ўδ6wjq/JnPtupW75RJ"V)䢠&r"lZ%2:]|ݣ$֍qaڼ EG3?x~x}Žl'ؒїv8,q&V6^~\ZVIHW^9wZJ>ӑ#B!4k6e2N&P(i: "Hd9F|?PԵ8￿:zݻ-PAZk{ݙyPMLy50eRMgnI5'h=ZrRlV.[<͝R!s`l@-dΒ4U)lhzIt]\͇޾A: ,˲&I(&''~^ojjlQǔdX[Tıx,߹iڤR%1a$>!ṓd`L- 8u춣vCp#!v3B3pPtGRn{PqAqiZi|^4BZQDիΟ??v[uE^-2;w?~uÚ"\R\2; Uɦ>լ"ǧ,Xط'P0ŚoQPj󄭸ʧ$H ݜHfϭu:D xaM+G}N숥t'W,.1[r7m^}/ebQp0(bтu?z1S]6mB|*ib.ֻvXJ!$mff=tЁ~@Ha^zW_jQLF!Bh?b'p-q\AAFc2h`D"d |\ c)>K8T$75o<=;>}nί T3jϫymv6N' {nr ƙT"^Zhھ B*($c]6 /mhlZ܄k6m(R:VAZl>|mCWr>βb#@zb(m"FAacT#Z}vEqIIIfffMMh{+|ĭJ9K>i}Bew=wGyz`gf@_?񏕗넅񋑋O*,,jgΜ I|### @ZZ5L!5CCC|AfD"ٵkWmmmOOOHilf;1r)׬ a=4GQTbb֭[#qXCVUU!6XJFFTDM")}M9~wH >'+V '79Śb9P1$rd[Qm[Cb ,9y|B?|` z‡#XQQY$Ԕ8+|_cg L/;#LkXuȇ/fU`~'玟g q>:_Rb( 8WNϦKK@ ~lZ-˃MMYIiaO{ЃMIG @ram ?Pvx02"(=t!oFkkkKKڵk~&J!U<!BE'Etww,HJaA:Ns;uP AiP86أ?yZzw[Gedd0#6(Bt!u53T[( :zJfW(eM1?/7;&)zChPa0$ I:i *`̻j#-:Ym:z̖-4Ñ8%HLsC1.\>< iMSZuvc?y W*..xb`8SEsiЙ*JJh7TE~C]y((57}_2r!l9>OmsddLjXs3E`!ᡡj4Vov۶ma$&&ڵ:UFGc][&m-_ݙu͚5 /^ ]6Fr|ҥ%%%!kN cp"233?F#h<x΅luKzykTa+%[@b  DvX^[ڿXK8\.1zRL@jtӮ/&6͍3M&~~#{tbp6ME(KoO.;EmOKM(ꪒ@Xb=r\\NKi0@rHup/0 G{in<1L#&b5QL.]ReeovC} H.&[kI+o?!BLر*г "E>l5?k72(n|j#77VF)//j+s~xf+=h۝TO? @zTk'kf)J˛ߴ!l~7ANOOEO"AEӒŽ Y|| Wz,z @T& 64gP]][UKorsh4|z~ۙV%ab,/Xl7ee%%%|勵X63 }NΤ=PR\Z9fMqriNoہxp?~&'ut^'\"ռ.U%AqLF|7V7Jͱa|D;w# ED?bbka>Л2 oqqV={lK척GFF4ͺu뒓ôZUyB.ǹyDeM~X000,Àݑgۺ?waMjQaQ"+vSgʹ&_'ȷiK~ۦ#i؍ڎe'ޖ,۲dk8 W@%^{qspsΒ`)O+ ƾ[Jد ٱ}dlr"*97aZK͒\-u..{KTCE=$?9`Y"<Ǖ6RСC3pâqBƖR]9ZS[\Ko-9BS#+"!EvҺPة_g$x}7iD{ 3Z?&v>#979؟)R4%2;}Jڹ IYk,{OT'^{QdIVdI$,?-CNy2C]{y?xit#{F߫$^³ HTGMB<+3L(Ik|=zܧ7,[YW;^x}7ՎYwyWm۶%q\<%I-}jn'?|=01${df`ƸZ3vL{M.la]ʉ"5^ڹxxje,U!HD:1* ª#GI^cL_UXj$L$̱=~VknnxbiJ. =}J8'XY{?m},P7ESYd)!":iU;;nKQUӍ)dIOMx:9X{ 3&nja7666)aJZ8&nZX&<8Vl8os<<-STMD Ѻ]A,A #܄3BHWE_}3wx58K:3h1gN;c\xÇ>v-I}ӑūg*WGjr};TLDrT IQK:՚QQ(f&xN6R5It\$X.nj၁:M- ^Œv64 ^W:(-R˜*KPhy<8(zYu-5)#$razo l}@֙6|lx݇y(#L_8}[twq/ܤ~/;lܳ,տoݻ(7?gEIoM ~1͝5>%]w}Kusyb_X"T󥈭o_ mX$>:35$"_Z3M~#$7Y:t|~`Fk2Nοkז9s;ܦ|>d|@ &G7#Pqɬ/gLn=?7CGq/$  9E683k0^T/չ- 蜽m瘈YΌYz9h{5.(:@ܨ:Wއ7[. 0>D$xk!{+ ×+s"JN&G5kaK1oq??Fg8Frjx:[PټO(s1)鰽νh븠;՘w'ApϽZ agD:( Bq/*l,Z,E555=Nk׮-Yno|]]]Ao IDATglHvB8LD%cc;{֭m<1BFA8cLy˲u^DN5|!N~.\8~adn?:%#֖erx\ϸ@aұ*R{1vhɺ&Bks#*:kלNgIIzsDZ̙3N3EJ/^\ZZz~zLM6ME"UVa)\P(|%,UE_r\< rAU}BI)R;5oWVsU9i;,82ϕU0 sp$ً˗V9V%=A $yI#{ӳ.$&,md÷c.'I_50e_7pT]AEٵk-Y,_UoJ+.D$oUxo[S/0եO _ 72H_dĐL"2)(2)2) ) ʅAcΕ0 >uG{N?YK|"gb7glGOYÚ*ech e}B:0GF5%|l6} .h1vtg(h͝;׽W!jzsjn*`4F"@|hlhًNwx˖ᚚI_F{uTΡ]M:j 68o.u˿kd)yL VUPG 1YDp:G }pUSSS3&%.,hhhH'%LDz~ժULins5/ʪV&԰uݺK;BV"ODn"^o,-ݰfŖlہ,w'g`8ql!"Xno~ٹhb,ڪ  ]ݯ|}}5Yhn#ӱ=:E.ٴSְxE*VI4IM?.7,'K95"˃t:G(˙gYښJ1*?Y,Lʗ_~7x~i"*aٿk׆}yUA;>Zo؁|Ρ K(P_ZGF||IT5X.7oLo,a&.g&tZگڞ@c=w\KP[555 ٳGfTVVtҦ|ksG^u->wKHLZ[;Z[gr{T=OS9Wfe)1JMYITI/)?gv+|Was5paff+̼/7F3vVVVReeѵ h>o|= ll&8eRd_b߮OB>fYf޽O=7eZf`keOZ{GUYW,N}]{1 %mD 1L8D0Į[U i;HlQ(7ƁdHQHdM;nϛ7ɒrV'+;>ݭ M$_kjbEИ][QF)d\FE}W[[sj6O:vwX47R5(2rȵ>^ky۹r:j4ES§.K/],{~ޖdipعMdƍق յii1;{;.yAV!.NȗΎqXdPtv@EQdYrM&=yϔWD9ߣڵ׬)vvOD)`4zmѪ*ғ=W&۰;H,+$˒Ȳ,ɲ,$,+7+Ȓ"K Ɋ,"ӊrixxXi/uOrLuZݦ'kF *|opMt<ޭKB6cȨfY!T.4|4hv) Ķ}YkD 㨮vTct) :nB׻f*tʍ!]Q5%"ɋa*))ioohv*)) [ڍ;"DNڴX_dgS3gb{Uavf+ҒZR5#Egnn i6A&0@BJhM10|Wҁ^E Ǐ %w>(ZLKKK@/a0@BJh!% P@ :uFgb{U>;f#_FҒ:&+u(N>={] (N.-j ϳ>,[,M`.a0@Ew@% P(ZH ->;s`N|W 00 LF|[-ի]$N8*Pq"joo6qN2(Z9%\ ̹@WLۻw zr1)5¹L!na6*/z 9KXiODЏs&kN'NpLfb} \@B:0%-"ZB % ?Nɖj]4\:Ժ2&ǟ69uW|"'⃔0dh4Z֡|W $l?&oP|WfB;C_| qrF>*f(Iw2+Thm0x<ϧbF3EJgIիWWVVwEA_D3|Lr]o?0M>.5=v?wEi # )@AE$Iaka?}V \]mzusiԢ@Ϯ]˵8F% 0 7tNwu@!N1AV!M3eT{ EJx}Zld8N-,Ŧ~A1;V^hK1( qh揉FѥfC }@>7cqeE>R(\(좥XMVR755>P' + Y+s`H mYa!io8 G'΀Mi5XJz;f{4O2Um21\aÆ'x")_8IcitaiNCFf>nA`H@[(sO"H!hq4^)H $N1\PWWח*AB$*c0d@!#Ya!iohE1g7((Jx>ĘⲤ Q{d娅HSyiիz}?yOq+dڵFڵkW\g@Ν.d!#9|0d*q;YV`R!PLQ( %.Y ,5qY,pa fXt:]:]~^ ,ˢp1Vo<߯E%pP앧;Ud`9dB$@Iyc@ⰭFt|`8t ٮ (,fTV2.1ESiEYxw"V"N^ill;: '#KS 㤬졇bk_Zv);&yQV8Fa-a{+ +:goͦߐʼ_\3aK?ڥ.SX6[#.P`6$\>#a&mmbV8 $;mۼRg:*7sEsY|h.gĐy2Y{+ @҈8td`uQ \ߧ-7כ9W&x>9,[H$(JyyyyyfSca^APpD":@tgWX,Zlf)Rd#i" #z1YnX[L\Kv\x O2c5rݢBEw>خkmEk\zІʂRg*2a}zG.~l_OMv,_P;ZB6-/6+|p!]w5X(q \ȳOvYoYUT:1㩷?"kػp?tO*ip;]lm'e'ս ~'gKr)BKrq8ߘ^T([n$l6feBSVfaán F=$n} .Ġ^rB]XMi e ²`܋6OVmX1sJ^Z=blz˓T鮡"J+l}`' z #a[MZ9rH:K?+ܿ3j3X(};&߅Lvcڵkm"2]7HvhGFWqj||TgzOV%)3ց͟4_;=Vn&!O 6lxdz2hq~K{oKHQ&Q ZI]]ŋhY*c&Ԭ𥿙ր4bL({K{< 7|0X>d{+<Pi}Ddzo"wY4o}`o>w@D$-#49YB:9i>X%kw|N4hs.5|5"Y'J r@7͸-ȉZ`~%q 9^.9.Wht?C ;CtKνח]tl\ֳ󡲦w KSMǕy{4Y'U-aBdpuCdPݫҏvv<\Vl|m1Lzg(Pݢ>yoc{^[2'N8I^_vm]]l6LHddd.\-ƲlSSSCCCUUnt(wyGɶ?+xw&Fj7,hZ:N-Z^Zg:jtQ#n/9HFbHRIo!">+H;դ5v*18.:E}4:Z|QFEU[>R+%DfTɹݖcșN'M qu{KDު>'Ii9v)rK4tD Yz{'0y~Oy)'DD\)QI}튩uWLg~_Y[._:S(~;u-h'CCCϟO>&SҪL k[Q3*m 4o6 zDSiRX=6oq%RhQsASmTdXy'nKwA}-u16' !rKnQXs5oSS™^w󶰽H<-h*6q-Ȃݼ,p`YdbAbkm:W_F$r[|pTl60dY|-Ye:}9뚤Ùr˗'4N>U K8ܽm DDcDnkZ^h/'D(Xl8>:h D翌bC9rth۷o^UUvҥwމ4̙3W\ B 455tMV裏>0ÒZzy|%ٺu+k,UǤ(߈I+1 %k H?8x>Tmت^/ r9مi,h*W6 hWۿՠ!nBhoH41셏 z ]Rwt q"װ2 U!"KQXǦU,.N:кF?c{6XLDu~+\.xp_GSZxd]w8aD Cic3O^ `^BqLn=ѿ'JL2Zrc544"VQP({kpLN%7s$MKLcQQ܏=gesT`)sDq+2 ʂ!:>(_Cda?V\I:ngDTu1+ʳpSQ}Fc4a6Y.[gqO}xV ɼNupIkvJݫߚb٨9)be幨Q,f1]QEQԌrlܪH$rڵI@:4D$zy EDG.M\HESiتhpZ]7&LYr.}SçO޸qcee1` ZF}XL|t:xtVg٫s/:|EsH=UowfDcRs\f=.M=]~͊L^6K޽pSzgRr\?T3 K\O c."&2^&ISxPGD"çNrFS;S/FI/͋=_ 62RoL6|4{-M 7lU(Gl5qc g܏kǎS^,y=a~/&p8<88DFc:CFEAФj(P8P8YMXNvq}v"tR1IVfoYԹڰ.~+ Vnw|4[."ҹ ×s Ӥb~.hJتH43E#֪wzrF&W{k8}#֪4!ncfd ˼od'iI"t{?2z :CGi|ziROGǭ]6.ᚫ-K/Dk\].L^lBj|/fV$Ir:ǎkkk"'N8(os + 'e"Q D?%<]DT僉 _ Ok[%e8^ h.Tnܹs˖-L[Ρe˖7|^R3Dy]vFD@@Jj#lLtfC澷c=A8ׯ SQ"Q#f<j[ab*^GD[˨!MrޛGv8kis؅>4ҳ'TҹdgY?]zy.]k!n CD$GbvaQ9٭nJsv>qoeg#g3NlF˂^-k͊P! dcFӡ(ӧ{ll,ߕMN%73P&'Gz "ѷ}/Q^dX^"*9nY[Yb#7Ro5fXג[4p eo0lv^rh"b׭i2uȑe'+pa9_rE8xZodԈٮZS4x歵v.wFDl$`9[8͆4l}pDd9W zv01h*}cmjt4څɚFqk9-fN#MV5Su6a+v96MmDtB`$"cF)X1Tʷ1Kquta;ϲbHu}$l%OQ|Іu[TX K>u\ϟٙt/Z~z S_uӧOd6_-NH348? 6 ]Kv ӿ]/d^*7lW{q`(,g8_;q</Dsy>;ph+vG@](sA6}[Fh*[K2ڣKs5o'D$x}7:n9ӿbR+Cl'2P ђ C`q5Mkx-QNx鯮Դs>bz"*9d8i G;W)_qx<"y_x9*׆7Գ*j^ )7tCaS&@)z (=K! bhh։ZBDD|'iNҧRp.Ea9"ռ-=8q?״^뜽kdQߙNnu~Uג+m὎pi뉔~=/G{uYѸu7x#lٲy_v}hiʓƉjmooG.(y1w zتe^ODmFAAfDӸ{LI p!d;xKh*URc0oǎ{%Ìu氵ZF:Ot {)"Kp&t@K(xv/)KF[DTQٔ|"~o`1d0$IR;4#3Wdruq6{!4UPi.ay["9%,O[UD79~F m_4Y1T~SO&nc+-NDo%0}.ŋ---q˝Ng!̢wm۶UTT~[lEQVYhg>v:~|ꩧf&qyfEQ|7rT<(s߈z})5o86J!ed"|el?cd3W1h.I.hÂƈm-v\(nVŻFPicR]"[Vx.:mFFNNH:He `4DrAOɹ*N=7Ye:ce`vV읽gi DDQT JD`01f,>aa0j4$:qv߳gn S;S/ɘ,dy_YHQ=3?w=?g٪CUz,S姞4gd8t))ە7UKDs.Ξʣ,@bIo%Rrp>tҸ[92sEş'w^lh}*/^lllX,$9sz~45jSaرN8dDNF~k*1R0rN4O}씁)4Ͼ:W=aaK S\ط+~Cd S*ϑi.nNݾ#DdD!nU9='`Yd1\դ/6Sm~#''WLDTNgH 4U``"AgP?֓MxNԽ ˅J#*׳a=sj"n q(^W۸4וeYQMiYSieo\T*ނ}~.|F?W((7դ %jZw{,FR< c'` y…gxчzhg^o~Av{mz^D2E]mW!HKׇhSoo}0$NQ:Tqp.lq 5#y|`&uW&W&"urgy&;>xba>{?XϯɢE(FsX f;|lq 5#eY^]( 7B+++/^dC" BK1~\z5;6d~l?wrF[[E7jc-ScDDNy򜁙:Gۭ@Hw*SRD5EJX1S9R^^.r+ ?ߵϯiɟ>`ϝ".fY[[ETis&%Yj3L`\]mOg@yŖ`B/ahkq@sf#-F[-"eY՚*gW|!ȷn3Z髛91/8%[ZB!X8K!B!B!B!nZ>%TTT&Amذan ~fF?7Ϝ޸{[弫sl`߇!B!%B!B!B!B7-%Bgύ>;gN9B!B!8K!B!B!B!nZ8Kcd`^d}10B!t,H߳X, Э!#G$$$-0 :.222 Fhl E|>_.s[wJ @7!Bh "BwgggWWW2ĭ KKKU*Vu+/**Z`,#w"H&566!h@qGBsz g@hZK ;vȚ5cJ趸s: p8@7{z~^"h4 4 7ܑ In X٦H""dRX,&qŒ@ph顪QUY9BCHĨȐP?Y{TT[N3aaany^WW' [K8B}~@7 (<C ރ;uerA544;xP}ǿ]'Qݢ+V-[:;;ϟ/9z~F,cN6 A!,aoĈQQQdUVڵk˖-n˗/?Э|ԩg^r[yTTȑ#}dO(:燇9hqGB 96pH}PE/ǃ-p0l1cl x[WQ>5X•%NJOwܾO8`/@ӝܒ%BiV*+[o~\uQѹ87`ͻnFɲu&PB!4ht[ų!B7>n 8xܑ9wtE]omk<D9Y!xa֖@7+w:>@{<g v.(,$)ă[&f97N)+;?-:rPIɱNbٓoX[W/2T*Q$qj%M?7(j<^ZE*''G~ 60GԳs6Ѱ'X5flB!CH!B@ ++<\ݔ#s `LE!0@:ŃA&`\x(eZPp۶ˮx018w/s:Qg>thθaÓvlnI]?"#JD$$b8q1?EFPh?~Fvo=++:' 746@$̘TС[EwNח6L P>VB!Chϕp`BN#֮]chWZE{|m< .tWN#"##q2M;7<$h/L>x0.(7~l`G1.0N]u9@9l+20 ~󳳳=ˣZmmmI4M477wŊJow8zwLa,-LlqwECS - /u5^6 XűtNqrdF'W hT\a_F<|lٜ7iO*6p^ƃaG卟4y9j ^[3?ҌApdeeEDD LH/˹\bioo B$9Ύ&MJv[3(bvyi(w:v! zl޵k[yyyykkg96,teeebt~6''' BX__@%=q;WOZbZIUq-9r\,|aL&SkkkIIIy嗋}6GTj05W6 3t߿e{}p8Nojl‡z1$NZa_Y PzhR6umP9[=yrØe/px;Z%뎦w֬kO4M_pt{ʅs8檪k****tVVVva}i}@GQprO &cbbbjninva&bbbD"Q{{ŋ⸸8W____Mn9{p؀VJJʺu?L6ݹ-@w{O`Ŋz&(%Z絵~ab>ǃdee 2 z>ܫx01)۹i=۟Qa\qҭ >R+ [[[-J!g4MBÇl>bs9-m&N |V&eX\jM'ڠNLv͸ĉdap:a|bxOBmٲŭ|ժUv,_|9|{ԩSgϞrJ(,Z;H$6ȑ# ACIo8mPv P@ mQFZڂ7Utv{QQѸq='#/^G}tn=\Nȑ#7nD֏М2kzMqb^uµ|Gyd„ n_)Vŋ?-[\dgg#G!Ç777{~C ow45vu2K/tС~Qos̚K)MxqU<9 .mzcTXa6Yp|iޏl38nrFUږ6#f$'oIlHђ}nӧ;666d'$$%MMM}]]k5fϞ=rHrt:wUXX{711~Ntt裚o}(11qWyҥuu덊Z`rQ8{uj?Os?"LP`0;$ rVƉ?;zO2Y2򘦩f. Ru84e0FSvj* 9 _GZM:{{aQ ⏐08 @o >hNWodG[Ñ|{gCy/'pWVxI<-$|qŢ{(<6!B(Pz =\ˤI~|p: FQ&D3g~/)))88flJ\.7%%ܢ R433 ,p8RTRedd?~ܭ!,,,11l6[ggP( \.… 5)lmmmNST ˜>_VV]p}є.o \.ky'ƏNNK$V%w#Ԟ2y*V8-}'x7p,?>|pב"^8p?w{l6[,#Hh[WZuȑ޾5*e cc  DzE56СC0*Z- |>?55555u3gH̙vV%<~ KjfV?AI1ynsܑ}Puf|ngK̦x]31:%gAUUC 9 ݘ{_kl]TYIu :(4`٢wړ'<.'O$;)<= 9FGzC(.]T@MMf ^lի]!nj3jԨsܱcN<ĉlDպ}vQ/q8?v {رcgΜ Nh4e˖U[?dzUUUB0<<<%%eɒ%|[w޼y֦jE"… 5*y3 ~^?rȴYfmذ#Jɥ֭[={/^ÞwaJ(K}_e<6z56׈QX5RN*6B!f 3'ίzǎ]]r|iii M2(h4"rt\\'&%%4mXN8AfDi4D床'ϏNwiAQT\\V kiiaiRDQ^?uɖF4 F_xhw:vyC:N8[o[7|pZtr:e2^GcN3Rq} n=z7%%%c.7iҤ9s'|rٲe`` Mi)$ '"wb{D]TCyv{KV7#UCҎؑ@Ѵ(Րx<ްaüjQQQ9ݹsyJӴ\. Yc Eo;y¶!j/mo-j*R?嗮SLzcfQ@ϟx0.Wj3uچܢ>mZt5cvKwiqmUFdgCDU$:-fME'-*Z2&nD ͺNh7|sY^tT*;>cѣ^mmm?1cؐ0M$w ŋGޯFuu,**ڹs'ɳrg͚5j(T:vح[)?>YxO?mmm M8q۶mlt޽{; /;w ˣ(pΝpԩ{,'''??MP1uT@P^^N%x.^!wIjҷxpYǃ%3}kjj&Nx}wнwCăqowL0NuJdѷ?P͕G84ܼ`zV2L^zo kw5ijXl"@"4-CuEE0hk3C99Zdvs) %K;o]BGL/p^؅-;vpw4'~fB!$L2H_gyK.WoݺfB@"`Ϟ=;bjrJEHJ2,,LVUUTd*sH!0/^T*b8** r2u,yFCQTPP.LJnNקuuwyyy`_{5Mvk H+5^ vʵ=y1,. r5HvR01|1,ik ڪ ։'F}{pvϟ?CD@͚EB8NU鏪*o{ǐU0E"3"b#ߴf!> vt:[[[[[[O 'dh]8W(lw'ꝝ#Fx0455mٲ;'Ie2\.7ӧOV%׫0r`^?cz/((hѢEyH+++})v}iii25Y7$''T7n$`(--1bD^^ݻ٥j&N$ fy͚5=+(( 5#""ԩS϶pVKnhhhNN0Bq}DQի @ Cnǫ Q_[]`6#uH$lEUvj"';bFy,a02kn 'Pдcٳ k2YE"AP?Z,ki1D}Sx)Ljm|̐k=b2tvWwۚ?{,a G,њJ"'8A#wvlLJ_3B!_f /\&}wF׮]{U˸{vͮ333 P(\n[[[ee_|AnU*խޚT*m6Y~bT:cƌ젠 t…;vxfTrP(JP__ZnXT*Zf$%juk[}}}XXMAAA\QmFfCCC\\ I\ba|>b^^^;]@ޑیFc\\nfffbbbTTZ&_Nooow?mڴ Z-ȑ#۷owíVfZ\.p8… wLV/⠮]mɺêpQbFRGm~cҷ^?'Npۦ%(5lEEEcƌRu۷^= $$$̘1#!!A,sm޼{DgH/e'yɏ;y_^3$lh ^ufUTTD6l00[ 5x_&-Xn궖grrY[[IQرcGM4.\سgW333)jnn.***,,!L䁸ۿDg Qì@:3N2eԨQrǎl|Bә%ڊݾUbccb1tSN]8ĉsss*{Ƚ1B9vձ\'W` M%!aDmC'd2S]IoM!`I-G~u+ L=))):e:ul6 trW.ֱX,x<" 'O ?d3fL||ԩSH,d!L涠 Mutt;wεѣ#FxIIIT*Itȑ#N+++KNN2diU*v __O[T23ҬmyO^I@Ԯ<v Ǐ6lw XV/wq<:jԨ7m򲽽ߑyfMDuUŽl`1;'mhhhh4 EWwT*%ǎs Ʋ!aZ&%]5'{ذaz-$裏9~;۬nY}- VeS:`8n"##{92HMM9s5k>3ߝ1cL:U"M:˻[|+w^|Eϥsss/^矯___bbbؒɓ'k[Qos& #:0FKP[=X[/R*m- LgdUαt [*C~U%6跀c _(Q]]wrR);'X&q8F.K&N(kkkKJJC}p j^ [n=~89rĉzә,tnʹ좮i圐 :c{$}8"kl )m-V٢:ѫ4~5+-L$&&jd 6lؑ#GB èT*O?4[޽;222%%e|Aoƻ 2&l! 0mmm!!!]syGg54-j,aso%k} 'H.WW7LV=4r?Lʑ)zH϶cZM"Q@qqr+p]]WjCaaYЕi_ut~(RO~Ӫ3z` 3>Lu>ty掾 W/t\ kzxBZ!aϕz\ᙇWjkk=]tt4xwJC:\X$]reSS)0iҤ -cu\^ ͒:\.׭ܳ22.[,*rrrZmDD0ΝS]' طoߘ1cd2ٳ>{M6ٳK|3sΓ'O>*j̘1l>pdC=s'N|=g|}Gf͚5bĈiӦm۶iU7-,W = 1#\(Z,7xía(/׭aoOs_U&@r1cBaU]5[sؐdPĮOц/W0|!.똆^S# zѫOW*'?xfߺ45w^ qajM~Ԋ wwmɹs) k0:>EmبkSW&)A"8:"L%IX)c<݆B!n %̦Yl/y/d@? //O(z}܋zquػV/:^*3 C-s nnn>}tjj@ HHH`&:ui߅~;0`C ?t+_jծ]<`SΞ={ʕn6mr˞'V7oŋ|~JJJJJʲe~5k,ϭ**==5$i۶mbXѸ~B=1 :T~ s;[U]N!BY;#ݞ\$!rMzuC~,Kqqqn{Nx=׬Yo@cP(z!fdgP^GM3n 0a]wv=q[n \q9s@ZZZO3>A0GM7of`׮]r`/ l߾}Ȑ!r\PW>  2DTJRS;::^իW4={իWǛ1cܹmO?4eʔٳg6$ӌ\cs2aKygB'Zq@KB@&l.S\px yKS04u'w?H$ry<@ =z'֯_2%$ |>\={o9sfbb"477oڴ:666==pybD"iooRa27&LW4MyΎ^;<`GyքK^npѸm۶D"RɶmG>P(Fc4͛cʕm$ph@&~i^n:;, f944T׮|2wS)p`dhpi6*V./?1Ν;yj|]hL?'vǓU)\&5 ohB$tS*cbAf_~dƯ5q[x'骏f T yWV^}Q뤨w;\OP(%voyHM󪭭Z|>ۯ,7 N_˅\UJJ j:u[?,>LԫT*]ñ EYaKKKJ.} 99{v޽{ccc^9^O3 !s:,cz7Yr ~CI9Nq}%(>ҭŭyQ@VFR233B_~V'ONQE͚5 <ڪP(xDZc6o%ϊ_N6mҤIeeenYù;SSSgk^խ^Z,|^OTN4I,Ԑf>|85[,q?xYY{?u_bǃ=&z)xW^ٹҥcǎ}z~1ut<ŋGo$;*z_<T*ކ$`)=bJj Ν2}|qµ>LNT?~AFDE"ļCiّ)pmӕJq26a92i#2YZa\oZ'XJW'?JWo}rcQ7̕mtgp(dpHkg i !B>Yl睒_9UV=2lwߎ;6mv YEu;֛ dl{ldRmeRaaQE½SRR:;;KKKm6MZ6**J(fdd;vRj߅~;0`Kw}C]xqzzFyxēl3fSRR:vG/{|!!! {Y~,qԽe)$Y\Z5q [+8+*\ZRR2j(3@OnBs8ѣG?O=Ԋ+cχK.^j/B}}-[}`GH2`VDZ֠(`hnC|+dBdff&'wuP'zw>OzYmmm\fM]]uou  F>-IN9nCp&84;63\-Z nJrWIP+**5Dtd> s8݅IT1u:nkBO'd{#8(\ IDAT?m'MO WyKfM>itߒs sX_[[{ ddd$%%9sT ;RnCNqjFqΝ\.wɒ%>&&fu!gϞ @0.\7o>q}}u/5h4RjrQD;wnvvd {z뭊M6D 9uttڽ;nx0^k2 |<> ߟtt<ţŒ5l1*Lnjf~))OweӯuY݇""9ȈQY:]uө3lAJqVL(LvOQ{u/yf'YWXu_P&#r p(avAb`on]G5w4!pthy`{N:EǏ[ {E~B!ԭBn}ݖaq8p-gΜY|w9}tD2w9slذ>#Q2HZcIyr-p8`$Jl Pd+dxVv,T*:̙3N-++FFF^pO{l~))))))?ex<޽/z-Z_Nŋ P(j.p{-l&n{o}Bx<à]lu'# b;͚;6ɱcDz7Sp8t贴R6m:y~ѣG-]tѢE} 6Y(EtnӖq+h0$\YY9iҤc;vRUUf3 ,{\ϯk躢6y_wU*u+{k-w'^4!/İmJ-w7XVU s]ګbl.'ds1>3rC}tJZ}aS)ЩkHxPcp8֯_ِ`P^"vJ>?}tعslh4 WlJG(,,j@~GU*Ϟ=fyH8꧟~r]Be4N'M^; =4M}oO'i̙dʠ,Grx """++Z?:2aJKK!:d8gpZkgMB!g ;wd\{O|ɗ_~9}ET;fNg\. R-̎4Dc 3T1"ۄ2ͺ;q,h4p[䵳)44fw .­]v„ ${-^N8믳4))7v؅{؅]>$@ϟl.))9r$^+a?>}< +--@TTTjo3gD"yZ[[u0x{CS)-AwYozdږ:>y:%n BBB3۸7D]o1<-k OA[:}/ȯ $QP444UV(n|Epƪ7EM4'2\> hB!'$0Q@j57]$l9FX,HY\111ZڍZ&|7N&577E tgϞ̌ TH4㧟~;wnnnu?~4˃fJ 11DDw fzEH)lxFgł F]{466-bH 3 \*ܩ/ix:ulPw.>i+p~a>AG@[#)Y3jq\I(PY+|*BCHs.l8qbbbbvvcǼ'ւ9U,((xWΝn?BaBBLYQemN@\B: F#;E׋D"KLF2![ZfY(* mGDjȴӠ w` w&11Q p>)))Ov͘l~ g2456Xt\U0 <0;xL"؏-થO?ݾ}7D2o<_ָu:)sÇSSS=:tZs7 t;1 =MJJBzzz/]n{cC >|ǕwttH%KpԔUD3)6 :ujB^-}8v_`lpG^ov mL~9olli.I+Yu< Cecʕロw?ag M6T*۷@\}=6>}:`ks:|rR2/K$7=cc^aF`+޷~3K$-:0 }z |>ǏojjZ[oo߾|7|ӻW_]n49#X /]]]C9lxj󉨴t1~y!pBK1W>m%"ͩ.#Lgmװ}Mxn#",VX/ |Smm-1f$"%%F><|\>Y7xچwqխ;my#+"|b|H$"lٲm , ]v<;(k"qΜ싉H_rݑI핪ƥM  V$5NZ+Ȗ<Хk,R!(t:>WX^^$??ѣUx饗⪪*hWnw!~9Tի].W?vqe1 Ҳ~{[R$mDt:}7f̌3t:*?KE mƗO~^ýQՄ\UTiiIIىI111AJᑣKNmK^ee̯??d~m]c,K,yӏw~+;K,yRHnMO5j4O̝yr}dY=3Y5齷0uT:;worN>GtM?uV߰{Jrʔ)s}X-++쌊|C;D"={vqqpTʏjwuu}W]wݼyFG}P ~"ݺukPn„ J2##8X0illmaZU*UffnYgccBcccbb@ =~a&33uxkkk3|%.СCB2eJ2G k***JP,]G͛O:ջwymm z8p`!믿^-Nzn17_/^xf|TՌe˖} <%%{Ue`}&8aXO.jx_[۴E]3S%&aDpT.|=b8::zڴi-,۶mG^Чdp߉K ̷o߾ѐ"LDs̼K%I^^ÇO>ycbb|W^yŗܩVz|d f|9g~{RK"kK̊D\W>jy?|0!!okǏ d]tщ'8,0suvvnc~~¤|PP8?h|<(\r/kkk&O|^s{o^N&D4!J)|Xr[⸽93ƃOϛ7/`"JMM?~ 7nܸA৞zӅA繆III[ęm ˺늅W}k[ܷIŞ S_PyZ5'qoY[d>V /D" 7ܠVon+ ߷ߖy<_>bl |4x644XVTp^\O333g̘qUW-YAYO?0 [ZZcbbNB/~UTT|ʔ)fD">EUYYjg̘aۥR)5olljd2 x^oXQ*|6W"t:_DP Jŏjro*p@!Zzk׮-**]K7(@xb^|rt:5`>ZѸiӦ Ξ={ڴi "(6)d2wqmH$ߖcdžph>aW"ag&"[ ?8DyJ>' <B'Mzjro&o޽{_|E~yCΝ{7:өRRRRPl6"H9M6P(w}mr5׸;w~?fljf˖-?Cۘo;yZ\KD"[{7U}1z۷gggX"??^2͝"(&&F"/)-- Nynɤho.O~M7TPPՉD$^x!LI;taiv|{lxݭoȵL"x_}gn%!vG _z0nN/{#r)E-Zfuuuqj}6l>7ǒk,Xv_}wѢE)))dzsʺjkk'NR***FCHNhx۶my""VZuT;SE*wo߾'N(---((={IfsllX,Ug޼y7558q"//n;rHRRRnnf 06Dȵd+d+"fS_DD6沉B;.'|RRRrO2[t:KJJ}~p\zkrrFvkjj @,ƘT_aGGǪU^'ꪫ*ZID,޽7 ɐ•.抏YL>_wwÇsrrJo蟎ʲlffX,7z8q"%%%))I"n[[[O>zkX8=BvNԵ0@8$>d2f۴i'|2l_|d2-YDR_^8h4h4N8׆?piD띸]pBWbER8://37fgë^JlmV7*ҩ2z~ĉ^{mFF#05ĉZ6!!,<~oK1/n喤$~"x7sҠ9t\D뒙E~hvOcs"n^3$N;֯{K.S+*x0/WWWxS)iƑǮDZ/ rykkٳYlywwwд36 ~~yTQ *jܷrƌ3ׄgm~*1qA>G| ij:&01.}$Js̙I}M$̓u[iQZ)I&ro砏%$,[̷Qջ_tryfffTT@ niiihhstHt:ۍFcd2V__5&PvSzDU.qG$jXjS*reY?ZURvwuuwܒ*rY Vk}C %jb3I7=C2~.>(uko^j<>駟޶m ;^{ .\ti,ᔔW_}u񒥢_LdQQQrp ?Z(FGG XlfdQ;vK %%E(-f!@)DRנo>r> L G744436f|ȻhgHH$%% e+&&&!!A*Z,!L~^9y p466ֽ}qqqǻGRSSY:ssI$xZ-Gss[BhIH;::a[=| ijjX,l( < 90IIIzf/0}xb MGH$lS&l]+{gI&_ p; yhTU6YZAɥ}F~g}6[X\\OG2x nک^^`< pϙ3'I#B{vo|Ccտk'b&S˸qʩS}%uumv$$ `Zh2Vqʫ"cŧvYl~D$fW7#u)E)Lym,;`d0f͚AKesU=6EjԖ,ȠaPn{ pid6hlj'B_ԫ}h4Ho\=zcYopTVVg C~lH~+aR3n#+A\ѓNg}}}۽Z3,y͉]~f8+xp-=駟X~p-,//o3]K?Qq'sj+111133?7v7hRϦNxΘ1/ kWܷTѮI{%%%)))=ʻNgr~4k(7x{755ڵk!muVWW76eS^^>m9ߞZ2z8o!uMNoo%'ЬPrI•[WWzcccsss@GqSN=x`FF͟?ă}QZ=DtvP8 G9Q(3/^zkq q qq:Bb]uMŇ,=z:ׄӣJ||޽E"T*ڸǃH.Lno1璲MX|SRX"FAB0Xٳ{ޯZWWg6{#.fgg(?|`"ݵk׊+B>nyj߾}eّnDy/&Ϋs X#555RtѢEp$@$$$,Yl6׵[8VvdcX W4:G~P؞9ZΙ(6J ^Ĝ# '>sFN-( !+'/1L# 4<n8FM"%"OnOL+))&9 g  % 0f!K8qp@4G=:[!K`bF Dv}0rDr|p^opTBD*"-:11*FHDb/vvwUԵ7[k'kEnh-/FH|sƣ[]O&fqj$|aNᬬɾUz6=& -r؂8B8 1$lki$ϺF~.TJ^UOy] :Zw*>3i抁xj-QҌ+C*T^T`o,-g=`qskR*d⬕{ ?~Uln#)`cպ+-xZC0d(!᪍/{pB`2~9wVx]D=!W>^yW>4pHN~!:/, H2d'^dP:T{rcnH<I)9D(ID]r35ۜl8H]X(Vں:E95[.ԴgQ֐8Xé&o [z^c(p\lm*o=%g}L<}TAD1//o;_S䕯_ *&_E26m[Xr<=0i*njMDnHDDDƯ.TˉOKQv\BCM%k9%W9ʇB[k":mޯ:KMUĤ_|*>ęۮƓ'W2F:08}MRn_ %<;mD?uѬ?sng{]v!%q EǶ!"JwȐ3+yR-ue@hȝ>VXf@șU ]n߲J*7%z_p]&k UaT痠Cœ/P;_8v>k6^ٟhU$oqUm|};ԉDd:y&H,X]DDW v}^Vڎ٩!{Vw[w_ 015S^&S^.ɿ)~_xWO㾯i qچS ˱*gd==6T! E<4/8Z[g ūaiBD;HOrpǩw&⸶HKЦQTTF 4vwH1M1tT^T;:w|M"tTJ]G{&c"IKdQ#N\2vTy/c"S/y=RmlBҶ;McI|Pj2)]oXXZMi(IfXqwd=.(%~+ElVev5`WAHF(06rt[‹cƃqaڅ/vQ-0yŧZ̉O;AsH cY!a"ʿ }=m8m^]7"RƎ3jXoO߼=D~WC\":lrC N$lȝ/>R+Y3ސ\vKq"RĤ'θuG,G(`"⇳eLERMO8NU\&ɣS|{~[,:Nò,q"H(d2~xF -*0::+ν%$XܫV 4v#TmjBXP8aRE/}ҦJ:eK cșCD|*1W(vamtt_loX/VhB)K\{-wv7s\ |sw[-?utޅDuPpXj_ϖ@lN[aPN|=qtf`GB=oWIlJT&9xH$D"Lۙ=*LZ%H̙c>S_noΜCw#2^UB8y=e憶쮳}]?Ǡ 9nqw]0afv\oLUԆ0.yN"&eq_?͗kR& QSDy<"OMlX4AVkSDmp[Iy-({{CF}pw/(&bKҸ\Ϛ<놼 ĮڴBL3#0+c^7ED|4ʴߒ8](V`0?Y7Y}wx^dr(V ZvbShP]pjR&]uxC.Ia4̎ZY[,_y%BֆT$ L{/{f7>+7$*J$N(VFO#"JK WM _{E1y|R2#}?f"Ii7wRgU|TKDP4O5}7ۮNYzGsSGg._p鋅fIȆXVSWwUw牉Sd2!e,8DzhDNt]]Z LVlfseŲ=-QMKZ[xcyqqs IDAT,\.JGC^YG;̵-NԌT<`17p98kkvu*,(u;9+˪e/{ TS86W.,*n$HzBapaUƤr8>QII3v\P(\;ӧhw{z{U5tmq,k{R˒H|9_~pta]g4FAa`V׬_<9dc^ *鹸 U*?=@4W.Ǿ6oN_NN 5s`s+~0bccx#GtsUdT?O>wV^0k;${wQs"-ڙ(tou C/~2 Wps #<ڝ|"i#e=t͌ $ط!#>[[kOKN?ۙ=j_<6F;mz>,8R%YTkpτAg۾2$ (!~bq q=dB8⸸8߮#HJ.T*D" vlv6~NJJJMMUNl6WUUY,ZFçx_-"a<-Z"$ 筠C}cfnF(Yv!"ԩSo/={=??h[i 9?طPxWx{0$p/W`wxHTplps^aéM_tjJO_MJx̙j7Fc$ ]|/KJwyg툪z\4믿^78~z {;v,@0+VZ𩪪Zf͎;FaCP(^{5\^YY´F fΜjyVFF>Ǒގ$v#)½w}Y}cY]ƴIfܸqtvKCkrEJOr|u]Ni ?OW{l/Ie|QT %^W~c| lh~jbj>b, Kn83֜[o=]釹;"r:|}6tveo-G>OBp~^70 x{ ~%ɝwIDUUU!<2T*?qDOj2B`SL9B—]v?JsVV?> /_pXEQ"6:\÷$"j`BdHS8. ގ$v#)½T7T#Ha)R!aҺg-}ԾUpBLqqk}UEOL^g8qm m@\(o?ľUJi4Jz#*kVb֔ޫ.XDDV|J=Ru0qX,1UZ ?Dww>(n:Σ l6w_2k֬뮻y+C ïnT*Ullٳ/R\~߿8?OYYYDt?YYYߵkH73BpDq0W\qCwU~qZtw>|7z_aa ޽yHBoG<ۑ>$v$#}ICGR{;4!a222h4l6_a0aYy}j' :=,e, lcu|lo Rlk8Xhx!a"J̚G{?aKOȘ\AŲVE!a"Grfja-eK:;;4槏X,rO=D"kׯwovaY|Z7端kM= C||<<}t")aB1qĺ:8"CCF ꘝ%ΉHm v`qT* l`_XD/q81LqT]Raɒ_1޶`&xmm쁶JoQ{}nw^9Njzʔ)ىJܹsoFG}fs=s{ SK^"^gHZSk0WCX %GŲ"O8b?۶2fZ>fZIrr`""xuuٳ׬6Nc6/Ɩg}`0dgg;>'~_ʺ袋mK/jɒ%կb$//p֭}ڴis@ Xj/h4>$,H~{O>}ŊO?tmm>IxӦM6mO1C^xD"!UVl3gҥK{1GDNN?Ǻuv/򢢢?iӦZz_Z ]wUܹpdgg/^8&&G::~| $ݻ蟿LBD4NdfJMM;y4P_~96lry6n8m6ۺu|=ۇwIk9J9s/ʯ}7o{s"abeeYF^z( ?C<ホt6oɫXFR@o-FgtL xr55~W^)/WN?l(Q. P(H= yZJKK{$ ۷_t۱c_Vo"*++{ꩧ\.נHOOᇥR={^,Ň)fΜɗZO>7+..ޱcǓO>VoG}$}ٳga8>3K/gӦMo-Z4}hC~j>Owƍ|999]vw}{Ϸ ~?@N?@ 0cuS|b"OL ~I {$P(6;zu9iz:q^W C04YP9pt`-9k8(/c5_𨔹L3P0Su{E>].ʺ\"$ЁSJK--!~PUFD)t|p8vڵtRc.\(J9{W<#J^j^Z.,,kU*Y/'mmm;}7|sWϞ=waMV&u]xH8ϟ/Nݻ}7oIOOή~9±PH$g޶mH7d, [|{66TLp8ۇLOno(&H+J].WKKÇGc@- ϟ?kjj8&I&eggt:Ph80wffqʚ#1 ގG顽$"#sa&N8i$Nq\ggcǎ9_OIIQ X,!Ho39sZ;yd3$m|b}O̐*>13Ӊ\QT111= G(ڍ>Cy?O?ZU(]?K3ir!$, _$ط%3$̲lί/pZ>+}kR;מ+˳d2Y[:11aoRMss #T(+<d2^/H!՘4iUWW@rr]wݥV9φc+T* [:X$?{]v]}`„ 1{nX,IC`"ڳg;wzRt…=\._ti||13pUՏW>EbD&@&nsgE'lP*ǣں߸@(Kn+BK;Lw:Nxrt̰Z_P[Ns8_̻d!* ѕWViio P|K.$>T<`~mSSS ur-fp^?<|ڵk(XLD /Ɔ#jd2F3z8lQQQ20Gjd"ڲen/..袋Ν:7it(**R*Cr?4Q/IJޜyG'lD +ߔ{~sz-}QN{KĎ,g/'HB@R)emKl-@KR@&-a4@Bى޶8,˲Nt{NM=o6Ñ\X~Tp /DK. _}ŋ-ͽ>^pP yrX\ZZ0V]`Azz3***F}>ߖ3sˆ?7(DT?~e DEmܸRtܹ ?jmWCCÉ'z.7-6e |nz>55!ޫ5ra܃˗/_K9]*Ι3WxB<"C}ENNN3騱,.2]x= Rϋ}GYgƒ0<~`7gL3:-E!>fC={=V⧙\"xs%Y5%\dbh{BF$w.hA[=m'Ke)Ci75;15Jh"Ǯmn6XdPM~}HVu/}}S T"ݴ"$Ҟ>$‹݁ NS@=;trvFq(m+it,M:m;+ i0ń.I}n*3g]>x61c˘[G̈́J/J"H$l6P(B"2g>&еtd x; C6'|q G7oޤIN8C An_hmmݼydںu}GQTff3oև%a$Gc/ 99y=Wk2Ex3a.`ߜғlk(5$0=. A?YCTºHŽY[Rp"Fջvvv5%%%h%f+7EsÁ4[_ba[ZXNeJ d2B&bƌ&N| f͚ŝ˿?rW\y]w\fiZ4{///o/((xꩧ}Yd4tǏ?u:+j+YP.oxbQQP(C`||MΞ=-U!W~jwQ*4^2~]NY{:, )HsxDRRxXTiz}[~$E kn\ٿaiY4Vl6_%H23)&See {/v7L&WWWMW eM69{7??GyWb::: >^o HիWq-ny{ܘ6lX/.....OJJ(Vh4bxM!N#͹sۛ:+l;5C rB)lыs\"plf;aWeB }*-Vϔdyb0,NZP8;R ݎ7ƅ*֖ Y*Ucұz9dK?uJ/~t{H vm_v6Q r!Ó @m ? IDAT1#"3ޫe֓sN˘ 8.u|[n58`-^XPٳ\л(lۜkmSlC&aI8%0>%D"R+%<~_u8g+ejT! $IF3~ ."Dj/<?Ҕ@&;O|:g*Xe0[<^ͤ+Ku4}R*ef&+>^"ZΞɄx#]|;vVfW`Ao5耲2K0I$^%-#w{i$EiJputP8ѣϗK./C eHnݺ3f]?@(4'Os oC, 4mZJeGÇϞ=;??ڵ fQJJJ?Bmmmqq1,ZhÆ P__A!>Ov'9:U*յ^+ [ZZLVVV*)ySIT kE$eeeZUGYvv6dddPx|T.~ܖ2G 9HI8mM[ r8R[,I!]0(6;{oWX{{T*ȐR&q\jnrNg`YV(}%y6nd&$@B[vYYa`m7Z 2H*H7+qP [nټysሏ9RWW}O(~WaAUVVgeffΟ?o.++{c_|xbRO\W.;v,lܸ1/UUUSL)))ٷow̨Tw7nN[jUffG}߆ ٳgݻwx B7|:%%%fY}[ShMXn{ϯqݡN|8!V" t;9P:ԝg^[kb TLx$A.эƋuC/ޔ HT:_VE"(  S9: jPwAZ[)͒f-V[YVˆ*`w9V7SҔ\2v ÒMFPbi[*L}Os=3gT(=_jmmrkk׮];o޼ec~J}k/3g'L-4 7nܽ{Bܼ֭,{lŊ]w]\\\.y677>}zЂ $?[f ,\… pA~7%%ETrӍmllo˲''NDwM%® 'jlUV;8sZ^lه~8oSxUa/~pւTh^;| Eây`\H%+ER*ѺI>UgI(2%YX&\8McͶNgw^U#NWNKQdw`poV"el˛>% >3=>h )*KSҔ rg:{g̈9xT8[A @Y5fkt?~b >d4СC*jʔ)EEE999g>pi*BY4U}(ʚ3gNWQ:;;}]LPTTxM7}o] =gHX`,˖UV gҬ+LTJƆ0dlBڵk{-|w}aɇvz\?.oun1Fp/>hvw#ϧ` ,k B$$0;, #S%Z|w>wޞ{ݵ'''Ԁc(۹sΝ;{?Am"(>>^ղ,t:fd;l~뭷}LJ:;;ڸ5noQYYUPlڴiӦMڤgtB>xؾ}]ӕJ%7@l}s(nwwwwSSvP??p(/\lhPONĩSzvC vMVTTtc٠ԩSeeeB-[!$Q4MWWWweYrFceee233KJJ>þѢW)3;yb\ZOq|@8&{lQF[7j6_NL}=*l;YA*!IRII.N 9b#ڗ,qM?7lCy9?O:%ڽ[p$H 9^6V4F124stdѳOp&d2Y!R® W9%1zp8{n {n\Hqj: eYruLkg)Rhy.$px=E} @CvklP H"Q!l `N/ӳ$GF?aVgvU]yq}"1#"xks(H`;@؅'O>ADNNz5־p´i^>.t޼y`6qx#Gdffjڸ8kj)cú].YJB%mo=:aL"էc},RDDL&|Z+T$LK$ Kvm;!ĂnaLQ@"e^7lj0339o2qSF<TyOr 99,A0j5Ѱz=K`4f3aAVW,+T*꼽v_$,BE@U`]b@aWJTz 憆U#N|: T"LRDYsKփ5)<\+'+H NR33"61JtT4\.fb !$6S` juaa!IFpq+ erV>~vυq)cz\1neIF^\Uxy|u?E,.6EQE%J0jn,kpE|3JRTTA (#m~4%J"!j *ܷ b(UYil238ȓ4{"T-8jzf+b}Vf ɠ*1 z!E<'q! R"0PϘC9D HWp *)))**ٻw07ʰ,駟]zks?El(mP(/_M.v>B=ZܵpzҞ?k^?~#epOzpC6N5o#A`NFr|d3 (jgCjt !BbU%e{y M 8@[RDF23Mƈ#]-nz|=& AG=7-yI !ElAt$ iJVeXW譝Zݴ{ydhyl f  dҔbiqӛh94*1%IxF/_T9t6ϘC׷F^o& !!a…ى]yhL"V֭[q:$ԳgvWQ;::z6N;w.[xm{OAV#)_ޓEyi# [g!BepSaVq8:::nH$JJJ斨n1,Ī𪂸tX%v|Ptk'IhzlRARD|̗x 2!|Ӌ3Ȅ)Y)QܜiϹkt est61,h%{f#6OWW)D>w90:%deKr4FЬD@Dr!K_&!1#֬Y222:;;V+0*@zw=ٍNɋ/d"ѥjkkwtVr t o **!! ~zMWǙݓ P,[oG~=!\hp'B!Ј*̍- C\\\VV%qԩ'OF5JUX)B5Y=5F׹{/bbdję,\r1q L~hdC $=#*v^IQ1`9Fjsc$N6b:c׹vGzp4uRNzr;h=b/3fD~x d6%&&&&&644߿h?/(h4n`0 wR1 PD0e7tkUsE./jdF#&qÝB!KyyyyypgV/޾aTR)M`0틃B1`Uӝ498O|pS@ Z, {VMc_~k-kqܹ B!tu7/j\Ou;ނ'u)+}'#j.a!BK^FvQ p5(uA!B"p[%>܉ {Fk￞+y}Ԍi|uWlsPⱗ0G&djɽ|7ZKո'0U}xG*B!B!B!B!$bx\Ehѱx;B!B!F-1T۳Ճ9c 䨥B!B!B!B⥗0˲qC C7 Zs(nʀk&F!0yꙶH$2L9<:"D$㝈!B!B 5;Mg&`7L &pC +P!B!xy׷@~xwz;N!B ~G,???15P pp{|㾢CDj|de#a/0aF"͉6#Ra .`H>x\Ehѱb B!B!ɋi f([$cٔeٶgEc/B!B!B!BcKriO f|86ÝЍ]8g$FDGT|lE%|Ǐׯ?LQ$M3cǚܒ|SY{s` .smm_|<£Gx4bk?[mw!o8n>:"s3<K/\;=#pUKyt; qO fe${y F" e04:9EK5̧$Ij4)S:td2 wF!B!PHzV|yB,ݞ>?NfC!vGxۆ#_nhF|Ǐb~ 6CmGhFY7T_v3ܩX}C&j!Y s=ghI]4=LAQTBB´iD"ٳl2!B!B"B!vL=tw)jzJҾpO;geLu!݉)&32MkpMӭ;wkD"Qfff}}p'B!tUށBF$va#'266x"@ŗ$I4~7`w|y_׌fdbdcьwXwXwXwWϨwXw|<,IE!賕1'3.Ş.hBH%a`Hp8N81sl, ,۬x}þr=s (U,fDJ5D!"׾3?$gsAżߞV| yB!Bd*S:틫8>{ڙ?KسRRIT*UyE]ԶL655ݳfN"A"+8x H[<:bd6#%I<2x!`HP^JOQ˲{ G12|4q\?aҠ =rxoXwXw|<=ǗG!'UKdKT_Œ0IIrˋb:Z̊:8XVc}5dNn6F<,Bv=o|ŋVS=|$ٯNQL!4WB!B!œ5Vy{Ck 86i`_?ƒ0B59hq (H wR#WGgzjiqQzڦwnwQzKBi$\R$_&˔xZ;^GTX%'E#zrKs |ΫzaQkּ{qԶ7wm?zVqhVJ7OyU? &$,>}Z5ȁr8Z#ȜG9AVx)ьwXwXw|<%)RDE< QubWM׏gi'|>b2d ZSSSZZ=(,m,>-{H|M%  $AwR_PQ]԰{OO~8?y]YF(Q$y ֗u4w:hD :5 ⒚ m|42ޝz{DSFj&`j'Oyk; B!BA*N3{Bj`Mα%f 5pt%&&>C4M{<D"?OGЛ Qq$B(9>([<[[ W==USja8VarEj{.th$^8zփ9%GW$I!P$t\wo޼x'я~:#"I#@9>Fp-gPMZ:-o/HgB B; V1tMf}VS$6С'tz0O=K5\,Sы.l}y# YU2ţ+׿xr]l㡝)=>+!U"wX 5"X l{AH iwE?;$A243~F?!sl&qU֟`ˣ]$>Ny黏#Y^V7ϛ^oEب<Κ55wUAQwdmռz޽6YmKUNI^]~/ ?ti\WnhV͙sԩ]7\~jURo3g^>vϟ߶tis;8 ~cLɌB!B!A({[k/%aHkKO-?<[ %cڭ6M')/ gi5/-_=~-C) m۶I$ܹ6m""777''GӉD֯͟??!!l6QH;Ճ@,02V|IsW~O_s۩a%{ E^oWyWGWޣSket" E7ҵ ّOoi]?_3|9:I|z ??a.8$. ^go.4wy/OyoFN7;F7۷|s=@]/3 qJcv㑟Lwo$fY8ѸlYcb31ivLQ|dQqٲFgm xԌ, yH]eٲɓ ?_LoC!B![* A!wM?Yg[i)%cr9"/Q st MOifm۶ fϞ㏋bͦP(֭[>"<䓹 ønT Ν{#0*)J-Sw"#[xP Mfc{.)5.15.o?| nueb]ʟ,h* ٓbzѢzpa 0 cǥQZ7W>{V_L*?HF?=w=%>_N؀;ɸO?ݛ-\xbzmҿ#1cӭ nym3ft8a۶4Xv{Μ2 bEÒ%M￟k2kzbn&uQn:|^|hǏ|Ǐ|Ǐ|̣9௞Qy4#cICNt9JtT(۱<4/xȲX؃UTTmmm혴oqN?w_UUչs &{|Jr͚5rˣ>3p4GVݒF90&-'/ZS="WY5֟-JU;jH᥂W0:{o=N$Aɟm?z$A<q gZ^󠀤;e*9?`+}qgs&;隢Y;)w~|5 ~t!ZN麲ƹ nmI>/ݹƂkf:7Co_Ғ%3gv*^z_$1, o9(6 ~c3;zq_ʕ0l~iyF_^*+U۶ Y;8"ŗ$ɀ;XE!B!Px, `@B[ *H2,11Q,t9s,[b۷Ͽsi<1˖-ov]]tuukoʕ+%aa|>X{/33sڴi D"f$Arn\.1,sb鼂o߂F$:%?kdހ7Gt;"]ZIZrXBa07 ?@'NK/ Kx?[g/}vs?o^, O$(NSR8G_}H[_}}U'Dt=W?`^A3#v= r~GS@UkL,M%ἂ1+_}Qth≳/,),KsvԴ7=tᄙ_zᬂGЀ7]nݚdIP,\‚sZ*a$TWe,{'OVR<ٰY6@}Q2RXsy[e @rs gjr/D x᠜NJ*33m/5F{~GT<a<$O={vqy4#?#?#?#?v33*"?#3f侢Tvx Pzo5)E׍)ztήҥK.]zܹ7|3 ,˞BcUM͚7h덽8mѸ=PyyvtЫ9LNL+A4ھ8{k6P_1c'5mWt2≳OMPǩeJ|c;˚kxߓшf`NekqJͰd?_- pkbSUEKm}w W,+^:_ݱpYc)ƽ+(W0 A߉Z=Hr,ou;^SWʲ]w%-`2IINhj ²,Ϸ$&:Ne Tz(;4MxR__o4/H<O6lg6@&E$A6ArX7}^6Q!o0[Fo@ǏO>nظRs*ʝ;C\s Mx|ʄQ+&+k5yܧRp,"3/ k>1G7}_QbX<\<61ixO`(3ַιm[Ȕ)%bqE뙒v=7a L=n\?o;hNm&I+<=[lnUNLZ2hPEhhca/Ϧ^K+9pN&xŊbO44hFьO"gӌڏG3j?f~|#)̨8QI֚%T5C䱗D"=Wn)gWI[+u%3]r+wrڵ /Jy*7|ggΜ9k,zUVTcǎZm۶uֱ$8|yCe= ,~0ֺ4*W&oϞTUղzO2?,)hvnħY/HP}qpD$Y%B1̧|P{;t2ܾV? ɱ "l'ܽ7eO`CSc^owuD!i)EysjNnNStI"'% ץGHZZKn%}LV=RR<-ZW4pY=7nu{ RA[66zp}so}IBy\vcyhظ1lܸ棏.~UM!! (.X?0~ K{?.24hFьO"gӌڏG3j?f~|#)̨8QI֚e2EQV @ J2ItvM52ygW,,,:u )))F+խXǧ) .rss+--miid6^ p9BD !OjeJ;hQ"9fD[uf)_*2w%XϞT|6<"~|'\HƧQyY-c^xI=XpVMK." k,6 NJbXX +[=\|JlQ)?ܴZW>}X Omd̊g]zzlxP^ɽx vrj4]`yo{ v;L]Rb&g&omk}G̋u&92[s{֭F|a+/6f%?L n}_qHyTᅬ\!˓2cFqϞqJVS2Y+#;?Nxt>ܜVY)OOTں~}ڵ(Kaw /@ @ @6MR)#J ;8 fTWWWWw7IN..*9u^ӪlV7R'YXCTL Z`*Z 6q Q)8z(ƐWv+s8Y[6tx$'|7NJA~Q=3uBOKktRmA: ֒)ٗ`JE^2?I^M˰! wlQ E"*FS{ϰ)y^<ZWhR63g&^-whgxݸ! j8otNJ 3ɼѣK=X.f/,ʒ%-&ZCSX:wjĤ̋W1/T[cD  RjZۖҲ; ?9@OҚׯyeѽZ;~7á8,~'&0\V=Wwܥn)1>|%ӫi՚{лN0űs5偞~3K/̞g/7+=Vt-4 =4jJ\**&A*m]VI|aTPpg}fۆ aݺ)4 JaM _ +*]{j4Y-2eeo[,nVDMj5RPR"jIꔘ2;@ @ bs6ݎ$%l+(6w$an<~(:"KO(kPХutUtV!DJ̧Dw/siQVL6VT>w0I4S{;7>$jBaRz<2_-jI"oťRݺGtv0IŽIۓBQTD8~a=le]y7}עlsGT(ZN#4.^ϸ٣>8{|& CQ 慟׍2MBE\.W ay\>ܽ|xZZΗg=>sฏ77xl=YE09iV(8e*76rduh=ǗFzBtS*?y,wIg򚪰|y&cqvN$wьڏG3j?M3j?f~ͨ8QՃԏG3j?M!$%l]-ЍZ]ǥG69s T6Ŏc`c/l\r>~kҽb(Z ^U Z +RPM*ԃe %5t0P&k&le۳~)裇^^VtfaR'K"|Zw̒~Qcz xˏ VS!u m0vlI g=}E/)zo[" \ّr;| f6^Pԇϣ;v?lW9l~01faJ) E%^mc{4)[ay|ABH^0z~ƣ>n ~?]/2еN ˗ sy @ @ 8+$%L rG'yEZceqNnq:(06r;'ut 㺁 PQYԲ-x.u&VS{vȖvZ]bbcv2l!uU/Vxzlkn>I<7=L9/*1&0|}Ɩ&b-rA ly_)B@B,ܪi6K@Jέ ГSZ;ईy0:[ԝ].g^2wWU,5l~.UӚuPl?۠1s`ۙ5h4 M'Et5{6`s(|;4Ʈ߻D~wAhL&G3j?M3j?f~ͨ$r6ͨ8Qq4hF7r ŒڏDΦِ[3gv[b0Qa蠇(**34£&/mutç=e8i):v?Z5+z'@={,w,M.h<۳ɮfД>ߚ|lyw?{uHνϾ5JU:Gum~oêvqdB=Kk*],1C56l;ԇ%IrV{ڥwz*^V=g&>MuUg]ү|썹OIdLi]d &\)-VެtF=s3^ IU}= wYW`OJE9'qdShU<=çZR~}z䗗|ʆ w-Ll ;@ @ ƭmۺ6.<˧Bօ8C㹫 OH _ЮNJYYr`mSd+]u]]&չO4¹>b~>O xU MfW_Y?&jZKˌ莍 po+GY,~T$yu"jǺZS9OdwWr~FwWL_!XĔ7~ñ;gḹμfX1+yԭYղfˣ+.K/j;tP 8 tr8Qq4hF'iFьڏG3j?%f~ͨ$r6͆YBоVU꠨><ՏZPV5Kx.=Ds]O\ "|?#`[ E|5w핣~p6ܼUJK?aKmUy9?ӪZTʴk3rl|<7aoWܲum1_Yr*b0ㇽ6{Є-Z/|fXtGMc= NEsd| @ @ q݃=z @pyRLmJ[ +{ }c:jfQ'%Iݔ tlOnBz>uPx CR̓J;n E=}1Y,\+U7}ڊ$$9_{b͋'}w,=}_8']:I% lYJ2G.88T8,aͨ$r6ͨ8Qq44hFьڏȁv 3j?f~9fCHJS௳[b My=byI^q+*6(huR+BUpp9%J*sq\%X #.m|Ù,$:uj}\x9t<*+>ā1AᏏѦ-Q3_?j{TÌ;ě@ @ @pd)33R)p)y.AѴ@Y^^ l@ S&s-jUCtF[ rRb.h L4iNih-SHZZKҷ7&yKJywOߌc!8 VskV=t_-߫zOLHdu (n.3p8Zym0H8Ve?tr8Qq4hF'iFьڏG3j?f~ͨ$r6͆O D!M_v7CSTн,o[H`u5;# 5 $+c x ft/O_y0EQ*EQz0V (63?oDd0=WYŹ=,9_4((ȶvt"tF3 |R&"@ @ c6%LQ!$>]]Jķ4$94+L 0M! Aq% wXeҥqQ靳K53j?M3j?f~ͨ$r6ͨ8Qq4hF7r zŒڏDΦِ6):H$u5wԱ#4@b`pWppP|+-r9~;(l8.~{['ǮO8aߏG3j?f~9f~ͨ8Qq49_=NaFьO"glH0;G%ReyJlM 10!A;8G?;G+psLmzhTWҥ3dag@ @ hV ܺ*6v&H   s%MGn}6ܕlYV5S99 ~y@f hF'iFьڏDΦG3j?f~ͨFWSQq44&%LST;Do?t8=ͿZr@υSXYotk`?ݾs?h͎,C&|{^y-ˇ6%O @``d./@ @ q1Z0Ee`TRK\) -FX)/ #u~qA!|é:Ee1X pMFxEypIvqnrr\wIu.LT 9BH $G|q MmlE||ab;\%˳DZur8Qq4hF'iFьڏG3j?f~ͨ$r6͆O  1?n.FK˽![ԭԟ?nsy\nzqZd|R:n;|~زt9cBkʿw^@ ~r >QC>cF_ҥW04@ @ ;S sV'~I[&?šh.]{>,`k&ESE|^[k.IR>\i&r@ ~ IDATR^}-\ҥ+V/sNL.o|%˳DZur8Qq4hF'iFьڏG3j?f~ͨ$r6͆K [sv\PZPƔxH':Su &ň.\1k@TTPY"L);U>S"G6 p2]ښ+?M"74w:D?N_W##{3gm~bS?P%ob6iK#ǍIٺK|(U't_mi|WfƼVjt{xg".Z8:io]wDuAeDytC@ lYnW@ @ @ g ?|Tg\OT@yׅl]JxVa+L{A&L}Cz30<>CQ{/Ujե;O챉\b4]f]#h]xi@*G{dݨ Ptgkfi@  q%@ @ 0~믃O-4}4T-Mw=܈w֥W{1Z04\J@ ]v_6j?c'dьڏG3j?M3j?f~ͨ8Qȯ0hF'i6MJi)M1ϟ'6z ,Y+˕EYW?[ؼ`֧~ՍMeuLc3oT ]V&xDƂʺfUk5ܬٓz]Wآܳ\Nzq~Ii߂\B~QU}R 5; 4~ϱhe5f*e)gix@b` 108B 烌+vp~vV82}qg`ۨ8N]q¾G3j?f~ͨ$r6ͨ8Qq4hF7r zŒڏDΦـ6)aEnnV͍WK*-aьO"gӌڏG3j?M3j?f~ͨ8QH8G3j?MmR5e}Y:5L{U]afj'ýzǕ{enпDrt;jխM^TI ^N| {ɱ2NhFьڏDΦG3j?f~ͨFWSQq44>%$eؖs5cQRb¸SbDLH|@VɔGQl6r<@ 8=}qg`ۨ8N]q¾G3j?f~ͨ$r6ͨ8Qq4hF7r zŒڏDΦV7R hi޽{m ǖ5Yv!}?f~ͨ8QIlQq4hFьڏo@~8G3j?MSkrkrى@0+I/ef'JMٞwqJO$4)' C*%R5 ^Gs/|w{҉17vކWS&5}@vs!D4fOX3ͨ$r6ͨ8Qq44hFьڏȁSQq44`.D t!7>J\Z.h`dn->q\^1cT*mѤ^KMOֵa~-BS, {@;<W(: 7PimѴs8 (,Kqu亼R;gFDZur8Qq4hF'iFьڏG3j?f~ͨ$r605>b LOH֐>S]}cQVؒnTnۿMU/i.V̈Wa mKG q_Y'Bx0{?nSh.Zo:".KQHoZDz~Tn]q}źxԣW&jHoʒAg*ѶuGxy"M륚ͅW~vFhO<%l] .}VvV0: ʝ/S5#O$ֳgvW[V^7eLpK",8ᏼ P@A',սl8oZ}媬]V-fOX3ͨ$r6ͨ8Qq44hFьڏȁSQq4ȭ4+J'JkjjlpKRF}J'* Inڠ&XMQ-B n!g\ZA>!hUeWhi{(ocl^-LXN$TI3ֿVg6%lee=[fܥby/ +l٨s /zw ޢ^3[61Br\6maw).>>/'}ӕLŒ 8.]v4v}.Ք6v );g{8_:`K7vKkϽ7jv8Mfۇ|щIOź@F}U+F<87_;d'wBP{x7ߧbv5;qĹut?@f hF'iFьڏDΦG3j?f~ͨFWSQq4ȭ4rD"JJBJHӴ\.gJڦ)4dP $- I?bx;]Y8&0j}ff\7Z0ط)!3ui^اDruk 3ϖUQDG$ _U %%N8A*[4)SJY ϧl!eeyΖi?68킑N j(WoLW;7ڶV:#|י'^נ愗@x3glBtj^gn8OG>a $%̢DΦG3j?f~9f~ͨ8Qq49_=NaFьO"\WW'8@ hllTզr(E1 M3KF4]WW>%l PeEJ~OCk3*^h֨E\P*dj7|f~|_?y}jMf,+#^'hñ44V4(%l,#aRqӷ?ޯ|~77LuOOKh)s|cnLAR"ql_I ?e\obx%qDRO5Knq##yǹKܤ.lihHo$Nʀz/ֶ̟?/"P#@nύkr)5{?T8:UM6* >/ҏ}~ĔC$V(TZclᾗ46]_& jQ|~)1:85[9#d^pbka:Gw>\5fBPԊ \sxKAZ3B5U_1QI^AQ2olĞ+t'8|3E[)a`„O$6ط[HNAcƂˆ? VeVWɃ/}t@TXW+jJʻV{ӮqH}̂ kf }¢DΦG3j?f~9f~ͨ8Qq49>q 3j?f~fFsM777f!h&۱ E}}FqrmRJMCup\lq?xU^]vN|%v4֣f yn2GiuhTׅg^mq {ܬ#Y|@-iK31t1σz_~L 6D#!8 +O o|BLoI=OXdx)!%z`Ɋv^9jE.z: ~E'7oȿdsTZMY\x ĺi!L:YQ.%<60,hUI]nmlB°Sv~}᩸Ui2%l+ons O&޳Zh6lH \}qO?pt9o|a$"E kMFC(Aai7 $n= kmms) WTM p :[\Ѯޕ-.]xorBn\yg"~uh?̀ur8Qq4hF'iFьڏG3j?f~ͨ$rF.d2@^jR)I? IDATuEh7Aļg&xaʠoX-ovWxlWyli Vv>ڄ+/mQ4ƳY)5"SDH `I+M˛`ԅ`i pq='.򸸱*>ɱnFYu@kO#_ b 'lV jMzo.m:Tf1=<껧$S/w{K*[@ű|Q b~Ҩ>|NڛNhr~6~hcT{od |iWX 3qGV>sDGnTPʵʦVUqĮ_~|Uq_f=zk,8;n^ԪijPxď 3~bGq,b)?\3\i˖6xafww:Kqv}lO([(]VFz'ݯݎ3O/L@U|uoX_QW. y9.'7v~P~X> ۓW=_UܤQIa ƚiys`ޟ0[<ܮL[5NLM)6R߲qҡڴO0lJnjG3j?f7Sn6}26r+lZsơ p?"wBWVkjj:s(DxVtvɉ":1 6;ם~/cy x⪆ -i.m)c|?%sBU 6AI˪Zq-ɏ3M7Ul޶G6wt1/0ߪ]۷|kR㓞}d>?휫ݼZc, l@euI(b\TE0'$O,=냑}7] |96~(P:+/0"'IS㐹>R]jv0\Fzv z S,SH%.[)aKo_ث/d~E_;haH?je^Fm?|Hڮ>:=4ᾈ>ON_iWϥ8oEFhQi5g* U >}FdsU~4?~ܮo/-^>xp#"+t`JH|wWPf|y}'Q@M%眮|o83`iNi*v]aPj~.U|5g\3ķ)Nv So8%.wS5?rl fx+st⋻XV6[=~pOJ]x_Fi۶Q{5ORKQMBRaF۾5hU`ƞ5JFJWOƚiU$>:/v&ΦĪH}=?3j?f~Hʳi%!zF|Uҫk))I1_?:I([[< bcN0܆ F3$6t~X88Y_>SXU?lZ{d?^oz)eGWs Sh).0xo\ӏ\>S&u01~V<]2.$B;Χ .7۽JYelc34/?0)&\p!P:/\^3Pu~f*;'B$Bo5QMIQ q򄑮^y3 a\~vOEݙUYIúsC\y`[QcF*3[;: :Ұe_a9&h ڲiG˵4]leӀjq)n%fԖ}qlm9㕚1n>S##fSÕf' Tȗ5օ]ܽb_Ck*Js<,<̋s9ܑ_UY'Yo{g?cXh\&-wF}Q(x<oߞ]Q*2c㪪rss %F#;Lܼw2Tw"Wߏ߮2EQC>>Qvl6z E_P(Z-SI>1#G4Znl"1IrX'FWOLOLGCH<[=j`O,Ъi81Unja'zklOlh}bUoꈖ\>1Mɭ';Nh5e;m BIFG]:.WV}L$csξFZaVug(VM|: he ? mpS#T$`g}'䪷|\ͷzx*Nh>r|Eˮz[:6 ŋxS_?:i' !^79Vx[?OOgUrK6MQP݋w9wJ[5X *ڥ?[0b[u U$}F innDY/OF@%nuA;Ϊ@qKOl!F|qthBI1},||saUli J FXkuhu;!dи N~->-/\~iĊaXb dHw846*z$(=/?ɧ_f^ػ{|,EqcF߳ydB.͍ߤ'F7kE>U(voI֤}kJvGz=O̓O2xQ]Lm=Z;U-w{|ȤCwߔ{ZRđ hUI}CWXr߲ {oa-V/1%SCB6g,{[w"(ʠ" GFYpnqE=Z4.=bvfML uf}̆i-Z-@ܮOT{B {{Z0P.{w$Hw[|ӻ skq?fy&;7a+D"S["MRy9pBݦqQN+[[zzINgYU;GC|^^y1J?7ɪEaDwW5nrˮ  ^8Z*jpmh w~vt4^ѽ 7=ϙ+wsfWo׍3=X[֮gPźꗔ5wf|vp~p_L(69Ο[@Q&V nC$.|]yUFKS_57ԳVi491?LѪ>1%lVk;E,fӖ +X.uE } +Κ+Of[f>1j֕[ױFM}:o7G8k|'4Un;m>gr3#UoNJOR+p`ٴmvΞ_x'4uwB:OP|[t~3v;ULI[Nj1Nؽv$.yo*vǢ0Cvi5I 6kϛvqohV7g x:U[ g |fS\7*%3C?%,oV)՚~mVT(U+P^;wD_W/GljsWt{RhiA,EEx{ 8WO}Eq"l BKK޻IG4>zh8''isE<<ZBq-guNdOv챹Ɣ+e3,K^CtwhV՝1\>u5'ڰ&IC<i5Lۖ^mWW܀z{rUh&o~ҫSjtas٠wQk8,=tlHVc2Zz7&i]n| :}Wi+c0I :mFU,klvEʈA{g>2+ JƁ+2LV3<& zUjN/iJM}_K\S>/bsH%7lᯭ(ѭRGXy1mm{d]W7$;tFceMK]/^R9L&zC_}||Na`}S4?^pms|"l↌P*oCbgI06^]Ƕ13MlEۆ1حr[9lRm[rrׁﭟ::>C2:rt1[C2Ç<㝰X >!oPj#~'3h Ce|_HF ޡB.C1/B'ŴU>>"ߖC bR41bKIDK?۾+5ar17jY}Ws69+:Șӻݧh7Kyk=G7|DÉ4cbIݾ{uAƣW:27)em6yzcށcPWߌ0C\r#2뽗媂?Su*[=64nGd{Ol+IH{J< [zH DmB`_ofwhsZo\>`<>syz] vቬt)a 汳;&=.q<~cv0_h7kf/%=ΐw{zײ^vbf{oUvdSbޗ` *8h3–ӦMڵP4i{Z6(;w [RkqO$s'o<- 9~(WrSZƌpg ;pDu+w BYCVWG%_?a sk  ==d{Aa^KR&LKr7~Ҹ'fwvef005I$rM fJ (us-JŕWϫ,X`ĝ'$FK㷮ﻑ7jV?+VM{d:c8 ~!O;26p^AѬTV^-WK IpxO<ĸxQnɲriqܘPJi<+&MR ّ1vbM~Wuy#@[opSoUor͹gܤp;@Hp6]LvN)5G2j>d|9d|ɨ8Qq$}7&8Qq$Ω$[Eb;>;1usGzI?KЦ<Á3fo9.dOQZS(3nM 囋'O[K|ێW5`śE#ܗAm۵:CMO̒Q?J7\%IPi\,՗'> x:r\pxog*͏C%kCP:Wf[M _[ pwbPQ-ۡ-}Ӳ?i=3sؐt2']{ڼ\ItU FȜJn-ھ{{5JgG'u`v(1ǔ?rYW.?}&$ɩSMˣ"ã"í6F!X3Ή~P҅=iƴ:WF9ǹ F-EK uzle\_`aJ2٪W)6`$@ݹu39̓'a1m2֭286kݣy[{ZT:LcsmlT)d vNnJդj3 <2 IDATgH|dؚW ڏ&j"DW8SU`vi[YHj4x^eVcq 5E5J ٸRT}=Lc: $'w=b0mNHr6K?i}\҂__`,[WdZJI}߯oըz\kvN HWoq%~kMN)/04d1-YH 0lΗk/.n~ubI߼uݠ@TF}0ת.fv'AO>3u~B`;V%ΜZZe]N 22s6+mo;ՓfYvKP }xa%5vN%5G2j>d|9d|ɨ8Qq$}7&8Qq$Ω$['ktg?! ^=y衩c] kor'+f&E}Ɏ$?#ą?5kS,6eOe5ȔK'FRIQxFU&EMIQFܷcf QI=L5U-2$\m*uZYryNܦϡӮ4g ~<5ZV#+ˈ >AqDoqδ^{%F&;Гs*LW'mC?jeEfNkQy|Xd?r8lcOn~`^*3goQo=a}+ή9);%_j̮\>JopbBuN=G2j>d|ɨs*ɨ8Qq$HF9ЫG5G2j>JzAW4ڪ&yZqLٱD@BiZQʯ῝}+D}`PfRi[el=V؝Юm= AnՍ c甶*g,v[\f2 qQC7?Hy"OOH< rZGChLĤOWKW/,RWX'~u]WwzkbH>z_"K{:nH޳rU7x#} ,EUIՕ#Wz 7NXyuaZRKVNv|k$,٨;^?\*c2{_jgw웽K(Y7nKA>z#s.xxEn^K/l\o4H<]%|ȰsE6m@z`xj`YҮ Jkcgo2[ר/<uG%n }}{Ė:uи:cQe7S28-?q8hG:s[5f?g>rt{as7w,3d|9d 7=QZeyU B? T5o sDžW7f^?1>˰7GR5ic+_\4:^1 mŒ+2 +CЇxedzy0,?-SqSI=vNOٳו8}]C|5cS{2El~?H HS_!`alPn =?aeLRAKJR&m{|OⳄE?;GR?kso}%7|&jgd (ϛu v:+H KM>"ё:ƬW$ŹV<^YHuWr) P*k%'ueg{Զ9Lȡ'ToGB%.Cu!1bk-jՉ$UaFM!/C'uz:j ef&A8EBem|ɑYlIzKKeML.L+I\^??qL׻ Ů|;CE7~v(iZg6˺п)S|pFІTqu[k^s0.Օ[Ryq\&gRkKxLXgO&Pj5|ۭáGH˻s:J^|EF:e!>0 w)`% Ξsq27*i*~+Y!-xa`o<s-?3+2;ZEt'E3$"X!>X%C%td6]LF)vN%5G2j>d|9d|ɨ8Qq$}7&8Qq$Ω$[h fJ[feX$76$ mվ)J:n̴VO;GVCz]bZyH$ud7tmegz۲,ʎZFȵy2-tR4EKG%N;]>8~!|;CMQ7ܺL^;;`?{zy"/tۇKX4o8G$ ?;GP4~^} E˓\A\Oe6m{m{$;y<™ʂazŚ4_uj~.Bb(lfą~te XWwF۱][WvUx1R)_$勌Պe_8uY/􋣧9K%*0-`káq-R[\)^ybW&@Ջ{C%.p Mw\v~^niqfyMF`ғ#<0=Nܪlkj/u]Uɴ͉qFs&TkBݼFumUedXA{#"UJUɦdzҞዢr%e<}&@doDÒ_;N> q~PSԅ`hʹL2ru`JҘ&-?jU,Kb24dẘ s;hF!ў)6p&Ժ:tM2-BpбeJ/pbBuN=G2j>d|ɨs*ɨ8Qq$HF9ЫG5G2j>JY^ÉGz-;o.f1혡BaM#]:]gR~鬒7Of2-g[2]g=kErUnGӉk߲9bg[/kۓ7^3Uy84}_.ACzBұwP Ki!۴Ũ3͢܍ON^%fqoԶs$nWr=%m}_tc37<: q8Ю{w^~ssfՊGؾ"Gw$VMVjzն=plRJ'>X __-UkC=VS5{c]myNw"TEAAbg0k2@Zm)¦ f;޸7]6Y5*W|pH#l F}5z<KQשf{e޺z|}`C#́+ujeY0eqkÊ[;m3O#jeQkCkzK߾߅oqveNsm~KcejUu[&ez9ř1> Ooޯއq+k۬bЖ? ]9˵:R/`q֧([?C*J-mhm˭V5[t7kf\ y Y f/zY=I:p" IR9PP 0?2TkiS4l?ws8;;+ 'ZkѮ>'KS83LйGkm=$͑V[c\\`0z`O6kVe٣b7?*ɬiiʬ(?gW з*";}8: xl6T7܏`O^2TB<}ed|9d|ɨ8QiTQq$HFǑs@9ߘHFǑO;lMʧgOX u͋?fL|:GƞUR|<}V^+:u2'1:wƓ)7Ky:*}~ǒ=<$aS#!%PӲǶEdL\Z_hMWfMҁ8mKH,~Oo^\k/t&lU4@cl,j޼yn2krhz7 C* ]L3fvjIӯ+Vt;*ebtX$?!6~|YnʹW-:Pf^km qiL賄.&]q$HFǑO;#5G2j>d||JxDQq$Ω$[[J I é_NVףkw vslkW7($SO]T,PwZn$|uU`vEw~sBݝfyq]is#$PZDjg}ZӒzY["MWʖM[-l{@DdC/Qs<¥!VՕTTdenय़ڏ+\v5M kE <ԉ>ιu.% ~Mhg0A⁖m:69^zBR .ύu/%:b0nq }fȧt76Ȉs?Gd/}vx.M "Q ,0ÔjPw/AdVW<`JDIZWڕ0_Ns术>`\" G@jN&Vgf"szL7Y쉬5KVLc„ UB,4MKd[bqO tqغ =w|w\_J {1bor:.&]q$HFǑO;#5G2j>d||JxDQq$Ω$[[JnQ$}oܦ\/pQrEe5(S_)4+sX.['4>b;p*pyCkP,B.?}m``EOswFB{(LX`O?~y08t+1DO:/Q0$=5(f!-k>3xUDv~kI#P1N^f2.kdĹ,q1>SC"W'ńz>d|ɨ8QiTQq$HFǑsW 2j>d|9d uK K = m|`+ܪ͝=&ϗN'w^{J{ԥw?@y¼dz}}uɠ.Lax1a2Y0?sAe&uדz })H$I$I= ^OmjԳRgLZIR){7ԗ ѵ\]b ЪZTlIS=l8Y-Դn5|u>QȈs?F1[$I/ܟ#eeuE?I50$7ڵ@$Fu2IVӿ]}#Nj䁮3GI ,WǞVٚw]:Ĥib>zׯ 65HHO=-zwhLZkQ+q d|9d7A IDAT [ۄ.CSJ%e-)e%fNl/ZISa?~0NCd!ԐzP<;{ 6g  @0B|Lt=6 ,BQBl:#2@ʶ.ה$rhX##EF}m#2B*>ޕI>_ '?1cZ^@& o쩫}wɊӦV4֯?f܃7m:]wZ:Qtv'[%_N(u#ۏܼ+k֭X%'/gg/;w kg ws'~S"IL%kѪ %-گl-*/eZ>Fls~I}?n*pqsHDZuSoKYcZx,}$% 'ITU5e5֟qLﰦO;#5G2j>J2j>d|ɨ8Qu*AFǑO;l!5JSNAj=<[$ BW`ZhѢE-uǥrͨztafk23M?Lk_k; 9EYR !ٍ$]`!wVSRof:w#CmKb'ڪ?2(ʯUA0:hzsH$jBw4`;wն`XQ%s.\ 5&wSjNMU njҲuMvbڋ^%'8v1:#5G2j>d|9d|ɨ8Qq$U##5vN%B)a4H q@k )0ay5D+j42<2F1e#-y`Nu ?۴CNa0-FKXsM9fM ͌,su2RN8LQm6S,4ȋ 4<[e5͍BQF}kz>@cI%pNy}` p2@XW(v$ls1%O>/:lVpf{8jR$I`">bZ1=֔QiTQq$HFͧSIFǑ#5G2j>΁^%<"Ȩ8QiT-d-%Lkkh<@{0`p@k䉞Whdydb8 kdڋ;9V8$2SRz]]ܵv gLQC!q vd1a8tCY /xiȨs*ɨ8Qq$Ω$HFǑ#5_@G5G2j>J0?! =4<y5q ,|ȯ? Z]pxۤjӫ5BI0 !2ڨ(A)$Ne /& Opj gQqA1pLcLpj{8d3(k׮Q^Ry|իhѢE-ZSM |H?ۤ][}͎euAT* 6nm%>vE-ZhѢE&s8-Z+,,lșV Dc`ckO*$UtLժpZhѢE-ZhѢE-ZhѢE-ZhdֲmB% Td|ɨ:[ψ HFͧSIPpcPXfHhRF4 ]$kN-ZhѢEa߯IaQq]LyB=G2j>d|ɨs*ɨ8Qq$HF9zF5G2j>JSJRNAH ah FyEFFG($0ڨ8F.&<#5G2j>d|9d|ɨ8Qq$o=##5vN%BRj=5"U p ڃA'z^Q1a,D&1$ScBPHFͧSIFǑ#5vN%5G2j>d|ɨ::&##5vN%BR$AP-- EK?lRnzîr m<1h|ҢE-ݽ9 F5hń'q$HFǑO;#5G2j>d||gDQq$Ω$[{ZPT2PNU PB 䁐 NdPae顾t &.EB L۠5֜ 7OX-,5 06j>6± =OHFǑ#5vN%5G2j>d|ɨ:[ψ HFͧSIban' @VըlWTR{+BQ~r+&UV0֡1)_ J9#[p h4sN{W=h1k26nowvv4\Ztk@^?xMCy) jm-ZhѺ;_hm#z>d|ɨ8QiTQq$HFǑs2j>d|9d ݞpV@[!շ!09.\z4VwGZ&ý܃*@ֵRNN@4bg^, M\~k8L'3&T *+ t%͘N| Si陕UjtC!-Z=ȈpvF5hń'q$HFǑO;#5G2j>d||gDQq$Ω$[^%LkJt(^D}Orv=TJ}BGMKKݓQxC>o47mj|8Zۨ]'amhn̗ݓU9;8=T5!բ r.*]צs.hѢC2|:& d|9d|ɨ8QiTQq$HFǑsc2"Ȩ8QiT-t{R·kHXd˥Vzc}P-7s?Px_߽3UcBvtƣ Uh X|&S 2Y jvA.1:6b]ZUtG! kUrjw1 ]Q 4n*Y0ڨ8F.&<#5G2j>d|9d|ɨ8Qq$o=##5vN%B*aO} %ZGE[U,>~jgƎk ÀW% No=T4+L;lޏ5IU`#Df\> K??G4v}LE"9N>͹➪ގWH*__^i(_SyONXWwncQ<2-GH,>5 06j>6± =OHFǑ#5vN%5G2j>d|ɨ:[ψ HFͧSIpO NaoW/r! ٱ.~B țRjocfKt cƄ$NQҮ~sƴ'=R=z^cn~d7&?v޹X2)Xn'yB5\p~ș+s|&[O2J)ӑzjN^'f=f0\}]lIzl놊+r0)G:\.𪔭3 [[ L&u۔8"s cZkR ٱ"9 Yww >{hnWflxo#NdgJq\-;޻~ GC,>5 06j>6± =OHFǑ#5vN%5G2j>d|ɨ:[ψ HFͧSIpO B>Qε} ׶<ԧ[ex}/ڪFf^.Ӈ{;`<>ڻ km`WʽT0%v4EmV^h oZi+wؘsvl. )CɏNw:_[<7C|,G@=t-1Y^{p勖vxu[_hoYa5c_ (K!/uUξ41<}=Tay[ ɔv ?Ѧ_A[R'"1⩰WA:R[aڳ{ZU[n g0[oa DjH PF:&QiTQq$HFͧSIFǑ#5G2j>΁Ɉ HFͧSIpO IJ5f%k͏ D$f9yX\90d=cZSX)hwz~ r r{9L݇s-=ꁱ҉S:. ,{a, <&+^/y9rʒ/ԕ*TXg_4D> xG- ZV㝼[ҏ^ hU}d0~D,W:M~k\v0{!~'жjՓd~0pa፨vDjj˿B4!c΁3"Ȩ8QiT-4S´lhƜ a/eJ<f<:ɟ΁3"Ȩ8QiT-t{R†3ѭngt'/2-hky#3W;(Na^glygS5  7.;=:e}!]9')%lS3@C&'?'叶$ñ%rʁ/.2Φi!΅L' L v>L4z\ mmW枩)>n1RL6!<u [S%}:8dԊX!Z-7!pgL>pK{Lm޺0Xgq%;JXE/@{0PNYaG7;_hm#z>d|ɨ8QiTQq$HFǑs2j>d|9d ѫ;A=znEtqygn_:  >ZVX,談NqsjS;飤^!õmzK [@:tx-vw^m~} ?ߧl81I"6ؗ!ۊE9F'{ % WWFMMjvlac\"G@QnLb3Sc]"TsE?֩VM(pu*MUmY߇`wwAd4Uw|miߏ7=@Yٚ"c/oJiƫ޺Pb+I<^wxR͇*r~ʻiM[:{MF9<6ܮLo%JfSUGvNC&:r s5EJ{1i >M2lA1Lznjw+"s'WlU w/\?"=< z H MB'28p*LS3m8A@`+n3ڲyᯍ6f)Xx}f0=ըV>#P{CRcIwuHSY;eX\W)ӫ˲m7F'lJ2j>d|ɨ8QuAFǑO;l!z0B8?=~H\ ˲6!b %k={Zr+џokԍ[&̲g @É6em\Z.iǽx0˒;$|;{ݴg%۪238fuC}\pȿZx` \'lc'|;hTu^%M*wmE讏&"&g\h߭ m-Abg'N]y !gǼFnድxh< H /LCyO]| Wt_윞C^5ȅ'zgL2u`K8z]%蛉Ktu[X}pkQk5!6ńA&u22j>J2j>d|ɨs*ɨ8Qq$HF9`5G2j>d|9d )a<`z3fFsXj(XCZO Wc$꼢0kN oF*`K{S={0GxGxWo+Ⱥ9/zI7߻<™~PϾY*=d2$E噞!Abg0d=mf8ߕ<_S4+m+Lj?\:Vgx luᎤ=>Q[vUsds󞎐~xwhn5۸Ln,oDN픏\YhG7GOZЙlλbogVwዺ&?~´Ҭ=/\0_)P*sd|ɨs*ɨ8Qq$HF9)AFǑO;ln)a$I|S|b< HJ* uflvgd w|g^${H1O}߁՘ ޴38zf| wDbiZ(ۛ}¬x7tKEfg)Xkku-W/LzCD07:5^gl3rgEٻ>Vക/$)Wv=eWuQ%}GyMu,huI?r;o^6G$%qyM>/d|ɨ$HFǑ#5_{8>#5ܟdu LFqDK^s"7 "<3Y-U͌(x̼R݆=k"m]3kGp֣֨m BEn\"jklb?ڛ L(8F0CbRCۦCnv&53}e ^Ǣ3۹a 6 B60;4Ҽ@H%r_Ȕ)}ۃUsi\ $|Z ðtn&Z哢RtSuQkS+U5"inTgAGcx)_.fT\x:Vm߯=t֖+iSTwh*X$7jk%+.k5,L/ˣ^@VXk G꯸kyךpi,bXxᡱMA9[L{2ܑ:L-dE֥i#ܻ沶u >%&]ڷX?U\phK#EKg<i1fƈ0 >I O}%NG NCז2%1R9OuNq{k+n{۵妺n..SJ"+.iV~%I,>ft ~E0gʶ추VJcߜطή c}y#+/O|ƿ1ΜG\{l+:kE}E#2E#_zpNñ 7|ɨ8Qq$s#5G2j>d||-.K8 Ȩ8Q9IvQ6= Miv%cel=鎴a/ V7l]ړoT&.z`j녣#mvs )ȥ_8Iw&3:Qw>7kK$g{@sDؿ͹/Nsj -W)wܫ Hߓ1*Eb?hZ Na;d_h/OR@AK4E9`Nc]NPh6+ӱ^^ydY`@Py+\$\1hbNx=64ټ-I؇ɿl1tf1]].xڼzԨw{Wt`X! yPͅGn|Rc$E IM`І7)GxyXǟ8£* eڏ!Q@*\g1M%M=frŰXf&ˎGCDP kjoKzCDjyOC$Ok1}tc0::B(~2Z4|S& -weE=K8q'P{(yN_oHGGY,!C$my?_uT xa0lAqI3< @+P|=?$ nd e+]mUbb"dʂvc[|p;NXu)ˍOޖ_;}A.9Ktn f; }R[x<~D'5Gdw>o_ɾ;9} ]<1GO }'쥷`zؾqT)lxNN8d|ɾ߈ph.:6:<1ۭ>㤨XJA7,elCONG(X{el ^.DYY5Quz\-67O#n:Gak23FSgMz9_ \wE^݀$ y<h34#=C}L2 !,2,_䚉ߒ8(I|oWtͻsӖ̊d[u 珘R mvח 9b\D2WL`8Pn/ 8ڪSvk 7Fu-r]j?,-Owwk M![mmC{D""SqNzx!VVA>GHLp ) = گH IshEo`iul ?|*锑)=0ҥEgo3-[.zVȝ} 7no0-w`:{mOX۶_28.-O2jn?\}r}gw8OLFl>OL='(l|OON{۫약z;v]w!Hߓ'ϯ@F$*CI+{7 1"?C~:khRZ=)} kj%T l6^VUВ;޸!-4\)0E 0G4TF&ey+ ia_:gXș9?;n*I+R<u]* ܄B 2tPy/gN7fw iq7럷|w[~=kzI5G٤ɾ.[1U6O 2gft(2\,g.V0u< عuv{ wY+DJ/e5zCG\gxXܱ96 dlPid֞PWkPx?o:v (u;% yS<)i"SmsF◈0IM#+(hپ7*^ -6[(6U:IvD%+Bmu&\Tx_>$YŒ#2A(zז[.32gRh⑍J& o\I! #'dEŇȕ2,FiiI\.b@ ftFm`J%VkKKKssso%T$goPT^۪ٖKg`_c*dFӏ3fȑf999 mXdܸqÆ cY( ҂¿QF2V]/MIy h*k8T?*>.(T.SuagfۥVN6T6{ <5=`"r'ux̳ʮU ou^6Ƒ3umIH 7v/ɽb9<7~98OK˽I [~ޅ=. :vq-gwzF\'&ɇffp =߭yK7_ؾctz:xg*{Pr~8A;ǶWGwORv M{>NC_w¥?/esrN)[ o Jn I5TMLb둄>PָqpoU{CoԹc#`^"O,f@+"0Z-NX8TwO<#YŒD#m+I3 %2I;֮^9o G~b/i|k zaaaB2Zʕky:-!ձ0bd%T64B^{C%L^w|~]͖>N5uԩS2%{ |#lȑ#Gɔ\`?Q|x:q6o0|[eszx3ї}nƭN<:iֿ~3s%BpX4b|sNE`Sڞ%%7n&=rUIυv/^S||E"!PȮ&Rx->ɒakQ&zeэtpםy[>qS=1?нpOLGykGr/l1˫皷ۧL&E:NǶֻ keo߬P| #{Vius/ 1 % Rvܑ9pXd U9(c{۩s;%zhX:"?>^v;}M#䵱 ;7=ec/yݗ=Ŵp <#("iS2FB֟4$7\>V Z/e#p/bZCG6SQԊfbϤ=Y:՝]|?OCa(nDrȱlSȤvt[F溒;)&mRt"oOg%UFIP;Ewei&K=kRDgOO.2c@p$3=k RVF,)_Gm%85h*3(±PHm/!/R¢ѠKYDS~E#b;VI}U|h-ȝ~Ǩl^L^.˷hS"^{٬קe|]OOH=]QƯ/9ėgF ‡}gY$A%^pn̟K:|ܾ#-{~Y t{m'ߍi*t-.[.c*8|znL:|St)grӅQ1l=^OԚۛ=mVعcx]CE3mc=L=TBha#[bJ8mm^I'Gy"2pHp4s56ޙ eXoZ]Pk3BC',霡PҶ&`Uɠloæ-}M=Rɠ{MN4G/v* HnQ{nnoٸe]..Nľ%F&eE;fGZ:vq̡oݳ,#'\"8qmI΄S:Ve]) =$$;6ug=7tJHyOo Sr蕧wb :C{u llj;%O`_t[P95uBnp AM3ZKp u.S)( < IDATO bg+m5_y˗Gg H"wf;3% e'O5,g#?Hiq2oG`?]Eݸk>˾>iGoHFǑ^ނ<, `y5)k'9Л̚7^XЭy#% nx<7tjfPj.6P ! N7V=}h f`sE҆]Q|*<5&{|OO۵{CcRzU EwV?/4~&񨭾 pU0ޜ.աܼbȤF* TaΧ5o_P2;35Q$ˏĜPؗ57FtAWg yxZL飝o>ppŐI9 UEI/ε]{핢h5_]1rnҠ6I%ȃBu^>#C05{fB̄2mbR 12ke4OR-S ¶&Ť-7jnbⲟ?,c؇ ] B%r_ez-;u=A"7np܂+Z $*PX:▤A$ z-ffX)*J(Z/褣m&;86:" >ZaO,e:ƨ8X<7 ۊZ$w_:reX6Lr1O044GEDv9tvQOX,#AR%Fǿo;v 8eiDdyAVy^`xŒx~ty l V=)1f֐wyaAZpΤ)~\>FpZ9ywsʩ#CikqB!CZa$1'ޏa3/y $HaIŇ4xH W:)HDžriXجk +C*S7:$Hd"7' H}_Bd`~L̤_M\nOL ƶo9IgW}r="g{gO"}r\/vO]-ʫ]ԟ֫iwZ/Y 4!>tX.*bG9OFǑ={=|*{uQ߫Wo)^mh+c<1÷X'y桘'&Oz8+wN蛇&۳'vzE7o_z'\kVz&.$D~sM\-][i?׺/nzrCEѮ_ho)^܌6lV? MemeH;}o>\uǥ bNgD޺_s.xc 29fUcܒ<8X$ 2٬YBB2jêGͺ=}xDttb?O|CBc$0nWk?wt/>I&*h|x[$S4]xkzge3C'Y(٦Ѿ꒩q̔p]yN_5I[B3eᛣg/J{ֽoqro)뼗s(ih@ѓ|9Qօ; (6Vշ9~XetokyAw5~xxZ(i0_19F^k}%eTUR n[*t1~F?(@lj\g ]0ڵEܑ^8s<(F|`UZpqp2$I" VKѣ]~| MwfzRLZak}qkwqU2UTir(1߽gC$Z([Pkn1OⲖ57];($:T,Tph=.QE$l(h/nmt7@k2۔B]}l/:"XoPJMZM%GE$}۱+6oMwu?/9tRHŅ56TXkv LᅪzaM EUg*J$L\ RZ=4 V J943HBh*)aDBg@Ki 1ʙK*b1!S^TWM4H'Z;( @AqAzRn2Rb0j_H@[myKV eR64M#CAltwr88IFǑ#5ܟd|ɨ8Qq$k9 {'8Qq$s".$̩#;Wh(?=zH趾M=_qڝYaߊ7&",D*SBP󦻗TT;.z淧~-1|t+jRwdŴXWUV_M3)RY)r[ewNȬ2,?_KkgZN\݀g2X-G7ׄܟd|ɨ8Q9IFǑ#5G2j>C+|ɨ8Q9IvQGHҜnI /uRtAqʥ8=:63,.ZÕmoK9^Q$ЏKӏKkӀV@DA! {).C o"%c5mpV_N, OC}Oܸr/>ILpQS,ޚ:iF^"UȠā  }0#lOIb!O-'"ֳ2bIȯ*O-'R>`_5s^Ӟ" yт|kR7qy(+ꄉ64m:u 7@%W;˽a 4ݱhyj&aaN(E`QZ>Nd.Kاp|:?G2j>d|ɨ$HFǑ#5_ˁ2j>d|r]e ۳.PiњݗNmlL Thmo2kMQJ]^g?ɨ8Qq$HFr಄#5ܟdu ']V9WPE~~ZjՀ^ 2(1HE01 =I_plN0"{fe3oMQ4E4E5juKg?vK!CF<{?ՍsHx{f y26Xe"T2[0@QԽ̛7odNog`rR֐k,a± 7|ɨ8Qq$s#5G2j>d||-.K8 Ȩ8Q9IvQ06/  Q!ADʧ6?fp@A AAItտk;>n19xA5J?qʽՕOoXgozqzב.KYuՋ/߲d֨q.^n4l)v2Svln-wMeV57Wk_kR$!Lut=5@EemYy7ݚ=pPCu-r͑Ɔ{>ϪfL )Zs4̱$Yz˱~S̘1cUcX:h9v Z>s8'_0gj䥱vT-QCa:痛a72j>g?ɨ8Qq$s#5G2j>d||-.K8 Ȩ8Q9IvS2P} }ֆ'1Y)č+(0rp5hʹɬB]VpgoǪBFRd8^Y}  o2DGGE:QF:|Z-Xdzĵ[ǞX\[bmlUAKo_M;KwtoK룣e+j۴*ƛ[־elI?fkMc+y_ŋSeAjLPTT`j`1sa:痛a72j>g?ɨ8Qq$s#5G2j>d||-.K8 Ȩ8Q9Iv0'N8qĉ?͸S:y6ߥ-/$El(_-f$^p ndJHX1"l娍Hi,<{^CJD 2j&n(XEMfIDE.87\Kņ5NS:qR""g1&l@B^6&쉋'/)2 %K8v>f#5G2j>d|rQq$HFǑe 5G2j>g?.ⲄT$.p6p658q}Yn!#F\ Q([6 VDBpl7f@-L DHR"&Ž4 4-<]qp[HĩkתGtX9K]g)Jo&$>^G< E< ȏ Q>'d|ɨ8Qp@Qq$s".K?ܕHq_g#F}N'n\GEߕL*ADcHF˧6j>"r ʱ; %47GFmF%s#ܿdnQq6#|=S $s#ܿd_2nh"X.|k::3==ݵ锢yK`ѕ pIV`H$bԗrĉ'N8!I"q ֽí/k@FyEܼ^N8qĉ'N+='Nկ_?3Y1cF\\܏?t(11qڴigΜ9|Wdeemܸرc/ZDuuum6P8zh^UWLמZһ\aN8qĉ'N8qĉ'N8qĉ'N8qJ, G=/p}ywKL755[,>O;w裏~m{!EQΝJzj Zr lܦΆf7s`[i>0iZXHh2"#knۨȻmF88ɤH)s#UGp#ܟd|n̈m|G{' 2#O;oWo}md o۶'>}Jr$Ohv"RO6md*//H$qqqjC6i>»JKK)JII"783##õv)W`F Oܸρы>,2a#od@m;;p|ɨ8Qq$s#5G2j>d||-[O@Qq$s",SNegg궶 #¾ۦ&Fsw8088`0߿رcvN\\\vvvDDJߺukkkYjjjbL&6mZ\\Rljjڶmە+W-IOO7n\.۶m3&LūW($I """f̘믿^|~z||Jl>ݻm6w%pXҥK?22`ג+~4 ]l`/)č+(0âѸ;OFd޶ñ 7NǑ#5G2j>g?ɨ8QqCڳ IDAT$HFr5G2j>g?ɮb M.\r`n͛7kZꫯJ${/x7@"ܹf^WTdz׶nziW[ZZL&Sdd}3رnCnnnnn.޽{5?ɓ'\R,)Gydʕ_}stժUYYYEiZBAǗ.]ꫨŋ 33 {K.effw'xwѸ/tg`7s`O Μ p|q$HFǑYO2j>d|ɨ8Qo=AFǑYOXg]h/l6B!Ǜ:ujQQё#GVkPPܹsZD"-[駟01;v6Lvt:Mә+Wlll|/\ W^׿̙3LƍDxnm„ o+̔z=)Rq_g#F}N'n\GEMbGȀv8v>Ɖ8Qq$HF,'5G2j>d|ɨZܷ HF,'EBou:̙3lذ򐐐c}mmm, uuuE} EaaaBBLb $IkjjJD"iiiX,5557o;BBBl6h4566Z֗^zI,+'N(jɒ%gϞ]`ݻ)*//7Ovm#G};&aKօvp?.p60{6l~|XPLgl[8qq}X}rŎ>p|q$HFǑYO2j>d|ɨ8Qo=AFǑYOCnݺ9s,]' ;wbٴiVD"(!!a̙qqq$I| mnnC ETTbFf2 PT ՂCBBZi޳gOuu5ϏX,#GD׺'%%a2 C[[BXеN/pluhʦo(V@%$o6 ذYMu}PiQf҆+uFpx+ܵÑR.?p d||-[O@Qq$s"ĉEEE-zꩧbIvYQQaق'MƓ'Oƴ4d`X{<$IG+/_^[[/JbLV__+e HRTgbh\ A.@{{oCœ MʳƲ~HPd2B(>Q gw RˇMĪ}_e !% 3, Jf3,si7* *2bOBC;>e#GOEݽt,S/{j7{sĉ'\N|gFv8v>Ɖ8Qq$HF,'5G2j>d|ɨZܷ HF,'Un`͚5/‚ Bw}JBXbN1cFUUV]nJf=qoDDw}ѥKj^zffvxN&9/5D"i @`4] Zu.+ͩеNmD"zdCoZJA]eiWNS%e'חY-2jYxb8]{ &rf*b{Bi<[RbGj / 68|@L&sQQIuu-#OCGlhh!Skj\br_7s` __06j>޶ñ 7NǑ#5G2j>g?ɨ8Qq$HFr5G2j>g?.r% 6mz'z衐r\YYYVVVSSCEQ`Z=i288 j4>Gә߮wݻw /_/2Iyyyjzƍ$I:);$Iă P4SRXXȔGcPshMDzgDҮ33R=h{5QF]+gb҆HIB&W@.2~HȠi| b62xV^~}`i$g6)m%6k}Gvt7C-."oƕ~^ah\L|'~#2o'HFǑ#5ܟd|ɨ8Qq$k9pz#5ܟdWu%~e˖SO=FӴRiرcSLyGWZ=~x`dfMu,,..nll?ѣGBCCrܹsSLyꩧe˖*G?hѢ^&]t_~>`ssg}fZ];唸K9ճe'kW •_qM1i=eao*:s|;q vi[:k/p:j.l(<u$E( JWҡOn}d||-[O@Qq$s좮BW_y?tBP,4sω+V{leeU;xx &Oĉ>liifs*JMM-))OSSSZt-﮺pN]EVŤ wYT{7RS݁:< 2)k}}IjCɣ>ͽ۹?rHTb7-k =WmqAaN~uWq!FD>UPD'I0={IG]>s=k /^̎|lFjzQ=v໘`m|mcn#5G2j>d|rQq$HFǑ} 2j>d|r]PYYaaaLyssezZA0!Tf)izcӧg̘a4CBBbq[[Nc\@`6u:o14zl6k4V̙ԑÇG](%%KP4,Ŀ4 G#5qM=3 K>~CEZcQSݾOGO[IӃ&̋*9 y&"ܡ?8xŎ*c˫zEbGos _%F-:/xNr>ihIv:,"TWWѐ͈u >I<%nGb9DSy$3Ze z|2BހxeRm{.&$W{Owߑ&b#od@m;;p|ɨ8Qq$s#5G2j>d||-[O@Qq$sn)ʖ'²2`/! 8w*$l6+$lz` H8ҥKL(bqXXӆs3G ]Kz$C*}(bdwTdY6R哪=_zf2jڭG|yݞL Ox;69St~wRbrTRV=Bt}*x~)#DNi+ 7o53S`&VJ>$S+zz}$un}HCɧixZ*Ԡ%ޜz9TS' wc,տȗ70 7 u`=X+HZ}N\6!^%T46w*w1%ۨ8z'8?G2j>d|ɨ$HFǑ#5_ˁd|ɨ$,J|SNj(Y$I{Ԗ$IS E"QllflAx<&$l?oXcbb l9UOg$^KVZz|nǘe=g?}Zw!ᮤ-9UƉ8Qq$HF,'5G2j>d|ɨZܷ HF,'E> sBsihTɚ7ģkY_>a3Jp <]3lug1~xlj% ]ӹ#{fwSqplU9{G-qw OoQ[ʐk@#4M fmBr.Tik4ƇRB 顿Ec"@Qq5 NӒ3cPިyV^} -6VcF1=8熌P(ѾK6e]>Bȱѯ%+o#A'Ow#bLzȈ`+EOJ@|'~#2o'HFǑ#5ܟd|ɨ8Qq$k9pz#5ܟdWyp4&#6X،4+E?|B]C4e36ť/=wܹȣ!k©}_qlQ᰸tcGe#HQVV .tt֯º |v~^5Aܝ̄f\|FM fa"״by9>$rp*D!RPj:R˩g(WKe[q0)$Z.w9;zv$@B !{oeC"K+Y5ɓGޝ;Fgglu']-]0VJZi(x}eW\06jRjhD:4 -E >38b˖#u}1rmߣ7 ;=lGyb${IX~rZEZwӴ8Tc}ޱ0IJԶ.+Ki*I3H(=P<:-#<'N7Y?>P R!w\2]- Jl:}BBukߌa:wZ?*N>*N&VJxA4u]luM.Iwm l=V_Ӯs%rKRR!Go.k9RѢgc=fٶYOgf~6Qq4ьF3j?ͨo=#ŒF3j? tt4d$ 4Aa&{;(&^\^[uX 8-J:-#65\Y_zŔ{$5ΎOMX]_e+DĔ^cQC@  [ LeӖNi5gW\y8lє5`lb唸Nkl[d"Ɋ+ 1rc-jQU2!77>D.͌QR4MR+Z [2=.;V~:;ϋ2gy,)#YެAVV%<㐎bd3sTN4} hPQμ*V2=~8>-?5T,༶A4`݅;{MfٶYOgf~6Qq4ьF3j?ͨo=#ŒF3j?75J $f+"r}&)i/A7Yٱrxkzf*R!X>96Z)~B]QJ${*G q()(l+%F\Xg$`f@mu9$~6QhFgGH3j?ͨl4ь3"̨l4iv 6&Ttxϓv;fW, a,\'.aht{k1.4[-H|B*UxS {&DgMxcזo|fpƢ*ꃕopic_@u0hoDŽT!cCTKOJ 9//Lw#%m[;-#|vnĿ/h&(IXw`'z0rC}S6˚z a˾\luĔPX| uK'vkљۼv 1Q__Y=fL\59-,6L}pτmZө#%mkg')px2wnCpa`J+?sW'ƅcDgлFLJ`yww9uΑ#%ma2IbNmN!cTb7'F^ İ^N9 ,6j?m.'F3j?ͨl4iFgf~6Q9zFf~y .im`j-Ls~h2 w,@P^j! hlcisWחk6ab^5/p"(P楺nP+;O7N*GrH"D3t1j<5_>=?.IYۮsz/Bny0U@UvˑMۣ7u捉"'_, IDATI KUj~qvFGYSۮ d$*TѬcVNDbZk.i3Yt˞\NnZ)TɅNղcJ7'%A%66.m mujLhUH*$D" y&[oTַS^.Q~/83 `.'F3j?ͨl4iFgf~6Q9 goNhFgGH+x0;߀ٴ )-+4e5 4ƽΆ]M7OnsR_jyrKbR|#wf=b,Fss]^w?;)W9mo0 tsxo:Ǒy11w3挎U:/a2\Ԛ{+6v+!o{sg鱲vj΍?V5Z>9P.}xbd-a1X *3ZI 8RP}shcT+E2Ԍ(e\&w) VpH]N{h fJ n[ S*J2m}Y>bqVHE_>k.~JyqENSfΌO0d?7'H#9 GH3j?ͨl4iFgf~6Q9zFf~y .nn LLEYڴVw$ kR`JձW3G(5 'L) :qBµ120VX45|bx9( {ff*#bPef!8~mg$չ۴/o/r!TdDŽ^9ꎷװ-ȊX5=>+&>m4SN;K&~ּNߟj_d"$ɑL=X3UOJ!*dl#ʥ e,++^/LiWsW/%^,aqx|š9\xb26V^HhH아嬸cWYHڝީkeok{ 4z01ANý |2MJ]9v`bvY;7-Q%rn-H`AQtYGo9abtc:o:骬h޹$d[w0)nhhFvČc}15 bGc 62 s<pj߻|UPMQSgb7Ť2VZS,K H}6JZ[ecj-zN P,o3WgM|Ͼ)?+ FC"斜랩;-U.6D^T_vu]k︕@ϙ}ufƕwl3n۹:Ʌ.ۋhl*1+Tv>}{_\7>\\p㔄qR!%_m,V#_}FrFĄ{Hօ<o>ꗘUȿ}+*>aygh?>;_~Ȉ gl5ba8O%!_@~}bׄTµۨu ߅~gW_z}*| ?O >*X?;~7p|:3!'>Q?$q&ӟOub,t>*A17OmPo(?3 E즺2!aT[x">6!abwL5vwc? qDX="]2Nu}Lskuђ-k˻i僩+F,z褉&MWSSf*֬^4Mϸ#Gdb<ĵ:OlWF34ddžpH^^ة11i6nd$rWNK_4,l=\csٱ!jE`&XFltFXV|SVƧ}rjЫ3wLa2Tȵ>TԥRn )aӹ&OаLwd($5wW٪.]ܳU!U`㏥FSS &Wyz l7÷@@].ܝ̷MB!466H̟46#^x׌{]nlGayob!&[_>qs1Gn ,9)rs9}̃KD"j1hBn(r>tVK$b ]`,hCIr LSa}ʉOfjיCGt_t'6]6&tm]=/߅΄"rĘWM.5W[[>]_!k.ɣW߻&;9by>vtUu l2m,?K9acr儅uW~ǹ]_9̀sc=𦰽Sao 6fۣ5i>U䥵~0d{segNp? m@\pwrՀW>73;l뇞~D*l4{y lׄ pKS~zMVEW"k;Y9D;Y3_i⸺֗ŤTukmiC}CĔ^eoFut6\<3g|ۖi 4˿֜"Pc@峄iH˳x\2"D8)5|HtS% }3L~~\iJ OWvPgBG*9q!m9*|ug1#'S{ze-kg+ڊhm挎:[u=טH> ݥOr._:w]rjǼpϹFemc 7)wCmԲ?-[K͎xW'Dds+!n6\70D̿cvPLŃy JX5]r%8[ (_5-h UNl.TZ.dUhڷ8qdaj\KEO9eW(iTa5]oKu]"ZGeŅp;cUpRՖ_o)=j1> ߴPk^/RD 5f?ԏޜ1v9r~7~@yb_i%g@,T؛dl{.ޘ<>dsw yͳ9$`f~֙1y`ޜq5ϖ뇞~a9AK[{ !aKKKm/m˨*!Ox1yVוin눘8ch]G~OÆk$](bVmZ\.!<`FtsleǮ '=ů/vD(};E-ɣFUkH4+5(>^r!U-ۖuٺtxs_ ʎif+uQ}d“+rR!`+oPϘm=3GWuפ%p"]ºn[n?gc vONU[ָv/U(p/v5m2afMDH$:4[gX?Whs+F'NyJpx1r<\W.["NP@[q*)Ҡ X,bE$(!K?^xǹ|ǛX>gb^FBXJQ= +@Vvj={?{]2-P5wY3Ӓwdǯ'/U|AHT2s`XHZ+̀,v9rIl4_//彟W^؛'3{.}m+ |6pzN D= >#ߌF3j?ͨoNe9*O: tM sm=Ow#6x}㹟kB'bLnfC=TTo=wQc4GpM,fDRx 03u ExvdO 3:㽉ǦI[WzocfM)CpxɷtVU XLֺc;Ț>yfcO4M'xƒ?XjrSW%S "ےLMv궋n2O4-nf1[~oW*B\7Z`?rˡpJ.sIZצm6 thwy(D̋WIB.thLͽ&wbϟork'ǧնiگ\~=_ݕwCw#rALhrљ^.%FHe"Jk N`v,5h^Dhk<1[s+]S_@܊ճRB6WG*=n0[N9m9@0K_r#nI8DG7_<4q9ڿ?o);m(pbMYm7s^J N?6wѕІ IDATh碿? ݰa\~z/ϨeiNؘmu9$~6QhFgGH3j?ͨl4ь3"̨l4iv0qeH_=a̷mQW\ɮ&톮Na(Ŏ?fddnw ö^xΡE"/9UD\*.XkMqsmY_WGʇFO>z~:[CMf>ϵJ}ywGٛ6f.g]Np? f~6QhFǑҌF3j?ͨl47r3j?ͨ8@]as8ذmp4e@&ixgZ-W pOϹzM՝6[7̗z}l_dǨ)"!I'~F5k@oo@ ֭[jUCC <QV[{=v\m8$,v &IӅٳӝ{Xҹ4]TTZi>}}DgF}i"a,$)ѭ0+e'HAĀ 9-Qd\=1X-³f-^k;ehiihh)z=MQA4Wضuכ->2s~ğ1d0(gB $H H $H $/ﲽ  ^ H$.A@-p7M<`F_I62C+ʛzּ3PF$lkn]`}cV݇Nʎ|W6m3M7KRPm .o{Ov4MYcrR9rM.-f1AGdfoNؘmu9$~6QhFgGH3j?ͨl4ь3"̨l4iv*h?0j]yihlkoiKɄᖵ Wjvm2~3zƷ3tiaƵ@E zxpI&}}~uuukw}+VxiX~TIUGgәVr!;6q8J;I0Pj{ႂ;g5B!EQ8 6a KJJl۽z`hl1`F_ъ F"!2s31ۨl̶ 6q?qam0'Ԍ؏{x@{x`6j?m9A=̃(GJKK y0Ã`hl1`F_ъ`~3 _`0 ` `0yH{dccӋs ^UWv% 4/D|xmLf`M}{\ \Ch4V_r]wq8(>hͷv۽޻~zwCvgۀ@JJmGSSSjw݂`0 `0 `0 `0Hq;$ {j?׸ I)DʚZ(M/<|_ng /w3==^/hmڴ: ˩ &7,Dߘ4˶ 6/O OP70`5!j{A{!a'eԊEG}{l @qy/ӣi h߽w'|š6.p L G~$B,iZ.B!Hd2f#\wѳm@pu `0 5\W?Ì7 6Qh1 ;ǢьF3j?{#`06ipXah{@,( C~^/LFi @ J%7^'n;4!!^gXXXXXcr###)X,Ap\ W*{$Fpa0 `0 `0 `0 KbH؆H˳fiƒ(=A܃-"c1[ Wadyd"Aa?ÌƜ~x?R3֝cQhFgc0 zHA>`0 OÌhFǑ31 -F3j?ͨ`؈ӳ}Lܿ#5FWY04`#b8~`0rsbXbJ0yr̀1`0~02<2Z 0Bs8'3Iͨ8rF3֝cQhFgc0 \x$3h1LV |>g,qC]qQhpc2q['OtfBjb0 uvV/s03Y4 XwEgf~F`0lߐ0'MM^~8' *0 ^K,2STxսX32r*ebqLjj‘_vlƒrZ=ʜ>sڅ+x<."nKxxmW@Qԝeó۶gM`0 `0 `0  3K3DFM^6U_O|K^hU\:s▪s'iTD~xe@ၫD}φkJuܕvCwb̃ TZZd؉!_jmm|ڇOd/6`u6b0C02<2Z̸{е3s03 ˶]κ~x?R3 -uX~6QhFgo F Wݴv`\)I ]R y(Pg-%{ғ|\%fn넘ɄH"Gd9h[7Yvm'_|*xZdϾji3`0 `0 `0 6,aLrF{8&cykǞL?!hlNXnZlskmɉoƨJ*/Ïl{Pg,f2YkZ _z1C,&o"AdʞT!BSSp3W~Ì%@3j?ь`0luX~6QhFgo F?K+ Y+ KVťz`Lx_=Qa.Gf_x2we8\^TRuo<¢]qZϓz}bJ_=/~["N/;H/!OU$Z-:: ~; I4t7'}zo9sn*-=aÝٳo't\0ܱ 38s03!Qqf a ;ǢьF3j?{#`06oHKVXKQV`J*x U\,rZPJՄLXHht7Gϸ=moQaG.]ܒ8}{Q95icB܏u ~:p$Pb1F"ã'BBhj/oh-99'>r/ܹ $9b14/'{!SjzS~ni<>뻯R ̏rCC#HՕՕyޠuh4ܱD"[b|F<_h9 pNhFǑ31 -F3j?ͨ`f%I ?'w;WgeqׄV"iE3>bVKNJ8W&,vmn7y}d2?2mFoom`"^Dҧ())h~Dhd2eCCΝ~xa]_$`fhFǑ31 -F3j?ͨ`9`PqAd2ԔiniZ&S$7`p8Gy3)){񙘘sWb`0 `0 `0%T0ΩFqEtmf][b5v5؜QDr%cIX6yYS'Q6…7/ı7sw \ ~v)S!j +]<? 1PQqWb`\=njj.;w(/?oMDttԩ >7 YվpvՊD"U#q^z}TT-;;ۆ_Is? ^c.)LT8ڵOdgOi Xv#9Ι3>}`c XwW;9 ,Qqf a ;ǢьF3j?{#`06&WX,٤tQ; MIF7!R.MC;qYSˇ';eT\U@_{~_{CrY8 r5p,X=޻3f|a]ژ| <̃~1 } r fckkr]>Ft}}_9*ܿ޶t֬eKާREٳ_><:^̌qHjh aaQtH^g ϚsϞ/,i]5?3/ojټm@X+V?U $`f9 Gh IDAT`0s,j?ͨl47r ax0ZJKKm/mv$h4Mӽ:*.qoe?+;M 7P8 wH#Lªtiz T̑x*6X{FǺ&_?ō4y >F(66u婩#?i LtMTTntbFB~xӦy3zV>qرYYz{;oȶE&S,ZtӜQ7GMM{~RBRE76VoƩS7of~roZmwmھg!aJUoobKz 2e^~XhhݮgJ{ ȕ+'I±cN0+=}ۺHdFMIJʎW(Ÿ~vs3juw>aZ^zøuHHXFjubHH(AG4;;PK"?#''_TI$!MWUUC;jjJda]_$`fhFǑ31 -F3j?ͨ` W\y]wYATd<̫gzrrr.3YKJi -L&SF/H׺M  7ܔ*v=*3Mta;1byI'BB뮽==Ƙ).SDU&Hc9Ss* ~j^'f],u׷n|M@GGB'.b@_[kx44[x-ԔVV_hMssm}bVܹ#33??tC»wo)9ygڟ\۹"wjfa_}zC1X/+ۛy={Iry裏gߒ=mB{]z{$yf8$-G4MLJOSSSr%w>͙=<.{BTTt>$A>_{RSsL{ jꄞ/h %K5<^1c`0 `0 !,`d|]vtx TtϷJ(tp% !n<4tИV^H㮚c2^}~2aWm\wDrSOQ5DQpyH3\.Iq33y'ܲ cc;>= /Zx˖gܹMwNΤe WO=Rb+)z#:~!APrr&9;Sj&..-$$þ](''8úNJ>wwm @uHK˳oQEEQRE/Xp{ddܜ9+!!!˗~NkۨT{L(-=믻jCBB׮}.]:V C!)F-'tqNfұ1'ޏԌB`0(`&j?ͨl47r a#ng ~zٲe4MWWWcx111oFu)&&,,L*eAtꖖ?u~8̸%V*# +kwSqm[tLVQ|WCwQmzM;!@轋TXZBS@EQ 4Hi^.qwKn/8?gvvq}}A$d4gL +lV"'O[K݆MIItlYG׎\.r'!6#GV^/ss'F؄㏓*}ґ\ %/b3go޼}ܮ,~O>}O0>ज़7Ee _`()U67}\w'$ hٷz>ɲgJ)~|Νꗘ{^=.a={kOPHHƍ{];y/w\1LI93{0 sѽL&M Z.2eR1&7dҟ~Xƍ3_|q`FE4=U>__ѝ^_L&ݼy-A&O3dɚǼ7o^&Kq8\WW2-9}ҁt^v>Ahh6۩߁N.L>}-G :bvmݺuSN=qĺu RF|*O'Tug76Rݐ΀Xb4?'.{``HDz K=E 4bse2kllH$~'OntT/ʈaH$bX:N?ڴU{|>?//=|&0kscBee!@(^zM:/rYYYcc#p8'Njb:qDooﲲÇF!Hrs]{ a```nT*Uzzɓ'? b#e0aOEEšCˍ'>rܱctǏ744Ś5kVTT=hYYYĿ_"a1lfw D֭oQNpvv[uQ9d6)o$D5ܹB.%bH4Ur8Ls2f3[ȏق6[eH_c<b|bHKav»Jf$X(.wne}JILPCqEE鮮OuKlF-< + Od|M炸XX&:ג%/ffm6ŰT*eJ +24'8trC'yl[t:ӷH 67Kr}Ǝ=d$Phaænڴ\g~{xͣ`0^^t3|>;.׼y.]ƍ-XnK7yƗe2MTvĚo}{%|- @tLcNNw J?qq=׭[6\5m ȼy6'Va0{\WO|Gfee~) H6gZ đ)[r()w.#S=|7|H$fgniird26p/^<{첲4WMMM=qFЧ~RX,f0ׯ_3g1 s͝;R)5U0ad2}||/^hѢ+W5 ܺuk``Rliiquu&fܾ'''#{H 8gffĐS3ʝN[SS^SS~G];.?wi1aMB TEss#' 'zxlRFtrs}9Wd¦y۷sGNx<={Yק={`֬%l6_1N544:dj c<ī~wq\.22n.: KR>qР ,[.=af'' tzbh8.Ӂ_~~ImɉO+jK v;Y;)g-((5AC-tr[ʔV[battk?17.k-ryO>hƌ8HJF"-hY9)Z8ځT8]Q`ݺZMKK+@{yj52x0z6i}2ʕ_4!I(=AGz3g|Q@@a2qC͜9?;wCSNJ|>׮]sݲe  ՃJL&sƍf6l؉'U<#Zv֬Y|^988xÆ 999?<1%u֍5tWrr H*++L&㵵 AAA=0&RL'zIjoe7aڴEў~X.bn!"5~KG?̑*Hcwy';Vf>Q)+rSYݒ f,~e𷏬tXPsn_c 2fc ##S 0L_Uid2)*õEZ× 4&O2<pxP[ȷȬ46Bww?P&if0Q$iJۺMt=lްAzBtk'?V+N{W^()i1355ew\7lS@> R)OKDq$Oa8 yp=wrϔJ*V T4ãQQ:w/SxlloCFƵB|SO-0T]RRe{=b6lY q\i;۶mɩT(H$uuu<oPu/ݽ{;=Vuu#jի|>[7?sٜIfϜ9ER[[.]pXCDZ>C:)Sjy iM>Gph5ڤvC"H.=AGH$2%p&-\QQ}٢E fkkkR}W}X,NWXXP(`ҥf۷ ʒ_x3lڵkm۶tRHTSSs߿f0 11q{-))QT C8hjkkZm^^^rr=QQQF4Z܎mw⃧eɚ,@Ubc,7bNP頥E"=yoDo*|}-Q !Q[CuqVޮ|*q&6*io\OLgֱG$WsOn.`c0G(SݬS'-ʺ>zzO4~.I܆~bp)5z] inn$>kwc9 n8ma9 @ڮ/m6V,ܕmFSv/78;Yݲa*j3/{Obbz{yVT; _?R)-7zm+01nܓGLք PyJش^ٷ5izeeqmm>q>55m3|#PQQdk7}iu2))Izyy? -rwws1b`T*rySSrpp01Tq|ʔ)#F`2B022BCCܹcXr߻wopByyZhL&388dCdM(SX4vU*L%⌫e`Ⓔ 0 Ei."q`5{õ_>(sMqm.}&j4{&c0"?Q]9efX{}AշO69g^q|ЁbŐq YaQQ= NgioLCHHlZe뫸p3^0ƴiϥ檢Ѩr/ԯVm+j Jgӿ{mm2YSSSPzکROOqϺwls-!5-kMӣ{2IV}@zM)EGwv&| ݽ'O^;wnT|ί@v-YieHo&aɤQjҤ/\@?d3\|FF>sXX_yzSƌrzt }|5ZPPae&~P;aǏeG~e'NvCIfٯpaa~?&T*z륂ܬB.4q'SZYN@ tvM??W0ݖ;m;#֧AGW)))qwwwZ, |L6bĈ/RP( R=r\.DQCeP"##5 !l}6jC \ꪟէ iuJҳEE9ӓް^cyB' 'IT8qJ)dM9i|0 7E߻{ oSbݱrW7ԝߵa[]{'7Lx;30}sӢm{ի 8I2bĴB¢ :~|\N)ee/8pBTTϧ~?TaaVtt޽'mx7l-hRiC!)StϦ qupuj}"g90677޼ymULh!C&tc~3=J"3dd<4%BB ~:ef޸pᰑNCCDie%%y)1ɿ_ (ORЗ_~SR~[.^}uӖ-3 %6luuu;w/lKKd}8p<&$ԫW_bjЧ(k]\b1OA \756"+I IDATkED[Knn"QXXx@Ĩt:&&  ^AssSj9ÔG*#]h!;dY6ӑTrQ—.]ѣɓ?CNgyn0b &Jґ#G޽X&XY_ a*WWWL2Yi6-\UU -CM11%7oS* )S9._>d 0_wݳ{_Nv+pg!e7;+r2Frm8:DšS̖U=7v4"%t[;#Vd#m0` i"5ׇ@TT@U=;~ .>='00b7lb..n^^YY7Rv}oksso-8;k˖-뢢zL4/00ro7_p8:W@@yorqXiA޽~Ƅ 6ԩ}fszܹsŜjvm,7zs„ꪖ/Y[keY> >>|ҥlY!24*Ž{i/,p/_>n؊c~6mH4w 8osyv^CLl$jnAvu"=:p\mۻ敔䆆=,""!;6'TVݭzNzz:<[hǎMǎ7̦pmζmegW8~P'T''̙]]._>RSKΜtv>P ,(ȍّJٕ"OoJήvHDJmmsvv%"=r䈑hð8b̜́b _Oh8NẓMtt2@ y:w7|&tc08K$r t:ך+kŋ 2o޼u)-8is%''ϟ?l5U{"##qhtk;?2vlV[YY,7B/@Ysyym:ww~n?Y=w y'B,[CO??J_Esn$2@ d?ofpD _,un8X|0-+tfrǡ_|qP]]…Ev9>ߨQ%%ZlFeeVvk7YVUUVbWW+W>|ؾ}ۦN]mߛꚚꝜ\b׳geknn\7?GA=z jiW8&l/}خ#Č13kj ~x˜:ŋߛ3gYKT"J$֔F؋ѣijwǎ,ivڍXH+<Ꚛr!<_h~S5554jZWW7"Õ+?7K.o>tǙ3RǏﶩ]>wWBɿPS^^Ƙ7o%87??T_~_3''ᆄ0LJ\\<ј΁t<q@ c+_%+dDDx{z:] MLs~† ֭bJ! tjE(|hWo"b>28qǹse5DI!!l6]1b1hmC4% M ;Pj}d92@ڍ6F>%MG}TZ#!.//뭷6nxʕ;vT*//^|yFFS׮]=z+r1CY#)sʿOr# +2WWg9p+ټW_822A(U饥f\2xD>_Ddj5F?Qnnڌ#$NNpOԸqOxx b9RwB/Ѩ~g]6xD6kl]]ݻ 3熆wy UmbJ=jVv$lw^OHѨoVdId;>dfHN:{+^ihI%2Ku{|3`x }&*=xÇq͇8i>_sdj;;QWCD0:>b԰/|Jj[vM/X@ &Uo㏿2Z577m{{;cǟ:㱉"<WP帗/T Yڍ65R4 23fĭ;#֧AGȧ?,//_z%}bSSSjj|Tʕ+y<^rrҥW---`rk|E6Unnn={opB"Um۶?<Ӌ-Z|e˪|aÆo{,X aaxx儈iJlޭ{n  *"??Cm%XnBLkzL|/ҶX}Ea0p暒Dė="XQJY]u~0?lebB5l{c+[ 5 =Psᯑ_^:b:øM"7J\f%˗ϔH<]]= wL5[ tv`X[RR)LE.^Occ?ϟ9;Ůl6EZZ_QQd"Ejߏ7-c FX`i5k? 'xvlx 8}zKG##{{tZ.o6. qZn)o88s8\ DJ&ۤwzXee F~Q_/ϯh$54No(a@5>A*#]hH6ӑTr# G\re\.7,,L 444ꖖ+V>.^]nܹ---..."H.S0e} PYYxbTbJZf0"h6linnfX<ރj VXd2Z&h˄3+v***]Q;U捌2𒤤(dMw=fEؒulFOD/-:{〷4V*Yyn.6q l6OR{~f0:qvQ~~-M 8`T9`]]U]]iVϿkHMM3F旖[YR榷"(++(++hsqDʔ31'y bWZ46664bg|}+*Ν=x]\JKi9RHKlBi$))̚[ycc=466FGG3!RzN`0|kjE"Ɍu*w ȿ5 -5|> -#@B@ :4D ѱ& &t/]dq/r񻤱@.1 0hq c)c&ݻ'\\\B!T*ZP(z@,555=<<%Lj!?q04aND#|ꢬ{Wۿ9X,FTzY٨m*r=b80řRr&d-5C}ALLQR+;0Gq|Zqh;Gy~k{t Z[R4j=<>>:x`0 *  ,Ki4 X,+nnn{ $Z&t Ft6&𪬝)|8 @X7U :g<>3msH1zQ=^Ǔ{sSHP|:Cw2ᵔ(\"NP@pp̲e8cƔsm~s`))goho@V3XSS#JMg1b֭[{lztؑ NwjoqAI Mӧ&NMjCzm5jLbbbUU~V\\=|||ʦ1G,heA;Be(aT#I.ni?DwLG*SO_%d'tmzmCA+FF+xz==ǵRUu~`h;@]?k:\q\o)q\#:W*T8csf\2Y//p!,-튻OccMIIޱcsrR;$}m mqEN'>-'{BC#lو_3))Ν[VVX_I_޷ׯ9s\ ӉLͻr矿d{n̙7o^q z& 鷮\94gΜJka\\<$!)yy9!!W^=qwwq׮}-+2:3%M7RˑOL>T Yڍ6++ Ë́oMh1L>}-G :b0ΔM0prE=m(jfVsbN3>d2`o&w? _޽իX (,z)ԙ_#>>4Xv{~ 6/Ef<'N:t4MՖtM:'5Vx~sfٳ-:tac tEEW~e7Ǎ4f8}b]]MvvfZڵE=hiH,G @؅hS.GM@ :/tdCaȐ@*ksB G#Wkk3cᆆAjի pM- oJ224Գj . D ES]]BhiQqoPԿeڵ.++JrȂ g74hbbb QZ_/7D&}ŋ{z | TUWWK }W^%g]kkR$xN4(Ei·{.wY9ptPZYN@ tM  m*M &c:Rj}Z@ tM #@ >ѧE`0 \\56ʉ6^\\P]ݤ0SlXWdWIIeNNF7xx,*ܹ$7s8>G]S#5f3>w7} rVnKLZr7pt'TpG*S!@ T@fQ6ӑTr#M ZQv @ a9?ՂEGZVbY11J\l6a1uan~~n J2 >`0,<x~ Y1& #<+$C.W8`0\4g|P0eaf! !\;2(a*S,'UF @ѦFUjT6Y,FbTZ#AQ´ :C Ȇfc~pb1 p08Vb1V#-``B!ڍrY\.GTY_&P ÔE ;Pj}d92Am2 +ѦP(Z/b=Hek9@БSԱv ЫWG[@  )XTdI2t6t6}Bu?A=ܑAHTQj}d#ӿu3݁ G*1ѕk(SOGeV*gffڽ"%LKEmB{ M@6 : Wkk"${9'R;ր |m?M@ V@wd J&@ @ @ $(aZq6!# dـz~F+Z6(ٛp5(SB;oSOm:~n u&OPw2;R:Gʜ9z) M@GPO8>A=k(k#y^zjjj:@  N'SʼOP?q>֧2tTZYHe1 d:*SOGek9_=]Bj}:*S,w)hh@ @ @ <%T那dـz~F+:3y})W:Qj}:*SOGeTvNG8}}BGeL>}-K(SOGeT6%@ nNߟI@CoSOGoiOOGeL>G;R'tTZTr@z2tTZYHeSД0MD Ngõk $K#i:i^F+A~~ڀ@ ԯCshEg"4Tv>ÔāT#L>֧2rG*;@ (SOGek9 t eL>ܑƠ)amqIRiU*-p8L0M'82Z$b bwJ*-Mnxeϣ"v]N9 46t^v>AtTZTQj}d#2tTZ%֧2rG*4Ni1{xNh4fNwj$bʹŅRf̵+aS\]Xɺ`Ã.j gV] !ܼ6l6 |.!M{+WSN7Γ[I&J;=@ @ @ %{e:lBuɄy Ro\#JHe.mp^:Z(suJ\?֕ܮНH@Trrr*\z-PU]yNYy>[ՙKD Wkk3cnN'SʼOP?q>֧2tTZYHeth'tTZTr@z2tTZYHeSД0󒝯s 0vL@l!G`G`TXvg矈 -JjmQ^S~Q0P -MU*,5wWevZA IDATzjccǯ:F)`-iiqzF;v+Ge@ӵG @ @ 0Д0ǽťKU!ͦRi]Y^"چ@ VUj:N'1 ]Sz fRh cݩ(hs:BuަZ֋'8^TQj}:*S,w)OL>֧~t eL>ܑʦ)aDg$5 akAmYa.xŌ0YlKN={F^:~|b]tZ66慏U]'aW@rwwߕ+gw91 A؆]Mg=zVNwޙ߶1rji:ږvwLzj}:z[/N;~x}:*SOGeL>ܑGS]@j}:*SO_TQj}d#MAS0LR)\ov70L}k9'?nLVg9'N^* &aFؠF; #N*Y^L}zf<$`r4b02h4*bO(,㺆ܲ6ƭZ=ũhga0A:'6cR1 c8:FZnIOz=<\b'jii*)-)ge{;6|+wNjlL 3f,={P4i})G;Rj}:*SOGeTv:*SOGek9 t eL>ܑƠ)aZB+|hGDD$89>?|@EEP]-w `zѤEQUuÃdX;L8[}dUh c_Z]8%3C{t\ـӑonbŷݺ fә):! sذ)& 2:TWWy{~ۆՃBc'{իvr0ر'MO[Z4&,t Þ~aSOQk[Iҳo ՉŮAtTZTQj}d#ӡ(a+SOGek9_=]Bj}:*S,w)T%M4﫯ϟVbgg7 cxBQ\ɉ2:qCfWok%U\6{D'/k'8ܙ|dAN#;xt(c#9ֶ_GK %%YRkذ)mhvD]]TZ}̙KM{>,췒K;wR)KͽR) !7uxLyKl6ljGBٷ Pn!C&1,JQT}q/ԾO&?1cf?nQd3@ @ ;(J^y~Ծw8 reeKx' Һʢ20WnN9qD~jc}Z[^{P8IXbօ;﷛; q!"p7N:QIO>yPCC1Xmxx<^ܵkSnnaoȀ- ȣKvnݒvt.RYdd>={II96@^C}vw`ۊ[5?& Çڵk\L.(AIʇ6PhCxL&֭s7oh[:{o M>OP?q>֧2tTZYHeS$N_Qj}:*SO_TQj}d#MAS4dU#Z8CfHrzw7M'ȿ}:'NИ~ԵǼӟ4{Px{YhCEexG_`}RwB[ ^4A(b5C?Nxע7WlxQ7ݯC8YдՆ!(&$̙Kc~۾}p/-/YsЖ߿㥥yo] >JiGc 9**3, y<4elm?S|qwvx0TZدm6 @ Z`_eس{OHj~aCw(JgVv>ÔāT#L>֧2rG*;@"qTQj}Z']Bj}:*S,w1hJ:t |ͪgw8K[GowHqueۈRYamXT<-+ 6DlQOvϻ&_>3V 7v>G|,<<W]] VHJݷ(o &XwҥÅY..c¬;޵fbϘ2\wAAᡞ=$J="<<8ynO<ߊ“'⒒F{x99I8NSS}Fƍkl5T`qq}Ü%,(>#mN5&(-Uj 2Y;u~wJWWAQۥj{} c?.)i?_zÌWDpuR &ac% #7Ε+nj&&P nz{3B1477RZ8;EE,0FuuKGLd=R8||CBS\]=B憂 .*ʦ% @CoSOGoiO̱tW-$>^0i>\:}}BGˑOL>ܑGQ]@j}:*Sj (@q8&픩֧2rG*i}Ǵi/@j3g7lڄFgP/ڊ;b#w}#8IL z76^wv&ל~Q, tU,SWdp|͸F&~p_6\>vIMlJ3_6ylYPPTƏwMH$^ɛMHT_P@>%_wإ|KJ(i{@ چM4DA 넇E?a,GV:Zvv&=TqE̮!5g\JgzwRټRńB[KS–a.=:zoe.糉ON﷯7΅ݝɓOܺu៚N?_t~nOf>>:Co_P(d..qq}o:gN{jn8= ??,b|{ L&g!'>+9/^O䍍GR]]V+]]=m}`Ԩq…,qAkz5e s򯉵SR\vB. 4/(yU꫕"Po8# YXO -Q{,g Bϛ;|;툈b>KW*&<0z̓'lA,y_?oi+V| 99W(vv,X޽Vu<qie֮]f6 8n5ZNjII-OZ96M͝M[Oo۶w@[51[1L4nO (rfB5|jfl&NTQj}d#mt c^45c6"̆InNVV?6Xc}bM֌ Z9!;^Ot%TQj}d#Mx HIIqL}Vի?#rmlʔO<*8=!ao0͕˛ܹrrn۫"=9.7J/+ß>li 78iH Ф)3=t?T*m֚ wiaZ567Iz,N9v4׎\.rPHU:cmjHmpJ,vLͲnJ*C=naKo}}#6~jHDG=vslB=[aժjk+K"rґ\sXX +VH$^6e9g߼y޿W_>'|m8۰ś7Z/<_X<"<MΝO'q/}zVSk=;^}Eױ'xuʔjSR^ptc=e2y󑞞_|q`4*h#g,_8Qo_L&ݼyŵk_4h%`FSի-|>Q_@]շn<{t;,Y3x{˦G\iԝ]h2uuu,H/z>ۡfS>LڸP&O'B@&vz)y>A=ܑTHe|~ȝ)Sb -uFouI zHe'w et w9oh^C{I@ฎ321q֭3Da^pGm8vt3ތ}/͍tlsō;ow-P0fAqb7748G?%LʈlZڹCw|._>ZYYгPbJd=Ξ=`|0mSSE:SSam}o|QEEя?~`ƍuK*//0<=}viKĔ0E > <0u677\9cΜCNa0IIF;tSY>>II @}}uGmp rc2nnnn}5ko=m?11bZUURSSouHHS`ǎE~:/TR[7'??H,ӱŊi.N]b0ȉXǍ/,{{KKK}}MVVZFF*i>O?]s^ UC1{ή u)sȜ8u'8^TQj}:*S,wM;hYT*[m?44&{8};^]Bj}:*S,w)hJر89I_>OKTbq"#{nooedܰ<6Lf3դ]\tq!t^qZ*99wKg-MUlg`EǟK{N@E*]l("XQ! IH'Æ%}yfvw)+X,۶Uw0<_{wSh5M;yɾ 2Cns7m}-b`}}a91ņ 0[/++#.\8ҦhкQQ=_|k&e|}a;mSi4]WUJ6 LRm'x^\/677YD,y4ZuG ױ&;X,?:5^ob/\g# 4N ͟NJ7.37~i%$r#cc,%j7? 77и޽ҥ_];+#㊃K`0 #UOryI̙+h4s22!nnCN;6eGI" r Xptßǎevnrfn 3k֫2`UU+֧2r2IÃQoަoqC{8TlԽwk9 t eL>LeSА3 [j4K6{ 棏Nں},a 塝/mZDYii},hۚJ.8 ;\Cq~#eh~3_t}دE(d9ܒE} {aaq[#҂6eO-fsd2C;BPP Xj4*&?..WT_?$㚔8gݺy$g*e0P]]ަ%%y[۶GO􂇇_TT޲xx|SM3= IBBb 2uee/9tU~px3gXd9r8<̼j.1tTTZmGF3,B~vb~tz:!#N{woHF.nvPh49sԩs֭kN*((T( -yvN˗ύJXXC}bcO# b9}|`%wFGw7夤Ӧu'G} *YCZ5MT dҍĉv'k>l:/uk4ˍ&A_*;w4, xj6ϞCD6qxSޡmxOOhm7&dZ@}b (k :8fIIn~~zXXBXX<13L5E"VvMeybI2}nzEq+\ńi~,[iuZ aTZU{7Fl}U;y~jŁƼ3tiJ`kظ_cn w70[Fju _ ݟ`0sz3tƓ 0En[;t6 Je|jȆZ^ۦ(++J}}C ~#afI,iJYbr&-zK6>>*N?magzskm>Q(mnF}fCl8㞒2jԹ߸͛ɩP*5.rssrY{ppq#==ZZԸҥ|KW>z]uhiQZ\\aÆCǼDu;@_z} *oVn.\wu$ IDATv/wni>YґcԭM,wémzǏZHDow=uZ~OU&}$MLQ4$LI>HBs@LDsIUEYHk}iFڑoE:CKE>=-fQU%mCl\n.PUly?j{*vs0!+mà F&Mf^=t+ھS$u1_ l `[}{~Nׁ5񉱆KsfMg0YYw0=28*2p}]w߁Ǎ9|l7`%YZڅ>p1 ԧXdd\bup { hZ\>7.r[yK͚&Ů.߰aA^~xC诼FZnA;:{ ؎C ..6rmZ뷒0{[P;p>===ˋz~\9nBٙl=ïCξj+*kj* ?>[*.߳gGE򢶮.웄̖EXl7?lFqZwб =QSSÝ;ǏS_odΞ*n,(77L&rY1LRilF}ҮdBB"r\9 𨇇X|֭۾^^=7O JLo0dDHHVWW\pœd^~~nn2>_X]]~?o((E!dgc*'2":fG@>#L>ܢ2 Z;[oͲSf[o}Cx;h{-pxmw6W9⚓ p odq'm=="OdY;vl!YJPooLԋ _()vACĽN!o[C4M*J$kӯz}]]]uu5iA¬䀀pR)/Jޘ19H›/odq楺LмbY[ UEiB%b{|lQƛpAN݊TyZ>®*=~MɎf<4zѻW3gL39sƴa2qx\lt\l$PXH3z)(IK`g3yK4}„y׮Ij 9'0VV45XىݛǃŨQO1bUƃ~ssCe]~{|n3:D&?c㕕%#G>o%$ӧ;g6V!h`Rm6J< w L`P Nap/]bСw rG~N^R攖;wёݟy~ОTYQQ=zLO1_B/jف׭ |r>9wN$YSS};F-Ip;wp9qq}4}D&~KydBampeAݻ^WTCBvB df>0$pcyf ý}e2Y䀻wSr7{Of'9y/V>|͍¢2^}uOx >;NHO>믛n3޽@ݺ۷o^~~nXtCHKՕ/q Z]qq qwo3Р(.Jx´ԥKs,ww!N/,0m͚Ņoq&^nԨ[QQC~ʅW\mllll3]8~wvP+Z9 4eۇ>qvnݏo96!'V}Kx]{7gÁ>֭tF>lnq@oQ(h6ʱMIPoS(X4bzk"]Fh}Z'TQ|Q烙Vv79?[P'\%loo!HX"͛ < !D"bC½z:sL;0Sn8G=;wˋ CQDP#UdcԜg+UbF咺(s2Մp0e✪~J;7m.,y3cLCBNU6O6~Zŏ,5Z^ΰLm`1냣i΁ȸ >ĉ9gbqCj]u cǗK|.z^Mkrs[\]\͵V[]*7nV@@Ν_eg0{o q={ *جYo˧apNZ>ÃSR8~|X[.677UT{z+'**MŬKl0 -|}d ?]Q'k!iL.YVdgg#լYD0L׮I*쳿.(-11I[GR)/7ߒOyGCmCw??JXfg✫{?oK;_/Fn>a^! xOR??O6 ⋿-^;7ZZ.y'}:^Yzo>%Z$rHdYYWx u={6?Ĝи ~olml%"ԩ k577\o~8 1q@KKS]]a.."1tQ>|Juu%qms'> ,Xso4yI߿Y`wW_*-ol..v 22h4W_a͚Y7o`.NŰaͣghSZm:\ 56,\Ph歪.d7Oa} >N'\][7x3giʔԑ#;Td2p#tbxzࣰee7#nj^aee@gZG49pׁcS-'& j溺(s`cHbfF[ R)׬r&!NKС}<~mKjjZD\.K׷ꍯ鯫kb1ǎ.;}߼,h,eXN8lYWW.-04MM0=zϿP#3~uOQqѐpP&ZD#T&AƐpGz =<܋ަ1cmr\.&nMR;**O] w eL>Les?$T@rsoZ̼y:jM~w(V7_#oU9fpmʥ^u4¿N]G /n2js`|/#kǬp2Y0;XNoQmm9^RdɄ)S8x{u:m]]?6y򂀀pM,v lV@ee%G>rS2/2Syyjj "Zz3f1pXc|lmmeFem\__^ziOPj[Z/V?[L7~ZKe3dslH22t_dgZZl}}%OLo<?=:'&v׮ohh8%_0Lob4ns<0'FAM#{ِ줪'Pȏ۵}Ɩ& _%`_Ç}"44_?ԍ͙/VnOI/9x0677 zy$ :uz=&Z.??RP`>pZ.m}&X >mb\B|6~ ر""bKJrs-7sOM kbH$:5`..f|U -..LquX p #GNXxZz뭗?W[+rYnصޤT,}B-G>!Mh}dEe S'L>Lez==0!M.Lަ!Gz_.gM m)©{Pr B]PQh}**,'S4$"""ڑj2ΝK'X웆FvC~;qb߈.ɾ'1:{\dO:\}>?4e81Uf]Ll5_;cM:& ЦW']i;/fMomثeS<$Z}o} pqqh 5wJss;wH* pxMMu%%rU{l޳gogHV+JJo䦦/|\t:\TUUjWYYф Qef^[`O'+*VW<ɓI$2ԛՊ F>lg-yh ދGv*l7%K&Z~}ʊ 7Kkm%}WED^'7VVޭ1ih~3wPHb[ZJJˋuqZZuu<̣9#VvCcw+7D L^֎¤s 3|O=*qpED$;wn:gf{kcqy1:c41 5Z455655w!uf@TT, 2p8qLf[ [Ss(/+ ֝@>T͜__j|>[`ccta&&NT j' D6rҔGL1m ;{&FR9ppzvR&Za]DSQh}d98אMBƎ)7)--(--l+Z >QE#p,jRޘqzg]јP=R% t4@Pe$b}L 0M:u9S\rqe>YcTЈr&qJpNaHuuk()/)ɷ3ZMo_FE]]U]]:E|4յk';(bMvqTZ4444X4ag}|ˋN#9"pgfҤ]]KJ{*2-IQP{^ uE\%TZt:ӧN(4OfBj%O -5-<"%ñɼNjWxrgf|5m{qFDv[deX߇:@ kӾަaH6#"b^]D 8א0aL?V]WGL%-/ή*ʪ,Hsq[ɤB/45]P5tf38Fb43vuaT\8_wI5֮|+0wD.3f:aNG] o1c|];Ιiq09++]dtjNw?`2ͽ㏧[ݘ7nذ YQQ QQ6˂)-po@̈`:{{]ŵ] zY73S'O2)NHS&ZYN2 vvl6zxԁM#0{端> PDSr B]PQh}**,o2R`p!a&NH[ENvݝUfm+Ɠ+A,X 2?Mӻ<n<>7 lGU#Nq#762rdn;[Y D :jW6 ~㍍?1 ۺuõk!5RA|}BFNQvB2r2I-vo3*Mj= ȱMkF:o3=:A͇T&Za]DSQh}d98א0aCkj7Qll;K{}d]a`Dӧg=cz=`z}]]ǎcaz=>sF1LTcf݋z]ary˯;h^d]>CKK(z74T߽wΜme J3lym 3. !Νd_ӯ?wׇ þb}ll[ҳ  `2eeキO矿?wW\9yXmm/ ݵG67ӟn͛2e+0 <=CmNOOxtr3gΜ}Zt:=pZn1 IDAT=ϟf1 Rj U"9E} iD#T&A\!KD6+*Jt/Anz^MHoÛC *O]ˁ. u}BEeL>-]}0n_@XvT:G7I.MNӧ/Z4|:t%\m|)( @aD+ܹssժg$5܆RfxLo};w/!!OA)))_|jjϟ/;w@@ӕ?Z-[6'f>GaX`?>Gi+8=w !3fkldD*^R76*""kּ33=<% ŐvР;-[0S⌑JE^^߮m0eM8N'Tl j'),'S}sqm\{ަR٨T*mW4CN{a]DSQh}d9!a@ D>U:Gpe:AX0iqqAUU^*B<GlF @t}LF߾MHѢ|4N7HXh B1f3tڃy}}ݕJV<N ܘ_:ЬtrY!t:=,38XP1 dlj5)>-P0I0 lfDNW4:NL>}m;Ӏ vkMlM8Nק2TT&ZD#T&Aߚ6ZB:&jD6T*mv*p;Ե*.L>Gl$p"  f9t ?wR83qdҙLa4fXd҅BqTt:M <4w8&J3NBxPt,E"L>LeL>GLCm2tkZKަ@[L8IvBEek9o=]Bh}**,'S4$@ |Lz)OEoOEoP'DTF-Le*p;C K8jP'T6}B>jd*N2jd*[v}-1 $E68C g@F)lb429zfb6FF )8u*M@ $`Iw"@ @ @ @ JDGu:y]dl@t=P"Qr\'=pؿr;XC&v&Y 8Tڞ1T $(0e׀gJ0>A)s8}T!'$CN\9Co=l'DSS'~cn5oߺ*@C@ aE jA3,qTT&ZD#T&Zh \mO8[Led*\C7 ;vlX@ @ @ @ ]F 'f E@6 D+r~pf08">fӧ2TT&ZYN2DM8j8}BFH4Oڤ'd*,'SFk@ D@#TYLed*O8*&&S٠O]А0iTT&ZD#T6ǹvCp+PdprRɰʣbŸZN(>}|j P"Q g C  Kc8}**OEed*OلS6IS'mDCԭMY|B2r2MACʀaXcZ֩:`l6C,fh6͑`Վ'{Ahí"YRR'%km1< \"rPw@8?/Oř'd*,'Sh}.ФbmUd* Z%L>֧2r2AC P^Rn3}F2aaOfI9Ylg8SFb1R~i¤/]=xxLMM P@ @ @ N SJ±UUrvX%#,)ʪ,ynOLs &46T+9Xi0  ^L׺%i0؂ڲ6E?TꜜrϋW/]VUU}/TVU*-+7D{hv9t ?wR83NpNY=c }OEeL>Le6Pq& =p*h3I9ˑOT&ZYN9hHd]G8و& Dyzgvш-FWTUׁodO{@KcZ+L,@rXO$N3jˮ [c֩1'd]<(:8@ @ @ DN,HodO;s#duy(SQ)&=_8izoO8__T2?sKz-FSuEU1 9*_DEZqX@ :mFDZrCHNFnu9 uO2C 燢jQ<02GL>uqbmT6St0iTT&ZD#T6 #Y4a? }#{ZQa.{FDkqLWp؅<{rۋzDߐ~_ {~qWiٴ/|ju4}|WN*Ybr^^zgHl.NW*[:DpHlSOaSsrne G4{ʖW^ٶt2A @ 8BşuOT&ZYN2bm!a2  OEeL>LesА `kRt:|}1#OZαÃ::.a\_lOI5=.(&OXklO!v\؁y9 9` Z5?˗J}Ȳ>>..))--oZHHʕ?2?\p9nx\MјLه6K|Mpd[k_&p,.%y̖ƻws޽CkŁ &V+;{D'/]Ak_Sp=c }OEeL>Le6Pq& =p*h3I9ˑOT&ZYN)hH8p_\mDؿm:,{X+BU5xE[7gE26*%w<74ũ}hH ֬ c߮ř\[\\w˴ SBLy8D?l0-n|>6~۷'$s:1t:#%eqM.V8ݛڱ{p||?OnhLL٣wvr }{~R*[a0LO1mFh?NJǎڴi=jm/8mbۃqv"g?ڸ̙[x֭_xx7XrDdDD)|rH$)-?pgxw.]@ ESq? D#T&Z 4Xh0}V OEeL>LeskHx&!cǎK,Fq{ēS:;hʡdWxŎjx07L960-Q1WRIM8Y8 ǎG(tybc{65Ԕ3L77O>_晒2߿jGGbϟ?h2eaHHcHNH׷H()ɫ*1\.y?F{lر3T*SH3%Zڊ 4_lp\Xb1&/ 3/^vXjcziې8&R0|kw)wmByx[wdz<~3ayy|KU$c^|SXX<ܼynǎi4ܯ(Eo&%%y/0Fp璒<qfcF\Y^^; .ZaRPK?~|OF岲 Θ`~TgPw@8?/Oř'd*,'Sh}.ФbmUd* Z%L>֧2r2q!a&n@jCBF?(Qy="6qo9U̮u21 =~K}-Ɨ&ܽ _YTT|%?c_9nHC 2!}Og?֒ r&r'>;MȈ| Ͷ<`cֶѨQ}˖waXII,%h71-߶7`DGLe6Pq&ž=(K9e>u!x&,G>!Sh}d9ʦ8א0sb0YY׎AZ50d8qg1|!"?3oR?K2:a؂d~-_zzH ,aj{';m/KyO 3:]fg?Gy2,,UJi4zr޽ d0 5yy(,<-U:b4(,ںu=UL&kܹ n_}PttRSS_D=6 dMgXXSOfر]a2Xf239d ʼnd뫋ro\mpFqڵ?D"kjj5QO<JK/]:bۄD"&h)S[/644""Qlٽ[Dqq}c\]W?v>}<vS{ 롸GFvFѫJΟ?h4 g?_={&K$2  /IgC5 Pt<g#L>Le@sժ\v3z,Hp8Z%lJ4}**OEed*) cРq!!qm^OTNEhhP{<0ypޭߺFn =pQٗd*`9a/DllT7#_‘G[kѢLuO;Ы3xlN)S^0yhY``dbQA77%K6v6pcݻ7ivm `|?0(c3+*kx7ϗ=0(i3-ѣY5G&G3ju2ΛfРq&?PX "&ef^⋥WW(6̙ot]]Sjj*L_^CLԾX_w3?Ɛ!z@C pinnb>6wDDbQQ vi\39yϯ5/?;΄ /͛N$oJ w#;~4F=Xd9ƍb=ZLL ]@ @89CpR;@ACˆ`0O>\px4⥢̋"Va} "n}qX<ͧT:@T0emLkbrXk>,Qk1g$==:rV_i*]v6|ĉ/@j~nnn=q|{ϗ.`8Az̙o8T]]eSSO[OH:>W3YYy7VUing0ݻ;vP`a&oh9xתF%xi*@``ի'S0R8sf=}IH5ȸҭ[V}D1fmOTOIC<}۶ [L߭Z%6q]cw`pp̆ ._>vЯn]4Ѽvih8Ճ`!Rsqq}=N]bq[.! 7džoWt:#2ixLÎoժ}}C򦯿^~r딎22QΦݽ 8p_@YJumlD^QQp>D#T&Z9] W^=u_.plCK4΢xݺ~1B-8v{|NR:l>d/o),'S4$xQ7gv-lz=Fḿ+]ÇO)3yK3?4Z#GČX}cwl,xH!CCNdz۶\w? IDATpӿ{)6l :xth#M ryՕWwpg?uק2$ ;kw&X!-AX{M*hnnJω;tsZP@9ed*) A#(( ȑD7As}hY[뜫e)\kRKcͰVk,򵶠h`nW}g/?yݡoD{G[LE֌^7%6ʤ^͝WgnF[,jr:cˋ ?>%@aaVEi ==ȡx .h4n+a9BTT 7_,}Iz_؎lT*uuuW(UU%jrTxnnII)2ܹG|Je'4V]v,[7ww%+C ucvȐ1f*N*啧jjH==}OPxfZQQaaޞ#-;vyKA͞?ܸn4x˩L>Le65;xҟDk8[P@9ed*7**a͚˫cc}n/** ω;,((T(&z)x;te['Php{HIIQ*.<  JkkkSSSO:e2-%&&fȑ...{%vCHw$\tT EX7%#^Z86/1;C܆֞Fju y3 N?5jg''aܶvb):,Zr[d>@f[(O}E>JN7|}@uu[}NssT!u(@8 BҦ:u.7n|wsr*yjkk\V!&fdHOO5nF?{Ek.ܥ^!Ԅ"U:RDbAbdž(P,RK$^.r)q\%|?|t3;͛}S]gqqd|ֹv# Kt:J7cwmK.)>*Te2\sGJ|wK.}>ʷKK6=좼UqRy M8ӡH|OLΰ'pF#pˠP(3gΜ2eŋ ]oAjkkKJJz=lذ 6# 4hɒ%rdC]dIEEE{e>u׮hvf"MG`y )C頾^[[1mffr:+[)$EY^IQffpY75CimF&M{3ԕc~gm_Wgf~B`:ٷ練Ԩo͛J6t`Td֡}^Ǎ9|Z 7`w+rPzElH]|ʌOBz$:ѵk7آZ`L`,}n3x Rs欦UU6,{'+>zNK6|3t:'8//Mla*/? [ݺb*jzLbc6M3w'ƍ*գ|[ qD7'0[UUօu!4Iu[јWTUW??W$*߳CQ~PZo$?*tb]Q+f2Nl`~a&F̚U=uT{@ I}lXXTDDJDP("STFS___]]Vzka46wgo_|*puxzlJ]_qnSvz#;2$$2!!0ƍDHOzx{+rBph#FL L&ʕW5P,JHH tss8ӧݻjjgáO2%:::88D"Qq7vX$d[s{~F'ͽVN`ZvhbmhuvcHcôZm~~dƋdL֐!ct YNNӇjkYc]]y $8CC#juvvûkswwHc8;Ƀ<c9gE3[nn\Ojm4J;>}G)В'c(i-9.8$]‰3f 4y!Vd2{޽;''x$FŶ}ԫw)*:'19OTe_5խ1YI ;iݞnkw{u/,,~at/̞~% gm6c(( LP:4ܿIMb%/:F4i~rY[HAfsUZvpa{C0xfB0_W',ҥ=߷Z R.KB1OWVxx9?7vΟ- 3..1 d>wuC1`Ӎ7v&D^7Tl BBb ++wTLER#$!R7W#RX777WWU 22#F7ކ>x]h_~U޾B0X("4hԨQn4+/ׯ?:nnݒ$r~_|U8TcN;|xgчl~0dg0D".y&3̬Y/@n=ۍ/6oٖrf^݂OTDdV8ZڭΑTwU2(y6'{4f'ܽ{K*5vOZc&}Fg 6:u}mkضARmZ4J1lmxN4J9~8zo=1M!~BRGnhsRDŽF8g,OOϷz+''gʕ :njj:{lttt`` ~'\?T* ==ƍz R#>v4](3>%;+C`q]FุE&٬F<Nx79wASZ<?5`͛BZ[sᯏO[(v\} OZ|ìY#)>^(t?zFݷkבÆ  zP_ߐU~~n۶T_ҭ[HAAAIIIХ@%%%JJJ'q6mZnnn]]{pQ&嚠 "S7n\xdfff]CYYg->|s-j~e3-;2jy 54d(9ȓ fwvs}-WrɮJ!cګ2xcqmc;2<=]Gߓvݹv- |}]H+:o**vu,F[TT0E"055eٲ,ݝGPȟNֻCCl|6'Rj|49nT[F)Ӧ=kY*Fz nn"F)=u"0~b:^cB0cpm>pڒ ***^{\*f!z[\NIYoGsN޽._R BC ;c!Q~FgYc"Acvf_VeK2O5ڸalpЕvDX7} ]fQ7Fvf !Z*+O$_|l쀀sW̓'ijz03 3^_}t"u?oNnC|W8RYn]M$7nN@@Νdep a_x8*؜9%Z8:''5)ixppAOt:322ݫuu"OOQfܸqH[K€OUUefҿN]gӛaF̝zjj*WV]muf$6xD}ٲM[{A}uP(TK~zÖ`;+ Kqߓ&-\^xa%\pcl}eb̙rr҈_:{問TZt88HXdf O =7q?oXqqnHHlûfe7>ssʦUJIKKqz4e۶MǎkM__\aئMÏYYD[[+@.a>6;Bw?{qqaO6W(tro.s8r]?ʕa^!]6O(~]oH?:2d֐:hVMMŋ/zEj'|:1˧VTiO?}mݺ<]++Kj*33oZ^ԕ+ayq6-R!/wl6oѢ~y}{Zo-;oR "( '/ mjjHJ\OO? @~_; ÆM77=;WҚmڗɪ7lXr<˫~<2 "9aL_ߐaIIÞ{ Z bWWw"ի'.\xPwӦ-"D?EE/8*ymܸ?5Jcc}LLOb~1t11=Xa9sVɓ3L>OX, cp70((?,R)Z-11$h@ y}KcW+SSII?)%H҈wVkkk>;H2t:]1fTww'2 4cE ..`Nx r8 :&@@@@iiU |> ð5kf;65?gŊ cufťQy2_N`6M>q7ֽvʼnDC׏>R^au0nCI6o~?kjʉܥK'M'Ʋ\o@"VJ%FԩSV+hɣG?7rb7t555ݭ. hT͞={OәT_70׵ޛ} ]mKK|ךӧwkgY(N IDATLV-ט+Kv00[$f8cvڵq]hYY)*fdkݻt_g%N&ξCDV(jvoQށLPYYr/:#uܸl^v͸6 iMCjw׬9Rd۶vRD!CCcY;delڴb޼8>rO||Ly9 c<к:ùAA"8BٶmNXFuBBiu@ZTJww146pOO8uJqw>wTaRcN-))$κPkTX/8t:U(8ޫWѣ' :#+dzO: L&ͬaK}kj o5jG`P[q2"E%X< Lل$z;|3P2b- Kdt ann"p>ּy1sv۶ڜ˓=Ǿ"1?jTMB}NWeto}uL hRj̲!o[a3P`*ovm &&WTX1C(T9Riw߭w"7 d<.K^tT$ `2 $_7{SS=?og/P DR*7 ֣Pȿz֭rVT*$R v ɋ rsq]EEqUUٳϞ/E"o6R5(B6n\qfFFBY,Ncc}yyaaa[a1/]:ҭ>x9>>,Ph{&XPQQq͛EBNT++kkqd2o(/BKCzTZf4?nXn2:e˦̪Ҭ;?(;bTyvk쳯Ν 3g|hDD×J+srtZ&gF΍ZhλFP 91by\ ~~nGE c/kkT_%.1("p%>; 23錐 66jfFJ3fLq#%**'V+BwܑɖOCZDz  Ü%kmn3ڜU]I@_J \tL~aٻop}o =&T*51qoHTT9sV͞W:cHu=NF0CffV==/ juZVʼn4fฆ>q q}FAA.a6lP*YQQ Qc)a@CC= jeZgFa4-a4&l6JF9@1Ev@t>PBPP8mۆd)\9R J8p`Ǽhls8֖8iӇ|ɓcNdZmx={7ڡROnj?,,V[+_&aB!G<E@(I # 0,>;W\R%ZM8xhAwFd'[m!cydhcIw Y}ARmզӍRtɖ4wdS:KxĈ555g\.?}43&33󩧞7!f{ǵ 5uNVʣS ._G씃3#Z]ZZְp8IN8:1OFpQ::0:W*QqBصˇ@ ԫ"LVU\w;!ygӇ`Br%N>+ -V~{[78WvKLL{7-%ژ1icJ@ ۷oBpkʊ>p|?ܻw7n={ɓMջz_xO6֭+ 700P紴W'&>q…gO+U766 qaqvɓ'D"~.=y獬[n/5֥Y?錌:ӭ["[j]|'oE3J&[>ܑɖOږB&'_HʘLF@@(}y}ĉO߼y6m**Ji4ݟk55Z-6'j,Zڬq 6nOL|yK$\gv {{{ɓ  0|'+W5kh׳Ut_a°6'#ԡOO^dv8;hMp5B蚸T oy4X(rGڕErrY3N:[P7ޘ@*5B;wҥKϤ Zp3^;v\a V[RROiWX8q 3f#GkI]߸qcΜ4h(]AAna#kּh# >RXSSao;|9{P_tuuq| .2d/#TMUVW!!NnV.oWWNKe"g>'9c+'6;ѐ;mM:u68gmQ {g 99)V$&{V=GZTTT[[kK ."bݻihh`ّNuHщA1t;wR4KJJmqWÔ[Sv-Owjk0M-r^P_)<P4ْse*%-~oHv;&INYz3dH&pFN68i O E>V.Pʤ55zI#CffH$0uiiBQРvf)+DGGDn d T^b H1LSUU)HƆX@(owT>_X]-ihmjR ^<+'G.Ru2YMeeLV/{z <.e8JS-Yd=<y&pF]S@B7{F?-<O U*ksrc %=kl\sPp|9Rkv76nLcB;~LX]M{^y@ >.B!"0˴ZL@԰0QQ H*+F '>05V{ 0a^Yapwޒ덢 O˪RөC?OBէ?TDRj-~~M5̔Bt[2@ccCff~MMa+vFvk.iHdwFVUgLl[PW]]},h4$996o0t6'j˗6kOL|#%\@ Ώ5F'=EEZVEӢpxD]E g>666 At:Q(ya~~*FKPl:BEcj4چQZ-NcA3 %,38XРqB04j'&ר,kF534 p ZDVkjRk: `Јu6II֍IФT6 ØLl6DudG;R2V,mc¼BBpolThtJER{V>X)~[ !(ht 6'jz %6)LVGjm:(ũN'lHsGJ6cM;vlhqX Pҡ|v:;G):2vqIQh4(5m|pRc1 8FXtPeT 2&lBb&<9V;Q2VBLX0r4f=`ڽ%f[pFX!Ngs 䤵t~:8d#)ٔF @ Ȁ%b p LNk$ 8V2Qs>X{o"(<;MȓZ#%8ט>y[3>ל㼣}9o wF8pSZGw,0Zl#EVt" MD+99Ja339'*~A}zE*t|A6!UsM6!n },@8 d<#Pk{$N\NDEE]&@ @ @ AX6lB=&wHڕcvhKYNX&WȐ@ NAB!oM63VG)l;R23p mK`x}1Pr Ν,*@ A*8qqIv:I , 0@ \@ g&Kز|@tKIPAFHFfh<$%6a'Pr Ν,*@ A*8q2N'Jv:I  0iq\.WTZJ 00 ko RH{b]+RJJ$ILoYnᦁ/]am@ NZ%@ pF*aIJز|@tK(r%3;Ņ"FDQ/˸ոU^^V>(~J3Ңru |ᣧdeV}w݊"JI1f*No/#vDnĭISf_ txo*">^ m^\FepkZ\МځI[ZZNy-KdaH.[)wKњR FPr ΝKtL@ N';dxhmӜTzA r #:.Y:#&OcrV"=Fdߙ2"ekmDURL\+UeFu|(tݲ0yD̨{ZRm]#Ui=byhw:v@ @ @ Fr 8p(dر ݹ ]F,KJ29y(;+SQ,f|jQ?p*(}M&{eap>K<fTBp:8ZR]&no-6a/kv(jb0 -//lout0@ Ag ;L2-G D'D ='7Gܳ&Z-qM3͞A1`^cKqs'׮x4V_6 s8@'믏0,mGzu-[Q~NMybtHZ%k'x,XO+sР Z_?YSSPH/_>6mI BC쥿A IDATtҷOT*$/%BVVZb8S2cV.Xŋ7 41o\.Ri*UcaaVVVJEE|o}v0)o_|}Cu@ @ UuǏt:-Wo={իlj@PQQ4[(LR[ b]fc299{y$TZ[VӭH ]O=)7~Y"֎[xqw3O}⴫~Sa yyi[+c XrKXX<ܹs鯿6* ||XߺpIIK/ 0#x|o%%yDʕD=_櫣fg^кV "VAGǍ _b3.zĒ%Oa;ܹ}ת̞Ju\{!@@ Z%0ɀV [@ Nr ;)v{'% 0B!ݾ}N` TZo\E( KWv)B4 \5 Avl|Q`[xyZ[xgEf1 hvQ\Ҧ:zv/[`)"tfoCFL常[Rڃt ix3juDRBcڴń?ر?p/)ۘphI~x0|OVJFGt BZZ .c"#Ϛ,, Xww/hiaAZ_S-%F={m>0PH*٠ɪMCBFz~mCaa3cp׮Z/|1:K@ ጠ&Y–#A.:7B*4R򁿁:FTٻ!B!. #)8b ZF~Wk)!Zy1M;>ߙ㳉_v&_1e Nzu;fg?C g29R$5569j(z~+Jdyy/.(0|ru1 2m{F:t ou6 :B!ݿg"w}9p?Fk:}vN"ccž..n C.fd<~|Ng*hڪ;6,)%. k'x*00*%%nMN>kg11D"o ê+n߾p~FmE,AXXR~^.a3bֺ@۱WcO==4ڵkjB1TVXQ (Ӧ-P(w^=ftg! w\.?.OppWhݺY-A0ʋ/RǏȬ@ p;H p0DRrSRi3g.Ç+-71(66Q(s3.^}|Z1:#ީجxEQiL@- LH?js7It&3zv/K7o, ݽ{FcT*}yPXS066^(ڷSq!"߹s1`@@d@@+'OzCFK517>pw g_|aKAÉ,Ԣڤ 3pD77ϛ7s :?9f%Kƛ͚ ##ݼyAi%QfHVMhEAbڥ]\ܖ/&<>$88_1)\KTTw.WPW'.6wDDBaa֕+ ^ zyBfb ֛vgq!!q8oNGVffK`6o>E{ߧw̤5jw\^?2 c1! 6٥@ i"xK''MZ@3[q8*6a‹pƩZL,j.UqOѯ6Ϫ-ʸ8i)MMZkTNc%l⚐"w9,IK1g1:rCnZ]Ǣk;ϝ<%HIpouuޓ'/,rٲI,gڟt: o߾بtuNI9oI~.}@ܼy!zD~~ƍ7۷/J$EE9uuT*[c}O\&>rDn =Z4 00r3,]tTZde6y55,{^j+\p5|i =wxΖP(+%*ON>{Ɇw`c}}C"߼yf̘YoVYZz晈pM&чX8HMld-E,XsPԧ~ \9f81w% =ذiN2UI p#/DP(^rKPPdg߹vxyy@6oHORmoabd_@ u>]xy|7kû΅m}m78`#`6 D=vl3_n/XNxxך~xaTZ@lVmi b[q)e̸9.u-v TZGKSݫ'c;f}grOd6/a7GҎ}x]d~m4ED>S9U#ŹVy=ͫsv튊_?W!e'/q֭sz! \k[Fk=޽*JfZk*//صe=yK>>AAz_icZ[j3!?޻{%˧O1MyJV0s1ٖ7vѨM]Bǘ1fM>wnfvgٷoD4޽u͚bc{F޵z{lذSGqU#gO ðn9QQݩT*s z;}Gyz޽[/AttP*͛g0Bf>u6ѝdYoRؼyto80M?b,^'[Q,@ a vǮ[;/75,m \.G4slV0LнR&aifm7)7omjk4ksփ@ F?T'N=22Y[o=+Vn}CywjHQWW'Vefds@tK)c'()ij5]0"BAɴt:A&1Y[{yQJ3&zyAd.Kt:^n_xNuu src{!ˣͦ0O*tbLϼ#Lu uh[&/3TWÑ}\\"di4jo?ODrx0qDEu_x|ղ{zdbUРHJTFbqMminn={}_|q]bO>yɰ&Jׯl@YYZԺ3nMTM(lР4lakNgg]7=Llº:_ڳ#0-\A7mZo̙OK,Wrll"ܼyF%[/Q "F?T>J 7\P*F/ (.NOIy2kS^c^혘~#<q99ކ ?Yx񴬬GT:ѵ!@ ƱzvUKѺu=-6zk_=G& n]׊ҽmیw%\ bCZ(-ߢ,d,Eh)}In AAQS.JHG?Tnш#tq 0ҥϔ)۾f6["&- Ze|ddJ@UUy۷گ)S^vOKL {t:3)i8A7/ .|3p8P(LHH,|+O@QQax~T۷4FohP=0qtZbh:u2WW+]\ğ jZk:)?PG @vSeh_xgyM>whNNZ?'7ŅmVrQQ]yX!C|bcN%cpyy\./4 hhh`fc^ټHT0$#б\0 ;vlhsxybDRoiJ+lY`и}G?^`\>f~SY0~2~n<c yPy?/n\]\Y׵5z7FTg%wk:XN]v-clP@KylWY{\1waƭi4Z*J,i R VUk%j 'afܲvb.£ɞ  -m+UZNӢ(++}}CT*A1]$IX$KZj 4C$ o 8=7"ό5cMMM rϠ.VcZVkZ6v0ksKɭ>ih8V79ib0çTU޽{5>>iРO^S(ԦmivOpIVMR8HXjM5`;s-PĒ봴kMM-A\dd\ұՙґb]8@  2Ʊvt4g^qݦMޞjjjX,z!LNNKKHj^EVk]gqqd];?{Cnz`ͭ| I6G D'c6bp߾c.?~W@||ҹsw +ck$H{}0Xz;>CPt:{',$\9V&IQWmAR&qxn]s񷑉#|b{}vtc,uY,py1Xr--=?5jGDyJff:0*2\о?HwF>zvVF0+rP;>*Ͻk2ɤNz/B=ҋ 身k˺vW׵aAtB@zɤO2}0Le2~s~9=3}@zP58:&&^^/|TWA[Ttf-K-577lTHHՕ觤]ٷI 'pzm7BYy7'qG=m/  00 ͸c]lld*//ܶV<E#G$*UGIIfcw(lTQ XѪzt.5ztVLLbn33'VU>}N:uvppTҥŸ:4TqD(|] C+$k ŢnowpRY%zXTyaH"R'(O@H8bko⚈P8˿?Ҝڶ)=f{VWRڝ'j4O葏L<8olC)c&SUUvJk׬u@SSmpb޼U|ȑaۍCOf0wg RR@^_tLbХ,YCOY#  Cx| =tӝO?}~EK^x/88̒2g .kk O5!$$h4a D"aw._Go] Ofd}'~TOxrɒGg) WOÍOz-fE,u5hъ;~|ͿZRru(..w<9||w>}w{II 4L6< } ]d2@||%֛O2MpOn' PcH@aڋ?l`=zW_hI rm >w8~|Mҥ|M* Gw2)3p&1~ F-Ւ8&P eup_b clޑҦD,8Z +3׬}E xm{凇۞Cc2籉i坡?{ٲ⨨Ļ~NR *,ReS }d,,vlvb⨢ =؛׻5Z \˗/S_}7޽:޻ M^O6X  z{؊84T*Xn,ĉ?ŋ5!'pZ$@_'QtEE b1b^o**j )--]EE\.eΝ6R ERZbbښ5kvڗru~T%$%ە$߶mfñczO)U wƎXdmeeD&?]|қҲɎOvڢ3e"]Ń!.oa\feuo%EE*>9 9peֽtOժq_UWWF_.XxNTtHA$o#lj}'{-? t4",Uy !s@@@ر3v_eIhy?oX,}W$IdAB:Bu}VXggk{{dɂ{N"{H{tWXGKٳ_Mf@DMoii,扇qL/'򮻞}~*/Ϸٱis8^"ˋM%I^rAh:|>(.Ę Hg?(11 :o.,l`Hk  2<_籉SqqqƍߵkGVVso>;Z7I|`joWD|k}zoH:$pÇ4´@*9@oEP#F(B.nk?Y+$>AA$Ғ{,-[)>]> b}Q6n\vӓ&͋Lζ[?ݺ3Qp7".>[ G ~y%8eΨ)w~ͅ";Iporob ܇>䋤`)—D<ѕ5yoοG}3)iH$immkkK7liŊ?OH EәM&c[F{4wuQQRiF7zÆe >omAAB:8i.4X 5k<9m"g}lkkS^)l6;hoW}?RxxXGj[WW.68d?D*q -ҷ *|淝mwo?ɓ{7nuYRѨgX&w>[fնN5h4wtSu'y̆ 7\ȤIsetf?/sd`;G}N]9۫! >}[2dF"#"\7!5|e.ZHZڢ3}+-MIHPt5})(2͵ר)k O/E>^[ V~G>$IBe<߾m{?1Yu -C׮ǫsu_橧VdAzޒh)sQ(bCB$?^Ru(uJee` *UG/LnRYgSW}}M7Wvݺy1!enlin\=xpd 46־_cb%lljOhw?xDpjUmmyCCf@c7Y\Ш,vi=LwoZ(vۣyo ]IIBl[|bc6٥^XOQc8AAqBsB.RuTžŷ mT %%X,6uWT4W}_*+,rcctO#r6݁F>GA| #=ժ]k+`.Fc٬+x¾g0μÛJ{>2 ԅ~ KCpD"P (uG#/=+Ӣ"=^up8,E@ˑ^b%?31C)g2onmmD 1yn47[GXEmmymmzmiUuuuuή)ڔwd,, : z: ٳ)O hpaѢ;c6PrC:67M EK"617V+2O?Ν;ڻWj=L$IHz€;XA4 # ÀkB7Luww_Eβ) /a׿7mdjf 23 d΂ >gOdB&`?y6ȏ*@׬{٪3MXW(RRFukl(_`tDXX…?yc(G*M&SPPpFF=-/AAă045(2)e7^ti_EQ.,%^ֹ6RzpF WI%IoͭV*Ul稫Zum_9  I(fTSg28&#BѠl* ~"MF/ ̍b1W;:[*=Y3{EEC;@'s  bbR|rO>g@jx΍l6Cg vA>WW8{bcc?U΃w͜?e)){@jZFAA04}./IW¨}>o߾] mP]5i˯eZZZzlpf`9ƍkj*^jl_222t+%@A+d2Qx b~~~2zE:~I݁>xpkkP(y99}wߊ+Ν;XG,""::˗8qڑ#Gז V}HTGGKEE9C>RZ  `AA gIWuWI3x把zѡS8VBMMFAA<oJHWVt="AKJJ i,..2MW@Pę=~"ҥsj,e[ ʊURK(ZR,ZRljuY,i0(WT_k])I@՜8q1''4 Ƿ3Je'3>a`4Z C+$   L65iRb HI 7L&"SS#pbRGEAA qqDizI]PjzD@!IFb1?-Ma44=ЇyY $)"Ic,ry--F$ |~\9HRGA8IޒC h0l@دhPłaI6DGAAAHc ưXXݖ "IB$Yt<tz"$ICofsv¾lR,>w'' 20  2=*&:'49}'L)GxWi}ܛL2̴>ZMeP>,0?e}9˙w-gcyGEer7 <^}j6a & hP~`ã.qpږ7ٷ"U 7)=%}A>arM>aDGa2sަE0Oc0s÷Lx/Ą2 \1?}H٢Xϐi|8!s]/k`Μ9|y_ q1학1}nNȴu۞xEQkm M@h`2~G+03fHAA̍>l]q9άAi4>}wNh gggk'NjZZڼy***nʼ`̐Bf(؀ ?_yъ! ŌEAks#)[}W|3+A!8b+3!*0sE{m 4B$I^[n9z}wvv.]t֬YׯolldLBf(؀ ?_yъ!/>%  V*a>sY! ^i|'L  O8qժU*|www+wM`<6Р 4Cd; ?V a(/" ȵ-+s>Ǚ 1학|´ pHH_򗒒7k:m4H駟vww@^^ӧǏ/ڼԀ3 ~4 M@h [_5;a*rtAfA NJ}ڀ w~PsY! ^ Y/Tᅙ/kS޾}-IillReմ4p%ŋ&LHII9~@*`&PfZYiЪ_" &H!du4|B&? Waxyxb! Mqh>sY! ^i|'LЊBQTSS%>E844ԃ!TwEmsEHtHio鴥×Lj"F}ŽEƉWC_$gNi# eӏ Ühzṧ:Ϲs.psfaZwV 71L:hFaM?uHOقFM||g&A,]͹TUBЭ*7uPŇ>Fe҆F-:ݯ+$\hj xr_ NP]F+0<<P/T$rS/]F}#6XP(<0q*XlUq(g3%Yna+|{N>c$qlF%fm^#ˆ׸JcLfK~"N{;0<޿ $驁$A֫j[T#Y2 }|89WX50$IBwf.WZ܁U7hV, u;=\5P|q A>>I nB|3+A!8b+3i}ߝZ ,[lԩtHfO8Z  掌;5K՝Ny3~ăi"X|7Y+l٠k<Ѥ+cݩzd2Em]fnTpx ^VHF]oI;o㋤~DHVׇ\VzT.rd{2:! pY$ѥ5V(J|dF_?h2״jjT|h`U{׵Y=ty!YAV07bEw}8BA#2^ 37_צ@ܐp^^.\ZXXx␐>l=?x9 DŽWo5[q7\hE;D8_`ǃiYs>I nB|3+A!8b+3i}ߝZ 믿q;L&?}km!NJq2PQ٬fɣ䂇?;殡5w1yWHҜ~Ńi"4G_׸~d嘛~zϫ. ?0*|pr˿hL# 耖dnh`?N <}ƷL>( 2pY/Nt q̑ƇsXʁ)Mk3#3k[5I&R'*PQ{=Ͻbo JŪ,=/>%  V*a>sY! ^ Y/T/n0 LEjGGSO=_]]Τccp:]%?҉^u8ýe7Vz:e4P⬌*dkc->I 9JRLm(=)/hڮ]Qݼˆ,^a[Qi_69~r_B/}@xrNDMyl Y(ՆOԤ5 `Lq yBH^q_AgJ \77^ p͙Za^nQz X[>A0eSݭ#xO'k?=P<]$pKmx]I`ɑv) %W (_ifda͇ƱH"53&OAAϝg 9 AqLs>aZwV `Vh|xVux֒S۹Ec ρ pJ K(=w('8I@MmuE5fڂG$C<$܍-{&̻͹}3am |v6"j̾GN_gK>{?nr4G)`d&/JeoZgJU{cbesF(jbJdӻ_{hn\B j,gsdnhӺ1؈LM@QjӎjSuz=BvjA@Rŵ-*#PMիq9KYž(_ALtvF6T&+RfU4\>{$"^R8߁aBi}Tus(yݨVVF$b9.6nʣ\ftz+Ejtۼ,[jXۿW  AaW SV:E3G@QTi3.ݎs 7gEDɅIa₺U<0gRћ~;WO0{+RBq$׏ uz]o׹;a^-ş6{ (7MP4t~rV٩s-Ue6-o*iutIumU)|j@_yieFZԒ&5"ؾNAggDINO5uojjdZ4֜&߰8ٹ<9ҥ((ON<6щi\麷~-r(,+""@Ю6|q"VMZ19~Ą!oŞiPC ?;NF:y6"VMͭ*~Y7Z!.S Y2q῞W֭5҉F& P~smF&<0 .Ta(feaHAA̍Δߺ; O>sul)bS}}W Lx@>wFA߅ѯf)OQfgm]d>XЁ{?LwBĭZ!a\6STsF|}dC`2)$$᝵PFJ(s ~@x%دC~FMȉ%B,wY+ Oi w=?IX0k$۽ $ѣܶt(gFL8"O(tm[D|9%5}\GQw4[˛zB½-|k} Xa(%ؓ׳ugzmXr oZ|V#^;mBe|c$P£DaņΞM7At<ݥ}Õǒ.߃Y0';Rڳ{N/jgV,ADWhFEpfq翗{3sڥץIxWt>89-Bܩ{">Ȏ.=+ /jOY$IdDI :m3+VcwvsYoݑ$_93;#~q}h A1(?q^}`W[U>7 .L/dn\CyZ]2s*a;l>^aIJ\>JrHLH,0@N8R$O6IdצްzJg͙z_׼[u{QK=ۆy oh2S,ӸǞ32v]hFo:p欈 Q)U:!aorʪ[6G@n^}3eL`tT4uo>Y3E۩1`o[}8BA#2W1L & z.9\dbLlL>CZH|N j&:kTD;F;I:/v\?twupD1}x? T$޽g>0A/y GGl8eJC!ngnU,p$@%պFfu9|Wz{G f@ah2FHIOސK Z" 6W<ti 6@K+zd˥ceŽwv8^ߘ=ꞥSdRuFgv~9n2 0A9e->sY! ^i|nNȴN8OBytw7+l^"Fg uo`c>+8#z)riρխvr"OZ&VQ~|ՃžI>*۬Jzg.Y!Q:%Y֙~>Uc_č |oa $ *ʯ,+&]x'Do.8EAA9emgV CpĴWfB֝*윐i}ߝpZ!axx:E)ћJTY%, (??yRi R1vK8gxʼKJX(*;xV1ubG,*a'WE~89gڇ _Dv gL^8mC[76  ?A&E,MWE=5WLG g g+5iB%Wدu[;>HI/5!1zSic`8W^ٻ:pECF펖zPֺ?8_J8Ooٳr[ktUKziٺzia2qoV@clyظa;d9IZLi_%jjt?]<N[k:7,MMLsZ12tsamՌȞk8'7G*ۯ7>:F+Z^'A!? i#m2_糚{ .x `֬Y[n42_(#Ҿ3YA]14S{Y|޶qf 8GL{e&db( 5QVg {/֛ꋶM H* J28{CnP_n6mުHpXMsw9u82CY]fc^]$pNo' O:]\U鋞9  ^ "-Wuێ/q=wb K(5#%ͩctu]ZϞe.잞D8]H6)n'{IOO U~`y3:%IvEzu"xwn)?<hЩ9k6@qY+ Fk[&'YXٷG+-9cFFV 8ZOw(~fմ==Xl[1*jm,k4wj-+F:_)jV ׵jV"Mx8l}7MzXxdQ\\~y幻Wްilieج? 2"t*mI7g L<8oH]C!PhdƏ0~LUU5+mʭ]cEeudWgh6Q-*|8ɡ~PҠ*W\n6S$I ˱Yl-*ݿw9^‡PhVB;\&D:Ն6ML06>C}tJmP%<1m|4_Ю 8!}%`ņ^jފH"H;نvMx_e [ `D?\hDGs/TY҃+u^H! zF$)\mŇ1z\jlL7'|S`\%CN"͛>="ѳklt |[Aj07b~$-dFcl<#cZz[ެ L|T|;{b16 w-;ߟ\>;!{[(3E46 iFLߝz{1W!a3# Sf T*=$6?nLюGM㋤K 縣,8{$z.|H_Q.N8;YLN!(=azW}ܡ>~t_=WDOt/`oA8X\u_._0V!/MlTڬ!܊ +1[l+f zmj˥PE.H&ͽV1|`%N`r0p-k)":C+#R=E}ʺ:Q/5;&6`tlYEnF7*PΒDQ?~x^҂% \6٩1+YF3֥) ,MƅCF36X,pBE%>.W~`n"úXپ/vjJ'D" Jenz=:% rka#Ǝf6d3fxwӿB'(sëR:u\THUU ƆAac#=xb٭zf;,<S쩝 e5H6WҊҒs^xf-׿N|y[jE܇]p3ׁ-`!oDO~mk<-vv4$^{FA|1r9oܑw2P}G sD)Q[r>\Nzc;^bz`nj9eDmi"_U+j'euz+šmuH4N༶|{ T3SuﳄSҏ/̔7%;թ4Ȭ$s""ؒSuv1󏣊Q ]h\D^u;ʈW ZBQPTRgմoT,O~ʦ]ƍܵG.ˊjV}})W]BC~q +=g(`# R>Sr,ul=jc2e-zKd' [e~Zj&YgCy֭~yǷQYu[{v\b'No$ ZBB -98QPC B*q{efl4b?wggG;tm#l>Rc"㵗NM?6`IUA 煩~>ZK ՟965*/o5*9åk秆ˏ 'w$Lωz@lFi& 84:\ SN"ō @Rr`4øQty~[]LLLLLL4hqܪGx %ısF*Pudgѭ'L99oawް}ڒݑ3ƨ BFV}|?.LLLLL􊵘꘻ź)*_FCsdַ}.BHꘚ}_T>=vCu1g{=kEMY5J2놊=?9OeDy%FĦgtwiBMc m\GgdL./4JL6=]q/kCVdw/9 mUSߝrt~qYڬ{mKW\x5לW7w̼R!M<Ѯ0/'%*ɉz_SbOX|iBFIIN_X811+>i)ONoH`J!#;ܢ7>]3A$ֵUU2SŞ{٭zSKQZUFaKD#ZfPpSߗͅӲÃl3JjC !!rT<ԿK8- 6b6ktsI@q\ `oU5 X|L͓OV)d3J4B\= @[s#udDpNe4 +N !/ׄEE tDh4D"Mfkotzp\ᩣ"Pi#lGÓBbPh+Cψv\8 DX9`@|.J )h\KN"pLhY_Z!v~"(8:p_'ιu( @"UDO_gɩ,0[>y' hp׍^t#}Sm PgLSWߙǾ_}Cjw ͇k^7{Ĝ8Rf¡5?t>׋{ݛO Vwr޽s:h2#$I %-|Eޫ07兩:k4OΊ2[.zl>';*9L5l~{xa Y65ꌇ4XƯ7!k`,=ws4OVuxn~|RdpZaYkSmKק~yv-VNTմ-2auEsFǥhzixKI}cǤF8|aشp-$|ĜΏ];^/ZVƤh %ZrΈT`%l7\Md۫`ʕ$31111ȵ2Sޘ< ~ >ofblRTf#Bԗ̛4s|vLD\&il R)(ʦ a1A!QZS}GݙMe@|)2Ѷ}5f D;8,h4m&&&&&zŹ{yOHkDb]TʇCuOrJ\ٷs$eY>qބț4T=ꖲ|xD'gJ4`rgGM[%{ލ?%w< '&_w=229x.|ѿu,;!$:T%JZU].=7fkuŵ>4 ftRʥqaHJ7X;&+>\3 y~NwoGDV4+:{7zoV7w֮eVe2[{:M}qOhA2U0k[ Ci˧_XwlD64X 2״t5w{ӴxOki1`B&0XUzw!FRnwsܾWh>:n|zdIaj*Զto' R\}[gxK)pK ӨdM:ɪ6TrGw < N嵍r2pRs)*X-&(&&&&&&zE^7vpii6m!.2dͳGj: S%U9"T*j1:+Zd@o&vG E^A"swKxÆ [-ZtV|Hl=+(B[[W[z*EM7u~XД8mթU#Y2GK%6\ooZcļ9KJJ;ۗow,\#[%9G:y:fÄ{d兓'+xn4%u]&FTL5bUP?p:ju.^(3-U!͖-&k0tвiQΟW-u) +玈(_:praˠ .=G \>?Tnfff9:H9mL&;v)T^ l, S\)RrT"ʺiv3AntVDC|{ڪ]g O&$$ouxCWHer4Mm m/LLLLL􊵘Ƙ6|K[ʋ"L7=;ܘl *N3+f⹃`(\5®֖pԜ !Ne6ߵQ&Bs)y!n%i&C%M]FK,:2)J0JlTȍ-_>=Mc!L\qd)"WLO ',l`9&?^TqbtV/x ;'}?B&ؽX$ئ#r- 'J*;C{}hH-׿a6%2Epx$Ro_V4DzBѶπ bTf>$.#}BP=&Cs?'4ѶPm,f h FQZ BJ@Jps{ӯ;zӒs/Z4؞%o|Pt@BlN"Yۦ7Dȵ|Ϳ`JvlRRE|zlAkC-]yO_pvmӿnx 4DzB O(ڂSm&&&&&zZL/C{OHcEb]t*V"0a5/LLW(0gnxoixDuLU]MU{C٠% ÔudὟAebbbbbNZLۿZuglƏ=p93- k@"f4:;ԹރL gOim=h~v_D^A"snX0 [d(<W(0cDq):,u҅`1rJ%S97fݶbӶڋg]xvaYuCsĘ)++ս\bЃdrLHeyeLDT&csL,> QТ y{ɽGQm&&&&&zZD,ힰ61u 311111111$<L?LLLLL&r-hj6Npzdɇ:+8T@S m^qݓH(,ΊɥXL&- }/?'X0 DPy7L'VQ`90JRRS`?@91!E>b".49LO# bdAoٳ~˞;MMe!1Nb1t5HuNhDȤȓM$m LO#4F2i>ΉއSBdbz1'wOh'' M>̦GUW]=rȑCS8uPFcǎ!8sLoB1a?#]]]AAA!`bbbbbbb:[jin r{SNK?g˞&q'U[C7{`&7mw&vDXNXJs"ȿ&ްaÖE'~Om Cc8E`xbJF*m'~\Q6)YMLEh$1sq>9Ģm1!]O`pgM4Ƅp1ɤIp14F4hDcLH݇VlIv{W0 ̃M̃M)8 8F)XVkf@h 89>Q,&Is1ɤ4Ii$3bIi$H&ͧLOs_=A&ͧLϜIvu 1*=CS+qqR&,"m''D42i>s.&4F2i>d|\L2i>d|ɤ4Iu' Ȥ4Is1N.a&̃M̃M)8 8F)Xd|ɤ̹d|ɤ4Ii$WO@Ii$3bĺT<4"0616W(0gQb7g)}D42i>s.&4F2i>d|\L2i>d|ɤ4Iu' Ȥ4Is1Nb]”*Cy1C2eMu<ܺ|ܽCpDnc4NW 聉GbJF~,t}JWb1LϜI&ͧLO#49LO#4F2i>d|z 2i>d|\LX0 .E6$ \p72qy p([0pߗO gqzoe۴qrÿU/ IDAT=01H^s`F )Jߜ_Ȥ̹d|ɤ4Is1ɤ4Ii$H&ͧ9د H&g$;u өxh<"PzudZUXIZ/ i R?*Y%="S{0.3yf .,`X÷|-CG/h\.O"l.[rM_GmۧfasxbJF,%,(D<Ȥ̹d|ɤ4Is1ɤ4Ii$H&ͧ9د H&gA<+%L౱WERj⃧.J?-9ewg5sc|A'ڄcHh%:qq*yڶǟm7H[E`?Mh*,,g.444z:V]Sۛsv721 S^s`&SND4P,&Is1ɤ4Ii$3bIi$H&ͧLOs_=A&ͧLϜIvSы"~4F2i>s>Y.aJ(Š eLRd,`8Qކæ%9v&Z*N/;cj4p*yHȪCF$5}讓o(mpF4n͎tdfR+k49?DV_TI\l OƝv6рub"49LO#4F2i>s.&4F2i>d|ɤ:d|ɤ̹d'.aJϾ=ae/D'g 3!_im7ˊ[2r/rwi2y\{Y +h0}0p*yHRp.aw,ˬwEx :s\ Z:Ⴍ2٤wg,+Ԋ.+4&"~Z$iٺ$"WP9N/J*6qt2%Jb1LϜI&ͧLO#49LO#4F2i>d|z 2i>d|\LX0Q05ko1!9l"d|z 2i>d|\LX03734\"_F:ĿU[zr WW䙸"liN-~h͞tdϋE eF-y"»`"G?z'eש?x<{s4?rwgq*E?(b"49LO#4F2i>s.&4F2i>d|ɤ:d|ɤ̹d'W ,Z8 l O=?~um`KWńwYHM T~$wKo~?/rȄc.r|3d_Q+JtTV\=,D/'#RǼH^.]ŸWt u` `uXpk$By\2jMւv@ZN#C%Bt~ZpxKY7 fp!\qVB8/r>:B?KgSLhd|\L2i>d|ɤ̹d|ɤ4Ii$WO@Ii$3bDCM& cbmª¦:]h_T G󸿌H8qv~{p>Ҩar\`ڛN⫏㤉cRF wRUO[}ĺ0Vc´Wx*{~X[ 0wi'xti8':='ce<'u#I V9gs.&4F2i>d|ɤ:d|ɤ̹d'W0LTU]U`*nOz|qx*hBѾ =2(m5vu4[[Xɝ2}PeaiW ^-ZJiH>\)mhFLҴӕS&WxTB[y.k (XϘY; kon:,4D)Aޜ5yu|"R;2;B(?s7s όxoj/g{F )Jߜ_Ȥ̹d|ɤ4Is1ɤ4Ii$H&ͧ9د H&g$;ɿՐ:dT'p ܳKž&״R=nnoj0rWό^S;5Ӓ]&9%㗧Vg{L .GC?>Vg] G'g[xA}hьOrKE'eТ}(etgӥ ie::\Hoݑ^|]8?-/<2pq0kRg >.ŸHi`xaWjF3xpÂ?֡SPH͈թ-xH8Ђ:#MJl-we2%P4 ܟ=Wt=w5D*pѝ7ЋcjǓǺc"5¶D)`Nwj2S3LMļO+/*Qh˒зAOv5HW{' V#py~} N8Ђ_* 'tpY#dSV3v7c\ܔ΀__qF%Z<8;,b†aKj h1 Jn\Vp.u )J&QD42i>s.&4F2i>d|\L2i>d|ɤ4Iu' Ȥ4Is1Nb]”j(Ͼ2o`Ӡv4vwf4IMBb.z)"RGLT+]grgbnpHވ1t%>v:DL.z)B9T{H`{ =$JU AGz:&Hiߛ)o=.3ۘHnt{礈;' :pJxg<őWj |Uy16#|m>,uiš)..19%4 M'@+0{ "!{%9Ӽ<}LBxk^W Q؁h%Շo'`w5ЙN>O:IKEh:g1LϜI&ͧLO#49LO#4F2i>d|z 2i>d|\LXK N8rRQ-&𡮗:zE??7k]֠恆: $"u $ :?x=EȷǝΉҹMBkpib]%zmLZGCq.1ExP(U0 pl VqL8T¸ձpLR=)kJqahUޝwJ VDy+E[($rZ9 rӜi0)nG.Jv5 PoDo?8X.24Q=6ʜf@n2 2p&p.X2=^9-v1nJ$ \hM5,&j,AM0+w 6JX\Q,I{ 7E#3bIi$H&g$H&ͧLO#4^Wx|ɤ4Is1Nb]t'(U2 8Y y\op5n1q.DzVwc.aIH|s{nb$ĵAI)wDD~gEէ.S6\C$ˮI :L4_@[kG`N ;JI74'tuP|!=o*윋t5zd]3"8ߡƀ˄ɮJQ^mdE:($!8 +]`MEvnm W?2ċcl㉻w<:~SD.Fa`’ܙNӰ/cG!hmhGOs.j!8Kr`@RVfUs{B>$\wEV;Iᎇs.&4F2i>d|ɤ: zDH&ͧLϜIvR4B.v7Y#;qfM'{`Vp(C"ګ1Nz'gn[]x 7眝KZ9&cM)6Tلp8ĩz Vpxe߆N|]`o34HSeXW qB窛?<)ɾ-"Sr=exN30*#p"+֔**|YQ!Xt;ʴ>W!?ٿo+ٸe? ڱ`tYQ܁5  us35(ijGhCt'ubl;"FXR>J 76+\d Z^bl"m_\ S'eښI|w92WBcB4D̹d|ɤ4Is1ɤ4Ii$H&ͧ9݇ɤ4Is1 .ٳg ݻw;l;wid2ѣG7l`6-X 44t_U $ܽEpjr°l8&\O>,MSxl݄0˽z,o]&\֧ar:L,jDF9{z`fWGCw|kMΧK-y$ 7cM)<>.=3nFA;ܐwȖzL#gn "h6asl7\U谠 ]V,3li9)./J .s$>1zޮjE?N윛F[88HwjIS6.eh5ǚR|06>MStfs=\o}c5_%\.DoY#gEf87qoC,K]U]x <= B΋\=;oI;Or4c'ϟ-zOss.&Y%,HڥKܹӡKZ`AuuuWWM74ubΞ=tUUUK,7o3zhm88:O+a;шZo9hAq* EqaDnZ̨~{Lh4@^HT`G#x.o0p[F Fj~/W9+|AMJvmdhZ>aw|sV +Tx_o"m֬9ґ4(*VNL9xXagg69?K·pqdhw'A9' ]fnXۇ!@\E9Xي0X]D>T=ܳ9.xM\&I0/.K9EE3ts%ܛ]ʠc>rG^֮!$u3{=VXmvww#üE0-Fw-s6;+'[TÇCKQ%i⅞δtX`-ۊM2|lǏz]?<,Ǟ?F]BvuJdtpnM2$b2=.$9*4pեh3^4ggY,VTjO1#J%yMfSgVK6`Sob VMmReIyj@-C~(h1u`|LgQޜ!;{*ލQWRJq]L{ gܟ -XS=w75 P0'Ɠ%o.Dž}42Kx{#I1;?Le#G$mZ]m=ѿ$EEE< / .Gٺ֭[t .%|{O8q'OB_7uxyyדB =TϦՓaj\sN]﫦?O"BAЃppJb<%zCi=ݶe8-g[v _]6lwѢE[VZU\\,:> //j=vԩS5MGGG^^Ç?rȔ)SFk׮ᖄ|#6#/ %l{ =*yh/;W]sgt&;Z}fn|0C4Lzu[\_]IP7/<`1K Þ26UC}2:`hHKe!}NmcЯ{b S&O(/a oXm 0{ G|`׳42H;%G_%pacm `~bb PnpW S1=0Y'esMJs9?[op^z}/7A+F^?_/9*{D"?_/9N.Iޱ:iIάٿ:9gOZz.cۿ=!Am!}p}xвGп  4A#̆pa6LYdǢ;K'V:o>=ugI?5^~[sɞ l\6%4ތ~ C('3$ /Jbzn٥/Ez?d矍=U}^6g^%<.ɢ;fѶNp\\\QQQll {I|[=iA}x n@c3z% ׃sZÄ*uȚX0 ߠux~+ja<y< ɡm^^1~>:,~QIqדZk@ C kpI{̎v5qvpG c`"Bn"w wZq!G]5|rkC f`GӊQ©Bm7+w\W秄sXv }5ڰsx*D$KW=ZCk7ݽmk!zͫ.ZvԿ|@E@7Tڀy0M>Mlb.+)Ptdc+Om9]kW0v|e밷=* I1!m|\L2i>d|ɤ̹d|ɤ4Ii$,&A&ͧLϜIvu ;J%ۤнRuuuُE '<=wE9X [[>dv*VV1WX==WhdUa\4)v\mrϱcz${ҙ"gxvf1U>xuW+H®&Cxz={L)EvᕱXW3-N1)'!n*#m`% JqC*;V3< KJo\t !mfaS=/Euܞ ^'vnS"#j§pr8Ђ5ӄ)PщARhđĊ67K.{NXF${Dh8=',O?s>j#~vsTܹskg܌ZD. k )C#s ȕ| ݯV*wxvn.>;==SC긶c+]azh:L NN4$'"C>d|ɤ4Is1ɤ4Ii$H&ͧ9݇ɤ4Is1.aj E.rbPvQޥ)pM&㚏R Xr)/YMTC=ٴ=v7Н}]|2lC; jixaLV ~i @r01@g[2 %G!2PIiAq[.9fGck/jlӯkȗ@aՊ2n[x `dd|ɤ4Is1ɤ4Ii$H&ͧ98(F2i>d|\LKh4FB@[[LRi4|R=; 녨7v?O«0<J~_<4T]Tf݀&LCwđloY_Oʝl΃`՛bd~BlqԠ \:ebǰ+ <أxoވ^RCG-&f RLM?p>mFA~Qzp9# 렐`rkZ!`x(>3vhī03Z6Mį |Uv NvŪ̎Tė(@ƾd3pQ;3Z͘ѡ.@Kv s"17$ )>y!a5SQN;pa Ux6).߸X0V\y͆ KL=ɧYxʒry0iudF!fY_ 2ܴjOPf5#Bׄ)$82WϻBPt5+a4*U(ﰦ݊ l8u1aoϧLO#4F2i>s.&4F2i>d|ɤ:%d|ɤ̹dgl eeeiiirlm4mkM4)++wl{dǿ_G{I's*{jFR&G@&wϐG_كuX ߮^3W6+%WO*%S(XLƆʂ_ct^@Z<sG }0Xv\^7ESFx-h5#Z PJ\"@6) ĩRҝ8F'ˊNqB_`lj P+y>MTҀ,-2}{7 "T718ՎVʑPy?l+%NpL&#BHS#Fٗ8B'sd.8Վ&#GL;6>VQ$jDl-apb֦O#4F2i>d|\L2i>d|ɤ4Iu H&g$;+`ݻ7;;{ʔ);w믿&޳g_>sL[L&:ujkkӧϲo/$[b}3˟KSλJ(9 z]syヽЫe յx涐]iho 2隋{%9X:sxwnWz:}Ƶy/ǑZB.= Mj΍vdFM"K8T~Q53aVUj>0!JoѸE(@qNio/˜`g !Nވ, n )i 1Szn]]eR/6y{m )7!J 8&@Z N[----xH$hf@PL2d!YKbǜQ[}xzs{r]V&kQlhpb&"O#4F2i>d|\L2i>d|ɤ4Iu6qt@Ii$3b]~ŋNBٹrJ'8qСC]tQsssAAŋccc~m?~ _y4ӮRNv@_zH"+g ~`=J-êUͧKRW Zܡ|MBÁRz2f!n4F!'AAhz= p8ꎣ|Tbz ]LLbʃ,A-f|Y+4Rw  %zU ˇlaS'SPX^zkyYipdlv0-V!pG;}F Yz9I\mB%l/Ѯ:q`0v,wmUg{%ގ؎콝PSJmiBQ(4-hK!ك,ww5uYd֕uis}{s$Lv)&@=W|L#J$[7 n\‹ny-21soFf:>#3{32lt|6Ff:>{3g127#Kjw?Z͛+l޼衇b|嗻wtGfY*{zI`\UtUgwHږ%[;$I!I8?S\%g(l@@"iȃbM7hzAb+!%GU#wc7qyBqҐ h{|1<0ŷj驫HH7m8bya u6TD%' 2!ާA8` FG$mG<8lYtݝ IΩPAB'2LVp N]_(#c8]gݘνLgcdcތt|6Ff:>#3w Ed121soFO-˚5k_~KLLrhoo~hBQ__|>UKU-f +; @%&u10% : @k@ $\q@1k-ӗ_}JY`ZۺGRe񠬔F+E )uBVϭjJ'$W.X) zB\G?yT zW3^U?<ǐ/o5Z01JX,_ڿ~3,?Z~NLX'Tɖ-t nyFvk5׷>+/S z*B؋Y3/^xx+^X)讽nPO<`Uk߰Zdcތt|6Ff:>#33fd12lt|fK/"3{3p~$bq}{z~!rudelרS{0뎼u֬Y$:Rc\d`2209#33fd127#3Lgcd7s1Lgcdcތ/ts.\"4N8??z\_Ǣcef×U:|SZ}t=8)oڣw ^07ڙAl@֫'vatl|/Ҷu6>ԙMy4ã#`Jl.0BA]Wr$ѿ6N2Px-Ef:>fLgcd121soFf:>#3LgoLgcdcތ<. | ]hr@w8G/|;ᙦpSbܵņ:"/Vr7 cnG)eR^aWY>`B@Z( .+%3! ~mR)^ /L̽lt|6Ff:>fLgcd12;cLgcdcތ<. |j ]hr@w8G/|;߸MW)>!6X V> bD$%mhW9R}"]5S]+r>MʸOMiLTj(!Hm1=['Y_"33fd127#3Lgcd7s]~lt|ܛ%ݻwەYfB2q^s| k9 ;c̃#'FYdp.b.unB Y7&sgcd127#3Lgcd7s]~lt|ܛ%a&?90/+q^2DhVd:~aA>09b.\|#3ӣnzG!B!ÿB poF f|66846 ;c3?"=ܛv^pÍ9|!p^y‡Q̌0CaB!B!\f'?90/+q^0!f(,B!B!BK¬=B0@y1 B!B!Э Y>|.`4́ 9 ;c>),B!B!BKB0PuKWcCvbQ6Ŕ*[LF^HrߌB;c>bf;/CaB!B! \f)?q]]{_m֫+2* 9^ˁ9PU9X%y:8#/AwX>ab.8 ;c> /B!B!nU$X₶&4v/i1+O['TNr|FŇ4IsWd&E %e )gNɴb+b_=Ç:%yq <,tm;~| j>9+z BB!B!- Y>|t ]R,\NvX|竁y9Ƥ빹SYč!k̵4wt\>ݝl>^7ׯW65пX0$|B~{{`%Ҧj]kp^y‡ὄB!B!Э >4v g> T+X梚*&E2ig74|}a_Qr@]W%& TZu~_򘬠<@2.ss+S맫JE3ב<k]Kze9jXNpEy +q^0!f(,B!B!B[K»w+Yf̈́dPw nLf@DE0H]q̕~˳;j1oh/?MEY,/˚=Su^5f,%tXͬW݉99ߊD5YF3T98<+ GkNsvXP/HXDDYFX%B!B!nUD'Ɔbc.|k_4uN=:|@R}ƥ _WVapYyECoYsc0@ُG8$gVJ4ccGfKXW_^-5lڜ5g%j fġL=B!B!mKC8$2r8%-VbP4lTIc Npmљfms4z6FĻt梻"+x/l$Vsp/>uG[sP)|笺O(9}O|J(&7 $A! [/"ЀwW1r9}kГYLJerIן_X=bpP8lN.FT2>$4v]Mc bfJ1bn\Zg`A B*n\ }Wף=ܾbf;/CaB!B! ZFnž=[\$=yUVނͻ48BoMAXPۊL\{:? >{ 9 j=׃iA9a u wI!?g_ rZIxR#q c9L`J-%=+w>gqbW?Eľ–?b\ɉ8] RxO@I[g+\T-[4-A!s`$ʮќz1G+L ]ӛr ӮGxS+o #o؜8~{[X[E1b^}~FxPxhB!B!t Gߊ{D=6^D|΋w c<-*90eɘx"+&j=9)22Z]ynll!g3|}KUck;s aGsa赢åH Z:Z'uv :cR!Y+2C$a0[ڴ\ċ {6a"xksI(ybU7,L C">gtWlxݴJwfŘB!B!B#],5TsDt^bd#M Nwbs]uWw6Aa1ErÚlrμʁf[+.:w8j G0ⲇ}O/횓8'< pKG77\>>sX@ 55u#Ei쯰:qz0tk:n5R4so5;Ն3a7p(N욻wNTt>jBE.'ݞA+΁5҅3_wJ|1`s6<{*/C*]ldb覿x/aB!B!pIqL̪yIuHhS~@WЉ`M;(4dmU%g$"gPVK}wSwr毎IuX?$jRձ}KY팕9~d3+*6$w jYvW>jӬ8yE+&# a%찹ᅇZbn(g(HiubypE }C#o&߭FǸ"lf K f*lL;.4=Mޯ6+[в&'|MN֌xh/$MO 4[50zB!B!<Yil=͹ J}E-gN4,8ruyۙO׿`| C CY-5dJΔG-zlD DD;ZVE'wkZE>]E]_Zw.JH5ҒAѰ/uYrfT뚯kDZz'+-Sh[x$#䒣%E*[mOOK^wO~s0*+lzR%,TPRw/sMH Jx2h6t\n?S>dhܩf[!2P:;,6X$`.oolڝ(MPD"B1c0[4rI"+N! }e۬79(6X,p4 {[LKn2!wuNE7Zʚ4 [hIܜxer42P( yB.Tv}[j tqmnxRԧ3m=]o!!D,38S߭cG{4oAR~4NFqHi[ՙ>1r޹'Ko[$`Qzp;uǯv67ܥ"U,'IZU+TM N덖°')t@FE.Ūpj]#kr¥Bnjsmp ?%Z>%Z$VJxA>|qIɼD\c]LȝB^TѢ9\VfCx/aB!B!t%aˇ&_]jմq__=]֡3ZԢ6$fêΖ'=6{PxX8иT]G$E^4\ ,!]Q+qYxJ7+c漴9gRS?G_QXfp0eNsh]iK/L k2BCW7Xx߼Gs%9 s7*?񠩱߮O md&Wuuj p_`{Jjlyf聢?|sQڌ, F7dOcz7F(E[a%3&'T!ӃlH-H IaRCsS?~mJn6D93rۙ´֦ tݳ~ߢnFS{ C}e77Zk0xj|!UIwΈP }MiXGoFrF@uEm[;;^MO%2|悢B{uCFZ|$9\:L2!7#&@.ڍ +(yUUӢ*ÞSXxO.l]ibEA/\r۳3yҶ05R8]bӟ; t׬@Qw5a΋M-F54@I԰s׻n1"Le^Ѭt}YSc B%. ?]tgŃ/W{F!8\ 3'IjCzmPT0b榿 E!B!BȃpI<1&l֫U;[ӝ5- l/sxH* fϬ8dTR]`{4"uZD괷n.e4aUL|Z;L}a{9P )m}jeYzbYfj7gJ%@Aul)6ݸނbm}o1X\d>Tץf~>4?9KNu|̬ EfeȢ8bUCQӮyu٢ 4!7b㥪'oSå3 [3bK7ϩǫOomŪ+2C?=YkۖdZ)$$̻17mLyǖgf$#VK%f(!燲bUbM\=umk.2Bj{|.aB!B!t%aVQb&|X (ncZҊ{:+;Yi+R??`ZH'pzZYad"Z +2|EWz`Sm9|.IQ`PoCY!9?S#QrxpzFmgE٬ݎZf"3V1I]٢I CNJ`aKf"@ wR(jɌU(%|GqN w7~xjp=|κioʷ_kތHoWFI _FLi~YINSgw+_W ^9i)n, 3buv8Դi-Rv'޿ʤYa$I,HU-HUմi8[y%^٪ElЩ6V( {LױvGF*EHK3jjJz6opP%O_<剱*d}7~7u;幟;)J%;z5u-/吙"jMŅB!B!%ݻwەYfB2uY0d( Jujuc꺚daI#chzyX9;) ZMSFCIFьgP䤠Go<%L OuX Wr}C!,-՜ݗWf)!ߜo Ӣb؛4xJv*Z : U]}z\ˉW JW|,d[S=p'FN]]}HYL0$ ,ߡ6$It'Go W׭y Ok86BeTUNq6 Z璡r]~ymn\ˊU\X/gЩkw|Qp3,g;6o <ᬰm¾9GiEKD\E'`W>;Ynz䊩aR!7.DWfg`aˣK%0x)pЖé=R"dOIP$(WyCj{쎊ʟY5$#7v, >*~kiIUyߋvX}vFOߡnź/. #B!B![KMGN4Kzq8Br|럿M-pxTXC~'H,ggS&JЃ`;uRCA~3_hK(Z15bg&kuV5hm_|\^X8z ʔRC@[o_Ӯysoٻ/}xa|RY~No,Isi} 楄a7v];]ֱvZdJL$ti-MiMn$7x39B&s\ Mc[4#4#Fa0Yqb|iFC o.4VVH1b(܏QCWȚYqA"Ces(@eWpbYG}H9ú"2{m'>ݡ\}ut[ke.aqpLթ-8\f6K㧬.a'G%A0l> x$X6 Sg$@R٪1,2%okSfCa IDAT .CK(%aiج:2A ̮GtϰW&mWąi!}MoD(E R8u~GXaonp;G]dٗt7,H WMzZ{] 7ܔ`$K<.3egʆG5@oTK83FhF 6,ǗONִܼxv k Cw=5f HQuOC<:䟃IѪY}|ylyYŎ}W sy.ð!B!B ZFnGGM8I T1)kL%WM*#VYxyf޽26^W&%HyN"h/Ow3Z/Fe1\#Th(rx۔y3Sq`7e9 b#߆A`r 9H$?|lǮ}֮{78Bn^RܝLfw\rxS5ts @Qcn~ NͶwws1E*䮘L-bj4~pS歵7̋ z6}]|FM]QԜ݆]yߩg]ru6m(Zm 1$If$/19R%{OxhB!B!ȑ Dy1ho!QxZKCNGO]~ JAOzRTG_p Աu'(m},ӗsHUie񕭽v;}L^u_Wn~y#`ws6>!{jƇ72CްjUyK-]h[>%=u26>!<~E.qMe= 5*Kvb~T蒊5*s6,SЩ6}dOA55VȈQ+m5Sa5G5\Zc/0N!avGKZΙ@!pj>=:}D6:""-ә&/jWg;Xxj{K {3<δ 8TVñ>]*pVj:w `JjzѫE5݃%!ryqնXb$9B|L4=.ZwTZ>+ (쬻 !߸ڐ7cANR_N0B!B!y$Ju3?uǚvGS_by`s>:{-uvh]?m DϕKH`l`P/^)XyATs7\~vaJɁч QWg@">_s"mv?/$+E}w.9]No~`AF󸤕;dӍ">g C={A!G5z/?]ovf&ŇJhi{uPřZ.[79<#ZqVTj`vJ0#<ۢyN|`vɔ$P Sm,P֯܈E!|.٧7ΐx?w- UԶk<ބ^ąH"t!'nQVwo_=A7~zB̙!B!B!4\f'~6NU=ЮF/7*=Wxl9i\NC]Mgchn$8%y{cq9x0&WXGVNcF4Ti.\ﰫsY,3 (C;7^jlҕ;w^i!/Pq"E>PTyS/EqpmK.XF.́ױ#Q3'y)sQgY -S~g!um;Yӭ5r_֊3Ciئ7L~\Rh[&},:wWZmP֛$WOf#>P2!wJtʼn[~:H~UoY/*l.ؖG^TEd,Rԛ^ۦ9)$4@`0Z鑋nNʑG LZpTSXVwHxê_Wo}O6ȑ#يdw !B!BངYʳ*vG|cp+d/39Y+7GNrl x.:504&:#2P3Y5CD<(ykU۳=P"N\>5|֞~$H׷p 1*+7̏oMVC_6[m)3'!hʹiqKr Yqd0!#zz) yQi ax|gG]5G*ST΋[7#SUr!ti Z8fLъ!v^gnjkE@L0;%ʮ:;/ԯ΍L 9+mz$bϱ<?-xMmDL@1*ɂiH5F,WZ7 `L%P<n1PJr z@a]q{ib,@̳Rћ]`,WJq!1%5vmcِh .A"n?[7|\<>qKL/^{qX'ܜI#OJZbR!pj<Ζ*Fo?_ȺW 4=eDas?U@Gk3eå:Tv)5B!B!0KM޸Ѕŏ^Rs-cmlpھ{[;#=;laڝ4CRJXMUȴOpx?5}*B,m'bÓඤW aR|a: ˁd,v33:OU]ݪvV`2׶kjG^iw7QRjVIbw{+eVT3yhb'#V%7ZtGK7yW_vk mԏr1ΣEb,$@tW^@Qu_{!!!uuۿԯE e@Yonzg_pG!.9E,=WO fB!B!Bh|kIxv%k֬L||;.$)Vx$ԸO OrLԱCA A$$@$A7?9X @$.A@Qs`F%BozoSWiqE/*[6s%0fLgcd121soFf:>#3Lgi1aid121s7#_v 0+~S/_9Zg/;_S[ـ!R`Ϗch 6c<ȅ9\VvaY7ڃ8anilsp|r'Lgh8&/,ȇ{oxd[{ 4|fdg-; B!B!B!>wyIL&Saafm%K̚5喔޽d2MTxNaz>sy1ыu&ŗ+HdBػ7 EFݚMn5G_}bXO,\^\~W\Ԥ}љ3gvk!BQܭ!h6b k[ B%z(22^())?#k֬?uԕ+W;v7ޠ((B!0!(|EЭ_!a9s`رcǽ޻h"z7//lBfw}w^^, w| k9 ;c Blo!6!:${nG׬Yc+jzd2L45--bΜ9S*j4OB!|~!fBlm"tW=Bm|kIxTFc{{{BB7tFr<RSZ[[ ,,b2Fz{]7(UP|1B?Bm!&B|#Ʒh6mz饗o4ccc'Md6M&D"Vk[U(z00 ժkk3fb1W$$ ̚>s8sCs0YFTj[HS+AaWghwWaxQ/b#|EЭ_!%ڵkT*۳N&bg[B,]~]Whh MEbiڻç!AHerDE+ڔ̩:ɔK %ePZZJQTFF]Mg%%%$Iۅ5;Bݚ!F6b k[ Bl%a>ӭ[zR_r `dM#])YC-UE';^WPV.evZ@b")Z[ZNeD" l[ļ it;tGO㒼>?Yn /:s^.o$B薅F!6·Y[_jUbv/ ӬVkww7_<Exa 0Y,(GNʖ\4]md1A]ΩwL]o_>Uq^8/ݎA/]Sv5[ؼ2kGO;vnP IDATy0*jź!B!B!B!o%arwyg^^މ'JKK'''Ϙ1ԩSwi?p|Wh.4v" (sx-QkWx$Aj36NpWg-fS{}H&ǹYUg~oɎm{.\^i飝0s[y#PٟU}_}Z%q: o?(*{4˾0n"bqN7щM༒&h6{,zP/HXLĵn[^!F6b k[ Bl%9s愄̝;WP:u7S|>_=?v;wcQ82)/O7WGd5ڏZ&gu8\^X|'TtˆoO"{eI}oݛ&d {˞jΧoO*? I%5 EQte8tѻ?M$_w~吹Ap99OD|TL`;T.e--c "rIT~rѢ;.75l۶5vԟ-_5eEBm!&B|#XK>`WWWaaC mw?sj7od}mRirhJ&7j;;[[zya=N6B;+kFM zSmlpÀ qb'nq}&Nݼyd7ɶ8vظM0؀M3# i?(B !$,=~;fF{#Qw>!w`s',Z-nJ*8W|+fϬlOZKm1ݖssؗh_u(7u|s,9S `} EsOOU{$|,l`2*ZVSA1\yBH׭chs1\\8FwrcD"N$N3|~XK66gqj7B9$|"Dh!h;%\PPrJ*++yPQ]]=888iٖ5^r^mYo=JU#g&޻C譮q 1hIuU.!Ġё_`~>" M]c{/g1*+l罛n{/yq<_QוҐQ {y junn.f@$ L#,))!I2.f|ҥWe,0AtZyG>y  vs{pTHc`h(MNup~phLL qq{\ys{sꙁ&*N-x @J` Ԑ 55S;.h4v\~ãG-̜~'+#0'32V$&GJPȑ2+toy\/]‡~!>fBm"4]r8<U*UEEEYYO~?/]jl 8F#ↆ>vvǝwNHLPOѵsWL+z&ziE0? YEI^wd*ZϾl)=6m=w@H>x?؞)H8H>?q☘x +:IIx,xUb|qqcWNUVvdryk `?ڰ!;6A *7::J{zHg.7@(n7+#eJNh== Kbb&) W&%J;=yIB!7BQ zdƴtVnD?8x6pl KOl[wEed6XiW[sZ@;f&-ܒ r%f@NA[ ~:W"2X8KωcXc9kSu@G~q4,K% W;,0"pg\ڧPP\3Z{{-nM[iOgf~|/v$&o$U|BX[9s`ǥKNYؙ͈jJb+#GDdEG)5I Z/?}W.xTf&F;wƯj'B1c!(&B !)ad Ƭ4Hb|} 70֭ ]]Kz5-4tQdd-ɍY!뛽,1ǍK3E`Z^x){Ow W 9L/9gbR+v>pԚGS4_FĢ^?OCr򜵆܍f/Fm| !]翸lt >P(trllpmOqquGN;8??7oHjJ;Gp8̉jM@@Jm.x6P)aפP ˇjvt@kuRCC^g.w4ȈJuuzdEfJvMwBZz{MJ#,:[燇ʇ;{l̠oo;-seqqb)[.R4pfxԚׯ4 LqR]W&'gnhhr`P ppz#ee7GSB!G6fp~~Aʖyom-ݝFKg++9sD|xJ GDjoLN~A Zךok߳G+ڱ+坭H٘g/^5sfLwuHAoHMǸug7hRNUR]vJؕתU8Z^lh>܉É9c!Rdr@@ߍX__͌NjtxzL2ƋE:46 &blcO0th(#*j:dpճfz9sFSa2MM{lESCB,{[7r;z!#,BQM]J #sYkUŭqYX(:U BnWJ۳'32a|[y$ MKk n{5Dƻޅ=r+L=7zֿKW^1{:y~e_w"-V4o! ***K;sNun o|cǨCA{'z{LZX-<]Qsaaa]NgIO'RCC_)4P)Ѿ>} w``hHw &"A,BcjMƽzcbX~4=fWd~}P(S(wfGnH\96j!o! Ylw-BǾRn3gER|8z]I@|-$U*|DV]!7<{j?w4&'':/GƯx F6zd\__]Q3w|޼3iF]^%[[ü7?@IW{mmX]TǥK}]]D,!Ɂ ]]Mz\YY\iJxܦ5IۧP<badȚΟ.\W\y8V7tu/N 'D&=-a2$M62ZܺjSZkoKOO[o{|9@pP|fʶ6Me/~|3_^z -yiucs_  b#hg@drrv "I2..NN[3xDK -ADuj5؝\O''Z)yyX,j}}^fgsYCJi2gNNk,=G d a\g]694N|uUm6ٳ`errΌ'ܸP^pzzJP,gH!wByeԟ-blN@<6 Nŋiڕ5=t'+G)N4kK7g߹M\ٵg>fBM!dunzCAkSR~ G]{Ak"r[ V[cd [ Y7 )"'c1+g^,`X9L1 e|/,zؾ >+˼љ]c "ח+>Hdo]_ӝvfȣ3; wX 3:څ˥J$IM?KR/f3\v# zugaaCר(s#L=LŜ9[m4twg@S] `h&VSDß;y"Wx۴I>4JJJOg1QQ\.J?4'~Zĩ# M$}#|| []\aRѡ]/*ZL:}`hhgaveW/@(0{+WޗTWxB- ZT_'oN!FOo:|tw=B`J1Yxk^Nm.W2QwWunX9DXJRw0ӳ.r`8ӔCWgd ]k/*=Ex<ۣs|` -ar菺3o_h4Z/SC#ȣ e%4 :=zI1#:]  xxRظMK-!AIIY5k >mR>T* g(P}jMկ3@ްT;j_\|+WGF\]W&&n=wz{ kkg-W\ iTSc/ۍ6`W?pY,:A,',I wd%L-$nw7RVԷf?`rIc ?('8ւ}oPxwb $G#Es_'W\6J)aVh/}N딛BYsRb0GD|ss^*{.)$^\6-Lw㞁~7wP X;{R'#Ff;Gp1RwpudM/ 5vw/3c̝>:;/)7u;MҦ&`Ck{Ҽ6hWuu.pSwwj^ :UwOG~[ vf Hjk9bɩµkG.\y3fx?@!4- !a->Bօ6nG9L ;&ۏf3]A]oiyeNDv6^7(EI $}f mM׸փp>wOcL:w`R&+xu\RUSMht]_O$ܽ.i%}ఱb I??L룙4Xʓcc#}|f]1Sljpիfq+W߰!J$ZՉ:9!E#1g86x1 +CC<uGZh Y BT>SQr̙_?>+(^.Y7߿C{[!I[Sb顿0գv=ѣڕ, 4MM牡 SrdP8T'@is4܂g_11i/݋'!d#6zzoN7BȊ&B !)adEG1O''+;w*Fq* vR`xBlp/{lsW]|&V||{"IV#.os1cƬg޹<<}3~f^ 7ؑs9MH| $A<*;ڢ;ϜɌċ˗?f850Il"G7v2b_qqHa2lvNO*f, V }r'rFRȦ ~ U Wˁ)lܹAAɁjkG5UD`A 4U$…/-!0.Gu IDAT`VUU˭vel-kn#9K4}f {3 ZZ8L&."3d^ih8RZӬD&pr nI@mGybH(QSt0xxFFW1Ԍ/:ں:'whnj!!;/ |tw=BX2.VW{?.e~]#J vO.qGX,i ͭx>&w OS2oYOhu}v=ɨ|p$@zUou{Ǐ>*%ʼnlQQy'͌[[`Nh X훋d !Ba lNήgSOQPf3/ѣeelNOJcMnЏu\&'.57(W;wva֬>q˗ ޘ GKKuہNpo麟Cv_N&~uZ]ب]+Sk./US cG~vqwprA{jnnPY]5$4hF~ݻoo?{PGyy_ccQy˗ BMIyz_~!+wM7x# vL68ŅKw$'qon砠ME~Xy3ms!\pԜT Wʸo6/dY}0q)\>[9Ii7${zztZ'{ޛk 5q4ӧt :sl\S*ٞTKL +&d{=}}m}}@|6_(Y$ ;TL`3B!ϟmv}g(, HJ']ǛnVP\t{R¨G6mtiYBBϪ3gh4WՅO 2o}B$`w HJz<3g`K&SB'':'_?2z~=N5p|W'{nKL\;{ض`OOIkng4*ZZCCԬ]2YD4x6;U qqL:k,#LJFJ%pC\6zAgΨό!tK)Z3@DjBZrBh!p+%g˗ߒH9j[+m= L< '~Y4Hh 0cǾ'fm--eYsM3y/SmƒϜgAgg]n(&ʝj38Ayh6)WWx6fg{9;kj룾fm.. ]2ٖm22g TMMz^+wt\c*3Ԓ%Kf`1Cx;R7r/xxѢhs57S_ |{tVLݕs4Zzz~:wnDž tT>]Tf}sNTw>pwHow}%@P`re"lp?ǎlv=t͎NG,zKWq|\8ʎsr]\@HtG=S\.!,fwFYDh!h+%de}п2ekM L(;OSN4f+/_8(;ݜSdNNX"nfg?`u(;3lLɚWW͍b T:;ws~ o[d׃_|al ?838A"in{3m~t&''@053p\ݻٷ/әUTRW6zfrSswk;wIRPtwwk'??n|~+Ǥ;;~K&?_v-=Z1J-gĊNN{__IcJ #O}f?_8%k@qXXfwdך$a28sF>zԦ4@tD+¤Ƞӟz)BrBh!p+%c(gQѯ֕]9Y_WRRn`Wᕝ{E SA/(U-5Wx^96[b+H8Åwn;}Q] QwwVşlTm3d{Ķ ]6c iRb%[{{[GEِRYPY9F:NT|P,74ð23/wwkM[/6uw*)Hl@68xz+ MT _)ehvIo²kpƆ W0=tk bU#F!OoB !Gc_)ad.;9Qfǜ䮚ڼ_N0Xza09899tK/Lwvw CeK-h]E>]K 4*ܤ`\%p9ò4554u%ueFQfƮ=xw߾V68< ٳÃGڗ]ki x}'>M|A;:BY>BWM7x#۷ypxPgW`,@&h-ey<˲ov\1 \pXa_-n1g 0&ZסRA蔤e-ܰzYsc1 dx]u4ߒ$Z66zjql&kee V2_DH|>ӟV^-H8rT1 񩼹loͦJǛ7o}|Ds !4!(ytw=B`J!M|\BB $T坝 yd|.Ã뗐ChmЂlĊ1xΛ}mI===^$Ij| Z%j5IFnx0xpԃו)QXS%uvtɜɨ y\GRS'9N^?rBh!h0%옦pAА@ .$T~  te2ÇlM=x]MqQ B!B!讨klxpY\@ C]5Gϰ@?[ut^.v+kI׎-#X6 l|T]RRRBd\\NMcW^h:T!B!B!B>aJٵ%<# /ok,/ߞjچ5#Y|>ۺ]<7INqsvbИ]}S$%o@_c c0Y:)aB!B!BS~8:yN&yDzD%,*5YF)K._6% _.UgN¤8:cJX4:ۙI4&DeYC#GJZ^~7֏=o[:w!8{ŚI!B!B!B>aJ!/p<=Shf̬ MEvoߞm4599x, 3t*pGCŹ=_E "lX y,{썂`Aᙱryυ}=\N1Ӈ7C`:+ǖ+!.!I2gQ:?|~W$w&-lJ5TNNw+=009鰇(hA#mmMSv& B!B! [B49+mحiU0sC ?^+wjGBӾ9^bg0m;M[o97%+g>a͛ϬlOZKm1ݖssؗh_u(7uprl6PАD&S˳ ۶mf|WXSh JVghajL0vc!u4yv"흹C h:%|e!/;q8Ria} Ŷ~h|[5!B!B!tWJܸrs[zz?:4ڊ5fjMg{7v>.!&Oj+v _h{z1\ͼ-$IA~ G~١YRRBd\LҥK7X(=qa8h|[1xȣ -` ʪcׯ..V koZK6PrPR#GY{<7υ˭%| B!B!4mꈦ&`^$tT_Wׁↆ55#}~Stmo'n ^ZQ8|0/O4CVQka&dO#TzgKiSў9 A7'$SNu5M榓~8K{Ӯ"Z}ڵpYo=7.n쪷©NW.ϿvͲlG6d2hAFGGisssO  x|el]tR]Tpp^^|IL$:Bʤ$8?_i#B!B!B`L3R*?;q`,)&$LN~fbg.9s;|uWTF&.aUv_;'i%>snkyNQ++-Z)KK+3{Ԃ} 1cͮϞz919n)yn\`W'wsj,='cYMA1+h?,$h_Hk--m68u-,l\1PJfo"1ξr眙cӋ#toHUQkM}~NG,z> Ysr7%&/fJ6؊fLMQTAP՝RiTzƍgf㷞;[f8qt2G'?A={$.Eͻ0+aHlY<{N-8㨪{I Ѯ70`xoZ~yYP=/orYU3g~uꔥ:ܰJGQB!B!4)ad_v Ŭ $|q))1xZHbLV+:<# ֿq~ 5%|??Q7lT&BVyH)i+%l0XS>vEϮh_'bbSM9&-b8`ƚٝ.,|`|'oI gFG{y|Y~X3cFj;_SCjq.wEbbR`+'S(Jv]g0Jm.x6P)aP ˩lvt@kuRCC^g.w4ȈJHNVThdt'D4xhA\om=Q^u~xx|xg@f Oob0WFoo+.֞bmmZhMK ; 7>?o+G#O- IDATF}f&tK{374490P(8q8ryU{ !B!B!d0%<(_˻niz)mWぎhҢQ` vEgn 7c2Dn /FbdF5R>,qql 0:K!J&$tMU6ӥfqIbc5aYd^ihx-aŠth-!ys/&޵tw ̤Ӆ|>Ki"#WzC* fWv ӧ|e鹂}һ~WBBvf58q9IL#7VTEАmIllPSlU՚w>uwgb==ݸM9ŊK7ig[zzްg+#424uwlm(^ml8GK僷?ET8;IIuu]zk.n<}s'Okք{{@YsɊn799@!B!B!dF{{LJr9B}ͶK)7IkAB#t$k,/w/C {7&ގo,Y>/_zC`8序|q߉XApĻ=.llN3^T!ϧȓd Rabf))\ .֘פʗwTo5Ň,o<99 k--E ?<X;{{F)!%8xX*{zԥ`II\nSOQk [>)`3gnn>>_=銊 t<[]Hz:UMyXLрJ Ӛi2004߻lpgM L1&^]h1C,O ?g+S%&BBϽ{OWVRE V!B!B!d?0%Lss]k+$i<@Ti<ՑV]!7<{j?w4&'':/GƯx F&i+ ˿ݺS5jכw& &BX~nn慅@u{BMu2%]97d] x'fp8V|˺\sw5/LЋe|!Zdxe Ս|'\(]{,T/$KX,Iqqq:5uܚqC'Zo \$߽UӬFE/J2he[D!Xy@M--#[zz@a0S 6yU6<1L: Ak&gf"6ڷ&\={6LNΙ1C{x^pzzJP,gH!wBxe40o[LhfZ4ZMc@Po\F׮]YɉJo:yrDYB!B!;%d2|Ͱo}ҥ>lKK UᩧZtiss\.衇̙K/)[Yzz_H}wW?kb#;epí}+έ12nT 4VF6ðr)aY5(-?fs?p~a3M'?Y_m'μmy+@D/3W<|% ߆6;cJ H$Z݃w]󅗳38*;9 ~~icSŽƜk-PmtJ23r.0h4hJ͹5j%رƋ_ߦM!8PRPz:Hw pTTe92&ND"7LJ\РՅurC<+&&^,T*?' tBʮ<^PEBB!B!ڭ`B7x㥗^zW_x.ajkBBҥK?c=3|͌3/_~kcj.;wj.u̙WZWfpn5߫%8|;Օr=<̏l,T)'{S ^ =FLZ.صq|~&x<]+78)<3hLq6] 0/(il|7zF2Xf0c NjY?֑6*?߸s&'^KX |0SRyC7|rLL68d~x8 ×jj>q aP\N:4#d WN-$nw7RVԷf?`8rY~P@GCOp콡gUHF#Nyz%O~z Xl̜_VwsV{\ {Ν3kqR4y'EEU2^niQR Mk j4M];}:ovG.࠙$), J<[AAAAAнpΝ;~O:U"<<믿@dgXXص }OI RSڮ/!齚^; 70yjeePb;{O$0ޠySxBk`JPZYY>I6nǤd=ue+? (Vc [r\?)vZzX O.^,1y-ΟX6;id/+N8 <ވ6ɵW&1nׁΟɱcbhNJP@ZmL7< 7dd/]dY_un*Ȉ>`1%~΄U{De%g 0,;g#+_xz}=7w+*NWUݳg?#m3[zz'>d' %$*x|Ѣ͏?#:["wDb₃_H&K[;z{V^7|.UwΘ`ի ztF#=OGss  ?{OFHzt4ZTPqrZTT=˾kf&&@~ZOZAAAAABxTTT|iii˖-կ~? p䎉"S GgDE=h5www2 #ecGCG+5PC]yQdbӍ9 3'z"9XuKM-{42 5𸉆O̦蹙3fnY0Çzt۽狳 )555,vvvWKՅ -+/+jʔ'N4\q7r !!?9:;uCCrXM2g蛳njj"Q,?x/nɑ#Z.P.! [zz~~٤g =28ExiffrxZ}w~mެj|~zTTJxxZw}UP!!nAAAAAOYYYYeeeYMMMG]xqHHHSSSUUUllP(4dHJJjM͎ {vz(,_&K {S s|{ʼ'9-?}si"G0|~М)m5~rjQ,n@R{OL `֮Y|놓 ;R@u;OAT0YhŭƼ,20Y"l\2o3M˦Mٴ2g@\pphj b@:x]eY\|TJ!,+ ?9AAAAA!aF0_Q ӂĉIIISNأGzќwʕkU{yEǾk*I\,5\\{6]C%QԷ>r'xbǎ3f8|pww7l߾oկ~%뮡_݁L+BдW*~I!Q)QIVT_lXZw¸ihg/)'+']B*ˣ tҒrMڸp]{ tںZwxS{ #X~e>7+)W*jT;vwg \=u*kwCAAAA6t gbxɒ%78q74yg~駞z z{{_z˗/_p݈۷cSsҥ!1bL]k,#UK8u*3JT eM\_^]Н,b`,%{R k1jn+qدlš5?Y}c׮c͍vv, cee"BAAAA;{HXcbbx<^]]]ɞP^^$$$ V{Bu/^/N_|<dzz}kr H!C̩}埚ہeΐez!060͒Y,c0N74 VO>_كa \ kWZ[7:bVvv,BCC_ݵ;NAAAAAr=$l@V::44T}g`:0,چ.'4[cH6W8, IDAT  :.|uְbzȘn,bxa@",a;_Bm#w3gQxoAAAAAo2=AAAAAAK!a:Pp i1 +nv@AAAA!a*3S ` u'l!aK> c9k};Bِ0 JF9um{?f~ͤ4I1r.ͤ4Ii4h&7r0h&ȹ4[YCbJa 3z^,-8:98:y\Ƽc[ ;D0gՕ5&)\ mC6E+'%wUwKƊso_/̚ dVJz ]<]x_|KبkW8j7~ٺ!Vr BAAAAAAijqNp9F=m{ZP"ZpG]y-_nuK Fz.^))螠|nҒI:m`[&rߏ^.x? LSX_[yd/;=<;0Ͼ7CoL@ᛏ,hŭ#*vʞq \,鸩: : MJ1LiSS]׮$qt \BK?mS&Oh&LO#LOF3i?f~z# 3i?f~K5$ j홹(LI[upTxT-E2s~<|0,.uC*>vϑ<>Y3 󯃯}coF,Cݎ}7Σ{<]!i 8\V,k ',~gP:5{!0 C:l?j ա~F$b 8yVdf?7Uƌ'Wu,^ j\ilm6~½F3i?f~ͤ9f~ͤ4Ii49]WIi4c\-!4B>0nhHO6*2k 9cD?8زhF9?c*Uݤ],QaV4+nېwdƢ$r_7!xx[tZ^y?鏃ɐokxqLӇ8  x<˲> <`: O˖-?덉ΐ^?׻V0BN0Ud0FH;BrXثkxg6m:YY9jp??} A SkUmmWZ[%s駱r ~ͤ4Ii4c\Ii4h&LOow=^a&LڏsicIZ,iwɟ 70o8c)tUT;^UTJ.U%Ļǃ (#_.PlD5.dܦ;py&o?S=%ӣbEE7OtEx0iƇF?F-HE,Z`ku֙}ط'!NHxt뗯Q͛jSUVrg}4xS>8|wxOb_4'%-q &%ƃ??vÇ,reJTn+Ww$ʍfȍocop+q]sSdӱjvVVmTw[ñOt;g6n431=dyU#WBDp ǀ|6m\\~7\ȕVvBǿz\;M3ۏOM읎GN 8|%t%lr7J]-_j˿ZGMhKpW:@״kw[ͯ3Yv-:[2%%o'ϴi9PZZjxHJJʑ#G\(8k,Gb`J!wBA>>,c^mBDD}gcX x箻₂9yr~JJe[-;=$/=RR0ؠ~ ѪxsN O>1~B>G?8<鳰ʌqrNhvߖJJ2'>}c߷][tf3w+JU_p `&Mϵt6!μ|Ől0j'b!!ݛOo42DܐTK XՖ55m+.hi}&Uo>}zvbbNJJT`hNVVxn8{|'DDh4Oĉ7#j6P* GӎϿw,Ȼp4 TzԩɌ11JG"ܼ#-sPp.ƥ6ʩk'i4h&Lڏsi&LOF3i?xF3i?FΥ ֭[ZFenrMoۦ')ZZBoXR4b~ӝ|6"SwSr֗:cXUW,qKT0sܸ'L8[[k9$,>4%Myڵ2͓&YPQc,_>79PfTT _>uKFy책 WfeտSwLWZ[͆LZiH OUU d_25"˜p}$tW"hgѩ[Z,;ӧFDW<ŋ=Qq1]2(6;t6;!!Fs.#6<7Mw[Zpuש:LwwN~ԩ""+{7cڲ%dm™QN]`?OF3i?f~K3i?f~ͤ4I+̤4I1r.͖0?]W*Ez֭=f#Zt}}rE#KOlU*)!yj!XAZˆm8HAA2mIPyy߀o/KίOYh+]ݱ496CY.10cص [6}(c#dԴĉ҆^eBB=[!< ** OMQ*2ti)r׬yav[3P^ v]] E*Omu [#@]Gi1JZ#z"" F ]=eJIZ  5cwPRxqP8__#@&{r"0s5AAAAAFlpNTt2ECm͕Xe`[sC E`g"+-eveg{"_1滋wA c>ys]O7&;U;! ÔXl>Zo?{uǩ skZ={z&8?{ϬYV7(QQT֞lm}3/gqMMI]{w-祤|}Y'NX%gkk_޵ǤSNE\kj  7;Pbe]u=Vƃڿߴ~^POq$??{8U@@RX؇{UUfΣ̝LOHYczJ0$<sr]=4FfTh޿߸\ъ~ LT,;W.75m^Q@"z1JZya#t "Nv}R$*}}̴c7ہ6r ~ͤ4Ii4c\Ii4h&LOow=^a&Lڏsi뤩TK;z{=xZݯXvha]QJ֭;&VwMg]wl',Z4>O{1Qvgj?--?jsЈ[>bӤ\!GEGEpRs󸐐X'$1 d|!.ƍcbjͶpG,,Ce%!JkO$YֲnO>Y`4IN|'w/5QsM3M5D"\|#ᙸ2fDEٺ::;C}}'-IO51JV{쫯ʛ \ZX̱ Pt TwwWGD[ J)zfS;apf&ȹ4h&Lڏsi&LOF3i?xF3i?FΥ޶mYʲeˮI$[q)N 3 ?-~b:d\1,;ydHIJlZZYN]_tß Qk:[gl,cA˲)5U#I R(l}|`^3.$D&]i¢Kf%&Fz]x/B?_/h h,@Vk+2fu`ѯ|~rx8!u ۶}QPbE'*$-Y$-[:; v={ܹSbceBqp%ֱ$ IDAToF;u1WѢ,Kbeb1h4f.1Lޅ g<1 pP̨(U݁B.O8cK:q~</\@[TqG7W7om{EpW-.4QN]`?OF3i?f~K3i?f~ͤ4I+̤4I1r.͖x֐0 n #'LH qպA 6v[:lDGkcLzh@'t\>a1<Ba\8;!]ݼlPeݷ￿u3cROpc m~60 Qo;ޫڞްjE+54YgcnŰiٳ'R,k0-+RT:%.?W[CCV#A܎#}{g+O_H:'k; M==v`v;wܹ"`nJʎ3gO*5 ?./wb) *bCe٤0ӣ~Rp4} *V{+/5I} M3dJ%UW{~LO s 048]W'Iĸs_wSG 0<00=!r>9K3&I1r.ͤ4Ii4c\Ii4h&LOow=^a&LڏsiqNX~ ?4]!`⚚nTTuռR߀ɳmșcv>IV 6UDOYfl˂+o B;g`݀lL`OzCE%gs_v^\[q_nya 18)</oӯ_c}Y=e'Ɉ ?zz Xl̜_VwsV{\ {Ν)\nYU5r[n[*e/ j4M];}:ovp4\$A3IRXyķXbb+ k~leμ)7t. {dJԵrGۇ?,0.6W^1|0˲KpMl9|xL0V-C@g`?OF3i?f~K3i?f~ͤ4IMh4h&ȹ4[CT1,m=}jϚHn:uj̕[9w%fHVK|:vu";meO$0yvYmp:o_z O(5CPZ޺C^s:I ~_;ĐD٪ˍ죷,:2̣+g>ՙ'l7Ӓj(*c\f!oV1mxz_RY =='+{=6lJϣuKɵWw>%0L11;f̘;9&TeIn;NJP@Zm<IJN|d22.55c.Z|ٯQl36uuqߝ:e<ϟcfCB|R0krH"FsM?_"[RbmS||bL%B>?*[ZR0vFf5$477;Ŏ?jUUOZ+ϜCݹfSٺg#p.pO>)T*)*1߼>߲o_pGȃƹڊ6q xhKN t 4Ii4h&ȹ4h&LOzLO#l  #_zҹ--ss[Ҭ6vyBT́㪺I Ԕ7^sXjydDGDž7Rg.-ҨWmfDke%g 0,;g#+翱1ZEBC%$L?q5Z\,̙%uuTɱ]}}Yqq24vw> d,'WWR;>-**.8EٿoxgYVr W{ν%%-uFϯZu>…cbbho.56z`w͚#}%%:pOǭ&>޽Cz}\7 x>չ)Ʋ?,+d C讫9q\$BxS{#EE=9uj\\Y P&3ٹsN0RiGO;[y4 K_%$X95G[kד2ۄ33i?FΥF3i?f~K3i?f~ͤ4I+̤4I1r.0lc76Lݵ˃%Qg͜\ g?? Z0&CKT>7ѐ"Uiþ—=73~Cuy09|0_/ӟβs|q!eN3Cuu50&Q`K);>tN)GTUA $B׮mnQJY:µ"#{nH/}xЇ|:z{tz}B]}}{[8Ȭo4 ||6o_hNUVf8iҜ>0.ԩIaa+LYe [ï;gnH|SK4uuFM MI}}j^1C|tE{5ow\jsa9Z9 `MxEE7df:uԦ`P}˖1mQifT54h VnWk0KNMKoEARXa:X R0v,4&㙎nYҀqHbqESS\\\tT}c:X B@(t>>LixGɂ.J_l̤9f~ͤ4I1r.ͤ4Ii4h&7r0h&ȹ4[Cˆ}݉]͒ȃ,<*-5'~h# ʜLY=jy-86nΞew=oԶ,1N؉gl[b-{8qqqϿ%wur}ȭpt:˲? 2 cN--~ܴ_߫o@9I'y<ÈN_Cqz=ykv4ES :[>{->Ipz'N ==EUUj{ϛ3~L$ 6jW^w}rȂ T2aF#4tv~sƓ'-|sĚbKMM.5}{Z}ra_;v䗕h( ln"4339<\վUOI]6o~bbX==oT={v|H#)b1 Z-_g5 ÏaĘE3gOVHm]]>r9;:߈EZ˟~^o;qr==__ t3c*D#6Lڏsi&LO#LOF3i?f~z# 3i?f~K8$L%nyd?wgnx啱k)=)~J䔶ɃuAsUl~ɩG% Db<mKc?1)pvfcO؂Y>^jdYgggfN^gO<JEy2 zE+n5dY֐ٔa Y=eu}f? 'Nl8q,/6e&}}e>G" zz,f" c{ƅD+--daX <^FsGF}=[~Yqq5mmQ? GP –-/+ꆆz4Ʈ.^Q6l۴[&dzh::{{y:{{{7//@. Ʉ|~V[ZnEz5vZ.IvRXr0ͨz7/TG"i>W[wSfgwO?5GycgqSU*XܪV_ӳD( P,bpf&ȹ4h&Lڏsi&LOF3i?xF3i?FΥFckh0pԀV+PĀNWP^>FIkOOkOeVhmata2Tl5ӧG)uϝKljWT\(3'P.nk|B +WRHrǗ)+WWuMH?˚כ/pʔI--=™cګ*}wk{zұz[ v8jJ._~x.?3wuדw?XTR[Xo{ْ6~½F3i?f~ͤ9f~ͤ4Ii49]WIi4c\-!a:icŁS6J֏?)7$*%8*jMKN4mL3E"t־ PHryTb@^ZRUY.2 4^Y'tbXjm]]-grT7m3tww?裓B)jܙg sh&ȹ4h&Lڏsi&LOF3i?xF3i?FΥ޶mYʲeˮI$۷cSsҥ!1bL]k,#UK8u*3JT eM\_^]Н,b`,%{R\-Fmŀ nmCC_X'˲ou̵Nź\!NƈSX~=g5al2:W&DGbB @CFȘ0><<$YU T$Xx ]n!xϾOMvlgggHD˲zސ__ruӰey 醆<){B; +{0L+!~~juUkSY*NEBhh_yc㫻vyh G/4⇾ѬzAv;~5N8~8|$#%TUUQ[R8qbMm_gAqqU} U[`CbJ.m™#LOF3i?FΥF3i?f~ͤFxf~ͤ9f ޼]`hpT)}rg~M83c\Ii4h&ȹ4h&LOzLO#lg #V4iDFF~{AL{k B_8{]pƀշZZcu򇨨K6P/2cm~cpf&ȹ4h&Lڏsi&LOF3i?xF3i?FΥA<0b@Wgֳt{3 ^8^\(ʓϟ;B!O 4ׅ;6ʓ'5c)Lc>9K3&I1r.ͤ4Ii4c\Ii4h&LOow=^a&Lڏsi)0{/7cO8`b@WgphƎ/0[l ?>lxC3gfEN3W, )_(4#+,UZ+6Yd@_NP"N6C@&I1r.ͤ4Ii4c\Ii4hMd IDAT&LOow=^a&Lڏsi &y)` ŀxد;;'G=%"2P&"H0fJBR0Qm ~g'Ե 4Ii4h&ȹ4h&LOzLO#l  Smۮu  nD*#]]lL۝E @.l@@Lڏm¥\kMΝdkMsk&έ{8~:[6@_cΌ=[3^ù5hg;; M jHԨ1_lyf7uS6dShƊ],Az303L?F&4f`;<s=:}Ρ) ]maK@ay@WW1Iu4)?b0HZmAAa|Gf\!a}mAA>??vAAAAmawH[n,3yfVk9+++33^tiǎY}Ϫ0x:~X } =4ܣ*ۂIKB8}KK@.{> 46 $*2Up[B_o`ctZ] _Cn}DCQM}~WΘL*ɰ>k +ɤG%z~D^di %b톐c=`:Rfddo3Fق!{AAĝ'AUPE,Mts&GdTwB7P[ts&-awH25))7ؽ{J :twߥ(jw_^^ޖ-[/    Ȩ(.e9#2;(ӭFe9;$l {駛׬Yc(k7o^bEnn.B;v0+qWG%0< +fp~vp 'V  ,ͫ`oFO g2IeKFVH2iJ^^ . y )\__oUeddHRB*    4!lt>AdwB7P[ts&-Y!Ar.]Z^^~qcQQiF~0,з?~CiiyMug:{q/0(_ +1}   B3,_9{AQ @n}6*ӭΙT`da֬YD @wwi5ïBpGn%Pz}OSVW,D@$z,qℱD3X!{f4~V$R4n8.GMrЧ;>TԉS4cD"MK(JII1iҥK$I&''ɪv@qW AAF,ͫ`oFO g2Ie !$啔tq.VjiMCa@ "#βsifdY\uՍ}ғbbmTK$ 6G 3R᎜ZX>Kں$$lν ,C(8㛅AAAayas 2lT[3l;333I3b;""Ǧ)>0U*[_RTTM\4 [t-e6yDGp\&K$u+ǯH'绛9yWOq [ ~[u-W&Y >Eat     ;ҥKfO2e 444**ĉ[87xqKٱobJ<'f-pfZ٭9.`D~4#~EytV4W_;xi\<g{oz4ZLoOwvxd幏[A{{~TM.XN+Irѯ1k<q?q\IN׫2-0Ӝ*HGĕv]IAAҥػH O g2Ie !$st~iXLton;LPcy;=pXl_(9 h }x:./8:y) ovyy9G.<46Ҧ9$!I4Z-}#Hw"l ش܇O5B Ifm ՙ%}`cƯ?z+1:eC&ܞ޺FUZ2Duz#$i rE|~n \%ɿl|b!^^B{DI!!R)$juek덖wX?3HdBa\~TA 1{pp# ܄yas 2lT[3lCQwO/5-wX=CBa"\tʨ-(Zd=PLL -++S.ia 3pX…w} gμs--Cj%t{q|hb*;yŌ#ǃ dοMwK6\ ކ/)Аc"&D3?1;dA/\.\u#)ªodYTTDQTJH X6wMa81gg7׍z 9$yOf'KrNw{ۺ}(x~…));/^|knGZ|bd ?Y.>OL9f̙78Ӥ9/豬Dn0vRȃv€3#ytiiW*/)qA4܍>K$jkq'AAU7#'BNtQn}tΤ%|ǨjR%l򍉪;*Atk׮1[ 2e 26tiw|Bv_Os*l?llXȸk+|'\pHhpnAcs$!1W=<ΞGDZ]ښjGͰ^nGww/;ZkTYU4s3/++/W+ۮ\xh;"'[(K(;zgX<hP4%>;dJ}oȘ8@= B?L1al77nα9o`gcr{ܹі|@o~ɵ@ Gψ^]*\U?RdG5{+[[E<^a_g>ZmC4:W̜8ӴE1v,\mhh~ӊ]==[ѰL`/өSjCW+TWo_z_6qGVfرSz<  {f:k |)'wD"qS@swlH$bAQ}wB 8D{;+1l|qp(8& #VIOO~glږ\EQԮ/sPʶ!$ܥRE&׫/tbɯa9%0Hw.3n-*njlXw 8ŎBV=a/?{cloS/eDRrtfMg=0 vs6gZ+@Zi $""#=< ˭w\ybG|QJJjxT-@\~|ŋ&}2YXpںo47<}`//V[ܼRM}XPJә3?>ɪU1'v)fcÖx3f@˥MMu2cbReNӧ %"]S2zI0;>>Z[\f\؜T v̙$A\ih8|ɡJ'@*L J$P5{3cW\Pl/,4]뷊11s -$l*|$qaaιb1I WXv.I9+ 6=[fzC(3!2O* ;MMKJʛb~p>?ϼAAKw@6:go 2 ;(ӭFe98%̡M0'VT?o/onnrX -a}[MykC~`#aY#<, rIS,69u\ dLJOs8 `p j# bڋ٪om~ xc軩۸E$A]dw)% +S3뽕+-˧Ĭ|~F=SL >vmiiO_/7 ??`AE0;Ç ?GlY>94uN!/a55/o~{r޼e'(]\>ΪLbrʔڎcHC(4T>[Yi__,ِG qAA,R*=2Pe =EMH +onnL 5x<'!,pqLfBU١55Scb"\py$27!ak&||:zz{Pݓy)+(d#߷ϵAAlҤL>:gR}AFx'te٨L>:gRf~ v|~J 5C\  MmOD`}+>lpC|?%_ Vtwo\P bWoݾxiJ<}}sg~<[5jQ| ^j詃6!,<۟yoo>|k'} O\ o,[Ʈ]JKt~ªKII.^/&jC(KMmfwwr\CggVkȹ44%regOoGKjkSGDܝqqIs]Ї`_\lE[SS_*:f̶ l.jv+!!PPVvڵ^H??'__1yDʕڹVד¢|Dvz g+*nj)wD}G% >Fm{њ4H?!e[PC<ӧ׫`O'z{/NOYXxi#GB~R[+V@I]ݑkj}grs     40FF<S([nk+STtgWU*Z*DQ~VϷ!ԷCycN$gϲֺ_|:B'{-W3ysGW+?]uSQCoz!Z]{@ 0֛<~o9ڢ^؛ 4p8|ի=E-[VRWܹ˗.k`gao\yytV\ܞ"c'Ouח-ޞ=z8S{&~F93ӳW}n Oy}16 Cn@auhNWRWgv_*]1j{\axxꔱ+= ǧ+ ]˹s*+4O=4{6K2$7ǎz`z=+j>b/6 T *go'?0Id2ːٳ%AicBC஻"*տv4bP]r  fP WRiYhY-~+7낿Y9}XZ-wfd<0cM M }4+kOp_8tJB/d!aKv]4=6V"xx4:;]ϝ3ƃ@x_<=BF2/l4!2l+_~<;**h 2~<hi$ zlKZOdg/HI! bV|--ΜUXh955UΊ7 ED@BQaD@w_E_&wypo OOq Ǐ7hVVZ}rFceC?Wwwܹ#>E,Aqyzp==euu] HǼ>GdT(ӭFe9%ø5[$'=2rqzzϼ$~rM&pȇ["B2R(ڸ^Bg ݝmOqnE[6ݲWO 2A§#O?H&L`Z)JII1iV~|=x>+APsB*7ZmβPOQ vޒzkZZX| SR^߹EۋhnJ<=-q8~RHڷMdzb̢$9$TS#3i!߿gdqeQW1BLZmoUyN|H.+!R;,0!w8P… SR^۾PmϥK={rT3o1!"N\Nٶd!awf=Y!0(XY,\2+b< /_طq!ȑ))s IDATi0|F# 2ڰyOoF}wJ*ט%;1FefUtQn}tΤ%%ӡo5ɧVQW44lzkvbbƘ1ccLF"z^ctk?RY=nδzW uʹ]eh<~hԜOpwsun,5)i~6W r ~# Z}`74g燂9sLwP ]*y7ZZ^:(myJ$ r*Z[?طӃ>={L]wݻfaEEΞrg'$xD"Chi#;8tT <${9w(N,ύ 6RX]mzK$FsiyvRxL~O8ΙV#BUp1Lp& 0W]9'?vgWTx SӧO8y8(ksuY5aX#2y g2Ie 0Kv|8jڹsFl˝>CoT_yպ"O 3V:w١Ù %VOl,l8 VV\}ꆊKmn׃/;toy%ζ%5q۞`<0e֝cZ$_eο$[Ewgoܸ+#lǙ3w+t: XiVϙzԥT|栭^3lmFğg ՄZYur-RmhBFka,Y2;>>{IZS\EĨE)) `Fl,I*ۢ/Kض`[w7|A}m k  +uuGS ?\6)p̝ L H󕕪 x@A,7Z6g6lGdT(ӭFe9ʖ`0 w-kjJ v8A檫V:L۳#zu[13灰TUW~Ġ^hDE4W_ NĹ7[&pz!V!㖫{_z[l*6 ^x$/{ᘕ% ONN6XW"yp,8_Y-[;w&|yv-}~=A>Z!YJ͝;rI2=*j}ߞě~mbTOM{x8^Zk̡Tm-4}o]+Z[]hjDj?::VN lZ[P`Z97D0kMEf<  o@ӧlwD*Ɔzox`11!>"@ի.Upo#+'zI$jZucں~_h+۽۸L_ZllI!~~ e٫v<1<7#'Bp^)ӭFe9`0;,aXn9NɂjX @xd+6i+?k e|>;>?K˩kVHIixz9r]o-~$\yeɆ?>zik$wh E}zTrKH;7.'Mnmx<엌;PeRh($_mW(XcY_x|xٶHđtAg kڞ^1DpBWOO}GGN1R'zǯ]k Z:q◇M}EEֳ)KokfCjys/pF  m$lhѴ"|6αd'<ހ4`ݵuG64ko?ߵΎXRl`Նl5;dEQ>w E/t4H?16(:JC=lQ!Ih`M%I;ΟW # ?_VI]J/PfmZaPYccJxcs@-;dմi&DF'`Rێ1EIТP+(j3͛wKjF>ۥT~Gղe13ccggI]]Rhm&l9{x8gbt4,)q8&0S$^&Sz|S7ʪ\y'9?>O$։K2!%=+׫,6寿>Up2/%%$AdeNn}6*ӭΙTܙq*50LJ_^$WTmҼ{ST+zHb«զ8ubsui]eYHB/lOzdET',M6z̙^b54n͙\qVZ_Fgk6Z=h{=zKeuw55IH /oj2..rsO"&ulθqUU1|$%<<: YkĺU~Dϟw 8P\y[|}wwxN'z䥥K$P79GDV/{@LfpcrJxd=E}gNEKK|H䰰ZziRPչAqs,6wߵ˗Gbc7O?;7nl-+Η2wyD6c]zeC#2yͲCpvpe9`0;q+jbc?6~GOO\ OO/O/ AQQQՖ^MvMҀ][~'*3A6\]zjIN(;z78:$zD$R^ټs_>fZ\*v@ѣGW'Oru-_2TWWSao* MQ`KF:_ O|deooSWZ<=I/_xE.LMխj5 u-BBLvꎞ6yyܧ׮=S^naϗoKO11[ϙo_VRukz֬1e=(55.8xɋ'Nl$I[,}wa#v/?֝wz/.^…$AyxH#g'$NHSVa|z?>G\钩nS= 9{(ٺkǮ^y-iiwL2/93: @Qo&zU_ހa6Hx*'v^pR3grRRx]sq$AZ+YAAQ7vƍ,Zp©)㦦\Qʚ5תoeR9dxh9{jXXXdxoɌ>QQ!|w>UOܬBxsE"sy\;v i%5O6,ƔGdTets&-q0KءcBrɊcsb/}uЩ2Z[:g%V@3͝3t'$7wɃƧ]1`?חgg ǷwkKmcO~wfWs^ln<‰e=RՍ;Kw<(hZh4AXI;`Po<U$@ h2Hb__(..""^^I@?<6qϧCh}lTfFAQk(ӭΙT,afs${wCu!IJUЪP ]/!$gݟMYlS5!$g#]H5zgLy <kkqq3':g?)pSS:/ӞoLE? PFL|%wvttPelJ{{;Af円^Pr/gΈ@D4&l[[ՋܼٴD}աC<G"/ڹ,)9XR!ޚ6gWF/u]Zf,|l}5~Ri]J忶m{w׮OHUNE 7o~s oXOQrX/}$otU--5mmz[jS($/gX۶sYfKрV"?2PUTSwQfWV{?Ks7ٞw*)IɤABqVOQBǕSAA3[)Ɔw~1c<=gD>>;hu \KQ.WTԽ~^||^(`Λ ߉SP($\~7(Jnmve_/dRfFAQkfts&-q0$P#(9Nni,NX `[C[iHlĄ e.nQ3KPVNS8<40w-oW W$ѬO$(mEO9v ޢ[x^_Qoh[rH!#^t"ZjmaQk%/ww233ϯ}oQP1B{ o,_uk47ni AA{yz㏿zゥS56{^En:^X?o]U\Y[QZjGo <n@-e7KIefUfL>:gRƽGnJ1Fo^HL OZjc+y&ϘD8Ӎ>ox --=m=W+$ !8S,]).% !~^^6 i ][[Că67q?p\نC3*ZZ =GL*[`0Gn%xFEzFEV_~-02Y KP=M|_AXlQNy0C' 5')xAg4V4^^ a^E3Z$~าMlPпV4JQ{\n{WDN9YZZ@N  .?tSƍ+)/WK8ΊyBv77.I}}33OKqZ3$$j  o6mҸWp-$(>;LlFZˮg23*p^3gP[3l f 7xzuƌ1c(^ҢRvW"Rs tVph…Ozxg5G`?PJQ^7Ɵ?j}@OQ$A1pTKrV/y$~าAq#˫Mliz#Q[=٩ *mh`|AAܟ y^VYWTxzd)5kLΡ lXQ;3ic=#55"X);߇~x%-R89&f[x@6:go 2 yͲCXL>GL*[Y ^itXwн1ѥs0#tf^}¿g &S ?$pBf #́6kj]U54gyAAq9f|.wѣ9SNNNl,^]? yn?}O|[hѢEMtth50a ?y/Oy̘>=22PأRzz=K3Bؘ>Ad8;2IeK 7y.i3 dp[@AAA\J:}kr9""" 4W/,/+S45iJ q29|S?~dž"/|PRoliQ55 _nM/Pf_._H$*ukWgJlkNL!~G>GdTets&-p,$L4gٵj7x~ H8==AAVtMJ]zV6-zz6=tD}͛6lޞnS)PD4,3Ҁ@ý|bwKrٛFAF!8Fe٨L>:gRBz=RIau$\z0 ܣ*AAlbm4V{{z- <|1gX'&'& |PѫTjU* .Hxb`\|`\-3botO( Bbp/=&GdT8;2Ie #K:nJHFq q  6Hd*D"H4)$'*0KIefUfg2IeKFAAL>aL ts&g2}ΟX4`M^}O 3lTStQn}6:yie1Uqc;hh,a M[aǎf%yyy4.T;w 2.=q?p\1{{\H斁$etꜦ6>{DEFgk2 IDATFe+N\n- `ΘL*' 㼆 $$$\0       >\8,a v{V ZK@ay@WW1 }4A1o/'.^T6E"wwС>O6>g?+.DdCS {51C g~om9%{ #   (QE>g2IefU g2IeKƐ0    B#E>g2IefU g2Ie 0K+#zi3G?U  kƔg2IefU g2IeK0KAAAA 0L>GL*3 ȨnL>GL*[Y8μ~8Co^/AAA0bLn}6*ӭΙTfFAQ @n}6*ӭΙTى<: ^ԤU*=="80 I< N B5iB22aAեJAX=Pn!& .BaGB? +fp~v@A1L>GL*3 ȨnL>GL*[Y hVSYnj LEb z{m7#|bۦkh9_S*Hucmv1D"%EEEEմU~%$d:AAq=C2lT[3̌> 2lT[3lf ##βsifdY\uՍ}ғbbmTK$ 6G 3R᎜ZX>Kں$$lν ,C(8㛅AAaC2lT[3̌> 2lT[3l f ##'c@aZ@D|@D9'7><{fJeK갱R+F@wk&PnO] 1^9~pF:٩9yWOq [ ~[u-W&Y      # 7xqKٱobJ<'f-pfZ٭9.`D~4#~EytV4W_;xi\<g{oz4ZLoOwvxd幏[A{{~TM.XN+Irѯ1k< hB8$Fji{ziΌp:4]IHX+I  n .+ǘ2lT[3̌> 2lT[3l.Djv>pM %Ƿ`Tx@MᲴUG'/;6;07:rici;?ܰ9-EmVvׁ[Qttt}Ǟ`6P;{H f:B(PN]S1DOp9ę  {D]JS7ow g++r2"|}Y"s_mh%IRHGYV\W̆ k[SS?[^WAAcLn}6*ӭΙTfFAQ @n}6*ӭΙT,a6nc[R6.,kE[EPYy+f <i/p<l/,&s~4oC'Xzjl]w6|ILO],19$z! z~…p¬?TIQV&c͢"RRnF,XIJo 9k7~_(]g%_paJ΋ڽ1ðSag5CW##|>ds '4iK$z,+++1?ݥT+)Asݡ0p;wqZ*/)qA4>K$jkY  l䝕wدNikK8^٩S(ʬO{A{ FqjZ:UvJk:N9Ծs>;N5` 62qco^aܥ̆fYqg޶|pMmWMt|v8 dd%9@9eٳYB7y&!tzP8Ԑ :.\qkCIxǷ $l4ƆV7V.JW_8$ N}uV'~1Y)81MLјsŊ__V6…釮^u"bD|sىm:GKKNp/_T xժ__PkJG,"\883hVQ!GsITإD-IOOz N]#}ڡm2F2P>qI_}۶n++8(nmC>eKplpAAclaϮ>1j8cT>oH$bƞO U* []8ĚL~ib jw_t'ۯƧm6y[cSM_pLt4՚GBڼR,@U*ťvO[(2#8{GT亂_L Z[mvŃo%g $8uM漕@ ]38C<'=(@eegsrN߸ѩT2QAA .+ǘ2lT[3̌> 2lT[3lC!&1*%M {x";{f\IjuekkRh耀W,xŵغY@FUuA3:ұ B|E~zʵ&L#"~}Ǝuʃ ESrc@wԳ /V|ysơ|6&Iʝ[,mW.$I>\[;:;p}<եK""tz?l9mbL?6:gM;plmxMj }gО'8ӴE1v,\mhh~ӊ]==[ϝ 8S3=SՆ6VTn߾vjlįFʹcDGk ^/ig2lT[3̌> 2lT[3lC!a3YaBB:uΉsʕ+XgpCRM(jWQїG9yrRh(eC yOJc ,zUs[@x嗎],5,gD)o&­EM-z迍FXw 8ŎBV=a/?{cloS/eDRrtfMg=0 vs6g˃ἲth^Ǯ_7յ2kQJxK,VUV-.t<'>>>86?e2J T?9ӟZe ;#WD) W[flɦodlz/;N;{w\b 6`b{ PECFҕ,$s3՝=3D$R*o&{SLӦ@eͦe2ReNg )|'"zmmJK[DiTA<8}: 46z,wZLLRPRxq2ooM((ό]XhscWׯfŭKZ/1!>>^ ;;s\4#,ϳfM<ޔ0H$:C%%ZZb<  !pj}ܝGdTwj}:*Z-w%TZZZ0Mwp?ޖPc5 ʼg$0Jc"w̒fYX&P-3Xn`droKwp ”u_\:V,!0a=67_If M[\ HYUQd{QQzwvv.c.S&&^5s 7Ѭ,6i%kMzi2A|gg*/䉧Y1n\BƍN٤dޝ[[\BCLX']bP|UKK xcŊ$C-ILlKx)2ץRQ'Фk--ͻ32;ό?3dfYkk'GEI$6:;h,G5$fp(k?^-Ic";'#㎉9wPȈr8<\A)` ۔]OGeWTv> eWQh;-A0=q"њT(mlTSl&O;񺴐 ʲWβ]s׸,8\pYI*<3) crT *dsHlW)/x9sX+w/vKb5x6 A`g?ЦMտ[d9?9L+>9*O%;;eV-6%iiOΙuمmm|'LJdzZ$ c ]_=PǑvE% &GF,(0W&ڨhQAo\yKK{5ܤ0ĬU&Y"PO'Gz&98_YyC_yS r7 sf4 ufaaɥA[BًĠ ?dzg44tU&yy-;vwaF*L/wQKDWcu=A0™|AAR"#%, 陙'NdX{պ%0T zWn=E& $Y~x˹s)Q(eV[s%?\BLEF55""}}%fYRhu)3iQJYngӿ;džo8yrrt;6 8QZs~~lTE33 (`%5~;UQG1F&t ?)r˚vRxlw&(Tw>QVfH7Fx8rAq } 6R7vy&isY0bD\j}:*Z-w{UhBytTv>ZNeKFKX {YYY,Kwww]<ܹsT*}7a„_~Ygcf.9fH03f?yKf6X Q:H3ou[]C{i؂k{LNa?6 J22}}v o cD†]O_vܖcI!~pbk޹ݶ-=,(_24d=i`˿cݮ,5+rfvmro>'2r(f1:Zc~}:*GAQV)wQJ@GeWT.{_|rsssgg'IA09s? ˿o_uHHN:HFF V\_x^RҬB8e,>_EY8"iJOd\eMs ,(6\YBfn?;4l?k${fGY^ |ZTYj΃_[2'p0Dٱnֲ6Tsx4G-[v[f\Mک,PAPnO3A V?xat-p/?hO$ՕiٜXG$b1mZˬʶ,7wvbN J>߯_h`_Qу+3>~ŋ챟ac( ֊\,aP,`N.1=|b>;11E&St&l&WٞoZK Hz8L BG{U*q` A^^y)|yo;q¦_#ʚE!eV(tMch9}AF.ZXSU }g:*Z-w%k./6K?~|vvvIIIUUVH$l6Z[[+**rGPPЕ+WƏb:::|||\WBb,* Xsׯ;QTZu4eYw59ۂfe͘OHYk IDAT8tbG;tZ&miԍYoݦ s*(>323c2 iCK>/ +rCQwCxh`X1.AA9;JzTm{x{L J?'+leO'ܰ=T"xo\ܖ-"dQC*OPk;ֲD?z,RV4A-,$E.v¹Q†:ݕz`Ë&\Ѭ,ؔi,l8BU?xHEAUհA #)WU(~DI?u;~*M:($&Ir(z1:Zc~}:*GAQV)wѽJ@GeWTdxeյzjHsԩz`2>>>%%% vUz]?.lF sniJqh VV7ǛꦌyGR T_J~7CC[jJ"QF5AW7R_^Hs4nI /W4o^/J`R6.a3/_d2iߑ#==O̝&~|^t)(H z(335$իټYˆS?c:ui>BGpAScbX goq)2oڎqqplX Z[ /d>>BWmmm454tׅzۘx]]v .ẎSAvQ0ӿ<7//{V H<6=)<<9:$4t]aaƊqs'N'lhm+*:[Rb7IIMMI;WkM:Zc~}:*GAQkV) &gI}\\tTv>ZNe K26rRrw}_xf q===h,EQך /LyOsc/ hB +5_#H",hR kTw''@3Z{`3/ʼ,rby{YzFs=.`.-=zǚ+p8t3Hju(% O8%lLYVE¤7WzoՔlCV$ 'N(K yiٲm 2D ĬxVhNQ ] ryNaJegoD$s'KK;;<=o7#GƇ"`eN߮X;6$4[Ңy 2P$lh4㮌 avGA!L$6 ,  V8K${.2(hYn߸IHۗ[XYi XflGE[,ϳJ$olܨRk, l4FGqLܯOGe#2UPv>]SْvBbŊ:_____u=çO6l}bt:@ 0p8kju ;0Et)Ecd1͞jn(fPcLjOAYK7q=+,oHY G]xdkLNV+\3%ftnZ@LSWl3n|}~{|s^lZ[mH|wY ΑĊv͙'Lw{ 93!(Kk6_cWMJx:{ڒȭgrYGg͚Ǚ3Lᄝ/bK<ei17.TԔ2%:ÇԷ̮L뜒LIO ;_Y9@ DЪP s3&եT./VkHX:;Kr1;{\D䖔 ~~|>5oif55,>+)R8L*5a3^^P2fAy? `ΝϜ>{S6yJƢ"G%K3Sd':kkB|}uK{r گs_FTpwK(a0kfh1:eWQh;ݣ Ȩ5?m/* J@GeWT.`6d2 +˫8h HasR-)9?')龩SgǗ56JP99CJu_ c][vwx{VdjzuGtKMٙ_IgOw|0IODF1Eͬ?ObW[ Y 8~8{$ٕ{߂ۆ$rBuu5A&(0)l|o_l@@RpӧWVTU (BOoyx<:kCMAk'wtPfvv\``DϥK3yo/a-ER센tXœvo[VRd\q؀&,7`x .lq֡P㧟޾1²eYAXV J334k]%bcWt3g4H7#Iv`mi  Ip.\-=%,.7lD?SN CqPgϞԄm9mXzF.}rQ=*\J/*bq[Sкu.Z6) X*dzVKӽc$=g,p(m_!Q6Tx-AھvwAFc1Apkwlr/Fp8aRۮP{{78Q^A_L@ [Z.s=ۣV@I]]yÍwpZ xlvKwYгSH +2"(--.0PV/7R;j5L  lW(/ # 31< fSN={:0+&߶]kt g># Po(/O6ӯ[WvR.OL 3}2Z~C*E%|%K]̝;7..n޽:v޽dɒ^xxzzs=}}}[nU* m !Nyd?t}}xxbpD$h64vwiYeb:$[[ve3D!L m5;>v|CbXYFR{ޯLg6&`߾OHɝz~W1IpxBL;g9E}6ć6$I AY!.%>6 ik)/o%7۳'K(5u|kڦߖkk `_ZzEDԴl_wt<~T,Dw|oϞp__>_׭R5vv*T*֮Lo+;;K ГdJ(/'[( LfZ]ZnmDzTvB|i-ݏcvΌNL<^sWWQm-&>ؓ7_ ܇G k $d".U\W'Iɓʁ! 2ܠR^^>eʔovժU7q8#ŧ$ZZ<<<&'Z[]l7㘸_GdTUG@aF*]Sz^'|O>$w޽q^ j믿3ϼjXL݈Jͯtg8y2@|򪞮e2n0*'˿wW}`T $=m>m Әӛ),>_{l3n,K#})ӂE+ϞcS[V[0ھb.o'IvwS>ǣR0l?jĎ厞{*X"ut(*mn@VV?cPXֶY%UZR\i 8 SLRjlhz?^UhD1|r#ww@DoGvY曾/˴Eߓh9}AFY}|ݩF*]SzcbXTvuurRMDyyg.b۵ZmKK AV>4F#vtA:)zek ,R}ꕂI #p$)ekkoOO{U`l0$kA_t_)f18[ &#%VlX򮺺Zt?0+0i|'c81F8; Ϫu4A6o /뮏^xnH(&b d?&7~b[_=<{kI=vM:Zc~}:*GAQV)ŝjtR;;Qh;-K B(--ǻYWWge0AAA޵oќ95O?6|%Ottz{;|M ɩ~?<q?kGͦFttTv> R^xm;ը^wvrw*[2\󣢢T*^p8,ր~xHZM$e0Ng1Vu32"#I^ڪRKgI QA"hԉW0$r_PIzސB__J}@O c!W0X,.q@Fx]ق _lW(Z[w?ofԔqvQ~e?2  ܒƟzՍFFFGF|=-AѼg?Gydܹ**tzS/Bܳ7).nI=?С軭-1q>ݣ Ȩ5sWTZ?\\tTv>ZNe FK؀_ hlx.!:wУ1ѩ6s`48 b8?\ab-˘nbxa(`%soqgGzQ?w- 2L'ƐҫR/}ٳgٽpyiOc(x_ףo6ujTdA_@B^7$$̝;7s۶Fk4!8&ק{Unw)ZʮGݩlɈr #22v6AAAaŗ#򮮴UujBQLie5w>|u[ +WbqV[بU**#$ ǎeZ[3xMb30E_NtTv>ZNe#2UPv>]Sق޵kYŋo%Ý~]@Іf2=q@Abyyc3:qRynNwsSkE$*QCoD~ baOk"/01-/?׽41ttTv> Rx|~Jqj}ܝʖ /0b'#`:6@ ^Wad 8+jGYᑼl1/6/6ά3j &r I "N3esIضl4FGqLܯOGe#2p*E5}N*ʮ֧rw*[0\l/#mn6 # Aq*.}^\}ch9}AF.2l\AtTv>ZNeKKAAA\AxX(*K?m8&Uv>ZnJ| NW62}:*Ɯc:}W:9}pzcݣOGeWQtիk8ce ]+\%LKTjpڀ6 7^Wadp~֛N.חo]R]48&NB w2޴E g3ptFݟ%JAĥ;]tEAAAAAAAdQ´랻o Gp6 dו{<2zqS:>V Џ_p8Q5lP(ul@t+]tĝdsqLܨRptׄ*/{t #   2!Iҥ.ҧ{Adi:&tTFAAA?t>ݣ (]$K122%LKFC1á h0l@Fx]1# \0JmGAh k.QAFFAAAdXQ2nSv> BSp^w,MDŽ2@0 Îso]fpz=kڵ AAA_M= MyRE4:*#(]d67J]o/[9 IDAT/# |,l r9O"X 4i6ڠhD"i"AU b 0K*]「 BSp^w,MDŽ2@0B:Jzma|_ @oKF~IDst Kz₄Rؐs".uv;EPmRTTDdrrYIk.]b0IIIf>W   ()GA)8QHcBGee /]R/^|K,A qis ,R}խc=\mRoS m)Lk?&%BBVX2`w%tZ#cFAAF`۔ݣ (]$K126KALixT@WS*.\RTG,(zZ{*oGJ&3y̮=DvjZ#7N]OҌSS*犗=pm?sWxMx6>E4F07:z{o!Jj4:{'-޿7E`Dfl, 45u؝$A n6e# 45JqtL訌 FNGI'gWږ!GVbD$-ӻG<ᛕ`95%rrޟ{-{3$s 9|۽8a\.=OC-:_CbF Y7 Ip6w_OG-?ȆOd1 +[ZߴM3BvTnn>>Z^mlb0JƞΜ<--[:>;wnZ}uAq8mGAh k.QAFհL3dڮȳcy5ߐ*}wy?@4okXBMf2L;OHq[sF/'7:~F0z{ʳ װp=t[ pUV޴|'|~b`T$b2 zk%XM)2kίv[( qNߜ:  dܦ}AF)"Y d.az;.l̲Ԝ9/C;Ǩ҅ΊHD33*r6iɳl@1gr3ծ 6X*/z=o-_SA#C'F11&T z… `…HCI@Cl3,**"I29|OȚ}0A~[>Odeݞnkm;UQag{eI7̈p; Xg-\09yŋog+W Q`矷tw`WdDF~݁&yϚ5+!5ЍݥT,)AfOChd>,-HJCJJм LW|# `۔ݣ (]$K126슄CF'}l뢻)ˆ’% o^VpUT*s._%% Nw^.'  uDHO̞m;lϥKG^L?.@qJumeqisM՗ϴ5W)|oʒ\8>mN}Yw`6)e6MZ֪_r+۔)^-}/ %MsA凛f0m `|H yy?_`wx G^]izsWtD=.䨨'f }ݔڱ2M]QֶφOsҤPv{w::bgf ד}cŊ/杽~StYL̵k]ն  `qtGAh k.QAF'J'gϞ -1(RMѨ6˼y<^Cg{.LU]Y]?}.(.GHz}UҭGҧ@YO_qwtC6h/(8~ߔ/;iMYdmUg<`^V3F͔:Ç}AAn6e# 45JqtL訌 t ӒU$I)*ر;&LH "V=-&ݿ_9aFI^KYڥKϛaLC@W7$Vܪ 6:;*vJo'²x/~Y7e M"1)"c MGmp3a_Idgs6l mY"h>}ڞsH J={b1'11\*eD]G񲲜˗#zIgٷĠns~8{ӻ6)@"u)iKM˗˚MdDEdJgR<;'N]/E2"+>>3.. ڎd76'ӆ xptA\ilA)w;%R4pp㨸SDQG*k--r^E'\z[ZC( ʼg$0Jc"w̒fYX&P-3Xn`droKwp ”u_\:V,!0a=67_Iצl.b,d b5j[gg*/Y3gŸq #7twiywF\om5s ]3qb\nt ym͕Y"\pLNEպDvr7#(a|P/~N*1}׭t_^%&'"‡- RӔrͽf'?־ V(gd۲Źf{{wX3te۸ZA).=ߘr}:*#hcxɤ46S,G ҩ'[VWzwZ+ qy:k9SX.O nJee;Z@ǎwҥ11B.W,n힋 .P |^dHH\ pŷ -%^aHw3-߭R}|w1%&H_==]ǎFd0&FD+*r%tgmaa5fG@g[Vҥ.RRzk&`Гe]*6<9{dÄwu˹s{ 7NJqq.PhS(*q} 5E+pnjH\3}A.o ;,LI2Vyl֬0DәmGo|&A |2< b3bEnٖdxqc'JYc_}衆/h83DAyέe;/w22Kcʼn[7q?][Z&e&x^ χI@TU[JSm(v'эY<^Og;uGQjhɃk;:비@꣗ xe+k %dWQ16,,K**MM-m~KDžr>ȵ qm[FXb!dZ40ȻWVj4|'B*ס+_4Z- 2tjlp26wf3qp0&Y ][>!>pײrC.=9!<\fXN S.9 {fFˍGL#)8 -6vpЭR?9g r._汯Xlp?vl`HqZH lq'N]e_&xԚ'Uu;<<|4g)$ juW7N9GAhfA;|Fg%L }z:*#hcx;qxA.^t}Rk)#K&U4ΠҨzYbkf[Kg L4l "7|q(+ṙOY*Ky0u벷S$Vwөm "gǺY:FdVP_l6Vd9iRbp-XV!Vi4"ᘦzNe|[R -hQ( ( H<=mg 1f=R>,G^B$'i# {eҲֿ=ve,# haeVe[ۇ~;;1Szydyׯ7lLE eeO1kE\eY^f(X֍ 0D'Ԙz|>\oi1M"urrHD6٫l7-%J$PP]=R߀ H`._U4jN M?`rQKUR zj4 C{PlKNJ.bq;~ }#AZy-G22%LO{6#6-)qfR||ikse0˪91bSUqŎF |}+swLFlӨ+/޺Ml =%ITP|fo{egdZ䇖$,|l_&W3܃ee09;e̽AwPyΜ%|)Ϝ z,onR*ۛkxlrbG'-)=ZaXV.ySnk#r.iHUj=#F $ݢkpZR_olxq$b=maapJ5#ÖPPU5b߀ 1;B(ĉ'*䶕@QQQRR/wXG8wW D`:ظb\pĩ{r]˥'K.Υ;{+n`ӋM/PAe X#fgg]V;xggf&;&s/aKwmNk>Tj3/@_oNaXv)yHVB!ƨEys[d0;'2qX2B7FC"|) IDAT%-AwזRw? 73\.^nP4f<gRd-ԖyF`kז |FACE>:Gç-&L`^'1_lQ^bKx$Շ/3;# FlU xhP] qFH_=+%Cg] D(tX 0tzCgq|W6-@tp}GX!ШXzoY`2pV$n/H Pk4eڻJ.\jjqDKGgT@xV+ٝuR>f X,޻lji@@[W/MQ|.w!<йs*+s xPL"89u-.>tX %#%;`2]¸А`;멆gϕ/a,>4t~rT"+K>*BȢiAdTTL*qX-r|1iqxM'% ɤȹ|y*ɼJ,v oxD\[re"O6>K2!ifpix=$]W叽OdؒZKVqHX3A*>YJn|R`G7$0^eEP=U%AQL0Lr,qCa!mrD(~r>GwG kZE_1JnS*'pQ*8CeeM]]^"ʤM惕Mzͬ%VM}[Z|b`@L:&=JMV/`O1)$ @!n[h[h(|`p8!3[n5_|Ϣ}NY1g+W{…I0}݂VKgg<w\yʕl+9y˟n~J"[`oaƑx|jTq q0笏 wqaߙ4߷xmfW%ӧ1S0v+#Q|۬YzIE~gLgok glKxBAJ tsp־]JwK -wv aK }o[4H؃l"ljŅ_D$5I2[L>YppoNwPPYc(Uug)xfo9<ra&e_0RB<=##xia$ᢢqq<޲m_7mIz:VRbcxl%+fK$UWNz6~,fJwJ绖{b kB13BB{/$ ue=cFb@@$0$B!:| Wf2Yv26wu54Dy{ߖyfo@8RT40Lvp^hŢ{sӟ7b]=TXhRl\f\ZBȚ!)j ),RYYSR,uqדZvS~ ;q8SR""6>|+W\Huo` ;vuwY/+̌yvw~ N޸sߗϙt6zF-.BAowwUuu@^TPr%t'.\XaCcu nYO?YYUǟ\j ox&'kFED?'?8M~ s>ݒy(kj?<Νrz*e$76I3CC|-,C' TV=!WP \r'/cO^e>=SY3_"j{O; =ֹULuꢇ|Bĺn;ů%:iW@i:'B~f^>3hƀ;_x2 'NoqEuoG]: jjjym-a>疞NJ֭ }m۶w?Vxf֞XLTW#9ß8ر+Wz8;n܄B!]V֗G Vf9-!ܜ5=~b*[DD#56ٌ}740// Z-t@!}DEfv pz#vrrqv 0(ɼ,veMm_HPБ#G\y|J<)' 6+z׷ɼTZ>3.nL3`ijנ )\n~qq\d䌨=99> zM`JK@I0p~C\goisXLfNa{*+9Bwfo 99fyqB!sc"-Zp8`-À1=9gw_  }$$)Izȷ2C濐KccimCFsv]_,ÿ}`v72^p.`O}}%rgGV(F!<ݾٽgeriHSn=G)ٙfӯkV./"3|Bݏn11zcyh↎GSxlv.W{etWS35(Hhwzuժh'.F{^p)l&Ő'zb/>Wϝg sBmV(l9Y?ɹ#5UW47{RS ߩ=YVf (IapTV[{;^XX@kOgI$ϞaB!{{c+Νb0xp8FaL+t+ ;6Z6)zMznMKkLxxxxx_L'תN3g٪[Z"####7CHl#Y wi@UU^^+RzT*zٙg Y{{csv"řc2] WG}sEx{;q Eq}I`Cŭ;ssw]Ɨ~IxX>q;;& ]Je0UVUM8֫V,,jGYHkOOkOyZ-4ðZ-kwvuT_@AhbrLe华9s\99Ti4\1IB!;|8㇃zmD'{5]Jy^J#E`2@R'$IQN#e;t{j]NgݽUHd%qPu ( ,KQtlU*;{ȉ-cr8,Z@bSk!hL[dnϒ`4 ^ܦ%_ˑ aԔ6וt q8iLl`%Xek]Q L!%.A#Ō@^RIͧ%Dh_obX:dا?bupוu V͋^3u*޽:Kه090BvTd~NB!4 k&:slڽ-Zd2@pÒP޸j!^wGy$"Q\XZ.|yJHmVXX`2l<'92_:xHaLIKr5z~hI-!26F-2PEQY2B7޽{IʲenH$nm;GZ=9\P ryZZUvs˖ηa`'qM9v׈KTxlO@ ,.'0I>}~kO8y@^Wօzzy)t#=90ٌ q<{ V2F!p pYaGwiU*g4`,drlG'OQ#C+&%3sJH-[{{"14?at7nܨof|ĵW47@"w䜄G* "1""2(HO>njj&=MX?AVg??c/.m?۷=m'fD!dضp"N=%#tq.alIR qG|645{֍L;IR:] ip룆Mt.:Ie;9(a4~𺲮>eM_ܿtҥKvvj5G xR!Imjjzz>t^?ݲYrzO~t׬Yu-t΂77l8uVBk52@Qx/e̙tB"rN٘̏G 27F-a"ǫdn6%B0ot!B!0/\ [uuu\Ronme~yW@c(~ϽJ'{V+|~u}}WGgwSFdX~S9Z{݄m EckUA2)SKW !b\ƉusNORw}+B!sc"oܾ_~5i&FwUx]q'Q BvfúqaXקTjU* &HÐ;*JbcYM6DGקQ(:`u}(eW^el>ߖ@rqGXoLZgSf bq9^ 2\ >VB!ƮE==Mvt '6nLZd\Kx&yGBGx<gQ8aX Ȧ 'gaLXzmG hZd;7EV jqxB!%WxNƭ<<'[X˻ >oȜ'b5v|b]S>F>%u."Fg6X_'~x[pWY IDATd4cb'{x!Pgɴ;"!OHؕ8%Os2F%SL;?Ǩdy->fDR-X6m7Ej!B!B!B37W5Jx݃l]lyP(lhhdQNgggood&jB!B!B!a-BX]# 9K/tʕ˗/^`uuu;\zKKK't$x{c-4u5>&yGB!B!d_!p.aADEEKOO_v^ߺuMBP(,b@oo/t:1B!B!B!.|M(//f͚,NL&S|:?͆7J4 v #ξ󎺫k<#{59b_Q\!B!B!v #dK؀UVV⋿{jjB!$Ig^Ip!PzURU(3y<,JSwvr%V)iF 00uwk@]tb 0I|w\HHHzzc@*>}p #0<~aԔ6וn% }li*^$`OY1B__[vi/MfI%lnρc@]ddMB!B!BM 8J!]˖-cժUw^$Z}.]+lHt=t͛7+J$Y,֍ )/q?w$__xHӻJ1hʶ U>! B)@o[@?IB ˵WuV,8s#^'4Ck4-GN{Zn;垞J2͚4=׽1$C÷O{UP:;;`jѺӉ}nϿ$].)3@Α}?L?S>dI."n~]ߗ65IϖO̝">_Yuh…j_}cA!B!1nbw J2X,@ 0ꪯh4@7~Nh,4: y|\\R>өP\k먚g̺k4ww9g(#k4-OM_w-殑'dh{iϭz˟ƛ!Lncf֌EpvA$ gc6xSO7x:w+i!DOc bA V#huz-$95L ٞΣ-{y&Iu۶UUC旊D\+;xQRP IZ]v!li%AL^ɭ# p5 wԍÍB!B!4* !{]V)b2l6$ 3p8[=KbbdIcg_~i1HEW p裃jhr]^ 狶HT]UQ) A -ٶ$> 8s{3m<+Qxlǟrׯ1|oi i݇{y8Bj^_d ,Ydmm(S{hC΂bb/^sz]+ ;qK&xϤLL%ꗷl9UYi!%Kx}l)5kɰ[zzW:lH :{04N==o޼Hf[HJСCEEv~ Μ)j۷?w{B!BaQ$y<7ryh&I-- 3x72KJLddCgʉ}eٲQQpK$+?ZB!B!4zXd2F3']M҉[ sil [GYyxBZ3mWWp` ŜSDrБL@U*{ Y?xpkw% w_!I-fşy,zȂXؐ C7 $ɏ7g>4"ȍ[=/O֭sn2N=̛b5ҦaqS\>nk[g.s4?WWr'z$v#";:2͏Np℞[Wd/ed\ҥTKzz<I|7:B!BGwiK?<2XKx&)˖MA2;ӳ # WnkXɜ?>}[ϟψJSv<~KsBk{\U;K Ggb1,DKHȰbL]t ijyvOK"Y \km~XhK6}vѐI1q׿b !$I>WLk+::qt\1_^W=![ bd'{ou[װOm_<{JG04f@iS'gV(F| 02=ӧ@iSǎћ*v<6{uR'Fz`250PzCB!B!F.GO/ ͱFvQ([PRR)K|zjƮ. R)=|wJWYRŢ}2R>>3%7skW7L}UVf UXF0:-ussxnh۱Xk/o,淉ԌAn l,bؗydFGGX 1PCașb^io?RRr$I  Kն+(PSf2焅M  ƮY&p̥ӂ<JkFGnk&ۻGM99nK% J Pxp,,..z8OjppL֥TnɡSy;N/% "=""-<[,ee) IBԈ$A45+-5:+44[|w}}i#2zDa+?x sUUӃ熇o]pb||| 2KJO$7?_nɉ˝/ .CEEC!B!r  _G{DDX]8&xL4v2lz" "xY&z1+hi􋴸0o@ϼ {H|gWъ"zU` &# QGV 6V,?Y)vϷߞߦ'ͻ$(kW,KTxD^h􋎐J3WWm\6ύ&OYs(E 0d ܳǐ( ׯ[?p֩r9v O^ߵTE"ek[`uRR\wߵj&qwj*\im5NcNCKg>_]m%,Z:G a ,-ۭT:q23ۭRY IOQ~~>>[Z{7NM6xl3z2!dź ;b&sOiDJ;.\{INt*;yҐeIM]7u*{ AA69B!B!]q `HKjwf-j\<=8xUR&:}fHH3/oPU(ż5%9L/k52.%§fobs^ZI\bk[V`= K\2ůqak9|ڤhXG810p~ڼ=wPzUKjI<;zj1#$d~TTk>B.ӻ/] -7'N77۽FRYv ׶U7 +#D w/KK:G_wdwdtk&W G F_ DD굵֛9yo=Ѫ64wvSat==4{H7o>?PQQ^M/ɖ]MV>1dcB.xY߶n51/6t O _rr~>sN)on6M)dP6l ʱ iil,ّ#@!;;Lp!!20/>UV577ڤS_dH$ t@_*ϼvd.|/6FLr#G H"w ?&pʛ)\kwK$r={ ZGB!B!dԍx]Ȳ'o13O6r|)g]W ͸mO=;e[hQd˧cc?1nrJ<<۟w@ޢm SB!%6i;M#JJ26֥TX4et]Jevet M(a -&KT,~f|]UUi0}_imŨ?e^oJ>; 愇 ٳ{ 7ʫWk}]]焇w @\^9pb@T|6z(.Xl?kIl,||a'ͣ'x矍_sM{{Go#G!B!sxy=1qRӍeLM<%<1evGTVJ[)itSqb>V&&V3zp?ڍ0\YM\V$4ZҘO:&)wh&ny蕷3<[w'D۳l7%Rjye ok_P0:|raP(JLL4NaEĘ4I?:OH\iuΟ Zjy"R Gw?R\#ψkev ,|!=rATXZS"e,[FcyBcJe>tj,5mqAgf1R)TV|&R?fgHL\4e ts˒%KbbkWcg'mKY,ˉ~~pzGeb]-hcF$f;9QLҟ]$k;9/88phB!B96Q/YhwY?u .B&]]Rf9m}3f(駒F f=ecv݀i-fQ*fŭګS-nS)R'kZ:S tl%րQI/8]Y7NЮfČ)\ՙ1y>OTwRݐ1Pfyb1|E=Ev~`<6ۤ:݉3t@{IIYee[ϟ-׶5+/-Rn 2 L枞A5Tֶώ̙R8V&[ذA JKc3i{/^Gw?(/%~ VoO\M$yyѣkkx<WZZGEdjL(`(g~ 8BCB!B!d.% Zקkx,g--<-Jˋ !G]}G"<8{6|zPIC)XIɹ+W;xIɨ0V}żh o$ѷ?Wxfsޟ熦Y+ɝ/5Nu$1QwX$_.$;,i0?i4kez'2KV(ڰaUrdoxnD܈ӕoӆo9U~jekg`"^aVw4ҧ+VK]]UUIKcbP T+W,5v <6A& rVk% [c||F&t);ː9.TWa_+חW] B!B!%&JڕUyH[EDGG꫻w61{\c.ad̐.jNdݖ AwזRw? 73\.^nP4f<gRdd/ Zj˼Gk~>#ݠ" yGOF IDAT|/XUl%jwƙb0z!Pa\B!B!ñY`2̗#dKLLӓ]Pp47!*U0c(Bv OLcxDF} .N<̔ .͞UpKwG㓓:~΢>!Q`xljw78xbɦޫkZ?QpPC^ήS3f,{|&?X]r(8x1H6)*$.d" Ycn11y&%9Ň ]Dңۤ-[˯[ i41)1̪ź9Ϭ?_ gc`mǦzXa27 bbXINZP-gy܄A?[_V/ (U `]ȃ3  U67,4,*ĀDG2T`AIBh :~ٳpK\\zd$V*OWT0 26wu54mƛbqR` )*y* tww^ɤ{sӟ7b]=TXhR=q"$}bjiqe!B!BG w|jJPPqUcozͭw}>/oJPп|B?pz6&+mFGt*+m993UNVsdXml#Aom]~[}pwl-oh*FS> 0{!%h`5[z1zQ̪41 (jvϑWϻA]ѣT,~kZ |]][N4oYzdW$H}'rG7xߞ~ypȘ24,pQ?h63ͦXhә3k.a g ך5̘bڵc%%ƛv]УR 8g32pa.+ B==ȠE xuJ&IgKhDdt톁1 ~~rZ[`fhht Ğ"'KLԎ4;7B!B!z` SŅ(?~[vs;::cZ>)DhrÉ'18?paO<_SHB<=@}|BG\v]rɶέjdfbS=bc-glt?3Qy⴫W4p T^?3/n|{^4æaY A8q~9)*}?)EuOinPSSCDGGIh,Ϲ˗:r>[$I?77@׿sgڢ8A[d6tt$-|ȯ)Y-/Km 4=8xG+WWU%ܚ0'" ::`K¼V,OJEϿC.on/-YEIy E\%NNut8^3gLKڑkz7zh*-ݓwK|S.n twX[;w~@Yc85].o/$A<{KzlFL LfIjd  B!BYacu-szrl?z,wD"KNኜ:ڵ,\-àr<%lٲfӭrcR85z+WG9#ӓs?+p -gh#!LIGƧ2_O#lr7zSOb\G >od"X^(J0y}rs0 0|?.uΜsڥݵ%2ٷOgƊ\ zw^`~n_co67#ۤӽx<wkw)+BBjT`r1R-)k.Xűc!BᣳfEj!B!B9)?; "0 J[ZhLf4&j; 66V"8*cs #eC>ÇT8r#}} F\^h7! '/yH2uW.> 'Ӗ.AyϞ"+Ss 3Y&u4*ek]_7 ⣽fϞyhKםEh 7.k{-_w4́@$G f_ f ^dU(}ecA/dVa['o QTu[[Cz{._c>`G~|-gn9{mT_{p8<6[_Rڪu_߯ǿCڶ-->FV(:; b7/vwoQTBaAUnW<^eh=JeM[[]{{=ңPK< _={ Q~̙8ȍn*&X+3|cM'OKE>XPp8N$XmRzEϜB!B!lC,&+{Зw@AK!gCh----#݀s(aRm¼=M9vO^K! 2N2nP=bVXu`&hoCpѧv|!nP ,|&c1a5PZ ^4g3Gŭ[N]'#hˇۼ\єZZavJ؆==6huzzPXqp8Ryb;;£g{x5bŏS՗LJB!B!nk񀱎W  C`흝 &'9\64{Zj{խ^<'8b֚ҒGc'xOK"61DajM.+:{?J^G{IQ^:4e2I [{ XbM}}A3<G#əѷ9qq+N>:p@:k [!Z9u*;tH3NC!B! *Ҳԉ/\d\T :\|38$<6{߶5=,=,yv8{TڮTZn1XA>ٶT&8B!G(~;k36L{8b<<]n'0)^laF!;Q~~^m:7Ѯσlڜv<;#B!B9-|{HxWnK7w+UfM<=)I~O@g1BN $x{PMJ)=bYy7'(iA 8Ď9̘Zy .f1(H$Et:} `6}@GQ$AaZ"tMv~@+krUGTZֶ%[5yP(oj!h>B!B!eSuMM|yxGo+Bg_<_:\`:@҆C gF}C|@$ն:.|gְbsPn8EIf,a4rߍ>\ӊGq B!B9f SwOoimyasKu[lcWS#A^r28$r:^{mS@!B!B! +Pzէ[~QtU FLHdrwl9\`:34s-zϮ B!B!}/aHIMMi9\/ap񂐐lQ+!1^  9 ׃p~vV B!Bٙ { Gg믲@$D.RIg&x|2Eș^q&`\W#55ZB!B!dWvrsc+Bc #B!G*e|dd#;:>f>,Fvt1c1Ȏ?#;:ܡcOrGGvt|%%%vs ۷Ϥdɒ%S?0) 34s-zϮ t(_~D(9%a}}[ZVu#;0gwhm'OBCȎm'xddG;|$#C5 뉣}2pnEs?M|&c592A!B!B!B!4k0 F;|v0r@𣏕PǺu@_^wDXT!BhgB[SR"Bh!1>! M0gFkk™B!τ!pH!B8#!B3!B!+F9 ァ^ght:@0׆B C!BLB &x|(NҢ52й\:%Hrr?(V)!+iӆ(蛃KR|B ,/7!& .Tj}52\]N  D!BLBq QV.WBc9L/YkJ^u W 4JgqsOL oZҠNl=pPb#uwwR`Rׯ$gV:"aBg B!|&D!xCȩuVTt\.I73'8b֚{=Sb="#Yc~~ӟ#h#f&m9E\Pp;~\^0Xuky%&˟HQԂe\Rt2] 0 W2_<Kժ4 r;d gx>$h J;\v% "!B B!4^9א0Bzu̘;AodQEYO?_ N;r'+7kzDF3G>< ,Bcw={]ΩH$8pm U/}x$i ޞ.J)W |)hI5^l-F͚A8(ۏWN+t$*A\ׇ+W?ysiSЂ .}vΜeE l>!ǹ;){֯\F.B!!Bs Ơ@W$`OOHku:Gg|*o5C FRZ,jk?%C6_Fx"}fĦbzbLњDpFB!gBBW5$leE ZQ",VH ߅ << %===v_:+*="l_/"QTJSuQ Ȉ!`Кgx0(2}co7æs' v=Ö?~kbF70ZQDȔx{{8ÿ.rt-EN4RA =NCBn/\pYOL3WV?c3gGG d*UnIɆ~kde2 ="BUh~ hѢ׮-_"-4J_~}C}Wbw<8Ν;iɧ%#Bb\w8ȠўZLmTBѣGpy?>cSKNj?pFB!gBB[C04SwB+K覅bEG\~6ԓw/JH\?}$L_*:7n'jsyM߬Qyt_^pNn/)q"uƪ)&V9a rkZ8uVԜG^3-mzf9^麥rʧL9'NjXn4n3†%$Mo!ukU[Nl_A ۖt:}蹬,koXa0|P4n=E 4gNMM5%|y) Ҧ'OKᩧ8Liim .:d'L>;v)^.B!!B IC{V(&QuS2%60-)8zʋ]]"Ԉ7$l~QJSG[_y_rfJxAA9wliT'8ZҦVQz.˙G-W=Аްtv|䟉N󜗖6$bsl#9=älIt^ߍo-.JpFB!gBBW8$<(7lmm^ӝ0kD(Z[B&Y<-n*g< hn;\w/~aqV2iiEAedD$Z[C+^ZeG0dڤIۋ?|T`k>nٱ$A]l`O' ba|<T7jќo'2cv\d(tcHOKՆ9'…&⃂%$-.N "QN\w79Y {&NX? 2٬,hg鑑;/^hսwٛ7ef&^7޴6k]a84ƫjk3N Y=ujDbx}{h omtr76;Q$rg +QTjHH\PPekq- E"WkjL]ˈ  4vXtgehƅ&N}ʿx֊ɓE3g lN5=="d~|}3wB8#!B3!B!-8xM[wCus#t}vg ٹZ[SOڒ1Ss1Y\iOOOZ*eBb4Uڇ4C:,tԪ{[IĀ76N ::Nֈ :kyj>;B"⻳g%Rݓ*.޺uMT*r O+&=|eK䐐GfswOlܨ"ǃ/WWe2|g֥G,1vwRx{^5f'euIxoŊIWQ[YLyzΏ  M*ys)^4>UV6|~\aHY`ayZ lY%%05<$N;/j)r)j7rvYZ^'&ǃ8~|>>/ߞ91a3㏟.+}7#$##~P(HC}$/d*եJ O6 GHR o<$AċDCOefXO?T(*ox0Z[k̝5;U+sLy+W"yzv?4I!BYτ!\gHx͚5˗/JCO? ?A!;|&D!x"Cs]|D"(OOτ>}O?H$˗/233=:5i] KqESk48fvt:-3GVFWSCY<$=؃]8ft[<4(=vc6M>/yѷiR字 +x_$QB~\jCQr(*55ոdR`RӤ̡YPA ݳ֯liX?wgQvOOA"k(O V;=H WXYʢGW;z|EǬJ1T0: r,5mqe7$F  k C^޲|6;O-JHx{FD_OffN 2-SCB͛@#[4der-ΑbXL#ɸ (0[ؙb@BaRv< roֻܸ籏~8éS}'xZ5ʭ`/_iR~ƋAǼZJJ.~)|lȁBo |TjHpq1B!gBB[0$,|-[DDD655h4OOym%ɦM~ lڴ)88xL a2XYsC_}zڴ鑑j3D7a`~:Q7uY7TBţږtT =T+xE?AQ1|5a9=i/4K0OsoK%>?yALg ]ymaݽ~'O6X{l&[.g\h 67 0tl!ү!7~d՗lre[ۧG8U$_\_P -5_Wіr?^|>$Jnş9ٱ͘ $D#6R;XXDf&NϜ8k臟+/%+]ϖb~CR 's jkzp8Tg&DJxX&Sv]l\Yp~M{vٲG-k.^(+e1aa k޷RF? o  Kg6g% IG$B! B!4^9א}]dy!xW~_|(:ݤI***${```IIɓtzGGW^c})}uWǏJxGf뵵v%>qb]g՗swf69Ԑ{2}cL6E!S v0ZFgXiuдn5Gy¸U\-:?wcΉ/3O=is{^m,a?3|0>Adאulf ׷ߞ&>t-`6GPӻNaɚft:aR2+g W[ZcҥkϘ1;&&P x.+ጌuGz l1Yê?ط.Fٴ~O4R-(8ZXe2cbe[ϟ˷n-NH8PP3HP՗~ݡ3 &F&K%HXє44M qèE?[քO +Պ闤N,nB1ʔG-Q?lzÆ *eݻ i-¹^[  7 083mA`ܴISuh/>b AfgCcN}$ɯ?zFA0ߜ>=DGIB!/UWOԯhiۮ]>nn+N7-fdX*ͯtthThz7g$r/Bվ(:Imݓxŋiaa!!"OϺ11p\ ZvW֦BeZ/Xlߋ&@Q}@P&#ݽ뇼<ʁԴn07ў~غ?h((Wx8Fivuu5ѿ󗚜S8ʆ*v&%ׯ_fw]f*eae3gU)&y RRI tf o_Wv B!иbgxmgB>pL|&D!hq!a \|ƍ;::h4;f@&d2`0<==U*V5.3FdH_Xq#;^#o+8$ 1S%-EN No~04חE+q>/kM ~3[|Z]~٤\Rҧ?$H2zњe۟|6)+r-g6PE}OR@n5sP38|i(hi  " ٟ֮/=pꗏeQFʹsI 3.=k?]3_i4|!BȎL;3addr73!B!Fvmk֬ijj:rX,h4a`=V t:]0zgD1L}S}tl-D׬a-]]><QȈ+G=wWԜKɸl++G<"#dN(8ch?+?z$N$1ܒOZ:1lӔ-SNe1_'kB] fb7,n>sF?ڒ%b_O˽+)ilMT^7cW?w(ʋǣdC&ײ U47 L"Xs`)bH K ܒ7J@Tj(t;.\&Mb]r7ͯ`YYj|8@ H Cs8N׏?;w.V[{$X*PooB:I pui`RZL UV]=zbc}bb̫t~ym9s%K~sX?(T nޤ(Jw\ӗܽ'[Z_o<xCVggP:>Ό?>F>8z݆B!R!B LOOyᇟ|ɿo,_NIIQ**dAлC'Ayz΋{wժyēk2.]gq x$Xml#^dpm]~3&[knpEùxA2(o$bbId芝O|p^zQ=Yғ@PԬ;w[Ҳ uw_Nf>9qߗ/'}()lm,X1e^<~^PnG\Pad'/-X~zy_wk9k`4m[3c==;+W.HHMħM{ŊO4.{wJ'KJr[XksrXQLw4Do'$)U*vθ9t<έ6׻Ǻvs3p:~P^qS%$`^@ӿ9s&o}7s^^MM]uJ?tp֙Ç}m}7m|qZh{'~_G@ZXXÔV : +Wm6l&|!BN!Bs-=(<fO4(&NRSS/^XYYYSSd2 S&LR-E~uѢ'Qԉ7>>t]*uE=,1b}ՠ!Ǯ+^1ֵU!wh}'&x7zgOg4gĊyK8|EV]_`3"t-'eAKtw)* 3~חR%HL"a,~w4F{|lw Ο-Sx,KVW[3R Gkw}^Z$F uunڤ9gee)XVPoo tsNq?[Jnn/o=TtVZX))'N݆ ҥIIN4-$I{Vo~sc__E;;IY,C3'N̜8s%2YTnn}O~8Ld IDATԇ}J6wPX xtիw%':u^\\sggݳ{F]498F;ARiX $A^-4o r4g?|IGhXt7~~g*sX,ۛb7ٸ':+"\*- >wkv)HL o`s`„ j"u׷WU&gD)U۷  VB!P/|&D!и5_}UJUYYpl[oj*Z-&LuI =wJ1tZK.liR]}V,KfL?y&5[k/>pgB¬+ JLZ9`+?66{gO#F~f<6wg!7*\bfy+u嵝\xx8";;w|W9Qh4E5PAƓ'߼xffZx8$<< ju~EE짺퉯~bΜ\&S/tnC5F#U*,~Yʖ/˯Q?LPkEC8{{₂֮۫7o~~L:x o[w,^[_?9gN֤I\&3p]*կ ӧccE^^.W5J$ϟy[,WXChx6t/O?g~;ؤ0ExqrrL@Tqqڽ ,Vbp0uwqXPYvy'q>>  ($DKhùۗ3:#xxt: ]'L@謯$?uo, vB!τ!𐰞VeX:N&iZC2_~Yt믿d2=<<|AVc\N$hU1.>Ϗ_?֛yS<$JTغ+N~iKRѼgOW9Gљ,:ܯ^ll{g_eٳ_s8[h*Ƨ[K ܵ뽽{#\ƎCIOW_zxa>>.A(5mmuHB.X/\0~G:f/#hگrs9'-]]uu:;]ݤKyUYwy,SYYI+x,(8Q\'Y6F} gNAvmI]K LNz#QTk@)'EQt$Ih4eGN#BLb2QL*B!gBB[c~Hfr\d2wy}]d|IYYRtss G;Z̹zꢄKY\+z 4d=^d0[{.پD:yS@t5Zuc5np49G 'Gڵn&A֔6ו\=;{Z :g0Hc2Dy[]V t>'.Bb^RN2wLR=_nrXbM}}A3l^W[mmܸSR8g\ ʩS`ݡC ט`>ҍՍ#O=mmFΖpwS:;^SfN#At7*ZPpu\G!rLBq˥Y__V$@6=I m[BBk+oR0~1gÁwL ^laF!;Q~~^m:7Ѯσlڜv<;$?"E}+V_=y6f1!巷mNE!0e2L0`8s4?AF_tIl;B!gBB[.8$dRf#""J%EQ,$QI̎\A5#"""(N֦em@qޜI6rp;3cj歂t~(J"R%k/(t:QI`nx{-6!UURiu[KlY"A"ãC\#.^_z~z}7tz_rLLX?C -4 g)oMVZZ*LB!d>"Bhr!a3t7  qЍv! IJmu;]άa q琡p }:tsYh}տuu}bF?MM|rqI-5ힽ/\9rRYvAQNI$5-'%b"Bhr!ad#xt&`z3\W#55Z,g/NQ(]Z6:]Č桂c "Bhr!aB!8(2aBCZc0gwP'#1c1ȎdW.>bю?#;:ܡcOhdG31rII/CcbNa l9 ׃p~vV8s[v Cta}}[Z$/v<2SRA;Dqza!cr;.z[o, #Lhb,c~,XwE;|$#kHFvn A!B!B!B!$v C'&`\W#55Z1uFycn̒q,ST9"2B!g:o\HS#AaRեJ-7Fkk!B!B!s #dQGY^" 09\d*e{ձ3,_!743:jӴ+=1iI:1tLRv;A0m\RXXHQTBBIʯ_Nd\\IXV누B!tF!B!r&5$sΊE1ffG[ZS\WZcgJGdS57zz 3<Ͼxysu~p- Ã'IF{ $o3&C!8K!B!Bȩ8א0BƚNDxmJ5 Ism:y?s#+ץj& :=vm[Oűn!”mݓlᤜwgng :v7-.{ICJ}ۊkV=`_wni"Qҕ#!B!B!B!s!1G ؄}G\lc@6=- ;.9tVuO[(ttVQ֖߷#!fcr0|er?ݳyWUJ8啱5rBҧ>0>n'~y/\++¿r?Dp)ZlUYjFcRҸ$C&LCRk*fd.GѺ{ul 2I(inpٕ$B!B!  #gTȌy AQL6ޯlNn=~.XSաqKĎ#}I~Gd=sÓn?pL*8vC圊D"GNVY҇HpR(}e?~˗"VTidڬy@H7ÒzhWNmpPbv℄+޹ssC. Õ+ݟڼihAXt:IrʖΙ,9ۇv98w'%2~R GphB!B!CɌ ww(AV[/E"76rML8k>7e!GM^DV91 p['Y!O;wL; 2_\r&Onc ~ZN$ɼ<󍭚$CmZ?=`<$.֛@4o\ӇTl;ä5ZD&V(>tPH3$@ `[]!wqQ]{ 0tAT ,јcĴM6mo7&MbILt T63Ǔq3|?~ofȜL7$I0cq&ImUU{ywArzk;&X hz}FqЄ΃fg hT(aB!BKŽiP 8gZc=rs-#W$$kW*#G 2mS}Bge('6UvVTBC4+z0MǾZ0Ӯ,s#0ϰ~iR<|U Ò%K`ɒ%nxc E@<#G[PP@QT\܍JŋZ>čqi;t)E cb̪\ioϭ殮Aҏ &%Kr USmtXiA{;%lH 949{b޼y[]JᢢDbw(eɒFl[0&CaLWK^фB!B!ƕ CKmSlBqzk+ `3Y,wyaM ,[rr_\laL)/bqRW}wY€4w0?IALYsd R8;c 2tZzmNi@DSG`ѾcvUZq|0IAblؐb c_\|I\\]Bvyy\ީTf.hݺX&IukRiaCCLF8xb&7wsv+WK$ed~Ӥp(+LX;աl[K  M9Z;E%,h(/w :%s@3}#+;w Jb.Irtk SI`{KS(ܲcH#󪫟ݶ~$POυ112ftk>KJ|4#4y\|韻vY&!V~ 0 `G^5yas?90FCļy <{Zkc B!B!, ;FwV766~u3 ~~MΘMM_8ALR#SnSP4x^׮*q Cu{ISfd$2kX؀r$/FJθ?Rȁ+vM1عᡨՏlk􈹧"y|㱢M!; G[rISo}kEg;&l2S aa^3% rD6$c|}byҋ䗮zC `O#0(mjӎ] ŮR Sώ Gnk;V;㠥% W4phB!BqKipZ=VD?ư"Qd^l:dSWׅs##7oS ?jkWO])1"Iv֪V@'HqUYN:JVsົÊ]/k}fEc߶Id؈9FHp 4Ɍp}`~ rkrp ww ;:˳ >$ɓ#<=BiɊxu_nj?;utI  J-vaM`l5 [ZLNH:ʟΝ[y$AGEEF@M[ɲ2ÉBh촑*IΞMDISӉRc}}ZgÙ%quuܺiTJ5-(}_IMY%%͈I7vUnrrSCCB'.Shi9RTtM*JQ!B!B ymݞΏ$AIcɰde$f/i20=+=*wYXz:irojh ;cW^+cq2]J/x0PԔX?kReus]JJqQQx@Ϻ_*kRi~ IDATKWc&sc h].{սVM&qu)_>mlXܝ̾uÛ9Q!B!B }vGb"tUU @@}GٱzW`S)jߚsLo׾ΚZ[r!29r×;!\%lUw7dW`\9nZr suK#3^&E9x9"@mݞ{ ^{)u>FGHN\@f/~P %%A,=!aj`8Aom zm5wup//''@aXZcm. ٝo `uh{fٙ$V2F#qu](u>SQ:)M xo͚kꙊX?`WC0(oo3ge2OAɭjpL חo;wQy;;NJuqY>\ɓ=%jP^^PpÕv"IfpX B!B!o%={,[lL2=#46;n(4̴r~ʷ^4RXR [\[[KMBLy}>2嵡=~.^cprUbF08hCAc^7k?v:fo ox;%^itv^$A8qb.jksgrn̯1PK7gg $A n߮jƂArmmRpV/jh0.ҷ[O׃?9v짳g伿vmb-/ߜ>=#,LeÆSee\x,ʇPӕ%I]WNY71yg UMǎ ]g*+|Q &I$%GNys˗.q@XRwr8;v3Gq1jNˬT*--ۿqWȥz~ؙ8) -<{֬RE/mj2փiܑ >E$7uv4y^رcJ``m{RbPU5%zk6z0(]*#|H9'2zk|XR6'2Ҵ$mrys\zgWM>6d͝>..^f#Gwy|޼@X=uioVꟵ~"h:Y\*]ohb0AX ә B!BM$c5 &s]l&>'斾žL.WaQCۊ&WRLI.og8t[e zLA&~_sK +Z@QԔ)SL[l6EQqqqf=O1O4?5ק:kưVW;wRY.Exfʈ}aV`1$\S#uBVj4<6;T-SU_vM ֬ȑWE7Vk}BcJe9tjYki-&f1>>pּ&J{sǔ)&MrYdI\k2ի%Y,SLEwJ}wH1FA~~pbbg>*YS U\|wc''['F P߯X۲=pОƵ ~: do B!B!Bh_%ad!~p/f Ūnk{n6eFJ.F34,exڦ.cSwdu%p~MW}3miXB4A32zb.t_NLz0Ӝ/NNy{<1 v}9oC' J=+{gl `IΉx\]_7o .]w%'[M̉dz8{Su9N!^ G,|XݱGʹ/ã^g^)FTu':Xv+H{?x iaC[nm|#j_IYL'l}9XmNJ:u 'Eq oBi_=oGܙ-Y:mÒ1?{zolw˝lj/rGBbU,gj_tp䝑^HXӕ44{s`kNNsg=10.UW4[SWWz_nxg~噧X_BLO? KSRNguGLoD!B!`po@|6A&{X;~ qs3>چ77$9nҚR+D&ngzr\)AޡiN:j@iM)C<9A2A{eAnPq+|cD'{_Kkޟ].V#ݿjvpnCg  $I~ JP*ͦ ~..kj{W̙ϝCUsS -k]6BNУN86M`+8o;3nKh|\\ V*; P;.+k:ǧa'eq"3qmh|OJ |r l6hZ @e< r-ah{{"@cccky_P.SQ}T*50H2dæ-|矏 2mQ(z!B!BXvH#Ϗzkldj,jh}ʔ_uu NJE!ON wvLΰ'a&v ||6}MEX^~;Ymo89w>Q~zݛKn@}kaa;]by \9;{kuY ӧӏ ٥wL:+""P,u%^e!Zx6oRӣ=/_nbKm3qp=Vѣf4n.kruSUU *;)jO,Xpl&K̭oi,.3xz:x@kb\XWjkmݝM ?J~EEI+)-)+eW'G(|?F+*z+\.{KVYTpڿ|6򸈈 cϞB!B!4 o]Pښ[Y)r-v)z2#A*;$)W??BI# rv=NAnQv]n@LimyCS1?X2K s2-!3BWxGУOAQ=rި$IĔ,CiӾ|88RX;叹*Asό0dB@8P,6 ""LwtcoqǩDX??hE3z=񄧳`=2&M2@3lc;bu~:{x}Eqq AZuojicWӏOnʼt[p8OedB[s@g0ՙJ$э(jq/ ]K$wwrxM(?Ɠ… ?oBC_cOIR^?W9=5n'~iّ#e;±c]uV2D!B!pcԀ w?oCk{?;UZ7?6wv{xpY, ݻ87&y AA9RS{njiέ)9LY_֖=}t{GCU" W_f:/AQi][<῿MRe VWcb`]a鲲1U]/y;w߭h](rߞ:c}O{yEx{z723⻒,ַ>˅ pzJikSkB.7ݝ$Em8/{~MfB=1ܝfh>{r*jjP9QQoN2)iԩ-$I7'ow徸bsK4wv!vrr8;EEEEa6MZ0n`mrߋJ DsL'T9OOp !!A^_'-+2 n";fɩ 0u&Y*Z[98B!B!~c$ܻ޻U.;%M ?Ǘvwg߾ee# 3;,֖?Cτd2ɹǾ'mE :)3?[5ө`Ͻ;p/ 8}.<+z(ysr]?NoNGQG#VKeZ[ýy< x) ٙ_]}kf/.nx$==)$fڴ֜xI&SC7l:zt|6já']kisGGL~MMrHV/offwsr]]tVXpIl&tokww~uuan⋇MMryQCXP|s 5d?=yRέIIp8E̓8Q0 bS8DxiBB\6މWwB'Z?9z4@,Oa!k߫mH$: nw,[?dͿ  +fϚyA\z`0ݥe^-j7/iZTXXii'^WRBξ3D!B!$7!㭷_Xĉm*3 gO'#ԁMIs/ OPغKO|f%`Ijݵ L6ǬN֕2)mT;#۟mgxfD" ΌlYԧzg-aD3YϞ f$|>qwd2Φ:::0kdZl&ӉEuN▅]-66>u xZ쉣j_޹}$bBߟ>-77ݽsr{89LV[[Ck_/?458wy܆G6ovwr w~w߾ gOwTMrtڶWWv|+33˅7PTJ|yV@+ңR-w[5\c=t过0+jX<_8.Njb%!*(b}! ~)ТTncFiӦ{rl (29fLՍ@pV/>9~.XpleO!B!Bh_%={,[lL2AFrl:Essm.K];_U'#mؓs%Ti g,;TOwNm~hdmQ 'w~8Χ6|N.n7AUO92V?b{ht,}QJV4# U%VI~c4:]Nyvwvw[uRk3 O1|äTlۚPAPrch9sj_7koҼLwDb2'gmz nx?z}@`ފ-͎h0AUO+wv۫_ϗB!B!F* #{M8SگWe~BtbnҚҒ1aӧ 22lxxek]Q L!%/DbA^R$ٖ'>bȱ6t=uzk #4Tx_A""Z[yy)oĸ< dA .B#Q¦?@gwNTT&FF.HO)2$ApMwʦ-7vaB/8xZZyIIdHp#>~Y}$< [rg%&8 zNF B!B), ; 0j@ڂ2k=:$IRz7G  &]t&vrQh}շ⺺'zxbM . EQQU]w&%vns8o{?o?ɬ":8864o0=74?W!B!Bh0Bhܙ cB!$,Yē)'+Ԫή.Fi4suG{K=.F:'#^?骮յww{FEE9`-4}5:&ugX%L3PTRF%I$nAAIꨩTPˍY\^5ѹ;JK5==*Y'hڢPJ eA|e'FB!B!#, ; x8́9ChjtL<1,(VK@2 ]%WDPhU*8;ҴG \PP$I7'NW{ :b$i#CB!B KŽii9`-4}5:&ug=J؈bAo%LV5X;5q##t|%%%~ . s8ٳgO4ٹϟ7듖d2 ߯j$ڻoSp 0r@Wcb\qYUuHpo:VQuEiwLs;|D68e$:5 ;|"F;5;|bD>jt|G4G&Q>>qqq}^VTT ]9`-4}5:&ugB!B!#GWcmG9>~}{ׯ_~]R BPX,===hz=B!BMtpB!M8ڎr|%-ug> e0p\\j!BhrB!B79b KVmmm޹sC=i&ZhB!$I09`@ RS(3y<'E^ɸb ~-aCa ܚK5"te  ڻrSM{ׯoii%I2 `Ŋa$I,]_d"{۷oW*$IXNoЙ_lt <07ԑHFT]* KL0z=Gw8\pwX>]q;+yk݌Jƛ~O=yH"Xz2D!B!B!$|Pcc㧟~g0X,\.?P(6nXVVVz ~OnY'+p:]}y?Hg KFݥ׻ ˧/z}i8i^ڲ{E #WƟlqZs&1Xe}flR);_wyOì6 &;8vsç"Y +gIV:JJbp\|Wm/L}ӿ>b ^"qr[/ԌPAr8Κ_93VcBhr#B!nrq8v(q" @k4L&6rCBBj5EQǞx7[D<8kҸ8%hk#"%Krqh+8JoQE[% Ol.쬨 "#iV`/4eg}`];XGVaa;am13~0$ 'x=gO9 %K%K@" y~G~aYPP@QT\܍JŋZ>čqi;t)6_o[N̙GD{W23hac<;7C+Wz:; Hms>Aj@Vm$5L rir8ļy󢣙v)>..<^[;94ۯ/,]Jd//'.eRVbqVnF! G!BJq(Qȁt)A^->}{^^Ri׌'su$;#rN}uno)>RECcSys4fjR]Xnc`W9^NA[s~I MXww;ug%c9ksʂ5@G~$I~=)An1&FX- A|rRjzܱO?x)!!kC=5,,98Xg0|tDx*!x2 C!B9T8(ljј jmXiS@盅V?ӹs[G-޴C *i׮R/cEq.f$U=#-ch"z&vDԹWVJOxI|_u3+M"&68%F́0F"YLfll3m,kn RMi97n`jHw7;Db@˜`6ՕSYy@Ymi\$??!+S(kj\O_'9sJպgjGPUu[|Phj-,$DG uq1 5JJNWTuX?0>ӥTVTVTV&cz `MrSi?:VPإTZ'0kdMl:Y"T*:wnq&'+WF5mm'ʌS' F$A<8{6I%MM', Ujߞ9BgjPPM xrV9P*մ |qII1Voro( 2/_6.TU yh%U ٙURbyь$y`G^^8qSB۩TV)*&%ymq#B!nrq8v(a48lVpRWJf晊 El#M!zX_V0z߼Kv}Bn[bRk`HLր2ds|BR)cpQ8 [O{_oyo3lݞΏ$AIc@w/DiIؙK a2g3((sY,(il46r̿v[zT%sfXؽ))ݱò.%ٳ&cxo8pbU'?`d04<;g!Z2VɈͫ~qNìY Ɵ/^]o0/TdK :U.oC?4X[[JS$'dƒKwΫ6- o\K/11R*xęRd)~~פR溔X__so⁞uTZ˙פҖ.=%A_+uu3BCb';h&sc h].{սVM&qu)_>mlXܝ̾uÛ9BM`!B!#GWcmG9>Fcgn;',B+u ÷ڷ۵C3}\kK.D&gXnbs>s]+sl%1[ :ټ %aZk/?0jA_=O[42}jR4@|(8pn|Cm/xwKmάIӱ(oo lɊEGύ58yD- ^: Nwt.Zt{< B!B!as1-aIr\,zaI7m~tPt옺9lGN plknح&M>LBebZ=7zk]h嚏|aČXap=(++3mǼZo~t?>nިxn|vJ]i MMqITo672%&ܡKJe]t=/NOe|"翼k&a\T]? wŋSz,I,\ 2c}Cw{+W6]ki8H2 ww8wקNg{DzavcHQQ~MK$&O6V4rs?;~d_=1>ȑ\[Mg ̖ta4>rOgۿyZߜ>=#,LeÆSee\x,ʇW%z|^쒬V IDATU._:?%Ph4G7w[bPLH@Rm:v}$IeI4Sܼebm\6P,T{n7ίBhFh"B!lG(2Oz, 1T+hzơgwX[j4 {o36xʨg3ikiv۴388'ƃ@B~\# $'J::~}Fi7lO߭!~%l..鳂4 r3J݇r2״՗{n]~㳲2oTdMOfa|bhZm;>+?fۤ!՞__Pz{ \j͋Ù *Ja2 7@Sgg}w-mn̙3f̋_ɑSRX  ޽Ǫw:_pFTԑb'SlZbUzM&,9`yyYI -'[oMOs _xȏgϺrɄ֭i!!Xr0XW*[lƃBvnĂh1;&fvLLR3{.\8ԶI$cblSM)5D- :E;bz)AAp͝rz$9>ؿzɣH͛K~io2Bh}>/ 0L*\>@&"pB! wϛ|6\nNH<jMCA֯ hq.'߿ZpL-Ka䱂Cӈo׬a1%MM5ݶp4ٍ|Pm^6W2˧:<Ѵ3D.p/ !溺 ^؞o7w68㭗 xuџW]x:QRAzgOv9$f{b$ԯddz7[q +>s5^S 1 DY̍fPF74@NyE -Vd]5IS).6 R1FAAԆi!!Pl]\eeWw˝rl5=Y^> -}] D\P#I${99'sY,9u,,LR({fhAApق!qƾt4ĉ/mCg\|~1Z%[OnAuךvx8]eISO;asJݽaorrnJKJLp||_dIRv5vtPŲ/]ΜPib9-8N4P#bA\=3vi@`3^ @aj8#GJN)j4B U? _O?eX>TJ3?zM!B!46ͻϒG| JNV75wWC f1,./nŊQgl&-4zҪUq/a K#A׮0mmlڋUhݛq\aᰄ%?$m Y0! @|QM=L//=63`8Otm"}q+o{'OAvlÍ!A mTZ I"A8^VYIIO5?^lH+3un|>vkz}b\$6$@JDY_4zuat'/W(|:3󆨨;O/oi_P0еrgmu:o//je 63roR|fW*%>PuesxyQwxZgU̜)oO?/7gEqRWwX8 *[/6wz]OH26ߟz\=+rE\.T_PL8@d麞m<^T y55!Ɠb/$Fc0 jk~~S&yK?{H(쪯|}XB!qW>b{~*;;ɧ`>յO4}mF5'>^ LJox'?Rwt09K@ [ {Zdg2D5`€ѠFpDQ5mmO~MP׊t,aOri7=5`~C%̓ɪ0tI 3.Lv98?I<j:s ѝ4NT2Ɔ;6uFرqڢiG#g.Jc$E wUdYZp8 ⶝;Z*өP=&}F t 3L`SAgÚm۶>˽15u~~½寶htuYt:$bq%`4%( >ڿߥxQyj]>>bj<vRF˅ /_M7͉7n>u ;;s&.MJsE#xq]%MFږFv $su )ƽxo^c::vO;2IkM,B1@n oěX *}M{#Rl)fˤD0ƐC[kKqkKþܪGcf$Le}'v!PxG-:8aX\'h{zՐFVV'Dwl>PXrҤ%Ν2RScݼwwUoG;:{T*[vtR//@@GoLjVK#r &SYs@oo':{l_z[\M^\5SXH_6&~LSNʺ[+80ǫ(6)4ts[ϞSfҞvKJ%B"N \yuj@~}[CxCGGZm=x~BaKW799b5HPԫTABWIMMojj_|)qq|.$'uIH( 8,V/=y x)$Y1{3fqcB!sn{eZϞT^n_3Y:}zrdb>b3tzECùbN`{ Ia6|m5kBCB{|[;K07-mZbD T9/͵ II3ebΠnj>x\ECðKO%m#ZE+8$<> yvb?P?y3{jśQ¿0dHEgZ* `@~VS{?[;Κԩ-3xw9- KJjx=qsOO]?-zE7}#)NZ55d)X&310bmㆸ<M)6E(Ft==J%dV۪Vh??W /_oy~\ܺSzGWu賄&bC%z?}}0PwwS~KJ:;E'OߡC顡ˎ_YzTj{[hmJb0Uq_i߿fGS # иbSaU)S'40D::ZŁiiD:;K'$"0B!Bc̭)-q_}e\bx+fЃeLtϲe%b㨣 &d2՟?/w)JO&[u劊g>Huujcg@Go0ۍ,_7W668r}^ g%a ^_jچt("~JbdXx^UtWQDPsX,A|C?:`(9dl$%M,H>C*,$vTrw:Li$9yg[N?~=(Nzc~ȫ pY Do4o[c Զ.'M *UQCurSSenTj6"ȉR͞M"׋M:A nIOg3$|>"BFSw9ZNWf/g(1~N M qܒ%Fc=hX~!$I`ZB [fAͤ|bkW/1<7ߚv\^^bxGT#L)}f]7%+ן?Wwن]9{#7T\k|qH+Mlǯ!vuFʚ)SJvh6甕@RP*,Fmg-PZRٞrTgw7F{s͚X:yʄ@xuS4'23%&RJiuS6}:U5~gw758-45kˆ3vZp2ßYdÚ5TlK- }Ct'rի>H^fMǎY o>yFXnNl,w&7O?|y6nϵE'v<=+Z>4ҩdO~=ݩS*ذzuVRf">nRV9cF@߅ǣXpQ]ϫz>Df&oO矯Vg?;q7~};>S`p8U2;%%1<\=߼ΩsTpɤӁ2311#>_7O|̾}_5մXî֯o;peI1W$ru3VpiX>  yu G041l}i^`Tcוoj/MOwumHgdouO`U֖ςv;!I<,:73.nxY R]0s&G=rqupwHmm-I";F{e/a#EE l)ohދ^AѢ~5nڵrx}իT==!>>L:~8}zߥK_޶zM/ZTbKuudgȑ`dab̨QQ/6[fAtmSd2\vPk0};5r0/SOk˗lE|p޼xJdh>>bٰsg*+۷r|VzzZx,@To>DoZ~E\_Vx/=u+/GԊIG #jsUUCCo4ivl~ڠRs疦DߚrN&.<h^ᇿ['pr3K4wvB*x.\C;'6;tvlH>GzN;gjGnn6^녣XqyyRSL0!3L&0$;wGj]AAPب=NݮPO.A< ?46G̙̤$& X F? &W!/qw !3gμ'e~F:?}C))UUH8y$}d'g۷KE] !B!4ߤK;8 )qq3~>}:pRX&ab7$?T~l˓YLߟјA,LO%P%Ir^d~p]Ze+O*UgpD­߿zEv?LLS2`#njzK&$RɤkkQW^?QkܣGϜa.XIn쎰y,vuF ~W@Tkm_˦<!7Myp[gTb4oִRۼ0X1 _KW׃}s^l'+}A-$mص bqHD#Rء9i`mk&b >Mk 9p`Gmۜ}.YH`thZ_;?Զi_/u>;]Qa!I+h63 5$dN5alηۤoE](pZD'缼Ax{DxRsts/ҥbd!>> nFSлtNرvxιQxpcGGw:ߟ>MM//.G?o:v`2I٪Vi˖~GEJp0 ǭSz˯mӓbv7@gB?E0sLdRw-2KݷW}^X~}GMD P'_ggz* IDATݳǎV^.x{sLئV׫TN.77׿Ţl60Lg:Ɨm{gTJ?j=upaaLm!jl[ōO pt==uJ:37wX99ロVF R+mPS@c2pWw;ٳ'T&r&Y7uvjz?Զ|啁"m[vEy< IF[Z'|r7*y<&5jʺzD׷k4|63gm2ZcѾ}N OBtu]RZ{/nh…C h -$a2>~dBpw۳f͞ "/gϽH/KIII{}=Hk Ǯ!B!{` ޥH"!fCk됊5olܕb}v?i/i]vm˖? EI|ƊJ1T|V T T ٦c4jjLe?Ŋ_oHg ;CCɳT4c%'^u9ggS{6 Ӯm3Bh#vw_*-ueΟ_Q_a ~}3&o_&3Y,bŊk"B!g{Og0h+=.]zޕbRy9^h45A } D@V $RRr%qd^q ,]YR 7,&S~y,[ $,,..5k֫?l6u 7c8Kx|ah04PN7$yyI@i7t-U, ;PXCʡTʕJGgS~}RiiuѵkgV(A 46iS]]ϽW^^d7lN4C Ad6voxB7jwNLF#Vj?WJ ǭPPeh4`p8,g щ51y((ojݗ B!'w}KKaee\Xخ] N'-IXPm}W{2^;!aB!Ƙ߲lUrYنO?O&kX\)FK377իϚeh2]]l>.u[gT$ZrR:_\vS{U% ^>8m4d2e߾׮]4>umS[ۇ~-]MF'[VTV>t-SӧֲGsZH-)ݴ}= S>ԇ8K9Fy/pS@!Bh?>ߣ;8Z8,usm 2LkI$P]UF B!p;|/MkT*5JeJEӃ2PR^}b$Ix{+e|"QTUSQ 3BVh=]Gi6$4L$SrNJK**,,v@rodZ, zu5: )Q?wh5 ]գO-,*T~T6?.7>>5Юy4CyB0r@WccbhB]q\Ng >0;8wR0B!Bc}[L&O" L-7iRua%UgzAF-fDm}PB@w5@͈FDt4v\5v֜xѠXYj|}eQQ%%F/XH8K9&gƞ́9Pb—HtE(OH?G vʳw˯I&nL$&`& < B!ИpRŃWb1f W S,mV΂{y݌X@2\絸o,aB!ƇА`/**PU]Oܚz2 [w E ;|"SO)xe?{2d >f}# w@sw!B!B!B!Ϛ%\$Yk{M0OMf9AH̑#֥/IʒOq9\ }r ;k+.Ma_ ;>-:u:X,}kHM6_ p0B!B!n85>|t c&;g,(a֚溊$IYQϡmKQqu2O F'9<=ӛDQƠTmCo'N}sx`)--ohh!gr[[-!yYh}56&F?OVx0K!B!B]pHx|ЄƜo$8)& Dx[G1+^ʣ|/!hՕNgTirBD9ܒbq—u2wJ$kJzTCaL0УT1ؘ<1ZHt"B!B!4 96('ӳnRu[s?!R9897j})}9cC}=Zjks_O /7꼿T4R:+sEaU/\H#e9}Kveo}9wvCr#_Ֆ ,4\0-dqcJS }H:|~#5C ,x.؄|L8=\c`ZvkDV_XBu"/h, s+9WbuGRZI5j2ûU'1 ;m壽H̕zGbIs+Uw|tya<|.'=sEASëtmi2KW#gS!Gv25njeP_J4CXQRm淺~IUS}K#e|kܒTkcûmyqݓǠE_ՠ&\f7 n]y*ć2=43!muB!B!cF.;V> ~GǼmfS]Wnj++m2XA< !Ms 򳱓fyQ7noa ӹBl 6톦Cʁbk:yu' p"eުS$ك{A]3"fEry|`ɡ|."a]q?KUw堅$+ZH/b>>pAѲ?>6k^UԮ~$1 yI4ft(cYbE+nPԆ/uwcf(\t+u+;y Ѿ!G-T,+ 8oP$}V8F*k?U:*{LnZF!B!BǧhLh&PҠPՓKtZrX:ޥ3aӅڶnۍTGIˢ֛F ˾sZq) 878kU %3m*"+>v)o8MɺBޯWR.DQOiw tݼǢyg Wa$2Kau˚4oceb,L=l72ܗMZ;KC~4pD ѣYqBV,CytfKJRf%~)=ߥ380w|ǗDC#TQÀ93OҌBko9>B!B! ǥ},,1mV)u ωِ}HYV% vXRΥKXxDCG6AFeAz:J]{QMpN$lHai,yG|_MV$t>$¦epۀ0 0Ї@+ö8ꗌH IS4%~UI, LT֤{Lb!nfXm=#`HH yFP֤ٝۨTئ=/A˷^u|U!Y*Ѡ>Z<߂ˎ}I@\P7}SD?MzJX]FIGm#3=喲 O$;u-'j#ݍ@#`n87jtG ֕ޭI,hT靤J#ya4QT>R 3c|=毎T- p%^'sCPMýI~д53_ @q abP!yA4uwF<?i]aLҟ IDATm,kv~mM+<!B!B!ᔔ3fH/?~ss̙2e _F@ƕQQȽri q`!Ig9\ uXT֔{L1TJ/ ) jET t ,xNY vZpn'̪3t=_˂*'vMg7ɀDv,aPE#ʫRY P1-$A-9b=0PԠW] ޫ+]TK}fho2"p["}:Z.!DIRmM{ztzZtJy,oasIknlYK]1}RMk?(VQ'D2%P6<1+ևN#Usd/_NJ v~mgq#5JhQH7eJgE\,nPۖ͐F#^9f48TUެeЈ`^FYothϋU} m5A?-ZJtV `A0gFcW ]\`w{ry|Qҝg-R݊/0Fq{5dI"ew9P]"+SҶjB^TxjNq[B&{X*ե,,zLΕ_nTGiD3p 8 !5Dmq`٫)+~m˛Z?;6Va{-޺#9 뺎*U|ˢBUG-p3C@5aXB!B!.***--2̙3fHJJZhѱc>oG3gFӢ_ A&>.b͖F#WqerqCׯ>R`Ή{gў U*$Wf.vGKͣs_BuGFh ث)Cԥ3☰} /Iƃ?WnO ~Tj𗇫EIRn\cs+ι(_F05{o;)XHp/U,n>!C 1`hZn)Q~4H ZW5A(mmbrXwקxNjTɅI~/;󠠠?*޽{˗/wxq\X:K")PPTT`0T*D`qcf)!Hf1h% ?f\]vFË"`N֎+x4J5 (g 4N&/d8ՌB3N&?tk(40Rz sm?'sX W3_Uw{_ K9H\OPp/t()QxN4 :dIEI~G ZŠE ʪю0=XѬY'\=~+W6kZ:"ΓKy,Ϲ =&۰tZb.TwxEuzSwˢ=Ƙ\'KKkºC@g0]c{S` ߍL-&@ڜɰ6`)^F/S7Tب2Խ/3\Ϥ01(E:ѻ]uנVvq4ڹBuG<64zc/ā{Aೃy7qlm]K{/cl fz/$ )~MMnHO$74rCB $Ыƀqƽ۲ɲ,l s~gg9sfvG޳3s*ƹGŻ,6[ooa1 ~~16|{TgjVd    pp7;eʔ'O0 N@lllFFC¬s;C~Zx{$r;eth3:rT+9߿ztvbj-c#&CSR(#`?[L>l {`^O߀guQoj=,'Xp⩸s;KXs~ rQ[pD|Y%l稗_Pw9o'3^q={Y&㗧.J\ ?\n|'cr̔Qh 7b0%8ƆƳɑ}Xo??s,x*' ]?OZL~ƖW<"`|*qtjuâeΚSiSuUr:]CMN҆;0'=X[*yZ*(enSj߰sQg=ҟ Ϙ^su["=2h8tT$Qg N[қ0v:>#Ѧ5O5&%ng8]֠mX^(\k^f4/裏[s' X}Zn:o)e+KAAAA{; /o>}BRT* Bk4cN4Mt wt6O+7Ө@Y{ xdV g_Xux0`[N&SEd$[?ITD򼾱>) % P d2[#VFLTZ ncJ +naC޽{4?q.㞓F=-MAAAA? +~:>>۳gO~~>sVWW}謔0?uMK/n-Rʞ.U!2yFU%3-(2>ҢL\LTl9ұEP~_6? m:bWt\i76Iv=K9,i \{-~u)"VQ=&w=xQ?;6hyo,NK{/Ngy(dF +jKA>U($,2Ni"2aT #<Ӻ}=P]k Qy̻NVԇ{^3$YXa _D' Chrszz8ohǎ999 j1a~ޗx%'0R*0ӺI+kOԶy`_UCUt(JflWqo`#f6|֒&n:?~`{m0TIcnc tx)dJU`{O؀Slz03gW3.lYA:c?6@+A?.,=*HӠ3l9^hT^Ņi ,i[o/Vխ?@{ LUu:!^ <˚ew)0gHlYG[^`SZE(\dgyq]1/c'CLX Z- oРAsÝȊ x`P^AAAAH;$7iҤ 6lݺ5//!""xJr@DDDlllzzzcc#)=nx 좚}*ebl:ZPY驔upUOrЉSwws-8KC?=ꕁ'4*a>0K#htzތb1\۠ʳP9Å5uauiX[RȮJ وuZ}u]1t~%#"|)eHz27oYgA.޺y2Bc51++Ə#WS&Cctt򧰼Ψ;okμXPx4rYڙ]^qxE-Ɲ-2/]qj/`mש˖ȭyL `Ki gv6SzKz;Sa;eƔjkx*.Y)+R+,O +lL&'U64y>GmZw˛]jaV㞋'$~=%#xYKP gT6Ujyd-h}9E*>t^B+qZAcpfs#<钇N `冽voPip>hwPTFiJ ][    h' 3gebnn1$\SS+<ï*w}733SHz=Ѵoz@6hԄDpqك[vgA&q˾ͫE?͢o /?tbNh}]{{3?5 z%1zԉ K<2bcbW*deuG/橨i}hL0;N]n"C)]hWLJz+~ª]{3Zs)I>Ji:ݿW5}BZN\lz N džm6ܢWt y/E(yB:Jp׶O<}jEqe1N?LK/C!3lJ{IJ .ݷ_c\pJa3OU3W܄Ԉ 9[ay?ͱ?8dJq*"n'"lRh@G/[ FҺ%G6?ݔ)hɜW~:^^jE%Uڏ7dĄx2{BzvM)/eً߬g-ͰJ>"YMAAAAG!K|ryyyPPZ 7++{UT Lstv:p0gf~觝zs+^qw\D.ټGl۸;q#y82d؍!SDPxy֯O~˽TVw{}UA>F^UOY{*.S7]ayCMthUGX}{F3 jS~)Mq~AS\_Vk%Z]YKX7o%κ}O;V-xwL-YM`ņm {{Ur'VW ' \nt)j/^l̵w[ij"z`FWm8%6MX&<~Ƅ=(+k3gka y5jhFaOFQ'.j(jMQ65rqJ+揈/hAkq˄ުڟ\h>\UJ :4O557IOаAq'YtAAAA\BH8:: j:!!A2T*L&sa":;UM"د6:4GHt`✌˹YgO6iJupi}՗UWրRMbwxbYe=2sjژ^A!vrd]„D׃}2ndJŢrBjq_N 6Is;ku݈8;׷i?f y2*'и:Y AAAA+8 nFEu]UII>ymcYnhlJPS]6eVG$GOqv`::[u:%ɣ!߇AcʌiCgA;~ ԯZ!1Ei_ÇO)JloF"`Mped£t!wy     jt'`4R9+y6 䱒\o  sn7>vg*J78l3 3WTg`3xjkwc|vVF`D֍&$+pK?W.^,Y?TnG~nO-yTuH]|_e cBylFAAAA{׭[g2s+b L qTOt"δ!yi2qq2NN2NdCg 3gqk'sPp2d\T4:XZ{ ]N?n'u ? /L-[jGP)[xAAAADǽB„t ݡ dwћ,D$ԯ\Csר;cʚYJe,'],֗RT[_=\>RT[,wP̙3N/B’n+mBgq* d@t=_Q wfo}1ǹ!E%t~aD>rmD>PwxppHD0й^;ON*ЈJeQGLH'.4t=ĸR?q= ޴i+ ٳ5eNW$      :o,8,4KX~.Jq+]lp7+55j&q]Y6%c{P& \xV(-e#^^:1%=bO)ɏ0E1{ `c4ό #R_It+j?)x;9 ($,QvP`l ԯ\Csר;# $x֗I2e<&.EOpChtpkEB>hk2wQ0AA7R% \x֗I2e<&ZHX>2e苄D}"4jJ'PHX0)w@6 D׃k~pkDz- f$,GLjM)02ĥ An.SvHH'mM_1)$LAA΂<AHMΫGLjM)`NO$:w @˔]/D[f W.0@6q;\;18M̬m5|G6~F- ErIt5H|ĤttťLB˔]/D[uY̜9XHeE7L>+e5/g7N!w "`mC)aϽ*nq;?,r8NxK!tģy6m 'A5t ?wZ14 EroKt^$>bRkLY´K p[htpkEB>hk,ap`9^A14a}YXQ{bPL^S_i+쇝}gɾcJUN| ^ԟd+zώt:2Ϟ`^z;n٘SfW*"6[hl4]AWzMA":}Ԛ.SoO$ 32e苄D}"ּ2!aӋr0 v&DUa 9B{gf& )|dB>ηSyyr/+ pl[>4'{hC2_ _SK,jϞ=wR}C4bsH Z/D$ԯ\Csר[C`AHn7.Ԛ.SѮR&meʮ DI G<:bNTV$#&ϴ|_^F9hƓ^!C꘯LϽU}j{Wz:}s;[rׇfy n},ُzΜo,Y P̅ o y" lΫGLjM)KW\A-4:L5"!QH5,j K.j ?T=8A9o¸r~ww\r fVxWDć!W(v;9uٻ.qPm0B5g~{f5 _^'\`{;t'[~W+c(b~%HZx]i;WuP)cŕuvQja^gԝK RDC7A͈7:HNYl}IԚ.S`hR 7F)F_$$&oⴗ0]wkUXYr!=+8XƂ>NGmwL,;춹ն6~ iÆa #O8ߝ>zf]~nRP!ȃq r\Jwi;5OuDAL@ʸĦdXԽR}# <e~7Ò?uz]@Z4|_UQDZ&V 4S[_#&˔+.Ee \}O$ښFM 6L]dBaIn擹EV^ ? s)4a^H{`#!=.~ϿrMI۹[ \xG򧎛W?p/sg9ꩬyp[W>$h j/_gpńEY JY r^"hmIvj\Q8GV Pn3+ˌfiV3yqS_#c^I~mBj‡J,/cz+9X+VGe,<+)ݸHPQWWZF2$GrrOeA@k~^gܦZ7>%BzuoQZl_"5Hđ3{ɑ8ΈA#MA#&˔KEA-4:L5"!QH5^·BJ1 Cv1⋤H'sYU=ohċ7?xg>yz #lY6_7D%2~׉zo' +up~%e^ڿ0VD&$|w+pchy(Bpu\ef{QAo@E DGLjM)KW\A-4:L5"!QH5)$L\Q0=I],3شlR?0 1eV+"YC#nd ndΗV5o.*lj]CG@o1-ca,ɭOg?*~|+Po/B<0fPwGoVew)ݐ1[1e~~`:oͰzd.{h9߬SO;pLmlGǓN:N/LM|qi"ȷAFArbKzĤt2^Lq)D~BB˔]/D[ꡫi/a©p+P "ޚ0_vVC(7No*nO&rB޹tgܢII_~-3Ф=#?2_G~?:p}'UĤHk7˸Q-rlLڶw8a|oPgu7[ncPwnPw.>=%_]_n^ߖpX (~ĕi]gأc2)PӀv qmtf/j 3ٍ2<%f?3Ӡg7:` -*t8n!ǽyV^/d05% y" lܛWԚ.S p[htpkEB>hk,aґY|ڴӇ⪶5S'uksL]prKB Ko5%~b3x9D(*wFo` eڭSF [ !QIybZy[lHdZy~ܴFwSp_z6ٞ`lp962bL8A_3~ e-}m?K妫):6s*(^z? ހP?.-s⬊n.oe6ťrQa@wXsN[sd2XN ]Zo{)_f.1v󇣕a J'bC}pӚ 60_e^xdlnԳ|SHX^l^3l#q-rOgO#6պoen%3~ڋE @r$wP_ZxU3K=x ì'j<;PQYdU1/yq^^Ƚ+w֮`J7q/TNkq3 d^BQyąr 8͌΀GB-QPQ\jL'"}ߍAD3S[_#&˔O'A!4:L5"!QH5y[׭[g2s+b9զm˞a:3#Pq棥7>-}^Ղߩ OڞL>ю`TBBqD8lAsڃ_U( WkQ#AXYVk`O#"{r?YaC^*VushjB꾳[%Mn:ﱲ ΰgěZl|+;tMCZf5 }S6%>f| d_Y? WiZL&?zcN0v'X(L"Ř;ۜ@[Sُp6`Z]s3l}:nkMܬ4xn}pQͫXw au-o9,SJy)UH kY\ٯgSϳvZT\k +elO&Rc0wVg1m:?XyyDEa*@.C@;Bs7kԷHܟVݓwG g k5쭵hYeFU67-"w0AFArbKzĤt2^Lq)D~BB˔]/D[Ө)QX^!a-a~ZF=ςBgqy xC{oMʶw1d!2 Uc>͆Upx)߀O}qcz^e/-nxp:*,ve_`,TD\T4*\*rw#obRVYǨ:ϾގjMLꋨ ۝9jn:eC)WaBQuw8fζYf<%;&rɑi@fkԷXLunPOK-wMrT\T IIvGp;v ˼"jZVPy-dv,i2VKoK"SE zӪ5_DzϮ3~fK/c6lhѹ%x7gjШ!8ɫ[;3 sڃn'>cKBfd>aED$hc w~KuO"5Ɣc懝/!1Z-BւSY5 ǾWlpف1Lh\{ giNk? p# ?--d΍u[)dnƱ8,WoĚ`$TJȿV!S^ymfplI5^{㷧llczaQx(0,ХrS1-v_z)_n1MĦCO_ƶ-?7l/`?ٜw &ج1s aavg7Tk^5;C x(MNA@ai Lf0ؕlCOz m ``SY6izd mgԄJ:^?SߟpP1&kk.O{Lă:]]]B//Mmm]Ҍ'FT*S&VﵟF^'W2=y`(cxVϷ 0 ;® |w6?߂Z-8 GG#g[Ol9$[P$-'EO 5&+ fb+#3[(pO ƾWr| <~އw4%a^<5I,p˸fMc9"15[\k߼ `d289C\(bqg_[[Oyk·6?ͧœ#c =x}cc] cx "ϟcɶ$cf-vxA3:ƋӜ;B儵}p5WVEq<x^/*"){;m#jc%7Uo;xջGb_ѵi1O:}qW㝐y OXogDx&'WDPy鰡ֹ9DS'"{3O$ z[䠐D{(Ocha`Ԕ1x{q Uu-`IZj+Ios 7FX5ৱNK G`TOӡ2/G8Ș^n1M5=4AcLXWza'vܰ% $Hs.:2;!G) &)FTQԊTuuu8[nx"iMRw+&) L/*")%]6UTx{{妇N1xUv^i_ѵi1O~ hĸӇ-RtOHPX9c)a%Np7+X@~"ވ)@^o~b)>H^$]g0Uj͸]P;r%vgqGscؖ\W)9רvAO'<$ojwH8>Ԭ*ħS!aq'J{x{SsE[~Ӟ}O}p%C$^J &&G[ Xs/CV!Cma~+g5Kyz4sM/$]ͽg+X/hl.plJ E_OI]l-#eCmPN)%*С`= `Tͧ_l#B@GbB[kviZ-N(kͻm `܈zF[ڍ~7 Ey^7 B?qj:9jo  %H9tZhiiw^Q-ҿߍgnhNn]Zd.™|coؾiN%:H0!&ip5G~Aotpd0uZV\3)W5\?i~60|NYun&=i1KѝВKefUӼc?%};$KWv[>s\:ᳵzDbp+/V3\)PHXtl|Z$EbsuT->QӀ60B I(9.ZoGWYGj/xj/Ps,inNBz\m򋇒qіxF2 c=7!5tC 5-q^oƕyCs-rLfڨk4`O1ZTg^*].a.ƓTvgbt ϭ FM Qh17*'_7An7\ؼVt" :P)< 4mJ+X(562پJY1{"p˷aX^*8ݩTJM^עZo'1wE[hG[&[ǷCR汸~nzB>m17W֎q;>֊+|kSJd-4}d^߈fX޸ ^ШZW0*-`)So~mx/x$e'.KAHFGx)*w%I;(naP)޶o pODwRt'lŒ“瓽b}&T5*nCWS?q.N($,I:< ׼zć>tC%"2EUd$My;/&ud]V>CuZӻ-')vҧy ĆXa@˰dQP0?|'\?4h+M;=[RTٜv( xKyS#8Ze댶 gkV6Iu<Xv'΀m' S?` <tǚ0# *pGqp[q~ӑm=hYЎSȀ7"QAxlV/@g ! Y wv)'۷NRq: ?Ss4 sjpHwRX_^'*^{V" xoD̀"Re muQ}yFlC;&P[v ;/ËU\?Z_W_a0;7\=Syi ;;f! b5e9,87WɅ-0(-lу#zwWy*]CeY#?t V>+ǏP|* N F[]cB PE >j\z`BA>pȱ坛"aK;Eo>.W kk3-ݴ2W8\``n^o,NT9ĖTT0y+8p4s, 8n[_͉^ Av16fE5Aa*3\,6-Aa\{4Uh'u% HK99l[(cؓ yW`3hhR+vl"/n1 ExX˔6=4j@AASGyz/8y ޾mtׁ ,fӮ}Gl~3܋G~2o.c*A1drsIEfޞWNoyd9YoKsFOP{.:}ͺ΍򎚗 w9(0=J{Ūq%ypUF-;0's?1[ok.pUDFq y/gʿG _ gjMp{o2gw] }} Oв"b?O%wB :@~9Y>@qF* }|CSo9ݤ{}Ը{D7~H7Ͳl9ec lwxm F  wW})esBb{]W-/\~[J!adU1HШ;>fAj= p2nĒ m9$1b3*¡5J!a^R?9E28ݗ1#K"&b=wX{&qj' xdzʕ)GνAjUA)fX9"KMu^RA}Z+2V5t\ꤳ٘jT,X x78n'.ڔ9OjWp1{$db8]B@VrѴAe?x:c< yxG"!İ-B/лB|woQ==ut,lp&ߵNXJ&v]7KLt_Яz&+sk6(.#1 Y0{Exak1y%!#J @@|~G-+RymA*i;0|]q K (?ΡKU qmhISwfHøa4}J= V ";#M>|7!SV"9PouP+ڔWh-upuߔ+ g)òH}pc)qj_ 6xv^AP(3`i=s\?U-)8=7h&_ѰGNo_)5\٦2Тk\n?Q,`l7;k S?9S;-g|0|'TO (,l>Q5ܦeCͣ7MKWr矨onaU -G:yeKLa5[nݭyTp'/Z0ޞ#?i՝C?}]DOUf-NtvVR(P,ߝIrG IDATZE:_/|رcgϞ}全@BG`XpZ}-'?קOER{N# =z Y[?Y0tk .D&o~T m¼Mw#X)^BCNV Ξߝb`SHTW3{Q1/ģMluIO8Lf\zV98GF\uu\X%MEpu܃dH%  ʗJaRwl9Mr%fh }~ OG#3b]!/]>z7pf]A;20e>~Xq뎣24Togl](x~>4n!@si8 C ˦͈MB O'ȑQy.1<@X|olAd?e %Hmb=$eD;! *a~=8t 3tîBz5ép{>|7c;wXdv嫄=mF/GN!^Z7,U(W l "%[q1-f ė!^]-U>Pן< Y:$ܦ*H>d SzUCA@Eh9n< XҦHb:#K`ixU9 lh(QRW.BPwÁ+IC۪=֬>T#:v 0lz<VX1lܫ ׎roGAF8-kra0$¶{ if6|\)SX=iK$"5c$ˇ~ς%&ݍ'6 pm<`vSX<3g9YN3G t 7/=)5m.M,ORI0 Kbb˞ OTf ֮J ~Ѹ4  d}'9Zu`?܉r1"Ifµ79yK$e&6 e\[>}<|EV&v}O;bxi1\@C¼fOoOV.V-Yjgx0\W"ȴiKyrJ.-ljjJVA"–ׂnϷ=ʪoI&\KK~52uϟ:Nجj h]ܣU;;rno%@`u0 p?AvXc =е)&tAJ_W=BzZ&G]w/ơm#,ٍϐ<naLG)=Uoƈh=q\OQ$+z5+w# }?Dvn<$/]ԲhUPv$E@5`[t~-"v\,r˂ ke?0bvĥh>K_4pk써/.8"9S4.)!Խ&!a1ήMqlD59M˘ [_WM~?G`y(4G,up`]s%쒷3ڗ8GKO)p5 =0k^oC/Fhw<(A%k)O_`ch)>ƻIo>k$gc +С1־yW/'~2ރ; V1 XVR)Ș%_/WR[Ol.>Mú4$7Q)R;WzBPFIJ'7$ Ьp J>1` kF;v$hpDpuy+2ƆOmz7jNZriOLQҔ<1~kDd#ɣҟ>8c(t*gݤ'/l1ccy1K痶a:X] lֺ I\6˦DB0"!>OFY!W^'j΂?#FvDB&HwGYɶu@ (,[8]RR kE(ļa=#9vhnF y0'1HޥkkK7 ~3R0MBn$b;wFFg|W/]*_IaDB;V1 X-HŔB 0㧟VիWz96/myىw8x4ʶOP( Ontcb;rQJ$DVr3ll&N|/[4gH"!UoiI]2B{KggVSV AµA$j4fVldB-yz}  \ohNkɷBp;Nh 1q4sZ3dg􃢗L5D*45gV U?89  ߿_'%22N4r,=+*Emc>ԱJ%y tJMA˸)CRF}[pb8 ~-J#yYbFz{jQ{:KY;(|T\?>H'_5hTf\jpAHDe1Ji  =*$VR n&4lZ\h B"~\7@xSW 7.\0?Z*%a@ZIlhӨBJukWc<ѭnVy)m p=5h{&xtB`'aEXe/9 Jϓzke'B7Kⓕ>o,HP(Fdpp * 'H.f嶽{NJabA~vb݁J^TLuE9,DNHsjsJ] $f<vPXBjQgdtgPGF|jx;?D]JEƜ.֮`m bp;Nhʐe=K{4Šӧ뇥'8$ .D\@]o@Y~bOTzkO@ύ%O?7aWmƬ4Myo!A`N߬}Ϸ7d%s7o7egÕD)#}>Ӡɽ=3˩5ӁS5o?\p=o?z~BHDxk_ׯ,Q(U&} a| k֊)X}9?uvIه0?GT:y6+>ߚ{,x Q( p::?IX{.]u.N8vhAA9P`Y?QRegoKe/_–.P*O>|8[1N!]ܿ} LNjFVWZԨ_U*I @,dtu-O,'Z;36vVR(nn;1 ~]W^ m?\#t `gS&ܿ}u#S B‘zԵFV ZML'/:-+?Ğ{v?1s^tf'=A]0=saL>/D \`F;;0p ;s&6[br CH΁6p~_S">DX}X<}[\V|Jo 71~B~a;?K{yH[_OuԺ B.ُnb rdQҕ`YvܯgY(C,]˼7hڷE9ז"zUxyWnhhhFNl?[ܴǎhǤ<K2nZ,ͬRޡSwy'=5hR>;?i3 .)ȡ֮J ~CFMjΉ5Qw7,b=prq[dIVQqtnM{fSOHl?'8b"`EJз9۷yRم.WoڣPf55ь:,㫝75FG=eU%Mȿv!e KKa KHq%D"*²&On~=Y,KXBn&6 7dkCu;!. iy-T]?h/TK=YrB_L5XsZqee POo*Ri޽I. ڻHz}OH%L-{\iaBDG#K*OP(nt *;>lؾC7=}LȰupjGhT[xNſ}<#"akƛo%=Ӗ2{w4jzhttը}.l<(%zL @#oQ=جoTT'\g6AtbOX̄3;>9e˖ݹ*2Rڻ:7VXO ȑTq4٭MP:Xr~fdh~1//';ܝ[6y UPo~c=oV،KR6bݻH4Otҹc$[{gv:m1XK 9N|ǵyI=x[&h@za f4?̨L+a `a0DM4E mH0&C6A.&$ܧ0|ͩЎRx}?=jEx&_T(2;2D(+}{&=;7v$@`] SLIXU^VEtP( Wp::,Sv-܂~G1NI=SHgi gW{’w+:MK&] cGIER:-*Bwx ѧxm J O̲F.muLރҟV+Xi-,nٔ|'ZjeggW $#VOd uAav!vVR(:Xl~r?7'XY-o4Vn?W 6RG6U«7~»G{iDgsS4$LP/ZCVÑD P ֌Ey>iW$| (dEy9Er*,!"[޸GDWks @OѰ42F[SɟoFE900?dNQj z@u:X+P?\?ZacF|4֙%(0 $"ֆ/(`04n`[Ua { j;TէP( 'p7:(l{؏"Mc "e<X}[mm[z]2$pi}FgaAAFU)^O;wKR U)~ۢ"?wa`o'Hx3?a8Wڽg fRCkS?nm bp5;a):wB}'{%nObTPXqtwN''7zy1V[?kj54@u6(W~ع~R2~JT'O+#-=ü2-$P_p9GVGp'Rd$&쿔R^]R+x7be끵pt#%%s=b&ϝS~z'E͛6mjv'>PT . ˓4ԯ_?۶mKNNNNNްaCAA=BP( BP( BP( B]uyUjddNJZZ{ѣbr899DDDDl777o1-_*ԜBQM:PtT IDATMJevFsV#8Y-|") bd2yI{{z ɑpgm5|Flw l>hmr'^CIPjG><1գwp' 9r! ,,>']`]!#B3vvvtPv\4l066!zq54꠆t?_Ya k# ӎP(^;\I{bwǽqms B1 ttdڄI˅p yuX,nԨPMPTTTTT$b1Br\ROP( BOb) bx'k1ioZL2,$̵) ,b-##xjZ&OmR u<* H$R*RThcc@}BqHl M:PMJevj8^F B;\I{bg¹9B::XLesOmd˅p2L.;884SIX_r BP(Jk(  |Ĥi1íyBנ$[F>G&G&}oÆ /^(Qa;nRD]H L=i fAdfj;qVU"Vg;vA?9 '8Ub&_Ya +yP(>{ӟ*$CJ%LPb-##xj&]%l?C Y`IT*ծ]X,k)r:u=J 7DGLڛ 4χ ,&296ioMHX`sH`ɒ%sꫯX2ёG/$2 'ncq_i vv 5'i?~m^>-̯wʞNL3Z!uz_""͟3ql>u'ܩqqRԿ^zM|%:== ӵ!ko'%hU^]eJ-~eꇝG+ &BPo+o-eXrb6t B5܍̵|^7-&) \b-##xjZ&OmRJa 6Ÿx)QFO'՞8 %G$UB SHQ/X(T`#Lv:kQ1'Ok5LPb6ӣ4 _ǥ8E*أ?<1uLѝr .킘'=9XM`Xs]kS5O Bчwot]x>b޴d*.tBAGI|Mxڛt0+EX[>d|8RPe3dJ!vᵎ i‰=6wHĝoڷE_8skGnŃո7hLz~ʶ'řT@_#Jx[X4wAdCSi Z{X;Pur0n*&3,[T1% ̏Ҏyk Ѓ+ ԊTпsdAeƹ ǫDw"zXn8#XI7UoU kD*%fB`Vfc31oj9wwċO7Efl8X{1+uHšAj:5aֿ.h`{Oc =BP(i<=} <1ioZL2VEP(u`1ɖ< O{n6?޿NJdddhRGT O'1E@VDWt5*&/?4 m\Jmx4yW7U\a6F8Ζܳ>|l  7qM H%PSgdv\vV:?fFIZ.d v]dt c,6NA fQm;g!L]k96_D ćqEZ.|$Z] }[Xc7a%%pC xN.ő^"YuT؊Wf[BP(O#&MIp B1ttdڄIC”:`tzAٌ|,¸;.{/R `* o0%R#zY1،`gf\ HMƲA+Q襜BJP ziS̫sa6ti`gCןe\oީr&` RfԳd-Լ-PcYRP +TW ԈVy dbW`4Qy5p ] 2ZD9yLDcf;_u9-m og? QEQ-!eCEN7|@B|j[֌; dFrfy c$AS{1m O[|,ݣگ{6O*Uܖ GFP(^;\I{b\  b`1ɖ< O{S硫م'kaqFqgoge GFv4D)cr6"IbJn׺wNbWOk84UKٻ@]y^] k{5YK~sjPPKsV c:^݂LlB{GԊwW'urMBD4f"3A^ l~1^!{ӅȎT"#B;"!w;CL. PPj S{ +$f DY udr 3 l=\ONܶ־u"̺G7k(  |Ĥi1ɼNP(悎lS7*aJRGϾӶgX Nl|;~ތB QpCP~)ڔ ]d|i(*zoREDFw4ڐwv5x>2!̏y=~ǹǮݺ`O[+a9#}kY GWBFΠҟ7@bVKN3w63bzoF"6 %"})D3>cZ7ҩ,M`Z2 Yr&e ')K(.LVf> p? م@3 ^S&*돓cCl>֋i{ȳLt N:*&ObBmr !#`fsp?cqrT)=9-Fv`FsiW®֌)9[6Юl;j8)Wbv`BWM 9F3wkZo&^˄T8Oi@3زI * pӴl#*ctg]B.܇-f-)H!? s>bc7 `zoˉ}R\}jC9 KHXr>GXq0 lf5 6%'n!9ݑi1-k>hzÿ) Rw$$j15+h޴JT\  bP5TY~bIlNC”J0|.y #&U^= 1p ha0o~f(f)O˗͛E>R'7n [$>l`j>gHGPdD6Fc=iڠ|-9K~^׏~:(˂{ҦMW>kXKtfHb zyKV(n\#U|q !1 #%WTux8ur22_W\XhӏIJKR[YĨ{DgA8`zk~=r'YeŐyvRÝ7$>Č>ձd,C9NBwЕ&xV#v&y씞BG`գ7ɦ8þ&ʲ]'[ΠoKkb+Cx )RbhLiM lfX{8ڡ\n6{\qi _ cLpdB;-Цa'nӤdh2kﲜy\+K~H*Wqiu.DT ʇ} NWM.ap[Msv]̟C5`L@Bdr22(#5"WVHOvkꙆ@\2 o#"*\z7#.d\56%9 /tÀ֌-spp,?3jß0 K7 %DfEDcF,DzFBĮ;4mW kPoaZ"=OsX6"dbzo b! ) ϐ:4Ƽr'$dbb[F vƥҿ,[#tj_qEy3O Fp-%>c=7!>ٗ CA 'B}IlƫM)*_0Fv`QޒXq)9迈letjd{}r;ir.K/(ՇЯn?~2 ? 1z^Aȕwr9"!>-bw!"J$+Xs3 n\,E|9qj1+r1tjQ>Hͬkn*a Bcm|4,kߎ~Zݡ/Q9Z-2~E Qq'j$ eL^#kS(m)J FRмv_kke.̪"4$Ss8 L #GՓDWHv! 1͸j 7oB{߬J5|)#T _WCy##~Yld{_QX2Ƹ0u,J#u %KC Qxi5*x;#jB|ʊP8 f >. 3`J~K`rnc셏/K|?яaR>$V'>E0cJj=_Hs&tD ++r&T"TE~ZBvpxǗ2 eX}OBTz+Ex?^S9<;/}%B~g{͑Uƒ Oc餲WeLﭑ S` x vf?l>s0^) ?Bk_MM#&g>NF/B|t>J3}[ͱ4bL+Ihy$0 t :݈Mb%u 0g(^}-"%LU&S`uE@o0~~ܼ*8sa24B!!ST#l8 +y7K /D۠"ja~rk ,umdF&gryQ,? -S_b' Brt0e8:0, X 41Q -6W"+j'vO%6-/?q@BHC}Lj U0)* y;ɭ־lZ6 B1 'Zᕏ5y&OQ8, ov ˎ5ܤk*y3Y(XO ڿ1p"B킰qLB0{= p3p*&OS fzK.BȫKlakQmH>3I ɮU,S IDATYK7qў G>mq} fcfn7ί pkt*$kxJ_NSV CiR e;Y~uȕHFVSz^FxNPz_0lD9}[T(r.9ȆZJ % zZ9Kѿ5\[z&2a4|"?( Q1׾-|y5uqwHaOY Aq=[Vmm־v9ںuW{FPP@HrG @rr1G<9}r>9V_Ovtgo[Ea>xtB ҰsF6bN@F7;Z]GP p$RV-l,(Kf+ -]w9M%'~TcD27~CG ;K ' 8kLq065(K;)-իU*6lX>}G^7q'm8Z``hMq n6@ OL8:鼓WfeFs#q*0Е] TZwe"Qgɶutһټ2QY&t=] !a#On/wPETv /U[جܘݸ rJ14wlF$kT!MxM/=߸>mh@tu tcim۫[x'5 U[z+Ezj4nyojP- ĜA -"RX]HZ]k3Y*#h8]P]riex ~0pcѰ S,2E cٹkuzM[v5(+M MgGѮ#bşb),`F: ze=Tv3kT>lX ` ??_s{SN͎V%IyMlx*#z@&n&ĸj41tiT2l{z}+O<wU/,@ Nw-DhƦ&6Jn7\wRZO&OgËZ6&w~&S?1 70m(eֵjUv[@ G'&Q] taBUVYpE׳m'_f_E2ߘˋ?eb?eΘN}7ȓot=] !aY&Vd_ iFkg_[j ۾'[6Yw{zZ{بe' mB :ȶmJJΘ6iSM[vOKKg3"&񍂝~9}mΘ>iѶt&?UFƠ@7I4$A/ 1hx{V14D}!Sٍfla#2n[fy @QE}SC4ZMSki@Pos53mX>F߰ oҠMVܔj*dp=avl& ^gD#(jKn>Ѡޮ`J*Cs̓9І:פ@$ MXLQn%r޳"ZQT^(:Qw !Q /a'  ʪ3s.m):t>G(|{<ʻ`?;|(c5ޮ^bWo_(>}~AWkfCPwW`XQt V]]U]t Q8̀j]mQsZe~ڊ+̽@  GI8WBwL=܆^ޘ?Cocp`$PZSv~IB>AfxuSYVϱ= ~~bDǕ4?ͭqqaV2uT֚pG 2l|ă:^^w 8G-]]9xG nN=et >} @~ntQ85i~w#h}~`YCsfʫX_/&-,L̟ov #4iX  Hsf Yc Ì1iy\ظMuhrqCиt 24]-2+MtR#|eWsqק<4>`vhrakΦ<'ć@7f~Ըxu|xcCnٶsXX-TOS/*{Ga #W~`l\EBcv\ҸwP %Z]cqS*4Hɱ&oflo-$,@]VυeTn޸Ut:wr4VPod'W?; l2f\_ |=6׎|e5 KϴUlWz`uϞxPs/f% ?xOP!K\lgv̨g9ɓ 'h'&p'Tv0Ft:O# ǟۋq%. ÀL9`"0UՕU0ݾ\tpSsZ˒ ?>P{N@ w'|h/0)޿xt:Q_@SQȑF|=6?u>pf0w>a:g)'~;vv_+I_ҟKE \$qk>~29Uz5ӌ2 "gM~tlkР870J Ok㣄~ 涪~Z ViG /\L)T|~ȞV]tH@ ă ,L;}GS* c,s}<&|NG~Rs+7~+y5O8eohLtsn_@fkq+ eW'\^ 9w8?1vD3F,O;q/ȂR47j4͛juRΛ}akmуKܦ_>e];vX~?As?  ~Ď6vOnh `iOw@l JRu ;+`'GduQuP~_Ӳ-ׯ`Ks㕁m8z@Ŷ lR-QwɘnC&zᏉc"{hYbX.u2KA iu)ދv?.gmxsho,?i|RL\OPV 6S Q>Ş CWc;`~(ġ<\dڂ_O HW@"qyC9lހşۇW]6m7X RoŔQ\qo1!mouP<:BnrI, _Y9`Ckq_PYjzzbjuxp\%08cu0oԿ`3Q i#I8~ )9:>rI}y>M+1C|t7 ᱞBYIk|3Hl6{FaꅳE}a*l>;a~X4j57 Љ]_~6KXtDM<٥XRSۉ|TrbR 3dM0vSs8Dw}'N\lY'@ v!g;ox۽nGZwcoG<~h^qnhoOW@Vr=i˙w~3TzGR]wKyiHYt`Kn:sС 'wWZUdXw@ ?ef͐XQGi[Tlʮ#7._8YM)弐^3>Q*H× ٳ3vxy G~{~ 2fYvK?_] J3䓐0@;+PѕYOT4bH?C@9s<W1Aqy|Q[]0mXp̰G}oKZ_nXѣhe.V: g3&~p4ԛOY"av l}j.,T~˽whi\0H*b#"ʪnTnVeYʠ(ć!&+1=᨞xu^ۈ7Z>B>~x~|V" /" 42p๙f_?^[bdLwaiF}\Xwn=XAJh},a/9/ǬƲ/wM#_JMiyz[O RG'`NdVN[BFt0Kغ#Zk-fUR< ,C! mTuO`eXZƭ@Bjp!Rj7,d* b+lpb>Nl0σo9kT*\(4ݺ,dl@ NwÎڻ&G{3[0cdpEL GM`ilk>ag&* z߼ɰ8ِ"~| ӨqN|K@GN>??yZfphjD!yLhTGMG3Djlh(~fOw PxT$+H$nߍfLpĺ7+TсJ]#|o7@Ǒ$EOh]YdqZ{X.mlPxG myx%\63;MU/WT*SN=32@ WxxqiO%BF5R#XPr  ƍ^m N|UppzV-DҼG;QVr }Bttֱ".+~4U4) o<:~o\ٌg!l%;0(ʼI/c!j0 ֞0wH _HP8y}M~~QG}}!;`1X6lZkg')b)kq57l8tſgH/hp#^xt"2G'?Ab,afR*,D \6ě@7yUe>j~1уM(U2ҭ >y IDATp7;\+|cT[0;L:9>B.5}8|_Zպ@cS]>ʲS `ɝbP*H(D R R`gj 3Tti@Bllk7m4Y/^" i5u; n2R1t~[?a8 iv; kY@  'N7pyTԿdiv~5K5]daau 7 (̺ZLv We\Gέp3cv ~B5=bgݐSҾktH;nik)os Ԟ|sL[4XKpo6)H__-I#g/Q:̚2D½7)iygn|w%0Zbf:LvUY%X:т SW;Sr"{՟93 7 R(}0}զ(ch,62nެy_>'&-C.EPx7(kOEj.+A.)۰vGQFZ*4 #·af+[2o5ʷjZ% 57!ʿ>>/,! D0_(ʌ߰ZmS8H?69ƁW_Dn.>Pi MMZ8;!^X>](Uσ ݼ~6Vxe6+R5z(Go*Cp ?ґX:uX+ D+{'V~#Lu+7k&x~&V՘?q:8KtWl&)]͑'Qx/ `К|)SYvʏ-3./&:~*dxm23|XںL؝,KqlxB1B5|Bլ&A򴩦cq-<$7e @[ Oƽ.ܒDڑf%v}߻CϝEDݚX%Bk}} 1_S::tf%vVmAQ"VK4gHy1X ֬d27^ê2TOh{q/2n|tw?[uֹw?]bԷB@]'B(*]i2003$4PQѴ(A(PM5eAꪋښJRWWMlIv^\<eL7J gc>_: 4=]͉ p;ps%tTd x=w8yz.QR LBA, V?1XebO?ak9'8n.X1-'>W&>:85kg:!cyer}u0}\X>l^ۓ-r}k|^Gn-S&qeV? a~{o*sȲwNDsC?tC>ǾoU|F U5<4kO8_uGǶ3HÉkxy=( /sFQ/ wvmǔqaV PV);O+0%``@YPeB3Sk^`W\"5Y?) ?]-8yyI"!*8 OWWWQxP 9m)gL'NmnyO$$0i0lz sOlv7Rv7$u>풼|ݍgaBO?GcJLǠht ;+IPmeq&/cvW`vhgE=Yck6^و|R|Xs>WE 9{_9czܾQ< !>,+0 / zT+X3И Ym(n2r/M  CMU ;|Gˣ&&ƏiV]O;wb04 C3 C34hhiCu*5۷lii0rcB\Xvh+OMAnJTh #C0oN99 -@32(鏻Gx;-5ܦQ Bra~'’J[n)ܕ=$.Kdb*^#_vivWWT@ 000{sޡ/\~>D ꊂU ݻ ^|^7!!TsWRcyrdgV֖pn}|vway@A!}]{85[ ٬!=9rԱTHh6^>qJoψ^ʥESA/W8O,d;ˑpZ[ә@ t2tI}5+ G]< /`ؿiOlyLJJz*l޼يD@ON˓yěvI GsasxwvJv CL Kyf+iL\}o2G(((xPb rkW#̽˄Gat!gN ݬĩM z\2ydSVSv-[6a @pё__vOZdSM6Tw@ Ngw zyYuZK]^4yf`H a yS+5)aﲠό EU2n!McN!dj,LlT^:c@Ӹ|xm~nn/g:*^^^¹pdO*%MOGm_? MX˃L m;vR^pBc?X'm802Nl~F;p̟̝~Ōɩ =ؾ~rU-no _Gauo}tB!A_u]]c5^>Jy:Bc յ `/:^}r|:JW^^RPXV{FB `^*|7]T*YYUUlx;VI)7`ɵQxQT뭭>ybQ}p+3J8osuSJP@-B`PSSuGyq%E&jJnАw'8rXݴ(}ߖ;f(E QUQ~VqyuFΛ'O @In%l_km0mݕLgVSaGh=MԴ~s'V{YUT-@O|\j*s', (e szOq'.@t0Bt0:į:a1 G VٍeR .%NlOMN@ 8-n2I\{p,J9tN3 (K`ʱ0G,ONOuHDd3f^_W}I#ɗ>6(-5DoZR$k M R^¤hr3'O|~I;njcO|$#?2N6ֱ(^6%vѕy H kj!=`x ŕ~,'YSnam/Oc$q':=^('u!HH)D0t t=_u]]c,sh5ZFg`( b%Q y$H'WkrS^_r>@ 8Ak*U[0e`Yq%]cdK@G2Lǧ TxE*b^jF>EGamP01^:4\_ʳ)SKzLMWz#Rn2jZG- сJ i W b1ۥ;?9nHv{ +\-2i} @uM~'hUHS%b` PJ~Ӓ"Hybz&8#?M?uflq:?@,&i50͚˟Zn1@ WWBwt Ok^!~bP@z Z՚O[ľ\wdCcG#@z(LƩn!N!S'sڄ+M:W2)dp9;dvxrWǩ˹@/VT2I k{i Ð~;y8<>;'Y}F(GHfΔ̵|g̵|'՜pOt%O~ьfL&kt.\ 99-cbb=t t=_u]]cRWϮBX,;L N/sH|'éΔlx8@pp8w85̘ft8ɝwh ;)]`AG#@z( s$@ @ @$$tg0GЁ ~9t ;wQ80 7&H,@ @ `GHH)aD#D#AsvphH0@ @  ;)]# ``t t=_u]]c C;X@ @ ~s;FOnk4)f؎T6>Q=fҪ} ~9t ;wQ80 7%@ @  ;)]cMqVQHB\JڊBg`;d]1^Of:0Y7.% LGQk{r£NVv t=_u]]c Y8@ @ O N@y꜋%|Cc"TP*jk3N%Gw3R|qU\OjSk)I9R yt:s]`V-19w҄)sB?]l4sғA{^/cWnxp!Y@ @ ,?JNz[4qxGPq3~C}FLbSVsڶ&]3n4LeOviAO+_E &a+ú ҚܩMkuׯgy9SHbSHyj},aBAsvp`^@ @ *&Jxhl>2y ռCCrc?ZhGLkJ.Vg]+Q. ꛆ )B*{O] E3}bse<TNnAf[uT,t>"aBAsvp`nL̑X@ @ 8VHHx|8|J`dJ\ͽ~'v-HYj+:D~Ɖ5B9F:uC+lV:N֡ DX&vMn硱v>7;o&lWJ'wRBJ*9ÉpobZ﫳b5%Zwa>@ @ 8V0V:Q&RiڔuZϣ<{uz??K)0bڅ%-hFhMXfK׽G 8k"ԵԁMgF<:(og3Y7xBYGx͢/P,E=$b!_jW[tJG9H)B>:3V%!p8S4Ңb  4c*Q<+e#Ɯ`ְ|,↔3oD( \QU[Ś['O1`[%^o`9pcWRU9h2}p/Gb @ @ ;BBNJ{FN'MY?ɶxyXE-͢v!T:tkG~pS#gVLv Lxe`D҄Z7^Zz*t߼_}\#Kֳ2[ P%j rB>Z44dNRd`_ IDATuRgO\/hgzYUG$ut {JO 5g`EQv|zap 1aaƬNYn̊U(Q:G<]Yw]P̖~;¤~N/Yi[h j s:ܿ!n2aviS3,@ @ *d n2(UʛE vP&M#ǝF٥q'w\N.uɐ6Ńxv Vgl_LO޷}kD5=Oj_/BiZ;b* SZs%VFO3B>/>̽M|'L%w]½5,$&@~{HEfNwaHjiZ[u}\%O;Gi+9y5 C2*q(`gE~P H`8v@ @ @h%줴c.GQN?Vb|z/wQ/7pNV4[RҘ"DΚ7ͤ_#egb ˬeYWO悂/$ ƒX&Zq#"M:VU[- eAq|qas!=~>}p`72/f׵/^ΕA=Zt,]caBǨR-@Uk,y{zEZ}ݪY8c&6Ȗ.0me01XKT>6!"Sr5kԯzTmnvZ&z·'646?+~?_75B*;ާCҺy<6!x ^WfyØ5ƏNe'W dh@ @ CBI3-:4{-O[ONyC܎4m*PUt*`hCvy޵ݟOPnK{L\h]0a{}hϠ3{HZjݨGF:_)_O+S4dv00w=4 f^xE]ӘGLFKuUnJsY> eV,шvZ?ӛ趞kNV'E QTVU1猊$cW7L2ɐ @ @ TxHqƍ;Wnݺuǎ-; {X¶iZ~Lh&ָje'2P[z;8K/]JyzQ9- }ƥξ=cQChk[ Qc\9N[;ύ\># z K.8 3M ="յʚCZѭ?mv(elo_gW\%0(@OH" LqtFދ:V n*i,_?X)lx\*էTPh&.vD^bf3,yS5rK5;,'ܣ!^2oظL}bV|FgلGadO^R٥WK(grbbZ9eBlbJWQe=Z9TO3* EUM'Ezu64xB*?$ܲm<Q~ym (,QWW3XQSgp&ѓߟ -`y`sC- |tbn.ǯ~7cT7씨nM(.XR<\EjRli4*4=$E)ܕrDXYK/ Wg@ @ ǧ'Mlٲ[nڵK,\f83(_ lQV~=5%2+ZKa9ʇ:1dcV-?v]kמb0KiU*5:n2a\rv5Y~C`|uY8$Z iіFuc!"aѴM?cΠwgve榌35.aw |Njiyj..&,q.eD|%\8$/]&\W $uLONf]Rci%:C:?1)%jk+-~Q3z͢f'_Ay5eKReeEODF'z)_ A0AEՇ]44GdzQŏtZ~oWoV҇zO_T]*ͰH x(Gv&XJ 4nJECC  IS&zwxH0@ @ J Oq1c\hrwV1mSe X٫҄]+`wY[,xZ颡!?+Z}p`Ll*U,H;i/rLuipacU95gh::H)q92۔eڛEՕ5:70sΠ TK??4z~Ѵ׶0P- .]TuB>/6H>Dڜ?=U42nVwjIM!S:o^#1+Xh~McK.v&Z {sKlt"_+*Z-7 3P RyxDM_rg3#SDƱ˱naL(aj6[`#Hao8h_w줠wђQ][ԁR.zgQ~TZ[^"|jJ T# ɦ%@ @ HW |3gJ$wwwa$ kPء>nG$E)|7Ck |OV]]bCMm|Սjia$0ڨ%i\]*4^tIpuuothz4 6! 6=~}\Xz˽|Wʞ]K,c<bGS_-뭐 ߚw5rܿ͞TThtn2HϿ4LѬefyi<;V UvU?xu&y{.}dDğJiNYV>2{,,t-_ݲ u{d7Gj3FؑwFK3Uύ󒋇zPB~0{2Li~;}wpcVlT̼څLUb@_1(IA"\,_s1aK5X*D;t+)RF4.M&|*BX8 qPUzo))O^!J& s WL7ʔ,-տ5|_.dGd?H0@ @ JW %&&=zTgB>l0}||Ν;WQQ!HXo(:ޡ͌p8xEA=\H y.bAL"&P1;)h6[s(P4mGƠi,669~nin͏;sqk&?!v+{ugq4Tn/:N(ezԯm2oP=D^l"6Hgh}7U$޴ (! STd* n/|As-2d({٣eCif#I4IWZ+{sϽ snd9؍q=Fz Lu*:]:T`g@ wUHlvg=7P_g#%ģKΦWj8z=7T*vN  9|өT,Ǖ:2JLgZ~R5rw|&_sp* \J)4≀Sf}htBŮٗT}hPPl;nXY#pf-g8w #gfr zuܯwS8$r;%B!B!؟.,,ҥWŬwǏW*WsN~~~rr_|tΝ;=zT*`YVP(qCBBd2Y^t6ũ'`*uxخ3IԼEgaMԚ_+q =~#c,09w'-+skѮck{":x/zh 9N&ٿZȺ{5:6ݻyy1+6@s;{\m^$^8Lc`tJajP(>sg5_BߤJYa C \vBh$o{wgv`GOYz]ScֵM QUݟ1^$H˯-r ܥR]6V=02:a* /R[nc^vejP TivetR;#7FT辦\,V1/kN ^o#$N|+P/B|zH_Dv_gz{u l%(x0\pi˷wƽc庵Kh ѮkEpfae^~۩Cݥ9UkXU @j^Qڅ3 *sK괐ysb2sk?NL Omb4'YY1g8}rY't2ӄA4,yC_3g[1OB!B!؟8.7777NׅX,faPXVVSєX0\LcqQҲ*kږ֒9 5ucpY@$ޔ0=nϹŀ n2r'\xwzf ϽoZٳ.`uvs}xkVYu3nɄ+ bԴܔ7=5SO-GH Yzh1zƤ~bqzI drNQ;Ebڜ&*"ݤNB'@a*_7[#`?.ggaCF*5 @$T෣w5D&},:HoLnC {B IDATxJ]%KՅ5t##FL.%3V~ij]Q "]K/v.mh4MRr.0" dF: /eTm~UbYv RrSLR{eIm޼?8w;J8۶I &*'k xyy5iB!B!b39۴iӣGP8b/^ݴ+)@0䜲3r]$Bӗa)}C[{|4+F(`XB}1X5u'wyD /%4&=59?㶭M%ffdʃץj}j_u픾.j2ϗ>:%OX CxchgE O _0C@W"~mb_gG3,Q͐>ast 5ړn0̼N'xHEs ǁ*U);VfF1jL0Z)Gv_bCkr%i݆w \vf6gׂ1zmJ15_مzuiex er;_$Gwl+v`m־˻Ӻi+6}.R,4jvj]Dkk'ZdS/%hDnr0<% @HNmܵ[%ωqP(5{֝fLlwvˁ}itikXCƤ]SuG1Cߝwru)MtfB!B!ԢaÆ/S111Æ qƩSh P?*\W8].k}'Rl n!]mɸuuDWNOx/ 'Wk=kӺM%ٻV_#{E szp@nc2~4~}kӛ@c|li@*yOpR[\UҸžnW[Fr8)&T`ǹ4!k]FFm"}v?gU;1cc ̚SvKݣuGS\)0꫏Ÿ-/D:?;<\YXTEȱ^άBT!UCT7=5hU\1[v2Ez~9ü4& Y L갮N"O^, "FH^3ǂ~͛g?jԨ>]HLwx 1+ɣu\*P\Vq!#B!B!X3% ##cٲe999/~ׇzر{Uf=ΞjeN;pk>M^'@ >zܴD&7-~%K }miM6;ӯlF {?Xj~*7Xsxg^XhrEᮆ/÷' }#=SV|*eޚEeJJ.)\@fg&Qq,-|'a\.W(ub5}!cŦ˙ `_.=Z{+T% JWM)s +=dZouÕ¹_w1SfQK6wXP^ķ6o1Piڥ^k5_lz-T[\ݨR <υt @/y+6Wxe_Nϯ,wigV~ύ/rqH)W\O+J*KgMqu% Fۻzz!abZGWwkEm\^v xvh^P\\﫦±9\4*,΍n˲/:ujqz86cP(8svqm!B!BُMLL\hRtwwT*eeeEEEr\(N4t說*g#ﷲdfAK?m[KRU]^X>AO]!ڴfSz3t5eo)qۿ~׈AEbQRpfoQ|Lfc,`~0a q=˺5oFגCNZ Tjn锭SdbaV.XXdV޽W֘,5KK%J];ϥ4#n+ZYoWIoP(/U喳F{V b],U)ْ enIURcTrr;&e9."쩵u"/w%Y+6]m[˝Xeʂz^j?_O}uܮ7];-\XZY+,)7T_ْ -W>q=U&Rk*UvaUYp Ț QT~߭tbJ[QPnKRDT~|{ؗn|M<,^u؂D~>dᳰ'.ȕ.RQ~FF1rR'Go~[0dj=F|ͯ~ѣGg'ro9uU*u<-6Y逫oo/~yAS8۽ێ?ĕb:Ԛv8֭UR>B!B!Ң0TR4PX,"AL1ypaue)XzyFx/oPoԄ{iI79Яǃ6t-(tteTe~zEEQaq8IC"GQpiծ8ک]"gT-Y=r57?vnQ']O߲gltg[1QNB鄜 ($%+eB}$%tp S mg&1{@[: !B!Bq@-*%|im9αҪ҂2NV"7נ 툡_ =KGf9j4jՍKqC&<ű,Dz}8XXDzXI"4,˱8Nrr\yYw˱,ǁ0据\"RdTur@Z,G|9*cnyH )9e;Φ^Ow:϶EV~̒/BNqlT񫳜DK ))9UJs.}X8e5j_||[th':q,BB!B!RT xh)07^>cҮ:1G0 00a]7 }#.̀aFVp@@0m:PK}\Kز*՛>_FzKߟZF?pKfqBi&^ַkm{vn[=)5_w[R7o|~ǔGxuV2n>B!B!ҲQJyjQ(Gˌ9K #Gqfw٨L:޾]*Dj49Ł^k֬)Keµ^\4B!B!bJ 7K-`DG8bpHCUh2‘q|jTJA#jevw@nڣУSmd+ %L B!B!G) $rS(G4W,w z0aJՎguGeAQNR,WK!B!B#p9)P @ZGqYfSӗ=QWI\Q[)qvo;D$*4$"m:IC|qkn)Ƭo57tͱ憮"o̚XsCknc ]3AL7ǚ~ʚo޼Y 1ӦMWUdRq"B‚|lRAo>cNGs l&x~+ͫ:q/44ͷO拯^QY9IC'!) Q3cޘ57tt7fZt7fͱZͱOx[FZޘ5ʊzoKP5B!B!B!Bq4qtwC_4)P @ZGq nJ{D"x!j&B!B!PJj :)P Z#@ZGqk՚ZB!B!B !BHG)aB!B!rߢp3>:)P Z#@ZGqR„B!B!%h!B!B!B!4%,iq˲M9-`<#Šp1<%n9+ +lllպuZc }8ZF?p\4JB!B!ܷ;%0@<&3 # Yu'}l>7or\a!xzӓЁ;B7ǠT2\vս=141\|lq=GwKNZc }8ZF?p\&B!B!N kray0qUTTFŮ㝽Dkw@yOd\^>zXMe=.'?bMkxg(F֭[ЩR~VmfŲ7~jL+#LvrhM>>^]<ęq`'[}4Bij&B!B!FM krXVi[1 \ 8 -Up>Y ~\ӽ2 ?캸 /5dH`u.7W?Rחر.?nnI,OrV'|ik F O eblgWϞͫ '7:%jfVvu1YHH}8ZF?p\&B!B!FM kkS:hndO6M"wP78UVo\7Т"|ξa*n?VyYRsa?[Vo͍4iHџ~ꡰ Α,>}FyCK8"hj 7='B!B!B} <_mp Hjr S @Ix6NO~mfP~wa" vk䣟=t9Vi>&ӟƌW_X7 (a3[naUT@gS`ˡVݽih Fj㐛5տkAB-,"6iKxh0!B!Bo :b9j?O8/|p5Xv>>+1h_܌ۮdiv4Zuz`kwϚl۪^'gxk3Aiiis;{n':wʎanjQ\\?ixk;r*$D&&PU/ +_\Y_}Uꪜ6M}_ss9L7OĸgF!B!BH-ͤ~)8yKN*+nݘ , !4xķ- ^޾}S-y?K[W{i*+1>]fM聏g<:-^ߑyz3)^xسO<]Z}.:ߌqF߽q`xy!:t^vrTJ2"w%8n^I3qP(lhր&*J-||T\wN!Ȟ``Cײ&<Ct}˺|w>]zfAv~&~UZoHWs-C>]8kW/uxz$y4'kX?Ҿq/7˖i^'Ύn+%۹Cٚk=[-,-kVuKWK͝TYĨQnm_®]֏36n1/5K3F3LMݪjzyF&SuhvojuknOCd$MlWQ„B!B!tH:x:U˳n ͱcž=y7_-]&%xY\@aՁaæ׽n>rСvF\Q+ir7}]a`CڎſyQ;hYhq/9,HO% rtک Z}TsႮ;>~NTWeexi͆ I8+(2ObQ$$pd2kǸ 'wpyNo꫕5V@ Y'u!B!B! J 7S6?L?{3K$PkH(xi_y*.peS%rWsǔH8Ŗ][27 %rWx qUTo~ 3mˠ_}w{fOl e>s߶}x{,[Ϝ8=LN-WhGpxoLI2C&12Y#55ZHPCU]j9 ٤$;e?+lȣ.8(lDWYm>+nNoMvrv[;ph.RE{_aa"VƨQ_ysDGB!B!==L o^|8v+-a sO9̐!L` ӦMķY D<9,9"U} Y~x|;r=Cu#BkYa IDAT;¯["[d_le/3\AG ^u׿ 1Y]hM?^_ҽ zdd2ܻnL[2X ӀΞ S{WӹϛiSz nb@TʐfcDPC]n9=z0^#u㓑 m8Ё^sϏΝ޽>PJB!B!ܷ(%LQ uØ5HIEv6W[:~`{!~<4Dx>3#xKg% ,[]y=4OI~˗ 8}XVs"{dH&bVV'HnMط3.m;A 'dxk*c>UhG 8KqOsF1\?g3fn+ڵٺQNyFWR,Ɔ W/!Ctl]$./(-;:wfLܹ]SS?OǿG}®Yt刈I /Ԟ:~kp/s))()@//2O<)Sfַ5k+w˭\}'TT`&Yܸ.?pvFXƌa.d~O>22hek]իL0 ѫ3gƌrΜ<=+0yʂ7flu%J,]xq[%v^yEw #SO1(̲ش۲wp"#GAs ?yhг'}ԸoN ,aA..II5Js=8̻2|9c¢"W}mW{NNĭ[Xa,c+*==u*+|>ѵoӻg\͛jݙhi~(,{%'Je+͛w: %iEX͔,4۫gTF}gED5c*.0rЈfFG9 Z??\UOx1zLW܍W3m.4n:/GpG`ZnݝRݘKX]ӽQ(p Μ~;xd] Xyyس۳mCj[ZՆ)xVcT_8#ô g85nts3&3id4K/qW#0gN`bJ%>й3R‡q|m+-=Ôpn.ƏNo֯i8~+(P ܩSƣ];}FܼO?!Z&:AAk0/o":++)56=mڄyo 9닥KVT?F,FwFI 4zPJB!B!ܷh.^`86>>۶xxa$&ر La!Xﮌ[ K6wa gywi#E­rR3k <oP&n8 29Y]qmؠ_{ݫ|pl6TӧkWx ڴ c8DQÔ)FߒFon*, [`N_sBoo̘woƴiHmnmw|)O:wƂpsCv6VƁ~~=7kQ>)##yΎK/q/dq\ONkq4VK|zHWf ޻bbo;w0֪5+Wߓ:Æat##Gbs ~j5 jҲel n  С"#ѷ/fD CcǢ}{h48q5>䭷PR1w|y9ō/`][; L!B!Bi _JX&M2"HєVۧO*ZdJQ^Űa(*9s&W{-TV͛)X<)F2[@Vk@[ۊWkmm?|r~F俞5qfTs 0ȑ},x+8b8W\w Lcmx1ܻXT7`t|n`^ Er2!%ߥGt6ѧN3K xA­ZOy5,3j38 yyyC 彮]+ ηPbL2/m?yCF wĀzsl ~[h^S}eM+նmáC@v{ŢE< ٰKMךoE?:k0TtvOO9Ev-4l݊[ѩ.Ĝ9xt4""pnO&m|_c޻OOTU%%8w1u* cЯF_ 4{iiG*n܀Lѽm呑s ἾC n{pF!?_guVO)bPm ^?rVܩ>(y ݺ$&?P81Q$rʻUfyzڜk&p[/羹Ծm?rQs2 ҧ-9o+N)%NxmC5g_?\wF~8~\9+Vn݊ RSݻ#2 Х{lu)a -u"nDf&T*W..6ݙN U6>p>%\}:׮zæ ˖[LJ$etzRԴ?-V(tsaqwO?Wf ~ EEv O?k/ 9#QRRaMj0oTkFpI qIIXصZV5õ <ved`rtVѣ0`CCxs>+TsJ5iҤK.]-!B!B!Νjժ$ӽRVtluyxƌ϶ڧoX\Bϸz ~{qa}LOB[vO~.-˻Kyyٜ(L\¿RQo=|G;k9l;w""̶Hcs6zx`j.6loI7d fʕ7mK/QAM sͥ`͕1\贺B^'Ob<\οW=x-^ѣ7jU'԰**t33;rxJ%22t%tX?)a Hs:v4}+<ʶYYPM۲:ϰj6nʕ{Ǐc0Oҥ `xk/ "&11x<$6m޽ؾs.~R%nōq8d2.z~=Jgk͛0ao `˖-ӧπΝ;/ B!B!hSJ8,,[n{pqqήg7n`8}>x@lۦXVM,vm1/O _^'ȦFD /J{27OYo+3-6{lP/Pdj0-`yw.^;cV ymPj/m?#<6M 3fFB3wV? ÇJ8q6%ꊧOi̜#/0 ynϜAr2mÿj 3.;+4u:F@eUum㶉ۄS62^ݴBh8l gkZi&Z}ZZnnDY0e f_]|-^~0x0ڵwFgg l)$kڵ5ks'*+_7Z7]HX&CLL'NWV7W߮v8ryJF$܂ngΜ `_.\P\\- eŶVK!B!BiN)X?k~~ɓ' snnndƌYYYڙO["=c8bdu 14 18;w۵ w0nԣ+j Pā6t`H/ vX;vO?j!4Wk?_~A?QXP̟Kp~[ʌQ#1cn*vôm>e{u8!- >>~|}(%L!B!BYsJ JgϞh"e9O%CBBxggg4\BG\q//t '9._=Ѿ=ӱp~!6TE:%wjs$0Kʒ{/%ys2~gjmԥ^|6hN[cК<{_"wt}fɆ62}exnN)a@?qt4g11={bD ke;J yxm|pp0Fj\x8/^{ 2RRPYZ 7mu#X~?rrp6.ԱcPwMggkn1r$vm3}~ ԘSB=7q>%l0~Sa~~ٳg}||J|W뇖H@ܦ׹s%<<7~ĉҕLIA׮ A~]1$ӦQ`~Zƥ?Ei/n_ הGD[3ntnZbtǤ uZ_=cO~چu|F1\ G;r 7 6`lokbƌ<sukܚ;/9#F|}NvVhz}F;Çe 9tpvFHq?X_Dλww|EUo{'H F %wԣqG;B pA@聣 9v8U.cYQ$k}>;;F3+moly@ wQw =Tho4P!5́^ ؝wB~>‘##3(cx%w ] ؟ ))qpebz5rC3]e6\pܮ.th툹 2?_;no…pHW_J7nJC[֯>RoM47#y9o;-ڲe˻{ଳY~`z|Lsj    !ឞZ-s05882_vwwamm A+[o 9e}|<к=q0{6[YǡOe~3\瞨رl9е?<*cΦWJMyWJ?>{`bg  <6~q'}wH\tp wEu睞W% S.SS? q956, :<SQ2<*̇GLC O>|G`߾p>_| V C曐;<.{ 0y2/BV /C%9>`h!3LJHK͗ƏO<8䡙 ¥z~n U0  Ý'CNtr dgÜ9P_c0~h WVz@Zǁ wpC3=7 V+̛sBy9lJ%C s9;bYg/_o.//nܸ5++k8Z]QQӳqFޓAAAA$ڠiHa*זR]833STvuu p;*_Q? (ynTUEķwX=r t'9# IDAT4,}+;`J#`?޹ix8s/}s sҟn3YMH0ʇH3b > KJ`X \0\ !9o{=j[Z|=̜y\6L#) ~.|hdȃl;w0~7(1OAtuf5Ks@uNqAWUUWBYF,o_`ڴ??u_dd mJoAy )x1(`4PW7EZTWw{q`>ص 'w}ۆc=mTT ßhY@".ؽjj>n=C~صw`j}˧MW_l6XoTx {#`l߾/]h4N4믿,ˮ\ 3f̐H$֭3- w,AAAAA%?uYW_}j={vqq?/%`Thsrr=\aWbBW ĉf*͗viSCҶjiX+MDL^o+| 5%X"ص t:Ά<Gh~Hde/3>>֮b(,WAi /~zhkl||<<Bo/HC˞ܣ\x5x9سA,xχ‰d ,YP[ `} ~jHKZ{55m~{ {̷0/L&سZ[&N!9pn4ƒwe(>Y.-[ӡbmcyZyҥ/Һu EI$>OW\q]wlֳ> ֬YcD"D"DAAAA聦!a[n_ǭ^?Ϛ5K̏twwKwpI"ۤ$3'UyGoV])Wō([v_/dm좶s浽k>&VZ<ꕒ~ÑmsN?m4$Fxx>#s;:_Wn4$ A p y+y睠PF24ugHKB R)A^qբVM?%Lx\l gu|0 {Z㶄ZBSbǧ!9N;s-`hMTV(.};J%̚u\]Q+wY,bcM59kVkOר'bXN^ZvUN2]xf9lڐbxx x40>rꊆc0I¾10{xTPPYE ص j%ҥ0gΘ]^3ڊ ̓yb$9sfMMM?ClH$]w-cHf͚dӕ6g 0    7$@L sdp == P\ I0b DcX@9LTήfcׯ?tյNe9u59cY88c9Q_zLL&۶m%jA$6!6? 䈐~S'i4h&Lڏ i&LOF3i? 3i?f~\H78$L%10(1a7-lݗq`^q3hx/0Fc    D!8$ 6mi\[8u$[-NdpH! "A$!q%WBi̶KN]NLOF3i?F.F3i?f~ͤF'&̤4I1r! VXE@Ahh@.=Ň7*Ujyl"7Yn``f h4crMM$J밳v{`Fk7 Tݨ+rNe3~Buh.=Z%Rg@ERDH     .:-:'gu챽(KP muCOBd``,krC+B52-kmK);g!cށ/v!;59OE^% 1.9u9~"F3i?f~ͤf~ͤ4Ii49௞0h&ȅ4{sܐE S+⓬WnlCPjKIrC{#1bq`ϨgEÔ.'1|zg~Y#޻Zz%2ؗ{ڿZ8Zk/eh8M Yj`sL Ẍ>r<1ŧ!H4&aF,c=wX^*)|V'UtH0-uUKecFD2d\d7R([0"sAM-RF$/Qd2[_O:B_4^X{zwY[T=B`ue$MN\Ym-o^,OjW޺n     Q|KsL>G,W6:-ͫW|\p̳]zc`ow]1@hX 6v\={rbOW\{kW&T*5_1b$r/?0?.ѕN PŦ{ܴ2+,V( aB!7I5‹Kq1dز -(Wi[{~8Q.Vh9G~j5Ô\u[)s6Pذ[So`YdϻHKe;m5y0 WDDZp53bDCYu Վ 3"Qnk׮MFeJF&ULsXbl["q:{%M<)jFkYAKzӘm`?OF3i?f~\H3i?f~ͤ4IWOLIi4cB1$\ݮˉxGn9+"!h}R ~sGXRgIdM_9 #7 _^[Bxl@_Y&}c7ޫS#Tjc])s"s믡(Օ}Lf;i\2)MSef]-j[1$1ጙ #ʔ ERj܋$J́/?%WݖR}PHG'l▿%VLbn;q:3W$|"1o66KW{\nD)бsT8eiʔ n;k<|2twX{:剩Ebߞ5>3uiFAAAAf 3H˳"cAm&aѵk:;.hmKmd 79%HVQZӾykiM8cpwסNQ鶪r(XF^:d2yܙ;mW>g8|pkO[{"1#` 8]/W8-CS긔);DްÐ뒊=5{_jaD‹>Su`ؿ#\7q|:)a@;T+ /V'Lý² lhC؊xpw4~8NTWq㋯հq3L}Z۱%MmGAsӁ1rߘve/ؐ0EiY{Ec.JOc]rrDx?f~ͤ4I1r!ͤ4Ii4h&7r_=1a&Lڏ i>ark Di]5CJ6fv_qDtx%FANE qKTZuxUddd\?/CzeKwhjQ|'O: ?>n%^˷ , >sH*emڷ׫X7˫Tٹg]C.N_mGrV567O$m`mS4)+cG16|pKergM(BKrNF0G}~H"? FIǍ-?|or^?;IrjJtEr:ۏvZvU'OK[8T_0"cΝ<&O:I[PZ$JuB$Mn2%CՉervѾe-px5y .륹S_{U3T">װOf4'ڵ;]t\wE}a%5_ &τc?OHp,krhka6T6?p")USV_b6tjk*Sf4 7{憔 ُ @_'LWH]S=H(P: W}s.I} j]{(^//l~ͤ4Ii4cBIi4h&LOo䀿zbLO#  SJT=t[V[!.lЄ<C#1u=Mn'*Ar)jơ!‚$isTZU9Q$/64vuuϳ32UYocȣoX;b0r/+3sW[[$*5_*)h'wmAkK\NaԙUMFJ_T.Uq %֦ΝοX[PjػwTM/6oIS\XsUwۥ_{ OΝ3`r]{<ދ}0L|abӵ{bP /1s}Oc*J F"[i'>? Iժll'Y'M IDATڍ#J'vl |o?\U}29aOFǿw    r GINf< l=hO>C ww"S{4xŠ<5Pa*uKpp9V~%|bhwW||៫U=5/100tcwm_{;#> >'iF(̈Do__ {vlrZ̴iTvޚ}2m¤}YSA[\~b".oxDǖv$Q'TTMWg^uڵ>R=gNsW0q%͗(pȯUP{ze7 XƩ@T1< Rae6zlʯB!iPkT/8אF=R.1󽷷m-QCo,>}?ְck@ӷW񈮤ë>LVfM]6]nG],0L„궍?k3"1jƅƯ>X'u%ߔ5)Cr47۶={ɕmOZ>hH<K .{p_by%p5ynޑBwec.Di1^ͷz~ZFLNʴ qp[ޅێ(SiYQoMv{k~[UwU9mۼ`.r@XG)G9" wgѱ]C$8n͝{AEu߶2#Ν3R*0E$q׶*Q^^H?vɩ 4Ii4h&ȅ4h&LOĄF3i?F.ٛ`g qflr\*@]]43xRWW +챋aKKK čnLݸo_,Wڎ|!-ЍD-.a5D.&V*]e2"X{i9g.:hk/#a5wlJ?ivj _oL=5>Cv I'GӘm`?OF3i?f~\H3i?f~ͤ4IWOLIi4cB v0/pdddz뭷vۋ/x;~O8nHs weT޶D,W ?}˧TNڶ^ 7O1|ֻ{f0e U΃86 pe< da #iNuÚ-*+j29-kMfCnWbsZ7I'1"j -#nȈb?_.Qk ;8q;Jx$*5 %iOn Qx68f\"ݔß^^[Er?AJ.:vl)UiiOfҲ4EB2$o^UOaO1.9u9~"F3i?f~ͤf~ͤ4Ii49௞0h&ȅ4{ڽ-KGG0~{vv\tѩSN6-99ٳj*ךҹZfݻeߕ2%=H{'wBijm=#\1Z ah8C1]L랜cZz+7h`vp,j)ZhLn(^9O&c HxLs8'"|7:׽?eZ]/Kt#'Ll:v#𖟿I,0n*9cSZy tl1-7kĬTusuh^*5Ӱ}*%sHT||d`DŽc[F0 Sq\n۹{#\O¤?YH0qYhzNpB+nΚM_}?8gػbT$dLmljHtH*s;OAAAA9avH7++K@GG$$$l޼g;w`HHHP*W^y 7`Z駟0a˲F1..N$mݺkeT ?///(~rEW,VEbcд#/Ȉ e{. b AAu{{_vbvR p M:M"68Q8tcS"% ERY w1bq]=ɲQt`ԏ8诫^~Ҭoe8~ |s !qWBi̶KN]NLOF3i?F.F3i?f~ͤF'&̤4I1r!ބp;bxڴif{l6[,{뮛5k9shO<ŋO?5kD0 ?񷶶{ p7x= JR+/ڂZW'Q(`mdP=cU ,#vEΉWsvѢK6]"]D _x#KL^ra]QyipZnxc۶_ ,J,P:Pg<#vy|<>}*SiIevs6w{rnuFZoĆo['Cײ0"Q֌S?c y|dt'`wD,828۶ER0d\в[Wier:?޾7a@m"LWTP=s\Zvzuץ755˲MMMŋtIoFnn.EKEߺ:8neeeHXGYν]t1hآ/6E%pc ÿvra0p4r"]s"ĊrqtrԔ[Z kZʤiyol`~lܱ[~k2s.8,f~ Y5c,.ֿ_~/]{{徾ڶjjsS8?wlpoE/ ιLҔ]v#*g.>6eF\Na9GׯT+,5:ZsqX&{vdI$oVe@7F39o=Qݾc~ZV8̦5_;JRGܦzg[k ϹTPU\sG%;̃r]eu!e}}0&OeR,.|q#ϸ$d6ĔuߎXޝ[FfW|wF!FjEMG!ԹysfLe6tj2s29pܞk7 oߐX21dX*sڬFeR\3d\]EL_I?O:Eſq    ?KW^Y~\.۽{woooool0aիv=i$'pfn&)... 4_>![\;*UkFzI`9h8I1!^)art [걗 O#Ʋg']aDtǖ3O-ʔ)^~,Luܿ;/]zR9/lo棌R3eq@)k+|?BBX_*5}g۳V\s:-KvTZzUxptÏ^~M2.>VˡUZq4|dx &bC4`n~dA\F`{/](!vKo^QRymHg]o4Tu|8 Ummد+*hص9 ?$.*Q^^H?vɩ 4Ii4h&ȅ4h&LOĄF3i?F.ٛ^n{^aV-99_vhH$`0˲dpq08p`I40<鰻v/i2s%* tMCMU7Q,_] hesWU:_ϗ52V$:&SQSbH柿._W<{h8Ӳ$c,[WS5?sOZ߶^WT%M\G3{o߮m/M*dj?:ءwmn鯽.҈%Vp凾[+,4CuݱuuޙY|lu_!7'ϖiN/Z!j W? 䈐~S'i4h&Lڏ i&LOF3i? 3i?f~\H7-ͣr90D.D$ؼysSS˲p8{D"`=}-pt 콅D0 MFHZKh8?/\^ȕR8 uMijVwR"m(g6bga>0f9T;b1k\`,Te vn9Rp@ۑ9=D8aiv2On ߥ'V:lXw>[yG7R0byݷVc_HF3$ ? 䈐~S'i4h&Lڏ i&LOF3i? 3i?f~\H7-ͣT*j{-[,X_?/H/ap԰g`0ѽ.@ @q8PW͡3 8;Sq ӲNhbq&C"K]QL>qaD' [긌i3<8n&!     /];yyy^zoq:}}>xR1b:nܸyQQ^7B4l C1$j kYivۜ%bED.]% 쳤ܺ{/eGyeهzS*N`0l6~^Q~ԩaG(ݵuzG>51߯FIelD+Vy> oҫaO1.9u9~"F3i?f~ͤf~ͤ4Ii49௞0h&ȅ4{ x0@:z =$FM Mx0'c}XGq"jD[F arG556|TCo3z!4f%Δ IDAT.'Oh&LO#LOF3i?f~z#f~ͤfo%̣j@7MNNNJJr8ljbXڕQ8///ڑabw44cx!$~% h     G8GJ$9Hk44cx!$~% hECOSzӘm`?OF3i?f~\H3i?f~ͤ4IWOLIi4cB!2$&ޗhhƀ1D[ HJb#ϱъ(DI'GӘm`?OF3i?f~\H3i?f~ͤ4IWOLIi4cB mh$Jsc0Fc    D!AAAAAAA!YµW[8NX˶` M0h=_ Cl96Zܱ}C8a7o N 0  ".nƀ1D[ HJb#ϱъYAb?Urr\fZ2"6Ɯ69.{fBi6i?1'D˖q' l AAAA$hWC~ґe7ZI#pCI HB5+:r99"AA0pc艆&` <O4Āد!6@d, h&}j儜_v䁏 5'@L@tDN:'!$Of~ͤ4IA>D>* \0Dho8c{WA6A3n|cٲe9"?qr8 tcH$X,‡Yb   H#LڏԒ 939cC5Q3gl4fW #'r~ LOAhǐ0˲"(??_ՊDG,>|eY{GAAAe˖]~֣ck  %<~xL L&S`,'8Z=JH$tǏ߷ojd[AD \ Mx0h=_ Cl96Z  4f~ܧ\N99j D̈́"ј]僌@ZPk'dƅ6-[vقG]qh!4Ii49 } FKӒ ˧ο>{LqjEd MFyZ^?^5.9ki,aEoX   h4c>99VfԀpʇZ;!3mOoN`14h&LOo 4!ac0`/ߤBEdb5u-Y3MPF$W}끝~8K'Xl<1/Y44cx!$~% h c_Lڏ{i5țf>^t.t3e˖]:" h%oxDC䘓ћX4Ii49 DrUg_ޱSι`¦8xʢS/i        HDlpW9)#H}M)i(ؕ 8(O+ r<'~c6NE.0/bhh1DC HJb#ϱ A#OB0~[lٲK?qO.r驪9>%Rfȃ|:^'$sH $ќ9df;V?f~ͤF B#跞v>w4lt:RN>/»Xٌ~}g sw{ yW5Jj,1` <{`slAAAAA#2C&I"&\e繙ɲ,l~x1 3-ؔkAAAAAAAQL}LmunTsғm2-cTKJXmRFxAAACh4cZ c^pˋ=0zȃ*W>[_]onH9FϘIi4h&1 A#2,#Wz ;6sok~; rU9fC&Ӏ61#c2Lƃ       2Ju8X C&n Mx0h=_ Cl96Z i4cZ dDMosr/`́c87[_P|y_qQ-[l\֡$2fWy}?Hb('J#I:i&LOa˖-AD("3$,qVYDmټ' [,Bou(8Z7蓳ƅq[X:AAA-G#c GY&=I}>Zd3A$T2 QkCB%:gVEgT$ P?YIDw|.I޻dɸ7L3 )i!@~iNo nUU˩]XYݞ5yg}3knvAV D7ޝ3mGyJ_?{]V`0jQ 3{S" *Go[g+v%AAADхr?EJG[Sho%r)5MmnO R.7}WgBq"|bB,]c?=+?=+?t#Y%l4B W~yH虺ɫM)MZ:lCvK8a_3N\N:G=NbcP   Be1a čm._f~Jz~h16).&{&LȄ g0$hw ʞ= XBqr_za/ޚ<[ FFu   *BѳQ9#ŏg+ڛ,k'MB|/YJx{Vȿʍ{HG9rEJEJ] V @)vj!:cQ_9R/=<\uT!M]Qʩ` /Pj@ ;8_nLAI-/JW` rc_R<|9X^~W&{__kFA fJr"~?űG><xMVbԹ²q*P:3  \躊PTNɽrxxꚨU[%C'=,]%,_+qs%C =+?=+?t#~[% @nRw_#Eqw##_k>#  2QL+0aR"qcOs}os{>SN&_D&qLY-"1_R}֛4{7XՊǐiZhJGvڨ DŽ_wIwDD<&ғwK_BWyx$$/? !?W @1{6+V}+1h\#;/92/GVben?=%69;ƜmNKWK_ۅ '3щƉV   LEJG[m()~ ͳw"b%nL/=\[8U4m|Ϥ _SH9 h~)rG#xBWyx_A$X%lXL&/NĈ]$MG#67c'reE{+aKNb}FCP8aä`hj@  ?p\sxA̫*zIJW.&"ٳ9 ^(ٻ,%{AjP}_x ׿Df/R#eJQ"A5Jx{qNZr_ rav4SȳLxL|q? *Zks|줜bCӞ7+7T=oZONI|n# GAA|*-2֬YOy;ƄYn[H{$w_!7ɜ7A崖r%[ H l=h[8UÏ*s%d4؎ r)J|@9v#-(xL؈ǐM6;m?7 ctR=F(;[q1SEJ|NTڵNa偏 WNr)Qcy(G MV ,{ԩ䄄NcFhT䃻.b xٿPq`hj@  ?p\sxAAAAAC`0,z#A̱cjjjDSGAAcC~w.%qk֬eO&hk ?-]I]himr鑔=)>9Ynq#cBfIsm's~d-+ߒ!ߞ^'!i8FNz/i)ZrZ.}bH&r\"I'kFUE<&P|:񡝛D\!YNN~zZ~D;kI='J^%c"=ھ{On#DWc%ߥGQS(iwx H"<%       8%   ]aqW\о\U6KnLz y I?!k'cL()T2ιFIԧyML1z( ={A$4)aAAAAAAAExJ`0zaƿG+aC9CPCpkؼoo|\GO?|~̥ic#.;q~ rkf"oK r%\"ir;|{%cID*%-"InZʯY#_ ~\# gq@ 4ϴF&3韯9P>i#VzLHzM⦆ȡ]hY%bRz_Hޏ;4{҆f)=39~h; y;*ȫ=-z=|*%{.y'idOr$[ʷ!dl#O]dȘbN# OD|?w@Z?WmD !,RTV5RKxN{d7aik!X.J3Mj0VHxjpF,/|&g.VG  ] -U"h?,&w9)M8 qV     tv\**// Ř1cFqI/T*[W_X>E9yR,Ær 5͇4϶-< w\jgO?k[)!`q8[ܩU;:}YWj5C~nޝiWih Y]0S%W˭ a)C0UoΑ~|Kn}r{%Ǧ:룜2]R𦖪#q#u+-g|+y).Yٽ\tpߖl*!3aҒz}HCٔ*;jx9;iJBC1Mh_1>]! Hҏ.E[x.es15Kd`{vn]8ܿbh?cQR^],bYe]뙓{|;mM Ui.OIL>Cj8ڸmz ~T*f4F4tN$QkT(*ȞN AŘl IDAT3zAs1s) 9H(ųABOn srlΦoZhZ;yW a]m.Xm1#Nqq:{xFͺF6釃c=Yfe"ًSfi{ddVzbtA t.e],dY˥ӽϫ|ܶg-[q~\REN`ۉ7OXc0 ^eOǏ!xWNi&h|m yO0C-oK##ֺSu7L&r&ؽum{-t:m^]Oﮮɷs7`XhHv[|͇{opt~?mEp4FU1!R@~qpO'KJ_X OՌF VN$dZ-0jYvLyl,eܙ 23sM:av  a1s1s1szO~ 8w^ʁWh܅ֹXbkՎˮӧ\vnPG^L>23eLZ/tX 20 ڕ^_wcۦs6U?qK+&'KXoXƱ!B"'ϜUf޺%nd-Niq>~bkuNѣonvF/\ _&)H*xW=o[41>lI)yS[Qp t5g6|rf=|ݎݯe_Zqnij46e̛=ZMjߎ_;*bѨf9xl㛯pO0LɐHJd\V.5F:ZwQ=Z. ҳLybqQ̏? i4n:c˒8JOIv:]U1 @`nsϞvylvů_sC14nw+t#.^hY߸pRRjF r]8ongS]Ю '7@F)"R.h<WMF3?,c9c9cA$$ڠfi+SeQ m|vO_z0C}Ƅ,VxXO-NU&+"ww^+YX7H(2Zvk֩5*򨫫c03/Ngx#q-#MSo][0o9]]9כɣg$t9U*wy4ФX"KG ]ju{;L1ie357>iԅ?]Û[ɹV# `xaz;||M|Agrbs.pף0:.t737n͓'υ†P[Օ[h0rz̍6+6we TWv봬s3`ﶚ! ! gs ̉QQ1zaz:l8DhZΩ~tzj!"R74`on=1~,7hΟظdt{XyOL6.iwйS퓦Ls2i0hk3x8zUd~h.?dZr~I1F^jAaiR$Jc'ts:cAAAA0EXFc6oزom=]Ц8[|={  z: cM*[ٓ>*`}X YȨQVwʽY:a*nBO_SڬXFWMn%W|׉;Q,@q5pa^l.'Rrfљl_ 2&NZk7ҏ빁6E`Mlبj [Q^rp@q}H~ F`߶齓PZavpiI`ѹe.]UtUðlg\1fZY(9=spTO-ps2~p䨁~ۖ]]_a]ߞ5{Qv䬼iW__}{!f/,DUto\wޚ'iihcHL6=km%Ii61&Cl\TKzf,8scowPodiWYG4cWdps'Doc9c9cysADf.Xih|xwf-sAbtʥm{N3śH@U3v_00>k)'/M^tJ-u˻Dz=l91icZ+Nw{d1,W>ώ\.E^-+?!M6iVڤY|Ϳ' Q¿KHN|_%Ÿ!v?]Z`~F9jW} pb:cdEOğEqS63=h̴΋1DEv:G}FE|9ǡ_\49i=jK-+u>Oy)+~%㓵'؋֟>Rbr d["Mz|UǕaF'7`*}$iiՆΔpnQCM\:m=(ƒe7֍[><"tyr,:]#ÜFŰQr{J_ftص>QVc]4+VLZg=a~5= ,,|*gS9ſ|sxS=j[m6R]NUkV sgO5e[0U+ `nyȈKQEN @]__;Uw,;y@^QUu Tw`oP|yI6^ :>;&E;Η¥'@Gk? 5@~xS/c̣hh.vuEVW<ÒωSgiY&tzM$8uE\ >=8a9c9c9AB?8ڃi bnO=hT. ^n f"\^03d,juhL '|B?(8m:c^)>rdUqitja5f+!ɠja> ?OtPK\<`mwuF/tZт_vRv5xg[߼L uba_^VGu ( 0ϾH>Ep>r,U XU+ioes0{a1JSgsזuQq=o\w٢k8sdWoB!TLv~<Ԝ\ū{3 s@cїE]ן@~q"TŷF_FRܿ404(6%|յ7>nE b;yn3/ңdjFfY؁MA+sjc9c9c~ S`0^{Uo޻z?YwtUw\.%TA[:Y<Ҿx7 y~o?`S¬/)Џfa!m\[mn{{O^[}@o଩3| .b[-x@- C4 7 si zt \0Dig=?6|q۪4ȍ9W$~?m1}w_^t=f4(4𞮬}^RmdDUd6~5F,Y4!,!"R s,[qcPS_ ^0o iG#ohޫmVD&%c{֜GFi6[es3r v'k+܅3f5*ud϶jqT{cpueD7jnC垯Fo茊ki4|4Ԍ6f(.6;N[z6A?b9c9c9ABO 8>[:ܵg[^{.KúX祗1:].N̢Ϳ}R뉕!ezlNiom1F;zXVn0pקZLm%I~Q;+_bڠ&bZwŦKxSa?5̲XU\\6$Xۿ{,ݰW*^qowN/wNgub+}KnZ]]tpdz ]1yD ܔ:bwl&YݾvqFhO0.6pL!)  ;Ά~yNgk݅OmVt '%6ţN+;&<ЪE z."GOgJv'*^#mΕ75Nnz.awxo=$DgXA3, h& b9c9c9AB?`0vK~Ϝ4e.ZZ_|3[DgS1/\D9:c8], Qd[vaslVWkZAg̚6gk5b0gD_7h:}VAt:5˲9g'$G"{MP3x}vKEwx ֽu]ٹKtE9]#}- Cs^O^ˎ56Zu$Ŋ+1cZ臁) Ut& Xh> qܕiNlW?a4 H7}M썆@jXR閈9W=?/?sf +~o{-:pttYw-ѱo<8}w76'":P[Օ[h^xu~eEc0yʼnp92:݅K+3GvmXweN4@GKj;,uLъD7smя5Y8'$iq?XWp7g#ͯm)V1x}bWyk(yżƺ)3ҵ:uq@cPe$^9c9c9&G Q<%jK 2c_-[y\JNYwt$Wjtzz,G\.֠G"Y6ŚS\]##g3J~nψN,wE9rx@|b$ 9eF|z:OSBf84-] 놄QTU 4]cP ]TS[z?/|^o U)} HqBv{s1b|g+-R]wwDž>;q5 g+e+1Sn˞y*2 .c_J1-k\/5*F19춑k8dn0/\\[,0<<'Ҩ9ssH IDAT;i3np֋On9%a 5榄ȥwOx\;8;/6$%)hm 盧}+q7kN}wժڞ7^/> c@܌e;j+g}Ĝph;    ܑArS4q3uwL g,n1}swOz V ZL[ٓ<)0o)9Zf]AIKogɬ w7YSghXr":s}_3⸍ޮ!d\D響YmzNp7"P|w{6ۆ/Ϛ"*K5wV fOGFfޭ$h܅]m K~ݛ־zs~]T(`u[z`[GѪ?c7[ʪ{>zR|`=eחUK-R g/h5.]U_h\.Wgc"|hsf"!Și@͹.N5өku_k1E$E5$#X$W\V"zk-ۻds4 lp{ho8NNSh;xyAl\6vhu,4};jV\kjEa~I5 ]0s1s1s -) ?ii08U:6]jL FxwOY:wꂸl|00*U\JNS.ᶺJ&@SqBhk=%fw}{v)]_R>oQdEcIu m`Km{}QTp/m>0u:KRƟsuv&wIU?lUWU~ qH,-YSmx{޻ۻ_ˊ`Ёts>v{7zb~KXmRJ[&uݝ02OVY;8t:]5_d8~F> cdj +;a<ۧ2 Thm V4\%řК7+5$nϫy   *΍Ld+*%i_r^g=|?i->l2PwqRf0U`ܷ#"swvBu5n-6Nq]e'6!(zmcgms6 ì Uxt+gk4lI}MFN6T 04: u Q,e[ъ9yκ9s\Q8c^fOPJzNfYx|C%y Z8s$yK-#˳, uRھ}󹲹 pۗ8DAAAA%p9Jv[~ 0Ίsg7*Jrw޳Q8բ;+{[D;>|}ݝzfҴ9|Sm/Z{7O=ki9ɊZl/K \R+~1kdYܮOOE3W=Rk|4c^aiS̸ٯ OE+M }uI?1uC>{x+/?q :eXֿq;}se-iDw&?&ƭhj9qaǦ+_|F[7`mU@jFD}L"W~cʍ}=ӮÍGNCvlwadԸOdhW],+!5]{;NkGA²x~ӣ|ܣ|ɥWZD.rT(c~9ui[+}kmqf'|OVuH0v Zv!B֩yé>*6f ZKbu:gmq>(y;M?yVFMغlN9¨Pr 9hQxy+npS]̜eΝWI*AAAAA@!0ij(7siLdSICA޵"%!:#U+;_}r~qb;Zs|gÙ5'[O49Xss?=[]e(/Ejhi;q g*Z6,/<=6`46|?E/Lxb ϸ}Zm6v1}V>ECW%7̽J?X8-}FJ2ϚNn]  J 3"Kc -۝UOnrO q>:8{ڪN`m*NWRj9rn,}۪?M@$8`y$s֣A[GIYҕxg{N+E r?u>F <69Zhǻ;8DGEUS$f)Y- HH"Jv:W zIFJ‰[Y?b]kr[y8>hsU9aY0,.Z : l|{㞓[6{oyVT m:8в2%bnTv{w_Ӱ`` (CҘ4*HJ|^oCݛr%$EG%^;X˕+os:CAxvjuŷSVyUV}}988`rY"NĿ/#v)99_9-V\}gbU]_C>QmoI/g ; !+˲NjnnU<(7?;.ӂ/^:.>>>555=--3=%'=97#93=%#=-%%%..Ysqrф 9G?'S~s˕={W'v^#棹˳~_\9+?sfzRq\Lj0n*ɜ{To{ޘD55@_8` ͸2Fuq;KJb{M}FU+ c2,:`߰ 9s"6^ i/_η[/)\$s)$=+$k!|^rŕK8rV/An %K(].#\"݃w@")#qK:k$▾\Fv$S^_qNKٖ^"vZү`Rt\DW|̈kW0_ A JXmmmmmm $ * S3`uַZaQ3-N¡ׇ?jS|ӺLJ_ g9}et\,b\.vr.b],rXe]>veu ],˺jc3 q@Y*5#dlQp) LjWO/^~P3,m9f/f-rxg{.F_(RM#x"lb9m8BE\R(V_-RBm~|!QY@nbEk/-((ciʕ' !4#biI9e3Rc'ZTp%ON)zU6vݾ}8G9VqĉSO9sꚺچގvKw`Op_m94Aݪ2S&'+vqƾS['Z >4]ټ)u=~pR!I6^dw1///;jCs1|\"j8?&J%-EۦtQdr1%Zϫ GY?m<=H23mD"=($zAn.0sJ䩑Ax6bDnYv/P j5k=ĖUjlY]W?#5,˜0?4{W{J{4T0ft=W^O?YNFCҞįuW2VdLh1GJ9ğ~ynq:D1W÷]mABUH`hjV w^t +Dq# Ay`\~h>i=HO;Jߏ%H "뒫P\GYܿwK1OըrE;,_gZ 2{Pg5~H׳/1kO@TE(_nLAՀ,{5'0Dܧ =JzSnAB\%   T^^Oz%gI==Òۦi<Kض6_o)豞1o/d-cȕ \X-<1锖G9~#H~|zDX{Ri㓦6ɶg?~Ƌ^?7^:Ǐ"rQB􈍯Qn5^^\ E[av. 7z Dww ; y+tW"=wRXI69cGO\9߆|xs9/y=JyJ!w 'SyMLz Vt:+AAA {F_J('"ܦyQPtG[n?Ҏ"_(ݏgtJ?B/l#ޢ@z^.r}/ ʍ)tr핎q"ʝ|{XJ^QowLqJY%ER$Wg:rAލ7DB)~C.~.#q:8P? )PCi~ rkf"wرaw.%q$-ܖҮcNAU4ϴpHJ ٿ,ɸ1!uJO$ˁ#Ï'߆oɏ\ %hlqDާ4=_Nř7u{OMzLr#C21N=Y6Ү*?7I#ɏY8WHӮsA%RWB&}M<ɽgqX׫SzLGwrm3a.+).=bdrBiL;HoģABU,2 /n,JAA oB%zK[<<{;ȭ?ڏHLDʥ[H.^EEjtG[hܿmKܽ($E-w=i;4rK}/'³Y/ܔ}GĿX :D<=+mEscH*~D }W |ptqqq LAAAB?op=ީE'1i^<&:dKƊ+{rR9\ GddVZLh|ܱd|©"GU.Jh~%DŽ,NFZJQNemcHx6#T"wל 6# i "%C'*Iv0ӝrFO44RNJ_<ABU*T1****_aap & U`8G+AAAA >q}p8A~ h Fа,s

Z# =8A~ h _0JG[+׏Gr=.γrqR4or˥\Bq),(+=޵Q {#ًH<8BYiYiA$ U111V5AAA2DrY |AnSgқ/1K+R7>g[/=>k#  "p[%j{zz&Z„o 5p$q# AV!YitGгrMYP%7i6ru^/%Dݛc}?DwJ# klggrAP$ܦccc(Kǂ 5p$q# A&E_=?*'˖KXVA&bYP5w_tocN@/?shȣܿJo{7=>~b"v Q^n{T]c?=+?=+?t# !3%l0zVUT.kpppxxÆa衡!Z6**JѨT*r&B,8PG0h@W!<@$(z`I=+\$=+J4R>uB#  VUxq1I͝cc2sRz-M \ɂ+ۺ9!Yit /)W7IM{{t{rY=%J,&+q;g=+?=+?=+?t#is@J;k{ju#:ClQ56 IDAT_RX?q_GFFtu5tr%.}bq8z>  M@ !4` |Bb3 W싏>BN|}{]jfƜ`rNv =TO 'x@; H6% -:xC:ڢ` %oؔ.U|%QQQ*h8IhFFFQBi \0j045p`Ѐ8Cx92*2;lV>rdD5,+?CγQ9G+\$=+V4R*NɤB ]: RԏXp]eݟZkcյcV .-B I2z\iIf&3|s{Ν[ι眐Ew;xW {F7d"x0?r33n,Q}ZOCBf>-a٘|w;HRdƱh>OD p& U@ ~c;wPcuR+'zŧ>75&ڸ³OD_|fh4jHd45,Ee;6""CS"b^uvn-nek+G]:)l9AUT6Xn8aLUtlQQ "e/˖-jwOQ[={zbڵ+++(>>dQZkX|0-G-XJDwΙy#|XqYA{E[ϰ׺I,WcRq36:B+a@ inn.//MJJOljjj<8ȁ9@ǃ*4:vk! bz饗gŋ?gZMpDͲg.QbbRqqfB6ɳ褄>\-'u˖/Ug=|}z}epBA̵\Bky"to{!9b~mc&mL~n09#1rGn!%4՗N֦DD&i\IΥ#Jf\hOQ*Dk4N(xQC]BS0uU*jl6Fܡl$ U@!r>&B)#Fbdުm&{bf0o6-:LDV Z"2: .ߨh22V~~m.d=nFuSzv\ H $I`hP_<~Ter|*{1 踠M gtBP*u_.\"jl4P *8}įg?~t ?jETDhh6\XbD[Y֫HP~ݖ%r#saM?/r3w=)_9!slG<Ɔ ~$FvH9@О=X֓ H=s@!"2ZܕZt*a$p iFSq\JD& "bl٫W_}%WAeӓ{5Hl>Ե}=ꚪC,ZNq\CmmA̹<-˲Vٯmy8m o/ |dA&ď?r3hc5M:qM& UvS%5 }JfYfkhhp& U@ ~c;wPs=vHe"j2D~SRhӯfj"2[LY"JؓsVK}tp?3YXZvz,rG^dȭAkKd>fmԆN;FvH9#7s=w./ "}RAb}ҵ&paA*Mi\1d?rU#7EFnud`?Ap(Gb`Ǐsk{""'vOMxYc00B8r@t<دBcl玱rK)k%U 3pH6M$6(*Z+a9GfopԔ>P!|w}ˁJE4ouڮD";>2w6x$xCy0.\p߶VgE_!}I8dmW'A&ď?r3hG|SsUٹNӣ/ᛛ,*a@ɲ-]WTr< ) ) 6߽iK%'b1kˉb,OXt|YyL|쥋%G=t ufQ״O;H/r#saM?/r3򴬿!-֖}C 1EW-?kſ Fh|+mSsmEoo'x -}DG;̷/徕D"aYd2x&,uo%V9p'rUht1ڃG}teI ]sj{0w~B7||H;HG`!Evk iuޗwCP#o?<į9iPFvH9#7sv}hmfPBD|sqS%񚛽u6 aa9p!x_Fc- Ԃ/˲A;e Ev`g~hwy'Z{ޣ}9ɱ}Uy񃽟op"9#1rGng6%R<%k0l</--MRxY:Xl6N+**l @C0Ih"=rrY#;,ucFo;HC{xv22{x(GvHڜH&Y?#;>21rAAA *+~ŗbn":|}ѣH$50PZoo (Zd2:uʱV_~t7`0HR4xJ M'}imP??PpaZL{'pYy{'Y)1T gddRwz;J#+ ._R%0M**yfcx`x968D 7=BEnpPT*bPIp@fѨfs{mn(>pbfjeY֯B0 +/=J FrEoU_H҆upX$rT! y>Ve2`L2hP( rjwF,,0,wlW> ϒֱX,&`˲jD" jJ`޵B*Q%*4VbzM=*xrF'}c;jl֨o򨪬lbx ׹=fsp#`F(* B!e2Yii)J];ܪ\a>́mq:pmn. KBZfL&abbbT*BD4lhht&㡭p[D'UΖ_NDԵGXpEԣp"ʾyj^M,͊["#ܥ u,UVgwACBU$ NsFEEx-^ DոwBU]P_'vgTF0%%1)v6v> uIHP1 5wA09 Hf~TFi+pn//IRϾ, ֱ D]d Le b6ʗIsʲ NU;QWHj_ܖ!\@Dminnnqp_#K릦&<aP揢FRa&XFDRM'CqLƽ%;wuh4I;q~G E5}j2ڄN`+1_ L|T%gTtYUكL(;͚⼩Қ}+NJBd6TV\\q{bY>2W+@ 9x' DT{iNA$#JҨQʈ(&o&qJ,&];'v);*8MW)k%+Q6UqJ.{UvYcN,vYMiѭ"Ipm+%!ik\}pC;Ʉ<Jϟ{{^?Ε\.-wl6V5I F77'bg97y?mV*a)Zs@LD8i^ʣJ:}P< 'ch{FRFxf_F1nCX!9dQy}JE(;ج3[tDf ySkNJB$b V}]T GH&"5Xtt{Z˜s *r\ʘb&+%1vRnU D%;c$Թ>!C,9S+@h^ՙb[3} µ D'dy<}0>*0#WU k4Qi4nJ鍵C+.6ԖE'$wWh~wpWR}H,;/*S*٧듺2UWj1CX.guعՋ1%uGõ L z:@8*a"J] <[Mk5qngzolWӥSFKʊRSx@XמY@9U]XηK1W[6UR".1]d w1phXV"o̔S:%,$>Vtu MEA MfZ}+;gHiyJf¾>f1b)ا9&om6{? Sҽ>9[]\-/4[ 0L .o,ëܽ:;,U:%,-/.mG'F- JJlCIoZ^fY(R઄빀ʾeVzkUVxյ|EMNSA餑njwL mR\#ˈXWU[v`o EtᛛcOpKڌ=SE9{@`hp8w\ &S躹1 0nUٹf؂}+8\@Z-%!j)HƩኡ/|7NGDpn/^#\ +U4Qm wZIhf LD;ǖL qD?R56&[ .1fFP(T(555n Fc{'AtI]JkcNW^! Jc\\@[ZJB|'HZd2qGL& DpM0˲'O UQkI#l6NWTT] MqwF;\u@]ZO fnMI߶>W׌`J IDATrBa4tlIeY|!0%&;e:M]-/z)@V Jj`tneg%04=ꬁ9oC(Vo28l6rytt4|g?}455㱊%NwA؂!µ ':S@&I"b$ja!lDh'۪={*;>1|3kG glDn*u^|>?**J.Y˲VseYF\'>Vk{IH0ERi4Jײ6^ Xwa\q"JQaa,aT"*--mDԩ]tAG= xÑ"U,{T*mt" Z tXq4 0qžp BGW!`h JHEEh>˲̲X,NHHhU^\tbxu{ - wnoZYűnd}@l6s~Hؘ7T C1yw ]gK>6pbl q}!w% 0 4]Yր& 7PXUU7h['e_LLL3\}@h4GZ[xvZ*Ẻ:h4D" FdCII{wi]]Zr^wr eo&9=M?~8q w"2&>'裥0K Q]]mycolrbF?ᖛ) ,r3Q.̵e?l ;T Cxq[fX{%yG.5>h;_z͹J8>>իE bbbjkkVk{'2w^"zO+-ߪƼ'QkbiicW:`N&sӷmȹ>BDN뗈Zy:a(O !"v:D h4E;k#D/F8ʯZ DEEUWW5}jl ?*p.x B Wui ؂sM~畇l}ҕꮔ$ vzxoZn\˲^z&T*UΏjγ>+;" <}qgW 6^q-_|ٲe|1쨆 bŊ#`y}駽{ny-G2jԨ~{|СCUEоjl,{.ݟ~7;ҾQ J0ъ]/3cQڈ!}k3/{"1˂W$DX=6b8H$""d"J~/rٲe|/T*ŋy# i~Zx5R}?''gƌ 8=fsrdL!"U!Y2}uw튾_+(اWhU sߔ`LJ۽\fӅ7>̣>eG>_va/ ; Jzf͚u}{k„ -"_rDDC `>]^^'/^Qqټr/=''LqYO7/Kyyܹso߾}Ĉyyy/Y[~$olb߯hqfw[OcާST ,1bS1N۶m|ЊauMJJ7oޠA 4+**{;-ޯ_svqSxb_8pٳsrr'VTT|{q_>c}O)..^d繗_ k._Ҹ1͞={u_,_.O>Nzp뭷>D4gΜ4-Lt/+V[2lɒ%ͷ~[>^u|}V|#x׻tb0N/ul7|SO=ED-<=xbav (y_Z3==T+ D[klY֤4z:7ޫ;ة^KEGGP(;wK/i6TjWV۫ϟ?ԩ7ō=>MjjLLl>p@}}}vvvFFƝwꃽ`;;wf /"vyG->\.גd2 ZRRRRZ3999/"w`zjeeeo~駞zȑ#yj-)))))*GK,yG|jĉ999V:.:::###..'|*r/}#gϞ5ݺuKII^ }̙qwygW :<>}:bhT:vouׯ_?eʔ]v+Ჲb߯h-{,8{l"ZfŋQbbSO=5R dӵZرcǎqƷ~uW [iiiND$ssssssLs544pӃqѣ㎲`-"߿?P(ZL3ۧNv@6uuSv##qwDٳm pwskǽ۵k׍7J dcǶ%۞Ià "GV+v= SϥKƾt^0ݧM5.Dg/9MeG~[>]r 6o=wnv}FO7L=0,1c/))ϷO\`ALL`xO8MgϞ=f̘;w8pO5jԝwɲԇzȱ]v5jT:?m#ڡb̙\?_ػVTÆ +++ p!7669sg5f̘G}=U6mƍ_uuu5D L8ի'M C0pܹsݶ$W괯 L:JJJ>;rU~a@{=2dHJJf#n9sd2O}"y'{Q\\|qnbA+??jdɓ''&&? ]14VZtR,z9iҤ=z'xk= r0QTs̙qgN~Ç1vt??͛>vخɓ<[ozkc ŋ?syO:f"ܹVRM4֬Yc*|2Wҭ[7KJJ:DDNT[W ֊ܡpѢE{q"fÆ z[>nq;={n{X"?~'|Cd2Gjug=zX,/cADA„`X,,666\r…emݦRsFVd2L,677_zu˖-_]뗕8sdZDs{0a桇oV"rg'G0aNo\f[&M~:'OQnnƯ DKao߾iiiD;%ˉl6zFY{U*o}We?ʼˏ =wb'3gr{9sطpd+0Q?-ǪKPcnJs8 FeO^>jD4WJy\̹M[s8m}(y@>(:}t ԵkW!uN*z}mmmΝmrѣ\ިQӭV˝ߺ;JKK'OD"ewa4$*˲w뮻z- OX5G իk׮)))111\/v'Oܸqc}}ƍٳVvʃnڴɩAH$իWNNNjjjTTP(tn/G4hP||d***矝tNKDn[[={{!**ʱ mVŋN8sp,4heS7kw^p\.6lرcӷo>`^zj6% {xk'7n\NNJjjj8vؖ-[< ю_|EoBcoJJرchŊ<y;ř={c%u< WױULLL݉h۶m ˉ+))/WZZ+Wt6R jjjZjբE|~NNWvWԩӸqkLtܹ~}ҥKNbl:t֭[=z=rȴ4HTQQq͛7{E,~T={ߑg˖-={Dv[;b"N=u@~*; .\t-^ߺm~m{aNڛwG_TTh&Oz`ի>>**J$ן8qbÆ nafС7|sN|~]]ٳgwu7oL>=**oU {fQLKIx*dϿv"Tlcf>g7\"Df`3e(D4[So6-''~gJӦM#o{N hʔ)Zjr/^\xP(^hܸqͳl>|ODEEE Jõȏ+)))>̛7/==}}]NܼdɒJ_b8q"!!K>l1 c/Vp[Arz]Uxe)vᒵay}ȷfNP(rrrJ^xuznnY~iYf%%%_v%//oҤI?cgn_G}wrr>[n&Lo?Skvkf|[fniDX5F-))qbߩ"W IDATn}V'N::3pm.]і-[|éX$6l}JFFF^^ެY}Yg}|g/˞âEnq>+W:M\.~*aLXODٷr˖-[^}UD/x<… 'NhcV&D(~D JS{B7??y'jiK.ݻ1cƺuDF8xCrrr\宊=܃&v7tSttSǜ9s.]dFFƀn'|ʕ+Z#tˣGrU ѣܭȑ#'Ln>}NO:="jy普]ӻ:K<0sLsϡ>}u*a SX^v_\~1cT*U+BBi@SAlQN/]C քϾ^ݷg?m4F/kرB0;;{ĉrߴih,))ijjR*ǏSRRҿ$*^ufBïp„ tʕ+$99Y ɘoիW;^߁q?K.S__5BWRRWl2MF$}ϯ\ٳ555,fdd{rG9|3 +V(++kjjd:thZZڢE}Yowi0u6eʔ%K/ /{a橧⪦N8k׮P<{l>vm۶9sF"xY;"jhhpI-7lPXXXQQa2fϞ6z8uA>_<\}ŋjFקO_r0qM_LfϞya:':?>zh1Ɨ#Occ/2rȾ}2d*I,=pyjcbb%+TM[ y>z褤$eu}USy̿|$᧌|umիWs\Ǐ_v}]׏kh;z_ƍ_~ j{Nw1sL{5kz}^뮸zj… Twp۷,޽رcuuubs:tK.z>?pӧ+ʿo=SR%Kl6۶m>l0{uAOȴifΜɲ믿KPhU [ֆMlKL8{ID}QtN,WwM14gԜ9Ml@Qο94wӏR7WyuTm8k9_y̭ƼΥ/L]Wg0g8E?KlGjT}rCg.aűTOzt[LDi# $?mv.ڹИ5y7?~xAAAnf̘ao!&«W6 \3NVZ۫W{6>L&;s̲e˼,(ˎO>mx˲\YM74=eKaG&^ںf=llٲ&%%eff{]vcĮ6nho۶m'Nx'Z7߼elN#߿g9r;[YY=pΜ9&i:[oϞ=K֭|PX,?bO0W\q,iD"?Bڵkbbf[bS1 m47Q(r⸸d3 pvEXNR'l;O9k{eF+Zʵ0J3fx6mr;CDAq#,{i/s^|{ĉӧO[`ذah+#6m8eʔA٫aUX_~پ}J5_~󹦁6[nW\tf>}K^{ŵʯcccoySN߿?[\}|͚5o9폺u@T;-  ^~妦+W:uj֭~]3psڵ555Ν߱cg5k۷3!m9mj`Cr7:Nׯ[n̙ӧO_~"k{ȑo\. ;宊;:uw~/9zkJNZx˖-'HMMsjps9 6S;VSS3L]wq,<쳎؍;vD:޷@{ \+a%"sMF---uT*})|7uV7N%"UP٠rN7.b>Oox](1=ZXo]"LEt`$u{GuiΎ\4$_W媟 UdcM;{[bW6BvTrD&w;MUǥ'ft&94Tڭ'T^aq)=#-t|?i2^}UD\ kK0d2vgrcr[ӰvZU~ǏH$,˾VV/5 ZURɵ[fcAD$;:߱cqϞ={… _LBD=z)W^u*pիaÆ}- y3:DQQQ fq:z(7ѣZ->d)y@hݩhAAjmgy7xbyWu:]bbbAAAjj3y$3)u/0f e8n۶m+V`%g`( KSHMBO,׶%,OOjHp5m/o(H3Qǽkp$[sbδ??~˹yfzNȕn١6De% >o%|OM~y? / s'Zj֭:thFFoEDDk׮]tSNmڴgyF9իWϙ3gtSN%''5P(<給[J;***|_ 2V+gYVx?D"yoү把5kּ[EEEzkBBB^^s=_rj#\y/-oD~zڇN-/`{w7߼^ C׬YpNsn)6?r/YСCt;UwۘABgM kO>[n~΢~lۤRs9͜^4$7֗?<|7n9gM GtU pzT*h4gR=y"|ᖖ$[%˲gϞݶmۮ]|wQQQ˗/iӦoVxy= Ѭnmر%l6F8/ax]zD")..gp0p$ڧf;T#Q]^e9QnOk_t8/5\P( iޏs̙>lٲe== ҳl _WC{N4馛n:wJǹF'^xoYdɰn_|mffT 9rO?MܪF\7_񮮮NIIϿbf2n)qJ^eO77H b`~\;Ǚ'2֫$Z$mB CgvEB w/Y”q,,SI,[%! !u +X:o/҄ d2ȑ#6::n#8q+ONN?ߔ'/\WJ_{{_t)P[o4EcǺN<:tEEE^^] XYYKkxWw_`0XtI5\#KC |B`immzlɿ-Q\A:s-p8޽ӟ(|Yrkt׮]yy IDATys̙>}ѣGk/;~&:P i i-\n4ɄNcSd2ق !~!]8d6o| \UU?\ʃ\oY}v BfsYYYnn.?ŋ>(:u~%L)-->_u5tFسeaÆI&M8NignA>U8p7|IRRRz_MMM>Ka0~?`lʕr駟v}aÆ sNqqq>jժ|`N.!7@퉺 i1%y1o$hmw|ï !T& E9B0.e=>J=N_NVnIfLs2Ϗo {W<"fTuLF^޼ye`'Npp~1c,\_|{nn{=/舌4> ViW]]υ]hP~턐 8׺`_x>hpQBq׶ER`0Q B1?ٛyyyt { Gd?٬,:?MzH-0Ϟ=;***55uuLK[p!M!ҧ}ݵW%xbzz:}E4}O={~ecWu6W;w*&33S;r9/QD}.~n"!mDz~~~jj/bυy999EEE^v~ͺjnn~7XJJJ222T*"ǻw6lشiv;ߜuAffOay~ᇊ?~_߄ R_bFc}}wo*{<"J鲲G~𯫡>ԟ>}ܹsQQQ]]]۶mq]1KL#t3g~G壬,--mС9o|h4t/-AJޙgyG5y1Vp][6 !8gO S !ԝ*L&n$%wmy]]]g0 ;1W tKEEq- øu&f`[y>H/-}7xmh;.Xh4Z,gw0̥KxFI\Z4!-A~{QQ=ޱP(tS__~)*}N9|~wX_k4̾XVV6a„l(d>WrSΟ??q!C:t(!U "v#P~K,7n\FFƥK ,ZR[[j_K/i4+W./_[jK/ĿO0zw|N Kr<~Y""".]ppH&=t:ӦMs:nw~u'!Gd/ȑ#.BaZ=׾+! 9}B^]+**߰aΜ9 tP4,˺Ntz/$*u5Ä&lذG%l۶m"}ŢP(<p޽{ʕ:c߾}EEEz~֬Y[nǹϏtΰҥK7pÖ-[GOTN0{|#G.!EVr8md_ubgvu_əM7>RN*!$>cU"aLsYYHa5 kNL V -nw]LMu%LO֭[<v xYax~nyyy`07W[ok3 !NRΕXlһyUbbbbbbn =ҥK\7fee_۷Xn-AƮ֭[GIJJzg\#H$>ҙkɤ#LG~'ƍ-Z&czBȗ_~oooo_=BNq% =IIIIDW}oɒ%|EbfȐ!^^زe MÇW ]k}7IG}u~K-_RAѪf$%%su=\Aċv$7n̘1A< ڤRO>C7j4~xȐ!{/iii)}'`„ ^ܾ};L{ڴi׿VZ5kz%L-0s=0O4ZvҥtZP9rĉ[neŊ| !DO d"BBC)ܻw￿n:hC=%jȑ[FEg$⇞{|ݻrnDGG|/UII !dРA3g^Y{KPwaӘ_>oJ-z\\?ϟڄNӲ,˾  <A!22$2>ln7XtI' !uu[60}z1>\maAP'h]Wٓt&7S,ڴ\sN夒ƻ-A,FPKfڵ ϟ1c!W_K9?355uɒ%|AO߻w/}:t;'Os֬Y㳩h4T@f?=))+_0B.0cu چ!--- ømޝZXXXXXx]wLe䄐}޽Xw9cƌ'7v>ٓhѢ^{R.*?Wї̙3gO>}_ӡիWw#>wʈ^{޶&a0o?z茌G}oZ ôos;8nllHs=t ޽{,XR֬YiӦ5k*!˲>?swquuuh4*s>g}w]aa!}]f555YVVHYY٦MwyphZIP/\xqnnڵkU*^_n];uTkkkTTرcC^]T}:h]AO?-**R*T9s=g4W\y}vNg02w;w\lYDDă>x}uvvB.\ϙѓ>LNN~+++M&D"v]H֯_dd2Lz^}W/bzg^yNzGyPкRI?e>m7BFt#w8~ә V%kYQ\yaeA%,qOllҿj+[(J -G"Ƽ~Pxe"V-7N0vX2sOLsfO Ё:|ʶo>{ŋoٲ,/]TRR2xoѵKppYSSsɃEfϞP(f%ŷB~x7"ۗgݸ~d裏d^ORuuu}ƍ{H~ϟo8Aȼ7|ٳrKVVk!|JKߗȯVV|ܹ.b7|tC7.aP8N)+lRL ~_l/<)1bI<7Jaac=t:.C*,=Rߎ_~=?QvP$%%BtԴt:7^~HyN+wI:FV%dfŨ4N'˻*++{7JFQ&Y,D2v4јh,Kuun+mSFQ.wttyH\\\VVL&vu]X;0766Ɯ+uOOy78RT-Ϗ444aA$RSSM&gJ(333zᨯ2 :eΆڞd&==6і^ZK Y2`wޑ?0$+ίDFFzf4322 E]]]YYYoW>CBO?ˡu!))I*677WVVQh ">>`0pEӗhH$4} 9_|Ma.rC|^&.a￧k[QQq Zi4RiGGŋꜣSSS5fkhhxb7X  r)/0AY !OU ҪreY)L0Lkkk111, f0bcc#""V+ll6s\\\bbR4LݾG\klA%*EYlCy]cS:yMڼC^ѲtS京Brv%=͡A7cdɍeryCg9u.^Jbico刋':\SY{2{طo_EEEjjmn s̉ltٿphluPIz,_MMM>+mf̙3~} Rl2BȖ-[f( o vf᭽ 7'BKss;Ν|}90Du u ^ w.\HaYu•+f******~d`J&~Yb0/^ n[_i+! L\y >䓹s禦N6m׮]vtQSSӛ=Gz!OU6Mt>/mrTUU  –軄uKt5'ak~FQW޿8WƼߑErr.k1;Orf'q2H%ŖE5w؝Ʒ>lˈk9TnU5KM?%{,a㸲&^Grp_,>,ם~ܙ%86z'|r֬Y{khs=O?ԯ1s!si"_H$ĥ{n힝iDTirw&$$}ǡ.NܹҢ 8jdVӖm_]ɍW3%qV8B:rq]]]Oe+\Q._w uqz$+ίV?;vnOK-K+c;r{A~-|#!$""B.Imvuqܻh"Z}…> e4dAHnOH$ yU4V80oeۼy33lذP&% 0`8 uO`0.a ],曃{@Kϵt w B6\gnaͭfy!G̞;$!LЏ ~ ~0#$͜:ZV \)m1wD}r_RB&Z?Z)(TZY}u8|$F}{7՚CR HGNRb)cN&>֎>A)9z#!D&D&virfO~P4Hjc;6>3(BVdRi\\}YVlJBI>WsHIJ2e[ڋ+J+k i12QZW^\Yd J:Dz6ј;wbbS_;xLV\O4*}GtKnl]$]wKj]“&{-C>#6ytOlt!@8%Qg]vaqj FBȴ\`c%^0vں}Yc@u0Ϙ:cvWSlx`BHzLR:~2K/rN N=YQ*bэ2t[U-_mYGN:7I<]u!0BK)))t@-8Ba?pQ[aY[vBim`96V跶arΣetӄ> #M%?vKuRݛ" oj7!G{V{bSUcor,0]7`R~aw:\Yӯk 4(v's '_$~t kjTnw?"tn;.QFBt|!8|rp)|m>F*0̘!/$hU Ww j1O:0vOi4/2caҫ5ߗ6s4BȵcN9'>].Bs}ZlKŻ%\;YQ:6#O&BӲ !0.aBSFJC];}Ώew/9^yl?BZ?9云|SaZosI%ם[?;Չ0C nS8BH{MSbă (#8kj3۬?+cu2w%[G"|v鹖nwZVl9G| SW [֊ڠ T|t 5\]?xӦMnqS5Y%`0c0 } HR ƄP" .qA\₸q B\₸q %.K\^\/" q %Qžw~1zUpLb .H!.qA\₸q %./q %.K\ ^D%.a{[>pС3TƏOVD%.K\ .qA\2xQK\ .qA\₸qkED!.qA\₸(}t 5? |oQQžc pRx &qD t7@ Bk+%߀J+@K8 EAAn߷o_ܠ0aV~ȑ/ODEE?>--MRY,Ç777\Ad츸8RiN8QVVRԩS|˥}\:ÇDGGKΊGz? Y\\\[[oEbccGycǎKa <.aq\kkӧO<ɲlHJ]LLL~~~jjVeȑ#u7 !q1oafZܹs=yÍĕ7xK\y?] u!""bny'%.Qg /q(c䤧{믿v:DlC`\"*jر j,666:u^>"!0. q(oPBZlĕ7(q4o| &򶶶3g;vK= `nM+ܚXW//M% %OJLL1bDRRFq8gϞ=uꔗ+Qd q+cKDCFXe q1ox%s@jz޼y &I]ўO:%.r֦hrrr6o\^^8}BqNSP+cK{\qqqO={2+cKC.\vuu)m۶uQ q+oK\ynW cl MLLe2!eY^Ow7nW8LWWX5+pkb^m\7D$xaI&%RRRnSK+.qe wa1%ѓ~%UTTh$OAyKNyc hn$d7Ԏ6+2lK>+1n;)?L(뮻N.WTTlݺj*ʩSfggϚ5kڵ!|}#(|'N(cǎUWW,=yǗL,pr8_~v^_XXHIRiښ#Gp9eB1uԏ>mZ]XX=儐GnZZh5jԨLӹ} .BӯcrEᠥ)J_›JZ"~K?|p8?~ٶ6333333=h%c2D1iOƠF|t /.11d4xp5m4vJr\ckw G:Ƶ 縤Ȉ:x6ݸMSReM_#K㸖ቮ3UETRiY5veD%c?.KJ&`~So`BGBWuA軒{W !'.4;8(E'-q[fR*)LӎKTJ%mVBʨd1-7&.B3EKFƹ:}C 8.?K"|Wq$" ϧUUt1f.k ݞ_UBRH֕]#!4]-u?^eE7y#m!=֖g"V&L7qnl6v +c>\PtttrR?'vvveeeyP(!---UUU^yaѣGB9B[i !/_>t}ѣa8|!˽4lܸgKK˖-[T~kg\]xmD"Yx1!$ '͝[5qͪ^c$vR9۷ΛUoϞ2-yN,Wh!zK $"UpaV'lqB>?Mp\㓵ZUo% ӕGHrܘHxH=EQ$WFK !~3O7tBT2 }!v ! }0yp{-_;UVʻYqߢ_,'JO8e J5k,e7o:V,Q㊎VՄr{CT*1 P(XfNm 8#Fŵ߿FC#ܻw{j][3\^TTD9p@{{=|Luplٲlv+M>=22rCvڦ6oRx>wtttuuEDDƊ~*0*@R(1RTǏ'߿n|8zhLpq/.c-cK͘1c4MKKɓ'Í9cB{K :v-}"]ޠը6lq!7+*J!)))11k!LWWܸzsjӛ 누ߒWoNpzf-ѓnQg\isƠ:.1>dZiXaLK6*&rSW8v'!$J^~%(!d`mRɖ QHr) UJj;"xIBZ,֟t(^n?@UHR*3.{ Z ÌJi9|}[7\ KyGHG=aF]|0UUU F* Edd$È sottnߢTApDGG ϟ/˫/]ԇ T`q=ZP466eA;y$0^W2Ę1\T#GB}b2h3Д)Sd4LqkQ+. q.o{ + G0yk 􄢓Ì W`AܸA'pH$C;v,q~ӈߺ]'xBB6ٳA9`p=.enu3+w!T%ƼA|<#W[Dc;Q6e#"-/)krһ—ZyYѪ8ep=t |DL!LB6x)V )3.)O +CSc#TGv\j%.RF9(j(0YtmGRq5vۭNRzMEprJ9(*3J*ev˒ \5D7E"oDp%FN9nMDF$_{ITN4f޽B^SSs)~ap#$NF3j(g!9\+˗BZ[[/^?t;wtt3T*^'+_qQr|ᄐ~[H8H;6|8ڷoKKDC`\bBw͞+eR"|Esܹs…˗/?uTKKˤIR޽{ś7?q(oaq.oTVV䤦Fױ5Œ7E^!nᇳ_qp* bHaaajjNS(w.//u@KP(ӧO'?myKDSOq4cW*FO=!ZV8̓%ElRřRg]bݲeK_ Wd]gA֠9:pUGC].eb#CbԄK$/ԲHEIܝ Lb:O7P?W99фmZ2 ji84rA-RT OO7eӼl` if*>RACF,J$=JyȆD7i9ˇʉt aUyݥ.WoM8QV7>oL&SDD^tiiiiii.\ضmˆ%0CM:5''GV_pbDFFĐ#xŠj={\.j!**j̘1yyyr:~E 2DP-y;:: E|||JJʢE>쳚Ж00~%!<.e D"'|=#ظe˖o1""g?x~!%q(oq+o;wnQQQ-:qDssL&3  / IDAT"l6?XqQBdWd= W\j"MS%!DP$'';"Əs&d2ܹs[[[xKDÍD1i+cu%q{AG{"R uuѯKϘp^;wuBZJ`v[{aPcN1)Ǚ ,Gqvu_Fٻπ(g/.˲tPP(E%QK51EobMEkbJ~nc;pΔwrzy;8Ӣwߪ,qN n5:vb?%n ]*ZűuO+ {y;J;,pܡud ѕ?]kU-^o5eDZ^)2J= =n=ۍvqb0Ǐ>8evCt!{iF.jkkO]xH$ ׯ_fffkbu]6ݷo_______QVIuPyy9f! 5~ɓ۷;99yxxDFFzzz5wEBky;vM.G陔gϞv-,"z.bR5"J0G$aÆi4+W __߀:t07@E(׋^qCR))):tի.qzaT_&XT/+^5 zYwcyb`xT9y$.۷ ᑐp.5 Wܨ߷o'njj*// 4,-EB4 W[hzQylG^JR/---,,LKK ]RDWG~~~yFZM-G z1bPӠWİ FqCO )aՂi~)VgyswVBMMMۗ3H) L/3 SkKxR'tEG7(kdQ¤Lq<5˚bu6X ә]xSjVjtfߓY~,čUj$uJ]0_/Ǐ *D='|NxMO4<<<<<8~< Az?R^zX,TZV(L&;!..!TSSSW縋[(˗/׫SNJǼFO5.ɓ̔Bǔr̈,"@Q_zzd3>^|VO/gV+54!>ha7 3Eev@1ͼBX,5554mI1T z#V/pn~H^/ԗc2S/GQaaa"|q8; i:Cr؈aKh1lZ#z9x<<<BzT*#)) !%{sI$cǎ9 QaL2Gz0Tk+) \={۷/BÇMM|^/zE +-" 7y^1H- sxZb QWWG5׋^qÊEf;v8zh<_7"MF7uFJQQQx IHH@=x3QEƦ gggWWW- #66ou(H.[^^NKQ"׋."oPtF(XfkU}TQQ!9NyyyكX]\"nFD~mKf>KDHQRhPB|C $Si>FTrJ[%pBht`lzq;C^]$EL"W TGomsu8sxPve&!ܝUO "EQ֘괋EE܀|0Bo߾Oī BۻV^/z ]HII";755efffggCǸA^$ jjj^T__+d2ٹsڻt֣X/K_5eэM!!!V*\._,:NJ|}}Bnnng6at&G^ԯ"7 zE +ޠ71E%쪪*77@GMMM7nܸ~zTB.SfTEjٝ&k%\^ ^xݫl>YPSC_Y:h 7RdeVCU'W+5o!WN/J|Z2!TNƉ_u$5jaFbVxh{3Ax:],@ uiRvq6aAb]vX I)kP8PZ[E!T<ԉg1muTJvqbV|x+WXq!H\\\LL&pԋ c_C%J hnnP(bzv߁b͍ O:x@i0_/:F h1LlZ~fCͰ^t-֋vqdRl Wܠ^/exp8NmmmEEf6WjMneH Emmm}iZՋE4,b`6vXЁLF A#?~N9V)Siub:Ѣeyu1TTF.nPcU|ޥ?*fzo캺:aFLj"E oz]'bmm8+7 ;<XF/";! h; @L e?nc#X^^@E/P/xjAE/P/zz ԋ^^٫@(L ,H 3e`|>F 35a„.;rge*`0< % ,H 3 R̂0< % ,V{@_=ڻ&׮]# W:ҋLxXRב^̏Q̂0< % ,H 3ˁRbWW.3E$%$$$$$ei p;LHH l@Zj PLL̢EbQju{Lj_~Y(nDZ f/ C ׷/}WBy%))i޼yl6{] b! lRl|!ܹsx<^SSSYYYAAܐ^ #7C5j3@@P(lhh+))C 7v6(Gvfm81p;^xΝ;: ҫW5kW_}u?"b[l1!::z̙AAAlb8Bn%z$hNE9##č`F EAAAffctSq:toB}Ǎ3ر*,\0%%!t\{} _~yذax%/Z?޽[Nttɓ'7mdS5l`u$..n֬YZmvv߿oZlX3$N:dGnljjڻw}~饗\.K>c2?~ƌ%%%o큔F_k׮ bѢEņ;[.**JP+&99矗H$i˗/>|X-Z3gѤl׮]?Cڵk\QRY͛F "m3TΝB\.wm_cǎgZB\.7,,lƌ}݄ VFYYY*[ 裏V__[]]r%YΝ_z饔򋤍kŦ1cK/$ =ztر?Q*^:>>^oaÆ-Zɩƍ,kܸqS lذsssܹR{=???6lP(n߾s}YnZbԩSF)..wޣGT*֭[,YdɨQxXۿŊ\299cǎ]n]PPZ~QVVJZ.b[]ɶE8[&&&Q!>pF9Nhh /7߄uB=8k=:55n׮]{QT=\bb>zdeuaÆk8\YYy& f^ D-[xڬjZ''3f!Dqm6p@{b?b88Z7nBH1bĈHJm7>>''gժU d:;;cС`ƌAAA|AW(*AK/9 IDATjg}vir2//SNo^}U<)C@@^z~۾};dݺus$o779s _~zL`0^xQFdff^v<}}}U*Ն p/! 6ŋXܓ Kr8UV>G+WԩӤIvպ68w~M6l6gϞnnnm3{vx|^^^W7޽{ϝ;Wok4={TUU!8΋/*ǎk{X1ؠmnQ%667566ٳԩSZ"_~]v%ŰիW+ ! 0`WW׵kΟ?ʭ'))iĉFHmQBtٲeG>{,ށdΞ=!~j_K_xPuu{w= .]ĐCuoOaP}}}II ضmۥKp׮]+Əo7sZZAw^poINN޹s'94e|>֭[.] RRRk׮s熅9~#{|IKKKGVp8Vrrr:tыGD]]ݷ~K^;w"$INȝnjprƍ\f?FM4S$ :!trֈGI`BCCq7%wށ/ܹs4>o3 o_RyҥG_ўeKqǺ:Q>5kxyy=~[oVV֢En݊m!B_GXw&vAh[Cquu]t)Bfʕ]h3!T*LW~ƍn݊rrr3fL[VwѢEZo%T*'i3LeذaUUU %ݻw+nJBZFk 1ŋż<<7Ƴb;wF]p,+88o >>((رcz?}ZtܙUWW߸q1rDzwbX(*sΑ?RHHHhhh@@@ `2oRፍ?.[dddppH$b2 yyy<֝=b>}^R?؄ xfL$ 6[n... H$uuu;w+Wva8 4BH*"H O% r%ݕqZ'fϞԧOc9|@пr-2EǏ:ܹsTkX,=z޻wV!'hjj( ///.++++>|s]yzzFFFvS$q8LwRX.JrGN8atEge3JJJ233o {ڵ[n:?$55k׮bXTVVVɓ'ӧQp8NNN-4S/}.]:t??}F=|PoZ.,,H$...T~y|b 8O>rÇN2m%%%u]*2&}=ecX G6o1bZK ͚5f[zuIr˴i&MDvرcϞ=|Mr:x>}mfg>?k,-;w0`ӧ7nܨ7pss8IѣFZzn`֬YIIIzG?~S SZ}.\h82> w}~*0ո\Bfbbb/_0㣏>ҽ͛7re˖Cܸqk}||BnZ!jukE=!z!k׮8{'**;;G߹sGyyyrcaKvqqQG1%D]v%g sժUd4=K.>}@.֭ Dԯ2dHFF'|V~ڧOX*L#""z-g!88o߾W\! F% ѣGdJJTG|G"hϞ=?b"QK gee}Ǘ/_M1ZW.w-!__7|LJ~<ݻ/_育2La;z׭[gX޽{?w16 !!!zթ#S_]R9rc޽ѣo߮;w#iѷ;ޢIBgjR. /^reN;LxP+bb) 4ydHwK{3.!۷-} ~||+4/Saٖ;ݝoGӧ{4| .x;w>|آC.]8} &ܿ :z[%裏BBBnݺ믛ڳ3->%\PPpAr!ӧ _~MK,moȨ ӸF5n8Vyf-))YP/^ƃ sB={6qĀ_;wӧZ622rذagΜ)((PTÆ /~WPSSoUVVjZwwĀdX;ﴘ̰1 \#GTTT( 5-=DI [j*..޽{wiiԩS===W\`rrD"ݻy]BpZ?֤ҥK&Mb$;y\|FIba"2移юH%%%=zj:YՖNmm-D׀ \NN-G]z_zkߌߍ">JZ6##MMMNNNݺu35uuÇrرcArrR<};w|}}nj#͛w7K_'Hs!]xQw/b ''ݻwY|||zajn l߾}\.WwKxx8N[(FuPll,:|JytTv3*$$w $bH$zwRiss?-ƌ9nܸ),,wa2ǏUT...IIIAtaQٰ;wֲΝ;5r^{s7 x 6h4?ͮݺu#_Hb(;;;##IooÇK$9sܻw>B2HRc[ qvvFLl;ܾJ}=1bĈjé񆆆9VGuV#o Kg.qBp8ӧOh4:wd2L&nQ7A78"jjjnܸAnqF``#Z-nV=z̚5+66V(dM6Yzyxx >}j|0/tҥO>˖-[xZ㺨Uݺu1:ޯ 2->%ѣ;vs7mbȡ={{Nݮ;TOrr/oN>m~$Ο?jS&L+[NK{=ߦM湺Μ9!t֭5k(Jĉ~E^|EQIV3߿?e˖'N[Ajjjrr?SJu؞={ow$H;nܺu6: }ir_ [|ILLӧɓ'=jj˗/o۶M" 00zMr޹sU~OFFF}}I:uը_~!w  :W^1.gDwէСC}?>zъ+p!''Ν;_}5jW_}w{nttJ2={H$:r{jjj||ۡC߿hѢЪ/S,f;ޅl@ hhhDxLѤ>uM2EoVrjjjR 7,rҥ .o7x#;;ѣΝ#Cɢq֬Y8~z4ÇMKׯGܹSw}]K.ulœ;'10%|* /[n%rss[|ۿBYYYCJ1#޽h F[ɓǎ[QQsi5.pB 9bcRSNJ bdpvڦM'NH^d&" x<-FJOO'o?ݻVH$h6j>>>ZvڵЍ7tH?s_|RSS7oެGָE82d4Lg?`Y,ւ B_59ԕ:駟z ߟfGDDT*&9k,uڵtIMMΝ;͛G̙3SSStKOտ}v=$$!!~IC </9WUU ݳgR9roeeMڲe.;wZ:wXXpS[hڍ7n޼s寽&Sm?&:`G ;+//'7n&eee?$&&.^ 茂hݤ2nCgXxBi$''h C p8Zv֭Im۶!RtT_G2yyyzGd2x0g:tԉ܍fd2Vp|}}[omܸa2+W R(7nqNŘO.!vQTϟtaQLāTƚ 5l0ӧMuhmW***UⷌVE8<ҙ=:&Lx7B*҉mPvܸq~~~׮]nH_r>(**zsrr"""bccU*H$#Eak׮ō3g,YBq"nSW޽ڊ+~V1K,Aeff\zuNNNPPP>}r7|믿:;;ՠ{4qs$d2٩S׹s~͟?L&Ը8544**,,lҤIfVظpµk.^TBꌃY?J$ 4MyyyUUUǎBXHХKL_lAk (JA}g\\|v>of zj*WZZGkF'O2$%%Cbxɟ~͛7nPhetCRpUײ2777W<)Nwww"Ç+++R/䔞Nqp]P*ׯ'{YB?~qk!ѵkAEDDoذW^/%\W_}o߾x9E%w;o,!F{XwEe3Ç!!!f>lW&&&ZT5*2ZQ>d...dFR}>TTH$'Nh47,sΟ?8e l2H%,L@.vjF`b*Ji޲e|p8 SN靚'O޽kEgb4 jZK.y=zѣ~d.]JJJƸ8Ѝ7Ep'|I͛7oũcƌ)));XѣG_]TZ\.gXz=jRp8n|0nճgpc=0(aJޔR3ƓD"ڵk=<yfZ-HN:dȐwygŊx4?ʦ^!d2qjnn[ "7}ȏpOІ .l7+S\ڽ{۷[nSN Jo+:F7775uvyRSju?GZYF(KӧO?uѣG)~! 2h З_~~EQR ĉiiisΕJ&M2E|vg>իW:}[]]mwEe3߷t%<"\5nQ[ݢ+}z[=ELrO\__g899}VKHJӧO0a€\ܹsj5>Wߨ~=~2^WWG>|8g5 4Ν;ݺu[~… miA/x2Ν;zJMMp9H9}!nڴ&~ۻwo="""/_pB;j;2%駟ODg 'cB/,))3g븸8ر6!CXfĐm`N'}(bXAAA!2AX3g$%%DasșpxLfj:_=~ |||BBBrss x722P.;;;[7/9ZFXFٳ˩ KVbS'N{t:&&F*޽{f0aGyy-[p>YhQHHH||<XJBSSSm4wV(xs]]^tyX 8_] NﭱcΞ=fO6wA,kjL9v)(B/҄ ,Foܹh']/B?LOOQj[\\\XXXll)ŋ۷ooYM||c *++M}U/r1V(|_Rl$fnQPAcE2ױcG8{]BJ)/VS3L`QJ,<!ΝKn'DG=qѩm8B͘1!{d__˗r/">>~TfkKC?޸qczzڵk9ӏ?{(^ԩSwyf={6^ ݻ:t8pBXtiii)BBB kcÇSm߾=##c֭/^`㇎~'ۻw}jz0LV駟~^^^3g+g8B)%Ϭ4iT*UT6m:{,#c͚5:t( bŊ%KwԨQfǎz;Yk٥$-7#Bqqq% Ǻ/j׮]{䉻pUUcǎz !ԭ[X=tЮ]mj˲Kzg0?6 _.h]tҥO N #222Ǐս{w&_M|||:˦<~8,,r>>>b|ZT*UYY7ޮ \dٳg?n[t`rw#61ݾ}?0x'J$ѣG+Ji \|9)..޺uO-+,HR&iun„ Ν;[$ØOufՕ%nEej0S.8&Syj/rh{999 ٳ멤? >,{3JLLDݽ{hNƏ#ƍ'H*0==LrJBBBn-%l.1Ν;G3fsǎu3v~o&/++[j`ddd߾}ՠ### vnR(!!!QQQmС̙3Bڸq{/~g?.7lذeꫯ.YDw0`J$G~|0=(//߾}… GA=oVISmq0RJAӇ3/^4?ش hQss V\?f̘? G>}޽{=z4ˍǙJ4////00044\R]taXsh˜GhӧOGrrjDFS?~|ڴi;wӖ9s օTXX`0(#rKܥߓ;vhë~*Jw٣~*HJv+!B)00.CWmd2 Ϝ9S dff޸qwa2x]***FuuuYYY"##{ / jɈqݔX{X,VΝƻwz{{GDDMmڹsg|Ƭ@A˖-srr4L`.pw{pWZZI1'ˏ9rׇOf:M<9$$D|dž-ih4dNyѣGoµD/A),,ŴݻwǍ`0%آY/((Դ<0*Hj[@S*/^LLL ޽7{rOOH;- r...ӦMCݻTwss;v,Bop+ߦ3Ȧ$niw ,pwwoll\f |0Çq_'OuSF755-w9vXjjjJJʹsZ|ZKJJ%K̘1cȖHljBj cCG'MDeL68?B0444,(]x!Գg`*h4}^yx#6p@|vu̘]v_qӧOS/;w!4~x*E5uPHf&3… T%J׭[WucxzLL?C?~\V=K͌955>...x6FQf7,  ~:ٶptmGT1F甾r ~-DH$1!==蓋dǎBj<pHHA8N`` 7<8&&b0޾}sUލcz|XWW׾}ōT:G\x7Ǐ'j&"44 .y9R[,6[M |ᇃ ҍeA{޼y!JuU&P8aCQ[CNFA??k BVo>r bSQ\\&NH&|}}SUU^4))iCP[0?)d???:U%GټysffS {+"6ﴇURRaP(Dl6TVVz aX]b][Sf.QSD")**0L Aϟ?oy0w*Ǐkkk14t:=((ð%s 2OW\d äR)F,XPTTT\\ +V`t]qV0 `-{ΝGPdt#Fe(Qh\__[oѣG5 LNNN62Ep6o߾@Od29((h4MӦۉURٳLTx#իW{yyeeeiZBaccZoV.++[`NQ(r\׳X,|r?þ>Bd2y<^gg''nw޽l6;555++kƌ(k>UuEE-ZA/_fLDEEEDD( 2p Gq۽{7ZS{{{Kߟ윖舞C555]-N$njNpr'O奧 O>Dբ<b9sԩSF: Щ$thh(77w޽ ..nÆ 2D"|Nnn={|~RRR||L&d<&==aQXXX,FrZR|> O8~ݼyvss{wѹViӦvN Rl6;%%eժU(r6~Gt/_1cFaaB`0%%%)((pww_lٲeryoo/ $)&&0 w<o޼yVZ\\>>`Hܩyw\pW^aJrhhWWWgff~7==ӧ===t: ŧG;k,fiط+W>}x?"""}p k׮uwwj2l``ӯj6F.ܹsD>ð7nDիW9rDRuuuQTPH>??m۶999 mz^.3ϬȢNԭ[5kֈDT*Jeoop***0 _]$ɥK :::8,LoI"O DL5gΜR֭cٛ6mJLL복W]|pqJMMe0IIIx|F >Lj&$$HWV rJttt``]jjjBCC\ݻw'+$lQdϟ?NbэU.^BP(kvh(c%<4ڸfT zhhh}vpp}ԫuҥ7Xtw;vWVV111W\y.!!A$7oX9''Q'OZ… Q \chٳ]]]G**:gWH _&555eff&&&b@`8?0+aoNKK `2xJA3ujN>-IuuL&sppC:.;;;>>>,,F4ztuuY&22fC,S(ޢE\.%kڋ/3Fk5M]]~u|[nmܸ100`T 'uuu5553gΔJxFgJ;vظqcxxa@[ZZo߾~p&FӍ:!Ws8Ph8p_WWWRRYYY!!!l6A.'r~$Fzzz t,:OK.P((700jvZj|/@NgwZZΝ+++3\.|ZٳgMQVm۶Ǽ.//tSSSFFFJJz2Ga؉)B&KKK-Z4c .0Fsڵõ]홙^^^3gp8===555$СC;Ν;GC^i V*eeef?{jT5W 0LϛN3"ΰ3'|Çk׮񱷷Nj( ӯy$X`0:;;Єidmęﴇuuyzz-1lE.;v#::fj4QOf4KyL% ` *))w^lll@@S{Pѩqqq~~~666x|AT;wWՇI^{OzZ~644|cj"\r5:>g'0Lݹs"d;P@ 0;z(Dh OWW'2q3*x͑.ׂ7d.^~J+7...x_DRXXg$nhhȑ#yyyk|U677wuuq\LƎd\#DWT iz}gg?cvvvB^өN3+RbRUBh4;;;Nhd2HW H$'#hL&h dD"R4ΎN i)[[[6fHR£cccβL&{顠RhD"uuu577?ş}6T#NNNvvvd2["TkxzWT*JNKK 9LNIDATG8y<;BQ(fPH$gggd#`2 CV V===---Fw:F={vkkiTuX,H$/GZ(/jÇW >>>T*&6u``aiϞ=DV1 ///.d2ѲǏ4;#%RS('hnn6r'''188P(ZZZ޷>>566;bFb.V[[kA$sGl"\BP<<<BkH͏ oxDD...*VY18a\]]]]]mmm5MSS_кUrӧ2Ngg0âTc춀ECBǏoݺu)xG]x믿ؔ >LP" ١V@@@dddUUL&S*d2#"">wd7aĸuttT*1}+W Ov`AHBBBBBBׯOJ0/((h֬YK,1ڮhR餴 &jMnyA" &T*p8Vi_~0ooiӦq8NT*kkk+**'=y1}ٳgܸ\.okkPTHPKST-}~A  s 9< 0^ q4`0 $ /,  Om/IENDB`muse-4.0.0/src/000077500000000000000000000000001404105311000132405ustar00rootroot00000000000000muse-4.0.0/src/AUTHORS000066400000000000000000000070011404105311000143060ustar00rootroot00000000000000+-------------------------------------------+ | MusE | | This software has been brought to you by: | +-------------------------------------------+ Name: user: users sourceforge net --------------- --------- Active Developers: Robert Jonsson spamatica Tim (E Real) Donnelly terminator356 kybos kybos ( web de) Former Developers: Werner Schweer wschweer Nil Geisweiller a-lin Frank Neumann franky Mathias Lundgren lunar_shuttle Joachim Schiele qknight Orcan Ogetbil ogetbilo Florian Jung flo93 Andrey danvd Fluidsynth logo designed by Josh "Element" Green. +------------------+ | PGP Public Keys: | +------------------+ Florian Jung: -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux) mQENBE/A9gcBCADKNwa/lSJRz73Vfs+IKmmltxnHqGZQvYGJ32nDQny04hdSzlOQ XVqyZHYDREpbXHnAPtANJTPozQVWoegxsC9ePEvydw3ezMO9P7hDjanXZvd4egHY VLR9ZpLvFx7WJDxTQqD1Zaa4sA7ZzSdDu9kv/Bc8Qx2Qtur5T67THV8Pr8izSFSN MKqR5X5fKqkN0+54hTDnj1YguIloYPKVI6+ns9RGOgvbys4rC1X1EqXdkkRXv3OT r98udsWNlq+FMmwR+igfzi8+B79V3OAmqUDtpNBAnGgmRW1N6mzv47jRjSX72Ev2 3HI8GXM2rxrsUyjDuDfgGG2rA0KDnWGpwHZLABEBAAG0JEZsb3JpYW4gSnVuZyA8 Zmxvcmlhbi5hLmp1bmdAZ214LmRlPokBPgQTAQIAKAUCT8D2BwIbIwUJCWYBgAYL CQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQc931Ar+c+qX3GAgAuFsDSSItndVq mPeMtjnQDinWAx//trezRoc3puC4mlrvr8dHD6cZ4n/1dPr58kuuGmS9+CzcDgdo 7HGiY1+rXyZIS2K18Z/YurIfJk7ZRr2QNv0aiuvz5JUh/TunV6OrUqapBb2XBgtW lDIDlFMQmWQ+pUq3g4FbjWgdoAJp3BDKniO2fAGqBSrWqLFllFhBI/RChdoVWD2v hbYQw+msbZ/cZjx6rDceOxsYr/ltXmfIHTVNpkQ/B9W84xHT64vqW9Z1wKbJ7yOs vf1qQ72h6yOTP4Ru17hY6oFNdFkIkkwmJPEmRC0hn2MnvvYOhgl6HwAJc+MM4W/D 5X2lO73StLQkRmxvcmlhbiBKdW5nIDxmbG9yaWFuLmEuanVuZ0B3ZWIuZGU+iQE+ BBMBAgAoBQJP7gyJAhsjBQkJZgGABgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAK CRBz3fUCv5z6pZPQB/sHAU2Z1LBVQ+ECw5OygLxXhLJOJzbDmoZpbEMrqkndcDLq 80eyiTx6YfccwqA4NbNItfwMIlVBXg3uCUK9fRKWV3J4BC7geJtkDL2K+7Z/qpbF V65OnghJtSyLYVSpKInEsKKAmNMZATZsnkdDrdJ7u953eLw1Y5W8aEa+1n4PYG2d jxySbeMLbvfVDrmDwl87RSH1EpuGQCFX0hNqWpUN9OLmx8IP8/j6/jv9U9+2bqbN up0O2AkKLNezkGZcXVUzhC8NECd0LTsrGNmSuVo632U2Q0v/AIAAMetqlHfk1DSx zzB1hNoca6Yosd/+NTnz8jvDqWBiG8aTjU5fdfijtCpGbG9yaWFuIEp1bmcgPGZs bzkzQHVzZXJzLnNvdXJjZWZvcmdlLm5ldD6JAT4EEwECACgFAk/uEfYCGyMFCQlm AYAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEHPd9QK/nPqlHcgH/23gHsd3 JYUhxAatuVCz1RCcy+iURIBd1Do0kCxCyQVF0NA7Y98AOTLJoyFPNB2Koq0CYunG InKGKZN8NFCDUFCmSkNu88PkjDRfNKWCq69z/nfE2OlyvEdAdmWn0FY89VFMvhUj NvG9n8g7s8tZciymRny4otjQjPOEmZUZyt5qm4KPfY58R/F+H1AF0E5V0pjwkBrV ItI3tsw15bKrvzFddU6pwospRNbyb2dcuedRSbSttnehXGR5w5rr41Myp3slQNVt TVbJedjWVQWK5ImIzYj8SRmG+dCXIL75RiWE72ctiy65MN8h/gG1PX/Dt/E3f2vG GaBRR0EHueE9ShK5AQ0ET8D2BwEIAMZEvoloCudmQEZ2L7L6FwzfULDyV1dFojbZ YiPXXKe4sYrFf7TK5dUsOIsBxQUcUANPzCpFOw84Ii2A7FRDYMzUfyb+KP9vXIVE W84WQI6zILN3YowhIa+bSTkQEx+7nn2JORnQp74goxxWKY9HaTtN3v6LgjseljSk YxEh8nLo9l1W3d9AYuMNmSv3amzC+p7P66E/+iFGjmKw0Nlb6eJL9iYkFGt+wjgM jbm7Rvheq95xtJpYcw6/1iqN7uQdup5ZwAk+PQhDOnM0DAZtbim20OujUP+imUot oEJAfG1QojKQqn6hr9hz6i/2BdJNwiss4d6ywILryJUnTFzWypcAEQEAAYkBJQQY AQIADwUCT8D2BwIbDAUJCWYBgAAKCRBz3fUCv5z6paB3B/9UISZmdmZD5ExQan1b W7Mz/ZvrJe+3jun1C5/ZADvBRgKBYfTA7GbwL6x963ovUXzyWUN16GH7KQca4yaq oneJk55s9DBXHzcICtSwnojZVXiZEK1765lna8lFvYEBjYWNR7upR4RlwH7N1P1V AVJ8yNJwNNjUu86CJMZ4rVXIie/o/Y/L2PEHZYiXSDfQtxEBKapXjbN2MP1DK4iq LexeEJLanIxyWKe1KHFE1W2iPnzVgqfvqv6LuIcMY6vy8/lRXrlne6nEXabMu9tN EoTXpkh0hYQzx3WP+CIutORts95i6aBAlo5Cv/67z5xBffeWCp9dmt30rlr/JIYy kkXE =iCVh -----END PGP PUBLIC KEY BLOCK----- muse-4.0.0/src/CMakeLists.txt000066400000000000000000001001151404105311000157760ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= project(muse) include(FindPkgConfig) include(CheckIncludeFile) include(CheckIncludeFileCXX) include(CheckIncludeFiles) include (CheckCSourceCompiles) include(cmake/Summary.cmake) include(CheckCXXSymbolExists) CMAKE_MINIMUM_REQUIRED(VERSION 3.5.1) if (COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) cmake_policy (SET CMP0007 NEW) endif(COMMAND cmake_policy) if (NOT DEFINED LIB_SUFFIX) SET(LIB_SUFFIX "" CACHE STRING "Suffix for installed library path. Ex. 64 for lib64") endif (NOT DEFINED LIB_SUFFIX) SET(VST_SDK_QUIRK "") if (NOT DEFINED VST_HEADER_PATH) SET(VST_HEADER_PATH "${CMAKE_CURRENT_SOURCE_DIR}/vestige" CACHE PATH "Path to vst header files (aeffectx.h). Default is ./vestige. See ENABLE_VST_VESTIGE.") else (NOT DEFINED VST_HEADER_PATH) if (NOT APPLE) if (UNIX) SET(VST_SDK_QUIRK "-DVST_SDK_QUIRK") # "Building with real vstsdk under Linux requires a quirk") endif (UNIX) endif (NOT APPLE) endif (NOT DEFINED VST_HEADER_PATH) message(" vst header path: " ${VST_HEADER_PATH} ) include_directories(${VST_HEADER_PATH}) # set libdir if not use -DLIB_INSTALL_DIR if (NOT LIB_INSTALL_DIR) if(CMAKE_INSTALL_LIBDIR) SET(LIB_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}) else(CMAKE_INSTALL_LIBDIR) SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}") endif(CMAKE_INSTALL_LIBDIR) endif (NOT LIB_INSTALL_DIR) if (LIB_SUFFIX) message(" Install libraries to: " ${LIB_INSTALL_DIR} ) endif (LIB_SUFFIX) IF(NOT DEFINED SHARE_INSTALL_PREFIX) SET(SHARE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}/share") ENDIF(NOT DEFINED SHARE_INSTALL_PREFIX) #set(CMAKE_BUILD_TYPE debug) #set(CMAKE_BUILD_TYPE release) # If no CMAKE_BUILD_TYPE is given on the command line, # cmake either uses the cached value, or 'empty' (plain un-opt build). # And yet the only way a user can reset a cached CMAKE_BUILD_TYPE # is with "-DCMAKE_BUILD_TYPE=". So we cannot interfere with this. # We should probably not attempt to do this at all. # Installation instructions now updated to be more specific, give options. #endif (NOT DEFINED CMAKE_BUILD_TYPE) #UPDATE: most of users even don't know what CMAKE_BUILD_TYPE is. #So it will be better to set unspecified build type to RelWithDebInfo if (NOT CMAKE_BUILD_TYPE) message("No CMAKE_BUILD_TYPE specified. Setting to RelWithDebInfo.") set(CMAKE_BUILD_TYPE RelWithDebInfo) endif (NOT CMAKE_BUILD_TYPE) # for debugging the make system uncomment next line: #set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_INCLUDE_CURRENT_DIR TRUE) set(CMAKE_SKIP_RULE_DEPENDENCY TRUE) # The MusE version number. SET(MusE_VERSION_MAJOR 4) SET(MusE_VERSION_MINOR 0) SET(MusE_VERSION_PATCH 0) SET(MusE_VERSION "4.0") SET(MusE_VERSION_FULL "4.0") SET(MusE_INSTALL_NAME "muse-4.0") SET(MusE_EXEC_NAME "muse4") SET(MusE_BIN_DIR ${CMAKE_INSTALL_PREFIX}/bin) ## The directory where we will install the shared components: SET(MusE_MODULES_DIR ${LIB_INSTALL_DIR}/${MusE_INSTALL_NAME}/modules) ## Synthesizers directory SET(MusE_SYNTHI_DIR ${LIB_INSTALL_DIR}/${MusE_INSTALL_NAME}/synthi) ## Plugins directory SET(MusE_PLUGINS_DIR ${LIB_INSTALL_DIR}/${MusE_INSTALL_NAME}/plugins) ## Audio converter plugins directory SET(MusE_AUDIO_CONVERTER_PLUGINS_DIR ${LIB_INSTALL_DIR}/${MusE_INSTALL_NAME}/converters) ## Top documentation dir IF(NOT DEFINED MusE_DOC_DIR) SET(MusE_DOC_DIR ${SHARE_INSTALL_PREFIX}/doc/${MusE_INSTALL_NAME}/) ENDIF(NOT DEFINED MusE_DOC_DIR) ## Data directory SET(MusE_SHARE_DIR ${SHARE_INSTALL_PREFIX}/${MusE_INSTALL_NAME}) ## Lib directory SET(MusE_LIB_DIR ${LIB_INSTALL_DIR}/${MusE_INSTALL_NAME}) include(FindGit) if (GIT_FOUND) EXEC_PROGRAM( ${GIT_EXECUTABLE} ARGS "status" OUTPUT_VARIABLE GIT_RET_NOTUSED RETURN_VALUE IS_GIT_REPO ) if(IS_GIT_REPO EQUAL 0) EXEC_PROGRAM( ${GIT_EXECUTABLE} ARGS "log -1 --format='%ci'" OUTPUT_VARIABLE MusE_GITDATE ) EXEC_PROGRAM( ${GIT_EXECUTABLE} ARGS "rev-parse --abbrev-ref HEAD" OUTPUT_VARIABLE MusE_GITBRANCH ) EXEC_PROGRAM( ${GIT_EXECUTABLE} ARGS "describe --dirty --always" OUTPUT_VARIABLE MusE_GITDESCRIBE ) SET(MusE_GITSTRING "${MusE_GITBRANCH} | ${MusE_GITDESCRIBE} | ${MusE_GITDATE}") else (IS_GIT_REPO EQUAL 0) SET(MusE_GITSTRING "") endif (IS_GIT_REPO EQUAL 0) else (GIT_FOUND) SET(MusE_GITSTRING "") endif (GIT_FOUND) option ( ENABLE_RTAUDIO "Multi platform rtaudio audio compatibility fallback" ON) option ( ENABLE_ALSA "Enable Advanced Linux Sound Architecture (ALSA)" ON) option ( ENABLE_LASH "Enable LASH Audio Session Handler (or LADISH compatibility layer)" ON) option ( ENABLE_LRDF "Enable LADSPA RDF (lrdf) Provides extra info for LADSPA plugins" ON) option ( ENABLE_OSC "Enable Lightweight Open Sound Control (liblo) (DSSI also recommended)" ON) option ( ENABLE_DSSI "Enable Disposable Soft Synth Interface (dssi) (OSC also needed)" ON) # Disabled but kept in case anyone really wants it. To tempting for users to set it. # This is soooo old. The code is unlikely to work. It involved fst. # option ( ENABLE_VST "Enable VST/win support (deprecated)" OFF) option ( ENABLE_VST_NATIVE "Enable Native VST support (see ENABLE_VST_VESTIGE and VST_HEADER_PATH)" ON) option ( ENABLE_VST_VESTIGE "Set VST header type is Vestige" ON) option ( ENABLE_LV2 "Enable LV2 plugins and synths support" ON) option ( ENABLE_LV2_GTK2 "Enable LV2 user interface GTK2 support" ON) # Disabled but kept in case anyone really wants it. # It's just too undesirable for most users, and some packagers were trying to turn it on. # Even if we were to hide the option but make it still valid, these older package scripts would set it. # The thing is, the code is (was) a fine working example of how to implement makepath especially for MusE, # so it is of importance. But very tedious and radically disruptive to a configuration to test and # maintain switching back and forth between set and clear. And it is most desirable to leave it off. # So let's just really hide it for now. Tim. ## option ( ENABLE_LV2_MAKE_PATH "Enable LV2 makePath support. Stores plugin config files separate from song. NOT RECOMMENDED." OFF) option ( ENABLE_LV2_DEBUG "Enable LV2 debugging messages" OFF) option ( ENABLE_FLUID "Enable fluidsynth softsynth plugins." ON) option ( ENABLE_RUBBERBAND "Enable RubberBand Audio Stretcher/Pitch Shifter/Sample Rate Converter" ON) #option ( ENABLE_ZITA_RESAMPLER "Enable Zita Resampler Audio Sample Rate Converter" ON) # TODO See below... option ( ENABLE_MIDNAM "Enable MIDI Name support (MidNam). Official MMA instrument spec. Also for LV2 plugins." ON) option ( ENABLE_INSTPATCH "Enable Instrument Patch Library support (enhances soundfont support)." ON) # Disabled but kept in case anyone really wants it. ## option ( ENABLE_EXPERIMENTAL "Enable various experimental features (not recommended)." OFF) # This was recently extensively overhauled, modernized, and tested. It used to be marked as experimental. # But lest users feel cheated out of the full experience by a default build, let's set it. # If security is of concern, users must start with a command-line option anyway for it to # even attempt to connect to a server. If Python is not found, no big deal. option ( ENABLE_PYTHON "Enable python control support." ON) option ( UPDATE_TRANSLATIONS "Update source translation share/locale/*.ts files (WARNING: This will modify the .ts files in the source tree!!)" OFF) option ( MODULES_BUILD_STATIC "Build type of internal modules" OFF) # This has far-reaching consequences. It allows events to be hidden before left part borders. # It causes many places to interpret the unsigned integer event time values as signed integers # so that we can support negative event time values relative to a part's left border. # TODO: Make it a user settable flag? Or only devs can change it? Or just make it permanent later if it works OK? SET (ALLOW_LEFT_HIDDEN_EVENTS true) if ( MODULES_BUILD_STATIC ) SET(MODULES_BUILD STATIC ) else ( MODULES_BUILD_STATIC ) SET(MODULES_BUILD SHARED ) # SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON) # SET(CMAKE_INSTALL_RPATH ${MusE_MODULES_DIR}) endif ( MODULES_BUILD_STATIC ) # We need these always, for a few of the shared modules in the muse/core build, # even when building muse/core as static. Otherwise it can't find them on run. Tim. SET(CMAKE_BUILD_WITH_INSTALL_RPATH ON) SET(CMAKE_INSTALL_RPATH ${MusE_MODULES_DIR}) ## ## Just print a notice if this is OS X ## if (APPLE) message("OS X found.") else (APPLE) if (UNIX) message("Unix (probably linux) found") endif(UNIX) endif (APPLE) ## ## look for thread support ## set(CMAKE_THREAD_PREFER_PTHREAD On) find_package(Threads REQUIRED) ## ## look for Qt5 ## set(QT_MIN_VERSION "5.7.0") set(QT_USE_QTXML TRUE) set(QT_USE_QTDESIGNER TRUE) set(QT_USE_QTSVG TRUE) find_package(Qt5Widgets) if (NOT Qt5Widgets_FOUND) message(FATAL_ERROR "Fatal error: QT (version >= 5.7.0) required.\n" "Cmake tries to detect QT5 by searching for 'Qt5Widgets' package\n" "If you have QT5 installed, make sure 'Qt5Widgets' package is in PKG_CONFIG_PATH." ) endif (NOT Qt5Widgets_FOUND) find_package(Qt5Core REQUIRED) find_package(Qt5UiTools REQUIRED) find_package(Qt5LinguistTools REQUIRED) find_package(Qt5Xml REQUIRED) find_package(Qt5Svg REQUIRED) # Needed for plugins factory: SET(QT_USE_QTUITOOLS TRUE) include_directories (${Qt5Widgets_INCLUDE_DIRS}) include_directories (${Qt5Core_INCLUDE_DIRS}) include_directories (${Qt5UiTools_INCLUDE_DIRS}) include_directories (${Qt5Xml_INCLUDE_DIRS}) include_directories (${Qt5Svg_INCLUDE_DIRS}) LINK_DIRECTORIES(${Qt5Widgets_LIBRARY_DIRS}) LINK_DIRECTORIES(${Qt5Core_LIBRARY_DIRS}) LINK_DIRECTORIES(${Qt5UiTools_LIBRARY_DIRS}) LINK_DIRECTORIES(${Qt5Xml_LIBRARY_DIRS}) LINK_DIRECTORIES(${Qt5Svg_LIBRARY_DIRS}) SET(QT_LIBRARIES ${Qt5Widgets_LIBRARIES} ${Qt5UiTools_LIBRARIES} ${Qt5Xml_LIBRARIES} ${Qt5Svg_LIBRARIES} ${Qt5Core_LIBRARIES}) #~ set(CMAKE_AUTOUIC ON) ##include(${QT_USE_FILE}) ## ## Begin MANDATORY packages... ## ## ## find ladspa.h ## CHECK_INCLUDE_FILE(ladspa.h HAVE_LADSPA_H) if(NOT HAVE_LADSPA_H) message(FATAL_ERROR "** ERROR: header file ladspa.h is required, but was not found." ${HAVE_LADSPA_H}) endif(NOT HAVE_LADSPA_H) # ## # ## alsa >= 0.9.0 # ## # # if (APPLE) # message("Disabling ALSA support due to OS X build.") # else (APPLE) # PKG_CHECK_MODULES(ALSA REQUIRED alsa>=0.9.0) # include_directories(${ALSA_INCLUDE_DIRS}) # endif (APPLE) ## ## find sndfile >= 1.0.0 ## PKG_CHECK_MODULES(SNDFILE REQUIRED sndfile>=1.0.0) include_directories(${SNDFILE_INCLUDE_DIRS}) ## ## find libsamplerate >= 0.1.0 ## PKG_CHECK_MODULES(SAMPLERATE REQUIRED samplerate>=0.1.0) include_directories(${SAMPLERATE_INCLUDE_DIRS}) ## ## find jack >= 0.103.0 ## PKG_CHECK_MODULES(JACK REQUIRED jack>=0.103) include_directories(${JACK_INCLUDE_DIRS}) ## ## End MANDATORY packages. ## ## ## Begin OPTIONAL packages... ## # Check for GNU-specific math support: check_cxx_symbol_exists(exp10 cmath HAVE_EXP10) check_cxx_symbol_exists(exp10f cmath HAVE_EXP10F) check_cxx_symbol_exists(exp10l cmath HAVE_EXP10L) # FIXME On MingW this is saying yes but later M_PI is not found! # check_cxx_symbol_exists(M_PI cmath HAVE_M_PI) ## ## POSIX Timers ## ## Tested OK. TODO Detection. TODO Resolution queries. # set(POSIX_TIMER_SUPPORT ON) ## ## alsa >= 0.9.0 ## ## If requesting DSSI as well, try to grab ALSA too. if (ENABLE_DSSI OR ENABLE_ALSA) PKG_CHECK_MODULES(ALSA alsa>=0.9.0) if (ALSA_FOUND) include_directories(${ALSA_INCLUDE_DIRS}) set(ALSA_SUPPORT ON) ## Try to fall back on the cut-down compatibility library ## TODO: NO-GO: Tested: Incomplete, missing several functions and defs needed for our DSSI implementation ! # else (ALSA_FOUND) # if (ENABLE_DSSI) # PKG_CHECK_MODULES(DSSI_ALSA_COMPAT libdssialsacompat) # if (DSSI_ALSA_COMPAT_FOUND) # include_directories(${DSSI_ALSA_COMPAT_INCLUDE_DIRS}) # set(DSSI_ALSA_COMPAT_SUPPORT ON) # else (DSSI_ALSA_COMPAT_FOUND) # message("ALSA disabled") # endif (DSSI_ALSA_COMPAT_FOUND) # endif (ENABLE_DSSI) endif (ALSA_FOUND) else (ENABLE_DSSI OR ENABLE_ALSA) message("ALSA disabled") endif (ENABLE_DSSI OR ENABLE_ALSA) ## ## find LASH ## if (ENABLE_LASH) PKG_CHECK_MODULES(LASH lash-1.0>=0.2) if (LASH_FOUND) include_directories(${LASH_INCLUDE_DIRS}) set(HAVE_LASH ON) endif (LASH_FOUND) else (ENABLE_LASH) message("LASH disabled") endif (ENABLE_LASH) if (ENABLE_LRDF) PKG_CHECK_MODULES(LRDF lrdf>=0.5) if (LRDF_FOUND) include_directories(${LRDF_INCLUDE_DIRS}) set(HAVE_LRDF ON) endif (LRDF_FOUND) else (ENABLE_LRDF) message("LRDF disabled") endif (ENABLE_LRDF) if (ENABLE_RTAUDIO) PKG_CHECK_MODULES(RTAUDIO rtaudio>=4.0) if (RTAUDIO_FOUND) include_directories(${RTAUDIO_INCLUDE_DIRS}) set(HAVE_RTAUDIO ON) endif (RTAUDIO_FOUND) else (ENABLE_RTAUDIO) message("RTAUDIO disabled") endif (ENABLE_RTAUDIO) ## ## check for python >= 3.5 ## if (ENABLE_PYTHON) include(FindPythonLibs) if (PYTHONLIBS_FOUND) if (NOT PYTHONLIBS_VERSION_STRING VERSION_LESS 3.5) set(PYTHON_SUPPORT ON) endif (NOT PYTHONLIBS_VERSION_STRING VERSION_LESS 3.5) endif (PYTHONLIBS_FOUND) # TODO Check for Pyro4? endif (ENABLE_PYTHON) ## ## check for dssi >= 0.9.0 and liblo >= 0.23 ## ## If requesting DSSI as well, try to grab OSC too. if (ENABLE_DSSI OR ENABLE_OSC) PKG_CHECK_MODULES(LIBLO liblo>=0.23) if (LIBLO_FOUND) include_directories(${LIBLO_INCLUDE_DIRS}) set(OSC_SUPPORT ON) endif (LIBLO_FOUND) else (ENABLE_DSSI OR ENABLE_OSC) message("OSC disabled") endif (ENABLE_DSSI OR ENABLE_OSC) if (ENABLE_DSSI AND (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) PKG_CHECK_MODULES(DSSI dssi>=0.9.0) if (DSSI_FOUND) include_directories(${DSSI_INCLUDE_DIRS}) set(DSSI_SUPPORT ON) endif (DSSI_FOUND) else (ENABLE_DSSI AND (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) message("DSSI disabled") endif (ENABLE_DSSI AND (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) ## ## Deprecated FST-based VST support ## if (ENABLE_VST) message("deprecated VST support enabled") set (VST_SUPPORT TRUE) else (ENABLE_VST) #message("VST support disabled") set (VST_SUPPORT FALSE) endif (ENABLE_VST) ## ## Native VST support ## SET (AEFFECT_H_FOUND FALSE) SET (VST_NATIVE_SUPPORT FALSE) if (ENABLE_VST_NATIVE) find_file(VST_HEADER_CHECK aeffectx.h PATHS ${VST_HEADER_PATH} NO_DEFAULT_PATH) if (VST_HEADER_CHECK STREQUAL "VST_HEADER_CHECK-NOTFOUND") message("Native VST support disabled") else (VST_HEADER_CHECK STREQUAL "VST_HEADER_CHECK-NOTFOUND") SET (AEFFECT_H_FOUND TRUE) message("Native VST support enabled") set (VST_NATIVE_SUPPORT TRUE) endif (VST_HEADER_CHECK STREQUAL "VST_HEADER_CHECK-NOTFOUND") else (ENABLE_VST_NATIVE) message("Native VST support disabled") endif (ENABLE_VST_NATIVE) # find_file creates a cached variable. We don't want VST_HEADER_CHECK cached. UNSET (VST_HEADER_CHECK CACHE) if (ENABLE_VST_VESTIGE) SET (VST_VESTIGE_SUPPORT TRUE) else (ENABLE_VST_VESTIGE) SET (VST_VESTIGE_SUPPORT FALSE) endif (ENABLE_VST_VESTIGE) ## ## LV2 support ## if (ENABLE_LV2) set (muse_lilv_ver "0.22.0" ) set (muse_lv2_ver "1.14.0") set (muse_sord_ver "0.14.0") PKG_CHECK_MODULES(LILV lilv-0>=${muse_lilv_ver}) PKG_CHECK_MODULES(SORD sord-0>=${muse_sord_ver}) PKG_CHECK_MODULES(LV2 lv2>=${muse_lv2_ver}) if (LILV_FOUND AND SORD_FOUND AND LV2_FOUND) set(LV2_SUPPORT ON) include_directories(${LILV_INCLUDE_DIRS}) include_directories(${SORD_INCLUDE_DIRS}) include_directories(${LV2_INCLUDE_DIRS}) endif (LILV_FOUND AND SORD_FOUND AND LV2_FOUND) if (LILV_FOUND AND SORD_FOUND AND LV2_FOUND) ## find Gtk2 if(ENABLE_LV2_GTK2) find_package(GTK2 COMPONENTS gtk gtkmm) if(GTK2_FOUND) set(HAVE_GTK2 ON) endif(GTK2_FOUND) endif(ENABLE_LV2_GTK2) include_directories(${PROJECT_SOURCE_DIR}/muse/lv2Support) ## Do we want to support LV2 makePath? if(ENABLE_LV2_MAKE_PATH) set(LV2_MAKE_PATH_SUPPORT ON) endif(ENABLE_LV2_MAKE_PATH) endif (LILV_FOUND AND SORD_FOUND AND LV2_FOUND) else (ENABLE_LV2) message("LV2 disabled") endif (ENABLE_LV2) if (ENABLE_LV2_DEBUG) set (DEBUG_LV2 ON) add_definitions(-DDEBUG_LV2) endif (ENABLE_LV2_DEBUG) ## ## TODO ## ## Optimizations ## SET (USE_SSE false) ## ## check for fluidsynth ## if ( ENABLE_FLUID ) # Be careful with variable name here since we have a shared # synth library named 'fluid_synth' to be built later. PKG_CHECK_MODULES(FLUIDSYN fluidsynth>=1.1.6) if (FLUIDSYN_FOUND) set(HAVE_FLUIDSYNTH ON) endif (FLUIDSYN_FOUND) else ( ENABLE_FLUID ) message("Fluidsynth disabled") endif ( ENABLE_FLUID ) ## ## check for rubberband ## if ( ENABLE_RUBBERBAND ) PKG_CHECK_MODULES(RUBBERBAND rubberband>=1.8.1) if (RUBBERBAND_FOUND) include_directories(${RUBBERBAND_INCLUDE_DIRS}) set(RUBBERBAND_SUPPORT ON) endif (RUBBERBAND_FOUND) else ( ENABLE_RUBBERBAND ) message("RubberBand disabled") endif ( ENABLE_RUBBERBAND ) ## ## check for zita-resampler ## # TODO FIXME: The check finds the file, but for some reason the check's test program fails # due to a problem in the zita resampler-table header. if (ENABLE_ZITA_RESAMPLER) CHECK_INCLUDE_FILE_CXX(zita-resampler/resampler.h HAVE_ZITA_RESAMPLER_H) if (HAVE_ZITA_RESAMPLER_H) set(ZITA_RESAMPLER_SUPPORT ON) endif (HAVE_ZITA_RESAMPLER_H) else (ENABLE_ZITA_RESAMPLER) message("Zita resampler disabled") endif (ENABLE_ZITA_RESAMPLER) ## ## check for libinstpatch ## if ( ENABLE_INSTPATCH ) # Check recent version: PKG_CHECK_MODULES(INSTPATCH libinstpatch) if (INSTPATCH_FOUND) include_directories(${INSTPATCH_INCLUDE_DIRS}) set(HAVE_INSTPATCH ON) else (INSTPATCH_FOUND) # Check older version: PKG_CHECK_MODULES(INSTPATCH libinstpatch-1.0) if (INSTPATCH_FOUND) include_directories(${INSTPATCH_INCLUDE_DIRS}) set(HAVE_INSTPATCH ON) endif (INSTPATCH_FOUND) endif (INSTPATCH_FOUND) else ( ENABLE_INSTPATCH ) message("Inst(rument) Patch support disabled") endif ( ENABLE_INSTPATCH ) if ( ENABLE_MIDNAM ) # Ain't much to do here... yet... Looking at you XSD and therefore Xerces, # to read the offical midnam DTD document. # Can we get away with just generating the hxx/cxx and slapping only them into the project? # Or will we have to generate the hxx/cxx here for example (or in our midnam cmake file) # for machine-specific differences? # NOTE: Using our own xml parser for now... set(MIDNAM_SUPPORT ON) endif ( ENABLE_MIDNAM ) ## ## End OPTIONAL packages. ## if ( ENABLE_EXPERIMENTAL ) set(CMAKE_CXX_FLAGS -DBUILD_EXPERIMENTAL ${CMAKE_CXX_FLAGS}) endif ( ENABLE_EXPERIMENTAL ) # # produce config.h file # configure_file ( ${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h ) include_directories( . ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/libs/evdata ${PROJECT_SOURCE_DIR}/libs/memory ${PROJECT_SOURCE_DIR}/libs/midi_controller ${PROJECT_SOURCE_DIR}/libs/midnam ${PROJECT_SOURCE_DIR}/libs/mpevent ${PROJECT_SOURCE_DIR}/libs/plugin ${PROJECT_SOURCE_DIR}/libs/string ${PROJECT_SOURCE_DIR}/libs/sysex_helper ${PROJECT_SOURCE_DIR}/libs/time_stretch ${PROJECT_SOURCE_DIR}/libs/wave ${PROJECT_SOURCE_DIR}/libs/xml ${PROJECT_SOURCE_DIR}/muse ${PROJECT_SOURCE_DIR}/muse/function_dialogs ${PROJECT_SOURCE_DIR}/muse/widgets ${PROJECT_SOURCE_DIR}/muse/components ${PROJECT_SOURCE_DIR}/muse/instruments ${PROJECT_BINARY_DIR} ${PROJECT_BINARY_DIR}/muse ${PROJECT_BINARY_DIR}/muse/function_dialogs ${PROJECT_BINARY_DIR}/muse/widgets ${PROJECT_BINARY_DIR}/muse/components ${PROJECT_BINARY_DIR}/muse/instruments ${PROJECT_BINARY_DIR}/muse/ctrl ) # # If the cmake version includes cpack, use it # IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") IF(EXISTS "${CMAKE_ROOT}/Modules/InstallRequiredSystemLibraries.cmake") SET(CMAKE_INSTALL_MFC_LIBRARIES 1) INCLUDE(InstallRequiredSystemLibraries) ENDIF(EXISTS "${CMAKE_ROOT}/Modules/InstallRequiredSystemLibraries.cmake") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MusE is a Digital Audio Workstation (DAW)") SET(CPACK_PACKAGE_VENDOR "The MusE development team") SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") SET(CPACK_PACKAGE_VERSION_MAJOR "${MusE_VERSION_MAJOR}") SET(CPACK_PACKAGE_VERSION_MINOR "${MusE_VERSION_MINOR}") SET(CPACK_PACKAGE_VERSION_PATCH "${MusE_VERSION_PATCH}") SET(CPACK_PACKAGE_INSTALL_DIRECTORY "CMake ${CMake_VERSION_MAJOR}.${CMake_VERSION_MINOR}") SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${MusE_INSTALL_NAME}") # Don't pack the binary tree and the subversion directories SET(CPACK_SOURCE_IGNORE_FILES "/\\\\.svn/;${CMAKE_BINARY_DIR}/*") SET(CPACK_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}) SET(CPACK_PACKAGE_FILE_NAME "${CPACK_SOURCE_PACKAGE_FILE_NAME}-${CPACK_SYSTEM_NAME}") SET(CPACK_STRIP_FILES "bin/muse;bin/grepmidi;bin/muse_plugin_scan") SET(CPACK_PACKAGE_EXECUTABLES "muse" "MusE" "grepmidi" "grepmidi" "muse_plugin_scan" "muse_plugin_scan") INCLUDE(CPack) ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") set(CMAKE_CXX_FLAGS "-std=c++11 -Werror=format-security -Wextra -Winvalid-pch -fexceptions -Wall -fPIC ${CMAKE_CXX_FLAGS}" CACHE STRING "Default CXX flags" FORCE) set(CMAKE_CXX_FLAGS_RELEASE "-std=c++11 -O2 -fomit-frame-pointer -ffast-math -fno-finite-math-only -Wall -Wextra -fPIC ${CMAKE_CXX_FLAGS_RELEASE}" CACHE STRING "Release CXX flags" FORCE) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-std=c++11 -O2 -fomit-frame-pointer -ffast-math -fno-finite-math-only -Wall -Wextra -fPIC ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" CACHE STRING "Release w/deb info CXX flags" FORCE) set(CMAKE_CXX_FLAGS_DEBUG "-std=c++11 -g -O0 -DQT_DEBUG -Werror -Wall -Wextra -fPIC ${CMAKE_CXX_FLAGS_DEBUG}" CACHE STRING "Debug CXX flags" FORCE) # NOTE: share/ directory needs to be at the end so that the translations # are scanned before coming to share/locale ### subdirs is deprecated since cmake 3.x #subdirs(doc libs audio_convert al awl grepmidi sandbox man plugins muse synti packaging utils demos share) ADD_SUBDIRECTORY (doc) ADD_SUBDIRECTORY (libs) ADD_SUBDIRECTORY (audio_convert) ADD_SUBDIRECTORY (al) ADD_SUBDIRECTORY (awl) ADD_SUBDIRECTORY (grepmidi) ADD_SUBDIRECTORY (sandbox) ADD_SUBDIRECTORY (man) ADD_SUBDIRECTORY (plugins) ADD_SUBDIRECTORY (muse) ADD_SUBDIRECTORY (synti) ADD_SUBDIRECTORY (packaging) ADD_SUBDIRECTORY (utils) ADD_SUBDIRECTORY (demos) ADD_SUBDIRECTORY (share) ## Install doc files file (GLOB doc_files AUTHORS ChangeLog COPYING libdivide_LICENSE README README.developer README.ladspaguis README.softsynth README.vstsdk README.win32 README.developer.undo_system SECURITY ) INSTALL( FILES ${doc_files} DESTINATION ${MusE_DOC_DIR}) ## Uninstall support configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake") ## ## Report errors and warnings and hints ## message("\n") ## Mandatory: ## ============= if (NOT SNDFILE_FOUND) message("** ERROR: sndfile >= 1.0.0 is required, but development files were not found.") endif (NOT SNDFILE_FOUND) if (NOT SAMPLERATE_FOUND) message("** ERROR: samplerate >= 0.1.0 is required, but development files were not found.") endif (NOT SAMPLERATE_FOUND) if (NOT JACK_FOUND) message("** ERROR: jack >= 0.103 is required, but development files were not found.") endif (NOT JACK_FOUND) ## Optional: ## ============= if (ENABLE_RTAUDIO AND (NOT RTAUDIO_FOUND)) message("** WARNING: rtaudio (>= 4.0) was enabled, but development files were not found. ") endif (ENABLE_RTAUDIO AND (NOT RTAUDIO_FOUND)) if (ENABLE_LASH AND (NOT LASH_FOUND)) message("** WARNING: lash (>= 0.2) was enabled, but development files were not found. ") message("** HINT: Don't have LASH? Try installing the LADISH LASH compatibility package instead.") endif (ENABLE_LASH AND (NOT LASH_FOUND)) if (ENABLE_LRDF AND (NOT LRDF_FOUND)) message("** WARNING: lrdf (>= 0.5) was enabled, but development files were not found. ") endif (ENABLE_LRDF AND (NOT LRDF_FOUND)) if (ENABLE_PYTHON AND (NOT PYTHON_SUPPORT)) message("** WARNING: python (>= 3.5) was enabled, but development files were not found or too old.") endif (ENABLE_PYTHON AND (NOT PYTHON_SUPPORT)) if (ENABLE_ALSA AND (NOT ALSA_FOUND)) message("** WARNING: ALSA >= 0.9.0 was enabled, but development files were not found.") endif (ENABLE_ALSA AND (NOT ALSA_FOUND)) if (DSSI_ALSA_COMPAT_FOUND) message("** WARNING: Using smaller DSSI ALSA Compatibility package instead of full ALSA. ") endif (DSSI_ALSA_COMPAT_FOUND) if (ENABLE_DSSI) if( (NOT DSSI_FOUND)) message("** WARNING: DSSI (>= 0.9.0) was enabled, but development files were not found.") else( (NOT DSSI_FOUND)) if (ENABLE_ALSA) if (NOT (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) message("** WARNING: DSSI and ALSA were enabled, but ALSA development files were not found.") endif (NOT (ALSA_FOUND OR DSSI_ALSA_COMPAT_FOUND)) if (ENABLE_OSC) if (NOT LIBLO_FOUND) message("** WARNING: DSSI and OSC were enabled, but OSC liblo (Lightweight Open Sound Control) development files were not found.") message(" DSSI support will be built without OSC support.") endif (NOT LIBLO_FOUND) else (ENABLE_OSC) message("** WARNING: DSSI is enabled, but OSC liblo (Lightweight Open Sound Control) is not enabled.") message(" DSSI support will be built without OSC support.") message(" HINT: Enable OSC support.") endif (ENABLE_OSC) else (ENABLE_ALSA) message("** WARNING: DSSI was enabled, but ALSA was not enabled.") message(" HINT: MusE DSSI support needs ALSA enabled and full ALSA development files.") endif (ENABLE_ALSA) endif( (NOT DSSI_FOUND)) endif (ENABLE_DSSI) if (ENABLE_OSC) if (NOT LIBLO_FOUND) message("** WARNING: liblo (>= 0.23) (Lightweight Open Sound Control) was enabled, but development files were not found.") else (NOT LIBLO_FOUND) if (ENABLE_DSSI) if (NOT DSSI_FOUND) message("** WARNING: OSC is enabled and DSSI is enabled, but DSSI development files were not found.") message(" OSC support will be built, but not currently used by anything until future features do.") endif (NOT DSSI_FOUND) else (ENABLE_DSSI) message("** WARNING: OSC is enabled, but DSSI was not enabled.") message(" Currently OSC is only used for DSSI support - you likely want to enable DSSI as well.") endif (ENABLE_DSSI) endif (NOT LIBLO_FOUND) endif (ENABLE_OSC) if (ENABLE_VST_NATIVE AND (NOT AEFFECT_H_FOUND)) message("** WARNING: Native VST was enabled, but development files were not found.") message("** HINT: Check the VST_HEADER_PATH variable. Points to ./vestige by default.") endif (ENABLE_VST_NATIVE AND (NOT AEFFECT_H_FOUND)) if (ENABLE_FLUID AND (NOT FLUIDSYN_FOUND)) message("** WARNING: fluidsynth (>= 0.9.0) was enabled, but development files were not found.") endif (ENABLE_FLUID AND (NOT FLUIDSYN_FOUND)) if (ENABLE_INSTPATCH AND (NOT INSTPATCH_FOUND)) message("** WARNING: libinstpatch (>= 1.0) was enabled, but development files were not found.") message(" Automatic drum lists for fluidsynth MESS plugin will not be available.") endif (ENABLE_INSTPATCH AND (NOT INSTPATCH_FOUND)) if (ENABLE_LV2) if (NOT (LILV_FOUND AND SORD_FOUND AND LV2_FOUND)) message("** WARNING: LV2 support was chosen, but development files were not found or too old:") message(" Requires lv2 >= " ${muse_lv2_ver} ", lilv-0 >= " ${muse_lilv_ver} ", sord-0 >= " ${muse_sord_ver}) else (NOT (LILV_FOUND AND SORD_FOUND AND LV2_FOUND)) if (ENABLE_LV2_GTK2 AND (NOT GTK2_FOUND)) message("** WARNING: LV2 GTK2 support is enabled, but Gtk2 (gtkmm-2, gtk+-2) development files were not found. LV2 Gtk2 UI support is disabled.") endif (ENABLE_LV2_GTK2 AND (NOT GTK2_FOUND)) endif (NOT (LILV_FOUND AND SORD_FOUND AND LV2_FOUND)) endif (ENABLE_LV2) if (ENABLE_RUBBERBAND AND (NOT RUBBERBAND_FOUND)) message("** WARNING: RubberBand was enabled, but development files were not found.") endif (ENABLE_RUBBERBAND AND (NOT RUBBERBAND_FOUND)) if (ENABLE_ZITA_RESAMPLER AND (NOT HAVE_ZITA_RESAMPLER_H)) message("** WARNING: Zita Resampler was enabled, but development files were not found.") endif (ENABLE_ZITA_RESAMPLER AND (NOT HAVE_ZITA_RESAMPLER_H)) message("") ## Show a summary of what we got summary_add("ALSA support" ALSA_SUPPORT) # TODO: # summary_add("DSSI ALSA Compatibility support" DSSI_ALSA_COMPAT_SUPPORT) summary_add("RTAudio support" HAVE_RTAUDIO) summary_add("Lash support" HAVE_LASH) summary_add("Lrdf support" HAVE_LRDF) summary_add("OSC (Liblo) support" OSC_SUPPORT) summary_add("Python support" PYTHON_SUPPORT) summary_add("DSSI support" DSSI_SUPPORT) summary_add("LV2 support" LV2_SUPPORT) if(LV2_SUPPORT) summary_add("LV2 Gtk2 UI support" HAVE_GTK2) #~ summary_add("LV2 makePath support" LV2_MAKE_PATH_SUPPORT) endif(LV2_SUPPORT) #summary_add("VST support" VST_SUPPORT) summary_add("Native VST support" VST_NATIVE_SUPPORT) summary_add("Fluidsynth support" HAVE_FLUIDSYNTH) summary_add("RubberBand support" RUBBERBAND_SUPPORT) #~ summary_add("Zita Resampler support" ZITA_RESAMPLER_SUPPORT) summary_add("Instpatch support" HAVE_INSTPATCH) #~ summary_add("Experimental features" ENABLE_EXPERIMENTAL) summary_show() if ( MODULES_BUILD_STATIC ) message (" Internal modules will be built statically into the final binary.") else ( MODULES_BUILD_STATIC ) message (" Internal modules will be built as shared components.") endif ( MODULES_BUILD_STATIC ) ## Make the user aware of what type they're building. if (NOT CMAKE_BUILD_TYPE) message(" Build type: CMAKE_BUILD_TYPE is empty. Plain un-optimized build.") else (NOT CMAKE_BUILD_TYPE) message(" Build type: " ${CMAKE_BUILD_TYPE} ) endif (NOT CMAKE_BUILD_TYPE) message("") muse-4.0.0/src/COPYING000066400000000000000000000360271404105311000143030ustar00rootroot00000000000000This program (MusE) 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. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS muse-4.0.0/src/ChangeLog000066400000000000000000020164561404105311000150300ustar00rootroot0000000000000024.04.2021 *** MusE 4.0 *** - Fix: Drum list: Disable grouping options when no different tracks are involved. (kybos) 23.04.2021 - Part colours: Fixed 'live' recording virtual part overlay, and eventual created part. (Tim) Both now obey the chosen current part colour on the Arranger toolbar. Drawing of 'live' notes also fixed to obey dark/light inversion. 20.04.2021 - Fix neglect at 11.04.2021: No 'live' midi recording events drawn inside parts. (Tim) 17.04.2021 - Fix: Memory leak with Timeline toolbar. (kybos) - Fix neglect at 11.04.2021: No editor events shown if any hidden events at left. (Tim) 16.04.2021 - Save recent list as soon as a new file has been opened, this to eliminate the possibility that the update was lost incase MusE was not properly terminated (rj) 15.04.2021 - Looping: Fixed silent wave tracks when Jack transport off or non-Jack driver used. (Tim) Each AudioDevice class gets a new transportRelocateOrPlayDelay() method. For Jack it is 2 * segment size and for other drivers such as as RT (Pulse) or dummy driver it is zero. The looping code now uses this value to look ahead, it was a fixed value before. TODO: This still does not fix looping the Jack audio driver with MusE latency correction enabled, causing mis-aligned or silent playback. That bug fix is still WIP... 11.04.2021 * Reinstated part left border resizing: Further to 04.04.2021. (Tim) Extensive re-writes and alterations of much code to support it. Extensively tested with wave and midi parts. Perhaps most important, class EL (EventList) is now sorted by signed integer. ANY BUGS SHOULD only occur when there are left border hidden events, otherwise it SHOULD behave normally. Should be OK to use but still some areas need attention such as 'functions', merging and splitting, and more. - Wave Editor: Reinstated 'Next part' and 'Previous part'. 10.04.2021 - Fix: Drum list: Drag mode activated after field editing. (kybos) - Fix: Drum list: Crash in tooltip display with some show/hide options. (kybos) - Fix: Drum list: Crashes in Show used/named instruments display options. (kybos) 09.04.2021 - Fix: Drum list: Drag mode activated after every context menu call. (kybos) 07.04.2021 - Fix: Crash when MESS synth can't be instantiated. Now the (existing) warning popup is displayed instead (as originally intended). (kybos) 06.04.2021 - Add: Many new vector icons as replacement for old pixel-based ones. (kybos) - Fix: Improve visibility of custom drop-down triangles on dark background. (kybos) 05.04.2021 - Fix: Record button: Inconsistencies between button state and internal record flag state in many situations. (kybos) - Fix: Inconsistent state of (pseudo-)record-arm icon and mixer strip button in Audio output tracks after loading of some files. (kybos) - Fix #717: Record button mustn't be activated when no track is record-armed (a warning message is displayed now). Record button is automatically deactivated when no track is armed. (kybos) - Fix: Don't allow deselecting all tracks, as this hides the mixer strip which then can't receive events and gets out-of-sync. (kybos) - Fix: Add own icon for Audio output Downmix trigger to avoid confusion with the arm-record indicators. (kybos) - Add: New vector icons. (kybos) 04.04.2021 - Part resizing and other fixes. (Tim) Rewrite of part resizing routines, esp to the left. No longer possible to have hidden events before left border, would require integer negative event times. Too much work ATM. When resizing, use Ctrl key to choose whether to 'drag' all events along with the border. - Midi controller cache: Fixed problems when parts or tracks muted, or tracks off. Cache was not updated properly. - Undo/Redo: Fixed a problem where the Redo list was cleared and the Redo button was disabled even when only non-undoable operations were executed, such as simply when stopping the transport (which executes non-undoable EnableAllAudioControllers). - Arranger and Wave Editor: Embellished wave drawing with dotted event boundary lines and coloured wave boundary lines. (Due to a Qt drawing bug, I had to break up long patterned lines!) 03.04.2021 - Fix: Appimage start time: Avoid updating plugin cache on every start. (kybos) 31.03.2021 - Fix: Don't load presets for lv2 plugins at program start to reduce start-up time and save resources. (kybos) 25.03.2021 - Fix: Wrong radiobuttons in Visible audio effects submenu. (kybos) 20.03.2021 - Fix: Improve spacings in global settings dialog. (kybos) - New: QSS properties for routing popup colors. (kybos) 17.03.2021 - Fix: Midi routes popup: Custom painted elements like checkboxes and menu item highlighting ignored stylesheets. (kybos) 16.03.2021 - Advanced router: Added 'Unavailable' colour to Jack audio ports. (Tim) 15.03.2021 - Fixed advanced router 'Unavailable' item colour and restoration colour. (Tim) 13.03.2021 - Fix: Metronome accents list not showing highlighted items. (kybos) 12.03.2021 - New: Settings to set gradient strength for track list and parts. (kybos) 02.03.2021 - New: Option to hide beat numbers in the Time scale. (kybos) 28.02.2021 - Moved scrollscale.cpp/h and cpu_toolbar.cpp/h from widgets to components. (Tim) They use our icon library, which cannot be allowed in the widgets module ATM. - Removed unused Awl and AL linking from several CMakeLists text files. 26.02.2021 - New: Multi renaming for selected parts. (kybos) 25.02.2021 - New: Add zoom buttons to scroll bars. (kybos) 24.02.2021 - New: Ability to assign custom colors to individual tracks. (kybos) - New: Part colors follow the track color if not overridden individually. Old behaviour can be restored in Appearance->Arranger. (kybos) - New: Part color toolbar. Drowdown to select current part color, button to assign current color (or track color) to selected parts. (kybos) - Fix: Parts created by double-click in range ignoring current part color. (kybos) 16.02.2021 - LinuxVST synths: Replaced songfile midistate with customData state block. (Tim) Uses compressed base64 just like several of our other areas. Old songs with midistate still load and are converted upon saving. 15.02.2021 *** MusE 4.0pre3 *** - Fix: Consistent sorting and terminology of all Functions menus. (kybos) - Fix: Dummy icon to work around Qt>5.12 menu styling bugs. (kybos) - Fix: Script popup couldn't be cancelled. (kybos) - Add: New vector icons for Quantize/Set range to selection. (kybos) 14.02.2021 - muse3 folder renamed to src for futureproofing (rj) - Rework: Removed LastEditedEvent from configuration and added it (rj) to the pianoroll specific edit menu instead + shortcut. 13.02.2021 - New: Menu option to Save As Template (rj) 12.02.2021 - Fix: Reworked Save As..., Project dialog and Save Revision. (rj) Project dialog is now only used on the initial save, after that the Save As dialog is used. For very quick Save As operation, use the Save Revision alternative which will increase a number at the end of the name to save a new version as quickly as possible. - Fix #836: Don't play existing midi events in replace mode. (kybos) 11.02.2021 - Fixed wrong exclusive indicators (radiobuttons) in some submenus. (kybos) 08.02.2021 - Fix #959: Trim event length function in pianoroll not working. (kybos) - Fix: Remove unused cutter tool from wave editor. (kybos) - Fix: Automation menu icon drawing. (kybos) - Fix #960: Wrong zoom level after track type change MIDI->Drum. (kybos) 02.02.2021 - Fix: Memory corruption by topwin toolbars. (kybos+rj) - Fix: Clean up and encapsulate Edit Tools code. (kybos) - Fix: Dark Flat theme: Improve visibility of checkboxes. (kybos) - Fix #265: Midi device dlg: Native GUI led not reflecting the display state. (kybos) 25.01.2021 *** MusE 4.0pre2 *** 23.01.2021 - Fixed SimpleDrums not loading. Moved raster widgets into components folder. (Tim) 20.01.2021 - Fixed bug in Create Project dialog where it would disregard the baseProject path. (rj) - Added tooltips for the various non self evident buttons in the Create Project dialog (rj) - Added a new dialog 'Save New Revision...' (SHIFT+R) for the case where you want to save but does not need the full create project dialog (rj) - Fixed keyboard track selection after duplicating tracks, it didn't like that multiple tracks was selected, if track up/down is done while multiple are selected it now picks the first instead of nothing at all. The source track is also deselected (rj) - Fixed automation point value printing for logarithmic values (rj) - Changed automation add events to place them on the automation line instead of where the mouse is placed, which often is slightly offset (rj) - Added the ability to cache midi event length when adding events with the mouse, this is to speed up adding similar events as it now will default to this length. Idea from a discussion on linuxmusicians forum (rj) 19.01.2021 - Add: Functions for moving multiple tracks in the track list (up, down, top, bottom), incl. key shortcuts for each. (kybos) 16.01.2021 - Fix #933: Sort automation controls in menu. (kybos) - Fix: Crash when changing volume/pan by keyboard shortcut for drum tracks. (kybos) - Fix: Changing volume/pan by keyboard shortcut also changed midi tracks that were not selected. (kybos) 15.01.2021 - Fix #718: Missing strips/selections at first mixer display. (kybos) - Fix: Missing simpledrums plugin, both normal build and AppImage. (kybos) 14.01.2021 - Add: Improve Key Shortcuts maintenance dialog. Add filter for shorcuts, list contexts per shortcut. (kybos) - Fix: HiDPI scaling for VST plugins. (kybos) 13.01.2021 - Add: Consistent shortcuts for Add track. Show shortcuts in menu. (kybos) 11.01.2021 - New: Selection dialog for Add synth track incl. favorites handling in dialog and synth menu. (kybos) 06.01.2021 - Fix #940: Tool shortcuts not updating properly when changed. (kybos) 03.01.2021 - Fix #932: Add Show All with Events/Hide All to track automation menu. Modify control icon so it's clear which controls contain events. (kybos) 02.01.2021 - Add: Enable CC events for MIDI remote control (#566). (kybos) 30.12.2020 - Fix issue #935: Track list toggling of solo/mute indicators. (kybos) - Add: Resizing track height in track list now also works for all/selected tracks (CTRL/SHIFT). (kybos) 28.12.2020 - Fix: Relative paths for temporary files didn't work from AppImage, e.g. when calling external wave editor. (kybos) 26.12.2020 - New: Create generic LV2 UIs based on available plugin metadata. (kybos) 25.12.2020 - Added double click as a way to initiate changing shortcuts in the dialog (rj) - Changed default shortcut for SOLO track to META-N (From META-S as the system eats it on multiple systems) 23.12.2020 - New: Implement LV2 UI features FixedSize/NoUserResize. (kybos) 22.12.2020 - Update AppImage build scripts (AppImage is even ~8MB smaller now): 1. Build on Ubuntu 16.04 for better compatibility. 2. Include latest versions of lv2/lilv for better HiDPI support in LV2. (kybos) 21.12.2020 - Fix: Total number of bars not updated when signature has been changed. (kybos) - For issue 920: More values (480-based) in global settings midi division list. (Tim) 20.12.2020 - Fix: Most plugin native UI windows now should behave sensibly out of the box on HiDPI (of course plugins with no HiDPI/scaling support still look tiny, but there should be no oversized or broken Qt windows around them). Global setting for the HiDPI fix is now on by default, but is only active when at least one Hi-Res screen is found. The container window should now also adjust to specific screen in multi monitor setups. (kybos) - Fix: More fixes for Surge and others on HiDPI. Native UI window now resizes correctly when the zoom level in Surge itself is changed. (kybos) 19.12.2020 - Fix: More fixes for HiDPI withLV2 native UIs. Surge synth should now display an adequately scaled UI and not misbehave on resizing any more. Surge 1.7+ and liblv2 1.18 are required for scaling support. (kybos) 18.12.2020 - Fix: Improve handling of LV2 native UIs on HiDPI. (kybos) 15.12.2020 - Fix: Bug in Set range to selection, not always correctly set. (kybos) - Fix: Set range to selection not working in wave editor. (kybos) - Add: Menu item for Set range to selection in all relevant editors. (kybos) - Add: Dynamic value tooltip in control canvas. (kybos) 14.12.2020 - Fix: Support both old and new shortcuts for setting range markers as a default. Remove corresponding options from global settings. (kybos) - Fix: Improve channels column shortcuts in track list, avoid dependency on middle mouse button (add shift+RMB for decreasing values). Wrap channel values around when increasing/decreasing. (kybos) 11.12.2020 - Fix: Clean up edit menu, move Structure submenu to Edit->Global. (kybos) - Add: Add Duplicate track function to track context menu. (kybos) - New: New vector icons for Duplicate track(s) menu items. (kybos) 10.12.2020 - Add: Storing missing slider and other settings in MESS fluidsynth. (kybos) 07.12.2020 - Fix: Standardize Track list as far as possible. Use left mouse consistently for (multi)selecting tracks (except for flags/indicators columns). Use right mouse button for context menus where appropriate. Use double-click for text editing and access to frequently used items. Improve consistency when applying functions to multiple tracks. (kybos) - New: Add context menu to audio output track type column for downmixing. (kybos) 16.11.2020 - Fix: Initialize all fonts with the current system font family on first run, to avoid ugly looking sans-serif default on desktops like KDE. (kybos) 15.11.2020 *** MusE 4.0pre1 *** 12.11.2020 - New: Replace non-standard widgets in the mixer strip by standard and customizable ones where possible. Make the layout more symmetric and clear. (kybos) - New: New mixer strip and track list icons for the Deep Ocean theme. (kybos) 09.11.2020 - Fix: Issues with saving window geometry/state. (kybos) 07.11.2020 - Fix: Complete theme specific internal color handling. Colors are not saved in the central config file any more, but in theme specific config files. (kybos) 02.11.2020 - Add: Refresh function for the midi Scripts menu. Scripts can be re-read from dics without restarting MusE. Revise scripts code, put everything in one central class. (kybos) 01.11.2020 - Fixed bug 853: Crash in infinite loop in latency code. (Tim) Don't examine things connected TO a device if examining its 'capture' part. - Latency code: Optimization of most loops, should be a bit faster now, depending. 31.10.2020 - Add: Optional display of ticks/frames in the position toolbar. (kybos) - Add: Draw marker/position symbols as labels in the position toolbar. (kybos) 29.10.2020 - New: Color range between markers in the timeline ruler. (kybos) - Fix: Fix huge tooltips in bigtime window. (kybos) - Fix: Fix non-standard button in tempo toolbar. (kybos) 28.10.2020 - New: Add new toolbars to replace the separate transport window. The old behaviour is still available - the new toolbars can be hidden and the transport window shown. Display state of all windows/toolbars is saved when MusE is restarted. (kybos) - Add: Show index of active part in title bar/tab in multi-part editors. (kybos) - Fix: Move Plugins submenu (contaning global/user Python scripts) from global Midi menu to Arranger scope Functions menu, as it's only relevant for the Arranger. Fix scripts being applied to wrong (non-midi) parts. Check for active part selection before executing scripts. Rename relevant Plugin menus to Scripts to avoid confusion with all the other plugin types. (kybos) 27.10.2020 - Fix: Tap button - flash when waiting for input, reset timeout after each click. (kybos) 24.10.2020 - New: Proper settings dialog for plugins. (kybos) 23.10.2020 - Fix: Many bugs and crashes in the mastertrack list editor. (kybos) 21.10.2020 - New: Context sensitive help for many widgets and dialogs. (kybos) - Add more status tips. (kybos) 20.10.2020 - Fix: Resolve key shortcut conflicts in docks. (kybos) - Add: Add new shortcuts in docks (List master editor, Event list). (kybos) - Add: Outline focused widget in dock. (kybos) - Fix: Redesign marker dock for better usability. (kybos) - Add: Key shortcut hints for edit toolbar. (kybos) 19.10.2020 - New: Create new SVG icons for tracks. (kybos) 14.10.2020 - New: Replace marker pixmaps by vector drawing. (kybos) - New: Customizable colours for all markers. (kybos) 10.10.2020 - Fix: Change key modifiers for opening part in new window to Ctrl+Alt, because Shift always selects all parts of a track. So now even Ctrl+Shift+Alt works, opening all track parts in a new window. (kybos) - New: Added status bar. Can be optionally hidden in GUI settings. Moved CPU load display to status bar, xruns > 1 displayed in red (reset by doubleclick). CPU toolbar remains available but is hidden by default. Status bar is mainly used for context-sensitive help, to improve usability for new users. Many status tips created, more to come. Default status text shows audio driver and related data. (kybos) - Fix: Make sure current splitter sizes are saved on application quit. (kybos) 06.10.2020 - Fix: Replace marker icons by SVG. (kybos) - Theme: Color tab text (active/hover) for better visibility. (kybos) 02.10.2020 - Add: Show number of opened parts per editor in title bar/tab. (kybos) - New: Don't open new editor windows when the same part(s) is/are already opened. The existing editor window is activated instead. It's still possible to open duplicate windows by holding Shift when double-clicking the part or clicking the corresponding menu entry. Added a new submenu to open editors in new windows. Added new keyboard shortcuts for that. (kybos) - Fix: MDI subwindows not destroyed properly when part was deleted. (kybos) - Fix: No icons for submenus (well, almost). (kybos) - Add: Keyboard shortcut to jump to Arranger window. (kybos) 01.10.2020 - More vector icons (view menu). (kybos) 29.09.2020 - Replace global settings and midi port icons by vector ones. (kybos) 26.09.2020 - Replace editor icons by new vector ones. (kybos) 24.09.2020 - Add key shortcuts and context menu for tools in mastertrack editor. (kybos) 23.09.2020 - Fix mouse wheel steps for audio volume slider. (kybos) 21.09.2020 - Add centered mode for pan sliders. (kybos) 18.09.2020 - Change default theme to Dark Flat. (kybos) - Remove system styles selection, default to Fusion. It's still possible to force a different style by specifying it on the command line. (kybos) 14.09.2020 - Improve dock positioning/sizing. (kybos) - Save geometry of external windows automatically, remove redundant (and buggy) geometry settings. (kybos) 13.09.2020 - Add Event list menu/shortcut to midi editors for easy access to the event list dock. (kybos) - Clean up topwin/subwin parenting. (kybos) 11.09.2020 - Master track graphic editor doesn't open multiple windows any more. (kybos) - Fixed several colour issues in score editor. (kybos) 10.09.2020 - Add editor specific icons to subwindows for better recognizability. (kybos) 09.09.2020 - Allow only one instance of master list editor dock. (kybos) - Add menu item (View) and shortcut (F7) to show/hide all active docks. (kybos) - Fix docks behaviour when editor windows are switched. (kybos) - Fix potential resizing artefacts when loading older files into new tabbed GUI. (kybos) 08.09.2020 - Settings > Midi Division fixes: Full re-work of grid raster and snap system (Tim): - Replaced INCORRECT hard-coded snap/raster fixed-value tables throughout, with new class MusEGui::Rasterizer, provides CORRECT dynamic raster tables that change values, and dimensions, when global Midi Divison changes. There is one single global instance of this new class. - New class MusEGui::RasterizerModel works with class Rasterizer to provide a Qt model for use with combo boxes for example. Specific columns and maximum number of rows can be specified. Model is specially designed for rasters (for ex. in pianoroll the first row is three columns wide and says 'off'). - New class RasterLabelCombo replaces hard-coded raster snap combo boxes. Works with class RasterizerModel. With a high Midi Division more rows appear, meaning HIGH-RESOLUTION editing is possible, rivaling that of frames. New classes RasterizerListView, RasterizerTableView, and RasterizerTreeView provide three types of views for RasterLabelCombo. - Wave Editor: Added a 'Snap' combo box (RasterLabelCombo). - Grid lines: Fixed problems with unwanted red grid line colours. - Removed never-used GUI Division from Settings. - Canvases: Area before bar # 1 now FIXED WIDTH regardless of Midi Division or magnification, by rearranging xorg/yorg origin stuff in class View, ie. origin is no longer magnified. 04.09.2020 - New streamlined GUI: Tabbed MDI as default, with the option to detach/float the graphical editor windows, per preset or dynamically (mainly for use with multi-monitor setups). List based editors (markers, clips, master list, midi events) changed to docks. Arranger as the central point is fixed to the main window, not longer detachable/closeable. (kybos) - Reused snap/quantize shortcuts in midieditors (1-6) as snap shortcuts in arranger as I discovered it was missing, and it's pretty much the same thing. Also added a shortcut for setting quantize to off (8). Execution slightly annoying as changing the quantize in the midi editors after having it at off will always give triplets (which can be turned of by pressing T). Fixing that would require a more extensive rewrite. (rj) 03.09.2020 - Fixed crash in #842 extending parts to the left often caused a crash (rj) 14.08.2020 - Fix MusE hanging when calling the color dialog. There is now an option in Appearance->Style to disable native dialogs. (kybos) 13.08.2020 - Fix horizontal slider groove length. (kybos) 11.08.2020 - Implement theme specific icon overrides, both global and user level. (kybos) 09.08.2020 - More styling options for knobs. (kybos) 08.08.2020 - Piano: Reimplemented drawing, changed from pixmaps puzzle to vector-based drawing. (kybos) - Piano: Separated key selection from playback. Made markers/selections better visible and independent of each other. (kybos) - Piano: Made selection/current/pressed marker colors customizable. (kybos) - Piano: Made piano width changeable from stylesheet. (kybos) - Piano: Added tooltip showing current playback velocity. (kybos) - Fixed dark text on dark background in midi control canvas. (kybos) - Create new theme - Deep Ocean. (kybos) 30.07.2020 - Moved rewind on stop shortcut from arranger to global, fixes issue #839 (rj) 29.07.2020 - Midi export: Format 1: First track must have only timing/marker events etc, no notes. (Tim) - Midi import: Don't create a first track if no events left once stripped of recognized/absorbed events. In format 1, first file track is supposed to be reserved for timing/markers. - Midi import/export: New! Key signatures supported - supports 'minor' keys as well. - Score editor: New! Key signature names shown at top of staff. Minor keys supported. 25.07.2020 - Midi import: Fixed importation of SMPTE/MTC division midi files. (Tim) Converts linear time to ticks. Ignores existing MusE tempo map so that it plays at original speed - this allows the tempo map to be set up beforehand without actually affecting the song. 16.07.2020 *** MusE 3.1.1 *** 27.06.2020 - Midi patch popup menu: Include patch number in the items' text. (Tim) - Mixer strip labels: Added popup 'expand' button, to expand/close the strips. 26.06.2020 - Fixed bug left over from 21.01.2019: Midi controller graphs: Drag n drop broken. (Tim) Items were wrongly being erased after dropping. - Fixed issue 830: Wave Editor: Copy-on-write always triggered. Error from 01.01.2020 - Fixed issue 831: Compressed song files (.gz,.bz2): Blank song on load. Error from 01.01.2020 25.06.2020 - Some rework of mixer strip shortcuts. They now work in both Arranger and Mixer. Default shortcuts are Alt + Arrow keys to affect selected strip(s) (rj) 22.06.2020 - Fix slider/meter groove misalignment in mixer strip. (kybos) 20.06.2020 - Cmake option changes (Tim): Removed ancient ENABLE_VST and ENABLE_EXPERIMENTAL, and dangerous ENABLE_LV2_MAKE_PATH, and enabled ENABLE_PYTHON by default since it has been thoroughly updated. No code removed. PLEASE REBUILD your MusE ! 19.06.2020 - Fixed long-time meter and slider scale mis-alignment. New stylesheet property meterSpacing. (Tim) - Midi controller graphs: Fixed no repaint when adjusting vertical splitters. Clear WA_StaticContents in CtrlCanvas ctor - need full update because contents scale. - Midi controller graphs: Moved 'Add ctrl' button from bottom area to menu and toolbar as well. 18.06.2020 - Mixer + strips: Replace hard-coded shortcuts with configurable ones. Several weren't working. (Tim) New mixer shortcut category. - Updated Simpledrums so all parameters update a status field, previously it was impossible to know what the dials were set to. Changing the look of the dials would also be a good idea, left to do. (rj) 17.06.2020 - Fix main window being too small on large screens when MusE is started for the first time. The size and position are now adjusted dynamically for the current screen size. (kybos) - Fix main window size/position set to random values when a project or template is opened. The window is now only resized if the file contains explicit geometry information. (kybos) - Fix progress bar not being displayed when opening files. (kybos) - Mixers: Maximize button removed. Certain window managers ignore a set maximum size. (Tim) 16.06.2020 - Replace invisible icons in file dialogs in new dark theme. (kybos) - Unify colour configuration file naming. (kybos) - Fix invalid Global area default in file save dialog. (kybos) - Fixed some bugs with Cursor mode in drum editor (rj) - Effects rack: Ensure updateGeometry is called if number of rack items changed. (Tim) Was not required for at least suse, but required for at least mint cinnamon. 13.06.2020 - Add option to hide menu icons. (kybos) 12.06.2020 - Menu: 'Mixer Automation' is deprecated, MusEGlobal::automation now fixed TRUE for now until we decide what to do with it. (Tim) 11.06.2020 - Audio: Fixed bad feedback / pinned meters with muted tracks in play mode. (Tim) Was problem near end of WaveTrack::getData(), what constitutes "have data" or not. Said "have data" when there was none when muted, causing AudioTrack::copyData() to build up feedback data in the buffers instead of clearing them. 10.06.2020 - Arranger: Removed the small toggle button for mixer strip display/hide and cleaned up the layouts. Function is now accessible from menu (Display->Toggle Mixer Strip) or by pressing F3. 09.06.2020 - Fix regression from 07.06: Qt::endl and Qt::dec only from Qt >= 5.14 (Tim) 07.06.2020 - Fix Qt 5.14 / 5.15 deprecations (Tim) QString::SplitBehavior > Qt::SplitBehavior, QTextStream::endl > Qt::endl, QTextStream::dec > Qt::dec, Qt::WindowFlags f = 0 > Qt::WindowFlags f = Qt::Widget, QWheelEvent::delta > QWheelEvent::pixel/angleDelta, QWheelEvent::globalPos > QWheelEvent::globalPosition::toPoint, QWheelEvent::pos > QWheelEvent::position.toPoint. 06.06.2020 - Fixed regression at 23.05.2020: Crash in viewMouseReleaseEvent. Copy/paste typo. (Tim) - Many new customizing features implemented, internal colors, css properties. New theme created (Dark Flat) as a showcase/proof of concept. (kybos) 04.06.2020 - Fixed blank mixers upon auto-restart. (Tim) - Make MusE QMainWindow own the mixers. They had no owner, no deletion. Leak? Test OK so far. - Reload the last song upon auto-restart. Templates included, and midi config loading choice. Was tricky, possible wee corrections later...? 27.05.2020 - Midi controller panel: Replaced 'Ctrl', 'S', and 'X' button text with svg icons. (Tim) - Midi controller panel: Replaced 'per-note-velocity' bitmap with button and svg icon. - Midi Controller panel redesign: Create only required controls, not keep all 4 alive but hidden. 24.05.2020 - Fix regression at 06.04.2020: Can't connect Jack ports to audio input/output tracks R chan. (Tim) Don't check out of bounds index in AudioInput/Output::registerPorts(). When it is called by Audio::msgSetChannels(), the new number of channels has not been set yet. - Added support for adjusting automation for Global structure cut/insert (rj) - LV2: Fix plugins without 'programs extension' support: Send directly as midi events. (Tim) Fixes plugins like gmsynth.lv2, which have midnam support but no programs extension support. 23.05.2020 * New - PianoRoll Speaker button has two selectable modes, single note and chords: (Tim) 16.05.2020 - Popup menus: Changed the text '' in submenus, to the first item's text in the submenu. (Tim) - Oops, removed redundant addAction() in RoutePopupMenu::addMenuItem(), causing duplicate items at 'More...' menu breaks. - Fixed regression at 03.02.2020: Plugin dialog user 'categories' not showing LV2 plugins. 18.04.2020 - New option in Global settings -> GUI tweaks to (not) keep transport window on top (kybos) 13.04.2020 - Fix regression from 12.04.2019: Midi controller cache not proper after loading song. (Tim) Since we now resolve track and instrument references AFTER a song file is FULLY loaded, calling addPortCtrlEvents() on all tracks BEFORE doing the resolving had ill effect. Now port midi control cache events are added on all tracks AFTER the resolving. - Simplified several sections regarding adding/removing port midi control cache events. - Removed ALL 'old-style drum track' code. Track type NEW_DRUM has been renamed DRUM. - Simplified many drum controller (per-note) code sections to call a common function in MidiTrack::mappedPortChanCtrl() - Support midnam controllers (gmsynth.lv2 for example). This was very difficult. Midnam supports per-channel AND per-patch controller lists. Instead of adapting our own instrument and controller classes to NATIVELY support that, which would be NICE but much more difficult and time-consuming, the instrument simply 'compounds' its own controller list and the midnam controller list together and presents that to the outside code looking for a controller list. But no changes to our own instrument definitions and structures were made. This still required many changes to the outside code in order to pass the patch number. TODO: Midnam supports enumerated controller values. Use them! - Moved classes MidiController and MidiControllerList from midictrl module into a separate midi_controller module (library in libs) so that our midnam library can use it. 06.04.2020 - Fixed bug 790 Crash when dragging parts to blank area. (Tim) Bonus: Now supports dragging parts from multiple non-contiguous tracks, it attempts to create new tracks for all of them. Bonus: The 'duplicate tracks' dialog pops up, asks what should be copied. Bonus: Previously, user must press undo twice to undo the move. Now a single undo. - Fixed 'drum ordering' bugs (drum notes order) after drag parts or duplicate tracks. - Fixed some flaws with track copy constructors and assign() methods. - Added new class TrackNameFactory. A complete list of new UNIQUE track names can be requested BEFORE the new tracks are added to the song. All requests for unique track names now go through this central class. It uses a modified version of Robert's smart '#' character detection and naming sequencing. Robert wrote: "there is still a gaping hole in the logic, making multiple duplicates of multiple tracks still does not produce valid results" but his logic was OK - it was the fact that the tracks are not added yet, which this new factory class SOLVES. - Streamlined and fixed some problems with audio input/output track Jack port creation. The ports are now only (un/)registered when the track is added/deleted. - Added crash protection throughout audio processing against null destination buffers, which may occurr if Jack ports could not be created. - Extracted audio input/output track port registration in setName() into a separate registerPorts() method. The ports are no longer registered in setName(). 21.03.2020 - Add shortcut (Ctrl+U) for opening synth UI for selected synth-track in arranger (rj) - Added wave part extend beyond end of part if there is more wave file (rj) 18.03.2020 - Enable multiple resizing in piano roll (#748) (kybos) - Enable CTRL+Left Mouse Click item selection in edit mode in piano roll (kybos) - Enable Right Mouse Click for exclusive single selection in edit mode/piano roll (kybos) 17.03.2020 - Add more information to note tooltips in midi editors (kybos) - Add global setting for switching off the note tooltips (kybos) - Remove fixed track heigth from file templates, to enforce global setting (kybos) 09.03.2020 - Transfer Help content to github wiki, link Manual to wiki in MusE (kybos) 08.03.2020 - Fix EditPaste cursor HiDPI issue in midi editors->controller views (kybos) 05.03.2020 - Fix tool cursor not changing in midi editors->controller views (kybos) - Fix background color setting ignored in midi editors->controller views (kybos) 04.03.2020 - Add tool context menu to Score Editor (kybos) 28.02.2020: - LV2: Now shows MidiName patch names (gmsynth.lv2 for example). (Tim) - Added share/rdf/README to describe what's there and why. 23.02.2020: *** Final MusE 3.1.0 *** 21.02.2020: - Drum Editor: Fixed horizontal scrollbar width and 'Ctrl' button position. (Tim) 20.02.2020: - Partial revert of 16.02.2020. dssi-vst works now. Extra path tab was required. (Tim) Description labels added to dialog vst path tabs (*.dll vs. *.so etc) Linux vst paths now include 'lxvst'. It's a commonly used folder besides 'vst'. Changed all std::getenv() in plugin_cache_reader.cpp to qEnvironmentVariable(). 19.02.2020: - Fix new drum move cursor. New Canvas::setMouseOverItemCursor() fixes hover cursor. (Tim) 18.02.2020: * New - MidiName instrument support, currently for LV2 : (Tim) Synths like DrumGizmo and AvlDrums should now show note names automatically. Will test FULL parser later with real midnam files, but should be OK for LV2. 16.02.2020: - Removed the VST tab referencing Wine VST and relabeled Native VST as VST (rj) - Remove Wine VST plugin type in dropdown as we only support that through dssi-vst and it is dead since many years 15.02.2020: - Fixed issue with plugin scan #763, where a valid vst plugin but nonfunctional was never added to the cache and hence caused a rescan every time (rj) - Added a question dialog for waiting for very slow plugins (like Addictive Drums 2) 14.02.2020: - Custom Drum Editor 'move note' cursor, fix a few others. Fix mouse-up restoration. (Tim) 13.02.2020: - Reverted previous addTrack change, it affected Insert menu which then behaved wrong. (rj) * Changed Add Track alternative in edit menu to have this effect instead * Added Insert Track to edit menu also, together with shortcuts - Oops! Fix bug 765: Crackling in recorded files. Caused by 01.01.2020 commit. (Tim) Changed SndFile::write(): wrFrames = realWrite(srcChannels, src, n, liveWaveUpdate); to wrFrames = realWrite(srcChannels, src, n, wrFrames, liveWaveUpdate); - Track List: Make current selected track darker than other (multi-)selected tracks. 12.02.2020 - Changed addTrack shortcut to insert after selected track, inserting before felt strange and made it impossible to insert tracks at the end (rj) - Drum list note names: Fixed mixing of default names with synth note names. (Tim) When there was no note name available from a synth for a note, it was allowing the default note name to appear instead. This is fixed and the note name will be blank now, that is, drum list should now fully respect the complete note name map provided by the synth - including unlisted blank names. 11.02.2020 - Some more work on MidiName support: Completed FULL parser and C++ bindings. (Tim) 04.02.2020 - Master track graphic editor: Fixed tempo ramp function was not undoable. (Tim) - Referring to 30.11.2019: "Fixed crash in drumlist when synth plugin was missing." Observed crash still there immediately (the drum list was empty). Fixed and tested OK, saving and reloading, even if synth plugin is missing. The drum list is filled now. Even if the missing plugin was MESS fluidsynth (unlikely), the default map will be used since no note names are available from the synth. 03.02.2020 - Fix issue 763: Plugin cache recreated on each startup. (Tim) When there was more than one LV2 plugin with the same name (but different uri) such as deteriorate.lv2/downsampler* and avw.lv2/downsampler*, the cache was recreated every time, and only ONE of the plugins was allowed, the other ignored. This MAJOR REWRITE of our cache and plugin system fixes that AND allows both plugins, as long as the uris are different (this is allowed in LV2). For example, both deteriorate.lv2/downsampler* and avw.lv2/downsampler* are now shown. The LV2 cache file has been ELIMINATED. It was useless, not necessary, caused problems. To distinguish between like-named plugins, several places where plugin names are shown now also show the uri either as a tooltip, or appended to the name, or replacing the library string, such as plugin dialog, synth menu, synth config, strip labels, tracklist... In addition, there was a problem with dssi-vst which caused cache recreation every time, since the dssi-vst library installs itself in BOTH the ldaspa and dssi folders. This has been fixed. The ladspa cache file now contains additional ladspa and dssi dssi-vst entries. TODO: There is a remaining problem: A shell plugin that reports no plugins may still trigger cache recreation every time because there is no entry in the cache file. Such as dssi-vst not finding any plugins. To fix this, the cache files must change from a 'flat' listing to one ALWAYS listing the shell plugins - and the plugins they contain (if any). 01.02.2020 - Global cut/insert: Overhaul. Fix crashes with markers. (Tim) Cherry pick from pos_ctrls_fixes branch: Redesigned markers and marker viewer. Marker functions now undoable! (Lack of that was causing crashes.) - Tempo, signature, and key maps: Fixed problems with Global cut/insert. Replaced complex realtime stage-2 map item-by-item operations with simple swapping of entire map with complete replacement map pre-composed in stage 1 (gui thread). Virtually zero cpu time, and realtime safe, no potential allocation or deallocation. TODO: Sig still seems to not undo quite correctly, possibly due to raster-snapping. Tempo list operations must be executed before wave part splitting! So two separate operations are done - user must hit undo TWICE to undo them! (Better that for now, rather than one single BUT INCORRECT undoable operation.) Most of this stuff is pre-existing. Still, it is better now ;-) 30.01.2020 - Replace deprecated QByteArray::[] 'past-end' usage with append() in xml module. (Tim) Caused thousand of errors in debug mode, very slow to start up. 29.01.2020 - Fixed and improved custom columns functionality (kybos) 26.01.2020 - Fixed broken version comparison macro in fluidsynti.cpp (Tim) - Replaced obsolete QTime with QElapsedTimer. Tested OK. - **Bumped up** song file version to 3.3, req for some new header fixes by kybos. 24.01.2020 - Global settings: Split GUI behaviour tab into two separate tabs (kybos) - Add another set of mouse shortcuts for range markers - Ctrl+Left/Ctrl+Right (kybos) 23.01.2020 - MESS FluidSynth: Add key modifiers to distribute soundfonts to all/empty channels with single click (kybos). 15.01.2020 - Cherry pick five things from pos_ctrls_fixes branch: (Tim) 1) Fix when tempo serial number is incremented. (Fixes broken looping etc.) 2) Add rounding choice to tempo tick-to-frame methods (part of pending Pos fixes). 3) Remove awkward lone undoOp in song.h 4) Pos class makeover: New 'lock'. Rounding choice. New method msmu() (MinSecMsecUsec). 5) Add default getDSP_Load() in class AudioDevice, remove useless ones in RTAudio/dummy. 14.01.2020 - LV2: Remove deprecated LV2_Event_Buffer and LV2_URI_Map_Feature. (Tim) 12.01.2020 - Oops. Reinstall missing darkgreendot.xpm (Tim) - Fix compile error when RtAudio headers not found. - Fix "lv2host_module does not exist" compile error when LV2 support disabled. 09.01.2020 - Fix rubberband compile error + pull trackheader size fixes from kybos. (Tim) 01.01.2020 * NEW - Audio live! time-stretch and samplerate-stretch conversion : (Tim) Merge from audio-convert branch. Many changes and fixes along the way. - Wave editor has two new tool buttons which place two new types of markers on the canvas. Move the markers around to affect the sound. Click on a marker to select it. Press Del to delete the marker. - Wave module moved into separate library with no external dependencies. - New MuseFrame_t MuseCount_t types. 16.12.2019 - Exposed new colours for stylesheets. See Ardour and Dark qss files. (Tim) - Fixed #740: sf3 files crash. libinstpatch code: We did not check returned pointers. 14.12.2019 * NEW - Audio effects rack size increased from 4 to 8 : (Tim) - New right-click context menu entry on strip labels: "effects rack visible items". 13.12.2019 - HOT Bug fix: Missing 3 MESS synths, undefined symbol MusEGlobal::config (Tim) Cause: #include "gconfig.h" in icons.cpp brought in core globals which should not be in these synth plugin libraries (or in icons module). Removed the line and instead passed the desired global value to initIcons(). That said, the plugins really should be stand alone and not use the icons module until the icons module becomes a true shared resource library. (Currently it is initialized by core, so not quite self sufficient yet.) So... I copied the required icons to the relevant synths, decoupling them from the icons module. By using icons directly from qrc instead of icons module, I was also able to remove the icons module from inclusion in widgets module (yay!), removing a further dependency of simpledrums synth which was missing too. * Module independence is a goal to strive for after 28.01.2018 MAJOR changes. 11.12.2019 *** MusE 3.1.0pre4 *** - Customizable size of icons and custom tool cursors (kybos) - Replaced one-letter column headers by icons in track header (kybos) - German translation updated (kybos) 10.12.2019 - Dependency problems: Moved several files. All source together with ui files now. (Tim) - Remove all dead obsolete trackinfo 'alt' button code. - FIXED: Mixer strips missing if loading an older song saved before 20.10.2019 - FIXED: Duplicate strips found in songs saved before 20.10.2019 - FIXED: Long-time mixer problem with restoring size. 09.12.2019 - Remove some more "old vs. new" drum track stuff, change some text etc. (Tim) 08.12.2019 - Added option for opening MDI subwindows maximized (kybos) - Oops, fix stuck icons from 06.12. (Tim) - Fix gross misnomer throughout, should be "timebase master, not "transport master". - Fix EffectRack::songChanged() crash when reloading songs: Check track still exists. (Tim) 07.12.2019 - Update start play position when making a seek. Rewind to Start behaves more logical then, imho (rj) - Fix some compile warnings with LV2 worker functions, some others. (Tim) - Moved appearance and confmport cpp/h files from /muse to muse/components. Trying to solve some dependency problems... 06.12.2019 - Fixed bug #719 downmix to existing file appends to the old file (rj) - Transport master fixes: New button on transport. Fix connect at startup etc. (Tim) Seems there is no way to ask Jack if are we transport master (without setting it). So new code in our Jack driver - and a 1Hz watchdog - monitor for changes to master status. Care was taken to not disturb who else may be master. Click flashing button to force. Tested the new button OK with three instances of MusE competing for transport master. 05.12.2019 - Added File->New from template... menu entry. File->New loads default template (kybos) - Fixed inconsistent UI texts where appropriate: (kybos) * Title-style for menu texts and window titles * Sentence-style for all other UI texts * Three dots (...) for menu entries triggering a dialog (like Save As...) - Fixed inconsistent tooltip font size (kybos) - Vector replacements for many pixmap icons (kybos) - Fixed bug #720 arm follows track did bad things while recording (rj) 03.12.2019 - Eliminated all but one tr("Empty") string usage in plugins/pipeline/rack. (Tim) - Snooper: Provide flashColor property. Changed it in Ardour and Dark qss themes. 30.11.2019 - Added a specialization of QMdiArea to work around problems with PgUp and PgDown shortcuts as it was found through Snooper tool that this is the widget that was stealing the keypresses. - Another keypress fix in pcanvas.cpp to allow Return to be captured - Fixed a crash in drumlist when using a drummap and opening without the port having been properly initialized (in this case the synth plugin was missing). The dm->port had an extreme value, now the range is checked. (rj) - Fix #697: On HiDPI displays, plugin native UI windows are automatically scaled by Qt, but most of the native plugin UIs are not scalable or apply their own scaling. There is now a global setting for reverting the Qt window scaling. It can be overridden for each soft synth track by a local setting in the Quirks toolbar. (kybos) - Fix # 708: Surge-vst crashes. It called our pluginHostCallback() immediately from instantiate() before we stored the pointer. Added VstNativeSynth::openPlugin() (Tim) 28.11.2019 - Fixed the DidYouKnow menu alternative so it shows the dialog even if the checkbox for not displaying it is checked. (rj) - Added a few more lines to the DidYouKnow.txt database - Snooper improvements, fix crashes, fix 'parented top level' not working right. (Tim) 27.11.2019 - Unfortunately had to revoke the 'easy fix' below for getting more shortcuts to work there were side effects... The search goes on for the 'real' solution (rj) - Snooper improvements. Moved to Help menu. Live realtime monitoring of events. (Tim) 26.11.2019 * New - Introducing the Snooper (object tree) for coders and stylesheet authors: (Tim) Click new Snooper button in Appearance to start. Then click on anything anywhere. 23.11.2019 - More keyboard shortcuts * First of, I found out that several shortcuts never end up in the dispatcher since they (rj) were tested against the "accepted" property. Seems this is not reliable nor needed, it affected atleast PgUp, PgDn and Return. * Arm currently selected track can now be bound: Default Ctrl+Return * Goto end of song can be bound: Shift+W 22.11.2019 - New solution to making bar lines more visible. Basically removed more hardcoded colors and made them configurable in the appearance dialog (rj) - Fixed escape-key shortcut for closing a few more dialogs/editors: master track list/graphical, instrument editor (rj) - Advanced router stylesheet support: New property 'RouteTreeWidget::categoryColor'. (Tim) Also specific instance IDs Router_input_tree and Router_output_tree, if desired. 21.11.2019 - Add command line option -C "Do not re-create plugin cache." (Tim) Avoids (looong) repeated re-creations in some cases. Helps devs avoid after rebuilds. 19.11.2019 - Soundfont *.sf3 files: Small tweak allows our MESS fluidsynth to load them. (Tim) 18.11.2019 - Add dedicated new 'mute parts' svg icon, for 17.11 (Tim) - Bug 681: Fix multichannel track feeding aux track, no audio. In AudioTrack::copyData(). - Fixed metronome selection of wave files, the selection in the ui didn't actually do anything. (rj) 17.11.2019 - Introduce new scalable icons and cursors for edit tools (kybos) Improved cursor interaction in piano roll/drum editor (event canvas vs. control canvas), cursor shape shows when cursor has no function in given area Consistent cursor shape display in all editors affected (midi, part, score, master, wave) Correct cursor size on HiDPI screens (fixes issue #637) - LV2: Fix sequencing/syncing plugins (Helm, LV2 metronome, NoiseMak3r...) (Tim) Fixed TimePosition usage. Add quirks class for different plugin behaviour observed. - Generic plugin UI: Added 'Quirks' buttons to toolbar: "fixed speed" (fixes NoiseMak3r modulator stuck in small loop in stop mode for ex.), "transport affects latency" (self-correcting LV2 Example Metronome for ex.), and "override reported latency" (for plugins with broken or inaccurate latency reporting). 12.11.2019 *** MusE 3.1.0pre3 *** 11.11.2019 - Added tab in Aboutbox with some runtime of MusE, feel free to add more (rj) - Disabled rate and size for audio device when Jack is selected as we currently cannot change this from within MusE (rj) 09.11.2019 - Reorganize instrument popup menu. (Tim) 08.11.2019 - Fixed missing font scaling in drum editor at lower zoom level (kybos) - LV2: Fixed program_changed callback lv2prg_Changed(), seemed completely wrong. (Tim) 07.11.2019 - Add: Tooltip for current note/instrument in the midi editors (kybos) 06.11.2019 - Adjusted raster drawing in view.cpp so it would draw lines that didn't (rj) draw correctly on my rig. We'll see if anyone objects * New - Instrument can now be changed on a Synth Track: (Tim) For example you can now set 'GM2' or 'My cool instrument' on ANY synth track. Not as useless/dangerous as it sounds. Tested OK saving/loading/changing instruments. - Add: Range selection (shift-click) for tracks (kybos) 05.11.2019 - Oops. Restore auto-create user instruments folder, likely swept away by 03.09 (Tim) 04.11.2019 - Implemented issue 644: Rewind on stop (rj) Added setting directly on Settings-menu as I think this might be a setting that is often changed might even consider making a toolbar button. - Some cleanups, especially the addition of using of an enum for CPOS,LPOS,RPOS 03.11.2019 - Fix issue 667: Hang on automatic restart. Reset jackStarted upon restart. (Tim) - Fix issue 638: Font too small in popups started from mixer strip (kybos) - Add: Double click on track ID selects all tracks of the same type (kybos) - Fix: Change piano keys in piano roll to rectangular shapes to avoid pixelation when scaled (kybos) 02.11.2019 - Mixers and strips: Mods and reverts to 20.10. (Tim) Removed new track UUID. Found a way to work with track index and a local LUID instead. - New: Persistent synth tracks. One consequence of the above fix is that synth tracks are preserved if the synth is not found. All data is preserved if project is saved. - Class EvData rewrite: Safer. Members protected. New methods such as resize(). 30.10.2019 - Fix some memory leaks reported by heaptrack. Still others are puzzling... (Tim) 29.10.2019 - Latency correction: Fix feedback/noise using channel routing (vs. omni routing). (Tim) - Fix issue 666: Crash if 'Prefer knobs not sliders' clicked on disabled instrument. (Tim) Changed delete to deleteLater() in ComponentRack::clearDelete() Disabled label was trying to call clearDelete() which deletes itself. 28.10.2019 * LV2 Major rewrite of state/worker support and more, fixes several synths: (Tim) - Worker message data now uses new LockFreeDataRingBuffer for bidi comm. Fixes major problems with synth workers. - Fixed problems with how our worker thread is awoken. Fixes several plugins. - Fixed problems with how our EvBuf reads port data. Fixes several plugins. - Fixed state file storing/reading and locations. State files are stored in a unique configuration folder for each plugin, even if the song file is not in its own folder. - All file references IN or UNDER the song project folder are deemed RELATIVE, to the plugin's configuration folder. All file references OUTSIDE the song file folder are deemed ABSOLUTE. - New cmake option ENABLE_LV2_MAKE_PATH, it is disabled by default !!! If enabled (NOT RECOMMENDED), the above mentioned configuration folders are used. If disabled, NO external files or folders are created parallel to the song file. This is what we want with MusE, all data stored inside the song file if possible. - Tested OK saving/reloading several synths that were broken before. 20.10.2019 * Mixer and strips - Save entire state. Fix label text: (Tim) - Entire state of mixer is now saved, including user-adjusted strip widths. The state is preserved even if no mixers are opened (created) during a session. This required re-working the mixer and strip configuration (song and config xml files) to be more persistent. - Mixer widths restoration: Fixed (hopefully) long-time problem with mixers not opening at their stored song size if the mixers are already open. - Strip track labels: Replaced multi-line word-wrap with elided (...) text. Reduced variable-font variation somewhat to avoid too-small fonts. Added tooltips on the labels to help see the full text. 13.10.2019 - Improvements to 11.10. Fix accuracy problems (re)storing view state. (Tim) - Jack midi: Suppress repetitive warnings of unused Midi Sense (ME_SENSE 0xfe). 11.10.2019 * Midi Editor now keeps state (zoom, pos, controllers) after closing: (Tim) Works on a per-part basis: Whatever you set on a part it remembers. Pianoroll Editor and Drum Editor done. 09.10.2019 - Pianoroll Editor: Increased default vertical zoom level from 1 to 2. (Tim) Was challenging. The global variable 'yscale' in pianoroll.cpp had no effect no matter what I put there. Fixed class ScrollScale (the slider!), had a problem with the initial scale. Replaced its curious successive approximation log() code with an actual log10() call, rounded up! Tested OK. 05.10.2019 - Merge PR 665 from hfiguiere: Remove unused "all.h" and all relevant code. (Tim) - Fix crash: SimplerPlugin (LadspaPlugin). Regression of 03.09. Wrong cache loc. 03.10.2019 - Redesign: All SongChanged flags are now 128-bit (split two 64-bit). (Tim) 128 bits of flags split as two 64-bit values in class SongChangedStruct_t, to support 32-bit OSs where 128 bit integers may not be available. All the SC_* #defines are now wrapped in a SongChangedStruct_t constructor. The new upper 64 bits are optional. They default to zero in the constructor. - Setting of extSyncFlag, useJackTransport, and useMasterFlag are now operations synchronized with audio. extSyncFlag and useJackTransport are now simple booleans. - Removed obsolete experimental _AUDIO_USE_TRUE_FRAME_ code. 15.09.2019 - Event List Editor: Rebuild upon time signature changes, shows correct BBTs. (Tim) 03.09.2019 - Replaced all hard-coded paths in main.cpp with QStandardPaths. (Tim) *** NOTE: This means the location of MusE config files and names have changed! Everything has been moved to ~/.config/MusE/MusE EXCEPT the qt config file which remains in ~/.config/MusE. The MusE config file 'MusE.cfg' has been renamed 'MusE-seq.cfg'. The MusE qt config file 'MusE-qt.conf' has been renamed 'MusE.conf'. The QApplication now has an organization name, application name, and organization domain. All QSettings usages now defer to these values instead of hard-coding QSettings("MusE", "MusE-qt"). These changes comply with QStandardPaths policies and QSettings behaviour. - Replaced getenv with qEnvironmentVariable or qgetenv in main.cpp - Disabled loading of default template files 'MusE-seq.cfg' and 'MusE.conf' when either was not found. They should not be required. All default values housed in the global config struct or other classes are now honoured. 01.09.2019 * Command line complete redesign using QCommandLineParser: (Tim) Supports long options. Qt text translations enabled. Cross-platform, no getopt used. * Python remote control redesign: Uses QThread instead of pthreads to start/stop the Pyro server. Launcher python code simplified. No custom event loop. Uses daemon loopCondition to cleanly shutdown. New MusE command line options for the python bridge: --pyro-ns-host --pyro-ns-port --pyro-daemon-host= --pyro-daemon-port --pyro-comm-timeout

at line 32 * Fixed MREventList in mpevent.h, to use midiRTalloc instead of audioRTalloc. (T356) - Possible accidental(?) coding error since there is no other usage of midiRTalloc at all in muse. - If it does help at all, it might just ease RT congestion a bit. * Working on MIDI VU meters. They're too fast to see with jack, but should be OK with dummy driver. (T356) * KNOWN PREEXISTING BUGS - If you move waveparts around AFTER the current play position, then play, muse actually plays the same arrangement as if you had not moved the waveparts at all, just one time - rewind the play position and it corrects itself. This is because muse does not prefetch the audio again after the movement of the parts. Workaround - After moving waveparts, be sure to rewind the play position to a point AT LEAST before those parts. - Wave parts will sometimes mysteriously go silent for no reason. Move the parts slightly, or reload, or do certain other operations, then the sound comes back. Working on this important issue! 02.02.2008 * Fixed a slight error in BigTime absolute display. Now shows proper absolute tick and frame values. (T356) * Added an extra arranger wavepart right-click popup menu item - "file info". Shows what wave file(s) are used by the wavepart event(s). TODO: Either remove duplicate file names or list the files by event number. Spice up with more info like wave starting/ending points etc. (T356) * Removed some debug output lines from dummy driver - may have been causing intermittent crashes when moving position. (T356) * Some more fixes to arranger partcanvas wavepart drawing - speeded up and tightened code. KNOWN BUG: Does not correctly display more than one wave 'event' (file) per wavepart. (For ex. two waveparts 'glued' together). Working on this issue ! (T356) 27.01.2008 * Fixed MIDI mixer strip VU meters. Let there be light! Not the best solution, a choice between two so-so methods. Looking at how to improve it... (T356) 26.01.2008 * Pianoroll and drumedit now open at the current cursor position. (T356) (The cursor position is that moving black vertical line on the time scale). This means you just click on an arranger midi part where you want to open the editor. * Arranger, pianoroll, drumedit and waveedit now stay at their current (left-side) position when you use the scale bar. (T356) * Fixed 'end of part' drawing problems - now you are guaranteed to see about 1+1/4 extra bar after the end of a pianoroll/drumedit (also compensating for drumedit drummap/canvas splitter), allowing you to add/move/clone notes past the end and allowing muse to properly auto-extend the part (to the right). TODO: Auto-extend parts to the left when necessary. (T356) * Added vertical, and fixed horizontal, lasso/parts/events auto-scroll - even past the end of parts. (T356) * Fixed drawing of wave parts in arranger. Now you can rely on the 'picture' of the wave inside the part lining up correctly with the time scale, regardless of tempo/timesig changes in the song. (T356) * More fixes to wave editor. Fixed drawing of markers - they were much too close together. Added songChanged synchronization with arranger (you move/resize a wave part and the wave editor readjusts, etc.) WORKING ON: In the arranger, if you move a wave part to the left, the wave editor will readjust but the wave may be chopped off at the end. Investigating... (T356) * Corrected the drawing order of items from ALL parts in pianoroll and drumedit. For multiple-part viewing. WORK IN PROGRESS: Added ability to select items from different parts at once, and to move them. For now, only items from the currently selected part actually move. (T356) * Added small checkbox to Big Time display. Now you can switch between formatted or absolute time display. Absolute is handy for syncing with another app, or just knowing, absolutely, where you are in the song. Absolutely. Added ToolTip popups describing each 'group' of numbers (bars, beats, ticks etc). (T356) 20.01.2008 (ml) * Added function for deleting note overlaps in piano roll (usually makes fluidsynth grumpy - silencing the following note). * Some work on opening the editor at current position and selecting the leftmost item (ws) * fixed hanging note when editing multiple parts in midieditor and switching part 19.01.2008 (ws) * fixed Song::changeEvent(): part tick offset was not used * fixed removing of controller events in Song::deleteEvent(); this affects midi recording in replace mode and controller editing 12.10 * Fixed automation right-click popup menu 'prev/next event' not working after a tempo change. (T356) - Now you are guaranteed to be able to navigate the automation events, regardless of how many tempo or time signature changes are in the song, or if you change the tempo/sig(s) AFTER adding automation events. 12.06 * Fixed softsynths 'Fluidsynth', 'Deicsonze', and 'Fluid' to allow pitch wheel changes. (T356) * Fixed softsynths 'Fluidsynth' and 'Deicsonze' to allow MIDI patch changes. (T356) Note that I included MIDI patch change code in 'Fluid', but fluid is slightly broken and still doesn't respond. 'Fluid' should be removed from muse. It's obsolete. Use Fluidsynth. 11.22 * Fixed LASH loading and saving. (T356) - Symptom - Muse fails building with error about 'lash_get_fqn' not declared in scope. - Cause - You are building muse with LASH > 0.5.1, where certain header files have been removed. - Fix - Replaced both calls to lash_get_fqn with QString manipulations instead. * NOTE! I encountered a muse segfault when you use LASH to close the project. It says 'received unknown LASH event of type 10' (which is LASH_Server_Lost), then it says 'received unknown LASH event of type <>, then segfault and a glibc backtrace due to 'double free or corruption'. Not a big deal, but this will need to be fixed. * Tested OK saving and loading projects with LASH 0.5.4 09.02 ======================================= * 0.9 final tagged (rj) no changes from pre3 ======================================= 08.22 ======================================= * 0.9pre3 tagged (rj) ======================================= 08.20 * Fixed bug 1650953 - select and move segfault (T356) - Symptom - In piano roll, select some notes and move them past the end of the part, then muse crashes. Also occurs when pasting or adding notes past part length. - Cause - Canvas was not adding the new resized part to the midi editor. Also a bunch of other stuff needed to be fixed for it all to work OK. * Fixed broken master list editor. Should also cure bug 1687166 (T356) - Symptom - Open master list and nothing is listed (should be at least one default tempo and one default time signature). Click to add new tempo or sig and muse crashes. - Cause - Some code was added to lmaster.cpp: LMaster::updateList() which broke it. * Added auto part resizing to drum canvas (T356) - Symptom - In drums editor, there is no auto part resizing, like in piano roll. - Cause - Missing all part resizing code. Added. * Fixed intermittent crash when cutting a part. Improper 'delete while iterate' STL operation. (T356) * Fixed arranger part selection - selected parts now draw on top of non-selected parts. (T356) * Fixed arranger part operations (move etc.) to use selected part. (T356) * Fixed arranger keyboard left and right previous/next part selection. (T356) - Now you can use left/right arrow keys to cycle through parts in a track, especially useful for overlapping parts! * Regarding part selection - A remaining problem is that there is no 'z-order' drawing scheme. The part item list has no such ordering support. - This means a selected part will (now) appear on top of others, but when unselected, it will not stay on top. Adding that support could be messy. - Best to leave it alone for now. At least you can (now) cycle through overlapped parts... * Tested arranger part operations with worst case scenario - multiple tempo changes and multiple parts. Test OK. All should be OK now. (T356) * Added tooltip to song length box in arranger with text 'song length in bars'. (T356) * Fixed wave editor - Black selection bar intermittent drawing. (T356) - Symptom - In wave editor selecting a portion of the wave caused intermittent drawing of the black selection bar. - Cause - Unsure of exact cause or a permanent cure, but it's better now. But it still draws intermittently if you zoom in (x magnify) very very close. Working on this issue. * Added feature - Wave editor now allows you to open multiple selected wave parts and will display only those parts and you can edit them. Tested wave editor 'functions' menu and it works OK on some or all selected portions of the multiple wave parts. - This is in preparation for a possible automation viewer and/or editor. - Work in progress. Some rough parts still to clean up. (T356) * Fixed intermittent arithmetic exception crash in wave viewer. (T356) * Fixed bad wave drawing above certain zoom level in wave editor. (T356) * Enabled serial numbers in my deltaTick2Frame and deltaFrame2Tick routines, and merged code. Makes it a bit faster. (T356) ======= 08.16 * Reverted change in synth.cpp, realtime playback of softsynths did not work (rj) * fix for sustain pedal to work as suspected when looping. (rj) * Above involved adding "managed-controllers", whenever a controller is found time will tell if this is the right solution (rj) 02.13 * Feature request: 1650957 space bar should start/stop playback fixed by adding this possibility, old style is possible with property setting in configuration. (rj) 02.09 * Fixed bug 1645575 - Eraser tool causes segfault in drum editor (T356) * Added the project AUTHORS (whooo are you... who who...) (T356) 01.27 * Fixes to remove lots of warnings and potential errors in the code (WillyFoobar) (rj by proxy) * Fix for bug #1074808 "NRPN - still 128 values?" from Sebastian Gutsfeld (rj) 01.06 * Changed 'exit' to 'return' when plugins are invalid (rj) 01.05 ======================================= * 0.9pre2 tagged (rj) ======================================= 2007.01.03 * Fixed some major issues with undo/redo. (T356) - Symptom - Delete track(s), then undo, muse crashes. - Symptom - Rename a track, then undo, muse crashes. - Cause - 'Delete' destroyed the track(s), so undo would attempt to restore non-existent object. Other problems caused rename crash. Still more undo/redo code was causing problems, like left-over synth instances hanging around after loading a new song, or crashing upon loading a new song if certain redo items were still in the redo list... - Known bugs - DON'T rename a synth track then undo, muse still crashes. Not sure why yet. After 'un-deleting' a synth track, you will have to open the 'MIDI Ports/Soft Synth' window and manually restore its MIDI port. Don't do anything too crazy like deleting multiple tracks while playing, might possibly crash. After undeleting an audio wave track, its sound might not be sync'd with the rest of the song - simply hit the fast-forward or rewind button to cure that. * Prevented saving, and segfault upon loading, of routes in med file with empty names. (T356) - Symptom - Jack shuts down, so instead of attempting to restart audio, you save your work, but when you try to reload your song, muse crashes. - Cause - Upon Jack shutdown, all audio in and out Jack routes were erased, and muse saved the song like that. Upon reload, muse would crash due to the empty routes in the .med file. - Note that this does not cure the original cause of this (Jack routes erased), so although you can now re-load the song without muse crashing, you must manually restore all the audio in/out Jack routes. So please try to restart the audio first. (Trying to fix this issue...) * Add new soloing system. Check it out! (T356) - Symptom - You click solo on a track but you hear nothing, and all other tracks are muted. - Cause - Wasn't really broken, you just had to solo all other tracks in that routing path to make sure whatever you were attempting to solo made its way to its destination so you would hear it. Inconvenient, yes. Especially with a large song with a lot of tracks, groups etc. With this new system, this is done automatically for you. Even MIDI tracks driving synth tracks, too. - Known bug - If a MIDI track drives some external MIDI device which you feed the audio back into muse, there's no way for muse to know how to couple the MIDI and audio in tracks together. Sorry you'll have to manually solo both. BUT - I'm CONSIDERING adding this - If an audio in track's name BEGINS WITH the name of a MIDI track, I could couple them that way. For example - MIDI track named 'Track1' and an audio in track named 'Track1 Input', then muse could mutually solo both tracks properly... * Enabled MIDI mixer strip solo buttons. (T356) * Some other minor fixups here and there. (T356) - Other known bugs - Due to an earlier oversight, if you use your own custom LADSPA plugin GUI's (see README.ladspaguis), QCheckbox and QCombobox controls may not automate quite right. Note that this includes just one built-in custom GUI - for the 'TAP Reverberator' plugin. 21.11 * Fixed loading of .med files, with plugins, created before muse-0.9pre1. (t356) They will load, then save, correctly now. - Symptom - Load an old song with plugins and some of the plugin controls can't be moved. - Cause - Controller id's in med file mixed up, not save correctly, because of moving rack plugins around, which never properly switched the id's before muse-0.9pre1. * Fixed soloing (again). (t356) - Symptom - Delete a soloed track, then soloing is messed up - other tracks may be muted but don't show it, only way out is to reload. Speaking of which, loading also did not set track soloing correctly. - Cause - Upon deleting a track or loading a song, track solo ref count was not updated. * Added infrastructure for an improved soloing system, much more intuitive. (Not done yet!...) (t356) * Fixed spelling mistake - Freeverb "Rooom" size. (t356) 13.11 * By T356 * More automation fixes. Everything should be working now, check it out! * Although still no automation editor, all of these changes so far should help manipulate and navigate the events. * Fixed touch mode events storing, was not right. * Fixed a couple of stray signal connects which may have caused wacky controls. * Sliders, knobs and labels now respond to mousewheel and paging properly. * Linked slider/knob labels with sliders and knobs so paging or mousewheeling a label works smoothly and linearly, even with logarithmic controls. * Fixed integer controls (like Glame Bandpass 'stages'). * Fixed external plugin gui's to look like the built-in freeverb. This means they now scale properly with different fonts. :) * Hopefully the last of major automation conceptual changes - making sliders, knobs, labels and checkboxes work together with the four automation modes, barring any fixes, and a few tweaks to follow (like shift key for label mousewheeling, and how to make a label stop updating so values can be entered). Some more right-click menu options may follow (like store event, enable automation, enable interpolation). Automation readme to come (need it!). * Still to fix - Controls' right-click automation menu still a bit confused upon tempo changes. 10.11 * reworked mute, old solution caused problems for solo * solo is now united between midi and audio, they were previously interdependant. This has the downside that if a soft synth is soloed both the midi and the audio needs to have solo enabled (rj) * fixed issue with hanging notes for softsynths when stop is pressed (rj) * Backported fix from HEAD for using a file descriptor from two different threads, should make it work somewhat better with SMP (rj) * fixed faulty off value for aux when set to max (rj) * Allow wheeling of tracks from track list (rj) 08.11 * re-enabled mute for outputs (rj) 07.11 * Fixed timing bug causing MusE to depend on jack buffer setting this needs testing, it involved uncommenting some code that might have been commented for a reason (rj) 05.11 * Added support for auto-scroll for other drag operations but rubberband (rj) 04.11 ======================================= * 0.9pre1 tagged (rj) ======================================= 04.11 * Adopted RT locking method from 1.0 branch. (rj) * Preparations for 0.9pre1 (rj) 31.10 * By T356 * Fixed old bug - DoubleLabel right click causes popup and runaway increment. * Currently the increment/decrement is fixed at 1, so that's why it doesn't work for pan or other controls with 1.0 or less max. - So fixes to that are next... 29.10 * By T356 * Changing global tempo now updates things. (Like resizing wave tracks). * Fixed segfault upon lengthening a blank new wave track part. * Removed some thread-unsafe code from my first patch * Fixed ladspa plugin automation not working until plugin displayed. * Improved automation and ladspa plugin automation behaviour. - While stopped and in touch mode, clicking an automateable control immediately writes the value to the list, so you don't have to press play then stop just to store one value. - Smoother slider transitions from last touched value to previous stored values, so that they don't suddenly 'jump' to previous stored values when you let go. - Checkboxes now automated. Their behaviour in touch mode is slightly different than sliders - they remain 'touched' until you stop, but store only up to the last toggle done. - Checkboxes now check as soon as you click them. - Sliders now have 'mouse cursor capture (tm)' so you can actually grab onto the thumbs even if they are moving too quickly due to automation. Paging can still be done with right button. - Sliders and knobs now work properly if you 'page' them (click outside the thumb or dial). - Added right-click popup menu on all automateable controls with previous event (seek) next event (seek) erase event (if cursor is on one) erase range (between left and right markers) clear automation (clear all events) Known bug: Becomes slightly confused if tempo is changed after doing automation. Workaround for now: Press play to update the cursor to a new position. 04.10 * Select the right item when items are stacked on top of each other, for pianoroll and arranger (rj) * Do not display resize cursor when last track cannot be resized (rj) 03.10 * Apply effect rack before sending aux, changed after discussion with Thorsten Wilms (rj) * Auto assign internal softsynths upon creation (rj) * New method for pasting parts. If several parts are pasted they are now spaced according to their original spacing. This only works reliably when the copied parts are all on one track. (Possibly copying parts from several tracks should be disallowed), fixes bug #1516294 (rj) * Muting of midi-tracks no longer mute input, bug #1092343 (rj) 01.10 * Fixed issue with playing of controllers when moving playhead. basically all events from all parts where played at the beginning, fixes bug #1316542 (rj) 30.09 * Cached peak files are now updated when wave files are more recent, bug #1469177(rj) * Fixed bug with recording notes to existing part, bug #1552783 (rj) 29.09 * MusE now asks when imported wave file has differing samplerate, bug #1428248 (rj) 28.09 * Always restore sequencer configuration even when song is loaded without config, fixes bug #1092289, midi mixer settings not restored (rj) 27.09 * Optimizations and fixes to rubberband scrolling (rj) * Aux changed to be affected by volume and pan (rj) * Aux now affected by mute (rj) 26.09 * Fix for pianoroll selection of events in/outside loop with respect to part offset (ml) * Added lasso/rubberband scrolling in arranger, drumedit and pianoroll, bug #1057732 (rj) 24.09 * Followups by T356 * Fixed rack plugin change not clearing automation * Fixed moving an empty rack position * Fixed moving a plugin onto itself * Changed colours of audio output and wave track strip names to match icon colours 20.09 * Fixed aux sending (rj) * Patch from terminator356 to fix logarithmic feedback to plugin sliders (rj) 19.09 * Various fixes from Torsten Duwe (rj) - deicsonze undefined returnvalue - vam uninitialized value - 64bit fix for calling external wave editor * Fixed multiple part undo for move,drag and copy in arranger, bug #1516298 (rj) * Fixed multiple note undo for move and drag in pianoroll (rj) * Fixed cursor placement in List Editor upon deleting events bug #1417718 (rj) 16.09 * Following changes by { terminator356 [at] users [dot] sourceforge [dot] net } * The changes can be applied to muse-0.7.2preX or muse-0.8.1 and work fine in both. * Fixed soloing. Also multiple soloing now allowed. - TODO: Soloing of tracks which feed a group. Workaround for now: Solo the tracks AND the group. * Enabled solo buttons on the mixer strips. * Fixed major issues with rack plugin moving, saving and loading. * Fixed problems with multiple tempo changes and incorrect wave part lengths. * Changed plugin code to use double instead of float to stop certain errors, and consistency. * Fixed some minor spelling mistakes. * Fixed issues with audio track automation. * Fixed very 'jumpy' volume sliders and pan knobs under automation. * Fixed several other small problems. For example a duplicate define in song.h * Changed the way audio track automation works. - For ease of use, the current mode now remains selected, even after a file write and re-open. - To reduce the chance of accidental changes, for added visual warning, Touch and Write modes colour the automation mode box red. - Write and Touch modes: Only if the controls were touched, changes will be stored when the transport is stopped. Since the current mode remains active, you can move to another part and do some more automation. - Write mode: The controls are updated with stored values until you touch them, then they store from the time you hit play to the time you hit stop. If you touch the controls AFTER you hit play, you may get unexpected results so remember to initialize that 'first value' (at the time when you hit play), since muse will NOT 'fill in' from the time you hit play to the time you first touched the control, with the value of that first touch. (Maybe support this later). - If you load a plugin preset, if the control values don't seem correct, try setting the track's automation to 'Off', because the track will usually be on 'Read' - reading stored values. - TODO: Muse really needs an automation editor/viewer! Perhaps integrate with wave viewer. Also, add one more automation mode: 'Instant mode'. * Hmmm, oh yes - added full LADSPA plugin automation. Check it out! Compatibility with existing med files' Plugin section should be OK, but if you open an existing med file with plugins, and the plugins' control values are no longer correct, let me know. See comments (with these changes applied) audiotrack.cpp around line 557. - TODO: Checkbox automation not working right. We need a better checkbox - one which checks as soon as you press down! Also would like to add bypass and mute automation. 05.08 * Fixed build without fluidsynth (rj) 14.07 * fluidsynth: fixed pitchbend events, fixes bug #1404212 (ml) 13.04 * Editing volume and pan with keyboard on audio strip now works (rj) * Fixed issue with recording audio, fifo could overflow when when running with small jack sizes (rj) * fixed storing of wave files with relative path when located "under" the project path (rj) 28.03 ======================================= * retagged app.cpp and Yamaha-Motif-Rack.idf (rj) ======================================= * fixed build bug in app.cpp (rj) * fixed bug with Yamaha-Motif-Rack.idf (rj) 27.03 ======================================= * 0.8.1 tagged (rj) ======================================= * some lash fixes (rj) 26.03 * Added next/prev marker, keyboard shortcut (rj) 25.03 * Reverted fix for silent softsynths, synths were not silenced upon [stop], gah! (rj) * Added LASH support (patch from evermind @ gentoo) (rj) 21.03 * Added Motif-Rack idf from europeen (rj) 19.03 ======================================= * retagged dummyaudio.cpp (rj) ======================================= * Fixed build bug in dummyaudio.cpp (rj) 18.03 ======================================= * 0.8 tagged (was 0.7.2) (rj) ======================================= * Added idf files from linux chaos Waldorf-Q, Yamaha-01v,Yamaha-Motif, Yamaha-P100 (rj) 05.03 * Arranger no longer performs "seek" while editing when in ext-sync mode (rj) * Play/Stop disabled for all when in ext-sync mode (rj) 02.03 * more work on extern sync, loop works better (rj) * no-audio + extern sync does not hang anymore (rj) * muse now starts even if jack is not found (rj) * fixed a number of divide by zero errors mainly affecting zoom (rj) 11.02 * Added menu alternative for storing plugin preset (rj) 06.02 * Updated/improved swedish translation. (rj) 04.02 * Fix for softsynths going silent under load. Sometimes events arrive with time=0, this is now handled. Why it happens is yet unknown.(rj) 02.02 * check audioDevice and _client for validity (rj) 13.01 * amd64 fix in driver/rtctimer.cpp (ws) 09.01 * Added patch for vam from mane_grotesk (rj) 07.01 * On drumtracks, there was no check for non-existing ports. If a drum instrument was configured to output to a port other than standard for the track, there was a segfault. Now output to non-existing ports are simply ignored (ml) 05.01 * Added updated french translation from Intent (rj) * Fixed crash bug in pianoroll when moving several events outside part. (rj) * Fixed esc key in drumeditor name edit (rj) 03.01 ======================================== * 0.7.2pre5 (final!) tagged (rj) ======================================= 02.01 * Added popup when enabling rec for a track unable to create it's wave file (rj) 2006.01.01 * Enlarged listeditor dialog (FR:1392090) (rj) * Changed preset file extension of vam presets to .vam (rj) * No longer translates dir name for drummaps (rj) * Fixed crash bug when arrowing left in an empty editor (rj) * Added F2 name edit in drum editor (rj) * Added up/down arrowing in drum editor (rj) 30.12 * Fixed bug in detection of RTC (rj) * Removed ugly printouts from probeing for browser (rj) 29.12 * Reawoken Organ: (rj) - read/write current settings - automation of all parameters * Reawoken VAM: (rj) - read/write current settings - automation of all parameters - preset dialog 28.12 * Changed audio prefetch buffer to be dynamically sized after the jack buffers (rj) 27.12 * Fixed allocation of wave memory when reading and writing sound files fixes problems with audio-operations (rj) * Fixed problem when external wave editor was not correctly defined (rj) 26.12 * Race condition between threads caused lockup upon quit and load project. Fixed by checking if sequencer is actually running before making internal IPC call, made major difference (rj) 21.12 ======================================== * 0.7.2pre4 tagged (rj) ======================================= * Now dynamically extends parts if events continue after end and ignores pastes before part, fixes bug:1363066 Paste outside segment (rj) 18.12 * ExtSync improvements, handles relocation of playhead during stop (on my gear atleast), does not work during play (rj) * fixed bug building synths (introduced during last checkin) (rj) 14.12 * fast_log10 exchanged for HEAD version (old version doesn't work with gcc4), fixes problem with meters and sliders not working (rj) * Added patch from Martin Habets fixes core dump problem while building with LADCCA (rj) * Added patch from LarryL autoconf changes to synth building (rj) * Fixed drag in effect rack that it does not start directly (rj) * Adapted optimization parameters for gcc4 (rj) 13.12 * Possibly fixed issue with crashes during load of projects by putting delays between start/stop of sequencer and actual load operation. This should make sure that the process loop is idle. (rj) * now tries both RTC and Alsa (in that sequence) for main timer (rj) * added checks if alsaDevice is valid (fixes several crash bugs when muse goes zombie). Should be done in more places. (rj) * added check if audio is really running when jack calls process. Sometimes it's called when it should not. (rj) 12.12 * updated muse_ru.ts from Alexandre Prokoudine (ws) 11.12 * removed assert, fixes bug:1376783, deleting track with pianoroll open crashes muse 09.12 * Added patch from Daniel Cobras regarding compatibility with 64-bit systems (rj) * fixed crash bug when muse tried to show plugin-guis when the plugin did not exist (rj) 29.11 * fixed seg fault when deleting last note in pianoroll editor (ws) 13.11 ======================================== * 0.7.2pre3 tagged (rj) ======================================== * Changed back to AlsaTimer (rj) 8.11 * fixed typo in share/locale/Makefile.am 7.11 * removed some dubious locks in thread start (ws) 19.10 * Fixed bug 1329537 (User defined fonts not updated) (rj) 13.10 * added emuproteus200.idf from Piotr Sawicki (ws) * updated polish translation 12.10 * added polish translation from Piotr Sawicki (ws) 26.9 * Handle restart of Jack and restart of audio (rj) 21.8 * Added new timer classes from Jonathan Woithe. (rj) 14.8 * Solo for audio tracks improved by removing the possibility to mute Output tracks (rj) * Implemented REPLACE for midi recording (seems to sometimes miss notes though...) (rj) * Fixes for Appearance dialog, background pic, event display (rj) * Marker window now toggling (rj) * Added "raise" to more dialog windows (rj) * compress event display in parts somewhat (rj) * reverted pipeline again...bah...think before you change (rj) * bounce now stops correctly (rj) 13.7 * Fixed position of import of wave files, inserted at cursor, now inserts at mouse (rj) * Added drag&drop support to plugin racks in mixer, internal and to/from disk (rj) * Changed the plugin pipeline depth to 5.. four is too little.. though it should be runtime extendable, wip (rj) * Added patches from Daniel Kobras that correct errors in EditMetaDialog and old html docs. (rj) * Added uppercase filters to midi import (rj) 12.7 * Added quick search to LADSPA plugin dialog (rj) 11.7 ======================================== * 0.7.2pre2 tagged (rj) ======================================== 10.7 * Added possibility to edit selection of wave in external editor (ml) 9.7 * Added gain modification functions + misc to waveedit (ml) 8.7 * Updates to wavefile modification, implemented normalize, fade in, fade out and reverse of wavefile selection. Removal of wavefile undo data on shutdown (ml) 7.7 * Added undo/redo handling for modifications of wavefiles + mute of selection in waveedit. expect updates. (ml) 6.7 * Added selection to waveeditor + ignore unhandled keyevents in waveeditor (ml) 4.7 * Implemented resize of waveparts (ml) * Added Idf files by Steve D for Roland FantomXR, SRX-02 and SRX-09 (rj) 2.7 * Fixes for waveedit: offset problem fixed (no more empty waveedit windows), initial zoom value to roughly match size of part (ml) 01.7 * Fixed bug with loading of background pixmaps (rj) 28.6 * Only send MMC continue message when in playback state when seeking (rj) 22.6 * Fixed bug 1199171 (Time change: a part does not completely fit into 4 bars), part resize problem (ml) 21.6 * Added scrollwheel support for vertical scrolling in arranger, pianoroll and drumeditor (ml) * Fixed bug 1056996: Multiple selection, but single paste. Possible to copy several parts in arranger (ml) 20.6 * Fixed bug 1092424: bug in reposition of instruments in drumeditor (ml) 19.6 * Added recall of last entered directory in filedialog for global and user mode + recall of opening mode (global, user or project) (ml) 18.6 * Fix for drumtracks and part export/import * Fix for opening Midi port/softsynth dialog when already open (now raised and set to active window) (ml) 13.6 * Added export/import of midi parts (.mpt-files), drag & drop also possible (ml) 05.6 * Fix for generating midi clock, needs field testing. (rj) 04.6 * Added fixes to AlsaTimer and DummyAudio from Jonathan Woithe (rj) * Added fix so AudioPrefetch is initialized upon starting the sequencer, which for instance happens when loading a song (rj) 24.5 * Added Roland E-28 idf file from Jonathan Woithe (js) 16.5 * Updated ladspa-gui for newer version of tap-reverb (rj) 15.5 * Allows for several midi devices with the same name, they are now renamed internally so they have a unique name. This is a partial fix for synths that do not correctly give each instance a new name. (rj) 12.5 * s1 softsynth added square wave with parameter (rj) 11.5 * Fix for bug 1198747, tests for fluidsynth and rtcap in configure.ac (rj) * Fix for bug 1198744, added patch for reading browser setting from config without crashing, from Philip Nelson (rj) * Fix for bug 1188767, downmix won't stop playback until reaching the right marker (rj) 08.5 * the instrument list in the drumeditor now has fixed width when resizing the window (finally, it made me nuts) (ml) * added nudge event position left/right w keyboard (ctrl+left/rightarrow as default) to pianoroll and drumeditor (ml) * added fixed length command to pianoroll, uses snap-to value (ml) 07.5 * added snap/quantize patch from Petr Mazanec (snap of notes in pianoroll+drumeditor is now controlled by snap, not quantize) (ml) * simpledrums: added save/load of setup to file, bugfixes. simpledrums version is now 1.0 (go figure! ;) (ml) 06.5 * No longer crashed when enabling audio metronome when there's an aux (rj) ======================================== * 0.7.2pre1 tagged ! (rj) ======================================== 04.5 * extern sync algorithm "reworked" (rj) 1.5 * simpledrums: backported fixes for channel number + memory deallocation, fixed issue with clearing of sampledata, I hope (ml) 30.4 * fluidsynth: bankno is saved to project, switched to hbank from lbank (ml) * Now really runs libtoolize in autogen.sh (rj) 29.4 * make sleep() in watchdog thread non interruptible to avoid watchdog timeouts at startup (ws) * added vst preallocation of memory "fix" (rj) * More fixes to filenames containing dots (for instance wca files) (rj) * Added Yamaha-PSR275 instrument file by Petr Mazanec (rj) 27.4 * fixed patch-info issue in Fluidsynth (bug 1191214) (ml) 25.4 * fixed bug w paste in drumeditor, 1189267, patch from P Mazanec 18.4 * removed file ltmain.sh; this file is now created by "libtoolize" command in autogen.sh (ws) 16.4 * Fixed bug 1152441, filename can now have several dots (ml) * Fixed bug 1183980: fluidsynth pitch controller wasn't given to MusE from the synth (ml) 15.4 * Added a redundant test that makes midi input work on PPC for some reason. (will research a better fix for 0.8) (rj) 10.4 * Added an error popup when importing wave files fails. (rj) 30.3 * [DONE] midi -> edit instrument is not implemented -> remove it (js) * [DONE] same for random rhythm gen -> is not implemented -> remove it (js) * [DONE] BUG: pianoroll editor -> tools resize wrong, they should stay on max they need instead of width fit (js) * have to go to the dentist on 7.3.2005, god help me, i fear this will kill me (js) 29.3 * README changed some links (js) * README added some icons to arranger (js) * added support for german localisation (30% translated, still quite bad) (js) * help: changed muse homepage location (js) * more to come (js) 28.3 * Fix for overflow when importing midi (rj + ml) 6.3 * Added some fixed on dialog handling, mainly "esc" will close the widget now. (js) * As usual added the icons which i forgot to add in the last release * Corrected the drums icon which was a wave icon (in the dropdown, arranger) 26.2 * Added Roland-SCD70.idf from Emiliano Grilli (rj) 09.2 * fixed bug with sending start play w/ midi-clock (rj) 01.20 * Added RT support and better working timing to DummyAudio backend (rj) * New version of MC505.idf from Wim VW (rj) 01.18 * Added script to convert MusE 0.6 songs to 0.7 format (rj) 01.17 * Midi clock sync first test, unstable (ml) 01.14 * patch from Erwin Scheuch-Heilig to allow for libtool >= 1.4 (ws) 01.10 ======================================== * 0.7.1 tagged ! (rj) ======================================== * ZynAdd instrument def file added (ml) * Now the length is updated when importing a midi file to a project, fixes bug: 1056994 (rj) * Disabled freewheeling for bounce functions (song.cpp:_bounce) (rj) 01.09 * Fixed bug: 1094622, MidiTransform now uses new controller types (ml) * Fixed bug with custom plugin guis that caused them to be uninitialized (rj) * fixed just introduced jack graphChanged problem (rj) * Fixed a crash issue with Thread class (only did happen when you mess around) (rj) * Synti tracks don't crash when being renamed (rj) 01.04 * Fixed a crash problem when using several fluidsynths (rj) * Now fluidsynth restores most memory upon deletion (but not all it seems) (rj) * fluid disabled when fluidsynth disabled (we should probably disable it all together) (rj) * Fixed mixdown clash with auto-rec enable (rj) * Fixed crash / hang when closing connected jack apps (rj) 2005.01.02 ======================================== * 0.7.1pre3 tagged ! (rj) ======================================== 31.12 * Mastertrack list editor updates (add sigevent + misc) (ml) 30.12 * Insertion of tempo events in list mastereditor added (ml) 29.12 * Added support for changing time signature in list master editor (ml) 27.12 * Added support for changing tempo + position of tempoevents in list mastereditor (ml) * Backported auto rec-enable from HEAD branch (rj) * Added visual feedback of marker addition in ruler as well as possibility to remove markers with shift+rmb (rj) * Made it easier to resize the last track (bug: 1041798) (rj) * Fixed bug: 966005, new projects are now called "untitled" (rj) * fixed bug: 1085791, no more crashes with delete + drag (rj) 26.12 * Listedit bugfixes. Consideration of part offset used for events (ml) 20.12 * Fix for bug #1085796 (when renaming channel by doubleclicking it in tracklist and a part is selected, pressing return opens editor for part) (ml) 17.12 * -a (No Audio) flag added, improved Dummy audio backend (rj) * alsa timer bugfix (rj) * added deicsonze patch from Alex Marandon to fix QT<->STL problems on affected platforms (rj) 14.12 * Disable of fluidsynth works (rj) * Added test for libsamplerate (rj) * Re-enabled --enable-suid-install (rj) * Added to simpledrums.h (rj) * Added -no-rtti to simpledrums (ml) 13.12 ======================================== * 0.7.1pre2 tagged ! (rj) ======================================== * SimpleDrums 0.2 softsynth added (ml) 12.12 * Removed -no-rtti from configuration (rj) * Extern sync (codename: it_works_for_me__sometimes) is back! (rj) * Changes to midi-input, events to softsynths vanished (rj) * bounce to file now limits output to +/- 0.99 (rj) * crash bug on missing event in sig.cpp fixed (rj) * Changed default timer resolution to 1024 (rj) * Applied fix from Levi D. Burton to allow midi thread to run realtime although Jack is not (rj) * New version (0.22) of DeicsOnze from Alin Weiller (rj) 9.12 * Now autogen.sh requires libtool = 1.4, 1.5 does not generate softsynths correctly (rj) 4.12 * Added another IDF from Christoph Eckert for Alesis QSR,QS7 and QS8 (rj) 01.12 * fixed import of type 0 midi files (ws) * Added updated DeicsOnze (0.21) from Alin Weiller (rj) * added a branch of new icons, changed default colors of wav/audio output track in arranger (js) * changed changelog (js) 30.11 * Added IDF files from Christof Eckert for Access Virus, Hammond XB and Waldorf Microwave (rj) * backported fix from 0.8 so listing patches for synths works again (rj) 29.11 * fix midi import: tick values of tempo/signature and marker events are now properly converted to internal resolution (backport from 0.8) (ws) * some make system changes to better handle precompiled headers (ws) ======================================== * 0.7.1pre1 tagged ! (rj) ======================================== * LADCCA was incorrectly disabled in config, now fixed (rj) * Changed URL of homepage to www.muse-sequencer.org (rj) 28.11 * Partial support for "input only" midi devices. (rj) 27.11 * Added Alsa Timer as a new timing device, RTC is still available, though not easily selectable yet. (rj) * Made some changes to how threads are created, for systems where thread creation has been erratic, linux2.6 in various configurations. Not yet verified if it makes any difference. (rj) 08.11 * Backported audio metronome (rj) * Backported open/save dialog improvements (rj) * Added -r parameter to allow MusE to start without RTC not the right thing to do, but it seems necessary on PPC, it's a start. (rj) * Added patch from CK to allow getopt to work on PPC (rj) 02.11 * Added icon stuff to tlist.cpp (js) 01.11 * Added Alin Weiller's DeicsOnze synthesizer (ws) * add dummy call to process() in Audio::start() to warm up caches to avoid "JACK - zombified" during startup (ws) 23.08 * fix crash in list editor - create new controller (ws) * increase required JACK version to 0.98.0 (ws) 20.07 * updated muse/muse.pro and share/locale files (ws) 18.07 ======================================== * 0.7.0 tagged ! (rj) ======================================== * output fifo warning only if cmd line "-D" switch is set (ws) 17.07 * fixed separate handling of recorded events vs played events (ml) 15.07. * do not start the disk helper thread in realtime mode (ws) * check for JACK thread really running in SCHED_FIFO mode, if not (as on my system with kernel 2.6.7 and nptl) try to set it. (ws) * removed some exit() and abort() calls to react somewhat more gracefully to to internal errors (ws) 14.07. * fixed -V (no vst instruments) option (ws) * do not save midi controller information in ~/.MusE file (ws) * another try to fix midi loop handling: Loop len now should be sample accurat. The loop is shifted left at most one audio cycle to meet the requirement of loop end matching audio cycle end. When JACK transport is in "JackTransportStarting" mode, MusE internally continues rolling, so there are no repeated cycles anymore (ws) * Added message boxes when alsa and jack fails to initialize (rj) * Disabled solobuttons in mixer (rj) 13.07. * added new icons for the mixer solo/mute (js) * added refresh for the solo/mute icons (rj) * added icons for drum-/listeditor in the arranger on rightclick (js) 12.07. * fixed typo in loop handling (ws) * added patch from Daniel Schmidt to be able to configure MusE without X being available (rj) * Removed geometry data etc from templates (rj) 11.07. * disabled midi mtc sync as its not implemented; disabled midi sync slave modes as they are currently not working (ws) * enabled sending midi clock (ws) 28.06. * split removeTrack()/insertTrack() into three phases: pre realtime actions - realtime actions - post realtime actions; this allows to move memory allocations out of realtime task (ws) * changed undo/redo of tracks: synti instances are now really deleted on delete track (ws) * jack connection changes with qjackctrl are now recognized by MusE (ws) 27.06. * applied patch from John Check to add a panic button to pianoroll editor (ws) 28.06. ======================================== * 0.7.0pre4 tagged - one more (rj) ======================================== 26.06. * Some packaging additions, icon, spec files. (only mdk at the moment) (rj) 25.06. * fixed midi timing bug (ws) 19.06. * don't catch SIGCHLD, this interferes with vstInit() (ws) * "givertcap" was not found when not in current directory (ws) * impl. "all notes off" for organ synti (ws) 18.06. * disabled buttons for not implemented functions (ws) * added muse/wave/solo button in the trackinfo ;-) (js) 15.06. * enabled some midi sync code (ws) 14.09. * dialogs for change of drummap when converting miditrack to drumtrack or changing port. redirection of keyevents from tlist to canvas (ml) 13.09. * save/restore parameter for VST synthesizer (ws) * automatic trackchange in tracklist when selecting parts in arranger (ml) * added modify velocity to drumeditor + bugfix for modify velocity (ml) * save/restore parameter for VST synthesizer (ws) 12.09. * fixed backup command when filename contains spaces (ws) * fixed midi step recording (ws) * fixed bug in arranger: pressing enter after renaming part started editor (ws) 09.06. * added support for VST/windows software synthesizer (ws) * delayed loading for software synthesizer: syntis are loaded, when they are instantiated the first time (ws) 08.06. * fixed --enable-rtcap configuration option (ws) 07.06. * increased "dimension" in memory.h to make MusE work on 64 bit architectures as requested from Simone Piunno (ws) * added aux send for syntis (ws) * added info box which explains why when MusE gets kicked by Jack (rj) 06.06 * added instrument definition for roland MC-505 from Wim VW (ws) 05.06 * Added backup creating save patch from Levi D.Burton (rj) 01.06 * transpose + grid patch added (alin weiller) * fixed moving events in drum editor (ws) * added new config option: disable splash screen (ws) 31.05 * fixed crash in pianoroll when using shortcuts for selecting quant values when quant was set to 1 (no quant) (ws) * fixed a crash when moving an event to tick positions < 0 (ws) * fixed: selecting a note in pianoroll editor and changing a value with note-info toolbar crashed MusE (ws) * bugfix arranger: fix for selecting part -> ignore global accelerators (ml) * bugfix for arranger selection of part above/below when using keyboard (ml) * added pianoroll velocity variation patch (alin weiller) 30.05 * hopefully a fix for drum in & outmap-issues in midi.cpp (ml) 25.05. * shortcuts for "arrowing around" in arranger added (ml) * 0.7.0pre3 tagged - the last!!! I hope (rj) 24.05. * fixed a crash on new -> load template (ws) * FluidSynth: added support for drumpatches (equiv to midichan 10 patches) (ml) 23.05. * exit if rtc open() fails (ws) * changed default start behaviour to open default.med template (rj) 18.05. * added many new/redone icons (js) * changed aboutbox.ui for qt 3.2 compatibility * changed app.cpp (added the icons) 17.07. * added stereo/mono icons to mixer (ws) * added a first version of an icon for muse and adapted aboutbox to the same. (graphics by Joachim Schiele) (rj) * Improved handling of browser for help system (rj) 16.07. * Added FluidSynth fix for ignoring preset selection of soundfonts that don't exist (ml) * fix midi import of pitch bend events (ws) * fix pitch bend handling (ws) * enlarge PitchLabel width so that 14 bit controller values can be displayed without clipping (ws) 15.07. * removed some debug messages (ws) 12.07. * show one more measure in pianoroll and drum editor (ws) * renamed controller name "Hold1" to "Sustain" in *idf files (ws) 11.07. * New try at fixing help browser on all systems (rj) 10.07. * updated muse.pro and translation sources (share/locacle/*.ts) (ws) * list editor: implemented input mode for program change messages (ws) 09.07. * fixed "edit - delete track" hangs MusE - bug (ws) 07.07. * fixed routing for stereo LADSPA plugins used in mono strips (ws) * midi import: first resolve note on/of then transform ticks to internal resolution (ws) 06.06. * set global automation default to "true", set midi track automation to "read" (ws) * enable auxSend chorusSend and reverbSend in midi mixer strip if corresponding controllers are added (ws) * init automationType for midi tracks (ws) * fixed gm.idf instrument definition file (ws) * implemented "Add New Controller" in list editor / edit controller (ws) * save current midi controller values in *.med file (ws) 05.05. * updated roland-XP30.idf instrument definition (Sverre H. Huseby) 04.05. * 0.7.0pre2 tagged (rj) 03.05. * fixed a cut/glue bug probably introduced at 24.04 (ws) * fixed compilation of musewidgetsplugin.so (ws) * changed splash screen handling, used QTimer instead of background thread (ws) 02.05. * Added first version of splash screen (rj) 01.05. * Updated LADCCA support to (almost) usable condition (rj) 30.04. * Added zoom scaling in drum editor, same as piano roll (rj) 29.04. * Disabled Random Rhythm Generator (rj) * Took a stab at fixing up shortcuts (rj) * Fixed crash bug when clicking Channel Info and there was no info (rj) 28.04. * Added single key shortcuts for edit tools (rj) * added shortcut for Marker editor (rj) * and fixed some shortcut inconsistencies (rj) 27.04. * update marker list on tempo change (ws) * allow adding markers from the ruler with shift-click (rj) 26.04. * added missing header file(s) (ws) 25.04. * fixed aux processing: stereo -> mono aux (ws) * metronom now sends proper note off events (ws) 24.04. * deactivated clip list editor. (ws) * after loading of template, treat current project as "untitled" (ws) * removed data structure "Clip". All information are now in WaveEvent; this simplifies the structure a lot and makes reference counting more reliable. Unfortunately this also means a new incompatible *.med file version. (ws) * changed reference counting of Event class; simplified and more reliable (ws) 21.04. * fixed some synchronisation issues between mixer and trackinfo window (ws) * fix update of mixer after removal of aux strip (ws) 20.04. * Added shortcuts to bug reporting tool, homepage, and updated AboutBox (rj) 19.04. * fixed QT version check in m4/qt.m4 (ws) 18.04. * add samplerate initialization to fluidsynth (ws) * compilation fix: added missing include in fluid.cpp (ws) 17.04. * File->New crashed when current project had selected audio track (ws) 15.04. * 0.7.0pre1 * tagged for prerelease (rj) * arranger: fast repeated pastes now works more reliable (no more stacked parts) (ws) * Thread(): crashed, when poll() returned more than one ready file descriptor and the corresponding callback routine manipulates the list of file descriptors. This happened timing dependend and only in real time mode. (ws) * fixed Fifo() get() (ws) * small extension in soft synth interface (Mess()): added return values for processEvent() 14.4. * fixed pan range for midi mixer strips * renaming soft synth instances + save/restore should now work * fixed fluid "gui" * changed CTRL_VAL_UNKNONW as it conflicts with valid values for CTRL_PROGRAM (ws) 13.4. * don't crash on missing LADSPA plugin (ws) * set metronome precount default to "false". Precount is not implemented. (ws) * fixed crash when toggling stereo or pre buttons in mixer (ws) * synchronize channel number in mixer/arranger-tracklist (ws) * changed all float printf("%f") to equivalent qt-string routines; don't localize decimal point so that the strings can be properly parsed; this should fix some save/restore problems in localized MusE versions (ws) 12.4 - arranger/portinfo: fix update of instrument names (ws) - fluid synth: enable drumsets (ws) - fixed crash on inserting meta/ctrl/aftertouch in list editor (ws) - fixed crash in arranger when moving mouse+Alt after removing a track (ws) 11.4 - fixed initialization of Pan after load of new song (ws) - fixed graphical master track editor (ws) - fixed Qt-Version check (ws) - small qt3.2 compatibility changes (string->toInt() conversion) (ws) 10.4 - made plugin selector remember the previous selection type (rj) 4.4 - drag & drop import of wave files (rj) - drag & drop import of mid files (rj) 3.4 - reactivated vam synthesizer - fixed initialization bug in s1 synthesizer demo code (ws) - added another vertical line in drum editor 2.4 - integrated new icons (ws) - increased required QT-Version to 3.2 in configure.ac (ws) 1.4 - added vertical line in track list as suggested by Joachim Schiele - fixed synchronisation issue between mixer and tracklist (changing midi channel can add/remove mixer strip) (ws) - Changed pan range to -1 +1 (rj) - added new icons from Joachim Schiele (not integrated) (ws) - Support for showing only selected plugins in plugin dialog (rj) 31.3 - Added various compile fixes needed by debian (rj) 29.3 - Updated mc303 instrument file from Conrad Berh�ster (rj) 25.3 - bugfix for pos increase/decrease shortcut (ml) 24.3 - bugfix for mtscale redraw area when window is scrolled left - bugfix for arranger shortcuts (ml) 23.3 - added position seek to drumeditor + arranger. - increase/decrease pitch of selected notes in drumeditor with ctrl+up/down - added quantize shortcut keys (1-8) in drumeditor (ml) 21.3 - added shortcut for pitch increase and decrease of sel notes, in pianoroll (Ctrl+Up/Down) - moved shortcut handling for pianoroll + drumeditor to EventCanvas - leftmost note selected when opening proll/drumeditor (ml) 16.3 - added shortcut for iterative quantize in p-roll. added shortcuts for selection of notes in p-roll and drumeditor: left/right arrows moves selection, holding down shift adds to current selection (ml) - bugfix, moved blocking of signals on startup to exclude loading of project since a bunch of widgets didn't get updated (ml) 12.3 - bugfix for menu initialization of "add track"-menu shortcuts (ml) - added some regular 7-bit controllers to Fluidsynth (ml) 6.3 - Fluidsynti chorus operational again, controlled by NRPNs and automatic gui-update here too. (ml) - Fluidsynti reverb restored. Reverb is now controlled by NRPNs. Automatic updates of gui when controller-changes occur. Unloading of soundfonts restored. (ml) 4.3 - Fluidsynti playback restored. Gain restored. (ml) 3.3 - Fluidsynti major rewrite, not fully functioning though (ml) - fixed crash on reload song with open mixer - fixed crash on saving *.med: don't save aux values for channels which have no aux send (like aux strips) - remove empty record wav files on MusE exit - fixed crash on undo controller editing (ws) 28.2 - more icons from Joachim Schiele (ws) - fixed crash with mouse wheel events in arranger track list - fixed some routing related crashes 25.2 - show mixer strip in trackinfo window for audio tracks (ws) 24.2 - compatibility patch to read midi tracks from old 1.0 *.med files (ws) - implemented quick input routing from jack (ws) - added some new icons form Joachim Schiele (ws) 23.2 - implemented quick output routing to jack; some routing fixes (ws) 22.2 - added instrument map for yamaha PSR 530 keyboard from Lalit Chhabra (ml) - misc compilation fixes from Joost Yervante (ws) 21.2 - added drum map for Yamaha DX200 from Joost Yervante Damad (ws) - "quick routing" buttons in mixer (ws) 17.2 - install musewidgetsplugin.so in PREFIX/lib/muse/qtplugins/designer and add this path to qt library path in application (ws) 16.2 - trackinfo geometry management changes (ws) 14.2 - added volume controller to organ, so that the organ synthesizer volume can be controlled in the mixer midi strip (ws) 13.2 - optimized "organ" software synthesizer: precomputed frequency tables, integer envelope generator based on bresenham algorithm; added "velocity" switch; (ws) - changed VAM synthesizer for new interface (ws) 12.2 - controller movements in software synthesizer gui's are now send as midi events to MusE and can be recorded/replayed/edited (currently only working for "organ" synti) (ws) - changed software synth interface (again) (ws) 11.2 - expanded trackInfo by default (rj) - added some graphics to trackInfo :) (rj) - changed "White" to "Default" (still white though) (rj) - fixed trackInfo updating for tracks without their own trackInfo (rj) - added lousy fix to update trackInfo when a track is deleted. it's lousy because it's updating the trackInfo even though the removed track wasn't highlighted, can place you in a tight spot at times. I added it anyway because it removes a crash problem. a better fix would be preferable. (rj) - fixed mouse wheel functionality on knobs and sliders in mixer. (rj) 8.2 - propagate track name changes to mixer (ws) - enforce unique track name when user renames tracks (ws) - implement one to many audio routing (ws) - fixed bug in route dialog refresh after inserting new strip (ws) - fixed aux send volume (ws) 4.2 - added missing activate() to LADSPA plugin initialisation. This fixes crashes with some plugins (plugins who use activate() to allocate memory) (ws) -fixed user definable LADSPA guis using QT-designer; added another example *.ui file for "Toms'Audio Plugins" TAP-Reverberator (ws) 1.2 - logarithmic values were initially wrong for spin boxes in LADSPA guis (ws) - On-the-fly-change of shortcuts in Listeditor, Drumeditor. Added selection shortcuts to Drumeditor (ML) - Added on-the-fly-change for shortcuts in pianoroll (connected to configChanged-signal) (ML) 30.1 save geometry of LADSPA plugin guis; fixed missing mixer refresh after adding an auxSend strip (ws) 29.1 mixer strip automation types OFF, READ, WRITE and TOUCH implemented; automated controller so far: volume, pan (ws) 27.1 more code for automation; better reference counting for audio Clip; Clips with zero reference are not saved in *.med file anymore (ws) 26.1 - removed obsolete driver/midirawin* (ws) 25.1 - removed obsolete raw midi devices & serial midi; all devices are now handled by ALSA (ws) 24.1 - changed "Configure" to "Settings"; changed "Display" to "View" for better standard compliance (ws) 23.1 - enabled controller editing in midi list editor (ws) 19.1 - added new dir/lib libsynti to collect common code for all software synthesizer (ws) 18.1 - ported VAM software synthesizer to new MESS interface (not complete) (ws) - events send from synth gui are not echoed back anymore 17.1 - drumedit: changed "keyFilter" to "drumInstrument"; use it to handle drum instrument specific NRPN controllers (as defined in XG standard (ws) - move drum instrument select status from drumMap to drum editor. This allows for independent selections in different drum editors. (ws) - extend midi controller definitions in *.idf files for drum instrument specific NRPN controllers (controller number contains pitch value) (ws) 16.1 - Added small fix to editctrlbase.ui to make it compile (RJ) - Updated various revision info in README (RJ) 15.1 - Corrected appearance of buttons in Metronome window (FN) 9.1 - "record" button in midi ChannelInfo; pressing this button inserts changes in hbank/lbank/program/pan/volume as controller events into song - pianoroll editor: new "multi select mode": If more than one event is selected, the event info spinboxes are set to zero. Entered Values are interpreted as offsets and added to all selected events. - some fixes and performance enhancements to "MidiTransformator" (ws) - fixed saving of drum tracks (ws) 8.1 - changed arranger shortcuts + shortcut-configurator to use configChanged; all arranger menu shortcuts redefinable on-the-fly. most menu options configurable (ML) - now possible to toggle mixer window (ML) - changed Ctrl+A in arranger to be used for select all instead of "Save As" for the sake of uniformity (ML) 5.1 - new config (Config->GlobalSettings) option: - start with empty song - start with last song - start with configured init song 2.1 - smf type 0 export (ws) 31.12 - midi export; fixes to midi controller handling (ws) 30.12 - added dynamic shortcuts for all menu options in the arranger (will hopefully work fine when config is loaded before menus are built) (ML) - added check and prevention of conflicting shortcut sequences inside respective categories, global shortcuts and misc "reserved" shortcuts (ML) - capture dialog grabs/releases keyboard (ML) 26.12 - faster update of mixer (ws) 22.12 - added dummy audio driver; this permits running MusE without suid root and without RT priority when started with "-d" option; this is for debugging & to get normal core dumps (ws) 20.12 - Reverb + chorus grayout-problem in fluidsynthgui removed. Some QT-designer files changed to 3.2 (ML) - More shortcut updates: Added a dialog for capturing shortcuts (ML) 18.12 (ws) - "declicked" s1 demo synthesizer - make organ controller values persistent - fixed integer overflow in organ synti controller values 17.12 (ws) - new: mouse wheel events for track list values - track colors in tracklist configurable in "appearance settings" - more configuration options in "appearance settings" part colors are now configurable - ~/.MusE now contains only configuration data; this settings are not replicated in *.med files any more 10.12 - more shortcut stuff in the dummy shortcut config window (ML) - new "load song" option: load all/load song data only (omit config data) (ws) - new "File" function: load template; this function initializes current song with another song (template) - new option while importing a midi file: replace/add to project 9.12 - More shurtcut stuff + dummy config dialog (ML) 2.12 - Bugfixes in pianoroll for insert at current location and seek before beginning of song (ML) - fixed crash on "New" or loading new project (ws) - "bounce to track" now works "faster than realtime" with JACK freewheel mode (JACK 0.91.0 cvs required) - mixer automation for gain & pan; some basic functions - preparation for adding imported midifile to current project 1.12 - More shortcut preparations (ML) 27.11 - Added mouse wheel handling for knobs and sliders shift modifier available for extreme slow motion. (RJ) 25.11 - drummapping for recorded events (not heard on record playback echo yet) (ML) - misc updates& fixes for track handling (ws) - removed activity column in tracklist; will be replaced by mixer meter display (ws) - midi record flag can be set again, enabling midi recording 24.11 - Added first steps for uniform handling of shortcuts (ML) 23.11 - some mixer updates(ws) 22.11 - fixed software synth handling; looping is only possible on segment boundaries (ws) 20.11 - Bugfix for drummaps and keyfilter in drumeditor (ML) - apply drum map when importing midi (ws) - retain track ordering across save/load (ws) - wave files in the project directory are stored with relative path, all others now with absolute path (ws) 16.11 - Map outputted drumnotes according to drumMap index (Mathias Lundgren) 14.11. (ws) - JACK transport interface implemented - finally removed ALSA driver; JACK now required; - default to RT-mode on start (-R option is removed); -d (debug) starts without RT 12.11. (ws): - removed midi mixer; first code to integrate midi mixer into audio mixer; mixer moved to pulldown menu "Display" - mixer strips are handled like tracks in arranger (prepare for automation) - track type cannot changed anymore in arranger track list; also removed double click creation of tracks which makes no sense anymore - integrated "atomic" patches from Daniel Kobras 09.11. - audio routing fixes; mono/stereo switching for input/output strips should now work; audio recording to tracks (ws) 08.11. - aux send audio mixer strips (ws) - Arrowing up and down update in arranger (Mathias Lundgren) 07.11. - Fluidsynth compile problem w. Debian/qt/stl/gcc fixed (Mathias Lundgren) - "Double undo"-bug fixed in Pianoroll (Mathias Lundgren) 04.11. - many changes to audio mixer and audio routing; implement framework for new audio features (ws) 31.10. - museProject set correctly when loading song on startup (RJ) - save dialog added when adding first wave track (RJ) - load/save of wave files handled with relative paths (to project) (RJ) - Updated swedish translation (Robert Jonsson) - Merged softsynth and midiconfig dialogs as in old tree(Robert Jonsson) - Some museProject issues(Robert Jonsson) - updated new midi controller implementation (ws) 29.10. (ws) - update soft synthesizer organ/fluid/fluidsynth; extent MESS interface - update from 0.6.2 (Mathias Lundgren): - Various drumeditor-related updates including: Horizontal splitter offset stored. Fixed length command added (Alt+L). Bugfix for selection of events inside/outside locators. Initialization of drummap doesn't overwrite entries loaded from project file. - Alt+P sets locators to selected notes in PianoRoll and DrumEditor - CTRL+Leftclick on an item in the Arranger, PianoRoll or DrumEditor selects all parts/notes on the same track/same pitch - Pressing Enter in the Arranger now opens the appropriate editor for the part (Mathias Lundgren) - The midithread now maps keys on a drumtrack according to the drummap. - Cursor up/down (Mathias Lundgren) - currently selected drum (Mathias Lundgren) - fixed compilation error in givertcap.c (andrew) - removed iiwu soft synth temporarily from compilation until ported to new "mess" interface - added missing file ltmain.sh - create new cvs at sourceforge.net ------------- - removed obsolete score editor - changed midi controller handling - new process structure - redesign of "mess" software synthesizer; removed alsa - new midifile import/export - fork from cvs; new file structure 0.6.2(omuse): - added first version of (incomplete) french translation from - removed stk based soft synthesizer + share/rawwaves - removed sound font - fixed bug in midi recording when recording starts with a note off; also the recorded part len was miscomputed when the recording ends with a pressed key (missing note off event) - added new allocator for SEventList and MPEventList (memory.cpp, memory.h) to make sure the midi RT-thread does not call malloc/new/free/delete - added misc build patches from Daniel Kobras - make selection of alsa audio device an command line argument (-A xxx default: hw:0) - fixed "edit->select->allInLoop" - fixed track height after renaming track 0.6.1: - fixed "Cakewalk Style" display in arranger - added russian translation from Alexandre Prokoudinek - arranger: tracks are now independent vertical resizable - arranger: implement part rename from popup menu - arranger: show part name in parts in addition to events - audio mixer: interpret min slider position as "off" - audio mixer: added value entry for pan (Robert Jonsson) - audio: some routing fixes - audio mixer: enable data entry for slider label - ladspa plugin gui: replaced value label with data entry to allow numerical entry of parameter values - pianoroll: added undo/redo to edit menu + accel. keys - ctrl editor: implemented changing (painting) of pitch events - added macros for big endian machines in midi.h - added spain translation (Albert Gonzales) 0.6.0: - added swedish translations (Robert Jonsson) - fixed editing of pitch events in list editor - fixed crash in score editor - check tempo entry values; don't allow invalid values which could crash MusE - fixed not functioning "Abort" button in MidiTransform dialog - fixed Ctrl-editing in drum editor - fixed "Group" audio routing - fixed editing of pitch values in parts not beginning at tick zero - fixed "unexpected EOF" after dragging of events in midieditor - fixed cut&paste in midieditor - implemented deleting multiple selected parts in arranger with Del key - fixed audio pan pots in mono->stereo conversion - changed iiwu to fluidsynth (thanks to Helio Chissini de Castro) - new popupmenu: click with right button in empty tracklist - LADSPA plugin guis are generated at runtime from qt-designer *.ui (xml-)files; testfile is "freeverb.ui" for freeverb plugin; - added "Slider"+"DoubleLabel" to musewidgetsplugin to make widgets available in QT-Designer - renamed poseditplugin.so to musewidgetsplugin.so - fixed midi ctrl editor - sparate sync device into txSyncDevice and rxSyncDevice. RxSyncDevice can be configured to "all". - use macros for atomically inc/dec count in lockfree Fifo implementation 0.6.0pre8: - prepared for internationalization: - created muse.pro - removed all implicit type conversions char* -> QString - added several missing translations tr() - Part text is now colored depending on background (FN) - fixed "bounce to file" fifo handling - disable transport buttons in slave mode - calculate correct size for new part after midi recording - fixed crash when reloading song while audio mixer is open - implemented "bypass" function for LADSPA plugin gui's - changed obsolete qt header file names - implemented external midi instrument definition files (*.idf) (examples are in */share/muse/instruments) - implemented moving plugins up/down in effect rack - fixed: renaming wave track switched track to mono - implemented LADSPA "LOGARYTHMIC" and "INT" hints - disable record button for tracks with no input routed to - implemented LADSPA "TOGGLED" port as QCheckBox in plugin gui - changed algorithm for zeroing denormalized floats in freeverb plugin; now it works again for gcc3.x and optimization flags turned on 0.6.0pre7: - prevent creation of empty wave files when switching the record button in audio mixer on/off; wave files are only preserved when you actually record something into it - made plugin guis persistent - fixed scissor operation on wave parts - added missing code for "bounce to file" - fixed "bounce to track" - removed/changed obsolete qt code - update for current iiwu cvs - fixed initialisation bug in wave editor - don't link iiwu libs static - (bh) added ladcca support - fixed midifile export - arranger, pianoroll editor, drum editor: tool popup menu with right mouse button click - update iiwu to current cvs version - implement trackinfo patch names for iiwu - fixed "appearance settings" - added keyboard shortcut "Del" to delete events in pianoroll and drum editor - "Asterisk" key in keypad now toggles record mode 0.6.0pre6: - fixed len of new created event in pianoroll editor - extend font selection in "apearance settings" - Added shortcuts for "Select All", "Unselect All" and "Invert Selection" in PianoRoll editor (FN) - Fixed Event coloring and shortcut ("e") key (FN) 0.6.0pre5: - fixed midi seek & tempo map - implemented global tempo change 0.6.0pre4: - fixed tempo handling - pianoroll editor/drum editor: fixed changing of note position - transport: some geometry/font changes; time signature can now be changed by mouse wheel - fixed glue/scissor tool - catch sigchld signal again so we know when a softsynth gui exits 0.6.0pre3 - fixed drawing of drum parts in drum editor - on reading *.med files reject events which don't fit into part (more robust handling of defective med files) - remove also synth gui when removing synth - implemented some of Frank Neumann's usability suggestions: - a "Copy Part" sets the current location marker after the marked part - "Del" removes part if a part is selected instead of whole track - new Keyboard Accelerator "C" toggles metronome click - removed channel info for selected notes in pianoroll editor and drum editor - navigate between parts with left/right buttons in arranger window - implemented changing note position for selected note in "note info" toolbar - fixed: changing "loop" flag in transport window does not change "loop" flag in other windows - call pcm_wait() in alsa driver with sane values - fixed: after load song seq did not run with rtc - filenames for audio recording to tracks are now generated automatically; every recording goes into separate file - (bh) updated build system to use automake 1.7 - fixe Midi->DefineController "Cancel" - new function: Midi->DefineController load+replace and load+merge - fixed MFile write: close() was missing; this fixes a.o. saving of midi controller sets - make organ synth aware of project directory for saving presets - fixed load/restore presets for LADSPA plugins - changed organ default values for envelope generator - more fixes for alsa driver (less xrun errors) - lokal allokator for soft syth midi events implemented - enable sample rates != 44100 for iiwu (JACK has 48000 default) - cleanup soft synth instantiation: call alsaScanMidiPorts only one time - small audio buffer handling optimizations - some thread cleanups - fixed audio mixer geometry handling - another fix for RT thread handling in iiwu - fixed recording of pitch events (not tested) - load iiwu sound fonts in a background helper thread to avoid being thrown out by JACK - fixed RT thread handling; now muse+iiwu+jack works - honour LADSPA default hints for controller ports - removed some restrictions for LADSPA plugins - fixed tempo entry in transport window - added high priority watchdog process to avoid system freezes - updated "iiwu" synth to use peter hanappes libiiwusynth iiwu now remembers last used sound font - fixed cut&paste for midi parts - fixed cut function for midi parts 0.6.0pre2: - audio mixer: reset meter on mute - changed input routing to allow monitoring while recording - removed superfluous second init() call for soft syntis - fixes for mono/stereo conversion - ensure all wave files are properly closed on exit - fixed segfault on second cliplist open - fixed wave part split function - fixed ALSA/JACK configuration bug - event time positions are again stored as absolute time positions to enhance compatibility with older "*.med" files - changed panic button: instead of sending lots of note off events only "all sound off" controller events are send for all ports/channels - fixed error on importing midi files when there are more than one track assigned to a midi channel - found another memory corruption bug in sysex handling - fixed precount in metronome - space key again stops play/record - fixed stop/play in transport window - prohibit change of mono/stereo for input strip - convert mono/stereo on the fly for wave parts - fixed crash when pressing play in empty song - audio loop fixed - _midiThruFlag not always initialized 0.6.0pre1: - attached midi mixer again - fixed metronome: loop mode, measure/beat configurable - moved part colorisation into part popup menu - added global midi pitch shifter in addition to track pitch shift; this allows for simple pitch transforming the whole song. Drum tracks are not pitch shifted. - fixed fatal error in soft synth handling resulting in sporadic core dumps - removed sf directory (sound file stuff) and replaced it with the real thing: libsndfile 1.0.0 - removed bogus kde stuff: kde.h onlyqt.h desk.h - JACK Audio support - AUDIO & ALSA now required - fixed memory corruption with sysex events - simplified organ soft synth parameter handling - removed illegal controller message optimizations - implementation of "panic" button - first instantiated synti didn't show up in port list - size of resized drum and pianoroll editor windows are now remembered - fixed crash when configuring null audio device - removing soft synth instance did not stop midi thread; alsa client was not removed - (bh) lots of build system changes and general cleanups - (bh) removed the use of the UICBASES make variable; .ui files can now be added straight into _SOURCES make variables with the new SUFFIXES support in automake 1.6 - (bh) upped minimum automake version to 1.6 - (bh) removed the use of the MOCBASES make variable; header files that need to be run through moc are now detected automatically - (bh) new iiwusynth softsynth - (bh) removed support for oss and alsa 0.5 - clone parts implemented (also called "alias parts" or "ghost parts") (dragging part with Alt-Key pressed in opposit to Shift Key which produces a normal copy); needed many internal changes; hope not to much is broken - mastertrack: new spin widget for changing midi signature - fixed midi thread initialization after loading new file - stopped sequencer before loading new file; this should fix occational core dumps on New/Load/ImportMidi - some cleanups with file load/save operations - Config->MidiPorts->otherRaw (device name pulldown): enabled OpenFile Button for DevicePath field: At least current Qt can now handle devices. - implemented: - structure pulldown menu: - global split - global cut (mastertrack cut not implem.) - global insert (without m.t.) - implemented part mute - added pitch transposition to pianoroll widget keyboard (Tim Westbrock) - Save(As) behavior patch from Tim Westbrock 0.5.3: - updated stk library to version 4.0; adapted stk synthesizer - added SECURITY advice from J�n Nettingsmeier - several compilation & portability fixes from Takashi Iwai - fixed keyboard entry in pianoroll editor - midi now runs synchronous with audio - midi record time stamps now again synchronous to play position - fixed trackinfo geometry (hopefully) - pianoroll: fixed endless loop if record was pressed without any mididevices configured (reported by Ola Andersson) - default to english help if help for $LANG not available (Ola Andersson) - detect misconfigured ALSA system (Ola Andersson) - updated demo app "rasen.med" to current xml format 0.5.2: - fixed: rtc clock resolution settings in Config->GlobalSettings - fixed: crash on second start of Edit->Marker - more consequent implementation of -a (no audio) command line parameter: no softsynth and LADSPA plugin loading; disable audio menu - fixed sending spurious midi controller settings on startup when track info is active - first code for "random rhythm generator" port from JAZZ++ - fixed start offset of midi recording - pianoroll editor: fixed selection update - appearance setting "font size" now persistent - does not crash anymore if no ALSA system found. (ALSA is still needed to compile MusE) - fixed: multiple recordings: clear events form first recording in record buffer - fixed: crash when removing last track with open trackinfo - (bh) added beginnings of alsa midi patchbay - changed suid handling: now MusE runs with normal user uid and switches only to root uid for some special operations - fixed mixdown file output - fixed lock on startup when wave file was missing - arranger: open tracktype pulldown with left mouse click (was opened on right click) - arranger: don't scale pixmap symbols - added share/rawwaves to cvs repository (needed by stk synthi) - changed software synthesizer interface "mess": moved more common synth functionality to "mess.c"; changed synti's to new interface - removed obsolete "short name" in controller type dialog - CtrlCanvas: always draw location marker on top of grid - fixed: TrackInfo: velocity - fixed: alsa midi: "pitch change" interpreted as "channel aftertouch" - fixed some midi controller bugs - implemented new parameter save/restore interface for soft synthesizer (applied to "organ") - (ws) fixed lost controller events on midi import - (ws) fixed crash when removing soft synth in use - (ws) appearanceSettings: changing font size now works better - (Bob) files now include "config.h" instead of relying on -DALSA, -DALSACVS, -DINSTDIR and -DAUDIO - (Bob) Added 'delete preset' button to vam's gui and made it remember what preset file it loaded - Mess: added new class MessMono() which implements some monophone synthesizer features. This is used in the simple demo synthi s1 - if you try to exit MusE and abort this process, MusE was left in an unusable state - loop end was not calculated correct; event at loop end was played - muse now again stops at end of song in play mode 0.5.1: - fixed crash: SaveConfig after Config->SoftSynth->AddSoftSynth - changed default audioSegmentSize from 256 to 512 - eliminated message: "input type 66 not handled" - SoftSynth gui was startet with uid root - save project: warn if file open fails - removed trace message "unrecognized event 42" (Sensing Midi Event from external keyboard). Anyway MusE does not handle midi sensing events. - changed geometry for trackInfo panel - more code for 14 bit controller events - install "rawwaves" for stk synti into right place preventing crash on start - fixed another crash when load soft synth configuration - fixed Midi Position Label (was -1 beat/measure off) - fixed problem with lost note off events - generate "note on" events with velocity zero instead of "note off" events 0.5.0: - pianoroll editor: caption is changed when current part changes - new software synthesizer adapted from: STK: A ToolKit of Audio Synthesis Classes and Instruments in C++ Version 3.2 By Perry R. Cook, 1995-2000 and Gary P. Scavone, 1997-2000. http://www-ccrma.stanford.edu/software/stk/ - added presets to "Organ" software synthesizer - changed midi routing for software synthesizer: - controller changes from gui can be recorded - new midi thread implementation - speaker button in pianoroll editor implemented: if on events are played if clicked - new Menu: Midi->InputPlugins - demo plugin "Transpose" - moved Config->MidiRemote to Midi->InputPlugins - moved Config->MidiInputFilter to Midi->InputPlugins - moved Config->MidiInputTransform to Midi->InputPlugins - as usual some bug fixes of old and new bugs - master editor: fixed: locator bars sometimes invisible - master editor: new tempo spin box to change tempo at current position 0.4.16: - new software synthesizer adapted: "Organ - Additive Organ Synthesizer Voice" from David A. Bartold - new simple demo Synthesizer S1 - remove the hardcoded qt path "/usr/qt3" (0.4.15) - fixed many bugs - new: implemented line draw tool in controller editor 0.4.15: - qt3.0 now required - many gui/widget changes - fixed segfault when pasting wave parts - changed (again) default magnification in wave-view - implemented prefetch thread for playing audio files - fixed: iiwu did not play with ALSA 0.6 - fixed: handle audio underruns for ALSA 0.6 0.4.14: - some makefile and compilation changes - audio play: noise between audioparts during playback - don't stop at end of song when "loop" is active - default magnification in wave-view set to 1 - fixed a audio route initialization bug - new metronome configuration: precount configuration added 0.4.13: - avoid "disconnect error" on startup - wave view: y magnification now persistent - small gui enhancements to reduce flicker - make install: now creates gui dir - implemented 8 bit wave input format - fixed another source of audio crashes 0.4.12: - audio play: mixing buffer was only partly cleared resulting in random noise - fixed: core after removing soft synth instance - set default master volume to 1 - fixed some audio routing bugs - drumedit: added missing display update after drum map loading - drumedit: fixed: when loading external drum map, velocity values got zero - drumedit: fixed: core some time after loading external drum map 0.4.11: - iiwu: in GM-mode dontt allow drum channel program changes; also ignore bank select messages - set GM-Mode resets synth - some changes in drum channel handling - substantial changes in audio implementation - reimplemented audio configuration - miditransform: val2 transforms fixed 0.4.10: - iiwu: implemented sustain, ctrl:expression - iiwu: changed sync audio/midi; this fixes some timing issues - iiwu: fixed: core when loading new sound font while playing - split RT thread into separate midi & audio thread - fixed some bugs: crash on midi recording - some new functions in pianoroll editor - added/integrated Tommi Ilmonens "givertcap" - iiwu: some fixes for ALSA 0.9beta - arranger: voice name popup in channel info works again 0.4.9: - fixed some memory leaks - before loading a new song all synthesizer instances are now removed - reorganized installation: - there is a toplevel installation directory (default /usr/muse); the environment variable MUSE points to this directory - architecture dependent files go into $(MUSE)/lib, architecture independent files into $(MUSE)/share - MidiSync: MC ticks are now also send in stop mode (if configured) - after "Start" is send, sequencer starts on next midi clock tick - iiwu: fixed core dump on save if no soundfont loaded - iiwu: high resolution buffer size independent midi event processing 0.4.8: - faster display updates - some changes for better compatibility with different environments (compiler, lib) - fixes for ALSA 0.5.11 - fixed core dump while removing soft synth instance - fixed some bugs with iiwu+gui - fixed: TransportWindow: tempochanges while masterflag is off - fixed: all tempochanges are now saved in songfile 0.4.7: - ALSA 0.5.11 compiles again - MESSS gui interface, first try: -every midi instrument can have an associated GUI (currently only impl. for MESSS soft synths). The GUI is startet as a separate process connected to the midi instrument. The gui sends midi commands (sysex) to stdout. This midi data stream is connected to the midi instrument port. - test implem. can load sound fonts into iiwu synthi - fixed a bug in loading big sound fonts - waveedit: waveform display works again - some iiwu changes 0.4.6: - completed midi input architecture: every midi track has now assigned input port+channel. Channel can be 1-16 or "all". This allows for routing of different midi channels to different tracks while recording. - changed max number of midi ports from 8 to 16 - fixed several bugs iiwu software synthesizer - fixed compilation problems with some ALSA versions - fixed: changing track name changed record flag - fixed: remove midi editor if associated track is removed - fixed: initial state of solo button in arranger - fixed: hard to reproduce core while deleting track - new command line option to set real time priority - max number of midi ports is now 16 - audio recording from master to file now works: - configure Audio->MixdownFile (only wave/16bit) - switch on record button in audio mixer master strip - play - fixed: graphic master editor: missing display refresh after signature change - changed midiThruFlag: removed from Config->MidiPorts; "midi thru" now is associated with a track, if set all input to that track is echoet to track port/channel 0.4.5: MESSS: (MusE Experimental Software Synthesizer interface Spec): A software synthesizer is implemented as a dynamic loadable library (*.so file) with two interfaces to the outside world: - a LADSPA interface for audio output - a midi sequencer interface registered to ALSA MusE searches all available synths and presents a list in Config->SoftSynthesizer. To use a synthesizer you have to create an instance. Several instances of an synt can be created and used. Every instance creates a) an alsa midi sequencer port (look at Config->MidiPorts) b) a new strip in the audio mixer As a demo i ported a stripped down version of the iiwu software synthesizer (http://www.iiwu.org) to MusE. Setup info is in README.softsynth 0.4.4: - fixed cakewalk style event display in arranger - track comments are handled as 0xf text meta events in midi files - fixed: follow song in pianoroll/drumedit (daniel mack) - fixed: refresh in list editor - implemented 14 Bit controller in list editor - new patch form Takashi Iwai enables MusE to compile with ALSA 0.9.0beta5 and current cvs version 0.4.3: - new: Config->MidiInputTransform - new: comments for tracks: click with right button on track name in arrange window - fixed: score editor sometimes eats up all memory on start; machine was unusable for some minutes until muse crashes - fixed some other smaller bugs - patch from Takashi Iwai for latest (cvs-) ALSA - fixed: score postscript generation (printer & preview output) 0.4.2: - added few missing display updates (bugs introduced with 0.4.1 drawing optimizations) - pianoroll editor: - fixed: edit->DeleteEvents - drum editor: - implemented: edit->DeleteEvents - use different cursor shapes while using Glue- Cut- and Rubber tools 0.4.1: - some small Changes for compiling MusE with gcc 3.0 - track info values (transposition, velocity etc) are now applied when exporting to midi file - better geometry management for ctrl panel - pianoroll editor / drum editor now allow for more than one ctrl panel - new: load/save midi controller sets - automatic creation of midi controller sets on midi import - new: active(used) midi controllers for current parts in editor window are now marked in controller list - fixed: parts in open editors are not restored correctly - many drawing optimizations; scrolling is now much faster/smoother 0.4.0: - input configurable for every track (you can record from more than one midi input device to different tracks) - you have to switch on the "record enable" flag for every track you want to record to - Note "h" is now called "b" unless you set the environment variable "LANGUAGE" to "de" - Changes from Daniel Mack: - bigtime window now shows hour:minute:sec:frame - configurable snap grid for arranger - configurable font size - again "tick underflow" 0.3.20: - "bigtime" widget from Daniel Mack - fixed global accelerator keys f11/f12 (toggle transport & bigtime window) - fixed: score editor: try placing notes in the right margin of the last row gave core dump - score editor: different cursor shapes - new try on missing midi sync ticks (producing "tick underflow" trace messages) - score editor: some small enhancements 0.3.19: - several small bugfixes from Daniel Mack - fixed "make install" - if you have trouble compiling ALSA audio: change "AUDIO = yes" to "AUDIO = no" in make.inc - some fixes/enhancements from Brian K: - fixed: score editor: no more "EVENT not found" during subsequent edits of a selected note - scrubbing with rubber tool in score editor - new part appearance option 0.3.18: - fixed: Export Midifile 0.3.17: - simple audio recording - midi sync slave: received "start" did not reset pos to tick 0 - fixed several bugs in screen update and synchronisation between different midi editors - new: Configure->Appearance dialog from Daniel Mack 0.3.16: - "follow song" now works in both directions - MidiTransformator: implemented missing "Copy" and "Extract" functions - fixed: reset sustain at stop only for channels which really had sustain set - fixed several bugs in midi sync code; needs more testing - received "set song position" from alsa midi driver now handled in sync slave mode - transport buttons are now disabled in "external midi sync" mode - fixed: do not send midi "start" "stop" "continue" etc. as sync slave - fixed: several small bugs i cannot remember 0.3.15: - fixed: some typos in "MidiTransformator" - fixed: core at end of midi recording from external sequencer as sync slave - replaced midi "continue" message with "start" when running as midi sync master known bug: midi clock output only if seq plays, should be send always even if seq stops not implemented: cannot change tempo as sync slave 0.3.14: - fixed: core: typo in "undo add track" - fixed: core: "undo remove event" - selection of events is now a global attribute: if you select an event in an editor, the same event is selected in all open editors - new: Midi Transformator (look at edit->MidiTransform) (not all operators and operations are implemented yet) 0.3.13: - fixed: TimeScale was wrong when using signature changes - fixed: enforce left marker <= right marker - new: mono wave tracks - more usable LADSPA plugins to play with - several small changes/bug fixes 0.3.12: - fixed: synchronisation of tempo/signature changes with sequencer rt-thread - fixed: track parameter were added again and again in loop mode - new: tempo/signature changes undo/redo - new: midi "transpose" function from Daniel Mack (Arranger: edit->midi->transpose) 0.3.11: - fixed: fixed serious bug in wave file handling - simple audio play with ALSA 0.9.x - fixed: editing events -> core (introduced in 0.3.10) 0.3.10: - fixed: core while deleting controller events - new: extended configuration of raw (serial) midi interfaces - fixed: some memory leaks - changed for ALSA 0.9.0 beta2 0.3.9: - some smaller fixes - fixed: core: missing MidiController() Initialization - fixed: pressing another mouse button while "drawing" an event in a canvas with left mouse button pressed gives core 0.3.8: - fixed: correct update of midi port table on song change - CtrlEditor: controllers can(must) now be configured - List Editor: corrected handling of meta/sysex strings - changed: combined pitch high/low to single value ranging from -8192 +8191; editable with controller editor - ALSA 0.9.0beta1 works for midi; as far as i tested it, this alsa version has no more problems with OSS raw midi emulation - new: colored activity display in arranger (Daniel Mack) - new: context sensitive extensions to "right mouse click pulldown menus" for arranger parts (Daniel Mack) - new: gui prototypes for extendend configuration of raw midi devices and audio mixdown file selection - fixed: quirks with OSS midi devices configuration 0.3.7: - start porting to ALSA 0.6.0 cvs version - fixed: option -M produces midi output trace for alsa midi - fixed: pianoroll and drum editor now accept input focus and then honour some keyboard shortcuts - fixed: score editor: core when inserting small rests - new: "ruler" for pianoroll, drum editor and part editor - fixed: midi recording: event len always 1 tick (bug introduced in 0.3.6) - midi port config: show only available raw midi devices - fixed: tempomap/tempo save/restore - fixed: initialize master button to saved value - some smaller changes: - midi recording: new parts begin at record start instead of first event - missing note offs are insertet virtually at record end - recording takes place on selected track - selected part and not on selected part if on different track 0.3.6: - fixed: markerList: click in list -> core - fixed: stop at end of song only if not recording - fixed: events of zero length crash the sequencer - fixed: missing note off events for metronome - fixed: gui: changing port/channel in trackinfo updates tracklist - new: midi recording & loop 0.3.5: - fixed: midi recording with alsa was broken - fixed: mastertrack: -> core - fixed: rename track -> rename part -> core - fixed: help browser: back function - fixed: score: entered lyrics are saved again - fixed: score->staffSettings: tracklist - fixed: score->enterNotes wrong snap position 0.3.4: - fixed: some bugs & quirks - new: implemented pianoroll->edit->deleteEvents 0.3.3: - new: MusE now stops at end of song if in PLAY mode - fixed: core if muse was started without song name and there was no ".musePrj" file in current directory - new: on popular request: "RewindToStart" button - fixed: changing devices while playing - fixed: arranger: could not scroll to the end of song - fixed: song length on midi import - fixed: fatal error in handling "note off" events - new: "sustain" is reset on stop 0.3.2: - fixed: part editing: endo delete, glue etc. - fixed: option -m (trace midi input) works again - fixed: midi input filter: could not toggle channel 1 button - fixed: midi mixer - fixed: midi recording into part: part length adjusted if events recorded past end of part - fixed: MusE initialisation failed if there is no ".musePrj" file in current directory! 0.3.1: - step 2 of internal reorganization - fixed: score: lyrics are now saved again - fixed: some quirks with lost track markers - new: Option -L locks sequencer memory - fixed: recording from serial midi (raw oss & korg tohost) - fixed: several smaller bugs & quirks 0.3.0: - fixed: pianoroll editor: entering new events -> core - new: drum editor click on instrument name "plays" instrument - fixed: changing the channel of a track now changes also the channel of all events in that track - massive changes for audio/wave integration: - start of audio mixer - audio routing 0.2.12: - fixed: wave files/tracks/parts: calculation of tick<->time; it should be possible again to import/play simple waves - fixed: funny things happened when muting all audio tracks - fixed: core if no active record track - new: Rob Naccarato started with documentation; press in MusE and have a look 0.2.11: - fixed: metronome - fixed: initial state of "click" transport button - fixed: midi thru: if midi thru is switched on, all received events are echoed to the port/channel associated to the currently selected track, regardless of the input port/channel. Track parameters as pitch shift, velocity compression etc. are applied before echoing the event. - _not_ fixed: alsa midi driver: strange sysex behaviour 0.2.10: - fixed: parameter change in midi trackinfo - fixed: some errors in *.med file handling - fixed: midi export - fixed: midi events are now played according to priority: - channel priority: 10,1,2,3,4,5,6,7,8,9,11,12,13,14,15,16 - note off before note on 0.2.9: - fixed: typo in seq.c destroyed timing of 0.2.8 - fixed: importing audio files - fixed: writing *med files - fixed: wave form display in arranger - fixed: core on click in arranger "no track area " with pencil tool 0.2.8: - fixed: oss midi devices now work again - reorganized midi event dispatcher - fixed: pitchbend for midialsa (Xavier) 0.2.7: - midi driver reorganization in preparation for better ALSA sequencer integration; soundcard synth work again - some fixes 0.2.6: DrumEditor overhaul: - fixed: reading drum maps - changed: exporting drum maps now writes the whole map - fixed: device popup: ...invalid menu item - new: instruments can now be moved - fixed: changing A-Note/E-Note did not show effect - changed: small x-offset in event canvas allows better placement of events at pos 1.1.0 - new: instrument names can be edited (double click instrument name) - new: some drum maps - fixed: update() missing after selection change in canvas - fixed: len of new inserted drum events was quant-value, should be default len from drum map Alsa Midi Driver: - changed (soundcard synth does not work :-( ) 0.2.5: - fixed: session management for list editor - new: list editor: hex entry in meta event dialog - fixed: Midi: "GS Reset" button aktually did a "GM Reset" - fixed: Midi: "GS Reset" on Song Start was always combined with "GM Reset" - fixed: Arranger: copy/paste produced core - fixed: Arranger: removed some (not working) key accelerators - new: Drag file from KDE fm and Drop on arranger partlist - removed bogus midi archiv - some major code reorganizations in preparation for audio integration resulting in new errors and *.med file incompatibilities; - fixed: "cannot delete busy part" even if part is not busy - fixed: arranger/progname.c: bad instrument name table caused segfault (Tim Mann) - fixed: score/layout.c: could not enter A# (Gilles Fillipini) 0.2.4: - fixed: removed silly warning: Cannot find translation... (translations are not (yet) enabled) - fixed: trackMarker->destroy TrackMarker->create track -> core - new: integration of track markers in arranger - export/import SMF-Marker as Meta Type 6 - changed: src/makefiles new arranged - fixed: score editor: too many rests - fixed: core if you try to insert note outside of staves 0.2.3: - MidiSyncConfig: extSync synchronized with button in transport window - audio: try oss audio device /dev/dsp in addition to /dev/sound/dsp - changed: column expand logic in arranger tracklist - new: KDE2.2: define HAVE_KDE in make.inc to compile a KDE version of MusE (experimental) - new: realtime recording of Midi SysEx Events - changed: better LADSPA plugin handling - fixed: Pianoroll: Color Events: RGB parameter out of range - changed: canvas: moving objects - fixed: AudioMasterMixer produced core after second invocation - new: track markers 0.2.2: - switched to QT2.2 - fixed: Transport: "Master" button initialization - fixed: session management for partlist in midi editors; (new *.med xml files are probably incompatible - fixed: cut&paste now works for parts on drum tracks - fixed: cannot delete busy Parts any more - fixed: honour LADSPA_PATH environment variable - fixed: TransportWindow stays on top and is manageable (testet with KDE2 beta4) - fixed: arranger: column order is now recorded in .med file - fixed: sometimes under obscure circumstances MusE crashed while opening an midi editor - fixed: several typos/errors in *.med file reading/writing - new: list editor: insert midi events (incl. sysex & meta) double click on list entry to open editor to modify values - new: MTC/MMC Slave: Configured as slave you can synchronize MusE from external devices using Midi Time Code. Midi Clock Master: Configured as master MusE can control external sequencers Hooks for MTC/MMC Master and MidiClock slave. - fixed: score: ScoreConfig::setTracklist() missing "clear" - new: score: odd rest values implemented 0.2.1: - new: Arranger: move (midi) parts between applications: - cut/copy part(s) to global clipboard - paste part(s) from global clipboard to arranger - drag parts with middle mouse button (experimental) - new: Pianoroll: move events between applications: - cut/copy events to global clipboard - paste events from global clipboard to arranger - drag events with middle mouse button - fixed: only write audio if there are audio tracks - fixed: PianorollEditor: moving multiple selected events (thanks to Chris Wakelin) - fixed: commented out unused/missing "color.xpm" - fixed: partlist changed to multimap<> 0.2.0: - fixed: another error in OSS midi driver which results in "bad file descriptor" aborts - fixed: MidiFilter - RecordFilter/ThruFilter - new: Master Part of Audio Mixer: - Audio Level Meter - LADSPA Host with - automatic generated user interface - presets store/load - new: LADSPA "Freeverb" plugin for audio master - new: Drum Editor - load/save drum maps - drawing reorganized - new: Pianoroll Editor Functions: - configurable event colors (none/pitch/velocity) - configurable function ranges (apply functions to all/loop/marked events) - selection functions (all/none/loop/invert) - switch between different parts in editor ("ghost events") - PencilTool: Control+LeftMouseButton prevents accidental creation of events - PointerTool: Control+LeftMouseButton restricts to horizontal or vertical move 0.1.10: - new: MidiExport: some configurable Parameter for exported Standard Midi File (SMF) - new: configurable Midi Record/Thru Filter for midi realtime recording - fixed: time signature changes in score editor - fixed: "midi bar scale" is updated on time signature changes - fixed: event sorting in "list mastertrack editor" 0.1.9: - fixed: tempo changes during play - fixed: "follow event" in graphical mastertrack editor - fixed: mastertrack list: dynamic content update if song changed - fixed: OSS midi serial output was broken 0.1.8: - bug: scaling in graphical mastertrack editor - bug: reduce value of MAX_TICK to prevent overflow in scaling - bug: pianoroll editor: length quantization - bug: midi import: timing broken; bug introduced in 0.1.6 - feature: editing of time signature in graphical mastertrack 0.1.7: - bug: typo for 't'-kb accelerator in pianoroll-editor - bug: quant values < 1/64 are not supported-> assertion bug for keyboard accelerator >=8 in pianoroll editor - pianoroll: new feature: step recording - midi input: - press "shift"+ midiKey to enter chords (if you enter same note again, it's deleted) - press "ctrl" + midiKey to continue last note - pianoroll: new menu function: quantize pos+len - quantize configuration dialog: added flag for default len quantization "whats this" help text 0.1.6: - bug: exported midifiles had random "division" timing parameter - bug: core dump on midi record start - feature: keyboard accelerators in pianoroll editor: '1'-'9' '.' and 't' set quant & snap values 0.1.5: - MusE now works again without RTC (Real Time Clock) Device (but much reduced timing accuracy) - new Debug Options -m -M: MidiDump for input and output - global keyboard accelerators: spacebar: while play/record: STOP while stop: Goto left mark while on left mark: Goto Pos 0 Enter: start play Insert: Stop "/": Cycle on/off "*": Record on - Midi Step Recording: implemented external midi keyboard as recording source (new "midi input" toggle button in pianoroll editor) 0.1.4: Audio ============== - simple audio play (ALSA & OSS) - stubs for cliplist Editor, audio recording - AudioMixer master volume - bug fixes for wave viewer Synthesizer ============== - first part of framework for realtime software synthesizer driver/synthis, s1/*; 0.1.3: Score Editor: ============== - print preview button (gv) - postscript default resolution is 72dpi and not 75dpi - configurable overall scale for printer output - simple beams Misc: ============== - bug: path handling for project file: project files are now saved in the correct directory - bug: canvas initial scaling - bug: core if configured device didn't exists - bug: ctrl editor produced values > 127 - feature: Arranger: Parts are now displayed with a horizontal offset - feature: Arranger: added save/restore for some configuration values - feature: Midi Recording: track parameter like Transposition are now applied before loop through - feature: "Thru" flag in Configure->MidiDevices now implemented - feature: Midi Remote Control: control sequencer stop/play/record/rewind with configurable note events - bug: typo in score/layout.c:split() caused core 0.1.2: - Score: - add lyrics entry - changed note head for 4/4 - changed positioning of 2/4 and 4/4 notes - ties can now span systems - tie connected notes are selected as one note - page settings, margins handling corrected - configurable fonts for score editor page layout 0.1.1: - master: scale changed - no more core when selecting the score editor without a selected part to edit - time signature scale in master track - master track: function tempo edit - new popup menu in arranger / part canvas - makefile: "make depend" target - new: alsa raw midi interface - improved score editor: - split system (piano left&right hand) - multi stave systems - symbols - lasso selection - dynamics symbol palette - window position of all toplevel windows is now remembered correctly - bug fixes & code cleanups 0.0.10: - removed obsolete file.c file.h - separated midi archive - removed unused widgets/wtscale.* - removed unused widgets/dial.* - midis with Meta Event Type 6 produced core - removed '\n' chars from windows caption - new setBg(QColor) method for class View - broken Ctrl-Editor fixed - Pencil Cursor now shown for Pencil Tool in Ctrl-Editor - Mute Indicator changed to red dot - added CtrlEditor to DrumEditor - process session info in xml songfile - more work on mastertrack - start ScoreEditor and moving the mouse on canvas (producing mouseMoveEvents) before exposure of QLineEdit (time & pitch in toolbar) produced core on QLineEdit->setText(xx) - in continuous scroll mode position marker updated correctly 0.0.9: - xml like configuration and song file - new midi mixer, shows only active midi channels - bug: metronom configuration: channel&port numbers - bug fixes 0.0.8: - new: quantize function - new: wave tracks: new classes: WaveFile Clip; load wave file; expand song file structure first try on wave editor; - logarithmic magnify - rework of View & Canvas Classes, resulting in much faster scrolling 0.0.7: - fatal error: use of uninitialized device 0.0.6: - more diagnostics in file functions - new: can load and save *.gz and *.bz2 files - new function: send local off to instruments - bug fixes in pianoroll step recording - bug fix: "follow song" save and restore - bug fix: in importing midi files: calculating parts - bug fix: metronome -> core - new configuration options for parts 0.0.5: - new: midi activity display in tracklist - new: patch selector in channel info; shows midi instrument categories - new: insert & delete Controller Values in Ctrl-Editor - some minor bugs corrected - nasty bug in Song::nextEvents(), which prevents simple midi songs played correctly 0.0.4: - implemented: forward & rewind buttons - implemented: drum edit: change values in info bar - error: arranger->tracklist: resize failed if columns are swapped - enhanced file selector for background image selection - more WhatsThis and ToolTip Help - Backport to QT202: Filedialog: Filterlists don't work - Midi Config: changed signal click() to rightButtonClick() - missing initialisation in song constructor - new subdirectory for controller editor - controller editor for some values 0.0.3: - new transport design - redesign of TrackInfo and ChannelInfo - some changes reg. fonts and geometry management - misc toolbars changed to read qt toolbars 0.0.2: - changed color for cpos to red - don't play metronome clicks with time < current - doubleclick on arranger trackname: entrywidget now gets input focus - midi device configuration: reworked - removed endless loop in Song::nextEvents() - ported to qt-2.1.0-snapshot-20000113 - changed QPushButton to QToolButton - some cosmetic changes in transport window 0.0.1 - first release muse-4.0.0/src/Doxyfile.in000066400000000000000000000134351404105311000153610ustar00rootroot00000000000000# Doxygen configuration generated by Doxywizard version 0.1 #--------------------------------------------------------------------------- # General configuration options #--------------------------------------------------------------------------- PROJECT_NAME = MusE PROJECT_NUMBER = @VERSION@ OUTPUT_DIRECTORY = doc OUTPUT_LANGUAGE = English EXTRACT_ALL = YES EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ALWAYS_DETAILED_SEC = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = INTERNAL_DOCS = YES CLASS_DIAGRAMS = YES SOURCE_BROWSER = YES INLINE_SOURCES = YES STRIP_CODE_COMMENTS = YES CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO VERBATIM_HEADERS = YES SHOW_INCLUDE_FILES = YES JAVADOC_AUTOBRIEF = NO INHERIT_DOCS = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES DISTRIBUTE_GROUP_DOC = NO TAB_SIZE = 8 ENABLED_SECTIONS = GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES ALIASES = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = . FILE_PATTERNS = *.cpp \ *.h RECURSIVE = YES EXCLUDE = EXCLUDE_PATTERNS = EXAMPLE_PATH = EXAMPLE_PATTERNS = IMAGE_PATH = INPUT_FILTER = FILTER_SOURCE_FILES = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = YES DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = @DOXYGEN_TREEVIEW@ TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = EXPAND_AS_DEFINED = #--------------------------------------------------------------------------- # Configuration::addtions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = YES PERL_PATH = @PERL@ #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- HAVE_DOT = @HAVEDOT@ CLASS_GRAPH = YES COLLABORATION_GRAPH = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES GRAPHICAL_HIERARCHY = YES DOT_PATH = @DOTPATH@ MAX_DOT_GRAPH_WIDTH = 1024 MAX_DOT_GRAPH_HEIGHT = 1024 GENERATE_LEGEND = YES #--------------------------------------------------------------------------- # Configuration::addtions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO CGI_NAME = search.cgi CGI_URL = DOC_URL = DOC_ABSPATH = BIN_ABSPATH = /usr/bin/ EXT_DOC_PATHS = muse-4.0.0/src/LICENSE000066400000000000000000000000311404105311000142370ustar00rootroot00000000000000please see src/COPYING. muse-4.0.0/src/README000066400000000000000000000370111404105311000141220ustar00rootroot00000000000000==================================================================== MusE --- Linux Music Editor ==================================================================== Welcome to MusE, the open source MIDI/Audio sequencer. MusE is distributed under the GNU General Public License (GPL). Please check out the file COPYING in the src/ directory for more details. ============================= Features: ============================= MIDI sequencing: - Realtime record/playback - Midi file import/export - Input filter - Support for internal softsynth plugins using LV2, DSSI, VST, LinuxVST and MESS - much more Audio sequencing: - Realtime Record/Playback several mono/stereo inputs/outputs - Graphical editing of automation - Support for LADSPA, LV2, DSSI, VST, LinuxVST plugins Technologies supported: - LASH - for session control with external LASH enabled applications - JACK - The Jack Audio Connection Kit can be used for audio and midi - RtAudio - basic audio input/output for Pulse, ALSA and OSS - ALSA midi - Plugin formats: LV2, DSSI, VST, LinuxVST, MESS - Support Inst(rument) Patch Library for enhanced fluidsynth features. - FLAM plugin guis. - Built on Qt5 ,-----------------------------------------------------------------. | NOTICE | |-----------------------------------------------------------------| | Some parts of MusE code are EXPERIMENTAL, and may well result | | in a CRASH, and who knows what other ill effects. | | RUN THIS SOFTWARE AT YOUR OWN RISK. | `-----------------------------------------------------------------' ============================= Requirements: ============================= - CMake >= 2.4 http:/www.cmake.org/HTML/Download.html - Qt: Qt >= 5.7.0 (development files) http://qt-project.org/ MusE does _not_ compile with older versions For ubuntu packages can be installed via: sudo apt-get install qt5-default qttools5-dev qttools5-dev-tools - gcc >= 4.x.x or clang >= 2.9 http://gcc.gnu.org/ http://clang.llvm.org/ - libsndfile >= 1.0.1 (development files) http://www.mega-nerd.com/libsndfile/ - libsamplerate >= 0.1.0 (development files) http://www.mega-nerd.com/SRC/ - JACK version 1 >= 0.103 (development files) or JACK version 2 >= 1.9.9.5 (development files) http://jackaudio.org/ - LADSPA (development file ladspa.h) www.ladspa.org ============================= Optional: ============================= - ALSA >= 0.9.0 Advanced Linux Sound Architecture (development files) http://www.alsa-project.org/ - fluidsynth >= 1.0.3 (formerly known as iiwusynth) (development files) http://sourceforge.net/apps/trac/fluidsynth/ - libinstpatch >= 1.0 Instrument Patch Library (development files) http://www.swamiproject.org/ - RtAudio >=5.0 audio input/output support. https://www.music.mcgill.ca/~gary/rtaudio/ - LV2 (LADSPA Version 2) plugin support: lilv >= 0.22.0 (development files) sord >= 0.14.0 (development files) http://drobilla.net/software lv2 >= 1.12.0 (development files) http://lv2plug.in - LV2 Gtk2 User Interface support: Some LV2 plugins may provide a Gtk2 based graphical User Interface. To view them, the following is required (development files): gtkmm-2 http://www.gtkmm.org gtk+-2 http://www.gtk.org - DSSI Disposable Soft Synth Interface (development files) http://dssi.sourceforge.net/ - liblo Lightweight OSC (Open Sound Control) (development files) http://liblo.sourceforge.net/ (Both recommended - DSSI can use OSC, OSC alone does nothing, for now.) - dssi-vst Support for DSSI vst plugins http://www.breakfastquay.com/dssi-vst/ - LASH Audio Session Handler http://lash.nongnu.org/ Recently LADISH has been emulating it instead: http://ladish.org/ - Python The python scripting language http://www.python.org The remote control supports Python 3. See https://github.com/muse-sequencer/muse/wiki/python-remote-control. The bundled midi scripts ('Scripts', or 'Plugins' in earlier versions) require Python 3. - Pyro 4 Python Remote Objects https://pythonhosted.org/Pyro4/ The remote control supports Pyro 4. See https://github.com/muse-sequencer/muse/wiki/python-remote-control. - PyQt5 is used by some of the bundled midi scripts ('Scripts', or 'Plugins' in earlier versions). Quick instructions for Ubuntu and similar distributions: ================================= Building MusE under Debian/Ubuntu: ================================= 1. Install build tools: sudo apt-get install git build-essential cmake 2 Get muse source (unless you already have it). Choose one of the below: a. Download the latest muse source distribution from https://muse-sequencer.github.io/ b. Clone muse source: git clone https://github.com/muse-sequencer/muse.git 3. Install libraries: sudo apt-get install\ libsndfile1-dev\ libsamplerate0-dev\ libjack-jackd2-dev\ ladspa-sdk\ qt5-default\ qttools5-dev\ qttools5-dev-tools\ liblo-dev\ dssi-dev\ lv2-dev\ libsamplerate0-dev\ libsndfile1-dev\ libfluidsynth-dev\ libgtkmm-2.4-dev\ librtaudio-dev\ libqt5svg5-dev\ libinstpatch-dev\ liblilv-dev\ liblrdf0-dev\ librubberband-dev\ python3-dev\ python3-pyqt5 4. Change directory: cd muse/src 5. Compile: ./compile_muse.sh 6. Change directory: cd build 7. Install in /usr/local: sudo make install 8. Run: muse4 ============================= Generic building MusE: ============================= - Download source from http://muse-sequencer.org/ - Unpack the source somewhere. You may also try the various MusE development GIT branches for up-to-the-minute features and fixes, but they may be less stable. - To compile MusE, run the following commands from the top level directory where the source code was unpacked (the directory where THIS README FILE is found): ****************************************************** * Notice: * * A quick way is to run the ./compile_muse.sh * * script in the same dir, it should perform the same * * steps for a basic setup without asking any * * questions. If it fails you may be better off using * * the instructions below. * ****************************************************** The build directory: -------------------- Building in a subdirectory is recommended to keep the build directory separate from the source tree. So create a new subdirectory with a useful name like "build" or "debug" or "release": mkdir build Change directory (cd) to the new directory: cd build Configuration: -------------- There are a few different ways to configure (notice the two dots): Type "cmake -L .." to see options, then compose "cmake " yourself. Some are: -DCMAKE_BUILD_TYPE= can be blank (to reset a previous cached type to 'empty'), Debug, Release, RelWithDebInfo and MinSizeRel. Release is recommended, to get optimizations. If no CMAKE_BUILD_TYPE is given at all, cmake uses either the previous cached value, or else 'empty' (plain or default system optimizations). -DCMAKE_INSTALL_PREFIX= The installation where the program is installed. The default is to install in /usr/local. Or type "ccmake .." (if you have it - text-mode GUI). It may be blank so hit 'C' to first-time configure. Fiddle with yer options if ye so desire, then hit 'C' to configure then 'G' to generate and exit, or 'Q' to quit without generating. Or there is also a desktop GUI for cmake called cmake-gui. Normally MusE builds its many internal modules as SHARED libraries and the executable file is small but it links to all those libraries. There is a special flag for building MusE as a more or less 'monolithic' application (one big executable) which forces MusE to build most modules as STATIC libraries and join them all in the final executable: -DMODULES_BUILD_STATIC -------------------------------- *** NOTICE TO PACKAGERS: *** -------------------------------- Some packagers of software like to ensure that there are no unresolved symbols in ANY shared libraries in the software, for example by using: -DCMAKE_SHARED_LINKER_FLAGS=-Wl,--no-undefined When using such flags, the -DMODULES_BUILD_STATIC flag MUST BE ALSO BE SUPPLIED otherwise there will be many unresolved symbol errors. Compiling: ---------- After configuration, while still in the build directory,type: make Installing: ---------- After compiling, type: make install (as root) or sudo make install ============================= Running MusE: ============================= Recommended setup: ------------------ MusE is a realtime program which requires special rights to work properly. - Check if you are running a sufficiently new linux kernel > 2.6.x A modern, standard destop kernel should suit virtually all needs. For even stricter realtime performance a so-called 'low latency' or 'realtime' kernel may be available. Check your distro's packages for availability. - create an "audio" group if it does not already exists and put yourself into this group - For realtime priority, you may want to ensure either the file: /etc/security/limits.conf or /etc/security/limits.d/audio.conf contains: @audio - rtprio 95 @audio - memlock unlimited Some distros have a graphical menu or tool which does this for you. - Start jack, typically by using the qjackctl application. MusE can also be run without Jack. MusE will use a dummy audio driver if Jack is not detected, or the -a option is given. Running: ------------------ start MusE by typing: muse4 Some are (complete list is printed by running muse4 -h): -h help -v print version -d debug mode: no threads, no RT -D debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively! -m debug mode: trace midi Input -M debug mode: trace midi Output -s debug mode: trace sync -a no audio -A Force inclusion of ALSA midi even if using Jack -P n set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) -Y n force midi real time priority to n (default: audio driver prio +2) -p don't load LADSPA plugins -I don't load DSSI plugins -L don't use LASH -l xx force locale to the given language/country code (xx = de,en,es,fr,pl,ru,sv_SE) (JACK and all its clients (qjackctl & MusE) must run with the same user id) Startup troubleshooting tips: ----------------------------- - Some rare distros might not load the alsa sequencer module by default. If necessary, load the alsa sequencer module with: /sbin/modprobe snd-seq - Timer accuracy for ALSA support: If ALSA support is enabled, a reliable source of timing is desired for playback of midi notes. By default, the timer is attempted to run at 1024 Hz (ticks/second). MusE will try the Real Time Clock (RTC) first. If that fails it will try ALSA timers instead - typically the ALSA High Resolution (HR) timer or the ALSA system timer. The RTC is recommended, with sufficient permissions, for best accuracy. To ensure you have permissions to use the RTC, on modern systems using udev rules, create this file if it does not exist: "/etc/udev/rules.d/40-timer-permissions.rules" and add the following lines to the file: KERNEL=="rtc0",GROUP="audio" KERNEL=="hpet",GROUP="audio" and, as stated in "Recommended setup" above, ensure you are part of the audio group. (The hpet is not supported, but that line can help other applications.) It is possible to use the ALSA system timer instead of the RTC, but it usually requires a kernel built with a 1000 Hz system timer (low-latency, realtime, or custom kernel as in "Recommended setup" above). With most desktop kernels, the system timer is limited to 250 Hz, while the HR timer is limited to 1000 Hz. On such distros, without modification, MusE should end up picking the 1000 Hz HR timer. But if you set your RTC as above, you can run even higher rates. See the Global Settings dialog for user-adjustable high rates. Run MusE in a terminal to see what it picks. -D option gives more info. (The following is old information! But may work for those without udev.) Make sure you can access the realtime clock (RTC) chmod 660 /dev/rtc chgrp audio /dev/rtc Make sure MusE can set the rtc clock: echo 8192 > /proc/sys/dev/rtc/max-user-freq inspect with: cat /proc/sys/dev/rtc/max-user-freq ============================= Known bugs: ============================= The odd wierdness happens and some things may not be completely implemeneted though we hope it is as stable for you as it is for us! If you differ in this opinion we are grateful for all reported issues. See the bug tracker for a better view of issues https://github.com/muse-sequencer/muse/issues ==================================================================== Let us know whether MusE works for you !!! Have a look at the webpage https://muse-sequencer.github.io/ for details. muse-4.0.0/src/README.developer000066400000000000000000000052621404105311000161110ustar00rootroot00000000000000MusE is really not big on making developers do it the MusE way. For good and bad (probably mostly bad) it's a bit of happy anarchy out there. But whatever, it's a labor of love. Anyway, maybe some things should be a bit more clearly defined so I started this document with some pointers for best practices and procedures. * STYLE * whatever floats your boat. Okay, maybe try to keep it atleast somewhat consistent :) * TOOLS * MusE is highly dependent on Qt, mostly this is a good thing, especially as we want to make MusE even more portable. So, when implementing stuff that requires some support library, do check if Qt can provide the necessary functionality. Many times it can. * DEVELOPMENT * * RELEASE PROCEDURE * When making a release there are some steps that should always be done to make the packaging consistent and traceable. 1. make sure that version info is updated. CMakeLists.txt:SET(MusE_VERSION "3.0.0pre1") CMakeLists.txt:SET(MusE_VERSION_FULL "3.0.0pre1") 2. add a release-line to the ChangeLog (search for old ones for some format hints) 3. commit everything to git 4. now create the tarball from the build dir (could be a good idea to create a new build dir) $ make clean (otherwise the tarball will way too big) $ make package_source feel free to ctrl-c out of this as soon as the .tar.gz build is done as it contines to make a lot of variants that we do not use. 5. verify that the built package has the right name and can be built NOTE: if you are making a point release (e.g. 3.0.1) the file name is based of the path so the package will be called muse-3.0.tar.gz while it really should be muse-3.0.1. Easiest way to fix this (for now) is to untar the package, rename the folder (in this case) from muse-3.0 to muse-3.0.1 and tar it back into muse-3.0.1.tar.gz. 6. make a tag in git for this particular checkin when you are satisfied the release is correct From 4.0 release we use https://semver.org/ formatted tags. For instance 4.0 pre release 2 is 4.0.0-pre2. 'git tag -l' lists all of the previous tags. Use git tag -a to create an annotated tag. To push the tag to master: git push --tags 7. create a releasenote 8. upload both releasenote and tarball to the sourceforge page (or whatever is in fashion when it's time). Update: Currently we upload to both sourceforge (because we always have and it's not a lot of work) and to the Releases page on github. 9. Update the frontpage and news page at https://muse-sequencer.github.io/. The frontpage is html and resides a specific git repo. The news page is in the muse github wiki. 10.Update wikipedia to reflect the new release. 11. send out a release mail muse-4.0.0/src/README.developer.undo_system000066400000000000000000000163731404105311000204660ustar00rootroot00000000000000V 0.0.1 03/26/2020 By Tim. A mini review of the Operations and Undo system in MusE. ======================================================== Operations: ------------ During each audio cycle segment, a realtime audio thread processes audio and midi. A block of audio and midi events are gathered to be played in the next segment to various outputs, while a block of incoming audio and midi events from the last segment are recorded from various inputs. Various containers house all the audio and midi events in a song, from which the realtime process thread gathers the required events for the next playback segment. When it is desired to alter the contents of the various containers which house all audio and midi events, and to do so from a thread other than the realtime thread, for example the GUI thread, the actual change must be synchronized with and take place in the realtime audio thread, otherwise these concurrent accesses will crash. In a realtime audio thread it is forbidden to use code that may block or wait or take a long time, such as mutexes, memory allocations, or printing functions. So MusE does not use any mutexes or locks of any kind in the realtime thread. Instead it uses synchronized OPERATIONS to make desired changes in the realtime process callback just before audio and midi are processed. Example: To change a note in a list of notes in a song from inside a GUI dialog, an OPERATION is requested, and the GUI thread will pause while the realtime thread executes the request. The GUI thread resumes when the OPERATION is done. Note that it is possible the realtime audio thread may NOT be running in realtime, for example if the audio system is not set up for realtime. MusE is fairly tolerant of that, but it is possible the audio thread may not finish before the GUI thread runs again, which may cause a concurrency crash if some list was being changed. This is why realtime is important and why it is important to try to make changes as quickly as possible in the audio thread in case the thread is not realtime - if the changes are taking a long time in the audio thread, the GUI thread might run. Undo/redo: ---------- It is desirable to have an undo/redo for many OPERATIONS. And yet, not all OPERATIONS can be or need to be undoable. So for many OPERATIONS, the request is sent to the UNDO system, instead of directly to the OPERATIONS system. The UNDO system remembers the OPERATION so that it can be undone and redone. Still, some requests are sent directly to the OPERATIONS system, bypassing the UNDO system. In addition, the UNDO system has the ability to process some requests WITHOUT remembering them for undoing, basically passing them on to the OPERATIONS system. This is convenient for example when a list of UNDO OPERATIONS contains some OPERATIONS that should not be undoable. This eases the caller's composition of lists of requests, using only the UNDO system instead of working both the UNDO and OPERATIONS systems. The UNDO system has a few tricks. It can combine previously executed commands into one list. It can reduce repeated similar commands to just one, such as when operating a mouse to adjust some value - only the last value at mouse-up is stored instead of storing hundreds of values in hundreds of commands. Operations vs. Undo Operations ------------------------------- A distinction should be made between OPERATIONS and UNDO OPERATIONS: Although the UNDO system takes requests and composes and executes OPERATIONS, UNDO requests are not the same as OPERATION requests. The caller does not pass OPERATION requests to the UNDO system, the caller passes UNDO OPERATIONS. Owing to the different nature of the two systems, the composition and structure of the requests is different. For example OPERATIONS system has the ModifyTempoList OPERATION, but the UNDO system has AddTempo DeleteTempo and ModifyTempo UNDO OPERATIONS. Stages: ------- Both the OPERATIONS and UNDO systems work in STAGES: * OPERATIONS system: The OPERATIONS system has two STAGES: A REALTIME STAGE followed by a NON-REALTIME STAGE. The idea is that the caller composes an OPERATION beforehand in the GUI thread, then passes it to the OPERATIONS system where any realtime-sensitive changes occur in the REALTIME STAGE, followed by any non-realtime-sensitive code in the NON-REALTIME STAGE such as cleanups, safe deletion of memory no longer used, etc. * UNDO system: The UNDO system has three STAGES: A NON-REALTIME STAGE (1), followed by a REALTIME STAGE (2) which basically just calls the OPERATIONS system REALTIME STAGE, followed by a NON-REALTIME STAGE (3) which basically just calls the OPERATIONS system NON-REALTIME STAGE. The UNDO system NON-REALTIME STAGE (1) is a convenient place where any non-realtime-sensitive code can by run BEFORE the REALTIME STAGE. It offers a place to compose complex preparations for a request instead of the caller having to do it, trying to make the caller's job as simple as possible. For example memory allocation. The OPERATIONS system has no such STAGE. The caller must compose the OPERATION and handle any allocation etc. although the system's NON-REALTIME STAGE can be used to cleanup, deallocate etc. Altering lists vs. swapping them out quickly: --------------------------------------------- In the OPERATIONS system's realtime code, the idea is to execute only the bare minimum code required to make the changes. Such as inserting in a list, or calling erase with an iterator that was already found in the GUI thread so that erase is 'ready to go'. Before the OPERATIONS system was added, the UNDO system handled all of this, but it was ALSO executing some awfully heavy code. But even though those list insert/erase techniques are as tight as they can be, they STILL may allocate or de-allocate memory, which is not realtime safe ! One way to guarantee very fast changes to containers (fixed time) is to completely swap them with another list, composed beforehand with the desired changes. If the container happens to be a pointer-to, the pointer can be quickly, even atomically, swapped with another pointer-to, guaranteeing no hiccups or xruns in the processing. If the container is NOT a pointer-to, unfortunately there is no pointer to swap, since the container is the object. However some containers have a swap() method which has very fast fixed-time complexity, which really helps in this situation. Note with this technique, the UNDO system does NOT store a copy of the container for each UNDO OPERATION in the UNDO history. The UNDO system only stores the necessary CHANGES, saving memory. When an actual Undo is performed, only then is a new replacement list composed based on those changes, and then swapped out with the original. Several OPERATIONS have now been switched over to this technique. One caveat and inefficiency is that with a very large container needing only a simple change, you will be making a copy of it, albeit only for a brief time until swapped. Tempo, time signature, key, and other special operations: --------------------------------------------------------- These three tempo, sig and key areas have very special needs due to how these containers operate. The code is somewhat complex. In addition certain other operations have special needs and have more complex code. muse-4.0.0/src/README.ladspaguis000066400000000000000000000035601404105311000162570ustar00rootroot00000000000000MusE can create LADSPA GUI's (graphical user interfaces) from Qt designer *.ui files at runtime. This allows a user to create or modify customized LADSPA guis without recompiling MusE. ====================== Installation ===================== Copy the file muse/widgets/musewidgetsplugin.so into a directory were Qt (designer) can find it. Example: if you installed Qt in /usr/qt3: su -c "cp -af musewidgetsplugin.so /usr/qt3/plugins/designer" Check: After starting the Qt designer you see all MusE specific widgets under Tools/MusE. If you cannot find any MusE widgets, designer did not find the file "musewidgetsplugin.so". ====================== Environment ====================== Naming: The Qt designer files are named after the LADSPA plugin ID. Example: the "freeverb" plugin has the id "1050". A gui for freeverb has to be named "1050.ui". The plugin ID is shown in the MusE plugin browser. Path: MusE looks for *ui files at (museglobalshare)/plugins/. If you installed MusE at "/usr" (configured with --prefix=/usr) MusE looks at "/usr/share/muse/plugins" for *ui files. ====================== Creating *.ui file ====================== The association between LADSPA plugin parameters and Qt-Widgets is done by name. All input widgets which manipulate a LADSPA parameter must have a name starting with the letter "P" followed by the parameter index. Example: A "Slider" widget which manipulates the 3th parameter of a plugin has the name "P3slider". Supported Widgets: "Slider" slider for float values "DoubleLabel" entry for float values "QCheckbox" to manipulate a on/off LADSPA parameter "QComboBox" select from a list of named values; sets integer LADSPA values (for more hints please look at the example *.ui files) muse-4.0.0/src/README.softsynth000066400000000000000000000054451404105311000161700ustar00rootroot00000000000000============================================================= Short instructions to get the soft synthesizer up and running ============================================================= 1. Compile and install MusE 3. Run MusE from an xterm with Option "-D"; look at the debug output in the xterm; you should see something like: 3 soft synth found found soft synth found soft synth found soft synth 4. Configure Software Synthesizer Open Settings->MidiPorts/SoftSynth. - select a software synthesizer - press "Add Instance" to create an istance of this synthesizer; the synthi shows up in the list of instances with a unique name 5. Configure Midi Port To use the synthesizer instance you must connect it to a MusE midi port: - click into the "Device Name" column of an empty midi port (Device Name == "none") - select the synthesizer from the dropdown menu. This connects the synthesizer to the selected midi port. The instrument type is automatically set. 6. Open the Audio Mixer You see a new mixer strip of type "Synthi" and the name of the new created synthesizer instance. Route the audio output of this strip to "Output". (per default the synthesizer is routed to the first Output strip) 7. Now you are ready to play with the "organ" synthesizer - create midi track - set the midi port to the synthesizer port - click the "R" column in the tracklist to enable "Recording" 8. Optional: open Configure->MidiPorts and click in column GUI to show a GUI for the synthesizer instance. There is no gui available for the S1 synth. The iiwu gui enables you to enter a different sound font. The organ gui has some "draw bars" to let you play with sounds in real time. These are the software synthesizers currently available: - S1 a simple sythesizer skeleton as a guide for all who want to code their own one: - only one tone at a time - simple sinus wave form - no gui The synthi may be used as a metronome. - organ, an adapted version of the LADSPA plugin "Organ - Additive Organ Synthesizer Voice" from David A. Bartold "organ" implements a gui connected bidirectional to the synth via stdin/stdout - fluid, is the adapted version of Peter Hanappe´s sample based fluid synthesizer (formerly known as iiwu) - loadable sound fonts - multi timbral - 128 voices - simple gui lets you load a different sound font Fluid loads a default sound font determined by the environment variable "DEFAULT_SOUNDFONT". muse-4.0.0/src/README.vstsdk000066400000000000000000000023351404105311000154400ustar00rootroot00000000000000============================================================= Short instructions for compiling MusE with native Steinberg VST SDK ============================================================= By default MusE utilizes the VESTIGE compatibility headers for VST support, it is however possible to compile with Steinbergs SDK for some possible improvements (vst chunks has been implemented so I actually don't know if there is any reason to use the VST SDK anymore) To fulfill the requirements for this follow these steps: 1. download and extract vstsdk (last it was seen here: http://www.steinberg.net/en/company/developers.html) 2. find the folder containing aeffectx.h (usually it's under /pluginterfaces/vst2.x/ 3. Edit the aeffect.h and change #define VST_2_4_EXTENSIONS 1 to #define VST_2_4_EXTENSIONS 0 4. Configure MusE to disable VESTIGE and point to the SDK HEADERS, for instance with adding these arguments to the cmake commandline: cmake -DENABLE_VST_VESTIGE=OFF -DVST_HEADER_PATH=/pluginterfaces/vst2.x/ Note that by default the sdk extracts into a folder with spaces, this may work badly, safest to rename the path without spaces. 5. run cmake, make and make install as you usually would. muse-4.0.0/src/README.win32000066400000000000000000000053061404105311000150650ustar00rootroot00000000000000# Building MusE on Windows. The file is named win32 though it extends to windows in general but the word Windows is just too ambiguous. There is a thread in the forum with the most recent effort to bring Windows support to MusE: https://linuxmusicians.com/viewtopic.php?f=61&t=19353 Be warned that the Windows build of MusE is work-in-progress and is mostly of interest to tinkerers that wish to pursue this effort. ## Instructions The following steps allow native Muse (git) compilation under Windows with the mingw64 compiler. Optional steps or alternatives are commented out. It is available in it's original formatting here: https://musicsecrets.euniversity.pub/muse.html Support files: Required dependencies Fix make paths script (place it in muse/src) Compile components script (place it in muse/src) Compile Muse script (place it in muse/src) Pack script (TODO) 1. Install msys2 from http://www.msys2.org/ in a short path (i.e. f:/a) # From the msys2 terminal pacman -Syu # Close the msys2 terminal and open it again pacman -Su 2. From mingw64 shell from now on, install common packages: pacman -S \ mingw-w64-x86_64-toolchain \ python3 \ python3-setuptools \ mingw-w64-x86_64-python3 \ mingw-w64-x86_64-python3-setuptools \ python2 \ python2-setuptools \ pkg-config \ mingw-w64-x86_64-pkg-config \ autoconf \ automake \ perl \ gtk-doc \ flex \ bison \ patch \ libtool \ mingw-w64-x86_64-libtool \ wget \ git \ nasm \ mingw-w64-x86_64-nasm \ dos2unix \ mingw-w64-x86_64-cmake 3. Install pre-built dependencies pacman -S \ base-devel \ mercurial \ cvs \ p7zip \ ruby \ mingw-w64-x86_64-qt5 \ mingw-w64-x86_64-ladspa-sdk \ mingw-w64-x86_64-libsndfile \ mingw-w64-x86_64-libsamplerate \ mingw-w64-x86_64-fluidsynth \ mingw-w64-x86_64-gtkmm \ mingw-w64-x86_64-dlfcn # Build the following packages with these commands in /usr/src: cd /usr/src cd #PACKAGE_DIR# makepkg-mingw -g >> PKGBUILD makepkg-mingw pacman -U *.pkg.tar.xz cd .. mingw-w64-liblo mingw-w64-serd mingw-w64-sord mingw-w64-lv2 mingw-w64-sratom mingw-w64-lilv mingw-w64-portaudio mingw-w64-jack 4. Build Muse cd /usr/src git clone --recurse-submodules -b master git://github.com/muse-sequencer/muse.git cd /usr/src/muse/src export LIBRARY_PATH=/mingw64/lib export CPATH=/mingw64/include # Copy here the files compile_muse_mingw.sh and CMakeLists.txt ./compile_muse_mingw.sh 5. Pack files (TODO) # Place muse_pack.sh in /usr/src ./muse_pack.sh muse-4.0.0/src/SECURITY000066400000000000000000000222341404105311000144350ustar00rootroot00000000000000========================== DEC 16 2016: SOME OF THIS INFORMATION IS SEVERELY OBSOLETE. IT IS PROVIDED FOR REFERENCE. MODERN SYSTEMS HAVE NO TROUBLE RUNNING MUSE. SEE THE README FILE FOR MOST OF WHAT YOU NEED TO KNOW. ========================== MusE Security Information ========================= In order to operate reliably at low latencies, MusE needs root privileges. For a stand-alone computer in a home environment, this is not a problem. However, on networked machines with multi-user access, there are some security issues to be aware of. Why does MusE require root privileges ? --------------------------------------- 1. MusE must set the real time clock (/dev/rtc/) to a higher rate in order to get a more precise timing source than the standard setting would allow. For this task, it is *not* sufficient to alter the permissions or group of /dev/rtc. You need root access. 2. The MusE audio threads must acquire real-time scheduling to perform with low latency and free of dropouts. Since this could be misused for a local denial-of-service attack (you can hog 100% cpu with a real-time task, thus effectively making the system unusable), only root is allowed to do that. Do I need to be root to run MusE ? ---------------------------------- No. You should not do normal work as root. Use the root login exclusively for administrative tasks. You can run MusE as a normal user, provided you have set the *suid bit*. This is done automatically when you build and install MusE. How does this "suid bit" thing work ? -------------------------------------- Normally, when a program is started, it gets the user and group id of the user who started it, and thus has the same permissions as the user. It can read and write the same files and use the same devices. Some applications need higher privileges to perform certain tasks, but must be available to normal users. To accomplish this, UNIX-like systems have the concept of the "effective user id". That means you can start a process, but the effective user id will be someone else's, most likely that of the root user. You can recognize such programs by the suid (for set-user-id) bit in their permissions. For example, when you do #ls -l /bin/ping you will see something like -rwsr-xr-x 1 root root 20908 Feb 27 2001 /bin/ping . This tells you it's a normal file (the first dash), it is readable and writable by the owner (root) and has the owner's suid bit set (the letter "s"). You may invoke it as a normal user, but the program will have root permissions while it runs. (Btw, there is also an sgid (set-group-id) bit, which allows a program to be run with the permissions of the group it is owned by. This can be used for finer-grained access control to some programs but is rarely used in practice.) (Btw2, the letter "s" actually means both "executable" AND "set-{user,group}-id". A capital "S" stands for "set-uid" without the execution privilege, which also very rarely seen on real systems.) What is the problem with suid programs ? ---------------------------------------- suid programs are safe as long as you can be sure they do only the job they were written to do. For instance, you certainly want users to be able to do a ping, but you wouldn't want them to wipe out the system disk while doing it. Unfortunately, there is a very common vulnerability in many programs called a buffer overflow, which allows an attacker to spawn a shell from within a suid program that inherits the permissions, giving him or her root access to the entire system. This exploit is fairly easy for an experienced attacker. All that's needed is the compiled equivalent of the C expression execve("/bin/sh"), which can be inserted into the running program whenever it does not check the length of user input properly, by overflowing a buffer and thus overwriting a part of the program code with the shell exploit code. The more complex a program becomes, the more likely buffer overflow vulnerabilities slip in. If you are interested in details of such attacks, I recommend AlephOne's paper "Smashing the stack for fun and profit", to be found in Issue 49 of Phrack Magazine (http://www.phrack.com/show.php?p=49&a=14). Does MusE have buffer-overflow vulnerabilities ? ------------------------------------------------ It may. But even if it had not, it is good practice to assume it does. As soon as you are in a security-critical environment, you should treat all suid programs with extra care unless they are proven to be secure. This is a gruesome and boring task, and we all want Werner to concentrate on cool new features rather than digging through the code to fix loopholes that aren't even a problem for 99% of the MusE users. MusE does not need to be as secure as server daemons. It is intended for home use in a trusted environment. If you run MusE on your company's primary DNS server, it's your fault. But even home machines can become targets for intruders the moment they connect to the internet. Since almost all of the machines than run MusE are occasionally used to surf the web, it might be worth taking a few precautions. What can I do to minimize the risk of a suid program ? ------------------------------------------------------ By default, Werner drops the root privileges in MusE's GUI thread - only the audio threads keep it. This rules out many possible exploits, since GUI code is usually the hardest to make secure. As a further very simple yet effective security precaution, you can create a group of trusted users, and give only this group access to critical suid programs. For example, you might create a group called musers, of which you and your best friend are members. Then you can set the muse binary as follows: #chown root:musers muse #chmod 4750 muse #ls -l muse -rwsr-x--- 1 root musers 20930049 Aug 28 19:34 muse Now only members of the group musers can use MusE, Joe Random Hacker can not. (However, if your account is hacked, MusE can then be exploited to gain root, but hey...) Additionally, you can use "givertcap" as described in the next section. What is givertcap and how do I use it ? --------------------------------------- "givertcap" (give real-time capabilities) is a small wrapper written by Tommi Ilmonen. When enabled, it is executed by MusE and gives to it just the capabilities needed to set the timer and get real-time scheduling, but not the full set of root privileges. This greatly reduces the amount of damage that can be done. However, it is not used by default, since it requires a kernel modification. To enable givertcap, simply call ./configure --enable-rtcap before compiling. (The givertcap code is part of the MusE distribution.) With current kernels, you need to apply a little patch to the kernel headers: Go to /usr/src/linux/include/linux (or wherever you have your kernel sources) and in the file capability.h change the line #define CAP_INIT_EFF_SET to_cap_t(~0&~CAP_TO_MASK(CAP_SETPCAP)) to #define CAP_INIT_EFF_SET to_cap_t( ~0 ) and the line #define CAP_INIT_INH_SET to_cap_t(0) to #define CAP_INIT_INH_SET to_cap_t( ~0 ) . You must then recompile your kernel. In this setup, givertcap must be set suid root, but MusE can be run with normal privileges. Now all possible suid exploits described above apply to givertcap, but since it is such a tiny program, it can be checked for exploits far more easily and can be considered reasonably secure. Unfortunately, givertcap can be used to grant real-time privileges to *any* program, so it's an easy way to have the machine clogged up by a malicious user who might run bogus tasks at 100% system usage. Therefore, you *must* create an extra group for it (called "musers" in this example): # chown root:musers givertcap # chmod 4750 givertcap Do not forget to remove the suid bit on muse afterwards by doing # chmod 755 muse . For more information about givertcap and kernel capabilities, see http://www.tml.hut.fi/~tilmonen/givertcap/ and http://ftp.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.4/capfaq-0.2. txt. Further reading: ================ General Linux system security: http://linuxdoc.org/HOWTO/Security-Quickstart-HOWTO/ http://linuxdoc.org/HOWTO/Security-HOWTO.html Secure Linux programming: http://linuxdoc.org/HOWTO/Secure-Programs-HOWTO/ Permissions: man chmod man chattr givertcap: http://www.tml.hut.fi/~tilmonen/givertcap/ An alternative approach, using a kernel module: http://arctrix.com/nas/linux/capwrap.tar.gz Kernel capabilities: http://ftp.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.4/capfaq-0.2. txt Adding capability flags to ELF files: http://atrey.karlin.mff.cuni.cz/~pavel/elfcap.html Buffer Overflow attacks: "Smashing the stack for fun and profit" by AlephOne 1996, published in Phrack magazine, issue 49 http://www.phrack.com/show.php?p=49&a=14 In the MusE source, app.cpp contains the invocation of givertcap and the dropping of the suid privileges: grep for "getCapabilities" and "setuid" to see how it's done. ________________________________________________________________________________ This document was written by Jörn Nettingsmeier Corrections and improvements welcome. Thanks to Werner Schweer and Tommi Ilmonen for answering my questions. Last updated 02/22/2002. muse-4.0.0/src/al/000077500000000000000000000000001404105311000136345ustar00rootroot00000000000000muse-4.0.0/src/al/CMakeLists.txt000066400000000000000000000034201404105311000163730ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB al_source_files al.cpp dsp.cpp sig.cpp xml.cpp ) if (USE_SSE) file (GLOB al_source_files ${al_source_files} dspSSE.cpp dspXMM.cpp ) endif (USE_SSE) ## ## Define target ## add_library(al ${MODULES_BUILD} ${al_source_files} ) ## ## Compilation flags and target name ## set_target_properties( al PROPERTIES OUTPUT_NAME muse_al ) set_source_files_properties( dspSSE.cpp PROPERTIES COMPILE_FLAGS "-x assembler" ) ## ## Linkage ## target_link_libraries ( al ${QT_LIBRARIES} ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS al DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/al/al.cpp000066400000000000000000000023151404105311000147350ustar00rootroot00000000000000//============================================================================= // AL // Audio Utility Library // $Id: al.cpp,v 1.1.2.2 2009/12/06 01:39:33 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "al.h" namespace AL { int sampleRate = 44100; int mtcType = 0; int division = 384; bool debugMsg = false; float denormalBias = 1e-18; } muse-4.0.0/src/al/al.h000066400000000000000000000023501404105311000144010ustar00rootroot00000000000000//============================================================================= // AL // Audio Utility Library // $Id: al.h,v 1.1.2.2 2009/12/06 01:39:33 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AL_H__ #define __AL_H__ namespace AL { extern int sampleRate; extern int mtcType; extern int division; extern bool debugMsg; extern float denormalBias; } #endif muse-4.0.0/src/al/dsp.cpp000066400000000000000000000156531404105311000151400ustar00rootroot00000000000000//============================================================================= // AL // Audio Utility Library // $Id: dsp.cpp,v 1.1.2.1 2009/12/06 01:39:33 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include #include #include "config.h" #include "al.h" #include "dsp.h" namespace AL { Dsp* dsp = 0; #ifdef __i386__ //--------------------------------------------------------- // DspSSE86 //--------------------------------------------------------- extern "C" { extern float x86_sse_compute_peak(float*, unsigned, float); extern void x86_sse_apply_gain_to_buffer(float*, unsigned, float); extern void x86_sse_mix_buffers_with_gain(float*, float*, unsigned, float); extern void x86_sse_mix_buffers_no_gain(float*, float*, unsigned); }; class DspSSE86 : public Dsp { public: DspSSE86() {} virtual ~DspSSE86() {} virtual float peak(float* buf, unsigned n, float current) { if ( ((intptr_t)buf % 16) != 0) { fprintf(stderr, "peak(): buffer unaligned! (%p)\n", buf); return Dsp::peak(buf, n, current); } return x86_sse_compute_peak(buf, n, current); } virtual void applyGainToBuffer(float* buf, unsigned n, float gain) { if ( ((intptr_t)buf % 16) != 0) { fprintf(stderr, "applyGainToBuffer(): buffer unaligned! (%p)\n", buf); Dsp::applyGainToBuffer(buf, n, gain); } else x86_sse_apply_gain_to_buffer(buf, n, gain); } virtual void mixWithGain(float* dst, float* src, unsigned n, float gain) { if ( ((intptr_t)dst & 15) != 0) fprintf(stderr, "mixWithGainain(): dst unaligned! (%p)\n", dst); if (((intptr_t)dst & 15) != ((intptr_t)src & 15) ) { fprintf(stderr, "mixWithGain(): dst & src don't have the same alignment!\n"); Dsp::mixWithGain(dst, src,n, gain); } else x86_sse_mix_buffers_with_gain(dst, src, n, gain); } virtual void mix(float* dst, float* src, unsigned n) { if ( ((intptr_t)dst & 15) != 0) fprintf(stderr, "mix_buffers_no_gain(): dst unaligned! %p\n", dst); if ( ((intptr_t)dst & 15) != ((intptr_t)src & 15) ) { fprintf(stderr, "mix_buffers_no_gain(): dst & src don't have the same alignment!\n"); Dsp::mix(dst, src, n); } else x86_sse_mix_buffers_no_gain(dst, src, n); } }; #endif //--------------------------------------------------------- // initDsp //--------------------------------------------------------- void initDsp() { #if 0 // Disabled for now. #if defined(__i386__) || defined(__x86_64__) if(debugMsg) printf("Muse: __i386__ or __x86_64__ defined. Using optimized float buffer copying (asm movsl).\n"); #else if(debugMsg) printf("Muse: __i386__ or __x86_64__ not defined. Using non-optimized memcpy for float buffer copying.\n"); #endif #endif #if defined(__i386__) && defined(USE_SSE) unsigned long useSSE = 0; if(debugMsg) printf("initDsp: __i386__ and USE_SSE defined\n"); // FIXME: 64? Shouldn't these routines work on 32 bit? #ifdef __x86_64__ useSSE = 1 << 25; // we know the platform has SSE if(debugMsg) printf("initDsp: __x86_64__ defined\n"); #else if(debugMsg) printf("initDsp: getting cpuid via asm\n"); asm ( "mov $1, %%eax\n" "pushl %%ebx\n" "cpuid\n" "movl %%edx, %0\n" "popl %%ebx\n" : "=r" (useSSE) : : "%eax", "%ecx", "%edx", "memory"); #endif if(debugMsg) printf("initDsp: checking for bit 25 SSE support:%lX\n", useSSE); useSSE &= (1 << 25); // bit 25 = SSE support if (useSSE) { printf("Using SSE optimized routines\n"); dsp = new DspSSE86(); return; } // fall through to not hardware optimized routines #endif if(debugMsg) printf("Muse: using unoptimized non-SSE dsp routines\n"); dsp = new Dsp(); } //--------------------------------------------------------- // exitDsp //--------------------------------------------------------- void exitDsp() { if(dsp) delete dsp; dsp = 0; } void Dsp::clear(float* dst, unsigned n, bool addDenormal) { if(addDenormal) { for(unsigned i = 0; i < n; ++i) dst[i] = denormalBias; } else { memset(dst, 0, n * sizeof(float)); } } void Dsp::cpy(float* dst, float* src, unsigned n, bool addDenormal) { // FIXME: Changed by T356. Not defined. Where are these??? //#if defined(ARCH_X86) || defined(ARCH_X86_64) ///#if defined(__i386__) || defined(__x86_64__) #if 0 // Disabled for now. //printf("Dsp: using asm cpy\n"); // Changed by T356. Get To and From not declared in scope compile errors. //register unsigned long int dummy; //__asm__ __volatile__ ("rep; movsl" :"=&D"(dst), "=&S"(src), "=&c"(dummy) :"0" (to), "1" (from),"2" (n) : "memory"); // FIXME: FIXME: I don't think this is correct but it works so far... // Tried clobbering, get "Can't find a register in class `CREG' while reloading `asm'" __asm__ __volatile__ ( "cld\n\t" "rep\n\t" "movsl" : : "S" (src), "D" (dst), "c" (n) : "memory" ); //: "%ecx", "%esi", "%edi", "memory" #else if(addDenormal) { for(unsigned i = 0; i < n; ++i) dst[i] = src[i] + denormalBias; } else { //printf("Dsp: using memcpy\n"); memcpy(dst, src, sizeof(float) * n); } #endif } } // namespace AL muse-4.0.0/src/al/dsp.h000066400000000000000000000075051404105311000146020ustar00rootroot00000000000000//============================================================================= // AL // Audio Utility Library // $Id: dsp.h,v 1.1.2.1 2009/12/06 01:39:33 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __DSP_H__ #define __DSP_H__ #include #include "muse_math.h" namespace AL { //--------------------------------------------------------- // f_max //--------------------------------------------------------- static inline float f_max(float x, float a) { x -= a; x += fabsf(x); x *= 0.5f; x += a; return x; } //--------------------------------------------------------- // Dsp // standard version of all dsp routines without any // hw acceleration //--------------------------------------------------------- class Dsp { public: Dsp() {} virtual ~Dsp() {} virtual void clear(float* dst, unsigned n, bool addDenormal = false); virtual float peak(float* buf, unsigned n, float current) { for (unsigned i = 0; i < n; ++i) current = f_max(current, fabsf(buf[i])); return current; } virtual void applyGainToBuffer(float* buf, unsigned n, float gain) { for (unsigned i = 0; i < n; ++i) buf[i] *= gain; } virtual void mixWithGain(float* dst, float* src, unsigned n, float gain) { for (unsigned i = 0; i < n; ++i) dst[i] += src[i] * gain; } virtual void mix(float* dst, float* src, unsigned n) { for (unsigned i = 0; i < n; ++i) dst[i] += src[i]; } virtual void cpy(float* dst, float* src, unsigned n, bool addDenormal = false); /* { // Changed by T356. Not defined. Where are these??? //#if defined(ARCH_X86) || defined(ARCH_X86_64) #if defined(__i386__) || defined(__x86_64__) printf("Dsp: using asm cpy\n"); // Changed by T356. Get To and From not declared in scope compile errors. register unsigned long int dummy; //__asm__ __volatile__ ("rep; movsl" :"=&D"(dst), "=&S"(src), "=&c"(dummy) :"0" (to), "1" (from),"2" (n) : "memory"); // From http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html __asm__ __volatile__ ( \ "cld\n\t" \ "rep\n\t" \ "movsl" \ : \ : "S" (src), "D" (dst), "c" (n) \ : "memory" ); //: "%ecx", "%esi", "%edi", "memory" #else printf("Dsp: using memcpy\n"); memcpy(dst, src, sizeof(float) * n); #endif } */ }; extern void initDsp(); extern void exitDsp(); extern Dsp* dsp; } #endif muse-4.0.0/src/al/dspSSE.cpp000066400000000000000000000251251404105311000155060ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dspSSE.cpp,v 1.1.2.3 2009/12/20 00:04:25 spamatica Exp $ // // (C) Copyright 2007-2009 Werner Schweer (ws@seh.de) // file originally from Ardour DAW project by Paul Davis (c) 2005 // licensed through GPL // Original author Sampo Savolainen // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //====================================================================== #; void x86_sse_mix_buffers_with_gain (float *dst, float *src, long nframes, float gain); .globl x86_sse_mix_buffers_with_gain .type x86_sse_mix_buffers_with_gain,@function x86_sse_mix_buffers_with_gain: #; 8(%ebp) = float *dst = %edi #; 12(%ebp) = float *src = %esi #; 16(%ebp) = long nframes = %ecx #; 20(%ebp) = float gain = st(0) pushl %ebp movl %esp, %ebp #; save the registers #; pushl %eax pushl %ebx #; pushl %ecx pushl %edi pushl %esi #; if nframes == 0, go to end movl 16(%ebp), %ecx #; nframes cmp $0, %ecx je .MBWG_END #; Check for alignment movl 8(%ebp), %edi #; dst movl 12(%ebp), %esi #; src movl %edi, %eax andl $12, %eax #; mask alignment offset movl %esi, %ebx andl $12, %ebx #; mask alignment offset cmp %eax, %ebx jne .MBWG_NONALIGN #; if not aligned, calculate manually #; if we are aligned cmp $0, %ebx jz .MBWG_SSE #; Pre-loop, we need to run 1-3 frames "manually" without #; SSE instructions movss 20(%ebp), %xmm1 #; xmm1 .MBWG_PRELOOP: movss (%esi), %xmm0 mulss %xmm1, %xmm0 addss (%edi), %xmm0 movss %xmm0, (%edi) addl $4, %edi #; dst++ addl $4, %esi #; src++ decl %ecx #; nframes-- jz .MBWG_END #; cmp $0, %ecx #; je .MBWG_END #; if we run out of frames, go to end addl $4, %ebx cmp $16, %ebx #; test if we've reached 16 byte alignment jne .MBWG_PRELOOP .MBWG_SSE: cmp $4, %ecx #; we know it's not zero, but if it's not >=4, then jnge .MBWG_NONALIGN #; we jump straight to the "normal" code #; copy gain to fill %xmm1 movss 20(%ebp), %xmm1 shufps $0x00, %xmm1, %xmm1 .MBWG_SSELOOP: movaps (%esi), %xmm0 #; source => xmm0 mulps %xmm1, %xmm0 #; apply gain to source addps (%edi), %xmm0 #; mix with destination movaps %xmm0, (%edi) #; copy result to destination addl $16, %edi #; dst+=4 addl $16, %esi #; src+=4 subl $4, %ecx #; nframes-=4 cmp $4, %ecx jge .MBWG_SSELOOP cmp $0, %ecx je .MBWG_END #; if there are remaining frames, the nonalign code will do nicely #; for the rest 1-3 frames. .MBWG_NONALIGN: #; not aligned! movss 20(%ebp), %xmm1 #; gain => xmm1 .MBWG_NONALIGNLOOP: movss (%esi), %xmm0 mulss %xmm1, %xmm0 addss (%edi), %xmm0 movss %xmm0, (%edi) addl $4, %edi addl $4, %esi decl %ecx jnz .MBWG_NONALIGNLOOP .MBWG_END: popl %esi popl %edi #; popl %ecx popl %ebx #; popl %eax #; return leave ret .size x86_sse_mix_buffers_with_gain, .-x86_sse_mix_buffers_with_gain #; void x86_sse_mix_buffers_no_gain (float *dst, float *src, long nframes); .globl x86_sse_mix_buffers_no_gain .type x86_sse_mix_buffers_no_gain,@function x86_sse_mix_buffers_no_gain: #; 8(%ebp) = float *dst = %edi #; 12(%ebp) = float *src = %esi #; 16(%ebp) = long nframes = %ecx pushl %ebp movl %esp, %ebp #; save the registers #; pushl %eax pushl %ebx #; pushl %ecx pushl %edi pushl %esi #; the real function #; if nframes == 0, go to end movl 16(%ebp), %ecx #; nframes cmp $0, %ecx je .MBNG_END #; Check for alignment movl 8(%ebp), %edi #; dst movl 12(%ebp), %esi #; src movl %edi, %eax andl $12, %eax #; mask alignment offset movl %esi, %ebx andl $12, %ebx #; mask alignment offset cmp %eax, %ebx jne .MBNG_NONALIGN #; if not aligned, calculate manually cmp $0, %ebx je .MBNG_SSE #; Pre-loop, we need to run 1-3 frames "manually" without #; SSE instructions .MBNG_PRELOOP: movss (%esi), %xmm0 addss (%edi), %xmm0 movss %xmm0, (%edi) addl $4, %edi #; dst++ addl $4, %esi #; src++ decl %ecx #; nframes-- jz .MBNG_END addl $4, %ebx cmp $16, %ebx #; test if we've reached 16 byte alignment jne .MBNG_PRELOOP .MBNG_SSE: cmp $4, %ecx #; if there are frames left, but less than 4 jnge .MBNG_NONALIGN #; we can't run SSE .MBNG_SSELOOP: movaps (%esi), %xmm0 #; source => xmm0 addps (%edi), %xmm0 #; mix with destination movaps %xmm0, (%edi) #; copy result to destination addl $16, %edi #; dst+=4 addl $16, %esi #; src+=4 subl $4, %ecx #; nframes-=4 cmp $4, %ecx jge .MBNG_SSELOOP cmp $0, %ecx je .MBNG_END #; if there are remaining frames, the nonalign code will do nicely #; for the rest 1-3 frames. .MBNG_NONALIGN: #; not aligned! movss (%esi), %xmm0 #; src => xmm0 addss (%edi), %xmm0 #; xmm0 += dst movss %xmm0, (%edi) #; xmm0 => dst addl $4, %edi addl $4, %esi decl %ecx jnz .MBNG_NONALIGN .MBNG_END: popl %esi popl %edi #; popl %ecx popl %ebx #; popl %eax #; return leave ret .size x86_sse_mix_buffers_no_gain, .-x86_sse_mix_buffers_no_gain #; void x86_sse_apply_gain_to_buffer (float *buf, long nframes, float gain); .globl x86_sse_apply_gain_to_buffer .type x86_sse_apply_gain_to_buffer,@function x86_sse_apply_gain_to_buffer: #; 8(%ebp) = float *buf = %edi #; 12(%ebp) = long nframes = %ecx #; 16(%ebp) = float gain = st(0) pushl %ebp movl %esp, %ebp #; save %edi pushl %edi #; the real function #; if nframes == 0, go to end movl 12(%ebp), %ecx #; nframes cmp $0, %ecx je .AG_END #; create the gain buffer in %xmm1 movss 16(%ebp), %xmm1 shufps $0x00, %xmm1, %xmm1 #; Check for alignment movl 8(%ebp), %edi #; buf movl %edi, %edx #; buf => %edx andl $12, %edx #; mask bits 1 & 2, result = 0, 4, 8 or 12 jz .AG_SSE #; if buffer IS aligned #; PRE-LOOP #; we iterate 1-3 times, doing normal x87 float comparison #; so we reach a 16 byte aligned "buf" (=%edi) value .AGLP_START: #; Load next value from the buffer movss (%edi), %xmm0 mulss %xmm1, %xmm0 movss %xmm0, (%edi) #; increment buffer, decrement counter addl $4, %edi #; buf++; decl %ecx #; nframes-- jz .AG_END #; if we run out of frames, we go to the end addl $4, %edx #; one non-aligned byte less cmp $16, %edx jne .AGLP_START #; if more non-aligned frames exist, we do a do-over .AG_SSE: #; We have reached the 16 byte aligned "buf" ("edi") value #; Figure out how many loops we should do movl %ecx, %eax #; copy remaining nframes to %eax for division movl $0, %edx #; 0 the edx register pushl %edi movl $4, %edi divl %edi #; %edx = remainder == 0 popl %edi #; %eax = SSE iterations cmp $0, %eax je .AGPOST_START .AGLP_SSE: movaps (%edi), %xmm0 mulps %xmm1, %xmm0 movaps %xmm0, (%edi) addl $16, %edi #; subl $4, %ecx #; nframes-=4 decl %eax jnz .AGLP_SSE #; Next we need to post-process all remaining frames #; the remaining frame count is in %ecx #; if no remaining frames, jump to the end #; cmp $0, %ecx andl $3, %ecx #; nframes % 4 je .AG_END .AGPOST_START: movss (%edi), %xmm0 mulss %xmm1, %xmm0 movss %xmm0, (%edi) #; increment buffer, decrement counter addl $4, %edi #; buf++; decl %ecx #; nframes-- jnz .AGPOST_START #; if we run out of frames, we go to the end .AG_END: popl %edi #; return leave ret .size x86_sse_apply_gain_to_buffer, .-x86_sse_apply_gain_to_buffer #; end proc #; float x86_sse_compute_peak(float *buf, long nframes, float current); .globl x86_sse_compute_peak .type x86_sse_compute_peak,@function x86_sse_compute_peak: #; 8(%ebp) = float *buf = %edi #; 12(%ebp) = long nframes = %ecx #; 16(%ebp) = float current = st(0) pushl %ebp movl %esp, %ebp #; save %edi pushl %edi #; the real function #; Load "current" in xmm0 movss 16(%ebp), %xmm0 #; if nframes == 0, go to end movl 12(%ebp), %ecx #; nframes cmp $0, %ecx je .CP_END #; create the "abs" mask in %xmm2 pushl $2147483647 movss (%esp), %xmm2 addl $4, %esp shufps $0x00, %xmm2, %xmm2 #; Check for alignment movl 8(%ebp), %edi #; buf movl %edi, %edx #; buf => %edx andl $12, %edx #; mask bits 1 & 2, result = 0, 4, 8 or 12 jz .CP_SSE #; if buffer IS aligned #; PRE-LOOP #; we iterate 1-3 times, doing normal x87 float comparison #; so we reach a 16 byte aligned "buf" (=%edi) value .LP_START: #; Load next value from the buffer movss (%edi), %xmm1 andps %xmm2, %xmm1 maxss %xmm1, %xmm0 #; increment buffer, decrement counter addl $4, %edi #; buf++; decl %ecx #; nframes-- jz .CP_END #; if we run out of frames, we go to the end addl $4, %edx #; one non-aligned byte less cmp $16, %edx jne .LP_START #; if more non-aligned frames exist, we do a do-over .CP_SSE: #; We have reached the 16 byte aligned "buf" ("edi") value #; Figure out how many loops we should do movl %ecx, %eax #; copy remaining nframes to %eax for division shr $2,%eax #; unsigned divide by 4 jz .POST_START #; %eax = SSE iterations #; current maximum is at %xmm0, but we need to .. shufps $0x00, %xmm0, %xmm0 #; shuffle "current" to all 4 FP's #;prefetcht0 16(%edi) .LP_SSE: movaps (%edi), %xmm1 andps %xmm2, %xmm1 maxps %xmm1, %xmm0 addl $16, %edi decl %eax jnz .LP_SSE #; Calculate the maximum value contained in the 4 FP's in %xmm0 movaps %xmm0, %xmm1 shufps $0x4e, %xmm1, %xmm1 #; shuffle left & right pairs (1234 => 3412) maxps %xmm1, %xmm0 #; maximums of the two pairs movaps %xmm0, %xmm1 shufps $0xb1, %xmm1, %xmm1 #; shuffle the floats inside the two pairs (1234 => 2143) maxps %xmm1, %xmm0 #; now every float in %xmm0 is the same value, current maximum value #; Next we need to post-process all remaining frames #; the remaining frame count is in %ecx #; if no remaining frames, jump to the end andl $3, %ecx #; nframes % 4 jz .CP_END .POST_START: movss (%edi), %xmm1 andps %xmm2, %xmm1 maxss %xmm1, %xmm0 addl $4, %edi #; buf++; decl %ecx #; nframes--; jnz .POST_START .CP_END: #; Load the value from xmm0 to the float stack for returning movss %xmm0, 16(%ebp) flds 16(%ebp) popl %edi #; return leave ret .size x86_sse_compute_peak, .-x86_sse_compute_peak #; end proc #ifdef __ELF__ .section .note.GNU-stack,"",%progbits #endif muse-4.0.0/src/al/dspXMM.cpp000066400000000000000000000072451404105311000155200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dspXMM.cpp,v 1.1.2.2 2009/12/20 00:04:25 spamatica Exp $ // // (C) Copyright 2007-2009 Werner Schweer (ws@seh.de) // file originally from Ardour DAW project by Paul Davis (c) 2007 // licensed through GPL // Original author Sampo Savolainen // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //====================================================================== #include void x86_sse_find_peaks(float *buf, unsigned nframes, float *min, float *max) { __m128 current_max, current_min, work; // Load max and min values into all four slots of the XMM registers current_min = _mm_set1_ps(*min); current_max = _mm_set1_ps(*max); // Work input until "buf" reaches 16 byte alignment while ( ((unsigned long)buf) % 16 != 0 && nframes > 0) { // Load the next float into the work buffer work = _mm_set1_ps(*buf); current_min = _mm_min_ps(current_min, work); current_max = _mm_max_ps(current_max, work); buf++; nframes--; } // use 64 byte prefetch for quadruple quads while (nframes >= 16) { __builtin_prefetch(buf+64,0,0); work = _mm_load_ps(buf); current_min = _mm_min_ps(current_min, work); current_max = _mm_max_ps(current_max, work); buf+=4; work = _mm_load_ps(buf); current_min = _mm_min_ps(current_min, work); current_max = _mm_max_ps(current_max, work); buf+=4; work = _mm_load_ps(buf); current_min = _mm_min_ps(current_min, work); current_max = _mm_max_ps(current_max, work); buf+=4; work = _mm_load_ps(buf); current_min = _mm_min_ps(current_min, work); current_max = _mm_max_ps(current_max, work); buf+=4; nframes-=16; } // work through aligned buffers while (nframes >= 4) { work = _mm_load_ps(buf); current_min = _mm_min_ps(current_min, work); current_max = _mm_max_ps(current_max, work); buf+=4; nframes-=4; } // work through the rest < 4 samples while ( nframes > 0) { // Load the next float into the work buffer work = _mm_set1_ps(*buf); current_min = _mm_min_ps(current_min, work); current_max = _mm_max_ps(current_max, work); buf++; nframes--; } // Find min & max value in current_max through shuffle tricks work = current_min; work = _mm_shuffle_ps(work, work, _MM_SHUFFLE(2, 3, 0, 1)); work = _mm_min_ps (work, current_min); current_min = work; work = _mm_shuffle_ps(work, work, _MM_SHUFFLE(1, 0, 3, 2)); work = _mm_min_ps (work, current_min); _mm_store_ss(min, work); work = current_max; work = _mm_shuffle_ps(work, work, _MM_SHUFFLE(2, 3, 0, 1)); work = _mm_max_ps (work, current_max); current_max = work; work = _mm_shuffle_ps(work, work, _MM_SHUFFLE(1, 0, 3, 2)); work = _mm_max_ps (work, current_max); _mm_store_ss(max, work); } muse-4.0.0/src/al/sig.cpp000066400000000000000000000313731404105311000151310ustar00rootroot00000000000000//============================================================================= // AL // Audio Utility Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "al.h" #include "sig.h" #include namespace AL { SigList sigmap; //--------------------------------------------------------- // isValid //--------------------------------------------------------- bool TimeSignature::isValid() const { if((z < 1) || (z > 63)) return false; switch(n) { case 1: case 2: case 3: case 4: case 8: case 16: case 32: case 64: case 128: return true; default: return false; } } //--------------------------------------------------------- // SigList //--------------------------------------------------------- SigList::SigList() { insert(std::pair (MAX_TICK, new SigEvent(TimeSignature(4, 4), 0))); } SigList::~SigList() { for (iSigEvent i = begin(); i != end(); ++i) delete i->second; } //--------------------------------------------------------- // add // signatures are only allowed at the beginning of // a bar //--------------------------------------------------------- void SigList::add(unsigned tick, const TimeSignature& s) { if (s.z == 0 || s.n == 0) { fprintf(stderr, "illegal signature %d/%d\n", s.z, s.n); return; } tick = raster1(tick, 0); iSigEvent e = upper_bound(tick); if(e == end()) { fprintf(stderr, "SigList::add Signal not found tick:%d\n", tick); return; } if (tick == e->second->tick) { e->second->sig = s; } else { SigEvent* ne = e->second; SigEvent* ev = new SigEvent(ne->sig, ne->tick); ne->sig = s; ne->tick = tick; insert(std::pair (tick, ev)); } normalize(); } void SigList::add(unsigned tick, SigEvent* e, bool do_normalize) { TimeSignature ts = e->sig; std::pair res = insert(std::pair (tick, e)); if(!res.second) { fprintf(stderr, "SigList::add insert failed: siglist:%p sig:%p %d/%d tick:%d\n", this, e, ts.z, ts.n, e->tick); } else { iSigEvent ise = res.first; ++ise; // There is always a 'next' sig event - there is always one at index MAX_TICK. SigEvent* ne = ise->second; // Swap the values. (This is how the sig list works.) e->sig = ne->sig; e->tick = ne->tick; ne->sig = ts; ne->tick = tick; if(do_normalize) normalize(); } } //--------------------------------------------------------- // del //--------------------------------------------------------- void SigList::del(unsigned tick) { // printf("SigList::del(%d)\n", tick); iSigEvent e = find(tick); if (e == end()) { fprintf(stderr, "SigList::del(%d): not found\n", tick); return; } iSigEvent ne = e; ++ne; if (ne == end()) { fprintf(stderr, "SigList::del() next event not found!\n"); return; } ne->second->sig = e->second->sig; ne->second->tick = e->second->tick; erase(e); normalize(); } void SigList::del(iSigEvent e, bool do_normalize) { iSigEvent ne = e; ++ne; if (ne == end()) { fprintf(stderr, "SigList::del() HALLO\n"); return; } ne->second->sig = e->second->sig; ne->second->tick = e->second->tick; erase(e); if(do_normalize) normalize(); } //--------------------------------------------------------- // SigList::normalize //--------------------------------------------------------- void SigList::normalize() { TimeSignature sig(0, 0); unsigned tick = 0; iSigEvent ee; for (iSigEvent e = begin(); e != end();) { if (sig.z == e->second->sig.z && sig.n == e->second->sig.n) { e->second->tick = tick; erase(ee); } sig = e->second->sig; ee = e; tick = e->second->tick; ++e; } int bar = 0; for (iSigEvent e = begin(); e != end();) { e->second->bar = bar; int delta = e->first - e->second->tick; int ticksB = ticks_beat(e->second->sig.n); int ticksM = ticksB * e->second->sig.z; bar += delta / ticksM; if (delta % ticksM) // Teil eines Taktes ++bar; ++e; } } //--------------------------------------------------------- // SigList::dump //--------------------------------------------------------- void SigList::dump() const { fprintf(stderr, "\nSigList:\n"); for (ciSigEvent i = begin(); i != end(); ++i) { fprintf(stderr, "%6d %06d Bar %3d %02d/%d\n", i->first, i->second->tick, i->second->bar, i->second->sig.z, i->second->sig.n); } } void SigList::clear() { for (iSigEvent i = begin(); i != end(); ++i) delete i->second; SIGLIST::clear(); insert(std::pair (MAX_TICK, new SigEvent(TimeSignature(4, 4), 0))); } //--------------------------------------------------------- // ticksMeasure //--------------------------------------------------------- int SigList::ticksMeasure(const TimeSignature& sig) const { return ticks_beat(sig.n) * sig.z; } int SigList::ticksMeasure(int Z, int N) const { return ticks_beat(N) * Z; } int SigList::ticksMeasure(unsigned tick) const { ciSigEvent i = upper_bound(tick); if (i == end()) { fprintf(stderr, "ticksMeasure: not found %d\n", tick); return 0; } return ticksMeasure(i->second->sig); } //--------------------------------------------------------- // ticksBeat //--------------------------------------------------------- int SigList::ticksBeat(unsigned tick) const { ciSigEvent i = upper_bound(tick); if(i == end()) { fprintf(stderr, "SigList::ticksBeat event not found! tick:%d\n", tick); return 0; } return ticks_beat(i->second->sig.n); } int SigList::ticks_beat(int n) const { int m = division; switch (n) { case 1: m <<= 2; break; // 1536 case 2: m <<= 1; break; // 768 case 3: m += m >> 1; break; // 384+192 case 4: break; // 384 case 8: m >>= 1; break; // 192 case 16: m >>= 2; break; // 96 case 32: m >>= 3; break; // 48 case 64: m >>= 4; break; // 24 case 128: m >>= 5; break; // 12 default: break; } return m; } //--------------------------------------------------------- // timesig //--------------------------------------------------------- TimeSignature SigList::timesig(unsigned tick) const { ciSigEvent i = upper_bound(tick); if (i == end()) { fprintf(stderr, "timesig(%d): not found\n", tick); return TimeSignature(4,4); } return i->second->sig; } void SigList::timesig(unsigned tick, int& z, int& n) const { ciSigEvent i = upper_bound(tick); if (i == end()) { fprintf(stderr, "timesig(%d): not found\n", tick); z = 4; n = 4; } else { z = i->second->sig.z; n = i->second->sig.n; } } //--------------------------------------------------------- // tickValues //--------------------------------------------------------- void SigList::tickValues(unsigned t, int* bar, int* beat, unsigned* tick) const { ciSigEvent e = upper_bound(t); if (e == end()) { fprintf(stderr, "tickValues(0x%x) not found(%zd)\n", t, size()); *bar = 0; *beat = 0; *tick = 0; return; } int delta = t - e->second->tick; int ticksB = ticks_beat(e->second->sig.n); int ticksM = ticksB * e->second->sig.z; *bar = e->second->bar + delta / ticksM; int rest = delta % ticksM; *beat = rest / ticksB; *tick = rest % ticksB; } //--------------------------------------------------------- // bar2tick //--------------------------------------------------------- unsigned SigList::bar2tick(int bar, int beat, unsigned tick) const { ciSigEvent e; if (bar < 0) bar = 0; for (e = begin(); e != end();) { ciSigEvent ee = e; ++ee; if (ee == end()) break; if (bar < ee->second->bar) break; e = ee; } int ticksB = ticks_beat(e->second->sig.n); int ticksM = ticksB * e->second->sig.z; return e->second->tick + (bar-e->second->bar)*ticksM + ticksB*beat + tick; } //--------------------------------------------------------- // raster //--------------------------------------------------------- unsigned SigList::raster(unsigned t, int raster) const { if (raster == 1) return t; ciSigEvent e = upper_bound(t); if (e == end()) { fprintf(stderr, "SigList::raster(%x,)\n", t); return t; } int delta = t - e->second->tick; int ticksM = ticks_beat(e->second->sig.n) * e->second->sig.z; if (raster == 0) raster = ticksM; int rest = delta % ticksM; int bb = (delta/ticksM)*ticksM; return e->second->tick + bb + ((rest + raster/2)/raster)*raster; } //--------------------------------------------------------- // raster1 // round down //--------------------------------------------------------- unsigned SigList::raster1(unsigned t, int raster) const { if (raster == 1) return t; ciSigEvent e = upper_bound(t); if(e == end()) { fprintf(stderr, "SigList::raster1 event not found tick:%d\n", t); return t; } int delta = t - e->second->tick; int ticksM = ticks_beat(e->second->sig.n) * e->second->sig.z; if (raster == 0) raster = ticksM; int rest = delta % ticksM; int bb = (delta/ticksM)*ticksM; return e->second->tick + bb + (rest/raster)*raster; } //--------------------------------------------------------- // raster2 // round up //--------------------------------------------------------- unsigned SigList::raster2(unsigned t, int raster) const { if (raster == 1) return t; ciSigEvent e = upper_bound(t); if(e == end()) { fprintf(stderr, "SigList::raster2 event not found tick:%d\n", t); //return 0; return t; } int delta = t - e->second->tick; int ticksM = ticks_beat(e->second->sig.n) * e->second->sig.z; if (raster == 0) raster = ticksM; int rest = delta % ticksM; int bb = (delta/ticksM)*ticksM; return e->second->tick + bb + ((rest+raster-1)/raster)*raster; } //--------------------------------------------------------- // rasterStep //--------------------------------------------------------- int SigList::rasterStep(unsigned t, int raster) const { if (raster == 0) { ciSigEvent e = upper_bound(t); if(e == end()) { fprintf(stderr, "SigList::rasterStep event not found tick:%d\n", t); //return 0; return raster; } return ticks_beat(e->second->sig.n) * e->second->sig.z; } return raster; } } // namespace AL muse-4.0.0/src/al/sig.h000066400000000000000000000065051404105311000145750ustar00rootroot00000000000000//============================================================================= // AL // Audio Utility Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __SIG_H__ #define __SIG_H__ #include namespace AL { #ifndef MAX_TICK #define MAX_TICK (0x7fffffff/100) #endif //--------------------------------------------------------- // TimeSignature //--------------------------------------------------------- struct TimeSignature { int z, n; TimeSignature() { z = 4; n = 4; } TimeSignature(int a, int b) { z = a; n = b; } bool isValid() const; }; //--------------------------------------------------------- // Signature Event //--------------------------------------------------------- struct SigEvent { TimeSignature sig; unsigned tick; // signature valid from this position int bar; // precomputed SigEvent() { } SigEvent(const TimeSignature& s, unsigned tk) { sig = s; tick = tk; bar = 0; } }; //--------------------------------------------------------- // SigList //--------------------------------------------------------- typedef std::map > SIGLIST; typedef SIGLIST::iterator iSigEvent; typedef SIGLIST::const_iterator ciSigEvent; typedef SIGLIST::reverse_iterator riSigEvent; typedef SIGLIST::const_reverse_iterator criSigEvent; class SigList : public SIGLIST { int ticksMeasure(const TimeSignature&) const; int ticksMeasure(int z, int n) const; public: SigList(); ~SigList(); void clear(); void add(unsigned tick, const TimeSignature& s); void add(unsigned tick, SigEvent* e, bool do_normalize = true); void del(unsigned tick); void del(iSigEvent, bool do_normalize = true); void normalize(); void dump() const; int ticks_beat(int N) const; TimeSignature timesig(unsigned tick) const; void timesig(unsigned tick, int& z, int& n) const; void tickValues(unsigned t, int* bar, int* beat, unsigned* tick) const; unsigned bar2tick(int bar, int beat, unsigned tick) const; int ticksMeasure(unsigned tick) const; int ticksBeat(unsigned tick) const; unsigned raster(unsigned tick, int raster) const; unsigned raster1(unsigned tick, int raster) const; // round down unsigned raster2(unsigned tick, int raster) const; // round up int rasterStep(unsigned tick, int raster) const; }; extern SigList sigmap; } #endif muse-4.0.0/src/al/xml.cpp000066400000000000000000000271401404105311000151440ustar00rootroot00000000000000//============================================================================= // AL // Audio Utility Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "xml.h" #include "al.h" #include #include #include namespace AL { //--------------------------------------------------------- // Xml //--------------------------------------------------------- Xml::Xml() { level = 0; } Xml::Xml(QIODevice* device) : QTextStream(device) { setCodec("utf8"); level = 0; } //--------------------------------------------------------- // putLevel //--------------------------------------------------------- void Xml::putLevel() { for (int i = 0; i < level*2; ++i) *this << ' '; } //--------------------------------------------------------- // header //--------------------------------------------------------- void Xml::header() { *this << "" << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } //--------------------------------------------------------- // put //--------------------------------------------------------- void Xml::put(const QString& s) { putLevel(); *this << xmlString(s) << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } //--------------------------------------------------------- // stag // //--------------------------------------------------------- void Xml::stag(const QString& s) { putLevel(); *this << '<' << s << '>' << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif ++level; } //--------------------------------------------------------- // etag //--------------------------------------------------------- void Xml::etag(const char* s) { putLevel(); *this << "' << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif --level; } //--------------------------------------------------------- // tagE // //--------------------------------------------------------- void Xml::tagE(const QString& s) { putLevel(); *this << '<' << s << "/>" << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } void Xml::tag(const char* name, int val) { putLevel(); *this << '<' << name << '>' << val << "' << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } void Xml::tag(const char* name, unsigned val) { putLevel(); *this << '<' << name << '>' << val << "' << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } void Xml::tag(const char* name, float val) { putLevel(); *this << '<' << name << '>' << val << "' << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } void Xml::tag(const char* name, const double& val) { putLevel(); *this << '<' << name << '>' << val << "' << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } void Xml::tag(const char* name, const QString& val) { putLevel(); *this << "<" << name << ">" << xmlString(val) << "' << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } void Xml::tag(const char* name, const QColor& color) { putLevel(); *this << QString("<%1 r=\"%2\" g=\"%3\" b=\"%4\"/>") .arg(name).arg(color.red()).arg(color.green()).arg(color.blue()) << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } void Xml::tag(const char* name, const QWidget* g) { tag(name, QRect(g->pos(), g->size())); } void Xml::tag(const char* name, const QRect& r) { putLevel(); *this << "<" << name; *this << QString(" x=\"%1\" y=\"%2\" w=\"%3\" h=\"%4\"/>") .arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height()) << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } //--------------------------------------------------------- // xmlString //--------------------------------------------------------- QString Xml::xmlString(const QString& ss) { QString s(ss); s.replace('&', "&"); s.replace('<', "<"); s.replace('>', ">"); s.replace('\'', "'"); s.replace('"', """); return s; } //--------------------------------------------------------- // readGeometry //--------------------------------------------------------- QRect readGeometry(QDomNode node) { QDomElement e = node.toElement(); int x = e.attribute("x","0").toInt(); int y = e.attribute("y","0").toInt(); int w = e.attribute("w","50").toInt(); int h = e.attribute("h","50").toInt(); return QRect(x, y, w, h); } //--------------------------------------------------------- // writeProperties //--------------------------------------------------------- void Xml::writeProperties(const QObject* o) { const QMetaObject* meta = o->metaObject(); // // start from dummy "muse" property, assuming this is the // first muse property in widget hierarchy // int from = meta->indexOfProperty("muse") + 1; int n = meta->propertyCount(); for (int i = from; i < n; ++i) { QMetaProperty p = meta->property(i); if (!p.isScriptable()) continue; const char* name = p.name(); QVariant v = p.read(o); switch(v.type()) { case QVariant::Bool: case QVariant::Int: tag(name, v.toInt()); break; case QVariant::Double: tag(name, v.toDouble()); break; case QVariant::String: tag(name, v.toString()); break; case QVariant::Rect: tag(name, v.toRect()); break; case QVariant::Point: { QPoint p = v.toPoint(); putLevel(); *this << "<" << name << QString(" x=\"%1\" y=\"%2\" />") .arg(p.x()).arg(p.y()) << #if QT_VERSION >= 0x050e00 Qt::endl; #else endl; #endif } break; default: printf("MusE:%s type %d not implemented\n", meta->className(), v.type()); break; } } } //--------------------------------------------------------- // readProperties //--------------------------------------------------------- void readProperties(QObject* o, QDomNode node) { const QMetaObject* meta = o->metaObject(); QDomElement e = node.toElement(); QString tag(e.tagName()); int idx = meta->indexOfProperty(tag.toLatin1().constData()); if (idx == -1) { printf("MusE:%s: unknown tag %s\n", meta->className(), tag.toLatin1().constData()); return; } QMetaProperty p = meta->property(idx); QVariant v; switch(p.type()) { case QVariant::Int: case QVariant::Bool: v.setValue(e.text().toInt()); break; case QVariant::Double: v.setValue(e.text().toDouble()); break; case QVariant::String: v.setValue(e.text()); break; case QVariant::Rect: v.setValue(AL::readGeometry(node)); break; case QVariant::Point: { int x = e.attribute("x","0").toInt(); int y = e.attribute("y","0").toInt(); v.setValue(QPoint(x, y)); } break; default: printf("MusE:%s type %d not implemented\n", meta->className(), p.type()); return; } if (p.isWritable()) p.write(o, v); } //--------------------------------------------------------- // dump //--------------------------------------------------------- void Xml::dump(int len, const unsigned char* p) { putLevel(); int col = 0; setFieldWidth(5); setNumberFlags(numberFlags() | QTextStream::ShowBase); setIntegerBase(16); for (int i = 0; i < len; ++i, ++col) { if (col >= 16) { setFieldWidth(0); #if QT_VERSION >= 0x050e00 *this << Qt::endl; #else *this << endl; #endif col = 0; putLevel(); setFieldWidth(5); } *this << (p[i] & 0xff); } if (col) #if QT_VERSION >= 0x050e00 *this << Qt::endl << Qt::dec; #else *this << endl << dec; #endif setFieldWidth(0); setIntegerBase(10); } //--------------------------------------------------------- // domError //--------------------------------------------------------- void domError(QDomNode node) { QDomElement e = node.toElement(); QString tag(e.tagName()); QString s; QDomNode dn(node); while (!dn.parentNode().isNull()) { dn = dn.parentNode(); const QDomElement e = dn.toElement(); const QString k(e.tagName()); if (!s.isEmpty()) s += ":"; s += k; } fprintf(stderr, "%s: Unknown Node <%s>, type %d\n", s.toLatin1().constData(), tag.toLatin1().constData(), node.nodeType()); if (node.isText()) { fprintf(stderr, " text node <%s>\n", node.toText().data().toLatin1().constData()); } } //--------------------------------------------------------- // domNotImplemented //--------------------------------------------------------- void domNotImplemented(QDomNode node) { if (!AL::debugMsg) return; QDomElement e = node.toElement(); QString tag(e.tagName()); QString s; QDomNode dn(node); while (!dn.parentNode().isNull()) { dn = dn.parentNode(); const QDomElement e = dn.toElement(); const QString k(e.tagName()); if (!s.isEmpty()) s += ":"; s += k; } fprintf(stderr, "%s: Node not implemented: <%s>, type %d\n", s.toLatin1().constData(), tag.toLatin1().constData(), node.nodeType()); if (node.isText()) { fprintf(stderr, " text node <%s>\n", node.toText().data().toLatin1().constData()); } } } muse-4.0.0/src/al/xml.h000066400000000000000000000043211404105311000146050ustar00rootroot00000000000000//============================================================================= // AL // Audio Utility Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __XML_H__ #define __XML_H__ #include class QColor; class QDomNode; class QRect; class QWidget; namespace AL { //--------------------------------------------------------- // Xml //--------------------------------------------------------- class Xml : public QTextStream { int level; public: Xml(); Xml(QIODevice*); void header(); void putLevel(); void put(const QString&); void stag(const QString&); void etag(const char*); void tagE(const QString&); void tag(const char* name, int); void tag(const char* name, unsigned); void tag(const char* name, const double& val); void tag(const char* name, float val); void tag(const char* name, const QString& s); void tag(const char* name, const QColor& color); void tag(const char* name, const QWidget* g); void tag(const char* name, const QRect& r); void dump(int n, const unsigned char*); void writeProperties(const QObject*); static QString xmlString(const QString&); }; extern QRect readGeometry(QDomNode); extern void readProperties(QObject* o, QDomNode node); extern void domError(QDomNode node); extern void domNotImplemented(QDomNode node); } #endif muse-4.0.0/src/audio_convert/000077500000000000000000000000001404105311000161015ustar00rootroot00000000000000muse-4.0.0/src/audio_convert/CMakeLists.txt000066400000000000000000000040351404105311000206430ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # audio_convert/CMakeLists.txt # (C) Copyright 2016 Tim E. Real (terminator356 A T sourceforge D O T net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= add_subdirectory(lib_audio_convert) add_subdirectory(src_resampler) add_subdirectory(zita_resampler) add_subdirectory(rubberband) ## ## List of source files to compile ## file (GLOB audio_convert_source_files audio_converter_plugin.cpp audio_converter_settings_group.cpp ) ## ## Define target ## add_library(audio_converter_plugin ${MODULES_BUILD} ${audio_convert_source_files} ) ## ## Linkage ## target_link_libraries(audio_converter_plugin xml_module dl ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${audio_convert_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## # set_target_properties( audio_converter_plugin # PROPERTIES # ## OUTPUT_NAME muse_audio_converter_plugin # ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS audio_converter_plugin DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/audio_convert/audio_converter_plugin.cpp000066400000000000000000000371511404105311000233620ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // audio_converter_list.cpp // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "audio_converter_plugin.h" #include #include #include #include #include #include #include // For debugging output: Uncomment the fprintf section. #define ERROR_AUDIOCONVERT(dev, format, args...) fprintf(dev, format, ##args) #define INFO_AUDIOCONVERT(dev, format, args...) fprintf(dev, format, ##args) #define DEBUG_AUDIOCONVERT(dev, format, args...) // fprintf(dev, format, ##args) namespace MusECore { void AudioConverterPluginList::discover(const QString& museGlobalLib, bool debugMsg) { QString s = museGlobalLib + "/converters"; QDir pluginDir(s, QString("*.so")); if(debugMsg) { INFO_AUDIOCONVERT(stderr, "searching for audio converters in <%s>\n", s.toLatin1().constData()); } if(pluginDir.exists()) { QFileInfoList list = pluginDir.entryInfoList(); const QFileInfo* fi; for(QFileInfoList::const_iterator it=list.cbegin(); it!=list.cend(); ++it) { fi = &*it; QByteArray ba = fi->filePath().toLatin1(); const char* path = ba.constData(); void* handle = dlopen(path, RTLD_NOW); if(!handle) { ERROR_AUDIOCONVERT(stderr, "AudioConverterList::discover(): dlopen(%s) failed: %s\n", path, dlerror()); continue; } Audio_Converter_Descriptor_Function desc_func = (Audio_Converter_Descriptor_Function)dlsym(handle, "audio_converter_descriptor"); if(!desc_func) { const char *txt = dlerror(); if(txt) { ERROR_AUDIOCONVERT(stderr, "Unable to find audio_converter_descriptor() function in plugin " "library file \"%s\": %s.\n" "Are you sure this is a MusE Audio Converter plugin file?\n", path, txt); } dlclose(handle); continue; } const AudioConverterDescriptor* descr; for(unsigned long i = 0;; ++i) { descr = desc_func(i); if(!descr) break; // Make sure it doesn't already exist. if(find(descr->_name, descr->_ID)) continue; add(fi, descr); } #ifndef __KEEP_LIBRARY_OPEN__ // The library must be kept open to use the shared object code. dlclose(handle); #endif } if(debugMsg) { INFO_AUDIOCONVERT(stderr, "%zd Audio converters found\n", size()); } } } //--------------------------------------------------------- // AudioConverterPluginList //--------------------------------------------------------- AudioConverterPluginList::~AudioConverterPluginList() { DEBUG_AUDIOCONVERT(stderr, "AudioConverterPluginList dtor: Deleting plugins...\\n"); // Must close the libraries. for(const_iterator ip = cbegin(); ip != cend(); ++ip) if(*ip) delete *ip; } void AudioConverterPluginList::clearDelete() { DEBUG_AUDIOCONVERT(stderr, "AudioConverterPluginList clearDelete: Deleting plugins...\n"); // Must close the libraries. for(const_iterator ip = cbegin(); ip != cend(); ++ip) if(*ip) delete *ip; clear(); } void AudioConverterPluginList::add(const QFileInfo* fi, const AudioConverterDescriptor* d) { push_back(new AudioConverterPlugin(fi, d)); } //--------------------------------------------------------- // find //--------------------------------------------------------- AudioConverterPlugin* AudioConverterPluginList::find(const char* name, int ID, int capabilities) { const bool id_valid = (ID != -1); const bool caps_valid = (capabilities != -1); AudioConverterPlugin* cap_res = NULL; for(const_iterator i = cbegin(); i != cend(); ++i) { AudioConverterPlugin* plugin = *i; const bool name_match = (name && (strcmp(name, plugin->name().toLatin1().constData()) == 0)); const bool ID_match = (id_valid && ID == plugin->id()); const bool caps_match = (caps_valid && (plugin->capabilities() & capabilities) == capabilities); if((name && id_valid && name_match && ID_match) || name_match || ID_match) return plugin; else if(caps_match) { if(!cap_res) cap_res = plugin; } } return cap_res; } //--------------------------------------------------------- // AudioConverterPlugin //--------------------------------------------------------- AudioConverterPlugin::AudioConverterPlugin(const QFileInfo* f, const AudioConverterDescriptor* d) { fi = *f; plugin = NULL; _descriptorFunction = NULL; _handle = NULL; _references = 0; _instNo = 0; _label = QString(d->_label); _name = QString(d->_name); _uniqueID = d->_ID; _maxChannels = d->_maxChannels; _capabilities = d->_capabilities; _minStretchRatio = d->_minStretchRatio; _maxStretchRatio = d->_maxStretchRatio; _minSamplerateRatio = d->_minSamplerateRatio; _maxSamplerateRatio = d->_maxSamplerateRatio; _minPitchShiftRatio = d->_minPitchShiftRatio; _maxPitchShiftRatio = d->_maxPitchShiftRatio; } AudioConverterPlugin::~AudioConverterPlugin() { DEBUG_AUDIOCONVERT(stderr, "AudioConverterPlugin dtor: this:%p plugin:%p id:%d\n", this, plugin, id()); // Actually, not an error, currently. We are the ones who purposely leave the library open. // Therefore we are the ones who must close it here. // TODO: Try to find a way to NOT leave the library open? //if(plugin) //{ // ERROR_AUDIOCONVERT(stderr, "AudioConverterPlugin dtor: Error: plugin is not NULL\n"); //} if(_handle) { DEBUG_AUDIOCONVERT(stderr, "AudioConverterPlugin dtor: _handle is not NULL, closing library...\n"); dlclose(_handle); } _handle = NULL; _descriptorFunction = NULL; plugin = NULL; } int AudioConverterPlugin::incReferences(int val) { DEBUG_AUDIOCONVERT(stderr, "AudioConverterPlugin::incReferences: this:%p id:%d _references:%d val:%d\n", this, id(), _references, val); int newref = _references + val; if(newref <= 0) { _references = 0; #ifndef __KEEP_LIBRARY_OPEN__ if(_handle) { DEBUG_AUDIOCONVERT(stderr, " no more instances, closing library\n"); dlclose(_handle); } _handle = NULL; _descriptorFunction = NULL; plugin = NULL; #endif return 0; } if(!_handle) { _handle = dlopen(fi.filePath().toLatin1().constData(), RTLD_NOW); if(!_handle) { ERROR_AUDIOCONVERT(stderr, "AudioConverterPlugin::incReferences dlopen(%s) failed: %s\n", fi.filePath().toLatin1().constData(), dlerror()); return 0; } Audio_Converter_Descriptor_Function acdf = (Audio_Converter_Descriptor_Function)dlsym(_handle, "audio_converter_descriptor"); if(acdf) { const AudioConverterDescriptor* descr; for(unsigned long i = 0;; ++i) { descr = acdf(i); if(!descr) break; QString label(descr->_label); if(label == _label) { _descriptorFunction = acdf; plugin = descr; break; } } } if(plugin) { _name = QString(plugin->_name); _uniqueID = plugin->_ID; } } if(!plugin) { dlclose(_handle); _handle = NULL; _references = 0; ERROR_AUDIOCONVERT(stderr, "AudioConverterPlugin::incReferences Error: %s no plugin!\n", fi.filePath().toLatin1().constData()); return 0; } _references = newref; return _references; } AudioConverterHandle AudioConverterPlugin::instantiate(AudioConverterPluginI* /*plugi*/, int systemSampleRate, int channels, AudioConverterSettings* settings, AudioConverterSettings::ModeType mode) { DEBUG_AUDIOCONVERT(stderr, "AudioConverterPlugin::instantiate\n"); AudioConverterHandle h = plugin->instantiate(systemSampleRate, plugin, channels, settings, mode); if(!h) { ERROR_AUDIOCONVERT(stderr, "AudioConverterPlugin::instantiate() Error: plugin:%s instantiate failed!\n", plugin->_name); return 0; } return h; } //--------------------------------------------------------- // AudioConverterPluginI //--------------------------------------------------------- AudioConverterPluginI::AudioConverterPluginI() { DEBUG_AUDIOCONVERT(stderr, "AudioConverterPluginI ctor\n"); init(); } AudioConverterPluginI::~AudioConverterPluginI() { DEBUG_AUDIOCONVERT(stderr, "AudioConverterPluginI dtor: this:%p handle:%p _plugin:%p\n", this, handle, _plugin); if(handle) { for(int i = 0; i < instances; ++i) { // Cleanup each of the converters. if(_plugin) _plugin->cleanup(handle[i]); } // Delete the array of converters. delete[] handle; } if (_plugin) { _plugin->incReferences(-1); } } void AudioConverterPluginI::init() { _plugin = NULL; instances = 0; handle = NULL; _channels = 0; } bool AudioConverterPluginI::initPluginInstance(AudioConverterPlugin* plug, int systemSampleRate, int channels, AudioConverterSettings* settings, AudioConverterSettings::ModeType mode) { if(!plug) { ERROR_AUDIOCONVERT(stderr, "AudioConverterPluginI::initPluginInstance: Error: plug is zero\n"); return true; } DEBUG_AUDIOCONVERT(stderr, "AudioConverterPluginI::initPluginInstance: plug:%p id:%d\n", plug, plug->id()); _plugin = plug; _channels = channels; // We are creating an object from the library. Increment the references // so that the library may be opened. if(_plugin->incReferences(1) == 0) return true; QString inst("-" + QString::number(_plugin->instNo())); _name = _plugin->name() + inst; _label = _plugin->label() + inst; int max_chans = _plugin->maxChannels(); // -1 = don't care, infinite channels. if(max_chans > 0) { instances = _channels / max_chans; if(instances < 1) instances = 1; } else // Don't care. Unlimited channels. instances = 1; handle = new AudioConverterHandle[instances]; for(int i = 0; i < instances; ++i) handle[i] = NULL; for(int i = 0; i < instances; ++i) { DEBUG_AUDIOCONVERT(stderr, " instance:%d\n", i); handle[i] = _plugin->instantiate(this, systemSampleRate, _channels, settings, mode); if(!handle[i]) return true; } return false; } bool AudioConverterPluginI::isValid() const { if(!handle) return false; for(int i = 0; i < instances; ++i) if(!handle[i] || !handle[i]->isValid()) return false; return true; } void AudioConverterPluginI::setChannels(int channels) { if(!handle) return; for(int i = 0; i < instances; ++i) if(handle[i]) { // FIXME: Multiple instances point to the SAME SndFile. // Make seperate SndFile instances per-channel, // make this function per-handle. // Finish this. For now just return after the first handle found: handle[i]->setChannels(channels); return; } } void AudioConverterPluginI::reset() { if(!handle) return; for(int i = 0; i < instances; ++i) if(handle[i]) handle[i]->reset(); } AudioConverterSettings::ModeType AudioConverterPluginI::mode() const { if(!handle) return AudioConverterSettings::RealtimeMode; AudioConverterSettings::ModeType fin_m = AudioConverterSettings::RealtimeMode; AudioConverterSettings::ModeType m; bool first = true; for(int i = 0; i < instances; ++i) if(handle[i]) { m = handle[i]->mode(); if(m != fin_m) { // Check: Each instance should be in the same mode. if(!first) { ERROR_AUDIOCONVERT(stderr, "AudioConverterPluginI::mode(): Error: Different mode:%d than first:%d in instance\n", m, fin_m); } first = false; fin_m = m; } } return fin_m; } int AudioConverterPluginI::process( SNDFILE* sf_handle, const int sf_chans, const double sf_sr_ratio, const StretchList* sf_stretch_list, const sf_count_t pos, float** buffer, const int channels, const int frames, const bool overwrite) { if(!handle) return 0; for(int i = 0; i < instances; ++i) if(handle[i]) { int count = handle[i]->process(sf_handle, sf_chans, sf_sr_ratio, sf_stretch_list, pos, buffer, channels, frames, overwrite); // FIXME: Multiple instances point to the SAME SndFile. // Make separate SndFile instances per-channel, // make this function per-handle, and work with // each count returned. // No choice, for now just return after the first handle found: return count; } return 0; } //--------------------------------------------------------- // AudioConverterSettingsI //--------------------------------------------------------- AudioConverterSettingsI::AudioConverterSettingsI() { DEBUG_AUDIOCONVERT(stderr, "AudioConverterSettingsI ctor\n"); init(); } AudioConverterSettingsI::~AudioConverterSettingsI() { DEBUG_AUDIOCONVERT(stderr, "AudioConverterSettingsI dtor: this:%p\n", this); if(_plugin) { DEBUG_AUDIOCONVERT(stderr, " _plugin:%p id:%d\n", _plugin, _plugin->id()); if(_settings) _plugin->cleanupSettings(_settings); _plugin->incReferences(-1); } } void AudioConverterSettingsI::init() { _plugin = NULL; _settings = NULL; } void AudioConverterSettingsI::assign(const AudioConverterSettingsI& other) { _plugin = other._plugin; if(!_settings) { // We are creating an object from the library. Increment the references // so that the library may be opened. if(_plugin->incReferences(1) == 0) return; _settings = _plugin->createSettings(false); // Don't care about isLocal argument. if(!_settings) { // Give up the reference. _plugin->incReferences(-1); return; } } _settings->assign(*other._settings); } bool AudioConverterSettingsI::initSettingsInstance(AudioConverterPlugin* plug, bool isLocal) { if(!plug) { ERROR_AUDIOCONVERT(stderr, "AudioConverterSettingsI::createSettings: Error: plug is zero\n"); return true; } DEBUG_AUDIOCONVERT(stderr, "AudioConverterSettingsI::initSettingsInstance: this:%p plug:%p id:%d\n", this, plug, plug->id()); _plugin = plug; // We are creating an object from the library. Increment the references // so that the library may be opened. if(_plugin->incReferences(1) == 0) return true; _settings = _plugin->createSettings(isLocal); if(!_settings) { // Give up the reference. _plugin->incReferences(-1); return true; } return false; } } // namespace MusECore muse-4.0.0/src/audio_convert/audio_converter_plugin.h000066400000000000000000000236471404105311000230340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // audio_converter_list.h // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __AUDIO_CONVERTER_PLUGIN_H__ #define __AUDIO_CONVERTER_PLUGIN_H__ #include #include #include #include #include "lib_audio_convert/audioconvert.h" #include "xml.h" namespace MusECore { //--------------------------------------------------------- // AudioConverterPlugin //--------------------------------------------------------- class AudioConverterPluginI; class AudioConverterPlugin { protected: friend class AudioConverterPluginI; void* _handle; int _references; int _instNo; QFileInfo fi; Audio_Converter_Descriptor_Function _descriptorFunction; const AudioConverterDescriptor* plugin; int _uniqueID; QString _label; QString _name; int _maxChannels; // Combination of AudioConverter::Capabilities values. int _capabilities; // Minimum and maximum ratios. -1 means infinite, don't care. double _minStretchRatio; double _maxStretchRatio; double _minSamplerateRatio; double _maxSamplerateRatio; double _minPitchShiftRatio; double _maxPitchShiftRatio; public: AudioConverterPlugin(const QFileInfo* f, const AudioConverterDescriptor* d); virtual ~AudioConverterPlugin(); virtual QString label() const { return _label; } QString name() const { return _name; } int id() const { return _uniqueID; } int maxChannels() const { return _maxChannels; } // Combination of AudioConverter::Capabilities values. int capabilities() const { return _capabilities; } // Minimum and maximum ratios. -1 means infinite, don't care. double minStretchRatio() const { return _minStretchRatio; } double maxStretchRatio() const { return _maxStretchRatio; } double minSamplerateRatio() const { return _minSamplerateRatio; } double maxSamplerateRatio() const { return _maxSamplerateRatio; } double minPitchShiftRatio() const { return _minPitchShiftRatio; } double maxPitchShiftRatio() const { return _maxPitchShiftRatio; } QString lib(bool complete = true) const { return complete ? fi.completeBaseName() : fi.baseName(); } QString dirPath(bool complete = true) const { return complete ? fi.absolutePath() : fi.path(); } QString filePath() const { return fi.filePath(); } QString fileName() const { return fi.fileName(); } int references() const { return _references; } virtual int incReferences(int); int instNo() { return _instNo++; } // Mode is an AudioConverterSettings::ModeType selecting which of the settings to use. virtual AudioConverterHandle instantiate(AudioConverterPluginI* plugi, int systemSampleRate, int channels, AudioConverterSettings* settings, AudioConverterSettings::ModeType mode); virtual void cleanup(AudioConverterHandle handle) { if(plugin && plugin->cleanup) plugin->cleanup(handle); } virtual void cleanupSettings(AudioConverterSettings* handle) { if(plugin && plugin->cleanupSettings) plugin->cleanupSettings(handle); } // Returns valid object only if plugin is valid ie. reference count is not zero. virtual AudioConverterSettings* createSettings(bool isLocal) { if(plugin && plugin->createSettings) return plugin->createSettings(isLocal); else return NULL; } }; //--------------------------------------------------------- // AudioConverterPluginList //--------------------------------------------------------- class AudioConverterPluginList : public std::list { public: AudioConverterPluginList() { } virtual ~AudioConverterPluginList(); // Delete all the items and clear the list. void clearDelete(); // Discover available plugins and fill the list. void discover(const QString& museGlobalLib, bool debugMsg = false); void add(const QFileInfo* fi, const AudioConverterDescriptor* d); AudioConverterPlugin* find(const char* name = 0, int ID = -1, int capabilities = -1); }; typedef AudioConverterPluginList::iterator iAudioConverterPlugin; typedef AudioConverterPluginList::const_iterator ciAudioConverterPlugin; //--------------------------------------------------------- // AudioConverterPluginI //--------------------------------------------------------- class AudioConverterPluginI { AudioConverterPlugin* _plugin; int _channels; int instances; AudioConverterHandle* handle; // per instance QString _name; QString _label; void init(); public: AudioConverterPluginI(); virtual ~AudioConverterPluginI(); AudioConverterPlugin* plugin() const { return _plugin; } int pluginID() { return _plugin ? _plugin->id() : -1; } // Combination of AudioConverter::Capabilities values. int capabilities() const { return _plugin ? _plugin->capabilities() : 0; } // Minimum and maximum ratios. -1 means infinite, don't care. double minStretchRatio() const { return _plugin ? _plugin->minStretchRatio() : 1.0; } double maxStretchRatio() const { return _plugin ? _plugin->maxStretchRatio() : 1.0; } double minSamplerateRatio() const { return _plugin ? _plugin->minSamplerateRatio() : 1.0; } double maxSamplerateRatio() const { return _plugin ? _plugin->maxSamplerateRatio() : 1.0; } double minPitchShiftRatio() const { return _plugin ? _plugin->minPitchShiftRatio() : 1.0; } double maxPitchShiftRatio() const { return _plugin ? _plugin->maxPitchShiftRatio() : 1.0; } // Mode is an AudioConverterSettings::ModeType selecting which of the settings to use. bool initPluginInstance(AudioConverterPlugin* plug, int systemSampleRate, int channels, AudioConverterSettings* settings, AudioConverterSettings::ModeType mode); // Returns whether all the instances are valid - that each AudioConverter is valid. bool isValid() const; void setChannels(int channels); void reset(); // Returns the mode of all the instances. AudioConverterSettings::ModeType mode() const; int process( SNDFILE* sf_handle, const int sf_chans, const double sf_sr_ratio, const StretchList* sf_stretch_list, const sf_count_t pos, float** buffer, const int channels, const int frames, const bool overwrite); QString pluginLabel() const { return _plugin ? _plugin->label() : QString(); } QString label() const { return _label; } QString name() const { return _plugin ? _plugin->name() : QString(); } QString lib() const { return _plugin ? _plugin->lib() : QString(); } QString dirPath() const { return _plugin ? _plugin->dirPath() : QString(); } QString fileName() const { return _plugin ? _plugin->fileName() : QString(); } }; //--------------------------------------------------------- // AudioConverterSettingsI //--------------------------------------------------------- class AudioConverterSettingsI { AudioConverterPlugin* _plugin; AudioConverterSettings* _settings; void init(); public: AudioConverterSettingsI(); virtual ~AudioConverterSettingsI(); AudioConverterPlugin* plugin() const { return _plugin; } AudioConverterSettings* settings() const { return _settings; } int pluginID() { return _plugin ? _plugin->id() : -1; } void assign(const AudioConverterSettingsI&); bool initSettingsInstance(AudioConverterPlugin* plug, bool isLocal = false); // Returns whether to use these settings or defer to default settings. // Mode is a combination of AudioConverterSettings::ModeType selecting // which of the settings to check. Can also be <= 0, meaning all. bool useSettings(int mode = -1) { return _settings ? _settings->useSettings(mode) : false; } bool isDefault() { return _settings ? _settings->isDefault() : true; } // Act like it's default. int executeUI(AudioConverterSettings::ModeType mode, QWidget* parent = NULL, bool isLocal = false) { return _settings ? _settings->executeUI(mode, parent, isLocal) : 0; } void read(Xml& xml) { if(_settings) _settings->read(xml); } void write(int level, Xml& xml) const { if(_settings) _settings->write(level, xml); } }; } // namespace MusECore #endif muse-4.0.0/src/audio_convert/audio_converter_settings_group.cpp000066400000000000000000000230261404105311000251340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // audio_converter_settings_group.cpp // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // //========================================================= #include "audio_converter_settings_group.h" // For debugging output: Uncomment the fprintf section. #define ERROR_AUDIOCONVERT(dev, format, args...) fprintf(dev, format, ##args) #define DEBUG_AUDIOCONVERT(dev, format, args...) // fprintf(dev, format, ##args) namespace MusECore { // Some hard-coded defaults. const AudioConverterSettingsGroupOptions AudioConverterSettingsGroupOptions::defaultOptions( false, 1001, 1003); void AudioConverterSettingsGroupOptions::write(int level, Xml& xml) const { xml.tag(level++, "settings\"%d\""); xml.intTag(level, "useSettings", _useSettings); xml.intTag(level, "preferredResampler", _preferredResampler); xml.intTag(level, "preferredShifter", _preferredShifter); xml.tag(--level, "/settings"); } void AudioConverterSettingsGroupOptions::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "useSettings") _useSettings = xml.parseInt(); else if (tag == "preferredResampler") _preferredResampler = xml.parseInt(); else if (tag == "preferredShifter") _preferredShifter = xml.parseInt(); else xml.unknown("settings"); break; case Xml::Attribut: fprintf(stderr, "settings unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (tag == "settings") { return; } default: break; } } } AudioConverterSettingsGroup::~AudioConverterSettingsGroup() { for(const_iterator i = cbegin(); i != cend(); ++i) delete (*i); } void AudioConverterSettingsGroup::assign(const AudioConverterSettingsGroup& other) { clearDelete(); for(const_iterator i = other.cbegin(); i != other.cend(); ++i) { AudioConverterSettingsI* other_settingsI = *i; AudioConverterSettingsI* new_settingsI = new AudioConverterSettingsI(); new_settingsI->assign(*other_settingsI); push_back(new_settingsI); } _options._useSettings = other._options._useSettings; _options._preferredResampler = other._options._preferredResampler; _options._preferredShifter = other._options._preferredShifter; _isLocal = other._isLocal; } void AudioConverterSettingsGroup::clearDelete() { for(const_iterator i = cbegin(); i != cend(); ++i) delete (*i); clear(); } void AudioConverterSettingsGroup::populate(AudioConverterPluginList* plugList, bool isLocal) { clearDelete(); _isLocal = isLocal; initOptions(); for(ciAudioConverterPlugin ip = plugList->cbegin(); ip != plugList->cend(); ++ip) { if(AudioConverterPlugin* p = *ip) { AudioConverterSettingsI* setI = new AudioConverterSettingsI(); if(setI->initSettingsInstance(p, isLocal)) { delete setI; continue; } push_back(setI); } } } AudioConverterSettingsI* AudioConverterSettingsGroup::find(int ID) const { for(const_iterator i = cbegin(); i != cend(); ++i) { AudioConverterSettingsI* settings = *i; if(settings->pluginID() == ID) return settings; } return 0; } void AudioConverterSettingsGroup::readItem(Xml& xml, AudioConverterPluginList* plugList) { AudioConverterSettingsI* setI = NULL; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "settings") { if(setI) setI->read(xml); } else xml.unknown("audioConverterSetting"); break; case Xml::Attribut: if (tag == "name") { if(AudioConverterPlugin* p = plugList->find(xml.s2().toLatin1().constData())) setI = find(p->id()); } else fprintf(stderr, "audioConverterSetting unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (tag == "audioConverterSetting") { return; } default: break; } } } void AudioConverterSettingsGroup::read(Xml& xml, AudioConverterPluginList* plugList) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "useSettings") _options._useSettings = xml.parseInt(); else if (tag == "preferredResampler") { if(AudioConverterPlugin* plugin = plugList->find(xml.parse1().toLatin1().constData())) _options._preferredResampler = plugin->id(); } else if (tag == "preferredShifter") { if(AudioConverterPlugin* plugin = plugList->find(xml.parse1().toLatin1().constData())) _options._preferredShifter = plugin->id(); } else if (tag == "audioConverterSetting") readItem(xml, plugList); else xml.unknown("audioConverterSettingsGroup"); break; case Xml::Attribut: fprintf(stderr, "audioConverterSettingsGroup unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (tag == "audioConverterSettingsGroup") { return; } default: break; } } } void AudioConverterSettingsGroup::write(int level, Xml& xml, AudioConverterPluginList* plugList) const { if(isDefault()) return; xml.tag(level++, "audioConverterSettingsGroup"); if(_options._useSettings != AudioConverterSettingsGroupOptions::defaultOptions._useSettings) xml.intTag(level, "useSettings", _options._useSettings); if(_options._preferredResampler != AudioConverterSettingsGroupOptions::defaultOptions._preferredResampler) { if(AudioConverterPlugin* plugin = plugList->find(NULL, _options._preferredResampler)) xml.strTag(level, "preferredResampler", plugin->name().toLatin1().constData()); } if(_options._preferredShifter != AudioConverterSettingsGroupOptions::defaultOptions._preferredShifter) { if(AudioConverterPlugin* plugin = plugList->find(NULL, _options._preferredShifter)) xml.strTag(level, "preferredShifter", plugin->name().toLatin1().constData()); } for(const_iterator i = cbegin(); i != cend(); ++i) { if(AudioConverterSettingsI* settings = *i) settings->write(level, xml); } xml.tag(--level, "/audioConverterSettingsGroup"); } bool AudioConverterSettingsGroup::useSettings(int mode) const { if(_options._useSettings) return true; for(const_iterator i = cbegin(); i != cend(); ++i) if((*i)->useSettings(mode)) return true; return false; } bool AudioConverterSettingsGroup::isDefault() const { for(const_iterator i = cbegin(); i != cend(); ++i) { if(AudioConverterSettingsI* settings = *i) if(!settings->isDefault()) return false; } if(!_options.isDefault()) return false; return true; } } // namespace MusECore muse-4.0.0/src/audio_convert/audio_converter_settings_group.h000066400000000000000000000104711404105311000246010ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // audio_converter_settings_group.h // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __AUDIO_CONVERTER_SETTINGS_GROUP_H__ #define __AUDIO_CONVERTER_SETTINGS_GROUP_H__ #include #include "audio_convert/audio_converter_plugin.h" namespace MusECore { //--------------------------------------------------------- // AudioConverterSettingsGroup //--------------------------------------------------------- class AudioConverterSettingsGroup; struct AudioConverterSettingsGroupOptions { // Some hard-coded defaults. static const AudioConverterSettingsGroupOptions defaultOptions; // Whether to use these settings or defer to // some higher default settings if available. // The highest level (defaults) will ignore this value. bool _useSettings; int _preferredResampler; int _preferredShifter; AudioConverterSettingsGroupOptions(bool useSettings = defaultOptions._useSettings, int preferredResampler = defaultOptions._preferredResampler, int preferredShifter = defaultOptions._preferredShifter) { initOptions(useSettings, preferredResampler, preferredShifter); } void initOptions(bool useSettings, int preferredResampler, int preferredShifter) { _useSettings = useSettings; _preferredResampler = preferredResampler; _preferredShifter = preferredShifter; } void read(Xml&); void write(int, Xml&) const; // Returns whether to use these settings or defer to default settings. bool useSettings() const { return _useSettings; } bool isDefault() const { return *this == defaultOptions; } bool operator==(const AudioConverterSettingsGroupOptions& other) const { return other._useSettings == _useSettings && other._preferredResampler == _preferredResampler && other._preferredShifter == _preferredShifter; } }; class AudioConverterSettingsGroup : public std::list { private: bool _isLocal; void clearDelete(); public: AudioConverterSettingsGroupOptions _options; AudioConverterSettingsGroup(bool isLocal) : _isLocal(isLocal) { initOptions(); } virtual ~AudioConverterSettingsGroup(); void initOptions() { _options.initOptions( AudioConverterSettingsGroupOptions::defaultOptions._useSettings, AudioConverterSettingsGroupOptions::defaultOptions._preferredResampler, AudioConverterSettingsGroupOptions::defaultOptions._preferredShifter); } void assign(const AudioConverterSettingsGroup&); void populate(AudioConverterPluginList* plugList, bool isLocal = false); AudioConverterSettingsI* find(int ID) const; void readItem(Xml&, AudioConverterPluginList* plugList); void read(Xml&, AudioConverterPluginList* plugList); void write(int, Xml&, AudioConverterPluginList* plugList) const; // Returns whether to use these settings or defer to default settings. // Mode is a combination of AudioConverterSettings::ModeType selecting // which of the settings to check. Can also be <= 0, meaning all. bool useSettings(int mode = -1) const; bool isDefault() const; }; typedef AudioConverterSettingsGroup::iterator iAudioConverterSettingsI; typedef AudioConverterSettingsGroup::const_iterator ciAudioConverterSettingsI; } // namespace MusECore #endif muse-4.0.0/src/audio_convert/lib_audio_convert/000077500000000000000000000000001404105311000215705ustar00rootroot00000000000000muse-4.0.0/src/audio_convert/lib_audio_convert/CMakeLists.txt000066400000000000000000000035371404105311000243400ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # lib_audio_convert/CMakeLists.txt # (C) Copyright 2016 Tim E. Real (terminator356 A T sourceforge D O T net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB audio_convert_source_files audioconvert.cpp ) ## ## Define target ## add_library(audio_convert ${MODULES_BUILD} ${audio_convert_source_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${audio_convert_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( audio_convert PROPERTIES OUTPUT_NAME muse_audio_convert ) ## ## Linkage ## target_link_libraries(audio_convert time_stretch_module xml_module ${SNDFILE_LIBRARIES} ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS audio_convert DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/audio_convert/lib_audio_convert/audioconvert.cpp000066400000000000000000000050611404105311000250000ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: audioconvert.cpp,v 1.1.1.1 2009/12/28 16:07:33 terminator356 Exp $ // // (C) Copyright 1999-2009 Werner Schweer (ws@seh.de) // // Audio converter module created by Tim // (C) Copyright 2009-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // //========================================================= #include #include #include "audioconvert.h" #include "muse_math.h" // For debugging output: Uncomment the fprintf section. #define ERROR_AUDIOCONVERT(dev, format, args...) fprintf(dev, format, ##args) #define DEBUG_AUDIOCONVERT(dev, format, args...) // fprintf(dev, format, ##args) namespace MusECore { //--------------------------------------------------------- // AudioConverter //--------------------------------------------------------- AudioConverter::AudioConverter(int systemSampleRate, AudioConverterSettings::ModeType mode) : _systemSampleRate(systemSampleRate), _channels(0), _refCount(1), _mode(mode) { DEBUG_AUDIOCONVERT(stderr, "AudioConverter::AudioConverter this:%p\n", this); } AudioConverter::~AudioConverter() { DEBUG_AUDIOCONVERT(stderr, "AudioConverter::~AudioConverter this:%p\n", this); } AudioConverterHandle AudioConverter::reference() { _refCount += 1; DEBUG_AUDIOCONVERT(stderr, "AudioConverter::reference this:%p current refcount:%d\n", this, _refCount); return this; } // Static. AudioConverterHandle AudioConverter::release(AudioConverter* cv) { if(!cv) return 0; cv->_refCount -= 1; DEBUG_AUDIOCONVERT(stderr, "AudioConverter::release converter:%p current refcount:%d\n", cv, cv->_refCount); if(cv->_refCount <= 0) { DEBUG_AUDIOCONVERT(stderr, "AudioConverter::release deleting converter:%p\n", cv); delete cv; cv = 0; } return cv; } } // namespace MusECore muse-4.0.0/src/audio_convert/lib_audio_convert/audioconvert.h000066400000000000000000000126261404105311000244520ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: audioconvert.h,v 1.1.1.1 2009/12/28 16:07:33 terminator356 Exp $ // // (C) Copyright 1999-2009 Werner Schweer (ws@seh.de) // // Audio converter module created by Tim // (C) Copyright 2009-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __AUDIOCONVERT_H__ #define __AUDIOCONVERT_H__ //#include "config.h" #include #include #include "xml.h" #include "time_stretch.h" namespace MusECore { class AudioConverterSettings { public: // Three types of settings: // Those suitable for offline work (highest quality vs. slowest is desirable), // those suitable for realtime work (speed vs quality depends on the end goal), and // those suitable for gui display (relaxed quality vs fastest is acceptable). // They can be OR'd together. enum ModeType { OfflineMode = 0x01, RealtimeMode = 0x02, GuiMode = 0x04}; protected: int _converterID; public: AudioConverterSettings(int converterID) : _converterID(converterID) { } virtual ~AudioConverterSettings() { } int converterID() const { return _converterID; } virtual void assign(const AudioConverterSettings&) = 0; virtual int executeUI(ModeType mode, QWidget* parent = NULL, bool isLocal = false) = 0; virtual void read(Xml&) = 0; virtual void write(int, Xml&) const = 0; // Returns whether to use these settings or defer to default settings. // Mode is a combination of AudioConverterSettings::ModeType selecting // which of the settings to check. Can also be <= 0, meaning all. virtual bool useSettings(int mode = -1) const = 0; virtual bool isDefault() const = 0; }; class AudioConverter; typedef AudioConverter* AudioConverterHandle; struct AudioConverterDescriptor { // Unique ID of the converter. int _ID; // Returns combination of Capabilities values. int _capabilities; // The name of the converter. const char* _name; // The converter's label (group). const char* _label; // Maximum available channels. -1 means infinite, don't care. int _maxChannels; // Minimum and maximum ratios. -1 means infinite, don't care. double _minStretchRatio; double _maxStretchRatio; double _minSamplerateRatio; double _maxSamplerateRatio; double _minPitchShiftRatio; double _maxPitchShiftRatio; // Create an instance of the plugin. // Mode is an AudioConverterSettings::ModeType selecting which of the settings to use. AudioConverterHandle (*instantiate)(int systemSampleRate, const struct AudioConverterDescriptor* Descriptor, int channels, AudioConverterSettings* settings, AudioConverterSettings::ModeType mode); // Destroy the instance after usage. void (*cleanup)(AudioConverterHandle Instance); // Creates a new settings instance. Caller is responsible for deleting the returned object. // Settings will initialize normally. or with 'don't care', if isLocal is false or true resp. AudioConverterSettings* (*createSettings)(bool isLocal); // Destroy the settings instance after usage. void (*cleanupSettings)(AudioConverterSettings* Instance); }; typedef const AudioConverterDescriptor* (*Audio_Converter_Descriptor_Function)(unsigned long Index); //--------------------------------------------------------- // AudioConverter //--------------------------------------------------------- class AudioConverter { public: // Can be or'd together. enum Capabilities { SampleRate=0x01, Stretch=0x02, Pitch=0x04 }; protected: int _systemSampleRate; int _channels; int _refCount; AudioConverterSettings::ModeType _mode; public: AudioConverter(int systemSampleRate, AudioConverterSettings::ModeType mode); virtual ~AudioConverter(); // Reference this instance of a converter. Increases the reference count. AudioConverterHandle reference(); // De-reference this instance of a converter. Decreases the reference count. Destroys the instance if count is zero. static AudioConverterHandle release(AudioConverter* cv); virtual bool isValid() const = 0; virtual void reset() = 0; virtual void setChannels(int ch) = 0; // Returns the current mode. virtual AudioConverterSettings::ModeType mode() const = 0; virtual int process( SNDFILE* sf_handle, const int sf_chans, const double sf_sr_ratio, const StretchList* sf_stretch_list, const sf_count_t pos, float** buffer, const int channels, const int frames, const bool overwrite) = 0; }; } // namespace MusECore #endif muse-4.0.0/src/audio_convert/rubberband/000077500000000000000000000000001404105311000202075ustar00rootroot00000000000000muse-4.0.0/src/audio_convert/rubberband/CMakeLists.txt000066400000000000000000000051471404105311000227560ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # audio_convert/CMakeLists.txt # (C) Copyright 2016 Tim E. Real (terminator356 A T sourceforge D O T net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( rubberband_converter_mocs rubberband_converter.h ) ## ## UI files ## file (GLOB rubberband_converter_ui_files rubberband_settings_base.ui ) QT5_WRAP_UI ( rubberband_converter_uis ${rubberband_converter_ui_files} ) ## ## List of source files to compile ## file (GLOB rubberband_converter_source_files rubberband_converter.cpp ) ## ## Define target ## # add_library ( rubberband_converter ${MODULES_BUILD} # add_library ( rubberband_converter MODULE add_library ( rubberband_converter SHARED ${rubberband_converter_source_files} ${rubberband_converter_mocs} ${rubberband_converter_uis} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${rubberband_converter_source_files} ${rubberband_converter_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## # - tell cmake to name target rubberband_converter.so instead of # librubberband_converter.so # - use precompiled header files # set_target_properties ( rubberband_converter PROPERTIES PREFIX "" COMPILE_FLAGS "-fvisibility=hidden" # OUTPUT_NAME muse_rubberband_converter ) ## ## Linkage ## target_link_libraries(rubberband_converter audio_convert ${RUBBERBAND_LIBRARIES} ${QT_LIBRARIES} ) ## ## Install location ## install( TARGETS rubberband_converter DESTINATION ${MusE_AUDIO_CONVERTER_PLUGINS_DIR} ) # install( FILES ChangeLog COPYING README TODO # DESTINATION ${MusE_DOC_DIR}/rubberband_converter # ) muse-4.0.0/src/audio_convert/rubberband/rubberband_converter.cpp000066400000000000000000000766161404105311000251300ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // rubberband_converter.cpp // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // //========================================================= #include #include #include #include "muse_math.h" #include "time_stretch.h" #include "rubberband_converter.h" // For debugging output: Uncomment the fprintf section. #define ERROR_AUDIOCONVERT(dev, format, args...) fprintf(dev, format, ##args) #define DEBUG_AUDIOCONVERT(dev, format, args...) // fprintf(dev, format, ##args) // Create a new instance of the plugin. // Mode is an AudioConverterSettings::ModeType selecting which of the settings to use. MusECore::AudioConverter* instantiate(int systemSampleRate, const MusECore::AudioConverterDescriptor* /*Descriptor*/, int channels, MusECore::AudioConverterSettings* settings, MusECore::AudioConverterSettings::ModeType mode) { return new MusECore::RubberBandAudioConverter(systemSampleRate, channels, settings, mode); } // Destroy the instance after usage. void cleanup(MusECore::AudioConverterHandle instance) { MusECore::AudioConverter::release(instance); } // Destroy the instance after usage. void cleanupSettings(MusECore::AudioConverterSettings* instance) { delete instance; } // Creates a new settings instance. Caller is responsible for deleting the returned object. // Settings will initialize normally. or with 'don't care', if isLocal is false or true resp. MusECore::AudioConverterSettings* createSettings(bool isLocal) { return new MusECore::RubberBandAudioConverterSettings(isLocal); } extern "C" { static MusECore::AudioConverterDescriptor descriptor = { 1003, MusECore::AudioConverter::SampleRate | MusECore::AudioConverter::Stretch | MusECore::AudioConverter::Pitch, "Rubberband Stretcher", "Rubberband", -1, 0.0, -1.0, 0.0, -1.0, 0.0, -1.0, instantiate, cleanup, createSettings, cleanupSettings }; // We must compile with -fvisibility=hidden to avoid namespace // conflicts with global variables. // Only visible symbol is "audio_converter_descriptor". // (TODO: all plugins should be compiled this way) __attribute__ ((visibility("default"))) const MusECore::AudioConverterDescriptor* audio_converter_descriptor(unsigned long i) { return (i == 0) ? &descriptor : 0; } } //================================================================= namespace MusECore { //--------------------------------------------------------- // RubberBandAudioConverter //--------------------------------------------------------- RubberBandAudioConverter::RubberBandAudioConverter( int systemSampleRate, int channels, AudioConverterSettings* settings, AudioConverterSettings::ModeType mode) : AudioConverter(systemSampleRate, mode) { DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::RubberBandAudioConverter this:%p channels:%d mode:%d\n", this, channels, mode); #ifdef RUBBERBAND_SUPPORT const int mask = ~(RubberBand::RubberBandStretcher::OptionProcessOffline | RubberBand::RubberBandStretcher::OptionProcessRealTime); //const int of_flags = RubberBand::RubberBandStretcher::OptionProcessOffline; const int of_flags = RubberBand::RubberBandStretcher::OptionProcessRealTime; const int rt_flags = RubberBand::RubberBandStretcher::OptionProcessRealTime; const int gui_flags = RubberBand::RubberBandStretcher::OptionProcessRealTime; #else const int mask = ~0; const int of_flags = 0; const int rt_flags = 0; const int gui_flags = 0; #endif RubberBandAudioConverterSettings* rb_settings = static_cast(settings); switch(mode) { case AudioConverterSettings::OfflineMode: _options = ((rb_settings ? rb_settings->offlineOptions()->_options : 0) & mask) | of_flags; break; case AudioConverterSettings::RealtimeMode: _options = ((rb_settings ? rb_settings->realtimeOptions()->_options : 0) & mask) | rt_flags; break; case AudioConverterSettings::GuiMode: _options = ((rb_settings ? rb_settings->guiOptions()->_options : 0) & mask) | gui_flags; break; default: _options = 0; break; } _channels = channels; // REMOVE Tim. samplerate. Changed. 12/19 Removed. TESTING Reinstate. // _latencyCompPending = true; // Set to compensate at the first process. _latencyCompPending = false; // Set to compensate at the first process. #ifdef RUBBERBAND_SUPPORT _rbs = new RubberBand::RubberBandStretcher(_systemSampleRate, _channels, _options); #endif } RubberBandAudioConverter::~RubberBandAudioConverter() { DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::~RubberBandAudioConverter this:%p\n", this); #ifdef RUBBERBAND_SUPPORT if(_rbs) delete _rbs; #endif } void RubberBandAudioConverter::setChannels(int ch) { DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::setChannels this:%p channels:%d\n", this, ch); _channels = ch; #ifdef RUBBERBAND_SUPPORT if(_rbs) delete _rbs; _rbs = new RubberBand::RubberBandStretcher(_systemSampleRate, _channels, _options); #endif } void RubberBandAudioConverter::reset() { DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::reset this:%p\n", this); #ifdef RUBBERBAND_SUPPORT if(!_rbs) return; _rbs->reset(); // REMOVE Tim. samplerate. Changed. 12/19 Removed. TESTING Reinstate. // _latencyCompPending = true; return; #endif } AudioConverterSettings::ModeType RubberBandAudioConverter::mode() const { return _mode; } #ifdef RUBBERBAND_SUPPORT int RubberBandAudioConverter::process( SNDFILE* sf_handle, const int sf_chans, const double sf_sr_ratio, const StretchList* sf_stretch_list, const sf_count_t pos, float** buffer, const int channels, const int frames, const bool overwrite) { if(!_rbs) return 0; if(_systemSampleRate <= 0) { DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::process Error: systemSampleRate <= 0!\n"); //return _sfCurFrame; return 0; } const double d_pos = sf_sr_ratio * double(pos); const MuseFrame_t new_frame = sf_stretch_list->unSquish(d_pos); const double stretchVal = sf_stretch_list->ratioAt(StretchListItem::StretchEvent, new_frame); const double samplerateVal = sf_stretch_list->ratioAt(StretchListItem::SamplerateEvent, new_frame); const double fin_samplerateRatio = sf_sr_ratio * samplerateVal; if(fin_samplerateRatio < 0.0001) { DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::process Error: fin_samplerateRatio ratio is near zero!\n"); //return _sfCurFrame; return 0; } const double inv_fin_samplerateRatio = 1.0 / fin_samplerateRatio; const double debug_min_pos = 256; int old_lat = _rbs->getLatency(); // For just sample rate conversion, apply complimentary ratio to time and pitch. _rbs->setTimeRatio(inv_fin_samplerateRatio * stretchVal); _rbs->setPitchScale(fin_samplerateRatio); sf_count_t outFrames = frames; if(_latencyCompPending) outFrames += old_lat; sf_count_t outSize = outFrames * sf_chans; float* rboutbuffer[sf_chans]; float rboutdata[outSize]; for(int i = 0; i < sf_chans; ++i) rboutbuffer[i] = rboutdata + i * outFrames; sf_count_t rn = 0; // Tested: Latency will change after the above setting of ratios. Also affected by converter options. //DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::process latency:%lu\n", _rbs->getLatency()); if(_latencyCompPending && old_lat > 0) { size_t rbinSize = old_lat * sf_chans; float rbindata[rbinSize]; memset(rbindata, 0, rbinSize * sizeof(float)); // Must de-interleave data to feed to rubberband. float* rbinbuffer[sf_chans]; for(int i = 0; i < sf_chans; ++i) rbinbuffer[i] = rbindata + i * old_lat; if(d_pos <= debug_min_pos) { DEBUG_AUDIOCONVERT(stderr, " Latency: old_lat:%d new_lat:%d\n", old_lat, (int)_rbs->getLatency()); } _rbs->process(rbinbuffer, old_lat, false); } while(_rbs->available() < outFrames) { size_t sreq = _rbs->getSamplesRequired(); if(sreq <= 0) break; size_t rbinSize = sreq * sf_chans; float sfdata[rbinSize]; rn = sf_readf_float(sf_handle, sfdata, sreq); // Zero any buffer portion not filled by the file read. TODO: De-normals required here? if((size_t)rn != sreq) { const size_t zeros = (sreq - rn) * sf_chans; const size_t zero_start = rn * sf_chans; memset(&sfdata[zero_start], 0, zeros * sizeof(float)); } // Must de-interleave soundfile data to feed to rubberband. // TODO: Optimizations! Would like fast in-place de-interleaving, but it's "harder than it looks". // There are even patents on this kind of thing! // If optimized, be sure to move it to our AL-DSP (assembly) library. With SSE etc, it's easier. // Alas, for now use out-of-place memory since stack *should* be cheap here without much nesting. float rbindata[rbinSize]; float* rbinbuffer[sf_chans]; for(int i = 0; i < sf_chans; ++i) rbinbuffer[i] = rbindata + i * sreq; float* sfptr = sfdata; for(size_t i = 0; i < sreq; ++i) { for(int ch = 0; ch < sf_chans; ++ch) *(rbinbuffer[ch] + i) = *sfptr++; } if(d_pos <= debug_min_pos) { DEBUG_AUDIOCONVERT(stderr, " Normal: required:%d avail:%d\n", int(sreq), _rbs->available()); } _rbs->process(rbinbuffer, sreq, false); } int savail = _rbs->available(); if(d_pos <= debug_min_pos) { DEBUG_AUDIOCONVERT(stderr, " Normal: final avail:%d\n", _rbs->available()); } if(savail > outFrames) savail = outFrames; if(_latencyCompPending && old_lat > 0) { size_t retrieved = _rbs->retrieve(rboutbuffer, old_lat); if((int)retrieved < old_lat) { DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::process: Latency retrieved_count:%d is less than requested:%d\n", int(retrieved), old_lat); } savail -= old_lat; } _latencyCompPending = false; // Retrieves de-interleaved data. size_t retrieved = _rbs->retrieve(rboutbuffer, savail); if((int)retrieved < savail) { DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::process: retrieved_count:%d is less than savail:%d\n", int(retrieved), savail); savail = retrieved; } if(savail < frames) { DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::process: savail:%d is less than needed frames:%d. Converter is finished ???\n", savail, frames); // We didn't get the total required frames. Zero the rest. const size_t zeros = frames - savail; for(int i = 0; i < sf_chans; ++i) memset(rboutbuffer[i] + savail, 0, zeros * sizeof(float)); } if(sf_chans == channels) { if(overwrite) for(int ch = 0; ch < channels; ++ch) for(int i = 0; i < frames; ++i) *(buffer[ch] + i) = *(rboutbuffer[ch] + i); else for(int ch = 0; ch < channels; ++ch) for(int i = 0; i < frames; ++i) *(buffer[ch] + i) += *(rboutbuffer[ch] + i); } else if((sf_chans == 2) && (channels == 1)) { // stereo to mono if(overwrite) for(int i = 0; i < frames; ++i) *(buffer[0] + i) = *(rboutbuffer[0] + i) + *(rboutbuffer[1] + i); else for(int i = 0; i < frames; ++i) *(buffer[0] + i) += *(rboutbuffer[0] + i) + *(rboutbuffer[1] + i); } else if((sf_chans == 1) && (channels == 2)) { // mono to stereo float data; if(overwrite) for(int i = 0; i < frames; ++i) { data = *(rboutbuffer[0] + i); *(buffer[0]+i) = data; *(buffer[1]+i) = data; } else for(int i = 0; i < frames; ++i) { data = *(rboutbuffer[0] + i); *(buffer[0]+i) += data; *(buffer[1]+i) += data; } } else { DEBUG_AUDIOCONVERT(stderr, "RubberBandAudioConverter::process Channel mismatch: source chans:%d -> dst chans:%d\n", sf_chans, channels); } return frames; } #else // RUBBERBAND_SUPPORT int RubberBandAudioConverter::process( SNDFILE* /*sf_handle*/, const int /*sf_chans*/, const double /*sf_sr_ratio*/, const StretchList* /*sf_stretch_list*/, const sf_count_t /*pos*/, float** /*buffer*/, const int /*channels*/, const int /*frames*/, const bool /*overwrite*/) { return 0; } #endif // RUBBERBAND_SUPPORT //--------------------------------------------------------- // RubberBandAudioConverterSettings //--------------------------------------------------------- void RubberBandAudioConverterOptions::write(int level, Xml& xml) const { xml.tag(level++, "settings mode=\"%d\"", _mode); xml.intTag(level, "useSettings", _useSettings); xml.intTag(level, "options", _options); xml.tag(--level, "/settings"); } void RubberBandAudioConverterOptions::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "useSettings") _useSettings = xml.parseInt(); else if (tag == "options") _options = xml.parseInt(); else xml.unknown("settings"); break; case Xml::Attribut: fprintf(stderr, "settings unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (tag == "settings") { return; } default: break; } } } //--------------------------------------------------------- // RubberBandAudioConverterSettings //--------------------------------------------------------- // Some hard-coded defaults. #ifdef RUBBERBAND_SUPPORT const RubberBandAudioConverterOptions RubberBandAudioConverterOptions:: defaultOfflineOptions(false, AudioConverterSettings::OfflineMode, RubberBand::RubberBandStretcher::DefaultOptions); const RubberBandAudioConverterOptions RubberBandAudioConverterOptions:: defaultRealtimeOptions(false, AudioConverterSettings::RealtimeMode, RubberBand::RubberBandStretcher::DefaultOptions); const RubberBandAudioConverterOptions RubberBandAudioConverterOptions:: defaultGuiOptions(false, AudioConverterSettings::GuiMode, RubberBand::RubberBandStretcher::DefaultOptions); #else const RubberBandAudioConverterOptions RubberBandAudioConverterOptions:: defaultOfflineOptions(false, AudioConverterSettings::OfflineMode, 0); const RubberBandAudioConverterOptions RubberBandAudioConverterOptions:: defaultRealtimeOptions(false, AudioConverterSettings::RealtimeMode, 0); const RubberBandAudioConverterOptions RubberBandAudioConverterOptions:: defaultGuiOptions(false, AudioConverterSettings::GuiMode, 0); #endif RubberBandAudioConverterSettings::RubberBandAudioConverterSettings( bool isLocal) : AudioConverterSettings(descriptor._ID) { initOptions(isLocal); } void RubberBandAudioConverterSettings::assign(const AudioConverterSettings& other) { const RubberBandAudioConverterSettings& rb_other = (const RubberBandAudioConverterSettings&)other; _offlineOptions = rb_other._offlineOptions; _realtimeOptions = rb_other._realtimeOptions; _guiOptions = rb_other._guiOptions; } bool RubberBandAudioConverterSettings::useSettings(int mode) const { if(mode > 0 && (mode & ~(AudioConverterSettings::OfflineMode | AudioConverterSettings::RealtimeMode | AudioConverterSettings::GuiMode))) fprintf(stderr, "RubberBandAudioConverterSettings::useSettings() Warning: Unknown modes included:%d\n", mode); if((mode <= 0 || (mode & AudioConverterSettings::OfflineMode)) && _offlineOptions.useSettings()) return true; if((mode <= 0 || (mode & AudioConverterSettings::RealtimeMode)) && _realtimeOptions.useSettings()) return true; if((mode <= 0 || (mode & AudioConverterSettings::GuiMode)) && _guiOptions.useSettings()) return true; return false; } int RubberBandAudioConverterSettings::executeUI( ModeType mode, QWidget* parent, bool isLocal) { MusEGui::RubberbandSettingsDialog dlg(mode, parent, this, isLocal); return dlg.exec(); } void RubberBandAudioConverterSettings::write(int level, Xml& xml) const { const bool use_off = !(_offlineOptions == RubberBandAudioConverterOptions::defaultOfflineOptions); const bool use_rt = !(_realtimeOptions == RubberBandAudioConverterOptions::defaultRealtimeOptions); const bool use_gui = !(_guiOptions == RubberBandAudioConverterOptions::defaultGuiOptions); if(use_off | use_rt || use_gui) { xml.tag(level++, "audioConverterSetting name=\"%s\"", Xml::xmlString(descriptor._name).toLatin1().constData()); if(use_off) { _offlineOptions.write(level, xml); } if(use_rt) { _realtimeOptions.write(level, xml); } if(use_gui) { _guiOptions.write(level, xml); } xml.tag(--level, "/audioConverterSetting"); } } void RubberBandAudioConverterSettings::read(Xml& xml) { int mode = -1; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if(mode != -1) { RubberBandAudioConverterOptions* opts = NULL; switch(mode) { case AudioConverterSettings::OfflineMode: opts = &_offlineOptions; break; case AudioConverterSettings::RealtimeMode: opts = &_realtimeOptions; break; case AudioConverterSettings::GuiMode: opts = &_guiOptions; break; } if(opts) { if(tag == "useSettings") opts->_useSettings = xml.parseInt(); else if(tag == "options") opts->_options = xml.parseInt(); } } else xml.unknown("settings"); break; case Xml::Attribut: if (tag == "mode") mode = xml.s2().toInt(); else fprintf(stderr, "settings unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (tag == "settings") { return; } default: break; } } } } // namespace MusECore //===================================================================== namespace MusEGui { RubberbandSettingsDialog::RubberbandSettingsDialog( MusECore::AudioConverterSettings::ModeType mode, QWidget* parent, MusECore::AudioConverterSettings* settings, bool isLocal) : QDialog(parent) { setupUi(this); OKButton->setEnabled(false); _options = NULL; if(settings) { MusECore::RubberBandAudioConverterSettings* rb_settings = static_cast(settings); switch(mode) { case MusECore::AudioConverterSettings::OfflineMode: _options = rb_settings->offlineOptions(); break; case MusECore::AudioConverterSettings::RealtimeMode: _options = rb_settings->realtimeOptions(); break; case MusECore::AudioConverterSettings::GuiMode: _options = rb_settings->guiOptions(); break; default: // Disable everything and return. break; } } #ifdef RUBBERBAND_SUPPORT //if(isLocal) useDefaultSettings->setChecked(!_options || !_options->_useSettings); useDefaultSettings->setEnabled(isLocal && _options); useDefaultSettings->setVisible(isLocal && _options); groupScrollArea->setEnabled(!isLocal || (_options && _options->_useSettings)); presetsGroup->setEnabled(!isLocal || (_options && _options->_useSettings)); warningLabel->setVisible(false); if(_options) setControls(_options->_options); #else useDefaultSettings->setEnabled(false); useDefaultSettings->setVisible(isLocal && _options); groupScrollArea->setEnabled(false); presetsGroup->setEnabled(false); warningLabel->setVisible(true); #endif QList allButtons = groupScrollArea->findChildren(); foreach(QRadioButton* button, allButtons) { connect(button, &QRadioButton::clicked, [this]() { buttonClicked(ConverterButtonId); } ); } connect(defaultPreset, &QRadioButton::clicked, [this]() { buttonClicked(DefaultPresetId); } ); connect(percussionPreset, &QRadioButton::clicked, [this]() { buttonClicked(PercussionPresetId); } ); connect(maxPreset, &QRadioButton::clicked, [this]() { buttonClicked(MaxPresetId); } ); connect(useDefaultSettings, &QCheckBox::clicked, [this]() { buttonClicked(DefaultsButtonId); } ); connect(OKButton, &QPushButton::clicked, [this]() { buttonClicked(OkButtonId); } ); connect(cancelButton, &QPushButton::clicked, [this]() { buttonClicked(CancelButtonId); } ); } void RubberbandSettingsDialog::setControls( #ifdef RUBBERBAND_SUPPORT int opts #else int /*opts*/ #endif ) { #ifdef RUBBERBAND_SUPPORT if(opts & RubberBand::RubberBandStretcher::OptionStretchPrecise) { stretchPrecise->blockSignals(true); stretchPrecise->setChecked(true); stretchPrecise->blockSignals(false); } else { stretchElastic->blockSignals(true); stretchElastic->setChecked(true); stretchElastic->blockSignals(false); } if(opts & RubberBand::RubberBandStretcher::OptionTransientsMixed) { transientsMixed->blockSignals(true); transientsMixed->setChecked(true); transientsMixed->blockSignals(false); } else if(opts & RubberBand::RubberBandStretcher::OptionTransientsSmooth) { transientsSmooth->blockSignals(true); transientsSmooth->setChecked(true); transientsSmooth->blockSignals(false); } else { transientsCrisp->blockSignals(true); transientsCrisp->setChecked(true); transientsCrisp->blockSignals(false); } if(opts & RubberBand::RubberBandStretcher::OptionDetectorPercussive) { detectorPercussive->blockSignals(true); detectorPercussive->setChecked(true); detectorPercussive->blockSignals(false); } else if(opts & RubberBand::RubberBandStretcher::OptionDetectorSoft) { detectorSoft->blockSignals(true); detectorSoft->setChecked(true); detectorSoft->blockSignals(false); } else { detectorCompound->blockSignals(true); detectorCompound->setChecked(true); detectorCompound->blockSignals(false); } if(opts & RubberBand::RubberBandStretcher::OptionPhaseIndependent) { phaseIndependent->blockSignals(true); phaseIndependent->setChecked(true); phaseIndependent->blockSignals(false); } else { phaseLaminar->blockSignals(true); phaseLaminar->setChecked(true); phaseLaminar->blockSignals(false); } // if(opts & RubberBand::RubberBandStretcher::OptionThreadingNever) // { // threadingNever->blockSignals(true); // threadingNever->setChecked(true); // threadingNever->blockSignals(false); // } // else if(opts & RubberBand::RubberBandStretcher::OptionThreadingAlways) // { // threadingAlways->blockSignals(true); // threadingAlways->setChecked(true); // threadingAlways->blockSignals(false); // } // else // { // threadingAuto->blockSignals(true); // threadingAuto->setChecked(true); // threadingAuto->blockSignals(false); // } if(opts & RubberBand::RubberBandStretcher::OptionWindowShort) { windowShort->blockSignals(true); windowShort->setChecked(true); windowShort->blockSignals(false); } else if(opts & RubberBand::RubberBandStretcher::OptionWindowLong) { windowLong->blockSignals(true); windowLong->setChecked(true); windowLong->blockSignals(false); } else { windowStandard->blockSignals(true); windowStandard->setChecked(true); windowStandard->blockSignals(false); } if(opts & RubberBand::RubberBandStretcher::OptionSmoothingOn) { smoothingOn->blockSignals(true); smoothingOn->setChecked(true); smoothingOn->blockSignals(false); } else { smoothingOff->blockSignals(true); smoothingOff->setChecked(true); smoothingOff->blockSignals(false); } if(opts & RubberBand::RubberBandStretcher::OptionFormantPreserved) { formantPreserved->blockSignals(true); formantPreserved->setChecked(true); formantPreserved->blockSignals(false); } else { formantShifted->blockSignals(true); formantShifted->setChecked(true); formantShifted->blockSignals(false); } if(opts & RubberBand::RubberBandStretcher::OptionPitchHighQuality) { pitchHighQuality->blockSignals(true); pitchHighQuality->setChecked(true); pitchHighQuality->blockSignals(false); } else if(opts & RubberBand::RubberBandStretcher::OptionPitchHighConsistency) { pitchHighConsistency->blockSignals(true); pitchHighConsistency->setChecked(true); pitchHighConsistency->blockSignals(false); } else { pitchHighSpeed->blockSignals(true); pitchHighSpeed->setChecked(true); pitchHighSpeed->blockSignals(false); } if(opts & RubberBand::RubberBandStretcher::OptionChannelsTogether) { channelsTogether->blockSignals(true); channelsTogether->setChecked(true); channelsTogether->blockSignals(false); } else { channelsApart->blockSignals(true); channelsApart->setChecked(true); channelsApart->blockSignals(false); } #endif } void RubberbandSettingsDialog::buttonClicked(int idx) { switch(idx) { case DefaultsButtonId: OKButton->setEnabled(true); groupScrollArea->setEnabled(!useDefaultSettings->isChecked()); presetsGroup->setEnabled(!useDefaultSettings->isChecked()); break; case ConverterButtonId: OKButton->setEnabled(true); break; case OkButtonId: accept(); break; case CancelButtonId: reject(); break; #ifdef RUBBERBAND_SUPPORT case DefaultPresetId: setControls(RubberBand::RubberBandStretcher::DefaultOptions); OKButton->setEnabled(true); break; case PercussionPresetId: setControls(RubberBand::RubberBandStretcher::PercussiveOptions); OKButton->setEnabled(true); break; case MaxPresetId: setControls(RubberBand::RubberBandStretcher::OptionStretchPrecise | RubberBand::RubberBandStretcher::OptionTransientsSmooth | RubberBand::RubberBandStretcher::OptionDetectorSoft | RubberBand::RubberBandStretcher::OptionPhaseIndependent | RubberBand::RubberBandStretcher::OptionWindowLong | RubberBand::RubberBandStretcher::OptionSmoothingOn | RubberBand::RubberBandStretcher::OptionFormantPreserved | RubberBand::RubberBandStretcher::OptionPitchHighConsistency | RubberBand::RubberBandStretcher::OptionChannelsTogether); OKButton->setEnabled(true); break; #endif default: break; } } //--------------------------------------------------------- // accept //--------------------------------------------------------- void RubberbandSettingsDialog::accept() { if(!_options) { QDialog::accept(); return; } #ifdef RUBBERBAND_SUPPORT int opts = 0; if(stretchPrecise->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionStretchPrecise; else if(stretchElastic->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionStretchElastic; if(transientsMixed->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionTransientsMixed; else if(transientsSmooth->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionTransientsSmooth; else if(transientsCrisp->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionTransientsCrisp; if(detectorPercussive->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionDetectorPercussive; else if(detectorSoft->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionDetectorSoft; else if(detectorCompound->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionDetectorCompound; if(phaseIndependent->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionPhaseIndependent; else if(phaseLaminar->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionPhaseLaminar; // if(threadingNever->isChecked()) // opts |= RubberBand::RubberBandStretcher::OptionThreadingNever; // else if(threadingAlways->isChecked()) // opts |= RubberBand::RubberBandStretcher::OptionThreadingAlways; // else if(threadingAuto->isChecked()) // opts |= RubberBand::RubberBandStretcher::OptionThreadingAuto; if(windowShort->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionWindowShort; else if(windowLong->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionWindowLong; else if(windowStandard->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionWindowStandard; if(smoothingOn->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionSmoothingOn; else if(smoothingOff->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionSmoothingOff; if(formantPreserved->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionFormantPreserved; else if(formantShifted->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionFormantShifted; if(pitchHighQuality->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionPitchHighQuality; else if(pitchHighConsistency->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionPitchHighConsistency; else if(pitchHighSpeed->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionPitchHighSpeed; if(channelsTogether->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionChannelsTogether; else if(channelsApart->isChecked()) opts |= RubberBand::RubberBandStretcher::OptionChannelsApart; _options->_options = opts; _options->_useSettings = !useDefaultSettings->isChecked(); #endif QDialog::accept(); } } // namespace MusEGui muse-4.0.0/src/audio_convert/rubberband/rubberband_converter.h000066400000000000000000000161431404105311000245620ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // rubberband_converter.h // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __RUBBERBAND_CONVERTER_H__ #define __RUBBERBAND_CONVERTER_H__ #include "config.h" #ifdef RUBBERBAND_SUPPORT #include #endif #include #include #include "xml.h" #include "ui_rubberband_settings_base.h" #include "audio_convert/lib_audio_convert/audioconvert.h" namespace MusECore { //--------------------------------------------------------- // RubberBandAudioConverter //--------------------------------------------------------- struct RubberBandAudioConverterOptions { // Some hard-coded defaults. static const RubberBandAudioConverterOptions defaultOfflineOptions; static const RubberBandAudioConverterOptions defaultRealtimeOptions; static const RubberBandAudioConverterOptions defaultGuiOptions; AudioConverterSettings::ModeType _mode; // Whether to use these settings or defer to // some higher default settings if available. // The highest level (defaults) will ignore this value. bool _useSettings; int _options; RubberBandAudioConverterOptions(bool useSettings = false, AudioConverterSettings::ModeType mode = AudioConverterSettings::OfflineMode, #ifdef RUBBERBAND_SUPPORT int options = RubberBand::RubberBandStretcher::DefaultOptions) #else int options = 0) #endif { initOptions(useSettings, mode, options); } void initOptions(bool useSettings, AudioConverterSettings::ModeType mode, int options) { _mode = mode; _useSettings = useSettings; _options = options; } void read(Xml&); void write(int, Xml&) const; // Returns whether to use these settings or defer to default settings. bool useSettings() const { return _useSettings; } bool isDefault() const { return *this == defaultOfflineOptions; } bool operator==(const RubberBandAudioConverterOptions& other) const { return other._useSettings == _useSettings && other._options == _options; } }; class RubberBandAudioConverterSettings : public AudioConverterSettings { protected: RubberBandAudioConverterOptions _realtimeOptions; RubberBandAudioConverterOptions _offlineOptions; RubberBandAudioConverterOptions _guiOptions; public: RubberBandAudioConverterSettings(bool isLocal); void assign(const AudioConverterSettings&); void initOptions(bool /*isLocal*/) { _offlineOptions.initOptions( RubberBandAudioConverterOptions::defaultOfflineOptions._useSettings, AudioConverterSettings::OfflineMode, RubberBandAudioConverterOptions::defaultOfflineOptions._options); _realtimeOptions.initOptions( RubberBandAudioConverterOptions::defaultRealtimeOptions._useSettings, AudioConverterSettings::RealtimeMode, RubberBandAudioConverterOptions::defaultRealtimeOptions._options); _guiOptions.initOptions( RubberBandAudioConverterOptions::defaultGuiOptions._useSettings, AudioConverterSettings::GuiMode, RubberBandAudioConverterOptions::defaultGuiOptions._options);} RubberBandAudioConverterOptions* offlineOptions() { return &_offlineOptions; } RubberBandAudioConverterOptions* realtimeOptions() { return &_realtimeOptions; } RubberBandAudioConverterOptions* guiOptions() { return &_guiOptions; } int executeUI(ModeType mode, QWidget* parent = NULL, bool isLocal = false); void read(Xml&); void write(int, Xml&) const; // Returns whether to use these settings or defer to default settings. // Mode is a combination of AudioConverterSettings::ModeType selecting // which of the settings to check. Can also be <= 0, meaning all. bool useSettings(int mode = -1) const; bool isDefault() const { return _offlineOptions == RubberBandAudioConverterOptions::defaultOfflineOptions && _realtimeOptions == RubberBandAudioConverterOptions::defaultRealtimeOptions && _guiOptions == RubberBandAudioConverterOptions::defaultGuiOptions; } }; class RubberBandAudioConverter : public AudioConverter { private: bool _latencyCompPending; int _options; #ifdef RUBBERBAND_SUPPORT RubberBand::RubberBandStretcher* _rbs; #endif public: // Mode is an AudioConverterSettings::ModeType selecting which of the settings to use. RubberBandAudioConverter(int systemSampleRate, int channels, AudioConverterSettings* settings, AudioConverterSettings::ModeType mode); ~RubberBandAudioConverter(); bool isValid() const #ifdef RUBBERBAND_SUPPORT { return _rbs != 0; } #else { return false; } #endif void reset(); void setChannels(int ch); AudioConverterSettings::ModeType mode() const; // Make sure beforehand that sf samplerate is not <= 0. int process( SNDFILE* sf_handle, const int sf_chans, const double sf_sr_ratio, const StretchList* sf_stretch_list, const sf_count_t pos, float** buffer, const int channels, const int frames, const bool overwrite); }; } // namespace MusECore //============================================================================= namespace MusEGui { //--------------------------------------------------------- // RubberbandSettingsDialog //--------------------------------------------------------- class RubberbandSettingsDialog : public QDialog, public Ui::RubberbandSettingsBase { Q_OBJECT MusECore::RubberBandAudioConverterOptions* _options; void setControls(int opts); private slots: virtual void buttonClicked(int); virtual void accept(); public: enum buttonId { DefaultsButtonId, ConverterButtonId, OkButtonId, CancelButtonId, DefaultPresetId, PercussionPresetId, MaxPresetId }; RubberbandSettingsDialog(MusECore::AudioConverterSettings::ModeType mode, QWidget* parent = NULL, MusECore::AudioConverterSettings* settings = NULL, bool isLocal = false); }; } // namespace MusEGui #endif muse-4.0.0/src/audio_convert/rubberband/rubberband_settings_base.ui000066400000000000000000000267411404105311000256000ustar00rootroot00000000000000 RubberbandSettingsBase 0 0 320 469 Rubberband Settings 10 75 true Rubberband support is not enabled Use default settings Presets: Default Percussion Maximum Qt::Horizontal 20 20 true 0 0 282 1031 0 0 Stretch: Elastic Precise Window: Standard Short Long Smoothing: Off On Channels: Apart Together Qt::Horizontal 0 0 Transients: Crisp Mixed Smooth Detector: Compound Percussive Soft Qt::Horizontal Phase: Laminar Independent Formant: Shifted Preserved Pitch: High speed High quality High consistency Qt::Horizontal 40 20 0 0 OK true Cancel muse-4.0.0/src/audio_convert/src_resampler/000077500000000000000000000000001404105311000207425ustar00rootroot00000000000000muse-4.0.0/src/audio_convert/src_resampler/CMakeLists.txt000066400000000000000000000050341404105311000235040ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # src_resampler/CMakeLists.txt # (C) Copyright 2016 Tim E. Real (terminator356 A T sourceforge D O T net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( src_converter_mocs src_converter.h ) ## ## UI files ## file (GLOB src_converter_ui_files src_resampler_settings_base.ui ) QT5_WRAP_UI ( src_converter_uis ${src_converter_ui_files} ) ## ## List of source files to compile ## file (GLOB src_converter_source_files src_converter.cpp ) ## ## Define target ## # add_library ( src_converter ${MODULES_BUILD} # add_library ( src_converter MODULE add_library ( src_converter SHARED ${src_converter_source_files} ${src_converter_mocs} ${src_converter_uis} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${src_converter_source_files} ${src_converter_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## # - tell cmake to name target src_converter.so instead of # libsrc_converter.so # - use precompiled header files # set_target_properties ( src_converter PROPERTIES PREFIX "" COMPILE_FLAGS "-fvisibility=hidden" # OUTPUT_NAME muse_src_converter ) ## ## Linkage ## target_link_libraries(src_converter audio_convert # Note that this also appears in the core. ${SNDFILE_LIBRARIES} ${SAMPLERATE_LIBRARIES} ${QT_LIBRARIES} ) ## ## Install location ## install( TARGETS src_converter DESTINATION ${MusE_AUDIO_CONVERTER_PLUGINS_DIR} ) # install( FILES ChangeLog COPYING README TODO # DESTINATION ${MusE_DOC_DIR}/src_converter # ) muse-4.0.0/src/audio_convert/src_resampler/src_converter.cpp000066400000000000000000000623021404105311000243270ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // src_converter.cpp // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // //========================================================= #include #include #include "muse_math.h" #include "time_stretch.h" #include "src_converter.h" // For debugging output: Uncomment the fprintf section. #define ERROR_AUDIOCONVERT(dev, format, args...) fprintf(dev, format, ##args) #define DEBUG_AUDIOCONVERT(dev, format, args...) // fprintf(dev, format, ##args) // Fixed audio input buffer size. #define SRC_IN_BUFFER_FRAMES 1024 // Create a new instance of the plugin. // Mode is an AudioConverterSettings::ModeType selecting which of the settings to use. MusECore::AudioConverter* instantiate(int systemSampleRate, const MusECore::AudioConverterDescriptor* /*Descriptor*/, int channels, MusECore::AudioConverterSettings* settings, MusECore::AudioConverterSettings::ModeType mode) { return new MusECore::SRCAudioConverter(systemSampleRate, channels, settings, mode); } // Destroy the instance after usage. void cleanup(MusECore::AudioConverterHandle instance) { MusECore::AudioConverter::release(instance); } // Destroy the instance after usage. void cleanupSettings(MusECore::AudioConverterSettings* instance) { delete instance; } // Creates a new settings instance. Caller is responsible for deleting the returned object. // Settings will initialize normally. or with 'don't care', if isLocal is false or true resp. MusECore::AudioConverterSettings* createSettings(bool isLocal) { return new MusECore::SRCAudioConverterSettings(isLocal); } extern "C" { static MusECore::AudioConverterDescriptor descriptor = { 1001, MusECore::AudioConverter::SampleRate, "SRC Resampler", "SRC", -1, 1.0, 1.0, // "SRC is capable of ... downsampling by a factor of 256 to upsampling by the same factor." 0.004, 256.0, 1.0, 1.0, instantiate, cleanup, createSettings, cleanupSettings }; // We must compile with -fvisibility=hidden to avoid namespace // conflicts with global variables. // Only visible symbol is "audio_converter_descriptor". // (TODO: all plugins should be compiled this way) __attribute__ ((visibility("default"))) const MusECore::AudioConverterDescriptor* audio_converter_descriptor(unsigned long i) { return (i == 0) ? &descriptor : 0; } } //====================================================================== namespace MusECore { //--------------------------------------------------------- // SRCAudioConverter //--------------------------------------------------------- SRCAudioConverter::SRCAudioConverter( int systemSampleRate, int channels, AudioConverterSettings* settings, AudioConverterSettings::ModeType mode) : AudioConverter(systemSampleRate, mode) { DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::SRCAudioConverter this:%p channels:%d mode:%d\n", this, channels, mode); SRCAudioConverterSettings* src_settings = static_cast(settings); switch(mode) { case AudioConverterSettings::OfflineMode: _type = (src_settings ? src_settings->offlineOptions()->_converterType : 0); break; case AudioConverterSettings::RealtimeMode: _type = (src_settings ? src_settings->realtimeOptions()->_converterType : 0); break; case AudioConverterSettings::GuiMode: _type = (src_settings ? src_settings->guiOptions()->_converterType : 0); break; default: _type = 0; break; } _src_state = 0; _channels = channels; _inBufferSize = SRC_IN_BUFFER_FRAMES * _channels; _inbuffer = new float[_inBufferSize]; _curInBufferFrame = 0; _needBuffer = true; // Reset the SRC_DATA structure. resetSrcData(); int srcerr; DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::SRCaudioConverter Creating samplerate converter type:%d with %d channels\n", _type, _channels); _src_state = src_new(_type, _channels, &srcerr); if(!_src_state) { ERROR_AUDIOCONVERT(stderr, "SRCAudioConverter::SRCaudioConverter Creation of samplerate converter type:%d with %d channels failed:%s\n", _type, _channels, src_strerror(srcerr)); } } SRCAudioConverter::~SRCAudioConverter() { DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::~SRCAudioConverter this:%p\n", this); if(_inbuffer) delete[] _inbuffer; if(_src_state) src_delete(_src_state); } void SRCAudioConverter::setChannels(int ch) { DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::setChannels this:%p channels:%d\n", this, ch); if(_src_state) src_delete(_src_state); _src_state = 0; _channels = ch; if(_inbuffer) delete[] _inbuffer; _inBufferSize = SRC_IN_BUFFER_FRAMES * _channels; _inbuffer = new float[_inBufferSize]; int srcerr; DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::setChannels Creating samplerate converter type:%d with %d channels\n", _type, ch); _src_state = src_new(_type, ch, &srcerr); if(!_src_state) { ERROR_AUDIOCONVERT(stderr, "SRCAudioConverter::setChannels of samplerate converter type:%d with %d channels failed:%s\n", _type, ch, src_strerror(srcerr)); } return; } void SRCAudioConverter::reset() { resetSrcData(); if(!_src_state) return; DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::reset this:%p\n", this); int srcerr = src_reset(_src_state); if(srcerr != 0) { ERROR_AUDIOCONVERT(stderr, "SRCAudioConverter::reset Converter reset failed: %s\n", src_strerror(srcerr)); } return; } void SRCAudioConverter::resetSrcData() { _curInBufferFrame = 0; _needBuffer = true; } AudioConverterSettings::ModeType SRCAudioConverter::mode() const { return _mode; } int SRCAudioConverter::process( SNDFILE* sf_handle, const int sf_chans, const double sf_sr_ratio, const StretchList* sf_stretch_list, const sf_count_t pos, float** buffer, const int channels, const int frames, const bool overwrite) { if(!_src_state) return 0; if(_systemSampleRate <= 0) { DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::process Error: systemSampleRate <= 0!\n"); //return _sfCurFrame; return 0; } const double d_pos = sf_sr_ratio * double(pos); const MuseFrame_t unsquished_pos = sf_stretch_list->unSquish(d_pos); sf_count_t outSize = frames * sf_chans; // Start with buffers at expected sizes. float outbuffer[outSize]; sf_count_t totalOutFrames = 0; _srcdata.data_out = outbuffer; const double debug_min_pos = 120; bool need_slice = true; double cur_ratio = 1.0; ciStretchListItem next_isli = sf_stretch_list->upper_bound(unsquished_pos); // fprintf(stderr, "SRCAudioConverter::process pos:%ld unsquished_pos:%ld\n", // pos, unsquished_pos); if(next_isli != sf_stretch_list->begin()) { --next_isli; cur_ratio = next_isli->second._samplerateRatio; } if(next_isli != sf_stretch_list->end()) next_isli = sf_stretch_list->cNextEvent(StretchListItem::SamplerateEvent, next_isli); double fin_samplerateRatio, inv_fin_samplerateRatio; double d_cur_squished_pos; const double d_end_squished_pos = d_pos + frames; sf_count_t outFrames = frames - totalOutFrames; // Set some kind of limit on the number of attempts to completely fill the output buffer, // in case something is really screwed up - we don't want to get stuck in a loop here. int attempts = 20; if(d_pos <= debug_min_pos) { DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::process d_pos:%f d_end_pos:%f unsquished_pos:%ld\n", d_pos, d_end_squished_pos, unsquished_pos); } while(totalOutFrames < frames && attempts > 0) { d_cur_squished_pos = d_pos + totalOutFrames; if(d_pos <= debug_min_pos) { DEBUG_AUDIOCONVERT(stderr, " d_cur_squished_pos:%ld outFrames:%ld totalOutFrames:%ld\n", d_cur_squished_pos, outFrames, totalOutFrames); } if(_needBuffer) { _srcdata.input_frames = sf_readf_float(sf_handle, _inbuffer, SRC_IN_BUFFER_FRAMES); _srcdata.end_of_input = _srcdata.input_frames != SRC_IN_BUFFER_FRAMES; // Zero any unread portion of the input buffer. for(int i = _srcdata.input_frames * sf_chans; i < _inBufferSize; ++i) *(_inbuffer + i) = 0.0f; _needBuffer = false; } else { _srcdata.input_frames = SRC_IN_BUFFER_FRAMES - _curInBufferFrame; } _srcdata.data_in = _inbuffer + sf_chans * _curInBufferFrame; if(need_slice) { if(d_pos <= debug_min_pos) { DEBUG_AUDIOCONVERT(stderr, " new slice: cur_ratio:%f\n", cur_ratio); } fin_samplerateRatio = sf_sr_ratio * cur_ratio; if(fin_samplerateRatio < 0.000001) { DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::process Error: fin_samplerateRatio ratio is near zero!\n"); fin_samplerateRatio = 0.000001; } inv_fin_samplerateRatio = 1.0 / fin_samplerateRatio; if(next_isli != sf_stretch_list->end() && next_isli->second._finSquishedFrame >= d_cur_squished_pos && next_isli->second._finSquishedFrame < d_end_squished_pos) { cur_ratio = next_isli->second._samplerateRatio; outFrames = next_isli->second._finSquishedFrame - d_cur_squished_pos; if(d_pos <= debug_min_pos) { DEBUG_AUDIOCONVERT(stderr, " new next_isli: next cur_ratio:%f outFrames:%ld\n", cur_ratio, outFrames); } next_isli = sf_stretch_list->cNextEvent(StretchListItem::SamplerateEvent, next_isli); } else outFrames = frames - totalOutFrames; need_slice = false; } // "When using the src_process or src_callback_process APIs and // updating the src_ratio field of the SRC_STATE struct, // the library will try to smoothly transition between the // conversion ratio of the last call and the conversion ratio of the current call. // If the user want to bypass this smooth transition and achieve a step response // in the conversion ratio, the src_set_ratio function can be used to set the // starting conversion ratio of the next call to src_process or src_callback_process." _srcdata.src_ratio = inv_fin_samplerateRatio; //_srcdata.src_ratio = 1.0; // Set it to neutral I guess? src_set_ratio(_src_state, inv_fin_samplerateRatio); _srcdata.output_frames = outFrames; _srcdata.data_out = outbuffer + sf_chans * totalOutFrames; DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::process Calling src_process inv_fin_samplerateRatio:%f _curInBufferFrame:%d outFrames:%ld totalOutFrames:%ld\n", inv_fin_samplerateRatio, _curInBufferFrame, outFrames, totalOutFrames); int srcerr = src_process(_src_state, &_srcdata); if(srcerr != 0) { ERROR_AUDIOCONVERT(stderr, "SRCAudioConverter::process SampleRate converter process failed: %s\n", src_strerror(srcerr)); return 0; } DEBUG_AUDIOCONVERT(stderr, " input_frames_used:%ld output_frames_gen:%ld\n\n", _srcdata.input_frames_used, _srcdata.output_frames_gen); // Note that if SRC is fed more than it needs, the next process call(s) // will generate output but will report NO frames used! // If some input frames were used: if(_srcdata.input_frames_used > 0) { // Advance the current input frame. _curInBufferFrame += _srcdata.input_frames_used; // Rollover? if(_curInBufferFrame >= SRC_IN_BUFFER_FRAMES) { // Reset the counter. _curInBufferFrame = 0; // Request a new buffer. _needBuffer = true; } } totalOutFrames += _srcdata.output_frames_gen; // If we got the number of slice frames, ask for a new slice. if(_srcdata.output_frames_gen >= outFrames) need_slice = true; else outFrames -= _srcdata.output_frames_gen; if(_srcdata.input_frames == 0) break; // Countdown the attempts only if no output was generated. If at least something was generated allow it to keep trying indefinitely. if(_srcdata.output_frames_gen == 0) --attempts; } if(attempts == 0) { ERROR_AUDIOCONVERT(stderr, "SRCAudioConverter::process handle:%p stretch list:%p Too may attempts to process! totalOutFrames:%ld frames:%d\n", sf_handle, sf_stretch_list, totalOutFrames, frames); } // If we didn't get the desired number of output frames. if(totalOutFrames != frames) { DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::process %s totalOutFrames:%ld != frames:%d\n", sf->name().toLatin1().constData(), totalOutFrames, frames); // Let's zero the rest of it. sf_count_t b = totalOutFrames * channels; sf_count_t e = frames * channels; for(sf_count_t i = b; i < e; ++i) outbuffer[i] = 0.0f; } float* poutbuf = outbuffer; if(sf_chans == channels) { if(overwrite) for (sf_count_t i = 0; i < frames; ++i) { for(sf_count_t ch = 0; ch < channels; ++ch) *(buffer[ch] + i) = *poutbuf++; } else for(sf_count_t i = 0; i < frames; ++i) { for(sf_count_t ch = 0; ch < channels; ++ch) *(buffer[ch] + i) += *poutbuf++; } } else if((sf_chans == 2) && (channels == 1)) { // stereo to mono if(overwrite) for(sf_count_t i = 0; i < frames; ++i) *(buffer[0] + i) = poutbuf[i + i] + poutbuf[i + i + 1]; else for(sf_count_t i = 0; i < frames; ++i) *(buffer[0] + i) += poutbuf[i + i] + poutbuf[i + i + 1]; } else if((sf_chans == 1) && (channels == 2)) { // mono to stereo if(overwrite) for(sf_count_t i = 0; i < frames; ++i) { float data = *poutbuf++; *(buffer[0]+i) = data; *(buffer[1]+i) = data; } else for(sf_count_t i = 0; i < frames; ++i) { float data = *poutbuf++; *(buffer[0]+i) += data; *(buffer[1]+i) += data; } } else { DEBUG_AUDIOCONVERT(stderr, "SRCAudioConverter::process Channel mismatch: source chans:%d -> dst chans:%d\n", sf_chans, channels); } //return _sfCurFrame; return frames; } //--------------------------------------------------------- // SRCAudioConverterSettings //--------------------------------------------------------- void SRCAudioConverterOptions::write(int level, Xml& xml) const { xml.tag(level++, "settings mode=\"%d\"", _mode); xml.intTag(level, "useSettings", _useSettings); xml.intTag(level, "converterType", _converterType); xml.tag(--level, "/settings"); } void SRCAudioConverterOptions::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "useSettings") _useSettings = xml.parseInt(); else if (tag == "converterType") _converterType = xml.parseInt(); else xml.unknown("settings"); break; case Xml::Attribut: fprintf(stderr, "settings unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (tag == "settings") { return; } default: break; } } } //--------------------------------------------------------- // SRCAudioConverterSettings //--------------------------------------------------------- // Some hard-coded defaults. const SRCAudioConverterOptions SRCAudioConverterOptions::defaultOfflineOptions( false, AudioConverterSettings::OfflineMode, SRC_SINC_BEST_QUALITY); const SRCAudioConverterOptions SRCAudioConverterOptions::defaultRealtimeOptions( false, AudioConverterSettings::RealtimeMode, SRC_SINC_MEDIUM_QUALITY); const SRCAudioConverterOptions SRCAudioConverterOptions::defaultGuiOptions( false, AudioConverterSettings::GuiMode, SRC_SINC_FASTEST); SRCAudioConverterSettings::SRCAudioConverterSettings( bool isLocal) : AudioConverterSettings(descriptor._ID) { initOptions(isLocal); } void SRCAudioConverterSettings::assign(const AudioConverterSettings& other) { const SRCAudioConverterSettings& src_other = (const SRCAudioConverterSettings&)other; _offlineOptions = src_other._offlineOptions; _realtimeOptions = src_other._realtimeOptions; _guiOptions = src_other._guiOptions; } bool SRCAudioConverterSettings::useSettings(int mode) const { if(mode > 0 && (mode & ~(AudioConverterSettings::OfflineMode | AudioConverterSettings::RealtimeMode | AudioConverterSettings::GuiMode))) fprintf(stderr, "SRCAudioConverterSettings::useSettings() Warning: Unknown modes included:%d\n", mode); if((mode <= 0 || (mode & AudioConverterSettings::OfflineMode)) && _offlineOptions.useSettings()) return true; if((mode <= 0 || (mode & AudioConverterSettings::RealtimeMode)) && _realtimeOptions.useSettings()) return true; if((mode <= 0 || (mode & AudioConverterSettings::GuiMode)) && _guiOptions.useSettings()) return true; return false; } int SRCAudioConverterSettings::executeUI(ModeType mode, QWidget* parent, bool isLocal) { MusEGui::SRCResamplerSettingsDialog dlg(mode, parent, this, isLocal); return dlg.exec(); } void SRCAudioConverterSettings::write(int level, Xml& xml) const { const bool use_off = !(_offlineOptions == SRCAudioConverterOptions::defaultOfflineOptions); const bool use_rt = !(_realtimeOptions == SRCAudioConverterOptions::defaultRealtimeOptions); const bool use_gui = !(_guiOptions == SRCAudioConverterOptions::defaultGuiOptions); if(use_off | use_rt || use_gui) { xml.tag(level++, "audioConverterSetting name=\"%s\"", Xml::xmlString(descriptor._name).toLatin1().constData()); if(use_off) { _offlineOptions.write(level, xml); } if(use_rt) { _realtimeOptions.write(level, xml); } if(use_gui) { _guiOptions.write(level, xml); } xml.tag(--level, "/audioConverterSetting"); } } void SRCAudioConverterSettings::read(Xml& xml) { int mode = -1; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if(mode != -1) { SRCAudioConverterOptions* opts = NULL; switch(mode) { case AudioConverterSettings::OfflineMode: opts = &_offlineOptions; break; case AudioConverterSettings::RealtimeMode: opts = &_realtimeOptions; break; case AudioConverterSettings::GuiMode: opts = &_guiOptions; break; } if(opts) { if(tag == "useSettings") opts->_useSettings = xml.parseInt(); else if(tag == "converterType") opts->_converterType = xml.parseInt(); } } else xml.unknown("settings"); break; case Xml::Attribut: if (tag == "mode") mode = xml.s2().toInt(); else fprintf(stderr, "settings unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (tag == "settings") { return; } default: break; } } } } // namespace MusECore //========================================================== namespace MusEGui { SRCResamplerSettingsDialog::SRCResamplerSettingsDialog( MusECore::AudioConverterSettings::ModeType mode, QWidget* parent, MusECore::AudioConverterSettings* settings, bool isLocal) : QDialog(parent) { setupUi(this); OKButton->setEnabled(false); _options = NULL; if(settings) { MusECore::SRCAudioConverterSettings* src_settings = static_cast(settings); switch(mode) { case MusECore::AudioConverterSettings::OfflineMode: _options = src_settings->offlineOptions(); break; case MusECore::AudioConverterSettings::RealtimeMode: _options = src_settings->realtimeOptions(); break; case MusECore::AudioConverterSettings::GuiMode: _options = src_settings->guiOptions(); break; default: // Disable everything and return. break; } } //if(isLocal) useDefaultSettings->setChecked(!_options || !_options->_useSettings); useDefaultSettings->setEnabled(isLocal && _options); useDefaultSettings->setVisible(isLocal && _options); typeGroup->setEnabled(!isLocal || (_options && _options->_useSettings)); setControls(); connect(typeSINCBestQuality, &QRadioButton::clicked, [this]() { buttonClicked(ConverterButtonId); } ); connect(typeSINCMedium, &QRadioButton::clicked, [this]() { buttonClicked(ConverterButtonId); } ); connect(typeSINCFastest, &QRadioButton::clicked, [this]() { buttonClicked(ConverterButtonId); } ); connect(typeZeroOrderHold, &QRadioButton::clicked, [this]() { buttonClicked(ConverterButtonId); } ); connect(typeLinear, &QRadioButton::clicked, [this]() { buttonClicked(ConverterButtonId); } ); connect(useDefaultSettings, &QCheckBox::clicked, [this]() { buttonClicked(DefaultsButtonId); } ); connect(OKButton, &QPushButton::clicked, [this]() { buttonClicked(OkButtonId); } ); connect(cancelButton, &QPushButton::clicked, [this]() { buttonClicked(CancelButtonId); } ); } void SRCResamplerSettingsDialog::setControls() { if(!_options) return; switch(_options->_converterType) { case SRC_SINC_BEST_QUALITY: typeSINCBestQuality->blockSignals(true); typeSINCBestQuality->setChecked(true); typeSINCBestQuality->blockSignals(false); break; case SRC_SINC_MEDIUM_QUALITY: typeSINCMedium->blockSignals(true); typeSINCMedium->setChecked(true); typeSINCMedium->blockSignals(false); break; case SRC_SINC_FASTEST: typeSINCFastest->blockSignals(true); typeSINCFastest->setChecked(true); typeSINCFastest->blockSignals(false); break; case SRC_ZERO_ORDER_HOLD: typeZeroOrderHold->blockSignals(true); typeZeroOrderHold->setChecked(true); typeZeroOrderHold->blockSignals(false); break; case SRC_LINEAR: typeLinear->blockSignals(true); typeLinear->setChecked(true); typeLinear->blockSignals(false); break; default: break; } } void SRCResamplerSettingsDialog::buttonClicked(int idx) { switch(idx) { case DefaultsButtonId: OKButton->setEnabled(true); typeGroup->setEnabled(!useDefaultSettings->isChecked()); break; case ConverterButtonId: OKButton->setEnabled(true); break; case OkButtonId: accept(); break; case CancelButtonId: reject(); break; default: break; } } //--------------------------------------------------------- // accept //--------------------------------------------------------- void SRCResamplerSettingsDialog::accept() { if(!_options) { QDialog::accept(); return; } int type = -1; if(typeSINCBestQuality->isChecked()) type = SRC_SINC_BEST_QUALITY; else if(typeSINCMedium->isChecked()) type = SRC_SINC_MEDIUM_QUALITY; else if(typeSINCFastest->isChecked()) type = SRC_SINC_FASTEST; else if(typeZeroOrderHold->isChecked()) type = SRC_ZERO_ORDER_HOLD; else if(typeLinear->isChecked()) type = SRC_LINEAR; if(type != -1) _options->_converterType = type; _options->_useSettings = !useDefaultSettings->isChecked(); QDialog::accept(); } } // namespace MusEGui muse-4.0.0/src/audio_convert/src_resampler/src_converter.h000066400000000000000000000152031404105311000237720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // src_converter.h // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SRC_CONVERTER_H__ #define __SRC_CONVERTER_H__ #include #include #include #include "xml.h" #include "ui_src_resampler_settings_base.h" #include "audio_convert/lib_audio_convert/audioconvert.h" namespace MusECore { //--------------------------------------------------------- // SRCAudioConverter //--------------------------------------------------------- struct SRCAudioConverterOptions { // Some hard-coded defaults. static const SRCAudioConverterOptions defaultOfflineOptions; static const SRCAudioConverterOptions defaultRealtimeOptions; static const SRCAudioConverterOptions defaultGuiOptions; AudioConverterSettings::ModeType _mode; // Whether to use these settings or defer to // some higher default settings if available. // The highest level (defaults) will ignore this value. bool _useSettings; int _converterType; SRCAudioConverterOptions( bool useSettings = defaultOfflineOptions._useSettings, AudioConverterSettings::ModeType mode = AudioConverterSettings::OfflineMode, int converterType = defaultOfflineOptions._converterType) { initOptions(useSettings, mode, converterType); } void initOptions( bool useSettings, AudioConverterSettings::ModeType mode, int converterType) { _mode = mode; _useSettings = useSettings; _converterType = converterType; } void read(Xml&); void write(int, Xml&) const; // Returns whether to use these settings or defer to default settings. bool useSettings() const { return _useSettings; } bool isDefault() const { return *this == defaultOfflineOptions; } bool operator==(const SRCAudioConverterOptions& other) const { return other._useSettings == _useSettings && other._converterType == _converterType; } }; class SRCAudioConverterSettings : public AudioConverterSettings { protected: SRCAudioConverterOptions _offlineOptions; SRCAudioConverterOptions _realtimeOptions; SRCAudioConverterOptions _guiOptions; public: SRCAudioConverterSettings(bool isLocal); void assign(const AudioConverterSettings&); void initOptions(bool /*isLocal*/) { _offlineOptions.initOptions( SRCAudioConverterOptions::defaultOfflineOptions._useSettings, AudioConverterSettings::OfflineMode, SRCAudioConverterOptions::defaultOfflineOptions._converterType); _realtimeOptions.initOptions( SRCAudioConverterOptions::defaultRealtimeOptions._useSettings, AudioConverterSettings::RealtimeMode, SRCAudioConverterOptions::defaultRealtimeOptions._converterType); _guiOptions.initOptions( SRCAudioConverterOptions::defaultGuiOptions._useSettings, AudioConverterSettings::GuiMode, SRCAudioConverterOptions::defaultGuiOptions._converterType);} SRCAudioConverterOptions* offlineOptions() { return &_offlineOptions; } SRCAudioConverterOptions* realtimeOptions() { return &_realtimeOptions; } SRCAudioConverterOptions* guiOptions() { return &_guiOptions; } int executeUI(ModeType mode, QWidget* parent = NULL, bool isLocal = false); void read(Xml&); void write(int, Xml&) const; // Returns whether to use these settings or defer to default settings. // Mode is a combination of AudioConverterSettings::ModeType selecting // which of the settings to check. Can also be <= 0, meaning all. bool useSettings(int mode = -1) const; bool isDefault() const { return _offlineOptions == SRCAudioConverterOptions::defaultOfflineOptions && _realtimeOptions == SRCAudioConverterOptions::defaultRealtimeOptions && _guiOptions == SRCAudioConverterOptions::defaultGuiOptions; } }; class SRCAudioConverter : public AudioConverter { private: int _type; SRC_STATE* _src_state; SRC_DATA _srcdata; float* _inbuffer; int _inBufferSize; int _curInBufferFrame; bool _needBuffer; void resetSrcData(); public: // Mode is an AudioConverterSettings::ModeType selecting which of the settings to use. SRCAudioConverter(int systemSampleRate, int channels, AudioConverterSettings* settings, AudioConverterSettings::ModeType mode); ~SRCAudioConverter(); bool isValid() const { return _src_state != 0; } void reset(); void setChannels(int ch); AudioConverterSettings::ModeType mode() const; // Make sure beforehand that sf samplerate is not <= 0. int process( SNDFILE* sf_handle, const int sf_chans, const double sf_sr_ratio, const StretchList* sf_stretch_list, const sf_count_t pos, float** buffer, const int channels, const int frames, const bool overwrite); }; } // namespace MusECore //=========================================================================== namespace MusEGui { //--------------------------------------------------------- // SRCResamplerSettingsDialog //--------------------------------------------------------- class SRCResamplerSettingsDialog : public QDialog, public Ui::SRCResamplerSettingsBase { Q_OBJECT private: MusECore::SRCAudioConverterOptions* _options; void setControls(); private slots: virtual void buttonClicked(int); virtual void accept(); public: enum buttonId { DefaultsButtonId, ConverterButtonId, OkButtonId, CancelButtonId }; SRCResamplerSettingsDialog( MusECore::AudioConverterSettings::ModeType mode, QWidget* parent = NULL, MusECore::AudioConverterSettings* settings = NULL, bool isLocal = false); }; } // namespace MusEGui #endif muse-4.0.0/src/audio_convert/src_resampler/src_resampler_settings_base.ui000066400000000000000000000066261404105311000270660ustar00rootroot00000000000000 SRCResamplerSettingsBase 0 0 293 276 SRC Resampler Settings Use default settings 0 0 Converter Type: SINC Best quality SINC Medium quality SINC Fastest Zero Order Hold Linear Qt::Vertical 20 40 Qt::Horizontal 40 20 0 0 OK true Cancel muse-4.0.0/src/audio_convert/zita_resampler/000077500000000000000000000000001404105311000211225ustar00rootroot00000000000000muse-4.0.0/src/audio_convert/zita_resampler/CMakeLists.txt000066400000000000000000000053071404105311000236670ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # audio_convert/CMakeLists.txt # (C) Copyright 2016 Tim E. Real (terminator356 A T sourceforge D O T net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( zita_resampler_converter_mocs zita_resampler_converter.h ) ## ## UI files ## file (GLOB zita_resampler_converter_ui_files zita_resampler_settings_base.ui ) QT5_WRAP_UI ( zita_resampler_converter_uis ${zita_resampler_converter_ui_files} ) ## ## List of source files to compile ## file (GLOB zita_resampler_converter_source_files zita_resampler_converter.cpp ) ## ## Define target ## # add_library ( zita_resampler_converter ${MODULES_BUILD} # add_library ( zita_resampler_converter MODULE add_library ( zita_resampler_converter SHARED ${zita_resampler_converter_source_files} ${zita_resampler_converter_mocs} ${zita_resampler_converter_uis} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${zita_resampler_converter_source_files} ${zita_resampler_converter_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## # - tell cmake to name target zita_resampler_converter.so instead of # libzita_resampler_converter.so # - use precompiled header files # set_target_properties ( zita_resampler_converter PROPERTIES PREFIX "" COMPILE_FLAGS "-fvisibility=hidden" # OUTPUT_NAME muse_zita_resampler_converter ) ## ## Linkage ## target_link_libraries(zita_resampler_converter audio_convert ${ZITA_RESAMPLER_LIBRARIES} ${QT_LIBRARIES} ) ## ## Install location ## install( TARGETS zita_resampler_converter DESTINATION ${MusE_AUDIO_CONVERTER_PLUGINS_DIR} ) # install( FILES ChangeLog COPYING README TODO # DESTINATION ${MusE_DOC_DIR}/zita_resampler_converter # ) muse-4.0.0/src/audio_convert/zita_resampler/zita_resampler_converter.cpp000066400000000000000000000435711404105311000267500ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // zita_resampler_converter.cpp // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // //========================================================= #include #include #include "muse_math.h" #include "time_stretch.h" #include "zita_resampler_converter.h" // For debugging output: Uncomment the fprintf section. #define ERROR_AUDIOCONVERT(dev, format, args...) fprintf(dev, format, ##args) #define DEBUG_AUDIOCONVERT(dev, format, args...) // fprintf(dev, format, ##args) // Create a new instance of the plugin. // Mode is an AudioConverterSettings::ModeType selecting which of the settings to use. MusECore::AudioConverter* instantiate( int systemSampleRate, const MusECore::AudioConverterDescriptor* /*Descriptor*/, int channels, MusECore::AudioConverterSettings* settings, MusECore::AudioConverterSettings::ModeType mode) { return new MusECore::ZitaResamplerAudioConverter(systemSampleRate, channels, settings, mode); // TODO Pass SF? } // Destroy the instance after usage. void cleanup(MusECore::AudioConverterHandle Instance) { MusECore::AudioConverter::release(Instance); } // Destroy the instance after usage. void cleanupSettings(MusECore::AudioConverterSettings* instance) { delete instance; } // Creates a new settings instance. Caller is responsible for deleting the returned object. // Settings will initialize normally. or with 'don't care', if isLocal is false or true resp. MusECore::AudioConverterSettings* createSettings(bool isLocal) { return new MusECore::ZitaResamplerAudioConverterSettings(isLocal); } extern "C" { static MusECore::AudioConverterDescriptor descriptor = { 1002, MusECore::AudioConverter::SampleRate, "Zita Resampler", "Zita", -1, 1.0, 1.0, // "The VResampler class provides an arbitrary ratio r in the range 1/16 ≤ r ≤ 64 ..." 0.0625, 64.0, 1.0, 1.0, instantiate, cleanup, createSettings, cleanupSettings }; // We must compile with -fvisibility=hidden to avoid namespace // conflicts with global variables. // Only visible symbol is "audio_converter_descriptor". // (TODO: all plugins should be compiled this way) __attribute__ ((visibility("default"))) const MusECore::AudioConverterDescriptor* audio_converter_descriptor(unsigned long i) { return (i == 0) ? &descriptor : 0; } } //========================================================== namespace MusECore { //--------------------------------------------------------- // ZitaResamplerAudioConverter //--------------------------------------------------------- ZitaResamplerAudioConverter::ZitaResamplerAudioConverter( int systemSampleRate, int channels, AudioConverterSettings* /*settings*/, AudioConverterSettings::ModeType mode) : AudioConverter(systemSampleRate, mode) { DEBUG_AUDIOCONVERT(stderr, "ZitaResamplerAudioConverter::ZitaResamplerAudioConverter this:%p channels:%d mode:%d\n", this, channels, mode); // "... hlen = 32 should provide very high quality for F_min equal to // 48 kHz or higher, while hlen = 48 should be sufficient for an F_min of 44.1 kHz" // _filterHLen = 48; // TODO: // ZitaResamplerAudioConverterSettings* zita_settings = // static_cast(settings); // // switch(mode) // { // case AudioConverterSettings::OfflineMode: // _options = (zita_settings ? zita_settings->offlineOptions()->_options : 0); // break; // // case AudioConverterSettings::RealtimeMode: // _options = (zita_settings ? zita_settings->realtimeOptions()->_options : 0); // break; // // case AudioConverterSettings::GuiMode: // _options = (zita_settings ? zita_settings->guiOptions()->_options : 0); // break; // // default: // _options = 0; // break; // } _channels = channels; // _ratio = sf ? sf->sampleRateRatio() : 1.0; #ifdef ZITA_RESAMPLER_SUPPORT _rbs = new VResampler(); #endif #ifdef ZITA_RESAMPLER_SUPPORT _rbs->setup(_ratio, _channels, _filterHLen); #endif } ZitaResamplerAudioConverter::~ZitaResamplerAudioConverter() { DEBUG_AUDIOCONVERT(stderr, "ZitaResamplerAudioConverter::~ZitaResamplerAudioConverter this:%p\n", this); #ifdef ZITA_RESAMPLER_SUPPORT if(_rbs) delete _rbs; #endif } void ZitaResamplerAudioConverter::setChannels(int ch) { DEBUG_AUDIOCONVERT(stderr, "ZitaResamplerAudioConverter::setChannels this:%p channels:%d\n", this, ch); _channels = ch; #ifdef ZITA_RESAMPLER_SUPPORT if(_rbs) delete _rbs; _rbs->setup(_ratio, _channels, _filterHLen); #endif } void ZitaResamplerAudioConverter::reset() { DEBUG_AUDIOCONVERT(stderr, "ZitaResamplerAudioConverter::reset this:%p\n", this); #ifdef ZITA_RESAMPLER_SUPPORT if(!_rbs) return; _rbs->reset(); #endif return; } AudioConverterSettings::ModeType ZitaResamplerAudioConverter::mode() const { return _mode; } #ifdef ZITA_RESAMPLER_SUPPORT // TODO int ZitaResamplerAudioConverter::process( SNDFILE* sf_handle, const int sf_chans, const double sf_sr_ratio, const StretchList* sf_stretch_list, const sf_count_t pos, float** buffer, const int channels, const int frames, const bool overwrite) { if(!_rbs) return 0; if(_systemSampleRate <= 0) { DEBUG_AUDIOCONVERT(stderr, "ZitaResamplerAudioConverter::process Error: systemSampleRate <= 0!\n"); //return _sfCurFrame; return 0; } // TODO const double inv_sf_sr_ratio = 1.0 / sf_sr_ratio; fin_samplerateRatio = sf_sr_ratio * cur_ratio; if(fin_samplerateRatio < 0.000001) { DEBUG_AUDIOCONVERT(stderr, "ZitaResamplerAudioConverter::process Error: fin_samplerateRatio ratio is near zero!\n"); fin_samplerateRatio = 0.000001; } inv_fin_samplerateRatio = 1.0 / fin_samplerateRatio; sf_count_t outFrames = frames; sf_count_t outSize = outFrames * sf_chans; float* rboutbuffer[sf_chans]; float rboutdata[outSize]; for(int i = 0; i < sf_chans; ++i) rboutbuffer[i] = rboutdata + i * outFrames; sf_count_t rn = 0; // const bool zpad = false; // TODO: Option. // // int z1, z2; // // if (zpad) // { // z1 = _rbs->inpsize () - 1; // z2 = _rbs->inpsize () - 1; // } // else // { // z1 = _rbs->inpsize () / 2 - 1; // z2 = _rbs->inpsize () / 2; // } // // // Insert zero samples at start. // _rbs->inp_count = z1; // _rbs->inp_data = 0; // _rbs->out_count = BUFFSIZE; // _rbs->out_data = outb; // // bool done = false; // TODO: LEFT OFF HERE.... FINISH THIS.... _rbs->inp_count = z1; _rbs->inp_data = 0; _rbs->out_count = BUFFSIZE; _rbs->out_data = outb; return frames; } #else // ZITA_RESAMPLER_SUPPORT int ZitaResamplerAudioConverter::process( SNDFILE* /*sf_handle*/, const int /*sf_chans*/, const double /*sf_sr_ratio*/, const StretchList* /*sf_stretch_list*/, const sf_count_t /*pos*/, float** /*buffer*/, const int /*channels*/, const int /*frames*/, const bool /*overwrite*/) { return 0; } #endif // ZITA_RESAMPLER_SUPPORT //--------------------------------------------------------- // ZitaResamplerAudioConverterSettings //--------------------------------------------------------- void ZitaResamplerAudioConverterOptions::write(int level, Xml& xml) const { xml.tag(level++, "settings mode=\"%d\"", _mode); xml.intTag(level, "useSettings", _useSettings); xml.tag(--level, "/settings"); } void ZitaResamplerAudioConverterOptions::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "useSettings") _useSettings = xml.parseInt(); else xml.unknown("settings"); break; case Xml::Attribut: fprintf(stderr, "settings unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (tag == "settings") { return; } default: break; } } } //--------------------------------------------------------- // ZitaResamplerAudioConverterSettings //--------------------------------------------------------- // Some hard-coded defaults. #ifdef ZITA_RESAMPLER_SUPPORT const ZitaResamplerAudioConverterOptions ZitaResamplerAudioConverterOptions:: defaultOfflineOptions(false, AudioConverterSettings::OfflineMode); const ZitaResamplerAudioConverterOptions ZitaResamplerAudioConverterOptions:: defaultRealtimeOptions(false, AudioConverterSettings::RealtimeMode); const ZitaResamplerAudioConverterOptions ZitaResamplerAudioConverterOptions:: defaultGuiOptions(false, AudioConverterSettings::GuiMode); #else const ZitaResamplerAudioConverterOptions ZitaResamplerAudioConverterOptions:: defaultOfflineOptions(false, AudioConverterSettings::OfflineMode); const ZitaResamplerAudioConverterOptions ZitaResamplerAudioConverterOptions:: defaultRealtimeOptions(false, AudioConverterSettings::RealtimeMode); const ZitaResamplerAudioConverterOptions ZitaResamplerAudioConverterOptions:: defaultGuiOptions(false, AudioConverterSettings::GuiMode); #endif ZitaResamplerAudioConverterSettings::ZitaResamplerAudioConverterSettings( bool isLocal) : AudioConverterSettings(descriptor._ID) { initOptions(isLocal); } void ZitaResamplerAudioConverterSettings::assign(const AudioConverterSettings& other) { const ZitaResamplerAudioConverterSettings& zita_other = (const ZitaResamplerAudioConverterSettings&)other; _offlineOptions = zita_other._offlineOptions; _realtimeOptions = zita_other._realtimeOptions; _guiOptions = zita_other._guiOptions; } bool ZitaResamplerAudioConverterSettings::useSettings(int mode) const { if(mode > 0 && (mode & ~(AudioConverterSettings::OfflineMode | AudioConverterSettings::RealtimeMode | AudioConverterSettings::GuiMode))) fprintf(stderr, "ZitaResamplerAudioConverterSettings::useSettings() Warning: Unknown modes included:%d\n", mode); if((mode <= 0 || (mode & AudioConverterSettings::OfflineMode)) && _offlineOptions.useSettings()) return true; if((mode <= 0 || (mode & AudioConverterSettings::RealtimeMode)) && _realtimeOptions.useSettings()) return true; if((mode <= 0 || (mode & AudioConverterSettings::GuiMode)) && _guiOptions.useSettings()) return true; return false; } int ZitaResamplerAudioConverterSettings::executeUI( ModeType mode, QWidget* parent, bool isLocal) { MusEGui::ZitaResamplerSettingsDialog dlg(mode, parent, this, isLocal); return dlg.exec(); } void ZitaResamplerAudioConverterSettings::write(int level, Xml& xml) const { const bool use_off = !(_offlineOptions == ZitaResamplerAudioConverterOptions::defaultOfflineOptions); const bool use_rt = !(_realtimeOptions == ZitaResamplerAudioConverterOptions::defaultRealtimeOptions); const bool use_gui = !(_guiOptions == ZitaResamplerAudioConverterOptions::defaultGuiOptions); if(use_off | use_rt || use_gui) { xml.tag(level++, "audioConverterSetting name=\"%s\"", Xml::xmlString(descriptor._name).toLatin1().constData()); if(use_off) { _offlineOptions.write(level, xml); } if(use_rt) { _realtimeOptions.write(level, xml); } if(use_gui) { _guiOptions.write(level, xml); } xml.tag(--level, "/audioConverterSetting"); } } void ZitaResamplerAudioConverterSettings::read(Xml& xml) { int mode = -1; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if(mode != -1) { ZitaResamplerAudioConverterOptions* opts = NULL; switch(mode) { case AudioConverterSettings::OfflineMode: opts = &_offlineOptions; break; case AudioConverterSettings::RealtimeMode: opts = &_realtimeOptions; break; case AudioConverterSettings::GuiMode: opts = &_guiOptions; break; } if(opts) { if(tag == "useSettings") opts->_useSettings = xml.parseInt(); //else // if(tag == "converterType") // opts->_converterType = xml.parseInt(); } } else xml.unknown("settings"); break; case Xml::Attribut: if (tag == "mode") mode = xml.s2().toInt(); else fprintf(stderr, "settings unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (tag == "settings") { return; } default: break; } } } } // namespace MusECore //========================================================== namespace MusEGui { ZitaResamplerSettingsDialog::ZitaResamplerSettingsDialog( MusECore::AudioConverterSettings::ModeType mode, QWidget* parent, MusECore::AudioConverterSettings* settings, bool isLocal) : QDialog(parent) { setupUi(this); OKButton->setEnabled(false); _options = NULL; if(settings) { MusECore::ZitaResamplerAudioConverterSettings* zita_settings = static_cast(settings); switch(mode) { case MusECore::AudioConverterSettings::OfflineMode: _options = zita_settings->offlineOptions(); break; case MusECore::AudioConverterSettings::RealtimeMode: _options = zita_settings->realtimeOptions(); break; case MusECore::AudioConverterSettings::GuiMode: _options = zita_settings->guiOptions(); break; default: // Disable everything and return. break; } } #ifdef ZITA_RESAMPLER_SUPPORT //if(isLocal) useDefaultSettings->setChecked(!_options || !_options->_useSettings); useDefaultSettings->setEnabled(isLocal && _options); useDefaultSettings->setVisible(isLocal && _options); optionsGroup->setEnabled(!isLocal || (_options && _options->_useSettings)); warningLabel->setVisible(false); #else useDefaultSettings->setEnabled(false); useDefaultSettings->setVisible(isLocal && _options); optionsGroup->setEnabled(false); warningLabel->setVisible(true); #endif setControls(); connect(useDefaultSettings, &QCheckBox::clicked, [this]() { buttonClicked(DefaultsButtonId); } ); connect(OKButton, &QPushButton::clicked, [this]() { buttonClicked(OkButtonId); } ); connect(cancelButton, &QPushButton::clicked, [this]() { buttonClicked(CancelButtonId); } ); } void ZitaResamplerSettingsDialog::setControls() { #ifdef ZITA_RESAMPLER_SUPPORT if(!_options) return; // TODO #endif } void ZitaResamplerSettingsDialog::buttonClicked(int idx) { switch(idx) { case DefaultsButtonId: OKButton->setEnabled(true); optionsGroup->setEnabled(!useDefaultSettings->isChecked()); break; case ConverterButtonId: OKButton->setEnabled(true); break; case OkButtonId: accept(); break; case CancelButtonId: reject(); break; default: break; } } //--------------------------------------------------------- // accept //--------------------------------------------------------- void ZitaResamplerSettingsDialog::accept() { if(!_options) { QDialog::accept(); return; } #ifdef ZITA_RESAMPLER_SUPPORT // TODO // int type = -1; // if(typeSINCBestQuality->isChecked()) // type = SRC_SINC_BEST_QUALITY; // else if(typeSINCMedium->isChecked()) // type = SRC_SINC_MEDIUM_QUALITY; // else if(typeSINCFastest->isChecked()) // type = SRC_SINC_FASTEST; // else if(typeZeroOrderHold->isChecked()) // type = SRC_ZERO_ORDER_HOLD; // else if(typeLinear->isChecked()) // type = SRC_LINEAR; // // if(type != -1) // _options->_converterType = type; _options->_useSettings = !useDefaultSettings->isChecked(); #endif QDialog::accept(); } } // namespace MusEGui muse-4.0.0/src/audio_convert/zita_resampler/zita_resampler_converter.h000066400000000000000000000157021404105311000264100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // zita_resampler_converter.h // (C) Copyright 2010-2020 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ZITA_RESAMPLER_CONVERTER_H__ #define __ZITA_RESAMPLER_CONVERTER_H__ #include "config.h" #ifdef ZITA_RESAMPLER_SUPPORT #include #include #endif #include #include #include "xml.h" #include "ui_zita_resampler_settings_base.h" #include "audio_convert/lib_audio_convert/audioconvert.h" namespace MusECore { //--------------------------------------------------------- // ZitaResamplerAudioConverter //--------------------------------------------------------- struct ZitaResamplerAudioConverterOptions { // Some hard-coded defaults. static const ZitaResamplerAudioConverterOptions defaultOfflineOptions; static const ZitaResamplerAudioConverterOptions defaultRealtimeOptions; static const ZitaResamplerAudioConverterOptions defaultGuiOptions; AudioConverterSettings::ModeType _mode; // Whether to use these settings or defer to // some higher default settings if available. // The highest level (defaults) will ignore this value. bool _useSettings; ZitaResamplerAudioConverterOptions( bool useSettings = false, AudioConverterSettings::ModeType mode = AudioConverterSettings::OfflineMode) { initOptions(useSettings, mode); } void initOptions(bool useSettings, AudioConverterSettings::ModeType mode) { _mode = mode; _useSettings = useSettings; #ifdef ZITA_RESAMPLER_SUPPORT //_converterType = (isLocal ? -1 : 0); // TODO Default setting #else //_converterType = -1); #endif } void read(Xml&); void write(int, Xml&) const; // Returns whether any option is set ie. non-default. //bool isSet() const { return false; } //_converterType != -1; } bool useSettings() const { return _useSettings; } bool isDefault() const { return *this == defaultOfflineOptions; } bool operator==(const ZitaResamplerAudioConverterOptions& other) const { return other._useSettings == _useSettings; // && other._converterType == _converterType; } }; class ZitaResamplerAudioConverterSettings : public AudioConverterSettings { protected: ZitaResamplerAudioConverterOptions _realtimeOptions; ZitaResamplerAudioConverterOptions _offlineOptions; ZitaResamplerAudioConverterOptions _guiOptions; public: ZitaResamplerAudioConverterSettings(bool isLocal); void assign(const AudioConverterSettings&); void initOptions(bool /*isLocal*/) { _offlineOptions.initOptions( ZitaResamplerAudioConverterOptions::defaultOfflineOptions._useSettings, AudioConverterSettings::OfflineMode); _realtimeOptions.initOptions( ZitaResamplerAudioConverterOptions::defaultRealtimeOptions._useSettings, AudioConverterSettings::RealtimeMode); _guiOptions.initOptions( ZitaResamplerAudioConverterOptions::defaultGuiOptions._useSettings, AudioConverterSettings::GuiMode); } ZitaResamplerAudioConverterOptions* offlineOptions() { return &_offlineOptions; } ZitaResamplerAudioConverterOptions* realtimeOptions() { return &_realtimeOptions; } ZitaResamplerAudioConverterOptions* guiOptions() { return &_guiOptions; } int executeUI(ModeType mode, QWidget* parent = NULL, bool isLocal = false); void read(Xml&); void write(int, Xml&) const; // Returns whether to use these settings or defer to default settings. // Mode is a combination of AudioConverterSettings::ModeType selecting // which of the settings to check. Can also be <= 0, meaning all. bool useSettings(int mode = -1) const; bool isDefault() const { return _offlineOptions == ZitaResamplerAudioConverterOptions::defaultOfflineOptions && _realtimeOptions == ZitaResamplerAudioConverterOptions::defaultRealtimeOptions && _guiOptions == ZitaResamplerAudioConverterOptions::defaultGuiOptions; } }; class ZitaResamplerAudioConverter : public AudioConverter { private: int _options; //double _ratio; int _filterHLen; #ifdef ZITA_RESAMPLER_SUPPORT VResampler* _rbs; #endif public: // Mode is an AudioConverterSettings::ModeType selecting which of the settings to use. ZitaResamplerAudioConverter(int systemSampleRate, int channels, AudioConverterSettings* settings, AudioConverterSettings::ModeType mode); ~ZitaResamplerAudioConverter(); bool isValid() const #ifdef ZITA_RESAMPLER_SUPPORT { return _rbs != 0; } #else { return false; } #endif void reset(); void setChannels(int ch); AudioConverterSettings::ModeType mode() const; // Make sure beforehand that sf samplerate is not <= 0. int process( SNDFILE* sf_handle, const int sf_chans, const double sf_sr_ratio, const StretchList* sf_stretch_list, const sf_count_t pos, float** buffer, const int channels, const int frames, const bool overwrite); }; } // namespace MusECore //======================================================================== namespace MusEGui { //--------------------------------------------------------- // ZitaResamplerSettingsDialog //--------------------------------------------------------- class ZitaResamplerSettingsDialog : public QDialog, public Ui::ZitaResamplerSettingsBase { Q_OBJECT MusECore::ZitaResamplerAudioConverterOptions* _options; void setControls(); private slots: virtual void buttonClicked(int); virtual void accept(); public: enum buttonId { DefaultsButtonId, ConverterButtonId, OkButtonId, CancelButtonId }; ZitaResamplerSettingsDialog(MusECore::AudioConverterSettings::ModeType mode, QWidget* parent = NULL, MusECore::AudioConverterSettings* settings = NULL, bool isLocal = false); }; } // namespace MusEGui #endif muse-4.0.0/src/audio_convert/zita_resampler/zita_resampler_settings_base.ui000066400000000000000000000054341404105311000274220ustar00rootroot00000000000000 ZitaResamplerSettingsBase 0 0 285 279 Zita Resampler Settings 10 75 true Zita Resampler support is not enabled Use default settings 0 0 Options: Qt::Vertical 20 40 Qt::Horizontal 40 20 0 0 OK true Cancel muse-4.0.0/src/awl/000077500000000000000000000000001404105311000140235ustar00rootroot00000000000000muse-4.0.0/src/awl/CMakeLists.txt000066400000000000000000000057721404105311000165760ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP (awl_mocs aslider.h # awlplugin.h checkbox.h combobox.h drawbar.h floatentry.h knob.h midimeter.h midimslider.h midipanentry.h midipanknob.h midivolentry.h mslider.h panentry.h panknob.h pitchedit.h pitchlabel.h # posedit.h # poslabel.h sigedit.h sigspinbox.h slider.h # tcanvas.h tempoedit.h tempolabel.h volentry.h volknob.h volslider.h ) ## ## List of source files to compile ## file (GLOB awl_source_files aslider.cpp awl.cpp # awlplugin.cpp checkbox.cpp combobox.cpp drawbar.cpp floatentry.cpp knob.cpp # ltest.cpp midimeter.cpp midimslider.cpp midipanentry.cpp midipanknob.cpp midivolentry.cpp mslider.cpp panentry.cpp panknob.cpp pitchedit.cpp pitchlabel.cpp # posedit.cpp # poslabel.cpp sigedit.cpp sigspinbox.cpp slider.cpp # tcanvas.cpp tempoedit.cpp tempolabel.cpp utils.cpp volentry.cpp volknob.cpp volslider.cpp ) ## ## Define target ## # Always build this shared now. It is no longer linked into the main app. # add_library ( awl ${MODULES_BUILD} add_library ( awl SHARED ${awl_source_files} ${awl_mocs} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${awl_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( awl PROPERTIES OUTPUT_NAME muse_awl ) ## ## Linkage ## target_link_libraries ( awl ${QT_LIBRARIES} al ) ## ## Install location ## # if ( ${MODULES_BUILD} STREQUAL SHARED ) # install(TARGETS awl # DESTINATION ${MusE_MODULES_DIR} # ) # endif ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS awl DESTINATION ${MusE_MODULES_DIR} ) muse-4.0.0/src/awl/aslider.cpp000066400000000000000000000162711404105311000161610ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "fastlog.h" #include "aslider.h" #include #include #include "muse_math.h" namespace Awl { //--------------------------------------------------------- // AbstractSlider //--------------------------------------------------------- AbstractSlider::AbstractSlider(QWidget* parent) : QWidget(parent), _scaleColor(Qt::black), _scaleValueColor(Qt::blue) { _id = 0; _value = 0.5; _minValue = 0.0; _maxValue = 1.0; _lineStep = 0.1; _pageStep = 0.2; _center = false; _invert = false; _scaleWidth = 4; _log = false; _integer = false; } //--------------------------------------------------------- // setEnabled //--------------------------------------------------------- void AbstractSlider::setEnabled(bool val) { QWidget::setEnabled(val); update(); } //--------------------------------------------------------- // setCenter //! If the center flag is set, a notch is drawn to //! show the center position. //--------------------------------------------------------- void AbstractSlider::setCenter(bool val) { if (val != _center) { _center = val; update(); } } //!-------------------------------------------------------- // setScaleWidth //--------------------------------------------------------- void AbstractSlider::setScaleWidth(int val) { if (val != _scaleWidth) { _scaleWidth = val; update(); } } //--------------------------------------------------------- // setScaleColor //--------------------------------------------------------- void AbstractSlider::setScaleColor(const QColor& c) { if (c != _scaleColor) { _scaleColor = c; update(); } } //--------------------------------------------------------- // setScaleValueColor //--------------------------------------------------------- void AbstractSlider::setScaleValueColor(const QColor& c) { if (c != _scaleValueColor) { _scaleValueColor = c; update(); } } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void AbstractSlider::wheelEvent(QWheelEvent* ev) { ev->accept(); double div = 120.0; const QPoint pixelDelta = ev->pixelDelta(); const QPoint angleDegrees = ev->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; if (ev->modifiers() & Qt::ShiftModifier) _value += (delta * pageStep()) / div; else _value += (delta * lineStep()) / div; if (_value < _minValue) _value = _minValue; else if (_value > _maxValue) _value = _maxValue; valueChange(); update(); } //--------------------------------------------------------- // keyPressEvent //--------------------------------------------------------- void AbstractSlider::keyPressEvent(QKeyEvent* ev) { double oval = _value; switch (ev->key()) { case Qt::Key_Home: _value = _minValue; break; case Qt::Key_End: _value = _maxValue; break; case Qt::Key_Up: case Qt::Key_Left: _value += lineStep(); break; case Qt::Key_Down: case Qt::Key_Right: _value -= lineStep(); break; case Qt::Key_PageDown: _value -= pageStep(); break; case Qt::Key_PageUp: _value += pageStep(); break; default: break; } if (_value < _minValue) _value = _minValue; else if (_value > _maxValue) _value = _maxValue; if (oval != _value) { if (_integer && (rint(oval) == rint(_value))) return; valueChange(); update(); } } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void AbstractSlider::setValue(double val) { if (_log) { if (val == 0.0f) _value = _minValue; else { _value = fast_log10(val) * 20.0f; if (_value < _minValue) _value = _minValue; } } else if (_integer) _value = rint(val); else _value = val; update(); } //--------------------------------------------------------- // valueChange //--------------------------------------------------------- void AbstractSlider::valueChange() { emit valueChanged(value(), _id); } //--------------------------------------------------------- // value //--------------------------------------------------------- double AbstractSlider::value() const { if (_log) return muse_db2val(_value); if (_integer) return rint(_value); return _value; } //--------------------------------------------------------- // minLogValue //--------------------------------------------------------- //double AbstractSlider::minValue() const { // return _log ? pow(10.0, _minValue*0.05f) : _minValue; //} //--------------------------------------------------------- // setMinLogValue //--------------------------------------------------------- void AbstractSlider::setMinLogValue(double val) { if (_log) { if (val == 0.0f) _minValue = -100; else _minValue = fast_log10(val) * 20.0f; } else _minValue = val; } //--------------------------------------------------------- // maxLogValue //--------------------------------------------------------- //double AbstractSlider::maxValue() const { // return _log ? pow(10.0, _maxValue*0.05f) : _maxValue; //} //--------------------------------------------------------- // setMaxLogValue //--------------------------------------------------------- void AbstractSlider::setMaxLogValue(double val) { if (_log) { _maxValue = fast_log10(val) * 20.0f; } else _maxValue = val; } } muse-4.0.0/src/awl/aslider.h000066400000000000000000000113051404105311000156170ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLASLIDER_H__ #define __AWLASLIDER_H__ #include class QKeyEvent; class QWheelEvent; namespace Awl { //--------------------------------------------------------- // AbstractSlider // //! The AwlAbstractSlider class provides an double value //! within a range // //! The class is designed as a common super class for //! widgets like AwlKnob and AwlSlider //! //--------------------------------------------------------- class AbstractSlider : public QWidget { Q_OBJECT Q_PROPERTY(double value READ value WRITE setValue) Q_PROPERTY(bool center READ center WRITE setCenter) Q_PROPERTY(bool invertedAppearance READ invertedAppearance WRITE setInvertedAppearance) Q_PROPERTY(int scaleWidth READ scaleWidth WRITE setScaleWidth) Q_PROPERTY(QColor scaleColor READ scaleColor WRITE setScaleColor) Q_PROPERTY(QColor scaleValueColor READ scaleValueColor WRITE setScaleValueColor) Q_PROPERTY(int id READ id WRITE setId) Q_PROPERTY(double minValue READ minValue WRITE setMinValue) Q_PROPERTY(double maxValue READ maxValue WRITE setMaxValue) Q_PROPERTY(double lineStep READ lineStep WRITE setLineStep) Q_PROPERTY(double pageStep READ pageStep WRITE setPageStep) Q_PROPERTY(bool log READ log WRITE setLog) Q_PROPERTY(bool integer READ integer WRITE setInteger) protected: int _id; double _value; double _minValue, _maxValue, _lineStep, _pageStep; bool _center; bool _invert; int _scaleWidth; //! scale line width QColor _scaleColor; QColor _scaleValueColor; bool _log; bool _integer; virtual void wheelEvent(QWheelEvent*); virtual void keyPressEvent(QKeyEvent*); virtual void valueChange(); signals: void valueChanged(double, int); public slots: virtual void setValue(double v); public: AbstractSlider(QWidget* parent = 0); virtual void setCenter(bool val); virtual void setScaleWidth(int); virtual void setScaleColor(const QColor&); virtual void setScaleValueColor(const QColor&); //! return the center flag bool center() const { return _center; } //! return the scale line width int scaleWidth() const { return _scaleWidth; } //! return current scale color QColor scaleColor() const { return _scaleColor; } //! return color of active scale part QColor scaleValueColor() const { return _scaleValueColor; } virtual void setInvertedAppearance(bool val) { _invert = val; } bool invertedAppearance() const { return _invert; } int id() const { return _id; } void setId(int i) { _id = i; } virtual double value() const; double minValue() const { return _minValue; } void setMinValue(double v) { _minValue = v; } void setMinLogValue(double v); double maxValue() const {return _maxValue; } void setMaxValue(double v) { _maxValue = v; } void setMaxLogValue(double v); void setRange(double a, double b) { setMinValue(a); setMaxValue(b); } void setLogRange(double a, double b) { setMinLogValue(a); setMaxLogValue(b); } bool log() const { return _log; } void setLog(bool v) { _log = v; } bool integer() const { return _integer; } void setInteger(bool v) { _integer = v; } double lineStep() const { return _lineStep; } void setLineStep(double v) { _lineStep = v; } double pageStep() const { return _pageStep; } void setPageStep(double f) { _pageStep = f; } void setEnabled(bool val); }; } #endif muse-4.0.0/src/awl/awl.cpp000066400000000000000000000021021404105311000153050ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "awl.h" namespace Awl { // int sampleRate = 44100; // int mtcType = 0; } muse-4.0.0/src/awl/awl.h000066400000000000000000000021371404105311000147620ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWL_H__ #define __AWL_H__ namespace Awl { // extern int sampleRate; // extern int mtcType; } #endif muse-4.0.0/src/awl/awlplugin.cpp000066400000000000000000000073571404105311000165450ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "volknob.h" #include "volslider.h" #include "mslider.h" #include "awlplugin.h" #include "posedit.h" #include "poslabel.h" #include "tempoedit.h" #include "tempolabel.h" #include "checkbox.h" #include "combobox.h" #include "floatentry.h" #include "panknob.h" #include "midipanknob.h" #include "drawbar.h" #include #include QWidget* KnobPlugin::createWidget(QWidget* parent) { return new Awl::Knob(parent); } QWidget* VolKnobPlugin::createWidget(QWidget* parent) { return new Awl::VolKnob(parent); } QWidget* SliderPlugin::createWidget(QWidget* parent) { return new Awl::Slider(parent); } QWidget* VolSliderPlugin::createWidget(QWidget* parent) { return new Awl::VolSlider(parent); } QWidget* MeterSliderPlugin::createWidget(QWidget* parent) { return new Awl::MeterSlider(parent); } QWidget* PosEditPlugin::createWidget(QWidget* parent) { return new Awl::PosEdit(parent); } QWidget* PosLabelPlugin::createWidget(QWidget* parent) { return new Awl::PosLabel(parent); } QWidget* TempoEditPlugin::createWidget(QWidget* parent) { return new Awl::TempoEdit(parent); } QWidget* TempoLabelPlugin::createWidget(QWidget* parent) { return new Awl::TempoLabel(parent); } QWidget* CheckBoxPlugin::createWidget(QWidget* parent) { return new Awl::CheckBox(parent); } QWidget* ComboBoxPlugin::createWidget(QWidget* parent) { return new Awl::ComboBox(parent); } QWidget* FloatEntryPlugin::createWidget(QWidget* parent) { return new Awl::FloatEntry(parent); } QWidget* PanKnobPlugin::createWidget(QWidget* parent) { return new Awl::PanKnob(parent); } QWidget* MidiPanKnobPlugin::createWidget(QWidget* parent) { return new Awl::MidiPanKnob(parent); } QWidget* DrawbarPlugin::createWidget(QWidget* parent) { return new Awl::Drawbar(parent); } //--------------------------------------------------------- // customWidgets //--------------------------------------------------------- QList AwlPlugins::customWidgets() const { QList plugins; plugins << new VolKnobPlugin << new PanKnobPlugin << new MidiPanKnobPlugin << new KnobPlugin << new SliderPlugin << new VolSliderPlugin << new MeterSliderPlugin << new PosEditPlugin << new PosLabelPlugin << new TempoEditPlugin << new TempoLabelPlugin << new CheckBoxPlugin << new ComboBoxPlugin << new FloatEntryPlugin << new DrawbarPlugin; return plugins; } Q_EXPORT_PLUGIN(AwlPlugins) muse-4.0.0/src/awl/awlplugin.h000066400000000000000000000221601404105311000161770ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLPLUGIN_H__ #define __AWLPLUGIN_H__ #include //--------------------------------------------------------- // AwlPlugin //--------------------------------------------------------- class AwlPlugin : public QDesignerCustomWidgetInterface { Q_INTERFACES(QDesignerCustomWidgetInterface) bool m_initialized; public: AwlPlugin() : m_initialized(false) { } bool isContainer() const { return false; } bool isInitialized() const { return m_initialized; } QIcon icon() const { return QIcon(); } virtual QString codeTemplate() const { return QString(); } QString whatsThis() const { return QString(); } QString toolTip() const { return QString(); } QString group() const { return "MusE Awl Widgets"; } void initialize(QDesignerFormEditorInterface *) { if (m_initialized) return; m_initialized = true; } }; //--------------------------------------------------------- // KnobPlugin //--------------------------------------------------------- class KnobPlugin : public QObject, public AwlPlugin { Q_OBJECT public: KnobPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return QString("awl/knob.h"); } QString name() const { return "Awl::Knob"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // VolKnobPlugin //--------------------------------------------------------- class VolKnobPlugin : public QObject, public AwlPlugin { Q_OBJECT public: VolKnobPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/volknob.h"; } QString name() const { return "Awl::VolKnob"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // SliderPlugin //--------------------------------------------------------- class SliderPlugin : public QObject, public AwlPlugin { Q_OBJECT public: SliderPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/slider.h"; } QString name() const { return "Awl::Slider"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // VolSliderPlugin //--------------------------------------------------------- class VolSliderPlugin : public QObject, public AwlPlugin { Q_OBJECT public: VolSliderPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/volslider.h"; } QString name() const { return "Awl::VolSlider"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // MeterSliderPlugin //--------------------------------------------------------- class MeterSliderPlugin : public QObject, public AwlPlugin { Q_OBJECT public: MeterSliderPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/mslider.h"; } QString name() const { return "Awl::MeterSlider"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // PosEditPlugin //--------------------------------------------------------- class PosEditPlugin : public QObject, public AwlPlugin { Q_OBJECT public: PosEditPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/posedit.h"; } QString name() const { return "Awl::PosEdit"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // PosLabelPlugin //--------------------------------------------------------- class PosLabelPlugin : public QObject, public AwlPlugin { Q_OBJECT public: PosLabelPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/poslabel.h"; } QString name() const { return "Awl::PosLabel"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // TempoEditPlugin //--------------------------------------------------------- class TempoEditPlugin : public QObject, public AwlPlugin { Q_OBJECT public: TempoEditPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/tempoedit.h"; } QString name() const { return "Awl::TempoEdit"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // TempoLabelPlugin //--------------------------------------------------------- class TempoLabelPlugin : public QObject, public AwlPlugin { Q_OBJECT public: TempoLabelPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/tempolabel.h"; } QString name() const { return "Awl::TempoLabel"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // CheckBoxPlugin //--------------------------------------------------------- class CheckBoxPlugin : public QObject, public AwlPlugin { Q_OBJECT public: CheckBoxPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/checkbox.h"; } QString name() const { return "Awl::CheckBox"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // ComboBoxPlugin //--------------------------------------------------------- class ComboBoxPlugin : public QObject, public AwlPlugin { Q_OBJECT public: ComboBoxPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/combobox.h"; } QString name() const { return "Awl::ComboBox"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // FloatEntryPlugin //--------------------------------------------------------- class FloatEntryPlugin : public QObject, public AwlPlugin { Q_OBJECT public: FloatEntryPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/floatentry.h"; } QString name() const { return "Awl::FloatEntry"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // PanKnobPlugin //--------------------------------------------------------- class PanKnobPlugin : public QObject, public AwlPlugin { Q_OBJECT public: PanKnobPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/panknob.h"; } QString name() const { return "Awl::PanKnob"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // MidiPanKnobPlugin //--------------------------------------------------------- class MidiPanKnobPlugin : public QObject, public AwlPlugin { Q_OBJECT public: MidiPanKnobPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/midipanknob.h"; } QString name() const { return "Awl::MidiPanKnob"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // DrawbarPlugin //--------------------------------------------------------- class DrawbarPlugin : public QObject, public AwlPlugin { Q_OBJECT public: DrawbarPlugin(QObject* parent = 0) : QObject(parent) {} QString includeFile() const { return "awl/drawbar.h"; } QString name() const { return "Awl::Drawbar"; } QWidget* createWidget(QWidget* parent); }; //--------------------------------------------------------- // AwlPlugins //--------------------------------------------------------- class AwlPlugins : public QObject, public QDesignerCustomWidgetCollectionInterface { Q_OBJECT Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) public: QList customWidgets() const; }; #endif muse-4.0.0/src/awl/checkbox.cpp000066400000000000000000000024561404105311000163240ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "checkbox.h" namespace Awl { //--------------------------------------------------------- // CheckBox //--------------------------------------------------------- CheckBox::CheckBox(QWidget* parent) : QCheckBox(parent) { _id = 0; connect(this, SIGNAL(toggled(bool)), SLOT(hasToggled(bool))); } } muse-4.0.0/src/awl/checkbox.h000066400000000000000000000032671404105311000157720ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLCHECKBOX_H__ #define __AWLCHECKBOX_H__ #include namespace Awl { //--------------------------------------------------------- // CheckBox //--------------------------------------------------------- class CheckBox : public QCheckBox { Q_OBJECT Q_PROPERTY(int id READ id WRITE setId) int _id; private slots: void hasToggled(bool val) { emit valueChanged(double(val), _id); } signals: void valueChanged(double, int); public slots: void setValue(double val) { setDown(val > 0.5f); } public: CheckBox(QWidget* parent); int id() const { return _id; } void setId(int i) { _id = i; } }; } #endif muse-4.0.0/src/awl/clock.xpm000066400000000000000000000007511404105311000156470ustar00rootroot00000000000000/* XPM */ static char *clock_xpm[]={ "18 18 3 1", ". c None", "# c #000000", "a c #0000c0", "...####a#####.....", "..####.aaa.###....", ".####..aaa..###...", ".###...aaa...###..", ".#.....aaa....##..", "##.....aaa.....##.", "##.....aaa.....##.", "###....aaaaaaaaa#.", "###....aaaaaaaaa#.", "##.....aaaaaaaaa#.", "##............#.#.", ".##...........##..", ".##..........###..", "..##........####..", "...####...####....", "....#########.....", "......######......", ".................."}; muse-4.0.0/src/awl/combobox.cpp000066400000000000000000000024631404105311000163440ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "combobox.h" namespace Awl { //--------------------------------------------------------- // ComboBox //--------------------------------------------------------- ComboBox::ComboBox(QWidget* parent) : QComboBox(parent) { _id = 0; connect(this, SIGNAL(activated(int)), SLOT(activatedIntern(int))); } } muse-4.0.0/src/awl/combobox.h000066400000000000000000000033011404105311000160010ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLCOMBOBOX_H__ #define __AWLCOMBOBOX_H__ #include namespace Awl { //--------------------------------------------------------- // ComboBox //--------------------------------------------------------- class ComboBox : public QComboBox { Q_OBJECT Q_PROPERTY(int id READ id WRITE setId) int _id; private slots: void activatedIntern(int val) { emit valueChanged(double(val), _id); } signals: void valueChanged(double, int); public slots: void setValue(double val) { setCurrentIndex(int(val)); } public: ComboBox(QWidget* parent); int id() const { return _id; } void setId(int i) { _id = i; } }; } #endif muse-4.0.0/src/awl/drawbar.cpp000066400000000000000000000070651404105311000161610ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "drawbar.h" #include namespace Awl { //--------------------------------------------------------- // Drawbar //--------------------------------------------------------- Drawbar::Drawbar(QWidget* parent) : Slider(parent) { _sliderColor = Qt::darkGray; setOrientation(Qt::Vertical); setInvertedAppearance(true); setRange(0.0, 8.0); setLineStep(1.0); setPageStep(1.0); setInteger(true); } Drawbar::~Drawbar() { } //--------------------------------------------------------- // setSliderColor //--------------------------------------------------------- void Drawbar::setSliderColor(const QColor& c) { if (c != _sliderColor) { _sliderColor = c; update(); } } //--------------------------------------------------------- // paint //--------------------------------------------------------- void Drawbar::paintEvent(QPaintEvent*) { int h = height(); int w = width(); int kh = w * 2; // knob height int kw = w; int pixel = h - kh; int ppos = int(pixel * value() / 8.0); QPainter p(this); QColor sc(Qt::darkGray); QColor svc(Qt::gray); p.setBrush(svc); //--------------------------------------------------- // draw scale //--------------------------------------------------- int sx = (w + 9) / 10; int sw = w - 2 * sx; p.fillRect(sx, 0, sw, ppos, sc); QPen pen(Qt::white); int lw = 2; pen.setWidth(lw); p.setPen(pen); int sx1 = sx + lw/2; p.drawLine(sx1, 0, sx1, ppos); int sx2 = sx + sw - lw/2; p.drawLine(sx2, 0, sx2, ppos); //--------------------------------------------------- // draw numbers //--------------------------------------------------- p.save(); p.setClipRect(QRect(sx, 0, sw, ppos)); QFont f = p.font(); f.setPixelSize(8); int ch = pixel / 8; QString num("%1"); for (int i = 0; i < 8; ++i) { p.drawText(0, i * pixel / 8 - (pixel - ppos), w, ch, Qt::AlignCenter, num.arg(8-i)); } p.restore(); //--------------------------------------------------- // draw slider //--------------------------------------------------- p.fillRect(0, ppos, kw, kh, _sliderColor); pen.setWidth(1); pen.setColor(Qt::black); p.setPen(pen); int y1 = ppos + kh / 5 * 2; int y2 = ppos + kh / 5 * 3; p.drawLine(0, y1, kw, y1); p.drawLine(0, y2, kw, y2); } } muse-4.0.0/src/awl/drawbar.h000066400000000000000000000031671404105311000156250ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLDRAWBAR_H__ #define __AWLDRAWBAR_H__ #include "slider.h" namespace Awl { //--------------------------------------------------------- // Drawbar //--------------------------------------------------------- class Drawbar : public Slider { Q_OBJECT Q_PROPERTY(QColor sliderColor READ sliderColor WRITE setSliderColor) virtual void paintEvent(QPaintEvent*); protected: QColor _sliderColor; public: Drawbar(QWidget* parent = 0); ~Drawbar(); //! return current slider color QColor sliderColor() const { return _sliderColor; } void setSliderColor(const QColor&); }; } #endif muse-4.0.0/src/awl/fastlog.h000066400000000000000000000016321404105311000156350ustar00rootroot00000000000000/* Copyright unknown. Code by Laurent de Soras . */ #ifndef __FASTLOG_H__ #define __FASTLOG_H__ #include "muse_math.h" /* Get math.h for HUGE_VAL */ static inline float fast_log2 (float val) { /* don't use reinterpret_cast<> because that prevents this from being used by pure C code (for example, GnomeCanvasItems) */ union {float f; int i;} t; t.f = val; int* const exp_ptr = &t.i; int x = *exp_ptr; const int log_2 = ((x >> 23) & 255) - 128; x &= ~(255 << 23); x += 127 << 23; *exp_ptr = x; val = ((-1.0f/3) * t.f + 2) * t.f - 2.0f/3; return (val + log_2); } static inline float fast_log (const float val) { return (fast_log2 (val) * 0.69314718f); } static inline float fast_log10 (const float val) { return fast_log2(val) / 3.312500f; } static inline float minus_infinity() { return -HUGE_VAL; } #endif muse-4.0.0/src/awl/floatentry.cpp000066400000000000000000000260671404105311000167310ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "floatentry.h" #include "fastlog.h" #include "gconfig.h" #include #include #include #include #include "muse_math.h" #define TIMER1 400 #define TIMER2 200 #define TIMEC 7 #define TIMER3 100 #define TIMEC2 20 #define TIMER4 50 namespace Awl { //--------------------------------------------------------- // FloatEntry //--------------------------------------------------------- FloatEntry::FloatEntry(QWidget* parent, bool leftMouseButtonCanDecrease) : QLineEdit(parent), _leftMouseButtonCanDecrease(leftMouseButtonCanDecrease) { _id = 0; _minValue = 0.0; _maxValue = 1.0; _log = false; evx = 1.0; _precision = 3; timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(repeat())); _value = 0.0f; //connect(this, SIGNAL(returnPressed()), SLOT(endEdit())); connect(this, SIGNAL(editingFinished()), SLOT(endEdit())); setCursor(QCursor(Qt::ArrowCursor)); updateValue(); } //--------------------------------------------------------- // calcIncrement() //--------------------------------------------------------- double FloatEntry::calcIncrement() const { double dif; if(_maxValue - _minValue > 0) dif = _maxValue - _minValue; else dif = _minValue - _maxValue; if(dif <= 10.0) return 0.1; else if(dif <= 100.0) return 1.0; else return 10.0; } //--------------------------------------------------------- // contextMenuEvent //--------------------------------------------------------- void FloatEntry::contextMenuEvent(QContextMenuEvent * e) { e->accept(); } //--------------------------------------------------------- // setString //--------------------------------------------------------- bool FloatEntry::setString(double v) { QString s; // if (v < _minValue || v > _maxValue) { if (v < _minValue) { if (!_specialText.isEmpty()) setText(_specialText); return true; } s.setNum(v, 'f', _precision); if (!_suffix.isEmpty()) { // s += " "; s += _suffix; } setText(s); return false; } //--------------------------------------------------------- // setSValue //--------------------------------------------------------- void FloatEntry::setSValue(const QString& s) { bool ok; double v = s.toFloat(&ok); if (ok && (v != _value)) { if (v < _minValue) v = _minValue; if (v > _maxValue) v = _maxValue; _value = v; updateValue(); valueChange(); } } //--------------------------------------------------------- // valueChange //--------------------------------------------------------- void FloatEntry::valueChange() { emit valueChanged(value(), _id); } //--------------------------------------------------------- // incValue //--------------------------------------------------------- void FloatEntry::incValue(double) { if(_value >= _maxValue) return; double inc = calcIncrement(); if(_value + inc >= _maxValue) //setValue(_maxValue); _value = _maxValue; else //setValue(_value + inc); _value += inc; valueChange(); } //--------------------------------------------------------- // decValue //--------------------------------------------------------- void FloatEntry::decValue(double) { if(_value <= _minValue) return; double inc = calcIncrement(); if(_value - inc <= _minValue) //setValue(_minValue); _value = _minValue; else //setValue(_value - inc); _value -= inc; valueChange(); } //--------------------------------------------------------- // setPrecision //--------------------------------------------------------- void FloatEntry::setPrecision(int v) { _precision = v; setString(_value); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize FloatEntry::sizeHint() const { QFontMetrics fm = fontMetrics(); int h = fm.height() + 4; int n = _precision + 3; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = fm.horizontalAdvance(QString("-0.")) + fm.horizontalAdvance('0') * n + 6; #else int w = fm.width(QString("-0.")) + fm.width('0') * n + 6; #endif return QSize(w, h); } //--------------------------------------------------------- // endEdit //--------------------------------------------------------- void FloatEntry::endEdit() { if (QLineEdit::isModified()) setSValue(text()); clearFocus(); } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void FloatEntry::mousePressEvent(QMouseEvent* event) { button = event->button(); starty = event->y(); evx = double(event->x()); timecount = 0; repeat(); timer->start(TIMER1); } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void FloatEntry::wheelEvent(QWheelEvent* event) { event->accept(); const QPoint pixelDelta = event->pixelDelta(); const QPoint angleDegrees = event->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; if (delta < 0) decValue(-1.0); else if (delta > 0) incValue(1.0); } //--------------------------------------------------------- // repeat //--------------------------------------------------------- void FloatEntry::repeat() { if (timecount == 1) { ++timecount; timer->stop(); timer->start(TIMER2); return; } ++timecount; if (timecount == TIMEC) { timer->stop(); timer->start(TIMER3); } if (timecount == TIMEC2) { timer->stop(); timer->start(TIMER4); } switch (button) { case Qt::LeftButton: if (!_leftMouseButtonCanDecrease) return; // else fall through case Qt::MidButton: decValue(evx); break; case Qt::RightButton: incValue(evx); break; default: break; } } //--------------------------------------------------------- // mouseReleaseEvent //--------------------------------------------------------- void FloatEntry::mouseReleaseEvent(QMouseEvent*) { button = Qt::NoButton; timer->stop(); } //--------------------------------------------------------- // mouseMoveEvent //--------------------------------------------------------- void FloatEntry::mouseMoveEvent(QMouseEvent*) { switch (button) { case Qt::LeftButton: break; case Qt::MidButton: break; case Qt::RightButton: break; default: break; } } //--------------------------------------------------------- // mouseDoubleClickEvent //--------------------------------------------------------- void FloatEntry::mouseDoubleClickEvent(QMouseEvent* event) { if (event->button() != Qt::LeftButton) { mousePressEvent(event); return; } setFocus(); QLineEdit::setFrame(true); update(); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void FloatEntry::setValue(double val) { if (_log) { if (val == 0.0f) _value = _minValue; else _value = fast_log10(val) * 20.0f; } else _value = val; updateValue(); } //--------------------------------------------------------- // updateValue //--------------------------------------------------------- void FloatEntry::updateValue() { if (setString(_value)) { // value is out of range: if (_value > _maxValue) _value = _maxValue; else if (_value < _minValue) _value = _minValue; } } //--------------------------------------------------------- // value //--------------------------------------------------------- double FloatEntry::value() const { double rv; if (_log) rv = muse_db2val(_value); else rv = _value; return rv; } //--------------------------------------------------------- // minLogValue //--------------------------------------------------------- //double FloatEntry::minValue() const { // return _log ? pow(10.0, _minValue*0.05f) : _minValue; //} //--------------------------------------------------------- // setMinLogValue //--------------------------------------------------------- void FloatEntry::setMinLogValue(double val) { if (_log) { if (val == 0.0f) _minValue = -100; else _minValue = fast_log10(val) * 20.0f; } else _minValue = val; } //--------------------------------------------------------- // maxLogValue //--------------------------------------------------------- //double FloatEntry::maxValue() const { // return _log ? pow(10.0, _maxValue*0.05f) : _maxValue; //} //--------------------------------------------------------- // setMaxLogValue //--------------------------------------------------------- void FloatEntry::setMaxLogValue(double val) { if (_log) { _maxValue = fast_log10(val) * 20.0f; } else _maxValue = val; } } muse-4.0.0/src/awl/floatentry.h000066400000000000000000000106011404105311000163610ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __FLOATLABEL_H__ #define __FLOATLABEL_H__ #include class QMouseEvent; class QTimer; class QWheelEvent; namespace Awl { //--------------------------------------------------------- // FloatEntry //--------------------------------------------------------- class FloatEntry : public QLineEdit { Q_OBJECT Q_PROPERTY(int id READ id WRITE setId) Q_PROPERTY(double minValue READ minValue WRITE setMinValue) Q_PROPERTY(double maxValue READ maxValue WRITE setMaxValue) Q_PROPERTY(QString specialText READ specialText WRITE setSpecialText) Q_PROPERTY(QString suffix READ suffix WRITE setSuffix) Q_PROPERTY(int precision READ precision WRITE setPrecision) Q_PROPERTY(bool log READ log WRITE setLog) int button; int starty; QTimer* timer; double evx; int timecount; double _minValue, _maxValue; QString _specialText; // text to show if value outside min,max QString _suffix; int _precision; bool _log; bool _leftMouseButtonCanDecrease; double calcIncrement() const; virtual void wheelEvent(QWheelEvent*); virtual void mousePressEvent(QMouseEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual void mouseDoubleClickEvent(QMouseEvent*); virtual void mouseReleaseEvent(QMouseEvent*); virtual void setSValue(const QString&); virtual bool setString(double); virtual void incValue(double); virtual void decValue(double); void contextMenuEvent(QContextMenuEvent*); void updateValue(); protected: int _id; double _value; virtual void valueChange(); private slots: void repeat(); protected slots: void endEdit(); public slots: virtual void setValue(double); signals: void valueChanged(double, int); public: FloatEntry(QWidget* parent, bool leftMouseButtonCanDecrease = false); virtual QSize sizeHint() const; virtual double value() const; int id() const { return _id; } void setId(int i) { _id = i; } double minValue() const { return _minValue; } double maxValue() const { return _maxValue; } void setMinValue(double v) { _minValue = v; } void setMinLogValue(double v); void setMaxValue(double v) { _maxValue = v; } void setMaxLogValue(double v); void setRange(double a, double b) { _minValue = a; _maxValue = b; } void setLogRange(double a, double b) { setMinLogValue(a); setMaxLogValue(b); } int precision() const { return _precision; } void setPrecision(int val); QString specialText() const { return _specialText; } void setSpecialText(const QString& s) { _specialText = s; update(); } QString suffix() const { return _suffix; } void setSuffix(const QString& s) { _suffix = s; } bool log() const { return _log; } void setLog(bool v) { _log = v; } bool leftMouseButtonCanDecrease() const { return _leftMouseButtonCanDecrease; } void setLeftMouseButtonCanDecrease(bool v) { _leftMouseButtonCanDecrease = v; } }; } #endif muse-4.0.0/src/awl/knob.cpp000066400000000000000000000207251404105311000154660ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "muse_math.h" #include "awl/knob.h" #include #include namespace Awl { //--------------------------------------------------------- // Knob /// this is the AwlKnob constructor //--------------------------------------------------------- Knob::Knob(QWidget* parent) : AbstractSlider(parent) { _scaleSize = 270; _markSize = 6; _border = 2; points = 0; } //--------------------------------------------------------- // Knob //--------------------------------------------------------- Knob::~Knob() { if (points) delete points; } //--------------------------------------------------------- // setText //--------------------------------------------------------- void Knob::setText(const QString& s) { if (s != _text) { _text = s; update(); } } //--------------------------------------------------------- // setScaleSize //! set the scale size in degrees // //! the scale size is the max moving angle measured //! in degrees //--------------------------------------------------------- void Knob::setScaleSize(int val) { if (val != _scaleSize) { _scaleSize = val; update(); } } //--------------------------------------------------------- // setMarkSize //! set size of the center marker //--------------------------------------------------------- void Knob::setMarkSize(int val) { if (val != _markSize) { _markSize = val; update(); } } //--------------------------------------------------------- // setBorder //! set border size //--------------------------------------------------------- void Knob::setBorder(int val) { if (val != _border) { _border = val; update(); } } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void Knob::mousePressEvent(QMouseEvent* ev) { startY = ev->y(); emit sliderPressed(_id); if (_center) { QRect r(points->boundingRect().toRect()); if (r.contains(ev->pos())) { setValue(.0f); valueChange(); update(); } } } //--------------------------------------------------------- // mouseReleaseEvent //--------------------------------------------------------- void Knob::mouseReleaseEvent(QMouseEvent*) { emit sliderReleased(_id); } //--------------------------------------------------------- // mouseMoveEvent //--------------------------------------------------------- void Knob::mouseMoveEvent(QMouseEvent* ev) { int y = ev->y(); double delta = (maxValue() - minValue()) / 100.0f; if (delta == 0) delta = 1; _value += (startY - y) * delta; if (_value < minValue()) _value = _minValue; else if (_value > maxValue()) _value = _maxValue; startY = y; valueChange(); update(); } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void Knob::paintEvent(QPaintEvent* /*ev*/) { //QRect rr(ev->rect()); QPainter p(this); p.setRenderHint(QPainter::Antialiasing, true); int markSize2 = _markSize/2; int restR = 360 - _scaleSize; int w = width() - _scaleWidth - 2 * _border; int h = height() - _scaleWidth/2 - 2 * _border; int xoffset, yoffset; if (_center) { h -= _markSize; } if (w > h) { yoffset = 0; xoffset = (w - h) / 2; w = h; } else { xoffset = 0; // yoffset = (h - w) / 2; // center yoffset = h - w; // top align h = w; } int x = xoffset + _scaleWidth / 2 + _border; int y = yoffset + _scaleWidth / 2 + _border + (_center ? _markSize+_scaleWidth/2 : 0); QRectF ar(x, y, w, h); QColor sc(isEnabled() ? _scaleColor : Qt::gray); QColor svc(isEnabled() ? _scaleValueColor : Qt::gray); //----------------------------------------- // draw arc //----------------------------------------- double dvalue = maxValue() - minValue(); if (_center) { int size = _scaleSize * 8; if (_value >= 0) { int offset = (restR-180) * 8; int r1 = int (size * _value / maxValue()); int r2 = size - r1; p.setPen(QPen(sc, _scaleWidth)); if (r2 > 1) p.drawArc(ar, offset, r2); if (size > 1) p.drawArc(ar, 90*16, size); if (r1 > 1) { p.setPen(QPen(svc, _scaleWidth)); p.drawArc(ar, offset+r2, r1); } } else { // int offset = (restR+180) * 8; int r1 = int(size * _value / minValue()); int r2 = size - r1; p.setPen(QPen(sc, _scaleWidth)); if (size > 1) p.drawArc(ar, (restR-180)*8, size); if (r2 > 1) p.drawArc(ar, 90 * 16 + r1, r2); if (r1 > 1) { p.setPen(QPen(svc, _scaleWidth)); p.drawArc(ar, 90*16, r1); } } } else { int offset = (180-restR) * 8; int size = _scaleSize * 16; int r1 = int(size * (_value - minValue()) / dvalue); int r2 = size - r1; if (r2 >= 1) { p.setPen(QPen(sc, _scaleWidth)); p.drawArc(ar, -offset, r2); } if (r1 >= 1) { p.setPen(QPen(svc, _scaleWidth)); p.drawArc(ar, r2-offset, r1); } } //----------------------------------------- // draw pointer //----------------------------------------- p.setPen(QPen(svc, _scaleWidth)); double r1 = double(_scaleSize) * (_value-minValue()) / dvalue + 90.0 + double(restR/2); r1 = r1 / 180.0 * M_PI; // convert to radians int rd = w/2; int x1 = x + rd; int y1 = y + rd; int x2 = x1 + lrint(cos(r1) * double(rd)); int y2 = y1 + lrint(sin(r1) * double(rd)); p.drawLine(x1, y1, x2, y2); //----------------------------------------- // draw center mark //----------------------------------------- p.setPen(QPen(svc, 0)); p.setBrush(svc); if (_center) { if (points) delete points; qreal x = ar.width() / 2 + ar.x(); qreal y = ar.y() - _markSize - _scaleWidth/2; points = new QPainterPath(QPointF(x - markSize2, y)); points->lineTo(x + markSize2, y); points->lineTo(x, _markSize + y); points->closeSubpath(); p.drawPath(*points); } //----------------------------------------- // draw text //----------------------------------------- if (!_text.isEmpty()) { p.drawText(rect(), Qt::AlignBottom | Qt::AlignHCenter, _text); } } } muse-4.0.0/src/awl/knob.h000066400000000000000000000054311404105311000151300ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLKNOB_H__ #define __AWLKNOB_H__ #include #include #include #include #include #include "aslider.h" // #include namespace Awl { //--------------------------------------------------------- // Knob // //! Base class of all dialer type input widgets. // //! xxxxxxxxx //! //--------------------------------------------------------- class Q_DECL_EXPORT Knob : public AbstractSlider { Q_OBJECT Q_PROPERTY(int scaleSize READ scaleSize WRITE setScaleSize) Q_PROPERTY(int markSize READ markSize WRITE setMarkSize) Q_PROPERTY(int border READ border WRITE setBorder) Q_PROPERTY(QString text READ text WRITE setText) int _scaleSize; //! scale size in degrees int _markSize; int _border; QPainterPath* points; virtual void paintEvent(QPaintEvent*); virtual void mousePressEvent(QMouseEvent*); virtual void mouseReleaseEvent(QMouseEvent*); virtual void mouseMoveEvent(QMouseEvent*); protected: int startY; QString _text; signals: void sliderPressed(int); void sliderReleased(int); public: Knob(QWidget* parent = 0); ~Knob(); virtual QSize sizeHint() const { return QSize(50, 50); } virtual int heightForWidth(int w) const { return w; } //! return text decoration QString text() const { return _text; } void setText(const QString& s); //! return scale size in degrees int scaleSize() const { return _scaleSize; } void setScaleSize(int val); int markSize() const { return _markSize; } void setMarkSize(int val); int border() const { return _border; } void setBorder(int val); }; } #endif muse-4.0.0/src/awl/ltest.cpp000066400000000000000000000024121404105311000156610ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include #include "awlplugin.h" //--------------------------------------------------------- // main //--------------------------------------------------------- int main(int /*argc*/, char* /*argv*/[]) { printf("Hello MusE\n"); new KnobPlugin; return 0; } muse-4.0.0/src/awl/metronom.xpm000066400000000000000000000007541404105311000164170ustar00rootroot00000000000000/* XPM */ static char *metronom_xpm[]={ "18 18 3 1", ". c None", "# c #000000", "a c #0000ff", ".........##.......", ".........##.......", "........###....a..", ".......###.#..aaa.", ".......##..#.aaa..", "......###...#aa...", "......##....aaa...", ".....##....aaa....", "....###...aaa#....", "....##....aa.#....", "...###...aa...#...", "..###...aaa...#...", "..##...aaa....#...", ".###...aaa.....#..", ".##....aa......#..", "###.....a.......#.", "##################", "##################"}; muse-4.0.0/src/awl/midimeter.cpp000066400000000000000000000076541404105311000165220ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "fastlog.h" #include "midimeter.h" #include #include namespace Awl { //--------------------------------------------------------- // MidiMeter //--------------------------------------------------------- MidiMeter::MidiMeter(QWidget* parent) : Slider(parent) { setRange(0.0f, 127.0f); setLineStep(2); setPageStep(4); setScaleWidth(7); _meterWidth = _scaleWidth * 3; meterval = 0.0f; } //--------------------------------------------------------- // setMeterVal // v - 0.0 < 1.0 //--------------------------------------------------------- void MidiMeter::setMeterVal(double v) { if (v < 0.001) v = .0f; if (meterval != v) { meterval = v; update(); } } //--------------------------------------------------------- // mouseDoubleClickEvent //--------------------------------------------------------- void MidiMeter::mouseDoubleClickEvent(QMouseEvent*) { _value = _minValue; valueChange(); update(); } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void MidiMeter::paintEvent(QPaintEvent* /*ev*/) { int pixel = height() - sliderSize().height(); double range = maxValue() - minValue(); int ppos = int(pixel * (_value - minValue()) / range); if (_invert) ppos = pixel - ppos; //QRect rr(ev->rect()); QPainter p(this); //QColor sc(isEnabled() ? _scaleColor : Qt::gray); QColor svc(isEnabled() ? _scaleValueColor : Qt::gray); p.setBrush(svc); int h = height(); int kh = sliderSize().height(); //--------------------------------------------------- // draw meter //--------------------------------------------------- int mw = _meterWidth; int x = 0; int y1 = kh / 2; int y3 = h - y1; int mh = h - kh; p.setPen(Qt::white); h = lrint(meterval * mh); if (h < 0) h = 0; else if (h > mh) h = mh; p.fillRect(x, y3-h, mw, h, QBrush(0x00ff00)); // green p.fillRect(x, y1, mw, mh-h, QBrush(0x007000)); // dark green x += mw; #if 0 //--------------------------------------------------- // draw scale //--------------------------------------------------- x += _scaleWidth/2; p.setPen(QPen(sc, _scaleWidth)); p.drawLine(x, y1, x, y2); p.setPen(QPen(svc, _scaleWidth)); p.drawLine(x, y2, x, y3); //--------------------------------------------------- // draw slider //--------------------------------------------------- x += _scaleWidth/2; p.setPen(QPen(svc, 0)); points.setPoint(0, x, y2); points.setPoint(1, x + kw, y2 - kh/2); points.setPoint(2, x + kw, y2 + kh/2); p.drawConvexPolygon(points); #endif } } muse-4.0.0/src/awl/midimeter.h000066400000000000000000000033251404105311000161560ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLMIDIMETER_H__ #define __AWLMIDIMETER_H__ #include "slider.h" namespace Awl { //--------------------------------------------------------- // MidiMeter //--------------------------------------------------------- class MidiMeter : public Slider { Q_OBJECT Q_PROPERTY(int meterWidth READ meterWidth WRITE setMeterWidth) double meterval; int _meterWidth; virtual void paintEvent(QPaintEvent*); protected: virtual void mouseDoubleClickEvent(QMouseEvent*); public slots: void setMeterVal(double value); public: MidiMeter(QWidget* parent = 0); int meterWidth() const { return _meterWidth; } void setMeterWidth(int v) { _meterWidth = v; } }; } #endif muse-4.0.0/src/awl/midimslider.cpp000066400000000000000000000104111404105311000170260ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "fastlog.h" #include "midimslider.h" #include #include namespace Awl { //--------------------------------------------------------- // MidiMeterSlider //--------------------------------------------------------- MidiMeterSlider::MidiMeterSlider(QWidget* parent) : Slider(parent) { setRange(0.0f, 127.0f); setLineStep(2); setPageStep(4); setScaleWidth(7); _meterWidth = _scaleWidth * 3; meterval = 0.0f; } //--------------------------------------------------------- // setMeterVal // v - 0.0 < 1.0 //--------------------------------------------------------- void MidiMeterSlider::setMeterVal(double v) { if (v < 0.001) v = .0f; if (meterval != v) { meterval = v; update(); } } //--------------------------------------------------------- // mouseDoubleClickEvent //--------------------------------------------------------- void MidiMeterSlider::mouseDoubleClickEvent(QMouseEvent*) { _value = _minValue; valueChange(); update(); } //--------------------------------------------------------- // drawScale //--------------------------------------------------------- void MidiMeterSlider::paintEvent(QPaintEvent* /*ev*/) { int pixel = height() - sliderSize().height(); double range = maxValue() - minValue(); int ppos = int(pixel * (_value - minValue()) / range); if (_invert) ppos = pixel - ppos; //QRect rr(ev->rect()); QPainter p(this); QColor sc(isEnabled() ? _scaleColor : Qt::gray); QColor svc(isEnabled() ? _scaleValueColor : Qt::gray); p.setBrush(svc); int h = height(); // int kw = sliderSize().width(); int kh = sliderSize().height(); //--------------------------------------------------- // draw meter //--------------------------------------------------- int mw = _meterWidth; int x = 0; int y1 = kh / 2; int y2 = h - (ppos + y1); int y3 = h - y1; int mh = h - kh; p.setPen(Qt::white); h = lrint(meterval * mh); if (h < 0) h = 0; else if (h > mh) h = mh; //QColor qc; //float v = 5.0/6.0 + 1.0/6.0 * meterval; //qc.setHsvF(1.0/3.0, 1.0, (v<=1.0?v:1.0)); p.fillRect(x, y3-h, mw, h, QBrush(0x00ff00)); // green //p.fillRect(x, y3-h, mw, h, QBrush(qc)); // green get a bit darker p.fillRect(x, y1, mw, mh-h, QBrush(0x007000)); // dark green x += mw; //--------------------------------------------------- // draw scale //--------------------------------------------------- // x += _scaleWidth/2; // p.setPen(QPen(sc, _scaleWidth)); // p.drawLine(x, y1, x, y2); // p.setPen(QPen(svc, _scaleWidth)); // p.drawLine(x, y2, x, y3); p.fillRect(x, y1, _scaleWidth, y2-y1, sc); p.fillRect(x, y2, _scaleWidth, y3-y2, svc); //--------------------------------------------------- // draw slider //--------------------------------------------------- x += _scaleWidth/2; p.setPen(QPen(svc, 0)); p.translate(QPointF(x, y2)); p.setRenderHint(QPainter::Antialiasing, true); p.drawPath(*points); } } muse-4.0.0/src/awl/midimslider.h000066400000000000000000000033531404105311000165020ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLMIDIMSLIDER_H__ #define __AWLMIDIMSLIDER_H__ #include "slider.h" namespace Awl { //--------------------------------------------------------- // MidiMeterSlider //--------------------------------------------------------- class MidiMeterSlider : public Slider { Q_OBJECT Q_PROPERTY(int meterWidth READ meterWidth WRITE setMeterWidth) double meterval; int _meterWidth; virtual void paintEvent(QPaintEvent*); protected: virtual void mouseDoubleClickEvent(QMouseEvent*); public slots: void setMeterVal(double value); public: MidiMeterSlider(QWidget* parent = 0); int meterWidth() const { return _meterWidth; } void setMeterWidth(int v) { _meterWidth = v; } }; } #endif muse-4.0.0/src/awl/midipanentry.cpp000066400000000000000000000031641404105311000172360ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "fastlog.h" #include "midipanentry.h" namespace Awl { //--------------------------------------------------------- // MidiPanEntry //--------------------------------------------------------- MidiPanEntry::MidiPanEntry(QWidget* parent, bool leftMouseButtonCanDecrease) : FloatEntry(parent, leftMouseButtonCanDecrease) { setRange(-64.0f, 63.0f); setFrame(true); setPrecision(0); } //--------------------------------------------------------- // valueChange //--------------------------------------------------------- void MidiPanEntry::valueChange() { emit valueChanged(_value + 64.0f, _id); } } muse-4.0.0/src/awl/midipanentry.h000066400000000000000000000034731404105311000167060ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLMIDIPANENTRY_H__ #define __AWLMIDIPANENTRY_H__ #include "floatentry.h" namespace Awl { //--------------------------------------------------------- // MidiPanEntry //--------------------------------------------------------- class MidiPanEntry : public FloatEntry { Q_OBJECT bool _leftMouseButtonCanDecrease; protected: virtual void valueChange(); public slots: virtual void setValue(double v) { FloatEntry::setValue(v - 64.0f); } public: MidiPanEntry(QWidget* parent, bool leftMouseButtonCanDecrease = false); virtual double value() const { return _value + 64.0f; } bool leftMouseButtonCanDecrease() const { return _leftMouseButtonCanDecrease; } void setLeftMouseButtonCanDecrease(bool v) { _leftMouseButtonCanDecrease = v; } }; } #endif muse-4.0.0/src/awl/midipanknob.cpp000066400000000000000000000025121404105311000170220ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "midipanknob.h" namespace Awl { //--------------------------------------------------------- // MidiPanKnob //--------------------------------------------------------- MidiPanKnob::MidiPanKnob(QWidget* parent) : Knob(parent) { setCenter(true); setRange(-64.0f, 63.0f); setLineStep(1.0f); setPageStep(10.0f); } } muse-4.0.0/src/awl/midipanknob.h000066400000000000000000000033721404105311000164740ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLMIDIPANKNOB_H__ #define __AWLMIDIPANKNOB_H__ #include "knob.h" namespace Awl { //--------------------------------------------------------- // MidiPanKnob //! MidiPan Knob entry widget // //! This widget implements a centered floating point //! knob used to adjust the pan position in an audio //! mixer. //--------------------------------------------------------- class MidiPanKnob : public Knob { Q_OBJECT virtual void valueChange() { emit valueChanged(_value + 64.0f, _id); } public slots: virtual void setValue(double v) { AbstractSlider::setValue(v - 64.0f); } public: MidiPanKnob(QWidget* parent = 0); virtual double value() const { return _value + 64.0f; } }; } #endif muse-4.0.0/src/awl/midivolentry.cpp000066400000000000000000000033261404105311000172600ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "fastlog.h" #include "midivolentry.h" namespace Awl { //--------------------------------------------------------- // MidiVolEntry //--------------------------------------------------------- MidiVolEntry::MidiVolEntry(QWidget* parent, bool leftMouseButtonCanDecrease) : FloatEntry(parent, leftMouseButtonCanDecrease) { _max = 127; setRange(-98.0f, 0.0f); setSpecialText(tr("off")); setSuffix("dB"); setFrame(true); setPrecision(0); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void MidiVolEntry::setValue(double v) { FloatEntry::setValue(-fast_log10(double(_max*_max)/(v*v))*20.0f); } } muse-4.0.0/src/awl/midivolentry.h000066400000000000000000000034161404105311000167250ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLMIDIVOLENTRY_H__ #define __AWLMIDIVOLENTRY_H__ #include "floatentry.h" namespace Awl { //--------------------------------------------------------- // MidiVolEntry //--------------------------------------------------------- class MidiVolEntry : public FloatEntry { Q_OBJECT Q_PROPERTY(int max READ max WRITE setMax) int _max; bool _leftMouseButtonCanDecrease; public: virtual void setValue(double); void setMax(int val) { _max = val; } int max() const { return _max; } MidiVolEntry(QWidget* parent, bool leftMouseButtonCanDecrease = false); bool leftMouseButtonCanDecrease() const { return _leftMouseButtonCanDecrease; } void setLeftMouseButtonCanDecrease(bool v) { _leftMouseButtonCanDecrease = v; } }; } #endif muse-4.0.0/src/awl/mslider.cpp000066400000000000000000000212111404105311000161630ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "fastlog.h" #include "mslider.h" #include #include namespace Awl { //--------------------------------------------------------- // MeterSlider //--------------------------------------------------------- MeterSlider::MeterSlider(QWidget* parent) : VolSlider(parent) { setAttribute(Qt::WA_NoSystemBackground, true); // setAutoFillBackground(true); _channel = 0; yellowScale = -16; //-10; redScale = 0; _meterWidth = _scaleWidth * 3; setChannel(1); setMinimumHeight(50); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize MeterSlider::sizeHint() const { int w = _meterWidth + _scaleWidth + _scaleWidth + 30; return orientation() == Qt::Vertical ? QSize(w, 200) : QSize(200, w); } //--------------------------------------------------------- // setChannel //--------------------------------------------------------- void MeterSlider::setChannel(int n) { if (n > _channel) { for (int i = _channel; i < n; ++i) { meterval.push_back(0.0f); meterPeak.push_back(0.0f); } } _channel = n; } //--------------------------------------------------------- // setMeterVal //--------------------------------------------------------- void MeterSlider::setMeterVal(int channel, double v, double peak) { bool mustRedraw = false; if (meterval[channel] != v) { meterval[channel] = v; mustRedraw = true; } if (peak != meterPeak[channel]) { meterPeak[channel] = peak; mustRedraw = true; } if (mustRedraw) { int kh = sliderSize().height(); int mh = height() - kh; update(20, kh / 2, _meterWidth-1, mh); } } //--------------------------------------------------------- // resetPeaks // reset peak and overflow indicator //--------------------------------------------------------- void MeterSlider::resetPeaks() { for (int i = 0; i < _channel; ++i) meterPeak[i] = meterval[i]; update(); } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void MeterSlider::resizeEvent(QResizeEvent* /*ev*/) { int h = height(); int kh = sliderSize().height(); int mh = h - kh; int mw = _meterWidth / _channel; onPm = QPixmap(mw, mh); offPm = QPixmap(mw, mh); double range = maxValue() - minValue(); int h1 = mh - lrint((maxValue() - redScale) * mh / range); int h2 = mh - lrint((maxValue() - yellowScale) * mh / range); QColor yellowRed; yellowRed.setHsv(QColor(Qt::yellow).hue()-8, QColor(Qt::yellow).saturation(), QColor(Qt::yellow).value()); QColor yellRedRed; yellRedRed.setHsv(QColor(Qt::yellow).hue()-16, QColor(Qt::yellow).saturation(), QColor(Qt::yellow).value()); QLinearGradient linearGrad(QPointF(0, 0), QPointF(0, mh)); linearGrad.setColorAt(0, Qt::red); linearGrad.setColorAt(1-(double)(h1-5)/(double)mh, yellRedRed); linearGrad.setColorAt(1-(double)(h1-6)/(double)mh, yellowRed); linearGrad.setColorAt(1-(double)h2/(double)mh, Qt::yellow); linearGrad.setColorAt(1, Qt::green); QColor darkYellowRed; darkYellowRed.setHsv(QColor(Qt::darkYellow).hue()-8, QColor(Qt::darkYellow).saturation(), QColor(Qt::darkYellow).value()); QColor darkYellRedRed; darkYellRedRed.setHsv(QColor(Qt::darkYellow).hue()-16, QColor(Qt::darkYellow).saturation(), QColor(Qt::darkYellow).value()); QLinearGradient linearDarkGrad(QPointF(0, 0), QPointF(0, mh)); linearDarkGrad.setColorAt(0, Qt::darkRed); linearDarkGrad.setColorAt(1-(double)(h1-5)/(double)mh, darkYellRedRed); linearDarkGrad.setColorAt(1-(double)(h1-6)/(double)mh, darkYellowRed); linearDarkGrad.setColorAt(1-(double)h2/(double)mh, Qt::darkYellow); linearDarkGrad.setColorAt(1, Qt::darkGreen); QPainter p; p.begin(&onPm); p.fillRect(0, 0, mw, mh, linearGrad); p.end(); p.begin(&offPm); p.fillRect(0, 0, mw, mh, linearDarkGrad); p.end(); } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void MeterSlider::paintEvent(QPaintEvent* ev) { int pixel = height() - sliderSize().height(); double range = maxValue() - minValue(); int ppos = int(pixel * (_value - minValue()) / range); if (_invert) ppos = pixel - ppos; QPainter p(this); p.setRenderHint(QPainter::Antialiasing, false); int h = height(); int kh = sliderSize().height(); //--------------------------------------------------- // draw meter //--------------------------------------------------- int mw = _meterWidth / _channel; int x = 20; int y1 = kh / 2; int y3 = h - y1; int mh = h - kh; p.setPen(QPen(Qt::white, 2)); for (int i = 0; i < _channel; ++i) { int h = mh - (lrint(fast_log10(meterval[i]) * -20.0f * mh / range)); if (h < 0) h = 0; else if (h > mh) h = mh; p.drawPixmap(x, y1+mh-h, mw, h, onPm, 0, y1+mh-h, mw, h); p.drawPixmap(x, y1, mw, mh-h, offPm, 0, y1, mw, mh-h); //--------------------------------------------------- // draw peak line //--------------------------------------------------- h = mh - (lrint(fast_log10(meterPeak[i]) * -20.0f * mh / range)); if (h > mh) h = mh; if (h > 0) p.drawLine(x, y3-h, x+mw, y3-h); x += mw; } // optimize common case: if (ev->rect() == QRect(20, kh/2, _meterWidth-1, mh)) return; QColor sc(isEnabled() ? _scaleColor : Qt::gray); QColor svc(isEnabled() ? _scaleValueColor : Qt::gray); p.setBrush(svc); //--------------------------------------------------- // draw scale //--------------------------------------------------- int y2 = h - (ppos + y1); p.fillRect(x, y1, _scaleWidth, y2-y1, sc); p.fillRect(x, y2, _scaleWidth, y3-y2, svc); //--------------------------------------------------- // draw tick marks //--------------------------------------------------- QFont f(p.font()); f.setPointSize(6); p.setFont(f); p.setPen(QPen(Qt::darkGray, 2)); QFontMetrics fm(f); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int xt = 20 - fm.horizontalAdvance("00") - 5; #else int xt = 20 - fm.width("00") - 5; #endif QString s; for (int i = 10; i < 70; i += 10) { h = y1 + lrint(i * mh / range); s.setNum(i - 10); p.drawText(xt, h - 3, s); p.drawLine(15, h, 20, h); } //--------------------------------------------------- // draw slider //--------------------------------------------------- x += _scaleWidth/2; p.setPen(QPen(svc, 0)); p.translate(QPointF(x, y2)); p.setRenderHint(QPainter::Antialiasing, true); p.drawPath(*points); } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void MeterSlider::mousePressEvent(QMouseEvent* ev) { if (ev->pos().x() < _meterWidth) { emit meterClicked(); return; } VolSlider::mousePressEvent(ev); } } muse-4.0.0/src/awl/mslider.h000066400000000000000000000043061404105311000156360ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLMSLIDER_H__ #define __AWLMSLIDER_H__ #include "volslider.h" namespace Awl { //--------------------------------------------------------- // MeterSlider // volume slider with meter display //--------------------------------------------------------- class MeterSlider : public VolSlider { Q_OBJECT Q_PROPERTY(int meterWidth READ meterWidth WRITE setMeterWidth) Q_PROPERTY(int channel READ channel WRITE setChannel) int _channel; std::vector meterval; std::vector meterPeak; int yellowScale, redScale; int _meterWidth; QPixmap onPm, offPm; // cached pixmap values virtual void mousePressEvent(QMouseEvent*); virtual void paintEvent(QPaintEvent*); virtual void resizeEvent(QResizeEvent*); signals: void meterClicked(); public slots: void resetPeaks(); void setMeterVal(int channel, double value, double peak); public: MeterSlider(QWidget* parent = 0); void setChannel(int n); int channel() const { return _channel; } int meterWidth() const { return _meterWidth; } void setMeterWidth(int v) { _meterWidth = v; } virtual QSize sizeHint() const; }; } #endif muse-4.0.0/src/awl/panentry.cpp000066400000000000000000000024641404105311000163750ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "panentry.h" namespace Awl { //--------------------------------------------------------- // PanEntry //--------------------------------------------------------- PanEntry::PanEntry(QWidget* parent, bool leftMouseButtonCanDecrease) : FloatEntry(parent, leftMouseButtonCanDecrease) { setRange(-1.0, 1.0); } } muse-4.0.0/src/awl/panentry.h000066400000000000000000000031031404105311000160310ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLPANENTRY_H__ #define __AWLPANENTRY_H__ #include "floatentry.h" namespace Awl { //--------------------------------------------------------- // PanEntry //--------------------------------------------------------- class PanEntry : public FloatEntry { Q_OBJECT bool _leftMouseButtonCanDecrease; public: PanEntry(QWidget* parent, bool leftMouseButtonCanDecrease = false); bool leftMouseButtonCanDecrease() const { return _leftMouseButtonCanDecrease; } void setLeftMouseButtonCanDecrease(bool v) { _leftMouseButtonCanDecrease = v; } }; } #endif muse-4.0.0/src/awl/panknob.cpp000066400000000000000000000024711404105311000161630ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "panknob.h" namespace Awl { //--------------------------------------------------------- // PanKnob //--------------------------------------------------------- PanKnob::PanKnob(QWidget* parent) : Knob(parent) { setCenter(true); setRange(-1.0f, 1.0f); setLineStep(0.01f); setPageStep(0.1f); } } muse-4.0.0/src/awl/panknob.h000066400000000000000000000027061404105311000156310ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLPANKNOB_H__ #define __AWLPANKNOB_H__ #include "knob.h" namespace Awl { //--------------------------------------------------------- // PanKnob //! Pan Knob entry widget // //! This widget implements a centered floating point //! knob used to adjust the pan position in an audio //! mixer. //--------------------------------------------------------- class PanKnob : public Knob { Q_OBJECT public: PanKnob(QWidget* parent = 0); }; } #endif muse-4.0.0/src/awl/pitchedit.cpp000066400000000000000000000053751404105311000165160ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include #include "pitchedit.h" #include "utils.h" #include namespace Awl { //--------------------------------------------------------- // PitchEdit //--------------------------------------------------------- PitchEdit::PitchEdit(QWidget* parent) : QSpinBox(parent) { setRange(0, 127); deltaMode = false; } //--------------------------------------------------------- // keyPressEvent //--------------------------------------------------------- void PitchEdit::keyPressEvent(QKeyEvent* ev) { if (ev->key() == Qt::Key_Return) emit returnPressed(); else if (ev->key() == Qt::Key_Escape) emit escapePressed(); } //--------------------------------------------------------- // mapValueToText //--------------------------------------------------------- QString PitchEdit::textFromValue(int v) const { if (deltaMode) { QString s; s.setNum(v); return s; } else return pitch2string(v); } //--------------------------------------------------------- // mapTextToValue //--------------------------------------------------------- int PitchEdit::valueFromText(const QString & /*text*/) const { printf("AwlPitchEdit: mapTextToValue: not impl.\n"); //if (text) //*text = false; return 0; } //--------------------------------------------------------- // setDeltaMode //--------------------------------------------------------- void PitchEdit::setDeltaMode(bool val) { deltaMode = val; if (deltaMode) setRange(-127, 127); else setRange(0, 127); } void PitchEdit::midiNote(int pitch, int velo) { if (hasFocus() && velo) setValue(pitch); } } muse-4.0.0/src/awl/pitchedit.h000066400000000000000000000032611404105311000161530ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLPITCHEDIT_H__ #define __AWLPITCHEDIT_H__ #include class QKeyEvent; namespace Awl { //--------------------------------------------------------- // PitchEdit //--------------------------------------------------------- class PitchEdit : public QSpinBox { Q_OBJECT bool deltaMode = false; protected: virtual QString textFromValue(int v) const; virtual int valueFromText(const QString&) const; virtual void keyPressEvent(QKeyEvent*); signals: void returnPressed(); void escapePressed(); public: PitchEdit(QWidget* parent); void setDeltaMode(bool); public slots: void midiNote(int pitch, int velo); }; } #endif muse-4.0.0/src/awl/pitchlabel.cpp000066400000000000000000000065561404105311000166520ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "pitchedit.h" #include "pitchlabel.h" #include "utils.h" #include #include namespace Awl { //--------------------------------------------------------- // PitchLabel //--------------------------------------------------------- PitchLabel::PitchLabel() { _pitchMode = true; _value = -1; setFrameStyle(WinPanel | Sunken); setLineWidth(2); setMidLineWidth(3); setValue(0); int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); setIndent(fw); } //--------------------------------------------------------- // setPitchMode //--------------------------------------------------------- void PitchLabel::setPitchMode(bool val) { _pitchMode = val; } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize PitchLabel::sizeHint() const { QFontMetrics fm(font()); int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); int h = fm.height() + fw * 2; // int w = 2 + fm.width(QString("A#8")) + fw * 4; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = 2 + fm.horizontalAdvance(QString("-9999")) + fw * 4; // must display 14Bit controller values #else int w = 2 + fm.width(QString("-9999")) + fw * 4; // must display 14Bit controller values #endif return QSize(w, h).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void PitchLabel::setValue(int val) { if (val == _value) return; _value = val; QString s; if (_pitchMode) s = pitch2string(_value); else s.setNum(_value); setText(s); } //--------------------------------------------------------- // setInt //--------------------------------------------------------- void PitchLabel::setInt(int val) { if (_pitchMode) setPitchMode(false); setValue(val); } //--------------------------------------------------------- // setPitch //--------------------------------------------------------- void PitchLabel::setPitch(int val) { if (!_pitchMode) { setPitchMode(true); } setValue(val); } } muse-4.0.0/src/awl/pitchlabel.h000066400000000000000000000031531404105311000163050ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLPITCHLABEL_H__ #define __AWLPITCHLABEL_H__ #include namespace Awl { //--------------------------------------------------------- // PitchLabel //--------------------------------------------------------- class PitchLabel : public QLabel { Q_OBJECT bool _pitchMode; int _value; protected: QSize sizeHint() const; public slots: void setValue(int); void setInt(int); void setPitch(int); public: PitchLabel(); int value() const { return _value; } void setPitchMode(bool val); bool pitchMode() const { return _pitchMode; } }; } #endif muse-4.0.0/src/awl/posedit.cpp000066400000000000000000000574251404105311000162130ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "al/al.h" #include "awl.h" #include "posedit.h" #include "al/sig.h" #include #include #include #include #include #include namespace Awl { using AL::sigmap; //--------------------------------------------------------- // PosEdit //--------------------------------------------------------- PosEdit::PosEdit(QWidget* parent) : QAbstractSpinBox(parent) { _returnMode = false; cur_minute = cur_sec = cur_frame = cur_subframe = 0; cur_bar = cur_beat = cur_tick = 0; validator = new QIntValidator(this); initialized = false; setReadOnly(false); setSmpte(false); } QSize PosEdit::sizeHint() const { if(const QStyle* st = style()) { st = st->proxy(); QStyleOptionSpinBox option; option.initFrom(this); option.rect = rect(); option.state = QStyle::State_Active | QStyle::State_Enabled; const QRect b_rect = st->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxUp); QFontMetrics fm = fontMetrics(); const int fw = st->pixelMetric(QStyle::PM_SpinBoxFrameWidth); int h = fm.height() + fw * 2; int w = fw * 2 + b_rect.width(); if (_smpte) w += fm.width(QString("000:00:00:00")); else w += fm.width(QString("0000.00.000")); return QSize(w, h).expandedTo(QApplication::globalStrut()); } return QSize(20, 20).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // event // filter Tab and Backtab key events //--------------------------------------------------------- bool PosEdit::event(QEvent* event) { if (event->type() == QEvent::KeyPress) { QKeyEvent* ke = static_cast(event); if (ke->key() == Qt::Key_Return) { //printf("key press event Return\n"); //enterPressed(); bool changed = finishEdit(); if(changed || _returnMode) // Force valueChanged if return mode set, even if not modified. { emit valueChanged(_pos); } emit returnPressed(); emit editingFinished(); return true; } if (ke->key() == Qt::Key_Escape) { //printf("key press event Escape\n"); if(lineEdit()) lineEdit()->undo(); // "By default, isAccepted() is set to true, but don't rely on this as subclasses may // choose to clear it in their constructor." // Just to be sure. Otherwise escape will close a midi editor for example, which is annoying. ke->setAccepted(true); emit escapePressed(); return true; } int segment = curSegment(); if (ke->key() == Qt::Key_Backtab) { if (_smpte) { if (segment == 3) { lineEdit()->setSelection(7, 2); return true; } else if (segment == 2) { lineEdit()->setSelection(4, 2); return true; } else if (segment == 1) { lineEdit()->setSelection(0, 3); return true; } } else { if (segment == 2) { lineEdit()->setSelection(5, 2); return true; } if (segment == 1) { lineEdit()->setSelection(0, 4); return true; } } } if (ke->key() == Qt::Key_Tab) { if (_smpte) { if (segment == 0) { lineEdit()->setSelection(4, 2); return true; } else if (segment == 1) { lineEdit()->setSelection(7, 2); return true; } else if (segment == 2) { lineEdit()->setSelection(10, 2); return true; } } else { if (segment == 0) { lineEdit()->setSelection(5, 2); return true; } if (segment == 1) { lineEdit()->setSelection(8, 3); return true; } } } } else if (event->type() == QEvent::FocusIn) { QFocusEvent* fe = static_cast(event); QAbstractSpinBox::focusInEvent(fe); int segment = curSegment(); switch(segment) { case 0: lineEdit()->setSelection(0,4); break; case 1: lineEdit()->setSelection(5,2); break; case 2: lineEdit()->setSelection(8,3); break; } return true; } else if (event->type() == QEvent::FocusOut) { QFocusEvent* fe = static_cast(event); QAbstractSpinBox::focusOutEvent(fe); if(finishEdit()) emit valueChanged(_pos); emit lostFocus(); emit editingFinished(); return true; } return QAbstractSpinBox::event(event); } //--------------------------------------------------------- // setSmpte //--------------------------------------------------------- void PosEdit::setSmpte(bool f) { _smpte = f; if (_smpte) lineEdit()->setInputMask("999:99:99:99;0"); else lineEdit()->setInputMask("9999.99.999;0"); updateValue(); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void PosEdit::setValue(const MusECore::Pos& time) { if(_pos == time) { // Must check whether actual values dependent on tempo or sig changed... if (_smpte) { int minute, sec, frame, subframe; time.msf(&minute, &sec, &frame, &subframe); if(minute != cur_minute || sec != cur_sec || frame != cur_frame || subframe != cur_subframe) updateValue(); } else { int bar, beat, tick; time.mbt(&bar, &beat, &tick); if(bar != cur_bar || beat != cur_beat || tick != cur_tick) updateValue(); } } else { _pos = time; updateValue(); } } void PosEdit::setValue(const QString& s) { MusECore::Pos time(s); setValue(time); } void PosEdit::setValue(int t) { MusECore::Pos time(t); setValue(time); } //--------------------------------------------------------- // updateValue //--------------------------------------------------------- void PosEdit::updateValue() { QString s; if (_smpte) { _pos.msf(&cur_minute, &cur_sec, &cur_frame, &cur_subframe); s = QString("%1:%2:%3:%4") .arg(cur_minute, 3, 10, QLatin1Char('0')) .arg(cur_sec, 2, 10, QLatin1Char('0')) .arg(cur_frame, 2, 10, QLatin1Char('0')) .arg(cur_subframe, 2, 10, QLatin1Char('0')); } else { _pos.mbt(&cur_bar, &cur_beat, &cur_tick); s = QString("%1.%2.%3") .arg(cur_bar + 1, 4, 10, QLatin1Char('0')) .arg(cur_beat + 1, 2, 10, QLatin1Char('0')) .arg(cur_tick, 3, 10, QLatin1Char('0')); } lineEdit()->setText(s); } //--------------------------------------------------------- // stepEnables //--------------------------------------------------------- QAbstractSpinBox::StepEnabled PosEdit::stepEnabled() const { int segment = curSegment(); QAbstractSpinBox::StepEnabled en = QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled; if (_smpte) { int minute, sec, frame, subframe; _pos.msf(&minute, &sec, &frame, &subframe); switch(segment) { case 0: if (minute == 0) en &= ~QAbstractSpinBox::StepDownEnabled; break; case 1: if (sec == 0) en &= ~QAbstractSpinBox::StepDownEnabled; else if (sec == 59) en &= ~QAbstractSpinBox::StepUpEnabled; break; case 2: if (frame == 0) en &= ~QAbstractSpinBox::StepDownEnabled; else { int nf = 23; // 24 frames sec switch(AL::mtcType) { //case 0: // 24 frames sec // nf = 23; // break; case 1: nf = 24; // 25 frames sec break; case 2: // 30 drop frame case 3: // 30 non drop frame nf = 29; break; default: break; } //if (frame == 23) if (frame >= nf) en &= ~QAbstractSpinBox::StepUpEnabled; } break; case 3: if (subframe == 0) en &= ~QAbstractSpinBox::StepDownEnabled; else if (subframe == 99) en &= ~QAbstractSpinBox::StepUpEnabled; break; } } else { int bar, beat; unsigned tick; AL::sigmap.tickValues(_pos.tick(), &bar, &beat, &tick); unsigned tb = AL::sigmap.ticksBeat(_pos.tick()); unsigned tm = AL::sigmap.ticksMeasure(_pos.tick()); int bm = tm / tb; switch (segment) { case 0: if (bar == 0) en &= ~QAbstractSpinBox::StepDownEnabled; break; case 1: if (beat == 0) en &= ~QAbstractSpinBox::StepDownEnabled; else { if (beat >= (bm-1)) en &= ~QAbstractSpinBox::StepUpEnabled; } break; case 2: if (tick == 0) en &= ~QAbstractSpinBox::StepDownEnabled; else { if (tick >= (tb-1)) en &= ~QAbstractSpinBox::StepUpEnabled; } break; } } return en; } //--------------------------------------------------------- // fixup //--------------------------------------------------------- void PosEdit::fixup(QString& /*input*/) const { //printf("fixup <%s>\n", input.toLatin1().constData()); } //--------------------------------------------------------- // validate //--------------------------------------------------------- QValidator::State PosEdit::validate(QString& s,int& /*i*/) const { //printf("validate string:%s int:%d\n", s.toLatin1().data(), i); //printf("validate string:%s\n", s.toLatin1().data()); QStringList sl = s.split(_smpte ? ':' : '.'); QValidator::State state; QValidator::State rv = QValidator::Acceptable; // "By default, the pos parameter is not used by this [QIntValidator] validator." int dpos = 0; if (_smpte) { if(sl.size() != 4) { printf("validate smpte string:%s sections:%d != 4\n", s.toLatin1().data(), sl.size()); return QValidator::Invalid; } validator->setRange(0, 999); state = validator->validate(sl[0], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; validator->setRange(0, 59); state = validator->validate(sl[1], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; int nf = 23; // 24 frames sec switch(AL::mtcType) { //case 0: // 24 frames sec // nf = 23; // break; case 1: nf = 24; // 25 frames sec break; case 2: // 30 drop frame case 3: // 30 non drop frame nf = 29; break; default: break; } validator->setRange(0, nf); state = validator->validate(sl[2], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; validator->setRange(0, 99); state = validator->validate(sl[3], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; } else { if(sl.size() != 3) { printf("validate bbt string:%s sections:%d != 3\n", s.toLatin1().data(), sl.size()); return QValidator::Invalid; } int tb = AL::sigmap.ticksBeat(_pos.tick()); unsigned tm = AL::sigmap.ticksMeasure(_pos.tick()); if (tm==0) return QValidator::Invalid; int bm = tm / tb; validator->setRange(1, 9999); //printf("validate substring 0:%s\n", sl[0].toLatin1().data()); // Special hack because validator says 0000 is intermediate. if(sl[0] == "0000") return QValidator::Invalid; state = validator->validate(sl[0], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; validator->setRange(1, bm); //printf("validate substring 1:%s\n", sl[1].toLatin1().data()); // Special hack because validator says 00 is intermediate. if(sl[1] == "00") return QValidator::Invalid; state = validator->validate(sl[1], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; validator->setRange(0, tb-1); //printf("validate substring 2:%s\n", sl[2].toLatin1().data()); state = validator->validate(sl[2], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; } return rv; } //--------------------------------------------------------- // curSegment //--------------------------------------------------------- int PosEdit::curSegment() const { QLineEdit* le = lineEdit(); int pos = le->cursorPosition(); int segment = -1; if (_smpte) { if (pos >= 0 && pos <= 3) segment = 0; else if (pos >= 4 && pos <= 6) segment = 1; else if (pos >= 7 && pos <= 9) segment = 2; else if (pos >= 10) segment = 3; } else { if (pos >= 0 && pos <= 4) segment = 0; else if (pos >= 5 && pos <= 7) segment = 1; else if (pos >= 8) segment = 2; else printf("curSegment = -1, pos %d\n", pos); } return segment; } //--------------------------------------------------------- // stepBy //--------------------------------------------------------- void PosEdit::stepBy(int steps) { int segment = curSegment(); int selPos; int selLen; bool changed = false; if (_smpte) { int minute, sec, frame, subframe; _pos.msf(&minute, &sec, &frame, &subframe); switch(segment) { case 0: minute += steps; if (minute < 0) minute = 0; selPos = 0; selLen = 3; break; case 1: sec += steps; if (sec < 0) sec = 0; if (sec > 59) sec = 59; selPos = 4; selLen = 2; break; case 2: { int nf = 23; // 24 frames sec switch(AL::mtcType) { //case 0: // 24 frames sec // nf = 23; // break; case 1: nf = 24; // 25 frames sec break; case 2: // 30 drop frame case 3: // 30 non drop frame nf = 29; break; default: break; } frame += steps; if (frame < 0) frame = 0; //if (frame > 24) //TD frame type? // frame = 24; if (frame > nf) frame = nf; selPos = 7; selLen = 2; } break; case 3: subframe += steps; if (subframe < 0) subframe = 0; if (subframe > 99) subframe = 99; selPos = 10; selLen = 2; break; default: return; } MusECore::Pos newPos(minute, sec, frame, subframe); if (!(newPos == _pos)) { changed = true; _pos = newPos; } } else { int bar, beat, tick; _pos.mbt(&bar, &beat, &tick); int tb = AL::sigmap.ticksBeat(_pos.tick()); //int tb = sigmap.ticksBeat(_pos.tick()); unsigned tm = AL::sigmap.ticksMeasure(_pos.tick()); //unsigned tm = sigmap.ticksMeasure(_pos.tick()); int bm = tm / tb; switch(segment) { case 0: bar += steps; if (bar < 0) bar = 0; selPos = 0; selLen = 4; break; case 1: beat += steps; if (beat < 0) beat = 0; else if (beat >= bm) beat = bm - 1; selPos = 5; selLen = 2; break; case 2: tick += steps; if (tick < 0) tick = 0; else if (tick >= tb) tick = tb -1; selPos = 8; selLen = 3; break; default: return; } MusECore::Pos newPos(bar, beat, tick); if (!(newPos == _pos)) { changed = true; _pos = newPos; } } if (changed) { updateValue(); emit valueChanged(_pos); } lineEdit()->setSelection(selPos, selLen); } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void PosEdit::paintEvent(QPaintEvent* event) { if (!initialized) updateValue(); initialized = true; QAbstractSpinBox::paintEvent(event); } //--------------------------------------------------------- // finishEdit // Return true if position changed. //--------------------------------------------------------- bool PosEdit::finishEdit() { // If our validator did its job correctly, the entire line edit text should be valid now... bool changed = false; QStringList sl = text().split(_smpte ? ':' : '.'); if (_smpte) { if(sl.size() != 4) { printf("finishEdit smpte string:%s sections:%d != 4\n", text().toLatin1().data(), sl.size()); return false; } MusECore::Pos newPos(sl[0].toInt(), sl[1].toInt(), sl[2].toInt(), sl[3].toInt()); if (!(newPos == _pos)) { changed = true; _pos = newPos; } } else { if(sl.size() != 3) { printf("finishEdit bbt string:%s sections:%d != 3\n", text().toLatin1().data(), sl.size()); return false; } MusECore::Pos newPos(sl[0].toInt() - 1, sl[1].toInt() - 1, sl[2].toInt()); if (!(newPos == _pos)) { changed = true; _pos = newPos; } } return changed; } } // namespace Awl muse-4.0.0/src/awl/posedit.h000066400000000000000000000053101404105311000156420ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWL_POSEDIT_H__ #define __AWL_POSEDIT_H__ #include "pos.h" #include namespace Awl { //--------------------------------------------------------- // PosEdit //--------------------------------------------------------- class PosEdit : public QAbstractSpinBox { Q_OBJECT Q_PROPERTY(bool smpte READ smpte WRITE setSmpte) bool _smpte; MusECore::Pos _pos; bool initialized; bool _returnMode; int cur_minute, cur_sec, cur_frame, cur_subframe; int cur_bar, cur_beat, cur_tick; QIntValidator* validator; virtual void paintEvent(QPaintEvent* event); virtual void stepBy(int steps); virtual StepEnabled stepEnabled() const; virtual void fixup(QString& input) const; virtual QValidator::State validate(QString&, int&) const; int curSegment() const; virtual bool event(QEvent*); bool finishEdit(); signals: void valueChanged(const MusECore::Pos&); // Choose these carefully, watch out for focusing recursion. void returnPressed(); void escapePressed(); void lostFocus(); // This is emitted when focus lost or return pressed (same as QAbstractSpinBox). void editingFinished(); public slots: void setValue(const MusECore::Pos& time); void setValue(int t); void setValue(const QString& s); public: PosEdit(QWidget* parent = 0); QSize sizeHint() const; MusECore::Pos pos() const { return _pos; } void setSmpte(bool); bool smpte() const { return _smpte; } void setReturnMode(bool v) { _returnMode = v; } bool returnMode() const { return _returnMode; } void updateValue(); }; } #endif muse-4.0.0/src/awl/poslabel.cpp000066400000000000000000000066351404105311000163420ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "poslabel.h" #include "al/pos.h" namespace Awl { //--------------------------------------------------------- // PosLabel //--------------------------------------------------------- PosLabel::PosLabel(QWidget* parent) : QLabel(parent) { _smpte = false; setFrameStyle(WinPanel | Sunken); setLineWidth(2); setMidLineWidth(3); int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); setIndent(fw); updateValue(); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize PosLabel::sizeHint() const { QFontMetrics fm(font()); int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); int h = fm.height() + fw * 2; int w; if (_smpte) w = 2 + fm.width('9') * 9 + fm.width(':') * 3 + fw * 4; else w = 2 + fm.width('9') * 9 + fm.width('.') * 2 + fw * 4; return QSize(w, h).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // updateValue //--------------------------------------------------------- void PosLabel::updateValue() { QString s; if (_smpte) { int min, sec, frame, subframe; pos.msf(&min, &sec, &frame, &subframe); s = QString("%1:%2:%3:%4") .arg(min, 3, 10, QLatin1Char('0')) .arg(sec, 2, 10, QLatin1Char('0')) .arg(frame, 2, 10, QLatin1Char('0')) .arg(subframe, 2, 10, QLatin1Char('0')); } else { int measure, beat, tick; pos.mbt(&measure, &beat, &tick); s = QString("%1.%2.%3") .arg(measure + 1, 4, 10, QLatin1Char('0')) .arg(beat + 1, 2, 10, QLatin1Char('0')) .arg(tick, 3, 10, QLatin1Char('0')); } setText(s); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void PosLabel::setValue(const AL::Pos& val, bool enable) { setEnabled(enable); pos = val; updateValue(); } //--------------------------------------------------------- // setSmpte //--------------------------------------------------------- void PosLabel::setSmpte(bool val) { _smpte = val; updateValue(); } } muse-4.0.0/src/awl/poslabel.h000066400000000000000000000031251404105311000157760ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLPOSLABEL_H__ #define __AWLPOSLABEL_H__ #include "al/pos.h" namespace Awl { //--------------------------------------------------------- // PosLabel //--------------------------------------------------------- class PosLabel : public QLabel { bool _smpte; AL::Pos pos; Q_OBJECT void updateValue(); protected: QSize sizeHint() const; public slots: void setValue(const AL::Pos&, bool); public: PosLabel(QWidget* parent = 0); AL::Pos value() const { return pos; } void setSmpte(bool); bool smpte() const { return _smpte; } }; } #endif muse-4.0.0/src/awl/sigedit.cpp000066400000000000000000000102451404105311000161610ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "al/al.h" #include "awl.h" #include "sigedit.h" #include "al/sig.h" //#include "sig.h" #include #include #include namespace Awl { using AL::sigmap; //--------------------------------------------------------- // SigEdit //--------------------------------------------------------- SigEdit::SigEdit(QWidget* parent) : QWidget(parent) { initialized = false; slash = new QLabel("/",this); zSpin = new SigSpinBox(this); nSpin = new SigSpinBox(this); zSpin->setFocusPolicy(Qt::StrongFocus); nSpin->setFocusPolicy(Qt::StrongFocus); zSpin->setRange(1,99); nSpin->setDenominator(); nSpin->setRange(1,64); layout = new QHBoxLayout(this); layout->setContentsMargins(0,0,0,0); layout->setSpacing(1); layout->addWidget(zSpin); layout->addWidget(slash); layout->addWidget(nSpin); connect(zSpin, SIGNAL(valueChanged(int)), SLOT(setZ(int))); connect(nSpin, SIGNAL(valueChanged(int)), SLOT(setN(int))); connect(nSpin, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(zSpin, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(nSpin, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(zSpin, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(zSpin, SIGNAL(moveFocus()), SLOT(moveFocus())); connect(nSpin, SIGNAL(moveFocus()), SLOT(moveFocus())); zSpin->selectAll(); } SigEdit::~SigEdit() { delete layout; delete zSpin; delete nSpin; } //--------------------------------------------------------- // moveFocus //--------------------------------------------------------- void SigEdit::moveFocus() { if (zSpin->hasFocus()) { nSpin->setFocus(); nSpin->selectAll(); } else { zSpin->setFocus(); zSpin->selectAll(); } } //--------------------------------------------------------- // setZ //--------------------------------------------------------- void SigEdit::setZ(const int z) { _sig.z=z; emit valueChanged(_sig); } //--------------------------------------------------------- // setN //--------------------------------------------------------- void SigEdit::setN(const int n) { _sig.n=n; if (_sig.isValid()) { nSpin->setStyleSheet(""); emit valueChanged(_sig); } else nSpin->setStyleSheet("QSpinBox { background-color: red; }"); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void SigEdit::setValue(const AL::TimeSignature& s) { _sig = s; updateValue(); } //--------------------------------------------------------- // updateValue //--------------------------------------------------------- void SigEdit::updateValue() { zSpin->setValue(_sig.z); nSpin->setValue(_sig.n); } void SigEdit::paintEvent(QPaintEvent* event) { if (!initialized) updateValue(); initialized = true; QPainter p(this); p.fillRect(event->rect(), p.background()); QWidget::paintEvent(event); } void SigEdit::setFocus() { zSpin->setFocus(); } } muse-4.0.0/src/awl/sigedit.h000066400000000000000000000042031404105311000156230ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __SIGEDIT_H__ #define __SIGEDIT_H__ #include "al/sig.h" //#include "sig.h" #include #include #include #include namespace AL { struct TimeSignature; } namespace Awl { //--------------------------------------------------------- // SigEdit //--------------------------------------------------------- class SigEdit : public QWidget { Q_OBJECT AL::TimeSignature _sig; bool initialized; QLabel *slash; SigSpinBox *zSpin; SigSpinBox *nSpin; QHBoxLayout *layout; virtual void paintEvent(QPaintEvent* event); void updateValue(); signals: void valueChanged(const AL::TimeSignature&); void returnPressed(); void escapePressed(); private slots: void setN(const int n); void setZ(const int z); void moveFocus(); public slots: void setValue(const AL::TimeSignature&); void setFocus(); public: SigEdit(QWidget* parent = 0); ~SigEdit(); AL::TimeSignature sig() const { return _sig; } void setFrame(bool v) { zSpin->setFrame(v); nSpin->setFrame(v); } }; } #endif muse-4.0.0/src/awl/sigspinbox.cpp000066400000000000000000000052111404105311000167130ustar00rootroot00000000000000#include "sigspinbox.h" #include "al/sig.h" #include #include #include #include #include #include #include class MyLineEdit : public QLineEdit { public: MyLineEdit(QWidget* parent = 0) : QLineEdit(parent) {}; protected: virtual void mousePressEvent (QMouseEvent* e) { QLineEdit::mousePressEvent(e); selectAll(); } }; SigSpinBox::SigSpinBox(QWidget *parent) : QSpinBox(parent) { setKeyboardTracking(false); _denominator=false; setLineEdit(new MyLineEdit(this)); } void SigSpinBox::keyPressEvent(QKeyEvent* ev) { switch (ev->key()) { case Qt::Key_Return: QSpinBox::keyPressEvent(ev); emit returnPressed(); return; break; case Qt::Key_Escape: emit escapePressed(); return; break; case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Slash: emit moveFocus(); return; break; default: break; } QSpinBox::keyPressEvent(ev); } void SigSpinBox::setDenominator() { _denominator=true; } void SigSpinBox::stepBy(int step) { if (!_denominator) { setValue(value() + step); return; } AL::TimeSignature sig(4, value()); if (step == 1) { // make sure that sig is valid then increase if (sig.isValid()) setValue(value() * 2); } else if (step == -1) { // make sure that sig is valid then increase if (sig.isValid()) { int v = value() / 2; if (v < 2) v = 2; setValue(v); } } } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize SigSpinBox::sizeHint() const { if(const QStyle* st = style()) { st = st->proxy(); QStyleOptionSpinBox option; option.initFrom(this); option.rect = rect(); option.state = QStyle::State_Active | QStyle::State_Enabled; const QRect b_rect = st->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxUp); QFontMetrics fm(font()); const int fw = st->pixelMetric(QStyle::PM_SpinBoxFrameWidth); int h = fm.height() + fw * 2; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = fw * 2 + b_rect.width() + fm.horizontalAdvance(QString("00")); #else int w = fw * 2 + b_rect.width() + fm.width(QString("00")); #endif return QSize(w, h).expandedTo(QApplication::globalStrut()); } return QSize(20, 20).expandedTo(QApplication::globalStrut()); } muse-4.0.0/src/awl/sigspinbox.h000066400000000000000000000007121404105311000163610ustar00rootroot00000000000000#ifndef SIGSPINBOX_H #define SIGSPINBOX_H #include class SigSpinBox : public QSpinBox { Q_OBJECT bool _denominator; protected: virtual void keyPressEvent(QKeyEvent*); virtual void stepBy(int); public: explicit SigSpinBox(QWidget *parent = 0); void setDenominator(); QSize sizeHint() const; signals: void returnPressed(); void escapePressed(); void moveFocus(); public slots: }; #endif // SIGSPINBOX_H muse-4.0.0/src/awl/slider.cpp000066400000000000000000000171651404105311000160230ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "awl/slider.h" #include namespace Awl { //--------------------------------------------------------- // Slider //--------------------------------------------------------- Slider::Slider(QWidget* parent) : AbstractSlider(parent), orient(Qt::Vertical), _sliderSize(14,14) { init(); } //--------------------------------------------------------- // Slider //--------------------------------------------------------- Slider::Slider(Qt::Orientation orientation, QWidget* parent) : AbstractSlider(parent), orient(orientation), _sliderSize(14,14) { init(); } //--------------------------------------------------------- // Slider //--------------------------------------------------------- void Slider::init() { if (orient == Qt::Vertical) setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); else setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); dragMode = false; points = 0; updateKnob(); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize Slider::sizeHint() const { int w = _sliderSize.width() + scaleWidth(); return orient == Qt::Vertical ? QSize(w, 200) : QSize(200, w); } //--------------------------------------------------------- // Slider //--------------------------------------------------------- Slider::~Slider() { if (points) delete points; } //--------------------------------------------------------- // setOrientation //--------------------------------------------------------- void Slider::setOrientation(Qt::Orientation o) { orient = o; updateKnob(); update(); } //--------------------------------------------------------- // updateKnob //--------------------------------------------------------- void Slider::updateKnob() { if (points) delete points; points = new QPainterPath; int kh = _sliderSize.height(); int kw = _sliderSize.width(); points->moveTo(0.0, 0.0); if (orient == Qt::Vertical) { int kh = _sliderSize.height(); int kh2 = kh / 2; points->lineTo(kw, -kh2); points->lineTo(kw, kh2); } else { int kw2 = kw/2; points->lineTo(-kw2, kh); points->lineTo(kw2, kh); } points->lineTo(0.0, 0.0); } //--------------------------------------------------------- // setInvertedAppearance //--------------------------------------------------------- void Slider::setInvertedAppearance(bool val) { AbstractSlider::setInvertedAppearance(val); update(); } //--------------------------------------------------------- // setSliderSize //--------------------------------------------------------- void Slider::setSliderSize(const QSize& s) { _sliderSize = s; update(); } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void Slider::mousePressEvent(QMouseEvent* ev) { startDrag = ev->pos(); // if (points->boundingRect().toRect().contains(startDrag)) { emit sliderPressed(_id); dragMode = true; int pixel = (orient == Qt::Vertical) ? height() - _sliderSize.height() : width() - _sliderSize.width(); dragppos = int(pixel * (_value - minValue()) / (maxValue() - minValue())); if (_invert) dragppos = pixel - dragppos; // } } //--------------------------------------------------------- // mouseReleaseEvent //--------------------------------------------------------- void Slider::mouseReleaseEvent(QMouseEvent*) { if (dragMode) { emit sliderReleased(_id); dragMode = false; } } //--------------------------------------------------------- // mouseMoveEvent //--------------------------------------------------------- void Slider::mouseMoveEvent(QMouseEvent* ev) { if (!dragMode) return; int delta = startDrag.y() - ev->y(); // if (_invert) // delta = -delta; if (orient == Qt::Horizontal) delta = -delta; int ppos = dragppos + delta; if (ppos < 0) ppos = 0; int pixel = (orient == Qt::Vertical) ? height() - _sliderSize.height() : width() - _sliderSize.width(); if (ppos > pixel) ppos = pixel; int pos = _invert ? (pixel - ppos) : ppos; _value = (pos * (maxValue() - minValue()) / pixel) + minValue() - 0.000001; update(); valueChange(); } //--------------------------------------------------------- // paint // r - phys coord system //--------------------------------------------------------- void Slider::paintEvent(QPaintEvent* /*ev*/) { int h = height(); int w = width(); int kw = _sliderSize.width(); int kh = _sliderSize.height(); int pixel = (orient == Qt::Vertical) ? h - kh : w - kw; double range = maxValue() - minValue(); int ppos = int(pixel * (_value - minValue()) / range); if ((orient == Qt::Vertical && _invert) || (orient == Qt::Horizontal && !_invert)) ppos = pixel - ppos; //QRect rr(ev->rect()); QPainter p(this); QColor sc(isEnabled() ? _scaleColor : Qt::gray); QColor svc(isEnabled() ? _scaleValueColor : Qt::gray); p.setBrush(svc); int kh2 = kh/2; //--------------------------------------------------- // draw scale //--------------------------------------------------- if (orient == Qt::Vertical) { int xm = (w - _scaleWidth - _sliderSize.height()) / 2; int y1 = kh2; int y2 = h - (ppos + y1); int y3 = h - y1; p.fillRect(xm, y1, _scaleWidth, y2-y1, _invert ? svc : sc); p.fillRect(xm, y2, _scaleWidth, y3-y2, _invert ? sc : svc); p.translate(QPointF(xm + _scaleWidth/2, y2)); } else { int ym = (h - _scaleWidth - _sliderSize.height()) / 2; int x1 = kh2; int x2 = w - (ppos + x1); int x3 = w - x1; p.fillRect(x1, ym, x2-x1, _scaleWidth, _invert ? sc : svc); p.fillRect(x2, ym, x3-x2, _scaleWidth, _invert ? svc : sc); p.translate(QPointF(x2, ym + _scaleWidth/2)); } //--------------------------------------------------- // draw slider //--------------------------------------------------- p.setRenderHint(QPainter::Antialiasing, true); p.setPen(QPen(svc, 0)); p.drawPath(*points); } } muse-4.0.0/src/awl/slider.h000066400000000000000000000050261404105311000154610ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLSLIDER_H__ #define __AWLSLIDER_H__ #include #include #include #include #include #include #include "aslider.h" namespace Awl { //--------------------------------------------------------- // Slider // //! Base class of all slider type input widgets. // //! Inherits from AwlKnob //! //--------------------------------------------------------- class Slider : public AbstractSlider { Q_OBJECT Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) Q_PROPERTY(QSize sliderSize READ sliderSize WRITE setSliderSize) Qt::Orientation orient; QSize _sliderSize; QPoint startDrag; bool dragMode; int dragppos; virtual void mouseReleaseEvent(QMouseEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual void paintEvent(QPaintEvent*); void init(); void updateKnob(); protected: QPainterPath* points; virtual void mousePressEvent(QMouseEvent*); signals: void sliderPressed(int); void sliderReleased(int); public: Slider(QWidget* parent = 0); Slider(Qt::Orientation orientation, QWidget* parent = 0); ~Slider(); virtual void setOrientation(Qt::Orientation); Qt::Orientation orientation() const { return orient; } QSize sliderSize() const { return _sliderSize; } void setSliderSize(const QSize& s); virtual void setInvertedAppearance(bool val); virtual QSize sizeHint() const; }; } #endif muse-4.0.0/src/awl/tcanvas.cpp000066400000000000000000001752141404105311000162000ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "tcanvas.h" #include "al/al.h" #include "al/sig.h" #include "al/tempo.h" #include "../muse/gconfig.h" #include "../muse/icons.h" #include "metronom.xpm" #include "clock.xpm" #ifdef __APPLE__ inline double exp10(double a) { return pow(10.0, a); } #endif static QIcon* clockIcon; static QIcon* metronomIcon; FollowMode TimeCanvas::followMode = FOLLOW_JUMP; QPixmap* TimeCanvas::octave; QPixmap* TimeCanvas::mk1; QPixmap* TimeCanvas::mk2; QPixmap* TimeCanvas::mk3; QPixmap* TimeCanvas::mk4; enum DragType { DRAG_RULER, DRAG_CANVASA, DRAG_CANVAS_B, DRAG_PANELA, DRAG_PANELB, DRAG_OTHER }; //--------------------------------------------------------- // TimeCanvas //--------------------------------------------------------- TimeCanvas::TimeCanvas(TimeCanvasType t) : QFrame() { setAttribute(Qt::WA_NoSystemBackground, true); _yFit = false; _tool = PointerTool; type = t; _timeType = AL::TICKS; marker = 0; showCursor = false; ctrlHeight = 0; curPitch = -1; mouseInB = false; dragType = DRAG_OTHER; followPos = true; // set default color canvasBackgroundColor = QColor(0x71, 0x8d, 0xbe); if (clockIcon == 0) { clockIcon = new QIcon(QPixmap(clock_xpm)); metronomIcon = new QIcon(QPixmap(metronom_xpm)); } button = Qt::NoButton; grid = new QGridLayout; grid->setMargin(0); grid->setSpacing(1); setLayout(grid); _widget = new QWidget; _widget->setAttribute(Qt::WA_NoSystemBackground); _widget->setAttribute(Qt::WA_StaticContents); _widget->installEventFilter(this); _widget->setMouseTracking(true); _widget->setAcceptDrops(true); // allow to set slider position before slider range // is known: pos1.setTick(0); pos2.setTick(INT_MAX); hmag = new QSlider(Qt::Horizontal); hmag->setRange(0, 100); _xmagMin = 0.001; _xmagMax = 0.3; _xmag = 0.04; hmag->setValue(xmag2s(_xmag)); vmag = 0; if (type != TIME_CANVAS_DRUMEDIT) { vmag = new QSlider(Qt::Vertical); vmag->setRange(0, 100); vmag->setPageStep(1); } _ymag = 1.0; hbar = new QScrollBar(Qt::Horizontal); hbar->setRange(0, INT_MAX); vbar = new QScrollBar(Qt::Vertical); timeTypeButton = new QToolButton; timeTypeButton->setFixedSize(20, rulerHeight); setTimeType1(AL::TICKS); yRange = 0; switch(type) { case TIME_CANVAS_PIANOROLL: _ymagMin = 0.5; _ymagMax = 3.0; vmag->setValue(lrint((_ymag-_ymagMin)*100.0/(_ymagMax-_ymagMin))); initPianoroll(); break; case TIME_CANVAS_DRUMEDIT: _ymagMin = 1.0; _ymagMax = 1.0; yRange = drumHeight * 128; break; case TIME_CANVAS_WAVEEDIT: _xmagMin = 0.001; _xmagMax = 100.0; _xmag = 0.04; _ymagMin = 1.0; _ymagMax = 10.0; _ymag = 1.0; break; default: _ymagMin = 1.0; _ymagMax = 1.0; break; } updateGeometry(); if (type == TIME_CANVAS_PIANOROLL || type == TIME_CANVAS_DRUMEDIT || type == TIME_CANVAS_WAVEEDIT) { addCtrlButton = new QPushButton(tr("Ctrl"), _widget); addCtrlButton->setGeometry(1, 1, rPanelA.width()-4, rulerHeight-4); addCtrlButton->setToolTip(tr("Add Controller View")); connect(addCtrlButton, SIGNAL(clicked()), SLOT(addCtrlClicked())); } grid->addWidget(_widget, 0, 0, 3, 2); grid->addWidget(hbar, 3, 0, Qt::AlignVCenter); grid->addWidget(hmag, 3, 1, Qt::AlignVCenter); grid->addWidget(timeTypeButton, 0, 2); grid->addWidget(vbar, 1, 2, Qt::AlignHCenter); if (vmag) grid->addWidget(vmag, 2, 2, Qt::AlignHCenter); grid->setColumnStretch(0, 100); grid->setRowStretch(1, 100); _raster = 0; updateScrollBars(); connect(hbar, SIGNAL(valueChanged(int)), SLOT(moveX(int))); connect(vbar, SIGNAL(valueChanged(int)), SLOT(moveY(int))); connect(hmag, SIGNAL(valueChanged(int)), SLOT(scaleX(int))); if (vmag) connect(vmag, SIGNAL(valueChanged(int)), SLOT(scaleY(int))); connect(timeTypeButton, SIGNAL(clicked()), SLOT(toggleTimeType())); } //--------------------------------------------------------- // resizeController //--------------------------------------------------------- void TimeCanvas::resizeController(int h) { if (h == ctrlHeight) return; int updateH = h > ctrlHeight ? h : ctrlHeight; ctrlHeight = h; updateGeometry(); updateScrollBars(); widget()->update(0, widget()->height() - updateH, widget()->width(), updateH); } //--------------------------------------------------------- // eventFilter //--------------------------------------------------------- bool TimeCanvas::eventFilter(QObject* obj, QEvent* event) { if (obj != _widget) return QFrame::eventFilter(obj, event); switch(event->type()) { case QEvent::Paint: { QPainter p(_widget); canvasPaintEvent(((QPaintEvent*)event)->rect(), p); } return true; case QEvent::Resize: updateGeometry(); updateScrollBars(); layout(); return false; case QEvent::MouseButtonDblClick: { QMouseEvent* me = (QMouseEvent*)event; QPoint p(me->pos()); button = me->button(); keyState = me->modifiers(); mouseDoubleClick(me); } return true; case QEvent::MouseButtonPress: { QMouseEvent* me = (QMouseEvent*)event; keyState = me->modifiers(); button = me->button(); QPoint p(me->pos()); int x = p.x() - rRuler.x(); bool shift = keyState & Qt::ShiftModifier; if (rRuler.contains(p)) { dragType = DRAG_RULER; if (shift) { AL::Pos pos(pix2pos(x)); if (button == Qt::LeftButton) emit addMarker(pos); else if (button == Qt::RightButton) emit removeMarker(pos); return true; } } else { dragType = DRAG_OTHER; mousePress(me); } } // go on with MouseMove case QEvent::MouseMove: { QMouseEvent* me = (QMouseEvent*)event; keyState = me->modifiers(); button = me->buttons(); QPoint p(me->pos()); AL::Pos pos(pix2pos(p.x()-rCanvasA.x())); if (dragType == DRAG_OTHER) { if (button == 0 && (rPanelB.contains(p) || rCanvasB.contains(p))) { if (!mouseInB) { mouseInB = true; enterB(); } } else { if (button == 0 && mouseInB) { mouseInB = false; leaveB(); } } if (showCursor && p.x() < rCanvasA.x()) { showCursor = false; widget()->update(rRuler); emit cursorPos(cursor, showCursor); } if (p.x() >= rCanvasA.x() && (cursor != pos)) { int x1 = pos2pix(cursor) + rCanvasA.x(); int x2 = pos2pix(pos) + rCanvasA.x(); QRect r1(x1-1, 0, 2, rRuler.height()); QRect r2(x2-1, 0, 2, rRuler.height()); widget()->update(rRuler & (r1 | r2)); cursor = pos; showCursor = true; emit cursorPos(cursor, showCursor); } if (rRuler.contains(p)) { int b = me->buttons(); if (b == 0) return true; int i = 0; if (b & Qt::MidButton) i = 1; //else if (b & Qt::RightButton) // i = 2; else if (b & Qt::RightButton) { if ((MusEGlobal::config.rangeMarkerWithoutMMB) && (event->modifiers() & Qt::ControlModifier)) i = 1; else i = 2; } if (keyState & Qt::ShiftModifier) emit addMarker(i); emit posChanged(i, pos); } else { mouseMove(p); } } else if (dragType == DRAG_RULER) { int b = me->buttons(); if (b == 0) return true; int i = 0; if (b & Qt::MidButton) i = 1; else if (b & Qt::RightButton) i = 2; if (keyState & Qt::ShiftModifier) emit addMarker(i); emit posChanged(i, pos); } } return true; case QEvent::MouseButtonRelease: { QMouseEvent* me = (QMouseEvent*)event; button = Qt::NoButton; keyState = me->modifiers(); mouseRelease(me); dragType = DRAG_OTHER; } return true; case QEvent::DragEnter: dragEnter((QDragEnterEvent*)event); return true; case QEvent::Drop: drop((QDropEvent*)event); return true; case QEvent::DragMove: dragMove((QDragMoveEvent*)event); return true; case QEvent::DragLeave: dragLeave((QDragLeaveEvent*)event); return true; case QEvent::Leave: { emit cursorPos(cursor, false); showCursor = false; emit pitchChanged(-1); curPitch = -1; QRect r(rRuler); if (!rPanelA.isEmpty()) r |= rPanelA; widget()->update(r); if (mouseInB) { mouseInB = false; // button = ((QMouseEvent*)event)->buttons(); leaveB(); } } return false; case QEvent::Wheel: { QWheelEvent* e = (QWheelEvent*)event; if (e->orientation() != Qt::Vertical) return true; if ((e->modifiers() & Qt::ControlModifier) || (e->modifiers() & Qt::ShiftModifier)) { // // xmag // int oldx = e->x() - rCanvasA.x(); AL::Pos pos(pix2pos(oldx)); int step = e->delta() / 120; if (step > 0) { for (int i = 0; i< step; ++i) _xmag *= 1.1; } else { for (int i = 0; i < -step; ++i) _xmag *= 0.9; } if (_xmag < _xmagMin) _xmag = _xmagMin; else if (_xmag > _xmagMax) _xmag = _xmagMax; hmag->setValue(xmag2s(_xmag)); int newx = pos2pix(pos); updateScrollBars(); hbar->setValue(wpos.x() + (newx - oldx)); updateRulerMag(); magChanged(); _widget->update(); } else { // // scroll // int step = qMin(QApplication::wheelScrollLines() * vbar->singleStep(), vbar->pageStep()); int offset = e->delta() * step / 120; if (vbar->invertedControls()) offset = -offset; if (qAbs(offset) < 1) return true; vbar->setValue(vbar->value() + offset); } } return true; default: // printf("event %d missed\n", event->type()); break; } return false; } void TimeCanvas::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down || e->key() == Qt::Key_Left || e->key() == Qt::Key_Right) keyboardNavigate(e); } //--------------------------------------------------------- // moveX //--------------------------------------------------------- void TimeCanvas::moveX(int x) { int dx = wpos.x() - x; wpos.setX(x); int wh = _widget->height(); if (type == TIME_CANVAS_PIANOROLL || type == TIME_CANVAS_DRUMEDIT || TIME_CANVAS_WAVEEDIT) { _widget->scroll(dx, 0, QRect(rCanvasA.x(), 0, rCanvasA.width(), wh)); //HACK: // update controller names int w = 100 + ((dx > 0) ? dx : 0); _widget->update(rCanvasB.x(), rCanvasB.y(), w, rCanvasB.height()); //HACK: // repaint rounded line end (splitter handle for controller // canvas) int x = rCanvasB.x() + rCanvasB.width() - 1; w = 1; if (dx < 0) { x += dx; w -= dx; } _widget->update(x, rCanvasB.y(), w, rCanvasB.height()); } else _widget->scroll(dx, 0); emit contentsMoving(wpos.x(), wpos.y()); } //--------------------------------------------------------- // moveY //--------------------------------------------------------- void TimeCanvas::moveY(int y) { int dy = wpos.y() - y; if (dy == 0) return; wpos.setY(y); // don't move ruler: int ww = _widget->width(); int wh = _widget->height(); QRect r(0, rulerHeight, ww, wh - rulerHeight - ctrlHeight); _widget->scroll(0, dy, r); emit contentsMoving(wpos.x(), wpos.y()); } //--------------------------------------------------------- // setYPos //--------------------------------------------------------- void TimeCanvas::setYPos(int y) { setWPos(QPoint(wpos.x(), y)); } //--------------------------------------------------------- // setWPos //--------------------------------------------------------- void TimeCanvas::setWPos(const QPoint& p) { if (wpos != p) { wpos = p; hbar->setValue(wpos.x()); vbar->setValue(wpos.y()); _widget->update(); // QCoreApplication::flush(); } } //--------------------------------------------------------- // paintClockRuler //--------------------------------------------------------- void TimeCanvas::paintClockRuler(QPainter& p, const QRect& r) { int x1 = r.x(); int x2 = x1 + r.width(); int y1 = r.y(); int rh = r.height(); if (y1 < rulerHeight) { rh -= rulerHeight - y1; y1 = rulerHeight; } int y2 = y1 + rh; //--------------------------------------------------- // draw Marker //--------------------------------------------------- int y = rulerHeight - 16; p.setPen(Qt::black); p.setFont(_font3); QRect tr(r); tr.setHeight(12); if (marker) { for (AL::iMarker m = marker->begin(); m != marker->end(); ++m) { int xp = mapx(int(m->second.frame())); if (xp > x2) break; AL::iMarker mm = m; ++mm; int xe = x2; if (mm != marker->end()) { xe = mapx(mm->first); } QRect tr(xp, 0, x2 - xp, 11); if (m->second.current()) { p.fillRect(tr, Qt::white); } if (r.intersects(tr)) { int x2; AL::iMarker mm = m; ++mm; if (mm != marker->end()) x2 = mapx(mm->first); else x2 = xp+200; QRect r = QRect(xp+10, 0, x2-xp, 12); p.drawPixmap(xp, 0, *flagIconS); p.drawText(r, Qt::AlignLeft|Qt::AlignVCenter, m->second.name()); } } } p.setPen(Qt::black); if (showCursor) { int xp = pos2pix(cursor); if (xp >= x1 && xp < x2) p.drawLine(xp, 0, xp, rulerHeight); } AL::Pos p1(pix2pos(x1)); AL::Pos p2(pix2pos(x2)); int sec1 = p1.frame() / AL::sampleRate; int sec2 = (p2.frame() + AL::sampleRate - 1) / AL::sampleRate; int sw = lrint(AL::sampleRate * _xmag); if (sw > 20) { for (int sec = sec1; sec < sec2; ++sec) { int min = sec / 60; int sr = sec % 60; int yy; QString s; if (sr == 0) { p.setFont(_font2); s = QString("%1:00").arg(min); yy = y; } else { p.setFont(_font1); s = QString("%1").arg(sr, 2, 10, QLatin1Char('0')); yy = y + 7; } int xp = pos2pix(AL::Pos(sec * AL::sampleRate, AL::FRAMES)); // printf(" sec %d min %d sr %d xp %d\n", sec, min, sr, xp); p.setPen(Qt::black); p.drawLine(xp, yy, xp, rulerHeight); p.drawText(xp + 2, rulerHeight - 4, s); p.setPen(sr == 0 ? Qt::lightGray : Qt::gray); p.drawLine(xp, y1, xp, y2); } } else { int min1 = sec1/60; int min2 = (sec2+59)/60; for (int min = min1; min < min2; ++min) { QString s; p.setFont(_font2); s = QString("%1").arg(min); int xp = pos2pix(AL::Pos(min * AL::sampleRate * 60, AL::FRAMES)); p.setPen(Qt::black); p.drawLine(xp, y, xp, rulerHeight); p.drawText(xp + 2, rulerHeight - 4, s); p.setPen(Qt::lightGray); p.drawLine(xp, y1, xp, y2); } } } //--------------------------------------------------------- // updateRulerMag //--------------------------------------------------------- void TimeCanvas::updateRulerMag() { int bar1, beat, tick; pos1.mbt(&bar1, &beat, &tick); AL::Pos stick(bar1, 0, 0); AL::Pos ntick = AL::Pos(bar1 + 1, 0, 0); int tpix = pos2pix(ntick) - pos2pix(stick); metronomeRulerMag = 0; if (tpix < 64) metronomeRulerMag = 1; if (tpix < 32) metronomeRulerMag = 2; if (tpix <= 16) metronomeRulerMag = 3; if (tpix < 8) metronomeRulerMag = 4; if (tpix <= 4) metronomeRulerMag = 5; if (tpix <= 2) metronomeRulerMag = 6; } //--------------------------------------------------------- // paintMetronomRuler //--------------------------------------------------------- void TimeCanvas::paintMetronomRuler(QPainter& p, const QRect& r) { static const int mag[7] = { 1, 1, 2, 5, 10, 20, 50 }; int x = r.x(); int w = r.width(); int y = rulerHeight - 16; p.setFont(_font3); int h = 14; int y1 = r.y(); int rh = r.height(); if (y1 < rulerHeight) { rh -= rulerHeight - y1; y1 = rulerHeight; } int y2 = y1 + rh; if (x < (MAP_OFFSET - wpos.x())) x = MAP_OFFSET - wpos.x(); AL::Pos pos1 = pix2pos(x); AL::Pos pos2 = pix2pos(x+w); if (marker) { AL::iMarker start = marker->lower_bound(pos1.tick()); if (start != marker->begin()) --start; AL::iMarker end = marker->lower_bound(pos2.tick()); for (AL::iMarker m = start; m != end; ++m) { AL::Pos pm1(m->second); AL::iMarker m2 = m; ++m2; AL::Pos pm2(pos2); if (m2 != marker->end()) pm2 = m2->second; int x1 = pos2pix(pm1); int x2 = pos2pix(pm2); if (pos[0] >= pm1 && (m2 == marker->end() || pos[0] < pm2)) p.fillRect(x1, 0, x2 - x1, 11, Qt::white); QRect r = QRect(x1 + 10, 0, x2 - x1, 12); p.drawPixmap(x1, 0, *flagIconS); p.drawText(r, Qt::AlignLeft|Qt::AlignVCenter, m->second.name()); } } //--------------------------------------------------- // draw raster //--------------------------------------------------- int bar1, bar2, beat, tick; pos1.mbt(&bar1, &beat, &tick); pos2.mbt(&bar2, &beat, &tick); int n = mag[metronomeRulerMag]; bar1 = (bar1 / n) * n; // round down if (bar1 && n >= 2) bar1 -= 1; bar2 = ((bar2 + n - 1) / n) * n; // round up for (int bar = bar1; bar <= bar2;) { AL::Pos stick(bar, 0, 0); if (metronomeRulerMag) { p.setFont(_font2); int x = pos2pix(stick); QString s; s.setNum(bar + 1); p.setPen(Qt::black); p.drawLine(x, y, x, y + h); QRect r = QRect(x+2, y, 1000, h); p.drawText(r, Qt::AlignLeft | Qt::AlignVCenter, s); p.setPen(Qt::lightGray); if (x > 0) p.drawLine(x, y1, x, y2); } else { AL::TimeSignature sig = stick.timesig(); int z = sig.z; for (int beat = 0; beat < z; beat++) { AL::Pos xx(bar, beat, 0); int xp = pos2pix(xx); if (xp < 0) continue; QString s; QRect r(xp+2, y + 1, 1000, h); int y3; int num; if (beat == 0) { num = bar + 1; y3 = y + 2; p.setFont(_font2); } else { num = beat + 1; y3 = y + 8; p.setFont(_font1); r.moveTop(r.top() + 1); } s.setNum(num); p.setPen(Qt::black); p.drawLine(xp, y3, xp, y+h); p.drawText(r, Qt::AlignLeft | Qt::AlignVCenter, s); p.setPen(beat == 0 ? Qt::lightGray : Qt::gray); if (xp > 0) p.drawLine(xp, y1, xp, y2); } } if (bar == 0 && n >= 2) bar += (n-1); else bar += n; } // // draw mouse cursor marker // p.setPen(Qt::black); if (showCursor) { int xp = pos2pix(cursor); if (xp >= x && xp < x+w) p.drawLine(xp, 0, xp, rulerHeight-1); } } //--------------------------------------------------------- // tempoChanged //--------------------------------------------------------- void TimeCanvas::tempoChanged() { widget()->update(rCanvasA.x(), 0, rCanvasA.width(), widget()->height()); } //--------------------------------------------------------- // canvasPaintEvent //--------------------------------------------------------- void TimeCanvas::canvasPaintEvent(const QRect& r, QPainter& p) { if (r.intersects(rButton)) { p.fillRect(rButton, QColor(0xe0, 0xe0, 0xe0)); p.setPen(QPen(Qt::black, 2)); int y = rButton.y() + rButton.height() - 1; p.drawLine(rButton.x(), y, rButton.width(), y); } p.setRenderHint(QPainter::TextAntialiasing, true); QRect par = r & rPanelA; if (!(par.isEmpty() || rPanelA.isEmpty())) { if (type == TIME_CANVAS_DRUMEDIT) { paintDrumList(p, par); } else if (type == TIME_CANVAS_PIANOROLL) { paintPiano(p, par); } else if (type == TIME_CANVAS_WAVEEDIT) { p.fillRect(par, QColor(0xe0, 0xe0, 0xe0)); } } QRect pbr(r & rPanelB); QRect hor(r & (rRuler | rCanvasA | rCanvasB)); QRect car(r & rCanvasA); QRect cbr(r & rCanvasB); bool drawPanelB = !(pbr.isEmpty() || rPanelB.isEmpty()); bool drawRuler = !(hor.isEmpty() || (rRuler.isEmpty() && rCanvasA.isEmpty() && rCanvasB.isEmpty())); bool drawCanvasA = !(car.isEmpty() || rCanvasA.isEmpty()); bool drawCanvasB = !(cbr.isEmpty() || rCanvasB.isEmpty()); // // draw canvas background // p.setClipRect(r); p.setBrushOrigin(QPoint(car.x() + wpos.x(), car.y() + wpos.y())); if (drawCanvasA) { if (canvasBackgroundPixmap.isNull()) { if (type == TIME_CANVAS_DRUMEDIT || type == TIME_CANVAS_PIANOROLL || type == TIME_CANVAS_WAVEEDIT) { QRect rr(car); // paint inactive area different // (darker) QColor c = canvasBackgroundColor.darker(150); int x1 = pos2pix(partPos1) + rCanvasA.x(); if (rr.x() < x1) { QRect r(rr.x(), rr.y(), x1-rr.x(), rr.height()); p.fillRect(r, c); rr.adjust(x1-rr.x(), 0, 0, 0); } int x2 = pos2pix(partPos2) + rCanvasA.x(); int xx2 = rr.x() + rr.width(); if (xx2 > x2) { if (x2 < rr.x()) x2 = rr.x(); QRect r(x2, rr.y(), xx2-x2, rr.height()); p.fillRect(r, c); rr.adjust(0, 0, -(xx2-x2), 0); } if (!rr.isEmpty()) { p.fillRect(rr, canvasBackgroundColor); } } else p.fillRect(car, canvasBackgroundColor); } else { p.drawTiledPixmap(car, canvasBackgroundPixmap, car.topLeft() + QPoint(wpos)); } } if (drawCanvasB) p.fillRect(cbr, canvasBackgroundColor); //--------------------------------------------------- // draw Ruler //--------------------------------------------------- if (drawRuler) { QRect rrr(r & rRuler); if (!rrr.isEmpty()) p.fillRect(rrr, QColor(0xe0, 0xe0, 0xe0)); int x1 = hor.x(); int x2 = x1 + hor.width(); int y1 = rulerHeight - 17; int y2 = rulerHeight - 1; p.setPen(QPen(Qt::black, 1)); p.drawLine(x1, y1, x2, y1); p.setPen(QPen(Qt::black, 2)); p.drawLine(x1, y2, x2, y2); QPoint off(rRuler.topLeft()); p.translate(off); if (_timeType == AL::TICKS) paintMetronomRuler(p, hor.translated(-off)); else paintClockRuler(p, hor.translated(-off)); p.translate(-off); } if (drawCanvasA) { p.setClipRect(car); paintCanvas(p, car); } p.setRenderHint(QPainter::Antialiasing, false); if (drawPanelB) { p.setClipRect(pbr); QPoint off(rPanelB.topLeft()); p.translate(off); paintControllerPanel(p, pbr.translated(-off)); p.translate(-off); } if (drawCanvasB) { p.setClipRect(cbr); QPoint off(rCanvasB.topLeft()); p.translate(off); paintControllerCanvas(p, cbr.translated(-off)); p.translate(-off); } //--------------------------------------------------- // draw marker //--------------------------------------------------- int y1 = r.y(); int y2 = y1 + r.height(); if (drawRuler) { p.setClipRect(hor); int w = r.width(); int x = r.x(); int y = rulerHeight - 16; QColor lcColors[3] = { Qt::red, Qt::blue, Qt::blue }; for (int i = 0; i < 3; ++i) { p.setPen(lcColors[i]); int xp = pos2pix(pos[i]) + rRuler.x(); QPixmap* pm = markIcon[i]; int pw = (pm->width() + 1) / 2; int x1 = x - pw; int x2 = x + w + pw; if (xp >= x1 && xp < x2) { p.drawPixmap(xp - pw, y-2, *pm); p.drawLine(xp, y1, xp, y2); } } } if (marker) { int yy1 = y1; if (yy1 < rCanvasA.x()) yy1 = rCanvasA.x(); p.setPen(Qt::green); AL::iMarker start = marker->lower_bound(pos1.tick()); if (start != marker->begin()) --start; AL::iMarker end = marker->lower_bound(pos2.tick()); if (end != marker->end()) ++end; for (AL::iMarker m = start; m != end; ++m) { AL::Pos pm(m->second); int x = pos2pix(pm) + rRuler.x(); p.drawLine(x, yy1, x, y2); } } } //--------------------------------------------------------- // paintCanvas //--------------------------------------------------------- void TimeCanvas::paintCanvas(QPainter& p, const QRect& cr) { QPoint off(rCanvasA.topLeft()); if (type == TIME_CANVAS_PIANOROLL) { paintPianorollHorizontalGrid(p, cr); p.setRenderHint(QPainter::Antialiasing, true); } else if (type == TIME_CANVAS_DRUMEDIT) { paintDrumeditHorizontalGrid(p, cr); p.setRenderHint(QPainter::Antialiasing, true); } else off = QPoint(rCanvasA.x(), rCanvasA.y() - wpos.y()); p.translate(off); paint(p, cr.translated(-off)); p.resetMatrix(); } //--------------------------------------------------------- // setLocatorPos //--------------------------------------------------------- void TimeCanvas::setLocatorPos(int idx, const AL::Pos& val, bool follow) { if (pos[idx] == val) return; QFontMetrics fm(_font2); int fw = fm.width("123") + 2; int w = qMax(markIcon[idx]->width() + 2, fw); int h = widget()->height(); int x = pos2pix(val); if (idx == 0 && follow && followPos && followMode != FOLLOW_NO) { int scroll = 0; if (followMode == FOLLOW_JUMP) { int x2 = rRuler.width() - 20; if (x2 < 0) x2 = rRuler.width(); if (x > x2) { int x1 = 20; if (x1 >= rRuler.width()) x1 = 0; scroll = x - x1; } else if (x < 0) { scroll = x - MAP_OFFSET; } } else if (followMode == FOLLOW_CONTINUOUS) { int x1 = rRuler.width() / 2; if (x != x1) { scroll = x - (rRuler.width() / 2); } } if (scroll) { moveX(wpos.x() + scroll); hbar->setValue(wpos.x()); } } int offset = rRuler.x() - (w/2); int x1 = pos2pix(pos[idx]); int x2 = pos2pix(val); QRect oR(x1 + offset, 0, w, h); QRect nR(x2 + offset, 0, w, h); pos[idx] = val; widget()->update(oR | nR); } //--------------------------------------------------------- // setMag //--------------------------------------------------------- void TimeCanvas::setMag(double x, double y) { if (_xmag == x && _ymag == y) return; _xmag = x; _ymag = y; if (vmag) vmag->setValue(lrint((_ymag-_ymagMin)*100.0/(_ymagMax-_ymagMin))); hmag->setValue(xmag2s(_xmag)); updateScrollBars(); updateRulerMag(); magChanged(); _widget->update(); } //--------------------------------------------------------- // scaleX //--------------------------------------------------------- void TimeCanvas::scaleX(int val) { _xmag = s2xmag(val); updateScrollBars(); updateRulerMag(); magChanged(); _widget->update(); } //--------------------------------------------------------- // scaleY //--------------------------------------------------------- void TimeCanvas::scaleY(int val) { int y = lrint(wpos.y() / _ymag); _ymag = (_ymagMax - _ymagMin) / 100.0 * val + _ymagMin; y = lrint(y * _ymag); wpos.setY(y); updateScrollBars(); magChanged(); _widget->update(); } //--------------------------------------------------------- // setRaster // r = 1 - no raster // 0 - measure raster // > 1 - tick raster //--------------------------------------------------------- void TimeCanvas::setRaster(int r) { if (_raster != r) { _raster = r; _widget->update(); } } //--------------------------------------------------------- // setTimeRange //--------------------------------------------------------- void TimeCanvas::setTimeRange(const AL::Pos& p1, const AL::Pos& p2) { if (pos1 == p1 && pos2 == p2) return; pos1 = p1; pos2 = p2; updateScrollBars(); widget()->update(); } //--------------------------------------------------------- // setEndPos //--------------------------------------------------------- void TimeCanvas::setEndPos(const AL::Pos& p2) { if (pos2 == p2) return; pos2 = p2; updateScrollBars(); widget()->update(); } //--------------------------------------------------------- // updateScrollBars //--------------------------------------------------------- void TimeCanvas::updateScrollBars() { hbar->blockSignals(true); vbar->blockSignals(true); int ymax = lrint(yRange * _ymag) - rCanvasA.height(); if (ymax < 0) ymax = 0; vbar->setRange(0, ymax); vbar->setPageStep(rCanvasA.height()); int xmin = lrint(pos1.time(_timeType) * _xmag); unsigned x2 = pos2.time(_timeType); int xmax = lrint(x2 * _xmag) - rCanvasA.width(); if (xmax - xmin < 0) xmax = xmin; hbar->setRange(xmin, xmax); hbar->setPageStep(rCanvasA.width()); wpos.setX(hbar->value()); wpos.setY(vbar->value()); hbar->blockSignals(false); vbar->blockSignals(false); } //--------------------------------------------------------- // setTimeType1 //--------------------------------------------------------- void TimeCanvas::setTimeType1(AL::TType t) { double conv = 1.0; if (t == AL::TICKS) { timeTypeButton->setIcon(*metronomIcon); if (_timeType == AL::FRAMES) conv = AL::sampleRate / double(AL::division * 120 / 60); } else { timeTypeButton->setIcon(*clockIcon); if (_timeType == AL::TICKS) conv = double(AL::division * 120 / 60) / double(AL::sampleRate); } _timeType = t; _xmag *= conv; _xmagMax *= conv; _xmagMin *= conv; updateRulerMag(); magChanged(); } //--------------------------------------------------------- // setTimeType //--------------------------------------------------------- void TimeCanvas::setTimeType(AL::TType t) { setTimeType1(t); updateScrollBars(); timeTypeChanged(); widget()->update(); } //--------------------------------------------------------- // toggleTimeType //--------------------------------------------------------- void TimeCanvas::toggleTimeType() { if (_timeType == AL::TICKS) setTimeType(AL::FRAMES); else setTimeType(AL::TICKS); } //--------------------------------------------------------- // setMarkerList //--------------------------------------------------------- void TimeCanvas::setMarkerList(AL::MarkerList* ml) { if (marker == ml) return; marker = ml; widget()->update(); } //--------------------------------------------------------- // pix2pos //--------------------------------------------------------- AL::Pos TimeCanvas::pix2pos(int x) const { int val = lrint((x + wpos.x() - MAP_OFFSET)/_xmag); if (val < 0) val = 0; return AL::Pos(val, _timeType); } //--------------------------------------------------------- // pos2pix //--------------------------------------------------------- int TimeCanvas::pos2pix(const AL::Pos& p) const { return lrint(p.time(_timeType) * _xmag) + MAP_OFFSET - wpos.x(); } //--------------------------------------------------------- // mapx //--------------------------------------------------------- int TimeCanvas::mapx(int x) const { return lrint(x * _xmag) + MAP_OFFSET - wpos.x(); } //--------------------------------------------------------- // mapxDev //--------------------------------------------------------- int TimeCanvas::mapxDev(int x) const { int val = lrint((x + wpos.x() - MAP_OFFSET)/_xmag); if (val < 0) val = 0; return val; } //--------------------------------------------------------- // setCorderWidget //--------------------------------------------------------- void TimeCanvas::setCornerWidget(QWidget* w) { grid->addWidget(w, 3, 2); } //--------------------------------------------------------- // initPianoroll //--------------------------------------------------------- /* 0 1 2 3 4 5 6 7 8 9 10 c-2 c-1 C0 C1 C2 C3 C4 C5 C6 C7 C8 - G8 Grid ve: +------------+ ------------------------------ 11 | | | b | 7 +------+ | 10 | a# +-----+ .............................. +------+ a | 9 | | 6 +------+ | 8 | g# +-----+ .............................. +------+ g | 7 | | 5 +------+ | 6 | f# +-----+ .............................. +------+ f | 5 | | 4 | | +------------+ ------------------------------ 4 | | | e | 3 +------+ | 3 | d# +-----+ .............................. +------+ d | 2 | | 2 +------+ | 1 | c# +-----+ .............................. +------+ c | | | 1 0 | | +------------+ ------------------------------ */ void TimeCanvas::initPianoroll() { static const char *oct_xpm[] = { // w h colors "40 91 2 1", ". c #f0f0f0", "# c #000000", // x "####################################### ", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", // 10 ".......................................#", ".......................................#", ".......................................#", ".......................................#", //------------------------ "#######################................#", "########################...............#", "########################...............#", "####################################### ", // 7 "########################...............#", "########################...............#", "#######################................#", //------------------------ ".......................................#", ".......................................#", ".......................................#", // 6 ".......................................#", ".......................................#", ".......................................#", //------------------------ "#######################................#", "########################...............#", "########################...............#", // 7 "####################################### ", "########################...............#", "########################...............#", "#######################................#", //------------------------ ".......................................#", ".......................................#", ".......................................#", // 6 ".......................................#", ".......................................#", ".......................................#", //------------------------ "#######################................#", "########################...............#", "########################...............#", // 7 "####################################### ", "########################...............#", "########################...............#", "#######################................#", //------------------------ ".......................................#", ".......................................#", ".......................................#", ".......................................#", // 10 ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", "####################################### ", //---------------------- ".......................................#", ".......................................#", ".......................................#", ".......................................#", // 9 ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", //------------------------ "#######################................#", "########################...............#", "########################...............#", "####################################### ", // 7 "########################...............#", "########################...............#", "#######################................#", //------------------------ ".......................................#", ".......................................#", ".......................................#", // 6 ".......................................#", ".......................................#", ".......................................#", //-------------------------- "#######################................#", "########################...............#", "########################...............#", // 7 "####################################### ", "########################...............#", "########################...............#", "#######################................#", //------------------------ ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", // 10 ".......................................#", ".......................................#", ".......................................#", ".......................................#", }; static const char *mk1_xpm[] = { "40 13 2 1", ". c #ff0000", "# c none", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", "#######################................#", "########################...............#", "########################...............#", "####################################### ", }; static const char *mk2_xpm[] = { "40 13 2 1", ". c #ff0000", "# c none", "########################...............#", "########################...............#", "#######################................#", //------------------------ ".......................................#", ".......................................#", ".......................................#", // 6 ".......................................#", ".......................................#", ".......................................#", //-------------------------- "#######################................#", "########################...............#", "########################...............#", // 7 "####################################### ", }; static const char *mk3_xpm[] = { "40 13 2 1", ". c #ff0000", "# c none", "########################...............#", "########################...............#", "#######################................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", "########################################", }; static const char *mk4_xpm[] = { "40 13 2 1", "# c #ff0000", ". c none", "........................................", "........................................", "........................................", "#######################.................", "########################................", "########################................", "########################................", "########################................", "########################................", "#######################.................", "........................................", "........................................", "........................................", }; if (octave == 0) { octave = new QPixmap(oct_xpm); mk1 = new QPixmap(mk1_xpm); mk2 = new QPixmap(mk2_xpm); mk3 = new QPixmap(mk3_xpm); mk4 = new QPixmap(mk4_xpm); } yRange = keyHeight * 75; } //--------------------------------------------------------- // pitch2y // y = 0 == origin of rCanvasA //--------------------------------------------------------- int TimeCanvas::pitch2y(int pitch) const { int y; if (type == TIME_CANVAS_DRUMEDIT) y = pitch * drumHeight; else { static int tt[] = { 12, 19, 25, 32, 38, 51, 58, 64, 71, 77, 84, 90 }; y = (75 * keyHeight) - (tt[pitch % 12] + (7 * keyHeight) * (pitch / 12)); if (y < 0) y = 0; } return lrint(y - wpos.y() / _ymag); } //--------------------------------------------------------- // y2pitch // y = 0 == origin of rCanvasA //--------------------------------------------------------- int TimeCanvas::y2pitch(int y) const { y = lrint((y + wpos.y()) / _ymag); int pitch; if (type == TIME_CANVAS_DRUMEDIT) pitch = y / drumHeight; else { const int total = (10 * 7 + 5) * keyHeight; // 75 Ganztonschritte y = total - y; int oct = (y / (7 * keyHeight)) * 12; char kt[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 }; pitch = kt[y % 91] + oct; if (pitch < 0 || pitch > 127) pitch = -1; } return pitch; } //--------------------------------------------------------- // paintPiano //--------------------------------------------------------- void TimeCanvas::paintPiano(QPainter& p, QRect r) { int d = int(_ymag)+1; qreal x = qreal(r.x()); qreal y = (r.y()-rulerHeight-d) / _ymag; if (y < 0.0) y = 0.0; qreal h = (r.height()+d) / _ymag; QPointF offset(x, wpos.y() / _ymag + keyHeight * 2 + y); p.translate(0.0, qreal(rulerHeight)); p.scale(1.0, _ymag); p.drawTiledPixmap(QRectF(x, y, qreal(r.width()), h), *octave, offset); if (curPitch != -1) { int y = pitch2y(curPitch); QPixmap* pm; switch(curPitch % 12) { case 0: case 5: pm = mk3; break; case 2: case 7: case 9: pm = mk2; break; case 4: case 11: pm = mk1; break; default: pm = mk4; break; } p.drawPixmap(0, y, *pm); } p.resetMatrix(); } //--------------------------------------------------------- // paintPianorollHorizontalGrid //--------------------------------------------------------- void TimeCanvas::paintPianorollHorizontalGrid(QPainter& p, QRect r) { qreal offset = rulerHeight - wpos.y(); qreal kh = keyHeight * _ymag; int x1 = r.x(); int x2 = x1 + r.width(); if (x1 < pianoWidth) x1 = pianoWidth; qreal y = kh + offset; qreal y1 = r.y() - _ymag; qreal y2 = y1 + r.height() + _ymag; for (int key = 1; key < 75; ++key, y += kh) { if (y < y1) continue; if (y > y2) break; switch (key % 7) { case 2: case 5: p.setPen(QPen(Qt::lightGray)); break; default: p.setPen(QPen(Qt::gray)); break; } p.drawLine(QLineF(x1, y, x2, y)); } } //--------------------------------------------------------- // paintDrumeditHorizontalGrid //--------------------------------------------------------- void TimeCanvas::paintDrumeditHorizontalGrid(QPainter& p, QRect r) { int offset = rulerHeight - wpos.y(); p.setPen(QPen(Qt::lightGray)); int x1 = r.x(); int x2 = x1 + r.width(); if (x1 < drumWidth) x1 = drumWidth; p.setPen(QPen(Qt::lightGray)); int y = offset; int y1 = r.y(); int y2 = y1 + r.height(); for (int i = 0; i < 128; ++i, y += drumHeight) { if (y < y1) continue; if (y > y2) break; p.drawLine(QLine(x1, y, x2, y)); } } //--------------------------------------------------------- // addCtrlClicked //--------------------------------------------------------- void TimeCanvas::addCtrlClicked() { addController(); } //--------------------------------------------------------- // updateGeometry //--------------------------------------------------------- void TimeCanvas::updateGeometry() { int wh = _widget->height(); int ww = _widget->width(); if (wh < ctrlHeight) ctrlHeight = wh; int x1 = 0; if (type == TIME_CANVAS_PIANOROLL) x1 = pianoWidth; else if (type == TIME_CANVAS_DRUMEDIT) x1 = drumWidth; else if (type == TIME_CANVAS_WAVEEDIT) x1 = waveWidth; int y2 = wh - ctrlHeight; rPanelA.setRect(0, rulerHeight, x1, wh - rulerHeight - ctrlHeight); rPanelB.setRect(0, y2, x1, ctrlHeight); int cw = ww - x1; rRuler.setRect( x1, 0, cw, rulerHeight); rCanvasA.setRect(x1, rulerHeight, cw, wh - rulerHeight - ctrlHeight); rCanvasB.setRect(x1, y2, cw, ctrlHeight); rButton.setRect(0, 0, rCanvasA.x(), rPanelA.y()); if (yRange > 0 && _yFit) { _ymagMin = double(rCanvasA.height()) / double(yRange); if (_ymag < _ymagMin) _ymag = _ymagMin; if (vmag) vmag->setValue(lrint((_ymag-_ymagMin)*100.0/(_ymagMax-_ymagMin))); } } //--------------------------------------------------------- // setTool //--------------------------------------------------------- void TimeCanvas::setTool(int t) { if (_tool == Tool(t)) return; _tool = Tool(t); emit toolChanged(_tool); setCursor(); } //--------------------------------------------------------- // setCursor //--------------------------------------------------------- void TimeCanvas::setCursor() { switch(_tool) { case PencilTool: widget()->setCursor(QCursor(QPixmap(":/xpm/pencil.xpm"), 4, 15)); break; case RubberTool: widget()->setCursor(QCursor(QPixmap(":/xpm/delete.xpm"), 4, 15)); break; case GlueTool: widget()->setCursor(QCursor(QPixmap(":/xpm/glue.xpm"), 4, 15)); break; case CutTool: widget()->setCursor(QCursor(QPixmap(":/xpm/cut.xpm"), 4, 15)); break; case MuteTool: widget()->setCursor(QCursor(QPixmap(":/xpm/editmute.xmp"), 4, 15)); break; default: widget()->setCursor(QCursor(Qt::ArrowCursor)); break; } } //--------------------------------------------------------- // setCanvasBackground //--------------------------------------------------------- void TimeCanvas::setCanvasBackground(const QColor& color) { canvasBackgroundPixmap = QPixmap(); canvasBackgroundColor = color; widget()->update(); } //--------------------------------------------------------- // setCanvasBackground //--------------------------------------------------------- void TimeCanvas::setCanvasBackground(const QPixmap& pm) { canvasBackgroundPixmap = pm; widget()->update(); } //--------------------------------------------------------- // setYMagRange //--------------------------------------------------------- void TimeCanvas::setYMagRange(double min, double max) { _ymagMin = min; _ymagMax = max; if (vmag) vmag->setValue(lrint((_ymag-_ymagMin)*100.0/(_ymagMax-_ymagMin))); } //--------------------------------------------------------- // setVSize //--------------------------------------------------------- void TimeCanvas::setVSize(int val) { if (yRange == val) return; yRange = val; if (_yFit) { _ymagMin = double(rCanvasA.height()) / double(yRange); if (_ymag < _ymagMin) _ymag = _ymagMin; if (vmag) vmag->setValue(lrint((_ymag-_ymagMin)*100.0/(_ymagMax-_ymagMin))); } updateScrollBars(); } //--------------------------------------------------------- // s2xmag // nonlinear xmag behaviour, feels better //--------------------------------------------------------- double TimeCanvas::s2xmag(int val) { val = 100 - val; double f = 1.0 - log10(val * val + 1) * 0.25; return (_xmagMax - _xmagMin) * f + _xmagMin; } //--------------------------------------------------------- // xmag2s //--------------------------------------------------------- int TimeCanvas::xmag2s(double m) { m -= _xmagMin; m /= (_xmagMax - _xmagMin); double val = sqrt(exp10((1.0 - m) * 4.0)-1.0); return lrint(100.0 - val); } //--------------------------------------------------------- // setPart //--------------------------------------------------------- void TimeCanvas::setPart(const AL::Pos& p1, const AL::Pos& p2) { partPos1 = p1; partPos2 = p2; widget()->update(); } //--------------------------------------------------------- // setFont1 //--------------------------------------------------------- void TimeCanvas::setFont1(const QFont& f) { _font1 = f; printf("TimeCanvas::setFont1\n"); } //--------------------------------------------------------- // setFont2 //--------------------------------------------------------- void TimeCanvas::setFont2(const QFont& f) { _font2 = f; } //--------------------------------------------------------- // setFont3 //--------------------------------------------------------- void TimeCanvas::setFont3(const QFont& f) { _font3 = f; } muse-4.0.0/src/awl/tcanvas.h000066400000000000000000000221041404105311000156320ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __TCANVAS_H__ #define __TCANVAS_H__ #include "al/pos.h" #include "al/marker.h" #define MAP_OFFSET 20 enum TimeCanvasType { TIME_CANVAS, TIME_CANVAS_PIANOROLL, TIME_CANVAS_DRUMEDIT, TIME_CANVAS_WAVEEDIT }; enum Tool { PointerTool=1, PencilTool=2, RubberTool=4, CutTool=8, GlueTool=16, QuantTool=32, DrawTool=64, MuteTool=128 }; enum FollowMode { FOLLOW_NO, FOLLOW_JUMP, FOLLOW_CONTINUOUS }; static const int rulerHeight = 28; static const int pianoWidth = 40; static const int waveWidth = 40; static const int drumWidth = 120; static const int drumHeight = 18; static const int keyHeight = 13; //--------------------------------------------------------- // TimeCanvas // // Layout: // Button Ruler // panelA canvasA // panelB canvasB // // Pianoroll: // panelA -> keyboard // // Drumeditor: // panelA -> instrument list // //--------------------------------------------------------- class TimeCanvas : public QFrame { Q_OBJECT Q_PROPERTY(QFont font1 READ font1 WRITE setFont1) Q_PROPERTY(QFont font2 READ font2 WRITE setFont2) Q_PROPERTY(QFont font3 READ font3 WRITE setFont3) // I could not find out how to set the size of a font property with // style sheets. As a workaround here are special "size" properties // which could be set with style sheet command // TimeCanvas { qproperty-fontSize1: 6 } Q_PROPERTY(int fontSize1 READ fontSize1 WRITE setFontSize1 STORED false) Q_PROPERTY(int fontSize2 READ fontSize2 WRITE setFontSize2 STORED false) Q_PROPERTY(int fontSize3 READ fontSize3 WRITE setFontSize3 STORED false) QFont _font1; QFont _font2; QFont _font3; int yRange; bool _yFit; AL::Pos pos1; // time scroll range AL::Pos pos2; int metronomeRulerMag; double _xmagMin, _xmagMax; double _ymagMin, _ymagMax; AL::TType _timeType; AL::MarkerList* marker; QScrollBar* hbar; QScrollBar* vbar; QSlider* vmag; QSlider* hmag; QToolButton* timeTypeButton; QGridLayout* grid; QColor canvasBackgroundColor; QPixmap canvasBackgroundPixmap; int dragType; bool followPos; // // pianoroll variables // static QPixmap* octave; static QPixmap* mk1; static QPixmap* mk2; static QPixmap* mk3; static QPixmap* mk4; QPushButton* addCtrlButton; bool mouseInB; void updateScrollBars(); void canvasPaintEvent(const QRect&, QPainter&); void paintCanvas(QPainter&, const QRect&); void paintMetronomRuler(QPainter&, const QRect&); void paintClockRuler(QPainter&, const QRect&); void initPianoroll(); void paintPiano(QPainter&, QRect); void paintPianorollHorizontalGrid(QPainter&, QRect); void paintDrumeditHorizontalGrid(QPainter&, QRect); void updateGeometry(); double s2xmag(int val); int xmag2s(double m); void updateRulerMag(); protected: TimeCanvasType type; Tool _tool; QRect rButton, rPanelA, rPanelB, rRuler, rCanvasA, rCanvasB; AL::Pos pos[3]; bool showCursor; AL::Pos cursor; QWidget* _widget; AL::Pos partPos1; // active time range for midi editors AL::Pos partPos2; QPoint wpos; // "widget" position double _xmag, _ymag; int ctrlHeight; int curPitch; int _raster; int button; Qt::KeyboardModifiers keyState; bool eventFilter(QObject*, QEvent*); virtual void paint(QPainter&, QRect) = 0; virtual void mousePress(QMouseEvent*) {} virtual void mouseMove(QPoint) {} virtual void mouseRelease(QMouseEvent*) {} virtual void mouseDoubleClick(QMouseEvent*) {} virtual void paintDrumList(QPainter&, QRect) {} virtual void layout() {} virtual void enterB() {} virtual void leaveB() {} virtual void setCursor(); virtual void timeTypeChanged() {} virtual void magChanged() {} virtual void paintControllerCanvas(QPainter&, QRect) {} virtual void paintControllerPanel(QPainter&, QRect) {} virtual void dragEnter(QDragEnterEvent*) {} virtual void drop(QDropEvent*) {} virtual void dragMove(QDragMoveEvent*) {} virtual void dragLeave(QDragLeaveEvent*) {} virtual void addController() {} virtual void keyPressEvent(QKeyEvent *e); virtual void keyboardNavigate(QKeyEvent *) {} // map logical coordinates to physical coordinates (pixel) int mapx(int x) const; int mapy(int y) const { return lrint(y * _ymag) - wpos.y(); } int rmapx(int x) const { return lrint(x * _xmag); } int rmapy(int y) const { return lrint(y * _ymag); } QPoint map(const QPoint& p) const { return QPoint(mapx(p.x()), mapy(p.y())); } QPoint rmap(const QPoint& p) const { return QPoint(rmapx(p.x()), rmapy(p.y())); } // map physical coordinates (pixel) to logical coordinates int mapxDev(int x) const; int rmapxDev(int x) const { return lrint(x / _xmag); } int mapyDev(int y) const { int val = lrint((y + wpos.y()) / _ymag); return val < 0 ? 0 : val; } QPoint mapDev(const QPoint& p) const { return QPoint(mapxDev(p.x()), mapyDev(p.y())); } virtual int y2pitch(int y) const; virtual int pitch2y(int pitch) const; void setTimeType1(AL::TType t); void setPart(const AL::Pos& p1, const AL::Pos& p2); private slots: void moveX(int); void scaleX(int); void scaleY(int); void toggleTimeType(); void addCtrlClicked(); signals: void posChanged(int, const AL::Pos&); // emitted from ruler void cursorPos(const AL::Pos&, bool); void contentsMoving(int, int); void addMarker(const AL::Pos&); void removeMarker(const AL::Pos&); void pitchChanged(int); void toolChanged(int); public slots: void setLocatorPos(int idx, const AL::Pos&, bool follow); void setVSize(int); virtual void setRaster(int); void setYPos(int); void moveY(int); void setEndPos(const AL::Pos&); void tempoChanged(); void setTool(int); void setFollow(bool val) { followPos = val; } public: TimeCanvas(TimeCanvasType = TIME_CANVAS); void setTimeRange(const AL::Pos&, const AL::Pos&); void setMag(double, double); double xmag() const { return _xmag; } double ymag() const { return _ymag; } void setYMagRange(double, double); void setYFit(bool val) { _yFit = val; } QPoint getWPos() const { return wpos; } QWidget* widget() const { return _widget; } void setWPos(const QPoint& p); void setTimeType(AL::TType t); AL::TType timeType() const { return _timeType; } void setMarkerList(AL::MarkerList*); void setCornerWidget(QWidget* w); void resizeController(int); int raster() const { return _raster; } AL::Pos pix2pos(int x) const; int pos2pix(const AL::Pos&) const; QScrollBar* verticalScrollBar() const { return vbar; } Tool tool() const { return _tool; } void setCanvasBackground(const QColor& color); void setCanvasBackground(const QPixmap& pm); void updateCanvasB() { _widget->update(rCanvasB); } void updateRuler() { _widget->update(rRuler); } QFont font1() const { return _font1; } QFont font2() const { return _font2; } QFont font3() const { return _font3; } void setFont1(const QFont& f); void setFont2(const QFont& f); void setFont3(const QFont& f); int fontSize1() const { return _font1.pointSize(); } int fontSize2() const { return _font2.pointSize(); } int fontSize3() const { return _font3.pointSize(); } void setFontSize1(int val) { _font1.setPointSize(val); } void setFontSize2(int val) { _font2.setPointSize(val); } void setFontSize3(int val) { _font3.setPointSize(val); } static FollowMode followMode; friend class CtrlEdit; }; #endif muse-4.0.0/src/awl/tempoedit.cpp000066400000000000000000000056151404105311000165300ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "muse_math.h" #include "tempoedit.h" #include #include namespace Awl { //--------------------------------------------------------- // TempoEdit //--------------------------------------------------------- TempoEdit::TempoEdit(QWidget* parent) : QDoubleSpinBox(parent) { curVal = -1.0; setSingleStep(1.0); setRange(30.0, 600.0); connect(this, SIGNAL(valueChanged(double)), SLOT(newValue(double))); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize TempoEdit::sizeHint() const { QFontMetrics fm(font()); int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); int h = fm.height() + fw * 2; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = 2 + fm.horizontalAdvance(QString("000.00")) + fw * 4 + 30; #else int w = 2 + fm.width(QString("000.00")) + fw * 4 + 30; #endif return QSize(w, h).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // newValue //--------------------------------------------------------- void TempoEdit::newValue(double val) { if (val != curVal) { curVal = val; emit tempoChanged(tempo()); } } //--------------------------------------------------------- // setTempo //--------------------------------------------------------- void TempoEdit::setTempo(int val) { double d = 60000000.0/double(val); if (d != curVal) { curVal = d; blockSignals(true); setValue(d); blockSignals(false); } } //--------------------------------------------------------- // tempo //--------------------------------------------------------- int TempoEdit::tempo() const { return lrint(60000000.0/value()); } } muse-4.0.0/src/awl/tempoedit.h000066400000000000000000000030371404105311000161710ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLTEMPOEDIT_H__ #define __AWLTEMPOEDIT_H__ #include namespace Awl { //--------------------------------------------------------- // TempoEdit //--------------------------------------------------------- class TempoEdit : public QDoubleSpinBox { Q_OBJECT double curVal; protected: QSize sizeHint() const; private slots: void newValue(double); public slots: void setTempo(int); signals: void tempoChanged(int); public: TempoEdit(QWidget*); int tempo() const; }; } #endif muse-4.0.0/src/awl/tempolabel.cpp000066400000000000000000000046361404105311000166640ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "tempolabel.h" #include namespace Awl { //--------------------------------------------------------- // TempoLabel //--------------------------------------------------------- TempoLabel::TempoLabel(QWidget* parent) : QLabel(parent) { setFrameStyle(WinPanel | Sunken); setLineWidth(2); setMidLineWidth(3); _value = 1.0; setValue(0.0); setIndent(3); setMinimumSize(sizeHint()); } //--------------------------------------------------------- // setVal //--------------------------------------------------------- void TempoLabel::setValue(int val) { setValue(double(val/1000.0)); } void TempoLabel::setValue(double val) { if (val == _value) return; _value = val; QString s = QString("%1").arg(val, 3, 'f', 2); setText(s); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize TempoLabel::sizeHint() const { QFontMetrics fm(font()); int fw = 4; int h = fm.height() + fw * 2; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = 6 + fm.horizontalAdvance(QString("000.00")) + fw * 2; // 6=indent #else int w = 6 + fm.width(QString("000.00")) + fw * 2; // 6=indent #endif return QSize(w, h).expandedTo(QApplication::globalStrut()); } } muse-4.0.0/src/awl/tempolabel.h000066400000000000000000000027051404105311000163240ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLTEMPOLABEL_H__ #define __AWLTEMPOLABEL_H__ #include namespace Awl { //--------------------------------------------------------- // TempoLabel //--------------------------------------------------------- class TempoLabel : public QLabel { Q_OBJECT double _value; protected: QSize sizeHint() const; public slots: void setValue(int); void setValue(double); public: TempoLabel(QWidget*); }; } #endif muse-4.0.0/src/awl/utils.cpp000066400000000000000000000031751404105311000156750ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include #include "utils.h" static const char* vall[] = { "c","c#","d","d#","e","f","f#","g","g#","a","a#","h" }; static const char* valu[] = { "C","C#","D","D#","E","F","F#","G","G#","A","A#","H" }; namespace Awl { //--------------------------------------------------------- // pitch2string //--------------------------------------------------------- QString pitch2string(int v) { if (v < 0 || v > 127) return QString("----"); int octave = (v / 12) - 2; QString o = QString::number(octave); int i = v % 12; QString s(octave < 0 ? valu[i] : vall[i]); return s + o; } } muse-4.0.0/src/awl/utils.h000066400000000000000000000021461404105311000153370ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLUTILS_H__ #define __AWLUTILS_H__ class QString; namespace Awl { extern QString pitch2string(int); } #endif muse-4.0.0/src/awl/volentry.cpp000066400000000000000000000025701404105311000164150ustar00rootroot00000000000000//============================================================================= // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "fastlog.h" #include "volentry.h" namespace Awl { //--------------------------------------------------------- // VolEntry //--------------------------------------------------------- VolEntry::VolEntry(QWidget* parent, bool leftMouseButtonCanDecrease) : FloatEntry(parent, leftMouseButtonCanDecrease) { setRange(-60.0f, 10.0f); setSpecialText(tr("off")); setPrecision(0); setLog(true); } } muse-4.0.0/src/awl/volentry.h000066400000000000000000000031021404105311000160520ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLVOLENTRY_H__ #define __AWLVOLENTRY_H__ #include "floatentry.h" namespace Awl { //--------------------------------------------------------- // VolEntry //--------------------------------------------------------- class VolEntry : public FloatEntry { Q_OBJECT bool _leftMouseButtonCanDecrease; public: VolEntry(QWidget* parent, bool leftMouseButtonCanDecrease = false); bool leftMouseButtonCanDecrease() const { return _leftMouseButtonCanDecrease; } void setLeftMouseButtonCanDecrease(bool v) { _leftMouseButtonCanDecrease = v; } }; } #endif muse-4.0.0/src/awl/volknob.cpp000066400000000000000000000025111404105311000162000ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "fastlog.h" #include "volknob.h" namespace Awl { //--------------------------------------------------------- // VolKnob //--------------------------------------------------------- VolKnob::VolKnob(QWidget* parent) : Knob(parent) { setLog(true); setRange(-60.0f, 10.0f); _lineStep = .8f; _pageStep = 3.0f; } } muse-4.0.0/src/awl/volknob.h000066400000000000000000000027211404105311000156500ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLVOLKNOB_H__ #define __AWLVOLKNOB_H__ #include "knob.h" namespace Awl { //--------------------------------------------------------- // VolKnob //! Volume Knob entry widget // //! This widget implements knob with a logarithmic //! floating point scale used to adjust the volume //! in an audio mixer. //--------------------------------------------------------- class VolKnob : public Knob { Q_OBJECT public: VolKnob(QWidget* parent = 0); }; } #endif muse-4.0.0/src/awl/volslider.cpp000066400000000000000000000050171404105311000165350ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "fastlog.h" #include "volslider.h" #include #include "muse_math.h" namespace Awl { //--------------------------------------------------------- // VolSlider //--------------------------------------------------------- VolSlider::VolSlider(QWidget* parent) : Slider(parent) { setLog(true); setRange(-60.0f, 10.0f); setScaleWidth(7); setLineStep(.8f); setPageStep(3.0f); } //--------------------------------------------------------- // mouseDoubleClickEvent //--------------------------------------------------------- void VolSlider::mouseDoubleClickEvent(QMouseEvent* ev) { if (ev->button() == Qt::RightButton) _value = 0.0; else _value = _minValue; valueChange(); update(); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void VolSlider::setValue(double val) { if (_log) { if (val == 0.0f) _value = _minValue; else { _value = fast_log10(val) * 20.0f; if (_value < _minValue) _value = _minValue; } } else _value = val; update(); } //--------------------------------------------------------- // value //--------------------------------------------------------- double VolSlider::value() const { return _log ? (_value <= _minValue) ? 0.0 : muse_db2val(_value) : _value; } } muse-4.0.0/src/awl/volslider.h000066400000000000000000000033211404105311000161760ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __AWLVOLSLIDER_H__ #define __AWLVOLSLIDER_H__ #include "slider.h" namespace Awl { //--------------------------------------------------------- // VolSlider //! Volume Slider entry widget // //! This widget implements a slider with a logarithmic //! floating point scale used to adjust the volume //! in an audio mixer. //! Internal value represents gain as a logarythmic //! dB value. //--------------------------------------------------------- class VolSlider : public Slider { Q_OBJECT protected: virtual void mouseDoubleClickEvent(QMouseEvent*); public slots: virtual void setValue(double v); public: VolSlider(QWidget* parent = 0); virtual double value() const; }; } #endif muse-4.0.0/src/cmake/000077500000000000000000000000001404105311000143205ustar00rootroot00000000000000muse-4.0.0/src/cmake/Summary.cmake000066400000000000000000000014271404105311000167630ustar00rootroot00000000000000set(summary_willbuild "") set(summary_willnotbuild "") macro(summary_add name test) if (${test}) list(APPEND summary_willbuild ${name}) else (${test}) list(APPEND summary_willnotbuild "${name}") endif (${test}) endmacro(summary_add) macro(summary_show_part variable title) list(LENGTH ${variable} _len) if (_len) message("") message(${title}) message("-----------------------------------------------") foreach (_item ${${variable}}) message(" ${_item}") endforeach (_item) endif (_len) endmacro(summary_show_part) macro(summary_show) summary_show_part(summary_willbuild "The following components will be built:") summary_show_part(summary_willnotbuild "The following components WILL NOT be built:") message("") endmacro(summary_show) muse-4.0.0/src/cmake/TargetDoc.cmake000066400000000000000000000124321404105311000172000ustar00rootroot00000000000000# -helper macro to add a "doxy" target with CMake build system. # and configure Doxyfile.in to Doxyfile # # target "doxy" allows building the documentation with doxygen/dot on WIN32 and Linux # Creates .chm windows help file if MS HTML help workshop # (available from http://msdn.microsoft.com/workshop/author/htmlhelp) # is installed with its DLLs in PATH. # # # Please note, that the tools, e.g.: # doxygen, dot, latex, dvips, makeindex, gswin32, etc. # must be in path. # # Note about Visual Studio Projects: # MSVS hast its own path environment which may differ from the shell. # See "Menu Tools/Options/Projects/VC++ Directories" in VS 7.1 # # author Jan Woetzel 2004-2006 # www.mip.informatik.uni-kiel.de/~jw FIND_PACKAGE(Doxygen) IF (DOXYGEN) MESSAGE("found Doxygen") # click+jump in Emacs and Visual Studio (for Doxyfile) (jw) IF (CMAKE_BUILD_TOOL MATCHES "(msdev|devenv)") SET(DOXY_WARN_FORMAT "\"$file($line) : $text \"") ELSE (CMAKE_BUILD_TOOL MATCHES "(msdev|devenv)") SET(DOXY_WARN_FORMAT "\"$file:$line: $text \"") ENDIF (CMAKE_BUILD_TOOL MATCHES "(msdev|devenv)") # we need latex for doxygen because of the formulas FIND_PACKAGE(LATEX) IF (NOT LATEX_COMPILER) MESSAGE(STATUS "latex command LATEX_COMPILER not found but usually required. You will probably get warnings and user inetraction on doxy run.") ENDIF (NOT LATEX_COMPILER) IF (NOT MAKEINDEX_COMPILER) MESSAGE(STATUS "makeindex command MAKEINDEX_COMPILER not found but usually required.") ENDIF (NOT MAKEINDEX_COMPILER) IF (NOT DVIPS_CONVERTER) MESSAGE(STATUS "dvips command DVIPS_CONVERTER not found but usually required.") ENDIF (NOT DVIPS_CONVERTER) IF (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in") MESSAGE(STATUS "configured ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in --> ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY ) # use (configured) Doxyfile from (out of place) BUILD tree: SET(DOXY_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") ELSE (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in") # use static hand-edited Doxyfile from SOURCE tree: SET(DOXY_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile") IF (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile") MESSAGE(STATUS "WARNING: using existing ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile instead of configuring from Doxyfile.in file.") ELSE (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile") IF (EXISTS "${CMAKE_MODULE_PATH}/Doxyfile.in") # using template Doxyfile.in MESSAGE(STATUS "configured ${CMAKE_CMAKE_MODULE_PATH}/Doxyfile.in --> ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") CONFIGURE_FILE(${CMAKE_MODULE_PATH}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY ) SET(DOXY_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") ELSE (EXISTS "${CMAKE_MODULE_PATH}/Doxyfile.in") # failed completely... MESSAGE(SEND_ERROR "Please create ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in (or Doxyfile as fallback)") ENDIF(EXISTS "${CMAKE_MODULE_PATH}/Doxyfile.in") ENDIF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile") ENDIF(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in") ADD_CUSTOM_TARGET(doxy ${DOXYGEN} ${DOXY_CONFIG}) # create a windows help .chm file using hhc.exe # HTMLHelp DLL must be in path! # fallback: use hhw.exe interactively IF (WIN32) FIND_PACKAGE(HTMLHelp) IF (HTML_HELP_COMPILER) SET (TMP "${CMAKE_CURRENT_BINARY_DIR}\\Doc\\html\\index.hhp") STRING(REGEX REPLACE "[/]" "\\\\" HHP_FILE ${TMP} ) # MESSAGE(SEND_ERROR "DBG HHP_FILE=${HHP_FILE}") ADD_CUSTOM_TARGET(winhelp ${HTML_HELP_COMPILER} ${HHP_FILE}) ADD_DEPENDENCIES (winhelp doxy) IF (NOT TARGET_DOC_SKIP_INSTALL) # install windows help? # determine useful name for output file # should be project and version unique to allow installing # multiple projects into one global directory IF (EXISTS "${PROJECT_BINARY_DIR}/Doc/html/index.chm") IF (PROJECT_NAME) SET(OUT "${PROJECT_NAME}") ELSE (PROJECT_NAME) SET(OUT "Documentation") # default ENDIF(PROJECT_NAME) IF (${PROJECT_NAME}_VERSION_MAJOR) SET(OUT "${OUT}-${${PROJECT_NAME}_VERSION_MAJOR}") IF (${PROJECT_NAME}_VERSION_MINOR) SET(OUT "${OUT}.${${PROJECT_NAME}_VERSION_MINOR}") IF (${PROJECT_NAME}_VERSION_PATCH) SET(OUT "${OUT}.${${PROJECT_NAME}_VERSION_PATCH}") ENDIF(${PROJECT_NAME}_VERSION_PATCH) ENDIF(${PROJECT_NAME}_VERSION_MINOR) ENDIF(${PROJECT_NAME}_VERSION_MAJOR) # keep suffix SET(OUT "${OUT}.chm") #MESSAGE("DBG ${PROJECT_BINARY_DIR}/Doc/html/index.chm \n${OUT}") # create target used by install and package commands INSTALL(FILES "${PROJECT_BINARY_DIR}/Doc/html/index.chm" DESTINATION "doxy" RENAME "${OUT}" ) ENDIF(EXISTS "${PROJECT_BINARY_DIR}/Doc/html/index.chm") ENDIF(NOT TARGET_DOC_SKIP_INSTALL) ENDIF(HTML_HELP_COMPILER) # MESSAGE(SEND_ERROR "HTML_HELP_COMPILER=${HTML_HELP_COMPILER}") ENDIF (WIN32) ELSE(DOXYGEN) MESSAGE("Doxygen not found") ENDIF(DOXYGEN) muse-4.0.0/src/cmake/UsePkgConfig1.cmake000066400000000000000000000034601404105311000177320ustar00rootroot00000000000000# - pkg-config module for CMake # # Defines the following macros: # # PKGCONFIG(package includedir libdir linkflags cflags) # # Calling PKGCONFIG will fill the desired information into the 4 given arguments, # e.g. PKGCONFIG(libart-2.0 LIBART_INCLUDE_DIR LIBART_LINK_DIR LIBART_LINK_FLAGS LIBART_CFLAGS) # if pkg-config was NOT found or the specified software package doesn't exist, the # variable will be empty when the function returns, otherwise they will contain the respective information # FIND_PROGRAM(PKGCONFIG_EXECUTABLE NAMES pkg-config PATHS /usr/bin /usr/local/bin ) MACRO(PKGCONFIG _package _minVersion _include_DIR _link_DIR _link_FLAGS _cflags) # reset the variables at the beginning SET(${_include_DIR}) SET(${_link_DIR}) SET(${_link_FLAGS}) SET(${_cflags}) # if pkg-config has been found IF(PKGCONFIG_EXECUTABLE) EXEC_PROGRAM(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --atleast-version=${_minVersion} RETURN_VALUE _return_VALUE OUTPUT_VARIABLE _pkgconfigDevNull ) # EXEC_PROGRAM(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --exists RETURN_VALUE _return_VALUE OUTPUT_VARIABLE _pkgconfigDevNull ) # and if the package of interest also exists for pkg-config, then get the information IF(NOT _return_VALUE) EXEC_PROGRAM(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --variable=includedir OUTPUT_VARIABLE ${_include_DIR} ) EXEC_PROGRAM(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --variable=libdir OUTPUT_VARIABLE ${_link_DIR} ) EXEC_PROGRAM(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --libs OUTPUT_VARIABLE ${_link_FLAGS} ) EXEC_PROGRAM(${PKGCONFIG_EXECUTABLE} ARGS ${_package} --cflags OUTPUT_VARIABLE ${_cflags} ) ENDIF(NOT _return_VALUE) ENDIF(PKGCONFIG_EXECUTABLE) ENDMACRO(PKGCONFIG _include_DIR _link_DIR _link_FLAGS _cflags) MARK_AS_ADVANCED(PKGCONFIG_EXECUTABLE) muse-4.0.0/src/cmake/WrapMacros.cmake000066400000000000000000000004261404105311000174020ustar00rootroot00000000000000 MACRO(WRAP_CPP outfiles ) FOREACH(it ${ARGN}) SET(${outfiles} ${${outfiles}} ${it}.cpp) ENDFOREACH(it) ENDMACRO(WRAP_CPP) MACRO(WRAP_H outfiles ) FOREACH(it ${ARGN}) SET(${outfiles} ${${outfiles}} ${it}.h) ENDFOREACH(it) ENDMACRO(WRAP_H) muse-4.0.0/src/cmake/cmake_uninstall.cmake.in000066400000000000000000000017161404105311000211050ustar00rootroot00000000000000IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") SET(DESTDIR $ENV{DESTDIR}) FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) STRING(REGEX REPLACE "\n" ";" files "${files}") FOREACH(file ${files}) MESSAGE(STATUS "Uninstalling \"${DESTDIR}${file}\"") IF(EXISTS "${DESTDIR}${file}") EXEC_PROGRAM( "@CMAKE_COMMAND@" ARGS "-E remove \"${DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) IF("${rm_retval}" STREQUAL 0) ELSE("${rm_retval}" STREQUAL 0) MESSAGE(FATAL_ERROR "Problem when removing \"${DESTDIR}${file}\"") ENDIF("${rm_retval}" STREQUAL 0) ELSE(EXISTS "${DESTDIR}${file}") MESSAGE(STATUS "File \"${DESTDIR}${file}\" does not exist.") ENDIF(EXISTS "${DESTDIR}${file}") ENDFOREACH(file) muse-4.0.0/src/compile_components_ui.pl000066400000000000000000000012261404105311000201700ustar00rootroot00000000000000#!/usr/bin/perl -- use strict; my $file = 'muse/components/CMakeFiles/components.dir/build.make'; # Extract commands open(my $fh, "< :encoding(UTF-8)", $file) || die "ERROR: Cannot open $file for reading: $!"; my $content = do { local $/; <$fh> }; close($fh) || die "ERROR: Cannot close $file opened for reading: $!"; my @commands; while ($content =~ m/(uic.exe .*\.ui)/g) { my @line = split(/\s/, $1); $line[2] =~ s/(.*)muse\/components\/(.*)\.h/muse\/components\/$2\.h/; $line[3] =~ s/(.*)muse\/components\/(.*)\.ui/\.\.\/muse\/components\/$2\.ui/; push(@commands, join(' ', @line)); } # Execute commands foreach my $cmd(@commands) { system($cmd); } muse-4.0.0/src/compile_muse.sh000077500000000000000000000025231404105311000162620ustar00rootroot00000000000000#!/bin/bash #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= if [ -d build ]; then echo "Build dir already exists" else echo "Create build dir" mkdir build fi cd build # to put the resulting binary in a specific location add -DCMAKE_INSTALL_PREFIX= cmake -DCMAKE_BUILD_TYPE=release -DMODULES_BUILD_STATIC=ON .. && make clean all && echo "Build was OK, now enter the 'build' dir and run 'make install' as root" muse-4.0.0/src/compile_muse_mingw.sh000066400000000000000000000027131404105311000174610ustar00rootroot00000000000000#!/bin/bash #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # #============================================================================= if [ -d build ]; then echo "Build dir already exists" else echo "Create build dir" mkdir build fi cd build # to put the resulting binary in a specific location add -DCMAKE_INSTALL_PREFIX= cmake -DCMAKE_BUILD_TYPE=release -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND" -Wno-dev .. && ../fix_make_paths.pl && mingw32-make clean && ../compile_components_ui.pl && mingw32-make -j1 all && echo "Build was OK, now enter the 'build' dir and run 'make install' as root" muse-4.0.0/src/config.h.in000066400000000000000000000043111404105311000152620ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // $Id:$ // // Copyright (C) 2002-2011 by Werner Schweer and others // // 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; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //============================================================================= #ifndef _MUSE_CONFIG_H_ #define _MUSE_CONFIG_H_ #cmakedefine POSIX_TIMER_SUPPORT #cmakedefine ALSA_SUPPORT #cmakedefine HAVE_RTAUDIO #cmakedefine DSSI_ALSA_COMPAT_SUPPORT #cmakedefine HAVE_LASH #cmakedefine HAVE_LRDF #cmakedefine OSC_SUPPORT #cmakedefine DSSI_SUPPORT #cmakedefine LV2_SUPPORT #cmakedefine LV2_MAKE_PATH_SUPPORT #cmakedefine PYTHON_SUPPORT #cmakedefine MIDNAM_SUPPORT #cmakedefine HAVE_INSTPATCH #cmakedefine HAVE_GTK2 #cmakedefine VST_SUPPORT #cmakedefine VST_NATIVE_SUPPORT #cmakedefine VST_VESTIGE_SUPPORT #cmakedefine USE_SSE #cmakedefine RUBBERBAND_SUPPORT #cmakedefine ZITA_RESAMPLER_SUPPORT #cmakedefine HAVE_EXP10 #cmakedefine HAVE_EXP10F #cmakedefine HAVE_EXP10L #cmakedefine ALLOW_LEFT_HIDDEN_EVENTS // #cmakedefine HAVE_M_PI #define VERSION "${MusE_VERSION_FULL}" #define GITSTRING "${MusE_GITSTRING}" #define ORGANIZATION_NAME "MusE" #define ORGANIZATION_DOMAIN "muse-sequencer.org" #define PACKAGE_NAME "MusE" #define APP_DISPLAY_NAME "MusE" #define APP_DESCRIPTION "MusE: Linux Music Editor" #define DOCDIR "${MusE_DOC_DIR}" #define SHAREDIR "${MusE_SHARE_DIR}" #define LIBDIR "${MusE_LIB_DIR}" #define BINDIR "${MusE_BIN_DIR}" #endif // _MUSE_CONFIG_H_ muse-4.0.0/src/demos/000077500000000000000000000000001404105311000143475ustar00rootroot00000000000000muse-4.0.0/src/demos/CMakeLists.txt000066400000000000000000000020731404105311000171110ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB med_files *.med) install( FILES ${med_files} DESTINATION ${MusE_SHARE_DIR}/demos ) muse-4.0.0/src/demos/rasen.med000066400000000000000000000277731404105311000161660ustar00rootroot00000000000000 128 1 Maestro3 Maestro3 1 384 0 0 0 0 0 0 0 0 127 127 Windows Arial,10,-1,5,50,0,0,0,0,0 arial,8,-1,5,50,0,0,0,0,0 arial,10,-1,5,50,0,0,0,0,0 arial,10,-1,5,75,0,0,0,0,0 arial,8,-1,5,75,0,0,0,0,0 Lucidatypewriter,14,-1,5,75,0,0,0,0,0 0 1 -1 1 00:00:00:00:00 0 0 0 0 0 1 1 /usr/kde2/share/wallpapers/Paper01.jpg lpr - gv %s & mops.ps serial1 /dev/cua/0 1 15 0 1 1 347 247
7 6 5 4 3 2 1 0 8
3 5 /usr/share/muse/wallpapers/stone1.gif 0 355 0 1 0
2 63 127 63 70 9 0 1 1 4 4 0 0 1 0 0 28 31 33 29 2 Master 0.000000 0.000000 0 0 0 0 2 Master 0.000000 0.000000 0 0 0 0 2 Master 0.000000 0.000000 0 0 1 0 2 Master 0.000000 0.000000 0 0 1 0 2 1.000000 0.000000 0 0 1 0 2 Master 0.000000 0.000000 1 0 0 0 96 96 600 400 96 96 80 50 0 0 600 400 0 0 96 96 600 400
Auf dem grnen Rasen Kinderlied 0 1 210.000000 297.000000 10.000000 10.000000 10.000000 10.000000 4 1.000000 0 1 1 0 0 0 0 0 0 0 0 0 12288 1 0 0 1 0 piano 0 1 thats all 1 0 Auf dem gr nen Ra- sen, wo die Veil- chen blhn, geht mein Schf chen gra- sen, in dem fri- schen Grn. 0 0 4 0 384 384 1 60 0 0 0 100 100 0 0 1 0 0 500000 0 4 4 Master 3840 12288 0
muse-4.0.0/src/doc/000077500000000000000000000000001404105311000140055ustar00rootroot00000000000000muse-4.0.0/src/doc/.svnignore000066400000000000000000000001111404105311000160110ustar00rootroot00000000000000documentation.aux documentation.pdf documentation.log documentation.dvi muse-4.0.0/src/doc/CMakeLists.txt000066400000000000000000000050341404105311000165470ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2013 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= install( FILES pdf/documentation.pdf pdf/developer_docs.pdf DESTINATION ${MusE_DOC_DIR}/muse_pdf ) file (GLOB single_documentation_html_files html/single/documentation/*.css html/single/documentation/*.html # html/single/documentation/*.jpg html/single/documentation/*.png # html/single/documentation/toc_.txt ) file (GLOB single_developer_docs_html_files html/single/developer_docs/*.css html/single/developer_docs/*.html # html/single/developer_docs/*.jpg html/single/developer_docs/*.png # html/single/developer_docs/toc_.txt ) file (GLOB split_documentation_html_files html/split/documentation/*.css html/split/documentation/*.html # html/split/documentation/*.jpg html/split/documentation/*.png # html/split/documentation/toc_.txt ) file (GLOB split_developer_docs_html_files html/split/developer_docs/*.css html/split/developer_docs/*.html # html/split/developer_docs/*.jpg html/split/developer_docs/*.png # html/split/developer_docs/toc_.txt ) install( FILES ${single_documentation_html_files} DESTINATION ${MusE_DOC_DIR}/muse_html/single/documentation ) install( FILES ${single_developer_docs_html_files} DESTINATION ${MusE_DOC_DIR}/muse_html/single/developer_docs ) install( FILES ${split_documentation_html_files} DESTINATION ${MusE_DOC_DIR}/muse_html/split/documentation ) install( FILES ${split_developer_docs_html_files} DESTINATION ${MusE_DOC_DIR}/muse_html/split/developer_docs ) muse-4.0.0/src/doc/build_docs.sh000077500000000000000000000116641404105311000164630ustar00rootroot00000000000000#!/bin/bash #============================================================================= # MusE # Linux Music Editor # Copyright (C) 1999-2013 by Werner Schweer and others # # build_docs.sh Copyright (C) 2013 Tim E. Real # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= #============================================================= # # Run this script to re-create the PDF and HTML documentation. # Usage: build_docs.sh [--help] [--no_post_clean] # #============================================================= if [ "$1" == "--help" ]; then echo "Usage: build_docs.sh [--no_post_clean (don't clean up after build)]" exit 1 fi # # Pre clean # rm -rf pdf/*.* rm -rf html/single/documentation/*.* rm -rf html/single/developer_docs/*.* rm -rf html/split/documentation/*.* rm -rf html/split/developer_docs/*.* rm -rf tmp mkdir -p tmp/pdf/documentation mkdir -p tmp/pdf/developer_docs mkdir -p tmp/html/single/documentation mkdir -p tmp/html/single/developer_docs mkdir -p tmp/html/split/documentation mkdir -p tmp/html/split/developer_docs # # Run the PDF conversions first to get the *.aux files required for proper HTML links displaying # Run each conversion at least three times to resolve all references # pdflatex -interaction=batchmode -halt-on-error -file-line-error -output-directory=tmp/pdf/documentation documentation.tex pdflatex -interaction=batchmode -halt-on-error -file-line-error -output-directory=tmp/pdf/documentation documentation.tex pdflatex -interaction=batchmode -halt-on-error -file-line-error -output-directory=tmp/pdf/documentation documentation.tex pdflatex -interaction=batchmode -halt-on-error -file-line-error -output-directory=tmp/pdf/developer_docs developer_docs.tex pdflatex -interaction=batchmode -halt-on-error -file-line-error -output-directory=tmp/pdf/developer_docs developer_docs.tex pdflatex -interaction=batchmode -halt-on-error -file-line-error -output-directory=tmp/pdf/developer_docs developer_docs.tex # # Now run the HTML conversions # latex2html -nonavigation -noaddress -noinfo -split 0 -external_file tmp/pdf/documentation/documentation -mkdir -dir tmp/html/single/documentation documentation.tex latex2html -nonavigation -noaddress -noinfo -split 0 -external_file tmp/pdf/developer_docs/developer_docs -mkdir -dir tmp/html/single/developer_docs developer_docs.tex latex2html -noaddress -noinfo -split 4 -external_file tmp/pdf/documentation/documentation -mkdir -dir tmp/html/split/documentation documentation.tex latex2html -noaddress -noinfo -split 4 -external_file tmp/pdf/developer_docs/developer_docs -mkdir -dir tmp/html/split/developer_docs developer_docs.tex # # Move the files # mv -f tmp/pdf/documentation/*.pdf pdf mv -f tmp/pdf/developer_docs/*.pdf pdf mv -f tmp/html/single/documentation/*.css html/single/documentation mv -f tmp/html/single/documentation/*.html html/single/documentation # mv -f tmp/html/single/documentation/*.jpg html/single/documentation mv -f tmp/html/single/documentation/*.png html/single/documentation # mv -f tmp/html/single/documentation/toc_.txt html/single/documentation mv -f tmp/html/single/developer_docs/*.css html/single/developer_docs mv -f tmp/html/single/developer_docs/*.html html/single/developer_docs # mv -f tmp/html/single/developer_docs/*.jpg html/single/developer_docs mv -f tmp/html/single/developer_docs/*.png html/single/developer_docs # mv -f tmp/html/single/developer_docs/toc_.txt html/single/developer_docs mv -f tmp/html/split/documentation/*.css html/split/documentation mv -f tmp/html/split/documentation/*.html html/split/documentation # mv -f tmp/html/split/documentation/*.jpg html/split/documentation mv -f tmp/html/split/documentation/*.png html/split/documentation # mv -f tmp/html/split/documentation/toc_.txt html/split/documentation mv -f tmp/html/split/developer_docs/*.css html/split/developer_docs mv -f tmp/html/split/developer_docs/*.html html/split/developer_docs # mv -f tmp/html/split/developer_docs/*.jpg html/split/developer_docs mv -f tmp/html/split/developer_docs/*.png html/split/developer_docs # mv -f tmp/html/split/developer_docs/toc_.txt html/split/developer_docs # # Post clean # if [ "$1" != "--no_post_clean" ]; then rm -rf tmp fi muse-4.0.0/src/doc/developer_docs.tex000066400000000000000000001010411404105311000175210ustar00rootroot00000000000000%% (c) 2012 florian jung %% (c) 2012 Robert Jonsson %% we should consider putting this under a proper license. GPL, or %% some GPL-like documentation license?? %% rules for editing documentation: (READ THIS FIRST) %% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ %% %% please try to let newly written lines be shorter than 72 characters. %% minor exceptions are okay, but please not more than 80 chars. %% comments shall start after character #80 of the line (that is, %% they shall be "on the right margin") %% %% DON'T MIX up changes and reformatting in one commit. when changing %% stuff, please don't touch the lines before and after your change %% (that is, do not re-wrap them), even if it will look a bit patchy. %% this is for being able to easily use diff. %% when you want to reformat this file, then do it. but don't change %% anything, as this would be hard to find in a diff. and clearly %% state in the commit log that you "only" rearranged things. %% %% please adhere to the "User's manual" / "Internals" / "Design" %% partitioning (genereally, don't change the chapters until there %% is a really good reason for doing so (adding a chapter like %% "feature requests" as flo did in r1497 IS one). %% Below that, feel free to change the logical arrangement %% (making paragraphs to subsections and similar) if you deem it %% necessary. %% %% Whenever referring to code symbols, constants or source/header %% files, please use \sym{someClass::someSymbol}, \usym{UPPERCASE_THING} %% or \f{file.cpp}. %% Only specify file paths or namespaces where it would be ambiguous %% otherwise. Specify 'someClass::' until it would disturb the reader %% and it is obvious. you have to replace '_' by {\_} (with the {}!). %% These macros do automatic hyphenation on Camel-Case, under_-score %% and scope::-operator boundaries. If you need to insert additional %% hyphenation points, use {\-}. %% Example: \sym{someClass::someAb{\-}nor{\-}mal{\-}lyLongName} will %% hyphenate: some-Class::-some-Ab-nor-mal-ly-Long-Name %% %% Whenever referring to URLs, please wrap them in \url{blah}. Key %% combinations shall look like \key{CTRL+C}. Menu items shall look %% like \menu{Menu > Submenu > Menu Item}. %% %% Where possible, reference other parts of this documents with %% \label and \ref. Avoid duplicate information under "Internals" by %% referring to the appropriate section in "User's manual". %% %% Please do no time-stamping of sections. if you need time-stamps, %% use "svn blame documentation.tex" %% %% If you contribute something, feel free to add yourself to \author. %% %% If you don't speak LaTeX fluently, a few tips: %% * \section, \subsection, \subsubsection, \paragraph, \subparagraph %% let you create sections etc. just copy-and-paste if unsure. %% * you must prefix special characters like the underscore with \ %% (backslash) %% * \emph{some text} emphasizes the text, printing it italic. %% * \texttt{some text} displays the text in a typewriter font %% * \label{someName} creates a label at this position. this doesn't %% show up in the pdf. with \ref{someName}, you can reference to this %% label. (LaTeX will insert the section number instead of \ref) %% For this to work, you might need to recompile the .tex twice. %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Dependencies: %% To produce pdf output from this document the following packages are %% needed (atleast under Ubuntu derivatives) %% latex???-base %% texlive-latex-recommended %% texlive-latex-extra %% %% To produce a pdf version of this manual type: %% pdflatex documentation.tex %% A file documentation.pdf should be generated. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \documentclass[a4paper]{report} \usepackage[a4paper, left=2.5cm, right=2.5cm, top=2.5cm, bottom=2.5cm]{geometry} \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage{lmodern} \usepackage[english]{babel} \usepackage{graphicx} \usepackage{hyphenat} \usepackage{wrapfig} \usepackage{fancyhdr} \usepackage{hyperref} \usepackage{xcolor} \hypersetup{ linkcolor=blue, urlcolor=blue, colorlinks=true, % hyperlinks will be blue linkbordercolor=blue, % hyperlink borders will be blue %pdfborderstyle={/S/U/W 1} % border style will be underline of width 1pt } \pagestyle{fancy} \lhead{\scriptsize{\slshape\leftmark}} \chead{} \rhead{\thepage} \lfoot{} \cfoot{} \rfoot{} \renewcommand{\headrulewidth}{0.4pt} \usepackage{ifthen} %% Borrowed from Lyx output. "Because html converters don't know %% tabularnewline". Is required? Maybe replace with \\ ? \providecommand{\tabularnewline}{\\} % Hyphenate symbols before each uppercase letter, after each underscore % (without a "-") and after each ':' (without a "-") % TODO for any latex crack: also do automatic hyphenation, that is, % instead of some-Automation-Expression, do some-Au-to-ma-tion-Ex-press-ion \makeatletter \newcommand{\camelhyph}[1]{\@fterfirst\c@amelhyph#1\relax } \newcommand{\underscorehyph}[1]{\@fterfirst\u@nderscorehyph#1\relax } \def\@fterfirst #1#2{#2#1} \def\c@amelhyph #1{% \ifthenelse{\equal{#1}\relax}{}{% Do nothing if the end has been reached \ifnum`#1=95 \_\hspace{0pt}\else % Check whether #1 is "_", then print _[thin space] \ifnum`#1=58 :\hspace{0pt}\else \ifnum`#1>64 \ifnum`#1<91 \-#1\else#1\fi% Check whether #1 is an uppercase letter, \else#1\fi \fi \fi % if so, print \-#1, otherwise #1 \expandafter\c@amelhyph% % insert \c@amelhyph again. }} \def\u@nderscorehyph #1{% \ifthenelse{\equal{#1}\relax}{}{% Do nothing if the end has been reached \ifnum`#1=95 \_\hspace{0pt}\else % Check whether #1 is "_", then print _\- \ifnum`#1=58 :\hspace{0pt}\else#1\fi\fi \expandafter\u@nderscorehyph% % insert \u@nderscorehyph again. }} \makeatother \author{Florian Jung, Robert Jonsson, Tim Donnelly} \title{MusE Documentation} % Orcan: not needed since the hyperref package takes care of all %\newcommand{\url}[1]{\texttt{#1}} \newcommand{\key}[1]{\textbf{#1}} \newcommand{\shell}[1]{\texttt{\textbf{#1}}} \newcommand{\menu}[1]{\textbf{#1}} \newcommand{\sym}[1]{\texttt{\camelhyph{#1}}} \newcommand{\listing}[1]{\texttt{\camelhyph{#1}}} \newcommand{\usym}[1]{\texttt{\underscorehyph{#1}}} \newcommand{\file}[1]{\texttt{\camelhyph{#1}}} \newcommand{\screenshotwidth}[0]{0.8\textwidth} \begin{document} \tableofcontents \chapter{Internals -- how it works} This chapter explains how MusE is built internally, and is meant to be an aid for developers wanting to quickly start up with MusE. For details on \emph{why} stuff is done please refer to the following chapter. \section{User interface programming} We use the QT Toolkit for GUI- and other programming. The \emph{QT-Assistant} is an important tool for getting help. Almost everything can be looked up there. GUIs can be either be hardcoded (see \file{arranger.cpp} for an example) or can be created using \emph{QT-Designer} (see the dialogs under \file{widgets/function_dialogs/} for mostly cleanly-written examples). Don't forget to add your \file{cpp}, \file{h} and \file{ui} files to the corresponding sections in the \file{CMakeLists.txt}! Additionally, MusE offers some custom widgets, like menu title items etc. Following, there will be a small, unordered list about custom widgets: \begin{itemize} \item \sym{MusEGui::MenuTitleItem}: Provides a title-bar in a \sym{QMenu}. \\ Usage: \listing{someMenu->addAction(new\ MusEGui::MenuTitleItem(tr("fnord"),\ someMenu));} \\ Defined in \file{widgets/menutitleitem.h}. \item \sym{MusEGui::PopupMenu}: Provides a \sym{QMenu}-like menu which can stay open after the user checks a checkable action.\\ Usage: just create a \listing{new\ PopupMenu(\ true|false\ )} instead of a \listing{new\ QMenu()}. (\listing{true} means 'stay open')\\ Defined in \file{widgets/popupmenu.h}. \end{itemize} \section{Configuration} \label{portconfig_sucks} Configuration is a bit pesky in MusE in its current state. If you get confused by reading this chapter, that's a sign of a sane mind. There are three kinds of configuration items: \begin{itemize} \item (1) Global configuration, like coloring schemes, plugin categories, MDI-ness settings \item (2) Per-Song configuration, like whether to show or hide certain track types in the arranger \item (3) Something in between, like MIDI port settings etc. They obviously actually are global configuration issues (or ought to be), but also obviously must be stored in the song file for portability. (This problem could possibly be solved by the feature proposal in \ref{symbolic_ports}. \end{itemize} \paragraph{Reading configuration} \sym{void\ MusECore::readConfiguration(Xml\&,\ bool,\ bool)} in \file{conf.cpp} is the central point of reading configuration. It is called when MusE is started first (by \sym{bool\ MusECore::readConfiguration()}), and also when a song is loaded. \\ %TODO: call paths! It can be instructed whether to read MIDI ports (3), global configuration and MIDI ports (1+3). Per-Song configuration is always read (2). When adding new configuration items and thus altering \sym{readConfiguration()}, you must take care to place your item into the correct section. The code is divided into the following sections: \begin{itemize} \item Global and/or per-song configuration (3) \item Global configuration (1) \item Code for skipping obsolete entries \end{itemize} The sections are divided by comments (they contain \texttt{----}, so just search for them). Please do not just remove code for reading obsolete entries, but always add an appropriate entry to the 'skipping' section in order to prevent error messages when reading old configs. \paragraph{Writing configuration} Global configuration is written using the \sym{MusEGui::MusE::writeGlobalConfiguration()} functions, while per-song-config is written by \sym{MusEGui::MusE::writeConfiguration()} (notice the missing \sym{Global}; both implemented in \file{conf.cpp}). \sym{writeConfiguration} is actually just a subset of the code in \sym{writeGlobalConfiguration}. \textbf{Duplicate code!} % TODO fix that in the sourcecode. \paragraph{Song state} Additionally to per-song configuration, there is the song's state. This contains "the song", that is all tracks, parts and note events, together with information about the currently opened windows, their position, size, settings and so on. Adding new items here is actually pretty painless: Configuration is read and written using \sym{MusECore::Song::read} and \sym{::write}, both implemented in \file{songfile.cpp}. There are no caveats. \paragraph{How to add new items} When adding global configuration items, then add them into the second block ("global configuration") in \sym{readConfiguration} and into \sym{writeGlobalConfiguration}. When adding just-per-song items, better don't bother to touch the "configuration" code and just add it to the song's state (there might be rare exceptions). When adding global configuration items, make sure you add them into the correct section of \sym{readConfiguration}, and into \sym{writeGlobalConfiguration}. %TODO: how to write config, %TODO: song/global stuff %TODO: config lesen und schreiben fuer plugingroups \section{User controls and automation} \subsection{Handling user input} \subsubsection{Plugins and synthesizers} \paragraph{Overview} When the user launches a plugin's GUI, either a MusE-window with the relevant controls is shown, or the native GUI is launched. MusE will communicate with this native GUI through OSC (Open Sound Control). The relevant classes are \sym{PluginGui}, \sym{PluginIBase} (in \file{plugin.h}) and \sym{OscIF} (in \sym{osc.h}). If the user changes a GUI element, first the corresponding control is disabled, making MusE not steadily update it through automation while the user operates it. Then MusE will update the plugin's parameter value, and also record the new value. When appropriate, the controller is enabled again. \paragraph{Processing the input, recording} Upon operating a slider, \sym{PluginIBase::setParam} is called, which usually writes the control change into the ringbuffer \sym{PluginI::{\_}controlFifo}. (\sym{PluginI::apply()}, \sym{DssiSynthIF::getData()} will read this ringbuffer and do the processing accordingly). Furthermore, \sym{AudioTrack::recordAutomation} is called, which either directly modifies the controller lists or writes the change into a "to be recorded"-list (\sym{AudioTrack::{\_}recEvents}) (depending on whether the song is stopped or played). The \sym{AudioTrack::{\_}recEvents} list consists of \sym{CtrlRecVal} items (see \file{ctrl.h}), which hold the following data: \begin{itemize} \item the frame where the change occurred \item the value \item the type, which can be \usym{ARVT{\_}START}, \usym{ARVT{\_}VAL} or \usym{ARVT{\_}STOP}. \usym{ARVT{\_}VAL} are written by every \usym{AudioTrack::recordAutomation} call, \usym{ARVT{\_}START} and \usym{ARVT{\_}STOP} are generated by \sym{AudioTrack::startAutoRecord} and \sym{stopAutoRecord}, respectively. \item and the id of the controller which is affected \end{itemize} It is processed when the song is stopped. The call path for this is: \sym{Song::stopRolling} calls \sym{Song::processAutomationEvents} calls \sym{AudioTrack::processAutomationEvents}. This function removes the old events from the track's controller list and replaces them with the new events from \sym{{\_}recEvents}. In \usym{AUTO{\_}WRITE} mode, just all controller events within the recorded range are erased; in \usym{AUTO{\_}TOUCH} mode, the \usym{ARVT{\_}START} and \usym{ARVT{\_}STOP} types of the \sym{CtrlRecVal} events are used to determine the range(s) which should be wiped. \paragraph{How it's stored} Automation data is kept % this is copied from in \sym{AudioTrack::{\_}controller}, which is a \sym{CtrlListList}, % "design decisions" -> "automation" that is, a list of \sym{CtrlList}s, that is, a list of lists of controller-objects which hold the control points of the automation graph. The \sym{CtrlList} also stores whether the list is meant discrete (a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value). Furthermore, it stores a \sym{{\_}curVal} (accessed by \sym{curVal()}), which holds the currently active value, which can be different from the actually stored value because of user interaction. This value is also used when there is no stored automation data. \sym{AudioTrack::addController} and \sym{removeController} are used % TODO: swapControllerIDX?? to add/remove whole controller types; the most important functions which access \sym{{\_}controller} are: \begin{itemize} \item \sym{processAutomationEvents}, \sym{recordAutomation}, \sym{startAutoRecord}, \sym{stopAutoRecord}: see above. \item \sym{seekPrevACEvent}, \sym{seekNextACEvent}, \sym{eraseACEvent}, \sym{eraseRangeACEvents}, \sym{addACEvent}, \sym{changeACEvent}, which do the obvious \item \sym{pluginCtrlVal}, \sym{setPluginCtrlVal}: the first returns the current value according to the \sym{{\_}controller} list, the second only sets the \sym{curVal}, but does not insert any events. \end{itemize} Whenever a \sym{CtrlList} has been manipulated, \sym{MusEGlobal::song->controllerChange(Track{*})} shall be called, which emits the \sym{MusEGlobal::song->controllerChanged(Track{*})} signal in order to inform any parts of MusE about the change (currently, only the arranger's part canvas utilizes this). \paragraph{Enabling and disabling controllers} Disabling the controller is both dependent from the current automation mode and from whether the GUI is native or not. In \usym{AUTO{\_}WRITE} mode, once a slider is touched (for MusE-GUIs) or once a OSC control change is received (for native GUIs), the control is disabled until the song is stopped or seeked. In \usym{AUTO{\_}TOUCH} (and currently (r1492) \usym{AUTO{\_}READ}, but that's to be fixed) mode, once a MusE-GUI's slider is pressed down, the corresponding control is disabled. Once the slider is released, the control is re-enabled again. Checkboxes remain in "disabled" mode, however they only affect the recorded automation until the last toggle of the checkbox. (Example: start the song, toggle the checkbox, toggle it again, wait 10 seconds, stop the song. This will NOT overwrite the last 10 seconds of automation data, but everything between the first and the last toggle.). For native GUIs, this is a bit tricky, because we don't have direct access to the GUI widgets. That is, we have no way to find out whether the user doesn't touch a control at all, or whether he has it held down, but just doesn't operate it. The current behaviour for native GUIs is to behave like in \usym{AUTO{\_}WRITE} mode. The responsible functions are: \sym{PluginI::oscControl} and \sym{DssiSynthIF::oscControl} for handling native GUIs, \sym{PluginI::ctrlPressed} and \sym{ctrlReleased} for MusE default GUIs and \sym{PluginI::guiParamPressed}, \sym{guiParamReleased}, \sym{guiSliderPressed} and \sym{guiSliderReleased} for MusE GUIs read from a UI file; \sym{guiSlider{*}} obviously handle sliders, while \sym{guiParam{*}} handle everything else which is not a slider. They call \sym{PluginI::enableController} to enable/disable it. Furthermore, on every song stop or seek, \sym{PluginI::enableAllControllers} is called, which re-enables all controllers again. The call paths for this are: \begin{itemize} \item For stop: \sym{Song::stopRolling} calls \sym{Song::processAutomationEvents} calls \sym{Song::clearRecAu{\-}to{\-}ma{\-}tion} calls \sym{Track::clearRecAutomation} calls \sym{PluginI::enableAllControllers} \item For seek: \sym{Audio::seek} sends a message ("\sym{G}") to \sym{Song::seqSignal} which calls \sym{Song::clearRecAutomation} which calls \sym{PluginI::enableAllControllers} \end{itemize} \chapter{Design decisions} \section{Automation} As of revision 1490, automation is handled in two ways: User-generated (live) automation data (generated by the user moving sliders while playing) is fed into \sym{PluginI::{\_}controlFifo}. Automation data is kept in \sym{AudioTrack::{\_}controller}, which is a \sym{CtrlListList}, that is, a list of \sym{CtrlList}s, that is, a list of lists of controller-objects which hold the control points of the automation graph. The \sym{CtrlList} also stores whether the list is meant discrete (a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value). While \sym{PluginI::{\_}controlFifo} can be queried very quickly and thus is processed with a very high resolution (only limited by the minimum control period setting), the automation value are expensive to query, and are only processed once in an audio \emph{driver} period. This might lead to noticeable jumps in value. This could possibly be solved in two ways: \paragraph{Maintaining a slave control list} This approach would maintain a fully redundant slave control list, similar to \sym{PluginI::{\_}controlFifo}. This list must be updated every time any automation-related thing is changed, and shall contain every controller change as a tuple of controller number and value. This could be processed in the same loop as \sym{PluginI::{\_}controlFifo}, making it comfortable to implement; furthermore, it allows to cleanly offer automation-settings at other places in future (such as storing automation data in parts or similar). \paragraph{Holding iterators} We also could hold a list of iterators of the single \sym{CtrlList}s. This would also cause low CPU usage, because usually, the iterators only need to be incremented once. However, it is pretty complex to implement, because the iterators may become totally wrong (because of a seek in the song), and we must iterate through a whole list of iterators. \paragraph{Just use the current data access functions} By just using the current functions for accessing automation data, we might get a quick-and-dirty solution, which however wastes way too much CPU resources. This is because on \emph{every single frame}, we need to do a binary search on multiple controller lists. \chapter{Feature requests} \section{Per-Part automation and more on automation} % by flo Automation shall be undo-able. Automation shall reside in parts which are exchangeable, clonable etc (like the MIDI- and Wave-Parts). Global per-synth/per-audiotrack automation shall also be available, but this can also be implemented as special case of part automation (one long part). \section{Pre-Rendering tracks} \subsection{The feature} All tracks shall be able to be "pre-renderable". Pre-rendering shall be "layered". Pre-rendering shall act like a transparent audio cache: Audio data is (redundantly) stored, wasting memory in order to save CPU. That is: Each track owns one or more wave-recordings of the length of the song. If the user calls "pre-render" on a track, then this track is played quasi-solo (see below), and the raw audio data is recorded and stored in the "layer 0" wave recording. If the user has any effects set up to be applied, then each effect is applied on a different layer (creating layer 1, layer 2 etc). This means, that also MIDI and drum tracks can have effects (which usually only operate on audio, but we HAVE audio data because of this prerendering). Furthermore, MusE by default does not send MIDI events to the synthesizers but instead just plays back the last layer of the prerecording (for MIDI tracks), or does not pipe the audio data through the whole plugin chain (causing cpu usage), but instead just plays back the last layer. The hearable result shall be the same. Once the user changes any parameter (automation data or plugins for wave tracks, MIDI events or effect plugin stuff for MIDI tracks), then MusE shall generate the sound for this particular track in the "old" way (send MIDI data to synthes, or pipe audio data through plugins). (So that the user will not even notice that MusE actually pre-renderered stuff.) Either MusE automatically records this while playback (if possible) or prompts the user to accordingly set up his cabling and then record it. Or (temporarily) disables prerecording for this track, falling back to the plain old way of generating sound. \emph{Quasi-solo} means: For wave tracks, just solo the track. For MIDI tracks, mute all tracks which are not on the same synth (channel?), and mute all \emph{note} events which are not on the quasi-soloed track. This causes MusE to still play any controller events from different tracks, because they might have effects on the quasi-soloed track. (You can have notes on channel 1 on one track and controller stuff on channel 1 on another track; then you would need quasi-solo to get proper results.) \subsection{Use cases} \paragraph{Saving CPU} On slow systems, this is necessary for songs with lots of, or demanding (or both) soft synthes / plugins. Even if the synth or plugin is so demanding that your system is not able to produce sound in real-time, then with this feature you'll be able to use the synth (this will make editing pretty laggish, because for a change you need to re-render at least a part before you can listen to it, but better than being unable to use the synth at all!) \paragraph{Exporting as audio project} Using pre-rendering on all tracks, you easily can export your project as multi-track audio file (for use with Ardour or similar DAWs). Just take the last layer of each track, and write the raw audio data into the file, and you're done. (Maybe we are even able to write down the raw-raw layer0 audio data plus information about used plugins and settings etc..?) \paragraph{Mobile audio workstations} You might want to work a bit on your audio projects on your notebook while you're not at home, not having access to your hardware synthesizers. Using this feature, you could have pre-recorded the stuff in your studio before, and now can at least fiddle around with the non-hw-synth-dependent parts of your song, while still having your \emph{full} song with you. \paragraph{Applying effects on MIDI tracks} If you have many physical audio inputs, you might already be able to apply effect chains on MIDI tracks, by wiring the synthes' audio outputs to your soundcard's inputs, and applying the effects on dedicated input tracks you have to create. This requires you to have expensive hardware, and is pretty complicated, because you need one additional track per MIDI synth. This feature allows you to apply effects on single MIDI tracks, and not only on full MIDI synthes, and doesn't require you to be have that many physical audio inputs (you need to manually replug your synthes, however). \subsection{Possible scenarios} \paragraph{Setting it up} Create a wave track, MusE will allow you to set or unset prerendering for every plugin in the plugin rack (recording the actual track is useless because it would be a plain copy). Create a MIDI track, MusE will ask you on which physical audio input your synth is connected. Setting up multiple synthes on one physical audio in is allowed, see below. \paragraph{Pre-rendering stuff} When the user presses the "pre-render" button, all tracks which have been changed since their last pre-rendering will be re-rendered. If you have multiple hardware synthes set up as they were connected to one physical audio input port, MusE will prompt you to first plug the proper cable in. \paragraph{Making changes} Change a note in a MIDI part, move or delete a part or change automation parameters. MusE will temporarily disable the pre-rendered information and instead generate the sound via sending out MIDI events, piping stuff through effect chains or similar. If you play back the whole song, or if you manually trigger a re-rendering of a track via the context menu, MusE will play back the stuff, record it again and re-enable the pre-rendered information. \subsection{Extensions} \paragraph{Automatic discovery of physical audio connections} The user plugs all (or only some) synthes' audio outs into the available audio inputs, then runs automatic discovery. This will send MIDI events to each synthesizer, and look on which audio in there's activity. Then it will assume that the synthesizer is connected to that particular audio in. Audio ins which show activity before any MIDI events were sent are not considered, as they're probably connected to microphones or other noise-generating non-synthes. \paragraph{Audio export} As described in the Use cases, MusE can allow you to export your song in some multitrack audio format. \paragraph{Cheap/Faked changes} For expensive or unavailable synthes, changing the Volume midi controller, the Pan controller or similar "easy" controllers will not trigger a complete re-rendering, but instead "fake" the change, by changing the volume data directly on the recorded wave. This might require some learning and might even get pretty complicated. \paragraph{Intelligent re-recording} For tiny changes, MusE shall only re-render the relevant part. If you change some MIDI notes, then begin re-recording shortly before the changes, and end re-recording as soon as the recorded stuff doesn't differ to much from the stuff coming from the synth. Then properly blend the old recording with the updated part. \section{Slotted editors} Currently, MusE has the pianoroll editor, drum editor, score editor, then the controller editor which is inside the pianoroll/drum editor. All these editors have a very similar concept: the "time axis" is vertical and (almost) linear, they handle parts, and events are manipulated similarly. A unified editor shall be created which allows you to combine different kinds of editors in one window, properly aligned against each other. These "different kinds of editors" shall be handled as "slots"; one unified editor window consists of: \begin{itemize} \item A menu bar, containing stuff suitable for the complete window, which might include window name, MDI-ness etc. \item A toolbar which contains controls suitable for every single slot. \item A container with one or more slots; the slots can be scrolled in y-direction if there are multiple slots. \item A time-scrollbar with zoom \end{itemize} Each slot contains the following: \begin{itemize} \item A menu button, button box or control panel for setting up this particular slot. This could contain "note head colors", "show a transposing instrument" etc for score edit slots, "event rectangle color", "grid size" and "snap to grid" for pianoroll/ drum editors. \item The actual canvas \item A y-direction scroll bar, possibly with zoom control (for pianoroll editor) \end{itemize} The main window does not show its scroll bar if there is only one slot, because the slot's scrollbar is sufficient then. Slots can be added, destroyed, moved around, maybe even merged (if the slot types allow it); basically, you can compare them with the staves in the score editor. The slots shall align against each other, that is, if a score editor slot displays a key change with lots of accidentials, then all other slots shall either also display the key change (if they're score slots) or display a gap. Events which happen at the same time shall be at the same x-coordinate, regardless which slot they are. \section{Controller master values} All controllers (MIDI-controllers and also automation controllers) shall have one set of "master values" which allow you to set a gain and a bias. Instead of the actual set value, $\textrm{value} * \textrm{bias} + textrm{bias}$ shall be sent to the MIDI device / the plugin. For controllers like "pan", the unbiased values shall be transformed, that is, a pan of 64, with $\textrm{bias}=2$ and $\textrm{gain}=0.5$, shall be transformed to 66 (because 64 is actually 0, while 0 is actually -64). These values shall be set in the arranger and wherever the actual controller/automation values can be edited. \section{Enabled-indicator while recording} The MusE-plugin-GUIs shall display a small LED displaying whether a controller is currently enabled or disabled. By clicking this LED, the enabled state shall be switched. Furthermore, there shall be a dedicated window which only lets you switch enabled/disabled states. This will be useful when using external GUIs or the MIDI-controller-to-automation feature, to re-enable a controller when in \usym{AUTO{\_}TOUCH} mode. \section{Linear automation editing} While holding some modifier key (like shift), operating the MusE-native- GUI sliders shall only generate control points when clicking and when releasing the slider. This will result in linear graphs for continuous controllers, and in large steps for discrete controllers (which is in particular useful for stuff like "which low/high-pass filter type to use"). Maybe make this behaviour default for discrete controllers? \section{Symbolic names for MIDI ports} \label{symbolic_ports} MIDI ports shall have a user-defined symbolic name (like "Korg" or "Yamaha DX 7"). The mapping between these symbolic names and the hardware port (like "ALSA midi out port") is stored in the global configuration. Song files only specify the symbolic names as the ports associated with their tracks. No information about physical devices/port names, but only symbolic names are stored in the song file. This resolves the issues mentioned in \ref{portconfig_sucks}, and also allows the user to share his pieces with other people: They would only have to set up that symbolic-to-hardware mapping once (collisions are unlikely, because an equal symbolic name should usually mean the same device) and are happy, instead of having to re-map \emph{every} port for \emph{every} song. \end{document} % TODO: song type etc? kill it! % song len box: same % TODO: unified automation and midi ctrls: % both shall be editable through the same editors % four modes: 1. discrete % 2. continuous (plus a global and per-port setting for the max rate) % 3. switch (not necessarily ON/OFF; signals with color plus text annotation) % 4. raw (no graph, instead a box with the value sent out (for "all voices off") % they shall be copy-and-pastable, at least between compatible modes % they shall be slotted, like pianoroll % maybe also "overlapping", like arranger (?) % midi recording tries to make out straight lines (like non-ended automation streams) % % new song (in general: load as template) plus "overwrite port config" % should re-create the default jack devices and autoconnect them. % what's audio aux for? % bug in arranger/pcanvas/automation: if a controlpoint is directly on % a line of another ctrl graph, you can't click it muse-4.0.0/src/doc/documentation.tex000066400000000000000000001445571404105311000174200ustar00rootroot00000000000000%% (c) 2012 florian jung %% (c) 2012 Robert Jonsson %% we should consider putting this under a proper license. GPL, or %% some GPL-like documentation license?? %% rules for editing documentation: (READ THIS FIRST) %% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ %% %% please try to let newly written lines be shorter than 72 characters. %% minor exceptions are okay, but please not more than 80 chars. %% comments shall start after character #80 of the line (that is, %% they shall be "on the right margin") %% %% DON'T MIX up changes and reformatting in one commit. when changing %% stuff, please don't touch the lines before and after your change %% (that is, do not re-wrap them), even if it will look a bit patchy. %% this is for being able to easily use diff. %% when you want to reformat this file, then do it. but don't change %% anything, as this would be hard to find in a diff. and clearly %% state in the commit log that you "only" rearranged things. %% %% please adhere to the "User's manual" / "Internals" / "Design" %% partitioning (genereally, don't change the chapters until there %% is a really good reason for doing so (adding a chapter like %% "feature requests" as flo did in r1497 IS one). %% Below that, feel free to change the logical arrangement %% (making paragraphs to subsections and similar) if you deem it %% necessary. %% %% Whenever referring to code symbols, constants or source/header %% files, please use \sym{someClass::someSymbol}, \usym{UPPERCASE_THING} %% or \f{file.cpp}. %% Only specify file paths or namespaces where it would be ambiguous %% otherwise. Specify 'someClass::' until it would disturb the reader %% and it is obvious. you have to replace '_' by {\_} (with the {}!). %% These macros do automatic hyphenation on Camel-Case, under_-score %% and scope::-operator boundaries. If you need to insert additional %% hyphenation points, use {\-}. %% Example: \sym{someClass::someAb{\-}nor{\-}mal{\-}lyLongName} will %% hyphenate: some-Class::-some-Ab-nor-mal-ly-Long-Name %% %% Whenever referring to URLs, please wrap them in \url{blah}. Key %% combinations shall look like \key{CTRL+C}. Menu items shall look %% like \menu{Menu > Submenu > Menu Item}. %% %% Where possible, reference other parts of this documents with %% \label and \ref. Avoid duplicate information under "Internals" by %% referring to the appropriate section in "User's manual". %% %% Please do no time-stamping of sections. if you need time-stamps, %% use "svn blame documentation.tex" %% %% If you contribute something, feel free to add yourself to \author. %% %% If you don't speak LaTeX fluently, a few tips: %% * \section, \subsection, \subsubsection, \paragraph, \subparagraph %% let you create sections etc. just copy-and-paste if unsure. %% * you must prefix special characters like the underscore with \ %% (backslash) %% * \emph{some text} emphasizes the text, printing it italic. %% * \texttt{some text} displays the text in a typewriter font %% * \label{someName} creates a label at this position. this doesn't %% show up in the pdf. with \ref{someName}, you can reference to this %% label. (LaTeX will insert the section number instead of \ref) %% For this to work, you might need to recompile the .tex twice. %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Dependencies: %% To produce pdf output from this document the following packages are %% needed (atleast under Ubuntu derivatives) %% latex???-base %% texlive-latex-recommended %% texlive-latex-extra %% %% To produce a pdf version of this manual type: %% pdflatex documentation.tex %% A file documentation.pdf should be generated. %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \documentclass[a4paper]{report} \usepackage[a4paper, left=2.5cm, right=2.5cm, top=2.5cm, bottom=2.5cm]{geometry} \usepackage[T1]{fontenc} \usepackage[utf8]{inputenc} \usepackage{lmodern} \usepackage[english]{babel} \usepackage{graphicx} \usepackage{hyphenat} \usepackage{wrapfig} \usepackage{fancyhdr} \usepackage{hyperref} \usepackage{xcolor} \hypersetup{ linkcolor=blue, urlcolor=blue, colorlinks=true, % hyperlinks will be blue linkbordercolor=blue, % hyperlink borders will be blue %pdfborderstyle={/S/U/W 1} % border style will be underline of width 1pt } \pagestyle{fancy} \lhead{\scriptsize{\slshape\leftmark}} \chead{} \rhead{\thepage} \lfoot{} \cfoot{} \rfoot{} \renewcommand{\headrulewidth}{0.4pt} \usepackage{ifthen} % Hyphenate symbols before each uppercase letter, after each underscore % (without a "-") and after each ':' (without a "-") % TODO for any latex crack: also do automatic hyphenation, that is, % instead of some-Automation-Expression, do some-Au-to-ma-tion-Ex-press-ion \makeatletter \newcommand{\camelhyph}[1]{\@fterfirst\c@amelhyph#1\relax } \newcommand{\underscorehyph}[1]{\@fterfirst\u@nderscorehyph#1\relax } \def\@fterfirst #1#2{#2#1} \def\c@amelhyph #1{% \ifthenelse{\equal{#1}\relax}{}{% Do nothing if the end has been reached \ifnum`#1=95 \_\hspace{0pt}\else % Check whether #1 is "_", then print _[thin space] \ifnum`#1=58 :\hspace{0pt}\else \ifnum`#1>64 \ifnum`#1<91 \-#1\else#1\fi% Check whether #1 is an uppercase letter, \else#1\fi \fi \fi % if so, print \-#1, otherwise #1 \expandafter\c@amelhyph% % insert \c@amelhyph again. }} \def\u@nderscorehyph #1{% \ifthenelse{\equal{#1}\relax}{}{% Do nothing if the end has been reached \ifnum`#1=95 \_\hspace{0pt}\else % Check whether #1 is "_", then print _\- \ifnum`#1=58 :\hspace{0pt}\else#1\fi\fi \expandafter\u@nderscorehyph% % insert \u@nderscorehyph again. }} \makeatother \author{Florian Jung, Robert Jonsson, Tim Donnelly} \title{MusE Documentation} % Orcan: not needed since the hyperref package takes care of all %\newcommand{\url}[1]{\texttt{#1}} \newcommand{\key}[1]{\textbf{#1}} \newcommand{\shell}[1]{\texttt{\textbf{#1}}} \newcommand{\menu}[1]{\textbf{#1}} \newcommand{\sym}[1]{\texttt{\camelhyph{#1}}} \newcommand{\listing}[1]{\texttt{\camelhyph{#1}}} \newcommand{\usym}[1]{\texttt{\underscorehyph{#1}}} \newcommand{\file}[1]{\texttt{\camelhyph{#1}}} \newcommand{\screenshotwidth}[0]{0.8\textwidth} \begin{document} \begin{figure}[htp] \centering \includegraphics[width=1.0\textwidth]{pics/muse2} \label{fig:MusE} \end{figure} \tableofcontents \chapter{What is this?} You are, if you have printed this document, holding in your hand the written documentation for the audio and midi sequencer MusE version 2.\\ \url{http://www.muse-sequencer.org} is MusE's home on the internet where everything MusE related should be possible to find, software, this documentation, forums, mailing lists, bug reporting, FAQs. If you have this document but not the software head on over there to find what it's all about. \chapter{User's manual} \section{Introduction} \subsection{A brief history of computer audio and MusE} To quickly summarize over a decades open source development: in 1999 Werner Schweer released the first version of MusE, muse-0.0.1.tar.gz, in it's first few releases (actually not few, Werner relentlessly churned out new releases) MusE was only a midi sequencer. The target was to create a fully fledged midi sequencer for the Linux operating system. Over the years audio was added among with other things implemented and sometimes abandoned. Today MusE is a stable and feature rich music creation environment which strives to encompass most of the music recording process, creation, editing, mastering. \subsection{Definitions} \key{CTRL} refers to the control key on the keyboard, e.g. \key{CTRL+C} means to press and hold the control key while pressing the c key. Make sure you know where you have it so you won't accidentally lose control (bad jokes are the best jokes, so say we all!).\\ \key{SHIFT} refers to the shift key on the keyboard, see above for usage\\ \key{ALT} refers to the alt key on the keyboard, see above for usage\\ \shell{\$>} is used as a generic definition for a terminal prompt. When the manual lists a command that shall be typed, the prompt is not part of the command.\\ Keys are always referred to in bold uppercase, e.g. \key{A}. For instance \key{SHIFT+A} for the key a pressed together with the shift key.\\ Sometimes terminal examples are written tabbed in with a fixed font to visualize more closely what something looks like on the screen. E.g.\\ \hspace*{1cm}\shell{\$> muse2}\\ \subsection{Getting up and running for impatient people} Install MusE from the repository of your chosen distribution. To get decent performance start \href{http://jackaudio.org/}{Jack} with the following command in a terminal:\\ \hspace*{1cm}\shell{\$> jackd -d alsa -d hw:0 -p 256}\\ Or, if you prefer, use the launcher utility \href{http://qjackctl.sourceforge.net/}{QJackCtl} to get some help starting Jack. After this, start MusE from the menu or fire up another terminal and type \shell{muse2}.\\ If this didn't work out read on for the slightly more complete route for getting things started. \subsection{Getting up and running} \subsubsection{Installation from binaries} There are several ways to install MusE depending on your situation. The most convenient way is to install a prepackaged version from your chosen distribution. The drawback of this is that it may not be the most recent version, though often there is a more recent package from a private packager. \subsubsection{Installation from source} Building MusE from source is not hard, there are a number of prerequistes that must be met but the actual building should be painless (ha, famous last words).\\ Please follow the README in the source package and/or read the instructions on the homepage: \url{http://muse-sequencer.org/index.php/Installation} \subsubsection{Hardware} MusE on the Linux platform supports midi through ALSA and Jack-midi and audio through Jack. For information on what hardware is supported there are some convenient places to check: \begin{itemize} \item Alsa soundcard matrix at \url{http://www.alsa-project.org/main/index.php/Matrix:Main} \item \url{http://FFADO.org} for firewire devices. \end{itemize} Also, as is often a very good approach for Linux and open source, the various forums available on the internet often contain good information. Chances are someone has already tried your configuration and/or had your specific problem and the solution is already written down. \subsubsection{Launching} After installation the binary muse2 is installed on the computer. If MusE was installed from a distribution repository the binary may have a different name depending on the distribution policies. Most distributions do however install a menu entry so MusE should be conveniently available from there. \subsubsection{Audio preconditions} In the standard case MusE expects to find and connect to the Jack audio server \url{http://jackaudio.org}. Make sure jack is installed (if MusE was installed with a distribution-package Jack will very likely already be installed) For Jack to run with best performance your system should be sufficiently tuned to allow it to run with realtime capabilities. The realtime configuration is configuration of the operating system and roughly consists of two parts. \begin{enumerate} \item By default on most distros only the superuser lets applications setup realtime capabilities. Please see the APPENDIX for setting up realtime \item Maximizing performance. A standard linux installation may not able to reach the performance required by a power user. This requires exchanging the linux kernel for a so called lowlatency kernel, this is also covered by the realtime APPENDIX. \end{enumerate} \subsubsection{Running MusE} Find MusE in the menu or open a terminal and enter muse2. \shell{\$> muse2}\\A splash screen should pop up followed by the main application window and you are off!\\ If an error like the screenshot below pops up the Jack audio server is either not running or started as a different user than what you are trying to start MusE as. \begin{figure}[htp] \centering \includegraphics[width=\screenshotwidth]{pics/no_audio} \caption{Jack server missing} \label{fig:no_audio} \end{figure} \subsubsection{Midi only} MusE can be started in Midi-only mode where MusE does not have any external dependencies apart from ALSA midi. In this case start MusE from a terminal: \shell{\$> muse2 -a} \subsubsection{ALSA midi with Jack} If Jack is running, by default MusE will not use ALSA devices, preferring Jack midi instead. To force ALSA devices to be used as well as Jack midi, start MusE with the -A option: \shell{\$> muse2 -A} \subsection{Beginners tutorial} To get a quick grip of what MusE can achieve please follow this beginners tutorial. \subsubsection{Midi Setup} First off, fire up MusE as was described in the previous chapter, making sure that the jack audio server is started with sufficient configuration to allow for audio output without breakup. Also make sure your system can make sound. \subsubsection{Soft synth test} With MusE up and running right click in the Track-pane (see Fig. \ref{fig:Main Window}) and select \menu{Add Synth > MESS > vam soft synth}. A Soft Synth track called vam-0 should appear as well as a separate GUI for the synthesizer. Now right click once more in the Track-pane and select \menu{Add Midi Track}. Another track appears called Track 1, and its track list Port column should show it is bound to the synth that was just created vam-0. If it is not, click on the Track 1 Port column to open a drop-down list of available devices and choose vam-0. \begin{wrapfigure}{r}{0.05\textwidth} \includegraphics[width=0.05\textwidth]{pics/arrow_tool} %\hrulefill \end{wrapfigure} Now select the drawing tool icon from the toolbar, alternatively press the shortcut key \key{D}. Move the mouse over to the arranger canvas as referenced in Fig. \ref{fig:Main Window} and point at the midi track, the mouse should have changed to a small pencil. Draw a Part along the midi track using the mouse. For this exercise it is not important where or how large the drawn Part is. When you are done double click on the drawn part. This will open up the Piano Roll editor. To the left of the Piano Roll there are piano keys in a vertical line, try clicking on the keys in this virtual keyboard each click should be rewarded with a synth sound (maybe of questionable quality, a sound nevertheless) \begin{figure}[htp] \centering \includegraphics[width=.5\textwidth]{pics/vam_synth} \caption{vam synthesizer} \label{fig:vam_synth} \end{figure} \subsubsection{Missing sound} If you got sound from the previous exercise you can carry on to the next, or keep reading for further enlightenment in case you come upon trouble later on. If there is no sound we need to do some fault hunting. First off, click on Arranger window once more and select the vam-0 track in the track-pane. \begin{figure}[htp] \centering \includegraphics[width=\screenshotwidth] {pics/main_window_with_midi_editor_vam} \caption{Midi editor view} \label{fig:Midi editor} \end{figure} Now bring back Piano Roll window and align the windows so you can see the piano keys as well as the Meter on the Mixer Strip (see the 5 Function by function chapter for more information on these windows). The result should be something like the following: When pressing one of the keys on virtual Keyboard the Meter on the Mixer Strip should light up in green to visualize that the Synth is making sound, if it is not try to trace back your steps and and see if you did anything differently than described. Now, if the Meter lights up but there is still no sound we need to check the routing between the tracks. Click on the Arranger window again and select the Out 1 track, this is the predefined output which MusE by default loads at startup, at the bottom of Mixer Strip there are two buttons looking like tele- jacks, these bring up the inputs and outputs of the track, click on the right one, the output and make sure that it is connected to some valid outputs on your system. \begin{wrapfigure}{r}{0.25\textwidth} \includegraphics[width=0.25\textwidth]{pics/output_routing} %\hrulefill \end{wrapfigure} Click on the outputs to select them, if you did changes here go back and try clicking on the Piano Roll keyboard again, hopefully it helped. If there still are problems make sure your system actually can make sound through Jack, this is however getting outside the scope of this manual.\\\\ \textit{This might be the time to bring up the concept of community support. Open source software could never be what it is without the support given by individuals on forums and mailinglists, if the information given in this document is not enough, try googling your problem and/or get in touch with one of the online forums for MusE or Linux audio in general. See some pointers in the Support chapter.} \subsubsection{Recording Midi} %TODO: walkthrough of recording midi TBD \subsubsection{Recording Audio} At this point we'll make a slight detour into full on audio recording. Getting audio out of MusE has already been covered in the previous chapters so we will concentrate on the additional steps needed to record onto an audio track.\\ \\ When MusE is first fired up, the output track has already been created (more about this in the chapter about templates), to proceed with audio recording we need to add two additional tracks, a wave track and an input track.\\ When MusE is first started right click in an empty space on the track view \begin{figure}[htp] \centering \includegraphics[width=\screenshotwidth] {pics/main_window_add_track} \caption{Add track} \label{fig:Add track} \end{figure} and select \menu{Add Audio Input}. Right click again and also select \menu{Add Wave Track}. Two additional tracks are now visible in the Arranger, "Input 1" and "Track 1", bring up the mixer with \menu{F10} and you should see the following configuration. \begin{figure}[htp] \centering \includegraphics[width=0.25\textwidth]{pics/mixer_with_one_input} \caption{Mixer with one input} \label{fig:Mixer with one input} \end{figure} \\ Note the buttons on each mixer strip. hover over them to see their functionality. For more information on all the buttons see coming chapters about the mixer. For now lets just do what we must.\\ 1. click on the stereo symbol over the slider to change the input to a mono track.\\ 2. do the same for the wave track (optional)\\ 3. click on the Mute (gray speaker) icon on the input track to unmute it.\\ 4. click on the input routing button (see the tooltip, it looks like a tele plug) on the input track and select an appropriate connection from your system.\\ 5. click on the output routing button on the input track and select \textbf{Track 1}\\ \\ Already after the meter on the input track should be able to display that there is incoming sound from your sound source. If there actually is sound coming from your sound source, that is.\\ We are now nearly ready to start recording. First we need to select a location to store the files. MusE does not use a centralized storage of soundfiles but uses the path of the song-file (extension .med) as guidance as to where the audio files should be placed. Now as it happens MusE will prohibit us from starting a recording until the songfile has been stored. So lets take advantage of this behaviour and just go ahead and try to record. Let's get started.\\ In the mixer click on the red \textbf{record} dot on the Audio Track to arm it for recording (or enable if you will). Now when there is audio coming into the input it will also show up on the Audio Track. Also note that all the input and output routing buttons on the tracks now have the same gray color, this means that all of the tracks have a proper connection. \begin{figure}[htp] \centering \includegraphics[width=0.25\textwidth]{pics/mixer_with_one_input_buttons} \caption{Mixer buttons} \label{fig:Mixer buttons} \end{figure} \\ All fine and dandy. Now bring up the arranger window and find the round, red on white \textbf{record} button and click on it. This is your queue to MusE to prepare for recording. However since we have not saved our song we are presented with a dialog to do just that.\\ \begin{figure}[htp] \centering \includegraphics[width=0.6\textwidth]{pics/project_my_first_song} \caption{Save song} \label{fig:Save song} \end{figure} Note the check box for creating a project folder, when working with audio this is very much recommended or you may soon loose track of what audio files belong to which song.\\ Finally we are ready to start recording! The process is completed by clicking on the \textbf{Play} button in the Arranger. If all went well MusE then starts to record a wave file from the Input Track placed in your song directory.\\ When you wish to stop recording press \textbf{Stop} in the Arranger, now the resulting waveform should be visible in the Arranger. After rewinding the Play position and pressing \textbf{Play} again the resulting sound should be audible through the connected output. \section{Basic overview} In this section we will make a step by step walk-through of all the different editors, their purpose and what functions they support. \subsection{Main/Arranger} \label{Main/Arranger} \begin{figure}[htp] \centering \includegraphics[width=\screenshotwidth] {pics/main_window_annotated} \caption{MusE main window} \label{fig:Main Window} \end{figure} Above is the main window of MusE, the Arranger, this is what greets you when launching MusE. The Arranger consists of two main parts, the Track-pane and the Arranger canvas. The Track-pane lists all currently visible tracks and the Arranger canvas contains all Parts of the composition. The screenshot above shows an empty project. Below is MusE with a song in progress, turns out it wasn't a very good song, but for our purposes it is fine. In the below screenshot there are a lot of tracks visible in the Track-pane, each have an icon which indicate it's type, wave-track, input, output etcetera, more about that later. In the Arranger canvas a number of parts are visible, the ones in yellow are in this composition wave files, the multicolored line are different Parts of a drum track. \begin{figure}[htp] \centering \includegraphics[width=\screenshotwidth] {pics/main_window_with_arrangement} \caption{MusE main window with arrangement} \label{fig:Main Window with arrangement} \end{figure} \subsection{Mixer} \label{mixer} Choosing \menu{View > Mixer A} or \menu{B} from the menu in the main window will bring up the mixer as viewed below. The mixer will open with all options enabled, showing channel strips for all tracks in the current setup, depending on how far you have gotten this view may become very large, at which point it may be a good idea to limit what is viewed in the Mixer. From the view menu all the different kinds of tracks can be toggled on/off from the mixer. Some may find it a good idea to use the two mixers A and B setup with different setup and store this in your song template(s), more about this in the Song Template section. It can be argued that everything in MusE is a track analogous to the Unix idiom that everything is a file. The types of tracks visible in the mixer (and track-pane) are: \begin{wrapfigure}{r}{0.5\textwidth} \includegraphics[width=0.5\textwidth]{pics/mixer} %\hrulefill \end{wrapfigure} \begin{itemize} \item Audio output \item Audio input \item Group track \item Aux track \item Wave track \item Synth track \item Midi track \end{itemize} There is also a Midi Track variation called Drum Track, they are however not distinguishable from Midi Tracks in the Mixer. Also the strips for midi tracks are different in the Mixer than in the Track-pane view. \section{Tracks and parts} MusE arranges your music in \emph{tracks} and \emph{parts}. The following section shall provide you an overview of how things are done with MusE. If you are or were a Cubase or Cakewalk user, you will feel familiar with this. \subsection{Tracks} There are two general classes of tracks: MIDI tracks and audio tracks. MIDI tracks (and drum tracks which are internally MIDI tracks) can hold note data. The Wave track is a type of audio track which holds wave data. There are also several other kinds of audio tracks. \paragraph{MIDI tracks} MIDI and drum tracks hold MIDI event data. They don't differ much, except that drum tracks offer a special editor which is more suitable for drum editing. \paragraph{Wave tracks} They hold audio data which can be just played back or be piped through effect plugin chains. They offer automation for these plugins. \paragraph{Audio input tracks} These provide the path for your audio data from outside into your project. Set up the physical audio inputs you want to connect your audio input track with, and then route the input tracks to various other tracks such as wave tracks. \paragraph{Audio output tracks} These provide the path for your project's audio data to outside. Set up the physical audio outputs you want to connect your audio out track with, and then route various other tracks, such as wave tracks, to the output tracks. \paragraph{Audio group tracks} Group tracks are like busses, where you can route other tracks to them, then route the groups to other tracks. Since group tracks have all the features of other audio tracks, like volume and pan, they provide a convenient common routing point where you have control of the sound before it is passed to other tracks. \paragraph{Audio aux tracks} \label{aux_tracks} These provide a more convenient way to mix several audio tracks together. With each audio aux track added, other audio tracks will gain a common send knob for adjusting the level sent to the aux track. This can be more convenient than using several group tracks. \paragraph{Synthesizer tracks} This type of track is a software synthesizer which MIDI and drum tracks can be assigned to. \paragraph{Creation} You can create a track by either right-clicking in the arranger's track % TODO: insert screenshot list and then adding the desired track, or via the edit menu. \paragraph{Attributes} Tracks have several attributes: \begin{description} \item [{Mute:}] If you click on the \emph{Mute} field (denoted with a "M" column header), the track gets muted and stops producing sound. \item [{Solo:}] \label{track_attr_solo} The solo button ("S" column header) singles out a track for listening. It mutes some other tracks but may phantom solo others. For more info see the section on soloing: \ref{track_soloing} and phantom soloing: \ref{phantom_soloing} \item [{Record:}] The R column "arms" your track for recording. When you rec-arm your song and have no tracks rec-armed, you won't be able to record anything. See also the config option "move rec-arm with % TODO: reference to rec-arm config option selection". \item [{Track name:}] Double-click to edit the track name. \item [{Port:}] For MIDI tracks, this lets you select the MIDI port to which the events should be routed. This can be your physical synthesizer or a software synthesizer. For soft synths, this is the port the synth is associated to. For other track types, this is disabled. \item [{Channel:}] For MIDI tracks, this is the MIDI channel the output is sent to. For any kind of audio tracks, this is the number of channels (mono, stereo). % TODO: what's that "T" column?! \item [{Automation:}] \label{track_attr_automation} For audio tracks, this lets you set up the automation display in the arranger. (See automation \ref{audio_automation}). Clicking this will provide you with a popup menu with lots of submenus. Clicking on a submenu will select or unselect it showing or hiding the automation parameter as a graph overlaid on top of the track.\\ The submenus let you select the color you want to associate with the automation parameter. There you can also assign midi controllers to the parameters, a dialog is shown where you can manually choose the midi controller, with a \emph{learn} button to 'listen for' and automatically recognize any midi controller operated by you. \item [{Clef:}] For MIDI tracks, you can specify a clef here. This only affects the score editor. \end{description} \subsubsection{The trackinfo side bar} In the arranger and the part editors, you'll have a trackinfo sidebar on the left side. You can set up track-type specific things there. \paragraph{MIDI trackinfo sidebar} \label{midi_trackinfo_sidebar} The MIDI trackinfo sidebar lets you change program, volume, pan and more. This sidebar can also be viewed at the left of the pianoroll editor. %%FIXME Ref to pianoroll \subparagraph{Old style drum tracks:} These are MIDI tracks as well, but with a few differences. They allow you to map certain drum sounds with different input notes, and you can change the output settings of a certain "drum instrument" without having to alter each single event. However, they have certain limitations: They only can handle 128 sounds (even if you have more synthes), they aren't really compatible with MIDI tracks (you can interchange parts between them, but if you touched the drum list, you'll get unexpected results), you can't set a program for the used channel and more. \subsubsection{New style drum tracks} Because of these limitations, we introduced the new-style drum tracks. They're not fully compatible with the old drum tracks, so the old are still retained. Under "Global Settings", "GUI settings", you can set up whether you prefer the old or new. They are handled exactly like plain MIDI tracks (staying compatible with them), and offer all of the functionality, though in a different way. They allow you to re-order the drum map efficiently, you can open parts from multiple drum tracks in \emph{one} drum editor (MusE will separate the sounds from different tracks according to your settings, see the "Window Config" menu), and you can set programs as with normal MIDI tracks. \subparagraph{MIDI trackinfo controls:} \begin{description} \item [{Output port:}] This drop-down list selects the midi port to send midi output from this track. \item [{Output channel:}] This box selects the midi channel to be used on the output port. \item [{Input and output routing:}] Selects midi ports and channels to receive midi from, and soloing paths. (See Routes \ref{routes}). \item [{Midi through:}] This button selects whether midi input is passed through to the selected output port.\\ Depending on your midi devices and settings, there are cases when this should be off such as using the same port and channel for input and output (otherwise a double-note \emph{echo} will be heard), and cases when it must be on such as when using a synthesizer track as output device. \item [{Input detect indicator:}] Blinks when midi activity is detected on the selected midi channels on the selected midi input ports. \item [{Transpose:}] This transposes midi input notes up or down in pitch. This is very useful if your midi keyboard hasn't enough keys or the selected output device plays an octave too low or high, and you would like to shift the octave of the incoming notes to compensate. \item [{Delay:}] Adjusts the delay of the notes. %% FIXME What is this again? Does it work? \item [{Length:}] Adjusts the length of the notes. %% FIXME What is this again? Does it work? \item [{Velocity:}] Adjusts the velocity of incoming notes. Use it to compensate for a too-loud or too-soft keyboard. \item [{Compression:}] Adjusts the compression of incoming note velocities. Use it to make soft incoming notes louder, and loud notes not so loud. \item [{Instrument:}] Selects the midi instrument patch to be used by the selected output port. This is equivalent of dialing the patch in the bank and program boxes, except it displays a more friendly patch \emph{name} as defined by the selected output port's midi instrument. See instruments, or port configuration %% FIXME Ref to instruments. \ref{midi_port_config} \item [{H-Bank:}] Selects the high bank number of the current patch. \item [{L-Bank:}] Selects the low bank number of the current patch. \item [{Prog:}] Selects the program number of the current patch. \item [{Volume:}] Adjusts the midi volume controller. \item [{Pan:}] Adjusts the midi pan controller. \end{description} The buttons beside the Prog, Volume, and Pan boxes store the value, at the current transport position, for midi automation. (See automation \ref{midi_automation}). Note that the 'Prog' button stores H-Bank and L-Bank along with 'Prog' value, so there are no H-Bank and L-Bank buttons. The 'All' button simply stores all three Program (and banks), Volume, and Pan values at once. \emph{Tip:} If the Song Type is GM, GS, or XG, you may need to store %% FIXME Ref to song type desired values at transport position zero, otherwise your adjustments may be overridden by the instrument when the transport is moved back to position zero. If this behaviour is undesired, you can set the Song Type to 'NO' meaning no song type. %% FIXME Ref to explanation of instruments and default controller values \paragraph{Audio trackinfo sidebar} Unlike the midi trackinfo sidebar, the audio trackinfo side bar is nothing more than an embedded audio mixer strip, the exact same strip as found in the mixers. (See mixer \ref{mixer}). \subparagraph{Effects rack:} On the top of the audio trackinfo sidebar, there is an effects rack which allows you to apply various plugins on the audio. For more information on this, refer to \ref{effects_rack}. \subsection{Parts} Within MIDI, drum and wave tracks, you can create \emph{parts}. Parts are chunks of coherent notes or wave data which can be moved around, copied, cloned and deleted independent from other parts. Parts are created by selecting the pencil tool and then drawing onto the right part area in the arranger. You can move them with the arrow tool, delete them using the \key{DEL} key, and a right-click opens a popup menu. This menu allows you even more stuff, such as setting the part's color, saving the part to disk etc.. You can use \key{CTRL+C} and \key{CTRL+V} for copying and pasting parts. \key{CTRL+B} pastes the part as a clone. Pressing \key{SHIFT} additionally provides you a dialog which allows you to paste the part multiple times and set more stuff. You can also copy parts with the mouse by moving the part with the mouse while holding down the \key{CTRL} key. \section{Routes} \label{routes} Routes are how tracks are connected together and to the outside world. (They are also how Jack midi ports connect to the outside world. See midi port configuration \ref{midi_port_config}). Each track strip has two buttons whose icons look like plugs. One button is for input routing and the other is for output routing. Clicking on these buttons will pop up a menu of available input or output routes that you can connect to. Most audio tracks list other tracks to connect to, but audio input and output tracks are special: Audio input track input routing menus list available Jack audio input ports. Conversely audio output track output routing menus list available Jack audio output ports. \begin{wrapfigure}{r}{0.25\textwidth} \includegraphics[width=0.25\textwidth]{pics/output_routing} %\hrulefill \end{wrapfigure} Meanwhile MIDI and drum tracks allow you to route available MIDI ports and channels to the track using a handy popup matrix. \begin{wrapfigure}{r}{0.25\textwidth} \includegraphics[width=0.25\textwidth]{pics/midi_routing_matrix} %\hrulefill \end{wrapfigure} \subsection{Anti circular routing} \label{anti_circular_routing} Any routing menu item which would cause a circular routing condition is grayed out. Find out why the condition would exist by examining routing paths involved and correct the situation if required. Also, you cannot use a track's aux sends if the track has an input route path from ANY Aux Track. (See aux tracks \ref{aux_tracks}). Aux send knobs and labels are disabled in that case. \subsection{Soloing chain routes} \label{soloing_chain_routes} Soloing chains (see solo chains \ref{soloing_chains}) are really just routes like any other. The available solo chaining paths are displayed in the routing popup menus. \section{Track soloing} \label{track_soloing} Soloing allows you to single out a track for listening while muting others, without you having to mute the other tracks. (See soloing track attribute \ref{track_attr_solo}). \subsection{Phantom soloing} \label{phantom_soloing} In order to solo a track and mute others so that it is heard, MusE employs 'phantom' soloing: When a track is soloed, MusE automatically solos all tracks routed to and from this track. (See routes \ref{routes}). A phantom soloed track is indicated by a black square in the track pane solo column. (See track attributes \ref{track_attr_solo}). \begin{figure}[htp] \centering \includegraphics[width=\screenshotwidth]{pics/soloing_window} \caption{Soloing, with phantom soloing} \label{fig:Soloing} \end{figure} \subsection{Soloing chains} \label{soloing_chains} When an audio output track sends audio to some external entity, such as an external running application, and audio from the external entity is fed back into a MusE audio input track, solo chains allow you to solo the input or output, and MusE will complete the path automatically soloing the other, and all paths that came before or after it. Solo chains also work with MIDI tracks chained to audio inputs: When a MIDI track drives some MIDI device whose audio is fed into MusE, solo chains allow the entire chain to be soloed. Solo chains are accessed via routing menus. (See solo chain routes \ref{soloing_chain_routes}). \section{Plugins} \label{plugins} Plugins are small add-ons which can process a track's data. MIDI plugins operate on midi and drum tracks, and are found in the \menu{Midi} menu. Audio plugins can be applied to any track handling audio (that is, inputs, outputs, wave tracks, synth tracks). The effects rack section describes this. (See effects rack \ref{effects_rack}). \subsection{The audio effects rack} \label{effects_rack} All audio track types (Input, Output, Group, Wave, Synth, and Aux) have an effects rack into which audio plugins can be inserted in a chain. Currently each rack can accommodate up to four plugins. MusE currently supports LADSPA plugins and DSSI synth and effects plugins. Plugins can be added by double-clicking on an entry in the effect rack in the track info pane (which is shown at the left side of the arranger when the according track is selected). Right-clicking the rack items offers a self-explanatory popup menu. All plugin controls can be automated. (See audio automation \ref{audio_automation}). One must carefully consider how many audio inputs and outputs a plugin has, and how may channels the particular audio track has (1 mono or 2 stereo), and how MusE uses the plugins in the rack. Learn more about this in the appendix Understanding the Effects Rack: \ref{apx_effects_rack} \subsubsection{Audio plugin Graphical User Interfaces (GUIs)} \label{plugin_guis} Once a plugin is added, you need a way to manipulate its controls, which affect its behaviour and operate on the sound. MusE can show a generic GUI which contains all of the plugin's controls arranged in a rather plain generic fashion. Some plugins may also have a native GUI which looks much better (it was specifically designed for the plugin). Both GUI types are opened from the effects rack right-click popup menu. \section{Automation} \label{automation} Automation is the ability to record (or construct) and playback exact sequences of control movements. MIDI and audio automation are each currently uniquely different, but share some similarities. \subsection{Audio automation} \label{audio_automation} Almost all graphical audio controls in MusE can be automated. This includes an audio track's volume and pan, and the controls of any plugins in the effects rack, and if the track is a synthesizer track, all of the synth's controls. Each control has a manual adjustment value. This value is shown when there is no automation data at all, or automation has been disabled. For plugin and synth controls, it is usually more desirable to manipulate automation with the generic plugin GUIs, because MusE has full control over their behaviour. (See plugin GUIs \ref{plugin_guis}). There are a few ways to enter audio automation data: \begin{itemize} \item By adjusting audio controls while the transport is rolling. MusE will record the exact movements. \item By adjusting audio controls while the transport is stopped, at different transport positions. TOUCH mode allows this. \item By right-clicking any audio control and choosing an operation from the automation popup menu. This includes storing, erasing, and clearing automation events, and seeking the next or previous event. \item By drawing the data on the audio track's automation graphs. (See track automation \ref{track_attr_automation}). \end{itemize} \paragraph{Audio automation modes} Each audio track strip has an automation mode button at the bottom. There are four automation modes: \begin{description} \item [{OFF:}] Disables all automation, uses manual value always. \item [{READ:}] Automation data is applied to controls. If any automation data exists, the manual value is overridden and has no effect. \item [{TOUCH:}] Allows you to alter a control at any time, while transport is stopped or rolling, If rolling, when the control is released it returns to reading from automation data. \item [{WRITE:}] Allows to adjust an initial value before rolling the transport. While rolling, when the control is released it does not return to reading from automation data. \end{description} Here is a screenshot of automation WRITE mode, and some automation data, with the track pane automation popup menu showing (see track automation \ref{track_attr_automation}): \begin{figure}[htp] \centering \includegraphics[width=\screenshotwidth] {pics/main_window_with_automation} \caption{Audio automation graphs} \label{fig:audio_automation} \end{figure} \label{midi_automation} \subsection{Midi automation} MIDI automation is a slightly different concept: Unlike audio automation, currently there is no automation 'mode' and it doesn't record graphical control movements. Data is viewed from within the pianoroll and drum editors, by clicking on the 'Ctrl' button %% FIXME Ref to pianoroll on those canvases. Similar to audio controls, each midi control has a manual adjustment value. This value is overridden when there is midi automation data. There are a few ways to enter MIDI automation data: \begin{itemize} \item By adjusting external MIDI controls (such as a midi keyboard pitch or modulation wheel) while the transport is rolling and both the transport and midi track are in record mode. MusE will record the exact movements. As mentioned earlier, note that graphical control movements are not recorded. %% FIXME Feature requests for true midi automation \item By right-clicking any midi control and choosing an operation from the automation popup menu. This includes storing and erasing automation events. %% FIXME Store/erase not enough functionality \item By adjusting volume, pan, bank or program boxes in the midi trackinfo panel and clicking the corresponding volume, pan, or program buttons. (See midi trackinfo \ref{midi_trackinfo_sidebar}). \item By drawing the data on a midi part's automation graphs. \end{itemize} Here is a screen shot of a midi track, containing a midi part which has been opened with the pianoroll editor and automation %% FIXME Ref to pianoroll data showing. The 'Ctrl' popup menu (bottom left) shows available midi controllers and the green dot indicates there is some data. \begin{figure}[htp] \centering \includegraphics[width=\screenshotwidth] {pics/main_window_with_midi_automation} \caption{MIDI automation graphs} \label{fig:midi_automation} \end{figure} \section{Configuration} \subsection{MIDI ports} MIDI ports provide an abstraction layer for your MIDI hardware and synthesizers (which can be both software and hardware synthesizers), and other MIDI applications. Port are numbered. In order to produce sound, each MIDI track is assigned to exactly one MIDI port, to which the MIDI events are then sent. The advantage of this abstraction layer is that if your system changes, for example you change MIDI hardware, then you need only modify the ports instead of all the tracks using those ports. This is similar to the audio input and output track abstraction to the outside world. \label{midi_port_config} \paragraph{MIDI port configuration} In the midi/softsynth configuration menu, you must map the port numbers to the actual devices (by selecting ALSA or jack midi ports, or synth plugins). Try left-clicking on the "Ports" column of some MIDI track. If you use a soft synth, right-clicking the Ports column of the synth or any track using the synth lets you launch the synth's GUI. \begin{figure}[htp] \centering \includegraphics[width=\screenshotwidth] {pics/midi_config_window} \caption{Midi configuration window} \label{fig:midi_config_window} \end{figure} \paragraph{Columns in the MIDI configuration ports list:} \begin{description} \item [{GUI:}] For synthesizer devices, indicates if a gui is available and if it is showing. Click to show. \item [{I:}] If present, the port can accept MIDI input. Click to enable or disable it. \item [{O:}] If present, the port can send MIDI output. Click to enable or disable it. \item [{Instrument:}] Selects the instrument to be used when MIDI is played through the port. \item [{Device name:}] Selects or creates a MIDI device assigned to the port. These can be Jack MIDI devices or ALSA MIDI devices (if ALSA is enabled), or soft synthesizers. Jack MIDI devices are created by selecting Create Jack Device from the Device name drop-down menu. Jack MIDI devices can be renamed as you wish by clicking the device name. Soft synthesizers are created by clicking in the soft synthesizer list and then Add Instance. Or you can simply create a new synthesizer track from the arranger track list, or even the mixer menus. \item [{In and Out routes:}] These are for Jack MIDI devices, they are the routes to and from available Jack MIDI ports. Jack may provide different alias names for these ports, you can select which alias is shown. \item [{Default in channels:}] Auto-connect these port channels to new midi or drum tracks. \item [{Default out channel:}] Auto-connect new midi or drum tracks to this channel on the port. \item [{State:}] Indicates the state of the port including any errors opening it. \end{description} \subsection{Global settings} \subsubsection{Audio settings} \paragraph{Minimum control period} Plugins can usually process an arbitrarily small (or large) amount of samples. If some plugin control value changes continuously, to provide ideal listening experience, MusE would need to call the plugin 44100 times a second, asking for one single value at a time. With the minimum control period setting, the user can force MusE to ask the plugin for at least N values. Setting this value to 64 would in this situation make MusE call the plugin $689=\frac{44100}{64})$ times a second, asking for 64 values at a time. While doing this will reduce accuracy of control changes, it may also reduce CPU usage, because calling the plugin more often, requesting smaller chunks, is more expensive than calling it seldomly, requesting larger chunks. \subparagraph{Recommendation} If you have no performance problems, or if you want to do the final downmix of your project, set this to a low value. If you're experiencing performance problems, increasing this value might help. \chapter{Appendix} \label{apx_effects_rack} \section{Understanding the effects rack} One must carefully consider how many audio inputs and outputs a plugin has, and how may channels the particular audio track has (1 mono or 2 stereo), and how MusE uses the plugins in the rack. MusE will try to internally create as many independent copies (instances) of a plugin as necessary, to satisfy the number of channels in the audio track. Basically it divides the number of track channels by the number of plugin audio inputs or outputs to determine how many copies to make. First it examines the number of plugin audio outputs, and if there are none, it will examine the number of audio inputs, and if there are none, it will simply use just one plugin copy. For mono tracks with plugins having more than one audio input or output, MusE uses the first input or output and ignores the rest. For stereo tracks: \begin{tabular}{|c|c|c|c|c|} \hline plugin inputs & outputs & copies & track in route channels & track out route channels\\ \hline \hline 0 & 0 & 1 & 0 & 0\\ \hline 0 & 1 & 2 & 0 & 2\\ \hline 0 & >=2 & 1 & 0 & 2\\ \hline 1 & 0 & 2 & 2 & 0\\ \hline 1 & 1 & 2 & 2 & 2\\ \hline 1 & >=2 & 1 & 1 (L only) & 2\\ \hline >=2 & 0 & 1 & 2 & 0\\ \hline >=2 & 1 & 2 & 2 & 2\\ \hline >=2 & >=2 & 1 & 2 & 2\\ \hline \end{tabular} Notice that on a stereo track with a plugin having one audio input and two audio outputs, only the first track input route channel is used (left only). These same rules apply to inter-plugin audio when more than one plugin is in the rack chain. Extra audio outputs of one plugin may be ignored by the next plugin if not used. Currently specialized plugins with many inputs and/or outputs are not really useful in MusE. Nor are so-called 'realtime' control plugins which use audio inputs and outputs for control signals. Loud noise alert! Beware of using such plugins in an audio effects rack. Example: Consider a stereo Audio Input track with these effect rack LADSPA plugins: \begin{itemize} \item comb\_splitter Comb Splitter by Steve Harris \item tap\_stereo\_echo Tap Stereo Echo by Tom Szilagyi \end{itemize} The Comb Splitter has one audio input and two audio outputs. The Stereo Echo has two audio inputs and two audio outputs. The stereo Audio Input track will therefore ignore its second input route connection. It will process the left input only, separating it into stereo with the Comb Splitter, passing the split stereo signal into the Stereo Echo, finally producing stereo output available at the Audio Input track's output routes. One improvement would be not creating unused redundant plugin copies between plugins in stereo tracks. For example, for a plugin having one audio input and one audio output, feeding a plugin having one audio input and two audio outputs, the extra copy of the first plugin is redundant and not required, but currently it is created anyway. \end{document} muse-4.0.0/src/doc/html/000077500000000000000000000000001404105311000147515ustar00rootroot00000000000000muse-4.0.0/src/doc/html/single/000077500000000000000000000000001404105311000162325ustar00rootroot00000000000000muse-4.0.0/src/doc/html/single/developer_docs/000077500000000000000000000000001404105311000212275ustar00rootroot00000000000000muse-4.0.0/src/doc/html/single/developer_docs/developer_docs.css000066400000000000000000000017551404105311000247460ustar00rootroot00000000000000/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ .MATH { font-family: "Century Schoolbook", serif; } .MATH I { font-family: "Century Schoolbook", serif; font-style: italic } .BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold } /* implement both fixed-size and relative sizes */ SMALL.XTINY { font-size : xx-small } SMALL.TINY { font-size : x-small } SMALL.SCRIPTSIZE { font-size : smaller } SMALL.FOOTNOTESIZE { font-size : small } SMALL.SMALL { } BIG.LARGE { } BIG.XLARGE { font-size : large } BIG.XXLARGE { font-size : x-large } BIG.HUGE { font-size : larger } BIG.XHUGE { font-size : xx-large } /* heading styles */ H1 { } H2 { } H3 { } H4 { } H5 { } /* mathematics styles */ DIV.displaymath { } /* math displays */ TD.eqno { } /* equation-number cells */ /* document-specific styles come next */ DIV.navigation { } SPAN.textit { font-style: italic } SPAN.arabic { } SPAN.textbf { font-weight: bold } muse-4.0.0/src/doc/html/single/developer_docs/developer_docs.html000066400000000000000000001507331404105311000251230ustar00rootroot00000000000000 MusE Documentation

Contents

Internals - how it works

This chapter explains how MusE is built internally, and is meant to be an aid for developers wanting to quickly start up with MusE. For details on why stuff is done please refer to the following chapter.

User interface programming

We use the QT Toolkit for GUI- and other programming. The QT-Assistant is an important tool for getting help. Almost everything can be looked up there.

GUIs can be either be hardcoded (see fterfirsta`a=95 _ `a=58 : `a>64 `a<91 aaa <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rranger.cpp for an example) or can be created using QT-Designer (see the dialogs under fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>idgets/function_dialogs/ for mostly cleanly-written examples). Don't forget to add your fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>pp, fterfirsth`h=95 _ `h=58 : `h>64 `h<91 hhh <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>> and fterfirstu`u=95 _ `u=58 : `u>64 `u<91 uuu <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>i files to the corresponding sections in the fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>MakeLists.txt!

Additionally, MusE offers some custom widgets, like menu title items etc. Following, there will be a small, unordered list about custom widgets:

  • fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::MenuTitleItem: Provides a title-bar in a fterfirstQ`Q=95 _ `Q=58 : `Q>64 `Q<91 QQQ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>Menu.
    Usage: fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>omeMenu->addAction(new MusEGui::MenuTitleItem(tr("fnord"), someMenu));
    Defined in fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>idgets/menutitleitem.h.
  • fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::PopupMenu: Provides a fterfirstQ`Q=95 _ `Q=58 : `Q>64 `Q<91 QQQ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>Menu-like menu which can stay open after the user checks a checkable action.
    Usage: just create a fterfirstn`n=95 _ `n=58 : `n>64 `n<91 nnn <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ew PopupMenu( true|false ) instead of a fterfirstn`n=95 _ `n=58 : `n>64 `n<91 nnn <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ew QMenu(). (fterfirstt`t=95 _ `t=58 : `t>64 `t<91 ttt <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rue means 'stay open')
    Defined in fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>idgets/popupmenu.h.


Configuration

Configuration is a bit pesky in MusE in its current state. If you get confused by reading this chapter, that's a sign of a sane mind.

There are three kinds of configuration items:

  • (1) Global configuration, like coloring schemes, plugin categories, MDI-ness settings
  • (2) Per-Song configuration, like whether to show or hide certain track types in the arranger
  • (3) Something in between, like MIDI port settings etc. They obviously actually are global configuration issues (or ought to be), but also obviously must be stored in the song file for portability. (This problem could possibly be solved by the feature proposal in 3.7.

Reading configuration

fterfirstv`v=95 _ `v=58 : `v>64 `v<91 vvv <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>oid MusECore::readConfiguration(Xml&, bool, bool) in fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>onf.cpp is the central point of reading configuration. It is called when MusE is started first (by fterfirstb`b=95 _ `b=58 : `b>64 `b<91 bbb <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ool MusECore::readConfiguration()), and also when a song is loaded.
It can be instructed whether to read MIDI ports (3), global configuration and MIDI ports (1+3). Per-Song configuration is always read (2).

When adding new configuration items and thus altering fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eadConfiguration(), you must take care to place your item into the correct section. The code is divided into the following sections:

  • Global and/or per-song configuration (3)
  • Global configuration (1)
  • Code for skipping obsolete entries

The sections are divided by comments (they contain --, so just search for them). Please do not just remove code for reading obsolete entries, but always add an appropriate entry to the 'skipping' section in order to prevent error messages when reading old configs.

Writing configuration

Global configuration is written using the fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::MusE::writeGlobalConfiguration() functions, while per-song-config is written by fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::MusE::writeConfiguration() (notice the missing fterfirstG`G=95 _ `G=58 : `G>64 `G<91 GGG <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>lobal; both implemented in fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>onf.cpp).

fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteConfiguration is actually just a subset of the code in fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteGlobalConfiguration. Duplicate code!

Song state

Additionally to per-song configuration, there is the song's state. This contains "the song", that is all tracks, parts and note events, together with information about the currently opened windows, their position, size, settings and so on. Adding new items here is actually pretty painless: Configuration is read and written using fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usECore::Song::read and fterfirst:`:=95 _ `:=58 : `:>64 `:<91 ::: <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>:write, both implemented in fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ongfile.cpp. There are no caveats.

How to add new items

When adding global configuration items, then add them into the second block ("global configuration") in fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eadConfiguration and into fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteGlobalConfiguration.

When adding just-per-song items, better don't bother to touch the "configuration" code and just add it to the song's state (there might be rare exceptions).

When adding global configuration items, make sure you add them into the correct section of fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eadConfiguration, and into fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteGlobalConfiguration.

User controls and automation

Handling user input

Plugins and synthesizers

Overview

When the user launches a plugin's GUI, either a MusE-window with the relevant controls is shown, or the native GUI is launched. MusE will communicate with this native GUI through OSC (Open Sound Control). The relevant classes are fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginGui, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginIBase (in fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>lugin.h) and fterfirstO`O=95 _ `O=58 : `O>64 `O<91 OOO <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>scIF (in fterfirsto`o=95 _ `o=58 : `o>64 `o<91 ooo <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>sc.h).

If the user changes a GUI element, first the corresponding control is disabled, making MusE not steadily update it through automation while the user operates it. Then MusE will update the plugin's parameter value, and also record the new value. When appropriate, the controller is enabled again.

Processing the input, recording

Upon operating a slider, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginIBase::setParam is called, which usually writes the control change into the ringbuffer fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo. (fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::apply(), fterfirstD`D=95 _ `D=58 : `D>64 `D<91 DDD <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ssiSynthIF::getData() will read this ringbuffer and do the processing accordingly). Furthermore, fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::recordAutomation is called, which either directly modifies the controller lists or writes the change into a "to be recorded"-list (fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_recEvents) (depending on whether the song is stopped or played).

The fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_recEvents list consists of fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlRecVal items (see fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trl.h), which hold the following data:

  • the frame where the change occurred
  • the value
  • the type, which can be fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START, fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_VAL or fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP. fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_VAL are written by every fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>udioTrack::recordAutomation call, fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START and fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP are generated by fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::startAutoRecord and fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>topAutoRecord, respectively.
  • and the id of the controller which is affected
It is processed when the song is stopped. The call path for this is: fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::stopRolling calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::processAutomationEvents calls fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::processAutomationEvents. This function removes the old events from the track's controller list and replaces them with the new events from fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>recEvents. In fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE mode, just all controller events within the recorded range are erased; in fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH mode, the fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START and fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP types of the fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlRecVal events are used to determine the range(s) which should be wiped.

How it's stored

Automation data is kept in fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_controller, which is a fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlListList, that is, a list of fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlLists, that is, a list of lists of controller-objects which hold the control points of the automation graph. The fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList also stores whether the list is meant discrete (a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value). Furthermore, it stores a fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>curVal (accessed by fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>urVal()), which holds the currently active value, which can be different from the actually stored value because of user interaction. This value is also used when there is no stored automation data.

fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::addController and fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>emoveController are used to add/remove whole controller types; the most important functions which access fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>controller are:

  • fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rocessAutomationEvents, fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ecordAutomation, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>tartAutoRecord, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>topAutoRecord: see above.
  • fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eekPrevACEvent, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eekNextACEvent, fterfirste`e=95 _ `e=58 : `e>64 `e<91 eee <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>raseACEvent, fterfirste`e=95 _ `e=58 : `e>64 `e<91 eee <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>raseRangeACEvents, fterfirsta`a=95 _ `a=58 : `a>64 `a<91 aaa <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ddACEvent, fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>hangeACEvent, which do the obvious
  • fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginCtrlVal, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>etPluginCtrlVal: the first returns the current value according to the fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>controller list, the second only sets the fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>urVal, but does not insert any events.

Whenever a fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList has been manipulated, fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGlobal::song->controllerChange(Track*) shall be called, which emits the fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGlobal::song->controllerChanged(Track*) signal in order to inform any parts of MusE about the change (currently, only the arranger's part canvas utilizes this).

Enabling and disabling controllers

Disabling the controller is both dependent from the current automation mode and from whether the GUI is native or not. In fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE mode, once a slider is touched (for MusE-GUIs) or once a OSC control change is received (for native GUIs), the control is disabled until the song is stopped or seeked.

In fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH (and currently (r1492) fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_READ, but that's to be fixed) mode, once a MusE-GUI's slider is pressed down, the corresponding control is disabled. Once the slider is released, the control is re-enabled again. Checkboxes remain in "disabled" mode, however they only affect the recorded automation until the last toggle of the checkbox. (Example: start the song, toggle the checkbox, toggle it again, wait 10 seconds, stop the song. This will NOT overwrite the last 10 seconds of automation data, but everything between the first and the last toggle.). For native GUIs, this is a bit tricky, because we don't have direct access to the GUI widgets. That is, we have no way to find out whether the user doesn't touch a control at all, or whether he has it held down, but just doesn't operate it. The current behaviour for native GUIs is to behave like in fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE mode.

The responsible functions are: fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::oscControl and fterfirstD`D=95 _ `D=58 : `D>64 `D<91 DDD <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ssiSynthIF::oscControl for handling native GUIs, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::ctrlPressed and fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlReleased for MusE default GUIs and fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::guiParamPressed, fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiParamReleased, fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSliderPressed and fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSliderReleased for MusE GUIs read from a UI file; fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSlider* obviously handle sliders, while fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiParam* handle everything else which is not a slider. They call fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableController to enable/disable it.

Furthermore, on every song stop or seek, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers is called, which re-enables all controllers again. The call paths for this are:

  • For stop: fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::stopRolling calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::processAutomationEvents calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::clearRecAutomation calls fterfirstT`T=95 _ `T=58 : `T>64 `T<91 TTT <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rack::clearRecAutomation calls fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers
  • For seek: fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udio::seek sends a message ("fterfirstG`G=95 _ `G=58 : `G>64 `G<91 GGG <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>") to fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::seqSignal which calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::clearRecAutomation which calls fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers

Design decisions

Automation

As of revision 1490, automation is handled in two ways: User-generated (live) automation data (generated by the user moving sliders while playing) is fed into fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo. Automation data is kept in fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_controller, which is a fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlListList, that is, a list of fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlLists, that is, a list of lists of controller-objects which hold the control points of the automation graph. The fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList also stores whether the list is meant discrete (a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value).

While fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo can be queried very quickly and thus is processed with a very high resolution (only limited by the minimum control period setting), the automation value are expensive to query, and are only processed once in an audio driver period. This might lead to noticeable jumps in value.

This could possibly be solved in two ways:

Maintaining a slave control list

This approach would maintain a fully redundant slave control list, similar to fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo. This list must be updated every time any automation-related thing is changed, and shall contain every controller change as a tuple of controller number and value. This could be processed in the same loop as fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo, making it comfortable to implement; furthermore, it allows to cleanly offer automation-settings at other places in future (such as storing automation data in parts or similar).

Holding iterators

We also could hold a list of iterators of the single fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlLists. This would also cause low CPU usage, because usually, the iterators only need to be incremented once. However, it is pretty complex to implement, because the iterators may become totally wrong (because of a seek in the song), and we must iterate through a whole list of iterators.

Just use the current data access functions

By just using the current functions for accessing automation data, we might get a quick-and-dirty solution, which however wastes way too much CPU resources. This is because on every single frame, we need to do a binary search on multiple controller lists.

Feature requests

Per-Part automation and more on automation

Automation shall be undo-able. Automation shall reside in parts which are exchangeable, clonable etc (like the MIDI- and Wave-Parts). Global per-synth/per-audiotrack automation shall also be available, but this can also be implemented as special case of part automation (one long part).

Pre-Rendering tracks

The feature

All tracks shall be able to be "pre-renderable". Pre-rendering shall be "layered". Pre-rendering shall act like a transparent audio cache: Audio data is (redundantly) stored, wasting memory in order to save CPU.

That is: Each track owns one or more wave-recordings of the length of the song. If the user calls "pre-render" on a track, then this track is played quasi-solo (see below), and the raw audio data is recorded and stored in the "layer 0" wave recording. If the user has any effects set up to be applied, then each effect is applied on a different layer (creating layer 1, layer 2 etc).

This means, that also MIDI and drum tracks can have effects (which usually only operate on audio, but we HAVE audio data because of this prerendering).

Furthermore, MusE by default does not send MIDI events to the synthesizers but instead just plays back the last layer of the prerecording (for MIDI tracks), or does not pipe the audio data through the whole plugin chain (causing cpu usage), but instead just plays back the last layer. The hearable result shall be the same.

Once the user changes any parameter (automation data or plugins for wave tracks, MIDI events or effect plugin stuff for MIDI tracks), then MusE shall generate the sound for this particular track in the "old" way (send MIDI data to synthes, or pipe audio data through plugins). (So that the user will not even notice that MusE actually pre-renderered stuff.) Either MusE automatically records this while playback (if possible) or prompts the user to accordingly set up his cabling and then record it. Or (temporarily) disables prerecording for this track, falling back to the plain old way of generating sound.

Quasi-solo means: For wave tracks, just solo the track. For MIDI tracks, mute all tracks which are not on the same synth (channel?), and mute all note events which are not on the quasi-soloed track. This causes MusE to still play any controller events from different tracks, because they might have effects on the quasi-soloed track. (You can have notes on channel 1 on one track and controller stuff on channel 1 on another track; then you would need quasi-solo to get proper results.)

Use cases

Saving CPU

On slow systems, this is necessary for songs with lots of, or demanding (or both) soft synthes / plugins. Even if the synth or plugin is so demanding that your system is not able to produce sound in real-time, then with this feature you'll be able to use the synth (this will make editing pretty laggish, because for a change you need to re-render at least a part before you can listen to it, but better than being unable to use the synth at all!)

Exporting as audio project

Using pre-rendering on all tracks, you easily can export your project as multi-track audio file (for use with Ardour or similar DAWs). Just take the last layer of each track, and write the raw audio data into the file, and you're done. (Maybe we are even able to write down the raw-raw layer0 audio data plus information about used plugins and settings etc..?)

Mobile audio workstations

You might want to work a bit on your audio projects on your notebook while you're not at home, not having access to your hardware synthesizers. Using this feature, you could have pre-recorded the stuff in your studio before, and now can at least fiddle around with the non-hw-synth-dependent parts of your song, while still having your full song with you.

Applying effects on MIDI tracks

If you have many physical audio inputs, you might already be able to apply effect chains on MIDI tracks, by wiring the synthes' audio outputs to your soundcard's inputs, and applying the effects on dedicated input tracks you have to create. This requires you to have expensive hardware, and is pretty complicated, because you need one additional track per MIDI synth.

This feature allows you to apply effects on single MIDI tracks, and not only on full MIDI synthes, and doesn't require you to be have that many physical audio inputs (you need to manually replug your synthes, however).

Possible scenarios

Setting it up

Create a wave track, MusE will allow you to set or unset prerendering for every plugin in the plugin rack (recording the actual track is useless because it would be a plain copy). Create a MIDI track, MusE will ask you on which physical audio input your synth is connected. Setting up multiple synthes on one physical audio in is allowed, see below.

Pre-rendering stuff

When the user presses the "pre-render" button, all tracks which have been changed since their last pre-rendering will be re-rendered. If you have multiple hardware synthes set up as they were connected to one physical audio input port, MusE will prompt you to first plug the proper cable in.

Making changes

Change a note in a MIDI part, move or delete a part or change automation parameters. MusE will temporarily disable the pre-rendered information and instead generate the sound via sending out MIDI events, piping stuff through effect chains or similar. If you play back the whole song, or if you manually trigger a re-rendering of a track via the context menu, MusE will play back the stuff, record it again and re-enable the pre-rendered information.

Extensions

Automatic discovery of physical audio connections

The user plugs all (or only some) synthes' audio outs into the available audio inputs, then runs automatic discovery. This will send MIDI events to each synthesizer, and look on which audio in there's activity. Then it will assume that the synthesizer is connected to that particular audio in. Audio ins which show activity before any MIDI events were sent are not considered, as they're probably connected to microphones or other noise-generating non-synthes.

Audio export

As described in the Use cases, MusE can allow you to export your song in some multitrack audio format.

Cheap/Faked changes

For expensive or unavailable synthes, changing the Volume midi controller, the Pan controller or similar "easy" controllers will not trigger a complete re-rendering, but instead "fake" the change, by changing the volume data directly on the recorded wave. This might require some learning and might even get pretty complicated.

Intelligent re-recording

For tiny changes, MusE shall only re-render the relevant part. If you change some MIDI notes, then begin re-recording shortly before the changes, and end re-recording as soon as the recorded stuff doesn't differ to much from the stuff coming from the synth. Then properly blend the old recording with the updated part.

Slotted editors

Currently, MusE has the pianoroll editor, drum editor, score editor, then the controller editor which is inside the pianoroll/drum editor. All these editors have a very similar concept: the "time axis" is vertical and (almost) linear, they handle parts, and events are manipulated similarly.

A unified editor shall be created which allows you to combine different kinds of editors in one window, properly aligned against each other. These "different kinds of editors" shall be handled as "slots"; one unified editor window consists of:

  • A menu bar, containing stuff suitable for the complete window, which might include window name, MDI-ness etc.
  • A toolbar which contains controls suitable for every single slot.
  • A container with one or more slots; the slots can be scrolled in y-direction if there are multiple slots.
  • A time-scrollbar with zoom

Each slot contains the following:

  • A menu button, button box or control panel for setting up this particular slot. This could contain "note head colors", "show a transposing instrument" etc for score edit slots, "event rectangle color", "grid size" and "snap to grid" for pianoroll/ drum editors.
  • The actual canvas
  • A y-direction scroll bar, possibly with zoom control (for pianoroll editor)

The main window does not show its scroll bar if there is only one slot, because the slot's scrollbar is sufficient then.

Slots can be added, destroyed, moved around, maybe even merged (if the slot types allow it); basically, you can compare them with the staves in the score editor.

The slots shall align against each other, that is, if a score editor slot displays a key change with lots of accidentials, then all other slots shall either also display the key change (if they're score slots) or display a gap. Events which happen at the same time shall be at the same x-coordinate, regardless which slot they are.

Controller master values

All controllers (MIDI-controllers and also automation controllers) shall have one set of "master values" which allow you to set a gain and a bias. Instead of the actual set value, $\textrm{value} * \textrm{bias}
+ textrm{bias}$ shall be sent to the MIDI device / the plugin. For controllers like "pan", the unbiased values shall be transformed, that is, a pan of 64, with $\textrm{bias}=2$ and $\textrm{gain}=0.5$, shall be transformed to 66 (because 64 is actually 0, while 0 is actually -64). These values shall be set in the arranger and wherever the actual controller/automation values can be edited.

Enabled-indicator while recording

The MusE-plugin-GUIs shall display a small LED displaying whether a controller is currently enabled or disabled. By clicking this LED, the enabled state shall be switched.

Furthermore, there shall be a dedicated window which only lets you switch enabled/disabled states. This will be useful when using external GUIs or the MIDI-controller-to-automation feature, to re-enable a controller when in fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH mode.

Linear automation editing

While holding some modifier key (like shift), operating the MusE-native- GUI sliders shall only generate control points when clicking and when releasing the slider. This will result in linear graphs for continuous controllers, and in large steps for discrete controllers (which is in particular useful for stuff like "which low/high-pass filter type to use").

Maybe make this behaviour default for discrete controllers?


Symbolic names for MIDI ports

MIDI ports shall have a user-defined symbolic name (like "Korg" or "Yamaha DX 7"). The mapping between these symbolic names and the hardware port (like "ALSA midi out port") is stored in the global configuration.

Song files only specify the symbolic names as the ports associated with their tracks. No information about physical devices/port names, but only symbolic names are stored in the song file.

This resolves the issues mentioned in 1.2, and also allows the user to share his pieces with other people: They would only have to set up that symbolic-to-hardware mapping once (collisions are unlikely, because an equal symbolic name should usually mean the same device) and are happy, instead of having to re-map every port for every song.



muse-4.0.0/src/doc/html/single/developer_docs/img1.png000066400000000000000000000015251404105311000225750ustar00rootroot00000000000000PNG  IHDRc0PLTEgggMMM''' tttZZZ@@@444lܬ]tRNS@fIDATHVOHQ8oa E\Nx!bCۣ]!::Ț,y .ʐ7 JСof[aBV{} C-峩N+i cn1^zGDͷ9IeObm>|ԌزÎc(c]RT@@$pbj6.bpo`üMY٣ɴ2bw|iѢt^']B۩W\5%0jG U=&C@hit)# Hy=&+?0,d!qV].!qp84ٺFyIENDB`muse-4.0.0/src/doc/html/single/developer_docs/img2.png000066400000000000000000000005661404105311000226020ustar00rootroot00000000000000PNG  IHDR=0PLTEgggMMM''' tttZZZ@@@444lܬ]tRNS@fIDATc`ff~0>u p6 C0)(} pNFFc@k (r$005xP@ (',5 |'9@$A~3` $߉)8X>z8Ez.!) &ƠoālTm6 gx+6ÂXۃM6NgDf,Kc`+;,IENDB`muse-4.0.0/src/doc/html/single/developer_docs/img3.png000066400000000000000000000007341404105311000226000ustar00rootroot00000000000000PNG  IHDRK$ 0PLTEgggMMM''' tttZZZ@@@444lܬ]tRNS@fZIDAT8Ւ1K@9嚤鵓PETWDp~An".ե BQ2 7/ ^M{q)'<{w^88ּpPؚ`\/[e )6eYUC*W,^ 8-= oTq,\y!t*Y[⌬NaҨqbfߛadQ27QsɤY>Q5kuŮ(e ʁeET6, <\Efl} >5$܇Ի d3 S ~Jz^zD(i&q8_/r8<` LIENDB`muse-4.0.0/src/doc/html/single/developer_docs/index.html000066400000000000000000001507331404105311000232350ustar00rootroot00000000000000 MusE Documentation

Contents

Internals - how it works

This chapter explains how MusE is built internally, and is meant to be an aid for developers wanting to quickly start up with MusE. For details on why stuff is done please refer to the following chapter.

User interface programming

We use the QT Toolkit for GUI- and other programming. The QT-Assistant is an important tool for getting help. Almost everything can be looked up there.

GUIs can be either be hardcoded (see fterfirsta`a=95 _ `a=58 : `a>64 `a<91 aaa <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rranger.cpp for an example) or can be created using QT-Designer (see the dialogs under fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>idgets/function_dialogs/ for mostly cleanly-written examples). Don't forget to add your fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>pp, fterfirsth`h=95 _ `h=58 : `h>64 `h<91 hhh <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>> and fterfirstu`u=95 _ `u=58 : `u>64 `u<91 uuu <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>i files to the corresponding sections in the fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>MakeLists.txt!

Additionally, MusE offers some custom widgets, like menu title items etc. Following, there will be a small, unordered list about custom widgets:

  • fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::MenuTitleItem: Provides a title-bar in a fterfirstQ`Q=95 _ `Q=58 : `Q>64 `Q<91 QQQ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>Menu.
    Usage: fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>omeMenu->addAction(new MusEGui::MenuTitleItem(tr("fnord"), someMenu));
    Defined in fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>idgets/menutitleitem.h.
  • fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::PopupMenu: Provides a fterfirstQ`Q=95 _ `Q=58 : `Q>64 `Q<91 QQQ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>Menu-like menu which can stay open after the user checks a checkable action.
    Usage: just create a fterfirstn`n=95 _ `n=58 : `n>64 `n<91 nnn <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ew PopupMenu( true|false ) instead of a fterfirstn`n=95 _ `n=58 : `n>64 `n<91 nnn <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ew QMenu(). (fterfirstt`t=95 _ `t=58 : `t>64 `t<91 ttt <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rue means 'stay open')
    Defined in fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>idgets/popupmenu.h.


Configuration

Configuration is a bit pesky in MusE in its current state. If you get confused by reading this chapter, that's a sign of a sane mind.

There are three kinds of configuration items:

  • (1) Global configuration, like coloring schemes, plugin categories, MDI-ness settings
  • (2) Per-Song configuration, like whether to show or hide certain track types in the arranger
  • (3) Something in between, like MIDI port settings etc. They obviously actually are global configuration issues (or ought to be), but also obviously must be stored in the song file for portability. (This problem could possibly be solved by the feature proposal in 3.7.

Reading configuration

fterfirstv`v=95 _ `v=58 : `v>64 `v<91 vvv <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>oid MusECore::readConfiguration(Xml&, bool, bool) in fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>onf.cpp is the central point of reading configuration. It is called when MusE is started first (by fterfirstb`b=95 _ `b=58 : `b>64 `b<91 bbb <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ool MusECore::readConfiguration()), and also when a song is loaded.
It can be instructed whether to read MIDI ports (3), global configuration and MIDI ports (1+3). Per-Song configuration is always read (2).

When adding new configuration items and thus altering fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eadConfiguration(), you must take care to place your item into the correct section. The code is divided into the following sections:

  • Global and/or per-song configuration (3)
  • Global configuration (1)
  • Code for skipping obsolete entries

The sections are divided by comments (they contain --, so just search for them). Please do not just remove code for reading obsolete entries, but always add an appropriate entry to the 'skipping' section in order to prevent error messages when reading old configs.

Writing configuration

Global configuration is written using the fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::MusE::writeGlobalConfiguration() functions, while per-song-config is written by fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::MusE::writeConfiguration() (notice the missing fterfirstG`G=95 _ `G=58 : `G>64 `G<91 GGG <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>lobal; both implemented in fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>onf.cpp).

fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteConfiguration is actually just a subset of the code in fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteGlobalConfiguration. Duplicate code!

Song state

Additionally to per-song configuration, there is the song's state. This contains "the song", that is all tracks, parts and note events, together with information about the currently opened windows, their position, size, settings and so on. Adding new items here is actually pretty painless: Configuration is read and written using fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usECore::Song::read and fterfirst:`:=95 _ `:=58 : `:>64 `:<91 ::: <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>:write, both implemented in fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ongfile.cpp. There are no caveats.

How to add new items

When adding global configuration items, then add them into the second block ("global configuration") in fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eadConfiguration and into fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteGlobalConfiguration.

When adding just-per-song items, better don't bother to touch the "configuration" code and just add it to the song's state (there might be rare exceptions).

When adding global configuration items, make sure you add them into the correct section of fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eadConfiguration, and into fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteGlobalConfiguration.

User controls and automation

Handling user input

Plugins and synthesizers

Overview

When the user launches a plugin's GUI, either a MusE-window with the relevant controls is shown, or the native GUI is launched. MusE will communicate with this native GUI through OSC (Open Sound Control). The relevant classes are fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginGui, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginIBase (in fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>lugin.h) and fterfirstO`O=95 _ `O=58 : `O>64 `O<91 OOO <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>scIF (in fterfirsto`o=95 _ `o=58 : `o>64 `o<91 ooo <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>sc.h).

If the user changes a GUI element, first the corresponding control is disabled, making MusE not steadily update it through automation while the user operates it. Then MusE will update the plugin's parameter value, and also record the new value. When appropriate, the controller is enabled again.

Processing the input, recording

Upon operating a slider, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginIBase::setParam is called, which usually writes the control change into the ringbuffer fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo. (fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::apply(), fterfirstD`D=95 _ `D=58 : `D>64 `D<91 DDD <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ssiSynthIF::getData() will read this ringbuffer and do the processing accordingly). Furthermore, fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::recordAutomation is called, which either directly modifies the controller lists or writes the change into a "to be recorded"-list (fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_recEvents) (depending on whether the song is stopped or played).

The fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_recEvents list consists of fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlRecVal items (see fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trl.h), which hold the following data:

  • the frame where the change occurred
  • the value
  • the type, which can be fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START, fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_VAL or fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP. fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_VAL are written by every fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>udioTrack::recordAutomation call, fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START and fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP are generated by fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::startAutoRecord and fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>topAutoRecord, respectively.
  • and the id of the controller which is affected
It is processed when the song is stopped. The call path for this is: fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::stopRolling calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::processAutomationEvents calls fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::processAutomationEvents. This function removes the old events from the track's controller list and replaces them with the new events from fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>recEvents. In fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE mode, just all controller events within the recorded range are erased; in fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH mode, the fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START and fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP types of the fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlRecVal events are used to determine the range(s) which should be wiped.

How it's stored

Automation data is kept in fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_controller, which is a fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlListList, that is, a list of fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlLists, that is, a list of lists of controller-objects which hold the control points of the automation graph. The fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList also stores whether the list is meant discrete (a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value). Furthermore, it stores a fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>curVal (accessed by fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>urVal()), which holds the currently active value, which can be different from the actually stored value because of user interaction. This value is also used when there is no stored automation data.

fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::addController and fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>emoveController are used to add/remove whole controller types; the most important functions which access fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>controller are:

  • fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rocessAutomationEvents, fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ecordAutomation, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>tartAutoRecord, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>topAutoRecord: see above.
  • fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eekPrevACEvent, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eekNextACEvent, fterfirste`e=95 _ `e=58 : `e>64 `e<91 eee <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>raseACEvent, fterfirste`e=95 _ `e=58 : `e>64 `e<91 eee <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>raseRangeACEvents, fterfirsta`a=95 _ `a=58 : `a>64 `a<91 aaa <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ddACEvent, fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>hangeACEvent, which do the obvious
  • fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginCtrlVal, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>etPluginCtrlVal: the first returns the current value according to the fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>controller list, the second only sets the fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>urVal, but does not insert any events.

Whenever a fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList has been manipulated, fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGlobal::song->controllerChange(Track*) shall be called, which emits the fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGlobal::song->controllerChanged(Track*) signal in order to inform any parts of MusE about the change (currently, only the arranger's part canvas utilizes this).

Enabling and disabling controllers

Disabling the controller is both dependent from the current automation mode and from whether the GUI is native or not. In fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE mode, once a slider is touched (for MusE-GUIs) or once a OSC control change is received (for native GUIs), the control is disabled until the song is stopped or seeked.

In fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH (and currently (r1492) fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_READ, but that's to be fixed) mode, once a MusE-GUI's slider is pressed down, the corresponding control is disabled. Once the slider is released, the control is re-enabled again. Checkboxes remain in "disabled" mode, however they only affect the recorded automation until the last toggle of the checkbox. (Example: start the song, toggle the checkbox, toggle it again, wait 10 seconds, stop the song. This will NOT overwrite the last 10 seconds of automation data, but everything between the first and the last toggle.). For native GUIs, this is a bit tricky, because we don't have direct access to the GUI widgets. That is, we have no way to find out whether the user doesn't touch a control at all, or whether he has it held down, but just doesn't operate it. The current behaviour for native GUIs is to behave like in fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE mode.

The responsible functions are: fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::oscControl and fterfirstD`D=95 _ `D=58 : `D>64 `D<91 DDD <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ssiSynthIF::oscControl for handling native GUIs, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::ctrlPressed and fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlReleased for MusE default GUIs and fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::guiParamPressed, fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiParamReleased, fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSliderPressed and fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSliderReleased for MusE GUIs read from a UI file; fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSlider* obviously handle sliders, while fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiParam* handle everything else which is not a slider. They call fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableController to enable/disable it.

Furthermore, on every song stop or seek, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers is called, which re-enables all controllers again. The call paths for this are:

  • For stop: fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::stopRolling calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::processAutomationEvents calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::clearRecAutomation calls fterfirstT`T=95 _ `T=58 : `T>64 `T<91 TTT <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rack::clearRecAutomation calls fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers
  • For seek: fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udio::seek sends a message ("fterfirstG`G=95 _ `G=58 : `G>64 `G<91 GGG <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>") to fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::seqSignal which calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::clearRecAutomation which calls fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers

Design decisions

Automation

As of revision 1490, automation is handled in two ways: User-generated (live) automation data (generated by the user moving sliders while playing) is fed into fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo. Automation data is kept in fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_controller, which is a fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlListList, that is, a list of fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlLists, that is, a list of lists of controller-objects which hold the control points of the automation graph. The fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList also stores whether the list is meant discrete (a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value).

While fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo can be queried very quickly and thus is processed with a very high resolution (only limited by the minimum control period setting), the automation value are expensive to query, and are only processed once in an audio driver period. This might lead to noticeable jumps in value.

This could possibly be solved in two ways:

Maintaining a slave control list

This approach would maintain a fully redundant slave control list, similar to fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo. This list must be updated every time any automation-related thing is changed, and shall contain every controller change as a tuple of controller number and value. This could be processed in the same loop as fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo, making it comfortable to implement; furthermore, it allows to cleanly offer automation-settings at other places in future (such as storing automation data in parts or similar).

Holding iterators

We also could hold a list of iterators of the single fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlLists. This would also cause low CPU usage, because usually, the iterators only need to be incremented once. However, it is pretty complex to implement, because the iterators may become totally wrong (because of a seek in the song), and we must iterate through a whole list of iterators.

Just use the current data access functions

By just using the current functions for accessing automation data, we might get a quick-and-dirty solution, which however wastes way too much CPU resources. This is because on every single frame, we need to do a binary search on multiple controller lists.

Feature requests

Per-Part automation and more on automation

Automation shall be undo-able. Automation shall reside in parts which are exchangeable, clonable etc (like the MIDI- and Wave-Parts). Global per-synth/per-audiotrack automation shall also be available, but this can also be implemented as special case of part automation (one long part).

Pre-Rendering tracks

The feature

All tracks shall be able to be "pre-renderable". Pre-rendering shall be "layered". Pre-rendering shall act like a transparent audio cache: Audio data is (redundantly) stored, wasting memory in order to save CPU.

That is: Each track owns one or more wave-recordings of the length of the song. If the user calls "pre-render" on a track, then this track is played quasi-solo (see below), and the raw audio data is recorded and stored in the "layer 0" wave recording. If the user has any effects set up to be applied, then each effect is applied on a different layer (creating layer 1, layer 2 etc).

This means, that also MIDI and drum tracks can have effects (which usually only operate on audio, but we HAVE audio data because of this prerendering).

Furthermore, MusE by default does not send MIDI events to the synthesizers but instead just plays back the last layer of the prerecording (for MIDI tracks), or does not pipe the audio data through the whole plugin chain (causing cpu usage), but instead just plays back the last layer. The hearable result shall be the same.

Once the user changes any parameter (automation data or plugins for wave tracks, MIDI events or effect plugin stuff for MIDI tracks), then MusE shall generate the sound for this particular track in the "old" way (send MIDI data to synthes, or pipe audio data through plugins). (So that the user will not even notice that MusE actually pre-renderered stuff.) Either MusE automatically records this while playback (if possible) or prompts the user to accordingly set up his cabling and then record it. Or (temporarily) disables prerecording for this track, falling back to the plain old way of generating sound.

Quasi-solo means: For wave tracks, just solo the track. For MIDI tracks, mute all tracks which are not on the same synth (channel?), and mute all note events which are not on the quasi-soloed track. This causes MusE to still play any controller events from different tracks, because they might have effects on the quasi-soloed track. (You can have notes on channel 1 on one track and controller stuff on channel 1 on another track; then you would need quasi-solo to get proper results.)

Use cases

Saving CPU

On slow systems, this is necessary for songs with lots of, or demanding (or both) soft synthes / plugins. Even if the synth or plugin is so demanding that your system is not able to produce sound in real-time, then with this feature you'll be able to use the synth (this will make editing pretty laggish, because for a change you need to re-render at least a part before you can listen to it, but better than being unable to use the synth at all!)

Exporting as audio project

Using pre-rendering on all tracks, you easily can export your project as multi-track audio file (for use with Ardour or similar DAWs). Just take the last layer of each track, and write the raw audio data into the file, and you're done. (Maybe we are even able to write down the raw-raw layer0 audio data plus information about used plugins and settings etc..?)

Mobile audio workstations

You might want to work a bit on your audio projects on your notebook while you're not at home, not having access to your hardware synthesizers. Using this feature, you could have pre-recorded the stuff in your studio before, and now can at least fiddle around with the non-hw-synth-dependent parts of your song, while still having your full song with you.

Applying effects on MIDI tracks

If you have many physical audio inputs, you might already be able to apply effect chains on MIDI tracks, by wiring the synthes' audio outputs to your soundcard's inputs, and applying the effects on dedicated input tracks you have to create. This requires you to have expensive hardware, and is pretty complicated, because you need one additional track per MIDI synth.

This feature allows you to apply effects on single MIDI tracks, and not only on full MIDI synthes, and doesn't require you to be have that many physical audio inputs (you need to manually replug your synthes, however).

Possible scenarios

Setting it up

Create a wave track, MusE will allow you to set or unset prerendering for every plugin in the plugin rack (recording the actual track is useless because it would be a plain copy). Create a MIDI track, MusE will ask you on which physical audio input your synth is connected. Setting up multiple synthes on one physical audio in is allowed, see below.

Pre-rendering stuff

When the user presses the "pre-render" button, all tracks which have been changed since their last pre-rendering will be re-rendered. If you have multiple hardware synthes set up as they were connected to one physical audio input port, MusE will prompt you to first plug the proper cable in.

Making changes

Change a note in a MIDI part, move or delete a part or change automation parameters. MusE will temporarily disable the pre-rendered information and instead generate the sound via sending out MIDI events, piping stuff through effect chains or similar. If you play back the whole song, or if you manually trigger a re-rendering of a track via the context menu, MusE will play back the stuff, record it again and re-enable the pre-rendered information.

Extensions

Automatic discovery of physical audio connections

The user plugs all (or only some) synthes' audio outs into the available audio inputs, then runs automatic discovery. This will send MIDI events to each synthesizer, and look on which audio in there's activity. Then it will assume that the synthesizer is connected to that particular audio in. Audio ins which show activity before any MIDI events were sent are not considered, as they're probably connected to microphones or other noise-generating non-synthes.

Audio export

As described in the Use cases, MusE can allow you to export your song in some multitrack audio format.

Cheap/Faked changes

For expensive or unavailable synthes, changing the Volume midi controller, the Pan controller or similar "easy" controllers will not trigger a complete re-rendering, but instead "fake" the change, by changing the volume data directly on the recorded wave. This might require some learning and might even get pretty complicated.

Intelligent re-recording

For tiny changes, MusE shall only re-render the relevant part. If you change some MIDI notes, then begin re-recording shortly before the changes, and end re-recording as soon as the recorded stuff doesn't differ to much from the stuff coming from the synth. Then properly blend the old recording with the updated part.

Slotted editors

Currently, MusE has the pianoroll editor, drum editor, score editor, then the controller editor which is inside the pianoroll/drum editor. All these editors have a very similar concept: the "time axis" is vertical and (almost) linear, they handle parts, and events are manipulated similarly.

A unified editor shall be created which allows you to combine different kinds of editors in one window, properly aligned against each other. These "different kinds of editors" shall be handled as "slots"; one unified editor window consists of:

  • A menu bar, containing stuff suitable for the complete window, which might include window name, MDI-ness etc.
  • A toolbar which contains controls suitable for every single slot.
  • A container with one or more slots; the slots can be scrolled in y-direction if there are multiple slots.
  • A time-scrollbar with zoom

Each slot contains the following:

  • A menu button, button box or control panel for setting up this particular slot. This could contain "note head colors", "show a transposing instrument" etc for score edit slots, "event rectangle color", "grid size" and "snap to grid" for pianoroll/ drum editors.
  • The actual canvas
  • A y-direction scroll bar, possibly with zoom control (for pianoroll editor)

The main window does not show its scroll bar if there is only one slot, because the slot's scrollbar is sufficient then.

Slots can be added, destroyed, moved around, maybe even merged (if the slot types allow it); basically, you can compare them with the staves in the score editor.

The slots shall align against each other, that is, if a score editor slot displays a key change with lots of accidentials, then all other slots shall either also display the key change (if they're score slots) or display a gap. Events which happen at the same time shall be at the same x-coordinate, regardless which slot they are.

Controller master values

All controllers (MIDI-controllers and also automation controllers) shall have one set of "master values" which allow you to set a gain and a bias. Instead of the actual set value, $\textrm{value} * \textrm{bias}
+ textrm{bias}$ shall be sent to the MIDI device / the plugin. For controllers like "pan", the unbiased values shall be transformed, that is, a pan of 64, with $\textrm{bias}=2$ and $\textrm{gain}=0.5$, shall be transformed to 66 (because 64 is actually 0, while 0 is actually -64). These values shall be set in the arranger and wherever the actual controller/automation values can be edited.

Enabled-indicator while recording

The MusE-plugin-GUIs shall display a small LED displaying whether a controller is currently enabled or disabled. By clicking this LED, the enabled state shall be switched.

Furthermore, there shall be a dedicated window which only lets you switch enabled/disabled states. This will be useful when using external GUIs or the MIDI-controller-to-automation feature, to re-enable a controller when in fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH mode.

Linear automation editing

While holding some modifier key (like shift), operating the MusE-native- GUI sliders shall only generate control points when clicking and when releasing the slider. This will result in linear graphs for continuous controllers, and in large steps for discrete controllers (which is in particular useful for stuff like "which low/high-pass filter type to use").

Maybe make this behaviour default for discrete controllers?


Symbolic names for MIDI ports

MIDI ports shall have a user-defined symbolic name (like "Korg" or "Yamaha DX 7"). The mapping between these symbolic names and the hardware port (like "ALSA midi out port") is stored in the global configuration.

Song files only specify the symbolic names as the ports associated with their tracks. No information about physical devices/port names, but only symbolic names are stored in the song file.

This resolves the issues mentioned in 1.2, and also allows the user to share his pieces with other people: They would only have to set up that symbolic-to-hardware mapping once (collisions are unlikely, because an equal symbolic name should usually mean the same device) and are happy, instead of having to re-map every port for every song.



muse-4.0.0/src/doc/html/single/documentation/000077500000000000000000000000001404105311000211035ustar00rootroot00000000000000muse-4.0.0/src/doc/html/single/documentation/arrow_tool.png000066400000000000000000000004021404105311000237740ustar00rootroot00000000000000PNG  IHDRoHCIDATx͓M0Fۤ7Ņ(weMԸ0 *ud L i1L٪1F bµjes'Z;( V.Eo Lg:|jָYWezPQ"i:, ȅ)_I< %Gqʩb ==b(>{|S G_RU+IENDB`muse-4.0.0/src/doc/html/single/documentation/documentation.css000066400000000000000000000017771404105311000245020ustar00rootroot00000000000000/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ .MATH { font-family: "Century Schoolbook", serif; } .MATH I { font-family: "Century Schoolbook", serif; font-style: italic } .BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold } /* implement both fixed-size and relative sizes */ SMALL.XTINY { font-size : xx-small } SMALL.TINY { font-size : x-small } SMALL.SCRIPTSIZE { font-size : smaller } SMALL.FOOTNOTESIZE { font-size : small } SMALL.SMALL { } BIG.LARGE { } BIG.XLARGE { font-size : large } BIG.XXLARGE { font-size : x-large } BIG.HUGE { font-size : larger } BIG.XHUGE { font-size : xx-large } /* heading styles */ H1 { } H2 { } H3 { } H4 { } H5 { } /* mathematics styles */ DIV.displaymath { } /* math displays */ TD.eqno { } /* equation-number cells */ /* document-specific styles come next */ DIV.navigation { } DIV.center { } SPAN.textit { font-style: italic } SPAN.arabic { } SPAN.textbf { font-weight: bold } muse-4.0.0/src/doc/html/single/documentation/documentation.html000066400000000000000000001622251404105311000246520ustar00rootroot00000000000000 MusE Documentation

Image muse2

Contents

What is this?

You are, if you have printed this document, holding in your hand the written documentation for the audio and midi sequencer MusE version 2.
http://www.muse-sequencer.org is MusE's home on the internet where everything MusE related should be possible to find, software, this documentation, forums, mailing lists, bug reporting, FAQs. If you have this document but not the software head on over there to find what it's all about.

User's manual

Introduction

A brief history of computer audio and MusE

To quickly summarize over a decades open source development: in 1999 Werner Schweer released the first version of MusE, muse-0.0.1.tar.gz, in it's first few releases (actually not few, Werner relentlessly churned out new releases) MusE was only a midi sequencer. The target was to create a fully fledged midi sequencer for the Linux operating system. Over the years audio was added among with other things implemented and sometimes abandoned. Today MusE is a stable and feature rich music creation environment which strives to encompass most of the music recording process, creation, editing, mastering.

Definitions

CTRL refers to the control key on the keyboard, e.g. CTRL+C means to press and hold the control key while pressing the c key. Make sure you know where you have it so you won't accidentally lose control (bad jokes are the best jokes, so say we all!).
SHIFT refers to the shift key on the keyboard, see above for usage
ALT refers to the alt key on the keyboard, see above for usage
$> is used as a generic definition for a terminal prompt. When the manual lists a command that shall be typed, the prompt is not part of the command.
Keys are always referred to in bold uppercase, e.g. A. For instance SHIFT+A for the key a pressed together with the shift key.
Sometimes terminal examples are written tabbed in with a fixed font to visualize more closely what something looks like on the screen. E.g.
$> muse2

Getting up and running for impatient people

Install MusE from the repository of your chosen distribution. To get decent performance start http://jackaudio.org/Jack with the following command in a terminal:
$> jackd -d alsa -d hw:0 -p 256
Or, if you prefer, use the launcher utility http://qjackctl.sourceforge.net/QJackCtl to get some help starting Jack. After this, start MusE from the menu or fire up another terminal and type

muse2.
If this didn't work out read on for the slightly more complete route for getting things started.

Getting up and running

Installation from binaries

There are several ways to install MusE depending on your situation. The most convenient way is to install a prepackaged version from your chosen distribution. The drawback of this is that it may not be the most recent version, though often there is a more recent package from a private packager.

Installation from source

Building MusE from source is not hard, there are a number of prerequistes that must be met but the actual building should be painless (ha, famous last words).
Please follow the README in the source package and/or read the instructions on the homepage: http://muse-sequencer.org/index.php/Installation

Hardware

MusE on the Linux platform supports midi through ALSA and Jack-midi and audio through Jack. For information on what hardware is supported there are some convenient places to check: Also, as is often a very good approach for Linux and open source, the various forums available on the internet often contain good information. Chances are someone has already tried your configuration and/or had your specific problem and the solution is already written down.

Launching

After installation the binary muse2 is installed on the computer. If MusE was installed from a distribution repository the binary may have a different name depending on the distribution policies. Most distributions do however install a menu entry so MusE should be conveniently available from there.

Audio preconditions

In the standard case MusE expects to find and connect to the Jack audio server http://jackaudio.org. Make sure jack is installed (if MusE was installed with a distribution-package Jack will very likely already be installed) For Jack to run with best performance your system should be sufficiently tuned to allow it to run with realtime capabilities. The realtime configuration is configuration of the operating system and roughly consists of two parts.
  1. By default on most distros only the superuser lets applications setup realtime capabilities. Please see the APPENDIX for setting up realtime
  2. Maximizing performance. A standard linux installation may not able to reach the performance required by a power user. This requires exchanging the linux kernel for a so called lowlatency kernel, this is also covered by the realtime APPENDIX.

Running MusE

Find MusE in the menu or open a terminal and enter muse2.

$> muse2
A splash screen should pop up followed by the main application window and you are off!
If an error like the screenshot below pops up the Jack audio server is either not running or started as a different user than what you are trying to start MusE as.

Figure 2.1: Jack server missing
Image no_audio

Midi only

MusE can be started in Midi-only mode where MusE does not have any external dependencies apart from ALSA midi. In this case start MusE from a terminal: $> muse2 -a

ALSA midi with Jack

If Jack is running, by default MusE will not use ALSA devices, preferring Jack midi instead. To force ALSA devices to be used as well as Jack midi, start MusE with the -A option: $> muse2 -A

Beginners tutorial

To get a quick grip of what MusE can achieve please follow this beginners tutorial.

Midi Setup

First off, fire up MusE as was described in the previous chapter, making sure that the jack audio server is started with sufficient configuration to allow for audio output without breakup. Also make sure your system can make sound.

Soft synth test

With MusE up and running right click in the Track-pane (see Fig. 2.8) and select Add Synth > MESS > vam soft synth. A Soft Synth track called vam-0 should appear as well as a separate GUI for the synthesizer.

Now right click once more in the Track-pane and select Add Midi Track. Another track appears called Track 1, and its track list Port column should show it is bound to the synth that was just created vam-0. If it is not, click on the Track 1 Port column to open a drop-down list of available devices and choose vam-0.

r0.05 Image arrow_tool
Now select the drawing tool icon from the toolbar, alternatively press the shortcut key D. Move the mouse over to the arranger canvas as referenced in Fig. 2.8 and point at the midi track, the mouse should have changed to a small pencil. Draw a Part along the midi track using the mouse. For this exercise it is not important where or how large the drawn Part is. When you are done double click on the drawn part. This will open up the Piano Roll editor. To the left of the Piano Roll there are piano keys in a vertical line, try clicking on the keys in this virtual keyboard each click should be rewarded with a synth sound (maybe of questionable quality, a sound nevertheless)
Figure 2.2: vam synthesizer
Image vam_synth

Missing sound

If you got sound from the previous exercise you can carry on to the next, or keep reading for further enlightenment in case you come upon trouble later on. If there is no sound we need to do some fault hunting. First off, click on Arranger window once more and select the vam-0 track in the track-pane.
Figure 2.3: Midi editor view
Image main_window_with_midi_editor_vam
Now bring back Piano Roll window and align the windows so you can see the piano keys as well as the Meter on the Mixer Strip (see the 5 Function by function chapter for more information on these windows). The result should be something like the following:

When pressing one of the keys on virtual Keyboard the Meter on the Mixer Strip should light up in green to visualize that the Synth is making sound, if it is not try to trace back your steps and and see if you did anything differently than described. Now, if the Meter lights up but there is still no sound we need to check the routing between the tracks. Click on the Arranger window again and select the Out 1 track, this is the predefined output which MusE by default loads at startup, at the bottom of Mixer Strip there are two buttons looking like tele- jacks, these bring up the inputs and outputs of the track, click on the right one, the output and make sure that it is connected to some valid outputs on your system.

r0.25 Image output_routing
Click on the outputs to select them, if you did changes here go back and try clicking on the Piano Roll keyboard again, hopefully it helped. If there still are problems make sure your system actually can make sound through Jack, this is however getting outside the scope of this manual.

This might be the time to bring up the concept of community support. Open source software could never be what it is without the support given by individuals on forums and mailinglists, if the information given in this document is not enough, try googling your problem and/or get in touch with one of the online forums for MusE or Linux audio in general. See some pointers in the Support chapter.

Recording Midi

TBD

Recording Audio

At this point we'll make a slight detour into full on audio recording. Getting audio out of MusE has already been covered in the previous chapters so we will concentrate on the additional steps needed to record onto an audio track.

When MusE is first fired up, the output track has already been created (more about this in the chapter about templates), to proceed with audio recording we need to add two additional tracks, a wave track and an input track.
When MusE is first started right click in an empty space on the track view
Figure 2.4: Add track
Image main_window_add_track
and select Add Audio Input. Right click again and also select Add Wave Track. Two additional tracks are now visible in the Arranger, "Input 1" and "Track 1", bring up the mixer with F10 and you should see the following configuration.
Figure 2.5: Mixer with one input
Image mixer_with_one_input

Note the buttons on each mixer strip. hover over them to see their functionality. For more information on all the buttons see coming chapters about the mixer. For now lets just do what we must.
1. click on the stereo symbol over the slider to change the input to a mono track.
2. do the same for the wave track (optional)
3. click on the Mute (gray speaker) icon on the input track to unmute it.
4. click on the input routing button (see the tooltip, it looks like a tele plug) on the input track and select an appropriate connection from your system.
5. click on the output routing button on the input track and select Track 1

Already after the meter on the input track should be able to display that there is incoming sound from your sound source. If there actually is sound coming from your sound source, that is.
We are now nearly ready to start recording. First we need to select a location to store the files. MusE does not use a centralized storage of soundfiles but uses the path of the song-file (extension .med) as guidance as to where the audio files should be placed. Now as it happens MusE will prohibit us from starting a recording until the songfile has been stored. So lets take advantage of this behaviour and just go ahead and try to record. Let's get started.
In the mixer click on the red record dot on the Audio Track to arm it for recording (or enable if you will). Now when there is audio coming into the input it will also show up on the Audio Track. Also note that all the input and output routing buttons on the tracks now have the same gray color, this means that all of the tracks have a proper connection.
Figure 2.6: Mixer buttons
Image mixer_with_one_input_buttons

All fine and dandy. Now bring up the arranger window and find the round, red on white record button and click on it. This is your queue to MusE to prepare for recording. However since we have not saved our song we are presented with a dialog to do just that.
Figure 2.7: Save song
Image project_my_first_song
Note the check box for creating a project folder, when working with audio this is very much recommended or you may soon loose track of what audio files belong to which song.
Finally we are ready to start recording! The process is completed by clicking on the Play button in the Arranger. If all went well MusE then starts to record a wave file from the Input Track placed in your song directory.
When you wish to stop recording press Stop in the Arranger, now the resulting waveform should be visible in the Arranger. After rewinding the Play position and pressing Play again the resulting sound should be audible through the connected output.

Basic overview

In this section we will make a step by step walk-through of all the different editors, their purpose and what functions they support.

Main/Arranger

Figure 2.8: MusE main window
Image main_window_annotated
Above is the main window of MusE, the Arranger, this is what greets you when launching MusE. The Arranger consists of two main parts, the Track-pane and the Arranger canvas. The Track-pane lists all currently visible tracks and the Arranger canvas contains all Parts of the composition. The screenshot above shows an empty project. Below is MusE with a song in progress, turns out it wasn't a very good song, but for our purposes it is fine. In the below screenshot there are a lot of tracks visible in the Track-pane, each have an icon which indicate it's type, wave-track, input, output etcetera, more about that later. In the Arranger canvas a number of parts are visible, the ones in yellow are in this composition wave files, the multicolored line are different Parts of a drum track.

Figure 2.9: MusE main window with arrangement
Image main_window_with_arrangement


Mixer

Choosing View > Mixer A or B from the menu in the main window will bring up the mixer as viewed below. The mixer will open with all options enabled, showing channel strips for all tracks in the current setup, depending on how far you have gotten this view may become very large, at which point it may be a good idea to limit what is viewed in the Mixer. From the view menu all the different kinds of tracks can be toggled on/off from the mixer. Some may find it a good idea to use the two mixers A and B setup with different setup and store this in your song template(s), more about this in the Song Template section. It can be argued that everything in MusE is a track analogous to the Unix idiom that everything is a file. The types of tracks visible in the mixer (and track-pane) are:
r0.5 Image mixer
  • Audio output
  • Audio input
  • Group track
  • Aux track
  • Wave track
  • Synth track
  • Midi track

There is also a Midi Track variation called Drum Track, they are however not distinguishable from Midi Tracks in the Mixer. Also the strips for midi tracks are different in the Mixer than in the Track-pane view.

Tracks and parts

MusE arranges your music in tracks and parts. The following section shall provide you an overview of how things are done with MusE. If you are or were a Cubase or Cakewalk user, you will feel familiar with this.

Tracks

There are two general classes of tracks: MIDI tracks and audio tracks. MIDI tracks (and drum tracks which are internally MIDI tracks) can hold note data. The Wave track is a type of audio track which holds wave data. There are also several other kinds of audio tracks.

MIDI tracks

MIDI and drum tracks hold MIDI event data. They don't differ much, except that drum tracks offer a special editor which is more suitable for drum editing.

Wave tracks

They hold audio data which can be just played back or be piped through effect plugin chains. They offer automation for these plugins.

Audio input tracks

These provide the path for your audio data from outside into your project. Set up the physical audio inputs you want to connect your audio input track with, and then route the input tracks to various other tracks such as wave tracks.

Audio output tracks

These provide the path for your project's audio data to outside. Set up the physical audio outputs you want to connect your audio out track with, and then route various other tracks, such as wave tracks, to the output tracks.

Audio group tracks

Group tracks are like busses, where you can route other tracks to them, then route the groups to other tracks. Since group tracks have all the features of other audio tracks, like volume and pan, they provide a convenient common routing point where you have control of the sound before it is passed to other tracks.


Audio aux tracks

These provide a more convenient way to mix several audio tracks together. With each audio aux track added, other audio tracks will gain a common send knob for adjusting the level sent to the aux track. This can be more convenient than using several group tracks.

Synthesizer tracks

This type of track is a software synthesizer which MIDI and drum tracks can be assigned to.

Creation

You can create a track by either right-clicking in the arranger's track list and then adding the desired track, or via the edit menu.

Attributes

Tracks have several attributes:
Mute:
If you click on the Mute field (denoted with a "M" column header), the track gets muted and stops producing sound.
Solo:
The solo button ("S" column header) singles out a track for listening. It mutes some other tracks but may phantom solo others. For more info see the section on soloing: 2.5 and phantom soloing: 2.5.1
Record:
The R column "arms" your track for recording. When you rec-arm your song and have no tracks rec-armed, you won't be able to record anything. See also the config option "move rec-arm with selection".
Track name:
Double-click to edit the track name.
Port:
For MIDI tracks, this lets you select the MIDI port to which the events should be routed. This can be your physical synthesizer or a software synthesizer. For soft synths, this is the port the synth is associated to. For other track types, this is disabled.
Channel:
For MIDI tracks, this is the MIDI channel the output is sent to. For any kind of audio tracks, this is the number of channels (mono, stereo).
Automation:
For audio tracks, this lets you set up the automation display in the arranger. (See automation 2.7.1). Clicking this will provide you with a popup menu with lots of submenus. Clicking on a submenu will select or unselect it showing or hiding the automation parameter as a graph overlaid on top of the track.
The submenus let you select the color you want to associate with the automation parameter. There you can also assign midi controllers to the parameters, a dialog is shown where you can manually choose the midi controller, with a learn button to 'listen for' and automatically recognize any midi controller operated by you.

Clef:
For MIDI tracks, you can specify a clef here. This only affects the score editor.

The trackinfo side bar

In the arranger and the part editors, you'll have a trackinfo sidebar on the left side. You can set up track-type specific things there.


MIDI trackinfo sidebar

The MIDI trackinfo sidebar lets you change program, volume, pan and more. This sidebar can also be viewed at the left of the pianoroll editor.
Old style drum tracks:
These are MIDI tracks as well, but with a few differences. They allow you to map certain drum sounds with different input notes, and you can change the output settings of a certain "drum instrument" without having to alter each single event.

However, they have certain limitations: They only can handle 128 sounds (even if you have more synthes), they aren't really compatible with MIDI tracks (you can interchange parts between them, but if you touched the drum list, you'll get unexpected results), you can't set a program for the used channel and more.

New style drum tracks

Because of these limitations, we introduced the new-style drum tracks. They're not fully compatible with the old drum tracks, so the old are still retained. Under "Global Settings", "GUI settings", you can set up whether you prefer the old or new.

They are handled exactly like plain MIDI tracks (staying compatible with them), and offer all of the functionality, though in a different way. They allow you to re-order the drum map efficiently, you can open parts from multiple drum tracks in one drum editor (MusE will separate the sounds from different tracks according to your settings, see the "Window Config" menu), and you can set programs as with normal MIDI tracks.

MIDI trackinfo controls:
Output port:
This drop-down list selects the midi port to send midi output from this track.
Output channel:
This box selects the midi channel to be used on the output port.
Input and output routing:
Selects midi ports and channels to receive midi from, and soloing paths. (See Routes 2.4).
Midi through:
This button selects whether midi input is passed through to the selected output port.
Depending on your midi devices and settings, there are cases when this should be off such as using the same port and channel for input and output (otherwise a double-note echo will be heard), and cases when it must be on such as when using a synthesizer track as output device.
Input detect indicator:
Blinks when midi activity is detected on the selected midi channels on the selected midi input ports.
Transpose:
This transposes midi input notes up or down in pitch. This is very useful if your midi keyboard hasn't enough keys or the selected output device plays an octave too low or high, and you would like to shift the octave of the incoming notes to compensate.
Delay:
Adjusts the delay of the notes.
Length:
Adjusts the length of the notes.
Velocity:
Adjusts the velocity of incoming notes. Use it to compensate for a too-loud or too-soft keyboard.
Compression:
Adjusts the compression of incoming note velocities. Use it to make soft incoming notes louder, and loud notes not so loud.
Instrument:
Selects the midi instrument patch to be used by the selected output port. This is equivalent of dialing the patch in the bank and program boxes, except it displays a more friendly patch name as defined by the selected output port's midi instrument. See instruments, or port configuration 2.8.1
H-Bank:
Selects the high bank number of the current patch.
L-Bank:
Selects the low bank number of the current patch.
Prog:
Selects the program number of the current patch.
Volume:
Adjusts the midi volume controller.
Pan:
Adjusts the midi pan controller.
The buttons beside the Prog, Volume, and Pan boxes store the value, at the current transport position, for midi automation. (See automation 2.7.1).

Note that the 'Prog' button stores H-Bank and L-Bank along with 'Prog' value, so there are no H-Bank and L-Bank buttons.

The 'All' button simply stores all three Program (and banks), Volume, and Pan values at once.

Tip: If the Song Type is GM, GS, or XG, you may need to store desired values at transport position zero, otherwise your adjustments may be overridden by the instrument when the transport is moved back to position zero. If this behaviour is undesired, you can set the Song Type to 'NO' meaning no song type.

Audio trackinfo sidebar

Unlike the midi trackinfo sidebar, the audio trackinfo side bar is nothing more than an embedded audio mixer strip, the exact same strip as found in the mixers. (See mixer 2.2.2).
Effects rack:
On the top of the audio trackinfo sidebar, there is an effects rack which allows you to apply various plugins on the audio. For more information on this, refer to 2.6.1.

Parts

Within MIDI, drum and wave tracks, you can create parts. Parts are chunks of coherent notes or wave data which can be moved around, copied, cloned and deleted independent from other parts.

Parts are created by selecting the pencil tool and then drawing onto the right part area in the arranger. You can move them with the arrow tool, delete them using the DEL key, and a right-click opens a popup menu. This menu allows you even more stuff, such as setting the part's color, saving the part to disk etc.. You can use CTRL+C and CTRL+V for copying and pasting parts. CTRL+B pastes the part as a clone. Pressing SHIFT additionally provides you a dialog which allows you to paste the part multiple times and set more stuff.

You can also copy parts with the mouse by moving the part with the mouse while holding down the CTRL key.


Routes

Routes are how tracks are connected together and to the outside world. (They are also how Jack midi ports connect to the outside world. See midi port configuration 2.8.1). Each track strip has two buttons whose icons look like plugs. One button is for input routing and the other is for output routing. Clicking on these buttons will pop up a menu of available input or output routes that you can connect to. Most audio tracks list other tracks to connect to, but audio input and output tracks are special: Audio input track input routing menus list available Jack audio input ports. Conversely audio output track output routing menus list available Jack audio output ports.

r0.25 Image output_routing

Meanwhile MIDI and drum tracks allow you to route available MIDI ports and channels to the track using a handy popup matrix.

r0.25 Image midi_routing_matrix


Anti circular routing

Any routing menu item which would cause a circular routing condition is grayed out. Find out why the condition would exist by examining routing paths involved and correct the situation if required.

Also, you cannot use a track's aux sends if the track has an input route path from ANY Aux Track. (See aux tracks 2.3.1). Aux send knobs and labels are disabled in that case.


Soloing chain routes

Soloing chains (see solo chains 2.5.2) are really just routes like any other. The available solo chaining paths are displayed in the routing popup menus.


Track soloing

Soloing allows you to single out a track for listening while muting others, without you having to mute the other tracks. (See soloing track attribute 2.3.1).


Phantom soloing

In order to solo a track and mute others so that it is heard, MusE employs 'phantom' soloing: When a track is soloed, MusE automatically solos all tracks routed to and from this track. (See routes 2.4). A phantom soloed track is indicated by a black square in the track pane solo column. (See track attributes 2.3.1).

Figure 2.10: Soloing, with phantom soloing
Image soloing_window


Soloing chains

When an audio output track sends audio to some external entity, such as an external running application, and audio from the external entity is fed back into a MusE audio input track, solo chains allow you to solo the input or output, and MusE will complete the path automatically soloing the other, and all paths that came before or after it.

Solo chains also work with MIDI tracks chained to audio inputs: When a MIDI track drives some MIDI device whose audio is fed into MusE, solo chains allow the entire chain to be soloed.

Solo chains are accessed via routing menus. (See solo chain routes 2.4.2).


Plugins

Plugins are small add-ons which can process a track's data.

MIDI plugins operate on midi and drum tracks, and are found in the Midi menu.

Audio plugins can be applied to any track handling audio (that is, inputs, outputs, wave tracks, synth tracks). The effects rack section describes this. (See effects rack 2.6.1).


The audio effects rack

All audio track types (Input, Output, Group, Wave, Synth, and Aux) have an effects rack into which audio plugins can be inserted in a chain. Currently each rack can accommodate up to four plugins.

MusE currently supports LADSPA plugins and DSSI synth and effects plugins.

Plugins can be added by double-clicking on an entry in the effect rack in the track info pane (which is shown at the left side of the arranger when the according track is selected). Right-clicking the rack items offers a self-explanatory popup menu.

All plugin controls can be automated. (See audio automation 2.7.1).

One must carefully consider how many audio inputs and outputs a plugin has, and how may channels the particular audio track has (1 mono or 2 stereo), and how MusE uses the plugins in the rack.

Learn more about this in the appendix Understanding the Effects Rack: 3


Audio plugin Graphical User Interfaces (GUIs)

Once a plugin is added, you need a way to manipulate its controls, which affect its behaviour and operate on the sound.

MusE can show a generic GUI which contains all of the plugin's controls arranged in a rather plain generic fashion.

Some plugins may also have a native GUI which looks much better (it was specifically designed for the plugin).

Both GUI types are opened from the effects rack right-click popup menu.


Automation

Automation is the ability to record (or construct) and playback exact sequences of control movements.

MIDI and audio automation are each currently uniquely different, but share some similarities.


Audio automation

Almost all graphical audio controls in MusE can be automated.

This includes an audio track's volume and pan, and the controls of any plugins in the effects rack, and if the track is a synthesizer track, all of the synth's controls.

Each control has a manual adjustment value. This value is shown when there is no automation data at all, or automation has been disabled.

For plugin and synth controls, it is usually more desirable to manipulate automation with the generic plugin GUIs, because MusE has full control over their behaviour. (See plugin GUIs 2.6.1).

There are a few ways to enter audio automation data:

  • By adjusting audio controls while the transport is rolling. MusE will record the exact movements.
  • By adjusting audio controls while the transport is stopped, at different transport positions. TOUCH mode allows this.
  • By right-clicking any audio control and choosing an operation from the automation popup menu. This includes storing, erasing, and clearing automation events, and seeking the next or previous event.
  • By drawing the data on the audio track's automation graphs. (See track automation 2.3.1).

Audio automation modes

Each audio track strip has an automation mode button at the bottom. There are four automation modes:
OFF:
Disables all automation, uses manual value always.
READ:
Automation data is applied to controls. If any automation data exists, the manual value is overridden and has no effect.
TOUCH:
Allows you to alter a control at any time, while transport is stopped or rolling, If rolling, when the control is released it returns to reading from automation data.
WRITE:
Allows to adjust an initial value before rolling the transport. While rolling, when the control is released it does not return to reading from automation data.
Here is a screenshot of automation WRITE mode, and some automation data, with the track pane automation popup menu showing (see track automation 2.3.1):
Figure 2.11: Audio automation graphs
Image main_window_with_automation

Midi automation

MIDI automation is a slightly different concept: Unlike audio automation, currently there is no automation 'mode' and it doesn't record graphical control movements. Data is viewed from within the pianoroll and drum editors, by clicking on the 'Ctrl' button on those canvases.

Similar to audio controls, each midi control has a manual adjustment value. This value is overridden when there is midi automation data.

There are a few ways to enter MIDI automation data:

  • By adjusting external MIDI controls (such as a midi keyboard pitch or modulation wheel) while the transport is rolling and both the transport and midi track are in record mode. MusE will record the exact movements. As mentioned earlier, note that graphical control movements are not recorded.
  • By right-clicking any midi control and choosing an operation from the automation popup menu. This includes storing and erasing automation events.
  • By adjusting volume, pan, bank or program boxes in the midi trackinfo panel and clicking the corresponding volume, pan, or program buttons. (See midi trackinfo 2.3.1).
  • By drawing the data on a midi part's automation graphs.
Here is a screen shot of a midi track, containing a midi part which has been opened with the pianoroll editor and automation data showing.

The 'Ctrl' popup menu (bottom left) shows available midi controllers and the green dot indicates there is some data.

Figure 2.12: MIDI automation graphs
Image main_window_with_midi_automation

Configuration

MIDI ports

MIDI ports provide an abstraction layer for your MIDI hardware and synthesizers (which can be both software and hardware synthesizers), and other MIDI applications. Port are numbered. In order to produce sound, each MIDI track is assigned to exactly one MIDI port, to which the MIDI events are then sent.

The advantage of this abstraction layer is that if your system changes, for example you change MIDI hardware, then you need only modify the ports instead of all the tracks using those ports. This is similar to the audio input and output track abstraction to the outside world.

MIDI port configuration

In the midi/softsynth configuration menu, you must map the port numbers to the actual devices (by selecting ALSA or jack midi ports, or synth plugins).

Try left-clicking on the "Ports" column of some MIDI track. If you use a soft synth, right-clicking the Ports column of the synth or any track using the synth lets you launch the synth's GUI.

Figure 2.13: Midi configuration window
Image midi_config_window

Columns in the MIDI configuration ports list:

GUI:
For synthesizer devices, indicates if a gui is available and if it is showing. Click to show.
I:
If present, the port can accept MIDI input. Click to enable or disable it.
O:
If present, the port can send MIDI output. Click to enable or disable it.
Instrument:
Selects the instrument to be used when MIDI is played through the port.
Device name:
Selects or creates a MIDI device assigned to the port. These can be Jack MIDI devices or ALSA MIDI devices (if ALSA is enabled), or soft synthesizers. Jack MIDI devices are created by selecting Create Jack Device from the Device name drop-down menu. Jack MIDI devices can be renamed as you wish by clicking the device name. Soft synthesizers are created by clicking in the soft synthesizer list and then Add Instance. Or you can simply create a new synthesizer track from the arranger track list, or even the mixer menus.
In and Out routes:
These are for Jack MIDI devices, they are the routes to and from available Jack MIDI ports. Jack may provide different alias names for these ports, you can select which alias is shown.
Default in channels:
Auto-connect these port channels to new midi or drum tracks.
Default out channel:
Auto-connect new midi or drum tracks to this channel on the port.
State:
Indicates the state of the port including any errors opening it.

Global settings

Audio settings

Minimum control period

Plugins can usually process an arbitrarily small (or large) amount of samples. If some plugin control value changes continuously, to provide ideal listening experience, MusE would need to call the plugin 44100 times a second, asking for one single value at a time. With the minimum control period setting, the user can force MusE to ask the plugin for at least N values. Setting this value to 64 would in this situation make MusE call the plugin $689=\frac{44100}{64})$ times a second, asking for 64 values at a time. While doing this will reduce accuracy of control changes, it may also reduce CPU usage, because calling the plugin more often, requesting smaller chunks, is more expensive than calling it seldomly, requesting larger chunks.
Recommendation
If you have no performance problems, or if you want to do the final downmix of your project, set this to a low value. If you're experiencing performance problems, increasing this value might help.


Appendix

Understanding the effects rack

One must carefully consider how many audio inputs and outputs a plugin has, and how may channels the particular audio track has (1 mono or 2 stereo), and how MusE uses the plugins in the rack.

MusE will try to internally create as many independent copies (instances) of a plugin as necessary, to satisfy the number of channels in the audio track. Basically it divides the number of track channels by the number of plugin audio inputs or outputs to determine how many copies to make. First it examines the number of plugin audio outputs, and if there are none, it will examine the number of audio inputs, and if there are none, it will simply use just one plugin copy.

For mono tracks with plugins having more than one audio input or output, MusE uses the first input or output and ignores the rest.

For stereo tracks:

plugin inputs outputs copies track in route channels track out route channels
0 0 1 0 0
0 1 2 0 2
0 >=2 1 0 2
1 0 2 2 0
1 1 2 2 2
1 >=2 1 1 (L only) 2
>=2 0 1 2 0
>=2 1 2 2 2
>=2 >=2 1 2 2

Notice that on a stereo track with a plugin having one audio input and two audio outputs, only the first track input route channel is used (left only).

These same rules apply to inter-plugin audio when more than one plugin is in the rack chain. Extra audio outputs of one plugin may be ignored by the next plugin if not used.

Currently specialized plugins with many inputs and/or outputs are not really useful in MusE.

Nor are so-called 'realtime' control plugins which use audio inputs and outputs for control signals.

Loud noise alert! Beware of using such plugins in an audio effects rack.

Example: Consider a stereo Audio Input track with these effect rack LADSPA plugins:

  • comb_splitter Comb Splitter by Steve Harris
  • tap_stereo_echo Tap Stereo Echo by Tom Szilagyi

The Comb Splitter has one audio input and two audio outputs. The Stereo Echo has two audio inputs and two audio outputs.

The stereo Audio Input track will therefore ignore its second input route connection. It will process the left input only, separating it into stereo with the Comb Splitter, passing the split stereo signal into the Stereo Echo, finally producing stereo output available at the Audio Input track's output routes.

One improvement would be not creating unused redundant plugin copies between plugins in stereo tracks. For example, for a plugin having one audio input and one audio output, feeding a plugin having one audio input and two audio outputs, the extra copy of the first plugin is redundant and not required, but currently it is created anyway.


muse-4.0.0/src/doc/html/single/documentation/img1.png000066400000000000000000000011451404105311000224470ustar00rootroot00000000000000PNG  IHDR[#~n[0PLTEgggMMM''' tttZZZ@@@444lܬ]tRNS@fIDAT8ݓOkAƟ Czrg#?AAzXJуGE^4E=x֛Ggv6D^ٝy7, ^ bjC ,{EmhoKܔ\ ];0Àgz %Y$4uIiSK%4Fu^;eZP:81Yi@.1Hn2_[q;33# 3J|Ea<{H(}KbVΝ;aDپY1ug~}+BelXШCg8ɶ?}6W+OP$Yzیn_k^yJ ~&7/vxLe'VJd[g-7a27/[? MusE Documentation

Image muse2

Contents

What is this?

You are, if you have printed this document, holding in your hand the written documentation for the audio and midi sequencer MusE version 2.
http://www.muse-sequencer.org is MusE's home on the internet where everything MusE related should be possible to find, software, this documentation, forums, mailing lists, bug reporting, FAQs. If you have this document but not the software head on over there to find what it's all about.

User's manual

Introduction

A brief history of computer audio and MusE

To quickly summarize over a decades open source development: in 1999 Werner Schweer released the first version of MusE, muse-0.0.1.tar.gz, in it's first few releases (actually not few, Werner relentlessly churned out new releases) MusE was only a midi sequencer. The target was to create a fully fledged midi sequencer for the Linux operating system. Over the years audio was added among with other things implemented and sometimes abandoned. Today MusE is a stable and feature rich music creation environment which strives to encompass most of the music recording process, creation, editing, mastering.

Definitions

CTRL refers to the control key on the keyboard, e.g. CTRL+C means to press and hold the control key while pressing the c key. Make sure you know where you have it so you won't accidentally lose control (bad jokes are the best jokes, so say we all!).
SHIFT refers to the shift key on the keyboard, see above for usage
ALT refers to the alt key on the keyboard, see above for usage
$> is used as a generic definition for a terminal prompt. When the manual lists a command that shall be typed, the prompt is not part of the command.
Keys are always referred to in bold uppercase, e.g. A. For instance SHIFT+A for the key a pressed together with the shift key.
Sometimes terminal examples are written tabbed in with a fixed font to visualize more closely what something looks like on the screen. E.g.
$> muse2

Getting up and running for impatient people

Install MusE from the repository of your chosen distribution. To get decent performance start http://jackaudio.org/Jack with the following command in a terminal:
$> jackd -d alsa -d hw:0 -p 256
Or, if you prefer, use the launcher utility http://qjackctl.sourceforge.net/QJackCtl to get some help starting Jack. After this, start MusE from the menu or fire up another terminal and type

muse2.
If this didn't work out read on for the slightly more complete route for getting things started.

Getting up and running

Installation from binaries

There are several ways to install MusE depending on your situation. The most convenient way is to install a prepackaged version from your chosen distribution. The drawback of this is that it may not be the most recent version, though often there is a more recent package from a private packager.

Installation from source

Building MusE from source is not hard, there are a number of prerequistes that must be met but the actual building should be painless (ha, famous last words).
Please follow the README in the source package and/or read the instructions on the homepage: http://muse-sequencer.org/index.php/Installation

Hardware

MusE on the Linux platform supports midi through ALSA and Jack-midi and audio through Jack. For information on what hardware is supported there are some convenient places to check: Also, as is often a very good approach for Linux and open source, the various forums available on the internet often contain good information. Chances are someone has already tried your configuration and/or had your specific problem and the solution is already written down.

Launching

After installation the binary muse2 is installed on the computer. If MusE was installed from a distribution repository the binary may have a different name depending on the distribution policies. Most distributions do however install a menu entry so MusE should be conveniently available from there.

Audio preconditions

In the standard case MusE expects to find and connect to the Jack audio server http://jackaudio.org. Make sure jack is installed (if MusE was installed with a distribution-package Jack will very likely already be installed) For Jack to run with best performance your system should be sufficiently tuned to allow it to run with realtime capabilities. The realtime configuration is configuration of the operating system and roughly consists of two parts.
  1. By default on most distros only the superuser lets applications setup realtime capabilities. Please see the APPENDIX for setting up realtime
  2. Maximizing performance. A standard linux installation may not able to reach the performance required by a power user. This requires exchanging the linux kernel for a so called lowlatency kernel, this is also covered by the realtime APPENDIX.

Running MusE

Find MusE in the menu or open a terminal and enter muse2.

$> muse2
A splash screen should pop up followed by the main application window and you are off!
If an error like the screenshot below pops up the Jack audio server is either not running or started as a different user than what you are trying to start MusE as.

Figure 2.1: Jack server missing
Image no_audio

Midi only

MusE can be started in Midi-only mode where MusE does not have any external dependencies apart from ALSA midi. In this case start MusE from a terminal: $> muse2 -a

ALSA midi with Jack

If Jack is running, by default MusE will not use ALSA devices, preferring Jack midi instead. To force ALSA devices to be used as well as Jack midi, start MusE with the -A option: $> muse2 -A

Beginners tutorial

To get a quick grip of what MusE can achieve please follow this beginners tutorial.

Midi Setup

First off, fire up MusE as was described in the previous chapter, making sure that the jack audio server is started with sufficient configuration to allow for audio output without breakup. Also make sure your system can make sound.

Soft synth test

With MusE up and running right click in the Track-pane (see Fig. 2.8) and select Add Synth > MESS > vam soft synth. A Soft Synth track called vam-0 should appear as well as a separate GUI for the synthesizer.

Now right click once more in the Track-pane and select Add Midi Track. Another track appears called Track 1, and its track list Port column should show it is bound to the synth that was just created vam-0. If it is not, click on the Track 1 Port column to open a drop-down list of available devices and choose vam-0.

r0.05 Image arrow_tool
Now select the drawing tool icon from the toolbar, alternatively press the shortcut key D. Move the mouse over to the arranger canvas as referenced in Fig. 2.8 and point at the midi track, the mouse should have changed to a small pencil. Draw a Part along the midi track using the mouse. For this exercise it is not important where or how large the drawn Part is. When you are done double click on the drawn part. This will open up the Piano Roll editor. To the left of the Piano Roll there are piano keys in a vertical line, try clicking on the keys in this virtual keyboard each click should be rewarded with a synth sound (maybe of questionable quality, a sound nevertheless)
Figure 2.2: vam synthesizer
Image vam_synth

Missing sound

If you got sound from the previous exercise you can carry on to the next, or keep reading for further enlightenment in case you come upon trouble later on. If there is no sound we need to do some fault hunting. First off, click on Arranger window once more and select the vam-0 track in the track-pane.
Figure 2.3: Midi editor view
Image main_window_with_midi_editor_vam
Now bring back Piano Roll window and align the windows so you can see the piano keys as well as the Meter on the Mixer Strip (see the 5 Function by function chapter for more information on these windows). The result should be something like the following:

When pressing one of the keys on virtual Keyboard the Meter on the Mixer Strip should light up in green to visualize that the Synth is making sound, if it is not try to trace back your steps and and see if you did anything differently than described. Now, if the Meter lights up but there is still no sound we need to check the routing between the tracks. Click on the Arranger window again and select the Out 1 track, this is the predefined output which MusE by default loads at startup, at the bottom of Mixer Strip there are two buttons looking like tele- jacks, these bring up the inputs and outputs of the track, click on the right one, the output and make sure that it is connected to some valid outputs on your system.

r0.25 Image output_routing
Click on the outputs to select them, if you did changes here go back and try clicking on the Piano Roll keyboard again, hopefully it helped. If there still are problems make sure your system actually can make sound through Jack, this is however getting outside the scope of this manual.

This might be the time to bring up the concept of community support. Open source software could never be what it is without the support given by individuals on forums and mailinglists, if the information given in this document is not enough, try googling your problem and/or get in touch with one of the online forums for MusE or Linux audio in general. See some pointers in the Support chapter.

Recording Midi

TBD

Recording Audio

At this point we'll make a slight detour into full on audio recording. Getting audio out of MusE has already been covered in the previous chapters so we will concentrate on the additional steps needed to record onto an audio track.

When MusE is first fired up, the output track has already been created (more about this in the chapter about templates), to proceed with audio recording we need to add two additional tracks, a wave track and an input track.
When MusE is first started right click in an empty space on the track view
Figure 2.4: Add track
Image main_window_add_track
and select Add Audio Input. Right click again and also select Add Wave Track. Two additional tracks are now visible in the Arranger, "Input 1" and "Track 1", bring up the mixer with F10 and you should see the following configuration.
Figure 2.5: Mixer with one input
Image mixer_with_one_input

Note the buttons on each mixer strip. hover over them to see their functionality. For more information on all the buttons see coming chapters about the mixer. For now lets just do what we must.
1. click on the stereo symbol over the slider to change the input to a mono track.
2. do the same for the wave track (optional)
3. click on the Mute (gray speaker) icon on the input track to unmute it.
4. click on the input routing button (see the tooltip, it looks like a tele plug) on the input track and select an appropriate connection from your system.
5. click on the output routing button on the input track and select Track 1

Already after the meter on the input track should be able to display that there is incoming sound from your sound source. If there actually is sound coming from your sound source, that is.
We are now nearly ready to start recording. First we need to select a location to store the files. MusE does not use a centralized storage of soundfiles but uses the path of the song-file (extension .med) as guidance as to where the audio files should be placed. Now as it happens MusE will prohibit us from starting a recording until the songfile has been stored. So lets take advantage of this behaviour and just go ahead and try to record. Let's get started.
In the mixer click on the red record dot on the Audio Track to arm it for recording (or enable if you will). Now when there is audio coming into the input it will also show up on the Audio Track. Also note that all the input and output routing buttons on the tracks now have the same gray color, this means that all of the tracks have a proper connection.
Figure 2.6: Mixer buttons
Image mixer_with_one_input_buttons

All fine and dandy. Now bring up the arranger window and find the round, red on white record button and click on it. This is your queue to MusE to prepare for recording. However since we have not saved our song we are presented with a dialog to do just that.
Figure 2.7: Save song
Image project_my_first_song
Note the check box for creating a project folder, when working with audio this is very much recommended or you may soon loose track of what audio files belong to which song.
Finally we are ready to start recording! The process is completed by clicking on the Play button in the Arranger. If all went well MusE then starts to record a wave file from the Input Track placed in your song directory.
When you wish to stop recording press Stop in the Arranger, now the resulting waveform should be visible in the Arranger. After rewinding the Play position and pressing Play again the resulting sound should be audible through the connected output.

Basic overview

In this section we will make a step by step walk-through of all the different editors, their purpose and what functions they support.

Main/Arranger

Figure 2.8: MusE main window
Image main_window_annotated
Above is the main window of MusE, the Arranger, this is what greets you when launching MusE. The Arranger consists of two main parts, the Track-pane and the Arranger canvas. The Track-pane lists all currently visible tracks and the Arranger canvas contains all Parts of the composition. The screenshot above shows an empty project. Below is MusE with a song in progress, turns out it wasn't a very good song, but for our purposes it is fine. In the below screenshot there are a lot of tracks visible in the Track-pane, each have an icon which indicate it's type, wave-track, input, output etcetera, more about that later. In the Arranger canvas a number of parts are visible, the ones in yellow are in this composition wave files, the multicolored line are different Parts of a drum track.

Figure 2.9: MusE main window with arrangement
Image main_window_with_arrangement


Mixer

Choosing View > Mixer A or B from the menu in the main window will bring up the mixer as viewed below. The mixer will open with all options enabled, showing channel strips for all tracks in the current setup, depending on how far you have gotten this view may become very large, at which point it may be a good idea to limit what is viewed in the Mixer. From the view menu all the different kinds of tracks can be toggled on/off from the mixer. Some may find it a good idea to use the two mixers A and B setup with different setup and store this in your song template(s), more about this in the Song Template section. It can be argued that everything in MusE is a track analogous to the Unix idiom that everything is a file. The types of tracks visible in the mixer (and track-pane) are:
r0.5 Image mixer
  • Audio output
  • Audio input
  • Group track
  • Aux track
  • Wave track
  • Synth track
  • Midi track

There is also a Midi Track variation called Drum Track, they are however not distinguishable from Midi Tracks in the Mixer. Also the strips for midi tracks are different in the Mixer than in the Track-pane view.

Tracks and parts

MusE arranges your music in tracks and parts. The following section shall provide you an overview of how things are done with MusE. If you are or were a Cubase or Cakewalk user, you will feel familiar with this.

Tracks

There are two general classes of tracks: MIDI tracks and audio tracks. MIDI tracks (and drum tracks which are internally MIDI tracks) can hold note data. The Wave track is a type of audio track which holds wave data. There are also several other kinds of audio tracks.

MIDI tracks

MIDI and drum tracks hold MIDI event data. They don't differ much, except that drum tracks offer a special editor which is more suitable for drum editing.

Wave tracks

They hold audio data which can be just played back or be piped through effect plugin chains. They offer automation for these plugins.

Audio input tracks

These provide the path for your audio data from outside into your project. Set up the physical audio inputs you want to connect your audio input track with, and then route the input tracks to various other tracks such as wave tracks.

Audio output tracks

These provide the path for your project's audio data to outside. Set up the physical audio outputs you want to connect your audio out track with, and then route various other tracks, such as wave tracks, to the output tracks.

Audio group tracks

Group tracks are like busses, where you can route other tracks to them, then route the groups to other tracks. Since group tracks have all the features of other audio tracks, like volume and pan, they provide a convenient common routing point where you have control of the sound before it is passed to other tracks.


Audio aux tracks

These provide a more convenient way to mix several audio tracks together. With each audio aux track added, other audio tracks will gain a common send knob for adjusting the level sent to the aux track. This can be more convenient than using several group tracks.

Synthesizer tracks

This type of track is a software synthesizer which MIDI and drum tracks can be assigned to.

Creation

You can create a track by either right-clicking in the arranger's track list and then adding the desired track, or via the edit menu.

Attributes

Tracks have several attributes:
Mute:
If you click on the Mute field (denoted with a "M" column header), the track gets muted and stops producing sound.
Solo:
The solo button ("S" column header) singles out a track for listening. It mutes some other tracks but may phantom solo others. For more info see the section on soloing: 2.5 and phantom soloing: 2.5.1
Record:
The R column "arms" your track for recording. When you rec-arm your song and have no tracks rec-armed, you won't be able to record anything. See also the config option "move rec-arm with selection".
Track name:
Double-click to edit the track name.
Port:
For MIDI tracks, this lets you select the MIDI port to which the events should be routed. This can be your physical synthesizer or a software synthesizer. For soft synths, this is the port the synth is associated to. For other track types, this is disabled.
Channel:
For MIDI tracks, this is the MIDI channel the output is sent to. For any kind of audio tracks, this is the number of channels (mono, stereo).
Automation:
For audio tracks, this lets you set up the automation display in the arranger. (See automation 2.7.1). Clicking this will provide you with a popup menu with lots of submenus. Clicking on a submenu will select or unselect it showing or hiding the automation parameter as a graph overlaid on top of the track.
The submenus let you select the color you want to associate with the automation parameter. There you can also assign midi controllers to the parameters, a dialog is shown where you can manually choose the midi controller, with a learn button to 'listen for' and automatically recognize any midi controller operated by you.

Clef:
For MIDI tracks, you can specify a clef here. This only affects the score editor.

The trackinfo side bar

In the arranger and the part editors, you'll have a trackinfo sidebar on the left side. You can set up track-type specific things there.


MIDI trackinfo sidebar

The MIDI trackinfo sidebar lets you change program, volume, pan and more. This sidebar can also be viewed at the left of the pianoroll editor.
Old style drum tracks:
These are MIDI tracks as well, but with a few differences. They allow you to map certain drum sounds with different input notes, and you can change the output settings of a certain "drum instrument" without having to alter each single event.

However, they have certain limitations: They only can handle 128 sounds (even if you have more synthes), they aren't really compatible with MIDI tracks (you can interchange parts between them, but if you touched the drum list, you'll get unexpected results), you can't set a program for the used channel and more.

New style drum tracks

Because of these limitations, we introduced the new-style drum tracks. They're not fully compatible with the old drum tracks, so the old are still retained. Under "Global Settings", "GUI settings", you can set up whether you prefer the old or new.

They are handled exactly like plain MIDI tracks (staying compatible with them), and offer all of the functionality, though in a different way. They allow you to re-order the drum map efficiently, you can open parts from multiple drum tracks in one drum editor (MusE will separate the sounds from different tracks according to your settings, see the "Window Config" menu), and you can set programs as with normal MIDI tracks.

MIDI trackinfo controls:
Output port:
This drop-down list selects the midi port to send midi output from this track.
Output channel:
This box selects the midi channel to be used on the output port.
Input and output routing:
Selects midi ports and channels to receive midi from, and soloing paths. (See Routes 2.4).
Midi through:
This button selects whether midi input is passed through to the selected output port.
Depending on your midi devices and settings, there are cases when this should be off such as using the same port and channel for input and output (otherwise a double-note echo will be heard), and cases when it must be on such as when using a synthesizer track as output device.
Input detect indicator:
Blinks when midi activity is detected on the selected midi channels on the selected midi input ports.
Transpose:
This transposes midi input notes up or down in pitch. This is very useful if your midi keyboard hasn't enough keys or the selected output device plays an octave too low or high, and you would like to shift the octave of the incoming notes to compensate.
Delay:
Adjusts the delay of the notes.
Length:
Adjusts the length of the notes.
Velocity:
Adjusts the velocity of incoming notes. Use it to compensate for a too-loud or too-soft keyboard.
Compression:
Adjusts the compression of incoming note velocities. Use it to make soft incoming notes louder, and loud notes not so loud.
Instrument:
Selects the midi instrument patch to be used by the selected output port. This is equivalent of dialing the patch in the bank and program boxes, except it displays a more friendly patch name as defined by the selected output port's midi instrument. See instruments, or port configuration 2.8.1
H-Bank:
Selects the high bank number of the current patch.
L-Bank:
Selects the low bank number of the current patch.
Prog:
Selects the program number of the current patch.
Volume:
Adjusts the midi volume controller.
Pan:
Adjusts the midi pan controller.
The buttons beside the Prog, Volume, and Pan boxes store the value, at the current transport position, for midi automation. (See automation 2.7.1).

Note that the 'Prog' button stores H-Bank and L-Bank along with 'Prog' value, so there are no H-Bank and L-Bank buttons.

The 'All' button simply stores all three Program (and banks), Volume, and Pan values at once.

Tip: If the Song Type is GM, GS, or XG, you may need to store desired values at transport position zero, otherwise your adjustments may be overridden by the instrument when the transport is moved back to position zero. If this behaviour is undesired, you can set the Song Type to 'NO' meaning no song type.

Audio trackinfo sidebar

Unlike the midi trackinfo sidebar, the audio trackinfo side bar is nothing more than an embedded audio mixer strip, the exact same strip as found in the mixers. (See mixer 2.2.2).
Effects rack:
On the top of the audio trackinfo sidebar, there is an effects rack which allows you to apply various plugins on the audio. For more information on this, refer to 2.6.1.

Parts

Within MIDI, drum and wave tracks, you can create parts. Parts are chunks of coherent notes or wave data which can be moved around, copied, cloned and deleted independent from other parts.

Parts are created by selecting the pencil tool and then drawing onto the right part area in the arranger. You can move them with the arrow tool, delete them using the DEL key, and a right-click opens a popup menu. This menu allows you even more stuff, such as setting the part's color, saving the part to disk etc.. You can use CTRL+C and CTRL+V for copying and pasting parts. CTRL+B pastes the part as a clone. Pressing SHIFT additionally provides you a dialog which allows you to paste the part multiple times and set more stuff.

You can also copy parts with the mouse by moving the part with the mouse while holding down the CTRL key.


Routes

Routes are how tracks are connected together and to the outside world. (They are also how Jack midi ports connect to the outside world. See midi port configuration 2.8.1). Each track strip has two buttons whose icons look like plugs. One button is for input routing and the other is for output routing. Clicking on these buttons will pop up a menu of available input or output routes that you can connect to. Most audio tracks list other tracks to connect to, but audio input and output tracks are special: Audio input track input routing menus list available Jack audio input ports. Conversely audio output track output routing menus list available Jack audio output ports.

r0.25 Image output_routing

Meanwhile MIDI and drum tracks allow you to route available MIDI ports and channels to the track using a handy popup matrix.

r0.25 Image midi_routing_matrix


Anti circular routing

Any routing menu item which would cause a circular routing condition is grayed out. Find out why the condition would exist by examining routing paths involved and correct the situation if required.

Also, you cannot use a track's aux sends if the track has an input route path from ANY Aux Track. (See aux tracks 2.3.1). Aux send knobs and labels are disabled in that case.


Soloing chain routes

Soloing chains (see solo chains 2.5.2) are really just routes like any other. The available solo chaining paths are displayed in the routing popup menus.


Track soloing

Soloing allows you to single out a track for listening while muting others, without you having to mute the other tracks. (See soloing track attribute 2.3.1).


Phantom soloing

In order to solo a track and mute others so that it is heard, MusE employs 'phantom' soloing: When a track is soloed, MusE automatically solos all tracks routed to and from this track. (See routes 2.4). A phantom soloed track is indicated by a black square in the track pane solo column. (See track attributes 2.3.1).

Figure 2.10: Soloing, with phantom soloing
Image soloing_window


Soloing chains

When an audio output track sends audio to some external entity, such as an external running application, and audio from the external entity is fed back into a MusE audio input track, solo chains allow you to solo the input or output, and MusE will complete the path automatically soloing the other, and all paths that came before or after it.

Solo chains also work with MIDI tracks chained to audio inputs: When a MIDI track drives some MIDI device whose audio is fed into MusE, solo chains allow the entire chain to be soloed.

Solo chains are accessed via routing menus. (See solo chain routes 2.4.2).


Plugins

Plugins are small add-ons which can process a track's data.

MIDI plugins operate on midi and drum tracks, and are found in the Midi menu.

Audio plugins can be applied to any track handling audio (that is, inputs, outputs, wave tracks, synth tracks). The effects rack section describes this. (See effects rack 2.6.1).


The audio effects rack

All audio track types (Input, Output, Group, Wave, Synth, and Aux) have an effects rack into which audio plugins can be inserted in a chain. Currently each rack can accommodate up to four plugins.

MusE currently supports LADSPA plugins and DSSI synth and effects plugins.

Plugins can be added by double-clicking on an entry in the effect rack in the track info pane (which is shown at the left side of the arranger when the according track is selected). Right-clicking the rack items offers a self-explanatory popup menu.

All plugin controls can be automated. (See audio automation 2.7.1).

One must carefully consider how many audio inputs and outputs a plugin has, and how may channels the particular audio track has (1 mono or 2 stereo), and how MusE uses the plugins in the rack.

Learn more about this in the appendix Understanding the Effects Rack: 3


Audio plugin Graphical User Interfaces (GUIs)

Once a plugin is added, you need a way to manipulate its controls, which affect its behaviour and operate on the sound.

MusE can show a generic GUI which contains all of the plugin's controls arranged in a rather plain generic fashion.

Some plugins may also have a native GUI which looks much better (it was specifically designed for the plugin).

Both GUI types are opened from the effects rack right-click popup menu.


Automation

Automation is the ability to record (or construct) and playback exact sequences of control movements.

MIDI and audio automation are each currently uniquely different, but share some similarities.


Audio automation

Almost all graphical audio controls in MusE can be automated.

This includes an audio track's volume and pan, and the controls of any plugins in the effects rack, and if the track is a synthesizer track, all of the synth's controls.

Each control has a manual adjustment value. This value is shown when there is no automation data at all, or automation has been disabled.

For plugin and synth controls, it is usually more desirable to manipulate automation with the generic plugin GUIs, because MusE has full control over their behaviour. (See plugin GUIs 2.6.1).

There are a few ways to enter audio automation data:

  • By adjusting audio controls while the transport is rolling. MusE will record the exact movements.
  • By adjusting audio controls while the transport is stopped, at different transport positions. TOUCH mode allows this.
  • By right-clicking any audio control and choosing an operation from the automation popup menu. This includes storing, erasing, and clearing automation events, and seeking the next or previous event.
  • By drawing the data on the audio track's automation graphs. (See track automation 2.3.1).

Audio automation modes

Each audio track strip has an automation mode button at the bottom. There are four automation modes:
OFF:
Disables all automation, uses manual value always.
READ:
Automation data is applied to controls. If any automation data exists, the manual value is overridden and has no effect.
TOUCH:
Allows you to alter a control at any time, while transport is stopped or rolling, If rolling, when the control is released it returns to reading from automation data.
WRITE:
Allows to adjust an initial value before rolling the transport. While rolling, when the control is released it does not return to reading from automation data.
Here is a screenshot of automation WRITE mode, and some automation data, with the track pane automation popup menu showing (see track automation 2.3.1):
Figure 2.11: Audio automation graphs
Image main_window_with_automation

Midi automation

MIDI automation is a slightly different concept: Unlike audio automation, currently there is no automation 'mode' and it doesn't record graphical control movements. Data is viewed from within the pianoroll and drum editors, by clicking on the 'Ctrl' button on those canvases.

Similar to audio controls, each midi control has a manual adjustment value. This value is overridden when there is midi automation data.

There are a few ways to enter MIDI automation data:

  • By adjusting external MIDI controls (such as a midi keyboard pitch or modulation wheel) while the transport is rolling and both the transport and midi track are in record mode. MusE will record the exact movements. As mentioned earlier, note that graphical control movements are not recorded.
  • By right-clicking any midi control and choosing an operation from the automation popup menu. This includes storing and erasing automation events.
  • By adjusting volume, pan, bank or program boxes in the midi trackinfo panel and clicking the corresponding volume, pan, or program buttons. (See midi trackinfo 2.3.1).
  • By drawing the data on a midi part's automation graphs.
Here is a screen shot of a midi track, containing a midi part which has been opened with the pianoroll editor and automation data showing.

The 'Ctrl' popup menu (bottom left) shows available midi controllers and the green dot indicates there is some data.

Figure 2.12: MIDI automation graphs
Image main_window_with_midi_automation

Configuration

MIDI ports

MIDI ports provide an abstraction layer for your MIDI hardware and synthesizers (which can be both software and hardware synthesizers), and other MIDI applications. Port are numbered. In order to produce sound, each MIDI track is assigned to exactly one MIDI port, to which the MIDI events are then sent.

The advantage of this abstraction layer is that if your system changes, for example you change MIDI hardware, then you need only modify the ports instead of all the tracks using those ports. This is similar to the audio input and output track abstraction to the outside world.

MIDI port configuration

In the midi/softsynth configuration menu, you must map the port numbers to the actual devices (by selecting ALSA or jack midi ports, or synth plugins).

Try left-clicking on the "Ports" column of some MIDI track. If you use a soft synth, right-clicking the Ports column of the synth or any track using the synth lets you launch the synth's GUI.

Figure 2.13: Midi configuration window
Image midi_config_window

Columns in the MIDI configuration ports list:

GUI:
For synthesizer devices, indicates if a gui is available and if it is showing. Click to show.
I:
If present, the port can accept MIDI input. Click to enable or disable it.
O:
If present, the port can send MIDI output. Click to enable or disable it.
Instrument:
Selects the instrument to be used when MIDI is played through the port.
Device name:
Selects or creates a MIDI device assigned to the port. These can be Jack MIDI devices or ALSA MIDI devices (if ALSA is enabled), or soft synthesizers. Jack MIDI devices are created by selecting Create Jack Device from the Device name drop-down menu. Jack MIDI devices can be renamed as you wish by clicking the device name. Soft synthesizers are created by clicking in the soft synthesizer list and then Add Instance. Or you can simply create a new synthesizer track from the arranger track list, or even the mixer menus.
In and Out routes:
These are for Jack MIDI devices, they are the routes to and from available Jack MIDI ports. Jack may provide different alias names for these ports, you can select which alias is shown.
Default in channels:
Auto-connect these port channels to new midi or drum tracks.
Default out channel:
Auto-connect new midi or drum tracks to this channel on the port.
State:
Indicates the state of the port including any errors opening it.

Global settings

Audio settings

Minimum control period

Plugins can usually process an arbitrarily small (or large) amount of samples. If some plugin control value changes continuously, to provide ideal listening experience, MusE would need to call the plugin 44100 times a second, asking for one single value at a time. With the minimum control period setting, the user can force MusE to ask the plugin for at least N values. Setting this value to 64 would in this situation make MusE call the plugin $689=\frac{44100}{64})$ times a second, asking for 64 values at a time. While doing this will reduce accuracy of control changes, it may also reduce CPU usage, because calling the plugin more often, requesting smaller chunks, is more expensive than calling it seldomly, requesting larger chunks.
Recommendation
If you have no performance problems, or if you want to do the final downmix of your project, set this to a low value. If you're experiencing performance problems, increasing this value might help.


Appendix

Understanding the effects rack

One must carefully consider how many audio inputs and outputs a plugin has, and how may channels the particular audio track has (1 mono or 2 stereo), and how MusE uses the plugins in the rack.

MusE will try to internally create as many independent copies (instances) of a plugin as necessary, to satisfy the number of channels in the audio track. Basically it divides the number of track channels by the number of plugin audio inputs or outputs to determine how many copies to make. First it examines the number of plugin audio outputs, and if there are none, it will examine the number of audio inputs, and if there are none, it will simply use just one plugin copy.

For mono tracks with plugins having more than one audio input or output, MusE uses the first input or output and ignores the rest.

For stereo tracks:

plugin inputs outputs copies track in route channels track out route channels
0 0 1 0 0
0 1 2 0 2
0 >=2 1 0 2
1 0 2 2 0
1 1 2 2 2
1 >=2 1 1 (L only) 2
>=2 0 1 2 0
>=2 1 2 2 2
>=2 >=2 1 2 2

Notice that on a stereo track with a plugin having one audio input and two audio outputs, only the first track input route channel is used (left only).

These same rules apply to inter-plugin audio when more than one plugin is in the rack chain. Extra audio outputs of one plugin may be ignored by the next plugin if not used.

Currently specialized plugins with many inputs and/or outputs are not really useful in MusE.

Nor are so-called 'realtime' control plugins which use audio inputs and outputs for control signals.

Loud noise alert! Beware of using such plugins in an audio effects rack.

Example: Consider a stereo Audio Input track with these effect rack LADSPA plugins:

  • comb_splitter Comb Splitter by Steve Harris
  • tap_stereo_echo Tap Stereo Echo by Tom Szilagyi

The Comb Splitter has one audio input and two audio outputs. The Stereo Echo has two audio inputs and two audio outputs.

The stereo Audio Input track will therefore ignore its second input route connection. It will process the left input only, separating it into stereo with the Comb Splitter, passing the split stereo signal into the Stereo Echo, finally producing stereo output available at the Audio Input track's output routes.

One improvement would be not creating unused redundant plugin copies between plugins in stereo tracks. For example, for a plugin having one audio input and one audio output, feeding a plugin having one audio input and two audio outputs, the extra copy of the first plugin is redundant and not required, but currently it is created anyway.


muse-4.0.0/src/doc/html/single/documentation/main_window_add_track.png000066400000000000000000003065131404105311000261300ustar00rootroot00000000000000PNG  IHDRBsBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATxw|ƿ- HQ)l( ,+**ذ+ T)* *H=!d);Ds9=̽;+!M]%z#Oi9-崜rZNi9-a7ᵝд֊42`23'HUNutO%{s )J:g'Ib=-UԪWTֺr)16zN [[h;7 C;%?J^7s ))*gW߭MerVDg)sp3xra9餛rL|yvpEn7ԯ_HRܜ&5[ "G|y)XLȩ/I') 詔C: o{Ƅ}fd+.kn&*$A=I*aīa(;sR_&qy J9 a'&i8QRifh@EhQ OH<*r"cSۊ$S`7Ӱo=C4Z:qg]c%n};ƺfl<6TIoՊ `DJZ IjSc3N?T itvj~?m-.l2 ]Ģ=P=~+p,.:9WJb)J{\L'a$"ǰx)8P`oM{s6ƷbǤ7X\b<*}k5j!V ÁٟkE6YʺIYm4 U+Y2`HGfe-"3HC6JNGe_r6ȘU{Ν4k֬ʰ_x9, ßz Ʉ}o6cD$mNf˵BЪUKnŗq4Mf2ǎRf-eŸ\ΨZ28v,  :@ƪ}PeZgm^tIU(,w"D"'zBnNj,Q}:wtP^Qi䟈Veڦ5ٓ>c]Iy~VDfA2vÁEν3~ٴiՒ6fV'5ӨFiFzxB|WH/?eMIhJYExkxWr7,@k ,Z5oFC`#0HdH'=wxngsxnۙ1FoY,d׻SvOH?HMbV~e?_gpkHYdQ~`IpV{+K7MeNAn7obK9!6e+3}DWt%-7g+W΋?蕿-Kw>?`-M/f<'dO{v( ϐGCrN`wP YނEk2:!v޴hFOO{>Vj,Mf"a(bZW_#RXP墤Og'bYocΘ1L2&M9VÎ)Sٶm| _OFJj1anx, |m6["d$,2/CԬR+r!dӁR˖-r9]NN'^Ŏ!"X|s\#0}Q(GR&::z Q+QE dk/(ǗR/jQv#]uzŕe{IٜAwg(÷h\JiN2~F.$Scn2R+ޗ\[p9os)*ZM˖3UEq<ߋV Ӟ-@..wIң#~7 @"=y^4"#@8ZvZ15w>sSX,bÔ(x g$nHO~Œl_Lr:Ԡnr!I])ɰcNG)$4=vtG]өU#ZM{puM]_osS:eY͌$Iy+"k]Mi;>Z׳xw$gfJf ? PLj:â^AU% *A-؇^?!Ft]=zzaW_Q]Y- ٘ca+[ O>=bՒy'=ˈ#p]zbV51n88^zL:iӧW gY đ#Ghۮh r=^RMd甕ǠYZjZj?p4%^:ty8Nn[0λ^B!|\!{~+n/tƊmF12M(P(}Jv"L3z"PK~E=,u=6 )T+?4P'8ѫ2QIȢ"j*(:^䕁=NZ7 ~,ǥ%{oqorHR0c5_ʣ}GƳ@F6O3_X/-M-?`k~'g;:w@ '7q)kp E#9s k+ʀ>g]QĂCO.\ôť9L!J1uIV/M d=]&w\؉-[+*me안qyV5$!;0y||Y;ڴnMcֱ"Qr`}mi[1_9:c9b+tT0ݨQ:@ya|9jWzXkLKW"EO#јu|ōPjE0f?Jڇcn{Ș q}NJJ w~&SZRs'Ln:ݵj՘lVk]N' /Y}Ǯ;9bN_tUY5v|mݪ[bmlٺf8|zPZdgFݥHe#YYs *`R1it{ROu *"O%&$,Bvt23v=/&|ɡ{(ϋ)fUþ];r99rɉvnJc嗬[SQz_M޽Ob'Zp:Y7lق-[&ˡ8~Ѡuh2_7c6[;jd母ɤWv\b*[ŕ(wEFjbD)ъzU\2UCNTXJᜠ9WqJ-4gUo_WYA_Geyx@|VM_s~ſ_e&{8@vP\O"@XJ܋)cWr<3Wwmy Y7d+9SwP;0+W xeÇ~h۶ nˍWb2ڬXmqlظIgPn=P bljNX0T4ᔩ a+S%͓ҷpL^" (8>##JHhj|R\*_.!≏\fqtɘlYSe5saSÈ-5U8F]܏_8tl!px";+"Vd223S.իaD}) yvo JB%DT!2D8&resJx|[^g)]*D 8J-b'>|'ExNnoxxqcJIBvu?T,(O+H*E,6iP91?{;/4Nv†پ'94mׁFb@!pݔp*o'11CS@TC8'@;v! 'DٍHN,/K͸d=*PH˾A%Մ >6C0#p IDATFAMtBD?,Ģ[I"O% 8 CeDƬ:8X| mڼ5Mz"I6 F?-4W)IP:. Nip$v/RWHWYJ}:YB"GIȖBDxB$|*Zfe렾 B2,7i{d4RZ|\Wh_`+*LA Ս$+\zȂ/ E~:M"-Nlzc|OBk:?->o^OWvbD&(b?qzD!~CJE/Ek.¸\ Q J'#FU7@Q9=׵@&i;x>؈FgSb \1_O+hka]bqBh]@:zce|C xEFtƘ4tZ 9!I¤2_=['FУ!!!#X9DWjF(i?AaBIOy?sy,ӏx3)ܰeYRN(Hj1%p|$E M-'4O WBb-cz!i{ 3k}0]D86 N2a}i_&f QǗ@$D t "E芐J"=D_ 9G#iHB" *jD'v2vfPl*@* :0Vv,ר&yԒX)8AqqHE61BK*QPTׯJES% %j}tӄᴜ8$!p >Pѱ(XG߅$ $)$DW諓sD:~@(*&QB`d5ʗ " e=!X$A0QH:MWp\V01ci #,zqݪV٬KСBd=& IW5N`t a@@TSXIK/!a+x*i~(LP PV1}:P1]/]0%FE Vg%#UO UH~nҏH8! qZ""0GK ~>T}4`L -[|S̰?=]"tExM$zK |b 9%.FeT}Lж-W;lWPŋ1O>bhf`TV`.{< G^U;1pӘ2ٰbHRlV"gcي;6'VaUl9"22]}Y=!ϦeGYeee)--aذI'2˰axW'&pfs~ ~1{/>(BILLfSVZ*&-8tύ}Wxhq>⒛&-(| LΜUۧ/CތS;fr(#L;TeEt|A }7B9K/ƒ|ه!dgIAT\̰KT}#$"+O,)^KQV}_s1g>\q|`/B&IiXn}9QOrq8g/>WWHêbWPf*Jql}]DŽy{@o^VRZwTmTD];rety-wng^7C^Aނ;~'r` c3oKop}n{2s| >]{/3n5_qSŠauʝ:4q&xBic^c* KeQ0#RtvwyB[Uquہ{ 9Sp(3x1#W^ _+=jvkJ&{ߣ'O='ӿgO`zݟr+p 7}~Kveao%\|E\B-HD e΂8%۾gB2_R>Nw ͑t#/gWFA8ٱfR&&Ij2mt֬^oAiq!b|-VX`X1Iqdx;wwr"d_b;&7#d"=g[f WN̵fĻhfRZ?4q-I*ܗ'f}gpA:] >/T'WY^]pYFw%+s՚&V^4_MLGX #Gx FxsX5?1:5$ɝ_ ?QC^27ⷉaG 'vP|tY Lb#ZnLPZ;m1\zk#êMw}\ܴ5IgML١L-W0ْ4180yy;+Rٝ6Jj\s4 obvP׌,s{&>{eδصu% ;gR54=Ǫn}]$_J5(&5ɂh qq-o pQP}wf%|ng%N(Ģfůν&CًnuiT꿈2o\EcV 0ɚ=A(jJs1an_}Hd۸y}e/^xkِjo_D;ZiVPޏ^.&s?y/mfkxl g9?-\o|4i;;c-DŽ aIkA-HtgkI_p>Wx$P׼7?AxMx>~퓣E0!$Bq()-0f.ʺj:7ѤQlu}3vfF|;zW-gW5`繩m"||Sшk/V|c油ȄYXVGu{[`!:z}9fV 憩=쓫cV̱e"/IO&8fxF%YB}g>|cqbF"~'=3ǵGX هHk~fq+nSN|m4ɻ?tb IQ Fu'Y !^ӒIa&_<\j:ټ- Or>}]fo*lP {:ZM[Wpی1KpnyoIv$fF_OΑQW'sQ]~+,iĚ L㊏:Ci!~|)uhѫ/6> ˻ݒT13;nnw[5 ɝ};OcY 0Q>&w|&nO` Kn}OnI.QcYlj/{8?Jђ{ö0;ػ=O^Hfh\hHt׈g.倳 ͬP'5 X8_Tޭ'-y /$Uoؗš,hwˌܘ8a cQ=G˾}'ƾQye_=Çf\?_:f'uc<:emC/`[22) O =p^h*[[#]#msm b? 9ɏmpӃ{xd曜k;1/3} 91}> nCDܭ~J98ImѾ/Z_-#9 Kᆩt-ǰ;]|i%- c;o?pc<#?Z3?Dcğs/CǓްWdWQmd^ {^5:^ţî5*1sy{zrd›cBZc>ߋcKPǰ. 5'#ƽS",XxO>Ǹ|l KSg3TNR gkF_<٬zѯQص\N` 1S_L y]> ^꺗IA\ڱ/37k?~v~'3.`ԈGv&3'=E5o̔iF z,˪Okֱ(e{ѳ5͡ #$%\(| dt:ϴ_bY2kx,f\ãp<_}z>b zUmÓlR},d-~~䑯1g{ )GwIC?w>sg IfGx3w9I&}v*=ޜU3̘dxϷ7µ1!{y'dw3ﬠ(b0&듼ѧLxVX7d֬^Ibb5V+׫zX6XjIɘLU+ZmUƌ ;cޣ Դt,Vk}~Bs)?{cЧO_jhb7۷_q5cj1*;Ҏ0MpeÃ,س|];:.ۭLƓUЯwc%0v`@ڜL_gY1>K=zkAXkҢ¬Bk&R\}2w@j5CB" lVK a%h`vZMzđe?_(2K<ڳ:x4f }.i8O~?{l K5wLÁQ([W~M _Q-4߇6NV5!/҃f֢ە]fmZ&{W +SR_VlݛGPP7v6,Zpe$9NcHɵH6  +X/$œNA=IںME)ڜvL$ܘ&I%.?\ƞ_a4sjZ@W_Oksf]B!Rjgmfr hVja7wm`ȭ=ɴ 5u|zӣay)g]ٟ9~)ǚ]@$5?[BJR6 Ei  ZtM7#9&E!2WJ2KbUk aʤM7e~@;{ @J=A\NdAp%4v @+u\ٞa~sQ .(c?p}7!frU/))d=cKf԰֤kfSJCj#S&gn$4s j½0#)S :MҖX諻9<.혊@JiLb!hȷuq~̓,^} u+ݴٔz#eOl/o]̖4.laHķH z-HsxY)~݂etaBH6^83ʟdKI[V IDAT%lz˲=%[Yz)W^ـKwKض{E-\V|5{#wWGpE}k刂BhzYRV`eptrդL~9& Yp;s$_ݼ]?z;zw~77[%6Ss|t5pW4-CnZ[owqCSbila#ZXRpqLZC'pЋ'w b$lu{s龽QSZo]֧Ww7؟.8x ||];k2dZK&ݯ>(S™ˀsNk`ӊC*7KNfWOryP-QPT9x\Tl7;JKʨ]v{\y󉏋N0wwaj~̛;ﺛxk^ =wMiY&NT23TPpDCQn1q-Y-/[aGT\u5c'ܩkg$bm^=mǢl$dEpLRr\:Ho x(> ^R0ЕII6Sv/`,aJﳣL,-z_ͽ }6=#sN!?̢Gj: ӟٚ߈-+ izI?%Z`̾m7Y.%Lbr): #LB3I 3 \ʪ]FϚ_B.Wұ|n eg1|'JB9@Ҭ!}ࣄ8br'T'Alx>ɖ x-&AR2Sy`$# 7,Hj>H j/si9؊[.AHl(ŞLsS+pS*ڑqn8>\L~9ژPm[ɧ֒CpQ2`h?Ƿd% <0e MML_^6¶-CI $RPY_J<Y X^ Ƭ]ye(UrqJD 3XHbrK̤ۃA90XqZ֖"H}r5?ΤxRM^[k$0lDO,(\䮞˜KYdCx?t!lL{n)О$/ݙ7ѿ $e1b`Dkzʌ9+ [2YjŤCDX23V:i5{t U3:%q9 X`'%KV!|#]x"Lӫa*MV/%9ŐTĸZtYE3IK=/aFJN@Ib%9T?ٛ!rݗϯI2y JLC&p_B0lřt8NNLuA jmBk˸Tf.Aì)=j ~TdkcY}hzl6k`OfOZ5 E>֬#mW z?\'ʯUy%\#BKqt~Zlf}+y5h \ثp|rKe#\IL:wbfn?Y4&\l}Z}[=W$.@ON%2"x)*CH S5e ]HN$A"]ק q*Ʃ`0T+gn72.,'ا"O}K3wVJY= wD\W>jD1G w|e37^I$t){GSPN=O]5)bETrS(*HH$gnvn k^y<3|oy6/5W,&jUmߡm$Jɟ5՗ T;!qyLO7dȵC5!"(omTꩶ$ ZԺW7׽EL la;؅yE;9Uv(|??fה7+~?+pS} EӢSMTgM H"DQ\-dғ9"~3"~#Y9n]0gPNY70 6cYoj JĘq?{YL9P*0b!C_%Q]#>-􀙇- wX҉qU\a\t\jSo1߸LE0defa6-1$$c4ۙz~y^r 8yo[]V*;vpjbtGCPA\M4i4t 8bSW6~=N: :9fYʻw݇Sш2vlHyJU[R[;?)ɧ^iCZ0%?ٶH> #w_bn Ƥ,WˤSWޠ4{ڭF>C \ 䕭(e|Nq~+_ToycQfFFOΤx>}7 ǀ&u8#xU]p~[V}TL݃ 왙g~GYƷXes/_وc|[#L*8zþu$ؾv{*A `Qim9M/9a'"3/M^#Iǂ#4x7WmAV a3smX& %A0?9k^`{dejÔ}b0ykfrAt7/BV&dzu8F[/ i,Tx6?M|:lb%~:@lW0eGMT}&aXgǬY_ {Ә_ɛA;߾۟dKn-5xWFob\(f2337䑙l;)-+ڊ}^4#%[.B2CtQMTڟs|R.bǍާq֙1T39>Ә{n"$߉ڗ?TM\So㞫DQ zMWuM $s3sAh2|R L9-|!f>OY{ CrOp4ܻH?㲳숂@гOsH+? ?T2}hH{ n;.\)5S\gZG֭@:v DNIx'q)h YYڿϥ @1!^dFLM7q-h @i_։uZ 0d0}ƒѩ^e<>A0"Qh NxəQDfʍ=ə {&Kp2.Gj~/.[?q޷rEooR+v32~ Y<_~tX:({]ɒQ1nEkq~y0U yg5##>w՛0TWm'q=P$ Z?vvº&cAz"ˠ_ZF=ky1JOrf[(DyG臁c4r5MH [4.Aɢ:2itR;s*IIn} ]FYrAVDC W_I-(45#O]>n.O2~q|It)$eaG  #H sD#},>Wz2q9 EdaR-k`™[oH>ItaBDĐWW:UpF>Zy,i0:ho7t|)SP{Cx1meƎVQ$3w:E(~KP=~%c !ƍC i| lOw<R=eϢN*_c3yҤ?|J''?Q5R?۴)*#1]U1#ĴS-l; vt'8!ur8Fάe҅.4Oish'n' |ެ|BC'MN⇋n5oR~>}VRu-(ŖW_fH1_uSGlGgcӂߺ+ C rD:Bڋ!߃ &k=^M^F++=' 9r_MS%9:Rqۻ7#G*JѽyzHoIAbǎ:b$c;ѡhZ0 xS?s|=06y}3\BX6ͨ/zGFFke5nUUUlټG;vlPy([T^5UUefr:wq;N6ۓ3_qePW{\ v?KvNNȺ @QQ1999b4%?ÁhbϞ=yئi:r>r!$uQk\atETCZ*$rs{3g/tn]=޵={۵̌ġ{p8iwzD X^EEEyd^ŤgƾQqĚoFCRR" u"nw#Ƿ5ZەﰻyWGM$&2lVtEAeN'-."1!NO܏VW`00猅Bk=($x/#vyjٿ7QӬiTcHmB-8K9:mzVى`'-oVfJJJj(DjZ*y}b0E0uJնF{Va01M B8f ÃƐ{2tZ+6 N9rAlWE !9<K['tLJJbj=?gZ7Ƞ` xL M@JMUCWF{3/hLv맓<T]h4vZZQT<hq846z>8/?~I:jr^ֶ^n 0Mڷ|ȳnz5$0@ ބ8У `܀K6Z-ZQjtvc6 ŬY?h4bXX,Qz`_p^IԵS`5 /e8 j5i6|\{w4F9maېN"Dhb"9xuuFjJ 򈏷L!" 5dXΘ{&LU59t;E ;EP8]N8QEQlo?f-B{ IDATB8j %ϒӋSXyvXKzv#ɲJNMsK $%%PV^N޽;=vs铗!Ce0>"%ۛrc3y$ ^^ G"-hHKx@dem`[7!{5 t~AQR{7$%%ק =J"FhdL&͟ץŘ En{ށt߾}r`_ӑ2i6xuq]L'6}9 h<EyӾ/FVEItA$q̙l޼?5NRUuFTT75N }Nс] h(RW_ <2yOj!DQD(®{j6ZZZEgO,6f4CMc!"9!!^y8‹xH?f88Q!-N۪?jv4KI(xEj=Cޫ -HIZ^|)*`tw#Y{܂(YQ:d_mG^BBfg2dM7>V; oTyZ墮a<% 'yR؉G֐)zу)-aS]_̠9k7W7r82d0>|RJ$222عk'n|8庆BWCA-" >ll S'm7V(F.6}C6c&Nbp жU5u?(dYfY5GoeYW;(Y4h_ jZg`ׄCz88|0mswDIq'W^Z^X"\޽{PT-06HDyi)9^CɫEa1*.ub ;=N#j4OB7dT>ɉpۀQ T3~ 餥80%K$INw)IIqX۽܍#GTBJ x#gj] =ȟJ*qa1t:}(~C ƍǮ]ibAUUp }NIqP^\go[ARRR1v(FTIQCb4?>L&W餢?>^OiYAhۜ{? 'e<ҋ̙5hHMiFɆOZЫ^M "byհzD IhE-ϝ E>%W}r{$U j%*yjQN5j[pB.gt~Qő Iax^@Սd$Y#Ix<$Itp <oD^|9eT(٭౺pV<蓳%R^etǃuse۹sHnvcKců*,PMd9Ru HCaa!<\ 1[7|BNsNs'3s~~>!ի#??^-W\*$IŎӣQ_ŕn^ٽ4hE,YL~s-qv;oDx~ƌM^:Ǵq^R>NzdG3-/-M1{餤DLkq\ \$Y yXDd,#m2))…SSSOmDQ9 N6t`4VQi)涒x;Fz=g+PI.`,6iښ05ԽltUY %whMZ\^*AHII*ɲ?DQ (~Wvi~mCAbJ:3ddb֬\x1s &!)O?̴);]wdCǷ}Ϯ])((`Μ9c0T+A@e222(((_g׮b)ÏwѣF,mbGhGR9@pKԅZaGs`gt 8( j䘾Ef^d?a$)đ#݁^!isn 4)u@,in#yLZ-FIh$%%3ox$a_Mv$w(Qؠ+c$&]SorSH2$!ɲjP'tF!{|]%n/(nQQWVone@r0:ˠD Y-Rzƒ6"r7oo199ؼDjbq{h4VDA$!! W[]ãS!8 .S'ֽFQ#=LDUY sgl9&NrzOLqq3gb9kE[iCq{yE.hj$۰( (3yqKIJaӍyhs*( K^lfӦM(v KZƿjzF ?W:lsWJѐ.-)QW PyDYr'fmo utpphx~L:^zq嫭U 5j.}QW@]}}޻MbG\p`sHh&X⌔W5QSSXBjyXh'M^z^}u:xIL碋Gt؍Ah4$'cmC$J,&7wI8Zxʵ8ְvW>X l TڵX6JK7oN@WQ_$MM/c^aj#&]x(47Cުơd$?F)SBg{!pC+hBxkb6UjpA@s{KEuF/Æ +X V|@6lX/9,G׿puCY f8VI>.N8¸,vxdߠoY/tK 4vCcc#gq[~;#"~-HLL.Sc{o5kX;tymۂJKKlw}W/hnn yާ]V_qU߫H2' Fh@zЙ8¨ ( Njf}QI'!1 ;LOɅ #vGp{qy$N8Ȓ,ݲJ<px(h(lF2H뭐$Yp69!>[O][MqKTZzǃF0<&L@~jE4Z ˅#J z<jb1D*BPa~ի)yfٺVKHhBL̶3@).*bL>"Pȓ$%HxF3dʠYZZl!ܡ(\BI⡇紱y9̅ : .Z|a=Y?{&@ee|&!ǡ~j#"-TVV8 p9i3΢ERX Me '-~CEBJ$IcHNV W2Ľ _/9,YT IZvfTg-*[NUVT-C`Q5@,P/X+xLU0} y,s[>}+W~wz#f(sw8 <,|:|K7ڒ"1t:t8jkyƿond2ѫW/ \MSO>ɺE>6pj'`i 4Bl?^Mǡ*Zįi$0V7Raa_`L M\v-;.$^tr~'n/fs̈́$;e$I\HQh58^uk- (8ܒ rEV,0(%;It^\C{D}\"zΐD@݉1B2-'-ɖ;b*j%W/:(BN\L&MV+۶m#33IRyvVF(quThѓіtҷ!|7hGfz!/f 707;? ࠳7n~ A`#0nX/`FF$cm-GR&ߵ#'uaժU\wu3AHOO箻o\yfRC߀~!Wftjګ8v0rr[7Ell)0~+קy#\k׀, h4qqpk(/pbEr'7j@*Rs\O>49{[o피ˈGEdYfǎ:t={;{n*++)//Sz=~?SE2wndX\yy9۷oGcM4l:g s~v999AjvgM@d%ŌJ꣫Ҳ$]WaQZ7;e,FZ1 +nRQ)*籇auP;N^~$nfWXvu+M 2ӑ$6 A$ ضcncWneQvJ*;8 Hh5k&Ͽ'x={ԅ# 0w4mbǒ[e!2Zó$IFHNNHbg1&ϋ1Vje A-Re u봨 ,:Ybuj4U\# r&R[M3;JIQkjjO }pFgQBիJ$|cSkB* <+ץ['$!Iދ%\w5 @*x8tZ`{@YEf$:Ls<E UUU~qLsN"4|$PRRoÇ9x 唔'ᠦ(&Shۙ6gZ&HF bBʃ±- 11|&_6O  Y +7o XB] IjMvUP_fQ'{܅J>k{Į >G{)H(mj9#Ʌ1SHN^lb2hZ|NN' ,ZП6P5=m409ooeHR2go=}?Od70+$ s\_jtksYQhhl!b"ά:0]4] 9pZ~WQq}Ht:yW6cZ-SNeժUY"]zI >U"myY )N2CG*|42 r6BeY hT«~ Y$McZ nMcqA-j;Ԥ@45AI ʀ¨)3I>Æ!^k ,ـw o M[9: 9,~{u: Jh`0  \P I% vXLy8T)fus0ġ̀,]K/>s,,:Fet:999=rJ֬YCrr2&,TVV;ĸǻ6u -Mt-P4@o FXmBm CeuY2ZW}`xSwlSF@hVhJ΍g >>R>SJKKK\,uJ jTH[%eDTCEj/ރBeCp`?)٧$:77_2l%KHLJ$55U]hCw~AhCwi|_{i3pr]};0)J ;LA])S92 n@D&;3fFN-O(+SOf ̝;UVqVKSS˗/''W.J 9>- k^brCkqB%rc=z&PiӶS^^Nbw'&pAQD:EQ“A% PS#Gzow PM#:9' Yhsl6q8TՊVߊ.r0&W Li]sPh}}FL"2EEE0xܞ R4/jK{iQ5WS3&5$;&hs En~y8kt:yĦR]i0CO7+ݭmp\$OKKG7rX̳8N{}n\.uD72o\W^ͤb퓓=YRY٠h]3&fOD\9tV,455( }ļRryؑuf)/oVw- -(bsDo$?qH. YVhiiJlxnR<}dnԽjjԝomjVq,O7G[$n)%C_M5~ T t F^1r/~`4FFêo7ioioKW ZЈD!X/P4 My3NJ׾vBDU&^YTנjqݜ:r$n^OCCCơ]屹 [M-vS8P]8`vwpE%&p@@e}לּk~0atCD^x9.2?y8RQE:d0.ӻ!bk4^w}5˗Q}(ee<#LF?O_{-Grznys=z 6$%b3wZ~ss3MMM>|+Vq9_wMU{n)=XރVGvK P׃&CTFU%QJ3$ Z K>#lP_o%.n-N24l6-J,,xx&L> )ޕ} $ 4=ۨFZʽ$GP9"5W-Zt:]PUjڡ ?=x`sž#jTPyuv<=s:(oR圳fά=w y]R6}>v Q9r F9k&N|x 6R㡾d) ;7JN@QT{$ j/4>iYqTGBʥ^̿eTVV2p`UvSݩϷrQeb?FtW{iem@H }Em*i!҂%.>p?C gnrmcX0LNÆqͷ +' o"WXX\ìQ9; @zZ i)',: }5Y̟7k\yɉ̞1g\ ٩cQH( 2JNBtk][Fsy 1vnp8jm6}Yh4"zVhIcc))Dqq*Ҋ dB=:U8x 4tʯ-MuAe0ZC胺C'XIaY&%n7O" HNVUP:L4T7 0VgVٳ;f !S2335JuTA{[ϡ%uȲB) 1}_ (zzs;ozkY菭)ng6h[rT ;_(|aS}̬G ŝ/>RAyDyY~@൸G  (_XC-2 @a6IO@ (xG : ٹ};"@P, fx+-*Jms@Pp 8"AX}bpl6MLL AF#J*hKZbEwɈZ.iq}7Ȕ8,˨Ԛܗ,#IR`4W!!!.ϙ7Íb4rr)dIFSmʎLڵ nIgF 9a۸%]L&֮T*UGd2Rj\Uw<ȴ4=BFM=@jj*GaY+Tt?F˜Mph~ԯGPP^4jfө߰1z3zVჄ<B (ߔIgϝiԬY?ԐJ̩4m(Ppf-ZQVٳ, %'IEN:d۸Jm+6A)l6/:]NIs0LBHppN+/WAƝ/,>R!)A`PPIr[1l߶6mۺ|ӧrʃ=JIFl鴩"]>qFmYd.MłNbAeu-I+J~oB IRInA)/,>"2{=KO_`ŁׯTTQ{@` ~̛uV. .+;n-q;_(|xBk:Ēŋ;aeeVp;ml ,7vCn0W[47̭CƔvNr9ÈB4Fِĩ9 5kӤq *3YqGw,46 Q/J!4JY%j=֯%5wnϣg<%sB2:&8PŕR`_y6l =:!jZû6!_%vϘQORd.axtg<^Q^\=$߿%2liEL=&ybBiL1H k}M;czW#KjxgTR̊wxmʏJ6؆}]*|ל/ȖU><nuL<-[2o 0ɓ>Rt"NW㛹_a_f fQ;/poM軤9K[fF6nJi^:})>¼nsM\X6Ør=|lo.za*XdwR88*wХۃ߾=j@ \9sZAuiC8`,u(tlO¾\̑uػ4jmC,I&"}q/hx<ح3-"R8 n%q#Yj k֮9Cy`6oOV1]yX(i&v=;*n_!xB]xx.^HŸK&ǖhڼ]ILJ{h֬9'}VҢ J9Ez~m5<8~Xo [R r:[ug`VTM7a񠡬 ڣ8st5,Kz,`8>o;sޏW{xkID?V9ˉyy&"&T۽kG$&69̜Z*^{{zLdw?`CSL[5kֲvG´^",IomەJSW7\L ٬랋eӠ}"7ʴ6p&gfCT:Q5r I@t6g@I6;z3a_,Sr>},0'ĐG_%o=#2|'zyv9kbL4^ NϟgLpj15,\/G<{o/M\қ~Wq5֜T(ecx˾_]1NO*;}:>M~Nu^ܗ}Ʊg'i}xHѐ**/K.<?/ |g,X8w帰0,Z/3?1[{0&rvͩ>)1ty3ԛe6~ y^}޷?3OOߛj< ߯ûb^<=b>;SR>Oy5d2O .n8]g[Cxs) l=~=Or6ꃽ~F_ɤ77gEx7{XROJ-Y=ʾpe*ULUG_k(TT!HW|򥷕!)(u'٢+0f"g-CY~S⏽IɔAS-Fj;Pg70^B)[0g`㧰Ih}dF6k4JmZSz#٩Ay!_.5YBH|%1Yĥ4%aQ;7OX4H9W1y) ]ulRsif\}7`fMFoupE*dI@qgY49i=rfM[ϳǕ,Qt?f9PI ՇN9 ?;g`5Z/F+[>0q y|l' %Ō,KIe?!\K*_7yt:FIrJ2l!9)``W]rdaI‡,#)|23?`WȠPuQOϾ`j[ZSt1cH=M{Ȣ Y:q|$\ro^s/May5p3学CȞREn? ɏu rD'{&<0]fӄ}њ.FhUS_yp hVS)sߦ1ÏXj5UVu ÏV)|bC;aul=Gт Rc`?,_:DzD3Jd$IRw?aOCgaL+ʼnVLf%?Q3o\!x><>xŤk{-P7 ~v2%Lϼq\'#5bۣxK&>A5 ]Ȥ+1S!eZ\uRɉ.ًN+XV{ *pG+ [7Kd**!rNo[ +r韽$dԱsOU2*'EJ%وl")Q:&VQ`h6c(UOKJhvzʆ$N:@j4PPc9o|yIWEBԥD1-ں_3q3<󌜾yT!$F3LHAmyTjNˈ7.GӨ;TDPÎ4P߃dfiݪ5[39| ϶ FeFr<YIdT/s1dTTm IDAT?ubiƘ111\|Of\?2~eZʐ<5c.[ϵC`=UjjH$Ewy@2~%$aKtL6l2;_X,HMߺh"%Z0h|)9-g R" %Ds+6ۗR7Zoѓ qI |3׬KǠG>'Cw JTr}̎[{h5|sG߃ߙߙ@zuXF>S?D(iēsժ*tT? PGPQS,Տz~(ipg 8Ro=X73ıyj~J|$2T4jۚZ7,Ilzo,-.f;rG7Q :۰ c.K.kv:w}@*iA-\oYGzLNǷa^)W|}ϓ8"35_iG62mp * _$j%XLd1J5 BE1@TRJM'=&5O[)<v݂v¬T{(G ss,ғ j옷Ymn)w>7ͬCDd$ԾFT"!;ي+*UNUt9\rܕ/njOE7U>4RBE)e ־HwL9"{C0^6aه@_ }ޡ`Aa'׹W9, h .B]ϝCTxpy$IZjNAOL^ k 0pVG`wg&+̀2)h#Wخ9 ~B)|>a-\͎lY1:\v3my {т_&fw`,n F" -:;^oU +*[ƒh vM~cՆL, .hv  t}UԦ cVLk3M kd풚JPF MP *] IFt և9+*@ e,LYuueX>Lع+wҬB⡜yϭL~hUkb9Z4ff!ϕGqz;;cк"j/u{X^. do tݪ NW9PFNJfXWs6z5Acpu:|*˿g0?![!r|NOy)Jg$E) cH1i`Ϸp^d#;l7~˽xϴ9p2.Cg .VU u& hF-t,sL .G倢aAɾg]ǒ?fvJ+Yr0 jFi2 HU߮QޖDY NeUNO(˘38/ZFLq'4ΞJu T/$gXN8Y]1`=6jK#YH\5rdc9( ȸGB2<*iBS\ɱ'IQu;'ügI7ȮюBҒ1Vz¦v̈́!Ӑ4^x`&ipWuSȶyavZY4~5MT @6\@lMS deʰa,k9Yn^? 8{3+ l3'R#ڵs'lؐHݻKH2AIsQzmm/,GMLm!R&nRx3.j0s._ul4hգ`0z.„6mUT8|D4ʱy̫cPͽ4 sA.T &*XƒLYWmf4ϡ-Ǣ@Z-bpnPuqQPGQSLUTxX O"]zlʸ>[ n utBAPF\\>Ep]*7~GT{6c8wMƎG""keWߤnp~AʤfX33n<Y>ߝAR|q'F{O>ӳ%ZQ^I^A&MؼioBΝA-f5n\" Jl26;_X,"b7?3t/ \uÒOq=3򗽃ܔMh!ogY½1wZoC&baOyLl =؜xK('-dȑ}ri,$YebBߏ n碝ŋh`RgSs7_G5R9%Gx9%w]Ai@T݈ʼnFõk(c"^N,:ǹΟJ_ ::f͚UHNNY.WXn|eQI#nf[ss€.+R9祸Aw3}b1кiEߨ^9ػಳoo @v܁F땍Vˮ D(CY@ԮSڵkPn]jrݣB q Gjr95&~=njqybp%!ਛm/k^}'xayp0m6x,.\V{}}|ެsv61ԖaKFff&NF4i֜ȒXv*ARRf_'>>J]^h(,HxDT[,N;ʥKDWR J3sҭ#''NGDd*T?RoBNvW/'xZ__*o>T)'G^#2+W/ BVf/[V{]oYVso;pfKT\ X,RSӈ+bY#8(RX+z"*DRb%@X,r+ދ;_X^|^C7R1%dB'oAyd[Ja(6e2w݋7R 筫*I].ųk ?JBAHH0Z_-Q.}J~)F/ DrRiW)L&|o1wH->>]hS^mΣsvj[W۵%,sF\ (_>v#IR97z< (ۨT*N,q`~) Iv+D%{l@p G na򄽭p p\ue8&>#>#>S>|P#mrtujU% 9.$'r;_(|a1J^69T*7@ -|G= b @ @ (@ ԣ,cmU%ϫ.{),am p_,qqod$9l.ܹ0)k)/$7$..aɲNʷ_<pc杣ܿM$}sOLy6B9Jsȧop@ n=$DtrsJy>4/Undŷv!H5/9ϗ! m׮MrǼdl2WekrIyyvv_䤾dy/Js}cyjAg>W7s+V8sbuP:f|ssty.byGcx^SrO$)\H w12j5z13SN (A1Xbepܹ+_ }VF \"@  |cCIRj@A @ ( A @ ; e^{"突+;Ч/JK"m0u@ 2G4Z5W\ޔN@ȦYN0ptu 7YGz@ <#W'vpHړBD`MM$f7GKZ@ tἏ3Th6zL98}zLHhRch%2dh|r9339BӴ NנR8cR@  WM$}Qjn|xG$U;:ck92a9m8.iHm^|>4YWtI|9 @ ^dm+c jՍ7z.L =g` ᷷SpZ",8'Zh!F >:?ss>8{JCR&hFsc"($K @ A~5eo rBqTP⋚4Lz =A#H>T5bt{7CB Z i*(oHD K\zbKT\&zjn0)dzeq A1 ط.ǚ}rtnύM9AMɝ? cÀhF #$H*!tlLz2 & XS@Ft$ǁ`ծNB2[u4AwVRTPKؽk'ޏČ,ٛ "1sj:1pr`ݓ⍦Zș_Er2QbA'|dׯf͖\0"&wsV'%5y<Y-cxf|3T/%S]J(9U[x授xnWwEM(gݖemۥ?qYR o N$T:^}s?(@C@Pԩj:hm싏`r#_`:0I $TJiw\FRL9x2+WsK+ҫфe<39@U^3w^㉉sA*Hyox>0wۻN'gc3#>x;bC 0ƑG1[Iltǯt. /- ߅,e+HMNFպWѢ 5h֝2ew4v9vO dm[wfO.BBe_+}ux: @DvZmi yw$s՟zLgAcf:DVzmLWeEq* LÇڳpj2Z3uP߿#ez$u( O|(*{_L YEv}1U}:Ol z^b2w_B p[" eK/Rˑ,ҁP[v};J[΍,c!o‰˙h>ȷeH]S]7%]@ \tFdQg"[n(}Ӝ|GP0j. )}V@`%T WaD #2,!|CC*^v/cpi>.5_Wp-rPı*#a:qUŻ6URh}o5%"V"]>PgޚдQ}b#DE2ݡm5z+9Q<N|?i[攒m4aقYP(%,6i?#Qfbo3qe->{& µ]G=+FT eUfOω0AsS[Al94P;mVK6`81e`X,Mܕ{ܹ%&&DӗeU< S3]o]fzwoVX,h84EeΈ%զZnhmZF,*sv0g,7cj,`X0H 6ԛmZ[ IDATBq_x_J5WcŚ\;IX{Αy壊~ j{ (96w>{u.9K" 2O+dJǎڰ|mD`*knƜ#oL]$ =6a+_2^MKvS٘_bSV^hDֱ8m6ևa@=ub$>꘮bŖe]?ōnI PUIU /cƹ !@ (dO߼k•[HMšd|"T=pԗ_Sր 7d䕄yfc}t W@`Gt&17[ ɜݽ%3'2:#|t2i1y|Z//A$gcW_U(i\00g%/@/aFُHҡٻ 3Y[cض?$wd`k2od%mP @-Z 7Ma,iCVfr9EL! 5ڼ|JUn=$g`Ѽt?͎_Ŕ*U2|O=.]bϮtr%_pSm5ٿGx~Ex۽mi[Mku0b=60|I.Lபd,_sgir!s,4`@9=|?Im Wd@?$fTLՋ$m= OɋM=%'[ 69zmrt ىdAVĴ KvJ(-֤C]Y?ÕV݈rx!9]]a$ɨcv4am1g&PEެ T+ 2<ʗoImI[3&>',-kҪqĭbޯ r4[V>O2:˓Ϊ %^U4'u+SA[_O~a9ߒg^ibBM6bd?]=8D*94\IqmSvXm/S]ԇߘ~/X~2WRQre:u1$#11tOҿ`.^;uB`"fVyMWPm,SߜK/}pỲ.T\K3&2sPGV%OUv8,etې$oe:p. F:<_gfz0JqP4[8;P;ve5=؂%$H@lMP$[Vŧy猝nhPч ;i6<ƴ %dsT/Rxa@eY~ihM Y0g% 6L5!<" 0rg`y@Ɂfi$>$QehSQ,ғeU@sf65x=ψhZr~s?֟M6< oK2sedh5-:nitmz{WyGl9&cGmgB%?t[D/Iuvڧ 󵇲MJ~~:)p_:zUՐ;ۆR:&Cw X6q )FԤeqLU$F x:UZʏbfT!ԸEJ%m"{ŏFďxo0չߋQ Aь5/Un~/!jOX8qgC~]#;$|4Fd>1tjf>h"3_j`{*DQm''fV/WK_j)*-;eF{\Gzsou FXY@ʆi5*&.Fx7X0d[t@uX2"0$$Q]*`dĻ)+qj7ȆnQ蕨R'1_n L~zo8K-m?LYE M]}bӑdk[AxWS!w_^JZZa /aI@Px~`]F;]@O9as-vJɩލAϷE{!Ӗm6@LٮA9a,)q?06ŷQFǻs9yw zXuzYcӥ|< aphȏ}Þ? 8E}R N\@ bgaPR3Y8c 2?~^}Ƶ+a&|ۓ`3mE岽 "ut93&ǧ[Դփg_ɟ.&] `Q'A} _k4ؑMBUNIIcd K ʲ$IeU I_-'$$@ 2{~ 0^ukHevTuIe'kH[}o)?gB#}iKN⾇s#d V!*vxfX2Ncp:'oR-u q8=!bҷ~>͢ZtnٹU4i8P6;fV[e@`;*vt-ջ!Ay9--/#+']^և-63y璳@ 17Qx%w|>wHHoЗ{"K'x{~|pGi7;~{ چc YSAU}O孶^3L5ϸh|oM@FM3 t -gų?#83We 0h6Z7Q3;ُ}nsvը!_N'ࡃiCPx&TԔd$'{0kuðax@ ^Kƃur􍮞T'G'ʣP>.:CH";.ږcͿH B"';jռ`'2/\@(-d@ Z{}U@ (@Q8T*$[c\Px@ Mb-RY>A>@b [Wփ$ddNi'Iאn2d$$[>8GYA.αHg -"O{8ˑ!,˹J8ɜw!W9]?kwd FS;A|fD_V~KZNi[$dK`[Ƕq )վ$r)צK_ö0kزo!/&$Ir)=ϗƥ.*]A>ȶ/ )sYk~;6/{ny.q#sAً+U,>aKSCh rE?5څڗWjIQfrv tyY?6X4:45{9p}$ΦU/۲=՝ڵ>IYC K|@ F `&>BV!.gKԨݥ"[@W[~LV+*cFUN6%m׷tBAƕߖw1|xc qb WRb魯}S\2o/VP#|ywqR?Ino(*"gEDXDY~z{/) )}vwj3;;3+N66<IXUU=bwWJGn;m(n 5!!Wz)~lO<꣡`Tπ54_8B6L<\ڹ\jtAIh>յ4:scuk'nШJ7 _$CeKɎ>f u$4Op^1 ƕ,E^Iwƶ}V8ICpE|>ӶUT^Q͒9(D ZU`~Pװ#"3(  L-[K 8dMߒCS7cv5zg9g߰=bz29Y fԐL]o4;ᴶ0) Fm_NEO m.r^|@&l S&vʦ$^p2M7;>x^\kwS/K'Y(=/毣 FYoۢr)9{so=fYSKB][g[)7t?\uuzy!/5kz̍-3~׭]o޷^z0S|b4q B[-r,9 Ǫq/ׁWdƺTRoY@ 4y ax8V$%XC?|L/[_++"saykm<.!< L%=Jw=c8 ~>o;Z[SKB:ˁ-̿jO}=&\Gi==4y3D>nlzqx~76}g<6l;HINQ#딦=4=g{O$7 b~澈Ϧڗz W8~|xOk§VY_}gܺ?9??ceԻyIyN9Y\l}2O~op0 )٬l:^7NSKB:+}ξ5,~Ë/^85[\քǡe˖>-CKIu6vl@۷ӻwe~?ɸ}><iW1A7"mu('RiRYPXY>>CMxo&0Ap@nb GsYrz^J~<C@ .%!Z[tx"[Hu5)p矄>QF|Te˖J-RK Mo>6mD=|ɼt)6lnwеkT)T=14r@0P:`!ܯG0pjp> >NP3!!%@eW2} ;{b#叴-I`s>%'L ?<LϏ701cu~|F/C5Mu. t.ɿZT@Lw}'4]r+M/M=ߍMߙ"֯tƎͿ3v ,_?~)1[Z|RP:X`EEE1{Z*> 8w(#"hh>ILk#NGτW{~ {syU;O2 cMO4&?1y}hq0x>%!Z[Ced-+ꇧsz窆J-[4Uj MZƦoH͛`ۙ0qy{:r,[-[p1Wd-Er7z=iwL*l{{|xTh9u \৲Ô>@6]zCq !T[nD^(]z$X aJ+hrǻrR*%smL. L@h_f00M_ ~#.? 5\\6g I 5ȶקЍM/5ƦwkNno@~?N1G5 }i8* !8T hv ' Gp>Țp(R JcOsˈsJƍJGѯ-[qh }ZƦoHZWq!.j~}^/ԩCBB)aQ^ T>:~bV~"`&?0e*"m#T\C G*+/(3y\.xǟX32d(?3aMb37g]IJa~c>3byz12r˿ bcx­sWwid\\6WWȝDdK730y6Te˖J-RK I&޺0 Yp.h4Lw숺zܹx3*uӴvhJ6xb|qD\FV}Kp*e*8ƔæE)Ӵz,f>3CNt?0d.G;p:?6-ѢDGeiԲeKSK T%ml5 4(*Ĭ0FtӴVD7CQGcQ`g\+UT? "Kԇ84ɾTl{} RPnlp~κkY~=ee(1CE>h88UqW7uǒ8etvS̳ ?fќ{VdH'45!BAM9Rq,mäC˖561 5kVV`/5 r7WFUa!#xqCE"ӽW|>@O= x,zٽC,Bvd1:_!/a v#8gbol]?g5fL!ѐ__xqBh-[-$phrNiMPPM$$ӳgOq&IT=fٷ{77oaNE!Nq[)[¹#l{Wpal>xNF[^6XrġN{ET:;$wov7eDjUGi."ӘjEӐCtq.7s!#N4&uBunn17]yґorT^/; 6ABbO:=40 ,VGUP>:|E%x z9V]p'b4lv;I X  IDAT4[DEdf8hV6P)hhl8`%lsܖOp|Q駌<Oco}MCQֹ<СM7k9׋p8(T7MR]c[0p5.'2cD |OmkQT MPB5 _@cSUUAQQ5;#yw~ӆ! naJVZ"Z:scuk'"R[e}2j5L?t =-e&;G')2LLCGu 3 {3fzjkgpYgq3}9x #1G(u(@{oF Iu$nB!y5YePSxh{F/|(;`~Hĸ ^-B!DgdV>T3fu{"ϣc35GCg B!آu /8(FG!BvqC^WOjZJ LàwC!Bѐ> Ucjrr^H+X!.茚CPst-\߅QԸuQ֩CBB)au!B5b}F)7B!B4TDcU' 6RҥDfHAIm-|r* k!8džЃ## jvG *V)aG!B!IF+J qc @cl)s61=:Laab:c :߸E+qӒ<6ϊ2iȷ'` ]3{[˅1ZZK]3w3@ѳ8uș>#ŬKGfԐL]!B!:T9T\:~j>0) F6IBt\r}BtF5oi- a4dO N>~Hĸ 4"\߅Qup2kFUjxB!#A!BN}B8Gw4fa !BѾ4uH!B^P@ӴB! JM8߱d}/N]ȥH9!.茢npZKճo0|W:8 ܳ7~b`;WB5 _@cSUUAQQ5;#yw~#qB!mN5~z.[ʜMvLN1Se&y󺎡h7dѯ.Jsܴd4O6 ﳢǩL:/ V!BbRC{oL]-\CY:LE^~FqLQ pb֥%{3jH Ӻq4~m뮹`jo\>gkBcx_ Z{7D3wʴf(Mj055Nm‡3%?Indef*,TU%x^=Oڻ$BiEySOw-;#iN. Y?I߾[@YZh J>}9KB!DˈUEC6ơ}Ҷ[n}^^Ok` p8tڻ#\߅iF(UԼHF FqdD23ۿwEt`~.]g#m|-Eȉֱ+茢=zv=vAՇc>W=m,VlB!YUUɬJ'Јk'aTaX,, bXZ7n}$HLL]jB !:8mjKVivI/aXl8l6VUUZAi((u\Qc{ѮUBUf*ՊfLeU?hM4M9 BtfӴ @7 @oZT70##ahVݷ~GPQ`^y"BA5$nxCElmeeЖ &f|$$%00'Ԕ;p&$?gvrה&>/7m]^(Ebb"/s2{l>l,XQB4Mc͚5vaQ8`ǼK9 v6MkS:y [pa;_vѽJ&e|Syuf<* Eq2r~[nYײ/oqߑq8OWFѯ%9qދb,tm@:-.c$8I!:^7D5PPRb; l\RnNIN VByRWIoXv!{>/ YgeǷ|u/ _> _?so,[3f1\6)bŖYʇܿ b8_XdK>󟟍Xۀ )͛[IZB_óCٯwڹ[#K4+/!Bw„z}㝻w1`vkJ=n7 N \RF9RW1Yt g`KӚ,<Ea=܍5=G(kg)t}(;-t9(̯u0G&\?.fUh+7nK5 q0)ff֨q 7k:qئ2J^~(lp$3xtdj)/C6Ƨo04(;W6s8wY%z0w~}fost XeabˤXMA01 v:t4Yjx?ʀsЬVg-X8KcXl) gkXp|/}/wɃ3L's}3 %~$MY::K !DGZDL\nt}*;ԩ5dQqp:*~6LSɵ%2ehvG`@UUfΜYI< CjFP1C2snB|]0| [9"S4A?LG cPC8y\w2΁X3~3J/Xyӯ䧷_Cʨ3kɌH9[p$c,=FpxKM`* nBtÍHB̭7{ǃ=(AjBVR>-c%6LÌ~~iӢ.4/]p7hu~~\aC0e:[.`DjiVy#^]è*bFI>*VWfUݎj.ߗ-bc>n;}|5;="Җ$R*:` )'Tgx3#!vj4ư^'2r1iTF]8Ғ % QWPO_|ʪ쇂-)o/߹u4V!v!Dg޶nCM%6c~{<~.'$fukH[d1Wޕ̜v~]ѣ6v /̩`]~U=?/g)đbW&WD~N8Rqحcbt\\bKiq'x|*"m53QF]KFp^ 5q}7㒧cr;o#ϟw%?jRqT̴&{o&@¤{Ϯ2s`CDw !:x׾< !kKΏfZ% 3Ʈ}24 LC=840[/k[/4F~RUy{;  /Dp曻ͽE Y{*~=lc8ʚġQ~6{Ten~{o! ne8܄@w,Y%2l/Ƀ1lx+*A,;aGoR/JHﻏVS_S?jr,'n~ .B!ZG qӤ#ם6mFu_0l~> ~3zL{IGW%o~v{j(j/4``.ɝL{~5\(\s5\}դWlo߾aEꪊ4Q.VL[u Z~â1uQJʠ&~Ӯ㎉-nvci}{8 {;JSIC'nkyhbLY,TUEQ*o/..2kciyUWV0-qҜr, 4/24bwS;_%34XQ f 7rٜeu(}ʋ(y]6v_6"_I> DžUR{ r\ބgg~4%E񔗢(bnJK߿7n2uI!j "63ݳ5F}Ʋ\^IbBPTԔl²ŋ8?-ؿ?W.tE+BDm+2S1k,6<kܦJ~An֌0 <7^rAϞ=ٵ{.[j򓓓ڥ ={=hM&4!DCcԸo џ.Y]p{shN$6qB![AY}8a7[G*#봑B.茢v3}V6i η+ )B!DSp[KRm:I`XXXO'*[[7BO?,/jb۱lv^/>׋ǒ7DְJ-/68ˌBtF5?QՖ6Ԩ?|W=|$8]8HR *k]\s2U|> f^?5]^0C3e'!30L1CWGn9cBb6hl6/;v$qYy-W'w„]5ۛ./VĿ{IJJ"## zA.]ҥ Nłi$%'QTXP\\Laa!wkB Qx Ču_3ma뵃 ɣ(^7ɕYða8ٳ'}!33t,ʚ$-Yj7##i !BMΪz81ozihmC<͕[CPH7jHMhILL$-- Abb"6 M0 ԴT^{ut}qk~@V{!D3r^c+*7Ss%څAaXp8b0 ~?=z'N"{6k~\Mv-iB*"沘iqhU8F֭iVL@O޽`Mut ^֒{2Wyxrا'\/yn2BtF~ULUjơ4 ?89|@eC펓I3G䓇Xq׫ IDATWw{xvnp̱&8i:w2]z:끌=zqo2T/]q'|*N_gs WpSx8|e #/#gɏ;ٵGg؅wqäo@o<[^k^ggcI&]/x^|+~gfpj˸$](ߵ\B!-*P27͑h_pԨaG֝u't@]̛|d6~C:g Rj~}/cKra$ڷ7}`Wy1|_Nq]hV4F>L/oUûk G_COw!S/|~kP-m_+~ޞ{#a?Y^? ϒGxyxf5{!?ʩes &{J Xs]B.u4vTґCcj #/̝6n`oٴqCCfX3@ a躎a$k% ]ѰgTz]7a<(6*l;|_Ѓá:c׻bKK"Q)OKWF0:vzqPPI՝{p I,ߋKIØxD_I*7 l~gٹߍǟPP콙r|pByEyQ81'8 9!:\\7>۳gk$''BJr )$'`K&%%<^%J\JprugatE[TI41c;Z uUUZc=%l*ǝ3&UYxcP^YyhI:t(/'ZzftKTH{.Չ5@Ь*?뉖AŮ%% '>z*I~=D/5OdrӰ5C !BTW;5}{\AC1eI7A&JZug}3#PY0z6>S~a-.8ߚ kcG*?J§Ve8xr4DuM;zI^3|,UJ EՎta>#iP}0a}Kfc*Eљ;#!d7LVVmyf'ێ  ?&d%,(4(@t7G[Y.N p~ !i[(J!^*7^B(muy6ŌyT T1?#rih,}Ǽ|v1 l6[^/>3r9wV0y$zɈÏ5]ϧCkÏ7xNG u>' f/?tpOtN'r\v<&`9;{+3%k$'O=ƃmOOB?>4 IPrc MǮO>csb̏ޢF Gϴ. t!W/G=Ƶnϥr-t9/=D巈l9O71횿r׿凒,͖BFQUM0~_<ݺʘIaa} ECU (Nmb>w+^|3rly!//9rϊa 6;n"`53qzҜ n. C?MY&\TϫzL^*]O饑Tҏf_ReeyKƼ?^k◩cPs孤Uzw*2'|]r;82%̊[B!#EQZSTP·FQRԪX-tBYyyb49Xjtm/Ÿ͙hj{xsN/<"vÁbAQ èr̖-[`Ӽ;/BъXHLp(U;Z,ߏ΄I1Rڋ`E|q_#ϦKpٶm$%%LRR XVTUEQ Hg_p/w3BtF'EQlXiTiZAQULv۷2#8rCbqpc0u`wʆ?~ !B5fTsU*E?%󑗗G^^d/놊ȧQ7f%  E4hV#W5*vĉɩD4Uf#%%% rr|\IIIQA!B6'o>4 V~c 3/!B!:_M-M0@ MZ@9m0B!h&NĈlm{uSs%Qh49BJ3KpCM+FmT@3Uϳ)BA]5otqKnKz9^*ajA&dK2(=L10$!Be61 fCk|^>_g|s%ڈpXXuP330<&^ĝhP-~"Hc*&KIh!VQUwbO;l6l6vƧU|F%͖h<">ۙK-ܪb*&(UapB!D*_WCcDEcƞ⬪ujDw&*?% m'*\pO:ۏoSzy6色`9%쩒_Ŕ+UY"N?/\hiL[J<5Xy&c560J>.zMgvCtdwM!ںM9Q7͘h? B>oA[VF;ط]=u;|@sǍ igT> n ?0ĽO12v0]I1wX;,wLN2Z,WG!B-?DobلS|2/6>c}w7M=|}9Nϥ硇'O2]zL`qDi?Cw/.?:EcW% dom%?qd0Jq#<' ч1Kbۋ_N$Kqӎ xX/gCX$NAVwPe/]z 'Z!ypO>Ƽom3)9N ~cϾ|]xͮF _=;Ɂ%Iwdr}BtFfƧ ~PlI!2W0B16y m,dՂt;u}Sm|x]IO\l^}AZZW7=&O7xoCۈ9܃T~dYѣ9=v;ZMi\NS*-f҃o2\>yYVX;~`MCN9ԢyyMkOahX֮J咗nZ /mɭ^ڜy bou9zV쎨1lw?lA!Ob"`ge#3qV|jIDD|>fL? i}"V`3"]=6%6o-tgq}p` cRn6?s3 \m]fX8b^ݿի 5f8Kx9<zy.`97y{Xe)[?Fs q`)1VDž-% xُdPRzT_ JO|h:2O=T$jW7NR*RH6obn"iIz\o#| 0˷ob>(z  `艽x?ߒwb.hP\W[b_̀w>e:|[,廝嘪YLwa)3G8~S,$&YgKy_~9. j⚑\B ShN?ji|c:z,.`X^A.~}N>Gob%WL P)8pS 3)"Vj%`rlj3bZ6RMח`3n˯7̃Mg`g W_^(7GOY5uk3*TY's 1cg-Ԝ(w?W# 'U3)z@řZGYWbz;>2gf{tTA# b9bb?) vѬ(M R"-gc6d& 7aw;٤}r?O,.%BW^sI5ȡq"-ђMIQ\;mWz<p; ӫ2;`+t;h:>3|O$C;geqݙ8d;?by9ʩ\J1<͆ՍӮ=7rکgq]Ki?.ebN*LA#wa!6poD{">gNMrquk ֧nYL):'&nS Z8z ^Гчs+;pJ^s^/o7kn+8o'0dk|l~|3Ҩ69BQSE֭<{]IXi6|x ڋab #_W`#\dC jeH\W)wz@ DsL֚fqM߫^諉:}kͽK#ÊyKcM&|%U5$B*6tc.I, B D VPYhj4Kh s0svFaĦXb㏢Yg6II?{;ꡥx_̗ǻocWO n\}{l 5-~5A|_>ygāF~ԬTWi#?+ְ=۱zN @ ]VA\nXFPZ='GgpA,= #9.Ct<; xIèj)dӘrH.׿ŘZ_z?ό~qeuDWbfERz{OÈ#5TEwo Pv =C;V#?^xWz]Ia##v'=Fs,fՓf$3X`U71e<<XSH^g^P \`yDyӉpVWN3;d1oh< !DM9Tm/mb%ZN\*+xk~´hRѬ"=VXkaؔvYo m/h/8i7r =ms]AkFzs luOp 0G3זYU﫮wrNtTZ(]?0u`vZ|&k?"\RDؕϦձ_bf\kCm4zW8KG!OgUUҚh6򯨁c9+ T4(QK eGl*޳O$u6#R^Ceǯx:` +>wElIFm\JgՖ8]E#Iu즟€N\DDw IDATI&NϪ"R O*񕙰ICPnBU4 ɸk>0Ey~II~O;[UGRwoH!)+81-;K9c5ZpBɳŰJ^.&lni4Π~3]UkQ9Gمl|'b?N@$] S0P"*(`ߑ6qۦ]~ŭ9::~SUli0Q(݅eSm*͡Ԩ)4\ 幔 ePW)5]gaå 9U,m|y'd՟jLQrBNt+3#6YTUB!D}ZDU%!+d^HE4ppBbHbA\InH3MEXƘO|ޤND,dUf͟~Ȇh[I7_Z=UK5΄!U2.>76/~#@o%ކ~ݝ`);C@h7K>J} Cpeh>͡HkKKbw^)O1܁h@lh2JC aPy~ZuC۹q%(JVe6ۡafc!,qqv+ͦ[/ \@nU$=;;ǞP.g:;|}l{kSVP.w(XXcߪ#I:dnyr{3 NZ>Z_')V޼rs1~~>zpu` O0ӫ)Ҕ ]q jLC*붧qhRS]FB YEVOKW.[wamۀzM꾅RMN1ZZ~Br,%rX1z_5Cv(&Z B!FZb{ ^l*UjRf ϲ%bsgCLSQPOrrJsgE![y/%p15PUIƒJ}lF5BEHXe8!Dc%'0rXuYBV$BkwF!-@ӚTU5$^NRVQUWWB!bZDCc˴D :(%u5*l!~ꎵbJ b_JBT\'B4Te_TUBƓXc%ZIyN3G0l~r0 @IQWc-IU%!h 돤qh)TsYn#11Bza]ױۇiy(JѴ0`bi@1Oν߀klް"- b%Zϒ: #Dr:,nE׮餥#s=0 IJ$_ξ_!^?-"pA4(9xNl۶S_`vX~x/ͪ *yq9B~B`hQ$n5tn4sgxd"`$4Zzͦx锕-CMA`C_0;Ƨaoz!BW>k-4[8+{&QG"Mas)x-O];? o{o^neP\R̼)/"k|)-ӏ气z !B(ͺk%8T忺 + (y ` #aDPi t #HPֿ/,c𔞬]ߚ֕ך8Uڏ3Yߏӳ{[M'Fi1޿5>RC]}ם8?+p?iԌnR>!$CD Q 4 t6o>Ν!ƍ{7kҶm[: {́\`p;ylG,c2w,|vsY|=g&sƑN@Y (6cOތ8(\1xu*<ͯv3L 3'P`M\HkY$1 Mňkd/q;(mR+;gVP#/.>JYk`D4ͼtl;aL;ы Oi&-Zċ/ n|gh%P_f>\CqOƔ1N}2PVz6HYO%mh o?yf3y)f;:el6aR8t@>-jFun[ΚbqO. ﹭ wO×m < m_Pg& HVkf*ٍeN kW8gs3 gsŌΚS®W╟  Ai?a#߽_癍+ wBFA85C˦g6ϧd[5.2jԨ. {14zӍd-ߺqTtdLڏXYhm&^c84YGi2GI("yk0'V񄶰MKpw8r> M u:HC 8PULJ7,b{ G Hņ?&3jm} 8gٜ1lΘv1,+D6 FG(lBbD[a$:jNBB(}=l[X=qBQX,h Ӵv~":#y@ (L;1ϰmBR yl. Ϩ$$SC8۸l\&`(7>wRO">E|GI%0b|NY=Ix+(*"M O/oFߺ4Sl#ׯu7QlnV+5:dOts6U 5 :?F#uU?#u* bFWb & tg.MH&eVۃǢI;!;u嵻*\]%YC8*ICCaۙa"eSF))ɣ̨~)wbzy>AAy~9ĥXAm%bV[ G0mZ/P56S\ ʈh{. ,m ?A *R+ B"m~ v{QKAiS v,Z}ŋXcRS~:ڝιn 4jiR݇|¨v5uC_WmD}wP~~1_"V~m)=9طEKW /ˋV%|1ӬCIKMK!B^R'N|aG߅iZi}iZhL3dI!1YUbѷJӘvz[/SRn'i l'5%v}̬wS)Њ[LV=ڽ-u]gCKtz/Ȧ؏| !Ƚ55E+(N8:/Y ''<۠B)^+h!BwZ8BTM=7+=+P8k?]qJB!D4p(*.aq{u RjweЮ-<4ь48+X YkUnND6$B4RnʠaINn|OLTA~>+.Kxfˇ*e =:n@tն]3uu93cL*V nk#|C\UkIgwpݧø#׸G'673?曗3[;6rwpﱙh2e_s}Kd BY'z` :|z&.q`[zlB`IXTڦ2N=hUy9֫maIi-}3B)j=IΆB!D1Fq{:ݺ kD*A~XfύcU|>u}ۈ|+.{Xmfp1V{mWbXb?fdˑLY,{R* @)p@`<{2KܓY=IGX|+,͈X_/pC^0Ynex蟣I*ן7n3ILm!S01?0I_Kd 8 NF֯@D)ʾov9"\5g%9co4Naa>6c \u<2+N \~x>{;?t ;k&d4O+\!WŘoMvv㭧]Y~:'t on 5^kg~grSwD*/^珏=!Z{i a0VF~@wt*fռu1 qH'Repyt7))X[jZyoY7<1~b \ g{ף*G|gk6;o;n@^xKyqw ?O{>7~|UFhr8fsٛBi:ZxKGs-q71czg3:^-Mg 'd`u>I!Of޸W^ū~` U#,}z 'M=.4oW__߲`S*Վ}\Qm: >:U*˘+SbNdE&dzkN2@h/FXΰ!pi'`G~T4pw=юof(70?vJ#ǎe5IRZ|WƎL'{v6!Ꙁ޶]֊a#:tԊu*]!Bt8()*+'-Ey̼Krzd<nlHp L&WܲǓ*SUO^<ϫ1GJb[j( ;ТtW"n=Vф"XXI. w/oPwc^wxpVӱiv!]uxKҪ뱾c35B͊:igA?3[73%J)дvFJ9,֝'_.bxa4>}Wvk(u5rt]*F-՘tS`?=?^u* `DW &>ˋ JVo?PsWz<9)DB-ޛ,g G&YLUI*piN&e!n4(ˣ\I^t>frKcQS0~X"E2uUOXbEE޿B5iU‚3=/z~2X}e?/[?2*ld)/ f)~eϖ 6Ⓖw܋UCMX#EW< +h؂4TVK{(a.912OQX_ 2뀱K~(0P*E %bWnZ~ŸXM>f}ErѦTF HG~Lތ͂E; ټFVH0X2=/W+ *k(WUf=juN*_@0J7Wz0z뺼c]3_nDҥY$܋d o8vSb2KXx %FŶ:!JfmA!+d%nMד^=Y;O1W_Y1\P*GXe}9Y}P[|xU??0֜}XUDez\Z+ՔjP1tEAXER3rX6w CO!^k3<:o=^rg/߾T>ɻ 8x^.]Gr>Lre- 矰3s2SZH1]yO4e#NǷsoRILȠ!p`GSaԓ/qeҎcfqů'˲y;eq~\uD8Hі%<:cJp4v4:N~bgO3I3)lDu &(#{>3nYkAl6;|z }\-OeN煻N."`+mK &5>wswc>us~NʟG'aXr8lv4 t]G4lu\l[yUm˿ח7a˅ANv6yyM1 6O<o ^P܉}M>cB!DC!RNI؎ש&2g#x(Bqؓ{g׊]{veoghIjz+2hp=tՇ8h¬f>")% w\ywaD˜Y^A)N=i BC5C$_fQ:S2KڭS$mDN]d*'zS9\6Ja*ilMDR6MtyHkݖ1"P& KBJNBO9h)F1 nBΆ;`N8hgR!b?m{JgXU*Dv~16OFk۱3E uFf64M,m0V ]W(SGMlIigD) ÈPTZJ/q嗡W/HK"b22jcA(<8rY !BkklKWE/WC/#6ڴkkVWkӾlvv#:6r{]t ]tM5 Ӆ'!r]M'H۟YYXS0h,TtZQn Xwz$!";=0d0cZN[͚ ygs{MJZqvmYn M0"`Uǡi4Y5 q^Rk08M0"{! sVin7xP\lU%$h0jYB!h;?IRj*;Z:9-[jR jiцGMCu\];Eʱ&ic(*&z V[.|7=v>bZkFjfk|/>. 9B.~{JH]\}\x饜wUY;#K`EY',B?W"CM ?W%E/ݮ*Ӵ8kikuUD4rKpc%c.TTUKH@:u{.]/Wڡ|e&vO5l [y ǒV%O<0;X#<;A'Q `BQ8JK*_=qV#hCh4PX@EhU;VTex^aLn|>+x0 Qr2l;Z%z eˠ XɽXbm;3D#9,{Y^Y#)Wt۲MsyylēҺ'wԛ GMማ&Xr-fQ/hho|s>n {]ܼ|,^w=ŎIp4l̻n>6N:OH![tۋVh#'O[=e.R טdGlܙ 4N=;M&yl%'К]ԮTjK|—I߁)&NC8!HF8|eۭL00"Ha1 81բ )t1M b"ZD!nݬy]Zo$ukU)E]T1_OڄMʑXۗ*0u'g]i2Sxck Ǐ=3.b/YD3d6;8yvE@ӱSou&^r=q;oDYƋ|MGG>wB!@V^F"5ZAaAĈ`D"D"֣aXEPZ5D)~?a5秤@RvcYY&kd]9jP 65VC پj3ZIAs9t<պQ9|p[\ ;$:Ôv>&Aw rbjjN1gy/8NVYsyl_g_ɍ翖]=G3MVGw(;F$ U(Σ<ُVvB}o&Bw*E6XU;㊋CVPJU{+ӡ][| 5PRP0{  ]53ڀ i2󉧢 6*DX՜*Q(D D\ ))0p`ˢUՖJJض uc0})[yj, ~ː^%aUCK@1~窸rq'(So\W9w_T[Žp.:H/K ctC6``R_?Bx ~+FHؽ;p;+ PԈ̝ǖ`BvZRnʠaINN'(g%t[9/Teeأ]gڔ nVjReɄiT>7M34z6hn V!/hiU-BAX1Codr#oy:V]< /SpyJ=-p)P4`P_gL!夻xVA*Z\LLwEÛ?'śX>B=Htһ1o׳Ym2&9%qkd{T䖅Q,P !bPqa :|z&.~ߌoPLV}Kݫ旔MF(P6nZUVPYɰD *0)@JJo$:tPbXefZAuR8Zw=]Frpynm"~,۾Mؒ+/θqo1j72̴՘4_/t|ci"}`ocl"U2gC.o,bWYu HUo3?/=:f 5 3紉Cp<5;b\h- );11΁=C:Xh%YҚlV,\_B!'VP ]nz $|tރdrU-Ɇf8 -U0+ӈ>F|eHUCnAW \~$F'&Zc6mkjmVS4Hu5rX>o# COfZ?N`=\|&g{97^KT MΎw$z~._!uƏZA_~6Z"_K~S6u&#N˷~[FtĹ2Bp%/C/'tCN^K+|jKi')344{<gH w*Wsw"`j>>qvbvq)9fLoJ[>66cτB? fckaYЍjˉ:}7DIyt6gy|x<e'&/+0 sHI@n]1uMdQb~+ 5DsYY)KRn54T}+(+ ֊#o}#ܙ>ĬQʨ->ʧ )_9x=<_~4\/-\c#i]j%y4u⻎rNCqr۸6o >Y#oã/Ss96Q ;B!Z$oo+vf?Q>M:2/ wv}{>Zς>~ݕRKIWC_M 10hJF)RT-?UQ#2p~P;rok7~H t*P$k65CdT>q* V4ÔWVa%ӱCߑ3(eVX VN`p8?=B!#VTMVH)VֲV;?9wI&[6IWZZڲ^ ( ?DQpdp# @B6mm&{~̒df Iﻯ靹sΝd2ܳt,ՠCݝKx}8\n @&f"N<!dBCaw`wB!;XъCyy9]%fϝϕw-*Gl_M={ K,6]TWͯS)b(qWٶM7H7Xvw2ft!B!{[< mtutJLr Ph:!ltr:<SnD< {B!#Pt8ۃ]ѻqS7`n,rqp;8qE8!꥽˲9m*3gLB!B p*WaPUQNey>A:{ 0MQV1RiBo^!Bd8,R@)eұΊb/'!![li (P{w3L-^U[ڠ/&A0 M=0,Y89)6p6(k=!B 4L933]V X$s؃N_Ct!AU:3C2֋ΙAKkM׊=(:!BGiY8\n_@$G8;*p8̝֭48SӽubjApK<v4^B!&h?`˲v+]y L_]8Y{KϥoGl#~ܹ>M37-O(kŇo[W˯<|;1}Eh"[sЀ,<>qh%~k_!F"֚h<Fk{G(IJLYq.{m,$ +!q}鋽KeWRg7m̄Pc[۹ NjݝlO'7U\5RW]'~z$BLCϪ4-\o}ufPfۚ>o~1m]PCr߂CP{C<4HYa>m٩U6V;׍0{ӿn[ǐo>ӕy4|RXNN) O)b6Nwu,<٥pq)qKύP~b|apjQWKs o,nկ1m* 6 qVrz!B 79dWlݿ_'o^fd nV\#H?J!Y9x4١©0"˶4T\lM-π걺4p;LYծ#9ޕs7D6Nz9tYcl{ 6?;* ^n$~'WvHǛͿ][6- IDATfk| qN;Ga,C EY>|0=S'Ը}X![4|<_`޼qwٙ gm۶ۥΉ$"oyHk#2D|b|mtxեsxyF|es,N:Xsʋx/I>l&fkpO/,QY{4 J{s͗QotD4J9)c/sS5}/O9 e T)sޟ?K?7?[:9{r-lݼ`ϰϓH!ɋ>ʣ'O?}&5՜rIqyp|򢏎JWt$y1M(ɖt!2D|b|mh|yY~_Q>9;8=oj^_v3w^-,LO< ,8k~Ng 9 )p?\I5L%w}XV ~\2/k_Γr/ $ WFtFn*!Cq(Udxtup/:w҉xq<yioo+.gJMMѸv%:v EC17.A-蜩N`{f$kH oJͪkKmX=G~TN5 =B!U*5a*9Й7wo{O<mp\yg9Cgp:xW%#y1k;dWiVR'wɖJ^' 2>@t}|Ş6_DZ#BŦ!+>I'C_֬YEϔɓq?F#IW/$[9.?tԱ/۶dao14C%BRcrwA+Vq¢ uwlj}J> #YߕF%a!98Zݦ5m:'B!8,mq0 @ yraTUeN{h ο 5SM:}1>I4 1"iu6Bq:ZZxj2B@i)5O}3vq9g#FW,ƢzB!>]TWUF -^?n#X/Ƈ%KWrљJex%Vu6Bq =5zѣ\)*P`+Ke_X`/JI7E!"d9|Xa c[>8X:zBn<."vS, 0Jc/uBiXO҅ӏ9`ps#&ʊr~!BRӱokmYdp8 /lc1Ͼ:٘:B!ĸ.&mq0Mϴ3x<hJ-@"g{CH9B!B$GfZtֺf1.B!BOT{o(GI8(ҜDUNQfta mmn3ټL\'mfΞŚW^b8XJJJ2]RٗRYͲ,քB!^]?PB!B~K@ |t:I8qY r4[Pn^ģqIض(  _RR… tp8TAka~|>eF^zX !B!@ y[D15ZӁr`f4 ̉ OAHa*oZC4˴ R Ӊt:1 ˲2%%%$ vܙ 3B!R…e.8Ӂ]saxhCkr;5хnNt֨ 2 9O  ǃaضR GYYJ)֭[Rɓ'rFB!!8dy+02/۠X<TN'|NVN#y׋ᠥ%gF$T8@0#v(}!B! Aĉ҅좤͔'lY|'%nfcQYD9q }nR\.$ ,²,LӤVZ[[9Y`eee8/B!ľ,}}]A\gxG3Nn@ WLZq>ߨ GZ~RII 矏ǶmB/"---Dic !B! Pl=N1t:(yfgWք#} mjMlY ˅vxXzuf{V֙43S$}!B!qpE\Í8h4ea*Q k9^(lm6l 3o<:;;8yGH$D""mKB!#4dšPb>Y cF"h[mj6ml˂:Ô/lzHHq.\Ȗ-[غu+n-cB!u(Oڠ9݋@?9nzߊ (:k`&Uh MW.,\[o@]]]f%˲H$$/B!>-UrVdעGm'[l}BfbLrb5pZ%pDtuM$444z`66mʄbDTW/B!H8瓷[Xq#nL4p7@wcLscc-gTL찉K8fWtE_cR466fb1z{{ UI!BHUvip5V4iGN5ǃCqp*8i#f0vp֚H$Bww7x^NgRϐH$PJJOOOrf%$B!Ĉ*ήn<^h$NVllec:QL F¿#秽p +}۶33$Iii)>/NC,Lj6eljD"9B!d8mq0 WVE' /VoA0 rdJUAN>I?R =>?<3 eڵ(0 Ñ3Ӓz h۶Z B!_rc à^כr'dMo !B!!c]B!W2X!B18Zc[DbT2r0 難sB!طY!4Mz#Q $yQ#[ebQK<0}ϴ3x fm7N!B!&۽KU 8X6~z{yiEα~ _O83κwPKX<;wl/?_x-'-]sÿ^U0N!B!&Iii)ec6,/ő6UU(xa8rrVu'/#s|$gk6y>륻_0tvurE#y{Kgk׮IOo47eg=秽='}!B!v`Yܾ ]t֚x,ƌI$⃎N2 ?۷QYYǓ7*0q8,YdPa^/HI0}je|Ɣi,YI" 秼W_J> #["B!^sgYXM,K>e1u ΘIw"̩F0(+7֮nTL9sͺ5SY˂cjmM--<|QI?PZJuu uxײ!B!xL[KnQxR񸩞4K׋P@YEuUը_Z= }!B!D:/0Jz#Bu$RiJQB!BLT>%CS8 oXt3_!B1δw @8hx*ؖH왜3N B!bB]}=g}6=(;pܔ +d&Ni3q{S7)JO-@rp[6/x~r:-" ܏XR@]B!B콚oEkMlhp-oYp y[DQ6m,7\8u[7#gs`(G!B1XFQh,JCc۠15:'B!Tߏ&yTpⲵt}Vf|*hmAQʇ=vB![ 0jGn?p{ qzޮ۶hmajXB!AMucmmmf: ᠻM<:JFv8 8l0dh!B11ٱs'^s5k&g7oa̙||b4`la~U**+x}1WzjJ~ CfmB!|DwE`0ȯ˲W=?oW%0(ճ~:I$j:;;Xԓ8ɓ B!&2Sy=|˹n M(Vx!dW >L4RjjjR?q[VB!ľ-̩u|O~߸oTV!c5"PvQ]5hsc^?n#X!B!&VN8/.ԧ8#ij>N(^׋.HL)wpB!۸qgP3ml*>] r:VµB!ɶw6(`ۖ>'UqoM.B!B#Zk] n@șBH* B!B|r*} Rj4 N5)qҩJ$ d'C XK?\zʼn1::5&7Ϟq?d*'L^<:ZkS=J/֚vX&L%{Ί#y8;(~dC9?$ vsO=\3?k\/CE9AH)T3:yȄKKNI=#;D (qT' *yW#PZ%?YjAg"T8đ40d-J HTU}Α꿲+?u_EC&]f-V!BcR/0+PΜEs_JB|IԗDz/,][//ĬcJMX֥jOU JΛJ}鬴 |<_^gRtz/Sȼ=+e* t~ s3#sR 2W_L?j}*ynbh3Rq ȺAuNOt4YO_LschrntdHש U!e?թT鿇97iB Yˈl3[!b/ u!7/e}<'pB!^dߐ-B!8!kA=lB8NtmK>fepB!{>C.e[c#gLdԳ5qMFit#]fkml[S` e2ʄ]$@SS3͞EMM5>]DؾuvBl˦aVVYmc!08C5}coYDDĶm rrp8cq!]WAIUQS5c!"[kmcov91-k$ĸt8X 6f״<ضM_8L4cUt18ݝlڰKߏa}ѐPvY|~i}B}m6 y8#P@.M(䑿<̦ٺm;3ḡ.eYtc%FCmm-SjYf ]Tӭ5:lq:yщΏ/++=xw5⛹{=Z? (aÃGsz1o,($W_8LSs+3Ore۶ƶm.g<[0B2~/,  }0M-̘(}NL7jt `|X WCUeebݫ8xKI>7w/_ᅰ_~Gc(WU@%NZXhe[|qU#ʊ,'QOOm;vvxݑfڲsr) 0yJ.]0\0M [1m*e{G IDATv&H$BO(ȑ=Xp@ |GX?iضuS4`bLӢVD۶E'Kı1VpL_d܌wpL\)8N7!+Ɋ0 :r_oTXvݬ5E7km?_}ĽϏ 3,ut—mW7/΋ϗNad>ZrX\C5sZ›']]KǗ[̍}V~>;4-w+W.J B1hjzN>T6X4:(R˶m+9l]XAhر,cgz6MovzswkYfrwćDضm:C<hj"OP_W[|ZZhnn*-Ų,fII0*Eu7fC"Em]ݻfrptp͡xLZgՠJ`pj $p+?JoG/ۻ0/OL(9wr>ut;|kq-e+h͢" %d3ʖg^'q8i ;$ΜV 16y{tۊB]_.N\ ֐H$s8T(Y:Ҷ=sla(yUY`XJg$[RStf`^o81(P\o:w}z X'͵,Z%3mTTPWW@ϋ t8(bSvn, EGW׻PqH-\:oxQh |>εw[IC0 :Tc[}tF JJ]3Ay@ヘ5N RsNYm8Ph쬼%&}]1%8yVnJ "]aLt& G]4eYf͘3E˕*ߩ̺cS{~Dˡ #/X8{̦PzYu>zɤN+gB0ǣvz}~I,Xp m-].R(nx,FW%A4I}r{z=hZr<ʪItuvZZ*UWp Qq6z5q/Sj\DYsS0T-:ڋ~j4kkηn5ϼk!Gn_!"5n+d(0Z8!f(++c5πnwa6a9b"SJaoC4'Ӱu;DE;ʊr6oQނ==L9 ӠPhmR441}a}R475Jo1Zl[S?u*ij>i% *uC}/G28:SxOΉh!#6ѷ$#$8'pGj[˟ʾ̯(\L5?W6?sԙyn%]q9ͬE1q;l~*9:RqBOkl|38|R6sfL*twu YJ)|~?}.;Sqgkx<- |ଳ 68iҲL9ۍiYlk1s L,VJriniccY#j5Mxp_M;'Ǚ" Zn[[SNSVquW~om~޻t0ڥ _ৄ.g٥5RJ{pCu$?qs a5|l5tB1ܾI59rKx<^tIMYi]=]5)fFw[Y] dኙQVdP  3iTVUAӉFK!Q;u:[6oq{,Xs:L>YegXggX(ͭ-L0 TU,;:xkz*LZK)xĝ~j*&WF Q%8 S !>yvMQǝӝ5Գ 13ϕR4m߶T^/xdGc#6l´,>Ӧ1iRt̪T%/%ҜyUiLjc!cֱΆb7y^&j( ưl xIiHWirVpŻcQ]$C!BeuT2 j8CԂyoB!>P BN=m1)ЋeWEB!DoODbkAjU!B}]$l_^r^ y'Sˀ}{B!H$š(-/qiiRЙ =يQ&,J>B!r6Z;QAV!?;28D!B}U˩+}>UIENDB`muse-4.0.0/src/doc/html/single/documentation/main_window_annotated.png000066400000000000000000002405141404105311000261670ustar00rootroot00000000000000PNG  IHDRN;8GsBIT|d pHYs+ IDATxwǿW"U M^{==5$Ƙhh1&&5bEPw[mSδ{̜=9sf93EQB md%+YJVd%+FT!^>eŊկcJzrqPJVԢA TÊFEv"hxm(J3sT m.lĖvLLl?DҌ̥YVMBU]#536>z08@O}$Y9ࢴ"Rg2l٦k{8JRubEzT+e~u5pg8hJK ճeߔn81WԕJ)O])UҠcҡby:qL ]mc' !16)rV'LeF-1rB똓.aF IfIyr,\8mX^@KRVYio F=S2qdj"y|٘ώ;9WC / :hE"^WĔVNڻxNd%-*5Nϝ0eӨ!O: ;{wlӫ.9vM|'.r 2q7 D tt݉=dBשR0w*Dޑ~k NJ Mfv`KںA.4eđKC8d*,i2J[ [*A wrփy қn9+#PA б;"J466Ѐ@Ao'x<0Oٟ%%ptI#E='NNF}[F$& :[q2d$Gy˿XN>Y*BOm!v汦v -[3b+zWbK#U-$6| SLY1)OvK6k,Et&tT7q3Աaqg:IpunE+=N"-x`ܓyom/e)0hʓx7:qo6?&Lղ\C5蘗6E4v\Q2uo3a fM$\^(1̟\v=&8f >D\{koqi4"|YLAD|XH4P(hFPSSe@n˓9I^2igy_tnNOBGPMvUWnv%|$Raԑj$WVC$DG$tuב%C=)b"xՒ0 /&han^0 F_r>s$صxfv47ZOHuTz6: T\i^Umpת\r%ra(>-%N'jz@ д׳E3-q&1R&ma^*ط~;կBAw}77|#S|˪0`w/:EQXn]{ !!Cfu̚5~;0g ۷<h:wAsc`vinl{F-rEdu 9i}lDϘI+ifTI&VtP"-I&r0fX¨gG)Mer:&]7+.]9c֍~C_ORyxnvJ,/sR\WH$ABoNX&DIL$SE)2/=kcVN09>%՜:{WC+LZ5 f:z&utZ-[HxIC3Yξy&4yzcC6nr[_~.},-> /~5kon .; ;E$7ܜ 4ߌ DT0d 0d ֮]ǚ5kYxd>"&7D( Be-}`7Xg,"m}Pa^#x6QK0 &z]xIܱ"+Za:Uə[T˵:fX&Umf5KHbTB'fMI1AO_E icp3]]#.N$Ru,C^}!]#N.}B p[/FV=z-ӎC}8jXᏟ@D,D,&Y,GvY_א Sx~iTt_~$L0R}:m;]Ur}֓ZK~8I=1׳8LÓx?}IugsC{q]7zfֵSVϢ\SGzֺaM߶ {[˘Z*-;*/E({*gaϞ=O?,'M^o5j :8v1C t ΈTG(/W]9SM]SΕ%?BArXdwUs|i#|FXDlL8%gԛuܱ"]2k&NOd#'m'ժ1$cx@N'L3`))q^QF*$%gz骥dTHP$uP e)fcھZ1=c8cƜ>_*ga_|ɥtڅm[6 6 ⪫ZēO|+t,/兗^bm¶$N]z ! /08^v|F=ni1,t)>_KQ\\gurSڣOBGV6a}/LoLc%%{+-j8+䖩Ι!`j#+$1$pTQ G4!&<"eB/z]c} Jk$6  ~,o1t*]=Q"zѸq^/_Y(p>5tpӵX![hŤJ>DK@XCfR?_BDIil@ YJ\DǴLw@5癡ZqK+L$z&ӵH)zZ=[]튩[1Lo<ΰ)bϚ_ÏyZWWeVŖ5`ͬ^أGkNS}-֮j.^L8"uzMx;w0x ֬^戮:v4E/!^Ntޝ[ҵs'LKذizSEEv "Bbe0w-/ګt-}%2!+=c1=R/(=x2 HcꩫV+U)X*֕A=H% .R TSmE ,ԥB{`dChlL!/f[j !x廼DBA4D(1"e\9=p,ffXUcVlV"I]axLlZ*Yy8f;5F؁Eo':AE ,SUdI46"DTajd#KL*g|;}79{[1'r{v`0aD[9%`dnTaU|hi,a}ɬl KvL=tl)!H{fZ 0jkJR֡M72x0]vv zLe6Ŀs,k K#=cNĻU"޲ߴ=1?55Dh si3ձ_G4'$so 7'6!-!x/   D|T! /@Z9I]/e6NHUDIj*ALPG$|,XIZD,K]&y,靬*dIS[J[<GyW?O"&%e]صm(+߾gDu32lL<Q_G|BENtܙbr kڥ+[nY$UPlOkcK#gXn0\ /I \$D6(7z9KsڈBpK dC UTHK -%$ShX? U%Lun IN4\əinjıQ%T ȒVi߄)}][V`jfGi˜P)`厚Ә#'qJ\̴9,|I>'L>ѧcD"멬]v˧GйsgJKJ{#OGcBRWu<22 ej=VzrkB܍LK, .7SMf:r3?Ci%CsRe?0dZ/QlKE4}BjeI!)m ؉;N`ٿ/7ws%FȱC2JKsH7=O<9v(87!Pnhw21Ÿ;V7m 8â@J%֣Tv\Wr$?Wڌ C0}0VrN9}.$ v!|$I#F[ 71I$o<:EB<'z!lt77<2W8&ϹgmsC ;F8o7)(x=^ )/HWgU>ގ%Ftmp {iG^Zӭ7)aS'zj]%1Dc*jٗ9sSbHS.zQO-т7i!2U[ I$g; H$QkϷZC"ɨy:I[A Nu-ނ[?h kTCDѨZ[T &y{Hi˜c'4_k]IŴ*[~ L}93>y[8^vdĚq !8P)$W8I (:3+L0RɆ|TDJ"TI}:F1}Fdf;/S^j'/4r~ I/[4YVrNRla#LdSla:QN8& L*Ҕ%Lm(mclϑg%+%J5O :!lB':F]^KxDB$kI.7y\O&hM3$wɔ,$̤)6*>E g㫎3'.6y}9&E*鑣3N$wf(8%JOJZaܴA"LZ,aJof%+QS @G (uHTR ӷ$R)4OIElܒ*)Rt[kqlɖI1"Yahnw1SpCLt-IqR.Q˺M6%KNu5Q2wSqd%+2ISVHR"*F!PF,+,(T|IGtmI1HdHiucdJ|[B15QЀ2 )L12OסɔmQnqy[,tC,bmLR%K#uBwF&Y”/>h?ѳMLe}Y`XDim钨 iFt3K Q)Ll4vZFdR-=(}9 h&$KP#$JA8"AiI]%G=.OH~눒3تS9!KLL&y)+|I|H";┕$7T@A+—LoʢգPnAoQх;Ql)ݓx ΈB`gF2e;*a(1w*^WKj-|JlVbi"P[Z{:o8.XVkl5{3-I28Ӎ:%JZCpx;RYz'Lʼu;LY9X%NY9e+|i|*F$mK{%$ʨ/WJI V)i[)mDIrI`bnd #TI4HD J,CoLbRv@ (%G$g6KS$JI7Dɍ~{ KCpOTY”CT]gؓ0R">I#Q4!ED&Y0~\ЀhԤDzIEjt0eΜP] !%U #E,:.kvi$Z"W,39X$yR6ޤ%Nc`Xsarӱ:oYY hW -[beõM v"7K3gxA0ep6-qko IDATXndaS|d-4&U\28h6(u~ɬS~guI4N.$ZZΞk |?]Nf)!!hGC8=d"4{*O 4E3f_q4MrIDC4hZ~ȇ]RJ[E2id~.v[7I ?X}~0Z>hi BAjnwx~Y8nK}$~蒋@ހZ"!ld^CwrQATuA!_$[ca#h[BC{b y\~kl\L˘נapyjYk43Ŵs,U?b8|]K|%v;UHN crI!m&X6Ng&1؉̹-X4X}VII>'VP<_E2jʦ-mLBHs'XCWpn<^?[&$JAc,:KhN|hybٓ(㡛RT;į-N%H6HYqHM gNME*?|W]9; Mqbh;&SKD1R͢gWsET9N^7:%NN&Ч ~&Z7R§WqiGTp `м/~=dԍNb2*d:͟ abr1P4L9ӏЩZ<caܝ<}u-;yo݊Bu;Xx#)2 v4ʯ^ON+ m'7qsx^feLg:9x/e9_*kdԢƍ?j=aO B'=>L]kv yQy 99ٚTT:]^1Q;_Z7)c]UETRwk8n1'<َ6RYѹ2ڨ롯V*Ty{i!,-2.8V j"ټVvἳGq|eg?yV5h#"&.uwG7glg8:mjGX&BGfn-v7 &ic!kcX&3K[$u7W@g.1xz~lJi%ENfܸӹEUM2 ZjsU'q3ʿQuĔDE6}hdkP?| Nh6J7~Փa~_}Փf\='|ָLyyLoX4&^3N1gwwwhHF/K/ggȑ{&?{a#\5f5, W1Q sՄ{/#{YX8+}6.8#wvѢU_5E_.Dk>jB7bzL>ϿH?ݵXJ = "ŵ m\?eofg<>%*DֱMa7d휲1ӗò1}BWg%OZtҏ'd@:#N_7>8x?G4ϐ'<{|4 c&[oI;:cDŴ?7ӏs?S.'<«b^(2i'F+^z֣h?Ysy2^Ólq+|׼8?o̙/f;U̞N\Wһfuri3`K Q{'ǻ=;^em3IQˢ?'=>sv,NWbMxaYg&<8s&ʜXٜ)Jp~g->?`l?u3͇,swqR7qZ37<%o5O~{>iz% {Ke2Q%0XW[Yӏx; "߿f𗷗; ]@ TE\+ULt6o^1_Cs"'{_agKy/Ki(˨QܻL: |x0-76_崪7a1u}o>Ț4,妱>L1Uϒ?F^ϖ+/($kg6ࡏY۸Q8T2=nsәkL'ڄczZbag1: 7~M#47 ún.p؋)K)~DcFSCL؂UGfMK~^7^IK'^)ͯx%^h5+M)U4Dkrq/C/ױJjir8g872MCǍt{lc#!mP~ϞaQBd*_3 ͠MD36xMN1-Ap퓋p?{8SN C&s|>IwU0>L\z5l M+M_8fRM(iCAN꯳ B@=d>&Td_Tx*]_T7R @)篦)a)kI^/^%IH͗~ 54K,^Us1L%hW)Ы9 rHɅx=|;?4~g.!V̺z^-F=mq=ETye:4(Wރr_׋M7a\pXϥchc۷{)L*V5Ϝ䗓\EcMΗ_By\KYQtrWn873UJdGf])+,90SH##gJA]o)] S/L~JNK͜3"Ҽ_C"bG8gbBZ%་yRza6]˪<9v1èȩ`|>yƎSdZۯcZ|:-;A`N>r›yw,~y%;S|uh]9> i۵2(K%n]ټpOw^7RŒg_d>~ݴ[%Ħ8{[K)97KNx3s>CEO=9&'[7R'ϼF:8n g"ꎜBÙ>Öm X LCli𒃟9f~]83G[keOTh5=$n{g~o4U1ŬT 2c GL4=O= {l?cg10Oҝ5\/?Ȱ$0SNhh]pֺm,,ϙHq[1΀iZ`[Vr(I&E(g-=&΢7'MO$6ah"Vƭ9S>u -|$T73$4˟gk|7g1iƍQ:AqJ#oq+nSNepPS@~SntP1)(4ॏ{3gH|=%V*F?.~^_ νOxˏLt s&qx!@ߗ)Gns~Fh1ƕL3_ΏnGju]-耈n~KgNcќ8Z5~~e((;1Ǐ'sޝ'|6~Obuo'|օQ?!Sg¾g0$>Oe:esm3׿dtz.d)cM|9>n<<üδhp8wd.&g) V kC} 0Llm`~}qXHfgqzosekrMY9f~N|%jKbvrޣKEc2H:qJRn;N~?gþa9NG !,g̀SͩJBs*H3QD)+Y1)&L,_`A+v1 wlfr(fqu& hBKH8%SZsq?6a4D Be-7N0-[&+Ֆ9ѢS.DYNb0:4-]jGqoNfly?ItB4}2)=qKAI"$LYM9ZzUI,QJVĦ0Tdŕg[aLJDw~OK߿;܌ʹi\AN~i{yH-$G4AJ?$)CxYʷIVD:R#NS(+Yq)BHndVMlʧw0d3Bx:8 `_>d'SS[߬`Đh˺Svξ!!BiSҪ[ȅ# 8Qn R%JYJ%FSVz)( t5wtI$^%enӶVx?$ڐՈJZkM!3k$)+Y9$qZJVrȐ) #BޗCR#pM4&1,WQ\79QQΤQG m@2 ;2K:$)+YfݧYEQ[rb吕/LeWId3J"g<2O0Nhv~}H&6\cۈddtFt'Hmdŝ$)ՈSꄬ׋)H^^p`s@ĪVVV%G_Ҳ2ƌMNZt["(BetAR% )NaF\$CdֈVW[GYYYڈV4mg84 GZn)tTWYB}]욍ĮkksTee3&kv[n/Ĭ+}y.P_uuym=H7@rܻ] v8m.V ңg3⯶ݻv3~x**:fV25?67Cne9e-2>GRWW[g /u!&_ IDATҚ577}{vӥ[wBAZqUVU`zA5_[]0a<RC ɤڃH'xc8Мd1P_gY9xRPXĶ-o榃`":UTгgO2⫱;wR]Sxfr #UL@s⯕j.U];H4Bii9M ˊ./(`?z I扐kTH.X@NٓmlldΝTW0~ SiĬ52܉>٨:!>,n6b;+(,9T tea9n@$uTO!4q5Sp4BYYyr`j0N:nx=B ^Oq9xb}=w,PJK(ݐ3?-ϹV1gʙUzq8I (;R 3wB )i AmZLsZ*:rxڳw/An4ŻAx?/2GU]SKEGrV,ۋw"mvR_W[o;\{b*1^F'~GK(fCClʪ،Ν:ѩShd0k+U:_ Wˍk[PU]Ց}JHI5[RUAENJ/(Uaa!e lٺjAXKIFٺmUTTPѱΝ*ۧOھKGDAD!?!DP5ByA@x(BUvOjA~~.d5v2: ߿׏׌ׂ{@?A8#ȥsc@M |P[ ΝkgLwǀ#u.6 ~wT5(jW2@3P Aƭ||><> BL:y摟Oqq1TWWŃOX[*)}+>(쮃jj$"e (-1A6ǃǣxP%|Q9UnU2/}JӷgwmπlGW7 #Xn?=y8+( oUlۼ \p8?Gַ?C<*ËqaR՛M7jiqɓ4m^ar:"O^o!N֭ҵK7Z-44ӵKvLnn.#W@;^z9T_uRVVJyy9}dݺ :|-N1UVqTy/ƛ˛o)3g꒾4G/Rͨk8իidFnm_Kjjj^}6?i򦎯N?T>( Q& WZVy\DiY &xW]<#-2x0''LQwB,Y&<EE)k֮%}q}ǭ|yQvl%!֬ae`q8j}nQǐ$F/8~Ա|},`|u _9c9rVD˾X昸߰6|JFȀNl~_(QhEx4HSWJDQD8 ңG3>0;ɏ''(Q4(-@W D".u[:O=C@N 1yy1]2$&0'Me/%FJ=@by,_o~\ 󑛛!OHWL’%Ky/:7u=t)]R HlWhrHM=S^O8iɋGQxnsTY+QٲS"yԇy~ ӞFҐO4oN>㉑'PZb8 -: !B+6M >P3G:%zҔ<9q#>p ;w>☘x(,,O>TUWW+9ꨣӺQ!9dyrڹ3׬%Bؼy-k"%@&0#N&F^I>L9yRѣYzuRǍ쭏 %d_=xs}*/;bM$^$qJT9yzQ&Ĭ]M@9r|OB=sP磸$W7\ s )y4 zwpĩ2 rӷ\ "U8!Db륖G b$iߴB ަ2 Nq۳g(d'_yyyɧ_;tm۷ѧO_Qd~@$%9ruII1?&xhk5O/M8u+LcO J,gUQ'L{̘6vd2)&JAx℻v? pn|Q^vEa=7iL4֯[W\A׮]-?w0owqcGҽ{7ӛEr߀+Yw=xNuuuwExΝ;EQ(]takZݻ3|PckoWHW74խ+l|F/Wd5.|[n zbdx}+Vqkur*_ZkӧSN믳zZtG'i=|rxRW5ܺ<^CnlA(hAT*(8hav l,1޲vv X?+_Frr ?>믡WV{c:}#|X֫ XtFD`r,䢗`(G"Fرcr^"?Fx<MްtR;.71ڤΉr3 \&vZk[ʵqzR#OKAY~UYWP=h [jv9 FK)m$CEĦ}FRDk;ڢAH$J?Ċ{Yy/_ܲ㫭VI?D8%F[u8YJ,@{{nkbǿޱ]vBTtȮ;i0Ze tO doe%;wb_>kٹs;w@0H tQ7JqpĠd_8p m0ڴDcyGQn 7wZ轫  (JAQ#*vzR;Hi"(tB I o֙&&@̓3o9yF#R/C⃨(:Dݻ{??"d*׉t ӧLhtFՔZZlT{i[PՌzJKYx1 `1;}fg{dggg3o}1xwHc 7VxƊGiJnwGhuW4O=oԸ=rUhF$QZZQ>hުu%eqˬ_:/7c0ʕ !.LE(,TzF%(BBf4WQ_^|)MǽIM߾=ΆZy}AiHj ĨQQQF/^J/@R_{*P\v (D(hrFx/>qf>۔k(Le>M3E7_پ%j"XE@vͫ'J[BF)lQqVH|e .@|(=6S8IR Ue~Zp:`)-%;7OkdZ99J0 I &slHrN_7Iyaر5?7-Z]4U3 M A1T7T>7),,d߾A)pGmx~UUOn+o~ 6lgŊ*{3ϰuV0aW\;p,ш^gXxݺu;>[>d_tmذ+Vj+J2SY_I֢ExwyyG1c?_Y+]z#Gw^jut#G0 2#KP~ip|ܭ~ Ⱥ[0>r$(+*JeQv[Rt]9(g@TUd~yNUsϟOQRJKW&hTKZ11T\ M(.y-[eA^!Ka IY̫pMȮz@l(JяNhAtگ%hD n)7ɭظNmƍ/pE~'BBxs+\9Zj({ݕG1c}xYJk.>9 %| T,@2urK1>BP ؜PlU󚼕$ōNpH[q+_nl!Xl$"DSC`H=|ݫU/=Wsr2XE'y8a']>s:h9$b):d|HZ-={b2%ijj*7oz~;4aqqڴQj}2-"'O*ak"v+ODsPWu+/J4TsKYQZ┏<6^:* \WVK>#TA6mP 0xje( Ox@])V/WT*<>&baa_J湾GDuJRƏ?ȋ/ɡ# IDAT'9~8_.Qs݃<޽{9uSL:(+<(Kv_ѣG9pM4AK<ȩS$''ӰaC骩,={pI hڴ)IIIC#WWDTdYG Z5&ZjiҤ\VE(%X%8<ʒc1D,|ٟk_`$IZ&6VŊŅp]pK.ᥗ>Aݾ IEBcQNUrKLTlQQe);Μ, ~1v%Ho{@&U*]$(ʓכI`rIYZ(V ¦R?~'H e!j5xН́91F~gj@-@<{_ : E V7*IM4;\j6f*n= <֦ȡLmݎD+|mV+?_G{Mތz2F)gR]3;Z)`uj9贚d I]uDQ8Pz{rooKebch߮lN{ =4kv{!I!=7Lh4>lVq1q >sTb@V7Dkpw]5>Ma17uCL:'0W94w.FbQ$ CIa/"QzbHLAa qFLʫbbъ͜ξ#W_}CK.gÆ h4z͜9s%L'>B(A@*Ox*ڃẼ5NmZ3;ϸux5iaTwjsoE)*o'p8HVQF\qmۖYf_hDvy?9s&{˗}6l6CÕEq]}/YRQ,F^{5n6͛GBBӧO$˫o%Kӡ뉎QFL&O[ l_@h2ÇszWǎ/'=Sɞ2 (aD)ҕE&MNԈgJk[V-q&U>I,wLz׿QHR[D'%%h8NvBEb"\磤o`Xh7u*+|t"`ҫWoի7{˻t 񓙨hX?R;b9)::^BޝA1gΞ9FSG1o^7+[) zm%1!.]VѵkWt)ڵc`J/ ~d5 7ʣad/Μ9CrrgSO=E=x)..F$rrr(((Wl\^$I=}Ə_n^P%gԨQ /0eʔZM||<* ݎN| ,ZM\\111<# 0s~Md5_}4a<`;J nbRNVQQ *.Z߉F U$IqcԣP֠g7oه*vl%n^JA(ӧqEQ\~w$LvD  ȠNE̐ E+(v#-3?^nz<< m-JxX%6 /ˬ\ڋ[y0蠠44Uʪ7G2=RZl&::.jU`ˈjh I2@E :J逪QLbXlּ97mW^kI8tiiiX-u:g b% Zr;JQkhִ)͚4EpE4o,{7k֜̌ zj.h^CP__­ހ=r@-Yٳy5k ɼyx衇<CCNvF;Yk_yCBX%ٵҿLb]`kpޗNFh޼2硨!?jʉI J'cTcEUt2dC)ר&AEdYp`w g.;g= (:h4ȲZ7ڶxZAP]4h5:iެyTNG-0MuztZZAr:SpכRVemum}vSn]&?%bX F|{ϡÇYnÇeС[C72j83s菦Mcٺ+Ҏrx:%9۵gȠAt,el6>3aÇӻwo,X@zZodd21f6mĝwIlϸ=djIj5L&tZ-AaK=t{k$%1xXd p8eҥUkҥFQ0$'&zA7IoGt4@nng+;^~;Fp#q)λ̑e4jLIEsm<#Ls2Yg { ϳ?_ ?^oɼ W?*p:X"O{ر7x#ڶh4T+}|ͷ\ӽ;}aܿGNiSy-%]wŜ9s/7v & ]ddԩ|ǖ_h2Y:e z^[;Fޝ#GSٱciiiL6dOk ٳIMMΝѽ{bo `sW_uGsNҘ2eOYsĉtԉ*$Yb)_P`P8jVꋹ.3eVdA'!Uܧa֧HfZAndEh4Z@f; nW79dp(w唓u( %%e SBBh\tDY"@C\dA7؀l73R4PǤFq<E۴%^!˽.w`r.3nS(dY>PjԪJe/5!#{ $$aX8z(g_^'59QT`F%G@i\@rdhp8trN':g+.*b)aS\TDiIi\?܃uU ؃AEz}Q~(dQu?t['JSmOb|<oգ<̤݈ԱwN;w/MBvv6C@F}Kvv6]\ƜA!sWQ\ROt JJJ(*.ɓ 8vGg<9s9Z03'7 FqN{J~F)a~ҿ' p m+W'66dT*O@qwaQ6"'[Z_РAT*gΜѽ[eI*CD*#NԈY(T5k1 #UO,ƉI#"e&?!DE-fZCiJFQ$$i-tOQh*$Q,[rVIl*.DYܴ̅׍U/*ӣ2$-*T".!qi MҀF(Ur(ty\: '/Bz^I6JmpUdlf+K`ʁ$?/$D:mAGCr@hS/**<ȭߧ:Ι3ghס(bw8HKKTI8{,yyHjRJå>D!ޱ(Ox}7p Cj le=_ҳBLL4%%DGxرШaC,bbbd4b0h4FsIx1T=K֕mݺ%NʀAdG Y?In*տwݺuqPv 3d*wjbЯuC]췖?/FfЀ\}W:ޠ~jՕPXA}v޽S餧+C7Wq]ܽY֚ >yأ{7ڶmÞ=TuUWҥe! 坨i,"8e%lغ3*<CdףdjN9 qTmWEժѬ٫6@VG* m#119|8VCɲ[krt=Ei}n0ï(sP48 J=uO4EYǩX.8Y("8"2Mŵpt:=S U7]?feeҔYZPQ*Y5Gy5DظǏf'IQQQ4_:@A$V/\ELLtZn'''B\|'PɚTR\eP\7l.Phs)*ǏѬEKJKB Cwwh׶mTns:8!8BsCyc >;\wbDVDօ &%G]+)Ush<, *XBVt:0H^v!":Ez3|z>h ˢQ1΁}8Bjqa^,3sCu!Ȫ nU)#E.s$7hY%\HDqq1 jj~D#g.2#".Y@ ` Qj]/xGP * j>p`oD"}lksYYᚪZ;djr53F!B$.>v9u!Ȫ)^͉!B"D!B"D!B"DEq"D!B"DpR~5{l&!B"D!"c[Kg6Yf5/"D!B'Cch"D!B"W"cHϷ*˘ 4!B"D!BDdl~A ̺5iX#kTD8H((aCբVQ"&s1H!BJ Нbcbibcb=Zn ÁZqDEE %%%NOawm .6i"DP="c@n^NQiۦ-VKEUZ-ڶ}8N4MwN'ZZ,+2ZUW^A:uKM\(m \feO(_ܺRlfZ P# Щ36~bEd7itΟ!77;vХԩ[?dgg}NRb~s1 (ynbjKKʤ"KE`;  jɉ5x'*q#_DQ$??Xss/B^JBRIGVSnة111TqZ8@k=ߏS#.6a;m \\Lmbi3STb)llumdJ 愸>EJ9N厕Yp+¹D$, ("EjtSs>hTմnӆs*7ÇС nۖԣG񓅟M6.NmfNeRJEAͅ0Ct5t4fHЛbZXO!vRpQHͅ]T*Xw< ןM*GcPJ }Og[cvf${"?p#g-L3ds]Z)KHxwwJr۵ -A>oUj{%?3}Ch;SO2x^xyC3=EsS>!a8*}7Y70ӵ+]!za`}p&<_Ųsڒ|`QhT6saL*RZd86cse] 'ٍJv})]TzX}xan *Y|2 x93%8KVbY_ YfftoZ0{ngcSl'Y$#\Y6)uwWX~-?.|aE<>OJs_EeIڀ+xcoov8ě|\ {Un]`3zaTĬc…0wl-\ӆt~oxk_>ڄG-O,ۙw>z~]:9nNOߔ1{C=gs݋8fslc!<,b#g?}ct g3{W<9kl:2w?)fd` cvnlΑ?b] us{\dY,şw LҖ}`,rn1C/AԀ˯I m̽t6Lj IDATyϊ^3UOFQUaMe$(-*A>1FðY[ Gr\[F7QhSzlTsq)NJ)~cQ% Ҿ8ٛkPnjSE V짤Rra,6|$۾㙤Ysm6@TE7?q͇5l|61^]I&Nv짰݃|έ_;>>[z ;@N|}'>O]bg ӂm4t#tay 88c~-w( _kX|$o߯n W '/+c۱}k9d2{n|)wO9o/C.iVRd%rs)E^y^>7:d%޿KY<~Ma 0*?R3Y_K{_>3#4K,¥i>/N8fn£nF߄UGFf:L4I4勯 _,ۭjp#s>Fn| SnO1! eX"Qg~5|/ɓIKK+رcL4^{5\E}E,1)i~/j~$D[zK\uΩ޺u ӧO},O?iӦ3u*{>&ͧiJ:OwN;Uq"ۄ7<5;mz~i]_S6Nah BvRswnϲ^^ v Nπ3Yge }+6mɑɖ0^46iF7#0MXᤶL~i4>^<ߐI,ٿ򶪶o,,@d EؒoΎ(h$_}P)ԭZ_(,64ޟZ"`= 3긠NL|| 86m.xYvUةSő?zUSbLpVC  )XR `";y?"֮[XbE 9MVFa1>SQ{J)j֗%OPY)u8&Zrg>sEftTFZrnNco_GbXZ/b?/p\VOh,fu]A9їwCәᎲ,I^\;N޽;G%dao1ܽf1{K\[{gfH_gj2c %Xӵ_~{ڽxq6mDtI^ǎׇ`9ɒdiOBM/9jHL :H߸5k 6_tA_9fq Ot=Va_ſA]ʹe+Ҧ"j_nIy-fLgFt?-c\CB|QAۊ> 1h~@ZHX#]n؛F(^ۏ8n p6M6)w'tYn1h-u&ύw[I@]2wr`Kydf:4iRn-Xf-ofj\lUVlM#?ҡOss@T,}d9w"z;>ӎ]/6l3O~)0>O]Mzq2cß&HϰtTں4VݍFF ۖbrKu>^錨J)>Їf&!ٜOؤ/ [&@䨢 c[qO7zܳoGf1|+K ɪPh"ӯH~Kehu*CJyJ|;_+}i_eu~e\W86w m; Tf&&}GV O/cFF| jSb>`j!O/ ]f֯[M"K"hhSĉ<'ĕrqrYT^C04񙙜|=zj)%.;r-b ʹ*5K}*"7JX2\ӁlǤ p,`1=+^ʹn:F7*/KK2rLɟrKc\ I0{`Y9v'K;3}P@ʩ h,Tѹ\rz..Sx $Zk@_bc;t%2VAe$VGY,\z%y';vżK(-).wG =8?/R~( 7v;M.- DKM-k:^E6!$o@ODZ%:((u"E#bi>$Tu6Me[\h?bKL0%YYI%U b\֠~Nc7fMte3_~]}M i ٸNjZlg؟'y"Nlx[ꑡא>5OyRR~JY,{7.ͯ0DgDJʐ:[w瑎ay9M|sg~=̓_sJ$7]%|1 ƺLFtTʖ3)^)a޽wx@PZQWzcʥy?=ﭤu oBI!!Hs֯W8OguC8~xGأ {dtxu< @mBxv?ޯiL=mTgZm \Q$hDXdȢ Ts\U u1m'@u TNPPx;.]q_A#P2{^ђh .:ؼ?RCzno [,cDU~S` -fek*_͕Iu46*Mr^.mwT"n F:W/#[7vTRD?WtbϷ} q޽: &wW~ʟzQ}9(&^Ũ1m@QJC ]be\lӛø| F\VBzAY%8_JrD OpKNMUbC(C)+@K8/,!KN$]Z,Vw N¡ vf4MF0˯1zA"cKȼ׿0^RH?68_R"T2I\Qya-6+!Gi0F=ݸWx:% 2pwg 2/mx]K?:&3t`ƹA4ɠǃhf:C^0ī|ܧҦň?:l ٭=e(YTĹ~erpkb~]43Go!2dc,ó^E߼w6]”{<6wٛZwǷwfˈ;Srd܆ ԔNd ЙžF2x3FY8 u:Ąxf8OK\gK,u 4i24TQ&utڕs璖mn%//&JZZΥk׮~Ss^ݕDӧ߫ U_RMT\AK~s88F H1-nC6P2 p)ۥT{'q_ЁCm.mI̵=,g2JE${.WGٓ67o3^Ǫ෿SX[r2[2c3z# Qczf? xei%¬p؄}_`=dYE$GCkꪽ#Q3vgQ䧔 N-ڸ&Qe !7RgESU<ѝjlh;٠e,ݮ@?t\n=Ov|ƓJELl|"D xx׌*ݓbț8zQ<dzjm_ӡ};s;} qH:U|\-CbŢ yQXX^/OT<Ɗe?йSxpsNZ.:*c8m \kKxj3,}:=Is||k)2t`ȱ5s oCA7r=9NWM82{ok!bim(~͌Hg7΋`"ZvgJb&<$G&}̮Q8:y9N>DǙ߅X?fuQҲ7;z-ٲ;ABv!l(hKǏ-PJ@l(/-eBY-VBB$d/ ;Oz?$O,˶,y8~{ιoΨiN|I30{+F M1f,6mC~pyQ#wB8& ߏjX "9ج6iv :]u\\. tnIkx=l6Kkq=*@)ROgz9iJO:GBHAA.͓@p`^g$  8&AAA42I   B+D G6xVjc=T=UTPRUiL vz*(`טC (%44ꨨjP#{ߔUŽn0Pyƴ@yJx䫍P=kuWq<~Hv3ՈQ&!|1+!5Êo~4g*x5s+~=+ BJ9 jk_0R1~E%XJކ ) uԠUM/Z #=˂ C\UƷNTix++(3J+>(P#)G DSJX@QuT[Fi86\FxPKHU64fGJWJ X' /&6e54?EA {+C&74P۞hn9Jc1akią mvU(LӴxp%PRF}C* -\M{4d>Ѥ7+?R34XݤB &850pC}-4;*rZ`ё*Ci"hwEL/H -M7ؤ#fx|xz3ŚRz1팦wjMH z5%ޭG&zcMƢ#ލ1=Ai3t }jhb4PLH9+݂n(z± [jf›Jz g#uZLA{rh$[j@d >8)1gP#ݗ   =6Todؤ]?l5AzT{Uz ={$[ A u5 IӋܒzAAAc?bT=Ek   $AAAZ!0Tg`gAh P[տNSAAJuʡvY-C]z)|`\ 9luAAAH2 S$ɇ9|ЛUCMo3`H>  BF) 'n=cj*gU)1a **N7?@U0,Mu)͵zfG%A!AAA:N--geWjq|$H}>b^җg*VOmyC8M~]ĺPO^)TlJ!4_AAwzg'TşZ\S{=eVKCFzr3Ph"k)?gvj\~!XU';>Ó䬻4+QWoe8Ttǜt _4”2Cٮ-||Q˿Vqg`X}'_ .,}y3'{㏏o;?tkx‹[]DK㿿Mn%L;]ɉ7v>]ˏs{#v1g/K߯albC2cuɗLO` KG6ژ}N>FMeԯx :%\{L> ^b,?~[__e~'X0[0oyއx|7rX{xɦnr&QHP}7xec} [~ƴ ]x_pEg^1˵ӗ7<dG| v[az9۱ܽ:$ӡp;7~*ߨwV6 `wq5n-j@zc{pf]3 'ܫǮ\1gnoNc 9g)0$81\XqɷȝC$צsTObqAb3Xea}1RȨel;cr*y N|~8)BZzչ/3~(ulb!x+6Y}m9s5U[ȝ[x} g1>!FеRZKHZ^uf&uOb7k9-6׮ӆX ʷ}E'=K ʎ9̦yZ;7TS6 3i{%^V2y=_T7׽K\Q[Ãt_j}.0!V<Y]O///+e8p}$nK辴t{vʝ;͜J=)?O~ٗO_%xOGWseo;b&tQyx{k.xm-pI%xdJ}bg@QԈ> ~+Ǯ=DՃ.Due V:n Ctٺ0ea((}sXUJp@#WRk-s9.#R.*|r 87Drs<0;vJml6$GUib^ßX9b:ygxwcRc: p+owOdэHC:|x+)dX=Tթ) Z| lJ߆:emFkz[;JsgJju֬_>'܌GD+K 15ڽ-_Nk>#HcU-RGW[`fޏ&w`*޺}ߌbX=sP.{iTb/sx'쨻$/b՚oq0nZLv~̅\ fٿZ}E{i QYſ>ǻKTf|Ss__MndiWsv r*Sq9i}\{V&c<bYa7WR[J[sO9ꪣ컷x `C"m1yf{a+Tw_XG2ܾzJ> /}~EI'kY^Vt{;|DG,[=._=}M9ȏ0(urO_lF=1!E ۧLܪwȪ:Q|*~?(FsIJ{Ї('^1-zX¿X?1I@'ZǾ 9-{MMQ!6L 1] ݟ&vӼZ>z'CN|v3[*^*QOp_w\i˙%F[82+>$MХqۮՐ1,qFy_σ?W,B[Xΐۦͼ?50}nd? GW۫.}3t<-( &2rmD-gOqE{ǘh'8q/9mS9ԡQ:_lOw1Ny \=lJO3RpףsCi]K3q|voסҟ.DJd˸7+` :4P~'ܧV|ᾃ7!ث&4O.뮻#Ax`Kut)4;[|4ׯ+)ylH?WV<]ݾCpϕׇ?m;}㢜dGj/ /7^g=^w8! ruoeOQ:rKNu^0|ZKB7Mvgg-n ŀ͚/fܝX-6 q=K?^pN[Aqy>) B-Cft]_jHEl;jA?b&KPs*S ғ.qk|EiUÇz*jEAU;ngڴdffȨEA.M,=O{6q R(ٸs }s7g%[ }K/qW/L|t'%y .y5.6y?=Z?}Aƪ|q#lČ;ک1& |EuJKvmKKط{< = b͚o4i"991$ $hӞ=#p.üxѱ$!ACk!"<CyA:JG](bw"v;*ءU{ψ#(--]_(jelݺ =OiA:4_k0mX^4Թ\XVTUl17[1ʚ   mܧV/XiPMC^e.cIlۺ%Zkعdu` #Fbݺ_xP=|2UAф?MiF p (j`v;0ROEmLD%(l(&OC RTcװQe5,_m(? NE_ J)A@V4O4 jC퐎 AՃBC:oJD6 B7@%(M;uFi$LWEA AQPB7E"(5ʔP!A]!AYjC9:iC%ڴ~`FqJ &Tjt >E#7h{zB_%l,Lq=1P`-cŲd 6{Ql#2F;d?IYլ\VLqvviͣp|W (pSrz8:T>V|ScLFoT/_ \Z{ B>ݒ/#bƝx 8L-|{Nrt8ʵ)3Ycwb& v̴e͖>Y8ϋq̛JAŖ k0lx u%;t{5J}dGv<3etOGv:´ # ƩosꫡC9!sS㷡g`ҥaUa#E#us1~@էpbP08HWBofdBJnu%TΣtiiͥ˒@:PTt C[1:N 42KϽ?CUIQ>}5!v'^؝xxjhҪ>(hy tv`p *~T XÖ́T]҆eUz6* ?NRo~qa)-[ "p2tZ<֕ eAڎmwh/bwCNjw<1f2gKb%xoy}L8@ 5Ven,Pi/UCQdj~,H %JmUÏ g?n PNeoIgi>2?B=fUm0( x]^_qڝWQ60ڟvo>/BRl=N2`/:k#$Ewm i ʨb `2Le×1͟HAm~rs(t}ir5IA0Dg_O*|قSm'ۀ,` `֙Z`5tvgW|7;BN;(Gl]*<}Ұ[>F-.g䪋P޿ 6‹grʓs0]|r[FS8 yA?[eUȨd ϲ緗q_+[xڱ{.~Oi#JXK ^ouTw6/[~{pĜ'j`bͥpz.}d2m~& )F}x;l8KUU|<)d[FOE66HA!ݤOC~P؁t~X[bV=v@?0zp8szW=>Ũ7% \;ƺjJ>?Ewm"͛Sp]yo.|.c^  rIWEGq;/MOʷbW붗Yޟx8JXw-1Q|9xIťGGv$)/[NqzO痿ycC?*2FOŦkdsp%OCt0X8l*jZL'0lRc *m 4Y1f1dJ#c?{W)1z+4n\ԏt"+"/)-bc5  ,2ovUņ~2C]?*p+5m)zm?f0*=NG '-!=S#Pj7䶤C(.8?k& **K7?w)IVWV٪LF}@}(MJolMБ2wSQgb7~ZÜ1i|Yȴj>UK#T.O3Fn:MEW~ϧg .p,aձ1knbx!/[Nl;㴮 #G⡇>}Q~xyu=}םĐ ߢŋ1^a:FS`BYדQ* ~^fTj+#yS^}iPir(w5ʬk5}~Ti2HyܻׯĞC adk):akD-&#Eab%AlLj{p 6RB~qJl;/;^K/LZZ>eG|TVOsgK/sے?5ϨҺ?r'mmH-p7(T+`w*ԣ~RQgfxMÀ*bF3äPIi 9qX,FS9?Z2/։/'q ;n Nm +GN=z3)*aߣ>Ha>/n=fKo v(3zw=%Bvn`>+cTKL{L?{r||S!҄JF<ko:[GUn^\k4\Vk%xbwe݉-v'^vzJ]mu;bywqv-ҥsN^1Tm[zIiƏ@ _a?z^ rt!FiqdǫT?_2b0.xcNԱ ¿{;a,4sԚZ%$϶XvN2n:q]$%<>*kjf M^\&١,#w gm0} Gsќ3oY1l jZJ?dAMOb ;Bo"us~"Se݉-v'^؝xr>/fP-}Wb\ǩh: BII ]=JVv֮e֬Y-X[)BeTGAAGF~} ߫ 4 @? 1f},!U[kg_9e C S1f.}Lnj0῕ ;ώ{Vp6T5ݐĥ̈́77y v/S{ .Jށ8ҌFN?'%}k9#=lt)8I+w}ߩ6^wc_K8>P"$Aw0/w?P9&% ;\tyt\2O&Q'^؝xbwe݉x-O;/D㤝vX^4Թ\XV<(u*0g?`D%ڷ20j ~P>: 4~, ^Q1MCgW>Û=B=f2O9^輠>Ӟʓ?0 ۋlYzwFnj" g =g))ׇzG Q7s@jNlJ1/j?_?DDtLt؏MQ'ӬSʒ˜'& |q/\tfI x5eBф(6F?}?Y~({mFlNS\ISe݉-v'^؝x;^9NΊP_Ť-$b !7ŷ,n[?n Tyʶө\/) ө /[Nl;hpC)޳r5m?Vrr9q֒8#_¶T_ *_΁ D Ug'?+. )J> /A}4RRrЭ+zp=dMCOu EAE$8|nbwe݉-vۨw/3&`4|~~?>:6oJmu ԃK noR@ϏOp4 !مN $`Z@0.T6luE-Sf3!U1 7xfXˏG-:,{;"v3>~Eu&1dޫɑ-v'^؝xbwe`[QS[1u͝8ѣX|9V%`ȾM*Xɲ9)[A0;u6j_:ӧ8HͶUlٰ<{q @CP3:ںK4R(Pu6-Mٷ*y;\FGA8|$ގm#L虯=hA7_sY=b6zZ("3 _4"Ysf.m͵tIA۝`4ŽlgM>I-v'^؝xbwewn- -oa9N1ŚK\ dL!"l{SwhpL#\5xRrɶ(If    G,8t-4G.Ξ   Bh;DZst)p^MɄbrcS}&S{gL7A~\9͆h44" $y BU+U8M8۶jm EYYS&OM*   ĉ8qj ߢŋ1^fFS`B>d#  B"heHCT?8h=5`o%AF BhNiP[tܹÁW իcۖx^l^AAA "zJU[֭cȐ!oj%+;uk2sOAD"\A!^4Yƶ 򪚨zM [vcZ{PraZx+S} GSj=LTk+`̱d&7*$#A_Cy8sf S1ۇ#7q*}4i 2-[ /A}4=[ )y֕P=8ƦII y mQt1EՃ~S'&ΞFES0P)&LBmpʂeGEi,K6")W@ +pA!\A!^4jPz_1 C+Y6'{+T]wQ q[Ŗ 9tQ^= |x\W>z  BA!9Nhiٹs'KWǶ-[zXd#  BK8'B*PnC w}}XVXv-3f̐N  D BhUxayզQ"n7VW8Mu.VHw Bg /hm^XT1. kعdu߲   ,Z_)z)QNJb댲;HWGAAN`xuZ]N6m҃ǘc(bnؽ(SnNäjV.+8;Q8>+S} GSj=LTk+`̱d&7JAAgA@WINi**ލ;Lc;7~OPnΜo$u M vdڂL˩fN!)M%h#/JS}8KQ7t&G+@+ ċIQ IPZQˡLԹPuvR00*Hϰ䑺?p8b1( +7@([WBFAAAV5 ӎNS. 8@Q`=.t)Cm&@ŏ $T6luE-Sf3!U1 WQ{ D BP۲6o#r,x^廨#l{زa7G.ʋgaׁpT3:l9y*rAA;жz hE :+Ǝj}f1b@RtGƐƠ* p\&Z6|d̙ض'7׊BAAg^)H/uCk.s)#i3 =L1:3ߡg1u_sI%ۢ$mtšU   $V']x%o9EEe9r60AHt'A!QAxѶI[jdrbXp\mP4>PeA=դ2m6.1 H  %8D$NA&Mmj[颬)䣧 Bg /t8i6o04Ͷ ,Z5kxW`4:e .O?w Bg /8mY) z=GY|IxmӴ*JK0VEAAxpD)囌Fz}+  B >CZ8fӖmTUW6jF!--1Fb6tzL& -s^qpx ҷAAAC{z ͓n7ُ܎iN'6nb1΋CU`\N*Vtlٲ;>/$d /ܵeHZNٿ CoF)))TbOKcI  &tR__i}>*п-77#d  efT0d=m@ j$-5#GN__WG~TVTnOTY BpAEMs"D04 '=͎jEUZIB>Tqr.na]co(3u   BiG%*0T/R8rUUcv~|O=S-BHvߏ_8UԔ ̲z8=\k0 7$9Jx8BA /ڸSyu÷Uy9T(=rQܥAǩGEE 8T ɯɑ\sU<ϫ8'b{Oxgs8;?{Wuuhfԛ-[rŸN0c `z KMKI6 !eBz ,Lƀ1r{,VMnj*$dz?Pts93<|9͍Q_?6"""I>pSk}dcׯg~3 &ÜØ=ۀ-|z;K;wGuLc{Wٺ N"""""GS)m:lR_Բ7ޜR'Sܰ" ", &/ ,0d8$>onTrqX4cDDy+""gJòe)CQڬG EꅒwqBN^zCZ633lZ㤟"""""GdA+bn=醯#N4oT=FlDSmg&!/ u+?=wc::%#w9dz3ijTk|*2IvV6~)~ 騼|</-^LvV6IDDDD/K"Tpc'#$'/.vrsh \X aGUU5ӎd 6vZٻeo4\Ri(p'""&TFqC{r IDATAayˇHXaC|׀ F!Cعk'o |g0d eedef&ADDDDDzlk4,AkX{K}LÀ#P\L[zdgx"9O""=C[I&h5Ufܘ{#xRޜM`""G=sED$]ڧ#O}3"ֽ2I$b;)ΐ￿3Ǟ`ɢ ̜}jWww)""ݠ/%ODDz&h9U/V5NtC=R?ta:EDDDDowquu+""""""GFt)ds!Zݚ7""sthV}!6\"""".ͪz%NꘈHoձ} O3 9d"""".̪rXCNq^qRrSDDb3WDDҥݐSҭ굼7]#N""""""IvIGnhti9Y @#N"""""$MRV=l-q:ED$6=sED$].q8ͪ>pv?/ ?UUGYh63O&)!'G\ICN7q^}LN :n5N suHlz抈HMGm0l#iteWƬˮǞHXk>|^ģgK۬zI8ɪlknsFģgK)qim<Ժ|ӑYQ8nH"""""gt69DOJG."""""G8FiUq""G9=sED$]:Šˆr9-]Z."stϔw{V]uǸH3WDDҥuӱzi:4U/N"""""rtV=#IґUSDDb3WDDҥY8Y.k-NuOVwwWDDZ3WDDŎ/SZ߫ pEDDDDH"zP:^%q*#3zNAIͪ SfŲwPytѬzkﺳnS9z抈Ht0eɪq8"SDDb3WDDҥuӡt-mly)q#\D+""*l2:6U6Nf""""""GӉɗ\SS.3Fq$"""""G9 '+O?&?fϚ5dgep8bL34W't̷s&9YձCAJ68].rdgebfz3:H_zz),ca``1 @0YGICN=WĦgKznw^0p8ᠩȓ' ļ߷uzNI?M"".1bҽ&M.""""""hIYDDDDDDqJqލ^IDD3WDD%sRN)NIDo3WDD%]S{/<_*?p͋<ΉNI#>|HwAn;[ґR=s:=owHDFpV=C EDDDDDZZr%%qE_[9z抈Ha鬳@vP SDDb3WDD%i oݝt.FNz'UODDDDD$H'$anBqIP#9"6mz;;_c==iKYDDD[V-{tDD$ N"""rD|YV֮}HD=#y!"p]FcMi [ }\'xꕈHl Oj8t.""GBiz%%ػw%IDz'pEDDUVcǎp82gɄB~v0`|zڎ75b?z3l&sN\ٺuv7-d w S^Ñѡ~B>^-{1sw7¤{͚سg)Lt3g~g&XVիyv}INN)Ç 'FiWS/j4$"""=nٲãMc^@qXƌ9>n/=v3˹S)RW|_W-KMM<lٲ͛aw8_s^۶xYV[[o5kq5 RGe:y[ځ8p` _3w#a}E'^$W$^5@""eXSo`ڴӊOp^yVC8HWxYγ^ʍ7R+ Zz2v p\k ]ݟ+ɧ98BJ8>9ܓ\>OަJ8HX@m.rrGm:sD]jkwq-/Iy0|l.4h*'^)*Ǔw,[(~7,+Y唦٭XÆgj(/ ~@0òB|{Zw-'rٿelݺ &l߶-}h4pDf#FFnn@#k?j,^{LAv""/ӑ̶m֯پ}1{x(*0a\֡UY?QϾNC@gɒ?rSl0xtf&c~%az:k9Mo䫣JdE=ҡnaO.mw.3"JKOd2/ %0NY m.W&3f|J}gl۶}˖=J( /o7޸'?z}/1dLo ~y ٳc9zճdذY 6Ѽ/ǭHߡ5N"\6s{54a1۷/ӡ۷/'ϧ W&]r44_OEEϘM;/1m E$j֭{)y^inN׿Lcc^oaJuϞL|U4pڵ볔9;ASK'^ *]߲'U-ī~+ng˄ n}/n9H)қ' 8\ʵ<Ӿu/ss9̝<sVZbz>^3k:UO"ʕO 60dv 81`+W>Ɍ񃀖9%XAn]DU ڕۓR}Æ͊y>/oXmwyFDvX=~Ƕ uXQ,/gD)hI$[V$ aLzS\KI:*+ױuBV0XVU> JkW?Kq8̹#N`˖w׿1eʵdg7"ܲeDN>fin`ΏS rrRH> CdȐ̚ä}_y; kEA1I툜RnqscƜOYLo2uu{YvMk~$o*v4zۘ??q]>c߾U 81iuڵ/ɟڎV_>N"m\T3g~]QƧ-DtXN>GfYY8`ӦR Zu5_Dk9K>|D~ǃMS';ӋaXVg :СY v%+k`z;*rR$Ssw$z bwrٿ4{זk:⥘S*+X}>VT,0h]EE pKKGgk׾7"Gh\а?z\T46&SܹϷ#Ktޱǟj2 It9_Ki@w6lx2 3UZV?aˆ &;mwz#FΦMoaҤ+=x_'gYE DgS(.N#)y*~"չ x饯]{g]E$L6izt4h9.g멯ϦMo#fͼ5ԩEl塇fquul=4;IWF'[?eΏij:m[TUU~||G{o"6IQge f+rN~tϓ+4]|Mݑ=:xpEE^kvϕ޶N@EzVCC%ϏMS_?ʘ,ĉWDl!>~zIWrS=ҟ!̞}V_jWn„KYYi:y<,|u,Z+-U>N#N")j;.`$.W&Me{Yٌu_s̙iU/[Fm6n|= ^~.]JKGHYÆ͢hLJe:P(>2k2tڃ= 'I>v3p"M7}DIQhI$tu?%nWҗ '}+w޶>744hw~9mmu_=~h&-[p8ѦfӦТGԣ8-< 8ӃǓGit9\w;qZv0Gg9t<|\Lgnx@!Z>шzq8nu)scҤ+((8lLӁ۝ˀ1q\p?wg(7"UV=br <}Μ))B ޽YSJW=a¥,ZK]ey :qsk9p`=[,dժϓ/Pҥl{/}[8th'=t2s#N`˖,XpG4ӓeʔZݗ.]&ME~Ee3øqR\_ mtCр +_ |@xD4]iƚ5-si?#;չ޶N@EhuV3F8uFhI$E^\,+ǓOQ ̈́ u'S^<kVff17޸ŋϪUOSSMi ̘{Kk?֬y. l"7w%%97Hn;w~Ν'-w?#3z$rt,q'9:{.ytuoffym4̝={dddswrwv޺NEDz08pI?]98AKDDDD[n+bE]^LINN)Ç 'Bi G"G8!n;&'""""""5Jʪ'"""""'$IDҮ""""qI_EDDDDDڲ#{7iIDDz#v8HȪ5k-(<]iE^3<#VVDDzwzt7DDDp'54UODDz+'z""+h^a6׿Lyl߾xm|oH'iw\ψ4˞=yKm+r{)iunҤ+1Q7Mmb0 ILOm%{OoØACHzG7 ӗ@b2ZJJvQY[jcРLx9ÇϦh O>>Ajj6l٣|߰ ۷/fݺcNkkŊ'6d8WCy,XAs'7#OgӦ+W>I'Ѯ+;#Gs(iÇS?ǓO Huu[NJO$ر*3dL ɬY?lWǭ.mw.3"JKOd2/h{Nee3q8\\̘-*yݟmۢvM|56XS@ekbF}v0hT ̙߉INSDDDG?=9킦ti/B*b۶MӃ1ccDyZر\^G0llOÙ El!a2a¥Lpis:=̞fI2wݕ| B*e:R`otj4']4LE$5'"""""'$8$aJ!"""""C8X҈H&h[D4$"""""DxDDDDDD1"4$"""""'$8$IDDDDD$.#'mV 9:9Z<~Z'"""""'$8$IDDDDD$pV=eiǎ rV=_H@D9 HwCDDDל.7@Sʽ u5df{o2Kq^a;tO= ??1GvHDDhN5N""""""Eb%eI")"{G:#"""""ҫDb#N DDDDDK_~D}>QAhHiMG^U]͚p{fftm0@  QPй"""""CpkjXr58%%]/oŔ);>9RJۈSŦ͜rL>tU6C +O?HY p(((tt-%N,ehY)c8_/p:#c|5.ͽo2,|_ר_/l#_L8 5/ҼnfV6OXyȹcc5.qwUDDDDD$JiͪJ k3 j9s=/@sɼU<ʗFRqYmqa:Kut[EDDDDDbJxhvV p3(*aM15+rT];*+zo,o趮5NMM-㣲>@M٥㲱m+3N,P%/$"""""F8pJӀ/#5|'/C}ypO2q޸Mغan<{,TSiY,"""""O$VJۈf5HEztYw7r0eR.ӯ99ʷ{Y4d C&qƏ| ;*qm9'yasQ ,.%-q)(*{X鞄^o\o#o_ >Axz^Ձl,/RcezNWi v鮈H0ҴT89:`)YDDDDDDFݷb3` rpɈ$775qAM0 Jc13]Л73Bm+رm+epEDDDDSR~{)o~9oWOEŌ0ofFj`0@0rr!'7ñcȈl(_C==s "S'Cxto>]O..c+}Lfc5vaٵcu1vMMcߞdR:d(IӦz2ځl"""""ҧّ =k 'E3=66?~=_[}Y*e٘IguO`Td]XrWw1 0xO0N=qb-=+7{E`N^|~ul>ؖu-[}w>&S3;0d;o;0/] ܾFuݳ uS"""""rtӱn:Y#9k~| |>Ɏ-Y7_p3R2` 8$ԉf 0j8AS29gc2tȐ.+"""""}_d.qj9bŋSn9!L>hsng0 i 9qƱvZ>|)e鯺o@U^F°2^{ _/:̬,~?vJ_guYYYձ~ƴ/"""""} `wyS c9p`{[3 s,vlP"Ty<̙$EDDDDh+u~S4~r ǜ9ŹKߦ,q{<:EQFQXXHUUP\TmmHק 4CQFmnoKDDDDDzpG{ѦfÇnoKDDDDDz'#nz6!n7`Cڿ)\;|]"SҘeY&N @'~,UլY of&N ۶1 #F&GAA~ """"""Ʉ8N55\g޽{y2euM&,.nC]dUۭ툈H/8Ul)δn0dB,N8prS,\=8:to'.a:ri0~$ p:8&uK~ɅKZV/ 1DeR7UPPp:NCA֭[P\mH<9/aMaa~޸rX-wM-O>5-+J Ȋ$hhrrbݒV :̈́l‡K)06R(S½IԄ!;.;TTTPUUENN֎nI8N k3 V)po2Ͷ͛(-Ñm|>o&LH{"""""t,p;@+ʽܶ_c#NAee!~: 8f䈴/"""""}O\:W>Cr|Tև)?xRes,b54qڊ ֬Yixt #I"""""rK8:=Ywkk9?Wg{I L^ P]UOa͊WGe-)***xy 0uHLI8y5v[q&<w8P-GCc#@ ͻ\.," f .-bZ [7?틈H3p B֒lju Rɧ4= 6|sId>ۋ t8E?%,SDz, aܿ _ &||1}'JO*?Ap #4 Nv.`-cC5PgnK)EDDDDY$^|maM0ٸ׋+Ӌ *ߌ` ,% EebCt XU#EoW?I:O͚"K,`L;|-d`3H}Â#N1%X+ x=r{=|]^t9eEoij > FP!abL2-BM&A ,`b```aaKԾ/5N Jm9`Ä GG)o愋45O#λG~m i2m;~#p`~9m0Ђ3iCqPCx*VJD틈H?>NVf-  o/^uS4hݵ˲,>˲իؚ!_p0 6X}jVO1N)y68 p` MIK7_߾>qqF8pd]Lє㹻VcJ'r1qTdfhCS y`)FZ!n`'Aj,m_DDDDDNd9 04an˅$s K#DƟb <L7&8GqQ@~Kb9Ցg6ˀ,`>NW,Ըӈ~(L m;a``h\2vc&Çw`OSM B.՘؉E%> @ȴ:ܾdYL0o\ǞvBC8@?A~ۮ`׾O-ҋ <@dcqK֣z""""",8u`ppP8v4U15<酀#&, +o߶XRgGf6tΗBv0 "T$8eY!j͛pfP2炐 S A'] f&x3r5Ȟ8)o+`IDDDDD:/nd&Sρm}*׬goE5V0عݵO˜oz~A4hS~^>U-b/"""""S8pj4M23Yb'͚EPs&zQPԩ|L>23K>a$5u틈Hs4Mr9x7|O`ࠒ7}|ᇸ .='"Ю}Nz Į={Ndg3pJz{O~&Yr0eDB}M=57Yٸ=ni{O `Gn^ݣ'`>%j_DDDDD#ve{a $"""""GpփL;RײmP@s5ut8/>%k_DDDDD'v#`0bpP=]}v p:[v=ѧD틈HѼ<(nT Q#G䣮:p8>j׭# t8bӾOqC O-Vȏ2*֭% U>nˢw߁틈H3y(l`VGSaglwmca6Ӂ+Å3ÉmX!?@(vm~ %'7 fN:}1 LlڶhӉvMMM~lö틈H)qm&,+w$40& SXe8\ 4鴰|6v \զ ۶hjjOat:q:8heYV=\.<ÁoYl;f"""""?ECamYؖ洍00Lˉ01!l0qx]8&i Bxmۈ$JO'۶1MIFFn*x anz8P__/N"Zζۏˁup91&iۄ 0lv &fA ] FyZ0 222p\Ѡy4y^S42331M PXXxm'l_DDDDDH(Hڭ?g^' 6ByuUb v4D?T0vZ2--t:1M34呙4MBP4`FFx^7o^ΒV'j_DDDDDH,jENۻ8%MB @.@\k0zëU477/z@h4Z|gϞ'ٺؒSsSqFҙp>V-0[npWƴ8t,1y}36~dil4pBNWkm͆X IS4% z~4eY.RbP8`PwVR9`v0QŏmUVmr[l -WfZD0-ӨҘ EQ +0Zk|{ケ`-}}}~R`إo(!*v,zAQ%- Aqb}{ x~k3<BxYx믧Fb |>BhVR@ys=sRk} IDAT) yYEDDDDd,NYQ8 r4۶m;ě~[Ⱥ5R=zmZW9!E |8P+V&MП8e2,f_DDDDDd1N&d0`|&N{zj4$G'YeKyشqSmBʘ J/Llٲb7R]tQu6%LN`P@W*ӑ \ի_(R7'N^b\787⸦b!ۊEvm +c*\Pl1/HӹJZr]-O-Yäy `-Zv=y ^ˍ}ʸ،W=U$BSh9*X|yu8& m+fR)RTqb ںPRa!*x27Aa%=Lֲ\,X6%BThQJ&A~?)8&plfNY @WwWq=< W|F$e'iFKiL͟z}/"""""cS.qAqZ[[GÜsi2ի!ʕ+ |Ni/&.1=EDDDDdɧJ58E"M\=ͱ&qȔD"aV"""""268 CsK CL0D25lAE!bM1BNIpSEDDDDd3Ʃ1H$L8M0L1U'/"""""cOCӑDb4$"""""ך$"""""rhp\Z<% ?Ws|iEDDDDdl'Nl6/`D7\"`mdn8bWy5Ì#fO$=LZ|1W7l Ŕ﷘j/"""""cSĩwo3fϡ/G*5|Sn CizƵ kL'u!x7$I&LHWO-SEDDDDdl%N|T*xX/]K///r紏K>#JkrLbV-LiYMR s QKkY !ʐ'bNLʗDDDDD|dd8<8 yxlL&NՋ)w}8O㢫>|l858y,6/^\wsyW/¶G~v~IDDDDDFG   Z6 LgH3>20qo!:o#߾); 9J͘[yn!yQ4:L>4[DDDDDdt uc \ɍrr;г^-{&2]x+\^+wxXk]l;c'Y ktzqP:I,xQ|m7m㣧d:Oc10}?#h?D# 7! ҿ1J k˞sWk4~\c29Ek,y0r!x1թ_DDDDDFp$C'ȵPMjL820ʵ[ۋAOopFs,FI2e2YSEDDDDdlT18aYܺem1Ng<E?!>cxk'9=imm;ͯ'Ͱu&.bu4,lm+ʊ?=Կ>E'B~ni*_:c]eGEhv˭kmkdY24]_L.l|i:{W+b͚=W!"""""L$$M+4c2lt||̎UMxEDDDDd!<"iDDDDDDF&NϿ,I,7^h#H^.ql$¿w"&ٽ7ܱb3$5󃈈BNMZkR?eŝrФY|oso~"""""rPص O4`Ύ|>&Nyȷ N^\}t>>{Alƿ\OLno/n߾c&9^ueը#"""""d2nm ===p۱/|irA~;ܲr˒oo' U=擥].0Ŵp5xt'{WGoT$"""""ݻ:8x˯ 'p|)+ hqR$"""""moe4s.Z[x '%M"""""r{exDISɩU)c-g`-c+ѵT0츲gcs"Ukmb0ORg@@>>eoz`콵%cn_PT_Zib%pY'ay?vqhN<-ߣy\jlncĘ"T.+Y)w&crs3ɟ䢿1%R8]~[lI<%w>ˍ%kHt+25/8c0_gce8G@ɱj,1T 4 )sTؒr*c竸w Edt?X[=l )<[ S\Qr_|e]*>P6]:\|齩N\b(qߛzyXDDDF+?F藘HN!-2#(tn#WOsL~lqp 4_DOOO@h5sDBCDDDDDF\Tc8S*vF&Lɓ1#Hn+̝uyLRL`뎝L?l:ڈF֨lj57}gGL6ለ%NzS(d\kͱp44b Jt("""""g-d?χekoEX hxcM(e',@ӦFo#kJAFbc^q`"""""ûAoowa1W7l }tt<(i/"rs];;غ};&"Ca1m--̜9AHa9%/2M6mdH4^Du:;w<$ ٹ`(6!#C,c玪q&OJWO-E/#hr↋oamcfٻ}1_|7|a^Z|ҹrIz\-uHݔ:zzzH$S?=CCjɛyEiY $d]֗[ ۭzpO`n5Oǟ|^J3we`$S`.M. ª9|a'1e+XsݲUY]qV-!O;D3˸y պ#"2x=?>8'|2K.e+|I1ɏwQ$"o q%~J޹'\vӹ{7袲l~M^ZnHWy]ݜvjѷxϲa+8Ns8]Ӈ}Uϖ-:M"s,C?~( b[xjcyDorKOɟ1c4~?XZ1ĐǞ̡_-b-Np*aCpα7R~u2'Qg㫯g„ z-eU}$JDmOKx]~UJxm:nַ8o1OL}T¡P×ff|>_'p 3GBT-[[}fV&Ls)ns>VGs};|f`]Hluqxﳬ0S `"qMЕȕsaT7Iu*ouN<$>y%ܹ|9^p>W_c|it8;̉}:7\=7|&>{?0w3JDm+b㮝D4yTi|%g_oicjmmUh4vO= FiinՉpjâmU)[-4q'q¬:׼M㤣RGngLD"yz衇Cz$I|>P0@x۟Gvƛ޾>nNXp쐒}@R!5lQ,ZM1޻ox;[p&/=~s9? J렢ZLyǺ%_z\|ٱ4ש;""7^g)p7Ibߦfc1ҙ /]ٳBgX6rEXmlq1 V,[Ȯ_l28Һ菳2p+-!_`sjl%1,[)pWni=Zl E-vk`)t"6lc,u )RxlY|շ:K/45T4B|U\{c$^Z\,p{qfi֭_O89DSs ]3 yT*0>/\oyfڒ/hPp9w?m/1Pw̞K$ΎN^~ >Sڕ8Qhdf""r`0D2cK$a)lʕO[>iLL$(G/+.599''LE!bM1BNSƢ]XZ(mj"W\RSSwl9x8 Ce7,̷8yջC|6zzz3Qk3X$"""""%J3]wE3#c0Mq3} 锒&1&Hl-Itdڵ|3a;8IOs""""""N? &.o#(k39;:/F:&H$Xqy7,u ˳ RDDDDDd$ ^5{vIDATZqD"|3'f3Y<6řT\DDDDD`64ƪwB_>gՃ-='y2Nm/8l#G/IENDB`muse-4.0.0/src/doc/html/single/documentation/main_window_with_arrangement.png000066400000000000000000005124151404105311000275520ustar00rootroot00000000000000PNG  IHDR{-IDATx]`G۞$HBТťW(+Ԩ-m)^Z"!$\=YfyY{]efz: 0g; A $H ADi#+)?_?IlZB.Sɰ (d~3/qV#V+xQR^DżU=d/hs= _?9^@؋;e#N  370?78|4B 9_2F)\ȅ ^C arXehS.P)=6oriZe|]FfzΓ+E.E!C3(x2+C 8]yyVԺ݃q`\Cqf:t.*HGAր)'vFO$L l/O.ϪC3z\\=ͩH="Þ kZ}10nGo+QCg[_v;m2Bಔ A$륨 Jk GsbS\|)Eh\q W2$_rb:q˹^:80 `~eQ= ]EFwʢ  ?Ȧo"˕%'DҿrBgs/ot.J0r߂0"AXNX8ʩ yszHiJg}q|ޢ0 * c$k JOd "֪s%PռH9h7(;޹U | z T˵bI_ _KqDGt2h;gnRo}x՞yӚ]]wV<ӡ \4Gk) -KVvvĤ~QòҢ"#T*%Hh?{?$ϋHEpqZ +W6rB(Z:)*G<Z;u3dZ\0 (!dG6FĠJtA^`fQ^ɝǣ\8)2en@WK ^R{,Ɇ+% U˖oox!IO/l%:?T2GGfk^++'?08Rr}LuV[@X\GYt  5k p %8$j.^8h$Q!7OB"D t]"*Q_6T'^%ԓ.j_o5rGU֠,zȂsRNn3~:ʕ˩1gkxDQ&x^+O:%jQpn|!uyY VP~a/݉Dzk]1TJ }gkS:Aae$5ژ|[ni5fc(*\8!>RCNű|/LE8U3r ZGT ? PШw+@BrۘNFxDe!\eh(%Gư(<<].i | 'uo.2B0su.S_8eJuxi4!ZAf3vV+k•d yHOolvSf<7}7Lq5~%1^}5e&x,[;-v;ՅIcJ[ъx4l-DyT0jxǚ D.9e_w6_{#쪹c\| MxO.,F8`/ `>ٳRJe$޺m̙6wz)^90a€>|͝7ĉ˖/N\~Qj4́WXTyz*+ϟjXYqY|Td5%X^!t \uNR}Vc'.>.+hFckǍqBXiȪ>@PLn}ՃZc/S=ty౫Ne۰ ^ji4c>Adwأ񰃘zk83CUՃ܃/n}QSV]ŕ씎=WÂ>Ԏ0|yЏW_ϚSj:N{`ɢʜaqf۔Za%풨bP[Q:J+982'w~."?K,g\k};>{Tay@)/4@ rwv7ZʉIM< e6r_[ϟM`{Aw ZacoplܼEϨNmXYjk5]:wSoD(*gta5fgTo-*"r?l6~@ACM?r[]Pɟ-2 %HwuMI=͚kd:=)`Mg"r^1x Q3IF K+Vs)J O6 #1vEjUvS$JLd r͟eUrTj0|XۺO/ikO\j#&mQ6#OӺ1]flƿX!uV[hG΍{>shC='/il.ծԎ?6]XPLevrǂ¬B2}ęOi=-nG,ggϽ<~^ `V'{N*زRd@ω_%ڼ\Zv6hNC4h6-lĩg|jS]9?h! N .ZrO|('z\sDϏgQ(wmfU3fO`mczm3mTh m*?;QlU|2>H3) 6U(L=Gnk+7e*טb(M`-Vpzћ1hȌזiZ쉓}z~:d4yᦛ|8AW@KsaMh V,b~,QBB ;`6܏`ƛ+~`#7lR@jהc3VqB]݃? jâ`Np|D֪~'L+#:He1RZNRպA%ܡ{cFu~}Md h<,,2 w6-Nip*W$'$J`ܬeW{$%[vݣ,̦*6)ŊXaö? 2gϝ{;w :tĉcG*6yR]6+j 3^fw Fù()4]0F5)Eh4GX'J኷֤.(苼OKˌ"h *`6n. .9]ː&\P-C>35eɲe# 3oݴ[BHϖNbܻsf=|pDlo^5k/\i˟J9[]렁93zXJ&辍ޮX–?SWwi k4Vte>:&((̩Ӎ7b ]DGV}M.D!Oxу V DŞg8•U`pp+oOx>.v\g_wTDZfjQ0RXW+檿}J褱[=S;7uOSji; R5ЊmFCY~ ")׶_7n,j<9~gZO4f3f,:s0G<]\L ȿx ]5) +xgɡOciqWV\a-gMB "=B cjTq}9+ Rg<էُ%ct)%Q 6)*,¦{M2XL盾aUݵƷ.e29׻ 1&3Mr!F8L2)R n.@7`֎8hꗙo-k*I-BZ ~Z.+vzAΰxM-'>[._\fl$5 54ԫWw܄9ODFEo]۷p"xy撙Vbk)))gw/nETDj11iw*cۄ޼}d\\' ~"EaQwApDs̀TN S t`PPjd_U ꭘbp*Ώutwfq|E&dZ?>J`;R)\uAسSΙPZ5VW/ xa~Hվ!R$tP×}شz~@t `M$_jcF 7,_k'?&>zF7b׆\q#zkyM"\Kr|u5H?|\E4 o~\X.tc]-XQ?JE,|o&1Yn\UzvSrj=`zz 3гeM r"wn|8Z@^}HȣX]Af@yd9,ݹp H P{CM0'B(7oR_`$'ѴiZ V\. 6N<o?sGz#Nn!bYàVZ*"VWLg wB޾5U\ @f73(a6p0geeT έldW eFah6̄7`u9.^~B:DPH{>%}:pouE\X`}=𒴄AdKC^8–_'`4m{M}滭ڵjWuAzoRdp111:]xDxAAsMfFIn(:::4$D+^ ( rvA ѴYI gv e(C`ts Ք=կvmy)0i O*BC%v?&߁@c|~ AV)L藘 ju8>7ߊ Pp3|~\21 U|y'؈qxM7zZL,@PӃvj\NP=i|i#`9]pCQYv‚Ok rqO(PC2ik-  DL@ʨIl&{270Iiڅ͐ j* Z6#B¨"" צA>'[6\vt|g -" Pg@x^a "_t)n+QOZ /l;!iT))i -˝BF܉ j<׌K‹9)ᕮ ϺY8s|y;W)WI@,YqOc-*h,Hb()q\1.e\Rٹyub Z` c8te#\`uǻJyɗx~%pa7q:%8DzJgUTm9^BdG `NCIxsnL5{ DBYy%wrB|_p uwl{(xe *BPy7N7໿pܺ>zڀB .. 2_l>;*zM9pɫx,|iGEK~RZtΛ{!8l $Xml sIKH L' vsn*+PPH7JNi(A ki# FJaK_Rb!3\.J喗OyJq w9w"8!DY[m(!Qd҂9dA+CUZPzY]ɛ9@o#ņmPSh kRPh ZrP0gͯKIn}A 3h^((!ŀTU_†U7IA)AIZB/@G!m?w]/p{= U%H^O해lK N"/ݬC *@h e1xA &rTA Ct|RA+n(2~^vk i㥊3LLO7Q_Ea  ^ 1 fJWo= P?-\! Tg]x2@- = hӜA P d<. U\)!1`na14Ta@0zBe(v#sD_J SσNH6i0s)?X( @?/ %@#|J9be݂Gl TNl#PeBkM{oo -f1ˌCs8y@YT X>\$1 iǐ +\ Q@l(z[ix*4ʄ(Ph6RCedh3 ;ppJ<P/#:Y%6P+]-DHD'9hn= ϐbYO|,-#^&Do;Me D7E@eQDYK$-!A7DzvyZRovMGv]s#eA珈չjXMbAogwdrUSLL6_9xuNN0_3Pm1cUxO䴱:cݵE?szjAYugnQXD}0.BZT0T6}(>YЖn b,ߤ'VhX5Q Zt[ȓy+nafq*7=3'[/eA-s P EF 4+ݹyxD VE"AQ7HsޘdC)(-SiEc(-^o .$"P^[B‡ A.El`;o/gutS p+Z.%-lؒ}ܓg?ZF{f ̿g|~ڄ_+[Y1B}X-[(0كpX2oZszߵrv=Ǯd{AYwkַwd]w=Ckfo%.|%,+OA{5ג:XVvQ TAB`Ƴ/o|NSΕEa,.L﷬効Mu2翛,9y]TzW=}הUˎj1:%el\ }pvj%V->KZ^LӔ.hv2#@DW(lA2Yk5awD -(\r~զ>nE3A!*,*p0@mEA!-0ԗ1PK򦠤%$Hv3  H:Φ60ڕhI۴k>/+gnI2FLN4vxkêun݃oY/k'mzt[%k?޻Qa=bǭ{, :{J (/Ns<{ݣ#&9짽OZBDΕfEBZ0=8m* T(̩]CF50u5)bYVaο\2끨JŒ|Aa2N1Œ?hg#1[ZD^Ĵ)`VgF!kxLK$ŏ@tAQּ]U⒐ ?BQ n{lL1]ZvɈ3˷;I\x$Lu͌OiƂwyԶK|?qުed+מ=V86e1]ӎ:N?-:]gx5C9o8]O0Uv$_Ve@u[`c[A ^;" 5Lظwv]=/Zu)'jI_ !IZXom3IZ iznDZ^cmO:z7{5 NǥjLjXeI%^y!(qTM[j[Pʰ ˿j-|S=sm]em- 0{'XywqC;ZW SX4mɾG%QV3NqX\p̞sS C_82p^ݑ\? CX{/?C:.ܻܩu;aT\2m><ɫSCmx!m==g;3g!#(=C`d$[Ʌ'-9Vk? va_\_O3E10 E_rF ^ p>zȵT-]7f@DTQE$[ vqc'KvIٱ3 W_Q{~XGe/ZuJ N|vҝ5l'?(yޮ|'e' k'{^L5 ݺ*Mz!w32Ag9:<އ Ub}oLisB׭XTZu0$qK78ӣ?n&2Q_h|8n*aזj }?6k-5Vk7m+kMy HzlUr׎=f9ω>sU8Psx_NS[E>Ր/5Y?55{mQBBI-䂓siFo8QI}԰N~[o/շ^|xu~̦^4 8&6A pID ٛʁb|JB-9Rqe}T'A)_a洿 VAtf!K6ש>(LiDQ+Qfq]sAp`'U4i$mV u'>xjŬ.(;ɇv0?n/P!i8&">H^okAel0WU~-2Mڕ`X ew}WB[k^Oi-Srmλ Jh!N^|drnΜXՏjMX9KxsYk(Ɋn[!0"ŭb c`T&*W`֞K!FjM[vRиl y6r@j~H{@Vo:cKTbu[^ jYuBZ T:XMcZ V_oMKc󍦊vr*"R<0O("Sz*Q܇ z }g&wt /w޾ Ө" 3TeXΗ5.,?O:YPq>S|֝RGxߺoIϺ[bM[v̈́jLm5 }H/f0sU*K5o4|ݩwжX¿k֑udѬz6NS9٠}4Ռ]|;Yƀ5-rNi18X5^z3M!l!E1T(2B4d S @\,HS(rMV(ȹmSn_O'Fg2xUL4e;(7=i~JUo  L^rg'紖g8͢:mKvsE7[%;*0:0ƿoAV!ӕy8Ħ:Fٕ"ن&RT~]ʸm#W-%ihaPPXX~]oz뺞׽_;ޙ|B&\ǿnQq۟IY;i!}/?iN8Kv1#zAZK @oD@s?ec9G<8O)m= ӓz,jla'ՕVܱ 02֗jF!z0`j [p yP1%[oA \\8]j̙׷9bgވ"pPD\|E [Sz =ߊ+kpLѧjOw(9ҿe|͊{@ [~-U\$<_;&E5 {[}G͢.ۺf7;fmS zB)3:׸Չ #gA.v}OKǾ;(2 f='RfTݸiLbe.nߑz 0#W=~ꏵH2ԖGS2dnԯ_-^?RQxe E!f֝X(z mi]_l9o9&mʈC#-ځ9u{OA i $1;l T+e[T|~7K= UjK[nZ:f,Ndm܊ԉZv.{H~T!P9|P<ܞ ^kk~5P$.0QLD5;l× a10 j)׶foR[r-c,na>4elNg-:zb5ôjn>ȷ)l0r]_NJ-(8vʀ'"9ثRE&rEou;؋nw Axd2PYHÑ.OR26ynJasuZ#7]"Z߱BYZfgQ[k+d{QH\S֯`ͺ%8׫\\#:e8 (VCi [ BWgܸCwo(vPte1 NSNPn*Mۗ]yJFE䂌wzÕ'K7}(;}.!?qa?F ˜ӋLTR9(w\!_wZ(bk \L}C]pF807ķjGjBhlMtMOv؈G0 e(fW`̐FØ%R?w.Mb2z,y{_wkV#Mkyv#'I gp`Ψotۥt;TI>{c^&j;z:x>BdQ7(\4(ހ_K*}`?ʑiwׄ),cޘD@uz#c8̽*$!AKκv6M(" _exQ"nT2wGvqgT 0 xx[x(ř-*<6(,w #$!AKwfK y o_pLFoF x\Y*%nw줷g>zKk=,է̷ֵȱ*diz+k2'8*~nOy(;Y5#e1ԗpZe{9e |N Q4n~HBQUeUʆ/4ih>,ROWU ЌpX>h ~Æǎj7o}2ucH&VAFWV@zNAw<裂~,%羠4^ RQ/W$ e5Սh=O%zS_·w@8qR&-0+f/>y℘p휑y`W/C4m$22yNnUnw۷\./l(͉Lx> A)R{>l ~ Ic! ^TXcS^$6Q|J$+|'ލz5BŊ%KHڂ IdT~Q;[ &;44{wAgT%QjJ ,QG 0?Nrxb3t[awjqh 9݅mݽw_@Ζ?b lWR%n2HOw^O?XxjS(@X/v]D pȘjuNIelVZ_7nkzxj9lHy|-_!F*Gjϴy ?PP90FːmO IPMEGEky󠰠YYyt_:th2ah4!(8$HH*p/s pʕ**)8V ={aX%)Ov;2R =o 0v@D(AxB_Gj|K0mlR s{37(Q/ߒ _΁݊D^ёSgKVdfdK $F16.tBJ% .(\"LAnYVB B\<$ > M^SSìJXmւŊ/  T>J,)-޸q: / 7UVkΜ>j۶i=0#[n9kIJW-GSձ];DaϸwuҥݹgEq5%epa\_:uXJe  ; 5w ^$Bb[2sV-QR M{1(tZPrnM#1\IT +))'Orr8}\VLDp^1mhyfa+)N9F8<Vp?},t?So܀/^|vʕ*;'&OLL|PG*Ƿش~\IjPRXd5݋z]sGsè#4^e`Pa iVە@ɔl.'P9(BCmѐhNP&0H e2xu(6[r%T DEqjz-`2n7B%s&])i(9! p9+xb%=$(Xۈ00 ?S ݢnC]~\-Nܱkݺݻwo~5lƗ_80++kuC*R%QS pPPL 7QFMfSmOqb&f?A| P@loh)@hڵkWX1V{{thJ&@m[p:H Q:~,[0"}dq"Gy޷͛jՊ?nݢ94$c&-+b7hEMֶl@crAbkqhȹ=z-fU0affa6 1fYBB5q LQ{ eP!bAF  w5xdb6*((P% Oi)BE*4j -JiAW،6a;(*ܴˈ؀ɑ 7իBf ,IχU;+tC LH Q[(<ٵ+}n9AU))=]1Sʕ+>}#<˿;0U+x&?eޓVTsu:]hH(,U)&x-?avfCn1ѯ6.߾,'BA\\, xVɋa0{ bGјj4ج6m 88mO*_qk{+rr"7Nd)=̈́Ab^P/^?|H`rb~vZ're.Z^̣nKw+QEVzlۺYHXx`[oeVMeʹBL:W3E0r!.]7֭[GGG#2 [l115WۅK^M-!Kо(cϋzSdĔQMT/_߲eKÿ /^j*H4ۻME<_QZpШk׭fiXҮ\ѫx-{v k:3 8M?4 $rcicJZm7Ȭ߿ީӨ4,3k (HVn6c7ϣB:5EEC ,C@ɒ+8\/@*åPR` ņ:>;cBG7;NAsG*-0[J+ A' GO~u"9@&-<ɉC Drg `-rpRȵ'Of]{vZ:U 6(c< V D* .96nw-)w3s`,_2fw@9ٛW;Od @,ZfS6'8(q`DFD\OM {͒qqPT F RCBB@c/AWz3hPNݻ/f>rbv횷hyF {%*|2Ÿp$'W'Oӭ2Re{f4-J 0Not\~+k2-Xп_bē323WYɸqPN̞=;,4K7}*'.)m[B޺ L؇###B@B]ݼQ[*M )77C4M۶mK(j~J#}ǎݻw;w.4/qs- ?~QF! ɱaCF?4mTC'dCwiҡÃt{m֬ N(-似cO>ኰk׮ ,fJ{;  ֣7<p^ڸ?h0 ѣp syH湫OjUFWwmLo* H N O ʔ60{\}VJŊ ?G'r`6UP'yyxT?|͚5I> *R;$A7EA?G"C MQ=}ol[~mBTցk6P')P'X7AA-S|:;AKA#.Ó. &=,H<rqn(> Jj""k4s"#`h`ټj -vy턄ʉ=z$CNxm σhV-[~pl9cdua$pt5%6ŋCɓO>̂bѢEYYY^>}̙5hZBqHrcǎAPϞ={P\kx^:r yu8өS'jrDF)M-[@U? ҩd^h͛7a۷Q OИݘz&h!#}s֬] y_ƍש)RN> .={=xv"$W7Ѩ*aK*'2ViʓuxtAXTfdH k~5qd?-ɑQ6[u`?2ŝ`\EL !iVsAܹr8F,#Dp c%RlL@cJJ뚭+BP?Q?ͻL|af׊;z_!LVգ$H`<$)? ڳ'} FJP Zd^k6l-xQ=Рt!( *ɘB5a'hZl_{ь+ h]x9GorL,\&L5J2P=k4EGDD&-s9LfRhٹ9ejTt W(6]'PQ(} FE= 0AGcyT̖?r駟\\~bbb (/ύp}nw?p&(d7y }@ݷn>wQF]Y 4 *|K^ǙX~(" a8- 07KA2 ۛHgMbA\Q Y2f*#bm$=ǁ0i2  6ܹ3##b@RW(4lաJ$u֭ۿ{ŐgS>$RB}RPPТE(x !0ˆjfggÖBU("CgϞ͜9^z v;|&O0+PQX!4xxshWC)ۀC`xV"qnc {R(Cf# 5@fW{gض` SrʔSf̴2ڿ+u[+SڦlKIqocӽwwϟ-D $FMǨ([*5Z5k|rr6OHJ!^p*4bʄ7A G.M yN:91p*f!irEI4 _/"=)9}ss{^,kh?8"[e1d>rЭDD ƬbǬAg(b #m~ɖpVzlS.YTflde@QTp һ , r)!R`F=]}s؝p5ۉncT}7a6'~ Jj޽{ O>ē'O@8}}}62xʕ˗/!~%vyqZ $ܸq4A UVVuǎGriTJO&Z't?DC3pQsĉw;rʔD~C/0m^N@u-4ƶ!~ $SNJPصG=MWޠٳ=X,ˮxKނPuÇ))Hݺ̀׊k׶oo!+mK$&R$9X1w1[b YG؅ \ - Y؎zJSH7oA\>a1tr%Y[ѩ)&UD!1 {`1%X\ Ԃk=:_i={Uq_v- M[.Z}jg*ͪ64\V c'R˕ ? E=ٴz.f_&-Ts%dR>|djE?$2嘄dbK[+') #BnA\9u{ }"\I܉z'>۴)gϋs>+2YʭqTr1 1] JxCMWp;xdf}\qrލsBK 0ƍ8e*)%KdHAuwA+Zi[5hR3ANd ʙvr1vNp0w={۷o߼y KtJlWPD":t(h>{F dZ?@pʭ[߿ߦM/_',, ԫFiG{ jN_ 6P1QO&<lxx8"I=f޿c3,&1א:ؒ{iOwI.Tc6ͫBd!DE97!vY6'Ph0$3Nf/ 9~Է*T7oQfG?91\LJl#"9L EvAŧ>!7@QOD:J_+OfGzj]b_p(0Bh={RGj6[d\m jB("݌C=;AFsUXti83$jtFGEG\~26θTcjԘ`5@-FjB䀒aE,j7"ɮU!8{̠}lxN$E2dh(̬p2ꌡTfիQO>O* aѿ_)l۶*FO&:܌餵wlt©'6v-6MZ˻…9;aY|$zxqxQk/L_( h2GD4rEVB( /GTZ{.]7n>'7 * !@6$$ZjJZv|>9}9̎0غuk`r@U@F1#nyɋ/۷͛S644 590Np -W#&w5P_Ђ'P̿7oG K:\簡z <ʝ@ IZw¸Pีg]6KC6! r)f1u1 "X2NzGz ȓRM]?LJT"6Cu#jL;dua;!Wo$Jwk&F29UDDuU8h,Q}1u01FNCzpPc\GA=y<(DT o) -x`RRt'jJC<&"{fۦ9ZLkdoB>"{! p@iW5l[dϞXܕ+H^ĠA%݌m  oچHDhSR š! !!PIlh@MOKd\ODHG@1g .a|8@dKU<`)!@Y'GU6׮^oկwA3LˤTl V%J뢞\Z,&+"<<",\]RNNvdDM!?|r9&L&_es0hš=@eT^Mt>eBN?i҆ #32NΎ]rsƍT}w͛5߀~{IxHkmsJ#d߿[uXvي۶mРp'2;Çoݺ0y{DYw[2\r6;Nj: NT|cJ,vZßaÆUTټyL&?+0m`(ZBK:<q#T}v;R[h8~c(ܠ@@bܨ' yX!U[nժS |ɓ')G3+`˒-oEMY5쭰qa0)Lb9!Y_ߥT`ݿ[U"*{~P6囨(W^<}}kOQ;v1￉ dwB̬@ύP+Sfc>ُщHI t!O9١EDhsJTM?:dE GKY\O飴>B9:aÆ1cƀRo-09fr56p& U48W\ VgMŋZglXF$j[14"QG=ZXI4\BLTlw3F."&.\ܚzʷӟ=(^ڵ!!  7nzHrr޿Vt@fͪV_Bضc'p'ׯm۶w L u ܾ}? <ȡcݖԓofOEgd8l4sh]|}7mrįݻ7BɧMAr`ذa+\ؾ8؆81ح[7*Z"W n]C:@SD z˦m2CRߠ>|pϞ=i5_}mV`lVsP{s {#o<{ޭ\E3TL' W*%FDX |D,XT\=n@ >Tfm{XR)MIPN h99[Ga>M$ܵX,4ml` RSÈ;$P)|6aLE=!FQ/^ăDj+1 "#< J INFFH/$R)e,6cux)԰lמ&|L_ "av"/E(8z H˗ϞF/_jRDݸvh\l_Oo^T[z#[։=e%}2{LJxÀbvn߶trpHq@SqF*jێ] +WSKyv)J=V+.AxI]nsÇ-[A,2x7Gnj7Ov /vaC֫[Gڬ jŽx꯿zÃ%P 6ppYM*'ZF\~5koݺDMbʗhx+ghs]Eo+NbM><`UL]BB5i d !joܜ=Nzq2\%mG'[.M!uRQbFxjj5˦@IHO'0DhzrQ;!%m $)G;q|_gp'uP\9 D~LbxHgkAlrr(!Ï,doL}D"5-]1 Iso߉H0Z/Bxe! ;fwY[ɼCͦR+=4/_LMP57H&vatMڬk>'p*WRA< ENvL&U)9RY~o-j.kϺ".$gYLKS׵nޝȣt/Ջr*>}X]R~~ `*,æ~VG7V6r$rJ7m.͛WV#G5{8$Hsr޾};k, &Oa'.`)(촥&~6ԱCƍYǂfUm>oog͜9dhfħO@UFoWvm+(%{}ϛ7Ϝ;?gH~# ]qZ/e1<ԓr'(ꡃ6O$! Wy`H 5SB2,61%O ,cip=I*oC P&3E*MdҐ>y bZ!JlDEaN.j…7*wr21{dB-FAϔK)⠨W j4-x2{_7+X HY tP@~~'rUZ[G&#)5SͶ%#>C $!'[yX q F;܁<9Nja >=-4>\~7PJ~Q'@ln175?_^ٽ{/],1R{*/_7 Pit'w,Fp\ԟ `WGgO,_ԶpޮW-=&"D!Z\2'!24 <<<| 96Ŵrbb&Nlg?DGm>>I Й)wFy?o !~ڴ}np`@,;p@3gy{7wvK>U~fM׬^==#0=0 ͱ*eneʔ{DjvhhHlUT8'дp\j$?nv\lR%ݻo(V+W)z.]O~V}B֨d_M:Jn^B5=0<c ڒ(+_Fj&]xi(%85Vf0gX-0wLNEHɡѫ,n!n6f#OF6I6=H(i[˲B '/BL=p+r{;ĎA ^K36ҊydPLIL'!iDB4WT 2e9npqGaԙa&t X/Vp8V_6EvBz0>t8/A :p=)U+V#@3"$|@Q@ PV9QA999\.מ50 \1HTyV#s /@ s lF *J!xE= j B BQ8prVB$R$ B bal@ @ b/p@ 8֣uYY". @ @Rt7ʺpl>k@ |fX,ԵfF!)@ @(8N'@ b 00FT [z8l6tnOHM@ CQ+;u^aWJb١BuG"|JLT)Um )HӜ A RNQl |ej *YJ.O Z%JzЖʯ5}\8n P),&Fj~~~ymͩ`L9-PA Ha%ΓN vj,[>F&(" h"aaO?͏KU|ZT7oW;ǧu1m &nkxifn]gA HABƩa b'UhEbqzj*ޓd ˋd\'Ojƕ+_^$\k O<[Nab"M96 qϬ@ ~f6\0Ls& 5?@ftɒ.4wyyٲeMҁѥJzPSs-3+2he$zhj6t P^ٰfoUˆMVɫP2no/J8V!c&jϾlϵ7,&jb]sg9'͝f?>aϗ')gTqAq:%W՟|;~PCOh[ҝ_ppDo~r`"kdfL߿oAg͞mPU,.@FUwef 9Og|l]@V9!Õ-K{B6Uۨ)ʹC&d`]m07ahc04\井.>IQ"'LYŋemb~#) LNM!}g_ZE [DOç<N kA&U /JOҕDdŶDG<:=]ibS8T'mՅmU 34c]c~}6Br3z^O(#F%]LVb%mDц&dUD#N&:#,\0nz[o٣w36o" #sU])G Lh>U K/SGjv|Q{)WOB2ꉷ{%o#ߤ45G7ɴ(4Ti}}yY4]V!_l׈^WlMͽ2/h4ʔ kt[da 6>Dׅ[i[S|~Yz M;(ɑZV";F/]qX~mD_'מ뙷H˝+zݿ]ea4~tk|ڌڎd v"QPX[ӌWn(5пGU!VXmT uՉ~K,-|m!ThYJkeʋtqUmۖ) &IAh=N7s;S N/Y*YғC`<*{=w ށ龣t;/`,^zF5:kzH.8Ǿ=|o!-c?3 d?x^1\")y \/E\+6Ꝑ+wLiyf͡TJBB!CbbbV\=kh˸c<+~ e/{sە\Ǘ)Sfێbx}m[1l/GUZ5 \`Cp:aY雇Tћ M~ځ9Bۏ5e/ {r LpoMtf8WюЄhJ "nٶvuy'˸{W}*ȜR%UvkHBذ'MT q̌]s@ZL_2 =2BD ^ &Ϋqgm m)YhB:lޡiݷ5~aוP,{fvF1Ö:LŎlqH?=?y3_O/><#}MA^}cZ#)@pb`'-w9־;a٫{[n}J1|u{Su//?<0,}mYm L)gyp 1dR]"h]^g#LS,_L,ڳWL;p4Y-]AI|Oy᠈LO{~Cs%{ \-p2e,5í~y8Losp; OϺŴ,3x*,8gMk-[ppm& F +_٬ hs/$}"Lɱ K#G*9,͇c L=cӛu] U|~_8R`.L@.Q/݌Ƭ6k~\7 \OƑN|GR%?` r, G "N6]VB7ou]: G73i\2(>PIP}6nMwGKZέ, Wq.7s,埫Kq2hnGY@ . Ÿ.ԅ bFԈ~K^nYOsp:{R9+SfŌF^V*/@qI%&F?ϔKp㳌x1 v*%`%:I+ܦUrBO BCc\>n2929z7_<{+FCd M~ QO_QЭ(nc,Gz ݉ps,dҊ+̝7ow`""A`rX j&~.;\X[Sa$:Z6YR麝Qa!g$ /*.Z73T,-h Oզ}J2jLhj(2#4:Ó||jpv3TbTD459Yx8mYRvh :m ުtTi)j8ՑMv¥sL8nYfhK:9RVs-2cf91}睸I圗KF̄exM){@ < ='ĕwTYbYwwYrEm#eH o S8t̵5N },9RyaYpK(5,,CS_HD@?prW!=ewqUSo*PaD\2U¾)?Y6 $6I&R{,4oyrgf뻘 x P W$8߭_)Wɮdxx'=쬖-[<}d]F2ҍ4] |*ܴaa/v~be}/TLL0g'w:**JyyVTzϵWL}NSHr17.?֜FVWJa6OdN:kna)&Sˋx!G-d;=wʉ+6pNc?i*Ꮢw<]ig t뛚گOJ5uڷNJ4W-N~Rc'y1Ђ9qK0b9Y bt cWGpyzS']8kM.0!`Pyzp|Z( ۼl%Q87R,1C=6I*w!p9]̍ {8 mme׫}}qMڴW %eVtvrrXyp< k7 AHs^#mu=B h;2|.݌zBvX˗iF5`Z#_""RP قXWR1='xP&% @go:ǧb)?y1L".ծgًzN l]AtvϢ1Ǒ. Y*YT7bk'M!<~[Cl9WA#+/io\ϴ/(ސːV?15V~Xb0[Q#+/b?'s1 Go/I4A-F$zTwry܈tVp!?v gሒ.l ~]w}֙7 E: ':KժU7l4h{ٷ{nێtbrU}oP՗kB"OW13'CUЫU6+np?|IJ"ZR+OtA,^t?]+ŎxVߧ2k 1*#j /N]7:Ox|"VwFp>L̓ Jg!KPzYڢ꿯*33b1} uWν{s}vϠi?31$y*BTAu'1L'=42=\xN, ,ƚi=MlL6'%0|.10DQPmOU$J»z;`4KO? =+_zi ڣe$0<f7dGEQ&)c,4Wg:cklfCW"@kɰ!6lA~m2fUFd$vX@&X3h0#i˭i޼ 6vlEug{+. 4#1Y~ځ9bo-} ; n༸:x5S5!+Xܯ.9V6x8`xR'T4:n 3|rⴙ Xq{ ̓ahsi-KUo^x3؆sO7/ʉ?ךcN0Q/U*={AS{ͧ F&G=axػNGz}~6剥FT#w{f#8eZO"Xbz//ԔUA||>N^vZ5-<7k.؟;njkZC>`*ߤզM䓚jtT 9C:,=ai\:?NK6-ƅ6e);rrjYJTTF7h+q.]"X]x姠 jx[.=)i ӎ_Fs ){@  UNgfgOŸ E&)yQ&>~x_:UPeEuQJ9v!c1gA ffs 1!(\/37ܧ@ >pT$wU,g?A]% WjJ'NT^-00!Gpܽ^?}|-tΏ_>OsEQ-x5j5PUk56 e*T&nxefVȃzvRY@ Ekk=AwD(FDF{3=AAa7 Pb6jJ#IJ .luhp[s*r74 u?E>7'}t(QLn:ٺQD]^6S{&F{\Ʃ(j4XL } H>5قܶߨ 9&NUXBt ] Y&jn&.P]#Zaz7D rY++LžP5*5+l2C7-/cDzF/F&Zlfjt`XECm'HtC1FwkU9(K܅%2}ڛ b;LcLQ{}O@D__Ƃv'"݉u2S+tkZo%_I_g"mҪj!wT!Z-ioΓhle.Q/ֆ¢h3m$9p< G=R@ @ې%.S6<Qa)4"## [ St QmdS~_ ~w@ RDFD"(R[EWswEƦvmrӇY [@ DfjPs<@ (󢫹[BQ8O12tݲbxxEP!& (t@ gnjVϣ0"uDRBW*br4{5 XSa@ UStEWsDQ ;ɚH RDFrf 9BFQ5UCw@ q[\ÝF 7#gt\3֣&<EE;Ɉ] jyeZapw?'uu?3ǿBQE"#[ l5h`|},.}uvә[Оe?9y+(gd͖FĦ)Q{9X/$mάի%o ;4F˫[ZbfmBv 6.tk]gKvB׆Xͣϣv䯴M|䆳`C.9*@ 2ٮ+bxg )BFهxPQ0~M'snJLd#vPU̯ϸjte +nxky|uC."83=(vX|SW?7L8H&RG\H}/2?Q36S*pš:=htKH;nfDK1ɏFTA+80 uw^xi45ۙ--F^%;pc* mW>waZl> b]jۜl[]!1 JY#P2Ί|v0: ͠am 90L%1OAL LOmvXêW\x׶oh٩ޝ=;2Σ9yȃ*y 0F@ _Bx_A.g^[7 ԊJѥ}j[>;t|"]w,]}g#Ƥ,>gPaLpm^jmCfx2A5r& 8!fMg+T,RʧS㋙fr3ˎ\ί NvtP7u:ڛ}[SK? m>mQx!58X$*)ΉK/###W 4$MMɓxjƕ?==xEpǴJ !onb5*S2k5EڟsJz]A&Z>;FNͳ mPyU1eW/j>}Ks"7Ӿy1kj5ׯ]-&*$4*w9V>,Zw]5dmܷ[ٶOra-5Qs{֏Mvmec )I^Y1㜑N4pdT \eK2-Jψ#l-dݟlp5- O`lԊUk-epurǯ0ωnoW[~|Va1^O4㶬e6oݾAko栙.gI9훈t.[oС]@볈5ʄ+Pj^]wU^i jddLR1Lqu/hU?gX3$fvh 1qbbNH Vߙ_T{.9[ܹ{ʟdUaq K(Ep[K/2fH +>d^*-NM'@kśˏ5YhH_'յٝ@\W)U15 GHp퉸ppʱm^*#"&y-/}ZR+>upQu_-W^ ;qGgژM iŻ.d Uf."6T_kךiշۡx> zP}C__ubkAs-Ŗ0{HP 5;R"p-s/uc ^*RE5*-";3z' oƏ>} g6+ B#@fp?ٞ?8j';Qvw=q?-oM5PAxb,Kkg$, *R~co8J~T̛vjUsu3_ \ ȸu/?o~?yͦ3xԓKZP7_%q!)$%J +ZņΖ CLwEn`ͧ+'.::sn-ҧ]s`45HiLi HG~F1,VJ<Zu[oo4f(V$y "[37m6hR"T5-Vgܺck@b1!(K4H&jX²CbNaQsMn?as-ۙwf<an9^abK[ݽӳ `q8b/ʾz* VHquj(V[nsY鉨Ёwv a43}t #IaNiZeDg޸xmDŽU A+uvu:,TF@~ߎ,Q:(1f빶s}_yǍ`Τvks+t4#ִb+aՠ~?ϯ8uF%>X[!=[;;U'|3zi MÚ^d+bC2pɾ"GTsΚۘ;b#r zT`Weyčr`ΦĻjSP働LOk`bsr@^q~Gu+7ËǞ9̀v7>3m+֏!LlP9nIST>m,ȟ|=oyBoCIJS,[͙H)P:= j+X=?&hd'egbg:x'}dkAt%v[aύx"knQ(^HBb͢k!GhpEw b;b wѼ[XTJ(_HefC MHRrq=Ѥx-AjG [WSkp=nov«_m?FR#vDL0iEHa64wbkCPli cQ)FzA!/u@/n"VG@Q]$^"QQ7f@ DlmNzKWG"N˒n<0yz '{R*fX̰۷TR9 _Sio[OnV@^KȻHtp{N͛n/V,-5`w?۫>Ϩ!,*HüܯB3$;|:N 4_-@-=2Ç'O@ݾ Q+~po݉O,l5򀑵0-?oy!c?&vhKǺUVYؚ֬8N 5wbkI"Pli cQ)tBJxBLRJ%%%)B)ٳ@ w* ӝ@ (?B;Fa+-0!w ɀ8syܼd @ @Qll7&;[qLwQ]xvNH.$ %bZA,( "X@@Q@D* zHH!~~e/].776zJw# ݺ ,:*.dښXj3!dB9gU%v5 @2%%[2y%Z"J$6I.i #2B:3D8%!D{jL HP#SB{#gm. )QIҚ |7Cyڅs"Ws雞)d, #3b`EqN,8,x0G篿?qa{_]w2'Z$r m6>/m&yF6 EcJhyp/% BͲHaM3%!t.h%'IDmd(gl!s攼(vݚidA$5>vR@&Dθ LPӃ464L%iz"y5%GxgF퉣;(T4]"@[[B ,X蠈KZ]ף}j?v֖[&hTЇYZv9YЂ,~+)R SZp3wBoYRʧy'A1-wE`;l~aj2Mutm궈jR2&:%5F-Lm`(hY8Drm}@:)e긲0Qډ~zYBBy.^. 5iʂgz}H> 5[Dio [τ)4 ӄL=w,H;#$7]k}m:,Xhg1[v˼q)HWv ԠSP^u*1 B%|؃oK`"x>B.$KK $(>_V|T8sL @VDqHLO<(H˂ŽV^`EHrA#2wrc>e|owely-Lʙ2o昡ANHH  :} s،# 3;R eH] h΁> c$CmD&nܢ QN_ L`"Ѐ!r55,XPP,sYNQa2w֓g;EQma!z7?A/VG%NxN㡌3} `X) XOݠP A̤8rT&h&9 -iV@S \h?6c~ p=1\^GX`h0h_L[g;E9qyW2o̝+T9`0̝"Ϩ0UnG1ZYvPc:Kj@Y\pﺜ+4[ 6\8ß ĹAA"-<54[̐A"a2‘.f`\X#rB5Z WE~cʁ;G8ysPYˎVosg׻En_a;.2oܹvt([0̝"Ϩ0Uތ0 Erycm#,((wP9 #T +, M(7K&MB' Zt4<T `̜䣘EE8 ;Bf= `s$hjcT{ BJ0K1zʞ??d#]zk#fE,淭{gn"pxyժ4O֪B3։#Znn@`H$btRUYY3 pEX7XB@2X "J< g, 2(dNRK]]nY53n|X";x́ﺌ"i| eG 27>R(4Ҩ)8 *TVwq?v>EŁ~L_+jsєiJk7m_.uꮽomċٷwkc٪?ʝ;yz _5o:R췘Q8.2wr9-X}3w|vop;J.]:f/Λ圸.4mZdexgtֳo_xԔAlzU/9p6A g>zpj :"0*9E9Uof@IB^ d3p{ 6|}(87_r\ fV[HXqBŕ0C24:D1X5)wE|Ph"It:;[KB.;#[.)cK;b8 UdةZΐg;E9˜EḼݗ9Sy[6lXlYM],kEmͪիG> ?IS Eac E;(ְʁp+a|  4x RcKb$D#@i8<['øOaJ" D (n|0h uc]r ,\oD-Iyq)IAo1бArBJЄ$peS&HIϿPZ"|ݷkj}6[K8zj wG CpV[\_V-[ZVJ yV4}:'sSMuh,!RywF?NM"Z)N<4T>D6)E<)Y`- ݷ_̙ʻآ@ z:R %Pr6;GFFa[ɦ݁?ϝׯ_ӫY茝ЬlFC`v#!H=%. K1{'@:W[%x >Z 5ᅼ~#W$)QR).8 Nۉ0`ߵ#yj HPG=pba Duu=-gdE 2l]U4#҃-U5#WYVj!-V2_ꍂ%?_CR=7băYy!:?a~r~`@1uK N]yǟKLI,s(ٙ)f~2g*=uZY$Lyr)jE"*Y#=-U-aJ0 BH? ѭk\u-Ik@ßF;\3 w?]  5x5byOO}9׫ˇCdEx?>_qewes_4w*%*{1Jd.JQ,0G~ ě <հP=PgRI{˺gxZx/A'o' <- M~㷐IIo q NdM͙(g;E9˜qy̙ʻGa$e? g(s<&p$QeDw50;gQ `}xo$0e<1`4A~Y->e@=Vln3(%\҇7x{`'x'g?ީ?_ I3uypX'<zj}YjWG.n:wwk'L ƫqk@((Co?Њ۱9c- YNQ2kQ8.reTif6=?NEص[+mx-I@yHkͳ1T6O6Vsݼ[# .Oz7զSRzѬ8;w&it>#͍- F r9-X˻yowe\y;oYNgT[*A I'bZW&DNKj[> YNQ2p\}/s.RjkTj9aGH(xtK%T =i* K hʯxӧ *A鷏^Яkޱ3:CMAuD 왫^3{d,H p":F.+QzrN lNAxxMc~Söi6`I5q+\ϔO}gOg;Eye̶O90ݗ9Sy̾S^QTxJ#6TJ!00bQ4Σ@̤0ŝ򪣷.VQX/|CXKa,P\#B0jIewJu]=8m>[,X;3ۙY`;.(̙ʻGryj6Sh|BE`Z@QW[NLL0Ңh n'$fWWW ("(ES~9t:T*Ҩd2Ao$h\vPc:Kj@S]sx&v sU~҆ s{s!_84(H$7zj!!!D"p8RFMɲ`qbcۛ ,X`j뫈DbfgfDA͑/maXE& %r&J!)FhwckYX&Fvh)H0C%x̜lh40ܬ&,X-ɋZ/q &&Y Wɷr'>?r20ńm]['(irKecЁ3'maJ, Ai| B,6%1JJTq{?NP@S \y.:F ?湌\. ]9 ^kV5pys eӡo- / y,s(Z]!0Hrʼńm^o]`$|b3#yAan|͟mYB X}Z(7dVTJRn5_?NO}iw*b1ƙ#J0C!G q6#Fpxar-!BUWbăCavPz}Ȼu  1OleI` H[Jp|_hyTpVo }:}]Z eR*~/W>n~wO~22g}ޯ)6RCO{q]|_n;r)m~עؿPcxu4W!X<)L 7oJ,LUhΈ@nL0%kWm17'aۉG, X W7M´aqU#ʛ0v6 3nG y"r2~=o!aiQ9Em@owӄ#{Ѽro˛!zh,S 3*f}7o7*ls櫶7*g[㋱0y=<,*4Lqyحъy[9KGH"`<~r15 GLRNV90 Xז:cYHX Zߋf7Ԗ]1uM1ZB$ױOJZ3s*0yi s25/+i[eAUnVBUh0K7Ǜ0-sڶ~Vi,l7lvDxϜ鴎dJxԔAhz.,}0 "a\3 .US+\8+~qMy}I_/U11dNu*--ᦽfnАK̉s<۾b^u!c:}}M͊8pf,4|wzC~(B+&N /E iSk2 Ӻ]rV~ɻi6|,D=5 ;3#NEǷ(xueMfbؑjLȈ UIS%L9a,J9d8muؑ&"iB*xG[1XUFczx2Lx}K2k kUmԧ4P_z/194} IK̗'-6VHLS.3?i،֌&%or+tom\xUKMesa,hZ*!cIP.@q R@L ^`0kIL̬@N_d2霹cbG=(v{o7L:+#*++U*DB7+fԜ飙Uzx9/z6S_|p:gV9\8BiUR 5osr৉ZuI{W; 1;5yK~dQX3!k2y%B9Gͪaa}ى3Hʻt+朼m u9Ut+cK?vPS=aFGu*ҖX /$ Gō>eqed볫>{rlSw8fY+Š[Bs/8pf€OLTJ$S)ǃr>xqEBx.c,YgˍkS*`>6!Ԗp{nŎlȂl*HÞ]M%F%I+&-CFĘ{?{# { (m9kW [̀$P;ci*`_7״唫4k5h2`u/X"n63{HExi+ffFy307/w ङ(.g91c ꭟ͗+?mJi$)O+lC4A[o%6ÖERQ[Yq}|dddYY-ܹ}4}d 8'Nq: ,pGoN >, lY֍4 ̜Ȼn\{$‹L#rs sZo L{nhҺ c^'y]>`h*.WB3c";jH]L}N`cLsI•M9 L3hPXC_5UK+bGNJ0n5. O R]Zx˥DE/k_JYƍ{ofژ S2dߓVAjI"e'O}󳐝ɸff3*lK9!C5Lk7gcmwYXw[iܯmVgYQ ̵6q\ N(QND/Fo}+S^{qz5l%OPehtV&iE[}[3,L3ZV$9G ҼE V'1 8%hpiD =ϽvpsSg0Ur6fN0ZVsOZbڂ:go;O^ϿzH} !yW%ʐ_V8^!n*H0soJފ~ >&X %dęKydQ`B +wݾ*þ(fH[[.TtkĢOJ_&d2G ^[l3x 8bou)Y1G]4ؐl)^ &j q=\; oe9=%.وc]˰NEá!6zVgʛ!/f^&1ƈk6^f<éG2S'7&V#́{7=6WMx.muE[QT<7Ƿ7~v^gmEk8?XD)SLEeQ27o"y^8~^٢rЁ̇QԡJeff=smu*R?ܯ{_ٲo[lB1V<.^Aԩ\7g6Z"?ȣk!j`,xwb p!Oysﻯs}̦6ٶ(eGw@ӟ/zdo~^<+ 5ܩ3Ǡ(o:_~_mn:,Ajs\nR]Cm&aeBY-H]dQ{ %]t^ß"zH[F͵i`k$0% DӠI86YiG7Q_+|˜ NV>hk-mZ؜M*.͓Y d`D`_z|Tg+&2oW5# {!s J~+HA|#f^^|ے Mu:4 (ea᯶:R[a`^XbļX>٢r۰P>Om#)R¦F:j_X~2$0IIOH=:FgmCܻkuiɔ2L[%Sd3f5R!oE*H&M zZ:=( ج͵-dpd'⬺ZʕkRñLdā(T4T6/lFC[-Y:Bo]k DĒ)iq ?.]Bt71oѣVXIZ_I^RΣuJ.Pohp];^CV~yWT*ڂ!=km6y橭2~ūNǽW3]tn˾Y4ZB:&Uu `ZV6!`X;B!@nt2d j`|?Ѭ*:P_$|F+f"3GsiG޾&{/1ܖ`a>vC]`Np>K z8:î4$JEԣz}5*+?7tUv@ݳ+wz0J!O3pΫKo-Oun!o'y;0~,K(  ,:)㩹]#@Yy~o֔# |*k]WJ_ #A:fyml'N\~W?zo{xR+1ߎ'7u1볱"O{@C)^2am]K9$h:&u;7K;Ov C%@[}Ǵ)G Y3Ƽld \{oK@9~I12&wiik뉩C۷Oa]N"kɞC}uе;7N0 v 7dn\@ <*h/oΩ s'$"w-_[4xC1 }qւxL3*^1ՉC} E;h}a[rf6g ~HS,,64Yd?XP^q+sh/Tumr޾z#çO uHn3ogYsyf۽g6#ڷSck s{|=FK Ṗ~:n¸ofdMI7OGosW7xZKYw5g /sw=.EA$KXEYUڳzܑ ya?-5kFP;;aɞOGlmviuyLH|e XHhvEAyRȨmyi$H(|uXB$ S^:͎z" ꩋ7}z~e x<30TG + Ξ?G"@.*n)c$O'F}!w{&&3%P|)@t lXXdBbfH @cNy_ }燡L`2y2f<fRxF{ 3((%q:?SMipBGceA sWXiw{,ΜZ̅^zDSZ!I  gbQ~B㨪lǿ' gy{ܢ$ڢO+ɹC$`(Q$oͻOVG5֖x')!8FzITwHH1o ' ~*ߧ`W}`J8P&> ,-F\SLO^ۼzuH5#FrL0$` yɬI>)g HߘmfQI{"NοCl@=?&MPVV嘵  \.<!]zeaa! |{vQ4?͉84\WCέ΂[g ːEJmHz%^[-5Ѐ@"]@x)"4C֙/ L.o0,K?jd&ݲ]@T8j IE@Wb7.n˫}?`ABrwgg>t,+Z/č~mʒ^rV3P+*?JDh@$I/z~EHzT@a jI T u̢דrTDI|"EM88eZ-1F™Kc~sw»_MKԹCwj{/*///Žۂ9U3}v z }'7|~ ~w'{yvM~9y'-c3ˀ/T.==3yBճw#~1[R"1o"Ӌ?&{O%wNt!bS{3M#13-X;E.!R?,XXrԓ;%%Prhy iksW/hVhK'xeJ v曅}pF;^_)_{9̛npCwܑOnE$)5םc[✟." .7^;EYD_Y.PUJ Q0-XT}ku;٭5@ 8B χ}meȤF=;F'cdUuẢ^iQuw-HǞ4/7nhczԪZGn)5peǜZ7gZ]N8]ogS5E Ϭmyb',QЀd %J4G~ĚJ}EeIfI f?+xVJ"!3P0/Fcb@ <=zR$`h$P٤nE#{1ʼ|j?,*zɓRFy;U`|l m6mQ7s;ȷm!è7¸m ;qH(L諲+=S|p&PȪ%y&$L2nJ ܱڎ3#XtVٲ.|۹L\ %.\?ܵ-_tjvKKneju9 egw|>_R Bu qRp`W}z oKX,8x.n=`50~k H"r᛫5c ,X`}Am {$_  D.ʔ檷 {9 ,X8̛o\/I^g`s82wrc -*\tv+G3gT[*7 ۿxqXdexgtVngxԔAko.-c qIŠx-av_6  0g`.)gӂe+vwXΕg0iYNgT[*Ӷ83ۀ"(ι;'H#L_/fPf8ۇn/uw5R? ݴ>gŋ[{㹰p:\t ?^],wwF7;pl>zJDODYSg]; M5_Wޅ@wcY`;.J-sPad;EQmaMb׮rНF=s^"0 jK[W ʹy..{3Pu|fDOp\ K_T]^9w݆nS"yĕ S:Bs}8\T^X>Yrf,=mץiw|U{ qySi(w/˜|Q_ ?Ȳ2FHf?w.==-wsFPQ"<0*ɮ"QOUuɄ ;=OOPy~7?!}_!0fuD9Cߛ:8 ʮ\s{_>g1>4룅z<>yخ[, w9,sYNQa2w B{P?SܧYu3!%Eώxޕ{|׶/<%q3zJ̗P &:NXً.}>Fp\e,sZ> K2g*qG=zjF9S v ?Oӵa]&rB5BnNC& qy;zSߖ /B uT?Pql 39L0YݩZLtΌⱤjc…-ԣp˾=:e$e~"#>Þ kz6VlMR. |Re+h sZ˳̝eN ֢p\}˜|ǵ(4> ʲWju\YpBRB~jqljzD2 WE $c9T9_{2H"QUɱS5}V; ?FG(8BϦ;<}𒅅2Z?Ƃ%8"/ѡdt@)`V .ܵb&F0_թ(_hғ7CōA:ަQVn,pr@YVlE4۠ڳ,s(gӂ(w~2g*B9Q_ׅe 4WO]S Ew]/KH5:"m\YpU=rQq8wK#Q}b=9 j9"dZ\&;ɉ0٭Ja2K%UA=_nx FxEB.$KK | IP0|>"Nοǽm˦}/ 9&wyxM%e[ٽ_W8tGpa!s^H-^'!UKw(ESgc}#^"ׯͮ6DT:{wQ~I W  nxs={Y^ |7sv76b.,1<$Ih YN:>6a G<)Y`}˻~eT2? H/VG%NxƗ"u} `X) X΀r``&ők°4)Ԗ{EtxCb߉l"ܗGfNXj5u#0X̌qPpL]2dO`k2mj~1 >n`?h-ڰdŨ YNQ2p\}/sGA("G܀@Hd_JL陊P o ;0M4 )݃pDzj;Ό4cѵ^]"KKږ̔WH9ܦ4Qy1oEL3jYNgX[!sQ#@V;}NmvMMIo^jdd3sqI&dݔAcYgFݱ} 7[\&,X `n;m}=ޛclzr85YC>8l\ *SqZmox(a9x2# N4HG1ς 'bcۛ=Ӗݒu+g$H0,(9go % zD" ʓ25Tz^,Xp.hpǙM|Gcp2ļOO}ŇA;[zCYzLL$Jy\nMMm}}}Q } `!|h\tFk^ĉWX0sbȐ!oT<[_\#9~E{uDjT%A0EK" (gEع0NW0g T9`B1w_i 6Ra:,sSo( `+vBaPU$ދNP3$`HpWl)s=TRD2$9Zrz2(G#kJ4|+1sA vuKԼi=ݎjN-_}}sWϜ1DvϩSΏ]>#|%4_tb-T`'C;&;s޺nk0oauXΕPmEQẗ3'<(fQDi`,t^{M1qDX|@8(5ڲU#{lr@.QBY//rss7-BXYV T]'l٥2__+[aPڔ8|l]eOf6'NoM_j~;JUnMsg;s9޻EܲwVe\{ eaoGacK`t- I\p PPu]l?f?`3gΐ7#=:u߹"3(PfEkTេu!XUBòʾ\ 4?tӻ|%.e?c´nw(GCSABB~y Q '#G^S&]Du}c- nYq{ ea ,KX*2^iuNxk wpM\wBKDFHUB*U^uՅ:j ZnGXGݸA!ɭ]! 0J}?6{~~=CL %@ JڧE">99٪4?2'ޡgϞrwU&'szlm)B09+ˁP6,u,vh5{.%V=&^2 nZzL)dtQB`Q'[W7A`'[ZnxG}J-N+d_rV v日eo^дĩk?'Ung3nJ/wda/v[O&P&_b 3Ҡ+* 8ǭbN"d)- OIs͛SK'nq`^MN;jʪݡ3eil*Y5zsa=j, gT;~x%9[-o pzcnaMF vm;yktW@[#߲2YaAal_Ǐb8Bt>dR]1@ Pfp"m`sRHOz{)s:ɔ\: W'08b AY4ڐDa\N3yp}zwq6CPXҀת[;ZD-Jj:|g_+c<< Oi"iGov5eUH\ImTK2-TN`*t;ֽ2<ѫ,Aa1X2"!a\Ncٸ& ,OiEQEEڱADDĦm0t KkK**$r4j.ئ/ftvY^^q|4~7咬8ZUy5\-D暒rม{|jst " ^Faz쀋3nwYR׹]ۮ[hosG/&ߩ9%Rlw">ow#ۈĩRr/8EveCX |RWS-%]߻@׀|5ݑQ'ztNef\hM]ʎ s2%@8 àXmWhv8 0B.{>0nZ4>=/lZg3ȓ 0'uޅ/l[WG!qOLҙքauY AUI#R~"3'j8XU];AsҊ>zN@m`O_2Ggq?f#RqyEL߽ղ(x?gkktz_%''nx-C ~ɱÂoE J?y4ԩgxb.r^\oM"[Ԕrfm6}5;׋W?>g莯KmXodzxzUH=Fei8P{<''gѭ? ysEnas?Ȧ]6,e61~C9P5C풓99?͸6҅5b|^cy-$'k߲5Gհ'AaXRE y?_}RS*ֶؒI ;,=.fv#A|O.±8d>eK , zJFHHMwxBPvH$ IŊ|RJ `*ݼ(aLOkF[w :)4ⅬN1}f®cbgJ8j? ~cO,*1,<ܭ,m,ޗcЇ Vm\:A{TNܻw]{9c4,Dw؋7r{ˇG. m!VP}FĮ֙C 0|;VkUB8$ˡ .bd)Iwʎ6T ^mO]kz7`ev+vWRڥ'F{`p~r 2fFp/Q5PUdT;";kҝe?'U U羯ڞqevwRy>-n鷪::gd٨<[C;2q\^iYy6tѪ_>tcI3&AXzBԱS\kMY Uinm0 @)BOx͚z3SPUɫWT]ibFWwCNsjԻi^U fB0MG0b'z!z:dkQiJK$, z3 anғW>;>8z)pw[%rALjՙPnQtDN. Nu<l ߥꯢcױ[M9+?ݜ`p1瓂-B $qQ~0 !WSo_0SG섨gۆxB QCt.8yWq=g{9|Jvg^:]<`V\]~Ry-؍cuVƆǟbޟj(]'&2ٚ%H]${pDὃ_+QKO$ȯ8f] [}) .ذct' PrFHV=!PC!k["q!{q>U RY[Ǫ::b:\ShZKC@|MԹjmT!+L9Ylc4n55XP@GsM^ꪵ֣TkQUS7JF#E#`%34]5k c#;F}g׎C(CRU^EAȸv.ϞE_6fDžn_:pp?] [h}u%|%!j vs3'uN4 qB7,P<꺕ovR ,bt00/v1}  J'ǎQWP*(`B"1~05hv컼m dJ(_G w!оέ9A/'p[pB{%n^o}/wauүjnXh[nkwuO߬P?fUWM bGSRj(oI'$# WvguHЫjkrjWUGs0+֊ sHkGB8"!Halb@lʕ g8ɄN8Eo:Eq[E8A|F3R$ُ\2wfGQ#z;|]J>^)|Pή˓'V&V}^YBߡBKۿr$ݵ7nϺXbun[Ƣ{f24*lRoғڷ&B;Uk (*j+lp.;AFaùI[E9h\;qB)~SӂۚT9>QFYO1#m#V0 ;21s (VvbU|uƩ^ֱ,'2>rR/]~^j#{|dk-{7L?ncwd± ]}"*ɑR51ǢP+o[.Ztm]4|CS NL+^Cիf6lM[;f^`dUCt(pլ'2@'"-?:#ׯl!-mYxw-N"DH" ";Yl.*nRzS304\LԞ&eBߊ N=If|BQ[9'ɪ!5d+0 [ n޼ѣp,.*0M*6<Pb)̑<USOۮfz5;bed!B|!,HErĘH+_͡HoRn3)75-Ks'NZCowYjwf23XkÁH,.J f#"+Eyl6c090J12rfɾs ]aC.ڋ$mhs {d% $L~o 5cƌٱd2'"##>a_s6 (h=L*uP ,z'Npc '!Ř(zwS=iMMsBr캧oVO(CalCpLZLbZRiB. uqB{s-))cUD"gn-(UcJJRH0p#7L<"+ ǀ4h.FTkDdDx'Ss~iq!K2'6R8SDΩ2qVLIа͋f&orOVLiQڱBcM7<|Dž:"1b?;byg(#l/Fwi;12FրZ}7ijޓ+4\,-K̮{f23g6ae%EԿFP2Y".L^VGi|_deTRY%~JS愘CRpP:¨@ `do5;ߍ3F \|(=ʬC}Qip8Q Aa"{MZ#0CYVW#/=?0oCzߥ߯2}h|-[bܑڳ24OKxH**s΁uo;7w΍?أ*}~tKZIKb<gCmV77#<{O͛QMtx9Vj E{`i|Sr:֘ꞾY= c KwШ@x5/Yk$jLzJ^`St 1"P]jʧ֭[S[IWrT!*W=w4,yϚb&,Q\quG?K.lT-tP<NxH%7oy.0~$G{Ws/1qTpcfN!cW UNG!vO(zbI*$^ۯ}}f_(Ck=0WyUfXu/\YW_+;ROܲ7Yo̊O̧lxRTjƍ\iў,1siG6,KNu i-\9-31vM L w)(}A쾔eHy22"PuUA]Qq(}F#9{t<ݺ&Q0v6oN-i+y%6e;>ܪ)vHZ0f CˌOWFx }S_׳{g?nqY-xͅd8=|v10[A^;ϣ?D5V3׫{lNy)Wd'VMI]~!ک_ ;0lI &ݎ[Zn"MYr#awsyI3ڱ:|lHyT~,{Fs %66]2-@/~j@jBԿu޿{@#5^ƎoP47XhYoV BiNodWmܠWWizyAF[ 6<̉zUP]1@ Pfp"m`sRHOz{)s:ɔ\: (dqà,TmHh0^tW]DB Nm!`A?EKǾX-_poN]0 1ºa7A4pNмŢpO}<ϊmS}QPiQg<KZuky>TȐEUQ{J +7B ?T-Pxz&:IĂSpOg):E Gw괝oͫ5!<-ʜPh":4z*M'}_XS72ﵣ(!-PV-4T:559%Đn׬`@5Uܨ:SX=Ct?1XܠpmͫK痡R=8z4d`' q ڨ"d]Kjr U7S@"QޕNvvP1٥e-_~HǏ^J;DSAXIN)cW|W|}#G:EѼ@v|Bi$$rp*#7;ϚVP`*t{ֽ2<ѫ,Aa1X2"!a\N{eٸ& ,OiEQEEڱADDĦm0t KMDI84hL\o_hWd1nΫyԻquSrIV_[J3 V:ѧ4_eij D`hj- }ԥw]1=(FRT5饟$zS=GA U Aic>ݍ>7}qC@Dh'L-gNz>J`$߆,8g~.Y!fH=? RC:k53{kU )qt$ ]NZC@kf!3y'WΘEsb)D*HRGc6 {ǎ˾ژ:UJ/s'={W³i`ڢwrx0x v|7oz\-,=@3'؛:\{/;0J݆DtNu]VK1a.Q$q .G!Fw-Qf?~c٧GMk,sy^wλ-v( NSzY|IImf(oxv؄Zponx\}ZIAUI#R~"3'j8X[3I+8 j=mV~u{vPJi?1~SWˢIztbЋ-u}ܮѭ鷙VINN,[|cߊ+!h~hRS==\弸YCH]Pijoy96lGrBˏ.3cmoѫ,k)Iuҥ |48j-Kd *?~ߓ8Դ7A6ww-l0{@لM>.~vǣg{ذB}jTK=a\Lg+vSϸUJn_B@JBк$ yr-S+VH#PbO(CuLS9Ƞ. l#0 e񄠈..[(H4c3H19R)UyQ"]aq :)4ⅬN1}f®cbgғY8>1Ɵ?U?fooI.84e#X=htˢa႕o,mo^9 Ӌ!IK˳\sNw {x Ұe8~#|xPpb `ߛ ߟgD`Q8t czg3a0xU"@IOu$qGQd9E,xs"%^)|Pή˓'V&vS}ҁW +sKV 5ͨEaq[.Zۻe,oVO(C?H(֤}QJhljK+EA\40(V`U|uƩ^ֱ,'2>rR/]~^j#{|dk-{7L?ncwd±<دg^_i[x5sL([斋9ݻ%-w[+P9E```h~Z@ъ?].(hò6[K_gQ葓rq@lZF'oߒYg.c\ۥ{.Քq_byZD][G gxo}-PsB\N5MISʧ>i>u[f71 .M-l!XW I-M& S3|D?Nvz;r۠|TN<[$~v%p E cbun˙1oVO(Cz}C.&e:Uf15ER3t6I%VIH}|Q Za4j ,TyYI>L,Λ!+Ur3*joEA ͞rIA[^WgHkgܦ d BAF```@Я_Vv";Yl.*nRzS304\kO=MDsSˤƿM^d Ůt$y$ib̑<Uu?]o Q`"C"VBȔ/$"% H\N3P/9YRg=\&%63fw/vC&oju4_CݲzTNc|bˁYhIږ0@J@FI|0PS pJz'!䤂 ˄$މQGY_$zӴwb-k^s#CɌm:f=E BwS{ьV],S2'(|+FZ#|%{މà| (\0 &, +fӅx/vH?? T>=Uv$oG穭yG$V1#~6ěkf.m{C~9ԼդO+^ oS3ֺ6E`?ImQPIAҕs7 i0x cD$(.QjB+h&Y"]5aO3->)0maAx[;=AR#g߼B5 0=8xF0@~H>>Vַߐ!ɇ "dEV*ro?3f=1ÿ:xfW?.D?ș(P?%i&* bˮ}u+X(B09})ˁP6CNr}w=~W/KN7v-_j&2:vŨ@QtB`Q'[MR!r.NiчW`6֣|іRfXu/K "Wn׊luFtT ԵOʂXşq#WZ{' {svz :byp]C~b Wz@ L]wv5St & on_/%O8rj'[ Vfzb(]3IR@ @@0 mZ5vEDs,.BbmtD=gۼ9t\۔?Zso:#Yvhɦ˜U3T0!R׳{g?nqY-䖷 ͅd8=|v10[&C^;ϣ?D5V3׫{lNy)Wd'V>^[W?C湵Sv`V[mۓ'M`D(F>ZrfNcagaOqiI1Ԛ`(jYTD Yd1pXi\Ӣ qEa A44k^קwwmN8T> ų{w2zO': "._b}rpP_u?v.ȝ_,B!ROCtxιۆ:e9/棠Ңp!d@3O5QgoD,8ys롑-ivWxc, &H3(Jmrs$@ C(Q @@֥>tjc(FfڡBmc$"dBb~MK!O?z)Ma%9]峭;_y7/"Hi2X8O(3c e([4CIyڑgn+(of0GBK]_A^ggϿFkae _۰,0.JltX\H'Ŵ~]O" ""bӶ@ĊJKYތ˥$r4\m⽋ūC'HN mQ: M^TN]* Jz' K@ӃX `!%@X|P]q0ݑ pwwwceMs9pЁ=b>yq9:IFj/SK#0=vvřS^,Bg }=iأHd2 ǹ<sA 1{?GQl2hR׹]ۮ[˯PHQ;z:}`WiSZq|{LE^ɕ3ortQ휘4GB R"ј ޱ76bb]*gܹbמ=QA6L{gϯmc da0!|wrۙ*ΘFiv#A <D>A̭[&!t);&%ʔ@8 àXmWhv8 0B.{>8bP4>=/lZg3ȓ 0'uޅ/l[WG!qOLj5Squrqc.VФ@UI#R~"3'j8X[3CI+89=mV~u{vPJi?1~SWˢIztbЋ-u}ܮѭ鎛VINN,[|cߊ+!h~hRS==\弸YCH]Pijoy96lGrq'ߑڅGiex[NfF:ԿYN0O8Ȧ:K"DBjj 5;XO*@  0Ree ,z]0FW p´NG)r !᫼x!>{pLٹkؙڏB'fO}bhpn`Y< >\n;ұ \+'aUbz1X:$iiyxݻX.={ısDLob\B Bt{ss_]3# N~aL,w&Q}3*~A n8(A "F9tnO{ι xv݊{{v="vln~lډ\-D Q0~ ֓qlLw+ S][wep1瓂-B $qQ~0 !WוAo_0SG섨gۆxB QCݪWmƽs9{T{cAlx:HZ>pqtIc7&IZ>yXwDЀTC${pDὃ_+QKO$ȯ8f] [}) .ذct' P@mc.9bTQ«aVe````h4/BzWu`q?I6uN4 qB7,P<@J7;)pNr1`UJA>OMik`Y%cѨoFY(c0c$&#l{ akА)n7wy ȔP|#nC}ŝ[s-^N<hiqk>DH4!Mk3c 3֣qఏܳ`'#?2;1$4fEZdiH(`2QPr9"@ B mI[ 'Mó|wwV/>- 2A'1PgI$۝d̎ G,vvu%|ӽ8qSB(~š]'OLaw,ڲ3nn 0d1CSD>\)s򤉺Q4-I#A>3.5b FF<a(qwd@#c$P¢`UQMu,Kx){K^%}K{; f:xqplC, x^<;gIp0ߍܢ Ȋ0ҐGL̠Szс߄ٯģw-N?%-|%Q_!\6+viΑUG=֧M&}:Xi]o(,:lG3vn1Ä.v~KzJ^`St 1"P]FBۊ@5Xt/̈́`>K^&l}iF=%lØM㭟oIT"}£ځk{<* Z;@o]"[?vLZY.~C'~0 A#0C5 ?FaF, nZ/ڃݙ(P?%rp ʳX+kʨ>00 LNw_r ,(++&)A\]fϥdߪ ?RS]Wᅳ;]1*PT9>5?l֕ʬN!-8| uza4_N#ȕub3,-~c N<1 DIYP_3nJ/wda/v[X\d+3@MY`````hNhQX´' }D @`'Ik' ZK3zĂmZ5vED9nFѫ{t<ݺ&Q0v6oN-i+y%6e;>ܪ)vHZ0f msB5D8YOXKVQc0gT;~x%9[- pzcna Ƚv9ޟG#vm;ykfW@[#ZrfNcagauOÿ:L{jFR]1@ Pfp"m`sRHO&taP 6$4рw ,yz\]EM;P,QG˨Q<ힰ$\S*T yBe,}y翮]A!ROCtxιۆ:e9/棠ҢSts=XE ͸CQ HP '#?6]lS^ @6P:VLqIV.ɥKWVqp6336F"BLviY(/w{I?$]G/ $Ⱬ|>:|g_+c<+Ij)\Jy춂fs,Tqq+^m`Zż ?? t+ML7QŅdyRL+jon/׵.ҎǜJDDĦm0t KMDI84h\÷l Y^bv1֩@c{4E{Bul((hWl.eDLJ`%,:hGA U Ai/ www7V&|9 #WgT`24cg\9už"lkmb!+GR\qH_vm:zfo- B!E"nD=>4өTkh-،8D|=b"/7v9vNL#Ł!Q)tQhD`qzZ>aNo3I|1kNj va3W[ [!fHf`````u]ݩ9MO. H lx Xy%|nFGRvLK)5p@AڮRqBa\p|p(h76}{d_ش2g:'A~aNx _NbzʏB89בtFkqyfŋ?ӎ47Ǡ`t/~_̉8>E9(p`au̱AsҊ>zN@m`O_2GgPJi?1~SWˢIztbЋ-u}ܮѭ국&''nx-C ~ɱÂoE J?y4ԩgxb.r^\,R!ǀ֮b45}w|̀y_MN# A}Z-0(~wcg,y.Vtvk8d>eK , z"z6 DQ' N2͋x Q.@\-FAiVϏSCWyB]}`>sa13% Ox'v|bY55c-io14 8ރFY,z_؎A.XIrn.*1,)(@"R@p)aHWg+9  q:99zDN]~6m?7,u=MO;H;KeGjA{{~0߯3~ f3/._Aj􁋣O*w1qN3AS ؾĄ_&#0{'ͤ?ЫpI}y?{tVuxRJl]w-K;>þk*_3M^s^572}\S˄|qK}w,S,sRClywpfy;s{f8Ϝ3y ɋr_wBB+)R,˜߿_'wV~yck=aO̶VQ;yluӮثP jMy\"e҄#Q .*I=4b$IhjwvDLb&YVIw'TO-jlTy8F444FֽCw29⪏w~w)i ,2qϵJC,*= Z4Ǜ=_°=hθAS-:ތ`nVef;'3' @sW\\!SkY"K`4rےP7A 5js=LFV(X>G8?\}TS gNo]쵱Q&(Mߑn.ֺٛbL1#OW|(yW1BD4A*z$3vL J6ej59k?aPpK4WBrE;i`(ADA>`ʱ֙_<*ww=4v~7t`>ĩb~Ůy|nf%fn{1+3G,˭yg- Фff;ќ'?(A'uAETYc\Ƿkf-j7w UHd-wƃs)WO=󥂝 (I^t%06d?sEW+e~m 4(< .)f=Yc՛սӾ{ Z5f<;⥫;N{f O+RI;p<1B%>34bI=o%|UN/P)9(7K}Z*Α%AM-j-:2)(&Je_s7G`X^T 2m1z5`B$hl0+8[`΍ʱrooO嵾r[UFPn{Z럤Wz<NaS]q?3 o\~>#Dd|}i@z.==J=%/xl Ӻ{ +'UWU"Nl`) W~+=eն H"fش95f/ltߥ{ܥuMMTM~so5k_'[va嘝ћTս#o%iALm'"sfO[1yfiR;@P^{P^絾r[ѡ#(ER&zlmx<+ `Xd` :xq&$0+Fhh(Ap|T^zH =7߽%9}N }6IUi#wē*cS VKK_Bs˼H qc4-yQm!{m5cgLӫkR/nܐ;gNpG>W'J;~vLГwlQO.UE=(sPn~-oc&2F5}r;v ݏm$:i{q?r}c a;h[_T3MwD(A>ro3yO XΙ~;&FfOhՖ,@:y^{-<tBs=+OJ e@tō"6Gk{Nȇ "}w6>cO2dlҾkt7'bD6qE_-GDQA SDyOV8@HA]2}TݸqYf))"l19[A W%/")i-bMx]j[[616'ۺ:๫ւ|ҽlO?hO\~ϖcg Y^{9PRU^Zx6T,7UFnX/ 3]<+<~L{%c|y4t䜘)W)mћNd_SF,uyoۨV$7fwu4҇s$ 3?lDѩų|ncW*qvP_̞Nc\jGzbpŐsE|=}hGIDUyNX 7ht"Nc" -0|O>R+BU>"VgA*QtR.թu'I`pͺK[\90H}~|eS+{ZXU_6KHul唹·/讪,.Ls*TlꔔK{wו1zۃr8f1;7R&;D (ƾrs=g*h߾%[ bpe]Xdy°[;G\s=ĥ]bT:KqB$|s>}׿cU5{GbKZԚ޼U aNm>aZr(W~| {aP٥X?=jH%\KRaDq w\2(! A2Ӄݟn&f1{ UŻ40qt)Ww,gHEOT8sr!(l" 8w~*" @`;H QCz!0zĆ0LLy)$(6VƦ^80xʌ/@+VpZFQ-SBK.6 jfOjcx3;; 58 i%Sf|>v^Z)PN穭۷S(}C=u r<)=C AG楲z5%RՈD0U8wo- K;cVԛ.6.#ӔlW~E+{:>P"6e~ܹγX^:~uYABzRDڢ2={n}zG+6etOݠjv'/j;NԲ/0:›(:= \J(%1WR 3{x.<ãjaH6hl;qn 9)9zܿ|I]? @ܫ#?5wd(yk?7{yU9GM&q[jole!(v--E999.G&??':Ǻu0jM4V"GK-sɍW|9~+FO{oY Oظ16N*{xi[fV"[}fTŃpc֤f46p8 \)=MH|B_gm@DQ{Pn,Qq厫R{G(ƀ r5TTTPDFC8G ,{e)4c>33}oVt7B[vӦ6q8쯀ۃ8dފe?AM-j-:j1l<;ۏ pXDT$}I ݺY;'W_|蚁~ugX؍-0FQ{Pn,0FQ{q[jc0(-wf ΀"Xg9q5e@8 ,A({fv%3Ƞ 5` %"#qFPU)0Drzw ·!5|g&ϸRC'*-?3彯zl]N{ϧ.唁qN[~eVy57݊_4K K/>6ڛ(9NK&r>vz[ 6gTս#o%iA[- ɍ3`LhbAp|8*"#, Q3hxvx@Cl)2 ř$p׸qĹ{/-AɭݨO_5|qÝNwGjmwV3vz֛q=& }pCf&Gw 5čw~ӴEͶB߉XvcݓB,U5`k({D5 DBRt CE|Ŏ{U"`00Bk`*e5hEj)}а /%?{ٮ̠קvNgd_gEIVe@tō"6Gk{Nȇm뀨^ xD!Cd]6NRh<(0e-h πE!d̬`W ں>u+<a ݽ{I^!moSwLcg Y^{9P‡NE.^\'rT᪇QXӴ#Op|.D{~@=6f)g)xV|J*ʴ^r;ƷF`i$uO>:zS۩Tkʈ.mՊ6%/@H03_YOfn\4ᣏ$BZcG9S,8lm;'*r̽ ?*N\vpї2?};^+ Ō5wD'5.V=D I}DL[aKWolStܸR(:t)Ժ2j׬?OsZJ*W.^=2-1Ue󱄴]&^N|"BN=BǦNI gI &)\ƃ"GCP+Ő.r4#AD0- ugzilLDAct&OIP*!&h3~o,V=N=_>ݒILHH9)ڣf[G\ /Zl{};tiƇwkB =(48]ᮟKE^ $!H~zPҲ3-ߤ,>2f~zoc;:%P8gūOd뻇WO^'HDĹl9k{fqQ#tY s"0$buk*F#bխg`A$hT#3 SN!sUt?;&OeyT׶~R͹;-ZTe_ b_}"KSQ 8C3c4H.~_rN&hfXAR;C{ȸ_-oWeaw>iic+W-Uh|4jJ߼]߷[C[s}z'`ʂ̈́Ww6/ҷg:8c՚:O QzNT ӻ((ؕk7ntb;Ŏ[Â\E\0&DSw;B.BGX۸Ďz~Ê|Vk@%|WP)ܥ F7BtNN"hqۏV?Q(oߛccG O,}3jb*3E~x|}ڸa7ˈǜBxw}~Di֙و7}ARG!/S7膭' =*/5ȻF 'Ϩ6ΐ:lfwr\SP^cA0W4tT(:bYSm?]0pZ:4?tsB>`}p1bs e9Φݺe71QJ%萸3! /eJ@ $.a:'ǿe~K|zˌYK޼6>q7MyFABƤ~18uW̸g-OziY)d-z1o* 4wvHչKHWeYhѐc*\d^æ!jAc"/%1ɋ/MP(8o;AÓGTFO˼^W ɀ/}s}@q/7G{!YsKؽnx`$;%IrGb%b߲*:|$DSɋ^4qLwGRF LbC5PҢ~+MG[8N0UkÕO[sM^)`K+!|{{Fͧe!/h5&҈> M̨_ y̚1LƦ$+n>+YOSzĴ+6w0alH-FE4C0HTJERık5Db,p)?G?/afU< o0Og="Tl(8g2=qJD+9w%q.ެ.28E(F(NA#W-i uC]3Я>wϴ1nKԘ4PΘnJPeUBN)TqY :\Ŕ/YvZOΒGMpرM^Si߽xNE՝n->If=DYdw,biEKOa$'Uiȳ#^:3Ӛ4e?+'TmX^FtVt)3fBscq–v &aD*ŗqkɉ0"hM aN:MC`pIhJ@:ר.9(={jNR{Zέ1N1FQ!1 hB:Ի췷]Z˜!E1Gt1 R#9Fr4komP7sAaZ!`"9@"bdp9Υso } :O8=iϥȯ)S2Q4G4A8¹(fA8 `QkJDF,`U~ )],<+BH#_3yuJ{{y+Ss+nʼn–]X9fgt+NW|a.3.4nhol{;-`y$o3lڜQ]TS1nMȜٓ`G(Xb@8N9>2Bs {,&xGc|DP@[ Aq&)= /# el;*v/tBtNwGjmwV3vz֛q=& [ށ7MӒ5 }'>w`فώ z&w|3;[{ڦ [#D|/݀hQ H\H!2RqqW(a`T.H] jЊSV N}%mq7}w6>cv:#s=+O7H.+n8\sB>tmpGXDZk'2/D "ڰ9v @Dc v_M!ұH¢<6Q[wGTT=~``i0J1$v'y,E B O1Ǝ+Sd-{MX>@ :]غxrQKFNGa׶Nӎ\>!I*\IS]D:rNLیsvp?HųSPKpG?Wu{15ZK#mћNd_SF,uyoۨV$7fwu4wO.xBȄtz⡊MO^rY+b-yn+#.Zd+lF q89kg!/ZKoˆ_|T0nIğPud]i&/9޸"]>fn\4ᣏ$BZcG9S,8lm;'*r̽ ?*N\p! x iգ+ߎQJMNT L$F>!{&?EǍk+ CrN+fI`pͺK[\90H}~|eS+{ZXU_6KHul唹·/讪,.Ls*TlꔔKpaWh8|?2\)|XtYH "iQªa& >Dʃ$L6K<QUBLfފY(zkzP˿}?+%5<~xc1RsV7"uSou˯<JRC^;GP٥XjLީͧ;LKngj-wkB =(48]ᮟKE^ $!H~zPҲ3-ߤ,>2f~zocs:%P8gūOd뻇WO^'HDĹl9k{h`G}ȣ(G>2x!89D`HĠ DUn== Q"SaJ0;Z\2WYEL|lofggC1.+ǤGƾ~sNbBpeW\4(B!cˆq1|s`tJ$? C9]ԋ]MW43\ r}T䡽sNdwϷO6g=4<<-ml{eJ #%@MW)vxGT-A1N4FT" NB*r* Je$YV"^|\O02͛7O)3 ;t`y$qzLLMxw{f|yû3 2]x_b3ě A͇ B>MGM]%ի)J\`qZ;CUl&zT}/={M֤yjs ?/2l ޝGq.N4A(ؑukXp Ɛ;Bw;B.BGX۸Ďz~Ê|Vk@%|WA]*bNps.D䔺q-"OhZ_Iiu]ϋr)<0Q` |'?`QU)Bݹ)yG y ;EP>o4lě>| r)ʣYxtVPmY]# ws[֓gTT fM}'.M!55t9 sGC IBܫ#?5wH:}@cvl|p%t,!\acYa6inM9GRt :$. zdDG4KG<a| G7H>.g^+Xf\yoG<5Xlʣ0"J2&%`~+qSDQ澛F"N]*DunG-K*E[TQŴ"*6ůfϞګL8d$/7E*oC3 O^QmF=y/{]-$Np=v&X! bNu BRܑX츊Qx)r"*!0=s4MQ~q8Pf}=7=G*~}6μL=yoG)EG' gk36QiYH.es:>Z={4⭶&H-G>3ׂv&}L5Ӈ'IJmBsE&Lbj|C-Zp6qdbTI>sD%T$EkPӈA$ƒ '3}offY%ßP?-{ef`zfVrJ\Y]+dp` 1Qb QFv[8蕱f`'֟i3vc ̘@(8'⮉! 匉UV%dM5眵0{ϥ{{+!^L9pNGmqŠ-=[ת4[/]ioQ`=cb,;UVZת4[/EiznB *ͶJ,qy_:q\ Z:XQE8ER$-8ILˆT/֒aD0њ -¸yMC`pIhJ@:ר.9(={jNR{Zέ5=:Wfbc !1ڕe*la,҈9XHŐ*p1:Y{kqaZ!`"9@"bdp9Υso A :O8=iϥȯ)S2Q4G4Q+FH(EqK(3hkU)0Drzw ·!5|g&ϸRC'*-?3彯.[va嘝ћZs a/ltߥ{ܥuMMTM~soUs=?_-RIH1:~Jܴ '7Rdάn ;`(cӂpUEGXfc6;c$ڇ*"Rd3 NI| Q(cS}/ث/nY8_m<@]1d࿭f^7{▙{zuM %2cg~;{A*TYS?ws옠'or7㱵m!8‰(^8j@C;e PucpU5xq \ ՠ1̄>c;\^1{0s=+O7H.+n8\sB>tm{+? .UUs ߵCB Mwm؜Nr;Ip”ˢ)s?B:IX1]&8jNH⃊ 1/mF0޽$/ŶAh7;&رtje (C [/C.9*yp(iڑ'DT" 9'm9Xh`;bYY)A%w+:zɽ_!yӿuZԿ^ȧOnv$5eRjEҿoycfwYOh}q  $LHw')_")֋*-yn+#.Zd+l홭F q89kg!/ZKoˆ_|w+K _O\Rq.4YW8K7H~۽/M_/QN+= N-l[!6s]#{vzt寚5 )҉$@g2$^q K^<6~L&ol_BxuKuj]5K{k]ʧ9MI-%eE+C/{Zޖª*QXBڮc/]w>|AwUeWv!mdScS\x^W`G( %!J1âbDHQ,LV6!HT$ >FgXyjb61=תG֩g[R397/8^Y>y"qo yQql_#tY s"0$buV0n<{{"Dy0ÔPa1 vS"d)c?θc]WIEe}? )ܝ-@=d^13.>8o)NCbiOҢg<| i᯳/x*.VTN*ށ9'2~WUYXl>yz;{h5 ݇JMqE.1 'WOurJϸߧG~W4F| w(0BL>?`ͨ`P»S/&J|FCWKIVڶG_l?w؎ I@{x.HR%eOM~tRjD_@\x}p%t,!\acYa6inM9GRt :$. zdDG4KG<a| G!<|[]':W̘ŋt{k3=~#yjؔGaD8.dLS'փFn;:wv׹j>ӮYwV/6 +yz7c?*6ů ݵW B[CFGa @+>yS6m.0NfDaѓ2/B2K_i\j85놷 qHXS]!w$V"v-k$ R)EUB`z /h8&QLp#&f1ZU{(iozZ&T#ǭly'{Q=c;BOBw)K=;vCw I^=^VGȖvUk];Y>A$s6! Q8cӮثP jMy\"e҄#Q .*I=4b$IhjwvDLb&,yesLl@2J]ry7s NA"f9@,"SnK@26xC 1Y{b6c7mccD9EfvSCM J6ej59k?aPpK4WBrE;QlllSDVXg&wuh)GV "ώxN _ƖCL<5;&&"{SeiK+Zz %9!xNEYğ֬7mmY*{WyڱV2l+DF(0^qHwPjo$5$IḐPAi%%24^p^Vl16&Er8Qr[[|:YI[joQkԹ)f=5M||J]]iop8N1AaB,AAai N 9^w&yU^k&ܶVt(.ޢbs(R9#0,/*jTy{yڶ=_eB$hl0+8cg_ -unT^y&AywwUn[{K#K>AM-j-:2րD?vǩ?0`oq^Hed)ǧ BsUwO.xAfWoo83g\yXIp[va嘝ћ*r /wwiqF{%gyڗ[)g@1æEu[I?%nZVK[{_9 ,E̞\дGP^{P^絾r[ѡ#(ER(QEPlB n'I?8׋>N.sHdgOJY[G>:/78FνwY[,nSft,}v:!{m5cgLӫkRp۩7dΙÇ5Uon]?!n%/jN|ck/UE=(sPn~-w1 =dϮoX$HN0? EJjϯ\Cی9oǤ;1BjuhB_@%lpy}JktF (zV@ȿˀ8El/4]"5J:y.^{-B Mwm؜Nr;Q9(7 Dw~*" 8}{P``W qq*n7nh֬YpppJH&dLΖ{PDA}Um|PliǶAh7;&_?wEn9o(ڜGo:Ӛ6箚ڣbZY8Zs K?-cg Y^{9PRe^?2af"pĉ6fg)xV47B/`_K}-MK#$|uuS'YeהK]6I卙e?M\Czny_ӓW\pq֊Xj-yn+#.Z6ٓ&Λr̶9j2͎R{PaG11}lvvӧl7< .Vv4YW8K.*2;_tj5߅ogò˳Uy,p+?0 3}Q٥Xڿ`Nm>aZr;S"\KRaDq w\2(! A2Ӄs ]8gūOd뻇WO^'HDĹl9DwoM;Iq[jd# θQ4"VB趡C. BB3ˆ0LLy)$(6VƦ^80xʌ/@+V/pZF0Rp /Ҩ.R>P1b~>؏73n(d{;Y~gOK^ B9$C)1N.,P"XeHu0y *WȒ9qZ<߮"M6$-U鄑LSn鲼{}0GXEAMr@DQ{;rKlDa"Feq%''YfHتÇd)7A$GX Q'_JA=w7JT3Px e:)qV_NKBGa9-zc/5T+GY\\sʌ+q_k>Uqrl9U_p닥o^up~G]Mo.AB`5Ŵg!T!2@yj}6YjD"}Nr `!UIMoOhA!>9J =(sPn(orUn=Dy8NYT6tL;;; mOO?ۺ5\0.&goTV_OSԳ]Q?]@ B$2q瞛: c]{MXeG=)5uqf"hqۏVL fM}'.M!5O0wj_MfxӇ^A.%Yy2VOݠjvj+Ry%A5ܖ~pUZ.s='uah/JipXt[OL;s%7{yU9GM&q[jـyhڵtxV} s\&p C_ 4X􋟷-pPυ&7^ɛռk~fjęqɃ'cFo+!o'7*n:+ ff^JQ0D' s!#f#yI^]bqA}7agDޝ^Ը ׹j>Ӯut-K*E[T1G_JMy]{ 1ռmHUfE6 y'hxj30~j!BEAMrE~UnQFA-+0 Y5X./0 IfưDAU`|PI|շ#b6OfQ?.zf#2a ^hy̚1}@}xp=cs5Rz:w͉GTI~G}1gFpdQόxЕ46p8 \)=MH|B_gm@DQ{Pn,Qq厫RQw1F r5TTTPDFC8G ,{e)4c>33}oVßP?-ݴ nܤv8!+.k}w7Y嶵CYFPn{Zm3Fk6 Z-ӡҒז-gg.HJD/)7[!1AH8^+ENz@MQgZ&ZBP8H#.c5$Sp"Ca$X6ɕsݸ v>Y")(ip#\0OD m8v+x㍏??Éy ,+eÉ$w"Xg9q5e@8=,g# [Y")>&$`8WD138L >Z33ۗeM "Mr{رKL@/1Ɗ$7&DMԨ1KlHFz]ew3י9e9,7<iAv;h\G U}z=NWx~ G9:؞(K9NƳq_l?z=2Wd}?yҕ CkBuٞՀFY@ B R U9940΀wڑR0ȑ#e2 n;CA~^qm[8cs0j0)6%p.8̟|Rsy{=PGo4E|hΡmtBC/N{L2GĥwoJ>}`¢H㢽G.n}]߈[wQ5dz9JyA^dsV6E@ hA^(g;@k'f|䪨~jR [Eh1gLd]VGxѼjof9@ Bghqv (݈'k>|2!{%_ED+ 'Nqzs-tţ !'K2L-zv˭vl'oVF}wU/mjesQ:6؊+m*Ȗ]0b rx_0~+L-~q]v\ψ rqYb4OKgXW3gQcʓ(]' 0YÌܥkODoV˃17U%%OmÿEа o2/MnWۃ9dLCY{OT> HTw]4 yRxu$[*,0dA|oc;8%PqO Iw+Oҧ@WppzKOkBEq-EYʹl S)h:E#ЂE @6@sd,i΂a,X|K䄱xiÉR;h9UL2äL#`&8RZ٧ NؗpVZZҍ{!8P>WV?VyJi.Z_VV^kE4K#fh+q-iILX_ ]53\$%HOoGٮSuw?[v W ޢR{X(& Ę6OCUHw֥}ES={nȃZXoO.q5?R]ON^`ȼo݊m *9Fm2ssvC@ ͋]w3C U,a4 <)RSTiS %;!X^TahWWI9e~+r)؊;NԴIߜ곤g),J,0 -"G[]kR^"4\qXc6u1w F! QA-L>J2zOĠ‚̈́][zN%|AtXhhP,gN Μr@l;/PFn%LUSl"k!~vu{-CJnʥteb>?ܢ^-54_c-hbGjPW"* ҁ `wqa,UPl)86=r҃\xIWG؏8K<[Nʲ {[V,uJTovq={eOdrя#D6͸ooWa_~к_+Q~(<-ni?]q=N='DvE9$ OI7t͜\*#sRUpkP_8D;CRgXe68.eߞ='Ӊ a BKGCohݠ_l'NaWJZI_9~C̸?!{P7뙬c`w}XIv&; VBw?b_O1aW 4=I~Qg}G28o<34ʹe:nd|@ foy䀳!,.!?p1 ?g*E5RJ?*|´Qd,&"8 g z0`B9SszI7v9+r勒x1ˮ?6[3sqT"gQ?\ 63}_*ș]-EC1`DyKwQ |SvW{N̊T5,)"HNqڳ'r#4!y4ȥCqB.,rsE0r߁^3;sXʀJ30eġWGJ1vFxOcX~#K4I2 GY.wOUðns~Im3;[=7]v}`q='D^wq { kF\\maōgz#p\fh9@ ^|Xhy1'a^* b`NuD|\uJu.:L!\;h '1:d1("An񣅎YL`bCUUof޸q1S}%[nU- 1(ׇhj gm=aw?˟,Fg>\ͰݧF<\yQ>N}߈xgRuv8hD/JS͐4˒~>,'{ϗz}ſWyH.;Xgwc۟ش3o>=[y>"+us[bk'nニI|MOo١_=Z@ BKT&f<[rCkm9=|' 1,IC`d(r%aDCF~}xt#_{>nTT 'lq'=VhiK٭>e@wDu4p#zޜm[4y Vm|]2{o&%7PѺ:ϥQr@4Zhr>}(xl\퓙6aD-{.۳ݨwlN#CRћ~W&:P9ɧSb}p";~[6u >n]!"+wo`~硿OR j%CuW&㛡h.*l$+L^ڶ0H Y\a}g+sOM uj Wݢgj{O-_|ٷjF-0W0aQ~qVL#ھOoĭ(ϚqMpgj@44- @9;w@:@b `U#ZϿ&30\*a=W5eO=F) !l_2RM>,oJk f:#}<ķgwB%n0>8h9"U{?}?.I)=htB쵡s;d*@ Ek@[(p1pg =+^m;c'**n?t/H!F ̚j, wwcNׂuq+.SmKW+vltFsmۜ+XeJXEc H| itlKS ,mt$pg(-M>tѺ(Gr~RʫP}vUکaCm󎭘b{ΉަliE #VL '8^o`_K#1M:#AĈµzFT@'Z&cyZ>$0ĺrϝ={ Vm6.fOcEUh] {wsS6V ^D> 5 -lD.4h8㰺Ƕm>Խ?ũ{|Da)\ӥ>S2:pAdiYC itCy˥ =2'Hi_  Zy*M3B]2\WReAoٞs0OqE!QꈥbwpQFnA_l@4=-sZl0mgbf̕%$UDAL8P4gwNz]8`Z/%;~9RI^eɟ^?򭙹si*ɏˆ3bFt.,(|Qb־o[2-ǀ- ߙGоmNۑ_%91+SհxP"tnb";ũ aߥ L9oG. va-*0"WTj2xЫ#z% \|Z@ 3ӲO RXu$ d3)OWct>++ "cx KQDX/G ĆJ%+v73̲qeցc68J0EȽݪX+^pŞJ IQ±-gU=,_}2c~sޓӇ> -h=be;Wv>~TSG7"ޙT]*]4@4 xl HQ)*/U:n H6e BA E8tpZ(1h(A1S`A>:Œ^xK??ր,uWO&!x` 1Li!Q( ڂasXK{F4NA,bX(!)PmI[vi7l4_Zqa-+77o-SmٞMŎ. dv,BkH[fEl<B?"E.D!S,0!)RHZVSX{ R9r;g4sŭw-SkkUk3euXg h@gN+|(%>,` hE,PŌR9+T`.,&`iRmC @ "=|Ky9f C0l9cS08''AuHѱz-.x#Jv}}f1P< @  JRU09! 8s!,@ $`Y*| *G{8''!GİYy呍-~f9Lգvoʔ)qt{;-}BN;_IW"6/ kFX e]F Ѩ[X6 q,c?h0/Վ|X(``7 Sm:kaK\ (,GO0W_~ќC0DY׼nd_:;zO.|Dk1Oxh |"ӟڞ#_s.A~ᑔ>AރI'^:L"h L*Q<hKE?brZ/Uж1 9)hYS-_7v,_Z5nŜt*8m]e#" S熴M:S7 )emP83Jt>@Ԇ'Hc'^|uQU1HR еծCJ;uz:7>Znmݭ#gMmTec;ώ$<뉃,*ݮ҉ [Eh1Jq0+-HQhh34ʸ^; vZKm~Ăar>`S+m` ɏ SbV6r7 UQaqY\Ԇ;b;'z ){m(X1zvmztZimN(Äb4OK?#jϓOy'3.&; dܳǨ1m%6aQxbɣ\5{i7^!m eI8u߁2֐}+֭%!OcEUh] {wkЃ͙2ŝS<Ÿ^֎.2W]Cpx/ŋYv#ߚ;(<#xy`@gǀ- ߙGоKSs;\='fE|JX\VrML|8U!r#4!y4ȥCqB.,rsE0r߁^3;sXʀJ30eġWGJ1v@ Dӂ<9'yt,:EkYuWᔌ'1:d1("An񣅎YL`bCUUof޸q1S}%[nO޳U F45GkhĥOf `/{q:`r'G>5lΣǏqj#F;Kܶ븘@ <1E^J `ar B(H@.T %27 40F@b ,gW@ko0ѷr[ -}E*A E`T-e3SFH#pʂ`Ҟgl>c$!0JHr (C9djׇϩd9:קF^@4c:%Q }X#AQ>&oS퓙6aD-{.۳ݨwl@ Ίbw;0@ @4;L(0}͘Ƣz 3{)hlS7G6@  xf)?שk)mR 9ޫƳN57[W'g=7mq>Ƙk<@s3n7%u&uZCKCf]@!X$4& ґj͊<1BxD`I)lduou\)YiGr9ջby]שԵzr0.gʻ6z@KtԻRۮRƀV Uh B-Pb1,Ř6@ v8E@[ F0[ Ap gc99XD3lpQ4$Q @ ^PL(ZJ@*_ 䜐 F,`>y[#=@#yjbجUku?3W{xFYQ;|U#sb߼`y,n_cf'RNG~Ӳlۏ^1-d#YOt%b+knP]5`@ fQ,TyRr8 1b`jGSD a, D0j0)6%p.Sݣ'/|hΡm~aN~ i8f53Ύӫ$Bٿ) "?bnu}#nEܝGyk҇?;UӠׄ@ 1) @9;w@:@bњ*ak`2å]?sUZYfB͌M7A:y,I5HgwB%n0>8h9"U{?}?.I)=htB쵡s;d*@ ~P 0y;2Ж : =+^mf ljۏFhb̬~b{}/zx;}-H[rbN:߶t宿2hˆ)OGa4׶͉)r\eI*sC&j80'f ۲o B,+]?QZ|uQD ;H8tkЭNQ-|6֑sߦ6*1gG~h?Aތ\uVMnWL-"w^S}h3&[!hXZghqv (䚽\+m` ɏ S]6r7 UQaqY\Ԇ;b;'z ){m(X1zvmztZRMGrqYb4OKgXWn2LcT蘶n(̘]*D@`VQ,`xy_qڸC楩*sm9azd;qW`T'ᱢ*]^=;S Wd_Z,mlJ#>"!X_?#in~I [wi/i@ zg&l0؁ 8X 0p=/ ( $:}/s$*z$3L&O0^yFUT)-t JKKqO1;D`m57b>~&=͍@2)ǦVj-Z ӒB eu)qL++kfH. K#|z?<Юsʬ?F~WO=$6RVOU]DljڤoNY S rei/m!em%rA?mYzW`RUnT}㗻d8dA 3Ky9/X$Р XRʝ9岁w^ h@ 1SXCMX|GjPW"* ҁ `wqauDTzP˔{4L,M'ԩd>uTӲ(5;Au6醮KzdNJӾnjgUhgH:`tP+ TgХs:?S\AQHT:bh-~CԶ[@ Mm!8i "`s IN612WPT=0!@ќ)S\9CXuဉkd~c'"^*ы:kG53w9M%2QyFh"#[3}-:#J̥sbVħaeEL!DwS՞=aߥ L9oG. va-*0"WT)':?Wb危ˇ!9@ 0/(tK$Je2PȲ,aITkЕcmmU?'yt,:EkYuYW1zYYAKX"Ě~?ZXD &6T*Y%.X][1f+1W)E+AVsT2DccO8Of `/{q:`r'G>5lΣǏqj#F;Kܶ븘@ CtptlXj&++:vloTֻST5^t &7Al@)p ,BP,cP@c4+|vu~~},uWO&!x0*;4ݾF^ ųs72C_p>sjfJr.@Y Z3Lͧq` bÒ0F IVNlK"C9djׇϩd9:קF^@4_0F!pk{LKxdnos޴fB6:>fs<:&r 2òtkt~fP}XUTm6>i@ܺ=K!ލ{ h:LΞ2Mst]b9O `[ø2xۅEG?mxz-j@ @43\}0?>AoΨy{n(.\YO#OGĺz}Lq^ j:Vng:@;9X0mf=dc:q YCl,Ҏe@BcbZ`-i֬#TAGdryA MLa#@(H+]+j koVC[P/sM{W4k`K{n߫.~cʷp `@<*/Ƙ36s ı_$gR9K7d+icI ţ@ @p($qJ VBgnclؗz80{Q>eW.ƁkL8|GAu2?Z tS"&VmՀA \ aN`$[͢VgP8?99488r&ͺ\+l^;32W+c6$9>js s :}+򈆦i߼`y,n_cf.[)<NƳq_Tn'OyaȳG͍<V@  }zLw+((8{wO;k[;7~{]9 X}djag߬zrRo,,,G U9940΀wA e aBZ8) g՗buw_4ЃeCK[[y\uvŭc4DC/N{L2GĥwoJ>}`¢'Doĭ(ϚqMpgj@4JR rrr8]ѠV=2YFzq;E  @w%\.TT0;_.芰B֚2ofl)%Ld?tHJi[Mk8h9"U{?DA ׆\@ Q? 41y;2Ж : =+^m;c'**n?}Ks0R,33Z!`qoX mks)8m]e#" S<W`4׶͉)r\e!mZnmݭ#gMmTects`Odz8›񑫢[٪I*)e^yJDf@[=G`D{YV`0Z"n#Tf_\W<+@%= mV;oA7e;*6|{I GZmwlۃwN6dKS.Pbj9ݵ=FiۜDe3O:\\20+$q!֕L&AvAv(Wb~ڍsqȻ|FaBkqqY꥿:Nw}c'}5dtߊuknIٕ0RԼgvS'cv9#M6%z@[Q`Ried"CA3ꢢ̢‚*0j(8N`j $PB~Ze%"JA IHo$.[!=u!5ؽeP}o˻Nj%`߇KݽsמlSǷtα-';Ll ^;041KS+U$J|+ Bh@=pQJnIUX`ȂXqJ=.=WO;z jm}]"aF^PEu]\^qk?1J*s?JkWڸscgGl(p~! Y+_1Q@Iu_sY@A&ar0ix3"Ji& NؗpVZZҍ{!8xm{=o3nnuR/t>W^2)ǦVj-Z ӒB }I",Wvx$)[yۛ*,, pd֭UbbFԶV.Jb1PuiA߮ex@QT@  Ů^^1aMӶ[&|رQ~5N6pۺbuQFCQ$E/-BBa׵{Y5:/UDb!J~P*0*$s'd8]{yT/vuum䝝Sf7z{">~~1 !zșOMͩ>KzZYǶm>u!U_NL , j(R#qx2jU)eV7YzN ˼?fF!lq w Ltu6醮KC?x a)\ӥ>S2:pAdiYCiMI)RzWq­BqcJ ICgb%ڼ* }{O'3y+( WG, v6!n@%h:Ky\`\ ;31ɵ6y(5 L)Bb\ L\'$;~9RI^eɟ^?򭙹si*ɏˆ3bFtyAw5fE|JX\vǀ- ߙGоmNۑ_%9Oitnb";ũ/?aߥ <%hۑK-ℂ]X@a(.gvȕfDaCΏ蕘/pyc!nHN a6ze Ͳhc vB2`fÝC+6f9u﮵W%_i $>GX~_*-O_[+єdef O5>^7KcA ש.X͸d5lΣǏqj#F;Kܶ븘@ 3ӏKo}9~Cg0W9cN˖?sCGAܦoh[k!]_ktg[nirfdRI>ah|FST5^t &7Al@w&pQU?w}}GEQWRZfVjjjZfj i3 \ff`@t>o  $(EjpGCK2JzwD="o3aޫ'8,;@YĘA .ɟO I Z~ @NGGG9w>4$ǫh<- 3dr9p "XDrXEg-i@88ёcWx~ـ r̽E8k@ ģ( @ޤ_/K_37!M hɾ[PxsmFs-#3UTm\h4\P>-69^!N{'.ڞ(zvg9@ (H#Iߑ~vn*Y'"]M8Rirr޽{+ @ !E!Vg0ixK'#<=$R^x'K"""||| |}5رƛ9YӾ{.f<7Ƣsf^:@ BLn  _KrETn6+.y*W/qBWqH .f<7Ƣs[5YM[R=z"󲯻7Wie4뾶Ur'|uK@XXӧGl -8HLˆTG Ɖ#0"iA a x`U>r HBS:wչVS|(r8G޾># n`2N]?"jRmqy Cm.v b ??_ f'Z2QE02N-]:4 b`Vk2'R1 `!FrZM/A @ 7vb =MmYW+`$ ^l)ǹU7Ç0HvpF~'`t '˯,+Q3QN-@ P((,LK*dr9)qaXm^)Fho3?rFdPH -p6%#pms0W!Œ Vm=t\rpe_]͵lGms>(z 7^>R<:Ϯڹvo=Ztn̴=)on k{Е\#7>ݑT}k҅ak{USL5Ӝ 9W@~vC¢K$$ to~?~zO@Lh-3Ӕ vyQFC9)栢0X0eP :"X50E.%p2!|B}58owU=}cO2բ WsZfXj7aDcO4ٜ3gnyRP7vHx]('l2\]?këLbZ;5Ѝ{r;w@Bnx@ ~wsOաS'JL4iXqB* Z2t#q q~Rt< R\REcz&FP7ҩl bň_d/Y^.BM ?kQH]9 ž $ҴB6:ʟ5jلA^O#+ ZimzX0 YۿWi$ߒJyڴuqBrrO|9/@Mг@‚\L3+؏[Fvݰ-T  .DCbݫ e1n kli---dp |N7,Xl&Ȕƪ*Ffҽd\xiƷJ?`)_碶Z 7DQ9Ne*XrA2ݵ}g?tuQ4zʠ%I-:t5ネ#Po/ ]pC ?EJp3RWzPEMY3NJ,cά|g+󗌉f@ XQJ7lOrحC_Zy|l@_+3.W{^*Ϝ{n,ڬiSP-2'vίD\{,Xz_լ+0JA%ջfݴkd}t|ׇzzq+a+_CË'.cIJ9]+-l#'`#yΰgs7fC>iFeO`zBR1kl xQyIcZ/>1EfSZ$gvټϟԶ&))KҜpρ=/>XujoͥuڲW׽G+9jdҼS^~vn,@ z!>G{ |!lJB*5B JӀDAAJ)`6<'Kc4ɩ1sb@HQWvP (AHF/ PW=IDMT&h9v{ـ=jKǐ_nB$&O^m)*ݏĥ0}Z>v[66 Ƀhp0a`V%U?)=h)X*F&.W.W~ˏ!SºuO]c^K \LY$C֡3T*qIP(t:n@ !4X..{?*H>PHqpܸ28֍ѭCBf^eƷMSKF?@'9 p~n: ]=~${s1 [L0fWѴ O%TdIqa@דw5kj·+` EB}d13->9FDrq52gL=yyY *5Xo"~`z篷nmdsnwxUFUZUb+ē)oXdx('.F 8'Yʤ*r&aͥ2YPh[8=h4>-›ۧ[DOŖ@xt hհ^7QfB]x;eNBkkM>̟$>2@s(ڿ2)8DoW/4'󒢅bāѾl5-} U TUNGNXQEXlj }&y`bg/uS ~\4yֲ涷j"8)T0F jEUd{U(b@aX zi7k j2`J Os>IZٳ^ose6:qt :CVVVZZ:3ؔymxdˋĀ7ww!S,[6.A5EۣcuFP [U @- .0 'N9r{%,3-+^JnBW>g&< D@;mu|Ȕ1xyZΨUzJ N.B4bqZ?<;fb\ 90G?F?Y}CQŞ6ZwlעOO8j1=uѩ$x)6ԦRͶS:14vJX( idXvySnH!~4 @ w!J/.(0m! vg4ۡ̍H$ʍ),ȳ..n޼Idxxsnʉ~ <9.߿2+Z9p1`ó ]uKyPSt1-.`D4Cy;$*0&qzQ[yط:-=bzNÃ{2d,D1lLԨ Aţ>`¯'DvD="n1pdݜOg/2q#:>ǴuPeV-ޛ ^)aF&E: o¿sf&K7q+NY0 ~)k(+)b<3w#F,y8Ҝa.!O]pzB .0̶;+J2ӂBCE"1}{MlTVl?dU48nͨ3mHOlg{?޽{wӧTZziii]6U/^7eFu1YX qd"(\1 ̀M%*P9YLbf 0fOM[Դw{w1`)ۻWsL̵ά#A"u}۞ETz47J;gbPǽSMzldٛ WgQ2HMH= {fTmܪzB T]_aaᩣEFo?xr'$& z}A~nnqC.Wzz[QB:x`ii)qU 3®f̻6Ɇxta"PAsŸH-5hhcI@IUGm5{ugǽzjcx@ q(_\ rsspo~4M+vp,۶c^^gΝh4{nٲefT&M 7222|tҮje<!a V\.\SkY"K`4%mG0:r/|Z"ܹ4 @ yRM\̼7 ph 5FkT$3*aXd Kw_`މ':mN=|usQO@X+8ߺo~~Nt0Ir_MC *P: @ *L3~_-`ǺorfQO=4Ʋ]yz=xflX` 1Ρ.9*W/qBWqH .f<7Ƣs[5YM[R=z"󲯻7Wie4뾶&EaMӄHT&w(:.HB̸1;&gۇoiDZ@`F<0NO Pp/#Wȕ.tn" MUZME^{/yt8Qt5vDKU5( L:Af"#t+ Ȱ̫Wa.$AXg}v{GFf@C7lɬ9XsKZ#)8!U 0c,G@ F]:FQTAD,2ʧ,cu5˜N3.;#cm+`$ ^l)ǹU7PȯD3N8re%p8FQ @ (&Ea1 RY\Tq?hÎoBaW>ZUʧJv0^0jKfDhQBXgXB H(0`:xR,[̟`EPu %c<l@ ½T(Xˉ KV1 ;GV8nѢWP\8xPz*.3&W1G ~lGL8ء`:8pYb8q`yRYW`,C{/瓡#g~ߠ'|bڋvͦ:0fIxXʳy#?m7!LRԇ ~~ه?Sc,#\Z-ǟ[>{y+Hq%Wmv8Z~9@ D=+ $`jWMo| #.9&/\(8NvLJr@sh0K?NfgIjB_3ӣ7[WC`D0F,8'$ЌE(xH@ B|0Hw0zfqH"tm5A˱ۿ5T]:x?_Rt*'1yoM5?{H\>Nh_\YD#nnc& ̣Ԫ?k XV ߻羷0t}}q@GxPBQidR-r2P^aH.s1UG!a%H .&V,CXs!x$:e 09m㬦JBƍ1FCCaj=3p>~=Z!ܻX&rSgپoOHzu&~1Nr1  `4 >v?*8wo"*VAf*`'5!ijVVz?C)$~դ0 JɻÚ5sw ?{Ļq*̵ 5 !̴cdrXS8H2?O ys~[f*.VTQ'_o7baܜlVnW'_Ea6_C[$}'-S@=o< SZ|珍GͻvK Sg('}N՜is ``0ubʵMvT:;=?*bKX yk  e@@lGJ4ad.4 pU6(NՙٔymxdˋĀ7Nx.qe Xx_u"HGꌠ)c;Z]*vaNp/-;rJK۳Yo+>[ȟFn?q%7GoL\di[D@;mu|F?;_8F< ;8Byw6* \ oWmCQWZbSŞ6p$En1q@;czSI Rqm~M?>m'tb.i!P^5t+i|Ȱze@ DxYz ef2H.DݛlAl@0PQv\]IFoPf](rK:;'Wgښ{//sPK0 6._gpvյk75EhFLp@30C¡!Hp1ӫ`cǾiɬ.1W4֛wZ)ba qİ1QR:(ةOGq#[~|;8{_wԱ5$Ihw@#6:pbr{Z> @ GDz[α$pIL̎m"k#W-4Ҕ* `uy\iOuniFH!C伕,y{8W%b-8 Zu~e"~5+FM\+BFȕյeL/dFcaFrڷ&X. t$%I 45MzX/cnr]u04A8C-Q䵷'@#wΧꋮ]wq*2eAHW,eqPbFdʚ_ $0- r  J0eϒ~x'RI;s(~ @0?Ҵǃx7(ۣF^ 6rנA|(rػT[\u.Ea5Lnrs= 3IzHv?RR>jS^999d sv^^ I8a]"r~~V|^{ \{y)t# ø3kQ\wչ)rgC9y{]u\bDޥǫ†B.믿 @Qqpn=szeeL*?yEhT\TTk6,8Nݺ3_8#=LG Z`MwhW耏L[ʖ\ٳm\gFZq%֚coae]^ۻYU5-:vgqݟ3mt,xʛ;,:*oߚtayWuU1Gf[h+j7sCP䵷ȫt7k__(rػT[\u^E!@Qxzz֏Ns@X֯sO<)"Ϡ K  v` C)L&& 27ˉ7?)eA$0aYإZl=nΊ*p߳_͝ŔXEpu ~35ʋjԔB7~үn$0fmH[?zܟ9~F^'EZkMFAG'v.yQUorLH=P}ƭT#PEQnq"w꣨}F}tfL& {ɓK4ncR*Ca.ủi?Rch$:@0q q Мpn4 3 VdS&DUiPKt!XcgX]?fON $MHח_y`ocrΜ=fI ;=$]VF@{yA󟷍 vUר!Cdv\ny]W EQ{{[EoF}V<a*bNow{dRRH%xC;`LJLJKK \\$5MΖ _R݃,?ƁlibճlX9wX)p=_ն4)=ˆ#k!eN:R!_~?,\,pX1o2/%{(aidD˜Q[VUw ekz?9*&sTD@'컪СBu^"6lN7׿>C=뿝V$TȊ|.Cg)K~vI ~VEgN|>3KyΞDE 6r43fAx#wվVL&J%n3Qw9P 0|;w~:zp`*7XjnAi3χnڵO.*>}WL .<`1'TbTi)+G8kwXaY_6c³?;Ml(UԒ3+V5KO:zYц;uNJZ0BH!)^H lgM{ۺn^l6աy$gvټϟԶ&))KҜpρ=/>XujoͥuڲW]eNJN,p4orNgC}G9! BhxWzl=p P<8'ڶmpXm4dYut\rS$O&*z٫arvWL}B4Md$D_Ϳ\7Z5UqU>K<}%/BUU.S=>L!F dmKу=h)ZP~h$?R=l"#< (P(42uv[~Pֽt{ M?woS,KÇ"%X cICgU$<nEEQnq"wRo VQmwD `- w JQ _Zz$ FGW+RO$d2Sk)׉U \"'x">|M.al<+ݮ\/%U OjApg:O#{M}dV>cdrXaB>*\ R#x;wBCCUo{\ɖP4C$t.->}f7^}n_(*L._tQZ/)Hl]?I Ϟ(y,9\LtI`ܼ޷XS&WKIW"$ցJKSq-CTXɿs2,29V@z~҇] ?\0G?F?Y}C^};8Byw6~ř#+(Y MZ[7Q8:/v3v*qB'N|5H#{2Эϛ #o'Q䵷Gy̮s`77]o3ŰA))7 AEDd^ s! :;]#7XhSwtd(yTF6/N=WL h~`"2(yjp7~)kMXijԷ.za363 1){o%/!o$12ih;6ͳs?1GߊaMA.U'Lyl䧿y捧AwRC@i⽙jސ2Me-l=i EF :>DŽVw"]$;jFhXw^tʂiJ*dE̠g<3nĈ%qV#0_32䉱^hV?&>ۣEGQ{KUG! rW.h(ljd]\?1b  ͺeN)*!W-3:vW`pC TY, l$c$E 70OiEEw);P o$\,p}M\w`" 9߾4.Q-?>޳%L_giYTN%GqTL긷,FEzldٛ{vl(+ӊԄԃg&9Joq7[s (jo"wsC}m7]oB@hVL҅("K1~ ~=&\XEXIZrI"RLr2,m,g\gh4zzy\T 6u޽"#iksFe?H_kVΡi`0@0ѣЉ AdkPP0FDVK8G ,{e)xs48Hѝ?xbov~oӗIА0gՁc,;=<;  o'JF^AEj? 6rנA#-.WJQ8 : (k[c8$ab0 p LbR44cY= +5 s;z&M! E"LAzTX7Xt:Ljuq~gXؒ d$.P\sVk|wXcB.NYu8ёcWx~ـ ^&Z"{(r8G;Q޾x#wվQQ+r,ky:$p!c|3 48qB&"$Pv`lci+`߂PKbDBD  B&$x2++KReHZjSWf^ \a[XȿUcQs6S9";qDG)Dгw>[1w$(r8o5s`f}xbi!"HH+Ђ#98)ld#.G@%ernQׁ9;"H BG0IۣEGQ{KUGQ͞٦k? $bR*cM~W_DXlF1]g\KH!!xQߜ Prx r"zd`偁f?w\ꅮfQAcY].ynE<kZC( uk9Rkym/r'.lU.}1`bmq5Rp1·:ϑ㤂$"\%$81aZ5M3nKmF a2o~@5eJtReqD"+^x@Gaߵڂ6\ ]͢!=4Ʋ]%neW7@@]㍼;\fjwWGa-V= Ap".bJ,8q"ģX9̕ڜ #R> 49;AN@TV>IizĉTXOct}@Y~r$Hy2^a8#"1~F/C@ S`MTAr*9oӟ={7V۷YFrϟ=7EiP> {zLڻT[\u^bTGaZϵ=0🈿Ν;ϟ'M  apwF[[+G ~svA E|jPW_~P9T劂(`;\P̈ ΦP`NTgHdo1 zJ@zlb:.92b jeKN8jsu f̽ݯBu4wDMy"u]s8{߈{ܘi{e)Saxn]5ySIu۷&]v^W5+9W] ͩs]Py;qx#Ty-D^jѩDKU3یl `U6Z <'K6<. Q5v}f0_9^[l0Qz( 3EF儧",s? }h)) D;@Ͳj`2]NK0%дqjKqΟvgٗٵvfz1Ƭi>}G9gH֫h1LnZ0u)W?ؙ?vj{?'v.yQUorW^(T[FPTcVll4ȁ+AEx˼6Wlt9h0jRmqy=+mVCxY¹RpLGLY? 143žug_egCs(..7nR!PKٕQ8.nJ7:-pN'EǓ.8P.8REc ;2R_4L;k5jلA^O1HQщ4ʣSHQѩD}=( Wi"\ >S< 0A: vsTʖeG6c` ^dTw4;aСCway楗^2$@a,JQ'(@SF,  㖑]7lv@Qay\.00Zߋ.DCbݫ e1n kli---dp ,,G6edWcUj.oJqqVe/~V'+j€.b*^(sQٔeUDbyƧ.0fWn-#`,҃eBbqh\U擖b~:U\REMT} )|EO9q'V< ܜ9rgZ6N疏:o_\fBTTG8iYqv̘0A61,c@؏e~*uh r/m6[?x=6O.t(x-=;RX)~Sg}_ΐ[o?E I#DLev/x[?{6a:iL+)<&|bڋvͦ:0fIxXʳy#?m7!LRR9႟'{_'}>~KߚK]esg<;ʜĕ^Y2~hi)o/? wQE+1M4^&GvqY0|EoOFRYSa_pjt>;(ye^y* ܾ̫-_%UkU8''rw9we5-ϩz^'ڦ|,] ?xTQV:&/c&Q.~.E?gv?E3/ίd^4#D?,Z@ B|0Hw0zfqH"tm5A˱F4T]:x?_Rt*'1yoM|iGRqF/Û`u -lao_)^8 D`pPYjUշ{xvSSZKMoIO T( Lj]]54B.uo#]ޟBT,JÇ"%X cICgU$<nVr@) ?mZ:XUE;綗9r&&&:@U@oreq(jOF. 4 S巇TeXyZuX8Q}kM$r';U۝\؛TX?Cq8?ݜ0SsOg./6,3+((<ᨧI <Ik׮RD"1r`BϢDQ~1Nr1  cѵǏTnsơ c/NjZ+B)+%L_լ4StIn)&ٵ*4i;. Rzf͜!0o"cgS˪.VT{R'_o7baܜlVnW'_E1Ri5E}[[<2/kOEeV(paӓOz>d0WwӄTQ~Yzx1#W֮gm9RǏ7:59M.t~xޙo>YLa w|uZ C3xPIP c(0BB~>ΰ;ږճ{1Kau,kdSʲVmtZjz6'KJJJMQT> ުF~' ~*“KH@@܏%0C~, e4iO淧^^la$q0hvʴǦLX]݇P#g.Xm??Bd@oX{*x>*X֕ SM l><0`'Y8ŐEg-kn{+iM* "H1BV$y^۟5 WX?׾3\km}& #ά9s1e{?8FH/ˌjj Q>빪]/Lﻫt9#*Xݖ9H6 0{X &Æ׮Vt11=!h3rL)\F0 "$D\. 1c."( AVņ 2 lyp.n7q言ĥXk"HG~1@- .0 'N9r{%, OJ#{ן#&Y4#C !Zs^'ԙ~g AQK?'S4oU2Qo3 MZ[ ֌ѩ$x)6ԦAͶS:14vJX( idXvyS[ad[#G\=U8!~/TtB <{jx+Mx揻$%Lj)t?=NվKg=k|1?\?Vb@9WLŅy`g;|4PRgJ$-#n\WMBbD\/.P#vөA0j /ݽlJ)RG[eFA(DFAd^Z2dZnJO.^h{/oYJl.ŲTSz=&|We~*nr.Yr~rȟ|^צ0PgKŲb`]\SkMC5 aHFWu.CA2L%F%((?hNmSOE9kBRrW̢| [P ^ X`APr՚;'T) ahpʔ񞾔ǃx\'16帆EwkG"v㾼=}pIG f_qay.޴L^ ?}ް*0HUésPidjۥeisX}W0?KYϸqC8O ljuI=gĮ /C! $ d# wkzLY,wGĻ.AjfjE,#?{zZDv?N=g}Mί{g{o͍Kήss+ Q爨/ub?Ж=u vj5\N __pdtn};e-tҮ%/Udf]iw}\!b|7侦6꾰Lllg4#0ikҷ{Cz9pe8'.a;x۟9ϸ{lʒ dʘ_l* 5`UNTRZy*.ԪrN:׳Ϗ?Bsט[VhoK/8R(8r)/Ub5Yg;HeY@BM2gMyrpN pNqmg*D;,N 2}NL*&xL['p8PN2m̠mÑٌ~9<,IW_A. v<G_p-Fs<9DMݒDD % ᪌b f6iUIC[VCcw`rDР緿g  W`nQEM6 :/>}:7<|pnиKYm;5HS|.%;Bo|0Q:ꯑ)_#E4 )F}潨F on_Tvuh^kk y){,:53m<1Ӻ{xEhA:'cՉD0jœ?5utsn[6{,4<03Q&J3o]Q0H^˝e-7Qnn0e>.泵}H$YN|#מGT"n`J qk?>s^s:)?DΚN%w]8Y@ES6hH#1˺Kť;b;eOlr"ynt.·UìEF%-3Uf$r}HIfWI%Pnh9$EaK<ϭ0lџ(V~BʾF/>qUU1" v&ȝG]pt-hG}5m"iϐ=I$1,л7m)"+QޔGq Q@j9CQPNRI0hX/ b $A¬>aSrD~=L F PO\YA?dÖR, (p@4 =ZBRRRi,6ێ< 6~.zFΟ/r^_qAsk8@@` z* P' Οu\:^ڼg|@CG?i+/Z(I]Z;jF _]DeQ\K_@z5ߥL(Sf06?ZC)!П6:=!FIC H(,.T]Zŕc_؍$55Umԩ7,x\񟇋$G6H`@ՈE"W9E l[Tս:vh Ԍ#߈0z"~Q!uĴO`])Gߛvc,D?hS ~㉠a6!O8x9m(̿wxCg'SA>6V|+p6[_y%\Ԡأ<|df@\/:R/ o}/J[ |cad` ΏG)C[)%f5dQGԌ)b:y$ĉ־ ’$IYoCA H@Z_3SuyqL* ~w\d_;'Ҁ2Q8C%ݨAآO 0r#q3#F-EƗu@] };NBR|BIi'U$A@\!WZHwIJ JV(Ցi/;56mUOY5U5bSJ[`"|l2zvѠdUY_ -98iקwL=( ;z1une'1GA@IӆFzr ?N謹0Ҋm<ן[sdIymy@ o cs%~P*T|+Gk._-<;b}[29h@<_xPSeLEDA+IF M h"U8_  ʯI|שSV'[O>bX;|bW@L't-Ca񱓚0vͩ#J*$B O'G`56d8nɹ8p Bq p%OVS=T": Rj/\܇A_RO0] YY-ZoΞ@uzbddТlOꏙlt7zX=eB*,GT$~!rtkEU.~\ߡ9e!t{yPĉ2y!ً_wt'-t)h=#wr&NI!̓;h!E F/cz|Ӹ6yQ@ya#Cr&; ~}aqҁyRI'J10nY03nvyr6~c9oġ=\JlPq؇A$h㓿/[ YN#5㯄=íi]E~ܺȫM^OugC[@j(lCQ4 )1Us'z2+,8&؆ mY7jc7wM?rG$wcٔE (E8Hܢ@.*e5^XZ~d*%P!l N2ӗx0t^kKAZϟmSkXtv/"i7wz aQh w^ֻ>NG:;Gr <}j[16ésPidjۥeisCݛ}W0?KC3a3nw7}@`.m:xcsGq>=~]fRsص"e(\$zD!Ұ1nMp#3##w \r~<שE_o's@C}&}~lah:77*u<~eZސG`]6srҧ\;a!)Yގ;Qѝqf]iw}\!b|7侦6 o^V&Z|ts/~pdt}1 R2ǖ=u vj5BW[+#%vTb'B/[c]"\.W(d "n?^{9c7/SRyrc+= 0'pۦ,gX@^7?zpD% gѡ<?5Y5% $: R(`Wed 0]wIME<0ܲD#+wү' /W`nQEM6 :/>}:7,V@'>4y[ޠmvOdP#qG荘Z:JFk$1YqW8IR7E5RxsR۬CZw]S\suHgIǨMOyl58v߫/vd5U6hH#GdD=yY:>kW[ preSPmq9mFGk.l7s~בϣbfw_VQ4k矇x j]VBo2.)&v]j#OQ\mƶ61k6Ԫ@7< AXPVlz|W$qy\ht&g"<<.%T(F72T yUy6ۥ&f]qI[ͳDK%gQJsOgDGpŤ #9~80!}@ Ď-3,6W㳎]*@ Hu WIIu)IZ[kUdlph> m^l}Xuk(ĴcI@!1BGU/$b%'H\ACm{Tz9e{9ٟlw Ŭr˦Z:ԋUM-hy[N5_2+TyJ9m޺uibM-lLg, P@ X@rm ܉hIy Y($5(;Kľ8jM͟X@j CKav۫ǭCU>R/Vb0A -Xn]^% ;qsu!$ v:5 |R < P%(> -LZ 4s_*8t @> ܨScLf#+#DLix'Ȼp薰uh1sBː:]*H.B%C?>iX ;r:-"wtx?ƍvF֬BQ`I̶ [av۫*,6w}k,/bU[+V 3rUZUnQϫ i6Y-,j`1 =~/-)Pr'(T@*xJ 7{'5D@Aod'b$R䄡ʣEĝ=Gx̩_4 ch=67 XBLHz>-jB]^?%n{6cq#'{V'FL?3V9q2"ډ6.ިwwP?Q%pDG+x9)^4UM5v[KX/KfsPm,Wޮbrs]aF?.qS'M_P:om9T-]4(_ PzPt8 $p1 ^SU n C/mAQ,3:A,E ?Ve k^-||fk~Y8UmV-˗@7A<@z#oD#7oo?"賬i ]I{Hbaא޽wmKA T'I}nGQ~[/+/-gaU8wT*yOk4OO>WWcFkhB~mPwDߑ9ۗ d?]Mj]f`F PO\YA?dÖLd(p@4 #j6kZ&A=-6yM^˥\ח6|?d5xP윁-Q*8dĨ o?k# nqc]J/ϐ +RY_Z;n_LM9/L8xὡn&Obޓm=eua6WA>,NO6M9g=%vHZB}o_z|`lQDub\˔lAR% >Fo7]}e*P/`(lm [;F$̤$g]]1XbkICw69}aQFa}=ڸ>z2ʖ<קkbbnB[nxj%e(nIt[Xm1;.VkdRnYy,٥j;S`r+(f)-:m[Ӓ"ǔ;ARe ?ss 챶 gB~c׼M|@CG?i+/*".X#^F˯:η y֏-ZVj]υ~["@ZJ302췬dm܊rkwQ6uEa&(ɞRؘsᖁ̉\eO2DQ[,RK)<%c1QCeM4PQۮ5o9NNfZvFRQ)$?bamH%z" $=+,Ф_ܼj뱧|_~Ŗ'fCT9'z|)426 R]$0wj"sJ"HG X6-g^ 'P3~#v F\ЋE>A;tP|w:o ۍA=L*P<'ֆ9UƖ?˫t[Y|+0nzӏL`IsOeAGxgm3̀h?>^t^hyu n_8>x1_Dz02#”^VPQcetBj,'N a=Jyr%w`.99vYn=V*A QH킖8) m dDქ0?p?(:Cխ7huNzXQ༕}a糖cWNuyq D@i@ X(I@ l!v@4[X8ۑ|=`T0>>^U_ו6]LvﻳkXN2ѥ9̫OoPe4ĹRRTS+QC `RQB'@UA A;Qo 딼N)'[O>bX;|bW@L'tV/a񱓚0vͩ#lr 5ގ6 HͅI QlU>.qHc^4!`\Wrvt[mCAkX  &lƜ#^9Aܯ. ]sØ|1R$bۀ \ZmEG 6*[~_Oz(Yv)T?9=q,wA˫/hܙIlk4fe? kT&^! r}L>4~z'?KssPYTl=tL8$8>I_$êGi_d-ͪ86?L @  + r+1}S{D P7! rBJDbe6)UOu;䊅 @3w#B$'qf8 `k1v dTcz&rt0Ay]E 9"KWk8Ir3+V+-Ө:=1Gb2uhQ6KL6rJUMy=,뎞2g3(wm95~㢪u?Gy2_yĽ͖oT֥ o>\]y>1#568.e_x#3ǥ4BA$H/[ YN#5㯄7zz92$W)է՛1;uҳcL7ۺz*Y b/FEz[~L5)þ M>{O|kLj\IzwRHq=yHknز4M{^X,M""{>v\y9!߼ueh}-|U^%eu*.N􋷒Cp'6\-迏BzyecS{t|_.^-K>Q4 \Q2%pFB9xE7\ APbUBBekAUkr,R* P)S{R"pmD#Ƶ?ۦװ|(^Dnܗ<);Ì04^qJkJ#tZ~A͎ޒl}ͥ`j^v#M_5o\0w,]fE< MxwӚ=Ҧ7lOkr~>s?v{kn4]rtv]:GD|K,smN:׳Ϗ?Ϲk- e=TgD.hde̦yک[>9x9˥ENBQWܲsqߣWO>{gk.TAK4W ꁝ◄*Ez\"1u(hxn~$DI@*&rMZmno/ _y`e%84vGh V%lJT Wh0&o_rf O>}87h\Cf/VAN  *m5tɾFG#1tNFk$@ N,7 pL۠$gk:ryTAuw-;툞{^Qz7V}M!"t֌w/+qtelАF($I{aa3VNtE¦*\?u"p\LGOI22Ԇ$k۪!ֿ2i2,gE2QgM՛an^^OI-f$\X].F^&J,כa%K=j($Z /PIӥQO}潨F on_TvuhS\RYt1jDgxcu j.tk-]?hEu\ަ[K7"emBS2yoM-7ڮ,)3KoxA!&0]R\Sbރ ii..)8;k`Mە-{g[nCf1}E;}\VIf0b/jof$E)$8 ^'j0W OeP:kXI*c+tVN@ ;|C O ?)৫$|Rnc8X*ҡe+r"SY-jQ\ZUn.,qV>tOWL7MOO_v-ѣ0gm7@ !nf;BԄ:`?;l?T}r Ak왶ܕd%&Q料hBκvrFH}kC(Э?vD {\ۿJ^O]Uiy}T+_U\I/uKw25l'ߨ^2L(啐-*oWiWpnvs|;uo-͟꒑jҝ(Êue^&QEļ<\vj0/XOסjږm2mpTN}ZDy}@O<ՆQx@=aE9á ?(*DEayi~m1JtX5RExx]T(F72T yUy6ۥ&f]btj`!3wxRN߾R)kt}Ԉҗ6>rYf|Su At@{_.MZM=Y|aRn­ ,2-DwoEc,3}'<3ڌsC )2Iѓ03  B91%@cjOg]핷ثܺpnvv w/[/GLDʢtM-= IƆb-eL(!H_>@kNjwt2S,aaiZJlyI0a TF|B @ `f.E#n0%oC/ -xAۆV;+Gʳ^k]2*´L@ R$Iฺhg6bZQTk/eX$n߂@ @*F^u>R(/LKzѨY޼ϓR߽Z6U ^B;} @ 5 R/??/#M7'ǫ( ެrmerJ]2D TA\.I)u޴-xJJˤ$$,|p"(pys l c5<Ɓy>P^P}V7hp2HT! XDR~ӆ@ ˤ4 A*p\SaKq|.SiA  AJ27⥯O7mq*;/Z$̪Z {PGq5q~ʀ6 XL_⻦_T JWǵX&Z]Ox\.O*oWPi P PEhPnBnS{?z`0 o_x)7;G"q8Щ@ @=bzoRMy8 M*7<@QՌ[ ?&8},KOrVTJ #vFqؔh 0깟?T(JYf6Uݯ3h~,= )  R(Y|3]!Oohy5LjGCap4[ĺf`uB[Q&r@j/\\~T*S8AY,T5Pi܂jE&Ey@ (Q%ɔg 8b萠֭*0m bţ0=actͿ7/6 с_RmcƍR{{/N869jLFBD'[Mcb;"}`{#gF~?9Nksr%1>{ Hp]_~CʋJoZ̓ =jf` V nA^/&k 1 zFNJ$v[+/vdY5{۾p nnٞ1eWҜYg}}DӿWnՀ@!RԶ)^\|re5\1y?m鷭yWnzn꺩.yZQDE歸BR\M[{DHvqqqOWEkTGkU}Ao(i S(g$ܵk[HȠ go߽W=T99:EYC8A\tABinN`=TRͻT4(uBT.ԍ^lB$/%^qqTTdի_6jk< 7s栠y:R>xq$dC9[v\xh35)Ӓ~zxFm4A ci_HUQ U(-P@z ԓ\M? )<~poew{BTԪ<.׺=ѓSbed6A~>UΝB|eTh0K;na!\SGN~ǯ ўW~̺!>jzYE' JLȡ".)=@B۶!C]vg5vxlӿ6 In5X원w3sssFݲU˼76P\.'-z+ܼ7rKy#$NX^kfȳ0ռ:`ƽtQo.(_#U)5eז}=|dk j 4D2&_I>B( qXr`)Y?nݺg͚=w_ssuX,ռ= @ Wӭ? IJN^tyfVւ(ᾹT*ʣ5KSG%b(ʉQI뽞Ffg iuy' G:6@uVM9W~G2nn`,s rx_O:^m|swz_'ۂoHR<ݰ_V3zԬg}7ٙ9f9a VPTlVj:@JSOr@+}=ii _7lnѝ(olj=!K޴zb$< ]^'6^l]Z=j6+Cd{}i]Ibt=kN::(:aT mubz|zaعŋ bHR$$ ,PTt-6zu GQ(D, ECP,~e"Pc_Oo٪)S0Oz 3`IP4t&(-Y٢nrmhdF@ā,m ِiC@@Tڢe} Y: Hd+߾,Zc֯>{ZX8RYJOqdR( \`Ϸxc_+'>6;Q6PHOKDن$--.@ĸnGfsXl]SX]eCN@Jnv C_hڢ%æ^<ΎU f,ORv88upvi:Äz a 7o Cd 77 <.+T0',QJyyyo@,kpBaF@ M =z[Q*vsT+zH,X,ʝishB*d︺3c;gN1LZS= 챺ɔ $spp5 @QPQ);@IA~Wߴ|/ԳgO3bݍ_B׬jNaa![LՎ -UIB(Ul@ "X"p J&㊄".]#}n]Q∰F@ R3>q-o֪ЩSg_ @ˁg!S@ djaHnv֓|G>oonO@ @j4|>}n7ѽy9_$C& @ eY;uNh`Qb/iϒ5]!ja m@ Rڱ')7;@7{,y>Y@ TWR e,(hPIENDB`muse-4.0.0/src/doc/html/single/documentation/main_window_with_automation.png000066400000000000000000003017161404105311000274270ustar00rootroot00000000000000PNG  IHDR \sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATx]w|ni)tBH[h* =쾧ς,@HIhRt)[zfwrf@r 9-̽3&.?Dt:8ʨʨʨʨʨD@P.,_ n-7!XTB@@H[YF辆swE6kVJɨ/VQ6j$߇':ڟHDR&IF5h0t (x)_/#9gg0/׫`g7!VDs"nK*GgyKzT+.sI@[k%ub1<||zMktI/XzE)OA5"9oK| TWS,W @hpT'M$3"+OA'RFivɔZRep=<BdXI{IWk jR"{+ˊ(IhIPkOaTd&+9T2@X9zBX47:a W ! @ӕmW]3zfPrSyղS*y rO\䑕ӅSF)ɗ.j| Ryy:ቤy['9bsHjN?ՁTsEVAJyŹjA"|o3eE|B| ʨ$}۠s'G#~$<^ 5Bp:! D\ln_;`C9 r`ZaX"@^ r!00,S8PekWhYM]>P,9\+ |2BXVk\UPW{R.*mE)_ghȧ QR_WɅp?R#:x huAH-h R&=y5R ,P> PLkTZQ.%bY$ cu.Q>iYVu{RH k`w\.O\эн_vjժN=#ą{k14{C gA/M-g-.z0'%>A:dY\@XCd|7BwC#*p=+ v̌s8u T͆{YYhԨ AzmRXGCHHzρ+Ex,^ૼ#]Qa2䓨CFHKG U.T!sg}%OH`'Dοs8[u$2@-6OupYTD&`Pf=HA(SRWS @^D1џbj!6+zHo(Ia6: ɣpUÌ1tP'XBt.} 0FpQnݺLVY^>řS'QJzM:wK~ya5^_H#1xP$"o0"PX}`|driAe/@Ѡn].$RnITMu e/1%s8z~)Z@/Hєf{-8De?[ޫX!ST>kibSWv; pul()rM@OyApE=+!}V,]wК30ٝ8~όjۦTPPaQd za 4D]%Ꜫ * mġO##HPX6$̗!\žpt7xtl>dmh' uVo-h@"!b={PWOKߖ/K!hPl$" ؼy׭P8ש#9$w[h̞)W o1Qx+h "QaKdy/%OjNrN&oli (*e],`cB}.$=$ %*a:uʯo-Z,(a>3V  !91bh_t:={A!)Bׯ3gϢkD<^3Ds N'eC a`Tل^=8Nz$j?ҡ|J D],5Cj B<ɶu [T.Rybbbb2 sssjJDGGC>'vH-@c (*X,KOGnn@>7'+W@tt4"4r#^H7lԋZ LS^Z OP,3 Yÿ'24왳fxw`6{o } Dœ{|SٳpYJEߗ r!?/AAAYhŋ4ՊPR %8)*e >N8Iӧ ǃ",ǥhWX} B/F#4Tw>z"Qȧ;)|~[ֺ Fgs;yò7JHߙF]SдQ# 0|fnnnbNܦ ʓ+(Q1})ưvԫ+ fj$&QЯ@;P,2/]7 s=ɉh]`HNj У[W^ mZ}Om&WgqD&W<2s&jOmAʾ'OFVЧw/y?v,RSѬIc|{ B@$9dijvA>wЧ0*|IOEI'3IeZkb/彴>g]x O\0W_ Y׬Ws_ƲeXx0 ;44 K-Ǚg1bs8Wh [)7f12=zu7왳!8s \t\v ~(Wb(tf,۾ЫW/c@NE6;s}zAn& \,Lnq5W~_! V ;Q`OKt4a%i惩ƣ8*: ztn w~W@6##ŀq8hٲ%~/^Dk… v*$$r qcߘ`?|},^jƍ,ӨAΆ?NZq;~ځtݭ۴Ƣ… Y&.'^2v+Ym(.,MjzSgr.@nۺGJѺMl۶w|ɓqc?+C]Mpеk|>{6ddwƲ+@޽8p0""##T᚛Sv^dxd#a ;qŗJ%G#d+w#sU*©A6'oAP4o^¬W_śoc8(gaO?a1֭Kbh`ӫX<; hժ%֮))p)䔰cJPQpw- \Eկ^z}Os+ y&'/?6+(*.=NC(*nnׄHû5k9O:9=|RQyD| !Q'@եO:tQy8^2=٭EK QJbYA!<0_mcxvũS >Ŕ Sz%i.vlߎʕ*2 x1ލݰAn+ԩԗݯ*[Xfym{;Prn^:Crظ~=A\:w빲!"I qBu?CпOodggsq`8w,W]wa̙ B>PPW22ʬYh0cFym QȨHG}]'~MS<,F+g zx}6m J$l~JmR>ᡌgZ(6C#G.Rxee—. :i!Y s!::Gb*gcwߣJJ|#{\ɭHG ~FLt> F?B` 2F2l ^5Hsd3xDII|Ԋ!b. Qq(284j2'Cփ"Zc7 pexҰR劸~/6ܼq 0[mp\O1aDحo˖-~}{h۶-~=:xPP4GmP~pA{-" O &N "(r ,}lV,3N'-̈́~n4=Ŗ۸?~@ad^ȌS-< <]xGUSA "nqIr )mACKr>kA~<{s+. /7k}ϳzYkWpbcm+V\#]X^a3!u`6}xշP~=>u ߊf>0q#GQ)"2 > زA!? @B1?S]VV<>'^O# ouB!"NVd<[J&.e$C8/zf3@6i/b6E\pf~꫘lQ\8}4hL25ۤaCu:\2U%Dxl Fq%جVB`XvW_/>͂?qi1}'{%o~σ֣}R;we ϋIT/<ǏdB a?"91իU^gY ^{G`(,,DݺxiJ$'+0۱9 L~ /#͔$m\RI&) |rРNtu'hxIgؿw$av'@An޹s&"kwnc;@?Y Mso^9]?DQa$lmP)J56t< CX&$~r! AA ALXv4iU*W) 4o'b.+uʼCiӂO^R,hp)$(ʔVGG#:|!_q'C< St 3TX@prp@BpQlRcjҨ!28] ٬B @HH(,H@@ .ٙ"(-!p8p:C \7^ppHC@A@=af0b6 dL'F|qA=pAx=ʻpUX*IXWVNv;]!'+ 5+f"8$o˗:uj*_+#yzPѩ6Ye8m{AWPZ?(HE &>u~v}2njڨwa|pN4ghs#pi aCֿPz5mvN7m=~\*SAG ]x,yKouI~m*˂PI 8c׎h4{V)~QQQrrrp ܾ}(r8#2* *T@ʕ>u*#_A GyDV!T~XH0P`i (^rKl*&@ >nwP&qO˜N'6\.FDFqymä)P`94*y,P2)n Zy{ k=lၦYlA0Zh8yN .wQaS<=}֫Y.dj}aGPPP12<08ŐcꃢG{?V!< Y\ B]-7GGt=u!j1W.|t~YAI[]IO&ٟ 22 8 ŕ18/ 4QI:|'4۴vԮ퓫ސNCpp0w2rC4i2m0KmALI?]O޽D-L OIN"K(@_H&[A(H D;w`BnDe-:[2ZKozKa:~Tq#_eK  T  i I seWT>ÂTf25b<ޑP,-8R[P!I ds0ت/oepSY(֧taEdq5i-tqN:oq)/o t`CFloМxn T ïT(D՚oK{^q?~ILRZfֻRiT]q]h ڇ4ڤuŁ5PUJע/0W< IDAT]hu:JO5EP\+˩>?ْ/َZe(y,H̽- r;Q> 78gA^ r%GA^F1jKFFKYx-,L D_WOx2$>J9 -$Bi[48JZإ^mQ}x[,ْ<`ޮDse@$|̥BADv5>=r,&R{Qv1A\.󅷢"j~PPQ'pN*o&p6N NW'KXADK*~f\ʭ\tMŕ&@U SLh TxTF?ŗ3ewVC+"KY¨5 /rl6^&Y[ q++P"9./EN2~{+O*#AJU"$4cpu8$CuWTdTGʕW"nߺI'N8< #{;}e'!IԱԃz| KuguO!S×8Z !D+\mYpsI~)\-?хp^Jk*ϊ9[ǟ9ċ@ĥȩ_jf=H$k se~olbtk*F𻂏R2!Ӆd*@A>gz<^Ν=^/x-8z=U 6ȁwPw5Ņ? vqBBC6Mo28C{?<ɋ/T<C!IM/)T$y)BK1:OO(28n(|95 fA ,?퐓cP4ZËe)TxIT,bJ&q2z< %n^!嗭!K*D/vyG0x//rz)//UVCa~lv.Qd UVC^^D? pSOm6p:P qE< ',̀m? w8غ};BqܱF2z4=١C qLNPBͼfbUvI_M^Xn'{"efK BjbY%6xI*'BN z@@oqRlT*V!?) deZ R0J5PzLء+W I'VJ4~66ڱ`Mgy Xi Ӊ ampfEXXsrt:9 } "a2bihҴZlg̀lBTAz='/ÍCqncG!#çJe!ޠ6&y{wI=MELt4+nk lk$~ LI(%nA&]͢b,w(ze>J$'rX܄Nʯ{(lR~('zq+Mmd͌$ LrG?5[t{.}_l˷N nC9~<r`X..&B#þlX`Xل3gsE\aXN>00P`' E^ ꂜշ(-_A%-JF:AXb%~?0j*Ca5!@'һq\NB??KY.U]qCwç^zP,U( ՝:u={ѧOFQQoߎύ0كN:uNAj x'33oxyLCߔqv, ^{u4j.B8P? +>iRځʛu/ fBn1}thYL@uGF>}?#C yɸs:vJ6A}v C}_^n.^utcnj3gdq]L6x/M} oҤo]1l8vQ/ıcۓK@\ .!H퐊Ls<[6oAjj*oΥM&dffcq!%9p1/z'm݆޽{wyϼF0zBƾ}½wԩֵ+"n߼lt& ftsąK / 91 Az/WSNY<{.L.`ɸu&@\(Lضu+z==y? 2н[wBо];|WiCЭKWlݲS;֭pIKzo))ɸ˗.y]/^Bvv6RSRBOq޽:u*RSR0i$ ;+ )I0lF玝tl*DrR@{+%:~ÁJ*_~mbcbQdsOiukػgN!hۦ6&0L:u*v;8qBP6J[W֭K.!-- ;uN=mZsƏ$=7R>{*)L&L”D > /[nMj\Bp?III;f n޸B6:b֥֭ :w숭[`uڹ3tm[ru~A <~ǁqa$m6jW .¬<HNJ¸cqMfLХS'lټݺtA.m6,\ad;wǎ ƽ 7SNغe+'SXXcGS 'Ƌn]b-ѭ;w톟n˗1|0t {e sO޹ɓ&cjL?nD,&l21ٌ.;lfAɄ)¯!%)Ç ñGycx9,w]@LL |' d'r;Ccj yWBӦM1o<hР8 ##V{ҷ$$$ &&FR .}vf^GU_Y͚5üy@|l6… HO_iӦav`Vv:ߜ9sUV'a=&n u+6AAAݫn*ٲu+z}ރPVMlܴ 0 \7X,mS2аaCPli[L֐~ }33<$s9sk0A8W0":TU+WbpZt:$[}&OƎ;0|| X8K 嗳ѰQClڼ [4ǜ/ =L&<V̗g믾ۈt<(W<7i{ܹZ>cq?dۇƍ\x^en2bʕXj\$''cժ8wsEÄX~=5o~ӄ߿14## l?o~F<7_|*TO$xO>Xh.\Қ@~_غu 9 ڣEcK;vxC_~%j|k֮EX7 ;w._Q#1w5<,Yr̝RRNf{ve>?lڴÆ ܹ_)m~!->^`` V+.^˗sNX`_xP( 8!G!((.z##2""((10ٳa6[`/PcLAAyn8ty/fFjjKtQph#GbР ÀqA_TkIlɓ'ѳg/ tݯM t:8 <O?4n߾ϸ~K 4aa8` F<pYxx8'`bbb{g7k^Dci!ݛzӧOlӃLn͛oBTt,+( p@ a6YPjuyOذi3BCBQjuLظn@fǦ a0vx"&|_}&LՂCa;qqqs.*W,[|DGG v? b6}W_ɄhWg1{7_XA pq4mǏGc& ={䮃@@ lCX`ƌق{E(om !ixwѱSG,?f}^XXHQQ+FI9yӦ͸u7nDZZ>溰Qێvhu׮PnMle۸iC >W5::Lj@׳}e=r=/_fcHE]h#UTW9_΁lݻ1fX:waqQ+RaHlcƽA;a(??h#֣1()9 | 3:yaqZ8x L࣏>c">>SNAM\4>R;bE>}:oS|/^q#../NyMy6ܹ!бctȼ{ li;׿O q䭏}``6Lbǧ {=,Y&LD||<&MH}$F# 22fo6>hd0Pz \z3WT իU`ʯZ!!!hڬ9W`۷nTyb}5&&-SBɹ/6A{eWttQ\9FfB+VČ1}4ر-KW xJ/V 4o˗3ۧhܸ/CfuFdgg 2Fc 1VZŨWTڵk@ڵ9'aÆN :DbDd0ݻ*b>͋9ڏ䍏 H0 E e$HJrrqYypxT/ԺukX6 [l/jT^=l޼ k8 IDAT7o~qG~:G0X|Ґڴi˗;w¨Q<2hk֬jŊ+ѢEsM&O~;wDJ k&R)mKSNr*tM ֵkW,_pi~zش~[lիWj`hѢ%3OU۷OĚ5,W@aɒO%KormժV^ՊWe˖x_nuņ Ph2aJ=>b{vrViKӗb` 2u DlWZrIIXrRSSe4nۡa ڵk18m0wޠAq,f 6nȴҾr͠F5WV8|wa=0LXx1;&0;bƆ ,-{6brX,f,]ZR,C0 xի!2"> -=0e(W]"%KЪU+MĿQWX шW:^ ;SPW[cXH_rE[s(w٥K,K_=atK,A.Ex<(oР֯_Ō 7@qUVXr+/c #55VBbb"OWb)1<Ǎǯ Éʕ*sOtU> 4h ֭[A$lFz2I '6:)gf͘>K/FNv6NѤIcNM6X|l66oތO(r8@׮QT+:խ[6mlkXc3y$>Nƍa6~:4lذT,m `@ QԣB0 Nq7S?dGϣ_XrF<7ӘGm]9cFc1dPԮSpOk6FO?ŰaQv- ]1ck~Xf̘![}= '8s5z~-zx)Nݰ=Ziիif-[Zjh٪6^"=;۷Pn}=f4Ν;1ckNOkڵoӉveY&׿bJϿ<4sGLgϝŘcbU2v;7lĐ!h.K1؞49E~cϞ=׿?\"9;vΝ=e1rHIp:x};:=N'9e. .đLIIEƍ*yOm۶o߾S.$b6JMk֮aSuk׮ ƏÇ}v:0 p\?q"n݊>nz.iǏggN ,XfgϽڵ+֬] Bq?BP--ǟ|i/MCg{Eq|[Ϝ z1uT|4i}ÈAXӧMG$u5nx?~x?AMA^ ^'ڢFhޢeV^:Z;" zh|'HZkm#GbѢEڭ>Cxc#$'PG<6Μ5y#GB.]1r(L&ьӧO_X=?@n]Ѽys,K_ ri)'K 6I'aaS`K jQt# *`K+q/#^bO܏$AB0%~ 8y/t͚5eQA}.2BW4*QMG~ $@Ѓ=/b*1ŧ3=_ ++ u+[ {j  M^lvbvh=TZ"Vooy4-eV4:hbvq"}{ov-L(f`=o74e!z/;^u s,ԮS3ge>VRuw &mU*Tԯ \ӡ;]ރKWۯ!/g` zJWJ}}7@BjBуN'޽UIRFeTTRV&H:꩘|$D(pu] @Bh%V3;Z;*oceIm'l)%Ư.%򠪫[4 y{P0le, Dn2[m~p [hySi]— R$Rϔ;|_m(D~%Jc+J( ,@"0me* ʨ|%&p(ke&%P§H;ТS&ɛ:P"m(%"Zx` /WOڊm$L@Yq!6VN'n\Uj!2zt^Nׯ]C޺u ǎCaA|4o*V[VߊPI R8>BnٺQ>[J ׮^Ajթƍ8zwDFFE˖XRj+U:zį7"y&9%hѼ*PuM;_]͛7G4]pu5k֜#!nݺcǎuuy3Kϝ: <u\ϔ.^Μ)A]ggϖg u+I]_#5/ nc~Ѹ6Ǿ0,RTT"#5[r5 10"ŘDGL6 YYٰ% !@tT"p8p]8NDGG#88DDyf#//AAzC49^TV!`>8R߿8_yo`-O/XkBVGJYJ\8N\zUUcJAKãuOoQf+SJu y~ F"#J38'ׯ ,-Zt -N91:0_8} P콷nm鼳CvN"ѨQcϞtBr`pU?wQQRώw圯N=E횵>x2Z'سk7\pzMc0>bxZ{eӯu\}yeiˁ}{ԛ:[~I۟~^FW0KjL1uP?X/ős\l۾2njY?E fT'۷`_0u}Dr*Za3ut|nsA-[0teddea.-Uŝ EqX`VJhcAh h$*KZ .RR.D iE1h IZMd{б#4iF4aFl*Ӂ ݿ/> h ;ȨCWZ_A1/yi4Z-z I: 15h ֯__kaIw)i嗣5Tn =JѾ7!tpQ)*.f о.{H-(jp8bh{EQرs7mQ:.vFe~߲ݻѩcw{  q` 3 ׎pN_iesb;܄ fԀ шn¡* K.UCc!E8x`R> p0[Ԕdw֕QD&dENظwdq2@h!'o)@~~:i8((yบW&6lD1!@Qhk.vꇨWdLMg{w0QvEa͚qlt^_%ִth4GHJ?g^F##FiӦL&UWn'//~UאCVC}ιG8g0v\[Ԭ߭Mfså ~)k А@+Mq .Ter "dm5 ЊZEWw(//W A@PJ(APn'!!]ΧмԼ--+OzGp^F7LA HKp\HTgс8Չ_t#⁈FAvNف= B]b"9R[:>vٶo߁j6m$ ՊjUbbb\m洡%ضm[/жm[-z/LM~FGhzO6m6lhMAgeez*W,^THDEKCn^> rNی)p55OCvٳ[oDfxGxi58 Y~yW޽iiiucj8#k?oZ7އB9fgۺ22e8}Z>ʱw<, (km29+ vu߾kL&?-ݮg3k/TnNF#Ā٬f -ZTݱm_QRET"`pjq0Ms__ EMT^/@u`k5iq$ Ҁը$,K*ō7ңG/!\AŠ4MMc!7aCe$**K]\9}Ds_ر6m0tPRRR0 2M6M6|رT.8]Nۻ m K"ʇp[XԃZa}tdpP7W-Ǵ N=/ M-?­Ԥ+Xmť2H=a%'eŸ$_!VhD$4Z-  Mo7eV$g(!Ƞ+wb$2 V h" $#I, [a.-U 9DE=)ۿ 2ɡhDJLb:@5dbŊHj9&\Ct,nBl6cXqh4JfDA$.. v Q'!컘iסrѰFQdx $q<'#F0` ֭}z=9 ':*={v3h`AgnuӭT#Bq{nqDջ)0hja4Bx?Ȩ/gyw񦛰l̘1I'ҴzHϒÏ/r~3ܮ Svh<2&qh)3dĥddfa.) Ą U ,y 1D'пz5[d7ljĉ"Ah/E]^SROfѪ`$Ai?%*܂sw5?W'T.4ig;Aw7Wp;j; ƪB 1FV;hjJE pM7d:>Ǝ띷4 [lK.Ȳp҆:ъ K64HAAQ$'cXp: :O{6:}^2iR3FÇdBy=Ċ+k1ck֭Z nsmJ PBPn} QQQ[}M杸~~ǵ9w{HU8v#)z5r_dsǚ*L' YXIXZn*ϭBx0;a*M_zb#tX4F#7܊ƤImiLҞ=t]0\&Mj3n$* x`4唖zŲ{vUN%ڴh8رs9f͚b x (umV|"[oUQTT4u-4x&mt[fv{'\wbL@+mB!݄ݮ*˹ٗ{`ؼbB2$UdHrqp:%l֭WYYQ9]8%UAئ]*1 P{qdeEe :Cm;ط21ŷiP\8$4Iaְb6IOKC!eldlf4kIRyUvVFqVPq2-߿nCx˗ <<;f k8VLʹ_|oױ^uEϞ=rޢgϋ 2+ IBtؘhb(,-{\ `.eŊo={6w}7ݻwGRRRxgiݲW\~yo̶DCfqogUޢ)biqbKC}r}YqN<dY@n)(/Ɨ_~ cbڵ,X0n~k5 :ǫ\qmBD *j$&W*Kwymk6T7) i 1/jìYt|.rV^V̞=>Xʍ7\_\ U3;" =YHӓ<99 4I*J&O=D3=~L),k-V*;䓍@g8vHTTFtII=!8ReT28r{ĔcxGV@{5AB%pA>s^lq70,{ tdffVh`05 ..&î2~Z: Ƥ+P$Eb NڝӖI{KpӪez{6 Y*t"2:tuFv9s&|LEY9v-[46޹k''K\ҿe5ԥ-Tp< p ? ϫ>ʗriZy#\Yʲ ZSr=74HQ駟> DG#RDT.7./ŢU?"( .GBqp%ؖ( Ij~aXIh͊dRu܉#Orrz 뺓-B m YO)/J)SXq}5! ?ѯ_2ϟnK?x9:%S^f.}]U9>Yvy, ll&&&|Wߊr2&x]+CLZ~m^~UyThh4܎;$T+F [NAEff<'F?Ep:cP!-9ls6ߏF2AGaР,\Xsnؠ:zEp5@/)UpP=A ^#bJG/pY Z|9 PY*hd([mFCG "˖g֬Y{̜ߗh;w.SN` MbU#tC_eeڟTx}׃@ԩSPp6:U8ά|gs9̜91ڃХKΝjez|)-k Lt$$~}pj,bh2Rnn.~",'',$IbTh:yYQ7 遇ܜ((2(NYN͛AС8N%IMQE ݷgm} SdO"l/5+X& "bQd':¼q7QUo^OfVQg0|߰uXR]hO3×_,gԕW67¤Ga6R`*}&Nw9}Ͻج6G~^T}qv={h|Ûo̤bsh2&cƜR4*Z.hߌÇawjYp#/3|r0ۼ"Ic^͂[oexMnmR/N rrrjQ-4f'RD_ⰃC@ʊ8晐 "-,WT4!**rs(+؊$I8 r|sIR$ +!U }m6p8T%\TKIڞΚ=U UQ:J*AL,h UgH){̨D` ǯv#:qv#?SN%1Rĉ^}}߾#X^qw%* M+@dl5N?)kWTOd;X :ϖe˸^=2e ˖-crɍbOURRVp J4Tc*@[>68g$jUiK̤8tJ,˔( ֭Z}Rp0WXSUӓ[ʽy(Xl $K8$ Ӂ,+TTSRRr%*,\N'.IBӡiO`o<\@ԀP hQlv AdR X]=̇_ת8\_!!{W!י(lтni{}1a$8Ӧyß6?EMYp>/{yȐp9_;tt.c2L, |={K.aڹq w1*vgf0~ܵDU2}8еq""b R ]@Pp4|c(Ҽ `O@/fkFY F,Ò%c*D haDP\0udNUT_'i[<@5!CWE'T? H@u]'%RCkesJU= ^w:T݆P-/icc#@iiWaMʥׅZ֪ϕE`qA P DB])SXNǢOHd̙L<_?$CR`ӧ7{g9r;4zRAAfb׹3}>89%)- 5@j!D !wΘZqTT_ ~<V+08Rq ,SRnQM*%CRGp$|( A8!m2HKmFA~ZɅ]tT8c^ʰTXTouط{'m۫۶;ky"t^8^u2xɩk^wz+CqQ =F/p4/Fqn8vCJk(z>l%m)?q&[/)wE\x̞3A=b0uN߆xb~yy9eee>|iӦry0kvIii)e IB8`N 4 BάÈYT VB1 Wv/.nNzQ,(.6 }2MQTj%DQFG\v\.ɿrto\VXR\IJp1jM))I%(v[F^Ũ{vazkbO.)(PTT3R(pHG%.˗sKxrs=ޢA@6 FYf++"J6XjU_^u] ( ,_軌*xR$KrjmX˯|w_"..4DQѣ`4}(]@8}.`4hGnuz۽{߶}Gމ0x)9n_{5*m\ >\.E HNJ`1@l8EGҠp2}QW~h'tMxرcQeu;q{󜄜8qbdYn/ ju=}wU$!66 bkҩӹXmVvIzXbcc!dh4Չh8sg|A`?mۚNڶm]wa#PGnj$&IG MSRuH6;S2bZ߹uV~- xI6]+F!1FFWKQ$Da@/+85 k\:'J ól'INV R iz:ш:Z ;MIJ$I&9!(*q cצp9*P'4ŶѢPE @+T?ub%IreYx;N\.p!Rb (~ NKRTNC^ @q) "(paf (43V+ ^;PSaGkQEDL@Miٲz*UKɦ[ #$0"yE:y9γf;]0>r2Z:~I[h$m;I IDATn!P[c;dYЏUZBffFXhːmk@~CrRÝFj9X$*"66F!"hh 03/e;]e^YuU[giP G+EULl6[}JfѺuM7NN8w+j^g^^\+)G_TUʠ4(ج6 UEV#%%шc4qɫ1pVɃ $'ZέNWl8ʤXDp`07Tu0; ŭ|oYq5ArD dWq ݆bE8 W$ƐWmPIqܒ$IdMFADADAx|Dmq֭[[o1Ltޝ~f8]t7ߤw>5ߺu+N[ԲտKbb">'N&Mq<^rڧϟ)8V.[nI$r IR JӧOy7b~c233yj,]+{d zo=ao={KU=ͭW^tCE]`a+]Э˅|'N`AT\S1o= >{ްrxIFO/**'c6^ȑ݇#`cǎ#rWqwk.oX6ilXx &q7ѯ_?t:)))uם<8;p0v5,Y>Fয়~b[+x"..0NGRRwy'jx.*3III8FA?~{>1Sω'>|-SO=ŨQλسgOVZ5\ױf͏at=.;W_=5k0~u\s͵^+uCz@k|,<ob֜9<3O?=ԩYh˖-cɒ8agO=$g>3g7|&A AX\W_k׮|2r>M[{5v:BZZsAFF:x }z~uZ-VE裏@Q9c&c|٧\wx̙}oС=;ojb4@Q0 X**῎aP5,5h@k֬êlOVbp-\=…o5InO>fܵ0cLP4f= /OU.gĉ̙=}zK{߽q r ?"jZȒĪիj]fCQT\Ѓp8z~_1s͚5̌ -ڳ޵}`[cVILL,G`Uf^y^k1)),d21㿯|~^N#&:^={?T /ݻqqAuo)J>w^.wq`ث=g6BQddYb֬Y;oڧ߇-4yb̘||l6>sƌ ׁ]އ {Ƅk[&== EQHJJ"#3oڍbKה;z(w7n]GSe$k(M~~xO9"ؼ#Ica}zk|,eVlڸF֭q8-[ء7,X4jjVشa= 66$ IKK%{i,]{Jrrwq'PQP͕LDQӐܛhaN NCԣ 2`@=ʪUyyʉ...h?&;;V~b  uge<3̝;7Ό38tvi7zod>LBBf&t!** Ftt4VzSɮGZZW]u%|IHOo޽{0ztUs$&&R^^Ryty& 4)&L.>'ׯ 0p(u,fc̘1w0q"[m"7o)4vF#6 So}q!D:9 x7' \3'xΝO?c竄 =z/3JLW ]t!!>:&]zcdB\\^ L 9FNhy'm}`aCcaڎ/"_|5fFzé}Q믿 *-6 N'z[_zҥ:}Q-Žs뭓x /67E1yOСC{%++St0J5k֌Gr8z(͚5wXCXҥK)..z}TyвeKZh?&>>^/D%Vʗ_~ENjM/,Kxb:u}޺ukZlaw%!!#5oZQ}wλj7\tޝ?η~mhjՊoKFF)ɷ:phw\svHR)EhEzҫ"WA@PKqwIGlMI.Ny<;̦N׺t?V^ݺbƴrHHH̙3QJ ]kκb֭xܹs;v,*xfO۷oS)Ro`6h{'0x;kׅ\}Wp/XHmSS1ut{6kĘ2yW~,f9`sw>J.-ǚ5k1h@ǨQWn>,T*Ξ=+ !>|g`ah9sb:QծJ'G+VDjݻݺ1|W߾}r*v_T\ v|;wҥ0jH|T?~cǎ!##tÇqa罙쑒2W\Ő!Cꫝ0x;X)S0bH<ر 1VP{Ʊc0`@ I7iM47CϞ=q]̙33|p5 %N֭+zc#' *W'YѶ+G~}qrx'݇zKR[K6/С/I[J֌0?=rnOSǎxn3.?ɓb,^_(7'-- K.۷Q3;v, . Ne޼pjժI&ZbԨQXp!?Vz %%扽;cƤC+lVlݺKՉT.`, st:ܾRKn3hа BCvZ#a/o*phR[~A?}6̛;>w6p藲 t:O̝;&MD:u,{p^.x꺐܆A8 g8]lܖT*_O 4<!~t#44 !8yJr䘽 ӟx_?ym@r7_͑~:uƂ h$r(˅Ү_]BiwDpg* Qeܖ_\NKgQn=~؟~?u jU*WF!o [nT鲨X/p:OBRz\Oܾse˔ׁ\( BwNO^RR@\b_8W周zN;oT2P$^ ܼuw ؘ,Ue˔C\\On|{ŋxRG7`{zfg;xp>#-W:PB ~7;"ڇ.E,p-o|nT*bK Ga{ûBdDcbSDDT*>DfFOd$Z5 @ ^. _@i"*]ҷD*on(8z/PT@Dxx@)0sXX8詖ׁR\[H ,fj E{~H7)" tAJ7gݾ} ݖ AAA%`w*m   Ff5GVP(<õAAAȄq,^   §FAAAlh,.2 M0mhi|Ņe|.oZg[Zd͘BpxX7Κa-X4nFXpE޼&Kɲ`4a51ek 2`xuL\F0f2lֱXOMsdc 5fj/cɬbLzb>SikNX12 ΟeLPS*~<c|caXC^eo!`\5S_uyfredp,crr1sV[)*p]t_Qq80ig>#ksߠXL1웰[0^Dt“]|}  d!$0p=& !$$n{`|0f<œ>3esI_{хu">$o!  `5_` Ĩ#B18N_=hP9> ~3cg;~2 <4뼈}Aa'ɯP(< zEz-haȊa^:$BdHzl wU"NÂQ1q  8dJĨD $ı+oOm̧?s0|ͩMuAiS _|)c͗z k&f/y/^ Ƌu_DjP1֮Qpzi@:EݐVqXGzܽ}%KZ. rR]U=)7XFHWAaM(9qPAM[o΃;(WU+U{|W\Cdx$&0`Ɏ%˲Z*J,?xVTu*Cߵ+{p]/J+ʭS ut0Ю} B͚`(Y°#۷_꥗)ٳP?)܅ 0n>h^>mpYddd <<իWG߷re߰}uDFFsϡdJ#Eڵt~EšvZп?-t~S1wdrT]_H믏b>lڵ{šLrYt ~LAN#d9PS?7ݐO{ۧOz|5vv.ډwo`ⲉөBBq@гzyW(Gp><|,$,qva=C8`>ҥKx"@ҥ꥗Q>"&F_ ^=ݟ8`^^fAFXĉ_W_cʢ2q<.Z#FQHO޽{1jHIuӉ\%p.RȲ,222gdR,X0n޺3iݦq}d(Zw<_y@b.^:+|޽;Fڍ4|xC<~=Ĺ6tNbDGGk.ػr7G.RjQD Ohy>ɓGAAü_E  ,,Uhժh:ӽx^|Ŋ; Sΐ}FNkn|Μ96Ν;(^8<IIXlΜ9e`ȑVMZW@УGwQ䪋 ..=ztG>o>Kp%TTƍEmQbEܿw>}z,zJ 7`ㆍPk48p[庒ŀ1dP_^: ᾏIkW{mRPP7{aC`t4h 6o e1x@ t7c±8I[4;fR0wsFux^{ f&'WOV㛍]gϧa6RоC{̝;M6J˲`LJ76_|ü7o'N@ףRŊ(W션 W^ŰCE%[DHHrssgؽ /ubغu `-P&NOcXjEO<9sxcGؿ3ԬYۺm1ct|^l>&N`PYfi>Rp5lݺN-[ w+%sNPTҥ >la8c6z&O>ŕWe&7^[oݺBe#V~Nn.^&XT]kHHHz 1#i:z$,h8hs OYڧPğ(Yz&_؄v.8/o귱Hg7MiA٬Y+(0Q$0 *U ?~^KAeQdIЦMkC-ko?bMk޽{#&:ZT ŊQNajWjtu[v-EXboaՈG&MдI/fΜ]M~||̙X*DDDc1$9pRNk_Cdd$4i ]iq:ded.dee!::&#Dn&cx ,뿮jѽkDA( @XtQ tiW%b{~$&&E6qt˖-Nj/~Ä 0 7o?s} _P8>>Dɒ%E){O\\sزe+n޼O,=yDtP8~x>}bŊI.Jf233AFfys}ȅzJ1vP`EvѣѹKW.]:;]6i4CƢbWI;a׮]4iX '~bdggcܹ믑ƍwER|-bz*fΜSNeY$%%a̙gEaa ]s}Mxp'bUڜ|hsϯ_b7ar>)>uo6qVyXrd:|7n޴sRMzt/z~ N?*UFٳ`ѢO=]{ԭ[~=ztQnq)۷xbngqR'u&3grY)SQQQ8s,֭… ĉW Zǵ֫3m"XkKwU?4 =m^2Gwy:tc0i$ocǎȑ#Ѷm[5j^z%z)9RZ!!!h׮O:yKyɥ'hK\+Puıg$&&_~q9@֑,;(QGc=.aE1w\@800 ^yYcj Qn͚5áCЬY35jn݆aÆ#++ QQQhܸ$*VLɓ۱r*DEE~:trbHM(9 `طo/aѢ-.]#G oڠAcy;w=}-[o@bb"Ǝk7̙bƌΝT3XqDΤ=|0Zl!e8r0֭3 $ , alL!ҝ}Տ=͵kH6)vСCt 4@NNm/VPPP8lڴ {ѣG #7@vU&wTqt:3ؾ}{d dzlٲÇ?233Rо}{?hҤ ͛'ٕZi:2 p'_طGeGa:t? 9ǒAH'O|Aujz'e1n8ԬYJ+!!ŊÎ;$eσ 6cuÆ C~~>6l؀_~\ՓuĹ_lk֬ADDy:rRRRpI~@g UV !!!8|0=ٳg}XfSpZGySL\a7fG Z׬8O,7}i 87`J*yyy7n7n^z˗c0RYrUg9pƎ N3f8e4X##HKKÞ={0i$96mr)@ѦMжm[DEEWŋ7xgvQI:DIJ8x  |;3<}GݾA?xa'nݺٳ.sNoߞ L]p ;jׯG~\OOJZF.]?KyN:[,MHH 'u$+۬+MG4=9\c}/fep'A~"Qz lIAHǕg={6:tnݺ9ݻcĈh߾=ߏUmr/40uThZ\ҭ@>=)MG;wk.ޗgu)@0w;wqSzÍkʡ2{՞1\D/¨ !;7Os, CTD$ K{o/h""_/XF0?wTzŹNԩS:u*\+bSGnYBw͟ozRO̚5 NBhh(ׯ3gBkH,/u?3f6m`ԩݻ#fe EM7#/ eʖukO_%q:j>["A=B3<4ʕ+III8|lS.Ii:Q>cYTəW F.!0G:I':!*99([| ŊǍסV ם >ƃ?Ap范FRr*ERn  BfD8\m߁J+RFq%- * rk[їbA8VvҬ,qi,X㿆 0|c y)YDƜY`LyNfY ,fMBYlx,B& ˚1$g&Y3Le;ecō3+W˚6/}40 a Q30[d :M\c7x|5ưgqX 518A9|z61łO lҏVfa̲?c X5gu>~aԯ +(Op`ؾLN5O99}5 OR3Ff}7+m,pB7jAor}Fz ~6qmo䶲kdXYPIʁ6 Lv?˙VlC gKg󲌙>03Y>IˇizZ?`[g'viFxىiWp˯bth ʗ/e+s hӪ߸@_@:dJ$ի@]p%7nFr>سwzz/kΏѽ[W)0[絲 \fXfEA`|va0Ln:܌ñѽk(AAAʗ-ڵŖmѵӫT'AբZժ|Ç_Obİ!r!UTZiW|rx-of؉(K?\p;wrW9k.b,m#61n-$,[a&@A8Dž˓pHOvj4P`YժU0(((@bb50 V˧h!Iv6mڸ>jTOĩӧѱ};] cź|-]Fy&lź^aT`YA H'tZaa;V:fpxt;ݺ} &:/(@Dx8bccQhQ8~ U՘Åk=iՑr InCŸJ|۶i>ٍ\:!?#i(J8ѢU+ܸz-ZqClW#-£G&@VGzz  D%#!Iի۾ޅNvpp>\Ԯma`nEqܜ\z>0 "~Uʼnل-\agD} H'qU^ѢK {wjڂd?B=G͈؎5AA40&I 8,*9.\1:YHG =9"P\2# -޿"&qDLllsLOGNn`΢A޻I/X.0Fj.R97oZ + l &"A,!I$*ɇ14I2 yK7{)cؔeh0of)eW`Nxґ4HOW%^eYTRa,Z횄A2޻IsƂk;+\v2vMؼd* } :! lgrsrq)T@65d'vG *Av|rX=C˯'1b@rv*U+WQl9DEFʭ6QR! -0u`䨛SœdE^bMաPovÑkR^;r;W`hzt:hZtx pC^BhUl^J ^ב8pCzz}ÐPZ˲V*AAAahZ>]ń '|iFtgQ=NFhMAO@ֻً>ݻvFR,31u3;`1|X~A"E)"ANc䨟Rp&Y P[?{#fXZ'oK_opTuܖRMut:-o0SBMI3`>!Fb>uDmZD-46@ x׮V͚(T(  *\3& ~L%c!`H9ݓ=!&Xl@vU rȻk 7`1 (29C(e+p9B3qU6e^E<1cP]+]>WT~H5ޝVޝ! o>&8#H zRDϏ3 ={tGhh[rAJkXGw>&;7`?Dy rz 2/!5}˖Cڌ d:Y9ӵ @0n9F?cj̜2k99<û]ϳ.83歶~+;iuULn_&+f'q=oyU P"vsM3 w slƲ=.|;}jd>e hG5g޾ԃ9Cj9jm^9Dp$WbV}t}"72rY#$=fy(wT\=#ߎl/eq8 qW"6.L^t_AA#EZYGr"Oۑ|λHUIri'瀸ޜaڜUs4m*Ԭ|\o`U6`&99uGF\ A@n8/=ua4gHi#y^Sg;7tWGGxzpR]y\sy ̔p*()-792`/Aj1uJZ)ui(srH?ae=fRnԋ)׆u}?Dm$*EFF"&66`9zd, ALΙ6 VxD1lι̝>ZZ>웞);\\vUIC|c4ZF"EsE! @ {nl9er̛1'e E m;whLܥ2wUۆ9@,Y Y FJPZ[_ $8\L\ye$Uj7cmL\{+Pz֘ZA\p2ѺΛ1Fr^]н3eɩ'O꫾-]!Ř6q]ǎ0>v~nePA?@r>+I@,G0,(_@( =2LJ> 9Ro\#`Ҍn`Xq׹lyG^=)w]-vn޼"mӊd {\57b݆Mq&U?a͸cǿŊs`KW7~;8wo܌||'.D} ^qW_TI9 @ M7\ ^qAʇ| %RODJJ_>$t`Yz;f?] WGܷ PlYԮ,lێ^Ezؼ}z# +WiWZ*Hr˖CTd $ٽ?@˧,`4jhfXZA 9D9XQa%50q,5aY߼e1]8!==>aH@VeYTV à0 Z-bBl>۴q#|3ԨSOcvZ`n40">Wf<\P 3aPyyDh_h8yUR } c7-Ƣ=*99\) Rڇ# o]`ĸ8bx 4fL3"?soݾw"&:/(ZFll,-'C|\bbb8Zg+f4fE>" !?6yOl 7TD K/)׶;ߗ$2o4qw;j< _=vǤ#pϿ}- b}ڥ+2p %SL?]u7wt:Ẍ́iEK9 g: )u[6_U DԺy]ȁr+U~t:t:m}-/N8/Ο>ݺtNCV-l*>nƍqиQ#>P]Y% ZX*`TlSښ#P(20j`X4m?`hXw;ݎ ` ܸrM(wo=?q=>G6,ZZ>K]:`˒5>vR 8 aau Ǡ#v2ze a/w?n<R>.r抬r'wl>k9]] p`5 zv}Fv@;!FRЪEst:L4o!!!s$X"KĉoBbuiԼ~9MA*q+_FQhx[int:>\YP&apR۵M[O6lM(W)$bS`s Y2wE )ao(: zȍRoO?~Ȩ(VG5W i]׻|L"z~ܘQ٣;BCC*Ǖ^}9`0aidT䘝SfD`0a6 ,TY2$3 qm>.ZٙO<":=n_7װE2i]D՚m…Rt/|5iƵ:WzMĉïi0w XENV_A_tx+#Ѿ{gI.o7Ҟ2zBgj9uĒ=rF.}!X\ P1*jh4(Z=,fG񒥑@@QCŨl)n=H. :k`ۥa&+pXz :4ӀPY2B0 <`e Dу(QExtzqO䔡PőL YW8$5iu {v3~t|nL}'EQγ(d bhW 7SB"uOLe'=t$Lo.DtC/~7E^mqt"-MgT tZ- 2[x,3u8nj]*Uwp]T^&NR%0p `ǣDZfR{`~O 6>YY*TH0Nl|l_r+8u/R:9X62L'kc&͓43iúc8H]ppsx~& gԝPR!$DAӂQ0ZehHƗ1s:hZyi+R4;X4B@th8YG2x&]:<Ņ iu rs4'_vwν_.S?Ӝ\|>-o -o݁@ ϳsVK-Qq_4L7buI1q3c ZelXT|2}8t(^y^*OsC_ R@Ib>=C_cnJ*(r YyJ@=ܭ30 Et(D#.&q11< *P! UÇWXҞ/| c$AÆCFhaqXxv7lS!ki l< @/WM(Kf㓍c&BZ50el*Wc@ol^jҳ˲` 3}h#~2߁eYakGQL)TCOT^ %˥wc,aג{a X;o),t Cي0{_)VfN}aJ ()h"x 'RY` FOTOJϱ\JOz_ O[_ȕsJèDh9"'Wqa]_kU)pXDHhp0%3y )5 '`amQVzGUzU./mڶ7kӂ\rE,hݴd$x""'MZrG4{ ^hjS a2jl=bX+؍1q?]yw+MS0+MQ<ټM ٠6X*RQ^ AJfT0Gr|VrP5"*<)6k.Rapdmt 0L~WaF$F͛vM4޵KWp]4̛J3W;; թ5aӲcfs0pK\"## U*zd, d  0}zB& /=QT:e,6,\",<&N…Ov!:OKtl 1,]IE9Y̻Y#W}ۺ6nKWG~L^|q`{:qt`/:}PPPeˢvgevt*'IjV*'&cד1l\rHrժVAڕ(_"#] Н $~>eWg¤Ob@<)p&q>Q2gͲww':fm;rXfhǻorCzz}ÐPZ˲V*AAAahZ>]ń '|iFtgQ=NFhMA'2O/a :%eTzQޠnJ4/Nˑ& >>C 0=u2?[ VEq  K!!/~ GxV>CbyH64X_J:k|מ:t~::m۴Gy p˯, }/࣏?A.ЦUK,[ۤqc14nԈAA(ۇVv9\NQ*X|-Ӽ= K,d'$TH.|y{۴n6[i5lF @AkU& р &R[넽6rZ9-à#) 9+/O3xTIXr65]כzxo)QG9 i U5>;x 9w/̫ 4@tX:~&RP|G$2#SuJu[rLP[Jaê9NC^1q\qcFg uW BE GlX5ץtF"ߠ#W=u8җua\km%ut&`R[2uVs:'qe kb)>᭶W%pBi k*@A(ޤ u4pdlZ=_B⽶ܴzlVIkS҃כTz#kD'y` ƂA,Vxr JVxA@/tlM94B>o3`dl^xشz> `)ro7w˅XxK6QAVjJubHOOG*V; de W%ˁLrS?t]0;n!;A~Lv[#|:!\O ض~{gBPkЗ}^p\F4 ;vA@w:8j:tZop:G>3/;*Ow{X1UI.Ao {G)m#eIG}ú۶~eno{sȍoI-ʚ&DJpA H6T|J K ׍m _a8ZY)=#.#;;`" Ejʈ@EDd*VphKFd%;X0<:ؾqyx>&ԞƕџOW%9k7P2 X8YYOXMƃVEHhU9Yx)k5kӿA A쁅g\Bɲ@)AIN{reDzn+O vܺsUUA%ش 8Lv"W%`FSIŨPpQDFFyL(+\7]tH@DD?X<AzFb=VA ,(*u=`E2umz;'6\œ7(z#۱!<"S3:u ~D <"gY>^^L|EET4lP*vaȌ$PA ϺN4>ܲ¥l;WeBʓ{Α* )WR~q8Y=Å.ڄsԚ7V?|cSCsڐA1Eh(UGJWhmz%G[`r˯W7WW%'dN]^?W;w8\wNhNΐF[Eǎdɒزe3dT*L< IIIyA  ut%߻*6R:<%i71ܕYhW%&ց}{7 ?o:un.LT_l(Nɓ';6zk̙֬30 Vz֭A<-txH?}HܶJr\қFڏ: zW%#Eܭsbbq&^Uq] 6&Ow h(MG=*Pt|EuU{XaO%sOwk {+s,%KQjXIx7+(*ww>~='RC_pHEfXױwy9Q~ 8;tAĂ;?;EDHŎ] `ł]T#&d+d33g=isλPܹTXs"]k1s,aܸ暫#! GV N\0qRй^L^u%CNj`f+8;R䮯YJ_l 0Uc5fQئյz ~^[B6n2^/jkЮm!8 #7/_9ZfQhm8\n_}r`   "9O ^rlz߷|^ǖ,ϾC`p =ZXֽи<ʧX:~q}Sqp\X()i< p]٬ԩ݃XzUGqQ:u;AVZtziByOhЛN#RxGAA>,փQԮvG<%ج6b69E}bjP*Kjڠ5dK4SIR6j ͫ~j AMpLC5x,VXս<s*BbV$ӥXRYJuP$[J͖Qм꧓t*YIU hLbcaD5TOc t)TRԴAk2ɖhhetl {^Z V Z jKjZmZ&\xˡތf-^F F &YYJuȤJJ& Tnޛ|tZkXdj>ZT8!Bk~} ]qS\X-ny7Έx<*TdԐTRԴAk2ɖhhetlԀZc%1S[c5 2ZN5>uPxQ@=D 1lr" 0yǁ,ꐢH\ 5euL%4! ʫ L$y5ZCTaNTQ_nRR=Kreb';I,C'*P*Kjڠ5dK4SIR6jW^fZ#huB >úkGR1Uq bIqЊ$zR: ꠦ ZIDC+;-eayErbg8um#ǁ ;ER&ˬ 5d(T5mКL%Z٩$l)50rDT gÇK{__ ɰf{:1@&Frг5=:hP%N|=ID&Gg6WD(xzUH)RP9q $ *a2UEՉ'2ɖhFWOJǙ$ă(=)_l~2u˱BJTQ ץjP*Kjڠ5dK4SIR6jW#OO76h.1#tqXTFN*ADK *{5j1"=5ҘY,ɠ9:]U  ZP{r%ԹO=$H1߹x/%B99X%E,u t8;brصM%tPVVXQVVÇdtO1iS5щ=C,(a9iыHUt8AD$8N^?vd|Q'"+Ij8 J: rܙuI˅-8۷?# /(@A~ _s> |GCSQW_:aDhAA$Mh6|=pxOW.Jg>dI M/N\]+I?4\BRِ1Dtݲ Yk0n̘8AjH+kC6Np'7, r |5,}-koiDs""]OՐ' -s"0 0TVVl6Hee줛=l6l6b};N\8T XDrBa aEAmoZjRT"텈te~q@<7炰kWxUM/q8qb~o};.c" @h%1XyVxӐDC$Od6^g#q79["rNx9jwvU2MhݵMEYA[D3&Ɗ5F2RH=ظ)3ʾr~ IhjKXdPk8D*A e0|pa"zB!Roʓ/|-=N, buN0r\C4J]q@=AD GP+'O{pn ^m)R:Іd'T0؄6CaN[KZzB:8\.TTT"5q].ԐTRԴAk2ɖhhetl@WAHǑ‹ =ʈϿ&>Gr!]m)4~xÑtУ1 =g'yK[fnlFAApף>|:\.yJ*T%/!C,:id-N%fK٨4 -&tq/ʆCR_8ȇƗyq CaAmUթHԁT́#:^ojAN^:j͍}L5U(K XM;YYJu{+A"d-N%fK٨4(9h-蟖Qf7cge^L؁9#C$j4gBS KIG/H賰i7[z2ߑQ^lz\' ,AՐTRԴAk2ɖhhetl 8(#sXS.{3Żf4ƗVh0{!p_L71ZDkuO#&S*A,eܒջ٨:tU%/KRY* PCRYJuP$[J͖Q+"dp|wN 8{ C;"_:BD g"x&)2C85oAyoΛz455zud(T5mКL%Z٩$l)5"BIwx{,_\nNo =V=T?c G[<=C,F R3~&߳7,1P `˖-8^8m QРJm۶WA?5t0i$[J͖Q+"D+xc*[asLziRz:e*y%ω>c GGƞUv-YtN}*|5WU4p>.ZQbG gmgA}*}Oogsf]ޅԲ-8J ]y?oOOL5eer L 0zY03xѺ+CO 00&~K$Q2ɖhrMroD*Y^'fk3q-Kv9b󫘱gYz t!#G )i4p@صc ov48h2x4[.) c>ud ^TAk2ɖhљ Jgbynpp|[}/8oV@75p(F^3P!> {8yo.zCACw樁}9p8CU%&`eF [_#`/UAk2ɖhљ JYfgU30x} eq?۸H^#mo@Y:yo@z7qFy? lo}N]oq*eIo 8 ^=!eoZzm8J2˛$[A= #q9XrN]f֮'sx_oqP:_ |-^XvDQ;q!<>{~[tN&vɹsй{wxy &p#^&[ HΡ ɌcNZt>)Hq~]KXd[鋢d@q9P~`?ihdD [kZ4]tV2 ~e:ۢ&r+v^zHKI9yHjN2<"2=n~d08` |%us9NI\]L u 9ncvQuJv;^/>AIyx>zsb7_p/nǒiS0}y::ႄ KIDzrthm 'v]Ζڢb~5e1n KpB*i0`3jr8r:΃gHuCzXx85җʔKCkj%GM^^eu!=CAp4x,^zDAo^2FN؇":LjPti[cbti-??=mKTwqgcɔpws-qF^A#!涫HZ_BwM Bq4֨ܜ 98W_ ޏW_PZғKŕK_']`sI\~? ;n&H8#Dt9z IDAT|E e@=xT1sӕX3gₛ&G o (,*My X_'g”cn1M7o,=5xp;cQFzlzz<⃚JHr{=xizB<7\9U<7n{ũ`<Ԥep*ĉpD_+/Nk?%|hqKм.UYdISs8I\ Cd(!YY[AH9Ȏ!˟x˟Dl}$4qzvb>%g'=@D_4ư+a*&s5E,eBS_c#N qI{q0 !wy03Ygqɦl'1ԫ7fA7r!'?,X(k[#O#OQPbq穏P'VhH7|1zXh// xZ:`\<>*l^A0z, ϭ ZIDC+;Sk4v"|Eܶ wn綿ж][zt\*.n9?+BzdS KNƥWKNN(^h+;kR~R߷ GMLˮZ ZzUpt4⠨ Tڲf&+1 8`U^nfm ZyG2ɖhhet<.6/.xw$PWoֶjcΘutzh??\|,^0/l9MݻSO`8X+xy\ub<a2y2#igF|7ҏ'0E5d(T5mIN%CgAOrԠ=_#Gl6[oOJ}{q`> |h44ԣ( YD?ٵ??`?#+py$=ڟYNا쮾~1~<'S9zEM6YYJuȤ?L%Z٩$l)XP8C*XqgIJo& MUa2R6hM& Tn:tֲJRRfkݻvaƔ|0[~ x@,lqHv>Z:{+Z%ފVЮ6"PVVvm(Y#ci'QJgڙxRlQcQ?9 z}k~L5e *LTCRYJuP$[J͖!@^)J+a*[۷/w=hmkґƉGv:$b8J7-WVeUCHå"CqjEr@?90x*Qs^h8uL%\AdQyi䳰MJj ee﫚v $(.!:Es na<-YXy|-#T ꠦ ZIDC+;ڐ $H:ޕ)KKO$ 1+cڶUUصo]GDG\ѫ@˱_Od-ѠXe@=z$# R*k QXPD’L |HH'VTM҂9eC!*^/,T)5 PLId-N%\gzH&Go%[IeZ/Az"l8jI|bS;vX߰gqFؽpYce%_y7p? .R3&jLpvXCTdC{4$(K.;&?Eǫ!D8 8_Od- !qLNfb}"u< =8 صs':wsNCeE%|!l۶ ݻwmނ⢢f!~[B ^׃awf9 7^H勲1HÝ䜋xQz $ӓcb9y7`'͟w6d0bYWyT\)&YYJuP$[JMކTTTՒ9z(} \t81|+̻o.ciVQЪ /C^~Ҏtz_ѷLm^~9cyԄ+:d2Eu޽?!***0}ƽ8eIػ{W,TԥKE'ΗŸW"K~ |(yfz rsPq`ltZJzh&~_w I<ˏ%smpog &82 D<q0-pŹJbsP94<#[d_tx᥸kЫWOلqftD?EDJJPY~ %ՊEŨ,?K䄡ㄡN$ [V8>l2l@Xix6ylz8vo闥J(Yxe;u5g{-Nxܨ(ߋo7oTN(;xݕس_v7W)Sف?мb,Zhd4@CxC̊juE98C`E noB޽(_$iICt9Oc̫Qضw=>uZm&8l6 ?Du˷/0ϭAqL{f62sӘ]z-1|8L44asǶf$wq>` Nu>`;ЦM1Ɵ} ڷ`hhh5\Gy1FtMxvq&Mgyv=J#.EQaKO A:ǁ>ʐu]K. ?ƍ=}t k4vVCϜt~s^}u5fZN>;w#`9Իb=)lѣओ?ņ#O>qtP(LM3g= фO~NƎ  5?O/7c̸{зo_z8#cl߾7A@aٰo>n;wĂ ԔrM!8h@DZ` &A'eaW4l0<upt.(Ű :5|wg֜g#VĶn7OwM~߁sλ: J{DzW⫯B֭;<.}F#0K$裏 ׯkY< aw݄]{ʫիWcx뭷ocŊ8q"n݊cڵܹ3rrrRR xvk]IP~PXF`hCyxxq=X҃y!,ln}{ 37WCJ1ճ</.><賸Aq;:,y\8*+kQYY÷bY]]t CXz<σ1l}ԠC`2444`a49Wq5{Lعs'N9l6c̙/СCb1bD4 v5#@g 2j<U}ǟ9 U>9݀V=VZ&tAqO5)qHeA r4z!Pc' qaNވKӯbғ7aku8eal'8'82➻{Z5߹</̞Ռ76ҙ7WaoAXlo/~׀1Ii5=e˖5}Wa{%8ôs@qq~g7XNnGmm\|?Vz_t*~~Nv:n 4?#9L>C Pd;`HƂՄ^D<@f%6 ́/sQ\ڶؑ<<^v܁r tTS/sx!\vWسǡcv82-"~x`2pcӏͿhc_ओ^s,Y?}>6> ӧ]!IΫKpcϞ jF ;3 u58o!j~ǜY7}{˱],5uV舮8:Z IDATOJ Je)AM&lVv*I7:G,+QS] Ǔr=q9qy.d,8|qknp(O:`4b6K.xm=/Nv1##M.))m\ߣGg ;a@ 4NϞѳg^ 8|ԡ|ԡh:N2180Z F O,SyfC o~EQq'T,C,:id-N%C Yǡuuu UsMDNA@>O48_ nNgݸ m\^X6Ǽ/@[DIO:b?iRENz;HPCJ,#BO;Mȵ7d֖6 ɋי/o8q5~ ,EsX&C}i0]DG@۶PQ?6ph[T M LhjAN^ki8Dۣ=j$I, 2 qU?xi8f8NΨV lV.G zHd~Ľn7ANJj@=A˱J0[,QeeO?;SԀ\e~[V\#͒Pbρg#RԶ-^Zn ˞y}N>iDzz^vkWLаʊrvl6Y[ )e?'\|RJ,#BW1l6Gn7fd8yyyyν?pmP^Q6mp3Чw/x<ιo 3#L~8M޿><ZU8iiicAݎ={baL<,f 99=D v & $!*jEmm-|mɧx`4qܹ0^/,Vkj؈?ވw \[nƍysJqz|ìN'݇{ޅs7y7eaojĎļ|&ofxvfؾ?_^rIx_>ښ)qĶ7Ɓpasssz HcO<ʪjLq/ =|3hjEmue\i556ua[rrrOADAצP%rHBkӛa%֭+vًE#Gg9 fs jҊǬ1oάr,|a74b#`Ƞ#}۟11؈:؈;1NADBCJ,#BO0{ ׃Z,^mڴEq7\6N:)).Bqq<^/6~ rsQQ_+}*r /m!|SᇗCp"Yd2}eA>V(..ŋ!ϫH Ai` &#(Aj|sLD#BJGUENLfθb]߫7A;3{ĩFx|t [l;R9S; !GBIA2ĽhXFkBz СCgXlܼ",`0g^*+ݻw`4:_{@AăeAA@qy8ov؁;vY8l90t:k2 7/7 $ R6/]rHcUҳ@TW&% fFAA ֦dHD9 ް:W"//999 *T|a%\?+d<=^|()*F~nn<*G3+>99 rEqW\ `87|@cq8XObaǜ8,4'Ʉb446`58餑hWn ^ǯيоC{d2). "~hrtJ,#B/x<8.YN7]_Hhl^^v{jEN`6rcNoA.]ХkWlA rRbAzb6rd Z]< нG| >Fy(,l~Gqqoϫ߃l 񭪤$ ǁ TopјcCyyyyZ(*j&;&x Fl9ɱfho?Sq ,@hAqk@ڒ~occ4\ˮjؕAD#SADd[ 0z49-J `0< @hCJ,#Boxk`1}PSW6AݵV P#pAY{_ I1āx-hTOjP)&6Adb/q  R֊Qa "[UI8Ad84VPZU %^ vC` ADhUXFA$@}aZP&x={ASS'lVkč$D bǁ6AxPW_P[];j+a4w㑕'7 I=) J9 L ETU^SclwCۯ?M:{F1`iP A-9   ՆƆ؀&8M 4uNkRSAzchllDNaoi?Ft YS@Nǁ APW_6p9 9Nmۢ hm ADk:!/7Gq2QrII89GMYA:D NFyTAd ADf׮رVQݺx]Aj5 W[TA"QƘ ?Mlȑ654<] sScK:\NYqsJ q7" TEYю`0g *#vp*)Y/S>AQ0NޭV9nVn.vm#'qX~xG.(S_e?CKTAz8f d6?~f`AF`Q^m 9q4jhJb>\WxK, " cϞݨD^^\q,jPQQ*Rܗ7A LAX_PTT?xulGb "򑍵uص{,9r;y2q\JGSt|Ajd2xp#Ѯ.O]o0`2[q`~|G(i_bLpAJˣ./ o|h&dTS]U͛Dn] B/X'/7]:y-nS>}c0poB.A Ws%l^U1MDL|˱2evٍCh6 -ߡoޚAA4k_Pc7_cx^ 械뇢v퐓UfsWFٜADC\Uz{nތyGà(A}1`8b_P^K B#xG^^V+ڵm vF6 6[l6+F?~oԜ "{𭪤PjE^y.)S!-\\;wᗟe^yْ{ шt=qҬ9sR&AXф!"TsA;w!77MMMx<+޽qDP|;w[q0+&iAAd1;BKCMuJwg8b{c|a񊊋d{#d6'^muUbAAqZѮ] !]Lv7}6,=35W]3<V5^:#?? ?~%yc U+=+AAᜳVZ {EBaM_E;fX;poiiiE,^/j*ϟ [N Bk^c8d`0|M ">V<8J5D&jlL BrWaEѪukL&n}hlhfw ?\-MD6ll!BADx<8.;N{m$N ެ]uU\˪몫*D@YAA JHHi2~{9<̝rܙI2ܙ{y$>GcY>R$XheXl9D7ceBQeXA|BL*۶{‰h(zHr]\&J݉is Dcql.6ɏ@1oa]7hhjpMömҩDx,J,!V!&:FXuqSIg+Hcbʣyu+סm`:?bBpصz<f[@9fBPP@ @TX4J<'M"kBqhHx-Oo%1i~8R!e -3YtYu]zx^jjjH$&!`gYl!* VJXtDlT-xY[|i9-Sڅ#6k:Z^jHu,_c:?`B:پ};A!&Dv*pHg466K1SA1|0'57gع~#[m4]g1VXɺ\W~S !!hM[_{s=t:5d=^/oڄ߲U)T5սS[n*+~so'|>"0u"f[o$%Acxx<ib&a:{u]5DZٶmn~J RS[K(!a:pv:;;|(ݼ>:;UZaAQN&_ջ﹔o~l} ?\>/)]7h={===Jai`b1ML0 i:T}{]u̱tFd8!`fV?V^YGP08ٗ SF~M NN-~{P3!AI'=IsAA4F]]sq5-4Zq3f`&---b~ B!aZs3x}\pӚf2{Rhaxmmm$[Mt]sS]u\E.(LŪ\Ho/`0H}HCCtUB+PiA|> $I$`~?YV+4_}W/S⡛B tHvClh8@ͲkNny[mVtkfu" L!I8" *{8>BLC2p)[-sNvΛ`dK:ߏrJT Owe,XE`@U08n!po**\鶷+S !BQVuri,_8M{>\W^\DziSѨoh`ٲ̞3FfϞ-_ܭHQҲrA"<mԄ,B!SZɡbࠣdQkjڵ|GxIϟ3;vre.bpM KCE#|_`QG[^=ߢ`na5[b:W)M6l-̄BR9G1˲?ηg?y\;] }W@4W0S_>RSH" `& tvv+t="taqR J} ̜58v2 IDAT+FB1"v[h1mM0 Yah!HiVW7n ._w׿DQ{e|xPRD{i3M,\ӉfPգW:4Xpq,?g)0o,0BI.T@09sff15={'T^ JbUtFwR<'"y['F.yذ~_W*hNSs33oغ۱!7waUem)e0 -_^yg8p3O=et?!#FUɶm2Y[IbutbʣIlzu>r\V7Tv!a -.Ah8@}7|;_770{;|XquIF#l߾|>/Y6vqllqlqr9ϲ8z1X޽{yYt)j!(RlY&Hr[NFӠt*k(ŵmҩ$ݝ 'L, krNM!**--.{qGxGy'yO<[O95v5Od~ΤI'lذT* 䂇e˖cH$dBu457k޻w/f>˗.L!hJCHRX?~%ǨkhgYS~k\Y3wJ18K?%˨kh@7 ^< A6wuquwN:=]֕-\RO>+VL3>cƈ.ge4/]VE!D*'hX.d<Μز5,˒BL->9'Ae;~ˇxehe kٺu 9֭#8ƊJ$! jk)uD#466UΤilj"PWWaU+T4]1r&Tax>ޡM4Cs]-|!/eLxm6lygq橧@h!heC:U\cNqSjO:UIQIQEF}MpX<-yu 4m^0fx YA!5rd `T)WUC!DvCiCA¡dE!hiٳ:Ҏ=suäunL@!R͎O<t@/h._!t*Ձ4Mx wөzګ|ѣm(W֩J)%22 ۶̞78]@SyȦ3ޱt21h|B?] lٺΎB N{{;K,2'!0򷇲5fq0<^,[N2'K9 dɲlڸbg+byxWThy,YGz-3XZ!tzVX4܅ H$ ue('NXBQEVVJa&M!NOqmM0=&`Njj466afٮP/jAGaqDX~?ޞQ%{q3µuD"Q0BQ=#̚ƦfXٳz}d2vMJgg;fb lA)E*W;l"X@ <xߕ.DP(4lJA*.>5M\~̠۴~ !X!ϛG(gDq0 @ @}}=GuM~Pm9((0*o${?>+஫;o:gSAǢ/\+c[!R%[y 464L%I-?+Т/=L:UMkB԰k*Hq]{%ox[UֽwyTbZY) 2/W;|BT[x$aA292e A Uzb+BCްܲ۞}{;Xhj\#^͢E;سooŴƒ?J p*#Уi7MBIOyd2?jk}OU&C&7ZaG;;T89q !UScBF0ơ|z̝;:0ǪyapYW~i6$oCр,2\Bq uÃ# *,FTU 2+<'IGBJN\rAC\cB!:a@~GBJ [*Xjw<ԟYt1yWl执̮]xiLAs\W$}-q rC!x1 B1Zzߑbx&TP{χzIy @:ǟxˇz_ջ*;!#r-!9̵!',J@i1$nBTPFUGoW?|syiO:c2{L>{T]XuϹ88A?}W!q]h4[N;L:E2x|"rI'gqB1A:ukrYg$<5k ?Fφ FBQ% %N QxF tߪZY{B4ԆkѺGc=o A5^::xєb9 x_)_!UTRD"KO P2ge,)TU%7\+g֌GbRp(DSS3-3f`|BL uƢ455U5IL4Ð.B1^ gʏq45a|^M Rq)eY^h/BUQ.tc⺣{\cNqOJSڅ#gBY]Ӱ~|5nMmb++J*tU(WJ >J&QB֌@S<]1ÅM=ٕ_!0LVI; ݻwcEjdb:-#-BTWEt]Yq=pxlzu#B __Y&aa8J:v6;.1=t(;`._!BO%4]' ƛ x6姽,^MץA.|0-öm|R Mgؽcd4'$BQ%" ˯|L|Yd6~Ok&T;k $`1`x,Yd2N"1>ӱɒeٴqO7W!D5U2 ;k7̖f͜kmU#aqDX~?ޞAf5nh^Hp bZUdRMw=/=7!\[G$%? bŊ\M0rJ>pPJ:@ߏ8$ R/rW!Di.Gts% Sk'H`.c%464,4MjSg*_b㠔"IP4@40}Lf3+MaԘQ [׆LS)HeVJkiX4Mt]qp8L0$Ͳw1BjfCk~|4בLHR8aX߲,|x'~S| !1J740&ޠ߃3P¿$6+CՃ:"&zNƣMs$@ @8χ븮i~jjj47iӦM*o!4 L3H @GB7l!Z4-3Scuuؗ֬x$uo) ԇNL}~c O à߬HD"NIWWJ),`vqJ- lllUI1&PqY1C<`cI ݋r8(ZVCTɭ0PZ\%Ͳe2 K. /{l6K2$L⺮8! 84)bq6ڇjW denU \pmq 5ơPxud2X۷sNfΜrkRZ\ו1Bqs]7`ȶQB5<)KdzjA1!q(5tמ|ؠcYX_w.*(q\}wJT}+V i&ZZZ3-9C6%JW%!hzFP/u]T:MmC#sͣK&ݻvՉ:iPE}?5ipBT21ºkE\&yľgGAiܔZ^'wUݩܱ;v`Y@Mp]7xx\:&J*ɝQ!&*qp][N?@ @*$hN0O" c(0*9NUҝL1c몤rql&Osg{7'pKPq7a-U0<@n샦iOb1tUBC83Yι"2]+%mۤSI }.k#Cc>1bpU8,*P>LH`ȇݑF[05 v$rnpRd2IOOMMM~XiŠ0!͢i77tUB3̬JRĢ1N;\2Dbrx  I3O>AMM zVܥbL+PJ {Y~ҩd*8(ʼnh>|sgVQ(7Ww3.nA9::;'Gw]8CmۄB!~k:.N.dH&ض=lB!|4N# o8PWWQҲǨkh$T[C:oYU,T%B0d~֭]gITFt [ -7E+܇T.@ʻ5k  ذa:aiI)5h(F+z =燪*H$u$~6lWi6~+,_?`b^^WqZu꒰A1C:v?c[^y M`^::xєb9 |_xqW!p]X,Fss3b8N1hh?p~vm}˙4ӈbkBQ]î`̚¾6[Xl\ hjje /B*R[?VK&*==Š`߾}DQjjj^L:7v(Q= !Deq5pMCSCTj\ `Y`e6W!q]E*_<4_qcFK +7g\553ƻIeW!q5 gYC |BTO1anmi̧n9s^n\@7Lvڅa(MJ!HgU|dW';!c ݔ>6/y;txxxD"jkktMpzyuz,=wR5 {i"'WB!QʨAW/饣m?ʱi߿X4RY8}{ 2BLpl6;.x ˲|e[\pG'G$(GU㠔"NNO#sfN7~*r]T:U/Ip]u`6V?H}%};b򙆁ݗeZn!|: =ơuA[CtiXo³d2].hU'½)-z˩gcgR=I!& B|^zr C Ve3kw{K7}§B4A3<4ՅG5uMMMDQ^zY>xŅ\/Pz?;(=A IDAT!I"!L+3933w+?Ĺg_|/0sQux7|g}{vsWo}R|~~)<ڲ#BLy qD2 P(8` [q]f=x<t};aЇf_g~2 ů,tonn]7NOO˧OO}:~TVbK*YBqhO~Ə6} -擟,*7>{c:w*8v9w.oYRdiΙC6_$] E}}>_Q٬au]Ѳ,:i%  [F,O1 0fV5 / !B!1p9wyc}V'">m o$.+82twvpbΛ㺤ܖَì9s5w=C,œkqpuZ{zxuZfbi]LO׿B}mb6\Î<sq44Fg}C?gϑA!8E àc?_\W|ʣD#f_z3f?JMm-rs4+=B|Xz2naӺ{tuub&_ݿ=W\o /}O} 74ßGzGz-Q.MW!b]̜5%˖cgY,h}_xNS?gvLtm?h:%&fi̜1e 5}mm<XU B4552cV`-B!O=w%\E4K_pyλs9/"i5zWw~l ՇM_ˮ`ǎ'?‹.ݗ]?8eY̜5˗Xb%y*4}++c޶ COk55|^I Z," ,fg]ׄy=454T=˲x+׶?Syp 'Dؿo5.Zįot*3oG׊Gsӿ}t:+/+/Tܷe|F:b/3'laҩ׿uݢ0Mltb2k~˲F5hhVviX~?qȿRB!}=]s-^xwy$qbb |x<' sl{ϜhIp !D5Dcq¡7R9]ɞBx ݺu]q%t[^x|2$E ׌8x,FMm-=݃Ec1 h4zjKU3ݑ`B:OGg% st!#Bj8laݵsq&>SO?}-ٯn^O7osqeCs4 s+#IGgdΑM0pJ6v&=ܡC7M ֽgK^^7w~C\ƷC6^yaz/FTw~/|S5]!NO$m̜.>ۇ㥶&\܊\glZ-P+@ssTB!-ӦK޳_ y@xCa9,|_챣IZd!|Dݭ{3̳?®={Xd#PaѲclƷ1Vlzd{dPL]cլfHt~yBTɑ!.|>\t۷lfپu.bib^`֠4 ֖=ݬx/t'dq݉ 7@mSdI!Bqsy׊?g3l{{%D[6=?À7&Յ%yAC(j"]]'8BqW^9I%BNRL$ke|rV\ z-)bNIc[Rd}*dsSq[B!BCA*4d_A.pPivo' ^: ^,t^bᘠp|lk*WڄBAźbjFC5y>( Q˛ >rlHR|4Rkc˲#8\9uy)TA!B1-4v\l.LJtvso !K8 ~3sL>=6*:o!"Bc"hz*yaWQ^{fx4j՗ sZ%bɽZ!Pa+GW'5~fWʀo;.Ӌy(ģ^Ԇ4Y`iU-B!bxCTT;`pŪ^2T}Qfo(zwZm>%-$B!(h?@{Gǐioz_A1nЛH3* BVx#ͨ΀[z}ROoŹW4=|IJFW<KB1#S ^?ji^{ UKK!swo7}+hZ[njjx6< AUԙV乚jj Bk x|͓UKji !pngfm'?%Dq793+ 9Aujٵxِ8 2WH<-8KO`n}#>SB!;wLKe&.,_n^:>mWU,;jb𰿭GD"WO_8?~3\s]w9'W"e Go|'?>7ή.I%N=߄V(y ٯyXJjh`qz DDV H&7D׎XBq(P N=xxg%.ܕiI\ϩ떘} }') 'Tbj*R JCi}T?J̿A> ,S h|*9}m~_^JQz|lPhT!Kjw4r@4r4pFx! 4^{BمKs_N_J4-ﷴLRߗV;Gzqfx`I/^VGI%GQAe-k/YuCsֻwaѨ H4??!iN0b}bڠWz.="%K  ryƒKS&})(B*!V=Pnpב#3N;ˎfт1BJr=8~Zd[!&|ȝ}vǝ0o pSeGڿч?D!ރ 2bX& ",7I$7@0gԄ[B#P&Mz˘u2 !9e8 ʜs$u f&Mvqb¹J\}߶mR S=$il&˄BLyY*o~,Oss~L&{v.BTr{c-$ܛ}[mX3>T&ǜZt&cSE~,e 4]J!͒ڸx<&8tb\W Ț@ rR k3EBqo~6oLMm RdY4==W~/OrƙHp8Hg2<ZLàTbT:ͦ͛A̜9ksHr]x"A*f`^l;COWolٌ70aM>~kiBLZw

f̘lXjkև;*UQw_zEso^!)^}p=Ŗb7w?]s5 F]kp\w]z9d]FU+ذ5:{-/>C*t6K&kd5UVk jxץ-xzVh1~Tcol{{ﻏ\ARK/e \q(aed3W^b%^y^ֽ ~=}+ܕ.~ۯF].% !c^|_r`˲Y>?)躎iXŁc.ʕXD"cNGT_}]-gq*0pH/ӦiZ8,R@?#^[Nd2Io4‰'t|iYV<]߲a8 u\A&}tT3kI $ JO݊)pXAj[zj+]cAl ]݊ڵ[* "*R)g dzOa5e5֠䕼M23γdɼ$>Ý͝!g߽ +W~| /wknWzJ&.>5젱}f_vYƹ-bQ~dZ4ekshYuYd1ws/˖/S[hjY..壖I!Zk_CձtZt(C1cC)ˊTxW:/](&^^KCPѣog>f/[~/֚={ٗCg~޽94jbRb~׳ifZZ)PJe<WJ%)l"S&[1tކt6 #-*UZ|p>7[nS2;S^۶TY! ^g0O]jhp ?n7LGg'.]:'^mA gĎmh$_fۦEIIIޮh@ Eմig2YLw:r::,y\N'hͶ^;v&歷fSOqY`> O;_06r_:=?ZnWz<,?On8[6opӍ7.N `BMM7x|>jzh;mal zV&dlbÆ /_c^}=Κ5|uwwSTT9H0(RR[i\)Vd6N879MYٳ~^i#(+/8&}0q\___¢Bz{r~pBo_W[֬(˝68 7wʲަWczF9A&W)o&sRޑUH 6L38.ʥc|kJrU秔r埡=L%PN"!<w%0tvx9Zt:)dR:dӨ!DU)-[e̟YR,[d_Dw)JKg?ӧd?[~z{(.o~yBE_o+n_N6m[RQ^v˕ )mD"U1)-kHpɮHc !#?bE4CmLLA5c:JR;vLO[RZZǰ455TUz3|i08!u_MM5ӦZW淋5@UU%WСW7nw/ԖMɟuL֭[ƍOHq?38\yҥ_W9s>#¡5,Lg-<Ӧ@tyTwPHċR GWgǹΊ8+cFjoogӓ[mۙ1Ɲ۲<Բ jf;S,fyu3,+or17!!40@k NtUEp?8>kW?Ⱦwlm44U1kWgm_5H$OlG~c yٺ'ܹjg_wY7pI!1]f#@4 ‚BX,r8p:Mɯ_}%^KYEԇtwwk4MN%SX Ȝsd]4t+é[EG0 {zz!>U 6R|>X(y|hãԭ BYlko{b"JeZb0c֥LRZF{{-ZGtWh9M p]2AdZ300L-+TYq٧/McLdd66ΟGd0Me0M vG000%3VY5[!ƼIDAT>=kf eOXŔ%]eTN eq!8Uqq:;ք#‘H~f&n K4(+`ZM-'>:ٙtii çrJN4 ,ۦ?SSSEUEOe&Zqo#5^˲<5rz F`8hBL4XcZH\_M),+@_ 9x<**qtvvrM5a6ay>ҩL).tAcVh4ø* ˲FD2  l!P,_'`h(1=' ǮZQcu !B!D0(zE5([Occ#}G B̓!B!HmF+dv翀fˤД>B!Bz{S0Tdz_dnn!B!.r݃!9BAq G:٩}]a*\B!B\L ŘB6V-Q8 [˚|SA !B!&Fab3d 5IENDB`muse-4.0.0/src/doc/html/single/documentation/main_window_with_midi_automation.png000066400000000000000000003047721404105311000304360ustar00rootroot00000000000000PNG  IHDRtlt>9sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATxwTUꦫ@Er%JfI1!ܙ;\ IEADJFP t?*ϩS?뜽WիW ߏͦ;$\KrI.%$?8v"iу֋͚Ӥqc`R 9+6هu;9LU\9NP[mZ31Ml~rPt?nƋձFW1gHk-^{%];WK"C9LQX _BWOO5Η >__;dIES pc<Օ%! –Υ4?^[w.5)JϻiڀNABd&Vg1 C8;]sдYs^/O HK?˽94VL8?5&kF\Tye#?RRU 5'@:)fϗv._}%>D#y6Ԧҋ #zKI[+ 31oRL szBsV㑵xƦ*44I I5%K5H ;`}>>N6m}ҥkTWUW,S; ʗg(,Hn2\Go Q[&u#D.)X+tm&+oCKt:IdN׮ҙ|R0Ob!_n$=3f x@'yXZ]S iVt 'ꋂ̩#sBD~,O:OL _fx;V!V0M@KXG Թ먭% q\"yJn7Av:Zw-"; ҨI ߎl4eG=dH{U:ӈ,C0;cw\KEE-[Q#lAN>ѣG###Ca^P$-OP2 ~tr%kBE_d@:ffk|}#VGnd<*ҨF D8ѦWO)6mTGXy5 Ღ`@=N<(3D"e2R~Ip86L0]*ܹ Z6oNK>ѣr M,z9WQA-اO∔ėeK#TƑ#`458T]S~Wv!άF͊is >:y-Zqm_|ȎYoTrw#݁哕ͮ];iܨ X'L dMuuS&0NBJ1*A\/^R4j cy 5ǪO~|b:T;А2K-zHH$A[o?ev2Jc~D/t/ L;D$_u19T1͗fD41DʹNA'ӧֵ!˧cV6wqFruOiTXv\T@VVv  9K\NA]>6#3o=Nɓt-T:{tw@ɷiѢ%G$dHZeoɃ5=CuaER/?s&u(^͓#T/F[օ|$$_XSpTLJl#n0^xhΌg- #*__L@M(amxS7!L/?єǟ|Jg "Mo2ˏ)j2'mHe3'sVp")޹I0OL~?'Ns.@|.Ѯ}JKCQ'OҥKWK;t`ݴhBSVrښvN/РA&;vm55r=Ǖ=tbּOh:mX0g6OҦ˭UUUde5$]kzqddt"?_~_b"'NI]|͸ ƖVM:sfUB:N/aW|mTG&T!o&N#lngZ'pUb:  :t[;PR’şČc#UNqN_u~g-)=}_spMtEo|hQYK㩣~>Zn;KM$`4Џ7 E[ |9à̈́cU9kFs9^Rd.nr},r7kZUU8YqLUUUUUgΞUG]x] ε]m[6oaŖM ]c()GVΝ;cuRZ}n3p4:u` +R~7wQ0Y5NäIeC|,ØH,9iS1}MKj[u~pщp.hd"vFqSqGtN';wlUdf:vuunvI!&-S"F"zT*8.78NoF.'333j_vcdf&vSh|̝O"ٳkw`oб3R`du"N ̣4+DO$+Jyժnzߞ ۋXe>M\ Ӊ]pksF|w6);v͛6qUWѹs]I?_S"@]7O̚ի_?JOoX6%t:Ԟ;4;wb:wDI>غfr.鷙@0HeEkjtUd7lJNW*O$κX!Uq.R:)IRޣT/",# WB_kЧwm@񈑜)?(ףj!>rwfqۄ̛;`0H۶>̾>]Iԟܤl}E}zq0v`RMo(qd0?I 9wDl2+fʹK;[έ'y|WpI;z׫Br^ 6&s6j̄'sV=II859w)w'ȪWD_2laVZtƙOmΝ;PR{KhC٩:;WHzL'O0zJD&#='Nrnv84#)Hg3u;!H^MVWm'ŧ $oTB|=$b&Ke(SIF(E($VD;iYd##nHj>/M6%7iByiNZZ^' ]Hqشa{v[n׹ݒ*%yԹJrGf}l޴OQӢhB'7Hܛ7n`Ν}9tۘ8w̙3Lr'^'Na{ok.:u;\4kҲ uX@N9!۹3gxn'//;vbOzniv{v&/7ڱdɍBaNAc(l5Cu3 D̵u=D':qK)@!}!D4K:BD~x"qt"';⛥FGU@0ȵn:?/_hkRTĎ;[!B'eV=cGCH"qDs[:qF[>D"uv*ߟ` SIQ;oJOU1*4 694Aɷtxγ-淴j}%{ǯ8B>|G٣m26o\륬d hۮ=-[ʩR*+*8~{~45eƊmuە} E+G=YQ/j׊>0CEBz1_u R&.ZPbDwRq }nρ}{8wV%1{G ^M==QݻwgSdyfLz'e'9~=wѺU_7@m=t܉oۯұN\5Ama 87l]2o[ʢiOJcjBз1~HOc!=ԭIDXǺ^+v,eW/ ,IJ|UND KJ;t^/6DU M4wm-AF%ٸn-5m?[Ω22338x(ʚ}T=РWw.f<⩫C Bb4b.kV~|)p: />73igޠ;ѣGZhaЩdʺ|PN/u=+%k˵Yψ-NZ/ƥƱaǦC qkctlѠiio` "Bɠ++K[[[cT__Έ;].<:ѠAfbZZG"`t,=#\!e:]:^FF Bux=.WbS_ O \YKBaUWV55S4nAɷכQԌz K@V𹍉^y/<cr 5[Ovh)v I$DT{qz1b@cǎ}vLuI`رvK+{ТE8PgϞ㔖RUY' ;'"7oN~~~m"L.:k8V'ՋY׵BbIѓg`RFBP`Y%z!]ɇu;ɡU WVT[(\r%.7ȫ骅LfD*+ǚ* }W g d\l4Mr5ΪN 瞧U9|>Μ=(=^t9994)*KXCxUIv„=\;}J)]I=*V"VSSϏ#݁33,uX*H])$v ֙Z`$vRc.h%͵J;u'x!D!z1f7q 盳%+-:sΩH`-fB ".8uMyZH\(9q d\}m1UpZw~/.'öS;YPDDwJ^Ml6222꯴F*: <4_Or5j٧W(7z_8 IDAT,QzzCkdPSB %礂j'%D.9I~z*X&՚I9N|{KvV ]F BSYx$sTD Ob$# ȉ]RC4AŌitMȝ)J?4VU)F~D:I9$ϲNN> zdӛmUg:Dͪwσ 7ˌDzkR%ZItCֈ!r&doRB RETZ AS$Ho hڢ 8:Ǐt |5k2JO#~ys 4 }*q"%i%%D`$w^srO*,4\1>V|r(mDL[; btkJnߘjxO hl5Xh$OWg` (|/Uhي +**hѲ?@VVC3jE[ȡu2Ų~?K?L'"?B DZ&9{1dD<@hHĤ E;&> `e0IrښR&BN4&񉚼c X'z?MOu7TaHp05h9XCo mCs]R:I?7Õ[)Nk3oh+{V$mgΜrzSٳgΞ%aKMM53|W^E޽y!'7KaN.lF0mFv^!R~\D1$w9Fq 2UdţlK_$}iV&^syV$I20UttLE8KItBNߣ'=ғm 127NJvB˸LRhaJǷ`N)-+*+iGklJ(;M)!1Zx`@:; #}"d~Xvʢgy3g$(<<3<pfsG#? U8iik3c D0HVV6/+jC$BW5)u!۷mo*]U{K.uh"Ӈ K|a_ݧo}-Y#?$ͦ>㏹Y`^גC?&Y&u/BUNagIGV*i6F+pw}7 ,PeWUV1Ǐl>#=`A_4ZRCpo^n`OL'FZLl6̝;oGj+O6ô ;ISN0'8˜U8%/B)~ ŤE5N_}U9rHOᎬW+m,f+@#GU+W1JfAFFꕫT˖/lRU5jXv, A4r۶m/nKr+FMiJ:<ɿO>Y`æ,=f og駟Jbɒ%>q"ظy%fFTc)s%(u2ȈoڲE4 %ifʤ3id.XDi,5q&oLkѨ| 0q$>]>ˊ#+Cggԇ> %YVMG[KS*9rWV|/_Yz5#F`U:0)Ո/jJ Cmjc,^DݚH $==G*}a;$== "8`*wN.]` .D QllÊyg;f,3AuUu !8a~: ׇlA6tBy6too|A'CpA  ^/7x˗-[nᖛo>U;ǿW9)--eȠ7AS<5}ғ'A|^/7p˗-喛on>`W_1ed+w֭[OAN 6D?bpvAUeeToǟ0|p-]իf۶m j_},t)7^7ϖ-:tcưv͚y9UVƣJΔ3hjjjBYSðC 0pM1߂ӼY3Xv z{&O#m>}~T)o'xˮ;U:չL4Q#FwQeee<4}:Cfiv&A][RPyz<ڹCqmkTkAGɓ\{5k *+чx0>O@Px9b˖-g,[-c3f4˗-zzCaIlڸ林]Ǭg!`uvxG1|8{۷n#+[ۢUAƬY,^qh.cFyy9t閞Sew)쫪i$_&==O'"Ր?NΞ;_j˯_g$==S`EL.r,O.]x뭷.k|  ; B׿ V(($%;w_>o|Wi۶~ݼ WX+^%$:]ve\yՕ@he#;{]|ђ?/Krw23u˭O%5jܘWtgmx`]x^-[6cubƍ޽;֭G/ngŸ׍F `Q:b۷O_k.,X0+_E~~>]taӦM_:6l؈6nD׮]^䷿-K,;`W$}D.Z7qڶo/"˖-3MKK#a6| -==WÆ|e IKKePSYK/Lm:?Ug/đ/(xHKT9M3 VL~~#CUIxj"|ot2amlܸ)זcƵ{nnf\.cǎ5EPڬzGɓp9 ?(߰q#&Lt2po &r7n۷il~LEQ Yj%).o̒ ZjZ:K 6dbnvsx-[@zurH6BqOc*N)sN:ō7ܨ:&]N6m۶! 6իV#\aÆrJŚΞ="z&ضl¤IqB`֭P9\ 80v5z \Ѱa'M8[)Iq_"D0TN/dܸ[5'>efN'SLaڵ /ʕVXIqq1+Zj*0.E,^!_|9i&\J߱cǏtr7Y!DfD͛7GǠ1c7PYY!S;-M7Ed-_3idN&֭[ {_؇df͌3`lݺU_`%i޳e&mnc۶m>a#0uT\N ; ɀT}wk!==#Fr ]*Wd믿r?~<۷mW-SDǖ(0NȧFxb&L@NvǏ 5k9r$ZStN9^ ؽk7| .믿^qCs pBs:tk׮E׬fȐ!^׬YÐ!CA~~>Wa̚.>F!(㩣)2.SNAA>^KSYYCNntbNjcU?^Dmf-[tXl9/^BfLlMuM |He/cNj0d6$ Mp:] xp9"5 e,Jh.]m6ٓ[+WǑW|Y qaC5knw-6n䭿Ž܋]ƍy'E0e~~ψ#5kW|~uU9ـ^}eŜTE])usr5cH $4xpxܫe}߾Gym۶3O>rusu|ry9@ 歷͖[xWx衇se}w}QF<أC^I7o~˿s3t jM].G嗟-77f͚ѺU+\.~iРW^Փ&EE@۶zƉ{mI~~>o7`ΧdeeQUUM^^.gϞM@ѠD=ȝ$l6|~?GBqrL97F"<***}:vv@fxgxi}ɼyse욓; yyyT}VD|&+V*rss)++U)Dk-UoG<}BQ@AAV1˧}ӥKZhIǎ[Cg׉гg/x/tҫW/jZaÆTIyC 2{j!ng4i"64~3gD"YUYI^^gLܹP>WkgbLxhhڑGxg(+C8N0NCÎ8V< f1rHSՕNy$+E^={NxBG+xolz21h.<=ʋy9 %2//v;oөS'ZhAyi߾}@fxx|sޟ7&+Sʮ~ǐF^^4-jGZ".l233uKgR/os}qxqK"۷/s駟 O:uĒ%q,YO5jԈcǎK4[XXu먩aΜ햰ǜ9sfŊs=(1|>[]>Ypuuu̝>{_6yhC|W5o]Vvf#Gds{XU֘c={6#F@gt.t҅E/]'|:a>}0|u̙=޽{mÊ1| @0h ϛzō@1~m-[칳;njBЧO͝fӧOI% K}:wG-ߟw5ڵnKNuWM"\s .nw}c>,6sF/ 2sR<8nLm 2e .dʔ)uҥ }!nw-(<>50Z͛C/kjժ5 ٲkBV^CMM-nhLv҅?Zw-a}eܹkk{v&O-ނ錑pVVhذ!_+ʰfZjjjxp7#|^.5kQ.֭Zxѣ eV IDATG1˰]޽Y0u:ޟNzTe̝;1cǨ4GÜs1b w,2$"]:^bsСCX`! `Р,X0oCÏՊ5ozm/&s[&sIKKrQTԄ\&݊r X}SaBp?_}~Əysw%5\CW\<cIti'~-7}$Aؿ3vXsxjiy !2_F/qVQqꪤl (˶-R>8-X@5'$y}.u`Í01l$q[>s ={ɓ&TW -E΂*qG!d>B?Yar>~)&~a'~0o/j5tBK&EX|.6zOJljHRJB` ;q0 yz=e^4˘iZI/ ˟ӧر#oBkhAWgM$)zf]N O$'$XSɠ MVF5D":ߤ-1'ȗ_}׊շ%cʥK2m0:x)5Ѫ `J¢L2r|8R3+i` 'SZBNJjifZ\uC^"o?WQ_rd_|AVH6V[KvҮ<+=*L(Gwծ+UMߟˢ@yr˹{.z^lJdpI6 %tn.CYGxHH}][$C@葤Kɮ说&:wKՇ7.=pȏJP 牨G¦3q/&y$aD~c0;^={RԴ.'N}k*}5U(jZ+'ٶm+)uՕ=)jZtё縘ʠ"tǏe 5&F.䅀9vׯ#{"j_M=k>>=ʱ#GXv #F@eRZzM7RXX@3\jjjشq&qBR3_R.]եy Xph?(,,Ht'|>ܦME}eS}m޼MDJٲy4O-[6s}iԸI| (?q1!ȑ23=|X;c_۽ݍQ#-- A]]NΔ (¯Wk-:3gs砬 һΗQ Um8}Ak }]lb9db;c^Zq·,4M*:W+͹vޓT%r^ O->j I=^ݯ*ñlmoԗ\۴Snem?iӢ_g2h\܂ ұ>`0DQ]SùgG 8z[ mq׹iޢ9۴Kn*bň#4jيחO3R$s=na'u2Qf-ZڋvUotk(~5ܹ+Ymlx+ϱӜA+>!%%ػgrYV;WRJU=AL6m@L'cƎ1x5ܼE+CQQqV>g2P}ėX}8p 843ի`;dn~lHuΦbk.?֔w޽-';>jmlI ZHQ"Hf) ٱ\@)bE+*ziK }f?fwnv 8Ϗ!;sΜ^yO(3ˌFѣGӹs=3'@7#^A٬xLCpäS4:$0!k%%F+d1"ehr*#"9وN%.NRҥh4h4t>BlJ @$v{l~:@ÆJ Ղ f" ¯/=?"́oa|ßQFC~Aȫyvm{|_( -\4,? U_t40lH=QI?uQj9ԈZ:;j4:<{O?0KE;Ǐm?)*hE@7Qp 2;th%͚?r)2%9999rՊc>_Dz}4]GOy8 IXc1"m.j噧0y]᯼Ȳ\>C\ڣ/щ Ѕk,F$K0 CX:O}a,nڸcGбcZP1ǑҪ۷c޽dS'O vZ  U2dRZUJ/^/oV~])&!!T[lvїA]$1` vl0p$ď,rt}hH;7~Ǹҭ{OxqKSçzq~UJ2|(LE z zLCdrH:iG*rV)]OpM w)- KUb0XNtt  л3g>O|E)p e(8w”MN梢 .ITʵt|v,sT@ο ^o/ZO7V΂e3os^* ].hl#Q#B xYS'N&h/O+s+~ 14:>wlv.RZZn ûNNCX[K+ƑtoE/#oDEE !L)I.3ѕ$(IEݎ(A:񥩫s5'4\*Hv &c/b9 KHFir I:-tۇd6mEɄfs FGGimII.ĩ*K TWSD~NKKs uiiiއAP*ö_v5I﫮M6Ï 6]M2[fӦmPJ^~>[Q)+5akA#h(-Fn6mڔrAOAƍw<4ueF#O= O>qqq+ѝMk')g/OӢYS=T2dՃr/LFϧ(/uBQ8gXHslqR,HzTz W Sf8vL&7WbQ䩿>˖mAP[,2_|XW&?_P\x͛=oV=AnW;'@}(  ̔ɓ:dW; 1H݅ !FtݻH1eS;>];~I L[.2ɎaTRc!Ks4 qNf_.dذQa1߅] %r>D#6JK p!PNGDD"t'0/D#)(1!A`,4J(QbDbT9 h<(!"$!;ՠE8zoy o{ c342`Srt;]\.UÇӼY2FW\\hfj)*.F#hA׻r+x5%5A:TIjErүoO["6gcEddwn Nrk$EEEqA?w0p@:HTT$ȾӨ:deecŀ.NFn1 Mf"", FØQXg|xMf.|ӦѨ񘓓G .._{8]3FqK8,/<0ѼnœlD^*6#M6嚱ccvrҽ[7~XVR/ /VRazwtb6[0ZDta" L 9U7ց N<2L (,c1+ec9pu11S\\Baa!&zwiM#j 8A$)Љ,6v]測Kj1YNE$Q ׀/]%UYsa:6K)`&Qi/иX_f}OLre:ʬ$Ei9=ncjuvF޽hZbj4huZvVss IT``Դ>;~W^^+_۾ \9bi]޾NGjjI߂eRRpA@8t 8̂VE<]CXD$Đܴ9:gRZj@=2y(pB&Le䥗_7dtԉgy&M1e KqOG!5K#Ε%BfPQϔ#PzZCqAFǏ. } ,X,lvkUe2q)FkS$!N9L]:MlslRHf&*蜓CA=_0UYDaDXTaxY9i"bS1ĥѨcؤDh`a]AfMhJKh֢9bvAƍEUˑ$A>$6t\TӍ$~;}Qe59_e:íۿ\WIs_֓'ܹt+|= _lӴQê3Ľz^Ά~bŋަW,x$IHf\b&ѭk'!X=7b|,^;=z III<3jќѣ|MTh&5kwS;vx_u>z.ȕlJ`F.̪:JZ(O\ʡ|׀R'lqyʰ{hFȱR6:AP꫷i!'O*ۗثZ ܂Xʅ6oñ\![lŠ<: IDATJ&^;^СCگ Ȩ(.[_>7|˝w̩{LO-8eLib^/gfܬnA@mb,0dvcl- " .̦`7Rj`ctq=¢7EhJDdׅ/!, s65RU_gA0py,$k߻Y: nb>˞,Ⱥd}VS1&m;^Ccv&%348Bt}}nׂVmiώ+.=J8[r!ZXXVNE%]=~l`2};fVIVl( Ic4I杅c Ox},E&NeghwZAǟE]FChP{Y\*C 7NbU⧘O8ߓS\3~Mq̹Et k*S.byfZb"IHR c^nDTrr ;[Y68]eq6=$(^;ul&7,CW;!h9s :NGdd$5PP\\J0Z) |mTa.Yo>! g0W51xneBN;xmkV&- I:L&:˚5ۀ8V!Im+)+ۄNfu?GfN (e DcWRN%9[+0s #ʬATRCw<,xE]՗ҒJvBݮm6^ƌ𣏸i٢yݫ8ϋ+OaQVܟW k~I58OTtrSlgTj3@d@խv &G2  QQXE +*G+NTJ&,ce EkǴ@le9Jb мy fVNc玜:y84hVu@P loZLsW]qR[+[‹L50wirw+c~ɕ}tڵQf,4H$7h%=Nَׅd\ޫ7CeŜ=NYfλK;?[:zh_J4ؠI`Oh}"I"͛ ᄅEPRJv3 ӯߟdff13k`X;z}v_œAP"ͅse5q龜d߾t$e8 (ͅM@F Q֜ 0Z-/.$qAN:d$<, FS^*9etaQzދ`/фª1PV_zJ"))dfdVEӒوp5MSe(kB]͇Z}lTE nG<5lؐxZ2aVb1Y̽{ 3|P#">‚"-5ןAmBdJ QQއ^ᥗ_a׏ŋSTϴ[n`/Qogҥ}A.AhA QH&+ħw[-heݽ 8fF)(MK()Yټ`ѣGjY\FdŊ@ 9غu PQ~Br0Q S~2~\ pEFp+@!cc3f"OAjޮcǎQUڎ&1ʮNL5bIiU:D糌 5kFJJ NYi1M6 p\%Ɋ@&J(_2HCAt;?%e)+V+&];vkNr`Z)++ײ~ꪪx^oAFZc M;J AFjD" GS@svOG!(ŵ*j*.wܕ}z,_l$))&?IΜy?e?~a6+z+{90\vU>Ɵ_S|}2G ra2`"P$m~UXX@IIqʓEHLfsr!hZ DV/ lpG/g͜-'~Wv5)Zq[$dYĉcҪejj).39D"nu:.{ h0$aE~dvQj"I2eebZ]Z2͆]: @ 9RrTB[( ȲL7!X`P6q 4s>Զ] % лWOZ%cKc 0Y,˴hޜSo ᄏ+1u Μ=˓O<2'k^7j{+ҿ_wWm?5  nlJW[бc'ƏK4IF#k~A^ׯ˖-+iÙ},+̜9ŋǟ0e$"}z  2ZlEZLn}x)WgEuY##hAƒVܘ#ZI>OddNq'VeeMFe/\(o[S)3Q*$S?`"vLvϚź_aHx7um\u]K)2(*ֈO?<:A$ đ#G9CF]P%R#8:cECVVNC56ӴIcrsrtl6.NXXl6UZRɤF]ZR̈,U/#Ni(m{-6&T]hyWVKLL FXۉE֫FXK}]~Cy)!2_Οf ՟q,˴ll Nwf3Z **;]~\84tj&TNTt["/7ѹT JI!,"kg\:wrarnf \جV"##r^5qGv,1MjsY.TT.ܫ=>Ɏה0lhuzZ$<TO.zq>ZKb, tal#"R8oO[TT qHaa!SNupj̅PCkPUq.CSYU(RPP@ln`0PPP@|\9_Ttq\ڕ6m."2*}.0j9v0]tԈ ukPUq.CsfUW&h$I VVwFАҚsoE?~zyMXRSR9~H.\h.}R/kWE}KïeFcy"7x]u!@TttȲ3O=Ŵ[nf}ڋ!2*vP@}?zQӿ*KT66}vVx'O_έ$.~v/K.qMҭ@vv6{aȐ!?T$y,[]vQTTDDD:t`ڴi "CCED]]5 tڕsҬys1lp~a}]^b2>C+eڴ?lpfu74toG`0УG~7iӭkWz-zS]v1}4vgT̗.nV6ḷv]mMIeBۭ@tkFrkU{e2e2_|zH/_~]Ǐ'!!}ȉ'jlVXXHiii;5x'i޼9o_+Wǀr˲qع;wu6VZEj",,(|X{/^D&Mx=̝dffȯ`:RzQ|Tg﹬[Wwٳ 8hŭoC7gs7ѷo_z=III̙3{e◀j2aD>cƌ/´i5j46z/77xc1o={֧;ɡqt:ɩYQQiii~20CNuN<Ʉ ҠAZ-111 <;PĚ8q7nd)L4͛7ߵkfc1c&;vcDZeƍԩ* )NHL䦛nرc[neĉՋYX, ߟw}>{D[fΘΩSO<3xjܱÏ0f5<.۶qz7ٳoJ^Y[A=ؾ#%5EK!6.EC-{5LL: b0u4k_y,cΜ9^Obb"Ϟ˯anܬ7lxۯj ϔ)9|빳?;Ξ=˰ayu3ffϞáC<۸q'Nbʔؼ?A.Hm εN`Lr'NbӦM_jj}R˟O=ɛo/bђ%<ēϟ|{ xȷUo@',Ӵi2'%%Uu?N޽+=^d2!K92o,|?ė_린d{˖-}v|G\r,[̧;.LDx82Lj{?TT Yfl޼S'O*s_uZ-FcǎފyX1ct֮YIY e fcǎG2x Wy y\DٳgXx1W\qK/]wl[ngE%t:-& Iٸic%s{,˼j2{?Zh1))|ڷo{*/ݻvC67l#alڸ1#DGGt V,]^RFtTzdO<̿+zE\lr+A,CʾWVY>&LK#vdYBD-ZĄ }N}yἊ Yj;wduGYaLfe}RSSt*n^}=FcX 3GU e y`1L;z˗q]wۋ8uUkgvA׮8s&_"Fpkׯ?˗-o:_SY]gf#ʾHFv;#G <, ~7п?o]?nkÞ{} I\j4$ɺ5 sNM1w݈/[~b)z FVV]Ƹ=Tj6/HfF&z¢"x!yiӺ8î]8qkǏgmE ̸Vf4mɤ:tC /Ě/ѢJ~g lTu?~_[;_|qWK2KllY'{Ykժ͚5oAe6l@bb"))^ݮ/ޝaïv]nM^^ϛл۶mwWџȤIOޕF,.Hmks9 IDAT (LpݛL_?R˟Y-پm;-[jO3_Y|PU$5%e 駟)_*fee} :hͣi&;q'|U>宻ҠA">/ (c߱qq(K}| Fjc0ʝC?` lܸg{//ȀVSlKlgEEGc4}]v@Nsrrtէ>ӧOӲeKN}Yx'2dK/T.tߥD3Q4Zm.Hm w`(OGo퐯QU/|U/o=1g6/~#GwƏg{W_nfgĈȨ*s2*5:t-[}(..&>>Jh`l6[S`82M6ekXfM@ZynVzq˴HHH4Rb|& (SNeĈ=w}7}E \NǓG|i3z5; (X,"""0YVk'htA)K-&N>FΝX ,xWUU]v_5ՉSn_dvJ|\<:n]xcP>TRPPZJ겎ve~T՟ylȐ s(K/W|KWS@WyY‹{Q4_xYZ~4i"~)6mjRTTg}3n*DL&W=Ö-[E&Mx v֕ua6Yf-]^T"$%%qv;gΜ!))f>cYj?eJhт͛cXz5qqqdeewc2oرcC'I"\;jՊ-Z`Z'=TCyJX̞3̧`ѣWb?09#e˖v]Vf&)))ߊ݇+뮻lyi;3s2mttkM71T6S1ïL6#G2euYo1) ooX[{AL&ΘN~~>̜>̽V6kĉطo?&Md\**d-z2ids=K^=<ʚ䒋*駟1c:\wu|'{=!.Hm׋P;GMs˛@o!..+esn]JJjC?gΤo߾d_XfԋeUUUr|gQ֯瑇^zoSh 9|$,,\Z':gV~R >y}='(^9 Q9p-fBvU\(>P^꺎e+B~UoQl6ިL.?g~UQ)oJXmh'GD}DGŸ9ߚr˴i|-L1nɫ=1暱< L>.]m\(PQO]ՋUԗruG~UmJKK9r($sɥ]x>s];wiIkӆ:8d:33͹]cE?w[^/I>@,7kV! -B=>TO5BEv']u;Ӹi36l&ef 2iPo$ItO(U>C5X1DYf0h(. lun'2@c ԇr*sԋ5\"9&5MnVpNp 6d 4  "AAxO|܇ -jW \5DGs~ZRzR]tlLv8_WzO՛𫊊Jŧ@j"yIŭ*m%^=P^'n=/%n;M*旷g>=|Wyd*o{U[;***:ro<#އg/Ȟ# Ar`W|y_)/g=Rv5_J\*{RxF^)fbx ;䎮[5 C(P\HqG]ų&S&inߣFqMN5kM7 ;+۷1h0"#Oѽ[JfoV^^W`(**"""3iӦMaرc+WS=1c:qqqštqP0b䨠 6.Ýx.bfΘAÆ vGgQgUe дoU"0^}5F09 EKE?SfQ8PBQVDQ>u"***p<ս*))᫯__Ir2eJ*ygT@믿[׮auz׽/zzӉh4u6~'n7 {_g),,b͚5Jν;8KPu`"ظ3,STTė_~_[5O0 ߪEao{5|A tH`gBmUY?OWbhUTTTBX0?C#v&o+p=߾}; l*:u/

/ֲ{n/yӧOӨQ#n5nݺP\\˯ѼY3nڶvW_{V-[1iDaU\!L4n}zhe?7|S[g֭DTZhǏsǜ9~^dB%%K>m:^څg|M/_ҥHMIeCsן,\<̳cF>gqEQQ_|%:ur=[bڵ_^+㡇aaa!j@ʀF,_;de^o ~{ qwytx+sfݗ_S'Ή?璔 2^DM\Z]1r#GfÏ2wEiҤ 7n_%!!-[zw݉(˯zmKJ CQQq~HEv;#06]^π>S>}Ǝ!>{WϜdРAU s;r0b(q'yyw=.ݻbܸ F޽{ g e@E&L@dd${"''nYY0pK<E ^`w^0ydٳg]+n3qA{.G:F0nXm^SQQQMs/^Bv0%; ?W^y~뎏^I'^CHBA.HQ4П"EA.ʋHHP ((H `=By-6g̷MٙfvVo4[ àcǎ7WbP:.Xl9މe?=v K~˗Soݽ{J3a\r}VJCVVsC+->n"wAmNܼy?"J߿Ͼēg2OM&rW+ƖھAn^6'XvXf &NBH^m999xO?ju֘>}:*UЊ .\z !͚5[oUّPnsÞ-[z2O{k$/?_FÆ K",, 5P9*V>}̙ӊz ֯Rq0o߾Xl[Ntt4n߹#i{괩< | I*F~iJl… yhغmu+0 dd}*_9@D?VsWbccnݷl{mڴQ~(*%K N8}">>]t={u<ÈGΝcǎb[~}@bƌXr%^}Ư7۷o믿-ꩨ믿={`믿bرʊ:^f3f}ʦW\ݺG-[GSlM<k֬EFFܹիW#11I1`o{nXVdffbݺux}әTZUVn+WZhu#'';^ܿSN4icƌ;+ $..4iظq#4i,)[L\rvoSM(9rV/J*đ#_K.#//ׯhdeKAʵe Տ}+͛7`XcB^E٘7o.U3Çꫯbĉ~?n81GŴi&O['f3z!hCU=:Zv-Ν;|-[={/[E)jժٳ'M3 -h۶-m۶|)[cƌqݬ|LycƌERR"zꅗ_~s"-- ªU1z4-[СC{])}vF6 e,^?رgϞELL ~;@멨֑oի_1:fsr'\M!:tQ4*'B(|ʑ#11=ъmըQʕòeT=.s-L<_Oc u~'9V9w-멨ב%''cVQSǏRعk7g{bѣѬY3 Νѣ1uT?S]_w_fôi|"sBhYOEk׮:$СC! 1l0t ֭[~GqQ :V\^zF5j@ip5ؿ?ƍш g+@z*juhDl:| :t* Խ;ݻ7}рm=cذ0ƍQ}x"vEu>`ʔ)5kV@+svT_~[.<Ց{.dH:tԿ (O:*^*JjՂ۷ϧwylB:1$10^:uP߸lθGVo\qWtСCGNV;&f[-߸en:3Uja#P[y]d`dljs]v!,, QQQBdd$N(8E*g ~KJhJ&t'جV<:x@ԖdBHH9f^ONN_a0 eT9-)m*ɊtB 7su>hJJ%?Aٌ6®qѪU+4?<PLw-IΗk20'I5 D|ˢ p:8y8: M0zM Hȑ9B7d}_~.;w1VWcG`HזeMЯE3޹Z{OC[a9u}m^Z>x MaYaJZ9#-ݬLz5fNy,B1δK ߾ҥwF N qMmahEVvf7R4]P_ SΡTt)TR{JdggcӆoqU\p'xO 8O-Η.Bcfpx+6=W_Cǎӱtyt8 N;N|~t"yn*Ēo`PrG^?cxMkhܷs3 ?>$N8NmZ1kd|Բ)4muTux!~z B##Ѽ]{wl?w^*Bl1x}[L1 ׮\Lxfn^~-ageFX/} wn^ׯ233!lY`𻯜>~O42Jq?&oݼi#_m` #cN /?(.;G-g#؇g.ӹg$f6a-M{[?۾(;p8ݧ? R/j쑖z v)yuvvyp5/ #_[k?{OZ*OXR`@~^;vKf~G/wBCXf}`xtp,|}̌ HNLDCek߫7vCݤ$u^`w8`0p?K73#.Alן~: X4h̝Áw+xk܏ǟAnؼm;{kVyăݺco׭[p9J'ԤRɥ3?FZh6IHXyapI=tes?ږ$IF"FӒ| :9rf#<<\BKnx|x^[Kp|yp:@!iO?4p u?dbm6t v3`Zwڻ3)$ڵCvx^N SOCފr= f|(ɯ?\| ;6b}`@:uPv8usW_ ~n=v |<~:~ -WK`w8܄װ/'-T >~I?䱿]'XB?sX:mkPIyEt/i\xuMӸF>[1anW*V˧,n[NV<χzE6TL,#:.YwTL,\zē񨕐mkVc}S9}E 5\ݥp,RtMFݯ!,"q6J#mDDEQ %jI!"KD%cKεR%O2*,̓й7j-Ⱦ~䎰1ཇ]t6֬ `ԨQ;w.U.,, +W^E0f v;W)}\ժXصa=JJj*WVy y965}[N]a70a[3 RU7:|5h07m?oGvemyF#qf o?_OĶ5QY3V'lY\IME a2eFXkF51g$T[Wg?*6ÃBTt4 -@] 5Z^+Ruxغz?܉;w`v= x j%&t=l%`P͖*Z%K0i^fyi`:Ӫ݂ݎs0W=$=vYmrk p7vXL&;wÍ7`2`20zhB&aڣ/ٯ{{6D>}$Gr^j`C`:ŽJhtBWX.:>쁜C(j#r8p5t:jl6v9>+F?~/eR>EgA$bV9{/MUDq tߟ N DWY&?hkk޿Y)ERY׸U@KmluU2!J{NK&4:+`BW*;M⅙E|YlQCq*UN…h'- QT NW5 Z!B(8E*g ~KJh((|Wq +VJnَYhT"44@cв Fq*U@z]L5BFt2EЩ)"T:;v^|#oj_P"}˵xCFiK4ưG_K ]C[%Ar-J{xRN芛5 vRE -/J(NeZUɄgbN97vV<Թ'-߬\B|=dԫ[Ӯ {z:w  ,y:ZV1hY`8E*g ~KJhhw'-L5 /mP~3' 㫥еK2f͜ QJm_dJv%֎qۄ@|tb]S@0V1hY`8E*g ~KJh(Ukl2xc 7jIld$]?d@ʕM|EX{Z6G7&!Ϸ? tF_*51J 8/Ж@ӡC:4v;H9w ը#"2 ۓsTLЇ5?PlYL NgbEQ* &?EjӡC:O:c0`0Fj$93KʗGxX >ݻ7#1s8nү/1gmT' 443?|KV}xq +T%ONJ::tQ$AtBTt {׭C5ʷXt8aYXv5jBz7n1I&|w̞5 #*,Oܹzy{DKբСCGIaޅB`\jR.bW>1 wQzuu Ǝp^6 1118VנǭX0SEk"a452 vd:d_ oСC" IY9ض}1iȹ'Oc>4w9 Ǝ3gaYh۩'0| i-슫׮kX@po>@CzСC{*҅G@Hv2{nf:iZ`4 leVhѦ3n_U16i+?l_Z}M)O ݽ 5:vs'@cв Fq*U@z]QPqYT#.<7}yhxbJW!7CQj=J5AC R:As]b#4%]ޖ돿_ zwh@me6SYrⷤ늎IBʟ;; Ԛ~:tСآl%mS"{tСCabi̡,ʂ%&s({Hk|p#O //J(NeCߒ6Z@+:=k ^ ǿ+!( (nB+, vz]hobbKRh -Ѕ'~k]EdIQp{7̂A'xqHJ$|*~)x! mcњZ+/.!b@3J>BD܋>czMAA]":dT+s*N~&O`2H$'q52l FO1 -ɾ͐&O֒\I3#HKm 6SjOB–?䐨 `*Pm/yԏҤȎA0;)%8dqEAɠA\'B@$ISȓ+'/) txKDy|e3pg@a13H1<^7ni'JiG2QnBWʗ!stȑ99HȵJ2)/۶2JhO}49piLҳc5Ck+IT,H&4LYQ Ɓ83RC ٌPXa:7V=Ў)y-IT#6.neeLJPAaN5#_q%>1ЉBɓ;o@x%TObO I5|g<|LIru9I-%'z2rj :N', V+FjɇnEHHUٶ }T)|mv~6!fe^P`C218TV&u,BO`60mPcOz,77#J=8x'S7A"#IBj VYV J- 1An4"$$);櫩 I⚳3*꫑Q/_멊VIˑ6|9Rp3ɑ:qD;J>qh @ ډ OJKY$#MԔQ(ϣFT99]mJ볩J7k>8Ms+mZ*+tv3E\33QUfNfTE[n 52b63fyN{aU8闎;Y(UeἘ|Ц=a[qx&w׀'TX,u+II0MZ۽ѱqY 'NCeJj%&tJT#%VI­ ΰIM·$sɒ:y.âVnBqDrN@d1|)!y?H^ IDAT f/=-+˱Z2<~Q's M2IG6g25}90zb͛(;FDQx~iXpV:p1+#=wlxACI1ǎl2# U>q*ġ8v*{A |+kX8MF,z1Q!h@C]\v׮]E K~dF:uqߓZ*L& Ν#)HF:6QdLIBYt)~uFԹQ [$}/m 7(Ѫ[Jx M ZX9CƠ,qg>,T5NP't|qoD\R2JfwBO/&(!-ƎJ5_ܐmv;^zTS_kթ3OqWv]xusw:uqITREl[/, jEDD$ԫOPe[PjF'5H,0Uk ik4m`ѷ۱j6o8jýcuȭ\Ԩnvv6""#a0 *yd2!<,Yْ6׉\.)Bӧѣg{ԬUJDŽnB呟բxXrsP\Yߺ! Vv(mD ųjIH$HDA$TEs"tY=~JzP v2HNL `Jc5ɸELL J~\9Wٔ #3t`˗q =q+Vl„q8)@D91C F'.([f@r]~ ͜3>7&NK O;!C#8'mڶkpQ=&]b5#U/A>Jϖ_|J)(een*%,C!Á(W;GVV&oNFsw~%.;o 6`A(sw9Jtx^éOwAnn>1ك)o./c[e/wwdK?Q@ 1#&܈܌ qiߺmwfx8Ndedl6Ò/p!%E0dffا_xj.\2ς Yԯ jPd$bGړ/I°#0\^9s0ͶfLRBۊ7GNиax1^|X,C"NNW(QFfֻgOl | ۡaB<ԣ;۳ԯrhn믾B)l6=F !㏀WhѬ)Kvc3ѵS'4iC ⍫݅ ?'fy+]'/F0hMR.op'6pժU$n'U۶m  ׎ϫlRkL+x׎T>%U,Ch2ޑP^F4bR <2Q`~]|^}Udedf"]L8 -̯{WJL{ӆX0>f͙իVc;+#47񂰰p\>!C}b՚Δ]VI9Y-Py}N{ P.5og񃏢\ H,V8W7<WHjjAS"5L9 SE;y5>]2~>){ev+GA׬gbUE)'unb8} gazC)뙽s׆+v͚7%rjq95 t:s[.C\Zkʕ4ׄIJhq)+aw& qݯr 4l؈|jddd`SObwoDFBFF:Yaq*]iF?v {3>BZī;S)؆̏qM,_e˖cK; `n޽'YV͚1nxhذ!h?7˖t (+W>t:() EI'':g2|>!I %uǃB+qPTR(+#SZW*plV:7+cĉ㙰xOz]j._lV ;vEWXO= @},VHfXaضfZ-cMH NwedmaU"B'0mvbKpf'!pAԏ܁ґ@MZ-l6m6ja6V+,V+g[9KoWgdOyr#!-nx}z"N9o^9J U`D3 t.Vn@sl^ x2Y5f f2 |:{6F}YYY}QԟZ?ʕ*8sp<]`?&6h[ c09s@@wЧWO ovu"#B&:W=9|cFBlL,~ 9p:{҄zS[iQ|90 PRE5x"ܹ AY"Ø//<v:xcdk7c0z8rMv=%%~={㝷_b a5%KTUb=xqAeKNmq*' (Q(6eԩĘίYXK`f ͯVߟE`W_*+bYFcŪ0s7 l[-|ػw6}Χt=uJ r%iR*q’[￧Q9x٩褿Bl6!,"׮]/ߥQq:qUl6S Se}"HDr2|~%6|GF EBd%r|`&J<6$t@H֥]@mVCM/.jEbR"yZGM4yyE~^pg)p)wL>%M͘ a^yl1 "ifXE.3"9w+͘!/q8@r{z*vhڬ8JB` L5O!r<]Krk>2c$EN.r"e^iiƑp)ghRj _-[ԋ̼Û_#\+9f3x)T\i/j(gyo;"f&lBhx|Gc5(S4VZ .?-a[{ FCWj[,ػs ~}it fܾ}|?*5xF0Oxф'N"66FQ"ZBYxXe2Q1Qex]Fb8>e8!\YorG[z!lR/ϒӁ^ٳK/aw'okV%?y\N8[ヌ{BCpZ ?/|X-m6^v;l.BPreݻViZ?w?nFժ rA8:j[׮]+ڬH+Orrg/W_.YP6kO>蘜 f\^Cժt\j N^pMzxn]OCpv:rk0~ }Zq%*o& %PeDdNK䋲$d`4NL;} -\.Gfz$bcca40Éc޹¹HȿpI}1rs7`0\cmjy?֮[ܻx,ܺ߮GY'N~t€r,W.rs³!>>Ndmp$E/h+CSFU!SILSx2d }/3 YG8y(oF##NI|{G2/uBFq6rs\?kϱI1<#aC޴Sq)qA8m(,GwU!c⍒>ҍ0/z9ioLNEzz:l q}1xP6Q]Ni^Ë\Ozf~AO?[P^=]5p:vs>Q]|5/U6v E+rK,l6IfxwI^22`DO79DL HȈ[{WcBR;!<))[=<$%iC2HI-H̽j>7 ڐ%q'yLJ:l6|rE5Ɍ0DFFpWepwDfsG_]*mKD_`PAM@SU³Z0 BBB8un}_1ZmrN 䧰OVU"eAE WOZ<-_ 5N\P|dNO}OC\zJAGB#qje"rzRSI`ą .7]VZ"_n_@0 ^bы*-:d KKt뎁clp%_C19s%IGC8U6ڲQhGL!8bWr"Y:",#S++M>T!(HD%M,T}'cdiOKJc?INpSū忁R^o22(/ʢ>AQ^.Vц  'VKQ7jƉu%,!-̟rQT\dE1o5NrNG-&$j dFBD5)\<`f$V&.ɉ-VB82*Ԓ=r(GvTFzOI;12嫾]i7PC U(^U/|G J2?I'Y-s#rʼTDx@z\J&n~9%C㗮 UYO&Iކ Jd&?LydiUi ]jvF$%x|~Bb(|鲩*~S18D(^)?aP&RD$$Lؾ9|wRF3}!}9͔M|KP!\.XE.xU i=~zPftEM'e"!/!]|S&n.)?ɛ*ZG1 p"rFN">ђp**7cb!r u|#t\-(hH|S@Wة&JƧwd&wT(%HQ(Plc,Mh(d J<G7+OUTlRy&Ttf U)Kj,"&q~`*d>QDE=y_T:~:rFJmlQΒU⇾H22vO1i 2}b v;6 8vL*Zc ܰ Zg%dyG8d,%  mP}@'zqOX z|2_8!aZơ% ,ŧA @8R"ySl L I&%IX"WP {uǖ4$v/;X+8X IDATΥ$Ȉ'5`rZ ;^<,-JWDuѓgO]WIo > `3#Hi+DH&u%%p2A q*E8@HtC+&t \A[H)Q&wt$C8 M@{(r[;GԮqERry"1)GPۜl'֥tb@UD!$/3iS>IPUQH7 /E#|GN!r8 jQ$Z{4}bڴy+ .vǃmde7Z?jy I ==~Bv퐖va 8@VíV/!J$ ULB{aAhۦ w튷Oǝ۷Uؗk;0o\mlQ<~ >n]O؟J*\o4]u(T򺕪^2UV}Z+bEtG$ q9|gW8Lup`isNMt/^Mٳ @}vv31ȖKH|/|ņY=Wo!Xv-y|:w[ E {o»d'n%toN7xm۵Ý;XlϟSSH`M_v _v i fW0[-Y^BFoLʛ7Q&K2Q+3KDwҶlytEu rf5JP>l8ܷ93@^2nܸɝ/N'tWbK9w"S'B`ZѷO_ؾG~Y̌ L|}"ztÆSĉ^|7_GdB\+tn[7ob옱٣'F׮fcm߯/틟 p~ ~itCC%!#'`8}Kڶs}kۖz? |i[nEp: ĉ>ȖCamxWo~~ھk׮]xѱC 8R}PVzݺvőGŖ~@׮]m6Vݻ8|v8 h՚Gغe3=wǎmظ/^O?ݻc{Ip ѩCGpN!7.@rs%9y ''۵CzzLNױoѩS2BfT(_ƽɓ`x/cxa\r Vݺt͛ѣ[7 ;w!lt0h`[8N6&)|?ԏ/k۷ʤhy9KVz\۴kZZOoI[ʞ{>-s/E!grl%Ѿ};,_ gϞ̛7sxgwmDWhDnn.Χ1f,YY`!j׮7`С5kvޅaÞ@\q,s4HhסqEw>+VرcdW`֬Yx7uV <WI k Vw22|r4j<.͟?XnN? H`4sa7n-ZLL> ?9s i$-rˣaÆسǵRxYW_hĉۻ 6DreB<]&L… `>?:u ]d7whڴ)ϛHLLľ@={ƑҥD=ww[cyHLL͛ѴY3̛7Lr={7l EDA0Oѥkln;j[-tu ç-Zß8|-cs]SmS񒉄D(DQDA^҃,4RH4$%!R{E6ԃ_ Vц%fRtF|mΝ.N8>}"""=;(gKl I EiT Ă@γܝxSإ  1BBif-cv73%~p<;<=Y(N}s@P`ذ4l̜9 Ç߁>ЉDB8y$fΘ B{ϜwATbذ;pZ9udd$ߏ*=g3V#yQU]/`% 8{,vJ̜5ym|Ìe¾Pu cǎ5ֲ af9z'}@~93f`ǎi`0aMzt:|( 1:AgΜӧCPbԩ,4?9́RY3qI4ƌo^3{M7QSSHkG8~3e^>J%FVs}>s O̞3#úNP;x`]ve2숵J/ƝƜ"uT*O؍iL2UxWt~2n GW3ڷ-Rݿf]5miD92rhjjBHh(h+zzFQHQ\.~RWz_ }QTx'ѧOoyw}y~V)_@LV:Әer 7W/-Btt4z)c{ M5YΤ5 }hQXx1œ˞ɓ'|rl,Vv sܞ_Gp3=+k>c[p] ,@KG?=͕#DGGڵkHHH.:Yu3l\wccc8 \! k29SC )vnTv1b9#?U wl^ EV#JAZ3&@mm(hu:jjD~^xDD `RC83?[bYxؙi?-C={H$LeVSJECT ׯYlaǜ9s3EzD`%ovA@ppzoR:SpHA@MuEQ__g>HKKgnݻ!!1ϑnvȬ={Zʼ='QRDcchf!NMrɪGeUf_#ӹzuŁIGEi7:: n씜{c?)Td :kff&o ---ؾ}ݹ7(++N;T*0ظa#R)aÆhjj޽{GDtt4JʠCeVh4lܸ jiqǰg^z`;Ŷ̔fnK3Cf5֯_TjmCSs36o֘س>.YwNC\|\{7wdž 0i$ &NowӜd޽~{m(lǀif-Xn= `9fl޴ټx&kHl޴ cƌa}_~%v܉ֶV/ /4bMo֙uZ:ퟻuq /elwDnfqsp8Ƒ]Ǎ{h̰v<8mՉuEಌ7j.du-1 >S5.x2Ec¶}E sAZZ̀ŋm?G> V2y3tzd]Us?o7iJg: ]vE߾}-?r daӹl9>v{###ƾ%>ӧOcŒe9zwh 9z#F41n8L$ OXp~āzua5`-jEQvg'Lx봇Nw$m#̟{IqF~=ݷUd>^jWDV#Zo^8Πc)sz~<ճ'F8pSg.:CGNuq꽳Lp/_rR9H!VޙϮvXG<1Nq|mxCnJȧ6Oo:mSyW8o.:N7imE'!1? m_GW͑7;)8bXbye'/!Cu{0H9b&L-8s\I3?7F_T G,镙yjz">!bȫx?Uze%HHrq|u+ĹǕ'O@cC#X4ͼ_vPqqhaB!dJ_Kz=JK -޹nyCn qyHhmn<0G{ʡr^yqc:{ODmJ!!O_t?_ņEǕֳs"a""Voyn.Ra e%%C;.DEEٕg WXkO]YyGRE!.6f477#;>dbbbw\= C+0ѮXBOK#CRmHHiC'34Pd]>rެv= * |x܌cǎbРN_@9v2-o|B"Ǝ I))[_ёj7KHL w!)9 uͪ]YXRx IDT )1AA ^ALLȗ$j"K0)hlj\;,i"T*ZR FsS3jG#JV8Z.]\oes%OïRmyK-ֳ5Փ(m+׸1'txD(3^3sNYY)L9LJ Z$ZJPU?dL uS+D'M""]4{pTWW4 DDD@BR׮P/MK hɓ1;W}ٹ$cjU!/NzR2 -Y*+--Eq522҉Cu\aRJKNOKMA *?m!#-ͮ3BRI ̟/fS1|˝+WpA0Q*&R)aM z[V:%AqB$RHecKcc#.׫RE!%9 vu uPT\jTU1=@*DTHNJBhh ~eiU]S*c˄BnAYEP[WGDXTr_moqtV ѐʂv^ArÆ*"}8eXUIi3uvezb)tՠߢ{ NA@X( 5Q4=({hF~E <<H@]]=]C~n郀 FBƻW)陖gI2PN]jzfܙZdz˙Kf΁ L{r jk%=N[。礔4Ʃ߱'!a{6ʠF $_RRPo(׫jͺuݻuȑ!l̉s>>D"UD«7i#G\ ?q@ݙ^0q!'Gѕ=wwI\=f_׉0p@ͭfq2^ȁZUxyգ;Rv)_HT*ǠAO݌Ys ah k--4Q5uy|pc)4]ACܹ! ` j4PD,\?s?FqAI2A4ً6=ztG`@ExDu/MԔ^)OH^+gdr:i υ#g/5=ӦWz奥7FDF0R/Gt\2:u5Y·Yَ_rj3gb?0r`7ωm?* JNAhh(8wwlΏ}Q93OB(+)Aq5Rz'H2{nhZ5t]N$W .Jki  &sP(H$hmmEyy9~Wn`\rHhƮA^EJI)Ə뺮_~E>]ރRt>Nu}v v_{c mnڀqžrH[4Zt ׆kEŅfsۖ z $s a`5z"!*)*(96n`ϟM驩P@բu% IDAT{ qY[ׅ 9E޽s%]XP[łBtR\r-d < JV/CZtz_jоݱe\:bo Gsoz]4/>"뚜ce F JBkm@c14T-694)>!HDT۾:\GbBFju :-R) $ \nqb'|>W8sft0?ס-\={qr KJZ3o\f7-fO'#ob"?sn=u:(zTS0aD<c󚔺qc ?FRB~,X7JH>UUU;VRAVMpy;mr M%p&A8vFzuѬ⺺zAAA?~5@q eQ(4f $6$z\\yz=4 jjkQ_uK T*BCC؄6mkV@0yMEBLzftc(#Xr:eB0trtr2t:h9mL{̽s"v47FvB\\f͞ 7~vGəK);奙c6׭z\ɵerpl߾R+-t]֖q:w9_w!Up,t}x/3<˗[jhh@Dfؾc8B| 6cǏ[@w.;'OQqCg G8f4+ JαBs{m[dBP,6t cF@:m:Rt:&L!C ŸJQ*@*BӢ |gy< sSLJ-%=î:wvFX}tG!ɐa/#9q$|Ev3x u(u7oG\'\ ogy{G=.qb^Z^\rh4TW31j_~%V^ׯw[kltiZԠeee())+Wlt]vM.92 k?+>#gXCHe%\1_UL4\%1 8HlC#(,4tN]%h Q!ё8sJ- HL.DB ]P(ЀǏ#&3zfxy= JvWNh։sc56G=e|ʕLytupaa!sfKm #ٳp?`?ojDfg.1.i\(*tk7/uux_ ǟȿx~˞x\,G%D;Top{VZZPH$ :u Ŏ;W EA" * e(-Եw^ٳeeeݻW_!?SСCjCII @.cڴi.몸ZiUUUFUUڠP(lUqR.HIIϾB&;祚r(++ɓ'!Uvux.}/WmZre2+S`eEwP~cWm3,R=hY(hy8Z }AeIаNSyYyrR9n[DS^$Y@K\$a lQ(dH QhmmExX8N D ɫ ^pcϜ[ʁ';~}lN=1c6^̟лSgLZ y~3f෿ \CcGc.!3iޓq*pثy#ZR"#= y_ ~t%eਞzMlbcbQT\ V \xw~zDFFBPi \z]5i$"//2e ״L|M>gϝիWgv@ 44+WСCk UUU`0`̙hnnƚ5kJl]O<f̘+ŋ+S'[ ADD$ 0])HX?/W1M q\_k>0YLaw:E"ev㖾Ϡk?P`IhiÐRBKK >yֺ H0o< s΅^]״i .4wWW }BG}Yٺr92{,X^oיܻ\ E׫~  3HLh7|/vZ_n[bece@" @ : "ՅH8[yc$ Ht=IDb^`rHQdka~|wBa~`[ 7 .k}p!frowu19sw  <6l޸uuo0ԩ-_:z7My"a4469T*Ѣi JM%5Q(;`&VCu3@F AMu_͡i6+SBQka+|1Aʙ*ϕ0N=ư¼ y99/&<-{{'A0DgsB1;sC{T>1;ueeeH0)jt $R)rdV&w\_cGgA"?jv;GztN˃pرg C\\$ ***PWW [=ÆqݾauQfs78= b@YIrFv)d$yE(5f4:2C cQ ( 4_FFL\k?kkkp`>yl<( G E3Ę3j^ M* ;( r8mB:|3!BnV#@.?Z^EO#sg475!$$J( q;s \_2 wA&<&&.EaCѭ[&NzI·t9tf n^z=jkkT(ѴtP(EDx8 7[WT\Dפd(J_sӢV#@.Gqq1ks6 "<w5qDʿ=:ơ dpsaz`!^ͼ6fk$]nAM4jT*ηK.(+:#Gf'M0qD)ePb~hV=@ 4wDvz%ןP(PڌHAq!!PR$''c_/EnD'cy>p1||2J% EV'OׯyaP^^'ObҤI.K;_[}MUU5֮]SNAAA޽;ϟT_=tv^',~7 XضmT*K8pLnԁ+^orI;i St\ +W\TdǏ]vzʼEׯUEe׼pg7)1+sjx0x`ao駟O(:ĢQQQׯ._BdffV[[F%0!׼|8r9:u[j̍o9 3f7߬ԩS`)Xx Μ9kNWUU3g}9\vͮ.*++LXszwx;Eސjk._3CRA*"44cǎ37rV3g~s0klٳǜԩSXQL2 ,0=L1mڽ7o>]丙sΙ۹c&Nhgnc뫰p#Rr˗/cX֛6i"4gCSO=k׮a s9wޭ|(,(@TTƌJBjkj?zAaR[[SN"88\.GT*q32.555xᅕ:n<y;Q9^_1rpΜCg|1 HJJf+/_ƌBDА3?9NUUSY.QTTC8.`͛ƻ+V,ۊ?МnOЭ[&֯{Oڕu (0iBvXrRwKaB!=j{˗^pT ZK.>O<> -bxwT"FK1vs}`qw}igXaa!.qp}Mw?=,4 {Yր ³<6`C!,, )zT<zMsFTTWU#.kﯾ 6l؈'=* >z444 ,<4а0ەÅD"NT*n9ʣb#l߾8E!k(dok7x-,, 4L8)Ϝ9O>%%h4,l!!PռˣqYc⤉P(~z*Gh0t`y j&Od/aжq aG++6_^ ?<2N\~y 6λu[,6W,g0!xC7ъU{a꫽V1re{`!n[ ?t(5 /WlذoDDD_7ihh02 3:?\gݻcNsc`{x駰~:L>W6vsBT疖0!xC'1qDAAp=w#//WU=Lúu`-6륩Ih΃+,66M͠ 4 zSn';GF{̝ 7>N6M][]Eh ǣ8;H\ KGju0(عsv܁m~p[& Cgl D]f(gΏx5k&6n܈ݻw شi3q|O?C#66ְ`~}C`˖6AĠ:q;L,)+^ P[[k~;úuѳ'5]tAbb"Z-[sS߾}ZZZmۏõ}e Ν;؀ 6~vӻ{47aٓO⋯C١Bs* OG|P[>\s5Lސ|9s4MHMIa< O_+xA;|A (/}r!Lސ|GfڊGa0oLpaby(((Ĝ9a˖xAw.Ԗ-[GqEn <~wԈxxPWdHVkr'k׮O̙3hllDpp0 (TWW7DAA233AR`*Ν;#a_GBjSPbϔ?թ3~232+ ~9t  0tst:8~55RЗɷa?mfO۶{CapU8~̼#٨Gc9[HKKs[NAώ\Fha(l1bbb @QP=EQ|zW]C>󻦦ȱ[azf`6,G\|<;wʕ/8R[97!GJ%A@@݅@\^Lwڿ 'S&ass5&poq5bccѣǭp<~y֞=m9yncT\J :6!8vFB"4$ǎڿWW8d٢ğ{Z[V'~$%'CV))53ջ#ƚ/Վ.6,{ wqu`z"##P]S?޷END;\~ NT&EddK=l?v΄T !!E{CDd$g8MӨBBenL[X,sC@ wJ#BB[ 6WĄ.P*[BS]=*eR!00 ~PȨ( 4TQQMG7w;zfq6@XH$DD#H1Px~ŧ( !(0Pp) H$jk=ϡT*kS^]\|B0 dJ #HT( ހ(o/00A6@?od񽳢6˵}z@ \uTHG @\xl+27?`Wz] .ː@ :2#twr Jd. @ @$FN޹g @ k<ʕE:ACQ[nz @%y@ [ZM6)̶% @ {03̰ۖe@ =ѹ!"Wg@ 5Љ E!W@ 7O%#!@x̡k@ @ 8>t@ BG#3@ )@ 9ο @ s<БD@Qsƣ"=l|m@ ?$_9tEA b_B!u H=#xi|m'gk@ 8{,tyS]6n܈d2KXr%>}`ɒ%Y"̛7{F^T*zj$%%LjF0]1~@II h?xy}֭Ǝ$ӧ1et cƌ?klزe_54o&֭[gyk֬]|r_3W:˗//ۇ~fmH=sZ 6@"ĮgzUxt:rwٲFYE%O=ΝSNg+|jg}-™3g/cbB`ǎ6Â?r'NĀǎU lڴ 6lx񢏭:z 7oFffͰAz~ٳg7zvke%ŗliO?@V}b]vT*^VELLOb1Ν_շF`?oڴ |^nfu^֭[&MZR\`0`ڵx'pa_ذ\#N'凾ʙcc4M7O@PL6)..F=|ggbPUt:,vHeocŊf{b_OЧO|?k|?nq>RnVZҥKzfmH=6f\xpgK|m'}mS-֐z:v^w 9tV@ >xΡ:@ 7 P[l Ņ:^q2yd?1o`"R*4L¼VMY|o`Oi6˦h 4e|EiР@Ѵ1} LZcFkRM0364Jum21 v1̘iNDKAQT+h4e+ެk 7Hs|X?loMS(l# ʼ !3EѸis:3f6}XݨTչ0}Y(ź|6b.rg`N$kLdGS&`-NjnAU0m黹KYX۝8fc.ƾ7le`dXeo Xj 0eG:v(6U&-^0h^0ʥ)cj+֢Tvg"Dc-,Tǎ"&&r H$Pe^kЫg/z@4;yCe> aQ( ݢ[DtµX , lwRenfQNs";Vg"8Ν|9-O9Vj;I&Oi;z??˳xu 0oR/K#;_V߭rIcqʶ]%iyIYɃuYs}紁wCyu@ ^s1{u+}VVqBEyøp*O<~y7sWu']|Yfb7^||<ʝ1ĶeFx|egz.F;be|9S_}ݩ-b_eVVt@a3=ti8J i>s&(S㔙@ t@@ nhΜ9 ߓs4..ax~~8|.u',oe2.>['璏mz.Q]5iƧ~;w"5%WԞ:2O!{s?k@~Ν; O?aI>J|_~%5ᅬKϿ@*kD؇@ Mnn.~]$%%a?kexBb.xw/HEw':@7l@hh(YnjRMnB\ @ pMU^u:HXimmjZ|lxǐyh@ EڢVf{[k+Dy}b%x{/gݢx*:INboumks)E/ÓxY$}Oz( de@ c0@Ӵ7CH%P_ySplzL ^3gpy4AQ`vU( 8X^={[H=7`-οN^eeeuT@ 1 pN<Lf|mO\'NDB!#;:_<;J %(B"!!fFhH(SBۺ**PTTn=٢@jy9xrf! _ϥ:I\<=|ls)ΎZkWBo0ٻ(Mv !Ԁb99Z,'HؑlX.vγz* ]A4Vz!!fwf~ldmIvvgךݙgyff<<$Il1* ee8Q9ɲ > 7$tnrWQCoFC%ʅ$K((̇drRNATꟉVT^n,{ U\+[n:ǠsS=cQT=$d>Pedsm(j0ÖxS授9qtb̀v>E 7""Cg08+Y!K2*/Y Yq]Roؒ ۷ow Q]krn2iYaDnyl2kTq 􈈈NpUWIR2*C(\Ȁ^^`,x宗KgŗE$pqKo`է:.]f|̢^+=25,٫ǏlK|p;wAA^׼(Ym˵k!8wm<O_\9gjN9| 2_iZZ6~i篿rѩk7s }k^۷o+55]YQ/_#/},VbF**,@Nvrιeg EE@Sa+5k8tDDDD,?\eF^oq&""u̡( >h>6oقv pC:E1#"""5)_o-[Gݦ?Rаɕ;wEзo_|ݧ.\~1E@S:r&"""hxgh۶-^z=q/*ZnwfƁjF:Q To%]DGGYP $ ̚|jU[^L"LDDD s/ш"H6fAa[PVRRz\;Sٳg&yz'߆ɓo)`gΜ9S@5~z=}-/IK'}ާsb+Gir>qmmybԪG Pdjj_:ݻ?X)--EDD_Wnn?5MZھNDbrfTKgJDDD %Sy`Ok(V} PK疂矈NCw4&W66oxMk:_\ާ/_I{+##K:_Z`ײզ\:gv^@^w-}MϿ>>g./e>C}ԏT.u?j\SYo%ڮ$""{Q^nʼnGy6mۦYV| l:Op^?{6ɍW0w,pw,zt8} 4Sg@@6S+@V`lDEE!:*8}m6VRw:mNQS! 0!L&#VϱCNbSKA6I ̖r( I|A!]E\ RSS}B,(.)lUE-rOر#0(hnt#""" "ID}`{MŜuv:`/?#'7 >iOğMv69smk_6^KiZR6^K3T'9}VyyyE޽`)/D~g!11ɕϩQ"W Ѫ/Ʀ ka2RPMDDDT9dYFaa:vr3$I,ر3Ξ>Ty:?k^ 0#""sHtCy,y^uÁD!T `zfl 4@lf^!D*).+?n\""""dYe(&`.5CVW#!{m0XCQ`\9tzg3 u r[ pY:S~C Wrz5t󌈈&NH/D6r/@/Bdd$Ƣ9S%99""" O?YIi&5[{˵DZp۷k0PQ/_#/},VbHQ30h֬ڵo֭[!+;۶lO>W_~HIivۣYf0.ᇝHv M Z(`\&’2th0X-% &Ɗ\1dpȑZj/jÖ$""" 8NHDL=AjLJ r8tDDDDK架:Q4C^ls%""BqMe˹\BrJo8y^^3vAt&JD3KI޹s{_Hdt^luD!l›a9>|/MN~ 0s~ Ԙ$IBfVz I^_KgʡK$ _u5ZiE|~(8{N䙊\Cˆ-_`̧xA3220x`׼,XItg|M-M-]rZ5 2뛗_r׼|s֬EKdddYN=vtu9g? EP-e,f^oNv7w~DDDDUi1ÖXm6""qQ7VI, (+-AW5?"""j ]56 pt E(.*)+^nz]`+@_;?""""4XszۢB E(/@%^}N(*.T)?"""jXj"u.{Tt4$-%:>,F⚫<#8XDDPPPI\7q$\z?pՕWcmPZR2?"""S©VC'@pk%ElH*:1T}Qlܸwnl޲=~7̞S:Ef $Eqs?p#//,#/?w:DQ, aa.#""şsZ)RPyVo=K.^ÕWM 9sWXi$SO?Ōz gϞO<-Q(Q-S*U9Tdtp=b钥طw$T#Y5M^.L4~مos~UJNNƍ7ވ'NuYE: M=ꪫߟpm{_j|/-m4ڶmӧO#>>6eoEf믿O>v?O!SeW||<U7^àAqYNz~+W±cФI"p5xʯ*Nq~WCBBbccquՊT4o˗/GӦMѦM|>wDDDD؃[RSwÉ]deeQ߯9s{n;}tK.w}禆s=,Xl`SNŶm۰uVZ 6mԩSٌyĉxyNwnTUYg,Ү;`1[7zY7esϽ1w1v ; 4s#M1!`X!&& ,!:: O=$I}ee.sY~E(ŪUТE Ǻm۶gϞ CDDb"<<)Ps/u[\}͵;sY)yfxtYMOĶ-[`C[ܾm۶e^o'NΝ;ѫW/GPGDDD(LvHgPFQcN`5ˑ̾{^'?"жm D;())L&̚5 qqqGQQy|Z ࢭd4e8{,6]b򊎎B&IhѲLFj+GyW^y%k!==_~9[ee2p](*)S`ʔ)N*{1xZIDDD{mYh!~W/sCE&Í>936"?"""j<.Z"EJZ}@ixDDDD䍪5tZoo)Pt /|NDDCӨб38 5QFPW.!ڎi*:mRE7IB\|" :Ȓr%E0px_A {T~DQDмUk6 r}83dE uz:bbfDYq!e~8j/4^aaK1ED"v(3u8J jQ ! * dZy$" /?H3T JE"i2~ Ȓ6aDȈ @ bW"ՋADDDK\!)3EDs8y͆l߸ F`:HjZfPE l6!*< :H ku^scCF?uǡezQѱ[70' 8~=/,Clm=h jAʡ8qQ&)(B"AE&"5>yV8}iRn7K.Ų%ѱSg4m^؟e: Yau$u%"ňc?_^͟gO_SsʹE[#I( ? ǠTf7$ NQ! B"\(yR"rr8 5X=CW&΄&Im6GoVAA1i8r0N<%/S!ۗ& 1EQP(y6"?Z ]m+Ql0F@&Uu77;*vR5CMvIJC^AaO|v>=Xyegg#11QQ>uYg$m6@rd6a">;/B֭ѤIRT(0FڿDDD7'NK>K>jRSo> *(VԂb@wpǬك̙|:QP&KcEFDD$"""-o Z!7ʫ:%^$U %YheL7݄G@bDGGAȲZ\`Xhb5(eLDA֘Oui,fMф*Ym`,PQqǝw" Q!IdY,ːeJE`W5lD!vh<6TSDeoCnB|ifjf_6+&:(@ɲ ](E05;m-iv9{Tr|mr--.36v B!0("W4ڃ9WA1LH&31`C DDDtir-)*Dt\㳠` ˕ MRRUsA$KnpuqthN#$""" .xmzdkz^6Kq)KRT+v_><ԗ<_}۷{{/lOsE@=OO_ҵCݧDDD~vRA3N>&׃cL&7CDY+i(I6΀ƤI:a`/?%HDD50#]?a-Ruajr VF ͂]""EaK,Vj6#aZHl۶n vQ|'O(:5=ٿ} }nAPRZ Օzd MQ50DDF]E@#V\xP+P5WF :_zr0"zshAeTnrui󔐚xͩ^Q;j"\ùx9{y圈QcgMm=OmRjܶ6Mv1z֨)~z=}d9ƌ\?#OK0jLKxGwޛ͟o7^W_nõCaƽw8!/8ӏxgZ=r( DQ[7;-6,C.]i-|m+Sٽi׾=vdllw|>=xS9EDDO_{`ꑜRaK}Yv0< r2:xm>EEEu',hs/l9Uv6]3"PZjgqISZo|=O+Qk(@O/W?b SnSM_-J$DԹ Exa1oͤeeeDqqb}r5hڎ ]KzGK!"Οφ(^8|$%5N:~-((@^n4 ,u*Þ=q5",, =i;o;q>` ѯ`1x*7H krUۆukpM7cE;o۸{a2vpV}f ԺG˺՟aIxאeK|5|k?[wLU·TQ:.@1QHRqTm~Y{ߙOSx'qO;qT\|5ӎ3RF:WUڮ$"""rM1L-T?JcTt?dQ014+WTzw3'O%[و}>cA7abHr.,!";+ ,շt:b-HK // (j"lQ-`_)K~A᧟Brr2 :=E @F$dfeg${,բu ~߽WgʅKj/?C=6Sq]s?>ǣw޸HJJ No0phKiG v1{NY@`ڶm+@QOW_k5^9##4R,XIX^; P|?EmU?gjӟ>}Ir׼|s* 222*v}~]:-5]]َmo($Xq^8z-8x iich" >py ìYq ,\{/>"""r,`5mtGl6 1aP;nL6#ݻ`Qqc(|Λv9jdff:JnQTT[nܹ@\"""X֊g;?'OlrzޱϾ<@DDD{r;E( [ۮS...N?+יL&#&&糳k_5{ӧOG~ QR\>:EipDDD,Tv[7}^>[ ٌO?_|c]NqFbժ<Ӻukn 6+V|X9sFi=Qq`Ю:u*/xM8p n}cOoĉгgyt?FϞ=0j(L~'""FQCu)ͪILLt;F]N`|@~~>ӱcG|*}b .zLDDD䊊\]5Y3g=JK˰| t%E""""5iQ۰aaw:ࡇ vHUZ \ȝ//wZ(J8eAמ4dӽ?o$ 8tDDDD1ޜ`/Taz\jeLDDD`WXaK?sB :"""ǀtDDDTIB@ats=.3#"Т;g9s&N:t={v5R} IDATz 숈(`4i6;o:vM6a޼y8<ӱcG`ǠHc]fփ8:شi^|Ex}"`0| L""PSD1`ǎإ=:t#M1|ٹe 26lvȐuZGDDD \[n8t#{r3;0qDGZ}ݺHKKÆ k-VZMs:>:tFHDEE9y{(--0}ػwvXI购uDDDM|ϛZ3t>vU}ܹs1m44i/rssyyyܡCcܻmq֭[c ƍסkΎ|!qbժ.:%ןȟϛ"L;Ex~7|toӦMskҤ tۣ[n}(..[\\_ w}7W]},]¢"DGGكF C?viG#""?:7m/w{wS;v8Ɵa|7Nwyyy(**BTT|mۊ,5i֭[1vX@II#XtQ#PGESÖ#c>|85e [-.ܰa#̙֎eOODZZA@DDiQi.tpU;78v:F0NlQQѧOt 9F͛7Cҫ:"""`Sm:OJػ镘:`ȑHOOG.]jÆ]cM:_~%&M:`08ΕCg"""8?:ڻDj\5>n9r$F 袋0g΅0k׮qΕM>g"""l(~\*?)CDDTVo]-}6Qcq_~(h;j"(0D+Q]2k(L!Z@ǡڈHB!XzMSDDD4@jۻ:>CuadR(,WA. @*A ۈ8ދĊeX&VN_"oε iI@\A(}`jPmY`2zbhDe S`[=PbeXˮ[wA\&W,g,:zA 흈!PՇTm-w#""+r;獐R'Qh Sq""" %DDDDHqM`-Q- 5m/jǠmۋGƺ54z wޛ{> uWyCiM[KZ??Ɵ-Ztk49$"""5h}h3;E4,C M Dz)Csǃ<l|#֯] EQpW`܍7A$t~G@Ll,&Lv;PVV g_x_} ލ>}/1iO@vvV,]Ct5*Z[Xrҳkgd>lWf#]݄-=X]XLEֹLȒ}乷~^ ^y 0E[7olt[7bm[ѽGOuz=ݑ (**D"66cHsjC=6S LN5&Cy,b|v -)Ȳo4K:>l6튊w: ?|\ 7 >>_V))|>|^*^!+uN/rW?"Exa1]:%[Xh! .o8vMsQtϙgO_\5/_g뜵Ji Ji]s~kΟ|MWscfr:lI _y\y|U071pp*FG|$%5N=V޳g7Z_n>ȣ{ɥEgCClLDDDfΞ=oQic1jt8tzY27muϽ0Lx=ng?q5bْEnnX7t3&N,]B\kcx=9}y W\\\ߝ2ۻTmI +'"" k#"""J1#""" q5Юv={""""x>:Ws)dƌמBQ%'"";3t_]e9 WFSQc@G՞e7"""`|tiϛWADDDys]PU+{g<V^rիCy~Q.C^Ν:cNj\5ѣG,Xб?B۶mxǸ1jlꫯ>u*.2|Y\6lQQQ=t"""m7h:~SQ|c`41fh.?0z(L&1X/`0 ^5z([Q.ٌ5kba""""w5Pšcؽ{7ztFΝ( JK1qdGz\ƊEEQQ(,,t+).FTdcs_Ӥ$m?W\7lذ}zFĆ#"{`v$9-SI@F%Klƒe0bǺ8,_k׬5O몖766EEP`bcccbrp^06- }Xv-҂r~"""j]QSNx7`Zѡ}{Ǻ}Vb`mxɧ\ѻwo[f^޽z9]z%XjJJJ7_2D={6 Ю];m=ڷk6mRxDDDU0m^ Ì9;w#G8-0a9 'a5喛]n?qmسw&NÇc u'M™g0qd,_ny<䓘g0eTDz4ݻcǎQ_}"`pV*j(5N(՚OݑdQj>1g:xRRh"""lVngN@!ŊbAJְZkW-s r&Iߏ9sg4y9Ѯ}EYYZmEt:^Sмek=uF^)r)v7t3bcc1qٳQ^"" wLK>Qu h t^׫fEIQ`ư%O]""k9~K͹\e"\fB@:ƍןo*ntيycODDUQq "}N&W"""" 9}6sra2c|F\^{"" E*>B$|Jp>'>%tdH׸Q 48y[#aZ>{Cbc=n=(_A◂(3ǀۏ;1p (--sZ@I""""[p\s8z`/lV+܏kA! ٹlEADDDzR8V+Z^Gc7\Z>R'5>DDD5e<EQ`1=P<\.\+CT? AQY2sg[۴O=.ӧƺ}ǟpm|{$=Ǩ#k[n; kƕF1SDՠg-ܦtӻwx׭P}3|0]e@=#cFi_DDDԸ7E ꫨV)'M#I6m] Xfƛoc]ڸ( 1qlߞSoǤ)S;( ޯrp)}8y{~O=[߆ǟxYYYtUi\;01qd|5n^SQ3xQ\\kڗH^X8֭_+SS1uC`e=hZtnއbmh׬(--űw0}Xtcݐk3kNcǎ`t /Z\c08vf> >Ng4GyÏMJ >`:vKjDDDxN0[,1|״-[Drr2gqqhݪSx}`q.+F^ @$d]CU?`ȵ_9bL&]wSѣ/..Ƹc]?Ęѣa41juWGDDD]vWް%T vHҏ5̚cٲsiڽ ~ӧObx/&>^wK&..:޽{ѭ[7ٻ;uBll,1EGGȧ{e^SZZw\8V_$""jTk-Yg@Ӥkm6n3t ]vt޽b1BFti&FysǢ8sîò+`| ~c]LL @AaQ!bbb\#l jU?a X ,[W9ðZm(*.bֻA$<0c ]'?z(RRRRQ#Gb-^.ַl-jk̳58j;vق7zV /jX׫WOl9,f ֮]^={'>>?3J˰fZt:_}`͚u-/1s3DDD$&„_| YYx()) '7/*":o;#?Vۏᢶma2T6[pQLc֮[nNCDD԰Mx$=Gqqv}v`GϽzt@UU}3 8gײFj 2111x4.GDDX"€~WԄ Xǘ&K>M"XaB,>>_V))nQh9VfO&{ob݅E"T|RT\qn;6 R( Kq>(O۱KgA(?!T{mEPr"""""Z;Ec<rvh);+o _rE1=#Im:3O(% 6lµ_~]ADQѧwomQY6E$XVX6Ȳ Qa0a0Kx l6Mhզ ;<|!yP#G?D0u:n@ѶE!US'2JJKaXЩKW@V|:p#"#" օM3c(+-FiIQ ƭ7Z""SPTXO?YCh-I_P ,<͚5Crf?Xiǀ=/%%El6 .z>^|΄(DE`4~ HYVs0e$&$R^ΠEdDDT)(/():]v6mn! E!X\ {@"$ E0L(хk/rAD4Mn> 0~ٵ -\HFyL&$ GJwQVVB\r`@ֽڶ&Gq[C( @V(^z; rI_ʳUv~Wsb7s{X4u\\p^rcTnmIu] ]swLxfZ!'E/ħF6m0yQ!,, ]M؉h<Mݦf}eYFqI St:M5w}HM{VhvdeCf9F[mV4Il kC@'Ih0iY:Պs!YCQM61+[3#gbP͍\uu'&Wztw/6oي5kᖛor׊K/haQ6l܈YO>SYP{77˫:&.T+\++8>9/wu\<^Xު"\/J-b x+rRxtU-9x‹/ġ8$XYi) ŠˑXD򙺼t urJԌ*ysY^\o~~t7߈֬_~݅Ɉ.o6nFy)~S1GᮻXݘ`!23!) L^zj 'NE4q7$UϾ=GϺ\W]GJA{sy.!<ʲWY|[x/|x?x+t"  &K/ME/]]QKR_Чo_D(,yDt!`pO [0I6]IDAT!`6!`@xX8pˡEQ`04 [ނl ໝ;61^5t҉kܮv4&&&"++| aܹcGG0[oBף 'N[o,mt]UzP*ۻئ3s|l;{N*k r*:`ҭ&mUڭ4jIjҶVSNJ5JXA@HH IL;!y~'9{r~t^gք(vΝ59a)pBɫk/t:1lCUU)۸m_e(~_fA]4Ŕ2\"_!dnfD*jmع+Q0S1\YRpk%eS}sOM! * Y ;h¤(sݕ;c9gv5U \م}ѠC(.dRDIBOVklRiSӁb̓˴O|z=4s_l,,,B!ny";vɐAK;PGGMwG3 P()FUt k!\wBa?[[W-BcŬ 1Z5ݳRf1#=?l)3<E#X*_8bL5P[s]{`Y.Kar^ b =%s": ]uSǞ7j'YVNP4tBDuueII\&srrr4M^`ߚ lbTD  o> Út"@€_x7 7i0f3 GH90%K|0ҍbC~e ;"^ݮ oXAmVeVKB߄+lgm 2;]Y)!B âOiJ]%F zXֱsYMOm?yF%m?GZmuMMDb2bL 0 wdx;;;;\YqXUnsioj;\Zw|̱ͮ-iV4[H6,X-9"fE+5΀0:LٳU1-4;J.U 3+Ne 7w3H;=@s!ɓ}V1 왛ݱcGHhL+جJ8怨65LͱfԘ -V6W@ HKvLfWP;[Hyb&R%uF}Da&L;>%V{bw5_1;0Ւmü|軯$wVZF`R>å2#ì2ݕJPH=㸳 zk%0pkӮݻx*JuF5u"ZýsrŚ b$M8g(4~>~l!D/pk&G1j7| z$ӣIq3"pŒ+in:AA-.3 W/_{4(޾&Z $K?=k+>> E=_Y\NkY: :]r˻Z3M$Μ:</1ןFn*ٯก:?/ݭS._-[YVB]H3WlmCbwBN<2g;sZ$-zsa}Ys{=eZңS'A=,~k*gggJEy#b?s3iپu4_zB%f{Ml<&VK3 ˼ψkK[$40uÆuč3G%I3fd3fJNDpp7:nةSnNN`eSCScEDڅހUލJ|`ttZ^7'#<.tNtv\\1ʜB<MHɗp3nBLdև>fXUnц~g9JdY5;jFxقJA+,ߤLMMjjzLX'OѨMXѤt%ԉuOJ+ bL84j͘&<5FkZ:vѣ$:m`Ux^NNpp0"P^rdR[$q,P( '!q^BḡKΝI'^Ԃ=zwARiEbB-58 ;X1ʊx T1ْ͓D3TIJi0kkEI3ٕ.{͜\VHq9M>C,xΘrMdfW~eΥKq+cOZN+@nn^w?mefrԎX,rH0+gQxHrFF{r80@-֣ sR;!(PѠ4(^YdҚ+0+^Ĭa10u8n~*N~~55T@:995;x9izQ+.dgO~fZUaHF OD\ֳ%Kr;wL֜V]MMM%%%%%%CH*v9wLҌ~|m 22"ǑXD|.[A^?ѓc`GU3NK63+_6)gy<`Zh`i 罜4'N*.R)-MɀA }YCx\^0tV)yPXT[[<{N9km[fճ'R~h w^y! ;jk&tF@(d;BPӱBQvvL&w8>jͳˣg3Ȉ3AMdbm(BLwX8fBW9E؏ rvZހ1:4Bf1c܀ -nH=)/ԩIJ㩴1MZMZ7Vc2ϟJ^7?m@ xsqe%;wPvQ޽{t/t˽gRAT=^R[2^ȝtz^|bl!O;ZUQѠPQ5eڨhG ݄92>̀h]fws(ӛ VAIĔe#3fV+s0klP˞<5~~є+).4B]y飂ktL3IG)suܙK bܯJyw^rr]?_*5(l*suqf654qs~T*r`jXLkXgb]le(/˫)~Xe|XbzG`h"ߺVDѸ])|N8^UYe<_eu۫MBq:hLܠ>VAxvqGq$U$<"%Fhu-8X2bϞ=oߺ5dPnJBQt:\1?yr1gϜڱ7V;cV2b Y*pL>]QVX0mFV1Nq?مhJ|VU>ʊkPpUe>-{or0tDw](y.z_XA8eΒJ$5uz @ ;ngittс/`3e-<-&on8b֭k-6-|Wղ*n*1pswWUӞt.N-+VNNy"Bޠ7ՕARb[oZjr["jم.D"qKI)vr7HT dZ'''uЪ3B8H(:V"U#6wBH,`$J*<.uuU)/@$ݻw3gLB!a8k5jpusW65R}sJNN;6Whx'+8b0{J|;ƀp| > bY<:FxYA: &[_Vqrvx򤰸ۻG:upCT^9}>ZJcAh|M -UV4B#YڮԪJ bǍsLDȁ&߯zsvTUFV> =[%\ ?JlB쐈ywm9~Ԩ)#'& ^_WWW^^^QQР7"rX,v4#Z(\a+8q$pC-1ۿCln=oxmK Y6R&bENf]%R@-/P (ZW15>[tP5 nUD{c4X nq$ .` PQF7sdY !F3/LRFmYf_y|[@CHnN`ɋ7PָZ; pAi`}!Dca`N.!V̠S'"vx\8"*<ŷ9nVզݛUw ӣ>$f%xog+ cM VG:vP(wuuh0ƌ9|:J\]K i$'q' 'NH$vW)?> O~,fɕEvDX~/B昋X)1';"V5Tq\NηZeoՂE6,wIDA3Yg}X|߳,4zG}khY~2nVw (8INKC+j$Im]d|Hh0==ʦիV 8hСYR)=>b%Wu2,GSLx@lfEPO+Mj,&LejG <Z ?Q U ZMҨjZR)׮]'߸@IJ7\vK*hd|ri22@?|sՈ \]oܠV5km yvs_v3˹PdZ) ,طoq11!Ɛ,\ЪizʕŋǏ6uڧ*+7mJ""2%LJݹs%1v&X+PXh˗h J 3Eş}񹪩 TNhJ1riIS˔P~!qq/rsN'O;o.ێ35M[w|'׬3fL]]Ν;;kߡ_{=m۶?ϤVx_ͧ`ZҴԝ3fͤ=vXJJ :{,Q\XaHgϞ#F`RSSSb1"#3d ˼pxfXF -mϬY{}yTf,Ln\|/&&ޝ7-mZ4BȣGii=2*rq6KY$q|3fGG^`r̙DWo5D"Ѯ]F< ܱ؞\x֤}oݻao\.oR*/df8p`lvʒGG%&t>#kkG?j[l`X+y%8&bHw]|'|zΙ;$vrbKijп_!23c*X ULt -BMuͺ'_juScmvG-`i.^4F#e11?3m=ǖmN7'O4mکȣ`|E ɨj'L|{Op!|GIKeeKFEF.Y͉ K@0Z%ݻT*WXd1}F #DkO<9|\dIdD B:66:ѣ qGǍwQ2W$' 4K@'OF F Nbc.ZqY"FsqqqqGAaCܿqoݺ@ƍ;r0tƍx@hI=rP¸q ;z)qqϵ\TYQtȱ.z\flxRDThU˝^[SsӦNNyzա7! N[ ^^|d2F3j4O9}FرsfϾqq\7^sPqqqr,ooo@z*2ZMuߎYlYccBhEaa7mZpQEEExhl:n„ G}~g?n;6|-C6SN!T*Uaaі-Xxٳ{޼_ G6mܧOԝؼG|3g?`Ci}}}%V]|P(ɹ/_ׯųcyډ޽{_zԬh._W\ӧ\.~vz044|ӦM}ٵ+u7oPR?# vǏ6o.%qȋ"bqO,vݸv] _";yʽ ֯W5*droXlOͅL% ߭_#X[-3nܸ9kLD2ctYljv'O"JǏoK1 3 ɳRihhhEEEG+WO.Ho.ļuIRtԩYYY:rkjjurHlllFF&q$wwS#"""##.\Ȍ^&Qn޼5}tT:m۷['%%Y*`{߽S\]]O(DG9ugϞE@ Ν=7ydZ_ydD2s P222###ϟ;2gDEEuNstҡC#G @^^AZZ;wL6U"L43! ^o޼hP()JJ&J&ODɵD]>+9Y"N1͛&3)hcC㕫W# 7nP( i^v-%%Y*Μ9͛dsq|+H$ ]QQAmcƈN)F>D۾vZrJD"1s7Caܹ$<<†y3=s B))3g<|BpلD00wp`yyɅB1UJu`.vqvЩKRy\دh-^"{I$҅jT&6~N9[J5c͵Ɩ]S <==~x#)dD"ѫݝOZLA\'|o|-zk6WDM6L`%Kdee!=,C͛رZ~ƍzkFvoE˫:00e\rSPX!R???., ջw;ׯ\.* TS:;vO?V]dRF}˗m=̬f?O?Yhd655OO"fkqQqኊhjHrJ )SrijjrssGYzA,;Y2")))qLl͛׮[kިL64g`o4fxYLڼ%}Jˎ?~ߒ)SP_ryeyxz"Qep@ ϜIFFFvڴ$JWKOK6?wwwJŐ5[T*ڥ룒ӧOOإsgTJ{Ngg灃@œ'otmIEHM%'zWW&̳\<҆!,Q[[kQL&khh03 `͚իW:u?;sg\]]ͭ;w4hϋX'"BB^zuر{[n =slJJ2>d2ECUdWTڤTzzxTUUz̰DCؤM6.]<\.J]t%CeݻSN5ZX@d^<WW׆LVWWˌX:@0}5(( ())ywݻ3g0@ 0 "%# \\kk܀`2^!2Icdi{mn,ңG֭]<+@ zHf'/z\Pԓ._"<<ð(s6nNݽT:tذ-[< !pss8p_ :T"2MKD.777/L&SS,ɻwK>}:vԣg_z)D:t|}ǎݷZ 4x`O$ɐCZ@[ِ}uhc(|ʋɠ;$MOھ};jl"H9YIRãx<00pڵ#N8ǟJe*Z2OoB!]]]mkZ3@ d...7*<<]\\_uuT<~[R5ש><5uV=r;וּ{UCj#1 ;}%Jsg*#FܹsgSSә3g_Q!Ch4 ̡sٲgΜq<00ro޽;11sgjllL[jD"""@xxXZHs۶m;yNWر㣏>jU'MVL2d0fv={:xPT޳NJn?w>j?u={d#vܩh:z*2aTdTڞ |l=iQQ;S`]Yc֬Y>k,d) ӧρ*0 zni׮K$AƘ;uvss~:PmM(n}z*Ѽf kWZھ}۰ag Bׯ]H%AA]]tvwwv}>Rں7PH-))SwTmm6lXs@ۮ]vYmݺE& __^G9|ԝ;jնm >Ktۺ_BBm&Nh 0q֭[yZg'B Dng߾U*0 #SSSUjmۆ k.sLL̮+55&&vႅN2pGLR172d殊 %9e޽)))흙RM(-\tiСC LTmKykMmkjk$9rСo6קL#_2^o zyAR{nc%KNO7x`@ X֗Loj[%?ZëW1?p@R~ brJ~]~~RԜAߖT ef)ܸ1v<`KZp'gϞӽGw'''Ե|̜5˶Lߘ_+sCHRݙnᢅ%ӧزu˚5k̋ZK ߹{g,^}_~e„}ի#:w

?}{9'۷!Xhg~6c{HRCK;6=)GNNN8"{</^rv֤rs/YVdPiQE/^駟%%%C*@իVM<Zt۷'Nͥd.@`^|+_%%%}e.B_z{IWgɒoߞ0~BܥK HkFw !~РA;vӧcJǽ+;؊’a0b z1=L   + θ ,վb f(%r(>IsAχ ھc;3zۊmM:E.le 5K,S픣C*S'߰~CMMM=֮['1 a 2&[ch(Q,43, ʲIR,9_=1EJxhy`s * 'ZFf&iPZ*duNԕQϞÛL0#985)dۊTuINl kKⶱցe LǬT6|ҷ.ՅŬ]?{oZd徭z`?5ض0:!$h# /&]2W_kh3UB]aec,%`?I*Gjv˺-8]mEUkby76]r:бmuC^}kw2ͅ|YL-bIjD8!Li daV=3v2ZCp(Ԧm7SŁqP}(i%m#&ZP ~.BPR:;;wiN<}`WaA^ɣ/^O4_!ωp׈W\"WVJemm݈}|#MC(Zxp'"IۇU٢YaqŌq }O\xh-}ؓK/US.:^K.=ۓQ爐+/'zREŏNbg0̸kϜes"iFCOS-ZMuund\@ V[RRRѱcXYYUaq&u1vdiM A]7?o1JH^ Xm>(Hy^UzNm*.ڇr\Uդ\}&Kd=l˴ѣG55 L&U"FF&aysE,qi;K\\(Wʞ7۝;wC>l'P@@3Ǐ1 Y2,S$+Z˱gm |r'D[ZRr13#.a= @@3.vG+r 1`@ŠZ[QU*K绸_y%g߾ryopZ#E"H `0j04'BR[E!+6: A}=TVBY_:KG/})1D}11)ae(H<j`N"@ )D& k"H$ H$@Rt8}kI} UxBQ(u  x@]TALgEii r"Dv hj5[ |y:V Z,\ccSQqqMmmuu5x{yu rww-G![7R?ohW\)(Jl㔋>oooo](d$JjQT>E U' gl@ֹ v)6>=Y8H @ ZfSUGlUuu:w NƖZ.qBzIPiYYΝ/kޠ֨;t%(O~Fb0#6cΝ:?(,|t i+5E.Rܳ2v57u k\:_Ъo~~A޽{2gP.P_8p˵˗%x LaC]l~^䚑yA$v:|FcEe .zxx!;^CCô/selQq 6"sr?+W,JQRR}z4qbڂcXTٙͦE йsl zӧY|TNHH $H쎋052*Hi$h $h0=!bR?l0xExߺ@$- ӁF" K~ `\ ,t@"@)@2Hd$Wb񦈎|2ُ`&"o?(j9o4mmw)6!1[p}]-3 5۶@ ݫP6K 4*_ tZ@Ϲ{uR)H< nܼ5|о}z[}PXXN=JeT liK}u}%{yoT* v֝Cr<BSds>3ywmhk?fLISI  Ua;vjiu`#.5^e OR9A;OMv@Bz{xQD,Bt:(y tZ :Tf}j T;bԻw7 8Z@LdU~zmb^zFӷog''cL)yedu)kH^Ff%|mT{!uCzXhۡ#G'+wFxttׇYYY11i{XIْJq~Aa֚QKJJ>2-)ÇG/ xfێK/ܹ &۠aQTk2KHmgg8D: BBR"@4;i׍p!A,0DuvvM aUFsqA!!>>^xcBxLPc cqZPA^ 3Eʼpe @D3`5bs2eBP@hYؚk6qP[5E,;<4B(#Ga`>V4њTB'֑42W(+t8vI(d26fÿU&>}Z$&ĿF BNB;q2?%!!O"V[^^~SKJKbyR 0l /\"tgUB8~X,)LOO?{|1-;b}CC'`?nݩ;ӫ˅ їO=f,r~ȬTtͩztwgH :-a֩e+&@!ua@ V+Ɇ PpL«Cˆzr!\]]==<1 $N—|O]B"aq8%s+P}ߤA0X"@zdhi Nb;oַ]'Z#$qZj5!!@m~ ڽ{w\w/[ۮ}Ϟٮǎ 9~丸XłCBN:>88lY6JFk=0Acٸ!wyo1R=T*'_հCZfaހn Ο;Vi%10 ;AߒAk6λJr' C!hP! L?z;iBT]Z 7DnnO? !j޽-3kkbpq77hhJJS w4_)/l%52* o<ۍ /q$ZV$qP(,$@@ 0 W^>|8B|@,-|Bl|Lcbg͊K+L>~Tn(Pj?ɺs7'^HHH\\P(0 ??} 8? ?VXpwݟI fܻ۵kטFw/ϯvDC{I[ ^{CRMم10;9i@C]u$\SknRpB_T{~FT n "p}SctjD"H$#GwCU%yuuU^ϡA>phT + +nVdV)[\y*aUZg q@8"ӇvYFHt:Fiu{쯻|VPB9Tubuu |}UjF,++)+//++ollh4Fәsy+_+_H^d޽{͏1r| 奥woߖH$Q1.U\yy111YngJ]A08t>~\Ǥ"'IiPRkZA!?!&4ai˖-3f dMzgZOOoF9ud@x?HfONA ehHWcw+&=tk"{ⳛR(T)bXX( 6SO?yݎ]XO=<^%!=[~,ĐtY^vg͛vv޼yy׌ۨ9{4ELzAqq_P]lF:{yuq?=wns t? A*b+,=%``g"\Z @4"*)4YM ٲ@ Ђ6Jk4R 8. #O߲<GQ(;v2d)ߣG]SmC:WPkgLҸn>5UÞh^^\ãRi1JD޾GֲdWDr-.&c>oggd2Ϟ=nܼ%He,ǏSkժUuvvv h"yeUِ`EEr_C޼y֯\RwX޽{СClwJ [$,y#89?zH'N=|4׻6r;(`"jYjJ ^Vd}EQiYe qiW^=k%o͎ұ1\a8RBzD<.711;nVϥ/KSO8j-n/=b;#htD?5h q#L mD9p4ZmQa"$N>DR*Tٳ۸B"{nrfqWRLR999 By\(fkj(ok]>I٬Z:) `/t+/ an9/o@">\@JQj& 9EyVfq귩ϟ_r< H2pWQQ|u(T r9xzG__hvR斜띯v\ $0 @8F@* CGOαWڵ&ջvmԩ ][1kV3Za̦&31Ǣ5M-(* 0 KIN0̬q\fux>)J >qG5hD/c^I~թSٳ7oQ}W_}u-X}`0*axLLd3+Q)"GuIM5#Gdq+"?b2x055ԧx[Kӣ'+L&Ze^ ;pc@2F: ըU(Rr(* Eb{H"Ԫ-}k󴄶$sNJbupmAh1\0 'pJ51\69.̷IZ DͩR˓mԩ7g?L='tc(ʼBlM_laZ c0u׷0Pd`Nժ- ;\5hdNکmu٬ݖ.}w_ݻOg2>*D3d\ll=w $Yٹr83l.͵v!˕ֿ1|I7n mߡÏ?q4mt.ujf*~f5}\b,LVT8ry&Hdfeek!Q($&wcDy:aGZ?;8,(6@ XvNGBmQ<\~]+٬euP P htFy{9uACWޝ"2U`[_gᅬ \iӳ;9ZuSv}_lSsq eoobj5\]To}lllFFsuup8"fH$:LQ^FqجH&R_#(9cP\OW۷7z#`rD<.A2Օb("/R{zx"uq s>pk\:Z,eZƍÇ;Snؠtuj9Vl/]8m AmҡCJsHL%v Ң[llF}?>s\dd̙3۶m իz߯lmeR6Km\r}zU]n<4z!t$MeIa0;;=`z<ҥӧ)uqƞ=87% PJ%ԯP@IXG2Vb ,% T%ʥ" @ pdq,ZP 724\n73b2'"ݮ{n߾ q_ϙY3gG4P(A?NJJzչsbbbө?vrrJ3ZVk׮]z5==]*FEEqqquQ9t:D믿X ffej<ϐ̬rٹ'O|ќ9sZjU3$`\ (qLcQYVG1)T_5>@ޟlfAQA%+.[Zz:)bq=\C _a2*+Z@ " _21+ ߷oD/~Wō۬_/vJ3oDfq^]xKLId*JeX+rX,&R,$IbLjj ^.r^W{IqjZ6) ۷uu(8TyuRQʵq S*ViW+<|J}(+;G*SdH><\R=w?pΝ;_zGݻB|ON6kYF^q2, ڗ00(kU2yA2uV6`DٳL@С}kW`Dj BDVt$j;"GG I(**eBVdgezMJ{R@%Ȉl<9'W>w* hI#Xj[;XQ MQY@-ՂMMM̌(JDO<߿|||LL'AU!իԌLWWW77*r?NNNH$uurrRYO0AU/^2<|&pS!uq^2 * *V{+sXd @Z `ن Z}5r#Vry?]d |#-+HUhEo|޸E NX=IT$~.ö6sm_$NRO̖g_so9p-g);@=3LF,8@2lIC#@ZF ةwP%;[.vv+VQ6kA>!r/ ;|p5IJ gB5/c1[M:~Yb2}YddCnjYbأF6-iQZj3qPʖ!{Ar,׈#&S] @@DPD1 3 &XWmֶۚj"JRR V6KVV-1jY@+k%EɄ  04Zb|||~ ٔzp\c-H8wNVl& >HG> 41[2a:]~&N?GUt .u\t:A,ݽM6 4شix<IAdeeyzxT`~4MllÀ!Ixׯ7n܎;͛7l0ǭ'\)-=COfs8@NϡK> ȀX\!k;~'<}jn4-N8!0;;g^Ud|Ƈ7[Z^Kd ]\ $fQzhErVz,A 3VJ٬>0u{QR5:96kJ٬鳮]?K|vg*GJ%&g@ձ+YRS1C:tӧOdddNV,**:uΝ;w=yR@oZ""" y:(`ae}zX0-*qdn@ ~ oN$y+߁۶mY:Z[ / !/23!;- fL&l 6A! @SeQnpڊ2(gҤzԯߒ]q˝ݠN@ bl(a,CVغu#G6lؠWULQU!Rk,WWWtPlٲN:YF.'Hj2$HDXXBؼy355VYBCC@,X"""8;(jZ6- jެ2 X, .\ثW/wy򥞠ӘQl-FF,**rqS9]+Xukdc R TRU8d2-xȒћbw$nK`Fqt ؔJa1fma@yAQa@2vuxG0 *aEy~vDo4*EpXHGScJYJ%U: UV…֭Hܪfjڽ5 .Ν;WٰaCPP.3!|<ͮͺذK!Yy.|+@V=sW`gj ǟ~7``n"##%3iӦڵǟ~^pu~V>lԯEv.vyJ R8 2xgs ѡ r\]۩YPEG(o^+‘PJLB!Dnn@QeYmR;cX ^IHL_eezKJFf='9 d=W+xE޶mٳ7md+=p cǎsΥVK%>YprAj"3}&,[bEfW]?qD7md8tرCREFFgL(;v,U!BO<:(F ܾ'c#dX ׯD;vXM4ӧOS&w0ވ]vq8MWE"2^l l R;рBcB.suu%3٘9 ' H pQ$xQ$TN@NjZJ|ssrZBH$8Q Sp O,3J5]6K628'KHHk~~Bv3NM}b3 @QjU [Y'A٬]:ttIiIrMggI_|THpY;M(#?qظ˗/׷/_۸i'BHMOYOO9o]+%1ubB oz:jfk~~gwNKM3zaC'NlШ-[Q{#=H=\i<. yn>`(& Rw*0L,AzzktFާO.L.((X?4Zp =D$*O y$ :]h'xI^جLuRvG.H*h dl&#X)b@gf^STzy1y,''3gujh:+Už3qfjhX?x8UjuAPP",6jcԻWvvÇRBtxhO: A{9998 VZP(NAYOݺt.ʆuPz +9tw,??ѣeqx>sppl zנL,1vƙz}Sh*t,**"I2)MAAWݺ|7V*$ XxS,3PUL8=qNKB!H$Zr*JLpb1Y*iĿ ۨt ( A$Ics[q)ǫaP;\6kU" :t8ubprrH>4wչS/- 0);'gT+>{^v}{wO;wZ |}h=0nTGhҤ6VQ*ǎб[nw~ХpOO?S?1|6ZDYt H~!C'e U8Ǒ!8-P(0aG["NQ4a09G5DbZ-`|u:6VچbOQ{78".yxKA(x ( 4pZc2Q3 J3Txgh`18L`T*TAƢ쬌iS)))FoߞԬiUnuhE]]KW >_FPⳣlVDޠWzel֯5 Vjc1J]ǹ6/ `!+$ ($BPĆETY p\]1LN_al6["J /TJV+/*R*Ż Yi5l ~ |b 蛯"[e 4fH^XP`/>F,̌6mX`_f(iS#rsrӶor(y\ N1#7'U˖wT>mڴ+;tryQQQrr+0fт.Z0[T'w]~jlo x6(KIh+r p`"'9tjQa{R 2;MT2Qd2q%A0La>fE]T>> H8I:bGG@h(rK(t[ )qĂX Ip@K   HpP_ejI(٢L6֦V bd2[ pkT%u~U x @VȑA p(dH,qj WZLf^=۷m[()4,S&x[9.!ҥsF>z85----<<ڷoUɩ'خMk?_/_fdeeddKV͛5k(6Cqةc@?~bHCZ@в;4`qn2^۳Uް\q\Px MLLRk5iٹԩSX@TAg9R2!( AV~uzZKT*=;h8&*r~_.pscҮ 7ڨF0<=2+ 0OMKmӦmUҨj_ZD t5x;xԔ$Ϻ*Ӕl9j]81D)+CUKK2 Deui}pv~U/M6mfN5Yn0 8Ue1L(1 l&X=6Mf ApOyvժіC^UHw{2$zJ'.&IRV{yy5?ȌhM i4-+>VC1 fU\d:kd\֖GQӵ&X)\N "Hp"I@hժqh{es8hI |AjJbz&)AVle҂>Ѩ55-6E4O j}@ԔO9ޅ 4hРAF 0[^r 4hРA 5[{{A 4hРa`$H(,q<3#͝Qf4hРA 4hT#cdef߻{gPH( u:c E,´њC 4D)ͽgPoA~.- :dpm7F*Q0O\h͡AOl5  0h$3_1/^`dlqq$I2Yl?G$ 6t:-H i-ͩfe{" J|d6h4aiph4^1/99: H/c^6iCgM5Ĵقt}Mh=Olc>n8^XXj2a<^,~+[W>ΖKP07[lIk-hNM7(6iРVЙ>JPA,H|"P$+;*+|}i-ͩ)V4vc4h|(P(\>>$AN1VZ5aCL v|ZlDsj8Xs{" ,,e޽|~L,( Þ>}*Jbq-^tɐZ޽YYYϞ=3Z2E^^ݻ?~,J\nƍ'LW.(?w\wQ͠ n^<&TZDZhFw- <m۶-ױ-[tǏO0GJ-ZRV-C}[[W4BA4hxo@ 3 <|ݺF~-[6={Ov蘔T%hܸq:u/Hr+Wؼyӧ׽{{>x0޽C.Y|7HIMIKK7*LMMMKM3b=֛7CB7'O.wֵnރZ[ #uy0:f>&O|ƍwe2 8l6j%EFnsqqY{#-==z%&ƏӬqc}p jcOY?w]k6."''w #yĥKSǖ-[B\|W!!C/JQw;bGܾ}*)((2%BRx~8YYY_|xРӦMꕅ:Ҡl5pqvڕb9;;Ϙ1}:jay`p &? "bӧn/XۃXg"xێTG\>AK4MxTjVCLYQu}3fbM?0orttj_s _~m8͝;/'''((x߾ ʾN>#..N+QaaFyru܋m(=kЫW12,lXTT1l05q޽v4+4[rm۶o_beqʕ.P=Ho^͛7?Zh~02re˾TǍU& ?K֭9q|۷oܽ{WF Ew6P\HTRGJr+ͶWݺ|\=w{իWSIЗ3 R͛}{̚5s}T֭&Mx_Çmܸ Hj͛7WϞlMNXNNΝ;S%?|9^:~k֐$d2T*W`Z^paAaIy ,js/ $Wc۶HoÇ6jp}eVF=a$7x V/?$vKqttܵg//Fa IhI; IDAT]@/]֗:ucHdVCLYaE_ҵKYC#G8FA۶m 5"RSC*:tY?mp\q@;w9|И17lH]h79cƞ={շ+/ޯ@ rņ4ʠlRToݳ{Y[nKIN޷wϬY^Vc:[ϓǏ/YjT/B3[ݦm[YpMLڷ_r,~ƞPȅQ;wv:wtn4UXPPФqF I>zP.Wtҥ]xH?~iѳGСC8lv`n>H@{[ H((q<-5qjz`֯__ {`͛J܄OxA>{.#AFQ(Yua}! [,eeUsq捛_,^2|M7d2p :uwZپ];գG,l<obckۮ]vwyϘ9C:ubXAA%u!CB5D~tT%YYY?S=_ߊ?~}AY@Ȑ!L&ðԹ4[ñOzz>5kU !CB>yZ>q!!!ei0+mD9NԡoP///ww$˗/;::zxy?v(s:mz~~ݧFΝ:2LJ :u0GZZ+ئFY8R=hĈF 2-cҔ)V lgݺݻ{N]//VV^SCGr[]&:k׭2Kή ˗Ϟ=suutw5sFՀ V*eLɜ~UԇvG5PYG4(0 w{  y._]ʕ+:<{\''iӦ7o d2H,&"T*58A,ܼ<{{{i=}^LMw+W֬]wCH$"L]vjC>) JҴ.daX~7.5>txժU;vnܸ1)9IրMN/]~ݺ}i۶`:ېUVP*#FY, kP?oCGz7Bl ,Ӛڵ}||nwSNoUٙ,CCfYM>33nݺzu;!Ir+WݻK.Sͱϕ'r\ D$`Xox%"YZ2S*kf,4_._>st?m떾=lA ׮ ۵kԠA~mm+V| Z$Vɓ'YAy>rk._ ȬY3;W+0aš5kk6 C}Ŋo):fHdp'ǧ /q7n>|XYU&YXXhTؚ7o.IιzX,d20#.#xjOs[.[E"(`퉋/NF^t:6ML:@9)ۿqㆉ'-_ A~&>|}"""WIII|ٳ}}}-Tב }&T*P((a>jR#GW̼8::޽{WR1͛w ]\\[}zϞ=ٳg .1c5F4*c=222-ڵ;z\qԩ*Gnݺo[Ө)YPo,K򛯖7l6;7'0,}{bBB~^7T INN-[lb 7nܨQCfP}iUog͚ٮ][}|:ҠVȸ}W,Y|ٱcLJ :uZjjʬY> 6^/3;~<8䧟~;w_9H%Hnݺ%ɼ*ZjQ2˗-ue5wU+#,X0͛g 8hi b`)S&/[l9͚503JWwرQQQ'O*(رc;ueȑYYYWi֬YsΝ0ab˖-2v8UR!룔ՔU2aBx||ˆ#;>f̘)KwڥRN;V~.^RLXPPyyy'NԨTgB/^VCkHidnnn۶m6lx^׮]a̘F*wޢEݻ;ͩ U_}c`Ҥii#Gg#9O^mS*ݳLCB 3#wڕ]}ʢhj6l8}J:tpVŖzǎ7nT*7o `x͛7?ԩS55*q9r*f]{پ}{BPu)yRbhDnN@ms3O5l@ﮨF9EMB¢ b^Ο;{V˖5yϟoٶG^u9z~~č7?믿Z!jNM./9}:tظy]Өʍ=V}֑ 7K q1/E8gfd4lOo׾#0]b#`9*;~ޑګO0\Ί{]:fŊch._xLL̚5k.]bhWjZ5FۻӨwgBPrAFV԰lEv\66S˃ޏ+RY%|p0a0qd7%sР}~ BM-z?c4$74>q{[hr<>!$_ސCW6FzzGlĸqÇ۴1 ׯb223M |DfF;r(AD"MKO̖dƞX$rqio/~G1JQkjv!(jmR(r2rs<> EVsjٲFrHƧjz\r E,r(֩]G >'λ6ɀAkчҜoQm'Ҡ)۪T|| rsljhc/cw-[/ D4jњC z[ T(+ͱh=DiZD 4hi3Hp/g`-@ 4hРA6@٫n={ҡA 4hРaS(e2@`o4hРA 4ho3^ѠA 4hШV4hРA 5LZ4l6B@<-4hРIaзf+Kև-ҍ@Z24hРAH28U{@˟FEuۛ 4ho߾aCKVWW}}smBI8MH$ 5/ZaTj+WA0dN2ƍ9Hfq 4hРQQ%q"33gRi̫-Z[a8$gO6i,k+UM/5maL"xyfbqkE 4>iU NLNjϯ>Ϥ+ &۲E"4YқZ}&S*,&31!˭^71 4hf+mH2G( 8~Ν ð\(y{ygtG6#"k2NNLuhРA' pHKKqB?#i<;I$իVM?.l_B!/`j+d|;m'5ۏU7hРAʘdJ"lYػg?ϟ߻׼Po-eknP0 4hРAS @^Fխg}3=lOiX2bXX ~U(ƏӬqc1{Awή_u6th͛7CC&NL_tС/?J?Ǐ/YjT2dȔSy<+/ޯ@ rŔ ʽ2~'@k^*={y<_|SRR `0BaϞ=g̘n$􈈩aaî\2xpI:nĈQQQF9r ɓg 1Ç0ܾ}oFεZ#<8xp֯P˗ZlU\ii?!0[MrߤA 4>9;aÆ&V߼Iܽ{̙"#޽k̙ CV'&&o={ܳ8oϞ 6ߛ7o J?)SL f֭[[Sii斚bz ʽ2ñOzza>5kYP׮]OII1+k>oWbu: L&ST%&&޽kƌT+@d'=z$,,t/?S7nY,KRya_&ȭ[GS\9ijûl[hРA jsW 22\(jdfzm[y\n,Dp:v nݺÇQ}u#IqzUɓ͛6Ϟ=H+V-GI_rŮ{5 RTܻ,úBYϙ=nc v2vXPh(/Ιm4̙3T`j P[aJ9El HҴ+$;'==V[-R8Bnl[MzsW4hР`Ŋ@/Kf@VFƃz [JTFKX,}H$& ]d2 ~?@Ybd2.H$ɪb^4!|u4J\Frjgvf/(.HNJB #O^rr2 u뚱A@̌zuݺ֭]cxB lXo}+<}l=iijBuoUV[[6O<]hsE A\t_}nI%a04h|Xx{{HIӟ3ōɶll*fTf/(.X嗯_Wɽo ִiӽ%(W<~Q9S("0//2K?yҤP 0dJ$*>ׯ߸~xLp_6DGG;99>VM4hРAS %8Amڴ9vFpˬ͝ sT*?g4iܘ*oԩjZ]CC{iXثw3gˋΜWަwܻ,c*jʤ7yDZ=Bכ>"Hp/,,Ç}||hBlٲvڵlrԩLh|X$&&hѢyǏ٪5#V^>~\|B¨ѣO{Q?ënqï^:qd|/_3&>>><<\e~!A%ԟ'7m@x{zP::m{?}$b4C ԉ ʺJ)))#"$ɴ)lТe9͝&1qYCL1SP.?ϸE_pq)͚6LyI˿zyM5߯Y`|q`q6m0q/3]VV[d;w-5nSNM?ŋA5jԨW^nݲM&nܸ1 5BIlZnԨ݋/h:o߾C\=D]E7"5~4Hj?a¸`Fv (rCkr_z͝wF ЦRڂUֱaܖˌ)9s&44kNTښrRL^pa CթS6;w;f/~Z,?{wG;R=v@ػ1cK4jkL3$ĮEFPD@p{w ,3ofggg#G@UkI[uHBzeF!/ݹeʓ*2^K$׳۽GdRVK'ennׯ ⋩U탱)+WʹeLxl'Gqu-ǾQD -[6iҤ۶m}ɓw9s_m됶YBpp0DEE-X'u|jՊ[7 "J:f ,*(HKI*).ťP(lѲ%p/).JOM.*(J=Hۿe6mX IDATD[dIլΝ;uJ87s nݚ>}P(\fMeV[tHO>_իWm}"*Zn߲\[]ߨFwѢE{1 0`ɽz:p@hhhլdjj9sh4+WVYfuT>{!lF6lhFĞ?qBY7007Bwmw:ro{LIY&b2 "\~׷U޽{sIKK \lY&MT`AJƎl!n%im[J0Cݢ2- v` ` ` P$ۺb` V O\4ij]x@ Tm$cwJXwI2ʲ,iOŗetX1E#yȌ3V (eTH}ϊJ +2":M-HL߈Fwa0 $a! "%Fjt] @0+ʪC)"WV ׷̥FakĈhTV]T^9  +_'9zꁕ!>F.o;z1}k$[ a"Ҩmx05gנψ!Rbą@ߪ aB# !+;aD ~>$Zgz!q11Y陁3CS0Fa&,0f>tOGzP;46m0ӑQLѶ9ª3BcsC8 1EtDi<7?-&JEI {*I:iReJkynL7!ٛ) & `iw2qFdQzᙡ*dH~OA:ԁD!ƣ4*GX}fƘFgm ӟ@1$m'HƳ54/s(̊ Dl-J;b,s*Ns-FjsIb$!0rjB'@I~O :3g:F.4~*=3LlOMz_M`pAN?J(D0EBL" ċYL2+ sڔmx3+W}|qgJ1U1}^AzLS82m{!C.{XHپQ3V@픅+~HD9e/J<Ӓ]&8T*UjBTUvvou5i6Nr#0+3Ai iy1; 1  Ӓ+dV"DrelH4jbN;Ӵ"(6TZIjZXiTp*; W&xeUYVf޲YL988* ZmooggTW@+u_0ojugP xM<lZ<`a&3A9fWOVqE[\7T" @}!s*jT 2f W9oWE+2WYrxm. a?1"Wq2YR:XpJX`ӷȬTcU+r:+"-]z>R k{RgLΤpLfÐ@'ǝ\ݭ B:mkeVV$#yEbWEPk$~_=8{0zR|ރU*yy 6ىT* wۣ8//ɭY)\Mt•3]cfU( 2y3+FlPWE.\$ܰe#CnвmFիP*h|Dv!=Mx'8V )\X2}[ōU'B9r WZlK+ծ0V "V^&R)Pjl)@yz1+ ŦBWm\dY>F#ąq({B3lGˬZ}$òF3bW`UT,֠A%UB"VURΌB핁艂ͬkB_5m Ja6: nwrױ;>xwJ0WPBE )2JeJE"X\\\̩_U4s qwO֙TXV߲F[ !ZuE bmWM72Z1#F'NCN矆 (Ys2^ @DXXDX1ePLtTF 7o Ҕ]?In׶ bj3B 8EQ)-##4a!5%ut>|QD8"Ag3O3S ̊h]ef5{hk:`bSaBBB~;ܹ \R~S mHśsq9 ށ8x=^1Mz8t6;Jp&&&-A?MLޣ'\?KP8V˭_?L(ŵ8S)ZwrJP(4`X(\4)-Srѹ2hUsSq(kee~w+{(K 4V4$]((Ͻ{iVNd)((}IbqH˯%krZ:IGGlپMx$)_ڸqÌ7;Pt]2d"d̙cǎ]hь3~]֍~]R2 [2Oǎ۸qƯ09R8w3x#?F8{>;|j5lupOR"Չ qqr)].>UhgWR%qu;>G&BO<owySܤ]?Zi>{!8(ȀH2~ǜxyyegg̘5 jun߁3t%}׷oFӧCǎBߺm;kg͘F# wR޽/D,nVXMjWό8ʘj7T,S:ZnMjjUⲨJuΝ3zܹ?FYteqQ,F O:a-۶׫W/daCcUJҢEFuKS˵.P@Iݜ+_NN->wxT,)xDmssJ\95~.Ņ&8S)jZV1cƽzST*ᠺf;RY&նbc$Ud1} 6@qm ʌ11Ede%Ӭ5G}ў*ӠRy#Fd~513-Λ$a ”b &ObW\b_9)S#iUK,2dhtL, yVU_ A*vUÇ-GL.tI]!;R*SLӧqq&IyСQ& CGyxHB>7n(-)1sVclYVw:NW3gΟ;wI[nlJmhce">zoŗPL(G*+F)#P!ffyrHn*UnJsfCMG}7#?eGn$ njDѶnZ^r6{B  Vӧ!/⏽ySҤ]?:lnBD8.^(.$~_CỤL[VҦj1X8Ah16up>jYڴGF"f|dx"{\T(TJe~h4VCdajպvJz7MVcg͘No`Oa]v]xˬ**#3h;sTUEҿ9ϿcVZݺuk@85AgALJMKNK'X69-nh ܅> TF@GS&Y:%+O+Z T@AZyZ6{<{Ŋ Sm‚|.e*LOIx_f>ONL+uV{[!+yw;vd>[.BGL(:8,?g'G99{ð>#tt?癍ܱK p/9a}+).O_Dd_5nѸaD㈈D HΟs;Ռ6uuPH7{Μir"kjX4lĈG믙hr֯f̜`XofKC6u~iqmXxVm[ZBqZv=6|'GJqz^ƽDҒҒҒb(-)F|PWAtVnh]T#0vVJ~ePVaՙ<.g‚W.8Ye*i٦MqA~LJ:;+;+}lB*J$ ÄBgÜŎťoJ5gnƿߥuDXO~r !ZׯDo KjJZ525a6``@;2! @b/x ? 0WtLŵZ'P "G&r=k !BHQkjd [wP.RZO~ &T(ty@]bv0 qI FXQS6INKWe'Yf}TVRvvs͟`aYAJ%F4lܨ ŒܔirYKKJLjKY齗K6V&Xq5 NYB.Y4Ye*4h!oޔ=|>5[ ;QPh[]%::gvY6_@Ie0WQJT8עIU@MT)9_~~Fuʠ!D£G5k֌<:tخ|_㌋,iݪOFd۷o>u:E<~ǟ~5*]F拴gϤRixxOM hT+Xv7 x蔞5]֮];"ݻe}GT6t(!kqiTAR}<?`}a\܆ jx{?YfPTTqqq~~~S&O 5l@͊֩3`@m?=N#/v6]<[1}../_~hD"W7Zjyxx^H,$WZ]Tp]<+^-P.bVW –6匰)Mk424nBj&VW2 yUR峘UB\د IDAT Xe`H{@4HtiċVQM\JJd7}'/uhη~ח@d[~8(+_w+TUjY- !={F˲,0hFU,kR-2C8oq}˴=[FN|z= 0@֪Ab+QU!jӧO#kFuV' 0UUsWJ$ڵkg4qK^a&PvJ[Daa!ajXTi0Fӽ{=u5߹sVk~]ضmmfgg/\GD[J!!,({{{{{{zv5f,x`3 -cᕏUm x(cU4kFP,g5lxJԖqz_5mo ʅS% UU01`-X+VZXb@VE)Ԑٯs3>.,J# ի׹s'<{{/}>hʕ;vؾcOd0S:uW.,^D[G^^mmYTA"όL%ɋxiI ߆V(^8eU?p֡2&7@s~N!!c6 NfNmi>>ў^P1&@g$`NxbC1F:ŠV"!Yarh_i!- 6鷄 <∼++"[L'ɍD+ǐ:L&.tɒ%OVTmڴo}}} ,XpV۷o߹sRf؊+K`dLiUWٶw1ԝy#pSzO>9sCBugge|oԨظQ#)>>ߛMVʕ3.2NQO?֭[a6` "-?KJ5wΜҒdڵ?*|h`\JG:V 0E{Jzh?-b(i`LӆJ,o@Ghl-/BƝ5Ȳi&fIYFЄz.8=LBzj R*؂bU0>"`}טKww1B.oݺuXjUAAyƎK沶aΠKrU:^-7{&vvvS&OZbҥK&裟~^UQ.g3w^ƍuZTT"7o^v'O9׳j PF9|䈻{VVo-?lڽGgH 'OZt˖1Bˁ]F;ٛTx&bc&l-Y cNz`~^3!=F@cFؙW{sx¸J|LՎ\˒/ӛEqAU0!0ajk ƚ3e ŒA2"us1j+Hsy۩Vzp ;;={xvx"tUR͛7ȑ#,Z(pM~{*lwvo_LBё۵kw?:`qIn2i򔒒gg֭[T"f͜eի-7|gLzɓ'a6yԿd?תUkI,-[PTK~xB \ᕤdįԇq!]zD,"%=njWC}'W0ҭRl`iF*HD ~̘.@*1`ZaC&UHE -y4xdBH DUjI"Q_Il$6f߼w/:u8ػw/ M!MὰM w{'N[V{9SWe敇2'/ƚDJ?阩`Ic }>Ckt?XEj~eJ+*_ 5Ɖ|~LY2&*C +ߕX"lcƧȞN>=qDJ~QF]paӧ B\K sEUsÆT׶9s[͊Wۙ+՚ :,CnrF`}cE9Vm60v4ZW0+JG+ ]΢F'&Q/$βOo3k`n5P# T4˚@Nkq ֯!,\pK8̭(wWA +%:thZv~gݻT*uޗ7z)ʴ`&[ r.li@ WsjXٮ?+0\6xh4j%&,΂TfNC2"YOs7\9RE+RŊՊU`_W6`nݺES׬YC0+H~ҥnիF:cf],lQ<7w|ӡC7oؾ}ڵg6Zer_eoݲ3Ѝ7fRiL-lWc·"Ep·x GVgekrj*TbFL!@N "HPP$;;gF?\La:c>ӷOׯEGEB&NzutTa^h cJ>O׷_}Ϟ9K-,(=z;.S*EGE~_kꔩٯ^2?ӧNӷogϜ!d^pqݺ}2 GBD2l谤$Uwԩ,;w82?>|xN8ѭk7P S'O|~ӧ!хs=jGJ~5&..Hpј'NPRR5ճN߫wNe^eeBj{n?ֳ{'O@ 8 ڴnpp 5::t@֠DpϰyqjS3Lrl*p}KYrFK57Sb`1-O Ɍ.tqi`t Ԍ&z)Me82ȫq 坅S<[,Dg);VРqS[H5 X{Ӧ;v߿M9믫cΞ=SW d2YZZm[o߾a(AAsι{/uLUڰac-ڳgφ̙CLMM۾}۷ׯ +VXK.]v!e/Wۻ&LukEu8p T*ySySwq֭u~˗!!/^\뚍6*FXyy{{7l굫J/~ǏGDDܸ~aÆQKHXZZgϞ7o]k׮ۺ:u4x={4Rk׮ vرn ߸q#**իrkQQׯ_D$EhJJW_}U+AcY&<"ܾ}5k.\(JKKSSR7nb]|mo\/o Z*:&fȐ!֮UT$[nKgmY{27;vO`,t|-5m*JmL!u 0Dj=}0'[0 #ޓB>N,?6o=jd4$!#ǏO6],s-֭P0L 1Ϛ5K$~or?7);Q @QQ/]%ŋQQe^|9::2{xzsΖ-[b>߸8`2Pu_e ήnz37NFEetakJj?'O 0wu^ru摏't<#3.QN6i/+#54o6l=j便6m::8T\jm.:5]D:W%%%\ 2BB2[dΝ;&LkҤ7btz9&z9Oq蓚pQf ڹp'۷T*2er&MrΝ;[N.]|yر &:O83gD yqJJJJ\\] 7w7I%ͽtyMr6n7v\.y攩ST*ɩUqg.**rH77BT%ʲp@F,JJJ5}ޞ)B<|4!"3]{ej-T3ϩf*cjwV9? +b38w€}Ό5RVoܨn#mcs^hhX2L8j׮^/4$5-mgB(}8WfᛑofWj᜝K$JЭD"%yF%H$"ݿ30B||}M>mڴ3g.]t6OaFD:0RW3fL 9}f%5E4+vvls׮MŹq;wl&v ,BٹX"QD")(,((,H$D" ٲyKXXhh͛CJ$IagϜ>bqJ$DkDZ?r!@blf }?F^^^/^YH$VR͛Gٳ?jzϞ=Fsm~}S"##?0t落{EF6c!I&߿}v|Zމie^k޼]B!1֫Wѣ}_mzlG&L|:tS2=vmEK[;ӣgs~[Dq0Z iР#2c)h}1b͛ QQW4yԱSukM2xN>l]ûK.2cO }2e+7WWOZ- kxyݺsC"qssZ58 $KH\$!!vBL t"bF~CƌsCHdo=p ġBiG:_~>|DpHX,H>;nlFffm6}t*R~:GHJNt ?͛{?L6lI0"ַď??/:tXeИc=6lذFCyvk1ѣ=/[lW L˳V+KުU:u4mL'!yuhQkBLϺrʎƍ֫gooE4fظ> 11qqDtIնo!СVةࠅ ޽'[#={6mzløħ>Od)U-(;?#0I&D:_WZ.7^#~L17CjX%0\ Yho+ý?SIw!,';UtwiT)EN"T rT[?s`Pa)~s͛7{M6ݽkw /FWX2J3ڏ\ bceĸu7GϞ;vl0*Xƥ"1VhZ93@C ~0P4NU-YgC ZƺٰOh|_~}X Zà*݀ @ 9%Az .JqRlښ58fhx IDATDPPrN;E뢟v?\%al+r8S>Y1!,ND Rzz꼼ЙfrP(Z k2lKlD Dݻ+sssիl&Y VeBpE&jrZdnjzs)ӚjC*LvxxHL`+Vǹ3ir,vD:i[-!T @P8h *5(W@RlcGN[?|ܲXZ;(W/h=y>UMu ɷ3f+b[jmv>샌[Y3Yg/^T&ɛ^UMmv%+M튘CfƬHhfCR>DkEFjjhw"%%ŋ.͛a+C1@ ҍj%2+D(~D2*;)JG!]t`'g>KKJo3Y0;/!N&|38IҠ#Wu_lC[Xt˻9'dM:|Ų641ɼ lT\˿+ @Vn,CWsQռͦ*ymʆU[9dFF/ŋL??lѷVlHDP(ʬ 0pR:2mk>@0@bYq})Y{O?Tu?Yz*? )Ua+bj r@OaSq#Z8\s(pAV > e[z+HʇQ#)Xʺ fw%smIKKU;|8%%KJ;鈨Ԋb4FYUt%(0@[c MZUrr Pu P SIQKZZTb{ lQ,T1Y8L3}C;"۩0shJWo&=Vo=65+Q0qdnZQQӧI]ғ;GEsӧl6Io$.`U\083@Ty-\+[s<ȹN+f+r"@norj)pp{4"Z~eOjWaSV}OmW^=x𠤸X!a˿..͚F&U,k- _L Z63:ԓr+[ *%nZƚGYYܿWl#·ȺCm;{g888t=;VzѭG#ddfDsV\.˫c+/ނHu.Te)*V¶uAe:js{(P/x{(7(Aʒ7k|ԅ~\;{ad3Li5HTlQEcb{H$ W }yU_g߹}[*cFojݺw*8kE+AcN$ϯ]ܵ{O//>`[܂@ DN 1aPPvv[7RO_ VGYA0<&`u蘓BF);`Jz[u)aۊ[谵RC-nzfJDR(>WdAT>S4եSh .:|o8yڭF-ݽ_%~%-VLl7yբծ~0,11K*utt4CBpttzWLkR9D0.KIWv= V@0@T|cDV.2#ɉOZ'>}] 6ݤ7yZ_-T*r^2پL[A,WMVNj dr9ӾR})VU# B(pRK jOvoؼgVߨ#3bk`Ĥ1b'K͌/9_^*vrC;O>]փP(H U6|+Zgzr2f@S,'S,KqTZRb;4_[qYYk"$ R/iqQV]ܓA^&P*L`x V{}C +,Gimc%YƍD@k$@J:6EKBT*dIK=<$RP~8JVҡSBj8 p6Khevݱ{]uٱ8qTAŋ[ׯp7jekq?(K2rje H,+QbA^8IBͫBIqrD"ԭ$B.GĠ{`0/ @ 35C@a H C07ptrtprP'GG݇V:B, ŇGB h C*{HAo))iY-sJ^)VEզW+Zړ4<$AXs+. wZgP"+**7;v7 ԩ&!YER)'u㄄n gggx)wkݪeDx^.8BP>-؊"oݾc,Z 䲜!'2L'05-F ^2b;w5|4{l#33k wr={Brr]LL E/^2++KgVߠ&njck=BQYvftk'$<)bX0ahzr-Tv2[ؼu[Eg.@M8$C^4niTʜtG-;O4 ;[`eȯqB. j2\Vrpqq!``ؚ%MS>J:)YdI\9fr,$Wz3BBBr ټy;G- rcsێO:ݭ[WېFc/_^_  ^99}ի1{xzzQ3**zA=bQNS,wԉ{uRNa\ ]vvpp 8^F>~۴Icvhq&$dnqL |Ӥqc..09%[*E`\'4i܈Gڽ{.QW^k^[< w"ߚJO<&=e !l# 0 K2@ll-Je2JWa6M)6jnr39w /9::899%BH?~|VxR@&}*^t"ڵC999988 tn:h  .tP똬aqqɭ.,{|| HÂK9ssb"+uh^({AQ{h4gt%{Xh`o("܁Rr\w}ٛyw߾3󎏳LKJnbze,Æ _n%tr%PP־ Ĕ3| qЁ)f?AڲQzkcAT+&vQ&#JKKO?J,N|3]0cmv-vh p)&T*_oV-[صgvmݾ]Z jr߹;lIIM  LIMqvq"EX[X%D N+:s<)T eRuB:ǷO^㚞[r c:Yښf1 #M2JvwoaX|.[PP` &p@p ipMJ4,S*# ,Llbs5dՠh*NƯ[tҥ#Gs;qS4VRJSQuqq)?´8.wgϞx R_VӞoD C=/..?~|Æ ϝ;'/_N^z &L0nܸ 6$<%$<#$X7o<O$G PUfXu{K_nl\5Vmtܞ: a:+_4iԸqpAmI_@׿/ԗg&oŔ*5y='+K7u w)((rAas!\W=!obY,\.'=z_V*<Ãd]=%f߻w̙3ؿkΝ{#Pd~OH-͛={,**ھ}{YYѣrh^`WU*UaaׯU*@QT#0+ :C!H֯_߱cǩS  9sQ}?в+Ar7b\^bSl6fT"rF6|->^4E[XҲRW7ěC[]\\bxŨÀ愌##$* ջ}SM(E;~Y67+$&ʉMJs98Jʡ\ .KvK[u^{QOz0pAXe/N  OBE9lPAjZF`3_Ϙ% g0//ǺY@ yw'XVVJՆmL< LsV+eAY/˧`P#*R\=ۘ#x<L&YfBg94m5Ԥj[VYXu(s}xTj0s7N!̜ynm$gX7%i ޮ 9Vm^m\3"4GS IDAT#GBBBzE@@Pn;ď[1/'Ozxx U:wW_8/H:u`p===<xllT*ݴi34/ʪ͛7|p k֬1c֯"I#ٙ`(J6MNlӲ9DQT( KFEEq,ʡ{Äg4ƍAJnS2NcFk7%FN뮬zS]49 Vc+ bŲ%/[ iVCᵐ pF$(+)Yd[ ǟmW˰lY:@0m4jQK! rI'(oaʡ+\ /ɪc~#Sz -$s@UTT:+WޤK7['xZY5|ԖT[rLg۲SAfͪ$=!w NִѲek8q"((hƍum۶ݶmL&۲e n٢ jj@.`0&MY3~x ê#0&& 6myn&7okn۶MnhX +Ǐ,}4͛53gΐ&Ota8^haɓލ?*7;;D9ber%5X$|Wa'5j'fN &vss+ĩӇ7n1p>?sC vss3GR&U_ {&DްM-5әr9SppÇv kߦMkMW~nRo'W!1*V^{ٓ E3WC-Kmjfnj|srܫݎ WYfAZ҄\I 0 PcfL <@&%r*Y-W)=Ex8"&Zswlt/͹GX\;/GTssENNY1õEV33V N(֎tM~C[qf}k¦+CMmd5U%(-ɲaº9Dl|MgkRO Wu(wh#S4oU*^V.y攃PI4իsjHXXݙJi~ Fl6mQ ryFh4Fj~݄&LÒj,eؓk][yZ״" =U˞j8I^wﮐ847G|5aҎO?mO[uN6=}hf͆-YfӠa j۷QAkLm9b5csJzoor3m0jYF*alZקusIz'0h~7+QAU5R_hw]dL&: )U<ɆsUԡj~Rb Vn`w.XMT4g% *B:z~[iРA_4hZ 4>H5*5s+ %Y_sgkB>h+gd:v0LVlѡhɇ=SdQda ҳgxAy_[t>ZT*:EjiOv~'PcjeFe W1j5 e4mٙ.Ԧ~'s\ T$ %e?yyy28+a Կ'uX,ͮ3A.:v3{[E9KJ6oP(p LaP(|=Ox℄A!B~TtբEӕ]G-gZL>V,[b0 V}h8M='"surrb2x% f;wt{?i+"y<\V2Vx"g*+{lii͚hhr6޺uk8ފ~,<,tׯ딗[ۀɹ0hx;w?~}F}رÇP}A۶mqצ}#\vǺ 3Zܥ_||UdEz%2䖘{}XdL4I{l HT-낂777ONv{^oQ?u|p|_k7eC[u=lCV2>sim\'Lkhnxqۈ;9ݛP0#飴ug xIQS~4{fU:}Ǎ Pإk7K|GG~MA8ޢ~,w;l(իYӋiZ%a&jF!k+P(kP̍XcGJDĚ-[#"3onGDٸɶmIR>];&eG))|rQtsI**7_}rhn&O[Gڀfݻs8pЈ#ڼJ}ŋo߾@$={"֥(WdsMJJ5_c0tի?NNN5s6.\ڶva֮>p۷o5~MʕQcQiP\\f6~>}i֏5jڵuTg~}%+ׯwGa ' K<՞M%J&`jW@ 2]}57@?д%p8z liivō?}yC@@Ъ}p?۴m&yYi|إ{CGH;-&R%+ vb.۴iɓ'66/^`Eޞ>}n׋-ԩD"9rΝ;/^\kIIIquu HII (R֥H?S.׭[{~a˖->SNӦMuvv.//xB``m|iG&%%pcǎqysoڸq=V++;^^U?@,}֓"ɡ.bdJBWP&eQ rrܿ?` KeQ#W,~Kw~,5tw+BN?dCDqq'֯oф[Z&WxowZVZ׿VFx2X,C,d2ff @ P^Pl2W=`3Aһ$ʠ(JCfZZ=݇:ua=+&<܉;wYsŢGYȱEiy9.{ӦgΜ޳gwֱR8|Ȅ ubׯ?gl g&ϕJ폎ׯO| **jܹdo;ʕ+#G1bիW>9s֠ASNwI bbN49== kӧOV?߿2WvﮎuuӦNԄdddL:o[jmj#I3`E^ӧ{ j^AD6%٫[JJEflz u=|}Xծ}AQQVc郿ETS?8;;5˗]N,..OgϞ\BʕFzZ=O>MR2$`2RRR2-Z˄,+88I&͚56r8,pvpC3'`P&UKO{:AC:X^@ڗX>l+`"#ΙC^ *jHKKnjL9ɔd9rb?ZGںu+sǎMؿU˖;Osd2.pʥRKtJAu<6ow&MBvMFDQLtRm ={→Wffd8 ghyyyjj];͛2y)Ǐ9"7A \.OMMݿHq1zhuSRRRSRz5f贴TK,˖.-,*$ %Jr*Ҙ̼k"UpVK`O? 'M`P J' <<=+, H$!MҳQ*"9 Ёtf;t`0^&2[ X8ުA)H<آEsK:߶:xpܸ6Ld0i{v;gΝ;kH:uZnJ%Z2ˈ +|>I(dI8H$2ACɢ*6ҥ@@U9uc-:3][ӳxÙ:1T.:yRBWO(Kw@ dй^:֬tqo#:X(: +4yxz=#>>>>>+vѤI短9|Ȏ;?hvBW!H{FDVПfˍuvv#",_6x̝Jz`0XbTRU3W>ܶu 6t肅/09V;Xpk׮_eVǻu6q'''F:]mk셞={~Ïh7hSOS,++{w~qF388po.^rŊ|6nlVl~6׉y&߹sWk[RX`,u .uHW7lCOWap%)9|f mM:yoܨpE۶m[p!H>~޾Tjթc.,O/N]}݇*:Ï?x?1 ]"}hui8wmBe_~Gϝ;Ofj <<܍:s3ygk<4B77YflJJJB@T\\J Ca(b8(aaWi֥"W(//5b5ҎyϟZ=zDbʕ֮+>X?~}l\7 Jop,mupp??=fADTvʕ׭۩J#wz0~k|E#k M@dh"Ӽv"Xjꁖ}U/ smH&~8"υ =IW{ IDATYY@@Qt페~Qo06I܌5d@ PuV]P]h\hTW^s[,X@A`m>geߢUACϞ:Μ9:߷/c$:u<}|TT}#HOO}f=zO}ڪU_ТEAΞ= owqqqӦML[ s.IױkO+nM=aqAHT*ZMYl6p8'Oi]^Ϛ/0\ߟիW۷xqiiCxxɓȵS_>999$$dennn仕cd* 5MJ")AEڃop`q&ops[)NQ|ܹ[wj{ ]چ_=sF[3gshrss߻K%s;8]ZgT*խxxS$n{ngR 07/Ws/*( |/xpqs۾e3Ep-4g];4xIT*w-v3tptжwn.TV5 9yysܝ?w6z8[ܓ ` *%H[3Hœ@ V2%V iа 0 l6u>aت!sO-dvQ*@chmzEpwwo֬g ][hXab- .w)*}/YP%"88AJLJ!c52Kv艓M`Ѐv"֜^1O4hc`0wnG u{ Qw F'ZKbU*lKd///OOOhOJxϋڪ𗤘...Cxxxh霐1sDbARqBAzsqvwg#&7,MnEKZz dY\XFKiР3ȷsAa?lզm#z(uqhtoua>Ay|GG (6kDaA>WqYM5w] y'wL&k.wT&\$!٣'OƎaXȞ?K S~;kf-x_AI,Mh-ΒA`^9CPݪiWx>9YW.],.) Y:9{4xIήna0J k8+rIaAYqI=WW1ϡvQTQTCS `5Z0w&V'zIF!c0 juZzz`ubQ;#{8:ڠ`8; rn8Pp9.]*Z,nŲ%/[ i4ChGixA, 믒Lq  ҠAOmճ2hP`@NX\7P8.H(.{4hРAL&y:f!!|BzjvmxOW>euý 4ha?a"燄4V"fqpp 4oj(S8p(4h-淕aΓ.\KoSFV>HAyOâAk Tw@%wEF-kB\77-? K놟4hݻG~`ѠQ[<<<#{iP:.gH2f6@Ď~@T!AAsً)2$99@Z/22Jp"((Y(ܾu 5 KvNv,qOle2Y(HIa@QC< bg...~ygH$ϞyaC: <>؁wpt3vܾu ҽS}'T&l140L]\\ 9c69J9+..05)*V|>LWY7G\u/ڹc? =v%EEU7H 4hРҹS)*Ao8A s}ڇ pP8F`8stֽ[111_5v8+p~}vWkGӧ}}C2Gmܷw%%%ǏkѴJK `6Y\8nuݹwM~uEc~ݸq 'hJ&:\zu-ډ|m."mk ˗.[VsknРAF>-b}0^4]ܷN}o@ //>Ug 9;w<@;ϟ?e˺u~ᣉ&rM;vN7(̦2{w._mNBm|||-YL?|^6l8yΝ;"">xVeSP8|Zm ~=}XϭA fT+ d#F%Z\Z4x8ݺy3v#Fݻ{ !aw@*U3W,w\nx9`aa ]rT*J;ڵkcƌ5jիW/^4fQF_v 0 6lׇ2ejFF)`ʕÆ _zE;⣇?\R'0f3gxt2 pt7}Q#Me?~HWJwx?:t+ 22rΜ:Zə1cfl+W CJ5jT\\ܘ1cGC٩3g)2-_';}ڴ?~SL» ݓ/xglݲYzgg粲2~e+ ! //oʤ_~6GMX$4P`6i裗/=֨2mZj͛7U9j }qp'-/w LgioMکSGdjBK"Ht«Y$ةӡ#GmRj޻^sKmҠAFʒ,!)9lcR aA4l ?`h?}mڴڹcۑ&OLîQ9xNv6fIJޮ]cǎ+.}$AΝ?/<{YӦd`6mN>#ˏ8ѦMk`0n+tqZk2y#YrM>\%#rI'$;ĉcǎ.^:f?qĄg cǍKNN4i&f&jjNS@x/Ysf߹}SXǏ͘5K[yb0TѶmo!33sd֌"QΏ6jݚ-\6g!1Cgϙ[.-_xnLx⥩ӦhܴiӦ 5o|s>[>?a"L8aML x>"9X;wZrkm>}:++1 nҤITTԍ7l.ѣG jҤIddKj);VC~_dɦM.^b Z+VܸqcͿh"Zh a]K3al͕@X G| -fg2جvaan*R_Px'>ԍ|RY6;?oʨ'VgNt9o Z\.dϝrS'WJڵ/O׬L %P(\:8vH$g 6|Zԁa9 zdϯGj~C sU߭48: BpnՓ/]VHt< TW\O,:;ѼV/];X,pxf>}nZPPhºy<8,2-jǰ\Q˵R6 l yӍ7_޽{m.???ӧϯB믿>cXr%ƍgϞ} ?>}X r9N'-!R (7(ӳg>kz3ŵD"IMNr;8yEQ}XCq/E%ID >,}}|WbIQjR߱Ƌ̠ebsKn؜P(˖-ԩرckH OI aÆ h :ujҥӧZ?9rdʕ ,hFx IDATWUAM˧a_ _iHֵLx<)*((-.wj>Ǯ`ȸ Kꎶ+uv!˧M3lذ_^=&9lذ߯1b=~tfl+ܾ}{ҥ(/5YiSYrKF\jxwOf TnTvI~~~6w3t+WU~I۷n?/X`ܹM[}G={vDDķ~KЫ"`0ѹF 3T7Wd9b@O> 'Q۶ʻpriUQFMWOMMQΝՐh]E5mc2&0Gɲ@d503Q5ݗРA 4h'@n^ұ[_F%AVR3bJ>T fڿJRss4? c>rbociа:v"""ڵk*hРa #Ť@=FUWDٙdVn7V/@.]U~nݾìsʰA'J_!koL :ZrbQ0ixWРA snok=FQQыDTF @z%AW c8HYjeHM<R@ڮE'.tkz[[z-,.,#Vڧ^S`$*/ȼjBTS%Ioii^^x'ɹ8Zgkd+Dk("J$ YS $ dDx"湺]*...AJjӶ5 m+wJZNJr0ƶB,B$F` ib$nnԛgvO!Dm$BY>`wF?9\o/7=E$mӹgI_G]dasv-Eǿ|2ܫAWϬQ]7ݬNN jGd- Ǭ{ {Sk0<}s(~B-Q jaWWόC2 01Q..>>>!'meYWE,z)7/BGKdXUƺ^oh{n3lv/_|[CvI~C~B)^hJICتz,[fgB.$Ӕb3/OKOMA,&IAϰ,+*.."|e9Tb,`Fm̳$en_`D&ܫ-3;+#αӿ/^X?̍ML{3[?7Oh޻(άvl[|e֕}Z8J3j"(q.$ 8gt:ڣ^^43%uЋ]ۯ{yzV4JH/~7=:q+l帒a kW\qC0J 77A-7mV#o߾YfQJSh4/駟ڷo?|___$K޽{ ,t444tSYbźu^9l/̿NUJ Rt. mL淍f0n\v)RNNE$ruqqsڋ.s`0P{S>x[7n_&Mt +WmҤIΝO8a믿X|ݻNvǏϜ9YE2s?s֭>ꗕΓZv޽{Ӿ\⭨u7H#zwhib$7b ߽T)ԩӫoBi2ÇI&892v?{ͪ<˖-_lc]Mzf*8޼呭ZzID(#zGfOUPrssG٫W޽{&Tooo޽{/^6޽{UJ%hϟD_|43҈H$ի׬YN>]IrҥK.]o=zY3}P񑭄8YITm]xMQ"L>=<.H~'o-ƾ}N87omi||͔7}4ҳg7n۷fTIr ?wRVI|[:Y՚*ԓHH##:.1!!?7բjžʵPx T-ž={veyy]jռy̙S~e˖UBG?~՗*ҥK.\8p@XnJMaXZ {z\7m hB$5n+zK)m(RWf羾{c =z32*{d21S??L<3>K0PH 8+Rc\2I!ϝy, 4ZlpF7Xճm*8+XO>V5VB)o?ꫯFb,W\pއElٲ5x0Wb"< I[v Y|L!LE*%#SLfZgzV //jc1JOe0 f ɤx#%MZX)Z"$sa1!3lLó8,lkR3$ /yF 7=4/eV 1+Ӊi4/_֥{[)kd"-إݮkvv0u ׭;v,G 'FRR˲k6u|aH:uJ"V/xzPeVm&tX\xdb[bX,K$ӿ:.//k@DFʚ7oK/DС##mܸ?f1+WF[ĉ DFN޽{׮wر)S>??gϞ(:v@Yr +VH{_ԭSvx`5^Yn2<BlNzWKm54nxx8)jg`-V8F.1>rIz t(?J\&O%sݡq_t?h*So:Q1|UgLxpkڴsg9ʝ;In 3t-W! mlU/ ݰ5k>5QNJeRTjM252W9sVZzGOΜ13//)K m')KK@'{`c0XGҘ J__B?~btO9z`~Qt=`vv6(qڶb{^^^K_~˔rLUgriԯtG͛afF朹s?~jUL+@ IYc߇޽{tlR_e2Y͚59[nʕQ0'ONM}Z,~hҤNmٲ%rssڴ1kK{z2ÏZl)!I*YW/يVg3 9JP̙;w SJ0I|w1rĤIG٫WRr1z>dYvEvDռva]FaPh&Ȋn6~#իW^ g~Aѽ[.ݺLX1jȒ7xݻ4dG@1nnݺ7lL/=Ѩ6:kR.7n6oތeʄcٚ*TrչZRX6iXuTH[;+#D*KMIeJZXCRJ?qA˖..k0cVXR-7BjZ]drLVVF$)Ν;өKW*Y 4\kҢi1j՚I.r`zKIMAOE"ў}t9 -lh*WYEoӪ:1-Cx]GbZiu&_rs)G)J'[[χ,ҪnYFoSUZ)`t@U!/OWFGs&sBoBjZ햝PjRVjZݧwY@o+Ur)p ` j !dddߍgD.g_ QJoASR3 Wԃ0*Bo+(I}#Nbˣ]ѥG,%I,!GvMl:y-ZoJ6\bSjV* !#HNN@'{`SqͲ,qN-[dPh᮰QR{xX6=-=ۻFfV-wMOIMmڬёR0LRRR+4%EO{H O\j 0fg:wc.\8OFZɓ.^P"[}l͟nӦ5KBHvZ5k^r˜Xk$@aVmMbX,ʔ IDATK$ӿ:.//k \> -Zԩ?~S$777 Aw]zͰ#[M17ߐH$!;nmS~Æl:<ՈQebkfT~x׿F:4~u$Y%5jm_ϗ_yjڎ/e,G3uN";p !6= $$t롃mTʥDhl x(okUS֭|#BBBCM"x Ed*]mu,|VgՐ{`Fۍ7nذaq2w;z___qqqyY|+e (eVBĈSɄA^d+z[ jwT01td!h\n DRG?o}#\j8f\/ۡN2D,&b1HH^^_D7Gљ33==P}7o"}ǩ2hq0HV"D$0WDiJJfZ] 2T,` cSj TA*7HhTh6<`wv"m3J^h5r(rqiJ<rG _UK*ܜL~M d擎O"iJJ5=.NY00 5LX|W_"X Qˬpq t(?sScuxj/%áVg6Vgq$?//;[H.ߝ>r77l|YVd_-0? Xt],x t"=8=mbwkQ CFfL.LBy:WFfJ,ڰRT&i*tS(DlE=m!^T[܍t`@^/K-mҼUT*]rss<*ÅPN]*JV`fJQXpwáz{$&%Ԕ_O}C9C-aY/O WoΊV̌5jXK3ä$ 8hnEMV6;8D~ݕIujN~|#YjuS)5}j֮][&P' Ys=gN[\.xۛma@Ykf*6S,V`Os_XuWRONnNIeJB"2??O"֬˖ieU3rru\KS&'j![̍:.òRT"W0 K Jpۚm)k1*\/[`VP-•sc& ͦ:њ#KUL+˲-X`-VD!.2"eV&FJ&jʼnX87P Tn)) ul0OFffD"SJIRG5wWCRni/\h԰!,Gkpmt <mӶqr77A#2 ϝkۮ]e] Kkך7kc[AM P~eH@@RwE ޵yPСGL*U@[ׯRnт(->z&u-<\[$]^{`=NeO:T`AjOINZ!¿޺eׯo.P*err2VѾ~n㱣˗. pRpp%O?dVcl+ov32qpa "uk{1&fzd7lT߸ӦNٶsWHH{)kHj*p*` Hd+pxVF1sq+Vٷ*aYJPlAڼ5_ޟ&zvy( .j![@qz.]!8Z.bXvFza|ݭPV"H] F#)ZazW0UYK X!^u+˲-X`-VD!.2"eV&FJ&Wq$ CjzBVsgϾ};n.߯oM j׊a,T;9mlh|qBimfC4jzyy>\;ej;T8J9eM?'0,k(d+pqf͞3:b˰lV!!!QVO8alDĆ͛Dz׷lBCrﮝ7_i*Ji^^^3 2 #\]]\D.ʵyk08W0yFPϜz{=gŋڵ#|4~]`0X6o1>>>˖,޼u6t+/42btn]=6dίvM[ ʰCѭkS>=rjƍ@|F7a°3ԨQ#/?XS V`WJ*+3çf-A#:lXR҃۶ 1r{Ki\e5Z휑#!#9|ϙ^y9'77a%ίmΙӧ5nԈ?1???4$M֟b>G`53O}ZN^}+JTV>tIrX\l@@DbA͎J%wxߺ̚3{CŠ woK$R ZcпΜ6FC)%r܊aZM8noޭ^yҤq#BHfffKʖ@jB7 z{tIiOg]ǹ)x_~">_tUV`6i7/_@ vWDmژpCVfF /O6I !=s:Cdffy7oּyx>/[nfS{ m~/T*exk=..4/˾Oce[\YQ~P~P~*UYѱv] ,:޽ж%9^80R<JMw![S%yԺmM5`0?wmv5j4 7\iִIKSK@٫ V6lA,8@ ,a*~GO)]p];:|KR5l+г+:ԢՄ TSװ^$L0VLP v>zݻvBn\-:BeU r X<+KN8N[ǎ._44,K!K,>qxʣ]~s zl-ZijIA`?pcv۷c0/L_*w7M:%66 y3mʨ3fΚ6}ʨ sͪU bhB2O Uf;ڃOs]B>p\Űlڵ.>0LO04XM Nv?<F#)ZzWP-d+`z,9! 0YN箛VU ZqΝ=O.U2`|) ^{`=fO]-J)[,V*8㿜<٦m[k9cFNNΨ!#Ni:nm;wC>own4Ҟ:{ΞuJ$&).8l0kիV~f3eHHHԪՉc#"222Dz>k٪#|Rw>J&*j-qJho+ 0 qFPϜ㯿4mj1{˗c۵v꼏>ԹK Uu W&L`Soܼe<`kcFǟ5͂O󅋖hZ/*#b8(YBG1tذ!CB9|33-"kϞ&N4qÇ ݲ o߹ͭQ``leBk5 _U@b z p6Rɽ;q^5D"a9sx=4,AO'HJ;1--ǛO!b{칯mhzk}Ͽ> jVRt, [GJ!C噑vbL,+(~HԺMS`LONFzQsrrΞ;߱}B1cOd}3O#,^<3#ݣOԨRVv+<Pj`,n.d+pDR ˒Ԕ47RiACWu~]g.; 0?oދ.קwΝ?6-zƷ|c朹+--da(c}N>LRa(gd{0ɫb:XjN5I)ŝۢ&[l*mєЗ_Ky;ve(-> ӸnBіDFSVL-i:E7A'YNĘqy>H ˲"O'z }j/;eXo aecdLnS$''WKa=ǩ!0 ᙑ~A~,DhZa.ڥ[$Ī31TChQ=)N˲.nrT,1 2 |"rgYOSkJ08(YYJdR Q0\ȿ|ԹZNJDFp6.R˲ ÈDaYq SK؎o:lEosQbc~WV߻־]JOKx\@BЩG?S~8x0 ˲ ÖCX]r!_E.&n߾Ir77A6 ϝkۮ]R[BئM;|ҙ}l06ӭN* r*[+q'js @o\g+aaFJJ)]p];:|KR6ltr,uG jJ{r6Co+pD$2B)[vIqÊ,P2YUMAF񔫫u+˲-X`-VD!.2"eV&FJ&U]un1HN׬?;|а 71bžkwY㫭!8T"d[no\б}]){V-wI8.rVk6̋11##_a}ƍ~~~ӦN*ȖwPi@dRs%\q{eԊ3gM>ceԊK/{ಥK!7m4=\n65V)[=<<!^^^Fd?]󜆔W'}0n޼E9:z܄_1SJ.\8z܄}5z}DN',{#"+_+*m84uJ d+ptzkܾqz.]vvڵ"[Z64,0GE}}ѥG/YZvv;i;pa܀O> !;v`r`Æz~`5+!|lFec[=aR;m٫hr9Tєܝ8yr^=gϙiƠZN1AJ٢3^BCɓBb1!-QggBpE.Rڪe0%Wc.B1S [AUMzBQrw䨈">]:b={:r;ֹg߾y{w׷y۶ј>>cGWD^7"!!!;;Dsk"PFb:*J6H  NHLlӡ]JOK9WBLz.]I~l՜۴дI3 εmN$kk@(wҥ&yQFZFGV p*JՍeJJF颅 w<(#^J*0KUUR{W0Dy[IpH ǕFoݲ{NBȍ׷EGUTR }e1Ef+eh6Vjw{PB(-mᗭK]x)88d'7c/dgjÏ1G*RWc3Y  1`Ka=pRzN![(F̅ ۷ؾŘky1&fzd7lT߸o)f X0`BŰА D捄yزgSdXd,x t"=8=d++eXxeԊ3gM>ce kEf*Byk!khLk&,s[^zi3Θuᅪ]p\Űlڵ.>”"֭Qɮ[&CswѣamokϾQ#5m΀~}<ӪK ! /_ڍ rnp )ArJFVz-:!Do00C)mJIMMd2BXjRR![-Z\ 'N\F`c0pXǏrdmo999F OKK#>\;ejdiU5_ݺwu&xL}}MC4Ww?q]=ml~PT/3kXݴe Zz c#"6l<&bԣGXeVZhqEW,3|Uc^ !ؾ} //2` _ Ko8jV{]xQaԹAK;qrÀ vwWWmJFzZ|baIIo6|A .曙&h웿\'tN@{RڇR4�}D.YsvzhXX1w^%Im>3˫^U x t"=8=>{zgRR8!_֋Dmڰl`0<=w&#=ya mnC* Iy$Sإ7dRX\RZl_AjX,%HJ7׭pbP'c[=aA\J4<<YfaTCpei$@ԭ6B5?Y\\\i^}-h!kٷ' q!.21T2a !e_`pP2I$r7LO)eFп|9ZMVb:*0(HP(8JU"e$z[}1?U}?~/yzg gj_OP 8z}_=GŮb7(bT5{yH~XN&M]\*kjkVVHRJnٽk'!ۢfPd2ߔ:U 7햍?;|а /,]=K~v1_p~&,Rcpt %Mm箐 <$c"F:j5iFV* !#HNN@'{`ScݸQ ?0qV\2WF1sִ3VF(d.[yA3Y !kVR(F{xxB6odƲ1^{c\^߿Y/^4uڤ)1/QJL7#g̺x)vc'N95vO MT55|.Zewc0H8:5n߸~s.]u~Z]g`-VVZˣ>|R#,lLVVb7zԸ1|}aN0 r 4k9vDzl~~~-'OM,jaC#s88'B'&5d{RޱF.SJ5M݉'s6m jڪ34d8z-:!Do003HR]5 7ViA jْa|F !YYzCvO:\T!.RR3-a~XBI*Z˧D)EwFox[Wb.]:z۶MQMlР]L Ph|_NlӶm]BŘRٸqcB49cFNNΨ!#Niـ>z>JN r/7֕ҿou_nrӦ}X>7vlĨbbp=T*|yr=}~Is2L#>$m_]oƒY猎2-[!vZ={GEB>~<0CBBVRqcM]BCCC_mXƠ P̱ض0 ݳ)K___qqqyY|+e (eVBĈSɄ5]dɣh촧i5)" 0/Ws]\;0B{=gŋڵ#|4~]L0 ;14iOuF6уh %#=M(6dPB#WL D* 99Ղ `VJ)T>"a9sxTYzK$P]` wwϤpBD۴1]`0ަ)))0>>v1 =LL@Xf%9$d`>|h洩5}j.>~bjz&9gy[CV! wn#$`~0f0uI']a)J"++kaԩ]]LlA-bAUM TTrْe˧M~æc"ذiZڧ.vTOZk׍3n'BaKUZǎ7; defԫ;c֬wΘp>L{0eC9OR?1&b].׬-dabAB5We_'Z2ZB\fe.DˬL(?(?(?Lb.OSܡ}vxذvm’~w ^7k\._׬,~scO̚٠~m;v9wa{v~)_ ?A畗Mw_+W^޾eQoڤyHi֮RVW7^Վ! jjދL5ޱ1$Rp_B~˵p={ h0ù}Slgƌ3zC,K5aBW7B88nqcF vx7ޕ. '**==Tl0̣dBȹڭj/8kբ0쒻,YF]-DZ,S5 7;ҬtGc[}a 74$?O-++ߍEaLwiS&,ZU̷خQ#{xP1E5SJj T6nЧWM[?BZR=oYԪ޽zvڴYs,}.;;S=YFe~*H҉㊬15nԱK;.}{Sbh|e%;02mfCH#$jQϳYr*~*r) (*`<FӣTDEPR623DŽe6m7̼33g:UK!j0lB!Z$)ϛG!jcw [Q[1r8pCBey ԑ([322~Cq@5?ݾ}{!NzH6o1B[un}L<B: V [QCHJ(x(._ òL-Њa+mQ222BQJرcG!NfͺH6o1B[ =UFFcی?^b؊j~|&ATjMbrJ _]{B{daějqQdʔi. `9.{oG?DzBpޝbaAaRRdff VԜhun=`a!P9iYrGL&< !LA .X,_DGG@;'fjFU>Q4m6^!PX:NRJKJJBa+jNU3RJ=>/HTf߽vgfE'_7ftKF$BU!j^EQy #sJe$6N|gf>jgfBp,Dzp,P4u)W:VԼw_cKm}tNNm SpBHߍ:E-S =zrFk [B s[Q [Ŗ\rvQ P5FeǎZaAR_D322Zn#mE0F!'-[M7[薶W(}Bilb0Xm60vBmJٿx:cߺp1 tKL\ǎFBv)qqqN?~xMB 0s01$1[<y`#zǰA !z^TXfǃr@:FLmj!Z [Q \yZX2!qgo뉶^8Bb%QWdRJ8d{u[/GXivaFyKUD322p BV9"B$I@?yՇ1 3iݻu\#;`WdVmݽgٽ{16_!j'7x [1lE%Htם=Cdǟ4{ֺO>Ǭ{oO0 Y^ֺ^->..ǏiV蹡bcVznFc6Dc6fxO#l cVԜ+2]/;Vу>u֊ՃܷU|cV{xaVԎ*vڽ[*7ݫ碠_0 k-)U(^;09/j5?_?YR%^)*.b]pYB1 wnNo^[>H=#1-0V2DEju}r!F0>o /PcVBUdsٲz%]-ޘRUi]Z;:ڑoаը7ݶekfKZW(}BilbRK6(۷h* !,a ŬWuZ cu_~a3xb&k _v^i:ױ#Q7ZL1TE6CEtDQ>OňUwwž~81ahs="LK;%E iЭ.ԪSE!DoЫ xNQ/GO^--+&7'2}m-T-.B nruJL0!9R4###D^5.)jǎ!҆:5"s}o1\RWOo3BOvrzav{ѳmUFS"3ge>XYv霬0@^h+.B6N$٥^Oka+t)p\[pyֽFܑrvIp!'Io|k9ѣwRtZƓFM(Z3[B5Nc))):xPV3DQQJK;Xt:mRd:vba=Oc/aX$:uK"Va):Z7r "q\Navյ[f~BΟuFGGbĞݻ].aZƒ [ZiʰC "˄*-&Fͳ}&]A!4sتP(KLrrO8VZL+h1m/+qmCmX;YYPU?nܰB ຉD\\U&?^ӬsC' Ƭ6'llϟFX [k%4k‚Js>糗^]Tn|^ڃklǸn"%5t8q"[]+wj;kXX!PRUn+m2Nܫgo0LJZt8.r]FoMcVSUCp$MOOLLX*Mo_:!j{SzEfΙ3zo6>f']~V&Od9d Ft;]Na+D\vVM@bm#b V:x!P[ӐBJZOFwwnMH0,^ZPisŅeŅRQJ9_kWSKĦ!8y0!ˆ8e(PeYDAQ]V[=u'ظ{*Ǭ*ҭZcB,)oeQk_%! RZBVC0ƭ!ZB!6QD(akԣ@}c]RanS{U,2 CJKa+rRVV$t*?R/dd+BE绲k3g:X:t5n4@c$! [Ya.zjDZ0,!Ra C(R$Q(O&^[$?"]OMDQ'&iv[Pj>I9qdMcT%KL_cZ˲,rqj2 0 C!JJgP2̆jE&8a}B|$Z sO*).=fLYxr[5OMge9.c c !Dm!D"0D!Zi+$I2(8.<ڤ2=/?Oj 0@)#*orz\N }+˯k!P빹:aa>[la P@3FѺvPq\ݎecw88tXTYvjڷKdN*/&V2\qB ta^o<}|0)TDAB$I *Al*^#JWbs8dhZ6Ef+v Rc${BS(v^ūUJ%I%&Yw%P!i*8J![Mo0Dt9={b OYCJj#?dwBaMî|5?p(g_UJGe ȕ $M2=Q ( ba^vr⏳gN@XӨ_8܀RDwƬ#︯op7i,,*4G\3w;Y~yW~y^}ߔ)s~k:%%(c$i]~Ax/rcB) + ֥ɓ#F]s!l7~>#RfYm@TyĨk;u³{5ق[^Iն^S(>|^aD@2D"Ww'UP^@H "6e.ͅ 8Y}e:Owe3 F;&Nԓk֮ׯ_j3z{@0b̸s رcԨQ5% 17t5kw=a_m ":s)Yxznil>f[:?!?^g;?0wm=C)#ak[pWjvxF[NBne)K*]LR-IZ1v~<8x8k޲eeW}_wo-ZV\e2l6XY֪56x¨jX}k萫k26ovϝw@>ͷO?u!+Q*zU(e ,Ҙ!ڪůwN]7JiiJYH a侮*t)rEB*Q<ye6Gjl4)9ͽan-.۫gټZ LI I#B+w}NI ^~aCx<+/{}=ͦ?Rw RkU*Tyw6'vq!P}1U~/oͲ6tİ?bݦ4V*7 *oj% (W$kyKJb[lΙdҥ{}\X<85}M&XuX}r}Wdﳿk8p0O>n7HԷw8e쑦TFNd *)(asUr$IH*¥IB5@uj.\=$I gT^b32Sp;솨hNr%Q,-.P2 e"1. ے\Zۨ$@.b9 Ŷ-??UWsµCYYeeenj &`ۿ5p,U>3@^_fAő4ٳYԮ)qaW΃d986Q^9iYrG5+!jP= r1c;?^b璉JL1eŅK,g[⥟tPVVBBBۻOQwzڹS^{ךR&kqߤ`-7_ɓfs.̫TL>(6W^ܩ$EE .Itg~1tW;vyՃ?;K>Xg'ʭP}3?^Eznws59B!/! OiZ%Ba+Mi19;mRvFoݢJ .jRiz%E(;׻G[xv50pǎ>xVpeTl%&&7l~Ъz@.SN@߾}8>|C xjZ,>;1o/¼_xq uPsZBm&e@e"Gº6Bc-gYiYqzc'H퀧EVZ ԔH֭k׮S$t{?'N +H۫g)OG}lO?8񶫮PSbZl brr2hXW }]]q!PTMSǎY,1,=Ocs;'%ͫ4b qK<}Q&SZj ~ym@MCy蛞mT~m k_k[~5k>:zGԓO|lݖ{d2qj\Ytt]wݵjժ7v[y%*77`Jtپ}{/;j{#Um5%cϾ힖&ϕ d{<үrIqaXMjT"DV!]I{iŢg r X/ۭE^#^,,:{-<"YXON'z밮:%I4 3f<^!W_xq?N_5ͥe|>xVմ=,]40\.O<~\%^'0L\>3IIIn8y$Bo. !Of2A!UmU%5՝ j^P*s,Q)J}>Q Q:(sϝ{a݀6*a$6F+g ))I&ݷU5-YϾ6/.W q2?~xjjɓ'n:dȐ}?U`ʔ)=\䯾ロP(Lb4oF;w… /_zu=;v|QO?o+{ر￟y(BՄTʦ(U)1&cQRfEAqJ8!rxl2Q;%aC\ͷ>z гGZ~yVaQћoezJ'gz%K͛jlϙ=ؾ}.]xe˖QJ{}?o)S[j' 0trغn:8qlro6 _}wu˖-0i$<B%kNODW$wX-9N?v0,׳[5ÇvrƬm).@ZRP*Ewu{=?X)S?C>mx2R9}W_}uԩVRN:۱cZ馛=6l{u]kϞ= 6RBFgΜQF%''oܸjb jʹ ԚdJІ;u8,GWf4Z^c=MD_M]N)1G~m˄ר:ƝxG%:ױ#Q7>m@||[o /8Yf9kw߭ٳg̘1O̚5k߾}/\^űcVM/^x{gܹ}]q}B5 wJAY2{t\5r)~; 9cS}8{faQQf٭5k.ctõLBJ͡~y^(yTޘ֯__u7|7߬4=UJdɒ%KTMxŋהpF 1bw}x)"P8vU!lu p 4~ՂsQ)K#هM&~24{65EQ^~9ztͪU Z]"#IyW?|>ɓii=h PP]O'77oڃ~DeDi>WS*<(f̤7ϛ}m͵4`$4~M&ݗ]un0D!Ԧ2Z݈8>OggϞ7p}`#?}?]?GJE5[1 ケp'+CyW\lyh\y 3zru9_дC޻o%ˮwݗ_}4FBP[lậT[ؿjG>o5FJ\JѠ}M[jiMW7_|?8Z\ܷWJ acRΝذB'Z-ne쿿c|%IZç'?p$I M ծ!BqޘNJrm$Qmߵ;77w&C<}Сpmw>(ڹٵs(jj|o;鞻$I;_3ߏ״Ҫ?!PWJ_~;oi%r5oNR9۱Yg? !slAۻwGس{:ll-l+M5|T:ļւӧsL&cՕ B!a+ؘÏf<\ƨ# ]{n߱PO)hǎ>xVpeTl%&&7l~/ Ka%J~:2EJ{41 #B3ld[<V޺{U*=8?g90*-v&RRSN'Ruڵke%֠jYYG{\o˄2tZc4Yvخը ߇#x !jakME4jn5|ǟȑ#0[cBl PCԐAc,"鉉K }d2EoE_B֪rbPƯ5֨( nz!ςt+|nm^ذ1x`HIv@tky^Zȍ|>)fz3\B{8@1tVpKZt虧|˖gewL0'׬]ׯ_!:x=wڵ{ȐJv, Gm}af+B !RZ.ñJ)X1@@Jհ@: ؉JɃC]?!8U*q È(OZ;wN)JZQlفCU[t+WɹfyUOV0DJ+n4ر롇ݷٲLJCM:IBb HzEfOI@J0˩9VP+K>9|<-J8)C_O5^7 *aI!j`0B=JСBuٲyӬ瞧@6~po9gk/X@&>>>0%s/6l y4))Rn'%&ΛO s{W|a+"j+2[TV.$ax+h>m@b`gPsc*'eYYxbgB<l6ۀ@TVHl0wΜ%KB}f /[9)QS׋dPOj :x]PIԝ)IJIQ8GXr'(JNP(VEQEA f08 f&;w.\bĞݻ].afsmgXu⢅ H_>Ÿ~$"Ba[0t9tYF)Q@)PI%A$~B="TfVZ@I$?~󥦦ZV7nn햰͡5WG;YYPU?nܰB DQx֒Rz܍ȼZST\08LBxo:m @O@&AM   JT$Qh}-c$I|]t9s֭[ >>^Tу*Imm?JKDJjjJJ vֵkJs˂vaFe/ *h>tHQ^"𗢚B W %JeKgK^ +ψ Ju*ǯR j=4oqqqr(~ߏڞ<1%师@htAN;RaQo;m>}͖8 ֢o7}C(MLPZJE!Zj+ jcMIzkɎ9gV:kuIn26REKLLZKK&.x:cߺAu:%&cG^UdBݰr;IOb|"F1H$*c.%*7Q +RJk-*tE~޽{@5/΢u8.K$YR}PūT ‚f-AռJT0 zRa1=OVNQӯ h1 5Ce5 RIBljO}`m\{ml\t]PI*)ڱ8czA!VS[逪õ&iDee6{^~J՚})%DU޴\ z}. Rk厱(B ~lNa0J%_|\hA"T8].0XA!P[[Vr\t߄ͱQ;gr :,*,;PbfۥsN+YEU(S\,=? kt^ ! ըyJ0T[8 !"^!@ h+Pm˕%A.w.i{hڈn_Ń <;b;$t8{Tyn=`i+H>Ӗܥ&) R,܅\'3YA!Dpf 1D^ ))CN g nG~D6W%ں3l6^g1ܴj "B5}jxUW V~ѣkV4Wg0@yARXQ b@BAdQIn+tb6jFn?`=Ǐ7.t*I\5OfO>ѯ_z=F@!&EW9lp*B*QGSJm;]qB%/Q7.k :-[vPvu֢E:nUrs(ټb'Nxg[|"T]*@%IZlΝ ӦfdgcwBmU=Ã㎿ߎ>nu>6#0!P7Wu;,uGcGOK2Q~M1`o8.z~^>[^}vq_"ZԶ͚=Gf;~=h}{ٽ{{vv\C 3͵>xMÇ>D~םI*-BFUB Ο;w2뽟\~[oŗ# Ի:^{K^gm7]w? Nxq1Zq׏w}ʬ *KLLLOO7nnX!dZRT^;,Wk@!֐BQ8U >[o#kߎ׫kZJ~}w,xm*E/o⹻oUW@^XrQσUGi5c\D7t:O8ڭ[׮]+-+ŬV>rnlRk&ˎ۵5`u!RU%OR#'J};]q++5:"UXLZHIbku_'kؼ+Jന yԱcK %'&&F[, p1J[zCNm[nAG@MJc0lE!Ԯ(ʆ-YVQYWgī|fk_?OHW;;u: w/xcvX766oX0%d63Tb "b㿗XM]buBǸ/~us;tKL\ǎF_7Bve9N'"5fN؊ Ta2[BW_ [)=yaҤ{_.}$ISW +y/-- cTT4h(H矞 \]n* `W%Ͳ(yUCCޠW)T`:F !u#jW\BY@?R))U2 jΞ9seJjʗׯ_UWl2N8"j5ꬃY#FYa;\RZzOQF`Kv[[;0~Zʕ"7D! )8[ v5p EQ'3B;A!cG{,$!j./^P$b0T*%::,:*^jb@5I&B!ԊTnV K@f٭a&!Pdnw8:m֦ 1D($!j _(*j+c jg1l 4D!Z .]Ӓ 7@$bP0,[Ɖ ,$!l.\M_+JA_UNOLV \r#tiM2Bft3EE 1ǃGe+dMuEUQJᴵ^r8ʓD!/IDATcq,BnVJ5f'~s7n,Z5 [ZO&BK>>OĤ5Lxn TU]d"B(cF~|O2/cUa(6]VX[y}(f1uhBE_.=C) rךI%;n=>mgfd7܉@WDQH XhKm}۷}V"ZZT.-(ZXW T nv7{yl6$K|?̜9s9ym}P{.zF%[O/N>ߧjBok_[:-=L沿NC 7"z$bΌ}z M*Rpӵ7Me!WOW\b!#oEx=2KvWOWs }e ݝi )n&Ń-sMs=VR;TU+WlnUꫯ7nz}&3=' [-ٺN?R씔gϹ63|u"[g= / U O>~MKoMK.9$h4utwBa"3P9WlN-5`lqlHWS5D@d2*Ai4<o3R)@ʌ%ߘ{6$3v_YeY#uu!D0d,GIXg{{42d9!&$=slyW~cnjӓ ]P]C ̞_VLk CB43eZeY:UB*,˜ p$0NW/ZyϽ'7NsmRU@žD<7N;`0\YUc$kOG:Ѳ Ϻ0d+E4[O0B!Aڔ LM($I$JQ2,;y=)KYYF˅XʆE| "ǾD oJ"V'5}RL/>o D`}α.L6J^ò,.>H4UnP(Nh0X~^kE^ZGV5csd+EQZQܳfy;dǎN oGaB"hq9Ģd$WjvL)S.uzA04ֿcwt:x[a)hq|Y!D" N(U43ؕ^,_ϬR˕hxjSO-#4Б>Gq4+V03oֳ5BP] 쎞ښ*eUU9rX8 pE?qC H82GrVHz^Z 64v%BBH' ӤX43Cɧ43. #.s۞~yĊ 䖭!^**NLJRY,[y/ AxrWnV}cY ϓxNc=#p`"61x?xA#q) j]߭xjM79}YFfX:S{ϋxN*z_jB?~jyz]YmXok8\Exky^9a0jPwHzkl0 56N[vۭn++Ok_}mI+濾v݆7jF:e40IR,ǃK&Gr_l YdvfHC־,qYڜfriޠ2l`f5f[:]% #qQ7gYu)GJb)+{y͚ys/5W/{yQOj0x~Q)OѪ⣏>:>Q,Ǖ[-VK9tfupÚW^ϝys)yA'f+:]>tn¹s[1B) >8Kl tuϫ9OXSlN|ٜ|`݆?,^q9'NR%&KeV)&dR2K9L&qʐ$pRWlBa\rq%GNt!00X-4m!4k9:nWSs&1It0&[`ۋt`^iJ]4MDJNܚhQjOR:.Q>!-̧wqVBx7RK&!Lܾ}R}&#*+=8ROOy9Np8{V>?kփyx<_M]=PN0Bapkkrx9@ʲ\ QURYUUQY#4}iu\Cˆczt $B0][1%>X8ЁgBX4ƈī&Q>Zc|4}aZě1gDC2W/ZD_{m~7@To{g˻?ϞveÏa&#낫xFÓĸ :}֦9sʬp8V^۶ ò .UEM3Cz!&NXF%)rӑ}> ܹ3¡`peIsH,Z<[TWmb)82wqpz,e]u'MvֲC2eYn:/5nm Ϛ';mzY3ώ RUY|kW0T_g+(&2hkmnc:0h\#YQiZ d,O.hsG…RA4ʶmVrL->gԯ14Aaa!hx?U5{WG>)H8ci\*rw76O;lfh: _ok̥Ŷz 5׽_?`\zgGž,K@ f=<_a-h4n@( M "_W]]Zj.1x/U 2v%lBB%c\'iEa$^n8|_Jo(X84jfxJ$jPUa;[{p9u2WcvzܮE!^4/e=gss՘i( _]#)HfssE߼sdvcRCR&BT8k`R.-]W~=gXu hEcx-c/l64PEs ħQ{cW* %0FCΐ,<4DHX_ATw8P8 C;wM%1?K!Yi5",:3ۛoκ`ՙfGQ5k^{sֿ6lᰎNbϟc@G)pyyD]4wEU+[ش!XxշĬUջO׬~)3Эe\麨4\:(2Ay4=~ڣj)eeYeL.m7i㺵k6{vjޭKO?!\y_ee]w-2yrj!,?355ߺzQ|$IKoYvқ^˄[o邻/Bhwoܴq+ZZZ&%O LL./ !8 %dumEʭaƲq"[ Lj/[D"!/vwO?|]!_}|wժ{d!G~Y̿xCP{{gV޽祗WϞ}k-?#S&qӛ/fM#v{kp_.6&IŖ>;%#L =SNd۟!sc72nD9؄3ǝs9, Dq7n)5W*q;GG',>7(=.BEUr3EQnyZfIaKJB$9N 5]>$oY/bc"O>h| V\{|cr3s,%b`ݩ(c#R _x"xG_yqɒX߻VQ~KӠ3Μ~S^o?ӷzǔ)';Vh8n(X\h;4ضi\>n!WT{y^WwʲtΝ;Ϝ1# I"(;?X>ĢEKޖ=ʪJ+;)uw3&(ᤃ_~ 0¼`|aF54LUUúTxA  ۖݞGzMNIENDB`muse-4.0.0/src/doc/html/single/documentation/midi_config_window.png000066400000000000000000002561001404105311000254530ustar00rootroot00000000000000PNG  IHDRaUW0sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1,tEXtTitleMidi Port and Soft Synth Configuration棆 IDATxwE=a#.KDN ", 9(*f󝂈HT$r^O%aabLPgwv/f穧 ;"@ `0 Ax+ ]Xw^$X,XH{ɨ0i^j/(lu0R"S|e3KOjIIJY`ڟ2($E(:PN?u Ӊ<ԩSUUYl "32Μ9*))^C@pC]O)-YyM*T0|"Ă%"p+Z*͗vbJˣ$&D ğȉ@N,/#s=u32%H& gT/4S cJMz3 c*%V邱S6\[nEr~-۶svY^ ;de!rU'V9ۈm.)ù,^{ž1`q8BӦatJLNIEbbۋjժ"66Vk=1d d AvqCo!D5I dm c( @2@0. '_=΅1@Hi B rj\$rF}" g]5J9 ~ Yb>%UG!\ɫ(&\)֩df7k`3@(xu?7aPp}u4ꁸJU`?JNģn\xXI Nds8ԭ_/^d=O)/z1 k2"aLTMd 25M'q$(\k}ZQQu邢"3xt+D{;HU5HBI fOR C2_bYBGD$gOo4o{4런h( m36x]hL߿Cmފ; (-Ki/I?2 Э˽.0-vdBߑIVH>%/0T*>G m~e˸;+K葜<<]}ALor]r -2!&ܕ쇵XNxnB޽P`OfnQ|$&VjCIq\NV\T͆kψJuIU Y) mkV9-kAp"ɧO  0Rk[ukѢys]l'4& )(v Zm-Z[{_Gl9eH;SBA%IC\E;wbЀY3ծ{P;"0i/Į{/@pGF=VkN 4m;۶ųO?wM?֟>@0d@Ҵ)hچV/O{ǎ?2";@MI|ءAtI>Щ(>tB"{> O F)=?*eZ^acB}WR6&"5 F!-̟ghjW_+/bN8̜1}VmR\ KLm89rR4lAqMUps8}x<dfeF())PEQ:22xT+%K">]) _v.@%MޤuR]aTWXF(O*&UUV"77O^d MOH>?ΞŞA||l1vL~'>,f>fe] +䈸,IHU0#c=X DSP/6U ܳψʺwjβA800nl۹ ~=!DI]I.̱c0x X8 >>ۦ;>$h#i ˤQ7cWJC} :#_g?"r DGA>*e{{r2˔H;2r\RKPV3O]WO" 7GQ&&>4ѫvGfĉq_alWƃ#F`Eb I?CoB|g&~wL{%];(9k6lhذ9Ç`OsЮqK#N~Y`^nQ\"|O8!I+))Allrs*gDQ81P!w3xL@ ]CVd%S+>$wݗ)<3s>G >!>2wgAݛ2\ƣmFF >ߞQÁx] 20~8dƍCVfVqЩcGڼլq8.q ѧWOi _,^lA]F4o}ƾ}{3˗.cЪeK 8(Ejj*8Cjвe+W21vjq+34׋&QhԠ~7ixZ*, U-oC.]p ;v{v4m{޽{xu4nP?NЬqcg }lL6 11HZwuW^3\#cZb#cp%J8wddMq_>ؿo^6iA9xIm[Ѷ-:,Xf&Mx-4o4?;uB[nA޽>}.#KBpڼnm6o|?2GqG۶xGAÆwnàp`9PR=x 2G hW5)2SG^CcB2bIP& }D_!5>WE8w~{#8@f%_♧A^N\N' 0e̝Yض]N'֬|`6/[%K4︸x,]ȑbۖX+$k].厡DJ8$p_9| zQ=ZWqP=ߦKt8t9%ۦ͛P(9xJBIȔ]NzV]&(SRƳ@)27 NNN |{ `xɧ'}Bm݆qƃA&C(WҼed`1X,xAlٲ ã`|zqp]HJZ\T" |/~ z莁իW׃  #Gu?1v;VDp.]S&`M3v,bcb0f#ؼy"Gp#4"Z uEL3֯'#Xqp GȖ@JJ ,‘ÇC3u͛7am=f,6o$s@4x=*8hKzޯNzv VECr!etrD}>={DNA~^p -[ѣãGc-}] cǎA\;h eO -t_T>Ƈd(9qHLY|%J'+v]Htu?wlJsGnq5늡9v~^_d3n/Ņah稝S'JJ\[ض6oނ5|'UwlP|Z톈Q{a#[Á v qx<^_>!I#^/.\x6M0 K4imѮ%HUW.OdGbD-5IMNъ'AKrfBdB |%%ys8hB8N4j%%:ў'M2Cn;q_˯ʕrn .Sh@.q<^/doOb~KVrU-V? x==xp9Y ~`y=!MQؖ8P\{౉10Gqaܾ}{|8{6QTT(+'><#(;h`hIOOǖ͛t8p9np\p:^!@KorQTX(t?g qF9sXGwby((,}Eؼy3'Ν=') eF=s%%X'_Y^N;㟯zHlzz:6oӁ u;<> 7WfVq'̟B,!Cp& uy9٦>9%|qm5Z5J p1dg^W+FuV JzYՊ_[ #^Z%H9P͌`".1'bbl1qصs'P55ah Ĵ\85K/ݾIUl1u23$+Ȅ譩{uױ͚؉h᠔x?dgҹ36kFS Hh”)8x :s>S'K0u4km[Lf`{Ixtxdn믿BE`;yTchڬm3vmrWx{LL:GpL:UZ nN^~o O~2xڶi/{o/tM&{~+hۺ5&? x70k,@޽v&OSǻqAL:5h)X'Oŗcԡ^l6[PukqgvDb7ijMNm_n/_o/NG&OơÇеK>r/LLnOxGӴ~T[G"U9j FCUQF}5} m;aUD3+ANUmߞl\8N/] gq]u-FȸcsNWAe45;!-$TAaA>V /S9C\|b?:m`l&$V r*jqcc!~,=F(vz}11X,z}&= XNl"bVG >zcAwU͆js{Ѫ@ݲI; V}Ep:8{;,tMQ&%%ťq)TZG:c,F-6]es{0F5+ S8Z2wdaK399uydvUA !12R&z37Ѓ8|, 7i_yR[ IDAT]WGCtedx_T^FgEDK/mE,ڈ9SA,1uF#/ߵѱ/y. 8<.]5]~ ]t^LJȢ"kpKl_Ns5C$׺mA_bߦU8z.܂7G޽uB\. Q\R ݆8$&&aT||v71ӽ*eRl0ʖX(Q!5( K?(kOOp}oGCrnfp"ge+cӴ+$ۓ+*V^d3Wd,$ _0x`N$ײ㈚^^cԭ5z=3T1:<m#'F``f~ Bew_ʧS %M"o EN]"I'`<_Eєd"H\D([2g& QyI'2R?vhFv<' giT7ǫ' /ФexQe" _,*3"9Gg |AA+ ) eG䋲^%Kd |dW!e:v2%T{*nYPCȨjNFjr8üԆ(ꗉ#gRLu"H6hz ?h9Hmax@H̒0ŰURN"%3c0e;qe1f/3,X0r?Vt-S~PBYs+ K͟4GD~T9itYaQB '[D?(5+*@eI"G ?ј({Rn5A):uL C[)K]5i9uԀI~Ij7Z4E<'i ; |4YIJ%cF%f;_Ҁa@j]ǰ(UBu}MzZ:%=Y@C.qY p<)*28 vZ@*er%xR PhnjKκ?t4̉tQ_\OP݅z֚\z⻄+O&YVYszo<0ȈRCXhY()NRK/S0-,e,$˿\jYK*ȅ>i] ,J)J OdH(}F?M xA I)8SzP&LU!%vWmvf%ET=0n$ݘ2zoW@Dv&7/welm`]F05Q?e*ٖz@t"n 1\2`El5d❰2iv:*AHhL D.2&ac1 P\SRZA8EmNIm)'Jĥw.f]f=23bp|0Y/ .Ibi]t0.t`a?̨8PVcl5fUC-q=5LQU)h ФVʴTU8Q0DdMy@QE K/&K r?#s3*>iJ@3? k(TPՃ2'CRʎҦ'A晐:IF('#(5 3> zooM޾/1* 7ͥK/fKi t( t|Ig.&D;eH)irsx\0BFrX+annE^n 1`-f&~ Ϙ)6͞sJs2YUdUí,ZɩvK:"ԌOF %1c.[Dx19pM8v8f< +E )_eD1lra_LTdSd^+Bnrg;69tUAw(e*N(&Q4L%. 3Xp (`K"ς.KbZ[thJ@&- kj}\:@ N1.i&d\ wdUU&-%VfPi KJz >r3Z'A(u.X` iDQC꩔kMΖJ3z?d@PrdaQER}x +|"F4 ϦĂs46c0‚I;a >P*el3Hg )Inj@MŗDRV\q%*e-XJn3&[Q%1`0̂a F$==2cx^؍tp&)è1 MjLyEM9GjEGZZgsװa#"2S5>h e[+,b0- @LHH=`#Y- sK x ?hgܮ,rªn1w"E Ekʦ" Bנּc#& iDqH1 B#`/a0k$ mA IMmJZ厃q\;jR&id6`떭<Bvٍ֭[ʅO{Շ=Aonv[蟦 Usiqhfv5`&w8"a䚹H$`:զʦhEp4l6V\m`o۾]MrмHgjӧohcDoQ6~$0Q"\G}qDmeW,VI+ڌ`Xv3j"bi^a 2l=py )Z{DzeM-ncㆍ͛7#55N6ՠSNbaڥ ~7+R9lCK`PXXcǎ_Ias@&9A.nfOhҸ OΗn݊IIϿ_I }y{ ظaz!{l8͛Gs?/SPPʕ+5#))\A~ nj+,,DϞ=ݻ#33S/JhvӼ3`ԨQH_F  R=gԺ9"dI+6 l1,s=#51RBBϙmB/D^zG?C/7| -Z@JJ > 3g*U< ++K''%%Q5/''UTAN/Ԓe˖#!!A⛒/c)zLKOÔ)S0yd[*֬YC-[+_{KjN-IX !˝ b4 [{ GEg=OO+ X#"Æի1|pգhԨmK,AAA֯_?w㫯VK~VZx*ޗ_.AII1[L&_WgrIW(..ҥK~3f~=k׆1oQ+)瘶>(q_Ti | C3:ɈQ GN&۝:uBѱS'Pg£bڵׯ7hx ȉG{ OF=`L2%7~ٳ={Ç1G |69=z`ٲe=z&_f͚~}4رcܹS'ɓKm"[y1H:hn1"Y`0LpqۮRt\8|0uY ڦ~JS/5>kklhK%UPsmu ajձѰaCv]Zjaƌ w9jzZzmӵn&Yc̙>|՘;Sᣏ>2#i6mN,[˖-EFFL"N\r+VҥKeRF)>X>ԩ?/Bǎw :9my}-EqF{!pp^/.'k,~h!G8))):t;L¤Ig˗N—_.E>}Oڵ-Z,+/ʖW+]vR |AꫦZt2 2zBBBЧOK~Y A~z 2ƍFzz#޽@>H]. 7ba4h06l؀k},ի2e*IGAAj}lh8ԪU <0Ò%Ik,tBtY~^z } VڙA~dYݻwcܸݻF]v0U4om|cVj+::9\_+0fX6P8řY IDAT0h` : 7u`VcVcZ*V+*WΝ;@Cs1FL[z.8$7Hn7%p" h%77˗@ӦMiGÆ lR4o v/V\C=(eYɓhܲVC$;MEzf'NĢE cEٳgcʔX?6l>SY\\'N`8q">xNDs|o+V,GÆ h"Uߕfн{wݻ'Okjժo ^=o ժUp7aŊ1bErx^hq+q?ѣGc1d`|l6vr%hRȍ_4ԁq\qhEC˸]bXx{Q,Xj#Z!ƌ:Sضm6nO}Vrb֖'TJJW4 P|#q#++=LPf>šo>طo?@8ܞ=z 6tӥnFY_^ʕ ^U) K-+ړٴGIzT*A^k!>.fRRլ,t>p6^4^ CP}ؽ{7ewh]Ki"ѶQRb" TۣM6 ^Oۦu;vǐ;ڵ'9J~>ڶ}=:]OQ=_1i2xfϞ\:d%Жpx "amoSf?֬ƺ?)!99\A~JJrrqRa}ݮw)vF.^yȷG6| G1xљT\ZZ;ӈ!Ch"{A޽{ټy8s,JJJ$fj(KkǍPQꋆqE/ZXժh'!Ƭ:8t;~h… X~曘1}:UGç^[r}7Dx^.jqXY@hDfDrr2r򐒒\I/8>s6ӥ*WD-EXn~a<`jժ8} nᆠ̱#--M-Pv7oo 7 nVI9FՂR[k֪'zO'qFxm|z>q\n7l65)OIIgE||.ߩ$ml-[Gq%dfe… WdJJJlTVMRZ;٤iSlٺ{Mo5cǎE۶~GUo=(7>(]OJ6~SZqc R_c:"vb̨#\?k}}ePu<^[m١Sg!.vl߆{6Bh6ڢ6|A f X+7l| JJJ׫ѢŭUV%_KK/excuXlt:qa˂ٳqi{5jm-AÇa͚oAQQWcذVz)xlPF\J=b"זFʨ]6 ʕ*|ߥ]z%._cGA GK.E˅#GG^r7\7`0 ЊƩ٭-rpxlS0Kh Rea'C\ZwߡAuxw `0 C+qqqσK%K'[[b0 `0'4Qn }ߋP`0 `0?x %̗Ӎ4LWnנ`0 \.N+;iр 0π7]\X`0 <b@ŮJ4!~틀 S+a=f+a `0 C7. ?-5/_o)NJbD?2 `0 Ƶ BF$it!w#2 `0 E_^^M '!lFp& ].ʅ]`0 Lh!^ٍ[UX3iZ7B7/twMoK16//3;umv`0lLdu x ]0/ nᯈc§>s:t(dA߃>:ׇE)?iKapq.|Z Qh+%2 ,@ RɎ]Ol܈Y3gȑHLLDF۶xW:|ɗhߡDؿN9>jpwW+0g7<`nNg6TR۴<aaXtn|7xqAz^^&>(v܅Z݆?ʕ+G/~]խ~ nrun= %"ymÌqL"YrcQy?̷g`nku;&7oٳg0jxC2C,%$H"!@||few>|+a\ dލ_\nDuI ::ɾ}Ab2}ϢlZPf-$UO 7{z^$]km_Q}6<ԓ0~֭Ml6,^/?111eU"?c/>61LlN9Ʒ?Fc8rn;'N0ҍ?md`h9Sg7ھpYu`!Rcby"RuPn0=z]}.t3F?V{/  ͆g]TY Uknkւ~Zf?eecڅa1h݋g>sgvι⬿ƒHMYf~fbNE Ը ROsGիIق9ۍ뮻? QZu8Nղ*jpw߾/(^!-oi?,+wE <M6pzfXpuS'n}}zs:x7mBAݛɖt#i?{ϞT2v ׇggJu`DHz&RuPn}Lxqx`$*UJ*Faر{T͛6gs?]O0J`Qj5aUX8o }> 5(GaMxK1|AXŸQm.&v]gpGÆ&bTqEWxI\?eu[bX07׭\zGP^}]P0c 0[nENN2wډ#DbJ:lM0zX$VFm ɂծMq& 11c>6hбS'~q挱 p 66L\ F$1{L4\/M"q]HzZj!WtZ0>ޝ5 /_6% p!&&+%"92R*#%) )­RJcx+ _ȐQNlk cpq *W A.}篂+`/\%HK,.[GԾ2qxFII ޝ71Tlzwʕ<.3lBii.,eT.=,RKq-r%HMQdE20 3 x=&{}ss=s ڵʽweZ_bB66@{AxlmW%..[Ν888hOJJ ڵhjFllL9ՓFW UT4?~L*ULZN_;{Ԍ'oPQo癦БݧC"!! 7$ L_f2YK0HSZ=a/Whə"iZKɉS$UBXVc%S՛CBbĝ܊@8>$=πv[M*Цm[l=k*Jڶkg6T9mߟ6n#y#eO?ioddff<孭I|zp{p?y|̢l3:jwttJX6koG1@N 0bȭN:}ߙIgϷ 1Dc6S*ڱs|w}y  D$>t< a{`|ܗtiZEzj쬚scz5}S|sm_{vVvl(-, 'k'xn%0l?~^`Qk?V UVպu'V@+˟=9IӦl޴ Z-[t<ˋk֐ª|L]Yt ?o˜?NԹ3+W-<;99AFF}^]WW32^;{{"""Knݺo^طo/Ż/r0LW Bi0Vh`BE0v8֬^5kHNNB̆X*1cuUÆחbBc/ +noמՇl(µ 'CIGw/pvvf,?.HMMم>> V`búVI¯0r+nnܰwWw㧍hߡMS&0mLJ%I2sƌU, B;/IѴTLbw -K*UЪukl%YI P5fW=>֮[P_8w"_LϠAXYY3o$Iv]g >5#GnӚ%˾/q.g}ΊjsA(-[ ʣzafϝmܴ_rXZZ1zG^7W #_{Jײ$Z׮>FG]g e_V燐S]jzAAT2228ݼ֤$c-T^IʾAccջ/MR+۳8gng +tWu/W  FʾQ{9W7jtlHyAAA}t/IY#&Τ  PJ~.#AAA(E" AAA( 3Fe1ʚAAAxY{7?|Z! X%C2U2.#FPre툈Yo5~=zy3X8G֤ˬ }'BQ5VSu'^55Y<#3 i;zO>O1v\ gø3/,'Yk n͏177/r]>[p0꺺翿~>oe-ŪK7o,t~<.?4S&^}ʕy{>4hȀ+Ƴg&^s}}٫CC9w7o &<Y}kgeGP@GE!W)KrY HR8  rjX(ޕ˗3It8::2XfM233]6yvTA}7x7Mp`޳~:zrVJ㫯֮G}k б}wڵfUrLzf`^g IDAT Пһ/}Ō=_?,-hYq'ge*:濲A SnܸWɗӦu(FT0c DKj,=t#wK24$c-%2mo<]NDMU/' [ȑx, Y oMƾ:Wΰ0vVe}6('O-\_W`XΛ;7z؉?|ۗC{Wׯ_wF.JxXΜcޜ3g6*gCiݦ ZMVV.]&d lI Eo5"&SMjfu-v'{[27G@^xuyY ym*ubRŮ/6&[[[.kyF: wzzmCE`ohYQZZ2p`͙7'Oo^@fMω.@JKK lj  DiiЀN>`JDFFC||T^[<ݺQՕaCW߃׻R/~)o<2va:}]"#0z0fHDff&Ғ=zuO;>r'gȑ(J># Mwrr"ѣ<#-ͅhJ8:b1$̐r g׳ܥj|| ,ZqUm=s-:QKX3L [27e#*x@AY?U2>*H#oy1 1C}71!k {_"xo_VZM ܽGF[N66$%=3>>[yUmmSI_WzmO^FO'}zG}껹(b3q!OV(deei'*DKO.k\Ռc2߿}i,}0d J+LA2D$aR(msE~MtErJ-<9[$Mk)9|JQêPvcPdzsHH윸[(R62[~ۇ͌ykrwڹi;[;fϚ 1C}ښħ{=  L>'&$`mmnooϣg;`JzqE}ݷ4x0. ҫ\A~ݶg:xuԻ l'=arl((b3q Eɺ8::r%,\e HɘڶkM:6<0I>~>CHN6c8 ޵K4-zj쬚scz5}S|sm_{vVvl(-, 'k'xn%#--1l?~^`qzG|X(rӧ}4mMPlݲEg24d@_Yx1-[.#I?m܈:9Ѽ/ {yvRRRXddd8/xvyIMIsC_ ~f%u c>X2VF]WW32^;{rDž'֩sgW$55[63xF0cǍcլ]$lXի3Vw;lp||}@JJJ+cOr7k?YB?=˥:gQ%S՛k  O&^̶;X0]:z }|62={ņu뭒_ac?ON9cFb`aaaFɗ-X șGn$m[Ύ5N;})|$&%q|wڅFaguv8̞KI?3&M7JKK\\\ m#}߯9BVRV[e/E.g Bi3FŴi 5,@$ލk׍i<}}6q-TͨYfϛktW6mf9,?~NMZ%G~ĊUXXXFB9'_{JײnX fddpu6ѹn,+.ҭW e_V燐S]jz^ 4jI&c/[^: 39}Ϲ$I+ccyEb߮s;Grdݙ3aSwUrU   \ T&  P݋g$`bRDAA Tw_]3a   J IL   BigAAAJQ0$#A$BX.AAc*I~QVyL=vM{sכopbVtbn51Wi=5IIeVF!5=UF btb? <#Dy%Y?7 3ɓ>oMܾ',hJ_Hە/S˹ffY;Z60/7~Ʌ;Ȑ2P&, \M րRTT&*w"ۊ"c.Q*iۖ3fRfM2.k~Ot)_g޻j?Ӿ=T9?GGG3ol8q8*WgL0.^3/JUd3=]j.gkkK֭t<4S7t2I]\Y)6(Ο;p<[ɓiҤvݻ Y(C%ͅdj4QW O1v\ gø3/,'Yk n͏177/r]>[p0꺺翿~>oe-ŪK7o,tH腋 `ŪWr%yFD;޷ 2~FQ*Shz >Ջӡ Ż7~C^D9_Ϝ!2Uݕ?% fm\2ˬ^;+;8z( L\R@:P-]SòF\M{7077ё }ƪ5k,Iڵ3#Gppp$==uUo5ܸݼlڄCMͽ pիWG.SjU_}v8[ծŪ3,,ݺkY_? ɓ'@uA W)־卞=9v._s!夦Ʃ3gx7g̙JPZiüs077GVŅK ٽ>4qҤc@홨Ǐ~+Jf}=ysfɂys=g.Jr:hNg7FAL'Μ>)a5ϧL5F@PDxXGg׳Y8v>r_͜A}ΞJ3N:E>Mv_No5"&SMjfu-v'{[27G@^xuyY ym*ubRŮ/6&[[[.kyF: wzzmCE`ohYQZZ2p`͙7'Oo^@fMω.@JKKCQZZ24 ӧ$_*Us_g&_Nݭu]]6ԟz=xk++ޯ666z[\0va:}]"#0z0fHDff&Ғ=zuO;>r'gȑ(J># Mwrr"ѣ<#5Yfߐ+W,'==\N_ktoE+m:G~Cs]'*1{)早!pkQl2R%h4(8k\<'겳^g0r^ p}-?fQ7|8#"0~ȡo&&$`mcdK]d2oU =yϩƆgvck=-qqqy988#p|}=g. 63(G__;wn}(1ēI^=iQn&}\ YYYEɺ%%%SKZ4W5#66F ekkGtttboog&+CG~w> < S$);q7ҙ08ȏW+َWhə"iZKɉS$UBXVc%S՛CBbĝ܊@8>^^5 ;ڴmgBYEIv چ!kmmMӃӃggg ygNӳf0%=ðz6t7o1h`]WmtXe Q* ccGb>ʥa&Lq\(HQ}.\ ~zR2ڱs|w}y  D$>t< %-J7Qn/G`ϦMظq_?޸Y=v5vV͹r1=ݚž)K¶vҽ Wo;+;6@̃Xlلt<[laős6ԟS?/Lٸa=#>,f{ӾM6eMjn٢32h /]ʃ,]Zi$6nDhXf ))) wɉ222 ?Y^ !##9據ktt4˖.[*chPQOtus+X}Dž'֩sgW$55[63xF0cǍcլ]$lXի3Vw;lp||}@JJJ+Pu]](k?YB?=˥:gQ%S՛k  O&^̶;X0]:z }|62={ņu뭒_ac?ON9cFb`aaaFɗ-X șGn$m[Ύ5N;})|$&%qB˘wڅFaguv8̞KI?3&M7JKK\\\ m#}߯9BVRV[e/Em+nyA'~1m>aeeͼ $7w#u#Gk>.rM@ U3j|J zafϝmܴ_rXZZ1zG^Ei,sR4-kS7+V\vK=Dx_tBwٗTn^A0ᡧ  82228o7 _k$gWL$es.Ifff>yX\tqU!;W]\̑;s0M m4hd1zAARr{O   By|G-Je4⬖ &&.EA ٹahϡQo9sAAA1D}*#L?N8TO*(R%F1U   4 g^TQQܹsUTkkHIyL\\bHْAnȫ rX8͝_~g9\κ[[0k~\yLi~е\D]˛o݉PNm>Auݼy?,[Fm8y4mڴeA(L'' +гW/Nr:4n Lxxx|>sH^6N9JXO˖Ċ~`jz~XZZaiitw &NUxuP:J[vSٙ x?Ɔڵ딸Mtt,vvsZNr~ιP&ammEttlכ͓Amq-Q8yΑе{-e˖ib|~{BvϬM4ލ^XfM")vlPQO9}S>KW9jOjڍ(!cB ))0;άgp||]ZQ徚9=Jg:u}zu`[9L&W4@Х IDAT-kX:^{_?y.Ե~]r_{t&6&Ej늱)JZZ 55X3va:}]"#_/<$iB%{ *v~aZaN?Έ#Q*|!G6n#"#D£GyG[&: ]Xw}}]jqTQ>3L8#gLFRRժ9z[6bqQqH 4qJ5.l^magg p}-?fQ7|8#Ws?з}$ry#rspx^6{Umm+VIIIT={y{=b̽={ˈJ*Ǐ\E`:1fx?ӫ'P.] 3QgBAtqttJXoOSvعc{CvѾC<ӆ"I|:gIPA-[Xd1qqqq9 g ,8C6nXO^_Ec>ۤiS6oڄZf-ܙ^^]V9VVNNfϞѸIlDrrkb|ddd8߻[wKrrۻxS X}u]]I x 퉈0~&`rԹ3+W-<}UƱfj֮YCrrd6_U{DaÇRRRt.'XI C O$az:t.664lAh|rǕ;qv:5ƺեk  wygggmKы 0iD:uQ ,ӳ[hђ2h1Cns:mCƯaaaQ02iΜ9MKO.Ť\76i2OWv4lRmҔlټ:вU2yk׭g[(pGFriڴl_/G/市1/Mw zIVKcO|˗/BՌdxǦKzذi3V͛Ӳ'6S׵KNx=#%ႠR4~_~fddp }{^+tm e_Gcffg3亷#++L233INNp$IcEi0UPQ=|.:וe  ddd.<6B&;dիWGa&GОTȹ&'OK7zy^qva6κ{ȡ{!I^^v:G3vvv|=xy77Ws/~4UP ]vXf*UY$ B"/z"H F&ONܽ{7oi\hmmMժ6xyse] T fiZ,&CAA'c<'t4?cZ( 666&ILU w]^.^a Pd]BgAAAҖ;/) +u^A4>CNTVA*Yq  25A (gIPhܹGժTdYYY<&..(y5 AAA*8ŋW\]qrrBBJkcxJYkT"YĔIJEAA*I&Y(A2h .ki_ӿ>sv$i4jTʕ+!IeffFJT+Wťii};Ҩ1[1׎󫴞њԤI(&FCjz^|x PqAx㎰_ +1Cu,P ~% ju j<{h4ڤLhի88أTV)b /t+_ s=̲xw88`Yѣ=JYr:— dHQdLZ&Fk@) iaV;mEY% TBi9 4oђ&OI&e\j""nrwF珯~CK3dx6|^]w +ٷ, TZDurv$5k:QJd22 333̴.oFFrkkrq6lپe_O|g͸FcHdT+[.H òen݆gNӦM[}Y˜w)))tUc8Uw5PSY#82ӟ3:<A0p\( W\ׇzq:4ӡxwo`Ë(3DVfJ[n=',),DƌE П{VNr/\Mr0'O@R2<0Z ~D~͛ {Qﮤ$Q,o0kzͫuj#\h8q];qӤQCvBBդ1!;wufٴ BBt٫Vx :d* 豣( 2rI"K@ BwYN :W|3qyUؾ=_o4OiQqbw`sg$&}K,f1aiiCX'8a<+WձS*@JpѹKF3giب3M3Zѱj9;92ELѱFV f7wÃhcWǜnnI\D}DM:hب_/ҠaC9}S>KW9jO B %%W13ӧqu;άsX8u޽I5ky[tdPAܺ~ 9rOe!KAoɓݳKqfK#DŒ匯ϫ[ڡqpFؐl9gr$1羰M&+j7BNU*chey1jR3km;3uݚ]gWo]za:}]"#?LKDff&~JKKG=v(g6Ұ~6%m 'kX:^ < S@&aRn +ɦNĆPlw0Byaφy2IHLL>_+qo4D)*ErB1sO0&MyܹCM;z>P.] 38ވȻyF"SFEu...]IH윸[(R62[~ۧ}P`q+P1P{{{(>>_#WµDžk4ZLEiۮ;wl7}w.wsx` $ɧ?}|,SbX-g_͛iӍV37oFV䙞sYg$I4cqqq6Zzj쬚scz5}S|sm_{K$^kiذrMW77꺺AQؽug߾$''o^Ίʎ#7 %}6dWb T8.k׬!%%U]:uLʕuf|ߨqfqYk֐:9 ױzU0c~ܰex@))):=M²(o#6z5~~} ?Dj55 zz.߼NȮ]NF 6ZTh7<[蝀ͱk/Ub4|wϞl ??^əӧiӲ]ȇ}T:+-X0d>ˇ/Au+PqPIpiZzt/&=8._D U3V.cæ?Z5oN~6nL] i/^OR4~5:dddp?n?"o;kWV_"/Fѣ133ӳUrݗEff&$''sj8$ѱc<#(燐S]ju(  P,B=d2z(Hړ%9ccF{K]vm77WsHr8$Ixyڵ9;..iC>|Hd]\WϽ\&`U:AAA 2&ONܽ{7oihmmMժ6xyƦ#AANJ:޽!clllD%  / yNXia͂   Bh/GyjJ?CfAAA0\JPV?C<'LyFY,R  PIXO#   " Q1mن~Cv:FqcTRT*~AxF Eyw!7UTg8v( ceKˍ_r2 ԨI2KBmF-W#ł5@4հIJC/KZDD-uWX*~A0DYۦ& 3x7m.effB#o{_=}۱+$wƆz Xw)))tUc8Uw5PSY#82ӟ3:"FRug9Bwջ ׏/BPQMm`Z7o,o˿SgC"WYIƀd*΍hjF7zύ%O(;+;8z( L\R@:P-]SòF޺y;wIl<ݴqccc<8A騚4f4W5óYSvhU.--f_cpwGn٣;/^,0t4jȮZT4&dNnٜO&yɽnnx6m¡ԛ{9]L }{V7YG>0Awtt4G}ǽwUN~dx={}*6r1UljJ}7W^܉cG7mP)=}&=\j SΝhZ# g>3="> Rv4 C<]_}s\sjc܁LXXG[̜> %ɠ6ur((00C,x>8[ծŪs׳ݻ'NsiAj {xpT* @PDǬgkVZMVV.].0 pO>SB %%W13ӧqxfOn卞=9v._T6z)öҳGw>̢nFڵo{ޜ٨T*N u6̛;Xt'v۳nF#gK}Ә\Q|TnD  ʢ7%C'd lI ӵnjYкt}fҥ>c|N1./_{JrVzɍnNܾY e3 {jݺ$%%ѶU+.T`\W&(RD?&,.JVc TTMsau&]:uv<&0<|{<.rgP@r(WITL4-W4rE[ᒚK.-▖z++MM&uCPAvf ì kb99ssoOx/PߏhV?owkؔ]rb}o/Y&}|HHHͭ<)׬Fjo=ݼۿʕڵKe֎ӽ:qO$(_9'ww/l.mس=ۛ-15Ʀ;XOEzto=#{S`}3w[RmQjU.* P*( \\\K빖HHN_QS/-_~?jeKl\v?0U+v#5k WQ\3è h5~zfQ^ZJ#m{Y`Ϯ{{{O*.]"۷nQR%랿|//B_xVX 9'Ld4iHСԫW}d*yydV ̙,_a܄ IQsNxY;w yߑm);XOE18{O1vEO[@Wx-4lAղOdyRD-p!.uҋ0h4hZ S]LܥyS\N{{{suo)OOORnescn3j$fVr21+qu~g̨Qs MBŞsׇ3hp$ɲs=Gdm{{[-LJN'{S`)ea̓#8j_~atYɧ4fݰ$HDΆ~Y|5nIMղjŊۂ۷gҥdddiˬ&%%}G!84aiFbqpqq{c00EfkӮV$==˖Ѧm[ҙE ;y{2; !JD?#;'eKC\\+<.3jOjՈ#''д;XOE1J*{7i,YJUpSʳ|\l̖ҬU:s/6op~-\b^!(UyL jۖ~IQ8qMdrƌk^V2")~1s/=Ŗ1y [6om4l$nnny+Yp!!|yYNf?_&yqbf:?B<1'9ma}SO?Mֹk88.yߙLg5 3O(4S4iILD6yaC4M/L#ާ9KshVYW,n9.gmaQܼqB IDATAήB!DU^3-Q[XY+x,-_njaKYg3aF/!04r0w"-5%114 ,>B!D9S3_psKΫS 7ӧ1y! !B'==b0`{Lf%p!!rB!D1=Sb<fk.2B!s„B!Bªe E|/B!%KԗLZEYXB!BE,aMx7Mm} FCV>c&kۧNYA?r;rqƜXVͼME //o(t: j N91gWٹ@֭[|1ftvUʵq/#==A6 $vժV| P~-$tzj5|<19z'bO=0}T*V C!kX\]]Qp}{ҫgO<_zeCI͉c?DˋJGqa@+ 'La>V,_˽zQjU),MtD4?jRJBl HJbuu{b] ܥ+2n\Y`?NGZر}{ۻʕ}ζ_cK1G@nn.۶nnZ7ƍ ;;ƍW4lJM]v>{ѰA}zW._6+]bb"aa<^/>{mر\ CCپm69lG>b5G3}G=/+WS& +4襤$"A?ݺtرck>۷ɓ \84n_}% >6?geeѴѓ|bٳt,͚4~ɖ9q̝ `KOO-[4x]iߌ-rTPո丠LUB2p`d<[YyұS' ?Ezx|om$Go"Kgz:ZiZFr*6~Ysy]9x0ϴlɜٳJoЀC%00mڰz:/f=v—_|nB)ord,ÆgJd6Fgdm{;x6jĉ;h>z1oQk>ϝ+w#dee{+iiir 8̙=WN8ncK=.*,Q^qϏ?3O쪈;":F#%ҩPf(QVMJ&B0ێ7;Ą~SfW~~;t+yݿW##Ѹ30"~7+_~aah4Ga=ի^z̏&z"7nBw!!‹=tkog`m{̱o^DХkWnQlէ("|@<<B͞sׇ3hp$X8bw&k@vϷ0V @|||mJN'eӓ۷Ѯrʸ5 3,AxȂ7놭%F"7w6[O5j3lڸ>IIIdee# 8I&r֭]C!6̳8н} hҵi׎U+WehӶY۷gҥdddi+oۢh `ێ<߽Je>/(!yϏ-YqqqPGd=ժU#..BvҘIIIawѹcYZQl\T-V(J*{7i,YJ*"иI6_OZZ6n,=+_8)1s1_Fҩ_ 2Wc hgUk拯6sq:3qxw`ᅦ }yZx瞷 {ý:f;qRqrh 'EqqZ6e3vl޶/6oCHBQ8{̉oNFxX_Oӿu6oDž1;Zx2")~1s/0+_5Զ-O=tm&NbJ$ڵi hg8DM–͛i۪% =Y9E?w2PZuпiUgZsgѳ׿po|Z8B!p7oܠCp뤳P999v3~(J߹UvQP/T(ܗVs-1,~Mz ؼi#~~MU,L#9h<f:v˚KK!B!ʸٽ{i,i`TBl-J61InY&B!(u ci$$$dF_`޽9A5R:O!B!BB~fhB!B \hժ(r*M!Ba P($ B!BǺ化b;^!B!JZg 6%|ɓDMșӧ WsDÆ%^ad0ɴ(MuBCDQ^cUf?n,#"x{k&N;]rk꺩xEiuђF*E"x( *Ѿj0:-)6}{=>} FCV>c&kۧNYA?E.Y@ȑ e3:jo,r\p~a}Y~C^0U+VpY9=aX$Ξ^xz^~g82q/#==A6 $vժV| P~-$tzj5|<19z'bO=0}T*V C!7V\ vkkiq׾{ճZ`/sZBLΟ?__%棏8p0@bmdŝevy/?tz Zvv5oo#PԨjT \5d@UC ݫҘOH.]quuחq&|t:jժŎ ~޽TKvv[9jrssٶu+χvc1n6n毾y`S5m7_ڵk݋ rY  z݋mcƎeOm͵G͉Og;Rߏ91;yGw^BZ5Y0o^y'%%1? ֥3ǎ+6_Sؾ=M4ނSVͼ.?OH`7|yN/YYY4m$q8w,];=K&,iߝ `% g`<_yiSgkߌ-rTPո丠LUB2p`d<[YyұS' ?Ezx|om$Go"Kgz:ZiZFr*6~Ysy]9x0ϴlɜٳJoЀC%00mڰz:/f=v—_|nB)ord,ÆgJd6Fgdm{;x6jĉ;h>z1oQk>ϝ+w#dee{o+iiir 8̙=WN8ncK-sb %/M}s 'Nߧ4y2&١!V#xҒPT(3n&x%{\!mǛ_bB^^^y?שU+??(m[Rٷ2Ӹ20gl,\Ă W`* t: ݝ]pꕼϫhn/0t04 #wϞU^=GGpsޛmi7 ! {ω~C\]]yŞ\tс{3}G=طw/Ghҵk >t EJp*TÇѿ4fv?8=Vq y0b /2?0/ؓcG!WaI(23 hR46aC,;<?*L䐡,^LF/`ljKYfq?a"cnjf¸q>}}'x/LNN˸IIIfj<,:j<) yi&G1n$(_9'ox. SA5?{8gw$kc[XR۬hZ*U2~p]G̮#@ʕ S8ܾgŊW`wӗ{>[6o&33oނ$P`-\C$bGmE`,ж];>ߴ5l o/oޝ5 Úr}=ǡoX;v&(81F28oVZn%'p+9W쩼%`p`pƌEPp;w@߰RԱ8 C9~2A5?{)1P~-FA_`RDp٢q$mc>EKN_Vlh֮YMFOOS 9=Y7l- 7ѸzyV8oeƍIJJ"++?aL<4Cn"eŅ=;gEGMvZt/[FmJܾ=˖.%##M7_yEG3``v=P*IBd9ߟaْ%xg ;= EqļLSZ5)4m,~;55nIMղjŊ۪TwҘ%TE?84n҄ דƦK _Xފ8&\+_5jԘm;v:„~-\r :YGڵ̛;AȠv:GĠA }y֮^CsR=^BBRIQ:O5 i` ov ")5j<»s͛BcN|s4틇'sCPЭs'bO8⸐#}g2՞U00)Z-G/mԔ)9M[.s/0+_5Ç|2ޞ?]mIL~´3h4 B(S5y #˘[V07Ze8eqΟDV[V!BQ7n!8uUqv~T*ٽ{'UVEB0^P(pqq/^Zb"ڵQ7~ 竍x1&*C5Y.Qw=B!9ݻwʒ:J%KsX;)erD!B!Qa̜>lԈ ̻w[8 0.yoaqU0!B!, V% B!Bƻd]g!B!(A*4B!BqE˖%-^hy8Q!Ba [W_%\U^e̕˗7 =SO?Kj[̜LTPWٙBCD!JC_~VԙNsveqa[?0, 2}{~<ƽ0t(TZ O_O1@ B@.׮u6jZ-bc_λ̟;7/%00L˖̙=۬toϜA 8Q lkݦ ׮#zbCh.|v=!ăsӈ&O2lpDMvl#ld>pƼHֶogOXh3?XޜUls2 +{9BVVVFnn.GLÜٳĉ;Vt'yYz3f;wҺ3VAV!Wa<iit*JTUp Ķ/1!//ԪY?~_} [[zi\bq3{6 .b…ԫW? :׆GN.]zJ_H4 J_:lCػg}W󣣉^9Ͷ儽]?CHbO.]ۇ=#Xsۻ#h4tڕwoq)C^<[4 k{NN B q8|@NafcmDS3au{؉hӮ>+;J~H$ ԙj I4)j갡gwz}&rP/ZDff& 0dk6,80cF3a8N>]>ހ&''eVˋ$iZjHZ5iؔĄc:# 9bw&SYz rFM#hؔu2w򵪞Q9|˗/YUy&LĔIL1F`(425Q0bp>YyѸOISA^._bEqGRw)z:`ți=g@qz'-.qKB!vt :߿,^믨]1<*V$./]R@TP(p'2 hǨZ>^'bg=VY$jjͻg +Ƈ,B!ܻ7[fIL M]c0uzR*lcAJE~Ԭ]V.cwæ|a5iB!ҧo3O#!!'5b~0cר].hKzLfcVa@ ̆*@B!(,_A?INNd3#Βbr&K!B!wɺB!B֭[]/GrFk/B!B{0px<44iؾeZ}0 B!B%%4oќ?Ê: k 2s2-JSA]T6i PjK¡? 66C!$y晖|k#&NrB-˯rQr9EKKi4 h**T̪D,봤ؼK)4 -[bTQ#o:jfgߗrd"GB5ΘԪwqe %VN#z|VX3g7զp9*yyܾgNEpܶgxx;Qe;z~sA]vhĸƑNP P;jU >a\gl=oQl`ь=>ujT*?]Uhr'ڭ^WϞhyzٷi !ʆK3:|>2*yF.]|^ϵ˗ԭ+j5W㭋_(_,r˗^:kŋ%U-q7OUjJ5* ut H*Y!ՋwiL 'Ls˸ Xret:jb~o^*W%;;~-5lۺCj 7l7bW_<)͚6ᛯKw5EK\|٬txEJJJ޶1cDzr't emZsIA9lG>b5G3}G=/+WS& +4襤$"A?ݺtرck>۷ɓ 㙨.?OH`7|yN/YYY4m$q8w,];=K&,Nث7tt9$%&b޴iuwτr, 777222\3˅3PBW2U u_uGl]cccةYhz=`>z|Ie#7ڥ3{aYv۴@Vj9O?¬weܹyi.C !4gNFԔ79~2aÇ3%jca#{3}G=_`<5~B~Y4xq=Ƙ7b5UYXYݻ`=JZZ=~0=sfϦkh(?'X'JHŋdee}Vf&._$)1I5#oY e_XGZ;PB^'gff۷a<iit*JTUp Ķ/1!//ԪY?~_} [[zi\bq3{6 .b…ԫW? :׆GN.]zJ_H4 J_:lCػg}W󣣉^9Ͷ儽]?CHbO.]*Wس9;1Ǿ{<8FC]ٺ}GW>t EJp*TÇѿ4fv?8=qFvfYY H T06>(WKN^r:u-oa~H$ ԙj I4)j갡gwz}&rP/ZDff& 0dk6,80cF3a8N>]>wVR.LNN˸IIIfj<,:j<) yi&G1n$(_9'ox. SA5?{8gw$kc[XR۬hZ*U2~p]G̮#@ʕ S8ܾgŊ|1΋V*Nٶm+ZmʳϚwp  …8jHJ/vߖ:sm׎7mEMF-[˛wgBV[[Ն=ǡoX;v&(81F28oVZn%'p+9W쩼%`p`pƌEPp;w@߰RԱ8 C9~2A5?{)1P~-FA=.Rwoԍ'eӓ۷ѮVB;]m _ٽ6|}x筷hؔ?<[orvfŪO[\KV׮1tZ'09%B!DY!$Sg-}1gWiƎ@vN .&Ί)o z!:1oSIOwJ!BQU{s撐'55s -XWE'w)h%/B!61Pϯ.8s,>s3ͩԩYT֪%/[0!B!VRy%<m݊۷ZFlME!B!(_'b@tJjUl#YΆks܍-ݷ0j.\dE0 ddZpϗhyW#: ߙ.9͕ób%ɰ敛Ŧ+z:`{(^x:vFܙ*<334 tV!B!(?T*  JÎԨQ Ywm s( z=^nnR!B!D9医rzYfP*+B!B\= ʵ )T~ci(JrzZX!B!ʞ\}.J,AB>WAV1B!BzB†m'LT9bL!B!(az.YGgz!B!x(-Qoۙ0 B!B(:uwVGL!BQu:6PqDo)S(P( L΄ !B!}n. Ba%-`@P L!BQuƇ5[gd{%4*J:uB!͵rǏ1 s0!B!DZ#Nn[)0 B!3aaaܹZJ^oKB!BQf\x91t36X~:MP0B!B\}^,dg;0 B!B#zg Ѻ{ <#L!BQn rD!B!cB!B/-67 9Y!B!x)B!) r*L!B!w垰\\]N=B!P(]\,^JKB!cRy`CF:5j>1;0W*_|V!etYT}n(%*_|j1(6tvǸumB+>ƹ3\\L/%!b1HjHMnݼAvBWvv6I7QZmqI!XzL2Ι0hC޾WF !ee))Z<<ޟ̞;bz6lDyo*νe+#B8; !GP왿p!-7_X_<==ȟg> h2ͤ$[V._@xUʶM?sxB!J8!, gr2oM+#8]>3Xqׇ͈֩[2cO`Pq& {/@޽,*+--)SY`j:/MVV6'E[3;~"vgiۦ5oNq_ _§R1 ڴn @[|>7~~7߸7V*?@ᆵc 6w8[}wNNCn]ͷ[‘$ C蹿ˉb5\oe" IFŖj%u$իWH?. QݹQVeOm8+/_)Y m؀?K?'6S|;뭼 +Kмy3|k-7ӓ w",^īٰsBV*%>`1`Myy~z->ZKbg-_#Ff z^ж̜>7|>c@x?:=۱U* \|B؟J*E_V}~a}tr{wXaVk}Fڏz(O >`nyɫ_CBxk; :OVBs_<߭+UpkΜ=Ov;.ue1@Vf&vn]Q`P7jތ7nY^?C*y3_{N8rh޶J+rAhӪs}d$t:BBpU>*/9.ɫw d7&MPTkӚfq1֕ nn<ݢy2>.K^ee퓗%JPжM%,z9mj*x&[2>ZCTܵO6'/PTuww\\\ 웞IƱo2u:^^^D  fRY˔IP*T+zB(bX֭_ϕdee]FjZMy$Dq矖헓>dP*yy(T(] w*'̝C_ y(Uɷo}_m9. Q=a̚l|-oFsm6o 'ٹ{غm;O6lXh^^\KH@Y]V:uh-G/nԪUӬvWv~M4fmdeeg>f^޶7K*U-4u4_mBQ=ԨQkn6<==PlMw[IKO0nbT6{(_q]΄ 0i2ii<<ռ9_O?fȰcİ +jfϙGzF˗|lUYٰ4ŕ_&fr Ne"__/̪k|}+3dg>V^fLA&BQ=;fh䫠P01j2kV4YFXX :ժU!$Dyꀡ_R/ZPq>vkeo`[ήUk4| gWG!Bb*òvZ/_jOA=|P!D)f5 !}|ݛA" fb%$1ckV"J5j$V.UKQ#-WkUhTCؑ!+7<~{?s?sB!LSIߑK; IDATy!B!(J,dB!Bf2YB!Bv<&B!H2!KB!JI !B!D1FB!B ;L)4zI!Di9B!i/B!JBfr'L!B!Qz#L~2B!B"ȲFT2BRD%!tGB!Bb!B!"oNB!B#E +褘1c%ЫT*+JVָ`oHQS5l@x5E/lhdLXjXRRi@l\PGG*B%%&qy<<W!^[be)RIE7}k/^AL8+` k$?H@{5{.ޟ_RRRxcy(^;JԮ (+Veo0i=z`оN``ܽK'07bܹ{ҧw/3lN7[BY?kqMVʈChV䩯3r0~\:]Ç !55 :İOHN]6[שUa}Tubcciۦuajj*S^_om'+>BOkѓl<{bb"cԶ8.e^*riii8UsҶukgbooV5zt4c34RTzoiX^ \\\;eVZ\;npF8XD5l>T\6-[BY˷de["^-s!-dgGrJ Wl"؁6l2Sb?R^ zE2r06|+\/3gLei:Owҥc>'+Y_{ }&'%q%2E0j_Dtp}*׀tFCΝ9xѵKg,,,r 5 t:V~!>{+zJr2)9^Ò XWPjrV/w]e+:HCiwk7lMV,^0˗lqqql۾Fnu?mHpb7⧍;LW݊H] }Mq.,g}}ƷΜ)RǛ~Cmrr2ڍ7~ w¥}iQb?R^JEZZ퉍 ^-[p]C=OILL ڵ㽙]Δ1l FJJjϢyfh4:oϭ۷Pٞ}szO.ѵsg8a8wz=]:* VKΝ*%IS.QF pfh;~{{{jժc|{.Nf'wwfGy%CxXy|}{7.ȵL>([%K-(pgFnnu/\"ӗO>!ղT8@k@Olش)^aKH/ h3}]ZHxJʴ?RAŊiIQцţK)ýqc*ٱnzlpog+TAB*U"66VVdG|B*`hgZ&1q=,h9fMEM˯Ǿ,`iI0@]ԩG+WQץgH\]”O)oVt:Vʕq"wg,4u5kԤFhi*Uč7.\?6Orr T-53t{gΐF  oRb"Μy1+8.ze^*L=}}m^zf5}$%'6xxsn_51}`E(~WyNSE*3jIٞ_ֵkb8t/ `X~*hy3߯leI^o:y2K?V?,--߯/Zmaݐ`2f͙C#77wY]3etƟǻ|*W.SɾMz2cf_pYț3JvD^,w@PѶM\ҶM|4y>oM ]s c xWN zq<*IܹS"A5#zJ8ƙ7:VRxrp^#GceNBd!ʷ4ߏvmgt:~ Z-'MIg$g2]KIy4sִERFy!0Jx&0OW.]>x[M62/hzs_rI_TEiCy&j45XYYQ갲?gNIIT}/Yeu;h]/ SOlGbb";=ŭ[jތM7ګ%Z`͆x׻;]ҭKgW/^7Z4g]FVj5.L:,_N@XWFё]MݻvlU·gO~۽[EtSxxzRF FcaiiɃ;wgޞ>XdU^#G1.0ߞ=@׳db.5~cV^+bccye͚5cUvIK2A FLZL5 ;J >'Ofs!~gf>W_Pt{Ľ1]m+}қVR\Z}$xkN՜8~Yq`ccc2D_oҥ jc`D]+ص!׮^e qL=nyC#死7NtNoUGqPreuMFFy&eHJJbs}3+>h|[;;&:Çt(3611`ݻwGSRR4qΥzFiƎiӸ{._~ƛI'?sƣ {>o29_~&uˣ9Es ףv-[7Q./E@la?|~{~ {Q7B9 .]Ea4oт+ܸq+WмE :YءcGTٳhK'T$ߒʢϞ5zxȔ0Z͏?@BBAkUֆwymv5iwwwnjif4nؤ5tuAÆRjU"""Jy? ~yhVɆÇ+}Z[nbrڶkgV%x 4tqnGy̟I`["nnF7h= 2qtǕJif>u*M (Āϒ ,p+&߭V\SOu*" Qf(4OXA VK h؝ڲmVޚhԶkLǎ57ǎ1n̙J^hۮ#G3޴N;ФYS G|uAd|:?cZE? ,>>ݝ8,]fV%0cGҡm;N:E8*R~gޙ1\ڽk9ÊK',\?E_cZ7kyQޯz?G6m;'4aeZpnIIT}qf%j\o_n]nE]WA> h֠Em[H[CJ(BQRSSڵꐦ_Ij$nFGiM^zZmxTZpsFXvBr-I#<&Qaq(0NJά WfNYhZ} /@Ͼ.E1B!JFI((ɣNNNL1]{֬8J|VAS ([m}d gFɿ* H(Kţ~L z=3.c1lC!DWEFre&A_Jȫ_tI|ʪȫ\|xxx2x.\`2 6$d·_dCǺ (aʕo߁ǎҡCG>Y%S ǎ o:O? 'Nbԋ/1}[^DU2w|ӧyXݝƮ ֥3 @rr2-5jOBB۷ckN:oGBBB!'Z SNet@˗-58իhpttd?gYq{.;Jow+0ϩ'yi9yƎB ۗ܎ySϣ>Y7HޗxDVcaaFa]h:Qj5j'j"Eeh4h4͜Y!(/'k hR>;g6}eL` '99٬[PϹYdTNľiw VVV$$$8yQ>̞3Y`>sym{%e6]-glJ%\I͚5YW( a ~aǫU+܎ySϣ/Qzuߔ2MRaѱ21wiÆ]5@ePZ5<ȇ/?- !PZN\gm\,XJ*1znM >&jԨADD4tuAÆRjU"2 *-7ʼ I|-7 =EGGrG.W&O6>tX۷jܺu˗Ӷ];(ۧۈ#4t>/3ol cܼΣ{qQkyq^BQw}U9m:AkN}ͷ1-~I<46ſv /}6=s&ÆҧW/ڶkȑxG_;}|k*{o{ykse̷ԭ3?v0-Zaa_űd2(a܄;zmq))Q'\]Yںe +>Zn4WFC+cޔzu9BQʩ}}DncysZiZ.E/7k?KK<o,ݹ} BZθ ?;JUbԋ/1: ?4+B!e/f hҒKKK|>}d[߷3S"WkyETg6B}%T_WjUt~O>_Ç(\Z}$xkN՜8~Yq`ccCrr2IIITXQe$%%t~L(q<ի-ݭ[7 wWlB!( ӗg‚E9_ɓLJWZ5;oϿ aܹcg @Nû9u?gr [./tX۷jܺu˗Ӷ];(ۧۈ#4t>/F@n cs /gI|W|WǑwkof+╅sZ^uFyB/GGT$_ghMeE&{i={6^-YL:6m 6mHmhҤQ v-m[yqy]EOad\?1cFiM9~+G^sg/<8-6a/yŤ B\7ŠQ|9~}K.g -"*._akh( 8 .GL پw}.(Ȭ8JXr%wtБOV}hB9o:~_}+++k%ȫٖungF{ifL}uB!D+0м'[6o^͛98uAkiՒV-efCty6Gn9|8mtM9׷Q׮QϹ,Z͛76 Fn ǾٳswfL5ѤIncm<Ͽ\'_7o YGcww> V~ 2{\HHH@qL6tpzx^c]tixxzꖭo)qpj$5kfZD^^Q ķgO,,,,Y?dͷSbccdǴjݺ"Bp`&ƕT*Zq㰵/ǏcIד&N0wrrqZϯo!Wݿakk˘@c(sjj*GcccSd7n ٺxo,:z?koAʕMd&11QRׅM70h nۆS5'SzgZruqj5{xG)Z!Bn.EyªT _,--tx_~ XA>Qzuf>ڷ/2Ѷuga AX\r٨Lj~~~/_&xF&ǏAL!)) +*(cymӷK|yQ) M?#άwgҫ~i&(B 8;`bcyu"Vj;5ݥ\':=Lu套GsB)GE?{W uؑ*0wll+'MSTOqLn ^XEB!D QftDURʯo 4QйK>3YaC^Pl[Jf劏i]LlX?|>^۷oju+/mvfQOBCGh6}_|^\Oqbż6$%%tq /Q$P}!C_ghMeEkx۶ͷqxd_0eTŶyu~ǎ~E? ,>>ݝ8,]fV%0cGҡm;N:Eq/̗ۼYmڴWr /ǜ2& ]]>b̘TBQ}}D?ҽ"Jj>ܸeѻ Y X9B(#55ԪUzXZhPҟSTXXXdKF͛nOԬ;hP>MΏ3^zZxTZpsF#S !DyDym 0N}ڬ@ !B!D(Cyu/*ׄ9R!e4 _(}B!DYQFB!B"1c5YBɚbBMIM֬W;PXdBI~/dIk6 Jr!(݊>*UGGUE2wռcp>,(Ntt4S&O]V֯G3OFg͊Dccc?:OQpyӢ>M9Cy!`x'LDU.]P<<<,?g,^0t̞3jY`>swfLgʏ9w^5ku69ܥ  Z˰!/Y#~Z+':W[hZnݺŊiۮYqӃm ooEů)\y8/KB5Ugm!(Jxl#7bƴx'l[[[z= ̝>o5 J`I_ghMeE<ߞ9aCӫm۵cQ<ۣ` Fӱ};6o1ΛoM3jϿ`ԩ<+[g8~A7aZ・+݉cefQ¸ 9v(ڶSS0_ 2gVAGS|^ !z؆S ~{Jj>܈2/-@T5z??EnB7W* J"[ڴ4oޤuFLD:޹ͬ[QuQ'kU N)MVGxL  Z )uRnbͅD&B!IaB!BQVV4F÷|/Wceeex]#L~e"&ٰnA%;;.X`s?^{s 87#*7?W72?7F2^&B!w)))4oɖ͛iWflf]ZZ{Ulټ{h(]D ѽG6i;wQ#qwsY:u͐hޘp ;h]DEEV”SeK azh40p ϙcV%޵g{ή={݊/jqvvfF;NNHII1+'^WXYYQV-f?h9'Uή^|XaʝJɩcnRBQ~FXHFXZZΝc}̙7= IDATY$pطsxBCwfLgʏ9w^t𺟟wb̈́S(Hj֬ @Z,ݾ1Xd iii@zw%?jQ?aaaȑ#'8GS˝Nƍ,[ݺU~!B?(P* VKqח1q$I';998UJ~~gv;܅-={۳!llxӷ@e]%7_]\^[[fBѫsT\8JHNNkkk[(+.u]شq@m8UsͭQ?wnߦSqPרQ11*7d.ZjoBQN=L SJ*UO %:oݻzrcaݣr;Vݻw T6UH\Qqr2NЪj9IT͉{1%lllHNN )) +*P^@X>\$.ُ?Ol !-- tOnUƲ-$=OW_ؙBV?@iպu1Wtt4+W|OxedCܾ} V˭[X|9m۵3+}z8BChh 060?'L+ ޴d޹'V1>&*_^_}E|| |[V9Lc;3^ݻwYr%-Z4B!('k.SauzY<kSpI͘KhՒ?d ao5cǎҶgΜi.VB 4nNvmٶu+oMBȘ[g8~A7aZ・+݉cefQ¸ 9v(ڶSKZ湲r7WhӦ-z?cxawV})fxwp}4MFǵڵnM֭_ǵ4hذ@uk]ƪ>3ܥZ'WLB!D0jG`pݬZKKt5=*2<C!2RSS ݴjT^o㣪LzH$@BDH( ґ ${룋 QQwWPHQ*( BQ`H'LcȘI&Nf>o_d93~9`rѾy4VG ġ/GLw( yZv̄@x*8XqL5vEd} [bRS>xe_&p*4 =$""0;K"""j/&Ww"""""6$IO 80a>d!"juߡk?Z=e8 ""xk.ֺ̝=Yձ[ɋpEgZ7GÇ/cg~ 7mF>}?uE_{U7)Xnm:"&Ek_'^{Uy]xxx Fg6,K fQIד&Oơa4F78ڒG YGD +3S2H:%))3v,r9DQě+V`귰is>g? Z[ɓ's5>qdlX:QW׆kM2ͦGSA^Gʒ%1cPVVG}:77ǜ?889yy8iM 0닙f7Ϟ?v ?= N4JKK#FJPT$ظ8DEGaO!8$=-9ixKY^yYBBBmҥ 3u&N:0@PPh4``0n:yҦ`Q5AAc"=88ئI%33âLLI'kᇟ`{GZbW)xzzBjkk-Iz)֪U7@3Ο籔uB^^^텅ܹu&""C9sgQxǟyQPTX(;w6k FyWxpB=sUy빹`.&F~;:u˖>R?-ǭ)ñ{W!ԟ waW=Ykm3+ýcI1uo0[M8`@TTM0}۶&HK.HOONqXᇨwه$%ɋSe&$/N.y,ey/p:CTVV7mĺϏZ/n.FO֞߰73{ u[ؽkF> }J:ws_q4o.nr+OAq9ҧCõZzݒ.|4Ԣo 3jzaLHTTT͕#%?ӧNaؐ8w,RH;ܑ:ZnuƍjOK籔?_~_ <C[>=bc˞DDD1?)'۵^GZ5$C쑛S7ߟՔw6;N];2ǎEXXrtO 5 +(ClaP'4}y=bCPXڸn #Ou4z#iq<.cyDN;5`KLj'/ך[f:.Qf_{p`/ADD1kTm/"j;wIDDDMX]K"""j7bDDDDDD$z*""r-AZX)JZQYr,.1l\ՎCDDQzx~DDD]]CGt;z6NEtE<<أ:xǠ_bϛ_~ٮ2 Qx'09W4]r)B@L>x%HØcաC[l?V5oi`رEoX¦q4+UWV 4ڣN|^yi~7FDn;!_~_36n;DxD-o]26d4y1##w#-G۶ۜG h4Dmm-%;69Grb<_1a$| ʫ8ix[;q E) EBbeٿ3DDDB{:( |OHNYX)KrݵΎ`ĝw5,3gZQQQ ALL' vt ꄗ-n>}8Z:ufk => >ں{u¬#TUUam}=xFMM og1b#6Z_Թ3}E޽h!^z*//yl߆??yY35(**^Gaa!֬^!CڕG IF#5u*++II$=>9GbSɋSKy4uc퍸x{􀿿?;&I9MKD5T,{¬xo7@Bx'moSOc]q0*-})~!t:ځ&G_cBtdWЈHzt @Z 3:cx=DDMވ+eZϭXa=Ĥ}`eM]OYpwzN ?/}!"jY9ŋ%<Y9()sZDDD$S,MSY =O?eD7 W5WVV"-=AAq̏{e|,|}|\V?"""k\/Y'&Yӕ认]sYD -W]@Ra1J N촫H;A~~!|lx!b͎̮' 32 >*:=:ٙ2~VW#;Ϋ#QK\/L0~aHDDPx=h5kkjvE"""r37z$ #"֡4""ߥ&_i`~X.^ȓ{CB\KLys?ەlV1\M0źRsӘniߟ>m-OKз-xYYd;b"af˵R-·GZlQ,N^4>[׭}'NDDDts p /kҕ8z;L4۷m+{v08} Æݎ'( lܰ޼y}~R?YǑoo~x/7ʷe&,X[neX5z%#X:^\O.]kN<`u%""a{4 no~ ǏØ{G9ruph4kjjp;P^n}}x%HØcաC[l?V5o DrJ ~Ų's3>F~ќ)Ӧcޮ~)SK.&L@A~>^bq+W~EQa!wCu%""=azݽӦ|L3ၪ*F9og#F=ҧpq$~sߴqFF@@ղt>}pYy8w,:Y`$I'0 HHLD\\|U[H!;; de6nFc$%cƎ\.(xs \6m`'VŠ-o޸ %C.cQr lބ?sf#Fشe9MTbyصk'Kw܉9Axߺ|N^?""rm/nj=aLQ t3w<==_ s)S,\ ~`U]]׭âEk$;{p9l jז`0 u>̜1og~v呂FJT*Hz|Vl\k}8YtZo=ӧN'N{SfLg}2sμy8jߏs!""OF>w8~^q]uCD|zFy'#"=Ȥ$={pL .3IDATߩL&äɓOg #-+<==Jkkk݆ ÒCSUP[[oō )Ο?)U1crsr;>1+.9t'IHLDmm-SS'Ob\KG^P"‚:+}*^֨?lGƂE88rK੥KQRR֮EʒZ-7ᷫVmݲb|n:yHS\Vjj%`ul6nފe|W飏\T3"""ašGl,:׿Щsg޽{#eBzjx o~O 1Oe2//yl߆??yY35(**^Gaa!֬^!CڕG IF#5u*++II$=>9GbWɋSKyxxx`+E퍵;oo[ޣq1=V~]O> NUk1/VoCo0>mϞy9̛= ƍÐCѿP_!|4<CضSW/`̨Q1I#QQQ7W+<N:aCٳHY"pG_57>zo}Aϟظ8̙;>4`M2mռSM-5 o^0+-GA~~=TVUa;|8<%ܿ8ߣ^kH5y99vW\z?Jeh gٷw/֭}{>&7:v <=AXm<nܰ8lKKw̄@x*n"DD7ZWʴ8[mZxǰ%&5EW\0SNߗ>KE{̙gO>QGQx8 g<M *4̟7GAxX(r2]]%""".kv+iךL8"u?'3va%""jm/L%Jvf$"4Q.wil`\"""[*^(gp`|$"j@d^?""rm/L M$ɑy7WODDH Ljk.k!;]UUpHs8T}aq"\x"O^^Q <qc/1/?lW)~jE%ho 9b5ӧ-iM=Zk<??0_nqDbHslz?tʯ̀BDDNz$Z'̱5TWVC`0:9\ >Pyzya]\'z$ #"e1lIMÆ_^Je>FQ4h4:VD7'LAA&DDDDΤLa4p5- ?9Fd2r9v~CmDixyy!WoDv A""|2E\MsC`pu:\ xyT*+h˅ DDEF:DDDd'] >0"-= L@|Ϟ:c&|ZKDZOwlCV+|5#f~&Ҭo=L=|@! k5&M0=5zuY6v˴/̍/6_H&xcX[ ?+˗cF࡯0s{ ""Y޾8w q=buSIhX>khD6QoE+~u~7FXP7R(hz=mJqq1{qO PUvJnf.oUՈ?\|qRYQoowe h푇 ZSk㭷FQq yyw"5gWR%D}c"b w/).+-j "/oo*\O*((+-`Az #zQ8gj#E3(/7A1q:DD>+P\*.X_iq:-ޑvWhjh4 &6+P=]XSGP 4$*ws(B &:z]O ŪUo!;#e%ΪۻϿ ;{6DD,kqT?FXk?oI8$Οm]]Z^zC) ^/YDdFPFP3d "bܜ&5u=mڦ|q)L#WmluT==4s`)B:i١{n kI\DFwǭ(>VRٻZK&iPDQ]YlaaPADD\j!6@) hB""""""%IOu%""w¸DDDL~9+w!I#2""r'KDDFDDDDDԆqzE""r'KDDr:aFNjtJ%d29d2BDTRØGDHf^L@h0- 'Ms~{"44"D1^\ 44e4fM]`@UM5IZFvN.Yo//#3; {w8Y 4|lt?"##}&XFfv<ѣ{4 gf"wO@FV6<=#ׯ#+'Q]=K ǼhDuu5$YYܩXoode{hx{{~ 9Үeږi ʋF^!E :ޜ ;A&olIkHE@&V5) C"( {[?QDBD (0zir5B!V @А`%{?xߠiuZlZ7$3** EQDUU51MJJpۋb7`0@ 0F22Kkn2u=G!?~ R]-Gߧ P[1n`0/̯z0$Y'}99텠 ~ o:/ Vc^F,dz=~~/(Q4 !0FcQ2M!dMEE%3b)FLt ˠPȡ,h.5WZ0]QZVl$􎷺_zF"K!CBT6B@d#""{ ߥK(2驲S-]7|] zS,3TAњ72v<"Śzm2br>(**FHpgǧ^|S!+;*y1m)jR[2x{{#449(*.A׈. yZ[|uCB:#/Y9P*bNk0 +aa!L] Bĕ"6UFXh0s@׈ Fnn.^6 bZ?C\C׈.ZN7uo:44QT|^^5ܜ냬\Dvp8>0˖pJu4א4j ggٵմ4sȸ]eW_!G(.=Y]EЗ#{+%jcIŖԔ l1;bw2A٦\PRR \rf"qcc#"wQDD_پކL&/Ξ=CB]ZiA6_`':q>oHDȄ1ܑ0AB4GA> ~#IN|+( we&"jK4䣹Evl1fSLjAz22;G3zyy#"<oEEeekAvEhh»˛DFSi5ĘGDR5&Yp#L h2~~P)5V _<=M7rv)"rsisd\"kHjĤw h_xz{Ai fA0< hn5qb#"@+#dr VAhxJ""DQ1BlIi=a2S e-O/θDDDfkLj{興ɌFŰ2k-Z~HDDNfpD%""r&Ɇ#::L&@49vh4B&k90.71dF#~S0%""r&[c}o8䃈˾ሌKDD<X7G&N4mq֘ԜV&'""2El5KDDlƤ8Po*`BEDDNdZU-ZŚF.IDDe4lȸDDDdkLj=aAQ4=9(;M`\""`KLjƑYLb9(d-%ŸDDDfkLFM1J2ѱ% ""3-iKDDL$k,a U^##版h S#=aDDDDDDe0"""""" 0"""""6# 8S q8"T@QE """"""kZg PxD""""""g7i*o0H'aDDDDDDP.GZ~5:) O`_4kj558y|S ;\RN=#"""""jj?ƽ#G:-3>2.Q#pz$#"""""r^)?VxGfz:7K%Q(cwX?#IENDB`muse-4.0.0/src/doc/html/single/documentation/midi_routing_matrix.png000066400000000000000000000431571404105311000257000ustar00rootroot00000000000000PNG  IHDR S")sBIT|d pHYs+ IDATxu`SWw:`ˆð1CCý6!C.Mڦm?ڤI4iJwޕs9ݾܼ{h9rDGA)E *V2-3fΞ9@4iP%KF^ AH,a0^|ɋ)Y "' gϜVgAhP+ B考ܽ{R˨ ѡhAA@ jg2g&MZ^<{JA!444۳GqJ"\Qi  RLף  DCדC" B1${AA08F]Oywn^M  $  6Wș;/M rX(A(4`8~6,A!&g̝oooΞ=K*OOAHDԽ{wuFQÇ A4߀ߌꕫ'R/kU[N0P^D[$I$})rCB(̓&MdɒqqMF_|ƍظq*gȑܾ};Mv YvׯKAOȉbўt5nݸfTtYΞ=wuZҨq#]\pvqIӦlْ+Waz= 64jԘ;q/_7#_C#@Hh(͚5g-ZyhGHh(M4-[YgΝekԨi={y?q"21EOo޾e̘ԯ߀޽pUc/:t 6b!oGM0+# uT1ӦIC@@1( M4ٙ&secݹhݺj5-[ɓ(0ڴi3*VӧPz֭[о};˲`<ҿ4ia[z̎SNѲE j*WĂ|:w-LMp}>YqFТys%I$I>9 }\yo]WʛI&Ymc'/^ SLf^&eʔfe@Znc'Oi+..hZ?^A DA|.]vL4\ynatY:wh͛ټe @l6m]v*Q .d+]46n$XcΝ 1lii-V; bΝT*+Wo @XWdI6oBPp07n;v͛;v dvoI$Iy'_=سNqFAmai7Zٳ̑=;'Oci݆6p1LL,YΞ=w`޽Xף{wn\NKNvM{޽{ء#y!E(6ٳ(*___NZ<p{8y,+V`׮]fڵcĈxإjժߞ9uΖ; Ҥb_(+]ygE='3??N``o0A[k͞[MYLc޼|-d  Ud  BL"Wd  B zP /RT*T)7P):E*%'|T(ST} 0`hlDʪL%"RE FMQQ$oyYDQ&(c_*w2| Gy7%>cZUFMe ùWL5lEB=*uS *ToT*EQJq1*Sp2"슼H+XGmk0ЏI{L%]8nߤkWa^7DC2|:E2G$*f8w &qVLb2UsD5,09s!j+#JLic , LogVC|4ɛ5i2tYGV4biEͬ&_F eO  4GAADp4dbۣ  A wAAd`m  ;nf40kD0EeA>1LQ!!0' QVcEAHLQADLkH`ACSİoF[l>~1Aw=$J^ ׀~\ \Kb}׃`?4y7iVTa=>Edɘ:W֬\NhX`>="a~xþMԩ`1w?iKoʔ)ܭٲ{})^$y4i*v<|b?+Tdxe@}defvxxzҩK7r;Y'L8Q#̔7f$?Nj͍]/=}U+{UAA ~>GolwmVZɉ'sMfϙ˝;w_߾x{)w5͛{JO]Pݶ3_!CqcQÆϦ\XXΞ͡Co>TTѦ\|ӧu <$MΝی5ׯ8.,, '''Td6b?2ɉgOиis9wߍ!klfvhՆG2aܹsFM>OlȖ=;͛7fM[Ç1Mb'cӴcvvD^@ ZJ-]JdÔhР6S:{]rg̠Ylܸ޽0_S‘GdjYb1~KnƍL+W0x .{)Nlǎ=jմ3^3ջ77ng̘9lԩêU+ɔ)V~qbJrɕ+'VKΞ1gM|ǝ5}Gkw7@63&=syRNmWT:]p0 G\ olz/~肃9y捲ёjQo9w1+wn<.8cG_ ~tG"Et"ٲ [\x1F? a:cꤟl?#y䎓̜9U&)Rp%`|Uz}iҤO_4ly| %rFSdIzRܛ7oXf-}'@ĝԩSfSݻG͚5pvvqF:ݲٳh֬)4jԈ3g%gϘ&Κ>{ƣ5[mGKr;Kr3" {F>,,+ uZju]+:V\@@E}ɒ%l̚>XB`6?ƬZ)aaa\xu-G/ZE;b!> woɓC;f~_;m۴K6JBÆH*?]ro޼k&NvZ#00q͛d͚+gNr?ѣ 64i0d (`w+VU8v0bpFVݝɓ&ٲfe>WƑ#Gx{ׯ_.mZPҧ˗TgOm;xx󸋔3G($"n..fXwՕ*\woscYPP..yOϸFbDwCH(B*_VSj5nߺi{-EIf:ۄ1L.=ƦT^NN|Y6׮\ji?+Vfޡb*899QR%?_jԬM)P25v]:vʃ-^tBR3nM§AHH?0ҥKS~= \Y׮_KnΝ 8 PACw{n(P'''ׯǃK:5˗ɉ/Wѣvߟjժ25ZMjոzZ䣓?~,f)],3ԩSkAc9!X&O ^dAqjߠAC}h[z6l.l~r dΜ/_8~? 3ɒ%M9V˴i8ٲecȐȑî>5kիⴉƍ̞=5kV@Q|C  |(K  DGQG8 `( `y2  1GAL  |Dd  60yӐ@RA_=:RA"sdQ%  D-=AA9q`k¸qsc~zmM6cQwFͶ[bߟsүo_֭khҴ/0N…i׶ݯ5w8`#L1[6kZ~>+WR@~F n*Yi,ؚ&9 q3mxO?Nk^z3Ņ3J٩cK*v߉#T~ (@հh2d`۷DT*?Ӯwܵƍk(>m[( Ϟ=c-&M${lqѣxpܽcg6RPaF ΝݼN;%Lc#.]JOΩ=5.)gK%ZrR6/N\RCNbGR;jhqsuZߤi3^+=Ǐ7ksRzzk_7o 00n]իW=L]1ϴ/wiێc36EP^ӮM4nĺum^zߏu;M ߌ4iڌUVpDVל;wΪׯ]SX:g>MŊpqv|>}l оCG;0q<{¬~f*֫ǚuЇ5kW.kΈ dhܤ1;wKk+VRBy-\[LXA|y}o)\+VZǒjҥyߖ={v/|rSLXu>Bn:wիhެ .jGƍ3ҤII'Od0 ֝;,?}g ?`br<|HNG̙INΝćjի| rbF2eؿ?wl2Ry>E*fc&Ȟ˗ѦM,Xî@|hѢEquNIΞ쉏N{1.TpGTɒhތK|ș#>>>Y2eˋp1Rzz5KbSpyW BUt߈ot賞WPn]jjըaŋ4ng?\]]jQ.\i&hҨgΜ,MY(Uқ䎎T O>}>,BrGGPR$ON{9[c#zi֤1j5ʔAвEscuQbEc^QׯZMuv7wk1oܨ!%<`/7lh_n\SL vϜ=KF Q;9Q\Y2̮O2fXWJ{ْTY|rI[Nl1{R eʔY(aZu2c5do>Y2f_s꣗K*vwbmDA=-;wf-j&k4nl4]pg,I IDATgʘ WW.^ NO&(0[6O<ٳ` +VApmX1y6-[0.֭ ֱw?|ԩy aa@̙ɔ)aalٲ ?~2pqA;vҥKq&C}TTLJb?عs?ʑٲ1irE٢鏰o/Kg%ٲeZG1h3rE?&M7om=}lF?X(3y8&ӧ>b-~'ߘ=ey\ 0pfZdr+T`]TPfvZժU{(V(:gt gg5qSǎL1?q#Δ3c,uAy4p@5k  hSю:0gxrI;q䒊o@TXgK |HlMϿ_Qzy  `P@k%AA18AAy  {AQӓ>{Qp!m۴Q#)^XW1n_plIv^fڵ{_Dyi׶59rs;i$3jgYh7oˋ>z'Oz+g 88?o ayqfϝǺ5ԦNUX¤Hy%;đK*v|MLJcM{CĂsh׶ k׭]+1vIɍkV[.vӦ SƌL'V.9fRb?`ӏǏZpb-…ԭ]K~Ko|l?AIc$y5޻qW-mJ©L*qg,'UBQ0KLurI[NT1YzߑcG\../_'7oӾXד>}:N:e?IhhE2b(z˙sg,{2UJ9$͕*U*ӭkatރc'NеGO ½/_⇟~K|?a_iܨK~[+Jzsa3TɒR}sgJ Yn]: uV![,^0͛jL4ؙ˔x(Aɩ\"Ϟ?AzܽǸIVӱc.ԫk_fMYKn޼eS'iPj2K3y9O/TnV;/',TG.6>9Vej5_TZDzuٱsW4ǂP7m{FSYñV7w7PAŊXO>ѣ̚=l㡻 ..5 nunn_]N]Xv}2ݻ-[Vkǟ&ݷcb+6uptȻ󆥣3<sҥKϏE :Mj{@:c@pp0iҦ&6#)`E|},rI;q䒊\ҥMKpP1m (;VIpBfя]\\jAoZm=yOOڵ#x h5<h@Mww39*]V_ftڅŊǰw _V&f,Ӯ|?I2%^;Sṣ^̟;>ooK x;iؠC4ڥ=+11tC b޼<4f32>d 2_rUcQUk\$&咊~[Xz=-[#ǎeǮ]*X0֯^֯k8w; %;o;q6]zPuBu3o>H9?;ufʨ} eʒ/?e˔5kom6̙;;vѧWO'ONxծwYoFB%ۻ,3Ͱƺ6Z1wv~5S!(0ň˱'P-[f2 OOOVYˀ*Zh%K(Z*k<Ϸ8y:uCؙЮ'Oٙ^={*F-aL:Ǝ1zȒ9 _ Lԩ4`]:۵i k2d@=,Ux BBB>Wƌ~:u86!n'Ռy7X%;đK*v|Rd >z^_K}.YCQP;q;vbq ` X888>c&Nd $z+W|*ڶnAރȧOb>ֳ7m݊ ~СsuW'AK(= D.=.-= ? Kg9/K  D'Vp҃bL  |(&䎂  10:p4L~3) g| KAsFAAAΟAA;b\~0yʹlQA|> AA0GGcAA,= `bݛlo>V2E Ǩ~?MbңWz,{ 7m+ɉ`#cL]:ulV:88w>GGG3o׹Ș!?E >eʕ#c7O<ߗSp!ΚA_lJW'SSXc:I*&*.&ef:B1%;o;6|'3dM;wҾM.=zz=iӦ3gڜp w,=zߏAC8w{uk3%Tpqqb tlE0}8}S@^a_k~</^(J_ן ?OWtыMZG[6ɓ\2<} E`CFK( 7NGxx8sg1Xd1-5*+(E޶VP;svl݄^FtSX!`ݿԉ?ѥcV]ˣǏ:g:w-[+Om:?ߧAL\yիVZv];w"<{FXXu7Qb 'N",,:8q]r EΜyQEKl3%cdȐ0=;wݝϞ=[6dɞ-;Y2g6oɝ.ŊeǮrӌ7̞ٳSPAwy`3Fq> 'dEcK*vwbW_2eJu]{6mL2@Yȇ~~ڕ#nGѵ 7?F|#.AeΔߌ͍!6lތ>K4t8rwVm:p 3f&0(93Ǩ?yoE᏿˦L OmH޽PT4k҄˖Sx1jU9  i&溼ؙҶuKM3:wFR ,?zu0}毌1()E#F:U*.[`N//wlѮŊʜy 1tHMlωC~w "TG.U*Ǐ4 + Sf# ǁ2dqՃ ؃>zKW8i Ȉcy^ro^bԷ2bXBCCچh8s,fwߛ鷤@FÌ_лW|n߹cѮ0-vXoʭwhټvB0\Od,uqX䒊_YB8|:'OQ \kTƖm(l޺իYc͔.Yi'r8ad*S6K ]fD}Ʋ,TG.6y(67*thߖ[߸mZ>c OA3xxx)SF2oޤFjjVwAݺ8;)ͫWoaaaTZ)pԪZf/z]}ܸyիLZ5no[?:* }x8AV]8Z/_Z7nlƼ(Kj55W[~0bEHrxcbIDATw_NeEQ"㢦Z/uMqNZ(Uқ̞/^0qT*U,O_ZWH|/',TG.!Vxmw">SXj5-7Br<7VbԈԭS (W,Abm+nnnVnۻN!YkcVkFc \#ҤNǰ hU֞s ƌaޟ{D{WW-^z7t!!4iҐ5Ks%… $uuq!((ȦN[%~<.4Xm#  ; ظ~&*Tɉ qm2͍7vr`wqv&PݻwV%fED|hj"hQC;^o6ZMP`ćׯĮ`O.\DJOOҧKgVn j8;[]؇5k2k4̝mVWnmXIFn??թmV5S.nn}֪|hE+#ğ8o-_$\RCNbLJ[1(md؉r)2f`J:w uԲ6g8t`CG}' o?:]P`Ac]ʔs<صdQ;rÇ o 𑣔3YvmQ ɐ!aazvڍ;O= {ldɒs=[vdl`Çɓ;]:+ʎ] ߧ;n=9gpt҅s. Q4sQX䒊X~n"D,=m>KG.|%׮%ctɬ˔.;(Stjќ|G]Iݝ-ܨ_cQނ B0[zȜ. !:YzAJEN<|H]/AG!bn,$$$MA{BBBHF.Ķ   A:aaa$sJ( .$IֺM[.$Զ   ZmJ_H  |ڼ~A1El-6*8%On.AA>mtJ2f-[d`g"M,onA$UJOzm67?[G=?PIENDB`muse-4.0.0/src/doc/html/single/documentation/mixer.png000066400000000000000000001567541404105311000227570ustar00rootroot00000000000000PNG  IHDRbݳIDATx`g!PA`{A^ *"X U tB{folۻ۝yfIE[{ $  &aJ㻺v%njǴ:}t"[ k= ܬg]U#lc| _)FΰomU@ڀ QjYu4N.h[)_Fv5űVMF\YͭRSU1N<{븨: K;0F$hp'8h b'NB H:xPơ˓&(ݙaC_TgHM;:@cc8ps% vj7;E(IL 3Ӆ9i\oq>,9GGL:}<雞>3g.uYYzaDU•FKw=4ܫ"t)" \'~"7q No2%?Fs7-p igb-zJ2׀iVʙŃ .#[Htg ρnm0f!@y|f+5 ^ Pi'Nѝu9s^ߔ߻iJ|>|hBzFl6*!F4bKt%uV)DtKSEKokG]Yb\^l1o6+*ZA]TTiR@)B:b=otte o<Ҟn !SҸt}IdL+fKR>ӇANeڛ\PoàI2&t2"0\F&b<`Om8yByS0Y(kU',QPNx(灘3b</YYn~-7nŰǝx~rp*Y{Y, >X<瀟(9l F8yl(9T!sgNq«AH5RYQ!ɨ&IRA^ߢg;lߡ-8嫟K#qEeŵ )|UjMX"1"&T&W*UPSqD8Хo *" WϽP$!5#hrp008:FcpesV讓v)b {4HsW5ǫ~6"ҡfݼ?p<!{u;ٽ]]:$5` $#t9JWby@;5ޢ:q>fSwtmqTz; URkk{͌$/Oc_{߬ L8o7{;BȉWU).m-ܽdw{]w9곿a+O ^94J Eܑ ;=ژ<1 g/`_ 3!lx/o9c挏5[,͙SYQZr_# n۶믿aŊ6+VzpxAGjTaR5tݸa1lcŪ յx{ۦ=wݶXMUE5X DKO>t^^0D}&v%yn5]J& 9 ҕ rT ,@ũILai`9YN1s C_, U"xܫHwdܪ'Ҧ2Qt}cǃ|5{iǮL@2EЩŸu=!kN ~'R1&y(Ex1<^ЙV,f?\p(!J’m!sTRoPͧQkHWUE~=II\UQ e6uz ccb]54" %T#Zhqfɍ70eʌ~ᅩ"B8G}xl[wg sÿI"ØGg#TT* s5lv_v&&k|Mx&[x*AQˬws?4Sr }tS{NƠ6=4nȩo3VٔP'K5vE5k?~bO?K$k֌7nүQ~#Ks;::%%yD͚szNQgLd|[c$u:ݩg][SåP(O: 9Mx2ji {:PM5UxhΗGa1j{${J99͞ІwGa`3U(򞩝?#DTs_w'>Aܺ ;uoV[V;#u#gA5sWljbO#;SL:\ yZ$h7Gk3I\gN^$y`"N?}to' /?үΟάvjbiA+SȽE5" H_|ښʒB}Hڟ?a8؛<}dښK47f ̙glM4CR:E?[XR9v} {piuUUP@p( vt#6m`)Egh 9^uܹ2;9I@wfiP(R?CF|Ow>KL%j6Z/n"MF j# +ւ?zcw6Z9~ou]p_^>ɾ3`Ԥ=/3[n~fh#]#Shex>3I(2j/yryΗuwfrf[hٓ\IK8=Tšz}JyvNxLrRա!!N7FuQYQZT `L߷B~#m{LI,k}hZ4[(**l^paǎ]p8Iٹ]st xrlGp\%l}xʳ Byn`x rR!HzN{ޅԆ!) yd#I> 84j;Fؑl5g,e3Vjtj1ԚZCl._*?8x=ݙ[:7}E4A8R|Hs߭[Su+4mZzTd49OFl =;ǿG {Pbֽ=o1L-4jMn^NA~YfE*hDdž%Q +SCyWloA~Nu S!g< Cz&!=R;s`PϦC3M!TX<;{nU< At:u͉v/t' ܜfY*CxE;U Bz`}跎i)We\8K$a:e| LQ(j w]Q Tca4J2zG0t.aLp>=#Od.( *5yagV4i{vpl .&(63e.që%/r9 /@x5}5Bm&!\vܗP' [w)E)/1ϕoy <4 %=Zwj3 Z'S|gvi#4<3TqӜ.J!0yyo* &, rrqP'QP3wu:gq@\nTk2.am &>6܆ @ܛP`!ZXMEz@ҨP.g#n?~&#+u)Qn5jFe4  * "=%2"XNB}={aTk<ȧUHB0ZnsWǻsk :S?BT'4g% <@TOe-*l[j|nmbUms7yx=ˡA-􀃾(la=ra{ A=&G|OAd)^31:G)RZRF(w7H;^t>΋D[=*cgVrd d9*NGZPk0 I6Ϲ/R9>-)dx 0kl[tI&Ly~L;-+=Ƞ ÂafO'Aagwt "F<_pBطxY465@PG' o&z;`#B_{jʲ@=qso~ bpVѨrpZN5$?O*k3텣&5 Nt9>B]@S|EXWo#6lG爐SA>凯0i'4\@/o]{l;:Q0Yv&Sk?də'8qlrv)D߾Y}[yd! U{_ƬBUC|B IOox+rOO|R\wYL遃ExO'x,g W u4FfprӛL^[+uSXg2 ;VS[ n{*xpTΕ |g5 x^\Bm"59~HXʲSc}wꮞ"߭?dEi/=N*ӕs7JbK}_jo GiˆG>꯼cb`3;~L $kQQjߴoOB_tP18~0ה_rߚt[/3T6? Y ] j. qVkD)[isVX y[wPO5gqQދۧ^#ȩUIQ|sMDUΑo4{.ȡ+roGA=0s"ޭBC1`8AWbUctySא-ޙ:!UT)o}='ݽ_.3%Ma~}CiK:ߺpә!N[}BK2K St+93 _އ {&y%=`q 5f9ɢ>rk!^  J:4H(|z>f7}0;3. omL?6ëTqtګ{2RVg G)QJ=ˁpʅY51sZdGNae:d7O0 *4)c\,͏` ٝ CNGwagO}3{*Q>8,ig(Z~@GI.xyv Vиaϫ'BAN p)$@%5 ѽ)ˡоM_ 9e= fOLle'<^ylem8 ZySկ81i=}>=LR>kҀU 8Onߩ˜w)կؾv5OZtjEm۷^ ?K|Z7;uYK{t}|g%pQQ>1Xx`?4 I fz>-Yx(:^<sDϕ̥s>P7ϯL?J1e֧Qo=?t3S5R u]ʩDYƦO<o*P0o|ޯ 41 3UmjYʷ[}M"(s?5ih+kSbytV)}=4B|Z9t\xF}h /G ;^QG4f@*0ijsZл>DTDS@R[ވhYj]x$~̻o긏k00 ݟ9OI{?1sTOL;3F֯<,k75gc~C*C>sm"$Q=-ߓ%J 2Wu#Ok/ o@vx誔l +2ַqt2,#@!^]yaC]XN-O\BgNN=Y,[,/W-f;bAt޲duO6vB?hw/8$?p=||y#%sY<;wlt]4H=a>ul 9@',}Z)ߙ)3ބ>ޜOᄆ/,ZtqgNz;HC| <۔w<ԧSr(Y{xp >~B^a}!-zS*؈pZԇӕ7xW6wF{h{]{@St #)#V_{O3Azf+8~ It_gyğɸyp©<~TAL8<ڠ0EؤQ#s 0$3Ѭ˨r>NmK~ӈqG޻aOF RR^eq꿺6"DC}Nn4_`_A R ʈ֫mwHu=Ԕ{ah@ -odp"7 EJ'Ϯ) j{+t/ Ind'n7Eȍfko$1Cwb{!u$;_* \@  x]!1l/DbUWh*8Xa,?=@D*PIqz Rdr**3l+*OԨ:'SgZT~oldHenI/n껦5Gf/5&DcOp^&b^yLHԱYnl:ǘ.ã,xEu(GRǴV$✢b{S3M,<^$DOɫ^k~6n5@ArFMyN"gWJD9׭kޭ{3ѳ^ߚU=P(21٪-4]i)]jL;+㇄*ÙI89n;gj_ ebya!uj٢)3=~4!8"S-~l3UZ5,yhHiҰo{Yu`?j[k 4#bC ϛiiSN2HYyվRh+&<"f3S3 [W?to#}P?Kv~*T)+o͗\y]qA5$š smwyRZF I A~j7.=j8)Hb;AG$Ú=: U߯9IF*ÈQUuKfy,0R}w~{Nػ[HC]mYJԈ"2z3-d}-8PÆEvl%5VgDМm5 ~V\U4SE-H%֗X55SEg>/*̲5 ķn/ g!6Qc1W+ *ra'G$X >{nQ^ЌdBrVPÅA"Z)9Hra P&@._-CČEsɺW9c_^>O\7>1T4X hI[,_}z)Ҋ׊kŬpJ".l}N*e]8۾t;gcc6^Vw\.`ξ}H1^?{ʟj6Uf饺dBN,j<#C/v=ϗ%]{s-sF 9n4ʯ\{ν,xBe9)xaO%az諥W|0*[6R[ӏ1б_ɪ օ$\xﮆNxg>>156:䚹GpKKX$4T Wo6S \ bښ%v]aB E賿2+R@9ipP+jV;I4Zr1uR,Cal1!qxl#֔~"gkɲg:=fXn%}I.XR8Tlm~y0e-+*ʕ&͎*shfE.?6C܃1~$EZ-yyYmML ׁ,5P%Vj)nزKE{twJ$wH/QZ;; 3梅'+} 9 9V,sra!}D%zᆈvY;~1Ήoh qx}ϯ9u~Hͧ/}ꔒ;*G^\y֜ikޗ;~ϪR}to*gAB .~{*m2ٶU5]썭`Tʉ,cr&%* L6y%^quզSW2l'1Y{s~`ҭ9U@gsLWev-Zogj; {۩C'o'CرME N׸/sslIҀ)UX^6V#j*N/J^wL!fev: 18-&^%xYŧ:X?lA$xQq‹ʟ&$|.w;&u2ԪdoXpTXXE[6S/䩩JUmwq1;{/@|y,߬owj%lU퓒=1dݵŏb*P.Cdž]9/OOvbw1H/Y08lpC(~~v9_[P)ark[p\mۖU)K֏<RIvTe__x{rHЫ%"8:L}04:dUNB?sJjRV|)Q{qNǾ`|}ѣظ3U .?2VUX_4 D"&7<^ȃ~ݥ-UTDLMVD*./lVƽDI2N[驩h[nՕS"+ɻ=uMF{sV8H`![>"*3؞E&]4"XJfn _$ 98nֹhrXF[ nA D[W[QMhvτ7;kEe'gZY8#Ҫ}U2~eb{oޓÞZw86.ωڋ2CӃ)m68] 2Xt8*R-yEfL$N%ADoφ_BPJj=C 0MM=RjOݢܧA>Ϣxȋ#Bد(&T RpCՕ6#ՄW"-nHכ)mwWiה_@8@__uw-x 9;oO򶖕7[O5'5I#;+w_ >sg#tr_m%vuW7FڅKæHd&*f޾ !ܐb/@{c=_SP *J:q!i{@kC:|Ʌݸvn_>rTށH1T-j TF:I!Fs]fGiU njjC*ݖs |m^r}gH}uTvaUd#B{JzxA n:UGvaz]^XKRP 2\QvHoྼwԸmzLzI,Vh2Ԋu98[uK?@$rh}9֚jN`=J <æ(Gp 7hV ^!.XlRyy#,Fۏ|W/۰h1WhepJxXHPۖu\IqAڼ"B1adlq_$L| *Tf"="kGώ$ET"CKsUp"ԩgJ"'H{1JERuŸV  Bqen8HO۰z~tk^vJmqtap'-TZVhN.xE3FVtsLZ.65[,s-jm eζOA ſ /b{<<9҆ğeh{xvg.s-m(+FLo6S_DO^XkNN}Ɠ#u Dcn/ R|Y)]d! g/G`:_?tm""ɇ{/E{_x=]?? Fnj53PF(Q pV>q:ʨ>f#}e߂JUkʿ~{)=ⴌ,ՌՔ^.(=SV?qrpȫ-/"Nȱ yyn9b8Jpy! ϹDbx U8U!xթeV#KW-P W朇 *̵8MO(|2ƄKWkԪ|Kˉ"">YzkOG4liPWZyuv߶J/2`x]{qi3DM 1%;YjTU\pI=xW|!Zkd,RO2Y>)Z\SJ>I Jk% .ZOm-D^XR0ʳ_3L\&⦴;zFhdb/޸+&6$5,Qu:qM$ZsSƷ q]};RU!G('H9p'CFeXm%‹r|!scWO-|G AȯV^h4Z$^^qiK#ªSd3WA+ 0?^ VXbMFI.ѭ}s}G`p xV!uY~} ]Bse!T,׉ oXX ?l.rS톥k.Ao-x$<>,sP3mx[sYw蚤0iq DE"nbx@22b01PfnΚQϏjcRZyb<;"OEco*#]B# BSn1FZ 6@4<i3 t˕ $\˅ija9SF匈0&X$q=d^nd;_vExOH}7N'ACSpjk3ڣE7j>)x"8Yp D䘵,xIG|^ECod^O]re }&wZ0q$6!`wa*o+%u#zx1Kl.0S+Bh ΕNoND}y5N\,}ihl}M҂0bxNqSÈFO7bz1YT=$Һ`_QU։dU]ƴo9EcϦ'T.K obv=٭\W~9]҉kVQ~XI?Ym/&jc*\ۙغ{  r2?ظGsw E#F[JꆲQSzwKkǭ 2yfE3+zte>y~sxa>b'. b%42(e}[fߞ;Iboj9{%"GkBSrbZV n;ƒ_iւJdv`ٿ[\ɷ7kN~+P/k}9rG++k;YEqQ]WNN p 06=5vWiX1(+..\\fu+Ma翇Q$8̘w!mJ~PxZ^;@?01 nɛ1$yK;]8hVf21+j.c)Qi飭=ZI &6d"Sa΍+p*S{A_~E=FԙW(,dij}6Vh,%; aaVъhV{SFF@nXm c O~_tXpLO>CR /ުgRxxl2Meg&hC%/e'QO 6Ce<"*)w\5%$?T⯏~|Y<:sr^DWC/^XxJmf]3XFuEƓ.o+_ۋĉO|zFzCf#f!Fv1M+UKߛ=.&]z=|Gb%BTk@ii~~z:m'k>a+7TZJ.u-tb@KY+3P6j(?o;"Zk&hg6 Q)歄g"'zpQI$򊱘.WT|z9@ix %cG 6k!af>òAl"c.2 #Y>U s-8sIS_Ji1'JLh+fjZ lf xd{fhZqGw't+;fO HLYo/0uם}l؏ +oS8!& YdWsF\ŰNUO-fAvن7"2GF:զ(*ɦ *k᱑߷=Vcp} ;D5ƒ62ƪd?#dBF`2m=7n¹b߀M)\^ج,#U;4nDFl!Lx=uaVׅ_GG6SJGFqÐ~}ht4f,T|msqaW'dm.IL&F;^} Nb#_2[c?-ʩwnӘYAu˦¿^mNAqd;Q@[ϽFM1ֽ=O38;lL3np4ly7GB*Fى? ,fSt{$oǽL~𢉅 2~d|;Z-F5V#$i[^FlձCs!vm|jW6Zmwm4sݚٵHQ,&[=h R^&0TIYpX7hf_H%բm5hŖab97hN>?:삋'6WSy}BD\h qE1׆/|thIm'ॺPsLOGMmxє W/_ALDa"t,M+xV;_?Wz`-B6u3M"_pFPDИ!{~'+Q/R^ciS-|o8a=qoNg+|~(+Q7EO6:=֌ t{67<F(w>lIaJQra6nX&B&x|7ݻhvGQgļ Y욶9 HSX,CuJ;TTz O^$ J0KJJϝ=[[Sc28ÍfK@T7M&Aڰ.)b>\i&BFSUEy~vJ RX&'N^x[_xѐ^x?_~YMm"Wv1Z^4x-x9 *9Fq GF#YWxԯUA:\\^"E/HctU,cD~fd1g]>#sSxQQ* Il\[nCCrvϻԝeg9 JTL,^UYA&%]H&X+W(6h,U[UiSjuF1<4U/FxAMv"6.>?$%<\uE[ X"$wߡ+"&."*Fd2nCRA!Q%E%E0j1ז˝^4Fxd R}g$䝤lj4 f_HD4R,Yo'S^4x)Җw 6mϴU.:ekV6bʎ!*UiJƋ$Q E4ZE>EF&8 /ҷkK FT1^FxtryAuõSsvlFu9mRBX/"0*!m} \1'rG2#G:~\ DINϐ+0]DPW BK8.jY0p*bʾ Hŭ/^h(!{j,طt6i~K0m1]~yߍ7Z]ثSkօOrAxB*%*b1T{b\l" T* )͂I" y-^FxayoE5wh$ψ"W}OhwܳӺp͗{[XN_^u[YuxgԜ1{5/ (SGމpSo,X8 T7ۻ܍S5BH$RI@X &.<"#jZ윜ؘŭ/^h0,{sK]WF?7;bv"0kS^lgNYOu¬=&OH1]5ˏ]Vu.7 / Hj)~%Z&c 1B!Ux$C/"̽DUKꖶK(L`bHӺO_?бZI/ /C eqi`obxƂdSoYc\$"JJK}^4FxaEлV^VdI}O Ɔ Ijz~AP%5-Ut8K:vxʝRS/C0V]UInC*8hbjϙvUJPWKTFnH%&yqk/LpOѓ SWt%>R"iƋu0^N؀uuXhfaJ 1f篠ߏ;bm%am9^ ܏?ŗJ{ǜ©y]]] /^4AhXv#6+֝ pbOs^eW/thr𢢢̙3izP^VvԩviZc\&ITPJOXWTWh!$JLL(Jbm\ g69~E`P*m xg^"E3-] ov xrڵNۨzH$W.^wMF:(nTUmB 'ޑV[Y Qy9R/Jg2***B/^x^r R^^ϘP#zϿ?ӧhZs0lzi̋l=]DٖUV ^HZ6AZ}Օ!QdUUu,]RP3*Jj1zPKVQ\VŸ fht5=|ODa~-\EYIVw{T gv՞-/A6Tc<&/i^wLp`gĦK<  %ā$If4Wk{e{_?WiW>kfFD3J` 1sZ>_ Y{ZF~l*ې_51mm@!e N/8E5(3ٕ>HykABLzbïܺ?T,\sc7,5uןt톥퓞Yae.ZTж{IaӿsKpKNJ{8 ^Zysy9_t4)߃jGҬ=HyM喍m8o٣k3QdE6OMT:)>rE\DfXXɛlp4rT8(&t:l 'viKr0t:<)RWaё.WL=VrƜ=-W7oq6}]R~dR D0j/.o_X\R)0YԡHO뢷6{~>N=ƾoZDvP0#?gDF%ʡojYqW|NLDHI7` V˵C'{ _`@Q{M =IJ'_1E=TB  &ƚo9kO-z5‚ ՚>zP@|ǥ-T13b$tڋ(-Y(&RXuu*{/ol!UG-[گYzst1=|onUj| ҡHbZgGG{9:>1ATVsU^a@)=M9 ׾]W:n2m&Ú7,L6vk 6pB_w>>h_zU({_dt(,^p QPF‘pmc Y9PJ?1oN&*cXSuL$v 'Q#F  jKjV81\ ~/v9f bs t(,t&FGF|^Tj:d4[IE.#PTQ/(*(3=^n=8ęݷDK82lP4AxūOt֡?͵_Q+Z^ܕFҡLf=1s|B zyݮ>h;HJAʚa >_P%. X.V׳Wv^p,lVFx6eѹǮIAa|oyqaG9EZ?8m~oZ=z&xK[No}=>h-Jh ;PW}(@J̬L&?EdߟرBޘi5"K#~O=)șg_s@sb (j4Eaϥ J9Їvut)@Q/ɐTx݃ʜB.A116fLzBR4x/}w~o u]l%OϻniSqn',Vۛo{ީ3m>QHRg ̑O(4A?SzGT~h` (O!ZkQpݔ%B*yP"-;YV8_[W?p//d4>kF\rO$&! *X9C( C+ &KBmKE#@!} yPHGv  }ܶքB "2|,FT} Dj[TMQ'PLM hW&E7$PviKr0t:<)j!V;:"`JN>ڋ(br;rGN>esj **%ɫ _E5u(j4o٥ɉu6{~>6PBNnED4aGrũxr )bz}:^2P$=Q><(jDE9~iIR=b( PC(j!?K0okP')Fnr]w\:6{¸1xq,@!M yPC(Li +_dO3.kҡbXj;9 s>_|+/ZٮnyeT_~䓏~jҔkbbׇ;7øP hu\P JT>c^Q;Vˬ AũD#aPH- PHd EfE~}:v~3Gz.\dOl8뛗˛.[ÿuՑxrVw^y5~wE;;۫[tCŁp=:JS_A{?#@o#h^f9CrhhE+J$ۋDYB)n4(Jv'VPx,j`dµoDnŕxI}pfMo3#aȦnc f2ϲ2[`KO*BV<,PHnT$aгמo"cḪ}}YCFu}qٚ[zG~hxx+h8{c85ͼü#w yB&_PTNQx(PTelX}J?ݎ$> F  jKjV81\ ~/v9f'B :No0ػzϟ_߫ƼEd0g<%yQc569|nmk[2FRMt z=<ϨɈikm )Sp~@!N7YWw{["ڎ%gEf g #?6.^}ϰ?hzZzP6bERWn]{*fh\vIϬ03ZTж{IaӿsKpKNJ{8 ^Z9sxS/("3Q\t=+/~ejb>femwoSX{ylE4/ :Îr )@!B=|K }7=pƖ[6~eD|EIJg4`0קpZZZ.b( +x+yF#Qk9{IA! WbgeJ?vІ7&EZ H=^EGv{^13*Z31)īnz׾VPDaǒ,a^D] wJ S` (2*y!>uJ}5݅S3Ͼ PPϧ٘-esoۇѯ48(4!HɌjYqW|NLʏn82?DLk/‘O=k@1)(0YE5)vOvh,?o9kO-z5‚ 2 ՚6l2zP@Rs<.ΠhIA#P$އڋ(.>&_p H?>sdA-=0PS(Vڷֶvв=}䍻N.-TFEQ)ݖ.Ōڂ% <ܺd4=?0{,QP`PHd N@QOɆ~ɫ$ͯ(꠲Z 1]Хi*upw*@,W no`0qe{1S)R~ ()E+YmT!NHyP?fP5}d2ChDM*%$I{`(脛s̟?HL Ք_`@Q{M5 ^胁 zUK,Ĥ EgG{{[ĨszG W=GMX3o~[kkkK^@QV A1UmNOLWnIԃ@QwBkfa4J)èM7{@Qj@!K6ertZ"E.>MsSx\.>FNg2';E݄‰a~`ȈV9#JcFlU(ДDEY/֚BlTujKcjm@#%Na|(l2ks[{b7>]C}:bwJcd|*##yPZS"|H^_1E=$%hvn?ZYxYK8ѩ7wn}e3tF I* PS}(@QMzeU:x>M\PH"-:? ?O$(g(?lz)Hbk2jm4a2 ͊zlD"V /w (C! KjW뻎m^EZ}ZGY}'ǚX|_7|~kdl_<{7~k7j/mS%;[ ҥ'EJh5Ɯ1'EvPL yPX[O?c<̳߰;w~pMӺG6>4-`˯aXϜsIΧ[yvetpc/kϾئt(Ҋú%{u0~,{=ȶi7/=fUo0"QM^@8~bjٲ8v$FrYZ9FP(Rf (&<(B\1Όch" <6E(J{Ks"0*w)@b3LNUձۧt:HUxz#jl|πoǥm5EgFM;3)zGE>(C!aZftō_i"˴@ϾKUt ,6[o{ީ/C1T3h&PnbkB b>` #l 7i}un1S5I:GGB q% 4SO?vІ7&EZZHb4ɜ؛| Q@mAnG 8hRVmR/"-CEQ@x} )w?@kiu:f*y/Jx<Hbx R_E4U(X $I-$i= A)v(4[&1O?(Z\~Ay&vW$hu\P JT>c^Q;VˌTJ~(b BZ(L!hP|77ϞMʏ?T<޹sg̜hn.vPMe)8j1={t* e*O>9p(7nfAA43GZx_ rUWd{(5+0?P;m{vt)@Q7M Ne`Xt⮙_\˞hR5 Ra5iubE}ćB]Ƣ D={?X/ 2"#]]{?{lby\fn3 >CCm]Fé_o 89sB 'vn"ZDbʿ`(w ?Wy7`4ZhD ƀϋ1pk[ۚ5X,l*ې_o&#:% )S` ($Pdd`JRr`E6Kl[^ky;ٻf;ڽ3ݰ4}3+%̦mF#a݁ v9) q^|.`;x2%\0{9k}szfVB]@!MR\2GҬ=HyM喍m8o٣k3QdE6OQBM' ''\ .X" pXɛlp4r2 R@ q]+Ws$# ɳD+>K\ʂEU foNё.WL=^8TPFīnz׾V=YH7]è" lJUtsE (R(r>3ϥl7JΙzRS4U53gVzH߁pHYtRꚥְwSe a(:(\dQ3ڧl1m~"J!0}ޜ= dF6e _M8i*f01O$BJc2^ G>ٳo֬3:5PHB~@!._cdzw]3MF}sEz?۔T@ TlUd];?@Q&SD(̝|wP hׇsՇEY i4~B([NeS .^a_:d4zP@R"gZgPkR~Π*jP( 'j/r̢E%N ҡ_E2)0PC TE#==[[[ZmY uZ{E?5YUeb-wdzȅ(Ǣc\1}f׮gٰzQ`( $٧zeu{cw\`;,ڋ(].;[ 9L(L*%0KyuɊi9zQD)݃ҡ_E2) U"?אg*HY/, o!#TAr NsF" 4\"2gj]㢭-v{.RV yw.FM㞨DjTL1fQ*CCh\EJE xq˖&{zBZ PUB@gnN?Z cO95+/'' 2Qq* rVeww-㑗 tLr3w2g 'pJ'衇3^qe΅̲w&{Y`jxu }wЈspx8IR$$``(}A7wJ,33PHB~@!EvQzi9{r8=/BNnf\T󯖎<~UOO'+((@9{P첵%ΖRkY'wY_BmY=(?A=(QYr~OS0=ZtJF (#;0XO3S63B;VU*#}Sh.ҍ3{vE)>7\E ҖY.RAzOOYuz果4IBӾ?Xq<=Tct̯c>|+nٻ\gNpIWRnqK.VOIUO ٔEJ.=uL)iAU)x~`L; UJHnxF~PAn/W?M> Ze'J5YLCC@x/VqV>^'QCN*gw?̯ϹW.ߎHϣ(o~omy)8W}(0YEa*̷SBM(4:|,ERR_PBf/ӂרձhTd @=\Փg={g oz{Gʦ>?-K-mc A_-D1Ne&8!PH"m19>}??;y}[_<ۖbotj<嶋n~mѭv<̹^cLϡir!޲>d&P lf"9^QG.\hiSt(*4MV?$Hl`_lO7HrPET"bbda EBNd#͹@!A*T2PNe/ һK(MUEvs;4s/{c~xI8G,)fh2xTRzz1RDP&)gPhN a8>2;=sNhiU'vv6 "GDtx_ _~&Y/f'r!brطw՚54X W\u(cHd`n([Y(gā_SNr!!+m t< KDxw,tϚǻ`rcf)7 ]vŕZ67`\s!f3k2r6w9GRrE!?0Ѫ;myeVE;v_T Qd,6SޢF F#mG+t $m6بzbã##ík.\Q -'_{Gݻ=nwkKsyFֶfejh&ȒƟ( B]MT)F[ZzGF#-Z.G|CuiM2{֬z*Iq%k@MR/*EERrźWcшu:O=<y]Vs$aN7N07>-ont:/ZXo"-, (-hJGn~!-EVĭ76ykc> K7>zfN2vT扐;"b?ON>o^T(wq r Ջh4{_>'>sڴbU? u-z(HRSSpJ!%%h8Brh`_߱B/Z@p P31sj-lbŊȔJ9TDzG himu\@呝) 5X&b9F#(\8κQ<#<)-[O_I͠]u @p `e'2V(OQ(Xz}Q٬Vħ}9mdlEQ^9r[( :m,{pςR1}K/sJg;v EY}C L>SjOj>֋+y_T)aA@!e2^8>tǫbVQe┒h$ ŎG,O޽WN Y@RYZRP [$9{޾!!PH"ɩY cHNv̮vTj;9oޝ;wzٳaF(y A)E=Q:d +b؄PԌeKl`P PxnM5K.nɱ/w>!PHUH/98PFJRɈd(#;|X(hoL Uk>;@^_N!7ش7 Z-Ix*%@JKL8.:eg)[RTTV\ _^TEՙ(HwrC@!U &(zzK&tv8DƘ?wnۼ|C+hklӵW~#;EPWzevBA DEYE ggBUaY@$_ܹJ~1d*hW(Lbܞ).Rm2R|:~3aeo~+4!0EGba(u.(*/dG}_>b;5/~T[Ci+UP4"+-6dG`80m΀klcmS$f1B1EB\ r (J"S_dI}ǀB,2^ sR1Irp Y'K(Hba(S,@ Aʌ PH ;qT XJe!)X@Q/UBjٕ l Z"Po㍤_YԮLXZة2EnZ>!PH";OV#D@0 [FbbSS,HA2PTlS/,NJ٨2y}uьad ۣ*Sݦ#܊YLUSL]FF ( 託B>т٫L#BBIIRS, 6JGҦ_T|%U8@Q%ś۷L,E_,Uҥ5][d="QDߩ [W}B(aS)dVt>B;ң_䦭"W]:@!ľZgKuы)UQ}BIIRS,@_ "$ RL(f%$5W{Xe7Jf6i %%IQL $}.jTb$xo,Baua>R;_䥥i'Sйi(Cr=]06kǢaS,@ AȔZ B`pa.UٓHp0v /I$Nr9֢4/"wЋ#a&EYYLPL(+Av @QQLe(h\eE)rfnłM0}ٿ^i+N(0bj4ʢve?\ '_yKz]bIP%W(TYTSR~h;Y8Q$+BBIIRS,@_T\T (Ģ 圤J *-z) 2VP ` Eʔe,_hE)flEd{ HrDWa*zju7cr6Hl'[0z)RKHU@QL9"K;S_~%&QQԌ{rvXxkO_"bSS,@ AJb3oh4f`0pW}> 5ދ.h1%T=) 5/(& "gg>d}0p|`I83y DD7@ 0;r\2PTnGJ H_Q#$))U\EzdO~QETXuZ^ (xY@~; |>% DL6H Eʔe,_Pi3zNt5D\E Tf8帕J+3y Š(+J!] SeܴkQt BbtF ˱ %%IQLyPH9{ xޚbK!] Se &l(ɭ=fJŒŔ* I+P#{ )Ḃ]dv!a)I!] Sej tl$uҀ@ fjt($CQQ`KRS,&cRΞĹ@SY\}mM+AQV|7B~eQ2c(ja2@ti;BփQs,BBIIRS,&cRΞĹ@SY.bʔPb$,x)W+S>v* ~@6s̲;X!a)I!bQ՝],;@Q˦~@Ag:jru(xY@~0/ @Jg'o(B\Ų՟l@ti\0/ :($Jd/IX@*P%EH()IbJ< PA CHTPPCfˉo+ڕ)@Q ;Y(`MЋ!!P4"ۘAf@ Ф JB(ֹ2LREM(0t F#J!] Se*ɁBlTfW-lCRe!DuД QrZHxP)TYRS0]=$))^ PA/(2U_(jJGRe!)X@Q/TB:iI*&&Ǵ DL6H Eʔe,_&,CU'Sec3ė>iٜ(ƧNDZة2PNe/(fny ^ a敮>a*J!] Se :`=zHRS,&cRΞ(2(HRS,&cRΞĹ@SY!IQL $+6v=Vk[$EHX,'bFRȯ,ǗeԎ(^jK(HAg5c փ@ HXl#!PH" |!` Mלr"o ʢve2PNe/ YO g#E_c ʱ( %%IQLyPH9{MeA=DX\}%JRS,&cRΞ(2CeR;Ң_E5eI| KMU[!9VIˉk+ڕTcB~Q89P*%&Ӆ tjˉo+ڕ)@Q ;Y LZMWSZґ(TYeLzdO~@0zE_IbTP LA$E1b29_@!I 4dV|!ex#)W+S>v* ~O!%sBPBP6pf}b.by6tB1F}!xgvq'YLbPȯ, :`"Q` _^U yx*"PHiR]fM=)2drB.>9n67g&8aEyYLPȯ,f:F#9<9p IB~@!E[j3x!MGW5eW!JBA D_YּFD:U?]S0T sG'LQX$ ¡h[= Jf\>nlla< (F!\eE)2O .qVi|k((TYL ep3P1=q5vA I^\ H==~Vj G4.Y ``ƴi&!@@@r(G_f}i^o/^jGE{f0*(%@!(ՔH!d=&W!B @K+ޛ7JY9E*==s&.o}VWۗ{U9G{_9o٣k?{5<`=2[>:ˇWi6(Jg={Dƶq_@Gr_E)EBx?M+VcH$x~9ϼwYggߺ*;`:m8_yэxr5!ɤj+dYD)zn=ϯUcm!<(>xƒFgsǶx(u%澌 >{7ɚxf# sO9sg|S&| ~~ֵ=b{aorUuןt톥퓞YQx},(@\S;:Ղx}/" > }ȝZ࿮7;=.]0 ?k3NסĀB,U$GҬ=HyM喍mHwە (x# yP H(bm[ڨBQBʠN؏:}혎Nzl&v2OqtcPnx<^:PH":;%龧Q',.s(@Eoimc5 mh8lېmAkfyW/"˞7|2)PHBcEk=%r|$7 yPPT(zZO ĔO<|7.ٙAkn:񂫏%LNo[qG[/S2bRhz/K]v<{ՈTdH! #:cd6[&{{W\6/~Ȗ޵i]{Nxx<ۿ3Ęl4e|7eu{cw\`dI!Lqa.zZMsddbl;ǵR*hE  >  > LϛJZYNd~*T@S@ (B\MAӏ](DPHI@!t TV3ٻ$!Pؚ,vh&J cX8 ~}^ZbyPHG@!tQ (+G/Jenjioأ?>ZsENo4VuO:<PHG@!tT+%9at=g|ӻ4Uq虿h;+{|E/Ѥ3Zp (((@1(tuxݯ]y~=(N-7SA[dVk鞿 E1sa' jdBA { 'Eu]X48=Sz}~Za_x/2=b؞e`{Z 6~k@4w6 f԰FTF5t9G(5  Yz] EK4GkAEq au#O[c\tӒp˷䝁 ȘF|YrAۖvb8NF=W7*3/ Xx@W9F8 %Vb' R]w Q G0R-h%t%(9>qYpZU(Ê#P*J*3 tcHΠX8,f(((bPڨz}hئ?}>vmnj.ō/@l$B R ! A> Е;.糚*Z(((Q;D)FSU-cmՁ%GItsʽmn 8IRgP)*NSMPsTjleT>XV A*lJcP(tՔLv=wT¾;?]ħه '3փ F / x,@8vc(v%q*RA1?@@RMd(h@Hu[}/mkBL3|9: ǎ0AuσQ P>$(ȠEWtFyϸmɜK(TA1n8h9Q) @!(ĕ:.Ee?aSՒJѷ?Ȏ/Sq‘9܀] *I@@@!`3W8DMIrT,&E8;05>kqWW$Wx8\ߩ(((G)Dւb&S8*2̸A}"N[`Re ?ԯ5ZAl >9#?L0áTrՙH$,#L<'*%[a ,T'bx@@@SKxhgً᥉o9`0t8Ԡ?T9 J)9.~x)'\;/ @@@!_htB/UZZQys砷~Wo4y.fIŃ& Oʋ%k.me=xdm&5ͼ#{{BVzڳM_@dl @)̙}> Z`4bo:.[7]\^Q &V㶃>jUQ\cNkqO<ןUUh8l z=ܘCTAmVzn=ϯUc" _P,1a2e(sX e랰i}SӫZuOOM<)M!,E(&%/" ENjVDǀ3F@,~Gtf>^*R%04eFH4:c4B=|K }7=pƖ[6~eD<SB~!Y .EsgL}[@6**EVCʠN_Q䰤riAgx,FG-*Kb 7z#ϡX@@d nstv+v-+ LN~!ApERTS"RVQ51bo?ihGaۆl ZSEK=1' 45[1:fd "0?(t>  G>ٳ(& Ekl1m~"Gg=J (_PC}UET^V䓦KO<|7.ٙ]v8~uJB9!!,@t:g糍6>ps8ag1Q$!$ U89tޞڝ%zj߷~=]~;"ՙMK%u:.d0$#!&.lv:'_j\?ԈX"Q; OӁD QrP}g>9~lB)X@)a]H!: k۳͓oءow;w3G~$}jQ?G?NO$N$KT Y])N>u j1f@ QlBBleSTʾA #B߲Vxug˪Qՠ l2>`pHPŒӑ('";7)’RfsTm(Tz%^A(&PbURf?DH 沲(.ˆ2"02WknHQ_RfsTM( O򼍘;w3v͛OjV\Q3!ׯ\RfKp\I̴){b&Qr_mYRu?QHEQ!Ei1GՆDXڏ^re@`<> ") r$ZU\ b.|#{I61~)ꋢ6BcEzC+Fmm/_zɒ%.7ڰfDyh )JN:.SᔙI1h#,f+,!REmٯU &5:&z]WC:~ȯV:>|j1SD0R ˶e*C 9FCBlR{e"[)ꋢ6Bc 1|W.q.Y|O~ruܴr1vT>Ra B)uZd nBX^+D!REm%ۯU e]zzEsFq_g WwO75"Bʠб:zϒc q9ANu[F _)rʒ96d{&ze.aM. Ģi) -k׮,˖i͠XȥlYr1L _Syшe@"AidCj^ߡ#z9ZIQ_)i ~E9)P=%@Q5dǻqmO|}+P?%f4U"[~ˆ/з^ٲ*5rT5(het[bXIyv9x_ܦBleSTʾA #B߲Vxug˪QUPrDa6~ 8y>EIL# )2" 0 )-W܋A_Rd96Ter! RNj5(j#0x BP(T0I]Gdd 68N{+ER[}_Ho^}!zU bd@>D"}R65oI>Wk\E8t ZR':FlB @ @ 0]6LZ@0:9~ǠЇ]G'uvzLfΊFÑH8F!I9OB #B߲VHaR[֪)z^ǏGփxw:7 aɂ@haOǂ)Є>K#׿  ;`,@*ǎ_i?`0cda>/X8nssbƹ8zfHv6p/@ @ @ PhU=(#8tc\'xH $E>-ࡺ+Jw< `|>{S}ݜM$B\l1)))< S[G0v޽$8q~KPgT,WHTN_Bv֭PsS֭ J&23 F͈ 2N˹$>xp ^P"ѰQ!խ i.)))4">XfAwzR45#QgGe8ۗH$O˟yz4x©#ȱ{á(:dZˢ$%&)qIXR R R y(P';ݤdhTs?H?TVR |AMQg>Cp8 )V1ZƊ*?uDFUݪ52jdj=#R R R&Ee]vmڴ>Kwu`Awٍ!Gڳg췷*%ENU`縸)~Dd%.?%U+fV@ @ @ Pj|8}~… !oIu*sòF :ty=8@X,U1:"0gL9"c7Hsq9&9QJ$SM.!R R R &o< BPz&zu޽ԬeSg9%[9͖/զ$ k2E5Q(}Zc[抢y뒲$MrҡU"z='N|DQB=ә{pAwS6\6! z*`9.Ն[R^rB }iYQVekQE~r46狓*sW%odߡ>JHhZc>ȩEs5reGt'X`FjNAH,WS'6JW4ARzY|9I/~P\}Umc[!Gz{TUQWw:#=GKI|9VQtp^#kjsΙc_˧MeE}Mm)Qt6lx!BsSgΣƏ$ͫneڐ={3w}_~|koW,_tl6Nf͚vLr-=د ?׀NQޣ 뮹`XOrҡ_R3{BR^S?k)_?ʛ4{KKo$̰ՀvzG<6l .N(Bi<ѕH7񂫻4E+/+? y7/~L? <=22|Cn~,1l* Ƒ^(,pFcۆ{i=kI%/kik ϰ:rxʴi}}idR?w^cs jhh8_e!D6]hyd .[~b1{8]ȩS]M/]:xH) -k׮Բ){{+CdYYy?cp8(޳}峯=veK,>\ įK¤kUNǛJUa:QLw=xHM߿kǎqMԓOEڹ}[ f)/l /ljdLm!݁v[bƴ~Pב^]=M-jO`lK/22sU)ܶr2B{,#9),p UwlZR*(ikz .eN~βyp:, qcL=r/>NޗBn LkOqmO|}+Pd^5(*%/з5(q0G[֪sTqzѳ NGKssuuxzNO/a ;f7tt$-;nW9ǭu:-MMˡe(*%/зe(,p0G[֪.sTC,VpeͶ,~N^AQ_Ho^t|Y纨=o9Ërv=HRWI-( # RKmQՖu~ kPT5G_x< BPnV![68[HQHŽq k\HQm)M㈢hs8;:':xQ1HU"<~KLj")#EA8Sm)ʼncGD)R E(%YDápB"+$ؖV)j)ɓ#᠙)85 G7拖%):a7_(O44z ^y$$)xCі>5 ,F{|>KxEQb)f;j{_?/Yf^#@3F."(2)j'E#IR E)44(r%_[G^;m?m0SB[yA PM+0q] RTPX7T0N^dzƉnzOvW͸Ż?pt?0<)*+kPft˪'C\;TSopFl~g/_Ȃ>_g i^HQYYw7_FXWK $Y`xHVEY}A_sȚj )LBXPA SA W[qs̿bof|%@{~ՄAAP1,@a)T!Ee ^*'+Wxw=ͼ;mR QJWSHa )#.z/\2eÞ(gY\k?k?(*sޗ~4OL~R }v=^BՖ =Hau)!aPZDQY3,vny hYioܸq$eYW,RV.bۅ zcfhz{R)q] E-e= f$p';FgT7p791cP%ulzt!(U)f7i#f]ष m3)LNa)RT,I2ŻqmOXvM06ay=moҧ)-} k\HQKY"t!Yg)tW:昉_"*nnw BL]l˻ ۚROpVXBP3\OD.v?~"$P( *EhTn߱e˶^uDog;yP( ʩt=4ӄϝ%7g$Q( :W,[f=;KHz=o}|1lxlCo2s( BL.i {h3IENDB`muse-4.0.0/src/doc/html/single/documentation/mixer_with_one_input.png000066400000000000000000000752001404105311000260540ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1 tEXtTitleMixer A" IDATxw?ھ{۾slLhC L@!!R~Tɛ Ho-;T1.wWJcW;o43j<3̬?'mذ*Nn9 /}rœ+DIiK1H:ù" ^gVuu1ͮ7}Bڳ<P[ȫmͪvrkpɶ&qJh ̦EQ4f lIOUBHQ$„qiwt yiL<[$/*7ۀm ɔIQ S3݇Sʮ4]])JL@*$S{$R;[Pc܂e.Q; Ӌ,8NB}}}:VE[k{afaV[+[ZZ.Vi B/Z_:Aq#d A"(B@oo_:30Ҷ{{eG!8r3"a;Boo/t!a3p9]R~U֐[Kl֍Z[7hьp^(݋ge! bttt0aB-&ON"x@mmmljcfƂIDpl :H-)yw#HDI>BjuZhPsUNwqts(mmTUV0ٶLyE+1θq㨫hPnZillmnXNj(ȲŽ;(--rpqH80UOiv6U2eoշzz ;?ۺjwV^~>>]s /ݥH8BΝL>b"(pH8R\R”hll" kKiEиsgTnb:dۤ/Su@D%Gw""_:ZPDFLBwn(., ^bv{Dhhh`: mQq1&NH8eHƦFNܔ2mtvDLnP&#v60s,JJ m1sl[MHja֬^x_~nyz&W{<ނRxi( ,,F2ML:!  DF#! !ISNeG\5v C0QːNQhy+Oy3%D͟HK6'^CYfTm2HeT+lPv߇auiksϿ] .;gr z}׭]khѭ.7&>$PP+ Kgi@ i::K`jDN;4$o3!;g.o`2mgL"2--TTVF=}TVVڬ1e׶Z1s0rd(Yћ \?fRB;嘽Ag\&2U}mrEDz̙;l{8!7Ugp™̝5/^*B%"GrG](i $msNv]͛϶dY6yYimm"<補sםwf*Z4& ۭTVVm_n:o [oW\]]]Ck[+z])0MIoP9s;g7gݺu,&8χo~wfZgم& gj[lD^:DI]}"L  5k?"CzZBTY ֊LoeYw BDw-5Xaɕ1+G[y?pn7qu;<=?j ٲT^4o.H׷Aڟsw]o.'"/.c.Դ2]g{`Whޞ.et϶%]P"+t}ox#<sOV ʍ9\թٰa8,_|b.\;J(-9׃[(T;p9Cw3^*FQh$L4:~m"ш/DD#]ytq5nUM*)dUBttfqwbݿg3ݹ{"S+5w'6R͏y# v,K9gwM{y1GVʦP"-(`B_~@Y-.b~^h'ʯ*$Ϡrg8Ţ)*.%m\~DcQڬgF>[A4A~n6.8>|!8h4,.]]DB, 4662PttilqYe;, ա$2E[B !P`9`9|y35vG"ʼnbYՅ}'_Lj%3OkZZ, 0!,1q*3/B ^C/Sqw8~x<> ,&PG VU <m0pfl=09f1g`WoJJJ&.u}vg?[wimλ06RZ\=?ڎLˢYF"\u,{I6}9W\~yLLj%fe-9a#gv~BOš;wo`ݺ̨ V]) ]n|?;Q9}[7e)BSS~ǭ{X;T]a4 FFLܩV*}Jd@ vQv" SQg+P!}&KHIb~˟D?7oIkB AZ?z7aa,{otvˬz5c). ˍ(ygSvغ39λٱ} ]V3'x<|~m9k_eo%ذ?8[Z?n۴Xn:w}7+W~}>Dyy>[>Ҷ DxG3k6֭O4=,:Lj&3ϓpPVZp:]9GRd횵p8t22W ET"-:u0"ԯ\ŧ@pə d:/ׯ>]})l /^yōxmVvl[ʏ7%^GRfZ8<[|0ϡg\i\]8N*+x'x~xJ tBݪ,ﴧZLzV_dؚe MzxR͉k90vb"3fϳfMfnβG;_QLZx>D$q^/X"zp<Ĺ.;1JhDF∅*߉ADDi6߇#G(]j+,l߶>ܹsL5,އ=+i;.S+>SnVYh!)٭WLvg'P1o9sR;|f'켃f'Kp%_b_ᠣNCV_"NÎ455&&\S3nuTVrMD5"Kd4+o3eV25^jN*!DvX(/V\X4J[{;ill+H4vQ\\̸q㩭KLu.q8mmm7Գ`X<墨qjP[KyyJ\FWv8mhhn8n"jjj02\.W?)q1pw[`B?]Z3Hr)S .~?=L!N&F:OFRTO!X8i!!ҢK)Tq"TC-K?5yzJ kV˥;3(T +gչRNJ_(x.~BnwW2wdC}r %t(((Nno*L )s*w*~BěΗ>|<\ve[ÿKm Ț?tĽ«?fa7g매8LX7Rga2AL4Mt"@QÄ5"uMI:Svq+5{R) O8&xq88ßF+;x;M'w>cG_uao|gy;.*?e]nJ.a7龜5yqT?w%W31Z6G_sf5@sدE'iK ..V}0_g'o]\w ;6Bi|%' A}>>2x̏筏"Ѭֶۘ*?.yuU]B6rN"<m"*HVbH:~W Rk41ܯ_$/S|{ë[]='{pQ fvt0OcQ|k|l1_ƺV%!ZZW(ݏ' 1Ә׾ g5yw p簯cO}ݬywQOc'P]G:>y2@UfTDP9EN YbS]֟~:}0q LtAJS": *t"DӋP^KKo 'ߚRL$%##zS+wA!w5V kj"ұF"ɭtP'4ERGX_sӸ_eva󍧴6_Q4vj~ G횺L{G^Ȕ=R"TθNVj}h{WTua.wf'|MU m j IDAT -8ଊXXYwbaev&uQDuK/ᴒҿ&zqB-x?'Z(P>& qUl|) 25qop>5 IJ_d[,JOD8 >p,}I ^ zsb? m$R8j8khfny_%iRD:f4CCT6s]RH>ZǦcE!XTusм}]Dϡ>L%KS"_8W¤ص"3-ɾ{ݿ?S2tW9xќ)oߜa!P gp̅2[d˯kPQ9.o~jjoGMMroq p߾˩Q@qRYx Dfբ%x#\ɷl2k? _ΣHA+/qst_څlav's=,RDm叿8h]р͇o>zq x~2?!gG[8kB']:"0 j39@^;fw&g0t`k 7}^R8G5(F_cWL:E>iG t^c+ +٫|6$SN/)6' +h,7]]I8 㸓kʽK(Ug8EeI([[\39^/Aส ilw)bH=%;TOayFBܻnth?6>n7mo݂n:2xs_AOo%tor0x7""r!ƣnf_"?}#Ղza1^߭4G7iO~cOw;b]֔ oO;.xp]l} ύ1}U嚋z*eAyaء@me1^6AsUpIxUs{i R?^y(wޔt#@@m묧[WA©߸n{29=I.'%L5mqbB7B{jYT[;`)s>o=R7HTqzyC#ޙ̺5WPx̯_ϺTGA4;Tx^pV87H 9Fjx^<HX&|ReRN)^u\s|wppD/uM܋UK?Rq?ob7nkzcf13\X!gz(Uۖm+ɧ"ЧU+%3RcW0k~vGqԢ)@n_8{ mf{OW?5KJypK-f޷doY L|lY;n;hjJ~0˾`^MK5o=MrH LFr* 5!^m$;65 Ԫ[/ZJ1Cl[CwQ#G7d}}̸io|ϮTwDW!)*r+9LoEx اɶDܟ{nwxwoTe}d=׿%޵,x{VBr]iy*ʜyDaZJibqܕ=LUl!% bPy\bN$ǓO =qG/XT"6!0N 'G~aGTiæ8 :PRTgo/Ea2 M|*Kj+z@r: h\>7.uWQ&𻑐jnfrEr8=b7xgW_ˈT]%d||:iOo<#1X#;JϥmyTy35ȑn+`Po&:g1RNiWc WWlJ(,d}%G{=x/zލs[mrrB/E=g8fcOf[W=R W o렙"ί*Uhg% c< VRiic;ά*D fgA> &Q5 ɼ3!9%UhYK 1Oac=TA kx2^:#o4}SJ=8]=!vl,R<"o|9>Nc-y5G-0,M%[XDT1smPb nAz2kWsn)B 6SOJzn6v=,kԶl௛B)xܰl;!}Mqªx/64֖lEQ{/"ԯ],Y/un EC<%W3 0L)XG\w+w7jsιsx>7QUDwy띗؂|~$το{wz>iWO}}@$Fߦf>:DpEFVTFCaZSB3]8iZ"b?`h$C(B4AP Jl$$'G2R*cAm@ j7oE)uUQ$qD?W}&eLP"x kǽ[ާEh Vixwk[7j+*'zFBz8b +-U{Qz7QL8w_-䕇/_YBWw|/p+\r.aQx 7„'uUs"?ŻXqI \D۾û3k;Ǣt^ ' E P@K97/ r&6^)kq_Lgx!ӎyj&8a\!~G 1-|rZ*^IͦH_õ+i{3bd"cjz YEE8Dw0G^>{AH>!*fF-,WG],=WB4ud"D :ճ6D$pQYNP@)1'UWtXx p ''b0;xUgv]5[a8w@Ĉ'_SͻNN8FBX"!E!B`@Y :o9CɤIiߙ,1I8  ' sʇӳ1ٵrTI5CΞޗW>?<(gO)?M[X$N: 7*ԝP+Pt'o2Z6׍dDAQr)xtoR>00xMOA$%0TM>`<#X0?\u)kvIV̯˄DkZ*ӿWƎ'i Jq2Bo89|H) ) +ڪI5ui'_\rwwUv8 #0WoOޓMwxX,/3V(o?V ]>ùj$grG(zp=?̍=UU7tDdfG w6l|kZV{G;׭/{RG_HC7o>ee66RE[nPp(^^yjB$pP[Wñg.Eh<ƶ[kœ]i.!nҤ=پmV7P#>RHɡ<_LJӑXr f5Ápt8ML oXCψ#w0 ]yl0`GE'Pn'qaFo)Y͇6Z$l}g-Bye4}<9f6==+Y>镈vWri_YJ'Lk}ߏ{+;^-]9wD<^cf|e)Go |haDRRy:NNM;88T8G6 ;@cpt] %K߱ͽD58av+}`^㩘EjW#Di]OL)p&c]DP[7yrnw_;RX N~\N7e^[H5HNq75֍[y-l>w. =WP5u _Y\-S$GB!´uqL+/p2n\ϼegGV]E9Nl>$EePddYNrrYF6ci Ӌpp2͛˂m|0|ʑ--ݰC~]Ex'ru_[2Tvg 8D؄qRV?}wDw"-FȲ~ -l>Hi*nH 59.be!\L9\(ѧJ݄]uݠ`͇&Hmx 58|i>Ji@zaıAu|ha@JR.@~x<ΖI.>԰b,ՀFiI)FP:;;C6JJm>4]|>4Bt5aE_(D_C ۳6F"~KJ~(tuu|;;n%%cEe{yCWQ^A}_}^|WTW/6*$! %C -v-l>vH`, wB6c\39"%Z|ha󑄽eD2 j|haEzW"’pyf0_߱Gy{ˀAC -,$<-l>{Q{I6Z|$*e}^͇6) r6 |haEr.e@a͇m̺)!1$\ dT3D^ --^"͇ 6ݳ6Fn%[h?azI, ۓ[d`oE|׏!8V[hĊj%Z|ha@o, -l>^͇6Z8 pEiX.J6))>rL汰$<|haE%Z|ha`׃K=l>F^͇6)fr{I6Z|`o0$C}hu͇c\svS{qL, 6Z|hw^"͇Z=h/C {ˀ$>*e98c6ZwJv'- 2@%[h/4ceIU|ha󡅽e aGBe@>{Kµ#w%Z|haEޝJ{I͇͇u$?4c3ƘYn6Z|$ ԳyuXYnur͇c\}ހ %7y|ɡf밗ka͇ykaGmi/ C -cX -l>ƣ6Z|hau&ĒpaME0e\.fΘF}CD~~|hcǎ=z|`yoEu53OrntcL2~|haf&x:erHx)x}8]N+Ʒy ry'nO͇6H¥H/Cw=fYp\\d@"N(,ar3G͇#T)kMXgg'>^ ,Z1$ =k%%Yb>L7|UrYu$((a؂ߞ,ttvv ۽SFAIQMlßR\ gq'd3FtzY!ʀ~>f Bc LPCO&fsK K?vQxQMj}u ?Rx' ]O3_EQDpa]2R吟pEsh}|Ϲr7_cbc|LJf/W_zit;d IDATX.\a\C)"¦oz!92S;"JfoOWVGvm>lIy>I.#2N'eɧZr0GyS]NmuFN&V݆|?ɚh_H{D^֖}-l>=gӡGI.dY6||G o_oCe:IBB{oExs^@a-_NAA!bGj F-`B,y'N[okniꯇ,Nx,$~͛7̓OϾRUYe%yW^~IL?tִ͇6ȼx,륪4< GF#ĺ:v44s P;61[ l>|,D8p8ޭ"STTT;|h1I,e7cֆh $IJ=У>T&VsH39 /# ca8"#Azxvv2c1bXxos M.ؼڲj/>IAog/bۍ͚=lF 81=X~4vҖ6m$>r"&OFގp]b0~7a͇dkf`vF>҃#l0β;}#+PYUjW`P-F P"&6BAeZ -+i0l>,|+1d߆>]NBQQEST\DQQ1OQzC`0H8MO_`7 Ȳ?~ m>l>r'C\|9G,[< ja͇6Zh?4[@Kk jܶZ̧lǓF5ᴕml> 1B0tL!*En]b YW?Lbv=vl>-|軈.q"t͡_ vGc"˝M+4a$ÚݑG/* btvl>e|A4|.@-l>oax(..6XTMww`0{shaaL?͇$`8o<ǡ9Kaa#㗚@Jyo8(49Lm=X|X;3JsB`#HaU/͇݃%#\ K-UVVZ/^fm>0FR`_q([6L4 -|haG"`ǏQTTUi7@wwzz ñEtw'ݜq܋ٲȇHY۹AxpxYch4WJ[V>p|X9k&Ǯ@Sq?KrѰ]b1>4AikwCGtͥ z\iݡe%͇u#,{s%)#g6P*CikwCGj&Lj"wl>|f~i lKahce?1E4l859Lae%͇U#KO ^T aU͇6R ELp=ᰕCse)w@z~Q7Z%W0Tp6m$>.bvl> |ѣ.pm7w%l> ||9:8r|Xe9O%Qđ4C N[ 6Z>";  Cl-FX.ݹy*9 *blYIaaCNqSl#* ؠj elYI@aaHB1]F\ц= ARE fJ6G6M؈kFÊ] -F.j~sL;}ns$ad/ 0HlTf=iUe%͇e#MXz0vl>m|س-]bz7o)#I{bfEacD<CWs\ꢲ(sss;:~TRJw2ϼ2IW>yy<>gi6:ɲlB:el)<>1K պFVz3tߧ iҫD"Uȴ>mx|8NpìNEက܂G9f-׋86ߧi6-7UJ#r 5|̃MAIxp MoN%ֻ j7j2FyDŽJ.|nqq#r m|LҋXD)<25ߧiP-W@'^ڛ7ٲum43F11YBA2u&ζ(l8p[(l`r m|fӷ<~TɾFz33nRZN|6<>|TCuEDBSQ3%p [´lAȴ?x̖ (M됅5n]Xϴn)iULKsz\RH8;S5:5BDAw}iUG9<>tG.؈9tI7t!ݵsHph/?Ǭ[Դ(vrbtv_E\*l|  퀹q/ ~3zA7qPM0e rD"ɖ[y '1]gayI.>hQ$_y|c"rf;x'j*Bp3L8Bo׮eQ{=XYI 3N lr$M7N#l qzQlpWV47vJ@QL5TNAWpwM4уgS^{M]Gb1fr|TSBɡ]I-$gOp߆Eξ?"mchyy BǮy/w=o 8Eld7dپ6 om:Fh<˩7__L>ʛFy.?ؽ~i5r2>juӗP1 )Ǧ~kKOpYI4j&%>DGu@2gq`͟?n8:{~wS;'2Oczx|$ >XyO7>DŽ^4LR/?E| JmGym_$_BD=Bn9.<>qQ28(J_~n$'m\a,oOz^}8%?t1"H_/×y9\阢oF4^y885quN/ ."\5wNDIj:c#l {v(';=ПN )"~gh_*`BC[`@AW㍂(wQ}@S}.n4{|4$98ץf3<.1\AE:{"!|iY24dT"N&gU([|4m1!̙L:Ůe8y/C1"mD:HN1pҁY޹,Y(k}{WUJblDi!I&Gϫ5ٷN!"V_x[^t2A: #`j(GKQS^eojvq#.>&!jiy$3W=ugxy|שM9^6E~R&/YM!{]i%IAKg wvB۽ ]cF:.`(~ AGi8 m/c ͤ˹χ:d=7^ٓƐM#jc>%W ~@ ȣQK4E0M 0tZI iJ?{,‘(CXe4 tMCu Ø [q^ d)L͊RG*V!Zhn1[:H7 e6$yIZ(G+!OL!WǧHl:.c^"0+ k¢V(r<%բдri<]711 Ѫ $Z%iD`l%x|(uc;(PCv("NMY8ބ.r,gRj. t єpW)a@^ [M$&>N뎦x|x|L,Lm6;$:::^VFY,9dxh&"mөߚ@#hݫ0^%۟ (g޵|^%P{9G9<>8u8sǝp"Vnڎ9`ܳ\|x"I8%YSA>t+&/#sYX"Alzrx|TP9Xl wp& "mH%FG4 ]5M5 5zV׬M^h:F\*qF6E81#FeLlxMMd#.2$T]޻Ξ>DD :BTӴcY{c8p39w۹dnأ^ͻY[ץpݽcߋөgi춏%IJ&P"%W񥍗RŒJ׍BieWUr1;wh<1> ;`]Fku'k `qz< Y$R4 l'f_ɴޭ@/!k:dž={Py-\>*U;('Q UUuq4ȥ#mϳmd $fޅ8\KbnC'ˑddgLJǤN8:2 (,-Y ˶#ysPsldxRz^EϫQB ؾ*Hki9MhPd(i=Uwlw}oy./^|+XτܭinDe0&%PH>*j. VJMNG{;/sLLJLJXK/CKU^x `vPǒe+il܎3:_y={SvIgHbNeeU_㻉<-?n]LbwtZ$!`2O`AY';A ܅sUuWQ (Bvj`4`(|>$p]'O$Cu͟Ǣ 7%l:hH7:F*&ˡi!%H,c\gvn13X FEI8Qk=a^6|h&m>ܶjm RNi!/Zdwj6 SycVr4 n}|h#,[L&E:trYݐ$e+W҆hZy?n]mp#N}䒉 ,&NÛhH_Z^%Nh!$s n}OǪR'PAcS^;/zk U%N< )tm:f3J$6Sf04ɪ9(Ld2I(d1 0 tRmBV͕i2 Zzq#&K][6Z9k>r?a\@Ĵ{G;o2:0iܸ{# {x|XhZ4qwskY~#eŽy~]䲹i\n1UÎaِc"[fvɭҥK,?4Xt)v;;vl|Ժ0 +jژ` s6(It=qDf[PU3OmxN/hеŢyPUov 1+6cm͈bIq rXLҪ0.NIDATn:.\ȩ$Iխa Iz .dݺu5Ӛ-pZda<+;c"*(}^+i ΞjrGk2,zfx|LD]{4׊??poxXP|>uC>yϪdpZ b;CzLRXya!⥗\apprq駑SUyG\zE|?*$J^I  D,9'S#="N~b_OW_Q~q-\NᔓNǟࡇehh(}==5Ӛ-pZ|G X?Ha,;xO>2 zt9W%{;n?=\}G8=D>D|LuM(>P&T#~qLk}E翗N>5YSO.8<.z{c|3hXԏ{!D8Z&# l>y|SCJ0о[ nQ71]!ڻy-!ӱ3S8< 6yLEڢ1~8(:W z7<{Ӥo`n}Ϣ@x!Pysk>F"tw0wPp \|mnڻ4; DA $[Y#( p|mn}{EA@  (JB %:m+QQ8]#mpWzpq'g ؽI 헮֮ϭyҾV3TҦ%hooE-~Bw`O,NEAi{5^[qJ<>ZP[GU>Jq &]BP+W( >χG${iJ>GUU{3͛7eP(DgW';vǦ ì^ںYF$;3(B! Nf<ThS葑DIDDg($SȊiY=ͨ#<>Z:Gn)LcG$! |$I躎d2r\pN @(;YQ@ ,ˈwhp8L>gΝ9ҜЦ,!|> AA`" d&)(8u(;bhKW IDQB] y4T I&WKcP(D4%E0`0H,C6l؀ ]+rP h%a42>'Gkvӛ3KQdYb fIRttte4QUuGut]G4dppN:UVŐeBİC[ֽR&Zbkb,/"CCCtuuٽ4vie$֫& 5\@(0 O<{!ڍbWVA@A l |BҢ!$vBx|2v LbymSb5(lڴ[{ng,%GګFeZMy;T}K4M4m (D:/}# 4 LI:Bk[J3UQh>ɾfsK$Ib׮]l޼\.W6No`bzmIѶay^yTUe pgswd2d2˷h/LR4;m" ~ su MG Y3[c?u>nQҋ fN1!t d'qh^uLGu| $fMju~'xr$ItsB" DW DUGM(/-@G۔HiLii #W(gy|TwNXe&G=ˑdiRc=IfhF6EUU46cҎ*uq(ݜtI(bhy; cccVFi;Kj5ic% ð{|4M# kr9;|-֢Ź6QhB@"H#ofnbCaFF' mz|)G5 tvvJ$Rt7c`X7?~o(NL$Hف ZdL&_a[X-H$B栆vOA& @Qٴ(޺i=\ |8j=i 3ooMiFA  #{*a5O?RBBQ |@!L&Ws9–pWmZRxPsY\sDkmaVmp۟BxmmqēTZ&,?nۯmMD]39Ž& ;KjZ.r (u mGm?n/P": Y5M#" iVL *%yڞ_ǡ_$+~׈XU˳yjƎ:km> m6:C+Qy hh"IT*EI4$E Kj<Ñ/mZˊ‘ygU[D(Lؗu;E]q@d2YuH`T "a ;9f^{ Iۓ*!86&G*^ Ռ޽;wU=/㎻;4~D}zَ5o|v~eӹM}ͷL+>Jܪi:|T}->}xZ|4:͔?v͌G1OJ9*Yɇˎ*GPV-[o:K;{}o})Tm;⼉apN.9 ~sö]|/q'f1ӗFvpe|8 3s>J|f .5XÜ}λʎ}|/_7`h]3urtwwH$XSgqNjKq4fq6>13ΔjiğQ?ԙغ\'O>Uݛ>u~Y^뛞h_]UQmo]o>DzkN:xIt|T9O=(;w\·k^>o~};pj>fQ󑫘t6K/]!ˏyEQ-<Dx2?%_eO} "}~Wn))4(䣨A'j`oyˉt>/XNjس{ʐMjT7S7.d\e(xc;<"y I뮻&<>1EtDC,%JuY9dR^}e4]A1e£S HbajP UU18ʋjjPx|`壶&ǚ`kP 5ṘcMZ£B 3}vҧ O]1m&6LȪ/yԽyD~F{CQMS8˹C/"lffsqrx||EӋVBysG"É`7s׾mpϲcfnmCdbՇB M }]A%_&Dž8J.xmfa&0R3]>=)t]/]mbV7 ӊuGg(D8C2eMy mvg ^9ͽHFՖiB)EԩѬF_Eu+Dg^Joa薺cO(DXm0v?(J!MZkQ=G_q4`y\1"GӼZ ZU L~!D|]E¡ϯ}4ZJ0Hp(TC㣈E,j.m7^K̿; cF{{kjPx|x|Mbvujikn{ih$BOOshPx|qQk֥"JEU\yncz-5gJL̩{h5 Q0K`ղcT5?Y"LxA"D\,Vo҈Z ͣ*[.;("gBŹ0L \xtnP9wO=xUHIGɞ=HVd21~=RWR{7j|*<֭g( "]b^C8##@ӧO(nÁN "Wy*2_ikcmR'(>0E&a Dy%t ?cf8*קZcH[幭{XTx)T'}\قYgX?U7~N`IENDB`muse-4.0.0/src/doc/html/single/documentation/mixer_with_one_input_buttons.png000066400000000000000000000211711404105311000276300ustar00rootroot00000000000000PNG  IHDRب5sBITO pHYs+tEXtDescriptionWindow Class: muse2l1 tEXtTitleMixer A" IDATxyU}LϹ.{+ (G<@BxgQ_WA|bE9"x  & Sw(GRcy4MD5XqVȬіF|$mdw`1;(@(llʳRL,,;eQ67-LeՋRw>fw `O:kKVus\@:~ӷç'&d+Id%k}}`kϿS?9cƉ=;lMWcts%uz4O!68G+|/F[Y,aN(Ig{TVVq| ?u|Dizzܲlqpq _β? CoaY;2]cٲgXPLE{X$b\a5L:s(5Lޞ~ilDw]E #8ӓqblhe&%]e%k.h^q&X*宯; ˜+,pKxAt]BJ0~٧e6F-*Ҳ(B) `H{OeߟVfdw8WI >E+FY,&.nf˻zu USUMUeo?9\Ҋ*WY"t5\&b 1b3xZˬnkyA.E VDbI%k4[Ck%DBA4X> 9skȾ%N&[BJ0 AVӭ-aWu@{״w/~~nu7[_|P7<\z-K)i-uo)`k'VM3ƢmadtVx{ѵ`/gw f˨].)%g\oT*?G\ L]d|њUTUvlM-;#&cl?p|+-GQJi0@0IbFn{KAYbbZEg~ߎ*,_Z#W)p Ut^:M^ 0uzdo>a$1!`2Fid$q16{DׁQ zVblb)_uU#Dk/,,{HS?io41͚;8Ø3(D811aSNJ+kz;;cPBt_1=b),=wZf Oxb`՛W˜mG;.7se |0Pg@,9I5/yJ ^D(maX!H;8.34%s—E]t1) re4gq*#MK,|Ye*HܘTPT! fFPo-H[F#IHS'^a1UjdL*E $ xQq 3:pNA:A6tMY,&bUINt{ay g[bAiCM,{[,CHx'Q*1~URf|t]'DJA lB0wy*GXl hD!=+^J\GdיC% IΒ2ӥcU53Ll \''b( 3F !eKq$KK$DϮ|6Ez7|g~ٟ7{@cS^i|>R8tBOdGNA,^4U #WmG&5 ye4ڶ+bCTڱS$S~5C);9^r1VCkq  }ǺUM8bs,f`Ʀa!bVeЯ펌I d5P0M@$rBA큠retOXl}RcUbݼ |'nƤ"J4Utvޞc=Xl=+}7֤b)NS.pUBT>}Uow5w^܆Պow댆YKZ[ޓĤBDH2ހp$;'chjXQh@ yYiiiɁ-d8c̐܁#aqƲdL`X BD|3CǺw4նL^zFX ?Θ.vNmd1\UʘTdJt; |mGTM=%%WgIc|ڊ~O(& "sdw2~R aX2SY1 `KKJufw4Ld3eZX=t@X $\s_'0 E ~;8񮝺eRf,G,r)k*t/]SAy~Y%^5K>xb(Ņɒ<ϯz.2MUu|X5y&5~TcEry&-qҌʢOuF; KR lp"?KJV̉zǭIg_'f;վmm7z1J?^}֞h-͑r͒bd˜-s@뉖9+Zdٲv8^ܪ۶Ed)mmW>* ]fItuqЈuBjܰ0e1nxٕMMM]s5ec(]suSS3Ϯ<|#&7#%QUb {H8,e1 fiœOz³K`Ǵ==5sN[^Y r!j&,%ojxb-x1ʢr͒}5>XB&njYk~8n|-ż!i4 ƓOR,L>ɿ}<3~Y}ILbfrk= U;Hh̰";?kHS5褩[n+--MdSNs̟cƘ,۶ȾRl,Sj/&GU<`#ZpHD^ejH%a]{?vp9sLy8qdbN0nB`(ڱcGQ%oX̒W?3d>068c@@c!@0`21p$l61Ƅcv:utt?%o9K,3c0b\$9΋N`cُz..B1qV!Q]. E&u5|Z>Kq1`#?ئ ?+ 9ݭ.cm>Byl9FvoOց2LgLgTgP`W7a `DQl;v0n6l3HcI:MY,ʒ v !;Z E !",Lq`[RMݫ{{{ .x5MSEQf\Yc0b!@]ULt`: T0ұB)UUu̙?x Չі "Kf!@?Qf9&{A,L0$LNhN~jE͜9p|P[[kܾB4M4͌}YPY+QJT<Ɂxr몮ڰupM`apI餔۷5B!j{?ܝS-oΛRXh+n~.Kj_}@շۏf_'p 竟 sQs[KlEƤҚ`[,U _ĉlyK}M5mK03Ũv0(铋~,ʝDį~tT5=w-g2^wWۓ4Q]wr8ѹfDZ,TLeWyEM 5ϹG +}3;~tqsa(Ieb)~^ =?8]~>x_[{~`ĵoX矍59Ϙ݉VXy=zHGǑai/U/pWzo#oβo--{W,t+5SOǦjGs,"a1c0УٖGlߵoǵLkwqˮ8^yuC߹ͪNStuv,y'ʫ k`7ޣ)O9,] o?z kN /.BG~z;|s a_YY)6s^x pQ~є-g1HV1luB_{˩{ O7?z`"?/xg%΍nNC/{q{koM6S_{ږR,&ƌ~(=}G 0N)KaD=ׯb}җN7ֽwĚhWc$v)}D,bcl1)nOSV!GkqUb{_N`ߌj&4ZxM>Ѷ,}Dr/:/&v]WI|D,"d1UuBs6)7\SD'$RŲ2Q;,h}BTUW0q]qsX XQMi۽}b.2W"Pwۃ !`433bY\xSl|\}b(DP<Ŭ\:1(2vɘ]pUg6ʏTNn\7ζb`m/&SZH;G -n ,~lmǭzO|{_G\lhϿX(XPs |~ aLz?+s˗NuӶ+/ƶba w(dPl>2Hz32DEbe#(Dd։;tJ)%Z}r5G#af(%  M8%}{.ڰ Sν=dR€1J(sqe%ޅ2ŘzyQTG= pE6wQFኻo0;e$} }D,8*4 FbA;۷٤ "nwرpX X.CaS >oڷ:Q;@ooϖoq#bKјkt^?Hэ׍!r\UUU5I|D,(%GYIDAT[1~{ȵLX,yUj!b}kG$B JbƿQR ++2yR%=_\cۋM'gD%k!r #w |گ>:+8bQAO4a0: b]ws 7MQXQZ z ^Yũ(tǯ9ʣ۫7 ƻ EHA֝9+e1#KS*EKйKHc@iigR"m:#OӶ]6CPRx_[\%%`](`O*\,}(J]~AeNLy 4~vPe鮀M☈@}jiJ3yɽ[y(w?pt!v{͍=Z@,eG('m`ci=mKl6@p HϊtsVe;vr;NvN IENDB`muse-4.0.0/src/doc/html/single/documentation/muse2.png000066400000000000000000002765711404105311000226660ustar00rootroot00000000000000PNG  IHDRN%sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATxw|UǿR& 4)U:(&v]W]^pk[u ]tQwD@zo@(IHf2ɴL{<Dy=wέsy 2qVg)MiJSҔ4)MiJSҔ85vŽmp|A}lv;v[4ȖX'e݂MQܚc?ML%c8dfruH܊nvZ^Q[DcjG>}F ɕUmr1c?jKJ)M,[8CTzym[4g~7!\tP Mi:dfuj ac]eJZYB[KY*oEaHc;ƐLГ :ccO24CƓD %gdkj͜sQKF:04iqS]{y6'@QU B fiJS}? NRUb*f&"&$F䢺j`yH[1(O*c56&4[c%v$+7Ԩh<#WUy:K?,M9ŕSErJxș`=}1͠>P"( H i[Lьԅj=R:(cOz`gdg>xpeK9UӦ8p( 0 \eR$I~*+}HN'~?>?? |lGk.S?2e%S(2js* Y;5`^s,A􀁦d'K#]πAeO1 ѱ ODHzZ YiVu^XZER4qkҮxtd}cav!>鱎B"n/ ۡN|MIYd4Bڹs$C.dOI +g~śYS1RE~?JJ9q\ivj5lHfffJ4|ر׹[]VJt [(`0pSa1@OEYe:νYA)yq`$P)6yvDavjDVVjc05vH@G۳[}l%(47Yna 8@T(y眧#HӯAG'`u?Ț YGvTM|${'@uڄ/K yN;tdhaIGjYW9JdM0PxNAF$jp x0zyf1c5L \2#4!$AY4!zsy~_12}֙T¬:uGkTTx͋<Q#*_%Nl7KiJo~c]e"U[m 6)oMjicTA EٛF<"CKp5DaR uz*Szq˅@csj(U++fpEǎt؁.;йc:w+3=#PNU;ز# .ܝ}ULX/BZzv4쟁z .k"IX8wݕv$0`0sܙC.l66mL'#SlܴѧMkǮMJDI!ט*/?iUҸqcJJ!IRLSu(YL2X3͚WߪkcޛEDxNy@(xnSWAK|< xzb]P;*Պagwc#rQ!ju%`P0,h'`H:8'><ߙh+d\,0Dr!bR)?ZElt!N%.ZKYCC TWQ" +giOI;u 池Kd]_(4\}>mY*T kп0N/9cc8MimOcv**~am)f&qe{þ{ذayQ|<_xvtꚞpQ?ہxStԑM6q&N}]ȮZKqÔᢺZٹc&&+3r)~[Eʣ۠CZL.nS!ݓ 5%{<Br?a֣<~qR|?:Q_CJシ#i䪾W0ʾMY:ѵS'4%B@w) S2M/dd OW9xgKL^|/-TLޗ2Bh[ӣ=:O:/P#N&>^݌r7~̂HQ^z4N('t@Ᏺ~?#ХcojB |+xԑ.:ңZ^^R.]+cGѻs'vΐ '_,P2g(wtz/-=5v {t.]8iY&e(F6tc(`6ɼLvJ #|3"^`RGUI2>QG'tDt3u ЖAk uHHӯAz> ;(/'Pr?H'5w%Ǐq㏙8abvVf&?M6qWd>S׫,0O?@~^L'}6`M 7lDHճtTQZۀ 8YG 3@H@0I .P~~?R8L0btJUÖJxBX!!K:9<, 5U{t"Yf|h1|6Bͣm˧1~\~sQ{'/)H<%xP`AyG'ϺcD@[8k L\ B/>IQlDQqrƄ߸įO$`е͊9dƛVV™۞M՛şelQ!? .ŗ$~JeDWՏ?'|=V80C4Cvq^|}> \]-ydF6nZ'W{l TŒWW~cy#ea|3;M_G"tVSڔ%aX$曆Oe1^T >yjhz r՜]dm K%_+ TuK2,]<v݅).u @H)1NtmI~r￟@0{,%T)Sx7x:t(|!| ~YWUҳWoGדN)DYYEZnG*R0&?U@I:u:u%s0K~ѻ]7PH KNO'j!bj[tcYNlc miC>T<:|uu BmaSۨ˧SN[%*MQ>ĔrCcT/Y=]n=݊NlGx؛GVLmwnb B!41>f}F#ĕO ڱ G-gQmG ȩydE^.]yPWY#_\cPLgm|Ƥ)κ<:'Int2h65m_ H[/F uS:URü;ԯ_kP8>WI'Uc4o֌۶_> {q/ZW'mV;`CG kޙ /rsNtԉ6aFNlSYW=h'Evv6"I!~޹Z'IdggiJo"Rٱ!dHVIMڥ|*2~k `ȧkζ^çk+c>a'TGt,eKDxDh@M"4d$[lLJW$?@c-{Y_5d{K1.[@t+S$[p;pc'c]X$B*UHȟ b/3C6x${%UokOV\- C |noP-󻳙NyZNjnn?$/3tM]/IN]rrAQJ KaP(b!_矲P]P8em=7Nz >aSS`PwTWVTfͼ[w*{vpYJ>ŗ\7; ۻlZc\>Lcv^{=%l@NT,9 ;afϚ)z6Ꜭ# Yf=폱d;Xv-+L|OEp@&kr+9nxÙ _$K7Su!t3EVE2w,:0u$sd tCTVKH4[zdbz<6 )#(EՂɗ( Hņadž454<] zwN6*"#{θ'_^D[Π]T"9=m/P sc ^ν^MsZT3ٽse)z۷lb w]lٸ}Ͽa-:'ΠASvo7h_VyaA:wذ|TSБp)((wZ'uS^nȟW?eVqSPPaHS~$ƻ'GR J(kS^2R _ګ3,,:p2EVEMMQ(2A=X!˵.BDyt0d@bV;46=>yp ZRIB-' wYAG@Q-;1j_{-,u3vI\[u<o|nn>n+>û Mؤ+6"1Bx ?l#~@qqwO%n`6N}sy/㖾0 *1ۙ6}ɉ*<7I U=p3VoOy{xaVv?g-ި)a(Wu @Nky%~u:Ug2U0&h,#ϣ`&5 ՀnE >XtP M'2IVIWO~Trp1 8xHے.|qi')_(޵9_ N X7He2Hʘ9B|cy=l_a6{ľ YY8y@PBddgˇ# úVsٌdde Ց j[lN\.챘""Ȍ-B! a:N& ; @P)R"f$)qf: e9ȐJvg&.{@}bf]W#}ymq\_?ȵoqeftD0E83dfgm5~s(P7G҉hj{u[X6(j:(I6ЀoMGiIc!Ջ'QP4moe /lټҒRڵkG`q1vaA;te Ju DM:qb%=JAH:@NhxvnkOi.*BUھ7>8q#G)oM ^KW]kA~Q}!CxgsmX@?^\ HY[g:`@@*f>%4]:>o;Oᣇs6SC3/dB!JKػoxBNrhҴ ͎oNAANg)M}:1_yQ>~a5H3 V%QT5 x4P\f)NWUNZdT;FG,2p9" `XٜddG8D?ID1-VN$XbHcI0*o "HSH۶l`ؼc?Ih8묔*&!`JUB!N'YYq\7iJAj5pjsdSIkPkB ^k dG6` Ԃ($S:Z',C8h,Ȥ8ꁀ(O29Q:̔HHR5piJSJt$}c;h׾3?P#fAFF_iJP/&D M"Fls`ӽ4JG!1o\,)Eq^&4 ,SëŒ|쥅Uc/TF0P T؄h0r^j@cqS;MMJg|Kd2SScm{n7RVg \@}t <1 'njde@ u伺ڪ4ӑIy00)4)MEP6l9SN5Ⱞ%#Z}*>| IDATVmK_7R76*@Q%ʟ7.X蟚 _BE\Ro*( d=y"?8)Sb':%ฃXsl!(`OөU~SlXE+`N 5_) P[t@ MiMS47K (X uG&B^{Sꕻ"{:76yEE f§"d:'⼪W^Xl M}wxUwIT6ȩmZy=:)9} 层TD䥵Bu +]do,oo"\G"et@芚&u)MqNt` MiS,@Lt:)LM^N6 _Klhڒ bW R4M$}i6}i@ůXR (TPMt*i2~˧C4\*0;W r<a;f>`2),UK5Rp-H_ ^ߚofhݹ 3T$'S'cqISTSJҔ;d9}d-(oBe 5D*N'' O (1N$ (dԆu ~86 n2Y+VX>ZbTnA \ Z@`t0*{ em:k{)MQ$p8L0-w\8v{Ry!!ݎfK*_8N-(y;ȝڔ5/<ȀC95sU&ASLdN\P 'ZWiCgO$P:?)`_#00 $|ә9)!v:`}R~:5:A1^FIy/&1Lrݺ &?3^Tid誗iNbE.E+Ӳ H c(^2ׯ⤱[N)4"Y7=Y&3YtmeWgf0`R0,ӪG/qZ~.210HNU.Q,7+R\Ҕ? ӼEK^O[\#kVrrrir|sB_a4kNvNwndgP(Ĵ6ʼns>MiCQl3Km {45[tw)bq6VjMUz%R*67ƢޚMnXБZղflrr5q狼 Ld;#NkS-dע$v*j?RЩ Ld Fx0j56!?zk9Q-4d[)h3oi+yVtX^aB,MiJ0ee~$)lj)-+#]NPrsJՋz θڎNnǸl sO+o`ډʅQXD&h:$$KOPAґȶGjB~Le <1ܷأZ uWʖ鈷;^R,,bcgk)x cQ­uUƗ|ნsXʟx3ǔi~,}lFZG@Rn}>Ny>Jrz82/]m'mS7Xx# ^Tg*疑73 ;GFj>}|?zt/^KZ TZ]qnFWr~-?k?ΉK${Ygt8ct @Uw0G1%7m⫴9ҡyb֕6@;I'+>+ˊlJXڏiφOQrh u~@r1A*  _h(+:JH%,dOp:D_Ux>Ea#0 ͫ r]FL\ g0lbn 5GNI?SKryf%2s#9lߠs`\ NCھ=TkAG]ں-i/pFĤ7 KbS_zA{>foM;y4~YG//z]3?+7ϭaI_Npڧ)[c\P\4Ӵ@+5́CiO:4Քpm6\N&N`,Z~O'qac8RX˓O=cFٙ2m>!PTI7XT2i]؃?QW,Z˨Gn1R?ͫam5%kߟHٹqǙ]hM~tp3s 91 ZSxG&( '!yg_݃=|Vj 9Ѱ#HI$p_)_.&R<=֙ lSNmnhL&ғ|̽!cCPTd '"N=kgɪ54듞Nխ@Fݗu1 y.##%<ɼչy䭞Ǯ؝p f}D[,S $@d1i!̘i;U6=;Tf_ׄsEz.v;Y9d UzBp%9.a/!v209ٌ0o Pi,RPfjNhؒ* : MQVT 0^| Cilܦ)Mi1G.WN˯p-7z4h0^v. WF 1p-7qRדعs3,Dւa; 7zT۫V2 &+-ꬥM1Ky>bjU:fq⁳bu8¶)Ku]g^kQTgYL(V??/"F^>X]AC*[;w]̨\pk,- 't sQ3.wFl])粶\! ;z+XV!k{ERk'~7jꍼ||+7U.)gVG>^2Uu5 ,?-՜a?K՛g+yݷ{f]]w%g3qiBl%*Mdt: *]gݮ[o'rm9^. .z=%m{*II 1b/.Z@շ~f7^N*Tx1ma V{@eKB DU񏋊8_?Nz}g.7 Qx<xpcQstvY ۏQ׽ȑ?߆^‹eE}g LG 6$1:)Hz`e{0w1KEoZH_dowdl̉<90^x\V؇pԽq‡5#9f* II"\Wo<0Yx(^3wws{!$׹伧Y_`3wÐnJ DtCMcCѭWD (CIu`ߴ9O_1Ed >t6}F&|`tH{?7++:˖п_?NaIJcY%|5v ?6>b+I^`ǘ4x^^{y)GɍU.'x>3-:hu9Ȝ ^ePu'~)CB8'^gB ɤߍ"xY W!Y>9|1]J}MckU#N֜sUg6|8}!"Fh3&:o}Jo݌i!23[1EʐDˣt/0jOC@Fa utVo ?!SaٸYI,Cw}&ˢ=-Ϊ |9ƈsړE'5I &,$<ͷSgcZ"*XӬ_Ϟ+S!)ջ߷/unȠ#}tg܃x'ۓ'AD9yfʼv=\&/)@{{77f3n,}qzq;̚?ޜj`^$=zČ I#EO=2uL8w]HEۘW>Ǿ×t>RŒ <3xD 26Z3w5 W !]=tEgʼ"tobKd̥\Lul?2̛?d΢yu:ʜ<=:槿̠ƃyI~x&֯ۘ_1|1/ǞG yY8Ƿ{{a/:ۼKy?3h|̙o]7M>0:O;z {)6$/^̿}xu^) z~ 'iK L4iQ`'s2d 4geqmW0j2_lUϢݐiϵ*{I_4X깇8qh;2Iuo4Ղb R%k'1ƿ9kpd7gpUY[&ulm+V|ʅ-Gqfb>]Q#;Gn+.Mfb^Wqi{9kfZGhT$/EgG r4g/e岽zo4ά)؆\}z:۪@Y'rhE>4+/27G$,@s㧓ydD 9|:a|aөvBu)&2' "g VEYei΢iQuE{3K1ʦ읹*5ixEFk(WqvZJha:qe"ܶm\UcNĝ݂W_L]mQkQ4 9lHlʢ]쟵P+WrQd%RV:HdfbI2cS&k|tP?QՒd^Og66~4̨3v9a&ONQy=yb/QFI7kK?z/+}Q˂lVqӌS ٵl݊_fuK<'28vfe`U&+ ъ]3k2S!k|٭tԝyzSTh+=x||ϗi#6f,DxNBʙ6uWeOW:/6 n6J K#^K=s8`@tDpPPЀ@ϴoPQQIۯ ((hPOz2*oъl7~=_O,ho7Y!gǔ'S]7Eiа1n{*C9Pˡ+y<{O*BpT{9XEߜH#8OSH۳i {8PiJnJJ\ˊ`28Sf9s{?%dk_^xXx!\rrwF籽4̼_Z1Sd}0ulsS gN&n6r;ٮyl9b~q+F1wSٰ{ZR1Wk!YkoUm`g״(䡸I(^댶{y+n:^sZ@ B_%=8f'06ޞEsEǾ[ S["roN }NŢ8V3i,۸ֱ;ӣ޻\ԋUf6ΪD@mSH #h;n[KC}û#g4⇍3ּUM=ʢ8%s!_cu*2l#sNm͘{f24dō͕x'oH~"W*cOYk6B8i^8?⇝g"7E)Ee$lixk0\k%u:O~L= [!\'|+xuE-{i2>zw{6 rT*`*ʹ#85#*jι>3w~Hގǿu'chޛ7?,̃<K$l@-._ 0HZNGiW:4MrݜvͬY3(//_>ef(I<{ȱ l0 ~/`6Ji7lm\]"vry~_OwǼR!Y\͇1WFdBR蜍4bӷEl~PG8VU˸xF$"O4DdkܓGG{J r_nJ|H U"ˮ2=ʼn>Z^6O}}?̾xҚX^{3L RU㰗CZ+UmB={056y f @:4nOVVM4@vS^YYYeeyڴnc(_8U ==^BJ/.v"3ZSحoو%}8-' [5e:#Z}g30d?+gc\|l `^ \Eػ| NX(WzNE#|w6v_idP]dm FAlTr<-KPHw:Zݬ- ? \ 9o&K?i{&.G͔֗*)^O>NMKg5̶aƬT١7kCm9|w`dzO @T'>XB={gdG"տnJ21;nh-]$dG,نL:6N>c[vyG@`7Sg^J<'i_ϔ:e;dv0cQ1~~)[c-E=ʙ<,)۪"20af/Ie?.e3dfdtk+""ξyJYzpY)_o' d3}Q1~/,IYg {5ejDfv-gx>,]0NȔyu;]e\p?ѣA =#6Tj ^EI?؁7-úfT"]:KJ\+ɇAJ)Fb˂-4֓qo0FȾ+ދ>ˮ30g9_o^~[eYm032cxMG#zw2d֝WCzܓ x{|z4qRJ!i\w6cM>o G)s ¼ ǘl?ſQ7_IћɪKTƒS{6" EK_ٗ L{5(GUϚ%#U)eD)!GU;f0K.?FDK

ُ<2 ̘!;>i:ww;d\Z'ԋI;# g2җi;#(]$_ce]cG8‡ ϥ0~y&L{r}6L]IVmHRĘ\ןE3\vF?kδDOѶg>((hȚt>9^̜-Cez&zd4) 0?=27pI\vFaG>qc}8v'+뾐̘{n#g 6i23~N!Y;ƾL-oU߄A+zHm˦<#lB2g~g93]W? zj+9s3KxdHw:&0[w/;:Dn{T^R3@2}\[tl[־*tCDCb& ֠) g=j{P7VDLX,u򅨻Ɏt {:1&& u1<$14 YW[*$ @.oMJ-R~i qsſ aMvox1m/rWѕ@:ЁXǛ WtcO^v5O_q}BiHD܇fSMX`k:J4e8nyQmQn$w#ȏ"fTQlEF3y-i$慉oINRmo<fVd(E9,2?!%caSxY-Tܖv)]ѢYLSQB"Mu@W iSE=ͷߴ)3AKyoCPnm$?}-D҆\N$=A'j9բ`9S[N÷VcϖJBO`)-J_zU^H]eP^n)t\5変#&nf2BhȈ[D QQv cBKs=~|_. Ri椠]\hhH K&NrhڵQ3Q4yJf։ ~? QS5;PbIV:ЁSK7)7ɥÕ _E%.M$%!va syAX|\ |ErHے `mjJDqOh21/ANJSW\d&zy}á U:ЁhUA?6oƫl9"+ }[jji]QKAE:,@M[ؤkh&m@Ğhj5ޖ-r,)1\Ii$ܞEC{*T}t?oJЁt"uNeA8&*L@9$5PKDsZo-n kSedX ĕvG.h]OA@bAb%NNA4aN {" hم!q k Sfi~b)Z|%`ߪ+N!WAzhڨYPt@k$"uF9YyK;Q_W4@EA_gM2|2doWD7)U ]I(.,{Ϟa߃R~G$%%1b(fumҒmqj5YY~ڮ\~@II ?3W\%l\]#pXAuC$ = te-^F.Fހ`6N%@Y1oċ=GcA҂lڰi3#55d8 ԗhe-//͛HOb&jGI 7 Qw[H3 a:26nXߎ\ 26og.Jظ=>_7gúLy>As-8yiGN@EYtrYnX%@S+:,lFUu5yV(@| ǃ( 8rJᐪ~L.6M]]=99ت*OHF:$I3%ZY~-VNƄ9N**+ihh`g'DfڎvU?+8n $srUZ,ؙ8yR _WnWI'nիۑkĵ꿈DI'!I6[ 99Z>ОXIaF#h :,l];w2pRNvvN:lۻӆ%%@{鼱p+F!55[UeTsǃg .vVؾYW%TNbRDRurQYYn~(($'&Kyq$I"%%$%%tRnwP\Zj[ i)&'=+ 9|mMDGRmȠS+ZI޽fGv\jlU͓;jkzNEA$MU2W!?͎V!%KEo9ADQL@=d40#IzwB6oANjL&hhSUt`0i ?JRcH޾ˊP{ w`[Ѡj!It!9æMphϫw(:3Jף54,6qAdٔ `<UKo{?~}NF AйHRr+z~OӃh'JZsbNX̋So1dP>Ӱ,V+f뉵*A\F)B ޑV SƑUdrfTJ+WLZ{k9Gݸ h| YQ2x0?l?Lrlb24RL-PjFAac*W[ܮnp9-rC8@SVyp IDAT_`XիW@y8,"f޽zQQY;zV5jjN'S>* 233ٽg7ܜ>沆ÉVc0v\9jwpXVX%$H9 +2SNeۏ[9Rqݱc'SN壏>hp(*.®|n¢"r=ʣ=SSش:ēছPYY{żҳG+fa9}!I(.*{=HKK |&U(\e[xz4N(xE-7u$Pb/` ( g̘1rs;өҹ[IҠ]CjڦrKR* =^p Az=թ,'i/Cͨ *w>j=#Ѡ5  uxJb4C4.^hDà~8묳سgO M<;V6Q^ }T ?'5qIOOoQ)ZF&rJN7'7Ï6h4xEQoؿF3gdBE\.%%%|7|rEE̘>}Vu6s$h\7m1Ɵ5/VsAt:ӦM ˵j*[R,iXUT\x<72^Yz3cUE RPgUseB$Mg{Of2_/ffzBx7ُe3v׻Amcw84L_vm>-mMOK@A~>;ow/7I;x< FV5蛛S"'Z9зo߀ro߾[ƵORv[,8\7;}ZB9|ERU߳K4$I˫Q&ݲп?ytC}3ˁy'HIIF$kfAN߹]ݳ[:ش^uб OPДxE޷LQEPu@L1"³L 1k>PQr} 0jx5h4jq].?^PUZ t`4 *Qa!Ѩ9NJ;,h(m=hM4u1X"Z:BMLQ;?y]nZwO1PӔ+2YzzzȲ(!ߊ~3T~k\̏ (jFr6:gv圩S/bæJJNl63y҄V/'_ȱJV[q|wŞ={et>dY&33\>KV׮ ;.<āb:p32Я)]]w>zԩSr}gݻ. ˪"`Ɂoю$s7ތ[ >f܈A|(wG7_?5q?e}o5JsX`ҕ"V?}3wכKocgd(%|[qNh87"H~rbǗ+?Fx:g֍sgDXnO-BnU>}~#n~ŀFlEQb¡q'*E=ST~ ( 䡾 9jGAb41hZRSӘ1y (/̣8xHL͎q}w)gW~9qn$IH:ռ7hN[r@׳a&jjj"{Q[~¶o%(|\f]S>X.72 ~ܽ*/&:wԍ{62olbUŢEgfk'/c*iWOU>7f´m;M(i]qȉ[l O^Z^Z={ڻD=O.h4$] l%{Xa[pw3m Ulmkfpq˰cQ_+5;|~IɘF9,24+sBP|eY{%["j ,FR,FRR,\/^j1 m?C{mHb/~HRmp8T}M ***8ZPHEeP1 ?~<|G#INjjjj;dddD}]=n*uH@YUʁVСCɧ$ߙ0q"?deeqɥ3rr٨蝝͗_$ NKB7bj1R\^GEU8qWPUy^}.gOwߣj>S.2Fr_^ye\9vtҹe!a4"raQ$K"[U^)kq3CR`ǎԠl\Vzwˇ{}ETWB]_6ɤZgTE@}=Z 4uɁa`BY`%h+A7hEǒ&{osXC˱5(a0\}/V^9^yzkhHpC3'7eWǕfQ3B Ս&*$IѸ%f0OaϞ=Aeґ +  Z% ǜٻ.ty+DQdǎw'%%kV{d2a0ظq#˗/X|7iv(ʕ+Y|9:\67G᪭`xe08p/;v@$\3gZ- ߮`\j>-jjkk+&/7lݺ5\aa!-?qx65X/9w\qkl}C%wq2ro|;.>ӯO w]˥wȆѱl{_fsuǥYb`aOsԀ'[$([®e6?=0n%m}rbߞy+- kK1L<['8!7P_i-o!՜FlFj@%zЙ:#(84I5HrR)a٨t0vX&7;HW H8\^ W܁MpTŀ,IȒg'")' -Gl2fN dvdL4 لr$F# **+ux^^/̙3;v,99Fh5x@D%r  H(W8_Kҷ_?Mؾe Z>9m3yyLrS T Wǂ h.=5UrvaqEG {eLW_= Xh'/s/2O=ɓpN,^Ƣ^ $G@izjksn rqfxJ[',uuum.WiiOհvGÿz!\:mPSy̿A; }A!vz[^fW_Œ}VX޽{)++8OdddP)d\WfժUSSSʕ+y7ٿ_H.n:<6B}Yt:^ziJˎ5*))Buvc .Wiٱ6 %?Nuu5~)۶m{am(Wi ͷcDA+SReү(Wzz:e-ʏ/X\\̶mИ{R`/}*:&~W>+9kNZ: ?[9c_;^V HVݿ|XzX8k ٺ X\zz[gO&õq4Y7IzcщGj8i5e景N$DV&Զ']Q"v%xts'aV%([!*4ShV&PO \*F&diR67l U_-Y71T7:]2-ծVYhQQQ &p8RVH,mo.~>Mmu_9p*b/@).*f5GVVɗ% ,vt:-ZgЇVF ).Q?_ՠ!Lc&SӸO"obz_~9>x@)P%;)7ݘnIXS;Պ$I YYQ4b5a1hpwAr-b(\.|1COƅ^ʕ+jL8Eq2-lA~q")VRa~+ >յy,k-jۮ"ihXV%Hxm~5SoC] )~7x}`o0 ]b j$ t3 ԭ%ib砚'TaEFѧ 4z>F#!r+^^nTnjׂ Vz^y+ NHLq-D&o~{n˜o歷ٳԘ=:ѵKWxeNG5jg…a2P|eeeq/ڔ .rGZZf͢.'KQL&_Yd iii{\yH>puAQqI^`0`Z޽;f9k4o6Yf6+s& |<Ͻ $S %ysCu1zAq8Q)C4/I<߽ BXAvf3iFuNDɔ%q]Pp7=<Ƭn}7f)y^HJѵiw*Zڤ8ĈفG+`Ȑ!,}㟭F+6,2 x0곖_TŚf IDAT8DPc24,(HR+j~MsS֮]Kaa bXdUUjԅ@=X #Q&^*+рWVyH w4Ӂd 2 :F X( = ~$Z蓓<ҧp0`^w) F`00gNn 'QW0 W U] _9`ԃ\(qѴ)9r$z} ϴs{wcAm8á[,vEYYYYY,@RRo7nGe***l),\q444 /0{v..rRRRX`sm3WΝٗ?j(nz=͸_pRgSHJJ`ڴim)W׮]ؽgOWƵKp)<:v> ߯D+q|zf/# /&yia>\qsOaw8_KP870.TAһs1KWƧ[ rAvex/=7jva 4ZZCrbăz[{@zz:r͸NVߑD!hM]4 .a,v;ǏTcbZi P[߱@L㜑co^A5:읝͚0aℐ YˣÎAƭA>U] /M<E޽T{^;|eɦ(VF9Npi,+4Z_$\Cu\n –W^`00d2Dﲺg67[/gpjYSKT˅00w^$QoNc?= /#+ywIPW%c.{ #}-T3ϔ3rLt/zξg_߸NWr+-Hߖĉ#FgsY+6VJJK/qam3m b~ظaU[U"rrr$yy4גݻw3겢eT_I ,9EQEp,xv;?m ޽;'IVSQހTe<_ d5p0BzzF/@heQ@(l6ٽ1[q[''݁(![Eg$qxV@k_)p3Vg??.$`ѹsg<K/hwӍm-*.λprg%7__T_=k'o~rsrb BztMlTٝ{^eԸ4׳o 2bVr׹`EL8W_}zKcy൥c6yX9mr+H4by=45 Q@ h  hfs P\<E95'$ISQQ?EQ I ~90-D^tۭ!,.}KNG}xiePt1j 4*}3QQ @F5=S+6@ .paF/v"{/'-͢<`u= o\?'E4*lk{ I2fL߷fl6ۥo~ESh23221}˖-kd2\]El63o<-[ơCT?8+ ˲h7.QfmJKKeoæ\3O#-5kܹ iiiL4ϿXٳѣ̺|Z䊧/;c84CH__g wF"ex\@>~&}4^i6~ &1?3'HCm@cd2@!7iѸo_wH’._"ɭ,6+rhZ3go~$g1 /X9S5K뷖?!c?IU 8>}0db@e|sSUUA^j[ :TWH?/>RȾbwDꕤb+I=ndY͆X4x=VCӶSc~X DB%hQYYBdD$n@R cO? Qddd0X(у7Ђ˯0~8zniyl,`]tק_SEM=ëK| e\€9MVɶ|ǜ5f,&MW_e=ͬ 6 -Zy_^u%fK$bytZJ\SAp ^z+Չ"*2!> uu .`4- o}fs%`@U[5n_&Q4MS'ume &b`` ݷ?η5E*ЍV0~+06QF#Au 9#Сu8gk 1t|H}ysquب>VvEn7O?&qf4.\ȼsxv I:>sƍClڴn6KM_z%:iC2nV뿕kԈ9z4f3n|'\GɁزe ̝;7,ŋg!}f˂O6NMRV׫Љ@bͯI ͖1M33V qn7Ϲk.l:#?[;G(H74erՕ/!};x Ǐѥs&Lm2zo5:q!YA{[i Am2څhZ<Gl!u8vEA:]# [zH 0s&Q{uu7w/nZ,e\sh*۵V+!25uJF{uuЧ6ZH$?x=~\ V(rqpFC+•47tP47jԕ bI P; ^пmĥ 4(* A`_*A8ֶΙ( |g7q%0}vv ]u7裏LVV(RZZfS/ƶ?_%\~lq֬e09}gy{^\}6n|?NRR]vEEʨh4rх0vYy7U {3J@{oe,*A8p"**(?A(QP=dϲ*-I3G6I4is99s=x ˗_I_F ']QN=&[#5 ߯ȶd/`VÕK /@Ov㹩%Daxn&M۶.- W.]-oBn~v'^mҷ"‹d"%9,&*G',XKJȲu&l oHލUbe;wK.ѨITTgUV, W\!99łAً=%~߲LG]N",,LBCBxwiڴl>L͘ #44`VWN͛3HĬ_~z_?}o, [Y\T&o'bxjt4Cʮݻ^θT_^~?W|X3SLP Uh K d3 K Q ImD1AG`Ζ!--k"Sg6'OVGTFRՂFc 5*+'f",-ƿmRie+} +`0k@p&@"plMCºEui Pe1y3r[mnٌhd2u .PURY/l)xԶI^b9FvSl{R__:oOժU+RYXg h>N$wJƍسg/Ο ֤siӚ*QQŎ[ ա][׭ˁCHtDjTFi޴E 4ڥ3 6`}ԡ=[*,o~F=ȌUC̙0.x3w0g\{Sn.9Pn۫6k[*Gzs R픊 (CJ%\lflXK%4r6N4P ddf_c s nO$X,PZboێ=wb]#nim1)o$!I $DѻcsrrHJJ";;ȼڗ̌ rlSD%kccb*GK약dU(ڵZP<.e9a:,ѻt;@qe26PumJ>JDWDF7s5!"_Q19 V)6QK{ũ[||\̉~ӯCAAA.MrETe&VD@JP&?fа0Je~dDX0 FMP#ޤ<p@wk4Z4$;GC (dYvxz}zWSN+DX!8ǰ/,ARw<"<ᰔ@E` %:(}kIW%ZMtt4AZ-9_J"VU",A!I Lf#*ڶǔ_<"]J :?h hGps7Uٶ^A4KU*""{\1 `pq@pQ@%*a GFopMh69g;O @ ^:":r,H @ 7"^:2K bj֤o{>@ @PqW_2Gm%A$ YQ*t Ԉg @ |n{. :m%AEEe%^Lmaa9 łlhq:ZRDQ]7 ws+ Xd [ێ1LquIv4$`4QԎF- 0겎(Aؿ@ x}=\P>m%פ\Ή'%AAAy,R/s1EADFD:/9O  .(uL&5hHNjR~Æ>xɄJU6'p1dYF.YQ >1 ƍY&io)?9A嗊A?БgRS8r-Z&RW\OI4mҘp;KVwǡV&ii4iXf qDU\qHMMC4m֢dp=%CЬy3"|(+ʃ@G_E~!#$Mu4߀WdeVGTqZl{( @%ĔAc6III!XC.f NGJJ aӴj݆z*NA YT*N8NV::"CYۿ|(%PLG^:R_JfmKΝ]9ÉcG|7d=8c~JiSPX,l,ú@C<9K;)$qq  xN'6$>ș[y+SlA̭L~B8'CJȔv}h4}*B>*N~)yAΔty&ظ:@Ppn+Z.߽2A&㟩 s}ͥcg՗8xK/fر>a<%g2nULYmfSp$n?57Ywc3F|Mcu[9cɪxt"Xk`O_%vX[+>b-?#>G<Ye0X)˲EX{ݲ{{N5[gą3/Wyr}3zfCzY5 =_X֯z-{}7m(ğ_wضglxd̲l!l}6^1;anfﱫ,t3?~?v9"!]x{MWxW`Xo{ҤV,quZ6^*%_s^W'y֟ kSXjcy콯Яq_В,W~pMckuaї-)WNW64onBB+E s+SW0;ns:#s'5RҤKJPqБ7hoTIP=0]`I./?Ĝٳiۮ;vu6~u~]_\ٱ]eBʚ1`l6sYf697\&jBuogn"9EQ1'gNR]BRz=zFnp/|yc4 OAžu(LFwO* ԊaA($%X:8/BN=IAPxm3YgH)Fsg1 IDAT9Ksq 1 ga铏>B`C[y孴ze1/ Je5iXRvqK$Vn1#6|Nq*Kiԭ+seZfi6s8s2>ͱo.:DY#q'<;u3oal:-"?P}JZ?9 )C'S{oͦgӛy,Id+uA(9Rv1$^_NXz?~o⍷䚥-~N/pAyC=Կ{kӠ:w)S磏G RV-4{#5NqPWLYqȝUI99sO5{O?z:¹wwV{/?{Q.D}/nI%j35vXt)$_NX*Ct03=R0Mza(%/%|Oa8ST9 (`Ūؕ)CYaM9<1¹w\&r^~xudb#B&/3zO ]`cBr u2l&ub Imi}83=^7HX8#~a0<8Ξ;p̙3|9oHN*8ΣFR,`c=\P.p#ov$KNŠ/vm&_ۙ.{> )ۋ;{|_oc}*e}P>lqq+W{^c) UW}TVu H ԨC[x9F\%HGw 쏞Go$.g`ֿ5' 2Xرwy?佁Yv-.Q;ӜMAIHP~*Ev Fn&5Ҳ2JO{ *.f_>O]_OO^)SHNIF-$_Kb0qޱvrx+%=CǬi-o>UNUL w<U9ܬόs}/_'y rZ"Uy!U zdA~v 6qƨFi̦%۩wV\u;%1E;gt{fh#Кͼ1u%'zV^CZ׉c5>M/|I;hiNV;8 ܝ6ciSR@RIS]∪,)M,3}|y ?#a|6!jm;fN;ɲC+2On{E JBF$B_&nvULbx,]`yԏCLtepK;8IpO Bo<ۀ֠ȾNiI4!Esi|;KKt_I[d'žT)µ]aq? Rݧt]=<-(fVK<J1 Qa{{{Q۾`ߞ[}$~}Oz[1=󎚱Ers@ݘPo3sEFʶ+DvnF[ K'!لߣ.g'N-5"/RGF9G|JLRv+:*d@҆ξFSz8M960j4e u>92 `NǺ?I>@BFkF:d2srڥ3f` t+3_xaqC]ڃ9x}@^ϽhC>]z[iԓ7?1͗XO[;/>^燐v(c3o:}p?Lk %%/-r&w9+ tjwߎ`"dNYNa?P%? +BbPPjJCXpBB6C<_ݳSVI:=TK.),2/C&1sN<:Kg7}xsbY3ONd̀>4 ?=E[&E2wUG[63|O'&5νӔ'g4 Ȳ}¥%^\&/,Ys[ GciS:3X9ex5UNmԷ)gjy*ۋ1sb N[="Ji3 (gΜA$j׮ٓl:]^OxWeE;6jXԉSS=[`wL].7d%4Rb?PƄ/d^2JZ#/ِYWkPGӠvkc\*<}=/zrU콼1~O7M2MgYO灣<1ڏ3f6db JX!_H";\Y Yu!#NiɨO![ir?qz cr`r"*-ynO7l8*2&##L׋222(8H}_31=ib?DξН8ڐ )~r9\ A(FT˧ >awvʕ e0 &+)Cl!ۘl2 W.c$$p9ngp٢{^MKާ|EHxQ/!F3~fڔ%] XPE׹;6r*GGւ^NA 9-g~ vl'|:ޛod(`_/%Y?{i($PzHU٥Wz=S,Հ)B7: 2(,khθJ6Sշ=]c|Y<[AMMG> F13NiOba?'*U ,k萋S f*cEGQCЮy~=NJv&hxWbUJ6mBNv"~='9Y8^xLE(RS4u8%˔Md4NmozQjcf\6aI֝H橑O|y$(ں4_l9d\M`Ho7aʱaٓ+*?۹I?[jݛ:j CiYN \0ԇ+svW33xj~?NvVg}B[-Kd/pcpnK%W,&,f _/̗m^HM:>jRSolZp˝,%a.lwעQzUU5\Y sP[ZӧE:o:IZ%]= tyo;|iT]SÈ/Op:)ûзVL=̴dggHNN ))ƍà)aZmt5S Sb\u/?ۛ3y7twʴxmkÐkx/۬!wMO1x9HIO6#oΰyf} <"5o&;n!cxu[CRG dneڈ:/R8K>aCpm3⡗Jf<Ѿ׵G[s1u6Gt#޾SW^xj]OkAax):e4U Go^'i՟LXl "\mJXH4YsI >y~rlɿ7q><<զ[U(R^AQ]EGG;_so籛IC&c8m` (' 'ƲkD+=Eŭ|GK!~ 2]\KW1DO=MJX/L$ZO>qc9x >sxGo\k.3O:J3,9HiM1y(id-cR8qsY[%i G? 5ѭG2{iar1]dʹ9q|& AĕMۚ2*.m̕3ypsFyg5١%/Clib.2._-޽{ӻ4FM$$)f" cɒ%9bX,W1ia^4_13XN:-o%(_cżky_5W9on|]YUj5Ljf鿧 JTP*$_K&((e W&T~c!a`ҍҺuk˚իhӶmN-2Nf)e-?HUc0082w<#m0NbHh.^Rz`s3L>ݥlR4zeJV[Ǻm)AdTv*$48y"Ӳu:J~+ݽJIz -8\p14hԸB6dYZRQU2ٽڹg䗊gf3{M۶?\lk@gM[,Y{[]J`]15  -eb!3#KHzVV}@ȪR9&Wals15Q*|JY A [αXXazj֬ENCWTTΜ:A5A*+$IƱb!;3+.v:U*W.7ʃJ%;t(p?KEч3Jvhxlt'&VNEkI^/xd"X#H7#*1 Ip1;wl- .Z( "##TZYV)^ENB!|ied2{_^ P_J> (p&P#o ]8GPV*FJj`euJL*Ve64 ::7+$Ir9 !PCy!QBCPpOB[I pөSb3Uc#WPt!! pqW%􁰛&!l"ym%֎YFl M Se@ R.(? АU{~51UF @ (,Y"$(h+  imNqsB@ @ lKIrJ#%BnNy& {srK)?}d6e;\dۃR؅+av~Hvy$7e^@ctr#/Ln@ >.AWs_4**d77`a IDATya}o$#}mU_E~[ݝ%`wŽvnrVsU>Ek1l˶ZD~mfj~|Hvh |?w׮>hOJ9/A;7WU^:*|ޟݾx;Г|] 9L%78!8+ ` -}u,S(ضwch,"v|qtsrpDs ѽ[K޻9UP7Zv. xl}ۡ0rV;ݼqƵyrw{gw/uɎ V q-P@6m7va_<ع< A7ǣͩ _T>vӏçC\Ud<#wuzP!q@&F. .Q#ۛn{e˴ϥ/Mgl/3Ʈab?'JD.4Cxl]T)= CHBE-Ğ}Щp:o,m6/-湎a^wb+m}!L?捲+_Wa @ Td$kWS Pkը$#TW$gx,@TI#9 bx"ah݄LJ#) D:AQFQYevKH ^\~\O?1A>)>Kڹ^_}HrQ~-N:6}kūXn ~4[xdɞ` =Y=wi/:/d( B:揶t_<-P%Aٞ]Y?y5yw<.~|{.v^؟Wq}1˱5;p*Ǵ0C::ʪ|ɵ@ AA$.mjr;lth `U \& |O5,SQC+_yLTEZ&=:0T([UJdYajt+UOlᔭ[=&v\Աl]Ɣw~&u/jmί?F1p@Khcp{[S9rR?DŽ߇GM8 Rt! B. ;'}mYH0x/wpf&d}iu6_2^SaMo+@ prtp1~?Aչ!=GJ]lTW1pqG(+Ӡ $]Pչ L * LPPJ8̵Vu3MVMl6lt/qjxgӮ9wD46_ lql=ik:EHHvBx/3. %kډxowl= Z,SnM`I7sbeUpiiZ`q(׊pWRӇوYRZ˷۽izSf>@HcEss?Y,J^o`†wxx\0VwܫB8 'ѤJ]t3xw9-r{~>m=k ʟ+H,7Oyԫx8e/L!w"/r y 2 c?8:K|{J'|ao@ (9he@:WF,s_؊cV"N$]VP#ps\RDȑBhr /'rh_>HNԧqfQQa+fŒ'/t1&Sγ履xum'&N{KB]كn+tr$7`8 LyVsgīqϺڴѝmSzUH`L\F7WrVc72;_,x-N{P>+Epu<i:S~Lʖt\0~zN}6\+Nч)GV#tvo;YH~F:iF4><5^=}O})IB>Jyi:@s@pg\cAaz:kyu41峏P| ӗ)f] _Ѽ/eRbyWl?*"B}q/B ! 4r(t9lc F)O"橦 vveʦbĘ#Quf[F̀y n\*Lj`F{S<7ٍQ;;Ю\otYߍQ;Ҥh1LX̖nWF fЭ4x({NeS]5D&G =DɡFѭsWH㑼r>u+xv`]6B֏Wȥ:\!2nRfNL7'3l9]:j,_,G쏳y~spBވsqkp1hy3Eq|};T[P릧# 3LfBV.VQt &#Z> 4JEPM GLr2ȤTZ^BInˍR9(<]YٰZQ֫*Iy cXF굾U1 0-~6h*T( «Z~b==zĸqWǰwǼ< =*ܵ8c ?ɽgAH=GQye(3{~Fa`WuǭnRCo>גt)1tL=&_je_fhC{}#?ʠR<=Ɯ M|D}9ŘF鋼sIGqWMFVEWS{jwgSm8PVӨ|XA/Oô%J\[񖄄3^w@?۵1XB¸w CɸcEAۘ5l|5b5G i>;opǬ }sg#P9j^dj0|L;B-HCI'M0@!i[ʿ7@W@_y*\#\McrM4`~e˲'LxjbD %.nϝ6rY z%VW\jl9u{CY\:V5.ȕ۔[.S}n`7.Ow+"➾Z<̿tf۹l"+;K0t>47ECZA+ʭ |ƥ3hZ |1f /Gq~۴ Or8e7r+8 rIsRgrd+93#/>`ٯP@Pm@.}(HLcS/i̿xG^A+~CRIch9g2{Z͡!T !߸DO|pDϑ= !/owoq`܁W湗;aJ0d g<eGTvZ,Z1YYq"A.mޝGULo*;XօVQAElEmץֶj[*u^wV*K+"&;B9dL&lI&'as{2r∈H7QΝ\>%cWk3{~3(H3XJV/Sy|mr)y)8dws•͠Ọ? PipٸIf^|~r:c~Ҵ%СT~uX₩g^G]n+}?کw'L>8a[> ̊ 6jX薟LbTexk;(n{ 0ka y8riʷUP? #yh[Y4i23hɻ铧%av$Mۛb4}4mNX6WyKm[cfM˦NyU :Aǹ)~SFFKܦKS--yZri4}Ghzߴ-ahMy~d?~U NP»old ,nI "" 4 !@}hE%>!濩sy_~h;M_6c~ FA"/fῨ9|ΏR[Wcύ/K龂>˝7!6yFHu6['v$n0y )[:Ly#>=nl VynmC6kn9ԭK^\APN"/O-z±6Vs\n29+C<-ٸijgRv_%7%S=kg;:b)JRAS~39ED$){ NDDD7ij~(ˑ snrxm0kȣƷǼc+^e1ZWqԷy2.Ve5']?q3rx}d?bMuZwaиwoY_oym?xX}߳ds\Y=tҧ%4ȇk9T^gd5*LSD1<]Dc_]N )d c$ [R?rYd8SkDQa\wupR IDAT^2e@eX;`4P >d K{'HU}~FHͱJlO?

an^qL <ٜG"5a4~ҵ@=x_js|6 u MMIN`kP\v9ۖ絷>worWdI9@u%_~ '_o5n0?cƘo|~}U9%=Z_(&m>6erѼ@S=Z6>o6Vu~n_< N 3xdžOyw7*曏݇N9M>??(2EgO˻q%|g,r¢w*7Ov;KW!{n`j:$$aiV[N~l;U7~2ۮ]ٻg/UٳaÆuwD5@oQ \ p2aa6- ( ';q#F<YeXe k68w~^.,d-= ,_Ycܚ(s2o{C6q ۇ m|q w=?쿙8)S[TO[}-J~1(&m}%N\va*YX1ss۶.q.}51-j'DQ;U.O7S_S>|A{0?p+|a[Txkb}b\ OI sL\$7تwcɏz'?vOwu:Ķm4x0]p/lٲ';LQÀx`xD7p1ɇx9eSi@ ָexpRAG"Ĝ ^GlȽQkCv/%CzLailbܸ/y"ܯJ{'<$~,/69mzXxK"imӆ }*ޣGQ\ұ?#׏0ؾ-SL|ԅzkL? {4\ _akLƛAU}ƿV50[Y5t2Zʩi՝!9&ىmPV[N~l;U7~2ѣl)} ʙg|̳XlS<絛>RH۲hw{/ 8"\:CmpaFSq'Jس9xe'th,gVfKUCυzCuqfUQR:Ġ}+l6;Ggtoc>i?}0Ï%? vb>w҃kxS[8z/LsbePV[N~l;U7~2ˑIfw/.{v2uʔv7J`3饺:jwq{;d. '^,&XpO`z*-j+oқ p64=R @/7553ľgoXx=Lp^(艹 x?~W\Ϥ؏U<9|BQR@}N֪*o 7]^*k| ŮfߑL)f~lv8߶p,,0piLۗč5sO)U pPz%<}3B䀁gc?40fDnڽU/Wz8|꽽8ϖϲK6jj2cV M8DzkR"ywĴ8Nis>ϒ2i4XӿRg?(0%i]|R7w$2{ 7r{0jPY漏cΛOŘocj|Ϡ/k&]xxy tXsiWyĂ6A£PIX ~ޯ6*c͝Y_{'_F~~0ڠcecOX?P9<Lr6~wl9$SD@C ~l;UV;8n.t0'ƥ^0s8#8i?Y[x'2th4 4-{rU 5f,>9gf_e;έ pgnwB}fzXPw}c [xFdQ?G% dY&+bx'V_"S=AOXz7Gʥ<Y_Q~=߳ ^uܘ ~W^}9ߺᶘ.Ngl Uبɏz'?تwlP%?L7M%Ár{$DzT7u acxn;L+iZIƌJgM>تwcɏz'?=f&֯gƍ`F-a@>L4c_ZaKh@C ᄆc'?ΦӖ; |cC<:&a{%ߙG}}oyVkc↩oe\9jN~><6gG ʁ>~>ngKudH$"x į<+2$HU}~FHű1-1>'""";oLˎ4>k}>][SMAsA`B8Np2osGe7|\N80Z=Ӂ/cjFXۨHہa`88rFqꘕs$]Y 9[؀e{la;\Լ$Iyd<'Ozg !4m䦈Hc~Ƴ|;heky=vߺeVm_y4`b2ㆭrO`LNVN i4aNbZ# DDDDDDDzX滚S'h~ԺɺiavA{Yxwl&Ͼ~FuRHϦyI'഍ue2F1QEYF2\"""""""+L$"9/"""&7F5ǀtgQ2ͪ~K-;"""""""ݎr}5 ߷/F4ؖEEEE!"""""""o0zuں:j.Wϡѧ""M"""NyvW<{xfmA"""N::{O %&/??m,ʊ4!""""""ҹ KFIBjMuQDDDDDDD( $p"j?Y:4@D$s^DDDImڜ:^t6uۼ̱)L~¦S'awȌlkI""M"""N6pj-q}`~o5t /Ԣw(kacO:ɕo&.:3"o;L<{&+^a7\rY^<ճ0kp,.8d6Hz缈pv6N'N97 u0k\3_gC|ū;0Z3ùWpln1Sg}~N)v;1&""""""NlO( ZWY 8J{ FF1k "ϳFߘDWWӁ*b¨B GAz鄂VϦ=tVΎ<0"w.P|78wTvAg|ĉcp82f Ӊ#gYɳ/5<.wk"""""""1aea6to&Ŝ|Hx!wy2s6O41 vyCI.'9bW # }ɼyc=N0c:Bib|b""M"""N:*7VS6>ǀiNճ[j7LLF}ܰ}9\ oii1M:IL+v4H>ղs|nZضE^,[⳯~]&eDDg缈D-W0_˲I(4 h7 Ht]S b}_Iot J """""""``jI3v% ~ѥI6;"""""""Lp+v&RH۲Hu1DDDDDDDR`aOк]ںd+B+""E"""N:}u[mm9/""" ;&mDC Oy,ˢ"{At0|_ip:+,vTEDDDDDD$~: Dk2+z4jUDgN3q:mMa{H<`'&awȌlYDDқ>EDD$A0mӺ780M7Xr{yj;5ذ'ʷxuq\WGEo7Ɲ&=}R.,/yY 58߁yxEsLQ uQD$s^DDDIE Z쐭h0e hoӉ.7v0t |l~m80c/;̤<}P>_* s|np0>(aDp.[9lh>k}d@.!}֋H:tv99*`3q @b&)"DgǍ.1UĄQ21Ӊ }AbkM{ 92FGCDDDDDDDyÀ;} (>;*;ՠ3>rā18 W3FE@ ۏđ3SǬٗv6Sq 4 `&Ŝ|Hx!wy2s6Op IDAT41 vyCI.'9bW # }ɼyc=N0c:Bib^jUhA gŷnjo na+;re"Ӯ9c6 uVh\t*"9/"""$ɓY0:ɺiavA{Yxwl&Ͼ~FH;beŖ{(4 h7 Ht]S b$""""""k4 a_  Z $Ia ""s^DDD҉m&x@@}¥ 4 t2>!DDDDDDDzVN~?kKdv]E^Æ1ط}bImYTTT0vhΈY]BxJx_Pѣp2شL?#SЯuIwt #ry>L4Cs)yc ܽ28ydfe8"""""""q@K@dz[z]DItaN+ NxjzNHk"""Nµ 4=tBc6AI߲,L'4_W Ñv0D#0`!} ZW=ʶۨ 슁{lƎM~^^~p1x23[fy4x|VjkpuH: 3 dހ6äm:/{2RNUUVk1ܖOÄS2q].'O6R〈B ;10.[֯>YޱcmifP]UKx\׋eW2zxV}CDDDDDD$9bY08Lwᠸ_}-aPܯ;w75MGʢb{MT=J^nnHI'-Whzi;1EfVv5m^{eNfVY-=,ۦ˲,m극q֏X @S^~cٝ0XQDDDDDD$]5Oae[CƂ˫)f 07 rbcSܓ|RnoEDDD'{ME֛C[ض_eFzW"O1`XvtOB~{U9olɘ‚_0*'}ƋH:i&Z/a\9d; - zh[q2Ͱ7qygp޵#0WX鼳}k K:;GDDDDDDk1A;B;=7hencsԦykɓrlc|xy,CQWDDDDDDCCwe(Avt ˞]ŗ 4-10W- Vk&#1qrj7l:&""=>EDD$ `QD0\|ɼ n0(6W&h}ui﵌cp<=RnSOG-"""""""鯱@u~fߖ<ˮ`æߖ8N|^/ ?/;w8ē(,F`D<76ሢYEDDz[ H =ٙmLzdf2`.noYu4׷z=++!C~^~y777 2tS- m^k?Q`W'P_=yyx2E!+3|>~GU5490Kn}r8_cԐvx<444zihhaS8q5U]ut@жH_UC^*OjJJTU) P[V659qc%""""""7Ϻ_7pgxcv7w9ۛ?i$ ⛍Ϳ~.KDDS no;~ߠ6rFGM鑨NnUGwi^"""""""鮩- ?9;Ȉ+; |߶4$2/I3tn}ҧvUNo]He «#wBjI%HvdDD$s^DDDI+ HP.vUcőI[pYo HK]ޏ<إ`J 1ȼDDDDDDDDZm4h_}qm>9r4^RJKKٺu+o)SpYgqglfڵ<|v5M ***ؾ};o̚5.$"")5 H>|{2lذ.:I#mOiӦiY֭cݺu,Zs2o޼h@iϧ~ʃ>ƍʧ~%K0{lTJyGs&zpxN"/XzuGuY"\v{?O(+GA:,Zn!F`il2ꪴY#"i&S wtQW^+O׬YeyHrضͲe7eeeg?C]{*n/^ ܱ*//?!~{R=O=ٰaCb]6%ED:~}7nS.'_@Z~=?xyWS]|,[,e-W)+HWPÀD lܸwQnݚD$y~?sO:}QQQbDr9rz(I(&Ur z>/]]uUZbwNH^x_uu5=XBj=+ rss;^x={ĕH\œ6F˭nmϣi\m>Kn']H^O<1!yitT>},z*N383< &.TTT駟o`YVL1^z%*s9Z}}=O?tLin7s??1b֯_ҥKӲ,x ~T&u٫㴁{W{%Rq2&D%"PJw1pNt{իWҎ7;Çfz꩜z\ydQ|d޼y13^y=uA#Fzp'2c /_… nXy׸k)**\"" ,`܇>n^s h"۷o<|JJJطo_J$"~SN (ȑ#9cc+t /D&++{M@00W7ͨ7M3KO$0 "](sHyN]wEfffT鲳NVVV17oLMMMaǎ12dHDΛ7#GFW^間JD)<7VÀ0CjDG;a)"d_:10`_|q,+Q /xW\u{h!"%Zf ͖!JJJX->*m}{WsFkcZp_G~~2Lk?յ9|d#泭oWY-Ҫ"F]WjmQ5. 0OD'I2228{ƔβF3Έŗ&WH::"x4 %Xn55k[SSuZfKDǴi"޷$v<ӧO9$V,W?xrrr;tPuҸvȟzQq1[jUiDDR,]v p )XiYa3 E$5OU֏>(qFcڴik1Œ׳eo޽{9x eeeTWWS__iضvM~~> 8aÆ1vXÑi$~;v:͔)SkÆ ,tqN?84NS邏NW^*ͧ~J}}}=DDӶ@dkOvd=|n7n澡|+H4= 0`1 .O>dTi9~&$ƍyJӷo_~$$~r^{5y>~y0}tN;4/J2ٶSoѣG'$~~~~i$z7n:1IɓNc&[lIHH|"ohF'\Ý/vq;x6 No}covY^"">|8w_v-eE}4ͨ 80 TTTiӦ USSuD7~qV\ƀ`ռۼ+yB:I>˲馛(--#G4?.//Dz 䒶x<)ݞ͛7Gc9^qq1TUUEN "lw>)}Ӟ)1#[W%"0>}:zDWUUuVƏU͛7GյW $''Lx@8uuu,]˗s7r9`ZRs:\paYEyyy҄5^UTD?OT^^^Bb'm1 Ljge0:thԍ۶m9Hwچ-W("=Դi"nphbF ]g]Iukjj~dž ˕?;Aaa!7.۶ͮ]NV49rHL[AE0Ip"r E$D{I~,ickUVV}/%/_Νwޙ 3ڵGFn]Pź|b. Nw޸bD-sؐ U-^#Gpȑ#FWİaׯ_ij~oذ!YO#.߿Kt|>wqGL˦uw}{w[oU hVMi8Sx%}UU555'""KDCt呛Kn^yy咛xi,Zee%UUUTUURUYEeU%UUVVjI;aD5'0cƌ߰a>/@zY~}/ɓ9sR]F|>/RL"hGsf͚.-Dkwiddd0rH @~~>0hhh;wƴ}qƜGJJJ+#_=vZmTVVGt/} JXVVv7;;;tjmG't܃mq2&D%"CRXXU[rNVVVu֨ʑ%Τ#GoG0 :,;<&MDFFFKRSSu_z%,XHWcF"ե݆miO?=]!D:KWQYYwlHu翓:nI֪tDD4@lێhFkbۑ(iAywY\.~q3}N W_ͣ>S#t^/^.&1bi(x+G?Q\s _BK^b<"" w|~l:/h[PD_|8+++ٶmƍk](++8#v}o5j(6mSZǻ˻ q8>}:&M";;;E^;ŜDZu]RuD E'@D$bMgmsl"1 "i0 M< MEJ޴ixז-[زe O?4q1e&Mɓ#z7^TTwr|ˉv":$@ "rv}vA1 "i"z-a_r)x}.1˲0d}k_me\s衇xWS .]Z00UjnёGY'K2T&TFcpB-\Pv$[O>~EP;\KJ:U=yTdY|xyNʤx +V|}?*+ȝsd֖}b\uoEr)*0eNE@E  LZ=.L7n,(رuV}_-bvaEv:;;ue.+){r?<Ї> T{{ 7P=X\Py U__kOSAuwwK/qu6me]VN?2`W֭Z˗xWuΝ;k]խުgJ$. epQGyD\r.䒢2vܩ+y(~V|bxS.0p+LV{g]p`@ʖ}S__k{صknF|˻.-\Ҋɬt$TTG%r(f~*zj}E]/N?tmذAK,Q8GmV̋B} 7PT#3Lѿ^~岬eݺuZreYr1Q KxTr,(իW577]z~}_]S___5XnN9yEẮ6o\ 9p}_v[1,^XtuUuXL~UPNް1(SgG[R|LWrs9*󶴴P_җ&vXv6lPe`f`t'~<^lmrGwy\SK|[:_i7{Ǒ{OS h>`+uSov<#8B'tRѽ}# 袋u֊W:J_|qQ^Œ |G\pAXxާ>/XBNH ٥sd)83geߧ,˒j*L\R˗+qE/~Q2W<??MMMMC=tի}OPK(}j/aƚ={~Na.b|2,c 94w\ΝVj~뼽橶&p($k13Ng? M|y%rZzu4Oc U__Wb̛7OtAUl455+W\QRρr8S/9AK^ƌҢ7k_^|Ŋw'_cᰜ-dY1m{ nV[DqhhHT*q'+6QG[nk +ԥ^W_}r)x<^W4%\N8A7nT{|ʭA^xN>Iy~F477kƍꪫ"sضO?N) Xc$>*#,:q|}͛7O?Ou572Lc.^Xf͚2-D"g?twhpp11:uYgcݫ{TF}Lg}z!}zG500PC:(tIZ~ThtG}~_{7߬M65eYz;ޡ>[s YZlIky$?5V]|=-[Mdr:V-]TWֱ 8餓tI'U8Zv֮]L&^zI=^yi׮]3{²,544Ђ t!hZreEmOڟ9g9s̙r~,҆ aܹS=~iKڱcǨPHy߭*XFZn֭['I}_DBDB)vJfqXB+V+LjppPLFqEQbqT2w\y:3%I못[DB* )[.12(VX,6-f"۶Ԥj/$BX.0PzJ 쎘fIV,I04 h<@\,RS *rgLd `0%I~ 1@J@ r$SJ@dwNWu5מ~Ѻ'k?/_}owIcӧ/kPDPsr}s'USPɟ,I2^ z `0XUJ@uUBP~.@)F`nI1* M `N>j%IN&[;wjП-iOɗ-%TR,[J i8cΩ`eK SJ@LR0{zJg ; ZJ@Օk>XUIer|"w`I(% (%ȝ0=dTy(%  ` xܩՙ  d `o,X@U3(%(% ,I2># c@E !`{ @XX60@N=1@ |S x0KLU&0& c$5  Օ T |#0@T,XT,ixT թUs*gI14 8"w@)k>XUI5|,{*AW&1@q\aE 遌VRG% (ùJ.@)Ʃ320X60@k~% c$Td `WX;`z$& "w@F`"w`I1Fll,RfIO2 cH Xc8@)AE R,uNՑ1@ & x 8"w@}fIRRJ@dd `\`: P-4  `ֹ:8#0@UTP-ST+(% S H3Kbs @ 1@ 1P;`z c#0@e> K6@F`$S9* F4sß)%(% r 0h>@YWҞT,|JVP-1, @>3J 0Ksf8@)AE |utt0>Hթ^~Yx0xg7j)ǩ ;@01(%G)ز:]}G%<S AA) fLӃc_7ʣ^K.+B,X,ڑuu ;uO+?u29Xmu.^. *s*mnosԴX?mɑ/h.҆ꝟI}0UeD+B_Q!I%PTP#H>uOEX>ϬZU@E/O/۶=_ži_~:)/\Ⱥ[z6cБY_7 =]`)-l=_DzvuLF/o٢H8<XSf͚=C:dr546jEiEc]=;_}nxsk󻮫 Tì JHF"(òq]Ţ`\í˒1 IpDpd)e L6˲ܬ&͒ I(h(`">jO_jEfiB`.XĀvlmk/^߮$}hFz͚MRWU:U`uY\=zQ_NNӭ[TS[9ݤ^eRCr&G)0~)]~?k$FK>p5\W$cYZb~BmZ>%TSW_5/`2}'SJƲժՠs8j:`LK[!+VU?Im翟Ux\CEJ.%p3idN=koӀo騏+Uʿ+3Oz GTФKVR&o &$IeeB͙*nG6Fc "իxK=o}rb2=ڴBv^L ΓաXDKο5$_sӿeWRHGoyjs|P e+ʲL.ad`vɸg7Q&vlU&zJ˵C2"P?mt5oztӟoiWji6nhNw[#% 鐥OGR6(tՑbm[e#ݖ߷4gڽMn&-LZݩ4̒eUAS%I{K>CV]>Cߒ]D9E;S,l"BPQ(R( q8,ےmٲlK XR(Ry`$K\7oi& (_P^!=UeuByS{-*m۲[B#gDclɁeKpDf_zz*E (۾V?Y&7}^ztu\3f &Ўk4h~˖m B2dj`#ϘlP}{4--nF~Lgٌ)fyCSvlmuc,˖ў ~~/Ihje#>,KxQO_dSJJ ʨnw][?kϓ/{LF}Jt顐 HDkχC}Ly%t'R_oH4 NpΖkH\̀|فIW H%5N͸Z2Zdcj/uAsuգ=m?Q{#>dG J}5{׽&t݌܌({7p_ +sA*iLFS%)bj冝:'ߺAm#{׌P[_XMM>Y못-nF  {>d|`uAhy͟b'u8{Ե0 換17Cۛz5`܌YfCO>9=wA,SuS_t\G0u١W_|A]j0:lߓ1kLZ}4ߧ1i[z}v-תK;0TZF#YFzknuutTdU,QhZ[&Rw*%uB$ۧ;JuH 4az+(8 GȸIGjтZtB 0´%[:5ժ >uk eH BӪeK8[I.00j16^0 70c}yL:]8,ۖ5F#J?ѼL&HF -R$3(I顔|u &S_'q*2+;֒+488D" mGKnR&;5/A2fG>xJ$I{^EtJDr&m HF͡w]W}b.h_&IG?r~_Rՠ>IRbyQ+<_M8HjjjdY;$IYsy|?<_z>sjk:8+ޡAI<%5hVF\q_uDƫJ~ǟxRl,MO~иcp/Yu:RLZQδ<5KZ&Gɗ+18c @dl=mvWi'R>ŋ_]m;;V5JS\F¾憥ŖyJ)je<ͷ$Ǔ>uQ + cZR)afyϾC?N'oXT*3 ]R2Xն4+w7'8rK [R’RY%IƘ1_yޘgyFx֭;^m< G<ϓmZxxzgr3xҲd%%˓,_HJzR$#%\),KQIWu~ L6 T]uϿn y~k%5'U#'yFjH`~tTJ3ޯxkZ]jk$I?~s}V-s;V;B-"6ٚ%UF}<{+lxc&_wnIR[[>wgtNEu͏[AOסF-i|.^'KiS'YLp/#IFcJ0QXT9:cuݯC<$Cgs>!pB!]wSvW-ɖf>^->|Ls|&ɟn,˒|I|oe3ceEgB 1*8|r1Feɲ-\ו=4ɨuۧh,=o8rNdٖmO=6 Er|W*;Qnyx^*Y S]]$9<ʕ+wٶ @˲,(u]% %I=S{=ڼɨL I/F{dIcˉlǒLYo|[%H}Fˌ:KP]7qEQE"9#˲n:qim߾=}w^aO%F0[-'(( Ɋ-_T5$+wmX}^GVTsx_SS:+Ȳ,y'cbeѳ>+c8B`00{d Iԅ-E_7jxV,̱$R 'Z rc>kmV4mڵk^ cKRoo֬Y#I o8 dM2BwdR3j3[(YQ֓+k#֑þ3(VPHJr]W*ɨGwݻukŊ8=/3'8 #cȄ,H*c}7^zQ}] 5,vٙ:Tc5JSFs?.%m[Cjjjyj׮]J&Vs0Y=,ǒXtڊsO]}ȓP&g|y_IcB!E"aE"=S >޽[3 2L p$~$KLh3[4?( 幮+6!W7FBgxt:_|QTJK.Ugg$SOM7ݤt:A <2ОXħQor$[221K&`v$/'yOJ yRV\^{Mo$i޼y ÒWϣ#9l<(it\XQV^V߿!?$G-^F'~6rJ矗$O*p]WtZt&1M1Y)|B.[$ɑG6$/>NTFTzF9Æ3$_W4UMM$d’,.""*K\{"\("裢F> >zEIf&o=Kye{z0If׫S]T9']:爈NW3=勈V6.EDDDDDD攱f%$iG FJǧ\(k| R)!B(3X5%K!7E_XT2Iͬ|yBlLH%yejzN8 *_FCFLr]VV3004JFϗ.?")vNqdI$8e툠lԫ^Riy t"RS] @5/D|6/7lQcGNE]ݔ!""""""rXV¥5:d2ZPYQށ 뛶rS8 DkjO:Pc C!0FcBC>rç:1ƐA/-`V E c6_>h WnvoigJ aR: """"""`0Ň ҁkJ1`Ǔ{/xsAoO[ay./1QV/ͣzw<.ԡT~o0$^T@DDDDDD1`UgeՕ z'93ߞ(;cMpΙ']6pL*0P<&+ݳ{|ijx䱧 { 9S#O<ȡS.ƽ<‘2|%uoC  [p1X//Xz#snlۗKl+*Hvvlſ ?~5!0orEDDDDDDvTeٻ` eC"giCdbD #醴mh4BF .$)|>?ׯ,qb&mc|++[4B3Ԁ1xײ~z|>?d*%pi<)8 """"""vV˖zx<H.x2;;rJ1`6UtuwܳϲhbzSטnl~r**my=r%n̚SN2g&ZTO#+WDDDDDDPмK8URJ%룹d] ‘0Kc[-4nƖW_*YPWG…#\zWfx᝼W Y#TQ>U9`Z[hkmyUHJLh[BA2T4Tq(!a[yU$`4J`4ZP\`ebc]h[pq0%SRu۲pEId0rm{DDDDDDDE}E's0`YӞ;:qaG#mwtc=ƽc3uI% i|9uE颋xWbOs3@qn#HR!,#褧>!H$4m"OGAg cw^9ulg[{w4eDfN>qu| 3;=H.~a """"""NB Iv||:;YY<8j1 J3)H^4[*L?z"(_zdfd"۸+ G?(E.}az:3-OPU]Fooc9ޅ1P;^zcFC`<7oS|;#ܷ=^b}J BG&55I{760r|1cp~|xOOu]DDDDDDD%f$=f Ŝpvl79ٻO~[Zqe>0rGF|жm;UՄ~90lڴh$m;A熏}d27ヌ~z{{]w.pØJ`6t4G1Km݂tC| tvvϢN9$UD|_c7\|owA$/>81D,^XOsk =\a(/+c,Z'2eO}r 7|S|榛Y4mwoV۲((' 0g`9CY4Bq3*DDDDDDDEm8Si~S֞DsqMF.- '&83rCeYX ,ww#׼*?и}wBEȡq6+hܱm |JȁakR ůRpۯ"3`1uc0yS`2|1&r [;{Lzf1Lf=3!]7ϟpcX"SL#sa~3pbٲr.Qa&&^P^L*o8w}\|Ab=ƀeFyO vx1.;LrT`0L\qαc=("""""""s@Oow ;%YqaTO@DDDDDDF >XƦ&4,lupgW[9v)3yx3Ͷ-,޸[D#aQ+g:""2ee\te.gF Y,H{^, 90ԍ1t.`ժI&nj1<&{z: lw?:D5. \G(0}c̸6K㞽B).r㰯u RDDdP``(8v}w äR.vbTVLorkO!=(`YIVs,gNu.{:}m{oK64 3|׻}npkeXb`2>}k(xw?+/Zc{5n"=i'{l\Y>ϱ6vx/|'kJ#""sC[{{[Zo/@&=m2 ۅ"`\i<,kY|U`(ySeZ̳P{-XpxaW^X{$ 䭗agn/ZQK*岯}ɒxGg;x27lG…~=l3|ᾏsuq=NO^\i~p|zz_qI ""؞f;XT_9˶'>44rC+p=sN2yy_.s^HY#2fU{K6?cD{W$W-ΛcaJ80޼>+҇*wgAą]#47H$_pJJKK+(+=$RI֑L$ \y $--X,\耔 5{6zm5k;p: ys!NzE1q~wݛʳYP`@DdN2D}R.wu8|EKN'`, u,"ўK D;% //d֗']?7uo5hX;:_uٵk'l²R1-" Һβe'u'TI&h޳םe<7ܕ`n0;ҿu%Oswݷ}kuyZ93G9+x&d,L"8.|rnѣyAQy\͖~kjg3}uED䐳ww#FL(+'##>yu !""tuuG(+ y=wgPa…ritmJhom}zzzIclذ׬a06Pϲ,"xT2Iw[&1Au7 Qj}m_\Oh+gx<=,?"""@EeKW+/:t5DDDdǡ.0 b⸞8!"(N(4'd (KϺ4Hp8< -ƞk}sp`ҁ!4ne3`4w>' msȜAj=WnmꛛS再VVto]}elث[DDDDDDDfA6?,J0;o*-"""""""SSw~񥷟eA^` 7I """"""2T]}0Q)ˬIENDB`muse-4.0.0/src/doc/html/single/documentation/no_audio.png000066400000000000000000000767031404105311000234230ustar00rootroot00000000000000PNG  IHDRi sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE fatal error83 IDATxw߳nrEE X * D#XQc5{{7&XP, (Uz88oNyfv ޗp3Ϸ>eSff,t+߉͍IkrQQQԱϿK1/}khW֞.P|>}"=m&]5ǡ8-G<45.D=D5i0$.3Gw9Ϝ$kM%ATPqm]Tp[sE;GYB%N&92JgrAIFzwDq۸6CStR=;ۃ&.).mֆd^'9L"uHl9h^9֧S}d t''DsJ7J Φ' eyreϡx̅tpV_YxF)1VweUu jql. Q?[;quiCz|Xgk7T#vWڳGYvJ,'-JBR!4a34n|} ptrnveK0öҦe[Z~*tp]H D]ߜ@^N(7 oZ;l /Emg%IIvo[߉]{wW0L@1GL&Yb%U[u[5>BA($5^]]uh׮~sTU%QЀ~"I26rY=g׉%yHyf6Y?[p:Βl@}`9̶x; fMMu碧*h ~S76FS(ilb;2[I|CżϦPS]K|V^KɮEH$+(--rڰ^ǯܦx,FuM ݺ7/kG0 I} B>!r=H$`jWZ\BN|;g.ee<]4eKUC# ̍VQ(kߞb.\@SX&W͢gcHs䃶k |]Ð$<ԓ"/d555 >\VOd 7of,^v__bG?@(T@mľ{M:SSB'KnltPsIJlL3JtCȘxס{r$+$x u2k Mduv^#jkVF%&`7 .r|mI*"[!/KOL{ I v!Yjkk),*  6pu\wD#);KҶ s94R_]5;!QW%O$+okV2hp q&1ߞp@{xa˖- 8HdR͚d"AΝX|JK|c-mMi ߲Oo*NޮoYw78m+-PH:U${Z:BH_ {5"e3`ﶇRL K@,Asw2m}'o[Yn֍2<^Pt_oƋ/љ*xܙG+XTۏIB"}1PA2Lpf [,2[#mW…D^F!c)!Jx`@ZхC)"rRgf6`,<ϜxhZR)7=UMeK%dktЁAX,͛޽;DΝ;|UUr el_z~t\z٥L}h*_ϚO>I,lԉeKҮXb[ Z[M̻P|4'j6m6*iZ޶} (D?ҥښDhUh4B(?DmMaùAgmYmwZ: yIjMHbqifn[Q91[\R:94W"uH3i±̫E2щc釾FՋY=MoAouߝux>b|\'@3=2d@x' :$1#ԏ@s8{r?Р2Q;<`Rf$?ܺΝ'ezU*_%ڣ8cJcßE?_~Ljgq^`@60U,[e+_ 7;RS|d7̺kbǙhnû7<Ͳ@$61.p-/dWfX҆mk7#㳹3wp5P̜y&vfi̷ECk̳݋F'^}O{1*Yvg L>I.# ,x&p#ӳq0\CII _~[kct-&J&#eXSh=܋7С&ض* 6jȳ?X(i.{al7|2=*l1(L ӟ4EQb8x6{?3H$DxcZ KG1^rH:yER^}!J S)()$֓[j'## |e􅍟IvaYީ,m>>m<{.NSO8MeSc']vz)$I}E2:KX1rۭ՗3[obr}2's ܲ-;Q]$1cͥwcv>|0򳏧u]}ABl؀&RMXzhĔ*+ާ[ۑ;\fY̧΍] 68o~c7|j]Nqha5+ EgַKv`w\SiISg5K˷|0$TX抍ifʥ<>s|ʫ煗_f5<ȣӦS*GQS[QAMU`>GY2_p}bQ?|>? ,]>Ei1 I"#!LM⨏{ eJY6e&Ӧ|k$1+hZMyޭ2Vc4D:sع{_9 dq9/gձ$pDʞHֱr::+n:~}V9^Y2ͻXQ)~$' |= w&S%aE^/FTzEFc}떮.aZX7 Z3~ڍ|3t(rܧЩe$|} ;clx^^۹3o*o8$}B\)J 𫮦K#n jmu/ٟ܌Pظ`^GɫpcW,cۖͼk}>[CƦ=z+y y_ ;3fdci0t|Q*l鏓!+ѡs~O~AAˑ_Pw͡~d!HRYBn?uhg'͗f?&}wnJo]#?ݦ3ʢm.jKÑ[)3t#u";_!B@2B}TK/V^7UFꈪ aͧRd1=' 7.> fc^b翞͈ a.ap8gޓ'dĨ Xܡ>v̻d { ۓ.xQ|O BØ41g&;;Ť m/&\BҎ@o>|={̺Cϥڛ|Ϙj ie Mwq"|cpdKx _pk )onϰdS>DZ$`_'Or lUSSW3>a* ~'1-R}'0ߍܛښl\XMٴ';`R^^_4c% o(~]te]/%AQP\O]Qǟ-+6G5b(*,dȮCǢ$b1'$ϼFc6|7w.όć =U"$Z6ɵ%LkJi&?{AJor"2MŞ騒8fG:ՍZ: "HLv9_>X8*@çYƉEcO0"HSQbq+-5A, )Z hQ^'$"t><~MGqb8'/ dQ䅂t} "-W& /#} ,9uIeɑ/d}S|]Dsxo|9/&/|IUUUP"4~):~9аq=?ӰqWW󈄣ntMd9O}Gv݈)GY_9Ѡg] J\Ak ;@}DP_KS >Baa!y++Yp1`E%I 7ld 8h0ݺu`Y|, 7_<ww'%6x`lc|%}d݀vO$t)DD*uI~?}> uojFе@ B gaz6Ulbݺ %% 2]Q^^!F;zi"70ˈL"SgHqsk+֤=BqdsAՠ/ Y@߼ՠ ԭYFZ_X3r?N|fIrϋ So0sjefYL.q`wx]َ\>6P^=(.,fƟW{Os`0H=(*.fÆ lڴkטl<.]R޾G̱zc%o_KFRH dcufگ546 GH$AB! :f2G*86{n,2U_n3s RIƥ麂7!#R|~r694vo0HQ6gۮzY,<66;t˭53 ? ]^-)6k)q?p5 s1qm#~eTuU@$!Od(ʼnrzBU=Y[e+c0'gbSkیbLc 8hO3Uae@802Pw ai KG`;ye!dh%k͍WM#iݼKdrnmY u 7_xo9cXNu]c W)եu <="<qz-(oSm*[$DJAղ涸]A#S k'^ gn `Aq[wꊄ̹9^x M*<`\wMܵvZ%|/ պ#{JxƷ ,av 笣#, O#o/<͗e0Uƍor'622 f7+AH ./\|(bw/L6"k4wh'6Os;5/{2薭w߶NBΗ0-KUr;;H4'wGb0ۀʛw;vF5{}@AK.trTktmn7jʒd3m:BvBx̅A7p3 e fsEao>p'Ƕ$Sj~t 8'2 IDATB|5s1@gw9ptvn%ֻYF@P$o*^e%yaɜlә:ԩqұlcrY2 ,gkI_*ch*]f f \ >2pR =A_ߎNsL/x3E΀Nr"p4 dDq1@`82?-',6qAMiv#Mu-%0$G*gq{洹]z6Zvx~ =o4wRs{3mp`ǙͿ}fY:7x4 Aܲ~%fvVYm5 {?x8-sGw%C\гq g?_0L*^X/H5K=Uo(}}gZSFtU}_.oM.Kh> Y}fvZU^^[Oq7.o,nrk!n/-}3]xd?W= 8I r]y$Ie2mOKd+_o=JיUf<#(ǷM=ĕfўWfSoiݫgwM~[ -^: s`*g)Wvs~އRW; .g佽&'3Hyn'M̙5ۺMH M+e9SڄY5Ei@ՆMS+=vH^ElS-: 9{O݁]Kr(csiŹ4ex+e~Ng 0H4Rz?k+e%o6oa+` FffH--sĦ9yǺ7K-1{$Oz\EEz6p4w),UWq8n Xa.eKm~);ƦZi`ȧc'k: Qo6p7vkY1zP MeK`7mopw\ݴԯu#L?'gM|hi ]W.1bÃ)XZSsth=0y&uQ,jcZ/إRM4o?2 w#;QΠo怾A):Iv~7]Yep@0waIqкlVrOzUX"ۣ#6-th_ 1Djѳ' blzodŧոANdlEШ:%:D(ѦLԖنl#G=zB:0Ԥ}MQ)jٕ2Vi}5FժŬ|1ut-3xY,ډ#T7g٨sËw3.蝍1b^x}&Q"Lji?Ga;Pd~B[$ؓ1;W0让=?=kƂ,i^%lh+Y˦:\DAH5پ<=EѐeԲy#[2aex6PUH-J a]jiٹU>;eYްݦދUa)ϖ'9 h4ܐь]81`5WYLs,-ng`Q/c =˩iPjj3Ȧ$c1|~?߯R=C4/ ϧikKq9kLdRKKdR_Lmh Y0F:۷A݃#'J|ޫgٽX2c&%LG?'w1(A'wr-өBF .3;J$l&\/憷bx莧8Pe&u'DafJg87u .ˉu xxq:}|\~!+g0wNEOxfr_M"Pٕ=+n< 4ït;_=N~c_?=c2?#];Du7XE+{qWrNYSow%|yH?s_-~?\\vg?,E+zɿΘzM{vj3#D#Vv)/?p8@̻ϝ w\3d5/~/m3CJ<:xz~`}'o.vrÚY{3ɸGߒ􊢠.YF(w PT_(@a!"EEX..&$7Rʴ}{B#}4O5t߁BB!|ݛ=P7T,~~t] ]>Jb\t}].o'1| w^.wVb*t'P;j]{'/o';Aezu2=ݧ(n\۱Tnʸ<ےaqiIM-v+-%m{ر/SbUk֐_VcGu⢦?؍ ۠A, BϼMi0ރCsC ,7ɨ;E{/ > ܗ'>aEd 'kO$*>YО?ڗ?O>ޟ͒w~BEXͺkF 97xof}yOlK/ݏ69tt|vE<4|^4>I>LyE*}<74:Ͼ8_},a-2|gЏCB?~do|҅ 7'FV?nd۱p.G"yZ?GW`10('GtCx®rB|el K^y'WnJXi7jt(YT,:%ffr7ώRyƮٗʝ]WWk(b+WHٶ[P޽)?VϜIaj;Ր ɹ,t3On0{!L1wu'(*6CoB G\u kWFB ߆)TPox Ыa|=Ј?qYatf]HԱ`9p0T0@ >ShԷ$*$_U DјpRS"Hs_e!͆P!:*!)JP J4f/]%YӰǩLG K__Hq9gA̅ *j)e?A TLBET*SV!Y$ugWլ(}(kS<|h浇zpE7s6~/:@h23J2*,ڍzdg{ 4A7- AZd\W/ʈѶ_lK63|FVOƒe_\~*>v='d|JB,^RcyL-sqrgY3gp`UD"|yu/w9.ȼW^d)>ٽXɇ-;S5o:?[Ʋ>d%돇'O%/N>˴8 tcFmj%s3~SF{ Q`˒*ُ<@2ޝ9r8褧X7ii48gUwgȮnvmT`ä]?m%Ffw#{/V[KmaDG\}a۶PYI6[?`Q;Ѝt%@FI{pޅ{𶓘p̕L+K}4o|?}G&߶=.<= S6qQr!Rlp(qt TubܛH)aKn}x0. 0pbk?} J'_y-^w WϢ|߹(V"4Ig[VSNbqS9H9n8AꏯNq#?]8[a?1Wy7%麳>QAΥLgq֣[z?ľYQߓnGrRog_?:h6v'\w [D×\|%l[̛ N깂F) tmb?#vS{'[ =0'q><?OJÜ9bs<ӬTz >4JO2+h{kOGf72;=)49FwKͰ+phX2JG6e=5VW#H/;jiN9NWUΆc j 3p Ӧ ˥4Y-9ie?UͥKCUA=+-g˛n@zh"xznvZm^-ujm<#)V,馴f ''c1nԂo:UY^>òIAI>Com6-9i4i] MڢNhsGhD,L$L]Z _= @(J3QܟBz:cC-mٚF LM_.{υ nɥgNrxٹY-vz.[Ľk~Z @DZurݍ QSQb$TUUӠ;,} A"$L|S kKKj"т-E2 My7n?}ཱུύ#ίsygYC˂pze?M67$IzLDQ,yyR- b4Yr(> [A4᝹$mA)Mg)0hތݮBqH5CiPh>>FhՄkg|FQH ,G"#/СrU$TzѨMǤmh'@{(,d嗓HD" DH QS^~qdyZgDNd=էXwc;$!~9Іs!{v,bhm'͉vpJgH=D1e eÆvuDIF[#nh^'qȴ a!D @"%AO2rAas[s 8 aPX rVUI-c%H櫥vv)04ƥ-S>BL$}i^=o/d4JqQ@ m'ajx4j=EE \{-j$?zL~3۰Ğbr6M#Uwb0[o56ʐЋ, IDATkm'`zi&mF.? Pc1T}l8o«$hާJ#KJ{hH$!jvqZ]MSO;1HfߦWOWM$J[k֜[;qd<1.:rS^ch n~ڨ\Hj3zU%OE~UE{Fwj6x8RZTD$c[m-=e(*bM7ySU3z=~} 0coRR0mGrh#fVzخf[c?׬oi֤T?2^ˑ X:[[fΤ>x]~Y<o4O$ ycJ=ؓ;Z f%;hc礬Y@s{-ٷQ5R?jr~.,[tW |Jܹ̿f7PY|dKs\?OPQN$@j[ z]m_eV^ϲ$yYK;sєV\j6Hv9JQh0q^AE{uYڷ/駏>bPRTD =lg̮fn2(e|hU6}7h{'/͙쫮b[nsu|һ]q1L"22tȥohB6n6J:j{j -9̗D4 O b% usͥ2;=Z,N:PA gjj2^mWZŴ1JmHH}ڣo-a<}[?H&!O$ B@i^9sP%z+|-|3C!J~WIcO"DLv 7#mFm0}KަYq8d7#*d @(Jf@CߓWɬpD8aRQJ E>0?mޗQw!1Dϧf y19 yyT ;l8)-Z$b:e*o V}~yu/'w7-H^7Wl^=(TB:*¥(kzl7^.=[[y$FJn63Srl=h ˳v^4bؚTLo_g]FH8]j H\Dꄠ@^Uqm ;ˠ6/UEozzLJ5?mpbZRIְr.8ZECBɟ0w[)*>1WsAT.t$&ɟNFRPXc#w"%<<2m~8 N>ZO/U'sPX;4l' ;dIȗZ"Fs)2a⑜xT>3?&3a\b z)|- q9 ,T} 5cm;ڬK,u{\tQyciBw[l s[y[881K9&7zr)rZn_ns e5SJVvlCzRus}Iܺt#.a!h?e }o3ȿ|y<3ago?v,z|0 :/D:N hrэ,ڑvFibIPu~U|>~Dd=s7w`xm)gX|DxǡWYΰX+3b7yv>V{=X4,~kZDݖQLsC=̰LXu]Wodd!hE&.'0e^"==/>{?RmF92kreu}#xDjKS2ֺM;f+xWyXs,R&r=xSݽk^&._Fn-JT-e}~zu{qO. Xy\ȚAR\rrwovfDZyyjO$Pn?_7MJl)_F(|H+bsMȪ/YA J,g҇~$kWRm;7Lާ;%Y< ƬoW׽PT؉'IST.yڟ٧g)||jj̡H652q9깔eI _?E'^佻QR؁OO߰6Ҝrb|_ݏI݋ ^@QGt7gUDXx<2e3:<=عP+2/uF.m9Pb bxhw`&Ыz11ՙu/eޙ7Ue^6IBee7qw\`dpQwGGP72+( (eVB)]4M~$iymRJ=_>$}{w߽I- M4vԊa:5Plf=4 7{2KH?n a ޽Цռ?7εkq9UZNNicԪl3v{j8= G:vA$+^[!NS ъpǜ, Qh*<6I$+ ۯL9ЛmgHСwI}p?ebTl4c\5?@mee\}5|ݞ (3,E!((ѣ8ۣ?y?G_MygcQnRisjyI>Dr ,f+֬XkyGJFs$^{2l_M 6 .qF;%P'KUNsF=?u5Y*.ҽ1ACDz)qYH4lf1uI!ˈ&oh3 m8@||ޘ%3']c}mXN>Uǖs>b'W`m9y^]WtىXM:W+#V!y={e^t#LzޝEGVAY_Ǝ0hs]Ct.C)q2i8TcZ|l[1)ҼkÎX|=\P) S_zכ)MNhCv+mg'Q88^/C TXV70`ߛEe c='Lt( ]њF0RQHa1 ~D{ T{Z{i׭ $Ga "Rv S<$FgV|eq!+ctFt:̗lÍymQЁ$Ch#ջNABtvxfiWP_̶ȌLQwS iY1ݎs8~!/M[CbNg@\|a[ {y_1'NRƮ-vh[gƮ듆ZZGUҼ+|>-3$}?}M1| RMW9kHڧܞu^jeTWqc9peɬ癝ӊa]cA&QmJl0E=3_;9%zzu1dݾO,sh퇼s &,nZHઋ1`2ԱsB{ضt/){Xg9#q₤ t辡C4{%x5(nJرϛW5il8ܹWk~UvdW2h|ڴA)?ѧ](Fw/x{b#Ԟgp×=,2KCNiT]-Sr',yyF7fND9OS:Ndzg.%+=9UCq}׳wKbZeqָqu'#(9bsS7űu4p#P23Q-84'k*Rp]׳q^1,3NkUh]TMWek'7ݯ~%ѷ]:VwR9Dn\}UG&OϗI=9_׻mbhDžw]֗8dH6&>j;%)ϚĄѥ(؆qۢU7F<Ӫ7qS]`+ -[poB;E!(x|=ysUQ}U`*rrHR_9jJcA_ G}0^i5êwщèA^]ƁˮCKF? =1S#C{nͻE?Ws7 A*ukǮ}|?2\[QpxUAjy݃;^.cǀ3}jwQ9reUM"p!} hԐ}~:_9 .j/z}^qb|Aw農\ٛKWr=e-U\1:_ 7z. wAey7T8/  7c+U޿zNe*{;i޽BP|双yO>0W4{VAf[ۏgk^A> {{ m;nȝo/޿]4 ~G8tAA{y7Qe^A8a{Հltbj`^' ]`o'`^v*W.ԣT͞rUc|PQF5E٨ ‰HdFV:˾Ĭa5T2&'C@ߗ=O0ll<=u_j^33^#8vA&'`^Qmًw5žM*cMܵN{>hzоas/ه#pVWC/c h(J5F_+T ‰F`qY\>ݓҼd9bv')!eA y|j8^xH< 2;w#P١QA8!ǯ_ZQ6u-fgphf S?KM ɹkZ:7Mee U6tANLk{7NUST}Ngp[]ǍՋdꁢ Ae^A8q Z^xx?C~A{M|cAeأܗUA_LNt  Hvk  4{AAhaxgë' BA FA]$Tbe8t:k\F'\W5ӓRPw]9Ҟ8_#ʬ_6V{4FOk!>[1*3i&N>|C :f\G;_Gc#3&drc +E#١uĞBxˎ} M?'CM_CM!m" <6Z&0jv8rQvi|$2h:OC|A}Vo{;mrl64͜X+b!2T$q򝓸}d F*O`eoe2W|8sxw;$=4K5'.ߤ- ׾OR)?76>獷?}60c{tcY:_N:=3IDAT]ƮOz;}& `v>ww5Mk!Xw”7 S2O7b zǩZͼovC<]5 SO^ɧƓܛﺋ˻Ǣh:~@-ᱱis\̃c{+/(Zm+ci0Q%sxe"rJԪ'x'WO@3g=&i @1~zZxhl 8Z17;=D;|rZ]O&ώrՖ]X(\YM%lΣWc6vn Kxq\kVGe]CojZ kfyݷrf]K} A r21wlm^{yY3eȱד/4ie7'ޝɋW[ͅ쪨t˯E&fg~9?'qJ^eEQҮa8LG#ձpk^{^8'#H|>gdz=D*;&w,*òLVW#o}̘=7yfCf5{MĒ糽xǒZGG/a띧Du 9#iot =7-Dspŏ>w0}82BG~X{w1Vwq_ٛ)tB:@#c_z{F!h!})J{")˧ct&^46,AXLfZu nj WF^np&j$b6-g6]xN;=fsC.=:9`6I;nfk,LnIw/Y:nj#ԡ]k,SOfԱZ QmA$&Z+Gfm_kO 'sȅl ?S.{3O{b;.7 Ro'9 0Y1*ЧS1_9YˍMtGR|z{` ->5]5D|~Cκr;/ҞKWul&96WcSab%<-'gZQ4poon^>{w.,fjeh(1hP^]jg2BlodڭeڋҊZ(pD3b 3ENzȷXJ~j)_W}|+Ys\ڥ/|9zxjYT&.έ!S55Mka4Y{ha8B2O.>xND3M(mڑ֪ 3"(7X[~@…%C-9jcK Wo"k I?y7: <3u.}u Y%(q]1K~ؓ<.)n5K'y7\>7Ys9҃oLbtnUќ6ܖ z6ҷ1LBr>(cV l:r188\nW-rL霜Uʢs泯S;'S/_5{gd 6l;B_^Ί0+VLFalnvCwhgŷwPT^Υ8ڛZd6V;7FzeNx' )iSZb_JNǓ\8lX4>ؕ1(Q w?nÍYHMgԬgQ6]/GR%o+TL <ۏZ[(؁Ǐ?1{Vޖt1ڷ7N+YS1hFR $IŠݝbpxkǾ0r-gPV̐p]SYtN#L| ⿚ £99綋hÖ_GwUGVC赫| w\9Zd6V;7FzeC$eygYYրޞ`L~:9%݅/ !yYqoeM7p͍Ts6iw+{Ǔ8E A9R`iEd^AZuia-|AANBGy 2tA.#zAAhQC(Ͽ#y8}5O+Meչ.DnCڨ)PL?{6M~^O~f^\rm\-H-ryDnʻ7^2}hv/ο羫zD} τKW0Njby[}|ιΝ}|?Ei*vÓ~c/pc(\2nyr wip3O(_ѡu={ơn_3,٭XƩ<wS߽ [F`[ރG&ۨiC{⽥;2~^l߸v-Zſ*j2cl$_,ߙΔK,9}dY{p1 8cx3gN繱h>Ylnu-O?|Bzllט|YSrx-5S氡9aXNG}=66|!?3xnNqGKЫo]Y7km_Lw9zW$5KOpT MOv \ƚMEx47G6(%l>KptZ]v -tk#mw=j:ϱCx`|S{>⎗jkme06 fC˻s"0mTpSO moׇXm $ͺ{76'-bIax3P0gs/VfM8QΈ$%*: _aŨBR@ WAWmng ,f!UyqcŖh^Q2v^\Q8~1ooZLq:bľ$|opf 9zk ~A٨.l]k}ؽ~ ŰyU!kO^=V Kz:DRH]OImkyU^i=+GE>5F>_ӵ\жoc5Cxۭ\yI| àI'5Uz5vk7x&1֜yۍ{}kJQFwT[x;? wg]9\&6:'Ze^G6=)q#hJ!x8bW0Tě:_L{_mEU3ianP-CRN*X3o1v|\K~f~KcDN:G%'/IkjzadԵJT5f{FOh=zVF mH㶑փa}[8% }-VzTaH :gWtx~>UvG ܋Am^1ԝ;'=̰ؐCRXUܶh K-DS~y-2]-v~;'>}kէv[Ws _Ȳz%Yt {sm5e~";v/د j ^{Ct 1u0#%?v(Wn_5UzMԪJȃL_Jٌ(RNQYwU΂ fPx~G@ V6` y1ʁCe6.. L޽ŋs}^g?Jgx7'sp[|vUԤCgk *î`x'oW!uGזrBSZUWϲӕc{GE6ITkE?Np_jzGzGbψHbtqokUPݎUcLadr0|iTY[]):E41Ԟ o87ޙmoDF2^yُNҕfΌ"~yWJThX^y6[f\}/|nRSp:^:eTT혴Q >~Gr P<  c&{GAAhHA4OՍ{AANpWS В?X~Ae«AAAhQ@7    (۽=z " x^A0t/A^AZ&Mm$ qqUT~r h2 J++5@  S\.+231LwnWr'{wnh4n ʬ{A9r1# x$ Fbu\ zL{AQZRB!CJp\[&qbBνnrpVSn;>zdS䮻 "xֽyAyG_OMY<̘|n>~oMa$^sCÒTC `ǂ-EHz#WޣאjA'@?u7orSEV^@91"9C/s6D/k,ܲ x/=<4^Wx BsFE<r3$$X9wuo㏾k/י˲.2SՏ).,_Fn.ƎӘ[z>I,u?5 B%?ouyz%:TW?⃗ѹc[2 Udtq߷#N|#< YMړ/λM"Q^Ahh]Կs*kVЁCltŦ{IRb\v?~=Oy!=2 ׅJM\N*|Oh 4;;NcN៸.s)og[ߣ B30]ןOkٺm#'u)JkSQ0Rxزq OWzuP7D:$)rĨA<"\I4xTx9,*goD-OOwL[AzAx"J :c_hLQID[pؘ6>=:]!UvrV;V~x7I3xYn=}bQ^EhZ6$  4qt'${bRR2;ucEN  ;;W/$wW窼jn!_]'ry٦VAhD2tȔ1#7]~'/act z/%a\5j't44x]@l5w͝#1u*&45|1r ]M8Q P\.9vqEwz4Jiѱ5hzdAcj!=-ۯ))-=*5 q6- ~#ݽN"  Q8L$'p8JMl&6JٌZf^ B3EU ff7EQa { Bsq5pe<<ʥAAh.jGpz9 Вj UADSɍ渑ʚ㭏  )5Ƹ `RbqVKAYQ+ܣ?Akx( BU֔Q=<3TjX%iPAdt_GOXIENDB`muse-4.0.0/src/doc/html/single/documentation/output_routing.png000066400000000000000000000655411404105311000247330ustar00rootroot00000000000000PNG  IHDR>А$k(IDATx]@[]nN~vX?"*b xv؁bl?.'s=3sι]geK@vC &(e Ҙޓ(DR|`EC~ :5 ^^[}XAbѣ&AP$\g8e) xNNu^nѣHD k*1VVV$Lֶ!z4|܈!)VV֪鿻׎ [iffײm@`faOd?e)ײXl8UKת9z(fΚ Yl2wZH$ k =jb JT槂\b!dyVuG;fN`~~xvobOoyyNN5 ?ӥ݆*Ŏ75efGO %<֣d0OOkP 0!` ӯ$oq/m732/k̓;ծ!U,Եگ酟ʚnx !w`1r^K|-Kw ^ZS#v]&_f]vkbOSH&da67{"}<:5szR EcΒf$9솨"aQ?[\hzp MBGMѹW/sw|%QT׮mGbfB'MlK'ᐷzƠ*D" ,sbo88EXlGlJx;[Z5h~%;_~k Vz/Q7`clI| s˾!zanvN7e.CM% _*(ΤzjʞH]E =/Rin]tD۴4ͿWg\:ekbى@aںxEK)a5ňb.tOc>\AZg4q M'ߌz p"W4~v%%n8j\k^^^0󩛈}&'+԰021lS ^>WTGnC( .B{׵z`ZF O^ l3?7硞%ɦf*ңZaLk[["*{saA>d7<u-115'1 Ν?ߪUKXVVX50ܓAN_P0$M8niV}"krY.;oDRfMka#Ѳ6V+ KXXEWu Eqg]"t.NYhLJEBFMDauppڵ_kRwr=ܾ`ɞFsb9I߶{X^w#phd(@llWG܍K5=&ϵ{Ԃ"TgK-OJMG/ Tu<rCIqMqI{~Y48{@z* θMȸQk7@i_ִn'?~|?wBmeO>t0ONIQr+Onzre vu6~yuTq$dž9-s2ns}9%ŕv8jiWr3SG5o.NCz|r]{G$OZ 4!t4ˣ]gv]y>}1uǛnMHM2`im"+Nyw`q4,l"*}+l!hO8mxqْc]N[QPiB=;M{%"ΰ'HP%*>\|(IAy@c]d!zM p@`h ڢPfC!-xqCW?B|7Xov9QcuROLEN!Mixqe$2:ӕ'vhz^zkiYO;񅫯'&CݮV;zuʕE̚0~ pP;fܸ&2_oyNԧ)ɳV } 4pojlڕoS4K@haP|]@8dH6%# "yӢ\x,O/$ TZ!#|HQ j&^z=k" i[F;9sEBaTTX*!Bʬ5&ߜK9](2y7tԩ/v=jFoĦ/T j\Ы+v1:N)XGl ?ό;{F=f\% 9 $+I58 E5]igM5wƖۭ52\`k%$\q{잟okgt89NifcDl2 ߕ:q MA!"-`pTDrjf)P֙ųJ\QIq=oGe]X( I? 4i}WKH>>&z!o AS'iͿGPkm8"g|T _vC)1[Z*j][+s@ w+V>cQ!7]BK:ͬŬU =ې7P4Q%N ݲWX`?>j2Ӆ#;<+D*`ݜ1H]_Jfw]Dl0plRTcFv[vlTZyeMEݻ{<_6xykYPUlIvgF֗5sD,l]1Qj265+*(yT ))iy~^PPH221-.*Ԓf@FC~olgLk[wL8Q)a}""D613'SopYL+[;M|^qa>Hdfz`Z- <.O87H$ZC5H+Py]%w֯s93PLϨnp8Hʀ \9fp/:+~o춄Fv_hnAs*zkT1F U1<ʇ@?z۰>m\mnVE|kZ |bb.|?/%MYr a9 9i@1+D:⅟vMXȝnd}닙=#Sqѣ?)03oVԠ,/oxް͚Ǭ:F{~u'3P;# Tu:*ii&C@QGM\~uJ\>ƅ2 hd͛>kuEtҾЍ2/ВcǕﲰݿ.j2ĥZZ+GՉv!]WO]i>swsPj n# :=Q߉, DT.Er?\~$fs~W4غgP{*7s"v$:t̑G41<|#7[PwD.cD:OqELHN5v%;ȇt)9h R[B pkn-[bQwuMω1I:?AŒ*Eo1fڧSx"|4&v_?Jݹ}=D"V}rt͜C#D'J;Q.`dg {8y,\@&)*|q~e˻'f*b-ݒM8|Pԥ|X ֫Ogܸ[OrG}$Q.|* [ ՄG[qf 'j8 74:]ʇ"3ٶ-U$U3澳S45><09Eei> ھa\%!0|f.C@SQMbPƇ5vx۱3i9f> /ڮ4b7vD:FXLI'.w`¤T*.^pf{N,wBTI!0b";5}e KfZϱp?7l/5>N鵂9t&M"oyͱh1 g~pIuyoȉX3s ҸC[4貃oF[Quz͟ܦJVZAcc-$>_,P Mi$i\D >=8-BQ P CF| 6t­!Go_ChjpbR3c|Ju"Q|F2~@wuHΔ7ʫ/k* [WCV㇄2 !O d> _|=՛|%P/:X:쿥$CK ^eyy86015+CESa@~~odoYb&R$hdU2`cD$ Ͼ#@o5!yLYSaiDvU2BW-= f 5miN:!0L82!jx\nA^.^< ۓ ` u֞/fq1SsSkr{\¬1u;ׂ'eS}c.j+@ T-K [v0r*5c*l难!jBwGPZ=PBz`z{yZHvakУ40ed !&jWVKp8l-·{sQa!AD=fQ5!y2>WA M̘ZZaqV|cmIX%*͝kh!]FMdVeΆV ee`8OVL$ei.\ٌ3W,j[Em%WXCg|xȔazp+>yGҊ:!\Y5}e)APSA7ǯ6ߪ~x=V&9"w'!|w%,ȻwA׫C/D8`lf˧5#ǺmEԥ{ye<)A~拃+(͚ёj*",|ujMO3,TƇ'< 2::ҋ*K2.3w!;K f$]%U{؎kI$2Wΰ.Y׬j>&uKWNa !܍9#E{\Dt%hau?'iT?)»z.5zP63 [5yuJFЂZؒ*ٯ8uIzG!|ؿaC rKe3)h |2wt6oy5=@Nf=JKlVŬ:vH®huYqy<*]Y)f[$]RGt<{k!4)>6ԎM4u4ӏ^ .9UwCLjuNsLE{l@K|"y=#7jK%v:oBRvG[n-̽iVڣmvuIEKﵚ8sJ֢ kc :}@ \g+ڏ:Q4K@$_GȿXBR>dHҺ*olg4bq.J L <#,;s6ۦER5A|b7.ŔAD^e$81X`!H Ia .[dy7hJĬHBI\1͜ӠP$.?qJB({C@qD[Ch փ>Kw#au_x>8~r4js y|:Mċw+3'4Ic5cLaA͘ʺwӲ>-l,xori7{%P3†V=TVzV_Pktfa=_z{؃kൿ0 ,xVpج<>} D265!ajjy\iV6||Iχ=_A\.\lljbWp]dB̫}gŇV]/KOSF~KeOt &ZA8-.+_K|ܧ+l ʹjWk+ޯ@.`| p?Gh@ɩl!hB!\]j>mt79CHb<7^>u wꪡuj./[37y9^C^QBv>.Wص7p4~ʼnlӦ3NibW}#{uWӔ9CMU_ f)ofFCcw *gS<2i;y:‡t5@eemܞs5-ܫ{wVkZf+QMʈJ~vu67qmܔ!kGܘ[t0}|5Ҏ(A\>k]R+`=u4d m}r/"λKoBz&zu[%O]aPl05dŅoxDѥzh`M&hiHH7$H>d4unֵA6:Y;NsJǞǼ}g;[\N3!B]cRE"ͪ|?_c._hn¹_m)"=IՌ> ƀCU|;u+% pbNf(<&&knʒQME(EH>!O"CU(& N={ <:~Q-O}H4r; t%eA&+fLʩXnŹ72L)=8CYT.#B&OL%B$F^oL!r>)Ǣ|ec)x:܋uǷ#I94ah0ӿ_<4ES'96 2Ȼan9C>C捇49ۀa.h bYZʎwZD1 Du6νƷZ<5rrt u>\)NYRdmfQAX6gf!*dptG{\I+u˦HiD;52ӽ# ˷j^Vkgח+F'9}Ra^$P@ 7g]>Kp29j ڣ!Bt.V2 \|d2" O;}"6E1ɠ%NEg&vWh8$aUjȢ!<яUߠ{CXR"Imϒ l*QzFmn3h`%iUuE x3jƖBA8lvnOFU.=GHޜd `^[*IQs-cYs|5b*OxV6{7g%`4+%/fL ;rDMVD]XC-F=?}Dz Ե7ky=z,x*%iD >DUHʕedr@|ʈe!,"\֠PL Bu*PJ(i"Ae/䚥T 4Yb*)P6E]FFc%L( Qk)$k+(ЀVՃIgA#`k~qڶD?˾w1൲ Rߡ7B[Z=(V9@]xޝ =x-S I-/Iz©Q Mz٘p>9?`k%(k~S̛ԲBΣ'VVk G'] 4<~М~ֳHfZrݎW7\ۇכj}Z4Ju$6Fsgvkه^Yy,<"w`1r^K|-Kw ^ZS#v]&_f]vkbOSH&da67{U>$<:ZQYTU:I!fg7Dm 8Bփ[OxXl:jju菉B$|iC3T.ȥvm+8;5g:iBe;^WBFPƱ#VBd8X|xv%JXlGlJQ>w$j".(K$ _=dw!@ȸAG>iz7zZb  *21 ya$>P }bkg}Nɖ-Hsڧӧ'RUަqor=)^+W(|u CEQ$Z5h3|̏뽶jx/zl߼M;63%uoh~q7#H+F`Ie%A~v%n?q`!ϧn"F? pHwPcÞ#9ϯ=9U0 sGŭo(}曆?P5C'4;]_V9ř7W(y#cۻW-O?"*hҌ vѻ?ZRXC2׎1MУ6/,T(Cu!/FoǀU2BP"SXX Oa"I&&d (A٧V \.Ͷ$й.|>AؑTjV]>gdbk "hdlb2ZG7G3}tϟxyG*.a>0DP,Rck52 ZSfX:XCS[T>YwU]Zbt!E5REUP߻ `7ת?(%V:L*8w͍;(jQ;ˡK;$Y8<9<(j\?ۿjݑ]'MC ۛwلY}'Sξ479xD]*|ԧ}xuۍ1 Ȥ>Wץ{/ POڎj]Xqq#<4{}t_Zeo}tAKQugT FV;Ki[CW2y+maBv>jk!c-| Y(i>GA/~ngReve.Ej^lk+Ԭ~cR7? 6ݬe~wӘMcDV&|1|%񳎮?G#0Zʼ݇^5CF3lΘ~%dL}?MNb?ݵ;ai&7oeeʼnO(j5ԛhI6ҝ}qmhB:xUp * Sc2@$:u #x^n:P+ 6Th;j vN?u' Hy[IQ+g?NDS|hBq5lL(s$ۓ͝Yw䴳ѾAZ*u] xIDpK;p&#~hcx[w}KY2p>x%Bj W81u]=H\/`}tXCIPfc.5ΨFEX[ā_p#2 o?US!%t^J?nvS?VG> wÁ({yh^avwly5A}gBƎ8Mο*/ ^&Q g?ӺT@" # n%o=|=&|dz+"7E2]H1ruWExF~7tArɎG&4 5} [ v$W^CYSaPwy2ycA.'x"A:h7!+ AuGءc:s\i3eaBU^e2-,rjHdYa~N_vsNrҖ Z;!E $μULj+d; vUj`@y*od9l]xLfS*@JѠzGMQcVXe1ZzNE Zؒc8N f~w2+BM]TM[V!5V]qV?UxtLӣ֌ F|Q6c9AGU_T.qMDiM^5c*l]觪P|z>j~Vǟo@ƪ zգ4zQXZ}e7DŽFZ=_G_=_JTP\p"}* H226!+Luq-*< EZV#ruHDbxXh\`0Dx|i>N94yLG$-TbH1.IX,ޘvY;[۶Zh̉8la#*^*:_B*c{9?qÍu[S/^+zK e@tCȽyˁoǡ{!sNffoc3 sL´ 1pu+f8XfݭӄI=>Eӎ1uɘ+6\1;p3<"p D]*|»C|c& DV'H7Tjډ_96ZQME/l̸I}e|Bэ[}#66v\w7,Qyst^^i}= [*"@;Pu|@"qM/]rY8rHX 2|65te Ü;o9п.&kӂζ%nTs }oC*$բQHt=CѤi6yi۞Mݠo#!js4)7]wlw}Cܾo"*y{Z#_]_M~-b)ƅw7\ ;q?^A,OBz8WVZvCoݺehd鳝;111֖lvU =ỗ[6!Cp q?YztR|_n!|Ac?=\:t ![vm!ϡlQFi#̎u+9Lj.}%v{Rq]w>}IhۯH}(xp|JCidݘ [{ED>p6*gck5s*ҹ˽{?~dYA֮_/  0Heo'Uq#wn]~Z:t3 @s/Sn8p$#9K-bf&*O2hf" h@KYf5'‚xCLERT>~}6ǵ#f*r]4 R(An!GA]cCX λӯ:?icc]BG />%Sq=} xft,0V\1u4qDZѽG~^ d8Co]fOHw5Gw 6wʕoVi xiC$O-yJ$j#fE!Ph8D~7qCmcru4S|rYH>!WL1@!Y!22A.V-># Ç[tf GրV:Vx7zT\ $\>zI9?ˈ钻YGSHc8˟sCN. @^Kʇ@ u>` )+WN\g|~n]7rKQaqפٸa rsTe`PG>.#;:~G-[y{i") cˬS)}۾J=u1"τ6xnM~<72o߼#ℚpݶlNqc=|h 7뵋F$DP z9O*z{OL`݌IS7(.䗁xm˥(ҾzzBEcM,WvNSײVyy9<.͛CuL65,ݻ$u/e89N& Y#d9{*@.@1ľ4N3F́h1zX[TXe=ҡIdI>:D͠徥nK .SVڪZKspHw f9L07k73E'%bލta{E&-G-m|t*¥bUk|M]HT~Wej.e ɳ';`rmPuY9,@kmQQaQQM!1YKn9ohX^Mm`hH34*ه<׬(*T6pGoǦvҖ\j"+@nrC}eWgHxL \e^ Xs EbQ^z#imrT> ZG Ns K'!ytdg/iS`3E"MrzH{<#1Z߀*DU]k m#/r#[MS BfhT _ɚ| Hz>,C1Kss [v0?``(X.cܖ'Dtd&@ke ·/_Pׁix<677kC0 =f!|\ Uyުnlj>Rxvο]6󗊘y_ktc#c4np 5! F,·y}AE'uanơ.>(6~-kOk$9?ZjD2IӚ[M#t\~G@5# M OΟZUHSk~pL=qSS̋iiUEϡBq-VpOlWU1evMvMT| l[?-pVo- VanJ@wg;pnntqwvIs:__ˉ&Yݲ k_ ' S1#ƌio%Q][9`hɛ.~j/S^.yįr>C6tz{&molkDTSUʺ_61t-:cy S#&S4!ČQT ^; X%eOȕa*dUQ3W̺$ٷ3~=>{|Hf;5k ,sigM;(M`[*^lp>#S2ψ'^ ;pET$ٶ}` NRύ~ Ǎz5ɧ%‡Ϫpidۥ ;2cfjohB&'5<ڗf 9TմZuu/.*wj^镴0|>(*%j@76aJo|u;VczCA?nIk̑YRbvAޫS+ +qTY!HZW@ֆH4ܾ|mw07vw˰zHMR5#bd6n޴JFBx Yu]nئӏ]2dɨ"v>‡STulD D2Rf񚥇G ̇PX_/f秺MZ2;T\wmx첞b4oljn`hDB*)u=`b1Ņ#H z>om~CC, )d[,CL<}{Nd:C@2^1K  ]+'‡ڦϜۡ|/gN? ɸU>S݃mXAn*OSzo22>u]@ZAƮG'IspÙ 'ᄄLH}SdA*/_ "%ww~-%DF6Ch =sdY?\.{)AVv6'UL$85.)h!T!h)CЍ%qH [KCXFR!Xzy($E6 &I(4^m^mg{L!<|q6<;ZO_ Qc~Mȟ'LE[}N1(uݷ KY[D:c)>=bz3 Jul=;ǧ cf&W*y?ԺdS!%/g+lF o%mj+eee.K"yRhIȠ1(bM-`G%[KbeAn.E &f5jYW8=MglfJ #3U$k=VI7aJ9#̉ OW)k*Qi׸JFݳWTb/tA>dFg7AkS/=9UFna ih@ /8˺z(n@HX'^IA>gT YZhpr#*Py:cٍ!S+] mhC#WhFoT휜 e/MZEOkqe!8S,'/YTTȔ*EJT+v&9qjԒ*&^}CZU%mǐ4|% aww#\pL/ZX$)%KA'=%%6--THFL \7DזG8Fc4R9xeeJ_ q]8xV$*#8bIXz#87163%K~lj 䄪oSAiiFwӍ>y-;604R p- {&JTɆлP^'b1LDzQ֘t|~\=TPs@P9ǵ2z,++T_J]UTyz`BaF0r}V$E i;eHm,+Vzk5Q:5"˅YuaEvY `ͺ RqO݁xm&F@@ lHR4у'*dM,Cb2htC˛(!88!kVE!+|9NqqOhhhD dHgY) BK,ãHLX(|v2 M-Ȁt TTԶ ]IMsy<.cffkC`28{ԣb@[Y.gMėX&HkԈ&Čj@g3pZcbn!c ؂yJ p˫ky\!N;݈f֊BդKѫri *[_bbo h&K$c3 dUˡPi$ E"g2dYyo,BWp%e2xD;QڼtH씿A qcw1M|eE4(6[!|eη/v kK%gNGŪbˇW lɺo"bM|eq(C{QL8P ~~2E^%^ (k[iu+rքU n[zWr0haÉ$Q=>2u-{91I6gOxydnjS!Dpmev猌aJ2> 1"|ګţN-TdwlvBf6DTSpe/ CPSG^OS!B+y2>^I5! xֶǍt!Z5L_bE1ԩ|((F< r Lh 'He$%-}b6~. ʽ 'sۖ%,OIll ^'a8r+N·p5‡ͺQCأ‡ [!RzJ/ >>)[L"C8\ʇNd8ʇp_n yZs>E_.5DL/YKoT:VQ MԈ)d) B撩x`\>-94z>_C0xd. ^`M|PL!Bo-rPGV\jD2p@(rG$,0f.R~A~=hPH6{ YS#8D(a'B A"bFonZ$RE^SAG>q}>e bǞ%(*<1D`alTd '22i| ;=˅5l'{rr#XYtCLM]+_=hpĐnL7`E FA1DАjfjAv !:.?!̇Pͨǰ hOd5,g1L-, rdyqntCpUE-ܰG~ˎ@sZVUł@ʚLFp¼\6H0dj\ j&QNN2>, N#V%o޼n,&Uz>B]#]=88: Ye>{* ͿC([Jm!0  lHXkB1-;խA1k[w,}q1L0oݷO/bb>nd gKéxzQA| ]]IdRb3g!ll *5|l=YxJvj|n /t%{9tA慰䉓_o_( .+&\6a~TH_>^+A[5 ˫cvJ'OÉhs\5>LOl@]HB3$ljL /ʗ>$X\RT2 PN,F'% 2r@E␀+qw 2gQNY%E*=%@C@ `!'@!PP"y Zu=>t2}P=[^=99:T䆨 ek6\}f5Hq܇`uW*O'<9j}ZZ0mBCCQ+Mh R@ mC鿝<@x YQ` llWsn[t?zMc=9Lmo-pq ?xQu_](ã~}?wt.enV8>Qߨs[Udܾ`6$!C3uP(n Qv쉱Tty֯NY]kQ+fuc7^mr RT+Og pUz^}k;ZA 0(̜'z3]R7-g/4#fݞVsYXO[Vb,=g a>zSK̥sdwoQH[?'CE >u*Pdq=J[$Eȋ'Lۿu/6vq҅ G,3=SB>q8vbewG s0ѽzlBW/ߖ\JI5$mJ3Meb Qc?z_1"pQTӯ9bɘ~l%>>Se7Nw->1ս_嗛wñ}vIGHYy遃iױ/ qV`=R$wr/O\ڑ*dֶ-&J'ϲjS]; RPY\J_w6Pe6N?ƨ59FU th/r"}{>T@Vthx8D[ (?w=̈B;<. \;*å3^1gnXYk_<0q}4%]2q>Q/ޮu 4ܼ|1ȿo3e,<"W(drj36e:/0=Q !Q}nߎoӹ>#1%AyhWLݎoz+Mw:gYI?t-WgƏIYCiIƩsگ((pHKߠ+R֧$ JݽUv+[M߳=l0o*3'J#Ɣ2^aQ0$ƔclLt]H$9 W\N< U/HCrV*p8j!AC[oC7D޹k+ |3O;fL;k1vAo4]*c>_EbAŘ)dC^r-7nѣG(㓒`_Ű:IgvhlBGGᶦLk3ۼ-Ií.g~ضcf"r܋ 3#rIZrZ'SݼXZļ@#Ycca$ ?U+&tĤݥee3g S6@LHd?a 9bH։Y]KC]KӬ:dk/By};kKFgj8V/n<2p)G>Zϐqz__6=E!4WVw-5Kзʹ'HprnkSX͕Pp( fgggf3n wR~I̽D[Kje@f[ fY_nB| `Pi}8'CD" ͲT b}ܽW-eoÇf'Nu agZAf%Q NhG)y@(FO6s1?߂+dm@| YNg0׭5'J)x7 MV9WDftMe)&)QR ljz30r,p]jo~2B[u} 7m }ECLJl !e0 #c06DR3MuF-cH7R YjzذaPBehS4(UUU-%T297(`yI,8V'z3|%a_=rJGua#tSf^5D cP ݵK nwr+}bbEo<;\D`XecqBU#;`, `WPdCՑ*Q<`?(eWFNS¿<0D_CZAlM{|q;!p ϙ1"BU+ǧ6y?j1<}b^7[Џ?NNv-MՕZ(L1I"(3A5+vߐ(U~S"a%~ۗY ٳY[y 6a#_{o [zd?gIF%,]7_[,ruź#I̋ ܀ļ.3 &!=fl^8Ǎ̖qs 3-#DN( +(ŃDm!xyn٘z oA\txl˰v-%V..$If )EZmJjţ@DaR*$̄E&(PX(*m۷Z#ǞUJQj4fHNA[aC{]+oL"컳g?ۮ}$i#MQS #:v/$I };0t(IY,{[y:߆MX UcNx~c )=F 7 HPPZ'4|cW1 7LcVONGojҦ+H(hV];BE)ARI-RGko$4( EZo0?~c""i.oLDęfk9#M7Ker 0^`]-E$aS牰~cźtfZCa7}:aGHۄ[_m$†`nd"Or/Om2,jWjq+6ݽ\DJsZԀKw<@fLl{Nڰ墪iCjQeҙ(Z0fR4M4;zmYDcXu80^=Ɔ>zRW ;c]YDcjtGQeQgGmD_Itg V3""UVUR Ze7M~bbmeJ} EEy4 T^I\r&]n#ea&4T67)w*_ߐxMƂ=k9\yAoDl~c"\9IENDB`muse-4.0.0/src/doc/html/single/documentation/project_my_first_song.png000066400000000000000000001555301404105311000262320ustar00rootroot00000000000000PNG  IHDR?Ð"sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleCreate Project-{_ IDATxw`E?{%ɥC'(bWT, (`W{E P+ V,PH =?mKh_w;<<3;3wgvbpEz16ق ,X`Cs,D/9'7rlڤX a7Q-S-rz*p?:B5rXQJ=4)|/]aįk6ci7= Dʫ⁑O}ܥ]kS @AHb1{HXX.C7e$cHQ$.#$1pFy C/ Z*1$ Z%A7G3_'I+L6jQc Ò:Ch*Ck<%!5=mkQ]~rys-'O2+Qfױ<+P،9X}t/Z}A_l@vNr ޤ EYeA p{1^(9Y%l#b19ہ>gd3-LU0ư ѺQ I D5Td9*+hl5Č`FR&@|4'>:GnP$¦K!)݄=eg!>+4jWӟxFƳm&.Aą(~Erm.uTm!l_f@VW:ۊW%z T!/#3rr>1-"IGEeڵ#??A\˲Lyy9t:h"xzM CL+~\<3?tP$Ǔ1ӌ}}=?KpyF_|k~Wʏ)Tbdb_fcNlU>LD#)rfL<b$ >TG'O|#tĕi8:rd%|>+*h۶ :vT]唔(6j튊J hwKJJ 6~1vz1,st]mFnn.NCӬ@YeS?gWE5Y2#m*wO:|~')Uy?lZZic֮ 9PV}p:x 9gPr-7'55s-یSfCT2樖1mSjs' r PȪUP\TQ1 &h}399`)}D4GLF ϥZɞLu1EW~+g+~HKUOhxңfDz )&52)H<3ڰ}9wѫW/ؘ+=UVҲE$6ƃ㡬}č+Wnc<3)*ZA]k}k{Ŋ4oBz،*T7syiƗvlשeao+oʗױ" wB(}vvCЀF< @aa7JKKDWC}=S7A[z2ɱe c44d1 CIV?_9ЂgJuރZQ#TG_eT!1e00jH/zuFC3aTJY9ږ,G4*8}xr?= za'>p^ RhcYb:1o;/'nL_4{y{#R*IuJd/+NtL^X`9jG͋IQh5 m%'I/K7I(RZZJ]±lN~>H|v]vll0."KڵTZXHIi&mvFiim-c; #P]Ui Aֽ;ulG?:{a :lyuַ̘ }) NIWdiy={12>dddy u8\iTWW+ć0oeےdT&GaGvdMD[AcBV(e3Fn\Y7?tEt%eݯ-/qx8bu&׾g-I"JQ^"# t" U>:]11쿦V{b23l4bԢN jq&+e 7gQCA3PДWk2,N$)vWuu5.Kkiݪ5ӦOk)bcjZZ86jCNOH:Ytm۫VԶR6ǰxۍ/H|~In ]CT<~;w& fZ+Fj,ՓkpʩйK] زiN iޢZ^O?w}-Z<挆X#nmQJP*d]jdGM51kY5!c4LJd'@03|DE~_Z4 dä#oW9\3m*7k(>=21A9<>w'p:a?7ztgȸ"z2tA#bYqv|B&Lj^Q}Q=q^W{dS77k{0Qu}nMm96Q0OFȁ27WƖZrM7ѡcGNqK.Q-)//th]N摸q ] 5mf1]x!~[;*Gt^ yV⑇IO?͏? nٲv#J[zB'%IN}pٳ'@^êUmFZr\NB$*RR*ƹiFU&-5*$Cs4 qXʤxe*$HjʠQRMFTI3Ls`n"n7KE5 *k*sO (Y=;;Ɉ6DIBddIBװ4 ~>8li'_qӥ yǟyu:?VH)CsHt:Vr?;kRҨj= ޙ&W!]XpUطYm3n䖏t^p7׼O.AYԫgTk>o?/݇FʦKmgAD%%rWLҒm4M)/ ^jHt%_edPܐvYgdM[:Յ8vr:vK2Z~)'zOJ&J?ڋr8APA Px'G[nŇlsL~@NE7-8P=-2JQ&?%ˁ @.f{+SO԰s!9@r_H%:vH)U^ Y-i[ȕmUNZw ):YJbND%*ԐٱYD ?!k|MP ::(r+B$x5sOc;&/\2$ĄeTR6<^xys܁x!q 6zz}Vܭdereӵip|ae]exim˦oޒwޟ7sPxe?oYd-['}ƍ_}'r7`5Nz^- zJWϞXz -=ԹsFUq:\^${6oZJ$q j#NCq1KDEW1*IÆ@2^($֞oem9ްB}Mu*޹C(/X5e¢19Tm3v3FA!SUl dd6 uZm[GfBR&3ҙv1-]@Elc'#KԼVmI3/Isތ^J7V"O}Fj;k uxLdd͠fRĶ. }Q}J5C}%@!ռuBZXEDhذhڹq~"P824f،!+aL|@&$͕غy#uu5ظ};i.N3H!Nteܭbӆ ro=Pv\ Yw:bb9K6ضu^ҒdeɥNg/mO>%Kh,wg`ӦLJvh0&?zxeѝU]~}ݵ ,RRM9=>Lqd4Nϙb|h:EB9mq];1ծBUeI=6yy,Z2ThEyyl6@c eܭ5+2lZOUEEL- ][Ǒ/6֭a _Kma"_S>C`Gkش~;9kGuT*m`sy*ΛO<VdId_*M6=`b+N~f%\cR]/33o>L@944.k| >TDuOKU9r:ӕ*phlHbO!s#Le1Ad-U- by><84] _ځ7I=2gq8\Bvep {NgpA~T@Ingl{L8xθyE 1v/-d vڞ5Ϫ絋3|ܓl/OSe_r1)nǏeqqsMӨ>%)8iKѭŁ4ucr&J@O.>a1ʣSBcV6*Ō\Lf#I{xGI瓖%KUqaCz}ظx_gfg!d;5#Kq?~G]*%`b+#fyQ1]fHI>!TWx^KQȒGoLvvaܛ/B[Vf?T+mJ6n"; 3(3o_%m,[ }zz{}fǙęq{ 8c}fqvNB 1v/0dU{` k#VNL3k'>!M#S}O[Ȕ|8Ɇݙn#*!~dύWoi't&K>|^?`Ln@(at*xŨ)Z]>QbÑ6G N ]SEt} 蓪S?RlNG-D<P^ɏ%PnX7sGĀUrGHO\<ccNm ]9 kD甙sr^L0AEdR\iiK?6Xgb.YB+޽c A%KtҸ1}x<^:_X }v= П:|y,83%(G+ҨزFLyy -$@6A ==Դ4Xj5vL|>IiI)-Y+-zSPPá{DD$Ȝ~$H?j$HÏ匕4<2k+2d()ӄ,E?߇E GrvYF_Ի8dY ӡ+R̠lLAȢQ 2 (6] Gܬe,g>}c -.7J+),~o0Ϗ,KE|C2$G)7NΞ'jut5E@LR$&& aJ%ő=cYjl /hv@#C[ {:E{&tL '.92(44UddC0i"?j%f$Q75ɉlgHvbr'< L!%/b`",扐B(!<-!CQ"-3v\m=<=B##= }n }c/ե&M q>Ab(2Y4ꎆ~Ax $5Hͦ`/IO*H| 7K U@d HҶDee -341 {j"fK~U( gze(ʡF" 尬9RI|LjPEXGޜCtL~& XH1KvGv4b ^ <K^ =JսKzBs#-p~ Z01׏:zʌ%gƏ!ƈ\D5*Ҥ(L5fTmO)5Nj ";ldnIGROjF˓ėx7k+isBa\=cD7ɬ1ىk X֠fq I(Ń>h iILbbn{i)H s$(PxGb+I1^J2Ch Ig*hK. %G%kT$,;ilEjyUEѦVkGFH9hP AY:e#&Ɛ-ĕ>1/%Jv [Iט'<}Kxt%׷DOmcNsĐ H%N"+ILn\;XObD(JOMV^퉾 R_ ADSdMY-ym5Qm)D h"q$b D틉޼LL zp%gL#a=+gQeE:7GxcOba&mC9n߯"*ArT@AA 8)ˎ:*yLm@t#C,!-yBS@) %UQ~ 'fOܾīET/XU@be+x"9L2|Ɛi&DIHxҍ!-%Lچ+'lK,J?HyM-;v24eմ > [%CQ4nu(b(!*KTe3entXGUN\<򥅸V Nƒ&f;Q'GxDD'e ]&$;]mm!}FD(W"mE4E6 ԺrLn22Pm)V@E &&HZLY11h T"<'HO90⡱DH&l1_ډ5mƊΕs^5;9׈jJagK̏dҾ}MM:&97n2@6KnKq4vI@RD'NnBbCI2%Iڶ'W4L6:vbPDXDQ~Ӑ#+Ӱɑa9%QJa-1 #ꤚ$bMI{j=֯)P6dU(ʎ"CYR$58HH!H7I6mha\}6,{j&MҒݦ 7" QDI=LtTBMEv|Gl ' $IkӉnf%o$X9з̠iH܎9{2wWj*wY>O>3χnWϞƍV>fmLImx<J!|8ΤlB @nrU(櫦JVXN^5,X`Bee%E+VЯ__rrc/JϋmT.Xj%H.RsCb!e T ߸ХsW32pւ ,XHuu86_Au*޳7>IbwE_TrVlZ4o%">CA]}^ˆ^k,ƒacNdeg' ,X$ԩ36nЕE%%%t-,JBE lڼUVSߡ=:uҵr\v{Ruh]C|Ȳk\e %a W` L3u%In@p -U˟-χG|nhٲ%eelظZ7Uu^j}EIV~@,jZ` Bsge[; nl^ݛϾkjԉsK0 ^ΓOa#͓ĪF=͸pօT/oD}[q ?i Z_WP껛8ilj ęyoꐏwď7`ൿP{!OrtTj0Bxyoz Oev@*2:;ͫ1\-4=cqCԾ'ܘ֮ N -0 ,H祡&@@!͆ntb#r\tkmh. EV~5(ˀ♣[0}nA'=U)/ ol "kB&IWNn ޜs2GHjNXƼe\05.0h I }Fߣ, I4IJMCF%D@0_jۃԏLg'{%/m-#8ImYŧo/Ƨżv |VI3 u ~}p[-Xp@ tn',|j+$)B+@\v;,N>f02x*Do^؎~|!j姩^0 }/O0җxN?_^Bƌ9ƌeSih`ӗpKkWbO8ruO3g;DsXN9gPnzlU˞+eY}?}:g3.WeWB\#6b>r]?c+e30f;j&N_FueYj l,˘[hேp9y3\&4_ep?؃ܫvpBGsu5?;<% dР8xaC7@ Лu Wc԰Cpy>p<1ѷБpoX6Lq̉ȷ;"kr=] }{pcuՅHTAp}W2ù8mPO$ipHVdd*}sO0jX݃zD0z."wsgP>ՓCN+OZZ`_ʏ(P_W'Hzn<9~e˗,]168o IzvEdlm`sn&28e+$t7>„x_CAH<=371Yc^gy<&?; Gr&kKJߛ:fy~WV3=>~: 7R%➧0 <:SS~9 7=z+Z,aIu@H=Ou#bG2իhC?'{×ML9)An۾,ſ֨<216T‡nFX%^5c:m}9@01K+N5`oӏ"c|VFnvsg!N'E_XMM7,K;5ÎAW̧8M ep]mqIV>b?K&V9xV~G..A RY>` B-ѺmK~ ӿ\ȴ/~X[IYjG*3Pw>x[j :}͚5&38!t(dnH{\:;7#=ĺ]ԑAȥ==ty Z|gJxSIѰɕ+dFp倘"!`ny>9ozrGq7( "VYنc HʈdsbCFE*ڐ5;>9]aasر}9=4jJ,x=pȬ+}S^g'+7-lמтL@nW RF`όB2|27nKȫYW-ǝzZlޑՊLyⴁ!--ZA.GbqvC|;j8rŻLO/N e29}>XX``L[ŗ_(JxC]6kbл g5Ñ%4 xɤZGE-|WTزg23ɳƬTdc- k {(߶ykUɮGsѝGs~*Ww?¤9+FZv jwx FV^"ͤQ ݆Fp埰n^n'eRX)6d1#9r|0-~ɟN3Hq,_YB?+/n7b'%ŕI{N=w˸K~k.᪇ݍ-Las+_d7'j9wQ[\z=L|2CXS!|h1dCHu7EͿl[`P^^KV;2`A"W0-x dA/5ݪڛfX2|/׏$dE p1/X]'5+gGg-Dx~fO;UK݆PE&KpӮ+8ggt8Ma]/ 7Y>s2ͽ_|?D߯M_ sk}kq 2NƧspE~Fė@0:@Br;Z 8b ~@D ewzKR8ӳG;+џ2|q3_a-Bü'΅b^̣CEr >2x󺸓/;Q'-=Z*H=?,X#0E@z̟P7ƗѻhXG;F?p&Kc}:g6tnG.,#er) a5qO0QO{!n ҐC_y- /O Ƞq7r~BUXFn%셣y>=-efpCyv{^Bo?m{Y0 ˅&Pk75UUs$Kl}5މkڦ _zxAS>laqztЁ۶2o\rrrhӦ۵'=] O{Y@Cjj>ARyPrcsa"*݊'l撖FVo"--|cက uϑ$_.T n`& IDAT _l fjt |Sox`aBk8p7"֖W{oIJkȌ$.Mi/  n'k#3y,"cBb5n,X0X=paM{y`ao!0ZX݁M X$ł+?Xs, ,C` ` ,X"v¿nc ,X7 eYIq8S/zY֧w?C=zؖ$ QqeNvm? C>UU\I\-XgQYYIQrKNvi=χ}x(.ކt6e U=b=uaƍ 8]ݱ`B p8lX70eV؍:jvvc~?5[sfԩ Y ,h +;Ν:iߦu٧/uub-/:w7Ke ]T[\ddfk7,X`,ͭ̊HEE.#~EEE(XE`Ǻǂ :$In@e9oo[,[_dnG'ˆmn!BmԤ:4ڗ-~j~/.fԹ|3w;|w7˞ / x­\x:,ο~s6ӃDwxYu:{REny|UUW?Dz 'myqXR>ćyu~*ƍQwc|w2 nF^ϻ{n ǜŵS~fsMp񸳸ٟ(~V/'͙c欳/޷;=߰!N?n~K[w28_Fϸg ؁99lwo`ꉝ9k _<7zt/(~ELcCqҵ_q%1e6x1>Ap}vo擻G1ooE!gf<ƱYK; &-7^cΠ}߫'rWt[dhzP) 1=N7'r ڟCG=ȂrZ4KIq̙K#5e6Ԃ==U l;-?Sr̓|4^vnbaxm fx yoءL?p^ȯy.㞱mH{#/$>hX/Ҏߞ{Pwlŝ}&ޞ=Of<[Ԭ&$ܷW7՜Ͽko?xbښ?zWCӸp S?vf;y bH~Kmc`ӳ٬ھ;:u#g+i<6 Hz6LnI.? 6̕/eoxj޼l>)o.)KP2c_-nɅ:ptx"z `ˡJef``BJA]VvAuH 첌lmi-B<۾ceͱA{,.6dRxX=6]JAnwzw3;ėu߱zKa(Og@at)-ƺV Ei)\QK/b#Ӻy<4{=E_".>!ҧv+-p漜vN$ņ $:?9IBj"'v$4湆%NrL"־x{FshB`,щ #X95vA²58:+lB8 bǔ~E5NNՀI&0}P)$k6AuNBh^eֲ7<|sk| L;<=NقziLpW*"gR4Ix虱*Ag"kHՙDd3bnIw'ٱ+9EDi*A9d2|D ە, xV5`2kCQnTt@O?gcLA)M}Ո%Pu1Mw7f=cD׼$6" -^Gג-枠:s,$Vh^E0M~rCX:#_k#=kvKbSf Vmx4^1+9]EL]װ C_nqT4k)ͤppy0z|l.-JQ8]@A? OldDV+;I\tMG gؾ2&0ssL"*Q`~uo=ݖMEաI/jT*7':ý- Uwzw@خsHfpgoG}H=4ΧW#tD88 v>,S?R-8-Z"gz֧#$izQ`<4'c q8QH96աyYKUIV})r#y䨪h=wɡ` 22UGmva4=]JViݶS1:dvHӻ؊V-Zi^TIauxoȫ,5g0LWIf0#nV Պ9͛)vg}X=:mhoMzϻSrc,L5|vGZy~YW4{E$BߤǸ=t1t9BjP5ڴgE|aMݕ*fT ޥ*7 `dFw\Kh^xs?W2!8dTZY17F#y:3t]_?q.^سՆOdЂ|wXVk2pUCN#Bsz=^'R{[KKТ](G^!OsSq^e0-]ҰIV@OC{+mx7g.Zao$+"0Vg)aFyw!#ߥk 5`45}DnDTdTHXWhߺ46DHUUf}ԪS`47ؿc>7,ڐ_UƲJ6Cu\uB#?u{Mnܑ_vRL_!G?-] ?E[ȁ.jSwX_Ĕ /e=[2㻆n*|9+Gv3V {x%/|1wyȈW.i.%nr"TDV|q^e9S'b=wF􆎼4"+7n ZLW6u K$ToBboDN>{#b6 JNd%Ⱦݗ$Utﺓf >{ ;UK?_9}TgbKs!99?mL}LmuRlG/^uІNr&] r1Nlߒ{oAX*0xT7%}(Mƌ>^&Ug#Iq*&S.;eCUUɃd&$$vg͊bۂ{@_VS'qAq+LI5$VԔ ɋbyx D")aMN"U"#1͘Lw>yz(Jz#.Hn"ɯ0ͨBLL4qqZD Ӂbi9H,i&<<9D"H$w'D"H$'4H$D"}CH$Gi8N[ƠףNaSsD"H:v@0mn v ڦ]ݓRc Q{j!DOD`EΡ]0ӱvq S|,oByQWblN@a>x!} H:5 7']+y %15s&;wLJ3ga6B>O׿p0UDQvic:j,8S!1dhӧawːn;h6 >O@acP0s?cs : Ѷ_76e%u{[0(N̦SQ}PlǙeqg0.^W`7jEݜv)hڔDc'SpF'^"cIL*'Z΁tOoJ>Kzz.:Gi;%k1{X0~[N& '|Uh7'u0wRѽޟԃ|0~fN& ^|w~cgx6KDӓf?xIkI 6!yCeE!kezNFZO. Tyvu!Iwpƿ5WNZ88+^(O ^1ɓz~!WuϜ@bK>dKddKpSʡȳ1 sVCFPЋxlxΞY>o/׵D3a%,֗'!zVʸQ0W_-a(up*#ghqݗBq9qG~#@-Μ^97r%V-a+{~<[ n㓽|Fo1Mx1Wb&|;Gd?&K{}eK&ұS=_x(7 ~$D~#s̥WD#ٻɓ'r=s*FGۺN!4+VOjâ hXc,xa@r j>}ƃm.Uy|2rʯ}10['|bZɚtT,>OH]xhM&xmmljv 0Vu&E̲~mY6v4q)rA!?D d`ٹgNwq:ڠ;w&9),Y2,~##x`DĮc,ߜBYLnymsP+-Aј:υ* X4i IDAT\C%&4"m,(3!D5)D'^IRT"xbS 81,J91? *^hf(ר+uEX/gGRe!aTF|egSysϑ3oC!OFP?LyujŔIQt(DaO^=Ǔaد95BϱjX*[1Q|f5y>G` ?abiN} 6\I.!,eSe(30DRvuQ&ţ7;OFg R6[j7f Wa^ ]sd)TЄSѬqX5HfG@)\i #ؒRq^5"/oq BX93F-8p"]"H!:JLt @s:QEK].s#՚DvM@dd$:-L7)DܙjۭQUXv[Zn,P FQz>qO:VVAaSSP|zJVq%'e3o})Z}8U.N!P^ݡH]қi6pyFk/sχrp<%k|~TjQyEKZhC5^ЂF=kR$s:o<.oO$>Zœ'{)&CߣFݦ5b$߸;J{D QG,D"p:5._Lrr2Ŗ2x#ۗލF#[ooOBz$H8-Xk6T_H$N'|wTC|yV埴Dx %*yb2 !6[f}|0yxR#)r#H$UU͘M&&^QE+H$wJZq ~DED"y(p$I$l(z#D"HSpoD"H$TGO%D"rK"H$ܳs (d]qF{2ܤp)4wB%\ !\ք"]@׳H{uOFL ۹u3 \"+-ɝ&M\LRg FdRfJG2-;s]a+y 1-[5NX):oٵ"F"<((7M^z&VpHh7͍PһMx^z~?.ƍn\NF˞H%ZT%]DɲBS4JYIU Jzw&svw63AFZ!wyοH7-eΜFfINLbGM+Q/#ȗ-ӳ3rX^ݾqV Jևrp\3Ȝ9gp~;[\{Y3$k,HC3oUsq:n ~_vo~=!=(2.p=.ݹ\;47o"|3wPd{_!m_"^;4pg}i9n!ǸiO>?7 =Q%D"HϏX D"H$6p<яD"H$;\%H$?GAH$D"W^##5?D"H#=i8NǣE"<zt:*%=d"F"'v;zőH$9_8ݖ`xH#5?Gr8RRb}"I$U8z/z7s:`ؾ$}GOȖc N5iG.Ty&e=/WUj},q{G;ߩJI0Muk琟ԓ3hR"r8C̦Tm2ө| Ո7Ǫ 4swcsկQR“NKCz,?pvH$L^uQvRUpmȄazLqc[@(J.M3/GV_:g=oRң p>,dkdžN4vžsWtUP|Բ(hO`ͳ$Az1$I2N@)GCTqyoHG8w.[r/ hK1^1~cɜX3OCR{ 狡ӫG3x/\ &\=YE-s^؎3j=8~[1M6//ؘSG9&p": mypVv3y% jZ#qn:Cp"&R"{5+zKzMA//o={f?.\{n|$&MA ݿK$@a1w*z߸\c4!&Pf-YĒSHD6N/ s.G/\8 ZOL<|,_S?e_G5ˇLOO6Gdoa+1 gWq%K/f\r NAQu)'[wLcy5q]AzZ2XYG|~HMEUl>Ơ|QZ/Lx%I|¢hz~Qhh -%ncBMLXd9>\Є@9c/yMb,R}r IAk뵄۷͇%ePV_LL8ZkcN[;KX'Bѣgђ1sbVsWbD8hv-M;o!VϦe">}d!P W;D4>1?Q$onw_!x`}[&L;sc?y8m$΄ 7|3?nU#~DN:I1߰qV~F䫳6lk>ƮcLi A*Tt`lKWy;S_x4^^ɬ_bp/{m| ͥcuI1ƻ6s9gFy5X;x;rIˬk[>Ϻ4(aB <KrP? Kt#h% P~8~ŢBm NQ7sGqk >g5,Q`KHcc7OѯRRR8}bbcф ::fO7<#< w|?kk:WǮz^ 3vrrΟ ur3~[8;wDPUK*сA츐J9QWC鷘h4 ybNWX!נ8Aۿ@T3!/5v\ 4*RooFp툢k)e#N+*8~g + C~jȏQh胊hZE1 #+q6B Jቆ0jkv P1u @8;Ҡ0&4<hП#! &9zLA:T , ٴ7ND`):cwG2ǿAr.,"LSZ]=|kP'XJ5i ]*@(s41׮FAC)ׯƓChѹ&y BBͶQwoXr2:LC P(T%J S|֬U{>рG@ x_KZf ʅѕDuN lҖAoÙej4ax{ᥧ}PC |8{yuwvGQ"PˡiWR=oAQxQƌK`zNH"nl$=~;9< Ҝ'a^჏ZRIjb9bHBWF3aGMI;%Ĭ6аٍXXc̾҆nO/=`UZcpYAz7:!)&0QCJv&~ٽ-2+T~qTM\JaIv/uEZG JhJJԲ-p:5B(FTMP(4x4K}OO=XJoBIEQ,R8`wuNq-TՀYvk :EimEEu˩ck,BŜ p&GƑ{pXQ EQA3lZ&uy[`@*=эcXj&"9oe=u4),9SH%!O{(۴&(Wh b4t*#>DEviȊLe-Y%6ن1&|A"Y|ϊGʋ\ӳ2K5KQ_tixˮĻjme/ʞj0w@`pEh-5)$;"Kh!  ?zU M@'FR(DjL"p\'hX zCh'<9#acg ^42Iя̑hX'.3|,F,b lĘ9oQ1|IqT2e*G/9.M;D(!px" ҸS]jL{<)&TIAdya !Hdl$$z0]>s`L͙7nP֢vbc̀a) |5 :#GZf2ł}=oFUƒͿ-&X4pq5ޑELඝH;-O P&^_#qI8%5d oHlqHPiq%-Lg6grN +=D2דz#dӂIIIi&tb:vDMP%:Pt](?L8oK̆0jT79jL8UeR~co؅F_y~Pt*ZJDq[l$oN4A:k-?֝j:0R[|+|SغX8 " m' eUHeSWl Rxvt=;/'7sDj!_u'~3pθ712o,JæE93g,K_! )8[fdil%ھNdfJJ=O,qTIs#ŇBNcIʹ-9h|KxPwx8/úC\w ɧas8>C.|YU'oeWHgj8[?m$G&sM.P F.jh'yWĩyP*}ƳVB`ܡUp'ei &6-㙑 P~+b͙̩o2d\wzPjq5;̉Vr~2];x۰4-osk!Lv69Nbv'YKW!3s4Ȧ-'I=zͥN~KfB|&a ٓwHJJᮌևxb~QR2x`-To4RpzX2:RŨӡ7Lj1`e&E)6,F^0ԄV:Ew N5Bs9`W0}x9D9+ɌY`GnyD'śow蹼n!~!%r ]tG!3eOD S*Y/ԊOz9}hg{Ə{†X?iک$u<$W[g =>Ju YhtUhF37&^ȺZGlo2J՛7y)HX-߮Rh%.C>)xeL*x4 5c<~bv."@\˩MK_0&>xcb1>C_/Q W/g]i6ѕ^!__?憴~f3:4bPO؟/ /`WT>X{1&m<;hI^z)eĠb ևoNKݳgZ<ϬPd0MF V>]]>O[$J܀.t޸]dKW]ׅ{y{o(n>@Xhs)R.T9wrXҧ4494-KF0mu95p^N7ݿ.TZ$=s+tXS2b;>7\TAP鶴㧾F=<8yq}㸴.r4C]a ,}=R91ϾⱵEBݷk(YܯH IDATQ#rN bދiB(Rc\=c蹄cIܼRΘ,2m^zHTbbb0G_@cN<r[D"y0:K"Oh(# 33gU_[6@$SD#DX ĊI$$}#dH!D)&N /7pm3",E,Q)@"H3B#7k*fi 1!xڅ`8`WD=QA.H$+N oh$aBm$$J$+9H$\% )Bo>1,YH|; ޤ!?9#H$ &wЦvMh8o ˎK&Y98-O;&-/6m@Y|j+?KDZFúkېΥ9s;>~8#b|4nݟE{WѣDk8I_G\i nkcZ4hsfa˖-*4rDظ{}2ܢ-GDލrm?ye\,5.gѣeSw!)v~s?{}Z̯LڇG>KxJ'sϣmݒ3RO^ɵ.Yԛ.ɔ{i{Qv"TpF0q)nt%ag2/J_|j V+n(Oř>bғY{[IdiLˏSn-c,BspZJ>fM>6=OOS.{:ܹm=]m֎ U1/kYQ6L]̎ 6t81MޯS%ݘ9Jc]fǂ,qT!2{˅L(h)Ht u:}з?q'ONYӝ\<,C+0cb5xc?ޮȥ5 z¨q3dϜGF|3@kld8+/Ƣ7ᭇ1UN̵AE>L9 cVLS XޚAyݕד 8*cЧo!Oo3cd Go/Kۘ=y ?_HT5gߖ<)19k~ DR/3co2ؔp$:Qqf*1uC]wRFՏ'V7)$śKf[9i.WCj. oc #7%NǑ*|(m1/{\8pn*Ы_5uMgvD#j&N#V/Q=㺸jivɬ~)VTYz (.V^di,"ѻ( }*|zwM~TMd]Sj~l+NnXþW\CѼuPrK\+-w9jB@m=2wCw}xAIzq=N/QO/ɢrѮ W#!K}HKVӻ.,ı}牍– lM93_zu[qo*rms.0OŃ&>ێŶF|0{U!d> P:lwsH=ч'SS/\/&l%"*Z8l^3˹qy/D hQ;8|;M˙׷({M`_ocL;e=\cKte.bŲ|3/@yBm0m`5l|9M[ʯ>N:m#jAy@ ˆWa}#y,K 兟єl8j6vL__-`d=}Nrb(f9_wqR?3}t*" b`vPDEnEQ1@;oo{%w{(w7yq;3'gH_M,E1KF?~z"M{s1=;xG xnoŚQ4cp3ߑ9<3\X+~΄~S7)yo6!LrK׎_Z^J{aP|7|gL&^yUbJTu~%l0 ; Q &U[9Y|Tb2*Y{GQ'sǸ3?0w+js'o|1v5'6^l_,EjeOp_hN/HM>=;t~Kx+֣Z"]l~ g1R ie捽1@L~n>K'Y7.ڮγ_g8f7n|_{;gc@|\zo9~e~|\+oj{ruKSWbe?OGC(g^9~ɢ!<‹L{}#rdaVٰ2f|7s*p+=S{EY:@O! ßW>]lnjp1P9^{ўA=5,WA:uXo"^#oxôu!b5 JӁ!w9y3l2K**QIڬ8^pUMX^$#[N'8Br^,8L wNwL XIɡcS9~]y(X䜖٬\zOϩ#^˶smлylL,^NChP00S:6p' 9VIȚ >VEܰ//촗AΖXgg՘Μsf,lԘ6<ڱ8KA~̙3kx1 +ʔ)<Ӵi lT$_K[yLڔr``n6̰Eà'g1D=G/_}2(K9Bd~vlos`[s൳c2\I1]8lF4NfT&[ Wmduz}mU%#CV<(&*vӰ{lU9,3fΠMKc>67u$WNܓf"r֠zs-GcgkӟN1 b^hM=QJb|ʼQMf&J}3:ޓ({OƑp(y`Sn>=Gϓv^yhfk SI ah4ןAUN0 S@%mGcj_Angs y yAbDafáo!ӭKI37wt^ Wf.[4M14i>͆s6AAѳbj([aa`mXSJ+D?y>~. iGzFT\*A~g(M},\FlhLIztx6ؘ? PC̉ԩ QF˯s}7wqO>$@n3+YUj8|;IuQD`Vǻ[f}9܇ t $.Kkݫb+rV,>BC'^s?N'1 s{n3\ `w(,e\mOɇOkd\RV)VcIS|^F @JFG' o8;F^:yl3|;VTb mTM݊Q Eľck͇p(wTxȫ`3 I\ћIH Ƅ~1%dF9pB{`xH;Xigxsx~ⅴv(}ø[z+Dd Oz.~66d8:nHitv= +>x^:t@.Z 7n5 @vJT%*lyvu4 b0s\wv}Og90zhr*NKSl?e%)1νo n3a~'xa> M`Iti%^qe:)].2=EnZt? DTb$wdSpRnG|-F~٢_G>OxTXŎ-ޝ$F8X$OHζ]J'D$U$UH4hNJ (䤒!Uc-"T,y((x^zJ>KN+~2]`AUC xoYϺ׮1jܭ̛>%t#,~9:Hc7KpH+, 's=tr~I_[!{2gi >C'{<8E,ըRi.uP׮/\긳\U4w9H7Ōsv0@Q5_.>\n^ @ڪlwRMKEe+-²+~ dC/xiO߮'3`?njVůO-#I10x}0u>2ER'KZzY{˹,8xv9 ~4>ޅ@:x2wEԳ7#4׍an@vvd/=3p"O'ZD]b4vix/{B &o< YXr 0ܻY ]JٟNEu"[%Q4]wz SCF<8WsU.TU%0ߟ7Zhy#zna`D䌫n T[ߗ+oez oa =No0vrn(CV&tR:<cGGЌ~W\Ud&{пηq˙pwtgm΀f&=r /8G𡡂o|+[x0t?0˧>ŵyP00¨. ?MySyoi ZB.FG*ιrg3ջكri%5>7u ]Nܜv}^``5D2Hc1-x~?hgNj/=ދ37`L+@QJ{i SbOgpχix{NYmKrtXgrev|ON2A}%|+Fx~uyp .KKotRG|v戧n~>*|a¡\Io0KcV Z#F{_y+FߨҚ^ бywTIIyVC36U+r&u&e0Cx~4?cSFZ DtnC4^ ]75.1 OM[|*y uiҋS:G/QӰji,M>YX]KZbaQǫAUCӾ33gNֶMd;]wZfِ&꯭Z\{~,#QD膂cwry3C*%Jaf˄i[.#/#'dOVe [\]>I1~bUq"y,gḃ]xqʥ46qo9u<&ppw޹s?ϐjN7o~7䉞l s TW&0?jQkgZŁlmGqEˍ;c sh8EiCru=W{ǭO{y@ .3rzj-~7]ǖp^TJpMmhPih?fDC7·dPϙL[+44+蠜ǠdmxIHN|gq~:?5%Fy2Jv*=Ws ᑖ ԙr-w(^AګI"(uHA;3Pe)$4`V)֛H!ıTY+sg!j8 U6@c>*h .88?B4CKW:(eDA!"%? !ʟ0G!5Y(QO!*646B!D Wtz,$Bq" F;]!t]'5NeL&4MCU+6sv+0o+ݰU/bǎ( Z00PE^U^U^U^+!ӦUKbbb}N|>vjl{=wqa6lahҬ6[p#{T^|w-x++;m۶ԭ;qqU[B!*%3#kVӮ][|G5iס#nW.Z4v:a>?&SH~Tmř]LnӖ-7nXT޽{֣'mNj#"kB!DE9_7ӹS2ߗCמ't&{o~RTT$ V1麎aҢUk֮\A|9tLJ3߇+/ߏ*撓rc`upDEEQlϋMӦشaq3hEzo/UQhѲQeGB!8iٲ5=H IDATl)=@,v;9Y?Lq8" n{}N'͚6-u>h23Fӊϵ]AvV:vFQ6;& gnVh, x^N'2IѴ%:6>ݘ[pڭlv;QQ]B!*)2* Qu]qut@7 ݼVd:|~~?>MvV'#3{#;Ϗ/P0p{\^vn CЃ&6:׃!>>8l6*68pޱ݊ V{U.]BAbOF(XHeE1(J!5MԱs\o۶aob挆(E~R[x^n7@EQ0Ll6L&11s0? .~WGx>|l{i }uRU2M+qw k}!e\֚Ӟڀ'_gHa||(]!=}snQewזYྈOW=OOg禡(jY)rydm*U !JU#.r3u_d2Ťix<"#ڿ'r "-(`~?yyy`{#E r $"d^cy Q٥ÿ3/ #;-gѻcKZǭm &r퀮t֝]O>c,+7_H}-3”/b䙽rLߐLg_?r!}:uG.q7W4EHgGc|Ox!<{ :{u&k\>йc:̠^cyfһe"cޅt?^{G63Ӹr _"~40ibif{ 2aJ=v\+y["%5~y&m}S0֡#I;xo⧑w7?sVhfIØ\sNĈǾgrgqF$;1_FAoՊSwPfa7]w53X9 {v}w-}FN/d'[f9=:ԩ=;ƕ.P9Q֥3y}ÜGһStlOS V>grr$zÔiz/Y8yHZ*c}>>gnXf-F3UkI[zd?n{5\-}3nt!/Y[xnz6iI\\Х=#g]yW.ϒ4<κ[R~) wO8mۼr֏ۂ#T\ӉӞXO#9l7JRRWzt;k'-$5t{?x:?%8`i ~oիY~='г;䧒5 ׋Au4Ml6aQ1Ѵj*8f,!TE%/Uĩ1Kh:iKf2GsoO5 X _r7kVbt[|?7 / ['|ϼ~6imbڔ8q饫<:V^;#w5X*\Xi3ro|0Vu`vCSHr7mfyYg9_-N q\>Ɏb³}>{F|3[Ku<nb<.M V1GX ^q *ͦiv7lbU/%я/ۋm( ͵f|#|v 9О,f*Bǟ=?gqfYcjU3&VN3 %HтV"x~g۸uҚ2wpN=cuSlnYXݓK6*7,2rV+kM~m[6~'睭=~+шg7|Z8m5f=ևs2@܋XO{YHz9s n 4hO]K}ݞLqhbbNsj|gOToXg=̢#u׸e깴Dd-gΗ1ctRwô "BcX͡e斌8?й̽)D( #ex ѹk9_б5`K⪋Y9kdWu SuYOHϛ *:Kn W1 PjщDO~Gv%'ǡbZvifY/Y?O۹$ pʼnt9p!CAuvZޛw6g$fdF$7-x&$zTANF :.Ivn.9yǢ:Pih&@7 s,<9*s0gsNUQI 7 9 ?k[ դY雔`6{d :uKts'K^RS#*V^-@gZzD<kԉ Gx?n(]Zwe<=TYϤ^b+"KST="!䁏}?)VHa]druMxfZlک7pf>>ܔ*5Ј:S6˪rzKN])@ U瀊I50 P1f_Mo?~qnj8JUܗ/39/NN;PFْәWb99 ^Sswp8D\G1"cwڈj%Vh&5+7\%&Z$m߰1M ^Xh}U44%rI cuB+I*˺'X"l^~LˁȺfm"-:guR2p{-qћP\ߒ_ro$iQD5hBVAJJR8PCNqr8%NnQ`A&hּ)[4Ef4oь͚%-5 CױX͘MhjtUF.^ǰYs'oh8ơ$ `6D7 27S c7n&m_Zidy B>f2Y`qg:y?r $1i|9^䦧V{i ?x]<}T6Vvt.,>#;]M812\݋bPP0':OCTSy7]歿d9Atga~8^L^Iz  c_fJ*[75=k;Y2^z!EGMQ_G{ gTJD 壕Mߺn%DCGr9&#CzL/n^nFk@7_c:9|vI V}XcRnT_V.~iTnBL9=Y xt^*U bm9i{сfpؐW"Mac߲xv3o"rP:I|"+磭9ytL^A[ݓh0%ҮucNdcSd=Nnb. ǵ}.%ZheߦC#w#s6*0ﶡ\&:h׶.V=^nt`f[Ԅ՚::_Ņ12w0.po#C~L\ˎ4R(7,oy;g~g`i6i斜}v}vy\ةL z3z;7_1mnr0anNކ*h|]OzIKsp *&M'/-{(:2=A%S7`鼿0L_5OEDsOגg>>~"Rgu 0!#|t/N "/ :$$ƣ44U`2,VS z{UsҜ|qt6=vvq}Fqy';s?0t8miRbd.Y;rx(Zth&?Й߯Fn}FQ=s];Շ.;4g3/a#'+QIC4w8à=hPax%fKhS2Mhg_ęg gMw^Z3n9Wsj$uH7[ho24iLjZӗzU-ڷa Wu^R] !ܩ]N! ZKGOnYnMνaյI:pII.rc$zte&2~3+wOa֛sޗ ㇬PQw=}k7<œ7xzw SϢW$H sEW`isjzAܹ-Wʯxe|!)eřc2^IIt~SS{0zʹqTUt@A⳩ '* /) Xm""Xm)(t@3i`8U3^Q.̸[=W)wɰsjf^ϓFU{fmD'mh؃OIx;~v6ĢR][4R~7;.HN]'U '̡EwEط HL|] (rL@hb5*qJML 7mWJV0_a>#ٽs[0hӡw hf-29n}vsRW:69XuŪj_)y&s(8/Lzf==DDKJpxW{:N~yo |=O %m攇K*=fv 1&.U@ 1B=!]`CRje߸fwv啛'8݆Zd|vۭ"#j0:XO| +ȌL~//c*Gwh Cqjj Oqi> ܶBUeSw8luVԥwSZu=ֶ7W֐yʘ>`oU_fq9eJpwo]VQ-s5M#22˗rFdrBPD%/QŘ#TvxS4%2:DŽBqlNNvHJ V ['Nw=z"!11:ʼGRSYb :- "/Ng.;wFWvNv+TV^IUh٢9"zpjlVҎVu!rvBgnqF$''oΩEGEQ^]6ln+svƍ| ??&ۭ_. 5n1oXV,Y!8{~+Jtt4V:up\|"ׇ}qsp^鳦|},] >'w/i[ˆ[V?F=laEs4fB1_qQmҳ|q ӄ8p:|ҙ&<羻ײII=LNl_nJŨO܌az8q!5Xs&66S#xzSL>]:оU},qԋ3wL,Ff#yKJE# KU^xqX\l}k` ƞSL9̝?xVCAt@:*9+'swgsifykfTNb۝V=ltI\p'-t:(:07K + Mg6rhxa,F\408JԀ>ĦaNE]39)55{DVc),",b :hzHƞ2<'^D{.2=XD֭)V"T.D `|iUS00tn3QceU Q f%b¦Pً W0YiR4l*@02'`S*T=Hz I1V` !e{{t&2шמ3/aaGOF.ѱ'8­\itN*04bR#g?|Y熧'򋓘y4+U$B*ShoJ,|;&}`mu}_hI~ݰn^@ǵ)6\PRtѹ?Hp䧯2J'e\D-UO_”؈vʪ'AQ4fʿ ",_[{mսrzy1x8 9NG/O;/~7Ot8¢R(tf '?"nkNЛ:W:a6BDRUgv:\!kl5/fܛIo/!ֿEvKп4osSt B! 簵> Tcߗ=yɋ8ɚņ+mr>M*BB(~r?Ln=3x5Gӹ3#~BQ%xT!>n"K?N!BcP aB!j^՚]!DNB!qb  !S`WK"!Bt%z{UGfV&;vrc(Z(****U~Jmvbdxd⫝ !BH9tUUJ`L0 滨Х[O7o#"jB!~6^ImViǠˢ4mڌC !B&*:fMujK6:UaؤG!8GDV),_B!8T5f }NJB!NѳW3B!U=fQdn/!B*m~G!uǢ*XS^B!'b!pLz{ !BR#B(2QCB!SbB!D]}BqbzRګkB!8~B!Dũ r(BN)>ȡB!ĉa~BQKJ}BQi,B!T٨F՗ !xNu,R#B_VlC%B%BQaj;B!lj!V roB!81("B!N UY!B!NՍYAWw BqT&40B!%BZE!B*^Ho/!B &3!B*&a !x bb@^2B!j8B!D- ~d!BtpGJ~BQɽBqfb:fkB!8.{{EB!B B!B!+: r(B<%FB!8^AuB!D-wL_%B!NՋYB!jr!BLYJ~BQH#BZ%ݐz/!B\UR5UsB!U~B:KB!j`cHB!j8^%BN)21&C!⸨n ~B!NTB!TŘh!BB!BEK~K!5PJ~BQT !xN̢^Ll*aB!NUYB!j! ~BQEBQÅ2!B*!vUtI#BZE!B*2B!j^oD!j/!B*R%BZEJ~BQFxS!BOSdbS!BBG!,BH)B!D͗HɏB!j]!VG!Jhn/i#B-.S)B!D"!V0d!BF6?v2B!iGBQFxG!5.BZHBQ;Hg!Bp2ȡB!jPg!B͐B!BB!DͦP*BQIB!j! ~BQ+sIB!j)B!DPB!D s{ !v0B!j!T!CMK!5T{ !6 V{^B!%TÐj/!BlT{ !V ?R%B.wB!D-!BQ <B!)2B!jB!D-$ !VP"^B!D gHB!j`"^B!dC!B2B!jj?-B!iՊ;B ?ըRTvT}B!gߨj+=j'F!4o=8"".ToC݆f˺lY]׫(!B|=V Zwn7A'6XlX,D!dHo/!BFGq~BQBwQǿLICPW*?إRD~ kCT)Y)ABf3)DT$$眜f}9+ $?B!hPJUB!YeU'{ !+wu !,MтA~]wM&TS& 1F5%؟-BѠV-0CJhp}~H;zO1F 5F =Ji#!a L$uL)Tk #I9p5Քƾ2$B‚R.@]wV4źFB6ݵ~!ǨT6zlBQ `,V+yuݝ:|D:/ 22ï*#-7'w|>ݠbo׎CrVP.8žBg>K{ ! ]J@uFuvz[e(w=K/hEQ(<};u`OH1 ιb_Y2%J4zs>??>~hv1tgͤ!m6nHɓXfO$mnSA~5-4%!aҵdf3k%ws+wM$d4}Dpt+JB~ʂO9Mr=ʇ3p's@9% m#-/sp 3ٳq3Cv@.7ُMĭI 9sѳQō\`9+n%54FmqmrWVXb0<8Q˘V;^]!D0tV}?5]Z7{+13VP_4%3#H^kO,1a29v͏߼3yvX2vP4̞ ̞K;OfNV&=q}Do2qex\n2>B|z{|xqv*h]&5ni{*q(T_,|-&3m% %Jɉ)~~G/ӛ?g{o6X|~ӗc/믲?}qٔnʏB* l۽$%wܖ訦At/̌1бmJ]bƆofNqv]H@ rzFPM5%{;gfa+"% Ew>1gX_9n08*F"]9K(1\7m W*6h`i%MFƵ( c,1+ʼnAECyaَQܐdMǚؗib=Tzj#^B!G84FD8C*_)B'So9{1z;d*8; _, HkEBbWcl5 453#sBTTǟ@-:5 F.nS=ՠc3*['Tp [ })r~w\{9V, EG5t]G1Y0)`PAӂYCb7EȡƲ#v@Tzh^k~ʄ<B!ƴ+v:vSD4"ϏCI:v 4B_1Znk}](u= ^@\R+f;7L˩I3}_a ‰%OWou(ۋ>n&ݿ/>W-F0%*q]|vh #m|-Vf_^'Y0=Mٞ1- M5 M5;fE_CS݇iv엏f)&ߑqjgYqtawe!><7uk"FF>@kL|Hz!]r5ikJWi|:Yuwqa&?YQɓKfEHF`1~P/?Pvt,G۲&ĿFtôc6/Atu'}6С5si/!URթgIsy?{Ī:b kWeXALju GL^-ilhޗMgʜ,{QWDSGe]8cR6NxO?Mrs v+yo9 ])TK?U| %#g0u*VGDԝg0(% c7wq'NgQ4N]pxIK!8#qD?_] KzrgOPt&-[nӐcT"! JHdú((,.՚Hx5mrjJec_!(D6a".&[]5FłICQMl+C!aSՊb)?!PPi1)ľ"䧖 !T_I{(z(=YG!\YϙB!D}Ϩ4BJG!QQdB!9t+ BQ)JHG!ܙɏ*!Bc<)Ȃg!B_{&?ޠ<)B~}1Bɏ1$Dn^ !BԐ¼x=^l6K z'%>JO zwy$[%fDͦR3ΆW$}`50ƘT|5}wJ 3' .$t$m 4CcKB4" r0|z= Q8=|~u^pb- G`51-d(?Hi{$4Frrr ičx V4(X@-/@O$ I <@y2/ &Dj0wF+XPQCdfAlA>e;奺s+pP<ГTBÿT2(U G٪yD ~$A/@Dmqb9;b{j:ߏFxe@rjbKM{TLf`=Vnf"tm{ywe8Ҝ,fI 5֛B!ϣSgp, XhӶ <9aDMx@+H0ejځZ`,@x_PP aD@y0hId"9I!Q!iPA߾pur* W 0`A%I=8h;| {SSEU$4(q=HِB3UdzIPB8_zu}>Ump PZz:k@NzyRwRWTiGNN*F}P A~e\ WcފUt;\pupX.UZp!A0P=7f l)6I7FFf~G̓ Qr;j6҉N#]Bg$"yyN\C UԼUAeT@'dG{CI(Q>8z$8  ];|bЮv@жd)FʼE~|"'ˠ)ƙ$YQ e+gW$yE$"jyJ3Pyjvk_ EWRhPbl1, RKں:TPm)va( }?,|^/~?^}L{(][3H'2cc0 >+ZE=i B:thCchB^N:Μ< eQ^qYYYFƍPQ^e 7?F Tx4ʼn"ɣL^l"ꓝZɇfK0reb/PS]-aEp8  (sb+&RH\?kj$AuU5.XۭAZ [vrn]ZVѱtW4x0zT:^(WOxTZM$ RERH<)D\1fΘ ghͣN%O BI)b5n$'FpΖ/9<}cEy9#ǒ%Kpw>UF((ˆAq'Cҋ/q^ņ g7jV:ADROL:thС=>Cc73pMؼ`ҟ qlSDn'OP|>$'%,+W*5t)M52XN(8 ÃE6W*_4Yk"Ə+q""źğ}?mތ:LSAG97EsA9R(FW5SAqpm>RXn꫺z \ӧM5PYY?0z쉛o { qFx09`Dz|HZ=]Q~|@LcP eP`5L/] SD/}0QYw.nhUС6={U0v8uv DGuu5x+.ǎ]ٟ‹/ ,W?dB.p?GͷO7_/lܰ[mGZz:dY㨹H9PZYOuďȝKP 1 ..7<@@r:z U,?Wן}=G?o:7|Q[[+))KXG޽񏿿~6my[| ?_=k"=|]SSG|g"e^{ k׬AN nQ}u~l 0t0,_Lb=-첧FKQ#1PYYȊ $Y!?~<9"`[#6 IV+,fP>`ӟD炎2^;zT[H  #=-ڏ#68_J6=#'cNȴ-1'\G/^"_ZRW+ti4}r<~?jGp` 0kx 2{2l~6 9͚ԉp930g|X,RvxĦMҥ8~8*zFB;B9`K!:1Сh؏:c~_)`Zbx4q8wl6Vk?:DڲDqJZ<ʼePIZU#I,|/r|>>#yb:,lZw꒿|̛;x.C@z3Hyֵ^/>~t 7Zp%=%r>0);s<Ѓ?BPVZm۶7@a~>y)l߶ JˤK !z3ޟ[E^ƛ`0(DzB ˄߸I}I[rVk[hVW(Ab6i*}c(b>>O8?z^;m*蒩prHIG8ęQS]%8p.S_R]9sgQt}f3'+` -SSǏ",\ ]"Lmq'/ {ytЮ8\=ߴdfe"%-;wF~~{ԇi;vfGfV&0 {O5(|ʫ~A"'}y]0yVN ] *epxd=Y L49FJ:dy>jWÚ"t-Ut_/ tGO^V^O?͆N"=tǟuu.5;}O^/@t'@qKx?L>gS|"ƧҢui".+\Z>=CCfj><(xV M 0d64s Q_o Zzv{j*23`2e]H32aKK;wM ؾT%ɄLwSGuUxpB#o48maiKC eQRR={fKAΝx qL&$$%`ҥ rrr4 CHS_!px}C#Je-Fx3)?mb`6adFRrL&3)))A'i [K,"{Rmv;>Ld2!99^GXC! kR2NBl@ͮ$x^RR2!Y,f~va&2O*#%̀9{j*<`4hӦ-ʕ|g|l`6[0 X/ݓ:M.]snCA e ,Kt` (( ZFy9,ˢY޽)))(G(K%&'cϞ=Cҥ34zd#ݻwfKAA~Gu{ѹsgM,ˢ\ ͆ؾ@tFN3:^~:C'+=o罈+-he6ġgd @T"DΞ-ѣGPY^hԸ1II(++ɓvڡyi1@FXeuoq9 $mIV`G%/nD^C`kjȒ I)SN9N;IcZNww݅d2cAh~e -A%>]Ey. aE%%jDF 饺"C4!ѫ4@ gqaT#u.7i χdSF[2xxGAeEr[F&MDإ8yѶm;4oRw**xaĩSpnj+w`Ȉ!6`/߮Z>E7uc5"D Pr.`Vhܤ r 3%y ŀ6c2tcr &#pLH/bZ.V(4q/ ,x>c2hu"1D&GJMb~'t2*[Ἦ&𠁮M`5}nC#43t#.M|*peeeE0bE#M4AӜd\Zt BIIolK7ш=wPW[`(ł f:؀,Έбw"9UW:v7T)OԄAnx}>!XRR ?ėU F&~=KLP dEB0YT` .x^٩qؓ.;_`0> JW )pNSQ'HsBnQc$@dS# DD"N =hwˆ3Q#jZ*BcQ ;@Ѽ@O1L oę"0A/1t,H@`L%pO߂ _$%ޗibUcMa'4>jbXl(@=[UlS<OAGjlb(j7T#híg@L *.Ic@+`G@P`,p8)2daq#4V3c75'DQ'PUD'wԩHe˷c&"amtҬ>P8/)rƔE\P⢮rV%; R(lW6[*O_$ɂ Us>Z}9bN36uoʵ9w b  =kp@T/UF"O5h@c_ 3FnXa6a2GX"10 KX^8j'co'Lae9&C/GڲxJˬԥ×^Laף+ΠU/$&N(`CTL0$./ c͚!9F(=W)~b 0&4nePvDx!lAf͐ G(Tf0c2If('Nx@ v1BSa. ŠJ_O(* Pfh<(I;XPpjDD{e¨*$t/A(-+ɅFo!67Tw)6hAS` 0,30(d]j-pM1h!Y<]. !bѶ]ugdX*7G0? bYоݰX,hDZB*/^@]!/ǎaB؎!u'9% DH{rPx? )'c\c Uʊ %A *ȇnԍZB$*|RD-ц˨e*?\d¼))TF&c+J]LE ZDGc,*N%CE/JpG&*|Qk mq$z:/=A)#'> .u ԠYNs c9P_Wf9-PSS#o3'!55 M  5נw^`YMS'\*/ilv`YV biDZ)?Lť>uR/Uʼ!-O&SL@ʉL\ SV㌵IvhꆢРqCP )UT9W2hQWX.SIV18TƍWB5NFiÀ˲nG DZGfCeeX˲bA(B=(:w ݺw?Ǎ @VUU,ʪ*X-fB!e8m6A6k.;]8@tePdwy9r&ǠHǑmuw:hٲ:kd;C'%b|<%P?TXC!ry $h4JM'41u a&uLzD܁DD$d5S[P]3=&$c|!n eGW^+[l@NAz%qz=X>H0|^}^^lxܘ6Imx{tp>iӦW^- ׿s [j*L&`{c=qHMM߁S#3L=)AEDGh ͛E(O<Xҗ6BД'IzrQIN}A-)VYQ}w`EںZ %%%Q_K//޽)(W! }zRzA]bD'M7;Dk UV T0C,wy}=n>Ջ'Mœٳysf =wO-)H?z͛6I˜vF=={SݳU҇}zƗK71μ5}6<K?4>5TP;RNN:ɫcI0.QƐm01 +,Xw*w=k8vN<Ƭr8@+k֮+/.L{߯U]Őtx_n%I1˓]X~']\\,aZ~p\%]dmBdpbX7nzjAZ { 1|~>e@DrZ+2h.]gy}CUUf9fL_n)H'O?׬mۄqJW2aΙn]r 9i۷ Sb٨dXp[y_R5lٺU#}[VkD,^ ފ/ 76߯ŽCS@./X,x<?~s| ?c aҥ xwwN,B5{ر#K\* 3n7?g kxwb][oAX$P"G7jN:aƍ=χG{fW6mFN(H -Ӟ{',CN>hb:|X ѵkW̘>.` ذa#^/6nBx; r>vҢ<;(((Ċ+ѵ[7; Xp8z(/Y'Mû3fug8֛ob!jWطw/_jc`ʕoVĘ1c?z?Aſ "!Ʉ ر ZjM=- ?oߎtL|:vl 5 W&ܵ5xx o NW,VD^jGǕC፷BMe9j+ocРpUq$e3T[2|8~q/[ & #=ƍêoV XG1B2vq׷ʄ a=;v vnؾc&z+l6;ƍ;wTVRlvM W&j ֺȤ5uuX|B23]00t!cehJlv\-X4k~%n|tD^lcSWVcf∉5k76"\I+^Dyá\XaA̅<$9%!##CrZ89LlG.]%gQE1c x_MC;'Gԟ$aX酳둖np[t p82PSO>֭[Ga[|4_DEƅ eQ1\NMM '22;Hb7z8 /^b;b׮]ڵ+v܉N:rRmq=jjM (T3g`-q-[裏RmV&`m^СC0sx'A` ~i~+5Db aPQ$g劕[f.흞:^8'mu4JD*հ"1NAcEqSz |_(8wnG+ęb]_w/84mWh=-q~dt+7}Yi)vډ@ @O4N[H'. ՘hu!PNU1PN L&NbJ*(;_:TTVqFv.r-PDsࣈW4œO> B;‹XxzbSѭ[7̝3W]uRsΘ3g6v & D 'i {PFDuu.kt:.i~ :w@9hۮ->m̃EZXX5];'M]t:\ռN\KZnee|2[xGQG3331o|'p:]hӶ->#'9ڵkO>tѭkW8N۰֭;ek"rN&'J/œ9sЪU+!Lq1䓸e-`& PETXUx q? J8X|8i}sϡh@tM ?礤&WPqv;22ґ؊TU*Zj*hOJ={Ă 7']z"k۷NJ+z7+U_B!|hCM6`%#":}PW_[-R^x=z`ybޭ;5 sSׂeCh֬J!>_s*`a?>F#Q0w\ 6\j IDgp=wDzx|rQ .ysѣGcAX`! BbႅV aE ;0k,^ 3~igϞXx<^̝Iʌ:`үv/SLÎz_G @ >ӧ̙ ϋ˖ᑇ `  ` q&%K0T{u cWKk0 CT>@q>o͆ZJpZh4ر @VV6nۃsl6 Xl<^ϔ-ѽ{wt RJHep԰ (DU1ST\֩(4y}BOd2nIh٪%hҤ1vjDDJf'9xr~ѣ;v.\IwM2ӈM4"Gg}wᶉ!M$%%#xꩧq-9p1՗<1qmmvjW7)(..7݌Y´iOyW0mړ7n<8&c=馛qaa |>|ϗĴ'I~DC*-[U] An=+D^=2[v>V^cǢmv|s*vރ믻S  ˲׿_/) o//͛CnSNݻq8t>`nuxsF'``e7vtϣAC܂u?[n;C1j(̛7{> )#ClOQ tEEtSzj3څ n/iDh/Z+ >*;||W o}׿ǣM۶9!V^qƣ][܇={nÇqߔ) ;"&/>qCgKI}=6 % q}ٚjEbGxʼnva.|"p"Čd 'V%qjnjtcaDqމzRa]V1ѸK[41ЊPUUn0Av]2",C7\WP"FYt3lT9mk^͈$>%D:$iu:r'H! (lY AR M8R)ژx`Qp~({KR"ʫ .fCb 4b4(ƠW0$K!{@#>0X/Ct~8~Pҳ .yGiHPuH*x4{`MÓI\ V CC]~-צ7Ds_=xu]xgv5|{͵<6m 4sm7=4lKtuHt@LT)oA%psQvQjG(z0 ӄp$8~l܉ٮi z.B&DP\`1@b3ľ R Jh8&1:O'EUb IDAT QBV)x)Ō Ht]f+tSoz=^cEDsEC[P-cq C7x%H"]CJc.M8)ѫD~ ~Q vo/]H%/Ֆ5l wLA=j*}]Ag=kdaKlC,tV# lJ!`!vL:DoAC(\=X`u_E`!ŵja0{h^6,ȈrQCj*@{>BG%$C/6+'Ĕn14Ȏ 1q-`D eY+9f9UߓN [Q(1꩑U 1˲(9[-D T|ɲ,JѼE ;w;wZ36&_\W,nݻ㊦M [UjdȠKca[' Gc#Ο_\WqtE:ܯ \YlټC S7 0zb\=[6nĐ#i|r*~q7%++--[W\aZ}rOsh$2^ CS\(JnfI&_WRCGb)<].I^YYoʏ۷oC^Шqcq2/_S¿hkXE]]=ZZxoKQ~ "6eY֢UCȪF/. BV KH|aupqQ?k( ~ll6tBs$ " }{Mj*ExQQQ@0.#d2-B!\p,p ))YDajAvv6,#[0fYY˲8+`A+3_Яn{R%زHc.JV-&9QJd(sf2Zm]INêÁDeUv:VBq|~?qa822,n'~E3]$''c&7/ꃘh9O3 ?eKd2-?kS^8~TqxyG'*\gbKx%,sFQ2pl)(?%B}HJ`1Avaw[l3(x5 rQlnzljhbqLRlK̝m)H^m:Or}٥\ ؽg/ :vŒI D]ty3豓VVTT!+˥HֆSy 'D{%gbM˩LJvHa$d4alI܆AfT! NUs0b.>i9ass})H 2Ѹ6~CLW'PZ^"o {[e4i!G%eUu`F8vV5+z6py2 rߣMe.6[o#UhׂA`,Dž_kA Qv N gKJpe_0SP0ϋf9pe\w,8g uAhѼ8@ O%@B@?:Qk;OH9l^ ͚7ǐ#ѪujD˜:n2,\W>{!:t舔\$e`"P[Cmuz #rD.&8t08ӁWTWBi)8vErJ ))6=JזӼVְW_c|cرcyy@971[n7mBzz:Vۏ:ss|:?98qY),[?T]3@x<^OX6'靨 T`~CT `+` /u7bֺRE\xǎC }{NbR:~;Gρ8TVV#)iBWY&d_;l(,SB0Q_]UAG*AWFG?eflaN`@5N@ &A3ೂJb+O'\(a ^;z~NFd<_QZZJ=f[-&e>J?x)1tHXw\ qcV"x}Aȹ>a"@zla`?;:/ؠ  'D,ˡS*U]LX,˂8s׶=9DWDx0D3u#y/D{E>^Ir $zދ&T)Eذ"(HUG/ҤCzz˵qwrI.%;ٙggI'AId}1K` %jmV«"bSG9ܤ¹Z%lҐNvрR$-= 88Zm/&Q&@ :u[4/BI(}U'|{];ѻ]6l$旝hL&n&$p1zKnϿԱ}Ξ=C9v0ݻw3-Kωntòeksbl!*gko5j:gܘDDZ>Xn߾ͪ0} BBB aŻ5L(#rxn5k?sܶ.U,,AhzNI5 J<+J q1b dggcKly0.uM+6r%އCdSyuO߇=]4 oQaMF\}֪( T*Aht\!1)Ƶ*ӱ)-DϺlݦ& ɌNo"4#$@_B|11-=fg&U>ΎpᾺi.QBAHH0)IFݜ>1>ks5#?.jBj`+~װxҏ)K '8 .lqe|jL&:jX"AAdfd7IJJ&;[NC׻5JPR0]d,âCt;… RO eNvt~vDEE?p0_ ghт۷SF I)$%PF ;oE.F+r 3֙Ph '073HLrmgy6l()II,[бS']=II$'afDLL ΝKpP #JN8 ac8j.֧DN%MuU>tqn6Ѱif8u/(<e,Nrpx(Oȑ#իdff9u:vc|~`T(@?y$(,]рuf Z CBIO 55:-$RǬz\ fM017M^:&9%l2a6R$WJFYa/.0@!'72D T5]>bE";1C%9IRHLLGh`4R0׏;HJJh@ףڴtmV)B ^rKNm e.Pб߉e,-_N:u0DZQTt:.QDgе[7vٳg,n\rdb L |DtT$3U\JfVsEǥ~afcȐ!iۖ~Ƽ_CѨQ#y*WdpϞŹMpŅ Uw􎰑Wp<0,/?m .qrӥKW.Y¶y<@ +V$))]vsʕ"URy8,G\__{T| UDꁰ):@| ^8<oAT +C`E F4a2\ę qdg뤡271'p-La0VskEt#.l6{mdVCY^gr9||ԾԮ ARȚ0j/bCŰ>Z(YөGBPIjhHOOTd,kl K"X{HIڸp2‹v7m&685o5gԩ4ڲ?LʼDEV,Vڶe/0a$A`W_Үm[{L&3TRE@hh i>APl*RWw^~z)VW&(bgRZQUCg#qfjjtZ4 JJE GHh*T@TJғɢ 39b [Pƾhۮ=5kj7aۦLɣ@L^I?Щsg.Yn;u̱E^,K_r f+'F#Y:#f;k,]ӻwo.\ȭ7PTddd0a/^K\špk@0 PkvljբO{˘OStU"wc07+Vr& ];Z?KХK.\HjJ2PTL8%K's>BQBP *kY4@0hf ؔ U$ JW)qc2\|Oiذt"RyR97ZfB`9s\]PbCv-!!*Uh$+3BM/"&3&R!znS6fr瞦e4X,z=l:Ç}6z,RSSIJLSTɺTޕF]ژJ(8oABJ }5Q$%]VgШP[&~|}}Q},kXgٗmߘ ,EѣѩC+tt؁[,lDDD0v .dgj2l6Tڏ]xdp֯_̙ ~ gΜeYa#C bjժ0sq.pRYi< MUo4oָrrr"?4￟K=?C<¾رc[_,]NN¶"*|#HFRcO~j˰і3JD-{gRSSHsd2V P*IIMOʵ bQ cV#]Ǖ`v6aOt*ӏXR\ IDATW:Df222E˗/HՋ^o$=Kk}02d̂EP(d\Ϗd 0L zfLRSSRoEVv6FɄJFVlL|9}pE%B߂B  X )+7 h, `Ěf1)|^){›@vmٲyHH6B Ls"ժV'Kء3Ocqܼuo)~M9~JX>ŋ09MBA1 ,]Rz4l؈RnMZvv67lmt҅KSz|}}4q2dzpB[=#.ve@.`JOQbHS"(DF%?fZ [u A gvIkL0[6hOHh_|~;e^NG`D ( Xm z.b-`0LsX=6[q4d27 f61ܺu&\:(x"i^y-gN(%DPPYYY٧siذZOJt4Zm6AAA)p_Ƽ8} Sv|~j׮ѻ/(Ƞ"鄈DT,>)Έ`9t0ѕ#ۧOaƎ; g.DFx6â(L;PU5aQb  4# 0sClBaPbc1(0 F)ȃC~T*zAVHIM^RRŘu'i}pWBVz7ʃd">!-[y} p7i*FzU[EUv;ٝ_S8+](ROQ.22jӧNT!#+ H3dd#r;)EZAE׈Eyexu:5jj22%]8͑) FVARmLFv-ZM|b{@n.c"#sc'N]2Elh5SAn&+b#n/{{8&x4Fw8(YYe%L2DXKLl2IFFFFFFbX)"mDrmL&ׯ]%*=8]ڞ׮r`>zKpPP &#HM&w%j RY dmv { 'DDE[6bjtw1 (UjT׷h;&'rrr`(22wz;ݍMoi;$+2h,B:usJurIFcmғΜ=(>(pG0ԯW2)[os7Rŝ~4څ yCIKOAl;L&rrrV#'S!{;CzJZZNa&w͋֞TN8Nƍ )kqCwj)vDC2ed"%%N[xшF!%%АR7VtM5V:jڥAvV*ѴiӲXM뽷SO a?!ΐ)mlKܚrfBvR bbj\z ľ{iۮӲ &6&KΗtnkeYEym?]X!NIOp-^p9hْӬYs.۱kiر]:%yÇy?~CwW^-VZ!=='G=N xr?ӽEi O*mܹ3j&O / ɫ9z=C V1`ƌ˃>OOK1%ĴiӹuxqMPTT\Y _[jj*sga:5 oÝ+\!PBJ%AAAٓgyFСصkÇ`ذRG2aDzhƍСCE18p{aР=˗/(pFŋgСo׎HNNݺveth+JĜ0~X_rGa/€3cKdffzt/<>}\*76<ȌiӈE_W_Mj՘ )/LF^wcHKKs[]ݻ’\ɓ'ӥkWj5L4Oug4R];w:\? Gחz A eĈ;wNn{Lm۶ckמ\PLzHIW V+CXEѻ/11K.ѡ}|pzVjf3k׮Qy_lڸ#Gh")ҥK_߯&%K]j(KvVGn11!!Çٱc{E]ݥ|ssut֍}{믿we,]TŋY|> ˗ܿ+ƍˆ>l(}R@qEV}-={V4fd֭,Z;J?C+O>$.hFRj1Lڽ+_E9^xq:Ӧ(INJdڋ/ N Jme;j!11Y=c ƱGyiB=\JO?@,[.M gɲh4}i@kkۖtp߲e ۷#$8mvu^<55cAh4ҿƟErr2ݺve/$GcAh4~xOp3쭀 &;$wnVZޝ N:U CQQNd=0㥙 >/'==-aÆhоC׋Ҧukj5ݻwƍ(<<`zϜ)Pg_3[uִmӖ=ۙd5Сj>}p5)``AE{ԭ[9|Մ̚9Դ4f47n'PfMy( Ge!h4<xt/bT8R#& 6{ift:6nAս+,yPbb"ҹMVF T?(ܹpbbc]^=} 'i< {A=ұCwdbذӡC{n.ܒ DEEM|\\n11,]_~qzkUyok8=z%((pn'&Uw<$4T:=MV^sSRB8'N,v b2g( V*z.M5~׮S_ե EM[4}ݺѵ[7_ή]yx%oR<$,YB||:._l3 0l!Z9{ۿ(Iof5[|>tyqert +C͆ACv4k̙ÃXd_} Zj!O7xw5 oGEE1pÜ9s`93p V sOKK, yDp_dќ:yJk1b~'vY#揷8GNyl(FÀDq꿰qof-N&33,nE3( Ca޼yaFi(z{nݚ-n2~7oޜАP>s4 -מ[NCϏˇNCSg|Qr2ݮ\!"/Ĝ>o?3pb?a1 m 65kְ{nz=iiiZy])|xx8Cղnz <ٳDʕ1uhќ͛7X~͛7s3*U1\~J*yǏgriQa/|%Y^fդH+ZeUFժU1 []Xm~Fղu4lذD&RRYr% 6רQjժ7J||\>g}F3zh>|O>9Zjh\v7 8O>ԩS9q$#G>ʹsSȹ5k$%%ݻwF͚5=r۶m۶m#>>;wA Ҵ[4 OQ\/p%&M̀3aDSOW^иqczA&M,רQQF{nƏW`|U۷@ر#Gƍےg}S2fX6m# fԨ'zJʤVX33sE߾cƌ 17{t/kjG+9ɓ6lh>7W[.o櫯̙3 6={{Ү][1fݭ[74nݺ9u/zm~-^{u{Rƍ%!*#Gdժy罖V^য়~g^^ Zƍ%99DƏKN /zvܹ3i.'>&DL&ZjJ=22ťo~.61 9{|GD-ԫ[ԷF>w<ҍr|B SB:u{Bvk0]Weسg6l>si.,w[;(z- 4CѶ][_@@)cv>,M6cђ%;'N૯ޣߒj wŶE[°q;vs;RArR2>>>.D|||};ҵ-MIcUXgTZf͚qQ6o@-)Ð70L>}ZȬY3wA-<{oa~Xͷe&K}yWywh۪%of>.$D): mȔ ؏iw>>8AnrKlF:uRN֫Gο?`13K<9f O<$cǍG9vaæ7X3֠Aڐ) 2339YIDDDH[=|JIZ,|^M **uտ#(H2͜4׮_j*e wAyޢđÇiѲSJ]5r&:u$L&姠df3i$\y`\ rT%VFhhH5ܲEh˷`n3nhWݹ]٥(Xa'tnwarV]neddGCa&`i^4ZA,u97UK\lm^={[R@AMA$/XSI2 ecU@zJyҵ`{$a"Zmed}vKdnˑ H<3fޚw6P˖c6|(k1_˵Jrw{eddddddq艨]aYQnxnꥸT IDATL|Y!#sT"ȸ!&&˗/; lXis.kdddddd?l"}Pi"######sb퉐-ddddddd Ӟ*wz dl,s Z#Keddddd>$%¾/Blwu#kѲvҖDO?HT&8i*?Y&4ބk'} oHY######s`]p'"ݔu <(e<npѤ{qEF ~2P6v,Kv6S -Z5R~edddddd<ǹo鉈KM ,<Z2^dư6=z6;O<@:-q¿xDxPs)/S֏sJ A!ԨQQ"lt ZI=6?`4Y5,߾l 1-??6x?ba޼wuI=Cyg[r Y\G-%YaR GEM%lR@F.mN 埮`ɫKGR#O#Ol~/>?7h}|}}%9zN݆Xn=?vDDE&NONwy(ozF=$~5?8v }׉d ht>;cj*L2uh4gQz Zr$ 2lPF=t=))>sQf-OFddd򎍍ӹHbbb.wF;?'@4( },/[Ha(궔8%0i3'2|؋=^oOz C^Uݖ4Vo~˚mkqm1Dz~o0nrz)!++kѠ~}']ϻGQ(^,0]~Lf35ccVC;.]3'WypZFbf5 -b옱4k֔~lR,YJlL,f#̟?y>w333wy;0ࡇJ=OHKKcM4jHlrի֭g̜RCY{̷eKU$ U,u&_A?S>15ecH~zE pG ENe`эdAY&11̘1]rZժT\]vӫWOOB^?WqMyY~w/z1fΝ;1j(ʐFhh< wh~W_~)w֕O?L:?x_.FGоC|7nd 4^zN&PR.",,- K׎_CCs.g-##so'"RdP*Ti-T 8…WݺuǓ1wtr?|>wY\ еkWv}uv+sϜu+CxXIIIT\m> ge$$ģ8$(((opg鈈(1Y KI܏J  88tw.)֬YìYEZ}'سgOw{SSS={6&믣T*ZU=tgÈH-={v2TҾ/˜ǘgdǡl?-Yd)~~~פI4 vTb={p4`tpuo>oaڵaINIqyW_}>2oIge !-=HKO'$$ey^`˖-#aLzz:+Vٳٸq#gfXe˖X%z>sLO+K~wy' "`a2bFYwSӥ&dIҨJ,yYO ZԩS;Sԭ[9:P#&G}0b^}U4iJ> "==ӨQe2d/+VcǎhZ~Ξ=+\RvEYݏf͚eV ƍi֬i㌉Q|a/_RlY+=@;zBouVz2dg̟???nנ8w\VVDVdJ&PiSXJǂ_u,YPþSNOtɩNʲeyg vy$Cll /<+Ve˖LC|3cڴiŎ++8fQ(ߟ+Wо}{{r5Vf(>>tڕٳgtJqq  TZfԔdFqq4j|_Y$#sOLەW4haj߽B&MWpW4h_zv8CfgSzFI|\JE6)l/!#sg;;ÙJPRh:qPܽy)AܮX{a s{6,,22即NHiu6E # @~q_4 X9gEh)}͖DKd&!mk(9:] ?p|vcmI`1kOT: 8l?Q/ۉ;w/pk k/Vr(lJW$َK훎ayɘ+Ww콷*xz[X⿌W&6{22Ev}ǩx JC>l%B>5|kB^70y尓5ǿȽ4}r` SStsRѪ:Q~E1}\[SH=CKQaq&[ɰQȫR!_;Bx~T^A-8qzt#.>[iִ 9-7':xgϝvb" ^OvF=7g#.ZoRҖa$##sc]D7NƎ~@Uir_c._вEs,[A-0ԭSi<;}3=Uƅ[6.^zjJ]S-tSŎqWoA7涝Xq-}g7Lr꟣^V e~c RT*Eu z"FQ kʒvhזu7Ѡ~=9Cl0ddd,|E ' 7mɈRY4o9E^η)rno[׮#AA5Ϗ"+VdCޓ[Xpfxab-n-k.|~eoagQ&Fo|Z-Zw~>jժ (aLI"n /ha[Oĭ[8ȑ}Fm-G/\Ǜ6rE<Ӭ[g.^00ijy55(C$0 RfZܲ M/_rjظi3|8ڹc,'gGY'GgF7] X?Ca¡'^.L"rvy,O 01}7sسّ-.~Vc-kǧhlifgFiޱ=Sf@34}@Q"ʹe^ݿlm!hB,MDصc{ ̾TUW~aB(<4[W?4@!B Q*2ODq˖zR׻X4sg]gEuuC&kMzB>uV@8Aos\߄(eK_vmD@WKDZC .}zZ~#G6şH9sO+ύ7\5/wM;QzQܲ/~.i9TEQ6$iOQn]'@ `L-Mog46~uq!Vto-[]K]]Yz5֭#ԁ()URQ}e)sy=l[n<~ .dpAk;j@b1N=l%N;\N[v.{yxW939iGɴ =1-_ڷرc' =ߟw{.<ꊒ0cl6kYj%pӉpr:q:p(ʕ+y=HhF9TE%1nXnů<֮[GFVYG~s[zZ-=?MM;y9eXvR-RA<>tݺ:z;#t ٸi{oq 9Agg6Orǝɏy^˛z]rғ(>?;ʹeK_vi =SI2@&`'۶ifM̛t#H~kYTP`s/p7f އ'ys?1uOƓz}K7j|IcML27nbܘTI}W1Du^o`u+?F(i||`irI'8%$3OcUx1cx6L#|ondjHHLt:{D55*+ o-[]K]^/v-啦I{{9sSQ[[˲0rLRFo>Od6E*Hb^oEmM UUXucB3VSkk5۩jQJNݻeqDlw.uF#)CZu\2]5凙qrAJlw.u3Mӆ*:HKDq˖zR;#<{W]A"i\BB@e!.rمΘg?Dt:wP8L(uϽ,=$ϛ˝w/g4S0}4S|+_?oWK;7nb|qƌB )Qܲ/Τ^N$V~+Ӊ4MN(b1M(hLwt$%|>$3OcUx!Jlw.u37E3zֲl)92xtl݆(T}X Kuu5xͷ*SPcȣм =; Wyٚ\T[ԻeYrM{ ?+(:xerPL5wؐGyzvAl>!DLL~05E(b!ٱ2=->ѕyW`gD!AZ]+*ut w5eNaOMӆ*fn!%5/p10|QQ&w7W^tt:2yweaRB!NQ\N554GEQyɰaP2Ly"̜*B0p|'Bώ\N*>n*5}hˉ$J "!oOvώK|(n^i*XvBQULðZ!RFg♃離z0 !ݺ"B(J33 bt4wϣ}yw&g!3 s`tD p:9~$Ĵu+߇bpv5cZ3ced n/+CUNXrƍ_c,^x4[>nd}7g_n̛׬eSyn /8G{9;Ye-)|Z?|r׼Lz{u< ՗=JGiPUaoD!!M!D\x&["^|e-=X \xb1ƍþ{e'1\{9ϟiL2Ӧ䟞k_yM_R|ر|qSLÍ7f,\0;OTrfu^_hvaCל%6 Ѵ&Va8w=y>N_rUWUC+˖"\"2u"hii:ִԩSPX,ƴiSQMӒ0ゃ'di\_R>z| د'*-;uhdJ>yof]K&QHols٪IpMɓou]O?7g;ؗylz}LӴٞTW!5Tzmm-˖ȑ#3I<P@ /zfxC55TUU\B48 o(@ єG;x@x{:|{R^X(hVgNKAw*|Y됺/rq{_eop-76r$^-t0ײ75n~{L9GZ,o~#Nĩ9z5g^?w&x1r/8w.-CƝXAJi`XG$%M:ό1%G|ˆYN &+1+\?U8͝|]|9t+G3 &Wuss~OcO1vdY r˕3x=s>5pKMJke>axːW|9]# )NO}9HbX+ABWKD>g ab삉C/zΉ*/Ai 3 1c3S943?e%,8EQʒ&TVWDa?!lom8 kǗs\&g%ϢC.ΰj6V0M=;uo6>/cAǺDw.صg@QdTv,tdbIͫs(`Bx0 -fPˉCU3]->ͫs{E~B>TF-lpaLL[~LDu%c_ޓ&zNM)D#lP.gzR݆ʎ< ͫsp(sBdg 3ڂl 'T܂v>1{K Hha9:l-t2k^Z43%(|f@W`gD!(#i3dV&O^Gd ipǖ>+}ii1ˍFu `ҔiQ[FC_RZ< ߼ =; '!Dn k+V@-ru]G]VvTPS[OKk+U#Đ%]w3A39S{{OOdݟ^7aDuyC*444pUDYeyY!(_E0[@2J-7|Cϸտs~ʵ\um+_ϫ>`bfƖO==OOч3oanEQ0MRQY* M6܊RQYC !J.ۼ0Q m".f{rbns)MLC?ݎoǾ[?ͺ;   UUl޼|D}sg/81lU: W9Gv-[]K^6mƢQ<^/#F5`Ma&ٙ2zhnʟ~`0hk@Æ1zh|>o N[ԻeITU*ǻ7/>@%[DQ&%eK_vmx<^nOWO$2͟B@e[ҥ,-e=oI]Caﯛ[HB6 Ez=vK)=fN7nNNN zb1bZ,C$2bD@ @M_ rx[h"/FT`IK GynXyֿ381qڔ'|]׼n⁗+O1-.~٥[ނ@ŒnuhEzh-a T*VJ<::\hFn5|uW^+'z*6~kaˇmD .Ҽ]ܲ M/_~;i'f`]g==fҶ+݃WHP=Lc~)Nj}& "Hzޘu租~u0 T Tw"6:#ACmۂ u_\1yKD:52PH$=]m`F灳/y% 4fJбZ"D3ng>*e4VjpY{z̹s/sge>$VT]p8nZ!LZ"[Իevq;ôQ2?|h19|iJzJ~8y1fNULDr+bY N/>ЃK.jMگ|@%G~v!^هyuuu.q)#J6* Q$(nR]zۡ3Zs*y٦l!Ow/[5ӞwμR }"AIzQvvijT7Bֈ X8!Z2ގ1qMۗ<߽:F|_<;o5}x>LDQ5kwݓq#;su޿Ҽ]ܲ/WDnxv%+JևμRܰa%ߘ<'V ~YnXDގ#VTXC9Ga? X#8*z5iممɟ[|tI{sgM+i(vR]z-ϢAK%D$ O LUj^ |B\۰fԁ-hʰsSC-RMY( IDATgΛh&I22r(iױMXX*mX8aQ)!d-.~٥+!n7UUU' ' ='jkk#1fxغgg^&F';F\fЀX߯2Şώ(nR]zs6~oc(M˱ d^BҐ]hzw+0+BZgg^B|ّ-[]K]oFgyh!H%L[vK;Z"J5S&82R3!bQKB! dgBB>;B .|{NMtv=/AQ6y_B!G>;B .vEY;c@j&B }*Xx B!DPҖ!B ^ {{d 1y !F7 v65rcPyiG(%[#*K'|}R"vEq)}"Jagv%苺Z?hƎWSBŞ("'YgA"~H4QBQ!B nͻw[3>!D ăiBbOe]{Gg/*z_CQ[f>y !w^P_pqZ[. o)#׋nVK!O{]e<B1${mWk[O+u4M>CtB3@j !" 'Yl v>,8Gtfx_L0ʒB1%;kj g>/@ WiSKvB!Đf猕* SL#PUHV\.CUzO&M|҄w6?\t-PUŔz!BY}"/Ke~7s+1 n9xq#֮Wʪ*|m!B^Æyڵ,\5kzf~sG?̘^UUҿA!(;Ȧߏittt8~ʝwMgg'wqGڱ cB!DD|o Be1'9i65k|!u5B!D_Dnf^#0gxn 0M8 Κի3R>uhܷB!D ,wߥagzW^].bTE去{_t'|^7؄cx-yilx]<O+#BXDIuu5wט8e:5uu̞?_wִK?%\ښ]iiߣew355B!zX.ÞU;v{.;My=8*Bv{p6B1Yq `B!'bj q\(hFp(DG{``r!Bör=~oFK8-l}ڃmyϽ\tx}~*Uk'غP 3|B! xˈ8u:m-Ͽk玌EĢQ-|ڸeaTE#E>s!b豆x}q//~53K*Sf̢~pֿ-6)}=H=?P?Pb@B!菴!G|/mMW2s+lkMs85PYUwւ ;l#㫮Eaʭ !q3Q8Xp|`G8sgr))3fWF8dM*Ga^Zvz}\*U ߋ];iݵ4ѵ]8}(鰭B!Ğ /^ҋ/;;vpusCdҤ\{g7)1rÆOf⫨DUdJjp:AZa>;5bAB\ B! b$ %/a0glE!ޞw.S[p8(`)Aӑ:4x|>ǎ-Z 4@2U2BPLf=`]hikmIvTUfp9\N\.. ˅3e/աPU UQq\ԏ<]hCp#B+r)'sÍ7 ,]--JX.'!B MfO{th%x}Vacbf|_iZx0_p]N'!!BEOD oTbhGә  CtPMEIL$񖉞\(U!iB|$u;CӴMӈncbu"$$$^k&PLtӽL!Bֶ 4WQD7V>&CtB3@ ?ىJFDTUE7]AD5`DזCa I4hN!e/DdhGPjkv77kbTVZ#&¡^?D;B\TӁɡ@WDfU01LXrN2Bx{@Ζƭp! 1}&9Ј0x{[L6 *-6*U7֭ %u RZ>tEZ0a9 B'BEaʔip8Z].࿫VYGl&PUŔ&jHU->S5tMG5tMCo[x]<1 ѴaB!ĐOKeJ_n9ΕM7?_׼ U+ڃNˮ6 4Z3Q&&Jv[0ttammAt]y !"x/^nZ.Zce5y-M&ilMM]; -[VMjHHa-蚝rm3:aT!%nćx槣ߏitt1d QY[?PE(h-s[#F0׉t LtLHֈŢ;l"1~ؼK!%^xYQQA(BӴHT@' ѵZ,Ǝ-ԏ㉯]1QRQh`sFP͍[=s.gYN)B5k/ḧF0gxn 0M8t43ע|5Zvac-TTRYUc-a pͺoDywFƌxB k}a8N\zŨwݕ|݄cqc$hƆwd<3E$jO@Lׇ嶦O$Fvv M 0c>qB! "ݼU)ә9s&|=^F=LҼGfjk4P.bXAaeMj&67nę̘: B!-;w`]rzp9_u:P<.ZQt]ǭ*hm e6gΐ[B! B>>vgPla5մ ]8]N\N'jYtpg'mAv:njf҉R!(Pޣ3ʍ*V T5dw[;HMPEUUYӦR_W+@!6tAD(*+T̚B!" uXl@r:Y:9T+Br3~|rX](xE|y3pg U~o !w3CJMqL>pP}@Np2e YMt8R~rBыxܐa{0ȸ{ bQLME 8`hg+W!-@uڂx}>[[C? gw#%P]C[[@|B!DnJ|jLӤ3Z 7~TU+eK1 P(u23Loчo}_xguB!D~9Rٷ-[/̟ati&/\|ٲ|[rό}1HOB!M1D~aZk֥-te׬㭕x+IJs߬9ɀAɲ3kNB!D l8udV~L4 /8Z~;a\xyL4? lۚ3%#s#B%0sܳ~( >@7{jA 裈Fܳ-},7GK*[j+Ё[߇rBћgq]z5ƍ#p_k&98P^:g^}-?9[& "@!o!9:m0K rHK؟otՌHm + 8",_!vF ٳyy2ud\.'KO91.xeN?mi dK  .Z$ XAD">B!}zaFܳvgǎDQ9H9H"(;vॗܳșW_o) gK!}zm0s̥0b0.~~u~+.Æ̫赿C QZ'-1DUB!2+vԓ&;<‹lٲe^̩'-tz;օD"L҃!B#1_KOcjkӖgƈqԘ+MW;'V 4 7(*Be "TA<^pL\.'cFjNӴ Uozjc5Ac})W!ɸT}v[vH秺W^y UB{/C !"?["TU:PƏyw~5nYdĘɋ@\!B "2 Z}k$۶oWloTV0{+ZB!DnfoMB*9Ke׋r_eVB!OD(x}><^/90s,(\ !"3[Bѓ&B!D8Mib:Z,60'r:/A!4!猕3~<JbDxf"Ngim q&BCQnp2}p t8L>w֮Ab8]Gq㨬>jnj2PikB!Đ` 5-ؤٳPIw!doUUQTZ00 sƇ%Ҍ(!` ~k%555c۶}ĉ{3lX>5ì/uyp8F& !K1#2̉sI(:TAabZ+f:\^7N0vF#[0-eDkhk p[#Uo[ىQ~:6~b)B ykiƖ4F01-tp(N#((880P J\3˙Iffi+RA(X,YED uW.~ D Z dS("M$>Lf9|̒d&[.5ٞs9M1=iAe\}csIDATlCETsUX؎R`E>DdN{yX/ ?PLO D(GĖ6B!@ϱ Ƕ ߍ W3vS E8[c8Q%Xv<Θ;߭TtmwVOzFybA5%U4`Ňc˭!bzL:Cc)츃,q[ "8 A 'bRw"&ZA;Y+yǒs=i,`+Fj[D!ӣ,iA*GaGByrADhKX]1% DZԐJ59bevWG3`J#vep#VJ!BLgJ]7kEFlG88(pyu0R+9nrwpl=mtuwFr˲0#b'},<TB9óqN_yWCp"vZXBhzrB-В]/M {O*yQgg^ }A4Mwi;D(P^6M/0B!FsO]) iji^AYEMrG`oF3@wOO>RT7d>Ӈeve~7vv&_X.S! v4>khg&<00#SQQIMu5ߗ.Hs4e$1W!mi7eeD)i&z(B!ԍS4LiXwHMFB!oq"@.B!MtI!N&0ber6Ιr #o{?^\!B-8ieolMʴ:8MMĆ"\9L/B! 1N ˶1H$@hF b\,9ܸJ2P\!B'7|a fE H;gqBP\!BL Fψm6P#=ㅈ6%eBan/B!3ưJ)8 'Ϝ< Uλ8ʁh<)2Vɺмg~z̸B!qƒV8=QV^` WL1y\XA ƠjqBQXְ9`z3w*6&OwlI}3߹~&]Bw"{FvA{9B19Ǝmy?ڇ2O}94]%_lm?{N~Tܻgqλbjkk$B1)g۹w +tLwߊGGYrR/[!.`w^ۋ-gR#ٱWC[ɺǟ2q?CXTa!<&T3gc宔+/<'|*g'3Yh@cN4~EEp"g~'ʐ/nJ9-B6ngRvxWXy^Yw!oߺ}7o+jXIU)Ƌ/͜vGsy-'O=\ɓϽR!sa(..Ʊm0vb<[ zzp{<ĢC9,;ycv=+c|9 pvQ dy}~JJ+xoCqOFUu=k׮0\$֘qBٮKq!Œe5,nA#}ك?544?AHQ2 @wOO>RT7d.cşlW^zqqBٮy;u,9.W`0e{3L::x  zSQQIMu5L/W! :;hҾ#fM4^2n4MxL=^f+BI&5 k36iud !sQx`@qфO&\KwOB!fֶv{|,[hmӗdiM(Bq]tu{=^QS(cXĮr/r CC!B!vwPS['O:}ֶ6n%@}X]<,R[߈Lvє> 'hтr?bB1 F"4t<>f_~cVO=Vу19umnp L=rp6oz˶q2jB>qn'V"wc'|oH5[Hn©>hd*$¶mB0^ޞ)^ S:P(L G![L67^AJ)((ǙG_?gp8nÏpYg|?xtB!VѡQ D֥qisTP5_Wu?w!Bѡ(ǙkOԓOﯾrFB\xQ3B! ܉*p8L0q!}_wO3B!Ԥ(|/±B[ KyY}}s& B 3kSOs'^+r⌌)w BϸC5DW'?/2޻|s⌌) B鷳s'SYQ1j]gg'QY9z]Z$B qy< t*ʯ_,}?#BeJ!sϽ?@kk+߼rW,oɏ|+?9NĈu]gkR:o^*#P2S2B˯$uBzl_TڐjXNuJAzs|~m}gШ?_!sNZ묹g|咋wM܂!* v3l(.j1f(B!fPu5|㪫kW65tuj]r%-}<84xꚆ5Y7ih<!hyǟ@ey<\0XS\pBn[}qM D|ҋ`B!ػ)NjTdJ! B!Ɠȹ3Jӊ *BgS /5$Zn"ʬYC`7Pm2%KʮQY*cn,K.@&eNzxOJ"G-g>5S=֮a:?J? @mx tWh-DˬN~g oOξ2u9@GO}wd+9 ciY^[Zg>{f;!Ĝ7:Ctt TDd9j"d9 ~;圠=*hhj6 lgή* YL#s44>~2Zsk*"'}J&*u-$VSh42ljYRYiV2JhU*H'#ɷRZ\鲩d'Yp93eH']8jbn}*D) !D69'!g;_R"{@ !B_%wc!c= !bJƝ ?ݭ[D"Zu6?E~-"_BJ{=FK[ZhX@qqn+```m-,8P#Ĭ(rGZ^t]CuR`Vmm,Z |5QϤ1k9Cm Wu†5mD"A"a8vp${gkwS~2ZZtQhھ ˶t.r7…;0oK\,+N_O75)ّ0MD:oNizy#Fu;|^/9vFI9Mۚtei,2վHx_wضRm~8td 륺j6}}̹6$Je3SN/ϣfZf6w?>8=xbˠ:BLH,hvg;q,fK6QsCCCCN 4-{8 >ӜSD.-HpsI'oֱxcF~;iokr~%_BV8P(ͷʆ +_˖.ul˲ |ՙuD %p˻QJq>ϪUG s{n6oŋ~q?b:AIi)䉼F\0DKi^}c@Z8eϻ2uZ·ܥ;\ܷ.ϛ{4 D4D6)Mۛc,wJ$n8|pq hp{Vv-J)^[{Q>i܏-(^jjkȱDz7EKO&f;6ce$69nel1/cEU+Zz5>m[Yw"[thēNmm jk/w`&]m&n%(O"q&D6UxJKKHۈD"A{{:55%w"r`Ycɺ.2?tz>o1#gZN>$sˣɿէ49øFmaFnZ|ɉ8˯fϗS1)A{8f*6m|D_~g&dC3s]>Xw$2 ]< ,O˿\Ӵ{' -9qn}̫^iFVω~'Dx Z<@gw%%%MDoo/@,x F,6vi躎0L?K_ζ- g9lۦMÈŰqp, t<^/2,ZHMU2.掭TkGFmi +eY++peSJӇdb !fDu][le%roh`thřh4JZ[ )/+eXMWw7|-J%US= 4twmdu^:C)aY6}̱_%Bf9crWukMhk޾O'iR= Kww--l޴˶q>Ic}=e+)3 O$B+ ;HseLj{(Bzݝ;934M+ D"D1l LӋ_Ή*dCXR iXEOt !b1 "ϗ M4]ߧ9 6𑏟R:D R!i00 cN&qL?rCW\#0wrB1E~tD(so?(m}#}B1Jd(vh(K/LQI)^n?(MpkrȔDB!CC6l@qI)>{mדm)3:W'S:+cXR!s]QךvjؤHU)F=U="B?;9yx^|IENDB`muse-4.0.0/src/doc/html/single/documentation/vam_synth.png000066400000000000000000002241251404105311000236270ustar00rootroot00000000000000PNG  IHDR0)sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1 tEXtTitlevam-0O i IDATxwUa I(F]Lݷ5U@W S׷bEE%q{f:+[̜/bs{Ovu7B< "    $"BD'&=p8SBZr%D=(ɩ>FFnaɬI1E;CdI#*gSԦшCl$枢~WաM9YZ"I,# Cp t*F/q?vUv Ѹ8UF Mi9At؛W .gic2Qmql$HmU.`5zKr ;W-z}рe/uE+Z\T/gDՆrёofNYl+'n?^&t\9^zYΥb +s]4UPqjlOql7nOtN|}ԉ},# C(t@Q!"At22X.%,^QzFV2y].U"/RUjU+P1h {uSmEC3-[q D7S7PMjsDΪ(s41Pnb pŽ1Gv(Ԍ+E# PU_p޸sqhS 󎝨Fѣp8xkjjw_9 "%&Er&pN}ϸ35V%Ffk  ß)װ8cq[mpV]\oVʱ3W L@9ۉyfMsϱ;c#StǀK(^Fl,#D"=.,˔zh {Z^οѐZHh59LF`39KUg_~Y!0o' OX߽g/:u555ǟw"?? (**4#~uݻ7:@/Q +GQa!LG6qgC''kـ162V.{Q" sxGU;Z<*1L1.QuKAoTzlMV<}#dو6fi`ghvM+Hcr*7[hA`dx;l\t=HfnA@US.qcW(*.F(BNnvBSB]5P]U!ڑTVUaذp]շ#77~%%m(vF#P)*` V5qs M`71C2>ljc#۠\3WjpeN\_GV%N<߆z|Db׶ `(eh&匭5K hnSkS#paߞLVb{!=-'}!<χFȮ|64c-;f 8tٱf~ A0p`:t`P.~nKY`(PڹPa#*~-_E)׼rk岗rK ZEV`Q J,e6"&r"Vn/* x18֢(Ҁi:_UeDr638_~d\Z$/\+E_8g{zzDU~U 9쪹9"{YU*!p9Y%1꘱LŎwmٛ֠XS=Z#رsGLh u;Ӆ&~?wi5r!E}}OZή[ok5м*0Q;# c#۠qD>Z b_/h +r+Wj2rRTtFT֣? KQKS{I,Z 1}:N8~(fθ c"]qY{ܰQđf^ObˊL'N9;^4ς_.@>/ $g]5׎`_xYw+fetIL25Vg{ԄK {Q̙=Xí9E/AްQ_ׂ" /ć|U4$EVV; PYUݺ~kD.]PYUP({"7-9eorׯXOxg] k\+#^} VyAAwrU"FTǐr;QYF9_gڵ+5;? sİg{~0lx3.a|UQ2hg`em/c&L{!*h]e^ώFr>FkIz,c K3ܹuu2X__;~6믿kŋ,QV9sǃ<1lذS5{NM gyꪎѣ{X3f4=Fح}avYniΝm0~6Iw5/&/ x)bg\,%xfU`v-F5fu?E /"zQnQ ri_u-m#mtuc) ϛ8Ibq.u,^0.ѣqС?O>~K0t(&M?*v{1kܘ9yq̴D={YH}LYUv]uV;6"r Ӭ;^fv:^yK {e@DU옍Nh7Za/Lls4K^w܉~464w=)ӣz {WCG[ex<} F@W g‘Ç0?fh#oi?W-44-B5E4 [o]`LDE4#j1qbBNK@]η11ÎQ:+9QG> ~OSŽcrIY6PD3ś EynXvqPr.^dĕQo\w9!"!ƝsNwx7|k}|X ڼO]Cի'DQD^=q!,{_!}EV[$>,0`' 9fhAn#?2Y u8|e}X1~*/-?̼HK3jv}ƌYm@ v^6HNy(3TgRt}~LCF]LYI-Zu1ȶJme$IA~(}やei*0Sԫ{cİQ\mGPd2Dl>IOybƍ5W";bܸqE.~u1e?W^Z.Rڹ3f;tȱEz=ÑvaC׺Ѣ>Q$;N^Ʋa{l}Q#Ukr1/cYYGU#yVN Ĵc #c8*~꒶W uz~|_"JBkN~p_2f?_X̘1`KӚQEWؽ'+r%҂<6nzMعJy׋PӼU&\ޖX~=&>PK^=vC"g߻gjviWiȻ5u_~UI22+Wpۧ hʎZ&=;ƅ\U}4]273bkmDfGP !DFxi-<kEL@uT)86,;%Gr%eyc+Y<([F[6ɣFBG |Ez^uvvb"D\NBXO;wŖݻc_>o^C& ؋,-|Jt\={`S5{c/U>DżmUb,Ź,7*9bdqɓt8c; ~qIYoin-m''VU^3θ G/`&9:"]k蝲vu`g >8#Y29@҈[ m =ṽɯ*Z~c}C|JT'Ojw5Ywv˿ky|/~]±Gr*^k?he^y5uYbk Sfynd]F2ĵ"-8\uOZ 'sq1<شkdyϵ.Y|z\\n{L0auf_<-_8vkjڬa/E.f1"`Th tY1oXЃi$ cUre#b9sqӍ7Q#ѷoX]h3Pqw6VU3Rs@W εe(cSmg XgGU^9D+ H*EmH϶zM;2wcT `hc$:ehO'Zn5G-idQVSƌA}Qxw>:{Х؅+{"B؏[›E0G G8۝wv6nيfO^={{EHUiWǜ/;q_l*E*;}v + ڶհQ۱ψS.K;q@ufƱ_5hp8AKK3­Op:]p"Ԗf<9^47bZpVV60뷆5A]bB@>bPX!h32*o}.Wd=aTjJD=&QߎfL2*%1R/#1+ڣ$S3 p{O<ݜ*=Ph {Qzp {>Eu44uAQ1((,Ádg{PQY.yp8p kxrr0dqٳgQxiW;1.vxAҾa6N{|ĎGߞn>Ϊf("H.p``!ɯ:P(`0P/Ts/ 2Z"tR.*͋u-;H2zdsGUvl$W3GBl)? ozz|_7cF_Tra8_  deeczS\8p}p\СCѳgOu85MV|4U%.2df _7]ӎm#_^ez4lv +]$H KE]Tx*4fEqBu`#l^d٪ӏ*p0m(3IPxkm@VEQm6boq6$[-D-;y-bQȐ:BFK-mv$إ$S3حFVSТ("Mp]x ۝sHM>2ξkj )}7j1KƫtfulͲw Fg>?؜$\1Ǭ@,Rlqɚ\VS'#14QnFL[cX|s܎EdjLMK!|#2nigA@VV}MƎw@(ZP"SpcuKWŐ&ug6UQ,fAkIDG$r\iEDXѬ,H9̘qNz5"vٶ ʼ*0Dz#@ *a!0޲6ެRk%)L '-zּfi Bļ%YZoyѴH7ht޺ɐTMHEVz%Cs6h!9m5n͟H~=btVc_՘&8t"2c'PEu10dbF1sې3MX":^ zj jm1k퍮4P om2:.bTs+eb\@fg%FE_Ph`8*es ^ gs+ Ś1fH$QcIG/LjZSD*;?b%/v8>*[ }Vb̾Zj_QM~kتEq*!2 k1!/ۤ%n`hZ1q rS-ml[Z gBqCE:;ٙxv)'^.#e~疠~it { r\U"CKElo~XoP_<VCkƔG.U'2̸9WGȫ}D~ża{[Ыd"d/͛}egfAn^Di_HKҙ7 CI+F6:W^c TdؖY;fف &zhƽ-bGxl)3{Xo/u+T^JTsjDI|W^(߂%J>jq.2ļ2=Z8~uMSr/I^G ^~mփ +]_5ld]l6*55ıMs7&TX u@:,mSeGB͐HTkB_v4œW Sc"ޘb7-ʄ`^[sXm7R:n$B݄u*E:[o0CK^"t8 G}!}kOJKJJ3#j-FiPJ eA3f'TF5~ul3i]_oL4󭌉uVߜOd7!UHlzEzcm= !҃YV8#8j+ DقH{/=ٕ+/gʙ' Șk >\j|SeVWZ)95j9ڔm[L08j'C\)AjVp[:E#v8d+$ DlU&# !H'g1i 0; h U|x5Ypp82KICkNGsuK {Iҽf>)XeIەSEJ'dPy=X'^KU @ t 9!8"b8v'ſIi8͙U9! sIxr<̔'z_׎~jDrsL8|p݆m@@׮ݐi i?8&@2ͨqBTw= " aR֠>`̢[` %3̛3L3,D`R D"9u͆[Egf7cܬa3b칁솣8].;w ''W Z (Z45JC]?f4^qۯ?~Gqc`֠GިA0 r"Ux01E$nZSݺPP(1N HHT/ؚk6{WnKަk)TfF{Ꭰfy}x`mY(DC}uAUx֠[E(4Z-m_-,FPEg$-@uR k}/ QEuϾYmbD`k[fFn ;EFm@0D^~*QPTө.kd- Cv1MgĤ;??ѿN$>daL'#1H}mﯨ8[[p /脪*t2B!TUWc\Vk V:` Pu( FspMVC iBJ$IdB}!PN+b/`c.>W! dgMj&_¡0 A?ԄP8 D?S,oYMhI=dIA*A՞hsv$PIfahj41)m Lqc؈z&!2fRy 'ߨض&ՁpY=)*J$)WZ$J?יf7VSMԱMKvr*S&Oo&X [D."h~Ow6=b/$$y{[fm;<KJ!o-YysYb^!قp zI֫VLŌ\c'<[(L+ Lҝ 61OL)wFD$ԉ6zQ9a'Q[ 0)#b茻.yc456WLuv^=I){`IkVzEbuhnm@fކa}FNH{vD:މ|95 \:FMP%1ż$@ m7pHBuq1@ ȟ00bSbi]f/ނƚ&̺+/&eu f8 ½'Ţ{[a R$?N\Ԫd6D2$5͸w 8낂x≘>}:JKKev},X]v!77cǎ-܂˜Ͷmsa۶mÇc֬Y2df֮]ׯn,C',/-dΜ[z_)jZyy7Bձ4@=m 6 KZ>9utuAAFfb2O?Νpi݊¢ͷ~Γo w=<zGEgBk X9 $ҥj(..ƈ#p-k׮)kߎ'|2epfφ '[n;woٲ{/Z^/"rss1O?ٳgcܸqx7oOǝwމ;vpoc_ff>;A$X0QLx=3l,\M7܈?lz3=(+uᅬŋÛgy6fqms[VUpƙg['_"ĩw}_~9#Vϐr7ìKї 4&[ִ헅;Ld\KlfÆ ذa֯_ŋGxRچ}>Be3fN={p8pB<_~3f ֭Yŋk%\ve/&7x#.]> jP'h%(/v c%ڤdi̻}tg1k&/O<#eKb p֜нf5[b1l*.ض˯ x#3[|/~?,L38eee5kf"u8"\tEشiSlYgyf?^}ĉ믹~7 ɚMpJy:A;fm\%w[cӁObTAa4eljSl= כ1aDI'E /۝}aÆ<967>j&6nFJ,[n im`& 555Xt)phjj(xwO>$JKK[oǂ b=^]eeexce<fҍCȌ{ۄ[?z($CΝUۻt邺:h n̋jy^+:&->GE]rM) ԏ-2w~~>b( еgn<xo3(ރd%ӞQT,Rx;Z;Z~ם;wƩ?O"@SLAvv6૯ҥK79sf̿}0qDL81VD+Z-_N`j;(QTT޹sg9r={(,,đ#GЫW/JY,3Hb~dex]Zoth?ak*3䚶r,qII 3ƻS9Aqv؁s!e>-E7-œM)M_+9?Si`Æ ([袋ben;V׿K,3PRRn 't>8Dw3Z$S."(ժU0Sbۇ ׭5\Í1{믗m_v-Fi?d|\0l7ߠO88u%,qݤ[F_cC457NȻz zj=l0v-:#;`WxwbΜ93v |rOΊ[)ɢxCz9 tDcٲez{sŚիq_S֊CDzvnt{K/ƪU0UrAklR~=~ॗ^}DeeexW𿯾F|>XbcwRFtv[ΒJJoMz;m$-i2vt"+*`ѢExFٴiZ,Yn,',[c6M+VW^_{ +/4I,%xibdv):_ȌgzHaOY젌=˗/Gss3z-vmfºu гgOaC~]tAyy9n"uNpfa=N:$<ݻw?/Xv1c0{v)O<Ǿ2|p<ӧn9E.@?EȐJQZ pa3[^ic<998餓gE޽c[:p x!Y<Ñ0v,3'fowsca]w~-8D hH ?~L:2VU&qH#U V[T\5テ! fx8z*tIw,"3z0/.Y!Ctrad>Xş|9쬻hiObضg[*f6U- ж}7Qikqm=r Boh$MTad3y%Ȋ%TdnHSd2X#a9f*M#l& Pe d^I'=" {O%gu[[dL(g+I-vL@S#vO1z;At$̌D圐-2@Mr2QutACpAiBqH 4m2LR{L% AMAa bzz & ND!NDzC%OAqZ;%3 0d &`rT=E ?Zns I!sA$ָÖ/W H('!rA$Hja*}~3A01#+)kAirAGo@q'v('!rA؃  "qAAACqOpP@ M!.nNO64vHNF ]ulyp@sK >!(',okh( v(ߌ D '0E ҌAA 44Ipo!\.JvC9}bf#=Zt`9a*޵ ¤b ДOmM,EKs3PCBaѣ'v؁ζŦrB O[=q3P]v_R {z{N92icl" {O@ϗ!B r^TWWN3Mj7PNh荝`0@׈zݘN}?>sBD>})ѷO_9%fD܉p8f¡0=hS0 w;55_ZFBAC |I,fډ6777Vw(E(F qm4('tXc' ǟpkM W*7ӺcS[#%rI6m^{5ٶ:L0mכu]`( `ĉfqq≲e¹sСCOv~h8Zbz@9Ay('߅7vB|EXXVfڶmnvL4 &Mٳ?o0pc۸a, ~u}ҤIذ~lۚ5kf 7`⤉Kxl(e |&|&|+xe%z?lY +&-{Yo^ۧڞJ('ď9Z6v(wϘ3gƍx:N;4u]عs~JDY&L[>Wcձmزe &N\7mڄie0qD\{͵/PYQqgC]]]q砲UUU}l70| ::N:$,X@ٹMMM"ֿrԄ//gSvލ;?_|q̎uuu_*((@mEE-((o}F8̿Qd̘|纶J?V.Ĵ2twΞ#F*Ϗdee1B_~?N;s>~9y2o5 ?-rBf#&TTTW^V?%"Ipȑ#|rtIm}NkN0|p8zc>wn~w`Ȑ!8x *++q 2@tRY EQQjkkU?Mk38Y_Z"@ Buu'Gp7{=oߛbXBvRC"4K.Ayy9^ZӮ]K475i('hmB9!$e$'իW뮓m_v-F& H/'wfҤIXr%.ɓ'c4il{߾}q1  b(,,!Gy ]TG+VVlȑ#kD[ IDAT+WLZrӻtׯGcc#-]~t]`={D(3˖-CSSoom%=Kx2thrL];brnqԩSꫯχ7x+Wĵ^kTDxꩧO>/^O>}Qdo&̞=3fqG,;v, ;6f?sL㏘2e V\U^fΜ[bʔ)ؾ};fΜɽ]lm?F7xˣK1h x^a><9{ιxqwnFla;&""~O -[| (-_c]jǢ@9#-vC@uU\nWk?0nTTT}~;6l؀_W⋱a<8cLS+9  ?CUoߎ&_#GliL[&DŽI磺v\kVǽ[7-{\ r('xc' |~ᬳF߂@@ywVYYp݇(**D^2S lބ /"L&` 'igdşN ;UUp0DVV**+pP*H/Zct6TTΆ˝ G*W! 8cE;A˩&nŽ! //g=O YYY`OPTTCX}./FNnn틽֡F"ѳ9P`_>O(DgX*++oOD.]D+_Aplz3=Y4('Ͽp8PTTw_OeěEA~><} F t[lzdZ['S.돽סF Eaatv/r;cQhSNC9}bt8Nxssb=m MB~7n4w[ͰtO ҅EA% cGD [phAl^/rrrꈌU('?hjZݖwz+$2crBq B0 h   2T  hЗS z7MH[o_nA ('a+ِp'Yne\@ AM2~9URDG)Zh-\,v4\7mxc969rA@98zFq'6Eb/ -JYHr*=PB|>jjjPUU|>Bt7-mfӬD[r qI(FCC=ѿtχD B}m vޅNЩSy8RƂz̶ǮSN r=dnNAuĔp7[(FI(BEe%<9:u4=LEdg;ݵ+:w}#RR1?'`+ZL԰@$ Փ9r ,;}wlWGEE&]2h_aTUVrb![ d  Ӊ*)6  ?/~"#5xWC'}h46PYY3Ec'A;>9^/:D"PN B9 "NDHɳA@aQ!j\~;|nf9|;\{ OZ<#fw͹ C sXf ?rp-7c475a7[w>t7' hll'##Fn^.^]rsspwo;+ ǟp"<99%PN V&AGKkjj+Nm `޼y7n>t@)/O"? عc'/_/>,&`.ƶobxA oYYY' vCŴݻvÏ>~=wsƍøǡ_~l @9CL3kr#PN0F{ A#.>{RAO"uAAХK3Rf :n(.Ai-D1 ߋW@} @'ѯ8MM>Ʉ=^(ys PN03t@Bj)&#ڔݺuѣ|r\wu/x,\"7v&K(Bscn뗤#`w-1h`SFy~=~"˾0~?|gnc+v֙[@9s-D=/yhllon|\r)z9s?9hjlDSccFQFfB^6nDvv65n,('tt('ADg~3eSZZe˖׿>j(̦ ?={'1f[Z rpggaÆhniƱ{̂ra Aq\}DEd. ݻuC0ݻEp\PQQoۆΝKP:`BADrAD*Cfzݻ7<>য়F0B׋A[n())MwSJٴ2khPNІrAt"3Yzza3\xPX\z455! r"''fsS0y;sve?NZߙ@$ |('D`}>|8ʦacʚ3g{480r1xqK7~UUU())i3?ѡ@rADd׽ży0tڵːo$$}>TUTcoA ,~[1PYYF/M'L@90  8){ ޣ;Xd ?_ŷ~! ߏN:᷷Gsk_ޏō\q1|ͷ܌k֑l[w>ז hll'# F^^'b9rssmasge޽qܰxlKD@9A Aqӫ JeLƌ3c۟}Y -[_x;ܱ˖-_| Ä֋4d[w^x<S 2P`ݺ?؂p(t8ѣg/degZ QIţA96EџsysqrA sq+%u塱1i& _otLA q啿駟:p!ɘ/Ke*~.p  !8\Nxs<ʲ=PnIE5ݻG?ž={}ŸqgF~Le:t@w)UFQXX[|cnr삂=:ʼn'vӯI @@ˍf89t9kBˍlx<ٶGEn/RwOǺuLW~1X3ә 8-'&-wŨQ#cEEEXEx^],ѣG ihiiƠ!t|^8tBÀ |>(5x}Q # ֞pXDзt|e(',('AIXGnѾ&Z^YY{yr V|W^[￿[qo%%%(//Gnrйsgl F<+V #u$/5| h2 vڅ-[`P %pee|?:w˾p'fcDr9('+SsAt}6ԑ'<'xzO>=.2twq{G466^'|MzEQ#g:I8Ɔrᓍ1Qhjl YYF]V"PN` A1Z{>}6xeXdql}ҤI4iRl}„ ܺZuC~~>z?lĘ1conA8B0;ۃl؈f;xt 3PN +N<\.u@ {v?]J:t۶}Ν1)ujAd Pf#2׋޽{8?~s^DnPR9_.LS˦fD ('PN "BV7̋dOOF E^MMp9] /^o>۾\Բ(y%dE(=\/AXr|޸9UL}[ӁN͍|0CA$OȴgO{ʦNET1єq4c^W)'Ƀrl sqCاS myO{.M  :2ND"zӬt7 rAN?@tt37M2C9lD[GLɵ}i   2V[e&$̭7j7n(' \?M9=%: Awq_:WGbg !h;;*-RFKb6ef  -%%{y۷%؉}˷lSo=d=/c0nN4n7TWWS[[FӴT7MtsY W&RK2A$d=qO Milhl& R_[?|ĕMn("dBشؓ7ϣz|hƮ]U8\>P+0Z_XȺkر}yf.Jźhzz6J&$;I&D 2H ׯMـDv?vZ~M72nܸ}Y~=]i ]שb`!L0lH֭]MuUyVBJ2A2A!DZvՓU&Fl6k1v}@,^Œŝf׮*&NB J ?ÁJ^CH!kqOɃ %|r8bB}Orrr )k[,4M~4=f&JVv#li’AyyCf9ddBLBѦ{WjO#&d0 | *^n5&a446ү@A'դSRZJJNNkM@6bRo${T֙d,dBzL]LK-BBrt9ꨣwxۗy]9g 77d4/ <*RDB4, ۶mgLH&G2!-I&!RT +{ス=ԩs뭷1yDx ~_\mnz٬ˬf&2Vl6kZ*1 Biy_wS$s%]muO;X—uGK&tD2Le=Cy sD<3Lc⡇n1qY5c1[ؾce#鏫(l޴  oK **S1p0Ɔ>3:TY$HLM2!-I&tPL'4]v8QضN=ŭ~|-\}U 7:1*NWӷ`0z EM0H*lb]46Gꌶ=f-dB!^<3Bt0"[boߝ8i58#Xd)GqD3UU"+;UV0nAO( +V`Xе UoiJ2A2A!c%;qqFP7:ʢzV\nGU[UfgŊ47PRRBVVV*-DRH&H&!2RZq)f3EE㏼UNa~>ٹk'}I}9d盘o==<&t ٜ< =dd]0\*D<\zT7AF$w,J z_]~EZ%Do'Β97(qB!"Rz  ]vE嗇Bz ).$3Dwtzqu+=As֋m#FK&H&H&^']\']$$k}zB~zaesanuY2Af .G2CtwJR};ze>Ž7BM2Ahrkj%GW PSWGUu55uu4=5-mG|%ާ^SY浥}nɄ-vL'ǺnqHuijr3p`Ajjoq:d\rD!z!]Ѽnw<ǵQ=i쪪[c0Y؊(,.bعo7xllrAծj6C ᛤ( 9jW5LrAmO:^wVjr$$zɄ;sޅ>bI97Y֛\.~&M|Ypݬ]uY25Mө>t-i˫,tJ4aȠ j9=dB$ғd"q]q+ۖŌ9EQ(((`Ĉ|gy||>.b. 﫯6t(1|8]jkNcc#$ZyLJqY V+999ү5M6n܈Ichd_b*LlLM2!=I&N zD~΍}# ώ9\Ǝ=+u)S|Jy$~N;Wf&'t2>Cet&xhŤa6X*RDB4, ۶`D3dugLM2!=I&t\.]܅h.7C￴,̷ GrJ:L&Mą^fZ.,O膁x}V+ ;Y5-X3lZ0 BIY9wS7>c;]BdBh I2A*qO11cK/1z(N`r91b-b̘1ڙ\fՄbf픕Ϥؼy(  ߰u~TDCC}dȗ9Ʉt$YsY⏹=[13+2y2\Mӣ/75W_rRM5NX}:(  6lᓦпY4MQ_WG6q*>ꌶ=!|fɄ6$zɄ _&,DH0Oqc)//g?(/+LQˢ*YYYdgfr : /( +W`QU];h!ҐddBK`'|#f3 k+rEaը YPS[+L*5@A1Xv/_N]]%%%defB$ddBtqc6),,$ x1F$?/Ō]X~=}e t=dBxB8vJ˰ll߱M6_f3NPTXH^<v{,ٳgT&O2A$d=Qo7WCx=hA լbq\8nu_uAc;âsfEߞm Un9dB/$v9 "[oi>]UFFyjRtp:a(({nc._ϑY3kvs(Ft$zɄϼ")&Qe]$RYX

/Aeѻ$pPV{wa(j#Ddk}gyg;,={gEwÕrtLp9Bλ.A ͽَ7BM2Ae a.Hx:iZ2Z "Q$viU'4660hsAjkq:\.dBhq͞2>4MlO8 0YSTT̚_sNPU5]u!TѶ'^&ɄI2!L]۾]C:ǟpb9s[ٵkR,mSO;m۶{ͷ?uyu~9j"U 5NUu5^@2A2A!Dlo.o,]Ktwqw0y$>YI?#~QWɓ'`6Ytin^{5,Kb^LMMTW0lH>@y߇eQTTnjJYH&!۽g(F#y+dgg35oO/#?dƌn֭[/  EEŬXǷ@ԯ)4]{5=<&UOn>K^ )}[ӄ%#RjhT|BzY;F2C =dBo&~ ls뒺 uuu꫌1E|?OL /\W_}СCak]  7ad0 .\_7HFcSDt2Vrrr_klڴ 0l䨈IgKb*L/dB!T{B$[B?vm\<~uٯZs0iD.w]_}=Sλx,|f!\|qB^C CGafif3&/k-S" bPRZ墩1M',$BLHO Bi= p0f^~eFt2|p^~y1Gn3QGf3z踵+b1[ؾ}+ee( lڼeh8`0]$o֤9i"wddB:LHr2A% LS&O뮻G}7p#W]}Uv۸׿Nt32͸\NX} C_( V6nS_Yٜ6^Mӂ}>kkLLH7 BTsqj,;-zÕxX5rd=r>ca?vDRoL\ZqDSSc+dQM ]۷uT]3hrzcCL#dB&arp^twFW$Mc1<"wG,UU;O CQ;0j5*|si"7LKeTSTΊWPW@qq )k<-Rm#}}L' Xʢm/A,ѹ`MLaa` 6ˋ9jyU3^];Xn=}Rٿ9.D% "ZFu_R6w`|7h ٌ`'.OׅYfvZgȘ͉#;I&D )Jr>mD77݆,z=ffr[n"aϐ쿣 'aɬY3mp'%‘L$B/G$+tQ*.N00 EQPEnc.l̙̚93QL$ DϤK$ rw L$pr9.h~HϊK&!wy$="uq;#F \r%nB\k]LdBtˑݝAr};ri" Mіd%雇TWF1lԘT7.h><͜9Y3gD<%Β{ܓA΄ ,’^g>~Zi[xLl9B~λ]M"xz<iZ2Z "Q$x؈eСde`6 T_q\r3!z!]L"4M̬w0ɄdddP\RJQQ1kVfWUy}j[-H!2w#tx8Sxeׇ=lj'Dt]UXN [>@0Q#ɰUU)i<-RmɄ} !]&DE^ź{ 1Ar}k2N:D>e[l {wvS][È#z<}>~>χaȑTvB$ddB$w}q/~W_}͐!pobΝ|ʩLzWiٳYf-<[m?z{k׮K.ӆ`0Ȃ{_~q {S__πA\lyR\NmM JKK0`0֌T7GL7dB:LBڞ>]qjo't"'̲eڲ[8SyW;{v#;LƌxAЃ<+|;ou^hll䦛oC㏏FGuICkrM ך&7mƄa#'C]O)L7z&rZH&G2!-I&L'9d #655z.|ɓ&h"t"Zƽ nyĉ,Ϲwl6==vn߾?p#|Gyd^Qh` 6ҚɄd¢8622HN:4 Ž;2,խ9$$+!DʴoU&wehsNΘ6|RDXn?$6m+kllbc5z)((2tO`0p@~~ϹƛΎFN5qb5}%|oE!#M9ITVVb6}ҁ yF#e*/3n. НI&t\ޕL'sޕP50k[L4I&l2Fu-ӟijj'#K._^+^x9 I&b +sE/QeD\f lܸW^^#+Ǭv%77 [!z!]e6;%mVv͛oЂATA),'Ov{, Œ;3TYg~Ʉ}I&N .G2CtwJrwv%EdgTE4TUnr:-7WgHSOdƌiyI27L!zDiM0UUq\8 0 EAQ貙ӧ3s! I2Ae8HE'b2s&P޷NuL%dB' z^2y'ZBpES݄q!@t`H'wJbwm++㳊 q!c6  -GUU6Hٜm_CB!R*7/M?~~$7//)a(L#kB<`le^4?QHϋK&!IN }QWddX1Ldbxض q]ѽH7 ѱ۝Τ+Dw2LVhv / /vX6c4fLLBėdB|#Doew!Wm=d=WQ_kIi~ L&2,222Z(zd Ͽ"ӧu|-TYgBh_H&DL2L!zM}qJ]ilj0xQ-@Zk;.S(D/ "\HCQU]MfvJi@(STXĺkUUE^>rMtFL=dыL-]ewΘsff&ÇcƴiTTTttfUN ?ta#FTUW'g?Tx(TѶ'7[LLM$!zU()bȭLeElذ!UDMMmC:}^AMM-Mnw.DRH&H&!^FQBOxa" jjjXp}A7Wt6Z{eݺur0h ֮]ǣ?֭[),(saРA\XpםX,}> n\č: 8ߏ#'/w^&}[ӈ%#Ғb**8 LH? BQxpgr.GrW>OPѯWJ2>?'|ʊ~\yůb|o[x895|zƍ壿}Tk~:LWbo:M $zy4U ך&7o( oHD$W+//3Y2!4Ʉ$;%D|Kymtn}~ڵ,lV'pֲvlV|0|pkwѺ̉&p}'p&>HEdRn_+<^uDi`RLXT&Gr҄iX,vŠmX-rɄ$ғdBe!zpM95Hݻ|dgg>v9[-KkYcc#N:֭_ϓO=͛|uPQQ'?9`|kG&/?/١:^cVx, +650 BqI)lϓvuSYRGBu&hɄ$zɄ} B{hd||NN ݾ?F ʒK﯋5FOUUf۷QZVN  m75guQ֯?U;SMk/VE/qP^VFYi)`7|,nFIq1_?nV O~}fRVޗ?rzEQXlںCHeefsR,Z0롦j'(d¾$җd"ZtpYO;VÇ?^OqQQ|gs'?? Z6g,Zp7b.ֲ3Ϝ]x_q%.| O:?p#ICd2rr9Yr%apX|igIiH 9H&H& ]W腰deeqWuXuò>z(Oss1|xLW&EE||ɧ蚎QU3&DU+p7y4h̤STLLB>(:54M믿W23*yA6mȫˆ#l6xź_''~(+i^(IwVɄH&b i BLbuu3fΜٌ92%mp8씕`رsX IDAT7mo! b6qOaA>}rsOtW^Oi:K&K2wLrZGhu}i9mmeESSMPU͆njڽ"6;6uZeӦzqɞxɄ$zɄ!R#z U`u*ӧN ?H "$ٚᄓ rw L$&G܅K/&!҈d"Zv;=y瞛&ą,D|H&$f9B^mqΫ, B+P[W?bjv'}+˿^?';++>MyhU&ց܅cIe^4 u0A )koƍ#'77 B5552xw-7`nl}Zw_DוH&ўdB = 2ekHwS&1bDrwd !pZ_\/.zòiS`3..K&L2!娊€Ȍi 3+՗뒲QeHPؽ7/|zqK ۘ !C2nǕfWf&v#)u@¢W4 K}}=542n;;LBQe .z]ilj6ujkoqmnu!#teʤ$b^2A="c'wpB"Q]SCVV6 Ҽ:&" Yn-UEUT7[ By]u\ өYs[V+ ̬_Y} 7rohک3fgT àN;tp֮jDpIu!gmL݅dBWz&.!ҍ8Cnsz7gߟ>sϙץeoٺ-ie-n7յuL7v#Fn!B2A!Dbv~@Duqߴy3:V+N#GLhoڌYf#O>g{j8>|ٵkSgSOai[ǟxm۶QPbK8̹<3rQ l#[n#HH}X'MB'L5UTPk6=lV~9 B2kKw!Hd#38.<|a|/uʊ~u8o<Ƶ\EJV})O=[no3Sd ϵO2glF+#q=w[baFYp_^ ewm زe+6 ˕:Dd4M RXP/@`#k&\Yl#x$;x,/R]iˑLLH %#Z&K=BeovlM=UbJV})O<$np衇0c423oru: <4i|vƍλ"PULv\̔b~]-`(> fsV7OX6m @yyz-jH2A2!]H&!իvޫSP8|۷o/;~~y{/7WWߒ':Æu8/ϰy|>߾ms+ 0fXÌ@Vv#FbdX, &&"|taS@ ?fV|>lv$L忾E?Ǵ; J}[~FM1Eχ?rtq:/. .$"L\ y }e^ǃݳW;ݵΜ3qcǢ(0w9tl 8[0d#JmUTBM.`U5O2{V* ]^܅#qwm߆HRW?FF'rhhh%K6th4ߟ3z(^[k{N88~rA;٧OmN~~fRJKKlYYYl۶DiFS9p?6? G֬^MEe%`WkFk֬f[C\H&H& ɄIW%!Q$t59!@ۮ2 rхY̳\qU465p88hX.ifNC{ygfa0}<ēspZ+q=<9g6F?EQUW4ڪ]Ԧ-w}h:~ =OSSs(`ժUdf?rH&H&ɄP_0"j gȣ tMG![~^ҐM׿%?ptF`a/>L|mws\. O9y}躆 ͨ KU|̀q\}F5Mm]Z$Dw!c]jcߑ'OnEӂl;քm7 T@w#I=܅H-UU'i&^{uF^|,f]|M~}GUոTެBɄاg&P[S͗koesŽ/D*F}m-ښrss}|s<ZQ D)-.fk.6o·|:|`#^vuawy}tr(Ғ ;L\2nkUΝDF!RI dX3"!=weǽyDXc]ԲmgISS^MPU͊n=v4#'L9ÒgO`/ŝNIt{/tY$[iH&t޾ekF֌w~H;ќ};)C^KUU\N'0Z/~T%ah~z8 !G2Ad]z 'B=!Djs&!D.h~ H# BT!DΙ7/MB!D" .]xB .CM2A!: ѴT.Rjԓz.MmiOWh7Fje#-z&t٥gޙg%':[%h%@TdX[YedC1P %6m߇lNH=YoBwe+Du>r'`0bR<& I@5ֱ.':vܻHQf+B}YIoˋyv>Zm\e!I&_NcS#F-dt&Moe%]ܜNU__O۳Of#7㻾FZ 0 (oϭ{'7TfKz"fZYU#zz0FVgvN6B;ei٩KS\]۶mcAD ix=~شqnwee1~dKaּU Z-4@xbi7xPZWBd8؟w=^B˾z4MR_@mm x^4MKuD7ّpe"$DtL ׇFC7۵3HU` @0D  x^)+Msh ذq`2JR\Z()*$`~oaVOqI9ťeQ^ޗ X‹oͥ,}s` ߧ7qHup{< z;>P Sb@-zt]xPM 5 nj x׽x<~?}#y0 7FS=xFZނ?POg>m ?`}f֭^#b'8q{˨2Fst::d?:s.a=c\50 à !C~kjG7B 3ī I&^ ;!9#''ge/ [MC.ai^ 4נz녛frr0*>+c&s/ڰ0 WD躆i^D#07un u\>C1!hnh{^g,8whޑ7Z5| @0z:K{C{M|׼;1}:v:KcߧTW)Qeimlldp_PSWyCO( >vӑF B !H&^* 6p7`Z/'ObIUWpǝwestrWcN=DOʙs.MͿc(-]eٵk'k֮rNŤx?]͋5rq~ ccpG75yɰɰ-4 wno S=m~Mqn/,`_2.=nzz8]T&ח,a[ۗ+.ů /.y?t?4g`XG}w #=տʊ >3^ 7뎎465QI@ÏaRMa 'G@ [QΕn ?o:K&&sH&tEgQZˌ;\tx9+W=a?P̞Iff&WwaN+nP={I3Q\ZҿA^nЇ,Ř(6sXznߴWCKn|,C0VnY%jS=ޛif3CaC1h,_#aL8N]xqͧE}[r 6;jnƃ[vv6dge1CءQ`0E NP0T̪n###,"94Mb*dB iI2!0}'Lτ ضm;rmO欳mb[fk׷;4MڍĄ[@}(t Ll&WVFTf4V* U9bD-3qb'U-k :2"m= u<W7W=DgԺ^h>̊o֓5߀H7x) Ƿ+w{<\x%wv 2t_,|Ylيk7o~}^{r֙s6thTuGJo2Ѹf!bafv "> bPXTBVN.9Hgf̞QPe&dBzLH$##͊ 8cx[ុonYjG;o&rHҼn2@evk*`(A=SnQ}Yeoc /./c0jHq5w1>-ܵg~1#0hwK|z-GL:pmw護rÐ ._NsЬ1z=cG !]>stnf*+*(--i-[nf3O۟CM{=k&c<sο,?s6=+WqsO)puGˤؾ};o( ۶lE0tQ~e})-ǎm["\tPuF۞Φ=ɄI& {+ ]C7t Oaȑyd2].x% 6u+.SNb|rp}}a˖7k-mݻ]akAZvwuo~ t UT6Ӄȧ8 L㑷\ }ƍCuTܜ|>/~@"Gt~?>]ywYz5L?)zv/@>}Z+BJ|͜gaw==Dބѥ2&(ǃ?﮽ݘѣY[x Z+WkgPӕRRRB0wd?=ƍ~PU3NS^^ HQfXٺ}'?9p**c6Kt ضO_D}R&ꌶ=Ok&Sϥd¾$zɄ= {- p\~ŕ466p88蠱\|͜9xsλ<nk5\ɟo ã<ڮ] zt=G PPP:|}tZ2qK̟yt:I&@ _]ociFFF]F#G7t~(zz=x^|>_\{h^zyΘ6 8磌44]g;sZcoHכPbqWIi>n<6%9y2=$ӧC{.9yyy;l9tg[nasW_[oqi^tIOrs?2:Tx`N#A} IDAT]bԪ]? :q񱄇G*QNsYY5Uuk,q'uB%u:AQ=r֭[s!؊J~猡NHI(tv><ԅzak$MlL,F[|4u 4$..ߎBP`ibلdr6˧֋^7hwNAKi4Rt F߅vAlr SdIUULo.uŃ Bd[M&rWtoBM܏g:A"O!"<hYlEQI Dֹ{ݤ'GƸ+Į QԩvP.H D uYuJcg5!^WU BR'u\7I5G6:1̓HR'QI D$T&YN_N(4՞jH D!uYu*#ߤ(v#HH D'uYuJBe%ϧ&B=MܾM!>:wW0B! zB!yBB!BpB!p4!B! !7jݎj͍ !2xxs<2"{ 9olP  MQQQ#@bK%&`0Jߟ" rtvkDH̭r !0™Sc1]$!@v񊎊F: Fb4څB,11(W";%K" Q,$7ճnۉd"M$M!(lKb^w!LI#Bh2*yJqB!pĸ4EB!HnjXNSB!wc]!B"+=rsECX ғrSOz:N>@5xoBBsB!D1+  ! W+2L;?L2}g`?0qʇkTX1*B'I KEU/G!Ck:ÆM_̄VF!xq|pB\q_hw!z2=::%Kϯ111.+B>Bs.(2qbq2eS~-|}}vфB"1SN-4!l׭S8@S~!խ#BMZnN̩BgWi=uwǛ[E`֛RO!9$ !Rsg1 /jXp/Q6(H !"r']z҄(VUZUkfBqܥ'M!BBQe]!B9Az܅B!rrB!n9-B!DA<.!B!pC.S" !BqWIB!dT!B! 䶺*#B!D!;1c/B!]K3JB!w !BQ 128iz.B3w1BdCrpK !Dq3~̨.f]E(TUEUUVk~@@ӡHBT(^=(?$6_y݃a&6l2=ѳ{<,Qa0=)_<.NdML %&`0HIR)hBSPї!DOglظ)MJ3-SA:!իClLT~@'xrĸ繠ޜVM][HXU]S+^:_J ͆^E+kBoMލU˗M۴ gnn_P-22Z fX65 s*U2$HV/AjUqˮ? F~"J}ƽ^TL!zQGQGݷaӗ#u b]}v""1u#S੉(EdT$%JHB^ HT\8obsT)Xo:6zcG-7n'@oǨS]\ݴJUU@45S(h'ܨ*DAT}R{n%X5©xOF❚ /DCSuPb`nKų|)%9: R>Y_u|Vd)ǭRKQHqE.MqZC}=.&z9ʏ7Q nӥ v+.QB Aܰ*p):]NKF13-&&fIvoس&~NdYa#;vb颅ti[SSd2+nEb2yRV- Dժ!a L qqqʐS?\2jS;>[yG:֓je܄ҹ_}rM.XMժ!؆qc=V+C=l2@ci7n`8qի/9m~7w!{>xAA/] ߢj^|,aС}[vC玡i;9o?C;ow}Muz={^:in&ߵ{!:O>ͅݺe˶ԬQGͷ߲u w]|u,[N{spЏ,^~+G}8{v PUsf|J*VƳif֬[ϰ!Xv-5mC]yVVkF*mS<X w& / >%)j5i Q\XV.Cb]t+ծUezJ~~~Ԩ^D ;gV*?N+U e>ԭ[xR>+Us4WQR%Jٳӭϗ}8}bV>[u;&gA'!!r_Yd)xMj  ֭[OGPU*UPBtGII366s3oL~_Xshu? ~FJyYn?_Q#M>?ƍˤISy7[L43>漈I֬[ϥ0ʕ-|ҥ0"##iڤ1xko͙g\nP:?Lƍ:$sգ;[mϴ+Olq{=oAf,QOBܼupiB0n ^^^7q 9GJ8q$+XÕWg`ԫ[6YӦzZ6okָyE.5]3}Fvb4z'Nɓ:w⍷uԩ<1|&'ͩN՚tڱY3mOEQxM0be(&v;\bJnnUbn-9zߒ}ӵsG|ځZa2>s[6gEF{rYԫ˹sgitOC2S?O0w1<ځWxpɓ6YΞNq3 fhJ-q,=$mgأ<=Ic>h3_i4]y4y {Y~4)Squf|)|c+Ed`0жM1UC}qoNG玡|T wmd朹4nNil+F}2,CdpJ%}!ڵk4EQ8ϿL2{nF0UPByƏӗ П7^}#Gt~VZM=S6f+:y"-7p krŝcIrC=#%|}1v)~_ەSCaZm=FhS:(.4MÃ2A(ǭ]4ޛ8^=!wIo7^u/Y&Zx{e ߗ'OĈamxm-5c7$Jϕ*ʒgNEuʟ&K(G'b2ҬEl\>DFZQt5D.i1رZ3yQSzxL݊BAU5WH v)'Ov7qre6[6[mvxyG=[Fu޺:ujg͙ˀ~}i|o#z`]ƍ7z!<%_h|}}35qTf!!ҥ#_mޒj݌NdLx*f)uˍkۚ/:ljhEJWߧNSFuժVu8xqq4;4kz̐rFՆAbb>d_RݸvsXn=aaS}1Hiy|;/Yƫ/C*r( 6L*7_nᇺr<#$3Cg7v]ݷizu\ 7l.{aEi⋫>4-樂;!DaZ!]đYv7+i-[` ş1n;nLMJӫcm :4}{~:F;?[칮u bcVQXwٓXUub/sA8Bpo֯O٠ԮY+UZ:u(Tzuz_Ǚ4f3# ^}MϟG dl~jsWyٓ9s3TZOLwl=O}g#Xr {bb &Me̘'R)ӛy3r*ҥX|y+⮩ RLBB"-jQF'Ob2yj)'N!p;2bF?1"٨aC=Fv8r4lLSoTz;~l߹t?qOs*Uq#Ae f{^, W^L` wWTM#};re1UJ%A)u1Kv }Zp-,.ϵAGgӼ)D$*PNBW+KB\vTT ^ibS%im?KS?FP V+(8?؎cF1ONqcFӮL/i1{6x0Nfێ,3˗/hRp2e>d0Ud|ʳdl.ѸI|7*Lf/tfʕ2lqay ឣSu:=ݞVOE#6JE7;3CO+xT4i !ܒuEnon.T6Nˋ;z[u#8iE|~,^^u 3P!P.L4խY-rhQCR繕bS :F= V^}{44,mkc,S~IQ|->D\]{ߠ!~eiɣ\_g4sinwo!cw-08. h<* c* : .(6}9׻?hp0yyQlY._fߞ]DE9Pe0I$=n!;qN={ݧa :@Q=b&Bl뺯ۣG\#=i)⃧GJ6ő䉏7F)i"r% "9:ҹ}.\BD3?CVܤSL^^x Lf҃*:]}? גk+B#ǤvPܛHr=ii4ص#Gƫbqygs\Wy|,c}Ms'Ϭ*e IDAT7}{Y0gv5t(ԯ[7Mکӧ`9.{zc&rpW H>}B+M);TCܵ'݆C?ҵs'u#+w|)}o?C;owpmlݾٟz={^:iHڽ7aH0733((I2Z>M-B*N)w5išujKˆii3ϿHlٶ*Бx䡮-M/v'Ndk3__խi9q݋|3gR51[f_>^ з͛ݗva.B3QeڶiÑcǜ7$kџN7EQl6m۴hr^6 `0ТY3]6 o-s򯿉qnӵKgL&iHxxD=e (́C8r+W6mc?sMwz#GY ;8ҬST$]!(NTUCW>K%J>u5ߧV*Kfɿbͺ]&!1ѭm~8xFLo)O>|N1A\lIi~~n;}{sP~fMcW2p<3)l;Odɒ ח5;ӋRqR )#!ņcwP ھ=7nO_ӧw/u(\*mݛF4Eq3-۾o3wR'ǜw HiBtL 3d277ndZfw TfM,>>Y'kLs^: aC3tơÇh18c( lǸQ0B!rjP!$$$2bVU*Wv{RJ:n`0Pd ~9~umNefg޽X,\Jt}IL&Tv2e2s_ԩ]eͳn޳va]ԩ]ۙVJe=|{}n\3+:w eŌ~bD{')46"tig|~{D &c !ňY)oE3,m܄ |dBKj}SMef(o˂972)N-[4g߷3mgݙpO?+1Lv1EB!D1㘀IE9s_϶;EHnTB!i@+VZ[7\2# "  k.BQ4_'b7h&!B!DHjKB!RRpĸ !Xs@o//W΀~}(eH(Kno˯LԪY}/Ŭw|}}]&}_R^$Wج؝B! :H(߰pgҋXSX3 Le}| ~j\u?]Y3^~Q 0%lP  MQQQ#@bK%&`01;w9_y /ӄ3alj'O5Wد/ȱc߰2h@jլ>._fܺyÇ<;{l6yЀ}м}Q٬V+<"B;e6̙<۶GnJG\(Z,<ܵ+Ͼ _W]6S98׉EB!DAa4R9$3nǠwrEll,[ZkVb7leæ/4m]bjIڵ9t0>_XJ{f*Vȱc|v-z\~#MCٹ{OWυpYpwGFe3 $$$*f?Ӆ:x`Ŏ]QZH+GDGEQ|yn_'11 dEYdE"$&&r#:*T$:*lQ 9aOfGs-FL tӓNC9oz6 `0ТY3]L豟y&hlΜ}1LyU]Qv;]td2螆GGFeSF6m0=[8'Rp-F-Jq:Ndd&QnrBQY)Uʟ(, BBT Efy;gjŚu LBbꨄg&<Ŗx,YRFL7@FpvX7 h֠<2;/]'L4a<)O]Id;@M4ASsHB!( T S5 {1壏R2ee{`2\nj ҷwo'Ǎw`ؐ qa/ZNt^f311X,"##]-g]gydv^7.שS8@=R^GyE9]AEYdEYR ̢ϖx{t}.njAe f}N, W^ཉQTU%tiTUukUOHl(,Y_'>>m;v׹!*wKz=}l%*:ʙg4yZ.B exMk\aCգ;,c³ߏҿz}i1{6x0`,?+X͂şϰg=;!;wfСxx8\kޝWЯL MU.ͫ/xIHL$04m۴!};UxMQ]6%b҆6!׮\KB!DAP{wo'JpR*ҧNlT!B!Dp|MVr<1pzt__j3=ߛ87^}%o )BS9U&ފ֍*BGkWHbҵvyaaar !y$WBe{BYd%g ?>``u_}-^2;Mޟp &ko ܞ Cv\)B~604_>{ad[^~јFޟH^W !DS[CѨaC"##ٶs6nbyV+ʂE1Lu!_z3q?[uj9a~9ٲm;5k`|lݾ^=gjőĸ",ր%Jd"V\+|T(~"KΗbtQ6n`Ϗj߸qSL2pʓӑ?^yGLl,/V5U^kܼuqO?-[rWo2v38xQcǡ%u(g=Ϗ?8F `Ql%G#( *VtXTL~ Wҩ#Ff5kδ_~c6h[iY99 EEY u2=~~r*vEQaCnFb}In!,p"6:k7ljH03MVݰNGBb".^dEs֮߈6C}4!%|}IHH@UUf͘&kӬiMęgZmNQp?NXasޥг{74ұפ1G0V;'BZ_TRq5\XIjLBl{4y c@>s9F֭ݳg>P!/2* ___8qN0Lthߎ>L4a2hAΞ; @󍳧lch>PΘ&mԈ }9U ұVVgθ\ eVYKwfZp4=<#Bw݈2=ߕ+W; g+!(hJ(7(]tD7ѩ-G7qqSBuj7_bN?||G➢Ȩ(, uw/QQ\gbnd5X6Ft+>%i\9C!0Z5.3\b!:&_gC=Ytt44R @6WݻӌQHd".6o"nѥαf͛Lt&AzJ FAeA@\8ʥwYdEYd,C]:e6#V(nԭ]{S3MQxjժ:*WD t*WL*S*ų{^T\zBTTdVd$\n=lAӤ5jؐ#GϑǸa\8q܅B!QB>96x4_o{ѫGw-\=OHH0F O܋/c OJҹӦW̝v3?|ϭ힛0g|Jl\3O#$8g&?A46o|m)HΝ3>vf<9 :\8Rz/9 iD̥,mh8{B;qT?ȱc<5ItZhFƍșH,;/0 !B%rV|1|fΙK-]&۶jppдIcڲ /RB7|\v&ޛDȥ(M!"F} _{vYڶXj57oݤrʌ6$[et1EKK/b43\711ϖ.gq ,2B!Dq$2BPR&0B!IjKB!r<;",",d),\1Y78n4]a#Ǝs^V;d'?IyƱˎ䶺 !HJ݆Wnl jȢ q%i !ظq&-TT#{'Бx䡮ލ#ǎ~&#"(ȠUp{W^qr5y6g}z:4`@>4ova/3{|nݼňCl6&<<g՚}bZYq1__~N?0IKB!Ck7ljH03MVݰ9 E ٽ?_ɓF2Ly+_]fqn]zx.\ȇ>`ߴənF{/S'OM]B|<^gb58 ~VZz0w'<ԥ>_Vّy6 $$$*fȕ{ \qSEhܢU~!ƏB<$uUqI} L&ڷwp]____G튢(vvd= 3Sg0l L&mhŦ6NUҹsp׮wvݢY3/YV,(( 6m`4zd9;eF*# c M&3JŕUCtL >>>x{Lx-[)Y$fwlr #(s:mX;/֬[OXe?=1yd;d&pEB^ ߸ W#O!({V<==-cbXbɸ!W: aC3tơÇh189/LlL lgݛF4Eqsev3krϺ^yȭW NB+?O7qI"cŹzdԯONTZՙVvmv]c.ԮL+UM?? MDPۈҥQU5?*|vhw?VlPY޻•W)9J,/ǏSN؉^ޭs \qW$=Gv;V+VUUtxxx`@w z߰rvr{[Ԥ}zbUlٶJ+δ^=3o"~yBB5vs&Ocit1V|?#ߟaefLYi7oeFX2\FZoܼu ?BN^*/wˑz 0r0B۷Oɼ>Ϯ~{[׊B]%{ɗbg2lϚ~p?zvӠ^4ʶ;0d{. bcbqZ!!! XY6?ߏӄ]/_7r鬷kv̞ڮ]T&mQǺJ=`?哏q\3OrJ=ws9*W_RtО?M470ڽǹUWSZU^s|e ֬a¸q5kiSڅm۷JtL4&ZQU77v;`4RdяZ\ CQr.N*nf/WvK.t<ԥ3uA? IDAT6T7+eK/g@~ԫ[%|:cVJ+SO￳%Lz?L111L 5mBu Mq;u&1p}>iuR\m`=f&MɬY(ۨgϝA=w{5rU|y<ئ5?%JPBTe?2f3;uW_s}4Fl6Ӯm[mx'$ m(t: z=^fF1v<<<A*!oo2卉g!(~6}9լswnsLq`3)14mڔMi<ܵ+iC(n[]U|t9bzjQn 8ܳn嗼Oqӗ޻sf;p +'O{ҵKg wݲ ?y b ]|hIj7n~XSߦFjfΜ=#?jʜyi٢9֯gaiܷQQQδM^nS5ۻ(㿽].K/BbhI(WUȕRMƩPNa(I.j5WӶ}Qj8`L1kWk[:}Fs[gN` :SO42o//AR:((:X-AOBb"Dlgv=Ivɼ߶36wa_קO`0x_DjVfOHV}"3a0zpM}醍8a!!;q۷3cګ[|i-hܨ˞}HJJ&$.];ww1d`^a9J%Fc_oqS|NuQ q}پcw7;?N{Y(LV1;~vwsNs︗Z\o!cmFGG`ѻL0@iۆ_~=m޷w/_Qc "1Gͻg^F;+]WRb1܉?v-[b(t-ItY& ._8wV9{(*3Gy<}Pv_7͚F2e$̝G1˖l_ޚ=ΕÆ!IS`%K5f >s%:%f啗xBmV۪syi9 o6 V#?3k08~G z=i=m~É2zpqu[(t:7L{khZZ` Ѵlт\.-7+/#?#ʔW2Jf.^Ğ]{hԸިJY$%%oAhH>>>(sn'ƍV n czw320gdwpSח'ѣXs.2>@V &S6 WK0׮_˓[ѻW2GrR͛7o ]߿yE.JP\sjVzR W89V J ΕP|}|U" @KV-OU'gێD6i/X.w7ib$U|QT̜5N;_ʠ߰7orW BdӉ[IxzzȻfϾ(-x, 獷fR[wǭnܼ޽bfʥ.q|mӺWa ?,SS憫V+qBEA ?а{tw5u[ 'ڪ~x8=TYj5'N~pF g߉Ʊ`{dp>ݟ|M[rZ|}|WkۖSO]A}۷;Xݻ~CΔ}вEs7a+< {?ADD[u'ZwTH!J6X,=G:vꕒm+f!˲J Ŵ#4/<-/~ur^ G??Ʊ'xnB+rQ#; Q>p/^+N?3rƾx)ڵuLLeҤaYbLUQN굠Dm# mUԺU k_HHp;P]*,*IOY|2٤1];wqm_.p5ZE PYƎQE(qB"ڪKP0rPV[+S&*w+x+<*.;ѥs ͷ2dggvF "VM`H(b  Ni TinD)*FAA߾ePŴPE( CU 1]Ѽ9#h?;t2h֔޽FyŝQy  ׅ ]W^_Ծj|{# !$$]Κupy7dffl*Ԏώkas/_y<6euT>wH POv㓝ѝ]D3P+zYr E n޼ɞ_';2~(& l\ÇVסbf6E׮Ȼ0qX(7ndkصg/6ّ#_{>{=toWϠ&CenP%)PD[Uv{O>⤤bΟ?Ohh(# Ƙ2h 6l]ˋgŬ7^埩$yL}i2LNaמ=\D7oۧ7lĊ%dlRIpO6̜5d ݺҳS;~BCBzaar*siJmlp=?1cpuՒ%W;238$5kjjbfZ7np3- cVJBRj+Qr|}"Q-پcY45dOP*EΛ˽aǟբ9}}t+Z`d2aXhmm1m[by,f~\!I}fϦ|#XO2sg8~GڴjU}m*cOg:u} Ixyy9׮]s*,33e+V2q=o>IXt8-oUDdV+7oq=) ߀@F6i(F6ϟkIIܼjV]L9j9#'WڪfZa3g~SGjqciެi0??_S  )9pА$I&˸á*"6-tȺJ4z#F b2E3ggѨqRRS4Vvх;d lo͜Ahh(3|/^bۂ $ݺtfמ=f3iii޻˖D6i/b2ط n&I_,~5 DBC {FpݺN^0>јŁ/:/^^$^bדvDzMEO};Z4ov[XT;~1cOвEso&db>ڨ|3_Tp;uʮ{罹d ~7"""x tgIJN" !qw$x2Ξ;G0= ___f ώK\lܲؿ?oӧYa# 2(n͛5+v4XgȈaC_}fNXz؜xHOׇ f?SVS' u Y|||׼y֋r4,ٮzv+سojȨq uh' aC;YDSL>۵ _q!L?۶og^)^^XGٸy3fhl!cOQڦ6UQHEFYd[eZD9}1HDp7jX[m:^sgSIh`zUL4ǎm]E[Uh WW-/L_ⰰP\Zy>ZK&KDXh(!=S{{6Zn]:kCn`x oމi~s&Z-KNcZynjK{{۳bj~bYhZZu!?BVV6ɔ(I( TJ%:W-jڹ)Պ ד2i̴vTˢzΝ|z$Id2w~fZ,PۈJ(-Ɵɦ=bxethߞ&g>"Ëߨ[vŋj.\k."Dqߺ1g7 "COFF?7:/0O?W^{//O@ƍ>vww'-ya*]gΰy6._tsEmQG-7lSکJ%YYF..hZMTg,@GQڦ6UB:t%KKff͘;?K#,,^-6Zf wN^W\;;]R2bPXɴ/ۯZ4oήݻy8|w{^}%>n]:ӺU+l6d<|{M##o[8rQN }_|AJBjт۵gz䧓'ٱSPGR@g['$ īi۾aaT5{l&+3 ^a"a:ʫcܓSR8a>tnQrT/PBCBxgBիGpݺeEjV P*|2̠:<Ѓ]ruy+VbԘaAz䃍Y|~ 4q^x2K9©2ۗAaC5nׯOo_Qc#0w{YC~J^ݍchٲ% k%IBqh> Ks &1}T$8_equn]y{eۇe|k7;v~ʀ~T*L&6ŋVzMQeS*Y}"sgɟO;6nfqp nnQXi ʝQ{)JpuuEed$)˿W==wgGDJ$ڪ7D$BCB |9 o6 V#?3a,N9[3fhhGxkrX,&Bm %wPb ?cNvаݟOV#AD[%BY߷\;vcAJ$m˖ 8A>TPS<3V+f3f͆BK.MܱSѣ8g)m՝hm T-⎻pl62222R{Q)]XH?@+j PK$^1lb PJJj*:Q5dP+&)9/OR}/mtvʊ'BWyU X~4Z] lаSdg|= de!e vl_`!/N|}h&7ߞÈ!C ԲTSBn陸25ʨ1ϱ|*n99sgZٶ# ^oȲLJ :5Ĝmœ9;fs9l6nVCJZE’yvx °Q;2/n`B_fc d0Qܼq1bؐbNHHp~d>۵˗[ ϫdpN'N$%'π~4nƘ2h 6l]%n] ~ZiX,MD\l?|Jl>f>d',۷m0f̠a#x[Wzvj:~20zY[޸5?θ1t Ǘ?V1ЫMwWmo (8V t^O{ '%w@?<3=wzaa5___&M~ a%w1a¥lFzF9Z6 o=r"K ҥKLu*;:n^p=NQclIII984n\si>ظD|}8?͛5-bٱߏM[,?{۷+~Ժ-|mBBIV+ƻOHVdfxѢ:-Q-[иQ#=LHp]vc8} 73q8BCB8v[ogƴWQ*EΛ˾/O8~,lwݤ =71Y(JLYY\oz?bM̝'>Ňlδ/; ˳FpioČi:L0E?m\cF)rW}m*cOԺ{^Qqp 3xb5\x}: dKnE&`FDD0i|k7[>cF5]Lt"W$jq&cߜaZɶǜmfsj/+ JBBÜʯ4Q`y۷oǏ/6 ɡs`<ꅅr47o孙3 =oDȑYn=.x@Jȅ h37n`0+S0{h5Z:uӦ:8zt2OXdggR*0HP(]Tt:'%\ǽc}%x.] @ݺAӦ m[!8W<#?@|16lڌ,˴mӚgz=s`رxsO];VZ}[\ ݺhKK|>Ժ-|m$E|V㶪^o(/4nBb",Y_~l6tra4=Ǯ{,<==ۇF ﲇ9 z˯GsJB vndžNQ223qss+4$i:1/LKKw9ɚ(瓸c<==IIN߯\KOOG9qIKK/t+7w7..5j\ ٦,Z V'~jV*deѨ5 ˋ)).SٲbW Kpp h_x%!Ikǽrh'^ʷKx)nyxyz2p@_> [m+Ŏ;nmQPB&Jguh2vqd&!*\tCxyyyk0<~ Ǡ8e5,=<==@fF:WWVO^DlĨ1cK/MG΍7*S``hR[MW CQx1ɹçT*q z2c~[߼Ihx}* PԜ΍,@HHBHp΃=ő$+ +tQ:?ڑ{st1| Yl9/ZXhڋ.%o_OCF#PPv* F0ݴ=wߏT eܸy4`X %S_N\Xٷ9k6:v$&: ;y$ICЩ#k4 C`` IzퟣtϧN)Iݺt懣|4)&9#9շWV*Ϊho7m[ƘšCpYƏ͹CM|^J=M///ARɾ/㏱k>"#﮶#e,V W Chx\\n7W]YV:tP,û$VxڶϏ+WuE,MYsn_ٽ{6st6 tLV1amE?uX'/`IHL$ r9V\SiР>|筪s[U珳g긷kۖSO]}m?$nc嚵08,bu8%`/}ˤ&' "-AXXHöz\u:Z6oΐ4k֭`@lFdID3cf&˗,O^,^gǎAD}FY l ׮*(. dcj13ЋDIt;lbLO2.gxQʣE9t([>^y t:1ь1ܩs=`&X/owӿo_f͙ՕC I+nu뾔su[J~ W綪iͱ'h:gV_/ƕoO?aC 64n~BӓeV1хΜR/,%3MMx -׎ fά7 ݍ_hyk:_3!J~ǏHvm Tpimެ͛5+2C edԗ M'=1~ ϳ8~ѭb6bfPPJ  G4^mV+/3aGHM?6 WU˖_7utImD%n/\pΤ˨T*&=?_d$&&"CdtILZp׮*6NsO-TR/.4joo/TJ%F#IIoxyxDJ'/AP0rPV[+S&X=u+ I{ΝҹSEVf 6VOx :Fz+pXVTJ%:WꅆプyOUX呗 UGE_BzBbGrj+Z͌ڦ|:Gpde3htnhe뻨:(L}V=ٱs';>)|A=lRW*/V P3ƽ5RMU,ېJLXzTĶ=zO9]y+8g(KU $5bPrw O yemD[%BY^٥R;zDeAX,4] *G\GAdb+f;ٱyܻ3/|3lPK]vbbEnp]4$dgggsE̦,T*@eM8}Y.?X_h؎O?uU^AXT~Wn~]z |'PVVLٌfCPvqťl+ ';?-r&Ga TQ] ϐFpX7R1gel7R &=-")B7l6i$$W'#iCr=9tl6[eUAYeʖ`wʍL+m~&/naw*8nBiYVRRS !ARC@?gRRSuD5}_Vd^Ζ"+Bg0,c4elCK~wMh&E<7>(q%5{\^2STqv:/GJQtRݭussQÆϯQdY&9Fb6a&:ɩ7Vrb T-9cܥ2^e{xrK|+ gJ8n]ˈKMKY +vࠂh+1=#_`يLej233IQ-1HM曯UMR B%s<3H"B%~N7[,<7~cm IF}~"˗,͍L $VoQ>ǏCa6yn:wgvBRRӫg;/UIE*p6oʄqcd?55#2:sg7;;^y[z\׭Zɩgذy30  --Vq/0|ꇇߖba5+gF.ٽwAWHGQ$7&(oFZZ:c2gcZW҆/w771PTEK _P;[rɔFqF7ޙ~:ɖmw s4oڔs-YJHpϏ>g;aFpݺU{AuP'0_~^'1*hޜ{ҳS;?ߜi6<f{3l.IIDAT0 N~@GT1t VZ)CG9gINh_HNRU'ObqXw$IE&1mKXX=TbPf$^R/L@Ŏ=q;i6[N@O( )$P3QH RRRpQkvv+պt= (PJDhK0~{3n<>>> PdZ&gd,}wQ-Uq^\Z_c-t܉ ʔES3y\]u 4Ӧf2<4KWb 0lРBG0ywbBjK3(062bz()6nUaȠΟ# nnt'** cfF$It:8b\p T{5d:H͆bI5TϼV V+Ӫ-ׯ&fx,u F*;+z/9tW1U\.h1צrgZqww#U+&ذY,(T*Fcd3_uŌ2B;zeú5%S\y}9q(AuĹ+ Տu[6TFPbZ^q+ < z}/f[흳B\']DJiPUYm66m³F>UJ¥K./oTJ%F''/e0"#V9rOeb T2qW ˶b@k\]ȕ ӋM[uѸV"@BAU 9^3zҤq#Q?FC?II\x?ՊJU%,.>>xzzhjݟ|ȼݪx6UPJP135'B0h.xctbȅVjxjRTv;PvZ-j?? zdLجVJ%Z7ZF.wPܐa'ʜf B)Pw!6Vɻkq7&B@sEEِ{]auDm*?*Jܕb   T%7op61oW+FsAAʕi4`4fr4>7OaEEZyAAJb4frI= }ȑeŗRAA<V[*Z{LIENDB`muse-4.0.0/src/doc/html/split/000077500000000000000000000000001404105311000161045ustar00rootroot00000000000000muse-4.0.0/src/doc/html/split/developer_docs/000077500000000000000000000000001404105311000211015ustar00rootroot00000000000000muse-4.0.0/src/doc/html/split/developer_docs/developer_docs.css000066400000000000000000000017551404105311000246200ustar00rootroot00000000000000/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ .MATH { font-family: "Century Schoolbook", serif; } .MATH I { font-family: "Century Schoolbook", serif; font-style: italic } .BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold } /* implement both fixed-size and relative sizes */ SMALL.XTINY { font-size : xx-small } SMALL.TINY { font-size : x-small } SMALL.SCRIPTSIZE { font-size : smaller } SMALL.FOOTNOTESIZE { font-size : small } SMALL.SMALL { } BIG.LARGE { } BIG.XLARGE { font-size : large } BIG.XXLARGE { font-size : x-large } BIG.HUGE { font-size : larger } BIG.XHUGE { font-size : xx-large } /* heading styles */ H1 { } H2 { } H3 { } H4 { } H5 { } /* mathematics styles */ DIV.displaymath { } /* math displays */ TD.eqno { } /* equation-number cells */ /* document-specific styles come next */ DIV.navigation { } SPAN.textit { font-style: italic } SPAN.arabic { } SPAN.textbf { font-weight: bold } muse-4.0.0/src/doc/html/split/developer_docs/developer_docs.html000066400000000000000000000066761404105311000250030ustar00rootroot00000000000000 MusE Documentation





muse-4.0.0/src/doc/html/split/developer_docs/img1.png000066400000000000000000000015251404105311000224470ustar00rootroot00000000000000PNG  IHDRc0PLTEgggMMM''' tttZZZ@@@444lܬ]tRNS@fIDATHVOHQ8oa E\Nx!bCۣ]!::Ț,y .ʐ7 JСof[aBV{} C-峩N+i cn1^zGDͷ9IeObm>|ԌزÎc(c]RT@@$pbj6.bpo`üMY٣ɴ2bw|iѢt^']B۩W\5%0jG U=&C@hit)# Hy=&+?0,d!qV].!qp84ٺFyIENDB`muse-4.0.0/src/doc/html/split/developer_docs/img2.png000066400000000000000000000005661404105311000224540ustar00rootroot00000000000000PNG  IHDR=0PLTEgggMMM''' tttZZZ@@@444lܬ]tRNS@fIDATc`ff~0>u p6 C0)(} pNFFc@k (r$005xP@ (',5 |'9@$A~3` $߉)8X>z8Ez.!) &ƠoālTm6 gx+6ÂXۃM6NgDf,Kc`+;,IENDB`muse-4.0.0/src/doc/html/split/developer_docs/img3.png000066400000000000000000000007341404105311000224520ustar00rootroot00000000000000PNG  IHDRK$ 0PLTEgggMMM''' tttZZZ@@@444lܬ]tRNS@fZIDAT8Ւ1K@9嚤鵓PETWDp~An".ե BQ2 7/ ^M{q)'<{w^88ּpPؚ`\/[e )6eYUC*W,^ 8-= oTq,\y!t*Y[⌬NaҨqbfߛadQ27QsɤY>Q5kuŮ(e ʁeET6, <\Efl} >5$܇Ի d3 S ~Jz^zD(i&q8_/r8<` LIENDB`muse-4.0.0/src/doc/html/split/developer_docs/index.html000066400000000000000000000066761404105311000231150ustar00rootroot00000000000000 MusE Documentation



muse-4.0.0/src/doc/html/split/developer_docs/node1.html000066400000000000000000000067241404105311000230060ustar00rootroot00000000000000 Contents

Contents



muse-4.0.0/src/doc/html/split/developer_docs/node10.html000066400000000000000000000301411404105311000230540ustar00rootroot00000000000000 Pre-Rendering tracks Subsections

Pre-Rendering tracks

The feature

All tracks shall be able to be "pre-renderable". Pre-rendering shall be "layered". Pre-rendering shall act like a transparent audio cache: Audio data is (redundantly) stored, wasting memory in order to save CPU.

That is: Each track owns one or more wave-recordings of the length of the song. If the user calls "pre-render" on a track, then this track is played quasi-solo (see below), and the raw audio data is recorded and stored in the "layer 0" wave recording. If the user has any effects set up to be applied, then each effect is applied on a different layer (creating layer 1, layer 2 etc).

This means, that also MIDI and drum tracks can have effects (which usually only operate on audio, but we HAVE audio data because of this prerendering).

Furthermore, MusE by default does not send MIDI events to the synthesizers but instead just plays back the last layer of the prerecording (for MIDI tracks), or does not pipe the audio data through the whole plugin chain (causing cpu usage), but instead just plays back the last layer. The hearable result shall be the same.

Once the user changes any parameter (automation data or plugins for wave tracks, MIDI events or effect plugin stuff for MIDI tracks), then MusE shall generate the sound for this particular track in the "old" way (send MIDI data to synthes, or pipe audio data through plugins). (So that the user will not even notice that MusE actually pre-renderered stuff.) Either MusE automatically records this while playback (if possible) or prompts the user to accordingly set up his cabling and then record it. Or (temporarily) disables prerecording for this track, falling back to the plain old way of generating sound.

Quasi-solo means: For wave tracks, just solo the track. For MIDI tracks, mute all tracks which are not on the same synth (channel?), and mute all note events which are not on the quasi-soloed track. This causes MusE to still play any controller events from different tracks, because they might have effects on the quasi-soloed track. (You can have notes on channel 1 on one track and controller stuff on channel 1 on another track; then you would need quasi-solo to get proper results.)

Use cases

Saving CPU

On slow systems, this is necessary for songs with lots of, or demanding (or both) soft synthes / plugins. Even if the synth or plugin is so demanding that your system is not able to produce sound in real-time, then with this feature you'll be able to use the synth (this will make editing pretty laggish, because for a change you need to re-render at least a part before you can listen to it, but better than being unable to use the synth at all!)

Exporting as audio project

Using pre-rendering on all tracks, you easily can export your project as multi-track audio file (for use with Ardour or similar DAWs). Just take the last layer of each track, and write the raw audio data into the file, and you're done. (Maybe we are even able to write down the raw-raw layer0 audio data plus information about used plugins and settings etc..?)

Mobile audio workstations

You might want to work a bit on your audio projects on your notebook while you're not at home, not having access to your hardware synthesizers. Using this feature, you could have pre-recorded the stuff in your studio before, and now can at least fiddle around with the non-hw-synth-dependent parts of your song, while still having your full song with you.

Applying effects on MIDI tracks

If you have many physical audio inputs, you might already be able to apply effect chains on MIDI tracks, by wiring the synthes' audio outputs to your soundcard's inputs, and applying the effects on dedicated input tracks you have to create. This requires you to have expensive hardware, and is pretty complicated, because you need one additional track per MIDI synth.

This feature allows you to apply effects on single MIDI tracks, and not only on full MIDI synthes, and doesn't require you to be have that many physical audio inputs (you need to manually replug your synthes, however).

Possible scenarios

Setting it up

Create a wave track, MusE will allow you to set or unset prerendering for every plugin in the plugin rack (recording the actual track is useless because it would be a plain copy). Create a MIDI track, MusE will ask you on which physical audio input your synth is connected. Setting up multiple synthes on one physical audio in is allowed, see below.

Pre-rendering stuff

When the user presses the "pre-render" button, all tracks which have been changed since their last pre-rendering will be re-rendered. If you have multiple hardware synthes set up as they were connected to one physical audio input port, MusE will prompt you to first plug the proper cable in.

Making changes

Change a note in a MIDI part, move or delete a part or change automation parameters. MusE will temporarily disable the pre-rendered information and instead generate the sound via sending out MIDI events, piping stuff through effect chains or similar. If you play back the whole song, or if you manually trigger a re-rendering of a track via the context menu, MusE will play back the stuff, record it again and re-enable the pre-rendered information.

Extensions

Automatic discovery of physical audio connections

The user plugs all (or only some) synthes' audio outs into the available audio inputs, then runs automatic discovery. This will send MIDI events to each synthesizer, and look on which audio in there's activity. Then it will assume that the synthesizer is connected to that particular audio in. Audio ins which show activity before any MIDI events were sent are not considered, as they're probably connected to microphones or other noise-generating non-synthes.

Audio export

As described in the Use cases, MusE can allow you to export your song in some multitrack audio format.

Cheap/Faked changes

For expensive or unavailable synthes, changing the Volume midi controller, the Pan controller or similar "easy" controllers will not trigger a complete re-rendering, but instead "fake" the change, by changing the volume data directly on the recorded wave. This might require some learning and might even get pretty complicated.

Intelligent re-recording

For tiny changes, MusE shall only re-render the relevant part. If you change some MIDI notes, then begin re-recording shortly before the changes, and end re-recording as soon as the recorded stuff doesn't differ to much from the stuff coming from the synth. Then properly blend the old recording with the updated part.

muse-4.0.0/src/doc/html/split/developer_docs/node11.html000066400000000000000000000121071404105311000230570ustar00rootroot00000000000000 Slotted editors

Slotted editors

Currently, MusE has the pianoroll editor, drum editor, score editor, then the controller editor which is inside the pianoroll/drum editor. All these editors have a very similar concept: the "time axis" is vertical and (almost) linear, they handle parts, and events are manipulated similarly.

A unified editor shall be created which allows you to combine different kinds of editors in one window, properly aligned against each other. These "different kinds of editors" shall be handled as "slots"; one unified editor window consists of:

  • A menu bar, containing stuff suitable for the complete window, which might include window name, MDI-ness etc.
  • A toolbar which contains controls suitable for every single slot.
  • A container with one or more slots; the slots can be scrolled in y-direction if there are multiple slots.
  • A time-scrollbar with zoom

Each slot contains the following:

  • A menu button, button box or control panel for setting up this particular slot. This could contain "note head colors", "show a transposing instrument" etc for score edit slots, "event rectangle color", "grid size" and "snap to grid" for pianoroll/ drum editors.
  • The actual canvas
  • A y-direction scroll bar, possibly with zoom control (for pianoroll editor)

The main window does not show its scroll bar if there is only one slot, because the slot's scrollbar is sufficient then.

Slots can be added, destroyed, moved around, maybe even merged (if the slot types allow it); basically, you can compare them with the staves in the score editor.

The slots shall align against each other, that is, if a score editor slot displays a key change with lots of accidentials, then all other slots shall either also display the key change (if they're score slots) or display a gap. Events which happen at the same time shall be at the same x-coordinate, regardless which slot they are.

muse-4.0.0/src/doc/html/split/developer_docs/node12.html000066400000000000000000000062201404105311000230570ustar00rootroot00000000000000 Controller master values

Controller master values

All controllers (MIDI-controllers and also automation controllers) shall have one set of "master values" which allow you to set a gain and a bias. Instead of the actual set value, $\textrm{value} * \textrm{bias}
+ textrm{bias}$ shall be sent to the MIDI device / the plugin. For controllers like "pan", the unbiased values shall be transformed, that is, a pan of 64, with $\textrm{bias}=2$ and $\textrm{gain}=0.5$, shall be transformed to 66 (because 64 is actually 0, while 0 is actually -64). These values shall be set in the arranger and wherever the actual controller/automation values can be edited.



muse-4.0.0/src/doc/html/split/developer_docs/node13.html000066400000000000000000000051771404105311000230720ustar00rootroot00000000000000 Enabled-indicator while recording

Enabled-indicator while recording

The MusE-plugin-GUIs shall display a small LED displaying whether a controller is currently enabled or disabled. By clicking this LED, the enabled state shall be switched.

Furthermore, there shall be a dedicated window which only lets you switch enabled/disabled states. This will be useful when using external GUIs or the MIDI-controller-to-automation feature, to re-enable a controller when in fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH mode.



muse-4.0.0/src/doc/html/split/developer_docs/node14.html000066400000000000000000000050451404105311000230650ustar00rootroot00000000000000 Linear automation editing

Linear automation editing

While holding some modifier key (like shift), operating the MusE-native- GUI sliders shall only generate control points when clicking and when releasing the slider. This will result in linear graphs for continuous controllers, and in large steps for discrete controllers (which is in particular useful for stuff like "which low/high-pass filter type to use").

Maybe make this behaviour default for discrete controllers?



muse-4.0.0/src/doc/html/split/developer_docs/node15.html000066400000000000000000000054531404105311000230710ustar00rootroot00000000000000 Symbolic names for MIDI ports


Symbolic names for MIDI ports

MIDI ports shall have a user-defined symbolic name (like "Korg" or "Yamaha DX 7"). The mapping between these symbolic names and the hardware port (like "ALSA midi out port") is stored in the global configuration.

Song files only specify the symbolic names as the ports associated with their tracks. No information about physical devices/port names, but only symbolic names are stored in the song file.

This resolves the issues mentioned in 1.2, and also allows the user to share his pieces with other people: They would only have to set up that symbolic-to-hardware mapping once (collisions are unlikely, because an equal symbolic name should usually mean the same device) and are happy, instead of having to re-map every port for every song.



muse-4.0.0/src/doc/html/split/developer_docs/node2.html000066400000000000000000000073261404105311000230060ustar00rootroot00000000000000 Internals - how it works

Internals - how it works

This chapter explains how MusE is built internally, and is meant to be an aid for developers wanting to quickly start up with MusE. For details on why stuff is done please refer to the following chapter.



Subsections

muse-4.0.0/src/doc/html/split/developer_docs/node3.html000066400000000000000000000165011404105311000230020ustar00rootroot00000000000000 User interface programming

User interface programming

We use the QT Toolkit for GUI- and other programming. The QT-Assistant is an important tool for getting help. Almost everything can be looked up there.

GUIs can be either be hardcoded (see fterfirsta`a=95 _ `a=58 : `a>64 `a<91 aaa <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rranger.cpp for an example) or can be created using QT-Designer (see the dialogs under fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>idgets/function_dialogs/ for mostly cleanly-written examples). Don't forget to add your fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>pp, fterfirsth`h=95 _ `h=58 : `h>64 `h<91 hhh <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>> and fterfirstu`u=95 _ `u=58 : `u>64 `u<91 uuu <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>i files to the corresponding sections in the fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>MakeLists.txt!

Additionally, MusE offers some custom widgets, like menu title items etc. Following, there will be a small, unordered list about custom widgets:

  • fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::MenuTitleItem: Provides a title-bar in a fterfirstQ`Q=95 _ `Q=58 : `Q>64 `Q<91 QQQ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>Menu.
    Usage: fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>omeMenu->addAction(new MusEGui::MenuTitleItem(tr("fnord"), someMenu));
    Defined in fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>idgets/menutitleitem.h.
  • fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::PopupMenu: Provides a fterfirstQ`Q=95 _ `Q=58 : `Q>64 `Q<91 QQQ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>Menu-like menu which can stay open after the user checks a checkable action.
    Usage: just create a fterfirstn`n=95 _ `n=58 : `n>64 `n<91 nnn <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ew PopupMenu( true|false ) instead of a fterfirstn`n=95 _ `n=58 : `n>64 `n<91 nnn <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ew QMenu(). (fterfirstt`t=95 _ `t=58 : `t>64 `t<91 ttt <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rue means 'stay open')
    Defined in fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>idgets/popupmenu.h.

muse-4.0.0/src/doc/html/split/developer_docs/node4.html000066400000000000000000000243451404105311000230100ustar00rootroot00000000000000 Configuration Subsections


Configuration

Configuration is a bit pesky in MusE in its current state. If you get confused by reading this chapter, that's a sign of a sane mind.

There are three kinds of configuration items:

  • (1) Global configuration, like coloring schemes, plugin categories, MDI-ness settings
  • (2) Per-Song configuration, like whether to show or hide certain track types in the arranger
  • (3) Something in between, like MIDI port settings etc. They obviously actually are global configuration issues (or ought to be), but also obviously must be stored in the song file for portability. (This problem could possibly be solved by the feature proposal in 3.7.

Reading configuration

fterfirstv`v=95 _ `v=58 : `v>64 `v<91 vvv <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>oid MusECore::readConfiguration(Xml&, bool, bool) in fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>onf.cpp is the central point of reading configuration. It is called when MusE is started first (by fterfirstb`b=95 _ `b=58 : `b>64 `b<91 bbb <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ool MusECore::readConfiguration()), and also when a song is loaded.
It can be instructed whether to read MIDI ports (3), global configuration and MIDI ports (1+3). Per-Song configuration is always read (2).

When adding new configuration items and thus altering fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eadConfiguration(), you must take care to place your item into the correct section. The code is divided into the following sections:

  • Global and/or per-song configuration (3)
  • Global configuration (1)
  • Code for skipping obsolete entries

The sections are divided by comments (they contain --, so just search for them). Please do not just remove code for reading obsolete entries, but always add an appropriate entry to the 'skipping' section in order to prevent error messages when reading old configs.

Writing configuration

Global configuration is written using the fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::MusE::writeGlobalConfiguration() functions, while per-song-config is written by fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGui::MusE::writeConfiguration() (notice the missing fterfirstG`G=95 _ `G=58 : `G>64 `G<91 GGG <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>lobal; both implemented in fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>onf.cpp).

fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteConfiguration is actually just a subset of the code in fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteGlobalConfiguration. Duplicate code!

Song state

Additionally to per-song configuration, there is the song's state. This contains "the song", that is all tracks, parts and note events, together with information about the currently opened windows, their position, size, settings and so on. Adding new items here is actually pretty painless: Configuration is read and written using fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usECore::Song::read and fterfirst:`:=95 _ `:=58 : `:>64 `:<91 ::: <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>:write, both implemented in fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ongfile.cpp. There are no caveats.

How to add new items

When adding global configuration items, then add them into the second block ("global configuration") in fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eadConfiguration and into fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteGlobalConfiguration.

When adding just-per-song items, better don't bother to touch the "configuration" code and just add it to the song's state (there might be rare exceptions).

When adding global configuration items, make sure you add them into the correct section of fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eadConfiguration, and into fterfirstw`w=95 _ `w=58 : `w>64 `w<91 www <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>riteGlobalConfiguration.

muse-4.0.0/src/doc/html/split/developer_docs/node5.html000066400000000000000000000612671404105311000230150ustar00rootroot00000000000000 User controls and automation Subsections

User controls and automation

Handling user input

Plugins and synthesizers

Overview

When the user launches a plugin's GUI, either a MusE-window with the relevant controls is shown, or the native GUI is launched. MusE will communicate with this native GUI through OSC (Open Sound Control). The relevant classes are fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginGui, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginIBase (in fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>lugin.h) and fterfirstO`O=95 _ `O=58 : `O>64 `O<91 OOO <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>scIF (in fterfirsto`o=95 _ `o=58 : `o>64 `o<91 ooo <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>sc.h).

If the user changes a GUI element, first the corresponding control is disabled, making MusE not steadily update it through automation while the user operates it. Then MusE will update the plugin's parameter value, and also record the new value. When appropriate, the controller is enabled again.

Processing the input, recording

Upon operating a slider, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginIBase::setParam is called, which usually writes the control change into the ringbuffer fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo. (fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::apply(), fterfirstD`D=95 _ `D=58 : `D>64 `D<91 DDD <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ssiSynthIF::getData() will read this ringbuffer and do the processing accordingly). Furthermore, fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::recordAutomation is called, which either directly modifies the controller lists or writes the change into a "to be recorded"-list (fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_recEvents) (depending on whether the song is stopped or played).

The fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_recEvents list consists of fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlRecVal items (see fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trl.h), which hold the following data:

  • the frame where the change occurred
  • the value
  • the type, which can be fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START, fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_VAL or fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP. fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_VAL are written by every fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>udioTrack::recordAutomation call, fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START and fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP are generated by fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::startAutoRecord and fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>topAutoRecord, respectively.
  • and the id of the controller which is affected
It is processed when the song is stopped. The call path for this is: fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::stopRolling calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::processAutomationEvents calls fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::processAutomationEvents. This function removes the old events from the track's controller list and replaces them with the new events from fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>recEvents. In fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE mode, just all controller events within the recorded range are erased; in fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH mode, the fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_START and fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>RVT_STOP types of the fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlRecVal events are used to determine the range(s) which should be wiped.

How it's stored

Automation data is kept in fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_controller, which is a fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlListList, that is, a list of fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlLists, that is, a list of lists of controller-objects which hold the control points of the automation graph. The fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList also stores whether the list is meant discrete (a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value). Furthermore, it stores a fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>curVal (accessed by fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>urVal()), which holds the currently active value, which can be different from the actually stored value because of user interaction. This value is also used when there is no stored automation data.

fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::addController and fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>emoveController are used to add/remove whole controller types; the most important functions which access fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>controller are:

  • fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rocessAutomationEvents, fterfirstr`r=95 _ `r=58 : `r>64 `r<91 rrr <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ecordAutomation, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>tartAutoRecord, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>topAutoRecord: see above.
  • fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eekPrevACEvent, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>eekNextACEvent, fterfirste`e=95 _ `e=58 : `e>64 `e<91 eee <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>raseACEvent, fterfirste`e=95 _ `e=58 : `e>64 `e<91 eee <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>raseRangeACEvents, fterfirsta`a=95 _ `a=58 : `a>64 `a<91 aaa <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ddACEvent, fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>hangeACEvent, which do the obvious
  • fterfirstp`p=95 _ `p=58 : `p>64 `p<91 ppp <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginCtrlVal, fterfirsts`s=95 _ `s=58 : `s>64 `s<91 sss <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>etPluginCtrlVal: the first returns the current value according to the fterfirst_`_=95 _ `_=58 : `_>64 `_<91 ___ <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>controller list, the second only sets the fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>urVal, but does not insert any events.

Whenever a fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList has been manipulated, fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGlobal::song->controllerChange(Track*) shall be called, which emits the fterfirstM`M=95 _ `M=58 : `M>64 `M<91 MMM <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>usEGlobal::song->controllerChanged(Track*) signal in order to inform any parts of MusE about the change (currently, only the arranger's part canvas utilizes this).

Enabling and disabling controllers

Disabling the controller is both dependent from the current automation mode and from whether the GUI is native or not. In fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE mode, once a slider is touched (for MusE-GUIs) or once a OSC control change is received (for native GUIs), the control is disabled until the song is stopped or seeked.

In fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_TOUCH (and currently (r1492) fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_READ, but that's to be fixed) mode, once a MusE-GUI's slider is pressed down, the corresponding control is disabled. Once the slider is released, the control is re-enabled again. Checkboxes remain in "disabled" mode, however they only affect the recorded automation until the last toggle of the checkbox. (Example: start the song, toggle the checkbox, toggle it again, wait 10 seconds, stop the song. This will NOT overwrite the last 10 seconds of automation data, but everything between the first and the last toggle.). For native GUIs, this is a bit tricky, because we don't have direct access to the GUI widgets. That is, we have no way to find out whether the user doesn't touch a control at all, or whether he has it held down, but just doesn't operate it. The current behaviour for native GUIs is to behave like in fterfirstA`A=95 _ `A=58 : A <`<=95 _ `<=58 : < u@nderscorehyph<271>>UTO_WRITE mode.

The responsible functions are: fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::oscControl and fterfirstD`D=95 _ `D=58 : `D>64 `D<91 DDD <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ssiSynthIF::oscControl for handling native GUIs, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::ctrlPressed and fterfirstc`c=95 _ `c=58 : `c>64 `c<91 ccc <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlReleased for MusE default GUIs and fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::guiParamPressed, fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiParamReleased, fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSliderPressed and fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSliderReleased for MusE GUIs read from a UI file; fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiSlider* obviously handle sliders, while fterfirstg`g=95 _ `g=58 : `g>64 `g<91 ggg <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>uiParam* handle everything else which is not a slider. They call fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableController to enable/disable it.

Furthermore, on every song stop or seek, fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers is called, which re-enables all controllers again. The call paths for this are:

  • For stop: fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::stopRolling calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::processAutomationEvents calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::clearRecAutomation calls fterfirstT`T=95 _ `T=58 : `T>64 `T<91 TTT <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>rack::clearRecAutomation calls fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers
  • For seek: fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udio::seek sends a message ("fterfirstG`G=95 _ `G=58 : `G>64 `G<91 GGG <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>") to fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::seqSignal which calls fterfirstS`S=95 _ `S=58 : `S>64 `S<91 SSS <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>ong::clearRecAutomation which calls fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::enableAllControllers

muse-4.0.0/src/doc/html/split/developer_docs/node6.html000066400000000000000000000052151404105311000230050ustar00rootroot00000000000000 Design decisions

Design decisions



Subsections

muse-4.0.0/src/doc/html/split/developer_docs/node7.html000066400000000000000000000171361404105311000230130ustar00rootroot00000000000000 Automation Subsections

Automation

As of revision 1490, automation is handled in two ways: User-generated (live) automation data (generated by the user moving sliders while playing) is fed into fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo. Automation data is kept in fterfirstA`A=95 _ `A=58 : `A>64 `A<91 AAA <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>udioTrack::_controller, which is a fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlListList, that is, a list of fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlLists, that is, a list of lists of controller-objects which hold the control points of the automation graph. The fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlList also stores whether the list is meant discrete (a new control point results in a value-jump) or continuous (a new control point results in the value slowly sloping to the new value).

While fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo can be queried very quickly and thus is processed with a very high resolution (only limited by the minimum control period setting), the automation value are expensive to query, and are only processed once in an audio driver period. This might lead to noticeable jumps in value.

This could possibly be solved in two ways:

Maintaining a slave control list

This approach would maintain a fully redundant slave control list, similar to fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo. This list must be updated every time any automation-related thing is changed, and shall contain every controller change as a tuple of controller number and value. This could be processed in the same loop as fterfirstP`P=95 _ `P=58 : `P>64 `P<91 PPP <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>luginI::_controlFifo, making it comfortable to implement; furthermore, it allows to cleanly offer automation-settings at other places in future (such as storing automation data in parts or similar).

Holding iterators

We also could hold a list of iterators of the single fterfirstC`C=95 _ `C=58 : `C>64 `C<91 CCC <`<=95 _ `<=58 : `<>64 `<<91 <<< c@amelhyph<269>>trlLists. This would also cause low CPU usage, because usually, the iterators only need to be incremented once. However, it is pretty complex to implement, because the iterators may become totally wrong (because of a seek in the song), and we must iterate through a whole list of iterators.

Just use the current data access functions

By just using the current functions for accessing automation data, we might get a quick-and-dirty solution, which however wastes way too much CPU resources. This is because on every single frame, we need to do a binary search on multiple controller lists.

muse-4.0.0/src/doc/html/split/developer_docs/node8.html000066400000000000000000000104041404105311000230030ustar00rootroot00000000000000 Feature requests

Feature requests



Subsections

muse-4.0.0/src/doc/html/split/developer_docs/node9.html000066400000000000000000000047151404105311000230140ustar00rootroot00000000000000 Per-Part automation and more on automation

Per-Part automation and more on automation

Automation shall be undo-able. Automation shall reside in parts which are exchangeable, clonable etc (like the MIDI- and Wave-Parts). Global per-synth/per-audiotrack automation shall also be available, but this can also be implemented as special case of part automation (one long part).



muse-4.0.0/src/doc/html/split/documentation/000077500000000000000000000000001404105311000207555ustar00rootroot00000000000000muse-4.0.0/src/doc/html/split/documentation/arrow_tool.png000066400000000000000000000004021404105311000236460ustar00rootroot00000000000000PNG  IHDRoHCIDATx͓M0Fۤ7Ņ(weMԸ0 *ud L i1L٪1F bµjes'Z;( V.Eo Lg:|jָYWezPQ"i:, ȅ)_I< %Gqʩb ==b(>{|S G_RU+IENDB`muse-4.0.0/src/doc/html/split/documentation/documentation.css000066400000000000000000000017771404105311000243540ustar00rootroot00000000000000/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */ .MATH { font-family: "Century Schoolbook", serif; } .MATH I { font-family: "Century Schoolbook", serif; font-style: italic } .BOLDMATH { font-family: "Century Schoolbook", serif; font-weight: bold } /* implement both fixed-size and relative sizes */ SMALL.XTINY { font-size : xx-small } SMALL.TINY { font-size : x-small } SMALL.SCRIPTSIZE { font-size : smaller } SMALL.FOOTNOTESIZE { font-size : small } SMALL.SMALL { } BIG.LARGE { } BIG.XLARGE { font-size : large } BIG.XXLARGE { font-size : x-large } BIG.HUGE { font-size : larger } BIG.XHUGE { font-size : xx-large } /* heading styles */ H1 { } H2 { } H3 { } H4 { } H5 { } /* mathematics styles */ DIV.displaymath { } /* math displays */ TD.eqno { } /* equation-number cells */ /* document-specific styles come next */ DIV.navigation { } DIV.center { } SPAN.textit { font-style: italic } SPAN.arabic { } SPAN.textbf { font-weight: bold } muse-4.0.0/src/doc/html/split/documentation/documentation.html000066400000000000000000000142571404105311000245250ustar00rootroot00000000000000 MusE Documentation

Image muse2




muse-4.0.0/src/doc/html/split/documentation/img1.png000066400000000000000000000011451404105311000223210ustar00rootroot00000000000000PNG  IHDR[#~n[0PLTEgggMMM''' tttZZZ@@@444lܬ]tRNS@fIDAT8ݓOkAƟ Czrg#?AAzXJуGE^4E=x֛Ggv6D^ٝy7, ^ bjC ,{EmhoKܔ\ ];0Àgz %Y$4uIiSK%4Fu^;eZP:81Yi@.1Hn2_[q;33# 3J|Ea<{H(}KbVΝ;aDپY1ug~}+BelXШCg8ɶ?}6W+OP$Yzیn_k^yJ ~&7/vxLe'VJd[g-7a27/[? MusE Documentation

Image muse2




muse-4.0.0/src/doc/html/split/documentation/main_window_add_track.png000066400000000000000000003065131404105311000260020ustar00rootroot00000000000000PNG  IHDRBsBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATxw|ƿ- HQ)l( ,+**ذ+ T)* *H=!d);Ds9=̽;+!M]%z#Oi9-崜rZNi9-a7ᵝд֊42`23'HUNutO%{s )J:g'Ib=-UԪWTֺr)16zN [[h;7 C;%?J^7s ))*gW߭MerVDg)sp3xra9餛rL|yvpEn7ԯ_HRܜ&5[ "G|y)XLȩ/I') 詔C: o{Ƅ}fd+.kn&*$A=I*aīa(;sR_&qy J9 a'&i8QRifh@EhQ OH<*r"cSۊ$S`7Ӱo=C4Z:qg]c%n};ƺfl<6TIoՊ `DJZ IjSc3N?T itvj~?m-.l2 ]Ģ=P=~+p,.:9WJb)J{\L'a$"ǰx)8P`oM{s6ƷbǤ7X\b<*}k5j!V ÁٟkE6YʺIYm4 U+Y2`HGfe-"3HC6JNGe_r6ȘU{Ν4k֬ʰ_x9, ßz Ʉ}o6cD$mNf˵BЪUKnŗq4Mf2ǎRf-eŸ\ΨZ28v,  :@ƪ}PeZgm^tIU(,w"D"'zBnNj,Q}:wtP^Qi䟈Veڦ5ٓ>c]Iy~VDfA2vÁEν3~ٴiՒ6fV'5ӨFiFzxB|WH/?eMIhJYExkxWr7,@k ,Z5oFC`#0HdH'=wxngsxnۙ1FoY,d׻SvOH?HMbV~e?_gpkHYdQ~`IpV{+K7MeNAn7obK9!6e+3}DWt%-7g+W΋?蕿-Kw>?`-M/f<'dO{v( ϐGCrN`wP YނEk2:!v޴hFOO{>Vj,Mf"a(bZW_#RXP墤Og'bYocΘ1L2&M9VÎ)Sٶm| _OFJj1anx, |m6["d$,2/CԬR+r!dӁR˖-r9]NN'^Ŏ!"X|s\#0}Q(GR&::z Q+QE dk/(ǗR/jQv#]uzŕe{IٜAwg(÷h\JiN2~F.$Scn2R+ޗ\[p9os)*ZM˖3UEq<ߋV Ӟ-@..wIң#~7 @"=y^4"#@8ZvZ15w>sSX,bÔ(x g$nHO~Œl_Lr:Ԡnr!I])ɰcNG)$4=vtG]өU#ZM{puM]_osS:eY͌$Iy+"k]Mi;>Z׳xw$gfJf ? PLj:â^AU% *A-؇^?!Ft]=zzaW_Q]Y- ٘ca+[ O>=bՒy'=ˈ#p]zbV51n88^zL:iӧW gY đ#Ghۮh r=^RMd甕ǠYZjZj?p4%^:ty8Nn[0λ^B!|\!{~+n/tƊmF12M(P(}Jv"L3z"PK~E=,u=6 )T+?4P'8ѫ2QIȢ"j*(:^䕁=NZ7 ~,ǥ%{oqorHR0c5_ʣ}GƳ@F6O3_X/-M-?`k~'g;:w@ '7q)kp E#9s k+ʀ>g]QĂCO.\ôť9L!J1uIV/M d=]&w\؉-[+*me안qyV5$!;0y||Y;ڴnMcֱ"Qr`}mi[1_9:c9b+tT0ݨQ:@ya|9jWzXkLKW"EO#јu|ōPjE0f?Jڇcn{Ș q}NJJ w~&SZRs'Ln:ݵj՘lVk]N' /Y}Ǯ;9bN_tUY5v|mݪ[bmlٺf8|zPZdgFݥHe#YYs *`R1it{ROu *"O%&$,Bvt23v=/&|ɡ{(ϋ)fUþ];r99rɉvnJc嗬[SQz_M޽Ob'Zp:Y7lق-[&ˡ8~Ѡuh2_7c6[;jd母ɤWv\b*[ŕ(wEFjbD)ъzU\2UCNTXJᜠ9WqJ-4gUo_WYA_Geyx@|VM_s~ſ_e&{8@vP\O"@XJ܋)cWr<3Wwmy Y7d+9SwP;0+W xeÇ~h۶ nˍWb2ڬXmqlظIgPn=P bljNX0T4ᔩ a+S%͓ҷpL^" (8>##JHhj|R\*_.!≏\fqtɘlYSe5saSÈ-5U8F]܏_8tl!px";+"Vd223S.իaD}) yvo JB%DT!2D8&resJx|[^g)]*D 8J-b'>|'ExNnoxxqcJIBvu?T,(O+H*E,6iP91?{;/4Nv†پ'94mׁFb@!pݔp*o'11CS@TC8'@;v! 'DٍHN,/K͸d=*PH˾A%Մ >6C0#p IDATFAMtBD?,Ģ[I"O% 8 CeDƬ:8X| mڼ5Mz"I6 F?-4W)IP:. Nip$v/RWHWYJ}:YB"GIȖBDxB$|*Zfe렾 B2,7i{d4RZ|\Wh_`+*LA Ս$+\zȂ/ E~:M"-Nlzc|OBk:?->o^OWvbD&(b?qzD!~CJE/Ek.¸\ Q J'#FU7@Q9=׵@&i;x>؈FgSb \1_O+hka]bqBh]@:zce|C xEFtƘ4tZ 9!I¤2_=['FУ!!!#X9DWjF(i?AaBIOy?sy,ӏx3)ܰeYRN(Hj1%p|$E M-'4O WBb-cz!i{ 3k}0]D86 N2a}i_&f QǗ@$D t "E芐J"=D_ 9G#iHB" *jD'v2vfPl*@* :0Vv,ר&yԒX)8AqqHE61BK*QPTׯJES% %j}tӄᴜ8$!p >Pѱ(XG߅$ $)$DW諓sD:~@(*&QB`d5ʗ " e=!X$A0QH:MWp\V01ci #,zqݪV٬KСBd=& IW5N`t a@@TSXIK/!a+x*i~(LP PV1}:P1]/]0%FE Vg%#UO UH~nҏH8! qZ""0GK ~>T}4`L -[|S̰?=]"tExM$zK |b 9%.FeT}Lж-W;lWPŋ1O>bhf`TV`.{< G^U;1pӘ2ٰbHRlV"gcي;6'VaUl9"22]}Y=!ϦeGYeee)--aذI'2˰axW'&pfs~ ~1{/>(BILLfSVZ*&-8tύ}Wxhq>⒛&-(| LΜUۧ/CތS;fr(#L;TeEt|A }7B9K/ƒ|ه!dgIAT\̰KT}#$"+O,)^KQV}_s1g>\q|`/B&IiXn}9QOrq8g/>WWHêbWPf*Jql}]DŽy{@o^VRZwTmTD];rety-wng^7C^Aނ;~'r` c3oKop}n{2s| >]{/3n5_qSŠauʝ:4q&xBic^c* KeQ0#RtvwyB[Uquہ{ 9Sp(3x1#W^ _+=jvkJ&{ߣ'O='ӿgO`zݟr+p 7}~Kveao%\|E\B-HD e΂8%۾gB2_R>Nw ͑t#/gWFA8ٱfR&&Ij2mt֬^oAiq!b|-VX`X1Iqdx;wwr"d_b;&7#d"=g[f WN̵fĻhfRZ?4q-I*ܗ'f}gpA:] >/T'WY^]pYFw%+s՚&V^4_MLGX #Gx FxsX5?1:5$ɝ_ ?QC^27ⷉaG 'vP|tY Lb#ZnLPZ;m1\zk#êMw}\ܴ5IgML١L-W0ْ4180yy;+Rٝ6Jj\s4 obvP׌,s{&>{eδصu% ;gR54=Ǫn}]$_J5(&5ɂh qq-o pQP}wf%|ng%N(Ģfůν&CًnuiT꿈2o\EcV 0ɚ=A(jJs1an_}Hd۸y}e/^xkِjo_D;ZiVPޏ^.&s?y/mfkxl g9?-\o|4i;;c-DŽ aIkA-HtgkI_p>Wx$P׼7?AxMx>~퓣E0!$Bq()-0f.ʺj:7ѤQlu}3vfF|;zW-gW5`繩m"||Sшk/V|c油ȄYXVGu{[`!:z}9fV 憩=쓫cV̱e"/IO&8fxF%YB}g>|cqbF"~'=3ǵGX هHk~fq+nSN|m4ɻ?tb IQ Fu'Y !^ӒIa&_<\j:ټ- Or>}]fo*lP {:ZM[Wpی1KpnyoIv$fF_OΑQW'sQ]~+,iĚ L㊏:Ci!~|)uhѫ/6> ˻ݒT13;nnw[5 ɝ};OcY 0Q>&w|&nO` Kn}OnI.QcYlj/{8?Jђ{ö0;ػ=O^Hfh\hHt׈g.倳 ͬP'5 X8_Tޭ'-y /$Uoؗš,hwˌܘ8a cQ=G˾}'ƾQye_=Çf\?_:f'uc<:emC/`[22) O =p^h*[[#]#msm b? 9ɏmpӃ{xd曜k;1/3} 91}> nCDܭ~J98ImѾ/Z_-#9 Kᆩt-ǰ;]|i%- c;o?pc<#?Z3?Dcğs/CǓްWdWQmd^ {^5:^ţî5*1sy{zrd›cBZc>ߋcKPǰ. 5'#ƽS",XxO>Ǹ|l KSg3TNR gkF_<٬zѯQص\N` 1S_L y]> ^꺗IA\ڱ/37k?~v~'3.`ԈGv&3'=E5o̔iF z,˪Okֱ(e{ѳ5͡ #$%\(| dt:ϴ_bY2kx,f\ãp<_}z>b zUmÓlR},d-~~䑯1g{ )GwIC?w>sg IfGx3w9I&}v*=ޜU3̘dxϷ7µ1!{y'dw3ﬠ(b0&듼ѧLxVX7d֬^Ibb5V+׫zX6XjIɘLU+ZmUƌ ;cޣ Դt,Vk}~Bs)?{cЧO_jhb7۷_q5cj1*;Ҏ0MpeÃ,س|];:.ۭLƓUЯwc%0v`@ڜL_gY1>K=zkAXkҢ¬Bk&R\}2w@j5CB" lVK a%h`vZMzđe?_(2K<ڳ:x4f }.i8O~?{l K5wLÁQ([W~M _Q-4߇6NV5!/҃f֢ە]fmZ&{W +SR_VlݛGPP7v6,Zpe$9NcHɵH6  +X/$œNA=IںME)ڜvL$ܘ&I%.?\ƞ_a4sjZ@W_Oksf]B!Rjgmfr hVja7wm`ȭ=ɴ 5u|zӣay)g]ٟ9~)ǚ]@$5?[BJR6 Ei  ZtM7#9&E!2WJ2KbUk aʤM7e~@;{ @J=A\NdAp%4v @+u\ٞa~sQ .(c?p}7!frU/))d=cKf԰֤kfSJCj#S&gn$4s j½0#)S :MҖX諻9<.혊@JiLb!hȷuq~̓,^} u+ݴٔz#eOl/o]̖4.laHķH z-HsxY)~݂etaBH6^83ʟdKI[V IDAT%lz˲=%[Yz)W^ـKwKض{E-\V|5{#wWGpE}k刂BhzYRV`eptrդL~9& Yp;s$_ݼ]?z;zw~77[%6Ss|t5pW4-CnZ[owqCSbila#ZXRpqLZC'pЋ'w b$lu{s龽QSZo]֧Ww7؟.8x ||];k2dZK&ݯ>(S™ˀsNk`ӊC*7KNfWOryP-QPT9x\Tl7;JKʨ]v{\y󉏋N0wwaj~̛;ﺛxk^ =wMiY&NT23TPpDCQn1q-Y-/[aGT\u5c'ܩkg$bm^=mǢl$dEpLRr\:Ho x(> ^R0ЕII6Sv/`,aJﳣL,-z_ͽ }6=#sN!?̢Gj: ӟٚ߈-+ izI?%Z`̾m7Y.%Lbr): #LB3I 3 \ʪ]FϚ_B.Wұ|n eg1|'JB9@Ҭ!}ࣄ8br'T'Alx>ɖ x-&AR2Sy`$# 7,Hj>H j/si9؊[.AHl(ŞLsS+pS*ڑqn8>\L~9ژPm[ɧ֒CpQ2`h?Ƿd% <0e MML_^6¶-CI $RPY_J<Y X^ Ƭ]ye(UrqJD 3XHbrK̤ۃA90XqZ֖"H}r5?ΤxRM^[k$0lDO,(\䮞˜KYdCx?t!lL{n)О$/ݙ7ѿ $e1b`Dkzʌ9+ [2YjŤCDX23V:i5{t U3:%q9 X`'%KV!|#]x"Lӫa*MV/%9ŐTĸZtYE3IK=/aFJN@Ib%9T?ٛ!rݗϯI2y JLC&p_B0lřt8NNLuA jmBk˸Tf.Aì)=j ~TdkcY}hzl6k`OfOZ5 E>֬#mW z?\'ʯUy%\#BKqt~Zlf}+y5h \ثp|rKe#\IL:wbfn?Y4&\l}Z}[=W$.@ON%2"x)*CH S5e ]HN$A"]ק q*Ʃ`0T+gn72.,'ا"O}K3wVJY= wD\W>jD1G w|e37^I$t){GSPN=O]5)bETrS(*HH$gnvn k^y<3|oy6/5W,&jUmߡm$Jɟ5՗ T;!qyLO7dȵC5!"(omTꩶ$ ZԺW7׽EL la;؅yE;9Uv(|??fה7+~?+pS} EӢSMTgM H"DQ\-dғ9"~3"~#Y9n]0gPNY70 6cYoj JĘq?{YL9P*0b!C_%Q]#>-􀙇- wX҉qU\a\t\jSo1߸LE0defa6-1$$c4ۙz~y^r 8yo[]V*;vpjbtGCPA\M4i4t 8bSW6~=N: :9fYʻw݇Sш2vlHyJU[R[;?)ɧ^iCZ0%?ٶH> #w_bn Ƥ,WˤSWޠ4{ڭF>C \ 䕭(e|Nq~+_ToycQfFFOΤx>}7 ǀ&u8#xU]p~[V}TL݃ 왙g~GYƷXes/_وc|[#L*8zþu$ؾv{*A `Qim9M/9a'"3/M^#Iǂ#4x7WmAV a3smX& %A0?9k^`{dejÔ}b0ykfrAt7/BV&dzu8F[/ i,Tx6?M|:lb%~:@lW0eGMT}&aXgǬY_ {Ә_ɛA;߾۟dKn-5xWFob\(f2337䑙l;)-+ڊ}^4#%[.B2CtQMTڟs|R.bǍާq֙1T39>Ә{n"$߉ڗ?TM\So㞫DQ zMWuM $s3sAh2|R L9-|!f>OY{ CrOp4ܻH?㲳숂@гOsH+? ?T2}hH{ n;.\)5S\gZG֭@:v DNIx'q)h YYڿϥ @1!^dFLM7q-h @i_։uZ 0d0}ƒѩ^e<>A0"Qh NxəQDfʍ=ə {&Kp2.Gj~/.[?q޷rEooR+v32~ Y<_~tX:({]ɒQ1nEkq~y0U yg5##>w՛0TWm'q=P$ Z?vvº&cAz"ˠ_ZF=ky1JOrf[(DyG臁c4r5MH [4.Aɢ:2itR;s*IIn} ]FYrAVDC W_I-(45#O]>n.O2~q|It)$eaG  #H sD#},>Wz2q9 EdaR-k`™[oH>ItaBDĐWW:UpF>Zy,i0:ho7t|)SP{Cx1meƎVQ$3w:E(~KP=~%c !ƍC i| lOw<R=eϢN*_c3yҤ?|J''?Q5R?۴)*#1]U1#ĴS-l; vt'8!ur8Fάe҅.4Oish'n' |ެ|BC'MN⇋n5oR~>}VRu-(ŖW_fH1_uSGlGgcӂߺ+ C rD:Bڋ!߃ &k=^M^F++=' 9r_MS%9:Rqۻ7#G*JѽyzHoIAbǎ:b$c;ѡhZ0 xS?s|=06y}3\BX6ͨ/zGFFke5nUUUlټG;vlPy([T^5UUefr:wq;N6ۓ3_qePW{\ v?KvNNȺ @QQ1999b4%?ÁhbϞ=yئi:r>r!$uQk\atETCZ*$rs{3g/tn]=޵={۵̌ġ{p8iwzD X^EEEyd^ŤgƾQqĚoFCRR" u"nw#Ƿ5ZەﰻyWGM$&2lVtEAeN'-."1!NO܏VW`00猅Bk=($x/#vyjٿ7QӬiTcHmB-8K9:mzVى`'-oVfJJJj(DjZ*y}b0E0uJնF{Va01M B8f ÃƐ{2tZ+6 N9rAlWE !9<K['tLJJbj=?gZ7Ƞ` xL M@JMUCWF{3/hLv맓<T]h4vZZQT<hq846z>8/?~I:jr^ֶ^n 0Mڷ|ȳnz5$0@ ބ8У `܀K6Z-ZQjtvc6 ŬY?h4bXX,Qz`_p^IԵS`5 /e8 j5i6|\{w4F9maېN"Dhb"9xuuFjJ 򈏷L!" 5dXΘ{&LU59t;E ;EP8]N8QEQlo?f-B{ IDATB8j %ϒӋSXyvXKzv#ɲJNMsK $%%PV^N޽;=vs铗!Ce0>"%ۛrc3y$ ^^ G"-hHKx@dem`[7!{5 t~AQR{7$%%ק =J"FhdL&͟ץŘ En{ށt߾}r`_ӑ2i6xuq]L'6}9 h<EyӾ/FVEItA$q̙l޼?5NRUuFTT75N }Nс] h(RW_ <2yOj!DQD(®{j6ZZZEgO,6f4CMc!"9!!^y8‹xH?f88Q!-N۪?jv4KI(xEj=Cޫ -HIZ^|)*`tw#Y{܂(YQ:d_mG^BBfg2dM7>V; oTyZ墮a<% 'yR؉G֐)zу)-aS]_̠9k7W7r82d0>|RJ$222عk'n|8庆BWCA-" >ll S'm7V(F.6}C6c&Nbp жU5u?(dYfY5GoeYW;(Y4h_ jZg`ׄCz88|0mswDIq'W^Z^X"\޽{PT-06HDyi)9^CɫEa1*.ub ;=N#j4OB7dT>ɉpۀQ T3~ 餥80%K$INw)IIqX۽܍#GTBJ x#gj] =ȟJ*qa1t:}(~C ƍǮ]ibAUUp }NIqP^\go[ARRR1v(FTIQCb4?>L&W餢?>^OiYAhۜ{? 'e<ҋ̙5hHMiFɆOZЫ^M "byհzD IhE-ϝ E>%W}r{$U j%*yjQN5j[pB.gt~Qő Iax^@Սd$Y#Ix<$Itp <oD^|9eT(٭౺pV<蓳%R^etǃuse۹sHnvcKců*,PMd9Ru HCaa!<\ 1[7|BNsNs'3s~~>!ի#??^-W\*$IŎӣQ_ŕn^ٽ4hE,YL~s-qv;oDx~ƌM^:Ǵq^R>NzdG3-/-M1{餤DLkq\ \$Y yXDd,#m2))…SSSOmDQ9 N6t`4VQi)涒x;Fz=g+PI.`,6iښ05ԽltUY %whMZ\^*AHII*ɲ?DQ (~Wvi~mCAbJ:3ddb֬\x1s &!)O?̴);]wdCǷ}Ϯ])((`Μ9c0T+A@e222(((_g׮b)ÏwѣF,mbGhGR9@pKԅZaGs`gt 8( j䘾Ef^d?a$)đ#݁^!isn 4)u@,in#yLZ-FIh$%%3ox$a_Mv$w(Qؠ+c$&]SorSH2$!ɲjP'tF!{|]%n/(nQQWVone@r0:ˠD Y-Rzƒ6"r7oo199ؼDjbq{h4VDA$!! W[]ãS!8 .S'ֽFQ#=LDUY sgl9&NrzOLqq3gb9kE[iCq{yE.hj$۰( (3yqKIJaӍyhs*( K^lfӦM(v KZƿjzF ?W:lsWJѐ.-)QW PyDYr'fmo utpphx~L:^zq嫭U 5j.}QW@]}}޻MbG\p`sHh&X⌔W5QSSXBjyXh'M^z^}u:xIL碋Gt؍Ah4$'cmC$J,&7wI8Zxʵ8ְvW>X l TڵX6JK7oN@WQ_$MM/c^aj#&]x(47Cުơd$?F)SBg{!pC+hBxkb6UjpA@s{KEuF/Æ +X V|@6lX/9,G׿puCY f8VI>.N8¸,vxdߠoY/tK 4vCcc#gq[~;#"~-HLL.Sc{o5kX;tymۂJKKlw}W/hnn yާ]V_qU߫H2' Fh@zЙ8¨ ( Njf}QI'!1 ;LOɅ #vGp{qy$N8Ȓ,ݲJ<px(h(lF2H뭐$Yp69!>[O][MqKTZzǃF0<&L@~jE4Z ˅#J z<jb1D*BPa~ի)yfٺVKHhBL̶3@).*bL>"Pȓ$%HxF3dʠYZZl!ܡ(\BI⡇紱y9̅ : .Z|a=Y?{&@ee|&!ǡ~j#"-TVV8 p9i3΢ERX Me '-~CEBJ$IcHNV W2Ľ _/9,YT IZvfTg-*[NUVT-C`Q5@,P/X+xLU0} y,s[>}+W~wz#f(sw8 <,|:|K7ڒ"1t:t8jkyƿond2ѫW/ \MSO>ɺE>6pj'`i 4Bl?^Mǡ*Zįi$0V7Raa_`L M\v-;.$^tr~'n/fs̈́$;e$I\HQh58^uk- (8ܒ rEV,0(%;It^\C{D}\"zΐD@݉1B2-'-ɖ;b*j%W/:(BN\L&MV+۶m#33IRyvVF(quThѓіtҷ!|7hGfz!/f 707;? ࠳7n~ A`#0nX/`FF$cm-GR&ߵ#'uaժU\wu3AHOO箻o\yfRC߀~!Wftjګ8v0rr[7Ell)0~+קy#\k׀, h4qqpk(/pbEr'7j@*Rs\O>49{[o피ˈGEdYfǎ:t={;{n*++)//Sz=~?SE2wndX\yy9۷oGcM4l:g s~v999AjvgM@d%ŌJ꣫Ҳ$]WaQZ7;e,FZ1 +nRQ)*籇auP;N^~$nfWXvu+M 2ӑ$6 A$ ضcncWneQvJ*;8 Hh5k&Ͽ'x={ԅ# 0w4mbǒ[e!2Zó$IFHNNHbg1&ϋ1Vje A-Re u봨 ,:Ybuj4U\# r&R[M3;JIQkjjO }pFgQBիJ$|cSkB* <+ץ['$!Iދ%\w5 @*x8tZ`{@YEf$:Ls<E UUU~qLsN"4|$PRRoÇ9x 唔'ᠦ(&Shۙ6gZ&HF bBʃ±- 11|&_6O  Y +7o XB] IjMvUP_fQ'{܅J>k{Į >G{)H(mj9#Ʌ1SHN^lb2hZ|NN' ,ZП6P5=m409ooeHR2go=}?Od70+$ s\_jtksYQhhl!b"ά:0]4] 9pZ~WQq}Ht:yW6cZ-SNeժUY"]zI >U"myY )N2CG*|42 r6BeY hT«~ Y$McZ nMcqA-j;Ԥ@45AI ʀ¨)3I>Æ!^k ,ـw o M[9: 9,~{u: Jh`0  \P I% vXLy8T)fus0ġ̀,]K/>s,,:Fet:999=rJ֬YCrr2&,TVV;ĸǻ6u -Mt-P4@o FXmBm CeuY2ZW}`xSwlSF@hVhJ΍g >>R>SJKKK\,uJ jTH[%eDTCEj/ރBeCp`?)٧$:77_2l%KHLJ$55U]hCw~AhCwi|_{i3pr]};0)J ;LA])S92 n@D&;3fFN-O(+SOf ̝;UVqVKSS˗/''W.J 9>- k^brCkqB%rc=z&PiӶS^^Nbw'&pAQD:EQ“A% PS#Gzow PM#:9' Yhsl6q8TՊVߊ.r0&W Li]sPh}}FL"2EEE0xܞ R4/jK{iQ5WS3&5$;&hs En~y8kt:yĦR]i0CO7+ݭmp\$OKKG7rX̳8N{}n\.uD72o\W^ͤb퓓=YRY٠h]3&fOD\9tV,455( }ļRryؑuf)/oVw- -(bsDo$?qH. YVhiiJlxnR<}dnԽjjԝomjVq,O7G[$n)%C_M5~ T t F^1r/~`4FFêo7ioioKW ZЈD!X/P4 My3NJ׾vBDU&^YTנjqݜ:r$n^OCCCơ]屹 [M-vS8P]8`vwpE%&p@@e}לּk~0atCD^x9.2?y8RQE:d0.ӻ!bk4^w}5˗Q}(ee<#LF?O_{-Grznys=z 6$%b3wZ~ss3MMM>|+Vq9_wMU{n)=XރVGvK P׃&CTFU%QJ3$ Z K>#lP_o%.n-N24l6-J,,xx&L> )ޕ} $ 4=ۨFZʽ$GP9"5W-Zt:]PUjڡ ?=x`sž#jTPyuv<=s:(oR圳fά=w y]R6}>v Q9r F9k&N|x 6R㡾d) ;7JN@QT{$ j/4>iYqTGBʥ^̿eTVV2p`UvSݩϷrQeb?FtW{iem@H }Em*i!҂%.>p?C gnrmcX0LNÆqͷ +' o"WXX\ìQ9; @zZ i)',: }5Y̟7k\yɉ̞1g\ ٩cQH( 2JNBtk][Fsy 1vnp8jm6}Yh4"zVhIcc))Dqq*Ҋ dB=:U8x 4tʯ-MuAe0ZC胺C'XIaY&%n7O" HNVUP:L4T7 0VgVٳ;f !S2335JuTA{[ϡ%uȲB) 1}_ (zzs;ozkY菭)ng6h[rT ;_(|aS}̬G ŝ/>RAyDyY~@൸G  (_XC-2 @a6IO@ (xG : ٹ};"@P, fx+-*Jms@Pp 8"AX}bpl6MLL AF#J*hKZbEwɈZ.iq}7Ȕ8,˨Ԛܗ,#IR`4W!!!.ϙ7Íb4rr)dIFSmʎLڵ nIgF 9a۸%]L&֮T*UGd2Rj\Uw<ȴ4=BFM=@jj*GaY+Tt?F˜Mph~ԯGPP^4jfө߰1z3zVჄ<B (ߔIgϝiԬY?ԐJ̩4m(Ppf-ZQVٳ, %'IEN:d۸Jm+6A)l6/:]NIs0LBHppN+/WAƝ/,>R!)A`PPIr[1l߶6mۺ|ӧrʃ=JIFl鴩"]>qFmYd.MłNbAeu-I+J~oB IRInA)/,>"2{=KO_`ŁׯTTQ{@` ~̛uV. .+;n-q;_(|xBk:Ēŋ;aeeVp;ml ,7vCn0W[47̭CƔvNr9ÈB4Fِĩ9 5kӤq *3YqGw,46 Q/J!4JY%j=֯%5wnϣg<%sB2:&8PŕR`_y6l =:!jZû6!_%vϘQORd.axtg<^Q^\=$߿%2liEL=&ybBiL1H k}M;czW#KjxgTR̊wxmʏJ6؆}]*|ל/ȖU><nuL<-[2o 0ɓ>Rt"NW㛹_a_f fQ;/poM軤9K[fF6nJi^:})>¼nsM\X6Ør=|lo.za*XdwR88*wХۃ߾=j@ \9sZAuiC8`,u(tlO¾\̑uػ4jmC,I&"}q/hx<ح3-"R8 n%q#Yj k֮9Cy`6oOV1]yX(i&v=;*n_!xB]xx.^HŸK&ǖhڼ]ILJ{h֬9'}VҢ J9Ez~m5<8~Xo [R r:[ug`VTM7a񠡬 ڣ8st5,Kz,`8>o;sޏW{xkID?V9ˉyy&"&T۽kG$&69̜Z*^{{zLdw?`CSL[5kֲvG´^",IomەJSW7\L ٬랋eӠ}"7ʴ6p&gfCT:Q5r I@t6g@I6;z3a_,Sr>},0'ĐG_%o=#2|'zyv9kbL4^ NϟgLpj15,\/G<{o/M\қ~Wq5֜T(ecx˾_]1NO*;}:>M~Nu^ܗ}Ʊg'i}xHѐ**/K.<?/ |g,X8w帰0,Z/3?1[{0&rvͩ>)1ty3ԛe6~ y^}޷?3OOߛj< ߯ûb^<=b>;SR>Oy5d2O .n8]g[Cxs) l=~=Or6ꃽ~F_ɤ77gEx7{XROJ-Y=ʾpe*ULUG_k(TT!HW|򥷕!)(u'٢+0f"g-CY~S⏽IɔAS-Fj;Pg70^B)[0g`㧰Ih}dF6k4JmZSz#٩Ay!_.5YBH|%1Yĥ4%aQ;7OX4H9W1y) ]ulRsif\}7`fMFoupE*dI@qgY49i=rfM[ϳǕ,Qt?f9PI ՇN9 ?;g`5Z/F+[>0q y|l' %Ō,KIe?!\K*_7yt:FIrJ2l!9)``W]rdaI‡,#)|23?`WȠPuQOϾ`j[ZSt1cH=M{Ȣ Y:q|$\ro^s/May5p3学CȞREn? ɏu rD'{&<0]fӄ}њ.FhUS_yp hVS)sߦ1ÏXj5UVu ÏV)|bC;aul=Gт Rc`?,_:DzD3Jd$IRw?aOCgaL+ʼnVLf%?Q3o\!x><>xŤk{-P7 ~v2%Lϼq\'#5bۣxK&>A5 ]Ȥ+1S!eZ\uRɉ.ًN+XV{ *pG+ [7Kd**!rNo[ +r韽$dԱsOU2*'EJ%وl")Q:&VQ`h6c(UOKJhvzʆ$N:@j4PPc9o|yIWEBԥD1-ں_3q3<󌜾yT!$F3LHAmyTjNˈ7.GӨ;TDPÎ4P߃dfiݪ5[39| ϶ FeFr<YIdT/s1dTTm IDAT?ubiƘ111\|Of\?2~eZʐ<5c.[ϵC`=UjjH$Ewy@2~%$aKtL6l2;_X,HMߺh"%Z0h|)9-g R" %Ds+6ۗR7Zoѓ qI |3׬KǠG>'Cw JTr}̎[{h5|sG߃ߙߙ@zuXF>S?D(iēsժ*tT? PGPQS,Տz~(ipg 8Ro=X73ıyj~J|$2T4jۚZ7,Ilzo,-.f;rG7Q :۰ c.K.kv:w}@*iA-\oYGzLNǷa^)W|}ϓ8"35_iG62mp * _$j%XLd1J5 BE1@TRJM'=&5O[)<v݂v¬T{(G ss,ғ j옷Ymn)w>7ͬCDd$ԾFT"!;ي+*UNUt9\rܕ/njOE7U>4RBE)e ־HwL9"{C0^6aه@_ }ޡ`Aa'׹W9, h .B]ϝCTxpy$IZjNAOL^ k 0pVG`wg&+̀2)h#Wخ9 ~B)|>a-\͎lY1:\v3my {т_&fw`,n F" -:;^oU +*[ƒh vM~cՆL, .hv  t}UԦ cVLk3M kd풚JPF MP *] IFt և9+*@ e,LYuueX>Lع+wҬB⡜yϭL~hUkb9Z4ff!ϕGqz;;cк"j/u{X^. do tݪ NW9PFNJfXWs6z5Acpu:|*˿g0?![!r|NOy)Jg$E) cH1i`Ϸp^d#;l7~˽xϴ9p2.Cg .VU u& hF-t,sL .G倢aAɾg]ǒ?fvJ+Yr0 jFi2 HU߮QޖDY NeUNO(˘38/ZFLq'4ΞJu T/$gXN8Y]1`=6jK#YH\5rdc9( ȸGB2<*iBS\ɱ'IQu;'ügI7ȮюBҒ1Vz¦v̈́!Ӑ4^x`&ipWuSȶyavZY4~5MT @6\@lMS deʰa,k9Yn^? 8{3+ l3'R#ڵs'lؐHݻKH2AIsQzmm/,GMLm!R&nRx3.j0s._ul4hգ`0z.„6mUT8|D4ʱy̫cPͽ4 sA.T &*XƒLYWmf4ϡ-Ǣ@Z-bpnPuqQPGQSLUTxX O"]zlʸ>[ n utBAPF\\>Ep]*7~GT{6c8wMƎG""keWߤnp~AʤfX33n<Y>ߝAR|q'F{O>ӳ%ZQ^I^A&MؼioBΝA-f5n\" Jl26;_X,"b7?3t/ \uÒOq=3򗽃ܔMh!ogY½1wZoC&baOyLl =؜xK('-dȑ}ri,$YebBߏ n碝ŋh`RgSs7_G5R9%Gx9%w]Ai@T݈ʼnFõk(c"^N,:ǹΟJ_ ::f͚UHNNY.WXn|eQI#nf[ss€.+R9祸Aw3}b1кiEߨ^9ػಳoo @v܁F땍Vˮ D(CY@ԮSڵkPn]jrݣB q Gjr95&~=njqybp%!ਛm/k^}'xayp0m6x,.\V{}}|ެsv61ԖaKFff&NF4i֜ȒXv*ARRf_'>>J]^h(,HxDT[,N;ʥKDWR J3sҭ#''NGDd*T?RoBNvW/'xZ__*o>T)'G^#2+W/ BVf/[V{]oYVso;pfKT\ X,RSӈ+bY#8(RX+z"*DRb%@X,r+ދ;_X^|^C7R1%dB'oAyd[Ja(6e2w݋7R 筫*I].ųk ?JBAHH0Z_-Q.}J~)F/ DrRiW)L&|o1wH->>]hS^mΣsvj[W۵%,sF\ (_>v#IR97z< (ۨT*N,q`~) Iv+D%{l@p G na򄽭p p\ue8&>#>#>S>|P#mrtujU% 9.$'r;_(|a1J^69T*7@ -|G= b @ @ (@ ԣ,cmU%ϫ.{),am p_,qqod$9l.ܹ0)k)/$7$..aɲNʷ_<pc杣ܿM$}sOLy6B9Jsȧop@ n=$DtrsJy>4/Undŷv!H5/9ϗ! m׮MrǼdl2WekrIyyvv_䤾dy/Js}cyjAg>W7s+V8sbuP:f|ssty.byGcx^SrO$)\H w12j5z13SN (A1Xbepܹ+_ }VF \"@  |cCIRj@A @ ( A @ ; e^{"突+;Ч/JK"m0u@ 2G4Z5W\ޔN@ȦYN0ptu 7YGz@ <#W'vpHړBD`MM$f7GKZ@ tἏ3Th6zL98}zLHhRch%2dh|r9339BӴ NנR8cR@  WM$}Qjn|xG$U;:ck92a9m8.iHm^|>4YWtI|9 @ ^dm+c jՍ7z.L =g` ᷷SpZ",8'Zh!F >:?ss>8{JCR&hFsc"($K @ A~5eo rBqTP⋚4Lz =A#H>T5bt{7CB Z i*(oHD K\zbKT\&zjn0)dzeq A1 ط.ǚ}rtnύM9AMɝ? cÀhF #$H*!tlLz2 & XS@Ft$ǁ`ծNB2[u4AwVRTPKؽk'ޏČ,ٛ "1sj:1pr`ݓ⍦Zș_Er2QbA'|dׯf͖\0"&wsV'%5y<Y-cxf|3T/%S]J(9U[x授xnWwEM(gݖemۥ?qYR o N$T:^}s?(@C@Pԩj:hm싏`r#_`:0I $TJiw\FRL9x2+WsK+ҫфe<39@U^3w^㉉sA*Hyox>0wۻN'gc3#>x;bC 0ƑG1[Iltǯt. /- ߅,e+HMNFպWѢ 5h֝2ew4v9vO dm[wfO.BBe_+}ux: @DvZmi yw$s՟zLgAcf:DVzmLWeEq* LÇڳpj2Z3uP߿#ez$u( O|(*{_L YEv}1U}:Ol z^b2w_B p[" eK/Rˑ,ҁP[v};J[΍,c!o‰˙h>ȷeH]S]7%]@ \tFdQg"[n(}Ӝ|GP0j. )}V@`%T WaD #2,!|CC*^v/cpi>.5_Wp-rPı*#a:qUŻ6URh}o5%"V"]>PgޚдQ}b#DE2ݡm5z+9Q<N|?i[攒m4aقYP(%,6i?#Qfbo3qe->{& µ]G=+FT eUfOω0AsS[Al94P;mVK6`81e`X,Mܕ{ܹ%&&DӗeU< S3]o]fzwoVX,h84EeΈ%զZnhmZF,*sv0g,7cj,`X0H 6ԛmZ[ IDATBq_x_J5WcŚ\;IX{Αy壊~ j{ (96w>{u.9K" 2O+dJǎڰ|mD`*knƜ#oL]$ =6a+_2^MKvS٘_bSV^hDֱ8m6ևa@=ub$>꘮bŖe]?ōnI PUIU /cƹ !@ (dO߼k•[HMšd|"T=pԗ_Sր 7d䕄yfc}t W@`Gt&17[ ɜݽ%3'2:#|t2i1y|Z//A$gcW_U(i\00g%/@/aFُHҡٻ 3Y[cض?$wd`k2od%mP @-Z 7Ma,iCVfr9EL! 5ڼ|JUn=$g`Ѽt?͎_Ŕ*U2|O=.]bϮtr%_pSm5ٿGx~Ex۽mi[Mku0b=60|I.Lபd,_sgir!s,4`@9=|?Im Wd@?$fTLՋ$m= OɋM=%'[ 69zmrt ىdAVĴ KvJ(-֤C]Y?ÕV݈rx!9]]a$ɨcv4am1g&PEެ T+ 2<ʗoImI[3&>',-kҪqĭbޯ r4[V>O2:˓Ϊ %^U4'u+SA[_O~a9ߒg^ibBM6bd?]=8D*94\IqmSvXm/S]ԇߘ~/X~2WRQre:u1$#11tOҿ`.^;uB`"fVyMWPm,SߜK/}pỲ.T\K3&2sPGV%OUv8,etې$oe:p. F:<_gfz0JqP4[8;P;ve5=؂%$H@lMP$[Vŧy猝nhPч ;i6<ƴ %dsT/Rxa@eY~ihM Y0g% 6L5!<" 0rg`y@Ɂfi$>$QehSQ,ғeU@sf65x=ψhZr~s?֟M6< oK2sedh5-:nitmz{WyGl9&cGmgB%?t[D/Iuvڧ 󵇲MJ~~:)p_:zUՐ;ۆR:&Cw X6q )FԤeqLU$F x:UZʏbfT!ԸEJ%m"{ŏFďxo0չߋQ Aь5/Un~/!jOX8qgC~]#;$|4Fd>1tjf>h"3_j`{*DQm''fV/WK_j)*-;eF{\Gzsou FXY@ʆi5*&.Fx7X0d[t@uX2"0$$Q]*`dĻ)+qj7ȆnQ蕨R'1_n L~zo8K-m?LYE M]}bӑdk[AxWS!w_^JZZa /aI@Px~`]F;]@O9as-vJɩލAϷE{!Ӗm6@LٮA9a,)q?06ŷQFǻs9yw zXuzYcӥ|< aphȏ}Þ? 8E}R N\@ bgaPR3Y8c 2?~^}Ƶ+a&|ۓ`3mE岽 "ut93&ǧ[Դփg_ɟ.&] `Q'A} _k4ؑMBUNIIcd K ʲ$IeU I_-'$$@ 2{~ 0^ukHevTuIe'kH[}o)?gB#}iKN⾇s#d V!*vxfX2Ncp:'oR-u q8=!bҷ~>͢ZtnٹU4i8P6;fV[e@`;*vt-ջ!Ay9--/#+']^և-63y璳@ 17Qx%w|>wHHoЗ{"K'x{~|pGi7;~{ چc YSAU}O孶^3L5ϸh|oM@FM3 t -gų?#83We 0h6Z7Q3;ُ}nsvը!_N'ࡃiCPx&TԔd$'{0kuðax@ ^Kƃur􍮞T'G'ʣP>.:CH";.ږcͿH B"';jռ`'2/\@(-d@ Z{}U@ (@Q8T*$[c\Px@ Mb-RY>A>@b [Wփ$ddNi'Iאn2d$$[>8GYA.αHg -"O{8ˑ!,˹J8ɜw!W9]?kwd FS;A|fD_V~KZNi[$dK`[Ƕq )վ$r)צK_ö0kزo!/&$Ir)=ϗƥ.*]A>ȶ/ )sYk~;6/{ny.q#sAً+U,>aKSCh rE?5څڗWjIQfrv tyY?6X4:45{9p}$ΦU/۲=՝ڵ>IYC K|@ F `&>BV!.gKԨݥ"[@W[~LV+*cFUN6%m׷tBAƕߖw1|xc qb WRb魯}S\2o/VP#|ywqR?Ino(*"gEDXDY~z{/) )}vwj3;;3+N66<IXUU=bwWJGn;m(n 5!!Wz)~lO<꣡`Tπ54_8B6L<\ڹ\jtAIh>յ4:scuk'nШJ7 _$CeKɎ>f u$4Op^1 ƕ,E^Iwƶ}V8ICpE|>ӶUT^Q͒9(D ZU`~Pװ#"3(  L-[K 8dMߒCS7cv5zg9g߰=bz29Y fԐL]o4;ᴶ0) Fm_NEO m.r^|@&l S&vʦ$^p2M7;>x^\kwS/K'Y(=/毣 FYoۢr)9{so=fYSKB][g[)7t?\uuzy!/5kz̍-3~׭]o޷^z0S|b4q B[-r,9 Ǫq/ׁWdƺTRoY@ 4y ax8V$%XC?|L/[_++"saykm<.!< L%=Jw=c8 ~>o;Z[SKB:ˁ-̿jO}=&\Gi==4y3D>nlzqx~76}g<6l;HINQ#딦=4=g{O$7 b~澈Ϧڗz W8~|xOk§VY_}gܺ?9??ceԻyIyN9Y\l}2O~op0 )٬l:^7NSKB:+}ξ5,~Ë/^85[\քǡe˖>-CKIu6vl@۷ӻwe~?ɸ}><iW1A7"mu('RiRYPXY>>CMxo&0Ap@nb GsYrz^J~<C@ .%!Z[tx"[Hu5)p矄>QF|Te˖J-RK Mo>6mD=|ɼt)6lnwеkT)T=14r@0P:`!ܯG0pjp> >NP3!!%@eW2} ;{b#叴-I`s>%'L ?<LϏ701cu~|F/C5Mu. t.ɿZT@Lw}'4]r+M/M=ߍMߙ"֯tƎͿ3v ,_?~)1[Z|RP:X`EEE1{Z*> 8w(#"hh>ILk#NGτW{~ {syU;O2 cMO4&?1y}hq0x>%!Z[Ced-+ꇧsz窆J-[4Uj MZƦoH͛`ۙ0qy{:r,[-[p1Wd-Er7z=iwL*l{{|xTh9u \৲Ô>@6]zCq !T[nD^(]z$X aJ+hrǻrR*%smL. L@h_f00M_ ~#.? 5\\6g I 5ȶקЍM/5ƦwkNno@~?N1G5 }i8* !8T hv ' Gp>Țp(R JcOsˈsJƍJGѯ-[qh }ZƦoHZWq!.j~}^/ԩCBB)aQ^ T>:~bV~"`&?0e*"m#T\C G*+/(3y\.xǟX32d(?3aMb37g]IJa~c>3byz12r˿ bcx­sWwid\\6WWȝDdK730y6Te˖J-RK I&޺0 Yp.h4Lw숺zܹx3*uӴvhJ6xb|qD\FV}Kp*e*8ƔæE)Ӵz,f>3CNt?0d.G;p:?6-ѢDGeiԲeKSK T%ml5 4(*Ĭ0FtӴVD7CQGcQ`g\+UT? "Kԇ84ɾTl{} RPnlp~κkY~=ee(1CE>h88UqW7uǒ8etvS̳ ?fќ{VdH'45!BAM9Rq,mäC˖561 5kVV`/5 r7WFUa!#xqCE"ӽW|>@O= x,zٽC,Bvd1:_!/a v#8gbol]?g5fL!ѐ__xqBh-[-$phrNiMPPM$$ӳgOq&IT=fٷ{77oaNE!Nq[)[¹#l{Wpal>xNF[^6XrġN{ET:;$wov7eDjUGi."ӘjEӐCtq.7s!#N4&uBunn17]yґorT^/; 6ABbO:=40 ,VGUP>:|E%x z9V]p'b4lv;I X  IDAT4[DEdf8hV6P)hhl8`%lsܖOp|Q駌<Oco}MCQֹ<СM7k9׋p8(T7MR]c[0p5.'2cD |OmkQT MPB5 _@cSUUAQQ5;#yw~ӆ! naJVZ"Z:scuk'"R[e}2j5L?t =-e&;G')2LLCGu 3 {3fzjkgpYgq3}9x #1G(u(@{oF Iu$nB!y5YePSxh{F/|(;`~Hĸ ^-B!DgdV>T3fu{"ϣc35GCg B!آu /8(FG!BvqC^WOjZJ LàwC!Bѐ> Ucjrr^H+X!.茚CPst-\߅QԸuQ֩CBB)au!B5b}F)7B!B4TDcU' 6RҥDfHAIm-|r* k!8džЃ## jvG *V)aG!B!IF+J qc @cl)s61=:Laab:c :߸E+qӒ<6ϊ2iȷ'` ]3{[˅1ZZK]3w3@ѳ8uș>#ŬKGfԐL]!B!:T9T\:~j>0) F6IBt\r}BtF5oi- a4dO N>~Hĸ 4"\߅Qup2kFUjxB!#A!BN}B8Gw4fa !BѾ4uH!B^P@ӴB! JM8߱d}/N]ȥH9!.茢npZKճo0|W:8 ܳ7~b`;WB5 _@cSUUAQQ5;#yw~#qB!mN5~z.[ʜMvLN1Se&y󺎡h7dѯ.Jsܴd4O6 ﳢǩL:/ V!BbRC{oL]-\CY:LE^~FqLQ pb֥%{3jH Ӻq4~m뮹`jo\>gkBcx_ Z{7D3wʴf(Mj055Nm‡3%?Indef*,TU%x^=Oڻ$BiEySOw-;#iN. Y?I߾[@YZh J>}9KB!DˈUEC6ơ}Ҷ[n}^^Ok` p8tڻ#\߅iF(UԼHF FqdD23ۿwEt`~.]g#m|-Eȉֱ+茢=zv=vAՇc>W=m,VlB!YUUɬJ'Јk'aTaX,, bXZ7n}$HLL]jB !:8mjKVivI/aXl8l6VUUZAi((u\Qc{ѮUBUf*ՊfLeU?hM4M9 BtfӴ @7 @oZT70##ahVݷ~GPQ`^y"BA5$nxCElmeeЖ &f|$$%00'Ԕ;p&$?gvrה&>/7m]^(Ebb"/s2{l>l,XQB4Mc͚5vaQ8`ǼK9 v6MkS:y [pa;_vѽJ&e|Syuf<* Eq2r~[nYײ/oqߑq8OWFѯ%9qދb,tm@:-.c$8I!:^7D5PPRb; l\RnNIN VByRWIoXv!{>/ YgeǷ|u/ _> _?so,[3f1\6)bŖYʇܿ b8_XdK>󟟍Xۀ )͛[IZB_óCٯwڹ[#K4+/!Bw„z}㝻w1`vkJ=n7 N \RF9RW1Yt g`KӚ,<Ea=܍5=G(kg)t}(;-t9(̯u0G&\?.fUh+7nK5 q0)ff֨q 7k:qئ2J^~(lp$3xtdj)/C6Ƨo04(;W6s8wY%z0w~}fost XeabˤXMA01 v:t4Yjx?ʀsЬVg-X8KcXl) gkXp|/}/wɃ3L's}3 %~$MY::K !DGZDL\nt}*;ԩ5dQqp:*~6LSɵ%2ehvG`@UUfΜYI< CjFP1C2snB|]0| [9"S4A?LG cPC8y\w2΁X3~3J/Xyӯ䧷_Cʨ3kɌH9[p$c,=FpxKM`* nBtÍHB̭7{ǃ=(AjBVR>-c%6LÌ~~iӢ.4/]p7hu~~\aC0e:[.`DjiVy#^]è*bFI>*VWfUݎj.ߗ-bc>n;}|5;="Җ$R*:` )'Tgx3#!vj4ư^'2r1iTF]8Ғ % QWPO_|ʪ쇂-)o/߹u4V!v!Dg޶nCM%6c~{<~.'$fukH[d1Wޕ̜v~]ѣ6v /̩`]~U=?/g)đbW&WD~N8Rqحcbt\\bKiq'x|*"m53QF]KFp^ 5q}7㒧cr;o#ϟw%?jRqT̴&{o&@¤{Ϯ2s`CDw !:x׾< !kKΏfZ% 3Ʈ}24 LC=840[/k[/4F~RUy{;  /Dp曻ͽE Y{*~=lc8ʚġQ~6{Ten~{o! ne8܄@w,Y%2l/Ƀ1lx+*A,;aGoR/JHﻏVS_S?jr,'n~ .B!ZG qӤ#ם6mFu_0l~> ~3zL{IGW%o~v{j(j/4``.ɝL{~5\(\s5\}դWlo߾aEꪊ4Q.VL[u Z~â1uQJʠ&~Ӯ㎉-nvci}{8 {;JSIC'nkyhbLY,TUEQ*o/..2kciyUWV0-qҜr, 4/24bwS;_%34XQ f 7rٜeu(}ʋ(y]6v_6"_I> DžUR{ r\ބgg~4%E񔗢(bnJK߿7n2uI!j "63ݳ5F}Ʋ\^IbBPTԔl²ŋ8?-ؿ?W.tE+BDm+2S1k,6<kܦJ~An֌0 <7^rAϞ=ٵ{.[j򓓓ڥ ={=hM&4!DCcԸo џ.Y]p{shN$6qB![AY}8a7[G*#봑B.茢v3}V6i η+ )B!DSp[KRm:I`XXXO'*[[7BO?,/jb۱lv^/>׋ǒ7DְJ-/68ˌBtF5?QՖ6Ԩ?|W=|$8]8HR *k]\s2U|> f^?5]^0C3e'!30L1CWGn9cBb6hl6/;v$qYy-W'w„]5ۛ./VĿ{IJJ"## zA.]ҥ Nłi$%'QTXP\\Laa!wkB Qx Ču_3ma뵃 ɣ(^7ɕYða8ٳ'}!33t,ʚ$-Yj7##i !BMΪz81ozihmC<͕[CPH7jHMhILL$-- Abb"6 M0 ԴT^{ut}qk~@V{!D3r^c+*7Ss%څAaXp8b0 ~?=z'N"{6k~\Mv-iB*"沘iqhU8F֭iVL@O޽`Mut ^֒{2Wyxrا'\/yn2BtF~ULUjơ4 ?89|@eC펓I3G䓇Xq׫ IDATWw{xvnp̱&8i:w2]z:끌=zqo2T/]q'|*N_gs WpSx8|e #/#gɏ;ٵGg؅wqäo@o<[^k^ggcI&]/x^|+~gfpj˸$](ߵ\B!-*P27͑h_pԨaG֝u't@]̛|d6~C:g Rj~}/cKra$ڷ7}`Wy1|_Nq]hV4F>L/oUûk G_COw!S/|~kP-m_+~ޞ{#a?Y^? ϒGxyxf5{!?ʩes &{J Xs]B.u4vTґCcj #/̝6n`oٴqCCfX3@ a躎a$k% ]ѰgTz]7a<(6*l;|_Ѓá:c׻bKK"Q)OKWF0:vzqPPI՝{p I,ߋKIØxD_I*7 l~gٹߍǟPP콙r|pByEyQ81'8 9!:\\7>۳gk$''BJr )$'`K&%%<^%J\JprugatE[TI41c;Z uUUZc=%l*ǝ3&UYxcP^YyhI:t(/'ZzftKTH{.Չ5@Ь*?뉖AŮ%% '>z*I~=D/5OdrӰ5C !BTW;5}{\AC1eI7A&JZug}3#PY0z6>S~a-.8ߚ kcG*?J§Ve8xr4DuM;zI^3|,UJ EՎta>#iP}0a}Kfc*Eљ;#!d7LVVmyf'ێ  ?&d%,(4(@t7G[Y.N p~ !i[(J!^*7^B(muy6ŌyT T1?#rih,}Ǽ|v1 l6[^/>3r9wV0y$zɈÏ5]ϧCkÏ7xNG u>' f/?tpOtN'r\v<&`9;{+3%k$'O=ƃmOOB?>4 IPrc MǮO>csb̏ޢF Gϴ. t!W/G=Ƶnϥr-t9/=D巈l9O71횿r׿凒,͖BFQUM0~_<ݺʘIaa} ECU (Nmb>w+^|3rly!//9rϊa 6;n"`53qzҜ n. C?MY&\TϫzL^*]O饑Tҏf_ReeyKƼ?^k◩cPs孤Uzw*2'|]r;82%̊[B!#EQZSTP·FQRԪX-tBYyyb49Xjtm/Ÿ͙hj{xsN/<"vÁbAQ èr̖-[`Ӽ;/BъXHLp(U;Z,ߏ΄I1Rڋ`E|q_#ϦKpٶm$%%LRR XVTUEQ Hg_p/w3BtF'EQlXiTiZAQULv۷2#8rCbqpc0u`wʆ?~ !B5fTsU*E?%󑗗G^^d/놊ȧQ7f%  E4hV#W5*vĉɩD4Uf#%%% rr|\IIIQA!B6'o>4 V~c 3/!B!:_M-M0@ MZ@9m0B!h&NĈlm{uSs%Qh49BJ3KpCM+FmT@3Uϳ)BA]5otqKnKz9^*ajA&dK2(=L10$!Be61 fCk|^>_g|s%ڈpXXuP330<&^ĝhP-~"Hc*&KIh!VQUwbO;l6l6vƧU|F%͖h<">ۙK-ܪb*&(UapB!D*_WCcDEcƞ⬪ujDw&*?% m'*\pO:ۏoSzy6色`9%쩒_Ŕ+UY"N?/\hiL[J<5Xy&c560J>.zMgvCtdwM!ںM9Q7͘h? B>oA[VF;ط]=u;|@sǍ igT> n ?0ĽO12v0]I1wX;,wLN2Z,WG!B-?DobلS|2/6>c}w7M=|}9Nϥ硇'O2]zL`qDi?Cw/.?:EcW% dom%?qd0Jq#<' ч1Kbۋ_N$Kqӎ xX/gCX$NAVwPe/]z 'Z!ypO>Ƽom3)9N ~cϾ|]xͮF _=;Ɂ%Iwdr}BtFfƧ ~PlI!2W0B16y m,dՂt;u}Sm|x]IO\l^}AZZW7=&O7xoCۈ9܃T~dYѣ9=v;ZMi\NS*-f҃o2\>yYVX;~`MCN9ԢyyMkOahX֮J咗nZ /mɭ^ڜy bou9zV쎨1lw?lA!Ob"`ge#3qV|jIDD|>fL? i}"V`3"]=6%6o-tgq}p` cRn6?s3 \m]fX8b^ݿի 5f8Kx9<zy.`97y{Xe)[?Fs q`)1VDž-% xُdPRzT_ JO|h:2O=T$jW7NR*RH6obn"iIz\o#| 0˷ob>(z  `艽x?ߒwb.hP\W[b_̀w>e:|[,廝嘪YLwa)3G8~S,$&YgKy_~9. j⚑\B ShN?ji|c:z,.`X^A.~}N>Gob%WL P)8pS 3)"Vj%`rlj3bZ6RMח`3n˯7̃Mg`g W_^(7GOY5uk3*TY's 1cg-Ԝ(w?W# 'U3)z@řZGYWbz;>2gf{tTA# b9bb?) vѬ(M R"-gc6d& 7aw;٤}r?O,.%BW^sI5ȡq"-ђMIQ\;mWz<p; ӫ2;`+t;h:>3|O$C;geqݙ8d;?by9ʩ\J1<͆ՍӮ=7rکgq]Ki?.ebN*LA#wa!6poD{">gNMrquk ֧nYL):'&nS Z8z ^Гчs+;pJ^s^/o7kn+8o'0dk|l~|3Ҩ69BQSE֭<{]IXi6|x ڋab #_W`#\dC jeH\W)wz@ DsL֚fqM߫^諉:}kͽK#ÊyKcM&|%U5$B*6tc.I, B D VPYhj4Kh s0svFaĦXb㏢Yg6II?{;ꡥx_̗ǻocWO n\}{l 5-~5A|_>ygāF~ԬTWi#?+ְ=۱zN @ ]VA\nXFPZ='GgpA,= #9.Ct<; xIèj)dӘrH.׿ŘZ_z?ό~qeuDWbfERz{OÈ#5TEwo Pv =C;V#?^xWz]Ia##v'=Fs,fՓf$3X`U71e<<XSH^g^P \`yDyӉpVWN3;d1oh< !DM9Tm/mb%ZN\*+xk~´hRѬ"=VXkaؔvYo m/h/8i7r =ms]AkFzs luOp 0G3זYU﫮wrNtTZ(]?0u`vZ|&k?"\RDؕϦձ_bf\kCm4zW8KG!OgUUҚh6򯨁c9+ T4(QK eGl*޳O$u6#R^Ceǯx:` +>wElIFm\JgՖ8]E#Iu즟€N\DDw IDATI&NϪ"R O*񕙰ICPnBU4 ɸk>0Ey~II~O;[UGRwoH!)+81-;K9c5ZpBɳŰJ^.&lni4Π~3]UkQ9Gمl|'b?N@$] S0P"*(`ߑ6qۦ]~ŭ9::~SUli0Q(݅eSm*͡Ԩ)4\ 幔 ePW)5]gaå 9U,m|y'd՟jLQrBNt+3#6YTUB!D}ZDU%!+d^HE4ppBbHbA\InH3MEXƘO|ޤND,dUf͟~Ȇh[I7_Z=UK5΄!U2.>76/~#@o%ކ~ݝ`);C@h7K>J} Cpeh>͡HkKKbw^)O1܁h@lh2JC aPy~ZuC۹q%(JVe6ۡafc!,qqv+ͦ[/ \@nU$=;;ǞP.g:;|}l{kSVP.w(XXcߪ#I:dnyr{3 NZ>Z_')V޼rs1~~>zpu` O0ӫ)Ҕ ]q jLC*붧qhRS]FB YEVOKW.[wamۀzM꾅RMN1ZZ~Br,%rX1z_5Cv(&Z B!FZb{ ^l*UjRf ϲ%bsgCLSQPOrrJsgE![y/%p15PUIƒJ}lF5BEHXe8!Dc%'0rXuYBV$BkwF!-@ӚTU5$^NRVQUWWB!bZDCc˴D :(%u5*l!~ꎵbJ b_JBT\'B4Te_TUBƓXc%ZIyN3G0l~r0 @IQWc-IU%!h 돤qh)TsYn#11Bza]ױۇiy(JѴ0`bi@1Oν߀klް"- b%Zϒ: #Dr:,nE׮餥#s=0 IJ$_ξ_!^?-"pA4(9xNl۶S_`vX~x/ͪ *yq9B~B`hQ$n5tn4sgxd"`$4Zzͦx锕-CMA`C_0;Ƨaoz!BW>k-4[8+{&QG"Mas)x-O];? o{o^neP\R̼)/"k|)-ӏ气z !B(ͺk%8T忺 + (y ` #aDPi t #HPֿ/,c𔞬]ߚ֕ך8Uڏ3Yߏӳ{[M'Fi1޿5>RC]}ם8?+p?iԌnR>!$CD Q 4 t6o>Ν!ƍ{7kҶm[: {́\`p;ylG,c2w,|vsY|=g&sƑN@Y (6cOތ8(\1xu*<ͯv3L 3'P`M\HkY$1 Mňkd/q;(mR+;gVP#/.>JYk`D4ͼtl;aL;ы Oi&-Zċ/ n|gh%P_f>\CqOƔ1N}2PVz6HYO%mh o?yf3y)f;:el6aR8t@>-jFun[ΚbqO. ﹭ wO×m < m_Pg& HVkf*ٍeN kW8gs3 gsŌΚS®W╟  Ai?a#߽_癍+ wBFA85C˦g6ϧd[5.2jԨ. {14zӍd-ߺqTtdLڏXYhm&^c84YGi2GI("yk0'V񄶰MKpw8r> M u:HC 8PULJ7,b{ G Hņ?&3jm} 8gٜ1lΘv1,+D6 FG(lBbD[a$:jNBB(}=l[X=qBQX,h Ӵv~":#y@ (L;1ϰmBR yl. Ϩ$$SC8۸l\&`(7>wRO">E|GI%0b|NY=Ix+(*"M O/oFߺ4Sl#ׯu7QlnV+5:dOts6U 5 :?F#uU?#u* bFWb & tg.MH&eVۃǢI;!;u嵻*\]%YC8*ICCaۙa"eSF))ɣ̨~)wbzy>AAy~9ĥXAm%bV[ G0mZ/P56S\ ʈh{. ,m ?A *R+ B"m~ v{QKAiS v,Z}ŋXcRS~:ڝιn 4jiR݇|¨v5uC_WmD}wP~~1_"V~m)=9طEKW /ˋV%|1ӬCIKMK!B^R'N|aG߅iZi}iZhL3dI!1YUbѷJӘvz[/SRn'i l'5%v}̬wS)Њ[LV=ڽ-u]gCKtz/Ȧ؏| !Ƚ55E+(N8:/Y ''<۠B)^+h!BwZ8BTM=7+=+P8k?]qJB!D4p(*.aq{u RjweЮ-<4ь48+X YkUnND6$B4RnʠaINn|OLTA~>+.Kxfˇ*e =:n@tն]3uu93cL*V nk#|C\UkIgwpݧø#׸G'673?曗3[;6rwpﱙh2e_s}Kd BY'z` :|z&.q`[zlB`IXTڦ2N=hUy9֫maIi-}3B)j=IΆB!D1Fq{:ݺ kD*A~XfύcU|>u}ۈ|+.{Xmfp1V{mWbXb?fdˑLY,{R* @)p@`<{2KܓY=IGX|+,͈X_/pC^0Ynex蟣I*ן7n3ILm!S01?0I_Kd 8 NF֯@D)ʾov9"\5g%9co4Naa>6c \u<2+N \~x>{;?t ;k&d4O+\!WŘoMvv㭧]Y~:'t on 5^kg~grSwD*/^珏=!Z{i a0VF~@wt*fռu1 qH'Repyt7))X[jZyoY7<1~b \ g{ף*G|gk6;o;n@^xKyqw ?O{>7~|UFhr8fsٛBi:ZxKGs-q71czg3:^-Mg 'd`u>I!Of޸W^ū~` U#,}z 'M=.4oW__߲`S*Վ}\Qm: >:U*˘+SbNdE&dzkN2@h/FXΰ!pi'`G~T4pw=юof(70?vJ#ǎe5IRZ|WƎL'{v6!Ꙁ޶]֊a#:tԊu*]!Bt8()*+'-Ey̼Krzd<nlHp L&WܲǓ*SUO^<ϫ1GJb[j( ;ТtW"n=Vф"XXI. w/oPwc^wxpVӱiv!]uxKҪ뱾c35B͊:igA?3[73%J)дvFJ9,֝'_.bxa4>}Wvk(u5rt]*F-՘tS`?=?^u* `DW &>ˋ JVo?PsWz<9)DB-ޛ,g G&YLUI*piN&e!n4(ˣ\I^t>frKcQS0~X"E2uUOXbEE޿B5iU‚3=/z~2X}e?/[?2*ld)/ f)~eϖ 6Ⓖw܋UCMX#EW< +h؂4TVK{(a.912OQX_ 2뀱K~(0P*E %bWnZ~ŸXM>f}ErѦTF HG~Lތ͂E; ټFVH0X2=/W+ *k(WUf=juN*_@0J7Wz0z뺼c]3_nDҥY$܋d o8vSb2KXx %FŶ:!JfmA!+d%nMד^=Y;O1W_Y1\P*GXe}9Y}P[|xU??0֜}XUDez\Z+ՔjP1tEAXER3rX6w CO!^k3<:o=^rg/߾T>ɻ 8x^.]Gr>Lre- 矰3s2SZH1]yO4e#NǷsoRILȠ!p`GSaԓ/qeҎcfqů'˲y;eq~\uD8Hі%<:cJp4v4:N~bgO3I3)lDu &(#{>3nYkAl6;|z }\-OeN煻N."`+mK &5>wswc>us~NʟG'aXr8lv4 t]G4lu\l[yUm˿ח7a˅ANv6yyM1 6O<o ^P܉}M>cB!DC!RNI؎ש&2g#x(Bqؓ{g׊]{veoghIjz+2hp=tՇ8h¬f>")% w\ywaD˜Y^A)N=i BC5C$_fQ:S2KڭS$mDN]d*'zS9\6Ja*ilMDR6MtyHkݖ1"P& KBJNBO9h)F1 nBΆ;`N8hgR!b?m{JgXU*Dv~16OFk۱3E uFf64M,m0V ]W(SGMlIigD) ÈPTZJ/q嗡W/HK"b22jcA(<8rY !BkklKWE/WC/#6ڴkkVWkӾlvv#:6r{]t ]tM5 Ӆ'!r]M'H۟YYXS0h,TtZQn Xwz$!";=0d0cZN[͚ ygs{MJZqvmYn M0"`Uǡi4Y5 q^Rk08M0"{! sVin7xP\lU%$h0jYB!h;?IRj*;Z:9-[jR jiцGMCu\];Eʱ&ic(*&z V[.|7=v>bZkFjfk|/>. 9B.~{JH]\}\x饜wUY;#K`EY',B?W"CM ?W%E/ݮ*Ӵ8kikuUD4rKpc%c.TTUKH@:u{.]/Wڡ|e&vO5l [y ǒV%O<0;X#<;A'Q `BQ8JK*_=qV#hCh4PX@EhU;VTex^aLn|>+x0 Qr2l;Z%z eˠ XɽXbm;3D#9,{Y^Y#)Wt۲MsyylēҺ'wԛ GMማ&Xr-fQ/hho|s>n {]ܼ|,^w=ŎIp4l̻n>6N:OH![tۋVh#'O[=e.R טdGlܙ 4N=;M&yl%'К]ԮTjK|—I߁)&NC8!HF8|eۭL00"Ha1 81բ )t1M b"ZD!nݬy]Zo$ukU)E]T1_OڄMʑXۗ*0u'g]i2Sxck Ǐ=3.b/YD3d6;8yvE@ӱSou&^r=q;oDYƋ|MGG>wB!@V^F"5ZAaAĈ`D"D"֣aXEPZ5D)~?a5秤@RvcYY&kd]9jP 65VC پj3ZIAs9t<պQ9|p[\ ;$:Ôv>&Aw rbjjN1gy/8NVYsyl_g_ɍ翖]=G3MVGw(;F$ U(Σ<ُVvB}o&Bw*E6XU;㊋CVPJU{+ӡ][| 5PRP0{  ]53ڀ i2󉧢 6*DX՜*Q(D D\ ))0p`ˢUՖJJض uc0})[yj, ~ː^%aUCK@1~窸rq'(So\W9w_T[Žp.:H/K ctC6``R_?Bx ~+FHؽ;p;+ PԈ̝ǖ`BvZRnʠaINN'(g%t[9/Teeأ]gڔ nVjReɄiT>7M34z6hn V!/hiU-BAX1Codr#oy:V]< /SpyJ=-p)P4`P_gL!夻xVA*Z\LLwEÛ?'śX>B=Htһ1o׳Ym2&9%qkd{T䖅Q,P !bPqa :|z&.~ߌoPLV}Kݫ旔MF(P6nZUVPYɰD *0)@JJo$:tPbXefZAuR8Zw=]Frpynm"~,۾Mؒ+/θqo1j72̴՘4_/t|ci"}`ocl"U2gC.o,bWYu HUo3?/=:f 5 3紉Cp<5;b\h- );11΁=C:Xh%YҚlV,\_B!'VP ]nz $|tރdrU-Ɇf8 -U0+ӈ>F|eHUCnAW \~$F'&Zc6mkjmVS4Hu5rX>o# COfZ?N`=\|&g{97^KT MΎw$z~._!uƏZA_~6Z"_K~S6u&#N˷~[FtĹ2Bp%/C/'tCN^K+|jKi')344{<gH w*Wsw"`j>>qvbvq)9fLoJ[>66cτB? fckaYЍjˉ:}7DIyt6gy|x<e'&/+0 sHI@n]1uMdQb~+ 5DsYY)KRn54T}+(+ ֊#o}#ܙ>ĬQʨ->ʧ )_9x=<_~4\/-\c#i]j%y4u⻎rNCqr۸6o >Y#oã/Ss96Q ;B!Z$oo+vf?Q>M:2/ wv}{>Zς>~ݕRKIWC_M 10hJF)RT-?UQ#2p~P;rok7~H t*P$k65CdT>q* V4ÔWVa%ӱCߑ3(eVX VN`p8?=B!#VTMVH)VֲV;?9wI&[6IWZZڲ^ ( ?DQpdp# @B6mm&{~̒df Iﻯ靹sΝd2ܳt,ՠCݝKx}8\n @&f"N<!dBCaw`wB!;XъCyy9]%fϝϕw-*Gl_M={ K,6]TWͯS)b(qWٶM7H7Xvw2ft!B!{[< mtutJLr Ph:!ltr:<SnD< {B!#Pt8ۃ]ѻqS7`n,rqp;8qE8!꥽˲9m*3gLB!B p*WaPUQNey>A:{ 0MQV1RiBo^!Bd8,R@)eұΊb/'!![li (P{w3L-^U[ڠ/&A0 M=0,Y89)6p6(k=!B 4L933]V X$s؃N_Ct!AU:3C2֋ΙAKkM׊=(:!BGiY8\n_@$G8;*p8̝֭48SӽubjApK<v4^B!&h?`˲v+]y L_]8Y{KϥoGl#~ܹ>M37-O(kŇo[W˯<|;1}Eh"[sЀ,<>qh%~k_!F"֚h<Fk{G(IJLYq.{m,$ +!q}鋽KeWRg7m̄Pc[۹ NjݝlO'7U\5RW]'~z$BLCϪ4-\o}ufPfۚ>o~1m]PCr߂CP{C<4HYa>m٩U6V;׍0{ӿn[ǐo>ӕy4|RXNN) O)b6Nwu,<٥pq)qKύP~b|apjQWKs o,nկ1m* 6 qVrz!B 79dWlݿ_'o^fd nV\#H?J!Y9x4١©0"˶4T\lM-π걺4p;LYծ#9ޕs7D6Nz9tYcl{ 6?;* ^n$~'WvHǛͿ][6- IDATfk| qN;Ga,C EY>|0=S'Ը}X![4|<_`޼qwٙ gm۶ۥΉ$"oyHk#2D|b|mtxեsxyF|es,N:Xsʋx/I>l&fkpO/,QY{4 J{s͗QotD4J9)c/sS5}/O9 e T)sޟ?K?7?[:9{r-lݼ`ϰϓH!ɋ>ʣ'O?}&5՜rIqyp|򢏎JWt$y1M(ɖt!2D|b|mh|yY~_Q>9;8=oj^_v3w^-,LO< ,8k~Ng 9 )p?\I5L%w}XV ~\2/k_Γr/ $ WFtFn*!Cq(Udxtup/:w҉xq<yioo+.gJMMѸv%:v EC17.A-蜩N`{f$kH oJͪkKmX=G~TN5 =B!U*5a*9Й7wo{O<mp\yg9Cgp:xW%#y1k;dWiVR'wɖJ^' 2>@t}|Ş6_DZ#BŦ!+>I'C_֬YEϔɓq?F#IW/$[9.?tԱ/۶dao14C%BRcrwA+Vq¢ uwlj}J> #YߕF%a!98Zݦ5m:'B!8,mq0 @ yraTUeN{h ο 5SM:}1>I4 1"iu6Bq:ZZxj2B@i)5O}3vq9g#FW,ƢzB!>]TWUF -^?n#X/Ƈ%KWrљJex%Vu6Bq =5zѣ\)*P`+Ke_X`/JI7E!"d9|Xa c[>8X:zBn<."vS, 0Jc/uBiXO҅ӏ9`ps#&ʊr~!BRӱokmYdp8 /lc1Ͼ:٘:B!ĸ.&mq0Mϴ3x<hJ-@"g{CH9B!B$GfZtֺf1.B!BOT{o(GI8(ҜDUNQfta mmn3ټL\'mfΞŚW^b8XJJJ2]RٗRYͲ,քB!^]?PB!B~K@ |t:I8qY r4[Pn^ģqIض(  _RR… tp8TAka~|>eF^zX !B!@ y[D15ZӁr`f4 ̉ OAHa*oZC4˴ R Ӊt:1 ˲2%%%$ vܙ 3B!R…e.8Ӂ]saxhCkr;5хnNt֨ 2 9O  ǃaضR GYYJ)֭[Rɓ'rFB!!8dy+02/۠X<TN'|NVN#y׋ᠥ%gF$T8@0#v(}!B! Aĉ҅좤͔'lY|'%nfcQYD9q }nR\.$ ,²,LӤVZ[[9Y`eee8/B!ľ,}}]A\gxG3Nn@ WLZq>ߨ GZ~RII 矏ǶmB/"---Dic !B! Pl=N1t:(yfgWք#} mjMlY ˅vxXzuf{V֙43S$}!B!qpE\Í8h4ea*Q k9^(lm6l 3o<:;;8yGH$D""mKB!#4dšPb>Y cF"h[mj6ml˂:Ô/lzHHq.\Ȗ-[غu+n-cB!u(Oڠ9݋@?9nzߊ (:k`&Uh MW.,\[o@]]]f%˲H$$/B!>-UrVdעGm'[l}BfbLrb5pZ%pDtuM$444z`66mʄbDTW/B!H8瓷[Xq#nL4p7@wcLscc-gTL찉K8fWtE_cR466fb1z{{ UI!BHUvip5V4iGN5ǃCqp*8i#f0vp֚H$Bww7x^NgRϐH$PJJOOOrf%$B!Ĉ*ήn<^h$NVllec:QL F¿#秽p +}۶33$Iii)>/NC,Lj6eljD"9B!d8mq0 WVE' /VoA0 rdJUAN>I?R =>?<3 eڵ(0 Ñ3Ӓz h۶Z B!_rc à^כr'dMo !B!!c]B!W2X!B18Zc[DbT2r0 難sB!طY!4Mz#Q $yQ#[ebQK<0}ϴ3x fm7N!B!&۽KU 8X6~z{yiEα~ _O83κwPKX<;wl/?_x-'-]sÿ^U0N!B!&Iii)ec6,/ő6UU(xa8rrVu'/#s|$gk6y>륻_0tvurE#y{Kgk׮IOo47eg=秽='}!B!v`Yܾ ]t֚x,ƌI$⃎N2 ?۷QYYǓ7*0q8,YdPa^/HI0}je|Ɣi,YI" 秼W_J> #["B!^sgYXM,K>e1u ΘIw"̩F0(+7֮nTL9sͺ5SY˂cjmM--<|QI?PZJuu uxײ!B!xL[KnQxR񸩞4K׋P@YEuUը_Z= }!B!D:/0Jz#Bu$RiJQB!BLT>%CS8 oXt3_!B1δw @8hx*ؖH왜3N B!bB]}=g}6=(;pܔ +d&Ni3q{S7)JO-@rp[6/x~r:-" ܏XR@]B!B콚oEkMlhp-oYp y[DQ6m,7\8u[7#gs`(G!B1XFQh,JCc۠15:'B!Tߏ&yTpⲵt}Vf|*hmAQʇ=vB![ 0jGn?p{ qzޮ۶hmajXB!AMucmmmf: ᠻM<:JFv8 8l0dh!B11ٱs'^s5k&g7oa̙||b4`la~U**+x}1WzjJ~ CfmB!|DwE`0ȯ˲W=?oW%0(ճ~:I$j:;;Xԓ8ɓ B!&2Sy=|˹n M(Vx!dW >L4RjjjR?q[VB!ľ-̩u|O~߸oTV!c5"PvQ]5hsc^?n#X!B!&VN8/.ԧ8#ij>N(^׋.HL)wpB!۸qgP3ml*>] r:VµB!ɶw6(`ۖ>'UqoM.B!B#Zk] n@șBH* B!B|r*} Rj4 N5)qҩJ$ d'C XK?\zʼn1::5&7Ϟq?d*'L^<:ZkS=J/֚vX&L%{Ί#y8;(~dC9?$ vsO=\3?k\/CE9AH)T3:yȄKKNI=#;D (qT' *yW#PZ%?YjAg"T8đ40d-J HTU}Α꿲+?u_EC&]f-V!BcR/0+PΜEs_JB|IԗDz/,][//ĬcJMX֥jOU JΛJ}鬴 |<_^gRtz/Sȼ=+e* t~ s3#sR 2W_L?j}*ynbh3Rq ȺAuNOt4YO_LschrntdHש U!e?թT鿇97iB Yˈl3[!b/ u!7/e}<'pB!^dߐ-B!8!kA=lB8NtmK>fepB!{>C.e[c#gLdԳ5qMFit#]fkml[S` e2ʄ]$@SS3͞EMM5>]DؾuvBl˦aVVYmc!08C5}coYDDĶm rrp8cq!]WAIUQS5c!"[kmcov91-k$ĸt8X 6f״<ضM_8L4cUt18ݝlڰKߏa}ѐPvY|~i}B}m6 y8#P@.M(䑿<̦ٺm;3ḡ.eYtc%FCmm-SjYf ]Tӭ5:lq:yщΏ/++=xw5⛹{=Z? (aÃGsz1o,($W_8LSs+3Ore۶ƶm.g<[0B2~/,  }0M-̘(}NL7jt `|X WCUeebݫ8xKI>7w/_ᅰ_~Gc(WU@%NZXhe[|qU#ʊ,'QOOm;vvxݑfڲsr) 0yJ.]0\0M [1m*e{G IDATv&H$BO(ȑ=Xp@ |GX?iضuS4`bLӢVD۶E'Kı1VpL_d܌wpL\)8N7!+Ɋ0 :r_oTXvݬ5E7km?_}ĽϏ 3,ut—mW7/΋ϗNad>ZrX\C5sZ›']]KǗ[̍}V~>;4-w+W.J B1hjzN>T6X4:(R˶m+9l]XAhر,cgz6MovzswkYfrwćDضm:C<hj"OP_W[|ZZhnn*-Ų,fII0*Eu7fC"Em]ݻfrptp͡xLZgՠJ`pj $p+?JoG/ۻ0/OL(9wr>ut;|kq-e+h͢" %d3ʖg^'q8i ;$ΜV 16y{tۊB]_.N\ ֐H$s8T(Y:Ҷ=sla(yUY`XJg$[RStf`^o81(P\o:w}z X'͵,Z%3mTTPWW@ϋ t8(bSvn, EGW׻PqH-\:oxQh |>εw[IC0 :Tc[}tF JJ]3Ay@ヘ5N RsNYm8Ph쬼%&}]1%8yVnJ "]aLt& G]4eYf͘3E˕*ߩ̺cS{~Dˡ #/X8{̦PzYu>zɤN+gB0ǣvz}~I,Xp m-].R(nx,FW%A4I}r{z=hZr<ʪItuvZZ*UWp Qq6z5q/Sj\DYsS0T-:ڋ~j4kkηn5ϼk!Gn_!"5n+d(0Z8!f(++c5πnwa6a9b"SJaoC4'Ӱu;DE;ʊr6oQނ==L9 ӠPhmR441}a}R475Jo1Zl[S?u*ij>i% *uC}/G28:SxOΉh!#6ѷ$#$8'pGj[˟ʾ̯(\L5?W6?sԙyn%]q9ͬE1q;l~*9:RqBOkl|38|R6sfL*twu YJ)|~?}.;Sqgkx<- |ଳ 68iҲL9ۍiYlk1s L,VJriniccY#j5Mxp_M;'Ǚ" Zn[[SNSVquW~om~޻t0ڥ _ৄ.g٥5RJ{pCu$?qs a5|l5tB1ܾI59rKx<^tIMYi]=]5)fFw[Y] dኙQVdP  3iTVUAӉFK!Q;u:[6oq{,Xs:L>YegXggX(ͭ-L0 TU,;:xkz*LZK)xĝ~j*&WF Q%8 S !>yvMQǝӝ5Գ 13ϕR4m߶T^/xdGc#6l´,>Ӧ1iRt̪T%/%ҜyUiLjc!cֱΆb7y^&j( ưl xIiHWirVpŻcQ]$C!BeuT2 j8CԂyoB!>P BN=m1)ЋeWEB!DoODbkAjU!B}]$l_^r^ y'Sˀ}{B!H$š(-/qiiRЙ =يQ&,J>B!r6Z;QAV!?;28D!B}U˩+}>UIENDB`muse-4.0.0/src/doc/html/split/documentation/main_window_annotated.png000066400000000000000000002405141404105311000260410ustar00rootroot00000000000000PNG  IHDRN;8GsBIT|d pHYs+ IDATxwǿW"U M^{==5$Ƙhh1&&5bEPw[mSδ{̜=9sf93EQB md%+YJVd%+FT!^>eŊկcJzrqPJVԢA TÊFEv"hxm(J3sT m.lĖvLLl?DҌ̥YVMBU]#536>z08@O}$Y9ࢴ"Rg2l٦k{8JRubEzT+e~u5pg8hJK ճeߔn81WԕJ)O])UҠcҡby:qL ]mc' !16)rV'LeF-1rB똓.aF IfIyr,\8mX^@KRVYio F=S2qdj"y|٘ώ;9WC / :hE"^WĔVNڻxNd%-*5Nϝ0eӨ!O: ;{wlӫ.9vM|'.r 2q7 D tt݉=dBשR0w*Dޑ~k NJ Mfv`KںA.4eđKC8d*,i2J[ [*A wrփy қn9+#PA б;"J466Ѐ@Ao'x<0Oٟ%%ptI#E='NNF}[F$& :[q2d$Gy˿XN>Y*BOm!v汦v -[3b+zWbK#U-$6| SLY1)OvK6k,Et&tT7q3Աaqg:IpunE+=N"-x`ܓyom/e)0hʓx7:qo6?&Lղ\C5蘗6E4v\Q2uo3a fM$\^(1̟\v=&8f >D\{koqi4"|YLAD|XH4P(hFPSSe@n˓9I^2igy_tnNOBGPMvUWnv%|$Raԑj$WVC$DG$tuב%C=)b"xՒ0 /&han^0 F_r>s$صxfv47ZOHuTz6: T\i^Umpת\r%ra(>-%N'jz@ д׳E3-q&1R&ma^*ط~;կBAw}77|#S|˪0`w/:EQXn]{ !!Cfu̚5~;0g ۷<h:wAsc`vinl{F-rEdu 9i}lDϘI+ifTI&VtP"-I&r0fX¨gG)Mer:&]7+.]9c֍~C_ORyxnvJ,/sR\WH$ABoNX&DIL$SE)2/=kcVN09>%՜:{WC+LZ5 f:z&utZ-[HxIC3Yξy&4yzcC6nr[_~.},-> /~5kon .; ;E$7ܜ 4ߌ DT0d 0d ֮]ǚ5kYxd>"&7D( Be-}`7Xg,"m}Pa^#x6QK0 &z]xIܱ"+Za:Uə[T˵:fX&Umf5KHbTB'fMI1AO_E icp3]]#.N$Ru,C^}!]#N.}B p[/FV=z-ӎC}8jXᏟ@D,D,&Y,GvY_א Sx~iTt_~$L0R}:m;]Ur}֓ZK~8I=1׳8LÓx?}IugsC{q]7zfֵSVϢ\SGzֺaM߶ {[˘Z*-;*/E({*gaϞ=O?,'M^o5j :8v1C t ΈTG(/W]9SM]SΕ%?BArXdwUs|i#|FXDlL8%gԛuܱ"]2k&NOd#'m'ժ1$cx@N'L3`))q^QF*$%gz骥dTHP$uP e)fcھZ1=c8cƜ>_*ga_|ɥtڅm[6 6 ⪫ZēO|+t,/兗^bm¶$N]z ! /08^v|F=ni1,t)>_KQ\\gurSڣOBGV6a}/LoLc%%{+-j8+䖩Ι!`j#+$1$pTQ G4!&<"eB/z]c} Jk$6  ~,o1t*]=Q"zѸq^/_Y(p>5tpӵX![hŤJ>DK@XCfR?_BDIil@ YJ\DǴLw@5癡ZqK+L$z&ӵH)zZ=[]튩[1Lo<ΰ)bϚ_ÏyZWWeVŖ5`ͬ^أGkNS}-֮j.^L8"uzMx;w0x ֬^戮:v4E/!^Ntޝ[ҵs'LKذizSEEv "Bbe0w-/ګt-}%2!+=c1=R/(=x2 HcꩫV+U)X*֕A=H% .R TSmE ,ԥB{`dChlL!/f[j !x廼DBA4D(1"e\9=p,ffXUcVlV"I]axLlZ*Yy8f;5F؁Eo':AE ,SUdI46"DTajd#KL*g|;}79{[1'r{v`0aD[9%`dnTaU|hi,a}ɬl KvL=tl)!H{fZ 0jkJR֡M72x0]vv zLe6Ŀs,k K#=cNĻU"޲ߴ=1?55Dh si3ձ_G4'$so 7'6!-!x/   D|T! /@Z9I]/e6NHUDIj*ALPG$|,XIZD,K]&y,靬*dIS[J[<GyW?O"&%e]صm(+߾gDu32lL<Q_G|BENtܙbr kڥ+[nY$UPlOkcK#gXn0\ /I \$D6(7z9KsڈBpK dC UTHK -%$ShX? U%Lun IN4\əinjıQ%T ȒVi߄)}][V`jfGi˜P)`厚Ә#'qJ\̴9,|I>'L>ѧcD"멬]v˧GйsgJKJ{#OGcBRWu<22 ej=VzrkB܍LK, .7SMf:r3?Ci%CsRe?0dZ/QlKE4}BjeI!)m ؉;N`ٿ/7ws%FȱC2JKsH7=O<9v(87!Pnhw21Ÿ;V7m 8â@J%֣Tv\Wr$?Wڌ C0}0VrN9}.$ v!|$I#F[ 71I$o<:EB<'z!lt77<2W8&ϹgmsC ;F8o7)(x=^ )/HWgU>ގ%Ftmp {iG^Zӭ7)aS'zj]%1Dc*jٗ9sSbHS.zQO-т7i!2U[ I$g; H$QkϷZC"ɨy:I[A Nu-ނ[?h kTCDѨZ[T &y{Hi˜c'4_k]IŴ*[~ L}93>y[8^vdĚq !8P)$W8I (:3+L0RɆ|TDJ"TI}:F1}Fdf;/S^j'/4r~ I/[4YVrNRla#LdSla:QN8& L*Ҕ%Lm(mclϑg%+%J5O :!lB':F]^KxDB$kI.7y\O&hM3$wɔ,$̤)6*>E g㫎3'.6y}9&E*鑣3N$wf(8%JOJZaܴA"LZ,aJof%+QS @G (uHTR ӷ$R)4OIElܒ*)Rt[kqlɖI1"Yahnw1SpCLt-IqR.Q˺M6%KNu5Q2wSqd%+2ISVHR"*F!PF,+,(T|IGtmI1HdHiucdJ|[B15QЀ2 )L12OסɔmQnqy[,tC,bmLR%K#uBwF&Y”/>h?ѳMLe}Y`XDim钨 iFt3K Q)Ll4vZFdR-=(}9 h&$KP#$JA8"AiI]%G=.OH~눒3تS9!KLL&y)+|I|H";┕$7T@A+—LoʢգPnAoQх;Ql)ݓx ΈB`gF2e;*a(1w*^WKj-|JlVbi"P[Z{:o8.XVkl5{3-I28Ӎ:%JZCpx;RYz'Lʼu;LY9X%NY9e+|i|*F$mK{%$ʨ/WJI V)i[)mDIrI`bnd #TI4HD J,CoLbRv@ (%G$g6KS$JI7Dɍ~{ KCpOTY”CT]gؓ0R">I#Q4!ED&Y0~\ЀhԤDzIEjt0eΜP] !%U #E,:.kvi$Z"W,39X$yR6ޤ%Nc`Xsarӱ:oYY hW -[beõM v"7K3gxA0ep6-qko IDATXndaS|d-4&U\28h6(u~ɬS~guI4N.$ZZΞk |?]Nf)!!hGC8=d"4{*O 4E3f_q4MrIDC4hZ~ȇ]RJ[E2id~.v[7I ?X}~0Z>hi BAjnwx~Y8nK}$~蒋@ހZ"!ld^CwrQATuA!_$[ca#h[BC{b y\~kl\L˘נapyjYk43Ŵs,U?b8|]K|%v;UHN crI!m&X6Ng&1؉̹-X4X}VII>'VP<_E2jʦ-mLBHs'XCWpn<^?[&$JAc,:KhN|hybٓ(㡛RT;į-N%H6HYqHM gNME*?|W]9; Mqbh;&SKD1R͢gWsET9N^7:%NN&Ч ~&Z7R§WqiGTp `м/~=dԍNb2*d:͟ abr1P4L9ӏЩZ<caܝ<}u-;yo݊Bu;Xx#)2 v4ʯ^ON+ m'7qsx^feLg:9x/e9_*kdԢƍ?j=aO B'=>L]kv yQy 99ٚTT:]^1Q;_Z7)c]UETRwk8n1'<َ6RYѹ2ڨ롯V*Ty{i!,-2.8V j"ټVvἳGq|eg?yV5h#"&.uwG7glg8:mjGX&BGfn-v7 &ic!kcX&3K[$u7W@g.1xz~lJi%ENfܸӹEUM2 ZjsU'q3ʿQuĔDE6}hdkP?| Nh6J7~Փa~_}Փf\='|ָLyyLoX4&^3N1gwwwhHF/K/ggȑ{&?{a#\5f5, W1Q sՄ{/#{YX8+}6.8#wvѢU_5E_.Dk>jB7bzL>ϿH?ݵXJ = "ŵ m\?eofg<>%*DֱMa7d휲1ӗò1}BWg%OZtҏ'd@:#N_7>8x?G4ϐ'<{|4 c&[oI;:cDŴ?7ӏs?S.'<«b^(2i'F+^z֣h?Ysy2^Ólq+|׼8?o̙/f;U̞N\Wһfuri3`K Q{'ǻ=;^em3IQˢ?'=>sv,NWbMxaYg&<8s&ʜXٜ)Jp~g->?`l?u3͇,swqR7qZ37<%o5O~{>iz% {Ke2Q%0XW[Yӏx; "߿f𗷗; ]@ TE\+ULt6o^1_Cs"'{_agKy/Ki(˨QܻL: |x0-76_崪7a1u}o>Ț4,妱>L1Uϒ?F^ϖ+/($kg6ࡏY۸Q8T2=nsәkL'ڄczZbag1: 7~M#47 ún.p؋)K)~DcFSCL؂UGfMK~^7^IK'^)ͯx%^h5+M)U4Dkrq/C/ױJjir8g872MCǍt{lc#!mP~ϞaQBd*_3 ͠MD36xMN1-Ap퓋p?{8SN C&s|>IwU0>L\z5l M+M_8fRM(iCAN꯳ B@=d>&Td_Tx*]_T7R @)篦)a)kI^/^%IH͗~ 54K,^Us1L%hW)Ы9 rHɅx=|;?4~g.!V̺z^-F=mq=ETye:4(Wރr_׋M7a\pXϥchc۷{)L*V5Ϝ䗓\EcMΗ_By\KYQtrWn873UJdGf])+,90SH##gJA]o)] S/L~JNK͜3"Ҽ_C"bG8gbBZ%་yRza6]˪<9v1èȩ`|>yƎSdZۯcZ|:-;A`N>r›yw,~y%;S|uh]9> i۵2(K%n]ټpOw^7RŒg_d>~ݴ[%Ħ8{[K)97KNx3s>CEO=9&'[7R'ϼF:8n g"ꎜBÙ>Öm X LCli𒃟9f~]83G[keOTh5=$n{g~o4U1ŬT 2c GL4=O= {l?cg10Oҝ5\/?Ȱ$0SNhh]pֺm,,ϙHq[1΀iZ`[Vr(I&E(g-=&΢7'MO$6ah"Vƭ9S>u -|$T73$4˟gk|7g1iƍQ:AqJ#oq+nSNepPS@~SntP1)(4ॏ{3gH|=%V*F?.~^_ νOxˏLt s&qx!@ߗ)Gns~Fh1ƕL3_ΏnGju]-耈n~KgNcќ8Z5~~e((;1Ǐ'sޝ'|6~Obuo'|օQ?!Sg¾g0$>Oe:esm3׿dtz.d)cM|9>n<<üδhp8wd.&g) V kC} 0Llm`~}qXHfgqzosekrMY9f~N|%jKbvrޣKEc2H:qJRn;N~?gþa9NG !,g̀SͩJBs*H3QD)+Y1)&L,_`A+v1 wlfr(fqu& hBKH8%SZsq?6a4D Be-7N0-[&+Ֆ9ѢS.DYNb0:4-]jGqoNfly?ItB4}2)=qKAI"$LYM9ZzUI,QJVĦ0Tdŕg[aLJDw~OK߿;܌ʹi\AN~i{yH-$G4AJ?$)CxYʷIVD:R#NS(+Yq)BHndVMlʧw0d3Bx:8 `_>d'SS[߬`Đh˺Svξ!!BiSҪ[ȅ# 8Qn R%JYJ%FSVz)( t5wtI$^%enӶVx?$ڐՈJZkM!3k$)+Y9$qZJVrȐ) #BޗCR#pM4&1,WQ\79QQΤQG m@2 ;2K:$)+YfݧYEQ[rb吕/LeWId3J"g<2O0Nhv~}H&6\cۈddtFt'Hmdŝ$)ՈSꄬ׋)H^^p`s@ĪVVV%G_Ҳ2ƌMNZt["(BetAR% )NaF\$CdֈVW[GYYYڈV4mg84 GZn)tTWYB}]욍ĮkksTee3&kv[n/Ĭ+}y.P_uuym=H7@rܻ] v8m.V ңg3⯶ݻv3~x**:fV25?67Cne9e-2>GRWW[g /u!&_ IDATҚ577}{vӥ[wBAZqUVU`zA5_[]0a<RC ɤڃH'xc8Мd1P_gY9xRPXĶ-o榃`":UTгgO2⫱;wR]Sxfr #UL@s⯕j.U];H4Bii9M ˊ./(`?z I扐kTH.X@NٓmlldΝTW0~ SiĬ52܉>٨:!>,n6b;+(,9T tea9n@$uTO!4q5Sp4BYYyr`j0N:nx=B ^Oq9xb}=w,PJK(ݐ3?-ϹV1gʙUzq8I (;R 3wB )i AmZLsZ*:rxڳw/An4ŻAx?/2GU]SKEGrV,ۋw"mvR_W[o;\{b*1^F'~GK(fCClʪ،Ν:ѩShd0k+U:_ Wˍk[PU]Ց}JHI5[RUAENJ/(Uaa!e lٺjAXKIFٺmUTTPѱΝ*ۧOھKGDAD!?!DP5ByA@x(BUvOjA~~.d5v2: ߿׏׌ׂ{@?A8#ȥsc@M |P[ ΝkgLwǀ#u.6 ~wT5(jW2@3P Aƭ||><> BL:y摟Oqq1TWWŃOX[*)}+>(쮃jj$"e (-1A6ǃǣxP%|Q9UnU2/}JӷgwmπlGW7 #Xn?=y8+( oUlۼ \p8?Gַ?C<*ËqaR՛M7jiqɓ4m^ar:"O^o!N֭ҵK7Z-44ӵKvLnn.#W@;^z9T_uRVVJyy9}dݺ :|-N1UVqTy/ƛ˛o)3g꒾4G/Rͨk8իidFnm_Kjjj^}6?i򦎯N?T>( Q& WZVy\DiY &xW]<#-2x0''LQwB,Y&<EE)k֮%}q}ǭ|yQvl%!֬ae`q8j}nQǐ$F/8~Ա|},`|u _9c9rVD˾X昸߰6|JFȀNl~_(QhEx4HSWJDQD8 ңG3>0;ɏ''(Q4(-@W D".u[:O=C@N 1yy1]2$&0'Me/%FJ=@by,_o~\ 󑛛!OHWL’%Ky/:7u=t)]R HlWhrHM=S^O8iɋGQxnsTY+QٲS"yԇy~ ӞFҐO4oN>㉑'PZb8 -: !B+6M >P3G:%zҔ<9q#>p ;w>☘x(,,O>TUWW+9ꨣӺQ!9dyrڹ3׬%Bؼy-k"%@&0#N&F^I>L9yRѣYzuRǍ쭏 %d_=xs}*/;bM$^$qJT9yzQ&Ĭ]M@9r|OB=sP磸$W7\ s )y4 zwpĩ2 rӷ\ "U8!Db륖G b$iߴB ަ2 Nq۳g(d'_yyyɧ_;tm۷ѧO_Qd~@$%9ruII1?&xhk5O/M8u+LcO J,gUQ'L{̘6vd2)&JAx℻v? pn|Q^vEa=7iL4֯[W\A׮]-?w0owqcGҽ{7ӛEr߀+Yw=xNuuuwExΝ;EQ(]takZݻ3|PckoWHW74խ+l|F/Wd5.|[n zbdx}+Vqkur*_ZkӧSN믳zZtG'i=|rxRW5ܺ<^CnlA(hAT*(8hav l,1޲vv X?+_Frr ?>믡WV{c:}#|X֫ XtFD`r,䢗`(G"Fرcr^"?Fx<MްtR;.71ڤΉr3 \&vZk[ʵqzR#OKAY~UYWP=h [jv9 FK)m$CEĦ}FRDk;ڢAH$J?Ċ{Yy/_ܲ㫭VI?D8%F[u8YJ,@{{nkbǿޱ]vBTtȮ;i0Ze tO doe%;wb_>kٹs;w@0H tQ7JqpĠd_8p m0ڴDcyGQn 7wZ轫  (JAQ#*vzR;Hi"(tB I o֙&&@̓3o9yF#R/C⃨(:Dݻ{??"d*׉t ӧLhtFՔZZlT{i[PՌzJKYx1 `1;}fg{dggg3o}1xwHc 7VxƊGiJnwGhuW4O=oԸ=rUhF$QZZQ>hުu%eqˬ_:/7c0ʕ !.LE(,TzF%(BBf4WQ_^|)MǽIM߾=ΆZy}AiHj ĨQQQF/^J/@R_{*P\v (D(hrFx/>qf>۔k(Le>M3E7_پ%j"XE@vͫ'J[BF)lQqVH|e .@|(=6S8IR Ue~Zp:`)-%;7OkdZ99J0 I &slHrN_7Iyaر5?7-Z]4U3 M A1T7T>7),,d߾A)pGmx~UUOn+o~ 6lgŊ*{3ϰuV0aW\;p,ш^gXxݺu;>[>d_tmذ+Vj+J2SY_I֢ExwyyG1c?_Y+]z#Gw^jut#G0 2#KP~ip|ܭ~ Ⱥ[0>r$(+*JeQv[Rt]9(g@TUd~yNUsϟOQRJKW&hTKZ11T\ M(.y-[eA^!Ka IY̫pMȮz@l(JяNhAtگ%hD n)7ɭظNmƍ/pE~'BBxs+\9Zj({ݕG1c}xYJk.>9 %| T,@2urK1>BP ؜PlU󚼕$ōNpH[q+_nl!Xl$"DSC`H=|ݫU/=Wsr2XE'y8a']>s:h9$b):d|HZ-={b2%ijj*7oz~;4aqqڴQj}2-"'O*ak"v+ODsPWu+/J4TsKYQZ┏<6^:* \WVK>#TA6mP 0xje( Ox@])V/WT*<>&baa_J湾GDuJRƏ?ȋ/ɡ# IDAT'9~8_.Qs݃<޽{9uSL:(+<(Kv_ѣG9pM4AK<ȩS$''ӰaC骩,={pI hڴ)IIIC#WWDTdYG Z5&ZjiҤ\VE(%X%8<ʒc1D,|ٟk_`$IZ&6VŊŅp]pK.ᥗ>Aݾ IEBcQNUrKLTlQQe);Μ, ~1v%Ho{@&U*]$(ʓכI`rIYZ(V ¦R?~'H e!j5xН́91F~gj@-@<{_ : E V7*IM4;\j6f*n= <֦ȡLmݎD+|mV+?_G{Mތz2F)gR]3;Z)`uj9贚d I]uDQ8Pz{rooKebch߮lN{ =4kv{!I!=7Lh4>lVq1q >sTb@V7Dkpw]5>Ma17uCL:'0W94w.FbQ$ CIa/"QzbHLAa qFLʫbbъ͜ξ#W_}CK.gÆ h4z͜9s%L'>B(A@*Ox*ڃẼ5NmZ3;ϸux5iaTwjsoE)*o'p8HVQF\qmۖYf_hDvy?9s&{˗}6l6CÕEq]}/YRQ,F^{5n6͛GBBӧO$˫o%Kӡ뉎QFL&O[ l_@h2ÇszWǎ/'=Sɞ2 (aD)ҕE&MNԈgJk[V-q&U>I,wLz׿QHR[D'%%h8NvBEb"\磤o`Xh7u*+|t"`ҫWoի7{˻t 񓙨hX?R;b9)::^BޝA1gΞ9FSG1o^7+[) zm%1!.]VѵkWt)ڵc`J/ ~d5 7ʣad/Μ9CrrgSO=E=x)..F$rrr(((Wl\^$I=}Ə_n^P%gԨQ /0eʔZM||<* ݎN| ,ZM\\111<# 0s~Md5_}4a<`;J nbRNVQQ *.Z߉F U$IqcԣP֠g7oه*vl%n^JA(ӧqEQ\~w$LvD  ȠNE̐ E+(v#-3?^nz<< m-JxX%6 /ˬ\ڋ[y0蠠44Uʪ7G2=RZl&::.jU`ˈjh I2@E :J逪QLbXlּ97mW^kI8tiiiX-u:g b% Zr;JQkhִ)͚4EpE4o,{7k֜̌ zj.h^CP__­ހ=r@-Yٳy5k ɼyx衇<CCNvF;Yk_yCBX%ٵҿLb]`kpޗNFh޼2硨!?jʉI J'cTcEUt2dC)ר&AEdYp`w g.;g= (:h4ȲZ7ڶxZAP]4h5:iެyTNG-0MuztZZAr:SpכRVemum}vSn]&?%bX F|{ϡÇYnÇeС[C72j83s菦Mcٺ+Ҏrx:%9۵gȠAt,el6>3aÇӻwo,X@zZodd21f6mĝwIlϸ=djIj5L&tZ-AaK=t{k$%1xXd p8eҥUkҥFQ0$'&zA7IoGt4@nng+;^~;Fp#q)λ̑e4jLIEsm<#Ls2Yg { ϳ?_ ?^oɼ W?*p:X"O{ر7x#ڶh4T+}|ͷ\ӽ;}aܿGNiSy-%]wŜ9s/7v & ]ddԩ|ǖ_h2Y:e z^[;Fޝ#GSٱciiiL6dOk ٳIMMΝѽ{bo `sW_uGsNҘ2eOYsĉtԉ*$Yb)_P`P8jVꋹ.3eVdA'!Uܧa֧HfZAndEh4Z@f; nW79dp(w唓u( %%e SBBh\tDY"@C\dA7؀l73R4PǤFq<E۴%^!˽.w`r.3nS(dY>PjԪJe/5!#{ $$aX8z(g_^'59QT`F%G@i\@rdhp8trN':g+.*b)aS\TDiIi\?܃uU ؃AEz}Q~(dQu?t['JSmOb|<oգ<̤݈ԱwN;w/MBvv6C@F}Kvv6]\ƜA!sWQ\ROt JJJ(*.ɓ 8vGg<9s9Z03'7 FqN{J~F)a~ҿ' p m+W'66dT*O@qwaQ6"'[Z_РAT*gΜѽ[eI*CD*#NԈY(T5k1 #UO,ƉI#"e&?!DE-fZCiJFQ$$i-tOQh*$Q,[rVIl*.DYܴ̅׍U/*ӣ2$-*T".!qi MҀF(Ur(ty\: '/Bz^I6JmpUdlf+K`ʁ$?/$D:mAGCr@hS/**<ȭߧ:Ι3ghס(bw8HKKTI8{,yyHjRJå>D!ޱ(Ox}7p Cj le=_ҳBLL4%%DGxرШaC,bbbd4b0h4FsIx1T=K֕mݺ%NʀAdG Y?In*տwݺuqPv 3d*wjbЯuC]췖?/FfЀ\}W:ޠ~jՕPXA}v޽S餧+C7Wq]ܽY֚ >yأ{7ڶmÞ=TuUWҥe! 坨i,"8e%lغ3*<CdףdjN9 qTmWEժѬ٫6@VG* m#119|8VCɲ[krt=Ei}n0ï(sP48 J=uO4EYǩX.8Y("8"2Mŵpt:=S U7]?feeҔYZPQ*Y5Gy5DظǏf'IQQQ4_:@A$V/\ELLtZn'''B\|'PɚTR\eP\7l.Phs)*ǏѬEKJKB Cwwh׶mTns:8!8BsCyc >;\wbDVDօ &%G]+)Ush<, *XBVt:0H^v!":Ez3|z>h ˢQ1΁}8Bjqa^,3sCu!Ȫ nU)#E.s$7hY%\HDqq1 jj~D#g.2#".Y@ ` Qj]/xGP * j>p`oD"}lksYYᚪZ;djr53F!B$.>v9u!Ȫ)^͉!B"D!B"D!B"DEq"D!B"DpR~5{l&!B"D!"c[Kg6Yf5/"D!B'Cch"D!B"W"cHϷ*˘ 4!B"D!BDdl~A ̺5iX#kTD8H((aCբVQ"&s1H!BJ Нbcbibcb=Zn ÁZqDEE %%%NOawm .6i"DP="c@n^NQiۦ-VKEUZ-ڶ}8N4MwN'ZZ,+2ZUW^A:uKM\(m \feO(_ܺRlfZ P# Щ36~bEd7itΟ!77;vХԩ[?dgg}NRb~s1 (ynbjKKʤ"KE`;  jɉ5x'*q#_DQ$??Xss/B^JBRIGVSnة111TqZ8@k=ߏS#.6a;m \\Lmbi3STb)llumdJ 愸>EJ9N厕Yp+¹D$, ("EjtSs>hTմnӆs*7ÇС nۖԣG񓅟M6.NmfNeRJEAͅ0Ct5t4fHЛbZXO!vRpQHͅ]T*Xw< ןM*GcPJ }Og[cvf${"?p#g-L3ds]Z)KHxwwJr۵ -A>oUj{%?3}Ch;SO2x^xyC3=EsS>!a8*}7Y70ӵ+]!za`}p&<_Ųsڒ|`QhT6saL*RZd86cse] 'ٍJv})]TzX}xan *Y|2 x93%8KVbY_ YfftoZ0{ngcSl'Y$#\Y6)uwWX~-?.|aE<>OJs_EeIڀ+xcoov8ě|\ {Un]`3zaTĬc…0wl-\ӆt~oxk_>ڄG-O,ۙw>z~]:9nNOߔ1{C=gs݋8fslc!<,b#g?}ct g3{W<9kl:2w?)fd` cvnlΑ?b] us{\dY,şw LҖ}`,rn1C/AԀ˯I m̽t6Lj IDATyϊ^3UOFQUaMe$(-*A>1FðY[ Gr\[F7QhSzlTsq)NJ)~cQ% Ҿ8ٛkPnjSE V짤Rra,6|$۾㙤Ysm6@TE7?q͇5l|61^]I&Nv짰݃|έ_;>>[z ;@N|}'>O]bg ӂm4t#tay 88c~-w( _kX|$o߯n W '/+c۱}k9d2{n|)wO9o/C.iVRd%rs)E^y^>7:d%޿KY<~Ma 0*?R3Y_K{_>3#4K,¥i>/N8fn£nF߄UGFf:L4I4勯 _,ۭjp#s>Fn| SnO1! eX"Qg~5|/ɓIKK+رcL4^{5\E}E,1)i~/j~$D[zK\uΩ޺u ӧO},O?iӦ3u*{>&ͧiJ:OwN;Uq"ۄ7<5;mz~i]_S6Nah BvRswnϲ^^ v Nπ3Yge }+6mɑɖ0^46iF7#0MXᤶL~i4>^<ߐI,ٿ򶪶o,,@d EؒoΎ(h$_}P)ԭZ_(,64ޟZ"`= 3긠NL|| 86m.xYvUةSő?zUSbLpVC  )XR `";y?"֮[XbE 9MVFa1>SQ{J)j֗%OPY)u8&Zrg>sEftTFZrnNco_GbXZ/b?/p\VOh,fu]A9їwCәᎲ,I^\;N޽;G%dao1ܽf1{K\[{gfH_gj2c %Xӵ_~{ڽxq6mDtI^ǎׇ`9ɒdiOBM/9jHL :H߸5k 6_tA_9fq Ot=Va_ſA]ʹe+Ҧ"j_nIy-fLgFt?-c\CB|QAۊ> 1h~@ZHX#]n؛F(^ۏ8n p6M6)w'tYn1h-u&ύw[I@]2wr`Kydf:4iRn-Xf-ofj\lUVlM#?ҡOss@T,}d9w"z;>ӎ]/6l3O~)0>O]Mzq2cß&HϰtTں4VݍFF ۖbrKu>^錨J)>Їf&!ٜOؤ/ [&@䨢 c[qO7zܳoGf1|+K ɪPh"ӯH~Kehu*CJyJ|;_+}i_eu~e\W86w m; Tf&&}GV O/cFF| jSb>`j!O/ ]f֯[M"K"hhSĉ<'ĕrqrYT^C04񙙜|=zj)%.;r-b ʹ*5K}*"7JX2\ӁlǤ p,`1=+^ʹn:F7*/KK2rLɟrKc\ I0{`Y9v'K;3}P@ʩ h,Tѹ\rz..Sx $Zk@_bc;t%2VAe$VGY,\z%y';vżK(-).wG =8?/R~( 7v;M.- DKM-k:^E6!$o@ODZ%:((u"E#bi>$Tu6Me[\h?bKL0%YYI%U b\֠~Nc7fMte3_~]}M i ٸNjZlg؟'y"Nlx[ꑡא>5OyRR~JY,{7.ͯ0DgDJʐ:[w瑎ay9M|sg~=̓_sJ$7]%|1 ƺLFtTʖ3)^)a޽wx@PZQWzcʥy?=ﭤu oBI!!Hs֯W8OguC8~xGأ {dtxu< @mBxv?ޯiL=mTgZm \Q$hDXdȢ Ts\U u1m'@u TNPPx;.]q_A#P2{^ђh .:ؼ?RCzno [,cDU~S` -fek*_͕Iu46*Mr^.mwT"n F:W/#[7vTRD?WtbϷ} q޽: &wW~ʟzQ}9(&^Ũ1m@QJC ]be\lӛø| F\VBzAY%8_JrD OpKNMUbC(C)+@K8/,!KN$]Z,Vw N¡ vf4MF0˯1zA"cKȼ׿0^RH?68_R"T2I\Qya-6+!Gi0F=ݸWx:% 2pwg 2/mx]K?:&3t`ƹA4ɠǃhf:C^0ī|ܧҦň?:l ٭=e(YTĹ~erpkb~]43Go!2dc,ó^E߼w6]”{<6wٛZwǷwfˈ;Srd܆ ԔNd ЙžF2x3FY8 u:Ąxf8OK\gK,u 4i24TQ&utڕs璖mn%//&JZZΥk׮~Ss^ݕDӧ߫ U_RMT\AK~s88F H1-nC6P2 p)ۥT{'q_ЁCm.mI̵=,g2JE${.WGٓ67o3^Ǫ෿SX[r2[2c3z# Qczf? xei%¬p؄}_`=dYE$GCkꪽ#Q3vgQ䧔 N-ڸ&Qe !7RgESU<ѝjlh;٠e,ݮ@?t\n=Ov|ƓJELl|"D xx׌*ݓbț8zQ<dzjm_ӡ};s;} qH:U|\-CbŢ yQXX^/OT<Ɗe?йSxpsNZ.:*c8m \kKxj3,}:=Is||k)2t`ȱ5s oCA7r=9NWM82{ok!bim(~͌Hg7΋`"ZvgJb&<$G&}̮Q8:y9N>DǙ߅X?fuQҲ7;z-ٲ;ABv!l(hKǏ-PJ@l(/-eBY-VBB$d/ ;Oz?$O,˶,y8~{ιoΨiN|I30{+F M1f,6mC~pyQ#wB8& ߏjX "9ج6iv :]u\\. tnIkx=l6Kkq=*@)ROgz9iJO:GBHAA.͓@p`^g$  8&AAA42I   B+D G6xVjc=T=UTPRUiL vz*(`טC (%44ꨨjP#{ߔUŽn0Pyƴ@yJx䫍P=kuWq<~Hv3ՈQ&!|1+!5Êo~4g*x5s+~=+ BJ9 jk_0R1~E%XJކ ) uԠUM/Z #=˂ C\UƷNTix++(3J+>(P#)G DSJX@QuT[Fi86\FxPKHU64fGJWJ X' /&6e54?EA {+C&74P۞hn9Jc1akią mvU(LӴxp%PRF}C* -\M{4d>Ѥ7+?R34XݤB &850pC}-4;*rZ`ё*Ci"hwEL/H -M7ؤ#fx|xz3ŚRz1팦wjMH z5%ޭG&zcMƢ#ލ1=Ai3t }jhb4PLH9+݂n(z± [jf›Jz g#uZLA{rh$[j@d >8)1gP#ݗ   =6Todؤ]?l5AzT{Uz ={$[ A u5 IӋܒzAAAc?bT=Ek   $AAAZ!0Tg`gAh P[տNSAAJuʡvY-C]z)|`\ 9luAAAH2 S$ɇ9|ЛUCMo3`H>  BF) 'n=cj*gU)1a **N7?@U0,Mu)͵zfG%A!AAA:N--geWjq|$H}>b^җg*VOmyC8M~]ĺPO^)TlJ!4_AAwzg'TşZ\S{=eVKCFzr3Ph"k)?gvj\~!XU';>Ó䬻4+QWoe8Ttǜt _4”2Cٮ-||Q˿Vqg`X}'_ .,}y3'{㏏o;?tkx‹[]DK㿿Mn%L;]ɉ7v>]ˏs{#v1g/K߯albC2cuɗLO` KG6ژ}N>FMeԯx :%\{L> ^b,?~[__e~'X0[0oyއx|7rX{xɦnr&QHP}7xec} [~ƴ ]x_pEg^1˵ӗ7<dG| v[az9۱ܽ:$ӡp;7~*ߨwV6 `wq5n-j@zc{pf]3 'ܫǮ\1gnoNc 9g)0$81\XqɷȝC$צsTObqAb3Xea}1RȨel;cr*y N|~8)BZzչ/3~(ulb!x+6Y}m9s5U[ȝ[x} g1>!FеRZKHZ^uf&uOb7k9-6׮ӆX ʷ}E'=K ʎ9̦yZ;7TS6 3i{%^V2y=_T7׽K\Q[Ãt_j}.0!V<Y]O///+e8p}$nK辴t{vʝ;͜J=)?O~ٗO_%xOGWseo;b&tQyx{k.xm-pI%xdJ}bg@QԈ> ~+Ǯ=DՃ.Due V:n Ctٺ0ea((}sXUJp@#WRk-s9.#R.*|r 87Drs<0;vJml6$GUib^ßX9b:ygxwcRc: p+owOdэHC:|x+)dX=Tթ) Z| lJ߆:emFkz[;JsgJju֬_>'܌GD+K 15ڽ-_Nk>#HcU-RGW[`fޏ&w`*޺}ߌbX=sP.{iTb/sx'쨻$/b՚oq0nZLv~̅\ fٿZ}E{i QYſ>ǻKTf|Ss__MndiWsv r*Sq9i}\{V&c<bYa7WR[J[sO9ꪣ컷x `C"m1yf{a+Tw_XG2ܾzJ> /}~EI'kY^Vt{;|DG,[=._=}M9ȏ0(urO_lF=1!E ۧLܪwȪ:Q|*~?(FsIJ{Ї('^1-zX¿X?1I@'ZǾ 9-{MMQ!6L 1] ݟ&vӼZ>z'CN|v3[*^*QOp_w\i˙%F[82+>$MХqۮՐ1,qFy_σ?W,B[Xΐۦͼ?50}nd? GW۫.}3t<-( &2rmD-gOqE{ǘh'8q/9mS9ԡQ:_lOw1Ny \=lJO3RpףsCi]K3q|voסҟ.DJd˸7+` :4P~'ܧV|ᾃ7!ث&4O.뮻#Ax`Kut)4;[|4ׯ+)ylH?WV<]ݾCpϕׇ?m;}㢜dGj/ /7^g=^w8! ruoeOQ:rKNu^0|ZKB7Mvgg-n ŀ͚/fܝX-6 q=K?^pN[Aqy>) B-Cft]_jHEl;jA?b&KPs*S ғ.qk|EiUÇz*jEAU;ngڴdffȨEA.M,=O{6q R(ٸs }s7g%[ }K/qW/L|t'%y .y5.6y?=Z?}Aƪ|q#lČ;ک1& |EuJKvmKKط{< = b͚o4i"991$ $hӞ=#p.üxѱ$!ACk!"<CyA:JG](bw"v;*ءU{ψ#(--]_(jelݺ =OiA:4_k0mX^4Թ\XVTUl17[1ʚ   mܧV/XiPMC^e.cIlۺ%Zkعdu` #Fbݺ_xP=|2UAф?MiF p (j`v;0ROEmLD%(l(&OC RTcװQe5,_m(? NE_ J)A@V4O4 jC퐎 AՃBC:oJD6 B7@%(M;uFi$LWEA AQPB7E"(5ʔP!A]!AYjC9:iC%ڴ~`FqJ &Tjt >E#7h{zB_%l,Lq=1P`-cŲd 6{Ql#2F;d?IYլ\VLqvviͣp|W (pSrz8:T>V|ScLFoT/_ \Z{ B>ݒ/#bƝx 8L-|{Nrt8ʵ)3Ycwb& v̴e͖>Y8ϋq̛JAŖ k0lx u%;t{5J}dGv<3etOGv:´ # ƩosꫡC9!sS㷡g`ҥaUa#E#us1~@էpbP08HWBofdBJnu%TΣtiiͥ˒@:PTt C[1:N 42KϽ?CUIQ>}5!v'^؝xxjhҪ>(hy tv`p *~T XÖ́T]҆eUz6* ?NRo~qa)-[ "p2tZ<֕ eAڎmwh/bwCNjw<1f2gKb%xoy}L8@ 5Ven,Pi/UCQdj~,H %JmUÏ g?n PNeoIgi>2?B=fUm0( x]^_qڝWQ60ڟvo>/BRl=N2`/:k#$Ewm i ʨb `2Le×1͟HAm~rs(t}ir5IA0Dg_O*|قSm'ۀ,` `֙Z`5tvgW|7;BN;(Gl]*<}Ұ[>F-.g䪋P޿ 6‹grʓs0]|r[FS8 yA?[eUȨd ϲ緗q_+[xڱ{.~Oi#JXK ^ouTw6/[~{pĜ'j`bͥpz.}d2m~& )F}x;l8KUU|<)d[FOE66HA!ݤOC~P؁t~X[bV=v@?0zp8szW=>Ũ7% \;ƺjJ>?Ewm"͛Sp]yo.|.c^  rIWEGq;/MOʷbW붗Yޟx8JXw-1Q|9xIťGGv$)/[NqzO痿ycC?*2FOŦkdsp%OCt0X8l*jZL'0lRc *m 4Y1f1dJ#c?{W)1z+4n\ԏt"+"/)-bc5  ,2ovUņ~2C]?*p+5m)zm?f0*=NG '-!=S#Pj7䶤C(.8?k& **K7?w)IVWV٪LF}@}(MJolMБ2wSQgb7~ZÜ1i|Yȴj>UK#T.O3Fn:MEW~ϧg .p,aձ1knbx!/[Nl;㴮 #G⡇>}Q~xyu=}םĐ ߢŋ1^a:FS`BYדQ* ~^fTj+#yS^}iPir(w5ʬk5}~Ti2HyܻׯĞC adk):akD-&#Eab%AlLj{p 6RB~qJl;/;^K/LZZ>eG|TVOsgK/sے?5ϨҺ?r'mmH-p7(T+`w*ԣ~RQgfxMÀ*bF3äPIi 9qX,FS9?Z2/։/'q ;n Nm +GN=z3)*aߣ>Ha>/n=fKo v(3zw=%Bvn`>+cTKL{L?{r||S!҄JF<ko:[GUn^\k4\Vk%xbwe݉-v'^vzJ]mu;bywqv-ҥsN^1Tm[zIiƏ@ _a?z^ rt!FiqdǫT?_2b0.xcNԱ ¿{;a,4sԚZ%$϶XvN2n:q]$%<>*kjf M^\&١,#w gm0} Gsќ3oY1l jZJ?dAMOb ;Bo"us~"Se݉-v'^؝xr>/fP-}Wb\ǩh: BII ]=JVv֮e֬Y-X[)BeTGAAGF~} ߫ 4 @? 1f},!U[kg_9e C S1f.}Lnj0῕ ;ώ{Vp6T5ݐĥ̈́77y v/S{ .Jށ8ҌFN?'%}k9#=lt)8I+w}ߩ6^wc_K8>P"$Aw0/w?P9&% ;\tyt\2O&Q'^؝xbwe݉x-O;/D㤝vX^4Թ\XV<(u*0g?`D%ڷ20j ~P>: 4~, ^Q1MCgW>Û=B=f2O9^輠>Ӟʓ?0 ۋlYzwFnj" g =g))ׇzG Q7s@jNlJ1/j?_?DDtLt؏MQ'ӬSʒ˜'& |q/\tfI x5eBф(6F?}?Y~({mFlNS\ISe݉-v'^؝x;^9NΊP_Ť-$b !7ŷ,n[?n Tyʶө\/) ө /[Nl;hpC)޳r5m?Vrr9q֒8#_¶T_ *_΁ D Ug'?+. )J> /A}4RRrЭ+zp=dMCOu EAE$8|nbwe݉-vۨw/3&`4|~~?>:6oJmu ԃK noR@ϏOp4 !مN $`Z@0.T6luE-Sf3!U1 7xfXˏG-:,{;"v3>~Eu&1dޫɑ-v'^؝xbwe`[QS[1u͝8ѣX|9V%`ȾM*Xɲ9)[A0;u6j_:ӧ8HͶUlٰ<{q @CP3:ںK4R(Pu6-Mٷ*y;\FGA8|$ގm#L虯=hA7_sY=b6zZ("3 _4"Ysf.m͵tIA۝`4ŽlgM>I-v'^؝xbwewn- -oa9N1ŚK\ dL!"l{SwhpL#\5xRrɶ(If    G,8t-4G.Ξ   Bh;DZst)p^MɄbrcS}&S{gL7A~\9͆h44" $y BU+U8M8۶jm EYYS&OM*   ĉ8qj ߢŋ1^fFS`B>d#  B"heHCT?8h=5`o%AF BhNiP[tܹÁW իcۖx^l^AAA "zJU[֭cȐ!oj%+;uk2sOAD"\A!^4Yƶ 򪚨zM [vcZ{PraZx+S} GSj=LTk+`̱d&7*$#A_Cy8sf S1ۇ#7q*}4i 2-[ /A}4=[ )y֕P=8ƦII y mQt1EՃ~S'&ΞFES0P)&LBmpʂeGEi,K6")W@ +pA!\A!^4jPz_1 C+Y6'{+T]wQ q[Ŗ 9tQ^= |x\W>z  BA!9Nhiٹs'KWǶ-[zXd#  BK8'B*PnC w}}XVXv-3f̐N  D BhUxayզQ"n7VW8Mu.VHw Bg /hm^XT1. kعdu߲   ,Z_)z)QNJb댲;HWGAAN`xuZ]N6m҃ǘc(bnؽ(SnNäjV.+8;Q8>+S} GSj=LTk+`̱d&7JAAgA@WINi**ލ;Lc;7~OPnΜo$u M vdڂL˩fN!)M%h#/JS}8KQ7t&G+@+ ċIQ IPZQˡLԹPuvR00*Hϰ䑺?p8b1( +7@([WBFAAAV5 ӎNS. 8@Q`=.t)Cm&@ŏ $T6luE-Sf3!U1 WQ{ D BP۲6o#r,x^廨#l{زa7G.ʋgaׁpT3:l9y*rAA;жz hE :+Ǝj}f1b@RtGƐƠ* p\&Z6|d̙ض'7׊BAAg^)H/uCk.s)#i3 =L1:3ߡg1u_sI%ۢ$mtšU   $V']x%o9EEe9r60AHt'A!QAxѶI[jdrbXp\mP4>PeA=դ2m6.1 H  %8D$NA&Mmj[颬)䣧 Bg /t8i6o04Ͷ ,Z5kxW`4:e .O?w Bg /8mY) z=GY|IxmӴ*JK0VEAAxpD)囌Fz}+  B >CZ8fӖmTUW6jF!--1Fb6tzL& -s^qpx ҷAAAC{z ͓n7ُ܎iN'6nb1΋CU`\N*Vtlٲ;>/$d /ܵeHZNٿ CoF)))TbOKcI  &tR__i}>*п-77#d  efT0d=m@ j$-5#GN__WG~TVTnOTY BpAEMs"D04 '=͎jEUZIB>Tqr.na]co(3u   BiG%*0T/R8rUUcv~|O=S-BHvߏ_8UԔ ̲z8=\k0 7$9Jx8BA /ڸSyu÷Uy9T(=rQܥAǩGEE 8T ɯɑ\sU<ϫ8'b{Oxgs8;?{Wuuhfԛ-[rŸN0c `z KMKI6 !eBz ,Lƀ1r{,VMnj*$dz?Pts93<|9͍Q_?6"""I>pSk}dcׯg~3 &ÜØ=ۀ-|z;K;wGuLc{Wٺ N"""""GS)m:lR_Բ7ޜR'Sܰ" ", &/ ,0d8$>onTrqX4cDDy+""gJòe)CQڬG EꅒwqBN^zCZ633lZ㤟"""""GdA+bn=醯#N4oT=FlDSmg&!/ u+?=wc::%#w9dz3ijTk|*2IvV6~)~ 騼|</-^LvV6IDDDD/K"Tpc'#$'/.vrsh \X aGUU5ӎd 6vZٻeo4\Ri(p'""&TFqC{r IDATAayˇHXaC|׀ F!Cعk'o |g0d eedef&ADDDDDzlk4,AkX{K}LÀ#P\L[zdgx"9O""=C[I&h5Ufܘ{#xRޜM`""G=sED$]ڧ#O}3"ֽ2I$b;)ΐ￿3Ǟ`ɢ ̜}jWww)""ݠ/%ODDz&h9U/V5NtC=R?ta:EDDDDowquu+""""""GFt)ds!Zݚ7""sthV}!6\"""".ͪz%NꘈHoձ} O3 9d"""".̪rXCNq^qRrSDDb3WDDҥݐSҭ굼7]#N""""""IvIGnhti9Y @#N"""""$MRV=l-q:ED$6=sED$].q8ͪ>pv?/ ?UUGYh63O&)!'G\ICN7q^}LN :n5N suHlz抈HMGm0l#iteWƬˮǞHXk>|^ģgK۬zI8ɪlknsFģgK)qim<Ժ|ӑYQ8nH"""""gt69DOJG."""""G8FiUq""G9=sED$]:Šˆr9-]Z."stϔw{V]uǸH3WDDҥuӱzi:4U/N"""""rtV=#IґUSDDb3WDDҥY8Y.k-NuOVwwWDDZ3WDDŎ/SZ߫ pEDDDDH"zP:^%q*#3zNAIͪ SfŲwPytѬzkﺳnS9z抈Ht0eɪq8"SDDb3WDDҥuӡt-mly)q#\D+""*l2:6U6Nf""""""GӉɗ\SS.3Fq$"""""G9 '+O?&?fϚ5dgep8bL34W't̷s&9YձCAJ68].rdgebfz3:H_zz),ca``1 @0YGICN=WĦgKznw^0p8ᠩȓ' ļ߷uzNI?M"".1bҽ&M.""""""hIYDDDDDDqJqލ^IDD3WDD%sRN)NIDo3WDD%]S{/<_*?p͋<ΉNI#>|HwAn;[ґR=s:=owHDFpV=C EDDDDDZZr%%qE_[9z抈Ha鬳@vP SDDb3WDD%i oݝt.FNz'UODDDDD$H'$anBqIP#9"6mz;;_c==iKYDDD[V-{tDD$ N"""rD|YV֮}HD=#y!"p]FcMi [ }\'xꕈHl Oj8t.""GBiz%%ػw%IDz'pEDDUVcǎp82gɄB~v0`|zڎ75b?z3l&sN\ٺuv7-d w S^Ñѡ~B>^-{1sw7¤{͚سg)Lt3g~g&XVիyv}INN)Ç 'FiWS/j4$"""=nٲãMc^@qXƌ9>n/=v3˹S)RW|_W-KMM<lٲ͛aw8_s^۶xYV[[o5kq5 RGe:y[ځ8p` _3w#a}E'^$W$^5@""eXSo`ڴӊOp^yVC8HWxYγ^ʍ7R+ Zz2v p\k ]ݟ+ɧ98BJ8>9ܓ\>OަJ8HX@m.rrGm:sD]jkwq-/Iy0|l.4h*'^)*Ǔw,[(~7,+Y唦٭XÆgj(/ ~@0òB|{Zw-'rٿelݺ &l߶-}h4pDf#FFnn@#k?j,^{LAv""/ӑ̶m֯پ}1{x(*0a\֡UY?QϾNC@gɒ?rSl0xtf&c~%az:k9Mo䫣JdE=ҡnaO.mw.3"JKOd2/ %0NY m.W&3f|J}gl۶}˖=J( /o7޸'?z}/1dLo ~y ٳc9zճdذY 6Ѽ/ǭHߡ5N"\6s{54a1۷/ӡ۷/'ϧ W&]r44_OEEϘM;/1m E$j֭{)y^inN׿Lcc^oaJuϞL|U4pڵ볔9;ASK'^ *]߲'U-ī~+ng˄ n}/n9H)қ' 8\ʵ<Ӿu/ss9̝<sVZbz>^3k:UO"ʕO 60dv 81`+W>Ɍ񃀖9%XAn]DU ڕۓR}Æ͊y>/oXmwyFDvX=~Ƕ uXQ,/gD)hI$[V$ aLzS\KI:*+ױuBV0XVU> JkW?Kq8̹#N`˖w׿1eʵdg7"ܲeDN>fin`ΏS rrRH> CdȐ̚ä}_y; kEA1I툜RnqscƜOYLo2uu{YvMk~$o*v4zۘ??q]>c߾U 81iuڵ/ɟڎV_>N"m\T3g~]QƧ-DtXN>GfYY8`ӦR Zu5_Dk9K>|D~ǃMS';ӋaXVg :СY v%+k`z;*rR$Ssw$z bwrٿ4{זk:⥘S*+X}>VT,0h]EE pKKGgk׾7"Gh\а?z\T46&SܹϷ#Ktޱǟj2 It9_Ki@w6lx2 3UZV?aˆ &;mwz#FΦMoaҤ+=x_'gYE DgS(.N#)y*~"չ x饯]{g]E$L6izt4h9.g멯ϦMo#fͼ5ԩEl塇fquul=4;IWF'[?eΏij:m[TUU~||G{o"6IQge f+rN~tϓ+4]|Mݑ=:xpEE^kvϕ޶N@EzVCC%ϏMS_?ʘ,ĉWDl!>~zIWrS=ҟ!̞}V_jWn„KYYi:y<,|u,Z+-U>N#N")j;.`$.W&Me{Yٌu_s̙iU/[Fm6n|= ^~.]JKGHYÆ͢hLJe:P(>2k2tڃ= 'I>v3p"M7}DIQhI$tu?%nWҗ '}+w޶>744hw~9mmu_=~h&-[p8ѦfӦТGԣ8-< 8ӃǓGit9\w;qZv0Gg9t<|\Lgnx@!Z>шzq8nu)scҤ+((8lLӁ۝ˀ1q\p?wg(7"UV=br <}Μ))B ޽YSJW=a¥,ZK]ey :qsk9p`=[,dժϓ/Pҥl{/}[8th'=t2s#N`˖,XpG4ӓeʔZݗ.]&ME~Ee3øqR\_ mtCр +_ |@xD4]iƚ5-si?#;չ޶N@EhuV3F8uFhI$E^\,+ǓOQ ̈́ u'S^<kVff17޸ŋϪUOSSMi ̘{Kk?֬y. l"7w%%97Hn;w~Ν'-w?#3z$rt,q'9:{.ytuoffym4̝={dddswrwv޺NEDz08pI?]98AKDDDD[n+bE]^LINN)Ç 'Bi G"G8!n;&'""""""5Jʪ'"""""'$IDҮ""""qI_EDDDDDڲ#{7iIDDz#v8HȪ5k-(<]iE^3<#VVDDzwzt7DDDp'54UODDz+'z""+h^a6׿Lyl߾xm|oH'iw\ψ4˞=yKm+r{)iunҤ+1Q7Mmb0 ILOm%{OoØACHzG7 ӗ@b2ZJJvQY[jcРLx9ÇϦh O>>Ajj6l٣|߰ ۷/fݺcNkkŊ'6d8WCy,XAs'7#OgӦ+W>I'Ѯ+;#Gs(iÇS?ǓO Huu[NJO$ر*3dL ɬY?lWǭ.mw.3"JKOd2/h{Nee3q8\\̘-*yݟmۢvM|56XS@ekbF}v0hT ̙߉INSDDDG?=9킦ti/B*b۶MӃ1ccDyZر\^G0llOÙ El!a2a¥Lpis:=̞fI2wݕ| B*e:R`otj4']4LE$5'"""""'$8$aJ!"""""C8X҈H&h[D4$"""""DxDDDDDD1"4$"""""'$8$IDDDDD$.#'mV 9:9Z<~Z'"""""'$8$IDDDDD$pV=eiǎ rV=_H@D9 HwCDDDל.7@Sʽ u5df{o2Kq^a;tO= ??1GvHDDhN5N""""""Eb%eI")"{G:#"""""ҫDb#N DDDDDK_~D}>QAhHiMG^U]͚p{fftm0@  QPй"""""CpkjXr58%%]/oŔ);>9RJۈSŦ͜rL>tU6C +O?HY p(((tt-%N,ehY)c8_/p:#c|5.ͽo2,|_ר_/l#_L8 5/ҼnfV6OXyȹcc5.qwUDDDDD$JiͪJ k3 j9s=/@sɼU<ʗFRqYmqa:Kut[EDDDDDbJxhvV p3(*aM15+rT];*+zo,o趮5NMM-㣲>@M٥㲱m+3N,P%/$"""""F8pJӀ/#5|'/C}ypO2q޸Mغan<{,TSiY,"""""O$VJۈf5HEztYw7r0eR.ӯ99ʷ{Y4d C&qƏ| ;*qm9'yasQ ,.%-q)(*{X鞄^o\o#o_ >Axz^Ձl,/RcezNWi v鮈H0ҴT89:`)YDDDDDDFݷb3` rpɈ$775qAM0 Jc13]Л73Bm+رm+epEDDDDSR~{)o~9oWOEŌ0ofFj`0@0rr!'7ñcȈl(_C==s "S'Cxto>]O..c+}Lfc5vaٵcu1vMMcߞdR:d(IӦz2ځl"""""ҧّ =k 'E3=66?~=_[}Y*e٘IguO`Td]XrWw1 0xO0N=qb-=+7{E`N^|~ul>ؖu-[}w>&S3;0d;o;0/] ܾFuݳ uS"""""rtӱn:Y#9k~| |>Ɏ-Y7_p3R2` 8$ԉf 0j8AS29gc2tȐ.+"""""}_d.qj9bŋSn9!L>hsng0 i 9qƱvZ>|)e鯺o@U^F°2^{ _/:̬,~?vJ_guYYYձ~ƴ/"""""} `wyS c9p`{[3 s,vlP"Ty<̙$EDDDDh+u~S4~r ǜ9ŹKߦ,q{<:EQFQXXHUUP\TmmHק 4CQFmnoKDDDDDzpG{ѦfÇnoKDDDDDz'#nz6!n7`Cڿ)\;|]"SҘeY&N @'~,UլY of&N ۶1 #F&GAA~ """"""Ʉ8N55\g޽{y2euM&,.nC]dUۭ툈H/8Ul)δn0dB,N8prS,\=8:to'.a:ri0~$ p:8&uK~ɅKZV/ 1DeR7UPPp:NCA֭[P\mH<9/aMaa~޸rX-wM-O>5-+J Ȋ$hhrrbݒV :̈́l‡K)06R(S½IԄ!;.;TTTPUUENN֎nI8N k3 V)po2Ͷ͛(-Ñm|>o&LH{"""""t,p;@+ʽܶ_c#NAee!~: 8f䈴/"""""}O\:W>Cr|Tև)?xRes,b54qڊ ֬Yixt #I"""""rK8:=Ywkk9?Wg{I L^ P]UOa͊WGe-)***xy 0uHLI8y5v[q&<w8P-GCc#@ ͻ\.," f .-bZ [7?틈H3p B֒lju Rɧ4= 6|sId>ۋ t8E?%,SDz, aܿ _ &||1}'JO*?Ap #4 Nv.`-cC5PgnK)EDDDDY$^|maM0ٸ׋+Ӌ *ߌ` ,% EebCt XU#EoW?I:O͚"K,`L;|-d`3H}Â#N1%X+ x=r{=|]^t9eEoij > FP!abL2-BM&A ,`b```aaKԾ/5N Jm9`Ä GG)o愋45O#λG~m i2m;~#p`~9m0Ђ3iCqPCx*VJD틈H?>NVf-  o/^uS4hݵ˲,>˲իؚ!_p0 6X}jVO1N)y68 p` MIK7_߾>qqF8pd]Lє㹻VcJ'r1qTdfhCS y`)FZ!n`'Aj,m_DDDDDNd9 04an˅$s K#DƟb <L7&8GqQ@~Kb9Ցg6ˀ,`>NW,Ըӈ~(L m;a``h\2vc&Çw`OSM B.՘؉E%> @ȴ:ܾdYL0o\ǞvBC8@?A~ۮ`׾O-ҋ <@dcqK֣z""""",8u`ppP8v4U15<酀#&, +o߶XRgGf6tΗBv0 "T$8eY!j͛pfP2炐 S A'] f&x3r5Ȟ8)o+`IDDDDD:/nd&Sρm}*׬goE5V0عݵO˜oz~A4hS~^>U-b/"""""S8pj4M23Yb'͚EPs&zQPԩ|L>23K>a$5u틈Hs4Mr9x7|O`ࠒ7}|ᇸ .='"Ю}Nz Į={Ndg3pJz{O~&Yr0eDB}M=57Yٸ=ni{O `Gn^ݣ'`>%j_DDDDD#ve{a $"""""GpփL;RײmP@s5ut8/>%k_DDDDD'v#`0bpP=]}v p:[v=ѧD틈HѼ<(nT Q#G䣮:p8>j׭# t8bӾOqC O-Vȏ2*֭% U>nˢw߁틈H3y(l`VGSaglwmca6Ӂ+Å3ÉmX!?@(vm~ %'7 fN:}1 LlڶhӉvMMM~lö틈H)qm&,+w$40& SXe8\ 4鴰|6v \զ ۶hjjOat:q:8heYV=\.<ÁoYl;f"""""?ECamYؖ洍00Lˉ01!l0qx]8&i Bxmۈ$JO'۶1MIFFn*x anz8P__/N"Zζۏˁup91&iۄ 0lv &fA ] FyZ0 222p\Ѡy4y^S42331M PXXxm'l_DDDDDH(Hڭ?g^' 6ByuUb v4D?T0vZ2--t:1M34呙4MBP4`FFx^7o^ΒV'j_DDDDDH,jENۻ8%MB @.@\k0zëU477/z@h4Z|gϞ'ٺؒSsSqFҙp>V-0[npWƴ8t,1y}36~dil4pBNWkm͆X IS4% z~4eY.RbP8`PwVR9`v0QŏmUVmr[l -WfZD0-ӨҘ EQ +0Zk|{ケ`-}}}~R`إo(!*v,zAQ%- Aqb}{ x~k3<BxYx믧Fb |>BhVR@ys=sRk} IDAT) yYEDDDDd,NYQ8 r4۶m;ě~[Ⱥ5R=zmZW9!E |8P+V&MП8e2,f_DDDDDd1N&d0`|&N{zj4$G'YeKyشqSmBʘ J/Llٲb7R]tQu6%LN`P@W*ӑ \ի_(R7'N^b\787⸦b!ۊEvm +c*\Pl1/HӹJZr]-O-Yäy `-Zv=y ^ˍ}ʸ،W=U$BSh9*X|yu8& m+fR)RTqb ںPRa!*x27Aa%=Lֲ\,X6%BThQJ&A~?)8&plfNY @WwWq=< W|F$e'iFKiL͟z}/"""""cS.qAqZ[[GÜsi2ի!ʕ+ |Ni/&.1=EDDDDdɧJ58E"M\=ͱ&qȔD"aV"""""268 CsK CL0D25lAE!bM1BNIpSEDDDDd3Ʃ1H$L8M0L1U'/"""""cOCӑDb4$"""""ך$"""""rhp\Z<% ?Ws|iEDDDDdl'Nl6/`D7\"`mdn8bWy5Ì#fO$=LZ|1W7l Ŕ﷘j/"""""cSĩwo3fϡ/G*5|Sn CizƵ kL'u!x7$I&LHWO-SEDDDDdl%N|T*xX/]K///r紏K>#JkrLbV-LiYMR s QKkY !ʐ'bNLʗDDDDD|dd8<8 yxlL&NՋ)w}8O㢫>|l858y,6/^\wsyW/¶G~v~IDDDDDFG   Z6 LgH3>20qo!:o#߾); 9J͘[yn!yQ4:L>4[DDDDDdt uc \ɍrr;г^-{&2]x+\^+wxXk]l;c'Y ktzqP:I,xQ|m7m㣧d:Oc10}?#h?D# 7! ҿ1J k˞sWk4~\c29Ek,y0r!x1թ_DDDDDFp$C'ȵPMjL820ʵ[ۋAOopFs,FI2e2YSEDDDDdlT18aYܺem1Ng<E?!>cxk'9=imm;ͯ'Ͱu&.bu4,lm+ʊ?=Կ>E'B~ni*_:c]eGEhv˭kmkdY24]_L.l|i:{W+b͚=W!"""""L$$M+4c2lt||̎UMxEDDDDd!<"iDDDDDDF&NϿ,I,7^h#H^.ql$¿w"&ٽ7ܱb3$5󃈈BNMZkR?eŝrФY|oso~"""""rPص O4`Ύ|>&Nyȷ N^\}t>>{Alƿ\OLno/n߾c&9^ueը#"""""d2nm ===p۱/|irA~;ܲr˒oo' U=擥].0Ŵp5xt'{WGoT$"""""ݻ:8x˯ 'p|)+ hqR$"""""moe4s.Z[x '%M"""""r{exDISɩU)c-g`-c+ѵT0츲gcs"Ukmb0ORg@@>>eoz`콵%cn_PT_Zib%pY'ay?vqhN<-ߣy\jlncĘ"T.+Y)w&crs3ɟ䢿1%R8]~[lI<%w>ˍ%kHt+25/8c0_gce8G@ɱj,1T 4 )sTؒr*c竸w Edt?X[=l )<[ S\Qr_|e]*>P6]:\|齩N\b(qߛzyXDDDF+?F藘HN!-2#(tn#WOsL~lqp 4_DOOO@h5sDBCDDDDDF\Tc8S*vF&Lɓ1#Hn+̝uyLRL`뎝L?l:ڈF֨lj57}gGL6ለ%NzS(d\kͱp44b Jt("""""g-d?χekoEX hxcM(e',@ӦFo#kJAFbc^q`"""""ûAoowa1W7l }tt<(i/"rs];;غ};&"Ca1m--̜9AHa9%/2M6mdH4^Du:;w<$ ٹ`(6!#C,c玪q&OJWO-E/#hr↋oamcfٻ}1_|7|a^Z|ҹrIz\-uHݔ:zzzH$S?=CCjɛyEiY $d]֗[ ۭzpO`n5Oǟ|^J3we`$S`.M. ª9|a'1e+XsݲUY]qV-!O;D3˸y պ#"2x=?>8'|2K.e+|I1ɏwQ$"o q%~J޹'\vӹ{7袲l~M^ZnHWy]ݜvjѷxϲa+8Ns8]Ӈ}Uϖ-:M"s,C?~( b[xjcyDorKOɟ1c4~?XZ1ĐǞ̡_-b-Np*aCpα7R~u2'Qg㫯g„ z-eU}$JDmOKx]~UJxm:nַ8o1OL}T¡P×ff|>_'p 3GBT-[[}fV&Ls)ns>VGs};|f`]Hluqxﳬ0S `"qMЕȕsaT7Iu*ouN<$>y%ܹ|9^p>W_c|it8;̉}:7\=7|&>{?0w3JDm+b㮝D4yTi|%g_oicjmmUh4vO= FiinՉpjâmU)[-4q'q¬:׼M㤣RGngLD"yz衇Cz$I|>P0@x۟Gvƛ޾>nNXp쐒}@R!5lQ,ZM1޻ox;[p&/=~s9? J렢ZLyǺ%_z\|ٱ4ש;""7^g)p7Ibߦfc1ҙ /]ٳBgX6rEXmlq1 V,[Ȯ_l28Һ菳2p+-!_`sjl%1,[)pWni=Zl E-vk`)t"6lc,u )RxlY|շ:K/45T4B|U\{c$^Z\,p{qfi֭_O89DSs ]3 yT*0>/\oyfڒ/hPp9w?m/1Pw̞K$ΎN^~ >Sڕ8Qhdf""r`0D2cK$a)lʕO[>iLL$(G/+.599''LE!bM1BNSƢ]XZ(mj"W\RSSwl9x8 Ce7,̷8yջC|6zzz3Qk3X$"""""%J3]wE3#c0Mq3} 锒&1&Hl-Itdڵ|3a;8IOs""""""N? &.o#(k39;:/F:&H$Xqy7,u ˳ RDDDDDd$ ^5{vIDATZqD"|3'f3Y<6řT\DDDDD`64ƪwB_>gՃ-='y2Nm/8l#G/IENDB`muse-4.0.0/src/doc/html/split/documentation/main_window_with_arrangement.png000066400000000000000000005124151404105311000274240ustar00rootroot00000000000000PNG  IHDR{-IDATx]`G۞$HBТťW(+Ԩ-m)^Z"!$\=YfyY{]efz: 0g; A $H ADi#+)?_?IlZB.Sɰ (d~3/qV#V+xQR^DżU=d/hs= _?9^@؋;e#N  370?78|4B 9_2F)\ȅ ^C arXehS.P)=6oriZe|]FfzΓ+E.E!C3(x2+C 8]yyVԺ݃q`\Cqf:t.*HGAր)'vFO$L l/O.ϪC3z\\=ͩH="Þ kZ}10nGo+QCg[_v;m2Bಔ A$륨 Jk GsbS\|)Eh\q W2$_rb:q˹^:80 `~eQ= ]EFwʢ  ?Ȧo"˕%'DҿrBgs/ot.J0r߂0"AXNX8ʩ yszHiJg}q|ޢ0 * c$k JOd "֪s%PռH9h7(;޹U | z T˵bI_ _KqDGt2h;gnRo}x՞yӚ]]wV<ӡ \4Gk) -KVvvĤ~QòҢ"#T*%Hh?{?$ϋHEpqZ +W6rB(Z:)*G<Z;u3dZ\0 (!dG6FĠJtA^`fQ^ɝǣ\8)2en@WK ^R{,Ɇ+% U˖oox!IO/l%:?T2GGfk^++'?08Rr}LuV[@X\GYt  5k p %8$j.^8h$Q!7OB"D t]"*Q_6T'^%ԓ.j_o5rGU֠,zȂsRNn3~:ʕ˩1gkxDQ&x^+O:%jQpn|!uyY VP~a/݉Dzk]1TJ }gkS:Aae$5ژ|[ni5fc(*\8!>RCNű|/LE8U3r ZGT ? PШw+@BrۘNFxDe!\eh(%Gư(<<].i | 'uo.2B0su.S_8eJuxi4!ZAf3vV+k•d yHOolvSf<7}7Lq5~%1^}5e&x,[;-v;ՅIcJ[ъx4l-DyT0jxǚ D.9e_w6_{#쪹c\| MxO.,F8`/ `>ٳRJe$޺m̙6wz)^90a€>|͝7ĉ˖/N\~Qj4́WXTyz*+ϟjXYqY|Td5%X^!t \uNR}Vc'.>.+hFckǍqBXiȪ>@PLn}ՃZc/S=ty౫Ne۰ ^ji4c>Adwأ񰃘zk83CUՃ܃/n}QSV]ŕ씎=WÂ>Ԏ0|yЏW_ϚSj:N{`ɢʜaqf۔Za%풨bP[Q:J+982'w~."?K,g\k};>{Tay@)/4@ rwv7ZʉIM< e6r_[ϟM`{Aw ZacoplܼEϨNmXYjk5]:wSoD(*gta5fgTo-*"r?l6~@ACM?r[]Pɟ-2 %HwuMI=͚kd:=)`Mg"r^1x Q3IF K+Vs)J O6 #1vEjUvS$JLd r͟eUrTj0|XۺO/ikO\j#&mQ6#OӺ1]flƿX!uV[hG΍{>shC='/il.ծԎ?6]XPLevrǂ¬B2}ęOi=-nG,ggϽ<~^ `V'{N*زRd@ω_%ڼ\Zv6hNC4h6-lĩg|jS]9?h! N .ZrO|('z\sDϏgQ(wmfU3fO`mczm3mTh m*?;QlU|2>H3) 6U(L=Gnk+7e*טb(M`-Vpzћ1hȌזiZ쉓}z~:d4yᦛ|8AW@KsaMh V,b~,QBB ;`6܏`ƛ+~`#7lR@jהc3VqB]݃? jâ`Np|D֪~'L+#:He1RZNRպA%ܡ{cFu~}Md h<,,2 w6-Nip*W$'$J`ܬeW{$%[vݣ,̦*6)ŊXaö? 2gϝ{;w :tĉcG*6yR]6+j 3^fw Fù()4]0F5)Eh4GX'J኷֤.(苼OKˌ"h *`6n. .9]ː&\P-C>35eɲe# 3oݴ[BHϖNbܻsf=|pDlo^5k/\i˟J9[]렁93zXJ&辍ޮX–?SWwi k4Vte>:&((̩Ӎ7b ]DGV}M.D!Oxу V DŞg8•U`pp+oOx>.v\g_wTDZfjQ0RXW+檿}J褱[=S;7uOSji; R5ЊmFCY~ ")׶_7n,j<9~gZO4f3f,:s0G<]\L ȿx ]5) +xgɡOciqWV\a-gMB "=B cjTq}9+ Rg<էُ%ct)%Q 6)*,¦{M2XL盾aUݵƷ.e29׻ 1&3Mr!F8L2)R n.@7`֎8hꗙo-k*I-BZ ~Z.+vzAΰxM-'>[._\fl$5 54ԫWw܄9ODFEo]۷p"xy撙Vbk)))gw/nETDj11iw*cۄ޼}d\\' ~"EaQwApDs̀TN S t`PPjd_U ꭘbp*Ώutwfq|E&dZ?>J`;R)\uAسSΙPZ5VW/ xa~Hվ!R$tP×}شz~@t `M$_jcF 7,_k'?&>zF7b׆\q#zkyM"\Kr|u5H?|\E4 o~\X.tc]-XQ?JE,|o&1Yn\UzvSrj=`zz 3гeM r"wn|8Z@^}HȣX]Af@yd9,ݹp H P{CM0'B(7oR_`$'ѴiZ V\. 6N<o?sGz#Nn!bYàVZ*"VWLg wB޾5U\ @f73(a6p0geeT έldW eFah6̄7`u9.^~B:DPH{>%}:pouE\X`}=𒴄AdKC^8–_'`4m{M}滭ڵjWuAzoRdp111:]xDxAAsMfFIn(:::4$D+^ ( rvA ѴYI gv e(C`ts Ք=կvmy)0i O*BC%v?&߁@c|~ AV)L藘 ju8>7ߊ Pp3|~\21 U|y'؈qxM7zZL,@PӃvj\NP=i|i#`9]pCQYv‚Ok rqO(PC2ik-  DL@ʨIl&{270Iiڅ͐ j* Z6#B¨"" צA>'[6\vt|g -" Pg@x^a "_t)n+QOZ /l;!iT))i -˝BF܉ j<׌K‹9)ᕮ ϺY8s|y;W)WI@,YqOc-*h,Hb()q\1.e\Rٹyub Z` c8te#\`uǻJyɗx~%pa7q:%8DzJgUTm9^BdG `NCIxsnL5{ DBYy%wrB|_p uwl{(xe *BPy7N7໿pܺ>zڀB .. 2_l>;*zM9pɫx,|iGEK~RZtΛ{!8l $Xml sIKH L' vsn*+PPH7JNi(A ki# FJaK_Rb!3\.J喗OyJq w9w"8!DY[m(!Qd҂9dA+CUZPzY]ɛ9@o#ņmPSh kRPh ZrP0gͯKIn}A 3h^((!ŀTU_†U7IA)AIZB/@G!m?w]/p{= U%H^O해lK N"/ݬC *@h e1xA &rTA Ct|RA+n(2~^vk i㥊3LLO7Q_Ea  ^ 1 fJWo= P?-\! Tg]x2@- = hӜA P d<. U\)!1`na14Ta@0zBe(v#sD_J SσNH6i0s)?X( @?/ %@#|J9be݂Gl TNl#PeBkM{oo -f1ˌCs8y@YT X>\$1 iǐ +\ Q@l(z[ix*4ʄ(Ph6RCedh3 ;ppJ<P/#:Y%6P+]-DHD'9hn= ϐbYO|,-#^&Do;Me D7E@eQDYK$-!A7DzvyZRovMGv]s#eA珈չjXMbAogwdrUSLL6_9xuNN0_3Pm1cUxO䴱:cݵE?szjAYugnQXD}0.BZT0T6}(>YЖn b,ߤ'VhX5Q Zt[ȓy+nafq*7=3'[/eA-s P EF 4+ݹyxD VE"AQ7HsޘdC)(-SiEc(-^o .$"P^[B‡ A.El`;o/gutS p+Z.%-lؒ}ܓg?ZF{f ̿g|~ڄ_+[Y1B}X-[(0كpX2oZszߵrv=Ǯd{AYwkַwd]w=Ckfo%.|%,+OA{5ג:XVvQ TAB`Ƴ/o|NSΕEa,.L﷬効Mu2翛,9y]TzW=}הUˎj1:%el\ }pvj%V->KZ^LӔ.hv2#@DW(lA2Yk5awD -(\r~զ>nE3A!*,*p0@mEA!-0ԗ1PK򦠤%$Hv3  H:Φ60ڕhI۴k>/+gnI2FLN4vxkêun݃oY/k'mzt[%k?޻Qa=bǭ{, :{J (/Ns<{ݣ#&9짽OZBDΕfEBZ0=8m* T(̩]CF50u5)bYVaο\2끨JŒ|Aa2N1Œ?hg#1[ZD^Ĵ)`VgF!kxLK$ŏ@tAQּ]U⒐ ?BQ n{lL1]ZvɈ3˷;I\x$Lu͌OiƂwyԶK|?qުed+מ=V86e1]ӎ:N?-:]gx5C9o8]O0Uv$_Ve@u[`c[A ^;" 5Lظwv]=/Zu)'jI_ !IZXom3IZ iznDZ^cmO:z7{5 NǥjLjXeI%^y!(qTM[j[Pʰ ˿j-|S=sm]em- 0{'XywqC;ZW SX4mɾG%QV3NqX\p̞sS C_82p^ݑ\? CX{/?C:.ܻܩu;aT\2m><ɫSCmx!m==g;3g!#(=C`d$[Ʌ'-9Vk? va_\_O3E10 E_rF ^ p>zȵT-]7f@DTQE$[ vqc'KvIٱ3 W_Q{~XGe/ZuJ N|vҝ5l'?(yޮ|'e' k'{^L5 ݺ*Mz!w32Ag9:<އ Ub}oLisB׭XTZu0$qK78ӣ?n&2Q_h|8n*aזj }?6k-5Vk7m+kMy HzlUr׎=f9ω>sU8Psx_NS[E>Ր/5Y?55{mQBBI-䂓siFo8QI}԰N~[o/շ^|xu~̦^4 8&6A pID ٛʁb|JB-9Rqe}T'A)_a洿 VAtf!K6ש>(LiDQ+Qfq]sAp`'U4i$mV u'>xjŬ.(;ɇv0?n/P!i8&">H^okAel0WU~-2Mڕ`X ew}WB[k^Oi-Srmλ Jh!N^|drnΜXՏjMX9KxsYk(Ɋn[!0"ŭb c`T&*W`֞K!FjM[vRиl y6r@j~H{@Vo:cKTbu[^ jYuBZ T:XMcZ V_oMKc󍦊vr*"R<0O("Sz*Q܇ z }g&wt /w޾ Ө" 3TeXΗ5.,?O:YPq>S|֝RGxߺoIϺ[bM[v̈́jLm5 }H/f0sU*K5o4|ݩwжX¿k֑udѬz6NS9٠}4Ռ]|;Yƀ5-rNi18X5^z3M!l!E1T(2B4d S @\,HS(rMV(ȹmSn_O'Fg2xUL4e;(7=i~JUo  L^rg'紖g8͢:mKvsE7[%;*0:0ƿoAV!ӕy8Ħ:Fٕ"ن&RT~]ʸm#W-%ihaPPXX~]oz뺞׽_;ޙ|B&\ǿnQq۟IY;i!}/?iN8Kv1#zAZK @oD@s?ec9G<8O)m= ӓz,jla'ՕVܱ 02֗jF!z0`j [p yP1%[oA \\8]j̙׷9bgވ"pPD\|E [Sz =ߊ+kpLѧjOw(9ҿe|͊{@ [~-U\$<_;&E5 {[}G͢.ۺf7;fmS zB)3:׸Չ #gA.v}OKǾ;(2 f='RfTݸiLbe.nߑz 0#W=~ꏵH2ԖGS2dnԯ_-^?RQxe E!f֝X(z mi]_l9o9&mʈC#-ځ9u{OA i $1;l T+e[T|~7K= UjK[nZ:f,Ndm܊ԉZv.{H~T!P9|P<ܞ ^kk~5P$.0QLD5;l× a10 j)׶foR[r-c,na>4elNg-:zb5ôjn>ȷ)l0r]_NJ-(8vʀ'"9ثRE&rEou;؋nw Axd2PYHÑ.OR26ynJasuZ#7]"Z߱BYZfgQ[k+d{QH\S֯`ͺ%8׫\\#:e8 (VCi [ BWgܸCwo(vPte1 NSNPn*Mۗ]yJFE䂌wzÕ'K7}(;}.!?qa?F ˜ӋLTR9(w\!_wZ(bk \L}C]pF807ķjGjBhlMtMOv؈G0 e(fW`̐FØ%R?w.Mb2z,y{_wkV#Mkyv#'I gp`Ψotۥt;TI>{c^&j;z:x>BdQ7(\4(ހ_K*}`?ʑiwׄ),cޘD@uz#c8̽*$!AKκv6M(" _exQ"nT2wGvqgT 0 xx[x(ř-*<6(,w #$!AKwfK y o_pLFoF x\Y*%nw줷g>zKk=,է̷ֵȱ*diz+k2'8*~nOy(;Y5#e1ԗpZe{9e |N Q4n~HBQUeUʆ/4ih>,ROWU ЌpX>h ~Æǎj7o}2ucH&VAFWV@zNAw<裂~,%羠4^ RQ/W$ e5Սh=O%zS_·w@8qR&-0+f/>y℘p휑y`W/C4m$22yNnUnw۷\./l(͉Lx> A)R{>l ~ Ic! ^TXcS^$6Q|J$+|'ލz5BŊ%KHڂ IdT~Q;[ &;44{wAgT%QjJ ,QG 0?Nrxb3t[awjqh 9݅mݽw_@Ζ?b lWR%n2HOw^O?XxjS(@X/v]D pȘjuNIelVZ_7nkzxj9lHy|-_!F*Gjϴy ?PP90FːmO IPMEGEky󠰠YYyt_:th2ah4!(8$HH*p/s pʕ**)8V ={aX%)Ov;2R =o 0v@D(AxB_Gj|K0mlR s{37(Q/ߒ _΁݊D^ёSgKVdfdK $F16.tBJ% .(\"LAnYVB B\<$ > M^SSìJXmւŊ/  T>J,)-޸q: / 7UVkΜ>j۶i=0#[n9kIJW-GSձ];DaϸwuҥݹgEq5%epa\_:uXJe  ; 5w ^$Bb[2sV-QR M{1(tZPrnM#1\IT +))'Orr8}\VLDp^1mhyfa+)N9F8<Vp?},t?So܀/^|vʕ*;'&OLL|PG*Ƿش~\IjPRXd5݋z]sGsè#4^e`Pa iVە@ɔl.'P9(BCmѐhNP&0H e2xu(6[r%T DEqjz-`2n7B%s&])i(9! p9+xb%=$(Xۈ00 ?S ݢnC]~\-Nܱkݺݻwo~5lƗ_80++kuC*R%QS pPPL 7QFMfSmOqb&f?A| P@loh)@hڵkWX1V{{thJ&@m[p:H Q:~,[0"}dq"Gy޷͛jՊ?nݢ94$c&-+b7hEMֶl@crAbkqhȹ=z-fU0affa6 1fYBB5q LQ{ eP!bAF  w5xdb6*((P% Oi)BE*4j -JiAW،6a;(*ܴˈ؀ɑ 7իBf ,IχU;+tC LH Q[(<ٵ+}n9AU))=]1Sʕ+>}#<˿;0U+x&?eޓVTsu:]hH(,U)&x-?avfCn1ѯ6.߾,'BA\\, xVɋa0{ bGјj4ج6m 88mO*_qk{+rr"7Nd)=̈́Ab^P/^?|H`rb~vZ're.Z^̣nKw+QEVzlۺYHXx`[oeVMeʹBL:W3E0r!.]7֭[GGG#2 [l115WۅK^M-!Kо(cϋzSdĔQMT/_߲eKÿ /^j*H4ۻME<_QZpШk׭fiXҮ\ѫx-{v k:3 8M?4 $rcicJZm7Ȭ߿ީӨ4,3k (HVn6c7ϣB:5EEC ,C@ɒ+8\/@*åPR` ņ:>;cBG7;NAsG*-0[J+ A' GO~u"9@&-<ɉC Drg `-rpRȵ'Of]{vZ:U 6(c< V D* .96nw-)w3s`,_2fw@9ٛW;Od @,ZfS6'8(q`DFD\OM {͒qqPT F RCBB@c/AWz3hPNݻ/f>rbv횷hyF {%*|2Ÿp$'W'Oӭ2Re{f4-J 0Not\~+k2-Xп_bē323WYɸqPN̞=;,4K7}*'.)m[B޺ L؇###B@B]ݼQ[*M )77C4M۶mK(j~J#}ǎݻw;w.4/qs- ?~QF! ɱaCF?4mTC'dCwiҡÃt{m֬ N(-似cO>ኰk׮ ,fJ{;  ֣7<p^ڸ?h0 ѣp syH湫OjUFWwmLo* H N O ʔ60{\}VJŊ ?G'r`6UP'yyxT?|͚5I> *R;$A7EA?G"C MQ=}ol[~mBTցk6P')P'X7AA-S|:;AKA#.Ó. &=,H<rqn(> Jj""k4s"#`h`ټj -vy턄ʉ=z$CNxm σhV-[~pl9cdua$pt5%6ŋCɓO>̂bѢEYYY^>}̙5hZBqHrcǎAPϞ={P\kx^:r yu8өS'jrDF)M-[@U? ҩd^h͛7a۷Q OИݘz&h!#}s֬] y_ƍש)RN> .={=xv"$W7Ѩ*aK*'2ViʓuxtAXTfdH k~5qd?-ɑQ6[u`?2ŝ`\EL !iVsAܹr8F,#Dp c%RlL@cJJ뚭+BP?Q?ͻL|af׊;z_!LVգ$H`<$)? ڳ'} FJP Zd^k6l-xQ=Рt!( *ɘB5a'hZl_{ь+ h]x9GorL,\&L5J2P=k4EGDD&-s9LfRhٹ9ejTt W(6]'PQ(} FE= 0AGcyT̖?r駟\\~bbb (/ύp}nw?p&(d7y }@ݷn>wQF]Y 4 *|K^ǙX~(" a8- 07KA2 ۛHgMbA\Q Y2f*#bm$=ǁ0i2  6ܹ3##b@RW(4lաJ$u֭ۿ{ŐgS>$RB}RPPТE(x !0ˆjfggÖBU("CgϞ͜9^z v;|&O0+PQX!4xxshWC)ۀC`xV"qnc {R(Cf# 5@fW{gض` SrʔSf̴2ڿ+u[+SڦlKIqocӽwwϟ-D $FMǨ([*5Z5k|rr6OHJ!^p*4bʄ7A G.M yN:91p*f!irEI4 _/"=)9}ss{^,kh?8"[e1d>rЭDD ƬbǬAg(b #m~ɖpVzlS.YTflde@QTp һ , r)!R`F=]}s؝p5ۉncT}7a6'~ Jj޽{ O>ē'O@8}}}62xʕ˗/!~%vyqZ $ܸq4A UVVuǎGriTJO&Z't?DC3pQsĉw;rʔD~C/0m^N@u-4ƶ!~ $SNJPصG=MWޠٳ=X,ˮxKނPuÇ))Hݺ̀׊k׶oo!+mK$&R$9X1w1[b YG؅ \ - Y؎zJSH7oA\>a1tr%Y[ѩ)&UD!1 {`1%X\ Ԃk=:_i={Uq_v- M[.Z}jg*ͪ64\V c'R˕ ? E=ٴz.f_&-Ts%dR>|djE?$2嘄dbK[+') #BnA\9u{ }"\I܉z'>۴)gϋs>+2YʭqTr1 1] JxCMWp;xdf}\qrލsBK 0ƍ8e*)%KdHAuwA+Zi[5hR3ANd ʙvr1vNp0w={۷o߼y KtJlWPD":t(h>{F dZ?@pʭ[߿ߦM/_',, ԫFiG{ jN_ 6P1QO&<lxx8"I=f޿c3,&1א:ؒ{iOwI.Tc6ͫBd!DE97!vY6'Ph0$3Nf/ 9~Է*T7oQfG?91\LJl#"9L EvAŧ>!7@QOD:J_+OfGzj]b_p(0Bh={RGj6[d\m jB("݌C=;AFsUXti83$jtFGEG\~26θTcjԘ`5@-FjB䀒aE,j7"ɮU!8{̠}lxN$E2dh(̬p2ꌡTfիQO>O* aѿ_)l۶*FO&:܌餵wlt©'6v-6MZ˻…9;aY|$zxqxQk/L_( h2GD4rEVB( /GTZ{.]7n>'7 * !@6$$ZjJZv|>9}9̎0غuk`r@U@F1#nyɋ/۷͛S644 590Np -W#&w5P_Ђ'P̿7oG K:\簡z <ʝ@ IZw¸Pีg]6KC6! r)f1u1 "X2NzGz ȓRM]?LJT"6Cu#jL;dua;!Wo$Jwk&F29UDDuU8h,Q}1u01FNCzpPc\GA=y<(DT o) -x`RRt'jJC<&"{fۦ9ZLkdoB>"{! p@iW5l[dϞXܕ+H^ĠA%݌m  oچHDhSR š! !!PIlh@MOKd\ODHG@1g .a|8@dKU<`)!@Y'GU6׮^oկwA3LˤTl V%J뢞\Z,&+"<<",\]RNNvdDM!?|r9&L&_es0hš=@eT^Mt>eBN?i҆ #32NΎ]rsƍT}w͛5߀~{IxHkmsJ#d߿[uXvي۶mРp'2;Çoݺ0y{DYw[2\r6;Nj: NT|cJ,vZßaÆUTټyL&?+0m`(ZBK:<q#T}v;R[h8~c(ܠ@@bܨ' yX!U[nժS |ɓ')G3+`˒-oEMY5쭰qa0)Lb9!Y_ߥT`ݿ[U"*{~P6囨(W^<}}kOQ;v1￉ dwB̬@ύP+Sfc>ُщHI t!O9١EDhsJTM?:dE GKY\O飴>B9:aÆ1cƀRo-09fr56p& U48W\ VgMŋZglXF$j[14"QG=ZXI4\BLTlw3F."&.\ܚzʷӟ=(^ڵ!!  7nzHrr޿Vt@fͪV_Bضc'p'ׯm۶w L u ܾ}? <ȡcݖԓofOEgd8l4sh]|}7mrįݻ7BɧMAr`ذa+\ؾ8؆81ح[7*Z"W n]C:@SD z˦m2CRߠ>|pϞ=i5_}mV`lVsP{s {#o<{ޭ\E3TL' W*%FDX |D,XT\=n@ >Tfm{XR)MIPN h99[Ga>M$ܵX,4ml` RSÈ;$P)|6aLE=!FQ/^ăDj+1 "#< J INFFH/$R)e,6cux)԰lמ&|L_ "av"/E(8z H˗ϞF/_jRDݸvh\l_Oo^T[z#[։=e%}2{LJxÀbvn߶trpHq@SqF*jێ] +WSKyv)J=V+.AxI]nsÇ-[A,2x7Gnj7Ov /vaC֫[Gڬ jŽx꯿zÃ%P 6ppYM*'ZF\~5koݺDMbʗhx+ghs]Eo+NbM><`UL]BB5i d !joܜ=Nzq2\%mG'[.M!uRQbFxjj5˦@IHO'0DhzrQ;!%m $)G;q|_gp'uP\9 D~LbxHgkAlrr(!Ï,doL}D"5-]1 Iso߉H0Z/Bxe! ;fwY[ɼCͦR+=4/_LMP57H&vatMڬk>'p*WRA< ENvL&U)9RY~o-j.kϺ".$gYLKS׵nޝȣt/Ջr*>}X]R~~ `*,æ~VG7V6r$rJ7m.͛WV#G5{8$Hsr޾};k, &Oa'.`)(촥&~6ԱCƍYǂfUm>oog͜9dhfħO@UFoWvm+(%{}ϛ7Ϝ;?gH~# ]qZ/e1<ԓr'(ꡃ6O$! Wy`H 5SB2,61%O ,cip=I*oC P&3E*MdҐ>y bZ!JlDEaN.j…7*wr21{dB-FAϔK)⠨W j4-x2{_7+X HY tP@~~'rUZ[G&#)5SͶ%#>C $!'[yX q F;܁<9Nja >=-4>\~7PJ~Q'@ln175?_^ٽ{/],1R{*/_7 Pit'w,Fp\ԟ `WGgO,_ԶpޮW-=&"D!Z\2'!24 <<<| 96Ŵrbb&Nlg?DGm>>I Й)wFy?o !~ڴ}np`@,;p@3gy{7wvK>U~fM׬^==#0=0 ͱ*eneʔ{DjvhhHlUT8'дp\j$?nv\lR%ݻo(V+W)z.]O~V}B֨d_M:Jn^B5=0<c ڒ(+_Fj&]xi(%85Vf0gX-0wLNEHɡѫ,n!n6f#OF6I6=H(i[˲B '/BL=p+r{;ĎA ^K36ҊydPLIL'!iDB4WT 2e9npqGaԙa&t X/Vp8V_6EvBz0>t8/A :p=)U+V#@3"$|@Q@ PV9QA999\.מ50 \1HTyV#s /@ s lF *J!xE= j B BQ8prVB$R$ B bal@ @ b/p@ 8֣uYY". @ @Rt7ʺpl>k@ |fX,ԵfF!)@ @(8N'@ b 00FT [z8l6tnOHM@ CQ+;u^aWJb١BuG"|JLT)Um )HӜ A RNQl |ej *YJ.O Z%JzЖʯ5}\8n P),&Fj~~~ymͩ`L9-PA Ha%ΓN vj,[>F&(" h"aaO?͏KU|ZT7oW;ǧu1m &nkxifn]gA HABƩa b'UhEbqzj*ޓd ˋd\'Ojƕ+_^$\k O<[Nab"M96 qϬ@ ~f6\0Ls& 5?@ftɒ.4wyyٲeMҁѥJzPSs-3+2he$zhj6t P^ٰfoUˆMVɫP2no/J8V!c&jϾlϵ7,&jb]sg9'͝f?>aϗ')gTqAq:%W՟|;~PCOh[ҝ_ppDo~r`"kdfL߿oAg͞mPU,.@FUwef 9Og|l]@V9!Õ-K{B6Uۨ)ʹC&d`]m07ahc04\井.>IQ"'LYŋemb~#) LNM!}g_ZE [DOç<N kA&U /JOҕDdŶDG<:=]ibS8T'mՅmU 34c]c~}6Br3z^O(#F%]LVb%mDц&dUD#N&:#,\0nz[o٣w36o" #sU])G Lh>U K/SGjv|Q{)WOB2ꉷ{%o#ߤ45G7ɴ(4Ti}}yY4]V!_l׈^WlMͽ2/h4ʔ kt[da 6>Dׅ[i[S|~Yz M;(ɑZV";F/]qX~mD_'מ뙷H˝+zݿ]ea4~tk|ڌڎd v"QPX[ӌWn(5пGU!VXmT uՉ~K,-|m!ThYJkeʋtqUmۖ) &IAh=N7s;S N/Y*YғC`<*{=w ށ龣t;/`,^zF5:kzH.8Ǿ=|o!-c?3 d?x^1\")y \/E\+6Ꝑ+wLiyf͡TJBB!CbbbV\=kh˸c<+~ e/{sە\Ǘ)Sfێbx}m[1l/GUZ5 \`Cp:aY雇Tћ M~ځ9Bۏ5e/ {r LpoMtf8WюЄhJ "nٶvuy'˸{W}*ȜR%UvkHBذ'MT q̌]s@ZL_2 =2BD ^ &Ϋqgm m)YhB:lޡiݷ5~aוP,{fvF1Ö:LŎlqH?=?y3_O/><#}MA^}cZ#)@pb`'-w9־;a٫{[n}J1|u{Su//?<0,}mYm L)gyp 1dR]"h]^g#LS,_L,ڳWL;p4Y-]AI|Oy᠈LO{~Cs%{ \-p2e,5í~y8Losp; OϺŴ,3x*,8gMk-[ppm& F +_٬ hs/$}"Lɱ K#G*9,͇c L=cӛu] U|~_8R`.L@.Q/݌Ƭ6k~\7 \OƑN|GR%?` r, G "N6]VB7ou]: G73i\2(>PIP}6nMwGKZέ, Wq.7s,埫Kq2hnGY@ . Ÿ.ԅ bFԈ~K^nYOsp:{R9+SfŌF^V*/@qI%&F?ϔKp㳌x1 v*%`%:I+ܦUrBO BCc\>n2929z7_<{+FCd M~ QO_QЭ(nc,Gz ݉ps,dҊ+̝7ow`""A`rX j&~.;\X[Sa$:Z6YR麝Qa!g$ /*.Z73T,-h Oզ}J2jLhj(2#4:Ó||jpv3TbTD459Yx8mYRvh :m ުtTi)j8ՑMv¥sL8nYfhK:9RVs-2cf91}睸I圗KF̄exM){@ < ='ĕwTYbYwwYrEm#eH o S8t̵5N },9RyaYpK(5,,CS_HD@?prW!=ewqUSo*PaD\2U¾)?Y6 $6I&R{,4oyrgf뻘 x P W$8߭_)Wɮdxx'=쬖-[<}d]F2ҍ4] |*ܴaa/v~be}/TLL0g'w:**JyyVTzϵWL}NSHr17.?֜FVWJa6OdN:kna)&Sˋx!G-d;=wʉ+6pNc?i*Ꮢw<]ig t뛚گOJ5uڷNJ4W-N~Rc'y1Ђ9qK0b9Y bt cWGpyzS']8kM.0!`Pyzp|Z( ۼl%Q87R,1C=6I*w!p9]̍ {8 mme׫}}qMڴW %eVtvrrXyp< k7 AHs^#mu=B h;2|.݌zBvX˗iF5`Z#_""RP قXWR1='xP&% @go:ǧb)?y1L".ծgًzN l]AtvϢ1Ǒ. Y*YT7bk'M!<~[Cl9WA#+/io\ϴ/(ސːV?15V~Xb0[Q#+/b?'s1 Go/I4A-F$zTwry܈tVp!?v gሒ.l ~]w}֙7 E: ':KժU7l4h{ٷ{nێtbrU}oP՗kB"OW13'CUЫU6+np?|IJ"ZR+OtA,^t?]+ŎxVߧ2k 1*#j /N]7:Ox|"VwFp>L̓ Jg!KPzYڢ꿯*33b1} uWν{s}vϠi?31$y*BTAu'1L'=42=\xN, ,ƚi=MlL6'%0|.10DQPmOU$J»z;`4KO? =+_zi ڣe$0<f7dGEQ&)c,4Wg:cklfCW"@kɰ!6lA~m2fUFd$vX@&X3h0#i˭i޼ 6vlEug{+. 4#1Y~ځ9bo-} ; n༸:x5S5!+Xܯ.9V6x8`xR'T4:n 3|rⴙ Xq{ ̓ahsi-KUo^x3؆sO7/ʉ?ךcN0Q/U*={AS{ͧ F&G=axػNGz}~6剥FT#w{f#8eZO"Xbz//ԔUA||>N^vZ5-<7k.؟;njkZC>`*ߤզM䓚jtT 9C:,=ai\:?NK6-ƅ6e);rrjYJTTF7h+q.]"X]x姠 jx[.=)i ӎ_Fs ){@  UNgfgOŸ E&)yQ&>~x_:UPeEuQJ9v!c1gA ffs 1!(\/37ܧ@ >pT$wU,g?A]% WjJ'NT^-00!Gpܽ^?}|-tΏ_>OsEQ-x5j5PUk56 e*T&nxefVȃzvRY@ Ekk=AwD(FDF{3=AAa7 Pb6jJ#IJ .luhp[s*r74 u?E>7'}t(QLn:ٺQD]^6S{&F{\Ʃ(j4XL } H>5قܶߨ 9&NUXBt ] Y&jn&.P]#Zaz7D rY++LžP5*5+l2C7-/cDzF/F&Zlfjt`XECm'HtC1FwkU9(K܅%2}ڛ b;LcLQ{}O@D__Ƃv'"݉u2S+tkZo%_I_g"mҪj!wT!Z-ioΓhle.Q/ֆ¢h3m$9p< G=R@ @ې%.S6<Qa)4"## [ St QmdS~_ ~w@ RDFD"(R[EWswEƦvmrӇY [@ DfjPs<@ (󢫹[BQ8O12tݲbxxEP!& (t@ gnjVϣ0"uDRBW*br4{5 XSa@ UStEWsDQ ;ɚH RDFrf 9BFQ5UCw@ q[\ÝF 7#gt\3֣&<EE;Ɉ] jyeZapw?'uu?3ǿBQE"#[ l5h`|},.}uvә[Оe?9y+(gd͖FĦ)Q{9X/$mάի%o ;4F˫[ZbfmBv 6.tk]gKvB׆Xͣϣv䯴M|䆳`C.9*@ 2ٮ+bxg )BFهxPQ0~M'snJLd#vPU̯ϸjte +nxky|uC."83=(vX|SW?7L8H&RG\H}/2?Q36S*pš:=htKH;nfDK1ɏFTA+80 uw^xi45ۙ--F^%;pc* mW>waZl> b]jۜl[]!1 JY#P2Ί|v0: ͠am 90L%1OAL LOmvXêW\x׶oh٩ޝ=;2Σ9yȃ*y 0F@ _Bx_A.g^[7 ԊJѥ}j[>;t|"]w,]}g#Ƥ,>gPaLpm^jmCfx2A5r& 8!fMg+T,RʧS㋙fr3ˎ\ί NvtP7u:ڛ}[SK? m>mQx!58X$*)ΉK/###W 4$MMɓxjƕ?==xEpǴJ !onb5*S2k5EڟsJz]A&Z>;FNͳ mPyU1eW/j>}Ks"7Ӿy1kj5ׯ]-&*$4*w9V>,Zw]5dmܷ[ٶOra-5Qs{֏Mvmec )I^Y1㜑N4pdT \eK2-Jψ#l-dݟlp5- O`lԊUk-epurǯ0ωnoW[~|Va1^O4㶬e6oݾAko栙.gI9훈t.[oС]@볈5ʄ+Pj^]wU^i jddLR1Lqu/hU?gX3$fvh 1qbbNH Vߙ_T{.9[ܹ{ʟdUaq K(Ep[K/2fH +>d^*-NM'@kśˏ5YhH_'յٝ@\W)U15 GHp퉸ppʱm^*#"&y-/}ZR+>upQu_-W^ ;qGgژM iŻ.d Uf."6T_kךiշۡx> zP}C__ubkAs-Ŗ0{HP 5;R"p-s/uc ^*RE5*-";3z' oƏ>} g6+ B#@fp?ٞ?8j';Qvw=q?-oM5PAxb,Kkg$, *R~co8J~T̛vjUsu3_ \ ȸu/?o~?yͦ3xԓKZP7_%q!)$%J +ZņΖ CLwEn`ͧ+'.::sn-ҧ]s`45HiLi HG~F1,VJ<Zu[oo4f(V$y "[37m6hR"T5-Vgܺck@b1!(K4H&jX²CbNaQsMn?as-ۙwf<an9^abK[ݽӳ `q8b/ʾz* VHquj(V[nsY鉨Ёwv a43}t #IaNiZeDg޸xmDŽU A+uvu:,TF@~ߎ,Q:(1f빶s}_yǍ`Τvks+t4#ִb+aՠ~?ϯ8uF%>X[!=[;;U'|3zi MÚ^d+bC2pɾ"GTsΚۘ;b#r zT`Weyčr`ΦĻjSP働LOk`bsr@^q~Gu+7ËǞ9̀v7>3m+֏!LlP9nIST>m,ȟ|=oyBoCIJS,[͙H)P:= j+X=?&hd'egbg:x'}dkAt%v[aύx"knQ(^HBb͢k!GhpEw b;b wѼ[XTJ(_HefC MHRrq=Ѥx-AjG [WSkp=nov«_m?FR#vDL0iEHa64wbkCPli cQ)FzA!/u@/n"VG@Q]$^"QQ7f@ DlmNzKWG"N˒n<0yz '{R*fX̰۷TR9 _Sio[OnV@^KȻHtp{N͛n/V,-5`w?۫>Ϩ!,*HüܯB3$;|:N 4_-@-=2Ç'O@ݾ Q+~po݉O,l5򀑵0-?oy!c?&vhKǺUVYؚ֬8N 5wbkI"Pli cQ)tBJxBLRJ%%%)B)ٳ@ w* ӝ@ (?B;Fa+-0!w ɀ8syܼd @ @Qll7&;[qLwQ]xvNH.$ %bZA,( "X@@Q@D* zHH!~~e/].776zJw# ݺ ,:*.dښXj3!dB9gU%v5 @2%%[2y%Z"J$6I.i #2B:3D8%!D{jL HP#SB{#gm. )QIҚ |7Cyڅs"Ws雞)d, #3b`EqN,8,x0G篿?qa{_]w2'Z$r m6>/m&yF6 EcJhyp/% BͲHaM3%!t.h%'IDmd(gl!s攼(vݚidA$5>vR@&Dθ LPӃ464L%iz"y5%GxgF퉣;(T4]"@[[B ,X蠈KZ]ף}j?v֖[&hTЇYZv9YЂ,~+)R SZp3wBoYRʧy'A1-wE`;l~aj2Mutm궈jR2&:%5F-Lm`(hY8Drm}@:)e긲0Qډ~zYBBy.^. 5iʂgz}H> 5[Dio [τ)4 ӄL=w,H;#$7]k}m:,Xhg1[v˼q)HWv ԠSP^u*1 B%|؃oK`"x>B.$KK $(>_V|T8sL @VDqHLO<(H˂ŽV^`EHrA#2wrc>e|owely-Lʙ2o昡ANHH  :} s،# 3;R eH] h΁> c$CmD&nܢ QN_ L`"Ѐ!r55,XPP,sYNQa2w֓g;EQma!z7?A/VG%NxN㡌3} `X) XOݠP A̤8rT&h&9 -iV@S \h?6c~ p=1\^GX`h0h_L[g;E9qyW2o̝+T9`0̝"Ϩ0UnG1ZYvPc:Kj@Y\pﺜ+4[ 6\8ß ĹAA"-<54[̐A"a2‘.f`\X#rB5Z WE~cʁ;G8ysPYˎVosg׻En_a;.2oܹvt([0̝"Ϩ0Uތ0 Erycm#,((wP9 #T +, M(7K&MB' Zt4<T `̜䣘EE8 ;Bf= `s$hjcT{ BJ0K1zʞ??d#]zk#fE,淭{gn"pxyժ4O֪B3։#Znn@`H$btRUYY3 pEX7XB@2X "J< g, 2(dNRK]]nY53n|X";x́ﺌ"i| eG 27>R(4Ҩ)8 *TVwq?v>EŁ~L_+jsєiJk7m_.uꮽomċٷwkc٪?ʝ;yz _5o:R췘Q8.2wr9-X}3w|vop;J.]:f/Λ圸.4mZdexgtֳo_xԔAlzU/9p6A g>zpj :"0*9E9Uof@IB^ d3p{ 6|}(87_r\ fV[HXqBŕ0C24:D1X5)wE|Ph"It:;[KB.;#[.)cK;b8 UdةZΐg;E9˜EḼݗ9Sy[6lXlYM],kEmͪիG> ?IS Eac E;(ְʁp+a|  4x RcKb$D#@i8<['øOaJ" D (n|0h uc]r ,\oD-Iyq)IAo1бArBJЄ$peS&HIϿPZ"|ݷkj}6[K8zj wG CpV[\_V-[ZVJ yV4}:'sSMuh,!RywF?NM"Z)N<4T>D6)E<)Y`- ݷ_̙ʻآ@ z:R %Pr6;GFFa[ɦ݁?ϝׯ_ӫY茝ЬlFC`v#!H=%. K1{'@:W[%x >Z 5ᅼ~#W$)QR).8 Nۉ0`ߵ#yj HPG=pba Duu=-gdE 2l]U4#҃-U5#WYVj!-V2_ꍂ%?_CR=7băYy!:?a~r~`@1uK N]yǟKLI,s(ٙ)f~2g*=uZY$Lyr)jE"*Y#=-U-aJ0 BH? ѭk\u-Ik@ßF;\3 w?]  5x5byOO}9׫ˇCdEx?>_qewes_4w*%*{1Jd.JQ,0G~ ě <հP=PgRI{˺gxZx/A'o' <- M~㷐IIo q NdM͙(g;E9˜qy̙ʻGa$e? g(s<&p$QeDw50;gQ `}xo$0e<1`4A~Y->e@=Vln3(%\҇7x{`'x'g?ީ?_ I3uypX'<zj}YjWG.n:wwk'L ƫqk@((Co?Њ۱9c- YNQ2kQ8.reTif6=?NEص[+mx-I@yHkͳ1T6O6Vsݼ[# .Oz7զSRzѬ8;w&it>#͍- F r9-X˻yowe\y;oYNgT[*A I'bZW&DNKj[> YNQ2p\}/s.RjkTj9aGH(xtK%T =i* K hʯxӧ *A鷏^Яkޱ3:CMAuD 왫^3{d,H p":F.+QzrN lNAxxMc~Söi6`I5q+\ϔO}gOg;Eye̶O90ݗ9Sy̾S^QTxJ#6TJ!00bQ4Σ@̤0ŝ򪣷.VQX/|CXKa,P\#B0jIewJu]=8m>[,X;3ۙY`;.(̙ʻGryj6Sh|BE`Z@QW[NLL0Ңh n'$fWWW ("(ES~9t:T*Ҩd2Ao$h\vPc:Kj@S]sx&v sU~҆ s{s!_84(H$7zj!!!D"p8RFMɲ`qbcۛ ,X`j뫈DbfgfDA͑/maXE& %r&J!)FhwckYX&Fvh)H0C%x̜lh40ܬ&,X-ɋZ/q &&Y Wɷr'>?r20ńm]['(irKecЁ3'maJ, Ai| B,6%1JJTq{?NP@S \y.:F ?湌\. ]9 ^kV5pys eӡo- / y,s(Z]!0Hrʼńm^o]`$|b3#yAan|͟mYB X}Z(7dVTJRn5_?NO}iw*b1ƙ#J0C!G q6#Fpxar-!BUWbăCavPz}Ȼu  1OleI` H[Jp|_hyTpVo }:}]Z eR*~/W>n~wO~22g}ޯ)6RCO{q]|_n;r)m~עؿPcxu4W!X<)L 7oJ,LUhΈ@nL0%kWm17'aۉG, X W7M´aqU#ʛ0v6 3nG y"r2~=o!aiQ9Em@owӄ#{Ѽro˛!zh,S 3*f}7o7*ls櫶7*g[㋱0y=<,*4Lqyحъy[9KGH"`<~r15 GLRNV90 Xז:cYHX Zߋf7Ԗ]1uM1ZB$ױOJZ3s*0yi s25/+i[eAUnVBUh0K7Ǜ0-sڶ~Vi,l7lvDxϜ鴎dJxԔAhz.,}0 "a\3 .US+\8+~qMy}I_/U11dNu*--ᦽfnАK̉s<۾b^u!c:}}M͊8pf,4|wzC~(B+&N /E iSk2 Ӻ]rV~ɻi6|,D=5 ;3#NEǷ(xueMfbؑjLȈ UIS%L9a,J9d8muؑ&"iB*xG[1XUFczx2Lx}K2k kUmԧ4P_z/194} IK̗'-6VHLS.3?i،֌&%or+tom\xUKMesa,hZ*!cIP.@q R@L ^`0kIL̬@N_d2霹cbG=(v{o7L:+#*++U*DB7+fԜ飙Uzx9/z6S_|p:gV9\8BiUR 5osr৉ZuI{W; 1;5yK~dQX3!k2y%B9Gͪaa}ى3Hʻt+朼m u9Ut+cK?vPS=aFGu*ҖX /$ Gō>eqed볫>{rlSw8fY+Š[Bs/8pf€OLTJ$S)ǃr>xqEBx.c,YgˍkS*`>6!Ԗp{nŎlȂl*HÞ]M%F%I+&-CFĘ{?{# { (m9kW [̀$P;ci*`_7״唫4k5h2`u/X"n63{HExi+ffFy307/w ङ(.g91c ꭟ͗+?mJi$)O+lC4A[o%6ÖERQ[Yq}|dddYY-ܹ}4}d 8'Nq: ,pGoN >, lY֍4 ̜Ȼn\{$‹L#rs sZo L{nhҺ c^'y]>`h*.WB3c";jH]L}N`cLsI•M9 L3hPXC_5UK+bGNJ0n5. O R]Zx˥DE/k_JYƍ{ofژ S2dߓVAjI"e'O}󳐝ɸff3*lK9!C5Lk7gcmwYXw[iܯmVgYQ ̵6q\ N(QND/Fo}+S^{qz5l%OPehtV&iE[}[3,L3ZV$9G ҼE V'1 8%hpiD =ϽvpsSg0Ur6fN0ZVsOZbڂ:go;O^ϿzH} !yW%ʐ_V8^!n*H0soJފ~ >&X %dęKydQ`B +wݾ*þ(fH[[.TtkĢOJ_&d2G ^[l3x 8bou)Y1G]4ؐl)^ &j q=\; oe9=%.وc]˰NEá!6zVgʛ!/f^&1ƈk6^f<éG2S'7&V#́{7=6WMx.muE[QT<7Ƿ7~v^gmEk8?XD)SLEeQ27o"y^8~^٢rЁ̇QԡJeff=smu*R?ܯ{_ٲo[lB1V<.^Aԩ\7g6Z"?ȣk!j`,xwb p!Oysﻯs}̦6ٶ(eGw@ӟ/zdo~^<+ 5ܩ3Ǡ(o:_~_mn:,Ajs\nR]Cm&aeBY-H]dQ{ %]t^ß"zH[F͵i`k$0% DӠI86YiG7Q_+|˜ NV>hk-mZ؜M*.͓Y d`D`_z|Tg+&2oW5# {!s J~+HA|#f^^|ے Mu:4 (ea᯶:R[a`^XbļX>٢r۰P>Om#)R¦F:j_X~2$0IIOH=:FgmCܻkuiɔ2L[%Sd3f5R!oE*H&M zZ:=( ج͵-dpd'⬺ZʕkRñLdā(T4T6/lFC[-Y:Bo]k DĒ)iq ?.]Bt71oѣVXIZ_I^RΣuJ.Pohp];^CV~yWT*ڂ!=km6y橭2~ūNǽW3]tn˾Y4ZB:&Uu `ZV6!`X;B!@nt2d j`|?Ѭ*:P_$|F+f"3GsiG޾&{/1ܖ`a>vC]`Np>K z8:î4$JEԣz}5*+?7tUv@ݳ+wz0J!O3pΫKo-Oun!o'y;0~,K(  ,:)㩹]#@Yy~o֔# |*k]WJ_ #A:fyml'N\~W?zo{xR+1ߎ'7u1볱"O{@C)^2am]K9$h:&u;7K;Ov C%@[}Ǵ)G Y3Ƽld \{oK@9~I12&wiik뉩C۷Oa]N"kɞC}uе;7N0 v 7dn\@ <*h/oΩ s'$"w-_[4xC1 }qւxL3*^1ՉC} E;h}a[rf6g ~HS,,64Yd?XP^q+sh/Tumr޾z#çO uHn3ogYsyf۽g6#ڷSck s{|=FK Ṗ~:n¸ofdMI7OGosW7xZKYw5g /sw=.EA$KXEYUڳzܑ ya?-5kFP;;aɞOGlmviuyLH|e XHhvEAyRȨmyi$H(|uXB$ S^:͎z" ꩋ7}z~e x<30TG + Ξ?G"@.*n)c$O'F}!w{&&3%P|)@t lXXdBbfH @cNy_ }燡L`2y2f<fRxF{ 3((%q:?SMipBGceA sWXiw{,ΜZ̅^zDSZ!I  gbQ~B㨪lǿ' gy{ܢ$ڢO+ɹC$`(Q$oͻOVG5֖x')!8FzITwHH1o ' ~*ߧ`W}`J8P&> ,-F\SLO^ۼzuH5#FrL0$` yɬI>)g HߘmfQI{"NοCl@=?&MPVV嘵  \.<!]zeaa! |{vQ4?͉84\WCέ΂[g ːEJmHz%^[-5Ѐ@"]@x)"4C֙/ L.o0,K?jd&ݲ]@T8j IE@Wb7.n˫}?`ABrwgg>t,+Z/č~mʒ^rV3P+*?JDh@$I/z~EHzT@a jI T u̢דrTDI|"EM88eZ-1F™Kc~sw»_MKԹCwj{/*///Žۂ9U3}v z }'7|~ ~w'{yvM~9y'-c3ˀ/T.==3yBճw#~1[R"1o"Ӌ?&{O%wNt!bS{3M#13-X;E.!R?,XXrԓ;%%Prhy iksW/hVhK'xeJ v曅}pF;^_)_{9̛npCwܑOnE$)5םc[✟." .7^;EYD_Y.PUJ Q0-XT}ku;٭5@ 8B χ}meȤF=;F'cdUuẢ^iQuw-HǞ4/7nhczԪZGn)5peǜZ7gZ]N8]ogS5E Ϭmyb',QЀd %J4G~ĚJ}EeIfI f?+xVJ"!3P0/Fcb@ <=zR$`h$P٤nE#{1ʼ|j?,*zɓRFy;U`|l m6mQ7s;ȷm!è7¸m ;qH(L諲+=S|p&PȪ%y&$L2nJ ܱڎ3#XtVٲ.|۹L\ %.\?ܵ-_tjvKKneju9 egw|>_R Bu qRp`W}z oKX,8x.n=`50~k H"r᛫5c ,X`}Am {$_  D.ʔ檷 {9 ,X8̛o\/I^g`s82wrc -*\tv+G3gT[*7 ۿxqXdexgtVngxԔAko.-c qIŠx-av_6  0g`.)gӂe+vwXΕg0iYNgT[*Ӷ83ۀ"(ι;'H#L_/fPf8ۇn/uw5R? ݴ>gŋ[{㹰p:\t ?^],wwF7;pl>zJDODYSg]; M5_Wޅ@wcY`;.J-sPad;EQmaMb׮rНF=s^"0 jK[W ʹy..{3Pu|fDOp\ K_T]^9w݆nS"yĕ S:Bs}8\T^X>Yrf,=mץiw|U{ qySi(w/˜|Q_ ?Ȳ2FHf?w.==-wsFPQ"<0*ɮ"QOUuɄ ;=OOPy~7?!}_!0fuD9Cߛ:8 ʮ\s{_>g1>4룅z<>yخ[, w9,sYNQa2w B{P?SܧYu3!%Eώxޕ{|׶/<%q3zJ̗P &:NXً.}>Fp\e,sZ> K2g*qG=zjF9S v ?Oӵa]&rB5BnNC& qy;zSߖ /B uT?Pql 39L0YݩZLtΌⱤjc…-ԣp˾=:e$e~"#>Þ kz6VlMR. |Re+h sZ˳̝eN ֢p\}˜|ǵ(4> ʲWju\YpBRB~jqljzD2 WE $c9T9_{2H"QUɱS5}V; ?FG(8BϦ;<}𒅅2Z?Ƃ%8"/ѡdt@)`V .ܵb&F0_թ(_hғ7CōA:ަQVn,pr@YVlE4۠ڳ,s(gӂ(w~2g*B9Q_ׅe 4WO]S Ew]/KH5:"m\YpU=rQq8wK#Q}b=9 j9"dZ\&;ɉ0٭Ja2K%UA=_nx FxEB.$KK | IP0|>"Nοǽm˦}/ 9&wyxM%e[ٽ_W8tGpa!s^H-^'!UKw(ESgc}#^"ׯͮ6DT:{wQ~I W  nxs={Y^ |7sv76b.,1<$Ih YN:>6a G<)Y`}˻~eT2? H/VG%NxƗ"u} `X) X΀r``&ők°4)Ԗ{EtxCb߉l"ܗGfNXj5u#0X̌qPpL]2dO`k2mj~1 >n`?h-ڰdŨ YNQ2p\}/sGA("G܀@Hd_JL陊P o ;0M4 )݃pDzj;Ό4cѵ^]"KKږ̔WH9ܦ4Qy1oEL3jYNgX[!sQ#@V;}NmvMMIo^jdd3sqI&dݔAcYgFݱ} 7[\&,X `n;m}=ޛclzr85YC>8l\ *SqZmox(a9x2# N4HG1ς 'bcۛ=Ӗݒu+g$H0,(9go % zD" ʓ25Tz^,Xp.hpǙM|Gcp2ļOO}ŇA;[zCYzLL$Jy\nMMm}}}Q } `!|h\tFk^ĉWX0sbȐ!oT<[_\#9~E{uDjT%A0EK" (gEع0NW0g T9`B1w_i 6Ra:,sSo( `+vBaPU$ދNP3$`HpWl)s=TRD2$9Zrz2(G#kJ4|+1sA vuKԼi=ݎjN-_}}sWϜ1DvϩSΏ]>#|%4_tb-T`'C;&;s޺nk0oauXΕPmEQẗ3'<(fQDi`,t^{M1qDX|@8(5ڲU#{lr@.QBY//rss7-BXYV T]'l٥2__+[aPڔ8|l]eOf6'NoM_j~;JUnMsg;s9޻EܲwVe\{ eaoGacK`t- I\p PPu]l?f?`3gΐ7#=:u߹"3(PfEkTេu!XUBòʾ\ 4?tӻ|%.e?c´nw(GCSABB~y Q '#G^S&]Du}c- nYq{ ea ,KX*2^iuNxk wpM\wBKDFHUB*U^uՅ:j ZnGXGݸA!ɭ]! 0J}?6{~~=CL %@ JڧE">99٪4?2'ޡgϞrwU&'szlm)B09+ˁP6,u,vh5{.%V=&^2 nZzL)dtQB`Q'[W7A`'[ZnxG}J-N+d_rV v日eo^дĩk?'Ung3nJ/wda/v[O&P&_b 3Ҡ+* 8ǭbN"d)- OIs͛SK'nq`^MN;jʪݡ3eil*Y5zsa=j, gT;~x%9[-o pzcnaMF vm;yktW@[#߲2YaAal_Ǐb8Bt>dR]1@ Pfp"m`sRHOz{)s:ɔ\: W'08b AY4ڐDa\N3yp}zwq6CPXҀת[;ZD-Jj:|g_+c<< Oi"iGov5eUH\ImTK2-TN`*t;ֽ2<ѫ,Aa1X2"!a\Ncٸ& ,OiEQEEڱADDĦm0t KkK**$r4j.ئ/ftvY^^q|4~7咬8ZUy5\-D暒rม{|jst " ^Faz쀋3nwYR׹]ۮ[hosG/&ߩ9%Rlw">ow#ۈĩRr/8EveCX |RWS-%]߻@׀|5ݑQ'ztNef\hM]ʎ s2%@8 àXmWhv8 0B.{>0nZ4>=/lZg3ȓ 0'uޅ/l[WG!qOLҙքauY AUI#R~"3'j8XU];AsҊ>zN@m`O_2Ggq?f#RqyEL߽ղ(x?gkktz_%''nx-C ~ɱÂoE J?y4ԩgxb.r^\oM"[Ԕrfm6}5;׋W?>g莯KmXodzxzUH=Fei8P{<''gѭ? ysEnas?Ȧ]6,e61~C9P5C풓99?͸6҅5b|^cy-$'k߲5Gհ'AaXRE y?_}RS*ֶؒI ;,=.fv#A|O.±8d>eK , zJFHHMwxBPvH$ IŊ|RJ `*ݼ(aLOkF[w :)4ⅬN1}f®cbgJ8j? ~cO,*1,<ܭ,m,ޗcЇ Vm\:A{TNܻw]{9c4,Dw؋7r{ˇG. m!VP}FĮ֙C 0|;VkUB8$ˡ .bd)Iwʎ6T ^mO]kz7`ev+vWRڥ'F{`p~r 2fFp/Q5PUdT;";kҝe?'U U羯ڞqevwRy>-n鷪::gd٨<[C;2q\^iYy6tѪ_>tcI3&AXzBԱS\kMY Uinm0 @)BOx͚z3SPUɫWT]ibFWwCNsjԻi^U fB0MG0b'z!z:dkQiJK$, z3 anғW>;>8z)pw[%rALjՙPnQtDN. Nu<l ߥꯢcױ[M9+?ݜ`p1瓂-B $qQ~0 !WSo_0SG섨gۆxB QCt.8yWq=g{9|Jvg^:]<`V\]~Ry-؍cuVƆǟbޟj(]'&2ٚ%H]${pDὃ_+QKO$ȯ8f] [}) .ذct' PrFHV=!PC!k["q!{q>U RY[Ǫ::b:\ShZKC@|MԹjmT!+L9Ylc4n55XP@GsM^ꪵ֣TkQUS7JF#E#`%34]5k c#;F}g׎C(CRU^EAȸv.ϞE_6fDžn_:pp?] [h}u%|%!j vs3'uN4 qB7,P<꺕ovR ,bt00/v1}  J'ǎQWP*(`B"1~05hv컼m dJ(_G w!оέ9A/'p[pB{%n^o}/wauүjnXh[nkwuO߬P?fUWM bGSRj(oI'$# WvguHЫjkrjWUGs0+֊ sHkGB8"!Halb@lʕ g8ɄN8Eo:Eq[E8A|F3R$ُ\2wfGQ#z;|]J>^)|Pή˓'V&V}^YBߡBKۿr$ݵ7nϺXbun[Ƣ{f24*lRoғڷ&B;Uk (*j+lp.;AFaùI[E9h\;qB)~SӂۚT9>QFYO1#m#V0 ;21s (VvbU|uƩ^ֱ,'2>rR/]~^j#{|dk-{7L?ncwd± ]}"*ɑR51ǢP+o[.Ztm]4|CS NL+^Cիf6lM[;f^`dUCt(pլ'2@'"-?:#ׯl!-mYxw-N"DH" ";Yl.*nRzS304\LԞ&eBߊ N=If|BQ[9'ɪ!5d+0 [ n޼ѣp,.*0M*6<Pb)̑<USOۮfz5;bed!B|!,HErĘH+_͡HoRn3)75-Ks'NZCowYjwf23XkÁH,.J f#"+Eyl6c090J12rfɾs ]aC.ڋ$mhs {d% $L~o 5cƌٱd2'"##>a_s6 (h=L*uP ,z'Npc '!Ř(zwS=iMMsBr캧oVO(CalCpLZLbZRiB. uqB{s-))cUD"gn-(UcJJRH0p#7L<"+ ǀ4h.FTkDdDx'Ss~iq!K2'6R8SDΩ2qVLIа͋f&orOVLiQڱBcM7<|Dž:"1b?;byg(#l/Fwi;12FրZ}7ijޓ+4\,-K̮{f23g6ae%EԿFP2Y".L^VGi|_deTRY%~JS愘CRpP:¨@ `do5;ߍ3F \|(=ʬC}Qip8Q Aa"{MZ#0CYVW#/=?0oCzߥ߯2}h|-[bܑڳ24OKxH**s΁uo;7w΍?أ*}~tKZIKb<gCmV77#<{O͛QMtx9Vj E{`i|Sr:֘ꞾY= c KwШ@x5/Yk$jLzJ^`St 1"P]jʧ֭[S[IWrT!*W=w4,yϚb&,Q\quG?K.lT-tP<NxH%7oy.0~$G{Ws/1qTpcfN!cW UNG!vO(zbI*$^ۯ}}f_(Ck=0WyUfXu/\YW_+;ROܲ7Yo̊O̧lxRTjƍ\iў,1siG6,KNu i-\9-31vM L w)(}A쾔eHy22"PuUA]Qq(}F#9{t<ݺ&Q0v6oN-i+y%6e;>ܪ)vHZ0f CˌOWFx }S_׳{g?nqY-xͅd8=|v10[A^;ϣ?D5V3׫{lNy)Wd'VMI]~!ک_ ;0lI &ݎ[Zn"MYr#awsyI3ڱ:|lHyT~,{Fs %66]2-@/~j@jBԿu޿{@#5^ƎoP47XhYoV BiNodWmܠWWizyAF[ 6<̉zUP]1@ Pfp"m`sRHOz{)s:ɔ\: (dqà,TmHh0^tW]DB Nm!`A?EKǾX-_poN]0 1ºa7A4pNмŢpO}<ϊmS}QPiQg<KZuky>TȐEUQ{J +7B ?T-Pxz&:IĂSpOg):E Gw괝oͫ5!<-ʜPh":4z*M'}_XS72ﵣ(!-PV-4T:559%Đn׬`@5Uܨ:SX=Ct?1XܠpmͫK痡R=8z4d`' q ڨ"d]Kjr U7S@"QޕNvvP1٥e-_~HǏ^J;DSAXIN)cW|W|}#G:EѼ@v|Bi$$rp*#7;ϚVP`*t{ֽ2<ѫ,Aa1X2"!a\N{eٸ& ,OiEQEEڱADDĦm0t KMDI84hL\o_hWd1nΫyԻquSrIV_[J3 V:ѧ4_eij D`hj- }ԥw]1=(FRT5饟$zS=GA U Aic>ݍ>7}qC@Dh'L-gNz>J`$߆,8g~.Y!fH=? RC:k53{kU )qt$ ]NZC@kf!3y'WΘEsb)D*HRGc6 {ǎ˾ژ:UJ/s'={W³i`ڢwrx0x v|7oz\-,=@3'؛:\{/;0J݆DtNu]VK1a.Q$q .G!Fw-Qf?~c٧GMk,sy^wλ-v( NSzY|IImf(oxv؄Zponx\}ZIAUI#R~"3'j8X[3I+8 j=mV~u{vPJi?1~SWˢIztbЋ-u}ܮѭ鷙VINN,[|cߊ+!h~hRS==\弸YCH]Pijoy96lGrBˏ.3cmoѫ,k)Iuҥ |48j-Kd *?~ߓ8Դ7A6ww-l0{@لM>.~vǣg{ذB}jTK=a\Lg+vSϸUJn_B@JBк$ yr-S+VH#PbO(CuLS9Ƞ. l#0 e񄠈..[(H4c3H19R)UyQ"]aq :)4ⅬN1}f®cbgғY8>1Ɵ?U?fooI.84e#X=htˢa႕o,mo^9 Ӌ!IK˳\sNw {x Ұe8~#|xPpb `ߛ ߟgD`Q8t czg3a0xU"@IOu$qGQd9E,xs"%^)|Pή˓'V&vS}ҁW +sKV 5ͨEaq[.Zۻe,oVO(C?H(֤}QJhljK+EA\40(V`U|uƩ^ֱ,'2>rR/]~^j#{|dk-{7L?ncwd±<دg^_i[x5sL([斋9ݻ%-w[+P9E```h~Z@ъ?].(hò6[K_gQ葓rq@lZF'oߒYg.c\ۥ{.Քq_byZD][G gxo}-PsB\N5MISʧ>i>u[f71 .M-l!XW I-M& S3|D?Nvz;r۠|TN<[$~v%p E cbun˙1oVO(Cz}C.&e:Uf15ER3t6I%VIH}|Q Za4j ,TyYI>L,Λ!+Ur3*joEA ͞rIA[^WgHkgܦ d BAF```@Я_Vv";Yl.*nRzS304\kO=MDsSˤƿM^d Ůt$y$ib̑<Uu?]o Q`"C"VBȔ/$"% H\N3P/9YRg=\&%63fw/vC&oju4_CݲzTNc|bˁYhIږ0@J@FI|0PS pJz'!䤂 ˄$މQGY_$zӴwb-k^s#CɌm:f=E BwS{ьV],S2'(|+FZ#|%{މà| (\0 &, +fӅx/vH?? T>=Uv$oG穭yG$V1#~6ěkf.m{C~9ԼդO+^ oS3ֺ6E`?ImQPIAҕs7 i0x cD$(.QjB+h&Y"]5aO3->)0maAx[;=AR#g߼B5 0=8xF0@~H>>Vַߐ!ɇ "dEV*ro?3f=1ÿ:xfW?.D?ș(P?%i&* bˮ}u+X(B09})ˁP6CNr}w=~W/KN7v-_j&2:vŨ@QtB`Q'[MR!r.NiчW`6֣|іRfXu/K "Wn׊luFtT ԵOʂXşq#WZ{' {svz :byp]C~b Wz@ L]wv5St & on_/%O8rj'[ Vfzb(]3IR@ @@0 mZ5vEDs,.BbmtD=gۼ9t\۔?Zso:#Yvhɦ˜U3T0!R׳{g?nqY-䖷 ͅd8=|v10[&C^;ϣ?D5V3׫{lNy)Wd'V>^[W?C湵Sv`V[mۓ'M`D(F>ZrfNcagaOqiI1Ԛ`(jYTD Yd1pXi\Ӣ qEa A44k^קwwmN8T> ų{w2zO': "._b}rpP_u?v.ȝ_,B!ROCtxιۆ:e9/棠Ңp!d@3O5QgoD,8ys롑-ivWxc, &H3(Jmrs$@ C(Q @@֥>tjc(FfڡBmc$"dBb~MK!O?z)Ma%9]峭;_y7/"Hi2X8O(3c e([4CIyڑgn+(of0GBK]_A^ggϿFkae _۰,0.JltX\H'Ŵ~]O" ""bӶ@ĊJKYތ˥$r4\m⽋ūC'HN mQ: M^TN]* Jz' K@ӃX `!%@X|P]q0ݑ pwwwceMs9pЁ=b>yq9:IFj/SK#0=vvřS^,Bg }=iأHd2 ǹ<sA 1{?GQl2hR׹]ۮ[˯PHQ;z:}`WiSZq|{LE^ɕ3ortQ휘4GB R"ј ޱ76bb]*gܹbמ=QA6L{gϯmc da0!|wrۙ*ΘFiv#A <D>A̭[&!t);&%ʔ@8 àXmWhv8 0B.{>8bP4>=/lZg3ȓ 0'uޅ/l[WG!qOLj5Squrqc.VФ@UI#R~"3'j8X[3CI+89=mV~u{vPJi?1~SWˢIztbЋ-u}ܮѭ鎛VINN,[|cߊ+!h~hRS==\弸YCH]Pijoy96lGrq'ߑڅGiex[NfF:ԿYN0O8Ȧ:K"DBjj 5;XO*@  0Ree ,z]0FW p´NG)r !᫼x!>{pLٹkؙڏB'fO}bhpn`Y< >\n;ұ \+'aUbz1X:$iiyxݻX.={ısDLob\B Bt{ss_]3# N~aL,w&Q}3*~A n8(A "F9tnO{ι xv݊{{v="vln~lډ\-D Q0~ ֓qlLw+ S][wep1瓂-B $qQ~0 !WוAo_0SG섨gۆxB QCݪWmƽs9{T{cAlx:HZ>pqtIc7&IZ>yXwDЀTC${pDὃ_+QKO$ȯ8f] [}) .ذct' P@mc.9bTQ«aVe````h4/BzWu`q?I6uN4 qB7,P<@J7;)pNr1`UJA>OMik`Y%cѨoFY(c0c$&#l{ akА)n7wy ȔP|#nC}ŝ[s-^N<hiqk>DH4!Mk3c 3֣qఏܳ`'#?2;1$4fEZdiH(`2QPr9"@ B mI[ 'Mó|wwV/>- 2A'1PgI$۝d̎ G,vvu%|ӽ8qSB(~š]'OLaw,ڲ3nn 0d1CSD>\)s򤉺Q4-I#A>3.5b FF<a(qwd@#c$P¢`UQMu,Kx){K^%}K{; f:xqplC, x^<;gIp0ߍܢ Ȋ0ҐGL̠Szс߄ٯģw-N?%-|%Q_!\6+viΑUG=֧M&}:Xi]o(,:lG3vn1Ä.v~KzJ^`St 1"P]FBۊ@5Xt/̈́`>K^&l}iF=%lØM㭟oIT"}£ځk{<* Z;@o]"[?vLZY.~C'~0 A#0C5 ?FaF, nZ/ڃݙ(P?%rp ʳX+kʨ>00 LNw_r ,(++&)A\]fϥdߪ ?RS]Wᅳ;]1*PT9>5?l֕ʬN!-8| uza4_N#ȕub3,-~c N<1 DIYP_3nJ/wda/v[X\d+3@MY`````hNhQX´' }D @`'Ik' ZK3zĂmZ5vED9nFѫ{t<ݺ&Q0v6oN-i+y%6e;>ܪ)vHZ0f msB5D8YOXKVQc0gT;~x%9[- pzcna Ƚv9ޟG#vm;ykfW@[#ZrfNcagauOÿ:L{jFR]1@ Pfp"m`sRHO&taP 6$4рw ,yz\]EM;P,QG˨Q<ힰ$\S*T yBe,}y翮]A!ROCtxιۆ:e9/棠ҢSts=XE ͸CQ HP '#?6]lS^ @6P:VLqIV.ɥKWVqp6336F"BLviY(/w{I?$]G/ $Ⱬ|>:|g_+c<+Ij)\Jy춂fs,Tqq+^m`Zż ?? t+ML7QŅdyRL+jon/׵.ҎǜJDDĦm0t KMDI84h\÷l Y^bv1֩@c{4E{Bul((hWl.eDLJ`%,:hGA U Ai/ www7V&|9 #WgT`24cg\9už"lkmb!+GR\qH_vm:zfo- B!E"nD=>4өTkh-،8D|=b"/7v9vNL#Ł!Q)tQhD`qzZ>aNo3I|1kNj va3W[ [!fHf`````u]ݩ9MO. H lx Xy%|nFGRvLK)5p@AڮRqBa\p|p(h76}{d_ش2g:'A~aNx _NbzʏB89בtFkqyfŋ?ӎ47Ǡ`t/~_̉8>E9(p`au̱AsҊ>zN@m`O_2GgPJi?1~SWˢIztbЋ-u}ܮѭ국&''nx-C ~ɱÂoE J?y4ԩgxb.r^\,R!ǀ֮b45}w|̀y_MN# A}Z-0(~wcg,y.Vtvk8d>eK , z"z6 DQ' N2͋x Q.@\-FAiVϏSCWyB]}`>sa13% Ox'v|bY55c-io14 8ރFY,z_؎A.XIrn.*1,)(@"R@p)aHWg+9  q:99zDN]~6m?7,u=MO;H;KeGjA{{~0߯3~ f3/._Aj􁋣O*w1qN3AS ؾĄ_&#0{'ͤ?ЫpI}y?{tVuxRJl]w-K;>þk*_3M^s^572}\S˄|qK}w,S,sRClywpfy;s{f8Ϝ3y ɋr_wBB+)R,˜߿_'wV~yck=aO̶VQ;yluӮثP jMy\"e҄#Q .*I=4b$IhjwvDLb&YVIw'TO-jlTy8F444FֽCw29⪏w~w)i ,2qϵJC,*= Z4Ǜ=_°=hθAS-:ތ`nVef;'3' @sW\\!SkY"K`4rےP7A 5js=LFV(X>G8?\}TS gNo]쵱Q&(Mߑn.ֺٛbL1#OW|(yW1BD4A*z$3vL J6ej59k?aPpK4WBrE;i`(ADA>`ʱ֙_<*ww=4v~7t`>ĩb~Ůy|nf%fn{1+3G,˭yg- Фff;ќ'?(A'uAETYc\Ƿkf-j7w UHd-wƃs)WO=󥂝 (I^t%06d?sEW+e~m 4(< .)f=Yc՛սӾ{ Z5f<;⥫;N{f O+RI;p<1B%>34bI=o%|UN/P)9(7K}Z*Α%AM-j-:2)(&Je_s7G`X^T 2m1z5`B$hl0+8[`΍ʱrooO嵾r[UFPn{Z럤Wz<NaS]q?3 o\~>#Dd|}i@z.==J=%/xl Ӻ{ +'UWU"Nl`) W~+=eն H"fش95f/ltߥ{ܥuMMTM~so5k_'[va嘝ћTս#o%iALm'"sfO[1yfiR;@P^{P^絾r[ѡ#(ER&zlmx<+ `Xd` :xq&$0+Fhh(Ap|T^zH =7߽%9}N }6IUi#wē*cS VKK_Bs˼H qc4-yQm!{m5cgLӫkR/nܐ;gNpG>W'J;~vLГwlQO.UE=(sPn~-oc&2F5}r;v ݏm$:i{q?r}c a;h[_T3MwD(A>ro3yO XΙ~;&FfOhՖ,@:y^{-<tBs=+OJ e@tō"6Gk{Nȇ "}w6>cO2dlҾkt7'bD6qE_-GDQA SDyOV8@HA]2}TݸqYf))"l19[A W%/")i-bMx]j[[616'ۺ:๫ւ|ҽlO?hO\~ϖcg Y^{9PRU^Zx6T,7UFnX/ 3]<+<~L{%c|y4t䜘)W)mћNd_SF,uyoۨV$7fwu4҇s$ 3?lDѩų|ncW*qvP_̞Nc\jGzbpŐsE|=}hGIDUyNX 7ht"Nc" -0|O>R+BU>"VgA*QtR.թu'I`pͺK[\90H}~|eS+{ZXU_6KHul唹·/讪,.Ls*TlꔔK{wו1zۃr8f1;7R&;D (ƾrs=g*h߾%[ bpe]Xdy°[;G\s=ĥ]bT:KqB$|s>}׿cU5{GbKZԚ޼U aNm>aZr(W~| {aP٥X?=jH%\KRaDq w\2(! A2Ӄݟn&f1{ UŻ40qt)Ww,gHEOT8sr!(l" 8w~*" @`;H QCz!0zĆ0LLy)$(6VƦ^80xʌ/@+VpZFQ-SBK.6 jfOjcx3;; 58 i%Sf|>v^Z)PN穭۷S(}C=u r<)=C AG楲z5%RՈD0U8wo- K;cVԛ.6.#ӔlW~E+{:>P"6e~ܹγX^:~uYABzRDڢ2={n}zG+6etOݠjv'/j;NԲ/0:›(:= \J(%1WR 3{x.<ãjaH6hl;qn 9)9zܿ|I]? @ܫ#?5wd(yk?7{yU9GM&q[jole!(v--E999.G&??':Ǻu0jM4V"GK-sɍW|9~+FO{oY Oظ16N*{xi[fV"[}fTŃpc֤f46p8 \)=MH|B_gm@DQ{Pn,Qq厫R{G(ƀ r5TTTPDFC8G ,{e)4c>33}oVt7B[vӦ6q8쯀ۃ8dފe?AM-j-:j1l<;ۏ pXDT$}I ݺY;'W_|蚁~ugX؍-0FQ{Pn,0FQ{q[jc0(-wf ΀"Xg9q5e@8 ,A({fv%3Ƞ 5` %"#qFPU)0Drzw ·!5|g&ϸRC'*-?3彯zl]N{ϧ.唁qN[~eVy57݊_4K K/>6ڛ(9NK&r>vz[ 6gTս#o%iA[- ɍ3`LhbAp|8*"#, Q3hxvx@Cl)2 ř$p׸qĹ{/-AɭݨO_5|qÝNwGjmwV3vz֛q=& }pCf&Gw 5čw~ӴEͶB߉XvcݓB,U5`k({D5 DBRt CE|Ŏ{U"`00Bk`*e5hEj)}а /%?{ٮ̠קvNgd_gEIVe@tō"6Gk{Nȇm뀨^ xD!Cd]6NRh<(0e-h πE!d̬`W ں>u+<a ݽ{I^!moSwLcg Y^{9P‡NE.^\'rT᪇QXӴ#Op|.D{~@=6f)g)xV|J*ʴ^r;ƷF`i$uO>:zS۩Tkʈ.mՊ6%/@H03_YOfn\4ᣏ$BZcG9S,8lm;'*r̽ ?*N\vpї2?};^+ Ō5wD'5.V=D I}DL[aKWolStܸR(:t)Ժ2j׬?OsZJ*W.^=2-1Ue󱄴]&^N|"BN=BǦNI gI &)\ƃ"GCP+Ő.r4#AD0- ugzilLDAct&OIP*!&h3~o,V=N=_>ݒILHH9)ڣf[G\ /Zl{};tiƇwkB =(48]ᮟKE^ $!H~zPҲ3-ߤ,>2f~zoc;:%P8gūOd뻇WO^'HDĹl9k{fqQ#tY s"0$buk*F#bխg`A$hT#3 SN!sUt?;&OeyT׶~R͹;-ZTe_ b_}"KSQ 8C3c4H.~_rN&hfXAR;C{ȸ_-oWeaw>iic+W-Uh|4jJ߼]߷[C[s}z'`ʂ̈́Ww6/ҷg:8c՚:O QzNT ӻ((ؕk7ntb;Ŏ[Â\E\0&DSw;B.BGX۸Ďz~Ê|Vk@%|WP)ܥ F7BtNN"hqۏV?Q(oߛccG O,}3jb*3E~x|}ڸa7ˈǜBxw}~Di֙و7}ARG!/S7膭' =*/5ȻF 'Ϩ6ΐ:lfwr\SP^cA0W4tT(:bYSm?]0pZ:4?tsB>`}p1bs e9Φݺe71QJ%萸3! /eJ@ $.a:'ǿe~K|zˌYK޼6>q7MyFABƤ~18uW̸g-OziY)d-z1o* 4wvHչKHWeYhѐc*\d^æ!jAc"/%1ɋ/MP(8o;AÓGTFO˼^W ɀ/}s}@q/7G{!YsKؽnx`$;%IrGb%b߲*:|$DSɋ^4qLwGRF LbC5PҢ~+MG[8N0UkÕO[sM^)`K+!|{{Fͧe!/h5&҈> M̨_ y̚1LƦ$+n>+YOSzĴ+6w0alH-FE4C0HTJERık5Db,p)?G?/afU< o0Og="Tl(8g2=qJD+9w%q.ެ.28E(F(NA#W-i uC]3Я>wϴ1nKԘ4PΘnJPeUBN)TqY :\Ŕ/YvZOΒGMpرM^Si߽xNE՝n->If=DYdw,biEKOa$'Uiȳ#^:3Ӛ4e?+'TmX^FtVt)3fBscq–v &aD*ŗqkɉ0"hM aN:MC`pIhJ@:ר.9(={jNR{Zέ1N1FQ!1 hB:Ի췷]Z˜!E1Gt1 R#9Fr4komP7sAaZ!`"9@"bdp9Υso } :O8=iϥȯ)S2Q4G4A8¹(fA8 `QkJDF,`U~ )],<+BH#_3yuJ{{y+Ss+nʼn–]X9fgt+NW|a.3.4nhol{;-`y$o3lڜQ]TS1nMȜٓ`G(Xb@8N9>2Bs {,&xGc|DP@[ Aq&)= /# el;*v/tBtNwGjmwV3vz֛q=& [ށ7MӒ5 }'>w`فώ z&w|3;[{ڦ [#D|/݀hQ H\H!2RqqW(a`T.H] jЊSV N}%mq7}w6>cv:#s=+O7H.+n8\sB>tmpGXDZk'2/D "ڰ9v @Dc v_M!ұH¢<6Q[wGTT=~``i0J1$v'y,E B O1Ǝ+Sd-{MX>@ :]غxrQKFNGa׶Nӎ\>!I*\IS]D:rNLیsvp?HųSPKpG?Wu{15ZK#mћNd_SF,uyoۨV$7fwu4wO.xBȄtz⡊MO^rY+b-yn+#.Zd+lF q89kg!/ZKoˆ_|T0nIğPud]i&/9޸"]>fn\4ᣏ$BZcG9S,8lm;'*r̽ ?*N\p! x iգ+ߎQJMNT L$F>!{&?EǍk+ CrN+fI`pͺK[\90H}~|eS+{ZXU_6KHul唹·/讪,.Ls*TlꔔKpaWh8|?2\)|XtYH "iQªa& >Dʃ$L6K<QUBLfފY(zkzP˿}?+%5<~xc1RsV7"uSou˯<JRC^;GP٥XjLީͧ;LKngj-wkB =(48]ᮟKE^ $!H~zPҲ3-ߤ,>2f~zocs:%P8gūOd뻇WO^'HDĹl9k{h`G}ȣ(G>2x!89D`HĠ DUn== Q"SaJ0;Z\2WYEL|lofggC1.+ǤGƾ~sNbBpeW\4(B!cˆq1|s`tJ$? C9]ԋ]MW43\ r}T䡽sNdwϷO6g=4<<-ml{eJ #%@MW)vxGT-A1N4FT" NB*r* Je$YV"^|\O02͛7O)3 ;t`y$qzLLMxw{f|yû3 2]x_b3ě A͇ B>MGM]%ի)J\`qZ;CUl&zT}/={M֤yjs ?/2l ޝGq.N4A(ؑukXp Ɛ;Bw;B.BGX۸Ďz~Ê|Vk@%|WA]*bNps.D䔺q-"OhZ_Iiu]ϋr)<0Q` |'?`QU)Bݹ)yG y ;EP>o4lě>| r)ʣYxtVPmY]# ws[֓gTT fM}'.M!55t9 sGC IBܫ#?5wH:}@cvl|p%t,!\acYa6inM9GRt :$. zdDG4KG<a| G7H>.g^+Xf\yoG<5Xlʣ0"J2&%`~+qSDQ澛F"N]*DunG-K*E[TQŴ"*6ůfϞګL8d$/7E*oC3 O^QmF=y/{]-$Np=v&X! bNu BRܑX츊Qx)r"*!0=s4MQ~q8Pf}=7=G*~}6μL=yoG)EG' gk36QiYH.es:>Z={4⭶&H-G>3ׂv&}L5Ӈ'IJmBsE&Lbj|C-Zp6qdbTI>sD%T$EkPӈA$ƒ '3}offY%ßP?-{ef`zfVrJ\Y]+dp` 1Qb QFv[8蕱f`'֟i3vc ̘@(8'⮉! 匉UV%dM5眵0{ϥ{{+!^L9pNGmqŠ-=[ת4[/]ioQ`=cb,;UVZת4[/EiznB *ͶJ,qy_:q\ Z:XQE8ER$-8ILˆT/֒aD0њ -¸yMC`pIhJ@:ר.9(={jNR{Zέ5=:Wfbc !1ڕe*la,҈9XHŐ*p1:Y{kqaZ!`"9@"bdp9Υso A :O8=iϥȯ)S2Q4G4Q+FH(EqK(3hkU)0Drzw ·!5|g&ϸRC'*-?3彯.[va嘝ћZs a/ltߥ{ܥuMMTM~soUs=?_-RIH1:~Jܴ '7Rdάn ;`(cӂpUEGXfc6;c$ڇ*"Rd3 NI| Q(cS}/ث/nY8_m<@]1d࿭f^7{▙{zuM %2cg~;{A*TYS?ws옠'or7㱵m!8‰(^8j@C;e PucpU5xq \ ՠ1̄>c;\^1{0s=+O7H.+n8\sB>tm{+? .UUs ߵCB Mwm؜Nr;Ip”ˢ)s?B:IX1]&8jNH⃊ 1/mF0޽$/ŶAh7;&رtje (C [/C.9*yp(iڑ'DT" 9'm9Xh`;bYY)A%w+:zɽ_!yӿuZԿ^ȧOnv$5eRjEҿoycfwYOh}q  $LHw')_")֋*-yn+#.Zd+l홭F q89kg!/ZKoˆ_|w+K _O\Rq.4YW8K7H~۽/M_/QN+= N-l[!6s]#{vzt寚5 )҉$@g2$^q K^<6~L&ol_BxuKuj]5K{k]ʧ9MI-%eE+C/{Zޖª*QXBڮc/]w>|AwUeWv!mdScS\x^W`G( %!J1âbDHQ,LV6!HT$ >FgXyjb61=תG֩g[R397/8^Y>y"qo yQql_#tY s"0$buV0n<{{"Dy0ÔPa1 vS"d)c?θc]WIEe}? )ܝ-@=d^13.>8o)NCbiOҢg<| i᯳/x*.VTN*ށ9'2~WUYXl>yz;{h5 ݇JMqE.1 'WOurJϸߧG~W4F| w(0BL>?`ͨ`P»S/&J|FCWKIVڶG_l?w؎ I@{x.HR%eOM~tRjD_@\x}p%t,!\acYa6inM9GRt :$. zdDG4KG<a| G!<|[]':W̘ŋt{k3=~#yjؔGaD8.dLS'փFn;:wv׹j>ӮYwV/6 +yz7c?*6ů ݵW B[CFGa @+>yS6m.0NfDaѓ2/B2K_i\j85놷 qHXS]!w$V"v-k$ R)EUB`z /h8&QLp#&f1ZU{(iozZ&T#ǭly'{Q=c;BOBw)K=;vCw I^=^VGȖvUk];Y>A$s6! Q8cӮثP jMy\"e҄#Q .*I=4b$IhjwvDLb&,yesLl@2J]ry7s NA"f9@,"SnK@26xC 1Y{b6c7mccD9EfvSCM J6ej59k?aPpK4WBrE;QlllSDVXg&wuh)GV "ώxN _ƖCL<5;&&"{SeiK+Zz %9!xNEYğ֬7mmY*{WyڱV2l+DF(0^qHwPjo$5$IḐPAi%%24^p^Vl16&Er8Qr[[|:YI[joQkԹ)f=5M||J]]iop8N1AaB,AAai N 9^w&yU^k&ܶVt(.ޢbs(R9#0,/*jTy{yڶ=_eB$hl0+8cg_ -unT^y&AywwUn[{K#K>AM-j-:2րD?vǩ?0`oq^Hed)ǧ BsUwO.xAfWoo83g\yXIp[va嘝ћ*r /wwiqF{%gyڗ[)g@1æEu[I?%nZVK[{_9 ,E̞\дGP^{P^絾r[ѡ#(ER(QEPlB n'I?8׋>N.sHdgOJY[G>:/78FνwY[,nSft,}v:!{m5cgLӫkRp۩7dΙÇ5Uon]?!n%/jN|ck/UE=(sPn~-w1 =dϮoX$HN0? EJjϯ\Cی9oǤ;1BjuhB_@%lpy}JktF (zV@ȿˀ8El/4]"5J:y.^{-B Mwm؜Nr;Q9(7 Dw~*" 8}{P``W qq*n7nh֬YpppJH&dLΖ{PDA}Um|PliǶAh7;&_?wEn9o(ڜGo:Ӛ6箚ڣbZY8Zs K?-cg Y^{9PRe^?2af"pĉ6fg)xV47B/`_K}-MK#$|uuS'YeהK]6I卙e?M\Czny_ӓW\pq֊Xj-yn+#.Z6ٓ&Λr̶9j2͎R{PaG11}lvvӧl7< .Vv4YW8K.*2;_tj5߅ogò˳Uy,p+?0 3}Q٥Xڿ`Nm>aZr;S"\KRaDq w\2(! A2Ӄs ]8gūOd뻇WO^'HDĹl9DwoM;Iq[jd# θQ4"VB趡C. BB3ˆ0LLy)$(6VƦ^80xʌ/@+V/pZF0Rp /Ҩ.R>P1b~>؏73n(d{;Y~gOK^ B9$C)1N.,P"XeHu0y *WȒ9qZ<߮"M6$-U鄑LSn鲼{}0GXEAMr@DQ{;rKlDa"Feq%''YfHتÇd)7A$GX Q'_JA=w7JT3Px e:)qV_NKBGa9-zc/5T+GY\\sʌ+q_k>Uqrl9U_p닥o^up~G]Mo.AB`5Ŵg!T!2@yj}6YjD"}Nr `!UIMoOhA!>9J =(sPn(orUn=Dy8NYT6tL;;; mOO?ۺ5\0.&goTV_OSԳ]Q?]@ B$2q瞛: c]{MXeG=)5uqf"hqۏVL fM}'.M!5O0wj_MfxӇ^A.%Yy2VOݠjvj+Ry%A5ܖ~pUZ.s='uah/JipXt[OL;s%7{yU9GM&q[jـyhڵtxV} s\&p C_ 4X􋟷-pPυ&7^ɛռk~fjęqɃ'cFo+!o'7*n:+ ff^JQ0D' s!#f#yI^]bqA}7agDޝ^Ը ׹j>Ӯut-K*E[T1G_JMy]{ 1ռmHUfE6 y'hxj30~j!BEAMrE~UnQFA-+0 Y5X./0 IfưDAU`|PI|շ#b6OfQ?.zf#2a ^hy̚1}@}xp=cs5Rz:w͉GTI~G}1gFpdQόxЕ46p8 \)=MH|B_gm@DQ{Pn,Qq厫RQw1F r5TTTPDFC8G ,{e)4c>33}oVßP?-ݴ nܤv8!+.k}w7Y嶵CYFPn{Zm3Fk6 Z-ӡҒז-gg.HJD/)7[!1AH8^+ENz@MQgZ&ZBP8H#.c5$Sp"Ca$X6ɕsݸ v>Y")(ip#\0OD m8v+x㍏??Éy ,+eÉ$w"Xg9q5e@8=,g# [Y")>&$`8WD138L >Z33ۗeM "Mr{رKL@/1Ɗ$7&DMԨ1KlHFz]ew3י9e9,7<iAv;h\G U}z=NWx~ G9:؞(K9NƳq_l?z=2Wd}?yҕ CkBuٞՀFY@ B R U9940΀wڑR0ȑ#e2 n;CA~^qm[8cs0j0)6%p.8̟|Rsy{=PGo4E|hΡmtBC/N{L2GĥwoJ>}`¢H㢽G.n}]߈[wQ5dz9JyA^dsV6E@ hA^(g;@k'f|䪨~jR [Eh1gLd]VGxѼjof9@ Bghqv (݈'k>|2!{%_ED+ 'Nqzs-tţ !'K2L-zv˭vl'oVF}wU/mjesQ:6؊+m*Ȗ]0b rx_0~+L-~q]v\ψ rqYb4OKgXW3gQcʓ(]' 0YÌܥkODoV˃17U%%OmÿEа o2/MnWۃ9dLCY{OT> HTw]4 yRxu$[*,0dA|oc;8%PqO Iw+Oҧ@WppzKOkBEq-EYʹl S)h:E#ЂE @6@sd,i΂a,X|K䄱xiÉR;h9UL2äL#`&8RZ٧ NؗpVZZҍ{!8P>WV?VyJi.Z_VV^kE4K#fh+q-iILX_ ]53\$%HOoGٮSuw?[v W ޢR{X(& Ę6OCUHw֥}ES={nȃZXoO.q5?R]ON^`ȼo݊m *9Fm2ssvC@ ͋]w3C U,a4 <)RSTiS %;!X^TahWWI9e~+r)؊;NԴIߜ곤g),J,0 -"G[]kR^"4\qXc6u1w F! QA-L>J2zOĠ‚̈́][zN%|AtXhhP,gN Μr@l;/PFn%LUSl"k!~vu{-CJnʥteb>?ܢ^-54_c-hbGjPW"* ҁ `wqa,UPl)86=r҃\xIWG؏8K<[Nʲ {[V,uJTovq={eOdrя#D6͸ooWa_~к_+Q~(<-ni?]q=N='DvE9$ OI7t͜\*#sRUpkP_8D;CRgXe68.eߞ='Ӊ a BKGCohݠ_l'NaWJZI_9~C̸?!{P7뙬c`w}XIv&; VBw?b_O1aW 4=I~Qg}G28o<34ʹe:nd|@ foy䀳!,.!?p1 ?g*E5RJ?*|´Qd,&"8 g z0`B9SszI7v9+r勒x1ˮ?6[3sqT"gQ?\ 63}_*ș]-EC1`DyKwQ |SvW{N̊T5,)"HNqڳ'r#4!y4ȥCqB.,rsE0r߁^3;sXʀJ30eġWGJ1vFxOcX~#K4I2 GY.wOUðns~Im3;[=7]v}`q='D^wq { kF\\maōgz#p\fh9@ ^|Xhy1'a^* b`NuD|\uJu.:L!\;h '1:d1("An񣅎YL`bCUUof޸q1S}%[nU- 1(ׇhj gm=aw?˟,Fg>\ͰݧF<\yQ>N}߈xgRuv8hD/JS͐4˒~>,'{ϗz}ſWyH.;Xgwc۟ش3o>=[y>"+us[bk'nニI|MOo١_=Z@ BKT&f<[rCkm9=|' 1,IC`d(r%aDCF~}xt#_{>nTT 'lq'=VhiK٭>e@wDu4p#zޜm[4y Vm|]2{o&%7PѺ:ϥQr@4Zhr>}(xl\퓙6aD-{.۳ݨwlN#CRћ~W&:P9ɧSb}p";~[6u >n]!"+wo`~硿OR j%CuW&㛡h.*l$+L^ڶ0H Y\a}g+sOM uj Wݢgj{O-_|ٷjF-0W0aQ~qVL#ھOoĭ(ϚqMpgj@44- @9;w@:@b `U#ZϿ&30\*a=W5eO=F) !l_2RM>,oJk f:#}<ķgwB%n0>8h9"U{?}?.I)=htB쵡s;d*@ Ek@[(p1pg =+^m;c'**n?t/H!F ̚j, wwcNׂuq+.SmKW+vltFsmۜ+XeJXEc H| itlKS ,mt$pg(-M>tѺ(Gr~RʫP}vUکaCm󎭘b{ΉަliE #VL '8^o`_K#1M:#AĈµzFT@'Z&cyZ>$0ĺrϝ={ Vm6.fOcEUh] {wsS6V ^D> 5 -lD.4h8㰺Ƕm>Խ?ũ{|Da)\ӥ>S2:pAdiYC itCy˥ =2'Hi_  Zy*M3B]2\WReAoٞs0OqE!QꈥbwpQFnA_l@4=-sZl0mgbf̕%$UDAL8P4gwNz]8`Z/%;~9RI^eɟ^?򭙹si*ɏˆ3bFt.,(|Qb־o[2-ǀ- ߙGоmNۑ_%91+SհxP"tnb";ũ aߥ L9oG. va-*0"WTj2xЫ#z% \|Z@ 3ӲO RXu$ d3)OWct>++ "cx KQDX/G ĆJ%+v73̲qeցc68J0EȽݪX+^pŞJ IQ±-gU=,_}2c~sޓӇ> -h=be;Wv>~TSG7"ޙT]*]4@4 xl HQ)*/U:n H6e BA E8tpZ(1h(A1S`A>:Œ^xK??ր,uWO&!x` 1Li!Q( ڂasXK{F4NA,bX(!)PmI[vi7l4_Zqa-+77o-SmٞMŎ. dv,BkH[fEl<B?"E.D!S,0!)RHZVSX{ R9r;g4sŭw-SkkUk3euXg h@gN+|(%>,` hE,PŌR9+T`.,&`iRmC @ "=|Ky9f C0l9cS08''AuHѱz-.x#Jv}}f1P< @  JRU09! 8s!,@ $`Y*| *G{8''!GİYy呍-~f9Lգvoʔ)qt{;-}BN;_IW"6/ kFX e]F Ѩ[X6 q,c?h0/Վ|X(``7 Sm:kaK\ (,GO0W_~ќC0DY׼nd_:;zO.|Dk1Oxh |"ӟڞ#_s.A~ᑔ>AރI'^:L"h L*Q<hKE?brZ/Uж1 9)hYS-_7v,_Z5nŜt*8m]e#" S熴M:S7 )emP83Jt>@Ԇ'Hc'^|uQU1HR еծCJ;uz:7>Znmݭ#gMmTec;ώ$<뉃,*ݮ҉ [Eh1Jq0+-HQhh34ʸ^; vZKm~Ăar>`S+m` ɏ SbV6r7 UQaqY\Ԇ;b;'z ){m(X1zvmztZimN(Äb4OK?#jϓOy'3.&; dܳǨ1m%6aQxbɣ\5{i7^!m eI8u߁2֐}+֭%!OcEUh] {wkЃ͙2ŝS<Ÿ^֎.2W]Cpx/ŋYv#ߚ;(<#xy`@gǀ- ߙGоKSs;\='fE|JX\VrML|8U!r#4!y4ȥCqB.,rsE0r߁^3;sXʀJ30eġWGJ1v@ Dӂ<9'yt,:EkYuWᔌ'1:d1("An񣅎YL`bCUUof޸q1S}%[nO޳U F45GkhĥOf `/{q:`r'G>5lΣǏqj#F;Kܶ븘@ <1E^J `ar B(H@.T %27 40F@b ,gW@ko0ѷr[ -}E*A E`T-e3SFH#pʂ`Ҟgl>c$!0JHr (C9djׇϩd9:קF^@4c:%Q }X#AQ>&oS퓙6aD-{.۳ݨwl@ Ίbw;0@ @4;L(0}͘Ƣz 3{)hlS7G6@  xf)?שk)mR 9ޫƳN57[W'g=7mq>Ƙk<@s3n7%u&uZCKCf]@!X$4& ґj͊<1BxD`I)lduou\)YiGr9ջby]שԵzr0.gʻ6z@KtԻRۮRƀV Uh B-Pb1,Ř6@ v8E@[ F0[ Ap gc99XD3lpQ4$Q @ ^PL(ZJ@*_ 䜐 F,`>y[#=@#yjbجUku?3W{xFYQ;|U#sb߼`y,n_cf'RNG~Ӳlۏ^1-d#YOt%b+knP]5`@ fQ,TyRr8 1b`jGSD a, D0j0)6%p.Sݣ'/|hΡm~aN~ i8f53Ύӫ$Bٿ) "?bnu}#nEܝGyk҇?;UӠׄ@ 1) @9;w@:@bњ*ak`2å]?sUZYfB͌M7A:y,I5HgwB%n0>8h9"U{?}?.I)=htB쵡s;d*@ ~P 0y;2Ж : =+^mf ljۏFhb̬~b{}/zx;}-H[rbN:߶t宿2hˆ)OGa4׶͉)r\eI*sC&j80'f ۲o B,+]?QZ|uQD ;H8tkЭNQ-|6֑sߦ6*1gG~h?Aތ\uVMnWL-"w^S}h3&[!hXZghqv (䚽\+m` ɏ S]6r7 UQaqY\Ԇ;b;'z ){m(X1zvmztZRMGrqYb4OKgXWn2LcT蘶n(̘]*D@`VQ,`xy_qڸC楩*sm9azd;qW`T'ᱢ*]^=;S Wd_Z,mlJ#>"!X_?#in~I [wi/i@ zg&l0؁ 8X 0p=/ ( $:}/s$*z$3L&O0^yFUT)-t JKKqO1;D`m57b>~&=͍@2)ǦVj-Z ӒB eu)qL++kfH. K#|z?<Юsʬ?F~WO=$6RVOU]DljڤoNY S rei/m!em%rA?mYzW`RUnT}㗻d8dA 3Ky9/X$Р XRʝ9岁w^ h@ 1SXCMX|GjPW"* ҁ `wqauDTzP˔{4L,M'ԩd>uTӲ(5;Au6醮KzdNJӾnjgUhgH:`tP+ TgХs:?S\AQHT:bh-~CԶ[@ Mm!8i "`s IN612WPT=0!@ќ)S\9CXuဉkd~c'"^*ы:kG53w9M%2QyFh"#[3}-:#J̥sbVħaeEL!DwS՞=aߥ L9oG. va-*0"WT)':?Wb危ˇ!9@ 0/(tK$Je2PȲ,aITkЕcmmU?'yt,:EkYuYW1zYYAKX"Ě~?ZXD &6T*Y%.X][1f+1W)E+AVsT2DccO8Of `/{q:`r'G>5lΣǏqj#F;Kܶ븘@ CtptlXj&++:vloTֻST5^t &7Al@)p ,BP,cP@c4+|vu~~},uWO&!x0*;4ݾF^ ųs72C_p>sjfJr.@Y Z3Lͧq` bÒ0F IVNlK"C9djׇϩd9:קF^@4_0F!pk{LKxdnos޴fB6:>fs<:&r 2òtkt~fP}XUTm6>i@ܺ=K!ލ{ h:LΞ2Mst]b9O `[ø2xۅEG?mxz-j@ @43\}0?>AoΨy{n(.\YO#OGĺz}Lq^ j:Vng:@;9X0mf=dc:q YCl,Ҏe@BcbZ`-i֬#TAGdryA MLa#@(H+]+j koVC[P/sM{W4k`K{n߫.~cʷp `@<*/Ƙ36s ı_$gR9K7d+icI ţ@ @p($qJ VBgnclؗz80{Q>eW.ƁkL8|GAu2?Z tS"&VmՀA \ aN`$[͢VgP8?99488r&ͺ\+l^;32W+c6$9>js s :}+򈆦i߼`y,n_cf.[)<NƳq_Tn'OyaȳG͍<V@  }zLw+((8{wO;k[;7~{]9 X}djag߬zrRo,,,G U9940΀wA e aBZ8) g՗buw_4ЃeCK[[y\uvŭc4DC/N{L2GĥwoJ>}`¢'Doĭ(ϚqMpgj@4JR rrr8]ѠV=2YFzq;E  @w%\.TT0;_.芰B֚2ofl)%Ld?tHJi[Mk8h9"U{?DA ׆\@ Q? 41y;2Ж : =+^m;c'**n?}Ks0R,33Z!`qoX mks)8m]e#" S<W`4׶͉)r\e!mZnmݭ#gMmTects`Odz8›񑫢[٪I*)e^yJDf@[=G`D{YV`0Z"n#Tf_\W<+@%= mV;oA7e;*6|{I GZmwlۃwN6dKS.Pbj9ݵ=FiۜDe3O:\\20+$q!֕L&AvAv(Wb~ڍsqȻ|FaBkqqY꥿:Nw}c'}5dtߊuknIٕ0RԼgvS'cv9#M6%z@[Q`Ried"CA3ꢢ̢‚*0j(8N`j $PB~Ze%"JA IHo$.[!=u!5ؽeP}o˻Nj%`߇KݽsמlSǷtα-';Ll ^;041KS+U$J|+ Bh@=pQJnIUX`ȂXqJ=.=WO;z jm}]"aF^PEu]\^qk?1J*s?JkWڸscgGl(p~! Y+_1Q@Iu_sY@A&ar0ix3"Ji& NؗpVZZҍ{!8xm{=o3nnuR/t>W^2)ǦVj-Z ӒB }I",Wvx$)[yۛ*,, pd֭UbbFԶV.Jb1PuiA߮ex@QT@  Ů^^1aMӶ[&|رQ~5N6pۺbuQFCQ$E/-BBa׵{Y5:/UDb!J~P*0*$s'd8]{yT/vuum䝝Sf7z{">~~1 !zșOMͩ>KzZYǶm>u!U_NL , j(R#qx2jU)eV7YzN ˼?fF!lq w Ltu6醮KC?x a)\ӥ>S2:pAdiYCiMI)RzWq­BqcJ ICgb%ڼ* }{O'3y+( WG, v6!n@%h:Ky\`\ ;31ɵ6y(5 L)Bb\ L\'$;~9RI^eɟ^?򭙹si*ɏˆ3bFtyAw5fE|JX\vǀ- ߙGоmNۑ_%9Oitnb";ũ/?aߥ <%hۑK-ℂ]X@a(.gvȕfDaCΏ蕘/pyc!nHN a6ze Ͳhc vB2`fÝC+6f9u﮵W%_i $>GX~_*-O_[+єdef O5>^7KcA ש.X͸d5lΣǏqj#F;Kܶ븘@ 3ӏKo}9~Cg0W9cN˖?sCGAܦoh[k!]_ktg[nirfdRI>ah|FST5^t &7Al@w&pQU?w}}GEQWRZfVjjjZfj i3 \ff`@t>o  $(EjpGCK2JzwD="o3aޫ'8,;@YĘA .ɟO I Z~ @NGGG9w>4$ǫh<- 3dr9p "XDrXEg-i@88ёcWx~ـ r̽E8k@ ģ( @ޤ_/K_37!M hɾ[PxsmFs-#3UTm\h4\P>-69^!N{'.ڞ(zvg9@ (H#Iߑ~vn*Y'"]M8Rirr޽{+ @ !E!Vg0ixK'#<=$R^x'K"""||| |}5رƛ9YӾ{.f<7Ƣsf^:@ BLn  _KrETn6+.y*W/qBWqH .f<7Ƣs[5YM[R=z"󲯻7Wie4뾶Ur'|uK@XXӧGl -8HLˆTG Ɖ#0"iA a x`U>r HBS:wչVS|(r8G޾># n`2N]?"jRmqy Cm.v b ??_ f'Z2QE02N-]:4 b`Vk2'R1 `!FrZM/A @ 7vb =MmYW+`$ ^l)ǹU7Ç0HvpF~'`t '˯,+Q3QN-@ P((,LK*dr9)qaXm^)Fho3?rFdPH -p6%#pms0W!Œ Vm=t\rpe_]͵lGms>(z 7^>R<:Ϯڹvo=Ztn̴=)on k{Е\#7>ݑT}k҅ak{USL5Ӝ 9W@~vC¢K$$ to~?~zO@Lh-3Ӕ vyQFC9)栢0X0eP :"X50E.%p2!|B}58owU=}cO2բ WsZfXj7aDcO4ٜ3gnyRP7vHx]('l2\]?këLbZ;5Ѝ{r;w@Bnx@ ~wsOաS'JL4iXqB* Z2t#q q~Rt< R\REcz&FP7ҩl bň_d/Y^.BM ?kQH]9 ž $ҴB6:ʟ5jلA^O#+ ZimzX0 YۿWi$ߒJyڴuqBrrO|9/@Mг@‚\L3+؏[Fvݰ-T  .DCbݫ e1n kli---dp |N7,Xl&Ȕƪ*Ffҽd\xiƷJ?`)_碶Z 7DQ9Ne*XrA2ݵ}g?tuQ4zʠ%I-:t5ネ#Po/ ]pC ?EJp3RWzPEMY3NJ,cά|g+󗌉f@ XQJ7lOrحC_Zy|l@_+3.W{^*Ϝ{n,ڬiSP-2'vίD\{,Xz_լ+0JA%ջfݴkd}t|ׇzzq+a+_CË'.cIJ9]+-l#'`#yΰgs7fC>iFeO`zBR1kl xQyIcZ/>1EfSZ$gvټϟԶ&))KҜpρ=/>XujoͥuڲW׽G+9jdҼS^~vn,@ z!>G{ |!lJB*5B JӀDAAJ)`6<'Kc4ɩ1sb@HQWvP (AHF/ PW=IDMT&h9v{ـ=jKǐ_nB$&O^m)*ݏĥ0}Z>v[66 Ƀhp0a`V%U?)=h)X*F&.W.W~ˏ!SºuO]c^K \LY$C֡3T*qIP(t:n@ !4X..{?*H>PHqpܸ28֍ѭCBf^eƷMSKF?@'9 p~n: ]=~${s1 [L0fWѴ O%TdIqa@דw5kj·+` EB}d13->9FDrq52gL=yyY *5Xo"~`z篷nmdsnwxUFUZUb+ē)oXdx('.F 8'Yʤ*r&aͥ2YPh[8=h4>-›ۧ[DOŖ@xt hհ^7QfB]x;eNBkkM>̟$>2@s(ڿ2)8DoW/4'󒢅bāѾl5-} U TUNGNXQEXlj }&y`bg/uS ~\4yֲ涷j"8)T0F jEUd{U(b@aX zi7k j2`J Os>IZٳ^ose6:qt :CVVVZZ:3ؔymxdˋĀ7ww!S,[6.A5EۣcuFP [U @- .0 'N9r{%,3-+^JnBW>g&< D@;mu|Ȕ1xyZΨUzJ N.B4bqZ?<;fb\ 90G?F?Y}CQŞ6ZwlעOO8j1=uѩ$x)6ԦRͶS:14vJX( idXvySnH!~4 @ w!J/.(0m! vg4ۡ̍H$ʍ),ȳ..n޼Idxxsnʉ~ <9.߿2+Z9p1`ó ]uKyPSt1-.`D4Cy;$*0&qzQ[yط:-=bzNÃ{2d,D1lLԨ Aţ>`¯'DvD="n1pdݜOg/2q#:>ǴuPeV-ޛ ^)aF&E: o¿sf&K7q+NY0 ~)k(+)b<3w#F,y8Ҝa.!O]pzB .0̶;+J2ӂBCE"1}{MlTVl?dU48nͨ3mHOlg{?޽{wӧTZziii]6U/^7eFu1YX qd"(\1 ̀M%*P9YLbf 0fOM[Դw{w1`)ۻWsL̵ά#A"u}۞ETz47J;gbPǽSMzldٛ WgQ2HMH= {fTmܪzB T]_aaᩣEFo?xr'$& z}A~nnqC.Wzz[QB:x`ii)qU 3®f̻6Ɇxta"PAsŸH-5hhcI@IUGm5{ugǽzjcx@ q(_\ rsspo~4M+vp,۶c^^gΝh4{nٲefT&M 7222|tҮje<!a V\.\SkY"K`4%mG0:r/|Z"ܹ4 @ yRM\̼7 ph 5FkT$3*aXd Kw_`މ':mN=|usQO@X+8ߺo~~Nt0Ir_MC *P: @ *L3~_-`ǺorfQO=4Ʋ]yz=xflX` 1Ρ.9*W/qBWqH .f<7Ƣs[5YM[R=z"󲯻7Wie4뾶&EaMӄHT&w(:.HB̸1;&gۇoiDZ@`F<0NO Pp/#Wȕ.tn" MUZME^{/yt8Qt5vDKU5( L:Af"#t+ Ȱ̫Wa.$AXg}v{GFf@C7lɬ9XsKZ#)8!U 0c,G@ F]:FQTAD,2ʧ,cu5˜N3.;#cm+`$ ^l)ǹU7PȯD3N8re%p8FQ @ (&Ea1 RY\Tq?hÎoBaW>ZUʧJv0^0jKfDhQBXgXB H(0`:xR,[̟`EPu %c<l@ ½T(Xˉ KV1 ;GV8nѢWP\8xPz*.3&W1G ~lGL8ء`:8pYb8q`yRYW`,C{/瓡#g~ߠ'|bڋvͦ:0fIxXʳy#?m7!LRԇ ~~ه?Sc,#\Z-ǟ[>{y+Hq%Wmv8Z~9@ D=+ $`jWMo| #.9&/\(8NvLJr@sh0K?NfgIjB_3ӣ7[WC`D0F,8'$ЌE(xH@ B|0Hw0zfqH"tm5A˱ۿ5T]:x?_Rt*'1yoM5?{H\>Nh_\YD#nnc& ̣Ԫ?k XV ߻羷0t}}q@GxPBQidR-r2P^aH.s1UG!a%H .&V,CXs!x$:e 09m㬦JBƍ1FCCaj=3p>~=Z!ܻX&rSgپoOHzu&~1Nr1  `4 >v?*8wo"*VAf*`'5!ijVVz?C)$~դ0 JɻÚ5sw ?{Ļq*̵ 5 !̴cdrXS8H2?O ys~[f*.VTQ'_o7baܜlVnW'_Ea6_C[$}'-S@=o< SZ|珍GͻvK Sg('}N՜is ``0ubʵMvT:;=?*bKX yk  e@@lGJ4ad.4 pU6(NՙٔymxdˋĀ7Nx.qe Xx_u"HGꌠ)c;Z]*vaNp/-;rJK۳Yo+>[ȟFn?q%7GoL\di[D@;mu|F?;_8F< ;8Byw6* \ oWmCQWZbSŞ6p$En1q@;czSI Rqm~M?>m'tb.i!P^5t+i|Ȱze@ DxYz ef2H.DݛlAl@0PQv\]IFoPf](rK:;'Wgښ{//sPK0 6._gpvյk75EhFLp@30C¡!Hp1ӫ`cǾiɬ.1W4֛wZ)ba qİ1QR:(ةOGq#[~|;8{_wԱ5$Ihw@#6:pbr{Z> @ GDz[α$pIL̎m"k#W-4Ҕ* `uy\iOuniFH!C伕,y{8W%b-8 Zu~e"~5+FM\+BFȕյeL/dFcaFrڷ&X. t$%I 45MzX/cnr]u04A8C-Q䵷'@#wΧꋮ]wq*2eAHW,eqPbFdʚ_ $0- r  J0eϒ~x'RI;s(~ @0?Ҵǃx7(ۣF^ 6rנA|(rػT[\u.Ea5Lnrs= 3IzHv?RR>jS^999d sv^^ I8a]"r~~V|^{ \{y)t# ø3kQ\wչ)rgC9y{]u\bDޥǫ†B.믿 @Qqpn=szeeL*?yEhT\TTk6,8Nݺ3_8#=LG Z`MwhW耏L[ʖ\ٳm\gFZq%֚coae]^ۻYU5-:vgqݟ3mt,xʛ;,:*oߚtayWuU1Gf[h+j7sCP䵷ȫt7k__(rػT[\u^E!@Qxzz֏Ns@X֯sO<)"Ϡ K  v` C)L&& 27ˉ7?)eA$0aYإZl=nΊ*p߳_͝ŔXEpu ~35ʋjԔB7~үn$0fmH[?zܟ9~F^'EZkMFAG'v.yQUorLH=P}ƭT#PEQnq"w꣨}F}tfL& {ɓK4ncR*Ca.ủi?Rch$:@0q q Мpn4 3 VdS&DUiPKt!XcgX]?fON $MHח_y`ocrΜ=fI ;=$]VF@{yA󟷍 vUר!Cdv\ny]W EQ{{[EoF}V<a*bNow{dRRH%xC;`LJLJKK \\$5MΖ _R݃,?ƁlibճlX9wX)p=_ն4)=ˆ#k!eN:R!_~?,\,pX1o2/%{(aidD˜Q[VUw ekz?9*&sTD@'컪СBu^"6lN7׿>C=뿝V$TȊ|.Cg)K~vI ~VEgN|>3KyΞDE 6r43fAx#wվVL&J%n3Qw9P 0|;w~:zp`*7XjnAi3χnڵO.*>}WL .<`1'TbTi)+G8kwXaY_6c³?;Ml(UԒ3+V5KO:zYц;uNJZ0BH!)^H lgM{ۺn^l6աy$gvټϟԶ&))KҜpρ=/>XujoͥuڲW]eNJN,p4orNgC}G9! BhxWzl=p P<8'ڶmpXm4dYut\rS$O&*z٫arvWL}B4Md$D_Ϳ\7Z5UqU>K<}%/BUU.S=>L!F dmKу=h)ZP~h$?R=l"#< (P(42uv[~Pֽt{ M?woS,KÇ"%X cICgU$<nEEQnq"wRo VQmwD `- w JQ _Zz$ FGW+RO$d2Sk)׉U \"'x">|M.al<+ݮ\/%U OjApg:O#{M}dV>cdrXaB>*\ R#x;wBCCUo{\ɖP4C$t.->}f7^}n_(*L._tQZ/)Hl]?I Ϟ(y,9\LtI`ܼ޷XS&WKIW"$ցJKSq-CTXɿs2,29V@z~҇] ?\0G?F?Y}C^};8Byw6~ř#+(Y MZ[7Q8:/v3v*qB'N|5H#{2Эϛ #o'Q䵷Gy̮s`77]o3ŰA))7 AEDd^ s! :;]#7XhSwtd(yTF6/N=WL h~`"2(yjp7~)kMXijԷ.za363 1){o%/!o$12ih;6ͳs?1GߊaMA.U'Lyl䧿y捧AwRC@i⽙jސ2Me-l=i EF :>DŽVw"]$;jFhXw^tʂiJ*dE̠g<3nĈ%qV#0_32䉱^hV?&>ۣEGQ{KUG! rW.h(ljd]\?1b  ͺeN)*!W-3:vW`pC TY, l$c$E 70OiEEw);P o$\,p}M\w`" 9߾4.Q-?>޳%L_giYTN%GqTL긷,FEzldٛ{vl(+ӊԄԃg&9Joq7[s (jo"wsC}m7]oB@hVL҅("K1~ ~=&\XEXIZrI"RLr2,m,g\gh4zzy\T 6u޽"#iksFe?H_kVΡi`0@0ѣЉ AdkPP0FDVK8G ,{e)xs48Hѝ?xbov~oӗIА0gՁc,;=<;  o'JF^AEj? 6rנA#-.WJQ8 : (k[c8$ab0 p LbR44cY= +5 s;z&M! E"LAzTX7Xt:Ljuq~gXؒ d$.P\sVk|wXcB.NYu8ёcWx~ـ ^&Z"{(r8G;Q޾x#wվQQ+r,ky:$p!c|3 48qB&"$Pv`lci+`߂PKbDBD  B&$x2++KReHZjSWf^ \a[XȿUcQs6S9";qDG)Dгw>[1w$(r8o5s`f}xbi!"HH+Ђ#98)ld#.G@%ernQׁ9;"H BG0IۣEGQ{KUGQ͞٦k? $bR*cM~W_DXlF1]g\KH!!xQߜ Prx r"zd`偁f?w\ꅮfQAcY].ynE<kZC( uk9Rkym/r'.lU.}1`bmq5Rp1·:ϑ㤂$"\%$81aZ5M3nKmF a2o~@5eJtReqD"+^x@Gaߵڂ6\ ]͢!=4Ʋ]%neW7@@]㍼;\fjwWGa-V= Ap".bJ,8q"ģX9̕ڜ #R> 49;AN@TV>IizĉTXOct}@Y~r$Hy2^a8#"1~F/C@ S`MTAr*9oӟ={7V۷YFrϟ=7EiP> {zLڻT[\u^bTGaZϵ=0🈿Ν;ϟ'M  apwF[[+G ~svA E|jPW_~P9T劂(`;\P̈ ΦP`NTgHdo1 zJ@zlb:.92b jeKN8jsu f̽ݯBu4wDMy"u]s8{߈{ܘi{e)Saxn]5ySIu۷&]v^W5+9W] ͩs]Py;qx#Ty-D^jѩDKU3یl `U6Z <'K6<. Q5v}f0_9^[l0Qz( 3EF儧",s? }h)) D;@Ͳj`2]NK0%дqjKqΟvgٗٵvfz1Ƭi>}G9gH֫h1LnZ0u)W?ؙ?vj{?'v.yQUorW^(T[FPTcVll4ȁ+AEx˼6Wlt9h0jRmqy=+mVCxY¹RpLGLY? 143žug_egCs(..7nR!PKٕQ8.nJ7:-pN'EǓ.8P.8REc ;2R_4L;k5jلA^O1HQщ4ʣSHQѩD}=( Wi"\ >S< 0A: vsTʖeG6c` ^dTw4;aСCway楗^2$@a,JQ'(@SF,  㖑]7lv@Qay\.00Zߋ.DCbݫ e1n kli---dp ,,G6edWcUj.oJqqVe/~V'+j€.b*^(sQٔeUDbyƧ.0fWn-#`,҃eBbqh\U擖b~:U\REMT} )|EO9q'V< ܜ9rgZ6N疏:o_\fBTTG8iYqv̘0A61,c@؏e~*uh r/m6[?x=6O.t(x-=;RX)~Sg}_ΐ[o?E I#DLev/x[?{6a:iL+)<&|bڋvͦ:0fIxXʳy#?m7!LRR9႟'{_'}>~KߚK]esg<;ʜĕ^Y2~hi)o/? wQE+1M4^&GvqY0|EoOFRYSa_pjt>;(ye^y* ܾ̫-_%UkU8''rw9we5-ϩz^'ڦ|,] ?xTQV:&/c&Q.~.E?gv?E3/ίd^4#D?,Z@ B|0Hw0zfqH"tm5A˱F4T]:x?_Rt*'1yoM|iGRqF/Û`u -lao_)^8 D`pPYjUշ{xvSSZKMoIO T( Lj]]54B.uo#]ޟBT,JÇ"%X cICgU$<nVr@) ?mZ:XUE;綗9r&&&:@U@oreq(jOF. 4 S巇TeXyZuX8Q}kM$r';U۝\؛TX?Cq8?ݜ0SsOg./6,3+((<ᨧI <Ik׮RD"1r`BϢDQ~1Nr1  cѵǏTnsơ c/NjZ+B)+%L_լ4StIn)&ٵ*4i;. Rzf͜!0o"cgS˪.VT{R'_o7baܜlVnW'_E1Ri5E}[[<2/kOEeV(paӓOz>d0WwӄTQ~Yzx1#W֮gm9RǏ7:59M.t~xޙo>YLa w|uZ C3xPIP c(0BB~>ΰ;ږճ{1Kau,kdSʲVmtZjz6'KJJJMQT> ުF~' ~*“KH@@܏%0C~, e4iO淧^^la$q0hvʴǦLX]݇P#g.Xm??Bd@oX{*x>*X֕ SM l><0`'Y8ŐEg-kn{+iM* "H1BV$y^۟5 WX?׾3\km}& #ά9s1e{?8FH/ˌjj Q>빪]/Lﻫt9#*Xݖ9H6 0{X &Æ׮Vt11=!h3rL)\F0 "$D\. 1c."( AVņ 2 lyp.n7q言ĥXk"HG~1@- .0 'N9r{%, OJ#{ן#&Y4#C !Zs^'ԙ~g AQK?'S4oU2Qo3 MZ[ ֌ѩ$x)6ԦAͶS:14vJX( idXvyS[ad[#G\=U8!~/TtB <{jx+Mx揻$%Lj)t?=NվKg=k|1?\?Vb@9WLŅy`g;|4PRgJ$-#n\WMBbD\/.P#vөA0j /ݽlJ)RG[eFA(DFAd^Z2dZnJO.^h{/oYJl.ŲTSz=&|We~*nr.Yr~rȟ|^צ0PgKŲb`]\SkMC5 aHFWu.CA2L%F%((?hNmSOE9kBRrW̢| [P ^ X`APr՚;'T) ahpʔ񞾔ǃx\'16帆EwkG"v㾼=}pIG f_qay.޴L^ ?}ް*0HUésPidjۥeisX}W0?KYϸqC8O ljuI=gĮ /C! $ d# wkzLY,wGĻ.AjfjE,#?{zZDv?N=g}Mί{g{o͍Kήss+ Q爨/ub?Ж=u vj5\N __pdtn};e-tҮ%/Udf]iw}\!b|7侦6꾰Lllg4#0ikҷ{Cz9pe8'.a;x۟9ϸ{lʒ dʘ_l* 5`UNTRZy*.ԪrN:׳Ϗ?Bsט[VhoK/8R(8r)/Ub5Yg;HeY@BM2gMyrpN pNqmg*D;,N 2}NL*&xL['p8PN2m̠mÑٌ~9<,IW_A. v<G_p-Fs<9DMݒDD % ᪌b f6iUIC[VCcw`rDР緿g  W`nQEM6 :/>}:7<|pnиKYm;5HS|.%;Bo|0Q:ꯑ)_#E4 )F}潨F on_Tvuh^kk y){,:53m<1Ӻ{xEhA:'cՉD0jœ?5utsn[6{,4<03Q&J3o]Q0H^˝e-7Qnn0e>.泵}H$YN|#מGT"n`J qk?>s^s:)?DΚN%w]8Y@ES6hH#1˺Kť;b;eOlr"ynt.·UìEF%-3Uf$r}HIfWI%Pnh9$EaK<ϭ0lџ(V~BʾF/>qUU1" v&ȝG]pt-hG}5m"iϐ=I$1,л7m)"+QޔGq Q@j9CQPNRI0hX/ b $A¬>aSrD~=L F PO\YA?dÖR, (p@4 =ZBRRRi,6ێ< 6~.zFΟ/r^_qAsk8@@` z* P' Οu\:^ڼg|@CG?i+/Z(I]Z;jF _]DeQ\K_@z5ߥL(Sf06?ZC)!П6:=!FIC H(,.T]Zŕc_؍$55Umԩ7,x\񟇋$G6H`@ՈE"W9E l[Tս:vh Ԍ#߈0z"~Q!uĴO`])Gߛvc,D?hS ~㉠a6!O8x9m(̿wxCg'SA>6V|+p6[_y%\Ԡأ<|df@\/:R/ o}/J[ |cad` ΏG)C[)%f5dQGԌ)b:y$ĉ־ ’$IYoCA H@Z_3SuyqL* ~w\d_;'Ҁ2Q8C%ݨAآO 0r#q3#F-EƗu@] };NBR|BIi'U$A@\!WZHwIJ JV(Ցi/;56mUOY5U5bSJ[`"|l2zvѠdUY_ -98iקwL=( ;z1une'1GA@IӆFzr ?N謹0Ҋm<ן[sdIymy@ o cs%~P*T|+Gk._-<;b}[29h@<_xPSeLEDA+IF M h"U8_  ʯI|שSV'[O>bX;|bW@L't-Ca񱓚0vͩ#J*$B O'G`56d8nɹ8p Bq p%OVS=T": Rj/\܇A_RO0] YY-ZoΞ@uzbddТlOꏙlt7zX=eB*,GT$~!rtkEU.~\ߡ9e!t{yPĉ2y!ً_wt'-t)h=#wr&NI!̓;h!E F/cz|Ӹ6yQ@ya#Cr&; ~}aqҁyRI'J10nY03nvyr6~c9oġ=\JlPq؇A$h㓿/[ YN#5㯄=íi]E~ܺȫM^OugC[@j(lCQ4 )1Us'z2+,8&؆ mY7jc7wM?rG$wcٔE (E8Hܢ@.*e5^XZ~d*%P!l N2ӗx0t^kKAZϟmSkXtv/"i7wz aQh w^ֻ>NG:;Gr <}j[16ésPidjۥeisCݛ}W0?KC3a3nw7}@`.m:xcsGq>=~]fRsص"e(\$zD!Ұ1nMp#3##w \r~<שE_o's@C}&}~lah:77*u<~eZސG`]6srҧ\;a!)Yގ;Qѝqf]iw}\!b|7侦6 o^V&Z|ts/~pdt}1 R2ǖ=u vj5BW[+#%vTb'B/[c]"\.W(d "n?^{9c7/SRyrc+= 0'pۦ,gX@^7?zpD% gѡ<?5Y5% $: R(`Wed 0]wIME<0ܲD#+wү' /W`nQEM6 :/>}:7,V@'>4y[ޠmvOdP#qG荘Z:JFk$1YqW8IR7E5RxsR۬CZw]S\suHgIǨMOyl58v߫/vd5U6hH#GdD=yY:>kW[ preSPmq9mFGk.l7s~בϣbfw_VQ4k矇x j]VBo2.)&v]j#OQ\mƶ61k6Ԫ@7< AXPVlz|W$qy\ht&g"<<.%T(F72T yUy6ۥ&f]qI[ͳDK%gQJsOgDGpŤ #9~80!}@ Ď-3,6W㳎]*@ Hu WIIu)IZ[kUdlph> m^l}Xuk(ĴcI@!1BGU/$b%'H\ACm{Tz9e{9ٟlw Ŭr˦Z:ԋUM-hy[N5_2+TyJ9m޺uibM-lLg, P@ X@rm ܉hIy Y($5(;Kľ8jM͟X@j CKav۫ǭCU>R/Vb0A -Xn]^% ;qsu!$ v:5 |R < P%(> -LZ 4s_*8t @> ܨScLf#+#DLix'Ȼp薰uh1sBː:]*H.B%C?>iX ;r:-"wtx?ƍvF֬BQ`I̶ [av۫*,6w}k,/bU[+V 3rUZUnQϫ i6Y-,j`1 =~/-)Pr'(T@*xJ 7{'5D@Aod'b$R䄡ʣEĝ=Gx̩_4 ch=67 XBLHz>-jB]^?%n{6cq#'{V'FL?3V9q2"ډ6.ިwwP?Q%pDG+x9)^4UM5v[KX/KfsPm,Wޮbrs]aF?.qS'M_P:om9T-]4(_ PzPt8 $p1 ^SU n C/mAQ,3:A,E ?Ve k^-||fk~Y8UmV-˗@7A<@z#oD#7oo?"賬i ]I{Hbaא޽wmKA T'I}nGQ~[/+/-gaU8wT*yOk4OO>WWcFkhB~mPwDߑ9ۗ d?]Mj]f`F PO\YA?dÖLd(p@4 #j6kZ&A=-6yM^˥\ח6|?d5xP윁-Q*8dĨ o?k# nqc]J/ϐ +RY_Z;n_LM9/L8xὡn&Obޓm=eua6WA>,NO6M9g=%vHZB}o_z|`lQDub\˔lAR% >Fo7]}e*P/`(lm [;F$̤$g]]1XbkICw69}aQFa}=ڸ>z2ʖ<קkbbnB[nxj%e(nIt[Xm1;.VkdRnYy,٥j;S`r+(f)-:m[Ӓ"ǔ;ARe ?ss 챶 gB~c׼M|@CG?i+/*".X#^F˯:η y֏-ZVj]υ~["@ZJ302췬dm܊rkwQ6uEa&(ɞRؘsᖁ̉\eO2DQ[,RK)<%c1QCeM4PQۮ5o9NNfZvFRQ)$?bamH%z" $=+,Ф_ܼj뱧|_~Ŗ'fCT9'z|)426 R]$0wj"sJ"HG X6-g^ 'P3~#v F\ЋE>A;tP|w:o ۍA=L*P<'ֆ9UƖ?˫t[Y|+0nzӏL`IsOeAGxgm3̀h?>^t^hyu n_8>x1_Dz02#”^VPQcetBj,'N a=Jyr%w`.99vYn=V*A QH킖8) m dDქ0?p?(:Cխ7huNzXQ༕}a糖cWNuyq D@i@ X(I@ l!v@4[X8ۑ|=`T0>>^U_ו6]LvﻳkXN2ѥ9̫OoPe4ĹRRTS+QC `RQB'@UA A;Qo 딼N)'[O>bX;|bW@L'tV/a񱓚0vͩ#lr 5ގ6 HͅI QlU>.qHc^4!`\Wrvt[mCAkX  &lƜ#^9Aܯ. ]sØ|1R$bۀ \ZmEG 6*[~_Oz(Yv)T?9=q,wA˫/hܙIlk4fe? kT&^! r}L>4~z'?KssPYTl=tL8$8>I_$êGi_d-ͪ86?L @  + r+1}S{D P7! rBJDbe6)UOu;䊅 @3w#B$'qf8 `k1v dTcz&rt0Ay]E 9"KWk8Ir3+V+-Ө:=1Gb2uhQ6KL6rJUMy=,뎞2g3(wm95~㢪u?Gy2_yĽ͖oT֥ o>\]y>1#568.e_x#3ǥ4BA$H/[ YN#5㯄7zz92$W)է՛1;uҳcL7ۺz*Y b/FEz[~L5)þ M>{O|kLj\IzwRHq=yHknز4M{^X,M""{>v\y9!߼ueh}-|U^%eu*.N􋷒Cp'6\-迏BzyecS{t|_.^-K>Q4 \Q2%pFB9xE7\ APbUBBekAUkr,R* P)S{R"pmD#Ƶ?ۦװ|(^Dnܗ<);Ì04^qJkJ#tZ~A͎ޒl}ͥ`j^v#M_5o\0w,]fE< MxwӚ=Ҧ7lOkr~>s?v{kn4]rtv]:GD|K,smN:׳Ϗ?Ϲk- e=TgD.hde̦yک[>9x9˥ENBQWܲsqߣWO>{gk.TAK4W ꁝ◄*Ez\"1u(hxn~$DI@*&rMZmno/ _y`e%84vGh V%lJT Wh0&o_rf O>}87h\Cf/VAN  *m5tɾFG#1tNFk$@ N,7 pL۠$gk:ryTAuw-;툞{^Qz7V}M!"t֌w/+qtelАF($I{aa3VNtE¦*\?u"p\LGOI22Ԇ$k۪!ֿ2i2,gE2QgM՛an^^OI-f$\X].F^&J,כa%K=j($Z /PIӥQO}潨F on_TvuhS\RYt1jDgxcu j.tk-]?hEu\ަ[K7"emBS2yoM-7ڮ,)3KoxA!&0]R\Sbރ ii..)8;k`Mە-{g[nCf1}E;}\VIf0b/jof$E)$8 ^'j0W OeP:kXI*c+tVN@ ;|C O ?)৫$|Rnc8X*ҡe+r"SY-jQ\ZUn.,qV>tOWL7MOO_v-ѣ0gm7@ !nf;BԄ:`?;l?T}r Ak왶ܕd%&Q料hBκvrFH}kC(Э?vD {\ۿJ^O]Uiy}T+_U\I/uKw25l'ߨ^2L(啐-*oWiWpnvs|;uo-͟꒑jҝ(Êue^&QEļ<\vj0/XOסjږm2mpTN}ZDy}@O<ՆQx@=aE9á ?(*DEayi~m1JtX5RExx]T(F72T yUy6ۥ&f]btj`!3wxRN߾R)kt}Ԉҗ6>rYf|Su At@{_.MZM=Y|aRn­ ,2-DwoEc,3}'<3ڌsC )2Iѓ03  B91%@cjOg]핷ثܺpnvv w/[/GLDʢtM-= IƆb-eL(!H_>@kNjwt2S,aaiZJlyI0a TF|B @ `f.E#n0%oC/ -xAۆV;+Gʳ^k]2*´L@ R$Iฺhg6bZQTk/eX$n߂@ @*F^u>R(/LKzѨY޼ϓR߽Z6U ^B;} @ 5 R/??/#M7'ǫ( ެrmerJ]2D TA\.I)u޴-xJJˤ$$,|p"(pys l c5<Ɓy>P^P}V7hp2HT! XDR~ӆ@ ˤ4 A*p\SaKq|.SiA  AJ27⥯O7mq*;/Z$̪Z {PGq5q~ʀ6 XL_⻦_T JWǵX&Z]Ox\.O*oWPi P PEhPnBnS{?z`0 o_x)7;G"q8Щ@ @=bzoRMy8 M*7<@QՌ[ ?&8},KOrVTJ #vFqؔh 0깟?T(JYf6Uݯ3h~,= )  R(Y|3]!Oohy5LjGCap4[ĺf`uB[Q&r@j/\\~T*S8AY,T5Pi܂jE&Ey@ (Q%ɔg 8b萠֭*0m bţ0=actͿ7/6 с_RmcƍR{{/N869jLFBD'[Mcb;"}`{#gF~?9Nksr%1>{ Hp]_~CʋJoZ̓ =jf` V nA^/&k 1 zFNJ$v[+/vdY5{۾p nnٞ1eWҜYg}}DӿWnՀ@!RԶ)^\|re5\1y?m鷭yWnzn꺩.yZQDE歸BR\M[{DHvqqqOWEkTGkU}Ao(i S(g$ܵk[HȠ go߽W=T99:EYC8A\tABinN`=TRͻT4(uBT.ԍ^lB$/%^qqTTdի_6jk< 7s栠y:R>xq$dC9[v\xh35)Ӓ~zxFm4A ci_HUQ U(-P@z ԓ\M? )<~poew{BTԪ<.׺=ѓSbed6A~>UΝB|eTh0K;na!\SGN~ǯ ўW~̺!>jzYE' JLȡ".)=@B۶!C]vg5vxlӿ6 In5X원w3sssFݲU˼76P\.'-z+ܼ7rKy#$NX^kfȳ0ռ:`ƽtQo.(_#U)5eז}=|dk j 4D2&_I>B( qXr`)Y?nݺg͚=w_ssuX,ռ= @ Wӭ? IJN^tyfVւ(ᾹT*ʣ5KSG%b(ʉQI뽞Ffg iuy' G:6@uVM9W~G2nn`,s rx_O:^m|swz_'ۂoHR<ݰ_V3zԬg}7ٙ9f9a VPTlVj:@JSOr@+}=ii _7lnѝ(olj=!K޴zb$< ]^'6^l]Z=j6+Cd{}i]Ibt=kN::(:aT mubz|zaعŋ bHR$$ ,PTt-6zu GQ(D, ECP,~e"Pc_Oo٪)S0Oz 3`IP4t&(-Y٢nrmhdF@ā,m ِiC@@Tڢe} Y: Hd+߾,Zc֯>{ZX8RYJOqdR( \`Ϸxc_+'>6;Q6PHOKDن$--.@ĸnGfsXl]SX]eCN@Jnv C_hڢ%æ^<ΎU f,ORv88upvi:Äz a 7o Cd 77 <.+T0',QJyyyo@,kpBaF@ M =z[Q*vsT+zH,X,ʝishB*d︺3c;gN1LZS= 챺ɔ $spp5 @QPQ);@IA~Wߴ|/ԳgO3bݍ_B׬jNaa![LՎ -UIB(Ul@ "X"p J&㊄".]#}n]Q∰F@ R3>q-o֪ЩSg_ @ˁg!S@ djaHnv֓|G>oonO@ @j4|>}n7ѽy9_$C& @ eY;uNh`Qb/iϒ5]!ja m@ Rڱ')7;@7{,y>Y@ TWR e,(hPIENDB`muse-4.0.0/src/doc/html/split/documentation/main_window_with_automation.png000066400000000000000000003017161404105311000273010ustar00rootroot00000000000000PNG  IHDR \sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATx]w|ni)tBH[h* =쾧ς,@HIhRt)[zfwrf@r 9-̽3&.?Dt:8ʨʨʨʨʨD@P.,_ n-7!XTB@@H[YF辆swE6kVJɨ/VQ6j$߇':ڟHDR&IF5h0t (x)_/#9gg0/׫`g7!VDs"nK*GgyKzT+.sI@[k%ub1<||zMktI/XzE)OA5"9oK| TWS,W @hpT'M$3"+OA'RFivɔZRep=<BdXI{IWk jR"{+ˊ(IhIPkOaTd&+9T2@X9zBX47:a W ! @ӕmW]3zfPrSyղS*y rO\䑕ӅSF)ɗ.j| Ryy:ቤy['9bsHjN?ՁTsEVAJyŹjA"|o3eE|B| ʨ$}۠s'G#~$<^ 5Bp:! D\ln_;`C9 r`ZaX"@^ r!00,S8PekWhYM]>P,9\+ |2BXVk\UPW{R.*mE)_ghȧ QR_WɅp?R#:x huAH-h R&=y5R ,P> PLkTZQ.%bY$ cu.Q>iYVu{RH k`w\.O\эн_vjժN=#ą{k14{C gA/M-g-.z0'%>A:dY\@XCd|7BwC#*p=+ v̌s8u T͆{YYhԨ AzmRXGCHHzρ+Ex,^ૼ#]Qa2䓨CFHKG U.T!sg}%OH`'Dοs8[u$2@-6OupYTD&`Pf=HA(SRWS @^D1џbj!6+zHo(Ia6: ɣpUÌ1tP'XBt.} 0FpQnݺLVY^>řS'QJzM:wK~ya5^_H#1xP$"o0"PX}`|driAe/@Ѡn].$RnITMu e/1%s8z~)Z@/Hєf{-8De?[ޫX!ST>kibSWv; pul()rM@OyApE=+!}V,]wК30ٝ8~όjۦTPPaQd za 4D]%Ꜫ * mġO##HPX6$̗!\žpt7xtl>dmh' uVo-h@"!b={PWOKߖ/K!hPl$" ؼy׭P8ש#9$w[h̞)W o1Qx+h "QaKdy/%OjNrN&oli (*e],`cB}.$=$ %*a:uʯo-Z,(a>3V  !91bh_t:={A!)Bׯ3gϢkD<^3Ds N'eC a`Tل^=8Nz$j?ҡ|J D],5Cj B<ɶu [T.Rybbbb2 sssjJDGGC>'vH-@c (*X,KOGnn@>7'+W@tt4"4r#^H7lԋZ LS^Z OP,3 Yÿ'24왳fxw`6{o } Dœ{|SٳpYJEߗ r!?/AAAYhŋ4ՊPR %8)*e >N8Iӧ ǃ",ǥhWX} B/F#4Tw>z"Qȧ;)|~[ֺ Fgs;yò7JHߙF]SдQ# 0|fnnnbNܦ ʓ+(Q1})ưvԫ+ fj$&QЯ@;P,2/]7 s=ɉh]`HNj У[W^ mZ}Om&WgqD&W<2s&jOmAʾ'OFVЧw/y?v,RSѬIc|{ B@$9dijvA>wЧ0*|IOEI'3IeZkb/彴>g]x O\0W_ Y׬Ws_ƲeXx0 ;44 K-Ǚg1bs8Wh [)7f12=zu7왳!8s \t\v ~(Wb(tf,۾ЫW/c@NE6;s}zAn& \,Lnq5W~_! V ;Q`OKt4a%i惩ƣ8*: ztn w~W@6##ŀq8hٲ%~/^Dk… v*$$r qcߘ`?|},^jƍ,ӨAΆ?NZq;~ځtݭ۴Ƣ… Y&.'^2v+Ym(.,MjzSgr.@nۺGJѺMl۶w|ɓqc?+C]Mpеk|>{6ddwƲ+@޽8p0""##T᚛Sv^dxd#a ;qŗJ%G#d+w#sU*©A6'oAP4o^¬W_śoc8(gaO?a1֭Kbh`ӫX<; hժ%֮))p)䔰cJPQpw- \Eկ^z}Os+ y&'/?6+(*.=NC(*nnׄHû5k9O:9=|RQyD| !Q'@եO:tQy8^2=٭EK QJbYA!<0_mcxvũS >Ŕ Sz%i.vlߎʕ*2 x1ލݰAn+ԩԗݯ*[Xfym{;Prn^:Crظ~=A\:w빲!"I qBu?CпOodggsq`8w,W]wa̙ B>PPW22ʬYh0cFym QȨHG}]'~MS<,F+g zx}6m J$l~JmR>ᡌgZ(6C#G.Rxee—. :i!Y s!::Gb*gcwߣJJ|#{\ɭHG ~FLt> F?B` 2F2l ^5Hsd3xDII|Ԋ!b. Qq(284j2'Cփ"Zc7 pexҰR劸~/6ܼq 0[mp\O1aDحo˖-~}{h۶-~=:xPP4GmP~pA{-" O &N "(r ,}lV,3N'-̈́~n4=Ŗ۸?~@ad^ȌS-< <]xGUSA "nqIr )mACKr>kA~<{s+. /7k}ϳzYkWpbcm+V\#]X^a3!u`6}xշP~=>u ߊf>0q#GQ)"2 > زA!? @B1?S]VV<>'^O# ouB!"NVd<[J&.e$C8/zf3@6i/b6E\pf~꫘lQ\8}4hL25ۤaCu:\2U%Dxl Fq%جVB`XvW_/>͂?qi1}'{%o~σ֣}R;we ϋIT/<ǏdB a?"91իU^gY ^{G`(,,DݺxiJ$'+0۱9 L~ /#͔$m\RI&) |rРNtu'hxIgؿw$av'@An޹s&"kwnc;@?Y Mso^9]?DQa$lmP)J56t< CX&$~r! AA ALXv4iU*W) 4o'b.+uʼCiӂO^R,hp)$(ʔVGG#:|!_q'C< St 3TX@prp@BpQlRcjҨ!28] ٬B @HH(,H@@ .ٙ"(-!p8p:C \7^ppHC@A@=af0b6 dL'F|qA=pAx=ʻpUX*IXWVNv;]!'+ 5+f"8$o˗:uj*_+#yzPѩ6Ye8m{AWPZ?(HE &>u~v}2njڨwa|pN4ghs#pi aCֿPz5mvN7m=~\*SAG ]x,yKouI~m*˂PI 8c׎h4{V)~QQQrrrp ܾ}(r8#2* *T@ʕ>u*#_A GyDV!T~XH0P`i (^rKl*&@ >nwP&qO˜N'6\.FDFqymä)P`94*y,P2)n Zy{ k=lၦYlA0Zh8yN .wQaS<=}֫Y.dj}aGPPP12<08ŐcꃢG{?V!< Y\ B]-7GGt=u!j1W.|t~YAI[]IO&ٟ 22 8 ŕ18/ 4QI:|'4۴vԮ퓫ސNCpp0w2rC4i2m0KmALI?]O޽D-L OIN"K(@_H&[A(H D;w`BnDe-:[2ZKozKa:~Tq#_eK  T  i I seWT>ÂTf25b<ޑP,-8R[P!I ds0ت/oepSY(֧taEdq5i-tqN:oq)/o t`CFloМxn T ïT(D՚oK{^q?~ILRZfֻRiT]q]h ڇ4ڤuŁ5PUJע/0W< IDAT]hu:JO5EP\+˩>?ْ/َZe(y,H̽- r;Q> 78gA^ r%GA^F1jKFFKYx-,L D_WOx2$>J9 -$Bi[48JZإ^mQ}x[,ْ<`ޮDse@$|̥BADv5>=r,&R{Qv1A\.󅷢"j~PPQ'pN*o&p6N NW'KXADK*~f\ʭ\tMŕ&@U SLh TxTF?ŗ3ewVC+"KY¨5 /rl6^&Y[ q++P"9./EN2~{+O*#AJU"$4cpu8$CuWTdTGʕW"nߺI'N8< #{;}e'!IԱԃz| KuguO!S×8Z !D+\mYpsI~)\-?хp^Jk*ϊ9[ǟ9ċ@ĥȩ_jf=H$k se~olbtk*F𻂏R2!Ӆd*@A>gz<^Ν=^/x-8z=U 6ȁwPw5Ņ? vqBBC6Mo28C{?<ɋ/T<C!IM/)T$y)BK1:OO(28n(|95 fA ,?퐓cP4ZËe)TxIT,bJ&q2z< %n^!嗭!K*D/vyG0x//rz)//UVCa~lv.Qd UVC^^D? pSOm6p:P qE< ',̀m? w8غ};BqܱF2z4=١C qLNPBͼfbUvI_M^Xn'{"efK BjbY%6xI*'BN z@@oqRlT*V!?) deZ R0J5PzLء+W I'VJ4~66ڱ`Mgy Xi Ӊ ampfEXXsrt:9 } "a2bihҴZlg̀lBTAz='/ÍCqncG!#çJe!ޠ6&y{wI=MELt4+nk lk$~ LI(%nA&]͢b,w(ze>J$'rX܄Nʯ{(lR~('zq+Mmd͌$ LrG?5[t{.}_l˷N nC9~<r`X..&B#þlX`Xل3gsE\aXN>00P`' E^ ꂜշ(-_A%-JF:AXb%~?0j*Ca5!@'һq\NB??KY.U]qCwç^zP,U( ՝:u={ѧOFQQoߎύ0كN:uNAj x'33oxyLCߔqv, ^{u4j.B8P? +>iRځʛu/ fBn1}thYL@uGF>}?#C yɸs:vJ6A}v C}_^n.^utcnj3gdq]L6x/M} oҤo]1l8vQ/ıcۓK@\ .!H퐊Ls<[6oAjj*oΥM&dffcq!%9p1/z'm݆޽{wyϼF0zBƾ}½wԩֵ+"n߼lt& ftsąK / 91 Az/WSNY<{.L.`ɸu&@\(Lضu+z==y? 2н[wBо];|WiCЭKWlݲS;֭pIKzo))ɸ˗.y]/^Bvv6RSRBOq޽:u*RSR0i$ ;+ )I0lF玝tl*DrR@{+%:~ÁJ*_~mbcbQdsOiukػgN!hۦ6&0L:u*v;8qBP6J[W֭K.!-- ;uN=mZsƏ$=7R>{*)L&L”D > /[nMj\Bp?III;f n޸B6:b֥֭ :w숭[`uڹ3tm[ru~A <~ǁqa$m6jW .¬<HNJ¸cqMfLХS'lټݺtA.m6,\ad;wǎ ƽ 7SNغe+'SXXcGS 'Ƌn]b-ѭ;w톟n˗1|0t {e sO޹ɓ&cjL?nD,&l21ٌ.;lfAɄ)¯!%)Ç ñGycx9,w]@LL |' d'r;Ccj yWBӦM1o<hР8 ##V{ҷ$$$ &&FR .}vf^GU_Y͚5üy@|l6… HO_iӦav`Vv:ߜ9sUV'a=&n u+6AAAݫn*ٲu+z}ރPVMlܴ 0 \7X,mS2аaCPli[L֐~ }33<$s9sk0A8W0":TU+WbpZt:$[}&OƎ;0|| X8K 嗳ѰQClڼ [4ǜ/ =L&<V̗g믾ۈt<(W<7i{ܹZ>cq?dۇƍ\x^en2bʕXj\$''cժ8wsEÄX~=5o~ӄ߿14## l?o~F<7_|*TO$xO>Xh.\Қ@~_غu 9 ڣEcK;vxC_~%j|k֮EX7 ;w._Q#1w5<,Yr̝RRNf{ve>?lڴÆ ܹ_)m~!->^`` V+.^˗sNX`_xP( 8!G!((.z##2""((10ٳa6[`/PcLAAyn8ty/fFjjKtQph#GbР ÀqA_TkIlɓ'ѳg/ tݯM t:8 <O?4n߾ϸ~K 4aa8` F<pYxx8'`bbb{g7k^Dci!ݛzӧOlӃLn͛oBTt,+( p@ a6YPjuyOذi3BCBQjuLظn@fǦ a0vx"&|_}&LՂCa;qqqs.*W,[|DGG v? b6}W_ɄhWg1{7_XA pq4mǏGc& ={䮃@@ lCX`ƌق{E(om !ixwѱSG,?f}^XXHQQ+FI9yӦ͸u7nDZZ>溰Qێvhu׮PnMle۸iC >W5::Lj@׳}e=r=/_fcHE]h#UTW9_΁lݻ1fX:waqQ+RaHlcƽA;a(??h#֣1()9 | 3:yaqZ8x L࣏>c">>SNAM\4>R;bE>}:oS|/^q#../NyMy6ܹ!бctȼ{ li;׿O q䭏}``6Lbǧ {=,Y&LD||<&MH}$F# 22fo6>hd0Pz \z3WT իU`ʯZ!!!hڬ9W`۷nTyb}5&&-SBɹ/6A{eWttQ\9FfB+VČ1}4ر-KW xJ/V 4o˗3ۧhܸ/CfuFdgg 2Fc 1VZŨWTڵk@ڵ9'aÆN :DbDd0ݻ*b>͋9ڏ䍏 H0 E e$HJrrqYypxT/ԺukX6 [l/jT^=l޼ k8 IDAT7o~qG~:G0X|Ґڴi˗;w¨Q<2hk֬jŊ+ѢEsM&O~;wDJ k&R)mKSNr*tM ֵkW,_pi~zش~[lիWj`hѢ%3OU۷OĚ5,W@aɒO%KormժV^ՊWe˖x_nuņ Ph2aJ=>b{vrViKӗb` 2u DlWZrIIXrRSSe4nۡa ڵk18m0wޠAq,f 6nȴҾr͠F5WV8|wa=0LXx1;&0;bƆ ,-{6brX,f,]ZR,C0 xի!2"> -=0e(W]"%KЪU+MĿQWX шW:^ ;SPW[cXH_rE[s(w٥K,K_=atK,A.Ex<(oР֯_Ō 7@qUVXr+/c #55VBbb"OWb)1<Ǎǯ Éʕ*sOtU> 4h ֭[A$lFz2I '6:)gf͘>K/FNv6NѤIcNM6X|l66oތO(r8@׮QT+:խ[6mlkXc3y$>Nƍa6~:4lذT,m `@ QԣB0 Nq7S?dGϣ_XrF<7ӘGm]9cFc1dPԮSpOk6FO?ŰaQv- ]1ck~Xf̘![}= '8s5z~-zx)Nݰ=Ziիif-[Zjh٪6^"=;۷Pn}=f4Ν;1ckNOkڵoӉveY&׿bJϿ<4sGLgϝŘcbU2v;7lĐ!h.K1؞49E~cϞ=׿?\"9;vΝ=e1rHIp:x};:=N'9e. .đLIIEƍ*yOm۶o߾S.$b6JMk֮aSuk׮ ƏÇ}v:0 p\?q"n݊>nz.iǏggN ,XfgϽڵ+֬] Bq?BP--ǟ|i/MCg{Eq|[Ϝ z1uT|4i}ÈAXӧMG$u5nx?~x?AMA^ ^'ڢFhޢeV^:Z;" zh|'HZkm#GbѢEڭ>Cxc#$'PG<6Μ5y#GB.]1r(L&ьӧO_X=?@n]Ѽys,K_ ri)'K 6I'aaS`K jQt# *`K+q/#^bO܏$AB0%~ 8y/t͚5eQA}.2BW4*QMG~ $@Ѓ=/b*1ŧ3=_ ++ u+[ {j  M^lvbvh=TZ"Vooy4-eV4:hbvq"}{ov-L(f`=o74e!z/;^u s,ԮS3ge>VRuw &mU*Tԯ \ӡ;]ރKWۯ!/g` zJWJ}}7@BjBуN'޽UIRFeTTRV&H:꩘|$D(pu] @Bh%V3;Z;*oceIm'l)%Ư.%򠪫[4 y{P0le, Dn2[m~p [hySi]— R$Rϔ;|_m(D~%Jc+J( ,@"0me* ʨ|%&p(ke&%P§H;ТS&ɛ:P"m(%"Zx` /WOڊm$L@Yq!6VN'n\Uj!2zt^Nׯ]C޺u ǎCaA|4o*V[VߊPI R8>BnٺQ>[J ׮^Ajթƍ8zwDFFE˖XRj+U:zį7"y&9%hѼ*PuM;_]͛7G4]pu5k֜#!nݺcǎuuy3Kϝ: <u\ϔ.^Μ)A]ggϖg u+I]_#5/ nc~Ѹ6Ǿ0,RTT"#5[r5 10"ŘDGL6 YYٰ% !@tT"p8p]8NDGG#88DDyf#//AAzC49^TV!`>8R߿8_yo`-O/XkBVGJYJ\8N\zUUcJAKãuOoQf+SJu y~ F"#J38'ׯ ,-Zt -N91:0_8} P콷nm鼳CvN"ѨQcϞtBr`pU?wQQRώw圯N=E횵>x2Z'سk7\pzMc0>bxZ{eӯu\}yeiˁ}{ԛ:[~I۟~^FW0KjL1uP?X/ős\l۾2njY?E fT'۷`_0u}Dr*Za3ut|nsA-[0teddea.-Uŝ EqX`VJhcAh h$*KZ .RR.D iE1h IZMd{б#4iF4aFl*Ӂ ݿ/> h ;ȨCWZ_A1/yi4Z-z I: 15h ֯__kaIw)i嗣5Tn =JѾ7!tpQ)*.f о.{H-(jp8bh{EQرs7mQ:.vFe~߲ݻѩcw{  q` 3 ׎pN_iesb;܄ fԀ шn¡* K.UCc!E8x`R> p0[Ԕdw֕QD&dENظwdq2@h!'o)@~~:i8((yบW&6lD1!@Qhk.vꇨWdLMg{w0QvEa͚qlt^_%ִth4GHJ?g^F##FiӦL&UWn'//~UאCVC}ιG8g0v\[Ԭ߭Mfså ~)k А@+Mq .Ter "dm5 ЊZEWw(//W A@PJ(APn'!!]ΧмԼ--+OzGp^F7LA HKp\HTgс8Չ_t#⁈FAvNف= B]b"9R[:>vٶo߁j6m$ ՊjUbbb\m洡%ضm[/жm[-z/LM~FGhzO6m6lhMAgeez*W,^THDEKCn^> rNی)p55OCvٳ[oDfxGxi58 Y~yW޽iiiucj8#k?oZ7އB9fgۺ22e8}Z>ʱw<, (km29+ vu߾kL&?-ݮg3k/TnNF#Ā٬f -ZTݱm_QRET"`pjq0Ms__ EMT^/@u`k5iq$ Ҁը$,K*ō7ңG/!\AŠ4MMc!7aCe$**K]\9}Ds_ر6m0tPRRR0 2M6M6|رT.8]Nۻ m K"ʇp[XԃZa}tdpP7W-Ǵ N=/ M-?­Ԥ+Xmť2H=a%'eŸ$_!VhD$4Z-  Mo7eV$g(!Ƞ+wb$2 V h" $#I, [a.-U 9DE=)ۿ 2ɡhDJLb:@5dbŊHj9&\Ct,nBl6cXqh4JfDA$.. v Q'!컘iסrѰFQdx $q<'#F0` ֭}z=9 ':*={v3h`AgnuӭT#Bq{nqDջ)0hja4Bx?Ȩ/gyw񦛰l̘1I'ҴzHϒÏ/r~3ܮ Svh<2&qh)3dĥddfa.) Ą U ,y 1D'пz5[d7ljĉ"Ah/E]^SROfѪ`$Ai?%*܂sw5?W'T.4ig;Aw7Wp;j; ƪB 1FV;hjJE pM7d:>Ǝ띷4 [lK.Ȳp҆:ъ K64HAAQ$'cXp: :O{6:}^2iR3FÇdBy=Ċ+k1ck֭Z nsmJ PBPn} QQQ[}M杸~~ǵ9w{HU8v#)z5r_dsǚ*L' YXIXZn*ϭBx0;a*M_zb#tX4F#7܊ƤImiLҞ=t]0\&Mj3n$* x`4唖zŲ{vUN%ڴh8رs9f͚b x (umV|"[oUQTT4u-4x&mt[fv{'\wbL@+mB!݄ݮ*˹ٗ{`ؼbB2$UdHrqp:%l֭WYYQ9]8%UAئ]*1 P{qdeEe :Cm;ط21ŷiP\8$4Iaְb6IOKC!eldlf4kIRyUvVFqVPq2-߿nCx˗ <<;f k8VLʹ_|oױ^uEϞ=rޢgϋ 2+ IBtؘhb(,-{\ `.eŊo={6w}7ݻwGRRRxgiݲW\~yo̶DCfqogUޢ)biqbKC}r}YqN<dY@n)(/Ɨ_~ cbڵ,X0n~k5 :ǫ\qmBD *j$&W*Kwymk6T7) i 1/jìYt|.rV^V̞=>Xʍ7\_\ U3;" =YHӓ<99 4I*J&O=D3=~L),k-V*;䓍@g8vHTTFtII=!8ReT28r{ĔcxGV@{5AB%pA>s^lq70,{ tdffVh`05 ..&î2~Z: Ƥ+P$Eb NڝӖI{KpӪez{6 Y*t"2:tuFv9s&|LEY9v-[46޹k''K\ҿe5ԥ-Tp< p ? ϫ>ʗriZy#\Yʲ ZSr=74HQ駟> DG#RDT.7./ŢU?"( .GBqp%ؖ( Ij~aXIh͊dRu܉#Orrz 뺓-B m YO)/J)SXq}5! ?ѯ_2ϟnK?x9:%S^f.}]U9>Yvy, ll&&&|Wߊr2&x]+CLZ~m^~UyThh4܎;$T+F [NAEff<'F?Ep:cP!-9ls6ߏF2AGaР,\Xsnؠ:zEp5@/)UpP=A ^#bJG/pY Z|9 PY*hd([mFCG "˖g֬Y{̜ߗh;w.SN` MbU#tC_eeڟTx}׃@ԩSPp6:U8ά|gs9̜91ڃХKΝjez|)-k Lt$$~}pj,bh2Rnn.~",'',$IbTh:yYQ7 遇ܜ((2(NYN͛AС8N%IMQE ݷgm} SdO"l/5+X& "bQd':¼q7QUo^OfVQg0|߰uXR]hO3×_,gԕW67¤Ga6R`*}&Nw9}Ͻج6G~^T}qv={h|Ûo̤bsh2&cƜR4*Z.hߌÇawjYp#/3|r0ۼ"Ic^͂[oexMnmR/N rrrjQ-4f'RD_ⰃC@ʊ8晐 "-,WT4!**rs(+؊$I8 r|sIR$ +!U }m6p8T%\TKIڞΚ=U UQ:J*AL,h UgH){̨D` ǯv#:qv#?SN%1Rĉ^}}߾#X^qw%* M+@dl5N?)kWTOd;X :ϖe˸^=2e ˖-crɍbOURRVp J4Tc*@[>68g$jUiK̤8tJ,˔( ֭Z}Rp0WXSUӓ[ʽy(Xl $K8$ Ӂ,+TTSRRr%*,\N'.IBӡiO`o<\@ԀP hQlv AdR X]=̇_ת8\_!!{W!י(lтni{}1a$8Ӧyß6?EMYp>/{yȐp9_;tt.c2L, |={K.aڹq w1*vgf0~ܵDU2}8еq""b R ]@Pp4|c(Ҽ `O@/fkFY F,Ò%c*D haDP\0udNUT_'i[<@5!CWE'T? H@u]'%RCkesJU= ^w:T݆P-/icc#@iiWaMʥׅZ֪ϕE`qA P DB])SXNǢOHd̙L<_?$CR`ӧ7{g9r;4zRAAfb׹3}>89%)- 5@j!D !wΘZqTT_ ~<V+08Rq ,SRnQM*%CRGp$|( A8!m2HKmFA~ZɅ]tT8c^ʰTXTouط{'m۫۶;ky"t^8^u2xɩk^wz+CqQ =F/p4/Fqn8vCJk(z>l%m)?q&[/)wE\x̞3A=b0uN߆xb~yy9eee>|iӦry0kvIii)e IB8`N 4 BάÈYT VB1 Wv/.nNzQ,(.6 }2MQTj%DQFG\v\.ɿrto\VXR\IJp1jM))I%(v[F^Ũ{vazkbO.)(PTT3R(pHG%.˗sKxrs=ޢA@6 FYf++"J6XjU_^u] ( ,_軌*xR$KrjmX˯|w_"..4DQѣ`4}(]@8}.`4hGnuz۽{߶}Gމ0x)9n_{5*m\ >\.E HNJ`1@l8EGҠp2}QW~h'tMxرcQeu;q{󜄜8qbdYn/ ju=}wU$!66 bkҩӹXmVvIzXbcc!dh4Չh8sg|A`?mۚNڶm]wa#PGnj$&IG MSRuH6;S2bZ߹uV~- xI6]+F!1FFWKQ$Da@/+85 k\:'J ól'INV R iz:ш:Z ;MIJ$I&9!(*q cצp9*P'4ŶѢPE @+T?ub%IreYx;N\.p!Rb (~ NKRTNC^ @q) "(paf (43V+ ^;PSaGkQEDL@Miٲz*UKɦ[ #$0"yE:y9γf;]0>r2Z:~I[h$m;I IDATn!P[c;dYЏUZBffFXhːmk@~CrRÝFj9X$*"66F!"hh 03/e;]e^YuU[giP G+EULl6[}JfѺuM7NN8w+j^g^^\+)G_TUʠ4(ج6 UEV#%%шc4qɫ1pVɃ $'ZέNWl8ʤXDp`07Tu0; ŭ|oYq5ArD dWq ݆bE8 W$ƐWmPIqܒ$IdMFADADAx|Dmq֭[[o1Ltޝ~f8]t7ߤw>5ߺu+N[ԲտKbb">'N&Mq<^rڧϟ)8V.[nI$r IR JӧOy7b~c233yj,]+{d zo=ao={KU=ͭW^tCE]`a+]Э˅|'N`AT\S1o= >{ްrxIFO/**'c6^ȑ݇#`cǎ#rWqwk.oX6ilXx &q7ѯ_?t:)))uם<8;p0v5,Y>Fয়~b[+x"..0NGRRwy'jx.*3III8FA?~{>1Sω'>|-SO=ŨQλسgOVZ5\ױf͏at=.;W_=5k0~u\s͵^+uCz@k|,<ob֜9<3O?=ԩYh˖-cɒ8agO=$g>3g7|&A AX\W_k׮|2r>M[{5v:BZZsAFF:x }z~uZ-VE裏@Q9c&c|٧\wx̙}oС=;ojb4@Q0 X**῎aP5,5h@k֬êlOVbp-\=…o5InO>fܵ0cLP4f= /OU.gĉ̙=}zK{߽q r ?"jZȒĪիj]fCQT\Ѓp8z~_1s͚5̌ -ڳ޵}`[cVILL,G`Uf^y^k1)),d21㿯|~^N#&:^={?T /ݻqqAuo)J>w^.wq`ث=g6BQddYb֬Y;oڧ߇-4yb̘||l6>sƌ ׁ]އ {Ƅk[&== EQHJJ"#3oڍbKה;z(w7n]GSe$k(M~~xO9"ؼ#Ica}zk|,eVlڸF֭q8-[ء7,X4jjVشa= 66$ IKK%{i,]{Jrrwq'PQP͕LDQӐܛhaN NCԣ 2`@=ʪUyyʉ...h?&;;V~b  uge<3̝;7Ό38tvi7zod>LBBf&t!** Ftt4VzSɮGZZW]u%|IHOo޽{0ztUs$&&R^^Ryty& 4)&L.>'ׯ 0p(u,fc̘1w0q"[m"7o)4vF#6 So}q!D:9 x7' \3'xΝO?c竄 =z/3JLW ]t!!>:&]zcdB\\^ L 9FNhy'm}`aCcaڎ/"_|5fFzé}Q믿 *-6 N'z[_zҥ:}Q-Žs뭓x /67E1yOСC{%++St0J5k֌Gr8z(͚5wXCXҥK)..z}TyвeKZh?&>>^/D%Vʗ_~ENjM/,Kxb:u}޺ukZlaw%!!#5oZQ}wλj7\tޝ?η~mhjՊoKFF)ɷ:phw\svHR)EhEzҫ"WA@PKqwIGlMI.Ny<;̦N׺t?V^ݺbƴrHHH̙3QJ ]kκb֭xܹs;v,*xfO۷oS)Ro`6h{'0x;kׅ\}Wp/XHmSS1ut{6kĘ2yW~,f9`sw>J.-ǚ5k1h@ǨQWn>,T*Ξ=+ !>|g`ah9sb:QծJ'G+VDjݻݺ1|W߾}r*v_T\ v|;wҥ0jH|T?~cǎ!##tÇqa罙쑒2W\Ő!Cꫝ0x;X)S0bH<ر 1VP{Ʊc0`@ I7iM47CϞ=q]̙33|p5 %N֭+zc#' *W'YѶ+G~}qrx'݇zKR[K6/С/I[J֌0?=rnOSǎxn3.?ɓb,^_(7'-- K.۷Q3;v, . Ne޼pjժI&ZbԨQXp!?Vz %%扽;cƤC+lVlݺKՉT.`, st:ܾRKn3hа BCvZ#a/o*phR[~A?}6̛;>w6p藲 t:O̝;&MD:u,{p^.x꺐܆A8 g8]lܖT*_O 4<!~t#44 !8yJr䘽 ӟx_?ym@r7_͑~:uƂ h$r(˅Ү_]BiwDpg* Qeܖ_\NKgQn=~؟~?u jU*WF!o [nT鲨X/p:OBRz\Oܾse˔ׁ\( BwNO^RR@\b_8W周zN;oT2P$^ ܼuw ؘ,Ue˔C\\On|{ŋxRG7`{zfg;xp>#-W:PB ~7;"ڇ.E,p-o|nT*bK Ga{ûBdDcbSDDT*>DfFOd$Z5 @ ^. _@i"*]ҷD*on(8z/PT@Dxx@)0sXX8詖ׁR\[H ,fj E{~H7)" tAJ7gݾ} ݖ AAA%`w*m   Ff5GVP(<õAAAȄq,^   §FAAAlh,.2 M0mhi|Ņe|.oZg[Zd͘BpxX7Κa-X4nFXpE޼&Kɲ`4a51ek 2`xuL\F0f2lֱXOMsdc 5fj/cɬbLzb>SikNX12 ΟeLPS*~<c|caXC^eo!`\5S_uyfredp,crr1sV[)*p]t_Qq80ig>#ksߠXL1웰[0^Dt“]|}  d!$0p=& !$$n{`|0f<œ>3esI_{хu">$o!  `5_` Ĩ#B18N_=hP9> ~3cg;~2 <4뼈}Aa'ɯP(< zEz-haȊa^:$BdHzl wU"NÂQ1q  8dJĨD $ı+oOm̧?s0|ͩMuAiS _|)c͗z k&f/y/^ Ƌu_DjP1֮Qpzi@:EݐVqXGzܽ}%KZ. rR]U=)7XFHWAaM(9qPAM[o΃;(WU+U{|W\Cdx$&0`Ɏ%˲Z*J,?xVTu*Cߵ+{p]/J+ʭS ut0Ю} B͚`(Y°#۷_꥗)ٳP?)܅ 0n>h^>mpYddd <<իWG߷re߰}uDFFsϡdJ#Eڵt~EšvZп?-t~S1wdrT]_H믏b>lڵ{šLrYt ~LAN#d9PS?7ݐO{ۧOz|5vv.ډwo`ⲉөBBq@гzyW(Gp><|,$,qva=C8`>ҥKx"@ҥ꥗Q>"&F_ ^=ݟ8`^^fAFXĉ_W_cʢ2q<.Z#FQHO޽{1jHIuӉ\%p.RȲ,222gdR,X0n޺3iݦq}d(Zw<_y@b.^:+|޽;Fڍ4|xC<~=Ĺ6tNbDGGk.ػr7G.RjQD Ohy>ɓGAAü_E  ,,Uhժh:ӽx^|Ŋ; Sΐ}FNkn|Μ96Ν;(^8<IIXlΜ9e`ȑVMZW@УGwQ䪋 ..=ztG>o>Kp%TTƍEmQbEܿw>}z,zJ 7`ㆍPk48p[庒ŀ1dP_^: ᾏIkW{mRPP7{aC`t4h 6o e1x@ t7c±8I[4;fR0wsFux^{ f&'WOV㛍]gϧa6RоC{̝;M6J˲`LJ76_|ü7o'N@ףRŊ(W션 W^ŰCE%[DHHrssgؽ /ubغu `-P&NOcXjEO<9sxcGؿ3ԬYۺm1ct|^l>&N`PYfi>Rp5lݺN-[ w+%sNPTҥ >la8c6z&O>ŕWe&7^[oݺBe#V~Nn.^&XT]kHHHz 1#i:z$,h8hs OYڧPğ(Yz&_؄v.8/o귱Hg7MiA٬Y+(0Q$0 *U ?~^KAeQdIЦMkC-ko?bMk޽{#&:ZT ŊQNajWjtu[v-EXboaՈG&MдI/fΜ]M~||̙X*DDDc1$9pRNk_Cdd$4i ]iq:ded.dee!::&#Dn&cx ,뿮jѽkDA( @XtQ tiW%b{~$&&E6qt˖-Nj/~Ä 0 7o?s} _P8>>Dɒ%E){O\\sزe+n޼O,=yDtP8~x>}bŊI.Jf233AFfys}ȅzJ1vP`EvѣѹKW.]:;]6i4CƢbWI;a׮]4iX '~bdggcܹ믑ƍwER|-bz*fΜSNeY$%%a̙gEaa ]s}Mxp'bUڜ|hsϯ_b7ar>)>uo6qVyXrd:|7n޴sRMzt/z~ N?*UFٳ`ѢO=]{ԭ[~=ztQnq)۷xbngqR'u&3grY)SQQQ8s,֭… ĉW Zǵ֫3m"XkKwU?4 =m^2Gwy:tc0i$ocǎȑ#Ѷm[5j^z%z)9RZ!!!h׮O:yKyɥ'hK\+Puıg$&&_~q9@֑,;(QGc=.aE1w\@800 ^yYcj Qn͚5áCЬY35jn݆aÆ#++ QQQhܸ$*VLɓ۱r*DEE~:trbHM(9 `طo/aѢ-.]#G oڠAcy;w=}-[o@bb"Ǝk7̙bƌΝT3XqDΤ=|0Zl!e8r0֭3 $ , alL!ҝ}Տ=͵kH6)vСCt 4@NNm/VPPP8lڴ {ѣG #7@vU&wTqt:3ؾ}{d dzlٲÇ?233Rо}{?hҤ ͛'ٕZi:2 p'_طGeGa:t? 9ǒAH'O|Aujz'e1n8ԬYJ+!!ŊÎ;$eσ 6cuÆ C~~>6l؀_~\ՓuĹ_lk֬ADDy:rRRRpI~@g UV !!!8|0=ٳg}XfSpZGySL\a7fG Z׬8O,7}i 87`J*yyy7n7n^z˗c0RYrUg9pƎ N3f8e4X##HKKÞ={0i$96mr)@ѦMжm[DEEWŋ7xgvQI:DIJ8x  |;3<}GݾA?xa'nݺٳ.sNoߞ L]p ;jׯG~\OOJZF.]?KyN:[,MHH 'u$+۬+MG4=9\c}/fep'A~"Qz lIAHǕg={6:tnݺ9ݻcĈh߾=ߏUmr/40uThZ\ҭ@>=)MG;wk.ޗgu)@0w;wqSzÍkʡ2{՞1\D/¨ !;7Os, CTD$ K{o/h""_/XF0?wTzŹNԩS:u*\+bSGnYBw͟ozRO̚5 NBhh(ׯ3gBkH,/u?3f6m`ԩݻ#fe EM7#/ eʖukO_%q:j>["A=B3<4ʕ+III8|lS.Ii:Q>cYTəW F.!0G:I':!*99([| ŊǍסV ם >ƃ?Ap范FRr*ERn  BfD8\m߁J+RFq%- * rk[їbA8VvҬ,qi,X㿆 0|c y)YDƜY`LyNfY ,fMBYlx,B& ˚1$g&Y3Le;ecō3+W˚6/}40 a Q30[d :M\c7x|5ưgqX 518A9|z61łO lҏVfa̲?c X5gu>~aԯ +(Op`ؾLN5O99}5 OR3Ff}7+m,pB7jAor}Fz ~6qmo䶲kdXYPIʁ6 Lv?˙VlC gKg󲌙>03Y>IˇizZ?`[g'viFxىiWp˯bth ʗ/e+s hӪ߸@_@:dJ$ի@]p%7nFr>سwzz/kΏѽ[W)0[絲 \fXfEA`|va0Ln:܌ñѽk(AAAʗ-ڵŖmѵӫT'AբZժ|Ç_Obİ!r!UTZiW|rx-of؉(K?\p;wrW9k.b,m#61n-$,[a&@A8Dž˓pHOvj4P`YժU0(((@bb50 V˧h!Iv6mڸ>jTOĩӧѱ};] cź|-]Fy&lź^aT`YA H'tZaa;V:fpxt;ݺ} &:/(@Dx8bccQhQ8~ U՘Åk=iՑr InCŸJ|۶i>ٍ\:!?#i(J8ѢU+ܸz-ZqClW#-£G&@VGzz  D%#!Iի۾ޅNvpp>\Ԯma`nEqܜ\z>0 "~Uʼnل-\agD} H'qU^ѢK {wjڂd?B=G͈؎5AA40&I 8,*9.\1:YHG =9"P\2# -޿"&qDLllsLOGNn`΢A޻I/X.0Fj.R97oZ + l &"A,!I$*ɇ14I2 yK7{)cؔeh0of)eW`Nxґ4HOW%^eYTRa,Z횄A2޻IsƂk;+\v2vMؼd* } :! lgrsrq)T@65d'vG *Av|rX=C˯'1b@rv*U+WQl9DEFʭ6QR! -0u`䨛SœdE^bMաPovÑkR^;r;W`hzt:hZtx pC^BhUl^J ^ב8pCzz}ÐPZ˲V*AAAahZ>]ń '|iFtgQ=NFhMAO@ֻً>ݻvFR,31u3;`1|X~A"E)"ANc䨟Rp&Y P[?{#fXZ'oK_opTuܖRMut:-o0SBMI3`>!Fb>uDmZD-46@ x׮V͚(T(  *\3& ~L%c!`H9ݓ=!&Xl@vU rȻk 7`1 (29C(e+p9B3qU6e^E<1cP]+]>WT~H5ޝVޝ! o>&8#H zRDϏ3 ={tGhh[rAJkXGw>&;7`?Dy rz 2/!5}˖Cڌ d:Y9ӵ @0n9F?cj̜2k99<û]ϳ.83歶~+;iuULn_&+f'q=oyU P"vsM3 w slƲ=.|;}jd>e hG5g޾ԃ9Cj9jm^9Dp$WbV}t}"72rY#$=fy(wT\=#ߎl/eq8 qW"6.L^t_AA#EZYGr"Oۑ|λHUIri'瀸ޜaڜUs4m*Ԭ|\o`U6`&99uGF\ A@n8/=ua4gHi#y^Sg;7tWGGxzpR]y\sy ̔p*()-792`/Aj1uJZ)ui(srH?ae=fRnԋ)׆u}?Dm$*EFF"&66`9zd, ALΙ6 VxD1lι̝>ZZ>웞);\\vUIC|c4ZF"EsE! @ {nl9er̛1'e E m;whLܥ2wUۆ9@,Y Y FJPZ[_ $8\L\ye$Uj7cmL\{+Pz֘ZA\p2ѺΛ1Fr^]н3eɩ'O꫾-]!Ř6q]ǎ0>v~nePA?@r>+I@,G0,(_@( =2LJ> 9Ro\#`Ҍn`Xq׹lyG^=)w]-vn޼"mӊd {\57b݆Mq&U?a͸cǿŊs`KW7~;8wo܌||'.D} ^qW_TI9 @ M7\ ^qAʇ| %RODJJ_>$t`Yz;f?] WGܷ PlYԮ,lێ^Ezؼ}z# +WiWZ*Hr˖CTd $ٽ?@˧,`4jhfXZA 9D9XQa%50q,5aY߼e1]8!==>aH@VeYTV à0 Z-bBl>۴q#|3ԨSOcvZ`n40">Wf<\P 3aPyyDh_h8yUR } c7-Ƣ=*99\) Rڇ# o]`ĸ8bx 4fL3"?soݾw"&:/(ZFll,-'C|\bbb8Zg+f4fE>" !?6yOl 7TD K/)׶;ߗ$2o4qw;j< _=vǤ#pϿ}- b}ڥ+2p %SL?]u7wt:Ẍ́iEK9 g: )u[6_U DԺy]ȁr+U~t:t:m}-/N8/Ο>ݺtNCV-l*>nƍqиQ#>P]Y% ZX*`TlSښ#P(20j`X4m?`hXw;ݎ ` ܸrM(wo=?q=>G6,ZZ>K]:`˒5>vR 8 aau Ǡ#v2ze a/w?n<R>.r抬r'wl>k9]] p`5 zv}Fv@;!FRЪEst:L4o!!!s$X"KĉoBbuiԼ~9MA*q+_FQhx[int:>\YP&apR۵M[O6lM(W)$bS`s Y2wE )ao(: zȍRoO?~Ȩ(VG5W i]׻|L"z~ܘQ٣;BCC*Ǖ^}9`0aidT䘝SfD`0a6 ,TY2$3 qm>.ZٙO<":=n_7װE2i]D՚m…Rt/|5iƵ:WzMĉïi0w XENV_A_tx+#Ѿ{gI.o7Ҟ2zBgj9uĒ=rF.}!X\ P1*jh4(Z=,fG񒥑@@QCŨl)n=H. :k`ۥa&+pXz :4ӀPY2B0 <`e Dу(QExtzqO䔡PőL YW8$5iu {v3~t|nL}'EQγ(d bhW 7SB"uOLe'=t$Lo.DtC/~7E^mqt"-MgT tZ- 2[x,3u8nj]*Uwp]T^&NR%0p `ǣDZfR{`~O 6>YY*TH0Nl|l_r+8u/R:9X62L'kc&͓43iúc8H]ppsx~& gԝPR!$DAӂQ0ZehHƗ1s:hZyi+R4;X4B@th8YG2x&]:<Ņ iu rs4'_vwν_.S?Ӝ\|>-o -o݁@ ϳsVK-Qq_4L7buI1q3c ZelXT|2}8t(^y^*OsC_ R@Ib>=C_cnJ*(r YyJ@=ܭ30 Et(D#.&q11< *P! UÇWXҞ/| c$AÆCFhaqXxv7lS!ki l< @/WM(Kf㓍c&BZ50el*Wc@ol^jҳ˲` 3}h#~2߁eYakGQL)TCOT^ %˥wc,aג{a X;o),t Cي0{_)VfN}aJ ()h"x 'RY` FOTOJϱ\JOz_ O[_ȕsJèDh9"'Wqa]_kU)pXDHhp0%3y )5 '`amQVzGUzU./mڶ7kӂ\rE,hݴd$x""'MZrG4{ ^hjS a2jl=bX+؍1q?]yw+MS0+MQ<ټM ٠6X*RQ^ AJfT0Gr|VrP5"*<)6k.Rapdmt 0L~WaF$F͛vM4޵KWp]4̛J3W;; թ5aӲcfs0pK\"## U*zd, d  0}zB& /=QT:e,6,\",<&N…Ov!:OKtl 1,]IE9Y̻Y#W}ۺ6nKWG~L^|q`{:qt`/:}PPPeˢvgevt*'IjV*'&cד1l\rHrժVAڕ(_"#] Н $~>eWg¤Ob@<)p&q>Q2gͲww':fm;rXfhǻorCzz}ÐPZ˲V*AAAahZ>]ń '|iFtgQ=NFhMA'2O/a :%eTzQޠnJ4/Nˑ& >>C 0=u2?[ VEq  K!!/~ GxV>CbyH64X_J:k|מ:t~::m۴Gy p˯, }/࣏?A.ЦUK,[ۤqc14nԈAA(ۇVv9\NQ*X|-Ӽ= K,d'$TH.|y{۴n6[i5lF @AkU& р &R[넽6rZ9-à#) 9+/O3xTIXr65]כzxo)QG9 i U5>;x 9w/̫ 4@tX:~&RP|G$2#SuJu[rLP[Jaê9NC^1q\qcFg uW BE GlX5ץtF"ߠ#W=u8җua\km%ut&`R[2uVs:'qe kb)>᭶W%pBi k*@A(ޤ u4pdlZ=_B⽶ܴzlVIkS҃כTz#kD'y` ƂA,Vxr JVxA@/tlM94B>o3`dl^xشz> `)ro7w˅XxK6QAVjJubHOOG*V; de W%ˁLrS?t]0;n!;A~Lv[#|:!\O ض~{gBPkЗ}^p\F4 ;vA@w:8j:tZop:G>3/;*Ow{X1UI.Ao {G)m#eIG}ú۶~eno{sȍoI-ʚ&DJpA H6T|J K ׍m _a8ZY)=#.#;;`" Ejʈ@EDd*VphKFd%;X0<:ؾqyx>&ԞƕџOW%9k7P2 X8YYOXMƃVEHhU9Yx)k5kӿA A쁅g\Bɲ@)AIN{reDzn+O vܺsUUA%ش 8Lv"W%`FSIŨPpQDFFyL(+\7]tH@DD?X<AzFb=VA ,(*u=`E2umz;'6\œ7(z#۱!<"S3:u ~D <"gY>^^L|EET4lP*vaȌ$PA ϺN4>ܲ¥l;WeBʓ{Α* )WR~q8Y=Å.ڄsԚ7V?|cSCsڐA1Eh(UGJWhmz%G[`r˯W7WW%'dN]^?W;w8\wNhNΐF[Eǎdɒزe3dT*L< IIIyA  ut%߻*6R:<%i71ܕYhW%&ց}{7 ?o:un.LT_l(Nɓ';6zk̙֬30 Vz֭A<-txH?}HܶJr\қFڏ: zW%#Eܭsbbq&^Uq] 6&Ow h(MG=*Pt|EuU{XaO%sOwk {+s,%KQjXIx7+(*ww>~='RC_pHEfXױwy9Q~ 8;tAĂ;?;EDHŎ] `ł]T#&d+d33g=isλPܹTXs"]k1s,aܸ暫#! GV N\0qRй^L^u%CNj`f+8;R䮯YJ_l 0Uc5fQئյz ~^[B6n2^/jkЮm!8 #7/_9ZfQhm8\n_}r`   "9O ^rlz߷|^ǖ,ϾC`p =ZXֽи<ʧX:~q}Sqp\X()i< p]٬ԩ݃XzUGqQ:u;AVZtziByOhЛN#RxGAA>,փQԮvG<%ج6b69E}bjP*Kjڠ5dK4SIR6j ͫ~j AMpLC5x,VXս<s*BbV$ӥXRYJuP$[J͖Qм꧓t*YIU hLbcaD5TOc t)TRԴAk2ɖhhetl {^Z V Z jKjZmZ&\xˡތf-^F F &YYJuȤJJ& Tnޛ|tZkXdj>ZT8!Bk~} ]qS\X-ny7Έx<*TdԐTRԴAk2ɖhhetlԀZc%1S[c5 2ZN5>uPxQ@=D 1lr" 0yǁ,ꐢH\ 5euL%4! ʫ L$y5ZCTaNTQ_nRR=Kreb';I,C'*P*Kjڠ5dK4SIR6jW^fZ#huB >úkGR1Uq bIqЊ$zR: ꠦ ZIDC+;-eayErbg8um#ǁ ;ER&ˬ 5d(T5mКL%Z٩$l)50rDT gÇK{__ ɰf{:1@&Frг5=:hP%N|=ID&Gg6WD(xzUH)RP9q $ *a2UEՉ'2ɖhFWOJǙ$ă(=)_l~2u˱BJTQ ץjP*Kjڠ5dK4SIR6jW#OO76h.1#tqXTFN*ADK *{5j1"=5ҘY,ɠ9:]U  ZP{r%ԹO=$H1߹x/%B99X%E,u t8;brصM%tPVVXQVVÇdtO1iS5щ=C,(a9iыHUt8AD$8N^?vd|Q'"+Ij8 J: rܙuI˅-8۷?# /(@A~ _s> |GCSQW_:aDhAA$Mh6|=pxOW.Jg>dI M/N\]+I?4\BRِ1Dtݲ Yk0n̘8AjH+kC6Np'7, r |5,}-koiDs""]OՐ' -s"0 0TVVl6Hee줛=l6l6b};N\8T XDrBa aEAmoZjRT"텈te~q@<7炰kWxUM/q8qb~o};.c" @h%1XyVxӐDC$Od6^g#q79["rNx9jwvU2MhݵMEYA[D3&Ɗ5F2RH=ظ)3ʾr~ IhjKXdPk8D*A e0|pa"zB!Roʓ/|-=N, buN0r\C4J]q@=AD GP+'O{pn ^m)R:Іd'T0؄6CaN[KZzB:8\.TTT"5q].ԐTRԴAk2ɖhhetl@WAHǑ‹ =ʈϿ&>Gr!]m)4~xÑtУ1 =g'yK[fnlFAApף>|:\.yJ*T%/!C,:id-N%fK٨4 -&tq/ʆCR_8ȇƗyq CaAmUթHԁT́#:^ojAN^:j͍}L5U(K XM;YYJu{+A"d-N%fK٨4(9h-蟖Qf7cge^L؁9#C$j4gBS KIG/H賰i7[z2ߑQ^lz\' ,AՐTRԴAk2ɖhhetl 8(#sXS.{3Żf4ƗVh0{!p_L71ZDkuO#&S*A,eܒջ٨:tU%/KRY* PCRYJuP$[J͖Q+"dp|wN 8{ C;"_:BD g"x&)2C85oAyoΛz455zud(T5mКL%Z٩$l)5"BIwx{,_\nNo =V=T?c G[<=C,F R3~&߳7,1P `˖-8^8m QРJm۶WA?5t0i$[J͖Q+"D+xc*[asLziRz:e*y%ω>c GGƞUv-YtN}*|5WU4p>.ZQbG gmgA}*}Oogsf]ޅԲ-8J ]y?oOOL5eer L 0zY03xѺ+CO 00&~K$Q2ɖhrMroD*Y^'fk3q-Kv9b󫘱gYz t!#G )i4p@صc ov48h2x4[.) c>ud ^TAk2ɖhљ Jgbynpp|[}/8oV@75p(F^3P!> {8yo.zCACw樁}9p8CU%&`eF [_#`/UAk2ɖhљ JYfgU30x} eq?۸H^#mo@Y:yo@z7qFy? lo}N]oq*eIo 8 ^=!eoZzm8J2˛$[A= #q9XrN]f֮'sx_oqP:_ |-^XvDQ;q!<>{~[tN&vɹsй{wxy &p#^&[ HΡ ɌcNZt>)Hq~]KXd[鋢d@q9P~`?ihdD [kZ4]tV2 ~e:ۢ&r+v^zHKI9yHjN2<"2=n~d08` |%us9NI\]L u 9ncvQuJv;^/>AIyx>zsb7_p/nǒiS0}y::ႄ KIDzrthm 'v]Ζڢb~5e1n KpB*i0`3jr8r:΃gHuCzXx85җʔKCkj%GM^^eu!=CAp4x,^zDAo^2FN؇":LjPti[cbti-??=mKTwqgcɔpws-qF^A#!涫HZ_BwM Bq4֨ܜ 98W_ ޏW_PZғKŕK_']`sI\~? ;n&H8#Dt9z IDAT|E e@=xT1sӕX3gₛ&G o (,*My X_'g”cn1M7o,=5xp;cQFzlzz<⃚JHr{=xizB<7\9U<7n{ũ`<Ԥep*ĉpD_+/Nk?%|hqKм.UYdISs8I\ Cd(!YY[AH9Ȏ!˟x˟Dl}$4qzvb>%g'=@D_4ư+a*&s5E,eBS_c#N qI{q0 !wy03Ygqɦl'1ԫ7fA7r!'?,X(k[#O#OQPbq穏P'VhH7|1zXh// xZ:`\<>*l^A0z, ϭ ZIDC+;Sk4v"|Eܶ wn綿ж][zt\*.n9?+BzdS KNƥWKNN(^h+;kR~R߷ GMLˮZ ZzUpt4⠨ Tڲf&+1 8`U^nfm ZyG2ɖhhet<.6/.xw$PWoֶjcΘutzh??\|,^0/l9MݻSO`8X+xy\ub<a2y2#igF|7ҏ'0E5d(T5mIN%CgAOrԠ=_#Gl6[oOJ}{q`> |h44ԣ( YD?ٵ??`?#+py$=ڟYNا쮾~1~<'S9zEM6YYJuȤ?L%Z٩$l)XP8C*XqgIJo& MUa2R6hM& Tn:tֲJRRfkݻvaƔ|0[~ x@,lqHv>Z:{+Z%ފVЮ6"PVVvm(Y#ci'QJgڙxRlQcQ?9 z}k~L5e *LTCRYJuP$[J͖!@^)J+a*[۷/w=hmkґƉGv:$b8J7-WVeUCHå"CqjEr@?90x*Qs^h8uL%\AdQyi䳰MJj ee﫚v $(.!:Es na<-YXy|-#T ꠦ ZIDC+;ڐ $H:ޕ)KKO$ 1+cڶUUصo]GDG\ѫ@˱_Od-ѠXe@=z$# R*k QXPD’L |HH'VTM҂9eC!*^/,T)5 PLId-N%\gzH&Go%[IeZ/Az"l8jI|bS;vX߰gqFؽpYce%_y7p? .R3&jLpvXCTdC{4$(K.;&?Eǫ!D8 8_Od- !qLNfb}"u< =8 صs':wsNCeE%|!l۶ ݻwmނ⢢f!~[B ^׃awf9 7^H勲1HÝ䜋xQz $ӓcb9y7`'͟w6d0bYWyT\)&YYJuP$[JMކTTTՒ9z(} \t81|+̻o.ciVQЪ /C^~Ҏtz_ѷLm^~9cyԄ+:d2Eu޽?!***0}ƽ8eIػ{W,TԥKE'ΗŸW"K~ |(yfz rsPq`ltZJzh&~_w I<ˏ%smpog &82 D<q0-pŹJbsP94<#[d_tx᥸kЫWOلqftD?EDJJPY~ %ՊEŨ,?K䄡ㄡN$ [V8>l2l@Xix6ylz8vo闥J(Yxe;u5g{-Nxܨ(ߋo7oTN(;xݕس_v7W)Sف?мb,Zhd4@CxC̊juE98C`E noB޽(_$iICt9Oc̫Qضw=>uZm&8l6 ?Du˷/0ϭAqL{f62sӘ]z-1|8L44asǶf$wq>` Nu>`;ЦM1Ɵ} ڷ`hhh5\Gy1FtMxvq&Mgyv=J#.EQaKO A:ǁ>ʐu]K. ?ƍ=}t k4vVCϜt~s^}u5fZN>;w#`9Իb=)lѣओ?ņ#O>qtP(LM3g= фO~NƎ  5?O/7c̸{зo_z8#cl߾7A@aٰo>n;wĂ ԔrM!8h@DZ` &A'eaW4l0<upt.(Ű :5|wg֜g#VĶn7OwM~߁sλ: J{DzW⫯B֭;<.}F#0K$裏 ׯkY< aw݄]{ʫիWcx뭷ocŊ8q"n݊cڵܹ3rrrRR xvk]IP~PXF`hCyxxq=X҃y!,ln}{ 37WCJ1ճ</.><賸Aq;:,y\8*+kQYY÷bY]]t CXz<σ1l}ԠC`2444`a49Wq5{Lعs'N9l6c̙/СCb1bD4 v5#@g 2j<U}ǟ9 U>9݀V=VZ&tAqO5)qHeA r4z!Pc' qaNވKӯbғ7aku8eal'8'82➻{Z5߹</̞Ռ76ҙ7WaoAXlo/~׀1Ii5=e˖5}Wa{%8ôs@qq~g7XNnGmm\|?Vz_t*~~Nv:n 4?#9L>C Pd;`HƂՄ^D<@f%6 ́/sQ\ڶؑ<<^v܁r tTS/sx!\vWسǡcv82-"~x`2pcӏͿhc_ओ^s,Y?}>6> ӧ]!IΫKpcϞ jF ;3 u58o!j~ǜY7}{˱],5uV舮8:Z IDATOJ Je)AM&lVv*I7:G,+QS] Ǔr=q9qy.d,8|qknp(O:`4b6K.xm=/Nv1##M.))m\ߣGg ;a@ 4NϞѳg^ 8|ԡ|ԡh:N2180Z F O,SyfC o~EQq'T,C,:id-N%C Yǡuuu UsMDNA@>O48_ nNgݸ m\^X6Ǽ/@[DIO:b?iRENz;HPCJ,#BO;Mȵ7d֖6 ɋי/o8q5~ ,EsX&C}i0]DG@۶PQ?6ph[T M LhjAN^ki8Dۣ=j$I, 2 qU?xi8f8NΨV lV.G zHd~Ľn7ANJj@=A˱J0[,QeeO?;SԀ\e~[V\#͒Pbρg#RԶ-^Zn ˞y}N>iDzz^vkWLаʊrvl6Y[ )e?'\|RJ,#BW1l6Gn7fd8yyyyν?pmP^Q6mp3Чw/x<ιo 3#L~8M޿><ZU8iiicAݎ={baL<,f 99=D v & $!*jEmm-|mɧx`4qܹ0^/,Vkj؈?ވw \[nƍysJqz|ìN'݇{ޅs7y7eaojĎļ|&ofxvfؾ?_^rIx_>ښ)qĶ7Ɓpasssz HcO<ʪjLq/ =|3hjEmue\i556ua[rrrOADAצP%rHBkӛa%֭+vًE#Gg9 fs jҊǬ1oάr,|a74b#`Ƞ#}۟11؈:؈;1NADBCJ,#BO0{ ׃Z,^mڴEq7\6N:)).Bqq<^/6~ rsQQ_+}*r /m!|SᇗCp"Yd2}eA>V(..ŋ!ϫH Ai` &#(Aj|sLD#BJGUENLfθb]߫7A;3{ĩFx|t [l;R9S; !GBIA2ĽhXFkBz СCgXlܼ",`0g^*+ݻw`4:_{@AăeAA@qy8ov؁;vY8l90t:k2 7/7 $ R6/]rHcUҳ@TW&% fFAA ֦dHD9 ް:W"//999 *T|a%\?+d<=^|()*F~nn<*G3+>99 rEqW\ `87|@cq8XObaǜ8,4'Ʉb446`58餑hWn ^ǯيоC{d2). "~hrtJ,#B/x<8.YN7]_Hhl^^v{jEN`6rcNoA.]ХkWlA rRbAzb6rd Z]< нG| >Fy(,l~Gqqoϫ߃l 񭪤$ ǁ TopјcCyyyyZ(*j&;&x Fl9ɱfho?Sq ,@hAqk@ڒ~occ4\ˮjؕAD#SADd[ 0z49-J `0< @hCJ,#Boxk`1}PSW6AݵV P#pAY{_ I1āx-hTOjP)&6Adb/q  R֊Qa "[UI8Ad84VPZU %^ vC` ADhUXFA$@}aZP&x={ASS'lVkč$D bǁ6AxPW_P[];j+a4w㑕'7 I=) J9 L ETU^SclwCۯ?M:{F1`iP A-9   ՆƆ؀&8M 4uNkRSAzchllDNaoi?Ft YS@Nǁ APW_6p9 9Nmۢ hm ADk:!/7Gq2QrII89GMYA:D NFyTAd ADf׮رVQݺx]Aj5 W[TA"QƘ ?Mlȑ654<] sScK:\NYqsJ q7" TEYю`0g *#vp*)Y/S>AQ0NޭV9nVn.vm#'qX~xG.(S_e?CKTAz8f d6?~f`AF`Q^m 9q4jhJb>\WxK, " cϞݨD^^\q,jPQQ*Rܗ7A LAX_PTT?xulGb "򑍵uص{,9r;y2q\JGSt|Ajd2xp#Ѯ.O]o0`2[q`~|G(i_bLpAJˣ./ o|h&dTS]U͛Dn] B/X'/7]:y-nS>}c0poB.A Ws%l^U1MDL|˱2evٍCh6 -ߡoޚAA4k_Pc7_cx^ 械뇢v퐓UfsWFٜADC\Uz{nތyGà(A}1`8b_P^K B#xG^^V+ڵm vF6 6[l6+F?~oԜ "{𭪤PjE^y.)S!-\\;wᗟe^yْ{ шt=qҬ9sR&AXф!"TsA;w!77MMMx<+޽qDP|;w[q0+&iAAd1;BKCMuJwg8b{c|a񊊋d{#d6'^muUbAAqZѮ] !]Lv7}6,=35W]3<V5^:#?? ?~%yc U+=+AAᜳVZ {EBaM_E;fX;poiiiE,^/j*ϟ [N Bk^c8d`0|M ">V<8J5D&jlL BrWaEѪukL&n}hlhfw ?\-MD6ll!BADx<8.;N{m$N ެ]uU\˪몫*D@YAA JHHi2~{9<̝rܙI2ܙ{y$>GcY>R$XheXl9D7ceBQeXA|BL*۶{‰h(zHr]\&J݉is Dcql.6ɏ@1oa]7hhjpMömҩDx,J,!V!&:FXuqSIg+Hcbʣyu+סm`:?bBpصz<f[@9fBPP@ @TX4J<'M"kBqhHx-Oo%1i~8R!e -3YtYu]zx^jjjH$&!`gYl!* VJXtDlT-xY[|i9-Sڅ#6k:Z^jHu,_c:?`B:پ};A!&Dv*pHg466K1SA1|0'57gع~#[m4]g1VXɺ\W~S !!hM[_{s=t:5d=^/oڄ߲U)T5սS[n*+~so'|>"0u"f[o$%Acxx<ib&a:{u]5DZٶmn~J RS[K(!a:pv:;;|(ݼ>:;UZaAQN&_ջ﹔o~l} ?\>/)]7h={===Jai`b1ML0 i:T}{]u̱tFd8!`fV?V^YGP08ٗ SF~M NN-~{P3!AI'=IsAA4F]]sq5-4Zq3f`&---b~ B!aZs3x}\pӚf2{Rhaxmmm$[Mt]sS]u\E.(LŪ\Ho/`0H}HCCtUB+PiA|> $I$`~?YV+4_}W/S⡛B tHvClh8@ͲkNny[mVtkfu" L!I8" *{8>BLC2p)[-sNvΛ`dK:ߏrJT Owe,XE`@U08n!po**\鶷+S !BQVuri,_8M{>\W^\DziSѨoh`ٲ̞3FfϞ-_ܭHQҲrA"<mԄ,B!SZɡbࠣdQkjڵ|GxIϟ3;vre.bpM KCE#|_`QG[^=ߢ`na5[b:W)M6l-̄BR9G1˲?ηg?y\;] }W@4W0S_>RSH" `& tvv+t="taqR J} ̜58v2 IDAT+FB1"v[h1mM0 Yah!HiVW7n ._w׿DQ{e|xPRD{i3M,\ӉfPգW:4Xpq,?g)0o,0BI.T@09sff15={'T^ JbUtFwR<'"y['F.yذ~_W*hNSs33oغ۱!7waUem)e0 -_^yg8p3O=et?!#FUɶm2Y[IbutbʣIlzu>r\V7Tv!a -.Ah8@}7|;_770{;|XquIF#l߾|>/Y6vqllqlqr9ϲ8z1X޽{yYt)j!(RlY&Hr[NFӠt*k(ŵmҩ$ݝ 'L, krNM!**--.{qGxGy'yO<[O95v5Od~ΤI'lذT* 䂇e˖cH$dBu457k޻w/f>˗.L!hJCHRX?~%ǨkhgYS~k\Y3wJ18K?%˨kh@7 ^< A6wuquwN:=]֕-\RO>+VL3>cƈ.ge4/]VE!D*'hX.d<Μز5,˒BL->9'Ae;~ˇxehe kٺu 9֭#8ƊJ$! jk)uD#466UΤilj"PWWaU+T4]1r&Tax>ޡM4Cs]-|!/eLxm6lygq橧@h!heC:U\cNqSjO:UIQIQEF}MpX<-yu 4m^0fx YA!5rd `T)WUC!DvCiCA¡dE!hiٳ:Ҏ=suäunL@!R͎O<t@/h._!t*Ձ4Mx wөzګ|ѣm(W֩J)%22 ۶̞78]@SyȦ3ޱt21h|B?] lٺΎB N{{;K,2'!0򷇲5fq0<^,[N2'K9 dɲlڸbg+byxWThy,YGz-3XZ!tzVX4܅ H$ ue('NXBQEVVJa&M!NOqmM0=&`Njj466afٮP/jAGaqDX~?ޞQ%{q3µuD"Q0BQ=#̚ƦfXٳz}d2vMJgg;fb lA)E*W;l"X@ <xߕ.DP(4lJA*.>5M\~̠۴~ !X!ϛG(gDq0 @ @}}=GuM~Pm9((0*o${?>+஫;o:gSAǢ/\+c[!R%[y 464L%I-?+Т/=L:UMkB԰k*Hq]{%ox[UֽwyTbZY) 2/W;|BT[x$aA292e A Uzb+BCްܲ۞}{;Xhj\#^͢E;سooŴƒ?J p*#Уi7MBIOyd2?jk}OU&C&7ZaG;;T89q !UScBF0ơ|z̝;:0ǪyapYW~i6$oCр,2\Bq uÃ# *,FTU 2+<'IGBJN\rAC\cB!:a@~GBJ [*Xjw<ԟYt1yWl执̮]xiLAs\W$}-q rC!x1 B1Zzߑbx&TP{χzIy @:ǟxˇz_ջ*;!#r-!9̵!',J@i1$nBTPFUGoW?|syiO:c2{L>{T]XuϹ88A?}W!q]h4[N;L:E2x|"rI'gqB1A:ukrYg$<5k ?Fφ FBQ% %N QxF tߪZY{B4ԆkѺGc=o A5^::xєb9 x_)_!UTRD"KO P2ge,)TU%7\+g֌GbRp(DSS3-3f`|BL uƢ455U5IL4Ð.B1^ gʏq45a|^M Rq)eY^h/BUQ.tc⺣{\cNqOJSڅ#gBY]Ӱ~|5nMmb++J*tU(WJ >J&QB֌@S<]1ÅM=ٕ_!0LVI; ݻwcEjdb:-#-BTWEt]Yq=pxlzu#B __Y&aa8J:v6;.1=t(;`._!BO%4]' ƛ x6姽,^MץA.|0-öm|R Mgؽcd4'$BQ%" ˯|L|Yd6~Ok&T;k $`1`x,Yd2N"1>ӱɒeٴqO7W!D5U2 ;k7̖f͜kmU#aqDX~?ޞAf5nh^Hp bZUdRMw=/=7!\[G$%? bŊ\M0rJ>pPJ:@ߏ8$ R/rW!Di.Gts% Sk'H`.c%464,4MjSg*_b㠔"IP4@40}Lf3+MaԘQ [׆LS)HeVJkiX4Mt]qp8L0$Ͳw1BjfCk~|4בLHR8aX߲,|x'~S| !1J740&ޠ߃3P¿$6+CՃ:"&zNƣMs$@ @8χ븮i~jjj47iӦM*o!4 L3H @GB7l!Z4-3Scuuؗ֬x$uo) ԇNL}~c O à߬HD"NIWWJ),`vqJ- lllUI1&PqY1C<`cI ݋r8(ZVCTɭ0PZ\%Ͳe2 K. /{l6K2$L⺮8! 84)bq6ڇjW denU \pmq 5ơPxud2X۷sNfΜrkRZ\ו1Bqs]7`ȶQB5<)KdzjA1!q(5tמ|ؠcYX_w.*(q\}wJT}+V i&ZZZ3-9C6%JW%!hzFP/u]T:MmC#sͣK&ݻvՉ:iPE}?5ipBT21ºkE\&yľgGAiܔZ^'wUݩܱ;v`Y@Mp]7xx\:&J*ɝQ!&*qp][N?@ @*$hN0O" c(0*9NUҝL1c몤rql&Osg{7'pKPq7a-U0<@n샦iOb1tUBC83Yι"2]+%mۤSI }.k#Cc>1bpU8,*P>LH`ȇݑF[05 v$rnpRd2IOOMMM~XiŠ0!͢i77tUB3̬JRĢ1N;\2Dbrx  I3O>AMM zVܥbL+PJ {Y~ҩd*8(ʼnh>|sgVQ(7Ww3.nA9::;'Gw]8CmۄB!~k:.N.dH&ض=lB!|4N# o8PWWQҲǨkh$T[C:oYU,T%B0d~֭]gITFt [ -7E+܇T.@ʻ5k  ذa:aiI)5h(F+z =燪*H$u$~6lWi6~+,_?`b^^WqZu꒰A1C:v?c[^y M`^::xєb9 |_xqW!p]X,Fss3b8N1hh?p~vm}˙4ӈbkBQ]î`̚¾6[Xl\ hjje /B*R[?VK&*==Š`߾}DQjjj^L:7v(Q= !Deq5pMCSCTj\ `Y`e6W!q]E*_<4_qcFK +7g\553ƻIeW!q5 gYC |BTO1anmi̧n9s^n\@7Lvڅa(MJ!HgU|dW';!c ݔ>6/y;txxxD"jkktMpzyuz,=wR5 {i"'WB!QʨAW/饣m?ʱi߿X4RY8}{ 2BLpl6;.x ˲|e[\pG'G$(GU㠔"NNO#sfN7~*r]T:U/Ip]u`6V?H}%};b򙆁ݗeZn!|: =ơuA[CtiXo³d2].hU'½)-z˩gcgR=I!& B|^zr C Ve3kw{K7}§B4A3<4ՅG5uMMMDQ^zY>xŅ\/Pz?;(=A IDAT!I"!L+3933w+?Ĺg_|/0sQux7|g}{vsWo}R|~~)<ڲ#BLy qD2 P(8` [q]f=x<t};aЇf_g~2 ů,tonn]7NOO˧OO}:~TVbK*YBqhO~Ə6} -擟,*7>{c:w*8v9w.oYRdiΙC6_$] E}}>_Q٬au]Ѳ,:i%  [F,O1 0fV5 / !B!1p9wyc}V'">m o$.+82twvpbΛ㺤ܖَì9s5w=C,œkqpuZ{zxuZfbi]LO׿B}mb6\Î<sq44Fg}C?gϑA!8E àc?_\W|ʣD#f_z3f?JMm-rs4+=B|Xz2naӺ{tuub&_ݿ=W\o /}O} 74ßGzGz-Q.MW!b]̜5%˖cgY,h}_xNS?gvLtm?h:%&fi̜1e 5}mm<XU B4552cV`-B!O=w%\E4K_pyλs9/"i5zWw~l ՇM_ˮ`ǎ'?‹.ݗ]?8eY̜5˗Xb%y*4}++c޶ COk55|^I Z," ,fg]ׄy=454T=˲x+׶?Syp 'Dؿo5.Zįot*3oG׊Gsӿ}t:+/+/Tܷe|F:b/3'laҩ׿uݢ0Mltb2k~˲F5hhVviX~?qȿRB!}=]s-^xwy$qbb |x<' sl{ϜhIp !D5Dcq¡7R9]ɞBx ݺu]q%t[^x|2$E ׌8x,FMm-=݃Ec1 h4zjKU3ݑ`B:OGg% st!#Bj8laݵsq&>SO?}-ٯn^O7osqeCs4 s+#IGgdΑM0pJ6v&=ܡC7M ֽgK^^7w~C\ƷC6^yaz/FTw~/|S5]!NO$m̜.>ۇ㥶&\܊\glZ-P+@ssTB!-ӦK޳_ y@xCa9,|_챣IZd!|Dݭ{3̳?®={Xd#PaѲclƷ1Vlzd{dPL]cլfHt~yBTɑ!.|>\t۷lfپu.bib^`֠4 ֖=ݬx/t'dq݉ 7@mSdI!Bqsy׊?g3l{{%D[6=?À7&Յ%yAC(j"]]'8BqW^9I%BNRL$ke|rV\ z-)bNIc[Rd}*dsSq[B!BCA*4d_A.pPivo' ^: ^,t^bᘠp|lk*WڄBAźbjFC5y>( Q˛ >rlHR|4Rkc˲#8\9uy)TA!B1-4v\l.LJtvso !K8 ~3sL>=6*:o!"Bc"hz*yaWQ^{fx4j՗ sZ%bɽZ!Pa+GW'5~fWʀo;.Ӌy(ģ^Ԇ4Y`iU-B!bxCTT;`pŪ^2T}Qfo(zwZm>%-$B!(h?@{Gǐioz_A1nЛH3* BVx#ͨ΀[z}ROoŹW4=|IJFW<KB1#S ^?ji^{ UKK!swo7}+hZ[njjx6< AUԙV乚jj Bk x|͓UKji !pngfm'?%Dq793+ 9Aujٵxِ8 2WH<-8KO`n}#>SB!;wLKe&.,_n^:>mWU,;jb𰿭GD"WO_8?~3\s]w9'W"e Go|'?>7ή.I%N=߄V(y ٯyXJjh`qz DDV H&7D׎XBq(P N=xxg%.ܕiI\ϩ떘} }') 'Tbj*R JCi}T?J̿A> ,S h|*9}m~_^JQz|lPhT!Kjw4r@4r4pFx! 4^{BمKs_N_J4-ﷴLRߗV;Gzqfx`I/^VGI%GQAe-k/YuCsֻwaѨ H4??!iN0b}bڠWz.="%K  ryƒKS&})(B*!V=Pnpב#3N;ˎfт1BJr=8~Zd[!&|ȝ}vǝ0o pSeGڿч?D!ރ 2bX& ",7I$7@0gԄ[B#P&Mz˘u2 !9e8 ʜs$u f&Mvqb¹J\}߶mR S=$il&˄BLyY*o~,Oss~L&{v.BTr{c-$ܛ}[mX3>T&ǜZt&cSE~,e 4]J!͒ڸx<&8tb\W Ț@ rR k3EBqo~6oLMm RdY4==W~/OrƙHp8Hg2<ZLàTbT:ͦ͛A̜9ksHr]x"A*f`^l;COWolٌ70aM>~kiBLZw

f̘lXjkև;*UQw_zEso^!)^}p=Ŗb7w?]s5 F]kp\w]z9d]FU+ذ5:{-/>C*t6K&kd5UVk jxץ-xzVh1~Tcol{{ﻏ\ARK/e \q(aed3W^b%^y^ֽ ~=}+ܕ.~ۯF].% !c^|_r`˲Y>?)躎iXŁc.ʕXD"cNGT_}]-gq*0pH/ӦiZ8,R@?#^[Nd2Io4‰'t|iYV<]߲a8 u\A&}tT3kI $ JO݊)pXAj[zj+]cAl ]݊ڵ[* "*R)g dzOa5e5֠䕼M23γdɼ$>Ý͝!g߽ +W~| /wknWzJ&.>5젱}f_vYƹ-bQ~dZ4ekshYuYd1ws/˖/S[hjY..壖I!Zk_CձtZt(C1cC)ˊTxW:/](&^^KCPѣog>f/[~/֚={ٗCg~޽94jbRb~׳ifZZ)PJe<WJ%)l"S&[1tކt6 #-*UZ|p>7[nS2;S^۶TY! ^g0O]jhp ?n7LGg'.]:'^mA gĎmh$_fۦEIIIޮh@ Eմig2YLw:r::,y\N'hͶ^;v&歷fSOqY`> O;_06r_:=?ZnWz<,?On8[6opӍ7.N `BMM7x|>jzh;mal zV&dlbÆ /_c^}=Κ5|uwwSTT9H0(RR[i\)Vd6N879MYٳ~^i#(+/8&}0q\___¢Bz{r~pBo_W[֬(˝68 7wʲަWczF9A&W)o&sRޑUH 6L38.ʥc|kJrU秔r埡=L%PN"!<w%0tvx9Zt:)dR:dӨ!DU)-[e̟YR,[d_Dw)JKg?ӧd?[~z{(.o~yBE_o+n_N6m[RQ^v˕ )mD"U1)-kHpɮHc !#?bE4CmLLA5c:JR;vLO[RZZǰ455TUz3|i08!u_MM5ӦZW淋5@UU%WСW7nw/ԖMɟuL֭[ƍOHq?38\yҥ_W9s>#¡5,Lg-<Ӧ@tyTwPHċR GWgǹΊ8+cFjoogӓ[mۙ1Ɲ۲<Բ jf;S,fyu3,+or17!!40@k NtUEp?8>kW?Ⱦwlm44U1kWgm_5H$OlG~c yٺ'ܹjg_wY7pI!1]f#@4 ‚BX,r8p:Mɯ_}%^KYEԇtwwk4MN%SX Ȝsd]4t+é[EG0 {zz!>U 6R|>X(y|hãԭ BYlko{b"JeZb0c֥LRZF{{-ZGtWh9M p]2AdZ300L-+TYq٧/McLdd66ΟGd0Me0M vG000%3VY5[!ƼIDAT>=kf eOXŔ%]eTN eq!8Uqq:;ք#‘H~f&n K4(+`ZM-'>:ٙtii çrJN4 ,ۦ?SSSEUEOe&Zqo#5^˲<5rz F`8hBL4XcZH\_M),+@_ 9x<**qtvvrM5a6ay>ҩL).tAcVh4ø* ˲FD2  l!P,_'`h(1=' ǮZQcu !B!D0(zE5([Occ#}G B̓!B!HmF+dv翀fˤД>B!Bz{S0Tdz_dnn!B!.r݃!9BAq G:٩}]a*\B!B\L ŘB6V-Q8 [˚|SA !B!&Fab3d 5IENDB`muse-4.0.0/src/doc/html/split/documentation/main_window_with_midi_automation.png000066400000000000000000003047721404105311000303100ustar00rootroot00000000000000PNG  IHDRtlt>9sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATxwTUꦫ@Er%JfI1!ܙ;\ IEADJFP t?*ϩS?뜽WիW ߏͦ;$\KrI.%$?8v"iу֋͚Ӥqc`R 9+6هu;9LU\9NP[mZ31Ml~rPt?nƋձFW1gHk-^{%];WK"C9LQX _BWOO5Η >__;dIES pc<Օ%! –Υ4?^[w.5)JϻiڀNABd&Vg1 C8;]sдYs^/O HK?˽94VL8?5&kF\Tye#?RRU 5'@:)fϗv._}%>D#y6Ԧҋ #zKI[+ 31oRL szBsV㑵xƦ*44I I5%K5H ;`}>>N6m}ҥkTWUW,S; ʗg(,Hn2\Go Q[&u#D.)X+tm&+oCKt:IdN׮ҙ|R0Ob!_n$=3f x@'yXZ]S iVt 'ꋂ̩#sBD~,O:OL _fx;V!V0M@KXG Թ먭% q\"yJn7Av:Zw-"; ҨI ߎl4eG=dH{U:ӈ,C0;cw\KEE-[Q#lAN>ѣG###Ca^P$-OP2 ~tr%kBE_d@:ffk|}#VGnd<*ҨF D8ѦWO)6mTGXy5 Ღ`@=N<(3D"e2R~Ip86L0]*ܹ Z6oNK>ѣr M,z9WQA-اO∔ėeK#TƑ#`458T]S~Wv!άF͊is >:y-Zqm_|ȎYoTrw#݁哕ͮ];iܨ X'L dMuuS&0NBJ1*A\/^R4j cy 5ǪO~|b:T;А2K-zHH$A[o?ev2Jc~D/t/ L;D$_u19T1͗fD41DʹNA'ӧֵ!˧cV6wqFruOiTXv\T@VVv  9K\NA]>6#3o=Nɓt-T:{tw@ɷiѢ%G$dHZeoɃ5=CuaER/?s&u(^͓#T/F[օ|$$_XSpTLJl#n0^xhΌg- #*__L@M(amxS7!L/?єǟ|Jg "Mo2ˏ)j2'mHe3'sVp")޹I0OL~?'Ns.@|.Ѯ}JKCQ'OҥKWK;t`ݴhBSVrښvN/РA&;vm55r=Ǖ=tbּOh:mX0g6OҦ˭UUUde5$]kzqddt"?_~_b"'NI]|͸ ƖVM:sfUB:N/aW|mTG&T!o&N#lngZ'pUb:  :t[;PR’şČc#UNqN_u~g-)=}_spMtEo|hQYK㩣~>Zn;KM$`4Џ7 E[ |9à̈́cU9kFs9^Rd.nr},r7kZUU8YqLUUUUUgΞUG]x] ε]m[6oaŖM ]c()GVΝ;cuRZ}n3p4:u` +R~7wQ0Y5NäIeC|,ØH,9iS1}MKj[u~pщp.hd"vFqSqGtN';wlUdf:vuunvI!&-S"F"zT*8.78NoF.'333j_vcdf&vSh|̝O"ٳkw`oб3R`du"N ̣4+DO$+Jyժnzߞ ۋXe>M\ Ӊ]pksF|w6);v͛6qUWѹs]I?_S"@]7O̚ի_?JOoX6%t:Ԟ;4;wb:wDI>غfr.鷙@0HeEkjtUd7lJNW*O$κX!Uq.R:)IRޣT/",# WB_kЧwm@񈑜)?(ףj!>rwfqۄ̛;`0H۶>̾>]Iԟܤl}E}zq0v`RMo(qd0?I 9wDl2+fʹK;[έ'y|WpI;z׫Br^ 6&s6j̄'sV=II859w)w'ȪWD_2laVZtƙOmΝ;PR{KhC٩:;WHzL'O0zJD&#='Nrnv84#)Hg3u;!H^MVWm'ŧ $oTB|=$b&Ke(SIF(E($VD;iYd##nHj>/M6%7iByiNZZ^' ]Hqشa{v[n׹ݒ*%yԹJrGf}l޴OQӢhB'7Hܛ7n`Ν}9tۘ8w̙3Lr'^'Na{ok.:u;\4kҲ uX@N9!۹3gxn'//;vbOzniv{v&/7ڱdɍBaNAc(l5Cu3 D̵u=D':qK)@!}!D4K:BD~x"qt"';⛥FGU@0ȵn:?/_hkRTĎ;[!B'eV=cGCH"qDs[:qF[>D"uv*ߟ` SIQ;oJOU1*4 694Aɷtxγ-淴j}%{ǯ8B>|G٣m26o\륬d hۮ=-[ʩR*+*8~{~45eƊmuە} E+G=YQ/j׊>0CEBz1_u R&.ZPbDwRq }nρ}{8wV%1{G ^M==QݻwgSdyfLz'e'9~=wѺU_7@m=t܉oۯұN\5Ama 87l]2o[ʢiOJcjBз1~HOc!=ԭIDXǺ^+v,eW/ ,IJ|UND KJ;t^/6DU M4wm-AF%ٸn-5m?[Ω22338x(ʚ}T=РWw.f<⩫C Bb4b.kV~|)p: />73igޠ;ѣGZhaЩdʺ|PN/u=+%k˵Yψ-NZ/ƥƱaǦC qkctlѠiio` "Bɠ++K[[[cT__Έ;].<:ѠAfbZZG"`t,=#\!e:]:^FF Bux=.WbS_ O \YKBaUWV55S4nAɷכQԌz K@V𹍉^y/<cr 5[Ovh)v I$DT{qz1b@cǎ}vLuI`رvK+{ТE8PgϞ㔖RUY' ;'"7oN~~~m"L.:k8V'ՋY׵BbIѓg`RFBP`Y%z!]ɇu;ɡU WVT[(\r%.7ȫ骅LfD*+ǚ* }W g d\l4Mr5ΪN 瞧U9|>Μ=(=^t9994)*KXCxUIv„=\;}J)]I=*V"VSSϏ#݁33,uX*H])$v ֙Z`$vRc.h%͵J;u'x!D!z1f7q 盳%+-:sΩH`-fB ".8uMyZH\(9q d\}m1UpZw~/.'öS;YPDDwJ^Ml6222꯴F*: <4_Or5j٧W(7z_8 IDAT,QzzCkdPSB %礂j'%D.9I~z*X&՚I9N|{KvV ]F BSYx$sTD Ob$# ȉ]RC4AŌitMȝ)J?4VU)F~D:I9$ϲNN> zdӛmUg:Dͪwσ 7ˌDzkR%ZItCֈ!r&doRB RETZ AS$Ho hڢ 8:Ǐt |5k2JO#~ys 4 }*q"%i%%D`$w^srO*,4\1>V|r(mDL[; btkJnߘjxO hl5Xh$OWg` (|/Uhي +**hѲ?@VVC3jE[ȡu2Ų~?K?L'"?B DZ&9{1dD<@hHĤ E;&> `e0IrښR&BN4&񉚼c X'z?MOu7TaHp05h9XCo mCs]R:I?7Õ[)Nk3oh+{V$mgΜrzSٳgΞ%aKMM53|W^E޽y!'7KaN.lF0mFv^!R~\D1$w9Fq 2UdţlK_$}iV&^syV$I20UttLE8KItBNߣ'=ғm 127NJvB˸LRhaJǷ`N)-+*+iGklJ(;M)!1Zx`@:; #}"d~Xvʢgy3g$(<<3<pfsG#? U8iik3c D0HVV6/+jC$BW5)u!۷mo*]U{K.uh"Ӈ K|a_ݧo}-Y#?$ͦ>㏹Y`^גC?&Y&u/BUNagIGV*i6F+pw}7 ,PeWUV1Ǐl>#=`A_4ZRCpo^n`OL'FZLl6̝;oGj+O6ô ;ISN0'8˜U8%/B)~ ŤE5N_}U9rHOᎬW+m,f+@#GU+W1JfAFFꕫT˖/lRU5jXv, A4r۶m/nKr+FMiJ:<ɿO>Y`æ,=f og駟Jbɒ%>q"ظy%fFTc)s%(u2ȈoڲE4 %ifʤ3id.XDi,5q&oLkѨ| 0q$>]>ˊ#+Cggԇ> %YVMG[KS*9rWV|/_Yz5#F`U:0)Ո/jJ Cmjc,^DݚH $==G*}a;$== "8`*wN.]` .D QllÊyg;f,3AuUu !8a~: ׇlA6tBy6too|A'CpA  ^/7x˗-[nᖛo>U;ǿW9)--eȠ7AS<5}ғ'A|^/7p˗-喛on>`W_1ed+w֭[OAN 6D?bpvAUeeToǟ0|p-]իf۶m j_},t)7^7ϖ-:tcưv͚y9UVƣJΔ3hjjjBYSðC 0pM1߂ӼY3Xv z{&O#m>}~T)o'xˮ;U:չL4Q#FwQeee<4}:Cfiv&A][RPyz<ڹCqmkTkAGɓ\{5k *+чx0>O@Px9b˖-g,[-c3f4˗-zzCaIlڸ林]Ǭg!`uvxG1|8{۷n#+[ۢUAƬY,^qh.cFyy9t閞Sew)쫪i$_&==O'"Ր?NΞ;_j˯_g$==S`EL.r,O.]x뭷.k|  ; B׿ V(($%;w_>o|Wi۶~ݼ WX+^%$:]ve\yՕ@he#;{]|ђ?/Krw23u˭O%5jܘWtgmx`]x^-[6cubƍ޽;֭G/ngŸ׍F `Q:b۷O_k.,X0+_E~~>]taӦM_:6l؈6nD׮]^䷿-K,;`W$}D.Z7qڶo/"˖-3MKK#a6| -==WÆ|e IKKePSYK/Lm:?Ug/đ/(xHKT9M3 VL~~#CUIxj"|ot2amlܸ)זcƵ{nnf\.cǎ5EPڬzGɓp9 ?(߰q#&Lt2po &r7n۷il~LEQ Yj%).o̒ ZjZ:K 6dbnvsx-[@zurH6BqOc*N)sN:ō7ܨ:&]N6m۶! 6իV#\aÆrJŚΞ="z&ضl¤IqB`֭P9\ 80v5z \Ѱa'M8[)Iq_"D0TN/dܸ[5'>efN'SLaڵ /ʕVXIqq1+Zj*0.E,^!_|9i&\J߱cǏtr7Y!DfD͛7GǠ1c7PYY!S;-M7Ed-_3idN&֭[ {_؇df͌3`lݺU_`%i޳e&mnc۶m>a#0uT\N ; ɀT}wk!==#Fr ]*Wd믿r?~<۷mW-SDǖ(0NȧFxb&L@NvǏ 5k9r$ZStN9^ ؽk7| .믿^qCs pBs:tk׮E׬fȐ!^׬YÐ!CA~~>Wa̚.>F!(㩣)2.SNAA>^KSYYCNntbNjcU?^Dmf-[tXl9/^BfLlMuM |He/cNj0d6$ Mp:] xp9"5 e,Jh.]m6ٓ[+WǑW|Y qaC5knw-6n䭿Ž܋]ƍy'E0e~~ψ#5kW|~uU9ـ^}eŜTE])usr5cH $4xpxܫe}߾Gym۶3O>rusu|ry9@ 歷͖[xWx衇se}w}QF<أC^I7o~˿s3t jM].G嗟-77f͚ѺU+\.~iРW^Փ&EE@۶zƉ{mI~~>o7`ΧdeeQUUM^^.gϞM@ѠD=ȝ$l6|~?GBqrL97F"<***}:vv@fxgxi}ɼyse욓; yyyT}VD|&+V*rss)++U)Dk-UoG<}BQ@AAV1˧}ӥKZhIǎ[Cg׉гg/x/tҫW/jZaÆTIyC 2{j!ng4i"64~3gD"YUYI^^gLܹP>WkgbLxhhڑGxg(+C8N0NCÎ8V< f1rHSՕNy$+E^={NxBG+xolz21h.<=ʋy9 %2//v;oөS'ZhAyi߾}@fxx|sޟ7&+Sʮ~ǐF^^4-jGZ".l233uKgR/os}qxqK"۷/s駟 O:uĒ%q,YO5jԈcǎK4[XXu먩aΜ햰ǜ9sfŊs=(1|>[]>Ypuuu̝>{_6yhC|W5o]Vvf#Gds{XU֘c={6#F@gt.t҅E/]'|:a>}0|u̙=޽{mÊ1| @0h ϛzō@1~m-[칳;njBЧO͝fӧOI% K}:wG-ߟw5ڵnKNuWM"\s .nw}c>,6sF/ 2sR<8nLm 2e .dʔ)uҥ }!nw-(<>50Z͛C/kjժ5 ٲkBV^CMM-nhLv҅?Zw-a}eܹkk{v&O-ނ錑pVVhذ!_+ʰfZjjjxp7#|^.5kQ.֭Zxѣ eV IDATG1˰]޽Y0u:ޟNzTe̝;1cǨ4GÜs1b w,2$"]:^bsСCX`! `Р,X0oCÏՊ5ozm/&s[&sIKKrQTԄ\&݊r X}SaBp?_}~Əysw%5\CW\<cIti'~-7}$Aؿ3vXsxjiy !2_F/qVQqꪤl (˶-R>8-X@5'$y}.u`Í01l$q[>s ={ɓ&TW -E΂*qG!d>B?Yar>~)&~a'~0o/j5tBK&EX|.6zOJljHRJB` ;q0 yz=e^4˘iZI/ ˟ӧر#oBkhAWgM$)zf]N O$'$XSɠ MVF5D":ߤ-1'ȗ_}׊շ%cʥK2m0:x)5Ѫ `J¢L2r|8R3+i` 'SZBNJjifZ\uC^"o?WQ_rd_|AVH6V[KvҮ<+=*L(Gwծ+UMߟˢ@yr˹{.z^lJdpI6 %tn.CYGxHH}][$C@葤Kɮ说&:wKՇ7.=pȏJP 牨G¦3q/&y$aD~c0;^={RԴ.'N}k*}5U(jZ+'ٶm+)uՕ=)jZtё縘ʠ"tǏe 5&F.䅀9vׯ#{"j_M=k>>=ʱ#GXv #F@eRZzM7RXX@3\jjjشq&qBR3_R.]եy Xph?(,,Ht'|>ܦME}eS}m޼MDJٲy4O-[6s}iԸI| (?q1!ȑ23=|X;c_۽ݍQ#-- A]]NΔ (¯Wk-:3gs砬 һΗQ Um8}Ak }]lb9db;c^Zq·,4M*:W+͹vޓT%r^ O->j I=^ݯ*ñlmoԗ\۴Snem?iӢ_g2h\܂ ұ>`0DQ]SùgG 8z[ mq׹iޢ9۴Kn*bň#4jيחO3R$s=na'u2Qf-ZڋvUotk(~5ܹ+Ymlx+ϱӜA+>!%%ػgrYV;WRJU=AL6m@L'cƎ1x5ܼE+CQQqV>g2P}ėX}8p 843ի`;dn~lHuΦbk.?֔w޽-';>jmlI ZHQ"Hf) ٱ\@)bE+*ziK }f?fwnv 8Ϗ!;sΜ^yO(3ˌFѣGӹs=3'@7#^A٬xLCpäS4:$0!k%%F+d1"ehr*#"9وN%.NRҥh4h4t>BlJ @$v{l~:@ÆJ Ղ f" ¯/=?"́oa|ßQFC~Aȫyvm{|_( -\4,? U_t40lH=QI?uQj9ԈZ:;j4:<{O?0KE;Ǐm?)*hE@7Qp 2;th%͚?r)2%9999rՊc>_Dz}4]GOy8 IXc1"m.j噧0y]᯼Ȳ\>C\ڣ/щ Ѕk,F$K0 CX:O}a,nڸcGбcZP1ǑҪ۷c޽dS'O vZ  U2dRZUJ/^/oV~])&!!T[lvїA]$1` vl0p$ď,rt}hH;7~Ǹҭ{OxqKSçzq~UJ2|(LE z zLCdrH:iG*rV)]OpM w)- KUb0XNtt  л3g>O|E)p e(8w”MN梢 .ITʵt|v,sT@ο ^o/ZO7V΂e3os^* ].hl#Q#B xYS'N&h/O+s+~ 14:>wlv.RZZn ûNNCX[K+ƑtoE/#oDEE !L)I.3ѕ$(IEݎ(A:񥩫s5'4\*Hv &c/b9 KHFir I:-tۇd6mEɄfs FGGimII.ĩ*K TWSD~NKKs uiiiއAP*ö_v5I﫮M6Ï 6]M2[fӦmPJ^~>[Q)+5akA#h(-Fn6mڔrAOAƍw<4ueF#O= O>qqq+ѝMk')g/OӢYS=T2dՃr/LFϧ(/uBQ8gXHslqR,HzTz W Sf8vL&7WbQ䩿>˖mAP[,2_|XW&?_P\x͛=oV=AnW;'@}(  ̔ɓ:dW; 1H݅ !FtݻH1eS;>];~I L[.2ɎaTRc!Ks4 qNf_.dذQa1߅] %r>D#6JK p!PNGDD"t'0/D#)(1!A`,4J(QbDbT9 h<(!"$!;ՠE8zoy o{ c342`Srt;]\.UÇӼY2FW\\hfj)*.F#hA׻r+x5%5A:TIjErүoO["6gcEddwn Nrk$EEEqA?w0p@:HTT$ȾӨ:deecŀ.NFn1 Mf"", FØQXg|xMf.|ӦѨ񘓓G .._{8]3FqK8,/<0ѼnœlD^*6#M6嚱ccvrҽ[7~XVR/ /VRazwtb6[0ZDta" L 9U7ց N<2L (,c1+ec9pu11S\\Baa!&zwiM#j 8A$)Љ,6v]測Kj1YNE$Q ׀/]%UYsa:6K)`&Qi/иX_f}OLre:ʬ$Ei9=ncjuvF޽hZbj4huZvVss IT``Դ>;~W^^+_۾ \9bi]޾NGjjI߂eRRpA@8t 8̂VE<]CXD$Đܴ9:gRZj@=2y(pB&Le䥗_7dtԉgy&M1e KqOG!5K#Ε%BfPQϔ#PzZCqAFǏ. } ,X,lvkUe2q)FkS$!N9L]:MlslRHf&*蜓CA=_0UYDaDXTaxY9i"bS1ĥѨcؤDh`a]AfMhJKh֢9bvAƍEUˑ$A>$6t\TӍ$~;}Qe59_e:íۿ\WIs_֓'ܹt+|= _lӴQê3Ľz^Ά~bŋަW,x$IHf\b&ѭk'!X=7b|,^;=z III<3jќѣ|MTh&5kwS;vx_u>z.ȕlJ`F.̪:JZ(O\ʡ|׀R'lqyʰ{hFȱR6:AP꫷i!'O*ۗثZ ܂Xʅ6oñ\![lŠ<: IDATJ&^;^СCگ Ȩ(.[_>7|˝w̩{LO-8eLib^/gfܬnA@mb,0dvcl- " .̦`7Rj`ctq=¢7EhJDdׅ/!, s65RU_gA0py,$k߻Y: nb>˞,Ⱥd}VS1&m;^Ccv&%348Bt}}nׂVmiώ+.=J8[r!ZXXVNE%]=~l`2};fVIVl( Ic4I杅c Ox},E&NeghwZAǟE]FChP{Y\*C 7NbU⧘O8ߓS\3~Mq̹Et k*S.byfZb"IHR c^nDTrr ;[Y68]eq6=$(^;ul&7,CW;!h9s :NGdd$5PP\\J0Z) |mTa.Yo>! g0W51xneBN;xmkV&- I:L&:˚5ۀ8V!Im+)+ۄNfu?GfN (e DcWRN%9[+0s #ʬATRCw<,xE]՗ҒJvBݮm6^ƌ𣏸i٢yݫ8ϋ+OaQVܟW k~I58OTtrSlgTj3@d@խv &G2  QQXE +*G+NTJ&,ce EkǴ@le9Jb мy fVNc玜:y84hVu@P loZLsW]qR[+[‹L50wirw+c~ɕ}tڵQf,4H$7h%=Nَׅd\ޫ7CeŜ=NYfλK;?[:zh_J4ؠI`Oh}"I"͛ ᄅEPRJv3 ӯߟdff13k`X;z}v_œAP"ͅse5q龜d߾t$e8 (ͅM@F Q֜ 0Z-/.$qAN:d$<, FS^*9etaQzދ`/фª1PV_zJ"))dfdVEӒوp5MSe(kB]͇Z}lTE nG<5lؐxZ2aVb1Y̽{ 3|P#">‚"-5ןAmBdJ QQއ^ᥗ_a׏ŋSTϴ[n`/Qogҥ}A.AhA QH&+ħw[-heݽ 8fF)(MK()Yټ`ѣGjY\FdŊ@ 9غu PQ~Br0Q S~2~\ pEFp+@!cc3f"OAjޮcǎQUڎ&1ʮNL5bIiU:D糌 5kFJJ NYi1M6 p\%Ɋ@&J(_2HCAt;?%e)+V+&];vkNr`Z)++ײ~ꪪx^oAFZc M;J AFjD" GS@svOG!(ŵ*j*.wܕ}z,_l$))&?IΜy?e?~a6+z+{90\vU>Ɵ_S|}2G ra2`"P$m~UXX@IIqʓEHLfsr!hZ DV/ lpG/g͜-'~Wv5)Zq[$dYĉcҪejj).39D"nu:.{ h0$aE~dvQj"I2eebZ]Z2͆]: @ 9RrTB[( ȲL7!X`P6q 4s>Զ] % лWOZ%cKc 0Y,˴hޜSo ᄏ+1u Μ=˓O<2'k^7j{+ҿ_wWm?5  nlJW[бc'ƏK4IF#k~A^ׯ˖-+iÙ},+̜9ŋǟ0e$"}z  2ZlEZLn}x)WgEuY##hAƒVܘ#ZI>OddNq'VeeMFe/\(o[S)3Q*$S?`"vLvϚź_aHx7um\u]K)2(*ֈO?<:A$ đ#G9CF]P%R#8:cECVVNC56ӴIcrsrtl6.NXXl6UZRɤF]ZR̈,U/#Ni(m{-6&T]hyWVKLL FXۉE֫FXK}]~Cy)!2_Οf ՟q,˴ll Nwf3Z **;]~\84tj&TNTt["/7ѹT JI!,"kg\:wrarnf \جV"##r^5qGv,1MjsY.TT.ܫ=>Ɏה0lhuzZ$<TO.zq>ZKb, tal#"R8oO[TT qHaa!SNupj̅PCkPUq.CSYU(RPP@ln`0PPP@|\9_Ttq\ڕ6m."2*}.0j9v0]tԈ ukPUq.CsfUW&h$I VVwFАҚsoE?~zyMXRSR9~H.\h.}R/kWE}KïeFcy"7x]u!@TttȲ3O=Ŵ[nf}ڋ!2*vP@}?zQӿ*KT66}vVx'O_έ$.~v/K.qMҭ@vv6{aȐ!?T$y,[]vQTTDDD:t`ڴi "CCED]]5 tڕsҬys1lp~a}]^b2>C+eڴ?lpfu74toG`0УG~7iӭkWz-zS]v1}4vgT̗.nV6ḷv]mMIeBۭ@tkFrkU{e2e2_|zH/_~]Ǐ'!!}ȉ'jlVXXHiii;5x'i޼9o_+Wǀr˲qع;wu6VZEj",,(|X{/^D&Mx=̝dffȯ`:RzQ|Tg﹬[Wwٳ 8hŭoC7gs7ѷo_z=III̙3{e◀j2aD>cƌ/´i5j46z/77xc1o={֧;ɡqt:ɩYQQiii~20CNuN<Ʉ ҠAZ-111 <;PĚ8q7nd)L4͛7ߵkfc1c&;vcDZeƍԩ* )NHL䦛nرc[neĉՋYX, ߟw}>{D[fΘΩSO<3xjܱÏ0f5<.۶qz7ٳoJ^Y[A=ؾ#%5EK!6.EC-{5LL: b0u4k_y,cΜ9^Obb"Ϟ˯anܬ7lxۯj ϔ)9|빳?;Ξ=˰ayu3ffϞáC<۸q'Nbʔؼ?A.Hm εN`Lr'NbӦM_jj}R˟O=ɛo/bђ%<ēϟ|{ xȷUo@',Ӵi2'%%Uu?N޽+=^d2!K92o,|?ė_린d{˖-}v|G\r,[̧;.LDx82Lj{?TT Yfl޼S'O*s_uZ-FcǎފyX1ct֮YIY e fcǎG2x Wy y\DٳgXx1W\qK/]wl[ngE%t:-& Iٸic%s{,˼j2{?Zh1))|ڷo{*/ݻvC67l#alڸ1#DGGt V,]^RFtTzdO<̿+zE\lr+A,CʾWVY>&LK#vdYBD-ZĄ }N}yἊ Yj;wduGYaLfe}RSSt*n^}=FcX 3GU e y`1L;z˗q]wۋ8uUkgvA׮8s&_"Fpkׯ?˗-o:_SY]gf#ʾHFv;#G <, ~7п?o]?nkÞ{} I\j4$ɺ5 sNM1w݈/[~b)z FVV]Ƹ=Tj6/HfF&z¢"x!yiӺ8î]8qkǏgmE ̸Vf4mɤ:tC /Ě/ѢJ~g lTu?~_[;_|qWK2KllY'{Ykժ͚5oAe6l@bb"))^ݮ/ޝaïv]nM^^ϛл۶mwWџȤIOޕF,.Hmks9 IDAT (LpݛL_?R˟Y-پm;-[jO3_Y|PU$5%e 駟)_*fee} :hͣi&;q'|U>宻ҠA">/ (c߱qq(K}| Fjc0ʝC?` lܸg{//ȀVSlKlgEEGc4}]v@Nsrrtէ>ӧOӲeKN}Yx'2dK/T.tߥD3Q4Zm.Hm w`(OGo퐯QU/|U/o=1g6/~#GwƏg{W_nfgĈȨ*s2*5:t-[}(..&>>Jh`l6[S`82M6ekXfM@ZynVzq˴HHH4Rb|& (SNeĈ=w}7}E \NǓG|i3z5; (X,"""0YVk'htA)K-&N>FΝX ,xWUU]v_5ՉSn_dvJ|\<:n]xcP>TRPPZJ겎ve~T՟ylȐ s(K/W|KWS@WyY‹{Q4_xYZ~4i"~)6mjRTTg}3n*DL&W=Ö-[E&Mx v֕ua6Yf-]^T"$%%qv;gΜ!))f>cYj?eJhт͛cXz5qqqdeewc2oرcC'I"\;jՊ-Z`Z'=TCyJX̞3̧`ѣWb?09#e˖v]Vf&)))ߊ݇+뮻lyi;3s2mttkM71T6S1ïL6#G2euYo1) ooX[{AL&ΘN~~>̜>̽V6kĉطo?&Md\**d-z2ids=K^=<ʚ䒋*駟1c:\wu|'{=!.Hm׋P;GMs˛@o!..+esn]JJjC?gΤo߾d_XfԋeUUUr|gQ֯瑇^zoSh 9|$,,\Z':gV~R >y}='(^9 Q9p-fBvU\(>P^꺎e+B~UoQl6ިL.?g~UQ)oJXmh'GD}DGŸ9ߚr˴i|-L1nɫ=1暱< L>.]m\(PQO]ՋUԗruG~UmJKK9r($sɥ]x>s];wiIkӆ:8d:33͹]cE?w[^/I>@,7kV! -B=>TO5BEv']u;Ӹi36l&ef 2iPo$ItO(U>C5X1DYf0h(. lun'2@c ԇr*sԋ5\"9&5MnVpNp 6d 4  "AAxO|܇ -jW \5DGs~ZRzR]tlLv8_WzO՛𫊊Jŧ@j"yIŭ*m%^=P^'n=/%n;M*旷g>=|Wyd*o{U[;***:ro<#އg/Ȟ# Ar`W|y_)/g=Rv5_J\*{RxF^)fbx ;䎮[5 C(P\HqG]ų&S&inߣFqMN5kM7 ;+۷1h0"#Oѽ[JfoV^^W`(**"""3iӦMaرc+WS=1c:qqqštqP0b䨠 6.Ýx.bfΘAÆ vGgQgUe дoU"0^}5F09 EKE?SfQ8PBQVDQ>u"***p<ս*))᫯__Ir2eJ*ygT@믿[׮auz׽/zzӉh4u6~'n7 {_g),,b͚5Jν;8KPu`"ظ3,STTė_~_[5O0 ߪEao{5|A tH`gBmUY?OWbhUTTTBX0?C#v&o+p=߾}; l*:u/

/ֲ{n/yӧOӨQ#n5nݺP\\˯ѼY3nڶvW_{V-[1iDaU\!L4n}zhe?7|S[g֭DTZhǏsǜ9~^dB%%K>m:^څg|M/_ҥHMIeCsן,\<̳cF>gqEQQ_|%:ur=[bڵ_^+㡇aaa!j@ʀF,_;de^o ~{ qwytx+sfݗ_S'Ή?璔 2^DM\Z]1r#GfÏ2wEiҤ 7n_%!!-[zw݉(˯zmKJ CQQq~HEv;#06]^π>S>}Ǝ!>{WϜdРAU s;r0b(q'yyw=.ݻbܸ F޽{ g e@E&L@dd${"''nYY0pK<E ^`w^0ydٳg]+n3qA{.G:F0nXm^SQQQMs/^Bv0%; ?W^y~뎏^I'^CHBA.HQ4П"EA.ʋHHP ((H `=By-6g̷MٙfvVo4[ àcǎ7WbP:.Xl9މe?=v K~˗Soݽ{J3a\r}VJCVVsC+->n"wAmNܼy?"J߿Ͼēg2OM&rW+ƖھAn^6'XvXf &NBH^m999xO?ju֘>}:*UЊ .\z !͚5[oUّPnsÞ-[z2O{k$/?_FÆ K",, 5P9*V>}̙ӊz ֯Rq0o߾Xl[Ntt4n߹#i{괩< | I*F~iJl… yhغmu+0 dd}*_9@D?VsWbccnݷl{mڴQ~(*%K N8}">>]t={u<ÈGΝcǎb[~}@bƌXr%^}Ư7۷o믿-ꩨ믿={`믿bرʊ:^f3f}ʦW\ݺG-[GSlM<k֬EFFܹիW#11I1`o{nXVdffbݺux}әTZUVn+WZhu#'';^ܿSN4icƌ;+ $..4iظq#4i,)[L\rvoSM(9rV/J*đ#_K.#//ׯhdeKAʵe Տ}+͛7`XcB^E٘7o.U3Çꫯbĉ~?n81GŴi&O['f3z!hCU=:Zv-Ν;|-[={/[E)jժٳ'M3 -h۶-m۶|)[cƌqݬ|LycƌERR"zꅗ_~s"-- ªU1z4-[СC{])}vF6 e,^?رgϞELL ~;@멨֑oի_1:fsr'\M!:tQ4*'B(|ʑ#11=ъmըQʕòeT=.s-L<_Oc u~'9V9w-멨ב%''cVQSǏRعk7g{bѣѬY3 Νѣ1uT?S]_w_fôi|"sBhYOEk׮:$СC! 1l0t ֭[~GqQ :V\^zF5j@ip5ؿ?ƍш g+@z*juhDl:| :t* Խ;ݻ7}рm=cذ0ƍQ}x"vEu>`ʔ)5kV@+svT_~[.<Ց{.dH:tԿ (O:*^*JjՂ۷ϧwylB:1$10^:uP߸lθGVo\qWtСCGNV;&f[-߸en:3Uja#P[y]d`dljs]v!,, QQQBdd$N(8E*g ~KJhJ&t'جV<:x@ԖdBHH9f^ONN_a0 eT9-)m*ɊtB 7su>hJJ%?Aٌ6®qѪU+4?<PLw-IΗk20'I5 D|ˢ p:8y8: M0zM Hȑ9B7d}_~.;w1VWcG`HזeMЯE3޹Z{OC[a9u}m^Z>x MaYaJZ9#-ݬLz5fNy,B1δK ߾ҥwF N qMmahEVvf7R4]P_ SΡTt)TR{JdggcӆoqU\p'xO 8O-Η.Bcfpx+6=W_Cǎӱtyt8 N;N|~t"yn*Ēo`PrG^?cxMkhܷs3 ?>$N8NmZ1kd|Բ)4muTux!~z B##Ѽ]{wl?w^*Bl1x}[L1 ׮\Lxfn^~-ageFX/} wn^ׯ233!lY`𻯜>~O42Jq?&oݼi#_m` #cN /?(.;G-g#؇g.ӹg$f6a-M{[?۾(;p8ݧ? R/j쑖z v)yuvvyp5/ #_[k?{OZ*OXR`@~^;vKf~G/wBCXf}`xtp,|}̌ HNLDCek߫7vCݤ$u^`w8`0p?K73#.Alן~: X4h̝Áw+xk܏ǟAnؼm;{kVyăݺco׭[p9J'ԤRɥ3?FZh6IHXyapI=tes?ږ$IF"FӒ| :9rf#<<\BKnx|x^[Kp|yp:@!iO?4p u?dbm6t v3`Zwڻ3)$ڵCvx^N SOCފr= f|(ɯ?\| ;6b}`@:uPv8usW_ ~n=v |<~:~ -WK`w8܄װ/'-T >~I?䱿]'XB?sX:mkPIyEt/i\xuMӸF>[1anW*V˧,n[NV<χzE6TL,#:.YwTL,\zē񨕐mkVc}S9}E 5\ݥp,RtMFݯ!,"q6J#mDDEQ %jI!"KD%cKεR%O2*,̓й7j-Ⱦ~䎰1ཇ]t6֬ `ԨQ;w.U.,, +W^E0f v;W)}\ժXصa=JJj*WVy y965}[N]a70a[3 RU7:|5h07m?oGvemyF#qf o?_OĶ5QY3V'lY\IME a2eFXkF51g$T[Wg?*6ÃBTt4 -@] 5Z^+Ruxغz?܉;w`v= x j%&t=l%`P͖*Z%K0i^fyi`:Ӫ݂ݎs0W=$=vYmrk p7vXL&;wÍ7`2`20zhB&aڣ/ٯ{{6D>}$Gr^j`C`:ŽJhtBWX.:>쁜C(j#r8p5t:jl6v9>+F?~/eR>EgA$bV9{/MUDq tߟ N DWY&?hkk޿Y)ERY׸U@KmluU2!J{NK&4:+`BW*;M⅙E|YlQCq*UN…h'- QT NW5 Z!B(8E*g ~KJh((|Wq +VJnَYhT"44@cв Fq*U@z]L5BFt2EЩ)"T:;v^|#oj_P"}˵xCFiK4ưG_K ]C[%Ar-J{xRN芛5 vRE -/J(NeZUɄgbN97vV<Թ'-߬\B|=dԫ[Ӯ {z:w  ,y:ZV1hY`8E*g ~KJhhw'-L5 /mP~3' 㫥еK2f͜ QJm_dJv%֎qۄ@|tb]S@0V1hY`8E*g ~KJh(Ukl2xc 7jIld$]?d@ʕM|EX{Z6G7&!Ϸ? tF_*51J 8/Ж@ӡC:4v;H9w ը#"2 ۓsTLЇ5?PlYL NgbEQ* &?EjӡC:O:c0`0Fj$93KʗGxX >ݻ7#1s8nү/1gmT' 443?|KV}xq +T%ONJ::tQ$AtBTt {׭C5ʷXt8aYXv5jBz7n1I&|w̞5 #*,Oܹzy{DKբСCGIaޅB`\jR.bW>1 wQzuu Ǝp^6 1118VנǭX0SEk"a452 vd:d_ oСC" IY9ض}1iȹ'Oc>4w9 Ǝ3gaYh۩'0| i-슫׮kX@po>@CzСC{*҅G@Hv2{nf:iZ`4 leVhѦ3n_U16i+?l_Z}M)O ݽ 5:vs'@cв Fq*U@z]QPqYT#.<7}yhxbJW!7CQj=J5AC R:As]b#4%]ޖ돿_ zwh@me6SYrⷤ늎IBʟ;; Ԛ~:tСآl%mS"{tСCabi̡,ʂ%&s({Hk|p#O //J(NeCߒ6Z@+:=k ^ ǿ+!( (nB+, vz]hobbKRh -Ѕ'~k]EdIQp{7̂A'xqHJ$|*~)x! mcњZ+/.!b@3J>BD܋>czMAA]":dT+s*N~&O`2H$'q52l FO1 -ɾ͐&O֒\I3#HKm 6SjOB–?䐨 `*Pm/yԏҤȎA0;)%8dqEAɠA\'B@$ISȓ+'/) txKDy|e3pg@a13H1<^7ni'JiG2QnBWʗ!stȑ99HȵJ2)/۶2JhO}49piLҳc5Ck+IT,H&4LYQ Ɓ83RC ٌPXa:7V=Ў)y-IT#6.neeLJPAaN5#_q%>1ЉBɓ;o@x%TObO I5|g<|LIru9I-%'z2rj :N', V+FjɇnEHHUٶ }T)|mv~6!fe^P`C218TV&u,BO`60mPcOz,77#J=8x'S7A"#IBj VYV J- 1An4"$$);櫩 I⚳3*꫑Q/_멊VIˑ6|9Rp3ɑ:qD;J>qh @ ډ OJKY$#MԔQ(ϣFT99]mJ볩J7k>8Ms+mZ*+tv3E\33QUfNfTE[n 52b63fyN{aU8闎;Y(UeἘ|Ц=a[qx&w׀'TX,u+II0MZ۽ѱqY 'NCeJj%&tJT#%VI­ ΰIM·$sɒ:y.âVnBqDrN@d1|)!y?H^ IDAT f/=-+˱Z2<~Q's M2IG6g25}90zb͛(;FDQx~iXpV:p1+#=wlxACI1ǎl2# U>q*ġ8v*{A |+kX8MF,z1Q!h@C]\v׮]E K~dF:uqߓZ*L& Ν#)HF:6QdLIBYt)~uFԹQ [$}/m 7(Ѫ[Jx M ZX9CƠ,qg>,T5NP't|qoD\R2JfwBO/&(!-ƎJ5_ܐmv;^zTS_kթ3OqWv]xusw:uqITREl[/, jEDD$ԫOPe[PjF'5H,0Uk ik4m`ѷ۱j6o8jýcuȭ\Ԩnvv6""#a0 *yd2!<,Yْ6׉\.)Bӧѣg{ԬUJDŽnB呟բxXrsP\Yߺ! Vv(mD ųjIH$HDA$TEs"tY=~JzP v2HNL `Jc5ɸELL J~\9Wٔ #3t`˗q =q+Vl„q8)@D91C F'.([f@r]~ ͜3>7&NK O;!C#8'mڶkpQ=&]b5#U/A>Jϖ_|J)(een*%,C!Á(W;GVV&oNFsw~%.;o 6`A(sw9Jtx^éOwAnn>1ك)o./c[e/wwdK?Q@ 1#&܈܌ qiߺmwfx8Ndedl6Ò/p!%E0dffا_xj.\2ς Yԯ jPd$bGړ/I°#0\^9s0ͶfLRBۊ7GNиax1^|X,C"NNW(QFfֻgOl | ۡaB<ԣ;۳ԯrhn믾B)l6=F !㏀WhѬ)Kvc3ѵS'4iC ⍫݅ ?'fy+]'/F0hMR.op'6pժU$n'U۶m  ׎ϫlRkL+x׎T>%U,Ch2ޑP^F4bR <2Q`~]|^}Udedf"]L8 -̯{WJL{ӆX0>f͙իVc;+#47񂰰p\>!C}b՚Δ]VI9Y-Py}N{ P.5og񃏢\ H,V8W7<WHjjAS"5L9 SE;y5>]2~>){ev+GA׬gbUE)'unb8} gazC)뙽s׆+v͚7%rjq95 t:s[.C\Zkʕ4ׄIJhq)+aw& qݯr 4l؈|jddd`SObwoDFBFF:Yaq*]iF?v {3>BZī;S)؆̏qM,_e˖cK; `n޽'YV͚1nxhذ!h?7˖t (+W>t:() EI'':g2|>!I %uǃB+qPTR(+#SZW*plV:7+cĉ㙰xOz]j._lV ;vEWXO= @},VHfXaضfZ-cMH NwedmaU"B'0mvbKpf'!pAԏ܁ґ@MZ-l6m6ja6V+,V+g[9KoWgdOyr#!-nx}z"N9o^9J U`D3 t.Vn@sl^ x2Y5f f2 |:{6F}YYY}QԟZ?ʕ*8sp<]`?&6h[ c09s@@wЧWO ovu"#B&:W=9|cFBlL,~ 9p:{҄zS[iQ|90 PRE5x"ܹ AY"Ø//<v:xcdk7c0z8rMv=%%~={㝷_b a5%KTUb=xqAeKNmq*' (Q(6eԩĘίYXK`f ͯVߟE`W_*+bYFcŪ0s7 l[-|ػw6}Χt=uJ r%iR*q’[￧Q9x٩褿Bl6!,"׮]/ߥQq:qUl6S Se}"HDr2|~%6|GF EBd%r|`&J<6$t@H֥]@mVCM/.jEbR"yZGM4yyE~^pg)p)wL>%M͘ a^yl1 "ifXE.3"9w+͘!/q8@r{z*vhڬ8JB` L5O!r<]Krk>2c$EN.r"e^iiƑp)ghRj _-[ԋ̼Û_#\+9f3x)T\i/j(gyo;"f&lBhx|Gc5(S4VZ .?-a[{ FCWj[,ػs ~}it fܾ}|?*5xF0Oxф'N"66FQ"ZBYxXe2Q1Qex]Fb8>e8!\YorG[z!lR/ϒӁ^ٳK/aw'okV%?y\N8[ヌ{BCpZ ?/|X-m6^v;l.BPreݻViZ?w?nFժ rA8:j[׮]+ڬH+Orrg/W_.YP6kO>蘜 f\^Cժt\j N^pMzxn]OCpv:rk0~ }Zq%*o& %PeDdNK䋲$d`4NL;} -\.Gfz$bcca40Éc޹¹HȿpI}1rs7`0\cmjy?֮[ܻx,ܺ߮GY'N~t€r,W.rs³!>>Ndmp$E/h+CSFU!SILSx2d }/3 YG8y(oF##NI|{G2/uBFq6rs\?kϱI1<#aC޴Sq)qA8m(,GwU!c⍒>ҍ0/z9ioLNEzz:l q}1xP6Q]Ni^Ë\Ozf~AO?[P^=]5p:vs>Q]|5/U6v E+rK,l6IfxwI^22`DO79DL HȈ[{WcBR;!<))[=<$%iC2HI-H̽j>7 ڐ%q'yLJ:l6|rE5Ɍ0DFFpWepwDfsG_]*mKD_`PAM@SU³Z0 BBB8un}_1ZmrN 䧰OVU"eAE WOZ<-_ 5N\P|dNO}OC\zJAGB#qje"rzRSI`ą .7]VZ"_n_@0 ^bы*-:d KKt뎁clp%_C19s%IGC8U6ڲQhGL!8bWr"Y:",#S++M>T!(HD%M,T}'cdiOKJc?INpSū忁R^o22(/ʢ>AQ^.Vц  'VKQ7jƉu%,!-̟rQT\dE1o5NrNG-&$j dFBD5)\<`f$V&.ɉ-VB82*Ԓ=r(GvTFzOI;12嫾]i7PC U(^U/|G J2?I'Y-s#rʼTDx@z\J&n~9%C㗮 UYO&Iކ Jd&?LydiUi ]jvF$%x|~Bb(|鲩*~S18D(^)?aP&RD$$Lؾ9|wRF3}!}9͔M|KP!\.XE.xU i=~zPftEM'e"!/!]|S&n.)?ɛ*ZG1 p"rFN">ђp**7cb!r u|#t\-(hH|S@Wة&JƧwd&wT(%HQ(Plc,Mh(d J<G7+OUTlRy&Ttf U)Kj,"&q~`*d>QDE=y_T:~:rFJmlQΒU⇾H22vO1i 2}b v;6 8vL*Zc ܰ Zg%dyG8d,%  mP}@'zqOX z|2_8!aZơ% ,ŧA @8R"ySl L I&%IX"WP {uǖ4$v/;X+8X IDATΥ$Ȉ'5`rZ ;^<,-JWDuѓgO]WIo > `3#Hi+DH&u%%p2A q*E8@HtC+&t \A[H)Q&wt$C8 M@{(r[;GԮqERry"1)GPۜl'֥tb@UD!$/3iS>IPUQH7 /E#|GN!r8 jQ$Z{4}bڴy+ .vǃmde7Z?jy I ==~Bv퐖va 8@VíV/!J$ ULB{aAhۦ w튷Oǝ۷Uؗk;0o\mlQ<~ >n]O؟J*\o4]u(T򺕪^2UV}Z+bEtG$ q9|gW8Lup`isNMt/^Mٳ @}vv31ȖKH|/|ņY=Wo!Xv-y|:w[ E {o»d'n%toN7xm۵Ý;XlϟSSH`M_v _v i fW0[-Y^BFoLʛ7Q&K2Q+3KDwҶlytEu rf5JP>l8ܷ93@^2nܸɝ/N'tWbK9w"S'B`ZѷO_ؾG~Y̌ L|}"ztÆSĉ^|7_GdB\+tn[7ob옱٣'F׮fcm߯/틟 p~ ~itCC%!#'`8}Kڶs}kۖz? |i[nEp: ĉ>ȖCamxWo~~ھk׮]xѱC 8R}PVzݺvőGŖ~@׮]m6Vݻ8|v8 h՚Gغe3=wǎmظ/^O?ݻc{Ip ѩCGpN!7.@rs%9y ''۵CzzLNױoѩS2BfT(_ƽɓ`x/cxa\r Vݺt͛ѣ[7 ;w!lt0h`[8N6&)|?ԏ/k۷ʤhy9KVz\۴kZZOoI[ʞ{>-s/E!grl%Ѿ};,_ gϞ̛7sxgwmDWhDnn.Χ1f,YY`!j׮7`С5kvޅaÞ@\q,s4HhסqEw>+VرcdW`֬Yx7uV <WI k Vw22|r4j<.͟?XnN? H`4sa7n-ZLL> ?9s i$-rˣaÆسǵRxYW_hĉۻ 6DreB<]&L… `>?:u ]d7whڴ)ϛHLLľ@={ƑҥD=ww[cyHLL͛ѴY3̛7Lr={7l EDA0Oѥkln;j[-tu ç-Zß8|-cs]SmS񒉄D(DQDA^҃,4RH4$%!R{E6ԃ_ Vц%fRtF|mΝ.N8>}"""=;(gKl I EiT Ă@γܝxSإ  1BBif-cv73%~p<;<=Y(N}s@P`ذ4l̜9 Ç߁>ЉDB8y$fΘ B{ϜwATbذ;pZ9udd$ߏ*=g3V#yQU]/`% 8{,vJ̜5ym|Ìe¾Pu cǎ5ֲ af9z'}@~93f`ǎi`0aMzt:|( 1:AgΜӧCPbԩ,4?9́RY3qI4ƌo^3{M7QSSHkG8~3e^>J%FVs}>s O̞3#úNP;x`]ve2숵J/ƝƜ"uT*O؍iL2UxWt~2n GW3ڷ-Rݿf]5miD92rhjjBHh(h+zzFQHQ\.~RWz_ }QTx'ѧOoyw}y~V)_@LV:Әer 7W/-Btt4z)c{ M5YΤ5 }hQXx1œ˞ɓ'|rl,Vv sܞ_Gp3=+k>c[p] ,@KG?=͕#DGGڵkHHH.:Yu3l\wccc8 \! k29SC )vnTv1b9#?U wl^ EV#JAZ3&@mm(hu:jjD~^xDD `RC83?[bYxؙi?-C={H$LeVSJECT ׯYlaǜ9s3EzD`%ovA@ppzoR:SpHA@MuEQ__g>HKKgnݻ!!1ϑnvȬ={Zʼ='QRDcchf!NMrɪGeUf_#ӹzuŁIGEi7:: n씜{c?)Td :kff&o ---ؾ}ݹ7(++N;T*0ظa#R)aÆhjj޽{GDtt4JʠCeVh4lܸ jiqǰg^z`;Ŷ̔fnK3Cf5֯_TjmCSs36o֘س>.YwNC\|\{7wdž 0i$ &NowӜd޽~{m(lǀif-Xn= `9fl޴ټx&kHl޴ cƌa}_~%v܉ֶV/ /4bMo֙uZ:ퟻuq /elwDnfqsp8Ƒ]Ǎ{h̰v<8mՉuEಌ7j.du-1 >S5.x2Ec¶}E sAZZ̀ŋm?G> V2y3tzd]Us?o7iJg: ]vE߾}-?r daӹl9>v{###ƾ%>ӧOcŒe9zwh 9z#F41n8L$ OXp~āzua5`-jEQvg'Lx봇Nw$m#̟{IqF~=ݷUd>^jWDV#Zo^8Πc)sz~<ճ'F8pSg.:CGNuq꽳Lp/_rR9H!VޙϮvXG<1Nq|mxCnJȧ6Oo:mSyW8o.:N7imE'!1? m_GW͑7;)8bXbye'/!Cu{0H9b&L-8s\I3?7F_T G,镙yjz">!bȫx?Uze%HHrq|u+ĹǕ'O@cC#X4ͼ_vPqqhaB!dJ_Kz=JK -޹nyCn qyHhmn<0G{ʡr^yqc:{ODmJ!!O_t?_ņEǕֳs"a""Voyn.Ra e%%C;.DEEٕg WXkO]YyGRE!.6f477#;>dbbbw\= C+0ѮXBOK#CRmHHiC'34Pd]>rެv= * |x܌cǎbРN_@9v2-o|B"Ǝ I))[_ёj7KHL w!)9 uͪ]YXRx IDT )1AA ^ALLȗ$j"K0)hlj\;,i"T*ZR FsS3jG#JV8Z.]\oes%OïRmyK-ֳ5Փ(m+׸1'txD(3^3sNYY)L9LJ Z$ZJPU?dL uS+D'M""]4{pTWW4 DDD@BR׮P/MK hɓ1;W}ٹ$cjU!/NzR2 -Y*+--Eq522҉Cu\aRJKNOKMA *?m!#-ͮ3BRI ̟/fS1|˝+WpA0Q*&R)aM z[V:%AqB$RHecKcc#.׫RE!%9 vu uPT\jTU1=@*DTHNJBhh ~eiU]S*c˄BnAYEP[WGDXTr_moqtV ѐʂv^ArÆ*"}8eXUIi3uvezb)tՠߢ{ NA@X( 5Q4=({hF~E <<H@]]=]C~n郀 FBƻW)陖gI2PN]jzfܙZdz˙Kf΁ L{r jk%=N[。礔4Ʃ߱'!a{6ʠF $_RRPo(׫jͺuݻuȑ!l̉s>>D"UD«7i#G\ ?q@ݙ^0q!'Gѕ=wwI\=f_׉0p@ͭfq2^ȁZUxyգ;Rv)_HT*ǠAO݌Ys ah k--4Q5uy|pc)4]ACܹ! ` j4PD,\?s?FqAI2A4ً6=ztG`@ExDu/MԔ^)OH^+gdr:i υ#g/5=ӦWz奥7FDF0R/Gt\2:u5Y·Yَ_rj3gb?0r`7ωm?* JNAhh(8wwlΏ}Q93OB(+)Aq5Rz'H2{nhZ5t]N$W .Jki  &sP(H$hmmEyy9~Wn`\rHhƮA^EJI)Ə뺮_~E>]ރRt>Nu}v v_{c mnڀqžrH[4Zt ׆kEŅfsۖ z $s a`5z"!*)*(96n`ϟM驩P@բu% IDAT{ qY[ׅ 9E޽s%]XP[łBtR\r-d < JV/CZtz_jоݱe\:bo Gsoz]4/>"뚜ce F JBkm@c14T-694)>!HDT۾:\GbBFju :-R) $ \nqb'|>W8sft0?ס-\={qr KJZ3o\f7-fO'#ob"?sn=u:(zTS0aD<c󚔺qc ?FRB~,X7JH>UUU;VRAVMpy;mr M%p&A8vFzuѬ⺺zAAA?~5@q eQ(4f $6$z\\yz=4 jjkQ_uK T*BCC؄6mkV@0yMEBLzftc(#Xr:eB0trtr2t:h9mL{̽s"v47FvB\\f͞ 7~vGəK);奙c6׭z\ɵerpl߾R+-t]֖q:w9_w!Up,t}x/3<˗[jhh@Dfؾc8B| 6cǏ[@w.;'OQqCg G8f4+ JαBs{m[dBP,6t cF@:m:Rt:&L!C ŸJQ*@*BӢ |gy< sSLJ-%=î:wvFX}tG!ɐa/#9q$|Ev3x u(u7oG\'\ ogy{G=.qb^Z^\rh4TW31j_~%V^ׯw[kltiZԠeee())+Wlt]vM.92 k?+>#gXCHe%\1_UL4\%1 8HlC#(,4tN]%h Q!ё8sJ- HL.DB ]P(ЀǏ#&3zfxy= JvWNh։sc56G=e|ʕLytupaa!sfKm #ٳp?`?ojDfg.1.i\(*tk7/uux_ ǟȿx~˞x\,G%D;Top{VZZPH$ :u Ŏ;W EA" * e(-Եw^ٳeeeݻW_!?SСCjCII @.cڴi.몸ZiUUUFUUڠP(lUqR.HIIϾB&;祚r(++ɓ'!Uvux.}/WmZre2+S`eEwP~cWm3,R=hY(hy8Z }AeIаNSyYyrR9n[DS^$Y@K\$a lQ(dH QhmmExX8N D ɫ ^pcϜ[ʁ';~}lN=1c6^̟лSgLZ y~3f෿ \CcGc.!3iޓq*pثy#ZR"#= y_ ~t%eਞzMlbcbQT\ V \xw~zDFFBPi \z]5i$"//2e ״L|M>gϝիWgv@ 44+WСCk UUU`0`̙hnnƚ5kJl]O<f̘+ŋ+S'[ ADD$ 0])HX?/W1M q\_k>0YLaw:E"ev㖾Ϡk?P`IhiÐRBKK >yֺ H0o< s΅^]״i .4wWW }BG}Yٺr92{,X^oיܻ\ E׫~  3HLh7|/vZ_n[bece@" @ : "ՅH8[yc$ Ht=IDb^`rHQdka~|wBa~`[ 7 .k}p!frowu19sw  <6l޸uuo0ԩ-_:z7My"a4469T*Ѣi JM%5Q(;`&VCu3@F AMu_͡i6+SBQka+|1Aʙ*ϕ0N=ư¼ y99/&<-{{'A0DgsB1;sC{T>1;ueeeH0)jt $R)rdV&w\_cGgA"?jv;GztN˃pرg C\\$ ***PWW [=ÆqݾauQfs78= b@YIrFv)d$yE(5f4:2C cQ ( 4_FFL\k?kkkp`>yl<( G E3Ę3j^ M* ;( r8mB:|3!BnV#@.?Z^EO#sg475!$$J( q;s \_2 wA&<&&.EaCѭ[&NzI·t9tf n^z=jkkT(ѴtP(EDx8 7[WT\Dפd(J_sӢV#@.Gqq1ks6 "<w5qDʿ=:ơ dpsaz`!^ͼ6fk$]nAM4jT*ηK.(+:#Gf'M0qD)ePb~hV=@ 4wDvz%ןP(PڌHAq!!PR$''c_/EnD'cy>p1||2J% EV'OׯyaP^^'ObҤI.K;_[}MUU5֮]SNAAA޽;ϟT_=tv^',~7 XضmT*K8pLnԁ+^orI;i St\ +W\TdǏ]vzʼEׯUEe׼pg7)1+sjx0x`ao駟O(:ĢQQQׯ._BdffV[[F%0!׼|8r9:u[j̍o9 3f7߬ԩS`)Xx Μ9kNWUU3g}9\vͮ.*++LXszwx;Eސjk._3CRA*"44cǎ37rV3g~s0klٳǜԩSXQL2 ,0=L1mڽ7o>]丙sΙ۹c&Nhgnc뫰p#Rr˗/cX֛6i"4gCSO=k׮a s9wޭ|(,(@TTƌJBjkj?zAaR[[SN"88\.GT*q32.555xᅕ:n<y;Q9^_1rpΜCg|1 HJJf+/_ƌBDА3?9NUUSY.QTTC8.`͛ƻ+V,ۊ?МnOЭ[&֯{Oڕu (0iBvXrRwKaB!=j{˗^pT ZK.>O<> -bxwT"FK1vs}`qw}igXaa!.qp}Mw?=,4 {Yր ³<6`C!,, )zT<zMsFTTWU#.kﯾ 6l؈'=* >z444 ,<4а0ەÅD"NT*n9ʣb#l߾8E!k(dok7x-,, 4L8)Ϝ9O>%%h4,l!!PռˣqYc⤉P(~z*Gh0t`y j&Od/aжq aG++6_^ ?<2N\~y 6λu[,6W,g0!xC7ъU{a꫽V1re{`!n[ ?t(5 /WlذoDDD_7ihh02 3:?\gݻcNsc`{x駰~:L>W6vsBT疖0!xC'1qDAAp=w#//WU=Lúu`-6륩Ih΃+,66M͠ 4 zSn';GF{̝ 7>N6M][]Eh ǣ8;H\ KGju0(عsv܁m~p[& Cgl D]f(gΏx5k&6n܈ݻw شi3q|O?C#66ְ`~}C`˖6AĠ:q;L,)+^ P[[k~;úuѳ'5]tAbb"Z-[sS߾}ZZZmۏõ}e Ν;؀ 6~vӻ{47aٓO⋯C١Bs* OG|P[>\s5Lސ|9s4MHMIa< O_+xA;|A (/}r!Lސ|GfڊGa0oLpaby(((Ĝ9a˖xAw.Ԗ-[GqEn <~wԈxxPWdHVkr'k׮O̙3hllDpp0 (TWW7DAA233AR`*Ν;#a_GBjSPbϔ?թ3~232+ ~9t  0tst:8~55RЗɷa?mfO۶{CapU8~̼#٨Gc9[HKKs[NAώ\Fha(l1bbb @QP=EQ|zW]C>󻦦ȱ[azf`6,G\|<;wʕ/8R[97!GJ%A@@݅@\^Lwڿ 'S&ass5&poq5bccѣǭp<~y֞=m9yncT\J :6!8vFB"4$ǎڿWW8d٢ğ{Z[V'~$%'CV))53ջ#ƚ/Վ.6,{ wqu`z"##P]S?޷END;\~ NT&EddK=l?v΄T !!E{CDd$g8MӨBBenL[X,sC@ wJ#BB[ 6WĄ.P*[BS]=*eR!00 ~PȨ( 4TQQMG7w;zfq6@XH$DD#H1Px~ŧ( !(0Pp) H$jk=ϡT*kS^]\|B0 dJ #HT( ހ(o/00A6@?od񽳢6˵}z@ \uTHG @\xl+27?`Wz] .ː@ :2#twr Jd. @ @$FN޹g @ k<ʕE:ACQ[nz @%y@ [ZM6)̶% @ {03̰ۖe@ =ѹ!"Wg@ 5Љ E!W@ 7O%#!@x̡k@ @ 8>t@ BG#3@ )@ 9ο @ s<БD@Qsƣ"=l|m@ ?$_9tEA b_B!u H=#xi|m'gk@ 8{,tyS]6n܈d2KXr%>}`ɒ%Y"̛7{F^T*zj$%%LjF0]1~@II h?xy}֭Ǝ$ӧ1et cƌ?klزe_54o&֭[gyk֬]|r_3W:˗//ۇ~fmH=sZ 6@"ĮgzUxt:rwٲFYE%O=ΝSNg+|jg}-™3g/cbB`ǎ6Â?r'NĀǎU lڴ 6lx񢏭:z 7oFffͰAz~ٳg7zvke%ŗliO?@V}b]vT*^VELLOb1Ν_շF`?oڴ |^nfu^֭[&MZR\`0`ڵx'pa_ذ\#N'凾ʙcc4M7O@PL6)..F=|ggbPUt:,vHeocŊf{b_OЧO|?k|?nq>RnVZҥKzfmH=6f\xpgK|m'}mS-֐z:v^w 9tV@ >xΡ:@ 7 P[l Ņ:^q2yd?1o`"R*4L¼VMY|o`Oi6˦h 4e|EiР@Ѵ1} LZcFkRM0364Jum21 v1̘iNDKAQT+h4e+ެk 7Hs|X?loMS(l# ʼ !3EѸis:3f6}XݨTչ0}Y(ź|6b.rg`N$kLdGS&`-NjnAU0m黹KYX۝8fc.ƾ7le`dXeo Xj 0eG:v(6U&-^0h^0ʥ)cj+֢Tvg"Dc-,Tǎ"&&r H$Pe^kЫg/z@4;yCe> aQ( ݢ[DtµX , lwRenfQNs";Vg"8Ν|9-O9Vj;I&Oi;z??˳xu 0oR/K#;_V߭rIcqʶ]%iyIYɃuYs}紁wCyu@ ^s1{u+}VVqBEyøp*O<~y7sWu']|Yfb7^||<ʝ1ĶeFx|egz.F;be|9S_}ݩ-b_eVVt@a3=ti8J i>s&(S㔙@ t@@ nhΜ9 ߓs4..ax~~8|.u',oe2.>['璏mz.Q]5iƧ~;w"5%WԞ:2O!{s?k@~Ν; O?aI>J|_~%5ᅬKϿ@*kD؇@ Mnn.~]$%%a?kexBb.xw/HEw':@7l@hh(YnjRMnB\ @ pMU^u:HXimmjZ|lxǐyh@ EڢVf{[k+Dy}b%x{/gݢx*:INboumks)E/ÓxY$}Oz( de@ c0@Ӵ7CH%P_ySplzL ^3gpy4AQ`vU( 8X^={[H=7`-οN^eeeuT@ 1 pN<Lf|mO\'NDB!#;:_<;J %(B"!!fFhH(SBۺ**PTTn=٢@jy9xrf! _ϥ:I\<=|ls)ΎZkWBo0ٻ(Mv !Ԁb99Z,'HؑlX.vγz* ]A4Vz!!fwf~ldmIvvgךݙgyff<<$Il1* ee8Q9ɲ > 7$tnrWQCoFC%ʅ$K((̇drRNATꟉVT^n,{ U\+[n:ǠsS=cQT=$d>Pedsm(j0ÖxS授9qtb̀v>E 7""Cg08+Y!K2*/Y Yq]Roؒ ۷ow Q]krn2iYaDnyl2kTq 􈈈NpUWIR2*C(\Ȁ^^`,x宗KgŗE$pqKo`է:.]f|̢^+=25,٫ǏlK|p;wAA^׼(Ym˵k!8wm<O_\9gjN9| 2_iZZ6~i篿rѩk7s }k^۷o+55]YQ/_#/},VbF**,@Nvrιeg EE@Sa+5k8tDDDD,?\eF^oq&""u̡( >h>6oقv pC:E1#"""5)_o-[Gݦ?Rаɕ;wEзo_|ݧ.\~1E@S:r&"""hxgh۶-^z=q/*ZnwfƁjF:Q To%]DGGYP $ ̚|jU[^L"LDDD s/ш"H6fAa[PVRRz\;Sٳg&yz'߆ɓo)`gΜ9S@5~z=}-/IK'}ާsb+Gir>qmmybԪG Pdjj_:ݻ?X)--EDD_Wnn?5MZھNDbrfTKgJDDD %Sy`Ok(V} PK疂矈NCw4&W66oxMk:_\ާ/_I{+##K:_Z`ײզ\:gv^@^w-}MϿ>>g./e>C}ԏT.u?j\SYo%ڮ$""{Q^nʼnGy6mۦYV| l:Op^?{6ɍW0w,pw,zt8} 4Sg@@6S+@V`lDEE!:*8}m6VRw:mNQS! 0!L&#VϱCNbSKA6I ̖r( I|A!]E\ RSS}B,(.)lUE-rOر#0(hnt#""" "ID}`{MŜuv:`/?#'7 >iOğMv69smk_6^KiZR6^K3T'9}VyyyE޽`)/D~g!11ɕϩQ"W Ѫ/Ʀ ka2RPMDDDT9dYFaa:vr3$I,ر3Ξ>Ty:?k^ 0#""sHtCy,y^uÁD!T `zfl 4@lf^!D*).+?n\""""dYe(&`.5CVW#!{m0XCQ`\9tzg3 u r[ pY:S~C Wrz5t󌈈&NH/D6r/@/Bdd$Ƣ9S%99""" O?YIi&5[{˵DZp۷k0PQ/_#/},VbHQ30h֬ڵo֭[!+;۶lO>W_~HIivۣYf0.ᇝHv M Z(`\&’2th0X-% &Ɗ\1dpȑZj/jÖ$""" 8NHDL=AjLJ r8tDDDDK架:Q4C^ls%""BqMe˹\BrJo8y^^3vAt&JD3KI޹s{_Hdt^luD!l›a9>|/MN~ 0s~ Ԙ$IBfVz I^_KgʡK$ _u5ZiE|~(8{N䙊\Cˆ-_`̧xA3220x`׼,XItg|M-M-]rZ5 2뛗_r׼|s֬EKdddYN=vtu9g? EP-e,f^oNv7w~DDDDUi1ÖXm6""qQ7VI, (+-AW5?"""j ]56 pt E(.*)+^nz]`+@_;?""""4XszۢB E(/@%^}N(*.T)?"""jXj"u.{Tt4$-%:>,F⚫<#8XDDPPPI\7q$\z?pՕWcmPZR2?"""S©VC'@pk%ElH*:1T}Qlܸwnl޲=~7̞S:Ef $Eqs?p#//,#/?w:DQ, aa.#""şsZ)RPyVo=K.^ÕWM 9sWXi$SO?Ōz gϞO<-Q(Q-S*U9Tdtp=b钥طw$T#Y5M^.L4~مos~UJNNƍ7ވ'NuYE: M=ꪫߟpm{_j|/-m4ڶmӧO#>>6eoEf믿O>v?O!SeW||<U7^àAqYNz~+W±cФI"p5xʯ*Nq~WCBBbccquՊT4o˗/GӦMѦM|>wDDDD؃[RSwÉ]deeQ߯9s{n;}tK.w}禆s=,Xl`SNŶm۰uVZ 6mԩSٌyĉxyNwnTUYg,Ү;`1[7zY7esϽ1w1v ; 4s#M1!`X!&& ,!:: O=$I}ee.sY~E(ŪUТE Ǻm۶gϞ CDDb"<<)Ps/u[\}͵;sY)yfxtYMOĶ-[`C[ܾm۶e^o'NΝ;ѫW/GPGDDD(LvHgPFQcN`5ˑ̾{^'?"жm D;())L&̚5 qqqGQQy|Z ࢭd4e8{,6]b򊎎B&IhѲLFj+GyW^y%k!==_~9[ee2p](*)S`ʔ)N*{1xZIDDD{mYh!~W/sCE&Í>936"?"""j<.Z"EJZ}@ixDDDD䍪5tZoo)Pt /|NDDCӨб38 5QFPW.!ڎi*:mRE7IB\|" :Ȓr%E0px_A {T~DQDмUk6 r}83dE uz:bbfDYq!e~8j/4^aaK1ED"v(3u8J jQ ! * dZy$" /?H3T JE"i2~ Ȓ6aDȈ @ bW"ՋADDDK\!)3EDs8y͆l߸ F`:HjZfPE l6!*< :H ku^scCF?uǡezQѱ[70' 8~=/,Clm=h jAʡ8qQ&)(B"AE&"5>yV8}iRn7K.Ų%ѱSg4m^؟e: Yau$u%"ňc?_^͟gO_SsʹE[#I( ? ǠTf7$ NQ! B"\(yR"rr8 5X=CW&΄&Im6GoVAA1i8r0N<%/S!ۗ& 1EQP(y6"?Z ]m+Ql0F@&Uu77;*vR5CMvIJC^AaO|v>=Xyegg#11QQ>uYg$m6@rd6a">;/B֭ѤIRT(0FڿDDD7'NK>K>jRSo> *(VԂb@wpǬك̙|:QP&KcEFDD$"""-o Z!7ʫ:%^$U %YheL7݄G@bDGGAȲZ\`Xhb5(eLDA֘Oui,fMф*Ym`,PQqǝw" Q!IdY,ːeJE`W5lD!vh<6TSDeoCnB|ifjf_6+&:(@ɲ ](E05;m-iv9{Tr|mr--.36v B!0("W4ڃ9WA1LH&31`C DDDtir-)*Dt\㳠` ˕ MRRUsA$KnpuqthN#$""" .xmzdkz^6Kq)KRT+v_><ԗ<_}۷{{/lOsE@=OO_ҵCݧDDD~vRA3N>&׃cL&7CDY+i(I6΀ƤI:a`/?%HDD50#]?a-Ruajr VF ͂]""EaK,Vj6#aZHl۶n vQ|'O(:5=ٿ} }nAPRZ Օzd MQ50DDF]E@#V\xP+P5WF :_zr0"zshAeTnrui󔐚xͩ^Q;j"\ùx9{y圈QcgMm=OmRjܶ6Mv1z֨)~z=}d9ƌ\?#OK0jLKxGwޛ͟o7^W_nõCaƽw8!/8ӏxgZ=r( DQ[7;-6,C.]i-|m+Sٽi׾=vdllw|>=xS9EDDO_{`ꑜRaK}Yv0< r2:xm>EEEu',hs/l9Uv6]3"PZjgqISZo|=O+Qk(@O/W?b SnSM_-J$DԹ Exa1oͤeeeDqqb}r5hڎ ]KzGK!"Οφ(^8|$%5N:~-((@^n4 ,u*Þ=q5",, =i;o;q>` ѯ`1x*7H krUۆukpM7cE;o۸{a2vpV}f ԺG˺՟aIxאeK|5|k?[wLU·TQ:.@1QHRqTm~Y{ߙOSx'qO;qT\|5ӎ3RF:WUڮ$"""rM1L-T?JcTt?dQ014+WTzw3'O%[و}>cA7abHr.,!";+ ,շt:b-HK // (j"lQ-`_)K~A᧟Brr2 :=E @F$dfeg${,բu ~߽WgʅKj/?C=6Sq]s?>ǣw޸HJJ No0phKiG v1{NY@`ڶm+@QOW_k5^9##4R,XIX^; P|?EmU?gjӟ>}Ir׼|s* 222*v}~]:-5]]َmo($Xq^8z-8x iich" >py ìYq ,\{/>"""r,`5mtGl6 1aP;nL6#ݻ`Qqc(|Λv9jdff:JnQTT[nܹ@\"""X֊g;?'OlrzޱϾ<@DDD{r;E( [ۮS...N?+יL&#&&糳k_5{ӧOG~ QR\>:EipDDD,Tv[7}^>[ ٌO?_|c]NqFbժ<Ӻukn 6+V|X9sFi=Qq`Ю:u*/xM8p n}cOoĉгgyt?FϞ=0j(L~'""FQCu)ͪILLt;F]N`|@~~>ӱcG|*}b .zLDDD䊊\]5Y3g=JK˰| t%E""""5iQ۰aaw:ࡇ vHUZ \ȝ//wZ(J8eAמ4dӽ?o$ 8tDDDD1ޜ`/Taz\jeLDDD`WXaK?sB :"""ǀtDDDTIB@ats=.3#"Т;g9s&N:t={v5R} IDATz 숈(`4i6;o:vM6a޼y8<ӱcG`ǠHc]fփ8:شi^|Ex}"`0| L""PSD1`ǎإ=:t#M1|ٹe 26lvȐuZGDDD \[n8t#{r3;0qDGZ}ݺHKKÆ k-VZMs:>:tFHDEE9y{(--0}ػwvXI购uDDDM|ϛZ3t>vU}ܹs1m44i/rssyyyܡCcܻmq֭[c ƍסkΎ|!qbժ.:%ןȟϛ"L;Ex~7|toӦMskҤ tۣ[n}(..[\\_ w}7W]},]¢"DGGكF C?viG#""?:7m/w{wS;v8Ɵa|7Nwyyy(**BTT|mۊ,5i֭[1vX@II#XtQ#PGESÖ#c>|85e [-.ܰa#̙֎eOODZZA@DDiQi.tpU;78v:F0NlQQѧOt 9F͛7Cҫ:"""`Sm:OJػ镘:`ȑHOOG.]jÆ]cM:_~%&M:`08ΕCg"""8?:ڻDj\5>n9r$F 袋0g΅0k׮qΕM>g"""l(~\*?)CDDTVo]-}6Qcq_~(h;j"(0D+Q]2k(L!Z@ǡڈHB!XzMSDDD4@jۻ:>CuadR(,WA. @*A ۈ8ދĊeX&VN_"oε iI@\A(}`jPmY`2zbhDe S`[=PbeXˮ[wA\&W,g,:zA 흈!PՇTm-w#""+r;獐R'Qh Sq""" %DDDDHqM`-Q- 5m/jǠmۋGƺ54z wޛ{> uWyCiM[KZ??Ɵ-Ztk49$"""5h}h3;E4,C M Dz)Csǃ<l|#֯] EQpW`܍7A$t~G@Ll,&Lv;PVV g_x_} ލ>}/1iO@vvV,]Ct5*Z[Xrҳkgd>lWf#]݄-=X]XLEֹLȒ}乷~^ ^y 0E[7olt[7bm[ѽGOuz=ݑ (**D"66cHsjC=6S LN5&Cy,b|v -)Ȳo4K:>l6튊w: ?|\ 7 >>_V))|>|^*^!+uN/rW?"Exa1]:%[Xh! .o8vMsQtϙgO_\5/_g뜵Ji Ji]s~kΟ|MWscfr:lI _y\y|U071pp*FG|$%5N=V޳g7Z_n>ȣ{ɥEgCClLDDDfΞ=oQic1jt8tzY27muϽ0Lx=ng?q5bْEnnX7t3&N,]B\kcx=9}y W\\\ߝ2ۻTmI +'"" k#"""J1#""" q5Юv={""""x>:Ws)dƌמBQ%'"";3t_]e9 WFSQc@G՞e7"""`|tiϛWADDDys]PU+{g<V^rիCy~Q.C^Ν:cNj\5ѣG,Xб?B۶mxǸ1jlꫯ>u*.2|Y\6lQQQ=t"""m7h:~SQ|c`41fh.?0z(L&1X/`0 ^5z([Q.ٌ5kba""""w5Pšcؽ{7ztFΝ( JK1qdGz\ƊEEQQ(,,t+).FTdcs_Ӥ$m?W\7lذ}zFĆ#"{`v$9-SI@F%Klƒe0bǺ8,_k׬5O몖766EEP`bcccbrp^06- }Xv-҂r~"""j]QSNx7`Zѡ}{Ǻ}Vb`mxɧ\ѻwo[f^޽z9]z%XjJJJ7_2D={6 Ю];m=ڷk6mRxDDDU0m^ Ì9;w#G8-0a9 'a5喛]n?qmسw&NÇc u'M™g0qd,_ny<䓘g0eTDz4ݻcǎQ_}"`pV*j(5N(՚OݑdQj>1g:xRRh"""lVngN@!ŊbAJְZkW-s r&Iߏ9sg4y9Ѯ}EYYZmEt:^Sмek=uF^)r)v7t3bcc1qٳQ^"" wLK>Qu h t^׫fEIQ`ư%O]""k9~K͹\e"\fB@:ƍןo*ntيycODDUQq "}N&W"""" 9}6sra2c|F\^{"" E*>B$|Jp>'>%tdH׸Q 48y[#aZ>{Cbc=n=(_A◂(3ǀۏ;1p (--sZ@I""""[p\s8z`/lV+܏kA! ٹlEADDDzR8V+Z^Gc7\Z>R'5>DDD5e<EQ`1=P<\.\+CT? AQY2sg[۴O=.ӧƺ}ǟpm|{$=Ǩ#k[n; kƕF1SDՠg-ܦtӻwx׭P}3|0]e@=#cFi_DDDԸ7E ꫨV)'M#I6m] Xfƛoc]ڸ( 1qlߞSoǤ)S;( ޯrp)}8y{~O=[߆ǟxYYYtUi\;01qd|5n^SQ3xQ\\kڗH^X8֭_+SS1uC`e=hZtnއbmh׬(--űw0}Xtcݐk3kNcǎ`t /Z\c08vf> >Ng4GyÏMJ >`:vKjDDDxN0[,1|״-[Drr2gqqhݪSx}`q.+F^ @$d]CU?`ȵ_9bL&]wSѣ/..Ƹc]?Ęѣa41juWGDDD]vWް%T vHҏ5̚cٲsiڽ ~ӧObx/&>^wK&..:޽{ѭ[7ٻ;uBll,1EGGȧ{e^SZZw\8V_$""jTk-Yg@Ӥkm6n3t ]vt޽b1BFti&FysǢ8sîò+`| ~c]LL @AaQ!bbb\#l jU?a X ,[W9ðZm(*.bֻA$<0c ]'?z(RRRRQ#Gb-^.ַl-jk̳58j;vق7zV /jX׫WOl9,f ֮]^={'>>?3J˰fZt:_}`͚u-/1s3DDD$&„_| YYx()) '7/*":o;#?Vۏᢶma2T6[pQLc֮[nNCDD԰Mx$=Gqqv}v`GϽzt@UU}3 8gײFj 2111x4.GDDX"€~WԄ Xǘ&K>M"XaB,>>_V))nQh9VfO&{ob݅E"T|RT\qn;6 R( Kq>(O۱KgA(?!T{mEPr"""""Z;Ec<rvh);+o _rE1=#Im:3O(% 6lµ_~]ADQѧwomQY6E$XVX6Ȳ Qa0a0Kx l6Mhզ ;<|!yP#G?D0u:n@ѶE!US'2JJKaXЩKW@V|:p#"#" օM3c(+-FiIQ ƭ7Z""SPTXO?YCh-I_P ,<͚5Crf?Xiǀ=/%%El6 .z>^|΄(DE`4~ HYVs0e$&$R^ΠEdDDT)(/():]v6mn! E!X\ {@"$ E0L(хk/rAD4Mn> 0~ٵ -\HFyL&$ GJwQVVB\r`@ֽڶ&Gq[C( @V(^z; rI_ʳUv~Wsb7s{X4u\\p^rcTnmIu] ]swLxfZ!'E/ħF6m0yQ!,, ]M؉h<Mݦf}eYFqI St:M5w}HM{VhvdeCf9F[mV4Il kC@'Ih0iY:Պs!YCQM61+[3#gbP͍\uu'&Wztw/6oي5kᖛor׊K/haQ6l܈YO>SYP{77˫:&.T+\++8>9/wu\<^Xު"\/J-b x+rRxtU-9x‹/ġ8$XYi) ŠˑXD򙺼t urJԌ*ysY^\o~~t7߈֬_~݅Ɉ.o6nFy)~S1GᮻXݘ`!23!) L^zj 'NE4q7$UϾ=GϺ\W]GJA{sy.!<ʲWY|[x/|x?x+t"  &K/ME/]]QKR_Чo_D(,yDt!`pO [0I6]IDAT!`6!`@xX8pˡEQ`04 [ނl ໝ;61^5t҉kܮv4&&&"++| aܹcGG0[oBף 'N[o,mt]UzP*ۻئ3s|l;{N*k r*:`ҭ&mUڭ4jIjҶVSNJ5JXA@HH IL;!y~'9{r~t^gք(vΝ59a)pBɫk/t:1lCUU)۸m_e(~_fA]4Ŕ2\"_!dnfD*jmع+Q0S1\YRpk%eS}sOM! * Y ;h¤(sݕ;c9gv5U \م}ѠC(.dRDIBOVklRiSӁb̓˴O|z=4s_l,,,B!ny";vɐAK;PGGMwG3 P()FUt k!\wBa?[[W-BcŬ 1Z5ݳRf1#=?l)3<E#X*_8bL5P[s]{`Y.Kar^ b =%s": ]uSǞ7j'YVNP4tBDuueII\&srrr4M^`ߚ lbTD  o> Út"@€_x7 7i0f3 GH90%K|0ҍbC~e ;"^ݮ oXAmVeVKB߄+lgm 2;]Y)!B âOiJ]%F zXֱsYMOm?yF%m?GZmuMMDb2bL 0 wdx;;;;\YqXUnsioj;\Zw|̱ͮ-iV4[H6,X-9"fE+5΀0:LٳU1-4;J.U 3+Ne 7w3H;=@s!ɓ}V1 왛ݱcGHhL+جJ8怨65LͱfԘ -V6W@ HKvLfWP;[Hyb&R%uF}Da&L;>%V{bw5_1;0Ւmü|軯$wVZF`R>å2#ì2ݕJPH=㸳 zk%0pkӮݻx*JuF5u"ZýsrŚ b$M8g(4~>~l!D/pk&G1j7| z$ӣIq3"pŒ+in:AA-.3 W/_{4(޾&Z $K?=k+>> E=_Y\NkY: :]r˻Z3M$Μ:</1ןFn*ٯก:?/ݭS._-[YVB]H3WlmCbwBN<2g;sZ$-zsa}Ys{=eZңS'A=,~k*gggJEy#b?s3iپu4_zB%f{Ml<&VK3 ˼ψkK[$40uÆuč3G%I3fd3fJNDpp7:nةSnNN`eSCScEDڅހUލJ|`ttZ^7'#<.tNtv\\1ʜB<MHɗp3nBLdև>fXUnц~g9JdY5;jFxقJA+,ߤLMMjjzLX'OѨMXѤt%ԉuOJ+ bL84j͘&<5FkZ:vѣ$:m`Ux^NNpp0"P^rdR[$q,P( '!q^BḡKΝI'^Ԃ=zwARiEbB-58 ;X1ʊx T1ْ͓D3TIJi0kkEI3ٕ.{͜\VHq9M>C,xΘrMdfW~eΥKq+cOZN+@nn^w?mefrԎX,rH0+gQxHrFF{r80@-֣ sR;!(PѠ4(^YdҚ+0+^Ĭa10u8n~*N~~55T@:995;x9izQ+.dgO~fZUaHF OD\ֳ%Kr;wL֜V]MMM%%%%%%CH*v9wLҌ~|m 22"ǑXD|.[A^?ѓc`GU3NK63+_6)gy<`Zh`i 罜4'N*.R)-MɀA }YCx\^0tV)yPXT[[<{N9km[fճ'R~h w^y! ;jk&tF@(d;BPӱBQvvL&w8>jͳˣg3Ȉ3AMdbm(BLwX8fBW9E؏ rvZހ1:4Bf1c܀ -nH=)/ԩIJ㩴1MZMZ7Vc2ϟJ^7?m@ xsqe%;wPvQ޽{t/t˽gRAT=^R[2^ȝtz^|bl!O;ZUQѠPQ5eڨhG ݄92>̀h]fws(ӛ VAIĔe#3fV+s0klP˞<5~~є+).4B]y飂ktL3IG)suܙK bܯJyw^rr]?_*5(l*suqf654qs~T*r`jXLkXgb]le(/˫)~Xe|XbzG`h"ߺVDѸ])|N8^UYe<_eu۫MBq:hLܠ>VAxvqGq$U$<"%Fhu-8X2bϞ=oߺ5dPnJBQt:\1?yr1gϜڱ7V;cV2b Y*pL>]QVX0mFV1Nq?مhJ|VU>ʊkPpUe>-{or0tDw](y.z_XA8eΒJ$5uz @ ;ngittс/`3e-<-&on8b֭k-6-|Wղ*n*1pswWUӞt.N-+VNNy"Bޠ7ՕARb[oZjr["jم.D"qKI)vr7HT dZ'''uЪ3B8H(:V"U#6wBH,`$J*<.uuU)/@$ݻw3gLB!a8k5jpusW65R}sJNN;6Whx'+8b0{J|;ƀp| > bY<:FxYA: &[_Vqrvx򤰸ۻG:upCT^9}>ZJcAh|M -UV4B#YڮԪJ bǍsLDȁ&߯zsvTUFV> =[%\ ?JlB쐈ywm9~Ԩ)#'& ^_WWW^^^QQР7"rX,v4#Z(\a+8q$pC-1ۿCln=oxmK Y6R&bENf]%R@-/P (ZW15>[tP5 nUD{c4X nq$ .` PQF7sdY !F3/LRFmYf_y|[@CHnN`ɋ7PָZ; pAi`}!Dca`N.!V̠S'"vx\8"*<ŷ9nVզݛUw ӣ>$f%xog+ cM VG:vP(wuuh0ƌ9|:J\]K i$'q' 'NH$vW)?> O~,fɕEvDX~/B昋X)1';"V5Tq\NηZeoՂE6,wIDA3Yg}X|߳,4zG}khY~2nVw (8INKC+j$Im]d|Hh0==ʦիV 8hСYR)=>b%Wu2,GSLx@lfEPO+Mj,&LejG <Z ?Q U ZMҨjZR)׮]'߸@IJ7\vK*hd|ri22@?|sՈ \]oܠV5km yvs_v3˹PdZ) ,طoq11!Ɛ,\ЪizʕŋǏ6uڧ*+7mJ""2%LJݹs%1v&X+PXh˗h J 3Eş}񹪩 TNhJ1riIS˔P~!qq/rsN'O;o.ێ35M[w|'׬3fL]]Ν;;kߡ_{=m۶?ϤVx_ͧ`ZҴԝ3fͤ=vXJJ :{,Q\XaHgϞ#F`RSSSb1"#3d ˼pxfXF -mϬY{}yTf,Ln\|/&&ޝ7-mZ4BȣGii=2*rq6KY$q|3fGG^`r̙DWo5D"Ѯ]F< ܱ؞\x֤}oݻao\.oR*/df8p`lvʒGG%&t>#kkG?j[l`X+y%8&bHw]|'|zΙ;$vrbKijп_!23c*X ULt -BMuͺ'_juScmvG-`i.^4F#e11?3m=ǖmN7'O4mکȣ`|E ɨj'L|{Op!|GIKeeKFEF.Y͉ K@0Z%ݻT*WXd1}F #DkO<9|\dIdD B:66:ѣ qGǍwQ2W$' 4K@'OF F Nbc.ZqY"FsqqqqGAaCܿqoݺ@ƍ;r0tƍx@hI=rP¸q ;z)qqϵ\TYQtȱ.z\flxRDThU˝^[SsӦNNyzա7! N[ ^^|d2F3j4O9}FرsfϾqq\7^sPqqqr,ooo@z*2ZMuߎYlYccBhEaa7mZpQEEExhl:n„ G}~g?n;6|-C6SN!T*Uaaі-Xxٳ{޼_ G6mܧOԝؼG|3g?`Ci}}}%V]|P(ɹ/_ׯųcyډ޽{_zԬh._W\ӧ\.~vz044|ӦM}ٵ+u7oPR?# vǏ6o.%qȋ"bqO,vݸv] _";yʽ ֯W5*droXlOͅL% ߭_#X[-3nܸ9kLD2ctYljv'O"JǏoK1 3 ɳRihhhEEEG+WO.Ho.ļuIRtԩYYY:rkjjurHlllFF&q$wwS#"""##.\Ȍ^&Qn޼5}tT:m۷['%%Y*`{߽S\]]O(DG9ugϞE@ Ν=7ydZ_ydD2s P222###ϟ;2gDEEuNstҡC#G @^^AZZ;wL6U"L43! ^o޼hP()JJ&J&ODɵD]>+9Y"N1͛&3)hcC㕫W# 7nP( i^v-%%Y*Μ9͛dsq|+H$ ]QQAmcƈN)F>D۾vZrJD"1s7Caܹ$<<†y3=s B))3g<|BpلD00wp`yyɅB1UJu`.vqvЩKRy\دh-^"{I$҅jT&6~N9[J5c͵Ɩ]S <==~x#)dD"ѫݝOZLA\'|o|-zk6WDM6L`%Kdee!=,C͛رZ~ƍzkFvoE˫:00e\rSPX!R???., ջw;ׯ\.* TS:;vO?V]dRF}˗m=̬f?O?Yhd655OO"fkqQqኊhjHrJ )SrijjrssGYzA,;Y2")))qLl͛׮[kިL64g`o4fxYLڼ%}Jˎ?~ߒ)SP_ryeyxz"Qep@ ϜIFFFvڴ$JWKOK6?wwwJŐ5[T*ڥ룒ӧOOإsgTJ{Ngg灃@œ'otmIEHM%'zWW&̳\<҆!,Q[[kQL&khh03 `͚իW:u?;sg\]]ͭ;w4hϋX'"BB^zuر{[n =slJJ2>d2ECUdWTڤTzzxTUUz̰DCؤM6.]<\.J]t%CeݻSN5ZX@d^<WW׆LVWWˌX:@0}5(( ())ywݻ3g0@ 0 "%# \\kk܀`2^!2Icdi{mn,ңG֭]<+@ zHf'/z\Pԓ._"<<ð(s6nNݽT:tذ-[< !pss8p_ :T"2MKD.777/L&SS,ɻwK>}:vԣg_z)D:t|}ǎݷZ 4x`O$ɐCZ@[ِ}uhc(|ʋɠ;$MOھ};jl"H9YIRãx<00pڵ#N8ǟJe*Z2OoB!]]]mkZ3@ d...7*<<]\\_uuT<~[R5ש><5uV=r;וּ{UCj#1 ;}%Jsg*#FܹsgSSә3g_Q!Ch4 ̡sٲgΜq<00ro޽;11sgjllL[jD"""@xxXZHs۶m;yNWر㣏>jU'MVL2d0fv={:xPT޳NJn?w>j?u={d#vܩh:z*2aTdTڞ |l=iQQ;S`]Yc֬Y>k,d) ӧρ*0 zni׮K$AƘ;uvss~:PmM(n}z*Ѽf kWZھ}۰ag Bׯ]H%AA]]tvwwv}>Rں7PH-))SwTmm6lXs@ۮ]vYmݺE& __^G9|ԝ;jնm >Ktۺ_BBm&Nh 0q֭[yZg'B Dng߾U*0 #SSSUjmۆ k.sLL̮+55&&vႅN2pGLR172d殊 %9e޽)))흙RM(-\tiСC LTmKykMmkjk$9rСo6קL#_2^o zyAR{nc%KNO7x`@ X֗Loj[%?ZëW1?p@R~ brJ~]~~RԜAߖT ef)ܸ1v<`KZp'gϞӽGw'''Ե|̜5˶Lߘ_+sCHRݙnᢅ%ӧزu˚5k̋ZK ߹{g,^}_~e„}ի#:w

?}{9'۷!Xhg~6c{HRCK;6=)GNNN8"{</^rv֤rs/YVdPiQE/^駟%%%C*@իVM<Zt۷'Nͥd.@`^|+_%%%}e.B_z{IWgɒoߞ0~BܥK HkFw !~РA;vӧcJǽ+;؊’a0b z1=L   + θ ,վb f(%r(>IsAχ ھc;3zۊmM:E.le 5K,S픣C*S'߰~CMMM=֮['1 a 2&[ch(Q,43, ʲIR,9_=1EJxhy`s * 'ZFf&iPZ*duNԕQϞÛL0#985)dۊTuINl kKⶱցe LǬT6|ҷ.ՅŬ]?{oZd徭z`?5ض0:!$h# /&]2W_kh3UB]aec,%`?I*Gjv˺-8]mEUkby76]r:бmuC^}kw2ͅ|YL-bIjD8!Li daV=3v2ZCp(Ԧm7SŁqP}(i%m#&ZP ~.BPR:;;wiN<}`WaA^ɣ/^O4_!ωp׈W\"WVJemm݈}|#MC(Zxp'"IۇU٢YaqŌq }O\xh-}ؓK/US.:^K.=ۓQ爐+/'zREŏNbg0̸kϜes"iFCOS-ZMuund\@ V[RRRѱcXYYUaq&u1vdiM A]7?o1JH^ Xm>(Hy^UzNm*.ڇr\Uդ\}&Kd=l˴ѣG55 L&U"FF&aysE,qi;K\\(Wʞ7۝;wC>l'P@@3Ǐ1 Y2,S$+Z˱gm |r'D[ZRr13#.a= @@3.vG+r 1`@ŠZ[QU*K绸_y%g߾ryopZ#E"H `0j04'BR[E!+6: A}=TVBY_:KG/})1D}11)ae(H<j`N"@ )D& k"H$ H$@Rt8}kI} UxBQ(u  x@]TALgEii r"Dv hj5[ |y:V Z,\ccSQqqMmmuu5x{yu rww-G![7R?ohW\)(Jl㔋>oooo](d$JjQT>E U' gl@ֹ v)6>=Y8H @ ZfSUGlUuu:w NƖZ.qBzIPiYYΝ/kޠ֨;t%(O~Fb0#6cΝ:?(,|t i+5E.Rܳ2v57u k\:_Ъo~~A޽{2gP.P_8p˵˗%x LaC]l~^䚑yA$v:|FcEe .zxx!;^CCô/selQq 6"sr?+W,JQRR}z4qbڂcXTٙͦE йsl zӧY|TNHH $H쎋052*Hi$h $h0=!bR?l0xExߺ@$- ӁF" K~ `\ ,t@"@)@2Hd$Wb񦈎|2ُ`&"o?(j9o4mmw)6!1[p}]-3 5۶@ ݫP6K 4*_ tZ@Ϲ{uR)H< nܼ5|о}z[}PXXN=JeT liK}u}%{yoT* v֝Cr<BSds>3ywmhk?fLISI  Ua;vjiu`#.5^e OR9A;OMv@Bz{xQD,Bt:(y tZ :Tf}j T;bԻw7 8Z@LdU~zmb^zFӷog''cL)yedu)kH^Ff%|mT{!uCzXhۡ#G'+wFxttׇYYY11i{XIْJq~Aa֚QKJJ>2-)ÇG/ xfێK/ܹ &۠aQTk2KHmgg8D: BBR"@4;i׍p!A,0DuvvM aUFsqA!!>>^xcBxLPc cqZPA^ 3Eʼpe @D3`5bs2eBP@hYؚk6qP[5E,;<4B(#Ga`>V4њTB'֑42W(+t8vI(d26fÿU&>}Z$&ĿF BNB;q2?%!!O"V[^^~SKJKbyR 0l /\"tgUB8~X,)LOO?{|1-;b}CC'`?nݩ;ӫ˅ їO=f,r~ȬTtͩztwgH :-a֩e+&@!ua@ V+Ɇ PpL«Cˆzr!\]]==<1 $N—|O]B"aq8%s+P}ߤA0X"@zdhi Nb;oַ]'Z#$qZj5!!@m~ ڽ{w\w/[ۮ}Ϟٮǎ 9~丸XłCBN:>88lY6JFk=0Acٸ!wyo1R=T*'_հCZfaހn Ο;Vi%10 ;AߒAk6λJr' C!hP! L?z;iBT]Z 7DnnO? !j޽-3kkbpq77hhJJS w4_)/l%52* o<ۍ /q$ZV$qP(,$@@ 0 W^>|8B|@,-|Bl|Lcbg͊K+L>~Tn(Pj?ɺs7'^HHH\\P(0 ??} 8? ?VXpwݟI fܻ۵kטFw/ϯvDC{I[ ^{CRMم10;9i@C]u$\SknRpB_T{~FT n "p}SctjD"H$#GwCU%yuuU^ϡA>phT + +nVdV)[\y*aUZg q@8"ӇvYFHt:Fiu{쯻|VPB9Tubuu |}UjF,++)+//++ollh4Fәsy+_+_H^d޽{͏1r| 奥woߖH$Q1.U\yy111YngJ]A08t>~\Ǥ"'IiPRkZA!?!&4ai˖-3f dMzgZOOoF9ud@x?HfONA ehHWcw+&=tk"{ⳛR(T)bXX( 6SO?yݎ]XO=<^%!=[~,ĐtY^vg͛vv޼yy׌ۨ9{4ELzAqq_P]lF:{yuq?=wns t? A*b+,=%``g"\Z @4"*)4YM ٲ@ Ђ6Jk4R 8. #O߲<GQ(;v2d)ߣG]SmC:WPkgLҸn>5UÞh^^\ãRi1JD޾GֲdWDr-.&c>oggd2Ϟ=nܼ%He,ǏSkժUuvvv h"yeUِ`EEr_C޼y֯\RwX޽{СClwJ [$,y#89?zH'N=|4׻6r;(`"jYjJ ^Vd}EQiYe qiW^=k%o͎ұ1\a8RBzD<.711;nVϥ/KSO8j-n/=b;#htD?5h q#L mD9p4ZmQa"$N>DR*Tٳ۸B"{nrfqWRLR999 By\(fkj(ok]>I٬Z:) `/t+/ an9/o@">\@JQj& 9EyVfq귩ϟ_r< H2pWQQ|u(T r9xzG__hvR斜띯v\ $0 @8F@* CGOαWڵ&ջvmԩ ][1kV3Za̦&31Ǣ5M-(* 0 KIN0̬q\fux>)J >qG5hD/c^I~թSٳ7oQ}W_}u-X}`0*axLLd3+Q)"GuIM5#Gdq+"?b2x055ԧx[Kӣ'+L&Ze^ ;pc@2F: ըU(Rr(* Eb{H"Ԫ-}k󴄶$sNJbupmAh1\0 'pJ51\69.̷IZ DͩR˓mԩ7g?L='tc(ʼBlM_laZ c0u׷0Pd`Nժ- ;\5hdNکmu٬ݖ.}w_ݻOg2>*D3d\ll=w $Yٹr83l.͵v!˕ֿ1|I7n mߡÏ?q4mt.ujf*~f5}\b,LVT8ry&Hdfeek!Q($&wcDy:aGZ?;8,(6@ XvNGBmQ<\~]+٬euP P htFy{9uACWޝ"2U`[_gᅬ \iӳ;9ZuSv}_lSsq eoobj5\]To}lllFFsuup8"fH$:LQ^FqجH&R_#(9cP\OW۷7z#`rD<.A2Օb("/R{zx"uq s>pk\:Z,eZƍÇ;Snؠtuj9Vl/]8m AmҡCJsHL%v Ң[llF}?>s\dd̙3۶m իz߯lmeR6Km\r}zU]n<4z!t$MeIa0;;=`z<ҥӧ)uqƞ=87% PJ%ԯP@IXG2Vb ,% T%ʥ" @ pdq,ZP 724\n73b2'"ݮ{n߾ q_ϙY3gG4P(A?NJJzչsbbbө?vrrJ3ZVk׮]z5==]*FEEqqquQ9t:D믿X ffej<ϐ̬rٹ'O|ќ9sZjU3$`\ (qLcQYVG1)T_5>@ޟlfAQA%+.[Zz:)bq=\C _a2*+Z@ " _21+ ߷oD/~Wō۬_/vJ3oDfq^]xKLId*JeX+rX,&R,$IbLjj ^.r^W{IqjZ6) ۷uu(8TyuRQʵq S*ViW+<|J}(+;G*SdH><\R=w?pΝ;_zGݻB|ON6kYF^q2, ڗ00(kU2yA2uV6`DٳL@С}kW`Dj BDVt$j;"GG I(**eBVdgezMJ{R@%Ȉl<9'W>w* hI#Xj[;XQ MQY@-ՂMMM̌(JDO<߿|||LL'AU!իԌLWWW77*r?NNNH$uurrRYO0AU/^2<|&pS!uq^2 * *V{+sXd @Z `ن Z}5r#Vry?]d |#-+HUhEo|޸E NX=IT$~.ö6sm_$NRO̖g_so9p-g);@=3LF,8@2lIC#@ZF ةwP%;[.vv+VQ6kA>!r/ ;|p5IJ gB5/c1[M:~Yb2}YddCnjYbأF6-iQZj3qPʖ!{Ar,׈#&S] @@DPD1 3 &XWmֶۚj"JRR V6KVV-1jY@+k%EɄ  04Zb|||~ ٔzp\c-H8wNVl& >HG> 41[2a:]~&N?GUt .u\t:A,ݽM6 4شix<IAdeeyzxT`~4MllÀ!Ixׯ7n܎;͛7l0ǭ'\)-=COfs8@NϡK> ȀX\!k;~'<}jn4-N8!0;;g^Ud|Ƈ7[Z^Kd ]\ $fQzhErVz,A 3VJ٬>0u{QR5:96kJ٬鳮]?K|vg*GJ%&g@ձ+YRS1C:tӧOdddNV,**:uΝ;w=yR@oZ""" y:(`ae}zX0-*qdn@ ~ oN$y+߁۶mY:Z[ / !/23!;- fL&l 6A! @SeQnpڊ2(gҤzԯߒ]q˝ݠN@ bl(a,CVغu#G6lؠWULQU!Rk,WWWtPlٲN:YF.'Hj2$HDXXBؼy355VYBCC@,X"""8;(jZ6- jެ2 X, .\ثW/wy򥞠ӘQl-FF,**rqS9]+Xukdc R TRU8d2-xȒћbw$nK`Fqt ؔJa1fma@yAQa@2vuxG0 *aEy~vDo4*EpXHGScJYJ%U: UV…֭Hܪfjڽ5 .Ν;WٰaCPP.3!|<ͮͺذK!Yy.|+@V=sW`gj ǟ~7``n"##%3iӦڵǟ~^pu~V>lԯEv.vyJ R8 2xgs ѡ r\]۩YPEG(o^+‘PJLB!Dnn@QeYmR;cX ^IHL_eezKJFf='9 d=W+xE޶mٳ7md+=p cǎsΥVK%>YprAj"3}&,[bEfW]?qD7md8tرCREFFgL(;v,U!BO<:(F ܾ'c#dX ׯD;vXM4ӧOS&w0ވ]vq8MWE"2^l l R;рBcB.suu%3٘9 ' H pQ$xQ$TN@NjZJ|ssrZBH$8Q Sp O,3J5]6K628'KHHk~~Bv3NM}b3 @QjU [Y'A٬]:ttIiIrMggI_|THpY;M(#?qظ˗/׷/_۸i'BHMOYOO9o]+%1ubB oz:jfk~~gwNKM3zaC'NlШ-[Q{#=H=\i<. yn>`(& Rw*0L,AzzktFާO.L.((X?4Zp =D$*O y$ :]h'xI^جLuRvG.H*h dl&#X)b@gf^STzy1y,''3gujh:+Už3qfjhX?x8UjuAPP",6jcԻWvvÇRBtxhO: A{9998 VZP(NAYOݺt.ʆuPz +9tw,??ѣeqx>sppl zנL,1vƙz}Sh*t,**"I2)MAAWݺ|7V*$ XxS,3PUL8=qNKB!H$Zr*JLpb1Y*iĿ ۨt ( A$Ics[q)ǫaP;\6kU" :t8ubprrH>4wչS/- 0);'gT+>{^v}{wO;wZ |}h=0nTGhҤ6VQ*ǎб[nw~ХpOO?S?1|6ZDYt H~!C'e U8Ǒ!8-P(0aG["NQ4a09G5DbZ-`|u:6VچbOQ{78".yxKA(x ( 4pZc2Q3 J3Txgh`18L`T*TAƢ쬌iS)))FoߞԬiUnuhE]]KW >_FPⳣlVDޠWzel֯5 Vjc1J]ǹ6/ `!+$ ($BPĆETY p\]1LN_al6["J /TJV+/*R*Ż Yi5l ~ |b 蛯"[e 4fH^XP`/>F,̌6mX`_f(iS#rsrӶor(y\ N1#7'U˖wT>mڴ+;tryQQQrr+0fт.Z0[T'w]~jlo x6(KIh+r p`"'9tjQa{R 2;MT2Qd2q%A0La>fE]T>> H8I:bGG@h(rK(t[ )qĂX Ip@K   HpP_ejI(٢L6֦V bd2[ pkT%u~U x @VȑA p(dH,qj WZLf^=۷m[()4,S&x[9.!ҥsF>z85----<<ڷoUɩ'خMk?_/_fdeeddKV͛5k(6Cqةc@?~bHCZ@в;4`qn2^۳Uް\q\Px MLLRk5iٹԩSX@TAg9R2!( AV~uzZKT*=;h8&*r~_.pscҮ 7ڨF0<=2+ 0OMKmӦmUҨj_ZD t5x;xԔ$Ϻ*Ӕl9j]81D)+CUKK2 Deui}pv~U/M6mfN5Yn0 8Ue1L(1 l&X=6Mf ApOyvժіC^UHw{2$zJ'.&IRV{yy5?ȌhM i4-+>VC1 fU\d:kd\֖GQӵ&X)\N "Hp"I@hժqh{es8hI |AjJbz&)AVle҂>Ѩ55-6E4O j}@ԔO9ޅ 4hРAF 0[^r 4hРA 5[{{A 4hРa`$H(,q<3#͝Qf4hРA 4hT#cdef߻{gPH( u:c E,´њC 4D)ͽgPoA~.- :dpm7F*Q0O\h͡AOl5  0h$3_1/^`dlqq$I2Yl?G$ 6t:-H i-ͩfe{" J|d6h4aiph4^1/99: H/c^6iCgM5Ĵقt}Mh=Olc>n8^XXj2a<^,~+[W>ΖKP07[lIk-hNM7(6iРVЙ>JPA,H|"P$+;*+|}i-ͩ)V4vc4h|(P(\>>$AN1VZ5aCL v|ZlDsj8Xs{" ,,e޽|~L,( Þ>}*Jbq-^tɐZ޽YYYϞ=3Z2E^^ݻ?~,J\nƍ'LW.(?w\wQ͠ n^<&TZDZhFw- <m۶-ױ-[tǏO0GJ-ZRV-C}[[W4BA4hxo@ 3 <|ݺF~-[6={Ov蘔T%hܸq:u/Hr+Wؼyӧ׽{{>x0޽C.Y|7HIMIKK7*LMMMKM3b=֛7CB7'O.wֵnރZ[ #uy0:f>&O|ƍwe2 8l6j%EFnsqqY{#-==z%&ƏӬqc}p jcOY?w]k6."''w #yĥKSǖ-[B\|W!!C/JQw;bGܾ}*)((2%BRx~8YYY_|xРӦMꕅ:Ҡl5pqvڕb9;;Ϙ1}:jay`p &? "bӧn/XۃXg"xێTG\>AK4MxTjVCLYQu}3fbM?0orttj_s _~m8͝;/'''((x߾ ʾN>#..N+QaaFyru܋m(=kЫW12,lXTT1l05q޽v4+4[rm۶o_beqʕ.P=Ho^͛7?Zh~02re˾TǍU& ?K֭9q|۷oܽ{WF Ew6P\HTRGJr+ͶWݺ|\=w{իWSIЗ3 R͛}{̚5s}T֭&Mx_Çmܸ Hj͛7WϞlMNXNNΝ;S%?|9^:~k֐$d2T*W`Z^paAaIy ,js/ $Wc۶HoÇ6jp}eVF=a$7x V/?$vKqttܵg//Fa IhI; IDAT]@/]֗:ucHdVCLYaE_ҵKYC#G8FA۶m 5"RSC*:tY?mp\q@;w9|И17lH]h79cƞ={շ+/ޯ@ rņ4ʠlRToݳ{Y[nKIN޷wϬY^Vc:[ϓǏ/YjT/B3[ݦm[YpMLڷ_r,~ƞPȅQ;wv:wtn4UXPPФqF I>zP.Wtҥ]xH?~iѳGСC8lv`n>H@{[ H((q<-5qjz`֯__ {`͛J܄OxA>{.#AFQ(Yua}! [,eeUsq捛_,^2|M7d2p :uwZپ];գG,l<obckۮ]vwyϘ9C:ubXAA%u!CB5D~tT%YYY?S=_ߊ?~}AY@Ȑ!L&ðԹ4[ñOzz>5kU !CB>yZ>q!!!ei0+mD9NԡoP///ww$˗/;::zxy?v(s:mz~~ݧFΝ:2LJ :u0GZZ+ئFY8R=hĈF 2-cҔ)V lgݺݻ{N]//VV^SCGr[]&:k׭2Kή ˗Ϟ=suutw5sFՀ V*eLɜ~UԇvG5PYG4(0 w{  y._]ʕ+:<{\''iӦ7o d2H,&"T*58A,ܼ<{{{i=}^LMw+W֬]wCH$"L]vjC>) JҴ.daX~7.5>txժU;vnܸ1)9IրMN/]~ݺ}i۶`:ېUVP*#FY, kP?oCGz7Bl ,Ӛڵ}||nwSNoUٙ,CCfYM>33nݺzu;!Ir+WݻK.Sͱϕ'r\ D$`Xox%"YZ2S*kf,4_._>st?m떾=lA ׮ ۵kԠA~mm+V| Z$Vɓ'YAy>rk._ ȬY3;W+0aš5kk6 C}Ŋo):fHdp'ǧ /q7n>|XYU&YXXhTؚ7o.IιzX,d20#.#xjOs[.[E"(`퉋/NF^t:6ML:@9)ۿqㆉ'-_ A~&>|}"""WIII|ٳ}}}-Tב }&T*P((a>jR#GW̼8::޽{WR1͛w ]\\[}zϞ=ٳg .1c5F4*c=222-ڵ;z\qԩ*Gnݺo[Ө)YPo,K򛯖7l6;7'0,}{bBB~^7T INN-[lb 7nܨQCfP}iUog͚ٮ][}|:ҠVȸ}W,Y|ٱcLJ :uZjjʬY> 6^/3;~<8䧟~;w_9H%Hnݺ%ɼ*ZjQ2˗-ue5wU+#,X0͛g 8hi b`)S&/[l9͚503JWwرQQQ'O*(رc;ueȑYYYWi֬YsΝ0ab˖-2v8UR!룔ՔU2aBx||ˆ#;>f̘)KwڥRN;V~.^RLXPPyyy'NԨTgB/^VCkHidnnn۶m6lx^׮]a̘F*wޢEݻ;ͩ U_}c`Ҥii#Gg#9O^mS*ݳLCB 3#wڕ]}ʢhj6l8}J:tpVŖzǎ7nT*7o `x͛7?ԩS55*q9r*f]{پ}{BPu)yRbhDnN@ms3O5l@ﮨF9EMB¢ b^Ο;{V˖5yϟoٶG^u9z~~č7?믿Z!jNM./9}:tظy]Өʍ=V}֑ 7K q1/E8gfd4lOo׾#0]b#`9*;~ޑګO0\Ί{]:fŊch._xLL̚5k.]bhWjZ5FۻӨwgBPrAFV԰lEv\66S˃ޏ+RY%|p0a0qd7%sР}~ BM-z?c4$74>q{[hr<>!$_ސCW6FzzGlĸqÇ۴1 ׯb223M |DfF;r(AD"MKO̖dƞX$rqio/~G1JQkjv!(jmR(r2rs<> EVsjٲFrHƧjz\r E,r(֩]G >'λ6ɀAkчҜoQm'Ҡ)۪T|| rsljhc/cw-[/ D4jњC z[ T(+ͱh=DiZD 4hi3Hp/g`-@ 4hРA6@٫n={ҡA 4hРaS(e2@`o4hРA 4ho3^ѠA 4hШV4hРA 5LZ4l6B@<-4hРIaзf+Kև-ҍ@Z24hРAH28U{@˟FEuۛ 4ho߾aCKVWW}}smBI8MH$ 5/ZaTj+WA0dN2ƍ9Hfq 4hРQQ%q"33gRi̫-Z[a8$gO6i,k+UM/5maL"xyfbqkE 4>iU NLNjϯ>Ϥ+ &۲E"4YқZ}&S*,&31!˭^71 4hf+mH2G( 8~Ν ð\(y{ygtG6#"k2NNLuhРA' pHKKqB?#i<;I$իVM?.l_B!/`j+d|;m'5ۏU7hРAʘdJ"lYػg?ϟ߻׼Po-eknP0 4hРAS @^Fխg}3=lOiX2bXX ~U(ƏӬqc1{Awή_u6th͛7CC&NL_tС/?J?Ǐ/YjT2dȔSy<+/ޯ@ rŔ ʽ2~'@k^*={y<_|SRR `0BaϞ=g̘n$􈈩aaî\2xpI:nĈQQQF9r ɓg 1Ç0ܾ}oFεZ#<8xp֯P˗ZlU\ii?!0[MrߤA 4>9;aÆ&V߼Iܽ{̙"#޽k̙ CV'&&o={ܳ8oϞ 6ߛ7o J?)SL f֭[[Sii斚bz ʽ2ñOzza>5kYP׮]OII1+k>oWbu: L&ST%&&޽kƌT+@d'=z$,,t/?S7nY,KRya_&ȭ[GS\9ijûl[hРA jsW 22\(jdfzm[y\n,Dp:v nݺÇQ}u#IqzUɓ͛6Ϟ=H+V-GI_rŮ{5 RTܻ,úBYϙ=nc v2vXPh(/Ιm4̙3T`j P[aJ9El HҴ+$;'==V[-R8Bnl[MzsW4hР`Ŋ@/Kf@VFƃz [JTFKX,}H$& ]d2 ~?@Ybd2.H$ɪb^4!|u4J\Frjgvf/(.HNJB #O^rr2 u뚱A@̌zuݺ֭]cxB lXo}+<}l=iijBuoUV[[6O<]hsE A\t_}nI%a04h|Xx{{HIӟ3ōɶll*fTf/(.X嗯_Wɽo ִiӽ%(W<~Q9S("0//2K?yҤP 0dJ$*>ׯ߸~xLp_6DGG;99>VM4hРAS %8Amڴ9vFpˬ͝ sT*?g4iܘ*oԩjZ]CC{iXثw3gˋΜWަwܻ,c*jʤ7yDZ=Bכ>"Hp/,,Ç}||hBlٲvڵlrԩLh|X$&&hѢyǏ٪5#V^>~\|B¨ѣO{Q?ënqï^:qd|/_3&>>><<\e~!A%ԟ'7m@x{zP::m{?}$b4C ԉ ʺJ)))#"$ɴ)lТe9͝&1qYCL1SP.?ϸE_pq)͚6LyI˿zyM5߯Y`|q`q6m0q/3]VV[d;w-5nSNM?ŋA5jԨW^nݲM&nܸ1 5BIlZnԨ݋/h:o߾C\=D]E7"5~4Hj?a¸`Fv (rCkr_z͝wF ЦRڂUֱaܖˌ)9s&44kNTښrRL^pa CթS6;w;f/~Z,?{wG;R=v@ػ1cK4jkL3$ĮEFPD@p{w ,3ofggg#G@UkI[uHBzeF!/ݹeʓ*2^K$׳۽GdRVK'ennׯ ⋩U탱)+WʹeLxl'Gqu-ǾQD -[6iҤ۶m}ɓw9s_m됶YBpp0DEE-X'u|jՊ[7 "J:f ,*(HKI*).ťP(lѲ%p/).JOM.*(J=Hۿe6mX IDATD[dIլΝ;uJ87s nݚ>}P(\fMeV[tHO>_իWm}"*Zn߲\[]ߨFwѢE{1 0`ɽz:p@hhhլdjj9sh4+WVYfuT>{!lF6lhFĞ?qBY7007Bwmw:ro{LIY&b2 "\~׷U޽{sIKK \lY&MT`AJƎl!n%im[J0Cݢ2- v` ` ` P$ۺb` V O\4ij]x@ Tm$cwJXwI2ʲ,iOŗetX1E#yȌ3V (eTH}ϊJ +2":M-HL߈Fwa0 $a! "%Fjt] @0+ʪC)"WV ׷̥FakĈhTV]T^9  +_'9zꁕ!>F.o;z1}k$[ a"Ҩmx05gנψ!Rbą@ߪ aB# !+;aD ~>$Zgz!q11Y陁3CS0Fa&,0f>tOGzP;46m0ӑQLѶ9ª3BcsC8 1EtDi<7?-&JEI {*I:iReJkynL7!ٛ) & `iw2qFdQzᙡ*dH~OA:ԁD!ƣ4*GX}fƘFgm ӟ@1$m'HƳ54/s(̊ Dl-J;b,s*Ns-FjsIb$!0rjB'@I~O :3g:F.4~*=3LlOMz_M`pAN?J(D0EBL" ċYL2+ sڔmx3+W}|qgJ1U1}^AzLS82m{!C.{XHپQ3V@픅+~HD9e/J<Ӓ]&8T*UjBTUvvou5i6Nr#0+3Ai iy1; 1  Ӓ+dV"DrelH4jbN;Ӵ"(6TZIjZXiTp*; W&xeUYVf޲YL988* ZmooggTW@+u_0ojugP xM<lZ<`a&3A9fWOVqE[\7T" @}!s*jT 2f W9oWE+2WYrxm. a?1"Wq2YR:XpJX`ӷȬTcU+r:+"-]z>R k{RgLΤpLfÐ@'ǝ\ݭ B:mkeVV$#yEbWEPk$~_=8{0zR|ރU*yy 6ىT* wۣ8//ɭY)\Mt•3]cfU( 2y3+FlPWE.\$ܰe#CnвmFիP*h|Dv!=Mx'8V )\X2}[ōU'B9r WZlK+ծ0V "V^&R)Pjl)@yz1+ ŦBWm\dY>F#ąq({B3lGˬZ}$òF3bW`UT,֠A%UB"VURΌB핁艂ͬkB_5m Ja6: nwrױ;>xwJ0WPBE )2JeJE"X\\\̩_U4s qwO֙TXV߲F[ !ZuE bmWM72Z1#F'NCN矆 (Ys2^ @DXXDX1ePLtTF 7o Ҕ]?In׶ bj3B 8EQ)-##4a!5%ut>|QD8"Ag3O3S ̊h]ef5{hk:`bSaBBB~;ܹ \R~S mHśsq9 ށ8x=^1Mz8t6;Jp&&&-A?MLޣ'\?KP8V˭_?L(ŵ8S)ZwrJP(4`X(\4)-Srѹ2hUsSq(kee~w+{(K 4V4$]((Ͻ{iVNd)((}IbqH˯%krZ:IGGlپMx$)_ڸqÌ7;Pt]2d"d̙cǎ]hь3~]֍~]R2 [2Oǎ۸qƯ09R8w3x#?F8{>;|j5lupOR"Չ qqr)].>UhgWR%qu;>G&BO<owySܤ]?Zi>{!8(ȀH2~ǜxyyegg̘5 jun߁3t%}׷oFӧCǎBߺm;kg͘F# wR޽/D,nVXMjWό8ʘj7T,S:ZnMjjUⲨJuΝ3zܹ?FYteqQ,F O:a-۶׫W/daCcUJҢEFuKS˵.P@Iݜ+_NN->wxT,)xDmssJ\95~.Ņ&8S)jZV1cƽzST*ᠺf;RY&նbc$Ud1} 6@qm ʌ11Ede%Ӭ5G}ў*ӠRy#Fd~513-Λ$a ”b &ObW\b_9)S#iUK,2dhtL, yVU_ A*vUÇ-GL.tI]!;R*SLӧqq&IyСQ& CGyxHB>7n(-)1sVclYVw:NW3gΟ;wI[nlJmhce">zoŗPL(G*+F)#P!ffyrHn*UnJsfCMG}7#?eGn$ njDѶnZ^r6{B  Vӧ!/⏽ySҤ]?:lnBD8.^(.$~_CỤL[VҦj1X8Ah16up>jYڴGF"f|dx"{\T(TJe~h4VCdajպvJz7MVcg͘No`Oa]v]xˬ**#3h;sTUEҿ9ϿcVZݺuk@85AgALJMKNK'X69-nh ܅> TF@GS&Y:%+O+Z T@AZyZ6{<{Ŋ Sm‚|.e*LOIx_f>ONL+uV{[!+yw;vd>[.BGL(:8,?g'G99{ð>#tt?癍ܱK p/9a}+).O_Dd_5nѸaD㈈D HΟs;Ռ6uuPH7{Μir"kjX4lĈG믙hr֯f̜`XofKC6u~iqmXxVm[ZBqZv=6|'GJqz^ƽDҒҒҒb(-)F|PWAtVnh]T#0vVJ~ePVaՙ<.g‚W.8Ye*i٦MqA~LJ:;+;+}lB*J$ ÄBgÜŎťoJ5gnƿߥuDXO~r !ZׯDo KjJZ525a6``@;2! @b/x ? 0WtLŵZ'P "G&r=k !BHQkjd [wP.RZO~ &T(ty@]bv0 qI FXQS6INKWe'Yf}TVRvvs͟`aYAJ%F4lܨ ŒܔirYKKJLjKY齗K6V&Xq5 NYB.Y4Ye*4h!oޔ=|>5[ ;QPh[]%::gvY6_@Ie0WQJT8עIU@MT)9_~~Fuʠ!D£G5k֌<:tخ|_㌋,iݪOFd۷o>u:E<~ǟ~5*]F拴gϤRixxOM hT+Xv7 x蔞5]֮];"ݻe}GT6t(!kqiTAR}<?`}a\܆ jx{?YfPTTqqq~~~S&O 5l@͊֩3`@m?=N#/v6]<[1}../_~hD"W7Zjyxx^H,$WZ]Tp]<+^-P.bVW –6匰)Mk424nBj&VW2 yUR峘UB\د IDAT Xe`H{@4HtiċVQM\JJd7}'/uhη~ח@d[~8(+_w+TUjY- !={F˲,0hFU,kR-2C8oq}˴=[FN|z= 0@֪Ab+QU!jӧO#kFuV' 0UUsWJ$ڵkg4qK^a&PvJ[Daa!ajXTi0Fӽ{=u5߹sVk~]ضmmfgg/\GD[J!!,({{{{{{zv5f,x`3 -cᕏUm x(cU4kFP,g5lxJԖqz_5mo ʅS% UU01`-X+VZXb@VE)Ԑٯs3>.,J# ի׹s'<{{/}>hʕ;vؾcOd0S:uW.,^D[G^^mmYTA"όL%ɋxiI ߆V(^8eU?p֡2&7@s~N!!c6 NfNmi>>ў^P1&@g$`NxbC1F:ŠV"!Yarh_i!- 6鷄 <∼++"[L'ɍD+ǐ:L&.tɒ%OVTmڴo}}} ,XpV۷o߹sRf؊+K`dLiUWٶw1ԝy#pSzO>9sCBugge|oԨظQ#)>>ߛMVʕ3.2NQO?֭[a6` "-?KJ5wΜҒdڵ?*|h`\JG:V 0E{Jzh?-b(i`LӆJ,o@Ghl-/BƝ5Ȳi&fIYFЄz.8=LBzj R*؂bU0>"`}טKww1B.oݺuXjUAAyƎK沶aΠKrU:^-7{&vvvS&OZbҥK&裟~^UQ.g3w^ƍuZTT"7o^v'O9׳j PF9|䈻{VVo-?lڽGgH 'OZt˖1Bˁ]F;ٛTx&bc&l-Y cNz`~^3!=F@cFؙW{sx¸J|LՎ\˒/ӛEqAU0!0ajk ƚ3e ŒA2"us1j+Hsy۩Vzp ;;={xvx"tUR͛7ȑ#,Z(pM~{*lwvo_LBё۵kw?:`qIn2i򔒒gg֭[T"f͜eի-7|gLzɓ'a6yԿd?תUkI,-[PTK~xB \ᕤdįԇq!]zD,"%=njWC}'W0ҭRl`iF*HD ~̘.@*1`ZaC&UHE -y4xdBH DUjI"Q_Il$6f߼w/:u8ػw/ M!MὰM w{'N[V{9SWe敇2'/ƚDJ?阩`Ic }>Ckt?XEj~eJ+*_ 5Ɖ|~LY2&*C +ߕX"lcƧȞN>=qDJ~QF]paӧ B\K sEUsÆT׶9s[͊Wۙ+՚ :,CnrF`}cE9Vm60v4ZW0+JG+ ]΢F'&Q/$βOo3k`n5P# T4˚@Nkq ֯!,\pK8̭(wWA +%:thZv~gݻT*uޗ7z)ʴ`&[ r.li@ WsjXٮ?+0\6xh4j%&,΂TfNC2"YOs7\9RE+RŊՊU`_W6`nݺES׬YC0+H~ҥnիF:cf],lQ<7w|ӡC7oؾ}ڵg6Zer_eoݲ3Ѝ7fRiL-lWc·"Ep·x GVgekrj*TbFL!@N "HPP$;;gF?\La:c>ӷOׯEGEB&NzutTa^h cJ>O׷_}Ϟ9K-,(=z;.S*EGE~_kꔩٯ^2?ӧNӷogϜ!d^pqݺ}2 GBD2l谤$Uwԩ,;w82?>|xN8ѭk7P S'O|~ӧ!хs=jGJ~5&..Hpј'NPRR5ճN߫wNe^eeBj{n?ֳ{'O@ 8 ڴnpp 5::t@֠DpϰyqjS3Lrl*p}KYrFK57Sb`1-O Ɍ.tqi`t Ԍ&z)Me82ȫq 坅S<[,Dg);VРqS[H5 X{Ӧ;v߿M9믫cΞ=SW d2YZZm[o߾a(AAsι{/uLUڰac-ڳgφ̙CLMM۾}۷ׯ +VXK.]v!e/Wۻ&LukEu8p T*ySySwq֭u~˗!!/^\뚍6*FXyy{{7l굫J/~ǏGDDܸ~aÆQKHXZZgϞ7o]k׮ۺ:u4x={4Rk׮ vرn ߸q#**իrkQQׯ_D$EhJJW_}U+AcY&<"ܾ}5k.\(JKKSSR7nb]|mo\/o Z*:&fȐ!֮UT$[nKgmY{27;vO`,t|-5m*JmL!u 0Dj=}0'[0 #ޓB>N,?6o=jd4$!#ǏO6],s-֭P0L 1Ϛ5K$~or?7);Q @QQ/]%ŋQQe^|9::2{xzsΖ-[b>߸8`2Pu_e ήnz37NFEetakJj?'O 0wu^ru摏't<#3.QN6i/+#54o6l=j便6m::8T\jm.:5]D:W%%%\ 2BB2[dΝ;&LkҤ7btz9&z9Oq蓚pQf ڹp'۷T*2er&MrΝ;[N.]|yر &:O83gD yqJJJJ\\] 7w7I%ͽtyMr6n7v\.y攩ST*ɩUqg.**rH77BT%ʲp@F,JJJ5}ޞ)B<|4!"3]{ej-T3ϩf*cjwV9? +b38w€}Ό5RVoܨn#mcs^hhX2L8j׮^/4$5-mgB(}8WfᛑofWj᜝K$JЭD"%yF%H$"ݿ30B||}M>mڴ3g.]t6OaFD:0RW3fL 9}f%5E4+vvls׮MŹq;wl&v ,BٹX"QD")(,((,H$D" ٲyKXXhh͛CJ$IagϜ>bqJ$DkDZ?r!@blf }?F^^^/^YH$VR͛Gٳ?jzϞ=Fsm~}S"##?0t落{EF6c!I&߿}v|Zމie^k޼]B!1֫Wѣ}_mzlG&L|:tS2=vmEK[;ӣgs~[Dq0Z iР#2c)h}1b͛ QQW4yԱSukM2xN>l]ûK.2cO }2e+7WWOZ- kxyݺsC"qssZ58 $KH\$!!vBL t"bF~CƌsCHdo=p ġBiG:_~>|DpHX,H>;nlFffm6}t*R~:GHJNt ?͛{?L6lI0"ַď??/:tXeИc=6lذFCyvk1ѣ=/[lW L˳V+KުU:u4mL'!yuhQkBLϺrʎƍ֫gooE4fظ> 11qqDtIնo!СVةࠅ ޽'[#={6mzløħ>Od)U-(;?#0I&D:_WZ.7^#~L17CjX%0\ Yho+ý?SIw!,';UtwiT)EN"T rT[?s`Pa)~s͛7{M6ݽkw /FWX2J3ڏ\ bceĸu7GϞ;vl0*Xƥ"1VhZ93@C ~0P4NU-YgC ZƺٰOh|_~}X Zà*݀ @ 9%Az .JqRlښ58fhx IDATDPPrN;E뢟v?\%al+r8S>Y1!,ND Rzz꼼ЙfrP(Z k2lKlD Dݻ+sssիl&Y VeBpE&jrZdnjzs)ӚjC*LvxxHL`+Vǹ3ir,vD:i[-!T @P8h *5(W@RlcGN[?|ܲXZ;(W/h=y>UMu ɷ3f+b[jmv>샌[Y3Yg/^T&ɛ^UMmv%+M튘CfƬHhfCR>DkEFjjhw"%%ŋ.͛a+C1@ ҍj%2+D(~D2*;)JG!]t`'g>KKJo3Y0;/!N&|38IҠ#Wu_lC[Xt˻9'dM:|Ų641ɼ lT\˿+ @Vn,CWsQռͦ*ymʆU[9dFF/ŋL??lѷVlHDP(ʬ 0pR:2mk>@0@bYq})Y{O?Tu?Yz*? )Ua+bj r@OaSq#Z8\s(pAV > e[z+HʇQ#)Xʺ fw%smIKKU;|8%%KJ;鈨Ԋb4FYUt%(0@[c MZUrr Pu P SIQKZZTb{ lQ,T1Y8L3}C;"۩0shJWo&=Vo=65+Q0qdnZQQӧI]ғ;GEsӧl6Io$.`U\083@Ty-\+[s<ȹN+f+r"@norj)pp{4"Z~eOjWaSV}OmW^=x𠤸X!a˿..͚F&U,k- _L Z63:ԓr+[ *%nZƚGYYܿWl#·ȺCm;{g888t=;VzѭG#ddfDsV\.˫c+/ނHu.Te)*V¶uAe:js{(P/x{(7(Aʒ7k|ԅ~\;{ad3Li5HTlQEcb{H$ W }yU_g߹}[*cFojݺw*8kE+AcN$ϯ]ܵ{O//>`[܂@ DN 1aPPvv[7RO_ VGYA0<&`u蘓BF);`Jz[u)aۊ[谵RC-nzfJDR(>WdAT>S4եSh .:|o8yڭF-ݽ_%~%-VLl7yբծ~0,11K*utt4CBpttzWLkR9D0.KIWv= V@0@T|cDV.2#ɉOZ'>}] 6ݤ7yZ_-T*r^2پL[A,WMVNj dr9ӾR})VU# B(pRK jOvoؼgVߨ#3bk`Ĥ1b'K͌/9_^*vrC;O>]փP(H U6|+Zgzr2f@S,'S,KqTZRb;4_[qYYk"$ R/iqQV]ܓA^&P*L`x V{}C +,Gimc%YƍD@k$@J:6EKBT*dIK=<$RP~8JVҡSBj8 p6Khevݱ{]uٱ8qTAŋ[ׯp7jekq?(K2rje H,+QbA^8IBͫBIqrD"ԭ$B.GĠ{`0/ @ 35C@a H C07ptrtprP'GG݇V:B, ŇGB h C*{HAo))iY-sJ^)VEզW+Zړ4<$AXs+. wZgP"+**7;v7 ԩ&!YER)'u㄄n gggx)wkݪeDx^.8BP>-؊"oݾc,Z 䲜!'2L'05-F ^2b;w5|4{l#33k wr={Brr]LL E/^2++KgVߠ&njck=BQYvftk'$<)bX0ahzr-Tv2[ؼu[Eg.@M8$C^4niTʜtG-;O4 ;[`eȯqB. j2\Vrpqq!``ؚ%MS>J:)YdI\9fr,$Wz3BBBr ټy;G- rcsێO:ݭ[WېFc/_^_  ^99}ի1{xzzQ3**zA=bQNS,wԉ{uRNa\ ]vvpp 8^F>~۴Icvhq&$dnqL |Ӥqc..09%[*E`\'4i܈Gڽ{.QW^k^[< w"ߚJO<&=e !l# 0 K2@ll-Je2JWa6M)6jnr39w /9::899%BH?~|VxR@&}*^t"ڵC999988 tn:h  .tP똬aqqɭ.,{|| HÂK9ssb"+uh^({AQ{h4gt%{Xh`o("܁Rr\w}ٛyw߾3󎏳LKJnbze,Æ _n%tr%PP־ Ĕ3| qЁ)f?AڲQzkcAT+&vQ&#JKKO?J,N|3]0cmv-vh p)&T*_oV-[صgvmݾ]Z jr߹;lIIM  LIMqvq"EX[X%D N+:s<)T eRuB:ǷO^㚞[r c:Yښf1 #M2JvwoaX|.[PP` &p@p ipMJ4,S*# ,Llbs5dՠh*NƯ[tҥ#Gs;qS4VRJSQuqq)?´8.wgϞx R_VӞoD C=/..?~|Æ ϝ;'/_N^z &L0nܸ 6$<%$<#$X7o<O$G PUfXu{K_nl\5Vmtܞ: a:+_4iԸqpAmI_@׿/ԗg&oŔ*5y='+K7u w)((rAas!\W=!obY,\.'=z_V*<Ãd]=%f߻w̙3ؿkΝ{#Pd~OH-͛={,**ھ}{YYѣrh^`WU*UaaׯU*@QT#0+ :C!H֯_߱cǩS  9sQ}?в+Ar7b\^bSl6fT"rF6|->^4E[XҲRW7ěC[]\\bxŨÀ愌##$* ջ}SM(E;~Y67+$&ʉMJs98Jʡ\ .KvK[u^{QOz0pAXe/N  OBE9lPAjZF`3_Ϙ% g0//ǺY@ yw'XVVJՆmL< LsV+eAY/˧`P#*R\=ۘ#x<L&YfBg94m5Ԥj[VYXu(s}xTj0s7N!̜ynm$gX7%i ޮ 9Vm^m\3"4GS IDAT#GBBBzE@@Pn;ď[1/'Ozxx U:wW_8/H:u`p===<xllT*ݴi34/ʪ͛7|p k֬1c֯"I#ٙ`(J6MNlӲ9DQT( KFEEq,ʡ{Äg4ƍAJnS2NcFk7%FN뮬zS]49 Vc+ bŲ%/[ iVCᵐ pF$(+)Yd[ ǟmW˰lY:@0m4jQK! rI'(oaʡ+\ /ɪc~#Sz -$s@UTT:+WޤK7['xZY5|ԖT[rLg۲SAfͪ$=!w NִѲek8q"((hƍum۶ݶmL&۲e n٢ jj@.`0&MY3~x ê#0&& 6myn&7okn۶MnhX +Ǐ,}4͛53gΐ&Ota8^haɓލ?*7;;D9ber%5X$|Wa'5j'fN &vss+ĩӇ7n1p>?sC vss3GR&U_ {&DްM-5әr9SppÇv kߦMkMW~nRo'W!1*V^{ٓ E3WC-Kmjfnj|srܫݎ WYfAZ҄\I 0 PcfL <@&%r*Y-W)=Ex8"&Zswlt/͹GX\;/GTssENNY1õEV33V N(֎tM~C[qf}k¦+CMmd5U%(-ɲaº9Dl|MgkRO Wu(wh#S4oU*^V.y攃PI4իsjHXXݙJi~ Fl6mQ ryFh4Fj~݄&LÒj,eؓk][yZ״" =U˞j8I^wﮐ847G|5aҎO?mO[uN6=}hf͆-YfӠa j۷QAkLm9b5csJzoor3m0jYF*alZקusIz'0h~7+QAU5R_hw]dL&: )U<ɆsUԡj~Rb Vn`w.XMT4g% *B:z~[iРA_4hZ 4>H5*5s+ %Y_sgkB>h+gd:v0LVlѡhɇ=SdQda ҳgxAy_[t>ZT*:EjiOv~'PcjeFe W1j5 e4mٙ.Ԧ~'s\ T$ %e?yyy28+a Կ'uX,ͮ3A.:v3{[E9KJ6oP(p LaP(|=Ox℄A!B~TtբEӕ]G-gZL>V,[b0 V}h8M='"surrb2x% f;wt{?i+"y<\V2Vx"g*+{lii͚hhr6޺uk8ފ~,<,tׯ딗[ۀɹ0hx;w?~}F}رÇP}A۶mqצ}#\vǺ 3Zܥ_||UdEz%2䖘{}XdL4I{l HT-낂777ONv{^oQ?u|p|_k7eC[u=lCV2>sim\'Lkhnxqۈ;9ݛP0#飴ug xIQS~4{fU:}Ǎ Pإk7K|GG~MA8ޢ~,w;l(իYӋiZ%a&jF!k+P(kP̍XcGJDĚ-[#"3onGDٸɶmIR>];&eG))|rQtsI**7_}rhn&O[Gڀfݻs8pЈ#ڼJ}ŋo߾@$={"֥(WdsMJJ5_c0tի?NNN5s6.\ڶva֮>p۷o5~MʕQcQiP\\f6~>}i֏5jڵuTg~}%+ׯwGa ' K<՞M%J&`jW@ 2]}57@?д%p8z liivō?}yC@@Ъ}p?۴m&yYi|إ{CGH;-&R%+ vb.۴iɓ'66/^`Eޞ>}n׋-ԩD"9rΝ;/^\kIIIquu HII (R֥H?S.׭[{~a˖->SNӦMuvv.//xB``m|iG&%%pcǎqysoڸq=V++;^^U?@,}֓"ɡ.bdJBWP&eQ rrܿ?` KeQ#W,~Kw~,5tw+BN?dCDqq'֯oф[Z&WxowZVZ׿VFx2X,C,d2ff @ P^Pl2W=`3Aһ$ʠ(JCfZZ=݇:ua=+&<܉;wYsŢGYȱEiy9.{ӦgΜ޳gwֱR8|Ȅ ubׯ?gl g&ϕJ폎ׯO| **jܹdo;ʕ+#G1bիW>9s֠ASNwI bbN49== kӧOV?߿2WvﮎuuӦNԄdddL:o[jmj#I3`E^ӧ{ j^AD6%٫[JJEflz u=|}Xծ}AQQVc郿ETS?8;;5˗]N,..OgϞ\BʕFzZ=O>MR2$`2RRR2-Z˄,+88I&͚56r8,pvpC3'`P&UKO{:AC:X^@ڗX>l+`"#ΙC^ *jHKKnjL9ɔd9rb?ZGںu+sǎMؿU˖;Osd2.pʥRKtJAu<6ow&MBvMFDQLtRm ={→Wffd8 ghyyyjj];͛2y)Ǐ9"7A \.OMMݿHq1zhuSRRRSRz5f贴TK,˖.-,*$ %Jr*Ҙ̼k"UpVK`O? 'M`P J' <<=+, H$!MҳQ*"9 Ёtf;t`0^&2[ X8ުA)H<آEsK:߶:xpܸ6Ld0i{v;gΝ;kH:uZnJ%Z2ˈ +|>I(dI8H$2ACɢ*6ҥ@@U9uc-:3][ӳxÙ:1T.:yRBWO(Kw@ dй^:֬tqo#:X(: +4yxz=#>>>>>+vѤI短9|Ȏ;?hvBW!H{FDVПfˍuvv#",_6x̝Jz`0XbTRU3W>ܶu 6t肅/09V;Xpk׮_eVǻu6q'''F:]mk셞={~Ïh7hSOS,++{w~qF388po.^rŊ|6nlVl~6׉y&߹sWk[RX`,u .uHW7lCOWap%)9|f mM:yoܨpE۶m[p!H>~޾Tjթc.,O/N]}݇*:Ï?x?1 ]"}hui8wmBe_~Gϝ;Ofj <<܍:s3ygk<4B77YflJJJB@T\\J Ca(b8(aaWi֥"W(//5b5ҎyϟZ=zDbʕ֮+>X?~}l\7 Jop,mupp??=fADTvʕ׭۩J#wz0~k|E#k M@dh"Ӽv"Xjꁖ}U/ smH&~8"υ =IW{ IDATYY@@Qt페~Qo06I܌5d@ PuV]P]h\hTW^s[,X@A`m>geߢUACϞ:Μ9:߷/c$:u<}|TT}#HOO}f=zO}ڪU_ТEAΞ= owqqqӦML[ s.IױkO+nM=aqAHT*ZMYl6p8'Oi]^Ϛ/0\ߟիW۷xqiiCxxɓȵS_>999$$dennn仕cd* 5MJ")AEڃop`q&ops[)NQ|ܹ[wj{ ]چ_=sF[3gshrss߻K%s;8]ZgT*խxxS$n{ngR 07/Ws/*( |/xpqs۾e3Ep-4g];4xIT*w-v3tptжwn.TV5 9yysܝ?w6z8[ܓ ` *%H[3Hœ@ V2%V iа 0 l6u>aت!sO-dvQ*@chmzEpwwo֬g ][hXab- .w)*}/YP%"88AJLJ!c52Kv艓M`Ѐv"֜^1O4hc`0wnG u{ Qw F'ZKbU*lKd///OOOhOJxϋڪ𗤘...Cxxxh霐1sDbARqBAzsqvwg#&7,MnEKZz dY\XFKiР3ȷsAa?lզm#z(uqhtoua>Ay|GG (6kDaA>WqYM5w] y'wL&k.wT&\$!٣'OƎaXȞ?K S~;kf-x_AI,Mh-ΒA`^9CPݪiWx>9YW.],.) Y:9{4xIήna0J k8+rIaAYqI=WW1ϡvQTQTCS `5Z0w&V'zIF!c0 juZzz`ubQ;#{8:ڠ`8; rn8Pp9.]*Z,nŲ%/[ i4ChGixA, 믒Lq  ҠAOmճ2hP`@NX\7P8.H(.{4hРAL&y:f!!|BzjvmxOW>euý 4ha?a"燄4V"fqpp 4oj(S8p(4h-淕aΓ.\KoSFV>HAyOâAk Tw@%wEF-kB\77-? K놟4hݻG~`ѠQ[<<<#{iP:.gH2f6@Ď~@T!AAsً)2$99@Z/22Jp"((Y(ܾu 5 KvNv,qOle2Y(HIa@QC< bg...~ygH$ϞyaC: <>؁wpt3vܾu ҽS}'T&l140L]\\ 9c69J9+..05)*V|>LWY7G\u/ڹc? =v%EEU7H 4hРҹS)*Ao8A s}ڇ pP8F`8stֽ[111_5v8+p~}vWkGӧ}}C2Gmܷw%%%ǏkѴJK `6Y\8nuݹwM~uEc~ݸq 'hJ&:\zu-ډ|m."mk ˗.[VsknРAF>-b}0^4]ܷN}o@ //>Ug 9;w<@;ϟ?e˺u~ᣉ&rM;vN7(̦2{w._mNBm|||-YL?|^6l8yΝ;"">xVeSP8|Zm ~=}XϭA fT+ d#F%Z\Z4x8ݺy3v#Fݻ{ !aw@*U3W,w\nx9`aa ]rT*J;ڵkcƌ5jիW/^4fQF_v 0 6lׇ2ejFF)`ʕÆ _zE;⣇?\R'0f3gxt2 pt7}Q#Me?~HWJwx?:t+ 22rΜ:Zə1cfl+W CJ5jT\\ܘ1cGC٩3g)2-_';}ڴ?~SL» ݓ/xglݲYzgg粲2~e+ ! //oʤ_~6GMX$4P`6i裗/=֨2mZj͛7U9j }qp'-/w LgioMکSGdjBK"Ht«Y$ةӡ#GmRj޻^sKmҠAFʒ,!)9lcR aA4l ?`h?}mڴڹcۑ&OLîQ9xNv6fIJޮ]cǎ+.}$AΝ?/<{YӦd`6mN>#ˏ8ѦMk`0n+tqZk2y#YrM>\%#rI'$;ĉcǎ.^:f?qĄg cǍKNN4i&f&jjNS@x/Ysf߹}SXǏ͘5K[yb0TѶmo!33sd֌"QΏ6jݚ-\6g!1Cgϙ[.-_xnLx⥩ӦhܴiӦ 5o|s>[>?a"L8aML x>"9X;wZrkm>}:++1 nҤITTԍ7l.ѣG jҤIddKj);VC~_dɦM.^b Z+VܸqcͿh"Zh a]K3al͕@X G| -fg2جvaan*R_Px'>ԍ|RY6;?oʨ'VgNt9o Z\.dϝrS'WJڵ/O׬L %P(\:8vH$g 6|Zԁa9 zdϯGj~C sU߭48: BpnՓ/]VHt< TW\O,:;ѼV/];X,pxf>}nZPPhºy<8,2-jǰ\Q˵R6 l yӍ7_޽{m.???ӧϯB믿>cXr%ƍgϞ} ?>}X r9N'-!R (7(ӳg>kz3ŵD"IMNr;8yEQ}XCq/E%ID >,}}|WbIQjR߱Ƌ̠ebsKn؜P(˖-ԩرckH OI aÆ h :ujҥӧZ?9rdʕ ,hFx IDATWUAM˧a_ _iHֵLx<)*((-.wj>Ǯ`ȸ Kꎶ+uv!˧M3lذ_^=&9lذ߯1b=~tfl+ܾ}{ҥ(/5YiSYrKF\jxwOf TnTvI~~~6w3t+WU~I۷n?/X`ܹM[}G={vDDķ~KЫ"`0ѹF 3T7Wd9b@O> 'Q۶ʻpriUQFMWOMMQΝՐh]E5mc2&0Gɲ@d503Q5ݗРA 4h'@n^ұ[_F%AVR3bJ>T fڿJRss4? c>rbociа:v"""ڵk*hРa #Ť@=FUWDٙdVn7V/@.]U~nݾìsʰA'J_!koL :ZrbQ0ixWРA snok=FQQыDTF @z%AW c8HYjeHM<R@ڮE'.tkz[[z-,.,#Vڧ^S`$*/ȼjBTS%Ioii^^x'ɹ8Zgkd+Dk("J$ YS $ dDx"湺]*...AJjӶ5 m+wJZNJr0ƶB,B$F` ib$nnԛgvO!Dm$BY>`wF?9\o/7=E$mӹgI_G]dasv-Eǿ|2ܫAWϬQ]7ݬNN jGd- Ǭ{ {Sk0<}s(~B-Q jaWWόC2 01Q..>>>!'meYWE,z)7/BGKdXUƺ^oh{n3lv/_|[CvI~C~B)^hJICتz,[fgB.$Ӕb3/OKOMA,&IAϰ,+*.."|e9Tb,`Fm̳$en_`D&ܫ-3;+#αӿ/^X?̍ML{3[?7Oh޻(άvl[|e֕}Z8J3j"(q.$ 8gt:ڣ^^43%uЋ]ۯ{yzV4JH/~7=:q+l帒a kW\qC0J 77A-7mV#o߾YfQJSh4/駟ڷo?|___$K޽{ ,t444tSYbźu^9l/̿NUJ Rt. mL淍f0n\v)RNNE$ruqqsڋ.s`0P{S>x[7n_&Mt +WmҤIΝO8a믿X|ݻNvǏϜ9YE2s?s֭>ꗕΓZv޽{Ӿ\⭨u7H#zwhib$7b ߽T)ԩӫoBi2ÇI&892v?{ͪ<˖-_lc]Mzf*8޼呭ZzID(#zGfOUPrssG٫W޽{&Tooo޽{/^6޽{UJ%hϟD_|43҈H$ի׬YN>]IrҥK.]o=zY3}P񑭄8YITm]xMQ"L>=<.H~'o-ƾ}N87omi||͔7}4ҳg7n۷fTIr ?wRVI|[:Y՚*ԓHH##:.1!!?7բjžʵPx T-ž={veyy]jռy̙S~e˖UBG?~՗*ҥK.\8p@XnJMaXZ {z\7m hB$5n+zK)m(RWf羾{c =z32*{d21S??L<3>K0PH 8+Rc\2I!ϝy, 4ZlpF7Xճm*8+XO>V5VB)o?ꫯFb,W\pއElٲ5x0Wb"< I[v Y|L!LE*%#SLfZgzV //jc1JOe0 f ɤx#%MZX)Z"$sa1!3lLó8,lkR3$ /yF 7=4/eV 1+Ӊi4/_֥{[)kd"-إݮkvv0u ׭;v,G 'FRR˲k6u|aH:uJ"V/xzPeVm&tX\xdb[bX,K$ӿ:.//k@DFʚ7oK/DС##mܸ?f1+WF[ĉ DFN޽{׮wر)S>??gϞ(:v@Yr +VH{_ԭSvx`5^Yn2<BlNzWKm54nxx8)jg`-V8F.1>rIz t(?J\&O%sݡq_t?h*So:Q1|UgLxpkڴsg9ʝ;In 3t-W! mlU/ ݰ5k>5QNJeRTjM252W9sVZzGOΜ13//)K m')KK@'{`c0XGҘ J__B?~btO9z`~Qt=`vv6(qڶb{^^^K_~˔rLUgriԯtG͛afF朹s?~jUL+@ IYc߇޽{tlR_e2Y͚59[nʕQ0'ONM}Z,~hҤNmٲ%rssڴ1kK{z2ÏZl)!I*YW/يVg3 9JP̙;w SJ0I|w1rĤIG٫WRr1z>dYvEvDռva]FaPh&Ȋn6~#իW^ g~Aѽ[.ݺLX1jȒ7xݻ4dG@1nnݺ7lL/=Ѩ6:kR.7n6oތeʄcٚ*TrչZRX6iXuTH[;+#D*KMIeJZXCRJ?qA˖..k0cVXR-7BjZ]drLVVF$)Ν;өKW*Y 4\kҢi1j՚I.r`zKIMAOE"ў}t9 -lh*WYEoӪ:1-Cx]GbZiu&_rs)G)J'[[χ,ҪnYFoSUZ)`t@U!/OWFGs&sBoBjZ햝PjRVjZݧwY@o+Ur)p ` j !dddߍgD.g_ QJoASR3 Wԃ0*Bo+(I}#Nbˣ]ѥG,%I,!GvMl:y-ZoJ6\bSjV* !#HNN@'{`SqͲ,qN-[dPh᮰QR{xX6=-=ۻFfV-wMOIMmڬёR0LRRR+4%EO{H O\j 0fg:wc.\8OFZɓ.^P"[}l͟nӦ5KBHvZ5k^r˜Xk$@aVmMbX,ʔ IDATK$ӿ:.//k \> -Zԩ?~S$777 Aw]zͰ#[M17ߐH$!;nmS~Æl:<ՈQebkfT~x׿F:4~u$Y%5jm_ϗ_yjڎ/e,G3uN";p !6= $$t롃mTʥDhl x(okUS֭|#BBBCM"x Ed*]mu,|VgՐ{`Fۍ7nذaq2w;z___qqqyY|+e (eVBĈSɄA^d+z[ jwT01td!h\n DRG?o}#\j8f\/ۡN2D,&b1HH^^_D7Gљ33==P}7o"}ǩ2hq0HV"D$0WDiJJfZ] 2T,` cSj TA*7HhTh6<`wv"m3J^h5r(rqiJ<rG _UK*ܜL~M d擎O"iJJ5=.NY00 5LX|W_"X Qˬpq t(?sScuxj/%áVg6Vgq$?//;[H.ߝ>r77l|YVd_-0? Xt],x t"=8=mbwkQ CFfL.LBy:WFfJ,ڰRT&i*tS(DlE=m!^T[܍t`@^/K-mҼUT*]rss<*ÅPN]*JV`fJQXpwáz{$&%Ԕ_O}C9C-aY/O WoΊV̌5jXK3ä$ 8hnEMV6;8D~ݕIujN~|#YjuS)5}j֮][&P' Ys=gN[\.xۛma@Ykf*6S,V`Os_XuWRONnNIeJB"2??O"֬˖ieU3rru\KS&'j![̍:.òRT"W0 K Jpۚm)k1*\/[`VP-•sc& ͦ:њ#KUL+˲-X`-VD!.2"eV&FJ&jʼnX87P Tn)) ul0OFffD"SJIRG5wWCRni/\h԰!,Gkpmt <mӶqr77A#2 ϝkۮ]e] Kkך7kc[AM P~eH@@RwE ޵yPСGL*U@[ׯRnт(->z&u-<\[$]^{`=NeO:T`AjOINZ!¿޺eׯo.P*err2VѾ~n㱣˗. pRpp%O?dVcl+ov32qpa "uk{1&fzd7lT߸ӦNٶsWHH{)kHj*p*` Hd+pxVF1sq+Vٷ*aYJPlAڼ5_ޟ&zvy( .j![@qz.]!8Z.bXvFza|ݭPV"H] F#)ZazW0UYK X!^u+˲-X`-VD!.2"eV&FJ&Wq$ CjzBVsgϾ};n.߯oM j׊a,T;9mlh|qBimfC4jzyy>\;ej;T8J9eM?'0,k(d+pqf͞3:b˰lV!!!QVO8alDĆ͛Dz׷lBCrﮝ7_i*Ji^^^3 2 #\]]\D.ʵyk08W0yFPϜz{=gŋڵ#|4~]`0X6o1>>>˖,޼u6t+/42btn]=6dίvM[ ʰCѭkS>=rjƍ@|F7a°3ԨQ#/?XS V`WJ*+3çf-A#:lXR҃۶ 1r{Ki\e5Z휑#!#9|ϙ^y9'77a%ίmΙӧ5nԈ?1???4$M֟b>G`53O}ZN^}+JTV>tIrX\l@@DbA͎J%wxߺ̚3{CŠ woK$R ZcпΜ6FC)%r܊aZM8noޭ^yҤq#BHfffKʖ@jB7 z{tIiOg]ǹ)x_~">_tUV`6i7/_@ vWDmژpCVfF /O6I !=s:Cdffy7oּyx>/[nfS{ m~/T*exk=..4/˾Oce[\YQ~P~P~*UYѱv] ,:޽ж%9^80R<JMw![S%yԺmM5`0?wmv5j4 7\iִIKSK@٫ V6lA,8@ ,a*~GO)]p];:|KR5l+г+:ԢՄ TSװ^$L0VLP v>zݻvBn\-:BeU r X<+KN8N[ǎ._44,K!K,>qxʣ]~s zl-ZijIA`?pcv۷c0/L_*w7M:%66 y3mʨ3fΚ6}ʨ sͪU bhB2O Uf;ڃOs]B>p\Űlڵ.>0LO04XM Nv?<F#)ZzWP-d+`z,9! 0YN箛VU ZqΝ=O.U2`|) ^{`=fO]-J)[,V*8㿜<٦m[k9cFNNΨ!#Ni:nm;wC>own4Ҟ:{ΞuJ$&).8l0kիV~f3eHHHԪՉc#"222Dz>k٪#|Rw>J&*j-qJho+ 0 qFPϜ㯿4mj1{˗c۵v꼏>ԹK Uu W&L`Soܼe<`kcFǟ5͂O󅋖hZ/*#b8(YBG1tذ!CB9|33-"kϞ&N4qÇ ݲ o߹ͭQ``leBk5 _U@b z p6Rɽ;q^5D"a9sx=4,AO'HJ;1--ǛO!b{칯mhzk}Ͽ> jVRt, [GJ!C噑vbL,+(~HԺMS`LONFzQsrrΞ;߱}B1cOd}3O#,^<3#ݣOԨRVv+<Pj`,n.d+pDR ˒Ԕ47RiACWu~]g.; 0?oދ.קwΝ?6-zƷ|c朹+--da(c}N>LRa(gd{0ɫb:XjN5I)ŝۢ&[l*mєЗ_Ky;ve(-> ӸnBіDFSVL-i:E7A'YNĘqy>H ˲"O'z }j/;eXo aecdLnS$''WKa=ǩ!0 ᙑ~A~,DhZa.ڥ[$Ī31TChQ=)N˲.nrT,1 2 |"rgYOSkJ08(YYJdR Q0\ȿ|ԹZNJDFp6.R˲ ÈDaYq SK؎o:lEosQbc~WV߻־]JOKx\@BЩG?S~8x0 ˲ ÖCX]r!_E.&n߾Ir77A6 ϝkۮ]R[BئM;|ҙ}l06ӭN* r*[+q'js @o\g+aaFJJ)]p];:|KR6ltr,uG jJ{r6Co+pD$2B)[vIqÊ,P2YUMAF񔫫u+˲-X`-VD!.2"eV&FJ&U]un1HN׬?;|а 71bžkwY㫭!8T"d[no\б}]){V-wI8.rVk6̋11##_a}ƍ~~~ӦN*ȖwPi@dRs%\q{eԊ3gM>ceԊK/{ಥK!7m4=\n65V)[=<<!^^^Fd?]󜆔W'}0n޼E9:z܄_1SJ.\8z܄}5z}DN',{#"+_+*m84uJ d+ptzkܾqz.]vvڵ"[Z64,0GE}}ѥG/YZvv;i;pa܀O> !;v`r`Æz~`5+!|lFec[=aR;m٫hr9Tєܝ8yr^=gϙiƠZN1AJ٢3^BCɓBb1!-QggBpE.Rڪe0%Wc.B1S [AUMzBQrw䨈">]:b={:r;ֹg߾y{w׷y۶ј>>cGWD^7"!!!;;Dsk"PFb:*J6H  NHLlӡ]JOK9WBLz.]I~l՜۴дI3 εmN$kk@(wҥ&yQFZFGV p*JՍeJJF颅 w<(#^J*0KUUR{W0Dy[IpH ǕFoݲ{NBȍ׷EGUTR }e1Ef+eh6Vjw{PB(-mᗭK]x)88d'7c/dgjÏ1G*RWc3Y  1`Ka=pRzN![(F̅ ۷ؾŘky1&fzd7lT߸o)f X0`BŰА D捄yزgSdXd,x t"=8=d++eXxeԊ3gM>ce kEf*Byk!khLk&,s[^zi3Θuᅪ]p\Űlڵ.>”"֭Qɮ[&CswѣamokϾQ#5m΀~}<ӪK ! /_ڍ rnp )ArJFVz-:!Do00C)mJIMMd2BXjRR![-Z\ 'N\F`c0pXǏrdmo999F OKK#>\;ejdiU5_ݺwu&xL}}MC4Ww?q]=ml~PT/3kXݴe Zz c#"6l<&bԣGXeVZhqEW,3|Uc^ !ؾ} //2` _ Ko8jV{]xQaԹAK;qrÀ vwWWmJFzZ|baIIo6|A .曙&h웿\'tN@{RڇR4�}D.YsvzhXX1w^%Im>3˫^U x t"=8=>{zgRR8!_֋Dmڰl`0<=w&#=ya mnC* Iy$Sإ7dRX\RZl_AjX,%HJ7׭pbP'c[=aA\J4<<YfaTCpei$@ԭ6B5?Y\\\i^}-h!kٷ' q!.21T2a !e_`pP2I$r7LO)eFп|9ZMVb:*0(HP(8JU"e$z[}1?U}?~/yzg gj_OP 8z}_=GŮb7(bT5{yH~XN&M]\*kjkVVHRJnٽk'!ۢfPd2ߔ:U 7햍?;|а /,]=K~v1_p~&,Rcpt %Mm箐 <$c"F:j5iFV* !#HNN@'{`ScݸQ ?0qV\2WF1sִ3VF(d.[yA3Y !kVR(F{xxB6odƲ1^{c\^߿Y/^4uڤ)1/QJL7#g̺x)vc'N95vO MT55|.Zewc0H8:5n߸~s.]u~Z]g`-VVZˣ>|R#,lLVVb7zԸ1|}aN0 r 4k9vDzl~~~-'OM,jaC#s88'B'&5d{RޱF.SJ5M݉'s6m jڪ34d8z-:!Do003HR]5 7ViA jْa|F !YYzCvO:\T!.RR3-a~XBI*Z˧D)EwFox[Wb.]:z۶MQMlР]L Ph|_NlӶm]BŘRٸqcB49cFNNΨ!#Niـ>z>JN r/7֕ҿou_nrӦ}X>7vlĨbbp=T*|yr=}~Is2L#>$m_]oƒY猎2-[!vZ={GEB>~<0CBBVRqcM]BCCC_mXƠ P̱ض0 ݳ)K___qqqyY|+e (eVBĈSɄ5]dɣh촧i5)" 0/Ws]\;0B{=gŋڵ#|4~]L0 ;14iOuF6уh %#=M(6dPB#WL D* 99Ղ `VJ)T>"a9sxTYzK$P]` wwϤpBD۴1]`0ަ)))0>>v1 =LL@Xf%9$d`>|h洩5}j.>~bjz&9gy[CV! wn#$`~0f0uI']a)J"++kaԩ]]LlA-bAUM TTrْe˧M~æc"ذiZڧ.vTOZk׍3n'BaKUZǎ7; defԫ;c֬wΘp>L{0eC9OR?1&b].׬-dabAB5We_'Z2ZB\fe.DˬL(?(?(?Lb.OSܡ}vxذvm’~w ^7k\._׬,~scO̚٠~m;v9wa{v~)_ ?A畗Mw_+W^޾eQoڤyHi֮RVW7^Վ! jjދL5ޱ1$Rp_B~˵p={ h0ù}Slgƌ3zC,K5aBW7B88nqcF vx7ޕ. '**==Tl0̣dBȹڭj/8kբ0쒻,YF]-DZ,S5 7;ҬtGc[}a 74$?O-++ߍEaLwiS&,ZU̷خQ#{xP1E5SJj T6nЧWM[?BZR=oYԪ޽zvڴYs,}.;;S=YFe~*H҉㊬15nԱK;.}{Sbh|e%;02mfCH#$jQϳYr*~*r) (*`<FӣTDEPR623DŽe6m7̼33g:UK!j0lB!Z$)ϛG!jcw [Q[1r8pCBey ԑ([322~Cq@5?ݾ}{!NzH6o1B[un}L<B: V [QCHJ(x(._ òL-Њa+mQ222BQJرcG!NfͺH6o1B[ =UFFcی?^b؊j~|&ATjMbrJ _]{B{daějqQdʔi. `9.{oG?DzBpޝbaAaRRdff VԜhun=`a!P9iYrGL&< !LA .X,_DGG@;'fjFU>Q4m6^!PX:NRJKJJBa+jNU3RJ=>/HTf߽vgfE'_7ftKF$BU!j^EQy #sJe$6N|gf>jgfBp,Dzp,P4u)W:VԼw_cKm}tNNm SpBHߍ:E-S =zrFk [B s[Q [Ŗ\rvQ P5FeǎZaAR_D322Zn#mE0F!'-[M7[薶W(}Bilb0Xm60vBmJٿx:cߺp1 tKL\ǎFBv)qqqN?~xMB 0s01$1[<y`#zǰA !z^TXfǃr@:FLmj!Z [Q \yZX2!qgo뉶^8Bb%QWdRJ8d{u[/GXivaFyKUD322p BV9"B$I@?yՇ1 3iݻu\#;`WdVmݽgٽ{16_!j'7x [1lE%Htם=Cdǟ4{ֺO>Ǭ{oO0 Y^ֺ^->..ǏiV蹡bcVznFc6Dc6fxO#l cVԜ+2]/;Vу>u֊ՃܷU|cV{xaVԎ*vڽ[*7ݫ碠_0 k-)U(^;09/j5?_?YR%^)*.b]pYB1 wnNo^[>H=#1-0V2DEju}r!F0>o /PcVBUdsٲz%]-ޘRUi]Z;:ڑoаը7ݶekfKZW(}BilbRK6(۷h* !,a ŬWuZ cu_~a3xb&k _v^i:ױ#Q7ZL1TE6CEtDQ>OňUwwž~81ahs="LK;%E iЭ.ԪSE!DoЫ xNQ/GO^--+&7'2}m-T-.B nruJL0!9R4###D^5.)jǎ!҆:5"s}o1\RWOo3BOvrzav{ѳmUFS"3ge>XYv霬0@^h+.B6N$٥^Oka+t)p\[pyֽFܑrvIp!'Io|k9ѣwRtZƓFM(Z3[B5Nc))):xPV3DQQJK;Xt:mRd:vba=Oc/aX$:uK"Va):Z7r "q\Navյ[f~BΟuFGGbĞݻ].aZƒ [ZiʰC "˄*-&Fͳ}&]A!4sتP(KLrrO8VZL+h1m/+qmCmX;YYPU?nܰB ຉD\\U&?^ӬsC' Ƭ6'llϟFX [k%4k‚Js>糗^]Tn|^ڃklǸn"%5t8q"[]+wj;kXX!PRUn+m2Nܫgo0LJZt8.r]FoMcVSUCp$MOOLLX*Mo_:!j{SzEfΙ3zo6>f']~V&Od9d Ft;]Na+D\vVM@bm#b V:x!P[ӐBJZOFwwnMH0,^ZPisŅeŅRQJ9_kWSKĦ!8y0!ˆ8e(PeYDAQ]V[=u'ظ{*Ǭ*ҭZcB,)oeQk_%! RZBVC0ƭ!ZB!6QD(akԣ@}c]RanS{U,2 CJKa+rRVV$t*?R/dd+BE绲k3g:X:t5n4@c$! [Ya.zjDZ0,!Ra C(R$Q(O&^[$?"]OMDQ'&iv[Pj>I9qdMcT%KL_cZ˲,rqj2 0 C!JJgP2̆jE&8a}B|$Z sO*).=fLYxr[5OMge9.c c !Dm!D"0D!Zi+$I2(8.<ڤ2=/?Oj 0@)#*orz\N }+˯k!P빹:aa>[la P@3FѺvPq\ݎecw88tXTYvjڷKdN*/&V2\qB ta^o<}|0)TDAB$I *Al*^#JWbs8dhZ6Ef+v Rc${BS(v^ūUJ%I%&Yw%P!i*8J![Mo0Dt9={b OYCJj#?dwBaMî|5?p(g_UJGe ȕ $M2=Q ( ba^vr⏳gN@XӨ_8܀RDwƬ#︯op7i,,*4G\3w;Y~yW~y^}ߔ)s~k:%%(c$i]~Ax/rcB) + ֥ɓ#F]s!l7~>#RfYm@TyĨk;u³{5ق[^Iն^S(>|^aD@2D"Ww'UP^@H "6e.ͅ 8Y}e:Owe3 F;&Nԓk֮ׯ_j3z{@0b̸s رcԨQ5% 17t5kw=a_m ":s)Yxznil>f[:?!?^g;?0wm=C)#ak[pWjvxF[NBne)K*]LR-IZ1v~<8x8k޲eeW}_wo-ZV\e2l6XY֪56x¨jX}k萫k26ovϝw@>ͷO?u!+Q*zU(e ,Ҙ!ڪůwN]7JiiJYH a侮*t)rEB*Q<ye6Gjl4)9ͽan-.۫gټZ LI I#B+w}NI ^~aCx<+/{}=ͦ?Rw RkU*Tyw6'vq!P}1U~/oͲ6tİ?bݦ4V*7 *oj% (W$kyKJb[lΙdҥ{}\X<85}M&XuX}r}Wdﳿk8p0O>n7HԷw8e쑦TFNd *)(asUr$IH*¥IB5@uj.\=$I gT^b32Sp;솨hNr%Q,-.P2 e"1. ے\Zۨ$@.b9 Ŷ-??UWsµCYYeeenj &`ۿ5p,U>3@^_fAő4ٳYԮ)qaW΃d986Q^9iYrG5+!jP= r1c;?^b璉JL1eŅK,g[⥟tPVVBBBۻOQwzڹS^{ךR&kqߤ`-7_ɓfs.̫TL>(6W^ܩ$EE .Itg~1tW;vyՃ?;K>Xg'ʭP}3?^Eznws59B!/! OiZ%Ba+Mi19;mRvFoݢJ .jRiz%E(;׻G[xv50pǎ>xVpeTl%&&7l~Ъz@.SN@߾}8>|C xjZ,>;1o/¼_xq uPsZBm&e@e"Gº6Bc-gYiYqzc'H퀧EVZ ԔH֭k׮S$t{?'N +H۫g)OG}lO?8񶫮PSbZl brr2hXW }]]q!PTMSǎY,1,=Ocs;'%ͫ4b qK<}Q&SZj ~ym@MCy蛞mT~m k_k[~5k>:zGԓO|lݖ{d2qj\Ytt]wݵjժ7v[y%*77`Jtپ}{/;j{#Um5%cϾ힖&ϕ d{<үrIqaXMjT"DV!]I{iŢg r X/ۭE^#^,,:{-<"YXON'z밮:%I4 3f<^!W_xq?N_5ͥe|>xVմ=,]40\.O<~\%^'0L\>3IIIn8y$Bo. !Of2A!UmU%5՝ j^P*s,Q)J}>Q Q:(sϝ{a݀6*a$6F+g ))I&ݷU5-YϾ6/.W q2?~xjjɓ'n:dȐ}?U`ʔ)=\䯾ロP(Lb4oF;w… /_zu=;v|QO?o+{ر￟y(BՄTʦ(U)1&cQRfEAqJ8!rxl2Q;%aC\ͷ>z гGZ~yVaQћoezJ'gz%K͛jlϙ=ؾ}.]xe˖QJ{}?o)S[j' 0trغn:8qlro6 _}wu˖-0i$<B%kNODW$wX-9N?v0,׳[5ÇvrƬm).@ZRP*Ewu{=?X)S?C>mx2R9}W_}uԩVRN:۱cZ馛=6l{u]kϞ= 6RBFgΜQF%''oܸjb jʹ ԚdJІ;u8,GWf4Z^c=MD_M]N)1G~m˄ר:ƝxG%:ױ#Q7>m@||[o /8Yf9kw߭ٳg̘1O̚5k߾}/\^űcVM/^x{gܹ}]q}B5 wJAY2{t\5r)~; 9cS}8{faQQf٭5k.ctõLBJ͡~y^(yTޘ֯__u7|7߬4=UJdɒ%KTMxŋהpF 1bw}x)"P8vU!lu p 4~ՂsQ)K#هM&~24{65EQ^~9ztͪU Z]"#IyW?|>ɓii=h PP]O'77oڃ~DeDi>WS*<(f̤7ϛ}m͵4`$4~M&ݗ]un0D!Ԧ2Z݈8>OggϞ7p}`#?}?]?GJE5[1 ケp'+CyW\lyh\y 3zru9_дC޻o%ˮwݗ_}4FBP[lậT[ؿjG>o5FJ\JѠ}M[jiMW7_|?8Z\ܷWJ acRΝذB'Z-ne쿿c|%IZç'?p$I M ծ!BqޘNJrm$Qmߵ;77w&C<}Сpmw>(ڹٵs(jj|o;鞻$I;_3ߏ״Ҫ?!PWJ_~;oi%r5oNR9۱Yg? !slAۻwGس{:ll-l+M5|T:ļւӧsL&cՕ B!a+ؘÏf<\ƨ# ]{n߱PO)hǎ>xVpeTl%&&7l~/ Ka%J~:2EJ{41 #B3ld[<V޺{U*=8?g90*-v&RRSN'Ruڵke%֠jYYG{\o˄2tZc4Yvخը ߇#x !jakME4jn5|ǟȑ#0[cBl PCԐAc,"鉉K }d2EoE_B֪rbPƯ5֨( nz!ςt+|nm^ذ1x`HIv@tky^Zȍ|>)fz3\B{8@1tVpKZt虧|˖gewL0'׬]ׯ_!:x=wڵ{ȐJv, Gm}af+B !RZ.ñJ)X1@@Jհ@: ؉JɃC]?!8U*q È(OZ;wN)JZQlفCU[t+WɹfyUOV0DJ+n4ر롇ݷٲLJCM:IBb HzEfOI@J0˩9VP+K>9|<-J8)C_O5^7 *aI!j`0B=JСBuٲyӬ瞧@6~po9gk/X@&>>>0%s/6l y4))Rn'%&ΛO s{W|a+"j+2[TV.$ax+h>m@b`gPsc*'eYYxbgB<l6ۀ@TVHl0wΜ%KB}f /[9)QS׋dPOj :x]PIԝ)IJIQ8GXr'(JNP(VEQEA f08 f&;w.\bĞݻ].afsmgXu⢅ H_>Ÿ~$"Ba[0t9tYF)Q@)PI%A$~B="TfVZ@I$?~󥦦ZV7nn햰͡5WG;YYPU?nܰB DQx֒Rz܍ȼZST\08LBxo:m @O@&AM   JT$Qh}-c$I|]t9s֭[ >>^Tу*Imm?JKDJjjJJ vֵkJs˂vaFe/ *h>tHQ^"𗢚B W %JeKgK^ +ψ Ju*ǯR j=4oqqqr(~ߏڞ<1%师@htAN;RaQo;m>}͖8 ֢o7}C(MLPZJE!Zj+ jcMIzkɎ9gV:kuIn26REKLLZKK&.x:cߺAu:%&cG^UdBݰr;IOb|"F1H$*c.%*7Q +RJk-*tE~޽{@5/΢u8.K$YR}PūT ‚f-AռJT0 zRa1=OVNQӯ h1 5Ce5 RIBljO}`m\{ml\t]PI*)ڱ8czA!VS[逪õ&iDee6{^~J՚})%DU޴\ z}. Rk厱(B ~lNa0J%_|\hA"T8].0XA!P[[Vr\t߄ͱQ;gr :,*,;PbfۥsN+YEU(S\,=? kt^ ! ըyJ0T[8 !"^!@ h+Pm˕%A.w.i{hڈn_Ń <;b;$t8{Tyn=`i+H>Ӗܥ&) R,܅\'3YA!Dpf 1D^ ))CN g nG~D6W%ں3l6^g1ܴj "B5}jxUW V~ѣkV4Wg0@yARXQ b@BAdQIn+tb6jFn?`=Ǐ7.t*I\5OfO>ѯ_z=F@!&EW9lp*B*QGSJm;]qB%/Q7.k :-[vPvu֢E:nUrs(ټb'Nxg[|"T]*@%IZlΝ ӦfdgcwBmU=Ã㎿ߎ>nu>6#0!P7Wu;,uGcGOK2Q~M1`o8.z~^>[^}vq_"ZԶ͚=Gf;~=h}{ٽ{{vv\C 3͵>xMÇ>D~םI*-BFUB Ο;w2뽟\~[oŗ# Ի:^{K^gm7]w? Nxq1Zq׏w}ʬ *KLLLOO7nnX!dZRT^;,Wk@!֐BQ8U >[o#kߎ׫kZJ~}w,xm*E/o⹻oUW@^XrQσUGi5c\D7t:O8ڭ[׮]+-+ŬV>rnlRk&ˎ۵5`u!RU%OR#'J};]q++5:"UXLZHIbku_'kؼ+Jന yԱcK %'&&F[, p1J[zCNm[nAG@MJc0lE!Ԯ(ʆ-YVQYWgī|fk_?OHW;;u: w/xcvX766oX0%d63Tb "b㿗XM]buBǸ/~us;tKL\ǎF_7Bve9N'"5fN؊ Ta2[BW_ [)=yaҤ{_.}$ISW +y/-- cTT4h(H矞 \]n* `W%Ͳ(yUCCޠW)T`:F !u#jW\BY@?R))U2 jΞ9seJjʗׯ_UWl2N8"j5ꬃY#FYa;\RZzOQF`Kv[[;0~Zʕ"7D! )8[ v5p EQ'3B;A!cG{,$!j./^P$b0T*%::,:*^jb@5I&B!ԊTnV K@f٭a&!Pdnw8:m֦ 1D($!j _(*j+c jg1l 4D!Z .]Ӓ 7@$bP0,[Ɖ ,$!l.\M_+JA_UNOLV \r#tiM2Bft3EE 1ǃGe+dMuEUQJᴵ^r8ʓD!/IDATcq,BnVJ5f'~s7n,Z5 [ZO&BK>>OĤ5Lxn TU]d"B(cF~|O2/cUa(6]VX[y}(f1uhBE_.=C) rךI%;n=>mgfd7܉@WDQH XhKm}۷}V"ZZT.-(ZXW T nv7{yl6$K|?̜9s9ym}P{.zF%[O/N>ߧjBok_[:-=L沿NC 7"z$bΌ}z M*Rpӵ7Me!WOW\b!#oEx=2KvWOWs }e ݝi )n&Ń-sMs=VR;TU+WlnUꫯ7nz}&3=' [-ٺN?R씔gϹ63|u"[g= / U O>~MKoMK.9$h4utwBa"3P9WlN-5`lqlHWS5D@d2*Ai4<o3R)@ʌ%ߘ{6$3v_YeY#uu!D0d,GIXg{{42d9!&$=slyW~cnjӓ ]P]C ̞_VLk CB43eZeY:UB*,˜ p$0NW/ZyϽ'7NsmRU@žD<7N;`0\YUc$kOG:Ѳ Ϻ0d+E4[O0B!Aڔ LM($I$JQ2,;y=)KYYF˅XʆE| "ǾD oJ"V'5}RL/>o D`}α.L6J^ò,.>H4UnP(Nh0X~^kE^ZGV5csd+EQZQܳfy;dǎN oGaB"hq9Ģd$WjvL)S.uzA04ֿcwt:x[a)hq|Y!D" N(U43ؕ^,_ϬR˕hxjSO-#4Б>Gq4+V03oֳ5BP] 쎞ښ*eUU9rX8 pE?qC H82GrVHz^Z 64v%BBH' ӤX43Cɧ43. #.s۞~yĊ 䖭!^**NLJRY,[y/ AxrWnV}cY ϓxNc=#p`"61x?xA#q) j]߭xjM79}YFfX:S{ϋxN*z_jB?~jyz]YmXok8\Exky^9a0jPwHzkl0 56N[vۭn++Ok_}mI+濾v݆7jF:e40IR,ǃK&Gr_l YdvfHC־,qYڜfriޠ2l`f5f[:]% #qQ7gYu)GJb)+{y͚ys/5W/{yQOj0x~Q)OѪ⣏>:>Q,Ǖ[-VK9tfupÚW^ϝys)yA'f+:]>tn¹s[1B) >8Kl tuϫ9OXSlN|ٜ|`݆?,^q9'NR%&KeV)&dR2K9L&qʐ$pRWlBa\rq%GNt!00X-4m!4k9:nWSs&1It0&[`ۋt`^iJ]4MDJNܚhQjOR:.Q>!-̧wqVBx7RK&!Lܾ}R}&#*+=8ROOy9Np8{V>?kփyx<_M]=PN0Bapkkrx9@ʲ\ QURYUUQY#4}iu\Cˆczt $B0][1%>X8ЁgBX4ƈī&Q>Zc|4}aZě1gDC2W/ZD_{m~7@To{g˻?ϞveÏa&#낫xFÓĸ :}֦9sʬp8V^۶ ò .UEM3Cz!&NXF%)rӑ}> ܹ3¡`peIsH,Z<[TWmb)82wqpz,e]u'MvֲC2eYn:/5nm Ϛ';mzY3ώ RUY|kW0T_g+(&2hkmnc:0h\#YQiZ d,O.hsG…RA4ʶmVrL->gԯ14Aaa!hx?U5{WG>)H8ci\*rw76O;lfh: _ok̥Ŷz 5׽_?`\zgGž,K@ f=<_a-h4n@( M "_W]]Zj.1x/U 2v%lBB%c\'iEa$^n8|_Jo(X84jfxJ$jPUa;[{p9u2WcvzܮE!^4/e=gss՘i( _]#)HfssE߼sdvcRCR&BT8k`R.-]W~=gXu hEcx-c/l64PEs ħQ{cW* %0FCΐ,<4DHX_ATw8P8 C;wM%1?K!Yi5",:3ۛoκ`ՙfGQ5k^{sֿ6lᰎNbϟc@G)pyyD]4wEU+[ش!XxշĬUջO׬~)3Эe\麨4\:(2Ay4=~ڣj)eeYeL.m7i㺵k6{vjޭKO?!\y_ee]w-2yrj!,?355ߺzQ|$IKoYvқ^˄[o邻/Bhwoܴq+ZZZ&%O LL./ !8 %dumEʭaƲq"[ Lj/[D"!/vwO?|]!_}|wժ{d!G~Y̿xCP{{gV޽祗WϞ}k-?#S&qӛ/fM#v{kp_.6&IŖ>;%#L =SNd۟!sc72nD9؄3ǝs9, Dq7n)5W*q;GG',>7(=.BEUr3EQnyZfIaKJB$9N 5]>$oY/bc"O>h| V\{|cr3s,%b`ݩ(c#R _x"xG_yqɒX߻VQ~KӠ3Μ~S^o?ӷzǔ)';Vh8n(X\h;4ضi\>n!WT{y^WwʲtΝ;Ϝ1# I"(;?X>ĢEKޖ=ʪJ+;)uw3&(ᤃ_~ 0¼`|aF54LUUúTxA  ۖݞGzMNIENDB`muse-4.0.0/src/doc/html/split/documentation/midi_config_window.png000066400000000000000000002561001404105311000253250ustar00rootroot00000000000000PNG  IHDRaUW0sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1,tEXtTitleMidi Port and Soft Synth Configuration棆 IDATxwE=a#.KDN ", 9(*f󝂈HT$r^O%aabLPgwv/f穧 ;"@ `0 Ax+ ]Xw^$X,XH{ɨ0i^j/(lu0R"S|e3KOjIIJY`ڟ2($E(:PN?u Ӊ<ԩSUUYl "32Μ9*))^C@pC]O)-YyM*T0|"Ă%"p+Z*͗vbJˣ$&D ğȉ@N,/#s=u32%H& gT/4S cJMz3 c*%V邱S6\[nEr~-۶svY^ ;de!rU'V9ۈm.)ù,^{ž1`q8BӦatJLNIEbbۋjժ"66Vk=1d d AvqCo!D5I dm c( @2@0. '_=΅1@Hi B rj\$rF}" g]5J9 ~ Yb>%UG!\ɫ(&\)֩df7k`3@(xu?7aPp}u4ꁸJU`?JNģn\xXI Nds8ԭ_/^d=O)/z1 k2"aLTMd 25M'q$(\k}ZQQu邢"3xt+D{;HU5HBI fOR C2_bYBGD$gOo4o{4런h( m36x]hL߿Cmފ; (-Ki/I?2 Э˽.0-vdBߑIVH>%/0T*>G m~e˸;+K葜<<]}ALor]r -2!&ܕ쇵XNxnB޽P`OfnQ|$&VjCIq\NV\T͆kψJuIU Y) mkV9-kAp"ɧO  0Rk[ukѢys]l'4& )(v Zm-Z[{_Gl9eH;SBA%IC\E;wbЀY3ծ{P;"0i/Į{/@pGF=VkN 4m;۶ųO?wM?֟>@0d@Ҵ)hچV/O{ǎ?2";@MI|ءAtI>Щ(>tB"{> O F)=?*eZ^acB}WR6&"5 F!-̟ghjW_+/bN8̜1}VmR\ KLm89rR4lAqMUps8}x<dfeF())PEQ:22xT+%K">]) _v.@%MޤuR]aTWXF(O*&UUV"77O^d MOH>?ΞŞA||l1vL~'>,f>fe] +䈸,IHU0#c=X DSP/6U ܳψʺwjβA800nl۹ ~=!DI]I.̱c0x X8 >>ۦ;>$h#i ˤQ7cWJC} :#_g?"r DGA>*e{{r2˔H;2r\RKPV3O]WO" 7GQ&&>4ѫvGfĉq_alWƃ#F`Eb I?CoB|g&~wL{%];(9k6lhذ9Ç`OsЮqK#N~Y`^nQ\"|O8!I+))Allrs*gDQ81P!w3xL@ ]CVd%S+>$wݗ)<3s>G >!>2wgAݛ2\ƣmFF >ߞQÁx] 20~8dƍCVfVqЩcGڼլq8.q ѧWOi _,^lA]F4o}ƾ}{3˗.cЪeK 8(Ejj*8Cjвe+W21vjq+34׋&QhԠ~7ixZ*, U-oC.]p ;v{v4m{޽{xu4nP?NЬqcg }lL6 11HZwuW^3\#cZb#cp%J8wddMq_>ؿo^6iA9xIm[Ѷ-:,Xf&Mx-4o4?;uB[nA޽>}.#KBpڼnm6o|?2GqG۶xGAÆwnàp`9PR=x 2G hW5)2SG^CcB2bIP& }D_!5>WE8w~{#8@f%_♧A^N\N' 0e̝Yض]N'֬|`6/[%K4︸x,]ȑbۖX+$k].厡DJ8$p_9| zQ=ZWqP=ߦKt8t9%ۦ͛P(9xJBIȔ]NzV]&(SRƳ@)27 NNN |{ `xɧ'}Bm݆qƃA&C(WҼed`1X,xAlٲ ã`|zqp]HJZ\T" |/~ z莁իW׃  #Gu?1v;VDp.]S&`M3v,bcb0f#ؼy"Gp#4"Z uEL3֯'#Xqp GȖ@JJ ,‘ÇC3u͛7am=f,6o$s@4x=*8hKzޯNzv VECr!etrD}>={DNA~^p -[ѣãGc-}] cǎA\;h eO -t_T>Ƈd(9qHLY|%J'+v]Htu?wlJsGnq5늡9v~^_d3n/Ņah稝S'JJ\[ض6oނ5|'UwlP|Z톈Q{a#[Á v qx<^_>!I#^/.\x6M0 K4imѮ%HUW.OdGbD-5IMNъ'AKrfBdB |%%ys8hB8N4j%%:ў'M2Cn;q_˯ʕrn .Sh@.q<^/doOb~KVrU-V? x==xp9Y ~`y=!MQؖ8P\{౉10Gqaܾ}{|8{6QTT(+'><#(;h`hIOOǖ͛t8p9np\p:^!@KorQTX(t?g qF9sXGwby((,}Eؼy3'Ν=') eF=s%%X'_Y^N;㟯zHlzz:6oӁ u;<> 7WfVq'̟B,!Cp& uy9٦>9%|qm5Z5J p1dg^W+FuV JzYՊ_[ #^Z%H9P͌`".1'bbl1qصs'P55ah Ĵ\85K/ݾIUl1u23$+Ȅ譩{uױ͚؉h᠔x?dgҹ36kFS Hh”)8x :s>S'K0u4km[Lf`{Ixtxdn믿BE`;yTchڬm3vmrWx{LL:GpL:UZ nN^~o O~2xڶi/{o/tM&{~+hۺ5&? x70k,@޽v&OSǻqAL:5h)X'Oŗcԡ^l6[PukqgvDb7ijMNm_n/_o/NG&OơÇеK>r/LLnOxGӴ~T[G"U9j FCUQF}5} m;aUD3+ANUmߞl\8N/] gq]u-FȸcsNWAe45;!-$TAaA>V /S9C\|b?:m`l&$V r*jqcc!~,=F(vz}11X,z}&= XNl"bVG >zcAwU͆js{Ѫ@ݲI; V}Ep:8{;,tMQ&%%ťq)TZG:c,F-6]es{0F5+ S8Z2wdaK399uydvUA !12R&z37Ѓ8|, 7i_yR[ IDAT]WGCtedx_T^FgEDK/mE,ڈ9SA,1uF#/ߵѱ/y. 8<.]5]~ ]t^LJȢ"kpKl_Ns5C$׺mA_bߦU8z.܂7G޽uB\. Q\R ݆8$&&aT||v71ӽ*eRl0ʖX(Q!5( K?(kOOp}oGCrnfp"ge+cӴ+$ۓ+*V^d3Wd,$ _0x`N$ײ㈚^^cԭ5z=3T1:<m#'F``f~ Bew_ʧS %M"o EN]"I'`<_Eєd"H\D([2g& QyI'2R?vhFv<' giT7ǫ' /ФexQe" _,*3"9Gg |AA+ ) eG䋲^%Kd |dW!e:v2%T{*nYPCȨjNFjr8üԆ(ꗉ#gRLu"H6hz ?h9Hmax@H̒0ŰURN"%3c0e;qe1f/3,X0r?Vt-S~PBYs+ K͟4GD~T9itYaQB '[D?(5+*@eI"G ?ј({Rn5A):uL C[)K]5i9uԀI~Ij7Z4E<'i ; |4YIJ%cF%f;_Ҁa@j]ǰ(UBu}MzZ:%=Y@C.qY p<)*28 vZ@*er%xR PhnjKκ?t4̉tQ_\OP݅z֚\z⻄+O&YVYszo<0ȈRCXhY()NRK/S0-,e,$˿\jYK*ȅ>i] ,J)J OdH(}F?M xA I)8SzP&LU!%vWmvf%ET=0n$ݘ2zoW@Dv&7/welm`]F05Q?e*ٖz@t"n 1\2`El5d❰2iv:*AHhL D.2&ac1 P\SRZA8EmNIm)'Jĥw.f]f=23bp|0Y/ .Ibi]t0.t`a?̨8PVcl5fUC-q=5LQU)h ФVʴTU8Q0DdMy@QE K/&K r?#s3*>iJ@3? k(TPՃ2'CRʎҦ'A晐:IF('#(5 3> zooM޾/1* 7ͥK/fKi t( t|Ig.&D;eH)irsx\0BFrX+annE^n 1`-f&~ Ϙ)6͞sJs2YUdUí,ZɩvK:"ԌOF %1c.[Dx19pM8v8f< +E )_eD1lra_LTdSd^+Bnrg;69tUAw(e*N(&Q4L%. 3Xp (`K"ς.KbZ[thJ@&- kj}\:@ N1.i&d\ wdUU&-%VfPi KJz >r3Z'A(u.X` iDQC꩔kMΖJ3z?d@PrdaQER}x +|"F4 ϦĂs46c0‚I;a >P*el3Hg )Inj@MŗDRV\q%*e-XJn3&[Q%1`0̂a F$==2cx^؍tp&)è1 MjLyEM9GjEGZZgsװa#"2S5>h e[+,b0- @LHH=`#Y- sK x ?hgܮ,rªn1w"E Ekʦ" Bנּc#& iDqH1 B#`/a0k$ mA IMmJZ厃q\;jR&id6`떭<Bvٍ֭[ʅO{Շ=Aonv[蟦 Usiqhfv5`&w8"a䚹H$`:զʦhEp4l6V\m`o۾]MrмHgjӧohcDoQ6~$0Q"\G}qDmeW,VI+ڌ`Xv3j"bi^a 2l=py )Z{DzeM-ncㆍ͛7#55N6ՠSNbaڥ ~7+R9lCK`PXXcǎ_Ias@&9A.nfOhҸ OΗn݊IIϿ_I }y{ ظaz!{l8͛Gs?/SPPʕ+5#))\A~ nj+,,DϞ=ݻ#33S/JhvӼ3`ԨQH_F  R=gԺ9"dI+6 l1,s=#51RBBϙmB/D^zG?C/7| -Z@JJ > 3g*U< ++K''%%Q5/''UTAN/Ԓe˖#!!A⛒/c)zLKOÔ)S0yd[*֬YC-[+_{KjN-IX !˝ b4 [{ GEg=OO+ X#"Æի1|pգhԨmK,AAA֯_?w㫯VK~VZx*ޗ_.AII1[L&_WgrIW(..ҥK~3f~=k׆1oQ+)瘶>(q_Ti | C3:ɈQ GN&۝:uBѱS'Pg£bڵׯ7hx ȉG{ OF=`L2%7~ٳ={Ç1G |69=z`ٲe=z&_f͚~}4رcܹS'ɓKm"[y1H:hn1"Y`0LpqۮRt\8|0uY ڦ~JS/5>kklhK%UPsmu ajձѰaCv]Zjaƌ w9jzZzmӵn&Yc̙>|՘;Sᣏ>2#i6mN,[˖-EFFL"N\r+VҥKeRF)>X>ԩ?/Bǎw :9my}-EqF{!pp^/.'k,~h!G8))):t;L¤Ig˗N—_.E>}Oڵ-Z,+/ʖW+]vR |AꫦZt2 2zBBBЧOK~Y A~z 2ƍFzz#޽@>H]. 7ba4h06l؀k},ի2e*IGAAj}lh8ԪU <0Ò%Ik,tBtY~^z } VڙA~dYݻwcܸݻF]v0U4om|cVj+::9\_+0fX6P8řY IDAT0h` : 7u`VcVcZ*V+*WΝ;@Cs1FL[z.8$7Hn7%p" h%77˗@ӦMiGÆ lR4o v/V\C=(eYɓhܲVC$;MEzf'NĢE cEٳgcʔX?6l>SY\\'N`8q">xNDs|o+V,GÆ h"Uߕfн{wݻ'Okjժo ^=o ժUp7aŊ1bErx^hq+q?ѣGc1d`|l6vr%hRȍ_4ԁq\qhEC˸]bXx{Q,Xj#Z!ƌ:Sضm6nO}Vrb֖'TJJW4 P|#q#++=LPf>šo>طo?@8ܞ=z 6tӥnFY_^ʕ ^U) K-+ړٴGIzT*A^k!>.fRRլ,t>p6^4^ CP}ؽ{7ewh]Ki"ѶQRb" TۣM6 ^Oۦu;vǐ;ڵ'9J~>ڶ}=:]OQ=_1i2xfϞ\:d%Жpx "amoSf?֬ƺ?)!99\A~JJrrqRa}ݮw)vF.^yȷG6| G1xљT\ZZ;ӈ!Ch"{A޽{ټy8s,JJJ$fj(KkǍPQꋆqE/ZXժh'!Ƭ:8t;~h… X~曘1}:UGç^[r}7Dx^.jqXY@hDfDrr2r򐒒\I/8>s6ӥ*WD-EXn~a<`jժ8} nᆠ̱#--M-Pv7oo 7 nVI9FՂR[k֪'zO'qFxm|z>q\n7l65)OIIgE||.ߩ$ml-[Gq%dfe… WdJJJlTVMRZ;٤iSlٺ{Mo5cǎE۶~GUo=(7>(]OJ6~SZqc R_c:"vb̨#\?k}}ePu<^[m١Sg!.vl߆{6Bh6ڢ6|A f X+7l| JJJ׫ѢŭUV%_KK/excuXlt:qa˂ٳqi{5jm-AÇa͚oAQQWcذVz)xlPF\J=b"זFʨ]6 ʕ*|ߥ]z%._cGA GK.E˅#GG^r7\7`0 ЊƩ٭-rpxlS0Kh Rea'C\ZwߡAuxw `0 C+qqqσK%K'[[b0 `0'4Qn }ߋP`0 `0?x %̗Ӎ4LWnנ`0 \.N+;iр 0π7]\X`0 <b@ŮJ4!~틀 S+a=f+a `0 C7. ?-5/_o)NJbD?2 `0 Ƶ BF$it!w#2 `0 E_^^M '!lFp& ].ʅ]`0 Lh!^ٍ[UX3iZ7B7/twMoK16//3;umv`0lLdu x ]0/ nᯈc§>s:t(dA߃>:ׇE)?iKapq.|Z Qh+%2 ,@ RɎ]Ol܈Y3gȑHLLDF۶xW:|ɗhߡDؿN9>jpwW+0g7<`nNg6TR۴<aaXtn|7xqAz^^&>(v܅Z݆?ʕ+G/~]խ~ nrun= %"ymÌqL"YrcQy?̷g`nku;&7oٳg0jxC2C,%$H"!@||few>|+a\ dލ_\nDuI ::ɾ}Ab2}ϢlZPf-$UO 7{z^$]km_Q}6<ԓ0~֭Ml6,^/?111eU"?c/>61LlN9Ʒ?Fc8rn;'N0ҍ?md`h9Sg7ھpYu`!Rcby"RuPn0=z]}.t3F?V{/  ͆g]TY Uknkւ~Zf?eecڅa1h݋g>sgvι⬿ƒHMYf~fbNE Ը ROsGիIق9ۍ뮻? QZu8Nղ*jpw߾/(^!-oi?,+wE <M6pzfXpuS'n}}zs:x7mBAݛɖt#i?{ϞT2v ׇggJu`DHz&RuPn}Lxqx`$*UJ*Faر{T͛6gs?]O0J`Qj5aUX8o }> 5(GaMxK1|AXŸQm.&v]gpGÆ&bTqEWxI\?eu[bX07׭\zGP^}]P0c 0[nENN2wډ#DbJ:lM0zX$VFm ɂծMq& 11c>6hбS'~q挱 p 66L\ F$1{L4\/M"q]HzZj!WtZ0>ޝ5 /_6% p!&&+%"92R*#%) )­RJcx+ _ȐQNlk cpq *W A.}篂+`/\%HK,.[GԾ2qxFII ޝ71Tlzwʕ<.3lBii.,eT.=,RKq-r%HMQdE20 3 x=&{}ss=s ڵʽweZ_bB66@{AxlmW%..[Ν888hOJJ ڵhjFllL9ՓFW UT4?~L*ULZN_;{Ԍ'oPQo癦БݧC"!! 7$ L_f2YK0HSZ=a/Whə"iZKɉS$UBXVc%S՛CBbĝ܊@8>$=πv[M*Цm[l=k*Jڶkg6T9mߟ6n#y#eO?ioddff<孭I|zp{p?y|̢l3:jwttJX6koG1@N 0bȭN:}ߙIgϷ 1Dc6S*ڱs|w}y  D$>t< a{`|ܗtiZEzj쬚scz5}S|sm_{vVvl(-, 'k'xn%0l?~^`Qk?V UVպu'V@+˟=9IӦl޴ Z-[t<ˋk֐ª|L]Yt ?o˜?NԹ3+W-<;99AFF}^]WW32^;{{"""Knݺo^طo/Ż/r0LW Bi0Vh`BE0v8֬^5kHNNB̆X*1cuUÆחbBc/ +noמՇl(µ 'CIGw/pvvf,?.HMMم>> V`búVI¯0r+nnܰwWw㧍hߡMS&0mLJ%I2sƌU, B;/IѴTLbw -K*UЪukl%YI P5fW=>֮[P_8w"_LϠAXYY3o$Iv]g >5#GnӚ%˾/q.g}ΊjsA(-[ ʣzafϝmܴ_rXZZ1zG^7W #_{Jײ$Z׮>FG]g e_V燐S]jzAAT2228ݼ֤$c-T^IʾAccջ/MR+۳8gng +tWu/W  FʾQ{9W7jtlHyAAA}t/IY#&Τ  PJ~.#AAA(E" AAA( 3Fe1ʚAAAxY{7?|Z! X%C2U2.#FPre툈Yo5~=zy3X8G֤ˬ }'BQ5VSu'^55Y<#3 i;zO>O1v\ gø3/,'Yk n͏177/r]>[p0꺺翿~>oe-ŪK7o,t~<.?4S&^}ʕy{>4hȀ+Ƴg&^s}}٫CC9w7o &<Y}kgeGP@GE!W)KrY HR8  rjX(ޕ˗3It8::2XfM233]6yvTA}7x7Mp`޳~:zrVJ㫯֮G}k б}wڵfUrLzf`^g IDAT Пһ/}Ō=_?,-hYq'ge*:濲A SnܸWɗӦu(FT0c DKj,=t#wK24$c-%2mo<]NDMU/' [ȑx, Y oMƾ:Wΰ0vVe}6('O-\_W`XΛ;7z؉?|ۗC{Wׯ_wF.JxXΜcޜ3g6*gCiݦ ZMVV.]&d lI Eo5"&SMjfu-v'{[27G@^xuyY ym*ubRŮ/6&[[[.kyF: wzzmCE`ohYQZZ2p`͙7'Oo^@fMω.@JKK lj  DiiЀN>`JDFFC||T^[<ݺQՕaCW߃׻R/~)o<2va:}]"#0z0fHDff&Ғ=zuO;>r'gȑ(J># Mwrr"ѣ<#-ͅhJ8:b1$̐r g׳ܥj|| ,ZqUm=s-:QKX3L [27e#*x@AY?U2>*H#oy1 1C}71!k {_"xo_VZM ܽGF[N66$%=3>>[yUmmSI_WzmO^FO'}zG}껹(b3q!OV(deei'*DKO.k\Ռc2߿}i,}0d J+LA2D$aR(msE~MtErJ-<9[$Mk)9|JQêPvcPdzsHH윸[(R62[~ۇ͌ykrwڹi;[;fϚ 1C}ښħ{=  L>'&$`mmnooϣg;`JzqE}ݷ4x0. ҫ\A~ݶg:xuԻ l'=arl((b3q Eɺ8::r%,\e HɘڶkM:6<0I>~>CHN6c8 ޵K4-zj쬚scz5}S|sm_{vVvl(-, 'k'xn%#--1l?~^`qzG|X(rӧ}4mMPlݲEg24d@_Yx1-[.#I?m܈:9Ѽ/ {yvRRRXddd8/xvyIMIsC_ ~f%u c>X2VF]WW32^;{rDž'֩sgW$55[63xF0cǍcլ]$lXի3Vw;lp||}@JJJ+cOr7k?YB?=˥:gQ%S՛k  O&^̶;X0]:z }|62={ņu뭒_ac?ON9cFb`aaaFɗ-X șGn$m[Ύ5N;})|$&%q|wڅFaguv8̞KI?3&M7JKK\\\ m#}߯9BVRV[e/E.g Bi3FŴi 5,@$ލk׍i<}}6q-TͨYfϛktW6mf9,?~NMZ%G~ĊUXXXFB9'_{JײnX fddpu6ѹn,+.ҭW e_V燐S]jz^ 4jI&c/[^: 39}Ϲ$I+ccyEb߮s;Grdݙ3aSwUrU   \ T&  P݋g$`bRDAA Tw_]3a   J IL   BigAAAJQ0$#A$BX.AAc*I~QVyL=vM{sכopbVtbn51Wi=5IIeVF!5=UF btb? <#Dy%Y?7 3ɓ>oMܾ',hJ_Hە/S˹ffY;Z60/7~Ʌ;Ȑ2P&, \M րRTT&*w"ۊ"c.Q*iۖ3fRfM2.k~Ot)_g޻j?Ӿ=T9?GGG3ol8q8*WgL0.^3/JUd3=]j.gkkK֭t<4S7t2I]\Y)6(Ο;p<[ɓiҤvݻ Y(C%ͅdj4QW O1v\ gø3/,'Yk n͏177/r]>[p0꺺翿~>oe-ŪK7o,tH腋 `ŪWr%yFD;޷ 2~FQ*Shz >Ջӡ Ż7~C^D9_Ϝ!2Uݕ?% fm\2ˬ^;+;8z( L\R@:P-]SòF\M{7077ё }ƪ5k,Iڵ3#Gppp$==uUo5ܸݼlڄCMͽ pիWG.SjU_}v8[ծŪ3,,ݺkY_? ɓ'@uA W)־卞=9v._s!夦Ʃ3gx7g̙JPZiüs077GVŅK ٽ>4qҤc@홨Ǐ~+Jf}=ysfɂys=g.Jr:hNg7FAL'Μ>)a5ϧL5F@PDxXGg׳Y8v>r_͜A}ΞJ3N:E>Mv_No5"&SMjfu-v'{[27G@^xuyY ym*ubRŮ/6&[[[.kyF: wzzmCE`ohYQZZ2p`͙7'Oo^@fMω.@JKKCQZZ24 ӧ$_*Us_g&_Nݭu]]6ԟz=xk++ޯ666z[\0va:}]"#0z0fHDff&Ғ=zuO;>r'gȑ(J># Mwrr"ѣ<#5Yfߐ+W,'==\N_ktoE+m:G~Cs]'*1{)早!pkQl2R%h4(8k\<'겳^g0r^ p}-?fQ7|8#"0~ȡo&&$`mcdK]d2oU =yϩƆgvck=-qqqy988#p|}=g. 63(G__;wn}(1ēI^=iQn&}\ YYYEɺ%%%SKZ4W5#66F ekkGtttboog&+CG~w> < S$);q7ҙ08ȏW+َWhə"iZKɉS$UBXVc%S՛CBbĝ܊@8>^^5 ;ڴmgBYEIv چ!kmmMӃӃggg ygNӳf0%=ðz6t7o1h`]WmtXe Q* ccGb>ʥa&Lq\(HQ}.\ ~zR2ڱs|w}y  D$>t< %-J7Qn/G`ϦMظq_?޸Y=v5vV͹r1=ݚž)K¶vҽ Wo;+;6@̃Xlلt<[laős6ԟS?/Lٸa=#>,f{ӾM6eMjn٢32h /]ʃ,]Zi$6nDhXf ))) wɉ222 ?Y^ !##9據ktt4˖.[*chPQOtus+X}Dž'֩sgW$55[63xF0cǍcլ]$lXի3Vw;lp||}@JJJ+Pu]](k?YB?=˥:gQ%S՛k  O&^̶;X0]:z }|62={ņu뭒_ac?ON9cFb`aaaFɗ-X șGn$m[Ύ5N;})|$&%qB˘wڅFaguv8̞KI?3&M7JKK\\\ m#}߯9BVRV[e/Em+nyA'~1m>aeeͼ $7w#u#Gk>.rM@ U3j|J zafϝmܴ_rXZZ1zG^Ei,sR4-kS7+V\vK=Dx_tBwٗTn^A0ᡧ  82228o7 _k$gWL$es.Ifff>yX\tqU!;W]\̑;s0M m4hd1zAARr{O   By|G-Je4⬖ &&.EA ٹahϡQo9sAAA1D}*#L?N8TO*(R%F1U   4 g^TQQܹsUTkkHIyL\\bHْAnȫ rX8͝_~g9\κ[[0k~\yLi~е\D]˛o݉PNm>Auݼy?,[Fm8y4mڴeA(L'' +гW/Nr:4n Lxxx|>sH^6N9JXO˖Ċ~`jz~XZZaiitw &NUxuP:J[vSٙ x?Ɔڵ딸Mtt,vvsZNr~ιP&ammEttlכ͓Amq-Q8yΑе{-e˖ib|~{BvϬM4ލ^XfM")vlPQO9}S>KW9jOjڍ(!cB ))0;άgp||]ZQ徚9=Jg:u}zu`[9L&W4@Х IDAT-kX:^{_?y.Ե~]r_{t&6&Ej늱)JZZ 55X3va:}]"#_/<$iB%{ *v~aZaN?Έ#Q*|!G6n#"#D£GyG[&: ]Xw}}]jqTQ>3L8#gLFRRժ9z[6bqQqH 4qJ5.l^magg p}-?fQ7|8#Ws?з}$ry#rspx^6{Umm+VIIIT={y{=b̽={ˈJ*Ǐ\E`:1fx?ӫ'P.] 3QgBAtqttJXoOSvعc{CvѾC<ӆ"I|:gIPA-[Xd1qqqq9 g ,8C6nXO^_Ec>ۤiS6oڄZf-ܙ^^]V9VVNNfϞѸIlDrrkb|ddd8߻[wKrrۻxS X}u]]I x 퉈0~&`rԹ3+W-<}UƱfj֮YCrrd6_U{DaÇRRRt.'XI C O$az:t.664lAh|rǕ;qv:5ƺեk  wygggmKы 0iD:uQ ,ӳ[hђ2h1Cns:mCƯaaaQ02iΜ9MKO.Ť\76i2OWv4lRmҔlټ:вU2yk׭g[(pGFriڴl_/G/市1/Mw zIVKcO|˗/BՌdxǦKzذi3V͛Ӳ'6S׵KNx=#%ႠR4~_~fddp }{^+tm e_Gcffg3亷#++L233INNp$IcEi0UPQ=|.:וe  ddd.<6B&;dիWGa&GОTȹ&'OK7zy^qva6κ{ȡ{!I^^v:G3vvv|=xy77Ws/~4UP ]vXf*UY$ B"/z"H F&ONܽ{7oi\hmmMժ6xyse] T fiZ,&CAA'c<'t4?cZ( 666&ILU w]^.^a Pd]BgAAAҖ;/) +u^A4>CNTVA*Yq  25A (gIPhܹGժTdYYY<&..(y5 AAA*8ŋW\]qrrBBJkcxJYkT"YĔIJEAA*I&Y(A2h .ki_ӿ>sv$i4jTʕ+!IeffFJT+Wťii};Ҩ1[1׎󫴞њԤI(&FCjz^|x PqAx㎰_ +1Cu,P ~% ju j<{h4ڤLhի88أTV)b /t+_ s=̲xw88`Yѣ=JYr:— dHQdLZ&Fk@) iaV;mEY% TBi9 4oђ&OI&e\j""nrwF珯~CK3dx6|^]w +ٷ, TZDurv$5k:QJd22 333̴.oFFrkkrq6lپe_O|g͸FcHdT+[.H òen݆gNӦM[}Y˜w)))tUc8Uw5PSY#82ӟ3:<A0p\( W\ׇzq:4ӡxwo`Ë(3DVfJ[n=',),DƌE П{VNr/\Mr0'O@R2<0Z ~D~͛ {Qﮤ$Q,o0kzͫuj#\h8q];qӤQCvBBդ1!;wufٴ BBt٫Vx :d* 豣( 2rI"K@ BwYN :W|3qyUؾ=_o4OiQqbw`sg$&}K,f1aiiCX'8a<+WձS*@JpѹKF3giب3M3Zѱj9;92ELѱFV f7wÃhcWǜnnI\D}DM:hب_/ҠaC9}S>KW9jO B %%W13ӧqu;άsX8u޽I5ky[tdPAܺ~ 9rOe!KAoɓݳKqfK#DŒ匯ϫ[ڡqpFؐl9gr$1羰M&+j7BNU*chey1jR3km;3uݚ]gWo]za:}]"#?LKDff&~JKKG=v(g6Ұ~6%m 'kX:^ < S@&aRn +ɦNĆPlw0Byaφy2IHLL>_+qo4D)*ErB1sO0&MyܹCM;z>P.] 38ވȻyF"SFEu...]IH윸[(R62[~ۧ}P`q+P1P{{{(>>_#WµDžk4ZLEiۮ;wl7}w.wsx` $ɧ?}|,SbX-g_͛iӍV37oFV䙞sYg$I4cqqq6Zzj쬚scz5}S|sm_{K$^kiذrMW77꺺AQؽug߾$''o^Ίʎ#7 %}6dWb T8.k׬!%%U]:uLʕuf|ߨqfqYk֐:9 ױzU0c~ܰex@))):=M²(o#6z5~~} ?Dj55 zz.߼NȮ]NF 6ZTh7<[蝀ͱk/Ub4|wϞl ??^əӧiӲ]ȇ}T:+-X0d>ˇ/Au+PqPIpiZzt/&=8._D U3V.cæ?Z5oN~6nL] i/^OR4~5:dddp?n?"o;kWV_"/Fѣ133ӳUrݗEff&$''sj8$ѱc<#(燐S]ju(  P,B=d2z(Hړ%9ccF{K]vm77WsHr8$Ixyڵ9;..iC>|Hd]\WϽ\&`U:AAA 2&ONܽ{7oihmmMժ6xyƦ#AANJ:޽!clllD%  / yNXia͂   Bh/GyjJ?CfAAA0\JPV?C<'LyFY,R  PIXO#   " Q1mن~Cv:FqcTRT*~AxF Eyw!7UTg8v( ceKˍ_r2 ԨI2KBmF-W#ł5@4հIJC/KZDD-uWX*~A0DYۦ& 3x7m.effB#o{_=}۱+$wƆz Xw)))tUc8Uw5PSY#82ӟ3:"FRug9Bwջ ׏/BPQMm`Z7o,o˿SgC"WYIƀd*΍hjF7zύ%O(;+;8z( L\R@:P-]SòF޺y;wIl<ݴqccc<8A騚4f4W5óYSvhU.--f_cpwGn٣;/^,0t4jȮZT4&dNnٜO&yɽnnx6m¡ԛ{9]L }{V7YG>0Awtt4G}ǽwUN~dx={}*6r1UljJ}7W^܉cG7mP)=}&=\j SΝhZ# g>3="> Rv4 C<]_}s\sjc܁LXXG[̜> %ɠ6ur((00C,x>8[ծŪs׳ݻ'NsiAj {xpT* @PDǬgkVZMVV.].0 pO>SB %%W13ӧqxfOn卞=9v._T6z)öҳGw>̢nFڵo{ޜ٨T*N u6̛;Xt'v۳nF#gK}Ә\Q|TnD  ʢ7%C'd lI ӵnjYкt}fҥ>c|N1./_{JrVzɍnNܾY e3 {jݺ$%%ѶU+.T`\W&(RD?&,.JVc TTMsau&]:uv<&0<|{<.rgP@r(WITL4-W4rE[ᒚK.-▖z++MM&uCPAvf ì kb99ssoOx/PߏhV?owkؔ]rb}o/Y&}|HHHͭ<)׬Fjo=ݼۿʕڵKe֎ӽ:qO$(_9'ww/l.mس=ۛ-15Ʀ;XOEzto=#{S`}3w[RmQjU.* P*( \\\K빖HHN_QS/-_~?jeKl\v?0U+v#5k WQ\3è h5~zfQ^ZJ#m{Y`Ϯ{{{O*.]"۷nQR%랿|//B_xVX 9'Ld4iHСԫW}d*yydV ̙,_a܄ IQsNxY;w yߑm);XOE18{O1vEO[@Wx-4lAղOdyRD-p!.uҋ0h4hZ S]LܥyS\N{{{suo)OOORnescn3j$fVr21+qu~g̨Qs MBŞsׇ3hp$ɲs=Gdm{{[-LJN'{S`)ea̓#8j_~atYɧ4fݰ$HDΆ~Y|5nIMղjŊۂ۷gҥdddiˬ&%%}G!84aiFbqpqq{c00EfkӮV$==˖Ѧm[ҙE ;y{2; !JD?#;'eKC\\+<.3jOjՈ#''д;XOE1J*{7i,YJUpSʳ|\l̖ҬU:s/6op~-\b^!(UyL jۖ~IQ8qMdrƌk^V2")~1s/=Ŗ1y [6om4l$nnny+Yp!!|yYNf?_&yqbf:?B<1'9ma}SO?Mֹk88.yߙLg5 3O(4S4iILD6yaC4M/L#ާ9KshVYW,n9.gmaQܼqB IDATAήB!DU^3-Q[XY+x,-_njaKYg3aF/!04r0w"-5%114 ,>B!D9S3_psKΫS 7ӧ1y! !B'==b0`{Lf%p!!rB!D1=Sb<fk.2B!s„B!Bªe E|/B!%KԗLZEYXB!BE,aMx7Mm} FCV>c&kۧNYA?r;rqƜXVͼME //o(t: j N91gWٹ@֭[|1ftvUʵq/#==A6 $vժV| P~-$tzj5|<19z'bO=0}T*V C!kX\]]Qp}{ҫgO<_zeCI͉c?DˋJGqa@+ 'La>V,_˽zQjU),MtD4?jRJBl HJbuu{b] ܥ+2n\Y`?NGZر}{ۻʕ}ζ_cK1G@nn.۶nnZ7ƍ ;;ƍW4lJM]v>{ѰA}zW._6+]bb"aa<^/>{mر\ CCپm69lG>b5G3}G=/+WS& +4襤$"A?ݺtرck>۷ɓ \84n_}% >6?geeѴѓ|bٳt,͚4~ɖ9q̝ `KOO-[4x]iߌ-rTPո丠LUB2p`d<[YyұS' ?Ezx|om$Go"Kgz:ZiZFr*6~Ysy]9x0ϴlɜٳJoЀC%00mڰz:/f=v—_|nB)ord,ÆgJd6Fgdm{;x6jĉ;h>z1oQk>ϝ+w#dee{+iiir 8̙=WN8ncK=.*,Q^qϏ?3O쪈;":F#%ҩPf(QVMJ&B0ێ7;Ą~SfW~~;t+yݿW##Ѹ30"~7+_~aah4Ga=ի^z̏&z"7nBw!!‹=tkog`m{̱o^DХkWnQlէ("|@<<B͞sׇ3hp$X8bw&k@vϷ0V @|||mJN'eӓ۷Ѯrʸ5 3,AxȂ7놭%F"7w6[O5j3lڸ>IIIdee# 8I&r֭]C!6̳8н} hҵi׎U+WehӶY۷gҥdddi+oۢh `ێ<߽Je>/(!yϏ-YqqqPGd=ժU#..BvҘIIIawѹcYZQl\T-V(J*{7i,YJ*"иI6_OZZ6n,=+_8)1s1_Fҩ_ 2Wc hgUk拯6sq:3qxw`ᅦ }yZx瞷 {ý:f;qRqrh 'EqqZ6e3vl޶/6oCHBQ8{̉oNFxX_Oӿu6oDž1;Zx2")~1s/0+_5Զ-O=tm&NbJ$ڵi hg8DM–͛i۪% =Y9E?w2PZuпiUgZsgѳ׿po|Z8B!p7oܠCp뤳P999v3~(J߹UvQP/T(ܗVs-1,~Mz ؼi#~~MU,L#9h<f:v˚KK!B!ʸٽ{i,i`TBl-J61InY&B!(u ci$$$dF_`޽9A5R:O!B!BB~fhB!B \hժ(r*M!Ba P($ B!BǺ化b;^!B!JZg 6%|ɓDMșӧ WsDÆ%^ad0ɴ(MuBCDQ^cUf?n,#"x{k&N;]rk꺩xEiuђF*E"x( *Ѿj0:-)6}{=>} FCV>c&kۧNYA?E.Y@ȑ e3:jo,r\p~a}Y~C^0U+VpY9=aX$Ξ^xz^~g82q/#==A6 $vժV| P~-$tzj5|<19z'bO=0}T*V C!7V\ vkkiq׾{ճZ`/sZBLΟ?__%棏8p0@bmdŝevy/?tz Zvv5oo#PԨjT \5d@UC ݫҘOH.]quuחq&|t:jժŎ ~޽TKvv[9jrssٶu+χvc1n6n毾y`S5m7_ڵk݋ rY  z݋mcƎeOm͵G͉Og;Rߏ91;yGw^BZ5Y0o^y'%%1? ֥3ǎ+6_Sؾ=M4ނSVͼ.?OH`7|yN/YYY4m$q8w,];=K&,iߝ `% g`<_yiSgkߌ-rTPո丠LUB2p`d<[YyұS' ?Ezx|om$Go"Kgz:ZiZFr*6~Ysy]9x0ϴlɜٳJoЀC%00mڰz:/f=v—_|nB)ord,ÆgJd6Fgdm{;x6jĉ;h>z1oQk>ϝ+w#dee{o+iiir 8̙=WN8ncK-sb %/M}s 'Nߧ4y2&١!V#xҒPT(3n&x%{\!mǛ_bB^^^y?שU+??(m[Rٷ2Ӹ20gl,\Ă W`* t: ݝ]pꕼϫhn/0t04 #wϞU^=GGpsޛmi7 ! {ω~C\]]yŞ\tс{3}G=طw/Ghҵk >t EJp*TÇѿ4fv?8=Vq y0b /2?0/ؓcG!WaI(23 hR46aC,;<?*L䐡,^LF/`ljKYfq?a"cnjf¸q>}}'x/LNN˸IIIfj<,:j<) yi&G1n$(_9'ox. SA5?{8gw$kc[XR۬hZ*U2~p]G̮#@ʕ S8ܾgŊW`wӗ{>[6o&33oނ$P`-\C$bGmE`,ж];>ߴ5l o/oޝ5 Úr}=ǡoX;v&(81F28oVZn%'p+9W쩼%`p`pƌEPp;w@߰RԱ8 C9~2A5?{)1P~-FA_`RDp٢q$mc>EKN_Vlh֮YMFOOS 9=Y7l- 7ѸzyV8oeƍIJJ"++?aL<4Cn"eŅ=;gEGMvZt/[FmJܾ=˖.%##M7_yEG3``v=P*IBd9ߟaْ%xg ;= EqļLSZ5)4m,~;55nIMղjŊ۪TwҘ%TE?84n҄ דƦK _Xފ8&\+_5jԘm;v:„~-\r :YGڵ̛;AȠv:GĠA }y֮^CsR=^BBRIQ:O5 i` ov ")5j<»s͛BcN|s4틇'sCPЭs'bO8⸐#}g2՞U00)Z-G/mԔ)9M[.s/0+_5Ç|2ޞ?]mIL~´3h4 B(S5y #˘[V07Ze8eqΟDV[V!BQ7n!8uUqv~T*ٽ{'UVEB0^P(pqq/^Zb"ڵQ7~ 竍x1&*C5Y.Qw=B!9ݻwʒ:J%KsX;)erD!B!Qa̜>lԈ ̻w[8 0.yoaqU0!B!, V% B!Bƻd]g!B!(A*4B!BqE˖%-^hy8Q!Ba [W_%\U^e̕˗7 =SO?Kj[̜LTPWٙBCD!JC_~VԙNsveqa[?0, 2}{~<ƽ0t(TZ O_O1@ B@.׮u6jZ-bc_λ̟;7/%00L˖̙=۬toϜA 8Q lkݦ ׮#zbCh.|v=!ăsӈ&O2lpDMvl#ld>pƼHֶogOXh3?XޜUls2 +{9BVVVFnn.GLÜٳĉ;Vt'yYz3f;wҺ3VAV!Wa<iit*JTUp Ķ/1!//ԪY?~_} [[zi\bq3{6 .b…ԫW? :׆GN.]zJ_H4 J_:lCػg}W󣣉^9Ͷ儽]?CHbO.]ۇ=#Xsۻ#h4tڕwoq)C^<[4 k{NN B q8|@NafcmDS3au{؉hӮ>+;J~H$ ԙj I4)j갡gwz}&rP/ZDff& 0dk6,80cF3a8N>]>ހ&''eVˋ$iZjHZ5iؔĄc:# 9bw&SYz rFM#hؔu2w򵪞Q9|˗/YUy&LĔIL1F`(425Q0bp>YyѸOISA^._bEqGRw)z:`ți=g@qz'-.qKB!vt :߿,^믨]1<*V$./]R@TP(p'2 hǨZ>^'bg=VY$jjͻg +Ƈ,B!ܻ7[fIL M]c0uzR*lcAJE~Ԭ]V.cwæ|a5iB!ҧo3O#!!'5b~0cר].hKzLfcVa@ ̆*@B!(,_A?INNd3#Βbr&K!B!wɺB!B֭[]/GrFk/B!B{0px<44iؾeZ}0 B!B%%4oќ?Ê: k 2s2-JSA]T6i PjK¡? 66C!$y晖|k#&NrB-˯rQr9EKKi4 h**T̪D,봤ؼK)4 -[bTQ#o:jfgߗrd"GB5ΘԪwqe %VN#z|VX3g7զp9*yyܾgNEpܶgxx;Qe;z~sA]vhĸƑNP P;jU >a\gl=oQl`ь=>ujT*?]Uhr'ڭ^WϞhyzٷi !ʆK3:|>2*yF.]|^ϵ˗ԭ+j5W㭋_(_,r˗^:kŋ%U-q7OUjJ5* ut H*Y!ՋwiL 'Ls˸ Xret:jb~o^*W%;;~-5lۺCj 7l7bW_<)͚6ᛯKw5EK\|٬txEJJJ޶1cDzr't emZsIA9lG>b5G3}G=/+WS& +4襤$"A?ݺtرck>۷ɓ 㙨.?OH`7|yN/YYY4m$q8w,];=K&,Nث7tt9$%&b޴iuwτr, 777222\3˅3PBW2U u_uGl]cccةYhz=`>z|Ie#7ڥ3{aYv۴@Vj9O?¬weܹyi.C !4gNFԔ79~2aÇ3%jca#{3}G=_`<5~B~Y4xq=Ƙ7b5UYXYݻ`=JZZ=~0=sfϦkh(?'X'JHŋdee}Vf&._$)1I5#oY e_XGZ;PB^'gff۷a<iit*JTUp Ķ/1!//ԪY?~_} [[zi\bq3{6 .b…ԫW? :׆GN.]zJ_H4 J_:lCػg}W󣣉^9Ͷ儽]?CHbO.]*Wس9;1Ǿ{<8FC]ٺ}GW>t EJp*TÇѿ4fv?8=qFvfYY H T06>(WKN^r:u-oa~H$ ԙj I4)j갡gwz}&rP/ZDff& 0dk6,80cF3a8N>]>wVR.LNN˸IIIfj<,:j<) yi&G1n$(_9'ox. SA5?{8gw$kc[XR۬hZ*U2~p]G̮#@ʕ S8ܾgŊ|1΋V*Nٶm+ZmʳϚwp  …8jHJ/vߖ:sm׎7mEMF-[˛wgBV[[Ն=ǡoX;v&(81F28oVZn%'p+9W쩼%`p`pƌEPp;w@߰RԱ8 C9~2A5?{)1P~-FA=.Rwoԍ'eӓ۷ѮVB;]m _ٽ6|}x筷hؔ?<[orvfŪO[\KV׮1tZ'09%B!DY!$Sg-}1gWiƎ@vN .&Ί)o z!:1oSIOwJ!BQU{s撐'55s -XWE'w)h%/B!61Pϯ.8s,>s3ͩԩYT֪%/[0!B!VRy%<m݊۷ZFlME!B!(_'b@tJjUl#YΆks܍-ݷ0j.\dE0 ddZpϗhyW#: ߙ.9͕ób%ɰ敛Ŧ+z:`{(^x:vFܙ*<334 tV!B!(?T*  JÎԨQ Ywm s( z=^nnR!B!D9医rzYfP*+B!B\= ʵ )T~ci(JrzZX!B!ʞ\}.J,AB>WAV1B!BzB†m'LT9bL!B!(az.YGgz!B!x(-Qoۙ0 B!B(:uwVGL!BQu:6PqDo)S(P( L΄ !B!}n. Ba%-`@P L!BQuƇ5[gd{%4*J:uB!͵rǏ1 s0!B!DZ#Nn[)0 B!3aaaܹZJ^oKB!BQf\x91t36X~:MP0B!B\}^,dg;0 B!B#zg Ѻ{ <#L!BQn rD!B!cB!B/-67 9Y!B!x)B!) r*L!B!w垰\\]N=B!P(]\,^JKB!cRy`CF:5j>1;0W*_|V!etYT}n(%*_|j1(6tvǸumB+>ƹ3\\L/%!b1HjHMnݼAvBWvv6I7QZmqI!XzL2Ι0hC޾WF !ee))Z<<ޟ̞;bz6lDyo*νe+#B8; !GP왿p!-7_X_<==ȟg> h2ͤ$[V._@xUʶM?sxB!J8!, gr2oM+#8]>3Xqׇ͈֩[2cO`Pq& {/@޽,*+--)SY`j:/MVV6'E[3;~"vgiۦ5oNq_ _§R1 ڴn @[|>7~~7߸7V*?@ᆵc 6w8[}wNNCn]ͷ[‘$ C蹿ˉb5\oe" IFŖj%u$իWH?. QݹQVeOm8+/_)Y m؀?K?'6S|;뭼 +Kмy3|k-7ӓ w",^īٰsBV*%>`1`Myy~z->ZKbg-_#Ff z^ж̜>7|>c@x?:=۱U* \|B؟J*E_V}~a}tr{wXaVk}Fڏz(O >`nyɫ_CBxk; :OVBs_<߭+UpkΜ=Ov;.ue1@Vf&vn]Q`P7jތ7nY^?C*y3_{N8rh޶J+rAhӪs}d$t:BBpU>*/9.ɫw d7&MPTkӚfq1֕ nn<ݢy2>.K^ee퓗%JPжM%,z9mj*x&[2>ZCTܵO6'/PTuww\\\ 웞IƱo2u:^^^D  fRY˔IP*T+zB(bX֭_ϕdee]FjZMy$Dq矖헓>dP*yy(T(] w*'̝C_ y(Uɷo}_m9. Q=a̚l|-oFsm6o 'ٹ{غm;O6lXh^^\KH@Y]V:uh-G/nԪUӬvWv~M4fmdeeg>f^޶7K*U-4u4_mBQ=ԨQkn6<==PlMw[IKO0nbT6{(_q]΄ 0i2ii<<ռ9_O?fȰcİ +jfϙGzF˗|lUYٰ4ŕ_&fr Ne"__/̪k|}+3dg>V^fLA&BQ=;fh䫠P01j2kV4YFXX :ժU!$Dyꀡ_R/ZPq>vkeo`[ήUk4| gWG!Bb*òvZ/_jOA=|P!D)f5 !}|ݛA" fb%$1ckV"J5j$V.UKQ#-WkUhTCؑ!+7<~{?s?sB!LSIߑK; IDATy!B!(J,dB!Bf2YB!Bv<&B!H2!KB!JI !B!D1FB!B ;L)4zI!Di9B!i/B!JBfr'L!B!Qz#L~2B!B"ȲFT2BRD%!tGB!Bb!B!"oNB!B#E +褘1c%ЫT*+JVָ`oHQS5l@x5E/lhdLXjXRRi@l\PGG*B%%&qy<<W!^[be)RIE7}k/^AL8+` k$?H@{5{.ޟ_RRRxcy(^;JԮ (+Veo0i=z`оN``ܽK'07bܹ{ҧw/3lN7[BY?kqMVʈChV䩯3r0~\:]Ç !55 :İOHN]6[שUa}Tubcciۦuajj*S^_om'+>BOkѓl<{bb"cԶ8.e^*riii8UsҶukgbooV5zt4c34RTzoiX^ \\\;eVZ\;npF8XD5l>T\6-[BY˷de["^-s!-dgGrJ Wl"؁6l2Sb?R^ zE2r06|+\/3gLei:Owҥc>'+Y_{ }&'%q%2E0j_Dtp}*׀tFCΝ9xѵKg,,,r 5 t:V~!>{+zJr2)9^Ò XWPjrV/w]e+:HCiwk7lMV,^0˗lqqql۾Fnu?mHpb7⧍;LW݊H] }Mq.,g}}ƷΜ)RǛ~Cmrr2ڍ7~ w¥}iQb?R^JEZZ퉍 ^-[p]C=OILL ڵ㽙]Δ1l FJJjϢyfh4:oϭ۷Pٞ}szO.ѵsg8a8wz=]:* VKΝ*%IS.QF pfh;~{{{jժc|{.Nf'wwfGy%CxXy|}{7.ȵL>([%K-(pgFnnu/\"ӗO>!ղT8@k@Olش)^aKH/ h3}]ZHxJʴ?RAŊiIQцţK)ýqc*ٱnzlpog+TAB*U"66VVdG|B*`hgZ&1q=,h9fMEM˯Ǿ,`iI0@]ԩG+WQץgH\]”O)oVt:Vʕq"wg,4u5kԤFhi*Uč7.\?6Orr T-53t{gΐF  oRb"Μy1+8.ze^*L=}}m^zf5}$%'6xxsn_51}`E(~WyNSE*3jIٞ_ֵkb8t/ `X~*hy3߯leI^o:y2K?V?,--߯/Zmaݐ`2f͙C#77wY]3etƟǻ|*W.SɾMz2cf_pYț3JvD^,w@PѶM\ҶM|4y>oM ]s c xWN zq<*IܹS"A5#zJ8ƙ7:VRxrp^#GceNBd!ʷ4ߏvmgt:~ Z-'MIg$g2]KIy4sִERFy!0Jx&0OW.]>x[M62/hzs_rI_TEiCy&j45XYYQ갲?gNIIT}/Yeu;h]/ SOlGbb";=ŭ[jތM7ګ%Z`͆x׻;]ҭKgW/^7Z4g]FVj5.L:,_N@XWFё]MݻvlU·gO~۽[EtSxxzRF FcaiiɃ;wgޞ>XdU^#G1.0ߞ=@׳db.5~cV^+bccye͚5cUvIK2A FLZL5 ;J >'Ofs!~gf>W_Pt{Ľ1]m+}қVR\Z}$xkN՜8~Yq`ccc2D_oҥ jc`D]+ص!׮^e qL=nyC#死7NtNoUGqPreuMFFy&eHJJbs}3+>h|[;;&:Çt(3611`ݻwGSRR4qΥzFiƎiӸ{._~ƛI'?sƣ {>o29_~&uˣ9Es ףv-[7Q./E@la?|~{~ {Q7B9 .]Ea4oт+ܸq+WмE :YءcGTٳhK'T$ߒʢϞ5zxȔ0Z͏?@BBAkUֆwymv5iwwwnjif4nؤ5tuAÆRjU"""Jy? ~yhVɆÇ+}Z[nbrڶkgV%x 4tqnGy̟I`["nnF7h= 2qtǕJif>u*M (Āϒ ,p+&߭V\SOu*" Qf(4OXA VK h؝ڲmVޚhԶkLǎ57ǎ1n̙J^hۮ#G3޴N;ФYS G|uAd|:?cZE? ,>>ݝ8,]fV%0cGҡm;N:E8*R~gޙ1\ڽk9ÊK',\?E_cZ7kyQޯz?G6m;'4aeZpnIIT}qf%j\o_n]nE]WA> h֠Em[H[CJ(BQRSSڵꐦ_Ij$nFGiM^zZmxTZpsFXvBr-I#<&Qaq(0NJά WfNYhZ} /@Ͼ.E1B!JFI((ɣNNNL1]{֬8J|VAS ([m}d gFɿ* H(Kţ~L z=3.c1lC!DWEFre&A_Jȫ_tI|ʪȫ\|xxx2x.\`2 6$d·_dCǺ (aʕo߁ǎҡCG>Y%S ǎ o:O? 'Nbԋ/1}[^DU2w|ӧyXݝƮ ֥3 @rr2-5jOBB۷ckN:oGBBB!'Z SNet@˗-58իhpttd?gYq{.;Jow+0ϩ'yi9yƎB ۗ܎ySϣ>Y7HޗxDVcaaFa]h:Qj5j'j"Eeh4h4͜Y!(/'k hR>;g6}eL` '99٬[PϹYdTNľiw VVV$$$8yQ>̞3Y`>sym{%e6]-glJ%\I͚5YW( a ~aǫU+܎ySϣ/Qzuߔ2MRaѱ21wiÆ]5@ePZ5<ȇ/?- !PZN\gm\,XJ*1znM >&jԨADD4tuAÆRjU"2 *-7ʼ I|-7 =EGGrG.W&O6>tX۷jܺu˗Ӷ];(ۧۈ#4t>/3ol cܼΣ{qQkyq^BQw}U9m:AkN}ͷ1-~I<46ſv /}6=s&ÆҧW/ڶkȑxG_;}|k*{o{ykse̷ԭ3?v0-Zaa_űd2(a܄;zmq))Q'\]Yںe +>Zn4WFC+cޔzu9BQʩ}}DncysZiZ.E/7k?KK<o,ݹ} BZθ ?;JUbԋ/1: ?4+B!e/f hҒKKK|>}d[߷3S"WkyETg6B}%T_WjUt~O>_Ç(\Z}$xkN՜8~Yq`ccCrr2IIITXQe$%%t~L(q<ի-ݭ[7 wWlB!( ӗg‚E9_ɓLJWZ5;oϿ aܹcg @Nû9u?gr [./tX۷jܺu˗Ӷ];(ۧۈ#4t>/F@n cs /gI|W|WǑwkof+╅sZ^uFyB/GGT$_ghMeE&{i={6^-YL:6m 6mHmhҤQ v-m[yqy]EOad\?1cFiM9~+G^sg/<8-6a/yŤ B\7ŠQ|9~}K.g -"*._akh( 8 .GL پw}.(Ȭ8JXr%wtБOV}hB9o:~_}+++k%ȫٖungF{ifL}uB!D+0м'[6o^͛98uAkiՒV-efCty6Gn9|8mtM9׷Q׮QϹ,Z͛76 Fn ǾٳswfL5ѤIncm<Ͽ\'_7o YGcww> V~ 2{\HHH@qL6tpzx^c]tixxzꖭo)qpj$5kfZD^^Q ķgO,,,,Y?dͷSbccdǴjݺ"Bp`&ƕT*Zq㰵/ǏcIד&N0wrrqZϯo!Wݿakk˘@c(sjj*GcccSd7n ٺxo,:z?koAʕMd&11QRׅM70h nۆS5'SzgZruqj5{xG)Z!Bn.EyªT _,--tx_~ XA>Qzuf>ڷ/2Ѷuga AX\r٨Lj~~~/_&xF&ǏAL!)) +*(cymӷK|yQ) M?#άwgҫ~i&(B 8;`bcyu"Vj;5ݥ\':=Lu套GsB)GE?{W uؑ*0wll+'MSTOqLn ^XEB!D QftDURʯo 4QйK>3YaC^Pl[Jf劏i]LlX?|>^۷oju+/mvfQOBCGh6}_|^\Oqbż6$%%tq /Q$P}!C_ghMeEkx۶ͷqxd_0eTŶyu~ǎ~E? ,>>ݝ8,]fV%0cGҡm;N:Eq/̗ۼYmڴWr /ǜ2& ]]>b̘TBQ}}D?ҽ"Jj>ܸeѻ Y X9B(#55ԪUzXZhPҟSTXXXdKF͛nOԬ;hP>MΏ3^zZxTZpsF#S !DyDym 0N}ڬ@ !B!D(Cyu/*ׄ9R!e4 _(}B!DYQFB!B"1c5YBɚbBMIM֬W;PXdBI~/dIk6 Jr!(݊>*UGGUE2wռcp>,(Ntt4S&O]V֯G3OFg͊Dccc?:OQpyӢ>M9Cy!`x'LDU.]P<<<,?g,^0t̞3jY`>swfLgʏ9w^5ku69ܥ  Z˰!/Y#~Z+':W[hZnݺŊiۮYqӃm ooEů)\y8/KB5Ugm!(Jxl#7bƴx'l[[[z= ̝>o5 J`I_ghMeE<ߞ9aCӫm۵cQ<ۣ` Fӱ};6o1ΛoM3jϿ`ԩ<+[g8~A7aZ・+݉cefQ¸ 9v(ڶSS0_ 2gVAGS|^ !z؆S ~{Jj>܈2/-@T5z??EnB7W* J"[ڴ4oޤuFLD:޹ͬ[QuQ'kU N)MVGxL  Z )uRnbͅD&B!IaB!BQVV4F÷|/Wceeex]#L~e"&ٰnA%;;.X`s?^{s 87#*7?W72?7F2^&B!w)))4oɖ͛iWflf]ZZ{Ulټ{h(]D ѽG6i;wQ#qwsY:u͐hޘp ;h]DEEV”SeK azh40p ϙcV%޵g{ή={݊/jqvvfF;NNHII1+'^WXYYQV-f?h9'Uή^|XaʝJɩcnRBQ~FXHFXZZΝc}̙7= IDATY$pطsxBCwfLgʏ9w^t𺟟wb̈́S(Hj֬ @Z,ݾ1Xd iii@zw%?jQ?aaaȑ#'8GS˝Nƍ,[ݺU~!B?(P* VKqח1q$I';998UJ~~gv;܅-={۳!llxӷ@e]%7_]\^[[fBѫsT\8JHNNkkk[(+.u]شq@m8UsͭQ?wnߦSqPרQ11*7d.ZjoBQN=L SJ*UO %:oݻzrcaݣr;Vݻw T6UH\Qqr2NЪj9IT͉{1%lllHNN )) +*P^@X>\$.ُ?Ol !-- tOnUƲ-$=OW_ؙBV?@iպu1Wtt4+W|OxedCܾ} V˭[X|9m۵3+}z8BChh 060?'L+ ޴d޹'V1>&*_^_}E|| |[V9Lc;3^ݻwYr%-Z4B!('k.SauzY<kSpI͘KhՒ?d ao5cǎҶgΜi.VB 4nNvmٶu+oMBȘ[g8~A7aZ・+݉cefQ¸ 9v(ڶSKZ湲r7WhӦ-z?cxawV})fxwp}4MFǵڵnM֭_ǵ4hذ@uk]ƪ>3ܥZ'WLB!D0jG`pݬZKKt5=*2<C!2RSS ݴjT^o㣪LzH$@BDH( ґ ${룋 QQwWPHQ*( BQ`H'LcȘI&Nf>o_d93~9`rѾy4VG ġ/GLw( yZv̄@x*8XqL5vEd} [bRS>xe_&p*4 =$""0;K"""j/&Ww"""""6$IO 80a>d!"juߡk?Z=e8 ""xk.ֺ̝=Yձ[ɋpEgZ7GÇ/cg~ 7mF>}?uE_{U7)Xnm:"&Ek_'^{Uy]xxx Fg6,K fQIד&Oơa4F78ڒG YGD +3S2H:%))3v,r9DQě+V`귰is>g? Z[ɓ's5>qdlX:QW׆kM2ͦGSA^Gʒ%1cPVVG}:77ǜ?889yy8iM 0닙f7Ϟ?v ?= N4JKK#FJPT$ظ8DEGaO!8$=-9ixKY^yYBBBmҥ 3u&N:0@PPh4``0n:yҦ`Q5AAc"=88ئI%33âLLI'kᇟ`{GZbW)xzzBjkk-Iz)֪U7@3Ο籔uB^^^텅ܹu&""C9sgQxǟyQPTX(;w6k FyWxpB=sUy빹`.&F~;:u˖>R?-ǭ)ñ{W!ԟ waW=Ykm3+ýcI1uo0[M8`@TTM0}۶&HK.HOONqXᇨwه$%ɋSe&$/N.y,ey/p:CTVV7mĺϏZ/n.FO֞߰73{ u[ؽkF> }J:ws_q4o.nr+OAq9ҧCõZzݒ.|4Ԣo 3jzaLHTTT͕#%?ӧNaؐ8w,RH;ܑ:ZnuƍjOK籔?_~_ <C[>=bc˞DDD1?)'۵^GZ5$C쑛S7ߟՔw6;N];2ǎEXXrtO 5 +(ClaP'4}y=bCPXڸn #Ou4z#iq<.cyDN;5`KLj'/ך[f:.Qf_{p`/ADD1kTm/"j;wIDDDMX]K"""j7bDDDDDD$z*""r-AZX)JZQYr,.1l\ՎCDDQzx~DDD]]CGt;z6NEtE<<أ:xǠ_bϛ_~ٮ2 Qx'09W4]r)B@L>x%HØcաC[l?V5oi`رEoX¦q4+UWV 4ڣN|^yi~7FDn;!_~_36n;DxD-o]26d4y1##w#-G۶ۜG h4Dmm-%;69Grb<_1a$| ʫ8ix[;q E) EBbeٿ3DDDB{:( |OHNYX)KrݵΎ`ĝw5,3gZQQQ ALL' vt ꄗ-n>}8Z:ufk => >ں{u¬#TUUam}=xFMM og1b#6Z_Թ3}E޽h!^z*//yl߆??yY35(**^Gaa!֬^!CڕG IF#5u*++II$=>9GbSɋSKy4uc퍸x{􀿿?;&I9MKD5T,{¬xo7@Bx'moSOc]q0*-})~!t:ځ&G_cBtdWЈHzt @Z 3:cx=DDMވ+eZϭXa=Ĥ}`eM]OYpwzN ?/}!"jY9ŋ%<Y9()sZDDD$S,MSY =O?eD7 W5WVV"-=AAq̏{e|,|}|\V?"""k\/Y'&Yӕ认]sYD -W]@Ra1J N촫H;A~~!|lx!b͎̮' 32 >*:=:ٙ2~VW#;Ϋ#QK\/L0~aHDDPx=h5kkjvE"""r37z$ #"֡4""ߥ&_i`~X.^ȓ{CB\KLys?ەlV1\M0źRsӘniߟ>m-OKз-xYYd;b"af˵R-·GZlQ,N^4>[׭}'NDDDts p /kҕ8z;L4۷m+{v08} Æݎ'( lܰ޼y}~R?YǑoo~x/7ʷe&,X[neX5z%#X:^\O.]kN<`u%""a{4 no~ ǏØ{G9ruph4kjjp;P^n}}x%HØcաC[l?V5o DrJ ~Ų's3>F~ќ)Ӧcޮ~)SK.&L@A~>^bq+W~EQa!wCu%""=azݽӦ|L3ၪ*F9og#F=ҧpq$~sߴqFF@@ղt>}pYy8w,:Y`$I'0 HHLD\\|U[H!;; de6nFc$%cƎ\.(xs \6m`'VŠ-o޸ %C.cQr lބ?sf#Fشe9MTbyصk'Kw܉9Axߺ|N^?""rm/nj=aLQ t3w<==_ s)S,\ ~`U]]׭âEk$;{p9l jז`0 u>̜1og~v呂FJT*Hz|Vl\k}8YtZo=ӧN'N{SfLg}2sμy8jߏs!""OF>w8~^q]uCD|zFy'#"=Ȥ$={pL .3IDATߩL&äɓOg #-+<==Jkkk݆ ÒCSUP[[oō )Ο?)U1crsr;>1+.9t'IHLDmm-SS'Ob\KG^P"‚:+}*^֨?lGƂE88rK੥KQRR֮EʒZ-7ᷫVmݲb|n:yHS\Vjj%`ul6nފe|W飏\T3"""ašGl,:׿Щsg޽{#eBzjx o~O 1Oe2//yl߆??yY35(**^Gaa!֬^!CڕG IF#5u*++II$=>9GbWɋSKyxxx`+E퍵;oo[ޣq1=V~]O> NUk1/VoCo0>mϞy9̛= ƍÐCѿP_!|4<CضSW/`̨Q1I#QQQ7W+<N:aCٳHY"pG_57>zo}Aϟظ8̙;>4`M2mռSM-5 o^0+-GA~~=TVUa;|8<%ܿ8ߣ^kH5y99vW\z?Jeh gٷw/֭}{>&7:v <=AXm<nܰ8lKKw̄@x*n"DD7ZWʴ8[mZxǰ%&5EW\0SNߗ>KE{̙gO>QGQx8 g<M *4̟7GAxX(r2]]%""".kv+iךL8"u?'3va%""jm/L%Jvf$"4Q.wil`\"""[*^(gp`|$"j@d^?""rm/L M$ɑy7WODDH Ljk.k!;]UUpHs8T}aq"\x"O^^Q <qc/1/?lW)~jE%ho 9b5ӧ-iM=Zk<??0_nqDbHslz?tʯ̀BDDNz$Z'̱5TWVC`0:9\ >Pyzya]\'z$ #"e1lIMÆ_^Je>FQ4h4:VD7'LAA&DDDDΤLa4p5- ?9Fd2r9v~CmDixyy!WoDv A""|2E\MsC`pu:\ xyT*+h˅ DDEF:DDDd'] >0"-= L@|Ϟ:c&|ZKDZOwlCV+|5#f~&Ҭo=L=|@! k5&M0=5zuY6v˴/̍/6_H&xcX[ ?+˗cF࡯0s{ ""Y޾8w q=buSIhX>khD6QoE+~u~7FXP7R(hz=mJqq1{qO PUvJnf.oUՈ?\|qRYQoowe h푇 ZSk㭷FQq yyw"5gWR%D}c"b w/).+-j "/oo*\O*((+-`Az #zQ8gj#E3(/7A1q:DD>+P\*.X_iq:-ޑvWhjh4 &6+P=]XSGP 4$*ws(B &:z]O ŪUo!;#e%ΪۻϿ ;{6DD,kqT?FXk?oI8$Οm]]Z^zC) ^/YDdFPFP3d "bܜ&5u=mڦ|q)L#WmluT==4s`)B:i١{n kI\DFwǭ(>VRٻZK&iPDQ]YlaaPADD\j!6@) hB""""""%IOu%""w¸DDDL~9+w!I#2""r'KDDFDDDDDԆqzE""r'KDDr:aFNjtJ%d29d2BDTRØGDHf^L@h0- 'Ms~{"44"D1^\ 44e4fM]`@UM5IZFvN.Yo//#3; {w8Y 4|lt?"##}&XFfv<ѣ{4 gf"wO@FV6<=#ׯ#+'Q]=K ǼhDuu5$YYܩXoode{hx{{~ 9Үeږi ʋF^!E :ޜ ;A&olIkHE@&V5) C"( {[?QDBD (0zir5B!V @А`%{?xߠiuZlZ7$3** EQDUU51MJJpۋb7`0@ 0F22Kkn2u=G!?~ R]-Gߧ P[1n`0/̯z0$Y'}99텠 ~ o:/ Vc^F,dz=~~/(Q4 !0FcQ2M!dMEE%3b)FLt ˠPȡ,h.5WZ0]QZVl$􎷺_zF"K!CBT6B@d#""{ ߥK(2驲S-]7|] zS,3TAњ72v<"Śzm2br>(**FHpgǧ^|S!+;*y1m)jR[2x{{#449(*.A׈. yZ[|uCB:#/Y9P*bNk0 +aa!L] Bĕ"6UFXh0s@׈ Fnn.^6 bZ?C\C׈.ZN7uo:44QT|^^5ܜ냬\Dvp8>0˖pJu4א4j ggٵմ4sȸ]eW_!G(.=Y]EЗ#{+%jcIŖԔ l1;bw2A٦\PRR \rf"qcc#"wQDD_پކL&/Ξ=CB]ZiA6_`':q>oHDȄ1ܑ0AB4GA> ~#IN|+( we&"jK4䣹Evl1fSLjAz22;G3zyy#"<oEEeekAvEhh»˛DFSi5ĘGDR5&Yp#L h2~~P)5V _<=M7rv)"rsisd\"kHjĤw h_xz{Ai fA0< hn5qb#"@+#dr VAhxJ""DQ1BlIi=a2S e-O/θDDDfkLj{興ɌFŰ2k-Z~HDDNfpD%""r&Ɇ#::L&@49vh4B&k90.71dF#~S0%""r&[c}o8䃈˾ሌKDD<X7G&N4mq֘ԜV&'""2El5KDDlƤ8Po*`BEDDNdZU-ZŚF.IDDe4lȸDDDdkLj=aAQ4=9(;M`\""`KLjƑYLb9(d-%ŸDDDfkLFM1J2ѱ% ""3-iKDDL$k,a U^##版h S#=aDDDDDDe0"""""" 0"""""6# 8S q8"T@QE """"""kZg PxD""""""g7i*o0H'aDDDDDDP.GZ~5:) O`_4kj558y|S ;\RN=#"""""jj?ƽ#G:-3>2.Q#pz$#"""""r^)?VxGfz:7K%Q(cwX?#IENDB`muse-4.0.0/src/doc/html/split/documentation/midi_routing_matrix.png000066400000000000000000000431571404105311000255520ustar00rootroot00000000000000PNG  IHDR S")sBIT|d pHYs+ IDATxu`SWw:`ˆð1CCý6!C.Mڦm?ڤI4iJwޕs9ݾܼ{h9rDGA)E *V2-3fΞ9@4iP%KF^ AH,a0^|ɋ)Y "' gϜVgAhP+ B考ܽ{R˨ ѡhAA@ jg2g&MZ^<{JA!444۳GqJ"\Qi  RLף  DCדC" B1${AA08F]Oywn^M  $  6Wș;/M rX(A(4`8~6,A!&g̝oooΞ=K*OOAHDԽ{wuFQÇ A4߀ߌꕫ'R/kU[N0P^D[$I$})rCB(̓&MdɒqqMF_|ƍظq*gȑܾ};Mv YvׯKAOȉbўt5nݸfTtYΞ=wuZҨq#]\pvqIӦlْ+Waz= 64jԘ;q/_7#_C#@Hh(͚5g-ZyhGHh(M4-[YgΝekԨi={y?q"21EOo޾e̘ԯ߀޽pUc/:t 6b!oGM0+# uT1ӦIC@@1( M4ٙ&secݹhݺj5-[ɓ(0ڴi3*VӧPz֭[о};˲`<ҿ4ia[z̎SNѲE j*WĂ|:w-LMp}>YqFТys%I$I>9 }\yo]WʛI&Ymc'/^ SLf^&eʔfe@Znc'Oi+..hZ?^A DA|.]vL4\ynatY:wh͛ټe @l6m]v*Q .d+]46n$XcΝ 1lii-V; bΝT*+Wo @XWdI6oBPp07n;v͛;v dvoI$Iy'_=سNqFAmai7Zٳ̑=;'Oci݆6p1LL,YΞ=w`޽Xף{wn\NKNvM{޽{ء#y!E(6ٳ(*___NZ<p{8y,+V`׮]fڵcĈxإjժߞ9uΖ; Ҥb_(+]ygE='3??N``o0A[k͞[MYLc޼|-d  Ud  BL"Wd  B zP /RT*T)7P):E*%'|T(ST} 0`hlDʪL%"RE FMQQ$oyYDQ&(c_*w2| Gy7%>cZUFMe ùWL5lEB=*uS *ToT*EQJq1*Sp2"슼H+XGmk0ЏI{L%]8nߤkWa^7DC2|:E2G$*f8w &qVLb2UsD5,09s!j+#JLic , LogVC|4ɛ5i2tYGV4biEͬ&_F eO  4GAADp4dbۣ  A wAAd`m  ;nf40kD0EeA>1LQ!!0' QVcEAHLQADLkH`ACSİoF[l>~1Aw=$J^ ׀~\ \Kb}׃`?4y7iVTa=>Edɘ:W֬\NhX`>="a~xþMԩ`1w?iKoʔ)ܭٲ{})^$y4i*v<|b?+Tdxe@}defvxxzҩK7r;Y'L8Q#̔7f$?Nj͍]/=}U+{UAA ~>GolwmVZɉ'sMfϙ˝;w_߾x{)w5͛{JO]Pݶ3_!CqcQÆϦ\XXΞ͡Co>TTѦ\|ӧu <$MΝی5ׯ8.,, '''Td6b?2ɉgOиis9wߍ!klfvhՆG2aܹsFM>OlȖ=;͛7fM[Ç1Mb'cӴcvvD^@ ZJ-]JdÔhР6S:{]rg̠Ylܸ޽0_S‘GdjYb1~KnƍL+W0x .{)Nlǎ=jմ3^3ջ77ng̘9lԩêU+ɔ)V~qbJrɕ+'VKΞ1gM|ǝ5}Gkw7@63&=syRNmWT:]p0 G\ olz/~肃9y捲ёjQo9w1+wn<.8cG_ ~tG"Et"ٲ [\x1F? a:cꤟl?#y䎓̜9U&)Rp%`|Uz}iҤO_4ly| %rFSdIzRܛ7oXf-}'@ĝԩSfSݻG͚5pvvqF:ݲٳh֬)4jԈ3g%gϘ&Κ>{ƣ5[mGKr;Kr3" {F>,,+ uZju]+:V\@@E}ɒ%l̚>XB`6?ƬZ)aaa\xu-G/ZE;b!> woɓC;f~_;m۴K6JBÆH*?]ro޼k&NvZ#00q͛d͚+gNr?ѣ 64i0d (`w+VU8v0bpFVݝɓ&ٲfe>WƑ#Gx{ׯ_.mZPҧ˗TgOm;xx󸋔3G($"n..fXwՕ*\woscYPP..yOϸFbDwCH(B*_VSj5nߺi{-EIf:ۄ1L.=ƦT^NN|Y6׮\ji?+Vfޡb*899QR%?_jԬM)P25v]:vʃ-^tBR3nM§AHH?0ҥKS~= \Y׮_KnΝ 8 PACw{n(P'''ׯǃK:5˗ɉ/Wѣvߟjժ25ZMjոzZ䣓?~,f)],3ԩSkAc9!X&O ^dAqjߠAC}h[z6l.l~r dΜ/_8~? 3ɒ%M9V˴i8ٲecȐȑî>5kիⴉƍ̞=5kV@Q|C  |(K  DGQG8 `( `y2  1GAL  |Dd  60yӐ@RA_=:RA"sdQ%  D-=AA9q`k¸qsc~zmM6cQwFͶ[bߟsүo_֭khҴ/0N…i׶ݯ5w8`#L1[6kZ~>+WR@~F n*Yi,ؚ&9 q3mxO?Nk^z3Ņ3J٩cK*v߉#T~ (@հh2d`۷DT*?Ӯwܵƍk(>m[( Ϟ=c-&M${lqѣxpܽcg6RPaF ΝݼN;%Lc#.]JOΩ=5.)gK%ZrR6/N\RCNbGR;jhqsuZߤi3^+=Ǐ7ksRzzk_7o 00n]իW=L]1ϴ/wiێc36EP^ӮM4nĺum^zߏu;M ߌ4iڌUVpDVל;wΪׯ]SX:g>MŊpqv|>}l оCG;0q<{¬~f*֫ǚuЇ5kW.kΈ dhܤ1;wKk+VRBy-\[LXA|y}o)\+VZǒjҥyߖ={v/|rSLXu>Bn:wիhެ .jGƍ3ҤII'Od0 ֝;,?}g ?`br<|HNG̙INΝćjի| rbF2eؿ?wl2Ry>E*fc&Ȟ˗ѦM,Xî@|hѢEquNIΞ쉏N{1.TpGTɒhތK|ș#>>>Y2eˋp1Rzz5KbSpyW BUt߈ot賞WPn]jjըaŋ4ng?\]]jQ.\i&hҨgΜ,MY(Uқ䎎T O>}>,BrGGPR$ON{9[c#zi֤1j5ʔAвEscuQbEc^QׯZMuv7wk1oܨ!%<`/7lh_n\SL vϜ=KF Q;9Q\Y2̮O2fXWJ{ْTY|rI[Nl1{R eʔY(aZu2c5do>Y2f_s꣗K*vwbmDA=-;wf-j&k4nl4]pg,I IDATgʘ WW.^ NO&(0[6O<ٳ` +VApmX1y6-[0.֭ ֱw?|ԩy aa@̙ɔ)aalٲ ?~2pqA;vҥKq&C}TTLJb?عs?ʑٲ1irE٢鏰o/Kg%ٲeZG1h3rE?&M7om=}lF?X(3y8&ӧ>b-~'ߘ=ey\ 0pfZdr+T`]TPfvZժU{(V(:gt gg5qSǎL1?q#Δ3c,uAy4p@5k  hSю:0gxrI;q䒊o@TXgK |HlMϿ_Qzy  `P@k%AA18AAy  {AQӓ>{Qp!m۴Q#)^XW1n_plIv^fڵ{_Dyi׶59rs;i$3jgYh7oˋ>z'Oz+g 88?o ayqfϝǺ5ԦNUX¤Hy%;đK*v|MLJcM{CĂsh׶ k׭]+1vIɍkV[.vӦ SƌL'V.9fRb?`ӏǏZpb-…ԭ]K~Ko|l?AIc$y5޻qW-mJ©L*qg,'UBQ0KLurI[NT1YzߑcG\../_'7oӾXד>}:N:e?IhhE2b(z˙sg,{2UJ9$͕*U*ӭkatރc'NеGO ½/_⇟~K|?a_iܨK~[+Jzsa3TɒR}sgJ Yn]: uV![,^0͛jL4ؙ˔x(Aɩ\"Ϟ?AzܽǸIVӱc.ԫk_fMYKn޼eS'iPj2K3y9O/TnV;/',TG.6>9Vej5_TZDzuٱsW4ǂP7m{FSYñV7w7PAŊXO>ѣ̚=l㡻 ..5 nunn_]N]Xv}2ݻ-[Vkǟ&ݷcb+6uptȻ󆥣3<sҥKϏE :Mj{@:c@pp0iҦ&6#)`E|},rI;q䒊\ҥMKpP1m (;VIpBfя]\\jAoZm=yOOڵ#x h5<h@Mww39*]V_ftڅŊǰw _V&f,Ӯ|?I2%^;Sṣ^̟;>ooK x;iؠC4ڥ=+11tC b޼<4f32>d 2_rUcQUk\$&咊~[Xz=-[#ǎeǮ]*X0֯^֯k8w; %;o;q6]zPuBu3o>H9?;ufʨ} eʒ/?e˔5kom6̙;;vѧWO'ONxծwYoFB%ۻ,3Ͱƺ6Z1wv~5S!(0ň˱'P-[f2 OOOVYˀ*Zh%K(Z*k<Ϸ8y:uCؙЮ'Oٙ^={*F-aL:Ǝ1zȒ9 _ Lԩ4`]:۵i k2d@=,Ux BBB>Wƌ~:u86!n'Ռy7X%;đK*v|Rd >z^_K}.YCQP;q;vbq ` X888>c&Nd $z+W|*ڶnAރȧOb>ֳ7m݊ ~СsuW'AK(= D.=.-= ? Kg9/K  D'Vp҃bL  |(&䎂  10:p4L~3) g| KAsFAAAΟAA;b\~0yʹlQA|> AA0GGcAA,= `bݛlo>V2E Ǩ~?MbңWz,{ 7m+ɉ`#cL]:ulV:88w>GGG3o׹Ș!?E >eʕ#c7O<ߗSp!ΚA_lJW'SSXc:I*&*.&ef:B1%;o;6|'3dM;wҾM.=zz=iӦ3gڜp w,=zߏAC8w{uk3%Tpqqb tlE0}8}S@^a_k~</^(J_ן ?OWtыMZG[6ɓ\2<} E`CFK( 7NGxx8sg1Xd1-5*+(E޶VP;svl݄^FtSX!`ݿԉ?ѥcV]ˣǏ:g:w-[+Om:?ߧAL\yիVZv];w"<{FXXu7Qb 'N",,:8q]r EΜyQEKl3%cdȐ0=;wݝϞ=[6dɞ-;Y2g6oɝ.ŊeǮrӌ7̞ٳSPAwy`3Fq> 'dEcK*vwbW_2eJu]{6mL2@Yȇ~~ڕ#nGѵ 7?F|#.AeΔߌ͍!6lތ>K4t8rwVm:p 3f&0(93Ǩ?yoE᏿˦L OmH޽PT4k҄˖Sx1jU9  i&溼ؙҶuKM3:wFR ,?zu0}毌1()E#F:U*.[`N//wlѮŊʜy 1tHMlωC~w "TG.U*Ǐ4 + Sf# ǁ2dqՃ ؃>zKW8i Ȉcy^ro^bԷ2bXBCCچh8s,fwߛ鷤@FÌ_лW|n߹cѮ0-vXoʭwhټvB0\Od,uqX䒊_YB8|:'OQ \kTƖm(l޺իYc͔.Yi'r8ad*S6K ]fD}Ʋ,TG.6y(67*thߖ[߸mZ>c OA3xxx)SF2oޤFjjVwAݺ8;)ͫWoaaaTZ)pԪZf/z]}ܸyիLZ5no[?:* }x8AV]8Z/_Z7nlƼ(Kj55W[~0bEHrxcbIDATw_NeEQ"㢦Z/uMqNZ(Uқ̞/^0qT*U,O_ZWH|/',TG.!Vxmw">SXj5-7Br<7VbԈԭS (W,Abm+nnnVnۻN!YkcVkFc \#ҤNǰ hU֞s ƌaޟ{D{WW-^z7t!!4iҐ5Ks%… $uuq!((ȦN[%~<.4Xm#  ; ظ~&*Tɉ qm2͍7vr`wqv&PݻwV%fED|hj"hQC;^o6ZMP`ćׯĮ`O.\DJOOҧKgVn j8;[]؇5k2k4̝mVWnmXIFn??թmV5S.nn}֪|hE+#ğ8o-_$\RCNbLJ[1(md؉r)2f`J:w uԲ6g8t`CG}' o?:]P`Ac]ʔs<صdQ;rÇ o 𑣔3YvmQ ɐ!aazvڍ;O= {ldɒs=[vdl`Çɓ;]:+ʎ] ߧ;n=9gpt҅s. Q4sQX䒊X~n"D,=m>KG.|%׮%ctɬ˔.;(Stjќ|G]Iݝ-ܨ_cQނ B0[zȜ. !:YzAJEN<|H]/AG!bn,$$$MA{BBBHF.Ķ   A:aaa$sJ( .$IֺM[.$Զ   ZmJ_H  |ڼ~A1El-6*8%On.AA>mtJ2f-[d`g"M,onA$UJOzm67?[G=?PIENDB`muse-4.0.0/src/doc/html/split/documentation/mixer.png000066400000000000000000001567541404105311000226310ustar00rootroot00000000000000PNG  IHDRbݳIDATx`g!PA`{A^ *"X U tB{folۻ۝yfIE[{ $  &aJ㻺v%njǴ:}t"[ k= ܬg]U#lc| _)FΰomU@ڀ QjYu4N.h[)_Fv5űVMF\YͭRSU1N<{븨: K;0F$hp'8h b'NB H:xPơ˓&(ݙaC_TgHM;:@cc8ps% vj7;E(IL 3Ӆ9i\oq>,9GGL:}<雞>3g.uYYzaDU•FKw=4ܫ"t)" \'~"7q No2%?Fs7-p igb-zJ2׀iVʙŃ .#[Htg ρnm0f!@y|f+5 ^ Pi'Nѝu9s^ߔ߻iJ|>|hBzFl6*!F4bKt%uV)DtKSEKokG]Yb\^l1o6+*ZA]TTiR@)B:b=otte o<Ҟn !SҸt}IdL+fKR>ӇANeڛ\PoàI2&t2"0\F&b<`Om8yByS0Y(kU',QPNx(灘3b</YYn~-7nŰǝx~rp*Y{Y, >X<瀟(9l F8yl(9T!sgNq«AH5RYQ!ɨ&IRA^ߢg;lߡ-8嫟K#qEeŵ )|UjMX"1"&T&W*UPSqD8Хo *" WϽP$!5#hrp008:FcpesV讓v)b {4HsW5ǫ~6"ҡfݼ?p<!{u;ٽ]]:$5` $#t9JWby@;5ޢ:q>fSwtmqTz; URkk{͌$/Oc_{߬ L8o7{;BȉWU).m-ܽdw{]w9곿a+O ^94J Eܑ ;=ژ<1 g/`_ 3!lx/o9c挏5[,͙SYQZr_# n۶믿aŊ6+VzpxAGjTaR5tݸa1lcŪ յx{ۦ=wݶXMUE5X DKO>t^^0D}&v%yn5]J& 9 ҕ rT ,@ũILai`9YN1s C_, U"xܫHwdܪ'Ҧ2Qt}cǃ|5{iǮL@2EЩŸu=!kN ~'R1&y(Ex1<^ЙV,f?\p(!J’m!sTRoPͧQkHWUE~=II\UQ e6uz ccb]54" %T#Zhqfɍ70eʌ~ᅩ"B8G}xl[wg sÿI"ØGg#TT* s5lv_v&&k|Mx&[x*AQˬws?4Sr }tS{NƠ6=4nȩo3VٔP'K5vE5k?~bO?K$k֌7nүQ~#Ks;::%%yD͚szNQgLd|[c$u:ݩg][SåP(O: 9Mx2ji {:PM5UxhΗGa1j{${J99͞ІwGa`3U(򞩝?#DTs_w'>Aܺ ;uoV[V;#u#gA5sWljbO#;SL:\ yZ$h7Gk3I\gN^$y`"N?}to' /?үΟάvjbiA+SȽE5" H_|ښʒB}Hڟ?a8؛<}dښK47f ̙glM4CR:E?[XR9v} {piuUUP@p( vt#6m`)Egh 9^uܹ2;9I@wfiP(R?CF|Ow>KL%j6Z/n"MF j# +ւ?zcw6Z9~ou]p_^>ɾ3`Ԥ=/3[n~fh#]#Shex>3I(2j/yryΗuwfrf[hٓ\IK8=Tšz}JyvNxLrRա!!N7FuQYQZT `L߷B~#m{LI,k}hZ4[(**l^paǎ]p8Iٹ]st xrlGp\%l}xʳ Byn`x rR!HzN{ޅԆ!) yd#I> 84j;Fؑl5g,e3Vjtj1ԚZCl._*?8x=ݙ[:7}E4A8R|Hs߭[Su+4mZzTd49OFl =;ǿG {Pbֽ=o1L-4jMn^NA~YfE*hDdž%Q +SCyWloA~Nu S!g< Cz&!=R;s`PϦC3M!TX<;{nU< At:u͉v/t' ܜfY*CxE;U Bz`}跎i)We\8K$a:e| LQ(j w]Q Tca4J2zG0t.aLp>=#Od.( *5yagV4i{vpl .&(63e.që%/r9 /@x5}5Bm&!\vܗP' [w)E)/1ϕoy <4 %=Zwj3 Z'S|gvi#4<3TqӜ.J!0yyo* &, rrqP'QP3wu:gq@\nTk2.am &>6܆ @ܛP`!ZXMEz@ҨP.g#n?~&#+u)Qn5jFe4  * "=%2"XNB}={aTk<ȧUHB0ZnsWǻsk :S?BT'4g% <@TOe-*l[j|nmbUms7yx=ˡA-􀃾(la=ra{ A=&G|OAd)^31:G)RZRF(w7H;^t>΋D[=*cgVrd d9*NGZPk0 I6Ϲ/R9>-)dx 0kl[tI&Ly~L;-+=Ƞ ÂafO'Aagwt "F<_pBطxY465@PG' o&z;`#B_{jʲ@=qso~ bpVѨrpZN5$?O*k3텣&5 Nt9>B]@S|EXWo#6lG爐SA>凯0i'4\@/o]{l;:Q0Yv&Sk?də'8qlrv)D߾Y}[yd! U{_ƬBUC|B IOox+rOO|R\wYL遃ExO'x,g W u4FfprӛL^[+uSXg2 ;VS[ n{*xpTΕ |g5 x^\Bm"59~HXʲSc}wꮞ"߭?dEi/=N*ӕs7JbK}_jo GiˆG>꯼cb`3;~L $kQQjߴoOB_tP18~0ה_rߚt[/3T6? Y ] j. qVkD)[isVX y[wPO5gqQދۧ^#ȩUIQ|sMDUΑo4{.ȡ+roGA=0s"ޭBC1`8AWbUctySא-ޙ:!UT)o}='ݽ_.3%Ma~}CiK:ߺpә!N[}BK2K St+93 _އ {&y%=`q 5f9ɢ>rk!^  J:4H(|z>f7}0;3. omL?6ëTqtګ{2RVg G)QJ=ˁpʅY51sZdGNae:d7O0 *4)c\,͏` ٝ CNGwagO}3{*Q>8,ig(Z~@GI.xyv Vиaϫ'BAN p)$@%5 ѽ)ˡоM_ 9e= fOLle'<^ylem8 ZySկ81i=}>=LR>kҀU 8Onߩ˜w)կؾv5OZtjEm۷^ ?K|Z7;uYK{t}|g%pQQ>1Xx`?4 I fz>-Yx(:^<sDϕ̥s>P7ϯL?J1e֧Qo=?t3S5R u]ʩDYƦO<o*P0o|ޯ 41 3UmjYʷ[}M"(s?5ih+kSbytV)}=4B|Z9t\xF}h /G ;^QG4f@*0ijsZл>DTDS@R[ވhYj]x$~̻o긏k00 ݟ9OI{?1sTOL;3F֯<,k75gc~C*C>sm"$Q=-ߓ%J 2Wu#Ok/ o@vx誔l +2ַqt2,#@!^]yaC]XN-O\BgNN=Y,[,/W-f;bAt޲duO6vB?hw/8$?p=||y#%sY<;wlt]4H=a>ul 9@',}Z)ߙ)3ބ>ޜOᄆ/,ZtqgNz;HC| <۔w<ԧSr(Y{xp >~B^a}!-zS*؈pZԇӕ7xW6wF{h{]{@St #)#V_{O3Azf+8~ It_gyğɸyp©<~TAL8<ڠ0EؤQ#s 0$3Ѭ˨r>NmK~ӈqG޻aOF RR^eq꿺6"DC}Nn4_`_A R ʈ֫mwHu=Ԕ{ah@ -odp"7 EJ'Ϯ) j{+t/ Ind'n7Eȍfko$1Cwb{!u$;_* \@  x]!1l/DbUWh*8Xa,?=@D*PIqz Rdr**3l+*OԨ:'SgZT~oldHenI/n껦5Gf/5&DcOp^&b^yLHԱYnl:ǘ.ã,xEu(GRǴV$✢b{S3M,<^$DOɫ^k~6n5@ArFMyN"gWJD9׭kޭ{3ѳ^ߚU=P(21٪-4]i)]jL;+㇄*ÙI89n;gj_ ebya!uj٢)3=~4!8"S-~l3UZ5,yhHiҰo{Yu`?j[k 4#bC ϛiiSN2HYyվRh+&<"f3S3 [W?to#}P?Kv~*T)+o͗\y]qA5$š smwyRZF I A~j7.=j8)Hb;AG$Ú=: U߯9IF*ÈQUuKfy,0R}w~{Nػ[HC]mYJԈ"2z3-d}-8PÆEvl%5VgDМm5 ~V\U4SE-H%֗X55SEg>/*̲5 ķn/ g!6Qc1W+ *ra'G$X >{nQ^ЌdBrVPÅA"Z)9Hra P&@._-CČEsɺW9c_^>O\7>1T4X hI[,_}z)Ҋ׊kŬpJ".l}N*e]8۾t;gcc6^Vw\.`ξ}H1^?{ʟj6Uf饺dBN,j<#C/v=ϗ%]{s-sF 9n4ʯ\{ν,xBe9)xaO%az諥W|0*[6R[ӏ1б_ɪ օ$\xﮆNxg>>156:䚹GpKKX$4T Wo6S \ bښ%v]aB E賿2+R@9ipP+jV;I4Zr1uR,Cal1!qxl#֔~"gkɲg:=fXn%}I.XR8Tlm~y0e-+*ʕ&͎*shfE.?6C܃1~$EZ-yyYmML ׁ,5P%Vj)nزKE{twJ$wH/QZ;; 3梅'+} 9 9V,sra!}D%zᆈvY;~1Ήoh qx}ϯ9u~Hͧ/}ꔒ;*G^\y֜ikޗ;~ϪR}to*gAB .~{*m2ٶU5]썭`Tʉ,cr&%* L6y%^quզSW2l'1Y{s~`ҭ9U@gsLWev-Zogj; {۩C'o'CرME N׸/sslIҀ)UX^6V#j*N/J^wL!fev: 18-&^%xYŧ:X?lA$xQq‹ʟ&$|.w;&u2ԪdoXpTXXE[6S/䩩JUmwq1;{/@|y,߬owj%lU퓒=1dݵŏb*P.Cdž]9/OOvbw1H/Y08lpC(~~v9_[P)ark[p\mۖU)K֏<RIvTe__x{rHЫ%"8:L}04:dUNB?sJjRV|)Q{qNǾ`|}ѣظ3U .?2VUX_4 D"&7<^ȃ~ݥ-UTDLMVD*./lVƽDI2N[驩h[nՕS"+ɻ=uMF{sV8H`![>"*3؞E&]4"XJfn _$ 98nֹhrXF[ nA D[W[QMhvτ7;kEe'gZY8#Ҫ}U2~eb{oޓÞZw86.ωڋ2CӃ)m68] 2Xt8*R-yEfL$N%ADoφ_BPJj=C 0MM=RjOݢܧA>Ϣxȋ#Bد(&T RpCՕ6#ՄW"-nHכ)mwWiה_@8@__uw-x 9;oO򶖕7[O5'5I#;+w_ >sg#tr_m%vuW7FڅKæHd&*f޾ !ܐb/@{c=_SP *J:q!i{@kC:|Ʌݸvn_>rTށH1T-j TF:I!Fs]fGiU njjC*ݖs |m^r}gH}uTvaUd#B{JzxA n:UGvaz]^XKRP 2\QvHoྼwԸmzLzI,Vh2Ԋu98[uK?@$rh}9֚jN`=J <æ(Gp 7hV ^!.XlRyy#,Fۏ|W/۰h1WhepJxXHPۖu\IqAڼ"B1adlq_$L| *Tf"="kGώ$ET"CKsUp"ԩgJ"'H{1JERuŸV  Bqen8HO۰z~tk^vJmqtap'-TZVhN.xE3FVtsLZ.65[,s-jm eζOA ſ /b{<<9҆ğeh{xvg.s-m(+FLo6S_DO^XkNN}Ɠ#u Dcn/ R|Y)]d! g/G`:_?tm""ɇ{/E{_x=]?? Fnj53PF(Q pV>q:ʨ>f#}e߂JUkʿ~{)=ⴌ,ՌՔ^.(=SV?qrpȫ-/"Nȱ yyn9b8Jpy! ϹDbx U8U!xթeV#KW-P W朇 *̵8MO(|2ƄKWkԪ|Kˉ"">YzkOG4liPWZyuv߶J/2`x]{qi3DM 1%;YjTU\pI=xW|!Zkd,RO2Y>)Z\SJ>I Jk% .ZOm-D^XR0ʳ_3L\&⦴;zFhdb/޸+&6$5,Qu:qM$ZsSƷ q]};RU!G('H9p'CFeXm%‹r|!scWO-|G AȯV^h4Z$^^qiK#ªSd3WA+ 0?^ VXbMFI.ѭ}s}G`p xV!uY~} ]Bse!T,׉ oXX ?l.rS톥k.Ao-x$<>,sP3mx[sYw蚤0iq DE"nbx@22b01PfnΚQϏjcRZyb<;"OEco*#]B# BSn1FZ 6@4<i3 t˕ $\˅ija9SF匈0&X$q=d^nd;_vExOH}7N'ACSpjk3ڣE7j>)x"8Yp D䘵,xIG|^ECod^O]re }&wZ0q$6!`wa*o+%u#zx1Kl.0S+Bh ΕNoND}y5N\,}ihl}M҂0bxNqSÈFO7bz1YT=$Һ`_QU։dU]ƴo9EcϦ'T.K obv=٭\W~9]҉kVQ~XI?Ym/&jc*\ۙغ{  r2?ظGsw E#F[JꆲQSzwKkǭ 2yfE3+zte>y~sxa>b'. b%42(e}[fߞ;Iboj9{%"GkBSrbZV n;ƒ_iւJdv`ٿ[\ɷ7kN~+P/k}9rG++k;YEqQ]WNN p 06=5vWiX1(+..\\fu+Ma翇Q$8̘w!mJ~PxZ^;@?01 nɛ1$yK;]8hVf21+j.c)Qi飭=ZI &6d"Sa΍+p*S{A_~E=FԙW(,dij}6Vh,%; aaVъhV{SFF@nXm c O~_tXpLO>CR /ުgRxxl2Meg&hC%/e'QO 6Ce<"*)w\5%$?T⯏~|Y<:sr^DWC/^XxJmf]3XFuEƓ.o+_ۋĉO|zFzCf#f!Fv1M+UKߛ=.&]z=|Gb%BTk@ii~~z:m'k>a+7TZJ.u-tb@KY+3P6j(?o;"Zk&hg6 Q)歄g"'zpQI$򊱘.WT|z9@ix %cG 6k!af>òAl"c.2 #Y>U s-8sIS_Ji1'JLh+fjZ lf xd{fhZqGw't+;fO HLYo/0uם}l؏ +oS8!& YdWsF\ŰNUO-fAvن7"2GF:զ(*ɦ *k᱑߷=Vcp} ;D5ƒ62ƪd?#dBF`2m=7n¹b߀M)\^ج,#U;4nDFl!Lx=uaVׅ_GG6SJGFqÐ~}ht4f,T|msqaW'dm.IL&F;^} Nb#_2[c?-ʩwnӘYAu˦¿^mNAqd;Q@[ϽFM1ֽ=O38;lL3np4ly7GB*Fى? ,fSt{$oǽL~𢉅 2~d|;Z-F5V#$i[^FlձCs!vm|jW6Zmwm4sݚٵHQ,&[=h R^&0TIYpX7hf_H%բm5hŖab97hN>?:삋'6WSy}BD\h qE1׆/|thIm'ॺPsLOGMmxє W/_ALDa"t,M+xV;_?Wz`-B6u3M"_pFPDИ!{~'+Q/R^ciS-|o8a=qoNg+|~(+Q7EO6:=֌ t{67<F(w>lIaJQra6nX&B&x|7ݻhvGQgļ Y욶9 HSX,CuJ;TTz O^$ J0KJJϝ=[[Sc28ÍfK@T7M&Aڰ.)b>\i&BFSUEy~vJ RX&'N^x[_xѐ^x?_~YMm"Wv1Z^4x-x9 *9Fq GF#YWxԯUA:\\^"E/HctU,cD~fd1g]>#sSxQQ* Il\[nCCrvϻԝeg9 JTL,^UYA&%]H&X+W(6h,U[UiSjuF1<4U/FxAMv"6.>?$%<\uE[ X"$wߡ+"&."*Fd2nCRA!Q%E%E0j1ז˝^4Fxd R}g$䝤lj4 f_HD4R,Yo'S^4x)Җw 6mϴU.:ekV6bʎ!*UiJƋ$Q E4ZE>EF&8 /ҷkK FT1^FxtryAuõSsvlFu9mRBX/"0*!m} \1'rG2#G:~\ DINϐ+0]DPW BK8.jY0p*bʾ Hŭ/^h(!{j,طt6i~K0m1]~yߍ7Z]ثSkօOrAxB*%*b1T{b\l" T* )͂I" y-^FxayoE5wh$ψ"W}OhwܳӺp͗{[XN_^u[YuxgԜ1{5/ (SGމpSo,X8 T7ۻ܍S5BH$RI@X &.<"#jZ윜ؘŭ/^h0,{sK]WF?7;bv"0kS^lgNYOu¬=&OH1]5ˏ]Vu.7 / Hj)~%Z&c 1B!Ux$C/"̽DUKꖶK(L`bHӺO_?бZI/ /C eqi`obxƂdSoYc\$"JJK}^4FxaEлV^VdI}O Ɔ Ijz~AP%5-Ut8K:vxʝRS/C0V]UInC*8hbjϙvUJPWKTFnH%&yqk/LpOѓ SWt%>R"iƋu0^N؀uuXhfaJ 1f篠ߏ;bm%am9^ ܏?ŗJ{ǜ©y]]] /^4AhXv#6+֝ pbOs^eW/thr𢢢̙3izP^VvԩviZc\&ITPJOXWTWh!$JLL(Jbm\ g69~E`P*m xg^"E3-] ov xrڵNۨzH$W.^wMF:(nTUmB 'ޑV[Y Qy9R/Jg2***B/^x^r R^^ϘP#zϿ?ӧhZs0lzi̋l=]DٖUV ^HZ6AZ}Օ!QdUUu,]RP3*Jj1zPKVQ\VŸ fht5=|ODa~-\EYIVw{T gv՞-/A6Tc<&/i^wLp`gĦK<  %ā$If4Wk{e{_?WiW>kfFD3J` 1sZ>_ Y{ZF~l*ې_51mm@!e N/8E5(3ٕ>HykABLzbïܺ?T,\sc7,5uןt톥퓞Yae.ZTж{IaӿsKpKNJ{8 ^Zysy9_t4)߃jGҬ=HyM喍m8o٣k3QdE6OMT:)>rE\DfXXɛlp4rT8(&t:l 'viKr0t:<)RWaё.WL=VrƜ=-W7oq6}]R~dR D0j/.o_X\R)0YԡHO뢷6{~>N=ƾoZDvP0#?gDF%ʡojYqW|NLDHI7` V˵C'{ _`@Q{M =IJ'_1E=TB  &ƚo9kO-z5‚ ՚>zP@|ǥ-T13b$tڋ(-Y(&RXuu*{/ol!UG-[گYzst1=|onUj| ҡHbZgGG{9:>1ATVsU^a@)=M9 ׾]W:n2m&Ú7,L6vk 6pB_w>>h_zU({_dt(,^p QPF‘pmc Y9PJ?1oN&*cXSuL$v 'Q#F  jKjV81\ ~/v9f bs t(,t&FGF|^Tj:d4[IE.#PTQ/(*(3=^n=8ęݷDK82lP4AxūOt֡?͵_Q+Z^ܕFҡLf=1s|B zyݮ>h;HJAʚa >_P%. X.V׳Wv^p,lVFx6eѹǮIAa|oyqaG9EZ?8m~oZ=z&xK[No}=>h-Jh ;PW}(@J̬L&?EdߟرBޘi5"K#~O=)șg_s@sb (j4Eaϥ J9Їvut)@Q/ɐTx݃ʜB.A116fLzBR4x/}w~o u]l%OϻniSqn',Vۛo{ީ3m>QHRg ̑O(4A?SzGT~h` (O!ZkQpݔ%B*yP"-;YV8_[W?p//d4>kF\rO$&! *X9C( C+ &KBmKE#@!} yPHGv  }ܶքB "2|,FT} Dj[TMQ'PLM hW&E7$PviKr0t:<)j!V;:"`JN>ڋ(br;rGN>esj **%ɫ _E5u(j4o٥ɉu6{~>6PBNnED4aGrũxr )bz}:^2P$=Q><(jDE9~iIR=b( PC(j!?K0okP')Fnr]w\:6{¸1xq,@!M yPC(Li +_dO3.kҡbXj;9 s>_|+/ZٮnyeT_~䓏~jҔkbbׇ;7øP hu\P JT>c^Q;Vˬ AũD#aPH- PHd EfE~}:v~3Gz.\dOl8뛗˛.[ÿuՑxrVw^y5~wE;;۫[tCŁp=:JS_A{?#@o#h^f9CrhhE+J$ۋDYB)n4(Jv'VPx,j`dµoDnŕxI}pfMo3#aȦnc f2ϲ2[`KO*BV<,PHnT$aгמo"cḪ}}YCFu}qٚ[zG~hxx+h8{c85ͼü#w yB&_PTNQx(PTelX}J?ݎ$> F  jKjV81\ ~/v9f'B :No0ػzϟ_߫ƼEd0g<%yQc569|nmk[2FRMt z=<ϨɈikm )Sp~@!N7YWw{["ڎ%gEf g #?6.^}ϰ?hzZzP6bERWn]{*fh\vIϬ03ZTж{IaӿsKpKNJ{8 ^Z9sxS/("3Q\t=+/~ejb>femwoSX{ylE4/ :Îr )@!B=|K }7=pƖ[6~eD|EIJg4`0קpZZZ.b( +x+yF#Qk9{IA! WbgeJ?vІ7&EZ H=^EGv{^13*Z31)īnz׾VPDaǒ,a^D] wJ S` (2*y!>uJ}5݅S3Ͼ PPϧ٘-esoۇѯ48(4!HɌjYqW|NLʏn82?DLk/‘O=k@1)(0YE5)vOvh,?o9kO-z5‚ 2 ՚6l2zP@Rs<.ΠhIA#P$އڋ(.>&_p H?>sdA-=0PS(Vڷֶvв=}䍻N.-TFEQ)ݖ.Ōڂ% <ܺd4=?0{,QP`PHd N@QOɆ~ɫ$ͯ(꠲Z 1]Хi*upw*@,W no`0qe{1S)R~ ()E+YmT!NHyP?fP5}d2ChDM*%$I{`(脛s̟?HL Ք_`@Q{M5 ^胁 zUK,Ĥ EgG{{[ĨszG W=GMX3o~[kkkK^@QV A1UmNOLWnIԃ@QwBkfa4J)èM7{@Qj@!K6ertZ"E.>MsSx\.>FNg2';E݄‰a~`ȈV9#JcFlU(ДDEY/֚BlTujKcjm@#%Na|(l2ks[{b7>]C}:bwJcd|*##yPZS"|H^_1E=$%hvn?ZYxYK8ѩ7wn}e3tF I* PS}(@QMzeU:x>M\PH"-:? ?O$(g(?lz)Hbk2jm4a2 ͊zlD"V /w (C! KjW뻎m^EZ}ZGY}'ǚX|_7|~kdl_<{7~k7j/mS%;[ ҥ'EJh5Ɯ1'EvPL yPX[O?c<̳߰;w~pMӺG6>4-`˯aXϜsIΧ[yvetpc/kϾئt(Ҋú%{u0~,{=ȶi7/=fUo0"QM^@8~bjٲ8v$FrYZ9FP(Rf (&<(B\1Όch" <6E(J{Ks"0*w)@b3LNUձۧt:HUxz#jl|πoǥm5EgFM;3)zGE>(C!aZftō_i"˴@ϾKUt ,6[o{ީ/C1T3h&PnbkB b>` #l 7i}un1S5I:GGB q% 4SO?vІ7&EZZHb4ɜ؛| Q@mAnG 8hRVmR/"-CEQ@x} )w?@kiu:f*y/Jx<Hbx R_E4U(X $I-$i= A)v(4[&1O?(Z\~Ay&vW$hu\P JT>c^Q;VˌTJ~(b BZ(L!hP|77ϞMʏ?T<޹sg̜hn.vPMe)8j1={t* e*O>9p(7nfAA43GZx_ rUWd{(5+0?P;m{vt)@Q7M Ne`Xt⮙_\˞hR5 Ra5iubE}ćB]Ƣ D={?X/ 2"#]]{?{lby\fn3 >CCm]Fé_o 89sB 'vn"ZDbʿ`(w ?Wy7`4ZhD ƀϋ1pk[ۚ5X,l*ې_o&#:% )S` ($Pdd`JRr`E6Kl[^ky;ٻf;ڽ3ݰ4}3+%̦mF#a݁ v9) q^|.`;x2%\0{9k}szfVB]@!MR\2GҬ=HyM喍m8o٣k3QdE6OQBM' ''\ .X" pXɛlp4r2 R@ q]+Ws$# ɳD+>K\ʂEU foNё.WL=^8TPFīnz׾V=YH7]è" lJUtsE (R(r>3ϥl7JΙzRS4U53gVzH߁pHYtRꚥְwSe a(:(\dQ3ڧl1m~"J!0}ޜ= dF6e _M8i*f01O$BJc2^ G>ٳo֬3:5PHB~@!._cdzw]3MF}sEz?۔T@ TlUd];?@Q&SD(̝|wP hׇsՇEY i4~B([NeS .^a_:d4zP@R"gZgPkR~Π*jP( 'j/r̢E%N ҡ_E2)0PC TE#==[[[ZmY uZ{E?5YUeb-wdzȅ(Ǣc\1}f׮gٰzQ`( $٧zeu{cw\`;,ڋ(].;[ 9L(L*%0KyuɊi9zQD)݃ҡ_E2) U"?אg*HY/, o!#TAr NsF" 4\"2gj]㢭-v{.RV yw.FM㞨DjTL1fQ*CCh\EJE xq˖&{zBZ PUB@gnN?Z cO95+/'' 2Qq* rVeww-㑗 tLr3w2g 'pJ'衇3^qe΅̲w&{Y`jxu }wЈspx8IR$$``(}A7wJ,33PHB~@!EvQzi9{r8=/BNnf\T󯖎<~UOO'+((@9{P첵%ΖRkY'wY_BmY=(?A=(QYr~OS0=ZtJF (#;0XO3S63B;VU*#}Sh.ҍ3{vE)>7\E ҖY.RAzOOYuz果4IBӾ?Xq<=Tct̯c>|+nٻ\gNpIWRnqK.VOIUO ٔEJ.=uL)iAU)x~`L; UJHnxF~PAn/W?M> Ze'J5YLCC@x/VqV>^'QCN*gw?̯ϹW.ߎHϣ(o~omy)8W}(0YEa*̷SBM(4:|,ERR_PBf/ӂרձhTd @=\Փg={g oz{Gʦ>?-K-mc A_-D1Ne&8!PH"m19>}??;y}[_<ۖbotj<嶋n~mѭv<̹^cLϡir!޲>d&P lf"9^QG.\hiSt(*4MV?$Hl`_lO7HrPET"bbda EBNd#͹@!A*T2PNe/ һK(MUEvs;4s/{c~xI8G,)fh2xTRzz1RDP&)gPhN a8>2;=sNhiU'vv6 "GDtx_ _~&Y/f'r!brطw՚54X W\u(cHd`n([Y(gā_SNr!!+m t< KDxw,tϚǻ`rcf)7 ]vŕZ67`\s!f3k2r6w9GRrE!?0Ѫ;myeVE;v_T Qd,6SޢF F#mG+t $m6بzbã##ík.\Q -'_{Gݻ=nwkKsyFֶfejh&ȒƟ( B]MT)F[ZzGF#-Z.G|CuiM2{֬z*Iq%k@MR/*EERrźWcшu:O=<y]Vs$aN7N07>-ont:/ZXo"-, (-hJGn~!-EVĭ76ykc> K7>zfN2vT扐;"b?ON>o^T(wq r Ջh4{_>'>sڴbU? u-z(HRSSpJ!%%h8Brh`_߱B/Z@p P31sj-lbŊȔJ9TDzG himu\@呝) 5X&b9F#(\8κQ<#<)-[O_I͠]u @p `e'2V(OQ(Xz}Q٬Vħ}9mdlEQ^9r[( :m,{pςR1}K/sJg;v EY}C L>SjOj>֋+y_T)aA@!e2^8>tǫbVQe┒h$ ŎG,O޽WN Y@RYZRP [$9{޾!!PH"ɩY cHNv̮vTj;9oޝ;wzٳaF(y A)E=Q:d +b؄PԌeKl`P PxnM5K.nɱ/w>!PHUH/98PFJRɈd(#;|X(hoL Uk>;@^_N!7ش7 Z-Ix*%@JKL8.:eg)[RTTV\ _^TEՙ(HwrC@!U &(zzK&tv8DƘ?wnۼ|C+hklӵW~#;EPWzevBA DEYE ggBUaY@$_ܹJ~1d*hW(Lbܞ).Rm2R|:~3aeo~+4!0EGba(u.(*/dG}_>b;5/~T[Ci+UP4"+-6dG`80m΀klcmS$f1B1EB\ r (J"S_dI}ǀB,2^ sR1Irp Y'K(Hba(S,@ Aʌ PH ;qT XJe!)X@Q/UBjٕ l Z"Po㍤_YԮLXZة2EnZ>!PH";OV#D@0 [FbbSS,HA2PTlS/,NJ٨2y}uьad ۣ*Sݦ#܊YLUSL]FF ( 託B>т٫L#BBIIRS, 6JGҦ_T|%U8@Q%ś۷L,E_,Uҥ5][d="QDߩ [W}B(aS)dVt>B;ң_䦭"W]:@!ľZgKuы)UQ}BIIRS,@_ "$ RL(f%$5W{Xe7Jf6i %%IQL $}.jTb$xo,Baua>R;_䥥i'Sйi(Cr=]06kǢaS,@ AȔZ B`pa.UٓHp0v /I$Nr9֢4/"wЋ#a&EYYLPL(+Av @QQLe(h\eE)rfnłM0}ٿ^i+N(0bj4ʢve?\ '_yKz]bIP%W(TYTSR~h;Y8Q$+BBIIRS,@_T\T (Ģ 圤J *-z) 2VP ` Eʔe,_hE)flEd{ HrDWa*zju7cr6Hl'[0z)RKHU@QL9"K;S_~%&QQԌ{rvXxkO_"bSS,@ AJb3oh4f`0pW}> 5ދ.h1%T=) 5/(& "gg>d}0p|`I83y DD7@ 0;r\2PTnGJ H_Q#$))U\EzdO~QETXuZ^ (xY@~; |>% DL6H Eʔe,_Pi3zNt5D\E Tf8帕J+3y Š(+J!] SeܴkQt BbtF ˱ %%IQLyPH9{ xޚbK!] Se &l(ɭ=fJŒŔ* I+P#{ )Ḃ]dv!a)I!] Sej tl$uҀ@ fjt($CQQ`KRS,&cRΞĹ@SY\}mM+AQV|7B~eQ2c(ja2@ti;BփQs,BBIIRS,&cRΞĹ@SY.bʔPb$,x)W+S>v* ~@6s̲;X!a)I!bQ՝],;@Q˦~@Ag:jru(xY@~0/ @Jg'o(B\Ų՟l@ti\0/ :($Jd/IX@*P%EH()IbJ< PA CHTPPCfˉo+ڕ)@Q ;Y(`MЋ!!P4"ۘAf@ Ф JB(ֹ2LREM(0t F#J!] Se*ɁBlTfW-lCRe!DuД QrZHxP)TYRS0]=$))^ PA/(2U_(jJGRe!)X@Q/TB:iI*&&Ǵ DL6H Eʔe,_&,CU'Sec3ė>iٜ(ƧNDZة2PNe/(fny ^ a敮>a*J!] Se :`=zHRS,&cRΞ(2(HRS,&cRΞĹ@SY!IQL $+6v=Vk[$EHX,'bFRȯ,ǗeԎ(^jK(HAg5c փ@ HXl#!PH" |!` Mלr"o ʢve2PNe/ YO g#E_c ʱ( %%IQLyPH9{MeA=DX\}%JRS,&cRΞ(2CeR;Ң_E5eI| KMU[!9VIˉk+ڕTcB~Q89P*%&Ӆ tjˉo+ڕ)@Q ;Y LZMWSZґ(TYeLzdO~@0zE_IbTP LA$E1b29_@!I 4dV|!ex#)W+S>v* ~O!%sBPBP6pf}b.by6tB1F}!xgvq'YLbPȯ, :`"Q` _^U yx*"PHiR]fM=)2drB.>9n67g&8aEyYLPȯ,f:F#9<9p IB~@!E[j3x!MGW5eW!JBA D_YּFD:U?]S0T sG'LQX$ ¡h[= Jf\>nlla< (F!\eE)2O .qVi|k((TYL ep3P1=q5vA I^\ H==~Vj G4.Y ``ƴi&!@@@r(G_f}i^o/^jGE{f0*(%@!(ՔH!d=&W!B @K+ޛ7JY9E*==s&.o}VWۗ{U9G{_9o٣k?{5<`=2[>:ˇWi6(Jg={Dƶq_@Gr_E)EBx?M+VcH$x~9ϼwYggߺ*;`:m8_yэxr5!ɤj+dYD)zn=ϯUcm!<(>xƒFgsǶx(u%澌 >{7ɚxf# sO9sg|S&| ~~ֵ=b{aorUuןt톥퓞YQx},(@\S;:Ղx}/" > }ȝZ࿮7;=.]0 ?k3NסĀB,U$GҬ=HyM喍mHwە (x# yP H(bm[ڨBQBʠN؏:}혎Nzl&v2OqtcPnx<^:PH":;%龧Q',.s(@Eoimc5 mh8lېmAkfyW/"˞7|2)PHBcEk=%r|$7 yPPT(zZO ĔO<|7.ٙAkn:񂫏%LNo[qG[/S2bRhz/K]v<{ՈTdH! #:cd6[&{{W\6/~Ȗ޵i]{Nxx<ۿ3Ęl4e|7eu{cw\`dI!Lqa.zZMsddbl;ǵR*hE  >  > LϛJZYNd~*T@S@ (B\MAӏ](DPHI@!t TV3ٻ$!Pؚ,vh&J cX8 ~}^ZbyPHG@!tQ (+G/Jenjioأ?>ZsENo4VuO:<PHG@!tT+%9at=g|ӻ4Uq虿h;+{|E/Ѥ3Zp (((@1(tuxݯ]y~=(N-7SA[dVk鞿 E1sa' jdBA { 'Eu]X48=Sz}~Za_x/2=b؞e`{Z 6~k@4w6 f԰FTF5t9G(5  Yz] EK4GkAEq au#O[c\tӒp˷䝁 ȘF|YrAۖvb8NF=W7*3/ Xx@W9F8 %Vb' R]w Q G0R-h%t%(9>qYpZU(Ê#P*J*3 tcHΠX8,f(((bPڨz}hئ?}>vmnj.ō/@l$B R ! A> Е;.糚*Z(((Q;D)FSU-cmՁ%GItsʽmn 8IRgP)*NSMPsTjleT>XV A*lJcP(tՔLv=wT¾;?]ħه '3փ F / x,@8vc(v%q*RA1?@@RMd(h@Hu[}/mkBL3|9: ǎ0AuσQ P>$(ȠEWtFyϸmɜK(TA1n8h9Q) @!(ĕ:.Ee?aSՒJѷ?Ȏ/Sq‘9܀] *I@@@!`3W8DMIrT,&E8;05>kqWW$Wx8\ߩ(((G)Dւb&S8*2̸A}"N[`Re ?ԯ5ZAl >9#?L0áTrՙH$,#L<'*%[a ,T'bx@@@SKxhgً᥉o9`0t8Ԡ?T9 J)9.~x)'\;/ @@@!_htB/UZZQys砷~Wo4y.fIŃ& Oʋ%k.me=xdm&5ͼ#{{BVzڳM_@dl @)̙}> Z`4bo:.[7]\^Q &V㶃>jUQ\cNkqO<ןUUh8l z=ܘCTAmVzn=ϯUc" _P,1a2e(sX e랰i}SӫZuOOM<)M!,E(&%/" ENjVDǀ3F@,~Gtf>^*R%04eFH4:c4B=|K }7=pƖ[6~eD<SB~!Y .EsgL}[@6**EVCʠN_Q䰤riAgx,FG-*Kb 7z#ϡX@@d nstv+v-+ LN~!ApERTS"RVQ51bo?ihGaۆl ZSEK=1' 45[1:fd "0?(t>  G>ٳ(& Ekl1m~"Gg=J (_PC}UET^V䓦KO<|7.ٙ]v8~uJB9!!,@t:g糍6>ps8ag1Q$!$ U89tޞڝ%zj߷~=]~;"ՙMK%u:.d0$#!&.lv:'_j\?ԈX"Q; OӁD QrP}g>9~lB)X@)a]H!: k۳͓oءow;w3G~$}jQ?G?NO$N$KT Y])N>u j1f@ QlBBleSTʾA #B߲Vxug˪Qՠ l2>`pHPŒӑ('";7)’RfsTm(Tz%^A(&PbURf?DH 沲(.ˆ2"02WknHQ_RfsTM( O򼍘;w3v͛OjV\Q3!ׯ\RfKp\I̴){b&Qr_mYRu?QHEQ!Ei1GՆDXڏ^re@`<> ") r$ZU\ b.|#{I61~)ꋢ6BcEzC+Fmm/_zɒ%.7ڰfDyh )JN:.SᔙI1h#,f+,!REmٯU &5:&z]WC:~ȯV:>|j1SD0R ˶e*C 9FCBlR{e"[)ꋢ6Bc 1|W.q.Y|O~ruܴr1vT>Ra B)uZd nBX^+D!REm%ۯU e]zzEsFq_g WwO75"Bʠб:zϒc q9ANu[F _)rʒ96d{&ze.aM. Ģi) -k׮,˖i͠XȥlYr1L _Syшe@"AidCj^ߡ#z9ZIQ_)i ~E9)P=%@Q5dǻqmO|}+P?%f4U"[~ˆ/з^ٲ*5rT5(het[bXIyv9x_ܦBleSTʾA #B߲Vxug˪QUPrDa6~ 8y>EIL# )2" 0 )-W܋A_Rd96Ter! RNj5(j#0x BP(T0I]Gdd 68N{+ER[}_Ho^}!zU bd@>D"}R65oI>Wk\E8t ZR':FlB @ @ 0]6LZ@0:9~ǠЇ]G'uvzLfΊFÑH8F!I9OB #B߲VHaR[֪)z^ǏGփxw:7 aɂ@haOǂ)Є>K#׿  ;`,@*ǎ_i?`0cda>/X8nssbƹ8zfHv6p/@ @ @ PhU=(#8tc\'xH $E>-ࡺ+Jw< `|>{S}ݜM$B\l1)))< S[G0v޽$8q~KPgT,WHTN_Bv֭PsS֭ J&23 F͈ 2N˹$>xp ^P"ѰQ!խ i.)))4">XfAwzR45#QgGe8ۗH$O˟yz4x©#ȱ{á(:dZˢ$%&)qIXR R R y(P';ݤdhTs?H?TVR |AMQg>Cp8 )V1ZƊ*?uDFUݪ52jdj=#R R R&Ee]vmڴ>Kwu`Awٍ!Gڳg췷*%ENU`縸)~Dd%.?%U+fV@ @ @ Pj|8}~… !oIu*sòF :ty=8@X,U1:"0gL9"c7Hsq9&9QJ$SM.!R R R &o< BPz&zu޽ԬeSg9%[9͖/զ$ k2E5Q(}Zc[抢y뒲$MrҡU"z='N|DQB=ә{pAwS6\6! z*`9.Ն[R^rB }iYQVekQE~r46狓*sW%odߡ>JHhZc>ȩEs5reGt'X`FjNAH,WS'6JW4ARzY|9I/~P\}Umc[!Gz{TUQWw:#=GKI|9VQtp^#kjsΙc_˧MeE}Mm)Qt6lx!BsSgΣƏ$ͫneڐ={3w}_~|koW,_tl6Nf͚vLr-=د ?׀NQޣ 뮹`XOrҡ_R3{BR^S?k)_?ʛ4{KKo$̰ՀvzG<6l .N(Bi<ѕH7񂫻4E+/+? y7/~L? <=22|Cn~,1l* Ƒ^(,pFcۆ{i=kI%/kik ϰ:rxʴi}}idR?w^cs jhh8_e!D6]hyd .[~b1{8]ȩS]M/]:xH) -k׮Բ){{+CdYYy?cp8(޳}峯=veK,>\ įK¤kUNǛJUa:QLw=xHM߿kǎqMԓOEڹ}[ f)/l /ljdLm!݁v[bƴ~Pב^]=M-jO`lK/22sU)ܶr2B{,#9),p UwlZR*(ikz .eN~βyp:, qcL=r/>NޗBn LkOqmO|}+Pd^5(*%/з5(q0G[֪sTqzѳ NGKssuuxzNO/a ;f7tt$-;nW9ǭu:-MMˡe(*%/зe(,p0G[֪.sTC,VpeͶ,~N^AQ_Ho^t|Y纨=o9Ërv=HRWI-( # RKmQՖu~ kPT5G_x< BPnV![68[HQHŽq k\HQm)M㈢hs8;:':xQ1HU"<~KLj")#EA8Sm)ʼncGD)R E(%YDápB"+$ؖV)j)ɓ#᠙)85 G7拖%):a7_(O44z ^y$$)xCі>5 ,F{|>KxEQb)f;j{_?/Yf^#@3F."(2)j'E#IR E)44(r%_[G^;m?m0SB[yA PM+0q] RTPX7T0N^dzƉnzOvW͸Ż?pt?0<)*+kPft˪'C\;TSopFl~g/_Ȃ>_g i^HQYYw7_FXWK $Y`xHVEY}A_sȚj )LBXPA SA W[qs̿bof|%@{~ՄAAP1,@a)T!Ee ^*'+Wxw=ͼ;mR QJWSHa )#.z/\2eÞ(gY\k?k?(*sޗ~4OL~R }v=^BՖ =Hau)!aPZDQY3,vny hYioܸq$eYW,RV.bۅ zcfhz{R)q] E-e= f$p';FgT7p791cP%ulzt!(U)f7i#f]ष m3)LNa)RT,I2ŻqmOXvM06ay=moҧ)-} k\HQKY"t!Yg)tW:昉_"*nnw BL]l˻ ۚROpVXBP3\OD.v?~"$P( *EhTn߱e˶^uDog;yP( ʩt=4ӄϝ%7g$Q( :W,[f=;KHz=o}|1lxlCo2s( BL.i {h3IENDB`muse-4.0.0/src/doc/html/split/documentation/mixer_with_one_input.png000066400000000000000000000752001404105311000257260ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1 tEXtTitleMixer A" IDATxw?ھ{۾slLhC L@!!R~Tɛ Ho-;T1.wWJcW;o43j<3̬?'mذ*Nn9 /}rœ+DIiK1H:ù" ^gVuu1ͮ7}Bڳ<P[ȫmͪvrkpɶ&qJh ̦EQ4f lIOUBHQ$„qiwt yiL<[$/*7ۀm ɔIQ S3݇Sʮ4]])JL@*$S{$R;[Pc܂e.Q; Ӌ,8NB}}}:VE[k{afaV[+[ZZ.Vi B/Z_:Aq#d A"(B@oo_:30Ҷ{{eG!8r3"a;Boo/t!a3p9]R~U֐[Kl֍Z[7hьp^(݋ge! bttt0aB-&ON"x@mmmljcfƂIDpl :H-)yw#HDI>BjuZhPsUNwqts(mmTUV0ٶLyE+1θq㨫hPnZillmnXNj(ȲŽ;(--rpqH80UOiv6U2eoշzz ;?ۺjwV^~>>]s /ݥH8BΝL>b"(pH8R\R”hll" kKiEиsgTnb:dۤ/Su@D%Gw""_:ZPDFLBwn(., ^bv{Dhhh`: mQq1&NH8eHƦFNܔ2mtvDLnP&#v60s,JJ m1sl[MHja֬^x_~nyz&W{<ނRxi( ,,F2ML:!  DF#! !ISNeG\5v C0QːNQhy+Oy3%D͟HK6'^CYfTm2HeT+lPv߇auiksϿ] .;gr z}׭]khѭ.7&>$PP+ Kgi@ i::K`jDN;4$o3!;g.o`2mgL"2--TTVF=}TVVڬ1e׶Z1s0rd(Yћ \?fRB;嘽Ag\&2U}mrEDz̙;l{8!7Ugp™̝5/^*B%"GrG](i $msNv]͛϶dY6yYimm"<補sםwf*Z4& ۭTVVm_n:o [oW\]]]Ck[+z])0MIoP9s;g7gݺu,&8χo~wfZgم& gj[lD^:DI]}"L  5k?"CzZBTY ֊LoeYw BDw-5Xaɕ1+G[y?pn7qu;<=?j ٲT^4o.H׷Aڟsw]o.'"/.c.Դ2]g{`Whޞ.et϶%]P"+t}ox#<sOV ʍ9\թٰa8,_|b.\;J(-9׃[(T;p9Cw3^*FQh$L4:~m"ш/DD#]ytq5nUM*)dUBttfqwbݿg3ݹ{"S+5w'6R͏y# v,K9gwM{y1GVʦP"-(`B_~@Y-.b~^h'ʯ*$Ϡrg8Ţ)*.%m\~DcQڬgF>[A4A~n6.8>|!8h4,.]]DB, 4662PttilqYe;, ա$2E[B !P`9`9|y35vG"ʼnbYՅ}'_Lj%3OkZZ, 0!,1q*3/B ^C/Sqw8~x<> ,&PG VU <m0pfl=09f1g`WoJJJ&.u}vg?[wimλ06RZ\=?ڎLˢYF"\u,{I6}9W\~yLLj%fe-9a#gv~BOš;wo`ݺ̨ V]) ]n|?;Q9}[7e)BSS~ǭ{X;T]a4 FFLܩV*}Jd@ vQv" SQg+P!}&KHIb~˟D?7oIkB AZ?z7aa,{otvˬz5c). ˍ(ygSvغ39λٱ} ]V3'x<|~m9k_eo%ذ?8[Z?n۴Xn:w}7+W~}>Dyy>[>Ҷ DxG3k6֭O4=,:Lj&3ϓpPVZp:]9GRd횵p8t22W ET"-:u0"ԯ\ŧ@pə d:/ׯ>]})l /^yōxmVvl[ʏ7%^GRfZ8<[|0ϡg\i\]8N*+x'x~xJ tBݪ,ﴧZLzV_dؚe MzxR͉k90vb"3fϳfMfnβG;_QLZx>D$q^/X"zp<Ĺ.;1JhDF∅*߉ADDi6߇#G(]j+,l߶>ܹsL5,އ=+i;.S+>SnVYh!)٭WLvg'P1o9sR;|f'켃f'Kp%_b_ᠣNCV_"NÎ455&&\S3nuTVrMD5"Kd4+o3eV25^jN*!DvX(/V\X4J[{;ill+H4vQ\\̸q㩭KLu.q8mmm7Գ`X<墨qjP[KyyJ\FWv8mhhn8n"jjj02\.W?)q1pw[`B?]Z3Hr)S .~?=L!N&F:OFRTO!X8i!!ҢK)Tq"TC-K?5yzJ kV˥;3(T +gչRNJ_(x.~BnwW2wdC}r %t(((Nno*L )s*w*~BěΗ>|<\ve[ÿKm Ț?tĽ«?fa7g매8LX7Rga2AL4Mt"@QÄ5"uMI:Svq+5{R) O8&xq88ßF+;x;M'w>cG_uao|gy;.*?e]nJ.a7龜5yqT?w%W31Z6G_sf5@sدE'iK ..V}0_g'o]\w ;6Bi|%' A}>>2x̏筏"Ѭֶۘ*?.yuU]B6rN"<m"*HVbH:~W Rk41ܯ_$/S|{ë[]='{pQ fvt0OcQ|k|l1_ƺV%!ZZW(ݏ' 1Ә׾ g5yw p簯cO}ݬywQOc'P]G:>y2@UfTDP9EN YbS]֟~:}0q LtAJS": *t"DӋP^KKo 'ߚRL$%##zS+wA!w5V kj"ұF"ɭtP'4ERGX_sӸ_eva󍧴6_Q4vj~ G횺L{G^Ȕ=R"TθNVj}h{WTua.wf'|MU m j IDAT -8ଊXXYwbaev&uQDuK/ᴒҿ&zqB-x?'Z(P>& qUl|) 25qop>5 IJ_d[,JOD8 >p,}I ^ zsb? m$R8j8khfny_%iRD:f4CCT6s]RH>ZǦcE!XTusм}]Dϡ>L%KS"_8W¤ص"3-ɾ{ݿ?S2tW9xќ)oߜa!P gp̅2[d˯kPQ9.o~jjoGMMroq p߾˩Q@qRYx Dfբ%x#\ɷl2k? _ΣHA+/qst_څlav's=,RDm叿8h]р͇o>zq x~2?!gG[8kB']:"0 j39@^;fw&g0t`k 7}^R8G5(F_cWL:E>iG t^c+ +٫|6$SN/)6' +h,7]]I8 㸓kʽK(Ug8EeI([[\39^/Aส ilw)bH=%;TOayFBܻnth?6>n7mo݂n:2xs_AOo%tor0x7""r!ƣnf_"?}#Ղza1^߭4G7iO~cOw;b]֔ oO;.xp]l} ύ1}U嚋z*eAyaء@me1^6AsUpIxUs{i R?^y(wޔt#@@m묧[WA©߸n{29=I.'%L5mqbB7B{jYT[;`)s>o=R7HTqzyC#ޙ̺5WPx̯_ϺTGA4;Tx^pV87H 9Fjx^<HX&|ReRN)^u\s|wppD/uM܋UK?Rq?ob7nkzcf13\X!gz(Uۖm+ɧ"ЧU+%3RcW0k~vGqԢ)@n_8{ mf{OW?5KJypK-f޷doY L|lY;n;hjJ~0˾`^MK5o=MrH LFr* 5!^m$;65 Ԫ[/ZJ1Cl[CwQ#G7d}}̸io|ϮTwDW!)*r+9LoEx اɶDܟ{nwxwoTe}d=׿%޵,x{VBr]iy*ʜyDaZJibqܕ=LUl!% bPy\bN$ǓO =qG/XT"6!0N 'G~aGTiæ8 :PRTgo/Ea2 M|*Kj+z@r: h\>7.uWQ&𻑐jnfrEr8=b7xgW_ˈT]%d||:iOo<#1X#;JϥmyTy35ȑn+`Po&:g1RNiWc WWlJ(,d}%G{=x/zލs[mrrB/E=g8fcOf[W=R W o렙"ί*Uhg% c< VRiic;ά*D fgA> &Q5 ɼ3!9%UhYK 1Oac=TA kx2^:#o4}SJ=8]=!vl,R<"o|9>Nc-y5G-0,M%[XDT1smPb nAz2kWsn)B 6SOJzn6v=,kԶl௛B)xܰl;!}Mqªx/64֖lEQ{/"ԯ],Y/un EC<%W3 0L)XG\w+w7jsιsx>7QUDwy띗؂|~$το{wz>iWO}}@$Fߦf>:DpEFVTFCaZSB3]8iZ"b?`h$C(B4AP Jl$$'G2R*cAm@ j7oE)uUQ$qD?W}&eLP"x kǽ[ާEh Vixwk[7j+*'zFBz8b +-U{Qz7QL8w_-䕇/_YBWw|/p+\r.aQx 7„'uUs"?ŻXqI \D۾û3k;Ǣt^ ' E P@K97/ r&6^)kq_Lgx!ӎyj&8a\!~G 1-|rZ*^IͦH_õ+i{3bd"cjz YEE8Dw0G^>{AH>!*fF-,WG],=WB4ud"D :ճ6D$pQYNP@)1'UWtXx p ''b0;xUgv]5[a8w@Ĉ'_SͻNN8FBX"!E!B`@Y :o9CɤIiߙ,1I8  ' sʇӳ1ٵrTI5CΞޗW>?<(gO)?M[X$N: 7*ԝP+Pt'o2Z6׍dDAQr)xtoR>00xMOA$%0TM>`<#X0?\u)kvIV̯˄DkZ*ӿWƎ'i Jq2Bo89|H) ) +ڪI5ui'_\rwwUv8 #0WoOޓMwxX,/3V(o?V ]>ùj$grG(zp=?̍=UU7tDdfG w6l|kZV{G;׭/{RG_HC7o>ee66RE[nPp(^^yjB$pP[Wñg.Eh<ƶ[kœ]i.!nҤ=پmV7P#>RHɡ<_LJӑXr f5Ápt8ML oXCψ#w0 ]yl0`GE'Pn'qaFo)Y͇6Z$l}g-Bye4}<9f6==+Y>镈vWri_YJ'Lk}ߏ{+;^-]9wD<^cf|e)Go |haDRRy:NNM;88T8G6 ;@cpt] %K߱ͽD58av+}`^㩘EjW#Di]OL)p&c]DP[7yrnw_;RX N~\N7e^[H5HNq75֍[y-l>w. =WP5u _Y\-S$GB!´uqL+/p2n\ϼegGV]E9Nl>$EePddYNrrYF6ci Ӌpp2͛˂m|0|ʑ--ݰC~]Ex'ru_[2Tvg 8D؄qRV?}wDw"-FȲ~ -l>Hi*nH 59.be!\L9\(ѧJ݄]uݠ`͇&Hmx 58|i>Ji@zaıAu|ha@JR.@~x<ΖI.>԰b,ՀFiI)FP:;;C6JJm>4]|>4Bt5aE_(D_C ۳6F"~KJ~(tuu|;;n%%cEe{yCWQ^A}_}^|WTW/6*$! %C -v-l>vH`, wB6c\39"%Z|ha󑄽eD2 j|haEzW"’pyf0_߱Gy{ˀAC -,$<-l>{Q{I6Z|$*e}^͇6) r6 |haEr.e@a͇m̺)!1$\ dT3D^ --^"͇ 6ݳ6Fn%[h?azI, ۓ[d`oE|׏!8V[hĊj%Z|ha@o, -l>^͇6Z8 pEiX.J6))>rL汰$<|haE%Z|ha`׃K=l>F^͇6)fr{I6Z|`o0$C}hu͇c\svS{qL, 6Z|hw^"͇Z=h/C {ˀ$>*e98c6ZwJv'- 2@%[h/4ceIU|ha󡅽e aGBe@>{Kµ#w%Z|haEޝJ{I͇͇u$?4c3ƘYn6Z|$ ԳyuXYnur͇c\}ހ %7y|ɡf밗ka͇ykaGmi/ C -cX -l>ƣ6Z|hau&ĒpaME0e\.fΘF}CD~~|hcǎ=z|`yoEu53OrntcL2~|haf&x:erHx)x}8]N+Ʒy ry'nO͇6H¥H/Cw=fYp\\d@"N(,ar3G͇#T)kMXgg'>^ ,Z1$ =k%%Yb>L7|UrYu$((a؂ߞ,ttvv ۽SFAIQMlßR\ gq'd3FtzY!ʀ~>f Bc LPCO&fsK K?vQxQMj}u ?Rx' ]O3_EQDpa]2R吟pEsh}|Ϲr7_cbc|LJf/W_zit;d IDATX.\a\C)"¦oz!92S;"JfoOWVGvm>lIy>I.#2N'eɧZr0GyS]NmuFN&V݆|?ɚh_H{D^֖}-l>=gӡGI.dY6||G o_oCe:IBB{oExs^@a-_NAA!bGj F-`B,y'N[okniꯇ,Nx,$~͛7̓OϾRUYe%yW^~IL?tִ͇6ȼx,륪4< GF#ĺ:v44s P;61[ l>|,D8p8ޭ"STTT;|h1I,e7cֆh $IJ=У>T&VsH39 /# ca8"#Azxvv2c1bXxos M.ؼڲj/>IAog/bۍ͚=lF 81=X~4vҖ6m$>r"&OFގp]b0~7a͇dkf`vF>҃#l0β;}#+PYUjW`P-F P"&6BAeZ -+i0l>,|+1d߆>]NBQQEST\DQQ1OQzC`0H8MO_`7 Ȳ?~ m>l>r'C\|9G,[< ja͇6Zh?4[@Kk jܶZ̧lǓF5ᴕml> 1B0tL!*En]b YW?Lbv=vl>-|軈.q"t͡_ vGc"˝M+4a$ÚݑG/* btvl>e|A4|.@-l>oax(..6XTMww`0{shaaL?͇$`8o<ǡ9Kaa#㗚@Jyo8(49Lm=X|X;3JsB`#HaU/͇݃%#\ K-UVVZ/^fm>0FR`_q([6L4 -|haG"`ǏQTTUi7@wwzz ñEtw'ݜq܋ٲȇHY۹AxpxYch4WJ[V>p|X9k&Ǯ@Sq?KrѰ]b1>4AikwCGtͥ z\iݡe%͇u#,{s%)#g6P*CikwCGj&Lj"wl>|f~i lKahce?1E4l859Lae%͇U#KO ^T aU͇6R ELp=ᰕCse)w@z~Q7Z%W0Tp6m$>.bvl> |ѣ.pm7w%l> ||9:8r|Xe9O%Qđ4C N[ 6Z>";  Cl-FX.ݹy*9 *blYIaaCNqSl#* ؠj elYI@aaHB1]F\ц= ARE fJ6G6M؈kFÊ] -F.j~sL;}ns$ad/ 0HlTf=iUe%͇e#MXz0vl>m|س-]bz7o)#I{bfEacD<CWs\ꢲ(sss;:~TRJw2ϼ2IW>yy<>gi6:ɲlB:el)<>1K պFVz3tߧ iҫD"Uȴ>mx|8NpìNEက܂G9f-׋86ߧi6-7UJ#r 5|̃MAIxp MoN%ֻ j7j2FyDŽJ.|nqq#r m|LҋXD)<25ߧiP-W@'^ڛ7ٲum43F11YBA2u&ζ(l8p[(l`r m|fӷ<~TɾFz33nRZN|6<>|TCuEDBSQ3%p [´lAȴ?x̖ (M됅5n]Xϴn)iULKsz\RH8;S5:5BDAw}iUG9<>tG.؈9tI7t!ݵsHph/?Ǭ[Դ(vrbtv_E\*l|  퀹q/ ~3zA7qPM0e rD"ɖ[y '1]gayI.>hQ$_y|c"rf;x'j*Bp3L8Bo׮eQ{=XYI 3N lr$M7N#l qzQlpWV47vJ@QL5TNAWpwM4уgS^{M]Gb1fr|TSBɡ]I-$gOp߆Eξ?"mchyy BǮy/w=o 8Eld7dپ6 om:Fh<˩7__L>ʛFy.?ؽ~i5r2>juӗP1 )Ǧ~kKOpYI4j&%>DGu@2gq`͟?n8:{~wS;'2Oczx|$ >XyO7>DŽ^4LR/?E| JmGym_$_BD=Bn9.<>qQ28(J_~n$'m\a,oOz^}8%?t1"H_/×y9\阢oF4^y885quN/ ."\5wNDIj:c#l {v(';=ПN )"~gh_*`BC[`@AW㍂(wQ}@S}.n4{|4$98ץf3<.1\AE:{"!|iY24dT"N&gU([|4m1!̙L:Ůe8y/C1"mD:HN1pҁY޹,Y(k}{WUJblDi!I&Gϫ5ٷN!"V_x[^t2A: #`j(GKQS^eojvq#.>&!jiy$3W=ugxy|שM9^6E~R&/YM!{]i%IAKg wvB۽ ]cF:.`(~ AGi8 m/c ͤ˹χ:d=7^ٓƐM#jc>%W ~@ ȣQK4E0M 0tZI iJ?{,‘(CXe4 tMCu Ø [q^ d)L͊RG*V!Zhn1[:H7 e6$yIZ(G+!OL!WǧHl:.c^"0+ k¢V(r<%բдri<]711 Ѫ $Z%iD`l%x|(uc;(PCv("NMY8ބ.r,gRj. t єpW)a@^ [M$&>N뎦x|x|L,Lm6;$:::^VFY,9dxh&"mөߚ@#hݫ0^%۟ (g޵|^%P{9G9<>8u8sǝp"Vnڎ9`ܳ\|x"I8%YSA>t+&/#sYX"Alzrx|TP9Xl wp& "mH%FG4 ]5M5 5zV׬M^h:F\*qF6E81#FeLlxMMd#.2$T]޻Ξ>DD :BTӴcY{c8p39w۹dnأ^ͻY[ץpݽcߋөgi춏%IJ&P"%W񥍗RŒJ׍BieWUr1;wh<1> ;`]Fku'k `qz< Y$R4 l'f_ɴޭ@/!k:dž={Py-\>*U;('Q UUuq4ȥ#mϳmd $fޅ8\KbnC'ˑddgLJǤN8:2 (,-Y ˶#ysPsldxRz^EϫQB ؾ*Hki9MhPd(i=Uwlw}oy./^|+XτܭinDe0&%PH>*j. VJMNG{;/sLLJLJXK/CKU^x `vPǒe+il܎3:_y={SvIgHbNeeU_㻉<-?n]LbwtZ$!`2O`AY';A ܅sUuWQ (Bvj`4`(|>$p]'O$Cu͟Ǣ 7%l:hH7:F*&ˡi!%H,c\gvn13X FEI8Qk=a^6|h&m>ܶjm RNi!/Zdwj6 SycVr4 n}|h#,[L&E:trYݐ$e+W҆hZy?n]mp#N}䒉 ,&NÛhH_Z^%Nh!$s n}OǪR'PAcS^;/zk U%N< )tm:f3J$6Sf04ɪ9(Ld2I(d1 0 tRmBV͕i2 Zzq#&K][6Z9k>r?a\@Ĵ{G;o2:0iܸ{# {x|XhZ4qwskY~#eŽy~]䲹i\n1UÎaِc"[fvɭҥK,?4Xt)v;;vl|Ժ0 +jژ` s6(It=qDf[PU3OmxN/hеŢyPUov 1+6cm͈bIq rXLҪ0.NIDATn:.\ȩ$Iխa Iz .dݺu5Ӛ-pZda<+;c"*(}^+i ΞjrGk2,zfx|LD]{4׊??poxXP|>uC>yϪdpZ b;CzLRXya!⥗\apprq駑SUyG\zE|?*$J^I  D,9'S#="N~b_OW_Q~q-\NᔓNǟࡇehh(}==5Ӛ-pZ|G X?Ha,;xO>2 zt9W%{;n?=\}G8=D>D|LuM(>P&T#~qLk}E翗N>5YSO.8<.z{c|3hXԏ{!D8Z&# l>y|SCJ0о[ nQ71]!ڻy-!ӱ3S8< 6yLEڢ1~8(:W z7<{Ӥo`n}Ϣ@x!Pysk>F"tw0wPp \|mnڻ4; DA $[Y#( p|mn}{EA@  (JB %:m+QQ8]#mpWzpq'g ؽI 헮֮ϭyҾV3TҦ%hooE-~Bw`O,NEAi{5^[qJ<>ZP[GU>Jq &]BP+W( >χG${iJ>GUU{3͛7eP(DgW';vǦ ì^ںYF$;3(B! Nf<ThS葑DIDDg($SȊiY=ͨ#<>Z:Gn)LcG$! |$I躎d2r\pN @(;YQ@ ,ˈwhp8L>gΝ9ҜЦ,!|> AA`" d&)(8u(;bhKW IDQB] y4T I&WKcP(D4%E0`0H,C6l؀ ]+rP h%a42>'Gkvӛ3KQdYb fIRttte4QUuGut]G4dppN:UVŐeBİC[ֽR&Zbkb,/"CCCtuuٽ4vie$֫& 5\@(0 O<{!ڍbWVA@A l |BҢ!$vBx|2v LbymSb5(lڴ[{ng,%GګFeZMy;T}K4M4m (D:/}# 4 LI:Bk[J3UQh>ɾfsK$Ib׮]l޼\.W6No`bzmIѶay^yTUe pgswd2d2˷h/LR4;m" ~ su MG Y3[c?u>nQҋ fN1!t d'qh^uLGu| $fMju~'xr$ItsB" DW DUGM(/-@G۔HiLii #W(gy|TwNXe&G=ˑdiRc=IfhF6EUU46cҎ*uq(ݜtI(bhy; cccVFi;Kj5ic% ð{|4M# kr9;|-֢Ź6QhB@"H#ofnbCaFF' mz|)G5 tvvJ$Rt7c`X7?~o(NL$Hف ZdL&_a[X-H$B栆vOA& @Qٴ(޺i=\ |8j=i 3ooMiFA  #{*a5O?RBBQ |@!L&Ws9–pWmZRxPsY\sDkmaVmp۟BxmmqēTZ&,?nۯmMD]39Ž& ;KjZ.r (u mGm?n/P": Y5M#" iVL *%yڞ_ǡ_$+~׈XU˳yjƎ:km> m6:C+Qy hh"IT*EI4$E Kj<Ñ/mZˊ‘ygU[D(Lؗu;E]q@d2YuH`T "a ;9f^{ Iۓ*!86&G*^ Ռ޽;wU=/㎻;4~D}zَ5o|v~eӹM}ͷL+>Jܪi:|T}->}xZ|4:͔?v͌G1OJ9*Yɇˎ*GPV-[o:K;{}o})Tm;⼉apN.9 ~sö]|/q'f1ӗFvpe|8 3s>J|f .5XÜ}λʎ}|/_7`h]3urtwwH$XSgqNjKq4fq6>13ΔjiğQ?ԙغ\'O>Uݛ>u~Y^뛞h_]UQmo]o>DzkN:xIt|T9O=(;w\·k^>o~};pj>fQ󑫘t6K/]!ˏyEQ-<Dx2?%_eO} "}~Wn))4(䣨A'j`oyˉt>/XNjس{ʐMjT7S7.d\e(xc;<"y I뮻&<>1EtDC,%JuY9dR^}e4]A1e£S HbajP UU18ʋjjPx|`壶&ǚ`kP 5ṘcMZ£B 3}vҧ O]1m&6LȪ/yԽyD~F{CQMS8˹C/"lffsqrx||EӋVBysG"É`7s׾mpϲcfnmCdbՇB M }]A%_&Dž8J.xmfa&0R3]>=)t]/]mbV7 ӊuGg(D8C2eMy mvg ^9ͽHFՖiB)EԩѬF_Eu+Dg^Joa薺cO(DXm0v?(J!MZkQ=G_q4`y\1"GӼZ ZU L~!D|]E¡ϯ}4ZJ0Hp(TC㣈E,j.m7^K̿; cF{{kjPx|x|Mbvujikn{ih$BOOshPx|qQk֥"JEU\yncz-5gJL̩{h5 Q0K`ղcT5?Y"LxA"D\,Vo҈Z ͣ*[.;("gBŹ0L \xtnP9wO=xUHIGɞ=HVd21~=RWR{7j|*<֭g( "]b^C8##@ӧO(nÁN "Wy*2_ikcmR'(>0E&a Dy%t ?cf8*קZcH[幭{XTx)T'}\قYgX?U7~N`IENDB`muse-4.0.0/src/doc/html/split/documentation/mixer_with_one_input_buttons.png000066400000000000000000000211711404105311000275020ustar00rootroot00000000000000PNG  IHDRب5sBITO pHYs+tEXtDescriptionWindow Class: muse2l1 tEXtTitleMixer A" IDATxyU}LϹ.{+ (G<@BxgQ_WA|bE9"x  & Sw(GRcy4MD5XqVȬіF|$mdw`1;(@(llʳRL,,;eQ67-LeՋRw>fw `O:kKVus\@:~ӷç'&d+Id%k}}`kϿS?9cƉ=;lMWcts%uz4O!68G+|/F[Y,aN(Ig{TVVq| ?u|Dizzܲlqpq _β? CoaY;2]cٲgXPLE{X$b\a5L:s(5Lޞ~ilDw]E #8ӓqblhe&%]e%k.h^q&X*宯; ˜+,pKxAt]BJ0~٧e6F-*Ҳ(B) `H{OeߟVfdw8WI >E+FY,&.nf˻zu USUMUeo?9\Ҋ*WY"t5\&b 1b3xZˬnkyA.E VDbI%k4[Ck%DBA4X> 9skȾ%N&[BJ0 AVӭ-aWu@{״w/~~nu7[_|P7<\z-K)i-uo)`k'VM3ƢmadtVx{ѵ`/gw f˨].)%g\oT*?G\ L]d|њUTUvlM-;#&cl?p|+-GQJi0@0IbFn{KAYbbZEg~ߎ*,_Z#W)p Ut^:M^ 0uzdo>a$1!`2Fid$q16{DׁQ zVblb)_uU#Dk/,,{HS?io41͚;8Ø3(D811aSNJ+kz;;cPBt_1=b),=wZf Oxb`՛W˜mG;.7se |0Pg@,9I5/yJ ^D(maX!H;8.34%s—E]t1) re4gq*#MK,|Ye*HܘTPT! fFPo-H[F#IHS'^a1UjdL*E $ xQq 3:pNA:A6tMY,&bUINt{ay g[bAiCM,{[,CHx'Q*1~URf|t]'DJA lB0wy*GXl hD!=+^J\GdיC% IΒ2ӥcU53Ll \''b( 3F !eKq$KK$DϮ|6Ez7|g~ٟ7{@cS^i|>R8tBOdGNA,^4U #WmG&5 ye4ڶ+bCTڱS$S~5C);9^r1VCkq  }ǺUM8bs,f`Ʀa!bVeЯ펌I d5P0M@$rBA큠retOXl}RcUbݼ |'nƤ"J4Utvޞc=Xl=+}7֤b)NS.pUBT>}Uow5w^܆Պow댆YKZ[ޓĤBDH2ހp$;'chjXQh@ yYiiiɁ-d8c̐܁#aqƲdL`X BD|3CǺw4նL^zFX ?Θ.vNmd1\UʘTdJt; |mGTM=%%WgIc|ڊ~O(& "sdw2~R aX2SY1 `KKJufw4Ld3eZX=t@X $\s_'0 E ~;8񮝺eRf,G,r)k*t/]SAy~Y%^5K>xb(Ņɒ<ϯz.2MUu|X5y&5~TcEry&-qҌʢOuF; KR lp"?KJV̉zǭIg_'f;վmm7z1J?^}֞h-͑r͒bd˜-s@뉖9+Zdٲv8^ܪ۶Ed)mmW>* ]fItuqЈuBjܰ0e1nxٕMMM]s5ec(]suSS3Ϯ<|#&7#%QUb {H8,e1 fiœOz³K`Ǵ==5sN[^Y r!j&,%ojxb-x1ʢr͒}5>XB&njYk~8n|-ż!i4 ƓOR,L>ɿ}<3~Y}ILbfrk= U;Hh̰";?kHS5褩[n+--MdSNs̟cƘ,۶ȾRl,Sj/&GU<`#ZpHD^ejH%a]{?vp9sLy8qdbN0nB`(ڱcGQ%oX̒W?3d>068c@@c!@0`21p$l61Ƅcv:utt?%o9K,3c0b\$9΋N`cُz..B1qV!Q]. E&u5|Z>Kq1`#?ئ ?+ 9ݭ.cm>Byl9FvoOց2LgLgTgP`W7a `DQl;v0n6l3HcI:MY,ʒ v !;Z E !",Lq`[RMݫ{{{ .x5MSEQf\Yc0b!@]ULt`: T0ұB)UUu̙?x Չі "Kf!@?Qf9&{A,L0$LNhN~jE͜9p|P[[kܾB4M4͌}YPY+QJT<Ɂxr몮ڰupM`apI餔۷5B!j{?ܝS-oΛRXh+n~.Kj_}@շۏf_'p 竟 sQs[KlEƤҚ`[,U _ĉlyK}M5mK03Ũv0(铋~,ʝDį~tT5=w-g2^wWۓ4Q]wr8ѹfDZ,TLeWyEM 5ϹG +}3;~tqsa(Ieb)~^ =?8]~>x_[{~`ĵoX矍59Ϙ݉VXy=zHGǑai/U/pWzo#oβo--{W,t+5SOǦjGs,"a1c0УٖGlߵoǵLkwqˮ8^yuC߹ͪNStuv,y'ʫ k`7ޣ)O9,] o?z kN /.BG~z;|s a_YY)6s^x pQ~є-g1HV1luB_{˩{ O7?z`"?/xg%΍nNC/{q{koM6S_{ږR,&ƌ~(=}G 0N)KaD=ׯb}җN7ֽwĚhWc$v)}D,bcl1)nOSV!GkqUb{_N`ߌj&4ZxM>Ѷ,}Dr/:/&v]WI|D,"d1UuBs6)7\SD'$RŲ2Q;,h}BTUW0q]qsX XQMi۽}b.2W"Pwۃ !`433bY\xSl|\}b(DP<Ŭ\:1(2vɘ]pUg6ʏTNn\7ζb`m/&SZH;G -n ,~lmǭzO|{_G\lhϿX(XPs |~ aLz?+s˗NuӶ+/ƶba w(dPl>2Hz32DEbe#(Dd։;tJ)%Z}r5G#af(%  M8%}{.ڰ Sν=dR€1J(sqe%ޅ2ŘzyQTG= pE6wQFኻo0;e$} }D,8*4 FbA;۷٤ "nwرpX X.CaS >oڷ:Q;@ooϖoq#bKјkt^?Hэ׍!r\UUU5I|D,(%GYIDAT[1~{ȵLX,yUj!b}kG$B JbƿQR ++2yR%=_\cۋM'gD%k!r #w |گ>:+8bQAO4a0: b]ws 7MQXQZ z ^Yũ(tǯ9ʣ۫7 ƻ EHA֝9+e1#KS*EKйKHc@iigR"m:#OӶ]6CPRx_[\%%`](`O*\,}(J]~AeNLy 4~vPe鮀M☈@}jiJ3yɽ[y(w?pt!v{͍=Z@,eG('m`ci=mKl6@p HϊtsVe;vr;NvN IENDB`muse-4.0.0/src/doc/html/split/documentation/muse2.png000066400000000000000000002765711404105311000225400ustar00rootroot00000000000000PNG  IHDRN%sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATxw|UǿR& 4)U:(&v]W]^pk[u ]tQwD@zo@(IHf2ɴL{<Dy=wέsy 2qVg)MiJSҔ4)MiJSҔ85vŽmp|A}lv;v[4ȖX'e݂MQܚc?ML%c8dfruH܊nvZ^Q[DcjG>}F ɕUmr1c?jKJ)M,[8CTzym[4g~7!\tP Mi:dfuj ac]eJZYB[KY*oEaHc;ƐLГ :ccO24CƓD %gdkj͜sQKF:04iqS]{y6'@QU B fiJS}? NRUb*f&"&$F䢺j`yH[1(O*c56&4[c%v$+7Ԩh<#WUy:K?,M9ŕSErJxș`=}1͠>P"( H i[Lьԅj=R:(cOz`gdg>xpeK9UӦ8p( 0 \eR$I~*+}HN'~?>?? |lGk.S?2e%S(2js* Y;5`^s,A􀁦d'K#]πAeO1 ѱ ODHzZ YiVu^XZER4qkҮxtd}cav!>鱎B"n/ ۡN|MIYd4Bڹs$C.dOI +g~śYS1RE~?JJ9q\ivj5lHfffJ4|ر׹[]VJt [(`0pSa1@OEYe:νYA)yq`$P)6yvDavjDVVjc05vH@G۳[}l%(47Yna 8@T(y眧#HӯAG'`u?Ț YGvTM|${'@uڄ/K yN;tdhaIGjYW9JdM0PxNAF$jp x0zyf1c5L \2#4!$AY4!zsy~_12}֙T¬:uGkTTx͋<Q#*_%Nl7KiJo~c]e"U[m 6)oMjicTA EٛF<"CKp5DaR uz*Szq˅@csj(U++fpEǎt؁.;йc:w+3=#PNU;ز# .ܝ}ULX/BZzv4쟁z .k"IX8wݕv$0`0sܙC.l66mL'#SlܴѧMkǮMJDI!ט*/?iUҸqcJJ!IRLSu(YL2X3͚WߪkcޛEDxNy@(xnSWAK|< xzb]P;*Պagwc#rQ!ju%`P0,h'`H:8'><ߙh+d\,0Dr!bR)?ZElt!N%.ZKYCC TWQ" +giOI;u 池Kd]_(4\}>mY*T kп0N/9cc8MimOcv**~am)f&qe{þ{ذayQ|<_xvtꚞpQ?ہxStԑM6q&N}]ȮZKqÔᢺZٹc&&+3r)~[Eʣ۠CZL.nS!ݓ 5%{<Br?a֣<~qR|?:Q_CJシ#i䪾W0ʾMY:ѵS'4%B@w) S2M/dd OW9xgKL^|/-TLޗ2Bh[ӣ=:O:/P#N&>^݌r7~̂HQ^z4N('t@Ᏺ~?#ХcojB |+xԑ.:ңZ^^R.]+cGѻs'vΐ '_,P2g(wtz/-=5v {t.]8iY&e(F6tc(`6ɼLvJ #|3"^`RGUI2>QG'tDt3u ЖAk uHHӯAz> ;(/'Pr?H'5w%Ǐq㏙8abvVf&?M6qWd>S׫,0O?@~^L'}6`M 7lDHճtTQZۀ 8YG 3@H@0I .P~~?R8L0btJUÖJxBX!!K:9<, 5U{t"Yf|h1|6Bͣm˧1~\~sQ{'/)H<%xP`AyG'ϺcD@[8k L\ B/>IQlDQqrƄ߸įO$`е͊9dƛVV™۞M՛şelQ!? .ŗ$~JeDWՏ?'|=V80C4Cvq^|}> \]-ydF6nZ'W{l TŒWW~cy#ea|3;M_G"tVSڔ%aX$曆Oe1^T >yjhz r՜]dm K%_+ TuK2,]<v݅).u @H)1NtmI~r￟@0{,%T)Sx7x:t(|!| ~YWUҳWoGדN)DYYEZnG*R0&?U@I:u:u%s0K~ѻ]7PH KNO'j!bj[tcYNlc miC>T<:|uu BmaSۨ˧SN[%*MQ>ĔrCcT/Y=]n=݊NlGx؛GVLmwnb B!41>f}F#ĕO ڱ G-gQmG ȩydE^.]yPWY#_\cPLgm|Ƥ)κ<:'Int2h65m_ H[/F uS:URü;ԯ_kP8>WI'Uc4o֌۶_> {q/ZW'mV;`CG kޙ /rsNtԉ6aFNlSYW=h'Evv6"I!~޹Z'IdggiJo"Rٱ!dHVIMڥ|*2~k `ȧkζ^çk+c>a'TGt,eKDxDh@M"4d$[lLJW$?@c-{Y_5d{K1.[@t+S$[p;pc'c]X$B*UHȟ b/3C6x${%UokOV\- C |noP-󻳙NyZNjnn?$/3tM]/IN]rrAQJ KaP(b!_矲P]P8em=7Nz >aSS`PwTWVTfͼ[w*{vpYJ>ŗ\7; ۻlZc\>Lcv^{=%l@NT,9 ;afϚ)z6Ꜭ# Yf=폱d;Xv-+L|OEp@&kr+9nxÙ _$K7Su!t3EVE2w,:0u$sd tCTVKH4[zdbz<6 )#(EՂɗ( Hņadž454<] zwN6*"#{θ'_^D[Π]T"9=m/P sc ^ν^MsZT3ٽse)z۷lb w]lٸ}Ͽa-:'ΠASvo7h_VyaA:wذ|TSБp)((wZ'uS^nȟW?eVqSPPaHS~$ƻ'GR J(kS^2R _ګ3,,:p2EVEMMQ(2A=X!˵.BDyt0d@bV;46=>yp ZRIB-' wYAG@Q-;1j_{-,u3vI\[u<o|nn>n+>û Mؤ+6"1Bx ?l#~@qqwO%n`6N}sy/㖾0 *1ۙ6}ɉ*<7I U=p3VoOy{xaVv?g-ި)a(Wu @Nky%~u:Ug2U0&h,#ϣ`&5 ՀnE >XtP M'2IVIWO~Trp1 8xHے.|qi')_(޵9_ N X7He2Hʘ9B|cy=l_a6{ľ YY8y@PBddgˇ# úVsٌdde Ց j[lN\.챘""Ȍ-B! a:N& ; @P)R"f$)qf: e9ȐJvg&.{@}bf]W#}ymq\_?ȵoqeftD0E83dfgm5~s(P7G҉hj{u[X6(j:(I6ЀoMGiIc!Ջ'QP4moe /lټҒRڵkG`q1vaA;te Ju DM:qb%=JAH:@NhxvnkOi.*BUھ7>8q#G)oM ^KW]kA~Q}!CxgsmX@?^\ HY[g:`@@*f>%4]:>o;Oᣇs6SC3/dB!JKػoxBNrhҴ ͎oNAANg)M}:1_yQ>~a5H3 V%QT5 x4P\f)NWUNZdT;FG,2p9" `XٜddG8D?ID1-VN$XbHcI0*o "HSH۶l`ؼc?Ih8묔*&!`JUB!N'YYq\7iJAj5pjsdSIkPkB ^k dG6` Ԃ($S:Z',C8h,Ȥ8ꁀ(O29Q:̔HHR5piJSJt$}c;h׾3?P#fAFF_iJP/&D M"Fls`ӽ4JG!1o\,)Eq^&4 ,SëŒ|쥅Uc/TF0P T؄h0r^j@cqS;MMJg|Kd2SScm{n7RVg \@}t <1 'njde@ u伺ڪ4ӑIy00)4)MEP6l9SN5Ⱞ%#Z}*>| IDATVmK_7R76*@Q%ʟ7.X蟚 _BE\Ro*( d=y"?8)Sb':%ฃXsl!(`OөU~SlXE+`N 5_) P[t@ MiMS47K (X uG&B^{Sꕻ"{:76yEE f§"d:'⼪W^Xl M}wxUwIT6ȩmZy=:)9} 层TD䥵Bu +]do,oo"\G"et@芚&u)MqNt` MiS,@Lt:)LM^N6 _Klhڒ bW R4M$}i6}i@ůXR (TPMt*i2~˧C4\*0;W r<a;f>`2),UK5Rp-H_ ^ߚofhݹ 3T$'S'cqISTSJҔ;d9}d-(oBe 5D*N'' O (1N$ (dԆu ~86 n2Y+VX>ZbTnA \ Z@`t0*{ em:k{)MQ$p8L0-w\8v{Ry!!ݎfK*_8N-(y;ȝڔ5/<ȀC95sU&ASLdN\P 'ZWiCgO$P:?)`_#00 $|ә9)!v:`}R~:5:A1^FIy/&1Lrݺ &?3^Tid誗iNbE.E+Ӳ H c(^2ׯ⤱[N)4"Y7=Y&3YtmeWgf0`R0,ӪG/qZ~.210HNU.Q,7+R\Ҕ? ӼEK^O[\#kVrrrir|sB_a4kNvNwndgP(Ĵ6ʼns>MiCQl3Km {45[tw)bq6VjMUz%R*67ƢޚMnXБZղflrr5q狼 Ld;#NkS-dע$v*j?RЩ Ld Fx0j56!?zk9Q-4d[)h3oi+yVtX^aB,MiJ0ee~$)lj)-+#]NPrsJՋz θڎNnǸl sO+o`ډʅQXD&h:$$KOPAґȶGjB~Le <1ܷأZ uWʖ鈷;^R,,bcgk)x cQ­uUƗ|ნsXʟx3ǔi~,}lFZG@Rn}>Ny>Jrz82/]m'mS7Xx# ^Tg*疑73 ;GFj>}|?zt/^KZ TZ]qnFWr~-?k?ΉK${Ygt8ct @Uw0G1%7m⫴9ҡyb֕6@;I'+>+ˊlJXڏiφOQrh u~@r1A*  _h(+:JH%,dOp:D_Ux>Ea#0 ͫ r]FL\ g0lbn 5GNI?SKryf%2s#9lߠs`\ NCھ=TkAG]ں-i/pFĤ7 KbS_zA{>foM;y4~YG//z]3?+7ϭaI_Npڧ)[c\P\4Ӵ@+5́CiO:4Քpm6\N&N`,Z~O'qac8RX˓O=cFٙ2m>!PTI7XT2i]؃?QW,Z˨Gn1R?ͫam5%kߟHٹqǙ]hM~tp3s 91 ZSxG&( '!yg_݃=|Vj 9Ѱ#HI$p_)_.&R<=֙ lSNmnhL&ғ|̽!cCPTd '"N=kgɪ54듞Nխ@Fݗu1 y.##%<ɼչy䭞Ǯ؝p f}D[,S $@d1i!̘i;U6=;Tf_ׄsEz.v;Y9d UzBp%9.a/!v209ٌ0o Pi,RPfjNhؒ* : MQVT 0^| Cilܦ)Mi1G.WN˯p-7z4h0^v. WF 1p-7qRדعs3,Dւa; 7zT۫V2 &+-ꬥM1Ky>bjU:fq⁳bu8¶)Ku]g^kQTgYL(V??/"F^>X]AC*[;w]̨\pk,- 't sQ3.wFl])粶\! ;z+XV!k{ERk'~7jꍼ||+7U.)gVG>^2Uu5 ,?-՜a?K՛g+yݷ{f]]w%g3qiBl%*Mdt: *]gݮ[o'rm9^. .z=%m{*II 1b/.Z@շ~f7^N*Tx1ma V{@eKB DU񏋊8_?Nz}g.7 Qx<xpcQstvY ۏQ׽ȑ?߆^‹eE}g LG 6$1:)Hz`e{0w1KEoZH_dowdl̉<90^x\V؇pԽq‡5#9f* II"\Wo<0Yx(^3wws{!$׹伧Y_`3wÐnJ DtCMcCѭWD (CIu`ߴ9O_1Ed >t6}F&|`tH{?7++:˖п_?NaIJcY%|5v ?6>b+I^`ǘ4x^^{y)GɍU.'x>3-:hu9Ȝ ^ePu'~)CB8'^gB ɤߍ"xY W!Y>9|1]J}MckU#N֜sUg6|8}!"Fh3&:o}Jo݌i!23[1EʐDˣt/0jOC@Fa utVo ?!SaٸYI,Cw}&ˢ=-Ϊ |9ƈsړE'5I &,$<ͷSgcZ"*XӬ_Ϟ+S!)ջ߷/unȠ#}tg܃x'ۓ'AD9yfʼv=\&/)@{{77f3n,}qzq;̚?ޜj`^$=zČ I#EO=2uL8w]HEۘW>Ǿ×t>RŒ <3xD 26Z3w5 W !]=tEgʼ"tobKd̥\Lul?2̛?d΢yu:ʜ<=:槿̠ƃyI~x&֯ۘ_1|1/ǞG yY8Ƿ{{a/:ۼKy?3h|̙o]7M>0:O;z {)6$/^̿}xu^) z~ 'iK L4iQ`'s2d 4geqmW0j2_lUϢݐiϵ*{I_4X깇8qh;2Iuo4Ղb R%k'1ƿ9kpd7gpUY[&ulm+V|ʅ-Gqfb>]Q#;Gn+.Mfb^Wqi{9kfZGhT$/EgG r4g/e岽zo4ά)؆\}z:۪@Y'rhE>4+/27G$,@s㧓ydD 9|:a|aөvBu)&2' "g VEYei΢iQuE{3K1ʦ읹*5ixEFk(WqvZJha:qe"ܶm\UcNĝ݂W_L]mQkQ4 9lHlʢ]쟵P+WrQd%RV:HdfbI2cS&k|tP?QՒd^Og66~4̨3v9a&ONQy=yb/QFI7kK?z/+}Q˂lVqӌS ٵl݊_fuK<'28vfe`U&+ ъ]3k2S!k|٭tԝyzSTh+=x||ϗi#6f,DxNBʙ6uWeOW:/6 n6J K#^K=s8`@tDpPPЀ@ϴoPQQIۯ ((hPOz2*oъl7~=_O,ho7Y!gǔ'S]7Eiа1n{*C9Pˡ+y<{O*BpT{9XEߜH#8OSH۳i {8PiJnJJ\ˊ`28Sf9s{?%dk_^xXx!\rrwF籽4̼_Z1Sd}0ulsS gN&n6r;ٮyl9b~q+F1wSٰ{ZR1Wk!YkoUm`g״(䡸I(^댶{y+n:^sZ@ B_%=8f'06ޞEsEǾ[ S["roN }NŢ8V3i,۸ֱ;ӣ޻\ԋUf6ΪD@mSH #h;n[KC}û#g4⇍3ּUM=ʢ8%s!_cu*2l#sNm͘{f24dō͕x'oH~"W*cOYk6B8i^8?⇝g"7E)Ee$lixk0\k%u:O~L= [!\'|+xuE-{i2>zw{6 rT*`*ʹ#85#*jι>3w~Hގǿu'chޛ7?,̃<K$l@-._ 0HZNGiW:4MrݜvͬY3(//_>ef(I<{ȱ l0 ~/`6Ji7lm\]"vry~_OwǼR!Y\͇1WFdBR蜍4bӷEl~PG8VU˸xF$"O4DdkܓGG{J r_nJ|H U"ˮ2=ʼn>Z^6O}}?̾xҚX^{3L RU㰗CZ+UmB={056y f @:4nOVVM4@vS^YYYeeyڴnc(_8U ==^BJ/.v"3ZSحoو%}8-' [5e:#Z}g30d?+gc\|l `^ \Eػ| NX(WzNE#|w6v_idP]dm FAlTr<-KPHw:Zݬ- ? \ 9o&K?i{&.G͔֗*)^O>NMKg5̶aƬT١7kCm9|w`dzO @T'>XB={gdG"տnJ21;nh-]$dG,نL:6N>c[vyG@`7Sg^J<'i_ϔ:e;dv0cQ1~~)[c-E=ʙ<,)۪"20af/Ie?.e3dfdtk+""ξyJYzpY)_o' d3}Q1~/,IYg {5ejDfv-gx>,]0NȔyu;]e\p?ѣA =#6Tj ^EI?؁7-úfT"]:KJ\+ɇAJ)Fb˂-4֓qo0FȾ+ދ>ˮ30g9_o^~[eYm032cxMG#zw2d֝WCzܓ x{|z4qRJ!i\w6cM>o G)s ¼ ǘl?ſQ7_IћɪKTƒS{6" EK_ٗ L{5(GUϚ%#U)eD)!GU;f0K.?FDK

ُ<2 ̘!;>i:ww;d\Z'ԋI;# g2җi;#(]$_ce]cG8‡ ϥ0~y&L{r}6L]IVmHRĘ\ןE3\vF?kδDOѶg>((hȚt>9^̜-Cez&zd4) 0?=27pI\vFaG>qc}8v'+뾐̘{n#g 6i23~N!Y;ƾL-oU߄A+zHm˦<#lB2g~g93]W? zj+9s3KxdHw:&0[w/;:Dn{T^R3@2}\[tl[־*tCDCb& ֠) g=j{P7VDLX,u򅨻Ɏt {:1&& u1<$14 YW[*$ @.oMJ-R~i qsſ aMvox1m/rWѕ@:ЁXǛ WtcO^v5O_q}BiHD܇fSMX`k:J4e8nyQmQn$w#ȏ"fTQlEF3y-i$慉oINRmo<fVd(E9,2?!%caSxY-Tܖv)]ѢYLSQB"Mu@W iSE=ͷߴ)3AKyoCPnm$?}-D҆\N$=A'j9բ`9S[N÷VcϖJBO`)-J_zU^H]eP^n)t\5変#&nf2BhȈ[D QQv cBKs=~|_. Ri椠]\hhH K&NrhڵQ3Q4yJf։ ~? QS5;PbIV:ЁSK7)7ɥÕ _E%.M$%!va syAX|\ |ErHے `mjJDqOh21/ANJSW\d&zy}á U:ЁhUA?6oƫl9"+ }[jji]QKAE:,@M[ؤkh&m@Ğhj5ޖ-r,)1\Ii$ܞEC{*T}t?oJЁt"uNeA8&*L@9$5PKDsZo-n kSedX ĕvG.h]OA@bAb%NNA4aN {" hم!q k Sfi~b)Z|%`ߪ+N!WAzhڨYPt@k$"uF9YyK;Q_W4@EA_gM2|2doWD7)U ]I(.,{Ϟa߃R~G$%%1b(fumҒmqj5YY~ڮ\~@II ?3W\%l\]#pXAuC$ = te-^F.Fހ`6N%@Y1oċ=GcA҂lڰi3#55d8 ԗhe-//͛HOb&jGI 7 Qw[H3 a:26nXߎ\ 26og.Jظ=>_7gúLy>As-8yiGN@EYtrYnX%@S+:,lFUu5yV(@| ǃ( 8rJᐪ~L.6M]]=99ت*OHF:$I3%ZY~-VNƄ9N**+ihh`g'DfڎvU?+8n $srUZ,ؙ8yR _WnWI'nիۑkĵ꿈DI'!I6[ 99Z>ОXIaF#h :,l];w2pRNvvN:lۻӆ%%@{鼱p+F!55[UeTsǃg .vVؾYW%TNbRDRurQYYn~(($'&Kyq$I"%%$%%tRnwP\Zj[ i)&'=+ 9|mMDGRmȠS+ZI޽fGv\jlU͓;jkzNEA$MU2W!?͎V!%KEo9ADQL@=d40#IzwB6oANjL&hhSUt`0i ?JRcH޾ˊP{ w`[Ѡj!It!9æMphϫw(:3Jף54,6qAdٔ `<UKo{?~}NF AйHRr+z~OӃh'JZsbNX̋So1dP>Ӱ,V+f뉵*A\F)B ޑV SƑUdrfTJ+WLZ{k9Gݸ h| YQ2x0?l?Lrlb24RL-PjFAac*W[ܮnp9-rC8@SVyp IDAT_`XիW@y8,"f޽zQQY;zV5jjN'S>* 233ٽg7ܜ>沆ÉVc0v\9jwpXVX%$H9 +2SNeۏ[9Rqݱc'SN壏>hp(*.®|n¢"r=ʣ=SSش:ēছPYY{żҳG+fa9}!I(.*{=HKK |&U(\e[xz4N(xE-7u$Pb/` ( g̘1rs;өҹ[IҠ]CjڦrKR* =^p Az=թ,'i/Cͨ *w>j=#Ѡ5  uxJb4C4.^hDà~8묳سgO M<;V6Q^ }T ?'5qIOOoQ)ZF&rJN7'7Ï6h4xEQoؿF3gdBE\.%%%|7|rEE̘>}Vu6s$h\7m1Ɵ5/VsAt:ӦM ˵j*[R,iXUT\x<72^Yz3cUE RPgUseB$Mg{Of2_/ffzBx7ُe3v׻Amcw84L_vm>-mMOK@A~>;ow/7I;x< FV5蛛S"'Z9зo߀ro߾[ƵORv[,8\7;}ZB9|ERU߳K4$I˫Q&ݲп?ytC}3ˁy'HIIF$kfAN߹]ݳ[:ش^uб OPДxE޷LQEPu@L1"³L 1k>PQr} 0jx5h4jq].?^PUZ t`4 *Qa!Ѩ9NJ;,h(m=hM4u1X"Z:BMLQ;?y]nZwO1PӔ+2YzzzȲ(!ߊ~3T~k\̏ (jFr6:gv圩S/bæJJNl63y҄V/'_ȱJV[q|wŞ={et>dY&33\>KV׮ ;.<āb:p32Я)]]w>zԩSr}gݻ. ˪"`Ɂoю$s7ތ[ >f܈A|(wG7_?5q?e}o5JsX`ҕ"V?}3wכKocgd(%|[qNh87"H~rbǗ+?Fx:g֍sgDXnO-BnU>}~#n~ŀFlEQb¡q'*E=ST~ ( 䡾 9jGAb41hZRSӘ1y (/̣8xHL͎q}w)gW~9qn$IH:ռ7hN[r@׳a&jjj"{Q[~¶o%(|\f]S>X.72 ~ܽ*/&:wԍ{62olbUŢEgfk'/c*iWOU>7f´m;M(i]qȉ[l O^Z^Z={ڻD=O.h4$] l%{Xa[pw3m Ulmkfpq˰cQ_+5;|~IɘF9,24+sBP|eY{%["j ,FR,FRR,\/^j1 m?C{mHb/~HRmp8T}M ***8ZPHEeP1 ?~<|G#INjjjj;dddD}]=n*uH@YUʁVСCɧ$ߙ0q"?deeqɥ3rr٨蝝͗_$ NKB7bj1R\^GEU8qWPUy^}.gOwߣj>S.2Fr_^ye\9vtҹe!a4"raQ$K"[U^)kq3CR`ǎԠl\Vzwˇ{}ETWB]_6ɤZgTE@}=Z 4uɁa`BY`%h+A7hEǒ&{osXC˱5(a0\}/V^9^yzkhHpC3'7eWǕfQ3B Ս&*$IѸ%f0OaϞ=Aeґ +  Z% ǜٻ.ty+DQdǎw'%%kV{d2a0ظq#˗/X|7iv(ʕ+Y|9:\67G᪭`xe08p/;v@$\3gZ- ߮`\j>-jjkk+&/7lݺ5\aa!-?qx65X/9w\qkl}C%wq2ro|;.>ӯO w]˥wȆѱl{_fsuǥYb`aOsԀ'[$([®e6?=0n%m}rbߞy+- kK1L<['8!7P_i-o!՜FlFj@%zЙ:#(84I5HrR)a٨t0vX&7;HW H8\^ W܁MpTŀ,IȒg'")' -Gl2fN dvdL4 لr$F# **+ux^^/̙3;v,99Fh5x@D%r  H(W8_Kҷ_?Mؾe Z>9m3yyLrS T Wǂ h.=5UrvaqEG {eLW_= Xh'/s/2O=ɓpN,^Ƣ^ $G@izjksn rqfxJ[',uuum.WiiOհvGÿz!\:mPSy̿A; }A!vz[^fW_Œ}VX޽{)++8OdddP)d\WfժUSSSʕ+y7ٿ_H.n:<6B}Yt:^ziJˎ5*))Buvc .Wiٱ6 %?Nuu5~)۶m{am(Wi ͷcDA+SReү(Wzz:e-ʏ/X\\̶mИ{R`/}*:&~W>+9kNZ: ?[9c_;^V HVݿ|XzX8k ٺ X\zz[gO&õq4Y7IzcщGj8i5e景N$DV&Զ']Q"v%xts'aV%([!*4ShV&PO \*F&diR67l U_-Y71T7:]2-ծVYhQQQ &p8RVH,mo.~>Mmu_9p*b/@).*f5GVVɗ% ,vt:-ZgЇVF ).Q?_ՠ!Lc&SӸO"obz_~9>x@)P%;)7ݘnIXS;Պ$I YYQ4b5a1hpwAr-b(\.|1COƅ^ʕ+jL8Eq2-lA~q")VRa~+ >յy,k-jۮ"ihXV%Hxm~5SoC] )~7x}`o0 ]b j$ t3 ԭ%ib砚'TaEFѧ 4z>F#!r+^^nTnjׂ Vz^y+ NHLq-D&o~{n˜o歷ٳԘ=:ѵKWxeNG5jg…a2P|eeeq/ڔ .rGZZf͢.'KQL&_Yd iii{\yH>puAQqI^`0`Z޽;f9k4o6Yf6+s& |<Ͻ $S %ysCu1zAq8Q)C4/I<߽ BXAvf3iFuNDɔ%q]Pp7=<Ƭn}7f)y^HJѵiw*Zڤ8ĈفG+`Ȑ!,}㟭F+6,2 x0곖_TŚf IDAT8DPc24,(HR+j~MsS֮]Kaa bXdUUjԅ@=X #Q&^*+рWVyH w4Ӂd 2 :F X( = ~$Z蓓<ҧp0`^w) F`00gNn 'QW0 W U] _9`ԃ\(qѴ)9r$z} ϴs{wcAm8á[,vEYYYYY,@RRo7nGe***l),\q444 /0{v..rRRRX`sm3WΝٗ?j(nz=͸_pRgSHJJ`ڴim)W׮]ؽgOWƵKp)<:v> ߯D+q|zf/# /&yia>\qsOaw8_KP870.TAһs1KWƧ[ rAvex/=7jva 4ZZCrbăz[{@zz:r͸NVߑD!hM]4 .a,v;ǏTcbZi P[߱@L㜑co^A5:읝͚0aℐ YˣÎAƭA>U] /M<E޽T{^;|eɦ(VF9Npi,+4Z_$\Cu\n –W^`00d2Dﲺg67[/gpjYSKT˅00w^$QoNc?= /#+ywIPW%c.{ #}-T3ϔ3rLt/zξg_߸NWr+-Hߖĉ#FgsY+6VJJK/qam3m b~ظaU[U"rrr$yy4גݻw3겢eT_I ,9EQEp,xv;?m ޽;'IVSQހTe<_ d5p0BzzF/@heQ@(l6ٽ1[q[''݁(![Eg$qxV@k_)p3Vg??.$`ѹsg<K/hwӍm-*.λprg%7__T_=k'o~rsrb BztMlTٝ{^eԸ4׳o 2bVr׹`EL8W_}zKcy൥c6yX9mr+H4by=45 Q@ h  hfs P\<E95'$ISQQ?EQ I ~90-D^tۭ!,.}KNG}xiePt1j 4*}3QQ @F5=S+6@ .paF/v"{/'-͢<`u= o\?'E4*lk{ I2fL߷fl6ۥo~ESh23221}˖-kd2\]El63o<-[ơCT?8+ ˲h7.QfmJKKeoæ\3O#-5kܹ iiiL4ϿXٳѣ̺|Z䊧/;c84CH__g wF"ex\@>~&}4^i6~ &1?3'HCm@cd2@!7iѸo_wH’._"ɭ,6+rhZ3go~$g1 /X9S5K뷖?!c?IU 8>}0db@e|sSUUA^j[ :TWH?/>RȾbwDꕤb+I=ndY͆X4x=VCӶSc~X DB%hQYYBdD$n@R cO? Qddd0X(у7Ђ˯0~8zniyl,`]tק_SEM=ëK| e\€9MVɶ|ǜ5f,&MW_e=ͬ 6 -Zy_^u%fK$bytZJ\SAp ^z+Չ"*2!> uu .`4- o}fs%`@U[5n_&Q4MS'ume &b`` ݷ?η5E*ЍV0~+06QF#Au 9#Сu8gk 1t|H}ysquب>VvEn7O?&qf4.\ȼsxv I:>sƍClڴn6KM_z%:iC2nV뿕kԈ9z4f3n|'\GɁزe ̝;7,ŋg!}f˂O6NMRV׫Љ@bͯI ͖1M33V qn7Ϲk.l:#?[;G(H74erՕ/!};x Ǐѥs&Lm2zo5:q!YA{[i Am2څhZ<Gl!u8vEA:]# [zH 0s&Q{uu7w/nZ,e\sh*۵V+!25uJF{uuЧ6ZH$?x=~\ V(rqpFC+•47tP47jԕ bI P; ^пmĥ 4(* A`_*A8ֶΙ( |g7q%0}vv ]u7裏LVV(RZZfS/ƶ?_%\~lq֬e09}gy{^\}6n|?NRR]vEEʨh4rх0vYy7U {3J@{oe,*A8p"**(?A(QP=dϲ*-I3G6I4is99s=x ˗_I_F ']QN=&[#5 ߯ȶd/`VÕK /@Ov㹩%Daxn&M۶.- W.]-oBn~v'^mҷ"‹d"%9,&*G',XKJȲu&l oHލUbe;wK.ѨITTgUV, W\!99łAً=%~߲LG]N",,LBCBxwiڴl>L͘ #44`VWN͛3HĬ_~z_?}o, [Y\T&o'bxjt4Cʮݻ^θT_^~?W|X3SLP Uh K d3 K Q ImD1AG`Ζ!--k"Sg6'OVGTFRՂFc 5*+'f",-ƿmRie+} +`0k@p&@"plMCºEui Pe1y3r[mnٌhd2u .PURY/l)xԶI^b9FvSl{R__:oOժU+RYXg h>N$wJƍسg/Ο ֤siӚ*QQŎ[ ա][׭ˁCHtDjTFi޴E 4ڥ3 6`}ԡ=[*,o~F=ȌUC̙0.x3w0g\{Sn.9Pn۫6k[*Gzs R픊 (CJ%\lflXK%4r6N4P ddf_c s nO$X,PZboێ=wb]#nim1)o$!I $DѻcsrrHJJ";;ȼڗ̌ rlSD%kccb*GK약dU(ڵZP<.e9a:,ѻt;@qe26PumJ>JDWDF7s5!"_Q19 V)6QK{ũ[||\̉~ӯCAAA.MrETe&VD@JP&?fа0Je~dDX0 FMP#ޤ<p@wk4Z4$;GC (dYvxz}zWSN+DX!8ǰ/,ARw<"<ᰔ@E` %:(}kIW%ZMtt4AZ-9_J"VU",A!I Lf#*ڶǔ_<"]J :?h hGps7Uٶ^A4KU*""{\1 `pq@pQ@%*a GFopMh69g;O @ ^:":r,H @ 7"^:2K bj֤o{>@ @PqW_2Gm%A$ YQ*t Ԉg @ |n{. :m%AEEe%^Lmaa9 łlhq:ZRDQ]7 ws+ Xd [ێ1LquIv4$`4QԎF- 0겎(Aؿ@ x}=\P>m%פ\Ή'%AAAy,R/s1EADFD:/9O  .(uL&5hHNjR~Æ>xɄJU6'p1dYF.YQ >1 ƍY&io)?9A嗊A?БgRS8r-Z&RW\OI4mҘp;KVwǡV&ii4iXf qDU\qHMMC4m֢dp=%CЬy3"|(+ʃ@G_E~!#$Mu4߀WdeVGTqZl{( @%ĔAc6III!XC.f NGJJ aӴj݆z*NA YT*N8NV::"CYۿ|(%PLG^:R_JfmKΝ]9ÉcG|7d=8c~JiSPX,l,ú@C<9K;)$qq  xN'6$>ș[y+SlA̭L~B8'CJȔv}h4}*B>*N~)yAΔty&ظ:@Ppn+Z.߽2A&㟩 s}ͥcg՗8xK/fر>a<%g2nULYmfSp$n?57Ywc3F|Mcu[9cɪxt"Xk`O_%vX[+>b-?#>G<Ye0X)˲EX{ݲ{{N5[gą3/Wyr}3zfCzY5 =_X֯z-{}7m(ğ_wضglxd̲l!l}6^1;anfﱫ,t3?~?v9"!]x{MWxW`Xo{ҤV,quZ6^*%_s^W'y֟ kSXjcy콯Яq_В,W~pMckuaї-)WNW64onBB+E s+SW0;ns:#s'5RҤKJPqБ7hoTIP=0]`I./?Ĝٳiۮ;vu6~u~]_\ٱ]eBʚ1`l6sYf697\&jBuogn"9EQ1'gNR]BRz=zFnp/|yc4 OAžu(LFwO* ԊaA($%X:8/BN=IAPxm3YgH)Fsg1 IDAT9Ksq 1 ga铏>B`C[y孴ze1/ Je5iXRvqK$Vn1#6|Nq*Kiԭ+seZfi6s8s2>ͱo.:DY#q'<;u3oal:-"?P}JZ?9 )C'S{oͦgӛy,Id+uA(9Rv1$^_NXz?~o⍷䚥-~N/pAyC=Կ{kӠ:w)S磏G RV-4{#5NqPWLYqȝUI99sO5{O?z:¹wwV{/?{Q.D}/nI%j35vXt)$_NX*Ct03=R0Mza(%/%|Oa8ST9 (`Ūؕ)CYaM9<1¹w\&r^~xudb#B&/3zO ]`cBr u2l&ub Imi}83=^7HX8#~a0<8Ξ;p̙3|9oHN*8ΣFR,`c=\P.p#ov$KNŠ/vm&_ۙ.{> )ۋ;{|_oc}*e}P>lqq+W{^c) UW}TVu H ԨC[x9F\%HGw 쏞Go$.g`ֿ5' 2Xرwy?佁Yv-.Q;ӜMAIHP~*Ev Fn&5Ҳ2JO{ *.f_>O]_OO^)SHNIF-$_Kb0qޱvrx+%=CǬi-o>UNUL w<U9ܬόs}/_'y rZ"Uy!U zdA~v 6qƨFi̦%۩wV\u;%1E;gt{fh#Кͼ1u%'zV^CZ׉c5>M/|I;hiNV;8 ܝ6ciSR@RIS]∪,)M,3}|y ?#a|6!jm;fN;ɲC+2On{E JBF$B_&nvULbx,]`yԏCLtepK;8IpO Bo<ۀ֠ȾNiI4!Esi|;KKt_I[d'žT)µ]aq? Rݧt]=<-(fVK<J1 Qa{{{Q۾`ߞ[}$~}Oz[1=󎚱Ers@ݘPo3sEFʶ+DvnF[ K'!لߣ.g'N-5"/RGF9G|JLRv+:*d@҆ξFSz8M960j4e u>92 `NǺ?I>@BFkF:d2srڥ3f` t+3_xaqC]ڃ9x}@^ϽhC>]z[iԓ7?1͗XO[;/>^燐v(c3o:}p?Lk %%/-r&w9+ tjwߎ`"dNYNa?P%? +BbPPjJCXpBB6C<_ݳSVI:=TK.),2/C&1sN<:Kg7}xsbY3ONd̀>4 ?=E[&E2wUG[63|O'&5νӔ'g4 Ȳ}¥%^\&/,Ys[ GciS:3X9ex5UNmԷ)gjy*ۋ1sb N[="Ji3 (gΜA$j׮ٓl:]^OxWeE;6jXԉSS=[`wL].7d%4Rb?PƄ/d^2JZ#/ِYWkPGӠvkc\*<}=/zrU콼1~O7M2MgYO灣<1ڏ3f6db JX!_H";\Y Yu!#NiɨO![ir?qz cr`r"*-ynO7l8*2&##L׋222(8H}_31=ib?DξН8ڐ )~r9\ A(FT˧ >awvʕ e0 &+)Cl!ۘl2 W.c$$p9ngp٢{^MKާ|EHxQ/!F3~fڔ%] XPE׹;6r*GGւ^NA 9-g~ vl'|:ޛod(`_/%Y?{i($PzHU٥Wz=S,Հ)B7: 2(,khθJ6Sշ=]c|Y<[AMMG> F13NiOba?'*U ,k萋S f*cEGQCЮy~=NJv&hxWbUJ6mBNv"~='9Y8^xLE(RS4u8%˔Md4NmozQjcf\6aI֝H橑O|y$(ں4_l9d\M`Ho7aʱaٓ+*?۹I?[jݛ:j CiYN \0ԇ+svW33xj~?NvVg}B[-Kd/pcpnK%W,&,f _/̗m^HM:>jRSolZp˝,%a.lwעQzUU5\Y sP[ZӧE:o:IZ%]= tyo;|iT]SÈ/Op:)ûзVL=̴dggHNN ))ƍà)aZmt5S Sb\u/?ۛ3y7twʴxmkÐkx/۬!wMO1x9HIO6#oΰyf} <"5o&;n!cxu[CRG dneڈ:/R8K>aCpm3⡗Jf<Ѿ׵G[s1u6Gt#޾SW^xj]OkAax):e4U Go^'i՟LXl "\mJXH4YsI >y~rlɿ7q><<զ[U(R^AQ]EGG;_so籛IC&c8m` (' 'ƲkD+=Eŭ|GK!~ 2]\KW1DO=MJX/L$ZO>qc9x >sxGo\k.3O:J3,9HiM1y(id-cR8qsY[%i G? 5ѭG2{iar1]dʹ9q|& AĕMۚ2*.m̕3ypsFyg5١%/Clib.2._-޽{ӻ4FM$$)f" cɒ%9bX,W1ia^4_13XN:-o%(_cżky_5W9on|]YUj5Ljf鿧 JTP*$_K&((e W&T~c!a`ҍҺuk˚իhӶmN-2Nf)e-?HUc0082w<#m0NbHh.^Rz`s3L>ݥlR4zeJV[Ǻm)AdTv*$48y"Ӳu:J~+ݽJIz -8\p14hԸB6dYZRQU2ٽڹg䗊gf3{M۶?\lk@gM[,Y{[]J`]15  -eb!3#KHzVV}@ȪR9&Wals15Q*|JY A [αXXazj֬ENCWTTΜ:A5A*+$IƱb!;3+.v:U*W.7ʃJ%;t(p?KEч3Jvhxlt'&VNEkI^/xd"X#H7#*1 Ip1;wl- .Z( "##TZYV)^ENB!|ied2{_^ P_J> (p&P#o ]8GPV*FJj`euJL*Ve64 ::7+$Ir9 !PCy!QBCPpOB[I pөSb3Uc#WPt!! pqW%􁰛&!l"ym%֎YFl M Se@ R.(? АU{~51UF @ (,Y"$(h+  imNqsB@ @ lKIrJ#%BnNy& {srK)?}d6e;\dۃR؅+av~Hvy$7e^@ctr#/Ln@ >.AWs_4**d77`a IDATya}o$#}mU_E~[ݝ%`wŽvnrVsU>Ek1l˶ZD~mfj~|Hvh |?w׮>hOJ9/A;7WU^:*|ޟݾx;Г|] 9L%78!8+ ` -}u,S(ضwch,"v|qtsrpDs ѽ[K޻9UP7Zv. xl}ۡ0rV;ݼqƵyrw{gw/uɎ V q-P@6m7va_<ع< A7ǣͩ _T>vӏçC\Ud<#wuzP!q@&F. .Q#ۛn{e˴ϥ/Mgl/3Ʈab?'JD.4Cxl]T)= CHBE-Ğ}Щp:o,m6/-湎a^wb+m}!L?捲+_Wa @ Td$kWS Pkը$#TW$gx,@TI#9 bx"ah݄LJ#) D:AQFQYevKH ^\~\O?1A>)>Kڹ^_}HrQ~-N:6}kūXn ~4[xdɞ` =Y=wi/:/d( B:揶t_<-P%Aٞ]Y?y5yw<.~|{.v^؟Wq}1˱5;p*Ǵ0C::ʪ|ɵ@ AA$.mjr;lth `U \& |O5,SQC+_yLTEZ&=:0T([UJdYajt+UOlᔭ[=&v\Աl]Ɣw~&u/jmί?F1p@Khcp{[S9rR?DŽ߇GM8 Rt! B. ;'}mYH0x/wpf&d}iu6_2^SaMo+@ prtp1~?Aչ!=GJ]lTW1pqG(+Ӡ $]Pչ L * LPPJ8̵Vu3MVMl6lt/qjxgӮ9wD46_ lql=ik:EHHvBx/3. %kډxowl= Z,SnM`I7sbeUpiiZ`q(׊pWRӇوYRZ˷۽izSf>@HcEss?Y,J^o`†wxx\0VwܫB8 'ѤJ]t3xw9-r{~>m=k ʟ+H,7Oyԫx8e/L!w"/r y 2 c?8:K|{J'|ao@ (9he@:WF,s_؊cV"N$]VP#ps\RDȑBhr /'rh_>HNԧqfQQa+fŒ'/t1&Sγ履xum'&N{KB]كn+tr$7`8 LyVsgīqϺڴѝmSzUH`L\F7WrVc72;_,x-N{P>+Epu<i:S~Lʖt\0~zN}6\+Nч)GV#tvo;YH~F:iF4><5^=}O})IB>Jyi:@s@pg\cAaz:kyu41峏P| ӗ)f] _Ѽ/eRbyWl?*"B}q/B ! 4r(t9lc F)O"橦 vveʦbĘ#Quf[F̀y n\*Lj`F{S<7ٍQ;;Ю\otYߍQ;Ҥh1LX̖nWF fЭ4x({NeS]5D&G =DɡFѭsWH㑼r>u+xv`]6B֏Wȥ:\!2nRfNL7'3l9]:j,_,G쏳y~spBވsqkp1hy3Eq|};T[P릧# 3LfBV.VQt &#Z> 4JEPM GLr2ȤTZ^BInˍR9(<]YٰZQ֫*Iy cXF굾U1 0-~6h*T( «Z~b==zĸqWǰwǼ< =*ܵ8c ?ɽgAH=GQye(3{~Fa`WuǭnRCo>גt)1tL=&_je_fhC{}#?ʠR<=Ɯ M|D}9ŘF鋼sIGqWMFVEWS{jwgSm8PVӨ|XA/Oô%J\[񖄄3^w@?۵1XB¸w CɸcEAۘ5l|5b5G i>;opǬ }sg#P9j^dj0|L;B-HCI'M0@!i[ʿ7@W@_y*\#\McrM4`~e˲'LxjbD %.nϝ6rY z%VW\jl9u{CY\:V5.ȕ۔[.S}n`7.Ow+"➾Z<̿tf۹l"+;K0t>47ECZA+ʭ |ƥ3hZ |1f /Gq~۴ Or8e7r+8 rIsRgrd+93#/>`ٯP@Pm@.}(HLcS/i̿xG^A+~CRIch9g2{Z͡!T !߸DO|pDϑ= !/owoq`܁W湗;aJ0d g<eGTvZ,Z1YYq"A.mޝGULo*;XօVQAElEmץֶj[*u^wV*K+"&;B9dL&lI&'as{2r∈H7QΝ\>%cWk3{~3(H3XJV/Sy|mr)y)8dws•͠Ọ? PipٸIf^|~r:c~Ҵ%СT~uX₩g^G]n+}?کw'L>8a[> ̊ 6jX薟LbTexk;(n{ 0ka y8riʷUP? #yh[Y4i23hɻ铧%av$Mۛb4}4mNX6WyKm[cfM˦NyU :Aǹ)~SFFKܦKS--yZri4}Ghzߴ-ahMy~d?~U NP»old ,nI "" 4 !@}hE%>!濩sy_~h;M_6c~ FA"/fῨ9|ΏR[Wcύ/K龂>˝7!6yFHu6['v$n0y )[:Ly#>=nl VynmC6kn9ԭK^\APN"/O-z±6Vs\n29+C<-ٸijgRv_%7%S=kg;:b)JRAS~39ED$){ NDDD7ij~(ˑ snrxm0kȣƷǼc+^e1ZWqԷy2.Ve5']?q3rx}d?bMuZwaиwoY_oym?xX}߳ds\Y=tҧ%4ȇk9T^gd5*LSD1<]Dc_]N )d c$ [R?rYd8SkDQa\wupR IDAT^2e@eX;`4P >d K{'HU}~FHͱJlO?

an^qL <ٜG"5a4~ҵ@=x_js|6 u MMIN`kP\v9ۖ絷>worWdI9@u%_~ '_o5n0?cƘo|~}U9%=Z_(&m>6erѼ@S=Z6>o6Vu~n_< N 3xdžOyw7*曏݇N9M>??(2EgO˻q%|g,r¢w*7Ov;KW!{n`j:$$aiV[N~l;U7~2ۮ]ٻg/UٳaÆuwD5@oQ \ p2aa6- ( ';q#F<YeXe k68w~^.,d-= ,_Ycܚ(s2o{C6q ۇ m|q w=?쿙8)S[TO[}-J~1(&m}%N\va*YX1ss۶.q.}51-j'DQ;U.O7S_S>|A{0?p+|a[Txkb}b\ OI sL\$7تwcɏz'?vOwu:Ķm4x0]p/lٲ';LQÀx`xD7p1ɇx9eSi@ ָexpRAG"Ĝ ^GlȽQkCv/%CzLailbܸ/y"ܯJ{'<$~,/69mzXxK"imӆ }*ޣGQ\ұ?#׏0ؾ-SL|ԅzkL? {4\ _akLƛAU}ƿV50[Y5t2Zʩi՝!9&ىmPV[N~l;U7~2ѣl)} ʙg|̳XlS<絛>RH۲hw{/ 8"\:CmpaFSq'Jس9xe'th,gVfKUCυzCuqfUQR:Ġ}+l6;Ggtoc>i?}0Ï%? vb>w҃kxS[8z/LsbePV[N~l;U7~2ˑIfw/.{v2uʔv7J`3饺:jwq{;d. '^,&XpO`z*-j+oқ p64=R @/7553ľgoXx=Lp^(艹 x?~W\Ϥ؏U<9|BQR@}N֪*o 7]^*k| ŮfߑL)f~lv8߶p,,0piLۗč5sO)U pPz%<}3B䀁gc?40fDnڽU/Wz8|꽽8ϖϲK6jj2cV M8DzkR"ywĴ8Nis>ϒ2i4XӿRg?(0%i]|R7w$2{ 7r{0jPY漏cΛOŘocj|Ϡ/k&]xxy tXsiWyĂ6A£PIX ~ޯ6*c͝Y_{'_F~~0ڠcecOX?P9<Lr6~wl9$SD@C ~l;UV;8n.t0'ƥ^0s8#8i?Y[x'2th4 4-{rU 5f,>9gf_e;έ pgnwB}fzXPw}c [xFdQ?G% dY&+bx'V_"S=AOXz7Gʥ<Y_Q~=߳ ^uܘ ~W^}9ߺᶘ.Ngl Uبɏz'?تwlP%?L7M%Ár{$DzT7u acxn;L+iZIƌJgM>تwcɏz'?=f&֯gƍ`F-a@>L4c_ZaKh@C ᄆc'?ΦӖ; |cC<:&a{%ߙG}}oyVkc↩oe\9jN~><6gG ʁ>~>ngKudH$"x į<+2$HU}~FHű1-1>'""";oLˎ4>k}>][SMAsA`B8Np2osGe7|\N80Z=Ӂ/cjFXۨHہa`88rFqꘕs$]Y 9[؀e{la;\Լ$Iyd<'Ozg !4m䦈Hc~Ƴ|;heky=vߺeVm_y4`b2ㆭrO`LNVN i4aNbZ# DDDDDDDzX滚S'h~ԺɺiavA{Yxwl&Ͼ~FuRHϦyI'഍ue2F1QEYF2\"""""""+L$"9/"""&7F5ǀtgQ2ͪ~K-;"""""""ݎr}5 ߷/F4ؖEEEE!"""""""o0zuں:j.Wϡѧ""M"""NyvW<{xfmA"""N::{O %&/??m,ʊ4!""""""ҹ KFIBjMuQDDDDDDD( $p"j?Y:4@D$s^DDDImڜ:^t6uۼ̱)L~¦S'awȌlkI""M"""N6pj-q}`~o5t /Ԣw(kacO:ɕo&.:3"o;L<{&+^a7\rY^<ճ0kp,.8d6Hz缈pv6N'N97 u0k\3_gC|ū;0Z3ùWpln1Sg}~N)v;1&""""""NlO( ZWY 8J{ FF1k "ϳFߘDWWӁ*b¨B GAz鄂VϦ=tVΎ<0"w.P|78wTvAg|ĉcp82f Ӊ#gYɳ/5<.wk"""""""1aea6to&Ŝ|Hx!wy2s6O41 vyCI.'9bW # }ɼyc=N0c:Bib|b""M"""N:*7VS6>ǀiNճ[j7LLF}ܰ}9\ oii1M:IL+v4H>ղs|nZضE^,[⳯~]&eDDg缈D-W0_˲I(4 h7 Ht]S b}_Iot J """""""``jI3v% ~ѥI6;"""""""Lp+v&RH۲Hu1DDDDDDDR`aOк]ںd+B+""E"""N:}u[mm9/""" ;&mDC Oy,ˢ"{At0|_ip:+,vTEDDDDDD$~: Dk2+z4jUDgN3q:mMa{H<`'&awȌlYDDқ>EDD$A0mӺ780M7Xr{yj;5ذ'ʷxuq\WGEo7Ɲ&=}R.,/yY 58߁yxEsLQ uQD$s^DDDIE Z쐭h0e hoӉ.7v0t |l~m80c/;̤<}P>_* s|np0>(aDp.[9lh>k}d@.!}֋H:tv99*`3q @b&)"DgǍ.1UĄQ21Ӊ }AbkM{ 92FGCDDDDDDDyÀ;} (>;*;ՠ3>rā18 W3FE@ ۏđ3SǬٗv6Sq 4 `&Ŝ|Hx!wy2s6Op IDAT41 vyCI.'9bW # }ɼyc=N0c:Bib^jUhA gŷnjo na+;re"Ӯ9c6 uVh\t*"9/"""$ɓY0:ɺiavA{Yxwl&Ͼ~FH;beŖ{(4 h7 Ht]S b$""""""k4 a_  Z $Ia ""s^DDD҉m&x@@}¥ 4 t2>!DDDDDDDzVN~?kKdv]E^Æ1ط}bImYTTT0vhΈY]BxJx_Pѣp2شL?#SЯuIwt #ry>L4Cs)yc ܽ28ydfe8"""""""q@K@dz[z]DItaN+ NxjzNHk"""Nµ 4=tBc6AI߲,L'4_W Ñv0D#0`!} ZW=ʶۨ 슁{lƎM~^^~p1x23[fy4x|VjkpuH: 3 dހ6äm:/{2RNUUVk1ܖOÄS2q].'O6R〈B ;10.[֯>YޱcmifP]UKx\׋eW2zxV}CDDDDDD$9bY08Lwᠸ_}-aPܯ;w75MGʢb{MT=J^nnHI'-Whzi;1EfVv5m^{eNfVY-=,ۦ˲,m극q֏X @S^~cٝ0XQDDDDDD$]5Oae[CƂ˫)f 07 rbcSܓ|RnoEDDD'{ME֛C[ض_eFzW"O1`XvtOB~{U9olɘ‚_0*'}ƋH:i&Z/a\9d; - zh[q2Ͱ7qygp޵#0WX鼳}k K:;GDDDDDDk1A;B;=7hencsԦykɓrlc|xy,CQWDDDDDDCCwe(Avt ˞]ŗ 4-10W- Vk&#1qrj7l:&""=>EDD$ `QD0\|ɼ n0(6W&h}ui﵌cp<=RnSOG-"""""""鯱@u~fߖ<ˮ`æߖ8N|^/ ?/;w8ē(,F`D<76ሢYEDDz[ H =ٙmLzdf2`.noYu4׷z=++!C~^~y777 2tS- m^k?Q`W'P_=yyx2E!+3|>~GU5490Kn}r8_cԐvx<444zihhaS8q5U]ut@жH_UC^*OjJJTU) P[V659qc%""""""7Ϻ_7pgxcv7w9ۛ?i$ ⛍Ϳ~.KDDS no;~ߠ6rFGM鑨NnUGwi^"""""""鮩- ?9;Ȉ+; |߶4$2/I3tn}ҧvUNo]He «#wBjI%HvdDD$s^DDDI+ HP.vUcőI[pYo HK]ޏ<إ`J 1ȼDDDDDDDDZm4h_}qm>9r4^RJKKٺu+o)SpYgqglfڵ<|v5M ***ؾ};o̚5.$"")5 H>|{2lذ.:I#mOiӦiY֭cݺu,Zs2o޼h@iϧ~ʃ>ƍʧ~%K0{lTJyGs&zpxN"/XzuGuY"\v{?O(+GA:,Zn!F`il2ꪴY#"i&S wtQW^+O׬YeyHrضͲe7eeeg?C]{*n/^ ܱ*//?!~{R=O=ٰaCb]6%ED:~}7nS.'_@Z~=?xyWS]|,[,e-W)+HWPÀD lܸwQnݚD$y~?sO:}QQQbDr9rz(I(&Ur z>/]]uUZbwNH^x_uu5=XBj=+ rss;^x={ĕH\œ6F˭nmϣi\m>Kn']H^O<1!yitT>},z*N383< &.TTT駟o`YVL1^z%*s9Z}}=O?tLin7s??1b֯_ҥKӲ,x ~T&u٫㴁{W{%Rq2&D%"PJw1pNt{իWҎ7;Çfz꩜z\ydQ|d޼y13^y=uA#Fzp'2c /_… nXy׸k)**\"" ,`܇>n^s h"۷o<|JJJطo_J$"~SN (ȑ#9cc+t /D&++{M@00W7ͨ7M3KO$0 "](sHyN]wEfffT鲳NVVV17oLMMMaǎ12dHDΛ7#GFW^間JD)<7VÀ0CjDG;a)"d_:10`_|q,+Q /xW\u{h!"%Zf ͖!JJJX->*m}{WsFkcZp_G~~2Lk?յ9|d#泭oWY-Ҫ"F]WjmQ5. 0OD'I2228{ƔβF3Έŗ&WH::"x4 %Xn55k[SSuZfKDǴi"޷$v<ӧO9$V,W?xrrr;tPuҸvȟzQq1[jUiDDR,]v p )XiYa3 E$5OU֏>(qFcڴik1Œ׳eo޽{9x eeeTWWS__iضvM~~> 8aÆ1vXÑi$~;v:͔)SkÆ ,tqN?84NS邏NW^*ͧ~J}}}=DDӶ@dkOvd=|n7n澡|+H4= 0`1 .O>dTi9~&$ƍyJӷo_~$$~r^{5y>~y0}tN;4/J2ٶSoѣG'$~~~~i$z7n:1IɓNc&[lIHH|"ohF'\Ý/vq;x6 No}covY^"">|8w_v-eE}4ͨ 80 TTTiӦ USSuD7~qV\ƀ`ռۼ+yB:I>˲馛(--#G4?.//Dz 䒶x<)ݞ͛7Gc9^qq1TUUEN "lw>)}Ӟ)1#[W%"0>}:zDWUUuVƏU͛7GյW $''Lx@8uuu,]˗s7r9`ZRs:\paYEyyy҄5^UTD?OT^^^Bb'm1 Ljge0:thԍ۶m9Hwچ-W("=Դi"nphbF ]g]Iukjj~dž ˕?;Aaa!7.۶ͮ]NV49rHL[AE0Ip"r E$D{I~,ickUVV}/%/_Νwޙ 3ڵGFn]Pź|b. Nw޸bD-sؐ U-^#Gpȑ#FWİaׯ_ij~oذ!YO#.߿Kt|>wqGL˦uw}{w[oU hVMi8Sx%}UU555'""KDCt呛Kn^yy咛xi,Zee%UUUTUURUYEeU%UUVVjI;aD5'0cƌ߰a>/@zY~}/ɓ9sR]F|>/RL"hGsf͚.-Dkwiddd0rH @~~>0hhh;wƴ}qƜGJJJ+#_=vZmTVVGt/} JXVVv7;;;tjmG't܃mq2&D%"CRXXU[rNVVVu֨ʑ%Τ#GoG0 :,;<&MDFFFKRSSu_z%,XHWcF"ե݆miO?=]!D:KWQYYwlHu翓:nI֪tDD4@lێhFkbۑ(iAywY\.~q3}N W_ͣ>S#t^/^.&1bi(x+G?Q\s _BK^b<"" w|~l:/h[PD_|8+++ٶmƍk](++8#v}o5j(6mSZǻ˻ q8>}:&M";;;E^;ŜDZu]RuD E'@D$bMgmsl"1 "i0 M< MEJ޴ixז-[زe O?4q1e&Mɓ#z7^TTwr|ˉv":$@ "rv}vA1 "i"z-a_r)x}.1˲0d}k_me\s衇xWS .]Z00UjnёGY'K2T&TFcpB-\Pv$[O>~EP;\KJ:U=yTdY|xyNʤx +V|}?*+ȝsd֖}b\uoEr)*0eNE@E  LZ=.L7n,(رuV}_-bvaEv:;;ue.+){r?<Ї> T{{ 7P=X\Py U__kOSAuwwK/qu6me]VN?2`W֭Z˗xWuΝ;k]խުgJ$. epQGyD\r.䒢2vܩ+y(~V|bxS.0p+LV{g]p`@ʖ}S__k{صknF|˻.-\Ҋɬt$TTG%r(f~*zj}E]/N?tmذAK,Q8GmV̋B} 7PT#3Lѿ^~岬eݺuZreYr1Q KxTr,(իW577]z~}_]S___5XnN9yEẮ6o\ 9p}_v[1,^XtuUuXL~UPNް1(SgG[R|LWrs9*󶴴P_җ&vXv6lPe`f`t'~<^lmrGwy\SK|[:_i7{Ǒ{OS h>`+uSov<#8B'tRѽ}# 袋u֊W:J_|qQ^Œ |G\pAXxާ>/XBNH ٥sd)83geߧ,˒j*L\R˗+qE/~Q2W<??MMMMC=tի}OPK(}j/aƚ={~Na.b|2,c 94w\ΝVj~뼽橶&p($k13Ng? M|y%rZzu4Oc U__Wb̛7OtAUl455+W\QRρr8S/9AK^ƌҢ7k_^|Ŋw'_cᰜ-dY1m{ nV[DqhhHT*q'+6QG[nk +ԥ^W_}r)x<^W4%\N8A7nT{|ʭA^xN>Iy~F477kƍꪫ"sضO?N) Xc$>*#,:q|}͛7O?Ou572Lc.^Xf͚2-D"g?twhpp11:uYgcݫ{TF}Lg}z!}zG500PC:(tIZ~ThtG}~_{7߬M65eYz;ޡ>[s YZlIky$?5V]|=-[Mdr:V-]TWֱ 8餓tI'U8Zv֮]L&^zI=^yi׮]3{²,544Ђ t!hZreEmOڟ9g9s̙r~,҆ aܹS=~iKڱcǨPHy߭*XFZn֭['I}_DBDB)vJfqXB+V+LjppPLFqEQbqT2w\y:3%I못[DB* )[.12(VX,6-f"۶Ԥj/$BX.0PzJ 쎘fIV,I04 h<@\,RS *rgLd `0%I~ 1@J@ r$SJ@dwNWu5מ~Ѻ'k?/_}owIcӧ/kPDPsr}s'USPɟ,I2^ z `0XUJ@uUBP~.@)F`nI1* M `N>j%IN&[;wjП-iOɗ-%TR,[J i8cΩ`eK SJ@LR0{zJg ; ZJ@Օk>XUIer|"w`I(% (%ȝ0=dTy(%  ` xܩՙ  d `o,X@U3(%(% ,I2># c@E !`{ @XX60@N=1@ |S x0KLU&0& c$5  Օ T |#0@T,XT,ixT թUs*gI14 8"w@)k>XUI5|,{*AW&1@q\aE 遌VRG% (ùJ.@)Ʃ320X60@k~% c$Td `WX;`z$& "w@F`"w`I1Fll,RfIO2 cH Xc8@)AE R,uNՑ1@ & x 8"w@}fIRRJ@dd `\`: P-4  `ֹ:8#0@UTP-ST+(% S H3Kbs @ 1@ 1P;`z c#0@e> K6@F`$S9* F4sß)%(% r 0h>@YWҞT,|JVP-1, @>3J 0Ksf8@)AE |utt0>Hթ^~Yx0xg7j)ǩ ;@01(%G)ز:]}G%<S AA) fLӃc_7ʣ^K.+B,X,ڑuu ;uO+?u29Xmu.^. *s*mnosԴX?mɑ/h.҆ꝟI}0UeD+B_Q!I%PTP#H>uOEX>ϬZU@E/O/۶=_ži_~:)/\Ⱥ[z6cБY_7 =]`)-l=_DzvuLF/o٢H8<XSf͚=C:dr546jEiEc]=;_}nxsk󻮫 Tì JHF"(òq]Ţ`\í˒1 IpDpd)e L6˲ܬ&͒ I(h(`">jO_jEfiB`.XĀvlmk/^߮$}hFz͚MRWU:U`uY\=zQ_NNӭ[TS[9ݤ^eRCr&G)0~)]~?k$FK>p5\W$cYZb~BmZ>%TSW_5/`2}'SJƲժՠs8j:`LK[!+VU?Im翟Ux\CEJ.%p3idN=koӀo騏+Uʿ+3Oz GTФKVR&o &$IeeB͙*nG6Fc "իxK=o}rb2=ڴBv^L ΓաXDKο5$_sӿeWRHGoyjs|P e+ʲL.ad`vɸg7Q&vlU&zJ˵C2"P?mt5oztӟoiWji6nhNw[#% 鐥OGR6(tՑbm[e#ݖ߷4gڽMn&-LZݩ4̒eUAS%I{K>CV]>Cߒ]D9E;S,l"BPQ(R( q8,ےmٲlK XR(Ry`$K\7oi& (_P^!=UeuByS{-*m۲[B#gDclɁeKpDf_zz*E (۾V?Y&7}^ztu\3f &Ўk4h~˖m B2dj`#ϘlP}{4--nF~Lgٌ)fyCSvlmuc,˖ў ~~/Ihje#>,KxQO_dSJJ ʨnw][?kϓ/{LF}Jt顐 HDkχC}Ly%t'R_oH4 NpΖkH\̀|فIW H%5N͸Z2Zdcj/uAsuգ=m?Q{#>dG J}5{׽&t݌܌({7p_ +sA*iLFS%)bj冝:'ߺAm#{׌P[_XMM>Y못-nF  {>d|`uAhy͟b'u8{Ե0 換17Cۛz5`܌YfCO>9=wA,SuS_t\G0u١W_|A]j0:lߓ1kLZ}4ߧ1i[z}v-תK;0TZF#YFzknuutTdU,QhZ[&Rw*%uB$ۧ;JuH 4az+(8 GȸIGjтZtB 0´%[:5ժ >uk eH BӪeK8[I.00j16^0 70c}yL:]8,ۖ5F#J?ѼL&HF -R$3(I顔|u &S_'q*2+;֒+488D" mGKnR&;5/A2fG>xJ$I{^EtJDr&m HF͡w]W}b.h_&IG?r~_Rՠ>IRbyQ+<_M8HjjjdY;$IYsy|?<_z>sjk:8+ޡAI<%5hVF\q_uDƫJ~ǟxRl,MO~иcp/Yu:RLZQδ<5KZ&Gɗ+18c @dl=mvWi'R>ŋ_]m;;V5JS\F¾憥ŖyJ)je<ͷ$Ǔ>uQ + cZR)afyϾC?N'oXT*3 ]R2Xն4+w7'8rK [R’RY%IƘ1_yޘgyFx֭;^m< G<ϓmZxxzgr3xҲd%%˓,_HJzR$#%\),KQIWu~ L6 T]uϿn y~k%5'U#'yFjH`~tTJ3ޯxkZ]jk$I?~s}V-s;V;B-"6ٚ%UF}<{+lxc&_wnIR[[>wgtNEu͏[AOסF-i|.^'KiS'YLp/#IFcJ0QXT9:cuݯC<$Cgs>!pB!]wSvW-ɖf>^->|Ls|&ɟn,˒|I|oe3ceEgB 1*8|r1Feɲ-\ו=4ɨuۧh,=o8rNdٖmO=6 Er|W*;Qnyx^*Y S]]$9<ʕ+wٶ @˲,(u]% %I=S{=ڼɨL I/F{dIcˉlǒLYo|[%H}Fˌ:KP]7qEQE"9#˲n:qim߾=}w^aO%F0[-'(( Ɋ-_T5$+wmX}^GVTsx_SS:+Ȳ,y'cbeѳ>+c8B`00{d Iԅ-E_7jxV,̱$R 'Z rc>kmV4mڵk^ cKRoo֬Y#I o8 dM2BwdR3j3[(YQ֓+k#֑þ3(VPHJr]W*ɨGwݻukŊ8=/3'8 #cȄ,H*c}7^zQ}] 5,vٙ:Tc5JSFs?.%m[Cjjjyj׮]J&Vs0Y=,ǒXtڊsO]}ȓP&g|y_IcB!E"aE"=S >޽[3 2L p$~$KLh3[4?( 幮+6!W7FBgxt:_|QTJK.Ugg$SOM7ݤt:A <2ОXħQor$[221K&`v$/'yOJ yRV\^{Mo$i޼y ÒWϣ#9l<(it\XQV^V߿!?$G-^F'~6rJ矗$O*p]WtZt&1M1Y)|B.[$ɑG6$/>NTFTzF9Æ3$_W4UMM$d’,.""*K\{"\("裢F> >zEIf&o=Kye{z0If׫S]T9']:爈NW3=勈V6.EDDDDDD攱f%$iG FJǧ\(k| R)!B(3X5%K!7E_XT2Iͬ|yBlLH%yejzN8 *_FCFLr]VV3004JFϗ.?")vNqdI$8e툠lԫ^Riy t"RS] @5/D|6/7lQcGNE]ݔ!""""""rXV¥5:d2ZPYQށ 뛶rS8 DkjO:Pc C!0FcBC>rç:1ƐA/-`V E c6_>h WnvoigJ aR: """"""`0Ň ҁkJ1`Ǔ{/xsAoO[ay./1QV/ͣzw<.ԡT~o0$^T@DDDDDD1`UgeՕ z'93ߞ(;cMpΙ']6pL*0P<&+ݳ{|ijx䱧 { 9S#O<ȡS.ƽ<‘2|%uoC  [p1X//Xz#snlۗKl+*Hvvlſ ?~5!0orEDDDDDDvTeٻ` eC"giCdbD #醴mh4BF .$)|>?ׯ,qb&mc|++[4B3Ԁ1xײ~z|>?d*%pi<)8 """"""vV˖zx<H.x2;;rJ1`6UtuwܳϲhbzSטnl~r**my=r%n̚SN2g&ZTO#+WDDDDDDPмK8URJ%룹d] ‘0Kc[-4nƖW_*YPWG…#\zWfx᝼W Y#TQ>U9`Z[hkmyUHJLh[BA2T4Tq(!a[yU$`4J`4ZP\`ebc]h[pq0%SRu۲pEId0rm{DDDDDDDE}E's0`YӞ;:qaG#mwtc=ƽc3uI% i|9uE颋xWbOs3@qn#HR!,#褧>!H$4m"OGAg cw^9ulg[{w4eDfN>qu| 3;=H.~a """"""NB Iv||:;YY<8j1 J3)H^4[*L?z"(_zdfd"۸+ G?(E.}az:3-OPU]Fooc9ޅ1P;^zcFC`<7oS|;#ܷ=^b}J BG&55I{760r|1cp~|xOOu]DDDDDDD%f$=f Ŝpvl79ٻO~[Zqe>0rGF|жm;UՄ~90lڴh$m;A熏}d27ヌ~z{{]w.pØJ`6t4G1Km݂tC| tvvϢN9$UD|_c7\|owA$/>81D,^XOsk =\a(/+c,Z'2eO}r 7|S|榛Y4mwoV۲((' 0g`9CY4Bq3*DDDDDDDEm8Si~S֞DsqMF.- '&83rCeYX ,ww#׼*?и}wBEȡq6+hܱm |JȁakR ůRpۯ"3`1uc0yS`2|1&r [;{Lzf1Lf=3!]7ϟpcX"SL#sa~3pbٲr.Qa&&^P^L*o8w}\|Ab=ƀeFyO vx1.;LrT`0L\qαc=("""""""s@Oow ;%YqaTO@DDDDDDF >XƦ&4,lupgW[9v)3yx3Ͷ-,޸[D#aQ+g:""2ee\te.gF Y,H{^, 90ԍ1t.`ժI&nj1<&{z: lw?:D5. \G(0}c̸6K㞽B).r㰯u RDDdP``(8v}w äR.vbTVLorkO!=(`YIVs,gNu.{:}m{oK64 3|׻}npkeXb`2>}k(xw?+/Zc{5n"=i'{l\Y>ϱ6vx/|'kJ#""sC[{{[Zo/@&=m2 ۅ"`\i<,kY|U`(ySeZ̳P{-XpxaW^X{$ 䭗agn/ZQK*岯}ɒxGg;x27lG…~=l3|ᾏsuq=NO^\i~p|zz_qI ""؞f;XT_9˶'>44rC+p=sN2yy_.s^HY#2fU{K6?cD{W$W-ΛcaJ80޼>+҇*wgAą]#47H$_pJJKK+(+=$RI֑L$ \y $--X,\耔 5{6zm5k;p: ys!NzE1q~wݛʳYP`@DdN2D}R.wu8|EKN'`, u,"ўK D;% //d֗']?7uo5hX;:_uٵk'l²R1-" Һβe'u'TI&h޳םe<7ܕ`n0;ҿu%Oswݷ}kuyZ93G9+x&d,L"8.|rnѣyAQy\͖~kjg3}uED䐳ww#FL(+'##>yu !""tuuG(+ y=wgPa…ritmJhom}zzzIclذ׬a06Pϲ,"xT2Iw[&1Au7 Qj}m_\Oh+gx<=,?"""@EeKW+/:t5DDDdǡ.0 b⸞8!"(N(4'd (KϺ4Hp8< -ƞk}sp`ҁ!4ne3`4w>' msȜAj=WnmꛛS再VVto]}elث[DDDDDDDfA6?,J0;o*-"""""""SSw~񥷟eA^` 7I """"""2T]}0Q)ˬIENDB`muse-4.0.0/src/doc/html/split/documentation/no_audio.png000066400000000000000000000767031404105311000232750ustar00rootroot00000000000000PNG  IHDRi sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE fatal error83 IDATxw߳nrEE X * D#XQc5{{7&XP, (Uz88oNyfv ޗp3Ϸ>eSff,t+߉͍IkrQQQԱϿK1/}khW֞.P|>}"=m&]5ǡ8-G<45.D=D5i0$.3Gw9Ϝ$kM%ATPqm]Tp[sE;GYB%N&92JgrAIFzwDq۸6CStR=;ۃ&.).mֆd^'9L"uHl9h^9֧S}d t''DsJ7J Φ' eyreϡx̅tpV_YxF)1VweUu jql. Q?[;quiCz|Xgk7T#vWڳGYvJ,'-JBR!4a34n|} ptrnveK0öҦe[Z~*tp]H D]ߜ@^N(7 oZ;l /Emg%IIvo[߉]{wW0L@1GL&Yb%U[u[5>BA($5^]]uh׮~sTU%QЀ~"I26rY=g׉%yHyf6Y?[p:Βl@}`9̶x; fMMu碧*h ~S76FS(ilb;2[I|CżϦPS]K|V^KɮEH$+(--rڰ^ǯܦx,FuM ݺ7/kG0 I} B>!r=H$`jWZ\BN|;g.ee<]4eKUC# ̍VQ(kߞb.\@SX&W͢gcHs䃶k |]Ð$<ԓ"/d555 >\VOd 7of,^v__bG?@(T@mľ{M:SSB'KnltPsIJlL3JtCȘxס{r$+$x u2k Mduv^#jkVF%&`7 .r|mI*"[!/KOL{ I v!Yjkk),*  6pu\wD#);KҶ s94R_]5;!QW%O$+okV2hp q&1ߞp@{xa˖- 8HdR͚d"AΝX|JK|c-mMi ߲Oo*NޮoYw78m+-PH:U${Z:BH_ {5"e3`ﶇRL K@,Asw2m}'o[Yn֍2<^Pt_oƋ/љ*xܙG+XTۏIB"}1PA2Lpf [,2[#mW…D^F!c)!Jx`@ZхC)"rRgf6`,<ϜxhZR)7=UMeK%dktЁAX,͛޽;DΝ;|UUr el_z~t\z٥L}h*_ϚO>I,lԉeKҮXb[ Z[M̻P|4'j6m6*iZ޶} (D?ҥښDhUh4B(?DmMaùAgmYmwZ: yIjMHbqifn[Q91[\R:94W"uH3i±̫E2щc釾FՋY=MoAouߝux>b|\'@3=2d@x' :$1#ԏ@s8{r?Р2Q;<`Rf$?ܺΝ'ezU*_%ڣ8cJcßE?_~Ljgq^`@60U,[e+_ 7;RS|d7̺kbǙhnû7<Ͳ@$61.p-/dWfX҆mk7#㳹3wp5P̜y&vfi̷ECk̳݋F'^}O{1*Yvg L>I.# ,x&p#ӳq0\CII _~[kct-&J&#eXSh=܋7С&ض* 6jȳ?X(i.{al7|2=*l1(L ӟ4EQb8x6{?3H$DxcZ KG1^rH:yER^}!J S)()$֓[j'## |e􅍟IvaYީ,m>>m<{.NSO8MeSc']vz)$I}E2:KX1rۭ՗3[obr}2's ܲ-;Q]$1cͥwcv>|0򳏧u]}ABl؀&RMXzhĔ*+ާ[ۑ;\fY̧΍] 68o~c7|j]Nqha5+ EgַKv`w\SiISg5K˷|0$TX抍ifʥ<>s|ʫ煗_f5<ȣӦS*GQS[QAMU`>GY2_p}bQ?|>? ,]>Ei1 I"#!LM⨏{ eJY6e&Ӧ|k$1+hZMyޭ2Vc4D:sع{_9 dq9/gձ$pDʞHֱr::+n:~}V9^Y2ͻXQ)~$' |= w&S%aE^/FTzEFc}떮.aZX7 Z3~ڍ|3t(rܧЩe$|} ;clx^^۹3o*o8$}B\)J 𫮦K#n jmu/ٟ܌Pظ`^GɫpcW,cۖͼk}>[CƦ=z+y y_ ;3fdci0t|Q*l鏓!+ѡs~O~AAˑ_Pw͡~d!HRYBn?uhg'͗f?&}wnJo]#?ݦ3ʢm.jKÑ[)3t#u";_!B@2B}TK/V^7UFꈪ aͧRd1=' 7.> fc^b翞͈ a.ap8gޓ'dĨ Xܡ>v̻d { ۓ.xQ|O BØ41g&;;Ť m/&\BҎ@o>|={̺Cϥڛ|Ϙj ie Mwq"|cpdKx _pk )onϰdS>DZ$`_'Or lUSSW3>a* ~'1-R}'0ߍܛښl\XMٴ';`R^^_4c% o(~]te]/%AQP\O]Qǟ-+6G5b(*,dȮCǢ$b1'$ϼFc6|7w.όć =U"$Z6ɵ%LkJi&?{AJor"2MŞ騒8fG:ՍZ: "HLv9_>X8*@çYƉEcO0"HSQbq+-5A, )Z hQ^'$"t><~MGqb8'/ dQ䅂t} "-W& /#} ,9uIeɑ/d}S|]Dsxo|9/&/|IUUUP"4~):~9аq=?ӰqWW󈄣ntMd9O}Gv݈)GY_9Ѡg] J\Ak ;@}DP_KS >Baa!y++Yp1`E%I 7ld 8h0ݺu`Y|, 7_<ww'%6x`lc|%}d݀vO$t)DD*uI~?}> uojFе@ B gaz6Ulbݺ %% 2]Q^^!F;zi"70ˈL"SgHqsk+֤=BqdsAՠ/ Y@߼ՠ ԭYFZ_X3r?N|fIrϋ So0sjefYL.q`wx]َ\>6P^=(.,fƟW{Os`0H=(*.fÆ lڴkטl<.]R޾G̱zc%o_KFRH dcufگ546 GH$AB! :f2G*86{n,2U_n3s RIƥ麂7!#R|~r694vo0HQ6gۮzY,<66;t˭53 ? ]^-)6k)q?p5 s1qm#~eTuU@$!Od(ʼnrzBU=Y[e+c0'gbSkیbLc 8hO3Uae@802Pw ai KG`;ye!dh%k͍WM#iݼKdrnmY u 7_xo9cXNu]c W)եu <="<qz-(oSm*[$DJAղ涸]A#S k'^ gn `Aq[wꊄ̹9^x M*<`\wMܵvZ%|/ պ#{JxƷ ,av 笣#, O#o/<͗e0Uƍor'622 f7+AH ./\|(bw/L6"k4wh'6Os;5/{2薭w߶NBΗ0-KUr;;H4'wGb0ۀʛw;vF5{}@AK.trTktmn7jʒd3m:BvBx̅A7p3 e fsEao>p'Ƕ$Sj~t 8'2 IDATB|5s1@gw9ptvn%ֻYF@P$o*^e%yaɜlә:ԩqұlcrY2 ,gkI_*ch*]f f \ >2pR =A_ߎNsL/x3E΀Nr"p4 dDq1@`82?-',6qAMiv#Mu-%0$G*gq{洹]z6Zvx~ =o4wRs{3mp`ǙͿ}fY:7x4 Aܲ~%fvVYm5 {?x8-sGw%C\гq g?_0L*^X/H5K=Uo(}}gZSFtU}_.oM.Kh> Y}fvZU^^[Oq7.o,nrk!n/-}3]xd?W= 8I r]y$Ie2mOKd+_o=JיUf<#(ǷM=ĕfўWfSoiݫgwM~[ -^: s`*g)Wvs~އRW; .g佽&'3Hyn'M̙5ۺMH M+e9SڄY5Ei@ՆMS+=vH^ElS-: 9{O݁]Kr(csiŹ4ex+e~Ng 0H4Rz?k+e%o6oa+` FffH--sĦ9yǺ7K-1{$Oz\EEz6p4w),UWq8n Xa.eKm~);ƦZi`ȧc'k: Qo6p7vkY1zP MeK`7mopw\ݴԯu#L?'gM|hi ]W.1bÃ)XZSsth=0y&uQ,jcZ/إRM4o?2 w#;QΠo怾A):Iv~7]Yep@0waIqкlVrOzUX"ۣ#6-th_ 1Djѳ' blzodŧոANdlEШ:%:D(ѦLԖنl#G=zB:0Ԥ}MQ)jٕ2Vi}5FժŬ|1ut-3xY,ډ#T7g٨sËw3.蝍1b^x}&Q"Lji?Ga;Pd~B[$ؓ1;W0让=?=kƂ,i^%lh+Y˦:\DAH5پ<=EѐeԲy#[2aex6PUH-J a]jiٹU>;eYްݦދUa)ϖ'9 h4ܐь]81`5WYLs,-ng`Q/c =˩iPjj3Ȧ$c1|~?߯R=C4/ ϧikKq9kLdRKKdR_Lmh Y0F:۷A݃#'J|ޫgٽX2c&%LG?'w1(A'wr-өBF .3;J$l&\/憷bx莧8Pe&u'DafJg87u .ˉu xxq:}|\~!+g0wNEOxfr_M"Pٕ=+n< 4ït;_=N~c_?=c2?#];Du7XE+{qWrNYSow%|yH?s_-~?\\vg?,E+zɿΘzM{vj3#D#Vv)/?p8@̻ϝ w\3d5/~/m3CJ<:xz~`}'o.vrÚY{3ɸGߒ􊢠.YF(w PT_(@a!"EEX..&$7Rʴ}{B#}4O5t߁BB!|ݛ=P7T,~~t] ]>Jb\t}].o'1| w^.wVb*t'P;j]{'/o';Aezu2=ݧ(n\۱Tnʸ<ےaqiIM-v+-%m{ر/SbUk֐_VcGu⢦?؍ ۠A, BϼMi0ރCsC ,7ɨ;E{/ > ܗ'>aEd 'kO$*>YО?ڗ?O>ޟ͒w~BEXͺkF 97xof}yOlK/ݏ69tt|vE<4|^4>I>LyE*}<74:Ͼ8_},a-2|gЏCB?~do|҅ 7'FV?nd۱p.G"yZ?GW`10('GtCx®rB|el K^y'WnJXi7jt(YT,:%ffr7ώRyƮٗʝ]WWk(b+WHٶ[P޽)?VϜIaj;Ր ɹ,t3On0{!L1wu'(*6CoB G\u kWFB ߆)TPox Ыa|=Ј?qYatf]HԱ`9p0T0@ >ShԷ$*$_U DјpRS"Hs_e!͆P!:*!)JP J4f/]%YӰǩLG K__Hq9gA̅ *j)e?A TLBET*SV!Y$ugWլ(}(kS<|h浇zpE7s6~/:@h23J2*,ڍzdg{ 4A7- AZd\W/ʈѶ_lK63|FVOƒe_\~*>v='d|JB,^RcyL-sqrgY3gp`UD"|yu/w9.ȼW^d)>ٽXɇ-;S5o:?[Ʋ>d%돇'O%/N>˴8 tcFmj%s3~SF{ Q`˒*ُ<@2ޝ9r8褧X7ii48gUwgȮnvmT`ä]?m%Ffw#{/V[KmaDG\}a۶PYI6[?`Q;Ѝt%@FI{pޅ{𶓘p̕L+K}4o|?}G&߶=.<= S6qQr!Rlp(qt TubܛH)aKn}x0. 0pbk?} J'_y-^w WϢ|߹(V"4Ig[VSNbqS9H9n8AꏯNq#?]8[a?1Wy7%麳>QAΥLgq֣[z?ľYQߓnGrRog_?:h6v'\w [D×\|%l[̛ N깂F) tmb?#vS{'[ =0'q><?OJÜ9bs<ӬTz >4JO2+h{kOGf72;=)49FwKͰ+phX2JG6e=5VW#H/;jiN9NWUΆc j 3p Ӧ ˥4Y-9ie?UͥKCUA=+-g˛n@zh"xznvZm^-ujm<#)V,馴f ''c1nԂo:UY^>òIAI>Com6-9i4i] MڢNhsGhD,L$L]Z _= @(J3QܟBz:cC-mٚF LM_.{υ nɥgNrxٹY-vz.[Ľk~Z @DZurݍ QSQb$TUUӠ;,} A"$L|S kKKj"т-E2 My7n?}ཱུύ#ίsygYC˂pze?M67$IzLDQ,yyR- b4Yr(> [A4᝹$mA)Mg)0hތݮBqH5CiPh>>FhՄkg|FQH ,G"#/СrU$TzѨMǤmh'@{(,d嗓HD" DH QS^~qdyZgDNd=էXwc;$!~9Іs!{v,bhm'͉vpJgH=D1e eÆvuDIF[#nh^'qȴ a!D @"%AO2rAas[s 8 aPX rVUI-c%H櫥vv)04ƥ-S>BL$}i^=o/d4JqQ@ m'ajx4j=EE \{-j$?zL~3۰Ğbr6M#Uwb0[o56ʐЋ, IDATkm'`zi&mF.? Pc1T}l8o«$hާJ#KJ{hH$!jvqZ]MSO;1HfߦWOWM$J[k֜[;qd<1.:rS^ch n~ڨ\Hj3zU%OE~UE{Fwj6x8RZTD$c[m-=e(*bM7ySU3z=~} 0coRR0mGrh#fVzخf[c?׬oi֤T?2^ˑ X:[[fΤ>x]~Y<o4O$ ycJ=ؓ;Z f%;hc礬Y@s{-ٷQ5R?jr~.,[tW |Jܹ̿f7PY|dKs\?OPQN$@j[ z]m_eV^ϲ$yYK;sєV\j6Hv9JQh0q^AE{uYڷ/駏>bPRTD =lg̮fn2(e|hU6}7h{'/͙쫮b[nsu|һ]q1L"22tȥohB6n6J:j{j -9̗D4 O b% usͥ2;=Z,N:PA gjj2^mWZŴ1JmHH}ڣo-a<}[?H&!O$ B@i^9sP%z+|-|3C!J~WIcO"DLv 7#mFm0}KަYq8d7#*d @(Jf@CߓWɬpD8aRQJ E>0?mޗQw!1Dϧf y19 yyT ;l8)-Z$b:e*o V}~yu/'w7-H^7Wl^=(TB:*¥(kzl7^.=[[y$FJn63Srl=h ˳v^4bؚTLo_g]FH8]j H\Dꄠ@^Uqm ;ˠ6/UEozzLJ5?mpbZRIְr.8ZECBɟ0w[)*>1WsAT.t$&ɟNFRPXc#w"%<<2m~8 N>ZO/U'sPX;4l' ;dIȗZ"Fs)2a⑜xT>3?&3a\b z)|- q9 ,T} 5cm;ڬK,u{\tQyciBw[l s[y[881K9&7zr)rZn_ns e5SJVvlCzRus}Iܺt#.a!h?e }o3ȿ|y<3ago?v,z|0 :/D:N hrэ,ڑvFibIPu~U|>~Dd=s7w`xm)gX|DxǡWYΰX+3b7yv>V{=X4,~kZDݖQLsC=̰LXu]Wodd!hE&.'0e^"==/>{?RmF92kreu}#xDjKS2ֺM;f+xWyXs,R&r=xSݽk^&._Fn-JT-e}~zu{qO. Xy\ȚAR\rrwovfDZyyjO$Pn?_7MJl)_F(|H+bsMȪ/YA J,g҇~$kWRm;7Lާ;%Y< ƬoW׽PT؉'IST.yڟ٧g)||jj̡H652q9깔eI _?E'^佻QR؁OO߰6Ҝrb|_ݏI݋ ^@QGt7gUDXx<2e3:<=عP+2/uF.m9Pb bxhw`&Ыz11ՙu/eޙ7Ue^6IBee7qw\`dpQwGGP72+( (eVB)]4M~$iymRJ=_>$}{w߽I- M4vԊa:5Plf=4 7{2KH?n a ޽Цռ?7εkq9UZNNicԪl3v{j8= G:vA$+^[!NS ъpǜ, Qh*<6I$+ ۯL9ЛmgHСwI}p?ebTl4c\5?@mee\}5|ݞ (3,E!((ѣ8ۣ?y?G_MygcQnRisjyI>Dr ,f+֬XkyGJFs$^{2l_M 6 .qF;%P'KUNsF=?u5Y*.ҽ1ACDz)qYH4lf1uI!ˈ&oh3 m8@||ޘ%3']c}mXN>Uǖs>b'W`m9y^]WtىXM:W+#V!y={e^t#LzޝEGVAY_Ǝ0hs]Ct.C)q2i8TcZ|l[1)ҼkÎX|=\P) S_zכ)MNhCv+mg'Q88^/C TXV70`ߛEe c='Lt( ]њF0RQHa1 ~D{ T{Z{i׭ $Ga "Rv S<$FgV|eq!+ctFt:̗lÍymQЁ$Ch#ջNABtvxfiWP_̶ȌLQwS iY1ݎs8~!/M[CbNg@\|a[ {y_1'NRƮ-vh[gƮ듆ZZGUҼ+|>-3$}?}M1| RMW9kHڧܞu^jeTWqc9peɬ癝ӊa]cA&QmJl0E=3_;9%zzu1dݾO,sh퇼s &,nZHઋ1`2ԱsB{ضt/){Xg9#q₤ t辡C4{%x5(nJرϛW5il8ܹWk~UvdW2h|ڴA)?ѧ](Fw/x{b#Ԟgp×=,2KCNiT]-Sr',yyF7fND9OS:Ndzg.%+=9UCq}׳wKbZeqָqu'#(9bsS7űu4p#P23Q-84'k*Rp]׳q^1,3NkUh]TMWek'7ݯ~%ѷ]:VwR9Dn\}UG&OϗI=9_׻mbhDžw]֗8dH6&>j;%)ϚĄѥ(؆qۢU7F<Ӫ7qS]`+ -[poB;E!(x|=ysUQ}U`*rrHR_9jJcA_ G}0^i5êwщèA^]ƁˮCKF? =1S#C{nͻE?Ws7 A*ukǮ}|?2\[QpxUAjy݃;^.cǀ3}jwQ9reUM"p!} hԐ}~:_9 .j/z}^qb|Aw農\ٛKWr=e-U\1:_ 7z. wAey7T8/  7c+U޿zNe*{;i޽BP|双yO>0W4{VAf[ۏgk^A> {{ m;nȝo/޿]4 ~G8tAA{y7Qe^A8a{Հltbj`^' ]`o'`^v*W.ԣT͞rUc|PQF5E٨ ‰HdFV:˾Ĭa5T2&'C@ߗ=O0ll<=u_j^33^#8vA&'`^Qmًw5žM*cMܵN{>hzоas/ه#pVWC/c h(J5F_+T ‰F`qY\>ݓҼd9bv')!eA y|j8^xH< 2;w#P١QA8!ǯ_ZQ6u-fgphf S?KM ɹkZ:7Mee U6tANLk{7NUST}Ngp[]ǍՋdꁢ Ae^A8q Z^xx?C~A{M|cAeأܗUA_LNt  Hvk  4{AAhaxgë' BA FA]$Tbe8t:k\F'\W5ӓRPw]9Ҟ8_#ʬ_6V{4FOk!>[1*3i&N>|C :f\G;_Gc#3&drc +E#١uĞBxˎ} M?'CM_CM!m" <6Z&0jv8rQvi|$2h:OC|A}Vo{;mrl64͜X+b!2T$q򝓸}d F*O`eoe2W|8sxw;$=4K5'.ߤ- ׾OR)?76>獷?}60c{tcY:_N:=3IDAT]ƮOz;}& `v>ww5Mk!Xw”7 S2O7b zǩZͼovC<]5 SO^ɧƓܛﺋ˻Ǣh:~@-ᱱis\̃c{+/(Zm+ci0Q%sxe"rJԪ'x'WO@3g=&i @1~zZxhl 8Z17;=D;|rZ]O&ώrՖ]X(\YM%lΣWc6vn Kxq\kVGe]CojZ kfyݷrf]K} A r21wlm^{yY3eȱד/4ie7'ޝɋW[ͅ쪨t˯E&fg~9?'qJ^eEQҮa8LG#ձpk^{^8'#H|>gdz=D*;&w,*òLVW#o}̘=7yfCf5{MĒ糽xǒZGG/a띧Du 9#iot =7-Dspŏ>w0}82BG~X{w1Vwq_ٛ)tB:@#c_z{F!h!})J{")˧ct&^46,AXLfZu nj WF^np&j$b6-g6]xN;=fsC.=:9`6I;nfk,LnIw/Y:nj#ԡ]k,SOfԱZ QmA$&Z+Gfm_kO 'sȅl ?S.{3O{b;.7 Ro'9 0Y1*ЧS1_9YˍMtGR|z{` ->5]5D|~Cκr;/ҞKWul&96WcSab%<-'gZQ4poon^>{w.,fjeh(1hP^]jg2BlodڭeڋҊZ(pD3b 3ENzȷXJ~j)_W}|+Ys\ڥ/|9zxjYT&.έ!S55Mka4Y{ha8B2O.>xND3M(mڑ֪ 3"(7X[~@…%C-9jcK Wo"k I?y7: <3u.}u Y%(q]1K~ؓ<.)n5K'y7\>7Ys9҃oLbtnUќ6ܖ z6ҷ1LBr>(cV l:r188\nW-rL霜Uʢs泯S;'S/_5{gd 6l;B_^Ί0+VLFalnvCwhgŷwPT^Υ8ڛZd6V;7FzeNx' )iSZb_JNǓ\8lX4>ؕ1(Q w?nÍYHMgԬgQ6]/GR%o+TL <ۏZ[(؁Ǐ?1{Vޖt1ڷ7N+YS1hFR $IŠݝbpxkǾ0r-gPV̐p]SYtN#L| ⿚ £99綋hÖ_GwUGVC赫| w\9Zd6V;7FzeC$eygYYրޞ`L~:9%݅/ !yYqoeM7p͍Ts6iw+{Ǔ8E A9R`iEd^AZuia-|AANBGy 2tA.#zAAhQC(Ͽ#y8}5O+Meչ.DnCڨ)PL?{6M~^O~f^\rm\-H-ryDnʻ7^2}hv/ο羫zD} τKW0Njby[}|ιΝ}|?Ei*vÓ~c/pc(\2nyr wip3O(_ѡu={ơn_3,٭XƩ<wS߽ [F`[ރG&ۨiC{⽥;2~^l߸v-Zſ*j2cl$_,ߙΔK,9}dY{p1 8cx3gN繱h>Ylnu-O?|Bzllט|YSrx-5S氡9aXNG}=66|!?3xnNqGKЫo]Y7km_Lw9zW$5KOpT MOv \ƚMEx47G6(%l>KptZ]v -tk#mw=j:ϱCx`|S{>⎗jkme06 fC˻s"0mTpSO moׇXm $ͺ{76'-bIax3P0gs/VfM8QΈ$%*: _aŨBR@ WAWmng ,f!UyqcŖh^Q2v^\Q8~1ooZLq:bľ$|opf 9zk ~A٨.l]k}ؽ~ ŰyU!kO^=V Kz:DRH]OImkyU^i=+GE>5F>_ӵ\жoc5Cxۭ\yI| àI'5Uz5vk7x&1֜yۍ{}kJQFwT[x;? wg]9\&6:'Ze^G6=)q#hJ!x8bW0Tě:_L{_mEU3ianP-CRN*X3o1v|\K~f~KcDN:G%'/IkjzadԵJT5f{FOh=zVF mH㶑փa}[8% }-VzTaH :gWtx~>UvG ܋Am^1ԝ;'=̰ؐCRXUܶh K-DS~y-2]-v~;'>}kէv[Ws _Ȳz%Yt {sm5e~";v/د j ^{Ct 1u0#%?v(Wn_5UzMԪJȃL_Jٌ(RNQYwU΂ fPx~G@ V6` y1ʁCe6.. L޽ŋs}^g?Jgx7'sp[|vUԤCgk *î`x'oW!uGזrBSZUWϲӕc{GE6ITkE?Np_jzGzGbψHbtqokUPݎUcLadr0|iTY[]):E41Ԟ o87ޙmoDF2^yُNҕfΌ"~yWJThX^y6[f\}/|nRSp:^:eTT혴Q >~Gr P<  c&{GAAhHA4OՍ{AANpWS В?X~Ae«AAAhQ@7    (۽=z " x^A0t/A^AZ&Mm$ qqUT~r h2 J++5@  S\.+231LwnWr'{wnh4n ʬ{A9r1# x$ Fbu\ zL{AQZRB!CJp\[&qbBνnrpVSn;>zdS䮻 "xֽyAyG_OMY<̘|n>~oMa$^sCÒTC `ǂ-EHz#WޣאjA'@?u7orSEV^@91"9C/s6D/k,ܲ x/=<4^Wx BsFE<r3$$X9wuo㏾k/י˲.2SՏ).,_Fn.ƎӘ[z>I,u?5 B%?ouyz%:TW?⃗ѹc[2 Udtq߷#N|#< YMړ/λM"Q^Ahh]Կs*kVЁCltŦ{IRb\v?~=Oy!=2 ׅJM\N*|Oh 4;;NcN៸.s)og[ߣ B30]ןOkٺm#'u)JkSQ0Rxزq OWzuP7D:$)rĨA<"\I4xTx9,*goD-OOwL[AzAx"J :c_hLQID[pؘ6>=:]!UvrV;V~x7I3xYn=}bQ^EhZ6$  4qt'${bRR2;ucEN  ;;W/$wW窼jn!_]'ry٦VAhD2tȔ1#7]~'/act z/%a\5j't44x]@l5w͝#1u*&45|1r ]M8Q P\.9vqEwz4Jiѱ5hzdAcj!=-ۯ))-=*5 q6- ~#ݽN"  Q8L$'p8JMl&6JٌZf^ B3EU ff7EQa { Bsq5pe<<ʥAAh.jGpz9 Вj UADSɍ渑ʚ㭏  )5Ƹ `RbqVKAYQ+ܣ?Akx( BU֔Q=<3TjX%iPAdt_GOXIENDB`muse-4.0.0/src/doc/html/split/documentation/node1.html000066400000000000000000000107441404105311000226570ustar00rootroot00000000000000 Contents

Contents



muse-4.0.0/src/doc/html/split/documentation/node10.html000066400000000000000000000210711404105311000227320ustar00rootroot00000000000000 Automation Subsections


Automation

Automation is the ability to record (or construct) and playback exact sequences of control movements.

MIDI and audio automation are each currently uniquely different, but share some similarities.


Audio automation

Almost all graphical audio controls in MusE can be automated.

This includes an audio track's volume and pan, and the controls of any plugins in the effects rack, and if the track is a synthesizer track, all of the synth's controls.

Each control has a manual adjustment value. This value is shown when there is no automation data at all, or automation has been disabled.

For plugin and synth controls, it is usually more desirable to manipulate automation with the generic plugin GUIs, because MusE has full control over their behaviour. (See plugin GUIs 2.6.1).

There are a few ways to enter audio automation data:

  • By adjusting audio controls while the transport is rolling. MusE will record the exact movements.
  • By adjusting audio controls while the transport is stopped, at different transport positions. TOUCH mode allows this.
  • By right-clicking any audio control and choosing an operation from the automation popup menu. This includes storing, erasing, and clearing automation events, and seeking the next or previous event.
  • By drawing the data on the audio track's automation graphs. (See track automation 2.3.1).

Audio automation modes

Each audio track strip has an automation mode button at the bottom. There are four automation modes:
OFF:
Disables all automation, uses manual value always.
READ:
Automation data is applied to controls. If any automation data exists, the manual value is overridden and has no effect.
TOUCH:
Allows you to alter a control at any time, while transport is stopped or rolling, If rolling, when the control is released it returns to reading from automation data.
WRITE:
Allows to adjust an initial value before rolling the transport. While rolling, when the control is released it does not return to reading from automation data.
Here is a screenshot of automation WRITE mode, and some automation data, with the track pane automation popup menu showing (see track automation 2.3.1):
Figure 2.11: Audio automation graphs
Image main_window_with_automation

Midi automation

MIDI automation is a slightly different concept: Unlike audio automation, currently there is no automation 'mode' and it doesn't record graphical control movements. Data is viewed from within the pianoroll and drum editors, by clicking on the 'Ctrl' button on those canvases.

Similar to audio controls, each midi control has a manual adjustment value. This value is overridden when there is midi automation data.

There are a few ways to enter MIDI automation data:

  • By adjusting external MIDI controls (such as a midi keyboard pitch or modulation wheel) while the transport is rolling and both the transport and midi track are in record mode. MusE will record the exact movements. As mentioned earlier, note that graphical control movements are not recorded.
  • By right-clicking any midi control and choosing an operation from the automation popup menu. This includes storing and erasing automation events.
  • By adjusting volume, pan, bank or program boxes in the midi trackinfo panel and clicking the corresponding volume, pan, or program buttons. (See midi trackinfo 2.3.1).
  • By drawing the data on a midi part's automation graphs.
Here is a screen shot of a midi track, containing a midi part which has been opened with the pianoroll editor and automation data showing.

The 'Ctrl' popup menu (bottom left) shows available midi controllers and the green dot indicates there is some data.

Figure 2.12: MIDI automation graphs
Image main_window_with_midi_automation

muse-4.0.0/src/doc/html/split/documentation/node11.html000066400000000000000000000206311404105311000227340ustar00rootroot00000000000000 Configuration Subsections

Configuration

MIDI ports

MIDI ports provide an abstraction layer for your MIDI hardware and synthesizers (which can be both software and hardware synthesizers), and other MIDI applications. Port are numbered. In order to produce sound, each MIDI track is assigned to exactly one MIDI port, to which the MIDI events are then sent.

The advantage of this abstraction layer is that if your system changes, for example you change MIDI hardware, then you need only modify the ports instead of all the tracks using those ports. This is similar to the audio input and output track abstraction to the outside world.

MIDI port configuration

In the midi/softsynth configuration menu, you must map the port numbers to the actual devices (by selecting ALSA or jack midi ports, or synth plugins).

Try left-clicking on the "Ports" column of some MIDI track. If you use a soft synth, right-clicking the Ports column of the synth or any track using the synth lets you launch the synth's GUI.

Figure 2.13: Midi configuration window
Image midi_config_window

Columns in the MIDI configuration ports list:

GUI:
For synthesizer devices, indicates if a gui is available and if it is showing. Click to show.
I:
If present, the port can accept MIDI input. Click to enable or disable it.
O:
If present, the port can send MIDI output. Click to enable or disable it.
Instrument:
Selects the instrument to be used when MIDI is played through the port.
Device name:
Selects or creates a MIDI device assigned to the port. These can be Jack MIDI devices or ALSA MIDI devices (if ALSA is enabled), or soft synthesizers. Jack MIDI devices are created by selecting Create Jack Device from the Device name drop-down menu. Jack MIDI devices can be renamed as you wish by clicking the device name. Soft synthesizers are created by clicking in the soft synthesizer list and then Add Instance. Or you can simply create a new synthesizer track from the arranger track list, or even the mixer menus.
In and Out routes:
These are for Jack MIDI devices, they are the routes to and from available Jack MIDI ports. Jack may provide different alias names for these ports, you can select which alias is shown.
Default in channels:
Auto-connect these port channels to new midi or drum tracks.
Default out channel:
Auto-connect new midi or drum tracks to this channel on the port.
State:
Indicates the state of the port including any errors opening it.

Global settings

Audio settings

Minimum control period

Plugins can usually process an arbitrarily small (or large) amount of samples. If some plugin control value changes continuously, to provide ideal listening experience, MusE would need to call the plugin 44100 times a second, asking for one single value at a time. With the minimum control period setting, the user can force MusE to ask the plugin for at least N values. Setting this value to 64 would in this situation make MusE call the plugin $689=\frac{44100}{64})$ times a second, asking for 64 values at a time. While doing this will reduce accuracy of control changes, it may also reduce CPU usage, because calling the plugin more often, requesting smaller chunks, is more expensive than calling it seldomly, requesting larger chunks.
Recommendation
If you have no performance problems, or if you want to do the final downmix of your project, set this to a low value. If you're experiencing performance problems, increasing this value might help.

muse-4.0.0/src/doc/html/split/documentation/node12.html000066400000000000000000000044761404105311000227460ustar00rootroot00000000000000 Appendix


Appendix



Subsections

muse-4.0.0/src/doc/html/split/documentation/node13.html000066400000000000000000000153361404105311000227440ustar00rootroot00000000000000 Understanding the effects rack

Understanding the effects rack

One must carefully consider how many audio inputs and outputs a plugin has, and how may channels the particular audio track has (1 mono or 2 stereo), and how MusE uses the plugins in the rack.

MusE will try to internally create as many independent copies (instances) of a plugin as necessary, to satisfy the number of channels in the audio track. Basically it divides the number of track channels by the number of plugin audio inputs or outputs to determine how many copies to make. First it examines the number of plugin audio outputs, and if there are none, it will examine the number of audio inputs, and if there are none, it will simply use just one plugin copy.

For mono tracks with plugins having more than one audio input or output, MusE uses the first input or output and ignores the rest.

For stereo tracks:

plugin inputs outputs copies track in route channels track out route channels
0 0 1 0 0
0 1 2 0 2
0 >=2 1 0 2
1 0 2 2 0
1 1 2 2 2
1 >=2 1 1 (L only) 2
>=2 0 1 2 0
>=2 1 2 2 2
>=2 >=2 1 2 2

Notice that on a stereo track with a plugin having one audio input and two audio outputs, only the first track input route channel is used (left only).

These same rules apply to inter-plugin audio when more than one plugin is in the rack chain. Extra audio outputs of one plugin may be ignored by the next plugin if not used.

Currently specialized plugins with many inputs and/or outputs are not really useful in MusE.

Nor are so-called 'realtime' control plugins which use audio inputs and outputs for control signals.

Loud noise alert! Beware of using such plugins in an audio effects rack.

Example: Consider a stereo Audio Input track with these effect rack LADSPA plugins:

  • comb_splitter Comb Splitter by Steve Harris
  • tap_stereo_echo Tap Stereo Echo by Tom Szilagyi

The Comb Splitter has one audio input and two audio outputs. The Stereo Echo has two audio inputs and two audio outputs.

The stereo Audio Input track will therefore ignore its second input route connection. It will process the left input only, separating it into stereo with the Comb Splitter, passing the split stereo signal into the Stereo Echo, finally producing stereo output available at the Audio Input track's output routes.

One improvement would be not creating unused redundant plugin copies between plugins in stereo tracks. For example, for a plugin having one audio input and one audio output, feeding a plugin having one audio input and two audio outputs, the extra copy of the first plugin is redundant and not required, but currently it is created anyway.

muse-4.0.0/src/doc/html/split/documentation/node2.html000066400000000000000000000050711404105311000226550ustar00rootroot00000000000000 What is this?

What is this?

You are, if you have printed this document, holding in your hand the written documentation for the audio and midi sequencer MusE version 2.
http://www.muse-sequencer.org is MusE's home on the internet where everything MusE related should be possible to find, software, this documentation, forums, mailing lists, bug reporting, FAQs. If you have this document but not the software head on over there to find what it's all about.

muse-4.0.0/src/doc/html/split/documentation/node3.html000066400000000000000000000172711404105311000226630ustar00rootroot00000000000000 User's manual

User's manual



Subsections

muse-4.0.0/src/doc/html/split/documentation/node4.html000066400000000000000000000535221404105311000226630ustar00rootroot00000000000000 Introduction Subsections

Introduction

A brief history of computer audio and MusE

To quickly summarize over a decades open source development: in 1999 Werner Schweer released the first version of MusE, muse-0.0.1.tar.gz, in it's first few releases (actually not few, Werner relentlessly churned out new releases) MusE was only a midi sequencer. The target was to create a fully fledged midi sequencer for the Linux operating system. Over the years audio was added among with other things implemented and sometimes abandoned. Today MusE is a stable and feature rich music creation environment which strives to encompass most of the music recording process, creation, editing, mastering.

Definitions

CTRL refers to the control key on the keyboard, e.g. CTRL+C means to press and hold the control key while pressing the c key. Make sure you know where you have it so you won't accidentally lose control (bad jokes are the best jokes, so say we all!).
SHIFT refers to the shift key on the keyboard, see above for usage
ALT refers to the alt key on the keyboard, see above for usage
$> is used as a generic definition for a terminal prompt. When the manual lists a command that shall be typed, the prompt is not part of the command.
Keys are always referred to in bold uppercase, e.g. A. For instance SHIFT+A for the key a pressed together with the shift key.
Sometimes terminal examples are written tabbed in with a fixed font to visualize more closely what something looks like on the screen. E.g.
$> muse2

Getting up and running for impatient people

Install MusE from the repository of your chosen distribution. To get decent performance start http://jackaudio.org/Jack with the following command in a terminal:
$> jackd -d alsa -d hw:0 -p 256
Or, if you prefer, use the launcher utility http://qjackctl.sourceforge.net/QJackCtl to get some help starting Jack. After this, start MusE from the menu or fire up another terminal and type

muse2.
If this didn't work out read on for the slightly more complete route for getting things started.

Getting up and running

Installation from binaries

There are several ways to install MusE depending on your situation. The most convenient way is to install a prepackaged version from your chosen distribution. The drawback of this is that it may not be the most recent version, though often there is a more recent package from a private packager.

Installation from source

Building MusE from source is not hard, there are a number of prerequistes that must be met but the actual building should be painless (ha, famous last words).
Please follow the README in the source package and/or read the instructions on the homepage: http://muse-sequencer.org/index.php/Installation

Hardware

MusE on the Linux platform supports midi through ALSA and Jack-midi and audio through Jack. For information on what hardware is supported there are some convenient places to check: Also, as is often a very good approach for Linux and open source, the various forums available on the internet often contain good information. Chances are someone has already tried your configuration and/or had your specific problem and the solution is already written down.

Launching

After installation the binary muse2 is installed on the computer. If MusE was installed from a distribution repository the binary may have a different name depending on the distribution policies. Most distributions do however install a menu entry so MusE should be conveniently available from there.

Audio preconditions

In the standard case MusE expects to find and connect to the Jack audio server http://jackaudio.org. Make sure jack is installed (if MusE was installed with a distribution-package Jack will very likely already be installed) For Jack to run with best performance your system should be sufficiently tuned to allow it to run with realtime capabilities. The realtime configuration is configuration of the operating system and roughly consists of two parts.
  1. By default on most distros only the superuser lets applications setup realtime capabilities. Please see the APPENDIX for setting up realtime
  2. Maximizing performance. A standard linux installation may not able to reach the performance required by a power user. This requires exchanging the linux kernel for a so called lowlatency kernel, this is also covered by the realtime APPENDIX.

Running MusE

Find MusE in the menu or open a terminal and enter muse2.

$> muse2
A splash screen should pop up followed by the main application window and you are off!
If an error like the screenshot below pops up the Jack audio server is either not running or started as a different user than what you are trying to start MusE as.

Figure 2.1: Jack server missing
Image no_audio

Midi only

MusE can be started in Midi-only mode where MusE does not have any external dependencies apart from ALSA midi. In this case start MusE from a terminal: $> muse2 -a

ALSA midi with Jack

If Jack is running, by default MusE will not use ALSA devices, preferring Jack midi instead. To force ALSA devices to be used as well as Jack midi, start MusE with the -A option: $> muse2 -A

Beginners tutorial

To get a quick grip of what MusE can achieve please follow this beginners tutorial.

Midi Setup

First off, fire up MusE as was described in the previous chapter, making sure that the jack audio server is started with sufficient configuration to allow for audio output without breakup. Also make sure your system can make sound.

Soft synth test

With MusE up and running right click in the Track-pane (see Fig. 2.8) and select Add Synth > MESS > vam soft synth. A Soft Synth track called vam-0 should appear as well as a separate GUI for the synthesizer.

Now right click once more in the Track-pane and select Add Midi Track. Another track appears called Track 1, and its track list Port column should show it is bound to the synth that was just created vam-0. If it is not, click on the Track 1 Port column to open a drop-down list of available devices and choose vam-0.

r0.05 Image arrow_tool
Now select the drawing tool icon from the toolbar, alternatively press the shortcut key D. Move the mouse over to the arranger canvas as referenced in Fig. 2.8 and point at the midi track, the mouse should have changed to a small pencil. Draw a Part along the midi track using the mouse. For this exercise it is not important where or how large the drawn Part is. When you are done double click on the drawn part. This will open up the Piano Roll editor. To the left of the Piano Roll there are piano keys in a vertical line, try clicking on the keys in this virtual keyboard each click should be rewarded with a synth sound (maybe of questionable quality, a sound nevertheless)
Figure 2.2: vam synthesizer
Image vam_synth

Missing sound

If you got sound from the previous exercise you can carry on to the next, or keep reading for further enlightenment in case you come upon trouble later on. If there is no sound we need to do some fault hunting. First off, click on Arranger window once more and select the vam-0 track in the track-pane.
Figure 2.3: Midi editor view
Image main_window_with_midi_editor_vam
Now bring back Piano Roll window and align the windows so you can see the piano keys as well as the Meter on the Mixer Strip (see the 5 Function by function chapter for more information on these windows). The result should be something like the following:

When pressing one of the keys on virtual Keyboard the Meter on the Mixer Strip should light up in green to visualize that the Synth is making sound, if it is not try to trace back your steps and and see if you did anything differently than described. Now, if the Meter lights up but there is still no sound we need to check the routing between the tracks. Click on the Arranger window again and select the Out 1 track, this is the predefined output which MusE by default loads at startup, at the bottom of Mixer Strip there are two buttons looking like tele- jacks, these bring up the inputs and outputs of the track, click on the right one, the output and make sure that it is connected to some valid outputs on your system.

r0.25 Image output_routing
Click on the outputs to select them, if you did changes here go back and try clicking on the Piano Roll keyboard again, hopefully it helped. If there still are problems make sure your system actually can make sound through Jack, this is however getting outside the scope of this manual.

This might be the time to bring up the concept of community support. Open source software could never be what it is without the support given by individuals on forums and mailinglists, if the information given in this document is not enough, try googling your problem and/or get in touch with one of the online forums for MusE or Linux audio in general. See some pointers in the Support chapter.

Recording Midi

TBD

Recording Audio

At this point we'll make a slight detour into full on audio recording. Getting audio out of MusE has already been covered in the previous chapters so we will concentrate on the additional steps needed to record onto an audio track.

When MusE is first fired up, the output track has already been created (more about this in the chapter about templates), to proceed with audio recording we need to add two additional tracks, a wave track and an input track.
When MusE is first started right click in an empty space on the track view
Figure 2.4: Add track
Image main_window_add_track
and select Add Audio Input. Right click again and also select Add Wave Track. Two additional tracks are now visible in the Arranger, "Input 1" and "Track 1", bring up the mixer with F10 and you should see the following configuration.
Figure 2.5: Mixer with one input
Image mixer_with_one_input

Note the buttons on each mixer strip. hover over them to see their functionality. For more information on all the buttons see coming chapters about the mixer. For now lets just do what we must.
1. click on the stereo symbol over the slider to change the input to a mono track.
2. do the same for the wave track (optional)
3. click on the Mute (gray speaker) icon on the input track to unmute it.
4. click on the input routing button (see the tooltip, it looks like a tele plug) on the input track and select an appropriate connection from your system.
5. click on the output routing button on the input track and select Track 1

Already after the meter on the input track should be able to display that there is incoming sound from your sound source. If there actually is sound coming from your sound source, that is.
We are now nearly ready to start recording. First we need to select a location to store the files. MusE does not use a centralized storage of soundfiles but uses the path of the song-file (extension .med) as guidance as to where the audio files should be placed. Now as it happens MusE will prohibit us from starting a recording until the songfile has been stored. So lets take advantage of this behaviour and just go ahead and try to record. Let's get started.
In the mixer click on the red record dot on the Audio Track to arm it for recording (or enable if you will). Now when there is audio coming into the input it will also show up on the Audio Track. Also note that all the input and output routing buttons on the tracks now have the same gray color, this means that all of the tracks have a proper connection.
Figure 2.6: Mixer buttons
Image mixer_with_one_input_buttons

All fine and dandy. Now bring up the arranger window and find the round, red on white record button and click on it. This is your queue to MusE to prepare for recording. However since we have not saved our song we are presented with a dialog to do just that.
Figure 2.7: Save song
Image project_my_first_song
Note the check box for creating a project folder, when working with audio this is very much recommended or you may soon loose track of what audio files belong to which song.
Finally we are ready to start recording! The process is completed by clicking on the Play button in the Arranger. If all went well MusE then starts to record a wave file from the Input Track placed in your song directory.
When you wish to stop recording press Stop in the Arranger, now the resulting waveform should be visible in the Arranger. After rewinding the Play position and pressing Play again the resulting sound should be audible through the connected output.

muse-4.0.0/src/doc/html/split/documentation/node5.html000066400000000000000000000150571404105311000226650ustar00rootroot00000000000000 Basic overview Subsections

Basic overview

In this section we will make a step by step walk-through of all the different editors, their purpose and what functions they support.

Main/Arranger

Figure 2.8: MusE main window
Image main_window_annotated
Above is the main window of MusE, the Arranger, this is what greets you when launching MusE. The Arranger consists of two main parts, the Track-pane and the Arranger canvas. The Track-pane lists all currently visible tracks and the Arranger canvas contains all Parts of the composition. The screenshot above shows an empty project. Below is MusE with a song in progress, turns out it wasn't a very good song, but for our purposes it is fine. In the below screenshot there are a lot of tracks visible in the Track-pane, each have an icon which indicate it's type, wave-track, input, output etcetera, more about that later. In the Arranger canvas a number of parts are visible, the ones in yellow are in this composition wave files, the multicolored line are different Parts of a drum track.

Figure 2.9: MusE main window with arrangement
Image main_window_with_arrangement


Mixer

Choosing View > Mixer A or B from the menu in the main window will bring up the mixer as viewed below. The mixer will open with all options enabled, showing channel strips for all tracks in the current setup, depending on how far you have gotten this view may become very large, at which point it may be a good idea to limit what is viewed in the Mixer. From the view menu all the different kinds of tracks can be toggled on/off from the mixer. Some may find it a good idea to use the two mixers A and B setup with different setup and store this in your song template(s), more about this in the Song Template section. It can be argued that everything in MusE is a track analogous to the Unix idiom that everything is a file. The types of tracks visible in the mixer (and track-pane) are:
r0.5 Image mixer
  • Audio output
  • Audio input
  • Group track
  • Aux track
  • Wave track
  • Synth track
  • Midi track

There is also a Midi Track variation called Drum Track, they are however not distinguishable from Midi Tracks in the Mixer. Also the strips for midi tracks are different in the Mixer than in the Track-pane view.

muse-4.0.0/src/doc/html/split/documentation/node6.html000066400000000000000000000424111404105311000226600ustar00rootroot00000000000000 Tracks and parts Subsections

Tracks and parts

MusE arranges your music in tracks and parts. The following section shall provide you an overview of how things are done with MusE. If you are or were a Cubase or Cakewalk user, you will feel familiar with this.

Tracks

There are two general classes of tracks: MIDI tracks and audio tracks. MIDI tracks (and drum tracks which are internally MIDI tracks) can hold note data. The Wave track is a type of audio track which holds wave data. There are also several other kinds of audio tracks.

MIDI tracks

MIDI and drum tracks hold MIDI event data. They don't differ much, except that drum tracks offer a special editor which is more suitable for drum editing.

Wave tracks

They hold audio data which can be just played back or be piped through effect plugin chains. They offer automation for these plugins.

Audio input tracks

These provide the path for your audio data from outside into your project. Set up the physical audio inputs you want to connect your audio input track with, and then route the input tracks to various other tracks such as wave tracks.

Audio output tracks

These provide the path for your project's audio data to outside. Set up the physical audio outputs you want to connect your audio out track with, and then route various other tracks, such as wave tracks, to the output tracks.

Audio group tracks

Group tracks are like busses, where you can route other tracks to them, then route the groups to other tracks. Since group tracks have all the features of other audio tracks, like volume and pan, they provide a convenient common routing point where you have control of the sound before it is passed to other tracks.


Audio aux tracks

These provide a more convenient way to mix several audio tracks together. With each audio aux track added, other audio tracks will gain a common send knob for adjusting the level sent to the aux track. This can be more convenient than using several group tracks.

Synthesizer tracks

This type of track is a software synthesizer which MIDI and drum tracks can be assigned to.

Creation

You can create a track by either right-clicking in the arranger's track list and then adding the desired track, or via the edit menu.

Attributes

Tracks have several attributes:
Mute:
If you click on the Mute field (denoted with a "M" column header), the track gets muted and stops producing sound.
Solo:
The solo button ("S" column header) singles out a track for listening. It mutes some other tracks but may phantom solo others. For more info see the section on soloing: 2.5 and phantom soloing: 2.5.1
Record:
The R column "arms" your track for recording. When you rec-arm your song and have no tracks rec-armed, you won't be able to record anything. See also the config option "move rec-arm with selection".
Track name:
Double-click to edit the track name.
Port:
For MIDI tracks, this lets you select the MIDI port to which the events should be routed. This can be your physical synthesizer or a software synthesizer. For soft synths, this is the port the synth is associated to. For other track types, this is disabled.
Channel:
For MIDI tracks, this is the MIDI channel the output is sent to. For any kind of audio tracks, this is the number of channels (mono, stereo).
Automation:
For audio tracks, this lets you set up the automation display in the arranger. (See automation 2.7.1). Clicking this will provide you with a popup menu with lots of submenus. Clicking on a submenu will select or unselect it showing or hiding the automation parameter as a graph overlaid on top of the track.
The submenus let you select the color you want to associate with the automation parameter. There you can also assign midi controllers to the parameters, a dialog is shown where you can manually choose the midi controller, with a learn button to 'listen for' and automatically recognize any midi controller operated by you.

Clef:
For MIDI tracks, you can specify a clef here. This only affects the score editor.

The trackinfo side bar

In the arranger and the part editors, you'll have a trackinfo sidebar on the left side. You can set up track-type specific things there.


MIDI trackinfo sidebar

The MIDI trackinfo sidebar lets you change program, volume, pan and more. This sidebar can also be viewed at the left of the pianoroll editor.
Old style drum tracks:
These are MIDI tracks as well, but with a few differences. They allow you to map certain drum sounds with different input notes, and you can change the output settings of a certain "drum instrument" without having to alter each single event.

However, they have certain limitations: They only can handle 128 sounds (even if you have more synthes), they aren't really compatible with MIDI tracks (you can interchange parts between them, but if you touched the drum list, you'll get unexpected results), you can't set a program for the used channel and more.

New style drum tracks

Because of these limitations, we introduced the new-style drum tracks. They're not fully compatible with the old drum tracks, so the old are still retained. Under "Global Settings", "GUI settings", you can set up whether you prefer the old or new.

They are handled exactly like plain MIDI tracks (staying compatible with them), and offer all of the functionality, though in a different way. They allow you to re-order the drum map efficiently, you can open parts from multiple drum tracks in one drum editor (MusE will separate the sounds from different tracks according to your settings, see the "Window Config" menu), and you can set programs as with normal MIDI tracks.

MIDI trackinfo controls:
Output port:
This drop-down list selects the midi port to send midi output from this track.
Output channel:
This box selects the midi channel to be used on the output port.
Input and output routing:
Selects midi ports and channels to receive midi from, and soloing paths. (See Routes 2.4).
Midi through:
This button selects whether midi input is passed through to the selected output port.
Depending on your midi devices and settings, there are cases when this should be off such as using the same port and channel for input and output (otherwise a double-note echo will be heard), and cases when it must be on such as when using a synthesizer track as output device.
Input detect indicator:
Blinks when midi activity is detected on the selected midi channels on the selected midi input ports.
Transpose:
This transposes midi input notes up or down in pitch. This is very useful if your midi keyboard hasn't enough keys or the selected output device plays an octave too low or high, and you would like to shift the octave of the incoming notes to compensate.
Delay:
Adjusts the delay of the notes.
Length:
Adjusts the length of the notes.
Velocity:
Adjusts the velocity of incoming notes. Use it to compensate for a too-loud or too-soft keyboard.
Compression:
Adjusts the compression of incoming note velocities. Use it to make soft incoming notes louder, and loud notes not so loud.
Instrument:
Selects the midi instrument patch to be used by the selected output port. This is equivalent of dialing the patch in the bank and program boxes, except it displays a more friendly patch name as defined by the selected output port's midi instrument. See instruments, or port configuration 2.8.1
H-Bank:
Selects the high bank number of the current patch.
L-Bank:
Selects the low bank number of the current patch.
Prog:
Selects the program number of the current patch.
Volume:
Adjusts the midi volume controller.
Pan:
Adjusts the midi pan controller.
The buttons beside the Prog, Volume, and Pan boxes store the value, at the current transport position, for midi automation. (See automation 2.7.1).

Note that the 'Prog' button stores H-Bank and L-Bank along with 'Prog' value, so there are no H-Bank and L-Bank buttons.

The 'All' button simply stores all three Program (and banks), Volume, and Pan values at once.

Tip: If the Song Type is GM, GS, or XG, you may need to store desired values at transport position zero, otherwise your adjustments may be overridden by the instrument when the transport is moved back to position zero. If this behaviour is undesired, you can set the Song Type to 'NO' meaning no song type.

Audio trackinfo sidebar

Unlike the midi trackinfo sidebar, the audio trackinfo side bar is nothing more than an embedded audio mixer strip, the exact same strip as found in the mixers. (See mixer 2.2.2).
Effects rack:
On the top of the audio trackinfo sidebar, there is an effects rack which allows you to apply various plugins on the audio. For more information on this, refer to 2.6.1.

Parts

Within MIDI, drum and wave tracks, you can create parts. Parts are chunks of coherent notes or wave data which can be moved around, copied, cloned and deleted independent from other parts.

Parts are created by selecting the pencil tool and then drawing onto the right part area in the arranger. You can move them with the arrow tool, delete them using the DEL key, and a right-click opens a popup menu. This menu allows you even more stuff, such as setting the part's color, saving the part to disk etc.. You can use CTRL+C and CTRL+V for copying and pasting parts. CTRL+B pastes the part as a clone. Pressing SHIFT additionally provides you a dialog which allows you to paste the part multiple times and set more stuff.

You can also copy parts with the mouse by moving the part with the mouse while holding down the CTRL key.

muse-4.0.0/src/doc/html/split/documentation/node7.html000066400000000000000000000126061404105311000226640ustar00rootroot00000000000000 Routes Subsections


Routes

Routes are how tracks are connected together and to the outside world. (They are also how Jack midi ports connect to the outside world. See midi port configuration 2.8.1). Each track strip has two buttons whose icons look like plugs. One button is for input routing and the other is for output routing. Clicking on these buttons will pop up a menu of available input or output routes that you can connect to. Most audio tracks list other tracks to connect to, but audio input and output tracks are special: Audio input track input routing menus list available Jack audio input ports. Conversely audio output track output routing menus list available Jack audio output ports.

r0.25 Image output_routing

Meanwhile MIDI and drum tracks allow you to route available MIDI ports and channels to the track using a handy popup matrix.

r0.25 Image midi_routing_matrix


Anti circular routing

Any routing menu item which would cause a circular routing condition is grayed out. Find out why the condition would exist by examining routing paths involved and correct the situation if required.

Also, you cannot use a track's aux sends if the track has an input route path from ANY Aux Track. (See aux tracks 2.3.1). Aux send knobs and labels are disabled in that case.


Soloing chain routes

Soloing chains (see solo chains 2.5.2) are really just routes like any other. The available solo chaining paths are displayed in the routing popup menus.

muse-4.0.0/src/doc/html/split/documentation/node8.html000066400000000000000000000122501404105311000226600ustar00rootroot00000000000000 Track soloing Subsections


Track soloing

Soloing allows you to single out a track for listening while muting others, without you having to mute the other tracks. (See soloing track attribute 2.3.1).


Phantom soloing

In order to solo a track and mute others so that it is heard, MusE employs 'phantom' soloing: When a track is soloed, MusE automatically solos all tracks routed to and from this track. (See routes 2.4). A phantom soloed track is indicated by a black square in the track pane solo column. (See track attributes 2.3.1).

Figure 2.10: Soloing, with phantom soloing
Image soloing_window


Soloing chains

When an audio output track sends audio to some external entity, such as an external running application, and audio from the external entity is fed back into a MusE audio input track, solo chains allow you to solo the input or output, and MusE will complete the path automatically soloing the other, and all paths that came before or after it.

Solo chains also work with MIDI tracks chained to audio inputs: When a MIDI track drives some MIDI device whose audio is fed into MusE, solo chains allow the entire chain to be soloed.

Solo chains are accessed via routing menus. (See solo chain routes 2.4.2).

muse-4.0.0/src/doc/html/split/documentation/node9.html000066400000000000000000000126641404105311000226720ustar00rootroot00000000000000 Plugins Subsections


Plugins

Plugins are small add-ons which can process a track's data.

MIDI plugins operate on midi and drum tracks, and are found in the Midi menu.

Audio plugins can be applied to any track handling audio (that is, inputs, outputs, wave tracks, synth tracks). The effects rack section describes this. (See effects rack 2.6.1).


The audio effects rack

All audio track types (Input, Output, Group, Wave, Synth, and Aux) have an effects rack into which audio plugins can be inserted in a chain. Currently each rack can accommodate up to four plugins.

MusE currently supports LADSPA plugins and DSSI synth and effects plugins.

Plugins can be added by double-clicking on an entry in the effect rack in the track info pane (which is shown at the left side of the arranger when the according track is selected). Right-clicking the rack items offers a self-explanatory popup menu.

All plugin controls can be automated. (See audio automation 2.7.1).

One must carefully consider how many audio inputs and outputs a plugin has, and how may channels the particular audio track has (1 mono or 2 stereo), and how MusE uses the plugins in the rack.

Learn more about this in the appendix Understanding the Effects Rack: 3


Audio plugin Graphical User Interfaces (GUIs)

Once a plugin is added, you need a way to manipulate its controls, which affect its behaviour and operate on the sound.

MusE can show a generic GUI which contains all of the plugin's controls arranged in a rather plain generic fashion.

Some plugins may also have a native GUI which looks much better (it was specifically designed for the plugin).

Both GUI types are opened from the effects rack right-click popup menu.

muse-4.0.0/src/doc/html/split/documentation/output_routing.png000066400000000000000000000655411404105311000246050ustar00rootroot00000000000000PNG  IHDR>А$k(IDATx]@[]nN~vX?"*b xv؁bl?.'s=3sι]geK@vC &(e Ҙޓ(DR|`EC~ :5 ^^[}XAbѣ&AP$\g8e) xNNu^nѣHD k*1VVV$Lֶ!z4|܈!)VV֪鿻׎ [iffײm@`faOd?e)ײXl8UKת9z(fΚ Yl2wZH$ k =jb JT槂\b!dyVuG;fN`~~xvobOoyyNN5 ?ӥ݆*Ŏ75efGO %<֣d0OOkP 0!` ӯ$oq/m732/k̓;ծ!U,Եگ酟ʚnx !w`1r^K|-Kw ^ZS#v]&_f]vkbOSH&da67{"}<:5szR EcΒf$9솨"aQ?[\hzp MBGMѹW/sw|%QT׮mGbfB'MlK'ᐷzƠ*D" ,sbo88EXlGlJx;[Z5h~%;_~k Vz/Q7`clI| s˾!zanvN7e.CM% _*(ΤzjʞH]E =/Rin]tD۴4ͿWg\:ekbى@aںxEK)a5ňb.tOc>\AZg4q M'ߌz p"W4~v%%n8j\k^^^0󩛈}&'+԰021lS ^>WTGnC( .B{׵z`ZF O^ l3?7硞%ɦf*ңZaLk[["*{saA>d7<u-115'1 Ν?ߪUKXVVX50ܓAN_P0$M8niV}"krY.;oDRfMka#Ѳ6V+ KXXEWu Eqg]"t.NYhLJEBFMDauppڵ_kRwr=ܾ`ɞFsb9I߶{X^w#phd(@llWG܍K5=&ϵ{Ԃ"TgK-OJMG/ Tu<rCIqMqI{~Y48{@z* θMȸQk7@i_ִn'?~|?wBmeO>t0ONIQr+Onzre vu6~yuTq$dž9-s2ns}9%ŕv8jiWr3SG5o.NCz|r]{G$OZ 4!t4ˣ]gv]y>}1uǛnMHM2`im"+Nyw`q4,l"*}+l!hO8mxqْc]N[QPiB=;M{%"ΰ'HP%*>\|(IAy@c]d!zM p@`h ڢPfC!-xqCW?B|7Xov9QcuROLEN!Mixqe$2:ӕ'vhz^zkiYO;񅫯'&CݮV;zuʕE̚0~ pP;fܸ&2_oyNԧ)ɳV } 4pojlڕoS4K@haP|]@8dH6%# "yӢ\x,O/$ TZ!#|HQ j&^z=k" i[F;9sEBaTTX*!Bʬ5&ߜK9](2y7tԩ/v=jFoĦ/T j\Ы+v1:N)XGl ?ό;{F=f\% 9 $+I58 E5]igM5wƖۭ52\`k%$\q{잟okgt89NifcDl2 ߕ:q MA!"-`pTDrjf)P֙ųJ\QIq=oGe]X( I? 4i}WKH>>&z!o AS'iͿGPkm8"g|T _vC)1[Z*j][+s@ w+V>cQ!7]BK:ͬŬU =ې7P4Q%N ݲWX`?>j2Ӆ#;<+D*`ݜ1H]_Jfw]Dl0plRTcFv[vlTZyeMEݻ{<_6xykYPUlIvgF֗5sD,l]1Qj265+*(yT ))iy~^PPH221-.*Ԓf@FC~olgLk[wL8Q)a}""D613'SopYL+[;M|^qa>Hdfz`Z- <.O87H$ZC5H+Py]%w֯s93PLϨnp8Hʀ \9fp/:+~o춄Fv_hnAs*zkT1F U1<ʇ@?z۰>m\mnVE|kZ |bb.|?/%MYr a9 9i@1+D:⅟vMXȝnd}닙=#Sqѣ?)03oVԠ,/oxް͚Ǭ:F{~u'3P;# Tu:*ii&C@QGM\~uJ\>ƅ2 hd͛>kuEtҾЍ2/ВcǕﲰݿ.j2ĥZZ+GՉv!]WO]i>swsPj n# :=Q߉, DT.Er?\~$fs~W4غgP{*7s"v$:t̑G41<|#7[PwD.cD:OqELHN5v%;ȇt)9h R[B pkn-[bQwuMω1I:?AŒ*Eo1fڧSx"|4&v_?Jݹ}=D"V}rt͜C#D'J;Q.`dg {8y,\@&)*|q~e˻'f*b-ݒM8|Pԥ|X ֫Ogܸ[OrG}$Q.|* [ ՄG[qf 'j8 74:]ʇ"3ٶ-U$U3澳S45><09Eei> ھa\%!0|f.C@SQMbPƇ5vx۱3i9f> /ڮ4b7vD:FXLI'.w`¤T*.^pf{N,wBTI!0b";5}e KfZϱp?7l/5>N鵂9t&M"oyͱh1 g~pIuyoȉX3s ҸC[4貃oF[Quz͟ܦJVZAcc-$>_,P Mi$i\D >=8-BQ P CF| 6t­!Go_ChjpbR3c|Ju"Q|F2~@wuHΔ7ʫ/k* [WCV㇄2 !O d> _|=՛|%P/:X:쿥$CK ^eyy86015+CESa@~~odoYb&R$hdU2`cD$ Ͼ#@o5!yLYSaiDvU2BW-= f 5miN:!0L82!jx\nA^.^< ۓ ` u֞/fq1SsSkr{\¬1u;ׂ'eS}c.j+@ T-K [v0r*5c*l难!jBwGPZ=PBz`z{yZHvakУ40ed !&jWVKp8l-·{sQa!AD=fQ5!y2>WA M̘ZZaqV|cmIX%*͝kh!]FMdVeΆV ee`8OVL$ei.\ٌ3W,j[Em%WXCg|xȔazp+>yGҊ:!\Y5}e)APSA7ǯ6ߪ~x=V&9"w'!|w%,ȻwA׫C/D8`lf˧5#ǺmEԥ{ye<)A~拃+(͚ёj*",|ujMO3,TƇ'< 2::ҋ*K2.3w!;K f$]%U{؎kI$2Wΰ.Y׬j>&uKWNa !܍9#E{\Dt%hau?'iT?)»z.5zP63 [5yuJFЂZؒ*ٯ8uIzG!|ؿaC rKe3)h |2wt6oy5=@Nf=JKlVŬ:vH®huYqy<*]Y)f[$]RGt<{k!4)>6ԎM4u4ӏ^ .9UwCLjuNsLE{l@K|"y=#7jK%v:oBRvG[n-̽iVڣmvuIEKﵚ8sJ֢ kc :}@ \g+ڏ:Q4K@$_GȿXBR>dHҺ*olg4bq.J L <#,;s6ۦER5A|b7.ŔAD^e$81X`!H Ia .[dy7hJĬHBI\1͜ӠP$.?qJB({C@qD[Ch փ>Kw#au_x>8~r4js y|:Mċw+3'4Ic5cLaA͘ʺwӲ>-l,xori7{%P3†V=TVzV_Pktfa=_z{؃kൿ0 ,xVpج<>} D265!ajjy\iV6||Iχ=_A\.\lljbWp]dB̫}gŇV]/KOSF~KeOt &ZA8-.+_K|ܧ+l ʹjWk+ޯ@.`| p?Gh@ɩl!hB!\]j>mt79CHb<7^>u wꪡuj./[37y9^C^QBv>.Wص7p4~ʼnlӦ3NibW}#{uWӔ9CMU_ f)ofFCcw *gS<2i;y:‡t5@eemܞs5-ܫ{wVkZf+QMʈJ~vu67qmܔ!kGܘ[t0}|5Ҏ(A\>k]R+`=u4d m}r/"λKoBz&zu[%O]aPl05dŅoxDѥzh`M&hiHH7$H>d4unֵA6:Y;NsJǞǼ}g;[\N3!B]cRE"ͪ|?_c._hn¹_m)"=IՌ> ƀCU|;u+% pbNf(<&&knʒQME(EH>!O"CU(& N={ <:~Q-O}H4r; t%eA&+fLʩXnŹ72L)=8CYT.#B&OL%B$F^oL!r>)Ǣ|ec)x:܋uǷ#I94ah0ӿ_<4ES'96 2Ȼan9C>C捇49ۀa.h bYZʎwZD1 Du6νƷZ<5rrt u>\)NYRdmfQAX6gf!*dptG{\I+u˦HiD;52ӽ# ˷j^Vkgח+F'9}Ra^$P@ 7g]>Kp29j ڣ!Bt.V2 \|d2" O;}"6E1ɠ%NEg&vWh8$aUjȢ!<яUߠ{CXR"Imϒ l*QzFmn3h`%iUuE x3jƖBA8lvnOFU.=GHޜd `^[*IQs-cYs|5b*OxV6{7g%`4+%/fL ;rDMVD]XC-F=?}Dz Ե7ky=z,x*%iD >DUHʕedr@|ʈe!,"\֠PL Bu*PJ(i"Ae/䚥T 4Yb*)P6E]FFc%L( Qk)$k+(ЀVՃIgA#`k~qڶD?˾w1൲ Rߡ7B[Z=(V9@]xޝ =x-S I-/Iz©Q Mz٘p>9?`k%(k~S̛ԲBΣ'VVk G'] 4<~М~ֳHfZrݎW7\ۇכj}Z4Ju$6Fsgvkه^Yy,<"w`1r^K|-Kw ^ZS#v]&_f]vkbOSH&da67{U>$<:ZQYTU:I!fg7Dm 8Bփ[OxXl:jju菉B$|iC3T.ȥvm+8;5g:iBe;^WBFPƱ#VBd8X|xv%JXlGlJQ>w$j".(K$ _=dw!@ȸAG>iz7zZb  *21 ya$>P }bkg}Nɖ-Hsڧӧ'RUަqor=)^+W(|u CEQ$Z5h3|̏뽶jx/zl߼M;63%uoh~q7#H+F`Ie%A~v%n?q`!ϧn"F? pHwPcÞ#9ϯ=9U0 sGŭo(}曆?P5C'4;]_V9ř7W(y#cۻW-O?"*hҌ vѻ?ZRXC2׎1MУ6/,T(Cu!/FoǀU2BP"SXX Oa"I&&d (A٧V \.Ͷ$й.|>AؑTjV]>gdbk "hdlb2ZG7G3}tϟxyG*.a>0DP,Rck52 ZSfX:XCS[T>YwU]Zbt!E5REUP߻ `7ת?(%V:L*8w͍;(jQ;ˡK;$Y8<9<(j\?ۿjݑ]'MC ۛwلY}'Sξ479xD]*|ԧ}xuۍ1 Ȥ>Wץ{/ POڎj]Xqq#<4{}t_Zeo}tAKQugT FV;Ki[CW2y+maBv>jk!c-| Y(i>GA/~ngReve.Ej^lk+Ԭ~cR7? 6ݬe~wӘMcDV&|1|%񳎮?G#0Zʼ݇^5CF3lΘ~%dL}?MNb?ݵ;ai&7oeeʼnO(j5ԛhI6ҝ}qmhB:xUp * Sc2@$:u #x^n:P+ 6Th;j vN?u' Hy[IQ+g?NDS|hBq5lL(s$ۓ͝Yw䴳ѾAZ*u] xIDpK;p&#~hcx[w}KY2p>x%Bj W81u]=H\/`}tXCIPfc.5ΨFEX[ā_p#2 o?US!%t^J?nvS?VG> wÁ({yh^avwly5A}gBƎ8Mο*/ ^&Q g?ӺT@" # n%o=|=&|dz+"7E2]H1ruWExF~7tArɎG&4 5} [ v$W^CYSaPwy2ycA.'x"A:h7!+ AuGءc:s\i3eaBU^e2-,rjHdYa~N_vsNrҖ Z;!E $μULj+d; vUj`@y*od9l]xLfS*@JѠzGMQcVXe1ZzNE Zؒc8N f~w2+BM]TM[V!5V]qV?UxtLӣ֌ F|Q6c9AGU_T.qMDiM^5c*l]觪P|z>j~Vǟo@ƪ zգ4zQXZ}e7DŽFZ=_G_=_JTP\p"}* H226!+Luq-*< EZV#ruHDbxXh\`0Dx|i>N94yLG$-TbH1.IX,ޘvY;[۶Zh̉8la#*^*:_B*c{9?qÍu[S/^+zK e@tCȽyˁoǡ{!sNffoc3 sL´ 1pu+f8XfݭӄI=>Eӎ1uɘ+6\1;p3<"p D]*|»C|c& DV'H7Tjډ_96ZQME/l̸I}e|Bэ[}#66v\w7,Qyst^^i}= [*"@;Pu|@"qM/]rY8rHX 2|65te Ü;o9п.&kӂζ%nTs }oC*$բQHt=CѤi6yi۞Mݠo#!js4)7]wlw}Cܾo"*y{Z#_]_M~-b)ƅw7\ ;q?^A,OBz8WVZvCoݺehd鳝;111֖lvU =ỗ[6!Cp q?YztR|_n!|Ac?=\:t ![vm!ϡlQFi#̎u+9Lj.}%v{Rq]w>}IhۯH}(xp|JCidݘ [{ED>p6*gck5s*ҹ˽{?~dYA֮_/  0Heo'Uq#wn]~Z:t3 @s/Sn8p$#9K-bf&*O2hf" h@KYf5'‚xCLERT>~}6ǵ#f*r]4 R(An!GA]cCX λӯ:?icc]BG />%Sq=} xft,0V\1u4qDZѽG~^ d8Co]fOHw5Gw 6wʕoVi xiC$O-yJ$j#fE!Ph8D~7qCmcru4S|rYH>!WL1@!Y!22A.V-># Ç[tf GրV:Vx7zT\ $\>zI9?ˈ钻YGSHc8˟sCN. @^Kʇ@ u>` )+WN\g|~n]7rKQaqפٸa rsTe`PG>.#;:~G-[y{i") cˬS)}۾J=u1"τ6xnM~<72o߼#ℚpݶlNqc=|h 7뵋F$DP z9O*z{OL`݌IS7(.䗁xm˥(ҾzzBEcM,WvNSײVyy9<.͛CuL65,ݻ$u/e89N& Y#d9{*@.@1ľ4N3F́h1zX[TXe=ҡIdI>:D͠徥nK .SVڪZKspHw f9L07k73E'%bލta{E&-G-m|t*¥bUk|M]HT~Wej.e ɳ';`rmPuY9,@kmQQaQQM!1YKn9ohX^Mm`hH34*ه<׬(*T6pGoǦvҖ\j"+@nrC}eWgHxL \e^ Xs EbQ^z#imrT> ZG Ns K'!ytdg/iS`3E"MrzH{<#1Z߀*DU]k m#/r#[MS BfhT _ɚ| Hz>,C1Kss [v0?``(X.cܖ'Dtd&@ke ·/_Pׁix<677kC0 =f!|\ Uyުnlj>Rxvο]6󗊘y_ktc#c4np 5! F,·y}AE'uanơ.>(6~-kOk$9?ZjD2IӚ[M#t\~G@5# M OΟZUHSk~pL=qSS̋iiUEϡBq-VpOlWU1evMvMT| l[?-pVo- VanJ@wg;pnntqwvIs:__ˉ&Yݲ k_ ' S1#ƌio%Q][9`hɛ.~j/S^.yįr>C6tz{&molkDTSUʺ_61t-:cy S#&S4!ČQT ^; X%eOȕa*dUQ3W̺$ٷ3~=>{|Hf;5k ,sigM;(M`[*^lp>#S2ψ'^ ;pET$ٶ}` NRύ~ Ǎz5ɧ%‡Ϫpidۥ ;2cfjohB&'5<ڗf 9TմZuu/.*wj^镴0|>(*%j@76aJo|u;VczCA?nIk̑YRbvAޫS+ +qTY!HZW@ֆH4ܾ|mw07vw˰zHMR5#bd6n޴JFBx Yu]nئӏ]2dɨ"v>‡STulD D2Rf񚥇G ̇PX_/f秺MZ2;T\wmx첞b4oljn`hDB*)u=`b1Ņ#H z>om~CC, )d[,CL<}{Nd:C@2^1K  ]+'‡ڦϜۡ|/gN? ɸU>S݃mXAn*OSzo22>u]@ZAƮG'IspÙ 'ᄄLH}SdA*/_ "%ww~-%DF6Ch =sdY?\.{)AVv6'UL$85.)h!T!h)CЍ%qH [KCXFR!Xzy($E6 &I(4^m^mg{L!<|q6<;ZO_ Qc~Mȟ'LE[}N1(uݷ KY[D:c)>=bz3 Jul=;ǧ cf&W*y?ԺdS!%/g+lF o%mj+eee.K"yRhIȠ1(bM-`G%[KbeAn.E &f5jYW8=MglfJ #3U$k=VI7aJ9#̉ OW)k*Qi׸JFݳWTb/tA>dFg7AkS/=9UFna ih@ /8˺z(n@HX'^IA>gT YZhpr#*Py:cٍ!S+] mhC#WhFoT휜 e/MZEOkqe!8S,'/YTTȔ*EJT+v&9qjԒ*&^}CZU%mǐ4|% aww#\pL/ZX$)%KA'=%%6--THFL \7DזG8Fc4R9xeeJ_ q]8xV$*#8bIXz#87163%K~lj 䄪oSAiiFwӍ>y-;604R p- {&JTɆлP^'b1LDzQ֘t|~\=TPs@P9ǵ2z,++T_J]UTyz`BaF0r}V$E i;eHm,+Vzk5Q:5"˅YuaEvY `ͺ RqO݁xm&F@@ lHR4у'*dM,Cb2htC˛(!88!kVE!+|9NqqOhhhD dHgY) BK,ãHLX(|v2 M-Ȁt TTԶ ]IMsy<.cffkC`28{ԣb@[Y.gMėX&HkԈ&Čj@g3pZcbn!c ؂yJ p˫ky\!N;݈f֊BդKѫri *[_bbo h&K$c3 dUˡPi$ E"g2dYyo,BWp%e2xD;QڼtH씿A qcw1M|eE4(6[!|eη/v kK%gNGŪbˇW lɺo"bM|eq(C{QL8P ~~2E^%^ (k[iu+rքU n[zWr0haÉ$Q=>2u-{91I6gOxydnjS!Dpmev猌aJ2> 1"|ګţN-TdwlvBf6DTSpe/ CPSG^OS!B+y2>^I5! xֶǍt!Z5L_bE1ԩ|((F< r Lh 'He$%-}b6~. ʽ 'sۖ%,OIll ^'a8r+N·p5‡ͺQCأ‡ [!RzJ/ >>)[L"C8\ʇNd8ʇp_n yZs>E_.5DL/YKoT:VQ MԈ)d) B撩x`\>-94z>_C0xd. ^`M|PL!Bo-rPGV\jD2p@(rG$,0f.R~A~=hPH6{ YS#8D(a'B A"bFonZ$RE^SAG>q}>e bǞ%(*<1D`alTd '22i| ;=˅5l'{rr#XYtCLM]+_=hpĐnL7`E FA1DАjfjAv !:.?!̇Pͨǰ hOd5,g1L-, rdyqntCpUE-ܰG~ˎ@sZVUł@ʚLFp¼\6H0dj\ j&QNN2>, N#V%o޼n,&Uz>B]#]=88: Ye>{* ͿC([Jm!0  lHXkB1-;խA1k[w,}q1L0oݷO/bb>nd gKéxzQA| ]]IdRb3g!ll *5|l=YxJvj|n /t%{9tA慰䉓_o_( .+&\6a~TH_>^+A[5 ˫cvJ'OÉhs\5>LOl@]HB3$ljL /ʗ>$X\RT2 PN,F'% 2r@E␀+qw 2gQNY%E*=%@C@ `!'@!PP"y Zu=>t2}P=[^=99:T䆨 ek6\}f5Hq܇`uW*O'<9j}ZZ0mBCCQ+Mh R@ mC鿝<@x YQ` llWsn[t?zMc=9Lmo-pq ?xQu_](ã~}?wt.enV8>Qߨs[Udܾ`6$!C3uP(n Qv쉱Tty֯NY]kQ+fuc7^mr RT+Og pUz^}k;ZA 0(̜'z3]R7-g/4#fݞVsYXO[Vb,=g a>zSK̥sdwoQH[?'CE >u*Pdq=J[$Eȋ'Lۿu/6vq҅ G,3=SB>q8vbewG s0ѽzlBW/ߖ\JI5$mJ3Meb Qc?z_1"pQTӯ9bɘ~l%>>Se7Nw->1ս_嗛wñ}vIGHYy遃iױ/ qV`=R$wr/O\ڑ*dֶ-&J'ϲjS]; RPY\J_w6Pe6N?ƨ59FU th/r"}{>T@Vthx8D[ (?w=̈B;<. \;*å3^1gnXYk_<0q}4%]2q>Q/ޮu 4ܼ|1ȿo3e,<"W(drj36e:/0=Q !Q}nߎoӹ>#1%AyhWLݎoz+Mw:gYI?t-WgƏIYCiIƩsگ((pHKߠ+R֧$ JݽUv+[M߳=l0o*3'J#Ɣ2^aQ0$ƔclLt]H$9 W\N< U/HCrV*p8j!AC[oC7D޹k+ |3O;fL;k1vAo4]*c>_EbAŘ)dC^r-7nѣG(㓒`_Ű:IgvhlBGGᶦLk3ۼ-Ií.g~ضcf"r܋ 3#rIZrZ'SݼXZļ@#Ycca$ ?U+&tĤݥee3g S6@LHd?a 9bH։Y]KC]KӬ:dk/By};kKFgj8V/n<2p)G>Zϐqz__6=E!4WVw-5Kзʹ'HprnkSX͕Pp( fgggf3n wR~I̽D[Kje@f[ fY_nB| `Pi}8'CD" ͲT b}ܽW-eoÇf'Nu agZAf%Q NhG)y@(FO6s1?߂+dm@| YNg0׭5'J)x7 MV9WDftMe)&)QR ljz30r,p]jo~2B[u} 7m }ECLJl !e0 #c06DR3MuF-cH7R YjzذaPBehS4(UUU-%T297(`yI,8V'z3|%a_=rJGua#tSf^5D cP ݵK nwr+}bbEo<;\D`XecqBU#;`, `WPdCՑ*Q<`?(eWFNS¿<0D_CZAlM{|q;!p ϙ1"BU+ǧ6y?j1<}b^7[Џ?NNv-MՕZ(L1I"(3A5+vߐ(U~S"a%~ۗY ٳY[y 6a#_{o [zd?gIF%,]7_[,ruź#I̋ ܀ļ.3 &!=fl^8Ǎ̖qs 3-#DN( +(ŃDm!xyn٘z oA\txl˰v-%V..$If )EZmJjţ@DaR*$̄E&(PX(*m۷Z#ǞUJQj4fHNA[aC{]+oL"컳g?ۮ}$i#MQS #:v/$I };0t(IY,{[y:߆MX UcNx~c )=F 7 HPPZ'4|cW1 7LcVONGojҦ+H(hV];BE)ARI-RGko$4( EZo0?~c""i.oLDęfk9#M7Ker 0^`]-E$aS牰~cźtfZCa7}:aGHۄ[_m$†`nd"Or/Om2,jWjq+6ݽ\DJsZԀKw<@fLl{Nڰ墪iCjQeҙ(Z0fR4M4;zmYDcXu80^=Ɔ>zRW ;c]YDcjtGQeQgGmD_Itg V3""UVUR Ze7M~bbmeJ} EEy4 T^I\r&]n#ea&4T67)w*_ߐxMƂ=k9\yAoDl~c"\9IENDB`muse-4.0.0/src/doc/html/split/documentation/project_my_first_song.png000066400000000000000000001555301404105311000261040ustar00rootroot00000000000000PNG  IHDR?Ð"sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleCreate Project-{_ IDATxw`E?{%ɥC'(bWT, (`W{E P+ V,PH =?mKh_w;<<3;3wgvbpEz16ق ,X`Cs,D/9'7rlڤX a7Q-S-rz*p?:B5rXQJ=4)|/]aįk6ci7= Dʫ⁑O}ܥ]kS @AHb1{HXX.C7e$cHQ$.#$1pFy C/ Z*1$ Z%A7G3_'I+L6jQc Ò:Ch*Ck<%!5=mkQ]~rys-'O2+Qfױ<+P،9X}t/Z}A_l@vNr ޤ EYeA p{1^(9Y%l#b19ہ>gd3-LU0ư ѺQ I D5Td9*+hl5Č`FR&@|4'>:GnP$¦K!)݄=eg!>+4jWӟxFƳm&.Aą(~Erm.uTm!l_f@VW:ۊW%z T!/#3rr>1-"IGEeڵ#??A\˲Lyy9t:h"xzM CL+~\<3?tP$Ǔ1ӌ}}=?KpyF_|k~Wʏ)Tbdb_fcNlU>LD#)rfL<b$ >TG'O|#tĕi8:rd%|>+*h۶ :vT]唔(6j튊J hwKJJ 6~1vz1,st]mFnn.NCӬ@YeS?gWE5Y2#m*wO:|~')Uy?lZZic֮ 9PV}p:x 9gPr-7'55s-یSfCT2樖1mSjs' r PȪUP\TQ1 &h}399`)}D4GLF ϥZɞLu1EW~+g+~HKUOhxңfDz )&52)H<3ڰ}9wѫW/ؘ+=UVҲE$6ƃ㡬}č+Wnc<3)*ZA]k}k{Ŋ4oBz،*T7syiƗvlשeao+oʗױ" wB(}vvCЀF< @aa7JKKDWC}=S7A[z2ɱe c44d1 CIV?_9ЂgJuރZQ#TG_eT!1e00jH/zuFC3aTJY9ږ,G4*8}xr?= za'>p^ RhcYb:1o;/'nL_4{y{#R*IuJd/+NtL^X`9jG͋IQh5 m%'I/K7I(RZZJ]±lN~>H|v]vll0."KڵTZXHIi&mvFiim-c; #P]Ui Aֽ;ulG?:{a :lyuַ̘ }) NIWdiy={12>dddy u8\iTWW+ć0oeےdT&GaGvdMD[AcBV(e3Fn\Y7?tEt%eݯ-/qx8bu&׾g-I"JQ^"# t" U>:]11쿦V{b23l4bԢN jq&+e 7gQCA3PДWk2,N$)vWuu5.Kkiݪ5ӦOk)bcjZZ86jCNOH:Ytm۫VԶR6ǰxۍ/H|~In ]CT<~;w& fZ+Fj,ՓkpʩйK] زiN iޢZ^O?w}-Z<挆X#nmQJP*d]jdGM51kY5!c4LJd'@03|DE~_Z4 dä#oW9\3m*7k(>=21A9<>w'p:a?7ztgȸ"z2tA#bYqv|B&Lj^Q}Q=q^W{dS77k{0Qu}nMm96Q0OFȁ27WƖZrM7ѡcGNqK.Q-)//th]N摸q ] 5mf1]x!~[;*Gt^ yV⑇IO?͏? nٲv#J[zB'%IN}pٳ'@^êUmFZr\NB$*RR*ƹiFU&-5*$Cs4 qXʤxe*$HjʠQRMFTI3Ls`n"n7KE5 *k*sO (Y=;;Ɉ6DIBddIBװ4 ~>8li'_qӥ yǟyu:?VH)CsHt:Vr?;kRҨj= ޙ&W!]XpUطYm3n䖏t^p7׼O.AYԫgTk>o?/݇FʦKmgAD%%rWLҒm4M)/ ^jHt%_edPܐvYgdM[:Յ8vr:vK2Z~)'zOJ&J?ڋr8APA Px'G[nŇlsL~@NE7-8P=-2JQ&?%ˁ @.f{+SO԰s!9@r_H%:vH)U^ Y-i[ȕmUNZw ):YJbND%*ԐٱYD ?!k|MP ::(r+B$x5sOc;&/\2$ĄeTR6<^xys܁x!q 6zz}Vܭdereӵip|ae]exim˦oޒwޟ7sPxe?oYd-['}ƍ_}'r7`5Nz^- zJWϞXz -=ԹsFUq:\^${6oZJ$q j#NCq1KDEW1*IÆ@2^($֞oem9ްB}Mu*޹C(/X5e¢19Tm3v3FA!SUl dd6 uZm[GfBR&3ҙv1-]@Elc'#KԼVmI3/Isތ^J7V"O}Fj;k uxLdd͠fRĶ. }Q}J5C}%@!ռuBZXEDhذhڹq~"P824f،!+aL|@&$͕غy#uu5ظ};i.N3H!Nteܭbӆ ro=Pv\ Yw:bb9K6ضu^ҒdeɥNg/mO>%Kh,wg`ӦLJvh0&?zxeѝU]~}ݵ ,RRM9=>Lqd4Nϙb|h:EB9mq];1ծBUeI=6yy,Z2ThEyyl6@c eܭ5+2lZOUEEL- ][Ǒ/6֭a _Kma"_S>C`Gkش~;9kGuT*m`sy*ΛO<VdId_*M6=`b+N~f%\cR]/33o>L@944.k| >TDuOKU9r:ӕ*phlHbO!s#Le1Ad-U- by><84] _ځ7I=2gq8\Bvep {NgpA~T@Ingl{L8xθyE 1v/-d vڞ5Ϫ絋3|ܓl/OSe_r1)nǏeqqsMӨ>%)8iKѭŁ4ucr&J@O.>a1ʣSBcV6*Ō\Lf#I{xGI瓖%KUqaCz}ظx_gfg!d;5#Kq?~G]*%`b+#fyQ1]fHI>!TWx^KQȒGoLvvaܛ/B[Vf?T+mJ6n"; 3(3o_%m,[ }zz{}fǙęq{ 8c}fqvNB 1v/0dU{` k#VNL3k'>!M#S}O[Ȕ|8Ɇݙn#*!~dύWoi't&K>|^?`Ln@(at*xŨ)Z]>QbÑ6G N ]SEt} 蓪S?RlNG-D<P^ɏ%PnX7sGĀUrGHO\<ccNm ]9 kD甙sr^L0AEdR\iiK?6Xgb.YB+޽c A%KtҸ1}x<^:_X }v= П:|y,83%(G+ҨزFLyy -$@6A ==Դ4Xj5vL|>IiI)-Y+-zSPPá{DD$Ȝ~$H?j$HÏ匕4<2k+2d()ӄ,E?߇E GrvYF_Ի8dY ӡ+R̠lLAȢQ 2 (6] Gܬe,g>}c -.7J+),~o0Ϗ,KE|C2$G)7NΞ'jut5E@LR$&& aJ%ő=cYjl /hv@#C[ {:E{&tL '.92(44UddC0i"?j%f$Q75ɉlgHvbr'< L!%/b`",扐B(!<-!CQ"-3v\m=<=B##= }n }c/ե&M q>Ab(2Y4ꎆ~Ax $5Hͦ`/IO*H| 7K U@d HҶDee -341 {j"fK~U( gze(ʡF" 尬9RI|LjPEXGޜCtL~& XH1KvGv4b ^ <K^ =JսKzBs#-p~ Z01׏:zʌ%gƏ!ƈ\D5*Ҥ(L5fTmO)5Nj ";ldnIGROjF˓ėx7k+isBa\=cD7ɬ1ىk X֠fq I(Ń>h iILbbn{i)H s$(PxGb+I1^J2Ch Ig*hK. %G%kT$,;ilEjyUEѦVkGFH9hP AY:e#&Ɛ-ĕ>1/%Jv [Iט'<}Kxt%׷DOmcNsĐ H%N"+ILn\;XObD(JOMV^퉾 R_ ADSdMY-ym5Qm)D h"q$b D틉޼LL zp%gL#a=+gQeE:7GxcOba&mC9n߯"*ArT@AA 8)ˎ:*yLm@t#C,!-yBS@) %UQ~ 'fOܾīET/XU@be+x"9L2|Ɛi&DIHxҍ!-%Lچ+'lK,J?HyM-;v24eմ > [%CQ4nu(b(!*KTe3entXGUN\<򥅸V Nƒ&f;Q'GxDD'e ]&$;]mm!}FD(W"mE4E6 ԺrLn22Pm)V@E &&HZLY11h T"<'HO90⡱DH&l1_ډ5mƊΕs^5;9׈jJagK̏dҾ}MM:&97n2@6KnKq4vI@RD'NnBbCI2%Iڶ'W4L6:vbPDXDQ~Ӑ#+Ӱɑa9%QJa-1 #ꤚ$bMI{j=֯)P6dU(ʎ"CYR$58HH!H7I6mha\}6,{j&MҒݦ 7" QDI=LtTBMEv|Gl ' $IkӉnf%o$X9з̠iH܎9{2wWj*wY>O>3χnWϞƍV>fmLImx<J!|8ΤlB @nrU(櫦JVXN^5,X`Bee%E+VЯ__rrc/JϋmT.Xj%H.RsCb!e T ߸ХsW32pւ ,XHuu86_Au*޳7>IbwE_TrVlZ4o%">CA]}^ˆ^k,ƒacNdeg' ,X$ԩ36nЕE%%%t-,JBE lڼUVSߡ=:uҵr\v{Ruh]C|Ȳk\e %a W` L3u%In@p -U˟-χG|nhٲ%eelظZ7Uu^j}EIV~@,jZ` Bsge[; nl^ݛϾkjԉsK0 ^ΓOa#͓ĪF=͸pօT/oD}[q ?i Z_WP껛8ilj ęyoꐏwď7`ൿP{!OrtTj0Bxyoz Oev@*2:;ͫ1\-4=cqCԾ'ܘ֮ N -0 ,H祡&@@!͆ntb#r\tkmh. EV~5(ˀ♣[0}nA'=U)/ ol "kB&IWNn ޜs2GHjNXƼe\05.0h I }Fߣ, I4IJMCF%D@0_jۃԏLg'{%/m-#8ImYŧo/Ƨżv |VI3 u ~}p[-Xp@ tn',|j+$)B+@\v;,N>f02x*Do^؎~|!j姩^0 }/O0җxN?_^Bƌ9ƌeSih`ӗpKkWbO8ruO3g;DsXN9gPnzlU˞+eY}?}:g3.WeWB\#6b>r]?c+e30f;j&N_FueYj l,˘[hேp9y3\&4_ep?؃ܫvpBGsu5?;<% dР8xaC7@ Лu Wc԰Cpy>p<1ѷБpoX6Lq̉ȷ;"kr=] }{pcuՅHTAp}W2ù8mPO$ipHVdd*}sO0jX݃zD0z."wsgP>ՓCN+OZZ`_ʏ(P_W'Hzn<9~e˗,]168o IzvEdlm`sn&28e+$t7>„x_CAH<=371Yc^gy<&?; Gr&kKJߛ:fy~WV3=>~: 7R%➧0 <:SS~9 7=z+Z,aIu@H=Ou#bG2իhC?'{×ML9)An۾,ſ֨<216T‡nFX%^5c:m}9@01K+N5`oӏ"c|VFnvsg!N'E_XMM7,K;5ÎAW̧8M ep]mqIV>b?K&V9xV~G..A RY>` B-ѺmK~ ӿ\ȴ/~X[IYjG*3Pw>x[j :}͚5&38!t(dnH{\:;7#=ĺ]ԑAȥ==ty Z|gJxSIѰɕ+dFp倘"!`ny>9ozrGq7( "VYنc HʈdsbCFE*ڐ5;>9]aasر}9=4jJ,x=pȬ+}S^g'+7-lמтL@nW RF`όB2|27nKȫYW-ǝzZlޑՊLyⴁ!--ZA.GbqvC|;j8rŻLO/N e29}>XX``L[ŗ_(JxC]6kbл g5Ñ%4 xɤZGE-|WTزg23ɳƬTdc- k {(߶ykUɮGsѝGs~*Ww?¤9+FZv jwx FV^"ͤQ ݆Fp埰n^n'eRX)6d1#9r|0-~ɟN3Hq,_YB?+/n7b'%ŕI{N=w˸K~k.᪇ݍ-Las+_d7'j9wQ[\z=L|2CXS!|h1dCHu7EͿl[`P^^KV;2`A"W0-x dA/5ݪڛfX2|/׏$dE p1/X]'5+gGg-Dx~fO;UK݆PE&KpӮ+8ggt8Ma]/ 7Y>s2ͽ_|?D߯M_ sk}kq 2NƧspE~Fė@0:@Br;Z 8b ~@D ewzKR8ӳG;+џ2|q3_a-Bü'΅b^̣CEr >2x󺸓/;Q'-=Z*H=?,X#0E@z̟P7ƗѻhXG;F?p&Kc}:g6tnG.,#er) a5qO0QO{!n ҐC_y- /O Ƞq7r~BUXFn%셣y>=-efpCyv{^Bo?m{Y0 ˅&Pk75UUs$Kl}5މkڦ _zxAS>laqztЁ۶2o\rrrhӦ۵'=] O{Y@Cjj>ARyPrcsa"*݊'l撖FVo"--|cက uϑ$_.T n`& IDAT _l fjt |Sox`aBk8p7"֖W{oIJkȌ$.Mi/  n'k#3y,"cBb5n,X0X=paM{y`ao!0ZX݁M X$ł+?Xs, ,C` ` ,X"v¿nc ,X7 eYIq8S/zY֧w?C=zؖ$ QqeNvm? C>UU\I\-XgQYYIQrKNvi=χ}x(.ކt6e U=b=uaƍ 8]ݱ`B p8lX70eV؍:jvvc~?5[sfԩ Y ,h +;Ν:iߦu٧/uub-/:w7Ke ]T[\ddfk7,X`,ͭ̊HEE.#~EEE(XE`Ǻǂ :$In@e9oo[,[_dnG'ˆmn!BmԤ:4ڗ-~j~/.fԹ|3w;|w7˞ / x­\x:,ο~s6ӃDwxYu:{REny|UUW?Dz 'myqXR>ćyu~*ƍQwc|w2 nF^ϻ{n ǜŵS~fsMp񸳸ٟ(~V/'͙c欳/޷;=߰!N?n~K[w28_Fϸg ؁99lwo`ꉝ9k _<7zt/(~ELcCqҵ_q%1e6x1>Ap}vo擻G1ooE!gf<ƱYK; &-7^cΠ}߫'rWt[dhzP) 1=N7'r ڟCG=ȂrZ4KIq̙K#5e6Ԃ==U l;-?Sr̓|4^vnbaxm fx yoءL?p^ȯy.㞱mH{#/$>hX/Ҏߞ{Pwlŝ}&ޞ=Of<[Ԭ&$ܷW7՜Ͽko?xbښ?zWCӸp S?vf;y bH~Kmc`ӳ٬ھ;:u#g+i<6 Hz6LnI.? 6̕/eoxj޼l>)o.)KP2c_-nɅ:ptx"z `ˡJef``BJA]VvAuH 첌lmi-B<۾ceͱA{,.6dRxX=6]JAnwzw3;ėu߱zKa(Og@at)-ƺV Ei)\QK/b#Ӻy<4{=E_".>!ҧv+-p漜vN$ņ $:?9IBj"'v$4湆%NrL"־x{FshB`,щ #X95vA²58:+lB8 bǔ~E5NNՀI&0}P)$k6AuNBh^eֲ7<|sk| L;<=NقziLpW*"gR4Ix虱*Ag"kHՙDd3bnIw'ٱ+9EDi*A9d2|D ە, xV5`2kCQnTt@O?gcLA)M}Ո%Pu1Mw7f=cD׼$6" -^Gג-枠:s,$Vh^E0M~rCX:#_k#=kvKbSf Vmx4^1+9]EL]װ C_nqT4k)ͤppy0z|l.-JQ8]@A? OldDV+;I\tMG gؾ2&0ssL"*Q`~uo=ݖMEաI/jT*7':ý- Uwzw@خsHfpgoG}H=4ΧW#tD88 v>,S?R-8-Z"gz֧#$izQ`<4'c q8QH96աyYKUIV})r#y䨪h=wɡ` 22UGmva4=]JViݶS1:dvHӻ؊V-Zi^TIauxoȫ,5g0LWIf0#nV Պ9͛)vg}X=:mhoMzϻSrc,L5|vGZy~YW4{E$BߤǸ=t1t9BjP5ڴgE|aMݕ*fT ޥ*7 `dFw\Kh^xs?W2!8dTZY17F#y:3t]_?q.^سՆOdЂ|wXVk2pUCN#Bsz=^'R{[KKТ](G^!OsSq^e0-]ҰIV@OC{+mx7g.Zao$+"0Vg)aFyw!#ߥk 5`45}DnDTdTHXWhߺ46DHUUf}ԪS`47ؿc>7,ڐ_UƲJ6Cu\uB#?u{Mnܑ_vRL_!G?-] ?E[ȁ.jSwX_Ĕ /e=[2㻆n*|9+Gv3V {x%/|1wyȈW.i.%nr"TDV|q^e9S'b=wF􆎼4"+7n ZLW6u K$ToBboDN>{#b6 JNd%Ⱦݗ$Utﺓf >{ ;UK?_9}TgbKs!99?mL}LmuRlG/^uІNr&] r1Nlߒ{oAX*0xT7%}(Mƌ>^&Ug#Iq*&S.;eCUUɃd&$$vg͊bۂ{@_VS'qAq+LI5$VԔ ɋbyx D")aMN"U"#1͘Lw>yz(Jz#.Hn"ɯ0ͨBLL4qqZD Ӂbi9H,i&<<9D"H$w'D"H$'4H$D"}CH$Gi8N[ƠףNaSsD"H:v@0mn v ڦ]ݓRc Q{j!DOD`EΡ]0ӱvq S|,oByQWblN@a>x!} H:5 7']+y %15s&;wLJ3ga6B>O׿p0UDQvic:j,8S!1dhӧawːn;h6 >O@acP0s?cs : Ѷ_76e%u{[0(N̦SQ}PlǙeqg0.^W`7jEݜv)hڔDc'SpF'^"cIL*'Z΁tOoJ>Kzz.:Gi;%k1{X0~[N& '|Uh7'u0wRѽޟԃ|0~fN& ^|w~cgx6KDӓf?xIkI 6!yCeE!kezNFZO. Tyvu!Iwpƿ5WNZ88+^(O ^1ɓz~!WuϜ@bK>dKddKpSʡȳ1 sVCFPЋxlxΞY>o/׵D3a%,֗'!zVʸQ0W_-a(up*#ghqݗBq9qG~#@-Μ^97r%V-a+{~<[ n㓽|Fo1Mx1Wb&|;Gd?&K{}eK&ұS=_x(7 ~$D~#s̥WD#ٻɓ'r=s*FGۺN!4+VOjâ hXc,xa@r j>}ƃm.Uy|2rʯ}10['|bZɚtT,>OH]xhM&xmmljv 0Vu&E̲~mY6v4q)rA!?D d`ٹgNwq:ڠ;w&9),Y2,~##x`DĮc,ߜBYLnymsP+-Aј:υ* X4i IDAT\C%&4"m,(3!D5)D'^IRT"xbS 81,J91? *^hf(ר+uEX/gGRe!aTF|egSysϑ3oC!OFP?LyujŔIQt(DaO^=Ǔaد95BϱjX*[1Q|f5y>G` ?abiN} 6\I.!,eSe(30DRvuQ&ţ7;OFg R6[j7f Wa^ ]sd)TЄSѬqX5HfG@)\i #ؒRq^5"/oq BX93F-8p"]"H!:JLt @s:QEK].s#՚DvM@dd$:-L7)DܙjۭQUXv[Zn,P FQz>qO:VVAaSSP|zJVq%'e3o})Z}8U.N!P^ݡH]қi6pyFk/sχrp<%k|~TjQyEKZhC5^ЂF=kR$s:o<.oO$>Zœ'{)&CߣFݦ5b$߸;J{D QG,D"p:5._Lrr2Ŗ2x#ۗލF#[ooOBz$H8-Xk6T_H$N'|wTC|yV埴Dx %*yb2 !6[f}|0yxR#)r#H$UU͘M&&^QE+H$wJZq ~DED"y(p$I$l(z#D"HSpoD"H$TGO%D"rK"H$ܳs (d]qF{2ܤp)4wB%\ !\ք"]@׳H{uOFL ۹u3 \"+-ɝ&M\LRg FdRfJG2-;s]a+y 1-[5NX):oٵ"F"<((7M^z&VpHh7͍PһMx^z~?.ƍn\NF˞H%ZT%]DɲBS4JYIU Jzw&svw63AFZ!wyοH7-eΜFfINLbGM+Q/#ȗ-ӳ3rX^ݾqV Jևrp\3Ȝ9gp~;[\{Y3$k,HC3oUsq:n ~_vo~=!=(2.p=.ݹ\;47o"|3wPd{_!m_"^;4pg}i9n!ǸiO>?7 =Q%D"HϏX D"H$6p<яD"H$;\%H$?GAH$D"W^##5?D"H#=i8NǣE"<zt:*%=d"F"'v;zőH$9_8ݖ`xH#5?Gr8RRb}"I$U8z/z7s:`ؾ$}GOȖc N5iG.Ty&e=/WUj},q{G;ߩJI0Muk琟ԓ3hR"r8C̦Tm2ө| Ո7Ǫ 4swcsկQR“NKCz,?pvH$L^uQvRUpmȄazLqc[@(J.M3/GV_:g=oRң p>,dkdžN4vžsWtUP|Բ(hO`ͳ$Az1$I2N@)GCTqyoHG8w.[r/ hK1^1~cɜX3OCR{ 狡ӫG3x/\ &\=YE-s^؎3j=8~[1M6//ؘSG9&p": mypVv3y% jZ#qn:Cp"&R"{5+zKzMA//o={f?.\{n|$&MA ݿK$@a1w*z߸\c4!&Pf-YĒSHD6N/ s.G/\8 ZOL<|,_S?e_G5ˇLOO6Gdoa+1 gWq%K/f\r NAQu)'[wLcy5q]AzZ2XYG|~HMEUl>Ơ|QZ/Lx%I|¢hz~Qhh -%ncBMLXd9>\Є@9c/yMb,R}r IAk뵄۷͇%ePV_LL8ZkcN[;KX'Bѣgђ1sbVsWbD8hv-M;o!VϦe">}d!P W;D4>1?Q$onw_!x`}[&L;sc?y8m$΄ 7|3?nU#~DN:I1߰qV~F䫳6lk>ƮcLi A*Tt`lKWy;S_x4^^ɬ_bp/{m| ͥcuI1ƻ6s9gFy5X;x;rIˬk[>Ϻ4(aB <KrP? Kt#h% P~8~ŢBm NQ7sGqk >g5,Q`KHcc7OѯRRR8}bbcф ::fO7<#< w|?kk:WǮz^ 3vrrΟ ur3~[8;wDPUK*сA츐J9QWC鷘h4 ybNWX!נ8Aۿ@T3!/5v\ 4*RooFp툢k)e#N+*8~g + C~jȏQh胊hZE1 #+q6B Jቆ0jkv P1u @8;Ҡ0&4<hП#! &9zLA:T , ٴ7ND`):cwG2ǿAr.,"LSZ]=|kP'XJ5i ]*@(s41׮FAC)ׯƓChѹ&y BBͶQwoXr2:LC P(T%J S|֬U{>рG@ x_KZf ʅѕDuN lҖAoÙej4ax{ᥧ}PC |8{yuwvGQ"PˡiWR=oAQxQƌK`zNH"nl$=~;9< Ҝ'a^჏ZRIjb9bHBWF3aGMI;%Ĭ6аٍXXc̾҆nO/=`UZcpYAz7:!)&0QCJv&~ٽ-2+T~qTM\JaIv/uEZG JhJJԲ-p:5B(FTMP(4x4K}OO=XJoBIEQ,R8`wuNq-TՀYvk :EimEEu˩ck,BŜ p&GƑ{pXQ EQA3lZ&uy[`@*=эcXj&"9oe=u4),9SH%!O{(۴&(Wh b4t*#>DEviȊLe-Y%6ن1&|A"Y|ϊGʋ\ӳ2K5KQ_tixˮĻjme/ʞj0w@`pEh-5)$;"Kh!  ?zU M@'FR(DjL"p\'hX zCh'<9#acg ^42Iя̑hX'.3|,F,b lĘ9oQ1|IqT2e*G/9.M;D(!px" ҸS]jL{<)&TIAdya !Hdl$$z0]>s`L͙7nP֢vbc̀a) |5 :#GZf2ł}=oFUƒͿ-&X4pq5ޑELඝH;-O P&^_#qI8%5d oHlqHPiq%-Lg6grN +=D2דz#dӂIIIi&tb:vDMP%:Pt](?L8oK̆0jT79jL8UeR~co؅F_y~Pt*ZJDq[l$oN4A:k-?֝j:0R[|+|SغX8 " m' eUHeSWl Rxvt=;/'7sDj!_u'~3pθ712o,JæE93g,K_! )8[fdil%ھNdfJJ=O,qTIs#ŇBNcIʹ-9h|KxPwx8/úC\w ɧas8>C.|YU'oeWHgj8[?m$G&sM.P F.jh'yWĩyP*}ƳVB`ܡUp'ei &6-㙑 P~+b͙̩o2d\wzPjq5;̉Vr~2];x۰4-osk!Lv69Nbv'YKW!3s4Ȧ-'I=zͥN~KfB|&a ٓwHJJᮌևxb~QR2x`-To4RpzX2:RŨӡ7Lj1`e&E)6,F^0ԄV:Ew N5Bs9`W0}x9D9+ɌY`GnyD'śow蹼n!~!%r ]tG!3eOD S*Y/ԊOz9}hg{Ə{†X?iک$u<$W[g =>Ju YhtUhF37&^ȺZGlo2J՛7y)HX-߮Rh%.C>)xeL*x4 5c<~bv."@\˩MK_0&>xcb1>C_/Q W/g]i6ѕ^!__?憴~f3:4bPO؟/ /`WT>X{1&m<;hI^z)eĠb ևoNKݳgZ<ϬPd0MF V>]]>O[$J܀.t޸]dKW]ׅ{y{o(n>@Xhs)R.T9wrXҧ4494-KF0mu95p^N7ݿ.TZ$=s+tXS2b;>7\TAP鶴㧾F=<8yq}㸴.r4C]a ,}=R91ϾⱵEBݷk(YܯH IDATQ#rN bދiB(Rc\=c蹄cIܼRΘ,2m^zHTbbb0G_@cN<r[D"y0:K"Oh(# 33gU_[6@$SD#DX ĊI$$}#dH!D)&N /7pm3",E,Q)@"H3B#7k*fi 1!xڅ`8`WD=QA.H$+N oh$aBm$$J$+9H$\% )Bo>1,YH|; ޤ!?9#H$ &wЦvMh8o ˎK&Y98-O;&-/6m@Y|j+?KDZFúkېΥ9s;>~8#b|4nݟE{WѣDk8I_G\i nkcZ4hsfa˖-*4rDظ{}2ܢ-GDލrm?ye\,5.gѣeSw!)v~s?{}Z̯LڇG>KxJ'sϣmݒ3RO^ɵ.Yԛ.ɔ{i{Qv"TpF0q)nt%ag2/J_|j V+n(Oř>bғY{[IdiLˏSn-c,BspZJ>fM>6=OOS.{:ܹm=]m֎ U1/kYQ6L]̎ 6t81MޯS%ݘ9Jc]fǂ,qT!2{˅L(h)Ht u:}з?q'ONYӝ\<,C+0cb5xc?ޮȥ5 z¨q3dϜGF|3@kld8+/Ƣ7ᭇ1UN̵AE>L9 cVLS XޚAyݕד 8*cЧo!Oo3cd Go/Kۘ=y ?_HT5gߖ<)19k~ DR/3co2ؔp$:Qqf*1uC]wRFՏ'V7)$śKf[9i.WCj. oc #7%NǑ*|(m1/{\8pn*Ы_5uMgvD#j&N#V/Q=㺸jivɬ~)VTYz (.V^di,"ѻ( }*|zwM~TMd]Sj~l+NnXþW\CѼuPrK\+-w9jB@m=2wCw}xAIzq=N/QO/ɢrѮ W#!K}HKVӻ.,ı}牍– lM93_zu[qo*rms.0OŃ&>ێŶF|0{U!d> P:lwsH=ч'SS/\/&l%"*Z8l^3˹qy/D hQ;8|;M˙׷({M`_ocL;e=\cKte.bŲ|3/@yBm0m`5l|9M[ʯ>N:m#jAy@ ˆWa}#y,K 兟єl8j6vL__-`d=}Nrb(f9_wqR?3}t*" b`vPDEnEQ1@;oo{%w{(w7yq;3'gH_M,E1KF?~z"M{s1=;xG xnoŚQ4cp3ߑ9<3\X+~΄~S7)yo6!LrK׎_Z^J{aP|7|gL&^yUbJTu~%l0 ; Q &U[9Y|Tb2*Y{GQ'sǸ3?0w+js'o|1v5'6^l_,EjeOp_hN/HM>=;t~Kx+֣Z"]l~ g1R ie捽1@L~n>K'Y7.ڮγ_g8f7n|_{;gc@|\zo9~e~|\+oj{ruKSWbe?OGC(g^9~ɢ!<‹L{}#rdaVٰ2f|7s*p+=S{EY:@O! ßW>]lnjp1P9^{ўA=5,WA:uXo"^#oxôu!b5 JӁ!w9y3l2K**QIڬ8^pUMX^$#[N'8Br^,8L wNwL XIɡcS9~]y(X䜖٬\zOϩ#^˶smлylL,^NChP00S:6p' 9VIȚ >VEܰ//촗AΖXgg՘Μsf,lԘ6<ڱ8KA~̙3kx1 +ʔ)<Ӵi lT$_K[yLڔr``n6̰Eà'g1D=G/_}2(K9Bd~vlos`[s൳c2\I1]8lF4NfT&[ Wmduz}mU%#CV<(&*vӰ{lU9,3fΠMKc>67u$WNܓf"r֠zs-GcgkӟN1 b^hM=QJb|ʼQMf&J}3:ޓ({OƑp(y`Sn>=Gϓv^yhfk SI ah4ןAUN0 S@%mGcj_Angs y yAbDafáo!ӭKI37wt^ Wf.[4M14i>͆s6AAѳbj([aa`mXSJ+D?y>~. iGzFT\*A~g(M},\FlhLIztx6ؘ? PC̉ԩ QF˯s}7wqO>$@n3+YUj8|;IuQD`Vǻ[f}9܇ t $.Kkݫb+rV,>BC'^s?N'1 s{n3\ `w(,e\mOɇOkd\RV)VcIS|^F @JFG' o8;F^:yl3|;VTb mTM݊Q Eľck͇p(wTxȫ`3 I\ћIH Ƅ~1%dF9pB{`xH;Xigxsx~ⅴv(}ø[z+Dd Oz.~66d8:nHitv= +>x^:t@.Z 7n5 @vJT%*lyvu4 b0s\wv}Og90zhr*NKSl?e%)1νo n3a~'xa> M`Iti%^qe:)].2=EnZt? DTb$wdSpRnG|-F~٢_G>OxTXŎ-ޝ$F8X$OHζ]J'D$U$UH4hNJ (䤒!Uc-"T,y((x^zJ>KN+~2]`AUC xoYϺ׮1jܭ̛>%t#,~9:Hc7KpH+, 's=tr~I_[!{2gi >C'{<8E,ըRi.uP׮/\긳\U4w9H7Ōsv0@Q5_.>\n^ @ڪlwRMKEe+-²+~ dC/xiO߮'3`?njVůO-#I10x}0u>2ER'KZzY{˹,8xv9 ~4>ޅ@:x2wEԳ7#4׍an@vvd/=3p"O'ZD]b4vix/{B &o< YXr 0ܻY ]JٟNEu"[%Q4]wz SCF<8WsU.TU%0ߟ7Zhy#zna`D䌫n T[ߗ+oez oa =No0vrn(CV&tR:<cGGЌ~W\Ud&{пηq˙pwtgm΀f&=r /8G𡡂o|+[x0t?0˧>ŵyP00¨. ?MySyoi ZB.FG*ιrg3ջكri%5>7u ]Nܜv}^``5D2Hc1-x~?hgNj/=ދ37`L+@QJ{i SbOgpχix{NYmKrtXgrev|ON2A}%|+Fx~uyp .KKotRG|v戧n~>*|a¡\Io0KcV Z#F{_y+FߨҚ^ бywTIIyVC36U+r&u&e0Cx~4?cSFZ DtnC4^ ]75.1 OM[|*y uiҋS:G/QӰji,M>YX]KZbaQǫAUCӾ33gNֶMd;]wZfِ&꯭Z\{~,#QD膂cwry3C*%Jaf˄i[.#/#'dOVe [\]>I1~bUq"y,gḃ]xqʥ46qo9u<&ppw޹s?ϐjN7o~7䉞l s TW&0?jQkgZŁlmGqEˍ;c sh8EiCru=W{ǭO{y@ .3rzj-~7]ǖp^TJpMmhPih?fDC7·dPϙL[+44+蠜ǠdmxIHN|gq~:?5%Fy2Jv*=Ws ᑖ ԙr-w(^AګI"(uHA;3Pe)$4`V)֛H!ıTY+sg!j8 U6@c>*h .88?B4CKW:(eDA!"%? !ʟ0G!5Y(QO!*646B!D Wtz,$Bq" F;]!t]'5NeL&4MCU+6sv+0o+ݰU/bǎ( Z00PE^U^U^U^+!ӦUKbbb}N|>vjl{=wqa6lahҬ6[p#{T^|w-x++;m۶ԭ;qqU[B!*%3#kVӮ][|G5iס#nW.Z4v:a>?&SH~Tmř]LnӖ-7nXT޽{֣'mNj#"kB!DE9_7ӹS2ߗCמ't&{o~RTT$ V1麎aҢUk֮\A|9tLJ3߇+/ߏ*撓rc`upDEEQlϋMӦشaq3hEzo/UQhѲQeGB!8iٲ5=H IDATl)=@,v;9Y?Lq8" n{}N'͚6-u>h23Fӊϵ]AvV:vFQ6;& gnVh, x^N'2IѴ%:6>ݘ[pڭlv;QQ]B!*)2* Qu]qut@7 ݼVd:|~~?>MvV'#3{#;Ϗ/P0p{\^vn CЃ&6:׃!>>8l6*68pޱ݊ V{U.]BAbOF(XHeE1(J!5MԱs\o۶aob挆(E~R[x^n7@EQ0Ll6L&11s0? .~WGx>|l{i }uRU2M+qw k}!e\֚Ӟڀ'_gHa||(]!=}snQewזYྈOW=OOg禡(jY)rydm*U !JU#.r3u_d2Ťix<"#ڿ'r "-(`~?yyy`{#E r $"d^cy Q٥ÿ3/ #;-gѻcKZǭm &r퀮t֝]O>c,+7_H}-3”/b䙽rLߐLg_?r!}:uG.q7W4EHgGc|Ox!<{ :{u&k\>йc:̠^cyfһe"cޅt?^{G63Ӹr _"~40ibif{ 2aJ=v\+y["%5~y&m}S0֡#I;xo⧑w7?sVhfIØ\sNĈǾgrgqF$;1_FAoՊSwPfa7]w53X9 {v}w-}FN/d'[f9=:ԩ=;ƕ.P9Q֥3y}ÜGһStlOS V>grr$zÔiz/Y8yHZ*c}>>gnXf-F3UkI[zd?n{5\-}3nt!/Y[xnz6iI\\Х=#g]yW.ϒ4<κ[R~) wO8mۼr֏ۂ#T\ӉӞXO#9l7JRRWzt;k'-$5t{?x:?%8`i ~oիY~='г;䧒5 ׋Au4Ml6aQ1Ѵj*8f,!TE%/Uĩ1Kh:iKf2GsoO5 X _r7kVbt[|?7 / ['|ϼ~6imbڔ8q饫<:V^;#w5X*\Xi3ro|0Vu`vCSHr7mfyYg9_-N q\>Ɏb³}>{F|3[Ku<nb<.M V1GX ^q *ͦiv7lbU/%я/ۋm( ͵f|#|v 9О,f*Bǟ=?gqfYcjU3&VN3 %HтV"x~g۸uҚ2wpN=cuSlnYXݓK6*7,2rV+kM~m[6~'睭=~+шg7|Z8m5f=ևs2@܋XO{YHz9s n 4hO]K}ݞLqhbbNsj|gOToXg=̢#u׸e깴Dd-gΗ1ctRwô "BcX͡e斌8?й̽)D( #ex ѹk9_б5`K⪋Y9kdWu SuYOHϛ *:Kn W1 PjщDO~Gv%'ǡbZvifY/Y?O۹$ pʼnt9p!CAuvZޛw6g$fdF$7-x&$zTANF :.Ivn.9yǢ:Pih&@7 s,<9*s0gsNUQI 7 9 ?k[ դY雔`6{d :uKts'K^RS#*V^-@gZzD<kԉ Gx?n(]Zwe<=TYϤ^b+"KST="!䁏}?)VHa]druMxfZlک7pf>>ܔ*5Ј:S6˪rzKN])@ U瀊I50 P1f_Mo?~qnj8JUܗ/39/NN;PFْәWb99 ^Sswp8D\G1"cwڈj%Vh&5+7\%&Z$m߰1M ^Xh}U44%rI cuB+I*˺'X"l^~LˁȺfm"-:guR2p{-qћP\ߒ_ro$iQD5hBVAJJR8PCNqr8%NnQ`A&hּ)[4Ef4oь͚%-5 CױX͘MhjtUF.^ǰYs'oh8ơ$ `6D7 27S c7n&m_Zidy B>f2Y`qg:y?r $1i|9^䦧V{i ?x]<}T6Vvt.,>#;]M812\݋bPP0':OCTSy7]歿d9Atga~8^L^Iz  c_fJ*[75=k;Y2^z!EGMQ_G{ gTJD 壕Mߺn%DCGr9&#CzL/n^nFk@7_c:9|vI V}XcRnT_V.~iTnBL9=Y xt^*U bm9i{сfpؐW"Mac߲xv3o"rP:I|"+磭9ytL^A[ݓh0%ҮucNdcSd=Nnb. ǵ}.%ZheߦC#w#s6*0ﶡ\&:h׶.V=^nt`f[Ԅ՚::_Ņ12w0.po#C~L\ˎ4R(7,oy;g~g`i6i斜}v}vy\ةL z3z;7_1mnr0anNކ*h|]OzIKsp *&M'/-{(:2=A%S7`鼿0L_5OEDsOגg>>~"Rgu 0!#|t/N "/ :$$ƣ44U`2,VS z{UsҜ|qt6=vvq}Fqy';s?0t8miRbd.Y;rx(Zth&?Й߯Fn}FQ=s];Շ.;4g3/a#'+QIC4w8à=hPax%fKhS2Mhg_ęg gMw^Z3n9Wsj$uH7[ho24iLjZӗzU-ڷa Wu^R] !ܩ]N! ZKGOnYnMνaյI:pII.rc$zte&2~3+wOa֛sޗ ㇬PQw=}k7<œ7xzw SϢW$H sEW`isjzAܹ-Wʯxe|!)eřc2^IIt~SS{0zʹqTUt@A⳩ '* /) Xm""Xm)(t@3i`8U3^Q.̸[=W)wɰsjf^ϓFU{fmD'mh؃OIx;~v6ĢR][4R~7;.HN]'U '̡EwEط HL|] (rL@hb5*qJML 7mWJV0_a>#ٽs[0hӡw hf-29n}vsRW:69XuŪj_)y&s(8/Lzf==DDKJpxW{:N~yo |=O %m攇K*=fv 1&.U@ 1B=!]`CRje߸fwv啛'8݆Zd|vۭ"#j0:XO| +ȌL~//c*Gwh Cqjj Oqi> ܶBUeSw8luVԥwSZu=ֶ7W֐yʘ>`oU_fq9eJpwo]VQ-s5M#22˗rFdrBPD%/QŘ#TvxS4%2:DŽBqlNNvHJ V ['Nw=z"!11:ʼGRSYb :- "/Ng.;wFWvNv+TV^IUh٢9"zpjlVҎVu!rvBgnqF$''oΩEGEQ^]6ln+svƍ| ??&ۭ_. 5n1oXV,Y!8{~+Jtt4V:up\|"ׇ}qsp^鳦|},] >'w/i[ˆ[V?F=laEs4fB1_qQmҳ|q ӄ8p:|ҙ&<羻ײII=LNl_nJŨO܌az8q!5Xs&66S#xzSL>]:оU},qԋ3wL,Ff#yKJE# KU^xqX\l}k` ƞSL9̝?xVCAt@:*9+'swgsifykfTNb۝V=ltI\p'-t:(:07K + Mg6rhxa,F\408JԀ>ĦaNE]39)55{DVc),",b :hzHƞ2<'^D{.2=XD֭)V"T.D `|iUS00tn3QceU Q f%b¦Pً W0YiR4l*@02'`S*T=Hz I1V` !e{{t&2шמ3/aaGOF.ѱ'8­\itN*04bR#g?|Y熧'򋓘y4+U$B*ShoJ,|;&}`mu}_hI~ݰn^@ǵ)6\PRtѹ?Hp䧯2J'e\D-UO_”؈vʪ'AQ4fʿ ",_[{mսrzy1x8 9NG/O;/~7Ot8¢R(tf '?"nkNЛ:W:a6BDRUgv:\!kl5/fܛIo/!ֿEvKп4osSt B! 簵> Tcߗ=yɋ8ɚņ+mr>M*BB(~r?Ln=3x5Gӹ3#~BQ%xT!>n"K?N!BcP aB!j^՚]!DNB!qb  !S`WK"!Bt%z{UGfV&;vrc(Z(****U~Jmvbdxd⫝ !BH9tUUJ`L0 滨Х[O7o#"jB!~6^ImViǠˢ4mڌC !B&*:fMujK6:UaؤG!8GDV),_B!8T5f }NJB!NѳW3B!U=fQdn/!B*m~G!uǢ*XS^B!'b!pLz{ !BR#B(2QCB!SbB!D]}BqbzRګkB!8~B!Dũ r(BN)>ȡB!ĉa~BQKJ}BQi,B!T٨F՗ !xNu,R#B_VlC%B%BQaj;B!lj!V roB!81("B!N UY!B!NՍYAWw BqT&40B!%BZE!B*^Ho/!B &3!B*&a !x bb@^2B!j8B!D- ~d!BtpGJ~BQɽBqfb:fkB!8.{{EB!B B!B!+: r(B<%FB!8^AuB!D-wL_%B!NՋYB!jr!BLYJ~BQH#BZ%ݐz/!B\UR5UsB!U~B:KB!j`cHB!j8^%BN)21&C!⸨n ~B!NTB!TŘh!BB!BEK~K!5PJ~BQT !xN̢^Ll*aB!NUYB!j! ~BQEBQÅ2!B*!vUtI#BZE!B*2B!j^oD!j/!B*R%BZEJ~BQFxS!BOSdbS!BBG!,BH)B!D͗HɏB!j]!VG!Jhn/i#B-.S)B!D"!V0d!BF6?v2B!iGBQFxG!5.BZHBQ;Hg!Bp2ȡB!jPg!B͐B!BB!DͦP*BQIB!j! ~BQ+sIB!j)B!DPB!D s{ !v0B!j!T!CMK!5T{ !6 V{^B!%TÐj/!BlT{ !V ?R%B.wB!D-!BQ <B!)2B!jB!D-$ !VP"^B!D gHB!j`"^B!dC!B2B!jj?-B!iՊ;B ?ըRTvT}B!gߨj+=j'F!4o=8"".ToC݆f˺lY]׫(!B|=V Zwn7A'6XlX,D!dHo/!BFGq~BQBwQǿLICPW*?إRD~ kCT)Y)ABf3)DT$$眜f}9+ $?B!hPJUB!YeU'{ !+wu !,MтA~]wM&TS& 1F5%؟-BѠV-0CJhp}~H;zO1F 5F =Ji#!a L$uL)Tk #I9p5Քƾ2$B‚R.@]wV4źFB6ݵ~!ǨT6zlBQ `,V+yuݝ:|D:/ 22ï*#-7'w|>ݠbo׎CrVP.8žBg>K{ ! ]J@uFuvz[e(w=K/hEQ(<};u`OH1 ιb_Y2%J4zs>??>~hv1tgͤ!m6nHɓXfO$mnSA~5-4%!aҵdf3k%ws+wM$d4}Dpt+JB~ʂO9Mr=ʇ3p's@9% m#-/sp 3ٳq3Cv@.7ُMĭI 9sѳQō\`9+n%54FmqmrWVXb0<8Q˘V;^]!D0tV}?5]Z7{+13VP_4%3#H^kO,1a29v͏߼3yvX2vP4̞ ̞K;OfNV&=q}Do2qex\n2>B|z{|xqv*h]&5ni{*q(T_,|-&3m% %Jɉ)~~G/ӛ?g{o6X|~ӗc/믲?}qٔnʏB* l۽$%wܖ訦At/̌1бmJ]bƆofNqv]H@ rzFPM5%{;gfa+"% Ew>1gX_9n08*F"]9K(1\7m W*6h`i%MFƵ( c,1+ʼnAECyaَQܐdMǚؗib=Tzj#^B!G84FD8C*_)B'So9{1z;d*8; _, HkEBbWcl5 453#sBTTǟ@-:5 F.nS=ՠc3*['Tp [ })r~w\{9V, EG5t]G1Y0)`PAӂYCb7EȡƲ#v@Tzh^k~ʄ<B!ƴ+v:vSD4"ϏCI:v 4B_1Znk}](u= ^@\R+f;7L˩I3}_a ‰%OWou(ۋ>n&ݿ/>W-F0%*q]|vh #m|-Vf_^'Y0=Mٞ1- M5 M5;fE_CS݇iv엏f)&ߑqjgYqtawe!><7uk"FF>@kL|Hz!]r5ikJWi|:Yuwqa&?YQɓKfEHF`1~P/?Pvt,G۲&ĿFtôc6/Atu'}6С5si/!URթgIsy?{Ī:b kWeXALju GL^-ilhޗMgʜ,{QWDSGe]8cR6NxO?Mrs v+yo9 ])TK?U| %#g0u*VGDԝg0(% c7wq'NgQ4N]pxIK!8#qD?_] KzrgOPt&-[nӐcT"! JHdú((,.՚Hx5mrjJec_!(D6a".&[]5FłICQMl+C!aSՊb)?!PPi1)ľ"䧖 !T_I{(z(=YG!\YϙB!D}Ϩ4BJG!QQdB!9t+ BQ)JHG!ܙɏ*!Bc<)Ȃg!B_{&?ޠ<)B~}1Bɏ1$Dn^ !BԐ¼x=^l6K z'%>JO zwy$[%fDͦR3ΆW$}`50ƘT|5}wJ 3' .$t$m 4CcKB4" r0|z= Q8=|~u^pb- G`51-d(?Hi{$4Frrr ičx V4(X@-/@O$ I <@y2/ &Dj0wF+XPQCdfAlA>e;奺s+pP<ГTBÿT2(U G٪yD ~$A/@Dmqb9;b{j:ߏFxe@rjbKM{TLf`=Vnf"tm{ywe8Ҝ,fI 5֛B!ϣSgp, XhӶ <9aDMx@+H0ejځZ`,@x_PP aD@y0hId"9I!Q!iPA߾pur* W 0`A%I=8h;| {SSEU$4(q=HِB3UdzIPB8_zu}>Ump PZz:k@NzyRwRWTiGNN*F}P A~e\ WcފUt;\pupX.UZp!A0P=7f l)6I7FFf~G̓ Qr;j6҉N#]Bg$"yyN\C UԼUAeT@'dG{CI(Q>8z$8  ];|bЮv@жd)FʼE~|"'ˠ)ƙ$YQ e+gW$yE$"jyJ3Pyjvk_ EWRhPbl1, RKں:TPm)va( }?,|^/~?^}L{(][3H'2cc0 >+ZE=i B:thCchB^N:Μ< eQ^qYYYFƍPQ^e 7?F Tx4ʼn"ɣL^l"ꓝZɇfK0reb/PS]-aEp8  (sb+&RH\?kj$AuU5.XۭAZ [vrn]ZVѱtW4x0zT:^(WOxTZM$ RERH<)D\1fΘ ghͣN%O BI)b5n$'FpΖ/9<}cEy9#ǒ%Kpw>UF((ˆAq'Cҋ/q^ņ g7jV:ADROL:thС=>Cc73pMؼ`ҟ qlSDn'OP|>$'%,+W*5t)M52XN(8 ÃE6W*_4Yk"Ə+q""źğ}?mތ:LSAG97EsA9R(FW5SAqpm>RXn꫺z \ӧM5PYY?0z쉛o { qFx09`Dz|HZ=]Q~|@LcP eP`5L/] SD/}0QYw.nhUС6={U0v8uv DGuu5x+.ǎ]ٟ‹/ ,W?dB.p?GͷO7_/lܰ[mGZz:dY㨹H9PZYOuďȝKP 1 ..7<@@r:z U,?Wן}=G?o:7|Q[[+))KXG޽񏿿~6my[| ?_=k"=|]SSG|g"e^{ k׬AN nQ}u~l 0t0,_Lb=-첧FKQ#1PYYȊ $Y!?~<9"`[#6 IV+,fP>`ӟD炎2^;zT[H  #=-ڏ#68_J6=#'cNȴ-1'\G/^"_ZRW+ti4}r<~?jGp` 0kx 2{2l~6 9͚ԉp930g|X,RvxĦMҥ8~8*zFB;B9`K!:1Сh؏:c~_)`Zbx4q8wl6Vk?:DڲDqJZ<ʼePIZU#I,|/r|>>#yb:,lZw꒿|̛;x.C@z3Hyֵ^/>~t 7Zp%=%r>0);s<Ѓ?BPVZm۶7@a~>y)l߶ JˤK !z3ޟ[E^ƛ`0(DzB ˄߸I}I[rVk[hVW(Ab6i*}c(b>>O8?z^;m*蒩prHIG8ęQS]%8p.S_R]9sgQt}f3'+` -SSǏ",\ ]"Lmq'/ {ytЮ8\=ߴdfe"%-;wF~~{ԇi;vfGfV&0 {O5(|ʫ~A"'}y]0yVN ] *epxd=Y L49FJ:dy>jWÚ"t-Ut_/ tGO^V^O?͆N"=tǟuu.5;}O^/@t'@qKx?L>gS|"ƧҢui".+\Z>=CCfj><(xV M 0d64s Q_o Zzv{j*23`2e]H32aKK;wM ؾT%ɄLwSGuUxpB#o48maiKC eQRR={fKAΝx qL&$$%`ҥ rrr4 CHS_!px}C#Je-Fx3)?mb`6adFRrL&3)))A'i [K,"{Rmv;>Ld2!99^GXC! kR2NBl@ͮ$x^RR2!Y,f~va&2O*#%̀9{j*<`4hӦ-ʕ|g|l`6[0 X/ݓ:M.]snCA e ,Kt` (( ZFy9,ˢY޽)))(G(K%&'cϞ=Cҥ34zd#ݻwfKAA~Gu{ѹsgM,ˢ\ ͆ؾ@tFN3:^~:C'+=o罈+-he6ġgd @T"DΞ-ѣGPY^hԸ1II(++ɓvڡyi1@FXeuoq9 $mIV`G%/nD^C`kjȒ I)SN9N;IcZNww݅d2cAh~e -A%>]Ey. aE%%jDF 饺"C4!ѫ4@ gqaT#u.7i χdSF[2xxGAeEr[F&MDإ8yѶm;4oRw**xaĩSpnj+w`Ȉ!6`/߮Z>E7uc5"D Pr.`Vhܤ r 3%y ŀ6c2tcr &#pLH/bZ.V(4q/ ,x>c2hu"1D&GJMb~'t2*[Ἦ&𠁮M`5}nC#43t#.M|*peeeE0bE#M4AӜd\Zt BIIolK7ш=wPW[`(ł f:؀,Έбw"9UW:v7T)OԄAnx}>!XRR ?ėU F&~=KLP dEB0YT` .x^٩qؓ.;_`0> JW )pNSQ'HsBnQc$@dS# DD"N =hwˆ3Q#jZ*BcQ ;@Ѽ@O1L oę"0A/1t,H@`L%pO߂ _$%ޗibUcMa'4>jbXl(@=[UlS<OAGjlb(j7T#híg@L *.Ic@+`G@P`,p8)2daq#4V3c75'DQ'PUD'wԩHe˷c&"amtҬ>P8/)rƔE\P⢮rV%; R(lW6[*O_$ɂ Us>Z}9bN36uoʵ9w b  =kp@T/UF"O5h@c_ 3FnXa6a2GX"10 KX^8j'co'Lae9&C/GڲxJˬԥ×^Laף+ΠU/$&N(`CTL0$./ c͚!9F(=W)~b 0&4nePvDx!lAf͐ G(Tf0c2If('Nx@ v1BSa. ŠJ_O(* Pfh<(I;XPpjDD{e¨*$t/A(-+ɅFo!67Tw)6hAS` 0,30(d]j-pM1h!Y<]. !bѶ]ugdX*7G0? bYоݰX,hDZB*/^@]!/ǎaB؎!u'9% DH{rPx? )'c\c Uʊ %A *ȇnԍZB$*|RD-ц˨e*?\d¼))TF&c+J]LE ZDGc,*N%CE/JpG&*|Qk mq$z:/=A)#'> .u ԠYNs c9P_Wf9-PSS#o3'!55 M  5נw^`YMS'\*/ilv`YV biDZ)?Lť>uR/Uʼ!-O&SL@ʉL\ SV㌵IvhꆢРqCP )UT9W2hQWX.SIV18TƍWB5NFiÀ˲nG DZGfCeeX˲bA(B=(:w ݺw?Ǎ @VUU,ʪ*X-fB!e8m6A6k.;]8@tePdwy9r&ǠHǑmuw:hٲ:kd;C'%b|<%P?TXC!ry $h4JM'41u a&uLzD܁DD$d5S[P]3=&$c|!n eGW^+[l@NAz%qz=X>H0|^}^^lxܘ6Imx{tp>iӦW^- ׿s [j*L&`{c=qHMM߁S#3L=)AEDGh ͛E(O<Xҗ6BД'IzrQIN}A-)VYQ}w`EںZ %%%Q_K//޽)(W! }zRzA]bD'M7;Dk UV T0C,wy}=n>Ջ'Mœٳysf =wO-)H?z͛6I˜vF=={SݳU҇}zƗK71μ5}6<K?4>5TP;RNN:ɫcI0.QƐm01 +,Xw*w=k8vN<Ƭr8@+k֮+/.L{߯U]Őtx_n%I1˓]X~']\\,aZ~p\%]dmBdpbX7nzjAZ { 1|~>e@DrZ+2h.]gy}CUUf9fL_n)H'O?׬mۄqJW2aΙn]r 9i۷ Sb٨dXp[y_R5lٺU#}[VkD,^ ފ/ 76߯ŽCS@./X,x<?~s| ?c aҥ xwwN,B5{ر#K\* 3n7?g kxwb][oAX$P"G7jN:aƍ=χG{fW6mFN(H -Ӟ{',CN>hb:|X ѵkW̘>.` ذa#^/6nBx; r>vҢ<;(((Ċ+ѵ[7; Xp8z(/Y'Mû3fug8֛ob!jWطw/_jc`ʕoVĘ1c?z?Aſ "!Ʉ ر ZjM=- ?oߎtL|:vl 5 W&ܵ5xx o NW,VD^jGǕC፷BMe9j+ocРpUq$e3T[2|8~q/[ & #=ƍêoV XG1B2vq׷ʄ a=;v vnؾc&z+l6;ƍ;wTVRlvM W&j ֺȤ5uuX|B23]00t!cehJlv\-X4k~%n|tD^lcSWVcf∉5k76"\I+^Dyá\XaA̅<$9%!##CrZ89LlG.]%gQE1c x_MC;'Gԟ$aX酳둖np[t p82PSO>֭[Ga[|4_DEƅ eQ1\NMM '22;Hb7z8 /^b;b׮]ڵ+v܉N:rRmq=jjM (T3g`-q-[裏RmV&`m^СC0sx'A` ~i~+5Db aPQ$g劕[f.흞:^8'mu4JD*հ"1NAcEqSz |_(8wnG+ęb]_w/84mWh=-q~dt+7}Yi)vډ@ @O4N[H'. ՘hu!PNU1PN L&NbJ*(;_:TTVqFv.r-PDsࣈW4œO> B;‹XxzbSѭ[7̝3W]uRsΘ3g6v & D 'i {PFDuu.kt:.i~ :w@9hۮ->m̃EZXX5];'M]t:\ռN\KZnee|2[xGQG3331o|'p:]hӶ->#'9ڵkO>tѭkW8N۰֭;ek"rN&'J/œ9sЪU+!Lq1䓸e-`& PETXUx q? J8X|8i}sϡh@tM ?礤&WPqv;22ґ؊TU*Zj*hOJ={Ă 7']z"k۷NJ+z7+U_B!|hCM6`%#":}PW_[-R^x=z`ybޭ;5 sSׂeCh֬J!>_s*`a?>F#Q0w\ 6\j IDgp=wDzx|rQ .ysѣGcAX`! BbႅV aE ;0k,^ 3~igϞXx<^̝Iʌ:`үv/SLÎz_G @ >ӧ̙ ϋ˖ᑇ `  ` q&%K0T{u cWKk0 CT>@q>o͆ZJpZh4ر @VV6nۃsl6 Xl<^ϔ-ѽ{wt RJHep԰ (DU1ST\֩(4y}BOd2nIh٪%hҤ1vjDDJf'9xr~ѣ;v.\IwM2ӈM4"Gg}wᶉ!M$%%#xꩧq-9p1՗<1qmmvjW7)(..7݌Y´iOyW0mړ7n<8&c=馛qaa |>|ϗĴ'I~DC*-[U] An=+D^=2[v>V^cǢmv|s*vރ믻S  ˲׿_/) o//͛CnSNݻq8t>`nuxsF'``e7vtϣAC܂u?[n;C1j(̛7{> )#ClOQ tEEtSzj3څ n/iDh/Z+ >*;||W o}׿ǣM۶9!V^qƣ][܇={nÇqߔ) ;"&/>qCgKI}=6 % q}ٚjEbGxʼnva.|"p"Čd 'V%qjnjtcaDqމzRa]V1ѸK[41ЊPUUn0Av]2",C7\WP"FYt3lT9mk^͈$>%D:$iu:r'H! (lY AR M8R)ژx`Qp~({KR"ʫ .fCb 4b4(ƠW0$K!{@#>0X/Ct~8~Pҳ .yGiHPuH*x4{`MÓI\ V CC]~-צ7Ds_=xu]xgv5|{͵<6m 4sm7=4lKtuHt@LT)oA%psQvQjG(z0 ӄp$8~l܉ٮi z.B&DP\`1@b3ľ R Jh8&1:O'EUb IDAT QBV)x)Ō Ht]f+tSoz=^cEDsEC[P-cq C7x%H"]CJc.M8)ѫD~ ~Q vo/]H%/Ֆ5l wLA=j*}]Ag=kdaKlC,tV# lJ!`!vL:DoAC(\=X`u_E`!ŵja0{h^6,ȈrQCj*@{>BG%$C/6+'Ĕn14Ȏ 1q-`D eY+9f9UߓN [Q(1꩑U 1˲(9[-D T|ɲ,JѼE ;w;wZ36&_\W,nݻ㊦M [UjdȠKca[' Gc#Ο_\WqtE:ܯ \YlټC S7 0zb\=[6nĐ#i|r*~q7%++--[W\aZ}rOsh$2^ CS\(JnfI&_WRCGb)<].I^YYoʏ۷oC^Шqcq2/_S¿hkXE]]=ZZxoKQ~ "6eY֢UCȪF/. BV KH|aupqQ?k( ~ll6tBs$ " }{Mj*ExQQQ@0.#d2-B!\p,p ))YDajAvv6,#[0fYY˲8+`A+3_Яn{R%زHc.JV-&9QJd(sf2Zm]INêÁDeUv:VBq|~?qa822,n'~E3]$''c&7/ꃘh9O3 ?eKd2-?kS^8~TqxyG'*\gbKx%,sFQ2pl)(?%B}HJ`1Avaw[l3(x5 rQlnzljhbqLRlK̝m)H^m:Or}٥\ ؽg/ :vŒI D]ty3豓VVTT!+˥HֆSy 'D{%gbM˩LJvHa$d4alI܆AfT! NUs0b.>i9ass})H 2Ѹ6~CLW'PZ^"o {[e4i!G%eUu`F8vV5+z6py2 rߣMe.6[o#UhׂA`,Dž_kA Qv N gKJpe_0SP0ϋf9pe\w,8g uAhѼ8@ O%@B@?:Qk;OH9l^ ͚7ǐ#ѪujD˜:n2,\W>{!:t舔\$e`"P[Cmuz #rD.&8t08ӁWTWBi)8vErJ ))6=JזӼVְW_c|cرcyy@971[n7mBzz:Vۏ:ss|:?98qY),[?T]3@x<^OX6'靨 T`~CT `+` /u7bֺRE\xǎC }{NbR:~;Gρ8TVV#)iBWY&d_;l(,SB0Q_]UAG*AWFG?eflaN`@5N@ &A3ೂJb+O'\(a ^;z~NFd<_QZZJ=f[-&e>J?x)1tHXw\ qcV"x}Aȹ>a"@zla`?;:/ؠ  'D,ˡS*U]LX,˂8s׶=9DWDx0D3u#y/D{E>^Ir $zދ&T)Eذ"(HUG/ҤCzz˵qwrI.%;ٙggI'AId}1K` %jmV«"bSG9ܤ¹Z%lҐNvрR$-= 88Zm/&Q&@ :u[4/BI(}U'|{];ѻ]6l$旝hL&n&$p1zKnϿԱ}Ξ=C9v0ݻw3-Kωntòeksbl!*gko5j:gܘDDZ>Xn߾ͪ0} BBB aŻ5L(#rxn5k?sܶ.U,,AhzNI5 J<+J q1b dggcKly0.uM+6r%އCdSyuO߇=]4 oQaMF\}֪( T*Aht\!1)Ƶ*ӱ)-DϺlݦ& ɌNo"4#$@_B|11-=fg&U>ΎpᾺi.QBAHH0)IFݜ>1>ks5#?.jBj`+~װxҏ)K '8 .lqe|jL&:jX"AAdfd7IJJ&;[NC׻5JPR0]d,âCt;… RO eNvt~vDEE?p0_ ghт۷SF I)$%PF ;oE.F+r 3֙Ph '073HLrmgy6l()II,[бS']=II$'afDLL ΝKpP #JN8 ac8j.֧DN%MuU>tqn6Ѱif8u/(<e,Nrpx(Oȑ#իdff9u:vc|~`T(@?y$(,]рuf Z CBIO 55:-$RǬz\ fM017M^:&9%l2a6R$WJFYa/.0@!'72D T5]>bE";1C%9IRHLLGh`4R0׏;HJJh@ףڴtmV)B ^rKNm e.Pб߉e,-_N:u0DZQTt:.QDgе[7vٳg,n\rdb L |DtT$3U\JfVsEǥ~afcȐ!iۖ~Ƽ_CѨQ#y*WdpϞŹMpŅ Uw􎰑Wp<0,/?m .qrӥKW.Y¶y<@ +V$))]vsʕ"URy8,G\__{T| UDꁰ):@| ^8<oAT +C`E F4a2\ę qdg뤡271'p-La0VskEt#.l6{mdVCY^gr9||ԾԮ ARȚ0j/bCŰ>Z(YөGBPIjhHOOTd,kl K"X{HIڸp2‹v7m&685o5gԩ4ڲ?LʼDEV,Vڶe/0a$A`W_Үm[{L&3TRE@hh i>APl*RWw^~z)VW&(bgRZQUCg#qfjjtZ4 JJE GHh*T@TJғɢ 39b [Pƾhۮ=5kj7aۦLɣ@L^I?Щsg.Yn;u̱E^,K_r f+'F#Y:#f;k,]ӻwo.\ȭ7PTddd0a/^K\špk@0 PkvljբO{˘OStU"wc07+Vr& ];Z?KХK.\HjJ2PTL8%K's>BQBP *kY4@0hf ؔ U$ JW)qc2\|Oiذt"RyR97ZfB`9s\]PbCv-!!*Uh$+3BM/"&3&R!znS6fr瞦e4X,z=l:Ç}6z,RSSIJLSTɺTޕF]ژJ(8oABJ }5Q$%]VgШP[&~|}}Q},kXgٗmߘ ,EѣѩC+tt؁[,lDDD0v .dgj2l6Tڏ]xdp֯_̙ ~ gΜeYa#C bjժ0sq.pRYi< MUo4oָrrr"?4￟K=?C<¾رc[_,]NN¶"*|#HFRcO~j˰і3JD-{gRSSHsd2V P*IIMOʵ bQ cV#]Ǖ`v6aOt*ӏXR\ IDATW:Df222E˗/HՋ^o$=Kk}02d̂EP(d\Ϗd 0L zfLRSSRoEVv6FɄJFVlL|9}pE%B߂B  X )+7 h, `Ěf1)|^){›@vmٲyHH6B Ls"ժV'Kء3Ocqܼuo)~M9~JX>ŋ09MBA1 ,]Rz4l؈RnMZvv67lmt҅KSz|}}4q2dzpB[=#.ve@.`JOQbHS"(DF%?fZ [u A gvIkL0[6hOHh_|~;e^NG`D ( Xm z.b-`0LsX=6[q4d27 f61ܺu&\:(x"i^y-gN(%DPPYYY٧siذZOJt4Zm6AAA)p_Ƽ8} Sv|~j׮ѻ/(Ƞ"鄈DT,>)Έ`9t0ѕ#ۧOaƎ; g.DFx6â(L;PU5aQb  4# 0sClBaPbc1(0 F)ȃC~T*zAVHIM^RRŘu'i}pWBVz7ʃd">!-[y} p7i*FzU[EUv;ٝ_S8+](ROQ.22jӧNT!#+ H3dd#r;)EZAE׈Eyexu:5jj22%]8͑) FVARmLFv-ZM|b{@n.c"#sc'N]2Elh5SAn&+b#n/{{8&x4Fw8(YYe%L2DXKLl2IFFFFFFbX)"mDrmL&ׯ]%*=8]ڞ׮r`>zKpPP &#HM&w%j RY dmv { 'DDE[6bjtw1 (UjT׷h;&'rrr`(22wz;ݍMoi;$+2h,B:usJurIFcmғΜ=(>(pG0ԯW2)[os7Rŝ~4څ yCIKOAl;L&rrrV#'S!{;CzJZZNa&w͋֞TN8Nƍ )kqCwj)vDC2ed"%%N[xшF!%%АR7VtM5V:jڥAvV*ѴiӲXM뽷SO a?!ΐ)mlKܚrfBvR bbj\z ľ{iۮӲ &6&KΗtnkeYEym?]X!NIOp-^p9hْӬYs.۱kiر]:%yÇy?~CwW^-VZ!=='G=N xr?ӽEi O*mܹ3j&O / ɫ9z=C V1`ƌ˃>OOK1%ĴiӹuxqMPTT\Y _[jj*sga:5 oÝ+\!PBJ%AAAٓgyFСصkÇ`ذRG2aDzhƍСCE18p{aР=˗/(pFŋgСo׎HNNݺveth+JĜ0~X_rGa/€3cKdffzt/<>}\*76<ȌiӈE_W_Mj՘ )/LF^wcHKKs[]ݻ’\ɓ'ӥkWj5L4Oug4R];w:\? Gחz A eĈ;wNn{Lm۶ckמ\PLzHIW V+CXEѻ/11K.ѡ}|pzVjf3k׮Qy_lڸ#Gh")ҥK_߯&%K]j(KvVGn11!!Çٱc{E]ݥ|ssut֍}{믿we,]TŋY|> ˗ܿ+ƍˆ>l(}R@qEV}-={V4fd֭,Z;J?C+O>$.hFRj1Lڽ+_E9^xq:Ӧ(INJdڋ/ N Jme;j!11Y=c ƱGyiB=\JO?@,[.M gɲh4}i@kkۖtp߲e ۷#$8mvu^<55cAh4ҿƟErr2ݺve/$GcAh4~xOp3쭀 &;$wnVZޝ N:U CQQNd=0㥙 >/'==-aÆhоC׋Ҧukj5ݻwƍ(<<`zϜ)Pg_3[uִmӖ=ۙd5Сj>}p5)``AE{ԭ[9|Մ̚9Դ4f47n'PfMy( Ge!h4<xt/bT8R#& 6{ift:6nAս+,yPbb"ҹMVF T?(ܹpbbc]^=} 'i< {A=ұCwdbذӡC{n.ܒ DEEM|\\n11,]_~qzkUyok8=z%((pn'&Uw<$4T:=MV^sSRB8'N,v b2g( V*z.M5~׮S_ե EM[4}ݺѵ[7_ή]yx%oR<$,YB||:._l3 0l!Z9{ۿ(Iof5[|>tyqert +C͆ACv4k̙ÃXd_} Zj!O7xw5 oGEE1pÜ9s`93p V sOKK, yDp_dќ:yJk1b~'vY#揷8GNyl(FÀDq꿰qof-N&33,nE3( Ca޼yaFi(z{nݚ-n2~7oޜАP>s4 -מ[NCϏˇNCSg|Qr2ݮ\!"/Ĝ>o?3pb?a1 m 65kְ{nz=iiiZy])|xx8Cղnz <ٳDʕ1uhќ͛7X~͛7s3*U1\~J*yǏgriQa/|%Y^fդH+ZeUFժU1 []Xm~Fղu4lذD&RRYr% 6רQjժ7J||\>g}F3zh>|O>9Zjh\v7 8O>ԩS9q$#G>ʹsSȹ5k$%%ݻwF͚5=r۶m۶m#>>;wA Ҵ[4 OQ\/p%&M̀3aDSOW^иqczA&M,רQQF{nƏW`|U۷@ر#Gƍےg}S2fX6m# fԨ'zJʤVX33sE߾cƌ 17{t/kjG+9ɓ6lh>7W[.o櫯̙3 6={{Ү][1fݭ[74nݺ9u/zm~-^{u{Rƍ%!*#Gdժy罖V^য়~g^^ Zƍ%99DƏKN /zvܹ3i.'>&DL&ZjJ=22ťo~.61 9{|GD-ԫ[ԷF>w<ҍr|B SB:u{Bvk0]Weسg6l>si.,w[;(z- 4CѶ][_@@)cv>,M6cђ%;'N૯ޣߒj wŶE[°q;vs;RArR2>>>.D|||};ҵ-MIcUXgTZf͚qQ6o@-)Ð70L>}ZȬY3wA-<{oa~Xͷe&K}yWywh۪%of>.$D): mȔ ؏iw>>8AnrKlF:uRN֫Gο?`13K<9f O<$cǍG9vaæ7X3֠Aڐ) 2339YIDDDH[=|JIZ,|^M **uտ#(H2͜4׮_j*e wAyޢđÇiѲSJ]5r&:u$L&姠df3i$\y`\ rT%VFhhH5ܲEh˷`n3nhWݹ]٥(Xa'tnwarV]neddGCa&`i^4ZA,u97UK\lm^={[R@AMA$/XSI2 ecU@zJyҵ`{$a"Zmed}vKdnˑ H<3fޚw6P˖c6|(k1_˵Jrw{eddddddq艨]aYQnxnꥸT IDATL|Y!#sT"ȸ!&&˗/; lXis.kdddddd?l"}Pi"######sb퉐-ddddddd Ӟ*wz dl,s Z#Keddddd>$%¾/Blwu#kѲvҖDO?HT&8i*?Y&4ބk'} oHY######s`]p'"ݔu <(e<npѤ{qEF ~2P6v,Kv6S -Z5R~edddddd<ǹo鉈KM ,<Z2^dư6=z6;O<@:-q¿xDxPs)/S֏sJ A!ԨQQ"lt ZI=6?`4Y5,߾l 1-??6x?ba޼wuI=Cyg[r Y\G-%YaR GEM%lR@F.mN 埮`ɫKGR#O#Ol~/>?7h}|}}%9zN݆Xn=?vDDE&NONwy(ozF=$~5?8v }׉d ht>;cj*L2uh4gQz Zr$ 2lPF=t=))>sQf-OFddd򎍍ӹHbbb.wF;?'@4( },/[Ha(궔8%0i3'2|؋=^oOz C^Uݖ4Vo~˚mkqm1Dz~o0nrz)!++kѠ~}']ϻGQ(^,0]~Lf35ccVC;.]3'WypZFbf5 -b옱4k֔~lR,YJlL,f#̟?y>w333wy;0ࡇJ=OHKKcM4jHlrի֭g̜RCY{̷eKU$ U,u&_A?S>15ecH~zE pG ENe`эdAY&11̘1]rZժT\]vӫWOOB^?WqMyY~w/z1fΝ;1j(ʐFhh< wh~W_~)w֕O?L:?x_.FGоC|7nd 4^zN&PR.",,- K׎_CCs.g-##so'"RdP*Ti-T 8…WݺuǓ1wtr?|>wY\ еkWv}uv+sϜu+CxXIIIT\m> ge$$ģ8$(((opg鈈(1Y KI܏J  88tw.)֬YìYEZ}'سgOw{SSS={6&믣T*ZU=tgÈH-={v2TҾ/˜ǘgdǡl?-Yd)~~~פI4 vTb={p4`tpuo>oaڵaINIqyW_}>2oIge !-=HKO'$$ey^`˖-#aLzz:+Vٳٸq#gfXe˖X%z>sLO+K~wy' "`a2bFYwSӥ&dIҨJ,yYO ZԩS;Sԭ[9:P#&G}0b^}U4iJ> "==ӨQe2d/+VcǎhZ~Ξ=+\RvEYݏf͚eV ƍi֬i㌉Q|a/_RlY+=@;zBouVz2dg̟???nנ8w\VVDVdJ&PiSXJǂ_u,YPþSNOtɩNʲeyg vy$Cll /<+Ve˖LC|3cڴiŎ++8fQ(ߟ+Wо}{{r5Vf(>>tڕٳgtJqq  TZfԔdFqq4j|_Y$#sOLەW4haj߽B&MWpW4h_zv8CfgSzFI|\JE6)l/!#sg;;ÙJPRh:qPܽy)AܮX{a s{6,,22即NHiu6E # @~q_4 X9gEh)}͖DKd&!mk(9:] ?p|vcmI`1kOT: 8l?Q/ۉ;w/pk k/Vr(lJW$َK훎ayɘ+Ww콷*xz[X⿌W&6{22Ev}ǩx JC>l%B>5|kB^70y尓5ǿȽ4}r` SStsRѪ:Q~E1}\[SH=CKQaq&[ɰQȫR!_;Bx~T^A-8qzt#.>[iִ 9-7':xgϝvb" ^OvF=7g#.ZoRҖa$##sc]D7NƎ~@Uir_c._вEs,[A-0ԭSi<;}3=Uƅ[6.^zjJ]S-tSŎqWoA7涝Xq-}g7Lr꟣^V e~c RT*Eu z"FQ kʒvhזu7Ѡ~=9Cl0ddd,|E ' 7mɈRY4o9E^η)rno[׮#AA5Ϗ"+VdCޓ[Xpfxab-n-k.|~eoagQ&Fo|Z-Zw~>jժ (aLI"n /ha[Oĭ[8ȑ}Fm-G/\Ǜ6rE<Ӭ[g.^00ijy55(C$0 RfZܲ M/_rjظi3|8ڹc,'gGY'GgF7] X?Ca¡'^.L"rvy,O 01}7sسّ-.~Vc-kǧhlifgFiޱ=Sf@34}@Q"ʹe^ݿlm!hB,MDصc{ ̾TUW~aB(<4[W?4@!B Q*2ODq˖zR׻X4sg]gEuuC&kMzB>uV@8Aos\߄(eK_vmD@WKDZC .}zZ~#G6şH9sO+ύ7\5/wM;QzQܲ/~.i9TEQ6$iOQn]'@ `L-Mog46~uq!Vto-[]K]]Yz5֭#ԁ()URQ}e)sy=l[n<~ .dpAk;j@b1N=l%N;\N[v.{yxW939iGɴ =1-_ڷرc' =ߟw{.<ꊒ0cl6kYj%pӉpr:q:p(ʕ+y=HhF9TE%1nXnů<֮[GFVYG~s[zZ-=?MM;y9eXvR-RA<>tݺ:z;#t ٸi{oq 9Agg6Orǝɏy^˛z]rғ(>?;ʹeK_vi =SI2@&`'۶ifM̛t#H~kYTP`s/p7f އ'ys?1uOƓz}K7j|IcML27nbܘTI}W1Du^o`u+?F(i||`irI'8%$3OcUx1cx6L#|ondjHHLt:{D55*+ o-[]K]^/v-啦I{{9sSQ[[˲0rLRFo>Od6E*Hb^oEmM UUXucB3VSkk5۩jQJNݻeqDlw.uF#)CZu\2]5凙qrAJlw.u3Mӆ*:HKDq˖zR;#<{W]A"i\BB@e!.rمΘg?Dt:wP8L(uϽ,=$ϛ˝w/g4S0}4S|+_?oWK;7nb|qƌB )Qܲ/Τ^N$V~+Ӊ4MN(b1M(hLwt$%|>$3OcUx!Jlw.u37E3zֲl)92xtl݆(T}X Kuu5xͷ*SPcȣм =; Wyٚ\T[ԻeYrM{ ?+(:xerPL5wؐGyzvAl>!DLL~05E(b!ٱ2=->ѕyW`gD!AZ]+*ut w5eNaOMӆ*fn!%5/p10|QQ&w7W^tt:2yweaRB!NQ\N554GEQyɰaP2Ly"̜*B0p|'Bώ\N*>n*5}hˉ$J "!oOvώK|(n^i*XvBQULðZ!RFg♃離z0 !ݺ"B(J33 bt4wϣ}yw&g!3 s`tD p:9~$Ĵu+߇bpv5cZ3ced n/+CUNXrƍ_c,^x4[>nd}7g_n̛׬eSyn /8G{9;Ye-)|Z?|r׼Lz{u< ՗=JGiPUaoD!!M!D\x&["^|e-=X \xb1ƍþ{e'1\{9ϟiL2Ӧ䟞k_yM_R|ر|qSLÍ7f,\0;OTrfu^_hvaCל%6 Ѵ&Va8w=y>N_rUWUC+˖"\"2u"hii:ִԩSPX,ƴiSQMӒ0ゃ'di\_R>z| د'*-;uhdJ>yof]K&QHols٪IpMɓou]O?7g;ؗylz}LӴٞTW!5Tzmm-˖ȑ#3I<P@ /zfxC55TUU\B48 o(@ єG;x@x{:|{R^X(hVgNKAw*|Y됺/rq{_eop-76r$^-t0ײ75n~{L9GZ,o~#Nĩ9z5g^?w&x1r/8w.-CƝXAJi`XG$%M:ό1%G|ˆYN &+1+\?U8͝|]|9t+G3 &Wuss~OcO1vdY r˕3x=s>5pKMJke>axːW|9]# )NO}9HbX+ABWKD>g ab삉C/zΉ*/Ai 3 1c3S943?e%,8EQʒ&TVWDa?!lom8 kǗs\&g%ϢC.ΰj6V0M=;uo6>/cAǺDw.صg@QdTv,tdbIͫs(`Bx0 -fPˉCU3]->ͫs{E~B>TF-lpaLL[~LDu%c_ޓ&zNM)D#lP.gzR݆ʎ< ͫsp(sBdg 3ڂl 'T܂v>1{K Hha9:l-t2k^Z43%(|f@W`gD!(#i3dV&O^Gd ipǖ>+}ii1ˍFu `ҔiQ[FC_RZ< ߼ =; '!Dn k+V@-ru]G]VvTPS[OKk+U#Đ%]w3A39S{{OOdݟ^7aDuyC*444pUDYeyY!(_E0[@2J-7|Cϸտs~ʵ\um+_ϫ>`bfƖO==OOч3oanEQ0MRQY* M6܊RQYC !J.ۼ0Q m".f{rbns)MLC?ݎoǾ[?ͺ;   UUl޼|D}sg/81lU: W9Gv-[]K^6mƢQ<^/#F5`Ma&ٙ2zhnʟ~`0hk@Æ1zh|>o N[ԻeITU*ǻ7/>@%[DQ&%eK_vmx<^nOWO$2͟B@e[ҥ,-e=oI]Caﯛ[HB6 Ez=vK)=fN7nNNN zb1bZ,C$2bD@ @M_ rx[h"/FT`IK GynXyֿ381qڔ'|]׼n⁗+O1-.~٥[ނ@ŒnuhEzh-a T*VJ<::\hFn5|uW^+'z*6~kaˇmD .Ҽ]ܲ M/_~;i'f`]g==fҶ+݃WHP=Lc~)Nj}& "Hzޘu租~u0 T Tw"6:#ACmۂ u_\1yKD:52PH$=]m`F灳/y% 4fJбZ"D3ng>*e4VjpY{z̹s/sge>$VT]p8nZ!LZ"[Իevq;ôQ2?|h19|iJzJ~8y1fNULDr+bY N/>ЃK.jMگ|@%G~v!^هyuuu.q)#J6* Q$(nR]zۡ3Zs*y٦l!Ow/[5ӞwμR }"AIzQvvijT7Bֈ X8!Z2ގ1qMۗ<߽:F|_<;o5}x>LDQ5kwݓq#;su޿Ҽ]ܲ/WDnxv%+JևμRܰa%ߘ<'V ~YnXDގ#VTXC9Ga? X#8*z5iممɟ[|tI{sgM+i(vR]z-ϢAK%D$ O LUj^ |B\۰fԁ-hʰsSC-RMY( IDATgΛh&I22r(iױMXX*mX8aQ)!d-.~٥+!n7UUU' ' ='jkk#1fxغgg^&F';F\fЀX߯2Şώ(nR]zs6~oc(M˱ d^BҐ]hzw+0+BZgg^B|ّ-[]K]oFgyh!H%L[vK;Z"J5S&82R3!bQKB! dgBB>;B .|{NMtv=/AQ6y_B!G>;B .vEY;c@j&B }*Xx B!DPҖ!B ^ {{d 1y !F7 v65rcPyiG(%[#*K'|}R"vEq)}"Jagv%苺Z?hƎWSBŞ("'YgA"~H4QBQ!B nͻw[3>!D ăiBbOe]{Gg/*z_CQ[f>y !w^P_pqZ[. o)#׋nVK!O{]e<B1${mWk[O+u4M>CtB3@j !" 'Yl v>,8Gtfx_L0ʒB1%;kj g>/@ WiSKvB!Đf猕* SL#PUHV\.CUzO&M|҄w6?\t-PUŔz!BY}"/Ke~7s+1 n9xq#֮Wʪ*|m!B^Æyڵ,\5kzf~sG?̘^UUҿA!(;Ȧߏittt8~ʝwMgg'wqGڱ cB!DD|o Be1'9i65k|!u5B!D_Dnf^#0gxn 0M8 Κի3R>uhܷB!D ,wߥagzW^].bTE去{_t'|^7؄cx-yilx]<O+#BXDIuu5wט8e:5uu̞?_wִK?%\ښ]iiߣew355B!zX.ÞU;v{.;My=8*Bv{p6B1Yq `B!'bj q\(hFp(DG{``r!Bör=~oFK8-l}ڃmyϽ\tx}~*Uk'غP 3|B! xˈ8u:m-Ͽk玌EĢQ-|ڸeaTE#E>s!b豆x}q//~53K*Sf̢~pֿ-6)}=H=?P?Pb@B!菴!G|/mMW2s+lkMs85PYUwւ ;l#㫮Eaʭ !q3Q8Xp|`G8sgr))3fWF8dM*Ga^Zvz}\*U ߋ];iݵ4ѵ]8}(鰭B!Ğ /^ҋ/;;vpusCdҤ\{g7)1rÆOf⫨DUdJjp:AZa>;5bAB\ B! b$ %/a0glE!ޞw.S[p8(`)Aӑ:4x|>ǎ-Z 4@2U2BPLf=`]hikmIvTUfp9\N\.. ˅3e/աPU UQq\ԏ<]hCp#B+r)'sÍ7 ,]--JX.'!B MfO{th%x}Vacbf|_iZx0_p]N'!!BEOD oTbhGә  CtPMEIL$񖉞\(U!iB|$u;CӴMӈncbu"$$$^k&PLtӽL!Bֶ 4WQD7V>&CtB3@ ?ىJFDTUE7]AD5`DזCa I4hN!e/DdhGPjkv77kbTVZ#&¡^?D;B\TӁɡ@WDfU01LXrN2Bx{@Ζƭp! 1}&9Ј0x{[L6 *-6*U7֭ %u RZ>tEZ0a9 B'BEaʔip8Z].࿫VYGl&PUŔ&jHU->S5tMG5tMCo[x]<1 ѴaB!ĐOKeJ_n9ΕM7?_׼ U+ڃNˮ6 4Z3Q&&Jv[0ttammAt]y !"x/^nZ.Zce5y-M&ilMM]; -[VMjHHa-蚝rm3:aT!%nćx槣ߏitt1d QY[?PE(h-s[#F0׉t LtLHֈŢ;l"1~ؼK!%^xYQQA(BӴHT@' ѵZ,Ǝ-ԏ㉯]1QRQh`sFP͍[=s.gYN)B5k/ḧF0gxn 0M8t43ע|5Zvac-TTRYUc-a pͺoDywFƌxB k}a8N\zŨwݕ|݄cqc$hƆwd<3E$jO@Lׇ嶦O$Fvv M 0c>qB! "ݼU)ә9s&|=^F=LҼGfjk4P.bXAaeMj&67nę̘: B!-;w`]rzp9_u:P<.ZQt]ǭ*hm e6gΐ[B! B>>vgPla5մ ]8]N\N'jYtpg'mAv:njf҉R!(Pޣ3ʍ*V T5dw[;HMPEUUYӦR_W+@!6tAD(*+T̚B!" uXl@r:Y:9T+Br3~|rX](xE|y3pg U~o !w3CJMqL>pP}@Np2e YMt8R~rBыxܐa{0ȸ{ bQLME 8`hg+W!-@uڂx}>[[C? gw#%P]C[[@|B!DnJ|jLӤ3Z 7~TU+eK1 P(u23Loчo}_xguB!D~9Rٷ-[/̟ati&/\|ٲ|[rό}1HOB!M1D~aZk֥-te׬㭕x+IJs߬9ɀAɲ3kNB!D l8udV~L4 /8Z~;a\xyL4? lۚ3%#s#B%0sܳ~( >@7{jA 裈Fܳ-},7GK*[j+Ё[߇rBћgq]z5ƍ#p_k&98P^:g^}-?9[& "@!o!9:m0K rHK؟otՌHm + 8",_!vF ٳyy2ud\.'KO91.xeN?mi dK  .Z$ XAD">B!}zaFܳvgǎDQ9H9H"(;vॗܳșW_o) gK!}zm0s̥0b0.~~u~+.Æ̫赿C QZ'-1DUB!2+vԓ&;<‹lٲe^̩'-tz;օD"L҃!B#1_KOcjkӖgƈqԘ+MW;'V 4 7(*Be "TA<^pL\.'cFjNӴ Uozjc5Ac})W!ɸT}v[vH秺W^y UB{/C !"?["TU:PƏyw~5nYdĘɋ@\!B "2 Z}k$۶oWloTV0{+ZB!DnfoMB*9Ke׋r_eVB!OD(x}><^/90s,(\ !"3[Bѓ&B!D8Mib:Z,60'r:/A!4!猕3~<JbDxf"Ngim q&BCQnp2}p t8L>w֮Ab8]Gq㨬>jnj2PikB!Đ` 5-ؤٳPIw!doUUQTZ00 sƇ%Ҍ(!` ~k%555c۶}ĉ{3lX>5ì/uyp8F& !K1#2̉sI(:TAabZ+f:\^7N0vF#[0-eDkhk p[#Uo[ىQ~:6~b)B ykiƖ4F01-tp(N#((880P J\3˙Iffi+RA(X,YED uW.~ D Z dS("M$>Lf9|̒d&[.5ٞs9M1=iAe\}csIDATlCETsUX؎R`E>DdN{yX/ ?PLO D(GĖ6B!@ϱ Ƕ ߍ W3vS E8[c8Q%Xv<Θ;߭TtmwVOzFybA5%U4`Ňc˭!bzL:Cc)츃,q[ "8 A 'bRw"&ZA;Y+yǒs=i,`+Fj[D!ӣ,iA*GaGByrADhKX]1% DZԐJ59bevWG3`J#vep#VJ!BLgJ]7kEFlG88(pyu0R+9nrwpl=mtuwFr˲0#b'},<TB9óqN_yWCp"vZXBhzrB-В]/M {O*yQgg^ }A4Mwi;D(P^6M/0B!FsO]) iji^AYEMrG`oF3@wOO>RT7d>Ӈeve~7vv&_X.S! v4>khg&<00#SQQIMu5ߗ.Hs4e$1W!mi7eeD)i&z(B!ԍS4LiXwHMFB!oq"@.B!MtI!N&0ber6Ιr #o{?^\!B-8ieolMʴ:8MMĆ"\9L/B! 1N ˶1H$@hF b\,9ܸJ2P\!B'7|a fE H;gqBP\!BL Fψm6P#=ㅈ6%eBan/B!3ưJ)8 'Ϝ< Uλ8ʁh<)2Vɺмg~z̸B!qƒV8=QV^` WL1y\XA ƠjqBQXְ9`z3w*6&OwlI}3߹~&]Bw"{FvA{9B19Ǝmy?ڇ2O}94]%_lm?{N~Tܻgqλbjkk$B1)g۹w +tLwߊGGYrR/[!.`w^ۋ-gR#ٱWC[ɺǟ2q?CXTa!<&T3gc宔+/<'|*g'3Yh@cN4~EEp"g~'ʐ/nJ9-B6ngRvxWXy^Yw!oߺ}7o+jXIU)Ƌ/͜vGsy-'O=\ɓϽR!sa(..Ʊm0vb<[ zzp{<ĢC9,;ycv=+c|9 pvQ dy}~JJ+xoCqOFUu=k׮0\$֘qBٮKq!Œe5,nA#}ك?544?AHQ2 @wOO>RT7d.cşlW^zqqBٮy;u,9.W`0e{3L::x  zSQQIMu5L/W! :;hҾ#fM4^2n4MxL=^f+BI&5 k36iud !sQx`@qфO&\KwOB!fֶv{|,[hmӗdiM(Bq]tu{=^QS(cXĮr/r CC!B!vwPS['O:}ֶ6n%@}X]<,R[߈Lvє> 'hтr?bB1 F"4t<>f_~cVO=Vу19umnp L=rp6oz˶q2jB>qn'V"wc'|oH5[Hn©>hd*$¶mB0^ޞ)^ S:P(L G![L67^AJ)((ǙG_?gp8nÏpYg|?xtB!VѡQ D֥qisTP5_Wu?w!Bѡ(ǙkOԓOﯾrFB\xQ3B! ܉*p8L0q!}_wO3B!Ԥ(|/±B[ KyY}}s& B 3kSOs'^+r⌌)w BϸC5DW'?/2޻|s⌌) B鷳s'SYQ1j]gg'QY9z]Z$B qy< t*ʯ_,}?#BeJ!sϽ?@kk+߼rW,oɏ|+?9NĈu]gkR:o^*#P2S2B˯$uBzl_TڐjXNuJAzs|~m}gШ?_!sNZ묹g|咋wM܂!* v3l(.j1f(B!fPu5|㪫kW65tuj]r%-}<84xꚆ5Y7ih<!hyǟ@ey<\0XS\pBn[}qM D|ҋ`B!ػ)NjTdJ! B!Ɠȹ3Jӊ *BgS /5$Zn"ʬYC`7Pm2%KʮQY*cn,K.@&eNzxOJ"G-g>5S=֮a:?J? @mx tWh-DˬN~g oOξ2u9@GO}wd+9 ciY^[Zg>{f;!Ĝ7:Ctt TDd9j"d9 ~;圠=*hhj6 lgή* YL#s44>~2Zsk*"'}J&*u-$VSh42ljYRYiV2JhU*H'#ɷRZ\鲩d'Yp93eH']8jbn}*D) !D69'!g;_R"{@ !B_%wc!c= !bJƝ ?ݭ[D"Zu6?E~-"_BJ{=FK[ZhX@qqn+```m-,8P#Ĭ(rGZ^t]CuR`Vmm,Z |5QϤ1k9Cm Wu†5mD"A"a8vp${gkwS~2ZZtQhھ ˶t.r7…;0oK\,+N_O75)ّ0MD:oNizy#Fu;|^/9vFI9Mۚtei,2վHx_wضRm~8td 륺j6}}̹6$Je3SN/ϣfZf6w?>8=xbˠ:BLH,hvg;q,fK6QsCCCCN 4-{8 >ӜSD.-HpsI'oֱxcF~;iokr~%_BV8P(ͷʆ +_˖.ul˲ |ՙuD %p˻QJq>ϪUG s{n6oŋ~q?b:AIi)䉼F\0DKi^}c@Z8eϻ2uZ·ܥ;\ܷ.ϛ{4 D4D6)Mۛc,wJ$n8|pq hp{Vv-J)^[{Q>i܏-(^jjkȱDz7EKO&f;6ce$69nel1/cEU+Zz5>m[Yw"[thēNmm jk/w`&]m&n%(O"q&D6UxJKKHۈD"A{{:55%w"r`Ycɺ.2?tz>o1#gZN>$sˣɿէ49øFmaFnZ|ɉ8˯fϗS1)A{8f*6m|D_~g&dC3s]>Xw$2 ]< ,O˿\Ӵ{' -9qn}̫^iFVω~'Dx Z<@gw%%%MDoo/@,x F,6vi躎0L?K_ζ- g9lۦMÈŰqp, t<^/2,ZHMU2.掭TkGFmi +eY++peSJӇdb !fDu][le%roh`thřh4JZ[ )/+eXMWw7|-J%US= 4twmdu^:C)aY6}̱_%Bf9crWukMhk޾O'iR= Kww--l޴˶q>Ic}=e+)3 O$B+ ;HseLj{(Bzݝ;934M+ D"D1l LӋ_Ή*dCXR iXEOt !b1 "ϗ M4]ߧ9 6𑏟R:D R!i00 cN&qL?rCW\#0wrB1E~tD(so?(m}#}B1Jd(vh(K/LQI)^n?(MpkrȔDB!CC6l@qI)>{mדm)3:W'S:+cXR!s]QךvjؤHU)F=U="B?;9yx^|IENDB`muse-4.0.0/src/doc/html/split/documentation/vam_synth.png000066400000000000000000002241251404105311000235010ustar00rootroot00000000000000PNG  IHDR0)sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1 tEXtTitlevam-0O i IDATxwUa I(F]Lݷ5U@W S׷bEE%q{f:+[̜/bs{Ovu7B< "    $"BD'&=p8SBZr%D=(ɩ>FFnaɬI1E;CdI#*gSԦшCl$枢~WաM9YZ"I,# Cp t*F/q?vUv Ѹ8UF Mi9At؛W .gic2Qmql$HmU.`5zKr ;W-z}рe/uE+Z\T/gDՆrёofNYl+'n?^&t\9^zYΥb +s]4UPqjlOql7nOtN|}ԉ},# C(t@Q!"At22X.%,^QzFV2y].U"/RUjU+P1h {uSmEC3-[q D7S7PMjsDΪ(s41Pnb pŽ1Gv(Ԍ+E# PU_p޸sqhS 󎝨Fѣp8xkjjw_9 "%&Er&pN}ϸ35V%Ffk  ß)װ8cq[mpV]\oVʱ3W L@9ۉyfMsϱ;c#StǀK(^Fl,#D"=.,˔zh {Z^οѐZHh59LF`39KUg_~Y!0o' OX߽g/:u555ǟw"?? (**4#~uݻ7:@/Q +GQa!LG6qgC''kـ162V.{Q" sxGU;Z<*1L1.QuKAoTzlMV<}#dو6fi`ghvM+Hcr*7[hA`dx;l\t=HfnA@US.qcW(*.F(BNnvBSB]5P]U!ڑTVUaذp]շ#77~%%m(vF#P)*` V5qs M`71C2>ljc#۠\3WjpeN\_GV%N<߆z|Db׶ `(eh&匭5K hnSkS#paߞLVb{!=-'}!<χFȮ|64c-;f 8tٱf~ A0p`:t`P.~nKY`(PڹPa#*~-_E)׼rk岗rK ZEV`Q J,e6"&r"Vn/* x18֢(Ҁi:_UeDr638_~d\Z$/\+E_8g{zzDU~U 9쪹9"{YU*!p9Y%1꘱LŎwmٛ֠XS=Z#رsGLh u;Ӆ&~?wi5r!E}}OZή[ok5м*0Q;# c#۠qD>Z b_/h +r+Wj2rRTtFT֣? KQKS{I,Z 1}:N8~(fθ c"]qY{ܰQđf^ObˊL'N9;^4ς_.@>/ $g]5׎`_xYw+fetIL25Vg{ԄK {Q̙=Xí9E/AްQ_ׂ" /ć|U4$EVV; PYUݺ~kD.]PYUP({"7-9eorׯXOxg] k\+#^} VyAAwrU"FTǐr;QYF9_gڵ+5;? sİg{~0lx3.a|UQ2hg`em/c&L{!*h]e^ώFr>FkIz,c K3ܹuu2X__;~6믿kŋ,QV9sǃ<1lذS5{NM gyꪎѣ{X3f4=Fح}avYniΝm0~6Iw5/&/ x)bg\,%xfU`v-F5fu?E /"zQnQ ri_u-m#mtuc) ϛ8Ibq.u,^0.ѣqС?O>~K0t(&M?*v{1kܘ9yq̴D={YH}LYUv]uV;6"r Ӭ;^fv:^yK {e@DU옍Nh7Za/Lls4K^w܉~464w=)ӣz {WCG[ex<} F@W g‘Ç0?fh#oi?W-44-B5E4 [o]`LDE4#j1qbBNK@]η11ÎQ:+9QG> ~OSŽcrIY6PD3ś EynXvqPr.^dĕQo\w9!"!ƝsNwx7|k}|X ڼO]Cի'DQD^=q!,{_!}EV[$>,0`' 9fhAn#?2Y u8|e}X1~*/-?̼HK3jv}ƌYm@ v^6HNy(3TgRt}~LCF]LYI-Zu1ȶJme$IA~(}やei*0Sԫ{cİQ\mGPd2Dl>IOybƍ5W";bܸqE.~u1e?W^Z.Rڹ3f;tȱEz=ÑvaC׺Ѣ>Q$;N^Ʋa{l}Q#Ukr1/cYYGU#yVN Ĵc #c8*~꒶W uz~|_"JBkN~p_2f?_X̘1`KӚQEWؽ'+r%҂<6nzMعJy׋PӼU&\ޖX~=&>PK^=vC"g߻gjviWiȻ5u_~UI22+Wpۧ hʎZ&=;ƅ\U}4]273bkmDfGP !DFxi-<kEL@uT)86,;%Gr%eyc+Y<([F[6ɣFBG |Ez^uvvb"D\NBXO;wŖݻc_>o^C& ؋,-|Jt\={`S5{c/U>DżmUb,Ź,7*9bdqɓt8c; ~qIYoin-m''VU^3θ G/`&9:"]k蝲vu`g >8#Y29@҈[ m =ṽɯ*Z~c}C|JT'Ojw5Ywv˿ky|/~]±Gr*^k?he^y5uYbk Sfynd]F2ĵ"-8\uOZ 'sq1<شkdyϵ.Y|z\\n{L0auf_<-_8vkjڬa/E.f1"`Th tY1oXЃi$ cUre#b9sqӍ7Q#ѷoX]h3Pqw6VU3Rs@W εe(cSmg XgGU^9D+ H*EmH϶zM;2wcT `hc$:ehO'Zn5G-idQVSƌA}Qxw>:{Х؅+{"B؏[›E0G G8۝wv6nيfO^={{EHUiWǜ/;q_l*E*;}v + ڶհQ۱ψS.K;q@ufƱ_5hp8AKK3­Op:]p"Ԗf<9^47bZpVV60뷆5A]bB@>bPX!h32*o}.Wd=aTjJD=&QߎfL2*%1R/#1+ڣ$S3 p{O<ݜ*=Ph {Qzp {>Eu44uAQ1((,Ádg{PQY.yp8p kxrr0dqٳgQxiW;1.vxAҾa6N{|ĎGߞn>Ϊf("H.p``!ɯ:P(`0P/Ts/ 2Z"tR.*͋u-;H2zdsGUvl$W3GBl)? ozz|_7cF_Tra8_  deeczS\8p}p\СCѳgOu85MV|4U%.2df _7]ӎm#_^ez4lv +]$H KE]Tx*4fEqBu`#l^d٪ӏ*p0m(3IPxkm@VEQm6boq6$[-D-;y-bQȐ:BFK-mv$إ$S3حFVSТ("Mp]x ۝sHM>2ξkj )}7j1KƫtfulͲw Fg>?؜$\1Ǭ@,Rlqɚ\VS'#14QnFL[cX|s܎EdjLMK!|#2nigA@VV}MƎw@(ZP"SpcuKWŐ&ug6UQ,fAkIDG$r\iEDXѬ,H9̘qNz5"vٶ ʼ*0Dz#@ *a!0޲6ެRk%)L '-zּfi Bļ%YZoyѴH7ht޺ɐTMHEVz%Cs6h!9m5n͟H~=btVc_՘&8t"2c'PEu10dbF1sې3MX":^ zj jm1k퍮4P om2:.bTs+eb\@fg%FE_Ph`8*es ^ gs+ Ś1fH$QcIG/LjZSD*;?b%/v8>*[ }Vb̾Zj_QM~kتEq*!2 k1!/ۤ%n`hZ1q rS-ml[Z gBqCE:;ٙxv)'^.#e~疠~it { r\U"CKElo~XoP_<VCkƔG.U'2̸9WGȫ}D~ża{[Ыd"d/͛}egfAn^Di_HKҙ7 CI+F6:W^c TdؖY;fف &zhƽ-bGxl)3{Xo/u+T^JTsjDI|W^(߂%J>jq.2ļ2=Z8~uMSr/I^G ^~mփ +]_5ld]l6*55ıMs7&TX u@:,mSeGB͐HTkB_v4œW Sc"ޘb7-ʄ`^[sXm7R:n$B݄u*E:[o0CK^"t8 G}!}kOJKJJ3#j-FiPJ eA3f'TF5~ul3i]_oL4󭌉uVߜOd7!UHlzEzcm= !҃YV8#8j+ DقH{/=ٕ+/gʙ' Șk >\j|SeVWZ)95j9ڔm[L08j'C\)AjVp[:E#v8d+$ DlU&# !H'g1i 0; h U|x5Ypp82KICkNGsuK {Iҽf>)XeIەSEJ'dPy=X'^KU @ t 9!8"b8v'ſIi8͙U9! sIxr<̔'z_׎~jDrsL8|p݆m@@׮ݐi i?8&@2ͨqBTw= " aR֠>`̢[` %3̛3L3,D`R D"9u͆[Egf7cܬa3b칁솣8].;w ''W Z (Z45JC]?f4^qۯ?~Gqc`֠GިA0 r"Ux01E$nZSݺPP(1N HHT/ؚk6{WnKަk)TfF{Ꭰfy}x`mY(DC}uAUx֠[E(4Z-m_-,FPEg$-@uR k}/ QEuϾYmbD`k[fFn ;EFm@0D^~*QPTө.kd- Cv1MgĤ;??ѿN$>daL'#1H}mﯨ8[[p /脪*t2B!TUWc\Vk V:` Pu( FspMVC iBJ$IdB}!PN+b/`c.>W! dgMj&_¡0 A?ԄP8 D?S,oYMhI=dIA*A՞hsv$PIfahj41)m Lqc؈z&!2fRy 'ߨض&ՁpY=)*J$)WZ$J?יf7VSMԱMKvr*S&Oo&X [D."h~Ow6=b/$$y{[fm;<KJ!o-YysYb^!قp zI֫VLŌ\c'<[(L+ Lҝ 61OL)wFD$ԉ6zQ9a'Q[ 0)#b茻.yc456WLuv^=I){`IkVzEbuhnm@fކa}FNH{vD:މ|95 \:FMP%1ż$@ m7pHBuq1@ ȟ00bSbi]f/ނƚ&̺+/&eu f8 ½'Ţ{[a R$?N\Ԫd6D2$5͸w 8낂x≘>}:JKKev},X]v!77cǎ-܂˜Ͷmsa۶mÇc֬Y2df֮]ׯn,C',/-dΜ[z_)jZyy7Bձ4@=m 6 KZ>9utuAAFfb2O?Νpi݊¢ͷ~Γo w=<zGEgBk X9 $ҥj(..ƈ#p-k׮)kߎ'|2epfφ '[n;woٲ{/Z^/"rss1O?ٳgcܸqx7oOǝwމ;vpoc_ff>;A$X0QLx=3l,\M7܈?lz3=(+uᅬŋÛgy6fqms[VUpƙg['_"ĩw}_~9#Vϐr7ìKї 4&[ִ헅;Ld\KlfÆ ذa֯_ŋGxRچ}>Be3fN={p8pB<_~3f ֭Yŋk%\ve/&7x#.]> jP'h%(/v c%ڤdi̻}tg1k&/O<#eKb p֜нf5[b1l*.ض˯ x#3[|/~?,L38eee5kf"u8"\tEشiSlYgyf?^}ĉ믹~7 ɚMpJy:A;fm\%w[cӁObTAa4eljSl= כ1aDI'E /۝}aÆ<967>j&6nFJ,[n im`& 555Xt)phjj(xwO>$JKK[oǂ b=^]eeexce<fҍCȌ{ۄ[?z($CΝUۻt邺:h n̋jy^+:&->GE]rM) ԏ-2w~~>b( еgn<xo3(ރd%ӞQT,Rx;Z;Z~ם;wƩ?O"@SLAvv6૯ҥK79sf̿}0qDL81VD+Z-_N`j;(QTT޹sg9r={(,,đ#GЫW/JY,3Hb~dex]Zoth?ak*3䚶r,qII 3ƻS9Aqv؁s!e>-E7-œM)M_+9?Si`Æ ([袋ben;V׿K,3PRRn 't>8Dw3Z$S."(ժU0Sbۇ ׭5\Í1{믗m_v-Fi?d|\0l7ߠO88u%,qݤ[F_cC457NȻz zj=l0v-:#;`WxwbΜ93v |rOΊ[)ɢxCz9 tDcٲez{sŚիq_S֊CDzvnt{K/ƪU0UrAklR~=~ॗ^}DeeexW𿯾F|>XbcwRFtv[ΒJJoMz;m$-i2vt"+*`ѢExFٴiZ,Yn,',[c6M+VW^_{ +/4I,%xibdv):_ȌgzHaOY젌=˗/Gss3z-vmfºu гgOaC~]tAyy9n"uNpfa=N:$<ݻw?/Xv1c0{v)O<Ǿ2|p<ӧn9E.@?EȐJQZ pa3[^ic<998餓gE޽c[:p x!Y<Ñ0v,3'fowsca]w~-8D hH ?~L:2VU&qH#U V[T\5テ! fx8z*tIw,"3z0/.Y!Ctrad>Xş|9쬻hiObضg[*f6U- ж}7Qikqm=r Boh$MTad3y%Ȋ%TdnHSd2X#a9f*M#l& Pe d^I'=" {O%gu[[dL(g+I-vL@S#vO1z;At$̌D圐-2@Mr2QutACpAiBqH 4m2LR{L% AMAa bzz & ND!NDzC%OAqZ;%3 0d &`rT=E ?Zns I!sA$ָÖ/W H('!rA$Hja*}~3A01#+)kAirAGo@q'v('!rA؃  "qAAACqOpP@ M!.nNO64vHNF ]ulyp@sK >!(',okh( v(ߌ D '0E ҌAA 44Ipo!\.JvC9}bf#=Zt`9a*޵ ¤b ДOmM,EKs3PCBaѣ'v؁ζŦrB O[=q3P]v_R {z{N92icl" {O@ϗ!B r^TWWN3Mj7PNh荝`0@׈zݘN}?>sBD>})ѷO_9%fD܉p8f¡0=hS0 w;55_ZFBAC |I,fډ6777Vw(E(F qm4('tXc' ǟpkM W*7ӺcS[#%rI6m^{5ٶ:L0mכu]`( `ĉfqq≲e¹sСCOv~h8Zbz@9Ay('߅7vB|EXXVfڶmnvL4 &Mٳ?o0pc۸a, ~u}ҤIذ~lۚ5kf 7`⤉Kxl(e |&|&|+xe%z?lY +&-{Yo^ۧڞJ('ď9Z6v(wϘ3gƍx:N;4u]عs~JDY&L[>Wcձmزe &N\7mڄie0qD\{͵/PYQqgC]]]q砲UUU}l70| ::N:$,X@ٹMMM"ֿrԄ//gSvލ;?_|q̎uuu_*((@mEE-((o}F8̿Qd̘|纶J?V.Ĵ2twΞ#F*Ϗdee1B_~?N;s>~9y2o5 ?-rBf#&TTTW^V?%"Ipȑ#|rtIm}NkN0|p8zc>wn~w`Ȑ!8x *++q 2@tRY EQQjkkU?Mk38Y_Z"@ Buu'Gp7{=oߛbXBvRC"4K.Ayy9^ZӮ]K475i('hmB9!$e$'իW뮓m_v-F& H/'wfҤIXr%.ɓ'c4il{߾}q1  b(,,!Gy ]TG+VVlȑ#kD[ IDAT+WLZrӻtׯGcc#-]~t]`={D(3˖-CSSoom%=Kx2thrL];brnqԩSꫯχ7x+Wĵ^kTDxꩧO>/^O>}Qdo&̞=3fqG,;v, ;6f?sL㏘2e V\U^fΜ[bʔ)ؾ};fΜɽ]lm?F7xˣK1h x^a><9{ιxqwnFla;&""~O -[| (-_c]jǢ@9#-vC@uU\nWk?0nTTT}~;6l؀_W⋱a<8cLS+9  ?CUoߎ&_#GliL[&DŽI磺v\kVǽ[7-{\ r('xc' |~ᬳF߂@@ywVYYp݇(**D^2S lބ /"L&` 'igdşN ;UUp0DVV**+pP*H/Zct6TTΆ˝ G*W! 8cE;A˩&nŽ! //g=O YYY`OPTTCX}./FNnn틽֡F"ѳ9P`_>O(DgX*++oOD.]D+_Aplz3=Y4('Ͽp8PTTw_OeěEA~><} F t[lzdZ['S.돽סF Eaatv/r;cQhSNC9}bt8Nxssb=m MB~7n4w[ͰtO ҅EA% cGD [phAl^/rrrꈌU('?hjZݖwz+$2crBq B0 h   2T  hЗS z7MH[o_nA ('a+ِp'Yne\@ AM2~9URDG)Zh-\,v4\7mxc969rA@98zFq'6Eb/ -JYHr*=PB|>jjjPUU|>Bt7-mfӬD[r qI(FCC=ѿtχD B}m vޅNЩSy8RƂz̶ǮSN r=dnNAuĔp7[(FI(BEe%<9:u4=LEdg;ݵ+:w}#RR1?'`+ZL԰@$ Փ9r ,;}wlWGEE&]2h_aTUVrb![ d  Ӊ*)6  ?/~"#5xWC'}h46PYY3Ec'A;>9^/:D"PN B9 "NDHɳA@aQ!j\~;|nf9|;\{ OZ<#fw͹ C sXf ?rp-7c475a7[w>t7' hll'##Fn^.^]rsspwo;+ ǟp"<99%PN V&AGKkjj+Nm `޼y7n>t@)/O"? عc'/_/>,&`.ƶobxA oYYY' vCŴݻvÏ>~=wsƍøǡ_~l @9CL3kr#PN0F{ A#.>{RAO"uAAХK3Rf :n(.Ai-D1 ߋW@} @'ѯ8MM>Ʉ=^(ys PN03t@Bj)&#ڔݺuѣ|r\wu/x,\"7v&K(Bscn뗤#`w-1h`SFy~=~"˾0~?|gnc+v֙[@9s-D=/yhllon|\r)z9s?9hjlDSccFQFfB^6nDvv65n,('tt('ADg~3eSZZe˖׿>j(̦ ?={'1f[Z rpggaÆhniƱ{̂ra Aq\}DEd. ݻuC0ݻEp\PQQoۆΝKP:`BADrAD*Cfzݻ7<>য়F0B׋A[n())MwSJٴ2khPNІrAt"3Yzza3\xPX\z455! r"''fsS0y;sve?NZߙ@$ |('D`}>|8ʦacʚ3g{480r1xqK7~UUU())i3?ѡ@rADd׽ży0tڵːo$$}>TUTcoA ,~[1PYYF/M'L@90  8){ ޣ;Xd ?_ŷ~! ߏN:᷷Gsk_ޏō\q1|ͷ܌k֑l[w>ז hll'# F^^'b9rssmasge޽qܰxlKD@9A Aqӫ JeLƌ3c۟}Y -[_x;ܱ˖-_| Ä֋4d[w^x<S 2P`ݺ?؂p(t8ѣg/degZ QIţA96EџsysqrA sq+%u塱1i& _otLA q啿駟:p!ɘ/Ke*~.p  !8\Nxs<ʲ=PnIE5ݻG?ž={}ŸqgF~Le:t@w)UFQXX[|cnr삂=:ʼn'vӯI @@ˍf89t9kBˍlx<ٶGEn/RwOǺuLW~1X3ә 8-'&-wŨQ#cEEEXEx^],ѣG ihiiƠ!t|^8tBÀ |>(5x}Q # ֞pXDзt|e(',('AIXGnѾ&Z^YY{yr V|W^[￿[qo%%%(//Gnrйsgl F<+V #u$/5| h2 vڅ-[`P %pee|?:w˾p'fcDr9('+SsAt}6ԑ'<'xzO>=.2twq{G466^'|MzEQ#g:I8Ɔrᓍ1Qhjl YYF]V"PN` A1Z{>}6xeXdql}ҤI4iRl}„ ܺZuC~~>z?lĘ1conA8B0;ۃl؈f;xt 3PN +N<\.u@ {v?]J:t۶}Ν1)ujAd Pf#2׋޽{8?~s^DnPR9_.LS˦fD ('PN "BV7̋dOOF E^MMp9] /^o>۾\Բ(y%dE(=\/AXr|޸9UL}[ӁN͍|0CA$OȴgO{ʦNET1єq4c^W)'Ƀrl sqCاS myO{.M  :2ND"zӬt7 rAN?@tt37M2C9lD[GLɵ}i   2V[e&$̭7j7n(' \?M9=%: Awq_:WGbg !h;;*-RFKb6ef  -%%{y۷%؉}˷lSo=d=/c0nN4n7TWWS[[FӴT7MtsY W&RK2A$d=qO Milhl& R_[?|ĕMn("dBشؓ7ϣz|hƮ]U8\>P+0Z_XȺkر}yf.Jźhzz6J&$;I&D 2H ׯMـDv?vZ~M72nܸ}Y~=]i ]שb`!L0lH֭]MuUyVBJ2A2A!DZvՓU&Fl6k1v}@,^Œŝf׮*&NB J ?ÁJ^CH!kqOɃ %|r8bB}Orrr )k[,4M~4=f&JVv#li’AyyCf9ddBLBѦ{WjO#&d0 | *^n5&a446ү@A'դSRZJJNNkM@6bRo${T֙d,dBzL]LK-BBrt9ꨣwxۗy]9g 77d4/ <*RDB4, ۶mgLH&G2!-I&!RT +{ス=ԩs뭷1yDx ~_\mnz٬ˬf&2Vl6kZ*1 Biy_wS$s%]muO;X—uGK&tD2Le=Cy sD<3Lc⡇n1qY5c1[ؾce#鏫(l޴  oK **S1p0Ɔ>3:TY$HLM2!-I&tPL'4]v8QضN=ŭ~|-\}U 7:1*NWӷ`0z EM0H*lb]46Gꌶ=f-dB!^<3Bt0"[boߝ8i58#Xd)GqD3UU"+;UV0nAO( +V`Xе UoiJ2A2A!c%;qqFP7:ʢzV\nGU[UfgŊ47PRRBVVV*-DRH&H&!2RZq)f3EE㏼UNa~>ٹk'}I}9d盘o==<&t ٜ< =dd]0\*D<\zT7AF$w,J z_]~EZ%Do'Β97(qB!"Rz  ]vE嗇Bz ).$3Dwtzqu+=As֋m#FK&H&H&^']\']$$k}zB~zaesanuY2Af .G2CtwJR};ze>Ž7BM2Ahrkj%GW PSWGUu55uu4=5-mG|%ާ^SY浥}nɄ-vL'ǺnqHuijr3p`Ajjoq:d\rD!z!]Ѽnw<ǵQ=i쪪[c0Y؊(,.bعo7xllrAծj6C ᛤ( 9jW5LrAmO:^wVjr$$zɄ;sޅ>bI97Y֛\.~&M|Ypݬ]uY25Mө>t-i˫,tJ4aȠ j9=dB$ғd"q]q+ۖŌ9EQ(((`Ĉ|gy||>.b. 﫯6t(1|8]jkNcc#$ZyLJqY V+999ү5M6n܈Ichd_b*LlLM2!=I&N zD~΍}# ώ9\Ǝ=+u)S|Jy$~N;Wf&'t2>Cet&xhŤa6X*RDB4, ۶`D3dugLM2!=I&t\.]܅h.7C￴,̷ GrJ:L&Mą^fZ.,O膁x}V+ ;Y5-X3lZ0 BIY9wS7>c;]BdBh I2A*qO11cK/1z(N`r91b-b̘1ڙ\fՄbf픕Ϥؼy(  ߰u~TDCC}dȗ9Ʉt$YsY⏹=[13+2y2\Mӣ/75W_rRM5NX}:(  6lᓦпY4MQ_WG6q*>ꌶ=!|fɄ6$zɄ _&,DH0Oqc)//g?(/+LQˢ*YYYdgfr : /( +W`QU];h!ҐddBK`'|#f3 k+rEaը YPS[+L*5@A1Xv/_N]]%%%defB$ddBtqc6),,$ x1F$?/Ō]X~=}e t=dBxB8vJ˰ll߱M6_f3NPTXH^<v{,ٳgT&O2A$d=Qo7WCx=hA լbq\8nu_uAc;âsfEߞm Un9dB/$v9 "[oi>]UFFyjRtp:a(({nc._ϑY3kvs(Ft$zɄϼ")&Qe]$RYX

/Aeѻ$pPV{wa(j#Ddk}gyg;,={gEwÕrtLp9Bλ.A ͽَ7BM2Ae a.Hx:iZ2Z "Q$viU'4660hsAjkq:\.dBhq͞2>4MlO8 0YSTT̚_sNPU5]u!TѶ'^&ɄI2!L]۾]C:ǟpb9s[ٵkR,mSO;m۶{ͷ?uyu~9j"U 5NUu5^@2A2A!Dlo.o,]Ktwqw0y$>YI?#~QWɓ'`6Ytin^{5,Kb^LMMTW0lH>@y߇eQTTnjJYH&!۽g(F#y+dgg35oO/#?dƌn֭[/  EEŬXǷ@ԯ)4]{5=<&UOn>K^ )}[ӄ%#RjhT|BzY;F2C =dBo&~ ls뒺 uuu꫌1E|?OL /\W_}СCak]  7ad0 .\_7HFcSDt2Vrrr_klڴ 0l䨈IgKb*L/dB!T{B$[B?vm\<~uٯZs0iD.w]_}=Sλx,|f!\|qB^C CGafif3&/k-S" bPRZ墩1M',$BLHO Bi= p0f^~eFt2|p^~y1Gn3QGf3z踵+b1[ؾ}+ee( lڼeh8`0]$o֤9i"wddB:LHr2A% LS&O뮻G}7p#W]}Uv۸׿Nt32͸\NX} C_( V6nS_Yٜ6^Mӂ}>kkLLH7 BTsqj,;-zÕxX5rd=r>ca?vDRoL\ZqDSSc+dQM ]۷uT]3hrzcCL#dB&arp^twFW$Mc1<"wG,UU;O CQ;0j5*|si"7LKeTSTΊWPW@qq )k<-Rm#}}L' Xʢm/A,ѹ`MLaa` 6ˋ9jyU3^];Xn=}Rٿ9.D% "ZFu_R6w`|7h ٌ`'.OׅYfvZgȘ͉#;I&D )Jr>mD77݆,z=ffr[n"aϐ쿣 'aɬY3mp'%‘L$B/G$+tQ*.N00 EQPEnc.l̙̚93QL$ DϤK$ rw L$pr9.h~HϊK&!wy$="uq;#F \r%nB\k]LdBtˑݝAr};ri" Mіd%雇TWF1lԘT7.h><͜9Y3gD<%Β{ܓA΄ ,’^g>~Zi[xLl9B~λ]M"xz<iZ2Z "Q$x؈eСde`6 T_q\r3!z!]L"4M̬w0ɄdddP\RJQQ1kVfWUy}j[-H!2w#tx8Sxeׇ=lj'Dt]UXN [>@0Q#ɰUU)i<-RmɄ} !]&DE^ź{ 1Ar}k2N:D>e[l {wvS][È#z<}>~>χaȑTvB$ddB$w}q/~W_}͐!pobΝ|ʩLzWiٳYf-<[m?z{k׮K.ӆ`0Ȃ{_~q {S__πA\lyR\NmM JKK0`0֌T7GL7dB:LBڞ>]qjo't"'̲eڲ[8SyW;{v#;LƌxAЃ<+|;ou^hll䦛oC㏏FGuICkrM ך&7mƄa#'C]O)L7z&rZH&G2!-I&L'9d #655z.|ɓ&h"t"Zƽ nyĉ,Ϲwl6==vn߾?p#|Gyd^Qh` 6ҚɄd¢8622HN:4 Ž;2,խ9$$+!DʴoU&wehsNΘ6|RDXn?$6m+kllbc5z)((2tO`0p@~~ϹƛΎFN5qb5}%|oE!#M9ITVVb6}ҁ yF#e*/3n. НI&t\ޕL'sޕP50k[L4I&l2Fu-ӟijj'#K._^+^x9 I&b +sE/QeD\f lܸW^^#+Ǭv%77 [!z!]e6;%mVv͛oЂATA),'Ov{, Œ;3TYg~Ʉ}I&N .G2CtwJrwv%EdgTE4TUnr:-7WgHSOdƌiyI27L!zDiM0UUq\8 0 EAQ貙ӧ3s! I2Ae8HE'b2s&P޷NuL%dB' z^2y'ZBpES݄q!@t`H'wJbwm++㳊 q!c6  -GUU6Hٜm_CB!R*7/M?~~$7//)a(L#kB<`le^4?QHϋK&!IN }QWddX1Ldbxض q]ѽH7 ѱ۝Τ+Dw2LVhv / /vX6c4fLLBėdB|#Doew!Wm=d=WQ_kIi~ L&2,222Z(zd Ͽ"ӧu|-TYgBh_H&DL2L!zM}qJ]ilj0xQ-@Zk;.S(D/ "\HCQU]MfvJi@(STXĺkUUE^>rMtFL=dыL-]ewΘsff&ÇcƴiTTTttfUN ?ta#FTUW'g?Tx(TѶ'7[LLM$!zU()bȭLeElذ!UDMMmC:}^AMM-Mnw.DRH&H&!^FQBOxa" jjjXp}A7Wt6Z{eݺur0h ֮]ǣ?֭[),(saРA\XpםX,}> n\č: 8ߏ#'/w^&}[ӈ%#Ғb**8 LH? BQxpgr.GrW>OPѯWJ2>?'|ʊ~\yůb|o[x895|zƍ壿}Tk~:LWbo:M $zy4U ך&7o( oHD$W+//3Y2!4Ʉ$;%D|Kymtn}~ڵ,lV'pֲvlV|0|pkwѺ̉&p}'p&>HEdRn_+<^uDi`RLXT&Gr҄iX,vŠmX-rɄ$ғdBe!zpM95Hݻ|dgg>v9[-KkYcc#N:֭_ϓO=͛|uPQQ'?9`|kG&/?/١:^cVx, +650 BqI)lϓvuSYRGBu&hɄ$zɄ} B{hd||NN ݾ?F ʒK﯋5FOUUf۷QZVN  m75guQ֯?U;SMk/VE/qP^VFYi)`7|,nFIq1_?nV O~}fRVޗ?rzEQXlںCHeefsR,Z0롦j'(d¾$җd"ZtpYO;VÇ?^OqQQ|gs'?? Z6g,Zp7b.ֲ3Ϝ]x_q%.| O:?p#ICd2rr9Yr%apX|igIiH 9H&H& ]W腰deeqWuXuò>z(Oss1|xLW&EE||ɧ蚎QU3&DU+p7y4h̤STLLB>(:54M믿W23*yA6mȫˆ#l6xź_''~(+i^(IwVɄH&b i BLbuu3fΜٌ92%mp8씕`رsX IDAT7mo! b6qOaA>}rsOtW^Oi:K&K2wLrZGhu}i9mmeESSMPU͆njڽ"6;6uZeӦzqɞxɄ$zɄ!R#z U`u*ӧN ?H "$ٚᄓ rw L$&G܅K/&!҈d"Zv;=y瞛&ą,D|H&$f9B^mqΫ, B+P[W?bjv'}+˿^?';++>MyhU&ց܅cIe^4 u0A )koƍ#'77 B5552xw-7`nl}Zw_DוH&ўdB = 2ekHwS&1bDrwd !pZ_\/.zòiS`3..K&L2!娊€Ȍi 3+՗뒲QeHPؽ7/|zqK ۘ !C2nǕfWf&v#)u@¢W4 K}}=542n;;LBQe .z]ilj6ujkoqmnu!#teʤ$b^2A="c'wpB"Q]SCVV6 Ҽ:&" Yn-UEUT7[ By]u\ өYs[V+ ̬_Y} 7rohک3fgT àN;tp֮jDpIu!gmL݅dBWz&.!ҍ8Cnsz7gߟ>sϙץeoٺ-ie-n7յuL7v#Fn!B2A!Dbv~@Duqߴy3:V+N#GLhoڌYf#O>g{j8>|ٵkSgSOai[ǟxm۶QPbK8̹<3rQ l#[n#HH}X'MB'L5UTPk6=lV~9 B2kKw!Hd#38.<|a|/uʊ~u8o<Ƶ\EJV})O=[no3Sd ϵO2glF+#q=w[baFYp_^ ewm زe+6 ˕:Dd4M RXP/@`#k&\Yl#x$;x,/R]iˑLLH %#Z&K=BeovlM=UbJV})O<$np衇0c423oru: <4i|vƍλ"PULv\̔b~]-`(> fsV7OX6m @yyz-jH2A2!]H&!իvޫSP8|۷o/;~~y{/7WWߒ':Æu8/ϰy|>߾ms+ 0fXÌ@Vv#FbdX, &&"|taS@ ?fV|>lv$L忾E?Ǵ; J}[~FM1Eχ?rtq:/. .$"L\ y }e^ǃݳW;ݵΜ3qcǢ(0w9tl 8[0d#JmUTBM.`U5O2{V* ]^܅#qwm߆HRW?FF'rhhh%K6th4ߟ3z(^[k{N88~rA;٧OmN~~fRJKKlYYYl۶DiFS9p?6? G֬^MEe%`WkFk֬f[C\H&H& ɄIW%!Q$t59!@ۮ2 rхY̳\qU465p88hX.ifNC{ygfa0}<ēspZ+q=<9g6F?EQUW4ڪ]Ԧ-w}h:~ =OSSs(`ժUdf?rH&H&ɄP_0"j gȣ tMG![~^ҐM׿%?ptF`a/>L|mws\. O9y}躆 ͨ KU|̀q\}F5Mm]Z$Dw!c]jcߑ'OnEӂl;քm7 T@w#I=܅H-UU'i&^{uF^|,f]|M~}GUոTެBɄاg&P[S͗koesŽ/D*F}m-ښrss}|s<ZQ D)-.fk.6o·|:|`#^vuawy}tr(Ғ ;L\2nkUΝDF!RI dX3"!=weǽyDXc]ԲmgISS^MPU͊n=v4#'L9ÒgO`/ŝNIt{/tY$[iH&t޾ekF֌w~H;ќ};)C^KUU\N'0Z/~T%ah~z8 !G2Ad]z 'B=!Djs&!D.h~ H# BT!DΙ7/MB!D" .]xB .CM2A!: ѴT.Rjԓz.MmiOWh7Fje#-z&t٥gޙg%':[%h%@TdX[YedC1P %6m߇lNH=YoBwe+Du>r'`0bR<& I@5ֱ.':vܻHQf+B}YIoˋyv>Zm\e!I&_NcS#F-dt&Moe%]ܜNU__O۳Of#7㻾FZ 0 (oϭ{'7TfKz"fZYU#zz0FVgvN6B;ei٩KS\]۶mcAD ix=~شqnwee1~dKaּU Z-4@xbi7xPZWBd8؟w=^B˾z4MR_@mm x^4MKuD7ّpe"$DtL ׇFC7۵3HU` @0D  x^)+Msh ذq`2JR\Z()*$`~oaVOqI9ťeQ^ޗ X‹oͥ,}s` ߧ7qHup{< z;>P Sb@-zt]xPM 5 nj x׽x<~?}#y0 7FS=xFZނ?POg>m ?`}f֭^#b'8q{˨2Fst::d?:s.a=c\50 à !C~kjG7B 3ī I&^ ;!9#''ge/ [MC.ai^ 4נz녛frr0*>+c&s/ڰ0 WD躆i^D#07un u\>C1!hnh{^g,8whޑ7Z5| @0z:K{C{M|׼;1}:v:KcߧTW)Qeimlldp_PSWyCO( >vӑF B !H&^* 6p7`Z/'ObIUWpǝwestrWcN=DOʙs.MͿc(-]eٵk'k֮rNŤx?]͋5rq~ ccpG75yɰɰ-4 wno S=m~Mqn/,`_2.=nzz8]T&ח,a[ۗ+.ů /.y?t?4g`XG}w #=տʊ >3^ 7뎎465QI@ÏaRMa 'G@ [QΕn ?o:K&&sH&tEgQZˌ;\tx9+W=a?P̞Iff&WwaN+nP={I3Q\ZҿA^nЇ,Ř(6sXznߴWCKn|,C0VnY%jS=ޛif3CaC1h,_#aL8N]xqͧE}[r 6;jnƃ[vv6dge1CءQ`0E NP0T̪n###,"94Mb*dB iI2!0}'Lτ ضm;rmO欳mb[fk׷;4MڍĄ[@}(t Ll&WVFTf4V* U9bD-3qb'U-k :2"m= u<W7W=DgԺ^h>̊o֓5߀H7x) Ƿ+w{<\x%wv 2t_,|Ylيk7o~}^{r֙s6thTuGJo2Ѹf!bafv "> bPXTBVN.9Hgf̞QPe&dBzLH$##͊ 8cx[ុonYjG;o&rHҼn2@evk*`(A=SnQ}Yeoc /./c0jHq5w1>-ܵg~1#0hwK|z-GL:pmw護rÐ ._NsЬ1z=cG !]>stnf*+*(--i-[nf3O۟CM{=k&c<sο,?s6=+WqsO)puGˤؾ};o( ۶lE0tQ~e})-ǎm["\tPuF۞Φ=ɄI& {+ ]C7t Oaȑyd2].x% 6u+.SNb|rp}}a˖7k-mݻ]akAZvwuo~ t UT6Ӄȧ8 L㑷\ }ƍCuTܜ|>/~@"Gt~?>]ywYz5L?)zv/@>}Z+BJ|͜gaw==Dބѥ2&(ǃ?﮽ݘѣY[x Z+WkgPӕRRRB0wd?=ƍ~PU3NS^^ HQfXٺ}'?9p**c6Kt ضO_D}R&ꌶ=Ok&Sϥd¾$zɄ= {- p\~ŕ466p88蠱\|͜9xsλ<nk5\ɟo ã<ڮ] zt=G PPP:|}tZ2qK̟yt:I&@ _]ociFFF]F#G7t~(zz=x^|>_\{h^zyΘ6 8磌44]g;sZcoHכPbqWIi>n<6%9y2=$ӧC{.9yyy;l9tg[nasW_[oqi^tIOrs?2:Tx`N#A} IDAT]bԪ]? :q񱄇G*QNsYY5Uuk,q'uB%u:AQ=r֭[s!؊J~猡NHI(tv><ԅzak$MlL,F[|4u 4$..ߎBP`ibلdr6˧֋^7hwNAKi4Rt F߅vAlr SdIUULo.uŃ Bd[M&rWtoBM܏g:A"O!"<hYlEQI Dֹ{ݤ'GƸ+Į QԩvP.H D uYuJcg5!^WU BR'u\7I5G6:1̓HR'QI D$T&YN_N(4՞jH D!uYu*#ߤ(v#HH D'uYuJBe%ϧ&B=MܾM!>:wW0B! zB!yBB!BpB!p4!B! !7jݎj͍ !2xxs<2"{ 9olP  MQQQ#@bK%&`0Jߟ" rtvkDH̭r !0™Sc1]$!@v񊎊F: Fb4څB,11(W";%K" Q,$7ճnۉd"M$M!(lKb^w!LI#Bh2*yJqB!pĸ4EB!HnjXNSB!wc]!B"+=rsECX ғrSOz:N>@5xoBBsB!D1+  ! W+2L;?L2}g`?0qʇkTX1*B'I KEU/G!Ck:ÆM_̄VF!xq|pB\q_hw!z2=::%Kϯ111.+B>Bs.(2qbq2eS~-|}}vфB"1SN-4!l׭S8@S~!խ#BMZnN̩BgWi=uwǛ[E`֛RO!9$ !Rsg1 /jXp/Q6(H !"r']z҄(VUZUkfBqܥ'M!BBQe]!B9Az܅B!rrB!n9-B!DA<.!B!pC.S" !BqWIB!dT!B! 䶺*#B!D!;1c/B!]K3JB!w !BQ 128iz.B3w1BdCrpK !Dq3~̨.f]E(TUEUUVk~@@ӡHBT(^=(?$6_y݃a&6l2=ѳ{<,Qa0=)_<.NdML %&`0HIR)hBSPї!DOglظ)MJ3-SA:!իClLT~@'xrĸ繠ޜVM][HXU]S+^:_J ͆^E+kBoMލU˗M۴ gnn_P-22Z fX65 s*U2$HV/AjUqˮ? F~"J}ƽ^TL!zQGQGݷaӗ#u b]}v""1u#S੉(EdT$%JHB^ HT\8obsT)Xo:6zcG-7n'@oǨS]\ݴJUU@45S(h'ܨ*DAT}R{n%X5©xOF❚ /DCSuPb`nKų|)%9: R>Y_u|Vd)ǭRKQHqE.MqZC}=.&z9ʏ7Q nӥ v+.QB Aܰ*p):]NKF13-&&fIvoس&~NdYa#;vb颅ti[SSd2+nEb2yRV- Dժ!a L qqqʐS?\2jS;>[yG:֓je܄ҹ_}rM.XMժ!؆qc=V+C=l2@ci7n`8qի/9m~7w!{>xAA/] ߢj^|,aС}[vC玡i;9o?C;ow}Muz={^:in&ߵ{!:O>ͅݺe˶ԬQGͷ߲u w]|u,[N{spЏ,^~+G}8{v PUsf|J*VƳif֬[ϰ!Xv-5mC]yVVkF*mS<X w& / >%)j5i Q\XV.Cb]t+ծUezJ~~~Ԩ^D ;gV*?N+U e>ԭ[xR>+Us4WQR%Jٳӭϗ}8}bV>[u;&gA'!!r_Yd)xMj  ֭[OGPU*UPBtGII366s3oL~_Xshu? ~FJyYn?_Q#M>?ƍˤISy7[L43>漈I֬[ϥ0ʕ-|ҥ0"##iڤ1xko͙g\nP:?Lƍ:$sգ;[mϴ+Olq{=oAf,QOBܼupiB0n ^^^7q 9GJ8q$+XÕWg`ԫ[6YӦzZ6okָyE.5]3}Fvb4z'Nɓ:w⍷uԩ<1|&'ͩN՚tڱY3mOEQxM0be(&v;\bJnnUbn-9zߒ}ӵsG|ځZa2>s[6gEF{rYԫ˹sgitOC2S?O0w1<ځWxpɓ6YΞNq3 fhJ-q,=$mgأ<=Ic>h3_i4]y4y {Y~4)Squf|)|c+Ed`0жM1UC}qoNG玡|T wmd朹4nNil+F}2,CdpJ%}!ڵk4EQ8ϿL2{nF0UPByƏӗ П7^}#Gt~VZM=S6f+:y"-7p krŝcIrC=#%|}1v)~_ەSCaZm=FhS:(.4MÃ2A(ǭ]4ޛ8^=!wIo7^u/Y&Zx{e ߗ'OĈamxm-5c7$Jϕ*ʒgNEuʟ&K(G'b2ҬEl\>DFZQt5D.i1رZ3yQSzxL݊BAU5WH v)'Ov7qre6[6[mvxyG=[Fu޺:ujg͙ˀ~}i|o#z`]ƍ7z!<%_h|}}35qTf!!ҥ#_mޒj݌NdLx*f)uˍkۚ/:ljhEJWߧNSFuժVu8xqq4;4kz̐rFՆAbb>d_RݸvsXn=aaS}1Hiy|;/Yƫ/C*r( 6L*7_nᇺr<#$3Cg7v]ݷizu\ 7l.{aEi⋫>4-樂;!DaZ!]đYv7+i-[` ş1n;nLMJӫcm :4}{~:F;?[칮u bcVQXwٓXUub/sA8Bpo֯O٠ԮY+UZ:u(Tzuz_Ǚ4f3# ^}MϟG dl~jsWyٓ9s3TZOLwl=O}g#Xr {bb &Me̘'R)ӛy3r*ҥX|y+⮩ RLBB"-jQF'Ob2yj)'N!p;2bF?1"٨aC=Fv8r4lLSoTz;~l߹t?qOs*Uq#Ae f{^, W^L` wWTM#};re1UJ%A)u1Kv }Zp-,.ϵAGgӼ)D$*PNBW+KB\vTT ^ibS%im?KS?FP V+(8?؎cF1ONqcFӮL/i1{6x0Nfێ,3˗/hRp2e>d0Ud|ʳdl.ѸI|7*Lf/tfʕ2lqay ឣSu:=ݞVOE#6JE7;3CO+xT4i !ܒuEnon.T6Nˋ;z[u#8iE|~,^^u 3P!P.L4խY-rhQCR繕bS :F= V^}{44,mkc,S~IQ|->D\]{ߠ!~eiɣ\_g4sinwo!cw-08. h<* c* : .(6}9׻?hp0yyQlY._fߞ]DE9Pe0I$=n!;qN={ݧa :@Q=b&Bl뺯ۣG\#=i)⃧GJ6ő䉏7F)i"r% "9:ҹ}.\BD3?CVܤSL^^x Lf҃*:]}? גk+B#ǤvPܛHr=ii4ص#Gƫbqygs\Wy|,c}Ms'Ϭ*e IDAT7}{Y0gv5t(ԯ[7Mکӧ`9.{zc&rpW H>}B+M);TCܵ'݆C?ҵs'u#+w|)}o?C;owpmlݾٟz={^:iHڽ7aH0733((I2Z>M-B*N)w5išujKˆii3ϿHlٶ*Бx䡮-M/v'Ndk3__խi9q݋|3gR51[f_>^ з͛ݗva.B3QeڶiÑcǜ7$kџN7EQl6m۴hr^6 `0ТY3]6 o-s򯿉qnӵKgL&iHxxD=e (́C8r+W6mc?sMwz#GY ;8ҬST$]!(NTUCW>K%J>u5ߧV*Kfɿbͺ]&!1ѭm~8xFLo)O>|N1A\lIi~~n;}{sP~fMcW2p<3)l;Odɒ ח5;ӋRqR )#!ņcwP ھ=7nO_ӧw/u(\*mݛF4Eq3-۾o3wR'ǜw HiBtL 3d277ndZfw TfM,>>Y'kLs^: aC3tơÇh18c( lǸQ0B!rjP!$$$2bVU*Wv{RJ:n`0Pd ~9~umNefg޽X,\Jt}IL&Tv2e2s_ԩ]eͳn޳va]ԩ]ۙVJe=|{}n\3+:w eŌ~bD{')46"tig|~{D &c !ňY)oE3,m܄ |dBKj}SMef(o˂972)N-[4g߷3mgݙpO?+1Lv1EB!D1㘀IE9s_϶;EHnTB!i@+VZ[7\2# "  k.BQ4_'b7h&!B!DHjKB!RRpĸ !Xs@o//W΀~}(eH(Kno˯LԪY}/Ŭw|}}]&}_R^$Wج؝B! :H(߰pgҋXSX3 Le}| ~j\u?]Y3^~Q 0%lP  MQQQ#@bK%&`01;w9_y /ӄ3alj'O5Wد/ȱc߰2h@jլ>._fܺyÇ<;{l6yЀ}м}Q٬V+<"B;e6̙<۶GnJG\(Z,<ܵ+Ͼ _W]6S98׉EB!DAa4R9$3nǠwrEll,[ZkVb7leæ/4m]bjIڵ9t0>_XJ{f*Vȱc|v-z\~#MCٹ{OWυpYpwGFe3 $$$*f?Ӆ:x`Ŏ]QZH+GDGEQ|yn_'11 dEYdE"$&&r#:*T$:*lQ 9aOfGs-FL tӓNC9oz6 `0ТY3]L豟y&hlΜ}1LyU]Qv;]td2螆GGFeSF6m0=[8'Rp-F-Jq:Ndd&QnrBQY)Uʟ(, BBT Efy;gjŚu LBbꨄg&<Ŗx,YRFL7@FpvX7 h֠<2;/]'L4a<)O]Id;@M4ASsHB!( T S5 {1壏R2ee{`2\nj ҷwo'Ǎw`ؐ qa/ZNt^f311X,"##]-g]gydv^7.שS8@=R^GyE9]AEYdEYR ̢ϖx{t}.njAe f}N, W^ཉQTU%tiTUukUOHl(,Y_'>>m;v׹!*wKz=}l%*:ʙg4yZ.B exMk\aCգ;,c³ߏҿz}i1{6x0`,?+X͂şϰg=;!;wfСxx8\kޝWЯL MU.ͫ/xIHL$04m۴!};UxMQ]6%b҆6!׮\KB!DAP{wo'JpR*ҧNlT!B!Dp|MVr<1pzt__j3=ߛ87^}%o )BS9U&ފ֍*BGkWHbҵvyaaar !y$WBe{BYd%g ?>``u_}-^2;Mޟp &ko ܞ Cv\)B~604_>{ad[^~јFޟH^W !DS[CѨaC"##ٶs6nbyV+ʂE1Lu!_z3q?[uj9a~9ٲm;5k`|lݾ^=gjőĸ",ր%Jd"V\+|T(~"KΗbtQ6n`Ϗj߸qSL2pʓӑ?^yGLl,/V5U^kܼuqO?-[rWo2v38xQcǡ%u(g=Ϗ?8F `Ql%G#( *VtXTL~ Wҩ#Ff5kδ_~c6h[iY99 EEY u2=~~r*vEQaCnFb}In!,p"6:k7ljH03MVݰNGBb".^dEs֮߈6C}4!%|}IHH@UUf͘&kӬiMęgZmNQp?NXasޥг{74ұפ1G0V;'BZ_TRq5\XIjLBl{4y c@>s9F֭ݳg>P!/2* ___8qN0Lthߎ>L4a2hAΞ; @󍳧lch>PΘ&mԈ }9U ұVVgθ\ eVYKwfZp4=<#Bw݈2=ߕ+W; g+!(hJ(7(]tD7ѩ-G7qqSBuj7_bN?||G➢Ȩ(, uw/QQ\gbnd5X6Ft+>%i\9C!0Z5.3\b!:&_gC=Ytt44R @6WݻӌQHd".6o"nѥαf͛Lt&AzJ FAeA@\8ʥwYdEYd,C]:e6#V(nԭ]{S3MQxjժ:*WD t*WL*S*ų{^T\zBTTdVd$\n=lAӤ5jؐ#GϑǸa\8q܅B!QB>96x4_o{ѫGw-\=OHH0F O܋/c OJҹӦW̝v3?|ϭ힛0g|Jl\3O#$8g&?A46o|m)HΝ3>vf<9 :\8Rz/9 iD̥,mh8{B;qT?ȱc<5ItZhFƍșH,;/0 !B%rV|1|fΙK-]&۶jppдIcڲ /RB7|\v&ޛDȥ(M!"F} _{vYڶXj57oݤrʌ6$[et1EKK/b43\711ϖ.gq ,2B!Dq$2BPR&0B!IjKB!r<;",",d),\1Y78n4]a#Ǝs^V;d'?IyƱˎ䶺 !HJ݆Wnl jȢ q%i !ظq&-TT#{'Бx䡮ލ#ǎ~&#"(ȠUp{W^qr5y6g}z:4`@>4ova/3{|nݼňCl6&<<g՚}bZYq1__~N?0IKB!Ck7ljH03MVݰ9 E ٽ?_ɓF2Ly+_]fqn]zx.\ȇ>`ߴənF{/S'OM]B|<^gb58 ~VZz0w'<ԥ>_Vّy6 $$$*fȕ{ \qSEhܢU~!ƏB<$uUqI} L&ڷwp]____G튢(vvd= 3Sg0l L&mhŦ6NUҹsp׮wvݢY3/YV,(( 6m`4zd9;eF*# c M&3JŕUCtL >>>x{Lx-[)Y$fwlr #(s:mX;/֬[OXe?=1yd;d&pEB^ ߸ W#O!({V<==-cbXbɸ!W: aC3tơÇh189/LlL lgݛF4Eqsev3krϺ^yȭW NB+?O7qI"cŹzdԯONTZՙVvmv]c.ԮL+UM?? MDPۈҥQU5?*|vhw?VlPY޻•W)9J,/ǏSN؉^ޭs \qW$=Gv;V+VUUtxxx`@w z߰rvr{[Ԥ}zbUlٶJ+δ^=3o"~yBB5vs&Ocit1V|?#ߟaefLYi7oeFX2\FZoܼu ?BN^*/wˑz 0r0B۷Oɼ>Ϯ~{[׊B]%{ɗbg2lϚ~p?zvӠ^4ʶ;0d{. bcbqZ!!! XY6?ߏӄ]/_7r鬷kv̞ڮ]T&mQǺJ=`?哏q\3OrJ=ws9*W_RtО?M470ڽǹUWSZU^s|e ֬a¸q5kiSڅm۷JtL4&ZQU77v;`4RdяZ\ CQr.N*nf/WvK.t<ԥ3uA? IDAT6T7+eK/g@~ԫ[%|:cVJ+SO￳%Lz?L111L 5mBu Mq;u&1p}>iuR\m`=f&MɬY(ۨgϝA=w{5rU|y<ئ5?%JPBTe?2f3;uW_s}4Fl6Ӯm[mx'$ m(t: z=^fF1v<<<A*!oo2卉g!(~6}9լswnsLq`3)14mڔMi<ܵ+iC(n[]U|t9bzjQn 8ܳn嗼Oqӗ޻sf;p +'O{ҵKg wݲ ?y b ]|hIj7n~XSߦFjfΜ=#?jʜyi٢9֯gaiܷQQQδM^nS5ۻ(㿽].K/BbhI(WUȕRMƩPNa(I.j5WӶ}Qj8`L1kWk[:}Fs[gN` :SO42o//AR:((:X-AOBb"Dlgv=Ivɼ߶36wa_קO`0x_DjVfOHV}"3a0zpM}醍8a!!;q۷3cګ[|i-hܨ˞}HJJ&$.];ww1d`^a9J%Fc_oqS|NuQ q}پcw7;?N{Y(LV1;~vwsNs︗Z\o!cmFGG`ѻL0@iۆ_~=m޷w/_Qc "1Gͻg^F;+]WRb1܉?v-[b(t-ItY& ._8wV9{(*3Gy<}Pv_7͚F2e$̝G1˖l_ޚ=ΕÆ!IS`%K5f >s%:%f啗xBmV۪syi9 o6 V#?3k08~G z=i=m~É2zpqu[(t:7L{khZZ` Ѵlт\.-7+/#?#ʔW2Jf.^Ğ]{hԸިJY$%%oAhH>>>(sn'ƍV n czw320gdwpSח'ѣXs.2>@V &S6 WK0׮_˓[ѻW2GrR͛7o ]߿yE.JP\sjVzR W89V J ΕP|}|U" @KV-OU'gێD6i/X.w7ib$U|QT̜5N;_ʠ߰7orW BdӉ[IxzzȻfϾ(-x, 獷fR[wǭnܼ޽bfʥ.q|mӺWa ?,SS憫V+qBEA ?а{tw5u[ 'ڪ~x8=TYj5'N~pF g߉Ʊ`{dp>ݟ|M[rZ|}|WkۖSO]A}۷;Xݻ~CΔ}вEs7a+< {?ADD[u'ZwTH!J6X,=G:vꕒm+f!˲J Ŵ#4/<-/~ur^ G??Ʊ'xnB+rQ#; Q>p/^+N?3rƾx)ڵuLLeҤaYbLUQN굠Dm# mUԺU k_HHp;P]*,*IOY|2٤1];wqm_.p5ZE PYƎQE(qB"ڪKP0rPV[+S&*w+x+<*.;ѥs ͷ2dggvF "VM`H(b  Ni TinD)*FAA߾ePŴPE( CU 1]Ѽ9#h?;t2h֔޽FyŝQy  ׅ ]W^_Ծj|{# !$$]Κupy7dffl*Ԏώkas/_y<6euT>wH POv㓝ѝ]D3P+zYr E n޼ɞ_';2~(& l\ÇVסbf6E׮Ȼ0qX(7ndkصg/6ّ#_{>{=toWϠ&CenP%)PD[Uv{O>⤤bΟ?Ohh(# Ƙ2h 6l]ˋgŬ7^埩$yL}i2LNaמ=\D7oۧ7lĊ%dlRIpO6̜5d ݺҳS;~BCBzaar*siJmlp=?1cpuՒ%W;238$5kjjbfZ7np3- cVJBRj+Qr|}"Q-پcY45dOP*EΛ˽aǟբ9}}t+Z`d2aXhmm1m[by,f~\!I}fϦ|#XO2sg8~GڴjU}m*cOg:u} Ixyy9׮]s*,33e+V2q=o>IXt8-oUDdV+7oq=) ߀@F6i(F6ϟkIIܼjV]L9j9#'WڪfZa3g~SGjqciެi0??_S  )9pА$I&˸á*"6-tȺJ4z#F b2E3ggѨqRRS4Vvх;d lo͜Ahh(3|/^bۂ $ݺtfמ=f3iii޻˖D6i/b2ط n&I_,~5 DBC {FpݺN^0>јŁ/:/^^$^bדvDzMEO};Z4ov[XT;~1cOвEso&db>ڨ|3_Tp;uʮ{罹d ~7"""x tgIJN" !qw$x2Ξ;G0= ___f ώK\lܲؿ?oӧYa# 2(n͛5+v4XgȈaC_}fNXz؜xHOׇ f?SVS' u Y|||׼y֋r4,ٮzv+سojȨq uh' aC;YDSL>۵ _q!L?۶og^)^^XGٸy3fhl!cOQڦ6UQHEFYd[eZD9}1HDp7jX[m:^sgSIh`zUL4ǎm]E[Uh WW-/L_ⰰP\Zy>ZK&KDXh(!=S{{6Zn]:kCn`x oމi~s&Z-KNcZynjK{{۳bj~bYhZZu!?BVV6ɔ(I( TJ%:W-jڹ)Պ ד2i̴vTˢzΝ|z$Id2w~fZ,PۈJ(-Ɵɦ=bxethߞ&g>"Ëߨ[vŋj.\k."Dqߺ1g7 "COFF?7:/0O?W^{//O@ƍ>vww'-ya*]gΰy6._tsEmQG-7lSکJ%YYF..hZMTg,@GQڦ6UB:t%KKff͘;?K#,,^-6Zf wN^W\;;]R2bPXɴ/ۯZ4oήݻy8|w{^}%>n]:ӺU+l6d<|{M##o[8rQN }_|AJBjт۵gz䧓'ٱSPGR@g['$ īi۾aaT5{l&+3 ^a"a:ʫcܓSR8a>tnQrT/PBCBxgBիGpݺeEjV P*|2̠:<Ѓ]ruy+VbԘaAz䃍Y|~ 4q^x2K9©2ۗAaC5nׯOo_Qc#0w{YC~J^ݍchٲ% k%IBqh> Ks &1}T$8_equn]y{eۇe|k7;v~ʀ~T*L&6ŋVzMQeS*Y}"sgɟO;6nfqp nnQXi ʝQ{)JpuuEed$)˿W==wgGDJ$ڪ7D$BCB |9 o6 V#?3a,N9[3fhhGxkrX,&Bm %wPb ?cNvаݟOV#AD[%BY߷\;vcAJ$m˖ 8A>TPS<3V+f3f͆BK.MܱSѣ8g)m՝hm T-⎻pl62222R{Q)]XH?@+j PK$^1lb PJJj*:Q5dP+&)9/OR}/mtvʊ'BWyU X~4Z] lаSdg|= de!e vl_`!/N|}h&7ߞÈ!C ԲTSBn陸25ʨ1ϱ|*n99sgZٶ# ^oȲLJ :5Ĝmœ9;fs9l6nVCJZE’yvx °Q;2/n`B_fc d0Qܼq1bؐbNHHp~d>۵˗[ ϫdpN'N$%'π~4nƘ2h 6l]%n] ~ZiX,MD\l?|Jl>f>d',۷m0f̠a#x[Wzvj:~20zY[޸5?θ1t Ǘ?V1ЫMwWmo (8V t^O{ '%w@?<3=wzaa5___&M~ a%w1a¥lFzF9Z6 o=r"K ҥKLu*;:n^p=NQclIII984n\si>ظD|}8?͛5-bٱߏM[,?{۷+~Ժ-|mBBIV+ƻOHVdfxѢ:-Q-[иQ#=LHp]vc8} 73q8BCB8v[ogƴWQ*EΛ˾/O8~,lwݤ =71Y(JLYY\oz?bM̝'>Ňlδ/; ˳FpioČi:L0E?m\cF)rW}m*cOԺ{^Qqp 3xb5\x}: dKnE&`FDD0i|k7[>cF5]Lt"W$jq&cߜaZɶǜmfsj/+ JBBÜʯ4Q`y۷oǏ/6 ɡs`<ꅅr47o孙3 =oDȑYn=.x@Jȅ h37n`0+S0{h5Z:uӦ:8zt2OXdggR*0HP(]Tt:'%\ǽc}%x.] @ݺAӦ m[!8W<#?@|16lڌ,˴mӚgz=s`رxsO];VZ}[\ ݺhKK|>Ժ-|m$E|V㶪^o(/4nBb",Y_~l6tra4=Ǯ{,<==ۇF ﲇ9 z˯GsJB vndžNQ223qss+4$i:1/LKKw9ɚ(瓸c<==IIN߯\KOOG9qIKK/t+7w7..5j\ ٦,Z V'~jV*deѨ5 ˋ)).SٲbW Kpp h_x%!Ikǽrh'^ʷKx)nyxyz2p@_> [m+Ŏ;nmQPB&Jguh2vqd&!*\tCxyyyk0<~ Ǡ8e5,=<==@fF:WWVO^DlĨ1cK/MG΍7*S``hR[MW CQx1ɹçT*q z2c~[߼Ihx}* PԜ΍,@HHBHp΃=ő$+ +tQ:?ڑ{st1| Yl9/ZXhڋ.%o_OCF#PPv* F0ݴ=wߏT eܸy4`X %S_N\Xٷ9k6:v$&: ;y$ICЩ#k4 C`` IzퟣtϧN)Iݺt懣|4)&9#9շWV*Ϊho7m[ƘšCpYƏ͹CM|^J=M///ARɾ/㏱k>"#﮶#e,V W Chx\\n7W]YV:tP,û$VxڶϏ+WuE,MYsn_ٽ{6st6 tLV1amE?uX'/`IHL$ r9V\SiР>|筪s[U珳g긷kۖSO]}m?$nc嚵08,bu8%`/}ˤ&' "-AXXHöz\u:Z6oΐ4k֭`@lFdID3cf&˗,O^,^gǎAD}FY l ׮*(. dcj13ЋDIt;lbLO2.gxQʣE9t([>^y t:1ь1ܩs=`&X/owӿo_f͙ՕC I+nu뾔su[J~ W綪iͱ'h:gV_/ƕoO?aC 64n~BӓeV1хΜR/,%3MMx -׎ fά7 ݍ_hyk:_3!J~ǏHvm Tpimެ͛5+2C edԗ M'=1~ ϳ8~ѭb6bfPPJ  G4^mV+/3aGHM?6 WU˖_7utImD%n/\pΤ˨T*&=?_d$&&"CdtILZp׮*6NsO-TR/.4joo/TJ%F#IIoxyxDJ'/AP0rPV[+S&X=u+ I{ΝҹSEVf 6VOx :Fz+pXVTJ%:WꅆプyOUX呗 UGE_BzBbGrj+Z͌ڦ|:Gpde3htnhe뻨:(L}V=ٱs';>)|A=lRW*/V P3ƽ5RMU,ېJLXzTĶ=zO9]y+8g(KU $5bPrw O yemD[%BY^٥R;zDeAX,4] *G\GAdb+f;ٱyܻ3/|3lPK]vbbEnp]4$dgggsE̦,T*@eM8}Y.?X_h؎O?uU^AXT~Wn~]z |'PVVLٌfCPvqťl+ ';?-r&Ga TQ] ϐFpX7R1gel7R &=-")B7l6i$$W'#iCr=9tl6[eUAYeʖ`wʍL+m~&/naw*8nBiYVRRS !ARC@?gRRSuD5}_Vd^Ζ"+Bg0,c4elCK~wMh&E<7>(q%5{\^2STqv:/GJQtRݭussQÆϯQdY&9Fb6a&:ɩ7Vrb T-9cܥ2^e{xrK|+ gJ8n]ˈKMKY +vࠂh+1=#_`يLej233IQ-1HM曯UMR B%s<3H"B%~N7[,<7~cm IF}~"˗,͍L $VoQ>ǏCa6yn:wgvBRRӫg;/UIE*p6oʄqcd?55#2:sg7;;^y[z\׭Zɩgذy30  --Vq/0|ꇇߖba5+gF.ٽwAWHGQ$7&(oFZZ:c2gcZW҆/w771PTEK _P;[rɔFqF7ޙ~:ɖmw s4oڔs-YJHpϏ>g;aFpݺU{AuP'0_~^'1*hޜ{ҳS;?ߜi6<f{3l.IIDAT0 N~@GT1t VZ)CG9gINh_HNRU'ObqXw$IE&1mKXX=TbPf$^R/L@Ŏ=q;i6[N@O( )$P3QH RRRpQkvv+պt= (PJDhK0~{3n<>>> PdZ&gd,}wQ-Uq^\Z_c-t܉ ʔES3y\]u 4Ӧf2<4KWb 0lРBG0ywbBjK3(062bz()6nUaȠΟ# nnt'** cfF$It:8b\p T{5d:H͆bI5TϼV V+Ӫ-ׯ&fx,u F*;+z/9tW1U\.h1צrgZqww#U+&ذY,(T*Fcd3_uŌ2B;zeú5%S\y}9q(AuĹ+ Տu[6TFPbZ^q+ < z}/f[흳B\']DJiPUYm66m³F>UJ¥K./oTJ%F''/e0"#V9rOeb T2qW ˶b@k\]ȕ ӋM[uѸV"@BAU 9^3zҤq#Q?FC?II\x?ՊJU%,.>>xzzhjݟ|ȼݪx6UPJP135'B0h.xctbȅVjxjRTv;PvZ-j?? zdLجVJ%Z7ZF.wPܐa'ʜf B)Pw!6Vɻkq7&B@sEEِ{]auDm*?*Jܕb   T%7op61oW+FsAAʕi4`4fr4>7OaEEZyAAJb4frI= }ȑeŗRAA<V[*Z{LIENDB`muse-4.0.0/src/doc/old/000077500000000000000000000000001404105311000145635ustar00rootroot00000000000000muse-4.0.0/src/doc/old/midieditfunctions.png000066400000000000000000000074771404105311000210310ustar00rootroot00000000000000PNG  IHDRgAMA a8tEXtSoftwareXV Version 3.10a Rev: 12/29/94 (PNG patch 1.2).IIDATx-84%2f E h%4l0%8f\ʑ,ӟO{]+K /m{5)x~~޶˷oFf+RŧOd ?Ǝ>;V>;/// Y|4 M&@A P(Γ<=0 DpZ{јrJArO34$rڱ?<~'( b?Zray9\ {>e?a~DŽ\r>MA P(h4 M&@A P(hP'&!;1qv`I&&1P8;ĩQI0 !bt,rIi1c&ƴ]2߱Dt!6*Nw4@;nh !q4Q?h!ɪ8󉈙B[KB[+ ; M&@A P(h4 M&@A P.DI5~ > FϪI:M";b[$!WwK&~KI_$9˙D.-z!eD!6\! 0dqSI&W57w@4 M&@A P(h4 M&@?mg_D njcpS!HtmVdGje _˔a&]ɲb}i0Sb$ѺC;.g0|"}"אL4DAoEo-S!O Q/l0&CSES=¦ CƎ׆?w 4 !W5ea(h4 Mz<|ϟfy5=+a> M&@A P*D"Q\1g-Դo}M_ jKsJsOz7e0.br3 TŽ #Ljx=1ZxԾ>\~,h:6-1e1{UR Ӵ6u=l֜u,b_ΙXzsO>0~ݱb̮ tg&@AY  P(h4 _=~_\$?XPO_թ}.EqQȖz"ջ ] o_[#[%X):P ݆#l<kWdÂ1~oWa[i vntَw c~M"*eǦt+br5⣕ؚΜǎLҁ&jT{GN* Ǒc EijD&zGGU5[A|MX3ygP@ aM&@>90//7 A P(h4JYOWhTW,-r1j uj_2f >h Kݍ 1 6UV޶<5,ފؼo;j@ nre5@ YL׍O|L&L3t?Yt<#>Ǝ;Αq$J0h4ч4}@ P(h4 M&@Aw=,x)z; /nJ۱~wbJ6?fw$&(㋳ Qu(h4ԚFG|t-E_2-`%A P(h4 M&@A P(hUh]|k'6mYkn7Yb#J_@PyF"7wj׃OiJ#9)Q,^b^CdKl]t~(hF\#1ecЈQF|zBroo{-a(h4 M&@A P(h4 JWRwq!wx0Myolz~k<>NilX31cߊm#E{8-,l4Ql%"!X1 !cY,4|/~L3,#7w@4 M&@A P(h4 M&@1wķ[{K܆` Kݠ#k`J7YPY˅ċƞY"%.H_.$=0ǔpQAa0vPm +M_+ܟ]Rhjr\MdKlv88(px  P(h4 M&@A P(h4&挲 cǐ<wO4qH#J~)OWpmȜY,QF!/>c~55}.S ^$2uS4aJqFf9&e1sU(v[T&/ "*SΏ/{b8OXzýEsM c(h4 M&@A P(hڸmc[GOyZAI= шVpe4|.INh7PAl})]ę^rXx=tb!vLˀ{VY4x=i8ǔe OQ w; M&@A P(h4 M&@ATiY)yHxFa9* !~|/?:#NQk#z#HX~$^δiv; zscƚrq  ǯ" K;o0h4 M&@A P(h4 Mb\Gz&W`A Einfhrung Sequencer gibt einen kurzen berblick ber MusE; was ist MusE und was kann man damit machen; zeigt den grundstzlichen Aufbau von Songfile <-> Sequencer/Editor <-> Tongenerator Stichworte: Sequencer Midifiles Kompositionstool Editor Midi gibt eine kurze Einfhrung in Midi, soweit wie fr das Verstndnis der nachfolgenden Kapitel erforderlich Song Ein Song besteht in MusE aus Spuren (Tracks). Es gibt mehrere verschiedene Spurklassen: Midispuren Schlagzeugspuren Eine Schlagzeugspur ist eine Midispur, die Schlagzeug Noten enthlt. Audiospuren eine Audiospur enthlt Audiodaten Eine Spur ist in "Parts" unterteilt. Ein Part enthlt wiederum "Events". Konfiguration Midi Ports Jeder Midispur von MusE mu ein Midigert zugewiesen werden, damit Events ausgegeben werden knnen. Dies geschieht nicht direkt, sondern ber eine Midi Port Liste. Die Portliste verweist erst auf das eigentliche Midigert. Dieser Mechanismus erlaubt es, durch einfaches umkonfigurieren eines Midi Ports auf ein anderes Midigert, einen kompletten Song auf einen anderen Ausgang umzuleiten, ohne jede Spur einzeln umndern zu mssen. MusE erlaubt die Konfiguration von bis zu 16 Midi Ports. Ports werden von 0-15 gezhlt. Um berhaupt Midi spielen zu knnen mu mindestens ein Port mit einem Midigert konfiguriert werden. start MusE ffne Pulldown Menu Config->MidiPorts click in Spalte Name um die Midi Treiberliste zu bekommen Miditreiber auswhlen prfen der Spalte State auf OK wird in State ein device Erste Schritte Abspielen eines Midi Files Erstellen eines neuen Songs Arranger Funktionen bersicht Arranger Window (Main-Window) - Part Canvas - Trackliste - Trackinfo - Channelinfo Tracks die Trackliste - Bedeutung der Spalten Trackinfo - Bedeutung der Werte Channelinfo - Bedeutung der Werte - Wie funktionieren Initialwerte? Tracks editieren - markieren - umbenennen - verschieben - lschen - neue Tracks erstellen Parts Parts erstellen Parts lschen Parts editieren - markieren - verschieben - verlngern/verkrzen - dublizieren - schneiden - kleben - umbenennen Der Sequencer Die Zeitachse Stickworte: ticks, beats, bar, resolution, tempo, smpte zeit Transportfunktionen - Transportwindow - start, stop, forward, rewind, ToStart, record - marker: left, right, current - loop, punchin, punchout Textmarker Die Midi Editoren Raster und Quantisierung Pianoroll Editor Drum Editor Kontroller Editor Beim Start eines Songs sind zunchst alle Midi Kontroller undefiniert. Fr Kontroller im ChannelInfo wird dies durch den Wert "off" angezeigt. Wird ein Kontroller im ChannelInfo auf einen Anfangswert gesetzt, so wird dieser Wert jeweils beim Start des Songs gesendet. -- erklren Timing-Probleme bei zu dicht gesetzten kontrollern --- Midi Kontroller Typen Velocity Die Anschlagstrke fr ein Notenevent ist eigentlich kein Midi Kontroller Wert, kann jedoch im Kontroller Editor verndert werden. Die Anschlagstrke wird dabei fr jeden Ton als Strich dargestellt. 7 Bit Kontroller 14 Bit Kontroller RPN NRPN XG SysEx XG SysEx Controller sind z.Z. noch nicht implementiert. SysEx SysEx Controller sind z.Z. noch nicht implementiert. Editieren von Kontroller Events Einfgen von neuen Events Konfiguration von Kontroller Typen Score Editor List Editor Mastertrack Editor Tempo Taktart Midi Editor Funktionen Quantize Modify Gate Time Mit der "Modify Gate Time" Funktion kann die Lnge der ausgewhlten Noten absolut und relativ gendert werden. Rate (0% ... 200%): Alle Gate Time-Werte werden mit diesem Betrag multipliziert (relative nderung). Wird 100% gewhlt, dann bleibt die Notenlnge erhalten. Grere Werte verlngern Noten, kleinere Werte verkrzen Noten. Der Wert 0% stellt die Lnge der Note auf 1. Offset (-999...+999): Der hier eingegebene Wert verlngert eine Note um entsprechend viele Ticks. Die beide Parameter "Rate" und "Offset" knnen getrennt oder gemeinsam eingesetzt werden. Die Lnge einer Note wird zunchst mit dem "Rate"-Quotienten multipliziert. Erst danach wird der Offset-Wert addiert. Modify Velocity Crescendo Transpose Thin Out Erase Event Note Shift Move Clock Copy Measure Erase Measure Delete Measure Create Measure Remove Overlaps Midi Recording Realtime Recording Punchin und Punchout Loop Recording Metronome Midi Fernsteuerung Step Recording Midi Input Filter Midi Input Transformator Midi Mixer Midi Sync Master Mode Slave Mode Device Konfiguration MusE benutzt fr die Midi Ein- Ausgabe Midi Raw Devices. Dies gibt MusE die volle Kontrolle ber das Midi Timing. Dies ist im wesentlichen der Grund, warum MusE das RTC-Device bentigt. MusE nutzt _nicht_ den OSS sequencer /dev/sequencer. Bei ALSA nutzt MusE lediglich die Routing Funktionen des ALSA Sequencers, nicht jedoch die Mglichkeit, Events zu schedulen. Midi Devices Serielle Gerte OSS ALSA Pipes virtuelle Ports Audio Devices ALSA OSS aRts Dateien Midifile Import/Export Muse Songfiles *.med das globale Konfigurationsfile das globale Konfigurationsfile Drummaps Eine Einfhrung in Midi Das Midi Protokoll Die Midi Hardware GM GS XG Standard Midi Files Midi Timing MTC MMC MusE Installation Systemvorraussetzungen Wo gibt es MusE Kompilieren Installation Liste der Tastaturabkrzungen muse-4.0.0/src/doc/pdf/000077500000000000000000000000001404105311000145565ustar00rootroot00000000000000muse-4.0.0/src/doc/pdf/developer_docs.pdf000066400000000000000000006715361404105311000202700ustar00rootroot00000000000000%PDF-1.4 % 1 0 obj << /S /GoTo /D (chapter.1) >> endobj 4 0 obj (Internals \205 how it works) endobj 5 0 obj << /S /GoTo /D (section.1.1) >> endobj 8 0 obj (User interface programming) endobj 9 0 obj << /S /GoTo /D (section.1.2) >> endobj 12 0 obj (Configuration) endobj 13 0 obj << /S /GoTo /D (section.1.3) >> endobj 16 0 obj (User controls and automation) endobj 17 0 obj << /S /GoTo /D (subsection.1.3.1) >> endobj 20 0 obj (Handling user input) endobj 21 0 obj << /S /GoTo /D (chapter.2) >> endobj 24 0 obj (Design decisions) endobj 25 0 obj << /S /GoTo /D (section.2.1) >> endobj 28 0 obj (Automation) endobj 29 0 obj << /S /GoTo /D (chapter.3) >> endobj 32 0 obj (Feature requests) endobj 33 0 obj << /S /GoTo /D (section.3.1) >> endobj 36 0 obj (Per-Part automation and more on automation) endobj 37 0 obj << /S /GoTo /D (section.3.2) >> endobj 40 0 obj (Pre-Rendering tracks) endobj 41 0 obj << /S /GoTo /D (subsection.3.2.1) >> endobj 44 0 obj (The feature) endobj 45 0 obj << /S /GoTo /D (subsection.3.2.2) >> endobj 48 0 obj (Use cases) endobj 49 0 obj << /S /GoTo /D (subsection.3.2.3) >> endobj 52 0 obj (Possible scenarios) endobj 53 0 obj << /S /GoTo /D (subsection.3.2.4) >> endobj 56 0 obj (Extensions) endobj 57 0 obj << /S /GoTo /D (section.3.3) >> endobj 60 0 obj (Slotted editors) endobj 61 0 obj << /S /GoTo /D (section.3.4) >> endobj 64 0 obj (Controller master values) endobj 65 0 obj << /S /GoTo /D (section.3.5) >> endobj 68 0 obj (Enabled-indicator while recording) endobj 69 0 obj << /S /GoTo /D (section.3.6) >> endobj 72 0 obj (Linear automation editing) endobj 73 0 obj << /S /GoTo /D (section.3.7) >> endobj 76 0 obj (Symbolic names for MIDI ports) endobj 77 0 obj << /S /GoTo /D [78 0 R /Fit ] >> endobj 99 0 obj << /Length 862 /Filter /FlateDecode >> stream xXMw0Wd `!K֣qպN9@ oB´j;SGm H2{KvP}3y@X'1s(GTINUR45ue ڎc/HH "@@X$nBIxph f(]:4)"^ࢲ\wѸUk7[98cIrjč`I8/EaDHVFͬeHbil/ .u5|^*x(0)yġK>6.ߤ)liST'O(4+BW-iMOZbTq(A> endobj 79 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [69.87 618.595 208.921 627.552] /A << /S /GoTo /D (chapter.1) >> >> endobj 80 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 604.703 231.375 615.496] /A << /S /GoTo /D (section.1.1) >> >> endobj 81 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 592.748 169.524 603.54] /A << /S /GoTo /D (section.1.2) >> >> endobj 82 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 582.73 239.401 591.585] /A << /S /GoTo /D (section.1.3) >> >> endobj 83 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 568.837 228.857 579.63] /A << /S /GoTo /D (subsection.1.3.1) >> >> endobj 84 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [69.87 546.92 168.663 557.814] /A << /S /GoTo /D (chapter.2) >> >> endobj 85 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 536.902 161.747 545.757] /A << /S /GoTo /D (section.2.1) >> >> endobj 86 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [69.87 513.047 169.616 523.941] /A << /S /GoTo /D (chapter.3) >> >> endobj 87 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 503.029 311.546 511.884] /A << /S /GoTo /D (section.3.1) >> >> endobj 88 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 489.136 202.013 499.929] /A << /S /GoTo /D (section.3.2) >> >> endobj 89 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 479.118 192.272 487.974] /A << /S /GoTo /D (subsection.3.2.1) >> >> endobj 90 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 467.163 182.448 476.019] /A << /S /GoTo /D (subsection.3.2.2) >> >> endobj 91 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 455.208 218.645 464.063] /A << /S /GoTo /D (subsection.3.2.3) >> >> endobj 92 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 443.253 188.619 452.108] /A << /S /GoTo /D (subsection.3.2.4) >> >> endobj 93 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 431.298 173.454 440.153] /A << /S /GoTo /D (section.3.3) >> >> endobj 94 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 419.342 216.182 428.198] /A << /S /GoTo /D (section.3.4) >> >> endobj 95 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 405.45 257.167 416.243] /A << /S /GoTo /D (section.3.5) >> >> endobj 96 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 393.495 223.903 404.288] /A << /S /GoTo /D (section.3.6) >> >> endobj 97 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 381.54 248.118 392.332] /A << /S /GoTo /D (section.3.7) >> >> endobj 100 0 obj << /D [78 0 R /XYZ 69.866 808.885 null] >> endobj 101 0 obj << /D [78 0 R /XYZ 70.866 771.024 null] >> endobj 103 0 obj << /D [78 0 R /XYZ 70.866 641.509 null] >> endobj 98 0 obj << /Font << /F18 102 0 R /F19 104 0 R /F16 105 0 R >> /ProcSet [ /PDF /Text ] >> endobj 110 0 obj << /Length 2638 /Filter /FlateDecode >> stream xڥYm۸_އFFWDQ eEH\KQm+K>Qf7!)ɫ]ɒ_3FeE*Xt\3aSJZ+$SOX%VE@; )d O~)N'pXD:YZB2(߹'(Gx ATա݂,qر،'vYu{P5Yr`^fWmɫ1"[$sQ.Iy;5M?tUˆ&*T.GYzy8ԁ\ Ž,i!׃X$re(DIsIPvqa $#ɂ(ygjCQHhؘ时:9vfUӗ|bž殸R1c2F V) Ell$$40O0!VҀjOx;Azy!v:B|zyRԔ_.LIMfV]:u)\<4m& 6J[;p;6=Xj!ekfŖJͰA=݂`ה6w|2 5* p7[A㤲' `.j|@aM y BО)*z$³Ax#g#&,yF;8h0f] #῿.\7B\vtBjBNz5b;X D0A3=(?6a0?zߞ39X-)$N% ZRM&7c|:{8k?RWgslOGٲ1ue__BcPvT (Bvi8l~ *4&R@y00; H;>!$T&^[}auwF9r3ZVhFK~c e,tg`&3.۷ruV= K}:NldyHOT'(c~ꊰ"Sy1R>y^T[v%.2om:ٵvkq(s8Ugdz'Y1"qpDn6C$@@+ +fQ->&ۂ s}eA}T ۶'gKH>REa1pa|u|Hu: ^>kqC a8Z\> endobj 107 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [384.883 168.838 399.605 180.793] /A << /S /GoTo /D (section.3.7) >> >> endobj 111 0 obj << /D [109 0 R /XYZ 69.866 808.885 null] >> endobj 2 0 obj << /D [109 0 R /XYZ 70.866 771.024 null] >> endobj 6 0 obj << /D [109 0 R /XYZ 70.866 555.817 null] >> endobj 10 0 obj << /D [109 0 R /XYZ 70.866 317.461 null] >> endobj 115 0 obj << /D [109 0 R /XYZ 70.866 155.894 null] >> endobj 108 0 obj << /Font << /F18 102 0 R /F16 105 0 R /F32 112 0 R /F35 113 0 R /F26 114 0 R /F19 104 0 R >> /ProcSet [ /PDF /Text ] >> endobj 118 0 obj << /Length 3357 /Filter /FlateDecode >> stream xڵZ[w۸~P K\כu+͞=HKl(R% E}@ fl3gϾ_}J3PF,Y,cpJgzWo.߮^/d[~͟g2fLJ¸ݞ̷CQ"95' ><4BƑt.]J%e]Ux?_ESZWG_͡Nڼ*i##!rcBR>)ŸVҌ;e5>wzZRVEu5g(#l jh"J,Ew7)ia]v#Z f.% R0.bAt f}{9q?M6'YnIL;<"pDȒ*5A:,v3A2 r:KioypB+ CKKIkwC'ijKվΓcVa/@/h 3o4&^ifPmZkK4&d Mcykh_8 q{u SMph G\G4м|}ȗK,H+# 2/ȼ>wҠG9p9R3:V#ڑP3BtvKkMǧ'},EjE0[TH3_DGzOr&.RTݾ)o3pGtzT,R9ĠSzxd8XCo1Y(0Osk2;UӯQ5HרASήYu;/5qLj(X /Ć`qKYDHZ:G2ipڰ;K<4qC)b{qp=~Ѿ%f2F5<C4 ;iD$d*Q6Ծw>b70RJMyL9F&:Y#O&DpXD7hj}l2rCȬзsrNv`a}kK(* U=ejCs1^R&U3&Of!#FCOQd[VB=|gұ@:ǶuGXdMD!8^^lX+C3G'FCE8뫪ΖKtPʶ"DC|,OfAhj v4*zG0WZ$Y:]\@AiuҧzI\vBTs"? Ԁ_J Q![<ŏ:|`& c8}/ z*4ؾHy@ZXcsjK'I?<]4shQg2h_  ֑2P _2"$CH#qUwbzwfk(- irkl#!Ώև% JI >DkV,Vj4y;\k8nWڰ2TT̮՝f^g{vl$X??z"˗'!|By2Zi=M@[#uk<@uj *'rhFP"d2S85 6}3TtQ. H4GPv+IaoP^oE@U ɎC[B :ڌ[oDAb)xN: ˍⰡ4,r<phM_|Ws F"K yV{Eqi~F>w4(W1Xy}aSqFX@]ol/-1|L l%мa5R GiQ8 MCŇZeZ&Zz/bHZgexۺ:l-йfq7TU1 x/U:OQmN "is88|V}@ T GSb"Qc™A)OpHx=dlUt eaM~5ASĐoF_plj֓k|,hL\a 48"+NJÉ~`y0K$< YXVbDQ5ᥱOI!;契J&+o;owִظ3K4wbR9w#y~4ɂYtU 8D h Іd D.k]8^Pʤ0Q%DC #i{q^m5+@"ē Ԕ_,}[S{dQ |,L&PWKû+YXm<ɂ.bB Y.\2% `a 6.6=?[+(LИ,)>L/xwp`'Ѿ߁zWy"9"28ܣkx|,s㨄lׯM3l8^rƕ@ ifLa\-U `4E4ntA9RCyҮM;Gl];3ngشB 8ROݍ.$xPϗtx,u@wj$cb fX+(G~ԲKںxY 47j"azMmHE̗d5A"b$hk.qd+ bZf *J[HHsa .cA 4OFDPf} 86ƣW=<#BC!D~Y~|XdSpF> endobj 119 0 obj << /D [117 0 R /XYZ 69.866 808.885 null] >> endobj 121 0 obj << /D [117 0 R /XYZ 70.866 663.506 null] >> endobj 122 0 obj << /D [117 0 R /XYZ 70.866 591.922 null] >> endobj 123 0 obj << /D [117 0 R /XYZ 70.866 518.401 null] >> endobj 14 0 obj << /D [117 0 R /XYZ 70.866 429.639 null] >> endobj 18 0 obj << /D [117 0 R /XYZ 70.866 404.054 null] >> endobj 124 0 obj << /D [117 0 R /XYZ 70.866 363.346 null] >> endobj 125 0 obj << /D [117 0 R /XYZ 70.866 363.346 null] >> endobj 126 0 obj << /D [117 0 R /XYZ 70.866 265.914 null] >> endobj 116 0 obj << /Font << /F39 120 0 R /F16 105 0 R /F26 114 0 R /F35 113 0 R /F19 104 0 R /F18 102 0 R >> /ProcSet [ /PDF /Text ] >> endobj 129 0 obj << /Length 4030 /Filter /FlateDecode >> stream xڭZYsF~ׯ`Pʄ1jl+^ &!H(hY뷏(gA`=f׳`蟋U2 $Rlq5?Y$lz{aqvqr|?]k? e+k&-q#cL?RbG0[gxvG373mov1Ur|U(x[չV,О/>^/\čnV ^ޟ; zARQ*zqCA[~E :eYЬi ^gieY &ڪOa=.ZKfqŧ½f 5Qb?̄._QmA9[G■g=j"[F%*5w䝱Քh5Yɷv;1^g&uN&擐㓨厌_)3o;wyy`3~Xx[+遝jmA-pUrH!tR8E&;G*Vir\ݐn;Z`. 5*;|هF. 0jOϱZ((bl'&u v~5../ 0Z A;i?蕺eɥBkZ$-m\v#xh%ْ֟'p[ơ @2awx,̀2^:.xUzQI¸T )">㋁+*DG[sSq жTMyx ,##4ƒIlOUY+H""y!?8bmn٭cDu|^eW,Cr8!&~$ CdO![%.~W3ͺn3R=}1h㮢S* A8Obvobd8Pdg럖`;#$.[k}<{3g:!cI)3G\&h*7=9 :9Z&#:#Kp}*=P*kA Ee%=sFjw/S{SKlQ2`eP?Դ^csuM1#ѫTFLc84vڜtt@D~WHof݀{m_oӂ١, ~rXYUYmW#&?LƂ&נUȼU6Qk&D[JE8ۀLEkKBgjmZYU_2]2%yyB6L7s#$W)p-RuxǺ@c yfkVtUfQvyFi@5i o@-C$!Z]ھOH:ta/ a63G"G@=ex3}iQmzotQ䕗2uZ1~M}Sآ DYҒt~{X|>*$<Ge| Y>z{ɳrvt#Ͼٲ;VtIy~ɋj{cgjtchQMFnj \^v?#8p/dTRx㇮DDKR ރ/FK_ gDzu*y*͕}zs_ɩ ]1ᓔ~䚔[ AIUp>mUQm_pEVP;Af-m-J&Z;* =q:.^v{,(l+piN%W/.WInnR2b=k7f6(!øW8fXJUevwfl/졁q3ׅ})W27]f b@h2"|| }w^8+hjJ@"+xAW $~xt {~C%:*o2~)XEF}r0k>V\Loj[]tOhz4mSvػ14D6UO1[ `ᮀ >bMlU+~?U{(/ ޳%JG>Hʊ0.f )Ⱦw);b oa"ń)(REɡ~)e_e뗚+jtt5,-TOdAxy + mi(%(C`8*St~64[U`vA4)ziq^jEb g7.=;=-~ QӤCQHɓS)1񶮡e46מl9*>ж׽]*urve/3?`-8W(AO[WD;,3\?ӊY)|"鐏_c|M9x$> endobj 130 0 obj << /D [128 0 R /XYZ 69.866 808.885 null] >> endobj 131 0 obj << /D [128 0 R /XYZ 70.866 685.407 null] >> endobj 132 0 obj << /D [128 0 R /XYZ 70.866 435.795 null] >> endobj 127 0 obj << /Font << /F39 120 0 R /F16 105 0 R /F35 113 0 R /F19 104 0 R /F26 114 0 R >> /ProcSet [ /PDF /Text ] >> endobj 135 0 obj << /Length 2258 /Filter /FlateDecode >> stream xڭXO O<"E{=6M3Lɡtm++H~(K^m:=ZAf.Cƿ_?}7,-KYN*KܕuJߒ}fHO*Vu%pU;&EN `\w5uvp:`R򺢂]xL ,O\$Wa-/-wMڔRd?A"'͒ѼDɾϲJ}0‹޴IuY"oU>*˓g3Q{IK?eEwo[Qz.Aب#!HC>no`ּNբbpDp$_.]ϋn{/) 2yKϋl 0h}??h?d7nMd%y`¢1 ˠlRr׷Cm !&.ʛ`#j|a]~u n^pQ2Wqg?ǿ iLx/G9L$ߣz߿^giIߐ T]$\*OL]qTTLx2X`!gs b{ +Ǐ8'nM؎w$.-Σ] +'Oވyl+(ӽdi]:oGZf* 4 U3FFRrE|t@i;w74B :/D[ fvi h޳6~; ~2v\ ;o/Ӭ~rԫz6uFP!׳.~@<[Ԇ ǀHؤJ5EeGe<;s2c{T2)p^E3(LFh;~Kw эk{~bVut;uT{|9oxTa@ 꼙3sBP*}epPg~1ILb /m_*k l8fߨ'ځ;\:Ҏfʳh"UI V]R)Qh/P;8qf݃GH%]yHDH@o铔uuC-  'buVl,lu*"ۿj2_B"^$z)rN, TbPj%}D9[xY*;M}0PFNC#,muW٬:5AJQnWJEY?J2b*@(BݝB ]b wWf%0 0R$4%TlZ >=hi2z=\P1uG>4֎4ӭP.*g+1\<#U,=CpҲa &'naVu^cN *h E1h'3y`ث!Sѭ.RANӈE6UC\{YG@Wl0bnsH蒨 89ޚEXZ_{ 1i.\ ~V5v_w1bvd\&˦g~ھyr6I#]1_RI _UN$B{_Fr"wEk;h,{HK؆C-| ='b?J1UP͂<0ne L *Â(b7@ZB7b[@^ Y#(ޠk Q =[eTTVX.ǃ0f{٫2̊$ ~ Ls> +:fv6֫@K$1mC- >t> endobj 136 0 obj << /D [134 0 R /XYZ 69.866 808.885 null] >> endobj 22 0 obj << /D [134 0 R /XYZ 70.866 771.024 null] >> endobj 26 0 obj << /D [134 0 R /XYZ 70.866 591.858 null] >> endobj 137 0 obj << /D [134 0 R /XYZ 70.866 421.494 null] >> endobj 138 0 obj << /D [134 0 R /XYZ 70.866 335.269 null] >> endobj 139 0 obj << /D [134 0 R /XYZ 70.866 274.062 null] >> endobj 133 0 obj << /Font << /F18 102 0 R /F16 105 0 R /F35 113 0 R /F32 112 0 R /F19 104 0 R >> /ProcSet [ /PDF /Text ] >> endobj 142 0 obj << /Length 2728 /Filter /FlateDecode >> stream x}YKsWlx rH9 e+U.)1T*\raa1k K0'̳gu W7+MxcIaGzTi4zlM/C}ÍqSR>:Ta\NVqX*a*/Hi`uqay_&7fW?t:*ǽŋpemhn \Yn>=n=R?G I} 3U; dݸG8,c~kn ƣ4~׏;n-|- {-P * C3 eǷ8TP=5*'2/Nupk4o,C~o|<.Znk<#IaZC=;}|m`myNx۷,Q́Glg (xr㲁E 4w' z7zh6S$GI%LiAt/2LJ4|&S4BG233;*- bFA3@3n:2-ٚ]W*M-N*&~2[P̡Ymߡg$ 2 ܪkY4A7 5S xp4͂'=;\cfhr!vVX} ^zD9pwLvȄÞC z˓dˈ'Ԕw3P.̹9(;:?W&-;sr.l07`#y#gf Y h>fدSm۝5>Z՚;0'[?љt O)* 2;",mo4pLEy>E7eS I0kGN 9!:ZG^h_q~80]`aݚ!0V%C0xhg!&>Ј"X =O7=]Q2C0{]Dw8޹LXLbX}=Y8] Ip@ *BŜp@ÎA?NaR#Fa;S7Dc-w{3"I+r1rgk 0^s*ozH=f"ujyRz$rz| zPݭ02,K08S'1 Dal-bE2 cN_)Bܨ CłOxِnʇ^*x"Kk0tN84f+֜a  N,'4u1sQHY338dYd\V݌dR} K1]a]&,a8UaG<fU.)![^QqnHR1gL?&lB涤C!Qt%E-2J4%.Aod4'N0&JVB jA1w1sBD}b׎9ŞC_yhXPlEGmqghqx/0y1pC{@<Mg&w,NOr7dc'~`0C=R;ܴaOr#6sy+I1b_"YVRCp^t=~A~MOѧFk(xYUE%RŘqBl/1(_0q@d\ R.2gT%ɱx>9+H\]i.]۟*SWfP%8@"5M&T}"k**r#qzC8gE)γ/K5 N2TCDn`Ď-gRRoѠ㪥,R`99qg64vU񜳓ɧŲ>I.5gN)zTqB&{D/UFJ^H لUFy1y GsW5R/I,42Js 4{t1OQI;3S'vO88U pjCkEX^JXyķZ4ZSMTm~b{ˊ*T\<0{ddfyr^7zS;ՙQAH>9r둛t'q8 VeiIԣ,sN#"%MNA] XmsQd/IHL nj~K ] ؞D'WFBZ~nE Vy,ʏJtSIS պi%}7h]?>e?yg)8ˤLruo 4Xڑ;EBSKgs^| ^!PP &_َ!X7b-"Hi~Z*`OM!guT2@K/ݿ.i endstream endobj 141 0 obj << /Type /Page /Contents 142 0 R /Resources 140 0 R /MediaBox [0 0 595.276 841.89] /Parent 106 0 R >> endobj 143 0 obj << /D [141 0 R /XYZ 69.866 808.885 null] >> endobj 30 0 obj << /D [141 0 R /XYZ 70.866 771.024 null] >> endobj 34 0 obj << /D [141 0 R /XYZ 70.866 591.858 null] >> endobj 38 0 obj << /D [141 0 R /XYZ 70.866 515.51 null] >> endobj 42 0 obj << /D [141 0 R /XYZ 70.866 487.413 null] >> endobj 46 0 obj << /D [141 0 R /XYZ 70.866 166.465 null] >> endobj 144 0 obj << /D [141 0 R /XYZ 70.866 148.574 null] >> endobj 140 0 obj << /Font << /F18 102 0 R /F16 105 0 R /F32 112 0 R /F19 104 0 R >> /ProcSet [ /PDF /Text ] >> endobj 147 0 obj << /Length 3525 /Filter /FlateDecode >> stream xڍZK6ϯPbb~fS*'oj+GHܡHO&~nxs y7߽ Mim7fII܍ts0 qn̹ӝ tO;?n FQI@[2Sno~ myMC?9 l3ϛ(8 ]o>xWskW3]/e/tij]zNwXY9PB Cbù:fŽ Ls3BȲBb* Ԝl$k5M;"!}2wX %B$r@vv1ȜS{Fy]RE{iB& K&aY(#' |J1?0v0.H47'U}0|[;:5~lBW|K,q L$|$ǴUBG6-`.x p80s0kGBl)ʪLmfw}_DOC).gNY JlC[ms|#MV¡Fa`Q@^;KBG# ]4.s$63q=xهD|tW~R,GEeK/- dV B~lƶ|a&վR@Dy=ҡ;|1M4 Y'^`;>SJ%X(aNJL<馓Lk^UgGK/giCdC^k?QmX^kzA2Ĕ<~Y]t ĠAnCXW Đ}]#6=4Hy'&@ht%i՛][\ 0p| ;] 6$1 A5.-+#jsXXRYfzdyE`,DN"/PUOҘ*{"5e7:m2v.,$_UcONVʂ؉`BbYjMMp1-u}\VS8E@1JEFG>!O EbVo+>6t k,+.4a5g9_Zͪكvh)C'=$DUӒXJ&:'#lrszd)J:0g)NzF4~˚L0J z'1.BWvInyp {Xœ%?Dksdp]o0L)4tStz-q%œs8YaIYKXrI<(%l -W \񍌝ۅ Gd%j*qQΕ!^Tw"ze;H &KSK<#Rc ^b`C{i!G4 5s%a'2ҀŏǏ=!4AvL~4H^YɱI(b,U%ʒVr<ܗXZ:0!XfCޮG`ᕫ?ʎ& S1Rzpݩ^ ؁w^lj_*9(͸[ǒϥ l`V,!;H-$ RjOu(.xfi,y{1i M )U" UbXPB[jAwBnv lf+^~ 5{4@"[D.)ڬg1S(s`y4Ђf/w+z>W-@_,>RWڵ\uK|'s7Ag{;VB$K&?M֚.Qk)`ЊN9h!֋ N|_#ߞݻmNzb~woǔOmo";~|4^KF^2q2QLX"I(XD59jB_ی,v6suMnEga$c/ȹ]9:.zߩWeѿW[Of.$(̶DQK8b٫G}Z&R[dvg1(t똍Ji偗[8C(3+! =bϞx-s7AOtDAЪˢko}<~Xӕ_MhPuEے0q==R#wS2YISadK(6#8S˜@ 2+rNlKze5BW^ y{!ŔBg|z! jIDSb~\\uflS ,a u "Y)Kћs2>JR8F%1s(8]{^:WI65 Z;?I"Kd\djXK)MfZn;*+eew7;!` endstream endobj 146 0 obj << /Type /Page /Contents 147 0 R /Resources 145 0 R /MediaBox [0 0 595.276 841.89] /Parent 159 0 R >> endobj 148 0 obj << /D [146 0 R /XYZ 69.866 808.885 null] >> endobj 149 0 obj << /D [146 0 R /XYZ 70.866 771.024 null] >> endobj 150 0 obj << /D [146 0 R /XYZ 70.866 708.764 null] >> endobj 151 0 obj << /D [146 0 R /XYZ 70.866 647.556 null] >> endobj 50 0 obj << /D [146 0 R /XYZ 70.866 549.376 null] >> endobj 152 0 obj << /D [146 0 R /XYZ 70.866 531.485 null] >> endobj 153 0 obj << /D [146 0 R /XYZ 70.866 467.787 null] >> endobj 154 0 obj << /D [146 0 R /XYZ 70.866 417.981 null] >> endobj 54 0 obj << /D [146 0 R /XYZ 70.866 344.264 null] >> endobj 155 0 obj << /D [146 0 R /XYZ 70.866 325.82 null] >> endobj 156 0 obj << /D [146 0 R /XYZ 70.866 250.166 null] >> endobj 157 0 obj << /D [146 0 R /XYZ 70.866 214.252 null] >> endobj 158 0 obj << /D [146 0 R /XYZ 70.866 150.554 null] >> endobj 145 0 obj << /Font << /F39 120 0 R /F16 105 0 R /F19 104 0 R /F32 112 0 R /F18 102 0 R >> /ProcSet [ /PDF /Text ] >> endobj 162 0 obj << /Length 3149 /Filter /FlateDecode >> stream xڽZYF~_!%ÛqƉ2!=$%5Co]C8F'YU]A|E. I\'M9lW{}mQhεuZx/o\ov.c$>IR\ruϕcƛ$6Yuo&7GZYm(p0vys˕;gK7^!5%7 m#M}*r7mrwwcжE}V_>\uRz5t(ķNF*q֪nڦ,Y/okϪVfibd (z:óvN|< o}&V9svQ9t] =2p ׭x  w;UWkHAz !+pmzNWT-w8O ${ OgmD>u5vU;7rUY5]-BDBء>+ nUw7kI'TCX`@Xemr螠û0\^H*u ~OSI=$(cdmᢙhy3po@S{]<z\4u-#lok{vlABvF~sT=vN x28Y{VJ\za 3Q0N+P' >E%=(鎱YhOb;tg49f(]W$U nnˢ\%蔹e$*G^gH|0ۡUUܬw/DE9_͢Yi9lRVS+3`76b_,ƄBVP]Pq^'r,<ͬLABW!&4g NM:~L{B%"rVmk3 ~}8#lxOs-޺(y` +F"Aw# QaУ e<䷆!ۺA : x)e o^jm0PG[a} -|)UQcɋ0&`vVv>:Kl4H:1|! "Go7*~P<&F1/0IN"A#2 }ƃAL$Y1J$Y -Xst3Xq2KwkE>ҽORdq@P6&C(7J(F(]mŹ;kv7@gz()"H ms粭mzWgr{@Yl'oI 4D|G:'29hʂUO{@ n͸<&?pjFիIź/c4!ƪ9H "{I1ΔG*#-v1C<;"TO$If Iwrn9p-LZGS'eͳ2kU_c9`8ȊqZ[pR0;snoۘ[e07x.!&\938(ˬL&H7ex^z-<qc_y#CT \{tn9]uT%rdT'k$Ek_(a@r>4tfbFD/0& ]$`DJ:*R( =\pA)zBD6P}gеvqB(G#w;05fն[9^ǒ㦐bK]u,o+]%+/H]܆ U"&$]f1~q379\/;0R2q9%, lKDTfox!ErtDCf$")t|+BVrH񞊦Gg*"@"ބ"mSʻFCyV%A2Ȓt2tn)Ci[qcAWLhQ:KbgaF eHJ>&l@C @GJ (9Vh,Z Hh&CJheHXv(T?}<O[ꑐzYo`%'A0Nx~"X:|=ǎƗ 65/U̥RT2lqZ3㜜B~t}Jo$UR¤R d qf*BL-y[lL -v#5$35(=K0( -nX,jP/byratl"ʞZ9}l$&:enLȤ>PTTGfoVA8>pD> endobj 163 0 obj << /D [161 0 R /XYZ 69.866 808.885 null] >> endobj 58 0 obj << /D [161 0 R /XYZ 70.866 771.024 null] >> endobj 62 0 obj << /D [161 0 R /XYZ 70.866 373.03 null] >> endobj 66 0 obj << /D [161 0 R /XYZ 70.866 258.236 null] >> endobj 70 0 obj << /D [161 0 R /XYZ 70.866 156.777 null] >> endobj 160 0 obj << /Font << /F39 120 0 R /F16 105 0 R /F18 102 0 R /F26 114 0 R /F20 164 0 R /F23 165 0 R /F35 113 0 R >> /ProcSet [ /PDF /Text ] >> endobj 169 0 obj << /Length 1008 /Filter /FlateDecode >> stream xڍVKo6W>QWE={su4-pr%z%D7dc}g8u7EO;7цlBndҠdA,+ +vr{odszTDD|%A -%|w|)=D/,c5(C䃞ݛwDH{͙4CDGY2G(#+6mHf|=Q +,FMF}ede6+vl<@' YzE Nqjg- h_m 义6j K*`mA4lC'nYN ,YU )>waR('}d˞vciZY nop1/wH2 endstream endobj 168 0 obj << /Type /Page /Contents 169 0 R /Resources 167 0 R /MediaBox [0 0 595.276 841.89] /Parent 159 0 R /Annots [ 166 0 R ] >> endobj 166 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [256.219 676.531 270.941 687.324] /A << /S /GoTo /D (section.1.2) >> >> endobj 170 0 obj << /D [168 0 R /XYZ 69.866 808.885 null] >> endobj 74 0 obj << /D [168 0 R /XYZ 70.866 771.024 null] >> endobj 167 0 obj << /Font << /F39 120 0 R /F16 105 0 R /F18 102 0 R /F32 112 0 R >> /ProcSet [ /PDF /Text ] >> endobj 172 0 obj [277.8 277.8 777.8 500 777.8 500 530.9 750 758.5 714.7 827.9 738.2 643.1 786.3 831.3 439.6 554.5 849.3 680.6 970.1 803.5 762.8 642 790.6 759.3 613.2 584.4 682.8 583.3 944.4 828.5 580.6 682.6 388.9 388.9 388.9 1000 1000 416.7 528.6 429.2 432.8 520.5 465.6 489.6 477 576.2 344.5 411.8 520.6 298.4 878 600.2 484.7 503.1 446.4 451.2 468.8 361.1 572.5 484.7 715.9 571.5] endobj 174 0 obj [777.8 277.8 333.3 277.8 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 277.8 777.8] endobj 176 0 obj [531.3 1062.5 0 414.9 295.1 324.7 619.8 590.3 590.3 885.4 885.4 531.3 295.1 403.1 885.4 531.3 885.4 826.4 295.1 413.2 413.2 531.3 826.4 295.1 354.2 295.1 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 295.1 295.1 826.4 826.4 826.4 501.8 826.4 795.9 752.1 767.4 811.1 722.6 693.1 833.5 795.9 382.6 545.5 825.4 663.5 972.9 795.9 826.4 722.6 826.4 781.6 590.3 767.4 795.9 795.9 1091] endobj 178 0 obj [500 777.8 500 777.8 777.8 777.8 777.8 777.8 777.8 777.8 1000 500 500] endobj 179 0 obj [525 525 0 525 525 525 0 0 0 0 0 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525] endobj 180 0 obj [357.8 306.7 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 306.7 306.7 777.8 766.7 777.8 511.1 766.7 743.4 703.9 715.6 755 678.4 652.8 773.6 743.4 385.5 525 768.9 627.2 896.7 743.4 766.7 678.4 766.7 729.5 562.2 715.6 743.4 743.4 998.9 743.4 743.4 613.3 306.7 500 306.7 555.6 743.4 306.7 511.1 460 460 511.1 460 306.7 460 511.1 306.7 306.7 460 255.5 817.8 562.2 511.1 511.1 460 421.7 408.9 332.2 536.7 460 664.5 463.9 485.6] endobj 181 0 obj [583.3 555.6 555.6 833.3 833.3 500 277.8 373.8 833.3 500 833.3 777.8 277.8 388.9 388.9 500 777.8 277.8 333.3 277.8 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 777.8 777.8 777.8 472.2 777.8 750 708.3 722.2 763.9 680.6 652.8 784.7 750 361.1 513.9 777.8 625 916.7 750 777.8 680.6 777.8 736.1 555.6 722.2 750 750 1027.8 750 750 611.1 277.8 500 277.8 555.6 750 277.8 500 555.6 444.5 555.6 444.5 305.6 500 555.6 277.8 305.6 527.8 277.8 833.3 555.6 500 555.6 527.8 391.7 394.5 388.9 555.6 527.8 722.2 527.8 527.8 444.5] endobj 182 0 obj [575 1150 0 449.7 319.5 351.4 670.8 638.9 638.9 958.3 958.3 575 350 481.5 958.3 575 958.3 894.4 319.5 447.2 447.2 575 894.4 319.5 383.3 319.5 575 575 575 575 575 575 575 575 575 575 575 319.5 319.5 894.4 894.4 894.4 543.1 894.4 869.4 818.1 830.6 881.9 755.6 723.6 904.2 900 436.1 594.5 901.4 691.7 1091.7 900 863.9 786.1 863.9 862.5 638.9 800 884.7 869.4 1188.9 869.4 869.4 702.8 319.5 575 319.5 555.6 869.4 319.5 559 638.9 511.1 638.9 527.1 351.4 575 638.9 319.5 351.4 607 319.5 958.3 638.9 575 638.9 607 473.6 453.6 447.2 638.9 607 830.6 607 607 511.1] endobj 183 0 obj [562.5 1125 0 439 312.5 343.7 656.2 625 625 937.5 937.5 562.5 342.6 462.5 937.5 562.5 937.5 875 312.5 437.5 437.5 562.5 875 312.5 375 312.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 312.5 312.5 894.4 875 894.4 531.2 875 849.5 799.7 812.5 862.2 738.4 707.2 884.2 879.6 419 581 880.7 675.9 1067.2 879.6 844.9 768.5 844.9 839.2 625 782.4 864.6 849.5 1162 849.5 849.5 687.5 312.5 575 312.5 555.6 849.5 312.5 546.9 625 500 625 513.3 343.7 562.5 625 312.5 343.7 593.7 312.5 937.5 625 562.5 625 593.7 459.5 443.7 437.5 625 593.7 812.5 593.7 593.7] endobj 184 0 obj << /Length1 2424 /Length2 28291 /Length3 0 /Length 29733 /Filter /FlateDecode >> stream xڴeT[5Skp ACpwww'rN{t.* %Uc;CI;[gFf^- 31BVلlP4r::Xy(R&&@1 olao6(9938&f&4@1;{G 3s1~G-50ssd vn@`hbn`m 3|JTՕTiU]=@\DAM`ARWUWߌ4./&$ wF dC jhgO=/3?-nvVૣ?q5_~ gadbdI_J`+N@;N&&_9%%9 kbL/&1G9rOSVcc3uq7]ӿ"L-M~w=3 d" 2j rųevǖDx̜v3pI%lll~O'g;Gj+[;7[Z=Epdf&fȜw6巘/{;{ b Ot[S;Ͽ@&V94jlgk061cRs.?grIX[+ؘPwCD7Ug 'I wc% g#u_rgҋؚY'H9.,ұ}gX9GE#+[''L=f `PRuH 0pt4c+&, wq9$'I_ $q  .f `L2;If11 `L?SV15x`kf  jc2p2 ^kM@V`RS`@_HOv`&YeAN_ A K+2 `?v1eVfw;bl X_O;J' ?9읬n1 0p@&h_.A`\/ Vﳩϝhgeia |򗉼63dʁ?OPsb`0pX8N|nyΈ/29Wp Klrt'>xPQ@&e4oJmG`L_mS»H"cꁙKݤ4DzZ?2ډc'b<=O17:kns,-hK(x]K]Oq}"˴XcP=( >7,CEۨR"cf.W':"2:FL Oe=Z6B(p.2;# EMmH}8\jg-б.. NC#œ&ln6Bv)h1i7 cbASQў"|pbcJnQ9*.GGh@M;UyYP%ʐ;M.8VAJ'nڍPQXܾZ2*νFs7A~'8R}Jh@SrZA.swf'TB;,7uA7n(j†KQgt"_ \fY QzMF"rVݝ rGOViMI$t?>!,^6-dxL56=T|YTs}y+BA]LA& Z,<̓/ LŲ%[=]By"2p^F J}GBg&0RL4˝+ZhP6W/1JWD8(k[52Nc c`Ɏ Sކf.!FIÞ/lBe#Ton޶3ڊsCtsI$e9cQ!4N%"?~˛+i9TK{>oK'}d0ai:6$5q *!*JE~ñ -ƒK {1ezlRs /•bY-[ؾ*|ę~I5\7߄J| Oy WAvznl#&wF!rÿF\yqruw&Z WWZ;HnS\ YPELo Gf]< ,DMT̆u/gÅ 8M.faLNOŖӤ$faSI=JԞ 9"x}~YCǟ ,+l{Ru8.G?G X;hhUۨJ vk9|01J[]N)|#gqx1&(QSƗpKwnm̑Hr05ꊃL2=5A#ciBE׎d|ݷR l|iቢ*4aJ5k.ƼFk&eiڅ1QHg{11xa'| eZWK^کibI$dC]D^pVܷ5y_6 Q5;fMlK/tD6]){=hYe?* %O&= m4gR3gt/ΆFtSϫ?Foo4DӠQeeɩ̭3JBKUaNqM}QpjKsFjEr :l d"䡻*o6$#u*jTmu2Jͦ%1_@2fƒ]l/.@ɧK!REjSo٨||bSvW0y@YbWLO(#{}`:Xklrnt1 S@ӓgz->X7Y e%V+wC>G@vyLφǚXpy${NxJW~ R "?v4Pp:.^tI M8y BR'i@aBP)Tm}v:$^\dv4mC1blE۱ WUw^e\wztd@<p6لQbI3^oR Ԟi:H]-Px&YDtd77M٫_6Ҥp<G 6LR LIS]ڑdPZ`L_tu;;jIESL k y=K #uM*a#$>ufml(7cq5驎A(sc\=`U?M? M7萘4]wS"q ^o'/%\NWW* `9HG]Ʉ>M$:N_* ^|+rT}gm]y' Cv J$"5߅[uvM)k:몯?%s1=~NӅpto,IƑ0 21˃oRCcuYzE{5 *Nbo^0ks qwe}јE_LRc[SI ~G.p/LEe n9HX&Myg&ZW4&=;c-9lD]EgǶoJ32͏(k9@L0T3zak5g󗀇]όtXM)diploOy(/k_`jo5 CKy ܄}&<bM*{@# ƒeDƭ S'J:[ti[C9ޗĽG=0~qHަ5%9b(MdXrpFz`8 :N? Sq1>we).NpQ_=N_;ͰĕZID$1ѵHyT`pViHGI̅4ßÕϖQ髏guk m jMF-\7|9u_J 2>_'5.x;>_BeAV&s ԮIHotxe^1o($Tl>14ek:^SІXlc~K:TЏ5UZ?R)-7]`pz<|L2I?ʄ3V&zp=H<\J^< .?/ 1qSjo | v~U 8Uv/ϫ٪ɚ=(-KNrV a>9j6ƆOdb8jLɄ[0(t) JG-}JĭmKpˎtx4~_2b4OtO9{ӡ}3~YdC fJ=2G$r 9_L7B)MڽP4Lĩ)=Q-s`EFAWEnXV"Ȱ) Ȁ;`^^uXo'0(l '>1rc[vD nBW'9d{ߚ"GjP wd|5x:h܋p#_2Z %/֓A\Kx $ B)-DCHr LuuR5Q7Y;>h+U!4'M}Np4>/TG2 35rVDLQfci( wȬۻKi{0]=^P0ٰ_⥉_2{q|H I$ϰHH`z M!7gP P:4*(U1>0\8@NmS W*Pz&Ll ȊE-fĘ [TxoՊiXC[VbTe\5 j4ئy$$%^˒Ɵ^y¼w@ޯ֑ 0aޔa@G*-]H8 @/ 5U{1fD?x+Ḙd{ڶe_(C7 sU7t2o *AGO.dneWr#(LH̎F|(h/s*^r1qcS%9d*GiJ%J?aTUXn#sKI$ehW+ha BR6I<Qآ"$g/\:'_uF „,Ԡ;QZ}I*3j:j nB+UIgD$Py&{)k{TGˊO+T/#%};cDcxLQ%dqYߜ?\ͩG] "[}!^iAst[.G_밪IJ+\',VRh[f婃Y%bu\ڇ|/Y؊# XqhS;QOH{ݘt-D\7߯W*,72qj߼X}Lg&4օJ  D=O7">?` wԾe@Y˝l,G^KRӭmgGLeXGp -kkڡ Lr(Ň7$XҤ7 '+cXtqذD+x{`[ds C^)ȪOTCj\jC+d0ǂ#bA*Y5 f t}Ͷ2&6BğT="mڙc-i9hZsz4X^hf34?0]fuB[TaMySZFM}y&3hIH9Ggʊ 0ƥqtnwV!An!JoVVlH|:x?qϫ.YlX|5 υm._U=bh>Հs{ ~,Hu'i՛h5; Lɑ|lXuu&u@L+ӫKs /*:y9m3|k/ /tT3vE] ixW~*&>F<4:R\ ah1,3u/hG~5`L,[ D`x;J>/dټ؂P"g؍췴a:0̶H'JIj Niʻ_q6ʫ>HE\aSt&׉ej[hvX5d~@?@I-yx~w~ɀg=ۜwt*/Sbl)‘ VpTIUSVIfL{\kG`\rsy#lwX4zɑbmB5=O%2n[O,=|$%-St˪B ޵nBc*vۡ/y{Qn2aS)3Bqzĝ'ظRrJG;p=ǚ!ʉv! A/.$H6>/]eTd/'B c-#)P aߙK7v|g+>(}> FΖkL^3!Y\ /U8c 1-TeG'x|Q#?71!}NޱɕhK -x: k = &+}v}1cMM̙?^,f2h15 V=w"9:k Ѻ1&:}XMC)W R:7pZNUF%YcPNHiyT)Y C5,sX͆|wWoOH/L{t*FEx -^۱v #ky XʜrGIoD?-y?l"OE  (H x%D/J?P#F*pM?x+ļ^i1*ؿĴ"X c-U5hm9}1Ht:A^N.@?thl%u tw-@U_F]!~g>® $ҡ$Á '='ۭIb;|>42zDHYI_w>M@ 3_Ɂ6O|>v: k MifeoճY Mg!b /| DD~8 v1uxsjq̀E Y%ط(Ij_1ET-X,ʡ:kl_֗~NWU@IN://OyI=PfL|*04}vƗ=(\N Wŝf 0q- uQ[kߒgؗnTW{*g'nBU Bf,RChp*[G?ܤѿ[Obt*{; 6>Ut;D8 .KZS렑.Ii61Jz .&ˡK{}}(An^#j&?aPzaZԕ(؄¥ DĨ"?ܴd$"9~/aݟ񇆝F[}Xw&UkrB Yeyc1Io_Z {PZl'H1i'⥟O'` z0bo4H'h[b{ (W`S)ɩm%И/F͏-u^E~'0sm'5\b%=!y[脣Ú?O1$liOb֛pvzE3эgwhjxuu[=zaHEŕ26Z{.“ \}ߪAH!~Χ2 .j D$bKiP9Zo cncOy4> &.}|@Ē۶,Ks]9mv̈ѹR-vB5o$lֆ^N~LqV-l?:[]a$URyNQ`e*\W;mi}Ojbbc y Òd&|Z0>3.#ưk; p۶B;.*эmU`/ M+e 76!snU V~أĽ•3 >4~ .Y)jxJ}?|9.D7}]|5zԔD9'_ N(nPb1M4q')T&`jjxw!z6ūD[S 6&h jy![XJV:TR$ ,Zd"Fb;;DlOK7Brb&[D9h8:]T7oVo>Ew8e8lt_ٱ8+3ϱЍQA_#X{lsz #J ~ffaڿxYxo 8ugxq8~E~ʛ>#*Q3Ijv{y:&K%&D#iҋS0M/w4pv0ZkvP,FHr\ <V5S3q}*uJc9_!Fh&Ql|Fs 7lCƓCT%x#3 QL<X bopX~j\u4*yi.9S{$0>PJJz%rjT|M 3)1wHr}vEuˎJ?Ye-˂rYǾKOktƐlL׬X@C<> F˯U .lْ ϻ#kh&YLmF+| ]rt*i Z|eG9N_(@O  *|0jԑbĦ}{ٷOuH4Sa_)S ␧XG݂;֏ǼTN\ԣ#[=y=ŽL鑝ompr =3tG;> 볅M4B/K{(-VA/a"[d%( ӷشYG0lmX:ۻ(gbc$YD\wi#r|\2C-E-b, mAڈDM!!|kdChaaa^vPDWjYQ=.>'}sY@$=lnxi1N@duk9[]bkn'D l$\g-kLOŐ hiGGn1_z6+x~O0pW ?fTCI3:yU6Ʈ<[,?W~/y<e({(ZQ=ŔV7Mr%qzZ͇̯U4O.%v-$Rf}6n+\zR >ݞ9 nu 9%j0RE|t~Ż`bׁL-JNS@lPr%m%b+F< ",KiQ4/V/5}Fנ,tiZnRX?P +ۑG\Ҋk|+XpfW?\aB?LVM-|9ɋ?ěȰIUdoOw(4:D-%p631;ÃSٟNCz5d皕q$rY64%,P|%OPd]/&v %ʃx->(xآ+G>]-+ԶedKϊ2*Csy`KyE:ULv6]qϞjM'c^NQ"Y^!c+ q^t.&O$E߹jL7`FQ^=AX7"ыtk:^u'>TFʕB]1XK(zN<19ڲ9ŨMEx&%T>!s_N˶9D`M|h'7qG|AbLp"|"G.ɶ"((okYꐖUlNGMOIN=$5p8@b f#H6+sryv_foVE #7/ iCk^N;D H%$/E4BP>'Q=]P^5CG`s v/$dJ>֓6_P?}l:AFdEeQ`;pP {W2DޚI924akJIp, '0[w%*J6$zTy6]WҢm"K,\W_ fpU3)'@D21RǗ7fp:Fّ@  ~۸OXb=]S7=,)Bjn\AY`$XYrmw8Fo}՚ kM/+dTBsD3 /lp~8ᴞP="l(BtS|Q ʞ ?3n4"T:Uv YBJYBT 䮗Jv5Kh-z]bmNjA2Mb:&qpT1{Vj7I0I6^Srg~ҍbK!$?-ؒCfKL>GEq Rc "wSrjjj^aкA+E$h)دR-4|Y|NWzB9\;t-Pci2`ZkF= NkQUcͧ <}u]ql !TX^d=ia=W:L(7 8>Ҧgv,K89]ōfLeCAQEHZQfB)X1Jl#o>_ƶAi/`myovj , 5Ikg K3n+ZG2)mB8k$$< '6keZdʫ]g#pӶې>jLEP#lȐ똾\%^MRHi킭t?ZoXG[%@P[ӊI5 D0뷍A=>a7qr.:5FoǒǚZFW;D)ĨZv$qQ(9Y-#ႎPP`iQ\;!@sHJm 4p -;[]D6{d 85>F*QDin(?-#\[ g^mJ[7-$vω`V4*9f?S"0 LW>oC/>h3ι7?üEB:^ d GL_.+>1nCg 7P(" >k(]24Y @CwGU0;`<ܑ qo /|Z:8 yJ\{+É` iO/bb֣LNȠ*|vgَ3ͮ]quƫ>cj YiA };Cp#?Y6 "TncÂXu ߥzFԣxZ[l8Ս=naM&륕|_[ )C⹶|!Bg`OPI#PUl&yOk_g2% ;Pnr_`Iu_!|9T>eCȱ1P xOJ}0Mv:]GF]`DO1@3Qs Eꇞ=dK"33uzW3@tRPě`?0Wd+qb\} c z5ey"q#RqG(!A8tՄˈjJzCT 3w!y,YS*'ThT}6>Xlڷ*XM[w?͸TPsrQaMu#U5|`>Vhj0lQc3nudR ')y@bCVL䪴yuJ#EwR߮9ٵ0qkGY흼uB3y \pY(Zа6=lǿ 4h]tz9~5-i(\=8PPhP7 >WeyP]sϟ0RZݐsH!z>Ѿ'iLb/IJ3fr%PS.>^ޒGn*傛 Aq %Qhi9-c^Dq=SimӵZ̗M9fȷ~A3w?%G6u] Ltp݇?YRwX}LT nyI= xƉ&oX^;JrSf{k_^o6Os dZO0 Kcy3U^@ɇH!SszR^Y+}GHlgib+x10˟R#g*\8рov*PN @z~ :p7D%%!{ , %muJ:6NavuvG*̈-|= "_܆%ASw;:@b=N~kO^AN͆ۏ B/"Wڂ%vMnD4g:]ѯ,⒁+m*c^U7i.[;i04L%RrB85qgxrˁ2o#{Y`:Fcpw x}Iݙ-+ݬTn|f6 ʑkR;/ ZF\p\h~:ΰLІ:G!mm/<[~lZV8IV~2i5a.d9&+ dlkTjomyM$2upv|Lf\ C 8b# "k:p (8Bg]dGm*~p_|#st*)C]w֌XBxy}+CP%EY¯۸MZF |1I8ڒgq!1 >0n3o2 ++n_u,єzU-A94MW瞖)J._t"( ^6X"S8ߤ#8G?cl`CH'6^ed8<ܴk2$=ɱ/Rۮ!6`FHX6xL .܏fcE),D%:zXhf 6EL3|gI%\7½3PUA~v86zWpʁeniFjA'agŤVD4v컁e2JM5m[ qA!sӅ:'>NYc5?ei_4u w!{! 6 f+ɰXh5똸B㻧"-31BCnb&>ҽHʾͭhSҌh]Cy2Ͱx )H7R~4WPgOFBRh Ӝ_GSGE_"2eamٸZHyZb|I;4]2D{\c1 2dF"f'E0oFU_ 8e8PROM e 1壋pMsG,YHkj-UG9l12\@il1rN/W(} "piЉomALYێp{C<\j:=_-YSl?|%̘Ro<}BS_Q$#T5!,&CT=]ȘyWigĐpߞvwpcz.iciۛwT p; iV˥UC8 > )k:6A5l\ASq6 uwhZ FgkOfDt\5hƠTql뽼sR1Ka: 7%+c_;G% VC*fMݷy'+8[TT(d.#dW3i۩/I|b=x;|82T[X0:Vש/ZϦ(BU6]KR#?{!u9{7%ljmy`Y:)=Ikp<u՗ e/IV! kF%P#_G.sηїͥMoeI:3T+%9[]w2|~ji E*OL#$oc-:$I, l;דp(prJ}&p="{Y6!-$}J҈[tToEBe5$)Щ6miYMA8bh*q+WZ;[zS`'?1@HP\5be|"0(##+f6:R р,s&zc{1a Au1M0' P9RsW?s^a!HIs(_Ϯ@ 8iZH2XeЋ\_do>X[hWo!>t(FZ |9>3muCRɨ4x@*l^ˁB+U7b41S{=Zn} =HTl"W,Xr3Ty&H]޳< +8oQdɳ#dV1lox≑7 wxCoӢ2vBۨ[!GpeS<9!]?.D،@13XA ?:`4rơRvyYOV]m[c5&'RoGu:i.^=6]VCƣhM>tC^W5xԢϐm;b/m= i?d[UU ye^`&rso+ШeekImeH,ݻ?ԺlV 3\ĔҸg3[UzTr}kYK^)$t?KnM>"SZBcoSO+vDae(.80LBGW)=V[8 =]VE8ζ-Ɉ+喕WΙ4=a{6~' sϙƃ>EJUexJ:N+U ~ҭMT?=F~oѵ)z%]b,zC{_~~Ӥ CJy%7j=L n.u ,NEG[H3}-ݪPF˕ONܺT jD](1ZG_6LQZp0H@9lj[(3Ցf]1X\"B3vurO[CA{QA҉44:|td]E۞Κ *5=P\ҹ7gM.DSeʉ#[Q12Medc4->k|.%k( MlQLn9(zj5jtχ jKP ?"d3IKb}$oM1jZ:_{7 DA/B'ߎJ_HEaawE9wDڃMu4X*u0ߜ0/1p% N\TU#i#.li9_.{}7B)Pk퀌Ě\ҹ~Hjbkh E;T8Qj/5C-KJF*T*\i#a}-ߵEQ~H ~3HTW0NxFhc.QɁ9a'"xv"DEwީ=uT[%_6N;n#]Fa\ZѦCg~WkO$eo=aDEGc]lʯ~0ImԧYm] *IO#A/%\?wIߖp= rUXwoM~%! DyeYU4㨇š[u}{j{p?&hKbCH-s,Knpt{oSD]V:֠Wds2$AY/DE?iJX~Ί>~d_-\8yP|/ ֚g>98#N_eB R+H8}_n]H4ӘϪd!S2 ldg AՙHf?F6LQO"EEeiHF-+Oz68zbaL#4ֿtț 2`¬WQ2wA(Z\mGP`sJ}y_IۡEr)"[+jm =kFyM?DԒy#>N3gM˸Z!էfvtPdDͷ"m[z, 1"v2i{O1Jkt ѿ>m,/@Wlhf5|Qa["NXQly ~iu أfЌB|Worr6̶t UD#E-NQSv..Z@rI=Z:ۃùx]Zi;gL n]'I##9qI3ʿa?L~8(uqh/pdfR*s؜NMJ{Ke|@0+‚eߍjhJ}t&#yX` ]K97߆PDs_1Pȕ9.a+d V?:uZAMM- kܿ[TqIR=8Vc1Z93Gl랶4ڶwC9ln {fG3̞C)ؑ*ЌY%ΰ|@ Ӵxa>ESr=.fVrzi 9P#͂#5Kb WtqԷtr#sx{[ -X䎻S>Va/[ϭg#Uw>')k(O6K]L$5esPO{٭~8!7-F rTS7&5sRH`A$H?zr>HtN:axDm K : =8Ie0*J <0F[@j tpg~ =Oܗ@:y$ki&f؈ ‘Zz}]O-iDuVQD%mpHV~B1HPAքZ4VjLe`=,:li̝ . ɵxkssiA^wo:/8g8[Wkڐ6L;חB kj\=`زN.qy<0߫Fl5ެ{teO4Yq}45E;DH3mA v]샃V(- bC,ubrQ,KNwmS Μ;? P]׀~BĵK0O @ 4Dn9 T<PQP+ ؓoem,# ."%Wvh{풯8<uz^hy}5=_džfvfFI7BF_)ϰyZI?j/y"Xhx:PTk_I`cVfCw0VG8vzkh^!ǐ|Em%)!>|ԂNUz R\1[ͮu~/6UMB;̒dMD@?T)ck0)t, CAFkkih>Z ^Zr7b'@tbI{~r%;_/ީ7YqCX qh]A,TQJpdSoPX:\-u/w^'ꗈ["k7t!72`J{pp#R'WdߵXa G06pXYiҺor3C Gn_ yoTzO~'Is8ML7e=yuಆL/.QD B͘^VM3ۨF|.s]4GTP-4L!K0LTEBȡpee_|hvD57$#ĺC "68"+fsެ-cS+R6ze+A!W>P; ]KT+3l0X҄u[-K؉ (O;5H`vNXEՂȃC\.nj O7ACPKP(Ȼ/\z'I'KZeuNؐb7ۏbyYd^RUN' 53}OaqCRDK.XVH4kiq4=vȲJS4 xlWc4|T[ݿR{uxz4, I&̹:GGd4*IG_#*8=ɣ)3LjH@9GZ~hTE0αvvFFK\놜T7RAv6IPGգ|=x(zhAi5%SNbF8nz OC 0z XVF3#f-VRbnCq?3H-Æh)8KEUQ1O@Q4RW C:ξ ݎ޲FZVW%V?톀CЉliRM;?hA#vXݎ>@_m©e<LM}}<|&_YirU,HgMR"0PRAp֬ȎGf-@Jlo=f^7NJVP xLso_h@j#e= wi?ha]v{sΑH;1:`c/k8ƕ83CpVoB?;ثd#&h?2kǍ?" c:_ezk=UƑeKOw4n-Rc 7b7/dQ*G4묔yx܎cM'EzC$hϙO-rP eR*5AF_MEP/73[sKITHnӾ_"eEԊ!NMç% HGQ5*a&|BS^mY9}1rm|ί,LAس:KX_osaw ݸsj}zc.9jYf)#VJrfaA&t!tϝ2W]z'ڄHA"bӞM \כ [5>\rNYpZk{ [  PD6 /1fQ|'ڷ".rZ7[+Ixgk A5b~EOg{VVmqV>r1L{Xb拤{-wql2;&y ]\-EI``V[)IzaeԊsm50z$[7(O ҩr[8#[o!q w @Q%##%= bnna cӶ|ZAbT;Hf荻R Ixt-h6ϰI~$#Yq>2 *[nIireؘe-n}"Tek (RN᩻ myӆlX:9Ċ ޢ-|p{<>f!oP0H̊0' vE1* 3]AJ=LwpzJYMR ؏;J8$KpoޘzkL )e?|l'4eb5 78&P&&n[}b!3oڰ^QQp$a[#-,-6ݣ |@<,Mcm D,DR|x@,,EH*A<8j֑;4_B@F! gIt%L*mjN+G_6 P582>7PF3 LF8hm̼emEbbu눝״]%Ph-dL}r#~= [Rz]Э#҄[1"s@<@pLvæ^&Gm|5 M YiqU-6%iW,D66'/gD֞Ձq m|gӅhdȆ[_+>Wd=s׺[BsRxSj@pٙaW!+*dFڄ~& ~z­M 8_zb :tۘ{#wإ3 LkQ~E$ѴD(uQ\ vĂxw%GSt!9T<]RF@FXC:ybcZ8_9`cϮ`V,Y&Qtɔ7u[snrWVWqa2_~nԙVWD ۶vr^Sx/m)X4TwRDћR'39r^]74T f48q2oE:9! .9[XZo:"0w$2y؄X"};eܾ-sSc e Ua:1‹fF~Z0x6 8̐UƎIfXdrqiE1Hw6/w.1|ͤkL˱U+6(NkذW>am|5LX|/s (AMFkIؿgfKGki@/BiYG~2e%k=R1/&eK@VMѰ6HvyBN9߶xS-rЌTOēp+*x-R}EB*Dmgґx)^ރa;T05򐷗v$ݍo>֤ӈS׷x>I璱K}dЃZ.]Bh ͍B1aK jh( !RZ!G! &Ya99ݪ_uu@;OyI|8rϷp㉉)Q섣KX"n p5;r4gγڣ#kCNQSrX4RTS5kI0,\w,nҊCH*pݕk~ȦH3@+80(L'ժݓ*$7)\A6P)B ^Ne݄ J|&.Rf"U5f SS"#_s|* Dh'ǕSit6[<)<\%㠭Jgf8 Ka,rd|_3&CJG؊ÑdmzaG'D5#yخܶfocՎ4{S`s](laaugu sFPnlPl wrk_: uE_qR k&{Unhk>%a \/ Ea-kg?m_"L.zhA[qD{'>k(7LF팡%H`@r”e> endobj 186 0 obj << /Length1 2355 /Length2 26515 /Length3 0 /Length 27929 /Filter /FlateDecode >> stream xڴst>ilLl۶mgbۍ՘mlil'Icۿsw&v@gg;?*{{0 K)iϸc$fgbojig`fc99y23}M  0wlߢ! `yq0 f` { ({?= `PYq4YAM ?] F2Ynb6wu+_?Y te. Wg|//dzPLInkoWLddiW;U;S?F@Ja{7w=;; '_{n{6%c~m>d8[5,M߿de"gd~1_ y {xӱ:fNY{ /_yo@إy{`ԆR?22. ~Mx6\,ќ-b@A@:y$_r]&Y0ZsRĵඑ.H:Z`ܢY1ՁtvV1tzK| @mP!y 2Xg.{,S# "N;d;ZlQa^(ƈ4Cw'I< RyjyO8n_iww Yz7ER (d6MO<+ٲUq{Ҽ}h:o@ļA ngKs5GȸAE'bύo30篤dhBՉ>F"V{`qѯcbZV66sR_͊%;٨FhIodNL+?t RGDZ <ˇtrq4G+g5TCCRn%| =OB(\py%mRiv?TX糾th@ %f M-[I05.st$t )7п`oQUK'dOĦ53izAHE:k[=SQt9ڊX~"mS p;hЮC^ k4o˔T@haӤfy[ukbt3⚴̲@ihI<}"$}y'7A~H'Z.<| !}m_S)[Cb_Xd ui4edcCQ3ȭ".ԋR@JWp΅J[}”! \u>'a|\3;|(1;WS ]eS/?xvlngkvBLܠ)چ&򏡹jf2yfzV yyq9XZ9 9q0+ZX j?&#M ZtHRÍ-qwA|[}a|{PFj' pZk P,ŀ(g8L{RmVp A, _ऻQY^/k 4;.ۜ;Մr|9ĝcc^ACdUtXwM?8tQ$^]y(:D- KG3}h~gE'ZlāF#/*xiJIpXN;9ގ:LHLZzXַcVE{>{׏)10cWeQ+ƑVì%5?+= YȺbx,H?"F%fͤ-p2۩j/{ӻGؘr? Gf ~xVN" 32-@2xhV ZD~T 8e{!tSV x9+Ҷ;>3j.Z:T=+i &7]mo)(f+Nca*BS 34f|;$h\ǭk-؞{C/\DMPInz'7QO M]>iH'FOAi>Ъ׭ ‰Bf4I䣓3* 9EsgFѱ,'hV֍j<*Q L&jЃCc_EXijpYU/aV!v[Ũ}HюdbBȀz֣U n̸0L#8gޚ^O.l!ԧ*Rk/SSf6gl%>hfd>ߘ GZ%C^b(k#̙;`ɞ+<}(6a>YY=\XtbhFrWmT-ewMch䅣Z|\l/hE{jGm8pz  5N #}j$B]L*^ JLA+(.j5er 2HJكAQA+e=q6lsV%VNLg;_c\jh bk ,0S!|&t|h1ܠx("uAX>l@A-rXW:]Y^|=s~~)`J* 2iwP8.M>bo73 $|\a{3L-sKݛ-kRkQ#w2nC%h{{}sjPB2|lӖ)M Վ`AJ`semuC7a[r҂;DP}|X{6Л>Gse~5W hoAr5hM=3S_OBBrMWQ5n+%Um -@G$֋c#?<=݀}zB4Y[@ w^Q*>M94OZ׽3_rܧƾYd!Z"֩sel Yd9wx@w;T!Ak{8<ঊt^̳Y3UW7{˜X pL|L*VV-'TK"j\膔Oàg)ųQZ?WZ.jymzfHYX:v`GK+E௄̾қڠ [&fAA rqCL9у5P,YWp7χ_%SX>;8?3(`k:n; 5VRj BޱN~=X0 v8Yk]:!͂ȿs b y:R)"woSj!#RkwF/ u(`37G4@1jBiZ`贇<ʷ^_{չ p۶U(~8zm^+= cZ`ٟ<<6Jl LwMдUD5-demI,OYvUE}ÿ Z` _bO)I$!7bR*Rv^➟M*J"ԣmSIuM'|--Crfq3#7DxvOD71S`8$VAA$p^q4#/)},i!^s#(,ӖtR;M ;>9WP}G2S:("; ˞\~}I``/LU{q~$n eN,cY 㥝{]Oj?|4u>PsjxFiA[SSKgUo&ۊp b u7I$}F__YQ.hR"4]uamM.U蟊)9͞蹛?9 m\:1t]baMkv!!P/IzK0C{ Gd;Fd_x{H~7ҠVSk'o}<nµSfQE'ҟ]9\+b% Υ NlmF9!7&*Ճ솶bS|b.V;4Sl0%MDy:"2{~ێ]J//ÛJ|~ɷ+3ȴ3n @@$}!_4- Ek4N+5763lL$vhɲcAe݀'r`~vgNc)ڏ3r51 v`u}S]Q'ѭjx#?=<rʃmŷj v7[N50 ې䞠PE u'`ƥG+c5pSX6sBr kZ)]/as/!$ǽićunUBuBY@z<)Y9fpu(339sok` N-=cVLԯ,򴑰E$WUG~ºW?0SsQ)^YMM3QS~RQ`l9nk,M`Y>k[a 37j H洶8mq.<>@OtlZ)/g B~i Uk&:`(J {a#f:s{n_ ʩ: $g R΃~q\%=\:g䤻a Kzْ;atoRDR=h[ۗ^a|SbŹY!T H^Grc0;9nCqOb M9eHS]:A_Y@:QV|[ Z<="bС)јm@qhsWw~H(h!7̠ԏN$a5l#'|0G4ozVs$Ecy-ryߔH=[\dAx8UDdܳq'}6%({Y ̱ff\pHT;酞5jopT^rcëZ\xUʋSXufmHA? '(ϘmsɎ9\<9v<*$9#gt-XQ].+3CEJwTQCS-^[.掘G8od;fvR,ٜ9U"3I| vݻOP#s*i0GOͳ&js#I:'Ji!?a ~ڀb|Yc֩ 7dI8 $%(V5ΣlRuf7y`ޡ3̨< db?~N$ ^4$?z&W M *n{<&Ɵ78p=JuYĒ1iv#ÉĴ⿨: u q,€aWM)Wx''@dijV$6wPKFbZl08k%~- e[Svsо[PBK+)|^BNm+*MlpHGrAjmWqe4&Z]dKd1Ic KO/Fx@%BFԯn;\:H]ŻC'z]$akcXȮ\/-!N0ʪ|: ͅ_W)p^5ͺ.kfMM%(]LfFoHB}_v LJ;q Kѣ9vW ։+\V:k.1Z%iL*50fMr1g ;J.ʈ֭ !:SE;֘gNDF]NU,Twb Is㈧3M֨cFzCwloу[?vҊ%$oXe}0RGOWB~qXAq]*L"Msh&UԢV]_]Ckku%߯a=\DIh'g6}P>,gHmqGNS|v%mdz0YLp_:YqÐk+:loXr昀9P 9O*ad%zx`EB34$ Vdb'entSqh&jE{HĻb+ l/RJW EE9\ʱajkC@n:]j0?}[xwrG-JחՅ 90 ü5/k͕TeX7hSGfFq`[k|LF_dpM͜1i`z+mFjJE>9X҂}U=QAaJ#wE؈1C&yBYږ2v:w~e)C7P֥{@39QJ)kU7!&-~Q)Yh uks͔e˜paQ&=XF{2lr'٩~kF0DjuL~($;NdsNJ|JcYL:˥넂g!B9m4.ځkπIJp,~8沔:Bg\TF/s%aFЯjB8 c|4LO K<فעx02b\^F3=Lb?R,45Pa~ϞStYYN3eWܣb|S/0lgV0;# 0nܧ|:dBY; zJkhDBDy8VqqaVOFv])#0xmBF%5 |,ANqkuL,R?InmrqY`'gNjl$BΌt\0&f7Rkׯt29%ՍӇْ;k9L-eU:VxC% &N7FA~IGA|k.qᙯ< Ғ''mӮEϦ!gE.mVJo6r:L"JiE]X1c,Ow Lw~A9щ{0V+黈;fST3R#dbeej6\)|`hr޹yS(IEݳ;@DGu,^OYq7VmT+վP|XpH R*6@7pٓm T )ݏ9j+ŐjB*(;䧼G7w^8G1Z2Y5/=y~0˷Ѷ&}|nDk) U]A1C/[Uqb+gpIXzw !˾\}v,NFS9NIkM] 7sr6:™!} *:ȡkG~$NK3~wITutj Fi"7CGO)Dc 7oi) cHShQ/h O Ԛ8S2ȭتʷSi4-\$c6Xh"u/xk 3L>8Od]k(WSF5N̒(F$`381k/┗\^4XЈdgdO^N9}7 Ӱ+\HK|ATל,T Qk8tWTI&TL>Pica­""N~+ ٲ!N^5+.B N`؟oP闧Hl%sO\7TcEE9|Tn_j3RVrk1ېvt"R**t)oQC1DZNhM^,#6Ƽ7 8d= QG-TVNuF1ђD/%x|@ -pJ2 T^6riܓ$||Cyڝe /El:d(H1tTK:r v`*㓒Z7Ld]2O{@?]WzXxa?9R"kHjb 8䷄0HyR7Ldsy,pm^ 6P÷6ZɃdi8P j"cHȈNe&3䭆A H4V WV[ʼ 8Т8Whϫ=Nss%j wt#BXrU4E:-# #o BQkax>ǝRF.v|'&H(| ,5"WOmZBS&s*-լbv}nvV}eCU"vŵVc%mplA I6|g@lSX1QP'% Qx"nWMײckzV&(Hد=*:˞ EGA=U2ˡQi+ܱpFbVpk=KJ e?ȾE4j]KRJ*v2n|W6{ɷJ1mz,'¼I p$bϧ{x$9 /n)߯_DHh:Ck v q)(vO<81Y`fy{Xt^;?l7H9Ԃy u:/^i78w'"uu2T> d-e*;^b i,OEDy0h & _~X᥺C–YaVC,o=.[ʤ,^N!܏ߨ4ջ ӷ`Fʃ\/ ȥQ|yd#\:wEb\S6@ܑ Chp+Dog~%9EZR1LM,S]BkUutcְx$1a=O1`f&7Zs-+ (Cݮg4sigʚ;&W\,<-p3ShfQ k"byFY[jhɘ՟# ؗR.䐗t^>q_ONqyiߗaZH5.еi ͚dT Ȃ.3Lz)1l_pq_Μ1=>ZAvm6Y9 q_&QI֖nΐB6<lj+po.[(εUl4~Şvq@I 5Gj:i437;%5~c8R c "]*s)Dn f^]ecDEc iN0*2l+>d=Xi3b1G 2{K}O]M(5^ȗoN!bj>_]kNֻɖ#!z9:ĺ$Rݽt{N|XU #T7 *xQH{ey}RҡIQ0lv`\Ifއ9fURZW1jX,[Bdv{8x`g-3ǽrз|f XT0' D.ԕ G\F_N$VhX:U 81rͨL'k [cˁhL]ߞ Zi_v+MmN_JewȿPyix6yS;ŰdGȱߵĉ]udʱ^/MFoDqJ/%-U^:wsƳȫ1noW%)rF1ܶsS"qLtf`20w@J~;e1Dh B}5]ÔfƬBعR91k%\ `]tIc߷.{R!ĖnN׵d()h^IhDQQW'|lW hR̊[/Ňv'|l!;W6iG"[͆}B,Dph._[ɏ8((1R>T[!FI:-Qʈ_&שPk= ('2rC ǐv$-# kÁ#";d9Iܲ^At5$ռնZ *t: pi~y_19x`' Z HwN.>f}Aǐ3w:Cͱ@ < 3, +e{Z&C+_Tu㲔rIkr0BmR Mʩ\ y{-<30b?hB#ÑoKE}#qQ"qnDT4g M,/%)Sx4 5:"{@66Eri ˝BZ9,bCGH:n"dDDj ٦޸'.2c1ohOvnMޏf^dUAj<HFȐłzw_'Kmw/K$4>BؗS'.^6SM3ЃժKH'2">TMyNP j?<JduL}:P]8/6&x@AXS䖖e{aDceYJJWүZ{.u{eG*=c#~!g9ߜވFQH by]u@El_\{iՠ`~%13Hǰ }@)m)44G8`nG^8RݎbjegX2<9_QN=6nohOqGE.cBAdQX_eD+"fePQ:M}MNlK1>NmIјvP97Ԙ y;M㲾:M ^]Yh -5xkfD2V-qoXE!,/2+AF2NY/_ߊ*`̿U݀답^VWŅ5=|2 S~Qm\~5ttMKVQ$+BüXF;9P׶nywxż5če9{+j⛁a+m!s,nRZ <-4k(bs'vP˾$0)# ,c ٗ.=֛7хWa0TC 6Mynn"yZyŪ^T҉ ʳ5LVD(zAcmP09KY=rC} CT+QA^1+% %L[eahlHєS 쭯^ˮ̊>'{c8Жo2~902% WU\qFX$t`ۢakQ 5qS;lH$oȊxmEޫug#H|=* IU4EB(UVt{ BdDG)S9è+Jh[{zDHoSJ*۴r/IX?z]DJ&g@J -|eb+@ OEwyX*, -<)-< :ߥo{ fG&5ՕHn)];q㨫E{VkvǛ!]d:LRBőe cqoy AN@j=|tXO)1Q~ז=a|q7&j0[Qso9n %9 DS5_!~c0Șnxcۼ 2],[Ϡ{<`mwM'[]~ZsB?=|e [쳽}>:EOQpzRALj#fOn$=E: @auCO ;다{-U^=Z{N&%!ǵe4q8Ÿ߮ɢIj^; J|A `rY(idN #Ih\j;AS^wE;ZTM;оBgɰS\)Teg+PLKͫ׬(/mԾxuUS2 ٠DHf <~Q0NYg,ZRtp 05o.fYO2=^]`X:'̗la8b[1 %z#i [UG*ύe RCko`V&(2KɔF3gE5qR'E9037lߐ9tdwXuo)m]EЮZ8"SKS\pK@!l3X4/u_SZ0&LxO4\CC؂8؄cl E/ܢ% *0o}Ra]J x2|k:N]k<ɛj"xeFe kr)Q'=~h7J o۰??x^'@#wuW3 Ԥn_|uW8LY*P} Ym21E0bU6P쌒g_f+iŁQ3RqEK6,ʥN=}j@Qv$!!abHuu WGIXUy[-xI(K: HWWw͂‰eRV;6]SNf;2Q8@q؋ѨDL,2M~>.5< Z#=bh{ohP*jR>1ܓkn sHP5lYΨYΎ iM$$^qF͵ϔ}>h! m \у&=] eÿ9g/< 5 )_A>lΰ WЛ6vg|Γtm#zOg$)H68$LmAUO#Ubl`2e6qEd2بFڟ=;͝IE.QEDA|dfTq,%4T:9 T&V9o6 @kͧW*SJX]޸ ٕkH c>6JK*SW7g؈Wqz@0ۋ ]≖N;eJ 'd(t'X8}sj ].u9B.>xY~Ur| G4g0M{P\qRs(ѐdOrlM7|MhT\d.3m>RVxAf}dLLZ7mN)_mUhAg gZrj)8Xd0nb5V>yIHWʚN RRZ_L\A7s_ʴjIҕx Ntr]{͵A&.Μ^ZYAuAtk2P\M?H=8-E3<~/);> !^9/+Wq2οfh٢0Ͼ :Xe+Cy4#x4P@B]ċPV#LZ%ѓ8Z̬ŊAwnjB~] ZA韗?ZC3vca b4e:e?A߆}Ɣt]/0 g0eS`*)2QOwqjJ-KSUXԱ#'ӫ1a^OV@i~AT-JT̈XBt+[b9_YЪ ye^aጵ{mHu<# AJ7[ުly~oENJhXI.Go DI7ʻ:l:LKJ 4#=k3>;83.y>4SKؿhH<=!32n z3+*.jC;/jʃ Utuw8yJ"&?p(1~NPZO6) 8l_h5M*&/S?\]p}ΤVm̌ѾU"8 j0ޔ8V0>@(kWR-]<1%:R Nle?X aQ$JţY[dw6uR{96#[ rz-Y_}fP?Ȁ1+f6{~~.Ck[ƕ@ o,ʦ`4YնځGQA#+YUI'l|< um,'elؒ`, "# ZMn>[X{0,[,L,'xGԡJǚUBت981mّ5&|}Ϥ8%ux62}|adYRѮl!9|=or89z;]wp6)2̌ }`p!:#cT %A 9϶t07Rv,'ۏxr)Q*+͹+܈PW\FXlںT XD^ҿ\ q{9YRB*kh2]3;,)}%3Z%H7nRG/UݩcGE@39 ?MgyDVr3&*fԇ(8!2jm9A;DNA#0~nX0%̆贓6|+cIR6(8\^p}S7 1^?;Q8V|"|_Jx1C&N"Mt3*s NxJ3La#&kpTt oT2gdBa%\ٸ *sr[\?/DjrtH9B4CzxLߣV`#i: HF) P7nH+V߱!E ` K@k0)7z%,WpA /O=Ոmhh yD={z2W$7z%urD A4#H׿<@oqT{ CL1vdNO"/}=v$,Wx֕K^ љ7rФU(oKǓ(VM;uHOIX5h_\zDe4SDdcdG3ա,>ZZ"Td_8uخ>RO Ɔۣ#S4ϾiWGԑbl+S:+I%$a$=*ҥ>sx[bw6 i˔iZ2#9Tp t^[oO W~0, &"U67@+ ?ʏ,3_ ɱ+!lӠ:Z`hB ۚ]RVcZăe}+[?JJL̄=/Ok*F&j"GUYv ]QR^֣ =~^[0ӟ(=<[ j`*4e"L^Y4vg!V2Ҳ6gE.޴>LÛR"?9^^=Ƣ{2Wg9K$W5G_RO,ڋRqzEb˰,'׷M*ȬAs;u JkFq+itoe:8eA77Sji>GqQ.}pJ @'\ZISFz苚 G; t[hʪbctLK=w( ) ?T*4 bn=wKiճMJһ3_K̐MЃ&Mօ%~Gqrx$P N1<1.gG0GQՏ1>F[hmYe>LQeFGɄ xڱcm\1-u^ 8È&?5gЀBoevZ'dS1lA;Ȣa|Nt `llvyFX0Wtx){#Y[$ԭM^A (nm2P(;o~_ OE?7+b:1 \#ʭ(a9ZiP,.9 rjl֤0_tEK-ƧQ x5? Hp4'M$p@R+' Ɓ=ze% $)u_N#c _|%e/:-?sI $ $˴aQYDn zF{"\׬WoYeq WŽzZ ʞpZ(Qvn lm TL?N{CU=pVgNF8L pE┡[Co,dd^ەIƆ+Djd*Ƚ-TYIR8f *#Hb|c(ᙕ=kʱ^d'GCP2"A;KI4AFjb=%jQ.Rn3 m=bt{V*.'E#I#=VҷRfI-Ia?l|reW1ثp|GN݉uV7ާw ]lq*USs9ouΈ@7"YG 2{O/5z s`2-~> a0 u8zR= nzE/ilOB[#xk'< A fǁpޜLyI/i v-)KXNX@W;NK4$B^|<=ecӿcxbfU~ňCpSMg蟇: _vf=-pgsz)9ΝP9 FP?@nG #1FE,&8B#ci.ڟo!MHц2?Lmp~n ~KC9a9+ºNq$X-8ߢ8 A޼O  `8)P%&k jIa9!%2$9[O(& ʰXzgU/yYЧ^^A+1Uފ \DS-V@PFGQqbv|` …Y2PKodꚅTrZW)]RW$aSGHzvMww{t \?"D 4r)*6_xA( 1?ig!PY7,dK Ֆaɍڹ16$[8:lh-+xN W7 ?pAipfK8l(\b6S_b}^&"x gEu.>l%oo&c[Y^0]cIFъ`q;SM܇MC;EL%U Jx+m@ 49NqA;N4 ŏt䍜{i^duD( $Aq endstream endobj 187 0 obj << /Type /FontDescriptor /FontName /EBIQZV+LMRoman12-Bold /Flags 4 /FontBBox [-476 -289 1577 1137] /Ascent 684 /CapHeight 684 /Descent -194 /ItalicAngle 0 /StemV 109 /XHeight 444 /CharSet (/A/C/D/E/F/H/I/L/M/P/R/S/T/U/a/b/c/d/e/endash/f/fi/five/four/g/h/hyphen/i/k/l/m/n/o/one/p/period/q/r/s/seven/six/t/three/two/u/v/w/x/y) /FontFile 186 0 R >> endobj 188 0 obj << /Length1 1817 /Length2 3671 /Length3 0 /Length 4804 /Filter /FlateDecode >> stream xڵUy8f -z a6K,Y313%D){wT ٳ'YJ~3:?\~?,mdQwЀ!0IS8&c z$A`bb$IzH ($R@wPSCqm]Ò@ȸ#0AIj.Dzb(22Nj`&L @ub p1H [ѷ OYHBmD/,6vҀ> lli߶ Sisrs}[m[GK}8$267lǩȀШhgw P*Ph@@ӏLH"n-K$o&8p?44Vc硣__m\bDMZ?@'*|| ZYk+lVBYJoT _MsN#X7S$NCuxbM2%L #(c-=|U9CIm,;.C#Ί{r <}E LF;M[ SVZl0x9夘LզKֽjy>E|*SJШU>tUTdgNkJ|| \w(rՙ*GPxﱭ nȊW/;tWI\:ӛu+=ˮS9F nNF~nRƧ1V'Vcw&0<@xN♈ѓ tJ5UhRlЩ04wǂdհseNH DB">\;_*ǡ?VE2c䀦T餌0ݎcRpZ{s?{Mˈ_6&Г"MfwZ>:tbg oIU9Sǎ_/M}iI81\>$s-"/V`ጨ^tLv C *6%~\ɀG[LlEa ߚň5G$bKR/ݻ%n^5VuiKg8fJ"Or2L8-Ҏ0pnE.@?' w׋zc6`j'zw@ܿ}@FwSڭvPo{T$\ڟ0 5F'e?kmF\ұwL5]՛Siw0"Mk!]q5J I;Os*3ey= r՗/49l OSs 4ff$ڙՖ%::el_/Gw.1&Ș67,N"&teMv5>_PΨw 7SUR\-h0oddI}h@𿥐~[|bR.x N5Kֺ7檄ƕ,Ԍ.ji%IP#ҁ|{ rRiMw5#%G \I? qLnsA 'H)MrRSjɍ9z[یYG#Cc ryw$ μnRhzivqܦC͢kUVp{؏wp& 9ТËꙗmfK9Vt{i6\*i8~0N :Wi.6ˆ#S`zz~r%ݼ't\Dޤ *c)Si R?wz`fX;Kaޤs16)\Nɵ=6o҉҆еtޡy*Xy)d!wؙQbN +P?j ?W*{IYdѱSKLkQ#ukX*\ (ݙ)Nv tbYivxxK K(S;dG-!pASnZ/ˉ.^r siƻq{zGƒ.5߂s[7. jQĊ܉{ϭ3,M?ۯ*P6xk!]P O_Sjn7ԾzW.c)K J[;̝-5]S3_yt+kD\K!JܝЦe)o-bOYd!1 9$AYQ}Րp;|V]ep&⻡{|7t -.]q`Q25y9cM!F O{ћ &4t~nN٫K[MaN ۻMZ^ ${:ǵ_ vG_M'\[ 5V.j4Vp`gk,) > ؙn{q~ IplBLqגq.vyj1lp^eVU}ylnpYJb72q4gP^޷{#$m ʪ?Ұ=Pxa`;tR(JeA#;lJЭ+np>2fuoGklMyHYOnbr>=ESX,ǣ ȃ^W/w574ɟ 16EѣKWl_بlep9mseَ/v:ͷv&wCw!o,g6V [U-"!ӈqg<1{b=lnj!??l!a[2Byu-Wo/KXX`[3_S(Qq֩OإL6n,9ϯ-o~I(p[o=^vӝJ͇=^+ȹ.XWζK0rO- =~ X`UW{-lhmӈHw*$j;25Wtto+=f΂8ҜP:|'5P_uVqșOyؠv+-J_SDWJ ]b ~-4|Gjojb̓:8)͐E#Dԕ$!\O#oԛ%OȮEZiҙ?{p6KS+ _2x}ex+L%Oꋽ}7jss?zUxea3Tk@`㑄 MwaQ3븦;ƙeNΣt w#9k则#5.X ͒HU ,{[$"UYLe|A0~PG;}&1#&  KHŽw@*Ӑ&r0QSMne>f%݈KK?ɟlX?j8UY{TW)fg[n'phHjw|\@5 (txE9dO44;ћL&M9Imzy#[v+Z0wަMXc5ɱ+-,jyZCk.rD!5VWk:=u endstream endobj 189 0 obj << /Type /FontDescriptor /FontName /CSOXHL+LMMathItalic10-Regular /Flags 4 /FontBBox [-32 -250 1048 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 60 /XHeight 431 /CharSet (/a/b/e/i/m/period/r/s/t/x) /FontFile 188 0 R >> endobj 190 0 obj << /Length1 2829 /Length2 33678 /Length3 0 /Length 35225 /Filter /FlateDecode >> stream xڴst=ضjNcm44m4i& i~##Z URe17J۹002UmXT6FNVFffv 1' bP4q"y(R@;i 0]<,j 3 3ҀR<,-\~`c`]w(#@`dg ag(ػj{;1 `oPjU%TTR*J4ªNELUM] .&jU~Tځ@}@%DԴ$X~p:9[n_(AR͜mipqqebrwwg4wuvaw2gt3#)HN  ;I_N[$@Bip3m,ɕSRYڹL@.F.lo)տbNN{m=he6޾FcFv^i6stvqWE =&/ -) <;y{:v..D'". fAC*ag*fok b[>qKN.NL`[ٻy3K;Sڛ:0Y:'dBc3@GĂw巙$kiBv6r\\;!pL-M\@:.T3 b* 蜚xLfL .W/IW#[ ZxghVw5/T_.b;s h[1>R6?/ ;@cibmtv+ ".*B?qv&vVN'3hX98,6z3,&F;{P `f{C99L"MB&? $q Ŀ3Ib0IAIb0A ."(A .F .JA .jhA .Z(EAH#4.kagm Z_6 =wt5 b0U ef*oWNA-/"nif c< H?9@v$2dp *`z?VtN6$ >g?~#W?¿2kcϦ,,迤g)hy@[#1@6Tol6FӖ$)InW_knA3f  69P%/ӿZ5;ޟ.N@MKSл_!F.N:̠ ߿ş_٢ lVUZ:h|#_2<8A hdol9Op [PK&j5cWPP =#Ftׄ>v-Yb59oJfyP;_  >3\Q{2KG[q=UCdbFЗK& TOc ;LhDV<9$Op0ً:k{$IJ~hsp_ٵj-&ҺpD}55U+F(N׫&NfA 'N;*-}t/#5{)da=G-< Uz4ۥuc14}g MruQwkүY>!΢`x@SEJ7DrGʃr<jPD[%OWBX+Rf_?Ktבwpjcy0u 7lӱ\%/{T3\fv"+X޸jrߐlV!@V\%ۉ(.f)7a"7x%J! BA4uGB ǖ}#?d%n;+,bLkH#uT~'$R3^j4t^I1 ʐ:͖D04Y;""ԳЎEDS2?̴Bx67Wi1Sqopngfw ./QaCW ,dCisnܙ~BԝƳc{(2n]2qq.;Sž|]"v=eUNbq/G';Q,o먟i9 SDi䢦jvJOIIsуp몱"Šf(Q$iVc|l=s$ 9>ssmW7-R2BC|]3./_\jpBݣT"rpXXym}RR6ʓ Z͆ZOg"V$G@=ˢ{AӨ*]n-!c[lLUtΫ=usTf%a %R:8b"rV )=!tCqy>S>M4F߇o |MR-(hN:|EpkΡw|Iʾg]\ 6+̅1۽HTjJY.wxjSIfpזx /@$ԯM-w +x!7&2 l/ 0Zǧ_]^WEԙvkp135 z =a[Of"'-kҹvBܭF/]C]~>ۅ>G3$sdU[JTL0 pA{aA<:-&.yʅ׏Tg(1S%:H|9,_DϽD#" wo*\׍Υi*j =Ӱ~,S&6O y^_'GD ִY rT\۱|R#&(,xg)ʥU/5"We],Y}hJz}Y˵eӥ!kABL/;/8O5ibF:IF/ |ɧ"ՇϷr ',Lc+$$/86l-c6v^2#u,[, nP8^:~|oa2/+f" 7$m+!cǻ-:\ǹ$D >^lIƇt$gYo]! ?;[GfhF¼O#p ?@[<ąCb8K`tv]WډA)&>,ӌbR5'yVe,1؀+y=̇)v A^ Y^:# 49HGkbUbÕؗ!zB[!֯;yjLy`n1% $n%צ""B4<_At*nZUFy"# W52ql5fhyaNJIlՖb$˳#S2W&$gwe@ꞜM8_ >8&z`h9v/殣Dwm_ف1 B=ީ-9PkNQ'_k 8DG`7;V 1L§h [$ s9GY)30 iX'oY ׳?ME4^pBmFž|^FW4R6Vzd.4k ocGd%%/}AcPzbUʸp:e&׀an?W 4c@/݋CikƆ<;.R@9늒2%TlUYS1R 85nq.BԽ"~s=ڃNBL}5u>bq>}a]mA҇l|N;Wu"/c6h?⑪;ŨDN Pb"T0*H̐]ABU^&F 2Ç{V'~ {£'NJ6qpE9l[JC̾5%5( En>Cߚ7󧟑d ,$ l@3 i1H$gFz–R4-)Ac/&i="^m^Hib!on>Hsdl GWo#m))R)JYi)*I 1v|Oz-e ޜUELfl>W[|7Z|P&+_vwQԤwóf[X{W0iNtN^YX 捽]G N)Rw= e"#!JSoMVy!#ơbH(f2{G{խB$ws=-k+B c-]M]8vӬ#ߵ-~  iQo#" ?|HE`iB+K#Y=9?4/G-j{f kt6e}?^X1 2tK?k@j,qDg}fhh`Kx ymφW_Pu4?ňěMњq>1LTg[M̀yb>%nEQ.]?9] u$̋tȃ_t #dG/-nK6WghE 2i B#{ q0yKi e?u7zk"j)e6/d32xֻ+KXEIҸaTRԒ~e=ᖢO.Ǐ] Tryk20-|硄!-\}l[UJi8h)SCw}7tggᩬޯ6 )Y`Cl^R Bz4us{ȑ\ ^gWAxؐf&2<L>M7)U@([^Qi f\<0qn`" Rr g^Ҋ7Vjۂ 5%T6CVB v7PJ\l_РܭWv'`htI'+61BKxzSO0/4zh6-C9ezRh^f 8odZD;ch6UǧulTdT^ɉmLyϺ3 CSOw45wN*X`^FdL1Շ1٪)ඇcL "(-OlԅߺbV$q#O/p±_^)qLoE8WH^ BuLٓZ%o)g5 U maGƏU }𶸋 ÍA u$6irݜx{~~ya`&7j Ty-0uڀr#g ;a78 *ũ:%Τ7',BRگ(^V̱1w8~=NCDJ_pt%&#i45"5r/CQ|XDrnUJ>̞1@|Ӻ1XN6v3ſT\r@IB3c7~O*N k%0R҂zʜC~?qH,>V‡f57ksVXޞ%drV]Z3fE˕b.{lL}]D!5׳.n^xFQ#ڃh)moJHk6D/k8h Og, {wWOG/FAQK̽Χ [^*'re[Q e7#|41 7_^`P ~kJ%)>;:J2V.s$f=#nSZ@ ^/>0A=it&dxpPf} k}b!iV1"( $Z$#ZtU('Ͷ)>&}v&&YT) h\~EQ^@H]EyĺBfDSLH*TAmB\"$}oMqu &(8p0FRmܲMQ|eTC{ߓ17ଵxsĉXَ9>>ϱ8!һV]:|jlKgyP2;ݛۀsm/"j{A㡡g}h@gsEfK3r4\[U",;&x$tt]Sy2+gFM,5%qE-©1|U/j#dxM5RzCm퍘-?PL O򞛃*z}(e Xü>bTC}u's{W f;ߠ䲢a*DJor瓢-fioQj^lsZ#)ׇ]#21B7ZEkOb!CJ|k?).%B%b$_893t&_r ĸs`!h'G>^<3e!ِ-P!2X$諫VH:\slp7c~ۼ)qdX5|m*nag/$'= w\}p`鋬# m@KTGc6KHmKZ>-ic룞]Ťo)8RmTwL)C4t^]_h8C/ǔUr '6B]zU\rCqvK\:^к!\y"ySdLP0[ ,3fN9p$=kc*൴I0 t$'8%Fkj]{OAKx`) 5hjAWbjC(8A^U0ϕ~:Ys0sKnF20tz.+Hsl7YTTC74Z?),sor1T;U˽ȡ xYt$fИGQ]`F("T'Sr6 +x^1&aɸD)%hGR . ^s^7T&Jf[4hmގ+⯦,4֪BM%s,O5;~-(NLz,7jat+쭈ƐG#N g7Tjᆿ3GjB L+p~tS3O^Q1G`< 04?B9Hu X 283`R 5EB@oq I(mۡ"skK&Ke9H1wE4_Ү w  }E Y%x;B:YS=lq0.#}TCdHhۧN݂vz y`d(keQL;%"{ S/"(Ոf~¬SC2w1P~,u|*eq> 2$Ώ0-zǎ9_CF4EAo"u^i:aSd}QWZ OB=ķz;<(yQƢQ8Q';< xjdCOq džP'U16 #]]|PO_`>tm"b@YUGv4\籓 IvQD#G$٤7 ┴U(P7oߝ{СKݔqmf ௚~AE9cC͓˲r0C2*.*XlO]4Fk\  emX_H]ߺE]ݠ X[Bu/XH*ڱ?XF fJ9hnh%D*r; !6/u҆;ǭ}5S+ܲ\N\؆gauXJ%FfJJs2'm3&l 9f'ҰÄ?mc}JU\5Ky㖣TIg X pې2fY^?WONZ<);9S>p;5D~^~C9|s]\5/@3 nb^ؠ]ȰgKvI΁UO%WUJX$T cB"4zgX{#֐R6=e;e}ȻlhЫHPz< wۊ&;%ȥ5y2 ڍQ{.yyt}S~A͒_׼oVz$ un',J{T0%Wkpr"ZneM63gXHpfa*8ZJm;RX`Cɤ\~:cnlĺoq)&H&`{[;9 J|h<-դnk 6t _-e"?F^,6Ξ#fX(xUJ^B6zh :KaVI%d? #N-Q{Қx `|5ko phݷW!$L•|t͈N-J<`}9釨ne/&{w6?Rl#Sx(dqU Lmw9 ^:c~Ÿ3nyj[Tj[NOiͯJ_=Q+:s*?> #_ {Mƹcos4(.fōɎ??;)mt-̧Ox[嫅)pBbGm)M{Q]\7lL"ꠌ}b)] fL@KWp3-dZUU!sUէ+ʰ [:>J(`'_ۚ&AHhg 27]l Qy:(h,}; `=W İd+PvB4 Lj)~O\ʆVބw;.1~Bm_*?|g'mpPH8W$ `ɚhS{Rɑۗ4,m NRzWp\\RT _}'r'ŜHD64\7}o+f8wDiCod'8.N"8%RՒTnSbR/AecBzKU=f_W'.}c IY^0" #Ӈٹ#\ÊANXW0p±.,=8S=3,Cc&#zLEfan>B#s >;m/xwdF츉9hߑ&Qg]~UxIo;O &;G }ϖP+k=^iT|\rİ"$C%㋥YQDj_bi%I(zY~S$puٵM7?T9]@`CnĻ9U5=56xa  >~7vi+HA߱y..)Yku.:iÛ*=Ve]5>_/Fm}o]p7WY~$8S=PˏT:ltz;/zfC(ΐ;]gt[8S) [Zr҉{|s2ZMe%w(4YTFJsOtsq`c6Ute: "ޜp3 hҋl }Cm;*udñ%OstvƝlA9vTqSSN\C 4@ꔾJ=i24j2Y7UASr iQq14eKiMaPa:Mлy2ߎ* i0vf屍J z"-%UILIe 2r@#I/ux GR%;ʘic?I_/3*b-d2Gbj4ap: }~uTHPb nko OulڙD\IEۜ/ /F$~(Bά xZI \K"R^ |MgfC`(OhsJt< vb<+gWƟ$OCO*u:YVZZ0R&[`Cн͟97 p { *Z SH>S|+@F>CvB@P`V¤ޯ-ۼ~E[=LHQ9 xÅ)Nlc]գ{!=X77S+V!W,`G}ؗMQOO^#BzmZDqh5\*Yx:sfCTM<2p{$nCٷwʰM6Mߴ\f0R[8d>8)P/9M *dCt~ *&[n_nWɐRGM/p8?PCg^nzJT"oѽvA.kuWAL::L ɹ\x:lA'Xƽ1!R8g]dіdajvZұ|Ȇܟ88<d&΂>mVb}FMjDddϡk'j/s5bawaz=@o:aNPj+A mi:28VrP+K|w*VUx=8 >ίP"FcUп 5*1'q;P|uxۢ`$J !iςqBԕzQyF=:W3w.Bsѿ߽,싂ǚþF)Op ^iZ}/1^ОjQ*!V~؉g?b؍I<ș0( tya5dƨ 穾c1#{_2tݽng|T_کg?_8LeV0)G8+Ch Bf}9>ZQY?krܳznh5# u8{oIVc\ -$ϗsEO qڞB`ɡݬXӂͣύYS ;i`g=vE+v8ZɓrlL:URI}"X>'Ku{(@.} *(x#^r2]V3^XԠq\NPW]W<{&tJq*exJR5z.Y)׵3YmZ;L &ئ(tJֻlgT fLBS @i_r. }x,%5Xi _F=<U2n8*,{/KLJ6,[c2lU TR >~*y(\<:jJgõ9iZ ծܴQlބ*/O G\կ홉5Cd*G:1Ir w6}_ kzYK-TtVSqjjDzFW:$hoI\n`1ݤϔOWeUdyoP3TX3N+ӊWD7jT'p_%Z0å8t3"PjG1Vӄ9K'~x#HcJ]-:4 ~}u 0Wkw.#paLcRUvP_MmFVgʟv8 @ > tIgEƏ[۲WbUxPl5%TWo֔-T;9E`\@ֿ(|;sAMl41]y>^J(:~Iq h8:mTb4qc*&n🵺Ŋ#] }aZy_C{gO+) ~mM3`cS:e\ )1Ƅֲqݖ5G.580niD(.C"~B)`|FRu#ͱb0u~X@3s9'\Dޕ!£QTpQNR[ïw $Ff5b./Un@V+otOs_VxVgEVt:z=7o8* QaDp8Lcw'5yG8iNFo_3BR1.l>*(E%x)0Bq(;?5w  R~K"+3=j oxԨހ}H&-إT߯cdzH,W APV +#H{u =ihp بŋ"0E0Ն[l΄qc뛎ښ5~zx7x#ś5͒A8#qB0-e' i Mha<=㠀DcKnzVo ŭ%-ڙ1=#zײrf%Pn-Ҡb*'1ȬÌuY4!=eL}hft9)ÁfS{#-| "sP/0 mf s2YĨ ex:t $n^?eSvdirkɦi]ަӤm2F]x]c !o,4u?qy˖s.j_nL t\/m@K5X/=a:Io5$CͶkp`= D""( }wScb^X<:`eFbI 6} Δ;mL@(A+˜O8ݧ=Ng >|̂BRn q/Ko\o$dod1 QV[CSwe-K-iΗw>r 8*j@XsE;7V++?9C3H0jT`Õ+8tv"u*B&QB1E<30۴Q}H؄\4LJX` QMyph gEj>A[-:O+ lm;\~\8On,JKQEOQxݾY (ʘd)@fNՌq㙺]tT6EN_=jLK{);kL_1 ({G'Z@r!t9u*wmfCJHQctuMߠ-_OzX8! F\Jtp3>oRR&b""嗊MV+(hCN),& )Rs7zPN`=x^+!z@zT$ލ0Qec<1gFpFBNW2ex$ fQ^ҽJ >Je*|+;zrΉ ͖. IȪ`ns|$!WB24+{Mǻ&ĐYL^46@ @zvkKJOJ]._7Gӧ{%]&ͮ|J5RmJʯpU|J3޸jPrbQPEjڬ]IJyN̙=p8rR`ldqG-N"T5~HcOgt>2W~8-.FJ BY!u~'O#y<$6 `=ʾ۸37nSd\6`g1W<e,sCU(-l\U-̊ ˕yen"y5YCYxҩ-44qjӳaHtGigU/J?[ٟE6z>;bҦ]f-7snK],eV&2H0h GrU[=peۂI?h6&דTHA. L MhLs'HOgد $Isu޾.J >AЫע>G| ͷN FB+DARŏҔ~f`R0_}Y!#)/Uhދ-Jq( c|ܩ% %ð}cZ, ,\:svPB +|XųB"뀞!FYjFW dO1lG H GUGʷ荟Uf0DwM]p D+EW,f$:}. !+A%d_6Ge0rvm=lJ6(2Q6%:'2Z+JMPxx*#%araβW Sʯ$>(;q;;vid6@#xקr \I , N Y,0px'W iR{iөxϾoGY؝+j :H~yt@"*fuj"H^.pIţHSu)1xJ!Zz}Àx9ά?P WdpEevFJ$+'7EmQeH aA T8GT\[]I5/˶a:FA"<x/f5&c+Qm]ƪd[*pn,}mLZז姜ev-ap^H^m5p gf̍p;d40(.1W2}j!"+x\&>Ycq0]=q9wgrtJхdw@rkZBy]?&|8?kEOd WwI8aR8$7VktN% < >索\;"9p dum.}(%Wf3aO<3Y]A|r/Q {[tj+AN ?IĨ:'?J8Fiܤl-W~".qy#֡}({{ֳz-^6rgTSπ qDj 2 +MxBJHg))Yg`8)G-f^1^tdr(W)D;-R+7 ʽ) ,"2]w;?eUqo[Ij^uE"5I_)"˪ކqSxm*!ō̳s3sk"R$]"/'/RULQCŀd$+#\@3GFz=67gSm= hEet~6Ti:\ïʩZ{$8Nj9s6\J Ctt/0]L63T+%:/y>a~ R!zfӛJ:s\eT`T _sj/VڕI\]X1Ioxj iſ[}~kAcC)#vgta"o(~A^~o+8ʵwjXTl0?h~j5d$S3LcQ̷z VG"{#Ώh ;uW8`=lzoD6c AkCD0,[8RXZvJftP׹EpYE+֕ၟ3"a3kwVqA/\֎>V䩒^gpGEXH'KyI*_y/ PQ8!=`RعgA"m-Ac9X S8{՝Y _IJ+Xk/\+$|[ >5JDDRSi9*]Y7-[aZҚRW5tɌmb=$Gl:]<*+G"gb2eX#IRSSmA+U#nr)L_C) Rt'8c |,xLaQݴ_4ٻNUMړl'm%@Fi'剠pctE|KL pT|g;=8ګd)&v~#`~z~%M* zc;֞?Ð%Va{%hT]т2F Am^Y|Na|_S lC"cGt+Dj?q m{gB|{$WC^UG!o@a@2 `O[ "+ժ dP;e t7S?/@(a_$Jw(!8>EQr'ޟaG;"J2h4fbe $X#ziWO!4-mJDw_-,75|{];~ms언Uйͭivv]_%2;"pNr_ =qf(M%2:'h@ED`z{m"A!f8>T4쭆}2ʲ{f#"]T-ݚ0"$1@Vp˃c2"GZL'S aCqB)!evrYn9Il4|MRVUc[Csg*LBJi {+}JE;Oa7Q!gʣY}y/7d/Ivߧsۘ7wyfi[vBcSZxutlƬdPc ONoRomȌ牰ߡHŇ h=di5FN7>XR6|\X>-&'?J9*\7DG|R?0ә,-t (e`,`2u ?!JG3bR)8LK⎕[шPs1\zL/YiVT _b~>lz>="G^ r^Nn_b%U֖g.tMZeѲPbgv[c(_4>mS}`_|y׉S~v%Q "apY&IܚU;q&wx UsP&u} @j2N#nv(tcuN̋w|Ï!wDyBƴ$OfdOp]z)|G:﫝* Z@(m<ǵ!e ,o(lRl5DeQ#o?B9 S Пќm݀3TvQO%Ʊs{Q&^ڣaG/x4SI,_i9YKr)?~dSrd"s`Ae;m7;#u"Lj}@>%w 87`gB\o>^l <{=PȀݸ8Y@,aYƮO:zij` ,0L{K!eޠlO!_Ò:5pJ"ej|B**;?po$Tp? 5$}OUGL[/w| FA~뗉 &{Mſ^Tǝy]j<( ,cɰ&@HwF-~|vQjnu`,HGsku?`d7؈L `P/+h&pG8G$>{]RǓ(d$t{a$6 `-ቲ5(Wc.(Jo tm%Y7Ed{)RDnڗPP4ԈcC~??lԼs;SJnbwU@*1;BomVzg6}#!vV_J$u l('A/ln]4Z)heATYt6DD[Lf:g$]lx Q"CPRpIIgە'o[Ff~fSHTnnWCͪIgIi˜ߟ*sY1**8]-%Owst1ѱ"*ǩ/@m8I|]r PUT?A\cq3AIz ^Cb`GH.ѬpD!o2E^|\:OGj lI&+^":!qZ>XxWvnNgdMa(щƎ 3 q%Qm =GdbI6@ݏUmѧΆ ǓgЎ jOc&&+BI)k{=%HuV }uB/mKҏVpVKO9Q3F fHs[!Tt:ҁXD"HҧJDrp:=3 Ж+}'=WKႴ`/5kv}w0dx]f ,^"ʽ5/vRVKЯ!.Bn!s+N=s?5x{\^&2sT3O3=LcZH)7v A:&'%,Qĭ&CK}R5"f~to_)pMz;yHًᾙ"; sJu]i$Yer>C!Jn({08>_g9?3vQ ]lSm@o2Gc,BRZJnne5*%Yez, ruQ'ߕ({gJ9Þcb0-zcq "/=+[$c3q*}:dW49CS`VꑑTkC>QN+qa_W3 #t$䎎{~>P+fcIN@[,*[cPLC 6'绰%t 'c~Cu^2Q2ͷ]0,:59]czz0}cpBG7=-qVFF s""K="J_u.|"D%uu:knk$X Ȍ^îqG꥿S0PRMKS ܾ|=g #&gb!q jލP\0,3QH awLd< uM'\n6^yY=<5={jC;/jʃ Ut{)/8boŚ@}DFT7~-?,qxF}=2m9˜M)Sӛgh= %sC5vz JWn$b Yr"}A!Yζxebmm.mQAfŲQ]a5aƥ]ZӔҡޫ_ %]$=: C}jUzDk?(6\QX)scۈg̬`n;$d*ӨUx2W\\cn LĀL 6A_`}aR3&y[]q( L!$Ӯ^@y.IT{o^BXinAL碔f:?+2?'fC4w a#hU~:Vge66o0k5[%v".VC"g(t`iD$iS]_^Y Z~44:sa/O#Yq>fF]!*"%YڿJk-LpvŒ.VCa|91Yhl4J-,{1G28w1e aO[=̈6Zq0r@`:ur׀7aYb4l%1t+Ҥxx@dws8<>͑ 8MLܪ0m+#S7-b+PÐ(3솟aG*c+~AF;KuAD U`4 7ŗYsc\'=@`vI qUIpQ-=nh揝X&fO(E :BϷK94i!Y}ˡvQ.A`hnmܽ 0 mfV*atʚ gt +F3cٗb~ eܘZFxOk'0Cd uC__) cλ& yYx8ŖBv4cT,&"r6'PL5% #"^oި:%(o tip:mh5Ρ͉{_ȟt~-ۏ8'<Ґt!~'^#:lHIxYB'UMwW}!gmiOɹI9Kjۨ/+ ˸jRj44'wht$BS1hqC3Ax2dj)#FQ)!JI-ԏ. $*,,9v+XŻQՓK1C 㽦 aL: ':!%$@Z͢u  o(+<Uv7R,Mq%/ Na;C@oT]r:"2Ne%9_ "|-Q71VDy˷Fҧ'j]~h*10ۍrC GYh`-2Vu[`IF;?IJXȆ{wdC`믝QG[4,\_6;{+b{ M^&Ho c:0`p {<.L^4M~uo)D"P8x"_MҞT1ƕzm E_]?,"cZ臜r&8KRawOb/?&nE 8 L\\/Xg^؄&c(=pi5)V['6d9n^8ZI8_r?ҀCÙp}f jsbއ$vؕI(Ȕ]4CbAeKzΒ:"s?_g v-CLS7lp^k=X&\Jl|&ƞPgPu.1Yi}C2~a{:5bX5[p\r,f ւ 24G(<5d 4[ ~H>+ $c}cɳ%,tL"6n{9F"HI> X)nJ mO0d[4?vAdB§V4/hbضvs ia⭎K^h ɹ&x^صd5 oČfyWN')\޻ԽfGy i]۱p<“ё טP&pm FR?xro8#7K$O Ŵ&1iNs{Ktv2EGuU_**on-U~`byN%%"v# ?6,GM)Dۄ؈C^*\6K3sw48a o]?:جSϒC~G/ACe.$vد_Ӆ3n&M?xU2v-6KJɺŸ ]Waiz܃r<5etPh=ëˆ)ۋ:o9''dF':+CA4e`E;*>kr=ܽ9!bKN#ܑy95_%}7:/to<]Ŭ/Z74i~CUf1$YlaDvSDUfԲP@&boϕA D`}‹IEV ܃H(X ] %qV^YHl?Z* 9ű#k T.#f("}s**sxԑ*'XC;{jk&K7M<>xƶ 6;d=rfAY$m*sBx#ZfPmQvo(č!([ wc@J& }'3fmu Wc571Zr@H UQ}@ٳK4! r)ԃOiܪ5pSn}v։mZi͚`l7H9E'lGv+c!3Cp6Cw@ wi.j׌i9?_}tS N3Z)t߉|[ SyHѩQ(6\.JCs$/쿴jJjovbR9n($~CVzMb+̥Wry_ZSΛ]aඋbuӦa-"B 2[Q&XiٹGۆQF7a؍·׀ߊxMf2:9Doc 􇄶E h㌮(q%]%lͧd vޡp%uIcqTVޜ"OVftQ :o&c\C!sh/Jtx|zEiᙂ#443R >4+v15_On.3C s"Cбc){ָj ,L` ?NZ}՟/?WT'*6'Pvn}E8,/[L*哅t (W^(,( f7 ߨO`, BBq)c ^V݌JKGY9ņ8#BRY[닗;qcq֠O;OagUĘʫ?JJwZ,ɖBN.+Q"==AYsDjV$f|8f܁1Gc!eD%*ؔ7gKIgJFA_/ߛsp" M7֯1b;<( : H!D{¾'|Dc$ qe~TcLT{ύc\0cϿp((s _3w(b!u+ANkkʧ_Z>x?k{,|,N@THSsX1DS..!\ZB@8߲$ $d1 ~ɩ52ffs{յ_A"Pp&/=QgX< @c`- @$w)-QKV*KzFnyC"X_SbɎl6]}V,Rr8v87 w=#IdRr%54fi]35GE*EӁ.k]7R* udq% 4”m[rS ·neM%~w?1s2AW4q!S,O#u3%z ى9fSvT\Q mY"P 8 jIh,ŝaԝ<52C44 ~7HirI(t>$nS`Nj|}!% _x8YfVUZ7hd&@` W-HB0vqkbٯ 4_ & XG դ endstream endobj 191 0 obj << /Type /FontDescriptor /FontName /REUDSA+LMRoman10-Regular /Flags 4 /FontBBox [-430 -290 1417 1127] /Ascent 689 /CapHeight 689 /Descent -194 /ItalicAngle 0 /StemV 69 /XHeight 431 /CharSet (/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/a/b/c/colon/comma/d/e/eight/equal/exclam/f/ff/ffi/fi/five/four/g/h/hyphen/i/j/k/l/m/n/nine/o/one/p/parenleft/parenright/period/plus/q/question/quotedbl/quoteright/r/s/semicolon/seven/six/slash/t/three/two/u/v/w/x/y/z/zero) /FontFile 190 0 R >> endobj 192 0 obj << /Length1 1973 /Length2 14678 /Length3 0 /Length 15923 /Filter /FlateDecode >> stream xڵxsxv$ Q'Fc۶db۶m۶4il&_yfgZb|Y*>Ck}5-;@RJRϊJA@OMKCL,`s0sXL2vzZZ6b h4z6@:_@ށJ_C 26X۸ڙ8@E'o~j)@ N-E vȬ@= #@ PRW(*SVp. J"A>iE!P Uhߘ "͟{v7fSp5+ٙށΘ/~&gk;sh0Vt0ϡ$M V?N+-?J!wB8i94&zJJ,LVzVzݿdO!G;?9We4K4-=Qmk+{S{#F?_2)>i1a!E*ɏƳ_ JXitlڏ&2`m|urs?}mnelFVF*ohCdej̿e@-h IW`ga45~\`;G?`X1,0E2-`icJ ,\@#ik 3gKBZH%o;=KS /dvz36uʚ:]ٿ2i(+AV=>Z ho`K(8?4Oe&de`mhje gb~4= .u ` 0sL?G!V:ܿ#F_@/DGK 1h4M?ژa!3`i7_*?"2h?n#?G^CDa"`gA1gt^vqbdbPѳ}4ڢg_w_3с,<4Y^60Kn *ʟ*DF}Zέ* 6Ձ)C)mI')eJ*R%@xhM5"7L.SE@~$V8ۆP;`~xGN",k[\8G׌jg첄Չ4 ǷL1>&iۍ 45W:!*lf8zId۵س^`C%$*ҭo΁6&3;-wNωv3PXFmPBQ#=C^>rV,S 1'Oc'zx7Vծ݃z\z >/@ .䎓|Cx'E{}c,^q֨\l Z⵪ĉ&Et G*+oDŽeiBq g .γ%P71/.d%A$Onͷ/w--O}䡳Xus6r!jIڳt\ҷt€DDKNb˰SIT:$n;wRDF_raѳQ ]ʒx*b"5DG MrlQqmnDHH$~tPv}-h ~eiԅE"$fSfa$jy_4ޅX ( k3@%H qI^IHš ٭᠍ sET} pB5ik46=f*49 &䨀RIi\0e©clw et|VX7^DRgH|5#݅VOH,lɺ+դZo^>gs3L4BUʐ FjgFXA_83'bJbiAC@Xͺz.y%B )V Y nr|B6F#f&<^7$%lU(:2!KcB֜m6sz(r4~┽RvLbSuNdBήb#(rO?WJnyS>PmIčA5Tbt?kT{D_9L oxN,me TU & #s8mJ;T=If;bE؎(kef_@oPlf(JnQMвX.:ydO}%'ħCIǛ轜JF$:}?c?Gd~Z>K٤vɑ|"EwrB.ǢxtJ:?ݏnfd󆅈35ՒQiΓ8k8@ tpL"OPgJv%͒Iwwv I].v7 o-M{͚䊼3Vhǚ֯s`NGeZ'IBCӎ\2Y9 |whθ ICAzq8Y]# 7jI(RzmuܥʉN`9J**m#K|g@:oHcWjoORx8d2̈́;a70 +s©ckp)Vdd$Z4z]6 V30J|Jwդ!)ԃl-BF"ȬWfMpJg"GEj>;T8RdNxh#Y' [qG"ƴOT\I̗ 'grl4y@7R<\!4&:`d0KjB$&鴇{`7@Krc>[ǰ>b-N%g ?rV,`"'Zpr__Tq ;VCaw@98ztaG&D=` }ġ:P[81ǬQtboJ&! uP=g "a$_3{+5%D[_5:z%:LydI$GtIY" -%`~V$vD٩-7,]HoM f>P%rÁb%u|LsxxWMrL^Պ˂ez5'R40b"GAzbou2( {^R2NԡUk{+SM"{$%UvC:4A,Tu??p齩!G`[Zڛ4.Wת?b^@kH6F~4ᒛ{- Pˎ81kYIz偡HA{A Xm2զYPǹ)ݺ^Bs<C5l#~%8ס;,r!螣$ċN̩;f}͐\'"<co6FA>$-(mH&/ō^zf#|wj~`Zn5WPP ?Ѐ.z(+V%u؇ Bn ?LBi{ޝufNo%aTX×7葝&϶$2^[A6\Rx+wg3[/.;$Vˍ4䦗=_b g)]!mW.|E5{%<#ױ'!5ǃtGZ~Y R}/@EԎ&9v8]Dk} q{@k\& CWAF`ͽ;+rQyUY5rbqUedr&΂ #ů= (6^!+_f)*U3/ƺfkL~x6|Yo7^첶Ns ywnDmT(e]E'1‰*"c 1\#IQ@'ͅ,A[Zq@xocW KkG( ymZAG`OKL-7*y6NtSo0<;ÁcJ#^lu T$#\D#Ϥb_Zjf B8ވ&we8=`Z*ĔOS^8Ԁ+b1 ~zg:I*x%0O_׍[Ci?5w7ON9|6)u>}> ho6hXFݼ;x0>5OSlՌj.Yp| Jdi&+T;CKimwt{3|ZOY =`֏ߎ.jWq@6ਬJfzN-չYHje`C{?푏\ǂv-[stQ2It-H|Ww/kO1x2n0P{zDV _RӗIR,NmR7AYR}o?'#ZͶW4jztVCUeD-zO -P\ӵXM 4wNI*;ʩm'1MOn9k_mW$T~{ɘR^1H_« udr\(n"œ~U{daկ@]2%FdxZUӧPZX n8*Sl{]YLMjcjA'W(uT RL#7?x*.}3Ȫ%!i|fYqshӿ/p~W?)Ѫ}}ן7 b%>ș 1k#I &9,pcVM\}@bw]o0Rku`D_%~d&#G$Q3E u2IMÕQ1`m(E[Dưfqgx zve!ϘP1Ӌ'8'%udO-ʧzki]9H I=ZX@ȚRc=2M?%qpNff0~]q ET{'Z-* w.d IX5ǬֹKhHI/3Fg^fxS'v~I+^͎Bvk3+p"56{u,a]l|k0_Gђ3OcpWQi55Ośf޲ Q rx,f#~ȬVRH*G  ŲHz4^^oe@jX5č{K+ u*7(E7d =S*d{aWB&6)0U64E^F pD MET4 ~6C4WBoyb'и?Xhq2ҵ陨j|lȯk0pd$hһq--r+;'5gBs'%G>nO ZcDFG.Q&- ZWX1.~J7=wVs`?{Y|y^Ӟg۰WDF`FhLNw|Oʝ mr>@hjV ֪fhy+SÝIHh V-O8'uf|{9T璎Ѵ[)v);%SP{Z"%-}5fBƍQQ~e9+S<6I6J>m|fvP5EW_g$X.WFox":poLi.8 ive="X/ΧZl%~S Oy_6p ҈];4nBl;J,4+MٯS*FF};N^F*rklo.3Ep5M?0[pK0zwD%Uj 3 8A4 a|qn! A&Ytl|&Wx^S${@ȥasx}du+DTJF!>PyB)6=ZwK*nE( ~D iTEW]בZowZe%rz~*j12KcCYYGKL#[MUPFbwQ5[/lG^H%^XW"H[il\qmu( jv89U1,k$%(&\a4]./7O:$/ҊCajC,u,1\hm9ɮ2'k3m<&|"u$=O"ХќLq F!4aNDIºjZ؇n^/BbT0z=5A|k#; $W1Kyl-6_I( pj{c9M)D(n6W qm0ꗥlm3k#R7)=&$"w e)Z r+([6۸D+aߜEso~mGq 9i24<:]Byֵe!аvP=3ҳa擐Kۛcx乓}U@TJrߑ# 0øJ'ᗠ[ qp~Lxܿ$3n=!k޶>e'z;NH*$V YOm9mgȭ~LuzP? |wcZJjU3| @1 } :, $nBiOލNʫ =BayH;`Faj@rڀ;0ϫgh[cLD 1p@sE /kN;4T̙imTU[I;+[1bjmEUQD;ĢBzR8\p6}IbpyiC;ұ gդed./WO!D7&:.j5EX)VFFPKPƷ eS?vi~wYU#ޖi* |{'Pkk#cXjdÅİhB]EN}x$tm1(,MlzlA 9<=ŝB >Np2 uEϘ<ګ|kHA#lurAةuH@ؠaJqhZN9`'mOٳC1[c)ϧO9Zia#T{̦TY SUaGut7:>z`oouo3`#QNW淉ۯuNaJFZ\L91/}KDZ8ea>wcAD5pn|%:5R|@D#SiiEEe,kb!5R};$%dvcJNvWUU&KT56 ȃk@Tw^ /pyKpYZd#Y- im澟E&FM1ærtlo垵 0;I K'?;i69eR@NVKPhRԊ_S"";>f>|d+ֱp%vy1aՃד~-r) Tu4~RGD/墂|\Η4Iϓ#k%3zLRPuR…T4Ke.9ȺC֣27W<3 ZJ:~? 5e5mOw{g]N69D6sޝ~=n5c-ߌ˾A 2?o ?猃me^=JSGVF&ձhȄ>gg\glށSIz[@ 2ʪDקיvlʖ[m4%1)B,/~ @:$6/'BY΅RI)>3-ٖ`zF˼É=ǰl@]=ķYpqs6fv5O=M~s9PaTRw%*t^hc߫jbiXBhQ,s}hE^t/eK=Eڎ.ɶ+Ye$Jtx!=abӺ8gM\#v&p.DvI+T r|M~ Latn[f5[_#s1)6P(&.;la3"D(8 h@vq}j{IQ`\u;9XDw 68{ ,-pQihj>+Hm 5FOQzoȎdʿZ o(vXgĮ$:-oo;̷?7p$~\wm-p_+~t؜/i)_f[ݭ:QXyPqrpGD̔\ ;c/.A6$k7lncWC܌?arnn4|Q=coHP("8JoI(ՍSpn"A ̞3$-bmCG$9,Ě $@k6I,@а$y ջ\0}2}&jZ&dK^1yz+5 J%^m`:;M7ru#£(2K:{[`p4.z?PaI|9aQE{Js'pb穒C%~mOa}/= Z05!X'ci{۟y_Vg 5__iv2㇫D ǧh_+a%:ɴ~Zx1>C/@C#@s 3KQVwH>A b's0=׶NȽ1-U=<bZd+^of[FE[. M/* Bh.n JibJ54mZsI6SU$LA0:Tq{*f7$];'8[sĥVa֜NB} LQ~qGIEQrfTeC5&mmCձǠ]Z&X`yp7;i']ظ!^K D OsRBkG^߷'Ii zN{ĆTV%bQv@agq9:SWGV5!%G~::tdH=zѯM* xKeثEP04=D%z[X(l!&Jff)v=Ƨ>>U^53sC#i$団v89{YO&h}Wu,l rym'k n)ܝ~ĭIٜN Gl i2e:"tyDp8- Ԟw֮ .q:M^'r44? mx{V^) V!wә ͐Um&/Qt9|-JAdTrNy'і:aLjUF{:np )!xe0 b>뾠]%^RB(㟆cj= !ӨʍȊ.Hx@@B.0:|F5jΤZUNOŜ~ 7'KK%ieL YESXea'eae …&%L0J|Rw)XS} McdoVUaZjY@pU;/,)]`1^*-0˗/)*,^HjvVK Qhu)uq;` 4`4)>%o}`@*blC4@}c9軏792h#OϢ Rp!'EƩOu}l8q8E}bx*̧exQƨ)omR?KS)KJ xj;wm^a)dq.tvs"+*DψX7)L GUy܂ 3ƽъ~ AT_bf7*/pG㌙DW'ʪW8Tn [9kΗc=:X;IOA"*Q Sŋ֋o2XIyR'9X}&79W_/D ZVŋWpLp a86~Z@Hl}n} rPW2z.=@̓kPa<XhZ.;V-nr"禠dTkav<*+U*tĤD #w,!SI" b6%(W*cZ3[Cq;]}U_Cxn? nyyj,岜 Ӟ; V̰Lw$]}W2j?f q/SJaknơ) ^}:DDdq9L,y*V妴mu#U8z:2OР4bV'Gs|#|Tm/\RRn_cB@^Ƃ-X#7g I SquVwG9Bq)\Ś,*jO4N> k@Fn i^iY(❰?9ShS3C|L\^8)yKpv̂& ͙zl0 bkQ.[:Pu -i1 #y㼟/_`0t`mJl1"퇤I9: 38kz9+GN$~Tt$ 1x|zw`¬״+0'0w54StW/Ds7u E9LK,v\iP(ɛ{.$>92. C'#}>q&QDrb n' B&kk^9c!hDD#琢B%s(e68֧~%+ |WOX#>>pQM|+Vdq)8p^@kή5̷r{ITAN"-lC^,S]X:% א 1[g?pp$k:pvtgAP)GFU !Ӛ u@ZBDIX*3__ 풶25%eZ;ڴtt$W`Yv(d WSA6FD8yc]&;7T亍>o>-{v|*rbiܻp0\kBPfs\ϳI>' /oF9f`v X4Of-Qq:da5$62$X-=E| !ٻYTiaR}4z8Zʼ3 JSit-@ڭm[/4ri=d1U8U~)*jd`芞_2z`cGpY}WdM2C8/t3Zo5%vw<~$vմY;Qo*d0^Ͽ\]I/6zWK{E۴w,6Zj_AxUWxCDh2PFʃ9HSkpuVFk:|^+E qMʇǝDFHyi1:Ԏn`׸8*4z%O7*Ԃs& ?b<:pw44Pܣ9#4wq&h1$z݉+Y_2 VXDï,,}.`+~§DB5 Lf &hlpD*f 2޷MN<aFv#Ka,Thr S$ y[>3VzM nH8d(نcCOݷn슫 ^,>2ñiS|q9u-TڀX x تH5-gʖy)Ed֝N>[6XbmV?LgK|I8-Q'\`I,!FiC\H4ԓNuE|ȃƵΦυĎ P`Hû21zyMYTe"ڮjٛSQ02Vp7;&e7ΏU!K9SI=0taq4GvL!_њ(SMAqrpHg= endstream endobj 193 0 obj << /Type /FontDescriptor /FontName /DDCFKL+LMRoman10-Italic /Flags 4 /FontBBox [-458 -290 1386 1125] /Ascent 689 /CapHeight 689 /Descent -194 /ItalicAngle -14 /StemV 56 /XHeight 431 /CharSet (/A/D/Q/T/a/d/e/f/g/h/hyphen/i/l/m/n/o/r/s/t/u/v/w/y) /FontFile 192 0 R >> endobj 194 0 obj << /Length1 1968 /Length2 25926 /Length3 0 /Length 27112 /Filter /FlateDecode >> stream xڴeTںJi-58w)Pw}y|##2?듕 Ez!S;cLS5\@sg#G <#li5y` ͂ ߔcwlndPFNoj ~sww4JO'oa%d fc۹ -Tv 1 `gPjTĔU j* oU@TH^U THyUUqSRRcfs3d'q|cڛ_ T`=#+ќ/~NW;Gkۻ#WaAo[3YK Io[)ߜ{+OLN@0rWVQQ`kd AF 7C ` 47A @O_ԅNke39;yM@NN`#f6?LNH^J\LE^m@rvo1Y'$*b0sކT d*bgk OD-stgd PYLٞQ d 7?2s :n&57o򴷳8,̀ooNF.@9&ෑ[KߘK?@R` 4g?_ĝmllTg]m_f?mlKg$n4UX]R`} l,+,DJ.5=2mXMA@''n^a`֖Ҥ/[1%0rt4rgzvv'ț#w{(G7|K0q2A,F+QS`qAo2(J||* 6?-?DFo)mdo;higo#o[쯵dysUv@ Kӷe"gvtaz)7_#?sg{= 7۹F?|MiS @~a΄7*g`X( n2,M )ZqD6Iy >yv%mҙ9ZlM1MDWԋ鷺%MP6hn(xZ߁_1h s;-&0_еBhhMDU30qop!!f"Iq2~p!z柳a/1Ά4B`{r)[׀ԄpRkTьhtH'"eGڣj3CZ-`x`#JY/yB'1A+QmH66[ྕ{~UX ޑk&d}9P r,>Q5P\Љ>%:bN :aod . `Gb\Kvyn4,d0w~n8#S.@UA_Lž7(dc\Ss\UГd0Č)8MWMT^jɵ7>"[\+@(ƉV[zoBCc3"9 9`QZGݒ]Jcрw&@~b> 9mQb3ND'ïb>KJ3HOmžGRQZےx}(kv&CTǰLfx\ʋp`_fCG]$*Qx;4uf_Cf~ǦZte |(nSDj \a䲾eۓb5Bhؿx/Z#hmUSbpW#,a YAnqL4qKX"u7U!UfRGhޜq 'ÐQqOJŇ _ZCa ˪B| 2q) 1%Aj퉽<C{`ՙsaٔão wzޜX@'X6V}XtvLC 0>ʲwյznmWl!sǰAU 6 D)4 #,S:[yiF|;R|DE.XC]N@z>s5=KA!NF)T]CzӴ&4yEBVJf #zY/=O#2el۔s dbNfW=(awXM^ϯ 㣱THX~Aؼe&- y^2[pe. "D:~hkŕ%T7=dw'W.Rts]H堙g$ŘRnmy%hm7l[(8QA'. 8l_ϧ d_YEdyWq!5PkU"jٙ5wb#P8=!NGȆs~⎨ti<'mTZ+(ֶ=w<҆ Kޒ谽5Dzʪ/T7$J7X[l֒y HͰj5"y6-%>e^FyWyalJ3,:_~vXA3Q@L] GcYP "m8@YvetrscmCrt2(:S>1l[LUyr$֎\ÊbT^/ˀu*t^u;.X1wvm02ҧg;!48  jeGHϱw~|z aP&2+iuEYH-l鎹>[͸qಆA$WW_-{W<0 <9qm LT]acXx1 ![ߕ~W6ov^">8Z* |OȜyW/=o4z!{ODVaK+lX` GNVXG \De-M@j4"{48W7p7;,\ϒҎ!-毆`";>JA#hhhJ91_n}d[/?䗣#KT \ZR6$aB =2 ЙcICHvTTɴ .NAR(Zˊҹoc6mP{sHٮC+ͺ}X5G_*9cb*}jEbE)-+HF@:nEyCt}M>blȞ 6g"s~clxSSS̉ygg, q-Op{ߩxdsX ᳢DzdJ7R1M:e+d\*r{Gq m"LC_J;)Q#3?>fY\pDA#O#tî*lk M\tx%M|xW`:3ky%ɶ,{gwl k=b tP0&HWQkғh`+F˲c.0aWǎ*WzJEv-x4ݪԱ}hh4&\RYZ$Q9a1D X~g_Մ[FX K@'ښ5uyV9 "חL05f(+C}PE0og?GeFuv<ʃ[njL  S1ǭ,/x̼gNq-ꆎ #"Tγعݽ4Y'jEJ"C6yZ Mq"npW/nO:_XuM \~I,tv RMѮ3> RUq,XwBpǼ&SYVٵ̒J߬!Gu?6S, \Jh M!D/CbU›EZRk&KiCEJqZ zA$ծPyQ&kRA{`P"$֣ R1jf="\:ټxܠ/壕@ȗwxΥm_b9'?h[ѱ̈ߪ2)NI4eTu+*a`f7ʼK3YC>xbQ)>2 H)zd{ EȂ Ui075>ElMEM g6a{J8|< k@4|՞y8LrXUؖL0yR cV\iφi:jZ ԏt-HYڈ4{ءN$cVTͤW5KȽP.!#v7}=B}yn*],UU=smݕm!N;À|ZBYWUfHOؾ(n56 6z/ve I"ܧi+KrBblW&_ןc'xKk5QG~QYĹZBuKU6$M/!1Z‡@Z%=kڷN}m•BF54XoBf]s <d͋`DJ J}iy?y ͎ pΒ:hCNG K -Tn?zY[Yel =x^YNUHǟNq˭/?G1梱#&N2^y20E"61$ĖaOT% f0W en/8" e{nȣV(FTrd Wg-"^M(?6"{˿L2%m|J˟5ro#@5IpvMmšgg ZNBt^zVD7_$=2!H~Q[]X i)IKL"Q3>'0ߏv %RtoFkZOX ;#:9l 2X{n oPi/z&guf,7kneᖠ5ҽP 눌^;ck Of0)P1>>}4'ڨ! py7U-2fSO>_;%J\F]5gq`LJk:GjMҹ{~w *lk, -ݗF$pX(njT W `%w:qetbz*7-԰S/K٨y Es(|B|R'i֪qUS*$\§ L*ҊqS0L:ϑk >14?^K$^بkW)jsT"u{Opn}D9eFǻJ icG)./ɗzi{;C`.̗JBI oMs)t,!P,8>x<s"c{uVoߪ:SsdvJcAR~rÔW#lEfĨXu 9]ޘ\!npyqz1|>{C%Ebط5Vs2^iRKV[Ƹiá6Cz~G#;C:Ej8ρy%4˓/?8Xy]űk&΂&ϹRB7?jDK_;jPD}{Avy%$IT=wⷙ$>0(̶4ޠeDe]9om ê)B\L{̵;)J ?xK`7a%hk6F2ik}%`5 /i v7xp5,Kruj8@qzl[h'W!|gO351lH;axug(8Z&}%z`TF<i0N}X2ЀlAvO[w>"L}R˻Q枥"y0e\jS[Az[(P@CN4/B7A ixEMN߁B ɋaDn ΢GZB!WHLj#FhѐhO_~|I%yh!NwMҨR`O\(D]8j<¸U_n6yxsب-zcs%~uQА$^ L"1B-W:crmЪV%= tHXoTF}&`z^ SCH,ϴ'U ]i\kizOpz,qHQ ZBfvF%C]F1:'㥤L!rn\\$hA:ŴX1~\Kwv{~y,?e}48m Z6sb[: vğHܓ1v~՝D#3FYF1}I>|Xs3 +𬴌0Uʢ2tXfcR[&A21њkx Tǭ!/.QcU f#K#KcvJ"`2+=9d/B%UtpbRפ=~،ʒF *"'#s9 Ruv U}A%a6.A6ʋ57ǹ)aB (hQݑc5iY7Nhyƅ.˰J¨eڣщ17Fwkw3+Epi3&svu&%r)4*%4y9=꘾ˆ*}{~>`h'M(녠r|O4N6N[Oe]Sd*#V B2n2ũl9Lp~N*TO@> 7n+m@kP{Bt*1t{K ճ(y辴(?pGze_A|Y=nߚ@V NZd}d:hUb.w+-e)+ghhOdSDhy6Re4ND7;[q[A3r*py56) 1̝ %x핦K7f4>Q6\d&xM<5 ƹv;('fp8 *%>(t|<T<ɦ (]s}>1 ]LNGCFeP>q/`` >ц`.JM3tD"Y##e${b- &6KʼrMm(v3kxK0 nE4ޞxәb/(Җ ?:<T|(YZ\cpZ<,>돊^N5waKfDy6ZAqu,oՖdS9>bݹ'' E2cAȼhVS „w)0۝! ]5u$—!t|NEh&Θ">3d sÐCy7U.@?OXȤ#vc’eH|g% *zR$u<~"Sv$ϛ4~c5e,WQSZ!bGûnV8U;g ]tl*g8y2 a}=P" W^0G3o&lHlݼ 'ίbZ}]bqY)*z#qN kghįANS4K\#b n>%b~jMkG"O:<^}b~qW *idA˗ƈ})sI(d7pei eԽP'Mȶe%XZt=#HrQ5p0*D4Amx.+pqJٸmyzUj@o=o{w x F8$ާZNd&K# Go8GQn ^)"'zDU ڴF?_Nˊl]0聾Sobѩ1qޗyT5-R2".#y!w ]MǡxQapjT<&6_C37OZu .KXtef"UWYo *ҏg.ܪhJ oZ̡aF/j޽gA E d ;f1_Ql\ 1υ5یPS]kiP݃*W88/-D$yijF:3\gH>on&„kv;v뱓3Cm5j}Y=w15}m~ ̏4,y_sIV=߫Z3f 0 P^##NQH=aT>j̈́ڄnNW5Ҧ8#NՋe8[ԸiB ;"olTrTeriU&WgZV' 蒙a2c=j)-돆9h<`&u5wq'5Pi6A$V!`d?M*ܼR"mmD+8)_5bjq uլOuR[F_G?Y$p~N7gI{eH{DIrT:<[ae~Tf{uuEF1H#ĺYڜB<{ éQIɇ!.6jug,w:~(=ڰc8lPs?,wq"_g+X^OOdnK8\ebdC_uqq%S֍a.K&Hv=ᢛz/ .Sp ^+r^I.1M?ijq'Exiiev 5vcwg7@ImYRW~M?(ch>X{(8ǫފІ-37 `ۗ׭ %R 9Ҳ8yD]W oF!X_L9跖cGn@9N0}NcPrС0 ^HlteJAoU\ó"CrRӍjIcHOkvV`xtE+`-Ԩ'}O^èfjeϴ#%Q~fE*+<笶tl=lfx:_;'}B MR (Hoxu|&'w5.1(_Jc`*gBNըiC5Unh]_/C?s#}->0}B)^%)i4?7b3Q }eIZrpE^IʉC;D}Ͼ b ] .>(hbVN.UmW dyd5$CgT0|twJ|kueۜ?L+c.ewQCb¯|֪s?V 1 %db-E]'9Vbe7q*)<3{LH*i*fX\緒8nCX AD]uY9WX=Qegyvh4Lj{&i+b!'|q v G zvC ՝CpBN敋'l@;#a2(r5\̐g.$zii6xƳAn{\.Bh'K{}@zծ7$C.=—*Igb悪ܢsp@ .U7")l"t^ܒ}b/  >c QVjl~tE ZMZsӀ3H.$k Vfq#t?_&y$O7]Mg 2dܑ!e_<:1u4twmlX~HN8>.{bzVٯ":?GGBx9YqaxOrvƁ$@v=#xl3F45hp_ԷlKs~.ur8V"UY$<浄, r*$0?ނ1e TflHՑ5UB2ƢǙZ-%14*s^jNq>ց7ɑ aUN,O4gKQQ>`_|B@&$Ftg@ǽNыѢS0.y]:r  #GD&΄Kz*zӋ-]U4yІ~Υk7-_bigZC8J.B4pMiweĐ&(yP_Ud8uW V>&3M/CfDxDoiW`X mcuQ'}kr=f:8(ҤΆP%ѷ#s -PI)|J!JPr8ZR} `<ۖH9hWU3WVo"ѯ?&q EWi!A'ã"3WazpC.Q<đBgp#,yL cSͨV5mFγ+>r3 & LY.lrbyvן/lOZ M']͇>ʺt FyQcۮ7쳲 SM/smq?k3Ιj;Rq7VbSZD>l_U҇ vB9O{J6WA.,rWcM~,S[?<Ѯ/TFǠ2gg^LDC6XsQ0p+/#yYth218`}_ay*41&4 Ga |ڊ)b آ<>>h"Nd+[HJlGRrpG79ėc_Ǵ^&-FYmOhmI;UxѭĖj ͛a\PcFvhƎ\XH_gzbp1}OH>E?M98WqI87Ŋϣh7Bݬ b?i9 |kX Jqqb O?٩/v.N-G5Nx(-;JF*k͊R>!!\O2֤~QB\jųc×*p`|2(eĮ?ʕ*fzGo-à& JUxaׂqԯto1>h.mhjr \_H^PV=5&$H^PdV"M >}FT`aMq\?ս|(_iB?C%nJ{ݚ+ۙI172C>1+dDtlW<Phj@145 ,/uSY0Ҿ zCz`Z=`ͼAm7!Gf iomV<y7zv?#|qc:8褅.fT,jsPҨyf1EypeSoeN_P!2nF_]aޕwX OS)r{ Wln0UFOR%]j]M1u8˦{Zf?APLCy*BӀ}-K-BO |܁ֹ %TхsAķίȿ޸>,]A,oҗR~Lc]C[z;y""hdWމ%W^\eE<ҭ 5E%SV4u9K;S2tKe:XUg̐}% 4K*Wʹz}R{38nעxBn}!=m j`=S?!1+Լ"@uFJ]`6-<gQ,P$. |J8Ԣ;jڐRaNe56ir 6pbֲ$j_7]B=OwI]'м7H=sĹz#s–iGM5lY|PsjD*͟snhEzSS_]:U96+RBABꖳno|]J?hv+Y{rs7-l߈[fR"lcm< ymA[,uИ{H~Zy{_Q+0 l{5R TZT|~4uЙΟ33YH9͵@@99qJƅ&1C>zՙ!3R` , SN湹!) aD%zwe;}BBehz,>u4qx&{뚼1M.I'Y>+XI]~%oo>"&a&$" 2ioˬ0Lw (yCQ9J)ݣ~+>:ad/Z¨Bg_[;.Iǂ%A>ہ5irZ/ <_-R{Q(p5p)#zm_ ro_te Љl3/:n^0T(~sW FiFw%/rMb6]f,8BsRVn1_t'u7Q.U`30*e梺{oTɋZդ%wfyŎUy^%TO\x`e_}83Q o[Q{Nw!Zi4#HG8ֻ*Sr #dBqM-<uJJW`#t\4\u|fK;QB=e?DT5pkꔱwIf֨lz2\+O[6?66<Օ Ctf:qwB2m>9Tݱ,/IXpw^@ac[ukqĥ/ˇrh+k ҘZ;)gV@Ec&<ٷH^ 7ndXC' I:g^s)j;DÚk aBUlrxZN\N(!=ZDibA@?7T.Ma6 Z573~pJ#bOGfeuBׂ\лjTq 4,vYs-*v{wmt}BCmm1 ߷&=_оCeYoIv\8]VTwHXTit1}Jc!ȩʼ#Em+=/we][ eQ"AB9yZ 5riM;]J?Rc}Ҍ&'\kzRMwh^wouO^^ץ҆ӝ$w@!٠3$>}͛ZZ|Dk:m:&IjۅFݥo-0Q T>OUcO`YzNWv%R,HT 탦MC 2 X=S.y\gj G` e u'H¹|GBs<O蓣(e{QU Hg,ā B>Ygqb0-eV|$&p~y1XkGpp1̵DVDyM\dTcA}wK쬖t4ip%3]0tI[e\>'LQ1DkKhH+W1J<ũtboCPXbs@+11A_{e-H&zޘ;x㩪|KC4 EY/Fw8 Xi=֔HB.le%)vO\ (ضLl۶m';1mĶmN㜺U>}Wuxfn? yN0Q ==T;n%1xf+Ѩ{0:vK8(B?Zo)5THoW-V6} >X9 w厐G"6MI,Et6oQPK*j>vw3Yޡf.i%Fcޫji0G2>!|fq%p.VFGJD?FʠU71rh-b19}T|0#o*NIPnyĂ̛+X}ۍa`˳ᚈ ~Jl,SЦ y*y7hev19y:|x>RYɚcA Ambޒ61}[? R%f4D{9Oa-LD p.H urcZS5&7)) e {ԃ*ҲF(0ǦS!' C$tvG Y!^pŸqhi'diY# Zߘ?En= OGDs ) %)H \RϖO+ngc+R<ፐxܷ̦WITuP,n>s&FkQtN7 ?˲!\M|KDB G*JP̣h" WqΔ'J$+nOCM9Cn|p>b1O!}J RF5jZkLW"N ]@`m?["NͷF}<%"#JRꌋ;G !WNOsCG'blXil;eAp|,㬘 znL?-CT g;:ui֧C`vDŽ:yl]`^ObQӱly"3(Y̥,kF;yUnkįځ̛»UſYUuhZQs!Ω S5Ku d+]ċҢդYT?2COp)\qaiC 뽧-4K]k[7b]1c,wPބD$\kg)2 RL{N˾%GawM^=F EF~ k ?"/ ԫv0XFƾ5R9-9nxI;9G*dpZ@^::_oMEw EZ;Vר:#0\, Sec!ʳw78]yYBSXlddhR,jS~49(ssh^DD%W,O ~_6otk8xFxl}OhS['GLvֆUL(S϶Ts>3'i+Txh"wK?b~g96w0)ֳu?$bNʩo0Iae%xq*6%nЏ p~zܐ"mYˆ>A2bƢB J0붍B8nGu13f )ft6 uԜp7ؒPpJf.P=+%yqD,-s 燔lZj{`bCU_m qꌎ@?zEz`6|TqܑځRfW.ZDnw8U&u"Y2,}aU;TODӫ2-p_=CP T.1 Xyl͒t |E*w!Q8-K/*h%BRoGT֣5VI3GZŎ5eF4O$|k2̞NW(K&teO8Pau@ HNC bz\n˥$ )nҔ9*܄~vƓ"VcLbG.etǎ0KT.`>xa vr rജVU!ոG4?$rNM#GKYyzjOه3hwo ?ЈuNe7s15JsoϿQ5W?AP!梯vQ)L;Jx\.)"ctq׾-UFnJm\?򔥧GI[Hl>TWׁO$J.!kɋMOGƴs iXw3Nu@G&c s -ը*8[UeSV6pg:kP68 4Z~|%YYXAm"jf๑ d9־.R_C8r)Pԥ]Z2;{nzT=D)EZZYh}96.swbW/Iڤ|5$Ƥw\+۟q3b/wo'mx)MW}.-PbǩRQX} V8C: zVq=x 0d}(&nF Ƭ]FbsfE88n|qvAU|ϥ:(DLiǙ6&CØ!C;ZG}F|9/lq89?N0%;2WeR!K\gb0A*l;*UJ]#smZw9_t(DdW^!{u1|C w^;'H#~n`1e>ARpzoȉ :2COr_-_?}5]bl_WCL3^oʣ.W.K¼PL?Dwk+u dL(o:kbSX`Wv.˺pqKO,ҡSWxojs4>:AYQ[`/R:=^D9SfͲT^r*7דK>>z& @Ԥ`{A! $*WofW-AlwPg\/ v t4ME_'Z3M;[c -4_V;P(`,g} %l0IxKrrQኢb]I':%Xn , "8w\L/\|r9fX%T0-"qPz,{ٶw1XI5өqT! 9z$fPPUN9t-rQ>-f%W[hޭZ8N#3NlC$ k9^MT-(wN8椳A|'&زcLyai^E7*Z.r8h]$0TY[7({ѿj(N_#j@Ld\73Ԥ'jt*Ocե}/7$Ŵe)|s@Y/|`z ,(r?(xv$*6d;]ck ^"!lnhZeǫg*^͆$ )Hp&-K2 簤.Y}+T5wRU_ꊸ!{i1G2nRH SVѫ\[yq}\} F0=quT`+8O c>)6slBxDOFǏ?R>/C9N}㲥8b@E〒KieAZb8CQ[?4z 닙%s Bŧؿ U(=@9hImOr}_+ RF6bK%@LAq08FJї͝2&ÿ="ǘW=¨͢? $~F0W#h?, ?Ê+[m< @wSg+ (9 8eFgCQ+/9 )b2_gAZlo58Kzd2kys)"ST[z/2Ahb:'ęⴺ';Bz0txTLIٝ#ik:nP3?I2罃֖'q(Ak?OÇFxumy!FRgn["SM_,40^"17`g&o _fTK +SZhp*%( gL:㌍"wKɳZкh.y.B=.OlOJAAv!t-IC%=f  Z<xKF]\jNb! SDARn_S @bB *n誱X6qN~yț+kBOVVmpjRTɎ4/bֆ='Z#p=ڲOu ^bb2yxC۱& ofU tL$*A>hAmO&AAވv5::+VžNB5=O՗oUVSْ]''E˕Q1K%rN(wHdeM2"uVܣ0F㇛#L4f;> y>1ըn]FAs#?IʘC ;(!_)3p` ʊOim4ﴃN.X򤤆Y~rRЄeG\ZKi,4Q $`T6VGP/C+/wG$h[Aw\O:sgRKgdzN4&wF%ZNCƗo"ibx-Y44T,=@OF3&VHT(m\40 [~$'G>KMe]3?ͮyI-?'"[7TCn)|لPXI7(sjL9D%Y6C/VY6! [CtG SDPK :2GO&`FCCNŁx~^F&A['gUs~?`gJ2jAa,4pΉ{n5i8PRZjl?###:i)Wu4nW` iکH J!4ٲ#dL ɀbȵp[IRC,):fdɴTt4UJ 4M|@JM  4)fY73YD&7{ɱ}r7AЬv ;t .v{f.ÙqUumMlFx%.:t8Y( eؑAn{if{%aGj1YVR3ƍX[gȄ,zaLg i#["),*N Y l2"LW=1e|+tփ)C]hBjK9,J}< ?qۋum;O ,јޡ8EC( C b,JZ. 隲'4~ȷR{ibpAES<}7_)2Џ7[m!fFV͉}#PT vwei&6 F+ q*saybSŽ Qiq^%pGE.b^_{Q}Qlk#j4`\mIyqi]fds<80//b)]]ͺ.@6Yb]u*Lv1>ݤ<_i6hWF1J̽wc\.\RuﭳbUw9qdNۂ 9IS8H2[muCDGo0tF/ysI_H^FL:x}O`+F&j%;s.4fa7Oɠf,#yy _L"@&Udq V8]o4'G4d[Nj *#$KP`;VFƽ8Շ}3H!|VəxńVLTiiI-ȈF[^d?$`˿$8ƒ$y䃟rs*೬[Fzs*6p1)wQ3Rt7S}Ğ6[F uctVn^u&}3j_?~dkw6a5X@Q6-iz,64:. OoH\E̤8T8_ı@D Ŵލ0C{ ?1;! o,gxmNx2K'T\N.wYJE%+l8/'l@Wmv!MT/*' X%I$c:ZpY.$EOFCaxOf=%$b7t:n6ۛC`iA=0fEJx1PGw/D@vM`%UDCtx44'Ӌ.dvO)3΅#kƴw{VKb+1yBE ݏt/W# \m= endstream endobj 195 0 obj << /Type /FontDescriptor /FontName /CEJZIX+LMRomanSlant8-Regular /Flags 4 /FontBBox [-483 -292 1526 1125] /Ascent 689 /CapHeight 689 /Descent -194 /ItalicAngle -9 /StemV 92 /XHeight 431 /CharSet (/A/C/E/F/H/I/K/L/N/O/P/Q/R/S/T/U/W/endash/one/period/three) /FontFile 194 0 R >> endobj 196 0 obj << /Length1 1717 /Length2 1847 /Length3 0 /Length 2908 /Filter /FlateDecode >> stream xڵT{Yzޥ$ٱ`Eplb< eF Q>F5!\1*fb !"[*LXÂ`XK%=# 8 0`bE a*0 IF0!! Dɗ \||d"1S,\,?chrcdY0B/pˎljJPA$,x-%ԈP PZQ(c8qB+ * [aych' %˂1P@"ĈFಜ1wweB!L aBBGۈfX$`|DB%vȏ$O BX7~!(0c3!rى"yCH Bƥ[ -xd:uB@@Ɣds}QQG GP CC9B‹nWƁ #, Ȋ˵#3Sef)1BT8cyx)`P$ p~D]ؼ=1:Jncf hdb e? a yn\-g1 `tv;$W8GX  890ۇyC\!\> HT3)6&OHx NajCÀތO<0|~'y(%B$4ssC%φr Aq"x,"%IS( DL 22x 1`dIX.J?_p5?Mƅ؉']oB(J؉ߗ : Dۣ,.T:X<ȐFe_ֲ10,CZ넰 㜎6Mճ$??`E)Y W:ցWo4to+r<@/a>c Wq곜jsאe3Zu -0kcZ啫ûiT^g~d_ D9Җje- eORv%.uW^:{{ ')oe]x̝g,Nv^I4M.+VheHi5|Gmdˁݸv?}f W vԈ]֊l)WbPqTV՝0:9O+:Ԩ=X_jha}>'7ݱ{rԬ%6gҤOcZN6 X^_DiFғv/XB3u sk'I1i`4z΃?+3"陱ۺȲQ_DL]dka𾪽KG) uwLZtrpǒ#9*7ӄmYܧkh4Ml XUV7INeKTF-E+ŝȧdjN \>}]'VuۭOl5^GxHʊ"#}g-Q7mYOy푗ql:S7?`~gDkdqViz Yjԋwo~_>E޾Jh۷5|u!WaKkZ|9">uN7Vه`9aH#UgݿY_P=oi6tjRaFg/:KN/eN_k-M/hN\ݴbeC msNLZ)tᥴٗVּԛ|)Fx'*y j^C+ڢZzs਷e=rMZZV5Ƞi;s0qKo>3T8QAeXx=Rݟ{;H`^HVMm*4I<^4fw]PEҎ IVC “m'Yx5N_sIq:FUӞ):@K{{98xY'PAo\I`^\%U-> endobj 198 0 obj << /Length1 2587 /Length2 27627 /Length3 0 /Length 29089 /Filter /FlateDecode >> stream xڴuXm>LwJtwwww(5t3twwww#-t ݝJ]0WW|{U5MҎ &6fV~#+:ʉDE%4Y;:H@ v[!Qd@`PL4lZ+:XZ;.N^.֖V18~G- 71up8䙕ʎ`5` 28Z4- )u 38qԒaH)kJڌ- ?5`eMwow%)M1M=U)65@Wi55? V ? +Œ~V֮G[h1nv {(Ek3+𷓴㿔Vr_W X㫨7vL +hN/@JV'Ϳ;+33߉8z՛.@7{3vG$,'-ɤ^<&%GpwAT"/ ^R)s G{{0kW퓴 쵭+v0ys7'-kg7YEHd@t=ͬX~g[~~mqrtXع-$Ww [`nm/:aA'#_b0ohyPOhĢ/?vS6oK,B+]= 3/bv@Li~k >z\&6nс7 Gw[-EEJIZMY̤ͭ,\/$Vsq|+mgQ, ptA=Ln.oѿ7EHA?" `,2;EA|8ΧA|j8ΧqX4 pZu&A`irYOkW?`S 6v0Sh66sbr n_ ւ/01 ? VZp. n/ /_\_\W(6pA0!?N&h#e?鿎Ou39pan/~l`_ճks30yW \ [48?_bw/N׺ F`scm~/ Ӏ|f_ Dy;z0qr8pFnr5M*!-/: dWKVR1 ',gvKPEJ:sKeӂuBq^6:SfnDwMФ& sVPUpt%u&N${ا_1) ֊`=ڱ]x~ Dm$;!֤_l~޸8wBΩC!XSOf1҅M}N\| 2lJHhk;YcoY|4R[WZI5H%A ڐ3Vڜr FLpa6W+ \)ZBX O< c3(ez18A۫kA9?tG o0^I[ЇJ{nxjӓo6 9L L~~gJ$T-lFrwLܤRIoA{-y^27ۄ!6~`I+/1t$w:TM[=|$9zjRK17At=VY4"4}L`TXVnN2Q+Rgj.! 5!R;@ =eUWkn=2%WjvePH,,|CstnzlAX/ʨV'ZݘzP|K֑XVh 7b% ,V͗nP޽""(~s/R<= "~}`S/40#D 5?Rp:]$ 8hƗueh|~ !25]^ݩ!Gߋ>euhYgĦ2z>@Y-7{JE-ȎG/v$&j/ W+O@!z\nǜ]}>w2FXbm#|;^ak|7Ln Qy]#stbqZo3D)Lp$enƵ߽(0T!,(^O8/\ކK3W!SA^0|i>rNܞrQ7·@ITMԱLSC0'<Ȭઔ`Ww%~+t_28x2/yby0LA1ECbRg3=+eXG'?A&Nz6O3,-H y]rg¥JNlo h:Q8g=.U&deTw^dhP 59٢GekJ^,($mՕxP~%99^< A'Y{)'ֱJ%f&Y+vNfh~@nspzz`V|?y%VHn!o !Hڀ=ٞH(HY[/h XnϾTdr™Gök+JGTq椘N7A]8wm(c{榋H%qf2worD9אvQ3зّ%ǿ3wcbCul6i!d.$&{Qx|1syo#lrY=v*3y- !> .f'W vf+S T#)8(YXѪUFa\] :U"4UL눭d៹% |h !&:$xU+G`LގuM-bEPULt|_=#Z~kU#0j͗>8ygO`,B{MȺWk_s#BRo~=tTy{鵏(j =m$h7QVW(_*ѣ!ߞMZ|߆_b8#|P6-UAfβ ҮlcT-]|pg_PNHsLT7RWcbw5”*Gym%qfJb"~PX6D]@˃ѐpPau(WX(B=HS//{\˦x'G|FHz0zSa8?(b/ &A鳉ɆZ PrVƎmP>11ASw1\LZ'#~Բ%/1:06H3F Qɵ>:SV)1⋪)įbOb~ u~R+5l#!6 #n6IUScjTxj%G;9' xP1# g.uZvI"|/ozBS%tya*,bO[9KxeTn`9<_$Z\"QLR.e`__m[ E#=fV8ҜƂ_.]9~11p.TCӌh>5C |zM7%K/9dO4emOvB9-;4YiW"_ڂFUž@[{ĄpPOp _n^E""N2$JSv;2)\wY\|R^YCB":ظE0q^U]B>mH1ЄVtR"m e(kή dC'IyO(!/e ҮoW+rڹņ89*0lX0#$L*.l~8%=K{-vu%emi]8Bd%OQz fM-Ln(\+@2/L2MƉ8 TvkCp r +*ێr=4%GB=P6z;7|vܧ$Vu Z9|݃Dȵ( {7ѐdɋ_!i̽'5cEM7`)GT?r&Y([Y&|b%Y6"rk^F>pFB!FmЅ&RA,KR$ z0hm1PG$MηBX+U἞  9Ҫt g0P^9b%֥s8CĀ9#ʫQp@N+ >f3Fys/Х$>ٞ:%6]r/=˙I֛(as]˄o!ZΫN& X G:=4̡0? rG)>6w,uY~ ju&>AZ#wĖ!䩿 ̸BFP*ĊppsvzvD2NDΧbvHvT\I? $EH#INL8Bh s뙰"!Ssw,?tܗk.h!3e*,> meӢ)ī^$Ui <N7<_e6-Svk~][?snYL!_'!(3;MmP[4%}ʫ+nhUAg|4k䪜 AH.NNY7%HAw>u q,?IAxMOU\XQ~cp]$_}B3VOz;1V|(~PNӋz`%ǎ=[L?ӶSqx1&%DžKOաdG8+?al wӔXEg}]@1SnNh֍\Sg}Ġo1&O<1YM/˫;[hגB@j@q?Cq"~5pzKa{عDN!>dVycҜ&١: ;60mu?Ufy$[l{9 A}nҦ`Lk̸J`ODw Q&`|Bz5:|5fdK5@m@c'JFҥ p)ƊgfDէe&R-?Cׄ~mkMPj;f^gݣ7C+ӐIIX %evrv6cYZ5N 9L n==9{wkR!_d09!C,d}SSq&lP׈bV;= Qno3s*a:C^nϒ&{뢜?yv3.څ Ɂb bֵQ | ܅\: V򠔆&קRUg)F+-L"V! =弢aYԸ%7š#$hR>U\vWWtK_|gwp\7fŽ|:QdB(vYJ٘S՗dwr+{o-6WJ#zX1Q/f2&0 *}yEb$Ɠȅn58r.<{NY)hhr>6>=[?16)_4<<;ke2AA *3ڽ_ZS7YX;`FjMӢ~sQu7MRbpcwi~9cGp͍ Gu|ghoptDb~Ta2 ':'qO¡t+ :D } <At`NC4t _v;9LJy}[=Ґ5y{GŽja~^F=rgGEXcػD5i7!EʻX%NoG#57B7ca~͍2voP)̨+W V0NQ"g&R~%QqAz>~;c>7(ؚZ8d%1mJe) %ag~u~LRP` Pς;\g<ɝ(\XQ~OS;a%(})iokDNs-ݩɔ{įtl]ʆ"S?Oż"2b:dOꉧqHsiT Hε)-s=  /*:[FPiķw^"ES O\&Ic1PLHx _M\vʩ>˼@hcA@Y^i&5E=lٽ,}BG{!<9&>~&+“C-hUE_q5vrj=Tuyf@*fH{;Fr@3 ܆cGo7筰c 4A *[ oϬaE#[΢yfeR˾h k W:C%*†mRN \P9H̄[v $T/|h/s8~C BUObagP\v—z}%.ؾ%oQv M:uwf|A3f+i:=paIp9-?m/M~r 8 &. {<-UPyb6( $?ֱ񎫞Y`j}^4 s0;#(_דs]P:=nf)f66{B$qcfoNByCbVl WfBLsv(ݭ$+[[x9S^l^i*HO%zIM_`"5\P5DUX1cRT{E,,mC[ S v «f9'uU;З&r#PBtd՛u4#3@NaN@w+e 6fQZT=ه;Њ"~6ƝDo!Ь{4% HGP%@zHd&w@Z2sÌ.qCC4gzV6˾@Nda cHs ӯ-lO'퀖U7*R-pMCK*֠$kp~-7?cHZ!uopsVj̞@(Wa" N6`**&@UÈrie::LNiÜwHsa׍eK=K +an6){^1q$nHɒDl`.fuRڰ#{g ݎޙM??pw'U86dPT5rԎ3Rq%Rz^FnNc|-:&ܢ1=F ȞX!GWY[ u+j7fn"1oP]D|z*:#ǮnxGgqV >&UTv E{O|G20uuم0Cc0.#QgŒJrfdl5 !>oy2y,oLrʊkM|cd;mxoC񪚐JQQo>1rf_@j5`=NB~#m B@6WN?9:4ysdZA""^\sa|6ۘFw-Ǐ yۛ^ =4ٔȱ(Aïڝ'{ Ir}MKg&ӂXp,&)+81W7LCu#X @`ڦ[Eףyy֒X# 9D9bas]Dn.,#kYNALlrK e"0f0`i;$ k_ i <ۍ(%=$,)?bMpNW<ZRzsC*F\a8k4V[栳R^(T"s%kX!(6K~5_=r2Wo߿/'+@O|X+ 2*!,Xz%~Zxk[Re'5̫#!Ps_544Xeo>LNK j~`.: NTNbtoPg[^-zѯ̋ld-uJȸG|/. ie xW֧\Zk_ uxu>Lq$uSZe*"GLi>=tElsI}Qy%MrZ9,W>_ N=nJTxeMC-K4{ty=& 'D:L4y{w*Ĵ󉥭GHkf Q{Pb7A=܂gn)tѲƹ&Uug:hڷ+Trj>2_=m@m)Eϣo!)@arJ PGi:AF {^5i{V%3EI}Voh*]ש;V%qz}xhdi\+;M[Ubv'&Q1 # Emic"ٛLPlh$UɀlTkۻ\]b TB}}cu8#=@kb&s* E #րc˛.6tdԣ4h*6c?TErgla5!U)m1^LQr_6ú*ߨ}X >8XVaTX\ЁP~%JIE ÚOM5B$~WxLQK˷x-;Lymշ_RKeS PU_7ne3Fv<^z1HJϢpŒ "BoU }lkwk$ZщE{26h5)wcY?jSCqy?##&y3_io_V>,;a63uf))Ȼ+}KJ"n9tB8,O'AJOg;ΠBl.VzЭwL8ޏ9uvx /%Qwc(REuq$5,CWgʉa&_A>|3֐,y;AcQc#ʪ~6@7hOHkG=cLI(Xbqؚ w$/?į Zu% Ty>ԝt >W5?*iR\rSu38cuf1[lټYBGdE3 `IDm,s J _&eL=vAd13xiU-jʉ4,N}P$FUn9AĢ,7)6i>+ްg}smEkd&j1-\Gy!ЊHa}asDѭVZU LwH䖓454.fhM*_Cc!\.NCq46Aѣ)R&F!5;:D=\ja_>"ACQ-F=eRxS A-'Gkrs~+`^ͻ=]mG?Մm |Y|&B %v IuW¯lXLwi>|hiJ^S]V1v}O_gw6Yޏ6(*^>{FFO7{g>?\ vVf!b2({GMGFjba>^]˟h| R]ʤ3LhPy`wm E)~eH\dc:=,-}4H(*qcrh_P|dYɌ}ӿiwu׹iA>Mt`*R"-JEԹļq tf؊g$(&3ˣ˻BQEP+TG( C;PEFҢՊ/8~D_#:Ÿ8@ #} qɂޖ]lv+vy, w}:!1x+?ҕasd5:MrI98Y֒NR8*`vasj E;7S ͑U?!g!(dj073҅JmE!`"ǘT褚YR÷`y.j7DXI4bv(;iLmL56cjGB2' PVCO +'gqL&7@Q+eyP75vx !h0[[-lKv[M؞mv^:lK  ^mXxjN=$~RAxP6y\Lhp}(QYWF 0gYz)`cZTHC.Ռy$>gql@(RjSQ=AG>vJ/0)\H73gqc_ղF6.M"R0i:4^Mؓ5W^}$-%.3;15` J2syDLxMA={Gܟp] ND);~@V̱-LlQ݅0ekiЭsz)\a<* ˼>;A_;Om4{O[fsu֌d:FCD5#mi)RWA8]z"qc.!p}oY,9@ڳI@a~ˏs"koDW^jDR[K8xSe49 !&58.\Y#β"P?'!5.g8anT;RD0VBе2Ңy,绐<ЩJB'~-l0U`zkxyT:%{Ձ& ~9%>CRB#64P˻x:!+c1.k!*53矉,O<ǥ#:l?1jB/|L[5 0*eMGKa)̱Uŗ~%g|#=}ctBVompOR}'bџQÐhfsڷw&ԃ|g4 U5%"+sETYqb/׽x6^7=SB.Vxt5fq7eN!X5[b*-_1)шB^nn )ap%I7Y>)IF5bT V JydrƚSZ<2p-pܩ_DxlRPk ɴdjZj@fuiһ3u$WCoF,?x:MX҂Xuc(DXnyzA<@Iz~^{/r%IH)U3tǎuYwGdhdEi eC2Mo.^ꎳ ӘkJ;h%Ќҧ0V fL-tZiN0$g<|J!Wqv\!B9bUtMT>29YWnsYQ̀wug6QP+=16?X]wl&pH bړzu2S'\yb W)ް R4@(Ѣ~xTC@@ ydX >:rR:+EyDsRa93лO?>.j,uKTErhG*;"fTw*5:kҳl(ƨRcUdYt* <"FF_<roGENTy~*v2ܠ]DSvCkY]Z];R%԰f{p>Bo _ѽ~9O`^({0ƻ%*C))T{yœ)oMo-%gўeOOD睰 \ ~[FyCN`+;FGe,;X׽?NỈ>u>ޏ$FNJs&6sO[_0'򚆊 ,-|[ pVx}fYM-[댎)TЀ<:1N[uu:(\!IOX3r )Ox) A%4u.azo4=݀Z󼦨dkE\|!&+IyVxκ8HkU{Ust +Cd&(KT9e:uگ_}NiD"|E4mk#RWI3eV48=746/Q&3!&xhzmCӷSm<'Alg=@HϽ"3e-^|*T_e-6ΡI6;m۶mc[vNlm仃PV ";c9bf^Ze1P6܅Q R _^]`O] JO5b79=00Faq.3w;m.VX(rITD=+N}!݁M"oQe*҂N|2FSصd걪۔”5 k'b39Ҷ6`C"+f:-~]Tk}i nUx)q%g_z\zuD5[;;kFuU-"At|ս̽]{/(RNV6$~%bJ!`ϡc=a<yG}z}C_4Ih|Ou f_BrxsJѮ\,)Xp]*|";S}OI͵c^!/)Vp]o?Χڃm0dgjvApA"Tk>U!1$b1GɼqHb?bA+R§.ʼJ4Ͱ-`4:#s Gw? }'Uw5詯G^BmHeݫdbjlsm3f=$a)J8RP,d="zdGߌ9Fa/BrR5Aqګe|"2@+I&WG6 x^gj';'OFEڸv5}Qd:UKZƆ8`j^W@|b*?`%K"(-U ?om%MbqF֒VG7F˴C;Ȟ%K4X:AAFAO[(d >[N:ʅdCSATexOH}:QgP5 S}G%%LoKeQg/#ik2m9ABC<..MLNf+ʩ_[`H28SW20=zEiA`\hͧ(b£LEoERv_1_ !h?D iټ:9{26[/>(|(h+hq#=6^)C1H3k? s|,w,wƫu|BD؈/Րk2(Wm֍Ţ.R (E|ISpd/=6) KYu%^Êڝ{ߖ;CAÞTqTou9v&-2ȦM\nЈʕ.H=$ cSȑ4]9ԴnQg7 v6 AC~ /zJS& XX@ti|:k2ͽ ʯYG3 x{bZ[''; 1]}~Uv>ߧ&/6+( z=Ԥys˻9kyjC%M\3Vb[_ < uS,bj gQ̮ m[ cH +'WkrFB`)+D tGn 9 59bCժV۟R'" r#9"QgId]XɸH>T4ys_!0LP"w$M7#htdnC(&uV+4O-yKV5j/C3&0o//A {IA@˷t"a)Eh{;Qd =a}`Y9donͱQqɓՇR9l,O9Fѭ61JSx\y/a4c]p(] !v<}2s5JI{&=73eN5WJ :v[곭rV`6O@ vd(@+'c砿IC^#)caeί1ۺ!!0τ囧raxpPudr9h~^Q©{8#㮺^@5/]hp7W=*d> \-d+M4w YD8thx7 6PqST&$vonY3kc~nVҭ(x=ETະ8w <=B2 v|*Flg\{5FLJjdm8Oɹ ?,Z(rvC?5[OwFfT'ѶU٬''ې9k3LE0uO~ggGsɒĻ*Sv|M6bo3MEy8mvLu>xb'&k[ҹz 3Fp j}c)rY$`;`5E{v^U_}c69 z)E4YwP'I7bαC[*RΩJ= ?1%h0XBbmJCEJou:B:yDkg  $uTET6<|У}s9.щ\)UV3Q[$:tCH]oH>E&ke 8e[bw? u$t%xT>wqIHa~G.ځ- fq8Ėj7ے7Xm vS~H\ ţJ>O,nsE[9YT8VHdpR>??r.f!l1EmbO/w mcZmo =mrc2x`c5БEq\ &eMs0x4OQGdi[(AeT`l.Ju0dT@oӬ# @lV`ljx$%/5~+D\󍩽 z$T=b3x%2*;\Nc[7UJq Gy-_,u¯7LW-p$KnBZGa7~) 5^]*5("21ds.L9I|;vhqthM! ]s` 2KXOb[+X0o0`0& D9 P@k_>Ybl/+6@+r } Y9P]:ٿ{|!]U7gx>! v1xlEfYSns H4We3HςwBPsoHԇp2rǝKN C W19b^<|a3l=BWgie JC %(ԪL2*23@;IcQ̻a>U! ZG.4S+ɭzsuju4L6<6Mu m)7'0>.n+Uzta|Z%'K^o.Mz ~ 6. '?/q>&aRLv,5 D+ \` LrkDxKoݿ@I򈢍Sa{xj<E r8ܖ5IPJV0y1?qMyM$sQN5Am5P:*̊.C~x#\-M=MaT KB@nxf[ѱ J[*F"WF@z^3/,Un'\CJ =N*a-z W#1Xq~:Ųel :]B)-͡Ik K(lrU3ihVԇg]TNVchC;0+3W)ظpf={Mg";Pog, G*VPLu+V0e/g~gA&d q[au~`jL1;|znȌ ) h}Ud+v(WUK?/ʿZ97n_:LsTPڌD ?I;RqY5h9 yq)|8=D* xHP /[㤞 Y^zzlݵib0 p 1C-JQRkufhC\z XUKtIu1͖00ކXbfSgԔU) H^kw 2B{8r\DZt(ܾb^|*Edv,bI\<ʯz]b>W*IrI:HJI˜M6nv/?|X;&iO+ wȌ1' Zgb8#ǩS0|5H0卨2r'DW]T1&U0!];L0@l~%P %pB(“/UyK6%lAMJ78< 7 k2m-+5|iIH7s]1- wu g 7s]x7+oŌ+ G-C=[R CM<`'.f܃W[]Xo@qyRIII,v98_/{/9` [})q+B4o\7& C$+~ϨZ16{ŷܐG^!BB9Yv]rj wͰE|؄׷䎺c= :r]。y'3?S+ = gUd3HFrHc35}" o'yڌ&ɻB8.Oי$\inxIOk)9/&Q35ؽ(Pi?Y[; @ǭE\{jSgpzO"2Ny5Ȩl!|,q`+_M g1  ~JGˠ8iy^u Ӆe :)?SFऊDZYH^Z[_֡:N Tף!kZ,kGc4L>S0|YBa̾ sh b͒p4"xd,+(6cԱ\o >Dk|/N}<3&%ZM; F |(t<3/ddX_͉~{:h{|i(xoW5d1fHj;_x ģ\C7kˮ06OBub7TiTPnw?c0}Àr/7Jkز>2iֿUl>a7ܰn2^[;" $Gmz|!eǧC %^o&'32BS{BzD#%R_?G@`{!-={޹DQR%FƫKސpNCox9wQ= ȖS!PiyŽ(a9YJ_a# @=|%ի.q(_=e^EvTS.9!aM1+ASzpJ4wZ%9ȥ|{9- Mes>fpCpOQf^'m+EbRC,e ,y@|M?3?F:Ts#Z Le}dQKqv$yjN9KRΣMN(/z *i(`1T}2{'8fsc8c},% xh7nTˬc41(ew$nLj?%YB%RcXJ` d-SuPՋs "yD$D6 l٫84Uy-BED͆ vmnP[fp/>?$pk#^-БۘrcNg%x0mp:_o`Nb# nGg(+VGD*;F>7Gs:.93g+N`}*GVkmZiz88C2Q#Qʛp_rM.y D!(~)]^lb)}*bj5 [6PGnB;v.Tvcef4; o̹ >ئ4VOM{apл00 Yy,o@cT*_aɩyVv&"uG$F@IA+l8餭H^oU8G;X_Qf'Dz {= KUEeea`=zlUINip!K 'y^{G"W FPg/G@xiEz[jܩ41aefܾƠwXfpc4DžM8!ʾІnp1R1|ӓ}0WG )V?ORz4e)'0lbNW(LGukDT" 흣RN&s!a@$mܘ#{ T}ΟbwT5%"m8u\ξѴQeH̐PS0$ AW'~&с}YĪ5BRd+wg'<9T i;c>T0r >+b>FR[c!kZsUHFzo>uJbJ[RM5(K= d.3F # _L[l(U< 2LFWgSy@Wxn/Yd&Ĩ + Si L @!bjXJ$ dkj]1T/T4^ȝ3S;*8MTCaf3s|-)snG`寎@zMn͖wkdr]˹RDlVe H(;!u A:>Iw~e/Џ%9b[ƹlěs' B!v rlu8z+ gt(Z,SKWu2F;U` ȋAJ#g W8y\) VƆI:{9l/KD?$ixYd&zsGbAs)p厬4"l.!oc/ P+=WyMCjAQvCe~qI(h!ͼŰK'X†Ǯ-JUPn&h~lQ 3{l*>&sy.ztjV}[cE?9p1w1pY*[tPUhKLyIܨ애W 3Y;ò9#XlYs283R=ݯ3.+n_Ύă%=%2R]tׁ~Z!FخKa{V8(beYLIIzJBjnn\+t0'ksm-Éxc/8ƇQ ,w4fe^eA -DJ~b_ uqs]=o~%g:IG`"FV˪LFHyȨ>5qga xi6ݾWJir2 BB6/^[T#&f\a9Zvɮz5:qn7yӊ sH|eiL_K>GĊi)S6w+~Aq}PT'pretMenXZ@y"^O+D2aV/3I|gxMXJ\t+{ =5#MK%vR] 7Skz%[-Qv-_jŽw\%x.7z|.wT?3ZܪMZ;W1|hB\)M*"r}k-FR>wqŌ_ ATl?&hAm&';^|_1M1EՖ!6\ D90q `Βr9*5`xB>06x\X00ߔq [7oMEU*m:^Lɽ*/)Rښ%u vĎZMhX Ў Nc:=6?O/g^Tq+ V؎jzkJ-y(`R25w>lwO88b2b--rң |# kFkR EJg27R aZ_]{Y1Qb:cgݴr t<.2?չ_uw;4?.-DUuidAȊdNN SA(9ԏYPJ^f&ǚ"A*>a'i$2l9iK{ #q0:D;3&)^8W8뗛>q{S2=r\Ɍ^aL@LbpKբٹ(|K}k.-ϡWŠ dgO3 ;8mId0)MG4B$wL ~3}> endobj 175 0 obj << /Type /Encoding /Differences [21/endash 27/ff/fi 30/ffi 33/exclam/quotedbl 38/ampersand/quoteright/parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon/semicolon 62/greater/question 65/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y 95/underscore 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z 124/bar] >> endobj 171 0 obj << /Type /Encoding /Differences [58/period 97/a/b 101/e 105/i 109/m 114/r/s/t 120/x] >> endobj 177 0 obj << /Type /Encoding /Differences [3/asteriskmath 15/bullet] >> endobj 173 0 obj << /Type /Encoding /Differences [43/plus 48/zero 50/two 53/five 61/equal] >> endobj 104 0 obj << /Type /Font /Subtype /Type1 /BaseFont /EUYNML+LMRoman10-Bold /FontDescriptor 185 0 R /FirstChar 21 /LastChar 122 /Widths 182 0 R /Encoding 175 0 R >> endobj 102 0 obj << /Type /Font /Subtype /Type1 /BaseFont /EBIQZV+LMRoman12-Bold /FontDescriptor 187 0 R /FirstChar 21 /LastChar 121 /Widths 183 0 R /Encoding 175 0 R >> endobj 105 0 obj << /Type /Font /Subtype /Type1 /BaseFont /REUDSA+LMRoman10-Regular /FontDescriptor 191 0 R /FirstChar 27 /LastChar 122 /Widths 181 0 R /Encoding 175 0 R >> endobj 112 0 obj << /Type /Font /Subtype /Type1 /BaseFont /DDCFKL+LMRoman10-Italic /FontDescriptor 193 0 R /FirstChar 45 /LastChar 121 /Widths 180 0 R /Encoding 175 0 R >> endobj 120 0 obj << /Type /Font /Subtype /Type1 /BaseFont /CEJZIX+LMRomanSlant8-Regular /FontDescriptor 195 0 R /FirstChar 21 /LastChar 87 /Widths 176 0 R /Encoding 175 0 R >> endobj 113 0 obj << /Type /Font /Subtype /Type1 /BaseFont /OEMFQT+LMMono10-Regular /FontDescriptor 199 0 R /FirstChar 21 /LastChar 124 /Widths 179 0 R /Encoding 175 0 R >> endobj 165 0 obj << /Type /Font /Subtype /Type1 /BaseFont /CSOXHL+LMMathItalic10-Regular /FontDescriptor 189 0 R /FirstChar 58 /LastChar 120 /Widths 172 0 R /Encoding 171 0 R >> endobj 114 0 obj << /Type /Font /Subtype /Type1 /BaseFont /GSCRSQ+LMMathSymbols10-Regular /FontDescriptor 197 0 R /FirstChar 3 /LastChar 15 /Widths 178 0 R /Encoding 177 0 R >> endobj 164 0 obj << /Type /Font /Subtype /Type1 /BaseFont /REUDSA+LMRoman10-Regular /FontDescriptor 191 0 R /FirstChar 43 /LastChar 61 /Widths 174 0 R /Encoding 173 0 R >> endobj 106 0 obj << /Type /Pages /Count 6 /Parent 200 0 R /Kids [78 0 R 109 0 R 117 0 R 128 0 R 134 0 R 141 0 R] >> endobj 159 0 obj << /Type /Pages /Count 3 /Parent 200 0 R /Kids [146 0 R 161 0 R 168 0 R] >> endobj 200 0 obj << /Type /Pages /Count 9 /Kids [106 0 R 159 0 R] >> endobj 201 0 obj << /Type /Outlines /First 3 0 R /Last 31 0 R /Count 3 >> endobj 75 0 obj << /Title 76 0 R /A 73 0 R /Parent 31 0 R /Prev 71 0 R >> endobj 71 0 obj << /Title 72 0 R /A 69 0 R /Parent 31 0 R /Prev 67 0 R /Next 75 0 R >> endobj 67 0 obj << /Title 68 0 R /A 65 0 R /Parent 31 0 R /Prev 63 0 R /Next 71 0 R >> endobj 63 0 obj << /Title 64 0 R /A 61 0 R /Parent 31 0 R /Prev 59 0 R /Next 67 0 R >> endobj 59 0 obj << /Title 60 0 R /A 57 0 R /Parent 31 0 R /Prev 39 0 R /Next 63 0 R >> endobj 55 0 obj << /Title 56 0 R /A 53 0 R /Parent 39 0 R /Prev 51 0 R >> endobj 51 0 obj << /Title 52 0 R /A 49 0 R /Parent 39 0 R /Prev 47 0 R /Next 55 0 R >> endobj 47 0 obj << /Title 48 0 R /A 45 0 R /Parent 39 0 R /Prev 43 0 R /Next 51 0 R >> endobj 43 0 obj << /Title 44 0 R /A 41 0 R /Parent 39 0 R /Next 47 0 R >> endobj 39 0 obj << /Title 40 0 R /A 37 0 R /Parent 31 0 R /Prev 35 0 R /Next 59 0 R /First 43 0 R /Last 55 0 R /Count -4 >> endobj 35 0 obj << /Title 36 0 R /A 33 0 R /Parent 31 0 R /Next 39 0 R >> endobj 31 0 obj << /Title 32 0 R /A 29 0 R /Parent 201 0 R /Prev 23 0 R /First 35 0 R /Last 75 0 R /Count -7 >> endobj 27 0 obj << /Title 28 0 R /A 25 0 R /Parent 23 0 R >> endobj 23 0 obj << /Title 24 0 R /A 21 0 R /Parent 201 0 R /Prev 3 0 R /Next 31 0 R /First 27 0 R /Last 27 0 R /Count -1 >> endobj 19 0 obj << /Title 20 0 R /A 17 0 R /Parent 15 0 R >> endobj 15 0 obj << /Title 16 0 R /A 13 0 R /Parent 3 0 R /Prev 11 0 R /First 19 0 R /Last 19 0 R /Count -1 >> endobj 11 0 obj << /Title 12 0 R /A 9 0 R /Parent 3 0 R /Prev 7 0 R /Next 15 0 R >> endobj 7 0 obj << /Title 8 0 R /A 5 0 R /Parent 3 0 R /Next 11 0 R >> endobj 3 0 obj << /Title 4 0 R /A 1 0 R /Parent 201 0 R /Next 23 0 R /First 7 0 R /Last 15 0 R /Count -3 >> endobj 202 0 obj << /Names [(Doc-Start) 101 0 R (chapter*.1) 103 0 R (chapter.1) 2 0 R (chapter.2) 22 0 R (chapter.3) 30 0 R (page.1) 100 0 R] /Limits [(Doc-Start) (page.1)] >> endobj 203 0 obj << /Names [(page.2) 111 0 R (page.3) 119 0 R (page.4) 130 0 R (page.5) 136 0 R (page.6) 143 0 R (page.7) 148 0 R] /Limits [(page.2) (page.7)] >> endobj 204 0 obj << /Names [(page.8) 163 0 R (page.9) 170 0 R (section*.10) 132 0 R (section*.11) 137 0 R (section*.12) 138 0 R (section*.13) 139 0 R] /Limits [(page.8) (section*.13)] >> endobj 205 0 obj << /Names [(section*.14) 144 0 R (section*.15) 149 0 R (section*.16) 150 0 R (section*.17) 151 0 R (section*.18) 152 0 R (section*.19) 153 0 R] /Limits [(section*.14) (section*.19)] >> endobj 206 0 obj << /Names [(section*.2) 115 0 R (section*.20) 154 0 R (section*.21) 155 0 R (section*.22) 156 0 R (section*.23) 157 0 R (section*.24) 158 0 R] /Limits [(section*.2) (section*.24)] >> endobj 207 0 obj << /Names [(section*.3) 121 0 R (section*.4) 122 0 R (section*.5) 123 0 R (section*.6) 124 0 R (section*.7) 125 0 R (section*.8) 126 0 R] /Limits [(section*.3) (section*.8)] >> endobj 208 0 obj << /Names [(section*.9) 131 0 R (section.1.1) 6 0 R (section.1.2) 10 0 R (section.1.3) 14 0 R (section.2.1) 26 0 R (section.3.1) 34 0 R] /Limits [(section*.9) (section.3.1)] >> endobj 209 0 obj << /Names [(section.3.2) 38 0 R (section.3.3) 58 0 R (section.3.4) 62 0 R (section.3.5) 66 0 R (section.3.6) 70 0 R (section.3.7) 74 0 R] /Limits [(section.3.2) (section.3.7)] >> endobj 210 0 obj << /Names [(subsection.1.3.1) 18 0 R (subsection.3.2.1) 42 0 R (subsection.3.2.2) 46 0 R (subsection.3.2.3) 50 0 R (subsection.3.2.4) 54 0 R] /Limits [(subsection.1.3.1) (subsection.3.2.4)] >> endobj 211 0 obj << /Kids [202 0 R 203 0 R 204 0 R 205 0 R 206 0 R 207 0 R] /Limits [(Doc-Start) (section*.8)] >> endobj 212 0 obj << /Kids [208 0 R 209 0 R 210 0 R] /Limits [(section*.9) (subsection.3.2.4)] >> endobj 213 0 obj << /Kids [211 0 R 212 0 R] /Limits [(Doc-Start) (subsection.3.2.4)] >> endobj 214 0 obj << /Dests 213 0 R >> endobj 215 0 obj << /Type /Catalog /Pages 200 0 R /Outlines 201 0 R /Names 214 0 R /PageMode/UseOutlines /OpenAction 77 0 R >> endobj 216 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.10)/Keywords() /CreationDate (D:20130221232623-05'00') /ModDate (D:20130221232623-05'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.1415926-1.40.10-2.2 (TeX Live 2009/Debian) kpathsea version 5.0.0) >> endobj xref 0 217 0000000000 65535 f 0000000015 00000 n 0000009199 00000 n 0000219020 00000 n 0000000060 00000 n 0000000105 00000 n 0000009258 00000 n 0000218950 00000 n 0000000152 00000 n 0000000196 00000 n 0000009317 00000 n 0000218866 00000 n 0000000243 00000 n 0000000275 00000 n 0000013376 00000 n 0000218756 00000 n 0000000323 00000 n 0000000370 00000 n 0000013436 00000 n 0000218695 00000 n 0000000423 00000 n 0000000461 00000 n 0000020872 00000 n 0000218571 00000 n 0000000507 00000 n 0000000542 00000 n 0000020932 00000 n 0000218510 00000 n 0000000590 00000 n 0000000619 00000 n 0000024288 00000 n 0000218398 00000 n 0000000665 00000 n 0000000700 00000 n 0000024348 00000 n 0000218324 00000 n 0000000748 00000 n 0000000809 00000 n 0000024408 00000 n 0000218200 00000 n 0000000857 00000 n 0000000896 00000 n 0000024467 00000 n 0000218126 00000 n 0000000949 00000 n 0000000979 00000 n 0000024527 00000 n 0000218039 00000 n 0000001032 00000 n 0000001060 00000 n 0000028728 00000 n 0000217952 00000 n 0000001113 00000 n 0000001150 00000 n 0000028971 00000 n 0000217878 00000 n 0000001203 00000 n 0000001232 00000 n 0000032808 00000 n 0000217791 00000 n 0000001280 00000 n 0000001314 00000 n 0000032868 00000 n 0000217704 00000 n 0000001362 00000 n 0000001405 00000 n 0000032927 00000 n 0000217617 00000 n 0000001453 00000 n 0000001505 00000 n 0000032987 00000 n 0000217530 00000 n 0000001553 00000 n 0000001597 00000 n 0000034640 00000 n 0000217456 00000 n 0000001645 00000 n 0000001693 00000 n 0000002685 00000 n 0000002946 00000 n 0000003095 00000 n 0000003247 00000 n 0000003398 00000 n 0000003549 00000 n 0000003706 00000 n 0000003854 00000 n 0000004006 00000 n 0000004155 00000 n 0000004307 00000 n 0000004459 00000 n 0000004617 00000 n 0000004775 00000 n 0000004933 00000 n 0000005091 00000 n 0000005243 00000 n 0000005395 00000 n 0000005546 00000 n 0000005698 00000 n 0000006029 00000 n 0000001743 00000 n 0000005849 00000 n 0000005909 00000 n 0000215714 00000 n 0000005969 00000 n 0000215544 00000 n 0000215884 00000 n 0000217104 00000 n 0000008984 00000 n 0000009438 00000 n 0000008845 00000 n 0000006126 00000 n 0000009138 00000 n 0000216057 00000 n 0000216405 00000 n 0000216755 00000 n 0000009377 00000 n 0000013679 00000 n 0000013013 00000 n 0000009575 00000 n 0000013132 00000 n 0000216229 00000 n 0000013193 00000 n 0000013254 00000 n 0000013315 00000 n 0000013496 00000 n 0000013557 00000 n 0000013618 00000 n 0000018229 00000 n 0000017927 00000 n 0000013816 00000 n 0000018046 00000 n 0000018107 00000 n 0000018168 00000 n 0000021175 00000 n 0000020692 00000 n 0000018353 00000 n 0000020811 00000 n 0000020992 00000 n 0000021053 00000 n 0000021114 00000 n 0000024648 00000 n 0000024108 00000 n 0000021299 00000 n 0000024227 00000 n 0000024587 00000 n 0000029274 00000 n 0000028365 00000 n 0000024759 00000 n 0000028484 00000 n 0000028545 00000 n 0000028606 00000 n 0000028667 00000 n 0000028788 00000 n 0000028849 00000 n 0000028910 00000 n 0000029031 00000 n 0000029091 00000 n 0000029152 00000 n 0000029213 00000 n 0000217220 00000 n 0000033047 00000 n 0000032628 00000 n 0000029398 00000 n 0000032747 00000 n 0000216932 00000 n 0000216577 00000 n 0000034425 00000 n 0000034700 00000 n 0000034286 00000 n 0000033197 00000 n 0000034579 00000 n 0000215266 00000 n 0000034811 00000 n 0000215450 00000 n 0000035194 00000 n 0000214866 00000 n 0000035305 00000 n 0000215371 00000 n 0000035722 00000 n 0000035810 00000 n 0000036233 00000 n 0000036694 00000 n 0000037236 00000 n 0000037808 00000 n 0000038390 00000 n 0000068244 00000 n 0000068628 00000 n 0000096678 00000 n 0000097041 00000 n 0000101965 00000 n 0000102226 00000 n 0000137572 00000 n 0000138075 00000 n 0000154119 00000 n 0000154402 00000 n 0000181635 00000 n 0000181929 00000 n 0000184957 00000 n 0000185214 00000 n 0000214424 00000 n 0000217313 00000 n 0000217382 00000 n 0000219128 00000 n 0000219305 00000 n 0000219467 00000 n 0000219654 00000 n 0000219856 00000 n 0000220056 00000 n 0000220250 00000 n 0000220444 00000 n 0000220640 00000 n 0000220850 00000 n 0000220964 00000 n 0000221061 00000 n 0000221149 00000 n 0000221187 00000 n 0000221314 00000 n trailer << /Size 217 /Root 215 0 R /Info 216 0 R /ID [<60847FB0DEF8E737B3F0BA56968343CE> <60847FB0DEF8E737B3F0BA56968343CE>] >> startxref 221640 %%EOF muse-4.0.0/src/doc/pdf/documentation.pdf000066400000000000000000053500001404105311000201250ustar00rootroot00000000000000%PDF-1.4 % 1 0 obj << /S /GoTo /D (chapter.1) >> endobj 4 0 obj (What is this?) endobj 5 0 obj << /S /GoTo /D (chapter.2) >> endobj 8 0 obj (User's manual) endobj 9 0 obj << /S /GoTo /D (section.2.1) >> endobj 12 0 obj (Introduction) endobj 13 0 obj << /S /GoTo /D (subsection.2.1.1) >> endobj 16 0 obj (A brief history of computer audio and MusE) endobj 17 0 obj << /S /GoTo /D (subsection.2.1.2) >> endobj 20 0 obj (Definitions) endobj 21 0 obj << /S /GoTo /D (subsection.2.1.3) >> endobj 24 0 obj (Getting up and running for impatient people) endobj 25 0 obj << /S /GoTo /D (subsection.2.1.4) >> endobj 28 0 obj (Getting up and running) endobj 29 0 obj << /S /GoTo /D (subsection.2.1.5) >> endobj 32 0 obj (Beginners tutorial) endobj 33 0 obj << /S /GoTo /D (section.2.2) >> endobj 36 0 obj (Basic overview) endobj 37 0 obj << /S /GoTo /D (subsection.2.2.1) >> endobj 40 0 obj (Main/Arranger) endobj 41 0 obj << /S /GoTo /D (subsection.2.2.2) >> endobj 44 0 obj (Mixer) endobj 45 0 obj << /S /GoTo /D (section.2.3) >> endobj 48 0 obj (Tracks and parts) endobj 49 0 obj << /S /GoTo /D (subsection.2.3.1) >> endobj 52 0 obj (Tracks) endobj 53 0 obj << /S /GoTo /D (subsection.2.3.2) >> endobj 56 0 obj (Parts) endobj 57 0 obj << /S /GoTo /D (section.2.4) >> endobj 60 0 obj (Routes) endobj 61 0 obj << /S /GoTo /D (subsection.2.4.1) >> endobj 64 0 obj (Anti circular routing) endobj 65 0 obj << /S /GoTo /D (subsection.2.4.2) >> endobj 68 0 obj (Soloing chain routes) endobj 69 0 obj << /S /GoTo /D (section.2.5) >> endobj 72 0 obj (Track soloing) endobj 73 0 obj << /S /GoTo /D (subsection.2.5.1) >> endobj 76 0 obj (Phantom soloing) endobj 77 0 obj << /S /GoTo /D (subsection.2.5.2) >> endobj 80 0 obj (Soloing chains) endobj 81 0 obj << /S /GoTo /D (section.2.6) >> endobj 84 0 obj (Plugins) endobj 85 0 obj << /S /GoTo /D (subsection.2.6.1) >> endobj 88 0 obj (The audio effects rack) endobj 89 0 obj << /S /GoTo /D (section.2.7) >> endobj 92 0 obj (Automation) endobj 93 0 obj << /S /GoTo /D (subsection.2.7.1) >> endobj 96 0 obj (Audio automation) endobj 97 0 obj << /S /GoTo /D (subsection.2.7.2) >> endobj 100 0 obj (Midi automation) endobj 101 0 obj << /S /GoTo /D (section.2.8) >> endobj 104 0 obj (Configuration) endobj 105 0 obj << /S /GoTo /D (subsection.2.8.1) >> endobj 108 0 obj (MIDI ports) endobj 109 0 obj << /S /GoTo /D (subsection.2.8.2) >> endobj 112 0 obj (Global settings) endobj 113 0 obj << /S /GoTo /D (chapter.3) >> endobj 116 0 obj (Appendix) endobj 117 0 obj << /S /GoTo /D (section.3.1) >> endobj 120 0 obj (Understanding the effects rack) endobj 121 0 obj << /S /GoTo /D [122 0 R /Fit ] >> endobj 126 0 obj << /Length 157 /Filter /FlateDecode >> stream xu@ D{Ŕ8vnH.JE J )P$*< 8.SuTmRbGm:dIKNbs6P 3)+]H]5O135$bЯW0:*:,4k endstream endobj 122 0 obj << /Type /Page /Contents 126 0 R /Resources 125 0 R /MediaBox [0 0 595.276 841.89] /Parent 130 0 R /Group 124 0 R >> endobj 123 0 obj << /Type /XObject /Subtype /Image /Width 1030 /Height 722 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 131 0 R /Length 91063 /Filter /FlateDecode >> stream xxHݻ=iϙsϞ{0&`M9gf 0ɘ`ls699v90:ju Q?#KT?U*C9m*-ZhѢE-Zyd34>n'݊ݙ~FZ8J~WEZ(Qk?6I :+ ~ 9*Ҩ]Zy'ч8y\7./{)3Jě}g50j(/<B/h HRQr*UJTʔ*BEJUTTLU2ITQueQS5^UZjTW-1ՁjjTSS#^SM"567IC͘Z[4ڊMV+&SL2!S':TKnB>= `}zŤU-H+_Ar&ȋAK.R?݂WGqUE0)zUy ʊ!*XAFRT9HXi2Wr3Jv~իKv>֩ =sqrRS^~ wPT#_^T/;j%zS\Tru%T^qLŅT J\Iؽ@v_|;zݪ;;#P/ʩ ۉb^X xMZtBD *5B ڠ@;h@ TA-TPB! h(Rn( =@IJFB dea@zX`:>D|“'āZBVDDPND Q'r" 8߂bw?d&ɭ>lגFP@[Xle)E\L71! @xуmmm tuE v. S@` Ptq>hT.tɠ!l1A+,$-nBO`t(yMٮ  au%6ā>t(6 )(v9!Ʉ__۶bݍ;}߳;W/ֆdd564hZ׬tHI(Z/LYPS@A 4BՁk̓ߪ-"M сJ: Z %15MH/`ëSœwH6 jԇU)Ʋ̻j¶4zK@eg7Cc@@Gg/솦mCk}eЀĆh8ykw 3IMN28{9xNCS7n DIlu"4!:(L@ f~⦕槿ۖF[IYiLV,t/cZ$Lп]?'( SqATo }ctxuݓ3s/_\@D|.^ q\U %Ř֧v蓇~x,0^;"m\@o>h}" UD_zmTeZ-o2 ;y?jljHi2EzSP(ϡebk0Yw긿;$>( d/9XAN 5"hT)Z7vS 6Hn=[4`Sf ] gf~9du`S-0 F V5m;g5]r6{Na27⥰;ү%tj-h(R}X@6BF5Ѐ1,9^Q:.f4YfՎ[t]MQ#CB'we*Y-8 kkkYZ̞a>s4 dJR!_(;wX$?vwA`rcfU\'U׵+E9pZxCzmYѢJ*#:,$DʵJ"@*RUT&z2yꪒ+U&\PBz /u?P[Wj&Pmu=>^5 x^x.K-^%pЬ&DM[ 7 jj/׎q,[!5/#чS9l bX ]MN d(J5ޥ6V7\%O %ySvp|}nFR֭Z(+#m+YUVHu]Lܻ;f䈼ܜV9aW/ ΅S'N }{véDnCX2"bBPr vw"ބav/st-,.tuvb))/.UcŅyEy)ғF*H?~ K)"PPmk$SV0fRG=D͊4HQH8|ԤYQ6D*e*hyXu'nt4\7)hpau5 ֦nD#w˛Ѧ/O:]͍f񰀏,yy I'nr7 7)JV٩t+)w|3+Z?)KҭlգiZ웅-mcd8,?ܬ4m*.INW>k(H }x+.."jҾF=5#醬 D&퍋ptD0l͵u5_5!Mc{IZ&F6-]]7ONZcݬJP{۬,(5J !P;͝mB(ɡ$ⶎ볇PJ-mWEn(ӓcڼ~ S+"Z:_xb3%v+Qn5GC܂uӐ:,: Á hJ t5Vӏ,P8PTY8Ч?4Ҵ|nޱ3lf p(LW }39y;Spj̙=$ƑiVL1}9F}RBz^(P@ @b( H?P 2 Cf2pS.fԂI8:d܉u}>n(c9jR/Q(&T5m vlT %C]rRP>cG$ L,$H»Pͭoԓwp 5hIj]'TJbNSŐ1S[P{#qvf|yU ?f]Ml Xa[n6 TWnCC-m[F}!mn15ͪCJtNA@500,(PG0$* 8@pܬoߖRFjxUH|. ⺺ZWLDLDϯl9s{6aao^?ӟ^~1?73).JSY_9@HIW*Z B,'.Ђ5%" DE} |5dR5㠭okZįdf&nԯ`dImmM<(2kwҫ[0*WT X'(IJm2 MyO}Q( kUKVQyy$SGZInѵgVFzbV1AvFt`nErW˕Q)EO7M J E jD\`hcBE} lYtT3rk+KTMÿ UYX\E 6*",6@kmեcA6 gאC)@'lԇpA:Y> СZSoݏN"3%L[WL]3}o\v|4́+]h}*PqA 2 P>EF Ƞ,OIB.MP:mGboq؏@NA `4O~LALDݟ7ۼ`{=wW߽$lf^NvZJRRB|4́t kD\BF с&Fz1) TeQɍzn 4?)BgacG>@P@?7Y2XP!Z>[( h\Gh@%p`:&(I2,"J To͔ 0  YZB8uJ"h}YqU<GC5,2ـ ,>2 ȲqAcdij"Wh|U5:,2e|d5j"YIHN:2FDء?r^0դF5nO 2j0X DV ZGF4c kVRª W .1İrpauۯ60( kSL>a1QVX!}8g h\炲$\ &..PhAUE䀠҉  ̈́@ JPs&ZWnieU@׉Ơ* 00@@ZY@J ,@&ZpXс (ЁHE}jAt ю*PMD:@; PWQ# @L P8hޯU&ܾOɿ@Z?N!C$Q@9E @9P`=Y DUiD@o @u. J#h` D>mt>`8P课&7iQ@O A З4 h8<6YT& ?/)/IzÖRA+g "=*[YHqD*:z+P6{Q^jIY{+Y!\K֨FOkv^krQj$?d {Oo a_:Xׄ)X{`{v7v&zS5'-cf򸜶֖^O[[H-}KsMKK'Ƌܖ/©~ EJhtPw:_@D>n#paGpZ!Ο>PNR PN@>9> `het8,fI۷o=ה$d9%pvc"(-Ʉ-OMh.ߐO!Bc CJ+ zE`^?Ug(xy~J_'z*m  o YV);3 .Z "M=!>=Ug3W8~=d 8_aӫ-e߹yK-~u.p)i"274A h b02)P:> }afu<SuͿ# @I0|G|.;2MooF:@2HhocTWJJۮ\ajkmmԀ좂v,=~-RpXK-i:h. fS#B&ʥ?Ru(`u5%X;//ggE2^(֛3,.)bCBLY+T0\cٔg D=I0t=%d_-T*LC{ڌZç mD\U#{qp'"c>\ !$%kr 6[v8R!K_ %eW-FoKGvD@%7d;VX~})qT~|$€]'3yS6vsÁxwlM{/00/Ɂi]wԜ|;z}"1ZȀua氥w* n棥#dedjhPۑV=+n'MZDqX5iSV(zP^fVSm^KI!LJM@$ ;W@V\Z 5HtzjG8qXe9:SN2D(^};Qs8T)L~MuS1;DTDZ*Eכ\ AU)d`aq,\Tɿ(dGz:E8@˼#@0/tP֧BD FuҲW/1J䭀eR){u8U4[ rDƌ8v xr/A_ODQbm}? { =9}'-dsxi/U{Q `aC^nSGpgXm!4fk"]hD0rѥ-n& e37Z?<6ER4 qfsͿ&ڲHIi_D3o-6xpRFU<Q˽yztID Y0j\񜉅$$gg|vQ\U|ڑ Wdsb6~ɵs˩m4ޒ/Zi}r Vь}7vfW(`eݢ;DZ*hk5 *?Gk@=},Q  @7ER!Ԉzl\nJ旬Zlƍ0i{[&`KAnX֮_[fj.A|KwhK sף XDy>1"UU_lO2v&-^ܵp4yRIG[Gƍ]WO?ϋq[f+T01VYDXiO5د#eFt -DY沜 =%F֛~i2I]4_{9U]M}-Bqqޜ XIe^+?jBŪ̬hƣe \0 +m2h򳡬rJ(MJ%nqNuʖ(iɘoxv6VJEy;^K`TpD#r\i%);|Re?A¹9b;4vQ\ɠcS&ؠK 1dǀ(-Z}UP!{gPp=)/@2HhjlJu֭Zj646 3'EzZ 5BjtqU+JKY@}"z_u)$-I{lnÈ׶υƽ5~`յ+FqSnnmŠj|A1]>m̓$a?aܝ^J4[@Xy MMˆC۽`ҋ,/ZgB3p&`=a`xA'//e8d0꽫;y,s_mGg)\{j﬋)ji |=Al*0[:EXX)W,E5q&Dn>_1AV}?S*e90z"ݽͨP q'.Q yëǰʥ+gV`EN䰺fsyЪuo{ {b` 3XMP%OvaY1xӳ&mvɁ9L]lvN[ }c

JZ^QhtjY>)Ebe>P1 3 oY@(iaEcvc0|>6䯘Y q}v_.) o΅[!kٝUb=a *Z~lrYus0lfAEXʪb(Q{.\i2M֚-~P()z|n$㗝~W$6x_qya/dYh2삛db~[(,Y}m4sr8?pxӡ>**=4m 9{K+nB4 ȿROJJү̋-=X6|>Rq1rj(Z.nЁ˜h]iSsqCͯ%!D_uoٞPGh(?ݍd֪*puL-}MuuoooG{{wwwWWLRK۷o!qsSY[mK l=r[Qy){|%W*+W mG[9Q 'QʭX1;_})~Ñ㿅9xL2b%,pr&(hӐy|\vZ5S|Z y+m0icsv8d_fs)hP_wbd5jڹhv& y Eows[0`'_[?vR(y-֮hi“Webi16x#!S*yLH˞[VO_VS( f;cÑ9j#'=;x%c,o4 v;5\Uc \Uv[< .5+j~|3\Yu׺1<]1]QvL JU4SB**3/AB**\hIn'GNY;IEX-οaJurD\W'rɁ)tyZjh/1` B}jD)Q̄ $L}vVfYY)(+3ų/06zN#1%LDTA@z[.]˓=KzkK9I Πb-)n$=q; ۫[[^&W2mkL+`n').nv+nuW-Qo{ř܁;0\<[C2{ef0&dϋf}Sq@U]~s̟Sd')=KttA)7ʬas9x+iCD퇸8pd,Nl4xnbk7Znse5qx?Zp)AbX-fy:zC= X7z,^9`ky"NM=XE| gŠExS [^!lg(^lÃ#J*r׆&ɬyrPAfWT 5UpB9.f{g;LmSW}_B=~R2Q_ ^4iv9%(ZP)m|iPg^L[jq 1Tyl/љhd/e"Y/.1ph۞sH+Mz^8p܋}YRVJzz419Y|i9.G̿:Rt-cr~p ;_$rDt5uˣp] yl 2`Tʻkp%wAZ3]~X2+AC&_-OiY,Dszܷ-\{r6#|k^hJ LG3t㙟ר ?'=K#l;H F 11d}3^4;GC=0~}׀QG3s2/>zh <𹬐ÓM *B7x"Tl3͇![7r֎끅ۗ{~^gT_^5gw<=%5$ ЭX'h E$#E }"tcRVY \M2eTUWU(J朢z1~.I%aK~O zO zo,O#-ZBD yZh U'LDR]&טXFa#Ypu@FOI@݇FpZN~^dڤP_~^qW}yZh"Eg=0RU^Ї4؁>#2}z&wϯn ׈FZh"E p`LR0( vpГs4͙Ennz\ ض_/0}?,"K_D#-Zy"ౙ 1MOi+pjrⓇn^ u  V|~zr]} "8@@ B>7).TzjӇ Ed!Cd Rf_3LOUi)sPV>gj3RafZ*vpz'w~=l?O?wVq|}Oʙ[ sGp@o\IO8=%nN^|hw'3~tc^ (/;o2S^*\Q2HSxKȼfg)YiFPޡF+aZ6@JR\,Z syR(̿ ;S 57*mçL3]_;vzrwsA]r218 uȤi#~#1&*3%t}P8PKc"E{$ce;v4Rb#g6Co2_Pca?J2S 7uoI8LgfsO]hªy,..'TNACs@3Ce.*09!>6:2&JȈ訜 }#P\V ,*RQL,T99,5)};;HNK1X8dB[J F ).J)ܝ2TKe(ǿ ~I} o$o˟ʊ(-mކC' ޿1>9g푟Vy{px|)^RDGDDłm[BLGH"4͜ar*9h*%)!??twu577pX%9v-6xxgf`)-+p]vRd0:rE*)*).E1Q!!Qa1oCc" r ^iAnNp޾w經ː<â*pMON˥p^rce3 ^j jXg B!g0fcҞP|(7;S$kvxK{G7{kPȚʜmE*UU%:v)>Âaɓ]*]q;jk㽽G~ۿP8R3d|~Q[hldD CNf0@% {sJ4 *% d0"  `3I F" 3s&ha0 %MLLLNNNOOA?III111vïBZg&2)Cjǐad(F~5?fTکP{~b\ ZH/)T f~ًxzzTdž7~&e!?7/^k3侥?/ _ݽc0ܺqS 3^6o/?ï vk3];AA}!`RPaZn߁<:=Ek7V-rsj2jtAmƑsKOo{ _~;ovwN#4*~Ņ(-eŌju(a0$Fn.#4N{D pg0BT#D e0b  ԝ!8`\pyyy/TlwFFFQQH$ҹ[hUA>6E>tFQ  Zۭy95>/_ѻw߇̜9h۶'N~bֻwo8x " adfB]dHnFS 2*+%%xN{&LxQ $O#G:u*22"x/O uh݆ݽC!$Cm zĉ?fO a~fRL2XhF4_a2hژpbo-~}z&j.-&`Q]Ȅ Υt~^x8A*yxV {oii;mÈݿzM/ 3-iys>ID2JKKRK]}^ :p9*&5͝iƘަNL -uM5-]1I[7oY`a|LM!!9LOL13"$(62J  py `5D@ %`8w"q=k3n^ `߽{ G"b۽sxep^[[[@@ʕ+ 0W۲lpq'O}&F4IJ`PPvG"ߵڵw&1$}Fx8#/Q^hn5H8+(*B ҙa jjߎgB" ی 90 70:Ќ@ ,l @l+0_(VT45É|>əokm555s6PMQFrpo"d9mI~>s2#766­n555pLo߾ 5 nCd(ކɁ ޽ ay*O> `0&L_A޵6 3t^azУUńMUoٷ,g,kn\;bA7'Ls)7akW675^j_E+(JpO]UCAHd ems|e$",mW$""bqМ/< S\r \"@?@'0G^^Jk E&de9wΜ7DƧ5'A 8#kOp?o~\LI ؾ|&jS4ȏӽ ;SNzjtxّH:vIGn0.<ANv60 TŒ v]O#06:bݚa1I-*P&~U><^!4: >Kx(ā@#c8ehXP_<}vw ӹ"_Ow︽vr[ׯ8rrx};WkM" ,0" }@=@`$9zٓ'h3FWgrmw\>xDlhtk`G enq''Cdfg:y"..Tp@AAp@ HEv5nН:+wAS ?C}GWw[cKKY(Z̐ƈDb@*CNP4:"'ɪ1{P!= CP$Q07LT0u3="3%>agHI"t FA !& ɉ@WH%nߎ>p"s*BίmW| 5kր χdXg!L,`\<%L,~ 7Sg|OCH6ɷo߾r PǨ^{^!0@Sxhzw== !$9rJ@gLT~zW&&,=Ŕ%ayu ʹ/_^0|;8~t1re]1[񋏹g0Ї:t#Sz׌ &+ͬKlgկB#tD t.EYv͔ɓs" [7oD̪e,J Xtpm6g'#I^Y5ُ_>ڣS-8#m8TSSsRRRn:Kxd,9̞9kmPd<>#U(-D*gkZζ4zK8aD  5!.6"%+=~bv ONIXr? G^<Ϗ\N?~&""@E"P8Á֚O>ݺ5.2555U<.).r8 ‰QQLGP۞Nnyyevp:kVoټ npz9r}4",& phgADI[o+窶B`LP{-,fXYY}W3gܾ}{[۞.Z;D4ff$[ƻw~.=]g%U` 6,&یs c}P:ډ8b,XA5YjEf7Ĉ ;;́\.tfbȑ1Q=QnTDpݻwSN!iڵs̙1c8{:W+'Oر@&`ܨdZ\?]V^bŊUV3:oCj%Ϟ= s`޽{L|ΝC)2l[(j.=k2U!\!A Xt)TA?MOϙ]:'kߪ_83vya i\n~M9$>4ەny<͎neur-ڈmzK1[Ʈ kn [3f} >=a ]K~%TJ>D@@MJ"q߱Ʒ~_gƿE2ݏ]B N<$:+(?7;GKg.~s '{1|ƪ+}CcEb *y\^Oh,$|5W$A:/7@Ȱ0 7kаؘ=ysE7]usrrsKnδ} kN/zv/0u"z^7Up*mM{$w?}c0'B}Bc ^}aV cX`{}GWL"힌.?t`Ӂ|pk {{ݻ1NݠAH' ~jkd(w*%g*kh@~اWǖ1܆3}hu5=-{ , E@bG{잽@E!$t`C&agggN18 'tAUh.޽{1"@ aʢ17wߔ=}ܻo.Ѷm˖6fzOv~}ʕ+9NMMŋO!;::vԩs 6@fgM<7Ic2S/`H/&-}Pa|"_Do{ntW4̉lg"e5rn>bL|h8H&fq=ܣ}f(tVCs3IaH>.Zqpu0<$/P1i&#ZwX2'x߽(ÑY&d?8n{b|˨р1jA`F@څLˈ r|_/),"6*<3-%=9"gOԆ] \"I?t`];v<C:qF`RR #h_˻vt$wGݽKҁo V6ppIG ~.ŠL)#p5~ۡ٫flC|k5ǎ[LÇ\OND:@f#W"Uȱ))P3'G)#xw?m߿PSu]ݥTT)/?@|7[W'C *+#"4RŅȸGJg )gR:a x\Lw 8#:;-KJJ`[HB߆br(y;s <<՜Ūa/ϋ =: $UV㏟}lΞՒ| 3 X"//ѣG#G$ҥK t/[8qFȬrCCCܠ/2,, ʄ9 -N \x"P,秱@j͟?Z_wB*!d IlPo '5-X|ڵ} ~uoloJ27`tS6F'P{l{C]X)po2PSr{p\"utvS2wOgZ<)5ÐqViOdg )ftp:eHx[ZD@pRDXǏR.-%<{42u) Ұw$| cg]W/F'Oi@lth!w<@## ]}5t@F-ϞKM"/)Er?{|ԨO>'&-}~޿mWWkj.\qTNwZǏ'5Cd"ɠ.`1kîI%H5J׽{'/_nQHDƥ}c> Kodb'%N9SV_NQgN\˗7xj_`۶n1c:0 7ܹn߾=:zΜ9Y‰PxP,-Pv tpp8|0bkk Ş8qBqzjƍ5vCrBSiH+=}y˯q:׵.ZHq'5,sZdTS@-ewA8?^HA0wn^GQG'}uײ_+fx,MX/bWP$`HO<ђLz~8w~;foQb,, qۻS8qr҄lmAVV1|؏_1 Q||Rx,&+K&Fk,$*<,_zJRVz*NH#W((_ܢ ߲oںZ0&ky@v\e[yFe_ H:ǖK6\o迀\|վeK4l^|WC$yFk+++Z$gX|/ۓ@6l؋/^>~[SvfF&!e@l]X F#P&!c" +>/@3v߷-i/iܸAJc%GŧTDŽGp. &X0SֲT 0~7CF,+#8w{LLLyyi,--NHrrA(**lϝx9jKExp%ҽipׯ_'D-p֬YP;l!N-![B9uTi^z]bes-Ёw%!dnFZC+Go )t9Y#hgwgB?>QTk1|!AuyڡzI]\Tw;_~^972hf2\@[b/>& @.#sPЀ1]ʴgG0:Þb;u'?{KÛ7;{矱o>^ xwa?3v&+,r*|!@M 8)0-HRe(g?)O: "V"ecC㳆j.Ν;lݺ:59eee~h:;;,Wcoفj+7jC-7 hI&͟?XXio>H_r%d/@_;,BPPRU+>*rĉnZ d!E ?r6gΜŋpڵ 27sw_Pe=;t/]AWuk\z3r+7 i^M \}ak )2U3 {Gﶷp ܹJ3;$6BI3 i*(}FNh(O$S2R?a <>T-5X`U@/&b-I\EKq^2<|p)?@ҁؽ{FFc\t)p2@6GsnM]kswX:02Ѿx+W,{;|%@!},[Dz^$/6a࿪~ 6q/ j*W?PEެՋǍ>y7o :tv&&ϟ^ 02)C^3 lzR#Se1&5d`2F\B ?"wu %V+B< 0#5Fs󃥐e [פ l(f u :ÇQo(*+ <=/|ߴ2U.zyڵ^[R?{,dlZ^Q N8MMnoK&Lسgϟ? [c< vdL'yA{&@MPTLMS~*a)x{zJ%/^FЄBH9#1g)CR:Ԁ`ː IpWwŞ/}{#Ϟ|#J2;lb"e1 wČZ2 BU\}T8˶_|8Xwȋ8 _tYӢI$@*vL={Lco<1Xӳx1ǎ99 Zll#==&4PM @qÆѣG/X$r`[TTb 8o@n('+c Mn9ny--R,pժUp2/PWa,}(5t>~mrϤA-e ㌝2k߽om@ڥxAAAP [rBі x <WJ1jهfʜy0sΡYLbgôKN)0=%iPLL}ԩgϘ>_]|̙3Ǐ>ּW]m,LvSk@ꟑF} 8] d@ӁFW;|q))~"/Იl&c)DzS b"#`dl O"ܬ˗rʥ*J"ƍ)-T-{w#iI7ލs/sezvOb =8ʴ&oR-?|T^kk>ytГ'uuVUU7--5X u ޾U\M,;3I\}rPdQlmٲ{ݳgΝ;o߾u͛7;;704w42j]@3C(XKFbWo:->Euk=rWHP@B\ 8˯j]G!AAZJb_<)߿~X-EOdIsCwGYפ֤& u?~]vCEűO?D"ϣG?Xݸ\wQ;&EE~XŪq"P5EY1)DN{bgt`7ǰÎCAV UUUܼyo.//Xic5'bk;-28Ÿ۱x2TcaMOƜ$8~ǴS]Hl@UP|TWZ9] 膚Ol#ȜuG?o<`[ΜL!֑C1tZI1Q1@>MaRFB.fcEP s\DŽt "^ n6@-}|r[yG*+QQD'^zx1Hrt  F+#h<7r󼷗kW}W}̖EoϋStP`YFPM;@$ xܷo< 7 'H|Ue^uX{9YHP>s[ ' IS#В||.;͠g|@04sg34`TF& #ƒg<{itnZmEL ^Ǥucold!teGW\e=z sFFSA#@>,ё_z\H+i\e>Ujc\fUKz?駎鿅m& !FЀ1#]Y5[*gfdVPuU8#0awp/?^| 6l@k-i1ۚ*^OkԺ֥cc6k !_>C^z2MwnܹјѣA~ (.,mРSw^g [mqjh`d5> m\Uw04 @)3ܡ6F{:N8k/ŋjŸ4]\WJ3|8xt\P,ȰƛmLT67"(I4ei%n&BZCnl ͉:a >7~"t6Oxe<剄S͉flN*s-_iQ JOp⛖)K犚ŋv8אDaEy)+|~[| <#x[~RӚdn^GSוT@;ڨ+J|>tN"IMld7r~|I6&$|muX xA5=ٖ.ꪴAmM}{/kEҺdlSiթWwC[BfKB%>co8/ыN J"^x޳Ok "x;IoǍܿ1~¦@bMph&Xk-g31w֔vtT$4ƍ}T[;ͭK11AnfG>@XRn󁛢^ yVGp$~|e@B!F;ޝ\c5ݓV8F`d`j9󊴋i六3HH6'b%\Zfg~"[ F#Zk)j95SQ 2nޜcx555[UU5i,K#`d>JYC\`EFStn k ^5Pqtu͐yWmJ+ J(Ⱦżaa+|igQk|2Mwz 0૏D^.^UYQ$ʫssDb!ҾL5Ca{F҉VK"yEln{#9cԽR`?u){wOT`#-Oӹ> )F[k| #0xlF襐,< #i|<d6uȅ{2GuDmMUtTT.]]n=q*j6͌z:{r. u-TݻM,Owi#gP#PKe|P9Ö{2lٻ#r͚JY;%/xaqnQ\51EtLUٚ`e$vLӔt-jJ|u7XAjƆCj.Um dl;leG|:ӌ]v|<)c2)7`͉'"`8(-?5=J`( wJ<5^닭IǭUz7O[D##~AJ췕sy⽹<#{s<%OZC!#DI$K : [cc.#=Z<+$_;0IH,8ǐޝ2 n2ʼS Ԩe?nCKPTv;ixR/h9FSu\$Tb`ŠnfDYP1)u_6pE7KKÅě7oݺ<;Nf_}TO.2 #K++]fsya^ooC_s2VP)GpkV p8NFX bC  kĐ)e|e@mN/GƕfC^㯲T?z[aKp9,hsWI;OOd|J({E)xG[/+Wq;s#W|589nD6 Fݣ?wlwn]u543j-F T1?~DM@]z1PAQOVK'u1כUVoguL՜۴nv4!# ^5ZҖ[Ury2j1I똖]FQMQӔv:1UW>=^8č 7mEA]H_ikR&3N%ҫK ¸m#h,#(`6l*wR/E$mì Pg 'KXd+J(!7ducX|NI iX&:Nd XA+."nF.ב(MTڿͩ1CHQ jq]{28>c"nu0[rq֥&}=h|zyzt#fHW9*{w(z!{03h𒲻Ł3LmD nR/.6jOdX#ǠSzT3#J5jKpψ=jI?#HNJ uuud@ĉ[gmdh?Y$͠'J+S F@yd\9VF .2]ob^sns024ߞ~^4Ji$#g2p oWfZt!-}Gquo]|T2 #O6'LЦkt5*#Pi]N@iսTo]t]RN[t:3#Mݯ04k\Ƽ՗9G6 N{(:t^kb bJOCȸ6rؖ`fn|suo$ԖSǨc7gXݺ: _UUztpT1D*"#P=ģhkt+%nl@#I=tSh>󚇻xƠx4PqapQGCEwdjG &*ߕ礎si]ƳtUN-fM |_#BZq1<~BsC|:w}ez2`O5Dz-ʹ#4;fԟF2~a׮F\}r]^7TT7; պ'y ιnE/AǴ40.Fui.@'F绺UΔVָM56s&rnV~V"տ,7dG2_G%l1=Zv52j#e_Xd?LQt^Yc'v1@jH{ÐӼIdZNF11]:vVSS6ŸSĄx5[#@@W \2F@, ԋC[AПܹ痘˄r36=Fpa=UwiUj @ H)m#5~>RsO:]2}SoZ|G@z)F@ ;Hs{T ,FFF3P%FöP(@X/eg?|hm $9Afnn#3wz I]]Iac1.|Z:BKcֈs>6;'pr W^eB!hx, <;)''FE'@69>0 PEhꘞ[G E/#PGi~#heGX<ۥ7 u[I?At :F H"Fv@<C@@@@@@@ЫC2Y>:yBaP@"Q2$MA@@@@@@@Hd&D(eBjvˬ5Ms21#@1>P3k(?3OBC$HzppHH#J!Xy@|N.@G-\\q a2pt1rpsH6[ lshl :F:\:2 &M HFN =ND8 3" in @ǑFSٰ%%HO%wSh2%5)-5MJIđJd123H".=).#HOd<=vcĈM'#h "A\bR$wcc"q#bp$DŽ'A\h@K ÷Qa Q nBdhBDG!x1a#><" $#40&40Vb7&$밍__vclGi !n<Sܐ*BcGM1 <.nɶZt9ў<'; IAva8ޡH:!{hJ7 gW}Ghnˋ;AIFtn} ݨ51NBG.;dˆ8D$]9ѿ'fx׏;-5Q! BdK< `]{d铐Bwi&]n!}!8.ud=(RK!@cߌp8r,دyR.u )"C@9R?Ŏ(qUJ-rb%7/-q+.^;p WkbB-đ/,@T,B7dPH1 (Q*F7ĸ)F1*T%)E)nIq]m*n(CmRqew2yS E?Tʡ\RTVHQ%J*z(U52Q][#G55jQ {GuTԑxb% ޣ (|ᝲ7 A~%[P8SzKsI yJOI@)݇|LAO}LoE_d_VߵQ;i'HɾR%V%}nMbw]9޹+t8$. u!~٠z r (z28?<LSIOUvGzeRWI:OOG6)q)ou8 /T5*ue[%z7NgxѤ_;ز.78+.뜃.ypU3yyy$@@@@@@@@@@@{@pFEfNJ@d0T (0cb@#@@@@P@cmZ)#@@@@P@#@ b( F (5#P;#}--u9CFV[227T`#@1P@1<|QLp gw^zzn$#ȗ{Bu" (|z4 `&s0acǭ&'Nw[xCY:nEʌb( F (xF0_0lz zmhdh`ԭ~*3MuÅ!N9mfnb`l"K낐 *G1#@Ph##0?] Gq:roodۼ LV:-T䤙:twcPFqpn C. >8b (."#Xq`7d\ n|K9zcoj6`p<|Hߒ t&.D4ȦO>SlCG/ dId1P@P@@֐GDdeɸsmn|'4I"'715Ȫ=DL':t5025:iJŢn` EuqaNҕAH)d\<1g]Ո%ީlh,1D>=c^a(?QD7Ƙ0i͙KFYtJ8k5Nf|Mohiznغk}3Z.%CfzsaBwS=3ES :mC9@]ԉ F (10/<~wF4T'q ?ܮ#8nSmM7߯WG<M=8'fnP(j_ofrVk=0͊+aW@11ϰmlyGS}]ֻCnV;RsgXx_RمM|⸁hPb<|Lკ6is] d}ɸZF@O;8+93TV+O0|g@J95C%E)8t&e⟃{OQz ,";UQ,"!F=pJQ7257oV̪uC1%+& nR.ϳ;xSlv$~@ F (2c>tՈ254<~Њʱ-p`Qf3]iSf>7#8:R)Z@ro51l"fq #;úӠ>-f/[ۮȥC?Ĭ!Uܼjp/,.d;##c{#xIf7 cn7KiGZ~YC lqxf\0ڬa|282b7qHCgXiYnDuqfHh_2F"33jbau(_2,$NWQc7xd`fČ wʯ]tb)U^jiN&u\v0P@PhA#yiz]}'FфY˺t0lky'cz4mSݔe~YNd tVe4k~N j3TYCpJF&{%9 Xdc"*(s640j7v_/%qU]~ e@ef;w7,3&`{ƉX6';/ OֱhxtEeҗٌ浶YC9I[yi\6=ȄSji{l 2 8L=>WgKf5v#=-`t9RQc 3NMt_l"=^~ڥZ˷[|&#W'eG  o"^&$9Ib O # 7B/?(@HU/;ۏQ7@섰6YL˧]nu((?KK;c!}vp0^4's nvБ< !Ĩn#lk H*}[8dU7: 3NwnCAsdf &sT])f q#||=zM ;1}ڴSMS]{RRz-8NˎPႤ-c9~AS6ty '%/mgGK[#іAğ5i?`b7Y˿/"$/n?mq8t] DVznj$l?ʌUX%cO n -+/ˎ;:þ, SM\Ez}xn,#P;'٬@YCml<'r.N.)7{IkGΟ6ġG;v+xdgB! k9xW,u5ʫopy>5d%l48jtC՛`/vrku~0#PYcubH.D:wŰS|&vh4~غk*7n93)_͹Ynh+̏gڕ*է8=NDZN5VobʴKiNHّIS)oJ?N}pUz\<g~8F60R:jɟ9z n"KS@>5ߙǣx>yn%wU)9C[ɕ^["``8N~kC/6wiNR2@br*58Lbj n ('یYs< &f$_2f,O_%x1[Y?CKu|%2I&k28s-K3.<ON$#?nUPNsغR3'[ 0LpzL~ƅ&%2Zf+Y!̈vN UGnGǕ\&ӏ'psb=[ՀZ kLi\vQԩ#g^50babXHj~q? 6_6# FpKWNIia3||o^+aɣV]K9eW OAGM:ɋw~rꟶP*{q?גlUJJQ-TVHFW.LV зQmߡ(‚3ˆuH3j_9/!`g]`kgf?cu&ע@{F `Y0Qn6W.U8Lj^ # K(s5n0xindhh>bOGa$ИF\/1\0ј m\Y8\ ӏOJ=>=ᗩ 5:ziZz*bzHA=#[BeR7z~x*#(Y}~f"# +Rmӯ]6NLsx$3nlCO'jLrjK)3y[]gFTr7[ՒkkmQ4Va Bgi3L:z0gXQ3&Og!F@`+ :Fpݵ?5K>OcJ[7r}OQfJjL_jA7tXO=2rz.mq\/$25U>3٪\[ୢ1 RUI'u]8o ıƻ\AA?`be!=wOreH[Hr$ysJ3JFwëa,ZEz% *bz$}:G#ɑ䭌|z2"1P߇I$G7D૏*a/m ͛< /A;(2( dyGHD<&!GpX vsxgYL^.eps<&ptnˤsr99tva3hlF6.ElfL&-o29t|#+126#JH6=N - I$ZjR %Fl x$9"INl ibĤH'FJ\TjlT 䘨"##HxbtDbT8 HB$l`+AhBDh<8ƒ HPl(D11! Dztp@Tu@tT |H#Enp"5700 ĂHlp[at$0ْ&Lsab3'"6@6 q8mE< x72GID4SD%H2 ]jMbz2h2#o034J.mlqH\Z&qؒڪ<=ɠt 9.+;qBt4DC>HC] vgy3V467?V4¯_E@@@h&xR  E8E8q ĸQP‚"1J KIn Q UAv)R1n޽uS7nέ{wnqw( x@^p }xXQ^ʊ*)VVaU58jqTxQ-:uHA@@@@@h!e|Oal\v?gev~Uwy+f7c-i',/ݵw?Kq+n\fɰ~cd~`=tA?)3$u=EuDE5UɬS5 J^96^ 1P@Ph tFFe-}"LWS# _V߲+IKVcs]:`ltPd1>qj(#0 |iڊ}۶?}WbL|1#(C^g5@$;P#HM95ͱ÷`g?ut^?EZǐ2G﷗>=8!|Lj^ Ǿ18eA&~ƷhCEgFj'DA5ֆNgr25^}{+'=4s[#NDKuͯk@#ɑHr$y$ona7D`:,ܗ*;̵_w1횦\e~!YF^P}t`f[l՗ؙ6XXOVaؒ6؂g=J vHP'Ŗqtw{wTDzD(#Ćbrðm(e-:?j~sq[/&<,ضB*MzfWBcztZg7wˤ_^գg*~~m=S_TaVa 48z;xdt,C9C9Snk1&O=s+RuD` Xy&D Dq4,vd67eNĆDܐאjȬiL` CT6]uKkYCi sa~P5A'm[^J1_Z5p걂r[x~Wnlߖ6VY4hͲ.;?_]|#sK/\l{nWSz<񰴝rdƆ:Lk{j }GgRma:E?Sv@Vr|.mݰO9}S>ӊR,ƁaZ_9%DTrO67Cbʡʡr[1ɯX_J8+hDi^_J|#pqHupUzN\dq(s,-.:Dh;4I  ^0̟$ߥoZ2r1^Oyݗ'0Ms:+i䜞׭Ӕ9U;~dڝ9 UKRNzͱwtwFR 'fjU]/vv`ZMTi3nbfvb\TH{wnApNln(5PPPOŘ䛔2ΟI?\ijHA={{zح{yHwG3 nL(9ݐh3 #8q lޑŁ QoqM˖R׷K#_JJ.GtV]jz{] m`EuyTÇq&I| - pSB ^n;Ƕ);n~7,+hْ;hrL''5v]xv)Q'4m7\k8׵݌𴚞-"=0|ds7d C9C9Snk1&u~n-=׿|K‘#ؽ9E7twC'aKίHnf_G4t,CW3_5(x5,ag2 F1 (νc)П5tap@ JMuoGvpbKo/ѷ{EkYRh=UOmUn:?k%nikN0Mg3 =>wMtϖ}ݒeSkEUo v \w!x;٫uaMxr6>se] ;5ѣxܰ|C9_6;W ʡʡ)|:TB?gO?wDғ=|Ӈ%KN8%=ɇ0kGGƣ!8{f^Cf#3aFQz: ls{R"(b iܾ[TzPv JScƥDl~s?P;vM>ԾBiPձmīit>֜pfi{~<5vWѵ[=IτLB]#~e.4qS&NU[6=gy>زCIѕ*I&Ч~8gǚS&[x0٦Fd]RI>r6SCw%U!LkF|/H[waD_m1~|{ *)9IF?e~%D7mlnyA9C9C9?co\zճ&wDE |j[LyW3ը8cFN-\u,IO\5xՁ ൪{[*𯖶v}^ЭMC.}|J=&zԴ|ٗ I&S7H^Υ_4k~nXBn^J;{vC0C9C9Snk1&&>G\YlLA}3f4fLagl0ѹwL&5nٳUG3LCG ,(S-z]|l٬'Dsr$jL7q?Չz:[Bpuܓ7wʡʡr[1ɷԕc'*3ݑYT vֹw̷wjT5a07&M.i52_ޒvٌul/H?u,4g:[B8Aܓ7wk}ʡʡr[17,RKWtO+''g.4izB1x=nS`ns;?]=lc0;&aak/cxK^_+i6kdsWݣ?^o^gKbds|jO(r(r(bLjG7t絁vmdXS+OAD7Δ44gΰ>*DA1΃ї~=|s_ʡʡ)|Z&&x{z6Ї|c7^HV:^]fգU>yE",%.v4WT.Vl4ZB0 Ԍ;!ްR}DpqDP+  /Dm]v^RỠk}y-2=`A&{(tn{I;D"fA]!ɮ$7+r(r(r~m-$ߘ*%'syrQcŽf>_P`׼.1>;2dhqa% ݆ VZ |G@XʡA?|uv.AV ﻎD"`jC9C9X0\GP=D Vm(r(rA%D9;"VIA!9KuDPD"`ٞ Y@9C9CAaI}|9ygWK򠸌 ?j@f6' 9ʡʡ K *Y7%V-,[^"aس@ hjC9C9lCubA PP ʉ\>*#:qgX Ⱦ<@9C9C#3]ьJ|\OzcM< D"f6C9C9<#ۗr9"(.#rxƙ&l?7/#R DP:ʡʡ J 8A]~;"(*#sD5148lg,I6rYۆLѫ#B654һCޓ&J:\=#e4T.mkKz D!@9C9CAQAI|FÛ ?]Ѫ%U 灋6,ns:7h&zlW=7hś9ӖxT/8<4kJ'^B|na4D"f6C9C9}D&~8v]5 7nt^D&E͒4K +~/.3Ѫ\҈D"f6C9C9"y7/Ygwy:*B\3D(CtmyIH ]巠P]tvйS%a sA PP 뗬WO~+*#E)CEieoxIڱA #C֎7`v_ mi1hUu 0|Gj@f>@!ʡʡhy*^nBɮhUCfgp.Z.КW_G"@3DUʡʡDP9nDkQQQ䓧;]v!n=KҤ<~3Gڤ'܂y!{ %A  uC9C9r&\vE3"_Aa>9K~Gj@f6' 9ʡʡ ߽z:+ڥG?WM~ⰕY "@3DUʡʡD`\~kqeDi"@3DUʡʡDR5lXaD"f6C9C9"~ ]Dl n 1"k@f6' 9ʡʡ U$A=vE-Zag eeg @ "`Նr(r( e#xԕ+A hjC9C9'-]DPW> "@3DUʡʡD` "0œ&XCO(D" PP"E~.*,,#K{0z^JY D!@9C9CA~^anE"( .UKQaQ 9ğqk^*(n輈M: %im>V:^]fգU>9D Vm(r(r@V"^! 6$BwA.[e{M: ^;P٩0v9D Vm(r(rA%DqECEieoxIڱA #C֎7`v_ mi1hUu wpDUʡʡD`<Ɉ ʈ up@E \>b]5 (AfDif{"eDPPGw8vE3ҳ*!3n=KҤ<~3Gڤ'܂ywD" PP"0%GiyD Vm(r(rAy"xynDp/eDP|?%[?q*,N9j@f>@݆DPPF<3%]L2@.(ȯH^v 4@XʡA *"Hb{zoOu/DpD"f6C9C9Dp¹`?e op[fN–.}yA PP rD.CnUDূeDp<'3#NG?g4@8޻Y7Y DpuC9C9+Ùr)%_t: zޭ  DUʡʡDPF ew!2"ȻWGD"`ٜp@PPPnwDwnE"0|GP+ڥ"1Q@f>@ʡʡhy7"O,fw D@F "`njC9C9j ɻ}'+SwD"f6C9C9R"xS2"WN;f-7Lf;?m BCc@ L371C9C9Ϳ<$7\GA6d]?zkWݽm 4ԭwPpjSKCCD"4`Նr(r(XN;ӟBAhG +iƵ+_i(D"f6C9C9AneD`2"ȽK~*!œGU:ώ'|ïVqaA ͉@>F"r(r(r#}mJLN6UAI_~+oFLJ>9/ɣGןSs;K7gӄ/@f PP Dp s7/[LzPIϰ#>'_??1W6oyLD4`.D"BC9C9t3P&'2"Ct_c7fEisȹ9N=ώϑ8͋V&Sz΀ԗONTҽ4Uʃ+\R"8LÂ@ L3APPPnDpǘ?y܈DP]zCUD߽6~HA eB 9o9="9?[K!g{yk 4@XʡAg?|Rd$X1H߿yY-~c%86+K nf~ҤOA PP rD^Cn5D[F'=Sڛ-+F* Gh(B vDճr^rxlZBIC&4/D "@'9ʡʡݹcL%FDPrRIUDo_?7vCoVH^<)Efɦ+0=%:&IGA PP '"ro_=3q2EC]NN2cG{C O4 D Vm(r(r)#xkȈQ}/}򩉛Fs(. ';¹ ghlo;x@ "`Նr(r(&?c^Ess2fݴih@vd[79ӧl'ǎh@D"` c (r(r(7")/ ~u"LvE{G%?WϞyBAh(ðNz1Heo4 E@ "`Նr(r(gb-G&YH(th{IudqF(hKmP\IL9iaGDi" PP"(#"OJPIJJurY`yHmM%bʡL ;@ L3Vm(r(r_">2"MΗnx@)$"L/SF)hKmP\YL9iaGDif{"x"r(r(r#۹Y ]@ T}uH- AB)%?텪."'mn,n"@vT7FiIäI R"WSeZD"`0)["zN9"ȯ"L~Awws DIΗ2M<@K+|.&f)hKmP\OL9iaGD` TP^ NVg"`K蔢omNRSH3X!ՍM'VЖZ.ʴ#DP,uYk_d%JQռeffK9rݾhYYY3g߿ǎ[hѠAFo7nݺLbŊm۶]|ϓ+WlٲeѢEС@ pppvvv޽1cO3 DPnGDP_k>(eB\QH# 9 R"WSeZD"D70X۶m|h֬%J=͛]՜v={v6m:GG~A\vnM:dVKhӨQ#K4x3g΀A eoH3D%MM'UЖzr(Ž ?~믿PD`D@֭[{eAXʕ+ubn۷O&; :kW[<@?(UDh Yty<mM)2-"xիA ;ַo_+ZYpB KL(ĀDPDp+N&g"`Kh@ zJÇd0c͘1#==ĥKH@ׯqj"S(X؛WK)ɾ[7{ }A"pvT7Y7S@A[jSŔCv@J/* -)!_ްaCC"zeLbc4h|HMMJV?˗DP3GJpNf'7e5e%tMe?sfD;N"%OX-)Bq`1PA ?ۿ%UĎ;,"dff?sM)ӧD :@E 5k,)$.\"|HDr;)SX~_ٳmww@9>.Cn%DPh{" rjH3R/M͖',RЖZ!ʴ#D"gA bcc-, `o6mp4S(|hD[/T)DQn,n-Z$''4HKKk׮] ٳAP"g"H.2"ȺIΓ}=@9@"Ҍh'Jueqs K6E(*ʴ#D"moi&!~6mԤI[ʶe˖~(NDn|G?*)`}G7pAr;oA9_"0ʁBPfDT7C7OTA[jSabʡL ;@ ޶pB~Č3@֠uرV.(n߾Y0aF1<++++)))::zܸqU`ׯ_oժG7^hkE}oܸs?҉'D`K"ȹfBV 4N)zSfeKl_c+P4c%IRLY|y2mM)2-"x[:t"R5:G UOVtww㨰]vǎ8qP`` G":<A6:RA^C쮁(-k UEqdn,nkǏ")C3f2ZZZY=q)xAfFvF"0\YlBil \P9xK%"$D"'=ڴ61pҤI Acryl...\_jUMŋ9oq̉'rO> (<))`Z"H.|}VVS]͏#u7PjHD;]'["OX-)Bq1PA AD[mrӳCBB@UcccΝK^zuԩm۶T@ 6mP.J @֊+Je||<{Gu9֭1x߾} `<䨨(wÈA| =;ㆁ=05"l ]Dp˗Y'88.lB[jSk׮ʴ#D";Vmȑ#C vDAttt/6e~5N2>%XRRT~6l#F z**ոqc.F1>gq|5y㢊gϞ8AP"=,fw"79eDp2"D\%T4%Z.LIMA[jSj71PA AD-[r*͛W=X,LA`` ѨhW#q#}6mԩI&JoI5$Ƃ*V>}:^=Zn] +!`ٲe5yԽ{j„ fpD ʉ)%tJ 2KjH-Ju8mT!,Oء-)Bqr(Ž j"pRISPP .]$[ڔ)S:@yJRVjT;w>,,hWOA!\wD߭ʈeDp3Wk*W "eq[ 1 R"WoSeZD"TM fZWJ={D@ͅA X||<2kLV娊2y\mٲ%A'霉 [B'2"HcKk~Nݍ=p@*Ty4mTIUKA[jSbʡL ;@ ]5jT}E(891b:uYjVZn޼"e%suu8r`:r_"HNΙ%tJ@ ;%_ܹ?&&9,^Y ؏l +rJ/5r@@Ǒ9"rA yA뜬6!/BH(*:,N#Oح-)Bqr(Ž Z"r)U-vR "8|p wڸ<@,7bvcbbb@P")#7|GV$؊BH$цKueq {6E(SeZD"TKǏ6GUP ϟp|?xT͙3gܹӦM:t(BٳN 4h_#e˖RSS9^SL/oJJJMjܑon5˨NDP+DT:r,#nkJG h#- R"W)2-"%֭[WPEqBE%T=Bǎ }W{„ wNOOPgΜYj@ &N5fo>[ q٘1cjXLJT}ow^G]# PB9_>÷+M2,# ˈ #]JOO h6E(SeZD"TKdƍ6ӦM[U*UݧM"v oܹs| ZDP~zG׵kW=zQ\D +AF4S"xuS"HΖ n#@nnF_ h7Iudq _+hKmP\ ʴ#D"B\>}V3fT=""D͛g˗/wԉnݺj"##|Ck^H$#x@9DpvڵwDpe1kD}Dp /D@].&4%P (%ZTi :mMŔCv@\ĉ:t @\APw1ﳪԿ*BCCD` pjÇ׼Hggg.$#sŒ3@P""(d2&rb _͙rOϴDZ?=;95h#J4J.&8PZػ BUH.Ѫ} R"W)2-"AVV!:uګةS'.^ݝȁIzGvZFs9F"ݻI&܏w̙y\uܑܾI&D`c"K$uv"9x>BUH!nv+hKmP\,ʴ#D"Bd&L_l~dF(&&&+266o߾}>KDP8дiSK˵"#tܑO:D  %WOnۈ<J?*DhXy7 R"W)2-"8z " k۶-SSSk,Xvmi4R[ckƍ"r=Ar;"UF77 BUHI*}%O8-)Bqur(Ž #n@,TE4[$R^Y~ymM#ŔCv@܉Ki=p@.Cq;zKW\14jo4}tzx-*w"cT#jwrQsrr⢶~ڱc8U<)JYM"(CH&Zbeq G6E(SeZD"p'.7n8--P:rPGfϞmOѩޖqx<ꃷ 6#ꫯA<+;vhܟPVPB9JDpaQnUD`P*J*꾒}#OWЖzr(Ž ;dee}Gv4|`[XҥKEuonE"e˖;`Ȑ!۶m02}/_nߋ{Νvr~M掼~/ (is"(BJHZN''$(hKmP\SeZD"p''Vqҥl5kM={v]'FcE" S5p=Uw!!!<D͛ہ]vQٓlgggsG޳gפ':r50uD"ؗƨl`Wm2Uunj]" ssskrw~Mkڴii6mJu]f͚-D ;#6"@ARmHvj_d6E(I-D#D"06!55\nR׉Gfן>=++ D`#}Esl޽ڵ/ zjB=Y͟?!{o+???#<D AB0a&іm{?(?rLA[jS*1'A YD@6iҤjΘ1cզB=zW&ڈ6l@}k BBB@'N#rwwxN2kkr999D ;#[]G  *7$]_t凎)hKmP<\%D#D"0no6m˖-C<jǛM6b7o~E.%q#ٮ]cȪ(b)sxmE:r n 0TJ4;龑WЖx&1x( \"HLLJ^n`?DJf}pj *v ;,[l/L&t=9`}6j(˱)IXD6!PahhV֝]_ɾF~0^A[jSaĔ<"%2Xly)J-u]vʤ+WQQQ'O E ==D`yyyӜ~իW9Y#sرcAP""H.9x B!hIttg, R"Sgg"xK ,8VŅ_4k֬'S;=rHPhߥa6J`kNjANˍ*p- "rAM]k6 |ChɖiLl R"SΆD#D"A\.%֢6mʯF1bD-Ieu񠠠ɓ's|lE[z5߿Mwmܸq֕.շo_c?*AD/mD~aP("Z9F#V|QmMG)ǣ<"x3g, edd>c;!KNN;vY5 @*[&Mѵh\D5o=9i\rD [Aa^yq]ʈ७O" GK1*hKmP<(JL9剀GGD,O~շ.qTus $*7n\3D^@-))iȑOh0*e2Y_u ȴZ-ǣ۽{719>лqPB9:DC^t.nDd B"eD#ʴ亣 R"SD#D"GfD@6fcd,L9sq|-X(RF-)hKmP;\L9iaGDJ%F0pxp*a??? +FP:Ν;sT?++ZDP+gϞ$*kxkt:Y3?$$gΜ!XES(XL "Vy \JZn-۪:-)BbYUxt@{arիWkl:U,TvZJUK: I(nذj.8uԒ%K^ "0>I& `FI޽{xS(X r+zE"HD"XС{^u3Dpy\Y7gZrQZm 4d3gtuu%\|رc}l jͭ֏ÖSp#GrAo?XpﶉfS``mC)UK5ݲ-:y!mMoSΊD#D"x\Jdɒ'2Ba}XD`LnQQQ\ 77>$" e[]fΜ9"rAG8QɓE#}t`M])QK4҈ݲ:C R"SD#D"x\JS+Df>H$r G"vZk2e WDxk{mӣ )xy*BkŸ2ݞTm6T_ؚy y$ۤZk8mM+ŔCv@=Ņ*Z!s/~;ÇGFFZט{## M.Su"0˖/_^ω,))ǩmԩ^"rU fڎhnϮp2D9"tK7GSC&D\`+"X%X t mTIIôM_˷)hKmPM)%剀GGD` Ud2.cڈRSSzؿ?l]"`@*LIٯ_?."0O>D8ȺVsoAnf[;_R']i2… /_f-[Жڵk)2-",$iӦq_.]ZDpKbT,۷g:]~)/ Sp7bGVOL9iaGD`!qs_wYDސ[nV,T?DJ$oCl޽vr@d7nܘ1cӠAWWWN@9ZDp']8yO\:w)R9 e K 6l斶&zYd Qt}׮]&ڎ/ ֯_ߺukkՐ-ZXbG3g>>> jvXz]#AP"""@=i8ӧOϜ9Yf\~À333+eS"`-##ccƌʭF}&O*5ӧCpttc~_ D"@<f Z1cF=L۰aCq?N߿ƍSL!j۶m_vPҳgǻjZ A3GݡC\veĉAAA/^oDu F +"Y)]-[#-)B~bYxt@0eee]rܹsgΜIMM}5*Ξ=K/\@GQWݼyOkD"`0 D<@  r ``phfd~tٗ;{6E(>WL9iaGD` TP"K/A @Aʻ@ @D`}"V0ChFd^t{6E(>WL9 @ND"@0 "@u D"`0S(@  PB9 $ݷ ]+!#,!]EjmԦſSD#D"`0S(G_@  PWC9D"`0 D7C Fy'KDHo!wݫ-)BbRxt@0 "@u 3D"`45?lt(ޱ? PP"g"HP07@7ʡA >C9C9D"@u "ހr(r(@ C9C9b"PM ̎."[CnԦ')GQxt@f PP DD"@"ހr(r(oD@ "`njC9C9@ T}   (r(F7R DoM ̊."[CvԦ)gpy"D"`jC9C9AL >6C9Cy$ܢoP07@XʡYCۂ>_+!#,!]El|^mMS剀GGDi" PP"0 >6C9Cy=>kWUsDUʡʡJD"@"ހr(r(g @@ PP!pwXo pͲek(hKmP|r(Ž  DUʡʡD)_@@ PP 5a1ޢIAeK˿ܣ-)B>bTxt@f PP D"`nPo@9C9;" P07@XʡA#kC݅E_IKm-._GA[jSc|Ĕ3<"0@XʡA 99DD"@PP^9DD" PP" X#.-$.Yx|mMS΀D#D"4`Նr(r($}D"@ (r(rD"@XʡA J5DUʡʡD`D@@ PP@@ PP UsDzʡʡ^A]8[4!H25\:glv= R"#剀GGDi" PP"0?P07@XʡA;kD"@ (r(rA}א|`ph|dJtf{6E(>GL9D#D"4`Նr(r(@ 6C9C9\"H@ L$"EMPB - D AQ&EBH6ɤI'Tw̄8 L=s7|w}4#0!rF C9FF0pՃ[?:9G.vxkd+2"}h+ kf`eе!r1s #XaFzr!APF)F 9C%`>Xo@9䐷2#H0V}е!r1#`ՇP !Vn-d&Y ~6OQ>?qZ'ʾ1'bF@atm!rFP`0j#kC9cF#60C9BZs'Y zzL(ͳyØoǮuˈ?ӿq"F`F@׆r! eF#606C9F`0j#`9C`>Xo@9cj&Y=3m|nKFZ'ʾm~u1#06C9F`d`>Xo@9䐷:#I/0V}е!r1#`ՇP !mF#60C9J1UF@m`tm!r匠$߷viy#;͵N}?c53N02ڐC9yFQ`0j#`9C޺ #XaF@׆r!0UF@m`7 r[8Lr|6Q>;~Z'ʾȸӇrC]#0D#(3 9C9#`ՇP !]CF#60C9AAIV߷~pcs̳ao__$[ٗC[9O]#0D#(3 9C`FPf]r!#hc>Xo@9䐷:#N0V}е!r1#`ՇP !Vm!F#60C9K0UF@m`tm!r匠$[i/vx:VeDV]8#0 #kC9cF`F@׆r!0#0 9C\ F]SͰyd/_rǿqˈi+ȑM<#0 #kC9cfAFF Cy3R#`ՇP]r!#h9#Ṵ2[9q'ʾrLϺF`ƉFPf]r!#0#06C9F`F`tm!rFb5`F`C9AkQ#0#r!}#0V}ހr!0UF@m`7 r1f5Zj} g+E#;^$[ٗ`ӣq"F`F@׆r!5#`ՇP !f!iF#606C9FЂF]+֎3lf+~";Z$[ٗӽq"F`F@׆r!0#(3 9C`F`]r!#]>Sΰyh/]rW8VeDg1u1#06C9F`d&Dc>Xo@9䐷*#M/0V}е!r1#`ՇP !l,0V}ހr!oe#0V}е!r10V}ހr!omFPF]S̰yp/Y0|5N}3mnu1#06C9F`F@atm!r#0#ڐC9A~ōVZa3x/^rW8VeD̴c53N02ڐC9F`eе!r1AF Cy+A D^SO^2WV;VeD̰c&`eе!r1kL9źtA(|nBa_^$[ٗ?VR8#0 #kC9cF`F@׆r!0#0 9C4BӋ1UF@m`7 r[w a>Xo@9cF#60C9BZ:O1źt>Q> [j'ʾxrLF`ƉFPf]r!#0#06C9F`F`tm!r1#(0V}ހr!omF "F`7Ѫk6tC9X$[ٗ;Vkf`eе!r1#0 #kC9cF`F@׆r!#PNbg'ʇ>|ó_\$[ٗwa+('bF@atm!r#02ڐC9Ac $#XaFzr!~(F C9F`0j#`9C`>Xo@9cF`Fzr!5AK}PVݦXn3僟?e/vˈa+tkf`eе!r1F~F 9Cdػ0#0r!#5TFqU)ֽ D9sG:G CykAKNM3K9pC [*'ʾȸt[9Ʀq"F`F@׆r!0#(3 9C4G1UF@m`7 r[3dlzRkG:>I/#2msO]#0D#(3 9Cg!F#60C9]CF#60C9F C9FмFpL5K,u|zle_Fdt[9F`ƉFPf]r!#0#06C9F`F`tm!rF)0V}ހr!o}FP`0j#kC9cF#60C9Z7`udlzRk?hÐOrˈwn+U8#0 #kC9c3"#`ՇP]r!#վkH`0j#`9CJ5bF~ս;/u|rle_Fdt[9}]#0D#(3 9C`FPf]r!#ha>Xo@9䐷B#(0V}е!r1д2Mlf}a :>Ju}qr'bF@atm!r#02ڐC9F`Fе!r1F| #XaFzr!#XaF@׆r!0UF@m`7 r11 V6;M3qCK_$[ٗWNc'bF@atm!rFP`0j#kC9cF#60C90UF@m`7 r[9t=~:>I/#2isg]#0D#(3 9C`FPf]r!#h%`>Xo@9䐷`udlRk?`ॎrˈwn+Ǵkf`eе!r13@`0j㏓ -F9C_]CiF#6с_/LKN r!0#Zkǎ r!0#xF9C9F`F0!r1#05@9C~}AZa X}g; ZJ'ʾxir1#x]r!#hc>]Ctm!r#Xab9CUM珕=>7%CV:VeDf+^8#0ʌ,kC9c0#0r!#0#06C9F:w?m>Vv̾|8>I/#2~4[9涺F`ƉFPf]r!#0#06C9F_(0V}ހr!o}F0V}е!r1F3kkC3}M<#0 #kC9cA&X#xFuz.1#06C9F\FΆ#wDo&'/ڧ'bF@atm!r@)CnS~2+T6Iia:ͲaGN}{n1rdO02ڐC9F1a]0#06C9F`F@atm!rw %F7BnYev/Oo/gsm%2kb#06C9FRFV`m\2#]SQ咑ٸfUDF@0#kC9cהl۴NtMФn߸# Fе!r1kvlZݹs~t}ӱc.ǮӜ7ۅQ_t`'&4}w-`F@׆r!)#p/\ÞZvLҜLJmtU#• ?6+;1`]r!#vbƄ %_]_ dNc= )܈SbF`tm!r0Mzl1QoD_`Ã>:v4]kck#0#kC9co5~?*.Čj9ݻk?€Iyۏ?#ڐC95h'.; cϮo?.yiGGpR]C+ql'N`#06C9FFH#%.[;@}:v8ͩON^KRboa#06C9FFjÂf&;}~m׈6믿=}*3%IJtXF@0#kC9c-n10ILxpD:$"dž7T0f#kC9cMzP# F`F@׆r!0#(3 9C\F]ZWuŋgϜ9vHyIqv&9& # Fе!r1%=Tyҥ 05>&*$ﯾ" OPRKK+LOUc#06C9F1}Ņ~ܙӚXCZĪr23rs40 9C4,NU^æoQc'N\t ;I#x`F@׆r!b5Cb\?wfЁsgN Q{5yFPR\cp_|\L|#`06C9F'AԎCr:+u<_}kg\d с=[ovCǎ=rDoط09>6ϧ(UZ`0c]r!#hhM}א*u=[n}.߽g݇oig.]}u@xb߉'NVWKFp襠Hz*OVop`@tD(F`tm!r6z]R?|vV33B~++EN>}sr;{̙3z#8~V dgeFHA^߽> Q*##ڐC9Fx#]=w]?F{FpP3k,$k}q_pA׿FPUuѣUؿ ?/#MS!/`F3Fе!r1k#\:j]'搞0K.~hvĉOx&/^7'O8~úi_&(,(I✨VaF@atm!rRZwkYCUgϞ=¯O?}z#8}TՉG9TypEGrs22}8`eе!r13@AJ w܅ ~_uZ#8}JơefgeŨx:{aF@atm!r7ϧy rk@O?Tد78<{rAݯ5'dײ%_cF@atm!rFŋk`žCǎ:}ٳg;w;~GOh}CW^^ZR\TP 0#06C9FЌFw5.;{yzx{I<GN߼~mXiϞ9)).uÇ;VUuɓSr;Y]-: 9}@!dged&'|1#06C9F`7@kVfk**51ikVEg>vHZUc{ 5%9/-Z0#0 #kC9cAxjop/KaO-I;}&i6U^?\u~䘨}b/Xr|lqQd鿿b(d\-с41's>y QFPf]r!#hbƄ %_į7~ 2INfzNf)';#]%EE/+--/@iI h_BIqh.۶ȴFPf]r!#0®dc:oU}#%~W8 F}ld?d_WXP "Ile@\@@vсĄ=swnްy0##ڐC9AkȄO]LzH2x+v?D4M^NeK@NV4MjR|\r\_wFPf]r!#hF#8Y]/7>7dn=;|;Yuh_)H }PBtTVFFvVV.YY2".Ѥ$k?;pB{=v-r_a`0c]r!#h/7q^l;u;v!>c~;s n.]J 8>F[K KKKIIOӉ@rRBl:<[2Q01 9C4sFF ߛtDt_Ϟ>$SEƒCJvn0[77##ڐC9A ?1Fmh@?uH`#Ć}#Bxyx^2D";rWrJuqV&oO+,PXVX>|Pag02ڐC9Av^^R`21Qr2dّ2(],c#ґo*br↷me (,Z*23F`F@׆r!ʊS dGʠ<$fBIyY;a']'SX>PiX|JFkAm(fQLA1Tx@FPf]r!#0 ~v^a=+Jw!?$ˠ<$<yB=7(nPTYXQTSP(^ԽwhFPf]r!#hDhfiƩ17g݇ePo&O46c}Rdi᧰HP(+mF+ WXQ(X*LΌ`eе!r1f4T=JPcLMvّ2(&O46cTXh;EvBFEbu[6 [6(,)Z*LΌ`eе!r1sr2dqXx]''1u#@fvj?Ypc"o),_Q+͌`eе!r1AjU ;-8iI3Ok׮';yDv CrC7Ca6 m*ڼPiixFxBJ3cF@atm!r S`23m۶ BɎܕAyJgRX V(޼Ak+*ڼh3^a1q2FPf]r!#0"L} #%JIWzjwnّ2xSa/YjL#Ŋ#*,+nP8~*3cF@atm!rYeۖ촫|Vo8(T(mUFPf]r!#h$DEvswu*8XQZMnԦ>m-;icF v7r ]nӎ(n0|*qر3a„ӧgfz7QRirf#(3 9C4b%#t盞kجyn6T~ַ /ݻܹ ow>ڡCсѣGƌgOe;gΜZj[o5snݺ#0 #kC9cheEytMeWc@G/Gzjp V Po3ߧw{ZQ1#F;vҤI˷qƽ;St1c,zmrf#(3 9CkI00omny[#O?ھ}l5G;;f=x_}ow>ڱcQFɲԩC}޸|9r䫺ۚ5k6mԯ_?3cF@atm!rQFe#8Ze 7~mu'Kmoͺ]l[f[9;ߩS'?xÇ F/_>$$d˖-b&g02ڐC9A3Aana,;[X:hȐ!wmwXޠ)p{se;zoƠ{uF`R;vܹs⋑#GʎJ r+_TOwW>Θz޾ۭow>T*z?pƌC/߆ Xbӭ[73cF@atm!rqFr5#H3Mmn[wlҫ]@;vuvnVܺUv]; ԩӫ:a„O>d˗/_r~tE_|1ojMIb]h͌`eе!r15+FPf]r!#0"R1UF@m`7 r[hf jU*,*$ILqH;D:3cF@atm!r@!~aj:*ʼ$""\ u@9K; 2 ƍId*##ڐC9AS <%?1w# 7$*2R&iku#c__{LJ~#2^70#06C9Fи,N5mA{C}&Sȴ7.0hп|S&01 9CmyFsZӬ7PUa>;zhT2L`eе!r1FAĩ¶oZ{x='2iePK~x]46z!1qxP02ڐC9A3G[~$Ǎ;ujT_c8~F`tm!ry **ҽQwsw6ﳙykr藟v7u36lmEΕm9 #]axF`tm!r%{m3?v+/\\usVI1QJk;Z)#qUPk~%(0quu]xL(n G gzS7h7tѣd*P02ڐC9t?z`ձƑqyȫAZwu bև3D eZߕp/%WOyokc̎.T1#06C9FШnF_GyyxlbdVjW:ٸ2IFPf]r!#hT"Rrd$Uw7%u]fFPf]r!#*@S F`F@׆r! aj[0UF`tm!fޞxa jU*LxF ?`eе!r1}B{}e?U@ ejC#vRltEi#\k[2ԢbF@׆r!.Gin'4 'NlZ628RMA\dx\}Ņ%lO U1f#kC9cܹuKuՉXWUubǖͻ]h!{}f_)+//)`##ڐC9>G>{LfJbHVjҙ3g9|E#H#=w>x ;ȑX1#06C9F HN79hTa;l8|"?3)ɍ1{xnv]436M7A]'~СX1#06C9FШ\r5'-mWNLN&HMz8[Vw?I01 9C4TooٚdsgW%~FмX6ö$7i1f#kC9cAU 3%׾(~}_ܦ5#z۬`ߴ`F@׆r!h)& =)lw3˃d?-xǻħGM ##ڐC9yFrE#8tBk~v푑Ag}tƘXj勽۠`ߴ`F@׆r!ZRbooal xa##ڐC9:ۣkǕ \mJ7k0)`0c]r!#g?Pvd ǁcFRj}"|wV^\x@YQ,FPf]r!#&!~`O>Hx KMJlxdP֍k7oX[\MKٴn `cF@atm!r@ާgSY^=Qyi 4*oM,O>f#x)`0c]r!#MI~Kpw/1bFuH`5H`F@׆r! sŋDYtYk7}ժk6]#`06C9F`#23rTqaq*Ihl~+  DDŽG R&PF/QG#$~{#=뷾~!~>{}j{Cx{}x+Gv<}<$fwn~]pjgn]}]kξ\|v9Kervxrݡv/.%.IYhS56U&7=-7#-/SK$lm 3 3 s$Yܬ좼ܜ<]sJrKu)ȓHʋt).,/.IѾbVKhe%e%+;X^~PVWW)ۊ}:Pqha/9|`G*%e{CJ1Co9|Br!urFm~VVjkI[WVAm>/?m5JMJejSSu^dm=kkJj+_I3P)=t&3K_'Isɘ#Myjl=m[{.k'x:&h KrQ{^IWKJ ⣿iHI5JT]q\t5Ť^F509F{L]1!rF[cBr!`B!`B!`+Au+W,[@ W bB!BAxߦuk}!]{~W&\ IP#tD! 8?77Ff'd_wl߱y# !{p۾i` !Dn;ڼCBVez7FuS&0:;o!Wܶod9MxBf~2s+ Q~>EG8ql r}291z{lhv[\G&\'nJͮ)n!ψ?_sgH'2|HkZ8ﳓK 5aKՎ5')I S*)̭=~jֵ\f\l\b>Rc+U 1W\sTqr._<rԤg}x1e[ׯq޲^o\y9ŅŅyWJiqAfz)O,<Fmvd䤙ved\X|ZA[1<F36*~'eO͏YcRC 3 BHv_:TuUZBRHG[4oq'-3Zb]뚖oԲwINg׋1r̥EF0řJkY@/_lwȐg˺U.[7zܳ۵^<<;3#/'j͒cH9xf u%g 29+!:ӓ5&^VۓݖXs7'uwǿ:w;,i}ߖ`4Ig+G~fQHr ;ð- ּwΤBge$Iqپ-+&R⢥-Rk ML sJ R^k~rM :5ot߿QHb*7UU/g\ ܮb#]b.2fos1sn U<dq^)!# :vP^N6E};kN#)Ik*ڐmB䘍k=T)<5>8{z%~>$'\)?_o9R7Y"xʊ}q1dz#JJ3iIЭLMÃbNԵn<#r _>K58+sC>3ƃ^"Tя>1>c?-KWTlް~ˆu_7¼LMnF*!+E sFPt5LLx_V+&>0UǎÇ7nؐII6geh6_'GƓ4K@WTFoщ$SFl"k_3lkɻ+}a`>6zՌg7z7;eoQ_̮sVRַJc0h8uӟtV'C F@S{ر{ 8/wUK۴vUQ^Br:YiJsj6bk8I+uWh*/'B!/ف"ؙ^:P{Q+ƄB!j&Z  endstream endobj 131 0 obj << /Type /XObject /Subtype /Image /Width 1030 /Height 722 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 1498 /Filter /FlateDecode >> stream xA 0İOz b}ĆGv h H+p_>WS endstream endobj 124 0 obj <> endobj 127 0 obj << /D [122 0 R /XYZ 69.866 808.885 null] >> endobj 129 0 obj << /D [122 0 R /XYZ 70.866 771.024 null] >> endobj 125 0 obj << /Font << /F16 128 0 R >> /XObject << /Im1 123 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 164 0 obj << /Length 1072 /Filter /FlateDecode >> stream xZMsHWpN[vuy\J!Dʠ ~ɀl 9 $~=00={> endobj 132 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [69.87 618.595 154.312 627.552] /A << /S /GoTo /D (chapter.1) >> >> endobj 133 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [69.87 596.677 157.91 605.634] /A << /S /GoTo /D (chapter.2) >> >> endobj 134 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 584.722 164.266 593.578] /A << /S /GoTo /D (section.2.1) >> >> endobj 135 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 570.83 336.481 581.623] /A << /S /GoTo /D (subsection.2.1.1) >> >> endobj 136 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 560.812 188.564 569.667] /A << /S /GoTo /D (subsection.2.1.2) >> >> endobj 137 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 546.92 337.38 557.712] /A << /S /GoTo /D (subsection.2.1.3) >> >> endobj 138 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 534.964 245.751 545.757] /A << /S /GoTo /D (subsection.2.1.4) >> >> endobj 139 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 523.009 220.167 533.802] /A << /S /GoTo /D (subsection.2.1.5) >> >> endobj 140 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 512.991 173.869 521.847] /A << /S /GoTo /D (section.2.2) >> >> endobj 141 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 498.545 208.102 510.501] /A << /S /GoTo /D (subsection.2.2.1) >> >> endobj 142 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 489.081 167.089 497.936] /A << /S /GoTo /D (subsection.2.2.2) >> >> endobj 143 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 475.189 183.223 485.981] /A << /S /GoTo /D (section.2.3) >> >> endobj 144 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 465.171 170.188 474.026] /A << /S /GoTo /D (subsection.2.3.1) >> >> endobj 145 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 453.215 164.792 462.071] /A << /S /GoTo /D (subsection.2.3.2) >> >> endobj 146 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 441.26 139.802 450.116] /A << /S /GoTo /D (section.2.4) >> >> endobj 147 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 427.368 231.901 438.161] /A << /S /GoTo /D (subsection.2.4.1) >> >> endobj 148 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 415.413 229.41 426.205] /A << /S /GoTo /D (subsection.2.4.2) >> >> endobj 149 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 403.458 167.642 414.25] /A << /S /GoTo /D (section.2.5) >> >> endobj 150 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 391.502 214.577 402.295] /A << /S /GoTo /D (subsection.2.5.1) >> >> endobj 151 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 379.547 203.369 390.34] /A << /S /GoTo /D (subsection.2.5.2) >> >> endobj 152 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 367.592 142.016 378.385] /A << /S /GoTo /D (section.2.6) >> >> endobj 153 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 357.574 237.713 366.43] /A << /S /GoTo /D (subsection.2.6.1) >> >> endobj 154 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 345.619 161.747 354.474] /A << /S /GoTo /D (section.2.7) >> >> endobj 155 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 333.664 220.748 342.519] /A << /S /GoTo /D (subsection.2.7.1) >> >> endobj 156 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 321.709 214.937 330.564] /A << /S /GoTo /D (subsection.2.7.2) >> >> endobj 157 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 307.816 169.524 318.609] /A << /S /GoTo /D (section.2.8) >> >> endobj 158 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 295.861 191.359 306.654] /A << /S /GoTo /D (subsection.2.8.1) >> >> endobj 159 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [107.728 283.906 207.091 294.699] /A << /S /GoTo /D (subsection.2.8.2) >> >> endobj 160 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [69.87 261.988 135.727 272.882] /A << /S /GoTo /D (chapter.3) >> >> endobj 161 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [84.814 250.033 242.722 260.826] /A << /S /GoTo /D (section.3.1) >> >> endobj 165 0 obj << /D [163 0 R /XYZ 69.866 808.885 null] >> endobj 167 0 obj << /D [163 0 R /XYZ 70.866 641.509 null] >> endobj 162 0 obj << /Font << /F20 166 0 R /F21 168 0 R /F16 128 0 R >> /ProcSet [ /PDF /Text ] >> endobj 172 0 obj << /Length 611 /Filter /FlateDecode >> stream xuTM0WD@B!84.Q=SZ\̼$@In}  + ]GʄUEA`iɮ%?Ǿ99i8-sʣ_X*9T%$jV lezea%/HB0J ,U)Sg)oTu9G x/(ɨbNTZy:JL {=j<`1 3F:XQ?t8Y2sr3ʗiJEZVF=I^#_&{Y/%] xv‰xz$ү';nyf8${T!E K+Ꮦf<˘wQ{Y#@X 74tL{W! V d a:-mX*_&SRm!4LllA*eEl|GfYU yLGcT/u~ A#X0C9W^ӧp}CY Դ\^͌0sn-Avn"&Q\ў-„0Yӓc7z$DZd&~F endstream endobj 171 0 obj << /Type /Page /Contents 172 0 R /Resources 170 0 R /MediaBox [0 0 595.276 841.89] /Parent 130 0 R /Annots [ 169 0 R ] >> endobj 169 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [69.87 557.602 224.041 568.727] /Subtype/Link/A<> >> endobj 173 0 obj << /D [171 0 R /XYZ 69.866 808.885 null] >> endobj 2 0 obj << /D [171 0 R /XYZ 70.866 771.024 null] >> endobj 170 0 obj << /Font << /F20 166 0 R /F16 128 0 R /F34 174 0 R >> /ProcSet [ /PDF /Text ] >> endobj 180 0 obj << /Length 2327 /Filter /FlateDecode >> stream xڵَ]_1 kއ($ˑaĚNz=3qjyaUU]wO;ݻg|Q?ˢt?/l'n_~^_u6S/;JC vIa}3|8FLaHb7a{*Wǡ6L!4uN& WK>ѴqOUmdU<~a+229:,(M{xz&ʽ΋ǶUM3 ѸS飪t/PWDtތc7> 7IXxu'eY2Oț;#G]=z]1";x$9%)zTtŋ{a^~:Ͽ^3|=Chr<&4P,ix33P_TCF=LAdjKX3 ) z`(-2ڻGPi2uwG-ޞnzؠ78Z|zc㎃Luu&.{rgnIz2I65dV uwft" ۶1Qz Q:1UMָc#4'> XF;xɌcN3ME©: Ihܫ|`PV4j,$uh,sNʪPx{MIN8c)>tN"޸8"[g"kqL Ðz^i PPDXRn-uVm^-<n2U!VuXO/bk ]{ ů?~؈qqdIf ~I 0|4sGWLi=me+8Kv cȝ77N#?j=j1/h`y|1M(Z 18^$e'h^hQ9vqR8 z~x+^(hC[Yd"lS4sQc](ژ^z|-AUlٟO+{~iqEbaYHD?a >~~ ?̳/| /I@d v|\0N"07vWpSfޖpQ *TmRՖhqM]nH~DN*%AR#ʇAFg dT+Bn[w:ƩEwLiHv _Fj` 8܈ }R 3ڪETѦPr?R*#^4\r/IveK :*8te H<ЏIӯ6lf|[?Fm'dç1)txt[,?b?ҨR 2 l\Nġ{?eY +ƙ“=R&m= h҄*T$.Դ.B,?HɸRh~{p pws ba&C.Spdĺn:9R-JwI⽙ ls/BTЋC@ [C,ZXʯwz?cMlI0  A͍hZ:8ȱ>w-ϸeu 0Bm?yO»{=45@g9w<*ke.Q7^z/Ba{Of^<{7JJ#AވNgh 8Vv"/WL-ƺ0]) I#z?8k8{S5GĦ>_!G;R@dfnKP$6O6DbE0quֺIn8"R*o] %;.'{J%wWܛc-B4n޴_p$  ]=X,\D nnCLL*,y^zIˀc$W\}m,qzWdRD+T2L=J;7T+% ']H iی+=DVEɭ֎G`:bqcrCcT  T(_(JI2o Y?/Hp endstream endobj 179 0 obj << /Type /Page /Contents 180 0 R /Resources 178 0 R /MediaBox [0 0 595.276 841.89] /Parent 130 0 R /Annots [ 175 0 R 176 0 R ] >> endobj 175 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [481.464 237.816 502.967 248.608] /Subtype/Link/A<> >> endobj 176 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [247.213 201.95 290.301 212.743] /Subtype/Link/A<> >> endobj 181 0 obj << /D [179 0 R /XYZ 69.866 808.885 null] >> endobj 6 0 obj << /D [179 0 R /XYZ 70.866 771.024 null] >> endobj 10 0 obj << /D [179 0 R /XYZ 70.866 596.677 null] >> endobj 14 0 obj << /D [179 0 R /XYZ 70.866 568.879 null] >> endobj 18 0 obj << /D [179 0 R /XYZ 70.866 460.888 null] >> endobj 22 0 obj << /D [179 0 R /XYZ 70.866 273.086 null] >> endobj 26 0 obj << /D [179 0 R /XYZ 70.866 153.14 null] >> endobj 183 0 obj << /D [179 0 R /XYZ 70.866 116.306 null] >> endobj 178 0 obj << /Font << /F20 166 0 R /F16 128 0 R /F21 168 0 R /F39 182 0 R >> /ProcSet [ /PDF /Text ] >> endobj 190 0 obj << /Length 2500 /Filter /FlateDecode >> stream xڍɎF_ N@ۉ wƱ;@'2Yj"smEXۗκv/W_9i'wNEVN]nuf뇁9mEnguw W1R'1Aℑ\]}rЉ}^Y;OD;k+}' |Wֻ߯vK>Nkئ]QgYt daַt[ [ (Ma:~Ro 2.6'U(AO\S9ށ59g*-)xB= !**IŌG޷l#H}`݄}pk5GDQ0c/93 dahR*-腅$&j]C6{ oY`r,{~rlv0zυjȹ'n%?p #O"uG^cہ*T=][ %\`PZ3"GY2b Z_-Ƹk5a#=H>O]9!r6mSxk56ˀJ{%H1 D5>BĦzr' $$xaI@qI͑뜲p Ahq 9T]a4\oߗCK=`]ˤ@= 쯼[Z!sLk,k0}M&|ʬĈq۶̵.8nsA^HD15<6(\Q"KMRQ|(+1f[ElZqhRK@5\\}_ӝh &ĉ ˒$D k":! Q4{yT r3&/\qx]g{C=rѺN;J2͎w_ vXin<,!u(b;9-/5ل/ߴ(F1ע>+doM M7Ĵ5,(oCY#kFiR^:Ϊr\/!lz)'~LҜLD#4HVK+H2"q|Y/۔D(zϢ2jqʁ 12儂i&5gOtRv` [oC6KEN mU:ҡi4!nS.n4!dfUT+ U)%Py2`צ̔Ÿ^z;X?{]S]%[KJN1&MTyf<{؇jke$D,xPBi-G8̿."v~2~:R J_[Dh{iH$OAb<͢~EJV?yT ]b ̰M$b\A2 EC mpEK!AS0uE_׍σ3t@|Cj]6,ǴǩYߗ,\=y{sVΗ'K_<'pg~4v )Ļ \2[j b?㼲 endstream endobj 189 0 obj << /Type /Page /Contents 190 0 R /Resources 188 0 R /MediaBox [0 0 595.276 841.89] /Parent 130 0 R /Group 124 0 R /Annots [ 177 0 R 194 0 R 184 0 R 185 0 R 186 0 R ] >> endobj 187 0 obj << /Type /XObject /Subtype /Image /Width 506 /Height 178 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 203 0 R /Length 26949 /Filter /FlateDecode >> stream x\]"{w{߮<*Qcb&($HBXEAٻ33)d& k̜=e;sfbeaSD@q > T?(m~(yXX\xzgaɵJVHZPh)R@PIE|PFP\,>z2E== f QPJƃo0#}7ϓ۴8μ:JcE߾p1\3&=O^F+O%Ny7 bCiVXg `P|u\Z0"F!W ]TބC{F#<<<9n||YjsQ2@}RӸ['N@ExP6540 荍 <k ŀiOXWݾk-c?͹k-Jv\8o63nC#zL0?$Pz)=H"y9 .ϕI2c26̽w`~XX˗~ 6!){Z'Hoib!;s,_B:~;yN>o:oZ# 8SMIv*0 уFJ?}|p "x>om1P+V`_C0>kn,8_ cE捇? 3 PNC6`kRTYEt 9!D*=&[ |}Kѣru"Yr45=P*+_\T eXx}ݻSڼq=>ʄ>NV>=g]&cǞ~UAW獡m3Y LiXh $>6 pǷϔuoQ4NxB+a>kljm):c^Tj_:m'D`K>~/Z=o᜺7u'̋im+AsLûVg5XI F#6X̮kE+* k#6"6:pM=Vsp ޲yX I b4<7gdo9sT_P-~O"tT ۱m[=V\GC5у20[mA^Ft?>b95ɂs> O?V˃S\߶={yWaސ Wη7o0'z<SGh(ᡲ\ 99>ͯ`PC M{iZ"[xRID .R.GDO+w&8OqۻsQ1©uWmj{ͤ|O" /qLAݯ|N> ; $c>WߴM~cω޴V IUm͢S&6,8-MK|z-M 8(fy-c jMkF)j*>!y܁I p:1IY4fp E3Y-{owY؊>:F'қz5C+rLzR!-+IJ<C=q۷l=bxg&v $zA$h'Iё%:ʓ-%=-d|wu5)admYE!*4ÿR!ʸ")x rԍSUBUZcQ}ul0u_a_ 7`F 7asɆA-XoiEԂGOg~1?~Td/mn30ߵD8C@+]E 2ޫ7\-r9;Q1L; ߚRN^=1&_\'émj}P-U4$HM &*~R;o sEawy15ZJǼ4 f cϽbN6Pd', %NX^׍ =1zUGbhsOZ!S PNb>~+x YYNpBQ!/̸J$6z\ ҦG#D_ 4ғeui_[P43 P.8|_ ߶||$= |q7,2"\/.4ωqXӛ J<ox7|r-|\<c_5Lx2텀-mm>A>mSf[ GKz8Mc}==-L~g{<{޿_Y6q0@ÜV= R㮿o~{8~OARȐy ߵ`Ϸ!_9羵}/d@{-+k/Iggg'emϡ׮\*,&=6%'`~ľ)$[oo>~o~wfG[4k>lCcio=?~R'/?#@y'>΃mC,Me9m:"{3~1$n܍o/ 8*7|c\z#ELh] *y9W/_HK r+t{Ir%g 3noIZJSdJ8~y։+f9ǂdCC(A*ʥ9ٙ7B8z$ჼ̌d7ǎ Refl%;;'{h 7ԯ#zM俥7~k:#Z1b1U4jy9/X`CjT@la_Y7{0NQXwQ,^DP@\&C24%piclYaRPRJ4_<ؼrЦt+՛JL;}/{~p&6Lu7*܎b_c'7k߬ 8X5"X^9bւMuoލ|o0KȓxmMϽ`Rwk9o Y,|ԜWsUF6(qkgO8q 8b2p`z$u>YXn0U( ^eRy@Z&qBpmyU."??:1hԝ޷czG{pgu11 ?ј>x$'21F2H8e<C$n&fZ8#x1cy(׍1_ZRAAgpCd*0-->l3hժFa qb"lWH&𘍎xAx_Dt(kg] XMDя;@-`&#dif]W0+-|%=Ox;)&^>#+|#1ߪ|97Jx- + C}7=ؐrEDBtHDHG+[̏yV[c^Hx<" G'^Oo L/'SƘ`8N 'c*\ e̱Gq3xo$2J%pEu WtM$¿X8£XPGxo+ʴLP,a͘LFNhޫ ⽂@ҐWB^dKoۡ=9+B';5o.MPF _\!Xab7IcxOzy'oHľ0;IFz\(H f S;. #{NĞZj/ @^/yReԳ4Exyd&/\9cR9W3l8JLp{_K+<3X/Tm 0!?,*K*!@gPpÖo߀X ?c(@P 3 FF B@}bAmkUz޴j1{0;vɆkq#JSH iw!% ?A!k~LJUa~HRީ,8tBsĔ\%QH~S5f$j%uQ#W^Um` E~\=:<$qˇMX{=1Cx})Q3qO5$UTs*'w>7P=)aZeBkغ2vJUTJp1{)j04NWŸ.G$3"}Bބ 0(UOi/K$I^"RT  oh/,!%C)W“nvM{O1{ ^(\ gHe1"=Ca!}_v(06 EFBFe*0Kx} K a HE*WL46V#c=T-}y;S3s :WRJ/ͦ=Tl74~`݅ ơCGc<:s}\ ˗yW]_.g͛۷EaawK""T{p$8)t@j M2H9| 書"}>ǻ'ߕYR3?B{h8Pc_ aYэCXMZ=F\sySxπvE)u: ÿ ?>wmД $ug6~w-Й#(9!>~w_ ݘ8ͬGՂ]>{vs]|wuH;(TH.JߘKJi:I$ԇ Ϊj1}N5B[&ws^w^Lr/8eܖزj4XiNnTWq#r|z*  ٵ{waJBU%[<ޡ'tJϡ,?=1oօe:ucQ=}p+ᶨ`N}jѓ_R(Uq\/~ fۿ-Lx%IҾݾ='Otejj NjŸ?_|"^H7oC`<,{P%=~\ Tl=`?ǃ 2H9|9/)!?D{vځ6%A*{nm0|ڃ3{4tL ]4t @{.B 啲\^* vM.h/!BLZΥ_#%ɠ sov/h鸈.Uk.jEyVJ+|^)W+M4}s4$.t)›ۿH$rI)Y_,U&$b5?z&<d f(˟>߾6  Jy!$}feܘ3`TR_Ƀܩ|h[^tkSop4nO<&{|iE ^CBc\~ i_{rjy5"G݀n U>w*i}&Ew| 5mEzCCj" 8ew&q҇{SnUlBZwʉTv4r, jHHxJ\[0 jG+US%̣}wƍ{ST7W?رhڗIq ]zZ~}ȼݻ<#~~)7jg'B0fi =~uјI%']:ѹ;kLHގz\vd'rPҖ_ѿ{" V6/+WmQRED Xn x7a^ 7()YܽQwlPbv?x̀NW YӝK2CV;C$>{Jc\)\B5&+jb=:ʥ`@Pˏ˕Ҭе^C@ݜfc?o:'牋% *@u mlq.=Z-d7_Y碚>KT; 20L;I 2rxG_YYTK$e%$9:5\hD#/ 4(d@`ULX i&  *ziӒiEUnZ ˨k_Z1[Dxl;Tn"`6}Ia^,],6?4}E~o{]y挡PU 靋N0S` 0l׮oz 6 q[DD%svWZc;wK*ECpw{8q-% *uUu$q靋N50ۑ?rAA%KJ֭mTKaoܹs?8Xv-8:#1/b@k$`0lE joNRݐJN¬R˶^hgtVyyAmd%=wnp ׭_/Zr>8_޽Z`Aղkr. ;q`*~ K+mP\L $Ni0Y/Pɖ>)WBGrJKJ%"RiNVe'V\ 8,\J/[֠1|jnN9pLђ%y|}k22򹵵xъb8#$//A~ 0 #[AXS{#Nf=`Hua(/QڧKm4NV%rAAe˖ ŋ%O?aMK<9ã*5>/ Y .!y|Am'<H4 '::R´7۷Bmd%ڵxbŋ / Ys6 7o==+Myn.= 7>ypC-"_(S2Ti,-yh:Y n`ϟ_<>g  eL۷8$23s}}g#|A`_pPCÿT?D·W^^NFe|fm}'+Yسgsf(͟͞=[0{`y~``)a ?iiYٳK)w p.|8G.;A0 #["JR̔P8Fr$b!)8i+܀^@? @( , , OܼlW'%̘! ,S\p\8w`C-$`0l(}OU+i|gf~M'ųӦMcߛK%@Ӧ L7f?~,+u_Y /܁ e;6hO%Jhڗ>Sf~L{֬E>>,__/ח+J}}徾徾b#-#|y\\3M9eJOY/⁍ 6lX5=%J(>Hzmu'"/ˋyy ޥ^^/roowWg[S…/ߟ;jT{wHҔx{fT^-[Z4~qCmI)SD!f~ՓҧOQ8|ݽ].wwpwrwqwWLPzg::$?poD1u&{{?=ς/1?lX_jghk`kkkEE 0iE{J(QҾ^)DR*Vtz60rx3B{{N߾"jĉ'<ӓ7j7n |!յ`(ׁ!Q)2 rmm3RP`0 GD(QC*Ly&6kh坭b&S[d;t;[[b&e]]--[ms[ݻ߷}`?~gؔhvv2[[|!?mIk(SDZHN3HX&R)F l'8['IvvvvYvvvvEvv0K`3mmml-k~Gؔa)ù9|A?a<%2]{{D'w=F1tivelb< 91tzeT8r\&Kc@YqT-=$4֗wnƸy _1׸cDZ׋hַYߥuHH$,'{%? }]].rxvvb,[Deo^~c֧<../)V B9p){'`0 GD/y; ptt,aDpqS!op|.h= )F˨VqLXK}2Lan-];]dmw|%<8l5Ck~㲓>^=~])E{9_>Î}]ڱwBl[Tl릦>4vvevvB8Ľφs67{VݡIX:POU᩻nj\OQrҮ/ )TFG9ŶY4{ٲ&#Ƅ;KJ~11L =ڽ}ҟm /f-xi']( n\t=)Kӡ#Y!@ʼjNz+[}}uy _^;~nh,Ƅ^MJGK<ll5wgYV %:XUq8|3Sƭ#޽@RҏN=k0jx)H#fh\q1/H IE~)P =Ŭe4ڢV 0S2 Hͧx˗764444D.]SCݺUōF?ʘ?,'p^t8_dq=0 #["}Yy)~ _:zν"-eq#\'w%"W\zyLmrtw|ݼ'l?\>r]ƭfeEv"` P8Q%I034h\#t=Hi@Y\ځzI.wx]nW?fé1r:Mcm`䓹^=\tN ;ttGJG]O9yjڧ_Ȥ Kۉ#%v?8>18mҁL.dx-ܫ)݇eA=~}\yQzNGz} f8Ͼ](.)ܠۅȥ*J;>4.8 _ Щ4}}N!;Ź';N g@Nu>Ž(9LnYa#kr8,pzKQeg R1@\8lF1$W@K`NRz1sr$?>oTXɾ885#Gn~ZKtnne*'DKٸ,8du5rSN9E2(D[PZŵB>%%1F[f5w{%Wx/c,ܩskkYη;u:Gz5KyiV &ÀydK'i.Sq3O+:r˘IRX>92#9̐i.>usKg^n!8>6kԆŃNf MNWv}{ .tɪwfL: t#u$Va.79#Nt:py"2YVI C 6B<a k_6[S-ؖ4~74 bΨtURϹ_33Svq×Er8f_:rW|-Ca[t0Y 2'1$l, Ê4|V;_Jhii_ch;[%_ ڋq{c}YKϋO,չ3sk߬?Y[+;w~ЩSHϞUBK@}VB$ d Оd ;|z xWxp#)B>=jˤBȸ:m[sNn{&8F02r88KSƮrj΂y^Fǂ.ҜJ1zvu: ϐ<$kYbρWrjԎLUR;MTY]|.dA%+5F\0Y 2'1N 3*qk K і}I:n_+@[\D_lmmHfA84Yi ŋt,Z[sgΝwޣ *F#׋ -xYW&V 5=(`MXqe=sͫשk:qo"}yD&s<\>z;,pq%!1˹7h|tN;0 #t%)$Rznʵ8'e]Fʘ}uʠ? 47"! q$v9igNu/ +\dJk@isnhZa0,$8 ,.]=8*c8a辞;#slg\*UC{ط8$b-X[%E;K P/G~~]IsS__xkk.]~S/ֵ;U-S'ss>x#T{8Hew( {:n gu㋹Τ+zw?oD.$X W p<%yPNAQ 6G# K>zQ_5c\r1ym` #3ԵךNc1ޙCzutdo壪vwad v~6} a>)#=ؐW`0 GD(}9q,ozgD[;l8Zs3M=ΤD*M\x<L\m&L{7'0`^̩N~ƦƦ潍M.]>G [llؔv"OyN=m%Rr#]% -%J_NCNLl8QkFޒ&+8ڜ3OJih_mhV 킼yyBm>=9R[[ª^h4;V5?xGfy: =9jرS 8PD%REUy!MJ&}6`m]i457a q hDQ3{> W_ƄFp 0 w&ÀydKD%J?Ps9BE4tkFGslm0j_<}uFml#ziE8zګ׾~}LȖ%J~H մVC{&#F󷶊.iGj#k5B:P V;YW4K숐z؟G8- L |{Ĕ(Q&Vɩq)mZggac@.P ~ a~_) Jsyp: d'9"6z0 GD(QCJۧ+ybA1t;:߱?ZI\Zf[[s)r;_#D~ 1@}[iX}};/Ւ?GD(Q#*UX}9/f2c0;4C|xa>?"jg8 xQD%q{,>L<&WTc@SYDQ o_ax"J(Q @^6=%J(xJ(u|{&%J(xJi|{(Q/@bˤSDO)|&Sdr(QDSڷ)QD釥=Oۃ 6STH%J~Pѻ|],n,6LtsLuhxe`dzR*d1įôyp7?`;ד0팜+G ~um ??&:xnƷg2 |JZ~--g'gE3ck)ye_:Lf~)}]%=99(J }d$3=7;O@vz9 )^) B!^Ҡ}ps˸C9БFvby$ڼAC$ڠp>߄|\o3l,LtsLy^e3db5:k(5tS4VEknZj^ͥ3c޻y,fc4hx|&-U'sʙBva>%ᮒonzFt3b%q)NM8RԹ΃~Myry~8" sq˩δSL +^ٱw^ ;x\IS t#ueFXcS]Wd{nQDMSNȺefE?6so, 8 b夠Iq{&6h(Ooi#{ ]8EG%<M'sdLz0Vᎄo,VvÃ>g&shm3Nܟ{d `,{+p~^Ĵ'"'@'>=:nNJ3BK `g];1O<}(db{))& ΣyڛYז8ʂy:h ot1,ס+}Dl3b `1 skb͌ć<&8yyZGa.fZ2gL6:k6 la>A!y``1l┵a!W$޿ld1Ggv@͡){8\u\z5ˋ XyJ= 67O|Bu8+'A N *b",l^8~𺻙WǗx ֳ߰% =y'Ns͍R,f[ܡ}L7 w;1N9 l$S􍞞ueH}$[k87'l F>0fjyNѰEג>蘗k(܁ꖙxܳӆ{n3MSWl0h|ŵG8@sӏ{=GʄhK%J~<}{Aў%J~Tj"9Ңf~.%J(Q‘#9|f~%J(Q‘#9R(Q*X3r(QDҏ-%lJ&UJE4td.ޙ05lq1U(.P67G @VEQPYeGA !&͞vD|ZΌ3s)^'$nȢI+sͿPftvp?ABS :d壾Zl~-0M6N!1 &+ux=5l}L֢jY]570_)pvZylNbR$Lw5hZ+S cT,ESe?Av˴ B--V׫;X \fYWjehÑh|d6Cw+z)BK`~FǞy^a,bɋYIWh-X5GpG`ʗ\,HsFIDq$ȉ'x\#/k# zx-n?a{ZڒߴN]WZ\8#7h|ټ~fSFRXK 9L/nJ EfK|M߮Rm`GK#Б^wrP~=XAg]@/N.LckU?<$FƸ191EmQ)՘d5̔{~u'>iGPN8IgJO.|w\l3vL{}uaY!1RswD.kmᬌTi1|1/31|F]eh{n#<:0ӻv]~Gc-hJwW-DBBt zi3Ch;Ѯ cփHl (}M,7Ԟbҁv.;pu̜3! N A w_p9+ A85lZ_ހUDqe;3bn0aUL9#uYk g]&+O܍u~e ZR6کnJz'Y/eJh*6;۳U R~*Gg5mdPf'̬EcltY' wZ#:\ ¦OX7@6t/3q8Ga##uYk g]&+O-l_Qlޠ)>m )|҉;sѕe 3l`TrD|Us 'lKi7˟v4h6^Z$ &ԧzᡶzVgH(ZreGIx Vl,}dⴳ3roM&~o8]pEnݢ|E%xSX:ޫ~[1|/跟hf`*ڏ!I )XIA# ["k؄#-EXU"'aHl}> f`Xx~Ӱ`EܛC;/xS;3Pc Z#9]Bצܖ}3M>q(tx_-r$ޓaþ ӮZ[8"7Yy`Ln:_|8V#;Y-GکNCqw d  o%B$ \6#NLo$K]6mFBFKNoꛐ~.ZJ؏\5Ƅd"3d[n/Rp.{\9HA2\cW߃#QT6q RæEa_я2f@di ng -%:GΪMW>J_pO͌gB֥UsKGM}%+ܰhwiWcl/S̓v%UK|) l =p=}P{Q́ح+g{ޞ 79Ntj?j`j!Mm0 ۲p@ hF)]R{p4bH⟿6 G)Xp` C>_6 R&W΂8XgK_>,(,~A);c=jP75}~[E+gƿq+X J{L9Y) û7VCB^ӓXΓ ~nN798^!Ҿ4x>A1^|srw^W΁juE$#\Q#60)+(>˘;iCީ#|VSqΩvUUAFTJg6;i2%T{9<GT8F݅-犛&L=ؑVԙZ԰/o` I euQLjlJFnG_OGu79or@p,oV'/%_9]s<6ⶳwnVo}g n/Ć⧱nq2juONGj?C{{P{Ng'&I8v`ƣꓯ5%ք*'peW޾\sSiHbN}.R)fݷ"ۈ3BBX{o?jbe0BsNM;x(;ZKe$ʗ1ڔ-zj')l9!Y,-_>OI$nk!P(llρwipT$Gm%:S%ʧ@M ٤V> `j/_R{t O\R{D!SbV2[Ȗ^ J v@$]R{R$="I^(JA)ZR{IدړKj/S` ~˗PUNQ`ȋu]C5 ӭX+Z 5?8(+[*u+:FI`wD;Dz.tj:_) _  endstream endobj 203 0 obj << /Type /XObject /Subtype /Image /Width 506 /Height 178 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 228 /Filter /FlateDecode >> stream xѱ 00tdE Ҷ}!^z %xgD;=o}u9xh endstream endobj 177 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [497.261 715.274 525.406 727.23] /Subtype/Link/A<> >> endobj 194 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [69.87 703.596 297.266 714.721] /Subtype/Link/A<> >> endobj 184 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [209.513 629.391 494.444 640.516] /Subtype/Link/A<> >> endobj 185 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [94.777 610.272 180.455 621.397] /Subtype/Link/A<> >> endobj 186 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [416.038 456.264 522.638 467.389] /Subtype/Link/A<> >> endobj 191 0 obj << /D [189 0 R /XYZ 69.866 808.885 null] >> endobj 193 0 obj << /D [189 0 R /XYZ 70.866 754.627 null] >> endobj 195 0 obj << /D [189 0 R /XYZ 70.866 674.824 null] >> endobj 197 0 obj << /D [189 0 R /XYZ 70.866 537.34 null] >> endobj 198 0 obj << /D [189 0 R /XYZ 70.866 469.492 null] >> endobj 199 0 obj << /D [189 0 R /XYZ 70.866 406.941 null] >> endobj 200 0 obj << /D [189 0 R /XYZ 70.866 375.867 null] >> endobj 201 0 obj << /D [189 0 R /XYZ 70.866 304.437 null] >> endobj 202 0 obj << /D [189 0 R /XYZ 281.352 95.907 null] >> endobj 188 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F21 168 0 R /F34 174 0 R /F28 196 0 R /F39 182 0 R >> /XObject << /Im2 187 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 211 0 obj << /Length 2561 /Filter /FlateDecode >> stream xڅ˒6aBU>Wc]q+'Z$!dz_IɊ}h4@{a̓{yyYeQ={޾y{$`)w&w~x3 ,ΐREf1G2$Y=>"^4yyߡW+/ 4 RW=hqEAE|n@"M>]56Q$E)cyo'`(|@?Tv5P!OЎZ<̽f3ؙ'j|"_m&\Zw1N #Owt((Ӕy-%aAv0Rcphzm^SXGtQTYcĺNʅ$r;MquMq6qevv3?6Áq:߷h#Cu`Do`kbw mqش-B6+F#LԸs=;mmInru,K^g.m-G [;5 g-GrE;B<&9;`kAɜtל%Q,,PȷcE R]E06* {J9 wLxR bB{vHS3mkNrk ϟ 0"*_aUTaCxq7t@Flh 8ƌU #?XG'x]UĀ,V(X7Fj`Nzſ=2FnMُBgg$=?[-욢E(q8 2 !@xxmP$ fExhrތ]-5ݛ^?u9.+HbJ0Oo!}q'8`(.עxJ(n=w~e)6ϚCԭ!ZNJRL6Po2> endobj 205 0 obj << /Type /XObject /Subtype /Image /Width 23 /Height 23 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 201 /Filter/FlateDecode /DecodeParms<> >> stream x͓M0Fۤ7Ņ(weMԸ0 *ud L i1L٪1F bµjes'Z;( V.Eo Lg:|jָYWezPQ"i:, ȅ)_I< %Gqʩb ==b(>{|S G_R endstream endobj 207 0 obj << /Type /XObject /Subtype /Image /Width 750 /Height 560 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 219 0 R /Length 57377 /Filter /FlateDecode >> stream xw֮{~:=c9mL6w gc3"刲P )guIsB;KjruzRjTTwSV_k|,y3ȚMQ" BP`$Tv](O&P>ѻ]|e~Z B)e&kлvUC>U!+{A/q:9r&~@׽/RQbDo4>H ܕO^ɾ|Ot^bs]1ѲVb5YQ 6P#'`WhW34 5yTjW˸F ֙dmW{H'YWq7DTMlj"!Fm5tjV%w #l%O'l@HN}od_)bc- Cv)腡˅ׅ ^D4~CxgGu=2'݉F)/W^}yݚO?P%0$d q1QY>?wSӖ/͚m$Skԧ%WRNZZB`&`vqf?x3~.*K˟g" C(fJabcq ` GDh|D288dB0snďV鴰V)_^ QC s"^L/ | 01˥/ #cZ5ØfC0Sc(/z[ :5˴N 63r^A(g`gkWv@2}iߘo\K>zuѝ[7>oK>_\o*lܹr,@Nz҅s9:g97m # 30 7,C9ڹcs˖ jAQ\aXwÇO-MM?cG:1S c F4BJ/"넙 ã+)]1^(/*ѣGe+z&%4[LX7]]bv@2 ʿ5\ZcSQOessUj>,)ȸH8,TmO41w-Nh4\F(paPA r90 ,Ο7k5+W[A>qiOn~nJfAy n.ЋWARV+FɌ1^L{}'uVNuv2N2n#Oo;:C6:@gw~;䡟yܙӑW/*+K Ko΃%+Μ6C&Ȟ4wLP .H#+?6F KRlC=в +ElsCo #$û&fwcuN\u'gEj7F)Rkh坿O*w n.Bœ k'Ƨ)P7%f (3J2zpY@'WuI4up+Yn7a`ML3' p?eGdFtAC?8Ї(4%)&8%/|\m$r ļ33>oL<qk'5'0[Ա o@ xiWTCm\qqP!/@ul?EyZ gh-\ѥBU  1 7 6/0L1SMOO|.VR]hc'0L#ŷU2|ǝ3`pG?<H5,lCLeW='7qD,^ pn 1b']1ߑ4fZ5zOV$3tij @&xm 9# oyr<}]]4A4>`#Q/a>9\tM:]P B,—[>0.~0 D1"L.H`SMm [ 7itmezJ#:3P '\ai -t9d'0H2I4vj| 6؆ oNe qa ~hAN#KYAUXH Zr/P":K-r| F 6汙8 M:> 6Tش0L3 )ϗ:$(la NئsiDOB5 .daFu!MOg62zaO&Ifr"MHoS e&Oنp  > D2R8bGpA8v-ET-5ylp}900`Zl̽7ṕ~X Gh,=V=73fk-ZÈ7ci %h`0ç$ĮӨovuex/,DcYjHj@mjsqtwthV{8T_ 45[$hvuO9 <\ Ygv)V>#_`@os^Wg5=3 4f_g~~,47(K2GJEU1H5>N`27j׎1n.ƬwVC)=B){*d. p-@ lpw~RȞ^fCE R[ vjuWȄzgs\\~AQa~Cxc}]qaCK,+GzRZ ?x=9RqsN/3hܿ_j& iddNs◯N`Ku} @.d—dLx'=xD5-PW۴1 %dY-n4Xrq.\sx3d  {,&@<# WZH!zF!ɇZ=jLIƝj6>`!x3J8p 7mVpS<\sΜ@J+"v!N2׳R@)Rkd&o r H26>`enx /hȱwzrq#x80O=<w>|ÍUB3:uz':H&<x[]dxLf6ʤ^HLS#jKPO|u*qѤwoj#=1Uo@kEO Ɂ} rd&b$i1ȳMdEI1G~`̲LF 2A |-7?(/)vݚ/͚>my~^vnʿusӆOϚ zgӛEwDlT n4K8!NAe.xo(C>hsUe쬌-7_MEom0g,лom*-)<B7_֭-.,+^'No7$BDѐ y`ҡ3bAK.-JbI/ ?zXtpys(-!6ؘ͙̜ܱ7ĦҢBhyLd ӧ}ƍݾgo_ `M@٦,kdg:`P٬o;ri Kr!++Mwn݄%&46hT_#6U¦K_Lp]i@W/]|u~~d\V|,$ciF}Nf+ɴN^gjԩU۱׽W/[$ÑdrBH8{#ӏ?"?|]@߂ߏq9rg[>&< _t|ޙo"Pz[,$T:tm0~>:'0?~?Zs!;&zVsf '"EЈ;_pvd23Z_$/OL|ԹVSan?!?5sU)~sӳf'81.v Ϛ>mU7rJrUg]t5$3HxἹ:pJ.E%bfJx#pZHu#xBPdk5.Өbт*ܵF!*Z\/(mBpONw߆d>/ ܠ{o#8_?m_̵۱_?!i_^&*)[B =LHH~'37(kCK_&]7"CpGE" MGjF2͍ `b@[`扛G_TUy'm|=xj'0#flM< ܩԉ:l}guWuk-$q1$Ǭ˞]X0j(k]kdbѲŋ8nwS a2u?yYd!^ָ?,`XyE 5U`bɥ;mRe`'U1Y`WƱ0NZ+ANp{$3-bH ؛X wϬӜI]`+/P ClbQr%w%Lhw_믈/?ؑÔܷ׵C_9ǿX8jWq$1F2U@ud5>G(}z~ךG|՗Zj)7}'L*^s/W$PwBA|^x ո:y/bhp]Y~ IdH24g|K]fOf~%Q:7ܹ|2%E'4NNMq Zu`C7َ 4zuZ}yGv=wޮ(/sݚ_i 6mzMe%cvEmP$n$W$Μٳ֮Zy#7g4^"_HKвh 8dPM8͞<`O'8i"_r}*(H*NNj  `&WӅqk`&7y9Yf(! $|5^ٝN1dgS*c.jb6a88%$wdP(oAˀxֻ2z-Iޟ__ |؀BMe2nU+b8A4 @t ʗH2]H2(/Cd|C&pi:E2H2(R7B;PH2(THn#PiHɠPS.u#r PSd:$c50͍ (T(0D>B;idL/;;TM5ɖ,h"d߅ﰘ 2hYi8#*LKG;PhT#,$XBnygӛǏ qMI"x=u Vz{{lڸRZڭRRj* {Lw1kqA9O ;mkAx >SZ\$%$HF!ȥYӧ9S+/OV<#x R _L_zܢ^bFZ TBiTDO<5Jbt;' a@ٳ?M`؃_".^0*#ï\pΛ TA]N]ljMuJjrҪ_KkܹET֚ ᄑ 6o[-]Rɝ5J%7k3 ! 8؜'> -?x3W "\Oy> IN2"7^!Y< wޢBކDr =KyᰒmkQsǯ OdݵO@Mc r{,FXmEGyaW.c'iysݹ51>oOw=mcByQbuk,ZgZkL*xif6~:!\KDծ5J@"*fN{r/ȟ> A2<.Qѹ|~+[Ӆg`\V>qKA`e]GuXVaETDcE [-^JT:'5w!ݝN Fsk3֬޷gFt$1A+HnBGwzo޸onXo׿n(ȿs׵@7=ugCgj3׺Hph߀I i%aj:-,\Pkr26 <V"q}>l! ;8S(]ni ,]6׹dXwfvtB{W_~έDbt{FT}QO͜A(>.8nm|A2h`?\pEU5RQ+R~H2Xd\߳>u|ɖ~'HQW!C')/*eR{_#Bwz`+~7=]SV돮 Mks#Zg3m-Q˗-%~|nzn(ir>r_ !4 \0=5Gϑdtxd2;3Y 5iל5x-F I~yw_aIo`d|p7_~wNغsa|4pUpHNJx _c[ ܂; ;oC56n3X$%V !47\0µxޜOq¤=}jἹUeH2Xd(tQ ׿Xgs4جPSo8k of.JOP΂%.?xkW˾NT gЛ62q+uyt]{kSGWE_ (&2ɵ=\Vxqi7PRSJ]w8׭^uFݞQQOw'ݦ#!ZrHsv@;xl~3/ϧS+(Md Y?}H2X(F4o&g$ qd(.d[QV38038؟s:wa IKPLG( qف5Lw:$v`5k׌COw7ji0%H ` ni7tҫ) Vm]VsEYI^N69n0͆epnr  $qb7Tj D>`kY2ivV 9L' m%Hxb$,T$;յ,ɀuVF}.&7 Kpbk5U%t&P$%I&WQkбJVf28PgUe3._dBMaGa@7$`#f⭷Y[$I 4rǾ ;4I&N\S]RT AGpe A;<؂_H2XdrtI2\PSX7!E2h $$2Nɀ%QPaԍg v` 24w ɠPH2( E2s"ɠPH2( $|/>EA|d$,"| \T "Uzqoe80vyH2X& ]0DY.$9ɸ([`[>k܌izO PuVZ&A$i%=IhIH&Oe!Vm\s^ͭs˖`j}wz o@KL v@?zdgﯾBܢ޹~T+ᄑ驙3^^<' rig,\~şi  ?z)پN=eE [a'ɄS$v@;ddtIFcZ!wɵ7_~mLz]Bl̫/DTxl|R?#e$'>~ҙSMI]9vh.NZF8Yɉtg{lH-HDw<\R7v`]B~w.ZL.t)UrC^`N*%Yӟxܟ䜤𔸨)t_> -NŰOeEE^j%?BIv@; `5zd莊2uϜ Gڝ0lo*)JKNIIMJ>uAg⮳ҩުר%4ɬ.-McA'v@;ddtVikt30Qf颅2q NX'=:E% IqSR).sf13|\Oʃ^߂(*쀻Z?O!,l&ЀG;`"d"\]0iYȲ"wݴlRkֿQVD]BMΎ}wz{RIdU*FI!Z~ZB{ N#\ ҝ!",l莇KF;v_,#~iEdox~`]Yӧ[fnvSw(H.'$ǘMz\(Bj1㢡 ɠH2Xd@2V/ *Ņ.5hJ$pZ̤DGVhCt`$uh$,~#\$Aοuʥ Gʨ(T2'3ȡP[Iv@$9 Ʉj./)JKN|Ǐ9 KXCE޷g  _ITCdPhIK Φ T`Sdp*d*0ԍ& j'%LI5iR'5EAd+е"ɄlfAVɥr57ufdh$,AO28N&d5 WW40%u=۷UuwwwvЩZB{+18pJCdh ߒfdUj4.e2qysO>_m{r{zv'VFv#ΜxnJʹC{w̵-m-Mtlk5ך%4JNG ZD>vl_H#˅dZN2^bn'(egfNĊJ.0v] rNRtxJ\ԔT|clmV3F8N"u۽sD?PekHve; `!h}H2E/"!;@Κ>{&WRsuT%d$&MIE:zȠ4Yl-zZB{ N#̪n/Ive; `$~K5*p wS73MVJ%&%M蔑h1F=,aݟړPPIh; `'CidjZHITlIvdddP  #; ` 6M(T(KƳB;!/3IDIi $s :B; ` iAAD90Ivdd; ɠ> `# N'QhGbQ=IvddEqb2JT,֡Jݔ)̈́,ɠH2Xd&SҮ5*ea5Podh$,H2HfRERJ)&%@ZkSI]Uy|>|8X< aE&Vh3ׁ*}g+P h;_,IB.Z"<8m$CWONwk&lTI5pꆆIz@KLh "I~'{{lN}O Ks2%w?zĔv@; ` F%:w@!*&)> ZPRvwuuөhm;qUa'_שH2hI/.5 #ʩltEkVP՗ܡkO9m--Mthk5u\Xs(V]:s# Sd44n l Ո_F2rKօg`%*ʻoɐ)KJOVo6ۘPo3q9%. ŪpHN2ɠPh$,~#<-UJ(5)޽!H#g%/_ԠUzdF)KKN2교f#mFyԸ$SbJZ(XgJWFw< !h`;dP u0DY$i)RJDX5ewc/oaޟ2hTJK ?:Y-7sO;bUzBRKyGS>Sʸb 6*1"v@;I D CtYJ!8bb2`z´%s{ʤܾy.RS+*9YP;eXIBM]-6)e\ !h`;r3dLJ- FVIk,'uՕё qQ&V!)5RDmlL$pSCH2h $o $3Ieio߼p6,:AV+ĐJXN~Bh)S7v@<$3]!L2 B~37L*/UW_H9/VhS7v@$$޲;/;sؑ֡a+&ɒIv@2Iyd2V)5) X SOzR*./)ʿw+75P?%$>3diwA*!"v@;xb$,dH# f컰d :&9vҥnOȒ $eRLI2UrqMrNG;= ]w:vH2Xx=]4dP  #; `4 #QBnb֠J2hUE Ñ#س=Ksrxs6{Ҁ$F]\pGtwvu ޾}'@*ıx\$v@ddHr*(uiMm[~syX!^Bvf:\xajB*ZQ_ghmimnS{kKpp+".CҙSɼ<XMMF9AkF;vP xB.)U2 N6Ț80`nWWΞ>OwaXٲg(wҔkVkClcG\NB*):Űtg~lH.HDw< h`;"`K- FVxdJ), e)bKİU) C} I%&:RkSլU+O=bUvJbZBLeIascS7e2HB; `5:UҚJw-9klp|9/?jBhЩd1n=ypJldfRU ҎI?^;G;oac6ev铑Y%gUNCPLDٳR5Iq1P[^}#76IC&' ꭵƫW.E5]}=(Vuwv65 :3OJ_|q c7T\,D>vlPN: $!A- FV$S)^yiQv+B `UWEG\45rI\R*>ilt=ԩ' I IF9LR5Cφz٠U)ebH X*dbFql6S.n$%xH&GՀ$R]<6=Y%E5eLK9oVhS7v@$[".}3!.\ةcG>5P[1NԽkǯ+dh$,SdrԍJUꊤ{5SOz K oe]',aj~JT=W׹\ACAChO쐃.aq#ha4IF٠X%H7N4*ZS)` x8nB{%?a M[?; +y3W\\U2SS[TCͼ+^Щ_֭t\Zg445Щh8wוsaϡXu̩Ǐd$&Ի7\R7v``'Ïd^&/ˈMa'O۰ߜ<~ VN;7_IΦgN+W.{.}%٬zfS3q9%. ŪpHN2ɠPh$,~#ewd$Uf\"Zx j` *\\㺇.Mp kjmZQO'VT!$Ň$3R\T*QEm{ӏ6;k>;oyYnQawS%曍FI'I+/)Iq(V'Ċ+JZM:͔HehAd"2.dO2gkqΚh ҹ NZfn',FBSHZL.jj:{Qhoil0鴓e  $I$Tю*%YTr+ g mF.#*W*J#\0hRQU2QAm#4rL-!Dw*S7sCE@PhO$'PGYwdPOA5$IvdTrM"=+P)xn4QhV; `'C-IUecPi4U`,ͭB;xb$,FEY.dlH2 L"*-(.%C ɕ)íIv@2I&W$3*\ ۷޽;44:[P[1bF;H2Xd!M2pY[*~XuUWVԍv@;d%MRUΪ⸨pUBF'M~?rصrk\.ا"\ZtvUE@]WA WBhcA{ ;0viאdL$ktideH敗ݷVr|vb`Hei)z:t'-Ĕv@; `$*d Ӯ%/[b׬xqi[QDMϞ1}ݪ)nSHD[6^9rNEo;~p߉P ;q,51VVSʩB; `mrVUEcUQO8zV8v?rփq(Wx1:B\s˖mhۿVW^x嵫]tWp!Hf5Zhu\Xs(V]:s#qtg~t-/}x AChO$L2tid $#({vb墚,./eNN)%"gys+K[7 Ca%Օ1ZjN:l4;7>rJ\UI/ ԍv@dddV)cUUiCٿm&Ƀ{0[Y?'TR19u/Yf'DyѰ(Fe5:YLBvԸ$Sb* -f?nJ6䓺h`; `D2E\$[f{T9ɬ|a͜ox~F.eH݋;wBZ]_`~IR#W\T%'6N&FFf&šX+(imjԫ( UcJ nE UHDnoUe%۷m]8/?o(x/ QzUye8R*νyFBR#B*2t/]jokexTG[kcMVN!CAhavXx4Z.u}>ʣ@=dgܳ >y{ޜ[$.*rǶ<5sTVo|YӧANH g_5U‚.Ƅ_ժrQUZ"%h-Q "UbϮ\xn `BH2X$C-IFfW(.2锑\Tq߯^V$5JuBv9 mTV{gin\np|SdL$ktid!UgHUES2VN3f^MUPؗ_]Q[n4Sw>vA;J2\d-l%$r'̜wzzZ e``Bf(-"n_oYq14)DJ_"c%ᒺ(P(^G2/ ols~*;=5XEIQJRbwWgG[+;;S%n$$Ԏmy;ֿfxoKeEockWLKJ *"#Vxqi+? ˖J*U=RoeQfuXXO;wvovXvXjB5/y墴˗.#ݷ72ZɠoY,f:5ٴj xŪKgN>~$=1̏=莇 d\SZ̪XȲnU3|X̜۰RVp繥ʅɪ+ȅ|רwK/yJT^ai-&CAG'Ihq9%. ŪpHNҝTsPW@hv@x!OFb.-fUya~+ܺs2ӗ,\@Cw5}RVS嶓ȗ׮yzi?G7lЋ]XUű*̨J:AnI'JoĴ j8ٻ!!h`;P|.EYHdXU^x'eG֐p>L/]ح oU.?ߡqMys)I;֢|6Y-]mblt9Ά$U]V`gdddVFFEPϸCeCGw@w@w; Ȥ(i4< %#`3/9egmŢ5?>sXLŸr?=喯'a\\*B|[25AoX2CP?<.WKOG`_vfz\!g4Fdt%I)]f֬n&wyMX-0(.$>g]H=Jc]U"!)^)=tpX9y9Y}RQDXz{r2 Ĝ-DU9)C9ҨM~tjw@I1I%`̬ll&kvC.iBl!|+3-B ntttT2*K"Hy_ng&(*t--țSu7.?[SQ.ͪ(t,a/ę˫6x>WX-Q9TN6|ttt]sP(it?1g,trL?gi M 9'\|yB,`syٓǵ4jmnt᫸bLM4Ѻ2o44UPɨ!M [cһ{fcU7/J 2U}FT2%PO Xu#*R2w:Цƻ;; sS)Ͽnz&Ov!1١lt;ج Ƴ%(*rZVTiRT2 p[r2Fn PPt鸃NOxbtvvdJF}IST2]Bބy joGLO<|ta:P\AwgGI~BQTix4Чa&8qL3mn6t ݡ0O ?@a"A T+p,*᫸C]Ub  #tI"g3a aF OJ)JAw*J?d:Gބ98LOLehG?}{>b4RqT;);9Tv&|ta:J?JBSi(i4uAF\ʦK?gӏc۟ID⠒APg.L;!b(2U۶8̳ۙX:LmL RGlȌ&eӚ|ޏ>|xV y1զ9)O Lx|q(0ewj+KQGQUi)J\zK*~ϞM ߻[+'[u7V>qӝ蔀 FmHՍJ?dn\]z s'DfdoB0?:p%kYRϭ6K|K Tc.[ljfҮP OG$cnߝM 쨮( x7ˋx_PC&LPS~P; qJT2QQ2J EISbW2>gBDlfAVŘ.QpPa{dbZJzڤ/trTdo|#A&Fuxb^Wm"^}yIce)BAMIARB,noOg&r􊒢6`Cg|ԇ89T|ta:d3KZsS *UOji>~$.G[P G23G.IMJdLFc}T+jKDS"lm>{U7Q{>yŇwo ޼z&fe ܻ.Īt$w{ckVتnttt%% [p3d~vcL+iwmmr޻k$v݌ ϛVĤ ^n&r3RUn +Ź0'fZu#cѳRoeevwD|Xzs2 ĜN Fw@w@wPR2??w3dtu!.MR@݋3RQCMeb\lfjJOg\,[mmD!O {!TyjHW!&)?TX&tZCmoG?lr(JJh'1,s&Df揗6)bHқ.;SYVa2I,-x a/t8QUj 5dC}Ȅ j8y(tA*Y;=? 8??~cn( 2!OSi(idƔ 3!Vf~&e"sk3._|,l! ^c<{f Ԕ1̀;L"͂[9P ڄBQTid@Hy 23؟tJE"n+K^t-O$&PP; @QTA%]21!l!{Ɓd!:#%MNRܗp['DgUAn PCAGw@rMO?~x7S[ͫ]15][L*dnJAw=wh$~O8?~S}isFcNM/LL\ $cjMՍ&@F^>l,8H˥rD&$ޮ'F&{,F wo$!ԨiipJu)aT2TZQ samE>U>}|'өâǿ_>}mnjJEk޷g' ?~ۡG<~0:pсF6>QPRTX^^|+Ȑ+W%0 *$|2hN}Xukؚ2Qɠ;;́;%y޽}xV-Ntg2>y?/3?"a{}}}LZsc]DDO~_??V̙>Sg6oT?~o??_LM׮?d2o%|8<鿒1KRAo?8wo_vh,`XbՍ0%xdhVQm ;]p(񳷠aϯ;P2w}nI {I/uj5}}??=yS7deDq$1LTpɒAЬѡ{J齒.)(qmjhܳ{ I&xRy>h"ъ2e%d6\)5T2!污aBK)ڽٙ z-rL#I7<\.o^MqtH-5r㇏hɣGLSף&F!PSZ$Pɠ; Fdk;׫}{og&қ(ڱm˱ýS":@!jTw^^Kgga)'a)+eu̒zIyEԩU.[t]}|u6窭Ղ}z֍;@e˪+ʴOm'oO"6JQƀ|s?oiBE.΄ƺ],u=aZ&T߻`& vwF_w۷Ֆk_J)?UFw%cc9v2p+=% ʭM؁} JT\[Ѕ0MotJg̩b,\|nSn`i6QeƐo/oRS*-]fuȶM-[/Yǹ=Ŀ.tXv,[R]VejMjy?X1|/%W##UI*U2~gm\skmšyLۗzU]ɘPCljP@}85PdK͝=QkJ ,ʃdJAwPT2w:0[Ν>&(dG Fk:\}'r$Y'i@ j> D`G1ԩz ̌ i{w\EǷH҄6E?~1okEw,B}w%~dBMJSܥT [j94dLzc];@/.ˡ8\KD\v~vfW^owoW&t%IѵEJdBZj*ʋe[387}2×M_OE<(DxcT2XwƱkDWDU\V5&,=5GUQVV~l/d%ku̿_ ))466z{z9;:,[YV?aj^+oOc#qM0?d@ğ<bw2MMOLSǗ>-L+87ӏFK*]."KS|{loǂ cq1 ( ]MkjC.ĝڪRdBKkz>U*DU9oBa&?? ؒSr[ݛ]m^8sJeKfDء kVRab|LȪJ8Mp"8w2U>Z9rbjx[P`*Jsk^3͒+-,,L"Dի\]\}.ԩ:,]|84 tL&Zj?}|!jyF %mFLi~wHD{pCU?x!ݳslj#b'>jJ ZPQ\causm5! r'g{õ(% d"Ă]irՋ޼F&gwCç*E3b96jO?@Tu;`QGwdvTi7mn<{뽻w8Lbܸ.j{YQa)p[`kԗt1%SP/׾eCʇVQV_U\p+,lGnVyK,e0MMM%+\}==S+rr8@՜:yM-inv~/2&'"\W@6%pjP+a+07b*cޡoBԵ]۷ae}j(E9YJ,.tsvH;yl>sS=xf$S=-Mi7R:ۤ"[Xq dG4񹪜!Q@i2/TPXq[z%woDp:38Y9N)V9VۭM1qksht$&)#Uz8IG##JJJ󓓓}m,tsq)ΠN a;-^|ꩥts2̧OnޘxEIR2Z*7^L(!!e1`+se۷n^1 ¿.DU9)C-Q} ʼAw|NI~wgO θ;RŒKCJ.](^,<#aTvߵ*Jso;{*4$5&ĸsY-,0onjZNz[+Un߾=. 9)3%y~'᷊,@"`wHJGP4UW[%SUR6ߐDqبsgN3&Ik,-?{a/1Ul Fm (ntaBw[_UV^RY3z^<{ZV\'"F(1:b߅}2lƞ3O>tg~8>f< ZLJ1ujiqU9,Zlid47js32AxL U=SbiHuܙ‚cGm3SK sj*X!It#(JRMY'r1iuYqW/?sIB,`w_GҨM~^<}e^SՍ;h\zSnFZ^VfwGÇG3:ڳ3piS(J:%34)%#b3K=~$  ͡4TV`A^sU9 )W\SP;;;L|.>/+=.ʹ'fHSFcNMG(iJQk%PY>pĀ|\xz!Oz:}0e^SՍ;hP~̖ʪĕh%JzA?uUj> ߇2Fw@&cژǞq <#ա(iTJ}HK%\[52/GiCP5U2P45Jj+Z]{ďe^SՍ;; sY(+H8,FC) ;P; Kڌ(fS}_OgH2Fw@,iTJ][%x3b$j2Fw@,ijV+hКz;1Atv9yMU7eIP2ڽ]sX{[&AACeT2 *Y%Ӣx)A1*P :cJOAOAtF4OOG#d]|4U%%#a5*A*/\ain ZUUR1`P d1H@hijKm--b\p@ԥvVx| _C MOF xVF`NT9ql릍xC Jf LM80-Z0mDP >(|e8>޷r!O1&kf2o#ddA% +liFc=F ̽i]R1cE;#MF*ZΝxRD ~O߃߅J.A#R2".dD|+d3 (4T2*ɾ]5IX>` R2ɣGLU>gy3I?}S D%ۥ'.IEbH,I ,:$b| *b6R>y4P1c9 d"o?}dS2C,cB-B//gv` adM A!nF^ɴ ե޾~42h..d=S2'31b't9L{W޽yUgB=32}2/jz'#srsy8o{..d}d XLKU;߽{-훗}wrn)"ėOK p@ɼ3`@eŪT2!)2E& LV֞/xDW/ uˈAtOڅ.>`iKm i|qs%"B-tၻ$2o]LZYl\TOW/` bJfkHߓ a/>y@-OGx6P; 1!دBV2OXoN -kV]8}rʹ@@ ͸% XRnW\z@1\f8LE(e}ϝm!񯰕 PE@-'=:jݲԄTSV k&) XG;[;kq׮8XQU &47)ZH԰Jlo'{Bκ`8ZNHm 3ˆ!NVi0[:9~qb4#Q2QW}W2.}/9(F03.8Ha)7[ %Yp:@LҝbNV,ttrRfYAo$D]ssq&dHeCXSB'o v{m7)UM*"t,BʍP8DRvsvRU2w:Iݝz+=&Cq`ďEh3 j8z$>g TlLąboOWmrhFd*@XVB';[a+%yn"6͵0;COfnǐ}vOV&^ne%c'ESJiȭ(sc]Wrn `YKЃovj2 }vFrֈқ-LQɨ(**d~4ax!Zx1ft!jjP<], E49蓁{w{sb^*j)--1&*@d$uO|Eug!1W.B ҅q"ضikJArQ?yš>q'_bjaSc,6ųf}󲯧^w[@8Z+r])$Hm<$V7iY @3A+-p9G޽y 8cadO~p?%"sۖA yJWa D!W(v21hfenF8z""|m*݆up͐}v iI7?x!yi2)V2R2">gR,05!mBJ&ENoϝ&`kgeZZm%-5u{+KJF]<0Fa*Kylx:; +cb_h'¼M@jxk[Ԅd`kԆI%s[c!A+Xbwo)͙ɣKs3чՅxU[j+ g:9ú  H^ZPLN[j K-!ّ"ev/]w8Z*2 uY;cTdP/˺AIEl]Z6XPUYXY FE}yIX|QLq[UUk<b6 kLt̘ʄU#C K$D]z#6I3\>w슲DfXD8>W\QzEm@ɬ B$ zVB{uiP q`_J(f\YV !apx\KeȖV=;8<>_:kyCP䶪8{yDVan`vC1}}ppw*h L dT2%Yb/W(|~5SW]Z6H aurQ˛Yq`Qu Kˊ ~0ԗN,s1'=57+Mpdx@@G4+#..Sݻ M*D7Ɉ򒡻_ r9.lB,`v AQ]@3d$<6.7#-spQpippj/N/4/CDOrf<(w^d'dZ[bhAcK&&k)Z] _[jY@4fZm CmNܰс |&m#h#RPU ju%;F;kKXSViZuA &hR+@,E|2*RdG !A uӆUrn}#JM3jfB% DpR#N=B&D l*f%pDdxժGA~@}psNMPW޾z'quOdR2pxvMAvS2ٙsͰ-VdԪĈz} iwI ڌCoP2ns1-MP{:d숮$B0ZZ0yjK5 Ҿ{߮rؤ]m!+._FM뇒iRXtAĨ x|E%C{ÚK6ي .4 DVB,}{}KD_^ P{kdg&/Vr?߬ɚ u;l=b}dRʀ"-MwV2jP\H%F)JW|JRJF2\gAcC|EBz#6jB%S^c^⣮ZZb$/®Ύ)YWm7[{)<\>w요 u-Uˬ͂VC0Y Y Ad>+-~$X5M1Eo ??>+)()܇>>-nq[x4< >1_n*Ck47#-u+WX͇6A)ڽ٩> zf$'ƛ1 ]MMݦ8שg#p?-]q}")Փ4[+fJZD,ޭ}c4Y-07%Ӧ2l'}ݝ=r) b@(T=ǯW~a]=z3ڡ_^*iI7@]\DANf [cxQ!d0PSǏRɈ9,3yaQ7b\)R=) bt+с:'>5AdW2psFTw˥ psdh}dLkǖb%n,Lo%Lݫށ ƪrjfHK%CuI*(tEQ HdG;ᾍNߕLvJtiY~ u[lv+!RSN6&JIKYmenY_Y{V^XHQidTjtEjtUabJ4oT2pW2S=~ bl<}-C6KK].Zj:ـ;ެԠZ@qёT]V@"D ZɨMݦ. mJG.AA U2(~䈑۷=cD_*_)ڽˋsƺfbl4(t'{;<*XxHg[ JfP2j"%*JAg4 HHT2vT2 + [# 27rwHUvoI^cf-fjnj0 Y[H03U}{2 > VɨMTiqm2\U+BVBT2N 9U%# !1&T䈷 1NP de1$@PۉR?DD8ˡ 4"@iKogmu1 xW bPJV@ 1BiW 6o:r蠔=z8=`IMMN;*"8 dcW2hd쭭 x,30"*K~>^paoccfƗ9lk{šJsxқ}< Ԧ__Yӣ0;\*dJk sز[)j7"%Q]R8d.o)o.y7c Mصk֯; # r)2\vUUOFu<xSG#-ME9t¨pQ2]2 cҮ\ꊝkĤijODAFLƵ!fxXU{5YF/wڲ kJ|==(j d'hOkk !wSG#e~BLI!]K::4UWݵm ;ezr]$P`<&hX}9ǏHsSC@*G[\bl޷a[n,trTټn- 3ʖ ).VB\ЊKX'>QkT{)9*(p]`ka>aMsFԵ %EAG/T(HZ,͈4(x΢0=\m뛚 8NFٺi1¤ :U Vluv6E҂~-/Ȱ&%R[с\ڒӽU%~^F뱒%\A]] -㣮4' 6[CڱesDA#{ZL8!d:A+.!FPp vK; h'P;mT|DaMo@DR2¥<99z8¨pT2 *]O&,ӳп'R[ T I%kciХH1!9iLqn"g{hj%S^GFMa7jGB'F+1낃8f"ZWP  F9@,DP rH7bȎ 6jX {JF&FA cS2B63/#&;5e7 I5R˓L/wB x.Ap_CV2&.W.isxZҍE.fKck4/jۋA% A UP  d$T d  388+ #d3WkNmgZ̙ɣ+IazIT2 KzR,NH}ejnЯZ}Q  dz6T/27[l)1Mr?_&" {3>}"r^;p0ޡ蓁vz5rEOw{k71m,- u5҅ȣS}<7psHNH_K%z7L剈p 3SMS%A̶К"mٸ^Eֶ87KʸsNjFɈ9,3yaoŸ:;s #91ANۣtp'c\tsvH_K%z7 Al7)q %곒#?u~:N9N)';[bGzCj jwrUi*"h#Bolk V abeIţ[UՎRnj(pK- )Tb_T2::N)sSIL al6֋ފFW'Gi +H"zdf풦Joomu3.VB\kKX +b 8a_Gt?]ٙ QyiD|>>$lWeqR6ڄ[;w.!d锈L =(o'psq_o})ʀ0q8wb$loCU,2C!ۿD7[sh$OA%6o p"ïLrIq`aMh$XNv\ <&0:`Ӛ8M$ϝ3iDsYSZt)h˚ktG(R s|%)ߑZ." jtrkK_o+Y`t=Al!Lm :M ,R>8?Z6H=muՕI 15%zA}yIX|tMډJl$E٠徨zlRf| 1MڝX͍٨*)̀Cb̅ATS27b6 b4Fr+s˖kM.%"B¿7c3(ۊ@ zGT'%vڲ4bǡ78:%%Bvb:kV'ϵ~~UUߺѰUھpx;kVٓǡ:y4)o *㢣z;$BXUݝm驩rՋ޼ ^=x侦ZT+҂e_d- B6ȅ|pЊʒ">q22`ܳC ٬$O7WB-y!A Cl^ ٶycZSdء-׫VŹYVk~>^^]>wctuL,&( Q{23/d":;R0PkAN@SS+./}Rٲa~+9df`%ɐ[7! [%;</Mk Zo|uJƺ2oEPN2o߲0)J%zK[i)2ꐉ3R!e_Q,Fr\.H2&F(JW}D+Jo%ĺ9;Y^t߮Vf׮PtHCk ݷl޷z5%~zz'T2j2KSV2F[H hekk+JJ` a검11שchkaa!Aro,Ոߖڪ@e7VC0/tpFr+19 9VU<vhhdb^⣮ZZz)dgKx*-)`ZR#~oF%3* %f!Ɗ԰Aw%s#&jӺ53PDהso}Ei[6',BVߌ1dJL>dİPgrw+/4U12nG}< M=\Q xxV)! `:~:N9^V MMNy{MvdT2S]A \Am%oAT2E5=A' :$6\Jy: :t@]r@S2nxXز1> B3({W( ;2=̀Lx8ӧz¿2<\bn8">!N/wWrZ),85"wJMp[홓B,L(ɫ+ػg Wawr7g'raVK| a4g5J\". >ؑKv[Iw**tQ>x0'^pE.KĞOL.΍k~9lH%bR^/Ǐ[Q;}ݥsg5=xfٷpz>~U)j{L+ Lqvt 6#IAxpr?(' o\#Kr )KN])奔[7MivnkSVG9;iB,wgO1bJF8hy߻òn@\*~3 }7# ,#]ՎWjeb%APɠ'^O ,|Gpx"axzz{{{1i􉗛+ڼ2%CacJ\KnmI! ͍4I?waL^\Jt+¤kWlD%6տ}ybͫF=XbGJ ^yv`%APɠ9+5Y)8@&yσ<| HL̓ղ+6fM}}B=y^ɀvC% {pniI 9._qί2ۛ\ .}r{vpsZ+vZ*&=|*s߹0/#*(b2t*ŷ܊wQݠ"Kh짹W|QQKD!0;_j%AxE 8;:.," ձ#fpJ +nB'eUSVBdjC}<=2LkCgߌ%3 r/Ym$B,/n’ғY_#NW2fXo^vpoώĿ?OI/]y, #,#BK*EvZ G!3p|DY"~09>lzjzjϦ%߫)+, J r2Y- ,aq0ʪ@Cs)atLd~uY3iÕh3;H~wLFG-cF 䐉ƹٝR+ɘGJ /A\ӗ"0{}!SV(HGi -E%*F'i{4:h3;,.^%UA\G+nXaA% ~ꛨLp%A!UB7AKF?wOqnϹx4śxRTXGw} T+!dDž32`!ugN?$:2QSMx:;: iTgKt7VWnL<^rK)I0H i'ƆdžuƀPm +dYV_!o֑."4(v{kgKy)8e92I'/Gp%A!>q&#) rC]mḒ5f] ntD80BdI3Γѩ۷ntA}RX:@dGp%~^Y,MԖ>nd2\_tH481ƣvUnmS}j|Pkd.?y0u ZZ)@(e($6@A+T2KގM ǒ6$+aX sޙY_KH:q$Fek^@#S>i HӃ4fy gN%"8]dX(MH Wm-i#jEJn f߾U0;ms=s{o߸F\ncבSV`۷n޷;n?ʆ+  d EvZ# \nwgVj?H-/  vvtTURDx­m1` iLG-߯<):c0xkCԆ:,}1-/*,+d" T2񰶯lVdܜ$(#0@My)̨ZJUD%X1<CpǝZZPeL$ਖKt\ HW#] sc6Ec%AڃbW7}-+ۖ*͓YWDF.ig2r35Nplj{C(\V[FTW۫+ V[R9W.ndv%CG$,dv~vsS=m5޻+ >)$]K0[ 9azߜ$6@A !s>o )vfr]/̞_./<$ܭ=iK=^K^n7^qurE8&hK\D7ӆ?wi gN#69q%b%'tL_OXx7V%B 4=M; AF%JGpcGjyJ`I%C>#®|V<}>dI$1u1߾X~7:rC(ۂ(~!MU^n%y9Z\˾pcG)- ӄ]QIo^~7^̼y¨K>?X6 O([6Fz=QT+"AnFӧ[D A,=~MK4"%z`pJ+Nv+ݲ0nRb4n~Vzh`袚r4W6F=aY]Z1,88kNZ[vô6<E|\vy {ص~ETxƢ/0KuwmuuZ#fCxWa+AawʽK#)X7.ÿbnϮwg(Xa |\ұVV)d6feZ&9i߹%ƀPrzQVdܜ+&V-tXP h㌚JPU! ?|ՖGភ9дm r.sHD%cv bؓW.wn^G{Ac樈v:@lf٠sS#- @fH-uYA.Nz~ĩ3` )x4&w6el=c^9V&fg9e޿o>'.$x13+%[hͰ {ٔA6 ^a9|'oYURX_YfmEZgSSx6dtx(P.(-.d Z>ZQ6GEZ©*-PɳRJwr>9y9F%A,=2A<[x vbݵHlC|av8몕"~GS#>K!Z w{{YA̺!+b_Hc3CCnc~Zkd^R[Z~>R2>{YW|u]Fˤ:+ёQ:&&%}EڒB< _d4 r jhKl֒SJa5|aBc8>6Z_UJ,Z<ȭ!'3@Fs1qrX]T[M>iGAEl P[Kx7>'(QNik}DꅓC]CRΎ_]pocuL@a#-G92t%cz,?o/ۢIݐE(_0Y SݤAP= <xP\ -GɌiyN-g63-憂4x42JO.yrLoJP Bh$'/3=p/3@o T |JX`{YjdV풡0 f1 3\Qw̉NY)ɤ証4DPUMΓp'8sG֦jQ/ Xͦ(X ʙ?y$B^{;X%Rvi :[=\]r32w7A>%jsNߓ_rY#$\p!"ǚ0||?k̾ }*!?62X^ZҧR>:50{NW> M<툽}㺄Ѫb{vTMх9`TW}a}h 7&zzf⯶JX`{HyƄj ffHĄKL ,8Xj4?T2Yn]L!kBP8L% eֆ:H%ȕ=;;/|X]A]?e=}q@)+g YOdSdz2^,1W?_+1[?$p_v3C~LG1-t'킢6fVZJian@I2LăZ.Ks!l\.! GT2Z/<4nRxSAܹq-*< <%YΜ8ƬY2qS2#ìueoĒ(4dmqIĦ̚ڄРS+bGpXI%HC-su>Yh  bJF4g2*ĒT+u*V)B=ˈvl-o y@5iQ v b؉Y*5@T2%+!*3ЃYx-lQ viJ߀#dR2x bgA,Z&P <'X< T2 d| *> rLjV)WKDƀЅOq,5$ ,dR"z22bJfH2ƀB65񷦚r*cFIv43CkJ$K.߼}Ϊ)(Dfc7^|AɛW/f޼b6ԁ%7A_ %351b K {nzG~fa,PXnsznx/zݺ1{W.Y1WIum-NB~H?i'(J{Q_D\9n "ݷ{lX(џ޹y}Z"rsv)C)s[PUA]zLog@￞x>m 9I{9yٝF Q ʔHkJzû'æ/C,ä^BQ:9|pF*&<7EFԔ isg N$":;:$%&@ԬdaNmrW?Ȱ.%#>ipפ,J`Q _UR4e> 'lNOt/-R] DP !;S 2RF׿LuϦ&lPZ򽚲2Ω*),d1+Y|2b%P Xf GC,C,ä^~r\"q  o߸I7 ow1R>OZ0oI>{{CHPxHt$I ˍf(?Q[#z z /a 4 d>4@I2P@?/q RR JQKuUc f{4:و9:3j9*k*IJ2.) PHk޿{<6h oM2LjPӾM.u_Ne4:N;+:"&: 4!mذWdm8Վ9֦~ٓI͎9>c4T2edLV'4T;93` &A* ppOzX`,En`5JI5:J!R[Fs ۫d,EqnF I?/>eƺjeX dclfSRjTNzjz_$j rep@cG#ݝ] La6$k_MYa.?aGOĪ T CdwbDk ou%2Lj}w8gv8:zFeD`W6È>=m'LV$ 2SwlU  jao.ݹ-N@>PyÖE ‚ĜS D\Folr)C)s iB>RFM7Mt0p|dTJn h5f}9o߼~1t_cRfR5^O< d ݭ4%[kz6S 铊5ϦNGSOb&5ľt軋s G_ qN!  0fQfY$)( *.XeIR_WSZ C:r{5PSMa,IMjUKȢSg>|p<)W22U%E a4tV iT2R*xXW^\6`i-Y[>ißLF%X*VFݟO*! )/3 o^trP1`zX3k[Y[7U0>HM.) 2B&xpT%~uq^ Xbq!@ DO<@C*”rL{s'u*9%r>bxPןρ/(ÃU` ƒs֤$A 5T-/K`"J.;-nҍZ-sh}&C(`A!&͎9\xd'}?! bJZ =0f<{1 lr15)IgG_HYȅ4Ȋ Teaj dT2"] ȀR gJ oFa@%Xy\%eO} jB00 dT2B kbdA썉!P 2L,aiAGC:4 3ҔkBqtXt d kzGAQm dkW22>gx@ӯ" vpT2U+>X!A썡>5 }d+W2*@VЬՆ $:T2*G&FQK[ b!Z 7]B, o#&F ]?NA, (d  u!%L  օP2>  Vd4OJF" be5Q4A.>B(Uj, AA RM(R  uR%V X^JaXe{A&rSGeaD  1>2dsf>ΓAA`Z\kAAQYx4|U[l AA,BLϿZ>ixJFAr0xʄV!1AA,3:}Ҡ endstream endobj 219 0 obj << /Type /XObject /Subtype /Image /Width 750 /Height 560 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 868 /Filter /FlateDecode >> stream x 0ð'Icņe>x..5 s_L2v3 Tw? endstream endobj 204 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [358.305 512.231 373.028 524.187] /A << /S /GoTo /D (figure.2.8) >> >> endobj 206 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [354.661 417.144 369.384 427.936] /A << /S /GoTo /D (figure.2.8) >> >> endobj 212 0 obj << /D [210 0 R /XYZ 69.866 808.885 null] >> endobj 213 0 obj << /D [210 0 R /XYZ 70.866 752.69 null] >> endobj 214 0 obj << /D [210 0 R /XYZ 70.866 698.387 null] >> endobj 30 0 obj << /D [210 0 R /XYZ 70.866 658.599 null] >> endobj 215 0 obj << /D [210 0 R /XYZ 70.866 593.932 null] >> endobj 216 0 obj << /D [210 0 R /XYZ 70.866 525.736 null] >> endobj 217 0 obj << /D [210 0 R /XYZ 288.796 166.569 null] >> endobj 218 0 obj << /D [210 0 R /XYZ 70.866 108.945 null] >> endobj 209 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F21 168 0 R /F39 182 0 R /F20 166 0 R >> /XObject << /Im3 205 0 R /Im4 207 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 228 0 obj << /Length 2092 /Filter /FlateDecode >> stream xڕ˒6_[䪱F/['f6L;$EܖEGq~,vk/xA.y{>"ˬ AGUQe]G*)6-}^3iZWiWu>0OH,:@y^EHA +ݟw TQ%pqڇ NyrE*_b$)4 TDy#/@Z**)BU\G+o8; z~BZT/:8KL\yYS(n4- ׫ui tk~1Dz(/TfdIT$i06ylVi>#OfMolNo-oY6]Ϙ#F*-C;1izYղop4(GEP#Q>5S'J\23k_z`ڃ̑*f*~㌗֜N=dH7GlP@<4 v}T0/~z:1Ե ?,,؃v{@;#K8&eIy. F!E|L"@`__.7ltL>Ѷګ{"6hctӓ+?%Aڜ\qpI͞fyJ,0&$|h-4zKo,Ug=و˪. z z>R6\JTFV$`)5- vTf{ezuwfFL/{ +[ " F(}MP@md Oc^D4#<3ج:ƨ N [nZS״ž^p}9nR,ܰG0,e{w3ͮ1OQwz^yJrm6 x K cVLtD |cC ?Hw'­6Ըl!p%'a#OYPծ͜TKXUpXFC3ז|M*yy)o@3 {HM*Bnˉ)/+de ,qIQDIy/㪐b f˰WaoBc̯ޣt[TzI󗂜O-Q-}1#pdrRDQ*z\!s4D:k'E #7uuw\Ir`41K.')Dh}-nrWٺȪåVԈk|1*k~^iW"1i5#tRz/[f?xfQ$Hr2rq`zJ(<1u?-7lP飏W͋wq::8NULo7᪄FTFuf)zB/O}'nrm49\1N!gb)v{ }knˋihP~,}*AhܓxhLI bcuA7L _LjāQw#3%kV@1Q ;C?N&haN! R^g¼g";xyL=NSN~drIf!@#p} % Ԃh/ٛ5d|wהGXDI}`#K9L3x-⵩ra?\ߘRiVUTgC2KcP_e7߂WYRL{?idt$aAPU?, τ n*Ci[!(StEv7esrCs О@nM{f>y+fϯɌs|/cE,p Xs>~#/q".tpi[^W*ג8Điv,!p`VI(W fZ endstream endobj 227 0 obj << /Type /Page /Contents 228 0 R /Resources 226 0 R /MediaBox [0 0 595.276 841.89] /Parent 234 0 R /Group 124 0 R >> endobj 208 0 obj << /Type /XObject /Subtype /Image /Width 914 /Height 679 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 130202 /Filter/FlateDecode /DecodeParms<> >> stream xw\vX{ XX@)57{ozQ{5ɛc{EAJQ: lߙc`]Kg3<̙3ssJ)iw0ɝ yKBVbVuNb@ Ba?[[W-BcŬ 1Z5ݳRf1#=?l)3<E#X*_8bL5P[s]{`Y.Kar^ b =%s": ]uSǞ7j'YVNP4tBDuueII\&srrr4M^`ߚ lbTD  o> Út"@€_x7 7i0f3 GH90%K|0ҍbC~e ;"^ݮ oXAmVeVKB߄+lgm 2;]Y)!B âOiJ]%F zXֱsYMOm?yF%m?GZmuMMDb2bL 0 wdx;;;;\YqXUnsioj;\Zw|̱ͮ-iV4[H6,X-9"fE+5΀0:LٳU1-4;J.U 3+Ne 7w3H;=@s!ɓ}V1 왛ݱcGHhL+جJ8怨65LͱfԘ -V6W@ HKvLfWP;[Hyb&R%uF}Da&L;>%V{bw5_1;0Ւmü|軯$wVZF`R>å2#ì2ݕJPH=㸳 zk%0pkӮݻx*JuF5u"ZýsrŚ b$M8g(4~>~l!D/pk&G1j7| z$ӣIq3"pŒ+in:AA-.3 W/_{4(޾&Z $K?=k+>> E=_Y\NkY: :]r˻Z3M$Μ:</1ןFn*ٯก:?/ݭS._-[YVB]H3WlmCbwBN<2g;sZ$-zsa}Ys{=eZңS'A=,~k*gggJEy#b?s3iپu4_zB%f{Ml<&VK3 ˼ψkK[$40uÆuč3G%I3fd3fJNDpp7:nةSnNN`eSCScEDڅހUލJ|`ttZ^7'#<.tNtv\\1ʜB<MHɗp3nBLdև>fXUnц~g9JdY5;jFxقJA+,ߤLMMjjzLX'OѨMXѤt%ԉuOJ+ bL84j͘&<5FkZ:vѣ$:m`Ux^NNpp0"P^rdR[$q,P( '!q^BḡKΝI'^Ԃ=zwARiEbB-58 ;X1ʊx T1ْ͓D3TIJi0kkEI3ٕ.{͜\VHq9M>C,xΘrMdfW~eΥKq+cOZN+@nn^w?mefrԎX,rH0+gQxHrFF{r80@-֣ sR;!(PѠ4(^YdҚ+0+^Ĭa10u8n~*N~~55T@:995;x9izQ+.dgO~fZUaHF OD\ֳ%Kr;wL֜V]MMM%%%%%%CH*v9wLҌ~|m 22"ǑXD|.[A^?ѓc`GU3NK63+_6)gy<`Zh`i 罜4'N*.R)-MɀA }YCx\^0tV)yPXT[[<{N9km[fճ'R~h w^y! ;jk&tF@(d;BPӱBQvvL&w8>jͳˣg3Ȉ3AMdbm(BLwX8fBW9E؏ rvZހ1:4Bf1c܀ -nH=)/ԩIJ㩴1MZMZ7Vc2ϟJ^7?m@ xsqe%;wPvQ޽{t/t˽gRAT=^R[2^ȝtz^|bl!O;ZUQѠPQ5eڨhG ݄92>̀h]fws(ӛ VAIĔe#3fV+s0klP˞<5~~є+).4B]y飂ktL3IG)suܙK bܯJyw^rr]?_*5(l*suqf654qs~T*r`jXLkXgb]le(/˫)~Xe|XbzG`h"ߺVDѸ])|N8^UYe<_eu۫MBq:hLܠ>VAxvqGq$U$<"%Fhu-8X2bϞ=oߺ5dPnJBQt:\1?yr1gϜڱ7V;cV2b Y*pL>]QVX0mFV1Nq?مhJ|VU>ʊkPpUe>-{or0tDw](y.z_XA8eΒJ$5uz @ ;ngittс/`3e-<-&on8b֭k-6-|Wղ*n*1pswWUӞt.N-+VNNy"Bޠ7ՕARb[oZjr["jم.D"qKI)vr7HT dZ'''uЪ3B8H(:V"U#6wBH,`$J*<.uuU)/@$ݻw3gLB!a8k5jpusW65R}sJNN;6Whx'+8b0{J|;ƀp| > bY<:FxYA: &[_Vqrvx򤰸ۻG:upCT^9}>ZJcAh|M -UV4B#YڮԪJ bǍsLDȁ&߯zsvTUFV> =[%\ ?JlB쐈ywm9~Ԩ)#'& ^_WWW^^^QQР7"rX,v4#Z(\a+8q$pC-1ۿCln=oxmK Y6R&bENf]%R@-/P (ZW15>[tP5 nUD{c4X nq$ .` PQF7sdY !F3/LRFmYf_y|[@CHnN`ɋ7PָZ; pAi`}!Dca`N.!V̠S'"vx\8"*<ŷ9nVզݛUw ӣ>$f%xog+ cM VG:vP(wuuh0ƌ9|:J\]K i$'q' 'NH$vW)?> O~,fɕEvDX~/B昋X)1';"V5Tq\NηZeoՂE6,wIDA3Yg}X|߳,4zG}khY~2nVw (8INKC+j$Im]d|Hh0==ʦիV 8hСYR)=>b%Wu2,GSLx@lfEPO+Mj,&LejG <Z ?Q U ZMҨjZR)׮]'߸@IJ7\vK*hd|ri22@?|sՈ \]oܠV5km yvs_v3˹PdZ) ,طoq11!Ɛ,\ЪizʕŋǏ6uڧ*+7mJ""2%LJݹs%1v&X+PXh˗h J 3Eş}񹪩 TNhJ1riIS˔P~!qq/rsN'O;o.ێ35M[w|'׬3fL]]Ν;;kߡ_{=m۶?ϤVx_ͧ`ZҴԝ3fͤ=vXJJ :{,Q\XaHgϞ#F`RSSSb1"#3d ˼pxfXF -mϬY{}yTf,Ln\|/&&ޝ7-mZ4BȣGii=2*rq6KY$q|3fGG^`r̙DWo5D"Ѯ]F< ܱ؞\x֤}oݻao\.oR*/df8p`lvʒGG%&t>#kkG?j[l`X+y%8&bHw]|'|zΙ;$vrbKijп_!23c*X ULt -BMuͺ'_juScmvG-`i.^4F#e11?3m=ǖmN7'O4mکȣ`|E ɨj'L|{Op!|GIKeeKFEF.Y͉ K@0Z%ݻT*WXd1}F #DkO<9|\dIdD B:66:ѣ qGǍwQ2W$' 4K@'OF F Nbc.ZqY"FsqqqqGAaCܿqoݺ@ƍ;r0tƍx@hI=rP¸q ;z)qqϵ\TYQtȱ.z\flxRDThU˝^[SsӦNNyzա7! N[ ^^|d2F3j4O9}FرsfϾqq\7^sPqqqr,ooo@z*2ZMuߎYlYccBhEaa7mZpQEEExhl:n„ G}~g?n;6|-C6SN!T*Uaaі-Xxٳ{޼_ G6mܧOԝؼG|3g?`Ci}}}%V]|P(ɹ/_ׯųcyډ޽{_zԬh._W\ӧ\.~vz044|ӦM}ٵ+u7oPR?# vǏ6o.%qȋ"bqO,vݸv] _";yʽ ֯W5*droXlOͅL% ߭_#X[-3nܸ9kLD2ctYljv'O"JǏoK1 3 ɳRihhhEEEG+WO.Ho.ļuIRtԩYYY:rkjjurHlllFF&q$wwS#"""##.\Ȍ^&Qn޼5}tT:m۷['%%Y*`{߽S\]]O(DG9ugϞE@ Ν=7ydZ_ydD2s P222###ϟ;2gDEEuNstҡC#G @^^AZZ;wL6U"L43! ^o޼hP()JJ&J&ODɵD]>+9Y"N1͛&3)hcC㕫W# 7nP( i^v-%%Y*Μ9͛dsq|+H$ ]QQAmcƈN)F>D۾vZrJD"1s7Caܹ$<<†y3=s B))3g<|BpلD00wp`yyɅB1UJu`.vqvЩKRy\دh-^"{I$҅jT&6~N9[J5c͵Ɩ]S <==~x#)dD"ѫݝOZLA\'|o|-zk6WDM6L`%Kdee!=,C͛رZ~ƍzkFvoE˫:00e\rSPX!R???., ջw;ׯ\.* TS:;vO?V]dRF}˗m=̬f?O?Yhd655OO"fkqQqኊhjHrJ )SrijjrssGYzA,;Y2")))qLl͛׮[kިL64g`o4fxYLڼ%}Jˎ?~ߒ)SP_ryeyxz"Qep@ ϜIFFFvڴ$JWKOK6?wwwJŐ5[T*ڥ룒ӧOOإsgTJ{Ngg灃@œ'otmIEHM%'zWW&̳\<҆!,Q[[kQL&khh03 `͚իW:u?;sg\]]ͭ;w4hϋX'"BB^zuر{[n =slJJ2>d2ECUdWTڤTzzxTUUz̰DCؤM6.]<\.J]t%CeݻSN5ZX@d^<WW׆LVWWˌX:@0}5(( ())ywݻ3g0@ 0 "%# \\kk܀`2^!2Icdi{mn,ңG֭]<+@ zHf'/z\Pԓ._"<<ð(s6nNݽT:tذ-[< !pss8p_ :T"2MKD.777/L&SS,ɻwK>}:vԣg_z)D:t|}ǎݷZ 4x`O$ɐCZ@[ِ}uhc(|ʋɠ;$MOھ};jl"H9YIRãx<00pڵ#N8ǟJe*Z2OoB!]]]mkZ3@ d...7*<<]\\_uuT<~[R5ש><5uV=r;וּ{UCj#1 ;}%Jsg*#FܹsgSSә3g_Q!Ch4 ̡sٲgΜq<00ro޽;11sgjllL[jD"""@xxXZHs۶m;yNWر㣏>jU'MVL2d0fv={:xPT޳NJn?w>j?u={d#vܩh:z*2aTdTڞ |l=iQQ;S`]Yc֬Y>k,d) ӧρ*0 zni׮K$AƘ;uvss~:PmM(n}z*Ѽf kWZھ}۰ag Bׯ]H%AA]]tvwwv}>Rں7PH-))SwTmm6lXs@ۮ]vYmݺE& __^G9|ԝ;jնm >Ktۺ_BBm&Nh 0q֭[yZg'B Dng߾U*0 #SSSUjmۆ k.sLL̮+55&&vႅN2pGLR172d殊 %9e޽)))흙RM(-\tiСC LTmKykMmkjk$9rСo6קL#_2^o zyAR{nc%KNO7x`@ X֗Loj[%?ZëW1?p@R~ brJ~]~~RԜAߖT ef)ܸ1v<`KZp'gϞӽGw'''Ե|̜5˶Lߘ_+sCHRݙnᢅ%ӧزu˚5k̋ZK ߹{g,^}_~e„}ի#:w

?}{9'۷!Xhg~6c{HRCK;6=)GNNN8"{</^rv֤rs/YVdPiQE/^駟%%%C*@իVM<Zt۷'Nͥd.@`^|+_%%%}e.B_z{IWgɒoߞ0~BܥK HkFw !~РA;vӧcJǽ+;؊’a0b z1=L   + θ ,վb f(%r(>IsAχ ھc;3zۊmM:E.le 5K,S픣C*S'߰~CMMM=֮['1 a 2&[ch(Q,43, ʲIR,9_=1EJxhy`s * 'ZFf&iPZ*duNԕQϞÛL0#985)dۊTuINl kKⶱցe LǬT6|ҷ.ՅŬ]?{oZd徭z`?5ض0:!$h# /&]2W_kh3UB]aec,%`?I*Gjv˺-8]mEUkby76]r:бmuC^}kw2ͅ|YL-bIjD8!Li daV=3v2ZCp(Ԧm7SŁqP}(i%m#&ZP ~.BPR:;;wiN<}`WaA^ɣ/^O4_!ωp׈W\"WVJemm݈}|#MC(Zxp'"IۇU٢YaqŌq }O\xh-}ؓK/US.:^K.=ۓQ爐+/'zREŏNbg0̸kϜes"iFCOS-ZMuund\@ V[RRRѱcXYYUaq&u1vdiM A]7?o1JH^ Xm>(Hy^UzNm*.ڇr\Uդ\}&Kd=l˴ѣG55 L&U"FF&aysE,qi;K\\(Wʞ7۝;wC>l'P@@3Ǐ1 Y2,S$+Z˱gm |r'D[ZRr13#.a= @@3.vG+r 1`@ŠZ[QU*K绸_y%g߾ryopZ#E"H `0j04'BR[E!+6: A}=TVBY_:KG/})1D}11)ae(H<j`N"@ )D& k"H$ H$@Rt8}kI} UxBQ(u  x@]TALgEii r"Dv hj5[ |y:V Z,\ccSQqqMmmuu5x{yu rww-G![7R?ohW\)(Jl㔋>oooo](d$JjQT>E U' gl@ֹ v)6>=Y8H @ ZfSUGlUuu:w NƖZ.qBzIPiYYΝ/kޠ֨;t%(O~Fb0#6cΝ:?(,|t i+5E.Rܳ2v57u k\:_Ъo~~A޽{2gP.P_8p˵˗%x LaC]l~^䚑yA$v:|FcEe .zxx!;^CCô/selQq 6"sr?+W,JQRR}z4qbڂcXTٙͦE йsl zӧY|TNHH $H쎋052*Hi$h $h0=!bR?l0xExߺ@$- ӁF" K~ `\ ,t@"@)@2Hd$Wb񦈎|2ُ`&"o?(j9o4mmw)6!1[p}]-3 5۶@ ݫP6K 4*_ tZ@Ϲ{uR)H< nܼ5|о}z[}PXXN=JeT liK}u}%{yoT* v֝Cr<BSds>3ywmhk?fLISI  Ua;vjiu`#.5^e OR9A;OMv@Bz{xQD,Bt:(y tZ :Tf}j T;bԻw7 8Z@LdU~zmb^zFӷog''cL)yedu)kH^Ff%|mT{!uCzXhۡ#G'+wFxttׇYYY11i{XIْJq~Aa֚QKJJ>2-)ÇG/ xfێK/ܹ &۠aQTk2KHmgg8D: BBR"@4;i׍p!A,0DuvvM aUFsqA!!>>^xcBxLPc cqZPA^ 3Eʼpe @D3`5bs2eBP@hYؚk6qP[5E,;<4B(#Ga`>V4њTB'֑42W(+t8vI(d26fÿU&>}Z$&ĿF BNB;q2?%!!O"V[^^~SKJKbyR 0l /\"tgUB8~X,)LOO?{|1-;b}CC'`?nݩ;ӫ˅ їO=f,r~ȬTtͩztwgH :-a֩e+&@!ua@ V+Ɇ PpL«Cˆzr!\]]==<1 $N—|O]B"aq8%s+P}ߤA0X"@zdhi Nb;oַ]'Z#$qZj5!!@m~ ڽ{w\w/[ۮ}Ϟٮǎ 9~丸XłCBN:>88lY6JFk=0Acٸ!wyo1R=T*'_հCZfaހn Ο;Vi%10 ;AߒAk6λJr' C!hP! L?z;iBT]Z 7DnnO? !j޽-3kkbpq77hhJJS w4_)/l%52* o<ۍ /q$ZV$qP(,$@@ 0 W^>|8B|@,-|Bl|Lcbg͊K+L>~Tn(Pj?ɺs7'^HHH\\P(0 ??} 8? ?VXpwݟI fܻ۵kטFw/ϯvDC{I[ ^{CRMم10;9i@C]u$\SknRpB_T{~FT n "p}SctjD"H$#GwCU%yuuU^ϡA>phT + +nVdV)[\y*aUZg q@8"ӇvYFHt:Fiu{쯻|VPB9Tubuu |}UjF,++)+//++ollh4Fәsy+_+_H^d޽{͏1r| 奥woߖH$Q1.U\yy111YngJ]A08t>~\Ǥ"'IiPRkZA!?!&4ai˖-3f dMzgZOOoF9ud@x?HfONA ehHWcw+&=tk"{ⳛR(T)bXX( 6SO?yݎ]XO=<^%!=[~,ĐtY^vg͛vv޼yy׌ۨ9{4ELzAqq_P]lF:{yuq?=wns t? A*b+,=%``g"\Z @4"*)4YM ٲ@ Ђ6Jk4R 8. #O߲<GQ(;v2d)ߣG]SmC:WPkgLҸn>5UÞh^^\ãRi1JD޾GֲdWDr-.&c>oggd2Ϟ=nܼ%He,ǏSkժUuvvv h"yeUِ`EEr_C޼y֯\RwX޽{СClwJ [$,y#89?zH'N=|4׻6r;(`"jYjJ ^Vd}EQiYe qiW^=k%o͎ұ1\a8RBzD<.711;nVϥ/KSO8j-n/=b;#htD?5h q#L mD9p4ZmQa"$N>DR*Tٳ۸B"{nrfqWRLR999 By\(fkj(ok]>I٬Z:) `/t+/ an9/o@">\@JQj& 9EyVfq귩ϟ_r< H2pWQQ|u(T r9xzG__hvR斜띯v\ $0 @8F@* CGOαWڵ&ջvmԩ ][1kV3Za̦&31Ǣ5M-(* 0 KIN0̬q\fux>)J >qG5hD/c^I~թSٳ7oQ}W_}u-X}`0*axLLd3+Q)"GuIM5#Gdq+"?b2x055ԧx[Kӣ'+L&Ze^ ;pc@2F: ըU(Rr(* Eb{H"Ԫ-}k󴄶$sNJbupmAh1\0 'pJ51\69.̷IZ DͩR˓mԩ7g?L='tc(ʼBlM_laZ c0u׷0Pd`Nժ- ;\5hdNکmu٬ݖ.}w_ݻOg2>*D3d\ll=w $Yٹr83l.͵v!˕ֿ1|I7n mߡÏ?q4mt.ujf*~f5}\b,LVT8ry&Hdfeek!Q($&wcDy:aGZ?;8,(6@ XvNGBmQ<\~]+٬euP P htFy{9uACWޝ"2U`[_gᅬ \iӳ;9ZuSv}_lSsq eoobj5\]To}lllFFsuup8"fH$:LQ^FqجH&R_#(9cP\OW۷7z#`rD<.A2Օb("/R{zx"uq s>pk\:Z,eZƍÇ;Snؠtuj9Vl/]8m AmҡCJsHL%v Ң[llF}?>s\dd̙3۶m իz߯lmeR6Km\r}zU]n<4z!t$MeIa0;;=`z<ҥӧ)uqƞ=87% PJ%ԯP@IXG2Vb ,% T%ʥ" @ pdq,ZP 724\n73b2'"ݮ{n߾ q_ϙY3gG4P(A?NJJzչsbbbө?vrrJ3ZVk׮]z5==]*FEEqqquQ9t:D믿X ffej<ϐ̬rٹ'O|ќ9sZjU3$`\ (qLcQYVG1)T_5>@ޟlfAQA%+.[Zz:)bq=\C _a2*+Z@ " _21+ ߷oD/~Wō۬_/vJ3oDfq^]xKLId*JeX+rX,&R,$IbLjj ^.r^W{IqjZ6) ۷uu(8TyuRQʵq S*ViW+<|J}(+;G*SdH><\R=w?pΝ;_zGݻB|ON6kYF^q2, ڗ00(kU2yA2uV6`DٳL@С}kW`Dj BDVt$j;"GG I(**eBVdgezMJ{R@%Ȉl<9'W>w* hI#Xj[;XQ MQY@-ՂMMM̌(JDO<߿|||LL'AU!իԌLWWW77*r?NNNH$uurrRYO0AU/^2<|&pS!uq^2 * *V{+sXd @Z `ن Z}5r#Vry?]d |#-+HUhEo|޸E NX=IT$~.ö6sm_$NRO̖g_so9p-g);@=3LF,8@2lIC#@ZF ةwP%;[.vv+VQ6kA>!r/ ;|p5IJ gB5/c1[M:~Yb2}YddCnjYbأF6-iQZj3qPʖ!{Ar,׈#&S] @@DPD1 3 &XWmֶۚj"JRR V6KVV-1jY@+k%EɄ  04Zb|||~ ٔzp\c-H8wNVl& >HG> 41[2a:]~&N?GUt .u\t:A,ݽM6 4شix<IAdeeyzxT`~4MllÀ!Ixׯ7n܎;͛7l0ǭ'\)-=COfs8@NϡK> ȀX\!k;~'<}jn4-N8!0;;g^Ud|Ƈ7[Z^Kd ]\ $fQzhErVz,A 3VJ٬>0u{QR5:96kJ٬鳮]?K|vg*GJ%&g@ձ+YRS1C:tӧOdddNV,**:uΝ;w=yR@oZ""" y:(`ae}zX0-*qdn@ ~ oN$y+߁۶mY:Z[ / !/23!;- fL&l 6A! @SeQnpڊ2(gҤzԯߒ]q˝ݠN@ bl(a,CVغu#G6lؠWULQU!Rk,WWWtPlٲN:YF.'Hj2$HDXXBؼy355VYBCC@,X"""8;(jZ6- jެ2 X, .\ثW/wy򥞠ӘQl-FF,**rqS9]+Xukdc R TRU8d2-xȒћbw$nK`Fqt ؔJa1fma@yAQa@2vuxG0 *aEy~vDo4*EpXHGScJYJ%U: UV…֭Hܪfjڽ5 .Ν;WٰaCPP.3!|<ͮͺذK!Yy.|+@V=sW`gj ǟ~7``n"##%3iӦڵǟ~^pu~V>lԯEv.vyJ R8 2xgs ѡ r\]۩YPEG(o^+‘PJLB!Dnn@QeYmR;cX ^IHL_eezKJFf='9 d=W+xE޶mٳ7md+=p cǎsΥVK%>YprAj"3}&,[bEfW]?qD7md8tرCREFFgL(;v,U!BO<:(F ܾ'c#dX ׯD;vXM4ӧOS&w0ވ]vq8MWE"2^l l R;рBcB.suu%3٘9 ' H pQ$xQ$TN@NjZJ|ssrZBH$8Q Sp O,3J5]6K628'KHHk~~Bv3NM}b3 @QjU [Y'A٬]:ttIiIrMggI_|THpY;M(#?qظ˗/׷/_۸i'BHMOYOO9o]+%1ubB oz:jfk~~gwNKM3zaC'NlШ-[Q{#=H=\i<. yn>`(& Rw*0L,AzzktFާO.L.((X?4Zp =D$*O y$ :]h'xI^جLuRvG.H*h dl&#X)b@gf^STzy1y,''3gujh:+Už3qfjhX?x8UjuAPP",6jcԻWvvÇRBtxhO: A{9998 VZP(NAYOݺt.ʆuPz +9tw,??ѣeqx>sppl zנL,1vƙz}Sh*t,**"I2)MAAWݺ|7V*$ XxS,3PUL8=qNKB!H$Zr*JLpb1Y*iĿ ۨt ( A$Ics[q)ǫaP;\6kU" :t8ubprrH>4wչS/- 0);'gT+>{^v}{wO;wZ |}h=0nTGhҤ6VQ*ǎб[nw~ХpOO?S?1|6ZDYt H~!C'e U8Ǒ!8-P(0aG["NQ4a09G5DbZ-`|u:6VچbOQ{78".yxKA(x ( 4pZc2Q3 J3Txgh`18L`T*TAƢ쬌iS)))FoߞԬiUnuhE]]KW >_FPⳣlVDޠWzel֯5 Vjc1J]ǹ6/ `!+$ ($BPĆETY p\]1LN_al6["J /TJV+/*R*Ż Yi5l ~ |b 蛯"[e 4fH^XP`/>F,̌6mX`_f(iS#rsrӶor(y\ N1#7'U˖wT>mڴ+;tryQQQrr+0fт.Z0[T'w]~jlo x6(KIh+r p`"'9tjQa{R 2;MT2Qd2q%A0La>fE]T>> H8I:bGG@h(rK(t[ )qĂX Ip@K   HpP_ejI(٢L6֦V bd2[ pkT%u~U x @VȑA p(dH,qj WZLf^=۷m[()4,S&x[9.!ҥsF>z85----<<ڷoUɩ'خMk?_/_fdeeddKV͛5k(6Cqةc@?~bHCZ@в;4`qn2^۳Uް\q\Px MLLRk5iٹԩSX@TAg9R2!( AV~uzZKT*=;h8&*r~_.pscҮ 7ڨF0<=2+ 0OMKmӦmUҨj_ZD t5x;xԔ$Ϻ*Ӕl9j]81D)+CUKK2 Deui}pv~U/M6mfN5Yn0 8Ue1L(1 l&X=6Mf ApOyvժіC^UHw{2$zJ'.&IRV{yy5?ȌhM i4-+>VC1 fU\d:kd\֖GQӵ&X)\N "Hp"I@hժqh{es8hI |AjJbz&)AVle҂>Ѩ55-6E4O j}@ԔO9ޅ 4hРAF 0[^r 4hРA 5[{{A 4hРa`$H(,q<3#͝Qf4hРA 4hT#cdef߻{gPH( u:c E,´њC 4D)ͽgPoA~.- :dpm7F*Q0O\h͡AOl5  0h$3_1/^`dlqq$I2Yl?G$ 6t:-H i-ͩfe{" J|d6h4aiph4^1/99: H/c^6iCgM5Ĵقt}Mh=Olc>n8^XXj2a<^,~+[W>ΖKP07[lIk-hNM7(6iРVЙ>JPA,H|"P$+;*+|}i-ͩ)V4vc4h|(P(\>>$AN1VZ5aCL v|ZlDsj8Xs{" ,,e޽|~L,( Þ>}*Jbq-^tɐZ޽YYYϞ=3Z2E^^ݻ?~,J\nƍ'LW.(?w\wQ͠ n^<&TZDZhFw- <m۶-ױ-[tǏO0GJ-ZRV-C}[[W4BA4hxo@ 3 <|ݺF~-[6={Ov蘔T%hܸq:u/Hr+Wؼyӧ׽{{>x0޽C.Y|7HIMIKK7*LMMMKM3b=֛7CB7'O.wֵnރZ[ #uy0:f>&O|ƍwe2 8l6j%EFnsqqY{#-==z%&ƏӬqc}p jcOY?w]k6."''w #yĥKSǖ-[B\|W!!C/JQw;bGܾ}*)((2%BRx~8YYY_|xРӦMꕅ:Ҡl5pqvڕb9;;Ϙ1}:jay`p &? "bӧn/XۃXg"xێTG\>AK4MxTjVCLYQu}3fbM?0orttj_s _~m8͝;/'''((x߾ ʾN>#..N+QaaFyru܋m(=kЫW12,lXTT1l05q޽v4+4[rm۶o_beqʕ.P=Ho^͛7?Zh~02re˾TǍU& ?K֭9q|۷oܽ{WF Ew6P\HTRGJr+ͶWݺ|\=w{իWSIЗ3 R͛}{̚5s}T֭&Mx_Çmܸ Hj͛7WϞlMNXNNΝ;S%?|9^:~k֐$d2T*W`Z^paAaIy ,js/ $Wc۶HoÇ6jp}eVF=a$7x V/?$vKqttܵg//Fa I]@/]֗:ucHdVCLYaE_ҵKYC#G8FA۶m 5"RSC*:tY?mp\q@;w9|И17lH]h79cƞ={շ+/ޯ@ rņ4ʠlRToݳ{Y[nKIN޷wϬY^Vc:[ϓǏ/YjT/B3[ݦm[YpMLڷ_r,~ƞPȅQ;wv:wtn4UXPPФqF I>zP.Wtҥ]xH?~iѳGСC8lv`n>H@{[ H((q<-5qjz`֯__ {`͛J܄OxA>{.#AFQ(Yua}! [,eeUsq捛_,^2|M7d2p :uwZپ];գG,l<obckۮ]vwyϘ9C:ubXAA%u!CB5D~tT%YYY?S=_ߊ?~}AY@Ȑ!L&ðԹ4[ñOzz>5kU !CB>yZ>q!!!ei0+mD9NԡoP///ww$˗/;::zxy?v(s:mz~~ݧFΝ:2LJ :u0GZZ+ئFY8R=hĈF 2-cҔ)V lgݺݻ{N]//VV^SCGr[]&:k׭2Kή ˗Ϟ=suutw5sFՀ V*eLɜ~UԇvG5PYG4(0 w{  y._]ʕ+:<{\''iӦ7o d2H,&"T*58A,ܼ<{{{i=}^LMw+W֬]wCH$"L]vjC>) JҴ.daX~7.5>txժU;vnܸ1)9IրMN/]~ݺ}i۶`:ېUVP*#FY, kP?oCGz7Bl ,Ӛڵ}||nwSNoUٙ,CCfYM>33nݺzu;!Ir+WݻK.Sͱϕ'r\ D$`Xox%"YZ2S*kf,4_._>st?m떾=lA ׮ ۵kԠA~mm+V| Z$Vɓ'YAy>rk._ ȬY3;W+0aš5kk6 C}Ŋo):fHdp'ǧ /q7n>|XYU&YXXhTؚ7o.IιzX,d20#.#xjOs[.[E"(`퉋/NF^t:6ML:@9)ۿqㆉ'-_ A~&>|}"""WIII|ٳ}}}-Tב }&T*P((a>jR#GW̼8::޽{WR1͛w ]\\[}zϞ=ٳg .1c5F4*c=222-ڵ;z\qԩ*Gnݺo[Ө)YPo,K򛯖7l6;7'0,}{bBB~^7T INN-[lb 7nܨQCfP}iUog͚ٮ][}|:ҠVȸ}W,Y|ٱcLJ :uZjjʬY> 6^/3;~<8䧟~;w_9H%Hnݺ%ɼ*ZjQ2˗-ue5wU+#,X0͛g 8hi b`)S&/[l9͚503JWwرQQQ'O*(رc;ueȑYYYWi֬YsΝ0ab˖-2v8UR!룔ՔU2aBx||ˆ#;>f̘)KwڥRN;V~.^RLXPPyyy'NԨTgB/^VCkHidnnn۶m6lx^׮]a̘F*wޢEݻ;ͩ U_}c`Ҥii#Gg#9O^mS*ݳLCB 3#wڕ]}ʢhj6l8}J:tpVŖzǎ7nT*7o `x͛7?ԩS55*q9r*f]{پ}{BPu)yRbhDnN@ms3O5l@ﮨF9EMB¢ b^Ο;{V˖5yϟoٶG^u9z~~č7?믿Z!jNM./9}:tظy]Өʍ=V}֑ 7K q1/E8gfd4lOo׾#0]b#`9*;~ޑګO0\Ί{]:fŊch._xLL̚5k.]bhWjZ5FۻӨwgBPrAFV԰lEv\66S˃ޏ+RY%|p0a0qd7%sР}~ BM-z?c4$74>q{[hr<>!$_ސCW6FzzGlĸqÇ۴1 ׯb223M |DfF;r(AD"MKO̖dƞX$rqio/~G1JQkjv!(jmR(r2rs<> EVsjٲFrHƧjz\r E,r(֩]G >'λ6ɀAkчҜoQm'Ҡ)۪T|| rsljhc/cw-[/ D4jњC z[ T(+ͱh=DiZD 4hi3Hp/g`-@ 4hРA6@٫n={ҡA 4hРaS(e2@`o4hРA 4ho3^ѠA 4hШV4hРA 5LZ4l6B@<-4hРIaзf+Kև-ҍ@Z24hРAH28U{@˟FEuۛ 4ho߾aCKVWW}}smBI8MH$ 5/ZaTj+WA0dN2ƍ9Hfq 4hРQQ%q"33gRi̫-Z[a8$gO6i,k+UM/5maL"xyfbqkE 4>iU NLNjϯ>Ϥ+ &۲E"4YқZ}&S*,&31!˭^71 4hf+mH2G( 8~Ν ð\(y{ygtG6#"k2NNLuhРA' pHKKqB?#i<;I$իVM?.l_B!/`j+d|;m'5ۏU7hРAʘdJ"lYػg?ϟ߻׼Po-eknP0 4hРAS @^Fխg}3=lOiX2bXX ~U(ƏӬqc1{Awή_u6th͛7CC&NL_tС/?J?Ǐ/YjT2dȔSy<+/ޯ@ rŔ ʽ2~'@k^*={y<_|SRR `0BaϞ=g̘n$􈈩aaî\2xpI:nĈQQQF9r ɓg 1Ç0ܾ}oFεZ#<8xp֯P˗ZlU\ii?!0[MrߤA 4>9;aÆ&V߼Iܽ{̙"#޽k̙ CV'&&o={ܳ8oϞ 6ߛ7o J?)SL f֭[[Sii斚bz ʽ2ñOzza>5kYP׮]OII1+k>oWbu: L&ST%&&޽kƌT+@d'=z$,,t/?S7nY,KRya_&ȭ[GS\9ijûl[hРA jsW 22\(jdfzm[y\n,Dp:v nݺÇQ}u#IqzUɓ͛6Ϟ=H+V-GI_rŮ{5 RTܻ,úBYϙ=nc v2vXPh(/Ιm4̙3T`j P[aJ9El HҴ+$;'==V[-R8Bnl[MzsW4hР`Ŋ@/Kf@VFƃz [JTFKX,}H$& ]d2 ~?@Ybd2.H$ɪb^4!|u4J\Frjgvf/(.HNJB #O^rr2 u뚱A@̌zuݺ֭]cxB lXo}+<}l=iijBuoUV[[6O<]hsE A\t_}nI%a04h|Xx{{HIӟ3ōɶll*fTf/(.X嗯_Wɽo ִiӽ%(W<~Q9S("0//2K?yҤP 0dJ$*>ׯ߸~xLp_6DGG;99>VM4hРAS %8Amڴ9vFpˬ͝ sT*?g4iܘ*oԩjZ]CC{iXثw3gˋΜWަwܻ,c*jʤ7yDZ=Bכ>"Hp/,,Ç}||hBlٲvڵlrԩLh|X$&&hѢyǏ٪5#V^>~\|B¨ѣO{Q?ënqï^:qd|/_3&>>><<\e~!A%ԟ'7m@x{zP::m{?}$b4C ԉ ʺJ)))#"$ɴ)lТe9͝&1qYCL1SP.?ϸE_pq)͚6LyI˿zyM5߯Y`|q`q6m0q/3]VV[d;w-5nSNM?ŋA5jԨW^nݲM&nܸ1 5BIlZnԨ݋/h:o߾C\=D]E7"5~4Hj?a¸`Fv (rCkr_z͝wF ЦRڂUֱaܖˌ)9s&44kNTښrRL^pa CթS6;w;f/~Z,?{wG;R=v@ػ1cK4jkL3$ĮEFPD@p{w ,3ofggg#G@UkI[uHBzeF!/ݹeʓ*2^K$׳۽GdRVK'ennׯ ⋩U탱)+WʹeLxl'Gqu-ǾQD -[6iҤ۶m}ɓw9s_m됶YBpp0DEE-X'u|jՊ[7 "J:f ,*(HKI*).ťP(lѲ%p/).JOM.*(J=Hۿe6mD[dIլΝ;uJ87s nݚ>}P(\fMeV[tHO>_իWm}"*Zn߲\[]ߨFwѢE{1 0`ɽz:p@hhhլdjj9sh4+WVYfuT>{!lF6lhFĞ?qBY7007Bwmw:ro{LIY&b2 "\~׷U޽{sIKK \lY&MT`AJƎl!n%im[J0Cݢ2- v` ` ` P$ۺb` V O\4ij]x@ Tm$cwJXwI2ʲ,iOŗetX1E#yȌ3V (eTH}ϊJ +2":M-HL߈Fwa0 $a! "%Fjt] @0+ʪC)"WV ׷̥FakĈhTV]T^9  +_'9zꁕ!>F.o;z1}k$[ a"Ҩmx05gנψ!Rbą@ߪ aB# !+;aD ~>$Zgz!q11Y陁3CS0Fa&,0f>tOGzP;46m0ӑQLѶ9ª3BcsC8 1EtDi<7?-&JEI {*I:iReJkynL7!ٛ) & `iw2qFdQzᙡ*dH~OA:ԁD!ƣ4*GX}fƘFgm ӟ@1$m'HƳ54/s(̊ Dl-J;b,s*Ns-FjsIb$!0rjB'@I~O :3g:F.4~*=3LlOMz_M`pAN?J(D0EBL" ċYL2+ sڔmx3+W}|qgJ1U1}^AzLS82m{!C.{XHپQ3V@픅+~HD9e/J<Ӓ]&8T*UjBTUvvou5i6Nr#0+3Ai iy1; 1  Ӓ+dV"DrelH4jbN;Ӵ"(6TZIjZXiTp*; W&xeUYVf޲YL988* ZmooggTW@+u_0ojugP xM<lZ<`a&3A9fWOVqE[\7T" @}!s*jT 2f W9oWE+2WYrxm. a?1"Wq2YR:XpJX`ӷȬTcU+r:+"-]z>R k{RgLΤpLfÐ@'ǝ\ݭ B:mkeVV$#yEbWEPk$~_=8{0zR|ރU*yy 6ىT* wۣ8//ɭY)\Mt•3]cfU( 2y3+FlPWE.\$ܰe#CnвmFիP*h|Dv!=Mx'8V )\X2}[ōU'B9r WZlK+ծ0V "V^&R)Pjl)@yz1+ ŦBWm\dY>F#ąq({B3lGˬZ}$òF3bW`UT,֠A%UB"VURΌB핁艂ͬkB_5m Ja6: nwrױ;>xwJ0WPBE )2JeJE"X\\\̩_U4s qwO֙TXV߲F[ !ZuE bmWM72Z1#F'NCN矆 (Ys2^ @DXXDX1ePLtTF 7o Ҕ]?In׶ bj3B 8EQ)-##4a!5%ut>|QD8"Ag3O3S ̊h]ef5{hk:`bSaBBB~;ܹ \R~S mHśsq9 ށ8x=^1Mz8t6;Jp&&&-A?MLޣ'\?KP8V˭_?L(ŵ8S)ZwrJP(4`X(\4)-Srѹ2hUsSq(kee~w+{(K 4V4$]((Ͻ{iVNd)((}IbqH˯%krZ:IGGlپMx$)_ڸqÌ7;Pt]2d"d̙cǎ]hь3~]֍~]R2 [2Oǎ۸qƯ09R8w3x#?F8{>;|j5lupOR"Չ qqr)].>UhgWR%qu;>G&BO<owySܤ]?Zi>{!8(ȀH2~ǜxyyegg̘5 jun߁3t%}׷oFӧCǎBߺm;kg͘F# wR޽/D,nVXMjWό8ʘj7T,S:ZnMjjUⲨJuΝ3zܹ?FYteqQ,F O:a-۶׫W/daCcUJҢEFuKS˵.P@Iݜ+_NN->wxT,)xDmssJ\95~.Ņ&8S)jZV1cƽzST*ᠺf;RY&նbc$Ud1} 6@qm ʌ11Ede%Ӭ5G}ў*ӠRy#Fd~513-Λ$a ”b &ObW\b_9)S#iUK,2dhtL, yVU_ A*vUÇ-GL.tI]!;R*SLӧqq&IyСQ& CGyxHB>7n(-)1sVclYVw:NW3gΟ;wI[nlJmhce">zoŗPL(G*+F)#P!ffyrHn*UnJsfCMG}7#?eGn$ njDѶnZ^r6{B  Vӧ!/⏽ySҤ]?:lnBD8.^(.$~_CỤL[VҦj1X8Ah16up>jYڴGF"f|dx"{\T(TJe~h4VCdajպvJz7MVcg͘No`Oa]v]xˬ**#3h;sTUEҿ9ϿcVZݺuk@85AgALJMKNK'X69-nh ܅> TF@GS&Y:%+O+Z T@AZyZ6{<{Ŋ Sm‚|.e*LOIx_f>ONL+uV{[!+yw;vd>[.BGL(:8,?g'G99{ð>#tt?癍ܱK p/9a}+).O_Dd_5nѸaD㈈D HΟs;Ռ6uuPH7{Μir"kjX4lĈG믙hr֯f̜`XofKC6u~iqmXxVm[ZBqZv=6|'GJqz^ƽDҒҒҒb(-)F|PWAtVnh]T#0vVJ~ePVaՙ<.g‚W.8Ye*i٦MqA~LJ:;+;+}lB*J$ ÄBgÜŎťoJ5gnƿߥuDXO~r !ZׯDo KjJZ525a6``@;2! @b/x ? 0WtLŵZ'P "G&r=k !BHQkjd [wP.RZO~ &T(ty@]bv0 qI FXQS6INKWe'Yf}TVRvvs͟`aYAJ%F4lܨ ŒܔirYKKJLjKY齗K6V&Xq5 NYB.Y4Ye*4h!oޔ=|>5[ ;QPh[]%::gvY6_@Ie0WQJT8עIU@MT)9_~~Fuʠ!D£G5k֌<:tخ|_㌋,iݪOFd۷o>u:E<~ǟ~5*]F拴gϤRixxOM hT+Xv7 x蔞5]֮];"ݻe}GT6t(!kqiTAR}<?`}a\܆ jx{?YfPTTqqq~~~S&O 5l@͊֩3`@m?=N#/v6]<[1}../_~hD"W7Zjyxx^H,$WZ]Tp]<+^-P.bVW –6匰)Mk424nBj&VW2 yUR峘UB Xe`H{@4HtiċVQM\JJd7}'/uhη~ח@d[~8(+_w+TUjY- !={F˲,0hFU,kR-2C8oq}˴=[FN|z= 0@֪Ab+QU!jӧO#kFuV' 0UUsWJ$ڵkg4qK^a&PvJ[Daa!ajXTi0Fӽ{=u5߹sVk~]ضmmfgg/\GD[J!!,({{{{{{zv5f,x`3 -cᕏUm x(cU4kFP,g5lxJԖqz_5mo ʅS% UU01`-X+VZXb@VE)Ԑٯs3>.,J# ի׹s'<{{/}>hʕ;vؾcOd0S:uW.,^D[G^^mmYTA"όL%ɋxiI ߆V(^8eU?p֡2&7@s~N!!c6 NfNmi>>ў^P1&@g$`NxbC1F:ŠV"!Yarh_i!- 6鷄 <∼++"[L'ɍD+ǐ:L&.tɒ%OVTmڴo}}} ,XpV۷o߹sRf؊+K`dLiUWٶw1ԝy#pSzO>9sCBugge|oԨظQ#)>>ߛMVʕ3.2NQO?֭[a6` "-?KJ5wΜҒdڵ?*|h`\JG:V 0E{Jzh?-b(i`LӆJ,o@Ghl-/BƝ5Ȳi&fIYFЄz.8=LBzj R*؂bU0>"`}טKww1B.oݺuXjUAAyƎK沶aΠKrU:^-7{&vvvS&OZbҥK&裟~^UQ.g3w^ƍuZTT"7o^v'O9׳j PF9|䈻{VVo-?lڽGgH 'OZt˖1Bˁ]F;ٛTx&bc&l-Y cNz`~^3!=F@cFؙW{sx¸J|LՎ\˒/ӛEqAU0!0ajk ƚ3e ŒA2"us1j+Hsy۩Vzp ;;={xvx"tUR͛7ȑ#,Z(pM~{*lwvo_LBё۵kw?:`qIn2i򔒒gg֭[T"f͜eի-7|gLzɓ'a6yԿd?תUkI,-[PTK~xB \ᕤdįԇq!]zD,"%=njWC}'W0ҭRl`iF*HD ~̘.@*1`ZaC&UHE -y4xdBH DUjI"Q_Il$6f߼w/:u8ػw/ M!MὰM w{'N[V{9SWe敇2'/ƚDJ?阩`Ic }>Ckt?XEj~eJ+*_ 5Ɖ|~LY2&*C +ߕX"lcƧȞN>=qDJ~QF]paӧ B\K sEUsÆT׶9s[͊Wۙ+՚ :,CnrF`}cE9Vm60v4ZW0+JG+ ]΢F'&Q/$βOo3k`n5P# T4˚@Nkq ֯!,\pK8̭(wWA +%:thZv~gݻT*uޗ7z)ʴ`&[ r.li@ WsjXٮ?+0\6xh4j%&,΂TfNC2"YOs7\9RE+RŊՊU`_W6`nݺES׬YC0+H~ҥnիF:cf],lQ<7w|ӡC7oؾ}ڵg6Zer_eoݲ3Ѝ7fRiL-lWc·"Ep·x GVgekrj*TbFL!@N "HPP$;;gF?\La:c>ӷOׯEGEB&NzutTa^h cJ>O׷_}Ϟ9K-,(=z;.S*EGE~_kꔩٯ^2?ӧNӷogϜ!d^pqݺ}2 GBD2l谤$Uwԩ,;w82?>|xN8ѭk7P S'O|~ӧ!хs=jGJ~5&..Hpј'NPRR5ճN߫wNe^eeBj{n?ֳ{'O@ 8 ڴnpp 5::t@֠DpϰyqjS3Lrl*p}KYrFK57Sb`1-O Ɍ.tqi`t Ԍ&z)Me82ȫq 坅S<[,Dg);VРqS[H5 X{Ӧ;v߿M9믫cΞ=SW d2YZZm[o߾a(AAsι{/uLUڰac-ڳgφ̙CLMM۾}۷ׯ +VXK.]v!e/Wۻ&LukEu8p T*ySySwq֭u~˗!!/^\뚍6*FXyy{{7l굫J/~ǏGDDܸ~aÆQKHXZZgϞ7o]k׮ۺ:u4x={4Rk׮ vرn ߸q#**իrkQQׯ_D$EhJJW_}U+AcY&<"ܾ}5k.\(JKKSSR7nb]|mo\/o Z*:&fȐ!֮UT$[nKgmY{27;vO`,t|-5m*JmL!u 0Dj=}0'[0 #ޓB>N,?6o=jd4$!#ǏO6],s-֭P0L 1Ϛ5K$~or?7);Q @QQ/]%ŋQQe^|9::2{xzsΖ-[b>߸8`2Pu_e ήnz37NFEetakJj?'O 0wu^ru摏't<#3.QN6i/+#54o6l=j便6m::8T\jm.:5]D:W%%%\ 2BB2[dΝ;&LkҤ7btz9&z9Oq蓚pQf ڹp'۷T*2er&MrΝ;[N.]|yر &:O83gD yqJJJJ\\] 7w7I%ͽtyMr6n7v\.y攩ST*ɩUqg.**rH77BT%ʲp@F,JJJ5}ޞ)B<|4!"3]{ej-T3ϩf*cjwV9? +b38w€}Ό5RVoܨn#mcs^hhX2L8j׮^/4$5-mgB(}8WfᛑofWj᜝K$JЭD"%yF%H$"ݿ30B||}M>mڴ3g.]t6OaFD:0RW3fL 9}f%5E4+vvls׮MŹq;wl&v ,BٹX"QD")(,((,H$D" ٲyKXXhh͛CJ$IagϜ>bqJ$DkDZ?r!@blf }?F^^^/^YH$VR͛Gٳ?jzϞ=Fsm~}S"##?0t落{EF6c!I&߿}v|Zމie^k޼]B!1֫Wѣ}_mzlG&L|:tS2=vmEK[;ӣgs~[Dq0Z iР#2c)h}1b͛ QQW4yԱSukM2xN>l]ûK.2cO }2e+7WWOZ- kxyݺsC"qssZ58 $KH\$!!vBL t"bF~CƌsCHdo=p ġBiG:_~>|DpHX,H>;nlFffm6}t*R~:GHJNt ?͛{?L6lI0"ַď??/:tXeИc=6lذFCyvk1ѣ=/[lW L˳V+KުU:u4mL'!yuhQkBLϺrʎƍ֫gooE4fظ> 11qqDtIնo!СVةࠅ ޽'[#={6mzløħ>Od)U-(;?#0I&D:_WZ.7^#~L17CjX%0\ Yho+ý?SIw!,';UtwiT)EN"T rT[?s`Pa)~s͛7{M6ݽkw /FWX2J3ڏ\ bceĸu7GϞ;vl0*Xƥ"1VhZ93@C ~0P4NU-YgC ZƺٰOh|_~}X Zà*݀ @ 9%Az .JqRlښ58fDPPrN;E뢟v?\%al+r8S>Y1!,ND Rzz꼼ЙfrP(Z k2lKlD Dݻ+sssիl&Y VeBpE&jrZdnjzs)ӚjC*LvxxHL`+Vǹ3ir,vD:i[-!T @P8h *5(W@RlcGN[?|ܲXZ;(W/h=y>UMu ɷ3f+b[jmv>샌[Y3Yg/^T&ɛ^UMmv%+M튘CfƬHhfCR>DkEFjjhw"%%ŋ.͛a+C1@ ҍj%2+D(~D2*;)JG!]t`'g>KKJo3Y0;/!N&|38IҠ#Wu_lC[Xt˻9'dM:|Ų641ɼ lT\˿+ @Vn,CWsQռͦ*ymʆU[9dFF/ŋL??lѷVlHDP(ʬ 0pR:2mk>@0@bYq})Y{O?Tu?Yz*? )Ua+bj r@OaSq#Z8\s(pAV > e[z+HʇQ#)Xʺ fw%smIKKU;|8%%KJ;鈨Ԋb4FYUt%(0@[c MZUrr Pu P SIQKZZTb{ lQ,T1Y8L3}C;"۩0shJWo&=Vo=65+Q0qdnZQQӧI]ғ;GEsӧl6Io$.`U\083@Ty-\+[s<ȹN+f+r"@norj)pp{4"Z~eOjWaSV}OmW^=x𠤸X!a˿..͚F&U,k- _L Z63:ԓr+[ *%nZƚGYYܿWl#·ȺCm;{g888t=;VzѭG#ddfDsV\.˫c+/ނHu.Te)*V¶uAe:js{(P/x{(7(Aʒ7k|ԅ~\;{ad3Li5HTlQEcb{H$ W }yU_g߹}[*cFojݺw*8kE+AcN$ϯ]ܵ{O//>`[܂@ DN 1aPPvv[7RO_ VGYA0<&`u蘓BF);`Jz[u)aۊ[谵RC-nzfJDR(>WdAT>S4եSh .:|o8yڭF-ݽ_%~%-VLl7yբծ~0,11K*utt4CBpttzWLkR9D0.KIWv= V@0@T|cDV.2#ɉOZ'>}] 6ݤ7yZ_-T*r^2پL[A,WMVNj dr9ӾR})VU# B(pRK jOvoؼgVߨ#3bk`Ĥ1b'K͌/9_^*vrC;O>]փP(H U6|+Zgzr2f@S,'S,KqTZRb;4_[qYYk"$ R/iqQV]ܓA^&P*L`x V{}C +,Gimc%YƍD@k$@J:6EKBT*dIK=<$RP~8JVҡSBj8 p6Khevݱ{]uٱ8qTAŋ[ׯp7jekq?(K2rje H,+QbA^8IBͫBIqrD"ԭ$B.GĠ{`0/ @ 35C@a H C07ptrtprP'GG݇V:B, ŇGB h C*{HAo))iY-sJ^)VEզW+Zړ4<$AXs+. wZgP"+**7;v7 ԩ&!YER)'u㄄n gggx)wkݪeDx^.8BP>-؊"oݾc,Z 䲜!'2L'05-F ^2b;w5|4{l#33k wr={Brr]LL E/^2++KgVߠ&njck=BQYvftk'$<)bX0ahzr-Tv2[ؼu[Eg.@M8$C^4niTʜtG-;O4 ;[`eȯqB. j2\Vrpqq!``ؚ%MS>J:)YdI\9fr,$Wz3BBBr ټy;G- rcsێO:ݭ[WېFc/_^_  ^99}ի1{xzzQ3**zA=bQNS,wԉ{uRNa\ ]vvpp 8^F>~۴Icvhq&$dnqL |Ӥqc..09%[*E`\'4i܈Gڽ{.QW^k^[< w"ߚJO<&=e !l# 0 K2@ll-Je2JWa6M)6jnr39w /9::899%BH?~|VxR@&}*^t"ڵC999988 tn:h  .tP똬aqqɭ.,{|| HÂK9ssb"+uh^({AQ{h4gt%{Xh`o("܁Rr\w}ٛyw߾3󎏳LKJnbze,Æ _n%tr%PP־ Ĕ3| qЁ)f?AڲQzkcAT+&vQ&#JKKO?J,N|3]0cmv-vh p)&T*_oV-[صgvmݾ]Z jr߹;lIIM  LIMqvq"EX[X%D N+:s<)T eRuB:ǷO^㚞[r c:Yښf1 #M2JvwoaX|.[PP` &p@p ipMJ4,S*# ,Llbs5dՠh*NƯ[tҥ#Gs;qS4VRJSQuqq)?´8.wgϞx R_VӞoD C=/..?~|Æ ϝ;'/_N^z &L0nܸ 6$<%$<#$X7o<O$G PUfXu{K_nl\5Vmtܞ: a:+_4iԸqpAmI_@׿/ԗg&oŔ*5y='+K7u w)((rAas!\W=!obY,\.'=z_V*<Ãd]=%f߻w̙3ؿkΝ{#Pd~OH-͛={,**ھ}{YYѣrh^`WU*UaaׯU*@QT#0+ :C!H֯_߱cǩS  9sQ}?в+Ar7b\^bSl6fT"rF6|->^4E[XҲRW7ěC[]\\bxŨÀ愌##$* ջ}SM(E;~Y67+$&ʉMJs98Jʡ\ .KvK[u^{QOz0pAXe/N  OBE9lPAjZF`3_Ϙ% g0//ǺY@ yw'XVVJՆmL< LsV+eAY/˧`P#*R\=ۘ#x<L&YfBg94m5Ԥj[VYXu(s}xTj0s7N!̜ynm$gX7%i ޮ 9Vm^m\3"4#GBBBzE@@Pn;ď[1/'Ozxx U:wW_8/H:u`p===<xllT*ݴi34/ʪ͛7|p k֬1c֯"I#ٙ`(J6MNlӲ9DQT( KFEEq,ʡ{Äg4ƍAJnS2NcFk7%FN뮬zS]49 Vc+ bŲ%/[ iVCᵐ pF$(+)Yd[ ǟmW˰lY:@0m4jQK! rI'(oaʡ+\ /ɪc~#Sz -$s@UTT:+WޤK7['xZY5|ԖT[rLg۲SAfͪ$=!w NִѲek8q"((hƍum۶ݶmL&۲e n٢ jj@.`0&MY3~x ê#0&& 6myn&7okn۶MnhX +Ǐ,}4͛53gΐ&Ota8^haɓލ?*7;;D9ber%5X$|Wa'5j'fN &vss+ĩӇ7n1p>?sC vss3GR&U_ {&DްM-5әr9SppÇv kߦMkMW~nRo'W!1*V^{ٓ E3WC-Kmjfnj|srܫݎ WYfAZ҄\I 0 PcfL <@&%r*Y-W)=Ex8"&Zswlt/͹GX\;/GTssENNY1õEV33V N(֎tM~C[qf}k¦+CMmd5U%(-ɲaº9Dl|MgkRO Wu(wh#S4oU*^V.y攃PI4իsjHXXݙJi~ Fl6mQ ryFh4Fj~݄&LÒj,eؓk][yZ״" =U˞j8I^wﮐ847G|5aҎO?mO[uN6=}hf͆-YfӠa j۷QAkLm9b5csJzoor3m0jYF*alZקusIz'0h~7+QAU5R_hw]dL&: )U<ɆsUԡj~Rb Vn`w.XMT4g% *B:z~[iРA_4hZ 4>H5*5s+ %Y_sgkB>h+gd:v0LVlѡhɇ=SdQda ҳgxAy_[t>ZT*:EjiOv~'PcjeFe W1j5 e4mٙ.Ԧ~'s\ T$ %e?yyy28+a Կ'uX,ͮ3A.:v3{[E9KJ6oP(p LaP(|=Ox℄A!B~TtբEӕ]G-gZL>V,[b0 V}h8M='"surrb2x% f;wt{?i+"y<\V2Vx"g*+{lii͚hhr6޺uk8ފ~,<,tׯ딗[ۀɹ0hx;w?~}F}رÇP}A۶mqצ}#\vǺ 3Zܥ_||UdEz%2䖘{}XdL4I{l HT-낂777ONv{^oQ?u|p|_k7eC[u=lCV2>sim\'Lkhnxqۈ;9ݛP0#飴ug xIQS~4{fU:}Ǎ Pإk7K|GG~MA8ޢ~,w;l(իYӋiZ%a&jF!k+P(kP̍XcGJDĚ-[#"3onGDٸɶmIR>];&eG))|rQtsI**7_}rhn&O[Gڀfݻs8pЈ#ڼJ}ŋo߾@$={"֥(WdsMJJ5_c0tի?NNN5s6.\ڶva֮>p۷o5~MʕQcQiP\\f6~>}i֏5jڵuTg~}%+ׯwGa ' K<՞M%J&`jW@ 2]}57@?д%p8z liivō?}yC@@Ъ}p?۴m&yYi|إ{CGH;-&R%+ vb.۴iɓ'66/^`Eޞ>}n׋-ԩD"9rΝ;/^\kIIIquu HII (R֥H?S.׭[{~a˖->SNӦMuvv.//xB``m|iG&%%pcǎqysoڸq=V++;^^U?@,}֓"ɡ.bdJBWP&eQ rrܿ?` KeQ#W,~Kw~,5tw+BN?dCDqq'֯oф[Z&WxowZVZ׿VFx2X,C,d2ff @ P^Pl2W=`3Aһ$ʠ(JCfZZ=݇:ua=+&<܉;wYsŢGYȱEiy9.{ӦgΜ޳gwֱR8|Ȅ ubׯ?gl g&ϕJ폎ׯO| **jܹdo;ʕ+#G1bիW>9s֠ASNwI bbN49== kӧOV?߿2WvﮎuuӦNԄdddL:o[jmj#I3`E^ӧ{ j^AD6%٫[JJEflz u=|}Xծ}AQQVc郿ETS?8;;5˗]N,..OgϞ\BʕFzZ=O>MR2$`2RRR2-Z˄,+88I&͚56r8,pvpC3'`P&UKO{:AC:X^@ڗX>l+`"#ΙC^ *jHKKnjL9ɔd9rb?ZGںu+sǎMؿU˖;Osd2.pʥRKtJAu<6ow&MBvMFDQLtRm ={→Wffd8 ghyyyjj];͛2y)Ǐ9"7A \.OMMݿHq1zhuSRRRSRz5f贴TK,˖.-,*$ %Jr*Ҙ̼k"UpVK`O? 'M`P J' <<=+, H$!MҳQ*"9 Ёtf;t`0^&2[ X8ުA)H<آEsK:߶:xpܸ6Ld0i{v;gΝ;kH:uZnJ%Z2ˈ +|>I(dI8H$2ACɢ*6ҥ@@U9uc-:3][ӳxÙ:1T.:yRBWO(Kw@ dй^:֬tqo#:X(: +4yxz=#>>>>>+vѤI短9|Ȏ;?hvBW!H{FDVПfˍuvv#",_6x̝Jz`0XbTRU3W>ܶu 6t肅/09V;Xpk׮_eVǻu6q'''F:]mk셞={~Ïh7hSOS,++{w~qF388po.^rŊ|6nlVl~6׉y&߹sWk[RX`,u .uHW7lCOWap%)9|f mM:yoܨpE۶m[p!H>~޾Tjթc.,O/N]}݇*:Ï?x?1 ]"}hui8wmBe_~Gϝ;Ofj <<܍:s3ygk<4B77YflJJJB@T\\J Ca(b8(aaWi֥"W(//5b5ҎyϟZ=zDbʕ֮+>X?~}l\7 Jop,mupp??=fADTvʕ׭۩J#wz0~k|E#k M@dh"Ӽv"Xjꁖ}U/ smH&~8"υ =IYY@@Qt페~Qo06I܌5d@ PuV]P]h\hTW^s[,X@A`m>geߢUACϞ:Μ9:߷/c$:u<}|TT}#HOO}f=zO}ڪU_ТEAΞ= owqqqӦML[ s.IױkO+nM=aqAHT*ZMYl6p8'Oi]^Ϛ/0\ߟիW۷xqiiCxxɓȵS_>999$$dennn仕cd* 5MJ")AEڃop`q&ops[)NQ|ܹ[wj{ ]چ_=sF[3gshrss߻K%s;8]ZgT*խxxS$n{ngR 07/Ws/*( |/xpqs۾e3Ep-4g];4xIT*w-v3tptжwn.TV5 9yysܝ?w6z8[ܓ ` *%H[3Hœ@ V2%V iа 0 l6u>aت!sO-dvQ*@chmzEpwwo֬g ][hXab- .w)*}/YP%"88AJLJ!c52Kv艓M`Ѐv"֜^1O4hc`0wnG u{ Qw F'ZKbU*lKd///OOOhOJxϋڪ𗤘...Cxxxh霐1sDbARqBAzsqvwg#&7,MnEKZz dY\XFKiР3ȷsAa?lզm#z(uqhtoua>Ay|GG (6kDaA>WqYM5w] y'wL&k.wT&\$!٣'OƎaXȞ?K S~;kf-x_AI,Mh-ΒA`^9CPݪiWx>9YW.],.) Y:9{4xIήna0J k8+rIaAYqI=WW1ϡvQTQTCS `5Z0w&V'zIF!c0 juZzz`ubQ;#{8:ڠ`8; rn8Pp9.]*Z,nŲ%/[ i4ChGixA, 믒Lq  ҠAOmճ2hP`@NX\7P8.H(.{4hРAL&y:f!!|BzjvmxOW>euý 4ha?a"燄4V"fqpp 4oj(S8p(4h-淕aΓ.\KoSFV>HAyOâAk Tw@%wEF-kB\77-? K놟4hݻG~`ѠQ[<<<#{iP:.gH2f6@Ď~@T!AAsً)2$99@Z/22Jp"((Y(ܾu 5 KvNv,qOle2Y(HIa@QC< bg...~ygH$ϞyaC: <>؁wpt3vܾu ҽS}'T&l140L]\\ 9c69J9+..05)*V|>LWY7G\u/ڹc? =v%EEU7H 4hРҹS)*Ao8A s}ڇ pP8F`8stֽ[111_5v8+p~}vWkGӧ}}C2Gmܷw%%%ǏkѴJK `6Y\8nuݹwM~uEc~ݸq 'hJ&:\zu-ډ|m."mk ˗.[VsknРAF>-b}0^4]ܷN}o@ //>Ug 9;w<@;ϟ?e˺u~ᣉ&rM;vN7(̦2{w._mNBm|||-YL?|^6l8yΝ;"">xVeSP8|Zm ~=}XϭA fT+ d#F%Z\Z4x8ݺy3v#Fݻ{ !aw@*U3W,w\nx9`aa ]rT*J;ڵkcƌ5jիW/^4fQF_v 0 6lׇ2ejFF)`ʕÆ _zE;⣇?\R'0f3gxt2 pt7}Q#Me?~HWJwx?:t+ 22rΜ:Zə1cfl+W CJ5jT\\ܘ1cGC٩3g)2-_';}ڴ?~SL» ݓ/xglݲYzgg粲2~e+ ! //oʤ_~6GMX$4P`6i裗/=֨2mZj͛7U9j }qp'-/w LgioMکSGdjBK"Ht«Y$ةӡ#GmRj޻^sKmҠAFʒ,!)9lcR aA4l ?`h?}mڴڹcۑ&OLîQ9xNv6fIJޮ]cǎ+.}$AΝ?/<{YӦd`6mN>#ˏ8ѦMk`0n+tqZk2y#YrM>\%#rI'$;ĉcǎ.^:f?qĄg cǍKNN4i&f&jjNS@x/Ysf߹}SXǏ͘5K[yb0TѶmo!33sd֌"QΏ6jݚ-\6g!1Cgϙ[.-_xnLx⥩ӦhܴiӦ 5o|s>[>?a"L8aML x>"9X;wZrkm>}:++1 nҤITTԍ7l.ѣG jҤIddKj);VC~_dɦM.^b Z+VܸqcͿh"Zh a]K3al͕@X G| -fg2جvaan*R_Px'>ԍ|RY6;?oʨ'VgNt9o Z\.dϝrS'WJڵ/O׬L %P(\:8vH$g 6|Zԁa9 zdϯGj~C sU߭48: BpnՓ/]VHt< TW\O,:;ѼV/];X,pxf>}nZPPhºy<8,2-jǰ\Q˵R6 l yӍ7_޽{m.???ӧϯB믿>cXr%ƍgϞ} ?>}X r9N'-!R (7(ӳg>kz3ŵD"IMNr;8yEQ}XCq/E%ID >,}}|WbIQjR߱Ƌ̠ebsKn؜P(˖-ԩرckH OI aÆ h :ujҥӧZ?9rdʕ ,hWUAM˧a_ _iHֵLx<)*((-.wj>Ǯ`ȸ Kꎶ+uv!˧M3lذ_^=&9lذ߯1b=~tfl+ܾ}{ҥ(/5YiSYrKF\jxwOf TnTvI~~~6w3t+WU~I۷n?/X`ܹM[}G={vDDķ~KЫ"`0ѹF 3T7Wd9b@O> 'Q۶ʻpriUQFMWOMMQΝՐh]E5mc2&0Gɲ@d503Q5ݗРA 4h'@n^ұ[_F%AVR3bJ>T fڿJRss4? c>rbociа:v"""ڵk*hРa #Ť@=FUWDٙdVn7V/@.]U~nݾìsʰA'J_!koL :ZrbQ0ixWРA snok=FQQыDTF @z%AW c8HYjeHM<R@ڮE'.tkz[[z-,.,#Vڧ^S`$*/ȼjBTS%Ioii^^x'ɹ8Zgkd+Dk("J$ YS $ dDx"湺]*...AJjӶ5 m+wJZNJr0ƶB,B$F` ib$nnԛgvO!Dm$BY>`wF?9\o/7=E$mӹgI_G]dasv-Eǿ|2ܫAWϬQ]7ݬNN jGd- Ǭ{ {Sk0<}s(~B-Q jaWWόC2 01Q..>>>!'meYWE,z)7/BGKdXUƺ^oh{n3lv/_|[CvI~C~B)^hJICتz,[fgB.$Ӕb3/OKOMA,&IAϰ,+*.."|e9Tb,`Fm̳$en_`D&ܫ-3;+#αӿ/^X?̍ML{3[?7Oh޻(άvl[|e֕}Z8J3j"(q.$ 8gt:ڣ^^43%uЋ]ۯ{yzV4JH/~7=:q+l帒a kW\qC0J 77A-7mV#o߾YfQJSh4/駟ڷo?|___$K޽{ ,t444tSYbźu^9l/̿NUJ Rt. mL淍f0n\v)RNNE$ruqqsڋ.s`0P{S>x[7n_&Mt +WmҤIΝO8a믿X|ݻNvǏϜ9YE2s?s֭>ꗕΓZv޽{Ӿ\⭨u7H#zwhib$7b ߽T)ԩӫoBi2ÇI&892v?{ͪ<˖-_lc]Mzf*8޼呭ZzID(#zGfOUPrssG٫W޽{&Tooo޽{/^6޽{UJ%hϟD_|43҈H$ի׬YN>]IrҥK.]o=zY3}P񑭄8YITm]xMQ"L>=<.H~'o-ƾ}N87omi||͔7}4ҳg7n۷fTIr ?wRVI|[:Y՚*ԓHH##:.1!!?7բjžʵPx T-ž={veyy]jռy̙S~e˖UBG?~՗*ҥK.\8p@XnJMaXZ {z\7m hB$5n+zK)m(RWf羾{c =z32*{d21S??L<3>K0PH 8+Rc\2I!ϝy, 4ZlpF7Xճm*8+XO>V5VB)o?ꫯFb,W\pއElٲ5x0Wb"< I[v Y|L!LE*%#SLfZgzV //jc1JOe0 f ɤx#%MZX)Z"$sa1!3lLó8,lkR3$ /yF 7=4/eV 1+Ӊi4/_֥{[)kd"-إݮkvv0u ׭;v,G 'FRR˲k6u|aH:uJ"V/xzPeVm&tX\xdb[bX,K$ӿ:.//k@DFʚ7oK/DС##mܸ?f1+WF[ĉ DFN޽{׮wر)S>??gϞ(:v@Yr +VH{_ԭSvx`5^Yn2<BlNzWKm54nxx8)jg`-V8F.1>rIz t(?J\&O%sݡq_t?h*So:Q1|UgLxpkڴsg9ʝ;In 3t-W! mlU/ ݰ5k>5QNJeRTjM252W9sVZzGOΜ13//)K m')KK@'{`c0XGҘ J__B?~btO9z`~Qt=`vv6(qڶb{^^^K_~˔rLUgriԯtG͛afF朹s?~jUL+@ IYc߇޽{tlR_e2Y͚59[nʕQ0'ONM}Z,~hҤNmٲ%rssڴ1kK{z2ÏZl)!I*YW/يVg3 9JP̙;w SJ0I|w1rĤIG٫WRr1z>dYvEvDռva]FaPh&Ȋn6~#իW^ g~Aѽ[.ݺLX1jȒ7xݻ4dG@1nnݺ7lL/=Ѩ6:kR.7n6oތeʄcٚ*TrչZRX6iXuTH[;+#D*KMIeJZXCRJ?qA˖..k0cVXR-7BjZ]drLVVF$)Ν;өKW*Y 4\kҢi1j՚I.r`zKIMAOE"ў}t9 -lh*WYEoӪ:1-Cx]GbZiu&_rs)G)J'[[χ,ҪnYFoSUZ)`t@U!/OWFGs&sBoBjZ햝PjRVjZݧwY@o+Ur)p ` j !dddߍgD.g_ QJoASR3 Wԃ0*Bo+(I}#Nbˣ]ѥG,%I,!GvMl:y-ZoJ6\bSjV* !#HNN@'{`SqͲ,qN-[dPh᮰QR{xX6=-=ۻFfV-wMOIMmڬёR0LRRR+4%EO{H O\j 0fg:wc.\8OFZɓ.^P"[}l͟nӦ5KBHvZ5k^r˜Xk$@aVmMbX,K$ӿ:.//k \> -Zԩ?~S$777 Aw]zͰ#[M17ߐH$!;nmS~Æl:<ՈQebkfT~x׿F:4~u$Y%5jm_ϗ_yjڎ/e,G3uN";p !6= $$t롃mTʥDhl x(okUS֭|#BBBCM"x Ed*]mu,|VgՐ{`Fۍ7nذaq2w;z___qqqyY|+e (eVBĈSɄA^d+z[ jwT01td!h\n DRG?o}#\j8f\/ۡN2D,&b1HH^^_D7Gљ33==P}7o"}ǩ2hq0HV"D$0WDiJJfZ] 2T,` cSj TA*7HhTh6<`wv"m3J^h5r(rqiJ<rG _UK*ܜL~M d擎O"iJJ5=.NY00 5LX|W_"X Qˬpq t(?sScuxj/%áVg6Vgq$?//;[H.ߝ>r77l|YVd_-0? Xt],x t"=8=mbwkQ CFfL.LBy:WFfJ,ڰRT&i*tS(DlE=m!^T[܍t`@^/K-mҼUT*]rss<*ÅPN]*JV`fJQXpwáz{$&%Ԕ_O}C9C-aY/O WoΊV̌5jXK3ä$ 8hnEMV6;8D~ݕIujN~|#YjuS)5}j֮][&P' Ys=gN[\.xۛma@Ykf*6S,V`Os_XuWRONnNIeJB"2??O"֬˖ieU3rru\KS&'j![̍:.òRT"W0 K Jpۚm)k1*\/[`VP-•sc& ͦ:њ#KUL+˲-X`-VD!.2"eV&FJ&jʼnX87P Tn)) ul0OFffD"SJIRG5wWCRni/\h԰!,Gkpmt <mӶqr77A#2 ϝkۮ]e] Kkך7kc[AM P~eH@@RwE ޵yPСGL*U@[ׯRnт(->z&u-<\[$]^{`=NeO:T`AjOINZ!¿޺eׯo.P*err2VѾ~n㱣˗. pRpp%O?dVcl+ov32qpa "uk{1&fzd7lT߸ӦNٶsWHH{)kHj*p*` Hd+pxVF1sq+Vٷ*aYJPlAڼ5_ޟ&zvy( .j![@qz.]!8Z.bXvFza|ݭPV"H] F#)ZazW0UYK X!^u+˲-X`-VD!.2"eV&FJ&Wq$ CjzBVsgϾ};n.߯oM j׊a,T;9mlh|qBimfC4jzyy>\;ej;T8J9eM?'0,k(d+pqf͞3:b˰lV!!!QVO8alDĆ͛Dz׷lBCrﮝ7_i*Ji^^^3 2 #\]]\D.ʵyk08W0yFPϜz{=gŋڵ#|4~]`0X6o1>>>˖,޼u6t+/42btn]=6dίvM[ ʰCѭkS>=rjƍ@|F7a°3ԨQ#/?XS V`WJ*+3çf-A#:lXR҃۶ 1r{Ki\e5Z휑#!#9|ϙ^y9'77a%ίmΙӧ5nԈ?1???4$M֟b>G`53O}ZN^}+JTV>tIrX\l@@DbA͎J%wxߺ̚3{CŠ woK$R ZcпΜ6FC)%r܊aZM8noޭ^yҤq#BHfffKʖ@jB7 z{tIiOg]ǹ)x_~">_tUV`6i7/_@ vWDmژpCVfF /O6I !=s:Cdffy7oּyx>/[nfS{ m~/T*exk=..4/˾Oce[\YQ~P~P~*UYѱv] ,:޽ж%9^80R<JMw![S%yԺmM5`0?wmv5j4 7\iִIKSK@٫ V6lA,8@ ,a*~GO)]p];:|KR5l+г+:ԢՄ TSװ^$L0VLP v>zݻvBn\-:BeU r X<+KN8N[ǎ._44,K!K,>qxʣ]~s zl-ZijIA`?pcv۷c0/L_*w7M:%66 y3mʨ3fΚ6}ʨ sͪU bhB2O Uf;ڃOs]B>p\Űlڵ.>0LO04XM Nv?<F#)ZzWP-d+`z,9! 0YN箛VU ZqΝ=O.U2`|) ^{`=fO]-J)[,V*8㿜<٦m[k9cFNNΨ!#Ni:nm;wC>own4Ҟ:{ΞuJ$&).8l0kիV~f3eHHHԪՉc#"222Dz>k٪#|Rw>J&*j-qJho+ 0 qFPϜ㯿4mj1{˗c۵v꼏>ԹK Uu W&L`Soܼe<`kcFǟ5͂O󅋖hZ/*#b8(YBG1tذ!CB9|33-"kϞ&N4qÇ ݲ o߹ͭQ``leBk5 _U@b z p6Rɽ;q^5D"a9sx=4,AO'HJ;1--ǛO!b{칯mhzk}Ͽ> jVRt, [GJ!C噑vbL,+(~HԺMS`LONFzQsrrΞ;߱}B1cOd}3O#,^<3#ݣOԨRVv+<Pj`,n.d+pDR ˒Ԕ47RiACWu~]g.; 0?oދ.קwΝ?6-zƷ|c朹+--da(c}N>LRa(gd{0ɫb:XjN5I)ŝۢ&[l*mєЗ_Ky;ve(-> ӸnBіDFSVL-i:E7A'YNĘqy>H ˲"O'z }j/;eXo aecdLnS$''WKa=ǩ!0 ᙑ~A~,DhZa.ڥ[$Ī31TChQ=)N˲.nrT,1 2 |"rgYOSkJ08(YYJdR Q0\ȿ|ԹZNJDFp6.R˲ ÈDaYq SK؎o:lEosQbc~WV߻־]JOKx\@BЩG?S~8x0 ˲ ÖCX]r!_E.&n߾Ir77A6 ϝkۮ]R[BئM;|ҙ}l06ӭN* r*[+q'js @o\g+aaFJJ)]p];:|KR6ltr,uG jJ{r6Co+pD$2B)[vIqÊ,P2YUMAF񔫫u+˲-X`-VD!.2"eV&FJ&U]un1HN׬?;|а 71bžkwY㫭!8T"d[no\б}]){V-wI8.rVk6̋11##_a}ƍ~~~ӦN*ȖwPi@dRs%\q{eԊ3gM>ceԊK/{ಥK!7m4=\n65V)[=<<!^^^Fd?]󜆔W'}0n޼E9:z܄_1SJ.\8z܄}5z}DN',{#"+_+*m84uJ d+ptzkܾqz.]vvڵ"[Z64,0GE}}ѥG/YZvv;i;pa܀O> !;v`r`Æz~`5+!|lFec[=aR;m٫hr9Tєܝ8yr^=gϙiƠZN1AJ٢3^BCɓBb1!-QggBpE.Rڪe0%Wc.B1S [AUMzBQrw䨈">]:b={:r;ֹg߾y{w׷y۶ј>>cGWD^7"!!!;;Dsk"PFb:*J6H  NHLlӡ]JOK9WBLz.]I~l՜۴дI3 εmN$kk@(wҥ&yQFZFGV p*JՍeJJF颅 w<(#^J*0KUUR{W0Dy[IpH ǕFoݲ{NBȍ׷EGUTR }e1Ef+eh6Vjw{PB(-mᗭK]x)88d'7c/dgjÏ1G*RWc3Y  1`Ka=pRzN![(F̅ ۷ؾŘky1&fzd7lT߸o)f X0`BŰА D捄yزgSdXd,x t"=8=d++eXxeԊ3gM>ce kEf*Byk!khLk&,s[^zi3Θuᅪ]p\Űlڵ.>”"֭Qɮ[&CswѣamokϾQ#5m΀~}<ӪK ! /_ڍ rnp )ArJFVz-:!Do00C)mJIMMd2BXjRR![-Z\ 'N\F`c0pXǏrdmo999F OKK#>\;ejdiU5_ݺwu&xL}}MC4Ww?q]=ml~PT/3kXݴe Zz c#"6l<&bԣGXeVZhqEW,3|Uc^ !ؾ} //2` _ Ko8jV{]xQaԹAK;qrÀ vwWWmJFzZ|baIIo6|A .曙&h웿\'tN@{RڇR4�}D.YsvzhXX1w^%Im>3˫^U x t"=8=>{zgRR8!_֋Dmڰl`0<=w&#=ya mnC* Iy$Sإ7dRX\RZl_AjX,%HJ7׭pbP'c[=aA\J4<<YfaTCpei$@ԭ6B5?Y\\\i^}-h!kٷ' q!.21T2a !e_`pP2I$r7LO)eFп|9ZMVb:*0(HP(8JU"e$z[}1?U}?~/yzg gj_OP 8z}_=GŮb7(bT5{yH~XN&M]\*kjkVVHRJnٽk'!ۢfPd2ߔ:U 7햍?;|а /,]=K~v1_p~&,Rcpt %Mm箐 <$c"F:j5iFV* !#HNN@'{`ScݸQ ?0qV\2WF1sִ3VF(d.[yA3Y !kVR(F{xxB6odƲ1^{c\^߿Y/^4uڤ)1/QJL7#g̺x)vc'N95vO MT55|.Zewc0H8:5n߸~s.]u~Z]g`-VVZˣ>|R#,lLVVb7zԸ1|}aN0 r 4k9vDzl~~~-'OM,jaC#s88'B'&5d{RޱF.SJ5M݉'s6m jڪ34d8z-:!Do003HR]5 7ViA jْa|F !YYzCvO:\T!.RR3-a~XBI*Z˧D)EwFox[Wb.]:z۶MQMlР]L Ph|_NlӶm]BŘRٸqcB49cFNNΨ!#Niـ>z>JN r/7֕ҿou_nrӦ}X>7vlĨbbp=T*|yr=}~Is2L#>$m_]oƒY猎2-[!vZ={GEB>~<0CBBVRqcM]BCCC_mXƠ P̱ض0 ݳ)K___qqqyY|+e (eVBĈSɄ5]dɣh촧i5)" 0/Ws]\;0B{=gŋڵ#|4~]L0 ;14iOuF6уh %#=M(6dPB#WL D* 99Ղ `VJ)T>"a9sxTYzK$P]` wwϤpBD۴1]`0ަ)))0>>v1 =LL@Xf%9$d`>|h洩5}j.>~bjz&9gy[CV! wn#$`~0f0uI']a)J"++kaԩ]]LlA-bAUM TTrْe˧M~æc"ذiZڧ.vTOZk׍3n'BaKUZǎ7; defԫ;c֬wΘp>L{0eC9OR?1&b].׬-dabAB5We_'Z2ZB\fe.DˬL(?(?(?Lb.OSܡ}vxذvm’~w ^7k\._׬,~scO̚٠~m;v9wa{v~)_ ?A畗Mw_+W^޾eQoڤyHi֮RVW7^Վ! jjދL5ޱ1$Rp_B~˵p={ h0ù}Slgƌ3zC,K5aBW7B88nqcF vx7ޕ. '**==Tl0̣dBȹڭj/8kբ0쒻,YF]-DZ,S5 7;ҬtGc[}a 74$?O-++ߍEaLwiS&,ZU̷خQ#{xP1E5SJj T6nЧWM[?BZR=oYԪ޽zvڴYs,}.;;S=YFe~*H҉㊬15nԱK;.}{Sbh|e%;02mfCH#$jQϳYr*~*r) (*`<FӣTDEPR623DŽe6m7̼33g:UK!j0lB!Z$)ϛG!jcw [Q[1r8pCBey ԑ([322~Cq@5?ݾ}{!NzH6o1B[un}L<B: V [QCHJ(x(._ òL-Њa+mQ222BQJرcG!NfͺH6o1B[ =UFFcی?^b؊j~|&ATjMbrJ _]{B{daějqQdʔi. `9.{oG?DzBpޝbaAaRRdff VԜhun=`a!P9iYrGL&< !LA .X,_DGG@;'fjFU>Q4m6^!PX:NRJKJJBa+jNU3RJ=>/HTf߽vgfE'_7ftKF$BU!j^EQy #sJe$6N|gf>jgfBp,Dzp,P4u)W:VԼw_cKm}tNNm SpBHߍ:E-S =zrFk [B s[Q [Ŗ\rvQ P5FeǎZaAR_D322Zn#mE0F!'-[M7[薶W(}Bilb0Xm60vBmJٿx:cߺp1 tKL\ǎFBv)qqqN?~xMB 0s01$1[<y`#zǰA !z^TXfǃr@:FLmj!Z [Q \yZX2!qgo뉶^8Bb%QWdRJ8d{u[/GXivaFyKUD322p BV9"B$I@?yՇ1 3iݻu\#;`WdVmݽgٽ{16_!j'7x [1lE%Htם=Cdǟ4{ֺO>Ǭ{oO0 Y^ֺ^->..ǏiV蹡bcVznFc6Dc6fxO#l cVԜ+2]/;Vу>u֊ՃܷU|cV{xaVԎ*vڽ[*7ݫ碠_0 k-)U(^;09/j5?_?YR%^)*.b]pYB1 wnNo^[>H=#1-0V2DEju}r!F0>o /PcVBUdsٲz%]-ޘRUi]Z;:ڑoаը7ݶekfKZW(}BilbRK6(۷h* !,a ŬWuZ cu_~a3xb&k _v^i:ױ#Q7ZL1TE6CEtDQ>OňUwwž~81ahs="LK;%E iЭ.ԪSE!DoЫ xNQ/GO^--+&7'2}m-T-.B nruJL0!9R4###D^5.)jǎ!҆:5"s}o1\RWOo3BOvrzav{ѳmUFS"3ge>XYv霬0@^h+.B6N$٥^Oka+t)p\[pyֽFܑrvIp!'Io|k9ѣwRtZƓFM(Z3[B5Nc))):xPV3DQQJK;Xt:mRd:vba=Oc/aX$:uK"Va):Z7r "q\Navյ[f~BΟuFGGbĞݻ].aZƒ [ZiʰC "˄*-&Fͳ}&]A!4sتP(KLrrO8VZL+h1m/+qmCmX;YYPU?nܰB ຉD\\U&?^ӬsC' Ƭ6'llϟFX [k%4k‚Js>糗^]Tn|^ڃklǸn"%5t8q"[]+wj;kXX!PRUn+m2Nܫgo0LJZt8.r]FoMcVSUCp$MOOLLX*Mo_:!j{SzEfΙ3zo6>f']~V&Od9d Ft;]Na+D\vVM@bm#b V:x!P[ӐBJZOFwwnMH0,^ZPisŅeŅRQJ9_kWSKĦ!8y0!ˆ8e(PeYDAQ]V[=u'ظ{*Ǭ*ҭZcB,)oeQk_%! RZBVC0ƭ!ZB!6QD(akԣ@}c]RanS{U,2 CJKa+rRVV$t*?R/dd+BE绲k3g:X:t5n4@c$! [Ya.zjDZ0,!Ra C(R$Q(O&^[$?"]OMDQ'&iv[Pj>I9qdMcT%KL_cZ˲,rqj2 0 C!JJgP2̆jE&8a}B|$Z sO*).=fLYxr[5OMge9.c c !Dm!D"0D!Zi+$I2(8.<ڤ2=/?Oj 0@)#*orz\N }+˯k!P빹:aa>[la P@3FѺvPq\ݎecw88tXTYvjڷKdN*/&V2\qB ta^o<}|0)TDAB$I *Al*^#JWbs8dhZ6Ef+v Rc${BS(v^ūUJ%I%&Yw%P!i*8J![Mo0Dt9={b OYCJj#?dwBaMî|5?p(g_UJGe ȕ $M2=Q ( ba^vr⏳gN@XӨ_8܀RDwƬ#︯op7i,,*4G\3w;Y~yW~y^}ߔ)s~k:%%(c$i]~Ax/rcB) + ֥ɓ#F]s!l7~>#RfYm@TyĨk;u³{5ق[^Iն^S(>|^aD@2D"Ww'UP^@H "6e.ͅ 8Y}e:Owe3 F;&Nԓk֮ׯ_j3z{@0b̸s رcԨQ5% 17t5kw=a_m ":s)Yxznil>f[:?!?^g;?0wm=C)#ak[pWjvxF[NBne)K*]LR-IZ1v~<8x8k޲eeW}_wo-ZV\e2l6XY֪56x¨jX}k萫k26ovϝw@>ͷO?u!+Q*zU(e ,Ҙ!ڪůwN]7JiiJYH a侮*t)rEB*Q<ye6Gjl4)9ͽan-.۫gټZ LI I#B+w}NI ^~aCx<+/{}=ͦ?Rw RkU*Tyw6'vq!P}1U~/oͲ6tİ?bݦ4V*7 *oj% (W$kyKJb[lΙdҥ{}\X<85}M&XuX}r}Wdﳿk8p0O>n7HԷw8e쑦TFNd *)(asUr$IH*¥IB5@uj.\=$I gT^b32Sp;솨hNr%Q,-.P2 e"1. ے\Zۨ$@.b9 Ŷ-??UWsµCYYeeenj &`ۿ5p,U>3@^_fAő4ٳYԮ)qaW΃d986Q^9iYrG5+!jP= r1c;?^b璉JL1eŅK,g[⥟tPVVBBBۻOQwzڹS^{ךR&kqߤ`-7_ɓfs.̫TL>(6W^ܩ$EE .Itg~1tW;vyՃ?;K>Xg'ʭP}3?^Eznws59B!/! OiZ%Ba+Mi19;mRvFoݢJ .jRiz%E(;׻G[xv50pǎ>xVpeTl%&&7l~Ъz@.SN@߾}8>|C xjZ,>;1o/¼_xq uPsZBm&e@e"Gº6Bc-gYiYqzc'H퀧EVZ ԔH֭k׮S$t{?'N +H۫g)OG}lO?8񶫮PSbZl brr2hXW }]]q!PTMSǎY,1,=Ocs;'%ͫ4b qK<}Q&SZj ~ym@MCy蛞mT~m k_k[~5k>:zGԓO|lݖ{d2qj\Ytt]wݵjժ7v[y%*77`Jtپ}{/;j{#Um5%cϾ힖&ϕ d{<үrIqaXMjT"DV!]I{iŢg r X/ۭE^#^,,:{-<"YXON'z밮:%I4 3f<^!W_xq?N_5ͥe|>xVմ=,]40\.O<~\%^'0L\>3IIIn8y$Bo. !Of2A!UmU%5՝ j^P*s,Q)J}>Q Q:(sϝ{a݀6*a$6F+g ))I&ݷU5-YϾ6/.W q2?~xjjɓ'n:dȐ}?U`ʔ)=\䯾ロP(Lb4oF;w… /_zu=;v|QO?o+{ر￟y(BՄTʦ(U)1&cQRfEAqJ8!rxl2Q;%aC\ͷ>z гGZ~yVaQћoezJ'gz%K͛jlϙ=ؾ}.]xe˖QJ{}?o)S[j' 0trغn:8qlro6 _}wu˖-0i$<B%kNODW$wX-9N?v0,׳[5ÇvrƬm).@ZRP*Ewu{=?X)S?C>mx2R9}W_}uԩVRN:۱cZ馛=6l{u]kϞ= 6RBFgΜQF%''oܸjb jʹ ԚdJІ;u8,GWf4Z^c=MD_M]N)1G~m˄ר:ƝxG%:ױ#Q7>m@||[o /8Yf9kw߭ٳg̘1O̚5k߾}/\^űcVM/^x{gܹ}]q}B5 wJAY2{t\5r)~; 9cS}8{faQQf٭5k.ctõLBJ͡~y^(yTޘ֯__u7|7߬4=UJdɒ%KTMxŋהpF 1bw}x)"P8vU!lu p 4~ՂsQ)K#هM&~24{65EQ^~9ztͪU Z]"#IyW?|>ɓii=h PP]O'77oڃ~DeDi>WS*<(f̤7ϛ}m͵4`$4~M&ݗ]un0D!Ԧ2Z݈8>OggϞ7p}`#?}?]?GJE5[1 ケp'+CyW\lyh\y 3zru9_дC޻o%ˮwݗ_}4FBP[lậT[ؿjG>o5FJ\JѠ}M[jiMW7_|?8Z\ܷWJ acRΝذB'Z-ne쿿c|%IZç'?p$I M ծ!BqޘNJrm$Qmߵ;77w&C<}Сpmw>(ڹٵs(jj|o;鞻$I;_3ߏ״Ҫ?!PWJ_~;oi%r5oNR9۱Yg? !slAۻwGس{:ll-l+M5|T:ļւӧsL&cՕ B!a+ؘÏf<\ƨ# ]{n߱PO)hǎ>xVpeTl%&&7l~/ Ka%J~:2EJ{41 #B3ld[<V޺{U*=8?g90*-v&RRSN'Ruڵke%֠jYYG{\o˄2tZc4Yvخը ߇#x !jakME4jn5|ǟȑ#0[cBl PCԐAc,"鉉K }d2EoE_B֪rbPƯ5֨( nz!ςt+|nm^ذ1x`HIv@tky^Zȍ|>)fz3\B{8@1tVpKZt虧|˖gewL0'׬]ׯ_!:x=wڵ{ȐJv, Gm}af+B !RZ.ñJ)X1@@Jհ@: ؉JɃC]?!8U*q È(OZ;wN)JZQlفCU[t+WɹfyUOV0DJ+n4ر롇ݷٲLJCM:IBb HzEfOI@J0˩9VP+K>9|<-J8)C_O5^7 *aI!j`0B=JСBuٲyӬ瞧@6~po9gk/X@&>>>0%s/6l y4))Rn'%&ΛO s{W|a+"j+2[TV.$ax+h>m@b`gPsc*'eYYxbgB<l6ۀ@TVHl0wΜ%KB}f /[9)QS׋dPOj :x]PIԝ)IJIQ8GXr'(JNP(VEQEA f08 f&;w.\bĞݻ].afsmgXu⢅ H_>Ÿ~$"Ba[0t9tYF)Q@)PI%A$~B="TfVZ@I$?~󥦦ZV7nn햰͡5WG;YYPU?nܰB DQx֒Rz܍ȼZST\08LBxo:m @O@&AM   JT$Qh}-c$I|]t9s֭[ >>^Tу*Imm?JKDJjjJJ vֵkJs˂vaFe/ *h>tHQ^"𗢚B W %JeKgK^ +ψ Ju*ǯR j=4oqqqr(~ߏڞ<1%师@htAN;RaQo;m>}͖8 ֢o7}C(MLPZJE!Zj+ jcMIzkɎ9gV:kuIn26REKLLZKK&.x:cߺAu:%&cG^UdBݰr;IOb|"F1H$*c.%*7Q +RJk-*tE~޽{@5/΢u8.K$YR}PūT ‚f-AռJT0 zRa1=OVNQӯ h1 5Ce5 RIBljO}`m\{ml\t]PI*)ڱ8czA!VS[逪õ&iDee6{^~J՚})%DU޴\ z}. Rk厱(B ~lNa0J%_|\hA"T8].0XA!P[[Vr\t߄ͱQ;gr :,*,;PbfۥsN+YEU(S\,=? kt^ ! ըyJ0T[8 !"^!@ h+Pm˕%A.w.i{hڈn_Ń <;b;$t8{Tyn=`i+H>Ӗܥ&) R,܅\'3YA!Dpf 1D^ ))CN g nG~D6W%ں3l6^g1ܴj "B5}jxUW V~ѣkV4Wg0@yARXQ b@BAdQIn+tb6jFn?`=Ǐ7.t*I\5OfO>ѯ_z=F@!&EW9lp*B*QGSJm;]qB%/Q7.k :-[vPvu֢E:nUrs(ټb'Nxg[|"T]*@%IZlΝ ӦfdgcwBmU=Ã㎿ߎ>nu>6#0!P7Wu;,uGcGOK2Q~M1`o8.z~^>[^}vq_"ZԶ͚=Gf;~=h}{ٽ{{vv\C 3͵>xMÇ>D~םI*-BFUB Ο;w2뽟\~[oŗ# Ի:^{K^gm7]w? Nxq1Zq׏w}ʬ *KLLLOO7nnX!dZRT^;,Wk@!֐BQ8U >[o#kߎ׫kZJ~}w,xm*E/o⹻oUW@^XrQσUGi5c\D7t:O8ڭ[׮]+-+ŬV>rnlRk&ˎ۵5`u!RU%OR#'J};]q++5:"UXLZHIbku_'kؼ+Jന yԱcK %'&&F[, p1J[zCNm[nAG@MJc0lE!Ԯ(ʆ-YVQYWgī|fk_?OHW;;u: w/xcvX766oX0%d63Tb "b㿗XM]buBǸ/~us;tKL\ǎF_7Bve9N'"5fN؊ Ta2[BW_ [)=yaҤ{_.}$ISW +y/-- cTT4h(H矞 \]n* `W%Ͳ(yUCCޠW)T`:F !u#jW\BY@?R))U2 jΞ9seJjʗׯ_UWl2N8"j5ꬃY#FYa;\RZzOQF`Kv[[;0~Zʕ"7D! )8[ v5p EQ'3B;A!cG{,$!j./^P$b0T*%::,:*^jb@5I&B!ԊTnV K@f٭a&!Pdnw8:m֦ 1D($!j _(*j+c jg1l 4D!Z .]Ӓ 7@$bP0,[Ɖ ,$!l.\M_+JA_UNOLV \r#tiM2Bft3EE 1ǃGe+dMuEUQJᴵ^r8ʓDcq,BnVJ5f'~s7n,Z5 [ZO&BK>>OĤ5Lxn TU]d"B(cF~|O2/cUa(6]VX[y}(f1uhBE_.=C) rךI%;n=>mgfd7܉@WDQH XhKm}۷}V"ZZT.-(ZXW T nv7{yl6$K|?̜9s9ym}P{.zF%[O/N>ߧjBok_[:-=L沿NC 7"z$bΌ}z M*Rpӵ7Me!WOW\b!#oEx=2KvWOWs }e ݝi )n&Ń-sMs=VR;TU+WlnUꫯ7nz}&3=' [-ٺN?R씔gϹ63|u"[g= / U O>~MKoMK.9$h4utwBa"3P9WlN-5`lqlHWS5D@d2*Ai4<o3R)@ʌ%ߘ{6$3v_YeY#uu!D0d,GIXg{{42d9!&$=slyW~cnjӓ ]P]C ̞_VLk CB43eZeY:UB*,˜ p$0NW/ZyϽ'7NsmRU@žD<7N;`0\YUc$kOG:Ѳ Ϻ0d+E4[O0B!Aڔ LM($I$JQ2,;y=)KYYF˅XʆE| "ǾD oJ"V'5}RL/>o D`}α.L6J^ò,.>H4UnP(Nh0X~^kE^ZGV5csd+EQZQܳfy;dǎN oGaB"hq9Ģd$WjvL)S.uzA04ֿcwt:x[a)hq|Y!D" N(U43ؕ^,_ϬR˕hxjSO-#4Б>Gq4+V03oֳ5BP] 쎞ښ*eUU9rX8 pE?qC H82GrVHz^Z 64v%BBH' ӤX43Cɧ43. #.s۞~yĊ 䖭!^**NLJRY,[y/ AxrWnV}cY ϓxNc=#p`"61x?xA#q) j]߭xjM79}YFfX:S{ϋxN*z_jB?~jyz]YmXok8\Exky^9a0jPwHzkl0 56N[vۭn++Ok_}mI+濾v݆7jF:e40IR,ǃK&Gr_l YdvfHC־,qYڜfriޠ2l`f5f[:]% #qQ7gYu)GJb)+{y͚ys/5W/{yQOj0x~Q)OѪ⣏>:>Q,Ǖ[-VK9tfupÚW^ϝys)yA'f+:]>tn¹s[1B) >8Kl tuϫ9OXSlN|ٜ|`݆?,^q9'NR%&KeV)&dR2K9L&qʐ$pRWlBa\rq%GNt!00X-4m!4k9:nWSs&1It0&[`ۋt`^iJ]4MDJNܚhQjOR:.Q>!-̧wqVBx7RK&!Lܾ}R}&#*+=8ROOy9Np8{V>?kփyx<_M]=PN0Bapkkrx9@ʲ\ QURYUUQY#4}iu\Cˆczt $B0][1%>X8ЁgBX4ƈī&Q>Zc|4}aZě1gDC2W/ZD_{m~7@To{g˻?ϞveÏa&#낫xFÓĸ :}֦9sʬp8V^۶ ò .UEM3Cz!&NXF%)rӑ}> ܹ3¡`peIsH,Z<[TWmb)82wqpz,e]u'MvֲC2eYn:/5nm Ϛ';mzY3ώ RUY|kW0T_g+(&2hkmnc:0h\#YQiZ d,O.hsG…RA4ʶmVrL->gԯ14Aaa!hx?U5{WG>)H8ci\*rw76O;lfh: _ok̥Ŷz 5׽_?`\zgGž,K@ f=<_a-h4n@( M "_W]]Zj.1x/U 2v%lBB%c\'iEa$^n8|_Jo(X84jfxJ$jPUa;[{p9u2WcvzܮE!^4/e=gss՘i( _]#)HfssE߼sdvcRCR&BT8k`R.-]W~=gXu hEcx-c/l64PEs ħQ{cW* %0FCΐ,<4DHX_ATw8P8 C;wM%1?K!Yi5",:3ۛoκ`ՙfGQ5k^{sֿ6lᰎNbϟc@G)pyyD]4wEU+[ش!XxշĬUջO׬~)3Эe\麨4\:(2Ay4=~ڣj)eeYeL.m7i㺵k6{vjޭKO?!\y_ee]w-2yrj!,?355ߺzQ|$IKoYvқ^˄[o邻/Bhwoܴq+ZZZ&%O LL./ !8 %dumEʭaƲq"[ Lj/[D"!/vwO?|]!_}|wժ{d!G~Y̿xCP{{gV޽祗WϞ}k-?#S&qӛ/fM#v{kp_.6&IŖ>;%#L =SNd۟!sc72nD9؄3ǝs9, Dq7n)5W*q;GG',>7(=.BEUr3EQnyZfIaKJB$9N 5]>$oY/bc"O>h| V\{|cr3s,%b`ݩ(c#R _x"xG_yqɒX߻VQ~KӠ3Μ~S^o?ӷzǔ)';Vh8n(X\h;4ضi\>n!WT{y^WwʲtΝ;Ϝ1# I"(;?X>ĢEKޖ=ʪJ+;)uw3&(ᤃ_~ 0¼`|aF54LUUúTxA  ۖݞGzM endstream endobj 221 0 obj << /Type /XObject /Subtype /Image /Width 231 /Height 318 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 27432 /Filter/FlateDecode /DecodeParms<> >> stream x]@[]nN~vX?"*b xv؁bl?.'s=3sι]geK@vC &(e Ҙޓ(DR|`EC~ :5 ^^[}XAbѣ&AP$\g8e) xNNu^nѣHD k*1VVV$Lֶ!z4|܈!)VV֪鿻׎ [iffײm@`faOd?e)ײXl8UKת9z(fΚ Yl2wZH$ k =jb JT槂\b!dyVuG;fN`~~xvobOoyyNN5 ?ӥ݆*Ŏ75efGO %<֣d0OOkP 0!` ӯ$oq/m732/k̓;ծ!U,Եگ酟ʚnx !w`1r^K|-Kw ^ZS#v]&_f]vkbOSH&da67{"}<:5szR EcΒf$9솨"aQ?[\hzp MBGMѹW/sw|%QT׮mGbfB'MlK'ᐷzƠ*D" ,sbo88EXlGlJx;[Z5h~%;_~k Vz/Q7`clI| s˾!zanvN7e.CM% _*(ΤzjʞH]E =/Rin]tD۴4ͿWg\:ekbى@aںxEK)a5ňb.tOc>\AZg4q M'ߌz p"W4~v%%n8j\k^^^0󩛈}&'+԰021lS ^>WTGnC( .B{׵z`ZF O^ l3?7硞%ɦf*ңZaLk[["*{saA>d7<u-115'1 Ν?ߪUKXVVX50ܓAN_P0$M8niV}"krY.;oDRfMka#Ѳ6V+ KXXEWu Eqg]"t.NYhLJEBFMDauppڵ_kRwr=ܾ`ɞFsb9I߶{X^w#phd(@llWG܍K5=&ϵ{Ԃ"TgK-OJMG/ Tu<rCIqMqI{~Y48{@z* θMȸQk7@i_ִn'?~|?wBmeO>t0ONIQr+Onzre vu6~yuTq$dž9-s2ns}9%ŕv8jiWr3SG5o.NCz|r]{G$OZ 4!t4ˣ]gv]y>}1uǛnMHM2`im"+Nyw`q4,l"*}+l!hO8mxqْc]N[QPiB=;M{%"ΰ'HP%*>\|(IAy@c]d!zM p@`h ڢPfC!-xqCW?B|7Xov9QcuROLEN!Mixqe$2:ӕ'vhz^zkiYO;񅫯'&CݮV;zuʕE̚0~ pP;fܸ&2_oyNԧ)ɳV } 4pojlڕoS4K@haP|]@8dH6%# "yӢ\x,O/$ TZ!#|HQ j&^z=k" i[F;9sEBaTTX*!Bʬ5&ߜK9](2y7tԩ/v=jFoĦ/T j\Ы+v1:N)XGl ?ό;{F=f\% 9 $+I58 E5]igM5wƖۭ52\`k%$\q{잟okgt89NifcDl2 ߕ:q MA!"-`pTDrjf)P֙ųJ\QIq=oGe]X( I? 4i}WKH>>&z!o AS'iͿGPkm8"g|T _vC)1[Z*j][+s@ w+V>cQ!7]BK:ͬŬU =ې7P4Q%N ݲWX`?>j2Ӆ#;<+D*`ݜ1H]_Jfw]Dl0plRTcFv[vlTZyeMEݻ{<_6xykYPUlIvgF֗5sD,l]1Qj265+*(yT ))iy~^PPH221-.*Ԓf@FC~olgLk[wL8Q)a}""D613'SopYL+[;M|^qa>Hdfz`Z- <.O87H$ZC5H+Py]%w֯s93PLϨnp8Hʀ \9fp/:+~o춄Fv_hnAs*zkT1F U1<ʇ@?z۰>m\mnVE|kZ |bb.|?/%MYr a9 9i@1+D:⅟vMXȝnd}닙=#Sqѣ?)03oVԠ,/oxް͚Ǭ:F{~u'3P;# Tu:*ii&C@QGM\~uJ\>ƅ2 hd͛>kuEtҾЍ2/ВcǕﲰݿ.j2ĥZZ+GՉv!]WO]i>swsPj n# :=Q߉, DT.Er?\~$fs~W4غgP{*7s"v$:t̑G41<|#7[PwD.cD:OqELHN5v%;ȇt)9h R[B pkn-[bQwuMω1I:?AŒ*Eo1fڧSx"|4&v_?Jݹ}=D"V}rt͜C#D'J;Q.`dg {8y,\@&)*|q~e˻'f*b-ݒM8|Pԥ|X ֫Ogܸ[OrG}$Q.|* [ ՄG[qf 'j8 74:]ʇ"3ٶ-U$U3澳S45><09Eei> ھa\%!0|f.C@SQMbPƇ5vx۱3i9f> /ڮ4b7vD:FXLI'.w`¤T*.^pf{N,wBTI!0b";5}e KfZϱp?7l/5>N鵂9t&M"oyͱh1 g~pIuyoȉX3s ҸC[4貃oF[Quz͟ܦJVZAcc-$>_,P Mi$i\D >=8-BQ P CF| 6t­!Go_ChjpbR3c|Ju"Q|F2~@wuHΔ7ʫ/k* [WCV㇄2 !O d> _|=՛|%P/:X:쿥$CK ^eyy86015+CESa@~~odoYb&R$hdU2`cD$ Ͼ#@o5!yLYSaiDvU2BW-= f 5miN:!0L82!jx\nA^.^< ۓ ` u֞/fq1SsSkr{\¬1u;ׂ'eS}c.j+@ T-K [v0r*5c*l难!jBwGPZ=PBz`z{yZHvakУ40ed !&jWVKp8l-·{sQa!AD=fQ5!y2>WA M̘ZZaqV|cmIX%*͝kh!]FMdVeΆV ee`8OVL$ei.\ٌ3W,j[Em%WXCg|xȔazp+>yGҊ:!\Y5}e)APSA7ǯ6ߪ~x=V&9"w'!|w%,ȻwA׫C/D8`lf˧5#ǺmEԥ{ye<)A~拃+(͚ёj*",|ujMO3,TƇ'< 2::ҋ*K2.3w!;K f$]%U{؎kI$2Wΰ.Y׬j>&uKWNa !܍9#E{\Dt%hau?'iT?)»z.5zP63 [5yuJFЂZؒ*ٯ8uIzG!|ؿaC rKe3)h |2wt6oy5=@Nf=JKlVŬ:vH®huYqy<*]Y)f[$]RGt<{k!4)>6ԎM4u4ӏ^ .9UwCLjuNsLE{l@K|"y=#7jK%v:oBRvG[n-̽iVڣmvuIEKﵚ8sJ֢ kc :}@ \g+ڏ:Q4K@$_GȿXBR>dHҺ*olg4bq.J L <#,;s6ۦER5A|b7.ŔAD^e$81X`!H Ia .[dy7hJĬHBI\1͜ӠP$.?qJB({C@qD[Ch փ>Kw#au_x>8~r4js y|:Mċw+3'4Ic5cLaA͘ʺwӲ>-l,xori7{%P3†V=TVzV_Pktfa=_z{؃kൿ0 ,xVpج<>} D265!ajjy\iV6||Iχ=_A\.\lljbWp]dB̫}gŇV]/KOSF~KeOt &ZA8-.+_K|ܧ+l ʹjWk+ޯ@.`| p?Gh@ɩl!hB!\]j>mt79CHb<7^>u wꪡuj./[37y9^C^QBv>.Wص7p4~ʼnlӦ3NibW}#{uWӔ9CMU_ f)ofFCcw *gS<2i;y:‡t5@eemܞs5-ܫ{wVkZf+QMʈJ~vu67qmܔ!kGܘ[t0}|5Ҏ(A\>k]R+`=u4d m}r/"λKoBz&zu[%O]aPl05dŅoxDѥzh`M&hiHH7$H>d4unֵA6:Y;NsJǞǼ}g;[\N3!B]cRE"ͪ|?_c._hn¹_m)"=IՌ> ƀCU|;u+% pbNf(<&&knʒQME(EH>!O"CU(& N={ <:~Q-O}H4r; t%eA&+fLʩXnŹ72L)=8CYT.#B&OL%B$F^oL!r>)Ǣ|ec)x:܋uǷ#I94ah0ӿ_<4ES'96 2Ȼan9C>C捇49ۀa.h bYZʎwZD1 Du6νƷZ<5rrt u>\)NYRdmfQAX6gf!*dptG{\I+u˦HiD;52ӽ# ˷j^Vkgח+F'9}Ra^$P@ 7g]>Kp29j ڣ!Bt.V2 \|d2" O;}"6E1ɠ%NEg&vWh8$aUjȢ!<яUߠ{CXR"Imϒ l*QzFmn3h`%iUuE x3jƖBA8lvnOFU.=GHޜd `^[*IQs-cYs|5b*OxV6{7g%`4+%/fL ;rDMVD]XC-F=?}Dz Ե7ky=z,x*%iD >DUHʕedr@|ʈe!,"\֠PL Bu*PJ(i"Ae/䚥T 4Yb*)P6E]FFc%L( Qk)$k+(ЀVՃIgA#`k~qڶD?˾w1൲ Rߡ7B[Z=(V9@]xޝ =x-S I-/Iz©Q Mz٘p>9?`k%(k~S̛ԲBΣ'VVk G'] 4<~М~ֳHfZrݎW7\ۇכj}Z4Ju$6Fsgvkه^Yy,<"w`1r^K|-Kw ^ZS#v]&_f]vkbOSH&da67{U>$<:ZQYTU:I!fg7Dm 8Bփ[OxXl:jju菉B$|iC3T.ȥvm+8;5g:iBe;^WBFPƱ#VBd8X|xv%JXlGlJQ>w$j".(K$ _=dw!@ȸAG>iz7zZb  *21 ya$>P }bkg}Nɖ-Hsڧӧ'RUަqor=)^+W(|u CEQ$Z5h3|̏뽶jx/zl߼M;63%uoh~q7#H+F`Ie%A~v%n?q`!ϧn"F? pHwPcÞ#9ϯ=9U0 sGŭo(}曆?P5C'4;]_V9ř7W(y#cۻW-O?"*hҌ vѻ?ZRXC2׎1MУ6/,T(Cu!/FoǀU2BP"SXX Oa"I&&d (A٧V \.Ͷ$й.|>AؑTjV]>gdbk "hdlb2ZG7G3}tϟxyG*.a>0DP,Rck52 ZSfX:XCS[T>YwU]Zbt!E5REUP߻ `7ת?(%V:L*8w͍;(jQ;ˡK;$Y8<9<(j\?ۿjݑ]'MC ۛwلY}'Sξ479xD]*|ԧ}xuۍ1 Ȥ>Wץ{/ POڎj]Xqq#<4{}t_Zeo}tAKQugT FV;Ki[CW2y+maBv>jk!c-| Y(i>GA/~ngReve.Ej^lk+Ԭ~cR7? 6ݬe~wӘMcDV&|1|%񳎮?G#0Zʼ݇^5CF3lΘ~%dL}?MNb?ݵ;ai&7oeeʼnO(j5ԛhI6ҝ}qmhB:xUp * Sc2@$:u #x^n:P+ 6Th;j vN?u' Hy[IQ+g?NDS|hBq5lL(s$ۓ͝Yw䴳ѾAZ*u] xIDpK;p&#~hcx[w}KY2p>x%Bj W81u]=H\/`}tXCIPfc.5ΨFEX[ā_p#2 o?US!%t^J?nvS?VG> wÁ({yh^avwly5A}gBƎ8Mο*/ ^&Q g?ӺT@" # n%o=|=&|dz+"7E2]H1ruWExF~7tArɎG&4 5} [ v$W^CYSaPwy2ycA.'x"A:h7!+ AuGءc:s\i3eaBU^e2-,rjHdYa~N_vsNrҖ Z;!E $μULj+d; vUj`@y*od9l]xLfS*@JѠzGMQcVXe1ZzNE Zؒc8N f~w2+BM]TM[V!5V]qV?UxtLӣ֌ F|Q6c9AGU_T.qMDiM^5c*l]觪P|z>j~Vǟo@ƪ zգ4zQXZ}e7DŽFZ=_G_=_JTP\p"}* H226!+Luq-*< EZV#ruHDbxXh\`0Dx|i>N94yLG$-TbH1.IX,ޘvY;[۶Zh̉8la#*^*:_B*c{9?qÍu[S/^+zK e@tCȽyˁoǡ{!sNffoc3 sL´ 1pu+f8XfݭӄI=>Eӎ1uɘ+6\1;p3<"p D]*|»C|c& DV'H7Tjډ_96ZQME/l̸I}e|Bэ[}#66v\w7,Qyst^^i}= [*"@;Pu|@"qM/]rY8rHX 2|65te Ü;o9п.&kӂζ%nTs }oC*$բQHt=CѤi6yi۞Mݠo#!js4)7]wlw}Cܾo"*y{Z#_]_M~-b)ƅw7\ ;q?^A,OBz8WVZvCoݺehd鳝;111֖lvU =ỗ[6!Cp q?YztR|_n!|Ac?=\:t ![vm!ϡlQFi#̎u+9Lj.}%v{Rq]w>}IhۯH}(xp|JCidݘ [{ED>p6*gck5s*ҹ˽{?~dYA֮_/  0Heo'Uq#wn]~Z:t3 @s/Sn8p$#9K-bf&*O2hf" h@KYf5'‚xCLERT>~}6ǵ#f*r]4 R(An!GA]cCX λӯ:?icc]BG />%Sq=} xft,0V\1u4qDZѽG~^ d8Co]fOHw5Gw 6wʕoVi xiC$O-yJ$j#fE!Ph8D~7qCmcru4S|rYH>!WL1@!Y!22A.V-># Ç[tf GրV:Vx7zT\ $\>zI9?ˈ钻YGSHc8˟sCN. @^Kʇ@ u>` )+WN\g|~n]7rKQaqפٸa rsTe`PG>.#;:~G-[y{i") cˬS)}۾J=u1"τ6xnM~<72o߼#ℚpݶlNqc=|h 7뵋F$DP z9O*z{OL`݌IS7(.䗁xm˥(ҾzzBEcM,WvNSײVyy9<.͛CuL65,ݻ$u/e89N& Y#d9{*@.@1ľ4N3F́h1zX[TXe=ҡIdI>:D͠徥nK .SVڪZKspHw f9L07k73E'%bލta{E&-G-m|t*¥bUk|M]HT~Wej.e ɳ';`rmPuY9,@kmQQaQQM!1YKn9ohX^Mm`hH34*ه<׬(*T6pGoǦvҖ\j"+@nrC}eWgHxL \e^ Xs EbQ^z#imrT> ZG Ns K'!ytdg/iS`3E"MrzH{<#1Z߀*DU]k m#/r#[MS BfhT _ɚ| Hz>,C1Kss [v0?``(X.cܖ'Dtd&@ke ·/_Pׁix<677kC0 =f!|\ Uyުnlj>Rxvο]6󗊘y_ktc#c4np 5! F,·y}AE'uanơ.>(6~-kOk$9?ZjD2IӚ[M#t\~G@5# M OΟZUHSk~pL=qSS̋iiUEϡBq-VpOlWU1evMvMT| l[?-pVo- VanJ@wg;pnntqwvIs:__ˉ&Yݲ k_ ' S1#ƌio%Q][9`hɛ.~j/S^.yįr>C6tz{&molkDTSUʺ_61t-:cy S#&S4!ČQT ^; X%eOȕa*dUQ3W̺$ٷ3~=>{|Hf;5k ,sigM;(M`[*^lp>#S2ψ'^ ;pET$ٶ}` NRύ~ Ǎz5ɧ%‡Ϫpidۥ ;2cfjohB&'5<ڗf 9TմZuu/.*wj^镴0|>(*%j@76aJo|u;VczCA?nIk̑YRbvAޫS+ +qTY!HZW@ֆH4ܾ|mw07vw˰zHMR5#bd6n޴JFBx Yu]nئӏ]2dɨ"v>‡STulD D2Rf񚥇G ̇PX_/f秺MZ2;T\wmx첞b4oljn`hDB*)u=`b1Ņ#H z>om~CC, )d[,CL<}{Nd:C@2^1K  ]+'‡ڦϜۡ|/gN? ɸU>S݃mXAn*OSzo22>u]@ZAƮG'IspÙ 'ᄄLH}SdA*/_ "%ww~-%DF6Ch =sdY?\.{)AVv6'UL$85.)h!T!h)CЍ%qH [KCXFR!Xzy($E6 &I(4^m^mg{L!<|q6<;ZO_ Qc~Mȟ'LE[}N1(uݷ KY[D:c)>=bz3 Jul=;ǧ cf&W*y?ԺdS!%/g+lF o%mj+eee.K"yRhIȠ1(bM-`G%[KbeAn.E &f5jYW8=MglfJ #3U$k=VI7aJ9#̉ OW)k*Qi׸JFݳWTb/tA>dFg7AkS/=9UFna ih@ /8˺z(n@HX'^IA>gT YZhpr#*Py:cٍ!S+] mhC#WhFoT휜 e/MZEOkqe!8S,'/YTTȔ*EJT+v&9qjԒ*&^}CZU%mǐ4|% aww#\pL/ZX$)%KA'=%%6--THFL \7DזG8Fc4R9xeeJ_ q]8xV$*#8bIXz#87163%K~lj 䄪oSAiiFwӍ>y-;604R p- {&JTɆлP^'b1LDzQ֘t|~\=TPs@P9ǵ2z,++T_J]UTyz`BaF0r}V$E i;eHm,+Vzk5Q:5"˅YuaEvY `ͺ RqO݁xm&F@@ lHR4у'*dM,Cb2htC˛(!88!kVE!+|9NqqOhhhD dHgY) BK,ãHLX(|v2 M-Ȁt TTԶ ]IMsy<.cffkC`28{ԣb@[Y.gMėX&HkԈ&Čj@g3pZcbn!c ؂yJ p˫ky\!N;݈f֊BդKѫri *[_bbo h&K$c3 dUˡPi$ E"g2dYyo,BWp%e2xD;QڼtH씿A qcw1M|eE4(6[!|eη/v kK%gNGŪbˇW lɺo"bM|eq(C{QL8P ~~2E^%^ (k[iu+rքU n[zWr0haÉ$Q=>2u-{91I6gOxydnjS!Dpmev猌aJ2> 1"|ګţN-TdwlvBf6DTSpe/ CPSG^OS!B+y2>^I5! xֶǍt!Z5L_bE1ԩ|((F< r Lh 'He$%-}b6~. ʽ 'sۖ%,OIll ^'a8r+N·p5‡ͺQCأ‡ [!RzJ/ >>)[L"C8\ʇNd8ʇp_n yZs>E_.5DL/YKoT:VQ MԈ)d) B撩x`\>-94z>_C0xd. ^`M|PL!Bo-rPGV\jD2p@(rG$,0f.R~A~=hPH6{ YS#8D(a'B A"bFonZ$RE^SAG>q}>e bǞ%(*<1D`alTd '22i| ;=˅5l'{rr#XYtCLM]+_=hpĐnL7`E FA1DАjfjAv !:.?!̇Pͨǰ hOd5,g1L-, rdyqntCpUE-ܰG~ˎ@sZVUł@ʚLFp¼\6H0dj\ j&QNN2>, N#V%o޼n,&Uz>B]#]=88: Ye>{* ͿC([Jm!0  lHXkB1-;խA1k[w,}q1L0oݷO/bb>nd gKéxzQA| ]]IdRb3g!ll *5|l=YxJvj|n /t%{9tA慰䉓_o_( .+&\6a~TH_>^+A[5 ˫cvJ'OÉhs\5>LOl@]HB3$ljL /ʗ>$X\RT2 PN,F'% 2r@E␀+qw 2gQNY%E*=%@C@ `!'@!PP"y Zu=>t2}P=[^=99:T䆨 ek6\}f5Hq܇`uW*O'<9j}ZZ0mBCCQ+Mh R@ mC鿝<@x YQ` llWsn[t?zMc=9Lmo-pq ?xQu_](ã~}?wt.enV8>Qߨs[Udܾ`6$!C3uP(n Qv쉱Tty֯NY]kQ+fuc7^mr RT+Og pUz^}k;ZA 0(̜'z3]R7-g/4#fݞVsYXO[Vb,=g a>zSK̥sdwoQH[?'CE >u*Pdq=J[$Eȋ'Lۿu/6vq҅ G,3=SB>q8vbewG s0ѽzlBW/ߖ\JI5$mJ3Meb Qc?z_1"pQTӯ9bɘ~l%>>Se7Nw->1ս_嗛wñ}vIGHYy遃iױ/ qV`=R$wr/O\ڑ*dֶ-&J'ϲjS]; RPY\J_w6Pe6N?ƨ59FU th/r"}{>T@Vthx8D[ (?w=̈B;<. \;*å3^1gnXYk_<0q}4%]2q>Q/ޮu 4ܼ|1ȿo3e,<"W(drj36e:/0=Q !Q}nߎoӹ>#1%AyhWLݎoz+Mw:gYI?t-WgƏIYCiIƩsگ((pHKߠ+R֧$ JݽUv+[M߳=l0o*3'J#Ɣ2^aQ0$ƔclLt]H$9 W\N< U/HCrV*p8j!AC[oC7D޹k+ |3O;fL;k1vAo4]*c>_EbAŘ)dC^r-7nѣG(㓒`_Ű:IgvhlBGGᶦLk3ۼ-Ií.g~ضcf"r܋ 3#rIZrZ'SݼXZļ@#Ycca$ ?U+&tĤݥee3g S6@LHd?a 9bH։Y]KC]KӬ:dk/By};kKFgj8V/n<2p)G>Zϐqz__6=E!4WVw-5Kзʹ'HprnkSX͕Pp( fgggf3n wR~I̽D[Kje@f[ fY_nB| `Pi}8'CD" ͲT b}ܽW-eoÇf'Nu agZAf%Q NhG)y@(FO6s1?߂+dm@| YNg0׭5'J)x7 MV9WDftMe)&)QR ljz30r,p]jo~2B[u} 7m }ECLJl !e0 #c06DR3MuF-cH7R YjzذaPBehS4(UUU-%T297(`yI,8V'z3|%a_=rJGua#tSf^5D cP ݵK nwr+}bbEo<;\D`XecqBU#;`, `WPdCՑ*Q<`?(eWFNS¿<0D_CZAlM{|q;!p ϙ1"BU+ǧ6y?j1<}b^7[Џ?NNv-MՕZ(L1I"(3A5+vߐ(U~S"a%~ۗY ٳY[y 6a#_{o [zd?gIF%,]7_[,ruź#I̋ ܀ļ.3 &!=fl^8Ǎ̖qs 3-#DN( +(ŃDm!xyn٘z oA\txl˰v-%V..$If )EZmJjţ@DaR*$̄E&(PX(*m۷Z#ǞUJQj4fHNA[aC{]+oL"컳g?ۮ}$i#MQS #:v/$I };0t(IY,{[y:߆MX UcNx~c )=F 7 HPPZ'4|cW1 7LcVONGojҦ+H(hV];BE)ARI-RGko$4( EZo0?~c""i.oLDęfk9#M7Ker 0^`]-E$aS牰~cźtfZCa7}:aGHۄ[_m$†`nd"Or/Om2,jWjq+6ݽ\DJsZԀKw<@fLl{Nڰ墪iCjQeҙ(Z0fR4M4;zmYDcXu80^=Ɔ>zRW ;c]YDcjtGQeQgGmD_Itg V3""UVUR Ze7M~bbmeJ} EEy4 T^I\r&]n#ea&4T67)w*_ߐxMƂ=k9\yAoDl~c" endstream endobj 229 0 obj << /D [227 0 R /XYZ 69.866 808.885 null] >> endobj 230 0 obj << /D [227 0 R /XYZ 287.329 491.517 null] >> endobj 232 0 obj << /D [227 0 R /XYZ 70.866 162.411 null] >> endobj 233 0 obj << /D [227 0 R /XYZ 70.866 118.126 null] >> endobj 226 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F40 231 0 R /F21 168 0 R >> /XObject << /Im5 208 0 R /Im6 221 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 237 0 obj << /Length 1399 /Filter /FlateDecode >> stream xڍWKs6Wpr)5S!ċ{s[Mδ299"-%+3=X}um$mz(ey&M~4it6^E^`P*Tr#e &yL;%S4X͔\@ њ }ڣm=_@ ``AS~ -Sj(4 8Xl="_E#32vʁ{@gS~cI =nUvN_`m['Pۻ29k|$amO'} Uu=wsC&> endobj 222 0 obj << /Type /XObject /Subtype /Image /Width 782 /Height 498 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 241 0 R /Length 92392 /Filter /FlateDecode >> stream xTʺ7sܹͬY&ɘd 9sp9gs79g~%i:i0?,T*JU?}U -{"ʖ,׸5qk\j2r.2(NjJi*9Q5,hV]b$)1)U). +^/X)9)ծ&WUܶ`;6UxϮJpB,օJYLzU!f,6x]eъ7$q!$T+ސr I8$D!T I(XNubje%NV@ R˪NFD eՄQ Ԥ֖fjkmQVvjhWV{GN9u@5N$ꖨKnz ugH==CꕪOV}RAKT$ˀTR}R\~E+aI̪$/p/Dftl+xFs*QZC=XHM z )+Pٔk R3UVZu5\&J,jR|c;$s6ɴMGD2m|( 5V"ŦL=ٖP66 M\몦m%ƶ] :=!=xϮNY.8K9Fͤ"Xk&%HJeR"%zR%:4Ttf`I5)%v4aW`I/LC4Nhj0̱H f~YFW`1IԮZu %Ԭ&5$ͤTDhII$HJ(0iH#)(L2f$ l][IªWTc\ZO;X&= 1T2L"YL-`8)i Q0+aV@JjIIYq̚6,R/Z^Be_RLrԤȝe@ `GHj'Ǥ4(55(*cVRRoPR"%] JQ6(H C"aͻ)ܑA|֭e_dwur; g<}]k4JYi1@?e%`ܟݬTҬgK㔐IJM ͌&WN*IMK}`5 4RaARaDR=֦##'570iG^4I* Ju #6)*ND֗Kş.)~*,O\"$~ ;gT(

7ݕ".AVk㕱42 =O85-|C#X`ۓV?dzǁJhPQE VH@fD606O%F#t Ah[ Hסю0%­ܤ1IMq1;Ci4I `H@RlII䣹бu)H];+ (d:vY|+ڇ6ܩid`Waw+P{5OU/÷bwԁu񿝜n Y%utܖ-<3ZqYcFe+<<+):{:x7x[@!-g)L0hcxvN‰ȷƉ;;ň|$d6MI41w<3h%<%B=44@, wN^tUC$qI)+6droK Pӫ~iKY^yS_?Gfd)äBIvH 7R%g.n<I.y=v3}tv1_/r\|`N#4S f;6:*qu ;pb9mMl R[Ih]O )6κ$ೠf1H@Jܖ8j:)6eg|GՀZkz#GZ Oc7$6d٬, 1FT'`z:HC?M t2e7N6\2vUt̮? #@>r<5C 1|kؔ:PM8{`&Wbd0#@_YC{DOMY{M}`wCZNlQl0^oK>_JAԍvށmf1̰^nj.h!u`B /r |{owWosaGcy ]-{>%S@!5E*J .M-ꬑpQ" V`Rz3HJ^;@g\\LsؚA/5dNZщQBd6Qvkm̌&O0v9]]T=DJ׫[9XK\!S/H;]fŖZ )j!RiSԿȦm ARy#?RރkBV#U&#zVKih:i."QTh9֮Zi^IhHܽS^V* XK"ܾk$vx{zIDi[6,R*͉mM;2=>wqKqV,S_>Ge*J ՉP]B?%)", K%vX_^ &y*dmxN/dj"*jj*b-&Ƞۮ_DiNkmU5!R?-Hk~&"x*45mK5)1 MiFHI·V Hknz ̪ zu+-?޾L4u+Φ'M܂2h"0CM4MgcQGu}[.i>!%a̛W|aCf"ͼ{A"$¤ԊO=40ܬtf:}IlhFy 1&H*pm߲y  (D(J)B WH9&@ekhgv h %djH nꠅgPLcSus9uxٍݭ`9ϫ.΍WӅC 6Y٬'$jvS/I[+qggwC@JaeFq!wō4x>fߗ0xoW|L)"W[9ZE0EԆfG| ȄƆj$ڨ]paҝ[W>z4TUQu7*Jr3SUUQK1i(xh\2 /5 B&sâ&C}ݝPސyzX|/Ɩ #SA%#9G> qs;UzsBT z+J@q#D&=V5fu|4ɟ6ۺ RK6^;٬K0afWOsEI܁NVd rtd}CB3ld4RGggd4d{A'AXg@?XAҘC;I2գ3257Lڨ %8utR{od0` tgGGw߀wOWGwWj{:սs &A2߿w+aUO f]zhKYAsE5gȉdN ~薾z%)=wO7<ﺫ xׁ676 #υlt{%G2D>E)ZF#t4TaVFG&@m#`Jtyǟ1P#h"%6Ćn)z|"fAq֧tHO }Ƶ+SSo_? ?qixIgS0LL"SvdҏPvp3:qG 3l'3Iw%Y jLV|uȾ N 8xΔ[ת{y_d$HXcg fHȚ#Cl$iiܔ4J?6))SE~n[NԙfڷгyDܿS.1h A7]:(LMXc9dԨ68b'E|HP2%Q(bॿC+V5KN=h^ں$wj-av:d>-@H:JS`1F07 w]g.rҏa?)i$0GE@m<6QkAit 8g'T4HP!JiJGU)S*P_{j"ar 4):A@jD@HXFKK@#ոFBpVkʔHw(E\o%Z~-7]+2E>Ԩ J.R+rTF*|K<+JҊPtjRO(_*TChTn.Y )Q! jhVmӡ{Jœ&gڪiת-R/U[Km%ڨiU{O[P;ӆhKh4/G8:W/iB& Lh D JjHMh ;'͟S'4t_]>HD D# H# a@gGT}pD qPjd$=HcIi K2Ԥ Ȥi4 sv 46TՈK[\|kPq 4f8a:|d0@BeD2"3ҸБy~8^4Cr?+2IM'P-:Bv-5qElQ"t$NSH$|FKCxbMhQf5b1:L*c"cr8W'rY*foҡ^errS3]QMW.^v8I*QiclH66rO1ZŷTMF9V֢=5ݧanﬡ( ]~T3MfI-D>F!iΎBPSʢ s22Ӏr Z{{rx}G:8ǠȤ5:4oҩxN_jYpBN(߶ӏS#@>jLCM:H+yM ^UCVzG#ZFb3":L"n`Ɇ./!VW`XUQS^vɆWwIg%MLaF mhҟ17Z;d}B'Mƺc!T8H'.UEH/Ñ!h棑$4RF?#!*+)jkkkaeh  O' Jh'BdPɲn"Ãnݠ*'1Kfb1d1CӈQ:s*pRoqN:jB QD G'S xP Eh=i"-V#hp4t4#ҨĠQR +S?#ỻ::dŋ-Yt˖b).$Y@\b9|UV+5NJbӘG&}Iϡ:I>BP9m WzY.i  i!"P+;F4nD2C)6k_ooA}}} 3stq5kZ[[Z׬Zbec^,Q 6$08vIQ(YRFJSF"eYi9g2)*ɊsZrXN{c(J,} h2ijz!hoUkdLLďkf_W2$['JIQzooߎ;ǣ&f/^fQ#jbFne ki/< '>%,$co^γ68:yRn''M|bgpII QS&X,{MuM&ݩͷ\,ﯹI; S.2516#YbbluX lԒ[ݭ|d?UР/=F+=N Խ\րV4[GIxʋ 6^wKNP$?[dϐpfDc&y6Z;FF=vm4}`t*?42hDhd.x)/S.Cx~ds Xvt+(͢W? ,1"AݷOPސ_gi>Z]W5:*R 9S!Rjij7_zc' Ϡ7@imnV&%({.Q@ 'QBVbQ*** HT`0)ICgq\?CN|rW,,7|F~vY[{IXa Mr蔪r"90) .}gL"%&>elMK&sTuH;5j>hp Ck<@ ؝P:Rjfcg=qpV?k31$ӨTv=JBJz!Jf jt ESUmYKlp8W Z*%&¤T]['fSQ Ҝ6%ȿK6O' R&0";BEá|.DR^$,4kˑ.Eb6&T-c~&dhFx )A#YDJf83ٌAJ<)i5Aop!]|b2]*zyҭXAyT5^8E 4„3o0@M$0(ODJ5H:Aի/]L%nXnu]ʤbC]r`QrDnd3h4dTI~+46*N6vv8GG4Ƭ^;0wQkb.rڹ~娷$ ů6N262<7K-{0|6Y ]Ȼ1bƵ[,su}(wΜlcimn3uJ|vgó*n#f}[مaNv;pSPJ)|kN_:w{CXq0l<W*zwӕ{\(=Lޡ, #L VY[o$YiMV$TG^X99oJOZY&zO>qROLKI'aN^ D@ oNsZ{zڝJv"W׌gadzx.'J(r'w~c49VD~6zÎۆ۟YzXa'-s%sM6y+yqٹ:}&tH ʴ1cVYJa'-EQ+rkĴ3lg캟k+[ |?O[ܜc\==nD!98b-6>k&SC~l%SҡB'({s2dd%+#|3jfEqn@q\}^[V-_l)!5v}WmK=]pe] L_wZv[4Uq_/Wεv@Y|cxr_xSE̺y5kź-?ک+;#f~;N !!_42i0ГY, xfal,A}i.d2 .q{7bo32~֔`9{:^AgP⶞/y67eQWsrپ^v;Q$=j7Poj< &)Hbl%UTU]"*N/z{f=a+Yz'<]޳xto;^R5L fԨ D-B>qͪ̚?{5kZ`4Y@A#n- 4|%x\Q@ CJTBvgic+@N=9jD?jw>L1>B2ϻ.zK80)15qߖ3\|i #mwv`"RpޮE-QyV·R|1+!%1lS-*AW)}%8:Syyg( AZ?ˢym׼D6:vyӂ+By0) ȯ6bNɃE6FV0)}@J>ATdniXM]5ERgu3q5W[[G9'넫,Wl-vgHG:Қ6LJU:vNHE d>e<j:ȀHi#6GPPQtsW!}4 )1)oRĸKV_H#WGjӕ8#.> /Jw˞\Fa2X.~Xb2ϸY{Vàs!P`ҪIvLYxwӱ<\iok5 p{{kltKwuPnSc~] gG>zs4 p!mGœt %!]5JTz@tow.kEiT"=,'kSzR2񸔎$2 1R yO$V Rhm-95(t#L6RȒ唋9§ N<ά  !kt20h6v J}?CN cԔnsp6!H̥||qgOᴘmƀ -w'g4j/ dzϻ|W_?|ӹ^͓HnQ0)%$/^'f$s9R@H}R! + A,Z*3zC4PrG5EEDX\o6ƊI%*=;-n )%qyYg'{E"g~hi-šH#Z)k߶جD._5T锛E\8/6u:֙ћGRY`]Ģ28bɻpu_bLJoh0 &%<`L9\L# -`b};h!T qd2!;X*/缏8e9 UAR%^A+<7T/I1>ѐ[nto;jqL:݉GV=}d(:f>l1lơy-ްR -ϼ8v*~ywtu0)R풔!41O+ujFyXT'>s,3wũ$&B+] $]%euZDJ:B ]D&FεU+LEf&G#j&%pu'Kd . #  :SlcJ#"?PHď߃ B^}`V!Q߿Z[WwnʦTv(dʑ8AmJԤ&S$叵)Is*r >rUr1 %]Ǝr3HDJay+ϴˬrbwKIYĖS"QEK3KBͺW`0/Oí(Oi6aVȑ)ѠW=eu16Ux'<7ZIBJG҉79lR}RF%^۾3J~l{L]'Wi'@3}a"C :|JM8d6 `6Zze?,0|,|lgOh`'nYoYKٜSVp b]a$yh.17^ϥ2# ~/5ig|f?(X\(T4J"DsaRbp/x9R>mu\̡JHIB%"iA,V.8搕C`ϾNWk\f S}d >_`%dbHH k+C:K©_V+iäƒ'2aR葀NgF8OJ. 5U2lr>RLfV[):1m}}|'Xq `R ldpʃaR>&q\Og&u\~v,4vڗDjNawb MιĻEnSNvMY\dp) ~yyGDχW2QBe0)U ̶YRDI<~8Zgx,rǫ:LX|y6dZp>7D77zL;FF6=ZU?.CRv.ĘCë@f:e`v46$ 9%eqj+qӯf1ʷNC/+Mt!x2$d;iޛjM E r)G^ݚq8.5w")I]e^k&W8:-xSÒRägT#`R: }]ci-OWXlb5,:oi'Nq,iWS9گ@&f U$~静SX w"땎ow_f4xDvUQ$-}h˂G>5LJ|XHK]o]:nHoZmeԱ%V#}:5~s,]}ug K, RTCLX-p~&SyH_\M!cäfl27ZFf9.m)90s8RNHnc:\)  &\(ĀHP STxTzVk_"6LJ$D"&G6Rf|G0IHĪfV#b%rƮy<~3⢸-~yvj%5jLH)ɧJH yJX9n' 48*;O)|傻ĄsflUUqeP5&H-7zd};acK2ނ2J Ѥ묬Σ0'l'}Sd<^ne9fr>l5xrZ)_YNsO=3]f_%ǬIf@JPg]HQd7 y}5r0roAwg-`wJ6GxmdI>eW4zvѬhlBI7e/+xЈ58@ LJLn͇-NBSDJ172#8/S 0ء_a>-X=)qd?AKvvDeW P[J;kY, ]imy 39ա5 Lrys[ETWWMJl׽&Reh7zSoyC%{7Vd-ΠQ3o.0=2E"%e6*G%PH QᬾGNE)e+ IrVy_Fi\(*)M0:ATF\djam1sۍ3@KJ2$ccʙzR Ii1%xY6s5&Hyo +>N&&SV &<D] f88: DI rMw5gxx1u)Io?nWz*^Dn_fi[n|)ao2^X`k焳ܗJ^2DJipJHiM$GIu KHͥeka8(&O\ ~2ᗚQ{p[ό.+#H`]2[[ۜ]OuiV-c#t9F\'xT܀sR4Wl7FQy:LJ< )p@7l{/`7Gӆn54H[ai/M? : ӹʄ<gYݮv; ooسKM-lfsm2}ȫ39N8[PDNABKi+唿g2eO\*+lr462z(- oup99)+ByE _bv1 aRJfypqqݞL~#m>nIha xTpeoO-v0m(YLJY,IPgP*ėyJ.' &N0OH)ΣäD9r!RzVrljkbnZmkeź,<𴐦>>K&&sZ Zh,&._;gO9Vqd Vkk:ZYV8;$ z< g>*k-%mn5ddO>vvv636\pj(nܢ|rIIҹc¤:wۛa\->t猏% %|- )uZKXɳV3z;.]n5wuO,))ől~lEދ8[".[wwl*OBw )bnfB|R Zi(UHb0$2葙ZJG"8S!:8F4}?&;}- Ƞ-3AGVHٗ<-B!cw4NMo4-~՜LC-zz*Yd>T4n@5 U@Z@HuHGBGD:B`h4 :F7"M15/d*4f^&gqRsuM(M985s NJ*zn%}h'uJPh!J=G)ДC$DkUNjxP1(ۨNz"tyQ&BZ""rhH~JN{ke%(OE"I &%*ZR888 褿IGzԛ[s ڋV +bIÿ rL:t ,:Z yE?'"%rN׸Tj3iH T\|;X[CC%7q&_f qe>"R񺟮 *@E=ZZ\;eZuT;H.  qKҸ:8t'}S M)1 6_ S#-|C2Td R!PdX.GqHR4Ƹ~,8Pv'CLt`2)^٨\thxҝ:J#EhdFN! JIؔ@e93f5HŧSe60/q[ׯh/ <-d|ҙt(jFVKQ3 AeЯ>|PZT` @j@ 7΃|Z?Azn¼aEңEyi+rs*QYn^XQZ2^Ƭ~2$X r e̗H= \Z鉩v"(wT JwRESp#n!m0 ԢʲR٣J _ʊ ղP/Be*-7@ u0@wXi 0wǩ*-@_u&%@etM#qc2RN0h޼|5cNzjI~߾|H5 RT.0YvWEaҫ0X#3ԍ#PzO$%CE8J$@u'%,*@Rg˒NI a$G&ege)-%9#-XW{LegWx9$"ê*/ r!&O۰٩)ez "1tqjR,= Jjc?GHͬ!CPZ c[PԍU'e)=>8?0H)S4gnLJLLOKMOOHOHʂozqr#Qd~_۞$s*>,}mmm!KT2Qs Z|ņu}TTRQ:<˪+׬^S9N ~YOA(9tdTuoi}fi;4^VWEGz{A^F+Dj4 (/g88hb0eX!n[btΣ gz炍r~K[+ʸz#(0Dʻ)벵(:"-?#'4DԧOoᯈIkkK ԆFJl-M6jK*߄UbZ[wt9:0p_kW?Śr`4,ih`lLq1WLH"B .-D%ɦPZe#`0)L1 I`0\ &6c0i`;ql)))999yyyEEE𒛛\7[PPPSSbd# Xab:`w1`Ly 7 v=ˆbTFj 86[<5dUE9苟?x`(?R*[}"47wHe7A ĵuu MMMi V-Vew5kbLo]א*":.%uv\"pY JFQi ,8|PNQy[g_{W?ށ@EeǏ[xIVFVRR KC`Ie=L}iVv \'־C};ٻWI#t:wͰ_#?f&E|iIYwnx>#%`{n`Rb@̌K J*W߿{F(<~8nz4d/`:SNp%KYwlD"1؅I;FaR&E7wO?w}GGYG-{-u9͍Mu ZIaBK{ٿ_~owKyF\0a1\.)R?c**0 /z TRT`9Rz<`1 )`0`0LC,M{fzr [;{s<< ugnݼeޜ(IIh`IsņGuGf.88)~! *=&= }8d}NPЍ@N?k %$mt$^z ]z/111w 5HSnI9/#7opp)';= @]BCC.]VXP\zunN~T&5(idr2ٍr TMY t[1/"9Qag  oſj=a&cl?oo}z}}yRR)cJJ@]>Lk,ut`b |qM Y\Hʦ$ )`>c0L4OIH'LdhJJUUUu퀔bVRbR$"uaK͛7o@J-q mÇ!8qDJJ (D*J yq  Xb?ԩSƀ@I*O & RI#iooaf]]U~mk[ +|1>`RK BpQXXJ(zea5WS 4Ҍ׭Y[?у7=h gJOCIJFڍ)uPwAbomk)PͻGsP4&냻Iq1b| P|Wd "ST&X3)%D)LNJ *R)Sn]!P)oOCЩd ~SKL<1Ԕ4])A޿w{K,&jd( ݎ?v'8Vlt:S*SDZTV*g4!@`I"Oޗ vqﶳ62nԪ3jf_+`066qw_<=h'1IIJ@ik .CEX[dSH_`dΫ"%S<`c00gL>d8B__ePJH+=,X!x<B.lgX"H+)QEJh݀::]]]gx\,BF[e'2I{ Ez䖖5wttNiPPA7j<==Sl+uWwxn&B#!U#(IM%f$o]]~i~^O'[Dsb*BWʻlXee%}:;B1.F9!%TSi)֮^֕SʖQV$Qc{rV*"RnH12)6Womoi!li(_ƊD|G3.EsZNk^_ZXV\t30ۯ^={C }#. P$X+)i%I"ǐS!0ixS#oD6|' ^QF= y@|VO?=rsbsCd^ K&xǏ,}qm%Ңn`'_ߴy &oQcEYIsSF 461/Zɛ[Unmo[TYő}ۻ[=ۻ݅d1 qݸƅ Hpwwgqœ v0!ɐt=7oU,[6dcfyگi_sڃvR<6}ATkT^<ա8!,iGB[%ݵxuxB$8[n|W`?~{#7 tQ_*++{=a5YI\^'O\Ϟ>!HFDlGP}D;)I{O޷oi.-n›7ollle: zhB O:U\E_tCF j$IIiI pL 1 w%n.z߽m$<٣Ȩ0 $b'?#%MMM%4*ZfȎa7_MH)I &N\a<&9]U򈉪Q\u!:)V$a+&}R깍fWmZjg俢&G̽Nr6.ݴ.}iȕI+{Pw-7n\\dgm yJJR , ) K}%N%9IQ|898`э^>UAmei-GD%9'"p9qITp0[ޙ2"VY[[س{L+GNMM=|п bRϤ$L)IpJJ%DR>?R۷,۶mmmmmmImm mmo|)+S(*APRc#a]<6e"RѧtKxVV+:PҐ~PRz uңGLLp88)I&>x6صi.HIɳg|p=i+#;)yذaCJJJ]]k5 cSSS& wϞMԢE~'qA[k:RMjiLzWoHfe\GB_bxfCҲզvA~|-P4fId%K#Ҋ+ꩅbTR0{5qiKN2Џ~(՛!16B}J6zh0oAc} !7MM 9FV)Z3?fe^/tWwW7g`!f̴T;Z߲uwq)IXFRd$%)IJ0]qԄz,nܢBD,̥2,NBAtL"`RbcvO8wAa\_`\tڵ@80JP(/Gqyy@3½-)u7yLJ ӦMKhij&')S=ҕ˗lP8|R'O<~W^An۶ 2{ až9s)Au)|-FnI@Vӫ; HIM0%Ƽx+_!*!CuC5 EJLj+ NQI9)^1n~}%<ܿKmH#,^a7܄= )% H"IbL@J0jz?Ç+++;".ۉe!% Wu'R5RI׿{W_}OvyA&ظsٳgCo޼DYYYEE͛7' whT*IQֺp@JPW_巎CH>Գ*2"8[tgWXc1xN0^PI5gcN~C]R3+<@Y+ /#)3v= XoRkU~bf{8 s ^NJIwLJ¶.;O%)FyyD-n\z"ڥׯY[t}"%$ ) C  ХͿ癘ee\QcRw߅}ˡ8 _Ȼl mijAڄA.yX]JbXڣz؍o65ھhk F'Cc_m9\]m%c#)t `S/jkMQQRnx|Ğ%fooIIIƅ~PGI0>x'28axC9tt]Y)ݻ4;Kz>.iC9!6Kᓔ@oݐ&{ )z]鎔:to۟'8n޴&۷8p=~xܹ8ݻwʔ)I_Vxɓؾ %۷1 Ai>/WRHΫ$ܛ$f$`VpY ,JrR_ )Xx)?^_ .bvgRch8o5hLJoNTy[w빍:JPZ-)h?? JbGxC89)%FC%'ZZ9, `ZOiJ !$uSe$&9v` y{yX%%qRvϦVީ'pfOy{{ì W8vޒ%F M T(ؙD@7R_~ץy)[vZ[W|775m=PQ!)Q@etwNbo/t)u"%A 3 mVOI%K(){=>|,p@sd K!u6g*,GM^C;Yt%%.ݑ7N# vK()*;rpRyoO;pLOHJG\ ,_*<)<_xPx.HTL7z3@׮^uw,;W;v¸hdHI> 8C JiB7.N)Ib97gu牉 LKLf$EEfe T\k{}@R &)ϯ9C`@0 ]0q\xTnmc<2v|t&9~{d?Oiک3)Ј Dtԙx=z^I~58H釶mm&L7)A5k֋/^46ij:3)F}+/Go##g,aw \AR|4Gt.!%!?~W()=:۩RЩz=a5䱣oz[ BI[ z}S~fI֒%K⪫׮]k`` ^LMM2e!@qq1dsqvU&+)pzCII---ߪNM+E\/:[p9?QD?̷<&ȼTOF:i2玝 ???(۳wܘ,ؕԳOJIId~m$+jjj9Θ$:g.U{;;K sD%$[LNL7ru!9n?e5^2X۷m \t.:{4$ZXX@NH)JauAmFgy>uC*⛗RCқ7g/lK~@WdMMg{3' څ_w56_@aF""d$%r/4JM^)=]%ML`O ` J[~DWZeZ}SkffV__Nƽ8SCCˎ6II޷~?%`Q83)sW\uVYhmڵk~A?I}]]I߀d HLH;OLHyGM3& Ui𳾅g )0w (:[G'Kjirws;p MIIiΝ&G4x^b|,E%a!)~6"D_\*gsӊ9arN~96< ̷} .,& )^8& wnqHqrr M~T6O7i#x:K}0J) sgu&%71&2Q(q|nJ22~E'0;3:191;1`>Gh k!IIS]`4e#\qÆqFcttMb0rd»G6ꍚ%LPQ\+-p"Ե2Cn^ILz՝GWVزw1c a D%$[V55@K2鴾P1S0i-]@bQȇlhҖO0yDuܰj9{tR/"~|QyW\]!Ϟ~'_Ekmkճg?amaeI)9 $#E3)].#)MJ{ nקHB6oA#K 읂>D:AE ހ ,8v'|㊀::4yL:U?=gkI 0AEZ[>kݰtH=I7\h%.˿J)I;anu%` =@ }ngJ p&,& UUUUVVVTT^B'Oz=!z;;Ϡ^tAo\FotB'O0.X`1\zU.yT Ǟg{wۦEk֬9|ѣG׭[/!-X6ׄz1p#ݮy!WP/r'RziXh`~ :yƒ{ܝ}ܝsʝs̝h$OO[w½ 8,7& J:s̍7^zʕ/;w&*(*۰!CH q1zn#9HJIhS9~[k+O8z>EYtՊ|ͷ~{B" GiěׯoyҰ?n/$JY>% "޲<{SM掩5lۘ6)irm⼪5eϸ/Ot%x`DMVqߪ/)1[SR_>II"JJmW"L=yQ6I:\Ekbb1GSLZ/C#{ñӎn'}Jmy4;\LRvs :yRyB}@U p5g^NcG׮YD971현(O?IE IA6\]sFk$I .:'1.`)*2\$&')tvӎN9l=i㶛Zo:jD~?f Sܺ9VFjr|L4e>9"fGE-+ TY~?/+ &g#U I<>EgSIE-+']%DU"}#jЇѽb>տ>pp*>^4 He`9!!Q_*2p$oį!jЇs--r22S~2&^4H\~e:!! x.۟DTzX[Oa$Ѐ"?F!D!B"DHg3 u$w}V!B2S<AJDJY="$o:S<AJd$%|G7 BzwEZuR")}WGDB"$N*ϭ3E {mwOsX,:MnŃB-EhDJL:)ꆔ)Zb4}&B!D+Փ($h/Bki"S+%7펻{0Aw<<=rdiO3<\]>Zpwp.#m%I)_Cx?[ l.gO>i[AZ[@Q"**9ՙB!~&J@qqQNBsu;,sQRNJgT^NǏ1Jjs={ &Eo0Ǐ|* F @q}FbШ~^<[7bիǃB^^tE~+8mYYpVc}=|O{o n|R C,㓼#G5/${%Љ' 맍PT )*kwlem~7*'@Ӌؙ1v7,\}c\<3nkne~5"^diu󺥅GP"*ݜ_Lcey%]~9.2C]oYݰ2p?&G wf:*ԕI*j#nC?Rjj2qB^1A]{ď=2d|-iۋ~Br wkkRkд!b>wW_ǭ5K?\Ii᲋TI֗M]CHwrZuhDàXl㦦eSŧ$n~+&+((i_VGuftŸ=[c܁Z4dv%عRTWPk..W!MFЛ: )5 a%1T5Fg(im R!,IcA<7Peɑ k*RZ_|x7aޖAmmuEҞæ[j+y˵T?:?FAc{OwQd=m'˒Bdc}m5_2˗/h()%Warub8|^AǮj I\ꐎVT[s?kb"{0Z\&[.mz/JOMҴy[]'b%Y4~oVoݝ\n1?}2IsC:vJFmwX Bs,{m{ʀj(O(}!|IvM=#=CaKD<]iǃ>Ɗ*++{NRڂEb *GlTjFǒ\\u핷3YżCT6R13`r$Ɏ5Kt>Q `&0R6<A˙\^1LLFS=lb7]̧'^9jW$iz{,ӵ"Gkcڑq/7Wp䐱;#jv)Un%wtw qp_<y}#%QG d8WўSP"#Ns3T*2蜠:M(0"o' ֟` #!6rKY.k[4A;&eDݶr<]H=RӤ].5_Ziޖ֡bR"8tOw!Cz=f@R5s,Tu SOmW>bfeS"~N5^TTWHq;l6!֣%l5lnWUd7"ͰeT/55h,YhyjI M d#%;3|>Kݙxğ<1=nvEAJKwhT*{ktHHROVԃ% h~Vڹqӭ(|<9Շa3/>Cd],3a= -Oz?FOkdl[<[m$8m:mx +j'%aul|fαGY#+LF. ?Յ=ASMMIwojcwq m2|RuY7Z{EHZVO5MNEq%pm~-6x6+K4zㅎVn M{GwKOg%2R1큔xخ!\g۠[X>v6d.fg$,0w-Zr=lSRz#%K-0b_-33rhQ.wyMzJf2(Tߦ'yTU]MJJ5U*AIɧPREN2:YO_u 4j>myk3PR.j~t9/575v )q5FXZZZ.OTZ$)i#)Icԥ=BZ]>+F#Dې')@<3n^ftYPi[tPO`p0┲Q]֦q[so;JL%7H\Uo΢Ժ%%N8>\MfK!^&+as e]1~)rÏ,5/IJ4O%{xJ6FЁfñK+j*j2!I3"Ͳm#Hw:6z!gY'0 7czF5'{gHfos/k ,_+{8:[Ίv1w ^W+, 3=-dY;uBFzJb~6gR\sىvtNlguF/eJcGQ9af\]2B*G:)ZNU#%Y?DaʕFJ$I \GecG*)U?4zEeU-$͵AAJULUU}:~Ya.$ $]HWmg1:X!+eϊNIJ))a\ )ii)},]x!ϷB* uHZ%IIN-)uX.]O2hSMŶ3&^̬yEyQ+޴6 }#h^6$]M#rsg}t:][C}꤉𲶶v1Y)uttm ~9ʳPѹs|ڢ[l)nNS)1QM{RbScNsr:Is]4wS~Cw ybjL>A?CmZkqC}J2dx/k^p8%HWݽ7#ձ+sv]bfDwĘ3g-=xqUѩSv5~!/tM:bEoe;2Qbn^vc&<]ӀL iy󆭃{t&u(ORkgzXeq;`lwt)DQE`s9h}J]}+$7f OP!lqK[K'e6BbRzT@]jXY~7ѪZMm1_mιI)Ϊ-4J3ƺZ:S֜ RR]]]pPPLtOϟGGEA > R(~$d[tNJwN꛴a.ueDad ]xC_ޏ8h!1nyFw0Qq6i6oːs=bcU)-[qjÒ["w5QCQiB>D6z()^Z3b( 8XQԞu*Z8l@FFWȌw9P[SOK]p[Ca-vMHIƚ$)JJ}-6JJWLh BGl dwGLJUB]I}W{Q҃^|i7AJ#>0Sχ>6؂^w: ?1$}JkB>PJ]wg0]z9>pZ2>[n-IJ])u+& RxhʺGI5P@Jf/ p` 2ɼ 'G4H!¹`\Ɔzdo vj3 t:KIJ uSu2[r) G"%(#B9,&A!B^0t$zz϶U_䅔R AGJo޼w-9)I ~UWTW1\p#C g 7q<6\7+~>>>Vn%|Wa>pyYA>ϝ[APT(- Pj ˊ l:Z9 *5/I|Lm!#ger>|r$ņ?I ^Bz#%&-?359>&JnŃB~Lt- qu"%VynO PRUI`)ݠ+8Dn!}J&RyZ!H'$D!bЧҼ?R %a"%"DHvuHU[gP@$(1)usM"Oe"%VynI%hюND!B"H %"D!D!B"WI)_-5C#?p%0ccb1b2,ǤsE0i .͠bPixbR),LL*Y|2#? EɣS ˠqᘃC#4)-R蘕Fr2)TeɨđtrfZ^Vz3xF*(W\Sr0%xrv( ERQe&f$f&fJLIJF#h<HfP%Ƨ'X)!iLOF(a<6*G@UdJLdr,(x(U("#CA  ! X$>"$!<$>"SH|xp\xp< ąXPhHqBɹ$hD %A?B8'$l&l&܂O2ش?)`v(t„Nb ).V4A<S":1 gH8b3gxC'XtvEYd 90iSpY= &vtzylOEHZdHzؠ nnp*+]l8jP)_ECMj2q'!QtDjFqZXTE.nF,ƍ8&Y =&*7 *J\L(*H (Nh!d 6v F&bVHg0Q;RHׁ<)_T/ Aq]%EJUXZL"T%*=T%u]I㺇\źuT@r UU hÊG+; ZGjUuPZITW=ZpI]MI=nl|$&PcI=4 z"VPOZ DOz*ԳQ=ksT/?P/_%zBk^ .ah۷m@A:ַ:%6' C"gQx!MM PIf0OwO }2F$EfϨ o;؜qnl07:*:H ܂ n_G Sը0&2gPuw`;DT$,І&6cFfEV[dą6>*/C%IӂP@X,!rN& 0"A #t&%O3y#!B"Dܧ~*aǖMD"D #OR"DAJD 74ď"H!B)DRD!B)D腔$Wߖ%5`z%u$(lPB3 %BD"aI@DJ&Emen\/BQKIP"~DHiuLѫR&#OsA!gKN!BR"00ĕBJ[A˜eצ0#* $EEYzs"gv9mOG")13OׅB8AJ"HD @Rv,5?˨ѣ((O՟h`I[o9Czm>j:+)2ƎP5fIQuv]? ,D!@" [UWR:` h܊{Kqto # );hS2&hg B-<ƯJBN $ћʄN6MnICmђ>\(*D!@" 0) l֮MXeG)EGG(*hwXGw+F`ٓ9T6{*w8OsHSM)qbR S-kAkF[eѱ,C EÐ5()xt҂=Y0i8NON3PTjzyjOSP;EΤDݢ taa5n! .L}.Q$\$S%U{S~u|#M ajWlz3 [܀;k E3Ts!9yO'&L퟉aQgg| JNwKqK)[r5+ivW}2|E1X(CˌG(cը6jƳttT^3 PZ}N₢B4R:Vih疪z>NI:jCM_EWI'yUv6 ]"a*Ӻc9sxdF29IV.u~RpXrKJ>6)x}RH97_KizEۑq|0ScaG*)Mq34P"od#ꕔ %" >(T1}Ze)QkU2TT>pHC6l:\_cRU{2nq8tܭy;W~~H 3:wX:nڱw]1jõĤS+5913X%d agܸQV:=]էpY'TTWԟ:e8;JW]EMm⃷Y5FgP2X}{OT^3S\Faӱ:zzuFjHVcuݹ..#)*^z9ٮSf4%0l{)^HJ]޵HGc(i}Y̞όDCwԿ7ЉYsVKC4g\gzԤ:}CIFvխ=N3@{h.@MrhCpMҧD?@Myy 7.&j.IIw\3㞑Zݭq0jK6ѩ~{)CF`͊RgRbD>DfJ/P\z"M.9f̅I*}67nR42NJtrĉz{sY%%Z.=,4^Uuɀ@Õ%)d@r9'Zq9"S2l8:/d/Na2Oi틋Fmdoy--\-t5͡4cTϥPRR@RC~cIoO0[RR}2\2rG kVR"MT %%{I ~åtGISuxJtR~!CձDzv\5L %̄Qki`IRaG@Oq>a0jg@לVSmc͊4R6Vxm|ǂigbMRo4:i'bhĽo{GP=g3x\Fecc LaCd[LQ/SHێAv1U%[)S^6r{k}wy ku4tf97GWjo4/tni]ua~~U$.KE- 9rVt ®zK/v!*7nH9fȅVȏ&cژ1SɓunH '{n'M9^&au*%=?7'%nzq{CЅ$S֮1袛({@䢔f4Vb_}p1tiya/J^]tU' 墮1g Oб<,zJTbF~ e͕ A'MTbnYdǹjChKz # ,J JJ)~S`QRu5ٞ:qC2ҕd.$%fgl\Fgן)Rkxuc=oFoWDz};A5"RU2^+L߼o!M5(b.' We\Uxq!ܳwo%hî$5S'2t(]kxgRp9˴_KD =ͤbuif2Rs0$ۢ4ryp: C 7}1B})oO2mU\I+%;KT;K1\wOTUu8*`Ɯ7Q5:lU]lSc9 isLbKRb-isHk0lY ۑ(T4!s`Fho|6E͍?NGWGGOg}XHJUz$aw1>jdѣ-0\ ]Tle uJJ]ͬ3)`)bX4tޕ ᲏.B>ڰA@J %$H!BRRfܒR 5}o $InAwAiT3-z %k{߸V.eyWu;) 8^"^qug]>7Hɧhoeagmaoc #rr#֖Ύ)"DH~H ,E A/uv\.Rt[mgڟ+}9 J8 ֝JZ~etmwĚA~=wi_@ / /ͽn.`v.g=ɅI)/+c/7kkW ggD!! ѻ_6Vg|kqPc3v"8.*$LH6L!hņA#ŇCƇCBC͍6z¡3@;v کbt<~x?)XGŻ+{ب2Ȩҡۣ?;#L 0E > "€‡>F CTO|20l CGo_}놔:o0A`w8JJ"4B1 xŨ% AI⢻%{e򻠲{eUݫ(WY~AUPUV<=UW=<UjjQԃjP55P5456!1,j?Ð̙ `:! 7G>}a*i |y!LFTKk!U̅) sķT)%(HRDL[SMl. @Ånbl8Ѽoh 3CT{~lHc 13qB}-gcvnH)[NJ)5tz @LJ"AH)1#~lutdl{#w$谅._4u@oϥAzQ'D շaƮf;[ 'B>`IJ-'/7_P.Zjṁ|0]c(_n,dk*-צ☫m>I67G?e@ϥLfGoaq̰Yf. c*9o,|cX~j0B1`秵ZvѪ$MWA\U|xb \xFwxBoc>cRJJ>y9C_mecRں3U|+;4$nYwn˵鳣IGTkJ׬b6|9odݴγB~^~.SX\PjmFQ)8A爿Q/z4W_~"|>"8(%=Rgݡ1dt7\@p>;ztշ7HI7 ̲4rƯ"1M`MJmO5JK]!q!AzT^SB)aK)Dl!;4$lNr6yu~a)mSJ|-+!-#ƻPY]O5웚jjDH$ lUUͣy/% mh>z~B"^[-:+ˮgw}jd@%)H\;U} ut?=}]ۦ}O潏&;Fn&#z/TPR|͓yowh YaݖkgG/6!$ $oݷK)KYSRR pM %-Jv*Kiշ,(&1AHWz@dRG(妬rcѱk|֡kF}O7Yk,=z3yl˴ƃL5ǿ|/jv۴üy/&` ?{vK N%'nuZmыM=ub׶C?n)unu)] bADR8ARy@nIMH)=$agov3uY9weد~E=秕Vo#[>د7[b'yiM8{w%ߘURl>t#[J1 4sݎUwn˵鳣Ik]xDZ6t]}ӯ}SR⣸dBoOZ`/Ax*EhK.Tה"[$%L j6uק b7z8GmtVrVWz~,Eeԏ-"K׫mnƵO v6{0}41rMo`>w"Mڷ[Jp*<[CcHhz -j?\ώ^')+Oޱp'Hď=J;p y玲7Ire=seKuZJ{s c bC^Z/A! &"h UVѼ\JG t%! ,ɥTG:8.9l\>c˻~f^?cMy;v|۳簐mnƥOwʏOmahcxvD/ X}1WÑ-Ш ge*^wn˵鳣_<&j)?9~$R'I)Iߨ{\Jz`rI)mMOS.2R,dG# lUUͣy/QK2GOԿeBR߫G˶5>86N(>9wO֝W_#0χms3-qIjv۴üdHHgj12yh+%8huݡ1dX -j?\ώ^' ޯOVIT5'O߿Ok)}ϤB&!᥸p-AUh F$Aสy4%}}__M&U)'[IQ8J/7Es+{eo<{2"1դ'a37ۯ{_ܘ h+e&l'\jk3N̳kn=q>1ΦNw֜nvK) tYrmwh YZwn˵鳣dUTEmVSJsfޯQBJqgΩFd,+L6p\Ua4%R%ѣ7ogOyח y9 j@;ٷOEδc>;:(g™}aƌ}IskֺKjv$f_7Syv-{0}޹cI 3?q9-eA1/8;4,^uZmы4]ڔ"O=A8*@vp\mѼ&}g=zZ~!EU(>wP\(#M֎0wvK N%gI`twh r6yu~^){.-Umh Z6}ҭO/:w'mq@,ۘ3Xaxh, <~ݡ1d -j?\ώ^'+sE<S:Pm[JoxM\w[f NOș :z`~!+V-rfytY|WMJa]m& )ɷJ (%{!%mB^CnpmV<:}vb?i+sRÃC`*"re*Pz:0$ge&)JJ) xRJFL[SMl. @Ånbl8Ѽoh 3CT{~lHc 13qB}-grRRl9J O8@3s~`wh 6{ݖkgG/|~zRՓǏgS|AKrGnRJPTJɌe##fѽ'Q fGD-th蠩z{. e֋:)0&zUਾ}/ 67v6zfhd\~>;?-oGw ǙJ4}17;4LXwn˵鳣 L["!6:6zՃ{w޻XWs ޿{uщBKrNRccϷ'ܤ}}<2~-f1f4dWK ]ϲ0vaF,sc]ŀRRP.Zjṁ|0]c[J?.g>iܨ%CB} )e^}{bVޗ:!xm\l|tPPhTwԾ^qq{[D$E66Ϣujh|\L>_meʀ>K?3az]2+WU%r߮ gY0#.}abJ O J RB)i+:;tY|M)%&ĭ^1/uBڸ蠠Ш}Hm?lE/Q}=xظظЩ˙;?(%CXXY0:'4ה upyVH4ϋbeؘ>1 J5%`rQJxB`tc!J Rҷ-dZH Z3DmKI7 ̲4rƯ"1M`MJPJxB`PJ(%JIc) '!jRK\)rU},A_ iQ7ޅj|M7`rQJxB`t)r2(%JIߤT+/SIF*QjR*#g]n 9J)q!"nHr?%[R"C))3J gRjR  k)șٷGF) A)PJ(.}J O J RB)i,%!JL\mVtPJ(%R!m [t R=}%˺m`f{OG) A)PJ(%-T)7mHIxSyM馀&)%= l0(%NƖ3t 䖣n 3|J o L["I67G?e@ϥLfGoaq̰Yf. c*9o,|cX~j0B1`'D RB)t)mJ)!.6!n}GFE~G0ODB\lޞa,zQV?N\ΌU A) A)PJ(Ne;D^Yg +KfC焆׶t~_; fyQ LY8'F#busA ) A)PJ(%$ZmRʔR*OIÄ̲4rƯ"1M`MJjSo9J)...((=)-)AuȠPJ(%}RQR1937.%m>mܐ~]Fɧ^ Ԯ;wϿϗ/1^z[7yDPJ(%)GwR쒻!uPJbғǏܿ{֍71z{wo?|p_ J RB)i'%KoHn '[R}n4h{@J/_RB)PJ T)RHQ1aS'a KIQCn9JI )@)ORzR)A)PJ&Ҋ"5)U@ȷ'AU5)3-R\v̕s/ *hp76zRMQRf?{S5GXz%+ǦPM\J*+;SM,#H/{a( e;3ZmPZeYppQ.J R7)$Cț@U5)3=SyR˺z矮],-)``*?5ؠrߞ/ 0 [%RǥaÈŤ+o߾[z)4/|55DU)>ޔZ­U4TC5lMIϰ{ZƯd(%J RzOĆIIV)5ґC sk%o6t00 u4sJ泧O/;%O>aXRHÇ%5jXJRRJj`U&ݺusL>#*{jʋMՅ?t>;2ǖtLt!~k%%!|>. ":l\gה5HItfc/k?rfErGa#&[[YM: jR*O?n9 (%J RRrȼYJ MR7r˔R*+KKݽuSCOK%G./.<~W޿ۉaX?PX'k{ݽ{*++[HV.V#4ֲR؍NpDT 5}m禲kO6\xRΉtˬsPm*QlW\چiX O@|BJGoIJU֛$%Ft~ bC V3ɪs6~kjwZX8bXij~Z`k’n62%d Რ?V_<RR (%JIϤk)qR*.$gJJtd;7[ǡO5-C=N**8z/_޿{?aXq)ݾ}RCC,eT*) G^ejllOF;<̭r]鶉~bW7 Nϵ}LB6af΁#Loʗ*JrW4kSJKwfkYSK·7VJtju+%7*sڢM#_[U]SC#gy]$J RB)i+%! QF%F5%RJP(PWsF}8hӢ R"G.+*87(n3:}o+~uU)e(%GRy{BC-nPUUE: =zk.-,Mb^m7m)l5L|r-Ss5|7{5Ue{Y-\瀏퐄aa Hͥ􆡴 )*+?qÆ 2ք2Gvu&+S]EF{*%8A)PJe7 B~ \UI Z3]%r)+T 9sx}m: U :{89rYQ|)\3|ʞru~^5c1窄g}&_8wԥkZmS.08JI+)h< %BDzwt?{Ez%n.Wjʒ N̖_S$qS_}2)_h\BF}LQRJuStNԠRBJ30T _!*YR)%e\J'$rLr*͌t-+"j3}񣬆/n[J Yk=7(ʭP햒w"fo&:iNu[vpqt3ԈC2RJЫ&d+?u@)PJ(%R'K)SZJJ)Cd4ը$P6+k`3'#6IN&mI;j_pO, >ǯr㿙@& J-JTRVJu 5-"!ʿ*~@P(~xttƍZX޺uÇwtUPJ(%J _R(T9dն)5)%.QZ/HZڬQ RZ**ŅO<}"ٳxw~{dq<kk0(%J Rҭ+JH)A"హ첢%y9 c€Ȑk)J$>%DRYy~QJ(%FwHuC)PJeį(dU%9_ "^LZYW-``J5,]\}ҡ?mMܰ&&A`(E)i)ke}ύ?~G)PJ(%vRROR7N);|c CLB)PJJ RzK!WwHD).Kyq!AD)PJJIyA)PJz&"VJ 2k)3Oշ )"(%J RB)RLJd,:*J R*T%*+0̉yY< Xa7BP qsUT$(BHIutB)PJ-k&b&C-ə~R:?W[)A61} %ZRhɮǴ XBj>j m"(K)%%\.D."L@vD)u\J/^#x|x=$ԫV\PR RB)PJKU rXd6m")Q bhFi2@}@vD)u\JϞ={鯿`ҝ;wTW j:yNTԴ@xr7׼^Rpaꕜ#M&2ΣgP.9$]H-&SS9St>|FGRRB)PJZH"䔑!W* ȴWJjAaQ %ɝEO6K(C 'Ch(%ͥښjixvEr3ڵkh=BJamߞDJMG'J oR*(+k!%L+)3D36w0JHIBKIO6K(C '8Ch(KիW`M[<{t!*5 Μ9ֹ[?2#*;%,^G k7w/UlI)IgdX][r[D%z iuކ#FOqpmDZL*g=8dc#6Kv6cHmRͤKg=$$g av:_Tv de,˄W}F6:3lD]J}WPJ(%JTJV_QF\}J)ALꮞU1Fa i2@}1R긔H&ߺKFE&դ'On߾.{'ۍۚW]-Z<_~GR$kKĵU-M,R_J{A\%aH[JҪk sEXSG9RiTRhjXu#,a$?%TLw$HJ0uOrXZz+֏1Yz~\9>&@"^acB~\b?+)[הYJJ Rүm !׽AU%9_KS.RJI}jR[_Ͷ 0(q%i-y,=ec7POh me;ҳ7IIKT]P ӓZPTՅO{b%簐lyH*wp}YQ4{v\JQ ҼYJ"e̵9+/p~ N^%EF3cK..Ea@XI^Sn羕CnWoz.% RB)陔 J 4RA9"'3b"׬ܺiR/;-gGf(*T¯4©mH 0~Q0J7 |2@}1R) &^zĉ]vApyK7Ep%5TINǎk]8)i6&b[*Ww55sp^n3}1J\q6z[|o64F_MoZRLk/uV7Yb(q%%)GO=PO46вQJP(J-ާA`NBA)PJ(%RK]\e}|6aD%0FQe󺸟}oE[7+t%FPu͎i2@}B1R\JAJ/_eCא/egg_xRB)PJ(/Ңm7g?ڞ\tpo*GKO%*URb6I HA=Ĭ03J\MIZKKDO1m7PO76вQJHi]MLZ"{=|ٳg_bD"QU` eդⲲ#`85U=E)u$RPJ(%~'0mKox,i3?y#gV\QXݙ-}92՝'u+1Fk(Ii[);2` e(ch-;q)aPJ(%J J:}FG.<QIiuշ Rʃh_Գ* fa|Qb%)3PO`Ch(%T[SU#{kWRHRB)MJy%KڋJ)նR)RALa|QFJ]}i XBj>a1RB)0(%>(K)jE)TJa c 6&ROJKqLMcPk `GJ A)G)u\JϾ^}ӱ VE%l$~O۶T, 5P\k m_SjGGRzRB)PJ(%J鍯 ]t*+ mlJ~'}^}2@}t1R\JpnDUKQJ(%J ԹR\R~BURACRRAQkb6Pwғ:&dP⌡ `GJ ѡG'J gR*+/XJŅLT!SV Q Yba4XGanN߼q~, 5Pk me;PJ~E)u$RPJ(%~'0mK Z3k) rRʅ;SYlta`QX%:v}c?3` eLch-;djE2>a2_d_a ʣ_r/M#ɣG5W?n=SOPJ]"J R7)岋 4R9դTBJcQNM?t``Vttن ;o<XJ4ffdNpCFUh$zn) XBj?@vD)u>OU_f|gƧ/Nq v<}XcޓGؿ TPJ(%J Jk=wEDFFƮ_P_x1RBJ>UI+E-AWKe:ū? 'zɶ?n{vZVIPJ(%J ;)]t*%% 5E6}V XBj~Jch-;q))$ǒuM^ yqsɏ6?@PJ(%JoYYRJd)%ew ]b0rP)Q5y苷:.ɀ%ǭ46вQJ.%S.Sqg_'"sZPU)qv,9|nfi#48hIw6v A|m(JP<~Bruǒ> lVoՂ#lqZ/SuW+nf y=M>_|;W+ 1h&pK"yIQ4+*~f1[KB1_'XKp9WVv~v&~;F)R*YI`+`gph/rXb<ڒ).ʡ(RB)PJ(NTJ ~b4!],g=}e;2@Pch\JR)9y/)j~۷C[z享bqqTYőE6 %E_^a-$l`vlۡbA1vc~(y+reIY'>tǰATxy=/o}A~|=&&t"Tk5mbo+;g9dPTs^OskFKԼ?|DL&G )RB)PJ(%IBR3pTHXڬ[`P߄CAͥԎ(ΑRϧ /"iQK)آuhʳN'F%.L T򚒴$y5^_SҙJJU? rF>revH۩;3/H[`^` ߎ0%$mk=byvn砯|Dk w MR*xP= 8 V+ + %3Oʵa+D(%J R)+T^SR^vJI]=ځcQJZI RdvS_.G^f33zB!Og ռdDLu/=ZR[KU"LRr}' ~sP[KIY7q&nXqJ(oP6fOl@/ /  L>gÐIv dlܫ?7R[)q2M#֊J&Kja3wWHg]n6l5շȫ9դT]w])d+Xl04pdѸHʔXy[`PB CuD)i.%8VK+zݽ|"zꋧ>{v,S>}W3=s\Ude,c's!f3;fI#ᓝhFn`6rDW1XNO(jSMl3؊Ut{ç18a\U)&QF㳎jgoccgm?be֏O()kxRRr?g cNnmgbme.`4%ji?j+T+Jio9n5n[A)uN(%JIߤtUș~.%J)ALꮞ 1IK^Oq, 5P?2@vD)i!Ǐ%bH"7gy/Uk_?^_\믋]23[0]MRL" RB)~ӶLc)AKrtFRr[l08pxѷIs8.%GCRjGGRǥ$>X/>wI}ύw\?*2kר/qA TPJ(%Jt``o#(s6;.΀%GCh(K*[&T/%RRJYd+ֱ[dolAͤM\Gqv, 5Pb mPGRaHPA%I"  Q  BJŁURj::PJ(%}RVY~R!gzIv`ao mv΀%Cms)#JI+)ɿm" N 8Gӏz|ѣ)zy ˿_hj=#J RB)NJlJIMJA RD~QT&B[HJ RB)u.HJ6 FQLugmgPBMs)#JI )=| aoXo%a#%"8iKHIutB)PJ-XJВR,2픒z8 `ƮO轝K(C  16вQJZII"x!8K[I&5]\" ʈ#xe}O]"{+;$r\JиXt7KA\aMY{xŜeGg/Tagl9|c#iXk+{ [+)]- Mwwud %Ԥ,dobN=[q7QrMt2QnNUonhw,90nhޅoL(T >; I/(RB)LJ׳J4R^9/TR]52ҵ<RJJB@E"Uq"5,V0IuA$H ~!Z fhL>A>JP_^}DܒZX;T$bP2ʹ(ԡqtnW1}x2Ek(Tpx.z QY6T{ +L؂-,lU SCC#Os6}p5m¾Q^dSG8g)3ͨ_.?l̶lRJњT,\~}5ua[ہ L[=qQqB JBa@-κj:$Y2}p/Dܛ[_ װ3{ A R)}lRbp;,|&%E`d7baҤ=im>t@GKZJ]Rꄔn>}F:Ta&wm "_uHNp7No\B4H/̉9XlfK)-x#)UH,M^ej,H]`~4mu+cmEiȮ}vI,c57kXoa{yI_8enOI+++ER*DIa%Q45fF{ER*7_\W)V װS?Ys6H R)zMJg{UJ -`dఝ4џ<5 6jA&QԳAJRb)EcX[*%2IKB)]^-WAn] ݖB p"ƨW -FRJw7ŏ dW푿FSJJѳiNU# S+=R*anVRo{vٻ8:CGml8BꗪDNRӞz#R2p9^!XTWuBh!H nH㓒zUJąt 0Pn$NOdhvcρQZJ]R긔 T][]!Tt# ր&|/< ~ N(%~W(ǘEG\W ZJgzdO#)wOR#;Ưl[RJEY$Ok* #Դ/DE+E}x eP d IQctTG*jDR.- G[R"VB[on w,dQmF:LԘz}RVV'F^ "qr@<(=}3[C)5Ý5tV [#.t*}{H R)@J ҉ڪ ?$̻$Ĥ;w(({ٷ7rnxRƢh=8{G#*1Kk+Y촔k ndU[爐ʊVT2yrQ[JVŪԪl_Ka>Ugkp 9155lC;PL«JUvQ$NDaȍYR oXmա v0'yKo "a*^t ڪS4ƅJhMzʤ%qHJQxUrBǻM %H RE)[6隔چKFca?e =̽q6 IAfhQ j7R) W‹;B7&ܦay #`ϾRy[W0_G)H0Joh1eEڳcxUeEGWN"G3Bk&FYFđ;˥0VtpKbQ/rYfIw ײEĪa h9mqaVX&%^izC'a٣yh͜d)6UR.'==*o00=vԵl, 4IKr oPŨQsVo^hr_[UB6NO3jmn)H#R_'H q;Am)&3|6EJl8]Rpɨ/GZ6d捳HNOv 2CkZP@@RH鼐I_}i" Î.."޷?͇|r!).-O RpRjR)>2)BvGTO)0R",짽l7nF¨d 3Fۨ=>i)ua HRtTMro .7Â0,Ftl},]°{|w")/O; %H R5)倔@J2Rݎ{,@Ĥ`^  &AJ )ݺ R)@J TJj i-4g0r;i?yrZmԂ <>jR@RR`LzVrZ@J {UJ4 P2Yn$Nr'O 2CkZP@/-. )uBJn YQ؃ 1imE|H oRn~ĥtݕ,6o#Mhhv=́ K6H RbW@J %H R@Jp R)W:JFkpHmԮ1QR:#u'*J!{x })@J=+%7S_:dg`d<> 6jA:Qei)ua HRyCxR>6H R5)gp vW"n Xo#Mhhv@'-. )@J8R3g@J >6ݼRo %HפԻWtYOm-w%5öF& 4CkZP@g0q)5ݸQ[Y^SV"ZML߼9󯿶|O<+7oP^ٳ(56W|H ξ@J Kh+l Xm#Mhhv-`i)ua H3R~D&>$~-۷o߼I{6ׯW|] >)=AJ %G+A .@tW[3@FhhvM i)ua HR*.?^+@>px:H R)z~J,\X3!XlȣmNt&-. )u\Jڑ۷}57ܼ>m$[a8zر^%?z{nkͧn+8̩P؁PW{NTt{k qئbKMqܒN,Ӝ`TdlҊ:4zHC S23{m4R)@J 0RեJ:^8C/Əl`hv ϯR@RgtDEiUiL޾ a IffZss~s39ԗ/>oq +Ëu71A+HQnL6Ҋ;gY_3_. 6jAQR@R't]޼Jklܜ4| &Y[[/\?xI'Pt.nhΌ\D6\[<@CzTxǽM$]#s7YK"YIRZDOcaɒ8|;SP\At036wCER*ZoCCC{:u ,J<]SmrF~^-B+y׺:h06NM'/io5fz;5̄?lZiI %H R)zXJU.W~v1yNx=r^|*5hmY%UfCy^oiAJPAK8H~/o'@aH+,ȣl&; 盘&sDH>DbYZRMvt}(*_t2}y R)@J%%J/KIɥj+p>S_0?3Fۨ>JR@Rǥt՚t˗߾}>}g}47/urrn]>ɓݽΝ` e>#Ø|v1chv4"iJ_܍49uQ3ߔ-XinZ$Q{c(5-'ԟ{Y9$SYn\F 6Fd^jhlql=A%D qqN:3 O@p.(j/k]u0:΁ ^ꈭeŸr (&cKX1;fqfhjL?̖H R)]>М/.P#mԮ>EZJ]R꾔?U']gs{޼龜?|ߢ;ʍ^c6zU$%q袟o"'tgF+%MGZ:s몎 @J %H KATW(iz|&$K?3Fۨ>?KK AJҵ5e%yTÑkn^gmm-H -cǎ}BŋħO>xh yFm-Ӹ))nԈs"eӹ΅ind`1%hp4$mW=u0x`j c {i:vRp u'qטx:VR*{S7YFS+ ^jvYZZC-o$~[\+)M n:MCca߅+ %H R)zTJWjLC]}>UIh/}9:{hpkX1rDc~<]݅G ?;"9©,f)N)UYxwU2jb#j2,g,λjŔqDs~f akK58/O9zsn^G^I YgCAR*M&fd j# +H ݓT?(秄\`2tQV%7^$%4 :J/@#F+b+X I O'awpr_. %H Rq)Q>~rJD/ؗdG`hvUOR@Rǥtڕ2A:Ku/B>o~k1cZ.]uW^}lǝ;Rix csuH ̶fN+|\3#Xn~Ċ d5و9[m R #%k=Ve610a&D܀e#t4ZF6,u+\gMp+v):z:&~qr/B!#{~JDбWяERjYLO8}%QcEOwZ1ڂIZ&?uWR R)@J YJ?u % /ȗdG 0CkZP@Gi)ua HRzTPҹsGѣk˗?G^2͛7okVݻgv|DU R)@J WJq7unC0%mPw2F-]} i)ua HR:wnӧ!a7oN|HΝ}}'bRП>ybAS˺\)@J %R/I)4H_)ե<ܽ{ϱgm?OMvmٳ _7{W… ijԕQHJGz8zAJ %H vHiK?%CV(p>/ԏl`hv@g0q)]r؃ %H RhK_\ ^!5F-}@i)ua H /FH R)}RgV(p>}_* 6jA>ԅ H ؃GH R)znH R)RnS*9]:J^8AϗdG 0CkZP R@Rǥtrcυ|A5H R)ҩHi]b)%~uSRYXOJ2EohV D}|́ H&|cPJ@J %H KRRxmJixRRsW x4<պ9R)A@J %xAJ{ғ'Eu\J2J)))Ʃ'l%"7CkZP+>n)uBJ*e<cOe1H R)zSJ>B_:w+RZƋ:+dʙitKG/#?@RǥtRE1W( CǰfPZ["_SQ̿xR)@Jٷv1})0q,DQ$H R)@J OYJ.VJ21ZI$&*@>p*)@J %?JJ𐠘ĄDĄZʇBP3@J$+.qK2(&J㶶JIwH R)sDJ _^^pR^VRRjxZI%Onsfe+M-R؁>_Gk֡&s.^kILXF #u)1Lȋ"hm5zCrۓ=j9?zWfhlaL=Dݹ0ҽL5,J=4:#N KW1Zmn:#R)@JھͤG攞;l m>s\_]u1xxNIw̼;ZOS H# e<0_&rR }Nlm W!/~)uœ`{Uy9n9$Ya4QסxLh,+gQ,jlv4]j1;  )nR"87s-JDiR#gPTVKqvuCD2WSFz:Qa;X-|Q% V)pbDR h")ɿ=>wR)@J45)xB|NaissXn-Rzs7mb)9j"9׷sپ?RM)2S[OӂlB6YȤv)FF'7ېuK\f@N,H7Fvm<~֎ö$3Rr<+jzklE.ǏޕQ,\6UGI&%D9j&V=J~HJ)iks$ӛ{3i;mR}&Hx|)B&2*IlR<[g~$c"FZ]aٹG2Ԅ5#43èB 8_e]`[oKհڒ|W c;\ru,]DXile95}0&iUR≤3>=?AHv»RB,Ŀ #bSp.OtF~dɒBZ"}⨭h33:=`0܃Ac#H R)z[+~%u1'afΞ5}zʻ_ߡ{Ow3-d{LɎnꟵW^)lH^n[)mRJ4 o(W&,i?=/v sڒ4FCG]l>b$R:=-dxWl+nRAvHJrEW`nhn2=::Rϳ!Z5wvrQ}Sj.r/uO %H R-)uo,j5#Rpׯ{zyw*Y\RHIC&MO(yebMWK%΁@J %HgD톔Pi9yYܬ _@˅o3Rn\J8ܷ)@J %H )K !_+NAQ>K&_+"o6i(R㟳K%KAJ %H k)RBN~Q> %H RT\>S{֍kO?~_ϟ?{_ztUY :(zNJgAJ %H R)}R*\ի'p򙹙S'0r2,ze3ϟ(+*`zDJ{h#|B>8AJ %H {R*9u%KIN(?QS]〔)ζc|_Ʈ^Ů\ܑcO`ׯc55EGT@J %H i%+RbeF,9~|Wo[+ vtTJW^ݻIO>Ktɿ={ɣRy+WDJu'+ tq䄣Q<cRR EMK#),=}55aTR)@J %R/IRZ!pēǏZ?~?~1 /_xjK͛׮]xBjSuu'jj**JjFNTajVHSRp)` vvqױwX:uJ",7R)@J O|NO֍kN׊wwO-)oBxΜJI</gۜR.^KIxj-uR㎅:)zRJLX,Ç _Ѵߍ.3wOw[J̰#%[YfL Ors/@J %H ҳg2s3Żx"=>}3%ĤO]zMrNEJEQg]}JK*՟}(+-&ڱ3RBL:y+)yǡ?N.I^$^^*+ˬ1$ 65m(y~*tZ$ǀ<>c!R^rCG$f.1eDK[ce0bf+l&uxaWJnRe-#l^bۆitz!3Շ3yhakc|z1Nc$fG%m§iiё:c)59 R)@J ޒR=q.76^v֭[wܹ-mK˗/]pܹ3O֞(+)).Ii- %Ē̂^MW{ כ%D'eh{#vvcS-"IV3]O&.7R-bCtznXJh;'fcEJ_h&a1f޶ ;'rք={Q )G&[m ;RSCuUAJ %H I^)Q2Cb"ⓓQ: ?r&W!ޥg\rnݼ}6]r-nݼ~tpBItM|?5U%>FIqAJҙvTUDb4ӧ KJC*PbqE`g٩m;5x6II z fWxn'/Rq`t5{UiI/1oN#%=^LE[ opPJjc3;Cރ#%;x@S3 b\@J %H J"'%ZVzh~LyYihP@^ֻ\iU.]DX~7nBA!j2 So‹JK Lq#{v:%%tl=?~}Ần g{Իg%qyه5sJ]Ԅg8[(qK Rbe&tm㴇mJiꪷrn!+-d"'=y2jd Ε3AMsZ:N,+I)7Uo#M\up @J %H=&/'"CCœ=UW!h?ŋ/7"!! !WR ON(U+19:Rl[P>^KuuXn;GI43`fH}4>+'k:%Z}a,BNea9Qn}c4~*`_ojRxS^!Os^(<9ySȺNAR$'Is)qa+kM1C5Go=.c'=pӌTR u"jڭKEf^쮉xH R)}R޷#a!KMov߭I >-͛ސ^a]͔23 3b7Zܢ jfG$&hvN7' zy5КgB3P1a4Fˉ8WOtmݙt~;D7EW g޼Z 5>~,@%&T@J %H oSr!>s\Ey~rۘ%ܧ?6K>PRQ>M|[vzMue]m-BS(mEF%eb&8Ԗo|;tw˦:ԓR:yRx#<=! %H Rd$ HJ/^@R*rkқi3LJ\lRV=E4ZEj)=\FJeaԜFF%mLJ>iOJ|l[YRtH R)zXJe[ͯF;M"q}GY > J>̾+sR~ܻ ŤfK沙F(Uh\h\ΣtK?yaOՁ@J %H R)})% 8_z:lٳ\Oq^7?o81eӦ{^x.PryUqa*/|>ARZ?†BHɧӊ8%H‹yE˥JtK>Ǣ(zH R)@J]9ƢP3*wyk7xw*y,Iԡ6 aHSRBVH R)@J]pfPZ3I q3oH|l6 R)@J %H7?V!!Wa̼촔ȈÇbJMMDL<w͛b">R)A@J %H R5,:53=X̡whe^]CC2ķ|O@J Nׁ@J %HD-H)R:R)@J +%.^Hv:%TR}XT})5 6avaOc7`JG(aMΓ %H Rԥ$w C-z[7PFv*ꃔ:!u 4kn)/c_)5͒bAJ %H RwIAnzbs *Jl z>HROVDmNn)@J %Hu-7-r+ HP)TVZq)kGJAJ'N;X@@J %H%V^J->HSӍ NґnxwzגbR/6)3%Εi%iH-%[9aaC=AIkXNٕ3\s˸$-UU2j_6cT- uRL(S %H R-)(S'S[]p :PΛ;=K muP8馬vCicð)u_J;tLJcJCېaS݆짳|Er H(>vZB)E,7[{DY nf#^vN)/{eM93rQ%jS%/rqc4ph=` ZͺmNIg>?Q [, 4^BRB9|R)@JIZ>y}q 衅 @Q#pvCY׎|ZaR긔Ξ-.P +'%8"]PaW$ 2NJ(obaW+'kĠ,}8)F#Ý4n9 D$%Nx4jr#ul-w#2Nrێh3~ʣlFAԢ:9t>{ 914Izef١ ]W+eRHg@J %H [R(ҵ'+3u3a,A-PzяP夤y3$nYa1=AJҙ,w ؋?(fIFV o;/pBvy?D]b<#[X;3N`nXIIHJKL6ķ$*һzYG2TEs N5!r| &yq\XJK5:IckmaN$]g*RH R)zVJJ\>D u#~han"FGRRm!cv`r aR긔NV(ƽ-p{-8zÚFjhZ Nx 08/>䨡(Se&<Ɖ_e7]2D[j@kױ,s۬UMFfJ"/?HxӒRFb1ـ[f]8[^7>)@J %҇S*OEQXJ KIaY)#wK_ZĤ6Sq)UW(ғpLr;Z8wG;)IzIf'whђ<utIԞ/gy _$Č0Iw!]D4U*"9G7ffinp&ODYz#l O}NXɻlˎ|TRe̛{=XWUR)@J )LqvBŋT};QzяP奤3FRD(h茛-0 a1=AJʲ"ER:H gn›EK(;r&4F;s9 O30kִl>;l +5-V1/|J \7DSF|Q6Qq^-We+^WGuOhTQ=&HdEH R)>ˋ䓝,V&z(uP8P^J 0G扼5as0lIv KHE/c0g)IZ7RkΔxѻ R)@J [iJ)x߿?rѢ.Ǣ#QzяP奤=0'fS1l2ðQ KXv ?f+* Vz2J;!/n ֿKOMH R)hRE Wa8<w}%N 57J^JaEGc0lHGcEH6faR긔* #SzxHxo-AON1E` {XXc=cr毇C#z7AJ %H [R*Q Rae;;9Pz3D^J<,0 Z#k}RǥT*cz7AJ %H kwRLDx麓ULR[]v$*+RcY kH2AJR)R4D&H R)zIJ4o\f^Jܱأэj{K'ˮ\x<& E{>HIJJ(*GnR)@J=*:y) FvAù(F)+)  AQYj}RW@0>ө=#@J %RHDJJ\vo *Jy{>HIMId0>w359R)@J@Jb̰r2Fw3*ƤޮRx2RL2RH %H KRRm+ݏoF}RǓGn@J %H>&P,1 b A0]yZNVFZ*0 R)zEJҿ]JǹD@J %RoKI:%Dt*+7AENR}!H ^pRܣ])!~W/_}JSS(׮4R$z>H H /8HgDR!gݽtƋTBQ&f}!H ^pR]}RrӒ/7^pL[/0a s/OULY؂}Gr,*ʊWaֺ -np*eȐ!!^}!H ^pROKMJ->HSӍ NK X]kx/=~rҷ>C~ئ~>/dnʢG@%Zƍ7a„'ƌ3,X0o޼)Sꃔ@Jz[7F6ݼpN2B)-=M*?lV~wqoVO0B5ߣ>S+3 TV #)ٍ3hΜ9EgF...ׯ9s&)k>H H /8HSj̹Z ) _/{h+hJĭ[o9z?ipW2$aРAIE֭Cknݺ5 00p…fTSSk>H H /8Ht=R:{d ˕JmsqdpuqW2%72ÛnJIgϞhѢӧn]̙3eʔ)EV{AJp@@@J %xAJ@JuՒKoctCM-GX㏿>&LprʡeVfd: < qhٲevvvӦMѺLSRfd:pYf\îu=zCCd{>jjj)))JP2}p}VzC fL}@.AqoWfd: 2e͚5߷oQƍ7mڄ~`ꃔ@JzZJRgb"Oם*Ґ/?_xն_?A~9:x;+A_~”[[]v$*+ rpp4iҜ9s-[j*/Ѳb pɒ%-B5k۫R"R) Rꡳo'e=xf/`>*}?S3—зZ>}TEQ}}էm+.BeQa éx5т6CeȐ!^}!H ^pRJ)O`dg ++4;rkT?o))),:%=52߶->ޫTBQYj}!H ^pR}K@fXYI GTjcRo)))=ە8<]|eQw“6Ro)))JKc H H /8H< R)0@J %H=)E/SY *UtN\)))dNPl %/JLB«/ݾukkWQ %8 B % U %B-35dfffu{AE *ʶ0AnpL7R>H H /8HgDFJ٩I99)=R\TViɗ/4^<K琗PY!H RU)qبHԣ DeQq#MM7`n7@eQq!H ^pRIIٷB:52,d$J/X%K AGeߐtS'>ioyoȅ"R)@J=.%;OI)^zE;g_tAOv@'2?))@J Rd{sJL&#Vz!أ{JfT5&c˯l[ΝӶFo߼A/)3\^JeAC@4QX\rA;#3DR)#"nzO餼b!pۢIҶfN^K<"K'4 G2e%v@'2cAJp@@@J %H=R:[Wu($!&=RHD=[TXJQa))ǎXʒ%Krc#:?~<9) K)&"tɪN=5HV;ʂ@J %B)brM;PJޙ}7u ֮7w(b =h5<)ᄈ hJhJhJhJs1%)汧?s3Ҵ#7O,~-BhJ8!"[7o_JxyӇ6.vn^#`1u]a[B<N8lY-֯۽uT/Ub^}yz6SLR[YZ]QfAI-XUVC4M M M MivS:oR+ڗVg&SҦ VV)W_LqN7% Akkpl")))).}o{t;`JK4%M M ?p4è|v~LҌ4%L 4%4%єrd7ҔMBS @SBSMiM81(6 %֦X 3$ZY2kIC¨VJ#ЪUVJfPRB'JN.!ZX'kb i@ZҢZ"P*1U "BWe O!ɩRȕ 2!G+Gg(yl KeAIޙ 8LQ6-lF * ,&=Y24~sc4N7r $\ZViu$,(kY@C-(kP_$$ 2+ !r2Dfo#~N67\%eҨLr*'|#E#d!{"ѧE/Q=DOHT?R쿓}YT'- %G(A=`$$5BQ1(V P$ĐEZShf٬:b&=r 4hęM M MzIʔF0T []fluY$&0{&6ⵛ6n:>Fर\=r݀3q {)a'DdI,x@ A<Bx8Ep(!#(I,tĢHg<ǀ6D+ew;ݞ#veg; tuuuuwwuC5=eO@o`_`o}P_p?7<C92?2 ͡A9d=-NuNYпFv:>MD?Jv[迱`D:xj1!.R1#981P1"blfb#I k0F40àєДД羅 3    CMޣ;Oϟ#(*\eTGAaL)4)g(:pUy,X ZNM]y 9B\ibZLSRg3JR:Ry 0kG} hzB2͠%"H%,Úg5*=4ՠ)!Mb=rArrطՏ%^|N[4<M8h#>w7Tܻ7<*7rȨ'W7RhЍ9>>:>6 坱o5uq>AO"/a:>kN9Go76eeHui'љ%_)!hZ]QL1iT'O=vkH_w;[cF! 'a$LU:4T B:@&7G*9Ա-ShJ-N@Zڲ7)¾L{e)}`2sL{E'-5 B JtSp̹Ot!H$ .dR2 k$a:Nsk}uQs$\'0kG2=gn֪LZ+/td^Ll(eP^@$8f4|Xț0O/l\~Ooj{u0m{գ`&>uۧ.O#˟5z<#`J氾 , /$l܃y׾WH<ƼW^u Vk}hػblkfp,hJ,<&D`.|7cI3IOٓX@N(8N'DLCB5{=1wfƜȈQb~c3~2 *ݔfIi\-|3sLrB~\[^TSY_Yvۑ'⑙H^Ԅ"AB&}MIrn61uƂxEg^S'xjL .\r]Qn}ź(Y!2z\0~}o |rw0D!r߼=.!W)3cAV0%c,ha&*7F@[w+pñp8#&ay̋/8$ҔsKrrjfLgF"Ff1ctdsܑs[F̉3dԕfOL<6[.:Ŝ~)*c4 YtvS{@ aEƒ kʊϔISrRN$ Mq{i!zi7/x]_}e/}Xv6[aiJ;n/_ԄISj -܃6|%;VYbtE=[_Ns+EgS4㡎P_AF$3KWڀ43Fr2`Ȗ\AdbNd usdٓ0VTW6k}`pjЉ8 !Wka1\p;l7ulu-P^A P`~SwRc\T NqU&C VB'6|e{(dUTN?u)hӚW65h00s>]zK@NR3TaYX '_uf3cASBdA0G# ;UUjB)GɑX Dd\pT"IfÔXB&E:vXg"s< aEs[=.q^zM͓Քn[̎[x0%q`r4%XK߾?OK,a×_b$weyԄ"ucuկ veJ+)\֐ϑ03"G"'ҭ΍lϋn[⮯h.ʧ^^^{r4T+),”\[)CNiJ:⽼:^ܔqú[|qf  B h'"ϝ*  ȣJUR 5cp[oMDB9f6hL) ZVYV u H)Z[E\f yOp,-\,9K~N<jբΞB܁\ʤрo$.vyrLuNmnXXP^|X :Ac(\v 8l:K6F2%yެSTؖ:pz a2H "ѮWJ+K$w JYw{1ߢ1./> stream xӱ iP{.I|/P?P7Hp/@=`^ [1 endstream endobj 223 0 obj << /Type /XObject /Subtype /Image /Width 216 /Height 418 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 242 0 R /Length 29880 /Filter /FlateDecode >> stream xgTSߺ=^9g($ ",(RE@ ذ!lXw{ 5DĶ?a%++J!BXXcaΙɜkx?O #Dh  ,9cfOҪꗠZY8`k@}(hQx5|P3'C`kؗDuKMɉK7B<RLj㑨,w;X-M <57򩩵Om"iX}20/l}_~ek뷡! }~|֏?A?A_D]{@ yw2{x졲e;/08EG|V?:?au#0aH #v:8c'PvZ8Yfz  @  KWucsiC۝.gӚbq؊! 6p؃將}O? r8p搃E@w"Q(/@ p!29.8!h8wm]<8l!%CT(o=:>)|X(aGYJwƽ$a{ZYCE9Aɨninb2X`pظ0]\G?[!y(r3wJr8)Q눇p$!Á.\}L:;Yu kZ簻CHdUd@8bT6 at9u5ՕeU8VBO͍,\T~pޘZ[J2QZR̞8ᰑUW\T t+`Qa>VCh!;20ќ3wv,)YP΁= ѨNYyy94~E0rȁ拂<1́Kak367'[L'9Yu5UFW琅V}|ǖٮ'>;ؘ>)RKotg"82zz`|=99NSDf1SKu(uFkgƕO*?}ŝ!'`+ Lg)Vd1!| 0Hr:;D矗-tXEq _VF q=@$=5L ,\)uxv˙;wx!$1}{n_LaN܋8EwOZ]_۠$-)"$K0X_UfZ e/ёo$Y,QYDQ= ۴]Hop'N}j~YPfM~j+faKkQtݝ@+nwt5 ktv:RPZ zf~Ȯ{V.[ʬ(r r㺵v6 _v2$+:2K!kSizNf*KȬ o^@;h\vĈv3V]H⼯iZՖƆKB@C#z|ӫ;͎|pvAp:憚JfV`#,Xh^ND>@~ϝ?piȥ"2+r'EEŁ+Y>nl}UihMo9sC\G]c{?3 eYVkkѴ鞩C_۟oМ{ro?>eg۾݌zR]t ~Mu֛_Rϼip=@O<=DE2mڸb钂 k--WhG?7!Χڍ.@N]Wh'.ץjw9cN^x eׂqH}mՋgtCK2q=ǦՌ ` E! #)ww !A q~i^co}͵5UO=Q^RYV (¾dVWG!vwXFFN7ќ4똮^t#Fs >~iJ{Փ֏9mjuȎC_.,Pg\i|3喋`wΔ-oڀ=IWݪ5}| jtm5W 'M , ?&âB-ad2ϞF;Bf.YCkG;(-rv 3!mgIm\tM( 2/z>=8cȫ cDgFo޼L(-,.ʲ"dEEl(s(Õ (,>CqήʃQ /U8^}jΙ&.GKh{nYh7m99W']; S&&]r#B,Bd6.67+.v;m5!DGj:df >V4A\lN"޼%xB>fXN=ݍ)`ln2=W/yd(v3t#'z09}(B[׮1+JD ~vjfzr`oIwq[:yhQcOٴi=074k>Em+ͦVߕvv{Q0^'NA%7۵qʏ<qA'طYuϞ=y \̇ݧTW ȧ {2u{e“!0CEY dI3`ڜQ^ K9trޝ{wJ!~챑2Ņ9'*ݵ|8T1Z[o" !$QQ,dgB kV)+2æF1LzpHO,-.Bf&|*<(b,,،lEQZ=cod<9}'?Qz{K܃RBx!Ygq{s.5٣W1b!7\i+O?:Vcm-mȓ#Afezn =>.{.GrÙ!17bxcs wѢ "dD}tAvIw!fG>bV[+pxqaM:=":28a '7@a([q!lBi9d OPl"N cE14I<,OR'J; 30 P4`Rr: 4(J11 82 SHM ?J; MR@(5-'K)0"u޳p7-LfC-$Z`/#׈0lR6~G-=ޟ2f?nFnF9aw'"i3 a-Xbm$sbsE+-g"1 nQ: GJԉ @/JpM"P{aG:{hRxK%FB0~'%Pe6)B,QRr(tgPhu Ek4ȿL (p[Qs<\ L}"E8iN8誾r(ʼn(6(v8cFQH!&E-߲J:{M=b-懰@8:J".6љ~ts"uK%rPpZly56Q46:S^/iEiN&#@U $"ġ_}Þ ~|uY B,58l(}AW^}mnu]Mu~tT DfVREU1Ng5J11_y45hz"7=:N]VTdt`kxL Ɂ+<̰E {;A`TJ ahC8|~nub1𪴆s̠~kgM3hNl6u.}}h}Uqd[h8$[o05ݭPf+mn) ߦ;cԃ ##fqrOZ@ᑁ7ԉaO?޵hќ+R"DtJiZA\%̇ M&Y~ HXr9s ,jwU]UV0DSn"qPzM +/!izNqȽÆZ+oV4I7OcM= wm]9)f'2!I]k:BM{2 ;vh+E@J[G٭Us\a^y}ޚb(:gO~24RG ̇5qX}eë*߲-lr.=dZӵÚ:utM1].gd2lI6l M5'P9S5eۂh_[RΛ89\a-$6 V0T'kjqk:ud5׶ҷH9hraM+t#$%?xOx F9 Jay[ |>̭rX֏NYVwW u`!ᩌFV=K-jpڑ9)f'c\gm{\̓\ CQ779gV3|xu>~ՍN-"Að'?4Gh@=?Om(}e>1Mze5qu<ͩ+Sjt2gւCqMMd=8E{˽rV}sMCsMY~U-k5檩0Jk9 fy7If[ ,{q3 Vu6,4.ӎ5mm]m%a+\|:YKn>e!:8{߳ !(_nĊUye|?4bˁ V}m0g3w. .KPiZ+ʨm?a5KS[o>b$ڦ+93uI+hc2}t؊z=ScoT٣K0j^a%ϪQ46zz];MgM4gOb[m ɳ<ؗDuP< ]PegU̅޿n$p+D}aQW`p/]s)F"6(尪tH1Bz(㈁Ds&tsm+aBxDw%|-{9V EhlR8X)R†O?xJrXGġ߿#'(HQ$?dJty0 7$_-} /_ eE1o 3!Ԍa}.Tjx)=/V.,'^C/j BJ-ݠ?A%J-oMZi=*s(@?8zYo/qi89(r~u7u=uaُ#gTS` 1:V(+.).tAzǢzъGfkN4F}D OAx2 D31FqiU͜i ZsP㗛ðA՟/eNXr.$j߳q+vaGcl4yeh`?ew ƹLOL}=mP̺ Vm5"pXuseѬ0yWXAްwh6 ]ӵ*mʂTs 8e{ 9…zK.ޜljg1\ri!=M2\0sع~&/cz%$h3 1$#[{ LV MPuz=PaH,f+H_c|tԱplGO54 BaS] ɶ*C#eݕ@=fdX eefU}k-[Dd҅Xw36_/PaWp"aVG+,-P-F+%#:}=7xF=h igmI Flm^f)9#ldZNDƟɽ$;G^IW73'k\o ^\Wn\Sl eL]5q:zJ+;<>kU΋8ϝ9e!BsYڪ5s%  {jq+ÖNFsZ] ^h)ml'iL>MKۮS=|cJIZmF֭`0`v'MV8[qbӭn_=hfS4ԩvR4si:ofaOkol;g51%  F m]]RQ֪:f 8]Tz]/\y@F݉Եɪ 0Q'//塯v9]}v/6lI#kkr:zOՐٍNzkif>%  F m[Sf/R}fOcas u?>@6K )׋5J9xl1$:0?#[LI i0T0Rz98 4}4ͤ Eeōlz;I(mW/׿ͿlM~7 ӻjy*_ .|1Ry쀍/G| NGvpk%ql fh^ws}w8QsW8|9vTG|>;n]:a:yo0,* )cs d~`I?R!+4瞉<}C%~) Vi>HC}tOmڷ~t,i1yB _ζ]0PaHO; [,0L3X VV2mii6OS'//~8>M@ųؠn }Paa a<~?q:ҩv{%͛2;N5l8zN!Sm:` օu @L*Ϻ8"퉛klht 8o?]cҖeFɫ"**L"k=3c2ss$?@̭jLMOVdAn?';UG]s˱̗u+ǎã. kh,|WU )[;a[]b)Yc] c#߹?ovͰ9CS}Nև~zkɰ.w0>fs**@9x`imԹ]3? Vz3"=97b zLk)dɆ 9ϣa/\?rn]HgIM|Ϣ~(uyU'4uy7!96I0)365~Ik[&3.|vkuWt&'P[wdE WOԘcܷ@T̝e=eTW,u\ui=O吻O1b"Z{7Dhk=DC9)D)ƛ FOQ`0`>CfO}M"6o])(͇ٙseaDv&v mƇuܕ.(PB^asE.s݆+!r;ksfZ*UKsm6]p-s׽/캴"t%늯爺=e} #Y]k#} :6X~ }=PFC8ṛgZnA _kfdxv/[hk3}zgLߖĹw]FsP^ ?砼~AI>瀻.r<~Ky%ܗ } C *8! T8ru!EVIa~nV._zڕK 0NHz{oE^$pX-¼[6]rqkVn*;‹½<|z:kwqY_cqgODNXZ"3/X._4CE^$q]WDda%25+{:+`0`6wHp/!\,゙y)E^9dJˡϾ2kygY{у&wzax}l4Lt LAק&ΞMv U3 fhM$O_sֿoU xFOzi}HW@/e sX>yu*fo_c뚉oj55V9z9[񇙽)$m=7 im:O=kov_@/rrxa}6ȚZӦiMQr{;&V6}Կl&>4v&iv?݆iE^sg>x+܎>g:mwaS6sk8f,r<4Ⴧ'Lԧjp/irGxQ]RrP}h䉚ӦN8y}"wӣN3}Lɪ[9O|F2^E<1UҜJ)wj68ٽxe蜄䜊/ƠJn3͑FͿou8,sڂ^ޣ#/r^LeT8쪫b q`~Lٮ?C宊2.T<cWuPcJE^p9]@wma,u ~#!‹½T`8DÞp{vrv=nۼq4Wey琉^ǖIb/ a\U!ep(q]޲iaxJ)-R˛6(벴Qܧ%q\@/rr\K@/2PjxV_"~g1 / @㐋N*rGxQ|rTϟKE^P?;‹HⰫY尼2jد=F`sD/ RE.Njs#(Ћ*sGxQIvP?;‹r~(C%z\b/ "N)9TsGxQIZ>^EF"Q/0b/w ˸P8"[9Tg%_ɋ]oP$1wzw݆ *H^˔A/‹)cvU3*`8U/!FC"{r")Ce^*)wz)%J^EdJs#( .(Օx_"~sa#|$ /㐋\CZsI#(Ћ$;s‹bCJsI#(Ћeez\R/ "a]ġ2=.)wzs>TbsGxQVU#B9,+gTlE^0a8ϡr=!>wzCz^|(H)a]V~@/騪(C㰌2Se1— ˸?<UKɡR])HAɡRKE^p9|ݧPHE^$pX.C^*!wz.UJs #(ЋP"J^ENs #(Ћ%Ss #( !w݆YQ尴Tϟ?E`sD/ RE.ay)"엘<1—r˸?<VJˡR=.!wzaexs #( .I*rGxQJ뺽^÷Rs\1@/sBٞ?Ld/"3qo^uUg}E=/wUn9 P%*w{Oi!(7]Ld|ǭ`xb#rQ#¹#(܋<*psGxQs(+ArXRRQ)%/rD0+%Er :;‹x/9Z Yi)î_ ]p^#Yfa?~|SCѹ#(֋$r~Nһl9ܿw''# }_~}񃜂N+E^d飇9Y69@W?D+#%ӧO?}[4p/8le#p~#V]uOWot#ʋ "_y}_oԏߡ[?wqᥰ!V}VFi1"1[;ۅty҂|t 宔2.8"尲aq1etī&\OVUt+J =~044$l 45^qڍ/:[Ugįoߠ!HxQ< (r(qm8lnjoŊgg$t%Wl0_s]n~'_|v>!VGL#Ղ&0:-|>PIP稖 C!(K!C)9|p;y~o%C5`ZTr.%Ԁ6nF#!@ 7wza lja8DǍ/4A;ʋGV Q4J(6nA#!$5lȟ;˸D< 8r;jA`}J\j)-|1PԔ!_/K!I\EXsHVh)a hp2D H%A)gQe %q2FNTT Q 'RBMah+p2D K:PSΆ;sX>RTTԂf) aKit8B"Ԕ28lf P 8b奩⧣0S-ȐlB ]D [A _G#!@}ԁr6X/N?}1PKOO5`Z19؜G(C⁳IPjpsGxyC\zl9w1r"Ź7S5`Z 9x!%t5l--ܚG(C⁆$5lȟ;˸Pa8t[ᰬ0|˸7~>9yZGw޼LMJ,ϭf/! P *CQhTfPBWS6·e@<Иu rWHx^*NxΎ# M;Soj|е0+Z-PMHPjP8w{0{~(C/_h(wƍ[nsrrrww9xce@|ΝPjP8w{aYpMojZ9؇zv~G(C{HPjpsGx!>ec?W /r%4D ?O#!@7ԁr6^FP|؂rXZ0?RhUUSBOP/e@<Гu C#zPNbyrSiA"yyȿ'_nUTԂS԰+t8B"&A)gC^ƅvDq8tQT U :B>A =G D A#!$5l8#<,*~>ՂOSB/Pîoe@< @M9X/9-uKO5 @-(L p2D H:PSΆ>^FPc}*:v$9k7hwe@p @M9v/24a{!|~sꩲWag)e@ @M92.T9/7"B9c/'**Uzr"5: &A)gClE^0Ɣ6"q{CGuA5cç)'.P^]M#!qC$5lȗ|˸NJ%s ^~UUռOQPO]ܢAԁr6E^$q(؈׮NA# M"uSRuS $QI#!qԁr6E^Dp(C~Қ5wml^:;'?^qbӝ;AP_AYWTNRip2D G:PSΆ¹#(܋xc pȉ7GX#zLHΆ# /CN'c]*{TyOtbs "Pė%A)gEyTQ]feq3=p2D l/ +CE8,i(ArÉ+]*{TxOp8 "Pė%A)gCl~/PĔ6phKlbUQ8CsH#!%{IPLj߯ G;wrYLjÝ*fNVl9FqxG(CK_v/24a=Ü,D<8r;jGnj )kRNlCp2D nN:PSΆ|! /Cn'q!f].Brʼnv*tGynje5GNlBp2D >ߝu C!(K6Cb0CNDV¢٩vT5vS[M^O˝u-WC^ƅ !X~y8CEAUUmԏ82<P HPjpsGxxffp"E;JsLW5C/eQ uEmy: 뻑Ԕ!6wh?zpvSj  /  k38LDd);TTg", .=O#!n$5lȗL˸P8vrST76NޡBuPѽ(eA5p2D >Íu&h"CC *Zzj~ԥA4t8B"߯ G;wra8LCoF8l2.T9r>eL8CetWY^d#?y uImy: 빑$~24^dh(Øڼ4D(霈l~ء2AUU<׏bH]D[uG(CHPj;‹bd`8D;qXXa'"_nXMܮ[UEM4$me0Pu]IPjِ/wqq3^!p8aʔݪ.j3P+ L+CERp!'"TaoW[6c~2?jq<G(Cڮ$\ sGx^* N`{9? U(U\hȳPP- 8Օu~eh8ڹ#C028,MOErʉlW[u>I`: kԔ!6wh?zPNb ƚ?hV6}(jv$G(C\IPO~24^dh()CvDV)Ve'Yߗ2m9: Ou!A)gCܥ^ƃ~9TC‹8džÿ۪LW⬦IK1HG[SB=Le@|wO qsGxQ\[TrGxQḊ!qQ|Sf#J^* oǔ7]xfأ<~z: vb\1eTsrJNDCCrkkkFOy9FfHsHx^* o0Fɺy2^sLNDr ѭk??#Vh#jK!!N& F2C9|p?햛֯x]6mްj}m?wyx֨2^9%6Y Z'.{[i=i{>^~ 228| у;~ҝ, }aBK.‹F,ٶrޜէ)[ _kBxm/Cn0:ݧd&% p~ իo=ϭ_G~+?`S( ꊴ[EߡwoJ3$DK /CNs~(]5oߟ3{th"rGxE<)wG· RUOz}U_WS ؗfggf~RZFx |#Q/_xQ!ѯc/q ~8?~M Ou‚d%EX/2sgoC]>. `.O6*),`2|`pZ l|/ggdgiu:ʅko!;‹½s+ð+k\׃p;(ujk[RkU\Pnu׺TDp_@YDא@ @¾֞{sc$!ʝyyf&KMt(߆B@^u[Jt3̄ʩ*Ȣoi:ɤeg:x.nƆ?SQގضmk7ҡoE\U^$}VQ:b߻ Lq>#ٖ"ą<"櫭;{\nSQtSuv~+~Yʛ^U%divjЬ  D{GSUqQN;j0ӳ<s[ ?{FP!4@'pݯЍ r}9nKs3n``J7\G--) uXx.i̦CF\OE=m>F x0(_U~J/ K˩''AQ50*L*탬D\DM8rPr\ ˳<s} |OEud;̟^/PZ,g3rS$ښcZM1(-ϥh/#!S\ Eˌ:L6J駢ޅOދޱdew|¯_b1 HXӶ" E 4SRIQ(/;vZ?G>Rw<sѠCPR++loX#-Cl rsSS2'}8S[w<siy0Ieg e)NLNOcgU&yFŰ\=.w>$̔Ы!A1גc2㲯'鉱qA{;y$j0Բx.°^n~ ul,䦧EE]?{$ZEoO~˂x.Sw<mu:?ł\!"Qwrqͅ] Y"?x[o!T1,krͅ] tX\WZ$'>~= : 0;%<ޭB: v"A~\5jĉ^6W[}H;܎S.:T.U+O{[P[qfv}g$K'?a;aH;܎S.'q `e@T,:™Ewn6E0ɓΝ9wfLCKL ChVTA]ZUj7U\_źAvw!;8I f|!uUqA2\4谂M7[Uibb  q8cvpx hXrą=>p G q9?VK}mڰ3 `8pڌ!W\o# }yf7Al ] [aKC*GD3O}K@mF#. Cd+Q`eh!۸]s]* '= q6au+]vE5|q*GQqb9A2\u[ĢZ*,F'aR=uq/ CH kE5T < WX@,  ۰갾\iI|873JVB0.PѰ: {|gXՊeM,  :鰮Leۿ433_ CK@mF#.91 +j B]J S w.u(kf~XS!Q۷Q$'S.#.Gx?1u(kҨü@דD9zA.?{:c^/]dUIo6דptt8qɓTiڴiSNΔ)SҤIp̈́ p1AZ^jfffڒ"YYYQ;666vdmmC\0524ÊI7k9nߣ HW9E= C+n4P\]]׮]qƭ[ڵk޽_}վ}v~zww+V,]"Txp y#&6h~HsO? `;|֭[z%Kp񞣾7Yo0:=w shϝCLw 2=!kso;c= 55XW9E=Mr|Txs}5է /СCǏ?yӧϝ;Ç!Ki&h}#=/B%FS3~m={y-Zvnnn`j* <[@xPﹼ<,L{A#vviÀ@WydX7;]<m|yFRa|TXS}Uks.]tѣlقTudF?... :8p[tǎ; ⋕+WܹA9N^q>ӐKc;4n'c=aXc/ >sV>z-uXWI7B~̙+W@ᑑ!Ɛ􍞞(\C%5cGC|Um n͚5 IE=z>s̔Lw i= V FM2-lO4~7j&<( ѽt\ѷ0O2.5Vh fϞ= Ax!zE!Nʼx񢗗j萞Ttdb &e˖ .R$ 7n>sLL6;e?$bU+] azV]$ۈVSO0GW9E+QaXCnD1]˗1c,F9k,0::ڵkq.Ce6\gsrr,¸I2[>s#[;؆Lpβ4mHe[2't\>aa\TX}”)S dǏcbbP#G:Tud=ۯ ; 77 thsAKOOǬ`رx1]uԲHG)3f4xi~ݺ'Z3"s槇24m}t\tȪCis}%.䠌61cFN!t gtx”td_ALа&XDASxL p_}Mzy[i9O>FD׺$A{?ͩ)[3!'5[W9E=5?̕102J?<~8C4=,F(҆  B ]%ttHϋP:T2\ڮ^|5K>$fxz{[hqűQn" }yS6m' s6>갶 !Xb>rHavzKh5֭yJUJk |5Ts"`ll,N8q#'''}Mx[[\u5~HDF7b|OAI}s A]fZ'sN?\YSk{=hǴP6aZJKC???0.P'{2=/BPpy:]vݖ>䓹sR`[_y7;^V[$ |c@8 ,=j{0fB)ti碍>ZZ&wz?,===+Fs8uFhgvލi-.Ex* -Z ۷?"#,=n3UD;;;}F6&AC۶D=_05"^3163`m),#` : #{ Rzy.9vD\^$d QnVyqaDy9A,2ICuEs]U+9 x9ϓjCMR%!=}y2Pe^5 :,.K$ SN",(@~mAnױߖ-uڴL'=KC>Asq/No~y=1;++---Bdg#3cB|_\]yͿ!uʔXu5{ݎ9tC ?&,cPb& l+k r/s͚ف@@-y;Y܎[.m7;>zi|>zV BD‚հAӧڕ +>=?'ﯫW޾ ˀ(l\4g1;(.F).+l:{17pw[8ݓbe+pk,KOXtgn6 :؟RΥP} . }5jԡPXbH1WI.,0F]R dV),(Y,:K[[m(edסp Ur]y:z1 :?*ݍ"]߱e :‚dAFdG QήCP7IЕRv ~/֕ ӽ8Xtbha~׊(aMmI$gNŽJ /׏dAFdG[( qӕ>~=].Ci6: 1 xrè-c\e.JUu^@dDvQAa+.ǭ](e{b]0݋S.ڋܬst 0C{7_pJRȎtdI+.muW[Q vᢔ]Jk\T˩C.ix=9ԧOo`ƍ٩1WuLIK0̩'?NQ$?ECnߋt.S\:a¦CQ 1&¹YY3555SvT\VV",dZՠtw-XR$ˀEPinV5}⢏?byd֟\ 2Ӄ.>P^JtY|O+Wέgx߅q۟ͅBPn~HcvJreoϯڡ'Kyϲ%zA <\s)uHI'D~J@btp΅OtaQ OOW՟"V<}uXb(>_/갣| \3=^|4g3iKu Pm߬wF{|袸$'`Žx.碏bQ@e3ga><|bD%?ZtP?}r# juq5\|yHCMs1<:׶Z?%96*2W-!Lդ H$T.]/-67nˊ/̞^OX^EPTVOQ|<DQ5y:x.Vg:,S:ʧ5:s@ GPR љKS$ŒR:|\^XRJ갸:>eGq}T e@Z7> stream xA 7=,I)g n5j_)mZD endstream endobj 238 0 obj << /D [236 0 R /XYZ 69.866 808.885 null] >> endobj 239 0 obj << /D [236 0 R /XYZ 301.443 470.274 null] >> endobj 240 0 obj << /D [236 0 R /XYZ 276.813 167.214 null] >> endobj 235 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F21 168 0 R >> /XObject << /Im7 222 0 R /Im8 223 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 245 0 obj << /Length 2270 /Filter /FlateDecode >> stream xڍXK6ϯ`N" >6b78"$!H_E魜4htwB_?r/ ,ν OS/+@U?=߽uuGfGӫ_l~{eg1HwL֝)9/0pPRl&fP4h8ҟڱxy7ϵ,ZT%a2szfn}]+xW/Fau cY7[}ZTSUnX&sq QIiq+v1eG$6ѹ8e7}U`+W׶u T/(?erɣ5ª})Pvp6;֎\[epXcoQg65z/v+BQR6X(ѥN-fC} uh>=ـ:~VeG΁x.pRY4wpIۛ Fn !MځgXye#]Ae=g^XJrLCɓB>t706h`k Vpox̽ ]d:N WZII>hYϤz0>(i?j)a p2^rNaXחgi;Guam-0lʦ?g줳B>,m5=O8aLH3%h`gRNXQE= T !$S4eަAFAyyڍ|\xl]Ӳuj3=_oyIa!N?`qS~qFu"1Oy~5}JP"p^yłỴ/_ues$jjr^hj\c7\qgdbV\ODyJD*4y%pQX,㓝vkTTǺ@ COk>f4\:sa FZM% za$>@DxQo@WGtΝxφVyEwu{.20!Wj. *[q^q"ا >ri/j;m:: ? b@^r'=d!MzҢK %{H!߶݇i>)mE WWƽ3 %׀J\g TԦC,*~] PrO65BkPU9 Φ\L y*D9_袤_):9DdN*ٺH_=hLwd,_Q!^ywe {XBP] #8Y{thg ڦpCj"|V6/-[j2<  @NHr7.sy*uȗ 0lLȅϐ?7_ d "z r׊Mj;u}XKj6m\m[3"]~yx>OaFjhELM2d6@|zqpg9mR4dO2{|w G#)A*ӯ3]9V~ʀHHDƎ+IK<> endobj 224 0 obj << /Type /XObject /Subtype /Image /Width 217 /Height 166 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 8652 /Filter/FlateDecode /DecodeParms<> >> stream xyU}LϹ.{+ (G<@BxgQ_WA|bE9"x  & Sw(GRcy4MD5XqVȬіF|$mdw`1;(@(llʳRL,,;eQ67-LeՋRw>fw `O:kKVus\@:~ӷç'&d+Id%k}}`kϿS?9cƉ=;lMWcts%uz4O!68G+|/F[Y,aN(Ig{TVVq| ?u|Dizzܲlqpq _β? CoaY;2]cٲgXPLE{X$b\a5L:s(5Lޞ~ilDw]E #8ӓqblhe&%]e%k.h^q&X*宯; ˜+,pKxAt]BJ0~٧e6F-*Ҳ(B) `H{OeߟVfdw8WI >E+FY,&.nf˻zu USUMUeo?9\Ҋ*WY"t5\&b 1b3xZˬnkyA.E VDbI%k4[Ck%DBA4X> 9skȾ%N&[BJ0 AVӭ-aWu@{״w/~~nu7[_|P7<\z-K)i-uo)`k'VM3ƢmadtVx{ѵ`/gw f˨].)%g\oT*?G\ L]d|њUTUvlM-;#&cl?p|+-GQJi0@0IbFn{KAYbbZEg~ߎ*,_Z#W)p Ut^:M^ 0uzdo>a$1!`2Fid$q16{DׁQ zVblb)_uU#Dk/,,{HS?io41͚;8Ø3(D811aSNJ+kz;;cPBt_1=b),=wZf Oxb`՛W˜mG;.7se |0Pg@,9I5/yJ ^D(maX!H;8.34%s—E]t1) re4gq*#MK,|Ye*HܘTPT! fFPo-H[F#IHS'^a1UjdL*E $ xQq 3:pNA:A6tMY,&bUINt{ay g[bAiCM,{[,CHx'Q*1~URf|t]'DJA lB0wy*GXl hD!=+^J\GdיC% IΒ2ӥcU53Ll \''b( 3F !eKq$KK$DϮ|6Ez7|g~ٟ7{@cS^i|>R8tBOdGNA,^4U #WmG&5 ye4ڶ+bCTڱS$S~5C);9^r1VCkq  }ǺUM8bs,f`Ʀa!bVeЯ펌I d5P0M@$rBA큠retOXl}RcUbݼ |'nƤ"J4Utvޞc=Xl=+}7֤b)NS.pUBT>}Uow5w^܆Պow댆YKZ[ޓĤBDH2ހp$;'chjXQh@ yYiiiɁ-d8c̐܁#aqƲdL`X BD|3CǺw4նL^zFX ?Θ.vNmd1\UʘTdJt; |mGTM=%%WgIc|ڊ~O(& "sdw2~R aX2SY1 `KKJufw4Ld3eZX=t@X $\s_'0 E ~;8񮝺eRf,G,r)k*t/]SAy~Y%^5K>xb(Ņɒ<ϯz.2MUu|X5y&5~TcEry&-qҌʢOuF; KR lp"?KJV̉zǭIg_'f;վmm7z1J?^}֞h-͑r͒bd˜-s@뉖9+Zdٲv8^ܪ۶Ed)mmW>* ]fItuqЈuBjܰ0e1nxٕMMM]s5ec(]suSS3Ϯ<|#&7#%QUb {H8,e1 fiœOz³K`Ǵ==5sN[^Y r!j&,%ojxb-x1ʢr͒}5>XB&njYk~8n|-ż!i4 ƓOR,L>ɿ}<3~Y}ILbfrk= U;Hh̰";?kHS5褩[n+--MdSNs̟cƘ,۶ȾRl,Sj/&GU<`#ZpHD^ejH%a]{?vp9sLy8qdbN0nB`(ڱcGQ%oX̒W?3d>068c@@c!@0`21p$l61Ƅcv:utt?%o9K,3c0b\$9΋N`cُz..B1qV!Q]. E&u5|Z>Kq1`#?ئ ?+ 9ݭ.cm>Byl9FvoOց2LgLgTgP`W7a `DQl;v0n6l3HcI:MY,ʒ v !;Z E !",Lq`[RMݫ{{{ .x5MSEQf\Yc0b!@]ULt`: T0ұB)UUu̙?x Չі "Kf!@?Qf9&{A,L0$LNhN~jE͜9p|P[[kܾB4M4͌}YPY+QJT<Ɂxr몮ڰupM`apI餔۷5B!j{?ܝS-oΛRXh+n~.Kj_}@շۏf_'p 竟 sQs[KlEƤҚ`[,U _ĉlyK}M5mK03Ũv0(铋~,ʝDį~tT5=w-g2^wWۓ4Q]wr8ѹfDZ,TLeWyEM 5ϹG +}3;~tqsa(Ieb)~^ =?8]~>x_[{~`ĵoX矍59Ϙ݉VXy=zHGǑai/U/pWzo#oβo--{W,t+5SOǦjGs,"a1c0УٖGlߵoǵLkwqˮ8^yuC߹ͪNStuv,y'ʫ k`7ޣ)O9,] o?z kN /.BG~z;|s a_YY)6s^x pQ~є-g1HV1luB_{˩{ O7?z`"?/xg%΍nNC/{q{koM6S_{ږR,&ƌ~(=}G 0N)KaD=ׯb}җN7ֽwĚhWc$v)}D,bcl1)nOSV!GkqUb{_N`ߌj&4ZxM>Ѷ,}Dr/:/&v]WI|D,"d1UuBs6)7\SD'$RŲ2Q;,h}BTUW0q]qsX XQMi۽}b.2W"Pwۃ !`433bY\xSl|\}b(DP<Ŭ\:1(2vɘ]pUg6ʏTNn\7ζb`m/&SZH;G -n ,~lmǭzO|{_G\lhϿX(XPs |~ aLz?+s˗NuӶ+/ƶba w(dPl>2Hz32DEbe#(Dd։;tJ)%Z}r5G#af(%  M8%}{.ڰ Sν=dR€1J(sqe%ޅ2ŘzyQTG= pE6wQFኻo0;e$} }D,8*4 FbA;۷٤ "nwرpX X.CaS >oڷ:Q;@ooϖoq#bKјkt^?Hэ׍!r\UUU5I|D,(%[1~{ȵLX,yUj!b}kG$B JbƿQR ++2yR%=_\cۋM'gD%k!r #w |گ>:+8bQAO4a0: b]ws 7MQXQZ z ^Yũ(tǯ9ʣ۫7 ƻ EHA֝9+e1#KS*EKйKHc@iigR"m:#OӶ]6CPRx_[\%%`](`O*\,}(J]~AeNLy 4~vPe鮀M☈@}jiJ3yɽ[y(w?pt!v{͍=Z@,eG('m`ci=mKl6@p HϊtsVe;vr;NvN endstream endobj 225 0 obj << /Type /XObject /Subtype /Image /Width 575 /Height 405 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 249 0 R /Length 45787 /Filter /FlateDecode >> stream x{?j6Uj"-eq[uJi[ܓ*uwxdXgI=wvs9sf1@ o7\eO-m $4ODrFD˘ V"1$)mcBPQBǂBE:0 u:=Zԏ.S=e!UhBFĊ*b"b@ ([ͭ\t)hO*VQYAQA%^\FR9P&R9r*(CR%\Jj)RTU*QBU(u2jeȨGCUNF@^&@#a(MCiin¢U@[ mJK.CJ)]rtwuIݣH E>}(ҏP-(Q,b(Bp$e Oœt;3|dOP~| Ҫ\JR@Cj VĬØ / UӠ\hH.IYU?D}aX(.$U}0ju)fee&|5wFpkS0bYDL]hζN<(lyk&.E[Vw{KK;TK}5 mK&ƸP] d 9YŅ.L5+ʔqBwڕ> 2լ$3#],WO^/?xq狳C: RKҟ:oyٚtfOOM;Y@eMKM* 愺5Jܘ(Rs OamDS0.Z(L!"k-m4]).[vu4K@gҔvz v; d ``k!^|J!J{qV=JF!Vy:;G!3$\mj5 ~iNaYtYP-).:qʂjFZ xQ:#=Hd" hXIJ^$ "5)JRm++o1Qgə'>yd_fAZXrHWYj<,ñ5X`xwfaZ$b-&YGY> ] CrE"FQ+FR&O0И&WQpX.Y?.Lf6c=;=fV?7-CYR-m-5w>Rwk5j$q9L:MT9,;wxϟ9-y9\ K^Hx䲙 :a1.$3= #3xiA9b݇ʳڼql&b0|y?\6%>  uJlL@J۴̑mј_bR5>*)NLCql<?B(l Ԑ8li%eiqKOogYKHHfd3*Y /Z~9Ssddmh =@$i>eȨ*ypW"˺d*YbL)qZ[QI饃sm]]?vE7R4j.ۻ! ,|BE溗]mCˈXX 綸@HzJ?K '?>=7~FP`c"k 5}F˾v ά6PeCBo=ݝu ({+6vu_nbSNa XmdzI#뺇Rt5-ۮǿQKʋ ^.j%,B"j5F'Ңo_ZR84xy\A5$(FHl%[h!BkV}\\OIqO>zhnj6kJ=8FPl%\v8Id+ ŕWQ"q~No?y`B.-.ĢH}/ ܺ~U(NH7'k-ǥDɬໜ!^Sl*2)*WT u5(U*Hj-Zy=CAbz954 Ԫ"qYP0Et֬`DVZpt:Td.kkhSqєDk.%.PZ+On rFmݽ͙ǬlhN@ל~l^S6uV .գڂYmiN3q6>ѨzI`Ww赛]Մ5uDQK]Od6tu5.S6"'I %)ʪrdjpT%4b~5jv)J*dXm5Km5QN^k*DrJ.X6K[IR@"TdȑŧH Р r._vjOHD<`XZ/V aݚ% r2WbŲ%UY,)a4Dptb#n߼! A !Mp@0?uℑݻ+V4{\1ё,m[7TJ¯_>lEC䕗/HȹIxyn69)a*)^`_Vz Iee2R @/ {. _+*L}jUժ/6AbDbEֆ'2FsNM$B]^泊4y~ݝ]iXlmoN?jo&.e̋yMu`.{S~;ҏb0S6fW풼"pZG!d䆎Zh:(@pv>Yx"|VK߬dt%'qr///gvˬ{v+IOM$Ayw=:&_=m.`\pȉ q^s9)x~,)!ICh(lR1MVa];bLǔ22".Ӡ" ƺ6? Lg8@\Hk.k+]olih sJRWJ9JYG \M0'&J}*+* 0.p@g TP*priݛϽw%j`1h?zz|bsӒ¤}'6p#ok,Ra+L}C&bZQE&ȴwΰ, v렞ӚsoH5ttT<:4!nh-gas^SG{y\3U ]j0kbS^t\;Co4˕炊Z;;foK"S+InHle+p:Zh~>R6} mS-5]7p)>n岿zzx~TALKUOu|Yxಙ`/"$tsXB5 tk ?q]>G8!zp%t"D j䕝zտBe~~ ?f'!3hWOz&7+=#%!%.:1&"9.)߼MOKrGW8 2bxXLedJI:Put/T|C]o}]@sI`&Ov}}=͘`恾)^j.d1kb\H6֎ŢMSK[g:,aͳ,b/V1mA`HqAϞf=_vtHO}ɧ5#-\hGwՄw}#=}%Ϟ&Io^s㺯WrB\ld|r^$8o.8w&´oKIb#Zљ4HmװRt h`q1^p"{+^ [R犠4W#:XpBjKԔwo^ݽyO!~r 0=59qhv#OIJ|坛C"`:m)u-NxaC _|~5ܼq LOLS5lA^ Sε7^?bϓJ3h8쐠p,um0HFΏYjE,Ք}A)L}đ$böؘL錠pnL7vl I::ԻUi5a5gNp{p^+M iya@Uݹs`:5e5ڒr Z^NhH PՃo߼`LӑNjͅ8 <ރ@_P}fgUݿs֍k`0SL{^j潛t:b̦VaE&d%F&KaqF;i1 Cj%8aN ";ߪFQUc(,bmcQK-EO sXl$!@{7|,R#AV[BzvV4өhQ[2H20>F# 8I./B \S/]V_4'b1b"WFIgZKMAmCƉFOU]$kҲyphK" PmiH*-q_"h!m(/9~'J!pJ5 y:[R,GsC^߁:ReXeG+;*_!}qG5;o{w\VƳ'e3UUxtZGpAZÃiXSP[6s*=xOW}JXJRj5y+kG2GڿbxO K1.wP?)=ȴ~Pb4]6:N߆'5Mj莍՟X-Zk LX~LT1uFXdD&bڥc.1wqmRZJDp`I@~(#ҠDְYLxy bZjdl\mRùצ6uvqS0]ɬԨ ܏rT5vam52 Bc3,N^ɓyFF'߼^_'tj /iY`q?RT?lKpDfr 8ufy ;~(mGm?Ɩ֞|׏sM -흗{hD^M9wMLn{4 ʐ}fk?:WϾp :Yytq{"EcrtŎҜ,n;Syuka8=cYwc1B2,Ly575bS@[ݠH֪jq4tU+{0xqDR}]` l=^ HeYBر [ MmEpEYVyd > Ka&'-B2 τ39_]qRUq^g2y )?f#[OC*yΘ^ _,rp:i|VS]?%rf|v'Dffy i>GGU٭?*|Jm_֐/,0Мuiž- E_dy;HnŽ?lr]·+vkM;`jʣԳ=f: +~>|z3Xoy]COm7[*ss̖?cƮH(fCC30.Θy-I ={>d}`!,V}+Xa)~Yz _Xn{(oWwN`eɫḙo^O)P*}Ψ|LC7*tؘjtd[@^6cxlz/, M*/)dsSI|5󎋡$ſcs_r:oP^4%V{lߝ6_Z]].Hd /Vӂ!$-x:GomOW}]v+Ϳ@Z:؍i23Ѻߥ y]t+ʫ z ̱?WaO}ߔُKndt~"?گ8 VydW}-۴Wߟ?)z.(''B>RÓ:l(+f^]O>k%W;s U:T[J>bvnb\6 /*/Yk{qeǛ(/X`jluWtGxGxGx7d"~w].5U$H5WaoB1Y4W$sҞ<|$GV[DsDO$QJ #sn'.0s7K(DY>gBF ;ҟ关u=/AE^2yEbf5w{^kߦyI?>=_E^bV'DFyuRO͹#'*$J {j@纣E~7Q_ݡ͉HF~,.='N?8ʺsjM?hFD}-d4vztO%VCZi1񇲼~v2;,Lo{~WrkmsS7vɲjSkn(/X`򪯭ly[t[xKx ^p-q0%Y3y-@'`!Oaڻ-Mͬ,f=/gIDP}`Ҕln!?$.DAqz=RҾZ1B)6KL)@p.KrmwXLwZ{&[|tKfS(f&6_ E"HŮ=VV7a W[P;SE^?{E.rknVnwۇҾOGZ)$;^4`??듹0khA"&BueSB-yX X)6$$!W8%2,[[s NF`^2\}]2vOkn$}.zyxgYb`<\ʫM,$g_]8x+_򇽗~ߕ|\ f=jkm +ҪXP,.A*)*ho1c;,@p!fb@K7i@ +!o_3r&#/àM4LP o\aᄌG3b`T1JԀ_׮jX,+Z^Z&EZ'yxUI@2O뾇~c\%x} p81(?zz|dbOet<_<0@ƁA<ÆûXL"@-/[c%b m}xy0&@<Byo ,JKS}BaZ[90,@&>~ߚ᫧a*LfV(`~ s k$DE0d,FG">7:"`dP^P^@ya nl'c~wX~M0r\vM3UFH|{<tu#X =0]GpPtV~^ltԼox0"Ĩ~ݰ?Nd0?q{1e NE=+] ˨˫j5mٻ7PRm0*2j%Ay!$᲋åX"#7ؙXna ɯ-43nFqZ~sG)s\`BعxÐ:G)ɀl5Dzن 6v@&VM|o ̄DDJnd dbDv^}>(OH^X'J9pk{ָ8R~xNf{fb0m+XSf8lN~лgYLI.EŹr= [sLwL^!aisi7cQLy Tu-(fVk|E& 8z˿҉ʫ|/On^hJY|>2fS柈*|5oGK޻{VabOl=w_,j _i<Ý3ȖRJ]joblF&.;X^G?,cwagf5q7:X)$ zځ /XRg8=Å/>۾j=f饤:0y93H&+zXDJ? "h! κW9OKulvI"Maf9  eeу}w6@Ȍ`8[ xj Gog0n=iW_8,GA7;%xT)tfhuYp5 (Ai').8Bn;]O {3 -7 x{ y{LÀX76U2Yx۫.;?μߡ~wyuww¼g f @04 *J1Oo>,MBO_.&muI ڇ5jYL:k!E l׳usL&+~7(/c"GoƕxA.ӷ:&u`&Wv@urR:μSȼxtqrylw_&ZB?yT\?yܰ3?JM|cHR}͡nGibtT<eP%/%(`3@}8 #!|fi'XP0Wsit* ]9R0_z"als,Ƌ8n7Y]Jͺ+oMLPH$=sn:M*n;zy=I _Xb7Ws3>ٺy_XXGWO0wpdvg$@CWsrКh(6MzB7ɵ.N.3gβk5?_vEID ZW?N뮄u(/Ow.1c_홇z[Z3t杶U:)lNӚ8tW5{W?"*"쫯C|sWߠ=˿_2S˧`jl"/7v[y A`w|4-d"=>!y!z؉ $sōx̋Cvp 5ѪyS\}Χi` 4j%/QOۛOdpޓvYxNSify .7SzWI3m1Zl`Sg7.C^UDq7;J)&ge93=y0n̥]ݾR$/J't$K9h8V=ἃ 5Ѫ@goe SXyD/֐ͷN^wm$'.o4tי~?zW_Mu#.:יw`mDkێjSkNe{C +XM6۟S^9,-y-{5+$(R=M ` l1?,Mͬ]֜.[YYSL,MKI$G/'WۓH$ xB>'|)zf˟rf^\띭If&fVv c+i C̥|0MyafCS/ikE2_q6(pԬ䇛f -m.?.&3ʢ 1Zn+>-"*1~*B2[-t ͆؟rt4Ji9|ަ+!Tٓܓ6BvcVgcsH$ b~̃@SgV{㚱B+S1Řd5wNjF+~{SS+ }h*"K+-v.};뷖I nmmmح%80`%e(h̄Zo~@^=MsOG-}Og.[O\?/>Ѐ5yޞ_`Q AyA$D~Ջyju0Z?]_'~ܚ5E[2)KScFyܜtoϞ<~ `0'4/1nu7C]P^ykv[uKJyDҏ.9+IEeuL?$hJ<2oU74Gwsdg~ɚB 贴H<_V1kÿ>:𨓎 M,G"9#kP^l@ 5򪯫r\ns甗aKNDT@'F&kg$3pC͹,אM,.'Ln>a3l~Tg͆P^j1|<0tkXP_#/7)H_zQ?)gϚ Sşw0tġno?{+6tut,l cY,7![YPv< .%omttz+#+6i =io}.ieaR`.MbKe/찗ʋ'3 +>N`r,0qEv>L]9R/&Kw@2[ 2zT>):ƶ.Gӗ{展f?„bmf9w<.Js DUWS%p޻/qo^sQٙu` "/?~,=K)ZYb9MD9 :LuV<y~ۭL6zQ' yeT1_>{Mr4d^5W((/BL^5Z6X 3<,Ԙ65Ճ)\y65Y nO(D(]dt1"/!/f)SD:s߭!Y+lX0t:dg>p3_, /]Gz8Y\INAǭu?KGWf š^L,U$3ᬫ) Nzvo6//ckq|0/Y6 &>H#{ /F3e~;M6Р 1yUW}/YWlT9akK+ tu>l(."߯2+I(替8ym'O5063!K Ɨsd]g3=ORK2奿MdNS7㹄\md5spÏ;yn8F׏h~X0o㢇ݝEecx>Rint&.(65^(O13I7]gވgª#51@pY5_v\sgnnnB0Krي;㡛t ;]Ur8j%rp4]])&vntmIsu 5ɋ奼>F14 XxY V;nש,6xSZbc#Gyg8̰[;YR)dcYf"^rj=ʲG_N/276 gC]۝33> Of^"ಳ|>zp%<_<`N0?*F"Bax 6"8.!6<(&,Fc‚CcBcBHtH`A~Qhe5J>(64HRaCpɄB$Bk $Cɥ\ҫRrKUC\\ ? i ,$$P$JHQ ) d - AH )' TQK$LߨyYhC!Ҳ3HST N@ /$R<<D~W @& 2iU%=@  @ P^P^>ay9pƊR^QP^pU xܶ^X`\,0bgceLFGG#D p\,q LMnin\&b pc1"B{{{[!Fz{z%.=~y߰Q׽!6ՌJh\27@!dWcCZ^TStudXze Ohs9덭v~chtO™~fI9sp,=@:yq-c 8ȫVMQ%)š vEɧe0ljը,搅򹭭ʾ4SGr@XUTu Z^eMtdۭ]o^zEϾʻf [5i#vivUw"Z2-}x0<NfqrRsڄtV喋rP?,# q=#5F^ۭ\pE^RyEɧ30Q*˨J5>tn+3/ԝYzbjn[JzSVeQ=='NgUV^y=dUUװ^ez7~0yl&]e"N׈(*V%AI5y!z!‰>?KB-BL,}eYdcA1PHpXr ʜbaxY#˿kMMɖnFq1fmgS z=nv&fdzlw0/kibeA?_k4ʍ͛mc:}/i ^,L;.;y稳_I{$zV C!S@󗎀N~J{s  g)/|s%yճtφs?1o\Xc<}әcwegea$Kr79S(+kN}dJvc YolςM 򪫩RQ"/KkwU+;ٚ;nQP!ٌbl`2} m̦lf̛bZ?\wyE-AhγޯD;[ő;NCжpb֌2 [ɻ +˼iD' +o鲻PQ]hsOY7=מ0%8=A{kQ%;; ]i ;3u회mIT1~]hEط\ m;=訍D:$lm*v1ag:h{$;t( RKں5Mz8}[ٱ{M@wY~C C!|23iݞ^ lS.G΁u=g '{[T#l̛ W|,8&:,u{ۆWw,$4n|,P3yX8 (Q.ĊxD8cqM[Ve ڭK(ˋOX]. #W;}N w8T!mXCȐ7kwD!;A%(SS3 ;^JewygRI#HсxC+CgnC}zbjiY45T:$/=39SCs 8Đ|t(l ~"yyYdr\ ˔j8- K?ij\keF}dM]9kh80E˃{h::BPm7WutUmvƏ()_XYvs&~U[r-]m`wQ;;:d!cgD-Ӿfvewvh9ͥHXmg9 /iv{\ m3ID«IkS5qR%e*Ax);Z8.?z;D[H0~2#TQU|y,D9WMvk)0 YN敄ͦ^jI:y2XAx)+Wf7UI yǟ*ĉ)7Ģy$¼z"I-DӉ<_e\ktV'FJk^Cν_; +oLRؤIXWSڈb.&זDYV!`9bY5v'h>i fk^]0GI|ʃqBԊK#N@;[ukrL[Qz5W[mΖkCzkYZI΍.`{@5]zyWAKu;[LUb NXaUG=sd-\5ok^k^_L[X-&f6mk3/uUY־ 5jr70VI[(QPբ_^mXʠ.LV'ʜ:^&:k~Fؤ=>+kӤOYAZǪJ<~bQiS&TWL8J"^қnS) .,|BJzoerp?84G-VT$HnQˮ ëR%.{`4[Q[d)U;q+i, "%^xthvgL{ q*yaÀlL?'OVWf$R*:IuBszi\X 7zI1O(t"{0_>4:[{]s,O{e ʴڬVe -c^;o!~SkT܄Yxq:f)ۮM^NޜS*<&kdlV]KpQܨ=nMp8Tf f+:~toh Ǣ{ζzYͳ-FQ] 5UM6Ù,-#mєXP6-sC:~Q;d^'}{«;oeʯ&+moru+.V (A!P8Ybaxf_Zbos*B[y]9nNFk˩78Bh[ys4WjᅖfW;X͏H:k=7J<#w#)Ӥ;:ښ{]< DتXteqvs[5 Hrf3meCeds]N&qk[*/h1M[BU|eejU4$"nURm$-[oʮn@+Cmucn&6UFMo-oו瞛O'ΉTv69wV9Z_Vҭ' Ҥ3h9_[f gμ&4,F5 ^XZUQKZzLeBmZ͏a-+R4*U'66 |U$L'8mm45q:ۮO_9hJTm[ǽ-O󵭮-eo}ZҬ`?WcCR~ɑÇN?o~g^?^roKu"%.5Nx{ő+rKK+ˋM%\S .xϣ9œc%H O@+}P̓ZdNY._DQAx8wfΗʕ#(~KpY&@R ,[-r^UE!~8mKRwP|'9a{3/ij٨2ΕYV%K3)Va777x@m3֦>ײSNjkaۼ$mU0jR6rxB -3H%- [|w8|W7.NY-Mhl\ TuۮNU?c6m[REsSkM!s9ۢKe46s#!)?%xY2miFT23`uA}c[kc]1a]uJ;K֦&ūjهq[U- ;}Frޑ'Mm ,DaMK^D-hE{>0f}u^e5KV4ȯ "um-E'QS*zWo.,vChL{ ׹Az#:k{Q=JV-]53p7ײښg״45ɣ68y55S6ÇuSPS^C k[&:2it:sƎhLm ʚ'I#36_^,qnD"YH uP'eAx@;V9/:x D>+N&;T}Liax).(Lgr^({퓝4Jq|p*Rt(,qf6[ĕ"3g6s-DXy[L.3݊AYQ>{_bm~73OD%™S=o]\ނ}U 4F}?V{ s뱛n77TXD2ϲ ͢^Q*"Ucە K*y}{hK"8O;YVq[)y2t:_|R&#Nc)ק2vd?T+{wMR7p"l~{Vſ{܊V3ѺwOa?xKǿ}%{(nےJEq &ib:$t}բ 6oң6 6 /Ngx):Ag{[[ 2 ^ yg#{?@\p\W+76:"6*Ç'|G+*ʣ#dAS쾿V+dsg M4MdAS K*R`A 뷄Wzc\)G 4 %~3g^`0 x`0+y`0x<  G /0  ۠7*BUQU˚}Ҡe*%t ü/@.JQ\fccFߢ4(%J*Wj\T(}yǏ00cJR( /[Uy_Rd++UJ?}a1oQGTJ%t ? ־?V(h_ɻϞ<~sp?{/(=ʥx/\JU6l㇇]B}?( IY+hpf:\&M:/zfyt85,+8{!UO!'F ݏJT{DT9G-,^;n{/*k7F:uF?|xn͍S& +B>|rBcB?fHmY$g=q@SAƿ<=K+&YI0#9-%ɖ06w~i /{p{_Vtew .S:݄ݕ,w]ci6<kAٹ>{B/ؽ$]oeW~PȌTCTR&>z%b`6YJS%I9isMЉ$ƊAI\!fm+/Enmi2/qޱ Ò#^4;qݳd*5p>d1+5Cޕ^(Dw;N[3\fn䕄ϤXL L:JeV V>o7"mMMî>xʱg^E97z;eO_yiKvp'3&( ufJ$Q;9R5"{s~$37r&n3=l+Ρ'So${_jYVটX<ֆ°[;t%KyW61F nYZx׹1jjyPy\W>2%BN"T-W)я)42 INL|ڃ^/ E|q1T Qg(2"kx E$M&ȿ3D{t&(i7zK@#`,;%!RT"Wb{iŽ2Wmxl\ qwNbwP|-yWƓ.&(S4"m-1@;Ź]I~gm7sPEIb3p78[Q2u'xt}5Іb^%-]ȍY7\`s~û$p0=ԜAG, 6$w N #d}ޔMr94W;eȒr\(F"}xNi/K(px1Z^Zpy8 W{I~. £h'UFUzk0@/Vz-C.1Vkc-6>/1/$}=lDE/ pswupsqď"͸-҇/ޱ!^ҵ'FI_A gՍja1fc0iTRedK6LqQ6 W@߄speAo'K7奥g}P5cS{Nǐ|Ok5xA ,yp+2 v ;x$ߌU0 Y7\u:O;e%Q6r,ϲL?AD:Yy_yŭ^[y%mw XT6ͅr~IFZ@`K nŪj^=/7 x;\[y qW鼛E<e8{)m,_@]">[:Z::QsM/?C_l ^eЅ3]IFqlcB@'5cVˮ5q8]p~/#VkuAߪj68/;ऋ W"*}9nbHŹ9E+/Vb]>`+ͨ3\vpx ɚ^Vջ(k#X62-}s_7.#1l3:d놎]n> Z iV^Ee/{۰K!^`LIvQrajmEw4(4%T+[IOnw' ^(vsbhVϼ ,ð ~ixLYnӶw=:[;ښjj֮^P_hݩw_Pz @A |ɚ;TqkbN_ C=&ͻʋA88 Z~*R'sKubΝu8sR;!s-<ؐITYo-.ࠋ[NхBr=T{!hMInHJbe24nuRH[&X gGI W(ܘT &u23Klxq]l4ŜgVtW _}2-s_e/9n8oFp$u&Į]ANV^>~><9Af4x1v-coM$8Nځa;^C"ϲH~ \͉MY U-Qɴ=$S+2VW?>lkmGF}Yֈn/wlң\Jkwm ?ohAIrUy`ڇ]狇WGGqċ80K9\*dY:n'ֲύ#8ŕH8QX$h{L*+eGo|~-_PJ*Cp?/$DC.s;\[U]KZϳ8/GۓaHN D*w4 L(UOȷ  yoQWe^[8Qx ?>h|KKܷF`.aR柙FEr[aaJ{f{,UKʫ N(?D*bš"cFߢ4(/I.0ooI T.IeƌaTQJ.0<^`0 `0;Kd0 }*WC`0<" ˯^DQAJV)dO=zk>>~2R /D B_ 55`0 u"3Ϝ[&˥O_zae\Sb7o0U)-U+R:=B,eLWlD'O@ (J"AJ><}Pk>Ho߾Eye2YfF\&%Q!oJ+$ܜ\-KL&V8/G,6kk+TW%]y^?~#~A(m xq޽{e[o^~ /:ۋ$IaaZ5467ty2a&nˋlO.a>Lb.}nK\dnN <#y;N'yy:#s;Ag;rGDmSZׯ_?z(7o456܎Nw5D!T58BKx#l?fS7 ++-pҾ#*[OQN/$gϞՕO>}Q{[[htڸW\}~]^M5^h./eedfg!xdg 1cksk lB4_ űIK Fb. ߉bnNt[QNCE{2LkޢP𚷎וOdҝ Bж7Xxߐg bôr Sgz>=,8:͖Bv/dv _C%X;O\{^ C{Z(d*g=xuwu}ӌdWPW>ҭ 6A_D Duzzdy+|ZnGVI[=u7&Fu|B$S$]=COݒ-Cm_8ϕF\8߃Awf뾣.T8u !kY3~x"l82JP|?NϨM;':QVa&y]5ȉΰz- !] a 55>|$fE=OYQ&ymy/X)~mխ)ǽ{^O#Oh@1c[j+? x#~gvQ8!xu7\ڀU}sڎ;3«I^oɗUPy;Ӧ4ۆFP,򮌥MzYGx /~(i"o"?mz]Š ן/\z[Y9bR)r|ִߓ3)_o[if/WZAC 7ݶZdm[cgRwuI! ZTwGt Qa@]'9Kilt7\ 0[MWݟ,!U+GH?}1_EKLYR ~ՇSRh&U/{ߪ;M콤}:uÂ詉@y2oWHTO]SDϻ6}v23 fAYz #Mn/R7jݖ/ݭSdʂ~xWXW{eM6ny m2wT9 3glJVRlvK~~d4m.cK=/,]Jx]-(}qgܗ}%o#3 10]b7Q:ng;܋8|\yؚD33Fk'0xqf9}ohXocό4cEoMWǬ%t}@Qb0x͇}+4h5Z_y}? XԲ𽭍/gY:;f E%k68>6/|lѝxZlԤ4Ӈ)^tv /=[ Zg79R޽B>4C%$]geGqSҟhkݍN^ݮ+>|8 e,!^u?UiE#Bӿ=SC{ot;-}>{"o0`:ٟGD:ь$ WmW0ډqx~/c4ŒǯWݳUIqauNW/9 ee߭`{#cծZzM!P毱 Z?eW3vhx}3xCO~0tw;jz4s6a!^M 5ȋgW}$p|ա#qUmh^q\ëJ0f-XY2!O"J3K r l^M D'ݏ[GKZ΄$4>=ap^8tyWvԕxNA$7zw+Iiwd~nX{w1g~=&xw34й^yW~ [1^Jz32֍]xS[fҜW%Am/~OSg:l PzWvfF\/sm_s=JJvKEUZG̤X+zc^ow0'ܨ~"]j[34^_>?ɘt:d'CgmW|114^ =·]K_|ؑO\O]gaA"2s}Z+67_/^6tبpƺ*XbcT`4m'!q\^T%G#Td,beBH)4MB*٘tR6)Bt^]{9' J.b Y3\gvϧm]).t5patg 8Mw#?9 ҞK fc 7k_zߐ{J4y _Pg&jMZ3C2ԄϏRfH{ͬ^_>Vw˧G&2O3b NKw벰lh$2LY~Uv{qZk _LOCw_twI'ѨdqF͒ffm}+-6$JnPF'Muh4&!`mڢחONeZ7`i_6J~xPY?:?hAܐ40LzsswnMBr7a|z»҇ɠӭ(̀5$X; ^߄n1C W}M%2j-qkri["8N B u^J VEe 1DBbx%h[ ^_l:xÃ`/)8cڑP>%Nw%خ.li*H ͺ1y8y hH<8ˑuhY \̣Og}5Xߚ+\ë ɸU2S%*eBTݣ,^G%c~ә<Yi텏{'<=OW~n!=+nNczPzN_Y+Od GW|Ld[[[ksВEG(Y7Q~@vj쏪Xq(eikilmmA| Ȧ*ҧԍ ëtxRO~|)zFӦ\ݾk{nuXuY/` s[<}~1*Wʿmm}D?}@]F`/r\Po}_ݜ|g^`mCQ&`0\)W66d^`0 fg{6̔7@`0׷mC%̸tw;[]V$Kpx%[R9F 7{G͸P,y]Tql`xPm@ mn~wݢ\(\6Ϥ&[-/K?)(o|9q8l->`0 %oo#]QCZǢ 7v,o&՚AcX{,:AlvN߿{Lݑ($&@srdR>W8}+ˆ4Ew?і0=/$(>7oⴽ^][&ؑGea,عuvnʡWv1Lqߙs"J!PI {$!/d+(ôĂn8}?:ܾ+gz{Qm}Wk[oH/y fHs,fnX}-6A 8X,pq;, em{SKQVĀӅs)䀃l8X"n|]L9v^J̸X 3v\IFJWVxy zcL^}9(Ma\>۽{j.ry&- MsԿOٱz3yΌxTDP>LrT:燳_%(ߓ`ǽ. X䱯Ϡc; XvQ**/Ljs>+탗qy&8-q%ҰB(Sr풄6D3E|B9^%'Fy-%'kWㇳpܕP9\Ia]e0 hV+S0ۧ[Ⱦc. &[SCR)iRHsohĭw^4+T^I,f+qNeKWHI_v44d|y+&8;pfN{J kåiWp{vy8 dÃ('[\&[fCK}K3>8*X˴t1W[,wëqyh2$bY+0/S ^,!wHRZ6]J/D+.J՗LUZzc.x, z'f톨B);VC\Mv^9צpȚg^h`BÝ\>xq<8㠆& ).h>?23p]q;>^ۆ7 a`4 ,qeĉt`0xKB_rq߮d׿b 8RN l_ GUoW~p ~BR\`0dx%AL`0wf?%| Yr G*2^`0 +/ _YzoBo`0xqrHΜ 9{ Y igșS/d^ÁWaNVzJPȯ`0X%'d ^KZ+MtdXJx'MJEP1Ē~#< @?B),/\)1?~@ b "w‹6/xx!^RF2%:xՊ&:%>@ h1Ē@ 4r%▚d~(YLP^:F6qJM4 @?^&c(S @ Ј@/IMߟqJL4 @?^b@ /ISb^ 2C~xKL4 @?^&c(S @ HKZ @ ЈKPhUl^  2C,6^  {۰@jF^W JMtr @?^qѦcHUyF U%(-2/x!F.X/@ ۆ y+/qD@ e2X_Rd^ 2Cz(~(Lǐ^JF2E/@ w)h~| 6 k!eWë,.!:&QPF ՛ B!ۆB&yA!ïB B!|?/!tw !^q!AQ K2f2>9864K~12?>gĎj~姧dd}ojaq^ʌxA(WO.|}$c&#''cSA T[W食>|%#'Y؎6fM܎gߡ;YHx}hǧ!(~rd2- _ yA{]yYQϰ7Ni㍏znqƫyAi׽[S&r2WT2;r[D%7,:#(4bJc{K-J˞?/Hj2펡W[{]EvtytQ͇[B7WyQKd7S+%S y9YQ-/M&D_g[2R!^B udׄy͊ټԫ-R3gTnFZ2W)za>f+ַovK|OcͅH7|;/FGUecTLDDAŒڰ *gݬOM_|jZ~3}|oX+_X}KxA)/wZ MH+es7$mslK~_l9[w*n[RةkkAcǬ3|MzM3|lE$tzh3^v#:4$G* W25͚~vЀ<Sn`n4.$Q#=V"hm5y)Y,ߙGʔ KN7'D1EMpX^_L7S(ί?/0$<.B6qAyr2_ǫݬken;%1wFtܺG8V.wnGu1deϠ5;Ilvufθ6%-_Р+mC1=uo Ɛԏ>?24uT &{M?׿U/KϠ YJMu`۴e&N-p[nsY*L +W6_ZSY*2kv25-IYsSuL+*Wؿa6ƶvyn 's&/\(Ăͧ6Ow3Hn^vMѐq3+ML&++?ыL;Wڎ/:ÜiJؾؑ)\IMB^Ɉ!'Y9l{73"Fmt=4[kh&Ɣ10EY+ʴ0VJuFR##WcҒ5|#3 ss6~a1oD6zLHx]uN{QC6V2כNiOXra\Fb?؍KaoV&,Q6}j5izz˻S X) BHM5UWrjUVf]M#'cSűr<!KX_Ys5lC钌̢M~ 몫n%3N=,!ʅ2.L>^*#E@!F&!wT/%PhBP ׼BHBŋx5TX !THeHj@!BLiX*j@!BҬȰB鈗DjT"j@!VújD"^f,oJrIīQ(‚@!;wIo֋ BNX䴿yA!콟sY~MIN"W^qS'G۸Bao,Y yǸJ2E&! BaNa\su-+-p@!ʭ7]o_P endstream endobj 249 0 obj << /Type /XObject /Subtype /Image /Width 575 /Height 405 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 509 /Filter /FlateDecode >> stream x ߴx̔l,C@H$`~}h=2 endstream endobj 246 0 obj << /D [244 0 R /XYZ 69.866 808.885 null] >> endobj 247 0 obj << /D [244 0 R /XYZ 292.283 495.052 null] >> endobj 248 0 obj << /D [244 0 R /XYZ 302.121 207.787 null] >> endobj 243 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F21 168 0 R >> /XObject << /Im9 224 0 R /Im10 225 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 255 0 obj << /Length 2084 /Filter /FlateDecode >> stream xڵ˒6TE H9lLNZo988đKB/PԌڭF?~iUFER٬"&eQD\n,7NR񧏯?XTn_~ET%U hL}$LHx}c4Rgyb3_K IVщ(AG?/ҹ#d$3{R$-+׉zpkqTkNW|HVIE8KRq#:_fZX}sD(Ə _V~wvA~0}N=6~x @ZqAvC0_6x RIiW,Ɩx ({]Ð.&eYNo(ywk_}n$<0KDdYt"MrJCs$9m.WoJE ΑDjeF'w˕18fdMtp6`5X=/2~ec0J+UU;bv|U)q>Wжo/G4I2!\hYi(`_ʘːbjL!܀pG)|t#c{ "ɢ/c n7 eT{E\`}Sok\Q^ɬ؇|c&YPwG!\ >dSm0^j`:VPTbz/xX[zEaڣe;:_W"63Gbt+C89I,h7ҷ^R(*`NDh0Xm{(ɡ=93о٧^v-^h (46Q1:~z2P*fy`b'{(mFԖI=q qVP$ {!LQ@-0dZ; x0*XSQ\_tYq /:4.wGaHn.8z@7ᩀEATlNJf4Iv7"s!~:?ҕq#*X|![u=N;'2d){ Sy#/z̛f"1fS/3fy^.M!%iPjh03Oo74o͉++im0v Zn/ھPFQAs=.74k0Լ =[80:AJ/w%$.3O G/oyޞJKYo@L9|ǓOhkK/4B"Ðs(L@vKr+.&Nyn]02o=pp{;RZh~*2D0l[m^Z\}b d(g6!T5oydOP:\Q$)SRz,1,VT`KS0O9Ph~Gg<#߅ZlRd%`G,&g?RX-(D'դ?^I"3~ϩM3 ~x+xPbUFcÂ@O᷹JDןtw6 endstream endobj 254 0 obj << /Type /Page /Contents 255 0 R /Resources 253 0 R /MediaBox [0 0 595.276 841.89] /Parent 234 0 R /Group 124 0 R >> endobj 250 0 obj << /Type /XObject /Subtype /Image /Width 846 /Height 571 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 257 0 R /Length 72572 /Filter /FlateDecode >> stream xxW'wf&ܻ$lH @ NwB jzIB3؀)66{mɒ;{gƒGTIE̙WgfΜ=3ﱲz ᖺ6(2(2ʨT[]S*ea˩+ ΂:WaΨ7^l^/X>jx9P T&\~/@2\ͤ:[+@q_?JaǿOo5l:¯vEk-Xĺfѫ}]Wޓ'O2x2EAШ<.GU`T*fP!"MPBMR*VSYQhU RUvL[eJPqUUhBJmUvSUFUۡNtUMj;U_[۩:TWWJ *5j5ԨR3 WZq5kUZ[j#У6m屆:'D=-OU"[]Sa,/*i-jBX4w_1li%9­'9#-"9ؙ>-<0)-aR60ek蔲Y*j=*e&jbkW j:/kJ*S^zpv^ZW+Z־UIuzeCuT%$XG4QUjt*E?w۴};.q_`wM}˭xgYːDa"^*GgAo WH`O=DzGGzW {jG{̤{ Þax:Qqb;G~~?PGvtGxTc]y4u鸭Oz11/݈lNБYh{}[^=U+֗-!K} HuTx84nޫ8Z3{T/hܣ=]si\|^>u#Nkӆ']TPzm,ɃGapQټ7^ޛAzarhHέ[0tWBncq9|1nPeKgjRbzJrUUeiqIP{QnWq +{\ q5 с}ss~V58/ق*R$E|ܭS )(t:5=q<%_ټWJ`3ngu1TFbDldӆ[l..*tp=?.[BcćjÿB`xh(|JEybE4z]5wy~D}W|E36y6\ T*`W Cڕ@qA;f 8MSTj" LMhjUW uІC<\.6WL6V*|oOhc8@<sR<ܡ`}!_s3%1{Tꀥ }f͗Y4|zԥZ\:_aGteo&\ʨ{D@zC+-jeF_8{4㮞:jwWC⻫TQ V縣)1qRʸz;]v%L)tlst>kA7CSZRdMM?gf(a_ c@8udݳmܰ0_NT*jX{Sm.CP̭{7ϼg5hQnC.''[FJ1G&΅ #FAW{ G{WF7[J{e&Ѿ[K| !_#5R3a t-&e'n_ka`_aVBQl3w\rO*i|Z}S+S:JSG;) tרMwj_}(1Qa^)WȄyF{1,T.^u^gW#hKMNvi8/\%%lڰnԈ`?XOc[XCEz)1&;Y^4]rtS['E"i(/59)%!.*&%'CI(o亗 {E1ދs9,u C}zRǏi`suW$?糛&,kjca@Z{ eᯰ̖m59{f~|F*XKk8w։GUw%u+ޕ`^t6dvl^:0 ΂ Rh\ڱm!EEnj޸n-u(<1t{Smq|>ai G#&"JP79)zH~νWu1 _=q>&c~Jc& ş:\ _|woL%UMᡟ5g]gEBI}SS}0k$Rpe+B+9 :\GJ#ݱ r򌧏+O"#:`)G1qONjM1EYW(ޒ`rPGutvk}uq1 2܈\祫6&ht2u}0 swzۈ(<0{.) |ԧ k@uln}Ik&5=}UKk)Yg䴴>k'.OZPM[M\@]avm<-[yWؕÈ 1/݈@7Ϭސx$ERIF|0>qрqQ>v(Z#{\sgc,Oqe?: GbuBXDZcAw ;!xlx/ gt:?Ƽ\#'2~|˨M5`cga(e{߻z iȁ|#ֹ.һD>>g?M&q¿z ю_%5h55%PGu hW8НNx߹xu\yF{HjP~!^w͜ʗI\!?+-919!>! 9ՔQp|@>y[)]M!Eي4U!%)4lz%Rt(ʈ"TEr?P*QOV!+թO]5GPmꉪ#e?jԈܠԙ ^!)hQHō!IcR )n tٖtWIU͕|2S^˄Ϋ)XMk\sJ ΁oO}6MV*ڃilDG1v8G{l%!D]`2B)IZR [od A*7QQ3nm͸A5Fg7pݼoS6 L8HZr;5UK.Mt 9lR5$qrרTLRhOщv5sUX5or X5Ϫ7`+ɂb2 , H/mb-hg03Y9ޕLd}iolć@zQd n<9..C!_Kӝ}},ǒt?rdNdBVd2T:51vD k S{[ʺtg88 D}zӏGP zuw@Lt,N\vzNW۠(E0@eHz^ @DӋ}}ti{d@2}XO2|UicгΑ'cu4M0hgߝ ,{gԟ8 ߋF>=}>:Xz8Wc@VHMd_ɍhYuzs]]UC.;w/>׵N<#'$Ĩ7WC>T\jsYj"bs>!ՃbqҬ5CefŤ?tÙ2s*P1v LFhL]*j*Uu(~QP Ǔx.X;Uhb\mC-ѷ=ڨdI"1&>ь;b4f8UcPylu4c\ra<`K$=Q-],ٟ|MM?8fa)Q֯K3bir5Vens!I  u,;`/XީIsG<]/ez<~՝vϑ ,PG{433+,Iϟ\ȧ>޳ KrD.)Q&V@V KX!Uh_>_8S^ew}OFBP(X) ax{fct S&/mL_P>s#U0Q_Ai{xl2Bz6uHEHOA~ӆl2tʥs{vPƉ'qAxg+UƕhPN&,06`mP].J$mb#T:S5ő\IdAtF+auN^,ϨךJaIO( Mu=H u'Ç$a W#'h'wK:&ĽC4)I&.bUS?'>`qtYPTzxVzùQQ>m7LCrrÎy hw{̮LgaB`\^H`Y}p䈝ArcEw\'tXUt܎+[3{eS '~>CZۯS{&6ئ)PSH~_1KNϑku&))ǨM>*_8a_e^N.=Nw(Ȱ'-&2}$ Mޜ{YiYy %DUbowCW=8{r,δ_?3s؂2ի셑fM)UMZE~ -A_a ֳsg~}&tV7ա#?e,U^JFUL$p)X u Q"P4- ^%t|{ytȋ=?2$uܓ~qN#Kt"=p+O9"oS_ED&^:돻pӡ˷?̑h0}ydfz,$w׌_g#ʊrа!9dtCFeedɥnϐWgZW ''>O9~ۇj"wSN7DjBףּGCZqd-B'^Q~SOEHʼnدױ+/4ʕn1!9m2Dhsᤗ[I}JOWb+ǯv)Ks?xD4nKrN2lVCR\yh IBqe|OZRAw=].q[5|EHf5w%nʓlWMO2*𛏻dI+Gh^A)- *ʼ9of9d$MCj-+0j 2ˤ~G-3>Ϻ0O.-92רB־2:(~w³ReۦI-\&)7nǂX7rv\އ*cxz^gd<'1OaLţw!}/Ģ>4ioz$y9G i/# ׇdęϽ!N1p&wC\S#zZ_:il夫8{1%MB 0tMi/~7t]f"ᤗ}lwsY:"Wxm8bzByzR+ r6Qղ Sp⡫:T>ϦA.%Ƞ:"]K}0Ak|E4^ e)1O4J~qQ'O ľ}wWl뙃K0ryJzc5Ffz Ƈ}hll,bUAYg)_}s֠u2zM+RcB0$5?}?Oz.JA݃y[qE ~?lkX>HRv8;eRXjYm7K $t|O,Sb1׷X{Dehf~B;'/?IRHu.v,78Ft:N\k^}g< $oaI/vn7? Iz̰g6(Isn.:=7f R$;YA?,0Nčf߉̛ɹ8Gb/哱Op::+eF;y|%"jm%} h^ݼ>?gb}],ǏO|mf7 WL<=ʴq~{v*_>(9.9ܧOARk|۾PIz93EWs]sq̻"˜X3~tE}ۭۧI.̾!l\7%cx q=cP0a,h[B咻ؼ{R)HOH2]~5Me( f; C%fj?#@Rztc{-v{'0e躓ˆN(8 QϞX d?:ֳ-ubᑓJ |W޺A3nunE,Xg-f#L,Y81=/{\YOo{xvbOZk$? Z݉w xHzSB'I! /1_kӃGA5̧-30k<*"m1s4дG}zpUd^9hP͏c6sr;HTaw֙Ep/ɶF}zRRLpDϺ3l"* g;c .cJfʃO>prqJ󳣮.ߦ`9w?#_BVŽdZ0 VcMq:uԱ.F;w;]ϨCzTճKry{4JdPғd[;p0e`|%/X8𛾝=) #EGK9OVINESƯsNjXQvÎݺUMsZ=yCH̏fp9=fiNaGz?8Fw㑇åN__'2 0@}HOתD}}'Ə=*8- aNq=((WuѧdBya .cZ1v}V˩=T'80f|54q&.{|V<7en0,32YRwƂ,-4 ^1nDԁ __<e00_]0{-E zwW [t%VV MrON9E$3LM9nkF-K4fi~ ~}{}53Gwo m>n9grQG>މ ?gcO \Y]?Ed碫{vFr3h|{M! ~IiX JD:2 . ʠӲl!$l,F=4 ;gMe֒NATg4QqK%!FӬ#'\g=+@ [a Q{}O&ڃdxrυ'ZWcG/yd-'Bf8{hW [Y!I>_n݇,77 ]`93(`笴=+OAzEbԢFlgz(-#g;C_ _~)2 %]l5W 7J΁frO#2Oޓw]:976\R(=hޣT4TQBť%~g9Z3PL.1IDb$Y%.Q0ڽ ש(UX~_3O,;3(6A>P);Cgm8\c6mʍ(==QsZ7>?DÑR$QJ88b5R]5cl*f l\-Ѳ26w uBBN7ҝQ]AzQFLX0CC:HKXG%aס|b5XMA-eYˣ$^w<c}#qL՜/SZS<\ ԑ9]ĚqqHLsbV1M(GȊZQ\W/`wpq>GPGTA$ȝ A dM#76NnvyBMİ:1WgSc9.8G;Pg{hg;^e}zF8_A}:zw~4pl аH΄B}񅨋Po~ӟԈN?u/^Ψ"_jjDlQ/TR ._)b 0}?9Ӝntw(\ܔש|VbwL}v_@C^,PDEHw vl;O d~ﮪJ\Z;oUI2z =_zUuwƞߨNIϨ?9  98[`[/P WF5Ch4 Ĺ<+΀\gD;g8ϐ;rDAu d\Ȑ.v%:̸AoN"'α#:\gD;x1^ ('38 :!.GK](,ȗKSu>ԺYA5ԽL3Q-Fzxi)QTa,'3>_jf~;q?AW.'%[1uHc`@ʣT!gDX_:gf1G)Ŗ҆nG;X( Sp, ϸFHi >%)S'Qv yV>O@tlW;53d/_e<=RqJf.0?#%>_'#kɉLuv A ) Ag@uEA@ 3ӡ-ɤWJvD]o 3sTJMN|] ⅴD:{\RS?AI/ӧ'V*-V$>|Wۃ͝_(G흮9{u@&BFp++…e)٥f8,57f"v)LhRS?An'$8WC\_#U:/&֒œ}'?7pËdmLdeJEBF;A\t^ZjNvb⥥A JꩊҒ0|1?gpڃmzr>`s0B̤u@2E+^);#9hn3 >VUFއO>玤%״nvХN.#%N)7;53NhCq~DxxdxxTDDTd$(Xvd{CBe!3 AAM0 yabbbFDD k㳲ryyy9v; $H )$@ .!UkH9MS6GMz \VFVWTVVVUUUWWԄû,uɦYUmVlo/سuH { *XMm]]]}}}CCC#4aK~^Aysٽ;&11ƖM-O[~q3g$= F;FثqsOҵ^n9bĂl AH?/s5+a8se~M126Z6H'1'=;;'1?~;r$((&sR :s?,$Lg`ltԶ[CBB/ZD˖.2+WCra\>;=;QQT4{쟴!CXYZR"kLHvkVVv211 xW X`$;4lyl ʉ[c&/O@NH^d2h~ |E%AKKNwz6x!XXڒvnLO<>|15f^  } |%P'OΟVϟ' ׭۷'>/--jkVTX"$9:eikC`)-E$:`×J'+OcEzA!_o?C_IE}cccSRRt(̴4=~C>^ii)AMJ?AxrT'RQm0g<6s̷6VW5=m{YusyUK2%ԔJ1EQb܍3xmE_ZWZTDwӦZSDlB'ujZ[+jE&]g$MB& uZ%I2URZ$EGRTC+?:P#(_H-hB~6́bk,I<mfz>et/9C-Z9\혞;sk$s>䊂Co:ޗv%]17r]Yp +XD"E؄1TcfA[KGq%Z gkc)l7aԀ|Rھ}˗jkkۊ Lv%(y^(Σ';,Cɱ5APMJ1\ 2ܪwmnnNe @1* )x(*sW[4!ϪJz8 b; Az~룏?ϑ}=q;aÐ $$@ c! *6c4(=LzJcN9 $ut' >ܧ\C/P=$B!4?=W@|!_.0=Ǎtij[pUQ`^5ɫS657O þGTQh\ahmK㧏RBKBgMȕbsnWX҈p?c =|b$ ~ԩ?XWUz@P4UIUCht<Gq!=\Rp'%Dlq-<82lx k '7-)\`sݝ[ׯZX]0ykU *#m{-P'2?fmnXYU 3 Wسj! +-BfG(keWol>*ٓ'Om>g"Hzzzp/T[ ŌelII?e#ޒ\}}q]y1==oy"9!푄>?㰞c:X@4Jz& rv'3gΗ_~9a„75myȱҜ ^l,ӓ`dӧ䜗hP@x6o:+5< {6{jy=$ S=OO$gNNNzpAA|Ņ9-t$ aaʭ(+ :7zݗ.*QyTkqQQJ2=镕_p4~{ҫ#=8;vػw/ǫݾ}Y]|gҤI{ӧ=rȎQ V\9{lUV 0qQniD!ޒ%KBBBVX1x#F 2nܸvȑ5dF4 y`p^nЌ5$R[0tʁ=o|g 7s֝8ܐGniӾw '1~1cϝZ@+ `Τ_h,1xi)~.v*s بH$=O7W_/@_0^zj gO.=i 7] 3ĜHϰ='dz6&Nc^ByJBc^{}{u3~)> }\..^q*һs)3 ?zƒhlfgf:hrbnnwn~fWEyx1< I?5܄1?׿>7m@uQ3D7u̝ᓭݺ={9sw]vM6 0oۿgϞڣGBaƟ#]hyw&F4بaC@;0boo 0   :t'|&!M7a6qzBp={v `XU n)v`k<˲ev{[[.\8A@P[+*_a樰04`})# {/gT/kFoj¼ G*,^ `*"r9|x/'' 🞞{ _DBUY"VHM*v#-Rw )hH2R0XP/n,#>#,_9@  M?٦lK#Ǽ5tԫgn;kؕO#`DXQΚRF ťVIzL;[_uﵵ%"Uz79>.9aBlL+׭‚"¢#[]1F#mY\L}|==;CE`\&c:/ټ, b"9a8Y?aϕik׬u0gıcp<:Ed¡GGcKN#d޴wYٽKE0xm \:uڵkqܗu{聻6n4`h6w6dlW'NAcǂY+++6݈K3 >!Mj0 `]]]ƇU+mrN;G1٫ n۶ ֩;EqMx[MppH`k??=w[sz jo?n~3ǏnrBq :nK@‡q4k HOyTo eA'l*1"žb}_`AAAII ^FpPs  A)RuHOaJaPCw \xB4SAz02ßS9MM,Io6#^՜ j2 iQt>l`ӹ?{5ns(_b.#АG~׮kڊ?;v¸PfȑHH2mnaXn~HSLdxVzjFJrZb&?/wQ1'ё uAd~z…_wdoӣ,``,IWjJg}FJVtxE5HOݺix␐իW1B55mڴQFA+H7O-N!رe888Ϙl75sLkkkxFO?Ey}cw|ȲgϷ=OӧO4i\~=lY˓'=:{yhfA|4^q-ޓ'h 4r0Asz(@)]|'ؓ^:_|!:.>Y6]녠>e6, |SwEE@ |Z<^~Q Gz9R'j 3sP# ݪ0/99 Ï Y#CĭFTUDcrKjR Ǥ1"+p]4q-v ͷGu7|Ȑ;{iuF4`F poRMUU|-{  ss=x{zdg=7M*u#^zJ2Uy = !|>ATPxܫVL̂bP8,!`ltԴɓ?lׯoڰa5/]rtt\xA~͘#}mhp3Uçn'lA=03恒|}YY`q-LOHM|6j|lH\_!VA([Ǖmۇ{vۻw_~*+s͛6&/ph,\\0hy'.X;;˖- YjՁnHl[XpfyD>e h 3moߜi??G=B8ՙ@zBN*+)~ \7|"mޞ9ٙ}<=agà>y=77{{CM,-8xy>X$fip?;ghLX0K8k/]0 GX/-(5)~h0Qټ1HWCWZ|L1HՍ7Ȳ]qCkVA1؝ Li5q)i.yy5yqk"KPAA8K @ ⬊=ZQ{ֽސ,PqomZ{$$$&y?\~幻o}$kXeý,Y5_/.β 1ӧxO$Z+ճi$j0cK2H[C " 6r0\+'ӧ?F 7o޸qo߾C{VZ{ p`¿z%g%QP*JH=zpOn{W.Eur4LJGJ y9)IuKRDjRB˨dfg"de{2*0O$[JKF{MCHplCACSFT>BB#=WժYD{M?sBC'+FTPPR*k<,g3^b+,ӹL  `QQ;@B$=SzASYIqqXx V^n=gC<`(T)TXwE H@ HTEzϠ5@ @ @Dzp@ HHOXDzOJX4@ fEʐ37;◟KϾ@@ R_ԃ ޿@ HRO@ ӸZEzaESS/q,z yCmp@pgo W^ ޻''p9ŅW^V,>͂p@ EĤUDɆn<S9VuMuoۺu;oWm6mذm欴T'5 !<53uC^-5K|VLz'*+=翿z#u^x UQ;dmZ.1![BB3!kzFSuڭ/=nc;B{I1~"*^wlW?s'B5ETu;jL^+I/:xuƌ% CA K.Ueۋ:t@ ̫VL;$kh%TZT]*]vdޓ6_aYq;88,@)QJ2϶ɗQ_G^krZ Tste'R"L;)؂l.957ޝJbr".&ӕ7+Y 1(RX?riyEhC5y3?ϓCmb^6$mVݶw5>,;\8wFlR" u;ߒ JΑ7oV3 wsB/dDo=@ 8ڭ&[')=ڷ^W{o+%\zSФo+/L2ZNPIeZ>1BzhQr 9I% svY2鼋3̏|DwpĦxKkwuticI!{zؿkkk(%6kw#(P!0)wfWh=?vFɿʾwht>Pq4L4&s"绸:ѩ(n J\ܪSƘٝůV;v=RԌ;oaq7u=^xKJ~n>犪r\ٙ9Y22s'F&ζX/^:;) 66FHO:]:xV4#H}/>FㄐR͕۵Lena|~;qà7jfDz2=Tn)hҗ|w9WV~.kqwOyꡜS=¥;oKx^ 6̢$=]3kڑlǾ6Mm֬eV<AK\+!W FR'm)v{L0Dm-*9T2"N㲹f^戉]C fPo_QQŰ2y%9jȖtaLgnuw&S^&""N)EX J\ܪ *6U5gAs͎vs9I~1,׻`hwUCRzCMh}74+ 7gzz.RT#|/ѡƄRa!?/bgN|I%ڭk4qNjrRgNcFsTޮ e]հn79y;|R |_ɓ'}7b2 Cz -1 u0TgHz81bGR+[薜RPxn낄F !eHuU' LѿRħwŊ,emNmə"a%WgL-5nGt;)̤G~4pႱ}=qORơ$AQN57lzo4SyPwP^{gxuXmRq{~Zq?nǵ Ya9n|QƦa棎((Z]֭ŤסC*!HO'6ݭDŽ,ޟ`U5`7Bz2=Tn=TФԚ޳;]*9W-BI")J;_i7l^1i#LEUYUbw&Qv2%f%k- UFmz,ƑTyfOG/swCU&9S3JDXcوdNi &F_,Fϯ*_?ݿw_EjgNh(G%} Z_&4***n޼Y-֭[ DzE[]v_5jlLm:p*NXlQNn,|K2;-hss[w3J':8WG籪 q)qj/7d'IONݩC<2koܣ<1ӨX<^;3vWq;srOғ~(-EzΔv!GrRwLXgq!Q ZdDaھ$~ Z!&Mh=E[4lՀW D螷l~tIH]Qf<;RHsG9zGN5=Ə_8(0'"-/'o\X]AਿН;]-g(<9W,.,ԃvSx|@39p5C:d[>Xp/WFjQR;1ӻ;7ߑޘѣ,sJJ:uPW¿>m[OBPP۞u =Ċ=u galْ2dYB Hzݝd6DNf~ƧƨmLqM''Mʒ{Xk~=k7ܣh=e̱ZWS}'dܿ1'(-ws)mGNܝ̭LT!}:kzG'w n!#sX̬-L=2'yzˌО;8|NSC^=GRvޛ[r9vxEKGŗ)wЮdUD*:~$I ˒GlHKE\#=&iȶo'Otǐ7F"qP۞u =TP}0к.}3^NyEЧ5R},ej)1쭿O>CnZ^3/x*:Z*\Ozw{]o)$E'Iz&w"P_5[F=Cdns~$刄um%>fN까Y\9՟k;pY\ř;N{ E"Cgdg럅C qsۧ#^dK[uŧ“=-H/aPO;Ө¢}yit..),)&mg%D&WQPU/)QU,J(uúu^)d!ɫn*J2B_ߨ3V5PW =dUNӧ\Z$(*asBbp<_f<~ =D/q0,O% F78|r< \Η2ŠbU]CzhAGS~]%ss,!*rS䇓TxpR7Tp˔vbѡ/i.9oQY(.t[Zq6ˠ+p}Va&},J8qZQNuR=Tnܤ^ںW*싡=-'칎?3ac%6[ٿE r5 _,^W$X w3@uGX=jfN_rGUǨZnK 9(qgHod<g\4R5LXS3Bl>WUR(4]/Çu~ٴeUd?G$NRy8:*޹!ZLz̪'577.)O{֫IVw3+ŇݣQVM\Z;o6sWxx,XriEb nFY>dOJx|1Zߴ:jOa='=ɔ^|ܙ;w<OI'r< i[0DVJ__~,ɺ0vE^C.`ih,?.iJ?Tg)DM9$_f,PHzԊhIQ!b.;ŋ S"*p=|V&zA!c]OesZCՇLe>?h:P1O<{A薵Z(0^KQmu#VPJnϞݾ9D>[K|.;>'?%' !- < 3C?BS Z׊p%=oYy 'm?YiOزiaSr|n5/{V=gd$ȅpRҚS0S~==iЭ;bi-<..#1DCe$$) 9D>UlF^VZj^v 0NjBxjfOռjZjUTAuCw摒埂-@ Z Eſ&CzHh%@j.z@ HIH@ HIO~@ dh @ '=>WP8+&8J\*p|,J8|Sb"D3y . %B,Ia沙&JrIrf!fbfQb`>T#;++#+G)3=+Ji9r3Rp"=%[$NCJFJM΢I2S3Q"+3%1391#9!#% %#%'S xihO)ZZB|jH(rRlJ|,_MG&TRդR(q51JbP x9R1xsX_%5v9(Kq8y8F英.օب 1bΣNGNJ:w5Ջ焊<{٫\t򅳤\9ҧ/3Ο:wyRNG;}ԥ'ϝ>{2 'D$#{iJ<9Qm@F4s`r[Ajet0ƫ+ϠK8]³RpBhRÒ 5'&'ϒ)x$gOJO?zROc=֋gϪ\ϟc+bD+WbbkUSJ毿ߒۿ[$^,X$_yNl}*I6rDh[r"7 oc hĪK*9ؒ]r@69Uq_ E )D5T/XO&'xV#D&3]GΆDhMh+T\|KвYRܦ%-@ = =`ңHP5e5%;M397>k$Izo@ = =`IùM4q7l7m5-pͿ\7q:ImP%6-F9ȹɹ@z HH6؀od I^Ⱦězaޛf_zo/6sGMob?(xM3ƽ-vz7QOx\ =`~qbU~-^5b"Q8:6$ˑjh U﬿>}N@@z[IHYϾ'¶}@n|@/Uhm^47U?ۅ"@!HS=Udc,꿹RrGXŕf]^?e#EO,<>a1~#~cCw'npKcdSeG8 kV r[[>~ =_ǧ238 0мk;wgv&*kV[߸v0'@z&W-3DsGϟ.EIZV%?o9z|h4״T~*oժ* Ϲ~r-<Dszlܶ&WMFɨjDNŽQ]MfpWͰr\K8k]{o<`0lڷe<*GF'mꁑΧڟ{fZ?ٳykY湵$nst0tJAE[l;2hmgDz}yGx^xծ}@Bf-oCh]io> GoJIĶûz%s ݩ)dݰcLVώvsHSǘޙήz ^ SlϾR` HO>d gp:Zlѳ+*Ysk s"@AIJ7!v0ۧ@zт&=+/ߍ [ pxHhݑ<˒%= ?֔P= Ҷ8[xUt,sUȊ\`{ѭT"kAGu8[<;æK eF?{CfDع'zO:Cql#{5t]|2@N.N3jbq2 a-8/,]z~ę<k^+m[<5'6g596"W* >;,̗h,ѻ~^AId,GZUPm<_{R ` 6m$IbjyXkm}[9t>Em uG}{T.jۉ$4.7V[6Q-qY #|Z"[igsQlMYSDvީڪA?\E/od{2TpFHO}܌9,i;bXYǘyE͘qĿ?};̾ջ BQ%Yٗ6y_G^?̩> ܴ6#ãY9[|p#.ɳ _]oЌ= 9yIۼǂ{Yi{'gr^NΥt{NӻI VZ ݾS&1%#jzѧ\}s\&R}<HWxK80c=d'"Wzu5 nddDT?#PqЦd{;;9byHcTxՠ5d`uu ETQ#)|OJ7oLʯ-;g}X"4͎YmoὟ/VSh¯SѾR ܐ(Y>h\o?OcK:K}r΅%D'`krQTWn_ u$L}C9okEBGZr58on[3ymUfA|@@t/uqTkzk&J˯dr=l;I#特v=]SKOMGӢ߂|T-&~Ȋ^l_v9|̪T~QZ;+8dW+eko]YضvԒBH.4?e|$U~gQTLyEyͪzg Ҭ˒БLK֬TrLnIe=̀F[VFzEQ?D ] :UptodK5jPv3C|#=+gQ(56SW#q썲>w:ZVLzH+EzH:uLh:v?!q9U*#O>,ܫTPumi_vEseDNAsMg.Y2bmr8W63i㹌ܜS?{YzlNPhIժDyw&#>wP6;7q͌(ij;ln#8vԒbsd%&:^^3՚eHA Ya`H| kW^^aF̾ /q3cκ?wk<;'^HlM^ZnIV+.T2(oȭTAr8G>tT;n }]m>eY!D/ݎ 4G}gOOJ~FWCfu(t^Gh=TZ_V92kjiAMoZ;7LEc]ͤdܩτZ͢Z?q: /t ^w%x8gp'[O9Jwu$=n^ܦ`' =}3g SlaN}QOeH;o_#HCGG3vz݆8_|~zF#g71QҫߑJ;l7z򓷎 NP!˘f2/5kocBaE#/sL6Ej ЈPj!;J;3w0VIoK-簍j~Yb`|׶-@z cHOVG98Kz Io}W5} MQӑo@Iz"؃yHk6EH%Cټ>B0ڿ:4Q҃6Z@qDcPi]ĔW0>UBGEzl7IxH,g=a1aq<6rssXH9̜,f6LFRR^fFާf f}zjNzj6ڧdd%g&g&g&e$g&'e$f&'f$'`%a'&']OOOKOM@kׯh1b}LRĸ+IWc`\Is)1Jt<.GDb\ hu!&| Gy>&Ջg\ܩKN}ٓgND=gND">u`#ԥ8fО g=hÏd|^@* HQG_BQb9 u2;#bw5).2(.ڣ.v=hiԞ솸3wR&fΛp*ٝQ&{wvzJɎFx4@cB9Dddg2)dxcKdr@ $jX$>[EHbJ~ VAIaA)HpF қen,q vR7+nUܹuͻoaU޾GN}w ݻ>=JGz'z1'Qz9ҳH/?CzX^xH^FzH 'ҟ盿zo7ۿz;w$r oA>'ɘCF۷8 3lTԡA' &w^0&G5/hGųgT@]ugO.1ң'Hdow.!z]?"Q=u{X}ݨSu|+ʩV9PV j(/-HIqV^qFQ!v h Ñ$Hx1&j'&8ui׾m2ʨ9xho;[3732qtDz?a6~e^ ( !ϲ@ H@ H-H!bMonؘuqs20>6gAn^N&ǻZۍ|<[ cI  df>#8hngdP TgY[ fe}z[m:Jլa&%` 6㭾VқDs6HXji/`%PHٗ>)0 wx7c|ZgΛ`HDz5!#XH#Tr2 +x@z@z@z!kݵaՕWspεHo`͇Ђ'ЅO&ξDߡG gѽv^U[C y<=ӛD!t:!q 2 " hK,`D>ATHHH$=4][ްsp?\~Mx0m.Fރ-Mw4G Nx;[[=kL3ˡ4_+~~hvJsͤ7h1F8M}(=hڟh2LhT;Gg|ִͅjռyXM ^IouW+l+#vspεyH:=EnM8@};v>d$׍UO 32dAOC׾#=w5׷ʠz9t10uOuZW#y͝ƒhC't4I4!M$tDF&pOxᎢmZJ>5]">}OҚ_kٓ9 WSYG@zyͺ_+8D6#5=5~Q/ڎ8rѿGuk8bjwG85?W|cWnФ]uʈ=+lWoks g^X7ғ6"|uݿu¼竨ݽWSy!ϊHϗq'! lNWiA!t{mB4 HyZWͥ`OUo[ ~k΢Örѩ',][M4m;}`˗;/&堁5Ԧ$eIuעTW^-] 98Ҷ ڍnzV)۹eiS8E)ZtɏӦںE.ِ`irRirhݤI2>d̓hBX"_WI#D2lvwI`з;B ԷSsm3_&W4Y̹Ko~Xip΂eCÓ޼ueUW^,Z 98vFC;޿wǹSym&NTHzWR(طsGHP ϥ@#O&fަ&ĦĶf!/lӋ 4Q@1 1M˖F4&gf4!1~&]q}B#'R/whm\w1齢u5}/WaFvI\ky{ ՅULA->h79ܑeR#OV^Ku`w'a`3菆'9뮹תϚsp%ɪEii <-).B9GU'=^-{Ƕ5כNol b{S|;F A:DƻNy Aa)=Lz7go+fذ# ~9z~=e-+O4jPXc#K|"g5/JVxqߛI޽loa`"O+/\TmǛH|̋6͚`+[as-p.oEo*9%7oV SDzQ|9}˦?N_iW 19Kz0i03M֌%L4B4Dcu!aO4koŤgܪy^Vݑ1iqt;2n?& ]4uWgFkZrՕWspεu3DBGټ>B0ey H]=2pBg3q!:Dd>3|E-;}MD~G(&N}oN֦]nƣY7Q_vֈa- _h]}7'/5֦W*ѬOYZ>kcoeYV8MoqI_DF--.vkm mZ~{ou0wb.԰,%"=^Lz׆ho>܇<.?x2 7w%'0>GT=5vXΦZ3&H_ D Hʒkm+[`p8Gl¬jp-&r4ﶍPIj^WDz(t"5úusz413Ҩ-O:lgl9`81L,|]yM 6oodyG~*!Holfe}z[m:JլaF \I6`UeoZpO\hO6DX'[EnM8@};v>d$׍UO 32dAOC׾#=w5׷ʠz9t10uOuZФT/98璤f%ה%At 󞯢~t^:WN<g = =9828qX O*)CzUNz.ݍLz:yҁ`sMϔKz(!WuG'$=f" ˖L `I#4}$yb?&=@ ̪H98Fz̼N>Ez<6K.%'IOuѢoNKz;l9m*_4E+.qڔ][g = =98Js^zJbһ'Kz%};wjК^HP\Z(@z@z0wspi^jMJpD{Ƕ5҃08@zsMsΑ .[.e%Ij&[6qU?oްNSre9x҃s$=#S^>+"\{wO\1kԩA#hT҃0 \J@zsM#"B>W.egHEu\odps"c"ܳy}a%ʞ]ԁAU9, sp5l^NV,j {rIoӺ)@a6}<?%<g = =98V+^nv ݭ6<1 p2Q(@z@z@z@z@z0wspW\X YH/w-"=@:@^AB|kŋ6jee}zF<[7-\ڸx:[cY`f0k?ԲS/OVg]ԁAE l =p9G_Dz%EˡHƺ(޽IoCĚ L{; s1je`h=|lG&ܼ|M -Gw7Ax03B7H?|Gp ܴȠ@!@z@zHHnp΁H{^&ܬ 9RƻZuu Ds4pG c1wqO jx2sw5y:uG9*҃0 J@zsM#>I^iq\gHIuQ⳷DDn=ڛO OL͝}qɉC-@Ϣ{ 57֯8:,$=@ ̪H98Fzy9|!()KzlCHzLjFzkƒicu1l@lib6gQWw?ZtROrq^cXX!@ lȰ PUB3k+镕%=N>E׶nM8@};v>d$׍UO 32dAOC׾#=w5׷ʠz9t10uOuZs78Kz7J,EUN|yu*/HH< ֒^Y\q5^~^6Ez(t`ώ&=_F&=Cz;҃098 \|Ez7^C*Iu;5^eKS>y<1HH< V͊2's^^~rޱsӦ"pESbMٵuˁ;҃0"=rX@kfXOHz,5E@z%};wjК^HP\Z(@z@z0wspwf\+%;MQ<g = =98Rһ}B.(Ez۷l5~޼a(|ӑs = = s78ZIzn%BEɽ͢H%wd\1kԩA#hwHHHL,jX@ks4pqYyݩ%JK(C%.J[Vr}}6 ̣<#҃0"=rX@kdqBһ[y[.(-3j%M"L  J `\[IޝJW~TY۸.b+bT@GU6 'Hze I/"Nlץ^6.^tW+.Cw31ޢIoo@JOObX33qt\zy'*<g = =98Zһ{G.UU'=r2EvK"քLvfilPY??W+CcKz<^^5EqwSJ ۵Po玐@ Z @K];?{ٷd{W}jY}kjuպqZ-jٛ@$Amhmb-ɝ뾸Brsn''?sNL3>l9yukN;dJ{whI"cNr W35?~ 3WRVzE)K/YB/] EgXsx҃҃܍hnx{DiTzQrEDz^^pAzf +3@zhh^ݤKѣjW\@Az}V #aAzhhnH{Z镔kH.=Q!҃ =L2 9W3鉢c_!%%ҋ}]qrC Azf|=hի9jC&=ZUҋG * =t =Y@_‚kYzIׯӰΐh&Oޓ'O4J/F\\zёnn1ёWR.1(ww7VLD\K{%hX҃ =L2EG@zhh^ͤ$)޳gOJӧғİ} 95꒒BȃW3d]I/MOlmY:~0 .Q!=H4ܐs{왒<+^Lz qڏ|-IN'ѶD2'e&idƦf%ܽ|Բ{_Lz"H҃ =>Cs4G՜v\:Kd!^I/)>.-9QSzv2IR&QxɟHhe/aؑwq,nA.JHN8ƺSyK_~0rLzipH҃09yu^DD[Z<4K/1.65)Amxȕ$*IVFAaQbZ8`Ǹnov`DvcŒD}S~$+i)ΐn4Gs+ߑ*=O%IRՆN7j$1^$=ZJb|i\$!q{o%bđuS =H!=Hh)W~JI/J.(bԆNWUSJB\TzQG#%KJwx!#$o<ߎ~ rR;z =HӬLzQ,9W3EI,l_Ezq⤄XaSE퍚U^R|lB4uBo8󎅋]7’8HCgHs7AJ_mh5I#@"IO"J rݢ8Gۊ 9!^B\ . 5"rmm\pl’1щq  Az@zhh^ӎ+A._~yVz=dң%پ{5IT\z$=KSt|ﭖbcD2G Ou"KDY/ˤDCzi pHͫ$"&ǏÒDl_Iz 8%JS{Hby:~$rZvfFLt^s5Jz Az Azhh)I/",4-91^"$EѶnO<~̌ʏyiX҃ =L3H Iz=T+xM)^dx(:=-5+󲦳*lKn)qhEwЁCňEy9kyIi(!=H4+lCѼI/2/dha!=H!=H Mz%jWt&y$]Dɥ&v|T%CP4 ҃ =L3s =4Gs4n Kf+)~VzݕK/<ҋKSGC*DqLH҃0{HͫWsq˥WH޹äGK}\zUE =H4ܐZݹ}["ҋcaF@QeuH҃0ʤ'ߙ@zhh^ͤ/.^{jwR鉣پΣm i,;9NeuH҃0 =4Gs47ݪ<.-W =H҃ =<9yIjwF%^#-r UrRMCzi ҝ hMz(&{w凞^ؾX.=R(\Taw&9W㊓Kjw:-uAzhKzwnR+t].^x(K'D)壚>V =HӬLz hLzqRݾuSJOuAzhKzn^~^fCH aBԦTa)Htg9yu^H(Tz7J//7[.HḢ =H҃ =4Gs47h^5\zVFz\Taw&9W㊕KfaZf_eң%پN񽷐Azh~޸VzW35K/_&^XK'D)壚>V =HӬLz hLza!ѥһqZ](^t$)-҃ =H3 9N\W++@ʑ&d"=Nv5KO6多V =HLAz;HͫLzׯ婕^ƕt"ؾ#҃ =H҃Ѽ:IZ~Z]IO,Cs4G՜v\WK/Id:w҃ =H҃ѼI/;+Cb5I#!?:D.`JIO>RG5}AzY;HͫB%һyE$ҋ g:w5( KerQM/CziƇߙ@zhh^GSzl_ 9҂X*' )':Azf +ݙ@zhh^ݤ$ cʸVzbؾ҃ =H҃ѼzI/JZƈ =H҃ =Hͫ2.D$=7oC =H҃ =4Gs4KOU+QFF TJzAR>cuH҃0ʤ'ߙ@zhh^ͤ' DJJZZ鉣"Ju )0HD)墚^V =Hӌ3Ѽz5WLD(dE3ђl_AzAzhh^VzQEz슎 =H҃ h•[x =H҃ hU*Dҋ $= =H҃ѼH/5)A芊J޺F˥,`(|TaIO3ѼI/X^*x *^x(וI/.'28P@I/PmI5=AzJw&9W7Ƅ0%'ĩ^h@.P^\zt2QJ!=H4L =4Gs4^i%f[4)>VBhISxM/҃ =H҃,][~\j8:B}<Vܳc!=H҃ =Hͫvo߲u{wEE*IcO[7mCr" d(|TaIO3ѼI/(Po綃wVٯZlY|ݚ7pqǡ=hIs =H҃ =4Gs46;qÇػq;uh':}ء} GZK6多V =HLAz;HͫaB& '9>~ /xnKOu\rң+,?REaw&9W,=Z_Ӄ =H҃ =4Gs47$@zAz9bsr Xx+:FijX҃ =L2w&9W3 D:K/Tu/K_4rOEzjSNauH҃0W3ѼI_پΝ?OOЍB Azeˆ =H҃ =Mw.A.^I-*/0 KerQM/CziƇߙ@zR%t u-I{ҋˁ =H҃ =H Fz[~>*M/',P.@JIO>RG5}AzY;HC j&@L'H ywһ'GK6多VX'A(Mz;HC &= D{*AzAz^>z AzAzdGo KTFz J):!~gAzWYz$-)+ !HoG ^K'D)壚>V =+|gAzWͤ%Yz Z>>F_v+G/K6多V =S^҃HI7J(YzsdJOAzAzx !aKUAzAzJ޺HBң+,?RE5AzLJߙ@zUG("u-uyzt%ߧi>,|jX҃HLz !^5Od }kL&!Azgҋ = =H KzbHCH҃HХ' AzAz,HCH҃H@z^Hoڴ N~A =#҃H3u>^nA =H҃H3 \V:LMW,^Ȁ =W^ۄ Fv^|ek>|.kꙛ7j>O/mQ:iyKnoZW^LpK?mڴc3f͊H+=OO AzAzU$VGe.W;X^yF -z!-^\}۴!?nݟ={~Mˋ]5|E =%{o!=Az*Q4hS-Sz_4QcnWk-#izSIy.+5h:MuӂY捴|Az򣷐!W%s?%98Q^y|ʪ;-ioX[23/O^h֬~曘UUӧ^ے%7GkjjοԦ7Ӗ_ ]IbLkF>`Uꫨn&k͙T޿҃T^UugwMٳMv27oHu? X1{v.=i ݨ1l V:x&գTZi81uTޓ| =HH-KX/ϝʇC4Fe40[{)ZFGF/5oG>NϐYoPwSvcvC>` [OY!=^Kzz?[Fvh㪍MoOT{:u 5a Ol5W~jPN +MMkb޼֫M p3+Zєիcҿ#' 2#GSw"ggΔ|}@fooWzxGWil3^ySxZviӂ*+ft:tkآS*vilmFܫhQ+Za6Y֭۔] -#~Oǣ! sU.qvh,dYŞff+CzTz17l-O]V_ Ʀc='~}ٳ*SOν+Mvb$.T| =H5Hy5]tc]!?^T9_5TWYڵU#t!=H҃.=-3aYvML*o?ڤIoͭޠAsb]˖OGN I>Wқ;7MGВ{w+RUC^*,=|r2^ =}IOgt2A~|9 pK*>b{wdo?95'@z^UKgEzm ۓYy\/`JbGowj,[v?g҃ j!=-ÿ[V#O": gP_?vY+~ȩA>҃TzRQz$-_ң[ T^rLׯ*7/qÐWsBz~ϖIAΩ`Z-!GdLMS*|ULd7l5[CU?H/?NIz2ЗOYiԨ2nIvczCHҫ3 =HOdze*/=vF7VS =H_%=m/J[ɿm*gaa'3fDZeM j?>9哓h֬3jL#kQۅ sN0`!-WUҫ2O ?.o5M<5'@z^Io ~FAJxҋߖo@>u640qdsAz޿Jz;[7Vtw=Y5#uT| =H?.Zʅ[|-Lk]zkx';Cc4ɹmItjյG?Z+3%Nڽƍۙ71Ifҭg8 ##҃Zz;[v}VV>9d=>sdЭ?9pEk7΂Jr}k>9uT| =H|-B׀1d@z^Ig?+mh)_T{t㷡S*vAz-֫gHﵜAzU% - ^zѭg]|}k랪[ԠAz^Uֶ'3'khѵQ6h`#I wd@z! -ÿ.7isn5jCʚ5kիgѪU2ى4qpn<k+Zˇ~oaх:ԩӸ}OTޓ| =Hҫh  {o!=H1' =HKF/H҃H҃ g_ڲ{65573ߴi޽5+q =H҃ =H҃H[H҃ =HC =HOK =H =HC =H҃ =҃҃ =H =HA =H҃H҃Hҫr;2 = =H W =HAJzn =H =HCÓ1oLj =H =HC{M/^,HK =3*Az-!IOAz@z-=Go!=KoݺL˦M;7411[i˖ bD%6l[c4i_Vݚ5kկoվѣ֮}Ǜ]Ử23gnިm?Ciy+lqNng XY|=?ƌ9ڡ ׬iFkիg٪UϞ Az^Ho669fS/..={m T]wȐ:t+}jln[d+4oG?҃ =HC =Fқ??ܼ#Ҳ{Ez=zO&&5ͻ/O$QÄݾd-і,Jx@޽gCz^UKAz¢ { 5ӧXQʕ%_3t^X[23/O-_^4kV\~MLL8b֪ՀV6r^SSsv.zUaö+6l6v-tWխیպBVvqQמ*{5v35R} =HC =HJK i[76TӦHoTǙ.sF?Ukts؍t/-SzZ `w i͚W@z!gkذ{3'hк/P17 &=w|r2 o/鮚C缩ԨQC~6ln_`:W+o~zKK҃ =H =cgyp:\%=?x}MJ|en{UP Ə?ӧϬteѢ|@ '=1!Gz.6mSo[7jKңBvW^_ѯznc=; ݺ}҃҃$]'l}WtܹU!3chre _`֬8=S+mw҃҃"/2u+YL-֭~Gi֬3KJfo걞%Kn6kV<[Դ6v k+{oݼI[H҃ hi^zwpgryy['=xR{8J~>H҃ h*=QEW'G= Ia\Ɏya\A߇]K?[VJݏ?(@zAzxDs4^!KBΨ xmeuQ~/p[6c<5FSV ]x.{yÇ =҃ =<9CzHM^^]r9w]xӧWr "(!)+iYK_gh[҃ =<9Cz:J]\U@zi9ѿ҃ =H҃{ MG>,~__.t~-KAz!=o^hoWgC =҃ =yr+i)r-7;>}!S't]n6W?KFw1혗q寿~$)IZr=??~gӓ!=҃ ==H_ksLS7_;k[7'?)(n\/i)ڙǒd%;lV: Azh.vn.=:zpvOIu7G=q]ܝ׍~x5 ^c#^B[!=H!=Hh搞(W[]ttaujn|3?\V~ IA[-GlH\w!HCgHs79Ǥު:tmG/|ۯҷ`<(@vQlH[`_/HCgHs7Q#C~siTs;w\>5rK/r{ s^)xYD;!=H!=Hh^ߑrtއ]`-w+^/WңxϞ Az AzxFs47wdOzUq4ܼWVQ|t|$399J ?{m}IG?@z:Cz9Cz>,~@֗|=IwKCz:Cz9;2Y/>μ$P!y@z:Cz9CzPz/^$)NBtHCgHCs4Gs#^鹜;aO6޵k}O]~qz Azg@4Gs4M)Ku+R} .ꣷpCgHs71K/Zn( @+#{Jߑ Ȁ҃܍h驑ގ?ܺu˟%QGvh^滴,\zuG(~w%=)+;Az AzhhnYz8>{L;ޚ|8Cƹƻ^h^X~u,ƧO%=.NCz:Cz9Czڥ[̢G']K" k^HsٶVyΝoC҃܍hnHIz'wCq' B kOv =3=2K|3hwd^HCgHs79|J޼ƪH糧 5/Yza~OH_{XR\mۼ\d|73$Gd(E}J~[]Ey(iRň*svƗ_\8u҃ =L3H!=U鱷vqmBqG=~xt1ƻмr-/GUz)˖,f{7wׄv+ AzhF*=wd?|Pϙ/S;q(cޥnaσO>*`_[0mٴ1A"Azf9Cz 2ŊHzt|XʡCJٳ^Z)CDq|H҃0 =4Gs47r鹋RU#E75Mؾ=~;܎q;z4nx-.;vЯGoDq$F*=U&Czi9Vzq8:iߋ>…⒒sD7d7fƀg.Z[9Ӥ8YUzJSx#d gҗ33ǙÙhZXq|H҃0 =4Gs4T) aj|y7.EEEĹ+#d #ѽeˉMǡȪSڜbpf#r&s5rft}0W?Wc aAzhhJO4dsC.F۷#CΧ]XXXHnbh6UB#d)KOis|&pfsxL! 9Y Τ?7TWQ҃ =L3H͍SVH碙Y_~I ;wܺu Ezڵkׯ_AxZ-ioY3q(4623͙ό[j9p^&5rBn 7ޫi҃ =L3H͍SzZ*sn]I&NxO(Xb////Gvͽ# ?T0kVyWB#JOis,8A7ńhΝ]0JÔ;`-4fr\2>Azf9I/"LLgz:.C.?<х1Qļ쬬̂Xh;|йo9_oY-q(462Pvz"sɝ9p>5jr;Me$@Dv wN NaAzhhn驓^xHphϩFo85O[xi۶.~!]r%==sOZ6ܙ&ẜ j)CU9>`lR aRÅUFǹq5s87ф4>Azf9<=U !A|N4kX˔vwٙļ{\ ff~28%L*=ͱ L>fsf8)Ts4rǸS97΄4>Azf9Dڤ,s̺cZ̸ 9זf ]s+ĭ0%99911{HGr7T8`6Wv\tTwG֔8n*g_@!=H4SPg[=f̹sVf~;~{.{;}+%---++!]javwU uǡT98\ͯ9&\S;#g:&rcM732>Azf9Cz 9rO]uPǭŲ˽{|n}bx dwr}:P>SڜRޫ#}q8=ə L.8>Azf9_!=?8Լf皛yu0v35K`vuYf׆52,=fo6;eS|vԫXfFpL8Az:Cz!=_)_/o0vVoYz Re(1$a)j2=KޖmԵ՗t 3$=}{đ(E>y;{V5uΞRXlL(2¤6'yysaAzhhnS8O/̱N%'Ծ2!4>m98JG+Azh*UoW}vھ57)!NX}4>mLJ =H Cs4GsHOQz~wܹno74&LVX nNi|H҃0 =4Gs47REӗ'NqohLg74>Azf9Azf9{oSH/6F#b7BzK7otC{͝YxrGg흚dw^zYjܽK ,5ݮƖ:?E#R{MoԨQƍf̘1o޼ .]./^j͛w޽wމ KՕƇ =H Cs4Gs^$(qtTz ,~gSUz:p@c]NwvѮ]a}ZFm-S]8v(4j#F3f cn͚57nܴiӆ 6.[l!=zaVW҃ =L3H͍Uz)Z) gaҫ̦;;7w\V}[߿^lߢCzKWZ^~e;-"x:څV燢Ȫcc7nܴiHnC5_F3СCDAZ]i|H҃0 =4Gs47z9z+cJoEUmiguK Zkw.Wmz_ۼw{-2]ۀ6P9'KYzlsDQFMڵzyA?6)ZڜACQhdUF/^\JW&L@Ξ=uVZ]i|H҃0 =4Gs4T*d!/e]zF5~xb^FIc[Jo\Wvm~͓i~J*=~( *=9mڴi??LJ$=PH:u4>Azf9H/"L"J?jr[$^zэ5JG̳Jb:~nv6ǥGo(4jD3fYСCxao…qM6JCzi9K/UUz!lih'+Rj3LIW,/UF ䷭zx<,}G?FVެYHz9u> +Inp႓B+AzhFj&HʦOl]kmz6Kz4Ki&#_|/=,ICQ„Ac#M4Io߾}r]ؽnnnϟ?pUhu!=H4ȥmHp`VwU[Z7#ue[Qz ˤسYUl/з,ܺ69Ё~凢 T6GTAzf9[ |YTz=l"{Y{ҳi+Y\ {4vkjp_{" Pۜosc{/\p]v-YVW҃ =L3H!=U }XnpsG:Y:w nJE\),=-bkӵqtF:wlCCQc#}g$5kޘ圝unnn$ ٳGٲe˂ hu!=H4S /ϛE*= 6n|Ņ.ݬ{XhB"~Yx]uy÷S~-?%(OUzls53gXbJ8wf["QQ}… oCulB2ꫯhu!=H4襗J^leʦ mtcc΍lkF>v:7rȥsK޸СOwf%1cL4iƌ/޴iӮ]rdݻnٹs7oL _o:u*@zn4GsHOIKւ73[eU[5pjvolT'?ڶޡuneE-km6]aQ.1>} VX^~!-]ȁ .̙C,Ǎ!Swd8#E)J&.jVkE6u6|K $?zkfkUՖ+-j.iZsA%D"#h+LzlsjC >|ȑ#ǎgM6"͜9~薩SN_ȄdAzf9|ZיcGKKN"ュ-Fi+-IQ*}LJ =H Cs4Gsc[o׎]۷&%?_ƧzݜaAzhhnҋR/`?o g;\~ Fi+~74>Azf9_ӗ'NqohLg74>Azf9R4IENc/OS҃ =L3H!=3@zi9K/ ҃ =H҃@S#`?o~Cc;)AzhxGKߴ,Ƈ =H Cs4Gsc xxfe%'%7iɉ9W3=i+7!=H4S۹3)II q1a,%!j}sCzi9J/IUzA>ǜf_%2IDQUO[mULJ =H Cs4Gsc^T =q 2"4FsX=vewe-Yh䜫ږ7LJ =H Cs4Gsc}G6#^C v^,==nNq|H҃0 =4Gs47r)~-O/"SdXm#trvh[1,FVͅ\{lm;7eEAF_9!=H4X&j)<$Xc|iK[(N-B-kB#JO.sxݯ.^:mNq|H҃0 =4Gs47„N?Iz3?&ђH,Hz/~z ^GV 9j -C#JO漷?\a`uڜaAzhhnғDj9u%KFk;z*4m )%ڑ=}OZ Tfs^[?K/pNj:mNq|H҃0 =4Gs47%ƈ~>;-,$Ui&)~O-Z֒FVZ5ݻ3wz7oKϻ9!=H4u9܉K#:e]NW~G: }4uv:#Zzc/;mE`nou'K7)Azh y3'H /ZR彷j' JOS҃ =L3H @z1aoFE9?$=o a[7b"B MIzN##bEQHU$6&ZA[acosCzi9HO{oݺQpT-t۷$aN};_ZraOq DG#ږ$(}8>Azf9ң+ޮvص}koRB,)i| )AzhF*H vpvu@1idjwsJCzi9K+%1}\<}'Ƥy}sJCzi9qJﭪX~T:=F9!=H4 G9y.S:zkH =H Cs4GsޗSjaiު!] =H4݂y6ݻv/5B̃ =H Cs4GsCִhn餉EddK7Nzg0m!=H4ܐo-[ Âk"ssn6AzAzhh^=uȈ _#+#G3s=fʇ][X]vAz\.]/ C.r~dKwtaov\0Ư;uH҃ =HπhhAzdP{Kϫ}lI?I/ Ko铮|{!6ֶ vF+Azg@4Gs4''W64ef,N;nNN;AzѼHodw7 ҃ =H3 9W{y[ݤmeGoo9oH҃ =HπhhZXup4vqq޵pԛl?Azg@4Gs4ByqQW[[v=M_秢;H҃ =Hπhh.<~q%;'ᓓ!=H҃ hzްut13ǎtln =H҃ =HͫCC:kۧWOOs>O閰FEAzAz9WŋZ8 -t;2JOHH҃0 =4Gs47wd&ĝeƵĄl'譡2҃ =L3H T{YzSP?o!_`?/Kz ^GoY|<=tE]@/w{ ],nzxxqCz߃ˮ{twppssInξn슫]q#î+xS\?].xK.g?x:sS\d9KP˿"FM#Tlv)Ny(X~4\63凃CF!}|ѣ'{ JeMYYz {=ew!C` ٕۍUh#۰ݎP/d%߲=t]~9wdH/4&DP#BA0(*$(:48:T( )11apITxlt%N/"g&bcDqR,%&9NBI&ĥ&ƧQғ.''R$]IMHMH&3=53=-r+W\Π\ɼ{53jV^vV~kyn\/~r:֍[ oܾYxr][ܦܿ{]ʃd_\TTK'O>}gϞ>);xOʟ%^@Az6I؄Cf%Q4hvFf=4i6ҜIf)Uy[tMc6inIN<|(="q Bz籇=E/zcqG=HGzTc8G+=fكXG4{hcxG==Nh@KоߠChOv)o= ghoCٞ'B/=hD(SZ hovk9T^C1@zhhn AA 3 K AACWdzؾm:U۷z2  .He{{ncGΝ>UM۷nhAAQzwӅg}#Cig> Tޑq@?_Qdx  y =|\X:b)K5|c8?z/BWӕ_d_,.ͯ}keB.d˳ )[V].1~4nu2TIХXZX֜CNJ,/@aAoL0(dĸᒘI$d23= ńU=nꮵE6 hLN̗8`єx4zUU=vO齻Cwhwm$܂0n0F0R0ddaMxc NG M4O9)MjA:~]$!;!3!?!QIn@>CbnkToWrr (3~()1(?7B=W[e"@XgVzz-'Ca+bVFo)xsw3+EA-*=A㇊6]S]~^&!)7 r _NO\T?E~rJa@d(=eQ&ou -O}%C #2g/,YlQdzR6zI\WiI&;(ŋ=OH9n1eRܳceIJg>eϒs$:7̙5svu6u#UAؘcVF pkV"\_39}~qJJA닧f]-dPz^A G3(^zoWW@=OB9lh5zJOsij6Iƨ1j7Me gw.[5 )o.|lлߚUnU\t{);܈x>vSyzat˰6w՘͵>ZQU*n~},:[rMq)C$xD+b $u̙R2 A Lh'޹?&5.x엻&d?tw9d&j { B`z;yQz"o%^c(jwZ6!k=f>-jmV=7_|unLˤWR `:).9h[P!2yi8 (u|vzjbG LWON\ z?p %H9?MGn;oF+76nslАZĺ|iƈ 8TzLOjJ2|D^ ($A1PpnI*V*gef< ++'BYaN6 $\RLS~'fiڵ^>_3s O[5W8: ld:&y㋛tm BRkHbM-nj JAIqkƙӧf?*ϗ]pAq'%Po=uzK>;iՔͱA6MЌ_~q)Cd\mfyYe#|Oڗ`6$ނ ȄXLzr'vYY0?btu<_dKFEU৉-L}.{ü<殡eŶ/$?oݼM~o!~+C(X1 O^?0UVAG9* eEy}U-J }?cG}N]` ypAm!J^=vsɑ+0#CK첯\%CMF +/ 4 7@+I$*0$O$֤d$!"$쐟$Q6W!6jZ+BC_1(8:輀 b1sғ=^\\Sl{k+$ysrYxrP$pSFT1JDPN Fn̦3')*HMNJL@d2Ғ,(ez>5 M6єɣN|YF h`S@MT1 g6 i8 Р]j+Wp Ӥ{F_!,KΜ> stream x! 6x ~ / endstream endobj 252 0 obj << /Type /XObject /Subtype /Image /Width 672 /Height 424 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 56755 /Filter/FlateDecode /DecodeParms<> >> stream x`g!PA`{A^ *"X U tB{folۻ۝yfIE[{ $  &aJ㻺v%njǴ:}t"[ k= ܬg]U#lc| _)FΰomU@ڀ QjYu4N.h[)_Fv5űVMF\YͭRSU1N<{븨: K;0F$hp'8h b'NB H:xPơ˓&(ݙaC_TgHM;:@cc8ps% vj7;E(IL 3Ӆ9i\oq>,9GGL:}<雞>3g.uYYzaDU•FKw=4ܫ"t)" \'~"7q No2%?Fs7-p igb-zJ2׀iVʙŃ .#[Htg ρnm0f!@y|f+5 ^ Pi'Nѝu9s^ߔ߻iJ|>|hBzFl6*!F4bKt%uV)DtKSEKokG]Yb\^l1o6+*ZA]TTiR@)B:b=otte o<Ҟn !SҸt}IdL+fKR>ӇANeڛ\PoàI2&t2"0\F&b<`Om8yByS0Y(kU',QPNx(灘3b</YYn~-7nŰǝx~rp*Y{Y, >X<瀟(9l F8yl(9T!sgNq«AH5RYQ!ɨ&IRA^ߢg;lߡ-8嫟K#qEeŵ )|UjMX"1"&T&W*UPSqD8Хo *" WϽP$!5#hrp008:FcpesV讓v)b {4HsW5ǫ~6"ҡfݼ?p<!{u;ٽ]]:$5` $#t9JWby@;5ޢ:q>fSwtmqTz; URkk{͌$/Oc_{߬ L8o7{;BȉWU).m-ܽdw{]w9곿a+O ^94J Eܑ ;=ژ<1 g/`_ 3!lx/o9c挏5[,͙SYQZr_# n۶믿aŊ6+VzpxAGjTaR5tݸa1lcŪ յx{ۦ=wݶXMUE5X DKO>t^^0D}&v%yn5]J& 9 ҕ rT ,@ũILai`9YN1s C_, U"xܫHwdܪ'Ҧ2Qt}cǃ|5{iǮL@2EЩŸu=!kN ~'R1&y(Ex1<^ЙV,f?\p(!J’m!sTRoPͧQkHWUE~=II\UQ e6uz ccb]54" %T#Zhqfɍ70eʌ~ᅩ"B8G}xl[wg sÿI"ØGg#TT* s5lv_v&&k|Mx&[x*AQˬws?4Sr }tS{NƠ6=4nȩo3VٔP'K5vE5k?~bO?K$k֌7nүQ~#Ks;::%%yD͚szNQgLd|[c$u:ݩg][SåP(O: 9Mx2ji {:PM5UxhΗGa1j{${J99͞ІwGa`3U(򞩝?#DTs_w'>Aܺ ;uoV[V;#u#gA5sWljbO#;SL:\ yZ$h7Gk3I\gN^$y`"N?}to' /?үΟάvjbiA+SȽE5" H_|ښʒB}Hڟ?a8؛<}dښK47f ̙glM4CR:E?[XR9v} {piuUUP@p( vt#6m`)Egh 9^uܹ2;9I@wfiP(R?CF|Ow>KL%j6Z/n"MF j# +ւ?zcw6Z9~ou]p_^>ɾ3`Ԥ=/3[n~fh#]#Shex>3I(2j/yryΗuwfrf[hٓ\IK8=Tšz}JyvNxLrRա!!N7FuQYQZT `L߷B~#m{LI,k}hZ4[(**l^paǎ]p8Iٹ]st xrlGp\%l}xʳ Byn`x rR!HzN{ޅԆ!) yd#I> 84j;Fؑl5g,e3Vjtj1ԚZCl._*?8x=ݙ[:7}E4A8R|Hs߭[Su+4mZzTd49OFl =;ǿG {Pbֽ=o1L-4jMn^NA~YfE*hDdž%Q +SCyWloA~Nu S!g< Cz&!=R;s`PϦC3M!TX<;{nU< At:u͉v/t' ܜfY*CxE;U Bz`}跎i)We\8K$a:e| LQ(j w]Q Tca4J2zG0t.aLp>=#Od.( *5yagV4i{vpl .&(63e.që%/r9 /@x5}5Bm&!\vܗP' [w)E)/1ϕoy <4 %=Zwj3 Z'S|gvi#4<3TqӜ.J!0yyo* &, rrqP'QP3wu:gq@\nTk2.am &>6܆ @ܛP`!ZXMEz@ҨP.g#n?~&#+u)Qn5jFe4  * "=%2"XNB}={aTk<ȧUHB0ZnsWǻsk :S?BT'4g% <@TOe-*l[j|nmbUms7yx=ˡA-􀃾(la=ra{ A=&G|OAd)^31:G)RZRF(w7H;^t>΋D[=*cgVrd d9*NGZPk0 I6Ϲ/R9>-)dx 0kl[tI&Ly~L;-+=Ƞ ÂafO'Aagwt "F<_pBطxY465@PG' o&z;`#B_{jʲ@=qso~ bpVѨrpZN5$?O*k3텣&5 Nt9>B]@S|EXWo#6lG爐SA>凯0i'4\@/o]{l;:Q0Yv&Sk?də'8qlrv)D߾Y}[yd! U{_ƬBUC|B IOox+rOO|R\wYL遃ExO'x,g W u4FfprӛL^[+uSXg2 ;VS[ n{*xpTΕ |g5 x^\Bm"59~HXʲSc}wꮞ"߭?dEi/=N*ӕs7JbK}_jo GiˆG>꯼cb`3;~L $kQQjߴoOB_tP18~0ה_rߚt[/3T6? Y ] j. qVkD)[isVX y[wPO5gqQދۧ^#ȩUIQ|sMDUΑo4{.ȡ+roGA=0s"ޭBC1`8AWbUctySא-ޙ:!UT)o}='ݽ_.3%Ma~}CiK:ߺpә!N[}BK2K St+93 _އ {&y%=`q 5f9ɢ>rk!^  J:4H(|z>f7}0;3. omL?6ëTqtګ{2RVg G)QJ=ˁpʅY51sZdGNae:d7O0 *4)c\,͏` ٝ CNGwagO}3{*Q>8,ig(Z~@GI.xyv Vиaϫ'BAN p)$@%5 ѽ)ˡоM_ 9e= fOLle'<^ylem8 ZySկ81i=}>=LR>kҀU 8Onߩ˜w)կؾv5OZtjEm۷^ ?K|Z7;uYK{t}|g%pQQ>1Xx`?4 I fz>-Yx(:^<sDϕ̥s>P7ϯL?J1e֧Qo=?t3S5R u]ʩDYƦO<o*P0o|ޯ 41 3UmjYʷ[}M"(s?5ih+kSbytV)}=4B|Z9t\xF}h /G ;^QG4f@*0ijsZл>DTDS@R[ވhYj]x$~̻o긏k00 ݟ9OI{?1sTOL;3F֯<,k75gc~C*C>sm"$Q=-ߓ%J 2Wu#Ok/ o@vx誔l +2ַqt2,#@!^]yaC]XN-O\BgNN=Y,[,/W-f;bAt޲duO6vB?hw/8$?p=||y#%sY<;wlt]4H=a>ul 9@',}Z)ߙ)3ބ>ޜOᄆ/,ZtqgNz;HC| <۔w<ԧSr(Y{xp >~B^a}!-zS*؈pZԇӕ7xW6wF{h{]{@St #)#V_{O3Azf+8~ It_gyğɸyp©<~TAL8<ڠ0EؤQ#s 0$3Ѭ˨r>NmK~ӈqG޻aOF RR^eq꿺6"DC}Nn4_`_A R ʈ֫mwHu=Ԕ{ah@ -odp"7 EJ'Ϯ) j{+t/ Ind'n7Eȍfko$1Cwb{!u$;_* \@  x]!1l/DbUWh*8Xa,?=@D*PIqz Rdr**3l+*OԨ:'SgZT~oldHenI/n껦5Gf/5&DcOp^&b^yLHԱYnl:ǘ.ã,xEu(GRǴV$✢b{S3M,<^$DOɫ^k~6n5@ArFMyN"gWJD9׭kޭ{3ѳ^ߚU=P(21٪-4]i)]jL;+㇄*ÙI89n;gj_ ebya!uj٢)3=~4!8"S-~l3UZ5,yhHiҰo{Yu`?j[k 4#bC ϛiiSN2HYyվRh+&<"f3S3 [W?to#}P?Kv~*T)+o͗\y]qA5$š smwyRZF I A~j7.=j8)Hb;AG$Ú=: U߯9IF*ÈQUuKfy,0R}w~{Nػ[HC]mYJԈ"2z3-d}-8PÆEvl%5VgDМm5 ~V\U4SE-H%֗X55SEg>/*̲5 ķn/ g!6Qc1W+ *ra'G$X >{nQ^ЌdBrVPÅA"Z)9Hra P&@._-CČEsɺW9c_^>O\7>1T4X hI[,_}z)Ҋ׊kŬpJ".l}N*e]8۾t;gcc6^Vw\.`ξ}H1^?{ʟj6Uf饺dBN,j<#C/v=ϗ%]{s-sF 9n4ʯ\{ν,xBe9)xaO%az諥W|0*[6R[ӏ1б_ɪ օ$\xﮆNxg>>156:䚹GpKKX$4T Wo6S \ bښ%v]aB E賿2+R@9ipP+jV;I4Zr1uR,Cal1!qxl#֔~"gkɲg:=fXn%}I.XR8Tlm~y0e-+*ʕ&͎*shfE.?6C܃1~$EZ-yyYmML ׁ,5P%Vj)nزKE{twJ$wH/QZ;; 3梅'+} 9 9V,sra!}D%zᆈvY;~1Ήoh qx}ϯ9u~Hͧ/}ꔒ;*G^\y֜ikޗ;~ϪR}to*gAB .~{*m2ٶU5]썭`Tʉ,cr&%* L6y%^quզSW2l'1Y{s~`ҭ9U@gsLWev-Zogj; {۩C'o'CرME N׸/sslIҀ)UX^6V#j*N/J^wL!fev: 18-&^%xYŧ:X?lA$xQq‹ʟ&$|.w;&u2ԪdoXpTXXE[6S/䩩JUmwq1;{/@|y,߬owj%lU퓒=1dݵŏb*P.Cdž]9/OOvbw1H/Y08lpC(~~v9_[P)ark[p\mۖU)K֏<RIvTe__x{rHЫ%"8:L}04:dUNB?sJjRV|)Q{qNǾ`|}ѣظ3U .?2VUX_4 D"&7<^ȃ~ݥ-UTDLMVD*./lVƽDI2N[驩h[nՕS"+ɻ=uMF{sV8H`![>"*3؞E&]4"XJfn _$ 98nֹhrXF[ nA D[W[QMhvτ7;kEe'gZY8#Ҫ}U2~eb{oޓÞZw86.ωڋ2CӃ)m68] 2Xt8*R-yEfL$N%ADoφ_BPJj=C 0MM=RjOݢܧA>Ϣxȋ#Bد(&T RpCՕ6#ՄW"-nHכ)mwWiה_@8@__uw-x 9;oO򶖕7[O5'5I#;+w_ >sg#tr_m%vuW7FڅKæHd&*f޾ !ܐb/@{c=_SP *J:q!i{@kC:|Ʌݸvn_>rTށH1T-j TF:I!Fs]fGiU njjC*ݖs |m^r}gH}uTvaUd#B{JzxA n:UGvaz]^XKRP 2\QvHoྼwԸmzLzI,Vh2Ԋu98[uK?@$rh}9֚jN`=J <æ(Gp 7hV ^!.XlRyy#,Fۏ|W/۰h1WhepJxXHPۖu\IqAڼ"B1adlq_$L| *Tf"="kGώ$ET"CKsUp"ԩgJ"'H{1JERuŸV  Bqen8HO۰z~tk^vJmqtap'-TZVhN.xE3FVtsLZ.65[,s-jm eζOA ſ /b{<<9҆ğeh{xvg.s-m(+FLo6S_DO^XkNN}Ɠ#u Dcn/ R|Y)]d! g/G`:_?tm""ɇ{/E{_x=]?? Fnj53PF(Q pV>q:ʨ>f#}e߂JUkʿ~{)=ⴌ,ՌՔ^.(=SV?qrpȫ-/"Nȱ yyn9b8Jpy! ϹDbx U8U!xթeV#KW-P W朇 *̵8MO(|2ƄKWkԪ|Kˉ"">YzkOG4liPWZyuv߶J/2`x]{qi3DM 1%;YjTU\pI=xW|!Zkd,RO2Y>)Z\SJ>I Jk% .ZOm-D^XR0ʳ_3L\&⦴;zFhdb/޸+&6$5,Qu:qM$ZsSƷ q]};RU!G('H9p'CFeXm%‹r|!scWO-|G AȯV^h4Z$^^qiK#ªSd3WA+ 0?^ VXbMFI.ѭ}s}G`p xV!uY~} ]Bse!T,׉ oXX ?l.rS톥k.Ao-x$<>,sP3mx[sYw蚤0iq DE"nbx@22b01PfnΚQϏjcRZyb<;"OEco*#]B# BSn1FZ 6@4<i3 t˕ $\˅ija9SF匈0&X$q=d^nd;_vExOH}7N'ACSpjk3ڣE7j>)x"8Yp D䘵,xIG|^ECod^O]re }&wZ0q$6!`wa*o+%u#zx1Kl.0S+Bh ΕNoND}y5N\,}ihl}M҂0bxNqSÈFO7bz1YT=$Һ`_QU։dU]ƴo9EcϦ'T.K obv=٭\W~9]҉kVQ~XI?Ym/&jc*\ۙغ{  r2?ظGsw E#F[JꆲQSzwKkǭ 2yfE3+zte>y~sxa>b'. b%42(e}[fߞ;Iboj9{%"GkBSrbZV n;ƒ_iւJdv`ٿ[\ɷ7kN~+P/k}9rG++k;YEqQ]WNN p 06=5vWiX1(+..\\fu+Ma翇Q$8̘w!mJ~PxZ^;@?01 nɛ1$yK;]8hVf21+j.c)Qi飭=ZI &6d"Sa΍+p*S{A_~E=FԙW(,dij}6Vh,%; aaVъhV{SFF@nXm c O~_tXpLO>CR /ުgRxxl2Meg&hC%/e'QO 6Ce<"*)w\5%$?T⯏~|Y<:sr^DWC/^XxJmf]3XFuEƓ.o+_ۋĉO|zFzCf#f!Fv1M+UKߛ=.&]z=|Gb%BTk@ii~~z:m'k>a+7TZJ.u-tb@KY+3P6j(?o;"Zk&hg6 Q)歄g"'zpQI$򊱘.WT|z9@ix %cG 6k!af>òAl"c.2 #Y>U s-8sIS_Ji1'JLh+fjZ lf xd{fhZqGw't+;fO HLYo/0uם}l؏ +oS8!& YdWsF\ŰNUO-fAvن7"2GF:զ(*ɦ *k᱑߷=Vcp} ;D5ƒ62ƪd?#dBF`2m=7n¹b߀M)\^ج,#U;4nDFl!Lx=uaVׅ_GG6SJGFqÐ~}ht4f,T|msqaW'dm.IL&F;^} Nb#_2[c?-ʩwnӘYAu˦¿^mNAqd;Q@[ϽFM1ֽ=O38;lL3np4ly7GB*Fى? ,fSt{$oǽL~𢉅 2~d|;Z-F5V#$i[^FlձCs!vm|jW6Zmwm4sݚٵHQ,&[=h R^&0TIYpX7hf_H%բm5hŖab97hN>?:삋'6WSy}BD\h qE1׆/|thIm'ॺPsLOGMmxє W/_ALDa"t,M+xV;_?Wz`-B6u3M"_pFPDИ!{~'+Q/R^ciS-|o8a=qoNg+|~(+Q7EO6:=֌ t{67<F(w>lIaJQra6nX&B&x|7ݻhvGQgļ Y욶9 HSX,CuJ;TTz O^$ J0KJJϝ=[[Sc28ÍfK@T7M&Aڰ.)b>\i&BFSUEy~vJ RX&'N^x[_xѐ^x?_~YMm"Wv1Z^4x-x9 *9Fq GF#YWxԯUA:\\^"E/HctU,cD~fd1g]>#sSxQQ* Il\[nCCrvϻԝeg9 JTL,^UYA&%]H&X+W(6h,U[UiSjuF1<4U/FxAMv"6.>?$%<\uE[ X"$wߡ+"&."*Fd2nCRA!Q%E%E0j1ז˝^4Fxd R}g$䝤lj4 f_HD4R,Yo'S^4x)Җw 6mϴU.:ekV6bʎ!*UiJƋ$Q E4ZE>EF&8 /ҷkK FT1^FxtryAuõSsvlFu9mRBX/"0*!m} \1'rG2#G:~\ DINϐ+0]DPW BK8.jY0p*bʾ Hŭ/^h(!{j,طt6i~K0m1]~yߍ7Z]ثSkօOrAxB*%*b1T{b\l" T* )͂I" y-^FxayoE5wh$ψ"W}OhwܳӺp͗{[XN_^u[YuxgԜ1{5/ (SGމpSo,X8 T7ۻ܍S5BH$RI@X &.<"#jZ윜ؘŭ/^h0,{sK]WF?7;bv"0kS^lgNYOu¬=&OH1]5ˏ]Vu.7 / Hj)~%Z&c 1B!Ux$C/"̽DUKꖶK(L`bHӺO_?бZI/ /C eqi`obxƂdSoYc\$"JJK}^4FxaEлV^VdI}O Ɔ Ijz~AP%5-Ut8K:vxʝRS/C0V]UInC*8hbjϙvUJPWKTFnH%&yqk/LpOѓ SWt%>R"iƋu0^N؀uuXhfaJ 1f篠ߏ;bm%am9^ ܏?ŗJ{ǜ©y]]] /^4AhXv#6+֝ pbOs^eW/thr𢢢̙3izP^VvԩviZc\&ITPJOXWTWh!$JLL(Jbm\ g69~E`P*m xg^"E3-] ov xrڵNۨzH$W.^wMF:(nTUmB 'ޑV[Y Qy9R/Jg2***B/^x^r R^^ϘP#zϿ?ӧhZs0lzi̋l=]DٖUV ^HZ6AZ}Օ!QdUUu,]RP3*Jj1zPKVQ\VŸ fht5=|ODa~-\EYIVw{T gv՞-/A6Tc<&/i^wLp`gĦK<  %ā$If4Wk{e{_?WiW>kfFD3J` 1sZ>_ Y{ZF~l*ې_51mm@!e N/8E5(3ٕ>HykABLzbïܺ?T,\sc7,5uןt톥퓞Yae.ZTж{IaӿsKpKNJ{8 ^Zysy9_t4)߃jGҬ=HyM喍m8o٣k3QdE6OMT:)>rE\DfXXɛlp4rT8(&t:l 'viKr0t:<)RWaё.WL=VrƜ=-W7oq6}]R~dR D0j/.o_X\R)0YԡHO뢷6{~>N=ƾoZDvP0#?gDF%ʡojYqW|NLDHI7` V˵C'{ _`@Q{M =IJ'_1E=TB  &ƚo9kO-z5‚ ՚>zP@|ǥ-T13b$tڋ(-Y(&RXuu*{/ol!UG-[گYzst1=|onUj| ҡHbZgGG{9:>1ATVsU^a@)=M9 ׾]W:n2m&Ú7,L6vk 6pB_w>>h_zU({_dt(,^p QPF‘pmc Y9PJ?1oN&*cXSuL$v 'Q#F  jKjV81\ ~/v9f bs t(,t&FGF|^Tj:d4[IE.#PTQ/(*(3=^n=8ęݷDK82lP4AxūOt֡?͵_Q+Z^ܕFҡLf=1s|B zyݮ>h;HJAʚa >_P%. X.V׳Wv^p,lVFx6eѹǮIAa|oyqaG9EZ?8m~oZ=z&xK[No}=>h-Jh ;PW}(@J̬L&?EdߟرBޘi5"K#~O=)șg_s@sb (j4Eaϥ J9Їvut)@Q/ɐTx݃ʜB.A116fLzBR4x/}w~o u]l%OϻniSqn',Vۛo{ީ3m>QHRg ̑O(4A?SzGT~h` (O!ZkQpݔ%B*yP"-;YV8_[W?p//d4>kF\rO$&! *X9C( C+ &KBmKE#@!} yPHGv  }ܶքB "2|,FT} Dj[TMQ'PLM hW&E7$PviKr0t:<)j!V;:"`JN>ڋ(br;rGN>esj **%ɫ _E5u(j4o٥ɉu6{~>6PBNnED4aGrũxr )bz}:^2P$=Q><(jDE9~iIR=b( PC(j!?K0okP')Fnr]w\:6{¸1xq,@!M yPC(Li +_dO3.kҡbXj;9 s>_|+/ZٮnyeT_~䓏~jҔkbbׇ;7øP hu\P JT>c^Q;Vˬ AũD#aPH- PHd EfE~}:v~3Gz.\dOl8뛗˛.[ÿuՑxrVw^y5~wE;;۫[tCŁp=:JS_A{?#@o#h^f9CrhhE+J$ۋDYB)n4(Jv'VPx,j`dµoDnŕxI}pfMo3#aȦnc f2ϲ2[`KO*BV<,PHnT$aгמo"cḪ}}YCFu}qٚ[zG~hxx+h8{c85ͼü#w yB&_PTNQx(PTelX}J?ݎ$> F  jKjV81\ ~/v9f'B :No0ػzϟ_߫ƼEd0g<%yQc569|nmk[2FRMt z=<ϨɈikm )Sp~@!N7YWw{["ڎ%gEf g #?6.^}ϰ?hzZzP6bERWn]{*fh\vIϬ03ZTж{IaӿsKpKNJ{8 ^Z9sxS/("3Q\t=+/~ejb>femwoSX{ylE4/ :Îr )@!B=|K }7=pƖ[6~eD|EIJg4`0קpZZZ.b( +x+yF#Qk9{IA! WbgeJ?vІ7&EZ H=^EGv{^13*Z31)īnz׾VPDaǒ,a^D] wJ S` (2*y!>uJ}5݅S3Ͼ PPϧ٘-esoۇѯ48(4!HɌjYqW|NLʏn82?DLk/‘O=k@1)(0YE5)vOvh,?o9kO-z5‚ 2 ՚6l2zP@Rs<.ΠhIA#P$އڋ(.>&_p H?>sdA-=0PS(Vڷֶvв=}䍻N.-TFEQ)ݖ.Ōڂ% <ܺd4=?0{,QP`PHd N@QOɆ~ɫ$ͯ(꠲Z 1]Хi*upw*@,W no`0qe{1S)R~ ()E+YmT!NHyP?fP5}d2ChDM*%$I{`(脛s̟?HL Ք_`@Q{M5 ^胁 zUK,Ĥ EgG{{[ĨszG W=GMX3o~[kkkK^@QV A1UmNOLWnIԃ@QwBkfa4J)èM7{@Qj@!K6ertZ"E.>MsSx\.>FNg2';E݄‰a~`ȈV9#JcFlU(ДDEY/֚BlTujKcjm@#%Na|(l2ks[{b7>]C}:bwJcd|*##yPZS"|H^_1E=$%hvn?ZYxYK8ѩ7wn}e3tF I* PS}(@QMzeU:x>M\PH"-:? ?O$(g(?lz)Hbk2jm4a2 ͊zlD"V /w (C! KjW뻎m^EZ}ZGY}'ǚX|_7|~kdl_<{7~k7j/mS%;[ ҥ'EJh5Ɯ1'EvPL yPX[O?c<̳߰;w~pMӺG6>4-`˯aXϜsIΧ[yvetpc/kϾئt(Ҋú%{u0~,{=ȶi7/=fUo0"QM^@8~bjٲ8v$FrYZ9FP(Rf (&<(B\1Όch" <6E(J{Ks"0*w)@b3LNUձۧt:HUxz#jl|πoǥm5EgFM;3)zGE>(C!aZftō_i"˴@ϾKUt ,6[o{ީ/C1T3h&PnbkB b>` #l 7i}un1S5I:GGB q% 4SO?vІ7&EZZHb4ɜ؛| Q@mAnG 8hRVmR/"-CEQ@x} )w?@kiu:f*y/Jx<Hbx R_E4U(X $I-$i= A)v(4[&1O?(Z\~Ay&vW$hu\P JT>c^Q;VˌTJ~(b BZ(L!hP|77ϞMʏ?T<޹sg̜hn.vPMe)8j1={t* e*O>9p(7nfAA43GZx_ rUWd{(5+0?P;m{vt)@Q7M Ne`Xt⮙_\˞hR5 Ra5iubE}ćB]Ƣ D={?X/ 2"#]]{?{lby\fn3 >CCm]Fé_o 89sB 'vn"ZDbʿ`(w ?Wy7`4ZhD ƀϋ1pk[ۚ5X,l*ې_o&#:% )S` ($Pdd`JRr`E6Kl[^ky;ٻf;ڽ3ݰ4}3+%̦mF#a݁ v9) q^|.`;x2%\0{9k}szfVB]@!MR\2GҬ=HyM喍m8o٣k3QdE6OQBM' ''\ .X" pXɛlp4r2 R@ q]+Ws$# ɳD+>K\ʂEU foNё.WL=^8TPFīnz׾V=YH7]è" lJUtsE (R(r>3ϥl7JΙzRS4U53gVzH߁pHYtRꚥְwSe a(:(\dQ3ڧl1m~"J!0}ޜ= dF6e _M8i*f01O$BJc2^ G>ٳo֬3:5PHB~@!._cdzw]3MF}sEz?۔T@ TlUd];?@Q&SD(̝|wP hׇsՇEY i4~B([NeS .^a_:d4zP@R"gZgPkR~Π*jP( 'j/r̢E%N ҡ_E2)0PC TE#==[[[ZmY uZ{E?5YUeb-wdzȅ(Ǣc\1}f׮gٰzQ`( $٧zeu{cw\`;,ڋ(].;[ 9L(L*%0KyuɊi9zQD)݃ҡ_E2) U"?אg*HY/, o!#TAr NsF" 4\"2gj]㢭-v{.RV yw.FM㞨DjTL1fQ*CCh\EJE xq˖&{zBZ PUB@gnN?Z cO95+/'' 2Qq* rVeww-㑗 tLr3w2g 'pJ'衇3^qe΅̲w&{Y`jxu }wЈspx8IR$$``(}A7wJ,33PHB~@!EvQzi9{r8=/BNnf\T󯖎<~UOO'+((@9{P첵%ΖRkY'wY_BmY=(?A=(QYr~OS0=ZtJF (#;0XO3S63B;VU*#}Sh.ҍ3{vE)>7\E ҖY.RAzOOYuz果4IBӾ?Xq<=Tct̯c>|+nٻ\gNpIWRnqK.VOIUO ٔEJ.=uL)iAU)x~`L; UJHnxF~PAn/W?M> Ze'J5YLCC@x/VqV>^'QCN*gw?̯ϹW.ߎHϣ(o~omy)8W}(0YEa*̷SBM(4:|,ERR_PBf/ӂרձhTd @=\Փg={g oz{Gʦ>?-K-mc A_-D1Ne&8!PH"m19>}??;y}[_<ۖbotj<嶋n~mѭv<̹^cLϡir!޲>d&P lf"9^QG.\hiSt(*4MV?$Hl`_lO7HrPET"bbda EBNd#͹@!A*T2PNe/ һK(MUEvs;4s/{c~xI8G,)fh2xTRzz1RDP&)gPhN a8>2;=sNhiU'vv6 "GDtx_ _~&Y/f'r!brطw՚54X W\u(cHd`n([Y(gā_SNr!!+m t< KDxw,tϚǻ`rcf)7 ]vŕZ67`\s!f3k2r6w9GRrE!?0Ѫ;myeVE;v_T Qd,6SޢF F#mG+t $m6بzbã##ík.\Q -'_{Gݻ=nwkKsyFֶfejh&ȒƟ( B]MT)F[ZzGF#-Z.G|CuiM2{֬z*Iq%k@MR/*EERrźWcшu:O=<y]Vs$aN7N07>-ont:/ZXo"-, (-hJGn~!-EVĭ76ykc> K7>zfN2vT扐;"b?ON>o^T(wq r Ջh4{_>'>sڴbU? u-z(HRSSpJ!%%h8Brh`_߱B/Z@p P31sj-lbŊȔJ9TDzG himu\@呝) 5X&b9F#(\8κQ<#<)-[O_I͠]u @p `e'2V(OQ(Xz}Q٬Vħ}9mdlEQ^9r[( :m,{pςR1}K/sJg;v EY}C L>SjOj>֋+y_T)aA@!e2^8>tǫbVQe┒h$ ŎG,O޽WN Y@RYZRP [$9{޾!!PH"ɩY cHNv̮vTj;9oޝ;wzٳaF(y A)E=Q:d +b؄PԌeKl`P PxnM5K.nɱ/w>!PHUH/98PFJRɈd(#;|X(hoL Uk>;@^_N!7ش7 Z-Ix*%@JKL8.:eg)[RTTV\ _^TEՙ(HwrC@!U &(zzK&tv8DƘ?wnۼ|C+hklӵW~#;EPWzevBA DEYE ggBUaY@$_ܹJ~1d*hW(Lbܞ).Rm2R|:~3aeo~+4!0EGba(u.(*/dG}_>b;5/~T[Ci+UP4"+-6dG`80m΀klcmS$f1B1EB\ r (J"S_dI}ǀB,2^ sR1Irp Y'K(Hba(S,@ Aʌ PH ;qT XJe!)X@Q/UBjٕ l Z"Po㍤_YԮLXZة2EnZ>!PH";OV#D@0 [FbbSS,HA2PTlS/,NJ٨2y}uьad ۣ*Sݦ#܊YLUSL]FF ( 託B>т٫L#BBIIRS, 6JGҦ_T|%U8@Q%ś۷L,E_,Uҥ5][d="QDߩ [W}B(aS)dVt>B;ң_䦭"W]:@!ľZgKuы)UQ}BIIRS,@_ "$ RL(f%$5W{Xe7Jf6i %%IQL $}.jTb$xo,Baua>R;_䥥i'Sйi(Cr=]06kǢaS,@ AȔZ B`pa.UٓHp0v /I$Nr9֢4/"wЋ#a&EYYLPL(+Av @QQLe(h\eE)rfnłM0}ٿ^i+N(0bj4ʢve?\ '_yKz]bIP%W(TYTSR~h;Y8Q$+BBIIRS,@_T\T (Ģ 圤J *-z) 2VP ` Eʔe,_hE)flEd{ HrDWa*zju7cr6Hl'[0z)RKHU@QL9"K;S_~%&QQԌ{rvXxkO_"bSS,@ AJb3oh4f`0pW}> 5ދ.h1%T=) 5/(& "gg>d}0p|`I83y DD7@ 0;r\2PTnGJ H_Q#$))U\EzdO~QETXuZ^ (xY@~; |>% DL6H Eʔe,_Pi3zNt5D\E Tf8帕J+3y Š(+J!] SeܴkQt BbtF ˱ %%IQLyPH9{ xޚbK!] Se &l(ɭ=fJŒŔ* I+P#{ )Ḃ]dv!a)I!] Sej tl$uҀ@ fjt($CQQ`KRS,&cRΞĹ@SY\}mM+AQV|7B~eQ2c(ja2@ti;BփQs,BBIIRS,&cRΞĹ@SY.bʔPb$,x)W+S>v* ~@6s̲;X!a)I!bQ՝],;@Q˦~@Ag:jru(xY@~0/ @Jg'o(B\Ų՟l@ti\0/ :($Jd/IX@*P%EH()IbJ< PA CHTPPCfˉo+ڕ)@Q ;Y(`MЋ!!P4"ۘAf@ Ф JB(ֹ2LREM(0t F#J!] Se*ɁBlTfW-lCRe!DuД QrZHxP)TYRS0]=$))^ PA/(2U_(jJGRe!)X@Q/TB:iI*&&Ǵ DL6H Eʔe,_&,CU'Sec3ė>iٜ(ƧNDZة2PNe/(fny ^ a敮>a*J!] Se :`=zHRS,&cRΞ(2(HRS,&cRΞĹ@SY!IQL $+6v=Vk[$EHX,'bFRȯ,ǗeԎ(^jK(HAg5c փ@ HXl#!PH" |!` Mלr"o ʢve2PNe/ YO g#E_c ʱ( %%IQLyPH9{MeA=DX\}%JRS,&cRΞ(2CeR;Ң_E5eI| KMU[!9VIˉk+ڕTcB~Q89P*%&Ӆ tjˉo+ڕ)@Q ;Y LZMWSZґ(TYeLzdO~@0zE_IbTP LA$E1b29_@!I 4dV|!ex#)W+S>v* ~O!%sBPBP6pf}b.by6tB1F}!xgvq'YLbPȯ, :`"Q` _^U yx*"PHiR]fM=)2drB.>9n67g&8aEyYLPȯ,f:F#9<9p IB~@!E[j3x!MGW5eW!JBA D_YּFD:U?]S0T sG'LQX$ ¡h[= Jf\>nlla< (F!\eE)2O .qVi|k((TYL ep3P1=q5vA I^\ H==~Vj G4.Y ``ƴi&!@@@r(G_f}i^o/^jGE{f0*(%@!(ՔH!d=&W!B @K+ޛ7JY9E*==s&.o}VWۗ{U9G{_9o٣k?{5<`=2[>:ˇWi6(Jg={Dƶq_@Gr_E)EBx?M+VcH$x~9ϼwYggߺ*;`:m8_yэxr5!ɤj+dYD)zn=ϯUcm!<(>xƒFgsǶx(u%澌 >{7ɚxf# sO9sg|S&| ~~ֵ=b{aorUuןt톥퓞YQx},(@\S;:Ղx}/" > }ȝZ࿮7;=.]0 ?k3NסĀB,U$GҬ=HyM喍mHwە (x# yP H(bm[ڨBQBʠN؏:}혎Nzl&v2OqtcPnx<^:PH":;%龧Q',.s(@Eoimc5 mh8lېmAkfyW/"˞7|2)PHBcEk=%r|$7 yPPT(zZO ĔO<|7.ٙAkn:񂫏%LNo[qG[/S2bRhz/K]v<{ՈTdH! #:cd6[&{{W\6/~Ȗ޵i]{Nxx<ۿ3Ęl4e|7eu{cw\`dI!Lqa.zZMsddbl;ǵR*hE  >  > LϛJZYNd~*T@S@ (B\MAӏ](DPHI@!t TV3ٻ$!Pؚ,vh&J cX8 ~}^ZbyPHG@!tQ (+G/Jenjioأ?>ZsENo4VuO:<PHG@!tT+%9at=g|ӻ4Uq虿h;+{|E/Ѥ3Zp (((@1(tuxݯ]y~=(N-7SA[dVk鞿 E1sa' jdBA { 'Eu]X48=Sz}~Za_x/2=b؞e`{Z 6~k@4w6 f԰FTF5t9G(5  Yz] EK4GkAEq au#O[c\tӒp˷䝁 ȘF|YrAۖvb8NF=W7*3/ Xx@W9F8 %Vb' R]w Q G0R-h%t%(9>qYpZU(Ê#P*J*3 tcHΠX8,f(((bPڨz}hئ?}>vmnj.ō/@l$B R ! A> Е;.糚*Z(((Q;D)FSU-cmՁ%GItsʽmn 8IRgP)*NSMPsTjleT>XV A*lJcP(tՔLv=wT¾;?]ħه '3փ F / x,@8vc(v%q*RA1?@@RMd(h@Hu[}/mkBL3|9: ǎ0AuσQ P>$(ȠEWtFyϸmɜK(TA1n8h9Q) @!(ĕ:.Ee?aSՒJѷ?Ȏ/Sq‘9܀] *I@@@!`3W8DMIrT,&E8;05>kqWW$Wx8\ߩ(((G)Dւb&S8*2̸A}"N[`Re ?ԯ5ZAl >9#?L0áTrՙH$,#L<'*%[a ,T'bx@@@SKxhgً᥉o9`0t8Ԡ?T9 J)9.~x)'\;/ @@@!_htB/UZZQys砷~Wo4y.fIŃ& Oʋ%k.me=xdm&5ͼ#{{BVzڳM_@dl @)̙}> Z`4bo:.[7]\^Q &V㶃>jUQ\cNkqO<ןUUh8l z=ܘCTAmVzn=ϯUc" _P,1a2e(sX e랰i}SӫZuOOM<)M!,E(&%/" ENjVDǀ3F@,~Gtf>^*R%04eFH4:c4B=|K }7=pƖ[6~eD<SB~!Y .EsgL}[@6**EVCʠN_Q䰤riAgx,FG-*Kb 7z#ϡX@@d nstv+v-+ LN~!ApERTS"RVQ51bo?ihGaۆl ZSEK=1' 45[1:fd "0?(t>  G>ٳ(& Ekl1m~"Gg=J (_PC}UET^V䓦KO<|7.ٙ]v8~uJB9!!,@t:g糍6>ps8ag1Q$!$ U89tޞڝ%zj߷~=]~;"ՙMK%u:.d0$#!&.lv:'_j\?ԈX"Q; OӁD QrP}g>9~lB)X@)a]H!: k۳͓oءow;w3G~$}jQ?G?NO$N$KT Y])N>u j1f@ QlBBleSTʾA #B߲Vxug˪Qՠ l2>`pHPŒӑ('";7)’RfsTm(Tz%^A(&PbURf?DH 沲(.ˆ2"02WknHQ_RfsTM( O򼍘;w3v͛OjV\Q3!ׯ\RfKp\I̴){b&Qr_mYRu?QHEQ!Ei1GՆDXڏ^re@`<> ") r$ZU\ b.|#{I61~)ꋢ6BcEzC+Fmm/_zɒ%.7ڰfDyh )JN:.SᔙI1h#,f+,!REmٯU &5:&z]WC:~ȯV:>|j1SD0R ˶e*C 9FCBlR{e"[)ꋢ6Bc 1|W.q.Y|O~ruܴr1vT>Ra B)uZd nBX^+D!REm%ۯU e]zzEsFq_g WwO75"Bʠб:zϒc q9ANu[F _)rʒ96d{&ze.aM. Ģi) -k׮,˖i͠XȥlYr1L _Syшe@"AidCj^ߡ#z9ZIQ_)i ~E9)P=%@Q5dǻqmO|}+P?%f4U"[~ˆ/з^ٲ*5rT5(het[bXIyv9x_ܦBleSTʾA #B߲Vxug˪QUPrDa6~ 8y>EIL# )2" 0 )-W܋A_Rd96Ter! RNj5(j#0x BP(T0I]Gdd 68N{+ER[}_Ho^}!zU bd@>D"}R65oI>Wk\E8t ZR':FlB @ @ 0]6LZ@0:9~ǠЇ]G'uvzLfΊFÑH8F!I9OB #B߲VHaR[֪)z^ǏGփxw:7 aɂ@haOǂ)Є>K#׿  ;`,@*ǎ_i?`0cda>/X8nssbƹ8zfHv6p/@ @ @ PhU=(#8tc\'xH $E>-ࡺ+Jw< `|>{S}ݜM$B\l1)))< S[G0v޽$8q~KPgT,WHTN_Bv֭PsS֭ J&23 F͈ 2N˹$>xp ^P"ѰQ!խ i.)))4">XfAwzR45#QgGe8ۗH$O˟yz4x©#ȱ{á(:dZˢ$%&)qIXR R R y(P';ݤdhTs?H?TVR |AMQg>Cp8 )V1ZƊ*?uDFUݪ52jdj=#R R R&Ee]vmڴ>Kwu`Awٍ!Gڳg췷*%ENU`縸)~Dd%.?%U+fV@ @ @ Pj|8}~… !oIu*sòF :ty=8@X,U1:"0gL9"c7Hsq9&9QJ$SM.!R R R &o< BPz&zu޽ԬeSg9%[9͖/զ$ k2E5Q(}Zc[抢y뒲$MrҡU"z='N|DQB=ә{pAwS6\6! z*`9.Ն[R^rB }iYQVekQE~r46狓*sW%odߡ>JHhZc>ȩEs5reGt'X`FjNAH,WS'6JW4ARzY|9I/~P\}Umc[!Gz{TUQWw:#=GKI|9VQtp^#kjsΙc_˧MeE}Mm)Qt6lx!BsSgΣƏ$ͫneڐ={3w}_~|koW,_tl6Nf͚vLr-=د ?׀NQޣ 뮹`XOrҡ_R3{BR^S?k)_?ʛ4{KKo$̰ՀvzG<6l .N(Bi<ѕH7񂫻4E+/+? y7/~L? <=22|Cn~,1l* Ƒ^(,pFcۆ{i=kI%/kik ϰ:rxʴi}}idR?w^cs jhh8_e!D6]hyd .[~b1{8]ȩS]M/]:xH) -k׮Բ){{+CdYYy?cp8(޳}峯=veK,>\ įK¤kUNǛJUa:QLw=xHM߿kǎqMԓOEڹ}[ f)/l /ljdLm!݁v[bƴ~Pב^]=M-jO`lK/22sU)ܶr2B{,#9),p UwlZR*(ikz .eN~βyp:, qcL=r/>NޗBn LkOqmO|}+Pd^5(*%/з5(q0G[֪sTqzѳ NGKssuuxzNO/a ;f7tt$-;nW9ǭu:-MMˡe(*%/зe(,p0G[֪.sTC,VpeͶ,~N^AQ_Ho^t|Y纨=o9Ërv=HRWI-( # RKmQՖu~ kPT5G_x< BPnV![68[HQHŽq k\HQm)M㈢hs8;:':xQ1HU"<~KLj")#EA8Sm)ʼncGD)R E(%YDápB"+$ؖV)j)ɓ#᠙)85 G7拖%):a7_(O44z ^y$$)xCі>5 ,F{|>KxEQb)f;j{_?/Yf^#@3F."(2)j'E#IR E)44(r%_[G^;m?m0SB[yA PM+0q] RTPX7T0N^dzƉnzOvW͸Ż?pt?0<)*+kPft˪'C\;TSopFl~g/_Ȃ>_g i^HQYYw7_FXWK $Y`xHVEY}A_sȚj )LBXPA SA W[qs̿bof|%@{~ՄAAP1,@a)T!Ee ^*'+Wxw=ͼ;mR QJWSHa )#.z/\2eÞ(gY\k?k?(*sޗ~4OL~R }v=^BՖ =Hau)!aPZDQY3,vny hYioܸq$eYW,RV.bۅ zcfhz{R)q] E-e= f$p';FgT7p791cP%ulzt!(U)f7i#f]ष m3)LNa)RT,I2ŻqmOXvM06ay=moҧ)-} k\HQKY"t!Yg)tW:昉_"*nnw BL]l˻ ۚROpVXBP3\OD.v?~"$P( *EhTn߱e˶^uDog;yP( ʩt=4ӄϝ%7g$Q( :W,[f=;KHz=o}|1lxlCo2s( BL.i {h endstream endobj 256 0 obj << /D [254 0 R /XYZ 69.866 808.885 null] >> endobj 34 0 obj << /D [254 0 R /XYZ 70.866 771.024 null] >> endobj 38 0 obj << /D [254 0 R /XYZ 70.866 711.407 null] >> endobj 220 0 obj << /D [254 0 R /XYZ 280.051 423.167 null] >> endobj 42 0 obj << /D [254 0 R /XYZ 70.866 288.789 null] >> endobj 253 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F20 166 0 R /F21 168 0 R /F28 196 0 R >> /XObject << /Im11 250 0 R /Im13 252 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 260 0 obj << /Length 1816 /Filter /FlateDecode >> stream xڝXIw6W꽈!6.ӦkbĘ".v;lb̂o:^uz%^HAE^bͼ/^[o>֛Gw_?oi#(T@Y]nW_WH =6*UA,;>~ @`n=@.Uh~DƢsO)$@_W I*.̍Po$xsd{UO4lDsx$`O$QžorpFJ_%юhPTY{ 5mtj otƭp/^dbֆX8 Y 6_թ4Cι{3 cX!ujsݠS7gIgHk2Nb&ŬPgQtkmzJH:]ɋq~0 $6FJ^{xM7Y g㔄dMh]sZ+51eSn*'5&`hп;;[g! l&sp k7twepCl2kUEpyڹw` ^Gi9G0,#;{]>{Dy1|gPݻla9ii Ѷv2ROXCOKOrKGPfA Nf^A&.9n}DԳhe 3clАkN?!Lr?)H}}C!#&4K-<#'! +XsH; ,~ExMҢ߆'}1bf~;Xv\ endstream endobj 259 0 obj << /Type /Page /Contents 260 0 R /Resources 258 0 R /MediaBox [0 0 595.276 841.89] /Parent 234 0 R >> endobj 251 0 obj << /Type /XObject /Subtype /Image /Width 1051 /Height 635 /BitsPerComponent 8 /ColorSpace /DeviceRGB /Length 169172 /Filter/FlateDecode /DecodeParms<> >> stream x]`G۞$HBТťW(+Ԩ-m)^Z"!$\=YfyY{]efz: 0g; A $H ADi#+)?_?IlZB.Sɰ (d~3/qV#V+xQR^DżU=d/hs= _?9^@؋;e#N  370?78|4B 9_2F)\ȅ ^C arXehS.P)=6oriZe|]FfzΓ+E.E!C3(x2+C 8]yyVԺ݃q`\Cqf:t.*HGAր)'vFO$L l/O.ϪC3z\\=ͩH="Þ kZ}10nGo+QCg[_v;m2Bಔ A$륨 Jk GsbS\|)Eh\q W2$_rb:q˹^:80 `~eQ= ]EFwʢ  ?Ȧo"˕%'DҿrBgs/ot.J0r߂0"AXNX8ʩ yszHiJg}q|ޢ0 * c$k JOd "֪s%PռH9h7(;޹U | z T˵bI_ _KqDGt2h;gnRo}x՞yӚ]]wV<ӡ \4Gk) -KVvvĤ~QòҢ"#T*%Hh?{?$ϋHEpqZ +W6rB(Z:)*G<Z;u3dZ\0 (!dG6FĠJtA^`fQ^ɝǣ\8)2en@WK ^R{,Ɇ+% U˖oox!IO/l%:?T2GGfk^++'?08Rr}LuV[@X\GYt  5k p %8$j.^8h$Q!7OB"D t]"*Q_6T'^%ԓ.j_o5rGU֠,zȂsRNn3~:ʕ˩1gkxDQ&x^+O:%jQpn|!uyY VP~a/݉Dzk]1TJ }gkS:Aae$5ژ|[ni5fc(*\8!>RCNű|/LE8U3r ZGT ? PШw+@BrۘNFxDe!\eh(%Gư(<<].i | 'uo.2B0su.S_8eJuxi4!ZAf3vV+k•d yHOolvSf<7}7Lq5~%1^}5e&x,[;-v;ՅIcJ[ъx4l-DyT0jxǚ D.9e_w6_{#쪹c\| MxO.,F8`/ `>ٳRJe$޺m̙6wz)^90a€>|͝7ĉ˖/N\~Qj4́WXTyz*+ϟjXYqY|Td5%X^!t \uNR}Vc'.>.+hFckǍqBXiȪ>@PLn}ՃZc/S=ty౫Ne۰ ^ji4c>Adwأ񰃘zk83CUՃ܃/n}QSV]ŕ씎=WÂ>Ԏ0|yЏW_ϚSj:N{`ɢʜaqf۔Za%풨bP[Q:J+982'w~."?K,g\k};>{Tay@)/4@ rwv7ZʉIM< e6r_[ϟM`{Aw ZacoplܼEϨNmXYjk5]:wSoD(*gta5fgTo-*"r?l6~@ACM?r[]Pɟ-2 %HwuMI=͚kd:=)`Mg"r^1x Q3IF K+Vs)J O6 #1vEjUvS$JLd r͟eUrTj0|XۺO/ikO\j#&mQ6#OӺ1]flƿX!uV[hG΍{>shC='/il.ծԎ?6]XPLevrǂ¬B2}ęOi=-nG,ggϽ<~^ `V'{N*زRd@ω_%ڼ\Zv6hNC4h6-lĩg|jS]9?h! N .ZrO|('z\sDϏgQ(wmfU3fO`mczm3mTh m*?;QlU|2>H3) 6U(L=Gnk+7e*טb(M`-Vpzћ1hȌזiZ쉓}z~:d4yᦛ|8AW@KsaMh V,b~,QBB ;`6܏`ƛ+~`#7lR@jהc3VqB]݃? jâ`Np|D֪~'L+#:He1RZNRպA%ܡ{cFu~}Md h<,,2 w6-Nip*W$'$J`ܬeW{$%[vݣ,̦*6)ŊXaö? 2gϝ{;w :tĉcG*6yR]6+j 3^fw Fù()4]0F5)Eh4GX'J኷֤.(苼OKˌ"h *`6n. .9]ː&\P-C>35eɲe# 3oݴ[BHϖNbܻsf=|pDlo^5k/\i˟J9[]렁93zXJ&辍ޮX–?SWwi k4Vte>:&((̩Ӎ7b ]DGV}M.D!Oxу V DŞg8•U`pp+oOx>.v\g_wTDZfjQ0RXW+檿}J褱[=S;7uOSji; R5ЊmFCY~ ")׶_7n,j<9~gZO4f3f,:s0G<]\L ȿx ]5) +xgɡOciqWV\a-gMB "=B cjTq}9+ Rg<էُ%ct)%Q 6)*,¦{M2XL盾aUݵƷ.e29׻ 1&3Mr!F8L2)R n.@7`֎8hꗙo-k*I-BZ ~Z.+vzAΰxM-'>[._\fl$5 54ԫWw܄9ODFEo]۷p"xy撙Vbk)))gw/nETDj11iw*cۄ޼}d\\' ~"EaQwApDs̀TN S t`PPjd_U ꭘbp*Ώutwfq|E&dZ?>J`;R)\uAسSΙPZ5VW/ xa~Hվ!R$tP×}شz~@t `M$_jcF 7,_k'?&>zF7b׆\q#zkyM"\Kr|u5H?|\E4 o~\X.tc]-XQ?JE,|o&1Yn\UzvSrj=`zz 3гeM r"wn|8Z@^}HȣX]Af@yd9,ݹp H P{CM0'B(7oR_`$'ѴiZ V\. 6N<o?sGz#Nn!bYàVZ*"VWLg wB޾5U\ @f73(a6p0geeT έldW eFah6̄7`u9.^~B:DPH{>%}:pouE\X`}=𒴄AdKC^8–_'`4m{M}滭ڵjWuAzoRdp111:]xDxAAsMfFIn(:::4$D+^ ( rvA ѴYI gv e(C`ts Ք=կvmy)0i O*BC%v?&߁@c|~ AV)L藘 ju8>7ߊ Pp3|~\21 U|y'؈qxM7zZL,@PӃvj\NP=i|i#`9]pCQYv‚Ok rqO(PC2ik-  DL@ʨIl&{270Iiڅ͐ j* Z6#B¨"" צA>'[6\vt|g -" Pg@x^a "_t)n+QOZ /l;!iT))i -˝BF܉ j<׌K‹9)ᕮ ϺY8s|y;W)WI@,YqOc-*h,Hb()q\1.e\Rٹyub Z` c8te#\`uǻJyɗx~%pa7q:%8DzJgUTm9^BdG `NCIxsnL5{ DBYy%wrB|_p uwl{(xe *BPy7N7໿pܺ>zڀB .. 2_l>;*zM9pɫx,|iGEK~RZtΛ{!8l $Xml sIKH L' vsn*+PPH7JNi(A ki# FJaK_Rb!3\.J喗OyJq w9w"8!DY[m(!Qd҂9dA+CUZPzY]ɛ9@o#ņmPSh kRPh ZrP0gͯKIn}A 3h^((!ŀTU_†U7IA)AIZB/@G!m?w]/p{= U%H^O해lK N"/ݬC *@h e1xA &rTA Ct|RA+n(2~^vk i㥊3LLO7Q_Ea  ^ 1 fJWo= P?-\! Tg]x2@- = hӜA P d<. U\)!1`na14Ta@0zBe(v#sD_J SσNH6i0s)?X( @?/ %@#|J9be݂Gl TNl#PeBkM{oo -f1ˌCs8y@YT X>\$1 iǐ +\ Q@l(z[ix*4ʄ(Ph6RCedh3 ;ppJ<P/#:Y%6P+]-DHD'9hn= ϐbYO|,-#^&Do;Me D7E@eQDYK$-!A7DzvyZRovMGv]s#eA珈չjXMbAogwdrUSLL6_9xuNN0_3Pm1cUxO䴱:cݵE?szjAYugnQXD}0.BZT0T6}(>YЖn b,ߤ'VhX5Q Zt[ȓy+nafq*7=3'[/eA-s P EF 4+ݹyxD VE"AQ7HsޘdC)(-SiEc(-^o .$"P^[B‡ A.El`;o/gutS p+Z.%-lؒ}ܓg?ZF{f ̿g|~ڄ_+[Y1B}X-[(0كpX2oZszߵrv=Ǯd{AYwkַwd]w=Ckfo%.|%,+OA{5ג:XVvQ TAB`Ƴ/o|NSΕEa,.L﷬効Mu2翛,9y]TzW=}הUˎj1:%el\ }pvj%V->KZ^LӔ.hv2#@DW(lA2Yk5awD -(\r~զ>nE3A!*,*p0@mEA!-0ԗ1PK򦠤%$Hv3  H:Φ60ڕhI۴k>/+gnI2FLN4vxkêun݃oY/k'mzt[%k?޻Qa=bǭ{, :{J (/Ns<{ݣ#&9짽OZBDΕfEBZ0=8m* T(̩]CF50u5)bYVaο\2끨JŒ|Aa2N1Œ?hg#1[ZD^Ĵ)`VgF!kxLK$ŏ@tAQּ]U⒐ ?BQ n{lL1]ZvɈ3˷;I\x$Lu͌OiƂwyԶK|?qުed+מ=V86e1]ӎ:N?-:]gx5C9o8]O0Uv$_Ve@u[`c[A ^;" 5Lظwv]=/Zu)'jI_ !IZXom3IZ iznDZ^cmO:z7{5 NǥjLjXeI%^y!(qTM[j[Pʰ ˿j-|S=sm]em- 0{'XywqC;ZW SX4mɾG%QV3NqX\p̞sS C_82p^ݑ\? CX{/?C:.ܻܩu;aT\2m><ɫSCmx!m==g;3g!#(=C`d$[Ʌ'-9Vk? va_\_O3E10 E_rF ^ p>zȵT-]7f@DTQE$[ vqc'KvIٱ3 W_Q{~XGe/ZuJ N|vҝ5l'?(yޮ|'e' k'{^L5 ݺ*Mz!w32Ag9:<އ Ub}oLisB׭XTZu0$qK78ӣ?n&2Q_h|8n*aזj }?6k-5Vk7m+kMy HzlUr׎=f9ω>sU8Psx_NS[E>Ր/5Y?55{mQBBI-䂓siFo8QI}԰N~[o/շ^|xu~̦^4 8&6A pID ٛʁb|JB-9Rqe}T'A)_a洿 VAtf!K6ש>(LiDQ+Qfq]sAp`'U4i$mV u'>xjŬ.(;ɇv0?n/P!i8&">H^okAel0WU~-2Mڕ`X ew}WB[k^Oi-Srmλ Jh!N^|drnΜXՏjMX9KxsYk(Ɋn[!0"ŭb c`T&*W`֞K!FjM[vRиl y6r@j~H{@Vo:cKTbu[^ jYuBZ T:XMcZ V_oMKc󍦊vr*"R<0O("Sz*Q܇ z }g&wt /w޾ Ө" 3TeXΗ5.,?O:YPq>S|֝RGxߺoIϺ[bM[v̈́jLm5 }H/f0sU*K5o4|ݩwжX¿k֑udѬz6NS9٠}4Ռ]|;Yƀ5-rNi18X5^z3M!l!E1T(2B4d S @\,HS(rMV(ȹmSn_O'Fg2xUL4e;(7=i~JUo  L^rg'紖g8͢:mKvsE7[%;*0:0ƿoAV!ӕy8Ħ:Fٕ"ن&RT~]ʸm#W-%ihaPPXX~]oz뺞׽_;ޙ|B&\ǿnQq۟IY;i!}/?iN8Kv1#zAZK @oD@s?ec9G<8O)m= ӓz,jla'ՕVܱ 02֗jF!z0`j [p yP1%[oA \\8]j̙׷9bgވ"pPD\|E [Sz =ߊ+kpLѧjOw(9ҿe|͊{@ [~-U\$<_;&E5 {[}G͢.ۺf7;fmS zB)3:׸Չ #gA.v}OKǾ;(2 f='RfTݸiLbe.nߑz 0#W=~ꏵH2ԖGS2dnԯ_-^?RQxe E!f֝X(z mi]_l9o9&mʈC#-ځ9u{OA i $1;l T+e[T|~7K= UjK[nZ:f,Ndm܊ԉZv.{H~T!P9|P<ܞ ^kk~5P$.0QLD5;l× a10 j)׶foR[r-c,na>4elNg-:zb5ôjn>ȷ)l0r]_NJ-(8vʀ'"9ثRE&rEou;؋nw Axd2PYHÑ.OR26ynJasuZ#7]"Z߱BYZfgQ[k+d{QH\S֯`ͺ%8׫\\#:e8 (VCi [ BWgܸCwo(vPte1 NSNPn*Mۗ]yJFE䂌wzÕ'K7}(;}.!?qa?F ˜ӋLTR9(w\!_wZ(bk \L}C]pF807ķjGjBhlMtMOv؈G0 e(fW`̐FØ%R?w.Mb2z,y{_wkV#Mkyv#'I gp`Ψotۥt;TI>{c^&j;z:x>BdQ7(\4(ހ_K*}`?ʑiwׄ),cޘD@uz#c8̽*$!AKκv6M(" _exQ"nT2wGvqgT 0 xx[x(ř-*<6(,w #$!AKwfK y o_pLFoF x\Y*%nw줷g>zKk=,է̷ֵȱ*diz+k2'8*~nOy(;Y5#e1ԗpZe{9e |N Q4n~HBQUeUʆ/4ih>,ROWU ЌpX>h ~Æǎj7o}2ucH&VAFWV@zNAw<裂~,%羠4^ RQ/W$ e5Սh=O%zS_·w@8qR&-0+f/>y℘p휑y`W/C4m$22yNnUnw۷\./l(͉Lx> A)R{>l ~ Ic! ^TXcS^$6Q|J$+|'ލz5BŊ%KHڂ IdT~Q;[ &;44{wAgT%QjJ ,QG 0?Nrxb3t[awjqh 9݅mݽw_@Ζ?b lWR%n2HOw^O?XxjS(@X/v]D pȘjuNIelVZ_7nkzxj9lHy|-_!F*Gjϴy ?PP90FːmO IPMEGEky󠰠YYyt_:th2ah4!(8$HH*p/s pʕ**)8V ={aX%)Ov;2R =o 0v@D(AxB_Gj|K0mlR s{37(Q/ߒ _΁݊D^ёSgKVdfdK $F16.tBJ% .(\"LAnYVB B\<$ > M^SSìJXmւŊ/  T>J,)-޸q: / 7UVkΜ>j۶i=0#[n9kIJW-GSձ];DaϸwuҥݹgEq5%epa\_:uXJe  ; 5w ^$Bb[2sV-QR M{1(tZPrnM#1\IT +))'Orr8}\VLDp^1mhyfa+)N9F8<Vp?},t?So܀/^|vʕ*;'&OLL|PG*Ƿش~\IjPRXd5݋z]sGsè#4^e`Pa iVە@ɔl.'P9(BCmѐhNP&0H e2xu(6[r%T DEqjz-`2n7B%s&])i(9! p9+xb%=$(Xۈ00 ?S ݢnC]~\-Nܱkݺݻwo~5lƗ_80++kuC*R%QS pPPL 7QFMfSmOqb&f?A| P@loh)@hڵkWX1V{{thJ&@m[p:H Q:~,[0"}dq"Gy޷͛jՊ?nݢ94$c&-+b7hEMֶl@crAbkqhȹ=z-fU0affa6 1fYBB5q LQ{ eP!bAF  w5xdb6*((P% Oi)BE*4j -JiAW،6a;(*ܴˈ؀ɑ 7իBf ,IχU;+tC LH Q[(<ٵ+}n9AU))=]1Sʕ+>}#<˿;0U+x&?eޓVTsu:]hH(,U)&x-?avfCn1ѯ6.߾,'BA\\, xVɋa0{ bGјj4ج6m 88mO*_qk{+rr"7Nd)=̈́Ab^P/^?|H`rb~vZ're.Z^̣nKw+QEVzlۺYHXx`[oeVMeʹBL:W3E0r!.]7֭[GGG#2 [l115WۅK^M-!Kо(cϋzSdĔQMT/_߲eKÿ /^j*H4ۻME<_QZpШk׭fiXҮ\ѫx-{v k:3 8M?4 $rcicJZm7Ȭ߿ީӨ4,3k (HVn6c7ϣB:5EEC ,C@ɒ+8\/@*åPR` ņ:>;cBG7;NAsG*-0[J+ A' GO~u"9@&-<ɉC Drg `-rpRȵ'Of]{vZ:U 6(c< V D* .96nw-)w3s`,_2fw@9ٛW;Od @,ZfS6'8(q`DFD\OM {͒qqPT F RCBB@c/AWz3hPNݻ/f>rbv횷hyF {%*|2Ÿp$'W'Oӭ2Re{f4-J 0Not\~+k2-Xп_bē323WYɸqPN̞=;,4K7}*'.)m[B޺ L؇###B@B]ݼQ[*M )77C4M۶mK(j~J#}ǎݻw;w.4/qs- ?~QF! ɱaCF?4mTC'dCwiҡÃt{m֬ N(-似cO>ኰk׮ ,fJ{;  ֣7<p^ڸ?h0 ѣp syH湫OjUFWwmLo* H N O ʔ60{\}VJŊ ?G'r`6UP'yyxT?|͚5I> *R;$A7EA?G"C MQ=}ol[~mBTցk6P')P'X7AA-S|:;AKA#.Ó. &=,H<rqn(> Jj""k4s"#`h`ټj -vy턄ʉ=z$CNxm σhV-[~pl9cdua$pt5%6ŋCɓO>̂bѢEYYY^>}̙5hZBqHrcǎAPϞ={P\kx^:r yu8өS'jrDF)M-[@U? ҩd^h͛7a۷Q OИݘz&h!#}s֬] y_ƍש)RN> .={=xv"$W7Ѩ*aK*'2ViʓuxtAXTfdH k~5qd?-ɑQ6[u`?2ŝ`\EL !iVsAܹr8F,#Dp c%RlL@cJJ뚭+BP?Q?ͻL|af׊;z_!LVգ$H`<$)? ڳ'} FJP Zd^k6l-xQ=Рt!( *ɘB5a'hZl_{ь+ h]x9GorL,\&L5J2P=k4EGDD&-s9LfRhٹ9ejTt W(6]'PQ(} FE= 0AGcyT̖?r駟\\~bbb (/ύp}nw?p&(d7y }@ݷn>wQF]Y 4 *|K^ǙX~(" a8- 07KA2 ۛHgMbA\Q Y2f*#bm$=ǁ0i2  6ܹ3##b@RW(4lաJ$u֭ۿ{ŐgS>$RB}RPPТE(x !0ˆjfggÖBU("CgϞ͜9^z v;|&O0+PQX!4xxshWC)ۀC`xV"qnc {R(Cf# 5@fW{gض` SrʔSf̴2ڿ+u[+SڦlKIqocӽwwϟ-D $FMǨ([*5Z5k|rr6OHJ!^p*4bʄ7A G.M yN:91p*f!irEI4 _/"=)9}ss{^,kh?8"[e1d>rЭDD ƬbǬAg(b #m~ɖpVzlS.YTflde@QTp һ , r)!R`F=]}s؝p5ۉncT}7a6'~ Jj޽{ O>ē'O@8}}}62xʕ˗/!~%vyqZ $ܸq4A UVVuǎGriTJO&Z't?DC3pQsĉw;rʔD~C/0m^N@u-4ƶ!~ $SNJPصG=MWޠٳ=X,ˮxKނPuÇ))Hݺ̀׊k׶oo!+mK$&R$9X1w1[b YG؅ \ - Y؎zJSH7oA\>a1tr%Y[ѩ)&UD!1 {`1%X\ Ԃk=:_i={Uq_v- M[.Z}jg*ͪ64\V c'R˕ ? E=ٴz.f_&-Ts%dR>|djE?$2嘄dbK[+') #BnA\9u{ }"\I܉z'>۴)gϋs>+2YʭqTr1 1] JxCMWp;xdf}\qrލsBK 0ƍ8e*)%KdHAuwA+Zi[5hR3ANd ʙvr1vNp0w={۷o߼y KtJlWPD":t(h>{F dZ?@pʭ[߿ߦM/_',, ԫFiG{ jN_ 6P1QO&<lxx8"I=f޿c3,&1א:ؒ{iOwI.Tc6ͫBd!DE97!vY6'Ph0$3Nf/ 9~Է*T7oQfG?91\LJl#"9L EvAŧ>!7@QOD:J_+OfGzj]b_p(0Bh={RGj6[d\m jB("݌C=;AFsUXti83$jtFGEG\~26θTcjԘ`5@-FjB䀒aE,j7"ɮU!8{̠}lxN$E2dh(̬p2ꌡTfիQO>O* aѿ_)l۶*FO&:܌餵wlt©'6v-6MZ˻…9;aY|$zxqxQk/L_( h2GD4rEVB( /GTZ{.]7n>'7 * !@6$$ZjJZv|>9}9̎0غuk`r@U@F1#nyɋ/۷͛S644 590Np -W#&w5P_Ђ'P̿7oG K:\簡z <ʝ@ IZw¸Pีg]6KC6! r)f1u1 "X2NzGz ȓRM]?LJT"6Cu#jL;dua;!Wo$Jwk&F29UDDuU8h,Q}1u01FNCzpPc\GA=y<(DT o) -x`RRt'jJC<&"{fۦ9ZLkdoB>"{! p@iW5l[dϞXܕ+H^ĠA%݌m  oچHDhSR š! !!PIlh@MOKd\ODHG@1g .a|8@dKU<`)!@Y'GU6׮^oկwA3LˤTl V%J뢞\Z,&+"<<",\]RNNvdDM!?|r9&L&_es0hš=@eT^Mt>eBN?i҆ #32NΎ]rsƍT}w͛5߀~{IxHkmsJ#d߿[uXvي۶mРp'2;Çoݺ0y{DYw[2\r6;Nj: NT|cJ,vZßaÆUTټyL&?+0m`(ZBK:<q#T}v;R[h8~c(ܠ@@bܨ' yX!U[nժS |ɓ')G3+`˒-oEMY5쭰qa0)Lb9!Y_ߥT`ݿ[U"*{~P6囨(W^<}}kOQ;v1￉ dwB̬@ύP+Sfc>ُщHI t!O9١EDhsJTM?:dE GKY\O飴>B9:aÆ1cƀRo-09fr56p& U48W\ VgMŋZglXF$j[14"QG=ZXI4\BLTlw3F."&.\ܚzʷӟ=(^ڵ!!  7nzHrr޿Vt@fͪV_Bضc'p'ׯm۶w L u ܾ}? <ȡcݖԓofOEgd8l4sh]|}7mrįݻ7BɧMAr`ذa+\ؾ8؆81ح[7*Z"W n]C:@SD z˦m2CRߠ>|pϞ=i5_}mV`lVsP{s {#o<{ޭ\E3TL' W*%FDX |D,XT\=n@ >Tfm{XR)MIPN h99[Ga>M$ܵX,4ml` RSÈ;$P)|6aLE=!FQ/^ăDj+1 "#< J INFFH/$R)e,6cux)԰lמ&|L_ "av"/E(8z H˗ϞF/_jRDݸvh\l_Oo^T[z#[։=e%}2{LJxÀbvn߶trpHq@SqF*jێ] +WSKyv)J=V+.AxI]nsÇ-[A,2x7Gnj7Ov /vaC֫[Gڬ jŽx꯿zÃ%P 6ppYM*'ZF\~5koݺDMbʗhx+ghs]Eo+NbM><`UL]BB5i d !joܜ=Nzq2\%mG'[.M!uRQbFxjj5˦@IHO'0DhzrQ;!%m $)G;q|_gp'uP\9 D~LbxHgkAlrr(!Ï,doL}D"5-]1 Iso߉H0Z/Bxe! ;fwY[ɼCͦR+=4/_LMP57H&vatMڬk>'p*WRA< ENvL&U)9RY~o-j.kϺ".$gYLKS׵nޝȣt/Ջr*>}X]R~~ `*,æ~VG7V6r$rJ7m.͛WV#G5{8$Hsr޾};k, &Oa'.`)(촥&~6ԱCƍYǂfUm>oog͜9dhfħO@UFoWvm+(%{}ϛ7Ϝ;?gH~# ]qZ/e1<ԓr'(ꡃ6O$! Wy`H 5SB2,61%O ,cip=I*oC P&3E*MdҐ>y bZ!JlDEaN.j…7*wr21{dB-FAϔK)⠨W j4-x2{_7+X HY tP@~~'rUZ[G&#)5SͶ%#>C $!'[yX q F;܁<9Nja >=-4>\~7PJ~Q'@ln175?_^ٽ{/],1R{*/_7 Pit'w,Fp\ԟ `WGgO,_ԶpޮW-=&"D!Z\2'!24 <<<| 96Ŵrbb&Nlg?DGm>>I Й)wFy?o !~ڴ}np`@,;p@3gy{7wvK>U~fM׬^==#0=0 ͱ*eneʔ{DjvhhHlUT8'дp\j$?nv\lR%ݻo(V+W)z.]O~V}B֨d_M:Jn^B5=0<c ڒ(+_Fj&]xi(%85Vf0gX-0wLNEHɡѫ,n!n6f#OF6I6=H(i[˲B '/BL=p+r{;ĎA ^K36ҊydPLIL'!iDB4WT 2e9npqGaԙa&t X/Vp8V_6EvBz0>t8/A :p=)U+V#@3"$|@Q@ PV9QA999\.מ50 \1HTyV#s /@ s lF *J!xE= j B BQ8prVB$R$ B bal@ @ b/p@ 8֣uYY". @ @Rt7ʺpl>k@ |fX,ԵfF!)@ @(8N'@ b 00FT [z8l6tnOHM@ CQ+;u^aWJb١BuG"|JLT)Um )HӜ A RNQl |ej *YJ.O Z%JzЖʯ5}\8n P),&Fj~~~ymͩ`L9-PA Ha%ΓN vj,[>F&(" h"aaO?͏KU|ZT7oW;ǧu1m &nkxifn]gA HABƩa b'UhEbqzj*ޓd ˋd\'Ojƕ+_^$\k O<[Nab"M96 qϬ@ ~f6\0Ls& 5?@ftɒ.4wyyٲeMҁѥJzPSs-3+2he$zhj6t P^ٰfoUˆMVɫP2no/J8V!c&jϾlϵ7,&jb]sg9'͝f?>aϗ')gTqAq:%W՟|;~PCOh[ҝ_ppDo~r`"kdfL߿oAg͞mPU,.@FUwef 9Og|l]@V9!Õ-K{B6Uۨ)ʹC&d`]m07ahc04\井.>IQ"'LYŋemb~#) LNM!}g_ZE [DOç<N kA&U /JOҕDdŶDG<:=]ibS8T'mՅmU 34c]c~}6Br3z^O(#F%]LVb%mDц&dUD#N&:#,\0nz[o٣w36o" #sU])G Lh>U K/SGjv|Q{)WOB2ꉷ{%o#ߤ45G7ɴ(4Ti}}yY4]V!_l׈^WlMͽ2/h4ʔ kt[da 6>Dׅ[i[S|~Yz M;(ɑZV";F/]qX~mD_'מ뙷H˝+zݿ]ea4~tk|ڌڎd v"QPX[ӌWn(5пGU!VXmT uՉ~K,-|m!ThYJkeʋtqUmۖ) &IAh=N7s;S N/Y*YғC`<*{=w ށ龣t;/`,^zF5:kzH.8Ǿ=|o!-c?3 d?x^1\")y \/E\+6Ꝑ+wLiyf͡TJBB!CbbbV\=kh˸c<+~ e/{sە\Ǘ)Sfێbx}m[1l/GUZ5 \`Cp:aY雇Tћ M~ځ9Bۏ5e/ {r LpoMtf8WюЄhJ "nٶvuy'˸{W}*ȜR%UvkHBذ'MT q̌]s@ZL_2 =2BD ^ &Ϋqgm m)YhB:lޡiݷ5~aוP,{fvF1Ö:LŎlqH?=?y3_O/><#}MA^}cZ#)@pb`'-w9־;a٫{[n}J1|u{Su//?<0,}mYm L)gyp 1dR]"h]^g#LS,_L,ڳWL;p4Y-]AI|Oy᠈LO{~Cs%{ \-p2e,5í~y8Losp; OϺŴ,3x*,8gMk-[ppm& F +_٬ hs/$}"Lɱ K#G*9,͇c L=cӛu] U|~_8R`.L@.Q/݌Ƭ6k~\7 \OƑN|GR%?` r, G "N6]VB7ou]: G73i\2(>PIP}6nMwGKZέ, Wq.7s,埫Kq2hnGY@ . Ÿ.ԅ bFԈ~K^nYOsp:{R9+SfŌF^V*/@qI%&F?ϔKp㳌x1 v*%`%:I+ܦUrBO BCc\>n2929z7_<{+FCd M~ QO_QЭ(nc,Gz ݉ps,dҊ+̝7ow`""A`rX j&~.;\X[Sa$:Z6YR麝Qa!g$ /*.Z73T,-h Oզ}J2jLhj(2#4:Ó||jpv3TbTD459Yx8mYRvh :m ުtTi)j8ՑMv¥sL8nYfhK:9RVs-2cf91}睸I圗KF̄exM){@ < ='ĕwTYbYwwYrEm#eH o S8t̵5N },9RyaYpK(5,,CS_HD@?prW!=ewqUSo*PaD\2U¾)?Y6 $6I&R{,4oyrgf뻘 x P W$8߭_)Wɮdxx'=쬖-[<}d]F2ҍ4] |*ܴaa/v~be}/TLL0g'w:**JyyVTzϵWL}NSHr17.?֜FVWJa6OdN:kna)&Sˋx!G-d;=wʉ+6pNc?i*Ꮢw<]ig t뛚گOJ5uڷNJ4W-N~Rc'y1Ђ9qK0b9Y bt cWGpyzS']8kM.0!`Pyzp|Z( ۼl%Q87R,1C=6I*w!p9]̍ {8 mme׫}}qMڴW %eVtvrrXyp< k7 AHs^#mu=B h;2|.݌zBvX˗iF5`Z#_""RP قXWR1='xP&% @go:ǧb)?y1L".ծgًzN l]AtvϢ1Ǒ. Y*YT7bk'M!<~[Cl9WA#+/io\ϴ/(ސːV?15V~Xb0[Q#+/b?'s1 Go/I4A-F$zTwry܈tVp!?v gሒ.l ~]w}֙7 E: ':KժU7l4h{ٷ{nێtbrU}oP՗kB"OW13'CUЫU6+np?|IJ"ZR+OtA,^t?]+ŎxVߧ2k 1*#j /N]7:Ox|"VwFp>L̓ Jg!KPzYڢ꿯*33b1} uWν{s}vϠi?31$y*BTAu'1L'=42=\xN, ,ƚi=MlL6'%0|.10DQPmOU$J»z;`4KO? =+_zi ڣe$0<f7dGEQ&)c,4Wg:cklfCW"@kɰ!6lA~m2fUFd$vX@&X3h0#i˭i޼ 6vlEug{+. 4#1Y~ځ9bo-} ; n༸:x5S5!+Xܯ.9V6x8`xR'T4:n 3|rⴙ Xq{ ̓ahsi-KUo^x3؆sO7/ʉ?ךcN0Q/U*={AS{ͧ F&G=axػNGz}~6剥FT#w{f#8eZO"Xbz//ԔUA||>N^vZ5-<7k.؟;njkZC>`*ߤզM䓚jtT 9C:,=ai\:?NK6-ƅ6e);rrjYJTTF7h+q.]"X]x姠 jx[.=)i ӎ_Fs ){@  UNgfgOŸ E&)yQ&>~x_:UPeEuQJ9v!c1gA ffs 1!(\/37ܧ@ >pT$wU,g?A]% WjJ'NT^-00!Gpܽ^?}|-tΏ_>OsEQ-x5j5PUk56 e*T&nxefVȃzvRY@ Ekk=AwD(FDF{3=AAa7 Pb6jJ#IJ .luhp[s*r74 u?E>7'}t(QLn:ٺQD]^6S{&F{\Ʃ(j4XL } H>5قܶߨ 9&NUXBt ] Y&jn&.P]#Zaz7D rY++LžP5*5+l2C7-/cDzF/F&Zlfjt`XECm'HtC1FwkU9(K܅%2}ڛ b;LcLQ{}O@D__Ƃv'"݉u2S+tkZo%_I_g"mҪj!wT!Z-ioΓhle.Q/ֆ¢h3m$9p< G=R@ @ې%.S6<Qa)4"## [ St QmdS~_ ~w@ RDFD"(R[EWswEƦvmrӇY [@ DfjPs<@ (󢫹[BQ8O12tݲbxxEP!& (t@ gnjVϣ0"uDRBW*br4{5 XSa@ UStEWsDQ ;ɚH RDFrf 9BFQ5UCw@ q[\ÝF 7#gt\3֣&<EE;Ɉ] jyeZapw?'uu?3ǿBQE"#[ l5h`|},.}uvә[Оe?9y+(gd͖FĦ)Q{9X/$mάի%o ;4F˫[ZbfmBv 6.tk]gKvB׆Xͣϣv䯴M|䆳`C.9*@ 2ٮ+bxg )BFهxPQ0~M'snJLd#vPU̯ϸjte +nxky|uC."83=(vX|SW?7L8H&RG\H}/2?Q36S*pš:=htKH;nfDK1ɏFTA+80 uw^xi45ۙ--F^%;pc* mW>waZl> b]jۜl[]!1 JY#P2Ί|v0: ͠am 90L%1OAL LOmvXêW\x׶oh٩ޝ=;2Σ9yȃ*y 0F@ _Bx_A.g^[7 ԊJѥ}j[>;t|"]w,]}g#Ƥ,>gPaLpm^jmCfx2A5r& 8!fMg+T,RʧS㋙fr3ˎ\ί NvtP7u:ڛ}[SK? m>mQx!58X$*)ΉK/###W 4$MMɓxjƕ?==xEpǴJ !onb5*S2k5EڟsJz]A&Z>;FNͳ mPyU1eW/j>}Ks"7Ӿy1kj5ׯ]-&*$4*w9V>,Zw]5dmܷ[ٶOra-5Qs{֏Mvmec )I^Y1㜑N4pdT \eK2-Jψ#l-dݟlp5- O`lԊUk-epurǯ0ωnoW[~|Va1^O4㶬e6oݾAko栙.gI9훈t.[oС]@볈5ʄ+Pj^]wU^i jddLR1Lqu/hU?gX3$fvh 1qbbNH Vߙ_T{.9[ܹ{ʟdUaq K(Ep[K/2fH +>d^*-NM'@kśˏ5YhH_'յٝ@\W)U15 GHp퉸ppʱm^*#"&y-/}ZR+>upQu_-W^ ;qGgژM iŻ.d Uf."6T_kךiշۡx> zP}C__ubkAs-Ŗ0{HP 5;R"p-s/uc ^*RE5*-";3z' oƏ>} g6+ B#@fp?ٞ?8j';Qvw=q?-oM5PAxb,Kkg$, *R~co8J~T̛vjUsu3_ \ ȸu/?o~?yͦ3xԓKZP7_%q!)$%J +ZņΖ CLwEn`ͧ+'.::sn-ҧ]s`45HiLi HG~F1,VJ<Zu[oo4f(V$y "[37m6hR"T5-Vgܺck@b1!(K4H&jX²CbNaQsMn?as-ۙwf<an9^abK[ݽӳ `q8b/ʾz* VHquj(V[nsY鉨Ёwv a43}t #IaNiZeDg޸xmDŽU A+uvu:,TF@~ߎ,Q:(1f빶s}_yǍ`Τvks+t4#ִb+aՠ~?ϯ8uF%>X[!=[;;U'|3zi MÚ^d+bC2pɾ"GTsΚۘ;b#r zT`Weyčr`ΦĻjSP働LOk`bsr@^q~Gu+7ËǞ9̀v7>3m+֏!LlP9nIST>m,ȟ|=oyBoCIJS,[͙H)P:= j+X=?&hd'egbg:x'}dkAt%v[aύx"knQ(^HBb͢k!GhpEw b;b wѼ[XTJ(_HefC MHRrq=Ѥx-AjG [WSkp=nov«_m?FR#vDL0iEHa64wbkCPli cQ)FzA!/u@/n"VG@Q]$^"QQ7f@ DlmNzKWG"N˒n<0yz '{R*fX̰۷TR9 _Sio[OnV@^KȻHtp{N͛n/V,-5`w?۫>Ϩ!,*HüܯB3$;|:N 4_-@-=2Ç'O@ݾ Q+~po݉O,l5򀑵0-?oy!c?&vhKǺUVYؚ֬8N 5wbkI"Pli cQ)tBJxBLRJ%%%)B)ٳ@ w* ӝ@ (?B;Fa+-0!w ɀ8syܼd @ @Qll7&;[qLwQ]xvNH.$ %bZA,( "X@@Q@D* zHH!~~e/].776zJw# ݺ ,:*.dښXj3!dB9gU%v5 @2%%[2y%Z"J$6I.i #2B:3D8%!D{jL HP#SB{#gm. )QIҚ |7Cyڅs"Ws雞)d, #3b`EqN,8,x0G篿?qa{_]w2'Z$r m6>/m&yF6 EcJhyp/% BͲHaM3%!t.h%'IDmd(gl!s攼(vݚidA$5>vR@&Dθ LPӃ464L%iz"y5%GxgF퉣;(T4]"@[[B ,X蠈KZ]ף}j?v֖[&hTЇYZv9YЂ,~+)R SZp3wBoYRʧy'A1-wE`;l~aj2Mutm궈jR2&:%5F-Lm`(hY8Drm}@:)e긲0Qډ~zYBBy.^. 5iʂgz}H> 5[Dio [τ)4 ӄL=w,H;#$7]k}m:,Xhg1[v˼q)HWv ԠSP^u*1 B%|؃oK`"x>B.$KK $(>_V|T8sL @VDqHLO<(H˂ŽV^`EHrA#2wrc>e|owely-Lʙ2o昡ANHH  :} s،# 3;R eH] h΁> c$CmD&nܢ QN_ L`"Ѐ!r55,XPP,sYNQa2w֓g;EQma!z7?A/VG%NxN㡌3} `X) XOݠP A̤8rT&h&9 -iV@S \h?6c~ p=1\^GX`h0h_L[g;E9qyW2o̝+T9`0̝"Ϩ0UnG1ZYvPc:Kj@Y\pﺜ+4[ 6\8ß ĹAA"-<54[̐A"a2‘.f`\X#rB5Z WE~cʁ;G8ysPYˎVosg׻En_a;.2oܹvt([0̝"Ϩ0Uތ0 Erycm#,((wP9 #T +, M(7K&MB' Zt4<T `̜䣘EE8 ;Bf= `s$hjcT{ BJ0K1zʞ??d#]zk#fE,淭{gn"pxyժ4O֪B3։#Znn@`H$btRUYY3 pEX7XB@2X "J< g, 2(dNRK]]nY53n|X";x́ﺌ"i| eG 27>R(4Ҩ)8 *TVwq?v>EŁ~L_+jsєiJk7m_.uꮽomċٷwkc٪?ʝ;yz _5o:R췘Q8.2wr9-X}3w|vop;J.]:f/Λ圸.4mZdexgtֳo_xԔAlzU/9p6A g>zpj :"0*9E9Uof@IB^ d3p{ 6|}(87_r\ fV[HXqBŕ0C24:D1X5)wE|Ph"It:;[KB.;#[.)cK;b8 UdةZΐg;E9˜EḼݗ9Sy[6lXlYM],kEmͪիG> ?IS Eac E;(ְʁp+a|  4x RcKb$D#@i8<['øOaJ" D (n|0h uc]r ,\oD-Iyq)IAo1бArBJЄ$peS&HIϿPZ"|ݷkj}6[K8zj wG CpV[\_V-[ZVJ yV4}:'sSMuh,!RywF?NM"Z)N<4T>D6)E<)Y`- ݷ_̙ʻآ@ z:R %Pr6;GFFa[ɦ݁?ϝׯ_ӫY茝ЬlFC`v#!H=%. K1{'@:W[%x >Z 5ᅼ~#W$)QR).8 Nۉ0`ߵ#yj HPG=pba Duu=-gdE 2l]U4#҃-U5#WYVj!-V2_ꍂ%?_CR=7băYy!:?a~r~`@1uK N]yǟKLI,s(ٙ)f~2g*=uZY$Lyr)jE"*Y#=-U-aJ0 BH? ѭk\u-Ik@ßF;\3 w?]  5x5byOO}9׫ˇCdEx?>_qewes_4w*%*{1Jd.JQ,0G~ ě <հP=PgRI{˺gxZx/A'o' <- M~㷐IIo q NdM͙(g;E9˜qy̙ʻGa$e? g(s<&p$QeDw50;gQ `}xo$0e<1`4A~Y->e@=Vln3(%\҇7x{`'x'g?ީ?_ I3uypX'<zj}YjWG.n:wwk'L ƫqk@((Co?Њ۱9c- YNQ2kQ8.reTif6=?NEص[+mx-I@yHkͳ1T6O6Vsݼ[# .Oz7զSRzѬ8;w&it>#͍- F r9-X˻yowe\y;oYNgT[*A I'bZW&DNKj[> YNQ2p\}/s.RjkTj9aGH(xtK%T =i* K hʯxӧ *A鷏^Яkޱ3:CMAuD 왫^3{d,H p":F.+QzrN lNAxxMc~Söi6`I5q+\ϔO}gOg;Eye̶O90ݗ9Sy̾S^QTxJ#6TJ!00bQ4Σ@̤0ŝ򪣷.VQX/|CXKa,P\#B0jIewJu]=8m>[,X;3ۙY`;.(̙ʻGryj6Sh|BE`Z@QW[NLL0Ңh n'$fWWW ("(ES~9t:T*Ҩd2Ao$h\vPc:Kj@S]sx&v sU~҆ s{s!_84(H$7zj!!!D"p8RFMɲ`qbcۛ ,X`j뫈DbfgfDA͑/maXE& %r&J!)FhwckYX&Fvh)H0C%x̜lh40ܬ&,X-ɋZ/q &&Y Wɷr'>?r20ńm]['(irKecЁ3'maJ, Ai| B,6%1JJTq{?NP@S \y.:F ?湌\. ]9 ^kV5pys eӡo- / y,s(Z]!0Hrʼńm^o]`$|b3#yAan|͟mYB X}Z(7dVTJRn5_?NO}iw*b1ƙ#J0C!G q6#Fpxar-!BUWbăCavPz}Ȼu  1OleI` H[Jp|_hyTpVo }:}]Z eR*~/W>n~wO~22g}ޯ)6RCO{q]|_n;r)m~עؿPcxu4W!X<)L 7oJ,LUhΈ@nL0%kWm17'aۉG, X W7M´aqU#ʛ0v6 3nG y"r2~=o!aiQ9Em@owӄ#{Ѽro˛!zh,S 3*f}7o7*ls櫶7*g[㋱0y=<,*4Lqyحъy[9KGH"`<~r15 GLRNV90 Xז:cYHX Zߋf7Ԗ]1uM1ZB$ױOJZ3s*0yi s25/+i[eAUnVBUh0K7Ǜ0-sڶ~Vi,l7lvDxϜ鴎dJxԔAhz.,}0 "a\3 .US+\8+~qMy}I_/U11dNu*--ᦽfnАK̉s<۾b^u!c:}}M͊8pf,4|wzC~(B+&N /E iSk2 Ӻ]rV~ɻi6|,D=5 ;3#NEǷ(xueMfbؑjLȈ UIS%L9a,J9d8muؑ&"iB*xG[1XUFczx2Lx}K2k kUmԧ4P_z/194} IK̗'-6VHLS.3?i،֌&%or+tom\xUKMesa,hZ*!cIP.@q R@L ^`0kIL̬@N_d2霹cbG=(v{o7L:+#*++U*DB7+fԜ飙Uzx9/z6S_|p:gV9\8BiUR 5osr৉ZuI{W; 1;5yK~dQX3!k2y%B9Gͪaa}ى3Hʻt+朼m u9Ut+cK?vPS=aFGu*ҖX /$ Gō>eqed볫>{rlSw8fY+Š[Bs/8pf€OLTJ$S)ǃr>xqEBx.c,YgˍkS*`>6!Ԗp{nŎlȂl*HÞ]M%F%I+&-CFĘ{?{# { (m9kW [̀$P;ci*`_7״唫4k5h2`u/X"n63{HExi+ffFy307/w ङ(.g91c ꭟ͗+?mJi$)O+lC4A[o%6ÖERQ[Yq}|dddYY-ܹ}4}d 8'Nq: ,pGoN >, lY֍4 ̜Ȼn\{$‹L#rs sZo L{nhҺ c^'y]>`h*.WB3c";jH]L}N`cLsI•M9 L3hPXC_5UK+bGNJ0n5. O R]Zx˥DE/k_JYƍ{ofژ S2dߓVAjI"e'O}󳐝ɸff3*lK9!C5Lk7gcmwYXw[iܯmVgYQ ̵6q\ N(QND/Fo}+S^{qz5l%OPehtV&iE[}[3,L3ZV$9G ҼE V'1 8%hpiD =ϽvpsSg0Ur6fN0ZVsOZbڂ:go;O^ϿzH} !yW%ʐ_V8^!n*H0soJފ~ >&X %dęKydQ`B +wݾ*þ(fH[[.TtkĢOJ_&d2G ^[l3x 8bou)Y1G]4ؐl)^ &j q=\; oe9=%.وc]˰NEá!6zVgʛ!/f^&1ƈk6^f<éG2S'7&V#́{7=6WMx.muE[QT<7Ƿ7~v^gmEk8?XD)SLEeQ27o"y^8~^٢rЁ̇QԡJeff=smu*R?ܯ{_ٲo[lB1V<.^Aԩ\7g6Z"?ȣk!j`,xwb p!Oysﻯs}̦6ٶ(eGw@ӟ/zdo~^<+ 5ܩ3Ǡ(o:_~_mn:,Ajs\nR]Cm&aeBY-H]dQ{ %]t^ß"zH[F͵i`k$0% DӠI86YiG7Q_+|˜ NV>hk-mZ؜M*.͓Y d`D`_z|Tg+&2oW5# {!s J~+HA|#f^^|ے Mu:4 (ea᯶:R[a`^XbļX>٢r۰P>Om#)R¦F:j_X~2$0IIOH=:FgmCܻkuiɔ2L[%Sd3f5R!oE*H&M zZ:=( ج͵-dpd'⬺ZʕkRñLdā(T4T6/lFC[-Y:Bo]k DĒ)iq ?.]Bt71oѣVXIZ_I^RΣuJ.Pohp];^CV~yWT*ڂ!=km6y橭2~ūNǽW3]tn˾Y4ZB:&Uu `ZV6!`X;B!@nt2d j`|?Ѭ*:P_$|F+f"3GsiG޾&{/1ܖ`a>vC]`Np>K z8:î4$JEԣz}5*+?7tUv@ݳ+wz0J!O3pΫKo-Oun!o'y;0~,K(  ,:)㩹]#@Yy~o֔# |*k]WJ_ #A:fyml'N\~W?zo{xR+1ߎ'7u1볱"O{@C)^2am]K9$h:&u;7K;Ov C%@[}Ǵ)G Y3Ƽld \{oK@9~I12&wiik뉩C۷Oa]N"kɞC}uе;7N0 v 7dn\@ <*h/oΩ s'$"w-_[4xC1 }qւxL3*^1ՉC} E;h}a[rf6g ~HS,,64Yd?XP^q+sh/Tumr޾z#çO uHn3ogYsyf۽g6#ڷSck s{|=FK Ṗ~:n¸ofdMI7OGosW7xZKYw5g /sw=.EA$KXEYUڳzܑ ya?-5kFP;;aɞOGlmviuyLH|e XHhvEAyRȨmyi$H(|uXB$ S^:͎z" ꩋ7}z~e x<30TG + Ξ?G"@.*n)c$O'F}!w{&&3%P|)@t lXXdBbfH @cNy_ }燡L`2y2f<fRxF{ 3((%q:?SMipBGceA sWXiw{,ΜZ̅^zDSZ!I  gbQ~B㨪lǿ' gy{ܢ$ڢO+ɹC$`(Q$oͻOVG5֖x')!8FzITwHH1o ' ~*ߧ`W}`J8P&> ,-F\SLO^ۼzuH5#FrL0$` yɬI>)g HߘmfQI{"NοCl@=?&MPVV嘵  \.<!]zeaa! |{vQ4?͉84\WCέ΂[g ːEJmHz%^[-5Ѐ@"]@x)"4C֙/ L.o0,K?jd&ݲ]@T8j IE@Wb7.n˫}?`ABrwgg>t,+Z/č~mʒ^rV3P+*?JDh@$I/z~EHzT@a jI T u̢דrTDI|"EM88eZ-1F™Kc~sw»_MKԹCwj{/*///Žۂ9U3}v z }'7|~ ~w'{yvM~9y'-c3ˀ/T.==3yBճw#~1[R"1o"Ӌ?&{O%wNt!bS{3M#13-X;E.!R?,XXrԓ;%%Prhy iksW/hVhK'xeJ v曅}pF;^_)_{9̛npCwܑOnE$)5םc[✟." .7^;EYD_Y.PUJ Q0-XT}ku;٭5@ 8B χ}meȤF=;F'cdUuẢ^iQuw-HǞ4/7nhczԪZGn)5peǜZ7gZ]N8]ogS5E Ϭmyb',QЀd %J4G~ĚJ}EeIfI f?+xVJ"!3P0/Fcb@ <=zR$`h$P٤nE#{1ʼ|j?,*zɓRFy;U`|l m6mQ7s;ȷm!è7¸m ;qH(L諲+=S|p&PȪ%y&$L2nJ ܱڎ3#XtVٲ.|۹L\ %.\?ܵ-_tjvKKneju9 egw|>_R Bu qRp`W}z oKX,8x.n=`50~k H"r᛫5c ,X`}Am {$_  D.ʔ檷 {9 ,X8̛o\/I^g`s82wrc -*\tv+G3gT[*7 ۿxqXdexgtVngxԔAko.-c qIŠx-av_6  0g`.)gӂe+vwXΕg0iYNgT[*Ӷ83ۀ"(ι;'H#L_/fPf8ۇn/uw5R? ݴ>gŋ[{㹰p:\t ?^],wwF7;pl>zJDODYSg]; M5_Wޅ@wcY`;.J-sPad;EQmaMb׮rНF=s^"0 jK[W ʹy..{3Pu|fDOp\ K_T]^9w݆nS"yĕ S:Bs}8\T^X>Yrf,=mץiw|U{ qySi(w/˜|Q_ ?Ȳ2FHf?w.==-wsFPQ"<0*ɮ"QOUuɄ ;=OOPy~7?!}_!0fuD9Cߛ:8 ʮ\s{_>g1>4룅z<>yخ[, w9,sYNQa2w B{P?SܧYu3!%Eώxޕ{|׶/<%q3zJ̗P &:NXً.}>Fp\e,sZ> K2g*qG=zjF9S v ?Oӵa]&rB5BnNC& qy;zSߖ /B uT?Pql 39L0YݩZLtΌⱤjc…-ԣp˾=:e$e~"#>Þ kz6VlMR. |Re+h sZ˳̝eN ֢p\}˜|ǵ(4> ʲWju\YpBRB~jqljzD2 WE $c9T9_{2H"QUɱS5}V; ?FG(8BϦ;<}𒅅2Z?Ƃ%8"/ѡdt@)`V .ܵb&F0_թ(_hғ7CōA:ަQVn,pr@YVlE4۠ڳ,s(gӂ(w~2g*B9Q_ׅe 4WO]S Ew]/KH5:"m\YpU=rQq8wK#Q}b=9 j9"dZ\&;ɉ0٭Ja2K%UA=_nx FxEB.$KK | IP0|>"Nοǽm˦}/ 9&wyxM%e[ٽ_W8tGpa!s^H-^'!UKw(ESgc}#^"ׯͮ6DT:{wQ~I W  nxs={Y^ |7sv76b.,1<$Ih YN:>6a G<)Y`}˻~eT2? H/VG%NxƗ"u} `X) X΀r``&ők°4)Ԗ{EtxCb߉l"ܗGfNXj5u#0X̌qPpL]2dO`k2mj~1 >n`?h-ڰdŨ YNQ2p\}/sGA("G܀@Hd_JL陊P o ;0M4 )݃pDzj;Ό4cѵ^]"KKږ̔WH9ܦ4Qy1oEL3jYNgX[!sQ#@V;}NmvMMIo^jdd3sqI&dݔAcYgFݱ} 7[\&,X `n;m}=ޛclzr85YC>8l\ *SqZmox(a9x2# N4HG1ς 'bcۛ=Ӗݒu+g$H0,(9go % zD" ʓ25Tz^,Xp.hpǙM|Gcp2ļOO}ŇA;[zCYzLL$Jy\nMMm}}}Q } `!|h\tFk^ĉWX0sbȐ!oT<[_\#9~E{uDjT%A0EK" (gEع0NW0g T9`B1w_i 6Ra:,sSo( `+vBaPU$ދNP3$`HpWl)s=TRD2$9Zrz2(G#kJ4|+1sA vuKԼi=ݎjN-_}}sWϜ1DvϩSΏ]>#|%4_tb-T`'C;&;s޺nk0oauXΕPmEQẗ3'<(fQDi`,t^{M1qDX|@8(5ڲU#{lr@.QBY//rss7-BXYV T]'l٥2__+[aPڔ8|l]eOf6'NoM_j~;JUnMsg;s9޻EܲwVe\{ eaoGacK`t- I\p PPu]l?f?`3gΐ7#=:u߹"3(PfEkTេu!XUBòʾ\ 4?tӻ|%.e?c´nw(GCSABB~y Q '#G^S&]Du}c- nYq{ ea ,KX*2^iuNxk wpM\wBKDFHUB*U^uՅ:j ZnGXGݸA!ɭ]! 0J}?6{~~=CL %@ JڧE">99٪4?2'ޡgϞrwU&'szlm)B09+ˁP6,u,vh5{.%V=&^2 nZzL)dtQB`Q'[W7A`'[ZnxG}J-N+d_rV v日eo^дĩk?'Ung3nJ/wda/v[O&P&_b 3Ҡ+* 8ǭbN"d)- OIs͛SK'nq`^MN;jʪݡ3eil*Y5zsa=j, gT;~x%9[-o pzcnaMF vm;yktW@[#߲2YaAal_Ǐb8Bt>dR]1@ Pfp"m`sRHOz{)s:ɔ\: W'08b AY4ڐDa\N3yp}zwq6CPXҀת[;ZD-Jj:|g_+c<< Oi"iGov5eUH\ImTK2-TN`*t;ֽ2<ѫ,Aa1X2"!a\Ncٸ& ,OiEQEEڱADDĦm0t KkK**$r4j.ئ/ftvY^^q|4~7咬8ZUy5\-D暒rม{|jst " ^Faz쀋3nwYR׹]ۮ[hosG/&ߩ9%Rlw">ow#ۈĩRr/8EveCX |RWS-%]߻@׀|5ݑQ'ztNef\hM]ʎ s2%@8 àXmWhv8 0B.{>0nZ4>=/lZg3ȓ 0'uޅ/l[WG!qOLҙքauY AUI#R~"3'j8XU];AsҊ>zN@m`O_2Ggq?f#RqyEL߽ղ(x?gkktz_%''nx-C ~ɱÂoE J?y4ԩgxb.r^\oM"[Ԕrfm6}5;׋W?>g莯KmXodzxzUH=Fei8P{<''gѭ? ysEnas?Ȧ]6,e61~C9P5C풓99?͸6҅5b|^cy-$'k߲5Gհ'AaXRE y?_}RS*ֶؒI ;,=.fv#A|O.±8d>eK , zJFHHMwxBPvH$ IŊ|RJ `*ݼ(aLOkF[w :)4ⅬN1}f®cbgJ8j? ~cO,*1,<ܭ,m,ޗcЇ Vm\:A{TNܻw]{9c4,Dw؋7r{ˇG. m!VP}FĮ֙C 0|;VkUB8$ˡ .bd)Iwʎ6T ^mO]kz7`ev+vWRڥ'F{`p~r 2fFp/Q5PUdT;";kҝe?'U U羯ڞqevwRy>-n鷪::gd٨<[C;2q\^iYy6tѪ_>tcI3&AXzBԱS\kMY Uinm0 @)BOx͚z3SPUɫWT]ibFWwCNsjԻi^U fB0MG0b'z!z:dkQiJK$, z3 anғW>;>8z)pw[%rALjՙPnQtDN. Nu<l ߥꯢcױ[M9+?ݜ`p1瓂-B $qQ~0 !WSo_0SG섨gۆxB QCt.8yWq=g{9|Jvg^:]<`V\]~Ry-؍cuVƆǟbޟj(]'&2ٚ%H]${pDὃ_+QKO$ȯ8f] [}) .ذct' PrFHV=!PC!k["q!{q>U RY[Ǫ::b:\ShZKC@|MԹjmT!+L9Ylc4n55XP@GsM^ꪵ֣TkQUS7JF#E#`%34]5k c#;F}g׎C(CRU^EAȸv.ϞE_6fDžn_:pp?] [h}u%|%!j vs3'uN4 qB7,P<꺕ovR ,bt00/v1}  J'ǎQWP*(`B"1~05hv컼m dJ(_G w!оέ9A/'p[pB{%n^o}/wauүjnXh[nkwuO߬P?fUWM bGSRj(oI'$# WvguHЫjkrjWUGs0+֊ sHkGB8"!Halb@lʕ g8ɄN8Eo:Eq[E8A|F3R$ُ\2wfGQ#z;|]J>^)|Pή˓'V&V}^YBߡBKۿr$ݵ7nϺXbun[Ƣ{f24*lRoғڷ&B;Uk (*j+lp.;AFaùI[E9h\;qB)~SӂۚT9>QFYO1#m#V0 ;21s (VvbU|uƩ^ֱ,'2>rR/]~^j#{|dk-{7L?ncwd± ]}"*ɑR51ǢP+o[.Ztm]4|CS NL+^Cիf6lM[;f^`dUCt(pլ'2@'"-?:#ׯl!-mYxw-N"DH" ";Yl.*nRzS304\LԞ&eBߊ N=If|BQ[9'ɪ!5d+0 [ n޼ѣp,.*0M*6<Pb)̑<USOۮfz5;bed!B|!,HErĘH+_͡HoRn3)75-Ks'NZCowYjwf23XkÁH,.J f#"+Eyl6c090J12rfɾs ]aC.ڋ$mhs {d% $L~o 5cƌٱd2'"##>a_s6 (h=L*uP ,z'Npc '!Ř(zwS=iMMsBr캧oVO(CalCpLZLbZRiB. uqB{s-))cUD"gn-(UcJJRH0p#7L<"+ ǀ4h.FTkDdDx'Ss~iq!K2'6R8SDΩ2qVLIа͋f&orOVLiQڱBcM7<|Dž:"1b?;byg(#l/Fwi;12FրZ}7ijޓ+4\,-K̮{f23g6ae%EԿFP2Y".L^VGi|_deTRY%~JS愘CRpP:¨@ `do5;ߍ3F \|(=ʬC}Qip8Q Aa"{MZ#0CYVW#/=?0oCzߥ߯2}h|-[bܑڳ24OKxH**s΁uo;7w΍?أ*}~tKZIKb<gCmV77#<{O͛QMtx9Vj E{`i|Sr:֘ꞾY= c KwШ@x5/Yk$jLzJ^`St 1"P]jʧ֭[S[IWrT!*W=w4,yϚb&,Q\quG?K.lT-tP<NxH%7oy.0~$G{Ws/1qTpcfN!cW UNG!vO(zbI*$^ۯ}}f_(Ck=0WyUfXu/\YW_+;ROܲ7Yo̊O̧lxRTjƍ\iў,1siG6,KNu i-\9-31vM L w)(}A쾔eHy22"PuUA]Qq(}F#9{t<ݺ&Q0v6oN-i+y%6e;>ܪ)vHZ0f CˌOWFx }S_׳{g?nqY-xͅd8=|v10[A^;ϣ?D5V3׫{lNy)Wd'VMI]~!ک_ ;0lI &ݎ[Zn"MYr#awsyI3ڱ:|lHyT~,{Fs %66]2-@/~j@jBԿu޿{@#5^ƎoP47XhYoV BiNodWmܠWWizyAF[ 6<̉zUP]1@ Pfp"m`sRHOz{)s:ɔ\: (dqà,TmHh0^tW]DB Nm!`A?EKǾX-_poN]0 1ºa7A4pNмŢpO}<ϊmS}QPiQg<KZuky>TȐEUQ{J +7B ?T-Pxz&:IĂSpOg):E Gw괝oͫ5!<-ʜPh":4z*M'}_XS72ﵣ(!-PV-4T:559%Đn׬`@5Uܨ:SX=Ct?1XܠpmͫK痡R=8z4d`' q ڨ"d]Kjr U7S@"QޕNvvP1٥e-_~HǏ^J;DSAXIN)cW|W|}#G:EѼ@v|Bi$$rp*#7;ϚVP`*t{ֽ2<ѫ,Aa1X2"!a\N{eٸ& ,OiEQEEڱADDĦm0t KMDI84hL\o_hWd1nΫyԻquSrIV_[J3 V:ѧ4_eij D`hj- }ԥw]1=(FRT5饟$zS=GA U Aic>ݍ>7}qC@Dh'L-gNz>J`$߆,8g~.Y!fH=? RC:k53{kU )qt$ ]NZC@kf!3y'WΘEsb)D*HRGc6 {ǎ˾ژ:UJ/s'={W³i`ڢwrx0x v|7oz\-,=@3'؛:\{/;0J݆DtNu]VK1a.Q$q .G!Fw-Qf?~c٧GMk,sy^wλ-v( NSzY|IImf(oxv؄Zponx\}ZIAUI#R~"3'j8X[3I+8 j=mV~u{vPJi?1~SWˢIztbЋ-u}ܮѭ鷙VINN,[|cߊ+!h~hRS==\弸YCH]Pijoy96lGrBˏ.3cmoѫ,k)Iuҥ |48j-Kd *?~ߓ8Դ7A6ww-l0{@لM>.~vǣg{ذB}jTK=a\Lg+vSϸUJn_B@JBк$ yr-S+VH#PbO(CuLS9Ƞ. l#0 e񄠈..[(H4c3H19R)UyQ"]aq :)4ⅬN1}f®cbgғY8>1Ɵ?U?fooI.84e#X=htˢa႕o,mo^9 Ӌ!IK˳\sNw {x Ұe8~#|xPpb `ߛ ߟgD`Q8t czg3a0xU"@IOu$qGQd9E,xs"%^)|Pή˓'V&vS}ҁW +sKV 5ͨEaq[.Zۻe,oVO(C?H(֤}QJhljK+EA\40(V`U|uƩ^ֱ,'2>rR/]~^j#{|dk-{7L?ncwd±<دg^_i[x5sL([斋9ݻ%-w[+P9E```h~Z@ъ?].(hò6[K_gQ葓rq@lZF'oߒYg.c\ۥ{.Քq_byZD][G gxo}-PsB\N5MISʧ>i>u[f71 .M-l!XW I-M& S3|D?Nvz;r۠|TN<[$~v%p E cbun˙1oVO(Cz}C.&e:Uf15ER3t6I%VIH}|Q Za4j ,TyYI>L,Λ!+Ur3*joEA ͞rIA[^WgHkgܦ d BAF```@Я_Vv";Yl.*nRzS304\kO=MDsSˤƿM^d Ůt$y$ib̑<Uu?]o Q`"C"VBȔ/$"% H\N3P/9YRg=\&%63fw/vC&oju4_CݲzTNc|bˁYhIږ0@J@FI|0PS pJz'!䤂 ˄$މQGY_$zӴwb-k^s#CɌm:f=E BwS{ьV],S2'(|+FZ#|%{މà| (\0 &, +fӅx/vH?? T>=Uv$oG穭yG$V1#~6ěkf.m{C~9ԼդO+^ oS3ֺ6E`?ImQPIAҕs7 i0x cD$(.QjB+h&Y"]5aO3->)0maAx[;=AR#g߼B5 0=8xF0@~H>>Vַߐ!ɇ "dEV*ro?3f=1ÿ:xfW?.D?ș(P?%i&* bˮ}u+X(B09})ˁP6CNr}w=~W/KN7v-_j&2:vŨ@QtB`Q'[MR!r.NiчW`6֣|іRfXu/K "Wn׊luFtT ԵOʂXşq#WZ{' {svz :byp]C~b Wz@ L]wv5St & on_/%O8rj'[ Vfzb(]3IR@ @@0 mZ5vEDs,.BbmtD=gۼ9t\۔?Zso:#Yvhɦ˜U3T0!R׳{g?nqY-䖷 ͅd8=|v10[&C^;ϣ?D5V3׫{lNy)Wd'V>^[W?C湵Sv`V[mۓ'M`D(F>ZrfNcagaOqiI1Ԛ`(jYTD Yd1pXi\Ӣ qEa A44k^קwwmN8T> ų{w2zO': "._b}rpP_u?v.ȝ_,B!ROCtxιۆ:e9/棠Ңp!d@3O5QgoD,8ys롑-ivWxc, &H3(Jmrs$@ C(Q @@֥>tjc(FfڡBmc$"dBb~MK!O?z)Ma%9]峭;_y7/"Hi2X8O(3c e([4CIyڑgn+(of0GBK]_A^ggϿFkae _۰,0.JltX\H'Ŵ~]O" ""bӶ@ĊJKYތ˥$r4\m⽋ūC'HN mQ: M^TN]* Jz' K@ӃX `!%@X|P]q0ݑ pwwwceMs9pЁ=b>yq9:IFj/SK#0=vvřS^,Bg }=iأHd2 ǹ<sA 1{?GQl2hR׹]ۮ[˯PHQ;z:}`WiSZq|{LE^ɕ3ortQ휘4GB R"ј ޱ76bb]*gܹbמ=QA6L{gϯmc da0!|wrۙ*ΘFiv#A <D>A̭[&!t);&%ʔ@8 àXmWhv8 0B.{>8bP4>=/lZg3ȓ 0'uޅ/l[WG!qOLj5Squrqc.VФ@UI#R~"3'j8X[3CI+89=mV~u{vPJi?1~SWˢIztbЋ-u}ܮѭ鎛VINN,[|cߊ+!h~hRS==\弸YCH]Pijoy96lGrq'ߑڅGiex[NfF:ԿYN0O8Ȧ:K"DBjj 5;XO*@  0Ree ,z]0FW p´NG)r !᫼x!>{pLٹkؙڏB'fO}bhpn`Y< >\n;ұ \+'aUbz1X:$iiyxݻX.={ısDLob\B Bt{ss_]3# N~aL,w&Q}3*~A n8(A "F9tnO{ι xv݊{{v="vln~lډ\-D Q0~ ֓qlLw+ S][wep1瓂-B $qQ~0 !WוAo_0SG섨gۆxB QCݪWmƽs9{T{cAlx:HZ>pqtIc7&IZ>yXwDЀTC${pDὃ_+QKO$ȯ8f] [}) .ذct' P@mc.9bTQ«aVe````h4/BzWu`q?I6uN4 qB7,P<@J7;)pNr1`UJA>OMik`Y%cѨoFY(c0c$&#l{ akА)n7wy ȔP|#nC}ŝ[s-^N<hiqk>DH4!Mk3c 3֣qఏܳ`'#?2;1$4fEZdiH(`2QPr9"@ B mI[ 'Mó|wwV/>- 2A'1PgI$۝d̎ G,vvu%|ӽ8qSB(~š]'OLaw,ڲ3nn 0d1CSD>\)s򤉺Q4-I#A>3.5b FF<a(qwd@#c$P¢`UQMu,Kx){K^%}K{; f:xqplC, x^<;gIp0ߍܢ Ȋ0ҐGL̠Szс߄ٯģw-N?%-|%Q_!\6+viΑUG=֧M&}:Xi]o(,:lG3vn1Ä.v~KzJ^`St 1"P]FBۊ@5Xt/̈́`>K^&l}iF=%lØM㭟oIT"}£ځk{<* Z;@o]"[?vLZY.~C'~0 A#0C5 ?FaF, nZ/ڃݙ(P?%rp ʳX+kʨ>00 LNw_r ,(++&)A\]fϥdߪ ?RS]Wᅳ;]1*PT9>5?l֕ʬN!-8| uza4_N#ȕub3,-~c N<1 DIYP_3nJ/wda/v[X\d+3@MY`````hNhQX´' }D @`'Ik' ZK3zĂmZ5vED9nFѫ{t<ݺ&Q0v6oN-i+y%6e;>ܪ)vHZ0f msB5D8YOXKVQc0gT;~x%9[- pzcna Ƚv9ޟG#vm;ykfW@[#ZrfNcagauOÿ:L{jFR]1@ Pfp"m`sRHO&taP 6$4рw ,yz\]EM;P,QG˨Q<ힰ$\S*T yBe,}y翮]A!ROCtxιۆ:e9/棠ҢSts=XE ͸CQ HP '#?6]lS^ @6P:VLqIV.ɥKWVqp6336F"BLviY(/w{I?$]G/ $Ⱬ|>:|g_+c<+Ij)\Jy춂fs,Tqq+^m`Zż ?? t+ML7QŅdyRL+jon/׵.ҎǜJDDĦm0t KMDI84h\÷l Y^bv1֩@c{4E{Bul((hWl.eDLJ`%,:hGA U Ai/ www7V&|9 #WgT`24cg\9už"lkmb!+GR\qH_vm:zfo- B!E"nD=>4өTkh-،8D|=b"/7v9vNL#Ł!Q)tQhD`qzZ>aNo3I|1kNj va3W[ [!fHf`````u]ݩ9MO. H lx Xy%|nFGRvLK)5p@AڮRqBa\p|p(h76}{d_ش2g:'A~aNx _NbzʏB89בtFkqyfŋ?ӎ47Ǡ`t/~_̉8>E9(p`au̱AsҊ>zN@m`O_2GgPJi?1~SWˢIztbЋ-u}ܮѭ국&''nx-C ~ɱÂoE J?y4ԩgxb.r^\,R!ǀ֮b45}w|̀y_MN# A}Z-0(~wcg,y.Vtvk8d>eK , z"z6 DQ' N2͋x Q.@\-FAiVϏSCWyB]}`>sa13% Ox'v|bY55c-io14 8ރFY,z_؎A.XIrn.*1,)(@"R@p)aHWg+9  q:99zDN]~6m?7,u=MO;H;KeGjA{{~0߯3~ f3/._Aj􁋣O*w1qN3AS ؾĄ_&#0{'ͤ?ЫpI}y?{tVuxRJl]w-K;>þk*_3M^s^572}\S˄|qK}w,S,sRClywpfy;s{f8Ϝ3y ɋr_wBB+)R,˜߿_'wV~yck=aO̶VQ;yluӮثP jMy\"e҄#Q .*I=4b$IhjwvDLb&YVIw'TO-jlTy8F444FֽCw29⪏w~w)i ,2qϵJC,*= Z4Ǜ=_°=hθAS-:ތ`nVef;'3' @sW\\!SkY"K`4rےP7A 5js=LFV(X>G8?\}TS gNo]쵱Q&(Mߑn.ֺٛbL1#OW|(yW1BD4A*z$3vL J6ej59k?aPpK4WBrE;i`(ADA>`ʱ֙_<*ww=4v~7t`>ĩb~Ůy|nf%fn{1+3G,˭yg- Фff;ќ'?(A'uAETYc\Ƿkf-j7w UHd-wƃs)WO=󥂝 (I^t%06d?sEW+e~m 4(< .)f=Yc՛սӾ{ Z5f<;⥫;N{f O+RI;p<1B%>34bI=o%|UN/P)9(7K}Z*Α%AM-j-:2)(&Je_s7G`X^T 2m1z5`B$hl0+8[`΍ʱrooO嵾r[UFPn{Z럤Wz<NaS]q?3 o\~>#Dd|}i@z.==J=%/xl Ӻ{ +'UWU"Nl`) W~+=eն H"fش95f/ltߥ{ܥuMMTM~so5k_'[va嘝ћTս#o%iALm'"sfO[1yfiR;@P^{P^絾r[ѡ#(ER&zlmx<+ `Xd` :xq&$0+Fhh(Ap|T^zH =7߽%9}N }6IUi#wē*cS VKK_Bs˼H qc4-yQm!{m5cgLӫkR/nܐ;gNpG>W'J;~vLГwlQO.UE=(sPn~-oc&2F5}r;v ݏm$:i{q?r}c a;h[_T3MwD(A>ro3yO XΙ~;&FfOhՖ,@:y^{-<tBs=+OJ e@tō"6Gk{Nȇ "}w6>cO2dlҾkt7'bD6qE_-GDQA SDyOV8@HA]2}TݸqYf))"l19[A W%/")i-bMx]j[[616'ۺ:๫ւ|ҽlO?hO\~ϖcg Y^{9PRU^Zx6T,7UFnX/ 3]<+<~L{%c|y4t䜘)W)mћNd_SF,uyoۨV$7fwu4҇s$ 3?lDѩų|ncW*qvP_̞Nc\jGzbpŐsE|=}hGIDUyNX 7ht"Nc" -0|O>R+BU>"VgA*QtR.թu'I`pͺK[\90H}~|eS+{ZXU_6KHul唹·/讪,.Ls*TlꔔK{wו1zۃr8f1;7R&;D (ƾrs=g*h߾%[ bpe]Xdy°[;G\s=ĥ]bT:KqB$|s>}׿cU5{GbKZԚ޼U aNm>aZr(W~| {aP٥X?=jH%\KRaDq w\2(! A2Ӄݟn&f1{ UŻ40qt)Ww,gHEOT8sr!(l" 8w~*" @`;H QCz!0zĆ0LLy)$(6VƦ^80xʌ/@+VpZFQ-SBK.6 jfOjcx3;; 58 i%Sf|>v^Z)PN穭۷S(}C=u r<)=C AG楲z5%RՈD0U8wo- K;cVԛ.6.#ӔlW~E+{:>P"6e~ܹγX^:~uYABzRDڢ2={n}zG+6etOݠjv'/j;NԲ/0:›(:= \J(%1WR 3{x.<ãjaH6hl;qn 9)9zܿ|I]? @ܫ#?5wd(yk?7{yU9GM&q[jole!(v--E999.G&??':Ǻu0jM4V"GK-sɍW|9~+FO{oY Oظ16N*{xi[fV"[}fTŃpc֤f46p8 \)=MH|B_gm@DQ{Pn,Qq厫R{G(ƀ r5TTTPDFC8G ,{e)4c>33}oVt7B[vӦ6q8쯀ۃ8dފe?AM-j-:j1l<;ۏ pXDT$}I ݺY;'W_|蚁~ugX؍-0FQ{Pn,0FQ{q[jc0(-wf ΀"Xg9q5e@8 ,A({fv%3Ƞ 5` %"#qFPU)0Drzw ·!5|g&ϸRC'*-?3彯zl]N{ϧ.唁qN[~eVy57݊_4K K/>6ڛ(9NK&r>vz[ 6gTս#o%iA[- ɍ3`LhbAp|8*"#, Q3hxvx@Cl)2 ř$p׸qĹ{/-AɭݨO_5|qÝNwGjmwV3vz֛q=& }pCf&Gw 5čw~ӴEͶB߉XvcݓB,U5`k({D5 DBRt CE|Ŏ{U"`00Bk`*e5hEj)}а /%?{ٮ̠קvNgd_gEIVe@tō"6Gk{Nȇm뀨^ xD!Cd]6NRh<(0e-h πE!d̬`W ں>u+<a ݽ{I^!moSwLcg Y^{9P‡NE.^\'rT᪇QXӴ#Op|.D{~@=6f)g)xV|J*ʴ^r;ƷF`i$uO>:zS۩Tkʈ.mՊ6%/@H03_YOfn\4ᣏ$BZcG9S,8lm;'*r̽ ?*N\vpї2?};^+ Ō5wD'5.V=D I}DL[aKWolStܸR(:t)Ժ2j׬?OsZJ*W.^=2-1Ue󱄴]&^N|"BN=BǦNI gI &)\ƃ"GCP+Ő.r4#AD0- ugzilLDAct&OIP*!&h3~o,V=N=_>ݒILHH9)ڣf[G\ /Zl{};tiƇwkB =(48]ᮟKE^ $!H~zPҲ3-ߤ,>2f~zoc;:%P8gūOd뻇WO^'HDĹl9k{fqQ#tY s"0$buk*F#bխg`A$hT#3 SN!sUt?;&OeyT׶~R͹;-ZTe_ b_}"KSQ 8C3c4H.~_rN&hfXAR;C{ȸ_-oWeaw>iic+W-Uh|4jJ߼]߷[C[s}z'`ʂ̈́Ww6/ҷg:8c՚:O QzNT ӻ((ؕk7ntb;Ŏ[Â\E\0&DSw;B.BGX۸Ďz~Ê|Vk@%|WP)ܥ F7BtNN"hqۏV?Q(oߛccG O,}3jb*3E~x|}ڸa7ˈǜBxw}~Di֙و7}ARG!/S7膭' =*/5ȻF 'Ϩ6ΐ:lfwr\SP^cA0W4tT(:bYSm?]0pZ:4?tsB>`}p1bs e9Φݺe71QJ%萸3! /eJ@ $.a:'ǿe~K|zˌYK޼6>q7MyFABƤ~18uW̸g-OziY)d-z1o* 4wvHչKHWeYhѐc*\d^æ!jAc"/%1ɋ/MP(8o;AÓGTFO˼^W ɀ/}s}@q/7G{!YsKؽnx`$;%IrGb%b߲*:|$DSɋ^4qLwGRF LbC5PҢ~+MG[8N0UkÕO[sM^)`K+!|{{Fͧe!/h5&҈> M̨_ y̚1LƦ$+n>+YOSzĴ+6w0alH-FE4C0HTJERık5Db,p)?G?/afU< o0Og="Tl(8g2=qJD+9w%q.ެ.28E(F(NA#W-i uC]3Я>wϴ1nKԘ4PΘnJPeUBN)TqY :\Ŕ/YvZOΒGMpرM^Si߽xNE՝n->If=DYdw,biEKOa$'Uiȳ#^:3Ӛ4e?+'TmX^FtVt)3fBscq–v &aD*ŗqkɉ0"hM aN:MC`pIhJ@:ר.9(={jNR{Zέ1N1FQ!1 hB:Ի췷]Z˜!E1Gt1 R#9Fr4komP7sAaZ!`"9@"bdp9Υso } :O8=iϥȯ)S2Q4G4A8¹(fA8 `QkJDF,`U~ )],<+BH#_3yuJ{{y+Ss+nʼn–]X9fgt+NW|a.3.4nhol{;-`y$o3lڜQ]TS1nMȜٓ`G(Xb@8N9>2Bs {,&xGc|DP@[ Aq&)= /# el;*v/tBtNwGjmwV3vz֛q=& [ށ7MӒ5 }'>w`فώ z&w|3;[{ڦ [#D|/݀hQ H\H!2RqqW(a`T.H] jЊSV N}%mq7}w6>cv:#s=+O7H.+n8\sB>tmpGXDZk'2/D "ڰ9v @Dc v_M!ұH¢<6Q[wGTT=~``i0J1$v'y,E B O1Ǝ+Sd-{MX>@ :]غxrQKFNGa׶Nӎ\>!I*\IS]D:rNLیsvp?HųSPKpG?Wu{15ZK#mћNd_SF,uyoۨV$7fwu4wO.xBȄtz⡊MO^rY+b-yn+#.Zd+lF q89kg!/ZKoˆ_|T0nIğPud]i&/9޸"]>fn\4ᣏ$BZcG9S,8lm;'*r̽ ?*N\p! x iգ+ߎQJMNT L$F>!{&?EǍk+ CrN+fI`pͺK[\90H}~|eS+{ZXU_6KHul唹·/讪,.Ls*TlꔔKpaWh8|?2\)|XtYH "iQªa& >Dʃ$L6K<QUBLfފY(zkzP˿}?+%5<~xc1RsV7"uSou˯<JRC^;GP٥XjLީͧ;LKngj-wkB =(48]ᮟKE^ $!H~zPҲ3-ߤ,>2f~zocs:%P8gūOd뻇WO^'HDĹl9k{h`G}ȣ(G>2x!89D`HĠ DUn== Q"SaJ0;Z\2WYEL|lofggC1.+ǤGƾ~sNbBpeW\4(B!cˆq1|s`tJ$? C9]ԋ]MW43\ r}T䡽sNdwϷO6g=4<<-ml{eJ #%@MW)vxGT-A1N4FT" NB*r* Je$YV"^|\O02͛7O)3 ;t`y$qzLLMxw{f|yû3 2]x_b3ě A͇ B>MGM]%ի)J\`qZ;CUl&zT}/={M֤yjs ?/2l ޝGq.N4A(ؑukXp Ɛ;Bw;B.BGX۸Ďz~Ê|Vk@%|WA]*bNps.D䔺q-"OhZ_Iiu]ϋr)<0Q` |'?`QU)Bݹ)yG y ;EP>o4lě>| r)ʣYxtVPmY]# ws[֓gTT fM}'.M!55t9 sGC IBܫ#?5wH:}@cvl|p%t,!\acYa6inM9GRt :$. zdDG4KG<a| G7H>.g^+Xf\yoG<5Xlʣ0"J2&%`~+qSDQ澛F"N]*DunG-K*E[TQŴ"*6ůfϞګL8d$/7E*oC3 O^QmF=y/{]-$Np=v&X! bNu BRܑX츊Qx)r"*!0=s4MQ~q8Pf}=7=G*~}6μL=yoG)EG' gk36QiYH.es:>Z={4⭶&H-G>3ׂv&}L5Ӈ'IJmBsE&Lbj|C-Zp6qdbTI>sD%T$EkPӈA$ƒ '3}offY%ßP?-{ef`zfVrJ\Y]+dp` 1Qb QFv[8蕱f`'֟i3vc ̘@(8'⮉! 匉UV%dM5眵0{ϥ{{+!^L9pNGmqŠ-=[ת4[/]ioQ`=cb,;UVZת4[/EiznB *ͶJ,qy_:q\ Z:XQE8ER$-8ILˆT/֒aD0њ -¸yMC`pIhJ@:ר.9(={jNR{Zέ5=:Wfbc !1ڕe*la,҈9XHŐ*p1:Y{kqaZ!`"9@"bdp9Υso A :O8=iϥȯ)S2Q4G4Q+FH(EqK(3hkU)0Drzw ·!5|g&ϸRC'*-?3彯.[va嘝ћZs a/ltߥ{ܥuMMTM~soUs=?_-RIH1:~Jܴ '7Rdάn ;`(cӂpUEGXfc6;c$ڇ*"Rd3 NI| Q(cS}/ث/nY8_m<@]1d࿭f^7{▙{zuM %2cg~;{A*TYS?ws옠'or7㱵m!8‰(^8j@C;e PucpU5xq \ ՠ1̄>c;\^1{0s=+O7H.+n8\sB>tm{+? .UUs ߵCB Mwm؜Nr;Ip”ˢ)s?B:IX1]&8jNH⃊ 1/mF0޽$/ŶAh7;&رtje (C [/C.9*yp(iڑ'DT" 9'm9Xh`;bYY)A%w+:zɽ_!yӿuZԿ^ȧOnv$5eRjEҿoycfwYOh}q  $LHw')_")֋*-yn+#.Zd+l홭F q89kg!/ZKoˆ_|w+K _O\Rq.4YW8K7H~۽/M_/QN+= N-l[!6s]#{vzt寚5 )҉$@g2$^q K^<6~L&ol_BxuKuj]5K{k]ʧ9MI-%eE+C/{Zޖª*QXBڮc/]w>|AwUeWv!mdScS\x^W`G( %!J1âbDHQ,LV6!HT$ >FgXyjb61=תG֩g[R397/8^Y>y"qo yQql_#tY s"0$buV0n<{{"Dy0ÔPa1 vS"d)c?θc]WIEe}? )ܝ-@=d^13.>8o)NCbiOҢg<| i᯳/x*.VTN*ށ9'2~WUYXl>yz;{h5 ݇JMqE.1 'WOurJϸߧG~W4F| w(0BL>?`ͨ`P»S/&J|FCWKIVڶG_l?w؎ I@{x.HR%eOM~tRjD_@\x}p%t,!\acYa6inM9GRt :$. zdDG4KG<a| G!<|[]':W̘ŋt{k3=~#yjؔGaD8.dLS'փFn;:wv׹j>ӮYwV/6 +yz7c?*6ů ݵW B[CFGa @+>yS6m.0NfDaѓ2/B2K_i\j85놷 qHXS]!w$V"v-k$ R)EUB`z /h8&QLp#&f1ZU{(iozZ&T#ǭly'{Q=c;BOBw)K=;vCw I^=^VGȖvUk];Y>A$s6! Q8cӮثP jMy\"e҄#Q .*I=4b$IhjwvDLb&,yesLl@2J]ry7s NA"f9@,"SnK@26xC 1Y{b6c7mccD9EfvSCM J6ej59k?aPpK4WBrE;QlllSDVXg&wuh)GV "ώxN _ƖCL<5;&&"{SeiK+Zz %9!xNEYğ֬7mmY*{WyڱV2l+DF(0^qHwPjo$5$IḐPAi%%24^p^Vl16&Er8Qr[[|:YI[joQkԹ)f=5M||J]]iop8N1AaB,AAai N 9^w&yU^k&ܶVt(.ޢbs(R9#0,/*jTy{yڶ=_eB$hl0+8cg_ -unT^y&AywwUn[{K#K>AM-j-:2րD?vǩ?0`oq^Hed)ǧ BsUwO.xAfWoo83g\yXIp[va嘝ћ*r /wwiqF{%gyڗ[)g@1æEu[I?%nZVK[{_9 ,E̞\дGP^{P^絾r[ѡ#(ER(QEPlB n'I?8׋>N.sHdgOJY[G>:/78FνwY[,nSft,}v:!{m5cgLӫkRp۩7dΙÇ5Uon]?!n%/jN|ck/UE=(sPn~-w1 =dϮoX$HN0? EJjϯ\Cی9oǤ;1BjuhB_@%lpy}JktF (zV@ȿˀ8El/4]"5J:y.^{-B Mwm؜Nr;Q9(7 Dw~*" 8}{P``W qq*n7nh֬YpppJH&dLΖ{PDA}Um|PliǶAh7;&_?wEn9o(ڜGo:Ӛ6箚ڣbZY8Zs K?-cg Y^{9PRe^?2af"pĉ6fg)xV47B/`_K}-MK#$|uuS'YeהK]6I卙e?M\Czny_ӓW\pq֊Xj-yn+#.Z6ٓ&Λr̶9j2͎R{PaG11}lvvӧl7< .Vv4YW8K.*2;_tj5߅ogò˳Uy,p+?0 3}Q٥Xڿ`Nm>aZr;S"\KRaDq w\2(! A2Ӄs ]8gūOd뻇WO^'HDĹl9DwoM;Iq[jd# θQ4"VB趡C. BB3ˆ0LLy)$(6VƦ^80xʌ/@+V/pZF0Rp /Ҩ.R>P1b~>؏73n(d{;Y~gOK^ B9$C)1N.,P"XeHu0y *WȒ9qZ<߮"M6$-U鄑LSn鲼{}0GXEAMr@DQ{;rKlDa"Feq%''YfHتÇd)7A$GX Q'_JA=w7JT3Px e:)qV_NKBGa9-zc/5T+GY\\sʌ+q_k>Uqrl9U_p닥o^up~G]Mo.AB`5Ŵg!T!2@yj}6YjD"}Nr `!UIMoOhA!>9J =(sPn(orUn=Dy8NYT6tL;;; mOO?ۺ5\0.&goTV_OSԳ]Q?]@ B$2q瞛: c]{MXeG=)5uqf"hqۏVL fM}'.M!5O0wj_MfxӇ^A.%Yy2VOݠjvj+Ry%A5ܖ~pUZ.s='uah/JipXt[OL;s%7{yU9GM&q[jـyhڵtxV} s\&p C_ 4X􋟷-pPυ&7^ɛռk~fjęqɃ'cFo+!o'7*n:+ ff^JQ0D' s!#f#yI^]bqA}7agDޝ^Ը ׹j>Ӯut-K*E[T1G_JMy]{ 1ռmHUfE6 y'hxj30~j!BEAMrE~UnQFA-+0 Y5X./0 IfưDAU`|PI|շ#b6OfQ?.zf#2a ^hy̚1}@}xp=cs5Rz:w͉GTI~G}1gFpdQόxЕ46p8 \)=MH|B_gm@DQ{Pn,Qq厫RQw1F r5TTTPDFC8G ,{e)4c>33}oVßP?-ݴ nܤv8!+.k}w7Y嶵CYFPn{Zm3Fk6 Z-ӡҒז-gg.HJD/)7[!1AH8^+ENz@MQgZ&ZBP8H#.c5$Sp"Ca$X6ɕsݸ v>Y")(ip#\0OD m8v+x㍏??Éy ,+eÉ$w"Xg9q5e@8=,g# [Y")>&$`8WD138L >Z33ۗeM "Mr{رKL@/1Ɗ$7&DMԨ1KlHFz]ew3י9e9,7<iAv;h\G U}z=NWx~ G9:؞(K9NƳq_l?z=2Wd}?yҕ CkBuٞՀFY@ B R U9940΀wڑR0ȑ#e2 n;CA~^qm[8cs0j0)6%p.8̟|Rsy{=PGo4E|hΡmtBC/N{L2GĥwoJ>}`¢H㢽G.n}]߈[wQ5dz9JyA^dsV6E@ hA^(g;@k'f|䪨~jR [Eh1gLd]VGxѼjof9@ Bghqv (݈'k>|2!{%_ED+ 'Nqzs-tţ !'K2L-zv˭vl'oVF}wU/mjesQ:6؊+m*Ȗ]0b rx_0~+L-~q]v\ψ rqYb4OKgXW3gQcʓ(]' 0YÌܥkODoV˃17U%%OmÿEа o2/MnWۃ9dLCY{OT> HTw]4 yRxu$[*,0dA|oc;8%PqO Iw+Oҧ@WppzKOkBEq-EYʹl S)h:E#ЂE @6@sd,i΂a,X|K䄱xiÉR;h9UL2äL#`&8RZ٧ NؗpVZZҍ{!8P>WV?VyJi.Z_VV^kE4K#fh+q-iILX_ ]53\$%HOoGٮSuw?[v W ޢR{X(& Ę6OCUHw֥}ES={nȃZXoO.q5?R]ON^`ȼo݊m *9Fm2ssvC@ ͋]w3C U,a4 <)RSTiS %;!X^TahWWI9e~+r)؊;NԴIߜ곤g),J,0 -"G[]kR^"4\qXc6u1w F! QA-L>J2zOĠ‚̈́][zN%|AtXhhP,gN Μr@l;/PFn%LUSl"k!~vu{-CJnʥteb>?ܢ^-54_c-hbGjPW"* ҁ `wqa,UPl)86=r҃\xIWG؏8K<[Nʲ {[V,uJTovq={eOdrя#D6͸ooWa_~к_+Q~(<-ni?]q=N='DvE9$ OI7t͜\*#sRUpkP_8D;CRgXe68.eߞ='Ӊ a BKGCohݠ_l'NaWJZI_9~C̸?!{P7뙬c`w}XIv&; VBw?b_O1aW 4=I~Qg}G28o<34ʹe:nd|@ foy䀳!,.!?p1 ?g*E5RJ?*|´Qd,&"8 g z0`B9SszI7v9+r勒x1ˮ?6[3sqT"gQ?\ 63}_*ș]-EC1`DyKwQ |SvW{N̊T5,)"HNqڳ'r#4!y4ȥCqB.,rsE0r߁^3;sXʀJ30eġWGJ1vFxOcX~#K4I2 GY.wOUðns~Im3;[=7]v}`q='D^wq { kF\\maōgz#p\fh9@ ^|Xhy1'a^* b`NuD|\uJu.:L!\;h '1:d1("An񣅎YL`bCUUof޸q1S}%[nU- 1(ׇhj gm=aw?˟,Fg>\ͰݧF<\yQ>N}߈xgRuv8hD/JS͐4˒~>,'{ϗz}ſWyH.;Xgwc۟ش3o>=[y>"+us[bk'nニI|MOo١_=Z@ BKT&f<[rCkm9=|' 1,IC`d(r%aDCF~}xt#_{>nTT 'lq'=VhiK٭>e@wDu4p#zޜm[4y Vm|]2{o&%7PѺ:ϥQr@4Zhr>}(xl\퓙6aD-{.۳ݨwlN#CRћ~W&:P9ɧSb}p";~[6u >n]!"+wo`~硿OR j%CuW&㛡h.*l$+L^ڶ0H Y\a}g+sOM uj Wݢgj{O-_|ٷjF-0W0aQ~qVL#ھOoĭ(ϚqMpgj@44- @9;w@:@b `U#ZϿ&30\*a=W5eO=F) !l_2RM>,oJk f:#}<ķgwB%n0>8h9"U{?}?.I)=htB쵡s;d*@ Ek@[(p1pg =+^m;c'**n?t/H!F ̚j, wwcNׂuq+.SmKW+vltFsmۜ+XeJXEc H| itlKS ,mt$pg(-M>tѺ(Gr~RʫP}vUکaCm󎭘b{ΉަliE #VL '8^o`_K#1M:#AĈµzFT@'Z&cyZ>$0ĺrϝ={ Vm6.fOcEUh] {wsS6V ^D> 5 -lD.4h8㰺Ƕm>Խ?ũ{|Da)\ӥ>S2:pAdiYC itCy˥ =2'Hi_  Zy*M3B]2\WReAoٞs0OqE!QꈥbwpQFnA_l@4=-sZl0mgbf̕%$UDAL8P4gwNz]8`Z/%;~9RI^eɟ^?򭙹si*ɏˆ3bFt.,(|Qb־o[2-ǀ- ߙGоmNۑ_%91+SհxP"tnb";ũ aߥ L9oG. va-*0"WTj2xЫ#z% \|Z@ 3ӲO RXu$ d3)OWct>++ "cx KQDX/G ĆJ%+v73̲qeցc68J0EȽݪX+^pŞJ IQ±-gU=,_}2c~sޓӇ> -h=be;Wv>~TSG7"ޙT]*]4@4 xl HQ)*/U:n H6e BA E8tpZ(1h(A1S`A>:Œ^xK??ր,uWO&!x` 1Li!Q( ڂasXK{F4NA,bX(!)PmI[vi7l4_Zqa-+77o-SmٞMŎ. dv,BkH[fEl<B?"E.D!S,0!)RHZVSX{ R9r;g4sŭw-SkkUk3euXg h@gN+|(%>,` hE,PŌR9+T`.,&`iRmC @ "=|Ky9f C0l9cS08''AuHѱz-.x#Jv}}f1P< @  JRU09! 8s!,@ $`Y*| *G{8''!GİYy呍-~f9Lգvoʔ)qt{;-}BN;_IW"6/ kFX e]F Ѩ[X6 q,c?h0/Վ|X(``7 Sm:kaK\ (,GO0W_~ќC0DY׼nd_:;zO.|Dk1Oxh |"ӟڞ#_s.A~ᑔ>AރI'^:L"h L*Q<hKE?brZ/Uж1 9)hYS-_7v,_Z5nŜt*8m]e#" S熴M:S7 )emP83Jt>@Ԇ'Hc'^|uQU1HR еծCJ;uz:7>Znmݭ#gMmTec;ώ$<뉃,*ݮ҉ [Eh1Jq0+-HQhh34ʸ^; vZKm~Ăar>`S+m` ɏ SbV6r7 UQaqY\Ԇ;b;'z ){m(X1zvmztZimN(Äb4OK?#jϓOy'3.&; dܳǨ1m%6aQxbɣ\5{i7^!m eI8u߁2֐}+֭%!OcEUh] {wkЃ͙2ŝS<Ÿ^֎.2W]Cpx/ŋYv#ߚ;(<#xy`@gǀ- ߙGоKSs;\='fE|JX\VrML|8U!r#4!y4ȥCqB.,rsE0r߁^3;sXʀJ30eġWGJ1v@ Dӂ<9'yt,:EkYuWᔌ'1:d1("An񣅎YL`bCUUof޸q1S}%[nO޳U F45GkhĥOf `/{q:`r'G>5lΣǏqj#F;Kܶ븘@ <1E^J `ar B(H@.T %27 40F@b ,gW@ko0ѷr[ -}E*A E`T-e3SFH#pʂ`Ҟgl>c$!0JHr (C9djׇϩd9:קF^@4c:%Q }X#AQ>&oS퓙6aD-{.۳ݨwl@ Ίbw;0@ @4;L(0}͘Ƣz 3{)hlS7G6@  xf)?שk)mR 9ޫƳN57[W'g=7mq>Ƙk<@s3n7%u&uZCKCf]@!X$4& ґj͊<1BxD`I)lduou\)YiGr9ջby]שԵzr0.gʻ6z@KtԻRۮRƀV Uh B-Pb1,Ř6@ v8E@[ F0[ Ap gc99XD3lpQ4$Q @ ^PL(ZJ@*_ 䜐 F,`>y[#=@#yjbجUku?3W{xFYQ;|U#sb߼`y,n_cf'RNG~Ӳlۏ^1-d#YOt%b+knP]5`@ fQ,TyRr8 1b`jGSD a, D0j0)6%p.Sݣ'/|hΡm~aN~ i8f53Ύӫ$Bٿ) "?bnu}#nEܝGyk҇?;UӠׄ@ 1) @9;w@:@bњ*ak`2å]?sUZYfB͌M7A:y,I5HgwB%n0>8h9"U{?}?.I)=htB쵡s;d*@ ~P 0y;2Ж : =+^mf ljۏFhb̬~b{}/zx;}-H[rbN:߶t宿2hˆ)OGa4׶͉)r\eI*sC&j80'f ۲o B,+]?QZ|uQD ;H8tkЭNQ-|6֑sߦ6*1gG~h?Aތ\uVMnWL-"w^S}h3&[!hXZghqv (䚽\+m` ɏ S]6r7 UQaqY\Ԇ;b;'z ){m(X1zvmztZRMGrqYb4OKgXWn2LcT蘶n(̘]*D@`VQ,`xy_qڸC楩*sm9azd;qW`T'ᱢ*]^=;S Wd_Z,mlJ#>"!X_?#in~I [wi/i@ zg&l0؁ 8X 0p=/ ( $:}/s$*z$3L&O0^yFUT)-t JKKqO1;D`m57b>~&=͍@2)ǦVj-Z ӒB eu)qL++kfH. K#|z?<Юsʬ?F~WO=$6RVOU]DljڤoNY S rei/m!em%rA?mYzW`RUnT}㗻d8dA 3Ky9/X$Р XRʝ9岁w^ h@ 1SXCMX|GjPW"* ҁ `wqauDTzP˔{4L,M'ԩd>uTӲ(5;Au6醮KzdNJӾnjgUhgH:`tP+ TgХs:?S\AQHT:bh-~CԶ[@ Mm!8i "`s IN612WPT=0!@ќ)S\9CXuဉkd~c'"^*ы:kG53w9M%2QyFh"#[3}-:#J̥sbVħaeEL!DwS՞=aߥ L9oG. va-*0"WT)':?Wb危ˇ!9@ 0/(tK$Je2PȲ,aITkЕcmmU?'yt,:EkYuYW1zYYAKX"Ě~?ZXD &6T*Y%.X][1f+1W)E+AVsT2DccO8Of `/{q:`r'G>5lΣǏqj#F;Kܶ븘@ CtptlXj&++:vloTֻST5^t &7Al@)p ,BP,cP@c4+|vu~~},uWO&!x0*;4ݾF^ ųs72C_p>sjfJr.@Y Z3Lͧq` bÒ0F IVNlK"C9djׇϩd9:קF^@4_0F!pk{LKxdnos޴fB6:>fs<:&r 2òtkt~fP}XUTm6>i@ܺ=K!ލ{ h:LΞ2Mst]b9O `[ø2xۅEG?mxz-j@ @43\}0?>AoΨy{n(.\YO#OGĺz}Lq^ j:Vng:@;9X0mf=dc:q YCl,Ҏe@BcbZ`-i֬#TAGdryA MLa#@(H+]+j koVC[P/sM{W4k`K{n߫.~cʷp `@<*/Ƙ36s ı_$gR9K7d+icI ţ@ @p($qJ VBgnclؗz80{Q>eW.ƁkL8|GAu2?Z tS"&VmՀA \ aN`$[͢VgP8?99488r&ͺ\+l^;32W+c6$9>js s :}+򈆦i߼`y,n_cf.[)<NƳq_Tn'OyaȳG͍<V@  }zLw+((8{wO;k[;7~{]9 X}djag߬zrRo,,,G U9940΀wA e aBZ8) g՗buw_4ЃeCK[[y\uvŭc4DC/N{L2GĥwoJ>}`¢'Doĭ(ϚqMpgj@4JR rrr8]ѠV=2YFzq;E  @w%\.TT0;_.芰B֚2ofl)%Ld?tHJi[Mk8h9"U{?DA ׆\@ Q? 41y;2Ж : =+^m;c'**n?}Ks0R,33Z!`qoX mks)8m]e#" S<W`4׶͉)r\e!mZnmݭ#gMmTects`Odz8›񑫢[٪I*)e^yJDf@[=G`D{YV`0Z"n#Tf_\W<+@%= mV;oA7e;*6|{I GZmwlۃwN6dKS.Pbj9ݵ=FiۜDe3O:\\20+$q!֕L&AvAv(Wb~ڍsqȻ|FaBkqqY꥿:Nw}c'}5dtߊuknIٕ0RԼgvS'cv9#M6%z@[Q`Ried"CA3ꢢ̢‚*0j(8N`j $PB~Ze%"JA IHo$.[!=u!5ؽeP}o˻Nj%`߇KݽsמlSǷtα-';Ll ^;041KS+U$J|+ Bh@=pQJnIUX`ȂXqJ=.=WO;z jm}]"aF^PEu]\^qk?1J*s?JkWڸscgGl(p~! Y+_1Q@Iu_sY@A&ar0ix3"Ji& NؗpVZZҍ{!8xm{=o3nnuR/t>W^2)ǦVj-Z ӒB }I",Wvx$)[yۛ*,, pd֭UbbFԶV.Jb1PuiA߮ex@QT@  Ů^^1aMӶ[&|رQ~5N6pۺbuQFCQ$E/-BBa׵{Y5:/UDb!J~P*0*$s'd8]{yT/vuum䝝Sf7z{">~~1 !zșOMͩ>KzZYǶm>u!U_NL , j(R#qx2jU)eV7YzN ˼?fF!lq w Ltu6醮KC?x a)\ӥ>S2:pAdiYCiMI)RzWq­BqcJ ICgb%ڼ* }{O'3y+( WG, v6!n@%h:Ky\`\ ;31ɵ6y(5 L)Bb\ L\'$;~9RI^eɟ^?򭙹si*ɏˆ3bFtyAw5fE|JX\vǀ- ߙGоmNۑ_%9Oitnb";ũ/?aߥ <%hۑK-ℂ]X@a(.gvȕfDaCΏ蕘/pyc!nHN a6ze Ͳhc vB2`fÝC+6f9u﮵W%_i $>GX~_*-O_[+єdef O5>^7KcA ש.X͸d5lΣǏqj#F;Kܶ븘@ 3ӏKo}9~Cg0W9cN˖?sCGAܦoh[k!]_ktg[nirfdRI>ah|FST5^t &7Al@w&pQU?w}}GEQWRZfVjjjZfj i3 \ff`@t>o  $(EjpGCK2JzwD="o3aޫ'8,;@YĘA .ɟO I Z~ @NGGG9w>4$ǫh<- 3dr9p "XDrXEg-i@88ёcWx~ـ r̽E8k@ ģ( @ޤ_/K_37!M hɾ[PxsmFs-#3UTm\h4\P>-69^!N{'.ڞ(zvg9@ (H#Iߑ~vn*Y'"]M8Rirr޽{+ @ !E!Vg0ixK'#<=$R^x'K"""||| |}5رƛ9YӾ{.f<7Ƣsf^:@ BLn  _KrETn6+.y*W/qBWqH .f<7Ƣs[5YM[R=z"󲯻7Wie4뾶Ur'|uK@XXӧGl -8HLˆTG Ɖ#0"iA a x`U>r HBS:wչVS|(r8G޾># n`2N]?"jRmqy Cm.v b ??_ f'Z2QE02N-]:4 b`Vk2'R1 `!FrZM/A @ 7vb =MmYW+`$ ^l)ǹU7Ç0HvpF~'`t '˯,+Q3QN-@ P((,LK*dr9)qaXm^)Fho3?rFdPH -p6%#pms0W!Œ Vm=t\rpe_]͵lGms>(z 7^>R<:Ϯڹvo=Ztn̴=)on k{Е\#7>ݑT}k҅ak{USL5Ӝ 9W@~vC¢K$$ to~?~zO@Lh-3Ӕ vyQFC9)栢0X0eP :"X50E.%p2!|B}58owU=}cO2բ WsZfXj7aDcO4ٜ3gnyRP7vHx]('l2\]?këLbZ;5Ѝ{r;w@Bnx@ ~wsOաS'JL4iXqB* Z2t#q q~Rt< R\REcz&FP7ҩl bň_d/Y^.BM ?kQH]9 ž $ҴB6:ʟ5jلA^O#+ ZimzX0 YۿWi$ߒJyڴuqBrrO|9/@Mг@‚\L3+؏[Fvݰ-T  .DCbݫ e1n kli---dp |N7,Xl&Ȕƪ*Ffҽd\xiƷJ?`)_碶Z 7DQ9Ne*XrA2ݵ}g?tuQ4zʠ%I-:t5ネ#Po/ ]pC ?EJp3RWzPEMY3NJ,cά|g+󗌉f@ XQJ7lOrحC_Zy|l@_+3.W{^*Ϝ{n,ڬiSP-2'vίD\{,Xz_լ+0JA%ջfݴkd}t|ׇzzq+a+_CË'.cIJ9]+-l#'`#yΰgs7fC>iFeO`zBR1kl xQyIcZ/>1EfSZ$gvټϟԶ&))KҜpρ=/>XujoͥuڲW׽G+9jdҼS^~vn,@ z!>G{ |!lJB*5B JӀDAAJ)`6<'Kc4ɩ1sb@HQWvP (AHF/ PW=IDMT&h9v{ـ=jKǐ_nB$&O^m)*ݏĥ0}Z>v[66 Ƀhp0a`V%U?)=h)X*F&.W.W~ˏ!SºuO]c^K \LY$C֡3T*qIP(t:n@ !4X..{?*H>PHqpܸ28֍ѭCBf^eƷMSKF?@'9 p~n: ]=~${s1 [L0fWѴ O%TdIqa@דw5kj·+` EB}d13->9FDrq52gL=yyY *5Xo"~`z篷nmdsnwxUFUZUb+ē)oXdx('.F 8'Yʤ*r&aͥ2YPh[8=h4>-›ۧ[DOŖ@xt hհ^7QfB]x;eNBkkM>̟$>2@s(ڿ2)8DoW/4'󒢅bāѾl5-} U TUNGNXQEXlj }&y`bg/uS ~\4yֲ涷j"8)T0F jEUd{U(b@aX zi7k j2`J Os>IZٳ^ose6:qt :CVVVZZ:3ؔymxdˋĀ7ww!S,[6.A5EۣcuFP [U @- .0 'N9r{%,3-+^JnBW>g&< D@;mu|Ȕ1xyZΨUzJ N.B4bqZ?<;fb\ 90G?F?Y}CQŞ6ZwlעOO8j1=uѩ$x)6ԦRͶS:14vJX( idXvySnH!~4 @ w!J/.(0m! vg4ۡ̍H$ʍ),ȳ..n޼Idxxsnʉ~ <9.߿2+Z9p1`ó ]uKyPSt1-.`D4Cy;$*0&qzQ[yط:-=bzNÃ{2d,D1lLԨ Aţ>`¯'DvD="n1pdݜOg/2q#:>ǴuPeV-ޛ ^)aF&E: o¿sf&K7q+NY0 ~)k(+)b<3w#F,y8Ҝa.!O]pzB .0̶;+J2ӂBCE"1}{MlTVl?dU48nͨ3mHOlg{?޽{wӧTZziii]6U/^7eFu1YX qd"(\1 ̀M%*P9YLbf 0fOM[Դw{w1`)ۻWsL̵ά#A"u}۞ETz47J;gbPǽSMzldٛ WgQ2HMH= {fTmܪzB T]_aaᩣEFo?xr'$& z}A~nnqC.Wzz[QB:x`ii)qU 3®f̻6Ɇxta"PAsŸH-5hhcI@IUGm5{ugǽzjcx@ q(_\ rsspo~4M+vp,۶c^^gΝh4{nٲefT&M 7222|tҮje<!a V\.\SkY"K`4%mG0:r/|Z"ܹ4 @ yRM\̼7 ph 5FkT$3*aXd Kw_`މ':mN=|usQO@X+8ߺo~~Nt0Ir_MC *P: @ *L3~_-`ǺorfQO=4Ʋ]yz=xflX` 1Ρ.9*W/qBWqH .f<7Ƣs[5YM[R=z"󲯻7Wie4뾶&EaMӄHT&w(:.HB̸1;&gۇoiDZ@`F<0NO Pp/#Wȕ.tn" MUZME^{/yt8Qt5vDKU5( L:Af"#t+ Ȱ̫Wa.$AXg}v{GFf@C7lɬ9XsKZ#)8!U 0c,G@ F]:FQTAD,2ʧ,cu5˜N3.;#cm+`$ ^l)ǹU7PȯD3N8re%p8FQ @ (&Ea1 RY\Tq?hÎoBaW>ZUʧJv0^0jKfDhQBXgXB H(0`:xR,[̟`EPu %c<l@ ½T(Xˉ KV1 ;GV8nѢWP\8xPz*.3&W1G ~lGL8ء`:8pYb8q`yRYW`,C{/瓡#g~ߠ'|bڋvͦ:0fIxXʳy#?m7!LRԇ ~~ه?Sc,#\Z-ǟ[>{y+Hq%Wmv8Z~9@ D=+ $`jWMo| #.9&/\(8NvLJr@sh0K?NfgIjB_3ӣ7[WC`D0F,8'$ЌE(xH@ B|0Hw0zfqH"tm5A˱ۿ5T]:x?_Rt*'1yoM5?{H\>Nh_\YD#nnc& ̣Ԫ?k XV ߻羷0t}}q@GxPBQidR-r2P^aH.s1UG!a%H .&V,CXs!x$:e 09m㬦JBƍ1FCCaj=3p>~=Z!ܻX&rSgپoOHzu&~1Nr1  `4 >v?*8wo"*VAf*`'5!ijVVz?C)$~դ0 JɻÚ5sw ?{Ļq*̵ 5 !̴cdrXS8H2?O ys~[f*.VTQ'_o7baܜlVnW'_Ea6_C[$}'-S@=o< SZ|珍GͻvK Sg('}N՜is ``0ubʵMvT:;=?*bKX yk  e@@lGJ4ad.4 pU6(NՙٔymxdˋĀ7Nx.qe Xx_u"HGꌠ)c;Z]*vaNp/-;rJK۳Yo+>[ȟFn?q%7GoL\di[D@;mu|F?;_8F< ;8Byw6* \ oWmCQWZbSŞ6p$En1q@;czSI Rqm~M?>m'tb.i!P^5t+i|Ȱze@ DxYz ef2H.DݛlAl@0PQv\]IFoPf](rK:;'Wgښ{//sPK0 6._gpvյk75EhFLp@30C¡!Hp1ӫ`cǾiɬ.1W4֛wZ)ba qİ1QR:(ةOGq#[~|;8{_wԱ5$Ihw@#6:pbr{Z> @ GDz[α$pIL̎m"k#W-4Ҕ* `uy\iOuniFH!C伕,y{8W%b-8 Zu~e"~5+FM\+BFȕյeL/dFcaFrڷ&X. t$%I 45MzX/cnr]u04A8C-Q䵷'@#wΧꋮ]wq*2eAHW,eqPbFdʚ_ $0- r  J0eϒ~x'RI;s(~ @0?Ҵǃx7(ۣF^ 6rנA|(rػT[\u.Ea5Lnrs= 3IzHv?RR>jS^999d sv^^ I8a]"r~~V|^{ \{y)t# ø3kQ\wչ)rgC9y{]u\bDޥǫ†B.믿 @Qqpn=szeeL*?yEhT\TTk6,8Nݺ3_8#=LG Z`MwhW耏L[ʖ\ٳm\gFZq%֚coae]^ۻYU5-:vgqݟ3mt,xʛ;,:*oߚtayWuU1Gf[h+j7sCP䵷ȫt7k__(rػT[\u^E!@Qxzz֏Ns@X֯sO<)"Ϡ K  v` C)L&& 27ˉ7?)eA$0aYإZl=nΊ*p߳_͝ŔXEpu ~35ʋjԔB7~үn$0fmH[?zܟ9~F^'EZkMFAG'v.yQUorLH=P}ƭT#PEQnq"w꣨}F}tfL& {ɓK4ncR*Ca.ủi?Rch$:@0q q Мpn4 3 VdS&DUiPKt!XcgX]?fON $MHח_y`ocrΜ=fI ;=$]VF@{yA󟷍 vUר!Cdv\ny]W EQ{{[EoF}V<a*bNow{dRRH%xC;`LJLJKK \\$5MΖ _R݃,?ƁlibճlX9wX)p=_ն4)=ˆ#k!eN:R!_~?,\,pX1o2/%{(aidD˜Q[VUw ekz?9*&sTD@'컪СBu^"6lN7׿>C=뿝V$TȊ|.Cg)K~vI ~VEgN|>3KyΞDE 6r43fAx#wվVL&J%n3Qw9P 0|;w~:zp`*7XjnAi3χnڵO.*>}WL .<`1'TbTi)+G8kwXaY_6c³?;Ml(UԒ3+V5KO:zYц;uNJZ0BH!)^H lgM{ۺn^l6աy$gvټϟԶ&))KҜpρ=/>XujoͥuڲW]eNJN,p4orNgC}G9! BhxWzl=p P<8'ڶmpXm4dYut\rS$O&*z٫arvWL}B4Md$D_Ϳ\7Z5UqU>K<}%/BUU.S=>L!F dmKу=h)ZP~h$?R=l"#< (P(42uv[~Pֽt{ M?woS,KÇ"%X cICgU$<nEEQnq"wRo VQmwD `- w JQ _Zz$ FGW+RO$d2Sk)׉U \"'x">|M.al<+ݮ\/%U OjApg:O#{M}dV>cdrXaB>*\ R#x;wBCCUo{\ɖP4C$t.->}f7^}n_(*L._tQZ/)Hl]?I Ϟ(y,9\LtI`ܼ޷XS&WKIW"$ցJKSq-CTXɿs2,29V@z~҇] ?\0G?F?Y}C^};8Byw6~ř#+(Y MZ[7Q8:/v3v*qB'N|5H#{2Эϛ #o'Q䵷Gy̮s`77]o3ŰA))7 AEDd^ s! :;]#7XhSwtd(yTF6/N=WL h~`"2(yjp7~)kMXijԷ.za363 1){o%/!o$12ih;6ͳs?1GߊaMA.U'Lyl䧿y捧AwRC@i⽙jސ2Me-l=i EF :>DŽVw"]$;jFhXw^tʂiJ*dE̠g<3nĈ%qV#0_32䉱^hV?&>ۣEGQ{KUG! rW.h(ljd]\?1b  ͺeN)*!W-3:vW`pC TY, l$c$E 70OiEEw);P o$\,p}M\w`" 9߾4.Q-?>޳%L_giYTN%GqTL긷,FEzldٛ{vl(+ӊԄԃg&9Joq7[s (jo"wsC}m7]oB@hVL҅("K1~ ~=&\XEXIZrI"RLr2,m,g\gh4zzy\T 6u޽"#iksFe?H_kVΡi`0@0ѣЉ AdkPP0FDVK8G ,{e)xs48Hѝ?xbov~oӗIА0gՁc,;=<;  o'JF^AEj? 6rנA#-.WJQ8 : (k[c8$ab0 p LbR44cY= +5 s;z&M! E"LAzTX7Xt:Ljuq~gXؒ d$.P\sVk|wXcB.NYu8ёcWx~ـ ^&Z"{(r8G;Q޾x#wվQQ+r,ky:$p!c|3 48qB&"$Pv`lci+`߂PKbDBD  B&$x2++KReHZjSWf^ \a[XȿUcQs6S9";qDG)Dгw>[1w$(r8o5s`f}xbi!"HH+Ђ#98)ld#.G@%ernQׁ9;"H BG0IۣEGQ{KUGQ͞٦k? $bR*cM~W_DXlF1]g\KH!!xQߜ Prx r"zd`偁f?w\ꅮfQAcY].ynE<kZC( uk9Rkym/r'.lU.}1`bmq5Rp1·:ϑ㤂$"\%$81aZ5M3nKmF a2o~@5eJtReqD"+^x@Gaߵڂ6\ ]͢!=4Ʋ]%neW7@@]㍼;\fjwWGa-V= Ap".bJ,8q"ģX9̕ڜ #R> 49;AN@TV>IizĉTXOct}@Y~r$Hy2^a8#"1~F/C@ S`MTAr*9oӟ={7V۷YFrϟ=7EiP> {zLڻT[\u^bTGaZϵ=0🈿Ν;ϟ'M  apwF[[+G ~svA E|jPW_~P9T劂(`;\P̈ ΦP`NTgHdo1 zJ@zlb:.92b jeKN8jsu f̽ݯBu4wDMy"u]s8{߈{ܘi{e)Saxn]5ySIu۷&]v^W5+9W] ͩs]Py;qx#Ty-D^jѩDKU3یl `U6Z <'K6<. Q5v}f0_9^[l0Qz( 3EF儧",s? }h)) D;@Ͳj`2]NK0%дqjKqΟvgٗٵvfz1Ƭi>}G9gH֫h1LnZ0u)W?ؙ?vj{?'v.yQUorW^(T[FPTcVll4ȁ+AEx˼6Wlt9h0jRmqy=+mVCxY¹RpLGLY? 143žug_egCs(..7nR!PKٕQ8.nJ7:-pN'EǓ.8P.8REc ;2R_4L;k5jلA^O1HQщ4ʣSHQѩD}=( Wi"\ >S< 0A: vsTʖeG6c` ^dTw4;aСCway楗^2$@a,JQ'(@SF,  㖑]7lv@Qay\.00Zߋ.DCbݫ e1n kli---dp ,,G6edWcUj.oJqqVe/~V'+j€.b*^(sQٔeUDbyƧ.0fWn-#`,҃eBbqh\U擖b~:U\REMT} )|EO9q'V< ܜ9rgZ6N疏:o_\fBTTG8iYqv̘0A61,c@؏e~*uh r/m6[?x=6O.t(x-=;RX)~Sg}_ΐ[o?E I#DLev/x[?{6a:iL+)<&|bڋvͦ:0fIxXʳy#?m7!LRR9႟'{_'}>~KߚK]esg<;ʜĕ^Y2~hi)o/? wQE+1M4^&GvqY0|EoOFRYSa_pjt>;(ye^y* ܾ̫-_%UkU8''rw9we5-ϩz^'ڦ|,] ?xTQV:&/c&Q.~.E?gv?E3/ίd^4#D?,Z@ B|0Hw0zfqH"tm5A˱F4T]:x?_Rt*'1yoM|iGRqF/Û`u -lao_)^8 D`pPYjUշ{xvSSZKMoIO T( Lj]]54B.uo#]ޟBT,JÇ"%X cICgU$<nVr@) ?mZ:XUE;綗9r&&&:@U@oreq(jOF. 4 S巇TeXyZuX8Q}kM$r';U۝\؛TX?Cq8?ݜ0SsOg./6,3+((<ᨧI <Ik׮RD"1r`BϢDQ~1Nr1  cѵǏTnsơ c/NjZ+B)+%L_լ4StIn)&ٵ*4i;. Rzf͜!0o"cgS˪.VT{R'_o7baܜlVnW'_E1Ri5E}[[<2/kOEeV(paӓOz>d0WwӄTQ~Yzx1#W֮gm9RǏ7:59M.t~xޙo>YLa w|uZ C3xPIP c(0BB~>ΰ;ږճ{1Kau,kdSʲVmtZjz6'KJJJMQT> ުF~' ~*“KH@@܏%0C~, e4iO淧^^la$q0hvʴǦLX]݇P#g.Xm??Bd@oX{*x>*X֕ SM l><0`'Y8ŐEg-kn{+iM* "H1BV$y^۟5 WX?׾3\km}& #ά9s1e{?8FH/ˌjj Q>빪]/Lﻫt9#*Xݖ9H6 0{X &Æ׮Vt11=!h3rL)\F0 "$D\. 1c."( AVņ 2 lyp.n7q言ĥXk"HG~1@- .0 'N9r{%, OJ#{ן#&Y4#C !Zs^'ԙ~g AQK?'S4oU2Qo3 MZ[ ֌ѩ$x)6ԦAͶS:14vJX( idXvyS[ad[#G\=U8!~/TtB <{jx+Mx揻$%Lj)t?=NվKg=k|1?\?Vb@9WLŅy`g;|4PRgJ$-#n\WMBbD\/.P#vөA0j /ݽlJ)RG[eFA(DFAd^Z2dZnJO.^h{/oYJl.ŲTSz=&|We~*nr.Yr~rȟ|^צ0PgKŲb`]\SkMC5 aHFWu.CA2L%F%((?hNmSOE9kBRrW̢| [P ^ X`APr՚;'T) ahpʔ񞾔ǃx\'16帆EwkG"v㾼=}pIG f_qay.޴L^ ?}ް*0HUésPidjۥeisX}W0?KYϸqC8O ljuI=gĮ /C! $ d# wkzLY,wGĻ.AjfjE,#?{zZDv?N=g}Mί{g{o͍Kήss+ Q爨/ub?Ж=u vj5\N __pdtn};e-tҮ%/Udf]iw}\!b|7侦6꾰Lllg4#0ikҷ{Cz9pe8'.a;x۟9ϸ{lʒ dʘ_l* 5`UNTRZy*.ԪrN:׳Ϗ?Bsט[VhoK/8R(8r)/Ub5Yg;HeY@BM2gMyrpN pNqmg*D;,N 2}NL*&xL['p8PN2m̠mÑٌ~9<,IW_A. v<G_p-Fs<9DMݒDD % ᪌b f6iUIC[VCcw`rDР緿g  W`nQEM6 :/>}:7<|pnиKYm;5HS|.%;Bo|0Q:ꯑ)_#E4 )F}潨F on_Tvuh^kk y){,:53m<1Ӻ{xEhA:'cՉD0jœ?5utsn[6{,4<03Q&J3o]Q0H^˝e-7Qnn0e>.泵}H$YN|#מGT"n`J qk?>s^s:)?DΚN%w]8Y@ES6hH#1˺Kť;b;eOlr"ynt.·UìEF%-3Uf$r}HIfWI%Pnh9$EaK<ϭ0lџ(V~BʾF/>qUU1" v&ȝG]pt-hG}5m"iϐ=I$1,л7m)"+QޔGq Q@j9CQPNRI0hX/ b $A¬>aSrD~=L F PO\YA?dÖR, (p@4 =ZBRRRi,6ێ< 6~.zFΟ/r^_qAsk8@@` z* P' Οu\:^ڼg|@CG?i+/Z(I]Z;jF _]DeQ\K_@z5ߥL(Sf06?ZC)!П6:=!FIC H(,.T]Zŕc_؍$55Umԩ7,x\񟇋$G6H`@ՈE"W9E l[Tս:vh Ԍ#߈0z"~Q!uĴO`])Gߛvc,D?hS ~㉠a6!O8x9m(̿wxCg'SA>6V|+p6[_y%\Ԡأ<|df@\/:R/ o}/J[ |cad` ΏG)C[)%f5dQGԌ)b:y$ĉ־ ’$IYoCA H@Z_3SuyqL* ~w\d_;'Ҁ2Q8C%ݨAآO 0r#q3#F-EƗu@] };NBR|BIi'U$A@\!WZHwIJ JV(Ցi/;56mUOY5U5bSJ[`"|l2zvѠdUY_ -98iקwL=( ;z1une'1GA@IӆFzr ?N謹0Ҋm<ן[sdIymy@ o cs%~P*T|+Gk._-<;b}[29h@<_xPSeLEDA+IF M h"U8_  ʯI|שSV'[O>bX;|bW@L't-Ca񱓚0vͩ#J*$B O'G`56d8nɹ8p Bq p%OVS=T": Rj/\܇A_RO0] YY-ZoΞ@uzbddТlOꏙlt7zX=eB*,GT$~!rtkEU.~\ߡ9e!t{yPĉ2y!ً_wt'-t)h=#wr&NI!̓;h!E F/cz|Ӹ6yQ@ya#Cr&; ~}aqҁyRI'J10nY03nvyr6~c9oġ=\JlPq؇A$h㓿/[ YN#5㯄=íi]E~ܺȫM^OugC[@j(lCQ4 )1Us'z2+,8&؆ mY7jc7wM?rG$wcٔE (E8Hܢ@.*e5^XZ~d*%P!l N2ӗx0t^kKAZϟmSkXtv/"i7wz aQh w^ֻ>NG:;Gr <}j[16ésPidjۥeisCݛ}W0?KC3a3nw7}@`.m:xcsGq>=~]fRsص"e(\$zD!Ұ1nMp#3##w \r~<שE_o's@C}&}~lah:77*u<~eZސG`]6srҧ\;a!)Yގ;Qѝqf]iw}\!b|7侦6 o^V&Z|ts/~pdt}1 R2ǖ=u vj5BW[+#%vTb'B/[c]"\.W(d "n?^{9c7/SRyrc+= 0'pۦ,gX@^7?zpD% gѡ<?5Y5% $: R(`Wed 0]wIME<0ܲD#+wү' /W`nQEM6 :/>}:7,V@'>4y[ޠmvOdP#qG荘Z:JFk$1YqW8IR7E5RxsR۬CZw]S\suHgIǨMOyl58v߫/vd5U6hH#GdD=yY:>kW[ preSPmq9mFGk.l7s~בϣbfw_VQ4k矇x j]VBo2.)&v]j#OQ\mƶ61k6Ԫ@7< AXPVlz|W$qy\ht&g"<<.%T(F72T yUy6ۥ&f]qI[ͳDK%gQJsOgDGpŤ #9~80!}@ Ď-3,6W㳎]*@ Hu WIIu)IZ[kUdlph> m^l}Xuk(ĴcI@!1BGU/$b%'H\ACm{Tz9e{9ٟlw Ŭr˦Z:ԋUM-hy[N5_2+TyJ9m޺uibM-lLg, P@ X@rm ܉hIy Y($5(;Kľ8jM͟X@j CKav۫ǭCU>R/Vb0A -Xn]^% ;qsu!$ v:5 |R < P%(> -LZ 4s_*8t @> ܨScLf#+#DLix'Ȼp薰uh1sBː:]*H.B%C?>iX ;r:-"wtx?ƍvF֬BQ`I̶ [av۫*,6w}k,/bU[+V 3rUZUnQϫ i6Y-,j`1 =~/-)Pr'(T@*xJ 7{'5D@Aod'b$R䄡ʣEĝ=Gx̩_4 ch=67 XBLHz>-jB]^?%n{6cq#'{V'FL?3V9q2"ډ6.ިwwP?Q%pDG+x9)^4UM5v[KX/KfsPm,Wޮbrs]aF?.qS'M_P:om9T-]4(_ PzPt8 $p1 ^SU n C/mAQ,3:A,E ?Ve k^-||fk~Y8UmV-˗@7A<@z#oD#7oo?"賬i ]I{Hbaא޽wmKA T'I}nGQ~[/+/-gaU8wT*yOk4OO>WWcFkhB~mPwDߑ9ۗ d?]Mj]f`F PO\YA?dÖLd(p@4 #j6kZ&A=-6yM^˥\ח6|?d5xP윁-Q*8dĨ o?k# nqc]J/ϐ +RY_Z;n_LM9/L8xὡn&Obޓm=eua6WA>,NO6M9g=%vHZB}o_z|`lQDub\˔lAR% >Fo7]}e*P/`(lm [;F$̤$g]]1XbkICw69}aQFa}=ڸ>z2ʖ<קkbbnB[nxj%e(nIt[Xm1;.VkdRnYy,٥j;S`r+(f)-:m[Ӓ"ǔ;ARe ?ss 챶 gB~c׼M|@CG?i+/*".X#^F˯:η y֏-ZVj]υ~["@ZJ302췬dm܊rkwQ6uEa&(ɞRؘsᖁ̉\eO2DQ[,RK)<%c1QCeM4PQۮ5o9NNfZvFRQ)$?bamH%z" $=+,Ф_ܼj뱧|_~Ŗ'fCT9'z|)426 R]$0wj"sJ"HG X6-g^ 'P3~#v F\ЋE>A;tP|w:o ۍA=L*P<'ֆ9UƖ?˫t[Y|+0nzӏL`IsOeAGxgm3̀h?>^t^hyu n_8>x1_Dz02#”^VPQcetBj,'N a=Jyr%w`.99vYn=V*A QH킖8) m dDქ0?p?(:Cխ7huNzXQ༕}a糖cWNuyq D@i@ X(I@ l!v@4[X8ۑ|=`T0>>^U_ו6]LvﻳkXN2ѥ9̫OoPe4ĹRRTS+QC `RQB'@UA A;Qo 딼N)'[O>bX;|bW@L'tV/a񱓚0vͩ#lr 5ގ6 HͅI QlU>.qHc^4!`\Wrvt[mCAkX  &lƜ#^9Aܯ. ]sØ|1R$bۀ \ZmEG 6*[~_Oz(Yv)T?9=q,wA˫/hܙIlk4fe? kT&^! r}L>4~z'?KssPYTl=tL8$8>I_$êGi_d-ͪ86?L @  + r+1}S{D P7! rBJDbe6)UOu;䊅 @3w#B$'qf8 `k1v dTcz&rt0Ay]E 9"KWk8Ir3+V+-Ө:=1Gb2uhQ6KL6rJUMy=,뎞2g3(wm95~㢪u?Gy2_yĽ͖oT֥ o>\]y>1#568.e_x#3ǥ4BA$H/[ YN#5㯄7zz92$W)է՛1;uҳcL7ۺz*Y b/FEz[~L5)þ M>{O|kLj\IzwRHq=yHknز4M{^X,M""{>v\y9!߼ueh}-|U^%eu*.N􋷒Cp'6\-迏BzyecS{t|_.^-K>Q4 \Q2%pFB9xE7\ APbUBBekAUkr,R* P)S{R"pmD#Ƶ?ۦװ|(^Dnܗ<);Ì04^qJkJ#tZ~A͎ޒl}ͥ`j^v#M_5o\0w,]fE< MxwӚ=Ҧ7lOkr~>s?v{kn4]rtv]:GD|K,smN:׳Ϗ?Ϲk- e=TgD.hde̦yک[>9x9˥ENBQWܲsqߣWO>{gk.TAK4W ꁝ◄*Ez\"1u(hxn~$DI@*&rMZmno/ _y`e%84vGh V%lJT Wh0&o_rf O>}87h\Cf/VAN  *m5tɾFG#1tNFk$@ N,7 pL۠$gk:ryTAuw-;툞{^Qz7V}M!"t֌w/+qtelАF($I{aa3VNtE¦*\?u"p\LGOI22Ԇ$k۪!ֿ2i2,gE2QgM՛an^^OI-f$\X].F^&J,כa%K=j($Z /PIӥQO}潨F on_TvuhS\RYt1jDgxcu j.tk-]?hEu\ަ[K7"emBS2yoM-7ڮ,)3KoxA!&0]R\Sbރ ii..)8;k`Mە-{g[nCf1}E;}\VIf0b/jof$E)$8 ^'j0W OeP:kXI*c+tVN@ ;|C O ?)৫$|Rnc8X*ҡe+r"SY-jQ\ZUn.,qV>tOWL7MOO_v-ѣ0gm7@ !nf;BԄ:`?;l?T}r Ak왶ܕd%&Q料hBκvrFH}kC(Э?vD {\ۿJ^O]Uiy}T+_U\I/uKw25l'ߨ^2L(啐-*oWiWpnvs|;uo-͟꒑jҝ(Êue^&QEļ<\vj0/XOסjږm2mpTN}ZDy}@O<ՆQx@=aE9á ?(*DEayi~m1JtX5RExx]T(F72T yUy6ۥ&f]btj`!3wxRN߾R)kt}Ԉҗ6>rYf|Su At@{_.MZM=Y|aRn­ ,2-DwoEc,3}'<3ڌsC )2Iѓ03  B91%@cjOg]핷ثܺpnvv w/[/GLDʢtM-= IƆb-eL(!H_>@kNjwt2S,aaiZJlyI0a TF|B @ `f.E#n0%oC/ -xAۆV;+Gʳ^k]2*´L@ R$Iฺhg6bZQTk/eX$n߂@ @*F^u>R(/LKzѨY޼ϓR߽Z6U ^B;} @ 5 R/??/#M7'ǫ( ެrmerJ]2D TA\.I)u޴-xJJˤ$$,|p"(pys l c5<Ɓy>P^P}V7hp2HT! XDR~ӆ@ ˤ4 A*p\SaKq|.SiA  AJ27⥯O7mq*;/Z$̪Z {PGq5q~ʀ6 XL_⻦_T JWǵX&Z]Ox\.O*oWPi P PEhPnBnS{?z`0 o_x)7;G"q8Щ@ @=bzoRMy8 M*7<@QՌ[ ?&8},KOrVTJ #vFqؔh 0깟?T(JYf6Uݯ3h~,= )  R(Y|3]!Oohy5LjGCap4[ĺf`uB[Q&r@j/\\~T*S8AY,T5Pi܂jE&Ey@ (Q%ɔg 8b萠֭*0m bţ0=actͿ7/6 с_RmcƍR{{/N869jLFBD'[Mcb;"}`{#gF~?9Nksr%1>{ Hp]_~CʋJoZ̓ =jf` V nA^/&k 1 zFNJ$v[+/vdY5{۾p nnٞ1eWҜYg}}DӿWnՀ@!RԶ)^\|re5\1y?m鷭yWnzn꺩.yZQDE歸BR\M[{DHvqqqOWEkTGkU}Ao(i S(g$ܵk[HȠ go߽W=T99:EYC8A\tABinN`=TRͻT4(uBT.ԍ^lB$/%^qqTTdի_6jk< 7s栠y:R>xq$dC9[v\xh35)Ӓ~zxFm4A ci_HUQ U(-P@z ԓ\M? )<~poew{BTԪ<.׺=ѓSbed6A~>UΝB|eTh0K;na!\SGN~ǯ ўW~̺!>jzYE' JLȡ".)=@B۶!C]vg5vxlӿ6 In5X원w3sssFݲU˼76P\.'-z+ܼ7rKy#$NX^kfȳ0ռ:`ƽtQo.(_#U)5eז}=|dk j 4D2&_I>B( qXr`)Y?nݺg͚=w_ssuX,ռ= @ Wӭ? IJN^tyfVւ(ᾹT*ʣ5KSG%b(ʉQI뽞Ffg iuy' G:6@uVM9W~G2nn`,s rx_O:^m|swz_'ۂoHR<ݰ_V3zԬg}7ٙ9f9a VPTlVj:@JSOr@+}=ii _7lnѝ(olj=!K޴zb$< ]^'6^l]Z=j6+Cd{}i]Ibt=kN::(:aT mubz|zaعŋ bHR$$ ,PTt-6zu GQ(D, ECP,~e"Pc_Oo٪)S0Oz 3`IP4t&(-Y٢nrmhdF@ā,m ِiC@@Tڢe} Y: Hd+߾,Zc֯>{ZX8RYJOqdR( \`Ϸxc_+'>6;Q6PHOKDن$--.@ĸnGfsXl]SX]eCN@Jnv C_hڢ%æ^<ΎU f,ORv88upvi:Äz a 7o Cd 77 <.+T0',QJyyyo@,kpBaF@ M =z[Q*vsT+zH,X,ʝishB*d︺3c;gN1LZS= 챺ɔ $spp5 @QPQ);@IA~Wߴ|/ԳgO3bݍ_B׬jNaa![LՎ -UIB(Ul@ "X"p J&㊄".]#}n]Q∰F@ R3>q-o֪ЩSg_ @ˁg!S@ djaHnv֓|G>oonO@ @j4|>}n7ѽy9_$C& @ eY;uNh`Qb/iϒ5]!ja m@ Rڱ')7;@7{,y>Y@ TWR e,(h endstream endobj 261 0 obj << /D [259 0 R /XYZ 69.866 808.885 null] >> endobj 262 0 obj << /D [259 0 R /XYZ 239.758 541.84 null] >> endobj 46 0 obj << /D [259 0 R /XYZ 70.866 376.946 null] >> endobj 50 0 obj << /D [259 0 R /XYZ 70.866 299.571 null] >> endobj 263 0 obj << /D [259 0 R /XYZ 70.866 231.55 null] >> endobj 264 0 obj << /D [259 0 R /XYZ 70.866 191.992 null] >> endobj 265 0 obj << /D [259 0 R /XYZ 70.866 154.371 null] >> endobj 266 0 obj << /D [259 0 R /XYZ 70.866 106.732 null] >> endobj 258 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F28 196 0 R /F20 166 0 R /F40 231 0 R /F21 168 0 R >> /XObject << /Im12 251 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 272 0 obj << /Length 2876 /Filter /FlateDecode >> stream xڝYKܶﯘr4 7sSdّJ9qR!gק_ .5|" tmov7^͒ s8$YF*G_{~Dԏ_~*߿/}#o27w Rh3#npۨi&76 K7ODyބlЮ77Ռ3V+>𝡨6C;8[9RW%9 x2Nޖ=H^Bjʼp}x!D.76ʃ,Lxl>/qp.BF\2'/ƃ~@dT%z&[5G~ܫ847ƛq[Q-ku$␢CZĻ8/cN1;;fžv8Hs>4mHY ΢:6/0uiWYQp/5:}L= vΟ RR+:HQ}sUkhq͛cu+<<-]/OCEHG@0vTU\Uz)4q߷#5}KfQspGxfF%4g`B0*E3ar%aJFwxg\ Q{tЛ.صٯŏ( o^uE^Q6/e Cr-7DSe 3$dgLg?KwԷe닉筦W\ 5@(ܫn%rg`I?^wJq#iB'7 09bNMvF9+C׏}?rP픃Fy0K/Y|OMŠ+:uWʐ ;͚ٓi{8LڣdO~|܆IYd] &|.XbKlCmqվ [̦׿D!s>pz0>0swx&=L Z 3 ίqrr0D 4-p&eN+YNG-7s=/ZD kF(bHuDts')$ c--dVr"s7yPui0}G鋱й= xVV/>joG{xE&:M~@ME}Pv2ƌqBo`f.Dem=ywFY'wAkJ>Rĩf"3]v6 (CMP>B`*`Z(ahǓI%q~BСRCOp1%7NQ ȸ>sk{ HtBCX:a镕hR=r61U"D4[ɗC;4$oi|iKWy?IMl]L̬ PT?@i(H^Eb~΋Z,(^ 1dBwc6ˤTx W8%;7AMZYu(lR,3Gph0փ6IA_K˶)R4K 9mDgWPqƐ칁:mm`#|@,WH7(p |7e):,%m7J0,cZU© fdg?QYLgvS^QFscwl(|sځ 4E!(Q"6qf 3M%N|Z&8U8&#.y 1/EwH6k뼒[7&_o,\:#꧱]иyjS}`]R&/$d\Hk`r0j|2RuQw`j+AX*/3HH1Npq/j5$:d\&f? w&5CuӌBRQkܱ~|J YO⥰̱z/F뙒oyK/Iow70eh/ endstream endobj 271 0 obj << /Type /Page /Contents 272 0 R /Resources 270 0 R /MediaBox [0 0 595.276 841.89] /Parent 234 0 R /Annots [ 267 0 R 268 0 R 269 0 R ] >> endobj 267 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [412.974 507.097 427.696 517.89] /A << /S /GoTo /D (section.2.5) >> >> endobj 268 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [94.777 497.079 117.248 505.346] /A << /S /GoTo /D (subsection.2.5.1) >> >> endobj 269 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [149.536 323.231 172.007 335.187] /A << /S /GoTo /D (subsection.2.7.1) >> >> endobj 273 0 obj << /D [271 0 R /XYZ 69.866 808.885 null] >> endobj 274 0 obj << /D [271 0 R /XYZ 70.866 771.024 null] >> endobj 275 0 obj << /D [271 0 R /XYZ 70.866 709.318 null] >> endobj 276 0 obj << /D [271 0 R /XYZ 70.866 659.512 null] >> endobj 277 0 obj << /D [271 0 R /XYZ 70.866 621.661 null] >> endobj 278 0 obj << /D [271 0 R /XYZ 70.866 583.81 null] >> endobj 279 0 obj << /D [271 0 R /XYZ 70.866 202.732 null] >> endobj 280 0 obj << /D [271 0 R /XYZ 70.866 162.944 null] >> endobj 270 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F21 168 0 R /F40 231 0 R >> /ProcSet [ /PDF /Text ] >> endobj 286 0 obj << /Length 3089 /Filter /FlateDecode >> stream xڝYIwW"!Fc[lϳgKN9DSD,_ IQVN赺v-^?T4f=7EnMz͛˕wď/o>_-Wʹ/~ϛ_bH4# 1qś.p[ЍZw_9\&{Z[! 4?/ .G"G^UN-<ɛ~]q I(E$ٚ(T;YC {"R~d|qД/p8NƋn42ؘ{n֦1ڴx.z@^F~4!sg=l]4]VTa]`*o-tQ{iVug_:Yp\&GzYukቺz麢> 3֛K9ʡs)h#_`@GD6po"-xVv!^ Kрh;ހo*p #f] xGVz(:\g@ >h ģX)]e]QW`H%vw]+ROx-l^>Sv> xsLꐷ] v7W.}` ""zMuq1`2w{b}̽" 23h̄ifd 1 'U{d=gr ㆆ%ʢEC4F_{fqY} e*#+ZRM}dȦnfLjo)Vi)O .fr̜aXQ3I~iɠDOB'NRҬcxn £j0"?UrGlIyFq4+$vDGD\*g`jdEqlj'#?a(uj^:x*^~K7q+Ⱦ ,&P;_mx۲k—r/yÓa'!/<%^g`Z"7fc'X.h\h8NW+R+Q48,1?uW,(H9ecZV@p &/ΤsLׅa1wWk,(=,kNR$J==N0G Tzz&1ix0GdQ?XV=QNpQ:r4B+1&eD2Eoc֦(N 큲?jteݖ }S s'ŨwmDALӊʋg𧯴w u^>xu@6{aj0U bBe 7b=N|spiQw蹁́kcbnIl*p |/֦_44&P |B"T$Jaڝ HRhJ%la"ePk# Ǣq)Ik[DrpBNW)E/Z9$\Vq1h_ш2XOwu<Z2kr%`QD!n!!)7| 9"WBLrXMTfjltI,M@Im@ɇeYTT"2*E-BbkZX<çh]qBTư"5X:5c@UQ[3~\! #)-;N QiKDA>ia\0%FpEN+'U`)ŴSz詹lj4/M4 1Bk ņۙ$N*>H = mLuujhN$yLC쳶=2"Jb@:bMNΩ{fAaOѲ ƒ2NG-"oޙW) 'Ï"< \_G 9Χ_HX ӧWð[vng8̎i)LC&#" IOGH*Dc?v~Va%Si,5WS/bYIi3?gc7Rp i"kI endstream endobj 285 0 obj << /Type /Page /Contents 286 0 R /Resources 284 0 R /MediaBox [0 0 595.276 841.89] /Parent 291 0 R /Annots [ 281 0 R 282 0 R ] >> endobj 281 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [149.765 460.245 164.487 472.2] /A << /S /GoTo /D (section.2.4) >> >> endobj 282 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [94.777 171.827 117.248 180.093] /A << /S /GoTo /D (subsection.2.8.1) >> >> endobj 287 0 obj << /D [285 0 R /XYZ 69.866 808.885 null] >> endobj 288 0 obj << /D [285 0 R /XYZ 70.866 771.024 null] >> endobj 289 0 obj << /D [285 0 R /XYZ 70.866 655.063 null] >> endobj 290 0 obj << /D [285 0 R /XYZ 70.866 547.418 null] >> endobj 284 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F21 168 0 R /F40 231 0 R >> /ProcSet [ /PDF /Text ] >> endobj 301 0 obj << /Length 3258 /Filter /FlateDecode >> stream xڍYYs8~ЛZC9$kbev2@1E(<"{dUFנ_8rEh'/wȱ0\DIbnXg߮?_~^\*ܼ|\ _rn-p *DA~qŷ $: w4#]lv_v]8ċ-Ak;{9m׶jX˕) We[v*IKCoK/cյ0Ή}VZv=hBlm+d݊PuZ5{f_ -+T-cv|;]lY-J;]{~`MԴ3d,R}/b);]† k$o',&~Y~t6ۈ' zVL'# ]OӕCnO9'h#P^ )740HDq벼/bfMۗOL7:ʒ665E$5h+/{/{c_l2]W;7uu&(jM4xs<WMvHZ,O9_ٯoB>Eqg?Ty1V3\Rhf ZAAXBz@͍FFYȎc7@vc#rͽ4kڝāAs۫:c䇀Vi@%jhU h [y{pwܚt>E#ԷIb**pD@Rŝ%g%*\U8Ie&oy)rؚm/^>^rgUaU2627E7EGHʁ7Hy(;!5,M|sTeFL"  pǀ䔥Edu%IQ6jcoJ2Žnh(7 (2,$<y͋==Ī1ݴl]>)](*Y6 (GMEw6=N䇱g|Wec.FvTW?dqn}'qz#m0:8;&C֤S>]É'uҗ( e%o JB{I&?$AcRh1 o hp3cׂ{&LFre岰?CG):2tD@#«Aj+& GI]@$@qv|c`eE B>č< 5sv ;q|ƲOC;ΠMAxyVᰣ l$@6mOtw xg;IG~E!Z(2(J}Fv8 '1-!Եh,90rl/Vf޼Zv, 3ݞow;IOV8= r j 4-]RĀ'-v}Mn54:M~{po/ Ň;Q " i~ \JsLa]Wܛ}xC fCk+ L;qJ[YJBE.3gYoVSJ5=-guN<#7w tăaI %B)BP _pTʳ^`h z~1;>~ ;˴(𨨁RL6RϚeyiQaUfC,5>;he+g8, 0TGTVŎ@jJ CAo0uo ?2LoKp".`6wЏ=Cpx*%BM;wdSV5~Y4 IݕE-6bjRu_qh#f+ kR *_(Efm\͋BHz+   %Hq^0E\Wy2OI һ.@l}ݡX ]P>eGs 1W)4L=`C;Lьj)rC&bq]O |zSԛ(gAו0sŎ+ZV5Bekw7)A2u,FزlCy> endobj 283 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [239.538 745.619 262.01 757.574] /A << /S /GoTo /D (figure.2.11) >> >> endobj 292 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [496.293 636.037 518.764 647.992] /A << /S /GoTo /D (subsection.2.2.2) >> >> endobj 293 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [407.963 598.74 430.434 609.532] /A << /S /GoTo /D (subsection.2.6.1) >> >> endobj 294 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [348.956 377.59 371.427 389.545] /A << /S /GoTo /D (subsection.2.8.1) >> >> endobj 296 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [274.529 199.806 297 211.761] /A << /S /GoTo /D (section*.20) >> >> endobj 297 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [209.701 141.573 232.173 153.528] /A << /S /GoTo /D (subsection.2.5.2) >> >> endobj 302 0 obj << /D [300 0 R /XYZ 69.866 808.885 null] >> endobj 303 0 obj << /D [300 0 R /XYZ 70.866 661.497 null] >> endobj 304 0 obj << /D [300 0 R /XYZ 70.866 623.093 null] >> endobj 54 0 obj << /D [300 0 R /XYZ 70.866 585.795 null] >> endobj 58 0 obj << /D [300 0 R /XYZ 70.866 430.849 null] >> endobj 62 0 obj << /D [300 0 R /XYZ 70.866 281.513 null] >> endobj 66 0 obj << /D [300 0 R /XYZ 70.866 177.397 null] >> endobj 299 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F40 231 0 R /F21 168 0 R /F20 166 0 R >> /XObject << /Im6 221 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 312 0 obj << /Length 1445 /Filter /FlateDecode >> stream xڅWKs6WjB'Ii:ILnH[K̇UbD` F]?$0,eo8b1AL8Xuǟ_ߗ+U(rnѿg@4AR# "*%N8ȳ`\#bxX 1 (TX`_\>i->P.Z|[DSC`)&SK g$\0 *s2Uam`Aöeu7cBgp(Wq ݮ^$<MF]Mw뾣Ìjwi{[7ؕmWTN #쑽n[4*rRIcmI`'E*f{$8ׁD2+B*F:*܍vpNl1(͝KϵL2x)@iQ0](,I!puU޿5ODpP HRC,ӵ=)gnĭ垫C )m@_yJpm.qIY1V1 ĀMUFKm¬ʉ>F"٤6hUYDᗾ j?P?sncp;  qR-*Ƕx/ExJ&@T:г@!>BM i60a5R䴶~/ <ږ%XZ<8D8UǤ}[k nolh9nGaC5O_ ʉCVH*n}Ũ >!PCx͉R97L@$Pȩrq۰R־̝D*dJ@X˻ND)MYa Je:qk;KqmS9 :lvTnj'$?8*lÐ_e5:=Xvm*o_j" J_W4U#:|mNTxu[G@ykYVMYWRSі .F LMa[ ^.ؾ؅ kXN<षO1_b7-J"ݷϗ8< D 1T~/izAbwA$]x@#툶aw9@`#YqlR2w dS!,IjÃ춳cHeg )ͦh[oc_> endobj 295 0 obj << /Type /XObject /Subtype /Image /Width 522 /Height 158 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 315 0 R /Length 13707 /Filter /FlateDecode >> stream x흇[/"9 0VWW]* ",b0U׸~oOAN̠y~O?gOU>oULWaaa~~~.E/z^EE?I^U{݇޽}KA޿;͐4<{4m& '*+:ܿwуͷoAFܻ{Ґݻ{MͷnAFν;M޽4N[7 #nCbbb޾yC@A0uxuE@AhRwn5ݼNA9wn}RHAx`u:y7EyKAM"]u5$Apttd Wׯoݼ}Nvaҥ:Q%%1ao+s30vOzJ>8~LƽÍ/Ydif5'zܹ} Ro鬒fgm#<ljocNB%OA߳:t^Γ'i:q56 -̣XZᜓ*JĬ{aBW/7:D6*rQ B=<u3r53s}{ybשx!7TU;;-%#0D=tHIQ;M,o\F݃fo^Ac4߾AgdBP,l?\b}U+Yɓol[xѼ(A53dxF:](p0q颅S pػvJyu5c%ɱ# g>KM,_z򆺚?>A|pYzKdܱÕq "3iFMe9߬+(su=F!J$v][U,hU(-C j]KYxݺ8ʄ'm]cu G$4ߺWL^%[[a %0?W\R\'W/ø~߬';HƼA>uDMVbƖ,Dn@YHR ~ u:P^Mf quk|&Kc)=+ttFukByu2>jݝ R{+)%u P۷nܼ~U Ǐg*pS\5VcuEVն- u5`{V$缜lפkWD9XlU+]X{gψ)5j˘֮X_zr Ӧ,sjG g͘bْƆN1z䈍עAHiD^ݾy+R BwԤLKỐ:ps鑧fO7¼{|V(Zb[cFY驽lmN43q6;Ǽ !9!6 [#r#-9=>>3`mQA|Un^nu:ujРAbiICc0WU^ M!A?:0@Li0Bu4a<, 5cUBB uq]ϊբg`lp17füdӇ /:i ȹ}AA@A:AmTA:jU7U?vƵˍ.]ʶ WmlW.6\nr]n,lAK¶41X[}bmJP_]PmU0j+jAT֔P]ˋ*E-UP\P!ErFa>QW+-+U (sKssJU)-.UmVaNfaj,Pdd ev2+]MJ6-?3F^Fj^fj^F ܌4lӒrR lS) ٌdHq̤Xl3@B #=A|3Z?DP}SO>g DEZ".k]$=%/= R]MkJed Zu+1]u= #MrU]Vs8U<$P -CH#'-c))JJIKB6E6P巖DxUL(dE!IVWT٠ʜ Z[ݨJBvEc2:g!K#]7 yRɑYn:Pzqu꫗/'R"c!k"5p֍n6s@ݦ͌{]w}xG?zp?z<~y<x|‹_xx5W+^xo x;>o3 @-ы^D~|>-/G]R}UWՁ+yp&jTp  G~m:m`_x-'_UAA@AhVōkW;QAqsڕ/$u  KR B:Q̜F/tPAA&B'7^~sׯ^/oB|H?AA2/Υ/9>j̹yDI@Ƭ0_~@I O ֠ M̮8SQ^0m(Pt{0;·7X\TȽn#-W>nj9"9!^g]]k0apMs 'uo ; (x^)FPEiGSX!8t;}86a}·T gٙ=sz_Uyi\پ22UmgVc]mZs-mSۙUbgFF_t^dAj=6||pqϟy{XנK~\%1]KI*$F܅ bq7 -:uqi3P`k#ymcԕٯAc37;չAgǹS" FNVL62wfuukq皗 mٳgl-lt/G l|BӠ~Be47c}t)wƲejAJ[ uJP_]9u}Dy`sf$ĵ-EAHȱ֭EW3|kH^fe _}mZ[Y#|P/"O9zŲ6/Yi2{<׼\3+óuԡՆ$%ıo jkynܷYyK1~ m059i^ԒBiʘxyC}]|lLPx~@0ї/IQbj*s"ݻwWR/H )):w  (Vf2ݑ'f-_+VUWa3KIӠӣ[ׂ]/Y+s=uki,̪X.c)ƩyGGGT_lrqj|B0z۷o/56–Z{>?jj)_{^ڷ{١Ak <_h[ͽPÇ{wnLZ~ǭmugFmէ)wW=;#+BC0Jc~`$yCd<_b[4=%Iw۠ȱ=}xEi1ee̙iRY~:GRf`.Ϭs- @[H\kƪ_!ASIny9<.׻f}{y&|Ϝ<3a^Se<+JƍAAr VAA1+.^j# ,?mUOݫ% Ϟ>!u  H AI:=)r!=5UuR0ۼ~mYr_'HQeGVs"ZS+ԯC0#w*- ={%8҅ q˘.TVT8wI2d\*=9NdApyQAXfC $ Ïp 0r mQ{wk+ʪK #:rۯL;u>C^Scrw;},Z=gAANր} j Jwl 5[N|~Ӟ=)rܠ!4}T⊢)֮7'/3}gցH_$# cGw6 CH>kɂɉʼ܌-fϘ&ӂN;9j,6xвE s[֯&ӂ!;ٸ7J5c&0g ~DV^zRC5",x Ii97̜+|u`ϮY3L/6w4\-=P2lGVk ݺƜ9ӓ0.D<ri}{v Pky0XP*2uqDذ튀☱b{G;[{k!9ڠ(4`p~j,>K4 CP*#kގZuVHO=<}s~0*,_֗:K/m,ڛ'EN!rա)V;gzp?wFꀴrW _/سt>#͟x~t)ضu{h!L:$< pA܉c63Bem5T2.k'ú +Ā)`/:g12Bxw*B7n:V.Y?c;YK1\[u0uA~LtX 4p#hOZ@[锬yRy"mjUԡs#Y- --ፄ4id&HJEŔ{(6"=d&'@uYa{B'{5O ݩS'x1G븱:=}W(lU ;dNkהּ/0Rc(0i q ѵUǦֵN++nd<)r FeIaYaK lafY[K>) '˭xL8?FQBWLby̜y >%\FX! [ܳ O2^8}F)>(lŅ=.cwď.tWAxz{ZAx0퇱bɢ)'*XǦ5D3~(rƾ wmoevŵ7O"oC䐃M>Cq!Rz9vFnFAH *o/OLU+VY)00ݕU#'=Id&'@u4 :c*L-mM[ݻ8ߵC|蠁ӧL2hNOh};#i,6l漬 ^x16 Ohg+S0,7]yڷUؽ>yv;EDD7}ӓ" rpv:ܾUY\PYAff. e(*-͘bV C.੶w`LN(Sd$'̚  XJe/ 3r+$9x2;#o_'  fHK^8o5:Gop>K>m3EwEt(3jhՂD'lϞ9EnP䐃;o2uLT*`9zi%rΧFQ>js8wK,@D\ ƎشSG̙mVD3lKnqgOBLw #lc'ZG 'ټt̟vFFOm]f{fJI']GZY:;uڷg@Kά]m1Uع C;=]cϜELjCM:(iwϠ}ٙ?!!.`Z-A';:ܬ(Rb>Q\#m2Oo% PfR￳EGw=/} PY>;kfҩCȆu@`RP^X|F:(//! ,H uutzYA^q 0r M7:AjpÍeʼ\Aa@:ܾqm$Aa@n_F@AíW[, ȁ֢ܺœ, )õ%y9, ȁܼzU) A#V׮) AS,uz87 ;CگitVxxxt5{׉5'X{׀f=]/ӂг +%oOiáFo{:?Q={RA*Q~#-7gPeVpܨf& J>}==$]8eݚS'΅Ac*R֯Z˭L {~~ݰz%=WLĊ`:H>=)rȡ7leV6c;9bzK=1)a[6]fL,v`G=z[[ *ka9;%Iz܌4+3m6bAԑìYkygWX% ` {tuB9lٌz8vԪ”C`7O"oC䐃RdXm 0l( dxӤ9:%iՉ(Z`oo9EކWO?N^dށIs];4i85iNy܇-LPB,X!'_w[fwSчm⃢ V\S2>}GHLgy 'ƒ ~K!>>Ժ-%RTwL 20apkt }Ɣ߰Ֆ!"`gLnՁӰnbeV`f–Ob0 }5Iz ӳ a|дuWq Y63)>39?tbԑCj`%`k#Ss];ovT9s,Z惤suk/yzR2܉:3dub-Cp\% ^Hh"͙1 V|\v+~0~}{ m37֍w>$\OuqVp:v3l(3cH,6#57aՊߣ:@1~U08C GOT SMlak}Jؖ|m3LΙ?jgXJKwae;kKG;|REj%, ,L˟mNuunciyj6iZ>C7sO6Ҩ=طϹGܶm`maIHhfDpu1lhF% @{tuB9l(ÙV]1tzz~#((PIYhGMQ~of=M<)r ^׮Xi*c9OPd>gFKh2AL;v󝑑%ő +j0k&ڟ7{Q23|Xĸ'$Xp6o*͙1ݝ )t[O.ysfH.=voE٩ JfMn2 xhaCUW-Rr|1ȎZ[;KS5%9EކuhcpPY0[ci6)Hz%rҬhoczTt3mPMQS: .cv;N=NF0g8ij`&8}z'<~#*mcŽT=}9-v fS {6.}6w- N"7 }O"oCX,\eXWS6tLV6Z5Dq`̷1wua)͊:d%6hИa<`lka"Q&i)eSĆ~q^b?jQnef ƌcIpФxwylj]CTfwF:Pȍ]YL.TiS&F7)a-gbfԘm @NvlV-l~놥M,?ykLOw!blRk&d̤^x^WDlmdckǢ"؎*gE:HoH)rrP]ԡ\:xt]jW&(B7C*Js戡^ E'qsfa;O!C6!CΙ1 KV8le$Ϝ:kLOnj^$=x?a0v?z0B?Zt v؎1\f>%=!vۦ .=Ԏ+lkn[OוD9,gP[^ZE0(`Հ-l:_~Bp2:ʭ厭!/:$?5)' 6]99ز~ ș=:!a"CT68h)f&SE[#n]: 8bY=´3+wGlF;.g[Xb5a3:r;þxRŘ߁ ~HeF8$#~ږ`!j2LG9`P]ZԡШaHo&L#u2!j.[?AėW SBQ=:Yvwf&:oZLa Q`P3;KAT LJJR dg@ O.& Z(@?HK  iɐsgϤ$hA ¹3g~h4 FC.O@BA@ e`xA?> stream xҁ BDLF8.IK@_YAzpW$#8sxܵ endstream endobj 308 0 obj << /Type /XObject /Subtype /Image /Width 529 /Height 476 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 316 0 R /Length 55880 /Filter /FlateDecode >> stream x|ɓ)o&qʓ<)`޻0զ. $@TP^OW{ ۛjoooo}>sss?4{v) S2, D>H?\l[#Ka @TW6P*`$ `VHTu </mU7mCxL,-](]}\CuUx6բⲒ">V 2J|*UR%TR[*TRJU*ըTRJTCZjiѪh5j g jjj&Z[T9Rk[[k;[mmjjPO*uIK{T{TQnnZԫzHHzD~}} |Qs?|)+X﫷C}`{_Fm\mHj_;wt(O }{sJ_٧l@*`',JZzmj,eƯ/Rv'ew}*ZGёRog:>_=΄-}F|HԽ\vOH\e]Ub]^ˋK+2q.^dreFFIF !Ȩ@ jоL.d F5ڹO 5dI upѣxCE=\0/ʋ.^kM7/°@U@^454k0ya28yQ^JyH7Q 72KKJ J vab b4gIf|QUPڲҌ@!^ $WJJ4rJAF04Іh 4c.anvhGD8«Ƅ.R肅n^t_mx1C ".0_!*r*H)3#3_!W9"S- 0"O.eB犲᪁Nd-n Se-oks++*>;&2:R;%_9wRgQT[znԢdn 4z0$6vt3Az) sJ 1gV 1;3+3ӊ JmffUstyxlejRS]\'礆\P4J 9;H[j77ewKB|~>IG# _ [mwyJȁq&'%=偆qW4JjhC jx=_\nMF00SA Ca /^*c 1Sм-%) ?ĀÄ)$9) BrbO!I $fa)PѨB n[ƸQ;cI҄KtI cky@hX*6\b80Ɠ2%5P j /;81g Q`7g( a2 tSh׻M>/W1ĄXԙ% bU&,$>.W/@.xfRZUrH!q1Qruj7֯]keiz7d7ݴ{텓5b<egIrEMM,Pss")QN@Ԉ@\wJ } pGy{z| pxD4;AlIm"ab(&ZNp06ˆ^LB ݑEgi \O0 xW9C ('+#]?LOek 1*35 ILpYڐ!H#|eӗ#ݻV-_F IOKA##b'cc# B "įޯ&DUIQAdD(jR]U΃x⨈7=#p' ƨQ.*++-Y*b)f{29Y}afLOKI!^ ͙5l5#-S)V._6-i'RPP'aaBu:%Og[g,7=8-\K^na\ԩG\fpSHjWrO4SJ TS*WImªB%+LURYTTTC춡ZB&} 7⎆>p0Ō8b!QJlSҼFIhrD+.A'Օ ʿu1)S9>DI 4 Tw-X}Ls=PE-CJ|;oknnnmm%-|O7Έpney⨳9NyW&ԗnLF0E1Bð5^QkJ}2;:Tbty5nJ^= kJ{|˃Բ%f}AZrys.Q!x{Ӆ\C̄%ȤR 3N8N~NXgvfl܀43{ĹL.0&Bn=tH[Z\ &lww~gL"vur,).fKDYpmQA-Tq!Z$q}BJ5 nĭҲN\XmJ8:Cfup;hn}P|TQQaBB5𞖚RGϐ0/e|}|0&aNu1'd34Qd:*A411{==- /$Aj_M6?EK.pv%u`/W[cF*"a8q/yecF^SSMn=dҍKCH(j6l@B hy'΀A3P-#P[]ZPB әt˓K\?,ȓ/_(xU 0_qB&\'J*B.YZdАFUA NӅ kt!Ϸl~wT2sWB\\&xxqT'3HaǕ}o~+%&lɉ*##>*11ĄxTw)ڜOf]@qȽ3Ymmm CnVZ*⟳HJJ@pXAM/^liF4ҙ9YY&\Jg^F9oE=JZxͥK*FǸ` d$''4ۛ[Jl\Ё}ui))4y}M Ź" VLAN'&LC c# mvBF* Ey!pt6bO"+61QpP]vٚYB.[\Ӟ92"%Rm<9y2IcC-RQC׏>aM+Iaqemx*͍5떓yTvfj|t3Ӝn:ݲd`20b {Ic>0fHZccio&1y㏆ݍ+֭j 铑NNLoGMtRBE99̚А5f4jB) ,xm_Ed3gf@`:i'ƕJ_C_ܮ~]WWcר簡/͙1S"DnLMa^Pz: Ģ''G'Gn9W hWM8^_Ꜭ[vNT!%E AUU8aKGFr/]D+\vllrtƚ Fl.YݰLKN wӓo d@-+0pM|>aՄC`K!u`J^X%H|C-呶-@i䚸'"#jH=t.Ha"R4rB;j,*g2u,sb|ev7&ƳaZr!M)).Զv8UBRN3 C\lEKZ ؆b0'9)p)Wv]\;+)sKNjɩHSǎz:GGcG*1Q/oʀp?!>AW: `?>O^]zθWy/׭Ahu&Xx‰YصrPjwGtB^Y`T,n0AGtS ^RwB@X!ci ֳb^9u%&2<48PN?HXg}amRN*ęQr^`:|,*"ͩcG<\8:q-*QЁolοc O C\zj7ܙS';IOM)sÍ2C/2geb|DD8PIFT,1\ڊc:4)Iw7 M</LMa0&}jy?0l-i`QՏo@?sԉcxG9pBWũ&@;};A>t428'"B,ںyٹy?24AwIJ!QyJRe08^78y6vCAtBD#Pu UL&/룫:/IJaB[@-c ?) m,,]. d&'%i2H%29UUG!LG=b4rT\72 DI 5g!¦JCMV1 !aD?2W4*`@@w@{ޤ՗B2lʈ0^(%Ȼܫ!CX XkQ9()\R>XNν\m y%ZW3WXH.t-y5Qfږ+rF$ҬM5kE\ ҙjij&G&#b͹&mmFU_ԮQ3oK K +/[M5"0KQqy*5U S_%^=PПN8?~D|'hE MZdJύ8΅V]vfꋉ@ !,jȐdVʔC1Q]j{VLnTs#CPgWU  Z˅= 0 &9GEH)4X>34r/L\?JPSOećD D?p𰣒#}ء5ŇQHh刾>2%4ः&>@aD##xb 2 5XB ,JE T̔\*.Sh:Ғ<\+$lH*$ YqA6Gvs $Q^42%ED腕b) VNKEssZ=t]=Wյ=.\0(^,wd 2ڮɮ}YmxAM:kȎ5NnJS-'r-[B+ĉ4 o0?u,>" ?9.:!:JG }P,!_W_( b}Uء:}a4ABD8GG%,\QCt!u0AGJIłavn2G%d+L>vww2ӤSm+ɹCBdR Jee叨E1!c@ChQ& 0žd%ч1Dt.02 4p@= ]2 cHQ& (+#ơenYmkLO% |,K 2| /0 !"#Pb PtSpT  F &QH~ +‚!ahaJvpC0;C#aAx!bGӤQT' Hb߿ڨ^tQ0kxg"EIHAmuY~ u)Ws/Z[[Z/]x₲ l}m z/F6ƾ?ܽ{ ß!:0 84j@&+Ds\hĜU;͟GuPй\ 3MhEb+/ H rӐ@Al]aDEyc_|Y~(lȂZ>x`ЗQ?'> |EK V&rfQq?[~,D)[+V&h2{|t (@ܼq %\l# 1<AфI fLAba+c@2U7dr 22[N^|ǎag!*(.J^0i#]]phϮ_o! -QN6jR\X{S(5bq'Gߛ` [~^SiGC_j2$ ֭dy^R ">w?s BPPLh (O=:ÊG_ ' yD!C̉lobE !_:+V(;uUWg&2^(lbeKݑ@Fa>("Ӊ 1;qNjRVK!Cˍqё3Nګ](d(I&?xDր52 5 T{5IzukW16k75 ܜe1hY~@thwe5)Mٙ?3╗?=+3-ew).'MO}8W3;;p}[6nDrg;5X][]IG&NdyHZJxzPP2E+Z0Sȼ9EYrkگw|LtY/hd\#QŊsZݲC [yC)z -֘)tNZJ7P0~d/]0osp c/W||+kXo޹Յn]l@{DzJxy(3oZ(ǵB}uLTwAS1#BJ#Q-JJIJį~U/7 JS%*+r'de.ҵbBlnоVXת1+bc:l,-+PȂ `_R)eeEP_\ !QS]\UQԸ|O>?X|yK3퉳ej? -/+ 5VIĢow@?vv2{ G퉏ezCuG{^"CBvd6#`24~Ir2ӑ22RG*DM"!/b~>emףG1y2ףGCdQ}yȋ#s B1`2QJUWFuE4>4CuCXf ~ #] 0IOMx0z@p OAo ^wНa![PrGO8BGip5xG T .pxGZ)(;+#McK9@_DFcf5ocxj#֣3T qwB -,7K/HqNq84#| 7պ!rwaiS&b eʀd^u hź`eq58wHz/:PØ Xķ|Q BoR x}pw77'/O1l},!_aA!y6#= JOKq$792ƽ470R9e{(#-9<犞bАC((+G_ofz*2X 19Ǿ9TMa vA,'Y ~꣇bKG"=i::!3 [`qdT#Y8^"!&gyd"x|h2gOdz!1BAR. ӚGWȑLOcRF_EE/7T{lt&p:@C^?E yH]D&|{;oû: @ǀLJРMS'O@jrҔwߡ `H`zLDNf}[ D;20_Ϝ:PWI J /S2k{`kB*|jsL ,\pa$}/>pʼnq1@ xF"cR9s2pD{w'Ⳮ;}9ߜ6eyv"vOItl&RBݞ͒SX>ɁaSXڞA^vˁ~4DNCDsJl';|-O|[7>0w{?߲db2Wlᗬ/Z!}eCKyu0Gh(sO[:Xё|-5't]uS/E=}Uڵc;2fՊ9>fqe/}!X ۫ɴb㵽l%_|Ξ"GAxẀ%qvJ 9<; F.J`knM;ϿTv.tbNSǏ+>_'G!C:{``Bcc`%8 aSB7`@2x/Ndg~0P/4EB~[ÆT{4>}wxĈ!A8o뵽r(8ȼNEW;Av; VIB2`ާH\E"?o/テ2'3ՠu"m8RF_{3}ӯrXHH'Ti]q1S'OB;cΊܜyʛaΟ49rEUl23g`7F-$`Ģr \87ܨɼ{Nk$##H8Rf rΜrC η58PȤ+-E%qCCLR]%!j8'}YAOgwV>teFOSفWN*jܿʻZukV[]8?/YY87]%ut! CpuSrVbvVƼ9)#77;3CS8 )Q-BZ&|@]2z!K))K5%l{.&]ɔR Se#LUr{9%XF \ҿ2j%~(TWj=Yg2N*9>IFke„xlǃ ١L DhvbApfJ ݳ{颅Fr߽! LP(X4  # O-Ё YDڦ3> @!#P&&.K==ޝ0~З|2+590wS=y{ WK]Pʅ2_N~(3fL!ha115Gt?QoNJ_zK'"k+5t 'Bwпt0=# % ďȐ7Dd୿&?Uv wAU- \?q&\OBFTX:Aǣ%nC┰{txb5S %n^yODAXt0 O" .`~d11˔` l,&>Ё}8# #~RPC (.ƊdOnJ-Izn3LK3#82 0[(|H>A#"5U4)J1!!&J9*I Wm.=y:wD<6S* 8OnJyT*6P,*CtP(?/.*<)Z/%!`L @oB~̱Yif^ p@$.::Giz#9'Mt0%>& Hgf_8zaGk |a2Ѝb}Un "P.;B3/TfĨx+JKJ0,1TI3575ѿ1"h475#DQ>8zs׺e-.2<-1aGdogtcUy?S^ D^te}M&| U[~-3ɓI$9ڥ^_`wߝE,n% 1Qa} LK7ʗKQZlTdNfL,VD%R9,)> ,:ߤA(1(X1GGi'aɉ sȏM%<*2R2c>p\P>:62SY2I_Hx! yiE VLo̼Pk]֌ M7%qg24ѣA[[D"8RJYhꕫi%ߪ.1.6ބLH23vhn\א*$Ѐ8=z4I4 x_\s Y=! Qaں!ro];k;/5ki{#<ǕGx ޽{_WBj #22&00x ǃ7c^=㜘+)0:2b?$".AJ`(QB~OlRug}@d0"nۜ=(㮒*Z6=\ qehp S!~,Ο?} 90#Ld*zd-r9yȾ}!!!rT˓x{$ܙ3FqqWCC bcnz][[k3ŋasٲeq1:;!?28^ *Ճ^u{Aqs!_jQZX]m<2%SfZ3f%ӒJ@@렿ɓ'Gha!yA`>{puM]u.|3<=c[PJp䈈{ͱv#r1sƌWD'wPy@d ') E |ݳ'N$$$H$kBu2 ѹ3D 4fVt'$FKXXX+K:;;ϛ'#xP(jjjvډ@ S0OytML+R TBuWe{McZ!d> h;v5/9fG"4?aĉ$ GҶ;㎩|E\J3TZS~E[*a5=b /DW&ky "4:ϸ=YQtBˏ9RP (egg>|ꦋþp M+0"l+-IllB PnlIYޛ^td8O Wo-92M%7EXYEE!sa ODdeV"IOnړziw'7Ƶ.l}e+y eV@'2tRC OQ/d'ϳgi$&쳫66EEER޾PcX+oOw |D j*TB x[G.Q ԉW\56mܘH$59/LOO/}ر|Dz95 2ew`*E- 2 ޒֽN m^~D]W 9s&GQв#㱄;33ߏ%Ľ{s{3΃^u\~^.;AS&T;269 @_ wԔԫN~o<S`TDشS.^_О[T ks(dԔ4Gc3E_d0ɋCyiާh׆ +w?]kӃo>m77.n]seI"=/Y\pݰbuܹӧ\n98 l.Z q2ļE2A:;OdB;0/oׯ\QԶ8 rJMNkW!$15)Ez%>6Vf ;::~g&g޵+::aΝ'5 6EBn$t'M|~w8z%+ U΅2W?_}^*͛7ӟD4ԓvz"? pԑL SI3wt-A Lm3mx_\w V҂w) 322ccccbbD+g[DEv ӮQ쵸]-~dLxwO))s.yQPg?X*mA!zkWlo FDY[Z"̃Ͻ1յ ]!C֪.EE ׭Q^Ƚx { Lj{J1눝]<²2.<~cN1s}~6__@$N>=J/d'4{62~IeJ)߻ߟS 2|_1ͭʰȉkWپom,/ޒWIYQ p 0FY)ˏ?殠8J%|ʤo3m1 #Dt 3rT^y4$g$d Q,@S ۛt@dKDR~Lu~WX3 C"0 0xb?weDG1I #Ar6fzSs]ى5..xb/Y+^N4 vKK˯9@ WYȈZu獚!mCoZ(_=x ܊#G$O$Ks ϏI|m;EjȓK3Y)`GZZuLI=HPq9ۮgu-{ǵmײ+wWۯS7Dv\GƈaǏݸq1c5ɓވ;׭] ^{3ƾfqxÃPزy6[ȷ{`o,do(YT9:򍺻CC;o;G;~̈́/]9s-w7`g͚u݄/Өp<$:<RC~#X!?XD)q˩{=[&H\{!qE: }#2]P2y֗y 1uꨑ9j+W6_f ;99Z[[/ZQF2lq# ,P5˄?GdHwC@!#SRFVduT0Ӓ}/XԐ^94(7;p!꫗/%>ܳw_#26fU8911<,(3-M}o,o6F;}6͞iEb"ѵ3EQbM {:fhC[# }+t;C;B^m _/܊0 XO?:s@k1UNZ ޙO/܆~A~NΤꅌ.?_?B>hworuwu#A#اNrnIWS\,qmIC^|sΝ>}ԩSǏ?rȡC80e}א_2xpݐ h¦z_7 /(Ӓ"ؿ`O任;::ݳy'G{?Yi=K_79}W=~i@Զ "(?Y+KDٗfU4yfE MoMl1!|vMJ㑡oOHM =F8ş8Y6#ܲ:)p~O+8(Bftd8x9pAۭne>MZozXmĆ7~{ _mwy[:)#CCRSRDZQwC l^rsK$xSQa;EzNY?+=J:yAD5Fjb*XeecPYq Ę(c:;vi"=6aWiQLy7$=);R4lEf+zȨ3YoXR'&GEBܒf+2[fTQZRWSPWP v穹Vd' r,2,"ø{2, !㩻,2ˬLjZ22,2#B< H!G;3,2ˬ)uddD?J!É&*OduVd@i-+ʠfMչΎ==Mjh>Mf+2[Zy> $W+ѣ:;PiɉN\]nB]];t!=%lErR{3E12G֦Sug?@Fj,FFZيdwLT恬~!A^sfk~3hXSaomQvۗ_|yDFϝ8ۻAH fO͍ Ey' L\Q$7)HD"fDF'‹;!K-lkm]4>dwV>|L 9ɜ٘B\tqprÏ ,(jS>Sot *2'm<:o|q[+ ƒLw{vaK4Y?375‰#ݸn-ɁTU8ܼ#SǏUW3 Tӧca|pͺZrm_HaK_}OCׅo\'Kg9%%cfJ`7_}iu۾f!#N+xw S'ƈ}p´i((2v,^0sa4+{i .P-買S#QTXӢbF4|Zf>[t]0v 4AiI [C;'F3zs(Azr[cFkŒՅsӜ~KJByu"Cxxg ^lrj/?ߚL7N2@gLG/@gi&qF3ә@ϓKYTkKӝD1 }ZT?wl "Z A" م #$@C#cqv&N+qZgcDEhZwD7#rR:,26_cAz%I*|we`ZTyIqfFEKdHUk**L9C4QoiX4Rdl&iS=I&K!#w5b t$ #X$,q:kKҫV!9! 7CY^jkiv6%"g''x6'jypttGP, dy!AB ׭^G%Xgcja48u6Cic+K!A r6۠H/yg? #=`-NpuÑ=ɖ/WP56#~꣇r g%HMJlg"ͥqoA>bD2yo^DVzu[LN?a'D}u"Cxx6x,C6֢ 8:{}M5- Eyp$`_ZTXV\Do;z-A|.٩q'(5?L%N0p6Nd0O[63Rf>z۷0[rL7GJ5Psetuu eZTiI sVJYZZVVEy6b[uUԐR `44$hWrafZ|H0t?`ۓ ;a~B/;dry0ntE`:!V<35o#;x߱ X槟 CѯN 3 eE:{]u%-HU`+v;`1''3-1BOKMV29Yñ(?__& ۑc*d/iZgcDa 9uL82,tNb٘=*wV~?YȀB"⭛6nYiphԌAEEH&t~-k:- 'vMU%#9m[3MVOb|ʲ*6 Mwi ޝ0vB2Ch3: ɑg&20G_@)4q ({ TBl+€/&J3e>hcrwu>^ `C6b;ߎ AZu9+#&=.C_Jgn} H&F/K-MyO^^!dBd"Í M lL`5s֮Z >ezHtYxgh%LjY8o.mIކӧ/]hp9ZTVV֫ÇQjhh6y2\('G/ (2iOG`v,10`cFEE{dDDwwwDx` CbD-DooYnjiI_w͟24$2"ܴE# ֵ *~nhD=pw~^ATPP^ts592<`gGvveB]C/vǔY?dD Ljףn~>ިg?XЅc AC䚛 ccsR/ hEr{;v0'LkQ+Za*+=E.564 4e/UȤy2aC}=B RITDي<-ѣ4_4E!CZ/ŹQwY?J)$) ^֖ǏhBP7e%&uv0`ڝygnVي9{ξy?33l4Kej(-53 JI&H eJ4% J2]($$.SӒRdP&ue41.(!J HcR eJ\tr\TJlɱQI1QI1Ę$F Qts P#CIWC"BC{.$6<.υĜ =M+0:4(:DDDDt62?"*Le/#s0;BV!( Ժ (!G 22@b a08vLJaaLHd0rB -{#:):D&;Kd:-DCr H&dB0Rj!Љ(9E+Mh 1NzdP2PM'F)$ӥLd2g,IT! ,-:ҹ20 d3sAt9IINL40 xPxQy :TT*U^zV ZUʫ*iUWVTgUyTEbyZՠ*Х Dՠ.\"HT{+5T Jtڕ:]rյ@7n\'8mЭDwnݺsNwܖν#}֟Z<#Z=֓Ǐi==yJ==@?Pn[ȷ=\===199FRw?9#IFu-/۷c}&n1.W0yLJd *ىMV5/2HtLjN}Ld!$Ig zER d]&өMkZMIO'g5t.  Bԓ2¹kEiGUᦛEQj?d/(Dn52@F4" 7 B!2pCdEFtտ09l<(k/Ie!8P6`D nAFsA"dw֎egZA&$1dп(A_yp{QZU@9tqoYdČ@!2píQP+@vXI'u%YOlF$kVk2ݥ%ݤs~>nPIBdqѓ.yz:|dh"ŴWS{t2`Y0fv>$J "7ܴ+,2nւ46 /w#K'8|D!`ٶx@Quo\ h;{YwJe{CovHoZg7  Ȩa(hژwC ыV.ᶼD/۶j)㓡I/cc|`GܳOc@*"ClKwaIEG 0MUy㨒'k;tZQ֥17'v&_w;s49鐚I'ؿQ޴5^=N#)."eȸAM{uUuf)8̦'`wޤY#+{i: o=FȨP{l' ګ[wawd7TkIY<}ac@*7 .$;-i쿩p_:|n⸱Ey9iL X~ErB\AN-l-|NۛcihMvNx24hNMJXG#2pCdpDFdXͲ#dz= ,LBJEq`oz)'ۯ@f+{S}[j PLڥcW6 qHÀ+Ն;ɐq\<mްE-(~J= ڛÆ|!8F|R&o~I~v&̹W:)*ԑ3>:<ꇄ(oG(p{6!)EdpYG ]t6v L|S];vݯ`uյc@S?^-&zKzTL/]<:L:B1<"4su ' E>ar-?̳W,< Æ >~0Lvmwد/;s|Y>Lȷ1E-Luir"9CvwVK {[S~&j2Nw)[lj2B:w QӧY|ٶ ץ&%4A  IBXQ+&ՆRv2qB{vjp~$T!7b˫ZS]IbX73ُQU\t$ |n-aU=4e>e_Q&>L_9"6 hTCH%~߲KHY٢\l&gNU4̩JhbI$62"ZeJ9|a2tOANj'`vyx˿b3/ב &PWL4GƦMP6#2DiBW" [nmjjZQQCP-C>ⴏ"92d}~ݚRzf(<}3ܬ>\(R.RXCVȱyΜ>yc _s3Tw?=\3eLYkbT; :w4 i5dE%$%@J7qjff+)W1={~Ǻ:*;;;C^W"op>yߋvc>-2{qL\:b l)sMbn ߎL'Fcd.J%j12L~NVJbi-B_=vB$ɉkV:yR!Fd@4Ir[Qn`?g٢t^aC+8r諁Hm>iY593gXgX-3Ӕ/2(E1:yb_҉Վt}TrrrCW"&|qq1 ^/2-tQM#e F}KL|L)ѽ}/zv yHwTsgWM.郃ڃ2_!aC90cķˏ` @]Yː2t%}?P(|G)t%>*j{.AdTTT_Ǐ"2^cE.J(TC+2tVZAwttѣu%CBe۶mPy뭷8|p=i 쳿CkrCƏ-|92tꝂ|ᇺy\\\Ϟ={アc_244ԏYƍ3>-!#/m j(+DFC+dֹKJJ~A2ȟ24#ٹu];@KR>Ib<"2B FF߾}yoo-[eʔ)# ֮] ={42`_߉'ޯ_?##Tea*5)aƔIQ߷X TDF}jP g1t aaa ~Ǔ&MDصkVZ\R0h/b|0t0We9}bǍY0ws[ IL0 D"C:sdF62G~ep C*"Ւ!/\_r.#k?.YlAB "/F S5M#c.Ud7ܚsSGan ' ;3JJ͔ʐdҲӥ2iVZjV$S J&KdDYrbZrBZR4)^H JIIJ JHOEG< =s. D: ;G\oXoYPwv(7<>d>| "0:D#,<xZIQEo|)qьb$ fa $N)ITP2 LZ~0e ) ˜t0hi3 BPnvQQN}d\n˖&9JAާQ"/ K J K K ʊ ˋ K@ŕ%e%JϗVUUWW*.PUy󗪫.]|⅚Kj/]| е+Wn\z5Эoݼqt[ܾuwn߻{ݻ޽?{=zѓǏ =|=oRુ/&+/܆PZ˅h#k B0._G=0C ?Ue`X! X0a`.37#na*8rP)#fhc|0yVZ8@ܺuoߎoݯo{oIӺf} g.0gw5A (T 6 d_ H2rAvӦΘ>vM7v΄C`vRɓMBvIIUL兙Io !gEh'/uíl0м c [oEPYioT!堿deMT:vhl1*!2:{NpԎ;]G1r=/;A+P4>k??Z{w~}N^ Bqھ$wg|;z#׳E%.)&nw 8}P@ٿo{ot9bK/Z-^g(B]8jܝ|ӎCE!2Bk8Q %-i^={ڳx.szןo,r,KJTCzK |bWxS D. cӎyQ|v~\"oIq C ?x%@d 2t#Gqݙ?m݇Y ̮.T{aJW~@d2os .;{1r=\aA 𐠆p[+Bd 2t(8;۹&7\"WbA\Tޞ2^jwEd 2tk6㨵[pwϷvcz"$qn;w?z%y7_8|u$1хFq1[0 c8D"CiGqݙ?Ww,rE8zpkW/`0ʕ+{>82.dd PI-})C=!2Z22`8i^|vv'FgT[s͛eE5n !͊i5M A X~Gqݙ?l,rE0-PCƐKM#c [6Y dtO+T'Ȁ9j]ܝ|6\"WbG@F# S:q[W ?P\Zwd8Qk];۹E2^z.C'.}a#dz^:M^gCj٧$^r߳!2N.e#3_ۮ܌Y0LJOSU^ЉFd|7dwd&,[_̀z6h?}nzDKazGpݙ?m7az9GX4lK/R\a}x^fF#Ȕ#L x+ :f#_J ɞVd~їLS9MyQ[N{]ڥX'˃{];wh/;2_27áZaDOظv+: ە[8jmܝ|\"qtPZ[mZݲ%zȖ#yV8AN<ȕFsK^*P8H.50UVe6Kr3s[D^mg@0?ƐR( 2!#Nj q3Gٮݙ?έV,rYV;89ܷO<~JOسq5I|HErd@%_ x+Jbn2W*`ό70 ITsS`LKJ$! |~#GY;lןoK,r2gG;G;+5\,+(%՚K/q۸f5x12dă\u̜l "A槓E* 2Z>2Vle;۹\"WDƝ[7+J@K."C's(`01N  !K!jD/!2Z2,_QK6pwϷNEkWj*J AK.EF&f-A: _|cW>`aE "j 91ibݳҬ]k !(AdFdXج(KuܝjFg+"b" v%dn: Wݚe SI*8SF㞝[`ڷk[N 2 >=]Ta(DFs#c3GYجןo狖,r%d*ˊKQ P<B%1pd_)V1ǔNt` 3e̙3_/t,ri/Q,-""/F|HTCЉ2oH$_ RE,gXtGY9qwϷEҢ<9 "#fyrd@%O '2RaPC7Addd2X:r%;\s;\"\Pyi!ed̿:r׆ |"2[8p˕ܝ|r\"WFFA]5#wEd 2tsWpԼܝ|B\"WBFq~6K "cx+"+Ȁ9 k|v>s2\"WBFQ~6\("ɽJ##D#O$1#2ZAF:ȘЖf/ןo0r=pV2@B)U@zȔ##3c`bFd 2 f@5wg|;>w FgΒ@Fan&0@LJ<м#rEd 2tqԌ6ܝ|lK\"WBFAN "=2t7" Ș>w)Gqݙ?ΧZY #ɽ+GTrЕlÏ DD[ZȘ:{ GMcݙ?'0,Ng]Ed1dO @ƔY5wg|;d#׳ȕ%e&{!7S&G C WO3"dȈ@Q̴ןo-,rYL T29r3Ȑ=uAޞ$fD"C`H dL253swϷScz9 ଴̈́y 7@FZ*/En<dL&N7ןocYJɐs2nɩn WD"CG1~0mwg|;}\"WFFz*(?+6ꦑr !(TId2O]ݙ?LYJNYom99rd@0hrPIx &|} Czϊ n-d,Y ]I|ːrUBp^÷ ߆|B@iܝ:ꜗ?'Z,red%r3Rnɹ&GF(<B%1dO%9|m- ?+2@ \&a)MS !9U/.GFz C'J# ;MKs6ې[(Vd##=-S$9"/!1dį_Ks6ې[(V \ϐQs:#5DNa)M{cn:q)%dH+x r&|} Cz 2 s32L$A8/+?;C/N7 -"ċEFLnyY*.VW֩"7Kc'ǭր'Gdă\ D&^/2@:ضnrg;S]ejӚUܺNd@DPÏ DDn0s3e7;z`ϡ= 8ύՉC=ڻsx1*z!C&2 m l DFȐIs3d;6?u#^y?szr x1S42tA D"/F2N;sc4#$<0%GTЉB$f\"v ɖ /qiP@3 ~t%A{novHoYu9r9d@FuVi3uA* #(+ȶX,(9mKpXۺ.]/T!3h$]I8m nϾABj.Jz{ƍEj}j 6"#adsFFz/N7 x2fdQU# bYBQ) ~;&(jRS[d.#C. )!/Cn<ȵ>26GQo 1-L.Px#)J^).AzOrҪS>a-oO?T)$SqP\tm۱ӻw>-Ϗ?2MQJrU"#Gdgo p'~1 rEJQ)"ns&^18`Um o?&cB{ֿ(y~l{]!aCϱg|,Mz4G)W˗_Bd`o02fUeJAni3AިX(j3F1_S /Ȁ/*x3={EEgg>{(DCUУ=:|n8Q~r6hmjѺ F}ǿ?JZl DFΖqEx1w C71UQQ@v a0`)؄Xd]Ed *x ֻ6o|xwq/ | =x_= RHBJֻ|ddhLk`׹Oyxt.V3W (uh_j}j #2 OOEdlPR1IQ)JU9MEk!`wW3qvUoj{_=gr#k}/}G[? 2|l(DpW@iBd`o02*CF-AK-}}98 m6Tv ؍x\3"~rUĈK}j:\Op+huvkjJ"#Gd0H##ƪ%?mgrl4 cC`/Aۄm8F< +rNg)t*ϲLP%yQ:F2D|;a!wF\6r%Czϊ 6"#eddR9##ƑJ  ;I L?Wl·!߷P;7P9"#*GFj c?/AۄoCovHoY 02RS8##- rd@EGEaKs6ې[(VxCd`9 ,O2YdgW 2lN%9|m- ?+DF"C\!KIJMJHQ[:QBy r&|} Cz rd$Eehꕠ{wD&d߻{ɓǴ/?w.t]> 2,%9|m- ?+v #20r]FFrV32$d7؈sG?*.5R={vέ7-h3JP c9o !g\d22N;SV\pRCA'ɣc-*,۷oݸ~VCݾ}_ q^s&|} Cz #23+2$rdHA<^PJ]8JzNKJ<㫵_+Ed 2 f#C%  S*(T}^U=o Z]{{SO9Ԯԯw_WGC@d`ȵt92 BUzr۔ZAW ҳ$12&*\Խ꫍ J tDFˑ $c>2Hy(+>_(, 4YwȡG\T ʨs.ߎ3OCܷOjG42A DVDuIaaR"+ؾG{3_ou#׳_92B *J )VUYtRUeiqA>_~hpq;Qןo[\`zʨ) qEy%JRCEA') d<-)!;kċo{{ٟ!2-[wmswϷM;azz`/ ̨$d@.͐Jcc5tB?*]JzEu‚W/ACЂa26sN6TTanv7}ƥ[7{o@1P طu&>6Dd 2!275swϷUwaz9 I2gd$12%dĄIN{$ć4 pcSTGNm4t1q <:X,0ϓj6:AEP(*QT"E%0%3(EP=IW בןo+,r`&GT@R}OGqG8CVQ; .M <:X|iذE#cEJTeFQMQ ;r(JJ"cO *!öX,{n,LEPTES zѶ5@D#n@Ʃs>;upڶ6*' jK'&ݻg#gO.l٧׈ 8 k|vn #׳@FlxXPz){mZR u$ zm(pj'X1P ؝xjP(ʪY, 54AσuCQUR̙x<8Et *EI^ cq˝W(UL uO#F nRV)5Ad!2_GZjwg|;_f#׳aK8## +eokr k8t7.:2#-#$22"CF\1q-tM1Q %vg1 1)ʦYL1R4ca(sym"3ˀF$ vA(k/ѭkI/![.!P@d^d8n(ە[;۹ՊE2+,2rd@l_d@he .ߛ"t'"``cO *"c230g谼) /fPm""hv&ִ:[ qo U7rf|vdZ\"o 1”w2itUP6Pֽ :}m[ˏ&n 'aCEdNQPL &2Q)j>ERDY8,L(O9/Q.C q6݄0P|St :!j jZf3_[ڮ,rHIz S͆ &L:u֬Y .utt\v-P gϞ >aCEd.Lc1[.\L /FjUFQTOޭE U}[nE# dL6i8M9 >K(NJ,X|G-ݙ?[;azP\ 6,~F+jz61P ؝͍<5ltԷ#EaĔ(WMQh"Z/",JК^ dxEܩ&[vv "zvqx7ZU`/JCd@dXج(KuܝjFg PTqrdi8EY573tZjNm}cJV6TBW5J@QLb)JedSھGL>wɣzEP E_12<q҆[ M{u1,.7ܷ_>ݺvQ nea3_/Z#׳aR9##yAeͅEK l65q"t)'`rcO E (`Pf`;2kVtE$0WRRU=_-2̬Vs2g|v#׳5FF2, m 6l2q!t#8$`1 T1IQ_=#OZӓr憄0NQ)۳ey [d5wg|;o#׳EFIcP[Ȱ67tr2ڸQb&twyx:Xl >aCɖ2zSw4$3@Q[(jEm(W EKGZdwg|;knY"#045Pdefj5-s@tJN6G,!2 vy9j#wg|;#׳a%sEx10h冶+VbUsh1P ؗ2԰!"‘:rܝ|Bk\"2@F!#VQs.u4[+XdNֽ]PB,`Zi >aCD"C+Ȩj c5lwg|;1FgNKN2_,L52fX̳647Z,XipJ<5l@dpdZhQp\swϷsyK1r=\Cd52.6p #K'Gݢb(/7԰!"‘:r6ܝ|K\"W "qzXig ^а!"‘1}GγןoSfY`zȨ0xFȀ&>^J2E\0aɴu[D\PB,`|SÆ DF Gƴ9K8 k|vLs\"W”45^XX~]c>,Nn]kՑ#7AP]0Kn MH[E}5`"\0~uY[D ]PB,`|SÆ D6!j u䨩pwϷI0r=pZgd$%1"#0T"GT$"U~oi`ɔ™E v:XcO "2Z;D@3sԔYܝ|EXI|`bI|1 uA -4g[XG? gl)`Kg5j@dhuCT dL253swϷScz9 `iR2h@dpdןoOY!#H6KѶM&N,S %KG\j4$ȀB54AƸ)9jԅܝ|̤E!2qrdŀDVQg|p([& n)`7K<5lH1vR^$ ߆|B@ib &rFFb<A”o-~h2~pfѬb(Xb >#C DF GwN:Ϸ0r=\]d*I&Ȁ H=d7`F?~q0F8eh1P GXxjЂ!u7rqd+ gC-`2vp&ьb(4>FCD"#oNd772z2k om?:F8itJC-w}d@d`ȵD"ċhfGFYC,FF;Y#I4}J_[OP!"ċkҢX92bAj"i -0la4Fɯk7C u+KcO "2@|"20rFFltjgd$đ1$-R@FQ u$_ DVQ7D.##*5!32bɘgV,GTEi .0da4F/k6C u}1  - \w#, $c>mA,u0iM;PB,`hi >#C D"/F dH D"/")d6,,^ F_F:F8fh1P XOP!"ċ72f1rdDDVQY-q0F&b(-<5l@dh!u1QxȈ%c>H2ˠ~F_F8Z#uh1P [OP!"ċk rdDEF:2`p$C %~ә5Dd 2!uȈ!cE̓N3 7h`Ÿ7mC u1  D&^Ird@%I]d8Ӡ|ދZ ڛ|,ihv1P XxjЂ!u7$ c>aA |moǍ߷:X|:G  D&^\Ȑ6247-6[ ؛t(] %^Ӿ>2h@d 20b͈ ia2R Ȉ LftosQkW&8 Gm] %v1  m C>D##:Rq1d)nKH##BȈP̯QCD"C+Ȩ \!挌h2浀v3 :7ب`pgElC u1 5"2 LAhd|1ày͍z.[ap(1P ػ,6#C DvA/LE!207 ͏Og|1ϰQe>+L9 m}"`{i}d)LVQ\05baG3V>v&V }M %N6Dd 2pa /Fޜ(hnd|bjva3nVv&W }M %I}d)LZ GTEi(cS\C3#@dg"^-^4rJ͍<5l@d&^?$dOc%rdH "!#HMd0m/kЂ$ qa #idDK䊌Hc%T3!_u3Zz7PB,`717P!"CiHmvd೹̌:[ zؙ[-^4bJ_#C D"/Fs 03d%ngwpzmb(37G  D&^\ Jo&d|lj\öfFL\/M %/̍P!"ċk\F!_k̨IEC:X|Uj4Dd 2x1rn.d|dj`2h@d 20b:}?M 333/Lz[/jJ쟛?#C D"/F|RaH3!CS5̨I¾E:X1|Xj4Dd 2x1ׂoF#w64xc4l@d 20bfzEƜ'TcEFm :,7J{hV1P ?13_j@d`ȵoar;2~g*2O~<#EFm /7JkhV1P kf >>6Dd 2x1r-VE+lysFƼyAˬYlSlm46Dd 2x1ׅ "ċk"[#2 D&^DFs##{:y)S'_U Fdx1 S!>oJ%%'idm> "/Fȶiw)MMwܠiJݮ "/F2뭛7Jjhk٧lԭKпnݸ@!20b-<:S>aX??_ǽP٣id8@!20b-銩W;c6(CE=;;VBFaA׃!2 L/?ٳgPQ2ӓ@GFd 2x1rGǏ?;˸uV.Ӿm#O>g4hп_fFFbB2{| 2\Fdx1u-OdxHybzY@q*48ZKʼn Jb &I1^ "/Fr޲䄛׮^׮%%D"2P L|a*m BB}"OtH 0ċ0dm  D&^DF`o7?Gd 2xœK߽Gݿ SS(D&^u#5/LI/|ٽwJ rc \o\DL\\XXZR@!20bZGFnK@FI~Oo^-A%:,8#M@!20bjFգ.0_Rs&9RPDF8΅Hb(D&^\kS>vy%CCaYRTDQ.M;@d 20b>2fMҥcaVGubNM8sA/Q!$_/$..TWdT&FxyD 2 LyDHة͊7 TLh vO#\ gEDyi˗kkkւjkj2WTj3>UW_dh"ċY-t˶mJk5x}ԳOJĵknܸq7_NqE@‚ܜtYZ5< D&^\KHS}D=;澿KgW_ +o^ƮG ۷oݽ{޽{P޾} Ο,/++.**Β+TNz#2 L.U Vu|NM8"#):ٳg2I˗a~C|<Aƕ+W._T]U,Oܜp҃ɣ@d`ȵr~Qukkknܸ3 O?~AƵWk._p|eEYiyLT 3!! D"/Fk5.xRc/^p͛݃sf{C׮kSeEIt,*,sȡSG D&^\H2߽EB2,_/S,22򲳲2dϙqCd 2x1fDFJ@?OIQхj u=x\ S $ҏI[ D&^\  ؋ʪ._thܹ}b)Ч*+*_g咅TxԆuk D&^\ I0u>;\pի7n\u-ؘf]d{ȶ‚ YZZjJ 5N D&^u!#{:y)S'D|`?oׯ %_x+W*l0^K0)*E~.Jꮳ=}|-"ċk  S!>oJ%%J*ٽ}[buu\D']ZZRRQQԀK55 .i^TXnU*C&n#I?'o^@d`ț)"cNijʽwkA'Ҕ]I2I2'>*ypREy@@Dm /TW>X+xQDIɧi]JDHgN}] D&^ ͛1,-m@~t/Mޘln܀I)ŅўYCbq`A&* / rsF:3HNJ y*' D&^9诿:S'3q8E(s :qؽ1Q0 &!%F e%R( `vx5u3 IJ:!.<@d`ȵѵs{E(sSB(#-5#M*g4x@8`FA(,;Dr zI*!.&H~5N"2x1}*_J%TDϟ?3(@ޘ*2# G&"7'S@ ]"1>}B=Vt9 "ċkg 2?~8uV.Ug17=z PJ|$9}T))YDvVL "C&cfIJ2/X^x;>b ċޅ+5$1iϙ33M+@ޘlWj.+!kDy+T2f`!KK &I zc/Bd 2x1]4 ɱ1ϟ=;PEm>TծFgϞ%DC b# .$H`VA"1!.:2"$>/;m%Dd 2x1׈`_oYrk_jmϟ?qjZRtC"BBmBrXa E^ 2 LB(*$ '"_CA'!Mu*24 ?ϓMġ8^@d 2x1rm#C%lPqaG:đCgN<xGtq^j{Q "/F><5#(>",{\nٰn-L):޼~ݾݮOƅ6@d`dh(D"# C@F\DXLX2_UJ=#2 LkAd丘Wj.jk,A*' 4_gD"#O!#\H|Tĵ++ˋ5t]Am/^QJ=#2 Ly3"#adz{,,(-|%@m3_]=#2 L0{.!Ke/Z .Wj/A9y@d 20by'UdĄܻҢƔ)K).ȆAAP]06Ѥt sdGQ3"ċk) AE%ںy  ^j*2\0btψ D"/Fd5,Ud͜aJQԴS` * Fx lX{Y kHDψ D"/Fޜ7 i  >۪U/2.:v`hͶX,&zFd 2x1ׄ 2U;&Ξ%K݁ .%ph 2EY52 %5@d`_2r3ء@ 8qD`PIQe, )jE53"ċk)9IG R#AF U3D3tX^R3(jE53"ċk助%KVP< EBWr'8c5'0F*PP/۵W As~{=wwİY)-˫9Oݙ 2xxyV!lˎteۼ`|V;thè Ɨ ' o"2P ,Gr NA8ͪ,Ѫ$FXoh#]6o0vx#8~Pч ?;|3cȨ T* /f$1529\tϟOCOApuM ;1o3d0^c0ƈ@e2bOKz%Z΁o( V!lFEWf/1o%0o84cd <1FdT*3ZLPJe}!AC8F2Fk`;^vh/:d;2*‹{^)IzQ3 IPo23 4,(Q T^#%| )s;7+'f5#;#Bp7ƧDFe2PXx1s+Vx1I Zh%U+b! %^-Q T^ f)-NMu@(HȨ T* /f$Xr + 2@(nO@e2bP[CUL%>LHY1>%2*‹{)ڵ2 T* /f@e ,d:m+C*,Au ?Be2PXx1) hʀ:{;CCz TW/=c\#/E޸*/Ae2PXx1s*C5Bb[&PowGWG  BXύ]QsHyl{B-Ny)*‹?qe\+bVZ5x&Θ`˜BsKOTF xD/AQ T^܃xx[c1ӆ[l@%~>X4%3,up̃K},9dΉUQ02oyOt;4a%h0tjyS}';BfR*‹?ie*lۚ띱W/|ZG̿4cʪmaϽF?=}uRd@Ɩ7{oCNW٠n0 kE+|yZ:pp#)@e2PXx1sOp^9k^KbwL[jز%M n-´}ye$TuJw2:o}aCtR֥-[k[O\pڼ]dp#Ae2PXx1s(22ȲL0m+$^ܶk׮K̙7k䬏Uʚem6{oCNQF^VId=߭Sti%[ۻ֬_BXN282P ,gboQ̴@YQ+8~Μ9^g6Xlea.?z,rIfSRjC/o E{ vg߽{wX?A Q T^3T@U'~s\5s1s"asϋ X,ڼ0wu(ݩL7j oaWP{p Nv T* /f1E*äS83-gN*%UJ"|zͲ[cz̽}BZ9_gΤt+àQ j2x2-?8x|Rql NN';BT*30jU}AR Ek6ͻ8aλ02_9iτΠtw ܬWCm;#gr~OBEkⶒɎ@e`̟2j3V:r^ *Tvk^5r_%APӕWtJ2x7?02|x= Nv T* /fdK*C;st+&Vքͯ^;&5)AV4폻jERB,䟛F*RZ#`U)ĵje2PXx1s*cKR85!bGO ep\ZS[ƤUib‹{B? ]P]Q^_ɜ BQ|$@e2PXx1 +s&@e`̟2 T* /feTfT*‹"[Xʐ b.KP]1A O@e2bP)*0K B_sW|zdT*3Lَx!eB_tu5M S"2P ,GNPFuIQwW{WGk{-tw@y1sdT*3o LhmC=>@e`=2ɶwn ( y'"edT*3Jkxx)Q#«i@e2b8jޙHONf%ݷ~JH@W їxu" Q T^i)j:l!x#?߰ߧt+Yh/Dp2q@e`ǩ B:Ne@1N%lP97ʙM6x$z~ XyW(p~C/ ƈ@Ȩ T* /f>Neuq*Z>bn e gdTά XN.߾w_ɻte8? .#@edT*32lf8-)pA-sw ʩ~g,Z\B}+zA_Xt^HedT*32鉱c( ''=Z sϙ3طo_ui>>? ߣDȨ T* /f>Ne-M".(; #(+v470W.77ct Ȩ T* /f>~e8>uwсdGSƓ])Q T^H8}/ṿ@e`= =Ȩ T* /f>e+]N@e2C)0'+f439iQ2P T^T0|AP^?2X'8FPXx1+LQz?K adʰO-mkjjz;;&hon$qoT* /fV'ʼ;8Hū 1-sZmI IB87*3P T^#LՍ6D4!dל YŹr29̢lfQNzAf|L%HOMOMKABj"L.Ls;v{8p8l&,G%P!m?܅pa8F}  -vJ*`D 3NPJ= rrp@k*0 ``rKb 'Q !_)+DVeG842V&8Ku ;Ľd JAjI:-6u:YVb[zXmu&d-uMֺFji- [}V5ڛۛ&N[KWkkW[kW{[w{[OG[Og{og{OWGogg_Wg_w==s(=] ;ða;v{;v8pP쇦qG~(Ƿ~ Aa؇J90` 9> -`YCPjǀT$FA%'-1a0Ðv l"Ę`s?  S{A O&T @Aro7He8}{2,FbpfM?J>6$xw;xghqw} {x3jxWU1 p<NO_o^. ȦY{Lѕa6z@a2< mͭͶрg95ӃW IH3J7TV%PuQɠW_|!#-ɨUD_s6=rN%MI}m+AT2w1C>GRw[)|5<{ȡ>x!$@gɣ2Ob-1.6).N^K(b: 8'[mVD(< h!: qZ-6[z;;(t+d8N-eZ:ݠS@KhO z+]#LA(C UTN%5.=CEumۚt7Ba#x7Â^}qWa^&WB7^xcBQȫ~sׇӛ{`!$@~HE y55k*q)7{_ShTG\PSLt6đi2^M%,ǎ,ZX(\|E#)jYSJ!U:gVOفLrO^}; 8yY1~Ga lr_Z&ʐ.h:jN9LZV.2 ӽ9!07?3VaT9B,/~xpAZb{JuYAMGe  bi, 2s5? 2բyjq@! <a:Fu gMz|%mZ  qa:wMZ K|PxsP~@]-ANcoȪW9ٟ&.EA`_Y"Θp endstream endobj 316 0 obj << /Type /XObject /Subtype /Image /Width 529 /Height 476 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 543 /Filter /FlateDecode >> stream x ð4^ G$ \:0:B"CC86ҕ endstream endobj 298 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [69.87 711.843 92.341 723.798] /A << /S /GoTo /D (section*.23) >> >> endobj 306 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [293.512 641.655 308.235 653.61] /A << /S /GoTo /D (section.2.4) >> >> endobj 307 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [115.892 617.745 138.363 629.7] /A << /S /GoTo /D (section*.23) >> >> endobj 309 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [376.36 125.477 398.831 137.432] /A << /S /GoTo /D (subsection.2.4.2) >> >> endobj 313 0 obj << /D [311 0 R /XYZ 69.866 808.885 null] >> endobj 70 0 obj << /D [311 0 R /XYZ 70.866 771.024 null] >> endobj 74 0 obj << /D [311 0 R /XYZ 70.866 698.899 null] >> endobj 314 0 obj << /D [311 0 R /XYZ 259.78 270.329 null] >> endobj 78 0 obj << /D [311 0 R /XYZ 70.866 230.541 null] >> endobj 82 0 obj << /D [311 0 R /XYZ 70.866 111.46 null] >> endobj 310 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F20 166 0 R >> /XObject << /Im14 295 0 R /Im15 308 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 324 0 obj << /Length 2867 /Filter /FlateDecode >> stream xڽYKsWfJB"&۲W[ؑL ʼnIJʯO))Tr¼~>[cRle,[.EV˯EHȟ_Q^ݼ_-oWŠl/($ybacήVgac0 M~_g/ϒ,Pnv8>l!o5aJ}_DSR7RT%ʦsk<ʼʌঢ ګj>⡟._.I#=~ru ^F}A-:iTLPw軖jW;9Ev5':󇉿LW%MoC}X;1`XkCk(qRnKft[ʺx[lͽͧ76n8 VD02?5ɠObd s!X%*J,`0|), aPFҒ(MKSS*h<ː[sR'õٶQ5,϶@1-h8:6Vmh0C=l5uM5MN!\ oM#3׏` MJ hBWb>avOނ62z $5ב_\ԑ,G8֢,IP!zǗ 'T\E/HSKi7AEսj@XMǀ5% ۭ@uG@MĻ[򯩲mdDjRvZ^ xRBa3%9H@w)΍F,nP3D4hl(Q{:x hP) |$f(WAB4 Q̼=DhN7ZjTD x5A(HSO\3!}Fhr<H1:(]_n(ؾ~W4=wn@)WQԕ`z-#,8hU o+19vdzLjhJzOXohd־6Բ`ŏDč3`JMw}yW.oySEk3aq$4-ˑb]w-+ײ)DrOwD^2_5ʈaOs4U XqF 7#f'{j(ؠBDेL2UיX0ӝl ^&VK]ӯ;΃6 6OQz,֝oU6@;nfΘ4^eH *1r/Ny@`i ypsafH](jOc ;Ȱx|z_r ~ ===֙I8'g=(}Ffiȫ9L[1}0/(!gBjD@ zדjq,9 9w$?Z83{’h,԰ P5?\w:&tQOR VS5; &{Y7푟_YO}376g!sBu|7&*=,g<[%% f,|M/\ BƱ)/*mI ,#>RвA8 .U({SvEoC"<@*8 ml 6nMkNT;Y8YʠÔ^$WhC֍AOfp䝮Z!Du$=cDb⭀x:NVvG߃0 wwW#avxs)̤^Gsx l1a8" Csj^K~(lPU$i  T۴odȔ]rg|j:ZNFa⭾ܽTfl|f4;Ŗo E+{5K`U3nk%[&JeA<qL1AKnQ57=K@d+PBɟ( ! 4XWBiz:No9LaR0(,0I c%%?6g8K b73-g35"0>NJ?9OQ {)*And_8WaZ1kiLG .r<^B'Lnm:ou]Fǰi~A/M߾dq臐>0k*mѹyf;7v9,bWJq/! 럦 @X:ENٱBU nuTq6 Mԣn@ @q [j%c{I> endobj 317 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [351.814 733.664 374.285 745.619] /A << /S /GoTo /D (subsection.2.6.1) >> >> endobj 318 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [356.378 615.889 378.849 627.844] /A << /S /GoTo /D (subsection.2.7.1) >> >> endobj 319 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [404.006 580.577 410.979 591.37] /A << /S /GoTo /D (chapter.3) >> >> endobj 320 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [436.938 279.979 459.409 291.934] /A << /S /GoTo /D (section*.31) >> >> endobj 321 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [467.905 155.173 490.376 167.128] /A << /S /GoTo /D (section*.23) >> >> endobj 325 0 obj << /D [323 0 R /XYZ 69.866 808.885 null] >> endobj 86 0 obj << /D [323 0 R /XYZ 70.866 720.953 null] >> endobj 326 0 obj << /D [323 0 R /XYZ 70.866 548.924 null] >> endobj 90 0 obj << /D [323 0 R /XYZ 70.866 451.012 null] >> endobj 94 0 obj << /D [323 0 R /XYZ 70.866 385.596 null] >> endobj 327 0 obj << /D [323 0 R /XYZ 70.866 142.462 null] >> endobj 322 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F21 168 0 R /F20 166 0 R /F28 196 0 R >> /ProcSet [ /PDF /Text ] >> endobj 334 0 obj << /Length 1903 /Filter /FlateDecode >> stream xڵXIFWpTe&W#Wv#<>okɇrwyig9a$q| C>H0%\eKa9M;s<]y׼h(M8dn1ZC(wR }8'pג KLXǚv{n RQ~'Ȯ40 7F><1No=w3-Ǵ9F~ś'f0F04_/t,BO"D<S-*xADAIe{ʒvLǴg0-$ԫJx&LLs0P9lӟN)P#vɐe՞GSL Z@䵀BE s/șk^J*+5O> endobj 329 0 obj << /Type /XObject /Subtype /Image /Width 782 /Height 524 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 336 0 R /Length 85838 /Filter /FlateDecode >> stream xxFƕ}IrI.׿R!  t$$B:$J(PC/4ccNq{ ZV]!Zxv4ш:͖M-0 Uzի^W "~8u+v|^*.UfU%^*YՖ+dϫTyjVUץr$J'Sf.. {KE&vJ".դ6+Y N6-;q>IiٙRYYP(eʦDYʡDP>rڕ׮vv(CkWAh˨QA1[E0*f*eQRF%ʡ2F*rJ* ȪJ^UjdTMvuV]U׮v5ЪPCjh#52jdDoM[3fBcǐ(MzMBIjYZ&ݤ. &(iI6V2Yq dK}-븉{;.-LlAj/K҅^J#|d ]M*xDWrIJJ$[*#IgvZisQLK1a3EMi*i:4IndZBI4\nueZcv2`@~DDC>Z %fdEfqhs^Y>oVFZfz*$#E,, RJxHJxHIJ`hRRK\RRK5=e`I"RjRw#Eef#DLQI,R$UHI&J`R 1D1Id0IERRIy H"rze61&Gy愹[&+>QLzĄ4{II%XӸY)OY׬’8^Rn_C&AjROԢt#~@aGR/L E4lPS]ROcT-X%M+bp`m.RF<$qFN3&>1(aRz{ڤ xXWiI土sݣVn,4PeWv0&#Ѿ-SGL6@8U)I h@eZZ?azJϷY,RMR"5qI1;)(Rs H )kkhNFQHԎIhPDI((-(OV xf`I.7.1'KqС/cLLyK/6xjbl dىf*)YpAqq1c*.*Zd1R"O2|zv*vJHEq߭^v q84RNG@LF6UIp[p?ߋI`@P_ZJ )AJ1%\Ϟ>)%8de}>eصscR"qPLJ^~`j2=G7?o*%E73--_/3B"|{pOyRZsT|N8>3T/)kbG~qqvyʴwO^}}h+tl1429z; x: DRr} ۳)*1F!%؀8>%?藙9x@Ғ˗}:rb2i?K|Iteru3C/N "hTXPÖC f͘A(77{>pS }l$ hRRH"0f%qSM1~ؼ雯'Dp޼VP,5d@9uDh1L4acFDcbYvF[Vx n]m-HGٙ7qs;eɉql:q,1>0V(gO Z❫DV>,_%bHI$%))Or -͍fvY֬^71 ᐲ0Raaܿ%%%%}2l(FI+^BCoݺk*&ǾߴҢ|U/1RAs;%7. #)E&2.:j_/3'/uSeO\<'Щan8)Z|T.K|sūJ*Z5g~j%:hCRFCj \ؠ7'$.Q)bbYY~}hHHfF:CSXr! lh:.aqn˺ sÇ C>tHaag4 L&|~_b5fr |ꓴ)PryQ# f8* j'jo5=ٴ(He8zlɢPASb'=mP qKΝV5ب-7;&!>)׬6:"T R q=AGA0"A RZ[zDE*R\T'B"DJ=wN5X<,u /񒂹L$x*48hׯOOK++-p3%>5 FքHH$H#`J}b@tDhKKKlTH Kp"}+??/-98b[7" $Rv|cΟ]tʹc/]!&"n45 tWdXM#A t$NظcyI61iP5̀7ӉGGENr}:h73&4}}alhLaK:-V~uIPpf$'K]+QQ!)1|RR/0{yE糙gS?M,Idnf6l`$` ̥ߗS{1#Qg͘Q+E7iJIOeNq-a1@de'2pO"XyO0/_|֮Gz:Io}]mkW}ܽ\x8@?CЛ6<6׽3z4/51L )&#tDzwY8hnn&ߗmnn"ٖG;=d_[@*6StP*7+%̈́g}ZZNJȱH0;ZzyGCNbz[ZR&iVVf`EZ[qjL@#d244 H*z#=I€0)AW:\]tyOy^V{"4LzSsٍ?~}f(=EX̥K]nbR64uN g:` :RTj51OU^/2dޜY?7Ο('9&#&+RWeFIĀ HypsXV)RClsO<~ {[o\Ue]K018I)2mhRDRvO:lPVC8r]QfƵ̢&bV`q4&+2 )7u~HH1"=eFR6sה4O1VL;nOV_R5$>%Dxyb7|$ag Ɖ%u/uLJ\+I8a'.A(>e*q*͋jqDh>-GG<RHZH1&iV$NIJIWϤq1^R r,DJ(JBSBi< T%>7DH CG1u! )5"u#u7G2{CJ*^u ыwǢߌD-ŭJ][`o֊ߪ--wGuyV][po +Y\*e8B@ +B)_ Wx٪VDF4~+R$¨#qV\HAGhsWZL[Z%yӖ]Rg=mKj琶Exam+ig ]I;SJ*.-]LHTw@>;%Ro< XZ^"^TK⾐"KJd`JWQST'$0CQʘ1+KJś-H4"hTZt*c$_Qk#`n`^L%]dK(]}4Rګe)kߒqI5X*KxI2N!DRvFUuS( |7;ё@n$TIL)I7d`);_`p-[Ep 8w 8AJlRO}{>IMj$8i/.sZ&խO*3yɕ*""pFHE:zGrdR3RSb9'~!^:H),3=WݪL&\VTމ/*^p [ply WP:!+s‹ /(n*כMN/"3W\j"t@^VZ!4X:; kb*R/GW{1Mvv lӰ&cѯ|lYS\O;[عLclas'$%$ִmuuu _S]xeȇW(^Rzx)ME^AUIjYElydFeSSׂv5OO(0 D$m}!.24<ɽԟ/xH*b_^SXHDɉ qՕׄ;OF*Q1!^Q$~RrL%gT61evUȤiCԤI18)'5I%Dֹ+{p8L !,E*qQ".B# GΛ2U2"(,$ UQ2=|_]`iI w}MMOKNdw~锄XޣzIR e$N-NI%|MP!LpNTg$~2UW@NGDXBX"EJHՈD|@6#o$--rEe܌`L7%F#{̝`s,^0 I $<,(%o.A?~&L]HMꂓCu*2|R9Ll4 Wݫ H,BJH9EGnzHJf(9456 Gu2˖UWUUVV,[xтyYx"g!G!6ΛJx)1|dsx?;Z'ȵZ21MB ݰv͈тsnhk$ew"툻 :)*sOꓪ$I#tsf3;o;|Y4 JV&)Suta7ofڱ-T5!%I qlMwEcq6uuSc!J A{ϊeK%tV}~_E6 >(^@U = p% H2+lA$1F $TM!Zad!6"3 cE=ܓǏ1EhɢΞəR8)a;By^d1]YVr:5rizpDXhfzZ\LWvMe=rcAt3Q)fgCJu55ŅtI^tsS?oά˗KZ:yR"G!6rEpx9%uRZgaA@q358pч(*QDN_|f~= <&7(O Ey)Z$+=,8|f~C썏+⏉߿FM8yD EՊb#QRȿÍN74(WQRzd⤨Bnq!Æ cɔޘtH3hĖҎQzSQpvߺy҃>ʗ6Ygiv̊F֖@ K2bF6v%uP=Sx2 Y8on]K\3j!mJZQ/dpd+׍ZqnU아Z+҉;kbxmGtzq XA>7(! htkC!G EYd1aꀳE.ل(_8{. cVW_|@$ N>=48<)(7C+ƍMJkVN<߷gڏ d 6вŋM?|6H}'@ MJ$xZW$IBwNJ%owIMuuJFZ2G(55UvL:l5eh'uuuLxYpTMuեM/T*)LQLJhsbg{5E*!@5rNN' 2)M`\RЂF LriaqU}!e}˖xBպ}+4p8-1p$!%dp\]tDx6mG aH ?xzOp]# 4:ڜ5>}0D1h$;Lˉg[8'}}y%+#SvJ Bwpe'hp6wYl4z%C¢s"Y lhAt$6FG MD t]-=بk;͓KK9JLu ֖"NxZ[[ jmUtCJ)A4nSBO˦ȓIp 3ں/LV~iM%%h.NIJdP˷M Sd~#郾}2EJN:h}Jb<|@>/.x?=ܤ̳50Z?4) >}l%KGwt3F'uI+E i6dPb|,Yɋ wr I!1&AΗ@#)q־p!7Ύˑ @*n}SI΅3^{ox~p/  |d/$F?& D*7;H{_PP2`9D%)Z<%3$5TT2̔^zjPvs{AL@ '>&#,8h2mʸ( :َ5'Byvir"u&)9vLT!:8o,b>_DG eI&pc<|LτnumJ]-"] $Bjvw.<~#x'  otjXhHNN6 91TDNfO@epD ҈C*18b}nD@kW.!o_d“__Yz%ڍW1&%|tDA'O Mp"͟3ϕgMQ"}:<:+'Ԅ=mSⶢgO>d2p>i'g'ׇp!,psa%1=JKN -7/O璹 ЎӾصGIJA) rRBx(| fIM0}%ohp 9 @#YaAb|VjAۤxbr[~I4ixZ$ ? e(r8SR;=S,{ 1ShܩDǎ"J"TǎeH1~&ƠFў3) 4{I٦K~ýp\%_jfY3&|Ƕ_}JíD#F*>U\42FWqjFY[ԗgV"З]]>$ xI m ̜jnIePaPOJcpE LwgGjO3o,jbrb'Mq01FٵTe㆝?n01 IbBn\ukS-bN8Xu4wjnmy>f܊/m"8MǒO;;{/!Mס9MNyn\E]>xnLǃGao<\ x#&2Դjr*nް(%)a=8&2V9uW`ЂmίvB\웯 GJ]|q\Azp#ƃ ?Y)(fT]6{ J-[iǏAHP Q99sWN*4QTgN@9~>;9d &ǯ-6(6s)=G{ 'A'-uv(?Qda@/w7GB]6,$ʼn?h?@|)1%- Vt=uc5]+K.')r${ҋt MOIF}Y_%.MO[.hW_45JO{x m.lG55`ϩΈ)*/FJ2㾣&%6&Պ x,Ev]?lބHIUb}WY㕖Ȇ"^RZ0w_ [)vyJGB)-_ %,@֖d Q )9$fXB 3,*&*2m G+p:y\N?n蠁P쳻:??f!;)?z:09dp8gd"ΟK0ip$1!>ȥysf-}|⸱3af7O(59q,_~>)XR" x"nwH y-+:2 H ׷OLt$w,MB4t y*;3#"4=ڹ#B%M)q:?7StmMiOrs*elx 1 \wq1hHP}'s|\ . 79V3] J:̞9{/D2PJX& LwЊ%}U|ꕺZuu!!Sc#T|_orv+K :@dZrdys.ï5Wx^ &.uI IPe w,+1Q̑^ tGsG썍?:sk8nů/I?}R圬L,1NN2˃[pwrl4VsDFb0pƐm#4J܅}4h=fՊ$54̧2ޱ}&$&Mo!cpw+W~-N~O*b>4ݼ+2n#4&CM|$2PKtWUۦTڛuNg+;SYk=\ BgeƳy/z JhQMHR4z-jdh"ň8]Κ#B>dsxjNk_is!+v^уԐSbI/)RSw}ra˗ݾu-,ٝ'U?" s JT nn`N*rx"#"HS\14umHW,[jv4N2CcnNM*];ݱ07ӏ͌"t> $3%rGO1Jg|Z% t6JG,(qb!@v4E" 8:R)+'H3|CP*Ŀ54B\E^4$^RSN,|$AQ"PJ1PTJE4F,QL7TND*U^4hM Q(CJ:ow{sǪ#O%精ٓǡsN@!gB^龍4O** TȪs|y 7{Q( ꕢB4a`'Ã:aH@կ8ПS\;!]3*שv}Vn R| >Q5RJ,ſ8ݙ_DgH٘૧u;jk_ Wp"hv@GSp"n!eLB) eR*I28dD MDϔ@ vcuзc+/'+J}o(nMJ2 d~~|01c陼p ib]W RT..&UUT=oj3W9rn̔ MUj*BNijKIISvpl`'FJxY qq1Q 6ydL-s /1.ݵCn.n!R{TxXBlڕ(QTx8z^c'KXDU#N@Je BJ_u/kpk1~44c5!3kHS2eXcJƫ"|O0~j# 흖RroKjb{>o4rgNL[\_"O}0eu 21;**lMqqq6wKɉ1xᢕ߮0 &  }ŋ-]#'EQ:̖tjDJt'H7=>lͩ^VB>itD5]:_76rr$c$YOgZ?neqKkRQi,bogQA3=})Rb5",L=bepDQg0RdW,\oZ7-YqR%.0(44,]#09yo/ih8d_UuGZZ=~jjzfdÉv1UZJUVRTp0ecC#d;STKRx9#dʰL )ʅ)E9SC(UEQUCQC&J bG.|}}u׀bvL͔*H Tq M^BQ穬OP%PPd 5#nnH%EW2*"}SNBp_x⽭ٯR-{~㯈R$2'z¢Ҳ (l1eMY}3~'RtŧѣUUVE.sIMNQnf>AuM5D u -Z[5BAaѻw4oeE%JIS|UǏtDP6foft ḱXU; twkfLZ$)q`I  w]Q>WY\8JKI׮^|ϑ0[tΟ?}  HXbZ?U1R3R2;q߷"$%gN#IwMݵϝ9 |}.::;X͛6=|իST-DⲥK}}ԣ2Ơ KOKtǞ)CJTD͸KdFG>UcWm_v@C"gKٳ?lnIp[ȑoO CÉOlma_eɒ]`CHSMMTU ,RT|kDFFFAARRJ>)R3֌93rsrhllRۉpRke˖ .GD\[[N`=sЍ#PƎKLId8E;SJOIJIWepTN[*$|6z:8W666:8OwśQy|W3{o$H q"##ݲ].ޡBL2ee.^e>Y7K^Q jT_|@F.@7$%|ۤ赕k*֔/ȸnAS/"d9ة$`TzưgNY޺is254s᜾K._b~hR`[?9FZNvCJcR9sgqcn|E4LMA;/ ꒾^RR"RcW vvqjLt?u˗K֯ A?nۊnwϞUo|;&( RQ^ %KMC%lLZ+3O7'O6=yb6o _~nk{pIuڴӧiifkiμ<`&B##}*,PQee4)pb *:$vLڑ1+Y"eFZeRG҆P8F2.ҟ^қ/S9yJU;+++''è3E4."""ӶxWafJEPiIP_קOW.NG4(UOAJ|;wĭ)//GdkmmÝݵk!pWzsR&N3YɠMM  IIsj/7eL"$4䢆Q;^KiF Ns0Y{䤢dlJpvs,)UkRm-0A2^ILx/R|?*aC>ׯ_H~f$$ {{{{yyE|CaR4a \RÒ&HUbkMF9&u'&)GMI﹮ fF~<8MssO19B_ ud?'zՋz7 M M R|<ᾢ{),`I$)1-'mET[Rw U<]\jkkTPhKINѺTbTiɉ\ -oEG]H̩SsE+Wzn۶꙼&aI߼ݤ *z YBSf>;9Dl?Ii*#ci[Y[ɓϜ9ޛ0aw}WWfMYMuirRrr¨TɛTk+ ?lYsV&L+ftADQV݂$ڍK)*P9)RRR BBBPIRFWHJOaR(/p:(RR(aR:uEk׮ٳYFOL/2e㛛q7n܈xy4jhƌ %d[/펗_~Y1)9 TX$$ِcffۋvT?wŖ? )p]~NZ f9qmjv} G#s{f6Sp y¸̽{=6868.C1ȴˆ{P%`_}g!VF9|nl^ %lAȵӭ8}h/+=S'ͯ1emanz횅9MQa!ϙ_72n]7fr3b,J$Ơ$Fw` mę3Uٶ6/&禪ৣPLWrBmK{I=hm{Z-,n[%K>RKϟGٕo߾¹r_#&J$d9.(,F^<شɓ_}E[oѓH/ɻj)CcRTqXEm\bS"[v6)ɿd[7!y EJ01(d)b( ??CJ(EqqqRJMMCJ H՘>&dS%ܳgO)^z۶>Ǥ[vUYYe˖w… ֭4i߯_V~./'+<,o]Uo4l0Ջ%¦T}6^9:؃j%nUold%ꄟR5qbn_[$ _C3~O™芍Gou/~/,H^h8ܘ RHA7FQL}EC${_ )ݘl2)n}ki/-oXݳ3nBH)",X'CgN:{FW勺$X%R%1)ĺA&Lˋѩ#vᮝb Sx;8:3꺍?CJF]hY?-Xq1Q"ٴwOs$I$Y1@)=7(1$0 6MMzE~R[ mm4:A!*+XSslcIb8.FJKINNJ#lcF#Xs޼o`;EvLk:-& `sss )edd`"K H':∈be3O)Us^.\R>Uyj0&'&T%*<%3H`׮]cnw1)mڴqǎ3g$Xm۶S^~?Ư@)1LJOϦ /($DyJ+'Z`&1|9ڋ&~&9cz*U2ٟ`OM[0)o!RFPfDinHVqiI')_]=9_Y4AJ.!{]bAm)JO/--6I1UlLo{zGg֍7uG%N١t@b֭[hmmm8}}{x@$Bc|=%ב%8Co}c?J9ѣ??yo^SܶmmgN?~l|r]Uyy"H),1aZzw@uRl#xylEIFP$$J`&4z Bd^7p8@bb"(CRaL>E/5~^v2MJt^Re's.P)xR~N+;w$ؾ]瘔>j*bS *oMMw%6k"p7t|N(*WOTXWMRڵc;w@G;wl߳'sss{۶mSԧZ IKJw_h5jRSɓAJmhk9a2I nl45ZUrRت}Du.u萪D X ŤW @BܩB1a88y_IZ)1B~*==Jig+퇷цr}*UC3R;xJJb]ͿWw:?[/ 5aqq+FɌozzzN:O?"5nN<~vqaA$9 D2(1T[[2U.&K:jrvZHRQN:&EhÏ )[ `mƌ1c(XN?ܢM)Ai"lB,| okL]'Jq}x1% ۔T}M*IY@mR216V޳DzӘ{έ?|A~.^ֺ቟@8]]~umV2Xæ.^f"qѦPE? 9tO---xvp6*~,:KƒhIdbB^iR 6ﵶ:zU;?kQoA͙uUBPhoȸTYInla)&rpIJuH4BjMbHiyJE Y$`RdŀH^ M*[ӣoE=|pyy9=??X(QJ#Lh.mnDێ*.v4ګhu:0)/]tÆ Ym L{ܹb^LRu._ɓR@bH)4 ]TsVxhQBJd JĚφH]*#Gn't/<~N[T/w`I qJH)$-919!{\ 'G;fAɓ't=WZFZiՒ݅vhzI B_/'8蓡._n͚U+W^8͛W^_8C1#G { u*GiPH@J^ݳ&%qN&XcfcR=$E}bcX&DEGE@1Ib"7W7016` }sСsΝ>}ԩSǏ?zÇ<8iwzowۧ) ߣ($%3]#DO7&ciy龽{uun^,k'_rMnb {XbA!" b`bWz}wM{;sa2ήg3ϙS>SvؕK"l2gщתU_?H@2[7եP8EEA7rNd-\]4mlY;}v+Ozy\QD rݪ9ǻU#zvzV99_Ν~_y|)egO^ Hʿɑ2(P?hDJq/)"J0i#Al% vQBM˟:*+++))~:o߾#7U)=K^Kk ^b:@o&r[-:y"C"?&ܻ*S$޳n$"ETbQ(g_D rJ/O"6țWʉ X HXJ:7psWorY\g;- nf. ^w+8y¼ 8TIZ (=5VLdxXH&fWg?GFapJJPiL\XE@/&O[iI7Ɂw*G %D3K?UZgaPnO܋E@a uXHwnfIRaB1Hn ,¼iJvSdEPnO܋E@KXS {R#LJX!pƵ‚Ę(-_rvY)nI܋E@ ,2_v()8RP~ۿ'9 @F?qb ދpCXXTVD=}[=XXXXXXXX&FJdtՃR .>{JU ]-ȋ =3 RPC30 =MdV_!6U`>Aឃ%rRZG>{7\S {?~{l i½,,,QR`#cPQ~n~n7o~{-6UXv$'O9~)3~A~Og9qҴ܋DsGv<$b鋔1I ?t3y$|Z^So߾ N,=CIzq51 ,ǣP#˴I)ࡺn/kk0$;9η=SF^k$5,#`.#5hO<%'BgJٵp<q˟҂=}r2@@d*Dd0cIJWX9+|vy&А`ٌy⅍5\ҩE1Qڵ|KmX؃ܜl;:3 09`N{& U\T MKq[2bP4V%%]w-{xTW,څGԷo_m$ZfNzϒIʷuΐGè9q0IOIӳGnVG`&hռY>a!cqG>=m*' .nV͚!UVyY 4yKAC-{(kdDh0YG\G*e9Y*&6{WyW yt޽`սۙS'B9kw.a!8Xhc޽}  1cl2sicm`Τc33thg;LR7z0 RbM9+n\{ׯ_ݳ>&ɤR8M?pX^^S>mZOJ `Xе F(7&neK]h5s];[i', V[X/_Yf];CO REWs3$OC8C< iW/#mZ_.ˁ]1u2jnlivs)h'5dU.UL͛=ȁ}P t=wϤȋ6HOGF"&XYԭqTRxKHV($P/=jčͨȈʙ4!%EtP33ff;޾} ꈔ޹s=w BJ0`!ʀӊwTY%)1Ō=cGAiI9 |Oi; )0 AJMNʑE{e*ʩ^2o^))H~=TYYƈ|$F5:aC酯yAQA~v&y뒊սų~{t tMG!ED .>}4,)b-WLNaZf~J Nr\I Q8!6~,dsY Ղ<ʹ˳(?߳sH$` U}٨XYACɊm.mif_"׸ҞdAGf{,s9|  k:N3Kगϟ?vɓ*kݧ+]&YJAU ?@2;C50*6XfKQ٘ꤲ5x4]<2 Nݬ:߽]LaC:Fq K3xRף<|d<*zAƣȈ:ZXDEFz#(0fg$Θ:92煎ڠH{HcG`eJ?y<܏܏b>i+ܬGwu fe),8}&"1'!<}䵫V R$ 4~]:uDpq!IMШr₶͒I )M?z*G\\b$ bCl|8%US=~pI>`zDa8;'+QrL!zźk`ۼqëϕhBJ4}4 )RpԺe @hV8@0`kڨ8=itc|oޤ1z2k'\>/5ozLսIn]ֆ>t`'Lr4i Ô2 KHe%Nd - ffUNM]@WE:ּeV18"Zvۡ]GIZwx5gfZOHBm@/ӳ\HJ[7o޽kׯvxznۦ#R̄KNVpɓ~{ `B \1IIl*IIO&}z:c(pv_^!^G=')B+eZƌ pvM]Obid1II_,-8<ЛK?8h@m71¥v(pq )$P³ :60قR~N JM.mK"UO IJaok܃,;-ĜaNf E~eڔɿ^ #G=%>oNO>ÿkGi7>յo_tthשElL~-4(৾}r̨iɉ( (&쎞ZSoMwxxmҰ֥`i'*ѧ'%{JʏL/_u6bRGw;uGJ߿ot?ze`ߓ> {Nu gaF:$^cMw=Jw$hv26RzoX&CJzFG\Kq,v`ەKN:̢ukVi\ܱ./V4"=co:Q#%GQ(P5)pLi3Ϟ>UTX| ,;uQW o_%H큘1KK3%&Jk$b雔ƄX&iIΞcbX`Z 'C*IN*~ׯ /)̿zUZ| ͔^$cM=JQ#K? .*1iXΐ&FM`ئ./K&KKɖI,0?+"QoQ""X$RC7qw cPe,,,- IJ Qhc8f$G 5T(  RpCXXXXXXXXRLJXXXXXXXXI CVEJW HU/>Rم r r)dU(;+?Y/W^v(?+#O,LZRPN eJٲ IӳhV!IZYLtاJO!%SJ&˕$'IS@Ċ@J$9ABJ P(>OOKKO}bNʕ p|L ԸhNERb@,ȤĘh2@A Q $FA8Tdx|$ {J!EP plX0pUCbp` HƄF` r|% _E +A#/EPK.R1b8 rA]>/9F Kb XPŨP9*2p1I#@ɤbnA$5ODMJ0;9(|B#7r xGrJ$'ƊIϢaKή4[1R"ajNF\ 69uWL0GNN%MB>EiZk5Ce0LEaLJXX0) ox7&)= y6-V)DZs`zE9+v׿K/[5jۢ$.8D[Xxaif!y>1a@eceGlfC#4SQ&%LJxtAJӷ\DJAFL|uU{.0g2a^pdʢ;ϢɞD4wlK>} <Ţ#}DEE:CY*#T %?߱ 7;:,"#*6IazSh&% o@JIw+ݨI/J^:o!syTD!q!$\]`Ҵa2JeE׃4VYF(LJ7ښo3:.:}u3$i 3<U)H8^%`$*IKS=\ D_$#YFƌ.aFM}YrB\ANq*.f;_^VFblBysg`7R'XjVMKJp]h5,7H>'Rrօ[CNpxb:ys3k[[t DvH>G\Hg?ȵa4|OJ,iz؟@X:ac|RزYӜ 'öl\y ӵLY@k-6óN½w;s$= ";EB3O[CB$ͼEsvPyf%!&J_~n,["32SиAwZ6hnnN%+AW.)ݽە T -\{mZܿgWvm}wF۴HTrP(WeP]^Pfm2l oxތ}NOztٵr:٩+*l:[͛5緟nݼ# v%p8]䒜&+`hA* |\OpFzbgӷȰ>={RK@Q^E<};k}ްB2(U|bl \Lנfy,s̀þzԑotDotӠոA-%%?;@Ƹ1aY ѣrFXMZvXt-?~$5Y]pik\ 2&+Τm\qё"guVGv%[0-[()Ui-) ys}FO߀xLJը )k.IJ}VTUe27v {ђҳЇE8b!';0=ޥ)X`!ս۹Ӿ !62c_K qQ>}C,0~O[4C_\,[8{Rw_b ݻ=}юحe9*AاoׯSx{5b_ePSݷ͛7'H!_ &L7|LMM5bcccffVv޽{řNۣG?Z^l)?(G1,%u.{l~y ˓-rͥYCBE=;IҘY~o\+īHK&ۭY2c%SEweJ3:S{ĸB\,dX2e١}jR`a,yW^BzuMJSD%Z.Y,*ʔgo wun׮]Z4iyF6m@ƍlrX\sR:XRWEH`@- %']b҄&q%C\(IMvw]ھm"ݘ S۫B.cGzvVIeq2)͜6mKLi)*G{ U&>s+dn{9{~@N 6jU6ʰ,Nɩ|Oj^&菪,fm۶]zu޽ulV\\W_qz~͚5^zNNNQQ47R 3/|&ӻիWwڕ)k(ֳkީ#%o(vR9k4 ua>>&jfѦ50:@:s|c>&ZRKm[ul͵e #Ҿz^pN92&It qÇ6o hC{t뻰&;|ngin)Dnݴ!g? 88Fl.aY,Sͽ#vR)i2_v JR|q9rE/'N'l1 60#W Եa1茥CgMFZ:u SwHdGJ "1=hӔ87o޴^b%|MXXd24~-СC5jԀ;CY~L5:}p"%p 1C k,)R^"%*Hh2쭤IJ ׹Æ [~=+]s4iRpp8s>O>Yr3kvM f&C#GQД\7ā_~x\cf:>sk7IJJO.=5rs8؋VsH0&%.W Rc#kR2)^<;סN" bR¤I HHI"N D.eRoxÛ6&,}n6̂|PVF~Vfer3eyLiN47$G liziYP*(3=5+=%3=%#-%3-dYZ,5I$%$'J$$P|:(1>=!.-1.-!)5>&5!)qhP$(HQ "IGG   } CA!QrGG]!x%"rd刀aaîB a}·: :Qd78z1.$ ?EBБ"{JE ';!C@?^Juذ=ٙKS}Ton?9aPAjD`d'ZH>a0! SOxdrS{rP΀ѝ = t$j6 4"}z,r!ɕQSJ^ȩ&g#e)jvi WT%)-]Hk}SJaaaDY0 Ra~1RQuRׯnJ^'uzuP PɭRo")$u[enߺ}twHݻ z>CГG>~DqJO<#Yʟ?+Aً^>NW/@_|o^oH޾y--߾wwH?A Dm?}d}(UlT?:Fu$Oߡ勉UoPo|7s+i_> W/^@''9t{?{T994 4jqD+4HQFCj`) %[0+#>`*T9EPs9i\/FsL&hVAM29Df$`B HPNJM!Rr[4z_|aV6.5jzpNk֬9guTVIwDCGi|)!)?ee2 ΗR R>!RZ=nɓO>7fY3W>v5 l'SCU?֦JDٌH)$`z皅kFUhm%Zl-xY[g,?77z*Iie W++h駟ґ 煎׃@fM;-@B &%LJ %}/ E;5=Ȅ]3m9!K%%WTҚ:u̝5A@*6eS/ѡRc_ lI"0)aR¤II RڱWR_}~d®wv%ZR}vRBmlǏ333^zW.̛E wU)p@~5kD C)aR¤II;R>OUIH+ڱ]ӛL5nc"/)cyOIC/&)yؐ,,}C I &%#%I {II`R!%N_C#VZz?:9:7aR¤I RھGҜxVg&vʹ۲H,Q BIqOiNJ<+ھ礞]ӛL5nX._8{Ç&%cnLJ0)aRHJ&1"%^u>]ӧL5naXLAIH雱&%LJ0)q"MH ħm}>e®wv% t왷oߜ?ḨHeR Y`/Z9I &%-HiczWRS]'>e®w=&&y:yX' >ys jIIǍ7{"6qLJ0)aRҊMIIYS&i{H,ai{!`^+}{|_߷{9YП\ I("1)aR¤dvF#$~RuT@)G%[v5Ȅ]3m6l?(K g׋ W ˟>xp GSI &%LJ)mqD(Oj^tMT2aLۻ&(@༻d;^O0[&%LJ0)q'BI6*Yn'p5QɄ]3mo/KHC1HI7x&%LJŴF,UڴyE.vʹ[uH,іQJ&%LJ0)q%  %6 bT! 'm<"k fޭݲO$T^VFn,K&QçH(~y&%LJAsT! '3y:g)fJ2:4Wkhv:u2,t4~<8@\Ο>=L9)tH|S~̈́"%fLJ0)*$~RZPR9wwۦ %tW%)\ TZ5ڀ+h駟ґ 煎׃@fM;-@B &%LJ [G,עYby֢_klINo4ͽUw5zE)RQ^6zQ>:T& R;fsgP`ıc0)aR¤II RXSRL/YZ6O+wi蝨/[]ouK @EH萅ɧo29)@B)6cR¤IФ$C?)-_SRF/YZv{Xg /NCD-5tmv.d镤 *.CB RJGY02夔))Q6cR¤IDD18~>r |jc?J=:rM5N<RsVmgu,)KZIJ9kH谒h6l؋fK?}äI &%.f8nW n?J)`  k❨:.*FV l BZѼэI &%LJ\HijOqJ'r &~>x'+RR$,)j^:d!ܻrR@W͘0)aR2l;IIdŨ EJb % JJ#I)/K*DBLRBYPmvR2_ĤI &%Njhn6bS˵t:j:S&މrqf10Hr3 s3!+)͔II o&ԩ3A6H'P'0H$j +('0)-)IOJ+Vas/FP5Q uiH!W]oC `jʒѤB/r!l)eI)C 8_cG}fkOk\Fn]̬GԱf͚~}YgNj՚4a|NA|ifP?[gH 2 )'?&H#I" "BI0)-)Q#II$R()UY֤OкFjAJw RʐfI$KA谒$ih6$*IiRпTIS'OO>dܘsfͨ^Fx@# l'SCU?֦J줴B#W%1yIOt Zj|YBf5j|]WU#d&LJ0)隔*Z[ Ų hBiZWrv6Q nF})R4%EB*H);)\ TZ5ڀ+h駟ґ 煎׃@fM;-@BhLrSrX)m CP%yQ:BԄ)aR¤RZER@'(yJ-]M?WN =P;m@`)eIR@$tBJjHiJP:uΚ @ O2y)m J`ٱ/Zu J,T #!|ZgRZE" 㶆D/j|Kߛ&q[=~YƠa# >^אC}WbRbJb/CAD>II8zwߞ1 tEMj_M>U=/&%’kP "r%]xm,$8jMJÒ)eJA$:d'9)Aϸd;~YջZua,ReZO Y&J8/ %tO LE63IiA8bbKz"=ӷH"<y[Roj#t V -[4[}]TNh5ZRWǎ*]9ti/~YdF@ӑX,ޣ+tѫ{RqP RB#1Hef1K!Tz򲗠鑖xlNu;=sz vԔ$$P, Ҥ)lV%)qze~1IA.Æ `a!J{J*IiȦF5=A铔yni*n.!RzD4f5˘E-^vh Wj6)ߵ9(ѹ͚4)<z?Ű6#wǺIlP.zݹK& |O@nݭD^V48=j!͚4eD#,u P^,ߢÙ;=[uIJHKד@RxR ι %$ ~w4MU|jA1"4e4"_OZDaTOR:N>@"uO) ")Lo(!dRJ+IMVW˜E]JnӦ:R &%y`=p:l&,iqvs8w b[ :pUW~*IZbɈ#>;iER,0ScR aRsi) EKT~QpIIDJLI*"\&$H&;;CNJh6$929)$ DJzA$De3 b"ALhEG'n1 8U0) Hd:k3#EIRzF10Y6_֞ رo鎔:vOc1 8YeKVBu23ޣ% dv(Jc>5o"X>ԩDDIyF1K)|-l^쾙kQ\LqZw"x|qҵ"liUW{ھ%/KvƝ7+Tv#7KmjmBJ4UIJF*Ii0ApȎrM%[0i>Iɗzv 1QBRDR 7]r68~rRELJZ{&ЈChҨalAa1)M7T('8kb$N=MV<[U `߸a}O5䂚4l`zmȥn0rJu n*OmTW/=M\ Pxz\d N[֭ھ~kp[ҝ+ݵs&z6I)(G5)$%1 FQ\4ڏ!1 ~w2:A5YI p HRj`VìIJ/+IiE]E-Yf;ͶIh `Rڠ-0ȯJTR^{@}{BXXܼo{I zJ$\E&s~xHuȥni$rJuq.x6j^'ut8;q:rtYռ(2e;6?~p{: ~oZG9~dfA9IiRI}G`Sa`agRz@.w$"J3#]IJglcꨜ$XVm[ڼ̢=o@?KELJX;aʄn {J,OTna#^m? pؕSʷ"XxRtWSܝcS<_0))s}!)dinHIGةkR~ϱ'?y~'i]9{ wyjNJc̛=al 8v ):D7dE{PQѝOhP|yNM$_G`FMo}{J>AgBqV'}[l'عK'&%ffdꇔT,V~|E6wĪk Þ=>M&m޴1o@= P*1fȥGJ XNi' 2App4Jw"rZhUm[Z4)8rS'H4:xޏ)QGJٲtDJ!R2 fu֎ S 7VjF_?F\DǨŐ?@Q73a KQcGy-vF#$~RZdnQ`FDw֮3U* \Ǔrn$%cILJ0)aRDJΫB1iuigS*ƿv88=Us.כx>} )˖I) LSGoflƤI I) I4oJWRrp^-l]ڙ' )\<8w"S,pLMIIf3sʲ,9)c=/0]{XW5jtjefff=re5k:[vU֤ Xv"K33'G?|Omݺ?8-/?"1)aR¤dvF#1 !Hr) K49wb %N8'RG05hLJf]%$ $nKAS&M<?qcFϙ5zcG[C l'LQV]lF[C`XnVV(R(tٌI &%5A'%{"ˁ"ˢu!k싻ϝJk8Y(Tr:Ƥ$٬RZ|Zj!V)c+O?#A ׯ͚.vZ"&%LJ0)iAJszVjWݺ5]cY"v:$%AN.,YܶRjIJe줴fN:sg@a M<}Kt%A:[҅%LJ0)aRҊ Ov5,֫P)3, p7O*fw۪LÓY)َgffVzV]7biYVS&j֬8γ ;{C S¤I v4gr܅+dWO̿^5QgDw8mi[4BRRA4b\] B7>|O&%LJ&T$lGwԜ|[t4O2 "ׄW? Y S ^k׫O/jt%qD9OR *L %)otC6cR¤IɰF"H4$5k;,Z5էdfLb_Ԕ-Ք %*I)[5)aR¤I gRr5IE4ԯ;UQH&Ht5ճBaH9x[̷^rCLJ0)aRDJ3湚f:,YkꥋtQ*W9ERK9ڎ3oLE(0l1K-x&%LJ8t&xn Fzb5]kUAQSf;م 9)cy{/qPj!ɱfLJ0)R™!jP .~ѝ*2H:tE"iviR_ ̾@JpKW.*"aRi1)aR¤IICR6wIj+Bԭ|z븫ֿ%=ר;O'Z$6׎&'#&%LJ LJ(~R:$5mR[L5TGUSKRիUIJzQwtIԔ-ј$ih6$rR &%LJmgz0)-6I3B/UYxc=Yˀmh7sH,I R,0 &%LJ>FR2$ Q|>D\Iɨϗ~\3lN@$͓OJҔ١LJ0)aRJJg9v_7v@B`z "aSװmh;}HZ7)HKR2Rtxȁ*$ zI;kIJHĄW *)(:u(=FxO?ϟBQP &%LJ0)iNJf8f: RWR Ck(i#&2lM:_$̓2R夔 :sj~e9J$%\8swDPbR¤IDq1BE€UJTu * t:ajJטSlBJΟ)qʲpS (3Sǎ{wԨ~=P/_TT;bR¤I d;}I .*JU[jC8>5lӍ2O$̛nEJ%nPfB.( H Kҽ;e L}LTxm?U' %(0&%LJng4e@J-0INw(II^Þ8>nM'F)ScRh6cR,5INJIKn\zf S4qAQS|;e R}TxҭҒ~bܱe'KsN(&%LJ&$$c %GBŅwPę&ۋ89UAJEJ3U@J1*#1P)}Fpz֦U3N =4?,8`ʹ0)aR¤I+)M:$B!&Ck'Nlk7W$hIJѭJ(f !¡Br2LJ%hpxU4oATfMs[LJ0)aRDJ7IM@4$bĉM6yC"'$Х~ׯ,B (9?TrjB)*XHaÂ#UR&%LJ MJ)9&Lu4 HIjc?qͤ9"4I)-fYI׋S R$7ohDN[6khr.LJ0)aRJJ&3IDbڍĉJclgޤT@J n*H)䂢\Icw-+-ZSnܹs ĤI &%IiIjdNT=qH,ђ2URFZ GJB\qQ^ɵ)@شd(\sg^xN7/Lz3~P ILJ0)aR¤ä#R ȼ!MIDP,=#=516*96( ?~G =zz}*?qB0)aR2l;c\d3iIj<NT0S$Ԕ!)AJ4f#%Pn0j!OA!P],(=)>2$đ[7_ ;EA0)aR¤9)cd]މJ%}`a5 4K3#"1q<,\3rBkt-gk9l\Ƴ50)rӼLRyP 0P!R덽.f@pD)I䤔:ʕ ˔>wFnjYϳ]zo{C įi<3VJ@B7圅kFUhm%Zl-=~1)rRLL%Mۅʛx]lt] N2RJHJl(=)}{V7vy=Mj3 _;H)yfD䗚I0s5#*6IazSh<[H);eIN&;WicGKJ3Rs_n<;5n~o{AV0@AH3#"˲LNykVIh ӳJ6F@JYɞ&&9xFG&뒤x9)Ń%xܽW[}}=.M{+Cb ~wR̈H)8;`皅kFUhm%Zl-x=~1)2wonXϳk>~n3 _;H)yfD]PI0s5#*6IazSh<[HId7`a54gFDJ%78 yYfZ&Z5Lr*ٸgkIID7- M92RJhLJ)Ih6 `<})5ܽW;~[=>Mg! 1wR̈I &%LJVtPtX]#&:'V[|z{}Zk|i %ό0)aR¤I)-vwmT])Ui@(K'N/N4Վ-nKr/ߑ&dh"gs9:sν^ŚjmR'lhÏ$GJ uI)\J[ƛXej1a7y ÷@ 6xo31a"ғ&GHJObChQ4X, is-,EK&ׯ&w ;F"k_@ DnʤE|y%鞔&XN5afnW,7@ 6xo31a"7䰊)L"COٞ~rf6%=$74@!)I{vn;zh޼W ^vAA0x8I]Sl0>c0}w`6!"5LDRBRBRBRBR<& II"Ј #^Jv"7]@H3Q$Yg-~)z&$%)RڽcUTI R Q6fj /gc9}s!Z~YtH vlI$%1r1Gz*n?)!)y$E>"%07#%HtI͟?~[dի-,,m۶{C 6xtRH :HJFJʷ@C^qI'x/$u@)!FURH̳ruGJ[~3\bjւayv7a`6&")!)!)!)!)=veE׏(΀g'~J*"d;Q"%r"H)L3R Ԗ&Mךnb`b9d<΁lƜ1a"ғ&GHJO4@1ƂRe[)W b"U&H3)_礴rz3ۭ ]̳X79Ђ X!"5LDRBRBRBRBR& 7"0X+RZabinjv'Ίy8`6&")!)!)!)!)B}ȶj )鐔VXZZo5Ͱ<{`Ͼy-6:@HJHJHJHJHJާ=r>%ߧs@՝T!%t)-iR27dj֊awy84%`6;&R|SMHuKҵ 0=WKgZ4 WIH_*n*~)!%YGJS ˭ֻl13ڳαo^@ 6xo1a"AJQyTS&RBt--LRٸM ~ dyJn?)%%UpM *Rt$iݱ})Al2aj iEhm&GI9O5j"!.I2\-k4\ I@H)siHJT`I?a3%o~$IJN)GSʤħGb^)G\h)jFJڼd![q;晳K79Ђ [!"5L$H)<'sj DC]eZJ=עi/$%_hxz"xgwߔo h"uq͐H)4>:BeR >HRS )mlmn+G3Oaľz-k:@)r( >穦PM:%Z[Գq-dnjʓ'%7]zG(M52jR &鎔m2ټt^3냌CǘvYg.s<෴C̺M&&ţ$B5jklazRƵh!*jVo8aCOoSm)=`)[\)RZdvm{`?ʴ=Ų>{-tH  RȢ$B5jklazRƵhIJMFuy`7EۤDr)GLJQ}JpΝ{o/Xvmd:UzZ= x N"Ie&kne}ca{sSW8Ђ oM0 % Jy)TvIj)l\j ))yU"8*Њe0Չ{Z}z$&r%)eyB HȕԢ&+[maeZ`:˶́l.:@)yS|SMN&N/;bY}F&#a"A~acUUBm-6.Nt--LRٸM ~ )y:+Ph֥ԛ?)GS\TRD^})-XolK6̽'Xβ]@ 6xvb'%5 RLIMRR{s^6E("t;=rK3Nϵ;zl%c]cȆڪKҵ 0=WKgZ4 WIHYAP_ʠFAGu7Mg+;$_*呐kRuͦ+wfl9eDz9>l%b RDܓRHvyեzhNgm]^ Uy'Zݛ< ]jklazRƵh!n)P֩ie>1SLdyR71GY)[Hiے%KV^maam۶ݻw߿ZKB DjH҃dB$&퐧m4d %GJbR_~GddYٷ+<\vIj)l\j ))=p-)8Iy, "% ʊav_(= uMJ,3 fngkQShH0 %DB0ORa9OPRKSLvџw%ؖ1wi3Mަ^Do੧ʔ AT{݃{!̑^[$Xju{ѷ# 9tȣwa)ظ11\"DȗOPVX=-)b#R4;&KNo AIJ τ}RXHMRJe&s֘~{`-=^s-&t?>Fۤ@- dD UDYn/uV7wFW't:]gN N]rfBaj*wժ[+v2rzErW16f{&c)6.%9%2XAHJ.w-Ap$ Ñ%MBr)K ELl՘)9;};)ELJZxݧ䑐 #`,^c3~kQSh?HDQ"AJq 49U94jw "@FP)H>%7{DQT4qnEF~\ٿ ^>s#y׆ y4Ȟ>{uNnGdOVKz 4!HjWX Ƶ=zErW{ݾSgRR3KNJTVIPY^- 6Rr#:BkҤ0 C^$4'%%Cȝ!)[1 H)8.2LeR #3!$>R#F]R"r%E x4L$H.7=ihi4BU'JDrka{z$t'&gnaa÷gWC66zd'AzkMJK)(U k5~T]$Xj#s}J [goeE_YZ\REU~EE`#%8޿`vywv$H/5!%tIHHVj`߂@J *Dh')ꈔf.3h f_mgU8Ђ 13)ݎ}G֋h:m%FFFH8)Tz>NJ$,IT4q/zzKI`jzW*pqZRIRc=eC*~M:&%קz}]id2/_|^L_z;W7`-vrEJ A*Tk3 W,@JBVV\TRBRFJ%*/)joku?ۍBP3{7vTԯ3;k/%ƄRh0HMRJjRYM?06w?g-<^q-'&WãG}(>q)>HhBI'~a=Dks8E~}ZSnOx>-3Tٗc&\t8傅x%;D9~wo^ׇʲ=BM{7OJ AT˙3?-[8=YSyg^6lYl1O8$խ ATSOI{}]?bH 8KTV\2I$n_fDUsmR KJARզs-̾v7Y @ 6x:LF'䥰)MbM&hR{@&TOȨ?]Ϭ x>FZ.]/t{v{u0.I2\-U^{[$m%ŅEyE: %W7 Q>"t@qBol3.,T Q&IRd1TyCXde= R UB ).QȨPMKEGBE\82p |TmԨM!F{Q}ը]eZJ=|.(Cv"wKbRj))*(ZjkKTZT20II$IF'cc?#)$I4o]d VR|Rt).),5y Ϸ1i?#e'9Ђ @0<FIp B5"yEw SX/_;"~ vz},1rFcMkVvIj)L)xDžK#OK&s$[[[ s uFJVhii~šEx^rܜM~J`O]]]r{?ZYQBԉ:%%bJD|(4---7oc& I"z NɱtJJ-`IJ.n6^r D\qS)뗶h=R|ntH)$%qYj2sGfnc|~֯GKNr<1a3A2)TvIj)2TTW )))w_MYTI')).v5)KT\40HImEt&c̔0ܦ!#9)]E+)ņLJZǑGJ uI)PJGD7@F5j"!.I2\-XR}]݆ukܛ-nllz$n0_7RjXF$7%V[d1܈I69Sh%bW)ƆLJ!ZHRHiL?\o6o+]~9^lρl# :@'1_JTP$]` szbponj,LJ{-w^vTdUn\HJ9ٶllXMЛHX\X1c'%1$%sTHƓ%=+74_LtүKJQȨPM:%Z[ԳxtfoU-R;::ǏتHJ۴Ԝ;٩Rjj4nRRHRtvNEu!b$myrDR)MYb2}fleg}s<Ђ1S'%j DC]eZJ=k 8756egAJD,Qa^*7&%g6N]`J} Ϭ1a<R" X ⥆o=I).'R\Id*֛}.wX=^hρlb RD7%j DC]eZJ=k $CJTI.$-W,w|:,`#0949E_&JJt?)'pU&%$#^H)8&)s%E x4K|tÛ@F5j"!.I2\-X rx !e$^Ɔz#%0$,DzM*WM*Yo*b|RUݧ$* rR@J0I),@ sJqQ}RDx4J$>!yRȨPM:%Z[Գˀ#>ssy(UT`6&tzE2ڍIHDb%JJĦ R@ nXʤLǟ)舔ZbYez[b~a{`X!F"AJ<( dTS&RBt--LRYeH.?RE9QsN@0I5rk )votz[b~aoh߳CF"AJ]PȨPM:%Z[ԳR]m^vVNf:Jgg>)iHSv*~KX_@>iXzw_«o,PȨPM:%Z[Գˀ#~}]mnvVvf:JU<)i $IIw;,E SI9͉V=!)gG+)Ja"AJ۝\( dTS&RBt--LRYeH&&Wq`?/_w NU; ˄3dR"& *RRԐaU%-.;2)$%q&-1tz2b~ahgZ!"5L$Hi2)TvIj)2Rjb|thpfJrnV\谠AEJuJJZ1i0U1:LRlIѵD)DŽJJAZyN)#Rd*>d}@ 6x?Â1)mDIp B5jklazRZ,u59YTYEg($BYi&7ݸ_><,7+VII$EJvG ܘhŔ^xuKJ<]҄%&o2lVǻ0g{`-3?)HҦ;2)TvIj)2_[]D$nrL"arӕkޜJ93oÇ0EuErN4u%a9UFJ:$^Mj}*,#Qz&K֩)m}@F5j"!.I2\-XkxiɲHNR Lnr>a7YRٳ>8{x %5FQ]y‚O7z}²3jEJ$ٮ@zcOIX0II N Ii [w) dTS&RBt--LRYeHJKOLI*wRU&Ľ=i;p{{/? O{E ƞ?֤ƳeO0yxp!A~FBqQrKe ~67OOJNO}$u5&t*E2vdRnO6:"LGݔTHJ:6~[L9>ʘ>ַ@ 6x?͂1)߼CIp B5jklazRZ,BAVZJFrJ T$XܺyF -mconX}L(2Hfa,A''m0_C؜S~ 1Q TQ^r{[6+DHI[烊W)ye %TڛT'w(QR3%E#ֺ-)suNJ%&U7{+c.XfdρlcAVRR# 5oQȨPM:%Z[Գ !+IInc8nZb<Ђۓ'.0d"HʐrF;:=b"`x'`L:l{(?'kU%?AR&큕DeJnr>),yMG$uG\l|.rJ.{S}5ή[b2y;ʘ>ׇ9Ђ kAqIIG?#~%j DC]eZJ=k 8q?#5)-)^VtUן޼Ɲ/p1<%! h&?Rc&49Bn1^̜3[BܘԤ*A\R{ёtLEth ݉<LOIIKտd/2*Y4򟓔7  )!)!))+3Re%P Ln*ơHh])`@ 611tHrt"{[p%xyB {ℐࠊEa)IB~?gR:oNEu׳&$%K3" ɗzQ"=915!NVMWQ㹤g߶lظ|N`v:$7"b횑ÇIUd/_hۦ1ۃ_}la`\%f$',7R@ܷR3RRBR)]b2if32>t9Ђ cAIID$%$OJ򴤄XY5+Ǥ:*䱣3O'=_|:oQ_}؄b,#ƍMJUd3߹~M ^ZjsS#HyI b[/3^9$'%5SWF(}5HJ:51M&4lƇy6/m?@ 6x?u=b RD$%$A@Jei)2*),hinVr 7}@*=1_^f࿐uRg B^⭷G((NFJnd8,+(rô%0MrR/z5KJ:} fdYvvt`h/+*LM,+ldw)q>}jPX~#^'?敗'#%7IW_xt̘aljʐKa M7էlc*YHJ#ыM&4}{-YOlXe-tݟHDRBRؤh*JM,+ldg)׎ Aӏs 'ܹi~3t*Q{{ٳ# wn_/fn{p]Lkk=|X`cۓK&à%;z2QWWu#&6lll:[=ܯwkWS; {zz*++Ϟ={ᆆkkkѕ8 kz8IJzrJQREiIr<7RE)܊FJ{w/X>ҏ/-]%$ltAI{$JBmm-/+%$3{!mm /`;}vY~+&9:aCm۶aERR_H ]|MFF8>|r׬Yٳݽ1--W^ٺu%+HV{Q׿3g쫫Jva~e =7h_xMٹ}&9:IJ&&&sfOݳkiggSv\ᬷv˽=$ :ʕ+qqq`'%%}EEEN&M9]%mJڻ{)= Dw4r k){g }K\oOp<k=bF'%5<)'$DGTQrlLY#ݖAg;?''rRI8;W\3OK:j%;-y~ _֦玌9MI"5I _0Y$[Oww'#;4 1nju< *//777}7VXQUU5|%K荔ZIYHJHJHJHJ@JeŅI**+*lk"}ɔ]Zzoi&H~>{Wx^Ç׿`oqq/B=c "'[Y!AJv.SZMv5ܜ[XjQQ6qի?tN5Вڂi!)鎔Mƭ4-,slXٲ9l1F"Ҁ'ҢpEGGGDDƍOr"Y파hoo*Zu={G]adح'KKK[Z;^jmm_E6lV[#\noKvh+*>vdKllLggGUU-7쵱Zh4o<[[[ :T1hӼ IIwODRBRBR&))EE*%9Xuwuґ׳vn_(˾:>찡-[58?7^x{gϘ>g /s_|> 3{/Mtec1i^!tW_O1mlh P Ylz:ČOJj$&j@ H ?>:"6"Ox8t8iԵ g_8{ҹ[n_xʥ]xͫo]ws-{߿|g_wW^nA>^!~ޡ>aA~!ѡA1X 8U e(U%WTW jkwl[}׺R(zE:*dCRH )TvIj)2s HWܾz[~\@Nޮ)ȏ!1 xy`R)yg|<}QYgI]E&V77{{3cN,kLJ@ 6x?ab'%5)bCDWx$ E{V@AJ>"Rrs%O@J"yIѽ{߹g]<wNDJHJRIIQR>/+&,8H PIΝ;f$f%<Ç0HM?/p)9IE)] $,v,/y}\8SLd?z :)dgedeA 6xpvHJ#%]=d,s638;Xbaǁl:: OJj$")!) xReF>z*ZD$nx/L1pEΝ-fQ%%ⴒ!K~ p~HJe85?y)g_K0܏7z`T!% "ܜoA[WW՛]%pvtMJE&؛do͚sZ~::0HJHJ2J֮L;!VӺѐ9ݼ6vH>vpߌw13#z愱cvo<5n"km%~XbKK 0/'#-Z{{{[Z3RS F22))(TQZCRBRBRBR2vRULJ ^o 2ܡ}րIǎ I4';-O/3^#-'F_ߺfk$(&<H JaRrb<Q[kK]MZ{zz%a?TQ@f{II)HJHJHJDJ9! IimE?x/QyZzRvH:)9ݺtk۷n+!%^fzJrbwwwM@zz I Yg36N"+LǘM̘9Ӛ!vhǮCIID$%$OJ)IH `iЮY2%yi&gپko])9߾>==qBHP`AvqG\R*.̏ j!_YHJ8;$%L0mn6q3cN kև8Ђ YGHJHJ2Ran6P"WKޙ 3yR7xCIf;rUKM7ʹӺ&%/ -5%9#%)2)H0/'(JP%TjagGCR&CKZpvHJO [n:zM);3Xd;΁l9b RD$%$OJi qQH)';9KRBlE %1)96@t1Ltͪ0G蚔kkcE?nsxnV B~rU =YHJ8;$%IRBRBRBR2|RJF%%XŔHb*+*'n )y) *˕ H⑔%pvHJRwt뎔^[h2t騵f61nŚuq`ZRR#I IiR7Z$ @/+ʲҤh}c^I0_R>uU2媭fS'%Ke%EmHJ8;$%e}){ɐ#ךxks냃쏏s<eN'%5<)%DGJRLh!А //&~yr555zyCpq:wTnvv]m_QYV_^W[:w$@$,CR)-4ys鈵f61&`kz {q`Ls:Ę'%56)ݺu+G 8R"~ǯ_)--Ђ}e{cb I YgORB7_k6vc;Vٳs<aN'%56)wrpC(RBJ%ŅEyrU3N>>kha/HP%%ECJpvHJJI#VW}Bח[k6fcT+֌쏎s<nN'%56)8G(RRDZ lߨ9I I II}CRBRBR2 RrsqpuA.R@J-%EEUa^N~ 7ZUI*-*hmAR!)!)!)!)7) E@RPHJ8;$%!WtZћv0Xdx-0s:ļڟHDRBR DţIlHJHJHJHJ}d~eɿYk6jcViٳs<jNWHJHJ sɿ_")!)!)!)n$%$%$%$BJ-Ņ(5T""f$%"RշЗ2֚``edpb`CrRR#I I IiPRKsqa~a^J #)쐔]}DRBRBRBRT-ARBR!))'%=ytr7ך171`Xd-?Ü1IID$%$%$%$%t˪?⑔%pvHJz#5f62FogN˚r=Z!"5LDRBRBRBR7*B 75W xWᐔ%pvHJHJHJHJHJry^]5U*ArT ;;: %pvHJ CJsfS%sO#k̆ndΜch_[KCD$%$%$MJM*_ȯPj!! I Yg$IJ^2dMlmYRn>e5fC62Fngz{1`^KGh4I)? '47m@JrRjmmxȂ\ʤhRq~nKS#IILyfJ=:)}p/?x^YKHJHJHJ ssxLtWW]ů(Sښܜl|$5q I g$2oF˽Ҋ^7yA,&͜6~Ȧ!L^s-] RD$%$%$%$%) pknj◗*WSS#`CIIS'syj! Z!&;+I{H2 I Yg)9q,>>6??Ow=sⸯ J%ܬnǎ\r]ZZ*FG_up;zB DYIIƢF$%BR}Jr]<{2>Pύt*T%W9Y q޷n\;y6B 6x{!F2*))(؀CR"%oSR.%&$L{PRb!oN tk4$%B@?_ohstMJI֕K===RT3CÇj#a oO$%$%$%$%m/#=3-%%1!9!ZCRKJ>OxzN' JPP$^FZr|,/3N `GHQܔ m*!)쐔7!I I IiRVZJR|\kkkooOkKsCC]}] `677^ARB!)#%nҀ'%@( ֖*Y@$,*II g_}ӅCS͝?uee8nTp\RjRNVxiMMIBE D"URڈ/":$%!>>FjDžx$%d )y>ɫoyY2O$s#R}]~vVNf3"B|~Sc}\͍ YKm\CR!)iNJÇ0@oMxN+X%S}@mMMB||TxrRJKt쬩T *Z5II g2U$%ho^85N+T_[#$&@PZRRXPPWTXP^VVWW+˶XnXp RHIza/O_\B~ooCIIDJ\Hiᅫ=5f!~ǎYz嘑}-U!%I* )dg󲲀¾ ARBRBRBR"H)O)8kkmAer)@<IImRƌ>u/ 氡wyM3NyYBAPd\***$`)+3#/'qޖfo/ _/$%$%$%$%8fgegK*#59//_Q\599YIIjhRII>)eqN&;bmDloI $jjjaeE ry̌L;)23$_^\MM\! 2)ڸ˄DR!))$%:4=ÅǍzXR*/)&cQccCsssKK K5 7'p~HJHJHJHJ z"_UMDj :\x"qR=II5#C{ǎknjdHJ!Af744';[[[jekpJɕD+~HJHJHJHJ 3]BAEijė/= ,CR"%O<*MrPPWW v4׋N+e%E232RSRRHrr护$c:}DWW[-WT A+2O$%pvHJ^O$)575)U ---[wwwG{;xjjD'W\\#;-5%)1~޼$RQNwo?b{7:T C}ҥc^ߡ,CRR>%DDSjoLdjok{tN$2RSrr~/;4I^1)qmllJb`$Z$%շ'@J$)Պnnlmmioo^LjmݧTZ\\7sJ"RJMN&Diw(Ҡ&ᦧ*Qzrb\LTXph'xfe")쐔 :;ɫoe:@Ԕ^K- Ewt %ő{~$%$%$%$%U +-%#5935Z9#!)쐔 Z[[IR*+). pxa?|Ej٢yyO@d6/+3==5%91!!>6$}VHJHJHJy)(5^_[CRRHJ~!W/_{㽻{ws8(B <45+$ 5Ox{ex>TtBI|xRZJrRb|<4ҲI I IipR'qQjI g$ Ỉcy*>.̉n.D9YYK.y?غ҅!AŅQ ?m"5&~8[rb"tTTTx$%$%$%$%d$%$%$%ų'-5ޠxn̥3syYK.Xd#=G?[R\rDN(%@u\*.9̃}&%%61!aڻ%Cxчt?]W='qss oo//++';;?/p0 Leff'%O(r'O?zҠ&꜌dCR!)Q"[W.H-lW~M ^ ) WG*eefC aHY0XnLYYim{w ERBRBR$I\ڔ(=91%!.-ylր,]-II3R?vLxC׬54n?x=ibpP\R%'_[ efB9ٹ9fPlD_=wՋr0Ԛj ښjTQ܌pvHJxRxÇ`HaRA~?L.VTzo"R 쩭!vuuo@JHO(eB vfz:0q.))Q%.C>).kgW7 '%8gf$UjB\|LtkKKooOkKsCC]}] `ZsSHP&%C`鐔pvHJrH)F)!&+EJpwWF '_HFxd`"ࢴTRbc1@b߻{DHJHJHJx)IJI ikmU{[kOwwTx(DYTII"X$%bRwJ(_."O)))!{w<2J"%љ0@2\eEEJQXI "FrbeeEzۃ.}I I I II.)%%555&V  664@$#)!K쐔=y܌ }2232sޝ9mZNv677f4yW';!>;,80",¼O(JN?臤$"*ϔ$'J)>:2]S%P(')F$^ZJMI gdz{x:'DÇ=|:!: &~>^ ުlqܽw]8sJ=) eI)=)!4П_YX_-+WscCey9CII9)}<|\=4t|rF*?w՞?MBRBRBRBR"HFLJ@GJIswYS%T*WMGP%%KYHJ8;$%%?WW89ދ+//okk#iii)** ;~l]W/'醤$MJI Jq{*_ȯPj!! r)RKt5BIIr E޽y⹳vǎ:~k#;] U$MJq\7Z[񐅤,CR2@RPHJHJHJHJRuq[W]ů(Sښl'#)!K쐔T @P?oSNzzFRBRBR̤T-d$%K*%>չ_^\MMn.! *)I m\FR!))&%on)Ą7: jb*E/3,^:ZXYVHU D#%I goH)'"8JXVRX\ :naI I I IInΎN+!XV5B){x$%d wcIqAiq~Q~V][b\-/3Ҡ&%?#915!NV_?~,ǫ+*JjkxYǎ@ DYTII"X*$%_ȕCJa>8Y~+*UJ:?3)U)&8nxPWŘmLT {!"%pvHJI)3\9奁1stJRJ:WMR􌤄B]_xA=ZlJb`$%~%IIo$*8ǎ{i 0%8a,YRRSFeN=#)!)!) nRHNHMUDnTThpo<8,! r61B$@HBKH#cL +rM%Yؖ{]b?i"kW-`og4;ꕴ!<@醏<ДpthJ52yukիVC” VAʆ&CSӤ=T8h&"))))55eed+EeɥHGuj4%R'M(_E&0p@ g͚ @B! ɦDY#F3M M M M M M Mo9ٔ s3|l)Fr2ep֛3C(UP!ɔ(D0QFF3M M MɜMI1%eRtuM G)SVckaaB'z C! ɦDFD2Nm#F3M M MɬM1/S!]vM Gԡ_ԙL^L)ߜ@䡐0%ʆdS"Y}hVv=iVh_i\:2yRCnR!]v]3M+RS)%lwk3dBxt(x *P6$RmN+E[v]fmFFF3M M MMIRH.M Gd̔R>}S)db#; i:QxH (M\gXC=Vh_j5i.f,2RB%&S*V>WKPrV<ДpthJF?}KzO)K."#6oyȐ!E߭[w D6MҔVBEOwLZ^ZTt52+v{$>92YR}]B)!n޸vId[77A}hm;mJ7ݷ]XWCS2nJy$0c._8@(aT|hJhJhJhJ C~/447% C+4%t )2I  &B4DFSBSBS2gSjU)R>R!?$?47LsItmgM`} uhJ8:4%L2%!IYDFSBSBS2kSUɥ>2?߿ Xo0_o߂ vڔooM GdncԷ@SBSBSBS"R=꥖:\|ꛩ)֠)ДJR_7\!EFG޺y47oވ vޔ}u hJ8:4%)Q3nATu?Ad4%4%4%s6,D)IIp%5ؔ1\j(PhYS"oCS)HT"Vw1k!פ'GFSBSBS2kSɒIbt^d(y禦Kj2MBMO4)6݇]PSCS"R.)Y)B.r@!l&P|hJhJhJmJY2R, c2|N^rIXW!U7_r 5u:oJ :4%ISj[RtDMuEmuyUyڠ~0``:4%R!MP^Ɣ6  O>d~˧Xd0cbC"C <@醏<SBSѡ)uʔJSh+~Kǿ:nԞIϹY8~+IS^I|}ܠ9ٔ>4iҥE7owppt׮]<{u/_ehJhJhJhJ/ o掄CS)oA[_| 2"lNoƈ ,N8?yk#2fy0hn24%`J .\bw}W_\o]]-[vB7229R]uUD$#] C"CSѡ)uҔU0!'^8"qo?{ذaMg%{(i|)'>eМlJkGdС 6̝;/޲hѢܹs &Md$ HɬDSѡ)5%)ٔ s3g9/u6{j)6X26ϞJ40%$Sjȑ`G۷o?~dddrrrbbbHHƍ.]'&%%yzz)QFFSBSBS2oSDS)U)ДH$1aJJ}/ßs|Y^xAO=1ڋyӞa ㇞zec As)鯵XbŞ={۷h"0L?.[lɒ%}ѹs&NHM M MɬM2C, HjєpthJM)EWt&%ק'?{vO=T?c?֏B>l3'-pڋa{Ҡ9ׂٔ)Z 4 O>Yt;i̝;ӡNNNǏdΦOJ@w6)bamUM/ORRNT0'Ganc0ď<:xC#>j )0 dS_kiizj0%"{{e͛7@'t:cǎt_%R H!%hJДpthJ&M7ROAO9=/Od ,='lggGM M MɬMB)yƐ2uskk iKs3@95ObbӽuєpthJקo%Cҳo Ϝ?φ 9c '_*0$)ut֔mOv MS!Ԡ'xzXO&0ѤWr a=mEAɦb[nݼy{|J۷~XvQFQFFSBSBS2kS(Wӥ|b^ZJ|A\R^v jB}aM>:؁hJ8:4%cĐ蛒gQa~n)M`/?qf,|bgG >=ysxf9@mJQ a S">|y>+Wnذa۶m;? qI4XuItmMrM GdܔrMNR|\xhX_7 JI= 2ٔ=}6Rsc=P;stч475n߾є%pthJ$,ԃ "))))<1O:]R\|R 8F3M' rRQaԇVΛ2 M GԱ9RUY,+SQanl%y:\nܸk߼ )tBΫ*+ASѡ) M M M MR ٔF\g˗ԍu5_rQWrRKEy')KДL3nAT9I] 2Y).gO755^R77Vnnjl<ԄhJ8:4~HNNK6k) &C_WSE|9W hJhJhJlJ% ]N+.,zEXWKNǏBkYSokAhJ8:4%wtKJf⢻@6!l&R\\j˫ʋ 4 Dqޞ R{e:oS)KДRBTxRb"LZ TrR%QS]Q]YVQZMjJ ٛR1S"dvCwcRC Z̔ДpthJ-Ba{tp!~>jucYQ^ТnM M M MɴY)ĸȰpH/;f 캊4%Lٔ{ݕf_F›'a]ZX,&%!M9DPrєДДkJ_r3ohJ8:4%)3IU+W,Z0' ɔ:lNtCM M M MS)ДoJl6~]wo/RT ٔ r2 NtCRORMɸ)M)/IZVZKaY` 1hN6 :hJP͔$LɿΧ}=cGԥ1҅4g1 M)'SC+h LhJhJhJhJ_Qdʔr)_3 *^g|E_/,_5K MI B:D-֔ДДДДLRa5IDaJ>헡C\Nr\}9] Xi!RL1l~C4%4%4%4%40b4%#nJ%yMRl{j)zGEFz-O=$?$н;ECX”= sJ vaD7ty*EA~.DSBSBSBS)yg3N@e+kIqQadڔ )I^n#`3U&LIN 8qJLLЗA .]ФDa0 UW:'KD'\2BX4%4%4%4.ҝ;wrs~Z~*3pʕiJŅhJ8G۔b1"72/&8PG\xˮ()](?} 3!GI@@#! %Jrs鮐IKKΧB&twP,md4%4%4%6]ЕKs9֭[\LkԱ#D];JKwAo߾s[nxCaUe%qMNe$oiïo'%A(hJhJhJ4%gp5U dֶm) 8̤H .غ@u䘨;~{BєДДٔ O뿯>|p?مrUɉ 6oڔdR +5eۦn55DR.c1Sy*#)C u >^W._6 eqS:SbHڙ!3nA _OCNR|\xhX_hJhJhJhJ1%Hrs˥֭oM͛;޸~a6ׯھ26?1![D4%4%47!dJG2&V "))))uӔΝ9}-?mP77AҔܹCVcڔt5޹wQo4%4%4RJ|lGLӷ> ۦT !=|lWa4%4%4%s7"4%4%4%4S$\onn3I)%|~تz9Ia4%4%4%4%4%4%4%4R}]+W2$B]ITtʕZcgJLFˍ?Z; :dަO:SB: άL I)*S&=TpLg2{&5vJ,uu--7̂CєДДДyДДДz'u$&R'c:tkm 5fQ>ڼ6[^2eSTCS"}m”vFjXmMǩVsl&Y[|`g --j5tG[cӅ-hJhJhJhJhJhJhJhJޔrv9oZ*i)P] ̶|&]I@SBSBSBSBS))ДR>yƩVg޴^7yLuM5yPLmRn䁱,E4%4%4%4%))))))IMRǣ@3G[ Ai,[C3 .DT 2yyOmM M M M M M M M5%)]_;[. ;zE6goF&7j& ˱V?OєДДДДДДД:nJ1aÿ );]իm%smz9mFSBSBS2sS*txoOךC[4%4%45E xu*$*>}e>7fJL;(?3to֤_M n1t Mwl~ݻ%Дz˧o&[)15g֛&O$SRM 2苧\xr@7z[ .r>t:f M=[o+?{؛3~͙\MS PǍ=xp3';yԉLpnRALL)7C2ny3bᄱvivRn̔R%LIa2"B}ڶqt FSBSBSBSꅦȏV}|ʄA>^鬔7gB)-m?od'*QPdkmӏsΞ8kДДД:xGwQnv`1!tza7M%NBL<= ˭݃NdTR'.Sb%͘6 y^jWWboܴP:SAS,ESљw*K9 t"#fbԔks$4%4%4%4 ӫLi={q:hMxڕ˧N|/(Iըtv*!]#~dKl4%J@]uefFFmU}әҔhJhJhJlJ9fJoz=@5_ACӧM5x_IƦkޒ )BSљ)L)!}4%4%4%4|UF2% ?xm¹zȜ9~4ɺB|:c[I ?0 4%iSz4 M M MlM)7C!MՒv2(<!qy g: 9)'M;M Gd$*7~5'ANpRS5`/KM$R M2R8 @<')%LeǦ&1@tj\tJ\fF2bc 49&" “4$F @h|D6 ! c0:AL40 "Qtd @ @ a>OCwWw^txґj‘ GYsC| N8OlsùAsE9 l;pio;{Ϛs85g;?&"YC$C3)4sD;S& <ٔ5 fi1 a>jfer1I ՐX;`^4wͬo@ve^%-Ŕ2%qǍ٘ Mɘ)HU <ĐӤ\'Bȗ t0])( 0C!S*ʔ3e,8K&Q%T d+*,[!Vs2TKRgAWe23 r U9\UQnvQnNq^vQ%9yeEEŅE(*#(./.+ʊʺʺꪺHkjmC"pGHjXU]s=4!-p'R%=X+מڳ@w~¹JڳWs&!Ȇ\s*85|yD̩f~igt9)SU3gBY sY;5[05>u AK\%W <S=ڦ]NnaSa)ДH20%APL)C"zMi|Q!A=nJєpthJhJ2aMjuZݠnnRsc1 kK5,yan>M`PP5g-cmGw$8tM Az^-:3e?&4HRpо-Mʲ_xQRSc`̖8 p, 7SyMGLz{9L3+P[_WMH:x}M)'SVA U2\ CSB^ùS'O?8 ΧCECTp=7_+/),>NtxhdOBz0k9hoNnI9c ]NK+1.׫@/=Sΐg+)c峔8.wۏ C}{6oϚIHԑ_,YgNlTAU+~ٷ[WRJL4m򤩓&? vmw ,1 b)!H/yx~ys?Pk9ņ%D |喊" 4y\D uEyihM_ mLbr I}&] 8" hoJ' ?'Ѓ{:>̔ygqNue.63659jB}8.gNA'=MX)1L U*4OtE͒޻kG\ߝgBtקu[jJ"y9_1moI!Ipn fՊ[7ZVC<3:zJT8FO O~{IXV_ކ3LF J 4O PS gҾMRCڶUuBR  8" 8ML/?Ik4 /s~n=E72Ņ9(.Pȡ&ԧ݃NFz ƼgJ,q&),9%ciPeQ)ĺj&OX wgr>}2b>c3`kSv)B}y A.>p5kkZ)?न0v}`JO"/hӉ\NEUf@ IrSL6C9sCs1>L  8" t9U\ȯ"x{td.@E}z?Gfe(sT΄:PSƁA'=]{̔ęm$RRRB%aNȇ}vc@otlTJ19Hıv ΐ@Rȓ{GSBNNؑ#p5d)MSpMIO^GjL8_um=ڞUiӃj~W"G@=}_Fne[7"}֘}p, 7YF5'/pX1A ipOٙLV"JP2t:)2{)0%PNiJr%Q>|udS2W._:J& &Qc O*2!)=Σ)!Çn WOsޚFoQJ Sjn͒#<?R/7䘜hDzsXa4[UV*sbCp>8QtSb"@db1p )@dnuy+[(#L#RRL!Ő y,[N ?]gޙ=ˠׅ3HOK/ԏI.\JmAƶoiBєPYZf0թ_^.Q0ܜ YReܺF,LwpAҹB~(T\]&ԧ+kG m3%ҔRJ)CФIǽ1_O[Ǎ .sg Fۼ;{VxH}H E^K.䲒!O!t>M Az EYkVJ/\ӂ}c"CCO9~lANVCmU=`}~) !pDLz0kahF% >d ,롓g9Ds @c aJi" * Ƨ'=f(_ n~S|o~|tp`;>! H"L  yC y(yx$Фe A~D  HoAaY) _t*SIL6AA*zPl дˮpSe AA 7Q'\Dm %,g AAs7) 09TIe}p?! '?Хv5 endstream endobj 336 0 obj << /Type /XObject /Subtype /Image /Width 782 /Height 524 /BitsPerComponent 8 /ColorSpace /DeviceGray /Length 848 /Filter /FlateDecode >> stream x 0 İJ4Ir{ytA4A @=`^ع= endstream endobj 328 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [317.136 681.858 339.607 693.813] /A << /S /GoTo /D (section*.23) >> >> endobj 330 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 0 1] /Rect [396.699 177.313 419.17 189.268] /A << /S /GoTo /D (section*.25) >> >> endobj 335 0 obj << /D [333 0 R /XYZ 69.866 808.885 null] >> endobj 305 0 obj << /D [333 0 R /XYZ 270.144 417.807 null] >> endobj 98 0 obj << /D [333 0 R /XYZ 70.866 378.019 null] >> endobj 332 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F21 168 0 R /F20 166 0 R /F28 196 0 R >> /XObject << /Im16 329 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 339 0 obj << /Length 283 /Filter /FlateDecode >> stream xePMO@x7.J!BЈVk!}] y3Pth,h⌰PbQ̀:̞}LP2$ŖdU˛,tAQ#Ns)J),bv{GH*b76J3*I(s˼(2 h)%PazpįR 8* 6av$ Fɮ>^CkB6M*"6q~w}޿ڮ^P/g#e endstream endobj 338 0 obj << /Type /Page /Contents 339 0 R /Resources 337 0 R /MediaBox [0 0 595.276 841.89] /Parent 291 0 R /Group 124 0 R >> endobj 331 0 obj << /Type /XObject /Subtype /Image /Width 628 /Height 620 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 342 0 R /Length 78555 /Filter /FlateDecode >> stream xxGǕ}.wK+Ʌ B %@zP @H#z16 W{w[wB׫jec8[x;DCАPKҼ4/KҼ"##D2ز[}}}=j}z+%^}ʪOT Q?FuSe05DwKOIGŨ Lj#%m/ćz_Ύ8&Xj*JJԩb\J(QP!ST!U CP*T5UzHTCZji1U 50UOTGVzZMP#ZPR 5 4 HUVZۥFuR瀺9uSRnQ-n%=> 9r-ң 9<"\eL],JuPHKP UN*@7 yH74h]hlN5ʶFQݤCTj`ctI`m3z}a\Q6& ;SKiLS2hƴxRPbiS9daU4%K//O U pwhR]U5`洂g<7 FGӇxBXT$g"8Z[N/ڱi^z;Kܻ፥Qz~1|l}xr AAgH}}]dxcxUޫGr`U%,,<7ߓ<Ԟ=yHC}jG={TGu`.SUwUKc}:Vg w3*2L18"BS+0 0T+$VWsim-?|g_WG+gO͚4~Ųe¤OH?|Nv7>P"H4WkFW 16IN  tӼpϫjkA:ZW̴m.,:4wshdf892sds2KULIRV T2AVtH25[Vـ[N5IbHU,U2UMkctTWPf$k ujeXOTMۺ&Y$mfiȟmaY8+`ZmⰐ8QFS&)//E)++hin''X@ _*+*@2a-)\<ۥ0׵ )wWVZ[vHAL*INjdzgajyC{{oo/L̿%jk/_$(xtQusqpIL1LKIƑ r-ŧ$:ʅIjҩ9$C㑴")/ݰ&vw LߑOz"g',(׳XBbX1ˠ :Y׃6fL+8td89ㆍQFj)!p8Í 3>A:W.#)\qr _/Kn:chDl"LLܸxfֺz_||e{zu SٙEY)дSqAeNV&[\V"pO{Jj+{Z:UurUpiZya8&irݓmV|Rҫm1G¨&%%5U5g#8=5d>%]Ɣ)vpC}}qkx/+-;bF$#=Դ iXE l~e\Erb=%RN= LKK kt!\8sx܎[*yr'ɟݕ&MUyf}|sNkXSU<+k{B= nTUJ9Yv6V48 s0h^G+s!pXI^GH0Nl(/C!kA ᆍƜׯݰLKIfk ["cwHPUn9Bc=`=>:\r2ӍrMa[1vdX^6ˁ,?g)˜ÒaDXL}sА&hxd Nܐ{b~ QE|;ީYbwIDx+#˩SHNdyMܔ@Qdxdwbeci9|%CG;1dIw7jQHJ?D˙jDj@U%Պ2-)_g#=%~48 B=+ϵhas舎IOu웙G#[iLJdeavjr\dhRM,Rm"^Gt~߁U~znZ!+L˜c yٯ(712(y2ǘڹS'/]o25@N bq!E,*,WYQ ɉIBQrQt7&% ]]bc^Ώ4:%JIL 2&YJ/=}&n)J@pu)1kWUzv,TR̭-|'baN AXiIQ^v&}:Ia~~r- :۱BJ- F  ; Q&׮uZA!P7VeȪ6]PKB[$r.2Lڒw(֭>`3KGvqȤ.RFqwvvצ}RǶoI9Lɣe0U7O&~UEҎ꘣qWOFE f&Bq:&/~BƧ0g7@owse|ޔ!``hk`ocu SwZ@vX% 3]3;lr;rrHf)}qmU.D EX^z+sJy%rK *tQ'~"uJLa!A׮;}ю[N">48H!8pVeƙBBGz3t gdQnB\MgdgO;ˆA<>%)uV(N>vnwcBVegVU @B .7vweQ5+wizH)R\S15.hv. Urcbu>pQz%u, qn. Ξ>u9\]c\+ReN }Ԃ0ud,5e :ZJȞwC~ x[S}Qrc %;~gv$9+ BhfrW¼` 6a U'.LUǓdxsT{_JF_\CScؘ$2!`U>jAle; )ʸ#Qpfd Re_eBU *₼ܜ۷o 5"QNvKMONȦ~R2xyglavcKƎd̪YU\ZZh˅&p`!IZaPQHZ+|0U9UO4RKDU_@s֖Gh-S\/3(4[-e: ' `r r29z 쨹#sfG\Z.jV) f`6 , 'Q Q$UrTs:<iU HIGu`'ą}2HÅL._x%˖,^|ȼ.rHG ZW>cE*XY^㻼@JT R0vU19SXnz (f&ב쵫 1 k rKearRT/&LyyཹWΞC[]̓ 7OHe x:TezhXU^vi@ 좼 ^pE,VrRr|urz55a,[ylz'ԷT+T-d=U-άʜx,? >gOM(-..Gޜ47NY[;c/X07fϜA32T]QN@/2@rbifJClt$ 9;Sl,HKAcÅS3ӑZQa>Odf0rqJO[xE)&Bf&$W0.R꤄yq0,)cՎp~s >h8Ȅ#VX/-[X*I|T^Rk/x|ʻ]vrn7H[8=*(2< ň"}o fb(+ |Mg'jARt ;3M w4[1:Аg@z )UkH)곏?" #hi$,-9 = dinIhWhׯ;1^n 0focA=~0z_$]Mt颁^NۂdLIsESYjʵT#Jfe[6mD% /' *5)By6Ę0Å ?hH B>i3as&*%?N샕1Dh.w!Pe's4?\<fJSJq09qU}g}~͚f*3 nђӗ/]\^V${١~njݞ_vdػgO?\uȡ쌴G~; V b@H bݹٙ3M͇Cp|I5+$aan8მ7~M.)BfH @?q <αB׍ ʄu A27xg*rrp^q~w_]13RV>x F ekm dcixqd<\d=.m|Aı[DyUQ7l!:,5_:qC];~8w b9yؑgOb!c:>Yd Xcgckak0QŒ5lb+a2O}5~B\,=^쩓̹ӧ䰵Fm{#ë:?l. ׭=s$o'Im݌v.vQ|zro0jlxo҃1Sr$`KZia@+DE>@4WPaI'F#,u#CH@˗2^p`?FWh37m`v¼\ttg-|y7m@M z.Ʈ>) % l!֬>qcGLPIo6ڿİ _#27E)p0y m|oQA\ P^OG*%%G&'%Qz ڪQX|PMn8 le6561")/ g>-8>.-*p~G{YʲU֧|&'!A*We)oLPM r)&j}U>[I #5`l5ekw[p$Gc t|JƟ/<kϝ}>J%GH~U=]h}]_~ގnbt )r2`";B^xg XJʿHCLMue`H pcpAyi s6HVBRS#H- 8@ sf$>/f!CEY |R^oۺpɸ|aY 3`Pv*&Io )]Y)•ecLź|f-ߎJrj@^N6<72c0^΂,bʼ<]I!FhZȄ̱(d!N!M0e =uk@@W__Gk` ׮2J S|) =}f JB7'#}E l:/Dž#Cw,NQ2ZDfZMgvy99M ,fz>uueϺ}6&EuFo/(PVĚR@ o}.Iʣ'߮ՕZWXYҺtQdSy%JϽ0gwz{_y{KDDCz`oIJ )ZQAn6z+|L0G\  DbmP;4A/0LYS|D#qmh0Dn7?+D>^Q1(q.NWuϞ>"aǛ^7JJL(//M?F4Ր*k:*Rle@n)3D~b()[1%d8ES6gr*ScwU!pdǏ'EkHO?֠|(nJ>;#?maP[;˗,>r‡XDvԭ7]8{\I2A WBP6!78)!&ʜc($(q,'3V cF MWpAEH ߸o.Dيkߵc;9I$Nn>zٶn'~`ifZL u)G 1rF >VqkzԎ+,ݰۃ,&Ȅt7VsB+y*x0_09<1J>4 y^MmtZtboӦ9yN(F ,4"o2~uI`+6!IX=De֗GM J0"u gl?Fc KΟ,Ɋ|꤉l,#o}woL] Nb]f\CHKa'$28YB|!.^D[@=:EôSKSSӆߦ$ٙ(8P&# tVVpruv`7`K ,Y$IvbݐW", $nceIGJ/)(jZܜu_F&ߛ;ǛDdrtWWefSOhP+;ThX`\7.3 DEc8V`ڡn^3>e,0 ܴ+]m-"}]#`fb<'SϾ;PUanN"鹅4K2SekDbM_uEOG{_!`r݈PsR%R!g@3@3KOMjɤRd\B6c$z.R1mpı&U!v>H0Y7\"RqK26hphYs!8 $=S"KNMX^z~.0z1tSq(WSf gFwKF靣JTVR*T$Ye{ *Jv* oH%DY,HUj S6X^ʒQ8 zWmK9z[,# {5(% M4lU?gU٘CQ.* \e]k %3V,AjE[% sK+A^ex 05RsSMdTM U%jP,j+mU'sUǮ%ç|"+Hm׮r1ʭEl Q3H4R$t̳0/'<8V\TT:m\-DxL#uM_Хsg y42KQxxD*yu3z~%,DB Y^Cba46z8 GG`i8"Gc+%[uNOILpp̢6<8ԑ:|[URXԤDKӺlge⨂pĸw3,+ hƩ K`Q%CHfM{1jH|L:MC72 Y[uMYڊsI}[3וĮMGHqYSjz44 ]xJlX US4e'qae$~+˜Ʃxs{})%G 7+#*<,$( 8pHA!A H-<$8-9)'3SJYʕ(-9D 21pR!wLbUNG"þG_ AM-[ \,*SqEfIu%Hbu%Ī%KD'Y$E]ffOpP`ppPHpPhHphhFozۿZ_8[|6'H ۷oW__oOO[[[VVM6?7?"VXպ6Nq )D) I iiI)ɩ7]VZ51QiD"cJx>#z=9ZWB9i?ڼ<70[/*%du[fZ+-M=s2%b)FQNJ@M\TpLNwe呈*Se]\y1~XPYF3D#R5$%J"KQq,I"~xßD͎4~&yCu橚ڄ솆ƼFOe.Y?}'Vwwuom=q֭]CWדMMO,K3AQCETR"ݼ)g&HwXfSE"([$ǐ`L b+,jkXBK,1_'$=+C[ $.sSS#3͵98gV&->1/X *+;ﲩg@Dϭ[]]]lmi $w+;vD%uwutuvw{KJ߳k%KBL2^eVOW_L ։9ZC沎ozw6,֜:ț?}a."}.^036h99Z_p>$?:"\z\PC|榦l ZlRcYɒVG[[e^|麱գ%"9|99|-Ly|$xyz-b\ݣ*6Q>T+jnb>_:<8n9Q{B"rz'R[9 %9i亱RN0^'z'݊Znebjif|E^3{7=~vg6lxs&&s޵}--guK(Z[!egD :u𼩃J MO?tkʔ)z%Uw:g: VBUp}ˌ^k{˟4ycS|k`ޒ U._it`pL?[.Xn޾֎[D-ۻں[[ڻz#^G|WW [xU ڑێ-[];9nYG[Os"n7yv_dO8[u`MƖa+WValӥϜ23@]pݻF! ȮHp}}M|mBWGKWWm%Y/WSSgn+UpH:\U=ČVoBRx4j6LĠQ|d)SD|_|'Eog̙̚M=lRSEI`8>̤)Pl Uhʜ%>z*d*߾rrւ1k,_z}; /whY_ѹrI_GuëGWƹPaLX+dBB"$uOSXeRt9]^֌&}w(;;\cC\B"r8EGD[b摗B"Μfhoݲ9!6'Du޽gOR{R/XjB!FYlWg'aEgζoIdjfywm.E{]⋹s׿3?xF}k?54RYi VXWIbGDy[D[ jWq(=xB`'e&H^ɧÜ ,N8F hDQR .\LSsbG[_wf+qW騛;v>"zF4R[]h΢sx{,\Bz%l%EEEIIWq׬>|> ,'[rOpdeC{FQ-N$MTZXSRbeī,XGuDU_5KYQHӧ|o}+51|so~\YY@&F:/]:$5jm,)D:ko~˽{ݻpێv--Pdg' Vkm-uUK [l*1ؚ=l=yj&Uev(ghɒ%SO=E䓦l lm&+n,.ӒqAYtvM?}DO(ـ'Aaѯ|u{^ V{dZP~!l;{Ίe= +h%U%H[SZr΃,[es*(Usդ~^zn7/-)Xg8ݗ]|7ٗ~U#=+3 3sgVz*#B ,[ɷ(V[5 Y׮^ЀaI HGK+;; B*<}#e+;tծWGʹs0⵶^jW׃۷orjiʲ+ 9x<,wCʀ-Ǩ> Ylsb#w޽g=g޸qcg֞vvml&{JHmݻdS`6ziܐ],QDX褉1Wd񳵥DžL?>84~`+e:VUouVcW볩k_ut7\_{U{Jڙs]Qr ] K*b=Bnڸ[:{ڹ[_Go?>73$w.^Y\YRUBC>l&;!*Vi 4g%8jsyoY^\o9,ZkW]gn jhRV5-M3tZs32,+%&*{x*ʜpeyirRҸg1Um&5U/^ o~cO%uGoIK~+vH蓼Ԝ͙݅<ӠOܲ1ӟ65'v3$"]5wߊQͬ3}WXeR;SҢm}8QG$"&l Zo>WE*{?b Nn6愭)IZ.?sp Z t}dX)r╗CVi=G7lmn7w.k[eeN{e%k㏻wFmɔ '˛4[Mor޴߯̎ N@2۫䥭Yi%[*X?uZiO=6߽ʕw7=rV/_(ߵ[56~[_NG1[(^v)64P3yy '2ҏ9Μ0Y0*u5ջvӟ_7m,b|Z># {ywttcz;WwW ?heYeZ~=Rha`مg839cVl;@͚>#D$X #igO^Zŧr}zӥ 164[|QaZ9fX][ Z\Tpz㆛{UULZ'8A)vI^_~I<- U{M;uqWpYǎmkksuuDGӡV&yn($ pX’^:ѻ?֭?ݽoɴ{ܻw޽whimCJlMJ8X 29A=U!~+/~r0lp}}kU,NV]eΧ~zW.f?l 6+l%+uuv d뚓g N9Qr;IO:䟆OACBMGy7m!tyël\rwg΄NJcO,r_pؚ)*LYt8 Xp=qйڟۭUM'Ȱ ?/ب2XM(a[UH vZ녽{[[T+[ΎYYYW-l>fggxjw*u)ս{vߋ/RlVf=?HC`:S6[]6mBvt<}y?iw*يlO]G]KOF=9H\\DG*V$g X]?{̑xN8)1 ?KU.3J=pZiŸ-..̤> /Oxr(8q#D'wR"aKޜ6{Xȼ*7k<_htk-οmumP?Ay>ߟ;wք mv{zuu,!ʢVU'~jk}=Mс do=5,hpNA7([G^)4Yф d#ĪJw[QW0mLH;Q[s%.3 alBB''+$]_u-9IڎۍSw0N)?KINvu_}m S9ig===qdmll6n0kIoVzYG8#K sS s ', Vyxmjj:vվƚ/Wo Ν9c /9>/"a*$3y'@˗twJKIVfran"{ "\pmoJV/~F7>vSa=^'{LBս׿uPssZw߹}֭Ymm44SU5K1[g\?e`kI u[lxQ [:VnQɧkpƦ[p-G{D~(gIGE?fcV ; )i{.bóC396ilҊp%Zɿxe Mڱt'ȰM0I 7oW_]|H__ŲoN4Wg=m«z 04WeRHl]o/!̰ت:bOtwjO5+3^|vFZzJRjR NHOxU_/&2gb~pS-ñсD Jsπ/Ca So4/J7.JP(ҀoURsZ\K>[|s&lyy ::t^筭/]ll<|Jg'9}ZSӤ%%9[(R߾YCx--ESM(L0[z~aO}91iw/Z4]uyJ]Yj?ZE8ZXp!1ao ,*J^-H9.N^4vaEMDl 4!u ٚ=^Gex'N.,| 55ؚF (_Uve}8'?\/*k}fEF1KXͺ_? Mp,:AJ'xt!248G fQmG}{p!-7ߴOw>{ufzh롫9>^q1Q q1P|l4@?/9i O=;BY"4^r'_kD~G!X/RSǼlHQPY=:,d8&y{")Myj46OPJ s23|cT8fe )Myj46OPQak44=(?';&",2$HFrK5%Xnc?A4l!SEiI]MuC]mcCR)8FFSf9HV4H#4z:iFiI4H#4~냵vR#4H#2[vvDyٙ~xgFjFiF VLkP~NVNfzzJXr/:4Hr4z nhC tv5uvt oȡ4Hr4zX٪ ~~[]]cM՝;Cs !!6tvCV֖q1V4ZxQdf*S\LT[["Muww!@R|,$)!VӊHylQc?XlL?dBz{~Dz0qrKMIjok=_aJr M+#-AoocErcUwű=ۋ|н3G{cwُZ[M+#-AioV;qDM[5[{zzZDzFBxû#`{#G^z~/>TyhZi9D{{Z#]n4[<'|_MI:`gclhwPdXU=W,r͆ߚ_YؼqC~N61 F%76_joK}0ޔjQlxTWOVΩ`Wgǿfv 8&2|O?~uօ{7ldb\ G2P2S22XiY̘d/f?jkk[vW^{{{ug_otU+XU?aQ($(p:;T Ϝ<Zџ2Z y Fq!U޽E}:杩o#ݴTnQaGH_&S&Kb̛1=SW,t–FioLd D5|w]h z+/agdif@ZrAx硤 ҋY驜l `x9afϜaokMB(FSk^}e;nyVVjoeyk/D> ߛ@}xy" p,rm~U_XQM/҂̛;WSRRVX"[Bʬ2蚼zGdrw۷{"$JOJg[|),ÍfV6Mk8-Y= :.ktU_O[K&^&vuGL;$x]+ZP0-#UAႜ,<@Q#7{ 騑GfWvthލɞj6${+IDQ{R~S#CL/J'("oO%ENF֌ioHtHުeKYΔUlj"o޸Ġyݬ051;}@%ǣo.?O\ ȭlw+Ew<ŒP_616;/c[\\@X#5V+1%%o9z&Ktw.&8[Z zG0D?oiE|hEj9B J]+-a}.$IaW#Ra.`YE7Z-DsYTkKeK 9ƴAMÄWG7l_&/V_F u#R-lE/Cw#dT+sg<&>t;Bz{%C@BHB:{Ol7v8]{;׎-kVj#=GH;3^?iC\n[sĘG@ O&}Hw~ϧ`؊bͦeE1E5,+N߮ȹr`~a@_iZ4I:gҡ?sYHb ɟd$%9+WV ^xјsO:mr3i%F_=P$J c~26u Bf tz#[hNvO {GFeKrz|pgrN>z WvXggӊpȔu6C̩WnـUwfYȎ]MϢX{*<]sk:tvpںe[\x;?588\^V}5' iD];wLq= ABkhGEGꐈk"%,%ULtঀ1b{ﮝ1=mt>y<W|^JbFj N(p:z$;#='3/CU+W=[˭3a+9vJ_y.aΝ;[i#/^[:Lڕٙb.ϖZlE`Դ(f%Yžӷ+r> $A+BݺLtu9߾MӧNYd+u޶UK1>& ݸ&4#iF1rЃ.]=cTj>*ry̫kkwW\I]i Ԫ1サbR':s8n1\9.cv>^.a契bIa>-THyAC֌7 (KGjÙ6"fΞ1;mرup/\>dpfFi#G V81l]kJꝑ#&˼6δV!å ,dU0Lk}0q㺵,s>v Fc%[pZxa/~AeٛMMb6^ҙE9ӷ+r> ? Dbށߪș?sdQA>,2:8t@D78{ڵ}Xȳ 8eNzj􍾽z"+Â{{b4-Z>LzqWLsSRWB~}1%CtreWtrn]Q۲ q!G,}۴ӳ'WFi?(۩C;'C6{twszOlA3VEY)Oleu݇lsiٺaݺ۷UUںemVЮu+GX,++Կ?hLtlũ5ϜbB8;"[YG S&g<<3H 7{ocZo#˱E/i\Ď36?׋ٛMMb6^ҙd]qvEGtwsj.;CXZRVθ V $1^(?מ%C\YY:7Oo߾m_W\0vǎ~ef>^Voe AC[1Gr7[VX<ϣדPZZJwH']::o o5Vz‚_{kEiI~~>i;>xNtak0N_߿/يCN(V"[[N7Yj磭 jlr֭WGyFr8[Vy/p|!B:ukV|[QXNC7Yj'MpAqԉ''%&ٛh!iLNL턭N5o$'z_SN,[ò(9ozFfX_+#q(9>6<8;nYg{Zhz &R LK]uvCXֹ|֭3֠WN,ڛ=[= יlXU*5.FG|<=MhfkׇF,0>*R"movkQv~>*olQV!HIk΄#7WojVZ*{aUTRJ޼q#5fTJ*UTY[k.RJ*Ua*UTRJe*UTRU*UTzDؚ@'2>xőy Q"hJ 1 F%cH6(.:2>*2Z(}AKIDb#yLkcW"("tdNInbC "ÉB0ׇ\3օ‚tDH64҅jAC~AaP_h/cLCޡ~ޘeԵ`Zg5 ozyxa]A"F](H]y;zٗJ^qsrAÕ2t^.^.^ssr]sp sWµK<]{^tިsW/rY$TztTcp9b<_Ȝ:}\O%e<'Z4YB|pz\ik<]q:^9q_>)@9!Pn8k5~џ\oޔj܎ϵj_K^!~^"cpnF/`L>$18Iόđ¯Rio) Ky 9۔ՍS SD 1‚ BQƈA LFNQ~YQFay5KKJ#f%q+MPZ2R3jF)R*,Ji2(eSJάVNfjfeR&ʃr|((*#ʅ i*.,UTXBTLV%eJKʡR 2Ju׫uutun@nҺ U"ݮ"tNnSs.;޹GnQD=!DBo5UO:=r$N7DJD 9Ԝ8Q {Z\OmէL)@~O=O#E(*j!%{v5^TK(UbJLtEy'wۯA@6B5 "9jȒAOF5NqXiXV50.=%FH059jV^'~Ԇ9x™SXW0$&w&Z?fK6qؑa٬iO$$=%u,+a"ܩC>(Wdٱ+usbZw;f#"܌[6#lR;V Yi’qb~?ef*[IꀭtZtA_u-h%0WNC||@ʮm[3oNsf}1MI`QJ$5L`+@8%l.eS­8"\3pCWCEبc ;@lƯPNʲ awhf -^|0XIWe^U1:f5dcZAߡm\>4__uɣ 8n VyV%+-(mZIl-V7k_6~^JCvжMg{wT9[2\I߻˹354̻ i׺ޝ;uhfٱ}T][$UZz۷Nt68@tSCǾN=482" kpk04N$81Cac^W=ht t]`Lx/Wl̀sp)n sװQK0aq`>NG+زW$SQ: ȬpP|L1a:fL;, }k ~"UZl!hF G.jf5U />߼ ޺qS?`+k%gwQm7+ݍ˒y~M=k==pnp|?[V68@tS?#jgRp0G[]SwMLJxd蠁Kxp9=q GpyښL$WgjB6^A6bXP+Q:F;|/Yۍ B<-с1|_dxK8\bLx`r(|ݥ 7i?g,Ϭvc$M#AS{r]0lUZ =;#EΜ<ׯw3'Yl D5wr"p0YCf4~mZ'!/+ m۪)g'=**2>9"­8QvLxU֬\!|%~N{)~ۍ 9@h݁,[׭[c5&}IHH4i/eaaaf ?߿5 Q$C6&b"~kU'o㈖^WdA_͞9k=_AZP1S?bR΂kl"b;t^$׼7 'Qڕ|ے0MekaH`y~[+p7-\3+?_f\1 u5.&x7.@zYӜSRVZn;a[<5/}/#_:txg5k,KڵkM6ݿoڴId#Iɓ'#f'''$gӽ]֭ۓO>yqkkI o cГhqD];0g7/8w~Z(>&*4(`޷_O;onw؆i'>nT(׋a!\б};kI=i|9GϮJ99ԷWOW%a) >Ja/~IG,VUfV~̧;!dYtv";{h^ coļ,vg9%oUH5 Ntn捣}U4ꚴb7nLNN޽{+"=(:::!CHP-z꩘$ 2.wYk?ۮg+}O #0xzG:?thqǣCpflE(ھMݺ" ]V-c%\cxy =@ׅH>A<ۥ>=6X2?r_۶~ `G]iN{`VKTMe铍;n*ƷEN׿U<222СC-[y;vL֯_5M4YP׮a[ L0vMTRƄųUo;veˤB_~ի"KÇO?4۷F{W_F٠AۮيK}UTRBvvw2[%vD@իec˗/ˮ@rڵX3tP{5 Tjl«|&''gӦMRBF}Yx w bccŔ:s 3ψXٻ5z(EJ]_W٪JGfx֏>ǒ4iyfqSv9,vH{WSO]pkćuk*[URƝI}ǙEļ:J [FӪUK.@RwM]rgϞɟۮlUJ#V^E=$L[R eTRQck ^UllVlUJV)IIXMb PTdd5c H-%6Z҆A(42>ڟ%'''$e&Ci)YiYPZvFZNfzNfFn]S[[WT@EPQYI1T^ZUVUBס +o^~[7oܺy۷nݮs}tݻݽܿÇ_8?u5k #ERt= &Uppx:(+.147:= X x(σ9`Z00 &ÃaiqPta0c3L bĸGURJFTR`kEbl.SIjnBvl-mXVR U%V+H=+ڤ%z [7z\$xE MauZI57NkV!F*[Ul5g~IgZDjA$[nVǭTsFobԀoRiՠz*\+I RH-(udkm긵jnXB[yQэr4[/GJ<"R J݄&Zv۰:nV5z>m;F_ i ԂR7!I6[+i*Ĩw ~Q]5Kְ IgZDjA$[nVǭTsFobԀkۍR(ˮzܙ[6;xPeEUV֞F5JN/a7WVՋlUBeVְzw=uio/U٪ZVG6Jv/{`kXm[(/KI+/+s<|xmaA*[U/U `k~o5J?Q=у{XLI/..>gIgG3W"!&JeEUVa1ڭFF6/& VVT%%kҒ+*ʋ ٠JU٪Z#֮}7JuV/{`oX\b}i|||4 fff﷪﷪\j@l{XT>#~[ŋVdIgZDjAXAU^o0Ш(,bf*..$[nVǭTsFobԀڹF.7jl-ǢW|K 5X3#SEGG'L111]W czHvl-mXVR UQbkC:(kGz*֫a,"^ "gJJJFFFZZIIIQZHv [>33Pvl-mXVR UV}?93rܯf,Eߟ=ga=*B.-hH6x$/?9y||qyNz =ֿ?غCw蘧׀KO<u=5PdrfԭcR?mRu f:jRVC\|9٘-Ղᄀ5 = 95ࢯhH6x$/h̅蕰2//Ub%tx/yE ]ҷ/pѓ?ޜgrp;جuI3gy>B'9txܔR{X6"+͕3"6/[p#DV XK c!*m\Ӓ.xPe^ҍkV"'jцHhF3'n%kj44g EJ|A,..d [zV,"eլ1THfzgik-۱9өwod@#ӡFќ/Yt*G9 ?ON~Hlt>J7̛l3WtЪcǽXӼm[挝9N17i'щ1?HK=@μ zk H١{w)?4iV%El-"gkn  lڡ`6T_l1GGV,]tŅ%Y)9iDH\pʟ?õB4xV5i?;"R% rؚbkZZa+NSaO>i_8a7qYK_"yO[ɛ-iݩyZRmveO3l36ڍ,>O&3ejsGNҙ?QNBbл}Wtܺk(o=yILH{lgS`N5!"VbkL[\T\"[ҡ 6#{`ʊ̴d(;#,b[˘l=M2_1m`Lh*ifffbj@X!DYYY~+N.Ww&{=XscN_XNz\sQx;9i|FLh㏿ޢE߷FvL~zo/4K/qN{Esp(҇}Q.H;YsR9XCxs_B8/v^r#+ J8Z p&1۲(3-]ZZ\X4"lu j4i4S_k43544w5g EJ|A,,,=M(İVVVƪYcl'sXPI/ܱI˖>Fg{'ڀ_[s1'uMwySi??a,&9 7~̟h`0U[`0];C~f-g3-_a#I(fFC3E$I|A,))AdZQQ9$~͛7X5kjjaў}]\ߞ/8V9j1[\ݿX_*/kʫ/\d5!#?D.ڼ̟W`ƌؤUWFru[vDV>N~Чg[ nڦS_N- dhDf덭Qzm-[ST"XV)[hӷ4F4AMdgHY^^ ZZZ b^YY_o߾}V͚/.ܳ? GMgʍ/ɧW&?e^j&ЍYc=$ Gg]z\Qo2o͓H%g5 &+?^)V_]5mJY}TǟxBb](~1"+v=zkۺ kv]⌟WlG#h$BYDdbel5藟w$xE MauZI57NkV!Fp#DmӂLI8Hɮ_O&H۵jGDUA%g/H<"R J݄&Zv۰:nV5zfmтLIRN֯Hd7v&@Y)DdQe[q_/z"GG:l-/U٪zQUС˗=ZB;w{3bn(~akHDU*[UNp#DWUSth[Zvع[Uo?hmWSR!>Y3.?2Z6sՙꋭ0f9[cXlENb*[U/UG~-G[N~hY6lsN^HJI6򽧞~2rlNNΫo$ԥmWv ձ셭qPzr"Y`k9VVՋlU:j쟭jҖ) c 3>O<_/c-jz@5=zW"oDC""\SV&cÌdL]٣ɸ5^3LM_'F7-&Nѩ>XGZsN$`!զ0bN]Zl@3^c+M(ٛFQgJ)fԫ9LdZgx@Xː5쯿Y;1U0:X^R-t-r V[ `E aEAjJɄf0}XH}B(J +VQGw1 fԗqH63=UL wuڈ1lMOM"*)*Fx=p[+Vפ ־DxpZ~EٕafQR(63stױp7${$N FWy]"w]m02, @'OecWx&mqIT6R%Ǫ648X .QiZ3SBpxh)Q 9`8|aa!B(%rؚ G (h]l=w곱=, t7=<;xگvl߶o[݈ ?(zDXZZ:I ] +P5IzCM|(  0g٨-/vefn>ߵSG1JQ^t\;etϧLٶuUfڶpXį׬fqlk++*J|1Uee^m*taU"6IDXV k9,ksX/&#Yg}{jټ͚ٛcEE5.8S&&%Y-!zOOa^(kzmP/ǝ9bxhH(qZ T)ti.[haܱhJV%ek=½u."6&/hE<$8̙!œӿ3H[Q?LhD GYޞ8@|,2|ë{5o_Һt:6Mk،ew@*u /I ;dgz~8xi!A[5+3\4˙ݽmX-u3lxUޫBO `"<ȶh;~͚N"Y 54;;֭M5P?Yhqc:lAp5dDs!G=uE'₂5lhbb}U'(+Pt¸atnٴfjfdJ$wsrr@LoBBCSy/ Ϟ9mR_دح4lDjSՊq`iOKZ?`%0\8Gfzwhx\sNt2].HOz).wnҡMkWWę;c|t۫'.K .;l#&E˖l-Lg慟 pۍ[m[68ygw世xΝ9.MIgj/+bkS qyٙ5ʢx*Wٵ2nr(_!Z1Vl1S\/@E,UhP1SgY S,sӶckYF [ڲsbFl&-3y9g)UݼIDG}pYXO28[$DGWUU$?+>4oai)),[\\ܥ`x`,(`fECҥϧ|nْQQTZcBNVPFa֌%BL0c"XXgY@ZZRb _b/]ڽkiS@0#Ӈ*.e=Kt_ALxj<|G:]5v橰1כm5I+Lf/Ce~1OIAT)l2`ezrbC@7U)1)87}`JRsgh? O`#GhBƏ=w Mpb|%Nϟ=MWcfwHLbBBpD8kJ@|Z4k:iyTSkpĸX&fkllVפr\Y0qrúo9 [6CW\&yGnv!99N;t,#-%!6*>@ \į'SG9B\U5~ys8/^"wj[w>EѪE>={,^:uD۶jIq-3e- O7b@)'uD6Jt@ݥ/oۊf߹]QQ~OxQ 9ўe^bti-ILqM{ǎE  N|Pm/<>-c;>-"֨o3EVy^rRNNKI}|mZ8ɴ>y1)15U8gh[Dte~uuE"EGΙ9o^ COb :NGb80rx&Yd.b]|1== +Ǻ8^s6MReK/\X4.6v䰡G{RW9DiM|޾: yjDj-/L;5QD}Up5pUXG2}}ÈqI ?n*3\bN}cHDvJΞ<^PtJb|yY٩㎤w"'+a+z`?M|'$tݲiI7gdxtܾ9X zǣRdge$'\ٚcH0>W˸(VC|p5گz2%c{6_;>na~le)kǶE{]9ڰ{w@X[%XJCJ`=qÇW5a aʃXoԒ͙| 05T6|7;2r4hPvCtK#BOO .쇍ߎɷ#̩JP40wV_B 2Bl舕,lENlVhO]}gWG]`+ i)I`kWO r|[|bcP,a&Xnl,sӬ(H1acn X/דP*e]~C %/)?d1%Y)r邸| 1΍@8OK>-pۈ#nIeJ@$C|a|DQ;-aBdmYj+-ˋ] /'MqLKa>ja$^7*>?Oyaj3 |P"R HCE 5T6bkx.V~+!A1i[q+d@8~yOؚ#j9KxER/$ ryx8+$ɚJS&y+./9IyL/w}Zn6+&`-V*[E5@|f#gs\E, ք:Ji"'C;e66^9jEҖD3vdz,Q["f7[PcH* J~*6FTUQ:(1&,ڎIeYiRfzٙҞ}ɦ$㚇rQPpkǕgllOjNçl цfk C r [auͯZ|Gg ,q?jV@?!Ug_IŏIz FIy#Si9L,DREUU 1J-62kauc#Yf럳Q+8,+.eW|pl^d)O[qoTFœThW L툧HZ#T5Ξ1V&Ō>zmHNGm8S̑OK$ԦoV+1>vܘ۴jj㢣xNa rv<x=CiBHv`ڶ9v06jXJ%QLEy3TrCj2).*-"K\?[`SwNy^5ťLA%Sy0q)eZw6{Rl b+5;[?˖$FXVE`l5@o|xT0WnĨfMb qǎ2Di\OؘsӦ|Fqf:\Xx7+GeD|.µǤikn dsXqn'>\|YjRw|| ^@=xyʈaCXYT;r ^ '"v1C%TQ,YW='>+pL nqY+Z>cXmǝixuǏ IY3/_D^O *t5Vn.cJ-#-%[[#Aԯ5NG5{Vgrsh FќVBZ`qqچDfZ*k81t^;;t@ZUF3/dѵ b@̑FL"\lxN 5:]H4.M75 8g̑F")!Hwh6R9L%Y޾O @sJz 1B;N AT ?,Yh#8n'@-0j'){uS!jcEjmܚl[C#l dA,w咸UbsVak ql"!.Hqڿp1r#%E`}D"6@C6q1p@<{q=ƯG@9?,OtOg~1._V@\IGF$jҔء=D!o.?c4ƎYtK&kј kp}ZU!n%0d%=yH^i*+дDOQH*i&0u2@ؚ(zOp?-]S~id+tH 1`cI++ڟR=vǎ^*bot9ٷtNW><7`3'OiۥtpkΟ=MGSUFIƗҜuLO]' Y9g֌/LO[xќ33Tvjfv"k6m۵nÁ.MNLظn>%r˗ee/3VKV2t pkukGU,AT!i4hӹ1*yE 4<*DmhHU~+p(!:, ʸD@﷒]зCΎG[hW/+}{ءp|\wNb[4(&*~<"{YI:i|zi|5lξp,:>sꤨOH~9r0/,`[ÆUATl(4 ̀Pq^lLԘC#>E: A+:8}˅#A̚G^Јk!0d%E781QPd,Ix 9Hp\*UdHP ,_i={.&/䛋9_{Kd#j)]+_{Ml-Ѕَ Ku%FF՜;9Qk~ZFODi"'CJ51P<' i\QA "댴?.px`m;&ĥn A]q KRTBlMjlf ׻D{lɍZmϜ:ѧgV-ȐyI\IM] @7JñΟ=ӷWOt{$P¥(nZBhc5Uu S?Xyj+ h=Y5n}0o(^ V=hm3R1ϑ\%+{wm߰f5q/IvЅ@.YP% ǡ \Ve +SGp%ނ1bs5{vnw jL~aLjC8,lEN*VjL~ j6כVs[6p^ڐ BAw:r8zXH#[ `QYi JefK&)㷂AhuBe2: Ix'ȡF㇭yNddՠ b Z"h@HYXk JOajL'VpYİ˾kvM+Nx]Ӈ #ʶwbn5|hrSlYVR獹ZP1 SN{4@+){_=[#lVҌZ51>s%C҉Qށsrn@Q~NCN׹W!CoٴmcD_bb5V5OYqYɒkFaʥ8{9D.(~q^@#Vʃ|@sKKXJvMaeJ}^Ƽ PjM76r¡'$/2sDQ٣ /7VBXiѽ >'qBXws,ؚ`EZ$[#B Ydk68@)crju{[e!g[ōʜ|ngͪ0D _g&J:"n[6?߅3ڳ{kDۮ6 Mln*4 m[خ2՛l+Gu}M7DT<VoHC [UM[4_KYWɧmO|.g]OJ{0ԧvK۶$FI1g"E3)9[Z 6Xք^Yh&"^M\ck.u=jV\]{i V[9C<1//ljW'qءw dںߛUwwׄ l٬RLTUU9z谰C_̷:׿B3gܲeL&okkhll7o NFBfuB r{*l>avYn:?02[JHZ+}D#H+IGLؚ%7V'm7Uy \UZm ^I)k 7oXwؾ}˦]۷߳+dv+yCJy0j8=eŃmG*s2yF 6)[Ca XK|RCgػkYB^l^/-<}߿Cl3,鴖=1xۏ[{82i7ͣsҥ᧟~jmmݹsgee~Kٌ 6< srr /^Hb+CNdRmWh2KZ)F'2d0ļF|ٙTWNUVZ mM.0nuMM557}Kxsu3x,9{js[M;M}Tέ[N=L:͛6_~$ȡD;j^djmyN \%=X2j[?ca[?wn__B&;gMv(tצ-fDN?@~ocjr4O8ٳj! ;-QupjokgkŽG|Gǎ۰a;}E"ь3NJŔ#CNcFX4Z,Xԙ@rB>TxXS ٷ׸½{nڴiɒ%s̙={6m+bvn2믿N0kYFCzHCV撘dkP &~R'ZWro*ћ*LI4҂+$1̤Wrwr!kw0#9+?/όͻZe#odzu>t"30J4 i?ȑ#oSO=裏+_[vȉCy RTmJJʛoIW*ܶmyT q_>q=OU+WWcǎE}SLyx )ؚ9n(rBɓ'[iT\%k*[ ck/7]lCwűw(ow)QG52~*36[{OG8q"B͛:ztPА[6nphQ²jv₰®C.Xt"e&N=-e`m^Qqs6o;}dhj^Fn\n~oZ_[qwկ|ObZ{KkN<4[-YfΛ7|3g*j˖-Ě'z#G$^)Ǹ´4*5m4Bqcǎe9l$OxVlC y8 8=tQQQLU/O۶WzGiKq wm!_:ɆZX!?`kZ]U<[9F}|UVQi:INtVǏPX7{Y [Rɀ[ 樤rq [)?-lW6Q3SikpUa:SVʬ/OqldG+bjQ+] V˚DΉGmU* ;*IOe^.V}p_SVZ!۳{ka+.#1mCSN%L>} ,#%.G-Uh]{w2eyH,OFo7x֭+ zKؼAV,*WU\/-޶i튥֮ly2 =aa8xm.s榃s:j- ;47Ywca##bJY[G!059XR(8:7=C,$޵hO'Q>RӇ \%5uンG7V_%pi,'tϮ;A$ ۷Uxá{onf*JuI1Y*ĉОAb> 8Dm֜8|0hة#QN>{svߎ-ZUU BrV)'%XOiHQA*K*g \,-&&{ؚ3G?biDx\IcQy9dV lS4`RCf^(9YiNS++Kђ'}F-kDFl{[+`+@K uM MPA2>hCdLؚ˶ٔTSU+& A嚰 Ϸ[]{xHր{ ;3 IfVU@> AB0ekV/nfS~RMتl`^9He&8/dWd41%)4 ER&At&h0!07NRNOr[MЧpe*ewW祋A]]t>tVP0ɕ5a`+a.\*J.Ӆ绻At&/DgEK|nC;taґaoIC cKITXNQOy:+:[ adgKbȶP G0V,%ADNܥ:;M/^s:u-@*P+>u2(g?غźZ3VVdVg _ZϞh7ѹk35Fjłregf=jMgkfzalɢ+ٚ.bqlhk79V;bl󄸘68r#OE$'%J WY^V$t{vns7[ s2fk&'Tlw[mqdgz[n?v|_Ƞ?zxe'?,#m\)jo6O~чwM7)o7};}$~u6W_V J 2ِl% ^8t9a-Z4[\J9)?zq-%9jgOGu硐dX rjzEx&*%qc:SXD{޴}s^tIa~. Hw׎o4JO[b9^Đ42+_4娤bTj(q=l1c.x{FfcR$#=LTmOPќ:yXʡ?ZBjjJ2UE[uaV2SdOEDT[+V-;jLykCR]Gщ1HD~}GDbdr:^~,R&:H@R&$rhkRn,e<;F*[$1y]cǏs`b"N\JھDF)kRp&GqTvFK6MzDvx00}M5)h[/45ԙ1xQRLsRG[CwQc\TNfE%hs3U‰=Gr4qHHgl}W.[JdIo|3mXW_sN.]`1Y̷eK~Nb'[jMDG1U/Q^7| kq=l1mՉ.D-|}d[8ĄGK9zVvz61^6[a?E;m(J)[\f=K;׶Nrw Iwl'm;5(as2c?D=~$I2E&@@!|cGXe"[n,e=Tx^S^vߣoM_~)}m1NaB>v<@FjlbŮ1bt%O{˓ ]QKS]u6 GMJ$}d[B>bHTTh␔tc)GϜmmm---}FﭲVc1䃼3-2q}$:0gkl*wUQRyg^B' aZW]oo6c7JTbȠGcsӍOtV}5%9)*^{0Q"P[Y\ fkӵ55͍~֖r^d3[7 r}/B!7K0eotաa ScϝX|Es >ak}]j<U 絛[fkz*7nfeP~RU9ֲׄOC"/2ĩUJesS?H46@gEf@J|ҼM(E)f ÉqeM8=n7[)?]l3/QU2iFr"m)APj0/m]r?΄·Ί y: tuvCgggscNpb>a`+Ḿ$YhU\$LONљ b@,CE$/.2Jx`c-BF8љyΘPвU3R$%AV f+&lMs^: }WTq"A!_([R 2fkf:'U5]lU..O!( FlM.LiTUV BAZU^ [9`+AJPz`G J1 [=r+oZY^[V?d+f+9l-+IMr^B@@@@0eBƑ:ԤT{ٚJIez7lE@@@@Kxak~+?5~+؊%[9r # `+0`+ j̷B@@@@0 jlE@@@@p?[kVleo[][෺ro5@twG(|V~B͈/iЕMt{V5)[`+0zluW,kV֯غ~ 3:ЧBxw?>,EnofffN8<++ -l1cl)qVuǎffs8׿-#Č3x78pu^hX>E֮]{1yR#2qڰ`[؊eaqh ale∸L/Z,~(b*$D H>"yA2V( eB$oEI_*4l%BXȓ( <1s̖+T+ H|Qa~Qa/,3ly‚<7װr ~~//oP6//]ˡH&E s2qIܜ Yg1[2R2hۯ\ڦJK&t);51;e@Y) YLd&1[R|FbKO4l 4N,)=!qbcR :gPA)qgI=KIm mOtLҹIgO';xt٨(&DGr#D"gH (`tD cF6p碘1,9CS#ٰ5LfIGӊ_ft<ibR6)>?BY$fʧ4` 1hFlNFc 2#YA3B65h6` F&3h0Hm7`B 9-+l5^2F󹬉&sMF`cޯA#AoLԏR%XaACAY$%|V{M&)d5Jyɀ%*v@JZcQ1khJ*ӖiIzF:~IeJ */2̠UTVTePmUAդ:F5jR=TCj 2j'ձjjg[57Z4 j5àv: j`ɪUnF݌zΓΓ.zt…^5wEKK.h0\& # # JOF0C,Ȅ92K;h~ ε== H3qpVf(M~ONv:s` o[BjflHks3cR e4765J$#cU8h l:16֠J6TjkH+ v,5f,3[Ӗoõ  k ЩI@0 !1wyLl  P<`0LaĈZ+Z6.H-jmuLh{]顆ebM70?NOLlȪVG `+ ZTwlչyR&a`+ ~>,V\ѻ{ @]`+TlĊ[᷂`+ `k`+ b`*"~r` [{m` R xZ{>.#;zWza=t?{C ;LƏӹ1BRSwu6*r{Cw`+ c[kKJ&a 3-l[Vl[`kFSY^U+FU^^-р 2`+ :V_@beAVϷV<ߊ[Vi5?jl[VχŊ \&Vl[VUV\$ |sQ:Y؊w`+ ,[Zݼg~ͬ/|5c軨d"?e~+l# ?VI#Ysf͠wߐ~׬(esD^}V[ؚ([Vl[VXzKܷb_žA47سvMkWQNl{ {PN `+l# rUa!I!2G [)g5AZ l[aVuUQ3lUJEjXP Q\E [)UJ+o[  Öz7ȊUrF vo6d]7ٺiwleuDc DIUV\W7tPk '#z2>{ܰL'޴ ڂh/Ӣ[o MLSj]|l[VlGJVl[VlVIôl `+؊`DRY3y=kך Hl[Vlu[jVFV4`Q2l[Vl[-5d.B괷=;oyp?eMl`+؊bkcG3*ʊg~ѣD>}硫lVl[Vχ ^}KJ4 >*bZޙq6,[[ ?`+!jrBsBV^Vըb޳[kSk=k 'wD/ssqzĺ2=q!LWAxsX!R\bb-\IتsEFn|!jqlYӦ6.cbs}VDn=llĊ}vl[Vl[Vd"`~Q`+ `+ l[Vl[?S))0[==q/e[V+.ʗ椧N|c5[Vl[VUR&r8w6/eeL~QߖU)`kY`+؊c+LWr8[Ï+jVl[V?f+U+ ׮*/xIXcw{2<{QS<ߊ[=t,VUWS-ikqEqvM8^\.`oIcM6Y5-csmru[VlĊ^-e+GR`+ zN(ߊw`+ n`kZl `+ `+j>q/ 5awL,(dw|'V<4Ϸ%uۆݶ9tD5lŻ#VFl[iM쌍<z]C,p*q`+؊sپ3'm#'KR[aVl5 oM{w?B=w'dׄV`+l# rR y[k6\K7`+؊d+9ɜ8>b___ooư5{{gk##l[1~V l s6\׵!{w%bPX_M[˥vpQ5@eklջ >T'.H-jmuL8^/7.eb72F$)/3=D W4*xfZJ~NfVZJ˗.tKyLJ夥Ez!7V[1~ou|XH9sP;w,?W3-Z{vJtk^ k]bk\Nc`+ yeg%'ƞKħ$qbQ [/-.y>a[/[Vl[=VFianm)n#'S XjVl[V!q"  ?`+bE0[E:l[1~V*p) `+l-ҹ  ?G8F;`]\~  yyu54VW|rXÚ0&Xk&9,٪m '3'#ņ(esKYlm[V\^O}j*,Z/뼥6F]/ `+l2+*L>Ά(eAD:]l[aVlVaವD*PW=Z(3q, 6`kP59 5c]l-Ɍ:qBOO]u%+DޞT  Ǐ+eRKyB3 ߼, 6`k/* &VYvVTlȪۄ쮘}WZ5mo 'Y, `+ [㉭y92nް>ЃaB]eYoVEEl# b6[)AR0>Gω=5gqYAJ3d5R䧟~ > 6?Ó'-- l[VY~^6c!̢E`+ nc$`J~ko`+lu\VlZPs… 绻Z[kkuH-luj1lm[V٪׸T,_tBOSmuFHZi^!ѫzFT`թ[Vl[aVLiMxjMjH\,k`+ rw߁`+}9VXTW[3{>T.`+"7;+eau[`+ [5ٙH"ӥʙ}k[L+kUeUezVTb^I6j[} lueX`7je0[g[ݝ̈́˼w5W9q--MMMu5zVRe2kvzjsY*[V? ]=fU/ldߙZѴ5^ꪫJK%*L4VM1\D\lu #`k@թ6zCǃ)kࡆe=1=yPƢܬK.7/U4];o%Ι=*eRiqQ\$`r3Ï)J[Vl[֠g+G*E߅亶47?{W;nnkkkjltaYX, S*\.EEan~>/of[# `+ ک8UF^֪Uzr%R1T[!GU12`U!bH(Ӛ[+` 9ylÂ`+ `k Xk5~DhxBA$%jjRR(fks \iȰ47MeyUUT5675 `+ AVc8akMuU}mMEjVl[ek<_^EjjJ p>TueŅ RSRVl[;[>a#;wb3>f,[Vl[ϐՌDNVFFbQe91ָWmţ`+ VÚpz*#'ք)2j?HjRgɂVD)9ͫwsf=e-l[VlA+J2تKܸnMkkzZZ-^HI)J߿w7Q)59ҥKvؘof}yHJ<K&>3u]ھje-Śf[Vl[}V߲uu5jkw$^ݤԑC;9]]]Zl%gP".驩޼a5jj;;;vbYG[mVl[BEnz # yB`kPFl[/+0[R`+귶l `RQqq\bk2#l[Vl[SVEʯ`kwwELWJe]`+l#؊Vl[Vl[Vl[Vk…&~k `+ [o-%3bZ^/Օ@RElm[1~,eO٪tVB `+ qT\r3[Yɉg#< F{+pkR*Ju]N ˆWpss#P =,l[ozV_+IVN7;^"7/ɜ#u=(٤ԙSO>B"ORNK>1Cٽcw?v %`+ Vi ䷾֛;曖/]Khy.Z0=eْE2Y3o 'ÏO tϛ>l[V T'6[ƿ;~|\$౻<.]B~!Eh}wgd3)y)V8HgL tVFl tHVt%j&NިVm)$ZVRD-}:IO6qGq&`+ `@,l,k,w@4;vM"rQKԴ88vԝ䁖s2ƥ=wL\,[`-*k:VFl[*5tMlӏ,\O>f|+Ģ9^~)1>Eǎ!`+ b:[/ɣnW_8H|B[dk'|'qbْ̽`+ ?`kp-*C`+ `+5aW4w͝`+ ?Â`u2#>p[_z4l[VVˤ[:KuZ] l[aV5z;c*Օ4*W*ӕ`+0akLdsl[Vl5p `+ X `+؊3V. `+l#؊ `+ Nl[}Vʝ<$nnj50 62CNTh[aV5n8u|N=|0 `+ zÅ ^,X`BX`l[Vl[g 0'²x[#)Vl[ڇa1aRuݽcM7\mFkh`+ `k5߇lU6zo65''akbb=l}nzғO`+ `k5k/[,^MB]]=5%MeIEK0j=q8D6`+`bD]RI$e,2xMLLDD+S/2Ə#(,7/0j#q8t`+ 2[MomV,as$̲5/ajU̮7ߤK)IXZL[Vl[[VcR 甅,Mk^GNKNb?f0R|ܘьbb" `+ [!K1aM2G<ܤL_xvR+-?,^x?o`ZL[Vl[@U[9kLX ukL7oXGIN [gbYkql[V[v8Vs7l[Vl[քMڦӨJItV a]aKQu`+ &yd `+ UQirW6m`+l# `+`+ `+ `+Sx. `+ ou+[M6l[Vl  [[jF!#2\bX$/.R@"B[ r9œLnvFAVz~fZ^FjnzrNZRvjbvrBf'3)>#!6=!&->&5\Jltr̙sF%DG$Sb9t t!t9tQ)IttttYitljjjj"isN[aVl hr\n7bFaxzFUZ.Ӗ2}UyiuEyMeEmUe]uU}muC]mc}]SC}scCKSckKs[k sB =}.]O._헁N2OT{/\K ˡ ˤK ˧Ffơ&FfƤ&F[Vl[gM"c+`+ <[ ugClm[aVl `MZV25u1ͮ[aVl`lֶl[fk> `+ %[󃅭-%*TpajRq֭^R*lfת`+l# V߲u]'ÏΝ=q"?0{Vl[ f+'`ec gOGĜ tCB|}駟tvvg2/n..\PR9\too/},HnoPy4569DI % ݚ l%ò)dhzqֶ~^#lmok#G8l5_x`cė.>7'ኊ-zf/1 KUEy >=uUl b>-ȷ-sf4'Bm&c/o: VDlbM:UIH>gͪeKN<>wL:::/]<Ζe~5skl%4>6Y_;R*٪՗m\u}f[aV7zo'֯^e|뚕]W.hs{zzj7oXg[7\뫫ݽc `+ +lm[aVlVl[gF![Vl[1~VdkUB> b.Potj\)@lm[1~V=)672T JԳM`+l#؊ *M`+ `+l0 `+؊M`+l/[Vl[V&}y`+`+ o \zgMIOY$Ri`K`+ ?A& _᷒UWɥ I{k֧hٱb~Ǻ{~ޔZ.mij[aV5e[%E{ݕǦ$;.H+/1W&/3akNFyȗ^x.35IfOyo婉;i\IO3 s' xE‡[Vl[VoJVl%-^0ysُYpEޟ.qǝ?wά/0a>N‚iL}Hũ}{'=I6ͬ7cjsfς `+؊5!J`k^V#> (N[dQѣhEkGm3$;)kq;G#xvz* `+ 2x}4SǏQTO>IyӍ׷r3h#d"!EhKqH`R9[>>-t^a){D?Z&iil[aV5a^nle+V<ȈW&(,,3QL℻ &9L侻ǓJRIg]Y֊Rliɥ"s.ڿlm[1~VZVl%=SN}gaSE+qnKMG.v9_$~ɿfoNfǩ||/>_ `+.ʋd+蚰ٺkV];ؔ쬩o~m#S'M@^B$řDnnr-7?#gN4)w6?v4hX(?:P0rĀ6]8" |+lUI%8~ 9qlmPrqQh߮Mz:Nlm[VuDdNlؾ]Q[dkl䉓a!Yɉ"gׄTR1yA {:mjl[VJҪG8t SCO;*䱜dJ}l-6aR*F|}w߶yc`6 AV/]eX\Y4^c*l[᷺^&'IˊD;年=lB iK/I tyلB3`JBB۸&ےqoVYq/$eٷ~|U,l53;3XG3e ݊c#݊n[arl[:vVѭVtkDlC[ѭ86[ѭpA+JO+ˍ[ èa6Z :$Vt[qlD[ѭnXpQ b%[[en5X Z$V@O;ѭ86[ѭ0[AϪb@&n5,z-+_[qlD[ѭas+>;qyiɘá vZqqvcNS[-ZF[ѭVt+5.j[iO:vXo攗EɭccVt+݊nE[" OV. Eǭ/n5H[qlD[ѭau+Xr+KAq/+_8VѭVt+K{GLՊnE[ѭVttk G߭VfJ4JP2Uh׮޼+%rvMB[ѭVtk քѭ!o_j̣_~x(3(mߚ݊nE[ѭUnvnЁ"L$Xvj; ONqAO~|myq-A[6l يe SW.OLJ\2uJ[= 4ԭNZW)\{.^6VwS .fWH޻:ֲX9|pE eX8.G؈nE[]ktk(\<|9]`1fcM j%5jT;?6nM6]>( 'qNc7|sgN|!SP֭dN$_̉cd2URZ>Vt+݊nndpb9i?= DvFßPLGR_~ɽ}q7_f$iσ4^m]rŴniϱ`> #݊nE[ѭ1wk-VǨ^^^G[YSo[ׁ(-^fF.ݲa}EI`?Xx̯[}e%,+}1l![IsѭVt+:[O]* ɩYtCrVŠ5jU4h/(L`-%Պi H){2}t5=*gt2TBwTrHڸӶI]b}/A9VѭVtk*m߲P o[몷nܰry\BDɭE5jT-̦@{]r'`K?wO-R"hx,{'AI>㝞uJ8{$Lf)@AΜ f? 0h;q/A[qlD[ѭatIb1ƺ=HxqkT+#V6H-JxZ}>-%|kV=-tܒE CtkƣTy8͓# |h>HSj?,!}1ѠVVMe%Vt+݊n[:d(-)qThV>r2ds+nE[ѭ7Uw޺yzޮ'Lq˭F 1(Ft+^}Zf/dz[:ADb݊c#݊n ߹LH+%%|njEܭN[gQl|pR*M ~z݊nBebMS-?o[_v[IÖXbr"I`-*`VA@bn[A"cV) `+aDW P)$"t+A|i5izQt+ v+k#ػv{AFayl`ܪOEӺBv+!j3iUz[O? Hsďyb+~|42k[ja iAŭ`UX8"?'3%+c>7\^N2100(߰1q_.ݝVz1eɢwUK~q/+.JO }Uǩ޹ ^(xqFiX-@tZJBybD jZuiaCg2 Yg11m$ƘMp+ݼ9 0;|РR,v0Wq C57ȋQ(ʇtQ A e$'+FH58\Ǟέ#V6 ,2rTVmؠVh@+I׮ [8wMk T[}fZ/Ed0o捋߼a]Q^(ኄ],)qYz -I=ЫACGb5{Ɩv`1)i~H-2yDݑLFENx5a̱Y}c67fC19 1[o n&Q}&u]̆is(?*%&jǩ,k0Ҵ{R UiU@^D%64XZR c StZ\qh\QrLȫ50ʁjZqU PTZQ%!ljœ ( Ɋ=7٘K(SAֱòMDdVFyb5^CŭK- 0o7e;> n pP? qmbf?~xڕ4@api~5+υR!>j4BKms2ӕ2Bc S*$# @9t O$OY3FGUC& 6ǭr\bwn߼Un:myoVHLpW"I2[WA$ιjvZ*sIZJG{jeXnD/+}̴TT$q< [QG.Ua/[f$.^ʳ7TҭfƯ[J".3_@xutdHkP ba8sO^N9G%$7*/dP1)qYs} | ˭9E9Yܖkx-7_>Ң)g!BuCP go@[)yܪᖎ+1 )n+m*P.%36_|C>o@ X.YfuQ^΅3K.R7':߇ H| ?~Ia.z}VQv_/]rȟsbuD-py*FtA8?j1j08.q+nG7"2avS})LTa iș47T4n)dg =+/ HJoqL##X?:> stream x 0İ2A_4}٥u"POse/9LGȏp7 endstream endobj 340 0 obj << /D [338 0 R /XYZ 69.866 808.885 null] >> endobj 341 0 obj << /D [338 0 R /XYZ 271.182 243.799 null] >> endobj 337 0 obj << /Font << /F18 192 0 R /F16 128 0 R >> /XObject << /Im17 331 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 346 0 obj << /Length 2100 /Filter /FlateDecode >> stream xڥMw_K,.AM/馱@Ć"\ eUk/0oz/ͻ" W2 ,M=A"TzϛLb? 6,J/~v#ۗ6=}y1edYf"GO7 0$4 toW'/ dy/ 0%k7ܐxŁhnI*0$< 2PYD62> ]WmCz#Ib6A&H o\08 'LI$2"}n*5bhx6}W쬆P?#оBہcѕ/xVt#kQꟺcu~ XHiϠ|GZkGv#It( JMG+e67,spю̴g^0ӆ<8NV>˰tT ];Z%#搼v1=4%屯 CZD1NU]tcLJIBJЯjiW5' 6h~ S, /up%T#2Lݥ.(B ^X4aWzš3!S j@p+D‚4gbd$wC5pgQӶ_65\ n~x;򏱜]BgaKp{CjluvWSyT vm=,W!( ?0xg ж8;JDAc`Kϝv#ӭK6WE4i>|㶯%t&jr*AYɡUzea֧Rv]9(˵ Zl\Ȣe40 !I sȣmfUmzݚ&Tkvŋ.Wqa\+˙v8K8S\{T`⤯AEPkҷ~CXNsMAglJ' 顄-f̬m*|>ɵ4/x#*h8ȗjn$RF*He ^@0Tk]kWM~mI"{E krtyig>xg>[*/3p2BD+U|aBk[n x#d)5dfZF)z;bKaFa0G>jTS6ã1 aQet]J]eͷ`^_z~Q'.Bpjj׍ )PXY&!=N&M4Ng|z"^_f-xdn8慲:|w\úśmqeƒͩ8%\LZFGb}#ު\yy*+P!iƟ)EMR} 9Y> ljr 7I2mTq lkh:{لDL 1GO̲RwS+(cSgesj@.yUpU/2tOL endstream endobj 345 0 obj << /Type /Page /Contents 346 0 R /Resources 344 0 R /MediaBox [0 0 595.276 841.89] /Parent 351 0 R /Group 124 0 R >> endobj 343 0 obj << /Type /XObject /Subtype /Image /Width 865 /Height 597 /BitsPerComponent 8 /ColorSpace /DeviceRGB /SMask 352 0 R /Length 67900 /Filter /FlateDecode >> stream xwTY9g=w}svgwg(9ͨyLcœEEg1iE,(s„&(34I%:p:Ѝs[nED?bNP( B>.aeoF9_~1յ A_"Tl QB79Jʏ *JM^xjgp1JqԿWUa(74v꫓}uiklGgŤKw=t3ɇ"b33r3Q検,PU+_D9 S@D oP!TP:7A:ԩAJ IA)%K bTi*U*UBT*eT*cR=U^՛Fz֛וlSU*ժSuT]TSj-SokiR;Zh=;wZlH#}4M 5e&-IgEgT}itV4Arn(BԔ)]u%^_*+ TĨ]PjVm*]o+aVCHL9nVeTMRZxKI^U'FP]ո3a3J VTԨj4kjFlĬ ؂~IyqO-5z_U9a G7N={hS_A3}޾({"Mv@Z0<("<?޳ 9 ɦ uC.>l|L! 2-"]= 2_ao]ږ+nw-T\Psw׽.حo±JKI y t_ՍƗJCD<>f.=/nd{mk{"v`'UBz2Tb>D-7*%J/镆DȐ=Ö5ؽKgBJPXzTWl=v#Z&ѐ6|+!6GCu܉1&B9CaDx#Y[}ۻW%%b"4́K 1(nKXKtEQac=EԈ`(~ۧ7U[6UyyLPG[gV[f.B O0 5; %#竫 E=b!!߿Ua |>0di-{ˉ|5:uDUUUi)Tݻ9];90Wj#&O!̖?)DFٵ#>.n%L.z@BdBB TWlEѺ5gL"2/"^2{B ).bNڥ=B=,* ٙ#5oJ,l(@cqys^n牝>h;P3/kY <ݰ~ݴɓ9'YZE.bXs}z) f-OSZB41 aIîPPf~ [^zUXPp?0oo)RTSUIB$Ce!TCeN ?b|X|hi  ܻ{wa$ EhpFZ ]i/BI ґC-[صc!Dxp\oi) &su?MԖS&0p˂n/oqr44=y |dODP%>,cљk:Q݉4(Zi ]5nPXr+\3fŌi&og3jp 4bE F Ba"''{ڔ)L:%7'bEpժu+}R@Z[:mw! {F  "Fy)}AFس{АF5>u*jƴ LҨ7_ߔ$7!>b #~緽{aE|ɚ!t?uu|k)&˅ D~J^^.GxNaaP\EK\9u1"C&CuQjtIARv2OAW̌'CN.@~p]4Iq1NMJ8nܜ3:ѱ_sYRrr>G%JY$_"fe0u޵s{h^JNx9ݵ3YGJrT斜~\|A$d󈔥](NFrA[`ZyujčyTdVC] R@J %Ŵ*UQ*.2@c9Ke ,XQ#h C9zGGEˆ2bI!f%̟m˫W^P+;a~^yx ׅ swcsߺxB BgJȣ(*~tֽJ2%%k'j1Z9Gi|계nn1S @meegEX-ZDO?Ѩel4vwr^Qnjv"ȁ*6jwA`lioYgg#& Y@5 .Ǖ?-///@h2ƴ.)'U *S4p]*.Z $]I\˨|J||fK^F+хSQ@ yW,Ecc`ܟ|a 6I#jJiӲpfgnbSGDDTRxS=4# /zsZ 55ـbEїHM/i>UU|G=gWvd+f /;ft֣%Uy6^ o*& 2ٛ|p j8UlJTa2n'hx0YTC4Ǡ i 3,aea:r4>, C.8D>ԃ/E;hΆ'#P@BÑʟn `@ ڹ{Rn =Z5ݵǍ3ۅcNB{  DFN7t@QIE Oy(=pcX|Y~^. MIE拸/ZufEQ!l@ dIR93Fs#pՊ,u47ҺR^VvܙANA[pW߹|Rbx'=-5-y; -5 ٸa=89~jx<-->#~aݹ >XM+[5CXŸfBjsqR{~d=<\P{X GHp0TB@n*DD(2ۯ"(w Pa~NǸɇ.W.#G@b"*;>&R@B++d&;5)3#g:&DǞ].Sv/sx zyԉNDGx_= ~'RAMOE,={XY3uhֲc߾40YFBh C+/@ 1#GDGEVWU1+p)1b5Њ{9AnvܥUSS}ZӹT`>#n޵c jrr!۶CnӚ*C@V;thyYⰷ*;D}?zEtͥ nnD,+4@13c3}qxMת'WDޖnߺsO޿wKu .A;фAXQ` g\*\kh7VM*٪"U%z>pcZj*׿DӎgTM"s|NCxcnjmkݡ ?z"B1 Or& Mbѥ_)_,wCUyڝgIY";msw-a>|)WlBPaRQ❊(}jQG\$ FQh$Fnz$HdR_W4V@2,2Ywj!("]P|P]sm˴aác)8yht]=D|pf MU f!55oL yc؂~$7DZw&E9(iREqLtjѐHF$FGz0))#V8bHb(HYd1|,$}E 9 u'Cݦ uPˮȇEDt(i?IE˴^mb$FIHCJH+5" B0,eЀd(:mh84gpш$$"~|Z_li\ -䰻;r~EC-oH\K/b w[E-nK8> C qk!,Xz?u]R*~Z)x %)IWQO Ϡ7IϠ4 ǗPH?G-ŸDJO@ďp@8~: HADt)Ѱm PEiŠ\3.rO1B# xL<H1bKC&3dhHJgT 1 !) 9 JL~,OFCCDDT3l&̔τ90wijwuNFH%9V$Eh\&cW bO'7 )1r.!!-dpbPC)7Bu\Ę 1f) !h* ̓e>QXbzHY%K&B"r=(>C JfL^hkD(1_T-LJΩ\}:D}Υ@9Ə>RĖ/J^F|a?~AҒr'xzFOH>% ƍ.Qq>]"gL4xAćM89~De5Ory_BauВ ݱ>K GOI~YQksHA1K󤿋[VfEw-n KHltdOHʣC&@>2 EAPw,zGM!p`>0GkIIA 8@8D!b "" P<ӤXQSqQ#?1*yQ4JoGU4Yn]%@"47( |@(Ą O % Q hIE d\?(TG]D~ä!N7 r4H4("LH8UhH,4hӆ(cO*<+UiI(ƍRQ KIQ'Iƕ9'օ PiP*];&gB`a C6D銈Ќ2 Vb#4FF2zTAxRvP*?b@"J@$Lhp,4(!N%DDTaE &FI(ƍd H"%L>r|^L)E PgP!Xh C$C"bIvF +M1;彉Q'%#M\/c,d}ۅ界L͓JW5͍oqy#kYR6Dgq+) ^K.7BWr儼GfBOLIݦw1k(A'#"Ċ9:4=."F^hɸ B `4$ Ϣ[B((Br0ə4PgMda!ʄ13-%-911.*>$I q)Nj*NI*:%*)tJPFj [i|Je2Χ4^e)SY2AlRe~2"ʐ, /&M9fVT$ǨuZ5pn*ШXR5)!݅JxǬO\P!bDA۬|Zjqoܭ郔z/e+r!/I5S7Ijq&,\7@noPurD8h}ebeA> M/ZKV%Q ECXZ'%ʊRIT=[Ra }ӹBJdfEKG4RkV]%TXl_Btթ go|X܀߀ /Þ@>D!.e8=k \FäDU.MYBB*"V%ҠPoLXj>'K2D|j^PBHUR)%Ad-P)]&OjگJVe[XYtx#G@[voh=C+Oˑ TDLMN{۷(㱢NX.ABU:`PUxt #&eM*}\\%_BP_LX3 QrU[[ ,' ""-(T꠺Hk^TT'ȐRSw0Vy%jh,mKIHOW n}m?d,'Ko\܀G7a) ?bG;=5P:K"' ".61pcᤥ^Hd0)) :]Ot?2 *e=B5b4UhɍoG]_=]* &%RӠP&^ە[9 QJSEpU5"PƐhS)M%5TD*jB"K$mBI|JvM}!xŬ5_ I|j b ޸yFWH?I "" ѰX irC2:eMcH-wa?L G. icS_&$rQPkdtBdBԧr'lQ&$'/Is=X,MҞF77SVXi=cb  ͛3R`UT^zJPݣy L*.k2>'%'. bQ,%/4cV1=pcT{̐fKӐs?~'{>ɱsG׮hTZj)Y[ CjgbBcڷk;vhS]:Ν9USI&mk dfhw J-nbF5'8dEoz[MU.P.Imb4&Ȅ(DTوf*RBށXd$J%GE^xܩ?"5]28,4D W(Ǭ~sےmL׍r2/_0l j"6~ݴ`\\mZo\6'+}zP)լީ9|^aB ~Z.p"fh33.k80Ν>EiU"X pw@F"ߢ߽ *DDBi44:1$?L""lgLr4m$&\`$p$ #bIݻtNILnp*¼\V-vvKNqDH~;㢣t"]JX' BB'u/b "" tuG]RO0,UTt7 wَ~{ߟϤ,6!)FD'Sm-Pq0d}88:[T|8PЬt"@ * 'Q<"f*%)oPR&Gx?eԿ eK*rQCi9lYiGI]:Dv%%/-wi/b9w5;#=1D5gOl5gY‚'{u憐KIqkzmHϟF$DDXܨgtҰ K(ӧڴlq)>D:yr23ܷlt5+Wd^8{fcjE le @e?,ٺܬGL7VD@a9>rg޿wTib'Dz, " E12n4=r1d`$/[҈XeOϥvV Z|ÿL&'M8e qƌnoܬ9t$%N2w[' zҷO홇zo0a{e#\+0H]5L %Z TTTB*$,Ud_>(HŪl#P)% "p%°* lY,!$+4֌_հ?A"^ ,G|% ͒1/BMTM =/+MXXf5vyiUWP(J"&!"PHD0D"1I ( BP(2ݽ{$ɤ0Q( BPNOX["b!k qX( BP$p|yɓ@DLD%"" BP(dDb)=OHbLED"" BP( "bXH0UK55W.]wxhAQ&)߅Ɇo !| \B}"SXLOJf!bl˃<Ϟ:(/̉4"&ouiuq8{O soܘѬ=;ny(C:l`a)ςЎ_ FGٶM-RC@ȳ8}^sɘS&޹eeʺM뻷I:+toY$U]W1h{7B% v׮^Re-|q`Bo?7{VNV[ٶ8n$70'aCXxGT(?OR2QU躗"#sg jiYIgj'Oh?ٰvI"@ J8zg޹MB;#=Tx/AM'N`OE뉡B%^ŠiԂ~nVl.IT(tۡqËg/a1ajXBz\EU\\^^,,D2*MkoӖ\ I *\= 2Zn=&o|H2.!̘:}˯=8kTNSу#:wdowA=M }v0YBuq=tCs)B?/qFqiS9 E-4W1=/^L0fczH;Yl ^+. ʲoޗΟ]@&XhڅvXX53ߴۣ$*24 Ꚉkh<B8vP$A^4o!bݳT&^hfД@C@üQ_=_zqygje\t䆵k̜N]aYiiúN  =Rpc枧BOef*3W r^ݻǡ PR v=WtV[j,)~Z:ŬӨPK DF-43BW> ̛ -<>Nvg_~Y!ݣ5fI..əoQR{XE% 5_CvfOQ/x磌ycL]HBQvFRQx_@t[1%^E"!&a݋2wv꯮C{uDvRnVh) qae%*Bk/(ο{|t ꧝TQR)z"XX>vw w/%%AWYQR K yYs64-@l2NI36-)ء'OROuq"*|Iflݦ1;(Hs/bU-PFb Ҙ>4xpn,"> [ζA ١3Q 䧏0?yDtIq DD?\נ_Y2P4l##iY:R"{wv^ܸ>m38fLnVj'39{g=|.ަ-4dQ]]rg{^E*"f% D.kΆ&|q PtLP7_7vH혋l&҉QDļm~4.ĸD"1)> :!\i>Qf;9iT,G*& Y5+W̝G=yxffVDZУv6eh?kn+/ԖÇ>wGz;}F %۷۵{w8?<qAǏ\q 0U'i9e㺟c<%O"&$;T &[\/qITD8coB૮P'kceI3rDd NvT䕯iJCDξ0k8gqGIIUDceiڂtDDΆ&|q PtLIcGڲi#0D +/^;ɜ=hEmZhʸvʥK/qeYrk>SXlR-e WO(_~xwImZ7g2x@08$1~*d 7p6|ȇ}z[im^NjQz25x:y(Y(W||J-b3sB3ϰeex~q"H e (^ݻ\8d D8 sg{c"+H#"r64ѣybSAgMֹ=0$Ve%gM%'ݰyGWݻzՖx;mL Iĉ( BP(ADJO۲qƵE̴TJV+ 6+$ZDD BPfSA)ߐDDDP( 2YDT)ҒAIX, HBP( %R)DT&Ƃ2R(Q?a"P$D _h6"~DCCCCCCCC#0'mDL%%P~y}Om b&n߸z9+Q( BPp|JK1). G@*S?|XFF`NڈCiDT/4KCD҅fDD44444444)^h%GS~J^h CDDCCCCCCC3ID %EQ~SD}MyY$41$oկ04444444EDL"Ƽ|J}II#"$2@D8`9 HY$q/-l2ŊVߚkMÅԼx6gL"UԺ{sC@  15#wliYiu,bmwg;d֝Y7|AznzWx/'bŕ~K6G# >me~Qa!BГ'd^{.I͇>sgH'yOJPv.&'%bI:^~zƴml`>Yi”R`:|;4=vzzi}MSH<hڔ)| 0??oS'b bR\TrBtTXH\d8%p$DǏ3+{^ˎy ZkcOÅPnAn8V,ZXܵxiac/Zy[Uݽw޽{Rv9ol6-[̙5u1h 54sIj>n_7 2X %?zcg.%%ztmA/7K@&kDDs7ݛs#I0剔ĄhqgϜ&:uD퓓|iˆ߳3>e~Nfa~%pEGܽmgGDsjy!fͤSX{ 5,~uEfD.A[-ǹپgl--,[XՙcgJa Sׯ7qV\qq4gC:?_{R(۶0vlA~h3 DMlce !~$ۮ;gHp`mZC9kΫ6GfFƐA73͙uO)O,tA<].`e/\Da}Ě[7ˆ޲aƵ[7mC> Ќ(6{pE<O:.9~U6=YˡgK/ e'oTN>9oxK^=+JR!O-4# KKK5qV\V?-[*ػ0F{ZÒÿw\*L 6)E 6o6['0w]M^M۷਩S" (Da4g odD1 \;ucБg&N(++ÚLY;O;}1/@xAZ缏^:}xiBh'M'_qb=;λ9#lv9y2aa?C@1G 9>ぎ_lbd۴l]%(ǜY3/մkY38+;@٣B;-+- խs8JKJw,LPu vTcI2B;&4V_Z#¥:f "6ݜ9+Q$fenkhq q~}Gu}h~9?'soN|i+gO^=wYZ7|ӐO4+REu% "f"pcn 4/t?j?N(?wЮ~nWg)z @HVf&_S硡:=ЮCCBwK뙋;*s~olՒ1-EIZZj]8ݭGkamEն6m73٘Ye'+s< |1fػgA^š,2xs?fB "fP/ˑ -DDlmzmHs~[L2<-ܘ!PV ^[z}>ⵂK;sǏ{ÆmX>%|Kc5@2G fKBl)-)fih&nn+/=uʺvr~٘7|[+6qVF.CHpXǎ9"3ܾ -[ePg :؟/۵cGuu6wwmS(^u>;;ڳfʽw\xaDmTt0>66whO <Aн{zJyV "63ќ9+|S!$reNMMmwUD_Iڤ,(-3k& e~O#>={>{Hʒ mp@\$DDR~/v "6?ڜ9+|"ɔ'RbbbELLHhoݚ5c:E4Dff7mhhhhhhhzGĀD%""""( QA#bJb17+DФ "@T1"O*Xt@L!"IGL(NehhhhhhhhhhhhhhhhDĄ"A[hhhhhhhhR*@T^1"feP\DT$ĉ*>:E "Dļ={^Wn21ԈsXhD7 Du0ՐA̱dY-V|\6oj.?]2(M 'Mp` <5$rMcoN;7'Å̺[r۽vֻZ8 ,|_Z@t=1lUVv/*,jտE[?~gma8smE<4#7&GD%D,1!3++߀N<ѱC{#{k "^D'Mi{ΔI&[|mfҩO,WU(ִhGOy>\i"#m2ggڞ𶰸naWgZz p8Ϟ67mp+ 9Tcvú$##=}@'kkce 0>m[k?66EBp00vlA~>쩭8e#GPvvΪٽDM}.Y^,\0䓎K-_aoMOrْ/eK@ى'F=g)';o}m[|y_;R*h1Ss ͈aRqX%Dv̛3c-$׬Z x/˚k@8|֌Υ?,;ܽsWWԖZoߒ g, CWA>x@xEFvVd`Tvьܜ?zϥozjD% ۴l=C= N-tۼ.u.`<果`Tfݜ4qBYY挈$Ԍڊ+q4u1aӦ3y͓ LŜGG:\92f!tb;-v}&5'^2Vo9gLK5ڏ3eh>JKǏBe\Eki?* p8\S;uTRYZRҽKgOтоZe)-""#O;+2PL93`jl=W.}BC!F}Y_C ww@~TW&E$2و'ȫDH'TDWڳ&h.\Ft[B jwo&"&"Wl/Yƻ{$qHqAw@YDFD=%<.H,++9}}1ݜorm3eh>Ƚg:)_@>T*͛?]==R"|ٮ;lv@ D}q^g;f4\>q]}=P(KXҭs6-[thg3cԄx]6u$*U]]ֲ)W׿s2zOAiAB<ƞ MYu݂-{ql3YYka"⋻_*, !,E'{{w{NR޹@0̜Y3YOQC=X3r}JjxEE+W6_Kzι  }=ckmaq"Pδ@pϟ=+mnڸV@sc.HO2 XYqv۶~~ml݋&+BS6-<vNC|,`[hF j%Dv̛3c`$׬Z x/˚k@8|֌Υ?,;ܽsWWԖZh$;wLr@8>|@"2ɰwj44C4g>|s^uI6-[AߧgOE 6oDa: X4??'''~7'MPVV5Q,ӧjdoʼn8 :iSɄ&{bο## mG\3:~ ;>Eq/3k&vGы~-Nv.~.]U_ۭ_RÅ?[N%%ꝥ%%ݻtf-@={$%&{-p>uED}H;+2PD)pj44C4g,ZMG W݅Oa>|ܨ/kkh]ߏ*L>DAߋreԮNnOj%tмv<) ;C󏾿5/vJVf&'Ў硡0Cvb(;Dv@OHI9=ⵂK;sǏ{K( a(;Daގ4.*,t{qC, \SRգj?^5q8Q2Ίۊ)zj44C4箝|_jZDԽGbɻݺB %=`юF|WYk2?"^!~u6$2F̌GK x/,Ye=8ʠ;C,"#mg߂Ҟ{$yFEFΜ>٘n7oW5wDK@; CǞ=I/T* <{zE*i];vTWWoswiŖn_?.Y{NcnjÇ7sv}ء=}/1g=5!3\(ƖݽSG{6"#1ݢhH{MMͥ }Yjjm~С9} 9%b8cMFDl6}۵԰ aW/$鐌tysZn5vH3%TsGD$#9)ɩZV /Z١c-\r%uO![kM;U*ڶ*bf~Rb"aayg) WwjI75Sv@Y[ү0FC3DsOϞϞ>K9+˦ED]z$[esfᇅ{rbbbELLH ߛ5c:E4Dff7mhkNhhhhh1-DhFCCDDCCCC @TN*$T&fDD44DD44444DD>D,LU+"f M*"Deg""$2 "#A>J"DD44444444鈘M<>I јmmAAQjjfTT<v34444444DD!b"T+,T<299-;;WPg%N8!!""""J/cTRJT* /` RSSHtD~횉?㙓 Y]YYի2潰p!5jDE4%44lΘ4CWӉWIO#21JT1/î\<:+5D2ƤL}X]] Iy5@k^VVہ!;nvmݮ_,N3+Vgm.HGF fպ]. 鿠u+6?"&g#b* }[,G|"1&JT1aW.msw7eILLK^[[[UUFc4)!|u݂-{ql3YYka"⋻_*^twλwT*;ibάE \'ͨK"җi%%۷|W]]] Axx b!B,2=rAn/sq{[ o j}u 9@i[)Ǎכ{u%|C:u?da\vCN7ohk~Bfmk=a؂|@hwVfcW_rZvhkӖrk#GPsv`91++ C ĞisHp`mZCh% p9Mի:vhח,..6y%FI[2=y.\^(aCa}@D',bD$T,G|1DRyh34M\?~):)**φN7zTSNvvLۜv<%:Cy`ߩÆ@t%|ػc DlӲo[B-ݳu.V-!̷oϥ?,;{%8wwC]Zvm|@|JKomiOSƗL(Paži>|s^uI;hnn)B9}.^m檪*a: X4?9999iℲ2E|/"$2X`R8}'8"^p?5ЌWQQQRRuYY!0#c@/|aoghBҍy_]sf̈́TӮf͘~-Nv. 2~S˭jֹ ptY:P>231Z];9l)Ht '0Rhhl!XsӥG @):قOe[|][襾:>Ǐ;;?D3D /2Dd?q8,3[.ƣ4+..φuP]ww|WΰS4!ЫBq xڻgАEDٹ$zB(Gч9K ҲUKj&8 Ϝ1~FNMǍ85X]]M/[5UsUϭzyn1QvDЋ""b4A}Q&!j1FPAe]VwdGѨ,xeUyIg23 [ߚ:s}~9N42z`ΣG 4N;ؒK ]9b 69LKˠŠAH-m-:uM\=Z9=v{G; ͆hocё>(_G и͜:i"sX>)ch-l0^9`x~ADZ!z%3 7<($84\D{DLs&'+hoJ'sшΏŋO9PD+/wr]}yn9ID|5((TY4b ;C>|BQjwhsӻ)U{D5OӴ^|YfJbcvp4{"|$ӴN`GDteť󈳳NvE1 yxPݿwo I +hg9}IWWD~hp%EEE`ik464vI0-oC{}8|q^)(y_L{55b7%~DFz:+ٲP&߸A둙(ͧ0"<_Q˗͘>(GS0E\v 3"?4ݹ4Tji."%" P6)uF*I?''Pe"L͛kW|R"D,v%w 7ۿksPED''e޼y+yIRR ARh C "qA>#Kꀈ2pnddTiiY}}Ë=z("&D]bϫVA ]\< 3bȔ2ݨ HH t45>tMA* bEmGgWUr211  H"ޖ,k>=8O4CA) tهX E?vR]էZĊ2. \D| JȔ-  eE XY^tuqAh"b]]CV튊mhhW\\F6B b׵C Ko.ljj|YIcc#uPa!'Z;[S  8Td buEˊ+ٲr7ssٱ/+!Q%ƜTL 1Gssݶ.Ozm׫.'!BD  qFD*Ʌ fYe]cB/C11 4鮮V=#!bmmmTT[]j~v!+Ŭ3?\枲tɀggδp9zdht3Ro[LχNjnxQ1< Èԏ*+-B| QDfHMI&;"*(7D}sEもB*<(Go]k4S_d~4=a|̏p4.i>r2XPaW^Qo hdd >؀OҀjjf_ou>\[xq=&?!RD 27#a(4NSOG{ŏ=Pɓ'+/ҤCE۲yӹgsNrߺ !D(jc= à!bv&,R.Ď?q:?Iy9 "o h-#7޺ ?-o>]}ϛkIzZ)a5"1!BtS{.z;戳3gdDӁLz4^xA j. 5CyZxjeK'ĩ1+G3Ah$ahypwr=vl֭JnrKw{wE=< FDĈe/miif PND,/bh?NW~=E{N7D(]lv;at,nc"vgK6y7{|5fUI=N (-i5J0=|VC:)FZl[_d}pQcw=!R$m2y1(y Jiǔ;|;L_Txb3Z>6 K-犈w˹PJ#biI 7)A6;Un%=|H0/^qQ"s Q'O`m=6r)uޢ V& $|.ϓ"bUy)ˊ|ɋXmm s3pAq3pz'?dOlS?5{̲sro566ΛkdlLL]e=۲=JTWU1'UZZtv4zG76!R[:if %4<—h֭9gϜٵsx^HIHU$*cX )"Yɓ'AABOaN7n}LÇCn k.q⟪5?_f= 2@DWۥKhNܿ_Vt9r)n'!jwhsӻ)UhgT."0)tvvqvv)_E{}8|q^D\66߫>_36U@ćER};;C_*/&t89ܱB[2l ZoS_QQZ\\d0^oIZjۉZ ujy}׮!]/('7 A*F$pKOG{2[JK()E*9"''Pea͛kW|D|!bâ۹R};+G0995--Cp͛W^QΓ'OR<>   DDob9.'xPBD+*FFF7ѣG ""nO}1AA@DaDxtCDFϟ?'DLOό!SF* Qe%\yc  HFATMEWD(Berh"BAEvTe\!D,+ "AA,HDU]Q$*#D$#|CļLfAA#bI5磈T u#b9.D  "*"yf8mffd`;ovjy#Z aޝ BD"CBīއzyDxO`; GoyY9[9Տ;< x_GwS5b-ҏU}EʵADFv˗{_>z䰎JEׯ|'.^\QQHE-m-3g:8m ߶wU- ybFyjշI|█$l4Ao q)^ÇDlA݈XZ7eBDza mTtR_ou>\[xq=&޻g1Ü7.(0F^̩'O\\WKvVy_FPcCА%_|i?8eU9wUaalP4TD##PHpiiUVa=ے~1~!2"cWeKhj =mǏz$&1fd$AޝͱN/5%y/>o ̧&wqFhq; KT+*D^-Jb>DDX^…zr"JG>AD-չ{niˊc>:r+ CB;;˖|OS#5W>pI;.֭vnkk~8:::v{3c8;9z!Rfb"X'^x! v=rJDGh Sgg~}ZlUׯWw{_Z)ߏ]*++YMv~Z2]۾uLNJ&jv`?pl5"1!tSAAľ,B#O}$4؝]bws(Jb> ED*ΈH%P!"QDlKXVD$'\EY_60up]4 knnbYOMMݙO V%d2! l~дتh)%e r|IqyfscIiyL k d6o,  }YEGHy-ފ,/EDVQ,"UqFIJ.T"fsAByeq"JG$4v~gNr4~?ʔG~9n'kIPd,LHB`JlWD+]vj'C7J'em=FUU_MIc~Y hT ."ʽcAʫSߡMTre<Mb/,VTbB{GteŅ6#;em=&ԝm.)**"fglh@0("("(5(Yp_:фM=:* cLjV(}"fIunFuK/:;t fq/O|I}ktg[XdKKR@^eBK]m嬂 bEy̙F|X{+ ?_Ɋe Rh+..27$-5vZr,2GV^V6c4 vg}DbwsA|r=GĶؽ"Qq\"|f8AܠKvYʬ7h9N74Aݜ bIaUY1;PLȾ]%ί9YRvʥΎB."*̧hj|"YCAݜ2 "ATe)WD\(X ! !bXR@D  󣳩$*"BA<(ɔ?AA#ba%U$*S _w4! b1;Sr" }+"cl'ۮW]R{ BwFD *K"bI*F="ٙR'"bYUom_nv"Aގͳr2s6W=;z䰎ʁxGs?A]ΈH%Pr\/_VD;Ą}$V-m-3g:8m ߶wU- ybFyjշVCNN?6&իWMMMǎm\Uck fb3j%!fѬ-]89) 6 @wDT"LN:>` "߹//"r|HvTW[51yx<^\G{%D||$t)H^.c+޼y+2)ڈ#(hAhHSv=mǏI(ɓ˗jiRV&*I̷fb XPa;eD.tu"ʪ<nJU*ǿM|v{Ybnq!WD,.Ber"2:+-j"pqxޮ2p /5՛7nql /OONG-B࿪?\V ?_򙞎e@bKht5T=''[/ @w...p7$-5vZr,2GV^V6c4tW٭Y.hFhBĜlfP@|m9mr˙3qqъCD' ΈH%P!"Q(DtJKzɳ8<AA is3_om"DUገwPY"zqA]後A)6o˜S^eBQqG""{  Ąif:}HU:D,;3]RAA*\AAD7DŸ]  HFD#(|J.T&"F! ݈XPQڼ4LMtm."BAɊQEJr2BD8b9."b̈Ų"byY'?EeN$۷]ZD+A)9E\Lq\.?Dglel?W?{͛7].#=r7&0%l.&{葄O550BЮ_GScBNK]VpU޻G뚯7.(0}_|i?slJl+TW[϶46ߊ~X*ՙ)"BnWIOOjjS ,/[=#N6x_%qJtH=bo{%4.UU*}l? wd354Ǝqvrho'zfbjΔz[%:::^GG)҄88X[ͦpl5"1!tSAAľ,B#O}$4.GDGq_:UaړmmmGNISjcNxEoqz*@Stw;9PU@D.'?"qbW;;;|3C.z~/Zz:8w/Gbi}8׮XJp渮*"b_ZF%bL_VC&s{^?e %iGI6 d6o, ʼn nr/#O_$ӼoP&]k3魕 Z>Dcc!bA^ya>WD,Ber#bnFTg%y\,+"jk|9k~k|\'0:32;+:QVfTXY0P]D_f5bQނ CIi %Y&ݻi4ilԏݙڂy6z:r/"O &tBܗN4VHP!kŶ[BHU$*cX +"Z[~w\kDBjik٭kzt_v8zY0͛7h^GG( ĥP9kVdE-L4'%4<]hBT9M0\Aޝ'S\D{yRME}:ܗN4a0^ٳg>IĮ}dlg" DxtvnzTg$'x bm. hAF䮼<nJU*ǿD" > b! m޸!..9n!b^"Be= (D  h)1!|g.}#"IOD,ၾ@D  Yї 6GD\(X bZT'%x_"BA "^X ! !bXt;p-"AA,HDURZD*ɅRtⅮ۹\AA!/AT 磈T b9."V D|%ZVa;ovj>A3"44vR!kN<| rvwh(_{ny |t7}!66ZjF|-7ձ?)BrsU*J DiIl D jik8)i[žͼʹj|x6W3JSrr 1ѯ^jjjr=vluc fb3j%!fѬ-]89) 6 @wDT"LN:>`rJ"b~*Ds͟w4f&Kuz+ FDruULHYGۚy>ތ}rhc+޼y+2)ڈ#(hAhHSv=mǏI(ɓ˗FN[[[L&1Ɔ&-;Sc9K[CbyjJ#bAA_}.OMM>"--kWԘ;ǪPRWc\Ѻ d_,~/_?J5ֳ- U {QIqͪn'OtaG#J-Y^{Gm޿Kq8bzl ~Kh]*UJ~vUYYfRkhD*4&WۏvLcǶo*/88A:: N&jv`?pl5"1!tSAAľ,B#O}$4.GDGӨ\֥]֭e^XEDPSm,Nj/zk3|vr!bMvjT'3XŲ""? P.:ᨃxxzr.F&;k(R""<wEhZR\,ie>d2'O0nnnS&NPMMd㛠JKJ&a~΂`Q&"(:E2[o er_:UIBdZV264hyQŜJr?1 )"***22瞙;uH3(S߉ReefL ̌ ED[kV,88E-89JPerPUU׻wDiؼ!3lt *^E~AD9Mׯ2/h˗R^l+U /eEwΐGysZ[[ܿ? rTK[n]Ӄ#W`.ڵs΂9o޼Dml::GQeh%&.XYj%s-boAf꤉>)-IBi "p-"{wdlL]cpQEPDQ("J7·p_:фxgϞQl& knnŶ9X_W7`XU^d#b_/S DLMI6:EWK?|gnˤ<nJU*ǿoee3O#J} ͱU/҉&hD`>͌&Df7n zV(/9ӈhD|pOgy]7{x^ @?O4߮NI+ bn! L=mnSF_{wb/,^%#1n*F_.qApZ5Y)IR 9\ D  hPyㆸX**=XK׽.]32߫YɉT#"qALGSO>CzP]XQԔOrD2r"L,C;\ D  9LAA@D"BA"&4"Ug%'Juڭ~lo@D  QVq;y&nD3 gO4! # 0D ͫLN[/'D,Ⰰ\1<,TY孭Wڍե϶6l0zm׫.V=J;#"A @B@w30߯3Xb|T$GcAG,B*rs]zggδ_qޡyVVff|ﹽs/pĿJhlT_WDk$%9YteU y ߮n~GU9h;"ǜ=sR'Es4z{y=}*%>N1'hxh϶Ľkوjik8)i[žͼʹj|x6W3JSrr 1ѯ^jjjr=vlucǬ_Fh0'U+ 5fEhIIض!h Ad:uJ᯴?>? +HSW/_x' y8vCC/?}#1 n "mp]-/^PKOGQYGۚy>ތ}rhc+޼y+2)ڈ#(hAhHSvb ?z$'OV._N9omme2r|jv(pL]l,m u)}\E~y[`>550B]JGScBNK]VpU޻G뚯7.(0}_|i?sRl+TW[϶46ߊxD{iYaSVtӧuaYQUI;JJ#0&[^ O-󷳼l85;\Rq+Ů-,~{.!tT9Dc+QXؽkWee%I1vSG{;4&WۏvLcǶo*/88A:: N&jv`?pl5"1!tSAAľ,B#O}$4.GDGӨ\֥]֭=w߱<""U6 ؔVڸ~=)xD,-,(S Xb3z թ쵈Y\,7"bTggm=uE = G;{lK#u1 l\#׮XJp dUVQjIq-L_fG1'777)'H(ICদ?JMP%%yCP?vg](N dp{y"歷xKń2/誤e[{-ɴLodlh)%Lc _X.UI:5>f z%:ꪪys%~k|\'0:32;+:QVfTurk׬fYqp<[q(sЕ>)-$9BwVUU~{7M&;S@[0FOGbpQEP\Dcބ\~Z(҉& _|)kŶ[Bx]ֻ"|,"Ue$ݒK=š"q3O<ꊐ(Y-m-:uM\=ZkG; yFͷHEjc=g̵ͪ&2C`&'u M*IИ("< "BPwIF!:::^ED"y:pQ_/|M{%߿f&k_l+𙣈uuOAK&"qA>D̈\Q'sׯ]C}Pjwhsӻ)UOroiiVA~>ɹgKXhGӧ#|$Ąfa=n:;;8;;PۯاrϙL&ԝm.)**"fglh@0("("(5(|gF[ͲuDv˗ ؿw FGE:xPc Ŷ=N& q;qBIΊBDn%EBD'_j7o@,-u^F[.FDZ{0lll|gz:+ٲ-IQPIjll4ݹ4Tji."ʱy[yYٌӈ҅j z_eGs,g˺t O3 sA|r=,*-g4Ų7GuD3AAis3_o'3!bX^UF-N3"BAڼqC\\,sNy CDz#:,TBBd>Or\/,AATbB43M>]$B L`LDܘ<R D  0DLOM5[;).&D͜>؀."FsA>E"BA)"--ySOGmRcϵ{HxExNAۙ\D   U˗ik1S]^X"qAP "AAr#"{9^|(+" h"BA)'">ͭ{|(?"V'Kurly1ߗ#" =\4۷o^uIBV  wD$|U%LJ݈AG,qRM}󖖼..G>\4C󬜭̜Gs{^壻) $%&ηRW3oټ?%9YtMJV hw| "#ׯ_=rXGScV믾[dgψ⺏"r@nWገW/y47禧p1O/\jsHH-m-3g:8m ߶wU- ybFyjշVCNN?6&իWMMMǎm\Uck fb3j%!fѬ-]89) 6 @wDT"LN:>`VU;r-S8Y1$"E^jjlMK蘐@_aaX bBcSLn~Hs*eED+=IMaaUOdGĻi qRsì$aaXU\!Q ͹"ފk>JJaaUŏ}@G,XD &DꔸׯZ[0 0 ߈ _hNw~̤[0 0 ð(NqWs<# 0 0*&~#S"Wg$0 0 êR8!bB.oaaoDq"bHvEXNTZXviaaXILFG,BrBD4'EG(Ÿ0 0 ð8?(XN*ńd9\u'' 0 0B.&よǝݕ"B3aaYDq\1?'+N&DLu3a ,ЄRܫ&z'>U\]QA;!ҟ]11fblr2`U(Q@Kux0 pwD [Zh p휻e`wDt-bRͫq6Vfh0JOh {``<""$aXYEogC53DZOGckԠ8I$RaJ|IY)l129!:pI#|}#qIrc܎b N0X4caʭX&43y{^`2&15S-ꡙ4]-MlCҜhFC74(>,G(]fT`T$TO(XO"kăaN""akΜd znǎPIK\DCPsx01ID2 "Lco܊NM//\nݞ_'oߺv 8FȽBW~"+[`ڙx'2s,-N8Nӽvs_[h%X&D||k+Qpkp[z1T43Y# ˀ‚l Stb"Bq,U))q|'ş%#`b $DG.Y?n2U[C}ɔ=NvA3D"`QlcWM4s8؏*OOy+^F<;"eD3<1/|0aX14[ND}qNjEc_~Fkhn`+5'qFS@DaEPY1=ax"`0 DaND̮"00UdCļT!`Nw4`N""aFDăa؃e@D30 "bhNal4 C9I8 Da1a "a0D*õ0 D<q-b7"faF"0 Dٲ!bnz* 0DD+0<a0 Da1a b;3DD0haw4aF"0 D ò0 #f"0 D 0 #f"0 D+"r~Ogݏu;~Յ$O|.?vEHZ6ï<3O|S!eIYz.1!9w\aϗ/t51榒#"_ꠒCo0 +mfUsDy\RlxHrl ð|}E%2>Q0 Ê6E9B\?,+"erWLh0a%" œBMihnAIܮET Sܾ 0hS3ӯn1ZRM`)P<g_=v҅g "V 5aX"2Daψ(󣳁0 2"DuuuR:620w4ɱ!~sJ&DLyKMO r2Ξ> DDB[Cq11~aC|$d6_(1LءYiJ-#:6p}ծzm5{ ǯw +"%jE_f5ӹkW.) "bPڈц ?yHޓJ_B|q=/Hш(;SX%?<)< 6yc꓍P_``>Nl[uӅ͜>ءQ <~9lNI~ӧus{."[Fq1FƱz:pB5,ٖ43Bs}?p'Dbޮ*/".[x qa4ٴv0< [6;ϑn/711"Bڶhr4Ai*'8OhJa/is>ha>}V/|N)=N㾩'ѤjciR ;4ضmuǗ__iw.vh Bo݌(R]EU(mShC7tjrl27 DK~B EE];"9h<櫝RJorτ:&*ˉf]%|Q&. #(Ȇ|څS3 a B3NM&wI>L 3hvGqTDڊV &&tT ]@l[rNlIiZUU(af4C -I \~ܯE46_ܖ 4/ DTC9ERWc7WW ͌W,]~(%{_3LF$h 6}꺕+Ϝ>헳g"Jh0;؜ ,g[O[vtu޶/a ٍY"S!DIMNILJ5ӯʃ*0 hC 40:3zۓ&4HD+I6Oю DT;"N22ⱛ!A4c/¡n=w4buqҟ+͌mH[Km,֗Z#qaa`ht޶K?U|d}z_Y" "9)I/ǯwܶ~c}a>}ߕK)q[Emf{[Ħ&b"_'0GRw 0N4+׭\k[7hf<kY8̕9wlgl[OfL+:=@$7oac]- 9VW~9*C>\``ht 3,d, 6G""bNjU_Nk{W:dM9sāADlJF9|>{؋~ CdEmwnB_l<+ȐC>ᲾOH ikM7zܙaY>̓G5<)rdD<~lm u)Ϝ8d^HsBa._IEӝ2Snu4.NhavH^tE17=ǽ}wGKKz^-O=/;r`\e:B#ʵRRlٳSb#KL5?q0eE@ysfSSZ ]E=Ѯ =aGabSnw_75?^cJ?PiwG~죈0" D ˒Ro(˒0ai))I ">r|1@Ĕ$.a"rx@DD<Dd;,/" b?Y[C}s?;C {QĦ{5 "=:S"" "%/"aV!Dl{XSYF_s10 Fė/_ ("b2rm]Eğ [ tvzUABEq EP0 }1E1XQJ`|\DS@DpV 56sY GAAIDܥBf."/zV7x)Tg(P"BDBbrMU9c+ b?"bx6 UCIJ"2s D wD "Bx\s1Aq1/=Cnj)UĜP7n(/gjhh5Ǝz]iIԟVanw=mmmt3ʄ:ߏrz{ fJ^SBF2v$+J ̍d+TKvNOQɻ3m)By!_HT]Xf˶Gp"]%_"pqQ`THo޼yW'z޽`)wׯ_ d#gϜioo;r#]zu/s>xށ#ٿkj8ELDĒ;dFf2U(b0("̈ݙn5o߾ZjR|!Qu &@?p'#KUQQN3gǘϟ?˗/z̧555oM02464 a~HOc9K[CbyjCښjٖ[f*ENRdҺZb%'9frEmm-%X[FN)߿Ѷo͚9 ?_Akj"byq1Ua쇣g(֦ч#^zEяbD##f!~׻ h*ԝiaޛlNRb"FDh׆)U %[2"R=Bb$[Z.8~JaW?;xGҠɩ=2"b ,Ą~MM̍׻y"?=a!UUBn ?1]_xp6T&!tݻPk;-"+ˊ3_9FysLOK#L3tP@SL=ci<,|\Qur =]ۥK?Fp"*U(bcc#%67\x13ǏQteCgg甉꘿0 Q-OR&'9SqK07oބl޸9bw={LOr$5 nThABjnnzfFL{vu3g39K[.[bKBLb36#,7sgL2ZccիXHC=]7tĊ_"sLvRˋˋ1EE_oJۿ$7 ƪQ;夿IGRsfCr 8+F#"Qm9|;//WeetIS-̹FT N>:Dttpa>5={貑tY0]Jֲ]"ݭ6:_ysЪʤOծRˊɕeE@Dj$-\X"(ڝe^jk{ATJ`Hm`mN6 !bTNED:>J;ݝ'~njBGv'^) 3S =iFZ>`5fEliip?mf<{ ͚𸿿ޞFi8DvsuG/\XV˖f}K_D,#T@D߯_>yBJ3)Ņ+ ,~ٳkQ1?#Mn\D`f\RisSSRkNv6D---1l4*=-@W2*+ϝknbK_GT5h_fC=]iUUU\у];wD SxD&l -7Q8(d{1'ܾ>>2.'ջ "1-Gj\rEi3*5.b0%l\DAOm M*E ߝeؾbrvϟ"rEB>xB @w"gS'" qƼ]%*2eKѢ ht#bzjN?gK r%?,$L BDaOA" Qt9=U5|t43ܜ A(EķSN3υd!aJЅfaAЈ %#*"LҲ65Z1D,MfOW "A@GD1UD,"[+ "qy҂fAAxFL2̾!"!ψXAf W "A@D5"bd(\`Y_]#W"n/s۶VUVrE---{fi5`/ʔ]]]l 5ȯȈpAԛ^POfႢBm/kS޸䤓-dJfP H8"͸bq48sѻ f:;r\kkkkӧO9tHi8t8G*YkVtw+W/Z(zv"ʌXYZXUV 3)]]VT^\q!_8$"I46jkkfL2ZccիXBC=]v@@D">dVw{becW/^ zz$2+yAq< d#vB޽{\J,6~Dja. 'L+~q_$"ptpa>5={*t%Y0]J.YlQխ:st NJVݻbcbb#b%끗n\ BM9@DADUEHb5"kx{ztr0ױΘ:%.6z+]J<ߒLS,L)_"K$_7o,[bKiE;MFKKi3׭YNTLx`oizE"ֹ͚ G쏪""ީ?ls2JBČ;ٙB1;SH"ADa1J츈ԫΞ1uQ\G|5 Sr؍ds, KKK,+4Wgg]H9"';IIE$w<''U4n4?>)35%E&3?/Oz\tEۋU}rpUz֕`"b 83ōÏMM]M YѲ%\[[[>}z=4yD㎎^==ޞN'cT}mټ{8o~4IRu/0lt9m/dno@D D)+pd$,,8誥 ڲ9̢^x{lՅЂi_x!W|`hK.*2]mm*"HKJauuDگ_fw-m2R-LsiP!bzʝt.$L BDaA %T|9EB3%:::XANVa<Ǐ=hnV:Ms㶶?߽43e5nmmfwLF_C?r@D}VvЁl%@DZ[hTBDܲyӵ`J n޸A2R4E7"LҢF"f(s!"E|H5)aO4 x@DF(t4"~K75i6rsrxIua>}jlh0Q[[3c]]׮^H"鲳ϟ?72(>Akzl4Ey64 >xH5` "v ">.""ӛ7onܰ>gVӦdZP7*sl`m. 'r8:8oܚϞ=StXH",uXW[d GqHZjk{ADvsu5Q4"FK&|@27+ʭ&w+:} D"B4>L, "Aа"b " "D乬A(X "A AD D b1'-Y8."APTLTSH#j "AÏc8DYD4q H"fGDks9MoZUYݳ{DcC,/+SZ$byظݍQͭʳY5ID=8A՟ 45wuv612fE˖؞vsmmm#)-<<-9)ҷ)^=YMZ8:';$3#cyzYϙfDxFӟbnNs]};SS6S굍"#'|L%yk tuV\E9}}}fƓ_f;KӤ^x!W1)3"\bI̙܌;:QG`o/3IN8D.Λ7n;1iLҢYz`` \"پm+;5tHE" ,g!U*f՜ D"BDD)IggeWrߎ\[E99\eKl3U:M _%<R=hW*s:9jkkfL2Zccի@DC=]vFߧlVi@D "AcKUBļT!~Dz=JIWXr?_?2^/ ש撰ޞ^:\;']JM ! ?'g].6 7ܽK%m?Umr7AD*w2"" D hDQ`Rf"j:[ "Θ:%.6:;=.|ω-|ҨHOb%%%.NY޻wOH57Wvg׶)iMŋAW*x63njOwwi7mXHDɉfMG"ʳY5"_),bf0%,D hd" Q_fajBqtuN`,''%Q!IKM3[gٟrЯfYY:;pccbL |2n}LiǓ'[6o2[`m# tͻv47#ݓvE"xC6."-?{RVy6X "1YG$B"B D"BD"!X " " "Y@D@D1S$"wDĸAqHE߽]%D"จ)/7žm[*+{vO֚hlhqeeJD,aWW>?(35%E&3?/Oz\tEKR?]$ 3} WTXPv*C=]uJJJ/ &}8ao߿_!!P\h900tȰ-[b{͵ӧCC:Hwtt:q oM26&^|̳*77tQLtW=|xXQEy95h{|͌'s#+]nN{{˩Sl@D "A@т3R?.L17771]4705VFOfQ߷p5{Q"uttl޸A_G{܉+WgJKϝ˱Յ:t6y_@ [_opGgAr9II\e%Wy~rvlK:۷;$2^D"BDD.͛7Z;:Ref<ޞgYYDn{l}wޛqq\ͽ{v8vc քN:9tvsjF#ǏO"#Ȝ) /_a 13GX_WGk֐Ҧ~%W3gL" 3&F:$\b``Z8wHD"B41*k4udIN`γ&c'E([N,-(AEӧX )eR~Eڑ0%۶3`_$1nrR ٩k! ܼq )^rѫW}4Cj$ڌ%yT {_D(_MMoG}͢ܜiR/3IiOJ )%ؤf)5668]JiD4eg?ndQ"fge}l5֤Y*lJN̜))4&c%Go+/"!")BAo#"ӛ7onܰ>g,-=z$I$N-'GeB{lkkZþ}rk>{LU`"h`~ݻ]8D$-=| }o Mݻ==["L/}{+pId{E1 E,.$L BDaA D|Ҩ;YRY;㎎WL'}8ᴛkooinU#rx63][f;)G"R{M+-h==n..Na ЧL>t䉷׎_#C_^==-LM \';::kYD "Aа#btVJ,#r9-5zlIZsg3 _hҢ Xfkk1ף^tԿSqqGӤԂ̆z %ͻv47֚H@QS$ 6ظ[6o"(d{1ˬsj!D\] \>RԔ%mүpF~qq%ᖜdj"_&0%F< @D(O4k:"B4D D"BD"BDdh(sPR ibA@D "AF!".4CD"BDT"7wDĠ7AqH"BD"BD8Է+z̈́\fx6=m[*+{vO֚hlhqeeJD.6."}vwwcT}RfjJLf~^芶B4y),(PD ׸*ދ"ģ5sD&FլhnO>z!E"VqGG/noOODZ?~Ҷl̝lmdzTm(̢Vt.\ M53,=TF!"E8x:/[νtrbkV\a/47SN__uAh9\]-X@WŋrS$X[744Pn}EE)Ur:HU䨭1uj.kWRZ$ tYϟcYY[͠5i=gvVfMSͩtNsswD"B41F{sRxD8- ">TDtKw~"?'g].`6 7ܽK%m?CDR['ٔJ[i9^W3J~'3Ӆ)AO4 xx\AD1Jܽc ?_4*2N~>ɐM{:;kM@tݻ;㎎wd/Yf5-v{{-[[[=E"ͺ)쏢1"\B >eyB4eLJ6toOO%:t[_D"BD|YcRS֙5w <Ӭ?wq|-}mv:s>eaf;Wuʔ <)2R/=Ƀ];wXikMG;;v2H"3qlD QoKX ϳTm§9%KXQ^6 D ""B4D D*Vqd!"BFD!q4!aJ*"W hDQ`R"z 4Ph@D MALE D 3Ճx\GD*Ejq"* "h h byQ@D@D1{9sԌ8D?,=`nb}֪Je,-&l\eyY"HUm!"'=jy…g_ .[Ҹi"֔ DD D Dz!E"qGG/noOODZ?Bl$lhjپEy9<ӬY0~p6׍>~xڵ:Wd332͟i)Ϟ=}}}4_x1{ǔ^psu!`iZ6_@ [_opGg9L INJ"*l/U[D,--fysp~^^8WIK.+w3\Dv5B!aJ*"AD1J츈%#D3wso޼֚xѱ':*x=Kϲb5 #%.Y۶lquqSWuvx;<(v&%&^ dLʱ?˕@ngxfڑK.*2]mm*"HKƽFzE "iֻ֖6 Wg{*D ~r2%^½d\jeIIɗkPZ=WW|+6UC}_t#~xсCD?"") Jwvv'pwJiafɟ޶MĸO9ɲ4Gֿ?|G9<\"پm+;}zD "RhTBDܲyPPhp ҕygm5stv@jӧOʊ }mԜ5,ʊ[>lʯ!$ !ܾK75i6rsrxI+=u=c\ivVֲ%FzL\]n݌o/1jDښS v*E"PO!>GqHm֤Y*lJ!Bճ>/ 焜{YDyT{M AЈ ;yX<͛7a7nXMwԄE|!siS{{z}$,*reeDT=>^ptpa>5={*tfw)QW[d GqHZjk{A!V4G!KD+/gw5Hl/U[2;#3K~ѣ ]{, 8DD WTd˖B0#bxh!s "A@Du#b1Qsss' 6D#2 JE!" "% "5$-iSCBŎjD/ Q^.=nmN^(faCg AwD$!  ="J~>Ϟi}d?z+~qM A {;"p AиG+A'Op9y9Oܿ/|l AИ@D"f!MX]-s( D YD h4" D DER071޾mkUe%WҲwiZ 6Liظݍ¿fdIh{ d_mnj/]ZH0p9_oݚ}45'Jd޸G A4"⸈@AbWgg#C~˖؞vsmmm#)-<;'=)Eт}}}Z?\"پmq:;;@IN`&Ki">lD2{K+l/EsaA8#ƙ33 vB_G>F;O?a>)h " ދH"w%;+k[#}=(vϮ]nQ⌷)};Mт53N\yUJD .Cn#}+I9[VٔraIXtŋ2ocyEL{WnN%ryADy=T&DD] +D ͣ T s{{zz{{2Ks*,ŋ.Yl#Ҟ ɭ3EWD ͂ wRV/k8">x>ϦWuu\Ѽso, GD*δpͪt@% e,- 8!,W~o "A@񉈫W())t9u޽{,ݚoky<@fƓ{{{[[[ @ēNN4랞n7WZE#bDDk)ϳ΅eKǐeKle*SiFz:nnߟk)&sgr;:GE̙Ifz: 1:h!biidYVVii:,?5%dgO*.6ѧ#.aCUUWyfZCΎH"qlۋQbYD+/y-fLK~S< @FEQˋ bcb.Nfd w\ČQBB^D$הasR zAQߟ3ܜ=Ax@ķw߈ktxl $-iSCHg\A,4]HnnK:  bAQnIK)/.ĸ[DqU1-YF,,I!D|p'8A1$[؅fA"B42B!b* "A喙J'@DIq1) u5#gMoZUYݳ{DcC,/+SZ$tuuq鳻M2SSRd2EW[Is];w>zHHkWbɬ_d^P[r+A4\8ɩ1qaaQA0 êG$4uhAqQ3n'܂2@jC!ADĬ}{GػU_G\ D"BDTÀqߞ- /דv>у?] "!7(Yfr¯޿oυ3U{ }mu:bH//t1pq!8"Fg3"&F_8pIO~ÖM50|K%:}_׃tIE "AF!\h :vwNZ p_zgUe0 !੅'o ajo+LLWK,@DruYΎ'M`xև`q~p|tax4;MFDa " ð!btaLD<s/8CR0 Ao`Giqa0 ã 0 #fa"2a ax; " 0a;'a#~x0 wd 5"f&%''2B+-""!lCĘAķsJRMeycC~C->'"*B᱁xC 7.XDIMR۩ +J*J ajCV PX͈x0<0#blxXuyiUyqyI,U%hEhGDDMxɗ2/'+u0<Q./(JPP=(s_'6R"2S_d$WEvZ28)Gi\B؄a QaRLQkW/GGܠDLč+\?YiII'S\&׮R͛t2)X%ݎH9/߅sT!62_ɝTaAIb"I^r2î_Q)ъՅ'`x>, rӓ97# /Vb vDDC؄9.ʈ̤AW}/T'# vNF2hERJa CDD*MoHn\!:2<$(,cZ-rh \0 +)%ˋx0.:, ;CHeaL k`]&~=?3t kW7#%yX0 eRXSx0 pD IO{`IK`15>.!&";5 (pvj2Ty1qԂIqљ aLJ`436Px7"0> stream x @İoӰ n-&i`pI_r0AŠ yZ endstream endobj 347 0 obj << /D [345 0 R /XYZ 69.866 808.885 null] >> endobj 102 0 obj << /D [345 0 R /XYZ 70.866 771.024 null] >> endobj 106 0 obj << /D [345 0 R /XYZ 70.866 748.406 null] >> endobj 348 0 obj << /D [345 0 R /XYZ 70.866 631.809 null] >> endobj 349 0 obj << /D [345 0 R /XYZ 267.404 312.947 null] >> endobj 350 0 obj << /D [345 0 R /XYZ 70.866 275.019 null] >> endobj 344 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F20 166 0 R /F21 168 0 R >> /XObject << /Im18 343 0 R >> /ProcSet [ /PDF /Text /ImageC ] >> endobj 355 0 obj << /Length 1474 /Filter /FlateDecode >> stream xڅVK6ϯ MUF%6dC"R(b,teR$Z~H"F"zz_qT]FyFYQ$΢:Ğ}z$_orW71".^\iT")ZZ$>oL Y]^P(҄g*v鋈j{ in˩\1YVʮ38= dt9~SpBJqgzwX{1k Ǟt;@֖$0Pz#wy|3;A(3i<@z!:=&#f9MlgL+b%>aֱa߶LK @Y)p,&o}@e(0ҙ=49 *I\vy&= XZ _YdبIiJ* ]@ >Q`*C2H"Jxa8F4t#ޒdI%L,UZw>1K53魔sTmy' myHQ`Q3{@f"A߃(xAKRl;b\:JS x%$]8483Nt=-*v`*Ƞ,R]{μILd 0Pw0‘IiJo:CÕH.+ \>",XKŏ(<̧.f,:p\p K]X=z8S7O|5w$7yk endstream endobj 354 0 obj << /Type /Page /Contents 355 0 R /Resources 353 0 R /MediaBox [0 0 595.276 841.89] /Parent 351 0 R >> endobj 356 0 obj << /D [354 0 R /XYZ 69.866 808.885 null] >> endobj 110 0 obj << /D [354 0 R /XYZ 70.866 705.333 null] >> endobj 357 0 obj << /D [354 0 R /XYZ 70.866 666.562 null] >> endobj 358 0 obj << /D [354 0 R /XYZ 70.866 666.562 null] >> endobj 361 0 obj << /D [354 0 R /XYZ 70.866 556.98 null] >> endobj 353 0 obj << /Font << /F18 192 0 R /F16 128 0 R /F21 168 0 R /F20 166 0 R /F22 359 0 R /F23 360 0 R >> /ProcSet [ /PDF /Text ] >> endobj 364 0 obj << /Length 2839 /Filter /FlateDecode >> stream xڭ[ے۸}P̩hV%U3N6li%jYԒޯOA<+>Fh񷛈oo^тGas,(Lxq($[ܯ?o}W4K@r%$M-2 S&WמI2^%Лڋ.*o.ˡccc$DZ|m-`xYc0d*=B[÷Uv^-f [O+F úCY]]>t[uCY@޾]Hَ1dJ*!ca}$Dϛ\yV!N; >>F*bdWW5VԟlB)j^'^謇;~hulpvO%2fLiHOvFY5`n*Cݪ)<^,X`8 ueAݴ`@Uj`ukVhs7#zlcmweŜcp3+9c9z/-W>oAw:߬^J T ̮b$[PJ+p\>(00+2reFvFUqB Bȱ5]RZn,Nb. :e#vQi"4FSuiڃ/$;u*w-;u]0;|ywgQyۥ6>ˣ _D3XBXGg-DAhn4;b†v \>T`u;ATA[o7 8I"UH,LC&bhO@Pd4-DT (ons:Pa$SC@cG0 d)) *8-?PL9S*f,Ny\40P%B)HCo`)ab<_Kp8K(TLbP3SDZ(cp1'Iz@!  @*,EOGnD_t{mLΒq1dľi3`B\t[٘c$W$Q˟yxNY Ӟ,ѱ؃ !puS ,)$_t,1HMp*GXRfy<&bT 3uLYcHwl^mGqߘ)?sq,L eWR:# ,#6XS >`CN /1٘c4BA~CdXD!>T1#9úID<#LGgcP+k{a20yTw\Du\qG s`}).|q٘cDԗT4_Sݕ+}_pgwIK辭TGW^;Yκ\+-I'=ŁM6i/Fnjk+Zb1=G}XDS5PF ߌCz⢥&鱨DR~)#1[x.{A'JKL}w'.2rL!< q=5\Z9P| 9:'X,UoMC,hI*m{8 ZٛCs7PEᘪ' l*rmA{G]JpMcSJd5ƚuvt2ڮ]pE No  Vʱ-=Co`NoFS/@xd|?hdtOhҏ}RE ϰj׃ɢ p_nUXXr|(N>\HZO~[vJȴ+6{4e;eї, | SE^.Xw^a1]`r?|+1"_uI9GIX]m£.#uN(l1]סJ(Smځ:d7On71Spa^4.ra`0QpN|h_{^YqfbzT<uƁGTmQ &l6ga?ROSCKn$kqĘJh4'a`>o#nI(-օE1mNo ﺘ Z,dŔxkh/m"%d՟{֋Nci/ԡVJ9l[{\tGy¥ yA?aUIrs7fm;QK+Zoxgǥۂ4PsTdb)waQP(T`ss(5U .څ,gjɒڄ0C䩍&H;,hQqFXw'y"̙[p6Z Æ> endobj 365 0 obj << /D [363 0 R /XYZ 69.866 808.885 null] >> endobj 114 0 obj << /D [363 0 R /XYZ 70.866 771.024 null] >> endobj 118 0 obj << /D [363 0 R /XYZ 70.866 591.858 null] >> endobj 362 0 obj << /Font << /F20 166 0 R /F16 128 0 R /F28 196 0 R >> /ProcSet [ /PDF /Text ] >> endobj 367 0 obj [569.4 569.4 569.4 569.4 569.4 569.4 569.4] endobj 368 0 obj [388.9 500 777.8 277.8 333.3 277.8 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 277.8 777.8] endobj 370 0 obj [306.7 357.8 306.7 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 306.7 306.7 777.8 766.7 777.8 511.1 766.7 743.4 703.9 715.6 755 678.4 652.8 773.6 743.4 385.5 525 768.9 627.2 896.7 743.4 766.7 678.4 766.7 729.5 562.2 715.6 743.4 743.4 998.9 743.4 743.4 613.3 306.7 500 306.7 555.6 743.4 306.7 511.1 460 460 511.1 460 306.7 460 511.1 306.7 306.7 460 255.5 817.8 562.2 511.1 511.1 460 421.7 408.9 332.2 536.7 460 664.5 463.9 485.6] endobj 372 0 obj [500] endobj 373 0 obj [295.1 413.2 413.2 531.3 826.4 295.1 354.2 295.1 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 295.1 295.1 826.4 826.4 826.4 501.8 826.4 795.9 752.1 767.4 811.1 722.6 693.1 833.5 795.9 382.6 545.5 825.4 663.5 972.9 795.9 826.4 722.6 826.4 781.6 590.3 767.4 795.9] endobj 374 0 obj [525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525] endobj 375 0 obj [525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525] endobj 376 0 obj [670.8 638.9 638.9 958.3 958.3 575 350 481.5 958.3 575 958.3 894.4 319.5 447.2 447.2 575 894.4 319.5 383.3 319.5 575 575 575 575 575 575 575 575 575 575 575 319.5 319.5 894.4 894.4 894.4 543.1 894.4 869.4 818.1 830.6 881.9 755.6 723.6 904.2 900 436.1 594.5 901.4 691.7 1091.7 900 863.9 786.1 863.9 862.5 638.9 800 884.7 869.4 1188.9 869.4 869.4 702.8 319.5 575 319.5 555.6 869.4 319.5 559 638.9 511.1 638.9 527.1 351.4 575 638.9 319.5 351.4 607 319.5 958.3 638.9 575 638.9 607 473.6 453.6 447.2 638.9 607 830.6 607 607 511.1] endobj 377 0 obj [656.2 625 625 937.5 937.5 562.5 342.6 462.5 937.5 562.5 937.5 875 312.5 437.5 437.5 562.5 875 312.5 375 312.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 312.5 312.5 894.4 875 894.4 531.2 875 849.5 799.7 812.5 862.2 738.4 707.2 884.2 879.6 419 581 880.7 675.9 1067.2 879.6 844.9 768.5 844.9 839.2 625 782.4 864.6 849.5 1162 849.5 849.5 687.5 312.5 575 312.5 555.6 849.5 312.5 546.9 625 500 625 513.3 343.7 562.5 625 312.5 343.7 593.7 312.5 937.5 625 562.5 625 593.7 459.5 443.7 437.5 625 593.7 812.5 593.7 593.7] endobj 378 0 obj [583.3 555.6 555.6 833.3 833.3 500 277.8 373.8 833.3 500 833.3 777.8 277.8 388.9 388.9 500 777.8 277.8 333.3 277.8 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 777.8 777.8 777.8 472.2 777.8 750 708.3 722.2 763.9 680.6 652.8 784.7 750 361.1 513.9 777.8 625 916.7 750 777.8 680.6 777.8 736.1 555.6 722.2 750 750 1027.8 750 750 611.1 277.8 500 277.8 555.6 750 277.8 500 555.6 444.5 555.6 444.5 305.6 500 555.6 277.8 305.6 527.8 277.8 833.3 555.6 500 555.6 527.8 391.7 394.5 388.9 555.6 527.8 722.2 527.8 527.8 444.5] endobj 379 0 obj << /Length1 2525 /Length2 29259 /Length3 0 /Length 30722 /Filter /FlateDecode >> stream xڴuP>{)P܊kq Npww^R{)_z{OϽLY; *1PW52ؙ٘YY9FΖv q#g ?h vt!Q #Xi 0LlϢ5 77[_F ;SK9`h v..xM d vػ8~/$7Eo `x,b/`X$ 6`8,2'Eg pv?] ήA|8]g5A|`dA`.& . 5,sq= ?֛6WiWiao7%_\ A0%? \οB˱ lkWyl``_{#n4s#e??bp6bז'xgv7 j dAc.0g G\`nvr_#ApnZE 8ǿ 'G:7_;UC27rvtal`9Ozυ/oQQ;w/&N~`bqǃ|MGMVL[JVR1T}MY͘"N|_I]d''ͯ*HSe#_y_BT Ϲꁙ+=tZ $Gb|]1ӯWn l؎6+݄+ݐίqF"_ Bq>k34,0'"U޽\!Lt}L<1+<R)@gXh '6_]d~@4eIEK#pcL{0nP5ɷ~[X\ Vǹȹx[/R.&ƣ"^9ԔV:|م9#M[0טKEQ!c% ĊASSkWv; =XDQκSA^2!uZSR9ü6*%{RlBc}ee|G3)oᖋa.;G,3KºRq~ImW?lGޮGy$2tw'#'}GFw60ZJ7W WѨl40_Zkvi棶T8$ϴjBeV,_(F?䚻P'zV?so,sCVZ |h/AUe$n9!gE H߽hf;aS!6~{p>?-Q,ص74FT=B.V1aax7.5u\XZ#&gmUhc  U C5DƊK g!y6VΥ￈׺?|dso]hoI$xU[` ?!> ڕ  i6::GZEJu%rxNسC+7b8w%mUh㦕Ǎ$kB"m\5aG7Vs#D7: *DMT̖}@!@ [U~*ܹUﯕZ[(Z$!*S{+##jtZN ei B*9v]~\}~I'Fߨ*8a*J0ܓzEaj"λZלǽӽ:M_!O=1?Њh]0?/.xIeWh2"J+v&4,dJžcJ T _f,C~\sIBidUa֫cv}cpnZ 9E:x5H?,1N(@G\Wl*IE8+U_QJMm [~CŎ>%} J2 c~mw.L/fQnV|pkQj! Ӻ蕆pd<f,%m3š?$T-b:U0P=qWm@_beHL6M%L訉*bŊ&޺̣Q6 m;5X^IvZw 8*6߇d{GFt^~G/]k;= 8rBkT 5=JzwBES.xB Bf7*pT;8t&ֳ!p顦r)$\9 wxb)`R~F:0OAѢT@JB(fcQA򒶐G4i &LZ7u}HD%[* 5Iݰ K F7 5x(ϿVߟ<ίO(&H<>ϧe^"}{"4eחM=[rH{Ɂœj|ʱP柁6['kUo&I N^Ut}a,h9dIevh=t7Q[GNw .]9 Bui榞>Fqɷu)RՄ|\XŅkhZ2LQ[g7Y|Ez±\ 3oM6"qCdI:$dVTWByMk}P&' 0Vh^JN$KAncj2J˺COaAо?!ͯŔClm$Q 2旅1-ӡ+ 9A˛(sAXuMF% 'aM7,e> ɘX( /(E7-X*K|KlЈ\ oze5@nφi 2ڥk-i^HX{1,DOV]ԨFe'Wy?**oVRqk+4zzf 3_itNbEqj/PK>>7?gNx7棌wR& 4!QZs1bd/.\ȼ2_^ΒE6tƸ6F  d@ϭ:dE63KMoٸF;=z̤P̿p c:6Ir`.Mo򍧎}DO/q b;Oy#V?oӀt]&GPgcg`I&u ^ox8.pZ̶q2H#ǛiU JWh^/RԀ>˭ ΟSRwyel6/BK*fF,X4`\4"(AmNfsf5۱wԈ}rȧrPsͣ A!fP2-Ye@O5@)MV $юnIu?I)3p"'67(oϫ?i@U=l.=)ztr<цGRύ@IZj17P? KRQ y3uDkh , 2Rw i vA^rx&Q{SR;eKCD6qEnOryGt[z9bvB|r"a.>x\zz٢_B;xؿn ԧM79Y~ƁIKjNM!'[Z)lPMM9O bx;۷S",>G k{[p45MY48]5f0{.Bwے\0ԗzEJrknyS[gvz2?%8@k "{脝qyAca rmq!}ZtgRoB,JMƊI&"0V)$$T UQ|ƌXy.f|D;wj^Kk<2UlS}fD$/b?F(yUtp{Vfk|{iFcG1 tȔQZez[3:PRqe $v̥N FX9#m@6ZRSKń믷wvo8==ub`2ʿbnYms0DHh?hO:CX:F<@õPg^Wky%a_U=/&"bۑ}-d!?xDqgzf} `:m .m(Ωq69SyIʺ4"[bUL-[ei (޽'1B6u.&gqsHNSVuf7mgaU牢 _P-{ɰwzr|g.'tbfHhX:wLT,*_긑c 5R-~B ^į W? $|E#L L5ʳ9٢h +6$B|oqƿLX(@Z"-O{2nq50̺6.+f ~~چ{U#0\/{F)A*ݱS8-eBI1LJhl;RA24evqYn xLe몵b}ϷAn{.Nϊ A E~bI7 hLlk-}X 7Ӻ<#wItdxsr ^VDy=1_p r|9ດSZG/0&q;>fI4B#;7ew8f!/.lUQ?HȹCøãe8 x'A}1눦X-ikv՝ݙbXe)O2*0IS-МqզY9mkEH(k{ kwPwo+z ̸iT9&bb5b&mVܕFTk :fJ C+Ʌu-HMwyC9[s]2A{v (=7½ߗy UU^*~Yi;(ͥdIHՎ2!zIsE)ji;inXbHW$TH,C IEMql7NLOev$feWqUk;,O 7կ3YxlpK]l \xxDN̚ͷěV'wD3M6Ґ|i$ě"[&kHajf+f,)XD gpJn@s}&zPWJ*b#؊&c7CWķIBƩ/)N5D\aMj`L{g,k`d4Hz:6vZ^3v7q^gܹ->= S|u7g.$+IXv8.d g_C'_Zsy/Kq|;+ǯ~"1V.7mBsW`COZ xʳ1ߏY-oT0*e0wc& PIg~߈ 1铛NWYz6hy6 t:Q`Jfub8L/QV8Nۦ Ovһm^]oRl1tBp G0Dn$whz©F E-6=oSت4#HS5/PLEuogwc._~Wɘp"ڝH=LgR+WӦ RoD1]O*sdف^K] }քF9;?|^E;- ׬4y*>A!{߃) nL`/t_$؃C3O`f-QX}%9RXzJ-P@toA#3{}4J}$>myWyiȘ8"'0'._-OxG'Ψ*4 ADGx(=42.KzCౕJE:+T 拺6UTɶ9RgQl<- R5펌Gd+6[:[yNH@zTB2< $>)lчZ7G>Ta2ut֕r2ƫ%[wѳl6cʫ,ncPzh ԓʸ?FA=2A dߏ࿃Z <ޝI ?(|g?<,*͋f+$@nq78{ONw]i}cΑo%MqўM抿Xݭ3+$(I-͓XsOI뾀 S3Pw,v%S)~oR桁MB[sMoYk::2PeɀzF}WqPϤC\gGN~ŭAߚz{xGͺSrAYK{a+.ֿۧr>vqx9u=4|&^ 7`n%C!0,`5s mb vڲf%b%ysbCƥk; nt;EJ2kH}Kq r%ә7K $h=c&gW.;aH D'0vSB=_2VJA=᎛qjlD.28&؜E%jsصrcPʏciAjMsɩ0ؽb#x{dHI)?VRWdy-"jEy^H, }³>9 ~^`w'̾\H" A~ k)A=;^]"WN4"Va }p|fj!4JV}l~{]mYwڐt}pZxj)YiMԙe^@DqH9JI2O ˳\۟\: P(j쭄/~V-YtWxŅ6fWٷHO^ 46>*yͶj$v]5-cHǤ8YD>iɳJ{#ՠdDבBW<;1Gb0^|q=VfF%CCߌmQr?OUܦPIXdW)[*^#`@n+ q$mᶻ(l\nQxN:2 RQUyX|bmFso+]7Fes]ׅ_ȔBM"%~{;h#r{yTљRVdʋEBc\ 3RFMskC J(FQNѷ'{F{Ltf|& )l$ 37fr׀){k 7;݊DWRϹrMrQj0‰{bK]6\i@僦QY^ g>t6,#$ & v|Q]X&`cKHM3gV_h2Plݬ̡+Qk; $<UxVa -ҏЏQAhήAלf] i\&F5Ƿg\k d)x Òmf [c1{P&v:fdВ/̗ H+e2A7"Wk=7$ιrbTrź:\\t&/ȮHz1RQ sD!G9ñVv}"M2Dc4#)o\ed,B"BRB._jY' &5aPK,p /I:4lL kl۟y$φ|\$I(dyI:jDy (CVRv.ЂJt1Xnb9`6 KdaGE]r`+ǻ>ZaAàaq<¡Uю9vbzcnvj+3[d\:7cYL&0 Z}g A\c] lW+o&=MQnE.=S7gsc i?~L{NO-tğqjlp[}6'9Md޾_]4-/%{\ i ا϶}(q eshvM2znfu!s@`ѣ]8IMɫ@gJJYχ!'̅ވC3ee6_U/7).Š4_buvCᦞc(L^= MGsKhI`Cҁf>0Eau| ?2v_YLS0Gub1gWJ[*˳=='K+u${_F:Fh.$jEρӨ06{p#B3n@ #v|wyb }SD^ԏf;5S_HPe oHiZQD{չ"X<8 Z{" rL;B>jQ H{=a Rrra$0Hb)qjRUe-c7I&HanqfoMU?.")~&7|% 5Ϟ c͵4AF'9*6}o9n.:[46ܝ8@c}CcǕa.JE,eB9;!突ǘX`KaAE,'G/^q =z)OܸJkO!Jcmwn ?LW CN8hV7it6!qbTk{gifI)Tury-'-19P!+nV:u?Oo8:#imĂOV"9/..ZHv@VbmۛVoVe+SX,WǢ U%V\؟/+xCu-'oO䜓~$J!v.ngpLt ~ˇ`g{ly_{є]rhF&8R;b `t\W.wθBs"jY5ˑxʭrQxGUֲےG]<_'mR$&]h{VC7Ӫ37 W!d"j$c")bOsZ]J3sr WXӇ(\\ʷOq'"q@f{p$J$llϝ АUάG$kld]ugЁXk?n^|T|k'] +TހC]V2Dإx/> ]{I]+F{+G }.gR?YarB5¸~"z]s6>CiZNq$H?Fbla4fwYƬ'Q>yHh±C{ 6c~ |(hlxhY픖G?:\Rˆ ogFxmML&o[NtY$2Ci@\d@YջN NF R <3[r,d|\'8$2bAǤ:qAЖ E|Q%(E%ōUQ;ᮿGּOW [E#"SȀ@ k\v6t:ԩ4P\reXJB&HIc;Cљ޺E1)YRudAWrIˏ?cLWu#9Ē:kΥ(_%w3'x{h1 sʟN!Fm鶐'uM^}>f%X?b}h8Yq/_YD8čW6w8VثP}27+V{ nBRimWrUR1tOַjI0EBOFR)\=6%tRzjK䐕m)Z+8ajw1}5U-;-G ۮX.)0,!/\2bN3A_'|X"LuRYr"3=e|؀U.}ř+)6{߄S{Ƥgs~r)ըCiʯ^;a~qYB:[" ?)>Af/FښP%_꯲YŢ^S]Q(">o=;[)( @b~sr}d 8EVy(! .@@g}"pM|n!Rtm "zcҡ'Gz3GҐ8"; z\H)vh_!Lř:"b|}MֱQ2Ig 3f%* ^ŒbiI/_ ZI b7=+4于#K7&9GI$bEJv.h3I/uMu[IU0͉ 5]`#ڀcھ^l3d/dmj΍yBƴWSFSNY Q^4SJZNqFȹ Bb&q>”lYPxQ:HM5.Vw12=PK}-%IbhV~LyfO1yGn 6&<&&AvՁ+kQ!Bf)Ä2:m̞7ˎ9|܇gdy9FpZ Ǥ Dڹ?#S+ϛ2C7yqdWpckG2y:[4g̱[N~^h,AF,݌Oi܎bY: )82 yq'!(ihl\֥86t H^ct~ؐtZ3U>[E`<(uגێo-gFq8]r1:Mq܅!N]wh%BIg!uG1bF`Lh~4_u-AӒY9q&P7|MA 516G]{KBYѹ_{mV+̟6%fEy9p>.RLS~E$&8)=V۬h;`\/c|R 9j:SVGoΌFYyCkVg\aVPV#rzYu=2L ="e=z:cτ]F蹆~RjY-S|u zUMkyZ\~SF2ď-\OH%W֓q' qmqo(, >Ɓ˨(}2 Jw7+/#V%OpE]_5qhbԋx(Ƿ>.h/6f;:mDG!W5ja1x 2FC3"66z5:K]#4R,Hx OⲨ)b,8+*M"le37u8W"n>ɑlńTi"FM:r#KJqq _qVWD`#a%zy, 6 t:sp'!&c5u2:+ׅ^?ۀHC$usLN%O>pSrGH/+ʬv4m2/ :Q[:oԊEsTؙ hPw=WjLȵи.V|(\ҢH (* 깐yiC 1P-xF~< (߾ѾC:Ή6U+Z A_Rvkldcsm՗%!FfZ׋+ʵ!R$|=kҦњ_f\Dl+h^JF !qrYK%+ĕt$fКxboMaY$"&;`DƷw!ENѨB*a$JD1["mw`/%^䅪W Ms}N/5&Iza ]`q Bѯz`fbKsR+F2Rxo9^UvhFvQ|M-|^Eo֬?Osq=i` *G%IA$uB igď - >3Q7#E"x2^ Cx IJXh[Z& 3謴jfQ  [ѿt2IJ _S(}"=hes2|tlE 7y=G̟ K6JRJb2B۾{\܆~ gȼ~_np32ΚT>ZlfS&+B롢А]u`/q2/_CāRX`SsuCUu$uNcNcxٻ] {T1 Ͻ"0 EsخDQ`l۶;tl۶m۶mc۶mə_i*)ex'4:N@>#fxnƏQ$'JJi'ne&3X\Ja!u |NmQմU0O1) 3] wGRAb& K `a (OkpFA#_]uJu[HVf  rIe+ zg-D`<Pp~ ?cIEpKu12AVJ夎dž(WcB ˣ$@qVK p*;4OnUZY `l|1R=.J쮾bTsjLEGme|i\ΌSe.E #j:M)PmƋeК7ƶGhUPY78ֻ5H ғOP.{8OP>^=(); laFIu/ٵBԍNj <%NoQ,|J[)f mdWr#E_3,]bq,@/,b7z †3[FA~^Eok^-Sq.=fP҂$O͟eɲk#G`Dmk> B XRQ&O ,i{u[D> K:AHRSƮgl ]bqj->'#AwDD"V(;Qj,# 8k9kU Aq])s"l'E4XBՍCgA^U[HtR&)G*2+Lhy}!ZRo6B[ r&PŴZY0j1K c=wH%WiR! BՇ_z["s=]jq-9o#[ِ0IQ=0Y@7>~Ld]s{_rUp{o#Ǐ,H!sLHm)kSO!E[16H|͋wYC8Ћxb\g?{RqX$yS@XNr(FjLde 'EW+u,f)ȂS`3&P-* ?PiW zd_Y֐tcYy:pD ao& GAi{?Bdt"&JwGCϷRk {,LӖω#VjAdl Y2h'}6>~xOaz^ [JUd}z@d}^E9-R2d̤XZ_4˛Ā1V^iB{Ɇm8&j| 3Ria!$YQ:TlwZӹ;=|<4 d4RObx|gZ ^]{Rvoȣ?{joPš@H)ׯ B2~ϼUTA @w~GNA % Xᾗz{ Y \ ($q ٮQ,VyV{Z`ZV fAdy."ZK6.ˆWٰ}vV1%XT9BSDhcpMT = !2J|:; 2<yi~֞~#cc,w _sD&GȣYoS,\3P>$#B/o>=Hn )mᕫ-DԞ6(~jc _7z7`y/%&Z(" 𓠀# vkeצjd/^D Šұ3~^gƺ&l|aMX֓@e$=Q(I|@` wXbPEv\D~9fP2H{D?4J |VYsHJmհ9ˆ9ڬ |P2u(6A \(t?5Erj3X[ENl}̲Toy9pz@V/FlP`jނ*';[,u]R } LTdN2ǤJ:PgluL:[xi/LiGKϮxA%4q'*8H# GT`pZ#_%Hknn#W ,Z+ͭ(,X֏G5?cŒs'nXgE1ytǺl}qIf Wo3[ĸuAcN;ݤϟQҦ3~?Yb^UCVླྀed+W,NcqI7{w̶npGIҗPۦUW͝X}d"BءX}ط-*މH5S0NNS%{5>YtAtS3} ;- +b@Wwr6ѐy\QyoLy dK2,LTܘ7[ B %įOF1W ;em$F I\9n\wEt\V1J#|,- /}1dE|.,WAt"XD2yBPH )٫{j!^A5cɨR:G3#Hkv* ҆Sd̫ˌ3'V?ېpn^rYg*/@MV"ȈWfGn"kDpuF_Swnzd6 ,Db'x^߱D hx8h܆0KEx饸|QV ?6&'1AcWtViB릃DVkcm{Sa܀<014n4]Jfr*G"Iӱ\Ҹ?H'!F1]qWIA}$O2RK91;>Q ~Q] +>5LB|#hmrQI({ܿy O_P!z*' R%{Mk5|2ྚ)˜>+|PlU.!ėW]7| ܤKN"^ק=`7vZӇ"^nMkZ+6{I y9JKƼnO׭ۊd;3qC[M 4Y<w /,XV;ˀ/x+#93jn /\m,(+&q=r>2#w3?-YG~C9 sK:4bj,r fjf`\žn2*$ O$?.P꡻m`NP$;mZO*'IP ,2ג +}EU{6ҼK+oFUt0g0P=UM7[7U,%m3iG;4Ϋҗ U)9^nRv}TjH4'2J˲QXEͰi @lȎ!&È>J6.IC A.qvcAȸ8ѸK/%kKՋ!5#0Ƣ'p=UHXAH;H\;YPQ8>NBVF(ԏ.Sh\U`jnhٜ?X@YV73<'QJzaNQgN?:0L?VKFHt}sЦ9Bo†69=@Co+^sw-BqHLpXKx _+AnBQl P,8aݎ=G*ҼJz"іc>Z:^ӠI1qۢقmLsrD>oNV<P$5|qjP0b3|.ǝ4j%KP-iwc^aCSvˡ҈yAA Z0зҼaeCSyxBTTc)p\PwOHr/7Bl%yhPc}LD_ٚHGfJ X G^ 2AèlZcXN4 1RӒ_gRRaPyz ȺC¶^q3_KLO%L[,P+$lbB=#rp[O &rXd̚0Mlِ&x?^5LJ{Aí"'i@ PLǸMġKue˛iM<q(KF;I6,r17wLg0]o{L, Aw``Hq?1?/Jq>*Y*ԑ!pUnqK'e4FpH o<ɃQ64&p[]͙ob%guIzKгٝYiC&ì@قzƣ8RH5{o!mŢ;ޗ z@#œ/`F֥%Y)coBɨi؃B#Ey/q {rV.&09LSD"r7¤I~xCT-5x, 4oT`%Fuij]i5d(7&7gq2\x.y|i!?LH(lWmañeokP`8t4:e`<6̾oWwQ)T](GÆ &%}Ny.^蚤Pvuyz*bB&)"LL~2O!UCZi!p]#8{FUlVE8CHY%I "(?pB?xj 7yYVqgl@/.m`/q#O1U&faE[r_O$,6+e-PJl!O:|-/׳zSrMM!8~ s\K}xwMUCYi<D1 >@T!/YўDs ,Nrc{zI kHTZYiW((vP R}U:gͦ|_LHϦҒMT;A~;ºCF&NL9R2(8yIf&Tq+ErKl(Y6C=YcԹ ̚!ƯqGq/F=?-2Z-qdO'AMj@ 7(ZW[6/ûUdΘpz{9`T*%], Yb6o+ؒ' c g1{Ex0gfտW5M;ʗY|W뿮,̱/.=D͞먻>Q ~̬:B ੨xrv碢gwB^%=S[fp4 ړ3B*ÒR,i<] o>J( IË:2˷ý_ ;SiCU= ,@ |MY/j Sph˛R?eR(*5[х0bw "48fHjT{x闃=4 뇆{'TPB5.R~ǚ}'=*\k\i N7ʋ9(42&NBdh \Wpf'i?|킣yI!,#í%:_鬜ߍ+H4-Wݜh疉0d,ū~?MWqn?oOr~:3P]jQ$VHjMkGae)\6w1P4~ϘeXܻV(ᖗI7AEXdm?OyF3^P !6p.(u#Y EikO~Q"oLKФUjtc ~pSzn&%^$;%_ǃ"Uշ hqI4p]ڥ B|PbΜָ]DV@ŹƽA5 7U=R V*lC7YZtx6~L,r~bķ(??ԉԳEC˿u_:ӌa9Q}|Ah|B VW\K%o#$\9`϶ Y /a AײZdq̚^Vavs_U iµun>EX\N+5Dh76 &5Zɲ#4z2P$̻HKֽLDS7?ܺ3IM-qQqNPxAkL tZYnSUln`8?hˑP1( qh5VKݱe׆ZҬJfdAwcls 58p'Le`v諾\h.Rs[/S't!:𮓻xJu&' P"BEVpS1S#]Fhy4ra}ӗ{^ORt8/"4Wlн$OoNُ_7aOz95V)ch"Y{k׹-G-^ {O.HI}~]j1]JzWfHr終-O)lAO^'N>G|>JW_$tkxsbODYn1b2ݘ jWQ2Ϭ{!6kWESR8fżH w=p6=\QeJb]v0LbŐG)FiW}*1oQ_J[Ƹ Wv[T;='|f_o <fOܔ:MS3) c5-x:?N*qd"l'z99S0eLО5xGQ]8!}d&~ȓ'}f۽#(4)9)gW[*޶[)kfk%Z=qH.7Ϊ]Wd|F> endobj 381 0 obj << /Length1 2382 /Length2 27269 /Length3 0 /Length 28664 /Filter /FlateDecode >> stream xڴyePݲ5 -;A4{=9u~Ez^~fj(HDL ,̼yU;c{VQ[3+#33;3^ r(:Xy(R@{ t5Vr. &.@{ +{ {l 3e8xX Ew`458ԁ 5 U5 {b57GG"!EQT5Rj[yw ., bq|gC=jKWWG^&&F 7WFg FG[Z<m@[?¸ٛj W[2ڻI:k]w뿉 ;.@4v'V^YY`gle 77}wt5vusc{Q  .9Rux샭ǿ϶M]\\]0f{Ϭ)(HJ3ȿ=û:x'". f߇TL oĭurupbwoo4쭜܀2nc@'Ԓw&巙]_oGG b:}^O02u}Ov{sϿLwCJ~Bmf@s8&Eas?o/c;+[/-oG'L_.j>"Ǥپc0pq,]\,5/f `וաqI 0vv6c~V7(={` {#99L"MB&? $q Ŀ Ib0AYL+IzSL;O?=ֿ;2yA_)j0ʘ~bV7(kA+Lm箮ti=7(rq}:̒@{샐/lGC)EXZ\;P\rM-zav 3@me^aqe{AH6`ۂc)nm CCBG!.[]ˋ8R=7qw BfTJa$p`PKM=ِ9E2b>myYFW SilƺDa}8tBZA3T+{~K[9 oԸ4 p9)bloK6k[D kP'@TYi#Eg7!y(='zB-<⟣6GN{dgS-wSFR$*K>iOo-ASq,2 )l#-s7KЌ$}TTh+|͇Nk.|–#"tԽc>a[jN{f.nS{0҈<0_C@I8۷PVPsx 7.n}C,s%Qi4St,hŞ ctkCqɒZyNW^P9>%g{8n{;qųg.-郴= \q =tpC6M (ھ1>rZ3:c)ۨn v1bˤC8$GF9~Kʑ3VZMSt=ۮ9;Ar9nk=NT EPy8/ atAAS;SX`㡟@y'C$>wۂGk?[ C0E)"fvp>w=J Dbl=}I#Z; dqU&n}}i唨񚻭&o M;=?&gZ1O]b/* j}M X-&8}Ņ礎hpGk-02 !U\-<H8M9kjDuU9# ,;݁ us 1 E'mzGѝb`: TA_2>ؼk_%-@)>lŒ&wxG-P♞]VX܈]帲:C!^,3霝'3Xk6Ze *!i onOJ=vs^`_4b4Í.#p zNcf>{RWQu]$iZܡx0I\KP4ohW0֚IW%8T?i:&֔}J7 .癟fv!'>!h aRM+r}Tլƭ5~ A^R@R8laω"jzx)5n}#Y1t[I5\A.1}\;43(os"ɐNG)eZF.W}sF[]1ث/R WZu*w*, [=(B4HIh(*D\0K'1ZXǍ!Dv mZ ^ht ZLܻz[N-ّ'O ԡ'^51"-Ϋu4loh# XVH_jX:tw+ '<~!l_+5MlK+Atfhg79CS7=ܳ=dgt-àѾjk E3Oq ao3{)yNu:3p=[{VqxQWhu|w:"P+g 3B4_Nڣ9*X\^&gn}+vn&%1jctS݂t+_ 3. ɓþvT'yޞM}b؅iǹg+ 8Mo|/ي=b״ߖ[T[Gy|UfpMDmQ=K{¤*7͓D HG ps@Es@E'55hNqUxˆ 9 e%pWya!:'tRQ ^ȣc\7nϦnGHtm S+`>59T_ m, Kb}5ôJElGU^ǮA'GXSN`6;d]Zꆗ͗>%br  q\($eo*1Ɏ`h\:V&%U5ߗu5ʢ4̭*V_P+?t'5!~ 霘a,8/z/fݺ21񅆳Ep-Al3L8e[y\CԯkedEXE?qN%Z3mCwzˡÚ :uLXqSc͇[-[1 ň5䦷eC#Hv?8ʾ4/;aC ܙm ~MN1/((u;y-ݡBpw.w<@Ԕ,tuHc+ ~l]2%4h&ERgY[r -&km& XC.A]3P:×orXl dqwB<@ѼZ#2&t,yJȻ-x=~l`U[K˘N(0ddReOk_\-"E=d yt&*- I;;.L>l=21>#KBӕn0czIb[δVeŔKU`%-.t~l3ic bmK ,Yr-CZe5fvQ1krǀ^1շcÎ|]-*\ah'6Kp6A78^GtNpKDXG6\sW6.)ĸzy_wCx0ga>qx/_fK68%wM$ɐA&Q/+5)P qӋfFGMș3 Nh08jF-I5~0$̙JtM #1%uCPG\&Ӌk6 ^k- 8v[3S$F~deɣH0OL|1 63}t-2h%yPY;s{˜9_ibuӀD\WR a "`NHZ |&2-` SY0֭_1 )OO/ߴ]r $4ҚӬGO1@^@!V!g}֓`C4m~eUi }@6;Kpɾl a3l#'c)Wω,9@n>?ЍFoSFv3Ӳ3ap~֑ %QK D\]_c!8jDW61)j6R8JK%܋w(j&݋ 1OZ_'\anZydJ!~fC/5&M4-\4|dbZA1E6qe)Zr7\Mv M4*6~n +NyutaO*ナ}GG.|t(9AO8h(_gY7< ;ͭ>vFZ)\am )KPe΂)r#ZY#[ć~kS0ݻ~P@ 5wMk$W[:|\xLgX\Uqn,**( ɓurWnA{C5E[c)]erXr_@,ݐC~yg0l8:hWb3=/QJ< $Nx==뱇z)H$'bZ>E"qEXힹx$QM=P}n C9FwbUwHAK&4 f}c$[̺O@mݧI PJ`#p`gZժIާ2כ)$)]YPdݳۑg㜯/ ԶW؞l~oh]O?P#N5HM~t:P?85P XC> 0r s-6 V (~xɖ$~+Acg'Œor/*Iud_"ϗr-Kȷ_^7l$}bEҞ"j$ }eRgj}jMA.O2 RqC+U,h{S{nԕc[4B!N,0+I GZ COZr0azQq%lT)ڮ~v>_q§h 2u QP;'r2uL;Jyp$?XZ;DH#{\vOGIj˜7Nf%\$l[Uʘ֒d5k`d >L XCCu [|J_$^pQYj&e6f OQgK+^ebsfXfЅfm8i5oOHZU} hÄ,+t(FoM[_& _iG ̓V^c0x qBq D]&@l}M}&E^ֹô@IR\d?.‚dT}PGSEq) ф7ڹGV_X1!-˩QHW0*^'FSe7}9@Mӷjk:cD2aSN+no<"p~E. t:y>j4Ƞ"퍬o*ҥ>мYMj~ihG#j Ւw(< ں>FvbGg'ZS+^teQczMU[bWzuJrW5@CbGm>n ilY2^2Cd٫zS(],O/Gf9x|<{VuO6jwhQxN.PS:&REF*9So}bU*?yx%t6b#gt@gƼ nTCIxJJ`43Nd~G̭euPna%JylG)I*Y}i1fyJc]VD"gKaD/'K<3ɵ # m3 c>.X(8Қn+A9C04o_!vK~օE LtrZҼDoZ4zqcqےZi=H,E0BY)TrA|I䐗« :K9^觺;tDin6V[HY8{ !vR+Ein>Tl֏wϥι?󔣴眼 ЖYk覮Kb,&U>pWI4pk9F|_EhP[HGb5zYQ{@IہllGcG/\lʌ:wBQAAZ5@^50`)>svDUbXbotB'Lk'ܐ>їQ;Q0Sp/aL0:Ť 1M sO:cT؞rUD>:N! 'H< S94)7@m@#mH 1.%)Dقrq#-5o0hd8~zK2X^-NE(Wd-H֣*!!l;y̝h͠l*p>0#=I/* +7H E[[BjJYbZ^`6@j1h+J.L3 /k{+քX8lR-]l /wɷ K=V *'V!'8-,\M/,hF][B_ m]Ȩ|nyd;p;ݪTkA$ă ϧS-HARiwύ~f3RupiSE ρtN6JI-EJc_kV~uJ.k>Tsnw[[ {LeR=ry4[R>ndvYVER"!}ކμG L_y ΍0fC˱LJtǶ!&Sp/<(-YCKKAݮNV LnV[$T$r a /MSeIWC=Ii-yh!I'lrZ6TjEfu-ᘍ! pYhM>B쀫JqШ0̦xGi덺)Қc_?I~h*O{KGlֻIF٨rmϚ>˛)Y9>4ձPRh r 1hw4Ë$h(y^`cHa sP4+_>l(P>& oClwmAmx-pƦ]8[*EH.av}J\Vx>^sК\h "My* ?ڛ+oKH]KZ[#jENxG}~.\N߰Z}/{:,he3nv(|]U,{n~Y"^V`d%7,ҧYߵe}ftViR~U-~)4%̴V7'}Q~s2{TOIuMoT;J@7F(+STa r`_D/ooVtmM\&oGN@¤IPڼBi#1s:xQ~z#!%E,^0F:V~rB_hJ P-y5:F]4H)%Rs.ZubaZb +\Zbш/jews ?B?=iyywϳOgA컏|o#\s-m(Dv4  ziP/,.|}"pL>G h\좟9 ^/좬{3%Xʀr:GyBMp!~bAÄBXCJ|.6ؾ@ k dw$ho Խ3X;3f;y%5T \@ΧJ:jQKH3M4ft& Gpp͒ CyIeEk٭%Sv$n?+S!\h% А#=~ .,M<׵OؿiiSYikSև85q^>G㮀VHm">P/z 6mI7=ODq!n˙~"^`cR:2  fu]X+9C쌁lHXm#%"::STںj׬ Iy>*y&l  lbGa+wǧ$֍O x~TLy7_T1,J5&/"5̧d^4r@oʘH8V9Q<1ۦ]Xp& zC~nt.p9֢H Obp$SM=!I6x3b;q~Vt%=ax'^DU:`6,HWc'e(}N̄ y KX5ai,{ >5]<%O -Yp9 k\}l$Qc吃5iu{Æ>~ wWH}0 -dӶ{Jj\_DO|0!pFA0%q8jju3Wy9A-Gtk^A]U><Be}'f0V{>Ay(KmG5EBf@8ؽXyu:MfvxbG5+_Q;7V5G&oOEsV$"E]y'uH%k@^8H:\JfYUnzoAhV,LTiC.iF]af <7xá 'badWr {:U"p2iRJ]o@_@dQyP4AxgNiLmdȗ|6tXv|$M&Kaǁb}%i3ϒ|AfÐ]P]k6,u~V)K>Vr#'N7[9;l—ʧFbwvArVWe ST¶hG}03J)NBf΢x8isEcoS\Nr < kR=YO mI$a;PtwIF&TKkZe蟓bXdTHP§Zr%L-XvH/Կ^l_V٘h[. .,#R}SM?(H< ` +g +jΡ" չ_yto10mI* gaћF!!A-,̎C1BT`{X\-@oq)nhU{ }хP % GӚ'\W\-sG:vh渑C@2yY@7\XyMZ|{cr\!$,w-2 P!5R~G&zāRM7zح PF_O>|Sk艾܏ 0[?C$*gK[yeHv"^K:3d{{ rx &ި$8V2i|Q=KVpAzE6]֭#8|I]J"&3pO36ƪ604|BKe^8˻}1>N`(k6YZc_]#JͿkӛP9ŀHY氍[Fl, I-@T|y1:uIB,m>B~`>FØ߷p+n'FzXcX;"Mkh}s]_&[&ڨHT%"eRٛ`Vz ܛq@1lkj~7q[*'#%8mԶꄪh`]-)0w4J1񽵡u^1ud1W YMwbnie-AxRֺLoSb=A]@zFh2~ڞm2컛c)FHvMsOs4h3y iHfUO;EGDw4SoT#;f$/vՋfz;'Q\9?*< b7b@Vi|?\\q~ BW-bChOe s/zڒ\ ֬|?Kbi?)*I}Ny(gY@h` x8>rX}gs%W^+֓JyVA?ta]+ 3΄IA?a̴y 'ó'; $A^n.x9Lg{{bCmp [kg8j񼧧mT_n}?r)"gzDZhQ&hN̊j^G\ еA2> :^+! |sowvuBx%标u7ܻX=qr q1{H;u5W ةhyac0hp>V*g֭`H|yK}jD GnB5AWE~>Γ2,ԷPt?-=FHix0g]nS@̃tڊ2(b|ዏӠZHn^lo6=rYJ$m>8 ] IE<ϲG9j%'(=0jYyXkXdCEe=0'C"+[/?HS64pZ'!Dn 0*<$pװ(Ȟ z|FPC* ee"wxO ;DtU7.-/>_:ɖ=>VhtC/[o&WpCS|y5R[ XOMfS ]Kb.|0Āc<[%jXv骓a"o4\JaұN3~)9JIp!z1 AW:%ͮ;mRwaC3tC |(sB^Q2scZ {hZF(]_Nh٧$!qӬG T['IDE~>E;}8Fzd19 DCu2?-V0NY]'WjF/i6+PEBx e>]m]C*k;V4%']NS $9[tpnOD ߵ5ڲ`n=iɶChfx7|!).~uJ~䕞:oFx 3̚jgytq/0VjiMQ4ˏ&%C=K VcⶏU(Di-{IզrdxvViwo$ Ny6˰]1y%%8nUsZ}7vDz3?@tL><ra+8<}L;EKψYJU7uj?+7󲣒D}YX`N~^ρOءCFke-TBV[ W'IsݨdטDz l3 N2BHytpKxs7,pb͘K$EaBo{1o+|a"ڼT\|=.%/ +m^ݧ.#➶ReAd&Jէ #J,HRp-e:M x9m uO+vrN, Yg1* ˜QZ)LU"w)W_5'VZX7(vۺ"F6S)>6bLFPͣ&Z Q.ѻArG ~)?Ie f Ab;tZ.'ݫRu;6pVUK"έ@Ek %v#&KD N@pAhY'˺nbjd&j]|JrqJ#>D11mǣɰg.ZW8Zc*~_-s,V -7Iq _rG-}7Bb6p:.gXP<EܢT'!NWP=SHV?e'۫n0k.-&6Gv2yE pkJ?t^MĿfT9i Oe6 Zp K=KS ]z e*jTU+d܄iK@EmP{0Zy52KKh程>9~)2d+f "x=on̰F,.:!O\a1Wk[ 6\B>ysuVag>aHŅ;$+\ɠKwhUc"Rs-Mў`1 !d Lav}gEtS*q06s+j%ŻC^ Wc;'gej=W/3#"xL@(Avl1s<d7*X 0BuM9Xm59Ny`Oi玷/cp~ sQvԐhlL w:v2Eq5-׃;qL?F"}j,a4]T)$[O4&.9ͽD#^Һ%cAYo;:L@$uw'l!,T=\C0Yd6BFJ4|(N\r?74,J L#j@Vζy@U'D72v?< G6|U%3 ҘaGQj tgǤ%Yܚ|"3/噞G`}Ueڛ:HفplUC0sl ^Kܩ[ sv@󢘮J"˩ }bOZ|~2E'hlAmq"Ш&5Nplpy8A[KV6|⨫I6{"{-V5 &$ /7fmYf0347J6:1*Ӄ g!{^; `N%| e.9geboyRãcqc@VU1Aa4 ADauZmhBJJCP Ho>\6 "6=@g08G5nNG=ʲ="}I͌SfYGvF\hin.~,"3PH9t]y~`0>W3+ڂv6AţDnrjm,UZ 59_7ᑛ'@Zs[#=6^Yw9+npo}n1k^ )^2gs ^Jvr` I"ZI6Z *5~0%?W3W_/|ѹȱ暎K.?x5iy1 F X^(▴I$Ӂ6zV55g>Iަν Os0I g4y= Ga=Tʔѕ45&_ÂNi$ۚL0qf!k@yDf!ˆe;kp8X 4s-҉ԍw\c{.4!*r3mm7Zd?Wo%|xk?lA`/b] OZ6W3B0MkϾ / GgH?Z6¹_ˬTwz'8,#O<4@OMW\L;T҅$GיoTf<(LS4e%F>Ho;c4ηfD_b=bZ}גb6=+P-#Ro#0pOI{%}jUdr޽)$ЮbLHx!ܑj. CcbBkkLQW?s&[ LEBi3١h;N*u]slerYAN wC'@RBOyrS"@a} c32i{dcg>y%s,,irPU O=e};4˛Gwe"#On=qw^[(xBh?Gp8ȥ5pfWŋ0\|PXON@~Nzl8;,Ð=v+滣j w*qvwD$I{ Ny~IvRME"n)sy#SihP-lVk%T˂,vH)T5}v#7 #nIJt<)ls?HyI+SЮ׎9*'\/u?akB2k,L *6PpqPҥV9ֻxC`TY)bC1'itlHEr^Fb%YtWٗ⟾0KfZ[<,ZKF c_/rrf%g Rڡr9FE⎜/]"R\ͨVLDL9!̂ϏN]$R:!9s=o9;@2"2LJyDb]D`+㑟)}J m[JH.Vҙ@ԛKXv?(,={vgZ(̋ [+,|`tkEMc׸#Hڅ_ "bup[?`9m҉Sعtb׆R ŸC?ȽdIM#i|THvNH8>1t!K[lpڿ0AX'L@Ulxx{F# Ӈ1vWtIl֖Z(i#v3ķ")S-yRBTrW2JZӛ0S) ׍ Žkݓ֤0>l4z4A΢;_Mlnu}dЂgwTlL= h-K\B2;8-m2P l}hIai6PҀieCB{: vGDY~vn-]tgw(?T]Ɲfj:tr%440TjZHg{1Nr[EML`)7mيg/s,9hdpN~YīgM!+PPe]5Mxg9?m"r6Tm9ߪu$-c!s\or藺W`ya?_5U9^#WZ<7`ֈTꋭ5T^i6{dGe -1+D;D}%kw*jey^h@D6XtybQj ]Gw`/s-ڷb},JUƲvϳA] ̐CzlN_΍ޜӀ>'ly>@Rb2dvfW%$fL}&vA.*ܖLBPq [ˊuZn<? z5Pڳ aPv}-xT:mnl##> fp)a q7hwGr!GzunXhwiET;-zKnJgXzJ^@{:2(,̚/rr qZ.Ҧek~B|{& b[z%|'L%kRYMX ٜ|,yeO)2Zւ#HyFេM]hD(miPT#|fXm /4.޶ OB} c5?vˎSv)8FuFb?o̷<TC'Iw6ECXf`(\ l}?#mXs.'p+3èҥ6Q㮑y;4yLl/2ZH/!k2ٕ|?cSSFP@s-v?e$z 8Wװ_$lZ )pos_xIQK eT /FQhxT2Q[7E^"zSQW`ڜiSi {1]"-#wDi,x~Tg #Ӷ:3B<(? <^{O"!*˲6ͮiR`ۘ8 ]4ۚįoo8nhNVNĺm4:ӵҨdIQp[-Ƥ1@zUV`hv+͕ c ELѧp m}UcUFs1;$*~ܠmSEG}$P' _:*9'i)6dZi#ݠJT癣J15ז5H̦7ѿvo8˹FE"^LBPEWޥ44#a88gx8*Q$,;%5,l .7Q9- ^ fj>S&+T6m,mw넥? g\pp/ "v>/OZՐ4՜;uJY!G[H{cVV9IbT:6/w@_gV1R%[RD /.C"R*.AX[Ks(ԏ Tb8j 2>n`>Pr8'^/Q3WvYI\$aczIivR5(6U?W:oFQӜ,նثM@W;'kv}{Z_:‰ޯ &T h ޸+v( 5%9hg+ήf x_wEO+|FTdl{4]lfӏ݇휓{10DpeEAa$wَSR);&yݠ 0WsIroQb]~X/7w1_+vyˬ<c$(hC WvL1HmkF,Ԏspj\&)W$s$N6&Y:L<ګټ B-Kh9pg7뙟G~9Р1 wIyr=<)QA`eq 9AhqdjIeATj* ͓oGPt"Re=kq' LB$kܭuԚ Frv4z'^7eEZrY q9%DK5hĦ `ݨLiǛ@I9k3 xn83F-0m~BbAV:et]@,İq IC`7ϱ8Ϗ-9̪t>O[/I>#ˌ(+ Ǫbh/i%5!{y@M@WQV-;=dRWܭ5?<H@bsX>-z76E޴7+8װN;h !gj Lp[r?~#7g=ARG|`N-5'ݣ1)cBQʘN9twg _u`{+2+-@x?AwCaUz[o)eYB̪ă;XHbزx>"<)KP>7L 8%͡dE82GeOj&ȇdy4h9ϑDzk rt-^s9L;RțOIM^E5e0Qy+ML إɎQ-A17g"s:pƜDWvUi޿.Ahcޙ[6Xsz*37Ep={Í_FZMq 9{Rgܫ;!Q8 @P:GZ-?k2B+YT[]Ѝxw:ue˄J$T[-XYa¯ÒjlSžI7L|vMRy@ͺNN\.be (-hH'kŒ@0Aspݮ8.Wwc݌6bθRٰfJ(kխFLЛ!pb;b2IoiD+c)?1Wdc碝 D$6?q}ܪȊ0yO2D|/E**Zzu?\)U;ZL1}/o]AģyɥJ  GwAqz v>y;&LW)t0eF$Et\v_(Vro;NZ_G˔ynR2v461ײ\J6}a #,ӓc(oH,m΅w޲eL&D6EW5ne;N1 5ޝ: ζLFZVJhk~N-2:3B8>1|S  9ǼFERz9@9%~=Gƥ`m;N㷩#x]yeKvN)~O̫N;7q vn2n? FOδ{2C] J熕Uj{Rd@q1znoFLUp4%Pc 6ƹA~/]Χ q.mKRrN"TVI1΍[{ y/-}y&oOك#Yv8>Fۄf hcmƝA8"rh_8 q4){C}ҬN)zYre Y mV(K?B폜]3P1u:IL`:OsJue8dFwv'=eaHoTwvΑ*R )Ht.! HBMN伥d1⟴ZI'rźԧI"J>꫺KkʻC&J [zlTMx2vX-> endobj 383 0 obj << /Length1 2828 /Length2 33630 /Length3 0 /Length 35217 /Filter /FlateDecode >> stream xڴeT\]5 #!kЂNpwwR8N%;wwwwb.sͽg*)(sf`adɫlXT.6FVFffv QG%N r(8s<I4Fj@?@ `lv-4Ql +aXܜ-FvFyF lP@ #3 $UՕTiU]AETUM] &&j$U~Tځˋ i+0p::Y^QP9lY@mlhr4g h :mbg w gisNi ;X5mpk #r唔Fv@;#;p?67Д_QGkeM]Lw/mmӿ*f6~?6yai qU591ȃ1:r'w=a19^73' Rq;SQ-o,:9ݙy8,L~kobϤngp  `O&Lg^~Y~BxyڃfF6N@/K3 pvtzyo04q: S] ߮* l@3&3x$sg- #[ ZڸwhV9IT_.la;s x[1>R6?,? , 4 Xa V7_;6ĉۙL-#GG#wf,rphļYHo&$ Qx (m^V+ƮL Pą4}K[her'cCͣ̏@:cEu /N} ykц ^ F{)N}ﰺթȇ\@%e 2&ߩ/Wz\*3]gm!%$ν$kr[I`&hi92#i.w{:C/pwu BOdy} Ej n xu8O%"_eEf|M.9(z W bZc`Pk&;Ihٙ[TϜT wh/#E ͂n)@sO&9z^eE83KY)Hǯ\jGtP5h֊Ɔ"i:(P֪4Bb_ҟ1?s<9DGC5UDU0K@=Y"M0RA,$EQإTἐY=^Nek+_ni{$$5".|ﲾMa bUԑ7smj3MZA>*KbU[q+vi~9ti K(++Lr*~%A]/I7uD^0Ńvđ{?2>wwA1K_}{p1$?5)O5ػ ^(,QtI[}Ae(P9DU}3y>\>Ap n@Zr<ӫ4fqTCćY삓[m9TXkhRkoqA+6Ǝͩ1)_ZyVxl OAΞiRl#eD3%y= m}|VPdžqs}{ޘM7;Wg^BGBlbNC9 }JdW.ZmOJO!#ƺT&G@8/G'YYpi$G_!O7U?E#!]Vgn:%0bvϨ&2 8(ٗV NZɽuE1*a:ρ-hX:՞r4m>fN[V+u:J[ݨl'doUJ,f~?cHžBӛWF^7 6B-j#2A.'qqE%a T=8R!7 DF|,o =.g8q\~d}I#e:m.6!ąٓۺ@XչoJY&9k+Rs[D UC\>T,r(=A`bKVHc1IG_s g8$>2 =g:_$Ivwj`=EnP\9^qED #bU7}jRTUNۑ0̡G3)h)By|v4cw`UhjL^Ez6[ 6ճG㋎-m7,Jh]$LACO46a 7.ָJ]iʌC*sܓqߍ#6q\6{딌xmT-;lBbպ:4rnY.!ʠ6_BOYU[jXd)s "Uٶ#Kqu'ѧ_DBHi(bdf~TYv*#L4$Ã5TDK\ Մo/Q6ς[Sd1!X=@̑ 7Ԛ-p-8zÊR}N5G2ʣ)7僱'F]n6/)+F`_ ]{;@[Z|p\&7؎s/L|q8OnW_=G2+5H4>3$c@YgL!?}%nMɁN(cZ/fK5 "*cع!"};T|P&_Fi!zV8RYs(S1 ɬn؇{17c$GP.+_7"quz#/P} Q#nB\e4<3M趴WkXgV8?j&Q 4Y0n@/َbÌ y.2dZ](!"t%~lslcQ%`?>wc@RMhcnb dވ_-PyG~"wvT4!qnqC{G$;ZS 8o3[eFeDq٦?n\~a/РBmar(L 5jי u7dv`#SGNe kv]<.OdڧCKSbQX뙭Γk: UkO3aL-SsPSD~V!6;*h„zu5RFB:3Ҿj- }GaOo1~Rj⵶.ƶK't֍?<_8w=C0ڴ4CbAp#oxL=AHt[Te9]&AwZ iE5?Lg4buOĹ&k=ʶ#_AQӞv?w9po'[vT>V Dc$FmW OecN訍rvӑZ)D>:J-OέN-8q?!oڻ߳&Fr,s<9$Irn. OP-F_HƖW{;/Ib1i? W$sAɤ_,*=mP $ƥn=o6b6E"]s Scqh6B*XF'2~gF sv<+ޖm q_OS)r!WyNNN=x!g~ &ffzsxhsd֗oʝ d^]C "1Uk f򭐰Bkv}p=랔?3=[.q*|ZefwZ`Sr#Iǣlϝ{W z7 Dߝ羊g&576\by]#ީm蜤L3yTحZsq0ghXő6LNa F6_b#,łshV :7 ){!e\ey̤7!EDSp^3^ƪ 1w{}1R"֝1x44eNNQήIWdCBvgG>6C$ Li~\:+w+Z_ ZHU%D{/-.AL?1PS04nx2EYqn+JK…srIE '+$3Zo Z@vL[L!l \s{̑`X,ؓ1y^t~Ft1h>'Χ`dfVSHjtVQn•x/s0w=?O*Z ڙ"85S\o,C`ٟ{'%e?O2$5갯_Ƕ 7` zjT~YlJϏS$PؔRvt**_DLT=9$9-2\àMqCNO6/òglw G*ū%r6gtl?tb3tWThf} > 'wC :t7#<q&jg@Yw&_SyyMU,Za.xOr#Tq7\2 Ad#V 6 lYݥ/Ndʂ/' D?ܚtVQоZڶQ)h(bxPb"3%ayeelk=m൐D-Nz܏Y6i[u՜grgv2+@.#3x9C.b:l eb6\qTկ #Mr˨9~;K2XfxK4)v]NF5{l2"l$lvs*˙&e8Q:IU2:3ZX}4fxћtӿ%AڰWd!Q'-߇lċ<#OA")^ɳ{fbo<1g=#z9HPQL|p6e j #xNJda!{̵w+ˋbdNZ*eOᒁ[V/o|vPL\JjoԽ CHTaH ^tLwMM첖qۜʮN0ӱs/#-M4~ϩFe#<>'bV΄ oǒubt%BL+=~71wCpDaͩlsCR=A)!ݣ̩NM)aM/rRVeHZܒK4D_!x.$:]KO %%pP}GpOMfcz|5/N$· VIĬ{IqEŴa;JSIB K\Fm_iq:b4=scc&Bz#5LHFYf.ؼIF`>b>f4 h h + s$Ɍ eEէ[{a6=„Xr qMKb^vLH1E 0} Ew빚be}v}b'>LEQls;Z)m !xg͊%><7:8Z'eoNf;^1HqjjtCZYaC(HșLH`P$_ XIz}9֎dsYN@Ku RX9wJ*!G}3g ?(%%00 Ato4#Om+`P\Ć̾\ilb;0MU%'LQ%%o( 07!oym{_AR~/@+!ܢuvĝU^ȫGcmO{b=8$ERf.rk|F>Zf$xtCw:*K w)R{Ըk[ A>oI|fHrpetU+0ζJ@2փG"0X;G@[OKǙղu?+mP @9U@^/9ysTk?Fҟ7bC]>8:%}j~u0N4uG+^n~Έߪ#B ]ѕq¼Y{: YDBϲb2RN H1n}vnR(c9wVg?Q Fm|srLwl:氩[9ء~V Z{ɔx^EVh#_wڟ{XV/gn/;۳]ihXnxш!^X*Qwb^bm!R0̹tTB F̱F7(zL;iE+|m}hB֔mJww7ӊ}XQsUGԤ JD2mWkk}yN-mUϔ/,q hE-:haM f2"Щ#6QB)<Cs{ Q#ѾcZKYBQK#kޫFH,o4!gDc{U:YΉS ε0zȻ|˜|N$5yn'6esԵZ+Y=E5 ì2ௗj.ŻґLV'QiH%ub[ͻ-P0j-lOXxFh((:Fdn"KB^6,pLL ]*mŀC7,V8$H僜 }թMEA ^g=  { g 5T o81}ɝ:ӭ.-h(kE}nR ?:wOHnܘ$FvcEn Fބ w_ضLK/Q/yXㄎh `\mE_ڑ)Xആ_J8Q=)_zd HV;EpW ˆC|ϑ%͟וȍ~S NW5%dkPF!6\l Tc@sɅ+X~tA h-bZB\]9bsg{\b @ HQ#Gd}hλݿituCS|eoEgjDԜPjS fX`H(-0^ P5NS._̭Rtd'WO)Irj߽h]%a?wZ![ uh^MQ t])L^{BqQ5O\qQwt3kvf֖^Q c&w`.2I5CrNDZLXq}Xlx)_Aa.Q϶%pɬLZZj"ޱgU]DM}-ilxcੲڪK89NAp2ϛ;ȣQQƍ1Wٕ/I_Uuw bzdQs6.O_]{x=~l.@_CW9WoG@Tmr %DDxb ou7fїe<6(L01\]@8Lqgq㍣ wtDt \d3U\yZh4wďÇI[xG,_^% M,%ODγXAJ.O‰QR1ʣR:!Y!w}0fCb:ܳ}A3S\BD*ĽدowɩZfj#x,b%1s< .ԎQ0-ovd0ΉI,Ց?۷%eh=8OP=`mW7MCb5FOx 3!T`O\]څR%4c͉BCkrcXVDcBQ%YGjMT6TU_]k?`+xMyϥ7\ժ_lwN*#x{ џ?Ή4rR2~B.a u-UyELbp;sNj.A IP?(!!TrN88u`뤚EagGrKoS.{LEí cź3Zf2 z1Gfi\fKX܋<"W8uGEa`/Uan3 2KP1mX'26(MEš H6OT0ULvMҊU,{$Ji1L1y+pz+wCɥу-nS$[R'7cungLŠ]3Cβn;:G*C5˕ v7͍OT Ʌ t Jq MD:UXl1&9pđ-YX{pU+mn:fiMh?(V l"n|%y(Dssy07ɔA MW8PşA[B~3 t'\&N"u% hR"F,XLb`"#DD)ǬIO &*ra5)'=!a OO,ېGR1?ޗiP %o/Qťцځd9Iʕ* s)q%8=۟|-H-߶.˯ |Vz`(V㔶I|>jdꗄb[20RSV F·o+6Hb2%{4dC8'eI=i}ULy$lFpa,R g)ɃeDarkm"'Aϡ3]B "sxXͩX}Qӥm9ٰvcR]El e`b+ [ A4Јi0Y\~Qi GD>zf|&dZ%F3/Dˈh{7yǤG 1Q/K<(pa#85|PƣCrLK!%>"^%hs0.NSz/ݧy!< (:t̲o_Α'H۪ihG.W` e1JY'?E< L $ >1$}>iG@۴?Ov ?GNA=-L _~\z! eK=Vv:c1.W2S{/ v|RT=Q5F3gj7;RR?('gdW VP"k/4x9 s.Mǫ0Sx8S3 @[F,;Tܬ7_~ZGȹw2 },6Ug NgQ3W4-Q'̒&M9~sK>D =`Z7MACL)([.}urd*e>4SBX ϒ< 8B-UUH9Fù+}h b?_ugy[mS!eUv_Ykuq7P5[ Kʸdbwò *.47>MtX}mty~2~qlx:7G!ǕGi-udUU"5zk7]YF@($T.a| oU˶ ?/{7keJܷؔdTPK2 P}ruZ_,p|hQbѰ~C!ګ mqb8 f+f[!/pgivFL|p'LgYWo,69o&0,v'0(XcWNDøP县R5 ƒ`iz˳YJt) \:dOՌySK|EyNԔRHaLHFZ 2b *̆q6ʓ+V5IC&=ꬉe8x=C9R5i@%0e[zVc+FX(msz :C"y]"\|+w`[ AO]:_b Үz0ʈyf.|ի5,[Е3+JnOj&R(]/ 3Tl+o [)0HIi1b TSTk.'D%AiVolHǨ.ι\܃aُժd 9:čl('&-`No3|1wrE؆ҳ刨%tkJ#<#fku1d ߰?lfk\>25egd ҿ*C}yϘ:ژq me^t҄;f_El=enڃ~6427u>vӓ@$-6c)Nl:<ۃϣ Q] j@"}c7Dq4ɚ\p:92r/g/eHgrF0}d?ZZ׈ H8Ѳ%&U2o|vz'!0}kuAí0x%;gJ%j=}3}Z+a޺˖_8CLwK1.Ұ!$F|*}n-dbmخ̈́di5 (nVeޑ4qw o‡7yԨ=FK=IJ]].}ޚi`Д?9:/}|9TS•WIvEFZS(F`J$.8[A)J/ԖG1R⺃.VaZuݴ!OƬ&SS Tȵ,eOՈPכsiDŽ=YAF܍ 4>P])2\dۣ?i=3EbZÈ@fӊyEQ^1T=J9qB{ymõ쥝ثRN䠾N6Q fy{f9"旙!+4%z1U\ot5>-tQл1Zd6&=tT{ W8*;!UN=h^J5:T~Vv6[~}H:ߴf ?3.b=ph,!] 5hDMʙ[ܗj@Driɏn&cjQoW [=ZEUA #j`zMwNEܡ‰3фǾv_q~ ڿo\'n`)fPo -fΙ=Τ.e ws5GRTZI6bwgҸ C:aE6q{oH DLS8/rɮ ms-Y@yn h-bծd ,Eob؞7{҉<}kܯ8=`HuE#K\7y-\=ޑ9h٤\b11*VnvhjNPPa"5TNJRr#k3eXi0ʯ) ӋO{\ȘjF2|(}B EDx[LiVs: m 1hr[H)lǶmcYuHYP<8Az֍>ɋ dV>Г( fV˂ڜnJMzܲ.pjx#8FG[W܄;svW/Y8Fu5Jk$`6 }%?`!1嫙+SrZ?pn>`M 8䉿 B5mٵFJ~?v=KM%}"^e.n ^߬hك(= `V XV=V=گ׸3;Co`ڷu 2`G-:Ei3=$Z[*} b)Es^8}!!|v;uͷ@&n=_PPGiٜmYiݰZ/k,&J^M˛9PDz(^N`Bz!"IܾW~2hR-&}P-';|׻Ə?X?PCnyc&d5@^I(|A̳de$^坈uO!BthR-j 65CJB q*Az&/sAbjz{5X#-A \ZBSFr #f3?@a*faIKB8bηKluGyB)An.ϢTGRj_CBH*Է79yM'2ζ{Ձ\T* k|T7(zf};KF=z!B*,k,u֒Zak-'f1IQ_^>3F'{[#`m۶m۶mn۶m۶mܗ6?QI%+8+J2v!ky"i yJZ%}(M;H\K(]0$)eU`yU p0T"enz ږ'1 ГϣcHZ[6GgxkfچR :8?7h>lSCDl#gsϔ;{qzG.ahōjBUi'm:$KΦ=҅y_Rl52"%"|R[Kf,ΎG1@E]5`Zw8l<|L,Q=NT?|EzXctRRy{{Fcx-t*.Ku)ъ+4#;(XQ%0NqcTXl Y *噚dWɔJR~eNƜ|2 ecyLـDrh^7[YIZ z5ylO8+5dEtO„y?X\wGm N'y f(< -Pz+?\xA[)A11)!5 Z.7/҅z; ,;"XN2TsgD؁ٚ|px 0Fd'THń m#ڧOgָumA.VknN1ЈCj*zhB\`dCPŠO3>\:)w6=Q3m?{ 4S[/WSS?C@V'yT1x!1Cs>H@#{U 2Q,V%W.w˒J(s@\Rg5oxzaa)F[|T[xR^gM/sTr5$YweKtJ~SY/CfeI9D[fTYDYn=H@nS]TBLt\Hx0El K:ϩ1h2s3mAŘ$Q6X$-vP'zu*)zUwi 8<s:C:ד~C@bON%1[pyM],,[N ;/Ff+XdŖؐ)}m~ >tMOst?4c5(iJ^jJ~Hg2rWJ*̬!@75Zy&IL+WՒ8ٿnM4&c]II0lEPelTzRj)pWCA4c99*!;-'(/3yA +77'Ǎ2#(x1vdHgaūID?w"y:{Dsd/ ܩ/b0D엉} Sriu$A ~Q8r( r߱z x3ezu=Glp {kܓ.Ky}31" ;c<-ʂ&ÕՁ)v̤3*v|g증WJ/fY{ $41:C:>J+~h46wL}VX5xMwYl,Y=?z0Gce4޵D4YfϢcEĴ&?輿"{ 3rs YD8.x0PdG`ށQJ`kX:ljb}*kh7րt]|#9yc`buMFX4nt{s,c fj@ O2ُcOV)\?wph:Nj]YVpIFB'-GEw\0.%I~gfs=^ %H'\c }LHXvNY6unp bsK:u0vTYWZ;C4 (AA/&n޲M>x72:%u;#5׋N1+eQ%*q9[ebJxKmVC)%FEQ-SBkk_g9㵩Bd6*utV}PWծ k `c'.E3ƤcSVJ_{1ls𘆈/zxΕY7nMt Wo \ {ۚIdNOqvc>6JNE;.g La) ĤG4:5ӎqBx &V9**YX㱇״mi)j%D"gQwzYd'TNݜ==3Y|8>(.qhl۪P]ޮ\#w_'\ɧyqU̅!˜I^@ث{odq Im@ne.¦f6f OQ-hf2_ QBm p][_ʦ^Q9u7%Ś^-ss'ED z!ﳈ%Ch zoăH=nEs'O@0G `ГZ+P)65χc6&gU. mgIJs5y߁ ]XxӈFW٪XE CL6[nQEVԂ>qOȶ 1#.GI'`*9d;P {jby~=7t>L"`ʶkQ=R!%y!Zd%3a6W[F] 2 U'-RwlR;U q.'{=摽%smfzf{4L>/?1w[ziU8ؤQ8W\GKmZ 2y!ݒo낃SHfǧȦ9"_n/ׄVBG{LQ;(xN>g:aFҰ2t_<%<9:DBgIͺY2="x6 Jux 0e_(:$8@XH$R1}9_4!\NY*ZѳnL.U: ''$ JtXE*hIlKXM?c:ϋ8/?إ /EUQ+NX^h̑ӋqPtע17z4Nwqltk -cnϸ.H1Bb&"ُe`qǞgqVO qLF2;K_~;wZ&E1;lrǎ) Y%nj [yvIrM4c2.DqXQK%OAm$](Lkm$qqQMa-!B6^D`ãSSngvv < f cGǀ"H4O46f"Ey OSL TD$Q.E5 [ے/O?j)BA!a1atAJVt@utb#(]$JZuӼf9A#v(Tc/m W"BՏP8w@s6Uj\#Kn :hq@ZPWJ`B9@")1lؚaSܿ(;tv]<8,yDStF\ͻ!F n/VIFP{͉lFG/U7^DކrvH7] 2X))xb*nJf(f1hkk+}F- @Ci1A50tf]0%s*tT 8mw 9 6DX/ ,}% n |YϤaT+Qr_RUy'{kmEx'XJr*-I豢8ӥyz]OɷGs0>t`F1_%&QDߛdj+4#4ձD* Yjhy*BHg߶K"1;6?Ņm6Dȫ}0H}e,^}xDJYP\2C7XDy7 ai6^՝l..G./Xm*CIY+}V=:p{"zP8nǓ9;bmeM3U"[ wKi#.oû*Q%p;ps ԽSo"2_/Ӓ,XW,}> mk  J݋* ]pfIv?c*f6gq6c)\'K/] \!]3* X"c4Tw"^7Bb GvG26m7HDpr4E^29*NYSS@v'ܩ=hEVK BŚɂrY` Ox8vo\w#H5=\ɮY#&|5[;P6ķNS@!l\9e]GOhL YKh`ONu\ߤqX+nMlI#jͲ!>z8-۪G7t8uP #.@%n\Fź뿮sM+Bёgz:m%mp q?Z7sxdl(sxdQ#R>[&KA]6rx`_qY Ta zngcJFrmc ızc0xHS)YRIƲvu\e!:C: њ^E]g:.r[;ÛG"ZojїlM x~@#&:,SsjWjX{zT`'= N!HpoM!A`n30}KM ?P b'~x'FV٣;Cw{t6}FHwQ+j_L4sF;Y 1& d~7jW')a,Y}y$YF)~O72 tp?̽b*3iX-&CwV黉UwSܬw| |`.ϋ9Nhh D]"o=dU&Z-kšɌ #S<{5-0qg x~z%{(d,vN GEKgDK:* bcAk 9+ a^uɚ<6 ^HUvG.@"MB;! RgAT*DBbThSMǾV4{&J }= z"M>޾Mڙykwwa~uwhAWq .8ɪ{CTZqz((MɫU*@A5[[4J)Xy> ƍ[}ܙ4 cC(5n 3\[ǧ.eh9!،cRp5sjrV~.a~1as#x;7c^yLSw&wGX8Lg F|$B׵GSۺ*C;HB*[)cH*Em&Kǁ{dm Â#;4WRs8 !B؆H֖{)w^x4#$龭 9F$!{ϳ&Yc)km2r.aat1611d7vmFw%LzkcF Y9nyCQV𮨷[< uU/[~Ll׼;{P44T`Ǟ5 8&CxEYyh)z)MkU\Z>r_v-nP&Whb3pGX6V2։gՓj^T };ǎ4T3.W u ~8a45sx%7( iV/F%=mwQ>ʆ_W[Z~d6ڤ}0xTxQJ'ʟon.꟭xP{e$IUO|3/y>@B=ӴXV.#,ja\UTV!)SZzbsۘ(к!04|Zdaީku؋I}DgXY9n$Cgy:={3sR Wqb{䁾Q!._QMUuk]#[y PlSހCd<{I~TO^"OR76LK$[jXH#M=k?)4 EM n!| AdnODFn(҇'ҷMKˬrF,ԖQ@Ԭ=vmos2Jo$r1׃w ލYv@ؚ7R>kD]r{fԁ;9_> vf?#e?#GV3 a/?2i@-%䙖.&Lh+8^or6mC+9uz>ѩ_uθU\/?[Ͱh6[o5nh&?+GHp=MtΖ3N"Һy]K"6!`M4ml6L#pW%#Xn*| !L~ $'PqX,,f/RѾ?sG^ ;P@p_dWInB|ux ,YiQ33, a/l6Ng#ΉpEx_'Ho(0 ;qtݯZJi =T_B4ۊb qGKh|z,n4%Ani74S+1?B;t e H;DŘq7ΉcԆx%PS$0f~tkqԎӡ ?$$?WS铐oMׅ;%i*- d&qώ0DhiC 9=S&?uD WJh>T :߼ BGQve宐7kb yT*My1Sw_ZˆU;k\|GA6eMj*|ӴV>{W^C9MMr EhK%|Vx0h!\ i/ 'ABJmgAć 'aM5JLti %Ax$?ۥ8;vkAuZVn!lTм\Q| 85/A,dsR9 YnDLEl4 iվTͲli'F nD&G^+S_H@ SAvH A%/\%r׬C$ߍ~@}z: 3D&-%̮VY~,T Fwy7P<1;렰苡$WW! ݚlsa_X5ªTԪ\WsraMnGs՜ BBJ=rJM Ydrs4\ؿi'?-e #>|C}neՋl:A*S:m*/- = ,d#nDۆS@h](_8}[;SBR \Z;<D"Jzv9u]kfN>řfR[^~-Sd}(n:"@%8(v]h@wQ ƽ>\2ziq}oLBY;l㍂T,.8Rȥqs 6yEq=OKhӹSaփ$?n0X1g{0ϝϧtGkܨ^ߓَhr2k&c)a?Z!YƠt+qu*Ѕr4 C+xsS|V͙[/ͷ_oئXf*0#FAv5LEd#RۢݟճԄbW%KˢO%}UN|!-`hOeRlK(_\˼:%,,PL tGEdR,U\tg#!CҸ"h!uE9S>eޘ9BMP]|Vjsa)95x* ˱3*=v֏mTZ 5z"_F [{B'ڀr(֍D@ɏdG OֶKΪ0,7|9osQ>+o+Jp3-3Rz#<1`tׯa.9&°zyT~:| VLJSA N4H*y4 fWVAdEF3ӧplB}t 9q>FY`[or}vG3`WEk3pGB]vܭ%I¢FqVFu&JIKUӰ|rH`tur aj35pCJA5& -$4~JȦعfK$hh8e'+=DHD^#RYWoDC5C.O{](ċ(* X^O>DT~k(B2Uj(:9z&B6aE9Faˡ!Muy@;*vx,>:\؀fo]R6'u T`$ 2qUo2T'9awcdPn M cSrY=MBk%L NfC¹-f\¶a"^P]K"+7>[CT 0 pmB&=O(Idc })p~+ @>7(% P rt0 CsެpȽyM*jfNغ58O`+fxͯ;i5i4uѺ"IqLW2m*OtWk>g2#M'#8]Adw}7(7414vޖv-% (M_uizKT֋ڪXwx<_zd<@Ikڡn i/<ȩ+;R7I408F.G|]yOӆFN^3Ej=EѴۙ0r?7=F%_w+>aK{f] ;7y5Ebv:sR/~#:uċDsL '=! bu:Dvf(C=)@:<@;%'lFU9_z!8Sbe{< fFow&N-Oy>*)àE1?Mqj@uyɑ7=yj%Cez7sſ/(- P_H(D\M+izp݌~k[!)%Fۗ` c]j92&q@ͬIn)~,`O˫dr1: sxߑ2c!-%.?bRAP)/v;h$- UU?ߜ(CPEI>r`[t`;IN)+/0X"ci(H=C5W!O#=5Q{5@ڧ 9f@hضE^N1:: J!XH`uMlkr{5)v2p811+ear-'@bB .ǎ꾮Ns}Itn,wbX0TD-4y/6 Pʨ/nvzT ZH8m{X07C%SF;r ex:̶|Nbzi8S:=#̀"; S3 %WetW|%wJA͑7@7)>fljbR X6`S)J:ܓq!" ~2U(3Q|&Kr4KJ o'AVi;-W9kt׀;/[-p͒>[ ]4x}GB̎qPӉ>g=@Ą0HQ<@OZ 9-Ju[/e=NBswئB'پȠ9ȵ}^x,.'k@^QVsn > Fno޶*r*S&T.>b\!N+TX?ӧ&x9KaJ?q6UMV((%e"#c.׬3/wEat_FwZI$"qZ^spCSfXTߝW f5܊=+RpP] 5;/q.Biw#)*uV t ,1!}w)]`-˔CnO!~E/48 ~sm`ɟlOaK_ ̴(;v.i>l"i j**)L6(T?5+ص=88&<8.H^n m(--4ZH+1j&K/oUdYjCi2g,ֳ9q](e:P7K`l[^a SzߪX H :[N1huVLgxpfb(KtSP e6,aCZi[wmB;P˺.#m>}:!d1׹ ~t[mJPVz0ݠJlL| W^|F$ad@Bt;O_+_ RU䭟#MIkE+&I2tfnY/ .u>/|>/Q*`+7!&7)d}M`Kv,> ϣQ< VJID5'3al/҅@# 9JʵNAMNs.0܂; {O׭If5O Ru#S 9ᗮ鸾Nw  jٺ@6Z";( lZw8:62$)oU]@dUdbӼ4ƒ?'E[!>mۑ0~ƝSBgւHQIl>8~d"rpݕ4eb#M!e* cM魄Tugrn.}}k,bP cػ^7ocd.5k ,rm` $W 42Fc _| Ht%49hlDbeZBȢzծRg` "bv&,\jj`/zC( 5V>KRO^=q/A`ryu UɓP*D$Ƨvc,9qR}[VeMҺ>o.*[H;^}+ CEjʹ),y!Amޏ0a3v})=n"nD[7|\_X=;Mz XXlr`K 2=9ii0ď3tMmt E ğ0i]Ha[="аB.G0P; BO,d`S GQHy`-M+auVL\fZn+-(IƘÜp4,vHx,ܬm endstream endobj 384 0 obj << /Type /FontDescriptor /FontName /XVMAGV+LMRoman10-Regular /Flags 4 /FontBBox [-430 -290 1417 1127] /Ascent 689 /CapHeight 689 /Descent -194 /ItalicAngle 0 /StemV 69 /XHeight 431 /CharSet (/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/a/b/c/colon/comma/d/e/eight/equal/exclam/f/ff/ffi/fi/five/fl/four/g/greater/h/hyphen/i/j/k/l/m/n/nine/o/one/p/parenleft/parenright/period/q/quotedbl/quoteright/r/s/seven/six/slash/t/three/two/u/underscore/v/w/x/y/z/zero) /FontFile 383 0 R >> endobj 385 0 obj << /Length1 1687 /Length2 19757 /Length3 0 /Length 20888 /Filter /FlateDecode >> stream xڴuT\߲-{ݽqh -w.νs=z^VUkɉL@F@q=#7@FV dchA4s6t032"8 ,@N@n9@#à  :|MFY @e@DgdaښY?BD@vfN`;haz`hkȁ\?@ `476L*@ 2@BI^UAcbeg;;qQVQ ɩj UeW3Zʇoǿb*B* bL `-nC#dO7+=~*WSg[r:5MXmA2|#w?> wNKcnO hkhkd0xM(EqvpCM'S}L?wߗm uptr׌@5/{ǿ{fa&+$'%.B'!<[:YGulܜ; 7 `惵#Z| j&+olǠjka /03݌G-aG=@vSCkG)h898=GL c4 ?Kٚ\?$@ORt `4E`9}>\r6@,XXxr9ZQ h`dl r2о5cSTn?G?l4::XX1??j.AAQDMTlA&ff6v;L6-#` 09 M6f)/`Ϙh?vNv?bd%QXdT086㇒>+ZXIcb0\Y-_?*4t#,y,SB| ˡɹ*5V2;qD~ [2) A2ܺ>AoIUS&>>bBcj~]$ҹ5KXg3۾TND:'~蔷C35c8Xv[F_wzˆ2Z35& cۍ~v 3pš"XHQ;i#H̾A3^2]!Uw~hc3ɳ./wКt'T{𯩲Ý;yR2 7)1\</f.p7X0r})vD\׸HN&LJԭ@T̡eA"ֈy]kbL**&XW/lKjƠP|"'?@Jù|?R@ȍ KHt[L#dSuٺt٫.#y`hE![˾ƴ >AG,(69;l -L-3WK,w8cg[H:0❄ςV&.(]:|MY5,O@ܹ9m~1 )Ã0~d^ӪAh?ɯe%O:f GMF_}+߾["΃]9U[)k+_F;jH`6EьE#qs $Õwk@-jB&<6FU@㬱 6dkC_ڍ X$Gl&ˌE݇yA}*x7SRnjꩀZG)fєW;wK.\1ʑ<`aZ=;ԸBc_UHHowȜ|4p%[rH! 7^KD襸З x?/Մq4)ة٩!u\z/D?W9ľ/5y}rm~\iq{bM +OW7VYXƚ OR]D6p-Ț6lN6vb'GxtӇ :erո( U`JR":v/tU%"ZCȟs|6? ˢQL-~6BjeseDEc?ߛdl@pVuE ƴ.<~2eq}(IT^mM_3CV% Jm7{aiiRZT}}_'VyB_n`jo S\s7DIȬp"2-ERϸ1iMrO[5Ǧ|jQ`묞#B{yUˌV[ן!a 4eو-Qzؔǵ}>)_6qZ,9:0uoz#ȋsIp-ܰ; Z?)*č p~Q$g0$}_ГM`aIxQ֫ۅ1ϊĺé$\IdWTL =Ծw{8qXڧP_?Wo9N2p 'uM,jC! 2h3mC ({Q9l }dƕqmM-1|oqԌ.~".WbI_9Ɩyd㸒 ).-Ta:W8&xf°3^xHu%BGCϟ:kTF2s+]{%vDd#\,b7;P˫wjV @pSt]׻i{wt?Tfڏ ?|H*~y-ʄ Ysz4*!c5 QI+ 8 8e<\ӥD$%A&@yEnG\1_gf֞pHeg t^/:,,/ϥ5 $?KKw|9ywuF,pЉofz&ɍIh>S T*=L]'qN>~2ؽ r`qޢjy&Ї\\.E 'ko Rkkgy݃Y)^®X~H(򹤈ȯe_teyQ@bj.Pdd7:IMlV4bK7x[ufwVzfyrQ 4m0~fxe4 -]`j 68_U}*AaI:\$ zFY {|]Si oot!1˄'5'n%yto~Q(*E5#pBml"8>GNqT-:fWeWE JKɌ=" Xѩ|Eud!?V1p"dm#PB@ukR$RQICi xY'֭bi-ݲ&33gu[[ZOJJ:N?#)VcM>Rf%1]rHm2T.>CE<1 w4Q"2nr*rIY0uR*O eN$1 P΢,PSV=u2s "6AY?!wFwJ,-:Uc m׆4?s h0 Dd?T9ւG vf-n s~qD"ngK ݋vj |l̔2[=W9"EE{39|VL Vן D\ID9 5%mHl %a8ZA8C0Zkc[S_)@5Nkޫw*Ъ;1Zi‰3-\9hpđ=[82WVI G6{>՟ip8ިR(њEBH_މX$Ϋ Ri:U) vr¥\od8KyPj֌f1U]~dM7 MϖtR0SHҩ&B+eEI\3 '}eoIhR!=8%fM($=_4.IJ(!X4RM.@,IaCXw^-(1oݷYv(;byI ,(ܵuB1%ݫtBɵ#9#bdá:-س鼔<˦Tmr @-yVM{bZg7ݠjtڱnֻ2_v*/^;vQ 3#Ū@I @~?)y@Þ s# TJ&ـjS_ڄ۞dIث EVG nj?RBP_2d- 02yTtgQͩr9w 4bGqƴoDhԶ\BsԌ[KO.uWGgm/ î)є={=tz9\v8~-WmdmEtAxݻB8 )]+PY{晑È/T=r3OF`r qޡ`rtռ;@uPħY ɹ'c"fm0&y8ŲkRF.WH:{:"?W>lO9{aC:۟ejOaBL=ogZ_|qWQEx7TWk!MFUXNʞ?"kfty`6miEӳ#!Ou5&R[%2Ff l1Wk./c /$nVlV>ܭ~!Be]E8C%=rSWK&lhS[L =F +})k*0,3&~DoĄ1g,[bBQzn`06#(wzz_N%gYc܏+YRs=X3|]eshb!n^>+eW*m\vˊVg-ZJ}lh;W! їQP{&z#7/se#~>")Q ٟ`Ks,-G1؊[dfpR:hGA)ec-@ kPǴ&#x:A$%ī\3dm^!˛]ؕl) ./{ԊQNÖOl~D\yfU!\̍ޕȯp }zNКі+Cch:{jwR̳Ze"ߋN̶VGrfb`H2D:}~cuOJgyl Y;ˆ,\{$Gm aN1k5]ykfNq ( Z(& 넣*;ƴ C)OW-Rmwņ~-Ff'}2k>RUU^y~ϻ JzFc㞔+1P/l9D 䶯޽2/MeծZ'I=Rv~ ܦj1)u;7Z-ޣ0=qq:bVj}20C ('0;7*-"});r'T6Zxa/9YLu;f,\_bpVА00ھU(zb"4YWh0[%w{)%lk2$wr%Ep 6¬Y%`8eM|o(X'鵇+4!.4$J*FV뇥n4emU?^dޮY'mzr10"ak~MVV\V~@Q$XNxp2Gz+ا,Ik'h}J{u%d|gp s>S-xQ)XK2oVFM @0rxG턐WCEWƆ u/ gvra*?~Wv}EbofjZ[%L2y\t^ !0*ZpzPQ`T5 'ɤb ݿc(GrA=Ɯ lxc,|܋nיP~?ls+ajLKRya7J`937 (/ djVwbjs2$p9b:mnR%YՇ'%1YٶS\ i1)1D"s,`jla z_b>_?\$p#Cq>&7Trte0i?SR1b}'ڵ 5|15Qj()t>yׁi<D K{ISZoT)-/] Z`=нP:dF4 "rC +M0V#'Bu˭D5lBcZ;)pB$:EJx5SuW8,Wd/^?['69ii{cQLF--L4W;n G)~-CH_(G'M^1l-p @,H3SⷤpzZ"/cY-b(>0I^շ@4i!#ϡn0CxvL:wu1y/ftZw#9*3nҍ=~nl& 6 ?MFnZ ;Iߋ$\5dRȯߛ]*ce07d>6Io[ u4&ЌA34|1:X"5]'74<\^Zk ]FS)P6߹(f<'VK,Xk|R#3Xtv\aE!L4*쩤;[GnϷ`->$W9'X(pjyr:*KLx+ TL&y D"G弣rum9P (Û@Z"Pia)iYr1G5O\ *O߲Z N 0` ө!g05#,vH, EZ1Dg%}c>3^Hʮφ?܄`Yѧ6]i~lʬR  G9skS*Ƿ[ WM .q.ͧԆd_be<sEǤ9*]?J8nyd81oB .+'5hQlөQhHc0k7|FO[?1pȢ$p&jix|,efD ]/$zz{I{"cOZW C@kv8<\UZx|7K^53"b}e͠BR@ ĕ!n>!MO_ ؅5k 2dG|{\ڃǂ :UoژAͰ1-!ڂDW޴oĨ!6ĉA g&8k~RHPY.:2tOvQyH肄UY٤T}('rdN~Zh#Np iE*D*ŷZʯX\_ HOJnt{\ FbLåvT3LS>"^~a`;k|%+la r*6ަ9:wJALo{d!կ ?Y2idmY{i2qA*!)WҠ`"pZ*]o<O35E_>ƄG^>4{CHjע iP>F_D ?y@4",Us$WJ#Rxp\v+ ǵ("l7U=`yZ)c+5o&2) @1% *a ucmrz1Hzw赁'!~ʒY֙MKW*B+١׈fbzZ PrAr!rk ؈_B}d"o I 6ޙtc93cT>ʚѡz[ܳ7g;z?#`'_uuZ2`Z|XH q/*L}~N6>h. 4!CD%q`*s<뎙YFgXТB5&I] .=/XMvI>ἴw4mw-9˹UfiHh-rC["#OnectʏOʼnT:C\֮82$#zi'j(3B^ahvq|kgfo o:)v`޶]n)?$~Q*N-e>ZbNQz(zrdcFCK6&xװ$Yw@BI0UKH[aoEa 𤋮r@ڙ%Vro!5We5$֟vڬa ه+Gw7qM胢"ŰTnX ;MOu, WF#XiST!tЁFRLˈpm+a[ЊaKÔ/,>Ϻ٣'-8 έ_*`@$YDL⎏rHuqaj1WJF/PtQxQjUS&/LeC_2Ю.iЊ ҡSw-W,W- Rwhr:̠%и E|y>/w\N˛TAsuzWAdeM~n}VL/ٿZ1Fnz !RIzJv7T9Q qFC=I{; ޯ=$+j;t8O.TmL%.j$Z퀵4njc%VMv(3x(GFJf_gޓ^S4[*wZuCHR5ʐ9}zƭn^S)bczG%yC J'&Nؽ!ɼæ(uIC1L< Zb꘮{l$6i>\_se\\\^NGJH [>2JK|2AN3&vAȏ8SUGtaL9'܅ez */^vQ!B]2W!SEj ]H3A$=eky'HQ7hCQט3.F' Qat/f?=b/Վ=uM>n̈́Zq#3m a9IUZɈ_~=΀˄'}^!>U+3&0-SKsj6SlpN$< a3a4V_g Mq<]ibtbF\,M0)_`[Z,^V4Y<.`4[u]iezޢʚV=L X!W]~م@{;3{o^tۻuTᔕ m*sG/gacB2P X~.KvCx$]VKd+ NpY4k;'o7%aKgXU~etO5uI12ԞӲHaJʤJ;LwQ QXK;D5lVd_ipk N8bquYeYh22XBWӬFBiV|{)U]$jq`tI.C7(@.XEP}{ô[/|uw6Y"G8P "a "έwwK"Hk\]6k3+b-x 2Q|i]i CܘO7nTAu^G3`!)2J{C>/TȽO{ً6"S}ZaaMM0_n'+p i.KAiϓ ͏[mה:B\FCN+H:4S΍t.\pfa+ii5FcC?9T/]jY.Bnqvt8Kϔ%oj~ :qO)@ @MJi ;:w$I2o:n _0W" JA~Ӥ.%u*I ZtJrTR&{kD14_%0m!ql%V%uUP}$6AMxWj„ v ` ؘEqn 5i!S2=o7xA'b:T(z=C8Td[# 8to3Ck,r rԬ%x_[K#?wN'v_.&yyD@uj#f+)]iHHWb\]F@Io{w)8[A]zu)t%Zό7z$Xk(ҍ6>1q1JA5K8)rҰƟ904k_>uЃ=ƘLITνWt ʊՠ4H OX `^@ uavdo]db'c'ЇɆR347CQxG'-Ѥ8I떆/b"^sиbt M 60eDw:Q1JpMWAC,l̃9C,n=a.rںD΀&<=EZp*qzv*ϋszvIz#AC-ypݹ^<,~}Yw;0Gf dIo-+57(LӲx.AG꿙39B܍3fXu`Qm4O<#[v1B`S9 =s$^.myAN[S6ELpU4%Ѽ;dSq-A<4C;lאI}ekB !(lIB=؞1.}[I여XZm^g| Io7þ GIt*ܴ[(W;=gLg2-9ɪ >`wdu/v ݜ0ۃ\nwxIw@qr*sͨM&]?//Ęc^&8Ʃ'+f`=uT e.8_'URtUs@JGHWs,Z㩋l0وQXe^on+F(RrK*ߩ5[|YhB|lX|=^7 -Lt4q5pҥyDv5I\Cs-}3 SEHXS;% P>;F)D$*H5?LY$6֊RrpڰU9ξ9Ғ?v$ب[ xH^c"yYPEGx4R|Z!jcwyJT-ogLV/If9z?qp_{b4d7/#VF TٗLU_EZDOyId8gtMz>]A:% (}8w[dbLҶUeV3/FЅ4_~n}6D V6,go|6$N@#ysGr_"3z{mP 83$Zu`wp9+ijG\Iǫk0ޣ/<1 R*=WI^zB=Re)5o|gT}~WqLkP<8Bz Req3*(џR=B7O I]J *7 >X@SӋ3!d5Cy"(ٲX,m.u\GF@:Q1I罌 ygIf!1ؒ*SSctטt4TN!ڱ_^rAR0XثثTOO-^P4m12,ȃЧtBgfB72yFq:z M${W]]G3|N$ Φ 0oRXAWx3Luǭ+VfUbܚ׏œ=Qb.Yg=V:ʿrL [W_$<~dTe.%& Mȫ*=$Oa5~({ǝ'-tփ@.{sۑqcmzg좂֕Ijh,̄Qp2຤tNQv:kA'g_{_699l4z8SjOLkjX- 7Q_đ݌75/w9ل˪O3^E4`v"RzJDŽ. KX. %cʤ9EB)͑wn?Ҋb++7\3 h2,v,D9#d*>ZMAM6lqsίK1dFHƐȽ,!kZh eu[#%&QRBcΛe0u 4 iVߦ$} WmO%F;N:}Yp4%o!}L6bǻ^}d7sq^n;PeHJG] 44#"mW gF.1P@?ag(E@:M̉-( vD+X]`"?!qLMJh >'I~DukXW-x{/Ko7!5u\ YDBJZ]eaeZ2o V USMx@zDZK )^DS!P6-6"dS)AR\#A۽ޘ[dz]. 6(1>I4[%rHڎ6G>?-2ifB[>Nn&'TFov37<}م v'Z2rN+NeE\(?g2bʓ}MwLTș1<  4wBbsx_:Q h{~Ķ4Ft hMzil?.VO)0喽lI_",{?F?JF7 v$D6rlq'6F1Q wsI2&lbBSߩhz`!& Vk{'^q!G<|;1CkKvl7QԨ}h?}ZF|\<T-9#NdB;AD _W+ -BBLp|PWcO ~QXJU/*7MIkW6ݎ#g}dľ@Krm~@_󇫎]| 0Fo.'ELݟd^kbEae3y> endobj 387 0 obj << /Length1 2147 /Length2 16621 /Length3 0 /Length 17929 /Filter /FlateDecode >> stream xڴeT\۶5w  =Cp(5w.s}CϹh*XE,ih9RV 4&FZzzfXRRA[= oj `g%-N @ hb dPdit>@K#K GL44*HY9ٙt- Ri+ 457X%ay%Gakk+"$J VJ Ҋ?}>K ++ 3hkgq#`G_ ttNNNFvVFS468Yٚ>@s_q41_, @Dhi$b/(?>Kcj+`Gc]r%ee%&@K]K@{]{;_zHemM]4!9[ٺ]YZ9Y_8X)Y8Ņ'hm@g}c?R3<ܬv@CNuzDC▆V??kR~R+KsNC%`n.kwFW Y i+[ ]؉8 dM5Ջa`gbeG϶2dg/߇4,vvf\q5C@" h0aK}+K## +@VC,,7mR #``0:?7VNo)󿈍 @7b)/bFU|?fo 3 :@@gY>16f?[ >3f5 LuQ#?W!W.Toc/`okeT11x$#DJǩax7h@l+g790ȗX<#W_ONox@}+}NĆbOܩHRv2LUh唩6\l" oOw*Yge*?Fs\䭁߮'.0h2oԒwIDfZ!LjKt {[c;uFIZ6SC#92N;T;#zTn2լNN uw'hx>K^*!:\ f8foQD۵nIsdUPJ>YVNJ}ӯ_Q[O<&ZLA;Bh/AUF UX~Sq~'{9}d}FZ~ ©,b;V޴]UEXw urI:je\<Կ ;Np@ Q(igqF;;lxWY!A2)7=ߩ[VYx;*"@/$;؟@[@T$ yq[lLBq)/MqnZՃ_ۼ?<V3w*\'S^"slya}xxx#Vy&o_߁Sic#Mþ_Ez}N~w l2AMl2t~/ Ed+ :$<'Hɍ,ƾ1]x(F IsXcA*Hy:?E>*O;TO7S5'!Bn:罨^t txa El[˃EI Rc"+<4PǕZ6kG> }ng/wD&oMXh7ּ)h0^06kUv)k!XgkgӨTg+RAۍ/9v&Cgm #xTt ;~2IDMX[F㧏\W:&6 =;2-1`+9_a'^|HOFo'* #g]]rw`]/g:DŽ N#st-&k5⃘b\xpFA}Ezpd$IPzQz=~Y -[Q[hBbUJ\jշMBػI{ ]|2Cz^h;*m8"%Yuތg<=jGuۯ+0RܶW/}BiknX \.Eg&`<9g.-Q $Sߔ' UoIAx.ȗvƚ^f ҌX^CnmFܚ-֢h{?K0> u8eY3@۞ĺGSloX0}-ĝ_zYC"x= iDO>mqz]<τnH&4bMyGjJJ-AS@ #M+ofDsWi%yLڕN%f/ղ~qU D@b1' 2 D!$f5I, HI/+fVQ-ϗF:&9搮 ^$yP\HAtS[v4 E6T)Kk1Ho-5Ll*ݝApw0ժjd&=DIK c`5Sr0g"4?-{+3hx*(i- |!)jkggy|jskglZhJ/C4La"6 NK!>JCΘ/-=5(pf\Ȓ>ܒv3*ywγH:t+hg%C…+ $P ȧ XOp{QkUf$d:zٯo?"B>oMK {sRAkX]jz(OV3O!$aSthPQ(@1J 69Yc0Oz헣B"m6cId=HG%^ѶO"/opo^Sf(J/4"&׻ǜKf#ïMrU0`1_ֱQ4qR7vi4Enh,Z~ 7s4% iw'R]O5nh"Ҡ#gC՝]"jA ę#됓 [Z b!΃D}Ge[Z e[$MI:!inϯ֒"vI$L"F!(~PISِ7$bʋBQj"ƫCP0At[g#plW&i.j5 sh Yl,ȜCTc:~Kz2A'W'j¨!Z'U1Dg ,i`Ɇ#JbdEE$nK0TVm+&讽$í(T\^hѕYŶ@؅9}ՙ1 H.=mRkZ-w#+nsSЪf:<ɽvL!|⭆RԛacCЯ5J$0Uw* ݮ@kg9҂z)3r nx^VpAơ½/㧰My[[v%ܹ)ߏ%R947J3\tmۣ(箕>{qpĤɹjp 5KSDEŤL]=s Xtw`/(& Ծ1ڕVo +)+a0Xb ن81WD!XY.?y})d#ީ"%ѱ~*ŸF-si Z]s=^=F,笚i֓tJc.pcm;gz\el 9}.TNfn|jш!Kwܳ@!Z?AP!k- M}&s:P !o4MyڡKP=VXHޗ"ykɞ" `Houoy /*Q##K!{j4((ɨAtpNnVԉffi8.T(!㦎wj =Zmm#!KMȑH5= Y=b[)Tl{𞺈фB/CRxqB Z*N Ɩh }7iCp84[)h=\W"B")|3f#o멘5SE ~_W@e Z~&&1)MJk5TN:aB(^Q0-츎,'qpU7Μm3#8iAELhNs(y41O4R25gkt7Kt߈> v^%[G!2}qpe.K+(Fy݃p5+d+Ә.GE51ZD+=KΈTL1bR壨 -I"wŶL~mh*Lצq׎BcDKts'Թ ~`÷VBly +D Y7ﳔ^lAю76OUo3cF_@C*2|AXJ<? 1 aI'HRz"r~m<ªNxg|9y&&bJt O e,-߃"`uSa[hex_DpfB0_˿=-V2.i]W'0/ELhۗȭs-LfI6)hF`ZߏeMe8Fk_xJf5&5K.69[8434:Lps}z"-J Dvg3Q+ 始XE>n=m_&L`֌ V`Dzf)ަ&Al:b`Yv< S:c,)lfduPacQ?IWh%2cV-ٮ|h\AEW\$31+vwdٕܴ醱b ȉ+Vc } ȂY.?^VK)fBVYjulLrt8j}J7:ڥz5֓k>q CXɨ}M.%0 3_Ӭ,;5xc;Y9f褥2DؤX.,Lߢ{!UFD]=6W\=y PNS :=' Xex#nXCH$ h,B" 6fѱLώ*kÃF&-!QzYDV'-By)%x\,bQzq c͏!@B7:]dv1]- Bb$!{*]4 Y3KWӽwuh=z'TzMS- x~6/^t.q >^5%dr.#<_~bDiJ4}vom y# ? _RKfܮN7*^XLׂk-2[>6ٽ؆n<Jv'ґS@(y& 9 `6 fx0d5li>'Qi_vg8/c %e3BۍU&ƢbMc~L}>mBXCvs個_ \wꆜln.^(bt c[+/ȶy%{Z.ke}c|[' Vg`"!@ۺGUFQNMv,-X+Z}k3EӗN^۹-L&a%=, ӧZ=ꬫk)J#MDqm}/o|$|؏uM %M .'tH)MEb*ͅñ~E41NPcln*}xLw͍s,k7x, 5DpJ7kdJ/BsЀw!sLT+r%C!o[QU$|Ė-NJ0yq9o'Rqy%>^bdNʝa`cDKŎ"3l6+v u#츪64S%W%c %q)'2_I pY! ..ӔM>;-bji _h?Q' Eem%((6\htS DƸg@uAa8ʬaL|yxV8q(JnF}.z,~# pS4iS)Ϭz.SXY;B66g󅬴Ѻ ˔yײ+l3GH[%I_o72ٳh+0*6m'W{̍Z]G񘸔b̈́J\OY~yL7% ci\~m23Eg=OfwTk6E`~"!oyكA2TOcu:=4.)I(nΜ8tg Iݺg4c!1ruGRԂ[^ۊXIqoݦE뽐W)"cFTkGkqZݱFqxy93wsIV//{$'X ٚqm~GdN]Pr O#"ln\V_Rmvݽ<{##d/O^JͶt]?>ջ: p҉ ;ԑ: Fqj̕J!ͺx -"Q ,2iQ3U6?HLw4|+uk~@z.9tNޘz7a(haMqXبh#Ư4i?'nI^PhJDj g/(8H*eV=uA&b䛹}A=ˤ%_I ~UeHC u0dqbiVG `ס1]rhY*۠\JБwq_8r,B"Z\߰j}f atC0[GS "KY75r#ﵮ:9E6>H~Elh4hVULWE~N0!0v*xpBzAW DϢDţSlKN1,ߏL꒢:"7 -E+Pg;` OFUU|rl<h ocoS1?-_Q6fDb`6OZNU⦙ߛ犋]NsUd2(ns3ꀺ?Jً!hOct|i-~>.At$@ 2t Kp7 AlKsXLvȥި~;^OH&JhE xJUu)N OGIOoӯx%9?I]rfm[JP>\y'+c1պ.Z77 yw~ͧ(lE}zzZ+@RB\,n*QBkU+N_I4n]8m]W h≶1w/r c.׊\XvNd+:>;6"6.0h4%vcAĸW#Wj V?wx۽O=C!F|ew~eB+gQėcÉډ%^3~8Zr^<~%+v1HS]R{fg6%}[kij[Gn6媪AhSW)\D5V)=0܈%/W ԣa̖ᙃ&+`V5_'5u:i!;O£g(< n%cI+~V,DrR}LstiO_r 5 s^F`H7U~.v`o7Ԇϻ( G  GyZDZM`;^E/[ߗr۾ uyRf NӉq4*:WCbgG37 kƜ ʼnU{Xjs.?3 _mX2 #)+gж٬7|~v d,(Je75fYAIi)|Ƒ2sP/u ͷp)1両1TTGPbQ_6βT&? _O)r 񛤩C{ _RRfփ!Ǝ{*DlcOVp2䨋| :^;= H:AdiNꁐ!Dc\Zܚf`/#PVCfc;"TWtKn]]89y` d[`a:IVOu47 ub՞_Banbup+#(+PmtYwtnңȒssyoIO[وz-)+K`Bi&# r _Ӻ]Q`~E$6>bW _[ X% {*s`we`;+H7)vm@=Ɯm+7!o HaQV|}@Ik_FJ%MiR/!<1&7& Ȯ xcVRo]vj@oȠX3t{a@Ak?``͚wŨzԼlY- %ڌal\FG0v8?I0Zt_V ]c33ͭ2ctWXOl*'o 0]F7lɰƌ/kAo|g' vȠY el6;hRF-FMD2F$i07]Wy= l;#&Y^ET.GE{b(4k[SwB}1=䛢Xdr]u(ReAHt RXǿy_;p:Dr2xBӿ+_mQC0t8W~x;v0)(RS6E'SxTa3=ފR\/ yv6N였%sn)vڴ~gUJV;;{2$֥hBP}թ]V8@:v$$y'yI-t~MD;dҲ hCh/}wcf;u׷apOx0 &XFd+Q x HP|&wcs\:^ԲUm1lJ?jOcٛKy 1I4\T8?s]4$V{ K-LCKݯ㲓 nq-6F 4ad:2ܶՖy0ڠ?)Jh<BFȾUV vkU}l6B\ @2ɦ ¨姆~Q0$-vCՂu^%/jX-åókoήEAʉ3U{S__G%.Mw>bs`;1ZfSb>i2F:DVV-sD1ǣ!o^?fYq⇶=1+6ޝ:S[hƟŠ}j.NeU]S Vqf#i9aTF3fy^9eCQјPzhzTԎ ]ZY+AۮNi)mݍei'nOp曟ʠ͙&&@Ao)>]c0E?/_P?aq+8V]~ek|&Ku5\G j" PHʣ]APvr2OT]u:/9E@rM@sAv L@DTEC Q+h5{SYXIS"tgM8K<va q+z]8: m0~!OOܢV>Sd'rA1~r+bLeE$?i/5< ecܟϔsc.?x6] \}loTC$U8f-% /&Q?,9KfzTZB٫so0k]&J0t]u7fa"K꾄N\ sFfYjD$)s偛xs_:%*""vŐ 'hlUأ8bijb6e2vΥQ^ZDu}#o1yqqT>LB8.nCƔ!\4S m14SP+P$?ΘWh;j藖rɖq,zY!nˢoe7Fg #x!dr^w'gџ"kq-VAϸb#o$Oh4Z+a<86ş8Q@ 1vA4xTZ dT_RgJ5"؜i&} Xez\Ud=ɧFSjJ"%?% $Ro}V~[J#k0&K[ 4f~햰]KI-/`DWQ:[ClQ!c+x݃lIb1H~OPL#n#:5d#$WlNBj]t>VΖa,TrTN3_:khn#lXEŕnȒbh#R3G>+sv~50hؔYZZ| %Fs6 y {_)!..['>ZjC9MAO ߮"]-8 z8 \ I#di"V^Rګ@%6*xRAg%yAOg6^~p,M":ՎG0r^NrߖO<ಷEiB @!02)ad2e3Ҏ}E*x* Ѿ\ ׷|&M endstream endobj 388 0 obj << /Type /FontDescriptor /FontName /WISOSX+LMRoman10-Italic /Flags 4 /FontBBox [-458 -290 1386 1125] /Ascent 689 /CapHeight 689 /Descent -194 /ItalicAngle -14 /StemV 56 /XHeight 431 /CharSet (/E/L/M/O/S/T/a/b/c/colon/comma/d/e/f/g/h/hyphen/i/k/l/m/n/o/p/period/r/s/slash/t/u/v/w/x/y) /FontFile 387 0 R >> endobj 389 0 obj << /Length1 1884 /Length2 24646 /Length3 0 /Length 25795 /Filter /FlateDecode >> stream xڴeT[ n, wCp'W?7_+Ϡ* +;ڃ&ooC'DUM,oo (Y-, v``{VVV Ǜ\XXX9-[{SĿ+ W|I_KKa(yOq>iHBNfo}Ө2މ~B @lk;޲ބńƲUd}K;hr5gZc[Ic"<]tJ[̳4a[c.t.Ow@:cEG ~<(3xF} 3n(3n˕@w}J{)b>&L,g̀!XVOUpz(g\̟#y#8F@b> x&l z$6EO&UF5Y-M&YYEw;m|Ca!Fbyick6+/_[&.s{`4 v lC)V( Pޕlt a*=)1&A!9K D=%{aH%'qlY~4"d<{qiȴ WԕyU0m TK e@?jBDs֓*"SUnHv޽P^MQ7;2_o|e(`B{xL݇*^Z? Yelu'aOzs2 'r ҥhdaoJH g~HAPo0hA PN_j M@od|4pDդ22yqP˲Dkn"~i< _5vL;?jaUmdZ9PuZS0@PaǖFP%!WLZu Gazc̚7;4_ +;`zQWŏI!8/+Xb#L}ISy,IH_$6] AwmPf#</_%k *:K̊'.1gM[Ѥ= E$,b_PzXI0OL7*eiff?yIlNؾ?=D谴@1oPy) ՚}Zb[B;OH-?J|љql c|FKiy4HuK(ПW۝[#_"K8O6&-RoğWZ[Ǵdt:`~qpFK 0)SlL4ְ!H ꓙAz>Ja0M)_>X6]Ȩ@ 65AXٸ} UѢ^GSqݣ٧|'xtbá _'s4cf& qSQWE 0m.=54QN(~>d=f1tDl\]ܧ6l#?9돽O3ukH[mȅKt_fP!unԱ*Ф>hxrq?7ʥD-Uw{Twņv/ΤXT4qI7iK. <0m+pp#lDgzxTq]TlN"n/iz=aAu8?E|=w.X6c4 ¥%~#J{nȪ~\ 6G}랡fwva`M*S߹A$] Ao 1UEWp$X"3SYmޏzhsk|9^TqkSbŇm:rm-ۻ}2MÉ(uӒ2rAx3x'8B 8(Fqs~*TeyZ4?lgyZ:%D,cRي Zh`//ŧC{:kZ0kmbEEUB)GDQٿtw v4p%ӌJNٻ HzwX5ԱOܰaH f3j,uۚe>>ϒNo#A&̯9:?3ܷT~g:cP; k >zQTu|9ܟ@Wr%h ˚chNǦ o& Gt )pHy{ۍW_85H>4mkZcBElӗ9sqEG*S ʄdnBέC2ԉ++CYx2r:r򸥺c51C/rO]}XC>I7|&?]iK8pEURQQle=\=mtleںu@.dEį|(mc}XrQYd,DVE^If*{tpm'JegGs"&YޘrFu1>ef~r o/)d/<`IjLjB[mI $W\{+‘S#ؤ"UOm`W@h:-;2s+V7e-[<- -}40>2.Z,,x)&p_tܵaaxRkĝs2Xow8/E㆙ ,wY|/ޯ<* `iB:)[uˊ :{IҊ:5]dw 5NoA2잆 5ZM}W_ cMN{eEvw.{– Ih|<E4`ZJ%Ql*dVvm?05o:?mB:khAҕu Z6 R>5[/fߚS0稥 ZE77vx&2gQ>*+j8խ9`'qrXx;j{JRB d$Tw^zGr^=rr{ƅɳ 9s#F JsG?C5(4+{P>u@TNͱ2m@@"*j*B** WnaZ ;av#FH^ x sOcmJI |{ag&^:? %I㻪RӖ`L-ACUUɡ3#L՟.2)"wf`blJ"KV {_&K-t%]AAxϧqr84anX'tf?UlJ&vj&JZbAho{_aRJs!Qh&6ߕ|:׸B[Txj'tÝ?D 䓳Wbs sդYb#wIe1hsX!rLQni?03GV8:e|^q 70W[a)^[5-RLfwm|q;42(>x^V[<:rDk9Cg(}GShnE;4T:; EEDER 5_(%K>Lw ۰P38"ٮx> N,NB>MS?E|m!G ę_3G=!B"lL.إ=ɧܧa\Wd.P6#䑻TR%>"_J.뛏4=Y? G$.vX4U}?\/UC?m<Qm)TlLC>ǤM{(i&Lh:>'[B}s'`cr`ۃrb8 7H)c)ƣZnN 1{V WZOKc5a`ƏU6crBS\6KN<1?)%xUe2"hvpw ZtN}|p#9NfwTh {pJb. j+ W+9˂8S\,`绚[5j".eyFNhk`}5uwYt@);ù,R`6aCӡ d܆ԃ˻R8q:B9[͖␞]lI!z(#m[%"†\~O[z(O'1D)"pX^b+;JeO7f HȾ:hLD''$jM>P̕OU|]6hu>"yWa%窗 CRk$_YOl;+>ng}8-:-rD FFѻ!zPVsr;[_-;(yq` oeBMip7L>Ns1G⬀xq3A$?oMBC ;J{iO"Zm8psWۚ6' fvT(![NVm]qz Fw /׿ ;6?pncWr5yy2e<ͨIGcCT9& Ck 74;iL {Zќ@n g,U/ K@UӣYC#ѨʡԝA^.Ln$snGJ7VB;1rpu.eS S\;fsn2g,^?fNAYsUV"2 T-njao⠗ ΡɆ>3X*vQ9V\\FB֫/h'xMZ}\Vl*2p6A(xKɈccNM* ٭QTow!P(dlMaBLZ:P@m !4mg/V;f0;$peKLT΀dL`-'i]~%Z1\c=YjZMg-ᝫ`rd3A FS-ZB=Ċ(J1 `MǨeǻ]cR&3VH_aξhȈWR,aGQÏ~;̸e*} IuΈrкuS)brp34?ԔU|9Z)j"Ii89HHr؆/AF=Ȑn%hRsmRFD7*0Ȁ4Jl9􄉟GWc,tE"nK*34 ӄ=}L*e 060.h]%DH. JxBBH>z776=p ? >+ G5ۜw}.80pSƯayau°t|@?u.16)Ba$8\~1L~pBB.sL3>j_~Z2wиCȭy<^ffujgH.[)2ucXc~kX|HW&on9ڨY|U :BF3D(e-ZCu}c3 ԝ#M~AQ%s5F|-9>pHփ@oۙ! Zj¸/ 0^h aR`U ~~q2: X` 5O>P&X[JحEMF1z5!"C=F]hK<b*Ф|H偪z9jX2?F۰m3. s*8ؔPrŲ4H\N'OF/͊$<ʸ b5gK{9Lμ~cAJ,j6CzZ_c.L)aM| _ CV6d -l B`Ļz,v Dm?Zl?xcY = knYNflQoJ|U#glK,4 ,A_[ FxТpr!qKʚ‰g^4˩T#b~&;SDGߡQLx+3&Ů?͸Vby؞BTl\+ɾ0y"H`Sv8_ `P_8VM!1 WJɃnA=V1S'aaPKb:OzEBof{I:C;0>Ci^^b@DAn z eoYt0k@=7@o@F)cKj{@M=Gڏ DI|>4=uɴeW>74}%شE|s )2mJ/Qd`LBe=ZN h*d Pt!27\0WX<)~QqA1A/a3/NGdEMn#_GKMCV)y=_*#'} Pj̢?Fk- vľV{#UL*ҿB7lwWI( 4O惮fAYgG#5 ,8E!ʟߥ뢍: ;E] 0bz7hA+n Q[kC:8vM*ݕv'u{]Acȝ|S" }Hֺ$h[φɶ*Mѻ .f`6rfQE}:2g@&k!>ޫ)󋊎d2Ցˇ&L%^%^˭ԁ[K" /n(T\]1|&o& yuoP# rЉ7ײ3Yǂ/Ĝ{EfK2!N AU 4Y*lT*<`JRh}(8mnYQU<1)7Z>ͺĂ#bͼxU_AIHptaT,y W퇟>%G7 +j_\W{iטq9@kr'7uVM1RvԼ=/bF*L!*L6%U==4s^tjsm =.q[8U0]1(/q=z)Hx h5 A 򰆰Ψ,on<[/{Ř/ Q`8+_Iڝ+^[$e״AidP,U`qBAF]MRrCk2O[o=M5wder[B[HG|Xzi.ܚ @"X $zt=LLaH2n?҃(+ChiS$A(L͸dK8Vّ^-%fo5qmR6X*T>xC ʹ<>*`o?G`iR$燬8dE'[>pN$YmtZh/.˸!+Q>tbZՠYxc[(;uݵx|ַ] {%sbwuX㜽lU4pBQ, ÐT|Q(9Ov^9Ƣ4Ow&1i]ʡ:u9eRҖ}nʪMTJ㹤IW@[sFor}ydI5ow2&vg}Ƣ>Tjf HcB]aaxȤaҵ#moJx=ETP8(*#Bnfm I: OSƎZ4*M@a6h1iψ'lOˬ Oy༟d$HJLԜ2Fl$|4T7֨z Ǐ ̹<乻Xr4Y{- >u۠kJz`4]vLb\閟 :S*4@}6 ğꖰj8iG;@tzE9:O^~nR'o@ZMHǃQ(e(tX}IO|]H bQ l6 s+}>@!W@G8jm`ȓnKS;T )РƅoQJ ɺI*HqCwT R$^2v6vq:fi_Yq>o[.P?go Kna/pG=<8]&?` q"šׯU|i)CT-.&M^>ׄNW; X S=4H`F[=׸fn1?wΈ!C1/|*Cc56wnCVte=ERZu;}Ә]05~ylM? L3L2F̬D;) HyaU&0LIedsM,Tl h##D6|W)Tb9Ӆ`̶K e\˜T4%t,$W[BQ%)W1eߨ"/dDA]Orv>+a/¿Pu[UQ o}B?8UJNHd^|RD-PK>{vȩ d4G! `@!ک<ςƲ^'bSu@{}]*ȍ6@/̛g}.jޢ 00Z"={6inRrEO}Sxu9b_Nkѧ숞ZA^}u@J/!E@f):_,ƾERp*0B0=ח0~*5=rt#X\j<}Sp!Thz |H@(GlDĈެ4WxngF"Uuw_qoÀL8&Jvf|/w;a~^/4!=Ir)/^+:ͩq|{P ,xi6K:;NGfBSaTeø1>,)jiϵEĆ1+DD؞T'0@j)x_D$F˸&;a/I-g+8Ի14YnFK->R@yKPue;p =.Z.V7VG+m^C ]LDe 2ip~K#3 ~QV[a1 a à̬UTzuE/=WZ3?m蔵vÛ/mb保3s` U]/"}@(ʂC,yw]&h)|TMWd!Znֺ(JSh녳@| Xl}!qأuƇ@/7>rOUW "t#<O'g Ms Uc6>凊q 7D+7`6{5x*Y',jLUw ȘS#39gY ڋ!z W;QOF9G1]oIL@?rP U}%ٟvf\x2cU6-G='a'g`^\}8W~u5̵I0% 'ȹBNpA&e(y?>a׷Cw$KЃJx{TAmD=nJ%ޏ4J,mN0aCm;DX>L{O>/Mg浯颵Z:[gm㫩]nA0 ] P lO9JWspmO* yLI{9a :T݌S tAdD} WUƖ r׶B3RxSbQ%*ROCjj QU^ey?hXwLS&`9v@!nfcd*',:S{~75DˆJJd3׹c+ap?jئ4I ێ 8[xb?Ln؜!b O<%TOr! "=K$~H6 f^0w.>]1צq57(կjyJ|H4/"))ozg5m+s^U3+[+}vֻB6"<3287Sxt7+Z鯱\yP&f_LY_mFQYYmm95W?E|hy!GYTy5qtf5Ib̗)[7 mʄ֚+l Q: hg0o(*6T+þ闼eqTǗ2w3Nh)?Q r 4V9JRq4{UyhC\wʷxA۔3 }wqV}~d]|&:3gPHQC,C.H ruEgX>pqko.r;T"P`1Q.hOlj݉NÒӷ| /[mU?iP>+ef.as|Z"H;M[~}Orlg?}yJe qƿJP*uBؿ#v%KID[J#Y4a2Bكl-"FOAq18EjO{}D*ua-hd5"Ip(WMe3uoBiH&oo6j'Ӷ9-W(X4fǶm۶m۶mwlc։O3VM_lEwKȵmg8\fL1 *ܻe(8yh%ՌMD]MNn/NYe=dUx!/pŇ>={0: Zmtfb_>yҿ vcqJo7e`BGv7枩]QISW㗂J0iwBP{isjדEQRDa@[4ZG~ddO~aDx9F _lu+uo {Nn1}t],"t8q 9A^ xe]Ѣ~AfЖSQdU;~_\.Q[yһT-;"[E;lHpEM,NύRN:`;ח@͔{hnWhᒘ; >fb%V6 _h9)t8FB?"|3|U6eSGh ,_S2=քr Qܹ+K2HEqn9u|RQݞحioUl*R0ܮ%ռm==i::oc/JDM1ǧ?͗WK ,F+PXKn&>tckUON]JJ ^G0ɬgm>PYhtDM)T-N|o>Uh/sR ]0(bA0Q;.%+nƝ@VܺN$'΀Z NhhKˊy"p1'_T]&.zWt{tE7#\Fl5nH WRq mX1[bYi-pI~)y99@RSs RjsИJ93-+>EnN}Qo.~1gv3ᆓ(0OU %Bµ nd\P<0OɩK A׾Ŧ7ïgQj9 \s:俇ztajĻ#frI[|1Yv 9|&Sg;JqwxƄ(Hn80"?Gra +.NZ㨛(35<땬|=w?mOXf'fXլ$9Qh^>b|Ӈ:rM;{O^A =>=z=lOd1.}ZfdOHb|cjo~AD{{b^5EI\Gk$V } 4SìBhTL#T<`K,{KaꕃVNYU|xf1n{: fJmeJEZ.#:Yg ]k!ݤթ?u^ѤQ 5k>Kҵ쯴.ksZ2:;_O"T;%C#c :Uir\5 ZsTB+%M>셸ח3fC{}_;w-)b9#0zp4sJn?䕊u Ჟ'q˰Od,AlbA"<Ф))O7HM8®YWV~9;:&α $oB`R<ۯ)c'"޲&0l,Q;lG`JL QSPzCZcz wHxx}ߜwv#h!vEuDꬶ5('w,)$ZRp0)bP΅)yfr!`3pƀ+ng!`<Git@j5Yg^B LEf B~h$?'m;W @T85XO浅wcm-Q2:[,4 OS1L;hD'P'nVo̔Mɪ`/~^}Oy3!qW )q-W{hr X\D<a."I/MG 03y>l7n?}k*F h/<2 b:RE F992#sPӬvb~0k>1# _^*[Jc#E{ĀUek\úya2m/x>.ѧ0 .cg!gSȑgCTfA VP;R n?Chj娿m9>W1ۭ[}w&]kQ@\ȴbD3yNa_Tp  y4Ҍaܗb\>kӟ!DDqyّy>˸T3FHazTQ: Haѡ;}NjK. <eA.Ї.D+wٵ" ؈`R%o~o}Xp"U^cj J` cXD w# G!7)'])MKFԾ!T^=k-i:пW; ;iUma{=I!K#x2JwrJ)6|T^O;^LaLJJ|hIĉ$z{Rvo Iob3˷Id"66W&=ؚdۓu+>6.8&LrABdTl=/%ZK(}$-fB1YT֩'VCK8%k;]^XOډ [π]=.SdG6Llק- ݉<KI=]U t SWm2T7}^4-Ȣm2+W'Lj !`cE_khvTTS#|Sl\Q {3˷9iCё =Hq%_ Y Qc!ڒnW$Ix n2ꬍ9<(a; #!yyk(b{{pt8ǻq@ot&^TA "rq2u gKJ:u"C/(K cKv0xXKy9a$96DzHa"⧧(,{MY"89*W41L87c7> ٫:*#:GHfw8Ά#jKw$xxkW8p0NMX=CNWǢ8OߑK ]_+W &}fL-BhEBEK$\4GKqRuRvg~I>\I CV^׳qND-ḙ_ Az"ݼ@ش{F #bF@eߌ H/a]bfȦLx6uҾeǪ({?dl9BȬ gڍ v]l еjǫ,7[M}81D\N+e+KRg N4s䯱M"1mKC̾]ӭ-.+Cs8/?0!D/#,ģ8VK`jY{&Qd#]#cnt/*Zv!hc6Fg>?C Xr#EL2#, Fr0jTIneEܦ5Y8)YHrrɆny,lR]i})2R8/ Y,/ߑClEaWGQs<[UԽ"-:E!~6Aj NQt$7@9ڕ%:ZROuNa`my͍<$ޅ,x-ZЮa]ǩ# &Z(;ďڿ­a*KpWguԠė]g :ACK:(^?O][W=N./4v[ Ptv8_+ص#,7^l7HoۍpKZ6?V`yhWeZ7?Y[>4ۈ<}7Te']zpD4ħK9'l|\ݙ]N s(MC _d@ӶsϧjG)dj,)V2R-a-DeG_v6o!3K1=->LyTzF/_UڇhITu5JCK,0RaF?f4R̎C<*qe CyL6#9ʝ2? $%YVW7 ]FĹ[4|=vNpNz#PH8}PìCcη gﭸ>vA2+k,-j"aM6@z9%5ɨ|oTX4{$E6#pIyO_9Q?;՜5ldD{V=~WbQ y~H&:]3?M8In+ڥlEYCô{% ν7 UYOvS`{g 6Vun7L8X[_Ţ|v_U!XĘk(A4&`.VEfa(7eb5jXn!;OD1?xrb=5NE:Z[P O<,nXM ۾Us `aX$#eLޖ_82Q/}n<2R5) L{5samhz,-,f"jJ=gOÛ Ħ׹,Z1im~V(K/iRE/?w fawްZ0-dQnAWi1"oMڿ>G k7׻B./tp5-- );m %L3_oI?59 4 lExNBl$H|yrVɝed+F!=\>s)*ȯlHJT!>DɃ +Z$o4i++& Z.OſLrU݄F/04-} RM*N t!/7<Ҩxnrx8!%vrZy{3"}td`0[1ӆICzfsLKJ I8͒|[NakwH-RITǚ9ܤBDWH/8eaJ m IJ& i6m !f0SQՌ[) a+i2jR[W"YK06mP'z,v*'IQ& BW6 0lL@*hK}>` _tAd!Mk CA"C4Bfg=&7.N:* W 3JݠXpmI;/x{*si{0C2b7Аc+.2#ld!A{ݍ=Cٿي{1l24aǕy9¬:W @a fw3Qhg8ꝭ鑱r o_nȉqhZاMeiZ3wAxvA *:8g2]pҝx<6; $ĵUp^}(/GGXLt Z.q˒uL*,Suem[+?-Ll H Q=W(l-n &\*>c$f&D8O%#qDz6G+O䃥L~QAYu\yr Vw"1fN?=(샀E68_f06 .p#ő&|2ˋ3#,zDX^4 >EoTRlZdXSknf޻onCYZ% }:U*VNj n6#3ui=i,o_U\0NwZ{}9P޴T~eM1O%^bӂDyO 1wN~4@բ*oJ \6aiƩ"Rۿ,:ncC@ 3VKQn[[uCu*_ޣBQ8?j|Ƙp/'@βA 0/b'syIRѼ `oubY iBHL֞AtB3\G Yp 9EuYJn+}ƓطanHsׅBβFPgr䚬f)gaa2gpaX endstream endobj 390 0 obj << /Type /FontDescriptor /FontName /HJFHSQ+LMRomanSlant8-Regular /Flags 4 /FontBBox [-483 -292 1526 1125] /Ascent 689 /CapHeight 689 /Descent -194 /ItalicAngle -9 /StemV 92 /XHeight 431 /CharSet (/A/C/E/H/L/M/N/P/R/S/T/U/period/quoteright/two) /FontFile 389 0 R >> endobj 391 0 obj << /Length1 1691 /Length2 1651 /Length3 0 /Length 2693 /Filter /FlateDecode >> stream xڵT{o9]u#Ep.Q,.(*D:"L%Ge;;91L?"c=G%TRP!C<L1H~ iRP\`8IDI!&C1@J-)] KWI8W(@0 f WFDD.a D @`dxTMJxO }„ΟBݿb4~t&>t. .rt{\ յm* |FJ \o)JjLERlr=>a"J5N$M@9$4T"/;!FB]\ߩh;/0JWSH?kЈP@P@ T$HJB"]o}qzgq D=LE}_)*:PLo͛ Bia,>N) <WN0PШI> -K Op5E$R9HTJNj$ĉ4Q=>Lvr9{t'M_Oy|j$IwAс_axTĸdٍ@ 'Z$nn K_5z):(UTCw\WҢϡDU/=_c"dJK/jbhpi;YGkY򀻻[gDXk05Ph%t$43`ǵ!L}gRx픅8{g; q9L~%E欲36ςKqm,:8u7;ӗ6qe'mu{t,!ie~ڢi]ؼyi7Jr)ϝeHYAfWus#f`~F`ϤVl4ڠ-(!&u=ᣵ0Go =[Ue/yfZ|ǸI_-J6X3^`T{]9yYUwڍKmpjvLZe4<}sץFGbv^EM;ay߫-'T2Yw;DͦFSVWf^T,.mj1dª08ѓlV3[>t'%~+wwE] rx[)ޭ:%@2(^2vqQRWvS*َ掟83e+,=/{MC zTGnLY9݈,nhg 2dž(1MI+g4y,JߞkؚU΍8 -}6uJoaMC4##Kb\[~=[&yx;+ fMr 3dE u`gʶ [`UWIcۈ UǷ<|jՙfKpGּW%mӪ7ެ.޲H"m7zFy[{K6p^\n7{l;CY.z}φ_bow3eI5wi,4ekk,7򲰻T=TMj״GC^ֱhc_~{*+1L욇Ϗ/SbF.E)~v .Ynv]w ҥFj\i lo]6]js u};u _< [_!$W].ow9OqxjIfjw;+^E^C٪E'\\/u|vB]S W_miGw2gj;?9nZ-S+殩Tvk<}I[?Vi_jX0~T+t ewönβCgb5(tQOα/9 ׳B/{!SÆb]Nlȹ[ÜĚ&۟uu{jf]h==S<ۤ!gԤz<| o}:w:q$#ڂ7s1# f69Ǭ*dNr̳VV7I,<װ~f1cUЕ{x>uAM@ä-}ڒ*^`Dys+._ᔝoզfN,78TqwZyȢ-U3Ym&K\P6WkJĆh?kܴٝY8eͣ/l8oPסp ߘT<7rTv8:ޝ:<#\a!6֌sBuW`)_ yjҢ=+_s'7}݈MR|=qe #+REe#f7U8;oUT Wg endstream endobj 392 0 obj << /Type /FontDescriptor /FontName /BDTOUX+LMMathSymbols10-Regular /Flags 4 /FontBBox [-29 -960 1116 775] /Ascent 750 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 40 /XHeight 431 /CharSet (/bullet) /FontFile 391 0 R >> endobj 393 0 obj << /Length1 1957 /Length2 23722 /Length3 0 /Length 24955 /Filter /FlateDecode >> stream xڴeXڶ5ݵi$8ݡCEt{|䉌5\3 9( rp UT@ e0;$΍DC# 4[M@A;?+;+'; @t~ D7 \,&.o@Kk Û x'oIV-``PdUaHk=` 2,@=@NSMG]-#Hii1%Te@]fWm)ْ'ϛwm 2l]/moK{sp@o; ZYAΖvӶvmo@;_qu0:Vsek3 ,_o|sz+?1ep#_{k0lvuŽ=tH:;ɡ%Mo钠y8x6m3 _ k;./NEBUAVFKEXT@oq`{OBZY M ҟI[ rdu;xh ks?u7wudqvr*H7g @'̊Oz~+#`ab!y`gW?!q̭omvU`~Si.(49ýHl [;s+=*f&ve_6.@suk_E6y{ K;ۑEJvo=6w , /T4Taq0[;X8yx&&Ho]xkgs_m`cu\`_Q$P!>Hf ޶of La` fa7 `3ցY/~hhg޷L6om߄o-RX{6v-?[l%:?R뒲}3Z`g-9&*&`gkƿ=?=-) f~ [U%f]&73@ iyd&bV'S0UK#zZ'1AL C + l Ȥ-) :фڽl&WMޘkH|˪PEp%} Lf[B@@G'ΉW̫ϔmkyEs8vXKDKS>eY0pL3#kY&Ho]u߯AxQt^wU|~zkZrɏ%x>b'^#cpY ))ٮ˸?+Vdos`a2';[Hu&6/%(Sy?)_D;,6-ġW hJ K5٪gLDlb: AL"^/40%9J u.El`n.WڠB'&]_)0kiQC_a-kPȘy C8D9u60Z)U_+; B%LTL= V ͐^_^'߸k@y3u8 LkiuV~S'Rv8MW6 +٬fsbw|02JTGZmY3P5PK῔N٠xKSN ƒbP/W4Os?_]l7O>G m}2a&,s7; Y*p@k[XJζ4ӆXag5Y{ZfkpgX01LW-(c|pvOUط.6Pʆ@N8b65}*b\^4L82wJyKj~DRzOKÅַ\E1uCFkmf0#CYLOq))tm&PY|F .7Ne^Pk!X:Bv.~TKD;Xܱ@;:MLGc Oo6A]Wפ a.e+4ycUY>zw.&LR1)@>`DJcIb`2.D$pqn2n "~C!D*Y5*ӧ歰|0g~ݵ)&(cCR#'Y>j"θ0fOTAļ+;yh?^ '׮m'O'q\(NEfa/I{-w7'}m%V ݄32!Tnr[~qgwA1P$WN--ٰ{oYd_k@D͔rx-oK~Ea<d@5Q# Xl[G_g}h+ٛS\GaE<]Vd>4HnYT+J]qbFZv^"NbϺY27 LRrݎ u.P$0Kwsn`h+Y@DR} _Z74񼪟E¶c/Q谀LrT@m'WNp~z`_~q@IڎdNG]dpX8}Į]SD=܌_.$r;&y 7j:`-<5[p sY!?arFyQDlLL?k'}rjXt""Y|0NȬĮD_XyrvyȔ\a ( ,1 Ń{yL !@dy*n vLuwPRwK 4=qqߖ! (FК{naa M7exZZw+ρ*~ς5/˂BپX\z}?E]ރ4uEsNC{řl]w|dUhp>1|?)9] CqYq:{r KeT C 42v ;>C8oqXL)ENТpI*4SCkX> DXD+ YiCU01{{+\ H:[|yߛI_5@)J+xC'zR;䣐\ oA:WS,Ƴ^\J$kD=JB/+ˆw)C6M{ 8iDpͯ:v54s)Xt w.?ymã+&p"=^xڬ|D4icb+>8A&˽_Yl{Bl6Hnfjq!$H7T~ ^eܘv#xɳ1b^*GqYT]Ҳ g k (G_ݡJޙ4Ṩ$j/DC|<0 J녗}tMn9[\|O"gj-j=b}kЧ;c]Ȟ#C2{ڙQe1+ĚN@6lq4Ao%NP))$Ɯy-No˓LJsp\0yRGlG.*;n ~>+&ۄ@Q aV͝(WP}vbJNTCnNETh2FiEC 2qu`:U l .锸!%R=d9j&H1dj 9=氣UI+UWvilTeӤEw/Y]'ePP`05kߕP$=Q{#Aay[l e5_#<(w-edb9)͏)k: _5]?-":}Jԩч^)k>l[` gS3i߶y'!:0r NQ2+[B:9^WF 53[$'!c@ OT)y7ٞk5 AH|Lj98ow]Ʊ3EmwwhǸc/gB ңfw.# Jx&<.CI9Pqd&3!Bܮ52*>ۿϑ 4Oh'9bI~hvm]η-+-qc⟝`oWm* fa2d/<\X\el  ](FM?B̊J)<%B85d6b<֡zmaaŠ4Z>p5z5JxzPJ !^*HLh.0fC?/M}ƳE!qg!3#ܹ,'H: ={ yپͭQ]g F˙_ǢAZ/F@˘Jk+dtj<b6<y{-טxx"*5w]]R![Jx=J/}yfI^0oNw襜qUHbyötB ǚ~ܑuErhD M֫{Lh3T톢bSa8 t#1KTNjp<2)IsN<;Qܱƾ+\7gD8ͩ*OǘzPf=v}@&t@ս탣.[Cʕn#u6bVeKՀPLVza qu/PU9ݕK$ۚ%*ƫYk&x|n1S׃ pNdõwOA&xnIU6Ŕanڄ `!t.3:'͍6G܋'E`8aF/H[|Qt+'&9@O%qF+;@y$Sam=7VG\?P薇 6Fl6ʓנt7SkI's G^݂F}_}88dPBxpR/'PkOe9 | gtog HmudRVZps\(9Tt6'"2'ȕ+*)ݘv?pxÿ9&`%EMfoa?A&h5&[Yz"__ݮƨϙF+??Ȑ:VZ*~KtjV9Gt [mY6\}5 gH/-4K[d۠^r"L+a~.Ʉw~񽇺0iS#")Vs~EA r|ݖ!X.RBU kqT=o%~s%8+^WuO8ɠDg@PY\}XC~t'G3JF*٫ >=ôE1ʙEL}{J&T1' )ŀPnlqӭ"7FEd$ό_pkAÒ<c]Mhb-ZNL E}$}ֻ3/>QƷ|o͕Gk).`$ ="h-EUa'aj NMi#n! *{}{A(IK, opZ4.! [sAGTPB=oO*I,ׂT!ئs8-\ DpU br޾9W1PvEކr#k?g.7\l&|Й7޹,w>/0Mc%k"l)'uvCPn7Yʑ-2fp~"_4 #aUA!kdI J#d|wNſEY 5w#QoGo1컪k44I fhJO=;l?/Lf^rtMW 4@c Q#Oq+)Q&x r:Y(eVyWrdGx`sݕWV-F׆H%*Du`H,VXjowYôr>?!f 5IW %u?S6ai߾Q+b0bWy֟l L;Tij'a~K!曪wq]a~ۉ3ɯ7<={icc#Q٦2&:kY FH 4ޕ֓z!r rXZ4y.?o̐42+9 qRD'=A8ܬW~`!PI+4KrXwgY@;_K+Uc⾳]fkt9}(tt`?q03Ou@`# cLM 7j-6q:H-maLٰY ( 1¼$ˌ''wvğc+X$IFZ ֶ#I?rK C$; H'wWZYC m!a3{e-D d q]ԉ"cp+gX{li Ig}:(8pʼn0,]!uK(IRCA.ax{6IU[ƿ!}0%n7a.P&b93'lc\Gޗe0)_Dw69:,TtN ,hbyW?Cڙ3H\E99\t;U\"4ʇ=L)rS?'a0$}RKA-l=7ҡ8^]T3IcۢZ--Ni0htE7csSVx/{&uK8eRr xI[gx|"\V⅋Uy_'}:Fz"ř?$MƑXq*[m</s狙rD͉dP QLSզMHv$OȫT˒g/aijiة\c"iT#{~cǵ;*Z!G{+KĦL2/%o憺FE5lE5ifq?Zxf )p& D }vRhyR<.*JMZ=8@(Gyi7́?/y{X?3vb(b\?Im~K#QPtҪNT(Mj= yԼwsU_Oq)]?gML 4mЂ:O *w*(oPgVHe}&ھYN 4@LqQC+EL}g;p$.&Yd.{x]d3T{vs`AznӷӕŶ҉cUVV&ZOCn\q1(IOl:7byyˊ-nݜҌ+oZB>Q%2Ѕ!.esBy1o:ۤ #[S%Ɩ:-R3Xr)SK[52¬<î͒|Iet3^qZΙNY}5EbtnO6+1x6&ə9ZgG1mEAJw XD4ic*_P\:m;޽-#k܌)x%fU!"~c;9|V4pZ79Y][|~ rG 95u@ԜЯ}I;L=J* 1l]@sM(LcӫrkΫcUJu3$_'kUI .r5ۗ/Bй67)BIyxҗUߚ#Hs| Pڕ1GsS#45-Ƽj>ٜxvԔ>V= g in!DSԭ^D&J%9}QPisY"9#RSā";xHfJv&:dkG OΑt u-5aH]kZC[ۥa\?AV/-4G; w${ߝ1Q8xFt0[6 s#v%uDӯ66q0f]mX~H/(/ g-ck#t9*L媃izlpL`'V.a807ԧ {3 oWQ]_ꇟ[K}#晬V!Į^g+XL {jHioQ0??P PVqd~ b; ?X#wQ/\@Y?G1'?Agqw >ΕyFI](bDżuQ9fH^T) 6HXo20u]P%n&ۭki;3J9n`[\uPV' IivJkb.S} k8K7!Vx(o 7xv[9RvxT^&Y?h 'Ǔ͘8Yh5{z0phƕkp5k_'y_wQe1XjL2etw~bf/aGwr*M["gM\f_xQr.ZnP*?V; 5m]hXCKvBaa E|?fbP:RO%5b˳&98Mscv\\7"|q/2j$Q&dP=~|ءIgpbx`# K󨴓 [љj{ z~y׽!fT[btl"h(f(ʍ|j(xBܤ8H eE*?*>G/[~H\eUGHWmoK)b[lXV#NKc.>`Ati7k]cĠze[Nt;|)y=䘓SIw0QklQU>x_vS0? p@ū5 ?{T)ir]PSh#qo<&EE$xKqS(0Zi(f65yX ~ +]}{[nЙL"S&0Ȯ6Xm`&2 3e_v[\$o6yoίGHa!""HChYg" 9G%Q"{h:C !>t:y;-˲ OA2^zO0)-:/qlxa$CαΩu!mN2))7E88aaZ.q)¢|+UJIw|L^:Wq]S@Gu Fޥk  "+%du`Ǚi5#YF(P۹HMJ\*WfВC;q̷| ]2ۃ7#M9WS3LjT23zU;gn.G&qr~9sV1}aLVmlk^/_JzȒT3=cA2A[\x(i'1m V8sF64~SW (0FIOMa5 "{e:?/`nfJkVAV]܅^qPʴj(N\Ds!tAIXqu$7Mϲw~ $A~vhvXVM\-xU.d7g>T$Y)qeI2k?u6,rM}z'A':7FZ~$bilO^`_C^քes@E&,O-ltLO$A=r')|!y~RY4UҸBۡ-GnRȑY֯3ت~|'·Ɨh/Zd-oY}֍HVZ]&+FPg^ʏβšXIeĨQE.sV^1懭yEy[OVؖYj$*fsFeGabteLu9lqRM49pj扔%(KN5TʑVwb+3C^ ʶR T cXI*BDU׎6J(Yh /jfؒ艨zkf.5IEL3~PKO0S ?q[  xLONG6P?o˛ Dki'{r0xA&ZρAوF^̖QT~MnPQ5%mxC׼Rg(B 2r5u!}C>O`f$1l:NOTMhnC'eJ7Z' 4r T;+tXV;IYHee⊕[Ctn%f0Yfe|]}0_'!Wx Y#xe>Gq7y@vȕ$5!ؾg|l>`}ud)qE!HOaI1jMT-! J6\\y`[@d?QgkPok#wgoM90M.Z*$OB4t7gHO[ڝI)_{p,rmBj)hvD G.H8gփ'Txۧ m6[k8}5)r.ZvN.6C /!!Vdtl N*\#5=T{-YYJ_׾+[b, "ֹ\+r"*:lWps[&al h)N+E>إ~*  9M!yU Ϩ.;|UZ!wͥMc|Q΃}~ɕ2&~܊2-=q~U+E|9nKQZӦSb4Q#UJ 1_>a/[5J,u0`Qf Բ}=Fz2Xs8F| )ZAK, 6F71!6 WGX( $!x nz*+~)ޖ1)=adwsd<]S~yaRoWe&n`\HȒ˷ ,rXy%pζY sTZ\ > <@dq\տdVĢŔ(SKůou;PIItHԩpvxxNG$9V\C|8`SN4PO"[ӓȚ˴*j O2DB_"xhQIf:KA,C_H j muL t>ra%J8@oDrmp9ۙ&pٚWxq;mŚeFx_ cT*h>t"h- uCaͺ|{Gmh 'ܗ`¼q膲0\.^ d<61[3K& S)uw&{Q*`Bqv@PKTcgZrj-1E^>Jh)]&jO3)ERip)%R?<eQq\[R7¯Qs/n)>Wj7\u.,Ք[=ZŎ?+oAQYŢ*$fgZHVgPJ6xq$hi&ya (pג4!*{ Js^/kDKCXr`+B͹Q%EE%b"Jt R 0&Ae;Y咰53bW#OApe+MVvEM/qh!b'.I52ᄼ2Ӯɒ 8bi#b]ӯ9 @S r ݗDx$N6†m\ῶSW6UԪ0>;OKg+!NV"4hɇ;j6m\BUo|W)/ljoTEv ߻i3\KB-WG8[E^?Z(8Q? L@Xծ'&{/R#R:9{F61[rf"U' Wyc߼|&RݧODc~ۇl\k!$J+d.-Ud~?e]rA:#vZOwcD>qc!Y !gIWz25@7 nDni[ק<{TMeWP+j1#zXȹ*Rc)鏲*Ʌz `ql#kJeΕ1م|;)M5Ne h"S`KjC&{WO`(dQq~=|YFizBA-ck [c-Fһ" *k*E%KP`7Z"[B [LJYT4GOrq]}7gp2]֐&ZP\ٝ._]ziC]rя[f[llj,dǔ$Ì[((N&GJfiBS̆u&ɜZofqL"wFeN>{~B'{eVX}1d)9K=u,#hmV`kA$SddG4^8!6Z^4 #s9.ƀěZ:ln̘jPIMJ5r,œ[-mN3{H[ܜa~5i{ Ky1TɗO<÷XS@-o;iR.}sPRյh>%A=}H_:MM2>/4G|'HtU-@&W[h^`m&+= ~aLF[/|ҝbJA aӋH0${V\{p܏Υ/?7--g2싄д>{ޛa܇dX`sifσ |Iz"ym8LQ7ՖF]q/ftJo7[y۟~R[H۴+JϒίB/dؙqUQřg*_l ~j.l"V垂ظ¹BPMۂ$.M:$VP6 G_YL)%]j٫JBc7_(n}іgz61WHf8>J4F3-LC%oj^2 6%K::lNwJ Cwnz2V1S6%n1"-Sx#,#`ZTZpBQ!שsgy^mU.$ ƚ&]Ҽ2ƣymHDsA,b1*doqQŽTEGiUCO^g 9PSȌt^Ö nUqj&kۓY`dpܨvs-PRd4rW!ZIx/MĎ/Xczz0}cpBG7=-bILiG/Uu6l+1}%*^dƶM|-|>(x.]M< T6ח[wӷT},="YY-1kXdI옴%6UsL#ߴ)lᠷ6kv!m*IO\*s=_iճQd'SS鱯c-v Gg1ҳ4QN9uXf3C#Uĩf^rwhqЌ V"V!-W'lSV Z1wn:[W {fdK͹z: ?KCfdM(B+ 5$eg0 ri^RƢj.Z=4:5Z$ I)arD_S>B]pڮc]9,ɇJZبj!5ETrKOJYG\c\_HFj|46:ڳpF50e`Ev_ZcYL:&oB/-/~C[2 >w9icY^G]4} H(Tnt zD)ϊ#{H+?9 VaM(ګSE1̑HI4TmrSlOuOV/pDWzsℑEh0܆Co$Nc&Ss'=N]5<^1bi-wc*J*vHOޣ ]= oȉ4ڸ<4l!H|@<: Jctş^[9ֻxv<RlY] tx k8魸]3'bmu'سRkq/|e8)|[\AG{_[߱Q̝JH;|sĚizBji'h[R&3,y.- RXB5ƙ^NuBp;J [#AfKuS!#4I-~g=+Q#H&ORw^9:f생E^G_Ń =ۓe%MeNRQʡ@D4y+=>v%,s)_JJn<C6Ҕ_3%-؜IڣPcӳw1 -<)yG%EMUƴ;-uD ܟԺ(9rb,v̜ڈ-iҹo)~)$E|7Zl@}NETYo3d\4|ʨ 4Q4.#5GfO+2{Xq븕L:rFHpCVδ$W y&L-I!Oy(?_TKO&҂1^!ypj@C)04&D誼B& qA0fV瞉*|&~(p770m)VD-T |>%Ei}گ7+ ;HɂsdARl;NB?D؛uO)Ws{<>[Y`y \CpUbt"y^haFmŸUBEɍBUNɅwbh=-Dpi}.k3.zJ9hTMok[P"Ki(dVrE7LM!H˯(ڹvW[HN17 8/fۤ Ȥ Zͪs$ QX}DwϨftVk:mƩ85/BDXTkj].|Dӱ}'2NGI,ġұ&'(({]@;kσxHF%a_ezR~f]ۿx))*HQ+A\Eɏ!lvMQh_#͢NDywT*>hݡר^P`% H5<{韑i (15ĉvځe2'y2*2g۠m۫ޤ[WC)a 5=etycaBrܺ7EI[O|SvIj;۰˜]gb.25^ &Av'AybHBiA/$a 2g.3tϢ(4v z#8hwrr8ͪ9\h)17̇i*]k*DTn_ {SqJ+6έ6NݔY;ҲO#k?7.9XU?n*]e:8ш/kCIAPd$c.A玭܅%: A( ȸ|frЌaX[}M%1x>cdަ}M|q8IG,|:$~V?Nש0/W8B)aS0X˨@6tt;1G+ʛJLU˚s9i5)ݥ΋o g(Ga> endobj 395 0 obj << /Length1 2090 /Length2 22730 /Length3 0 /Length 24025 /Filter /FlateDecode >> stream xڴeTk5!@wwwww) w -Xpwwwtg0K置yEJL+h2lh2 [#DOJ*4tي:NycNxR84dN*v@F_@DkdښY)SAvfNj0'[ ehlruښdr wd 0Z@@UYTI $LI^X\UTi"r* @\UY_;3ʻO?鲢** p:8Zi?ޙjɎՕщ`Fgg?s G+ 5g[q:Uϡd,?Ib9mGnwA8ip#_m ʕQPZ:m m &";;8!ot= ] m=1~lc*?_6YA9I1QeZwʂާcKWz"2Rd011E*jk" ygg|"sr9?zvS [?7qWwJ_ o n?f?1x{ځ֎@o S  <#;]꒶ L? PuQ)o `49 ?z9[[)wghca_ÕB`ch?> G1 7_SY]f3ˤ6Y}X\ZF6+`|}z !91eG2L,lLlCCwxw0<%mtK(z:[{ ` rslz?!;^o eFz wgd>[ۘ2mY,́,nd[s|coߙvSyctQ߹sw|'w;1GkCGt|p|7dzo)٢-uo/ae'P!Nn ﷛tߋBB 7OZVF-c339X6J5עkϖn@c91werCphD)4)'I&T,bD.H1/$#o[Aa֜P>~m(m#냋,*8F5]vP*;Oe*%:|$4zDSڲ }5Gv܅vpO=TyR;?jQ'huy4]$ ]b`^)t( X;<+1/*e@fB~гP9lI^ ^YKW!% MIϷaVT]JD}_`Kl,o8ٕxNS9$~%St*L yhόBJ@~xιي;vfPF<5r8'oځgr נB,Y".B?5L$a{Ft谜b^Ap ?WC3J&6}&`f tpFO3DZ+',WiІPORGRm yMj!il 9Uj+w?3(Z>,f ͨ jcz328^J!<ώb"V:@pRoQPFr9vӢa* xpK v+ͣuq.@Sly|”D{/~amU\n=…P"Zp,/Rh&ɫ埑yW^Ld^ 9~D%Zvƍ ;4('5bRR;͈PC[܊{Ke.baP fMYdc6BsZ=>4 ؞SNtWgUb )JHPx?׿LzGa XΧ3&M)[pimcA֮GV~]I|$E "\΍^8j =BF v8u;䂮P ݬ;O#Ny cI}OA`K|񳶾2%R_5Z;ANE$[fui˭}M/@E'Mk[¬U`R_tvq֣д11k`w؊>5C\d\S҅r^j? u 7rRI ;`"'غ, a@1)'dF|J?*,KV(xxBͿ/#& n˙$Q]>_}`X2hz ]lP aw}Z :sOaqXPp< B.E-8KH2Xa9[EhH"k\&o7H%|A#?0TJļ“?EO mKi.ZUD1M $_ȋId](]ҝd#c1z)m׵k =?Y Z*+dS]6M#IZ@R:v5w*i͡*87r6G\؃IێöZRI`yOJ^NZOv!<2og5~źa뜧}lr)AȥӰ@̷Ժ@w:Qt[Zyx"ὩHڵq"ƂxXpz[1nb1E6&k_" K=|hj\6u[Dӏ]*wơc&VjpHM߮EngniCTDP4Q r;"JBoy\\Sj`=Y{Z.,煘BO G5ŋfbYlLCgDnaoF,=3V'oS\5tgںv qȳ:ixL)Fjx/~ү'yI*nwI4}(UTnkhV hMxf eЃ~@𥫨SGkWxGa=c4oJÕI\3 E$Dĥj~pAURUz#(2Pg|kdht"Fgv(EՌqWw9h6h==1ۥrJOEL+ b=;5$3L%!D@"P_=6Zve@NA_~-/sI⭽Pcʭyc&ܲW4t=y[E6RLv><%%hSL,| enxz䊘w+8C5XkkX"1[Ս646 e_j4輌Gߍ#bE>OB~Au4G@]Dp u7Xv]]j/yw %#ݝVX6{% Rʜ G2*6D0+ň_2UL~cIt1$&G%0$:>7&`ڽ|(9vO F+dLjz22B|Rf"<~\f_/L(S7^$qmO}8ҿf3@_]:ш!BX>XR 2zN9uV}<[B'F"y1OR+Z1+]č޷EQ}&rk"Ү;7c7N21vMfnuxId4gZRv +A:~н]} |>ٳO4T#l6 # i.B *\m,E*D/lMDo^ Ҝ7.mg~vHjdN N'+b"rB%jG2n]4#ŋ Wg>vAl1Z1K me n<~}|p{2#*H0 #tuYG|އ@A 5Yo~p'lKLLdՉdh6| U8E/c( h_[/,bAba2[9U2vtZ财r{Yg IZPaċkd2s%r8g$W5֩C>gV f G&|>yZ)Cocbqɐ&JWoIaU6G? ' RT`B7܏P +H lĮN/,~p|N uE  ZkB?^y; _m%- :$Nf]3O5?2!ȏ$2oC7l"ctf~cdx#IPIztv߄#Rp*}[ D{m{@n"œ@IUη9PaОϩ=Ġ|߫>8AW@"J3v{ZC|&&p8k7 8M ~ԯd+b[ԙ۴׮$ZMVYՑ b&\;;6o툏mD!`8"2h9JDCd{Lkޟ03[]DqCa>^YM*CI>VK!*Fz{n!|l'ftqntO lƛo7>{4%)_8 Ҳ:$; \Tb5PD<ÃL@|Qzc(J u@KuS+ՈhAbRƫ^iUN[}𞠐d8Kl'"I]*鎐N7xP{=>AMɉE$261{g%5xPsU"GV l}h* }RttI%vW+r岠FYl1_'YcޮgΫ٠بa:,GELAT4A<q4?j0s` xڸO @RDc|ҕ61%Wz]@HV:^jG +hi/ՅVH7G@zUmyAtZgwJ ̍,%I y]aC*ĸFjԦ]`A|nMM(CQvyI(8+\T`HF >w9A:<%ŽmA,&2p GM1WaU)0J0VU#$-\FEƐ}v\s`dȢlJ?[qaEW_65 X,AHIn~ZrGJ%j)>xɝV@[ pRXcT Li_LS}d}bWW̜I!\1o1d$ST4k(/!Xv&onTZ9i|!#JZ .K߻B080N<ݮIY2GlWµdNaJ~EBhreQΏklLPC& m>|?pޘn |>1S㵔%\%-`fjHDž.:"eExx@َ?ekJoK`υX(E&glKϮ-$(2I1ƒwCtXt36D4K(:ҵ76::rcHTx5^EÃb<`QN\ FH˶GƃC|,}l 1LKʂ`%?5Y ǒؔ (jửIJ# Ɂw7s>x搆|`=/Dv*Ϊa.8ꍝ`%ejMDIx]F-L65_SkE?MnǛIws  ~WR=-_l} m?ƶZ_NxzZ|hX=8Q/(k^&#;R >[+#?>H? Gh M2w amQ.bS=:xU{32 1\wkjGYE+WQ{0q͟s'Ƒ؎vP̸4gQ}쎳O@x.' V& Ҁdw 57ihsQGEs7XU]!a5hp_3"pLqi#2; .ϒv Fu!_謿< w͛hH'G4Qd5꧸ۏ#~pIاpplT(*%7?>jVQ_(u-t[B1CqK!wkH<(Z_kOl ]Ԫ6tY .oegxڛ"L u p~b { W|%ƚIHG 2Z莔7Lrd;Y8h{k ^6g./sL=. X]|Y>1=b-kI5t9M362 șԣy$pOm4]F)29]M9_O7F9bJ+`iCV֞_4;KE&*Au\/\C=s :3*Fr[yKn7,nT'(J14[ B{eTh o^p": $wm&!`<+h@'M>}2,Fk'u˞stE_=4ʈĪ:%%=5s£m󥼧nzǴb3%꥛`%a-%Gf9`5g`7;򙉖I,h2N )3#;tRbm zV2i%k7-3Ӂ*{fMX9h̰.ƌ8VG%B2y64O‚MژoǾJdMDA| 7y,--rٸqj[GXքJ U!U $D$Bϗ 0oĚDoVg~]I63V3`_e?{Rq9b˛jX|.*:')MT|dEԹ+a t8%CEzi=ofckԐgNM6LqC:q؅IRR{1(n*"hb /4"+B{HV.ucJ&H*$?}EePH{npYтBAaQ^h,udX'L!c\o÷?9[j&͖<-߮WiwV6 -ł. (c`?h 8YFS`ųQrl,#}}Ay3'g^i5eg%Za2 SV~\FG:aZN ==>u$dq}44h# 00$Sqa'+. Z}}x#W&!r#沱%[=. }~ŶvE!1)3kCF:ʇf9%T}ϳ/xRII?0H\Z/]5O e8lY5=L4jCJ/Iq59ybƃHEC;78,1/&#؃a;,LݢxY\${ &ޝ.~mOa8hԗcDWI2nXtZZ1HDiF l&]*9gB{eЋL3p'j)I RPy>s)Sf%Ay`GP''d0Nӷv 81E2x/M91d'l%(H-dn%퇆ыq?L5)P7_kuVpsm ǯShFiF'O$# K{;aYAޮFvG!dfRLPGhn҂ENSڼvq ֶR~w4Wڷ^9j~)`PX>w4Cqr(qXJ8)a4Ul\)!gN ( ;Oyrj&<`-6oZ{;L^`^١kB?Zx Y7''ۣu3yqsZ&@XG:Pώ`,SlF<>/} aE<[rIl$T=bS%5NH4 1nz}$=C݊e4UHg_&*{LS0Mb/exI{FGcĝs'U/Z >Hs}4u>u2r A~RMɳ+U+X!gGJ]kȭn,&H/Ħ7H/suF,.[_/P P"6$D Eís hxluk+>ěrfgHP/ V8u+`]ՃdjC3fJg]L.U58p6κah.^\y#g7C ҡ22z$G)CMX7 ηGx_L>=D|'Ī;ap-iv!W{-ʌlKt`/"[>V7쏂&;0X94IX#!_$bR\ .pDX(0AG. ƨn,]nԟ~yŹ./) '@R"2K.b-: 1SvQ5cI8Hl7ʍU}7pT)Z;1X n(&&GGVHokS~|DdzgNVx3Vn:F´_vwN2Z(CGI4x)u>1+}WU$(Ox_e.F0JbRIm2q0gw/i7Dbdصl֗BfR^f'&d@ܠn ZbeG/oeI nDU]̾A{kX «bX͐A!C'1I=uSIǙo^MZD "xnгf -l7fM~F3 f< .ы iCshM5_\]su<-m6.ؠXº 36)EE+ D_ǛiYXR]SѺB+ȴaxpF @C--jF7XsG[ FdѰ|CE:E-vr 됳^@X~k󆂒 ZO9qU9IEAX~>ý xHu֑٫3/WOS;bpa;CaDں,&TrorβA57o7r'-}aq*楟s6_ӾlV"L F!Xn\dz!d潒0Dc,;@a]ܹLG(Xl-vA-  1"6%[g˜{XzT"2F~X <ڈhLYјGc!g.xԘqVڶ' 67qqqq^r&Bi~;HvK OsE=͸fyt\7HFm^~T ϥ.PG9iA5'캄|J(< +1ԕrז1ގL )_0OmVR߮{+Ks[&૶[|d03 x }a.YJJ\S/1m29am^Q`RSfQ[#˕`fQmO,IzN %oCER%ۢ]E ]VэuQb(ax;&/QFbp"8jk{ !Mm/.Q)ⅉ&UGDدC2L6{Ob=* .Nטkxm#6 =Tws=8du˺U;D~ɢM!zY$Y>'Gf$ 5Q lE5I*,l1k4,a H̼*q733 ZӴ`hͦOm/uֳw*b닅MKJJۜ# wRTf܈':zUCTX?Rb"ww O }5|͝ݲS v'zx#A6FALѵ;\3vq/z2pF6l9nb"Moh0yM'?GS-BkQXOAwbwŪsѰ*1fR5%i.G]hӎ?<$/»>40AlF%@{T3̮S:x;9+Dn;@X$E]ugϥF(=kݶ_8UJŚ v<` Xvc1\:hCЦ#$uŧVtuѣc"̡b|=[.R>c(g[xO]-xg[)8g·[T^ ]M{?iK۾鎽 ~pv"~_#FT˫}MAY Y]ﴏ l,mi= scK 8?$η4'TÁ'ڄ/.-BiZBvC=zg|%O Yb8ȌPLyfV}pg;Jh\Csk+z j@OcCd>+`iz2v M7mj7gkk}F,'V/'ES[w -n›$֘P K|''վaB!-zd\芩2?5ڲwU'Qn t t;c ,И`;//iF|j3n͊BbeMpեKh[g%iޫ8ә<MiA]DR*;GP=#"5't H络/a;m#'?鵄S=;{O7k;Zlˤp(„DٜkgC))nH#.Ʈ_9H'PNĞXa}Jwm'h8 2̱90zXpǠsԊ˘,rQn+W#rdZ3h.MxuX"6/?ߖbAI u\bxne?"[dЌtl[0Lj)MOBwkhrH*hQދ.ƚ)&scTTӟ#n1ŞzisyuGJc>/ .S\^Lz>{ ͻ6}N}ZqHp@_ 0JWPGMId}6|N5yrTu/LdWQH!& +ɾ7:}h93x)ϻE7gftg@h GIL:%7v*E =D#y[FleפJcwIB"|'WT{ޗZ{\W$ kO‚^t1&o|M\&9B6$[χ\@X>KȬ"u{6B.4#Ulj 複oJ& ~-{9%$>6yFa0j'5c$$Bo&BJU+XZTW8{Mϔ*-ZT(A D-$G]{405nqY++j1g+!:ڽ&]!`&^ ȍ)uUJV [IҖ RI5o5Ȯ^@1>)hUCᔳ}+D#c 'NRm{|P0ɶmcƲ[6Nv۲rgˮm۶t,;=~ć*GB'|*$%qIX83= `-0WK,޼;DTìlFm*f7 5/GdVJ \SPx>]ǖvȘr0n-q ]rN~yk uLxtd'r9x/=GjbC#Ch<l$CfoБZ";TOMxN:0\jdѾgIQgtf]J`#cz ]i-^D;sΙ<0x}C܅ R]eDz8D3yjB +/d/Ȧ04O]ko텬["ԍ@ *$w+-:-V} W[fl0}uuO?%ljB1"bSG) UeЦL)xQQޥx[7kojnH`?QLbq֥ ҸnQ8U;W&vk3Ftt')+;WX"|[#Fu8Dt ΫDsgUIl.#$fKqϨa^{D~c2=P+Ov⧴gsG ͫ}NG `j^ HaQ2i>XW^ƒCDy/(ۑ2c$"+K-4i͗ojj̆h<҇v .ѡ]*Z$Q~է ո8E[vBB1#f8ubLg }_CJ™PȐd~lJ吐DQLXerFqd-L4^Z1ͻ8~fȷaw2aC;*³Hr8'onej1m =d*z@d1^2ş?J[JO 9׾ET*i'ECE?W#"CG33#0i?XW:o7(~~޹9-4Ë[uٮD>rn܆1ñTu-1ŏړr˃Bbg-{MP`¹}`;mJ,xHl[ ՃoFt0g//{n+`HIT!9IU]?LQӚrތ`zv83k[޲0,`rUr1Z" A,-Z8H9ѶHJ9rA,kʋAud^pR|8; LLV2kpKOI_2Ql',Bֳ/G#=, ?q7? 'Rg9{`*y߰TB=YôK6Q}21RvEmnT&L090BFay hڏ}!5l᥎e<=(DY,,!\q&CCeDѬgBzۢm}+N;,Z?a>\'*7 !,_/v@xZgѪÇYmY2g8>9f2ݐs<~<8 kt6|Kay2"T5ؾ5$Gq콸Jn[њyr-7iZx4S$-h 8}x<=Q7"j~aEMx!9 ijroeaD i&9;լ'ʥ }Me6' &PՆ.lU KGɮ|9t]ȴ ۷u="_ᓰ9a/fqdE~@%WA%XAͳ<^VTm`/I9BABjV?y$t׷YcY6)z2)[ƫW-//`K|4CgLh047paOMCʒD/g־!4b@Y DBUun`sJbM[sH}Ӭ;J`EHrFE j! qԏ#/t6CK͸wdS/b"SPВuEº 6& MO:I͹^c<;ןY UL>'HiCW}YO+5qOcswO`g}JA4~<]I >?YF^N! -y0"KBi3$=p*Y Y$9peybOvAoZmF}6U5Bayk&sl-N5ֱo{lQY񖳶;V;]썝v0Mղ[#]G~ܫE=ӾAe@!ߢ Kf#W:UXx7w%xNV[vC-IX aLd>3[073Yǵ5nwM >C=~ Ĩ OA͏ZҠ )i#2h0 !]iKg)@tFˮR!#ylbt9˂|Ԡ#'oZ͔ؼ5kW5Ӈ)9F-c?U@Zc15 _R4-4oD e8Ы.*RE&«7H ]e?9|a5B20?E%_,'2:!oϿR'&K3t=-)MOsJ4Sw.d,Zttõ0PkEEb6% 4(2uWmW.qZ%MLl9YtZPZ&riuY;h1'zK~C'6;Y^BaǯfB,Q6ܫB?4c/GڕuuE<P?ya?t+T o,ը⧫ Sig;(ŗKEP\vɃxiqZ_Me`AlwG'= ~FRIScqc+yC ׷Z /y5Aϻ$nYO8)o>it,DCħS0 l'cw\œأ7wLYKަ9.>L &H6t3M\>OBݮCMGV73Xzt";9g$є&gT |pT]rE'O瀿&)Hp&ږZLιPFۈNI݂ ԬN)rHFAՁɭ[ơuqNI)Ԧ[L&fXRmZЀe݌RCGlm"UA YAr BώVLB[f+;ŌxC{h?'dnRc6a:,|~4DRtzr0ɣn +*?YH>Aélgx1G3|D v688L)N.Ƥ;>AsVW4g[Df=N>d 7#V۵Z7.io| -ٺ-HnsDSZu/NRqpP*KcAwk 5fo{/G}^ 9_V#!X'璭I1' 608ƅa&1\lzg4/+؄؂º]Ls l"3}N܌h&QHdPɊɶ1mYy++7" j%'/X&ڮo*Ǔ@1UY/*3^tWsY\_jڬ'54dYSفF9뛰X9ذQɶѨXQkn7% U.w2T:1۔otI i)@ \8zҷJheqQU]D|L{(,4)A+7X@mxM'>GJnx{;ZZDC=!>k{ЈsB}Fgӡ]L@ٚ#l3p0G5$Jb#&6 sC뚜;`{3DʨjkEt Bw>;Cv:o+;HYY듣p]H.IT۲C쀸!$xR0;G'Ω[⛫7]D~|h:ْb(a1c-DU V}xJs.z͕"'/-Ėj͠oyS{&W 5GF|Ҙ2:t%TnT3|=!Iw)垃kD#Hx 8PZOvʇ[I[es3'`H@oߺ4vY'l2떘׻)DݐZAvCwz ]>׊lix.0>΃Dz̺k6^q0h&ҋ0$%0eG !az.}*v򾚝}͟Qʿ?Ӥ}=++|)Zw| ZXeB;nH SGMmX $ 19͕\\Wi^f "r>ʂCy0 8 L–/,16x=}8((FxRZt!O* uah#0c$M4Aο AIw7]% c{CXERZ$&J#2JR1ʅl5i@+GC>E_MU\EGxw=3k4حMI,W={ӥD_yM^aq^ܡ|}x&5"h˼_ǯ8f;l9Y|Q%f V8փ+VDv re]e; ҃qtV4Z\&F5o1w$")l6KCCL' iK۞56%gљ\9-*%4 $^l؅GV#(zsYH=-t Ϳj d`@Sv+/3xBLbXi u Cc~wa+hOa SMHL $0+mKVno; z“`Z#2roK4xxo) endstream endobj 396 0 obj << /Type /FontDescriptor /FontName /HBNFSD+LMMono10-Regular /Flags 4 /FontBBox [-451 -316 731 1016] /Ascent 599 /CapHeight 599 /Descent -222 /ItalicAngle 0 /StemV 69 /XHeight 431 /CharSet (/A/D/F/I/M/O/a/c/colon/d/e/g/h/hyphen/i/j/k/l/m/n/o/p/period/q/r/s/slash/t/u/w/x) /FontFile 395 0 R >> endobj 369 0 obj << /Type /Encoding /Differences [27/ff/fi/fl/ffi 33/exclam/quotedbl 36/dollar 39/quoteright/parenleft/parenright 43/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon 61/equal/greater/question 65/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y 95/underscore 97/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z] >> endobj 371 0 obj << /Type /Encoding /Differences [15/bullet] >> endobj 366 0 obj << /Type /Encoding /Differences [41/parenright 48/zero/one 52/four 54/six 56/eight/nine 61/equal] >> endobj 168 0 obj << /Type /Font /Subtype /Type1 /BaseFont /DLEPQL+LMRoman10-Bold /FontDescriptor 380 0 R /FirstChar 27 /LastChar 122 /Widths 376 0 R /Encoding 369 0 R >> endobj 166 0 obj << /Type /Font /Subtype /Type1 /BaseFont /LZHLJY+LMRoman12-Bold /FontDescriptor 382 0 R /FirstChar 27 /LastChar 121 /Widths 377 0 R /Encoding 369 0 R >> endobj 128 0 obj << /Type /Font /Subtype /Type1 /BaseFont /XVMAGV+LMRoman10-Regular /FontDescriptor 384 0 R /FirstChar 27 /LastChar 122 /Widths 378 0 R /Encoding 369 0 R >> endobj 231 0 obj << /Type /Font /Subtype /Type1 /BaseFont /WISOSX+LMRoman10-Italic /FontDescriptor 388 0 R /FirstChar 44 /LastChar 121 /Widths 370 0 R /Encoding 369 0 R >> endobj 192 0 obj << /Type /Font /Subtype /Type1 /BaseFont /HJFHSQ+LMRomanSlant8-Regular /FontDescriptor 390 0 R /FirstChar 39 /LastChar 85 /Widths 373 0 R /Encoding 369 0 R >> endobj 182 0 obj << /Type /Font /Subtype /Type1 /BaseFont /MRKGKM+LMMonoLt10-Bold /FontDescriptor 394 0 R /FirstChar 36 /LastChar 119 /Widths 374 0 R /Encoding 369 0 R >> endobj 174 0 obj << /Type /Font /Subtype /Type1 /BaseFont /HBNFSD+LMMono10-Regular /FontDescriptor 396 0 R /FirstChar 45 /LastChar 120 /Widths 375 0 R /Encoding 369 0 R >> endobj 196 0 obj << /Type /Font /Subtype /Type1 /BaseFont /BDTOUX+LMMathSymbols10-Regular /FontDescriptor 392 0 R /FirstChar 15 /LastChar 15 /Widths 372 0 R /Encoding 371 0 R >> endobj 359 0 obj << /Type /Font /Subtype /Type1 /BaseFont /XVMAGV+LMRoman10-Regular /FontDescriptor 384 0 R /FirstChar 41 /LastChar 61 /Widths 368 0 R /Encoding 366 0 R >> endobj 360 0 obj << /Type /Font /Subtype /Type1 /BaseFont /PQCVDX+LMRoman7-Regular /FontDescriptor 386 0 R /FirstChar 48 /LastChar 54 /Widths 367 0 R /Encoding 366 0 R >> endobj 130 0 obj << /Type /Pages /Count 6 /Parent 397 0 R /Kids [122 0 R 163 0 R 171 0 R 179 0 R 189 0 R 210 0 R] >> endobj 234 0 obj << /Type /Pages /Count 6 /Parent 397 0 R /Kids [227 0 R 236 0 R 244 0 R 254 0 R 259 0 R 271 0 R] >> endobj 291 0 obj << /Type /Pages /Count 6 /Parent 397 0 R /Kids [285 0 R 300 0 R 311 0 R 323 0 R 333 0 R 338 0 R] >> endobj 351 0 obj << /Type /Pages /Count 3 /Parent 397 0 R /Kids [345 0 R 354 0 R 363 0 R] >> endobj 397 0 obj << /Type /Pages /Count 21 /Kids [130 0 R 234 0 R 291 0 R 351 0 R] >> endobj 398 0 obj << /Type /Outlines /First 3 0 R /Last 115 0 R /Count 3 >> endobj 119 0 obj << /Title 120 0 R /A 117 0 R /Parent 115 0 R >> endobj 115 0 obj << /Title 116 0 R /A 113 0 R /Parent 398 0 R /Prev 7 0 R /First 119 0 R /Last 119 0 R /Count -1 >> endobj 111 0 obj << /Title 112 0 R /A 109 0 R /Parent 103 0 R /Prev 107 0 R >> endobj 107 0 obj << /Title 108 0 R /A 105 0 R /Parent 103 0 R /Next 111 0 R >> endobj 103 0 obj << /Title 104 0 R /A 101 0 R /Parent 7 0 R /Prev 91 0 R /First 107 0 R /Last 111 0 R /Count -2 >> endobj 99 0 obj << /Title 100 0 R /A 97 0 R /Parent 91 0 R /Prev 95 0 R >> endobj 95 0 obj << /Title 96 0 R /A 93 0 R /Parent 91 0 R /Next 99 0 R >> endobj 91 0 obj << /Title 92 0 R /A 89 0 R /Parent 7 0 R /Prev 83 0 R /Next 103 0 R /First 95 0 R /Last 99 0 R /Count -2 >> endobj 87 0 obj << /Title 88 0 R /A 85 0 R /Parent 83 0 R >> endobj 83 0 obj << /Title 84 0 R /A 81 0 R /Parent 7 0 R /Prev 71 0 R /Next 91 0 R /First 87 0 R /Last 87 0 R /Count -1 >> endobj 79 0 obj << /Title 80 0 R /A 77 0 R /Parent 71 0 R /Prev 75 0 R >> endobj 75 0 obj << /Title 76 0 R /A 73 0 R /Parent 71 0 R /Next 79 0 R >> endobj 71 0 obj << /Title 72 0 R /A 69 0 R /Parent 7 0 R /Prev 59 0 R /Next 83 0 R /First 75 0 R /Last 79 0 R /Count -2 >> endobj 67 0 obj << /Title 68 0 R /A 65 0 R /Parent 59 0 R /Prev 63 0 R >> endobj 63 0 obj << /Title 64 0 R /A 61 0 R /Parent 59 0 R /Next 67 0 R >> endobj 59 0 obj << /Title 60 0 R /A 57 0 R /Parent 7 0 R /Prev 47 0 R /Next 71 0 R /First 63 0 R /Last 67 0 R /Count -2 >> endobj 55 0 obj << /Title 56 0 R /A 53 0 R /Parent 47 0 R /Prev 51 0 R >> endobj 51 0 obj << /Title 52 0 R /A 49 0 R /Parent 47 0 R /Next 55 0 R >> endobj 47 0 obj << /Title 48 0 R /A 45 0 R /Parent 7 0 R /Prev 35 0 R /Next 59 0 R /First 51 0 R /Last 55 0 R /Count -2 >> endobj 43 0 obj << /Title 44 0 R /A 41 0 R /Parent 35 0 R /Prev 39 0 R >> endobj 39 0 obj << /Title 40 0 R /A 37 0 R /Parent 35 0 R /Next 43 0 R >> endobj 35 0 obj << /Title 36 0 R /A 33 0 R /Parent 7 0 R /Prev 11 0 R /Next 47 0 R /First 39 0 R /Last 43 0 R /Count -2 >> endobj 31 0 obj << /Title 32 0 R /A 29 0 R /Parent 11 0 R /Prev 27 0 R >> endobj 27 0 obj << /Title 28 0 R /A 25 0 R /Parent 11 0 R /Prev 23 0 R /Next 31 0 R >> endobj 23 0 obj << /Title 24 0 R /A 21 0 R /Parent 11 0 R /Prev 19 0 R /Next 27 0 R >> endobj 19 0 obj << /Title 20 0 R /A 17 0 R /Parent 11 0 R /Prev 15 0 R /Next 23 0 R >> endobj 15 0 obj << /Title 16 0 R /A 13 0 R /Parent 11 0 R /Next 19 0 R >> endobj 11 0 obj << /Title 12 0 R /A 9 0 R /Parent 7 0 R /Next 35 0 R /First 15 0 R /Last 31 0 R /Count -5 >> endobj 7 0 obj << /Title 8 0 R /A 5 0 R /Parent 398 0 R /Prev 3 0 R /Next 115 0 R /First 11 0 R /Last 103 0 R /Count -8 >> endobj 3 0 obj << /Title 4 0 R /A 1 0 R /Parent 398 0 R /Next 7 0 R >> endobj 399 0 obj << /Names [(Doc-Start) 129 0 R (Item.1) 199 0 R (Item.2) 200 0 R (chapter*.1) 167 0 R (chapter.1) 2 0 R (chapter.2) 6 0 R] /Limits [(Doc-Start) (chapter.2)] >> endobj 400 0 obj << /Names [(chapter.3) 114 0 R (figure.2.1) 202 0 R (figure.2.10) 314 0 R (figure.2.11) 305 0 R (figure.2.12) 341 0 R (figure.2.13) 349 0 R] /Limits [(chapter.3) (figure.2.13)] >> endobj 401 0 obj << /Names [(figure.2.2) 217 0 R (figure.2.3) 230 0 R (figure.2.4) 239 0 R (figure.2.5) 240 0 R (figure.2.6) 247 0 R (figure.2.7) 248 0 R] /Limits [(figure.2.2) (figure.2.7)] >> endobj 402 0 obj << /Names [(figure.2.8) 220 0 R (figure.2.9) 262 0 R (page.1) 127 0 R (page.10) 256 0 R (page.11) 261 0 R (page.12) 273 0 R] /Limits [(figure.2.8) (page.12)] >> endobj 403 0 obj << /Names [(page.13) 287 0 R (page.14) 302 0 R (page.15) 313 0 R (page.16) 325 0 R (page.17) 335 0 R (page.18) 340 0 R] /Limits [(page.13) (page.18)] >> endobj 404 0 obj << /Names [(page.19) 347 0 R (page.2) 165 0 R (page.20) 356 0 R (page.21) 365 0 R (page.3) 173 0 R (page.4) 181 0 R] /Limits [(page.19) (page.4)] >> endobj 405 0 obj << /Names [(page.5) 191 0 R (page.6) 212 0 R (page.7) 229 0 R (page.8) 238 0 R (page.9) 246 0 R (section*.10) 215 0 R] /Limits [(page.5) (section*.10)] >> endobj 406 0 obj << /Names [(section*.11) 216 0 R (section*.12) 218 0 R (section*.13) 232 0 R (section*.14) 233 0 R (section*.15) 263 0 R (section*.16) 264 0 R] /Limits [(section*.11) (section*.16)] >> endobj 407 0 obj << /Names [(section*.17) 265 0 R (section*.18) 266 0 R (section*.19) 274 0 R (section*.2) 183 0 R (section*.20) 275 0 R (section*.21) 276 0 R] /Limits [(section*.17) (section*.21)] >> endobj 408 0 obj << /Names [(section*.22) 277 0 R (section*.23) 278 0 R (section*.24) 279 0 R (section*.25) 280 0 R (section*.26) 288 0 R (section*.27) 289 0 R] /Limits [(section*.22) (section*.27)] >> endobj 409 0 obj << /Names [(section*.28) 290 0 R (section*.29) 303 0 R (section*.3) 193 0 R (section*.30) 304 0 R (section*.31) 326 0 R (section*.32) 327 0 R] /Limits [(section*.28) (section*.32)] >> endobj 410 0 obj << /Names [(section*.33) 348 0 R (section*.34) 350 0 R (section*.35) 357 0 R (section*.36) 358 0 R (section*.37) 361 0 R (section*.4) 195 0 R] /Limits [(section*.33) (section*.4)] >> endobj 411 0 obj << /Names [(section*.5) 197 0 R (section*.6) 198 0 R (section*.7) 201 0 R (section*.8) 213 0 R (section*.9) 214 0 R (section.2.1) 10 0 R] /Limits [(section*.5) (section.2.1)] >> endobj 412 0 obj << /Names [(section.2.2) 34 0 R (section.2.3) 46 0 R (section.2.4) 58 0 R (section.2.5) 70 0 R (section.2.6) 82 0 R (section.2.7) 90 0 R] /Limits [(section.2.2) (section.2.7)] >> endobj 413 0 obj << /Names [(section.2.8) 102 0 R (section.3.1) 118 0 R (subsection.2.1.1) 14 0 R (subsection.2.1.2) 18 0 R (subsection.2.1.3) 22 0 R (subsection.2.1.4) 26 0 R] /Limits [(section.2.8) (subsection.2.1.4)] >> endobj 414 0 obj << /Names [(subsection.2.1.5) 30 0 R (subsection.2.2.1) 38 0 R (subsection.2.2.2) 42 0 R (subsection.2.3.1) 50 0 R (subsection.2.3.2) 54 0 R (subsection.2.4.1) 62 0 R] /Limits [(subsection.2.1.5) (subsection.2.4.1)] >> endobj 415 0 obj << /Names [(subsection.2.4.2) 66 0 R (subsection.2.5.1) 74 0 R (subsection.2.5.2) 78 0 R (subsection.2.6.1) 86 0 R (subsection.2.7.1) 94 0 R (subsection.2.7.2) 98 0 R] /Limits [(subsection.2.4.2) (subsection.2.7.2)] >> endobj 416 0 obj << /Names [(subsection.2.8.1) 106 0 R (subsection.2.8.2) 110 0 R] /Limits [(subsection.2.8.1) (subsection.2.8.2)] >> endobj 417 0 obj << /Kids [399 0 R 400 0 R 401 0 R 402 0 R 403 0 R 404 0 R] /Limits [(Doc-Start) (page.4)] >> endobj 418 0 obj << /Kids [405 0 R 406 0 R 407 0 R 408 0 R 409 0 R 410 0 R] /Limits [(page.5) (section*.4)] >> endobj 419 0 obj << /Kids [411 0 R 412 0 R 413 0 R 414 0 R 415 0 R 416 0 R] /Limits [(section*.5) (subsection.2.8.2)] >> endobj 420 0 obj << /Kids [417 0 R 418 0 R 419 0 R] /Limits [(Doc-Start) (subsection.2.8.2)] >> endobj 421 0 obj << /Dests 420 0 R >> endobj 422 0 obj << /Type /Catalog /Pages 397 0 R /Outlines 398 0 R /Names 421 0 R /PageMode/UseOutlines /OpenAction 121 0 R >> endobj 423 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.10)/Keywords() /CreationDate (D:20130221232619-05'00') /ModDate (D:20130221232619-05'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.1415926-1.40.10-2.2 (TeX Live 2009/Debian) kpathsea version 5.0.0) >> endobj xref 0 424 0000000000 65535 f 0000000015 00000 n 0000103734 00000 n 0001416380 00000 n 0000000060 00000 n 0000000091 00000 n 0000106855 00000 n 0001416257 00000 n 0000000136 00000 n 0000000167 00000 n 0000106914 00000 n 0001416148 00000 n 0000000214 00000 n 0000000245 00000 n 0000106974 00000 n 0001416074 00000 n 0000000298 00000 n 0000000359 00000 n 0000107034 00000 n 0001415987 00000 n 0000000412 00000 n 0000000442 00000 n 0000107094 00000 n 0001415900 00000 n 0000000495 00000 n 0000000557 00000 n 0000107154 00000 n 0001415813 00000 n 0000000610 00000 n 0000000651 00000 n 0000201691 00000 n 0001415739 00000 n 0000000704 00000 n 0000000741 00000 n 0000680635 00000 n 0001415616 00000 n 0000000789 00000 n 0000000822 00000 n 0000680695 00000 n 0001415542 00000 n 0000000875 00000 n 0000000907 00000 n 0000680817 00000 n 0001415468 00000 n 0000000960 00000 n 0000000984 00000 n 0000852607 00000 n 0001415345 00000 n 0000001032 00000 n 0000001067 00000 n 0000852667 00000 n 0001415271 00000 n 0000001120 00000 n 0000001145 00000 n 0000865959 00000 n 0001415197 00000 n 0000001198 00000 n 0000001222 00000 n 0000866019 00000 n 0001415074 00000 n 0000001270 00000 n 0000001295 00000 n 0000866079 00000 n 0001415000 00000 n 0000001348 00000 n 0000001388 00000 n 0000866139 00000 n 0001414926 00000 n 0000001441 00000 n 0000001480 00000 n 0000939826 00000 n 0001414803 00000 n 0000001528 00000 n 0000001560 00000 n 0000939886 00000 n 0001414729 00000 n 0000001613 00000 n 0000001647 00000 n 0000940007 00000 n 0001414655 00000 n 0000001700 00000 n 0000001733 00000 n 0000940067 00000 n 0001414532 00000 n 0000001781 00000 n 0000001807 00000 n 0000944232 00000 n 0001414471 00000 n 0000001860 00000 n 0000001901 00000 n 0000944353 00000 n 0001414347 00000 n 0000001949 00000 n 0000001978 00000 n 0000944413 00000 n 0001414273 00000 n 0000002031 00000 n 0000002066 00000 n 0001034292 00000 n 0001414198 00000 n 0000002119 00000 n 0000002154 00000 n 0001186700 00000 n 0001414083 00000 n 0000002203 00000 n 0000002236 00000 n 0001186761 00000 n 0001414004 00000 n 0000002290 00000 n 0000002320 00000 n 0001188889 00000 n 0001413925 00000 n 0000002374 00000 n 0000002409 00000 n 0001192369 00000 n 0001413809 00000 n 0000002456 00000 n 0000002484 00000 n 0001192430 00000 n 0001413744 00000 n 0000002533 00000 n 0000002583 00000 n 0000002873 00000 n 0000003007 00000 n 0000095942 00000 n 0000096135 00000 n 0000002635 00000 n 0000096013 00000 n 0001411754 00000 n 0000096074 00000 n 0001413139 00000 n 0000094264 00000 n 0000097767 00000 n 0000097917 00000 n 0000098066 00000 n 0000098219 00000 n 0000098377 00000 n 0000098536 00000 n 0000098693 00000 n 0000098852 00000 n 0000099011 00000 n 0000099164 00000 n 0000099323 00000 n 0000099482 00000 n 0000099635 00000 n 0000099794 00000 n 0000099953 00000 n 0000100105 00000 n 0000100264 00000 n 0000100422 00000 n 0000100574 00000 n 0000100733 00000 n 0000100891 00000 n 0000101044 00000 n 0000101202 00000 n 0000101355 00000 n 0000101514 00000 n 0000101673 00000 n 0000101826 00000 n 0000101985 00000 n 0000102144 00000 n 0000102294 00000 n 0000102569 00000 n 0000097396 00000 n 0000096243 00000 n 0000102447 00000 n 0001411584 00000 n 0000102508 00000 n 0001411414 00000 n 0000103498 00000 n 0000103793 00000 n 0000103359 00000 n 0000102667 00000 n 0000103673 00000 n 0001412446 00000 n 0000106446 00000 n 0000106615 00000 n 0000137701 00000 n 0000107274 00000 n 0000106299 00000 n 0000103891 00000 n 0000106794 00000 n 0001412275 00000 n 0000107213 00000 n 0000138090 00000 n 0000138292 00000 n 0000138455 00000 n 0000110152 00000 n 0000139171 00000 n 0000109966 00000 n 0000107385 00000 n 0000138623 00000 n 0001412099 00000 n 0000138684 00000 n 0000137896 00000 n 0000138745 00000 n 0001412618 00000 n 0000138806 00000 n 0000138866 00000 n 0000138927 00000 n 0000138988 00000 n 0000139049 00000 n 0000139110 00000 n 0000137294 00000 n 0000201203 00000 n 0000142148 00000 n 0000201356 00000 n 0000142586 00000 n 0000204476 00000 n 0000201996 00000 n 0000141986 00000 n 0000139344 00000 n 0000201509 00000 n 0000201570 00000 n 0000201630 00000 n 0000201751 00000 n 0000201812 00000 n 0000201873 00000 n 0000201935 00000 n 0000200156 00000 n 0000680755 00000 n 0000334921 00000 n 0000364614 00000 n 0000458189 00000 n 0000491479 00000 n 0000500372 00000 n 0000362840 00000 n 0000204342 00000 n 0000202169 00000 n 0000362595 00000 n 0000362656 00000 n 0001411927 00000 n 0000362718 00000 n 0000362779 00000 n 0001413256 00000 n 0000488847 00000 n 0000364480 00000 n 0000363000 00000 n 0000488662 00000 n 0000488723 00000 n 0000488785 00000 n 0000457199 00000 n 0000488262 00000 n 0000547225 00000 n 0000491345 00000 n 0000488994 00000 n 0000547040 00000 n 0000547101 00000 n 0000547163 00000 n 0000546352 00000 n 0000549672 00000 n 0000683068 00000 n 0000623577 00000 n 0000680877 00000 n 0000549538 00000 n 0000547373 00000 n 0000680574 00000 n 0000622437 00000 n 0000852970 00000 n 0000682949 00000 n 0000681052 00000 n 0000852485 00000 n 0000852546 00000 n 0000852727 00000 n 0000852787 00000 n 0000852848 00000 n 0000852909 00000 n 0000856256 00000 n 0000856409 00000 n 0000856567 00000 n 0000857213 00000 n 0000856101 00000 n 0000853144 00000 n 0000856726 00000 n 0000856787 00000 n 0000856848 00000 n 0000856909 00000 n 0000856970 00000 n 0000857031 00000 n 0000857091 00000 n 0000857152 00000 n 0000860641 00000 n 0000860793 00000 n 0000864839 00000 n 0000861195 00000 n 0000860494 00000 n 0000857324 00000 n 0000860951 00000 n 0000861012 00000 n 0000861073 00000 n 0000861134 00000 n 0001413373 00000 n 0000864992 00000 n 0000865151 00000 n 0000865309 00000 n 0000868063 00000 n 0000865467 00000 n 0000865617 00000 n 0000939151 00000 n 0000866199 00000 n 0000864645 00000 n 0000861306 00000 n 0000865776 00000 n 0000865837 00000 n 0000865898 00000 n 0001034230 00000 n 0000939302 00000 n 0000939455 00000 n 0000882356 00000 n 0000939607 00000 n 0000940126 00000 n 0000867885 00000 n 0000866359 00000 n 0000939765 00000 n 0000939946 00000 n 0000881963 00000 n 0000938429 00000 n 0000943394 00000 n 0000943553 00000 n 0000943712 00000 n 0000943863 00000 n 0000944017 00000 n 0000944534 00000 n 0000943223 00000 n 0000940275 00000 n 0000944171 00000 n 0000944292 00000 n 0000944473 00000 n 0001033862 00000 n 0000946804 00000 n 0001034016 00000 n 0001035011 00000 n 0001034352 00000 n 0000946642 00000 n 0000944658 00000 n 0001034169 00000 n 0001032835 00000 n 0001114872 00000 n 0001034877 00000 n 0001034513 00000 n 0001114749 00000 n 0001114810 00000 n 0001113759 00000 n 0001117309 00000 n 0001187006 00000 n 0001117175 00000 n 0001114994 00000 n 0001186639 00000 n 0001186822 00000 n 0001186883 00000 n 0001186945 00000 n 0001413490 00000 n 0001185402 00000 n 0001189132 00000 n 0001188709 00000 n 0001187154 00000 n 0001188828 00000 n 0001188950 00000 n 0001189011 00000 n 0001412796 00000 n 0001412968 00000 n 0001189072 00000 n 0001192491 00000 n 0001192189 00000 n 0001189269 00000 n 0001192308 00000 n 0001411296 00000 n 0001192589 00000 n 0001192650 00000 n 0001410860 00000 n 0001192771 00000 n 0001411232 00000 n 0001193238 00000 n 0001193261 00000 n 0001193562 00000 n 0001193917 00000 n 0001194240 00000 n 0001194783 00000 n 0001195336 00000 n 0001195878 00000 n 0001226721 00000 n 0001227137 00000 n 0001255922 00000 n 0001256304 00000 n 0001291642 00000 n 0001292143 00000 n 0001313152 00000 n 0001313400 00000 n 0001331450 00000 n 0001331772 00000 n 0001357688 00000 n 0001357970 00000 n 0001360783 00000 n 0001361027 00000 n 0001386103 00000 n 0001386405 00000 n 0001410551 00000 n 0001413583 00000 n 0001413669 00000 n 0001416451 00000 n 0001416628 00000 n 0001416825 00000 n 0001417019 00000 n 0001417197 00000 n 0001417367 00000 n 0001417533 00000 n 0001417705 00000 n 0001417907 00000 n 0001418108 00000 n 0001418310 00000 n 0001418511 00000 n 0001418711 00000 n 0001418906 00000 n 0001419102 00000 n 0001419325 00000 n 0001419561 00000 n 0001419797 00000 n 0001419931 00000 n 0001420041 00000 n 0001420152 00000 n 0001420273 00000 n 0001420369 00000 n 0001420407 00000 n 0001420535 00000 n trailer << /Size 424 /Root 422 0 R /Info 423 0 R /ID [ ] >> startxref 1420861 %%EOF muse-4.0.0/src/doc/pics/000077500000000000000000000000001404105311000147435ustar00rootroot00000000000000muse-4.0.0/src/doc/pics/arrow_tool.png000066400000000000000000000004021404105311000176340ustar00rootroot00000000000000PNG  IHDRoHCIDATx͓M0Fۤ7Ņ(weMԸ0 *ud L i1L٪1F bµjes'Z;( V.Eo Lg:|jָYWezPQ"i:, ȅ)_I< %Gqʩb ==b(>{|S G_RU+IENDB`muse-4.0.0/src/doc/pics/bad_timing.png000066400000000000000000001030631404105311000175510ustar00rootroot00000000000000PNG  IHDRi sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleBad timing~Q IDATxw|e߳%JB@ JW@gWN{og9ٻO={o(:"M@i m)&~|y yyfvFBx'6iMI;i'vN#~bLK_\Ѣe+@T#{%EzxcۚKwmI?<řgOm8桡q!ͼWζ Cb7:stAY{PD%guy%5Uxx?w爮w$**J΃92JzdrAIFzwDq۸6CStR=;ۃ&.).mֆd^'9L"uHl9h^9֧S}d7 'DsJ7J Φ' eyreϡx̅trV_YxF)1VweUu jql. Q?[;quiCz|S9[hE Z( 7) J(ҀIМj-@'@w/ Gf^_43l;L.mZeIGG nLۅ@E1ͩ [ppm 6Ҥ捶acDI!IP^^I[;v P̑)B7&Jj8ϗU iE*hTUS__Omm-|hޞW׉%uHyV6U?[pm %) xSx=$sXmwPw.zRRS:|RTƨ?F/>A%F{c.I|)Id2? ԱO7+7ؓC'ކx<֭؁.[(Տ7~ZZlI0LǕ.OEO |'ӥ@.\@#x`d \EKX M1xAވBV%?t{ gOO nPBgIhPZÞb0dZſYΒ"''KqE 2Nk<'0v⹁/@IXbr0hφ3c90w3hg{.547Sew"^<g :t@q9~[¯@N.[+*б]ظkҪe I)lMT$˿-xM[*/,&RuZv.BOi١k7mbo3Rl{P4|&s/ XhZQPЌ^HIbBT,+Ofѳ1v 9A5ՅbH.aHL"/7?>+nyG_]K3pV[/qKsY$e!X4J,(uu(B=(--%LZvCzMصKݰƜ0: Dnz 4m2OOHy:(폙'RS9O‰/$|a!2Bd-\r|Yi#YG{t!Ćw|1!2 sFms^2I~b7k_p9ꆂK_(!KSG]l$|idr% JKKѽ7i/h$Cʊ ʊ EG^wF=eZ{ߟ~ÎGm~O)|1/j+G~5$k٭IQee%jbG]m p8BeecFrm/ޞ,8V<=cӁ:e٘fG=71L c}ٽgOI>}<&&; ^$kٰZ+gㄠ;.s_VYm=6>u)ͭц*g.ek:sMBq)_Vt|jR&CI ϢWYYI~A+_m4`7ȍ_Ou]ziv~$*ڮ/I.#scP%KnT'KJ^Xx cKVtP+*h&SRRB}]5X4Q_[CI7& TgZ8 TX-zq]:C:` ߦo4ZD#tiCz*f3߉g}r^&i%=SVpu"Ru30V~m`I^=3xxTorL9`fۙSkeIy4Z*Nl]ylMXvRxl rj)y /ݣxpR6a;2Mԙ'$K6 )~K\|TVTŨ+'t/;%I*,á/K,SNe̙h`;vz^6FcqZ9#@QXd ebT!hG_U2z;TX<*TC~TNMR ]9A(%oSy$[qTC9e< I5LC&SѲ$D.cKR6ƌয়)yAR-u! ~y8tKa/22҆,2kZA_dF=*Yi;HzU<{.NSilN$[3Wr!K#d YDͩ1 ~UUlɧEu$I^xejk2/^'VlEy̘o.._]gLĉŜ1C>lnߛHJV{w՛Kڪ{(U5L8y,m% DPUMWbzS/%1˭.usc~å M#ť1P S45UqܳF@io~+ԔwDh}3r =~ac\ jË9Y (*xR/ՖU-s >E[1ӥŮPc$S TS͜kg*?L@t/"{\"mJ]XWgi# 1sљ-EҮCcaL |*- !uJ4]Z=[`;>A6OWeVVXO=͠ASXz[d𫴔pČ_ 6N2skSTԊ^}U~ǟp67=z0<%zQ2@z%5e5ys5{@Nm -[O,… i٢%>EA&2.<^\d$6 P1prʀ|36LGf1A3Hn*c/ovYR>*j5yGX~(CZ?"3*Dz1~TNqH}lqXS~6WUE˗,ǟ`_ز|fjʶƅ38p@^MjX2o7`ذ h}>[=zjӃ~zP,1m[SԮ m}f"o-G(O?&RP@qQ~? G*\rz7΄O/#~LUMKt2tv̄E_ 6ap"0e7ud#:XôNZ+$2؀%Rd Q٥`6 ރ}3 yq8yD:"7g3<}_/ŝ(wa0a={؋V>_xO&_2^ዬ K;\\6ϛwu:p_?hmLql[C6Lr-}ƗU0'L`gISn "!"s52rw͌V&?uS yq \R;DS\TD(?bï+6o$||B0t Def֯]C,t=e0t0,"Iħ^vߋSFV6Ջ?Ww@:Y?Ю:~FM]v#+*׮]y)g=v'H*>`^`^ w_:v(όć =U"$yZ6ɵ%LkJi$?9D5d9=I騒8fG:Սݚ\)>jx,=/"/ϧYTb1*SdǯhՇk7_=\mϧ.J߾}С/"wGB vFmMeBeߣ'2[A]zy6^cYf ˖.et֍6mJEc(+-U(.*GϞtԑ`0ї}PwsvGlp;oGo#te%肘]LZuGl؏r"v, ON.i_ĿO+,J?-{w̉=îEA˸lv*Y#e@֝l ߙ'`eqp̎Yڅ-!{[kI :W_;,>8k֬aٲeظr%EEEѓ;8<$xnGxS=v4G·§G00m>WU7oaQ]UE< Ь}-> YuWe)ng7%ϓARp& qdm< 7mOٖB`(LПmǢēnaN(< ـ=p[hp7e~ɂ_ۧH$ؼe ֭Jmi׾=E4N(ъg>j(pC'MÎY3Gيtۣ/"TسESҺ-k]' GkxEA{O8< W)T<*H۲YW7neodroH,=ାEA6E:hөdH>nqx,'  VW~L}+gˢ%`Nr6c"l|OfI}ps, [(r N"l m>KdVMAB侺" <qz-(oSm([$otC jnf^lg@];\ y9u& r@ǝYaI 5ss (Uqp-klK80ΓW2*V Rd \>gȒ4Ra3_1*|Y&Xe)}a/1h##`}sպ$Ʌ:S2vީf^&ɀ;9]% A=vrt6߶NBΗ0-KUr;;H4'wGb0ۄʛw;vF5{}@AKnt7|㩂 ?uiv";蚱_Vl|SIb`gaʁi'A*c[| c[EN(Vm^6IpCH+Fng#e]E6^.6=g]6 lK}8`גUv`9(8cm.M SC>#{:am i[=Je3 %;xۅUsZ{fbٰ8wab|Z3?g1+=MN5ŚtV2'N&E.Eqp[|xkY'8nԐ-Fڔ)uŅ o h(g="2R3n}ROV1mA]'->E=3!rU]Ю#չo`@T$2-yW*n[_ s:cr*,9;)5,$ ۽\-l]8K dr{-EE+dg^&]*a;A;<~Ns_A/x3"g@O'9"p4 dDq1@X3wY$ŦI5C}6:)i ma_wK=Yzm9m,kལ? ۘvx~=oﴊwRs{3mr`ZǕͿ}fY7x4!Iܲ-FVYm5 {>p[ R+.%C\гq>H` Y8UB^X/H5b IDAT[K3?U5 :UQ<kMe{*tuh4OBr㘙mg/Jq75.o,nrk!nM;FPC?w5cOL,c]zp3Id,۞(Xakzڕ)q+}'xfgP6qlzW_>@6 2ڥ=Y6m6rVn zR.iݫ 7D׽gxRcX@ah~Fdz)/᠟9S&c۷_it| pP0070;MpiJv}sǁqWi9۔6rt|C^'Sw`ג8ʘ\Zq.MlZpo.J s]X`@?1*߫cToII&;d<-^~E&&[#6&o6bn5 Il sv) wH~ZKh[Yu%y$#1L<\o ߶glZ,}%# td-~w$FtT4*m>9g925kA=sT"Mr}6V7-?)JE~Oï]8A4\c$&,2g}5Syd,o &k(Gipf@1GiXZuL,){mxt<1.Ѩ$EOrw;ȺN&zwl xzM/t>ɽ>CZ;=z/> `Wp:)awqwޜUހmq;? ed!-@Ʉ#7%x}8m6wEɗ30c ̥K^'6' ށQƒn=6sTܙ{2OQ@ @ߢ0wַp+u~Zճ ˬ:;N'(>~A,9:ۇ<@ZkaysIrHru-RN 7׏IH)LS(g7~i1oPimѺMzVٴ| ?!9trdR'հܦv^hG),Q]tvMC4Ar7rw;=WJ>y !r/t=B^2z4S(q mG^w5^c&f#.Na~ gy r@?!2?/ѱ)f6Q,".1;&NHlaa>2\tի܃囪l1^6O>a1psڢ"$ֿi0th>syOX| tv•Fmw)۩s4^'6%!@!Y{9y?Zy}%Ldr*1 /oa~{dy#b["I,Ѕ#hG8O6HUG! C6 h-3OT=+oR;r_Ni vu5Ggiɍ̚dK$t`6=inG^ [VnFZŖ{Yshcg&"j8JWS9EM maZsC{zp}F|;8$>j&ב\F^rAu>Q "0ƭUJY4qR5ӹ˘^w{FN=t cƟS8a~ƌ_Dt.9Rmۭf:Wx9z4G_}&pȡsp0]/HP7jFS[Qf<{$9h47^w\tELIm?N/jӸrO4m ?r>R_u,pF?쭪^f5Ӹk[=kieӸp9 fW$i<9qsuLi\0MMuOi'ʹGC_m .z&;8bc=q{z ݧZ1/?qc1[oS/?m.ĸp+3,z&=.BL8>)NEQͣF?$_li0اA)f@?g}:~f.ڻxy7f?S/NjWɬ{gV09u+j8׽>/^O>Ư͛°k1Y_wx>%EQϢ!xR N۞k{O5.*f?(>ygj{w(YONE\j@?޿}íǐ|ns^n*f=*yg?'_~#cGt;j%I6y ͒ZkYXW!W;9>3=2m^W0r$7 ]?u?Uj5yF۟mƻwPgQŷ?n ISP: C 5?r_悗y޿F 2Bv$TI`+~+8vp83n~EID!%SԦOJG>S恩S:u*SxKG}X ۽s7$N٬V 6=9.'z{V5v(*f=~M.򘞟|R:;W݋W/KVm{O>{NmƇIJzNj~晏œ+moo_$~>d1<1ZsnSm;cw|#Ubƛ^y)ue3}r<̺fdd(΍/.a 샚asow<0f1= Ų5[w+oyWȲM7&  S+в w@_28Yg wP\T~_ _Dts=O9y+rVNPF/YYo..# ]5x7 ԯYq:/$)7f);ѕ|߅f2 tYlZۂ_̎ө}sJ~h6KVA{r Pi+Jf#t*jv;G?/C/܍#m?. #֛&|:k WKg(Mz8tΏPgB?ʨgؾ_~Ci6+VDcd+|}ne]úET%`F-MڏO؜`>aI30Kqık`_rNLC Yn9q:t[3ffUD7.>tu /?{4,n:Sо,zW+2N!J.[s iqġtnMaoX hjvz0ioo]ȏt` GkHq'6|ԓ_Fl\F DkΣD{HǠ {l5 / 0'47$Q|! CzLIi}Z&?O?B Bm) ̲oW_o7>̃WNBʉ(@װ.@aH!P[M־)P "ǓbC|?8SOTl.75*?qgy0D0S唨bcn:@nڡMu~"EGZ~°=BDs񈑆PP˧e*J)W25fꏙ&wyL8+Xuw$|]99sV<<}/aw7H4>1ˣ*'I=' QD?_>E< QvsH{_ rR [^#hW,)rȢuzM'F*@|T S@UʽF!Lޅц0ğ0hk/g zx>ؽG*@!@( J&jA5esy_*zWz4f,gZig\8ħ˺VL]ۑz eֆR_>JcHTQ^6[&l1AK ~s//ǭ1VH{| &Pڄ> hǪS>3-_%첉j6ƙ7EW^f?G.4{p?V^Pr<g$ևۣu0e8pq_[JP|mL,.:y~~T!=ƾgҷꠇhZs>ő(kf~NJfz<⡜P΋nfgrM ?Bp:̇֒#}%Ԩ|j#ETI;SZuÈG*=g\Bh(eNm TTTN 4 mE+j6QЪd\b3]~@:?BJ}RgT=1>zVwCfhڭbSr#C =ľ/_A߰EAtT{Gڝ| "'$%(c}E6d$k(Aq@F3S >z>%|'Am|͡Ş m<})XQ2>zQ>#V BPuUt_:Kן>f#E A"'k[z 傇r~t3>+nx}\F߰D1YMYUЏHԣP!iY7>rפSYD"mbOzϾl=zq>VιZȒ]GŔMPiպ/<|Ûe$ hٺ/W onz D}DD&\1FmCגc5/k|ZUݡjטeMzfD(rU6.zBmi"6%/OP (/;Dn+ݯA(E(XO4 W@7j"=pws7MC]8h*r9ڛ787eU|9h:f&ˍsȽbyZ|u'8b»2VHT⽫9nKpg:exx_߻czB]3߯v=3^+XW2k7XV],6dnSz_\& 2wAx kOzLzce.Oלj7SHԿfpxPDɓ%"|δkMI~8Қ-,/){uJ&sɘ[[222h,?=(Y} õ(s.>QÚnc)O4f%S[c'-F- "Z'TW+YWSG9.yZ_vWsNM0V5ۋ@+7ď@})KFOR(IY4t5:& '2yIA\ 缪vc1-H4QکwqO, "oW䛙kc5D{.c[5|^ω{Չ,zy2o%|2~ \l5~N!I@-'7#81IH=!˥6Np>q_gv'MJN{WVx @(3qz;Ɵ2?J)ؕI去&b~9lt)G>TEQځ4W\x.$#9'B=tz>߰P!tt| <7ɸ[̙Eǀ%N['P МAegutWS{M%Q-^0 /x. >q = ׉,tƦlpj(ds`1M: +O[^4)~/ykļnpxns2GܡӨkb@AfE<}{,NҶ7m&4@=$v.N9l3 UFϖs3Xi|K߻~]&^֯Mn` }% ooO~m^N&9ak+N ڐ5;Guc%㮳9*?zU> 딻)9ޛEkK'Od-g1>ί;Q?%Jk9ڤt(-{hK K.[chyku ů@9'O`尅[;ӯV˖Js5?IV/'98 IDAT;.⨛7p'^z<^gwLxV{M+9y o;#~J`9#1'W@߳.fm2/uXzn&ɏA>8cЮtf3F>ޔUmZ`=*I@ը%Rt!}Q?: j}~ sC>\O] zAwOMf7+ٍL璓b‹d^bVoq\t nMPW`/>쓿M{RلwKpR iXFırJkwm^r%E$eDsU%@ jd [ v_z" ("^A%쵟90(V6xR=_1kW2`0dg=R'R)vNl)k^.&5d.*?8J:!QC(p( .]8A$QZ_;ɚT~A}q_Jk4qHo\'ΩqۿNjv?FMtSյ{xn:ybYCv?'uP}pr[bڟkxkwBc!c{,iwXOlB ϕpݮzˣ l줦'u*6?cI!A^r-Cd(BMw4p׶s"bWx4c LwMineo}o;4Ɇ[MO\T`TCA{_Q|cHD{BAĕ$bF&$`>vUkwGs0=YΜT׏6Ў} #Rě1'G[/SkڤMWBerиc9&7\n;6mw ӶմZ ꧢD -fi /bh1CjBG}z T"7D_շFɑP+_S.&4.}|cgO%\3&KuSU'rE!M W;󵁀j2YUWS^v>D!]Gy?^J=1?y?_jn<Pn NW}\pp3 rkDp-9.AMsWm4nm6 ;i[>oȧk) } Dv_7Dx jx_%P-Ь Z%B.3i ´g(TVɮ÷΄!5 ˰x?rq߅t$UTdVπ@ѷՅ3.+3H~#-7 B)!9CZ3Joi}tPRY7u\^欄HE*B*#Bx$ɓy1ϟDB[q4Wxz8u૜O8q_v hL*п&|ZDQOP3|zD#]V:p?L">Lo1RAiC"_}m֝Ui["oz nִ$FSRoSV[j}n]xI)(_whVTJֵv&X=$oZV;A{'?ާ0^*q{*>pr_ uF (c~@$G>![9LN 3_&~7_7x~鿞=ߕR ~5k#i*I}:ҺV~9 e@?ZIP|4;ԧ˅A 7o^tzx-_/uTO!ڢIZ* &fV#.87Tz]׏K9cmtAt9w;sՙrӷ) v+_wl! 5@{d=+䝴v@rߋ3@GvB`rSI'3߮}(Iu5_AtUCaD-_AR-$mDOu Lw|ERPDz `o}_ V],X@PQDEtI$J 63<3;D"H$'.z|_G3Wp`va^=Q&CuE<FoQ&(`sپՠNGnƒ7W6xE*B;'H$ɉ1^CecEqi܃ UWAi/d^+9@P4;poA=K?MM`F {D"wl(¤e`["F{)~d@q.u ^ύ>Iɋxj`0K՘S|Z2K$Dr ~/xBv{H. })<"0^9]x|(:N h=//echp:m(XV65ŧ^D"HjoK&9y!P)ۉ)Wj-3fDa6܁w&ЛU<- }qC/|A_ iOCPQ;]1WiHyrvo8o*Zuq_2pʣ6>N@ס='Vgjr+&kx56zʮ/?nqVZ_ 6|J嗔ցI%/x{_Wx;D2;63CzQqz-o|(l94郒k:A9J}݁ėgzl$ج.§Bn&6b "߫>4;yE'QݞlqOφUahnyY|((pCyb>COAmXTO*%8Q>im,B W0EAQIUʊ"W CS0i;Љˡ@m_ˇ0ky[R&~00/qC|F' (^G_K/f2?L~euV1\z<." 9.-NK/Ubmo 7~SG3! 6y7x+ZʤQOh=O*-G 6+gC/F2,78L5f?MÇ0xx,>Dw8*-ew0ʐ@[e#rg_>)2òoж{d潌0HT vka )o@p+{݁`~!=5TR2h}?dTkSqC_2HA,rrPaLWq;zoL }Nbk0]||L͓-7kBZ&qX/C3?坉mYc_SQ|Gxo|vwy53f?y*s^ҿ(dŴAf}AMeMGd6G,o0{/ʛ36P.x'#_/>kO#?k/}?lx`YH:T=h+^K|:cMy]")Zy~σ?;Zf4>ىb_3}ߴfNgz}.`GwجY<ݼ7= ׿) %n"s-(e.1O,.1߽xS2hǣ:q Qt pP Vg.Ԫq7eS:CFL;`ܴP]")^L-tD_4܏9'} |*C"-t?nmѿO3l4.Md@%$ާ?[*-9z6'5Ŋ.Z=UbE"_U'0Bpj%٬qkF\%znlSQ3oܱ3e,ǜ:J;cO,Rp˳}ٝgߣhoQbp>KP85HPn^K 6ry!IAVtr(%>j%nc H ˻/}spLWVP"Bģ~B+oHv=NR"(ka6WDn_q :(F(#_Vnv1k!vnŒQ_HO?!3qcJT /Vn^N vֽ((l@AthE:bHX~^%!|h(dS\2X9ו"L-u;l|`cFߞ<^t ԱXPƦ\> h8-zkomt>¢{):ٿvEn<үgGXtv(`)tb߳CMo@s;`'m6:G):*pE!byCR$"&~eӕw_.IƵx: );E+oEEԂ gɈT z%"2.qBC=UbE0us? =Ak$DЏŭ7ͯsP&khvV*H8,t{≠WéM*{K Xc<a ~tXr\zaxε#!ܯHPiLgB~Ї+C}`-~'y&$Տh77ѫŽEl$y֯xz_lBg9A?dU'LI9R}wiZ_>݄Vx].n~+][4"I$P9~v٨H$ǒG ARқn·u̘ҭF>wmzdx2JȂGa+ 8#?ŻmƛpД^"8ͯcqגּ釟9tiHWH$ϴx􂰍`(81=vWuKck#4 _\׏0!vcjz}JCٙvԪ5>B]BLm=Ͽq+ UO?K Zʖ^"9γj}^>buǻe+1c3T؁oG].6I76#e{ئn-p!Iіat1?-X65ƣe^"ig3Gړg<*I$tL_j*v"_:|S>#4hu  :PtsOh { |@gn`u^D"HNL }Շ|EVZ c² ,Do}ۇ\'JCzC ZpajmD]e/2i5}{e'Q)oAE/RVWz8C/-(\/V.::zsVd5kI`26 /ݟr3 .xZ4/ΈGqD~"9 (r }ߺEc#DPSB]4P9}<,cW+I;yE!s!TWe.槃9raqfnv~5ZQlؔnL`E9|NG6jp6'ysYw!mc .S-O>|>1 g;f {/x%kѫ0|Æ`dݑi*JĔ}9{ ExvcACln*F`xeQ޸u ~ѺnkY_N%9_ktZ/dp[ kk[9|$<4UZiyO_kʗ6>7?JE9~>AcvXD 9N_cĘ;y᳏gc凱ÜQ^V_EW- 3Gs3p }b(>uboLsWIÄ(uͨ|HAe0H5$YB~&k߶eBHJuRR %R0HQf<;bH9xxhjjl^;CKS>ui̋|q glq 7PWy3>&'9ge2s0R}B͜ށ0} ^meԯM7⃃I7[%XLv |G csGPy:D)g~Zvfx~懯H2"T]G*~^$~|'ͱ -c. p86]wfUZ@dE_?BΌUJ:>,6츑3եu~MlPßk sۙ6YfJ!<[쟦wIU:fEGv# yn-`ݏs765o]{YDэ0ŴDSf ތd~|+8VowdS>.Ue ƜGn%>(ѯ\klI+1uz!^]^#ɈSy?뭏[hq)(GIXu=''sUWVG-׎Y%!o>QV }<(COX|Efmn ԎFa}[}c7۷ކFw}}==>%cV(2Pi9qB9̋n -߯$ornA٬_I^3Hէ"΁|$=]|)Q'\ON &тΕxP5lC$ު)3{8;9gFܘwu@aU=jZFU2TGz ZYtpةl Ԙ$?sէlN1 k|8_!Yvqx#etRX*;9N3l~9: ×3 S5') - 6L:bpbDm(Kb>E~~nP{.@ł&hݥ^FG5Ty~!'ge\xv/.vx]JOn|'7c[L~a]^vdRrjX){D[/s1?gb+#=(sѷƴY\ִ:_&s{EiSG&_dB/܎ϜU'l~ߊZ-M)gJJ=[ϞC/#ɴG#d-(do,]vx!A(’w<^N_EWB*(׎ }:?: P:X ^Dɧ/d+^_K0۹(d) ˤl%$J~A٤\Hz!9琯N+<P6 ،mU,㵳k3\p=F>}& ~`3p{3&K QH4[Pqx եr口^bG̶wChoUomeg ~una%hkL_lSvF˹ sZKqrU֋&(eAPk7C"2CCpx }:[Mu\q f٪Xdb;nN6noqktlw/ReHg@BsGXvU oD9 оSb74OpMߣ"0 v B1a hTL!̪$L.!(( t"6|77o؃wGc}ƌțXwCӋ zә2^~ 0`d:YAf(G>oO|i-ҝ}${jkVIc胍ї=MϹFOee7>7=iVFtn&xM !"^}_|مD%pIeܜ~w: `A}3= kEOz4=Bxc'&vWAbDI3o< qyI7gD;Ϲ#.W`+r 1`!4D3Xfp]Y-B1psOsw 8gb݊r SjK\SũcB_ve.ʪxIpo.R7s}N^;.NǐFZ|&=^O;Fr9djkso \CФc m%^aJBKi<S\|M k_Ў7X[_ȭwKRL;:^WjyNM]H~NM$w^ND֢?ֻ߳|̍;n9 FYhwfmZjvCOO?%OOpBpKzYf욝_Oas0wjObZr"Յ}%'|)4c%* ֻs)C$=ÅS&J3/x$z;q߻$=˚Qet_2O)U@)߿elYIj8t*3.++QIW2is~=|[cG^:XHFmSeoz\ԓ̟WV9]j(W\aIۘDa8쇺dJbE#ÃoMݱ3k<=o|.QL1ge5ѡ plg<[<2`>ZGO1zYm;{?f'oߞ~df|04Ͼb3d&QٳL/߱ӹ?zr[{G#L`OovA}ƨ[ĺ\{XQؓ;_ ѻc+C2ҍ߹vֶ(w?E!-Qtuackxh=Fc=O+b|x]g/ke&DIx8[VƯ cU=cSY7{gg«q/ҹi[El#2ϴ'rpEx'@]˦'A9s&gZק :kqc!P1L\Vxe - {to`AnX.7\py_l4ћmϽ4jNՓҾ'M34gԋr{@3QCgzf7l0hM csν FuG3o4_^ٿrӘQ+Hr\|I7ZBBTgaP? -AN9?UWcSwoJr5TN$_THՌ8=c4]9DrVƯ cˬWt.'&vE#>+q/ҹi[-Vzhڈ^]T]JJ R&zfŏ9c%w&uHjUBאsWfs?S1q e oyiz08pxCȔbJb)nv>\U2Ä)gFʃ(2i,\iܨym#.4fyޕ8,.=e ( nS496M4VκB_ >ޤM-Ռ+_GA<NJ^Tkౠ3tF87"7D{O!Yv= rlIUQ0& 2їM|0/}A7%rqW]Jw{;׺wyYtzFچmIǍ.\{ > zޠT3ImٻyUmr=pAf<[ =썤[E;b}cԳl,0OQ=.W#ׇx~~@UOhkXF[{ulȲ**)yuR.;WMVu]j&~#H5v?g^B;[Y+v 9Cc½x7xv;h?]u,TG>N`lJS {~^'`?'MdÏ߯%igϊ8))}ۨ˴ٽn;yo.;rmՋm8"IH2C166_o2O ozGàQ=%t#x뢘߸I޻kq;3Y\v)*E~/A+8TDem }5T:t1njD(s( cbxcs#ҶHuolF6E,+B!V~?=*RC)T @μDg^Lg.? t_7ק=uo$!:X@@1S\Am.:c#L} N_/T/4/NK$ m.޾;d#_pn[8 ;VcWֽeS$|L#j?93"CESk.՞W߽kTN;koȈ2HCK$DR[ȳ6b9t/H$ImDײa D"HNV sl%D"5%x&D"H$rSֺWg%D"9y)y"D"")*[{D"Hje޺/~O%D"%(A=zuD"H$-D"H$2ajEH$VPeV嘠7Pu:T֕ G"H$d޲%[ڹٵ Î^ʷ%DR| FNkuU:&tzNkӖMztMcD"yХg/|^V؅FSZU$''-#^6D"|>)`2Ro?s4Ĥd) 11]ܽ{91D"HjBnhBh.`v.`T@p]6T?a3H$DR4Qil4l]cM 1ܿa\_-} gIAȱ>~}eߺD"Hj.B}fv.cѩK[\^);,a3׍w}@ 4Ad0b= B}$FK dZD"L8~NMKK~(06KC$uxE<շ{-?S9Ŝa#AKOh4@8f] vcLH$I Fc/럱ߍҪvQs8ܼ}lٸocO&=GӋfSlx<±sIL;؎is[<:!D"Hj0ŕrZM߭Є15,ᛯceѧCr\Y:Ngm˸1]hC3O8=w9 SN6D";=r4KJ&cB7f٢y+8hy}=A|7O‹[b ׹X5/7^S}vWUyV"H$5x j[ͧc|)JIjT7K59QD"Tˎ]ٵr|2NmֈmWGo8EZ3_ j#H$?ltTJ( 9w5kv1[dd Q'ߺH$IMCUUlV ˗-')fs i&xSB ˆ^"H$5iN|%SI$D"OV:WD"H$hU$D"EZнN$D"9)P D""H$DrRf^"H$ZD"24D"H$%;zWΗ#H$Im/T,*ˑH$Vk=tH$DRM`uST ˎZD"Hʷ`SKdpi ]40㥣D"H$8)du.4cS5bafɹ%"ף;J$D")zӮWm=(pvIENDB`muse-4.0.0/src/doc/pics/main_window.png000066400000000000000000002570611404105311000177770ustar00rootroot00000000000000PNG  IHDRN%sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATxw|UǿR& 4)U:(&v]W]^pk[u ]tQwD@zo@(IHf2ɴL{<Dy=wέsy 2qVg)MiJSҔ4)MiJSҔ85vŽmp|A}lv;v[4ȖX'e݂MQܚc?ML%c8dfruH܊nvZ^Q[DcjG>}F ɕUmr1c?jKJ)M,[8CTzym[4g~7!\tP Mi:dfuj ac]eJZYB[KY*oEaHc;ƐLГ :ccO24CƓD %gdkj͜sQKF:04iqS]{y6'@QU B fiJS}? NRUb*f&"&$F䢺j`yH[1(O*c56&4[c%v$+7Ԩh<#WUy:K?,M9ŕSErJxș`=}1͠>P"( H i[Lьԅj=R:(cOz`gdg>xpeK9UӦ8p( 0 \eR$I~*+}HN'~?>?? |lGk.S?2e%S(2js* Y;5`^s,A􀁦d'K#]πAeO1 ѱ ODHzZ YiVu^XZER4qkҮxtd}cav!>鱎B"n/ ۡN|MIYd4Bڹs$C.dOI +g~śYS1RE~?JJ9q\ivj5lHfffJ4|ر׹[]VJt [(`0pSa1@OEYe:νYA)yq`$P)6yvDavjDVVjc05vH@G۳[}l%(47Yna 8@T(y眧#HӯAG'`u?Ț YGvTM|${'@uڄ/K yN;tdhaIGjYW9JdM0PxNAF$jp x0zyf1c5L \2#4!$AY4!zsy~_12}֙T¬:uGkTTx͋<Q#*_%Nl7KiJo~c]e"U[m 6)oMjicTA EٛF<"CKp5DaR uz*Szq˅@csj(U++fpEǎt؁.;йc:w+3=#PNU;ز# .ܝ}ULX/BZzv4쟁z .k"IX8wݕv$0`0sܙC.l66mL'#SlܴѧMkǮMJDI!ט*/?iUҸqcJJ!IRLSu(YL2X3͚WߪkcޛEDxNy@(xnSWAK|< xzb]P;*Պagwc#rQ!ju%`P0,h'`H:8'><ߙh+d\,0Dr!bR)?ZElt!N%.ZKYCC TWQ" +giOI;u 池Kd]_(4\}>mY*T kп0N/9cc8MimOcv**~am)f&qe{þ{ذayQ|<_xvtꚞpQ?ہxStԑM6q&N}]ȮZKqÔᢺZٹc&&+3r)~[Eʣ۠CZL.nS!ݓ 5%{<Br?a֣<~qR|?:Q_CJシ#i䪾W0ʾMY:ѵS'4%B@w) S2M/dd OW9xgKL^|/-TLޗ2Bh[ӣ=:O:/P#N&>^݌r7~̂HQ^z4N('t@Ᏺ~?#ХcojB |+xԑ.:ңZ^^R.]+cGѻs'vΐ '_,P2g(wtz/-=5v {t.]8iY&e(F6tc(`6ɼLvJ #|3"^`RGUI2>QG'tDt3u ЖAk uHHӯAz> ;(/'Pr?H'5w%Ǐq㏙8abvVf&?M6qWd>S׫,0O?@~^L'}6`M 7lDHճtTQZۀ 8YG 3@H@0I .P~~?R8L0btJUÖJxBX!!K:9<, 5U{t"Yf|h1|6Bͣm˧1~\~sQ{'/)H<%xP`AyG'ϺcD@[8k L\ B/>IQlDQqrƄ߸įO$`е͊9dƛVV™۞M՛şelQ!? .ŗ$~JeDWՏ?'|=V80C4Cvq^|}> \]-ydF6nZ'W{l TŒWW~cy#ea|3;M_G"tVSڔ%aX$曆Oe1^T >yjhz r՜]dm K%_+ TuK2,]<v݅).u @H)1NtmI~r￟@0{,%T)Sx7x:t(|!| ~YWUҳWoGדN)DYYEZnG*R0&?U@I:u:u%s0K~ѻ]7PH KNO'j!bj[tcYNlc miC>T<:|uu BmaSۨ˧SN[%*MQ>ĔrCcT/Y=]n=݊NlGx؛GVLmwnb B!41>f}F#ĕO ڱ G-gQmG ȩydE^.]yPWY#_\cPLgm|Ƥ)κ<:'Int2h65m_ H[/F uS:URü;ԯ_kP8>WI'Uc4o֌۶_> {q/ZW'mV;`CG kޙ /rsNtԉ6aFNlSYW=h'Evv6"I!~޹Z'IdggiJo"Rٱ!dHVIMڥ|*2~k `ȧkζ^çk+c>a'TGt,eKDxDh@M"4d$[lLJW$?@c-{Y_5d{K1.[@t+S$[p;pc'c]X$B*UHȟ b/3C6x${%UokOV\- C |noP-󻳙NyZNjnn?$/3tM]/IN]rrAQJ KaP(b!_矲P]P8em=7Nz >aSS`PwTWVTfͼ[w*{vpYJ>ŗ\7; ۻlZc\>Lcv^{=%l@NT,9 ;afϚ)z6Ꜭ# Yf=폱d;Xv-+L|OEp@&kr+9nxÙ _$K7Su!t3EVE2w,:0u$sd tCTVKH4[zdbz<6 )#(EՂɗ( Hņadž454<] zwN6*"#{θ'_^D[Π]T"9=m/P sc ^ν^MsZT3ٽse)z۷lb w]lٸ}Ͽa-:'ΠASvo7h_VyaA:wذ|TSБp)((wZ'uS^nȟW?eVqSPPaHS~$ƻ'GR J(kS^2R _ګ3,,:p2EVEMMQ(2A=X!˵.BDyt0d@bV;46=>yp ZRIB-' wYAG@Q-;1j_{-,u3vI\[u<o|nn>n+>û Mؤ+6"1Bx ?l#~@qqwO%n`6N}sy/㖾0 *1ۙ6}ɉ*<7I U=p3VoOy{xaVv?g-ި)a(Wu @Nky%~u:Ug2U0&h,#ϣ`&5 ՀnE >XtP M'2IVIWO~Trp1 8xHے.|qi')_(޵9_ N X7He2Hʘ9B|cy=l_a6{ľ YY8y@PBddgˇ# úVsٌdde Ց j[lN\.챘""Ȍ-B! a:N& ; @P)R"f$)qf: e9ȐJvg&.{@}bf]W#}ymq\_?ȵoqeftD0E83dfgm5~s(P7G҉hj{u[X6(j:(I6ЀoMGiIc!Ջ'QP4moe /lټҒRڵkG`q1vaA;te Ju DM:qb%=JAH:@NhxvnkOi.*BUھ7>8q#G)oM ^KW]kA~Q}!CxgsmX@?^\ HY[g:`@@*f>%4]:>o;Oᣇs6SC3/dB!JKػoxBNrhҴ ͎oNAANg)M}:1_yQ>~a5H3 V%QT5 x4P\f)NWUNZdT;FG,2p9" `XٜddG8D?ID1-VN$XbHcI0*o "HSH۶l`ؼc?Ih8묔*&!`JUB!N'YYq\7iJAj5pjsdSIkPkB ^k dG6` Ԃ($S:Z',C8h,Ȥ8ꁀ(O29Q:̔HHR5piJSJt$}c;h׾3?P#fAFF_iJP/&D M"Fls`ӽ4JG!1o\,)Eq^&4 ,SëŒ|쥅Uc/TF0P T؄h0r^j@cqS;MMJg|Kd2SScm{n7RVg \@}t <1 'njde@ u伺ڪ4ӑIy00)4)MEP6l9SN5Ⱞ%#Z}*>| IDATVmK_7R76*@Q%ʟ7.X蟚 _BE\Ro*( d=y"?8)Sb':%ฃXsl!(`OөU~SlXE+`N 5_) P[t@ MiMS47K (X uG&B^{Sꕻ"{:76yEE f§"d:'⼪W^Xl M}wxUwIT6ȩmZy=:)9} 层TD䥵Bu +]do,oo"\G"et@芚&u)MqNt` MiS,@Lt:)LM^N6 _Klhڒ bW R4M$}i6}i@ůXR (TPMt*i2~˧C4\*0;W r<a;f>`2),UK5Rp-H_ ^ߚofhݹ 3T$'S'cqISTSJҔ;d9}d-(oBe 5D*N'' O (1N$ (dԆu ~86 n2Y+VX>ZbTnA \ Z@`t0*{ em:k{)MQ$p8L0-w\8v{Ry!!ݎfK*_8N-(y;ȝڔ5/<ȀC95sU&ASLdN\P 'ZWiCgO$P:?)`_#00 $|ә9)!v:`}R~:5:A1^FIy/&1Lrݺ &?3^Tid誗iNbE.E+Ӳ H c(^2ׯ⤱[N)4"Y7=Y&3YtmeWgf0`R0,ӪG/qZ~.210HNU.Q,7+R\Ҕ? ӼEK^O[\#kVrrrir|sB_a4kNvNwndgP(Ĵ6ʼns>MiCQl3Km {45[tw)bq6VjMUz%R*67ƢޚMnXБZղflrr5q狼 Ld;#NkS-dע$v*j?RЩ Ld Fx0j56!?zk9Q-4d[)h3oi+yVtX^aB,MiJ0ee~$)lj)-+#]NPrsJՋz θڎNnǸl sO+o`ډʅQXD&h:$$KOPAґȶGjB~Le <1ܷأZ uWʖ鈷;^R,,bcgk)x cQ­uUƗ|ნsXʟx3ǔi~,}lFZG@Rn}>Ny>Jrz82/]m'mS7Xx# ^Tg*疑73 ;GFj>}|?zt/^KZ TZ]qnFWr~-?k?ΉK${Ygt8ct @Uw0G1%7m⫴9ҡyb֕6@;I'+>+ˊlJXڏiφOQrh u~@r1A*  _h(+:JH%,dOp:D_Ux>Ea#0 ͫ r]FL\ g0lbn 5GNI?SKryf%2s#9lߠs`\ NCھ=TkAG]ں-i/pFĤ7 KbS_zA{>foM;y4~YG//z]3?+7ϭaI_Npڧ)[c\P\4Ӵ@+5́CiO:4Քpm6\N&N`,Z~O'qac8RX˓O=cFٙ2m>!PTI7XT2i]؃?QW,Z˨Gn1R?ͫam5%kߟHٹqǙ]hM~tp3s 91 ZSxG&( '!yg_݃=|Vj 9Ѱ#HI$p_)_.&R<=֙ lSNmnhL&ғ|̽!cCPTd '"N=kgɪ54듞Nխ@Fݗu1 y.##%<ɼչy䭞Ǯ؝p f}D[,S $@d1i!̘i;U6=;Tf_ׄsEz.v;Y9d UzBp%9.a/!v209ٌ0o Pi,RPfjNhؒ* : MQVT 0^| Cilܦ)Mi1G.WN˯p-7z4h0^v. WF 1p-7qRדعs3,Dւa; 7zT۫V2 &+-ꬥM1Ky>bjU:fq⁳bu8¶)Ku]g^kQTgYL(V??/"F^>X]AC*[;w]̨\pk,- 't sQ3.wFl])粶\! ;z+XV!k{ERk'~7jꍼ||+7U.)gVG>^2Uu5 ,?-՜a?K՛g+yݷ{f]]w%g3qiBl%*Mdt: *]gݮ[o'rm9^. .z=%m{*II 1b/.Z@շ~f7^N*Tx1ma V{@eKB DU񏋊8_?Nz}g.7 Qx<xpcQstvY ۏQ׽ȑ?߆^‹eE}g LG 6$1:)Hz`e{0w1KEoZH_dowdl̉<90^x\V؇pԽq‡5#9f* II"\Wo<0Yx(^3wws{!$׹伧Y_`3wÐnJ DtCMcCѭWD (CIu`ߴ9O_1Ed >t6}F&|`tH{?7++:˖п_?NaIJcY%|5v ?6>b+I^`ǘ4x^^{y)GɍU.'x>3-:hu9Ȝ ^ePu'~)CB8'^gB ɤߍ"xY W!Y>9|1]J}MckU#N֜sUg6|8}!"Fh3&:o}Jo݌i!23[1EʐDˣt/0jOC@Fa utVo ?!SaٸYI,Cw}&ˢ=-Ϊ |9ƈsړE'5I &,$<ͷSgcZ"*XӬ_Ϟ+S!)ջ߷/unȠ#}tg܃x'ۓ'AD9yfʼv=\&/)@{{77f3n,}qzq;̚?ޜj`^$=zČ I#EO=2uL8w]HEۘW>Ǿ×t>RŒ <3xD 26Z3w5 W !]=tEgʼ"tobKd̥\Lul?2̛?d΢yu:ʜ<=:槿̠ƃyI~x&֯ۘ_1|1/ǞG yY8Ƿ{{a/:ۼKy?3h|̙o]7M>0:O;z {)6$/^̿}xu^) z~ 'iK L4iQ`'s2d 4geqmW0j2_lUϢݐiϵ*{I_4X깇8qh;2Iuo4Ղb R%k'1ƿ9kpd7gpUY[&ulm+V|ʅ-Gqfb>]Q#;Gn+.Mfb^Wqi{9kfZGhT$/EgG r4g/e岽zo4ά)؆\}z:۪@Y'rhE>4+/27G$,@s㧓ydD 9|:a|aөvBu)&2' "g VEYei΢iQuE{3K1ʦ읹*5ixEFk(WqvZJha:qe"ܶm\UcNĝ݂W_L]mQkQ4 9lHlʢ]쟵P+WrQd%RV:HdfbI2cS&k|tP?QՒd^Og66~4̨3v9a&ONQy=yb/QFI7kK?z/+}Q˂lVqӌS ٵl݊_fuK<'28vfe`U&+ ъ]3k2S!k|٭tԝyzSTh+=x||ϗi#6f,DxNBʙ6uWeOW:/6 n6J K#^K=s8`@tDpPPЀ@ϴoPQQIۯ ((hPOz2*oъl7~=_O,ho7Y!gǔ'S]7Eiа1n{*C9Pˡ+y<{O*BpT{9XEߜH#8OSH۳i {8PiJnJJ\ˊ`28Sf9s{?%dk_^xXx!\rrwF籽4̼_Z1Sd}0ulsS gN&n6r;ٮyl9b~q+F1wSٰ{ZR1Wk!YkoUm`g״(䡸I(^댶{y+n:^sZ@ B_%=8f'06ޞEsEǾ[ S["roN }NŢ8V3i,۸ֱ;ӣ޻\ԋUf6ΪD@mSH #h;n[KC}û#g4⇍3ּUM=ʢ8%s!_cu*2l#sNm͘{f24dō͕x'oH~"W*cOYk6B8i^8?⇝g"7E)Ee$lixk0\k%u:O~L= [!\'|+xuE-{i2>zw{6 rT*`*ʹ#85#*jι>3w~Hގǿu'chޛ7?,̃<K$l@-._ 0HZNGiW:4MrݜvͬY3(//_>ef(I<{ȱ l0 ~/`6Ji7lm\]"vry~_OwǼR!Y\͇1WFdBR蜍4bӷEl~PG8VU˸xF$"O4DdkܓGG{J r_nJ|H U"ˮ2=ʼn>Z^6O}}?̾xҚX^{3L RU㰗CZ+UmB={056y f @:4nOVVM4@vS^YYYeeyڴnc(_8U ==^BJ/.v"3ZSحoو%}8-' [5e:#Z}g30d?+gc\|l `^ \Eػ| NX(WzNE#|w6v_idP]dm FAlTr<-KPHw:Zݬ- ? \ 9o&K?i{&.G͔֗*)^O>NMKg5̶aƬT١7kCm9|w`dzO @T'>XB={gdG"տnJ21;nh-]$dG,نL:6N>c[vyG@`7Sg^J<'i_ϔ:e;dv0cQ1~~)[c-E=ʙ<,)۪"20af/Ie?.e3dfdtk+""ξyJYzpY)_o' d3}Q1~/,IYg {5ejDfv-gx>,]0NȔyu;]e\p?ѣA =#6Tj ^EI?؁7-úfT"]:KJ\+ɇAJ)Fb˂-4֓qo0FȾ+ދ>ˮ30g9_o^~[eYm032cxMG#zw2d֝WCzܓ x{|z4qRJ!i\w6cM>o G)s ¼ ǘl?ſQ7_IћɪKTƒS{6" EK_ٗ L{5(GUϚ%#U)eD)!GU;f0K.?FDK

ُ<2 ̘!;>i:ww;d\Z'ԋI;# g2җi;#(]$_ce]cG8‡ ϥ0~y&L{r}6L]IVmHRĘ\ןE3\vF?kδDOѶg>((hȚt>9^̜-Cez&zd4) 0?=27pI\vFaG>qc}8v'+뾐̘{n#g 6i23~N!Y;ƾL-oU߄A+zHm˦<#lB2g~g93]W? zj+9s3KxdHw:&0[w/;:Dn{T^R3@2}\[tl[־*tCDCb& ֠) g=j{P7VDLX,u򅨻Ɏt {:1&& u1<$14 YW[*$ @.oMJ-R~i qsſ aMvox1m/rWѕ@:ЁXǛ WtcO^v5O_q}BiHD܇fSMX`k:J4e8nyQmQn$w#ȏ"fTQlEF3y-i$慉oINRmo<fVd(E9,2?!%caSxY-Tܖv)]ѢYLSQB"Mu@W iSE=ͷߴ)3AKyoCPnm$?}-D҆\N$=A'j9բ`9S[N÷VcϖJBO`)-J_zU^H]eP^n)t\5変#&nf2BhȈ[D QQv cBKs=~|_. Ri椠]\hhH K&NrhڵQ3Q4yJf։ ~? QS5;PbIV:ЁSK7)7ɥÕ _E%.M$%!va syAX|\ |ErHے `mjJDqOh21/ANJSW\d&zy}á U:ЁhUA?6oƫl9"+ }[jji]QKAE:,@M[ؤkh&m@Ğhj5ޖ-r,)1\Ii$ܞEC{*T}t?oJЁt"uNeA8&*L@9$5PKDsZo-n kSedX ĕvG.h]OA@bAb%NNA4aN {" hم!q k Sfi~b)Z|%`ߪ+N!WAzhڨYPt@k$"uF9YyK;Q_W4@EA_gM2|2doWD7)U ]I(.,{Ϟa߃R~G$%%1b(fumҒmqj5YY~ڮ\~@II ?3W\%l\]#pXAuC$ = te-^F.Fހ`6N%@Y1oċ=GcA҂lڰi3#55d8 ԗhe-//͛HOb&jGI 7 Qw[H3 a:26nXߎ\ 26og.Jظ=>_7gúLy>As-8yiGN@EYtrYnX%@S+:,lFUu5yV(@| ǃ( 8rJᐪ~L.6M]]=99ت*OHF:$I3%ZY~-VNƄ9N**+ihh`g'DfڎvU?+8n $srUZ,ؙ8yR _WnWI'nիۑkĵ꿈DI'!I6[ 99Z>ОXIaF#h :,l];w2pRNvvN:lۻӆ%%@{鼱p+F!55[UeTsǃg .vVؾYW%TNbRDRurQYYn~(($'&Kyq$I"%%$%%tRnwP\Zj[ i)&'=+ 9|mMDGRmȠS+ZI޽fGv\jlU͓;jkzNEA$MU2W!?͎V!%KEo9ADQL@=d40#IzwB6oANjL&hhSUt`0i ?JRcH޾ˊP{ w`[Ѡj!It!9æMphϫw(:3Jף54,6qAdٔ `<UKo{?~}NF AйHRr+z~OӃh'JZsbNX̋So1dP>Ӱ,V+f뉵*A\F)B ޑV SƑUdrfTJ+WLZ{k9Gݸ h| YQ2x0?l?Lrlb24RL-PjFAac*W[ܮnp9-rC8@SVyp IDAT_`XիW@y8,"f޽zQQY;zV5jjN'S>* 233ٽg7ܜ>沆ÉVc0v\9jwpXVX%$H9 +2SNeۏ[9Rqݱc'SN壏>hp(*.®|n¢"r=ʣ=SSش:ēছPYY{żҳG+fa9}!I(.*{=HKK |&U(\e[xz4N(xE-7u$Pb/` ( g̘1rs;өҹ[IҠ]CjڦrKR* =^p Az=թ,'i/Cͨ *w>j=#Ѡ5  uxJb4C4.^hDà~8묳سgO M<;V6Q^ }T ?'5qIOOoQ)ZF&rJN7'7Ï6h4xEQoؿF3gdBE\.%%%|7|rEE̘>}Vu6s$h\7m1Ɵ5/VsAt:ӦM ˵j*[R,iXUT\x<72^Yz3cUE RPgUseB$Mg{Of2_/ffzBx7ُe3v׻Amcw84L_vm>-mMOK@A~>;ow/7I;x< FV5蛛S"'Z9зo߀ro߾[ƵORv[,8\7;}ZB9|ERU߳K4$I˫Q&ݲп?ytC}3ˁy'HIIF$kfAN߹]ݳ[:ش^uб OPДxE޷LQEPu@L1"³L 1k>PQr} 0jx5h4jq].?^PUZ t`4 *Qa!Ѩ9NJ;,h(m=hM4u1X"Z:BMLQ;?y]nZwO1PӔ+2YzzzȲ(!ߊ~3T~k\̏ (jFr6:gv圩S/bæJJNl63y҄V/'_ȱJV[q|wŞ={et>dY&33\>KV׮ ;.<āb:p32Я)]]w>zԩSr}gݻ. ˪"`Ɂoю$s7ތ[ >f܈A|(wG7_?5q?e}o5JsX`ҕ"V?}3wכKocgd(%|[qNh87"H~rbǗ+?Fx:g֍sgDXnO-BnU>}~#n~ŀFlEQb¡q'*E=ST~ ( 䡾 9jGAb41hZRSӘ1y (/̣8xHL͎q}w)gW~9qn$IH:ռ7hN[r@׳a&jjj"{Q[~¶o%(|\f]S>X.72 ~ܽ*/&:wԍ{62olbUŢEgfk'/c*iWOU>7f´m;M(i]qȉ[l O^Z^Z={ڻD=O.h4$] l%{Xa[pw3m Ulmkfpq˰cQ_+5;|~IɘF9,24+sBP|eY{%["j ,FR,FRR,\/^j1 m?C{mHb/~HRmp8T}M ***8ZPHEeP1 ?~<|G#INjjjj;dddD}]=n*uH@YUʁVСCɧ$ߙ0q"?deeqɥ3rr٨蝝͗_$ NKB7bj1R\^GEU8qWPUy^}.gOwߣj>S.2Fr_^ye\9vtҹe!a4"raQ$K"[U^)kq3CR`ǎԠl\Vzwˇ{}ETWB]_6ɤZgTE@}=Z 4uɁa`BY`%h+A7hEǒ&{osXC˱5(a0\}/V^9^yzkhHpC3'7eWǕfQ3B Ս&*$IѸ%f0OaϞ=Aeґ +  Z% ǜٻ.ty+DQdǎw'%%kV{d2a0ظq#˗/X|7iv(ʕ+Y|9:\67G᪭`xe08p/;v@$\3gZ- ߮`\j>-jjkk+&/7lݺ5\aa!-?qx65X/9w\qkl}C%wq2ro|;.>ӯO w]˥wȆѱl{_fsuǥYb`aOsԀ'[$([®e6?=0n%m}rbߞy+- kK1L<['8!7P_i-o!՜FlFj@%zЙ:#(84I5HrR)a٨t0vX&7;HW H8\^ W܁MpTŀ,IȒg'")' -Gl2fN dvdL4 لr$F# **+ux^^/̙3;v,99Fh5x@D%r  H(W8_Kҷ_?Mؾe Z>9m3yyLrS T Wǂ h.=5UrvaqEG {eLW_= Xh'/s/2O=ɓpN,^Ƣ^ $G@izjksn rqfxJ[',uuum.WiiOհvGÿz!\:mPSy̿A; }A!vz[^fW_Œ}VX޽{)++8OdddP)d\WfժUSSSʕ+y7ٿ_H.n:<6B}Yt:^ziJˎ5*))Buvc .Wiٱ6 %?Nuu5~)۶m{am(Wi ͷcDA+SReү(Wzz:e-ʏ/X\\̶mИ{R`/}*:&~W>+9kNZ: ?[9c_;^V HVݿ|XzX8k ٺ X\zz[gO&õq4Y7IzcщGj8i5e景N$DV&Զ']Q"v%xts'aV%([!*4ShV&PO \*F&diR67l U_-Y71T7:]2-ծVYhQQQ &p8RVH,mo.~>Mmu_9p*b/@).*f5GVVɗ% ,vt:-ZgЇVF ).Q?_ՠ!Lc&SӸO"obz_~9>x@)P%;)7ݘnIXS;Պ$I YYQ4b5a1hpwAr-b(\.|1COƅ^ʕ+jL8Eq2-lA~q")VRa~+ >յy,k-jۮ"ihXV%Hxm~5SoC] )~7x}`o0 ]b j$ t3 ԭ%ib砚'TaEFѧ 4z>F#!r+^^nTnjׂ Vz^y+ NHLq-D&o~{n˜o歷ٳԘ=:ѵKWxeNG5jg…a2P|eeeq/ڔ .rGZZf͢.'KQL&_Yd iii{\yH>puAQqI^`0`Z޽;f9k4o6Yf6+s& |<Ͻ $S %ysCu1zAq8Q)C4/I<߽ BXAvf3iFuNDɔ%q]Pp7=<Ƭn}7f)y^HJѵiw*Zڤ8ĈفG+`Ȑ!,}㟭F+6,2 x0곖_TŚf IDAT8DPc24,(HR+j~MsS֮]Kaa bXdUUjԅ@=X #Q&^*+рWVyH w4Ӂd 2 :F X( = ~$Z蓓<ҧp0`^w) F`00gNn 'QW0 W U] _9`ԃ\(qѴ)9r$z} ϴs{wcAm8á[,vEYYYYY,@RRo7nGe***l),\q444 /0{v..rRRRX`sm3WΝٗ?j(nz=͸_pRgSHJJ`ڴim)W׮]ؽgOWƵKp)<:v> ߯D+q|zf/# /&yia>\qsOaw8_KP870.TAһs1KWƧ[ rAvex/=7jva 4ZZCrbăz[{@zz:r͸NVߑD!hM]4 .a,v;ǏTcbZi P[߱@L㜑co^A5:읝͚0aℐ YˣÎAƭA>U] /M<E޽T{^;|eɦ(VF9Npi,+4Z_$\Cu\n –W^`00d2Dﲺg67[/gpjYSKT˅00w^$QoNc?= /#+ywIPW%c.{ #}-T3ϔ3rLt/zξg_߸NWr+-Hߖĉ#FgsY+6VJJK/qam3m b~ظaU[U"rrr$yy4גݻw3겢eT_I ,9EQEp,xv;?m ޽;'IVSQހTe<_ d5p0BzzF/@heQ@(l6ٽ1[q[''݁(![Eg$qxV@k_)p3Vg??.$`ѹsg<K/hwӍm-*.λprg%7__T_=k'o~rsrb BztMlTٝ{^eԸ4׳o 2bVr׹`EL8W_}zKcy൥c6yX9mr+H4by=45 Q@ h  hfs P\<E95'$ISQQ?EQ I ~90-D^tۭ!,.}KNG}xiePt1j 4*}3QQ @F5=S+6@ .paF/v"{/'-͢<`u= o\?'E4*lk{ I2fL߷fl6ۥo~ESh23221}˖-kd2\]El63o<-[ơCT?8+ ˲h7.QfmJKKeoæ\3O#-5kܹ iiiL4ϿXٳѣ̺|Z䊧/;c84CH__g wF"ex\@>~&}4^i6~ &1?3'HCm@cd2@!7iѸo_wH’._"ɭ,6+rhZ3go~$g1 /X9S5K뷖?!c?IU 8>}0db@e|sSUUA^j[ :TWH?/>RȾbwDꕤb+I=ndY͆X4x=VCӶSc~X DB%hQYYBdD$n@R cO? Qddd0X(у7Ђ˯0~8zniyl,`]tק_SEM=ëK| e\€9MVɶ|ǜ5f,&MW_e=ͬ 6 -Zy_^u%fK$bytZJ\SAp ^z+Չ"*2!> uu .`4- o}fs%`@U[5n_&Q4MS'ume &b`` ݷ?η5E*ЍV0~+06QF#Au 9#Сu8gk 1t|H}ysquب>VvEn7O?&qf4.\ȼsxv I:>sƍClڴn6KM_z%:iC2nV뿕kԈ9z4f3n|'\GɁزe ̝;7,ŋg!}f˂O6NMRV׫Љ@bͯI ͖1M33V qn7Ϲk.l:#?[;G(H74erՕ/!};x Ǐѥs&Lm2zo5:q!YA{[i Am2څhZ<Gl!u8vEA:]# [zH 0s&Q{uu7w/nZ,e\sh*۵V+!25uJF{uuЧ6ZH$?x=~\ V(rqpFC+•47tP47jԕ bI P; ^пmĥ 4(* A`_*A8ֶΙ( |g7q%0}vv ]u7裏LVV(RZZfS/ƶ?_%\~lq֬e09}gy{^\}6n|?NRR]vEEʨh4rх0vYy7U {3J@{oe,*A8p"**(?A(QP=dϲ*-I3G6I4is99s=x ˗_I_F ']QN=&[#5 ߯ȶd/`VÕK /@Ov㹩%Daxn&M۶.- W.]-oBn~v'^mҷ"‹d"%9,&*G',XKJȲu&l oHލUbe;wK.ѨITTgUV, W\!99łAً=%~߲LG]N",,LBCBxwiڴl>L͘ #44`VWN͛3HĬ_~z_?}o, [Y\T&o'bxjt4Cʮݻ^θT_^~?W|X3SLP Uh K d3 K Q ImD1AG`Ζ!--k"Sg6'OVGTFRՂFc 5*+'f",-ƿmRie+} +`0k@p&@"plMCºEui Pe1y3r[mnٌhd2u .PURY/l)xԶI^b9FvSl{R__:oOժU+RYXg h>N$wJƍسg/Ο ֤siӚ*QQŎ[ ա][׭ˁCHtDjTFi޴E 4ڥ3 6`}ԡ=[*,o~F=ȌUC̙0.x3w0g\{Sn.9Pn۫6k[*Gzs R픊 (CJ%\lflXK%4r6N4P ddf_c s nO$X,PZboێ=wb]#nim1)o$!I $DѻcsrrHJJ";;ȼڗ̌ rlSD%kccb*GK약dU(ڵZP<.e9a:,ѻt;@qe26PumJ>JDWDF7s5!"_Q19 V)6QK{ũ[||\̉~ӯCAAA.MrETe&VD@JP&?fа0Je~dDX0 FMP#ޤ<p@wk4Z4$;GC (dYvxz}zWSN+DX!8ǰ/,ARw<"<ᰔ@E` %:(}kIW%ZMtt4AZ-9_J"VU",A!I Lf#*ڶǔ_<"]J :?h hGps7Uٶ^A4KU*""{\1 `pq@pQ@%*a GFopMh69g;O @ ^:":r,H @ 7"^:2K bj֤o{>@ @PqW_2Gm%A$ YQ*t Ԉg @ |n{. :m%AEEe%^Lmaa9 łlhq:ZRDQ]7 ws+ Xd [ێ1LquIv4$`4QԎF- 0겎(Aؿ@ x}=\P>m%פ\Ή'%AAAy,R/s1EADFD:/9O  .(uL&5hHNjR~Æ>xɄJU6'p1dYF.YQ >1 ƍY&io)?9A嗊A?БgRS8r-Z&RW\OI4mҘp;KVwǡV&ii4iXf qDU\qHMMC4m֢dp=%CЬy3"|(+ʃ@G_E~!#$Mu4߀WdeVGTqZl{( @%ĔAc6III!XC.f NGJJ aӴj݆z*NA YT*N8NV::"CYۿ|(%PLG^:R_JfmKΝ]9ÉcG|7d=8c~JiSPX,l,ú@C<9K;)$qq  xN'6$>ș[y+SlA̭L~B8'CJȔv}h4}*B>*N~)yAΔty&ظ:@Ppn+Z.߽2A&㟩 s}ͥcg՗8xK/fر>a<%g2nULYmfSp$n?57Ywc3F|Mcu[9cɪxt"Xk`O_%vX[+>b-?#>G<Ye0X)˲EX{ݲ{{N5[gą3/Wyr}3zfCzY5 =_X֯z-{}7m(ğ_wضglxd̲l!l}6^1;anfﱫ,t3?~?v9"!]x{MWxW`Xo{ҤV,quZ6^*%_s^W'y֟ kSXjcy콯Яq_В,W~pMckuaї-)WNW64onBB+E s+SW0;ns:#s'5RҤKJPqБ7hoTIP=0]`I./?Ĝٳiۮ;vu6~u~]_\ٱ]eBʚ1`l6sYf697\&jBuogn"9EQ1'gNR]BRz=zFnp/|yc4 OAžu(LFwO* ԊaA($%X:8/BN=IAPxm3YgH)Fsg1 IDAT9Ksq 1 ga铏>B`C[y孴ze1/ Je5iXRvqK$Vn1#6|Nq*Kiԭ+seZfi6s8s2>ͱo.:DY#q'<;u3oal:-"?P}JZ?9 )C'S{oͦgӛy,Id+uA(9Rv1$^_NXz?~o⍷䚥-~N/pAyC=Կ{kӠ:w)S磏G RV-4{#5NqPWLYqȝUI99sO5{O?z:¹wwV{/?{Q.D}/nI%j35vXt)$_NX*Ct03=R0Mza(%/%|Oa8ST9 (`Ūؕ)CYaM9<1¹w\&r^~xudb#B&/3zO ]`cBr u2l&ub Imi}83=^7HX8#~a0<8Ξ;p̙3|9oHN*8ΣFR,`c=\P.p#ov$KNŠ/vm&_ۙ.{> )ۋ;{|_oc}*e}P>lqq+W{^c) UW}TVu H ԨC[x9F\%HGw 쏞Go$.g`ֿ5' 2Xرwy?佁Yv-.Q;ӜMAIHP~*Ev Fn&5Ҳ2JO{ *.f_>O]_OO^)SHNIF-$_Kb0qޱvrx+%=CǬi-o>UNUL w<U9ܬόs}/_'y rZ"Uy!U zdA~v 6qƨFi̦%۩wV\u;%1E;gt{fh#Кͼ1u%'zV^CZ׉c5>M/|I;hiNV;8 ܝ6ciSR@RIS]∪,)M,3}|y ?#a|6!jm;fN;ɲC+2On{E JBF$B_&nvULbx,]`yԏCLtepK;8IpO Bo<ۀ֠ȾNiI4!Esi|;KKt_I[d'žT)µ]aq? Rݧt]=<-(fVK<J1 Qa{{{Q۾`ߞ[}$~}Oz[1=󎚱Ers@ݘPo3sEFʶ+DvnF[ K'!لߣ.g'N-5"/RGF9G|JLRv+:*d@҆ξFSz8M960j4e u>92 `NǺ?I>@BFkF:d2srڥ3f` t+3_xaqC]ڃ9x}@^ϽhC>]z[iԓ7?1͗XO[;/>^燐v(c3o:}p?Lk %%/-r&w9+ tjwߎ`"dNYNa?P%? +BbPPjJCXpBB6C<_ݳSVI:=TK.),2/C&1sN<:Kg7}xsbY3ONd̀>4 ?=E[&E2wUG[63|O'&5νӔ'g4 Ȳ}¥%^\&/,Ys[ GciS:3X9ex5UNmԷ)gjy*ۋ1sb N[="Ji3 (gΜA$j׮ٓl:]^OxWeE;6jXԉSS=[`wL].7d%4Rb?PƄ/d^2JZ#/ِYWkPGӠvkc\*<}=/zrU콼1~O7M2MgYO灣<1ڏ3f6db JX!_H";\Y Yu!#NiɨO![ir?qz cr`r"*-ynO7l8*2&##L׋222(8H}_31=ib?DξН8ڐ )~r9\ A(FT˧ >awvʕ e0 &+)Cl!ۘl2 W.c$$p9ngp٢{^MKާ|EHxQ/!F3~fڔ%] XPE׹;6r*GGւ^NA 9-g~ vl'|:ޛod(`_/%Y?{i($PzHU٥Wz=S,Հ)B7: 2(,khθJ6Sշ=]c|Y<[AMMG> F13NiOba?'*U ,k萋S f*cEGQCЮy~=NJv&hxWbUJ6mBNv"~='9Y8^xLE(RS4u8%˔Md4NmozQjcf\6aI֝H橑O|y$(ں4_l9d\M`Ho7aʱaٓ+*?۹I?[jݛ:j CiYN \0ԇ+svW33xj~?NvVg}B[-Kd/pcpnK%W,&,f _/̗m^HM:>jRSolZp˝,%a.lwעQzUU5\Y sP[ZӧE:o:IZ%]= tyo;|iT]SÈ/Op:)ûзVL=̴dggHNN ))ƍà)aZmt5S Sb\u/?ۛ3y7twʴxmkÐkx/۬!wMO1x9HIO6#oΰyf} <"5o&;n!cxu[CRG dneڈ:/R8K>aCpm3⡗Jf<Ѿ׵G[s1u6Gt#޾SW^xj]OkAax):e4U Go^'i՟LXl "\mJXH4YsI >y~rlɿ7q><<զ[U(R^AQ]EGG;_so籛IC&c8m` (' 'ƲkD+=Eŭ|GK!~ 2]\KW1DO=MJX/L$ZO>qc9x >sxGo\k.3O:J3,9HiM1y(id-cR8qsY[%i G? 5ѭG2{iar1]dʹ9q|& AĕMۚ2*.m̕3ypsFyg5١%/Clib.2._-޽{ӻ4FM$$)f" cɒ%9bX,W1ia^4_13XN:-o%(_cżky_5W9on|]YUj5Ljf鿧 JTP*$_K&((e W&T~c!a`ҍҺuk˚իhӶmN-2Nf)e-?HUc0082w<#m0NbHh.^Rz`s3L>ݥlR4zeJV[Ǻm)AdTv*$48y"Ӳu:J~+ݽJIz -8\p14hԸB6dYZRQU2ٽڹg䗊gf3{M۶?\lk@gM[,Y{[]J`]15  -eb!3#KHzVV}@ȪR9&Wals15Q*|JY A [αXXazj֬ENCWTTΜ:A5A*+$IƱb!;3+.v:U*W.7ʃJ%;t(p?KEч3Jvhxlt'&VNEkI^/xd"X#H7#*1 Ip1;wl- .Z( "##TZYV)^ENB!|ied2{_^ P_J> (p&P#o ]8GPV*FJj`euJL*Ve64 ::7+$Ir9 !PCy!QBCPpOB[I pөSb3Uc#WPt!! pqW%􁰛&!l"ym%֎YFl M Se@ R.(? АU{~51UF @ (,Y"$(h+  imNqsB@ @ lKIrJ#%BnNy& {srK)?}d6e;\dۃR؅+av~Hvy$7e^@ctr#/Ln@ >.AWs_4**d77`a IDATya}o$#}mU_E~[ݝ%`wŽvnrVsU>Ek1l˶ZD~mfj~|Hvh |?w׮>hOJ9/A;7WU^:*|ޟݾx;Г|] 9L%78!8+ ` -}u,S(ضwch,"v|qtsrpDs ѽ[K޻9UP7Zv. xl}ۡ0rV;ݼqƵyrw{gw/uɎ V q-P@6m7va_<ع< A7ǣͩ _T>vӏçC\Ud<#wuzP!q@&F. .Q#ۛn{e˴ϥ/Mgl/3Ʈab?'JD.4Cxl]T)= CHBE-Ğ}Щp:o,m6/-湎a^wb+m}!L?捲+_Wa @ Td$kWS Pkը$#TW$gx,@TI#9 bx"ah݄LJ#) D:AQFQYevKH ^\~\O?1A>)>Kڹ^_}HrQ~-N:6}kūXn ~4[xdɞ` =Y=wi/:/d( B:揶t_<-P%Aٞ]Y?y5yw<.~|{.v^؟Wq}1˱5;p*Ǵ0C::ʪ|ɵ@ AA$.mjr;lth `U \& |O5,SQC+_yLTEZ&=:0T([UJdYajt+UOlᔭ[=&v\Աl]Ɣw~&u/jmί?F1p@Khcp{[S9rR?DŽ߇GM8 Rt! B. ;'}mYH0x/wpf&d}iu6_2^SaMo+@ prtp1~?Aչ!=GJ]lTW1pqG(+Ӡ $]Pչ L * LPPJ8̵Vu3MVMl6lt/qjxgӮ9wD46_ lql=ik:EHHvBx/3. %kډxowl= Z,SnM`I7sbeUpiiZ`q(׊pWRӇوYRZ˷۽izSf>@HcEss?Y,J^o`†wxx\0VwܫB8 'ѤJ]t3xw9-r{~>m=k ʟ+H,7Oyԫx8e/L!w"/r y 2 c?8:K|{J'|ao@ (9he@:WF,s_؊cV"N$]VP#ps\RDȑBhr /'rh_>HNԧqfQQa+fŒ'/t1&Sγ履xum'&N{KB]كn+tr$7`8 LyVsgīqϺڴѝmSzUH`L\F7WrVc72;_,x-N{P>+Epu<i:S~Lʖt\0~zN}6\+Nч)GV#tvo;YH~F:iF4><5^=}O})IB>Jyi:@s@pg\cAaz:kyu41峏P| ӗ)f] _Ѽ/eRbyWl?*"B}q/B ! 4r(t9lc F)O"橦 vveʦbĘ#Quf[F̀y n\*Lj`F{S<7ٍQ;;Ю\otYߍQ;Ҥh1LX̖nWF fЭ4x({NeS]5D&G =DɡFѭsWH㑼r>u+xv`]6B֏Wȥ:\!2nRfNL7'3l9]:j,_,G쏳y~spBވsqkp1hy3Eq|};T[P릧# 3LfBV.VQt &#Z> 4JEPM GLr2ȤTZ^BInˍR9(<]YٰZQ֫*Iy cXF굾U1 0-~6h*T( «Z~b==zĸqWǰwǼ< =*ܵ8c ?ɽgAH=GQye(3{~Fa`WuǭnRCo>גt)1tL=&_je_fhC{}#?ʠR<=Ɯ M|D}9ŘF鋼sIGqWMFVEWS{jwgSm8PVӨ|XA/Oô%J\[񖄄3^w@?۵1XB¸w CɸcEAۘ5l|5b5G i>;opǬ }sg#P9j^dj0|L;B-HCI'M0@!i[ʿ7@W@_y*\#\McrM4`~e˲'LxjbD %.nϝ6rY z%VW\jl9u{CY\:V5.ȕ۔[.S}n`7.Ow+"➾Z<̿tf۹l"+;K0t>47ECZA+ʭ |ƥ3hZ |1f /Gq~۴ Or8e7r+8 rIsRgrd+93#/>`ٯP@Pm@.}(HLcS/i̿xG^A+~CRIch9g2{Z͡!T !߸DO|pDϑ= !/owoq`܁W湗;aJ0d g<eGTvZ,Z1YYq"A.m}ǹQόV]ozkW\01!PM57ԛBB !&M~oB /)@BCB ! -@ ƀ v1ҮZI+Y釬f9sf3яԻOe lgoֽW' t.s􃋎GnΦqJg /.;N?5te7?w&zƛOIRfNc_C$='jVXeݕ{XW9/ӹTy-My3|p"IzbWJI2ryQWg Ԋs%W=1puחr㨮1`pp|gHG>.cV]]wu# Fc|-jLC1kVbڵefYfǽErڂe |>)1Mj xƽblqvJtO~Dw@^۬ Zx tkނrJ[]ʕu pc3D[<=qmc[rF4薮!W+5&F64rfuΈ_#Zn,^wx ׍O&% w8\%N8r^oҫ}[nt2Plu^(N:n\ Oƍ4;lICgO,߄BS!H~73o}\MbK^F^fyo)~Hu;DJ?)N/چ ?n kIcpweXrc?~-yi{ >Ί}X]b4 7f m$\ `,1vg%$**ktEjOkU@?xQ;HCq *:^y~b9NHxw R^%]PKH:|HڵXgM|m$1ʾXG"cظ)AH71ш I xWe'F_'ˤ}xSo5NqB!Nxgk7yG`!CC_U߆'7CǟsվUwZzeMڏ}v?G:KtmOifDk6Z|2u?6K|}cjջr-?Fˏ" bx{(R\TՏoUQőeYRd}bfVOgIKV˰Zp_FwO'! ]{؟;t+ױ]8A?6g/nP+T.)8kF^Y6o;|w Y~o>V(%9Vqoz-~Kw^}}WHvuvi=yI2T;UFMϟFc[tȼj7$˔᛬Es[ժ|Y#CL˒eI-ChV{jj^r͞ܚWP IDATGyؼUGj!k!MpOIm< U䯐rPSnQcSl;@ay^>߲:޹/zl]wnJ(ڪ2mےѦN{n iDG$Ƕe+ղ#[;ůSZ}󽪽b-XzbϿ˧ƶ Nn$r䗩Zjm 'K?_Իh$GRX2O ą\\I>cxِ"9 l]w˦/zrwׁ,?`u yr,$URP_ =ΓU6iVdIX1}5:w)ܥO?o}FǏ6D9)S'p^/?q~iErU}kݦߦ{8D6=kO?+M9#ӆ/g|A?}]켭wtg/IuMkL irڧOӽzֿ]i$5R^w8m칐3f%E1~+Cj0A8Q= J8TnP6-_'4K#_~Y/_0CN)j b}pntg_߮3U1_W [MՑW^J[cƩl[#:KGkbhɊ\vSxGzb!55S]_3kϟ\>fB8pX=̴nLM_6.~ԻeS]ηb7xC[lUScYvtVifn35oaClH R$,It#햢c8Z}㎻()MtA)@k= %Yŕڠ?>֬a҆ݥ# 7z?W$+'E/kWU8`8*WB~Au!ue9}9#lk{ZMQ͎wf3j:jt"L:'ř#NUÏRmWoݭ(t ;V~//?T;e-2{~QH_ tM7hdK ;r4;t>?y57aPs-zl]w˦/\o=e]w=B_úo[7=7 =z;e|up: }_Cijֈ瀜&vHN>nib /`MbQnM^^gБj/z?_~/w6ެO"r|jsL]/3(u 48ʠ<v\l]w˦/zrw㸽&=;wȶmݽS8C=Zڤ=4mY ܸqb˱4_ۻIc,$RH 8Rz|Rqx)ÑEoaW$"++7<_A]깫kE7z$(hZrg 蘉C Xf#-GBvߎV⺴YnW *cж'?&]Q-iXsSm-`uz*zn͚g4ώNG:)2a7Avf&L>LW|:)迿<'EM_6.~ԻeS]η8n6'\@@{wP$5C+.HPHvO6Z%czf+׌w6k)WTHHSۏW;q|= i@q)"gM'5G|y#)ZжZ0GKn>>_J<3@q˦/zl]w.z[~1[ϾNR8Uf(];ዖqz557W8㨻K[A Fj7]uHnK$Y2嗣mjdq$tW#_9WY ,Jh}RLI/JIz_ЫTnzŎ Z[Wڏ~N2k5%㛮җv-<$M[p$44oWUw>JD)G/dzVT9Ȫk[GoH~wh]r״0g-rǐ 4o^)G5j:tV|E"aE4I3+;?s|e"YuJ;_:hD#a3uGO/oISDNAZR t $;Z͹l5.YSթ9]Miy@WM_6.~ԻeS]ηbp/]23 ϸg˗.-^D?^$mX4}ƌ ĸ[+. $I=IRV^(웒ɻn_!9:_ mh0Wk#Ey- C7~@T:>oɒMKxIu+t%IwW3UJU|5mhuf¯L}z_WY(C-ty]^ٜҖ{Lʭ! .~ԻeSMsu%C_M)-u%*A@@08⽣KnH$5qW4UyB~i+>U$m,myzmI%MbC_nu]f޳LSa߻e2Xf.X4`;u4s #cck^ ((C=kgMKGҫ~嵦bN )XB~AO5kj}OiNRcsGk{QmYͦ+;+ <3`q˦/zl]w.z[x7۶/k͚5aqoBÐ&L%ɴ.8G>xV@p` m\)S}VoauҊi>|CDas}ZT}D:[ 'eSM_6.~;7_WC/lV'o- /42b}t{7S\ U߆'7CǟsվUwZzeMڏ}v?G:KtmOifDk6Z|2u?6K|}cjջr-?Fˏb *3.)y@q˦/zl]w.z[7W2dhÛԨӧ /VcC骧GO~[ܤ  _E7TR@d}bfVOgIKV˰Zp_FwO'! ]{؟;t+ױ]8A?6g/nP+Oi7lYQ<+}(djy[m?|X:d×פ/ [82 ]̲w˦/zlꝛR{ȶmE"yyّqOh;[?2MKǑ/W@V@vuvi=yI2T;UFMϟFc[tȼj7$˔᛬Es[ժ|Y#CL˒eI-ChV{jj^\/ww>_Jq>1`,%UgǞ^L1? HeYQS&̫uzzZ|>KL ˒i2"ZWꪛf)Í(dKel?93>C_'t"gd'}evabIʦ/zl]w.z[~1[(zwٶ-m:97P/;[+OtJrl[R-;rMQǬ8Em>n١7߫.ւG-KZ|jl[vDn.ߍD{oY H_\ua$[\_:򢕺$mV'\i,vѺD9@˥co(e|Xt!Ǜuۑ^+tY6Ukveddn<֗P8J⺹_ħ>gb їU3W++Q,|stu Rc5jJoHP8J*Ti4؟aJDdf.{rs~MzN^N/@޽j8QF\QWWWwHȲA40yb yOqdHٷNF{RYMO(@%t;uOYu%WSssŌ38**02iR$ɲ,MjmU֭ hM%z|YK4AO|@%f=yC˲,6~ͪ]Po>]a,wk>qAktEj/`$l@%ɧ{|Zٗ.y9/oZ ᛡ9Wjߪo{J KǾWKԣkmr֥z]'435L->m~D%m>]S{Ճ]~Pۏtir1\/@e<*I@ RjDA˲d{5cŵ9S_RLvB W-೏kgЛ?S/{J]{zv ͮ>ynlaG7h߶)lFTR-q~PI\=AV-˒"٥ܪ%Ir&OWP@1 ,@%q2>6~tVj/Tdp iJP__gquwuUt+211 >2iR$ɲ,MjmU֭򗪡@& |YmV^*(@,p%f.X 7Pe;'^lèTP8J2r{L_.7 OhC/ho?\}).0ʚ.^-~RuY}sw۞T̈l2e~mtNUwZ~@o?E p<$'%I0VnZSDڲ,Y^=FXq>q)SݧЂtU4~Y3ÏjsĔa^RמszC]BkgO^t m Tm~+s @@%u X~{ndnw6-?Y45yn,QI)7Y涪U%4}Ey IDATG %˒Zy9-:XFG#QS&̫uzAu=-C>%KeɴLޫzuMUߔF%òd6gf(ynlۖ6wޛtcNӗ':r%9-[O}OP٨cV6_fU kAԣx%-\>]5-;l HmV~r}Sֽylٚ7r9N&ȋVꒀZpէulU@FfU\t:yT|OJ3+adӟͺu]A,Bj;2M22lp'kjus%9JoοXAʱ$pXHn&>VБ )miQX9nI ?TX`^!4ݣ"sc} P2,҂smj8QF\QWW̟'j_pMJ-7hj͢ 5{,Ljq0Ɔ#u Zdf͘NE¡ ޿j2MCMͪ+ܛ F6$U;>*|s=$ 2]a|/*M\5PIRbGPqo2F9wG(4__OiV406]i6!WnA%^ SsSӈ G"Wvx׌<%CA~T#ЙT "m<qww6o٪9ScSC.{;^y=SH$@t\ϧK_"|U(𶷷kA5}ʸM780 uڼq>x80ӫqz{ ^n(vkނz鿨eb8r[ZN4LM aZ6inp^b~څ=TdP-RD䳬~nѥ/\2}c D"&g:::580I1UH5Ԡ 齖w|DGӸqFOО={d~/}Jm0MMj}dP]uu'`?5<1-|lb0MSMM m=֩A1mܦ+,10UU?q\14R_8<*I GKkD[b`p &.0:~$Fxϒ  +LMRcc8m虯>k    e56h?ŋ=ny* M@%Id n]igs}f=5ڱ+' 5aBv7}/G^{G1ڑ{Y3yPb]FKn/Jںݐ&s!~P[C%?/z7~r/~q\TnٮmcJJPU}{F>ٳ{\b^;pBt_TWAN0owB0@%I5ـ)}ig%F~1yPVRDv}qD`Hӗ`ͼ;~y* yPI 5]-@I$MHDy2/@E<*I %_lst?9B,@%Ksig%t?b9=+dV@R:#M\`Y <$e@*@ %aDoi1@`P|` @/y2/@E<*I=|icY0+-eϻwݜ+LѐY <$)f%pG,9+O* ob}5bD7(*Vc<J JP< *yPIR=OZ_ȣ< p$u\ t٥f%מ <$i2OWX2 >@Y  - \>'}C9b|n,d^yT[sʞw? 1q'p J#,d^yT7/T[fi-bc ]i1@@#0@`Pż] <$)z9ڬiRZ@^p$Ed H*sZٗnBJ_7#,d^yTa79yyd3c 0 0b>Pc{i$1 a箝zy?dYԤ&565IF555I5Qoz{ӭnȶMK>r'gV@8J~ѿN1@ڮ/Ƹw#u酗^;Y.MIrSLWQѡD#2(w#kRqg qM e3F<AB tx!ؤs-_+09hBCˮ>0ny* @%|m3z'Xؖ1yP%Jڕ (Tl6v‚K'<$"/qmr(Ҟ={gϞKL.=I^ Pa8JRȀ+WSSԤ&565{IݭtG=ꉾA.彡"pG{8qS#N4^ >(nuPb=f~<TI3gzWv/|)s-֬4Oίy*+ضUR @2DFm"vxyRK˘-I&)eG^J١u_OWP RæjVw䊴c qry֑z%c `RK4f7:]o 8Û00,I==陠ކ贅i.G_qu%+n} [ϬutvFL(=20c)K>3/@E+Hn4z#~e K"oGYȼ0Mqj{#K6^˧r PY8J⺹_ħo1`=c fFd14 bpЬ%A`҈5@W*E_s[ @(M{z'x@9^Z) Kڕ 6}b. t%Jϼ=+t&N^FtAdA@M.K(%|s3*mW<e%zs]>^Q= !pF7J1ู?3/K(@inRni10.5I2J4+c<zq gP xg^ν(H `&LhT= i,KGI')Ӧ':;;?Q>O>%nFfA"2cj|5@#0@i@ub1+ `Jd&jJ@#0@niz3%2+Uϔ$`AK(t%E,^C@7+A%U ǥfZ P PJە1yP-Th,T1f%Ph1@#0@ @uh1@3%Zb*tR3C401ĈJ@+ @] ƥfS T/@W)I.*P-bM.$+$$jt%Eӕ`\j@yU̔${+ (mPR10.5<0UU,: *P-bbTҢU?{w'YU}z}eYE 1QD[F4$~51qi=@apꮮ{~UtOL8Uoo7<2S}9'>nnEɅ. q֮.T+W^wB ]Q.38cpc(@}'=WJ`ffk|ϴ'LFP Nz %oA(L prqB6ߵE8~1`^j4b`h,L> 4%GA*{QL.\@p$iSY~]QܢEGҖbʯ_EJ&GxS2~GG%0yM F1e``qџ\Bֶ+8X,utu^ΎYl>wͧ)3͒FS|F[l״h!ISJ IDATn629tPKԮ~_ft$ϴh̼@+0Gcp(*H*Oq1k1(Ie3^Bͩѽ*L=zi{^۫{K֮QSSӂŜߧsl٦D29I̒zj VNs2ǀ# 6),u庁Zv6'=M2*2Szv֯]_s}5Ts6l4U hXsj_أtP_V;I] k>_K#+KSsnAϪzCgn^kIvV_ߡKׯyo0ӤM/|6楄@TmN9)H&K+[G}YSyMDCsCwMVi]VXPWjY;U*([H {x.} |^?n;/Y%I@sۉ.iu#=}.lߦѪ#i_ +iur)IFĮ]\$7«uًZkҍjӅֆoآ7\^WU#UJC dYgbA{.E#/cHE}znfk;~]wGHK~_;}@_|1 uiЈGm 1nAǢ c)DD7X 6g=5q٩֎R- Ò1'\` kŚSl*fFt`oN鿾I^: %SJC~eCA5qB,[u7khp@W=]B>B>R)֎EjjhzH^>wK)$I̲v}m7h$) /yu)i;Bqa6u,ZG_]O=93ieJHXW &6^̭?6gV?pC!u,^5nά_e>Emr\r<Ry/0"G= >;4b]uju{*o鐞UlS<ԝF3#$qd*-ZLSlWO.GL5}R `aYk{ez7HZm#?>H+ߩuv^w{UnOާKVZ鍅#_>zV|fY,Y\7y%(ŴnVœMr#SPpC!E':|`BA02, DÊGp*7@bOٕF~A}NyK2y:iEOqp8u7WzhPR1(q۳庮Ǒ1FVAU` 䖪eX*kъ%+@ 絨E3O$ps$E8;/ߥ֫_|R#οb))8mڪh16B)H )KTG!Gb& 5Gcb0@_VŦ-+x̖c1 ‘p( C '$Ҁ>nB>_uCJ?|/)p1eԃ`&kkeܱ ](ǀy JA@ȗ8qYZXY;$Pu`*LJkᄑ&r|v>fFdz 5RlVpk quޝ*O.`"ժ| PP.u,]H$*SNP^}<`!SGA)̨ݯӷnQ84Ii@t,iu#=zmZ(Z|j{ L'DʼnpXZ[48eZSc7-)]v)K_=fIaT:W`A&cq\(+2p8+=׽ '#qg*N닞 4()&M5)G~ @#pGD\^]xEJ*;:upHE voS2CM E~#R`HBdIQKY{>_:Ygs$ɨ\W1 BbF BrGWΥR)%I ߿r& @cArIFD]Y*~ y%e}E$keBrzN4gH$Jܬh4*q1jnn1F;w1F/V8U ؠF#)d\Rac2IG=Fw^.E:J<RGxH9!vu뵮u]WXLСCV0TKА9IR$q@+'So6IaI䶅eh/zjǓ 6lk 崅j ɸN4x1FHDMMM SP}_ippP\-[YPhp23vU lF&Ea>Wm!=ǕWu{z)s#RFJ8Rii0Pd2)u tW+H(iy:t萲lerBgyps I1rd}?iZوUftD^gxV$[;͙oQ8V4U$Q4++WRwwU,i4$)םcH#߱K#ãr[L*}/S0Y_6uz!"r T(+kƍ$]~BQ*z XoJex2i7:ZLܓg@ @K?ZcC@|^[nSO=~Z|rE"I7@0BN<(NwRXJU1%䮏hCOf\ H'??S-btc֭J$zG%I˖-T ¬d7Ů㷲 (@ SJ}d=9ETTNA֗{IJ^ޓ/L:/+={()H(L*޽r].S6- %Y4P ]xV~>e*''>"7d+Q_PA?:;;u() U 1`qePQ708ŕˎ?i@D-HPg%} xB==rw 0y8Q\BA'q ռx.$Iƌ.Z]y/DPP*դ,Y^ŢQyR,pOǟb,fkd t=kZD7' @\VZZG3 Ih$=&ǝAh/ ƙ &mK4N|ngk^3ۭyGɖU:uIK&4 dT&{eѮ;'jǍ[eu:=tV}GspIDATIU;MM[GLF4Tj^׽V7ʣ$]KUqoVϹpP_nԷ@/R}WIodh )}?6M{ֽw>;wNՖ uiګ/I-}w+ӥVE. \7oyRP`bZ[[^% 9#uoi]Ʉ\p{dmu! ^xC݉-r\Co֭?U׬r-;y|_+V֊k4[I cq6`@?X%W2ڹA"q$INxnپCg?9jom)X1z=񸤞!)iwtVDp.-_R6oQ(T+h$ց]5JOĵrr9栺|B;/u ͩ,^K*WGSZTv\ե|\BWk)(ڜp>tP<4:ƨYhDNX`b1brD8(١p<˕|#ɤ-$R,`ʩ cdk ԝ$1F&EIYd0|g8GjJ4ԍc#Li߁S"׆f$pW=}zz{h |_^pt%<΄a9АQju[ĞKktdX#C*B BZnv? 6P?3׹-  9ZsՓq[A kUtGáϿS[[Ͽ_M؏q9J,$IS ƳPGpW7ayvŝ@HűTj̉r,`9!0.{ޭ3/[ÿ7T@A@s"jկѦ~Ju7Ho A)/+ |I8݇eE>|XѢEϕ8A"aY`e}\/Igo?y$C`s8oI߾:-^rn}_Uk׬o4FRQ2Wk[~qZڴ}v% 9δ0pܹBӧ>9%ICCC zuMy͡%լ.۴ԱxIdNGI}}#5Zx1 9?׽S?>To_>O*({9bV,]׷V:,RMMZh-YXh CZb>Q]J>plQz׾qU cjN) Cٹg`b15%brJ*8օ?W{_Z}k_OsсL{o10P]XPW |*M]޷Ԋ&j)2V]Q鸠^o@,Ƹ`U-SNno>g,X: &[q̪fP.---g,=TJFӕGNfɼKktcX,p0_/? h,!IJɧR&)>JJ&Zw)jNg1WL|pU+J5s/޻O\;Z P08Fq01IouʺZSxX1::x"}Ӧԩ ] @{;893,]Z{rB}_BA 8\]-k0Q{Gı*q#H][X`tui+.*ڹaZ@dq56+w))HqN LbGܣcz]^ϙXB ?KP#΍W;4t;Hm3:*ـAx6=2цk^TSJ`ҷuvwzYF:AQ}_J瞯z+Y)jҥZlڱCjmii$Z+;10?Ww3o^_ t;7kI[[JFzSٮOǃz zE&pRdtPV\H8\9VA(ʗ\Qut/`I?MMͺWo}_A`wN̾Zdc3LZASڲe \klݦܮAɜ=$f.(\kҞiW>JW<{5Ӯ89 k~E"aEc1  b_R0x2CEMY-|=i^B-C:($c ں4+5p9 تJ27}W_ZwoF{qJE$C;s3tu5[d2zWW};]s#7ޤS\?n}Do?g?v^!s ;tӿݠ>ا+:8z @c8ӫU/|ы*7Í⊶B^!K0٪k>T49SCsf\%c[s_V0|sFW _q{Pe;?0c{3oP1M2jHD/-?ԞgbuvtԽ~*!<-PyM_\!`9nK;TS2 #-YH-Q(tl4L(~#?M|:0`}Z;;Cb¯-:|ۿ_Y^Nу_^}o[5pgO}Wk?' }qEzOSJ.o}o7>}HҺk׹W޿HAsThIl߁ײue](N7?~Gɪعo.hLMssmHIJN*mI-?e}w6שƍf{z׷;DM({xŤjDfY%;:ݏWD/-_tj>|HR,S+ٱX|AA0sE.\BA#˖]M|fscadLԌdleoqR)+&շ|&wS;sH[Rrp΢NR:ܪ gYh&=.bopoZ[Ϯ~Sbu|^!וQ.7uТ9*)?bH$y {$) ґ %VrbJE~tЃOvݻ7Y/Vጣ[Z)[?x2KߣH˗U1)1\?lXMuܠ2~y J'eW*ܦ۷o]R0Bᰪc:T}Sz˰47c(=5NX޷T'}&5[]\9-ว3o̬w[,Q}d2 I:)=80::e6>H8\32粊Ţg0u]eÊFŢ3.\*k%jkP_Uz 9lǤ1oR/\]=nL1Z39&M}\>]|yj1Մj+Wkw?qeǿ{'; JC6 m(4PC$@ TqAJz TRAJ-$E ;:^^xSJ\l͌>˻<,|EPZ5Ezz?J}}=oCãNݰBbP-<1ޭH$Jl}L2zu g!mŶˑ&bY'Ɛ ݙm!;c m+R h蠭=Dff51`;gNҕk $'X` ޷;9ze } g5×FOe|E>ǡO10^{v'O.2}v/ &{[\O5!._ʩ&2 WY/Sށ"""L$e_G; >Wmȉ} WXZݫ},#W}JTxV8LXDQ$s}m;ґ~+2j&s%\߶kC+y>M ՋbRA07aʝ7n֑fd̎904=L^t8XX$;5x?r&¡33;˅iKF[K+i˺]ǵ<^cJ? 91$f0DDDdǡK}]=++y~'dp(x'7 jץ[PrKER""""""""fPD8!',n}K9F!""""""RӖs9d 4G̿jH-( 7 Hxg9 `pQ[DDDDDDDvBIRb1>w8YHªkoSuEZlDcAݓL;IeU_} ~\YDDDDDDDvư+^ IENDB`muse-4.0.0/src/doc/pics/main_window_add_track.png000066400000000000000000003065131404105311000217700ustar00rootroot00000000000000PNG  IHDRBsBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATxw|ƿ- HQ)l( ,+**ذ+ T)* *H=!d);Ds9=̽;+!M]%z#Oi9-崜rZNi9-a7ᵝд֊42`23'HUNutO%{s )J:g'Ib=-UԪWTֺr)16zN [[h;7 C;%?J^7s ))*gW߭MerVDg)sp3xra9餛rL|yvpEn7ԯ_HRܜ&5[ "G|y)XLȩ/I') 詔C: o{Ƅ}fd+.kn&*$A=I*aīa(;sR_&qy J9 a'&i8QRifh@EhQ OH<*r"cSۊ$S`7Ӱo=C4Z:qg]c%n};ƺfl<6TIoՊ `DJZ IjSc3N?T itvj~?m-.l2 ]Ģ=P=~+p,.:9WJb)J{\L'a$"ǰx)8P`oM{s6ƷbǤ7X\b<*}k5j!V ÁٟkE6YʺIYm4 U+Y2`HGfe-"3HC6JNGe_r6ȘU{Ν4k֬ʰ_x9, ßz Ʉ}o6cD$mNf˵BЪUKnŗq4Mf2ǎRf-eŸ\ΨZ28v,  :@ƪ}PeZgm^tIU(,w"D"'zBnNj,Q}:wtP^Qi䟈Veڦ5ٓ>c]Iy~VDfA2vÁEν3~ٴiՒ6fV'5ӨFiFzxB|WH/?eMIhJYExkxWr7,@k ,Z5oFC`#0HdH'=wxngsxnۙ1FoY,d׻SvOH?HMbV~e?_gpkHYdQ~`IpV{+K7MeNAn7obK9!6e+3}DWt%-7g+W΋?蕿-Kw>?`-M/f<'dO{v( ϐGCrN`wP YނEk2:!v޴hFOO{>Vj,Mf"a(bZW_#RXP墤Og'bYocΘ1L2&M9VÎ)Sٶm| _OFJj1anx, |m6["d$,2/CԬR+r!dӁR˖-r9]NN'^Ŏ!"X|s\#0}Q(GR&::z Q+QE dk/(ǗR/jQv#]uzŕe{IٜAwg(÷h\JiN2~F.$Scn2R+ޗ\[p9os)*ZM˖3UEq<ߋV Ӟ-@..wIң#~7 @"=y^4"#@8ZvZ15w>sSX,bÔ(x g$nHO~Œl_Lr:Ԡnr!I])ɰcNG)$4=vtG]өU#ZM{puM]_osS:eY͌$Iy+"k]Mi;>Z׳xw$gfJf ? PLj:â^AU% *A-؇^?!Ft]=zzaW_Q]Y- ٘ca+[ O>=bՒy'=ˈ#p]zbV51n88^zL:iӧW gY đ#Ghۮh r=^RMd甕ǠYZjZj?p4%^:ty8Nn[0λ^B!|\!{~+n/tƊmF12M(P(}Jv"L3z"PK~E=,u=6 )T+?4P'8ѫ2QIȢ"j*(:^䕁=NZ7 ~,ǥ%{oqorHR0c5_ʣ}GƳ@F6O3_X/-M-?`k~'g;:w@ '7q)kp E#9s k+ʀ>g]QĂCO.\ôť9L!J1uIV/M d=]&w\؉-[+*me안qyV5$!;0y||Y;ڴnMcֱ"Qr`}mi[1_9:c9b+tT0ݨQ:@ya|9jWzXkLKW"EO#јu|ōPjE0f?Jڇcn{Ș q}NJJ w~&SZRs'Ln:ݵj՘lVk]N' /Y}Ǯ;9bN_tUY5v|mݪ[bmlٺf8|zPZdgFݥHe#YYs *`R1it{ROu *"O%&$,Bvt23v=/&|ɡ{(ϋ)fUþ];r99rɉvnJc嗬[SQz_M޽Ob'Zp:Y7lق-[&ˡ8~Ѡuh2_7c6[;jd母ɤWv\b*[ŕ(wEFjbD)ъzU\2UCNTXJᜠ9WqJ-4gUo_WYA_Geyx@|VM_s~ſ_e&{8@vP\O"@XJ܋)cWr<3Wwmy Y7d+9SwP;0+W xeÇ~h۶ nˍWb2ڬXmqlظIgPn=P bljNX0T4ᔩ a+S%͓ҷpL^" (8>##JHhj|R\*_.!≏\fqtɘlYSe5saSÈ-5U8F]܏_8tl!px";+"Vd223S.իaD}) yvo JB%DT!2D8&resJx|[^g)]*D 8J-b'>|'ExNnoxxqcJIBvu?T,(O+H*E,6iP91?{;/4Nv†پ'94mׁFb@!pݔp*o'11CS@TC8'@;v! 'DٍHN,/K͸d=*PH˾A%Մ >6C0#p IDATFAMtBD?,Ģ[I"O% 8 CeDƬ:8X| mڼ5Mz"I6 F?-4W)IP:. Nip$v/RWHWYJ}:YB"GIȖBDxB$|*Zfe렾 B2,7i{d4RZ|\Wh_`+*LA Ս$+\zȂ/ E~:M"-Nlzc|OBk:?->o^OWvbD&(b?qzD!~CJE/Ek.¸\ Q J'#FU7@Q9=׵@&i;x>؈FgSb \1_O+hka]bqBh]@:zce|C xEFtƘ4tZ 9!I¤2_=['FУ!!!#X9DWjF(i?AaBIOy?sy,ӏx3)ܰeYRN(Hj1%p|$E M-'4O WBb-cz!i{ 3k}0]D86 N2a}i_&f QǗ@$D t "E芐J"=D_ 9G#iHB" *jD'v2vfPl*@* :0Vv,ר&yԒX)8AqqHE61BK*QPTׯJES% %j}tӄᴜ8$!p >Pѱ(XG߅$ $)$DW諓sD:~@(*&QB`d5ʗ " e=!X$A0QH:MWp\V01ci #,zqݪV٬KСBd=& IW5N`t a@@TSXIK/!a+x*i~(LP PV1}:P1]/]0%FE Vg%#UO UH~nҏH8! qZ""0GK ~>T}4`L -[|S̰?=]"tExM$zK |b 9%.FeT}Lж-W;lWPŋ1O>bhf`TV`.{< G^U;1pӘ2ٰbHRlV"gcي;6'VaUl9"22]}Y=!ϦeGYeee)--aذI'2˰axW'&pfs~ ~1{/>(BILLfSVZ*&-8tύ}Wxhq>⒛&-(| LΜUۧ/CތS;fr(#L;TeEt|A }7B9K/ƒ|ه!dgIAT\̰KT}#$"+O,)^KQV}_s1g>\q|`/B&IiXn}9QOrq8g/>WWHêbWPf*Jql}]DŽy{@o^VRZwTmTD];rety-wng^7C^Aނ;~'r` c3oKop}n{2s| >]{/3n5_qSŠauʝ:4q&xBic^c* KeQ0#RtvwyB[Uquہ{ 9Sp(3x1#W^ _+=jvkJ&{ߣ'O='ӿgO`zݟr+p 7}~Kveao%\|E\B-HD e΂8%۾gB2_R>Nw ͑t#/gWFA8ٱfR&&Ij2mt֬^oAiq!b|-VX`X1Iqdx;wwr"d_b;&7#d"=g[f WN̵fĻhfRZ?4q-I*ܗ'f}gpA:] >/T'WY^]pYFw%+s՚&V^4_MLGX #Gx FxsX5?1:5$ɝ_ ?QC^27ⷉaG 'vP|tY Lb#ZnLPZ;m1\zk#êMw}\ܴ5IgML١L-W0ْ4180yy;+Rٝ6Jj\s4 obvP׌,s{&>{eδصu% ;gR54=Ǫn}]$_J5(&5ɂh qq-o pQP}wf%|ng%N(Ģfůν&CًnuiT꿈2o\EcV 0ɚ=A(jJs1an_}Hd۸y}e/^xkِjo_D;ZiVPޏ^.&s?y/mfkxl g9?-\o|4i;;c-DŽ aIkA-HtgkI_p>Wx$P׼7?AxMx>~퓣E0!$Bq()-0f.ʺj:7ѤQlu}3vfF|;zW-gW5`繩m"||Sшk/V|c油ȄYXVGu{[`!:z}9fV 憩=쓫cV̱e"/IO&8fxF%YB}g>|cqbF"~'=3ǵGX هHk~fq+nSN|m4ɻ?tb IQ Fu'Y !^ӒIa&_<\j:ټ- Or>}]fo*lP {:ZM[Wpی1KpnyoIv$fF_OΑQW'sQ]~+,iĚ L㊏:Ci!~|)uhѫ/6> ˻ݒT13;nnw[5 ɝ};OcY 0Q>&w|&nO` Kn}OnI.QcYlj/{8?Jђ{ö0;ػ=O^Hfh\hHt׈g.倳 ͬP'5 X8_Tޭ'-y /$Uoؗš,hwˌܘ8a cQ=G˾}'ƾQye_=Çf\?_:f'uc<:emC/`[22) O =p^h*[[#]#msm b? 9ɏmpӃ{xd曜k;1/3} 91}> nCDܭ~J98ImѾ/Z_-#9 Kᆩt-ǰ;]|i%- c;o?pc<#?Z3?Dcğs/CǓްWdWQmd^ {^5:^ţî5*1sy{zrd›cBZc>ߋcKPǰ. 5'#ƽS",XxO>Ǹ|l KSg3TNR gkF_<٬zѯQص\N` 1S_L y]> ^꺗IA\ڱ/37k?~v~'3.`ԈGv&3'=E5o̔iF z,˪Okֱ(e{ѳ5͡ #$%\(| dt:ϴ_bY2kx,f\ãp<_}z>b zUmÓlR},d-~~䑯1g{ )GwIC?w>sg IfGx3w9I&}v*=ޜU3̘dxϷ7µ1!{y'dw3ﬠ(b0&듼ѧLxVX7d֬^Ibb5V+׫zX6XjIɘLU+ZmUƌ ;cޣ Դt,Vk}~Bs)?{cЧO_jhb7۷_q5cj1*;Ҏ0MpeÃ,س|];:.ۭLƓUЯwc%0v`@ڜL_gY1>K=zkAXkҢ¬Bk&R\}2w@j5CB" lVK a%h`vZMzđe?_(2K<ڳ:x4f }.i8O~?{l K5wLÁQ([W~M _Q-4߇6NV5!/҃f֢ە]fmZ&{W +SR_VlݛGPP7v6,Zpe$9NcHɵH6  +X/$œNA=IںME)ڜvL$ܘ&I%.?\ƞ_a4sjZ@W_Oksf]B!Rjgmfr hVja7wm`ȭ=ɴ 5u|zӣay)g]ٟ9~)ǚ]@$5?[BJR6 Ei  ZtM7#9&E!2WJ2KbUk aʤM7e~@;{ @J=A\NdAp%4v @+u\ٞa~sQ .(c?p}7!frU/))d=cKf԰֤kfSJCj#S&gn$4s j½0#)S :MҖX諻9<.혊@JiLb!hȷuq~̓,^} u+ݴٔz#eOl/o]̖4.laHķH z-HsxY)~݂etaBH6^83ʟdKI[V IDAT%lz˲=%[Yz)W^ـKwKض{E-\V|5{#wWGpE}k刂BhzYRV`eptrդL~9& Yp;s$_ݼ]?z;zw~77[%6Ss|t5pW4-CnZ[owqCSbila#ZXRpqLZC'pЋ'w b$lu{s龽QSZo]֧Ww7؟.8x ||];k2dZK&ݯ>(S™ˀsNk`ӊC*7KNfWOryP-QPT9x\Tl7;JKʨ]v{\y󉏋N0wwaj~̛;ﺛxk^ =wMiY&NT23TPpDCQn1q-Y-/[aGT\u5c'ܩkg$bm^=mǢl$dEpLRr\:Ho x(> ^R0ЕII6Sv/`,aJﳣL,-z_ͽ }6=#sN!?̢Gj: ӟٚ߈-+ izI?%Z`̾m7Y.%Lbr): #LB3I 3 \ʪ]FϚ_B.Wұ|n eg1|'JB9@Ҭ!}ࣄ8br'T'Alx>ɖ x-&AR2Sy`$# 7,Hj>H j/si9؊[.AHl(ŞLsS+pS*ڑqn8>\L~9ژPm[ɧ֒CpQ2`h?Ƿd% <0e MML_^6¶-CI $RPY_J<Y X^ Ƭ]ye(UrqJD 3XHbrK̤ۃA90XqZ֖"H}r5?ΤxRM^[k$0lDO,(\䮞˜KYdCx?t!lL{n)О$/ݙ7ѿ $e1b`Dkzʌ9+ [2YjŤCDX23V:i5{t U3:%q9 X`'%KV!|#]x"Lӫa*MV/%9ŐTĸZtYE3IK=/aFJN@Ib%9T?ٛ!rݗϯI2y JLC&p_B0lřt8NNLuA jmBk˸Tf.Aì)=j ~TdkcY}hzl6k`OfOZ5 E>֬#mW z?\'ʯUy%\#BKqt~Zlf}+y5h \ثp|rKe#\IL:wbfn?Y4&\l}Z}[=W$.@ON%2"x)*CH S5e ]HN$A"]ק q*Ʃ`0T+gn72.,'ا"O}K3wVJY= wD\W>jD1G w|e37^I$t){GSPN=O]5)bETrS(*HH$gnvn k^y<3|oy6/5W,&jUmߡm$Jɟ5՗ T;!qyLO7dȵC5!"(omTꩶ$ ZԺW7׽EL la;؅yE;9Uv(|??fה7+~?+pS} EӢSMTgM H"DQ\-dғ9"~3"~#Y9n]0gPNY70 6cYoj JĘq?{YL9P*0b!C_%Q]#>-􀙇- wX҉qU\a\t\jSo1߸LE0defa6-1$$c4ۙz~y^r 8yo[]V*;vpjbtGCPA\M4i4t 8bSW6~=N: :9fYʻw݇Sш2vlHyJU[R[;?)ɧ^iCZ0%?ٶH> #w_bn Ƥ,WˤSWޠ4{ڭF>C \ 䕭(e|Nq~+_ToycQfFFOΤx>}7 ǀ&u8#xU]p~[V}TL݃ 왙g~GYƷXes/_وc|[#L*8zþu$ؾv{*A `Qim9M/9a'"3/M^#Iǂ#4x7WmAV a3smX& %A0?9k^`{dejÔ}b0ykfrAt7/BV&dzu8F[/ i,Tx6?M|:lb%~:@lW0eGMT}&aXgǬY_ {Ә_ɛA;߾۟dKn-5xWFob\(f2337䑙l;)-+ڊ}^4#%[.B2CtQMTڟs|R.bǍާq֙1T39>Ә{n"$߉ڗ?TM\So㞫DQ zMWuM $s3sAh2|R L9-|!f>OY{ CrOp4ܻH?㲳숂@гOsH+? ?T2}hH{ n;.\)5S\gZG֭@:v DNIx'q)h YYڿϥ @1!^dFLM7q-h @i_։uZ 0d0}ƒѩ^e<>A0"Qh NxəQDfʍ=ə {&Kp2.Gj~/.[?q޷rEooR+v32~ Y<_~tX:({]ɒQ1nEkq~y0U yg5##>w՛0TWm'q=P$ Z?vvº&cAz"ˠ_ZF=ky1JOrf[(DyG臁c4r5MH [4.Aɢ:2itR;s*IIn} ]FYrAVDC W_I-(45#O]>n.O2~q|It)$eaG  #H sD#},>Wz2q9 EdaR-k`™[oH>ItaBDĐWW:UpF>Zy,i0:ho7t|)SP{Cx1meƎVQ$3w:E(~KP=~%c !ƍC i| lOw<R=eϢN*_c3yҤ?|J''?Q5R?۴)*#1]U1#ĴS-l; vt'8!ur8Fάe҅.4Oish'n' |ެ|BC'MN⇋n5oR~>}VRu-(ŖW_fH1_uSGlGgcӂߺ+ C rD:Bڋ!߃ &k=^M^F++=' 9r_MS%9:Rqۻ7#G*JѽyzHoIAbǎ:b$c;ѡhZ0 xS?s|=06y}3\BX6ͨ/zGFFke5nUUUlټG;vlPy([T^5UUefr:wq;N6ۓ3_qePW{\ v?KvNNȺ @QQ1999b4%?ÁhbϞ=yئi:r>r!$uQk\atETCZ*$rs{3g/tn]=޵={۵̌ġ{p8iwzD X^EEEyd^ŤgƾQqĚoFCRR" u"nw#Ƿ5ZەﰻyWGM$&2lVtEAeN'-."1!NO܏VW`00猅Bk=($x/#vyjٿ7QӬiTcHmB-8K9:mzVى`'-oVfJJJj(DjZ*y}b0E0uJնF{Va01M B8f ÃƐ{2tZ+6 N9rAlWE !9<K['tLJJbj=?gZ7Ƞ` xL M@JMUCWF{3/hLv맓<T]h4vZZQT<hq846z>8/?~I:jr^ֶ^n 0Mڷ|ȳnz5$0@ ބ8У `܀K6Z-ZQjtvc6 ŬY?h4bXX,Qz`_p^IԵS`5 /e8 j5i6|\{w4F9maېN"Dhb"9xuuFjJ 򈏷L!" 5dXΘ{&LU59t;E ;EP8]N8QEQlo?f-B{ IDATB8j %ϒӋSXyvXKzv#ɲJNMsK $%%PV^N޽;=vs铗!Ce0>"%ۛrc3y$ ^^ G"-hHKx@dem`[7!{5 t~AQR{7$%%ק =J"FhdL&͟ץŘ En{ށt߾}r`_ӑ2i6xuq]L'6}9 h<EyӾ/FVEItA$q̙l޼?5NRUuFTT75N }Nс] h(RW_ <2yOj!DQD(®{j6ZZZEgO,6f4CMc!"9!!^y8‹xH?f88Q!-N۪?jv4KI(xEj=Cޫ -HIZ^|)*`tw#Y{܂(YQ:d_mG^BBfg2dM7>V; oTyZ墮a<% 'yR؉G֐)zу)-aS]_̠9k7W7r82d0>|RJ$222عk'n|8庆BWCA-" >ll S'm7V(F.6}C6c&Nbp жU5u?(dYfY5GoeYW;(Y4h_ jZg`ׄCz88|0mswDIq'W^Z^X"\޽{PT-06HDyi)9^CɫEa1*.ub ;=N#j4OB7dT>ɉpۀQ T3~ 餥80%K$INw)IIqX۽܍#GTBJ x#gj] =ȟJ*qa1t:}(~C ƍǮ]ibAUUp }NIqP^\go[ARRR1v(FTIQCb4?>L&W餢?>^OiYAhۜ{? 'e<ҋ̙5hHMiFɆOZЫ^M "byհzD IhE-ϝ E>%W}r{$U j%*yjQN5j[pB.gt~Qő Iax^@Սd$Y#Ix<$Itp <oD^|9eT(٭౺pV<蓳%R^etǃuse۹sHnvcKců*,PMd9Ru HCaa!<\ 1[7|BNsNs'3s~~>!ի#??^-W\*$IŎӣQ_ŕn^ٽ4hE,YL~s-qv;oDx~ƌM^:Ǵq^R>NzdG3-/-M1{餤DLkq\ \$Y yXDd,#m2))…SSSOmDQ9 N6t`4VQi)涒x;Fz=g+PI.`,6iښ05ԽltUY %whMZ\^*AHII*ɲ?DQ (~Wvi~mCAbJ:3ddb֬\x1s &!)O?̴);]wdCǷ}Ϯ])((`Μ9c0T+A@e222(((_g׮b)ÏwѣF,mbGhGR9@pKԅZaGs`gt 8( j䘾Ef^d?a$)đ#݁^!isn 4)u@,in#yLZ-FIh$%%3ox$a_Mv$w(Qؠ+c$&]SorSH2$!ɲjP'tF!{|]%n/(nQQWVone@r0:ˠD Y-Rzƒ6"r7oo199ؼDjbq{h4VDA$!! W[]ãS!8 .S'ֽFQ#=LDUY sgl9&NrzOLqq3gb9kE[iCq{yE.hj$۰( (3yqKIJaӍyhs*( K^lfӦM(v KZƿjzF ?W:lsWJѐ.-)QW PyDYr'fmo utpphx~L:^zq嫭U 5j.}QW@]}}޻MbG\p`sHh&X⌔W5QSSXBjyXh'M^z^}u:xIL碋Gt؍Ah4$'cmC$J,&7wI8Zxʵ8ְvW>X l TڵX6JK7oN@WQ_$MM/c^aj#&]x(47Cުơd$?F)SBg{!pC+hBxkb6UjpA@s{KEuF/Æ +X V|@6lX/9,G׿puCY f8VI>.N8¸,vxdߠoY/tK 4vCcc#gq[~;#"~-HLL.Sc{o5kX;tymۂJKKlw}W/hnn yާ]V_qU߫H2' Fh@zЙ8¨ ( Njf}QI'!1 ;LOɅ #vGp{qy$N8Ȓ,ݲJ<px(h(lF2H뭐$Yp69!>[O][MqKTZzǃF0<&L@~jE4Z ˅#J z<jb1D*BPa~ի)yfٺVKHhBL̶3@).*bL>"Pȓ$%HxF3dʠYZZl!ܡ(\BI⡇紱y9̅ : .Z|a=Y?{&@ee|&!ǡ~j#"-TVV8 p9i3΢ERX Me '-~CEBJ$IcHNV W2Ľ _/9,YT IZvfTg-*[NUVT-C`Q5@,P/X+xLU0} y,s[>}+W~wz#f(sw8 <,|:|K7ڒ"1t:t8jkyƿond2ѫW/ \MSO>ɺE>6pj'`i 4Bl?^Mǡ*Zįi$0V7Raa_`L M\v-;.$^tr~'n/fs̈́$;e$I\HQh58^uk- (8ܒ rEV,0(%;It^\C{D}\"zΐD@݉1B2-'-ɖ;b*j%W/:(BN\L&MV+۶m#33IRyvVF(quThѓіtҷ!|7hGfz!/f 707;? ࠳7n~ A`#0nX/`FF$cm-GR&ߵ#'uaժU\wu3AHOO箻o\yfRC߀~!Wftjګ8v0rr[7Ell)0~+קy#\k׀, h4qqpk(/pbEr'7j@*Rs\O>49{[o피ˈGEdYfǎ:t={;{n*++)//Sz=~?SE2wndX\yy9۷oGcM4l:g s~v999AjvgM@d%ŌJ꣫Ҳ$]WaQZ7;e,FZ1 +nRQ)*籇auP;N^~$nfWXvu+M 2ӑ$6 A$ ضcncWneQvJ*;8 Hh5k&Ͽ'x={ԅ# 0w4mbǒ[e!2Zó$IFHNNHbg1&ϋ1Vje A-Re u봨 ,:Ybuj4U\# r&R[M3;JIQkjjO }pFgQBիJ$|cSkB* <+ץ['$!Iދ%\w5 @*x8tZ`{@YEf$:Ls<E UUU~qLsN"4|$PRRoÇ9x 唔'ᠦ(&Shۙ6gZ&HF bBʃ±- 11|&_6O  Y +7o XB] IjMvUP_fQ'{܅J>k{Į >G{)H(mj9#Ʌ1SHN^lb2hZ|NN' ,ZП6P5=m409ooeHR2go=}?Od70+$ s\_jtksYQhhl!b"ά:0]4] 9pZ~WQq}Ht:yW6cZ-SNeժUY"]zI >U"myY )N2CG*|42 r6BeY hT«~ Y$McZ nMcqA-j;Ԥ@45AI ʀ¨)3I>Æ!^k ,ـw o M[9: 9,~{u: Jh`0  \P I% vXLy8T)fus0ġ̀,]K/>s,,:Fet:999=rJ֬YCrr2&,TVV;ĸǻ6u -Mt-P4@o FXmBm CeuY2ZW}`xSwlSF@hVhJ΍g >>R>SJKKK\,uJ jTH[%eDTCEj/ރBeCp`?)٧$:77_2l%KHLJ$55U]hCw~AhCwi|_{i3pr]};0)J ;LA])S92 n@D&;3fFN-O(+SOf ̝;UVqVKSS˗/''W.J 9>- k^brCkqB%rc=z&PiӶS^^Nbw'&pAQD:EQ“A% PS#Gzow PM#:9' Yhsl6q8TՊVߊ.r0&W Li]sPh}}FL"2EEE0xܞ R4/jK{iQ5WS3&5$;&hs En~y8kt:yĦR]i0CO7+ݭmp\$OKKG7rX̳8N{}n\.uD72o\W^ͤb퓓=YRY٠h]3&fOD\9tV,455( }ļRryؑuf)/oVw- -(bsDo$?qH. YVhiiJlxnR<}dnԽjjԝomjVq,O7G[$n)%C_M5~ T t F^1r/~`4FFêo7ioioKW ZЈD!X/P4 My3NJ׾vBDU&^YTנjqݜ:r$n^OCCCơ]屹 [M-vS8P]8`vwpE%&p@@e}לּk~0atCD^x9.2?y8RQE:d0.ӻ!bk4^w}5˗Q}(ee<#LF?O_{-Grznys=z 6$%b3wZ~ss3MMM>|+Vq9_wMU{n)=XރVGvK P׃&CTFU%QJ3$ Z K>#lP_o%.n-N24l6-J,,xx&L> )ޕ} $ 4=ۨFZʽ$GP9"5W-Zt:]PUjڡ ?=x`sž#jTPyuv<=s:(oR圳fά=w y]R6}>v Q9r F9k&N|x 6R㡾d) ;7JN@QT{$ j/4>iYqTGBʥ^̿eTVV2p`UvSݩϷrQeb?FtW{iem@H }Em*i!҂%.>p?C gnrmcX0LNÆqͷ +' o"WXX\ìQ9; @zZ i)',: }5Y̟7k\yɉ̞1g\ ٩cQH( 2JNBtk][Fsy 1vnp8jm6}Yh4"zVhIcc))Dqq*Ҋ dB=:U8x 4tʯ-MuAe0ZC胺C'XIaY&%n7O" HNVUP:L4T7 0VgVٳ;f !S2335JuTA{[ϡ%uȲB) 1}_ (zzs;ozkY菭)ng6h[rT ;_(|aS}̬G ŝ/>RAyDyY~@൸G  (_XC-2 @a6IO@ (xG : ٹ};"@P, fx+-*Jms@Pp 8"AX}bpl6MLL AF#J*hKZbEwɈZ.iq}7Ȕ8,˨Ԛܗ,#IR`4W!!!.ϙ7Íb4rr)dIFSmʎLڵ nIgF 9a۸%]L&֮T*UGd2Rj\Uw<ȴ4=BFM=@jj*GaY+Tt?F˜Mph~ԯGPP^4jfө߰1z3zVჄ<B (ߔIgϝiԬY?ԐJ̩4m(Ppf-ZQVٳ, %'IEN:d۸Jm+6A)l6/:]NIs0LBHppN+/WAƝ/,>R!)A`PPIr[1l߶6mۺ|ӧrʃ=JIFl鴩"]>qFmYd.MłNbAeu-I+J~oB IRInA)/,>"2{=KO_`ŁׯTTQ{@` ~̛uV. .+;n-q;_(|xBk:Ēŋ;aeeVp;ml ,7vCn0W[47̭CƔvNr9ÈB4Fِĩ9 5kӤq *3YqGw,46 Q/J!4JY%j=֯%5wnϣg<%sB2:&8PŕR`_y6l =:!jZû6!_%vϘQORd.axtg<^Q^\=$߿%2liEL=&ybBiL1H k}M;czW#KjxgTR̊wxmʏJ6؆}]*|ל/ȖU><nuL<-[2o 0ɓ>Rt"NW㛹_a_f fQ;/poM軤9K[fF6nJi^:})>¼nsM\X6Ør=|lo.za*XdwR88*wХۃ߾=j@ \9sZAuiC8`,u(tlO¾\̑uػ4jmC,I&"}q/hx<ح3-"R8 n%q#Yj k֮9Cy`6oOV1]yX(i&v=;*n_!xB]xx.^HŸK&ǖhڼ]ILJ{h֬9'}VҢ J9Ez~m5<8~Xo [R r:[ug`VTM7a񠡬 ڣ8st5,Kz,`8>o;sޏW{xkID?V9ˉyy&"&T۽kG$&69̜Z*^{{zLdw?`CSL[5kֲvG´^",IomەJSW7\L ٬랋eӠ}"7ʴ6p&gfCT:Q5r I@t6g@I6;z3a_,Sr>},0'ĐG_%o=#2|'zyv9kbL4^ NϟgLpj15,\/G<{o/M\қ~Wq5֜T(ecx˾_]1NO*;}:>M~Nu^ܗ}Ʊg'i}xHѐ**/K.<?/ |g,X8w帰0,Z/3?1[{0&rvͩ>)1ty3ԛe6~ y^}޷?3OOߛj< ߯ûb^<=b>;SR>Oy5d2O .n8]g[Cxs) l=~=Or6ꃽ~F_ɤ77gEx7{XROJ-Y=ʾpe*ULUG_k(TT!HW|򥷕!)(u'٢+0f"g-CY~S⏽IɔAS-Fj;Pg70^B)[0g`㧰Ih}dF6k4JmZSz#٩Ay!_.5YBH|%1Yĥ4%aQ;7OX4H9W1y) ]ulRsif\}7`fMFoupE*dI@qgY49i=rfM[ϳǕ,Qt?f9PI ՇN9 ?;g`5Z/F+[>0q y|l' %Ō,KIe?!\K*_7yt:FIrJ2l!9)``W]rdaI‡,#)|23?`WȠPuQOϾ`j[ZSt1cH=M{Ȣ Y:q|$\ro^s/May5p3学CȞREn? ɏu rD'{&<0]fӄ}њ.FhUS_yp hVS)sߦ1ÏXj5UVu ÏV)|bC;aul=Gт Rc`?,_:DzD3Jd$IRw?aOCgaL+ʼnVLf%?Q3o\!x><>xŤk{-P7 ~v2%Lϼq\'#5bۣxK&>A5 ]Ȥ+1S!eZ\uRɉ.ًN+XV{ *pG+ [7Kd**!rNo[ +r韽$dԱsOU2*'EJ%وl")Q:&VQ`h6c(UOKJhvzʆ$N:@j4PPc9o|yIWEBԥD1-ں_3q3<󌜾yT!$F3LHAmyTjNˈ7.GӨ;TDPÎ4P߃dfiݪ5[39| ϶ FeFr<YIdT/s1dTTm IDAT?ubiƘ111\|Of\?2~eZʐ<5c.[ϵC`=UjjH$Ewy@2~%$aKtL6l2;_X,HMߺh"%Z0h|)9-g R" %Ds+6ۗR7Zoѓ qI |3׬KǠG>'Cw JTr}̎[{h5|sG߃ߙߙ@zuXF>S?D(iēsժ*tT? PGPQS,Տz~(ipg 8Ro=X73ıyj~J|$2T4jۚZ7,Ilzo,-.f;rG7Q :۰ c.K.kv:w}@*iA-\oYGzLNǷa^)W|}ϓ8"35_iG62mp * _$j%XLd1J5 BE1@TRJM'=&5O[)<v݂v¬T{(G ss,ғ j옷Ymn)w>7ͬCDd$ԾFT"!;ي+*UNUt9\rܕ/njOE7U>4RBE)e ־HwL9"{C0^6aه@_ }ޡ`Aa'׹W9, h .B]ϝCTxpy$IZjNAOL^ k 0pVG`wg&+̀2)h#Wخ9 ~B)|>a-\͎lY1:\v3my {т_&fw`,n F" -:;^oU +*[ƒh vM~cՆL, .hv  t}UԦ cVLk3M kd풚JPF MP *] IFt և9+*@ e,LYuueX>Lع+wҬB⡜yϭL~hUkb9Z4ff!ϕGqz;;cк"j/u{X^. do tݪ NW9PFNJfXWs6z5Acpu:|*˿g0?![!r|NOy)Jg$E) cH1i`Ϸp^d#;l7~˽xϴ9p2.Cg .VU u& hF-t,sL .G倢aAɾg]ǒ?fvJ+Yr0 jFi2 HU߮QޖDY NeUNO(˘38/ZFLq'4ΞJu T/$gXN8Y]1`=6jK#YH\5rdc9( ȸGB2<*iBS\ɱ'IQu;'ügI7ȮюBҒ1Vz¦v̈́!Ӑ4^x`&ipWuSȶyavZY4~5MT @6\@lMS deʰa,k9Yn^? 8{3+ l3'R#ڵs'lؐHݻKH2AIsQzmm/,GMLm!R&nRx3.j0s._ul4hգ`0z.„6mUT8|D4ʱy̫cPͽ4 sA.T &*XƒLYWmf4ϡ-Ǣ@Z-bpnPuqQPGQSLUTxX O"]zlʸ>[ n utBAPF\\>Ep]*7~GT{6c8wMƎG""keWߤnp~AʤfX33n<Y>ߝAR|q'F{O>ӳ%ZQ^I^A&MؼioBΝA-f5n\" Jl26;_X,"b7?3t/ \uÒOq=3򗽃ܔMh!ogY½1wZoC&baOyLl =؜xK('-dȑ}ri,$YebBߏ n碝ŋh`RgSs7_G5R9%Gx9%w]Ai@T݈ʼnFõk(c"^N,:ǹΟJ_ ::f͚UHNNY.WXn|eQI#nf[ss€.+R9祸Aw3}b1кiEߨ^9ػಳoo @v܁F땍Vˮ D(CY@ԮSڵkPn]jrݣB q Gjr95&~=njqybp%!ਛm/k^}'xayp0m6x,.\V{}}|ެsv61ԖaKFff&NF4i֜ȒXv*ARRf_'>>J]^h(,HxDT[,N;ʥKDWR J3sҭ#''NGDd*T?RoBNvW/'xZ__*o>T)'G^#2+W/ BVf/[V{]oYVso;pfKT\ X,RSӈ+bY#8(RX+z"*DRb%@X,r+ދ;_X^|^C7R1%dB'oAyd[Ja(6e2w݋7R 筫*I].ųk ?JBAHH0Z_-Q.}J~)F/ DrRiW)L&|o1wH->>]hS^mΣsvj[W۵%,sF\ (_>v#IR97z< (ۨT*N,q`~) Iv+D%{l@p G na򄽭p p\ue8&>#>#>S>|P#mrtujU% 9.$'r;_(|a1J^69T*7@ -|G= b @ @ (@ ԣ,cmU%ϫ.{),am p_,qqod$9l.ܹ0)k)/$7$..aɲNʷ_<pc杣ܿM$}sOLy6B9Jsȧop@ n=$DtrsJy>4/Undŷv!H5/9ϗ! m׮MrǼdl2WekrIyyvv_䤾dy/Js}cyjAg>W7s+V8sbuP:f|ssty.byGcx^SrO$)\H w12j5z13SN (A1Xbepܹ+_ }VF \"@  |cCIRj@A @ ( A @ ; e^{"突+;Ч/JK"m0u@ 2G4Z5W\ޔN@ȦYN0ptu 7YGz@ <#W'vpHړBD`MM$f7GKZ@ tἏ3Th6zL98}zLHhRch%2dh|r9339BӴ NנR8cR@  WM$}Qjn|xG$U;:ck92a9m8.iHm^|>4YWtI|9 @ ^dm+c jՍ7z.L =g` ᷷SpZ",8'Zh!F >:?ss>8{JCR&hFsc"($K @ A~5eo rBqTP⋚4Lz =A#H>T5bt{7CB Z i*(oHD K\zbKT\&zjn0)dzeq A1 ط.ǚ}rtnύM9AMɝ? cÀhF #$H*!tlLz2 & XS@Ft$ǁ`ծNB2[u4AwVRTPKؽk'ޏČ,ٛ "1sj:1pr`ݓ⍦Zș_Er2QbA'|dׯf͖\0"&wsV'%5y<Y-cxf|3T/%S]J(9U[x授xnWwEM(gݖemۥ?qYR o N$T:^}s?(@C@Pԩj:hm싏`r#_`:0I $TJiw\FRL9x2+WsK+ҫфe<39@U^3w^㉉sA*Hyox>0wۻN'gc3#>x;bC 0ƑG1[Iltǯt. /- ߅,e+HMNFպWѢ 5h֝2ew4v9vO dm[wfO.BBe_+}ux: @DvZmi yw$s՟zLgAcf:DVzmLWeEq* LÇڳpj2Z3uP߿#ez$u( O|(*{_L YEv}1U}:Ol z^b2w_B p[" eK/Rˑ,ҁP[v};J[΍,c!o‰˙h>ȷeH]S]7%]@ \tFdQg"[n(}Ӝ|GP0j. )}V@`%T WaD #2,!|CC*^v/cpi>.5_Wp-rPı*#a:qUŻ6URh}o5%"V"]>PgޚдQ}b#DE2ݡm5z+9Q<N|?i[攒m4aقYP(%,6i?#Qfbo3qe->{& µ]G=+FT eUfOω0AsS[Al94P;mVK6`81e`X,Mܕ{ܹ%&&DӗeU< S3]o]fzwoVX,h84EeΈ%զZnhmZF,*sv0g,7cj,`X0H 6ԛmZ[ IDATBq_x_J5WcŚ\;IX{Αy壊~ j{ (96w>{u.9K" 2O+dJǎڰ|mD`*knƜ#oL]$ =6a+_2^MKvS٘_bSV^hDֱ8m6ևa@=ub$>꘮bŖe]?ōnI PUIU /cƹ !@ (dO߼k•[HMšd|"T=pԗ_Sր 7d䕄yfc}t W@`Gt&17[ ɜݽ%3'2:#|t2i1y|Z//A$gcW_U(i\00g%/@/aFُHҡٻ 3Y[cض?$wd`k2od%mP @-Z 7Ma,iCVfr9EL! 5ڼ|JUn=$g`Ѽt?͎_Ŕ*U2|O=.]bϮtr%_pSm5ٿGx~Ex۽mi[Mku0b=60|I.Lபd,_sgir!s,4`@9=|?Im Wd@?$fTLՋ$m= OɋM=%'[ 69zmrt ىdAVĴ KvJ(-֤C]Y?ÕV݈rx!9]]a$ɨcv4am1g&PEެ T+ 2<ʗoImI[3&>',-kҪqĭbޯ r4[V>O2:˓Ϊ %^U4'u+SA[_O~a9ߒg^ibBM6bd?]=8D*94\IqmSvXm/S]ԇߘ~/X~2WRQre:u1$#11tOҿ`.^;uB`"fVyMWPm,SߜK/}pỲ.T\K3&2sPGV%OUv8,etې$oe:p. F:<_gfz0JqP4[8;P;ve5=؂%$H@lMP$[Vŧy猝nhPч ;i6<ƴ %dsT/Rxa@eY~ihM Y0g% 6L5!<" 0rg`y@Ɂfi$>$QehSQ,ғeU@sf65x=ψhZr~s?֟M6< oK2sedh5-:nitmz{WyGl9&cGmgB%?t[D/Iuvڧ 󵇲MJ~~:)p_:zUՐ;ۆR:&Cw X6q )FԤeqLU$F x:UZʏbfT!ԸEJ%m"{ŏFďxo0չߋQ Aь5/Un~/!jOX8qgC~]#;$|4Fd>1tjf>h"3_j`{*DQm''fV/WK_j)*-;eF{\Gzsou FXY@ʆi5*&.Fx7X0d[t@uX2"0$$Q]*`dĻ)+qj7ȆnQ蕨R'1_n L~zo8K-m?LYE M]}bӑdk[AxWS!w_^JZZa /aI@Px~`]F;]@O9as-vJɩލAϷE{!Ӗm6@LٮA9a,)q?06ŷQFǻs9yw zXuzYcӥ|< aphȏ}Þ? 8E}R N\@ bgaPR3Y8c 2?~^}Ƶ+a&|ۓ`3mE岽 "ut93&ǧ[Դփg_ɟ.&] `Q'A} _k4ؑMBUNIIcd K ʲ$IeU I_-'$$@ 2{~ 0^ukHevTuIe'kH[}o)?gB#}iKN⾇s#d V!*vxfX2Ncp:'oR-u q8=!bҷ~>͢ZtnٹU4i8P6;fV[e@`;*vt-ջ!Ay9--/#+']^և-63y璳@ 17Qx%w|>wHHoЗ{"K'x{~|pGi7;~{ چc YSAU}O孶^3L5ϸh|oM@FM3 t -gų?#83We 0h6Z7Q3;ُ}nsvը!_N'ࡃiCPx&TԔd$'{0kuðax@ ^Kƃur􍮞T'G'ʣP>.:CH";.ږcͿH B"';jռ`'2/\@(-d@ Z{}U@ (@Q8T*$[c\Px@ Mb-RY>A>@b [Wփ$ddNi'Iאn2d$$[>8GYA.αHg -"O{8ˑ!,˹J8ɜw!W9]?kwd FS;A|fD_V~KZNi[$dK`[Ƕq )վ$r)צK_ö0kزo!/&$Ir)=ϗƥ.*]A>ȶ/ )sYk~;6/{ny.q#sAً+U,>aKSCh rE?5څڗWjIQfrv tyY?6X4:45{9p}$ΦU/۲=՝ڵ>IYC K|@ F `&>BV!.gKԨݥ"[@W[~LV+*cFUN6%m׷tBAƕߖw1|xc qb WRb魯}S\2o/VP#|ywqR?Ino(*"gEDXDY~z{/) )}vwj3;;3+N66<IXUU=bwWJGn;m(n 5!!Wz)~lO<꣡`Tπ54_8B6L<\ڹ\jtAIh>յ4:scuk'nШJ7 _$CeKɎ>f u$4Op^1 ƕ,E^Iwƶ}V8ICpE|>ӶUT^Q͒9(D ZU`~Pװ#"3(  L-[K 8dMߒCS7cv5zg9g߰=bz29Y fԐL]o4;ᴶ0) Fm_NEO m.r^|@&l S&vʦ$^p2M7;>x^\kwS/K'Y(=/毣 FYoۢr)9{so=fYSKB][g[)7t?\uuzy!/5kz̍-3~׭]o޷^z0S|b4q B[-r,9 Ǫq/ׁWdƺTRoY@ 4y ax8V$%XC?|L/[_++"saykm<.!< L%=Jw=c8 ~>o;Z[SKB:ˁ-̿jO}=&\Gi==4y3D>nlzqx~76}g<6l;HINQ#딦=4=g{O$7 b~澈Ϧڗz W8~|xOk§VY_}gܺ?9??ceԻyIyN9Y\l}2O~op0 )٬l:^7NSKB:+}ξ5,~Ë/^85[\քǡe˖>-CKIu6vl@۷ӻwe~?ɸ}><iW1A7"mu('RiRYPXY>>CMxo&0Ap@nb GsYrz^J~<C@ .%!Z[tx"[Hu5)p矄>QF|Te˖J-RK Mo>6mD=|ɼt)6lnwеkT)T=14r@0P:`!ܯG0pjp> >NP3!!%@eW2} ;{b#叴-I`s>%'L ?<LϏ701cu~|F/C5Mu. t.ɿZT@Lw}'4]r+M/M=ߍMߙ"֯tƎͿ3v ,_?~)1[Z|RP:X`EEE1{Z*> 8w(#"hh>ILk#NGτW{~ {syU;O2 cMO4&?1y}hq0x>%!Z[Ced-+ꇧsz窆J-[4Uj MZƦoH͛`ۙ0qy{:r,[-[p1Wd-Er7z=iwL*l{{|xTh9u \৲Ô>@6]zCq !T[nD^(]z$X aJ+hrǻrR*%smL. L@h_f00M_ ~#.? 5\\6g I 5ȶקЍM/5ƦwkNno@~?N1G5 }i8* !8T hv ' Gp>Țp(R JcOsˈsJƍJGѯ-[qh }ZƦoHZWq!.j~}^/ԩCBB)aQ^ T>:~bV~"`&?0e*"m#T\C G*+/(3y\.xǟX32d(?3aMb37g]IJa~c>3byz12r˿ bcx­sWwid\\6WWȝDdK730y6Te˖J-RK I&޺0 Yp.h4Lw숺zܹx3*uӴvhJ6xb|qD\FV}Kp*e*8ƔæE)Ӵz,f>3CNt?0d.G;p:?6-ѢDGeiԲeKSK T%ml5 4(*Ĭ0FtӴVD7CQGcQ`g\+UT? "Kԇ84ɾTl{} RPnlp~κkY~=ee(1CE>h88UqW7uǒ8etvS̳ ?fќ{VdH'45!BAM9Rq,mäC˖561 5kVV`/5 r7WFUa!#xqCE"ӽW|>@O= x,zٽC,Bvd1:_!/a v#8gbol]?g5fL!ѐ__xqBh-[-$phrNiMPPM$$ӳgOq&IT=fٷ{77oaNE!Nq[)[¹#l{Wpal>xNF[^6XrġN{ET:;$wov7eDjUGi."ӘjEӐCtq.7s!#N4&uBunn17]yґorT^/; 6ABbO:=40 ,VGUP>:|E%x z9V]p'b4lv;I X  IDAT4[DEdf8hV6P)hhl8`%lsܖOp|Q駌<Oco}MCQֹ<СM7k9׋p8(T7MR]c[0p5.'2cD |OmkQT MPB5 _@cSUUAQQ5;#yw~ӆ! naJVZ"Z:scuk'"R[e}2j5L?t =-e&;G')2LLCGu 3 {3fzjkgpYgq3}9x #1G(u(@{oF Iu$nB!y5YePSxh{F/|(;`~Hĸ ^-B!DgdV>T3fu{"ϣc35GCg B!آu /8(FG!BvqC^WOjZJ LàwC!Bѐ> Ucjrr^H+X!.茚CPst-\߅QԸuQ֩CBB)au!B5b}F)7B!B4TDcU' 6RҥDfHAIm-|r* k!8džЃ## jvG *V)aG!B!IF+J qc @cl)s61=:Laab:c :߸E+qӒ<6ϊ2iȷ'` ]3{[˅1ZZK]3w3@ѳ8uș>#ŬKGfԐL]!B!:T9T\:~j>0) F6IBt\r}BtF5oi- a4dO N>~Hĸ 4"\߅Qup2kFUjxB!#A!BN}B8Gw4fa !BѾ4uH!B^P@ӴB! JM8߱d}/N]ȥH9!.茢npZKճo0|W:8 ܳ7~b`;WB5 _@cSUUAQQ5;#yw~#qB!mN5~z.[ʜMvLN1Se&y󺎡h7dѯ.Jsܴd4O6 ﳢǩL:/ V!BbRC{oL]-\CY:LE^~FqLQ pb֥%{3jH Ӻq4~m뮹`jo\>gkBcx_ Z{7D3wʴf(Mj055Nm‡3%?Indef*,TU%x^=Oڻ$BiEySOw-;#iN. Y?I߾[@YZh J>}9KB!DˈUEC6ơ}Ҷ[n}^^Ok` p8tڻ#\߅iF(UԼHF FqdD23ۿwEt`~.]g#m|-Eȉֱ+茢=zv=vAՇc>W=m,VlB!YUUɬJ'Јk'aTaX,, bXZ7n}$HLL]jB !:8mjKVivI/aXl8l6VUUZAi((u\Qc{ѮUBUf*ՊfLeU?hM4M9 BtfӴ @7 @oZT70##ahVݷ~GPQ`^y"BA5$nxCElmeeЖ &f|$$%00'Ԕ;p&$?gvrה&>/7m]^(Ebb"/s2{l>l,XQB4Mc͚5vaQ8`ǼK9 v6MkS:y [pa;_vѽJ&e|Syuf<* Eq2r~[nYײ/oqߑq8OWFѯ%9qދb,tm@:-.c$8I!:^7D5PPRb; l\RnNIN VByRWIoXv!{>/ YgeǷ|u/ _> _?so,[3f1\6)bŖYʇܿ b8_XdK>󟟍Xۀ )͛[IZB_óCٯwڹ[#K4+/!Bw„z}㝻w1`vkJ=n7 N \RF9RW1Yt g`KӚ,<Ea=܍5=G(kg)t}(;-t9(̯u0G&\?.fUh+7nK5 q0)ff֨q 7k:qئ2J^~(lp$3xtdj)/C6Ƨo04(;W6s8wY%z0w~}fost XeabˤXMA01 v:t4Yjx?ʀsЬVg-X8KcXl) gkXp|/}/wɃ3L's}3 %~$MY::K !DGZDL\nt}*;ԩ5dQqp:*~6LSɵ%2ehvG`@UUfΜYI< CjFP1C2snB|]0| [9"S4A?LG cPC8y\w2΁X3~3J/Xyӯ䧷_Cʨ3kɌH9[p$c,=FpxKM`* nBtÍHB̭7{ǃ=(AjBVR>-c%6LÌ~~iӢ.4/]p7hu~~\aC0e:[.`DjiVy#^]è*bFI>*VWfUݎj.ߗ-bc>n;}|5;="Җ$R*:` )'Tgx3#!vj4ư^'2r1iTF]8Ғ % QWPO_|ʪ쇂-)o/߹u4V!v!Dg޶nCM%6c~{<~.'$fukH[d1Wޕ̜v~]ѣ6v /̩`]~U=?/g)đbW&WD~N8Rqحcbt\\bKiq'x|*"m53QF]KFp^ 5q}7㒧cr;o#ϟw%?jRqT̴&{o&@¤{Ϯ2s`CDw !:x׾< !kKΏfZ% 3Ʈ}24 LC=840[/k[/4F~RUy{;  /Dp曻ͽE Y{*~=lc8ʚġQ~6{Ten~{o! ne8܄@w,Y%2l/Ƀ1lx+*A,;aGoR/JHﻏVS_S?jr,'n~ .B!ZG qӤ#ם6mFu_0l~> ~3zL{IGW%o~v{j(j/4``.ɝL{~5\(\s5\}դWlo߾aEꪊ4Q.VL[u Z~â1uQJʠ&~Ӯ㎉-nvci}{8 {;JSIC'nkyhbLY,TUEQ*o/..2kciyUWV0-qҜr, 4/24bwS;_%34XQ f 7rٜeu(}ʋ(y]6v_6"_I> DžUR{ r\ބgg~4%E񔗢(bnJK߿7n2uI!j "63ݳ5F}Ʋ\^IbBPTԔl²ŋ8?-ؿ?W.tE+BDm+2S1k,6<kܦJ~An֌0 <7^rAϞ=ٵ{.[j򓓓ڥ ={=hM&4!DCcԸo џ.Y]p{shN$6qB![AY}8a7[G*#봑B.茢v3}V6i η+ )B!DSp[KRm:I`XXXO'*[[7BO?,/jb۱lv^/>׋ǒ7DְJ-/68ˌBtF5?QՖ6Ԩ?|W=|$8]8HR *k]\s2U|> f^?5]^0C3e'!30L1CWGn9cBb6hl6/;v$qYy-W'w„]5ۛ./VĿ{IJJ"## zA.]ҥ Nłi$%'QTXP\\Laa!wkB Qx Ču_3ma뵃 ɣ(^7ɕYða8ٳ'}!33t,ʚ$-Yj7##i !BMΪz81ozihmC<͕[CPH7jHMhILL$-- Abb"6 M0 ԴT^{ut}qk~@V{!D3r^c+*7Ss%څAaXp8b0 ~?=z'N"{6k~\Mv-iB*"沘iqhU8F֭iVL@O޽`Mut ^֒{2Wyxrا'\/yn2BtF~ULUjơ4 ?89|@eC펓I3G䓇Xq׫ IDATWw{xvnp̱&8i:w2]z:끌=zqo2T/]q'|*N_gs WpSx8|e #/#gɏ;ٵGg؅wqäo@o<[^k^ggcI&]/x^|+~gfpj˸$](ߵ\B!-*P27͑h_pԨaG֝u't@]̛|d6~C:g Rj~}/cKra$ڷ7}`Wy1|_Nq]hV4F>L/oUûk G_COw!S/|~kP-m_+~ޞ{#a?Y^? ϒGxyxf5{!?ʩes &{J Xs]B.u4vTґCcj #/̝6n`oٴqCCfX3@ a躎a$k% ]ѰgTz]7a<(6*l;|_Ѓá:c׻bKK"Q)OKWF0:vzqPPI՝{p I,ߋKIØxD_I*7 l~gٹߍǟPP콙r|pByEyQ81'8 9!:\\7>۳gk$''BJr )$'`K&%%<^%J\JprugatE[TI41c;Z uUUZc=%l*ǝ3&UYxcP^YyhI:t(/'ZzftKTH{.Չ5@Ь*?뉖AŮ%% '>z*I~=D/5OdrӰ5C !BTW;5}{\AC1eI7A&JZug}3#PY0z6>S~a-.8ߚ kcG*?J§Ve8xr4DuM;zI^3|,UJ EՎta>#iP}0a}Kfc*Eљ;#!d7LVVmyf'ێ  ?&d%,(4(@t7G[Y.N p~ !i[(J!^*7^B(muy6ŌyT T1?#rih,}Ǽ|v1 l6[^/>3r9wV0y$zɈÏ5]ϧCkÏ7xNG u>' f/?tpOtN'r\v<&`9;{+3%k$'O=ƃmOOB?>4 IPrc MǮO>csb̏ޢF Gϴ. t!W/G=Ƶnϥr-t9/=D巈l9O71횿r׿凒,͖BFQUM0~_<ݺʘIaa} ECU (Nmb>w+^|3rly!//9rϊa 6;n"`53qzҜ n. C?MY&\TϫzL^*]O饑Tҏf_ReeyKƼ?^k◩cPs孤Uzw*2'|]r;82%̊[B!#EQZSTP·FQRԪX-tBYyyb49Xjtm/Ÿ͙hj{xsN/<"vÁbAQ èr̖-[`Ӽ;/BъXHLp(U;Z,ߏ΄I1Rڋ`E|q_#ϦKpٶm$%%LRR XVTUEQ Hg_p/w3BtF'EQlXiTiZAQULv۷2#8rCbqpc0u`wʆ?~ !B5fTsU*E?%󑗗G^^d/놊ȧQ7f%  E4hV#W5*vĉɩD4Uf#%%% rr|\IIIQA!B6'o>4 V~c 3/!B!:_M-M0@ MZ@9m0B!h&NĈlm{uSs%Qh49BJ3KpCM+FmT@3Uϳ)BA]5otqKnKz9^*ajA&dK2(=L10$!Be61 fCk|^>_g|s%ڈpXXuP330<&^ĝhP-~"Hc*&KIh!VQUwbO;l6l6vƧU|F%͖h<">ۙK-ܪb*&(UapB!D*_WCcDEcƞ⬪ujDw&*?% m'*\pO:ۏoSzy6色`9%쩒_Ŕ+UY"N?/\hiL[J<5Xy&c560J>.zMgvCtdwM!ںM9Q7͘h? B>oA[VF;ط]=u;|@sǍ igT> n ?0ĽO12v0]I1wX;,wLN2Z,WG!B-?DobلS|2/6>c}w7M=|}9Nϥ硇'O2]zL`qDi?Cw/.?:EcW% dom%?qd0Jq#<' ч1Kbۋ_N$Kqӎ xX/gCX$NAVwPe/]z 'Z!ypO>Ƽom3)9N ~cϾ|]xͮF _=;Ɂ%Iwdr}BtFfƧ ~PlI!2W0B16y m,dՂt;u}Sm|x]IO\l^}AZZW7=&O7xoCۈ9܃T~dYѣ9=v;ZMi\NS*-f҃o2\>yYVX;~`MCN9ԢyyMkOahX֮J咗nZ /mɭ^ڜy bou9zV쎨1lw?lA!Ob"`ge#3qV|jIDD|>fL? i}"V`3"]=6%6o-tgq}p` cRn6?s3 \m]fX8b^ݿի 5f8Kx9<zy.`97y{Xe)[?Fs q`)1VDž-% xُdPRzT_ JO|h:2O=T$jW7NR*RH6obn"iIz\o#| 0˷ob>(z  `艽x?ߒwb.hP\W[b_̀w>e:|[,廝嘪YLwa)3G8~S,$&YgKy_~9. j⚑\B ShN?ji|c:z,.`X^A.~}N>Gob%WL P)8pS 3)"Vj%`rlj3bZ6RMח`3n˯7̃Mg`g W_^(7GOY5uk3*TY's 1cg-Ԝ(w?W# 'U3)z@řZGYWbz;>2gf{tTA# b9bb?) vѬ(M R"-gc6d& 7aw;٤}r?O,.%BW^sI5ȡq"-ђMIQ\;mWz<p; ӫ2;`+t;h:>3|O$C;geqݙ8d;?by9ʩ\J1<͆ՍӮ=7rکgq]Ki?.ebN*LA#wa!6poD{">gNMrquk ֧nYL):'&nS Z8z ^Гчs+;pJ^s^/o7kn+8o'0dk|l~|3Ҩ69BQSE֭<{]IXi6|x ڋab #_W`#\dC jeH\W)wz@ DsL֚fqM߫^諉:}kͽK#ÊyKcM&|%U5$B*6tc.I, B D VPYhj4Kh s0svFaĦXb㏢Yg6II?{;ꡥx_̗ǻocWO n\}{l 5-~5A|_>ygāF~ԬTWi#?+ְ=۱zN @ ]VA\nXFPZ='GgpA,= #9.Ct<; xIèj)dӘrH.׿ŘZ_z?ό~qeuDWbfERz{OÈ#5TEwo Pv =C;V#?^xWz]Ia##v'=Fs,fՓf$3X`U71e<<XSH^g^P \`yDyӉpVWN3;d1oh< !DM9Tm/mb%ZN\*+xk~´hRѬ"=VXkaؔvYo m/h/8i7r =ms]AkFzs luOp 0G3זYU﫮wrNtTZ(]?0u`vZ|&k?"\RDؕϦձ_bf\kCm4zW8KG!OgUUҚh6򯨁c9+ T4(QK eGl*޳O$u6#R^Ceǯx:` +>wElIFm\JgՖ8]E#Iu즟€N\DDw IDATI&NϪ"R O*񕙰ICPnBU4 ɸk>0Ey~II~O;[UGRwoH!)+81-;K9c5ZpBɳŰJ^.&lni4Π~3]UkQ9Gمl|'b?N@$] S0P"*(`ߑ6qۦ]~ŭ9::~SUli0Q(݅eSm*͡Ԩ)4\ 幔 ePW)5]gaå 9U,m|y'd՟jLQrBNt+3#6YTUB!D}ZDU%!+d^HE4ppBbHbA\InH3MEXƘO|ޤND,dUf͟~Ȇh[I7_Z=UK5΄!U2.>76/~#@o%ކ~ݝ`);C@h7K>J} Cpeh>͡HkKKbw^)O1܁h@lh2JC aPy~ZuC۹q%(JVe6ۡafc!,qqv+ͦ[/ \@nU$=;;ǞP.g:;|}l{kSVP.w(XXcߪ#I:dnyr{3 NZ>Z_')V޼rs1~~>zpu` O0ӫ)Ҕ ]q jLC*붧qhRS]FB YEVOKW.[wamۀzM꾅RMN1ZZ~Br,%rX1z_5Cv(&Z B!FZb{ ^l*UjRf ϲ%bsgCLSQPOrrJsgE![y/%p15PUIƒJ}lF5BEHXe8!Dc%'0rXuYBV$BkwF!-@ӚTU5$^NRVQUWWB!bZDCc˴D :(%u5*l!~ꎵbJ b_JBT\'B4Te_TUBƓXc%ZIyN3G0l~r0 @IQWc-IU%!h 돤qh)TsYn#11Bza]ױۇiy(JѴ0`bi@1Oν߀klް"- b%Zϒ: #Dr:,nE׮餥#s=0 IJ$_ξ_!^?-"pA4(9xNl۶S_`vX~x/ͪ *yq9B~B`hQ$n5tn4sgxd"`$4Zzͦx锕-CMA`C_0;Ƨaoz!BW>k-4[8+{&QG"Mas)x-O];? o{o^neP\R̼)/"k|)-ӏ气z !B(ͺk%8T忺 + (y ` #aDPi t #HPֿ/,c𔞬]ߚ֕ך8Uڏ3Yߏӳ{[M'Fi1޿5>RC]}ם8?+p?iԌnR>!$CD Q 4 t6o>Ν!ƍ{7kҶm[: {́\`p;ylG,c2w,|vsY|=g&sƑN@Y (6cOތ8(\1xu*<ͯv3L 3'P`M\HkY$1 Mňkd/q;(mR+;gVP#/.>JYk`D4ͼtl;aL;ы Oi&-Zċ/ n|gh%P_f>\CqOƔ1N}2PVz6HYO%mh o?yf3y)f;:el6aR8t@>-jFun[ΚbqO. ﹭ wO×m < m_Pg& HVkf*ٍeN kW8gs3 gsŌΚS®W╟  Ai?a#߽_癍+ wBFA85C˦g6ϧd[5.2jԨ. {14zӍd-ߺqTtdLڏXYhm&^c84YGi2GI("yk0'V񄶰MKpw8r> M u:HC 8PULJ7,b{ G Hņ?&3jm} 8gٜ1lΘv1,+D6 FG(lBbD[a$:jNBB(}=l[X=qBQX,h Ӵv~":#y@ (L;1ϰmBR yl. Ϩ$$SC8۸l\&`(7>wRO">E|GI%0b|NY=Ix+(*"M O/oFߺ4Sl#ׯu7QlnV+5:dOts6U 5 :?F#uU?#u* bFWb & tg.MH&eVۃǢI;!;u嵻*\]%YC8*ICCaۙa"eSF))ɣ̨~)wbzy>AAy~9ĥXAm%bV[ G0mZ/P56S\ ʈh{. ,m ?A *R+ B"m~ v{QKAiS v,Z}ŋXcRS~:ڝιn 4jiR݇|¨v5uC_WmD}wP~~1_"V~m)=9طEKW /ˋV%|1ӬCIKMK!B^R'N|aG߅iZi}iZhL3dI!1YUbѷJӘvz[/SRn'i l'5%v}̬wS)Њ[LV=ڽ-u]gCKtz/Ȧ؏| !Ƚ55E+(N8:/Y ''<۠B)^+h!BwZ8BTM=7+=+P8k?]qJB!D4p(*.aq{u RjweЮ-<4ь48+X YkUnND6$B4RnʠaINn|OLTA~>+.Kxfˇ*e =:n@tն]3uu93cL*V nk#|C\UkIgwpݧø#׸G'673?曗3[;6rwpﱙh2e_s}Kd BY'z` :|z&.q`[zlB`IXTڦ2N=hUy9֫maIi-}3B)j=IΆB!D1Fq{:ݺ kD*A~XfύcU|>u}ۈ|+.{Xmfp1V{mWbXb?fdˑLY,{R* @)p@`<{2KܓY=IGX|+,͈X_/pC^0Ynex蟣I*ן7n3ILm!S01?0I_Kd 8 NF֯@D)ʾov9"\5g%9co4Naa>6c \u<2+N \~x>{;?t ;k&d4O+\!WŘoMvv㭧]Y~:'t on 5^kg~grSwD*/^珏=!Z{i a0VF~@wt*fռu1 qH'Repyt7))X[jZyoY7<1~b \ g{ף*G|gk6;o;n@^xKyqw ?O{>7~|UFhr8fsٛBi:ZxKGs-q71czg3:^-Mg 'd`u>I!Of޸W^ū~` U#,}z 'M=.4oW__߲`S*Վ}\Qm: >:U*˘+SbNdE&dzkN2@h/FXΰ!pi'`G~T4pw=юof(70?vJ#ǎe5IRZ|WƎL'{v6!Ꙁ޶]֊a#:tԊu*]!Bt8()*+'-Ey̼Krzd<nlHp L&WܲǓ*SUO^<ϫ1GJb[j( ;ТtW"n=Vф"XXI. w/oPwc^wxpVӱiv!]uxKҪ뱾c35B͊:igA?3[73%J)дvFJ9,֝'_.bxa4>}Wvk(u5rt]*F-՘tS`?=?^u* `DW &>ˋ JVo?PsWz<9)DB-ޛ,g G&YLUI*piN&e!n4(ˣ\I^t>frKcQS0~X"E2uUOXbEE޿B5iU‚3=/z~2X}e?/[?2*ld)/ f)~eϖ 6Ⓖw܋UCMX#EW< +h؂4TVK{(a.912OQX_ 2뀱K~(0P*E %bWnZ~ŸXM>f}ErѦTF HG~Lތ͂E; ټFVH0X2=/W+ *k(WUf=juN*_@0J7Wz0z뺼c]3_nDҥY$܋d o8vSb2KXx %FŶ:!JfmA!+d%nMד^=Y;O1W_Y1\P*GXe}9Y}P[|xU??0֜}XUDez\Z+ՔjP1tEAXER3rX6w CO!^k3<:o=^rg/߾T>ɻ 8x^.]Gr>Lre- 矰3s2SZH1]yO4e#NǷsoRILȠ!p`GSaԓ/qeҎcfqů'˲y;eq~\uD8Hі%<:cJp4v4:N~bgO3I3)lDu &(#{>3nYkAl6;|z }\-OeN煻N."`+mK &5>wswc>us~NʟG'aXr8lv4 t]G4lu\l[yUm˿ח7a˅ANv6yyM1 6O<o ^P܉}M>cB!DC!RNI؎ש&2g#x(Bqؓ{g׊]{veoghIjz+2hp=tՇ8h¬f>")% w\ywaD˜Y^A)N=i BC5C$_fQ:S2KڭS$mDN]d*'zS9\6Ja*ilMDR6MtyHkݖ1"P& KBJNBO9h)F1 nBΆ;`N8hgR!b?m{JgXU*Dv~16OFk۱3E uFf64M,m0V ]W(SGMlIigD) ÈPTZJ/q嗡W/HK"b22jcA(<8rY !BkklKWE/WC/#6ڴkkVWkӾlvv#:6r{]t ]tM5 Ӆ'!r]M'H۟YYXS0h,TtZQn Xwz$!";=0d0cZN[͚ ygs{MJZqvmYn M0"`Uǡi4Y5 q^Rk08M0"{! sVin7xP\lU%$h0jYB!h;?IRj*;Z:9-[jR jiцGMCu\];Eʱ&ic(*&z V[.|7=v>bZkFjfk|/>. 9B.~{JH]\}\x饜wUY;#K`EY',B?W"CM ?W%E/ݮ*Ӵ8kikuUD4rKpc%c.TTUKH@:u{.]/Wڡ|e&vO5l [y ǒV%O<0;X#<;A'Q `BQ8JK*_=qV#hCh4PX@EhU;VTex^aLn|>+x0 Qr2l;Z%z eˠ XɽXbm;3D#9,{Y^Y#)Wt۲MsyylēҺ'wԛ GMማ&Xr-fQ/hho|s>n {]ܼ|,^w=ŎIp4l̻n>6N:OH![tۋVh#'O[=e.R טdGlܙ 4N=;M&yl%'К]ԮTjK|—I߁)&NC8!HF8|eۭL00"Ha1 81բ )t1M b"ZD!nݬy]Zo$ukU)E]T1_OڄMʑXۗ*0u'g]i2Sxck Ǐ=3.b/YD3d6;8yvE@ӱSou&^r=q;oDYƋ|MGG>wB!@V^F"5ZAaAĈ`D"D"֣aXEPZ5D)~?a5秤@RvcYY&kd]9jP 65VC پj3ZIAs9t<պQ9|p[\ ;$:Ôv>&Aw rbjjN1gy/8NVYsyl_g_ɍ翖]=G3MVGw(;F$ U(Σ<ُVvB}o&Bw*E6XU;㊋CVPJU{+ӡ][| 5PRP0{  ]53ڀ i2󉧢 6*DX՜*Q(D D\ ))0p`ˢUՖJJض uc0})[yj, ~ː^%aUCK@1~窸rq'(So\W9w_T[Žp.:H/K ctC6``R_?Bx ~+FHؽ;p;+ PԈ̝ǖ`BvZRnʠaINN'(g%t[9/Teeأ]gڔ nVjReɄiT>7M34z6hn V!/hiU-BAX1Codr#oy:V]< /SpyJ=-p)P4`P_gL!夻xVA*Z\LLwEÛ?'śX>B=Htһ1o׳Ym2&9%qkd{T䖅Q,P !bPqa :|z&.~ߌoPLV}Kݫ旔MF(P6nZUVPYɰD *0)@JJo$:tPbXefZAuR8Zw=]Frpynm"~,۾Mؒ+/θqo1j72̴՘4_/t|ci"}`ocl"U2gC.o,bWYu HUo3?/=:f 5 3紉Cp<5;b\h- );11΁=C:Xh%YҚlV,\_B!'VP ]nz $|tރdrU-Ɇf8 -U0+ӈ>F|eHUCnAW \~$F'&Zc6mkjmVS4Hu5rX>o# COfZ?N`=\|&g{97^KT MΎw$z~._!uƏZA_~6Z"_K~S6u&#N˷~[FtĹ2Bp%/C/'tCN^K+|jKi')344{<gH w*Wsw"`j>>qvbvq)9fLoJ[>66cτB? fckaYЍjˉ:}7DIyt6gy|x<e'&/+0 sHI@n]1uMdQb~+ 5DsYY)KRn54T}+(+ ֊#o}#ܙ>ĬQʨ->ʧ )_9x=<_~4\/-\c#i]j%y4u⻎rNCqr۸6o >Y#oã/Ss96Q ;B!Z$oo+vf?Q>M:2/ wv}{>Zς>~ݕRKIWC_M 10hJF)RT-?UQ#2p~P;rok7~H t*P$k65CdT>q* V4ÔWVa%ӱCߑ3(eVX VN`p8?=B!#VTMVH)VֲV;?9wI&[6IWZZڲ^ ( ?DQpdp# @B6mm&{~̒df Iﻯ靹sΝd2ܳt,ՠCݝKx}8\n @&f"N<!dBCaw`wB!;XъCyy9]%fϝϕw-*Gl_M={ K,6]TWͯS)b(qWٶM7H7Xvw2ft!B!{[< mtutJLr Ph:!ltr:<SnD< {B!#Pt8ۃ]ѻqS7`n,rqp;8qE8!꥽˲9m*3gLB!B p*WaPUQNey>A:{ 0MQV1RiBo^!Bd8,R@)eұΊb/'!![li (P{w3L-^U[ڠ/&A0 M=0,Y89)6p6(k=!B 4L933]V X$s؃N_Ct!AU:3C2֋ΙAKkM׊=(:!BGiY8\n_@$G8;*p8̝֭48SӽubjApK<v4^B!&h?`˲v+]y L_]8Y{KϥoGl#~ܹ>M37-O(kŇo[W˯<|;1}Eh"[sЀ,<>qh%~k_!F"֚h<Fk{G(IJLYq.{m,$ +!q}鋽KeWRg7m̄Pc[۹ NjݝlO'7U\5RW]'~z$BLCϪ4-\o}ufPfۚ>o~1m]PCr߂CP{C<4HYa>m٩U6V;׍0{ӿn[ǐo>ӕy4|RXNN) O)b6Nwu,<٥pq)qKύP~b|apjQWKs o,nկ1m* 6 qVrz!B 79dWlݿ_'o^fd nV\#H?J!Y9x4١©0"˶4T\lM-π걺4p;LYծ#9ޕs7D6Nz9tYcl{ 6?;* ^n$~'WvHǛͿ][6- IDATfk| qN;Ga,C EY>|0=S'Ը}X![4|<_`޼qwٙ gm۶ۥΉ$"oyHk#2D|b|mtxեsxyF|es,N:Xsʋx/I>l&fkpO/,QY{4 J{s͗QotD4J9)c/sS5}/O9 e T)sޟ?K?7?[:9{r-lݼ`ϰϓH!ɋ>ʣ'O?}&5՜rIqyp|򢏎JWt$y1M(ɖt!2D|b|mh|yY~_Q>9;8=oj^_v3w^-,LO< ,8k~Ng 9 )p?\I5L%w}XV ~\2/k_Γr/ $ WFtFn*!Cq(Udxtup/:w҉xq<yioo+.gJMMѸv%:v EC17.A-蜩N`{f$kH oJͪkKmX=G~TN5 =B!U*5a*9Й7wo{O<mp\yg9Cgp:xW%#y1k;dWiVR'wɖJ^' 2>@t}|Ş6_DZ#BŦ!+>I'C_֬YEϔɓq?F#IW/$[9.?tԱ/۶dao14C%BRcrwA+Vq¢ uwlj}J> #YߕF%a!98Zݦ5m:'B!8,mq0 @ yraTUeN{h ο 5SM:}1>I4 1"iu6Bq:ZZxj2B@i)5O}3vq9g#FW,ƢzB!>]TWUF -^?n#X/Ƈ%KWrљJex%Vu6Bq =5zѣ\)*P`+Ke_X`/JI7E!"d9|Xa c[>8X:zBn<."vS, 0Jc/uBiXO҅ӏ9`ps#&ʊr~!BRӱokmYdp8 /lc1Ͼ:٘:B!ĸ.&mq0Mϴ3x<hJ-@"g{CH9B!B$GfZtֺf1.B!BOT{o(GI8(ҜDUNQfta mmn3ټL\'mfΞŚW^b8XJJJ2]RٗRYͲ,քB!^]?PB!B~K@ |t:I8qY r4[Pn^ģqIض(  _RR… tp8TAka~|>eF^zX !B!@ y[D15ZӁr`f4 ̉ OAHa*oZC4˴ R Ӊt:1 ˲2%%%$ vܙ 3B!R…e.8Ӂ]saxhCkr;5хnNt֨ 2 9O  ǃaضR GYYJ)֭[Rɓ'rFB!!8dy+02/۠X<TN'|NVN#y׋ᠥ%gF$T8@0#v(}!B! Aĉ҅좤͔'lY|'%nfcQYD9q }nR\.$ ,²,LӤVZ[[9Y`eee8/B!ľ,}}]A\gxG3Nn@ WLZq>ߨ GZ~RII 矏ǶmB/"---Dic !B! Pl=N1t:(yfgWք#} mjMlY ˅vxXzuf{V֙43S$}!B!qpE\Í8h4ea*Q k9^(lm6l 3o<:;;8yGH$D""mKB!#4dšPb>Y cF"h[mj6ml˂:Ô/lzHHq.\Ȗ-[غu+n-cB!u(Oڠ9݋@?9nzߊ (:k`&Uh MW.,\[o@]]]f%˲H$$/B!>-UrVdעGm'[l}BfbLrb5pZ%pDtuM$444z`66mʄbDTW/B!H8瓷[Xq#nL4p7@wcLscc-gTL찉K8fWtE_cR466fb1z{{ UI!BHUvip5V4iGN5ǃCqp*8i#f0vp֚H$Bww7x^NgRϐH$PJJOOOrf%$B!Ĉ*ήn<^h$NVllec:QL F¿#秽p +}۶33$Iii)>/NC,Lj6eljD"9B!d8mq0 WVE' /VoA0 rdJUAN>I?R =>?<3 eڵ(0 Ñ3Ӓz h۶Z B!_rc à^כr'dMo !B!!c]B!W2X!B18Zc[DbT2r0 難sB!طY!4Mz#Q $yQ#[ebQK<0}ϴ3x fm7N!B!&۽KU 8X6~z{yiEα~ _O83κwPKX<;wl/?_x-'-]sÿ^U0N!B!&Iii)ec6,/ő6UU(xa8rrVu'/#s|$gk6y>륻_0tvurE#y{Kgk׮IOo47eg=秽='}!B!v`Yܾ ]t֚x,ƌI$⃎N2 ?۷QYYǓ7*0q8,YdPa^/HI0}je|Ɣi,YI" 秼W_J> #["B!^sgYXM,K>e1u ΘIw"̩F0(+7֮nTL9sͺ5SY˂cjmM--<|QI?PZJuu uxײ!B!xL[KnQxR񸩞4K׋P@YEuUը_Z= }!B!D:/0Jz#Bu$RiJQB!BLT>%CS8 oXt3_!B1δw @8hx*ؖH왜3N B!bB]}=g}6=(;pܔ +d&Ni3q{S7)JO-@rp[6/x~r:-" ܏XR@]B!B콚oEkMlhp-oYp y[DQ6m,7\8u[7#gs`(G!B1XFQh,JCc۠15:'B!Tߏ&yTpⲵt}Vf|*hmAQʇ=vB![ 0jGn?p{ qzޮ۶hmajXB!AMucmmmf: ᠻM<:JFv8 8l0dh!B11ٱs'^s5k&g7oa̙||b4`la~U**+x}1WzjJ~ CfmB!|DwE`0ȯ˲W=?oW%0(ճ~:I$j:;;Xԓ8ɓ B!&2Sy=|˹n M(Vx!dW >L4RjjjR?q[VB!ľ-̩u|O~߸oTV!c5"PvQ]5hsc^?n#X!B!&VN8/.ԧ8#ij>N(^׋.HL)wpB!۸qgP3ml*>] r:VµB!ɶw6(`ۖ>'UqoM.B!B#Zk] n@șBH* B!B|r*} Rj4 N5)qҩJ$ d'C XK?\zʼn1::5&7Ϟq?d*'L^<:ZkS=J/֚vX&L%{Ί#y8;(~dC9?$ vsO=\3?k\/CE9AH)T3:yȄKKNI=#;D (qT' *yW#PZ%?YjAg"T8đ40d-J HTU}Α꿲+?u_EC&]f-V!BcR/0+PΜEs_JB|IԗDz/,][//ĬcJMX֥jOU JΛJ}鬴 |<_^gRtz/Sȼ=+e* t~ s3#sR 2W_L?j}*ynbh3Rq ȺAuNOt4YO_LschrntdHש U!e?թT鿇97iB Yˈl3[!b/ u!7/e}<'pB!^dߐ-B!8!kA=lB8NtmK>fepB!{>C.e[c#gLdԳ5qMFit#]fkml[S` e2ʄ]$@SS3͞EMM5>]DؾuvBl˦aVVYmc!08C5}coYDDĶm rrp8cq!]WAIUQS5c!"[kmcov91-k$ĸt8X 6f״<ضM_8L4cUt18ݝlڰKߏa}ѐPvY|~i}B}m6 y8#P@.M(䑿<̦ٺm;3ḡ.eYtc%FCmm-SjYf ]Tӭ5:lq:yщΏ/++=xw5⛹{=Z? (aÃGsz1o,($W_8LSs+3Ore۶ƶm.g<[0B2~/,  }0M-̘(}NL7jt `|X WCUeebݫ8xKI>7w/_ᅰ_~Gc(WU@%NZXhe[|qU#ʊ,'QOOm;vvxݑfڲsr) 0yJ.]0\0M [1m*e{G IDATv&H$BO(ȑ=Xp@ |GX?iضuS4`bLӢVD۶E'Kı1VpL_d܌wpL\)8N7!+Ɋ0 :r_oTXvݬ5E7km?_}ĽϏ 3,ut—mW7/΋ϗNad>ZrX\C5sZ›']]KǗ[̍}V~>;4-w+W.J B1hjzN>T6X4:(R˶m+9l]XAhر,cgz6MovzswkYfrwćDضm:C<hj"OP_W[|ZZhnn*-Ų,fII0*Eu7fC"Em]ݻfrptp͡xLZgՠJ`pj $p+?JoG/ۻ0/OL(9wr>ut;|kq-e+h͢" %d3ʖg^'q8i ;$ΜV 16y{tۊB]_.N\ ֐H$s8T(Y:Ҷ=sla(yUY`XJg$[RStf`^o81(P\o:w}z X'͵,Z%3mTTPWW@ϋ t8(bSvn, EGW׻PqH-\:oxQh |>εw[IC0 :Tc[}tF JJ]3Ay@ヘ5N RsNYm8Ph쬼%&}]1%8yVnJ "]aLt& G]4eYf͘3E˕*ߩ̺cS{~Dˡ #/X8{̦PzYu>zɤN+gB0ǣvz}~I,Xp m-].R(nx,FW%A4I}r{z=hZr<ʪItuvZZ*UWp Qq6z5q/Sj\DYsS0T-:ڋ~j4kkηn5ϼk!Gn_!"5n+d(0Z8!f(++c5πnwa6a9b"SJaoC4'Ӱu;DE;ʊr6oQނ==L9 ӠPhmR441}a}R475Jo1Zl[S?u*ij>i% *uC}/G28:SxOΉh!#6ѷ$#$8'pGj[˟ʾ̯(\L5?W6?sԙyn%]q9ͬE1q;l~*9:RqBOkl|38|R6sfL*twu YJ)|~?}.;Sqgkx<- |ଳ 68iҲL9ۍiYlk1s L,VJriniccY#j5Mxp_M;'Ǚ" Zn[[SNSVquW~om~޻t0ڥ _ৄ.g٥5RJ{pCu$?qs a5|l5tB1ܾI59rKx<^tIMYi]=]5)fFw[Y] dኙQVdP  3iTVUAӉFK!Q;u:[6oq{,Xs:L>YegXggX(ͭ-L0 TU,;:xkz*LZK)xĝ~j*&WF Q%8 S !>yvMQǝӝ5Գ 13ϕR4m߶T^/xdGc#6l´,>Ӧ1iRt̪T%/%ҜyUiLjc!cֱΆb7y^&j( ưl xIiHWirVpŻcQ]$C!BeuT2 j8CԂyoB!>P BN=m1)ЋeWEB!DoODbkAjU!B}]$l_^r^ y'Sˀ}{B!H$š(-/qiiRЙ =يQ&,J>B!r6Z;QAV!?;28D!B}U˩+}>UIENDB`muse-4.0.0/src/doc/pics/main_window_annotated.png000066400000000000000000002405141404105311000220270ustar00rootroot00000000000000PNG  IHDRN;8GsBIT|d pHYs+ IDATxwǿW"U M^{==5$Ƙhh1&&5bEPw[mSδ{̜=9sf93EQB md%+YJVd%+FT!^>eŊկcJzrqPJVԢA TÊFEv"hxm(J3sT m.lĖvLLl?DҌ̥YVMBU]#536>z08@O}$Y9ࢴ"Rg2l٦k{8JRubEzT+e~u5pg8hJK ճeߔn81WԕJ)O])UҠcҡby:qL ]mc' !16)rV'LeF-1rB똓.aF IfIyr,\8mX^@KRVYio F=S2qdj"y|٘ώ;9WC / :hE"^WĔVNڻxNd%-*5Nϝ0eӨ!O: ;{wlӫ.9vM|'.r 2q7 D tt݉=dBשR0w*Dޑ~k NJ Mfv`KںA.4eđKC8d*,i2J[ [*A wrփy қn9+#PA б;"J466Ѐ@Ao'x<0Oٟ%%ptI#E='NNF}[F$& :[q2d$Gy˿XN>Y*BOm!v汦v -[3b+zWbK#U-$6| SLY1)OvK6k,Et&tT7q3Աaqg:IpunE+=N"-x`ܓyom/e)0hʓx7:qo6?&Lղ\C5蘗6E4v\Q2uo3a fM$\^(1̟\v=&8f >D\{koqi4"|YLAD|XH4P(hFPSSe@n˓9I^2igy_tnNOBGPMvUWnv%|$Raԑj$WVC$DG$tuב%C=)b"xՒ0 /&han^0 F_r>s$صxfv47ZOHuTz6: T\i^Umpת\r%ra(>-%N'jz@ д׳E3-q&1R&ma^*ط~;կBAw}77|#S|˪0`w/:EQXn]{ !!Cfu̚5~;0g ۷<h:wAsc`vinl{F-rEdu 9i}lDϘI+ifTI&VtP"-I&r0fX¨gG)Mer:&]7+.]9c֍~C_ORyxnvJ,/sR\WH$ABoNX&DIL$SE)2/=kcVN09>%՜:{WC+LZ5 f:z&utZ-[HxIC3Yξy&4yzcC6nr[_~.},-> /~5kon .; ;E$7ܜ 4ߌ DT0d 0d ֮]ǚ5kYxd>"&7D( Be-}`7Xg,"m}Pa^#x6QK0 &z]xIܱ"+Za:Uə[T˵:fX&Umf5KHbTB'fMI1AO_E icp3]]#.N$Ru,C^}!]#N.}B p[/FV=z-ӎC}8jXᏟ@D,D,&Y,GvY_א Sx~iTt_~$L0R}:m;]Ur}֓ZK~8I=1׳8LÓx?}IugsC{q]7zfֵSVϢ\SGzֺaM߶ {[˘Z*-;*/E({*gaϞ=O?,'M^o5j :8v1C t ΈTG(/W]9SM]SΕ%?BArXdwUs|i#|FXDlL8%gԛuܱ"]2k&NOd#'m'ժ1$cx@N'L3`))q^QF*$%gz骥dTHP$uP e)fcھZ1=c8cƜ>_*ga_|ɥtڅm[6 6 ⪫ZēO|+t,/兗^bm¶$N]z ! /08^v|F=ni1,t)>_KQ\\gurSڣOBGV6a}/LoLc%%{+-j8+䖩Ι!`j#+$1$pTQ G4!&<"eB/z]c} Jk$6  ~,o1t*]=Q"zѸq^/_Y(p>5tpӵX![hŤJ>DK@XCfR?_BDIil@ YJ\DǴLw@5癡ZqK+L$z&ӵH)zZ=[]튩[1Lo<ΰ)bϚ_ÏyZWWeVŖ5`ͬ^أGkNS}-֮j.^L8"uzMx;w0x ֬^戮:v4E/!^Ntޝ[ҵs'LKذizSEEv "Bbe0w-/ګt-}%2!+=c1=R/(=x2 HcꩫV+U)X*֕A=H% .R TSmE ,ԥB{`dChlL!/f[j !x廼DBA4D(1"e\9=p,ffXUcVlV"I]axLlZ*Yy8f;5F؁Eo':AE ,SUdI46"DTajd#KL*g|;}79{[1'r{v`0aD[9%`dnTaU|hi,a}ɬl KvL=tl)!H{fZ 0jkJR֡M72x0]vv zLe6Ŀs,k K#=cNĻU"޲ߴ=1?55Dh si3ձ_G4'$so 7'6!-!x/   D|T! /@Z9I]/e6NHUDIj*ALPG$|,XIZD,K]&y,靬*dIS[J[<GyW?O"&%e]صm(+߾gDu32lL<Q_G|BENtܙbr kڥ+[nY$UPlOkcK#gXn0\ /I \$D6(7z9KsڈBpK dC UTHK -%$ShX? U%Lun IN4\əinjıQ%T ȒVi߄)}][V`jfGi˜P)`厚Ә#'qJ\̴9,|I>'L>ѧcD"멬]v˧GйsgJKJ{#OGcBRWu<22 ej=VzrkB܍LK, .7SMf:r3?Ci%CsRe?0dZ/QlKE4}BjeI!)m ؉;N`ٿ/7ws%FȱC2JKsH7=O<9v(87!Pnhw21Ÿ;V7m 8â@J%֣Tv\Wr$?Wڌ C0}0VrN9}.$ v!|$I#F[ 71I$o<:EB<'z!lt77<2W8&ϹgmsC ;F8o7)(x=^ )/HWgU>ގ%Ftmp {iG^Zӭ7)aS'zj]%1Dc*jٗ9sSbHS.zQO-т7i!2U[ I$g; H$QkϷZC"ɨy:I[A Nu-ނ[?h kTCDѨZ[T &y{Hi˜c'4_k]IŴ*[~ L}93>y[8^vdĚq !8P)$W8I (:3+L0RɆ|TDJ"TI}:F1}Fdf;/S^j'/4r~ I/[4YVrNRla#LdSla:QN8& L*Ҕ%Lm(mclϑg%+%J5O :!lB':F]^KxDB$kI.7y\O&hM3$wɔ,$̤)6*>E g㫎3'.6y}9&E*鑣3N$wf(8%JOJZaܴA"LZ,aJof%+QS @G (uHTR ӷ$R)4OIElܒ*)Rt[kqlɖI1"Yahnw1SpCLt-IqR.Q˺M6%KNu5Q2wSqd%+2ISVHR"*F!PF,+,(T|IGtmI1HdHiucdJ|[B15QЀ2 )L12OסɔmQnqy[,tC,bmLR%K#uBwF&Y”/>h?ѳMLe}Y`XDim钨 iFt3K Q)Ll4vZFdR-=(}9 h&$KP#$JA8"AiI]%G=.OH~눒3تS9!KLL&y)+|I|H";┕$7T@A+—LoʢգPnAoQх;Ql)ݓx ΈB`gF2e;*a(1w*^WKj-|JlVbi"P[Z{:o8.XVkl5{3-I28Ӎ:%JZCpx;RYz'Lʼu;LY9X%NY9e+|i|*F$mK{%$ʨ/WJI V)i[)mDIrI`bnd #TI4HD J,CoLbRv@ (%G$g6KS$JI7Dɍ~{ KCpOTY”CT]gؓ0R">I#Q4!ED&Y0~\ЀhԤDzIEjt0eΜP] !%U #E,:.kvi$Z"W,39X$yR6ޤ%Nc`Xsarӱ:oYY hW -[beõM v"7K3gxA0ep6-qko IDATXndaS|d-4&U\28h6(u~ɬS~guI4N.$ZZΞk |?]Nf)!!hGC8=d"4{*O 4E3f_q4MrIDC4hZ~ȇ]RJ[E2id~.v[7I ?X}~0Z>hi BAjnwx~Y8nK}$~蒋@ހZ"!ld^CwrQATuA!_$[ca#h[BC{b y\~kl\L˘נapyjYk43Ŵs,U?b8|]K|%v;UHN crI!m&X6Ng&1؉̹-X4X}VII>'VP<_E2jʦ-mLBHs'XCWpn<^?[&$JAc,:KhN|hybٓ(㡛RT;į-N%H6HYqHM gNME*?|W]9; Mqbh;&SKD1R͢gWsET9N^7:%NN&Ч ~&Z7R§WqiGTp `м/~=dԍNb2*d:͟ abr1P4L9ӏЩZ<caܝ<}u-;yo݊Bu;Xx#)2 v4ʯ^ON+ m'7qsx^feLg:9x/e9_*kdԢƍ?j=aO B'=>L]kv yQy 99ٚTT:]^1Q;_Z7)c]UETRwk8n1'<َ6RYѹ2ڨ롯V*Ty{i!,-2.8V j"ټVvἳGq|eg?yV5h#"&.uwG7glg8:mjGX&BGfn-v7 &ic!kcX&3K[$u7W@g.1xz~lJi%ENfܸӹEUM2 ZjsU'q3ʿQuĔDE6}hdkP?| Nh6J7~Փa~_}Փf\='|ָLyyLoX4&^3N1gwwwhHF/K/ggȑ{&?{a#\5f5, W1Q sՄ{/#{YX8+}6.8#wvѢU_5E_.Dk>jB7bzL>ϿH?ݵXJ = "ŵ m\?eofg<>%*DֱMa7d휲1ӗò1}BWg%OZtҏ'd@:#N_7>8x?G4ϐ'<{|4 c&[oI;:cDŴ?7ӏs?S.'<«b^(2i'F+^z֣h?Ysy2^Ólq+|׼8?o̙/f;U̞N\Wһfuri3`K Q{'ǻ=;^em3IQˢ?'=>sv,NWbMxaYg&<8s&ʜXٜ)Jp~g->?`l?u3͇,swqR7qZ37<%o5O~{>iz% {Ke2Q%0XW[Yӏx; "߿f𗷗; ]@ TE\+ULt6o^1_Cs"'{_agKy/Ki(˨QܻL: |x0-76_崪7a1u}o>Ț4,妱>L1Uϒ?F^ϖ+/($kg6ࡏY۸Q8T2=nsәkL'ڄczZbag1: 7~M#47 ún.p؋)K)~DcFSCL؂UGfMK~^7^IK'^)ͯx%^h5+M)U4Dkrq/C/ױJjir8g872MCǍt{lc#!mP~ϞaQBd*_3 ͠MD36xMN1-Ap퓋p?{8SN C&s|>IwU0>L\z5l M+M_8fRM(iCAN꯳ B@=d>&Td_Tx*]_T7R @)篦)a)kI^/^%IH͗~ 54K,^Us1L%hW)Ы9 rHɅx=|;?4~g.!V̺z^-F=mq=ETye:4(Wރr_׋M7a\pXϥchc۷{)L*V5Ϝ䗓\EcMΗ_By\KYQtrWn873UJdGf])+,90SH##gJA]o)] S/L~JNK͜3"Ҽ_C"bG8gbBZ%་yRza6]˪<9v1èȩ`|>yƎSdZۯcZ|:-;A`N>r›yw,~y%;S|uh]9> i۵2(K%n]ټpOw^7RŒg_d>~ݴ[%Ħ8{[K)97KNx3s>CEO=9&'[7R'ϼF:8n g"ꎜBÙ>Öm X LCli𒃟9f~]83G[keOTh5=$n{g~o4U1ŬT 2c GL4=O= {l?cg10Oҝ5\/?Ȱ$0SNhh]pֺm,,ϙHq[1΀iZ`[Vr(I&E(g-=&΢7'MO$6ah"Vƭ9S>u -|$T73$4˟gk|7g1iƍQ:AqJ#oq+nSNepPS@~SntP1)(4ॏ{3gH|=%V*F?.~^_ νOxˏLt s&qx!@ߗ)Gns~Fh1ƕL3_ΏnGju]-耈n~KgNcќ8Z5~~e((;1Ǐ'sޝ'|6~Obuo'|օQ?!Sg¾g0$>Oe:esm3׿dtz.d)cM|9>n<<üδhp8wd.&g) V kC} 0Llm`~}qXHfgqzosekrMY9f~N|%jKbvrޣKEc2H:qJRn;N~?gþa9NG !,g̀SͩJBs*H3QD)+Y1)&L,_`A+v1 wlfr(fqu& hBKH8%SZsq?6a4D Be-7N0-[&+Ֆ9ѢS.DYNb0:4-]jGqoNfly?ItB4}2)=qKAI"$LYM9ZzUI,QJVĦ0Tdŕg[aLJDw~OK߿;܌ʹi\AN~i{yH-$G4AJ?$)CxYʷIVD:R#NS(+Yq)BHndVMlʧw0d3Bx:8 `_>d'SS[߬`Đh˺Svξ!!BiSҪ[ȅ# 8Qn R%JYJ%FSVz)( t5wtI$^%enӶVx?$ڐՈJZkM!3k$)+Y9$qZJVrȐ) #BޗCR#pM4&1,WQ\79QQΤQG m@2 ;2K:$)+YfݧYEQ[rb吕/LeWId3J"g<2O0Nhv~}H&6\cۈddtFt'Hmdŝ$)ՈSꄬ׋)H^^p`s@ĪVVV%G_Ҳ2ƌMNZt["(BetAR% )NaF\$CdֈVW[GYYYڈV4mg84 GZn)tTWYB}]욍ĮkksTee3&kv[n/Ĭ+}y.P_uuym=H7@rܻ] v8m.V ңg3⯶ݻv3~x**:fV25?67Cne9e-2>GRWW[g /u!&_ IDATҚ577}{vӥ[wBAZqUVU`zA5_[]0a<RC ɤڃH'xc8Мd1P_gY9xRPXĶ-o榃`":UTгgO2⫱;wR]Sxfr #UL@s⯕j.U];H4Bii9M ˊ./(`?z I扐kTH.X@NٓmlldΝTW0~ SiĬ52܉>٨:!>,n6b;+(,9T tea9n@$uTO!4q5Sp4BYYyr`j0N:nx=B ^Oq9xb}=w,PJK(ݐ3?-ϹV1gʙUzq8I (;R 3wB )i AmZLsZ*:rxڳw/An4ŻAx?/2GU]SKEGrV,ۋw"mvR_W[o;\{b*1^F'~GK(fCClʪ،Ν:ѩShd0k+U:_ Wˍk[PU]Ց}JHI5[RUAENJ/(Uaa!e lٺjAXKIFٺmUTTPѱΝ*ۧOھKGDAD!?!DP5ByA@x(BUvOjA~~.d5v2: ߿׏׌ׂ{@?A8#ȥsc@M |P[ ΝkgLwǀ#u.6 ~wT5(jW2@3P Aƭ||><> BL:y摟Oqq1TWWŃOX[*)}+>(쮃jj$"e (-1A6ǃǣxP%|Q9UnU2/}JӷgwmπlGW7 #Xn?=y8+( oUlۼ \p8?Gַ?C<*ËqaR՛M7jiqɓ4m^ar:"O^o!N֭ҵK7Z-44ӵKvLnn.#W@;^z9T_uRVVJyy9}dݺ :|-N1UVqTy/ƛ˛o)3g꒾4G/Rͨk8իidFnm_Kjjj^}6?i򦎯N?T>( Q& WZVy\DiY &xW]<#-2x0''LQwB,Y&<EE)k֮%}q}ǭ|yQvl%!֬ae`q8j}nQǐ$F/8~Ա|},`|u _9c9rVD˾X昸߰6|JFȀNl~_(QhEx4HSWJDQD8 ңG3>0;ɏ''(Q4(-@W D".u[:O=C@N 1yy1]2$&0'Me/%FJ=@by,_o~\ 󑛛!OHWL’%Ky/:7u=t)]R HlWhrHM=S^O8iɋGQxnsTY+QٲS"yԇy~ ӞFҐO4oN>㉑'PZb8 -: !B+6M >P3G:%zҔ<9q#>p ;w>☘x(,,O>TUWW+9ꨣӺQ!9dyrڹ3׬%Bؼy-k"%@&0#N&F^I>L9yRѣYzuRǍ쭏 %d_=xs}*/;bM$^$qJT9yzQ&Ĭ]M@9r|OB=sP磸$W7\ s )y4 zwpĩ2 rӷ\ "U8!Db륖G b$iߴB ަ2 Nq۳g(d'_yyyɧ_;tm۷ѧO_Qd~@$%9ruII1?&xhk5O/M8u+LcO J,gUQ'L{̘6vd2)&JAx℻v? pn|Q^vEa=7iL4֯[W\A׮]-?w0owqcGҽ{7ӛEr߀+Yw=xNuuuwExΝ;EQ(]takZݻ3|PckoWHW74խ+l|F/Wd5.|[n zbdx}+Vqkur*_ZkӧSN믳zZtG'i=|rxRW5ܺ<^CnlA(hAT*(8hav l,1޲vv X?+_Frr ?>믡WV{c:}#|X֫ XtFD`r,䢗`(G"Fرcr^"?Fx<MްtR;.71ڤΉr3 \&vZk[ʵqzR#OKAY~UYWP=h [jv9 FK)m$CEĦ}FRDk;ڢAH$J?Ċ{Yy/_ܲ㫭VI?D8%F[u8YJ,@{{nkbǿޱ]vBTtȮ;i0Ze tO doe%;wb_>kٹs;w@0H tQ7JqpĠd_8p m0ڴDcyGQn 7wZ轫  (JAQ#*vzR;Hi"(tB I o֙&&@̓3o9yF#R/C⃨(:Dݻ{??"d*׉t ӧLhtFՔZZlT{i[PՌzJKYx1 `1;}fg{dggg3o}1xwHc 7VxƊGiJnwGhuW4O=oԸ=rUhF$QZZQ>hުu%eqˬ_:/7c0ʕ !.LE(,TzF%(BBf4WQ_^|)MǽIM߾=ΆZy}AiHj ĨQQQF/^J/@R_{*P\v (D(hrFx/>qf>۔k(Le>M3E7_پ%j"XE@vͫ'J[BF)lQqVH|e .@|(=6S8IR Ue~Zp:`)-%;7OkdZ99J0 I &slHrN_7Iyaر5?7-Z]4U3 M A1T7T>7),,d߾A)pGmx~UUOn+o~ 6lgŊ*{3ϰuV0aW\;p,ш^gXxݺu;>[>d_tmذ+Vj+J2SY_I֢ExwyyG1c?_Y+]z#Gw^jut#G0 2#KP~ip|ܭ~ Ⱥ[0>r$(+*JeQv[Rt]9(g@TUd~yNUsϟOQRJKW&hTKZ11T\ M(.y-[eA^!Ka IY̫pMȮz@l(JяNhAtگ%hD n)7ɭظNmƍ/pE~'BBxs+\9Zj({ݕG1c}xYJk.>9 %| T,@2urK1>BP ؜PlU󚼕$ōNpH[q+_nl!Xl$"DSC`H=|ݫU/=Wsr2XE'y8a']>s:h9$b):d|HZ-={b2%ijj*7oz~;4aqqڴQj}2-"'O*ak"v+ODsPWu+/J4TsKYQZ┏<6^:* \WVK>#TA6mP 0xje( Ox@])V/WT*<>&baa_J湾GDuJRƏ?ȋ/ɡ# IDAT'9~8_.Qs݃<޽{9uSL:(+<(Kv_ѣG9pM4AK<ȩS$''ӰaC骩,={pI hڴ)IIIC#WWDTdYG Z5&ZjiҤ\VE(%X%8<ʒc1D,|ٟk_`$IZ&6VŊŅp]pK.ᥗ>Aݾ IEBcQNUrKLTlQQe);Μ, ~1v%Ho{@&U*]$(ʓכI`rIYZ(V ¦R?~'H e!j5xН́91F~gj@-@<{_ : E V7*IM4;\j6f*n= <֦ȡLmݎD+|mV+?_G{Mތz2F)gR]3;Z)`uj9贚d I]uDQ8Pz{rooKebch߮lN{ =4kv{!I!=7Lh4>lVq1q >sTb@V7Dkpw]5>Ma17uCL:'0W94w.FbQ$ CIa/"QzbHLAa qFLʫbbъ͜ξ#W_}CK.gÆ h4z͜9s%L'>B(A@*Ox*ڃẼ5NmZ3;ϸux5iaTwjsoE)*o'p8HVQF\qmۖYf_hDvy?9s&{˗}6l6CÕEq]}/YRQ,F^{5n6͛GBBӧO$˫o%Kӡ뉎QFL&O[ l_@h2ÇszWǎ/'=Sɞ2 (aD)ҕE&MNԈgJk[V-q&U>I,wLz׿QHR[D'%%h8NvBEb"\磤o`Xh7u*+|t"`ҫWoի7{˻t 񓙨hX?R;b9)::^BޝA1gΞ9FSG1o^7+[) zm%1!.]VѵkWt)ڵc`J/ ~d5 7ʣad/Μ9CrrgSO=E=x)..F$rrr(((Wl\^$I=}Ə_n^P%gԨQ /0eʔZM||<* ݎN| ,ZM\\111<# 0s~Md5_}4a<`;J nbRNVQQ *.Z߉F U$IqcԣP֠g7oه*vl%n^JA(ӧqEQ\~w$LvD  ȠNE̐ E+(v#-3?^nz<< m-JxX%6 /ˬ\ڋ[y0蠠44Uʪ7G2=RZl&::.jU`ˈjh I2@E :J逪QLbXlּ97mW^kI8tiiiX-u:g b% Zr;JQkhִ)͚4EpE4o,{7k֜̌ zj.h^CP__­ހ=r@-Yٳy5k ɼyx衇<CCNvF;Yk_yCBX%ٵҿLb]`kpޗNFh޼2硨!?jʉI J'cTcEUt2dC)ר&AEdYp`w g.;g= (:h4ȲZ7ڶxZAP]4h5:iެyTNG-0MuztZZAr:SpכRVemum}vSn]&?%bX F|{ϡÇYnÇeС[C72j83s菦Mcٺ+Ҏrx:%9۵gȠAt,el6>3aÇӻwo,X@zZodd21f6mĝwIlϸ=djIj5L&tZ-AaK=t{k$%1xXd p8eҥUkҥFQ0$'&zA7IoGt4@nng+;^~;Fp#q)λ̑e4jLIEsm<#Ls2Yg { ϳ?_ ?^oɼ W?*p:X"O{ر7x#ڶh4T+}|ͷ\ӽ;}aܿGNiSy-%]wŜ9s/7v & ]ddԩ|ǖ_h2Y:e z^[;Fޝ#GSٱciiiL6dOk ٳIMMΝѽ{bo `sW_uGsNҘ2eOYsĉtԉ*$Yb)_P`P8jVꋹ.3eVdA'!Uܧa֧HfZAndEh4Z@f; nW79dp(w唓u( %%e SBBh\tDY"@C\dA7؀l73R4PǤFq<E۴%^!˽.w`r.3nS(dY>PjԪJe/5!#{ $$aX8z(g_^'59QT`F%G@i\@rdhp8trN':g+.*b)aS\TDiIi\?܃uU ؃AEz}Q~(dQu?t['JSmOb|<oգ<̤݈ԱwN;w/MBvv6C@F}Kvv6]\ƜA!sWQ\ROt JJJ(*.ɓ 8vGg<9s9Z03'7 FqN{J~F)a~ҿ' p m+W'66dT*O@qwaQ6"'[Z_РAT*gΜѽ[eI*CD*#NԈY(T5k1 #UO,ƉI#"e&?!DE-fZCiJFQ$$i-tOQh*$Q,[rVIl*.DYܴ̅׍U/*ӣ2$-*T".!qi MҀF(Ur(ty\: '/Bz^I6JmpUdlf+K`ʁ$?/$D:mAGCr@hS/**<ȭߧ:Ι3ghס(bw8HKKTI8{,yyHjRJå>D!ޱ(Ox}7p Cj le=_ҳBLL4%%DGxرШaC,bbbd4b0h4FsIx1T=K֕mݺ%NʀAdG Y?In*տwݺuqPv 3d*wjbЯuC]췖?/FfЀ\}W:ޠ~jՕPXA}v޽S餧+C7Wq]ܽY֚ >yأ{7ڶmÞ=TuUWҥe! 坨i,"8e%lغ3*<CdףdjN9 qTmWEժѬ٫6@VG* m#119|8VCɲ[krt=Ei}n0ï(sP48 J=uO4EYǩX.8Y("8"2Mŵpt:=S U7]?feeҔYZPQ*Y5Gy5DظǏf'IQQQ4_:@A$V/\ELLtZn'''B\|'PɚTR\eP\7l.Phs)*ǏѬEKJKB Cwwh׶mTns:8!8BsCyc >;\wbDVDօ &%G]+)Ush<, *XBVt:0H^v!":Ez3|z>h ˢQ1΁}8Bjqa^,3sCu!Ȫ nU)#E.s$7hY%\HDqq1 jj~D#g.2#".Y@ ` Qj]/xGP * j>p`oD"}lksYYᚪZ;djr53F!B$.>v9u!Ȫ)^͉!B"D!B"D!B"DEq"D!B"DpR~5{l&!B"D!"c[Kg6Yf5/"D!B'Cch"D!B"W"cHϷ*˘ 4!B"D!BDdl~A ̺5iX#kTD8H((aCբVQ"&s1H!BJ Нbcbibcb=Zn ÁZqDEE %%%NOawm .6i"DP="c@n^NQiۦ-VKEUZ-ڶ}8N4MwN'ZZ,+2ZUW^A:uKM\(m \feO(_ܺRlfZ P# Щ36~bEd7itΟ!77;vХԩ[?dgg}NRb~s1 (ynbjKKʤ"KE`;  jɉ5x'*q#_DQ$??Xss/B^JBRIGVSnة111TqZ8@k=ߏS#.6a;m \\Lmbi3STb)llumdJ 愸>EJ9N厕Yp+¹D$, ("EjtSs>hTմnӆs*7ÇС nۖԣG񓅟M6.NmfNeRJEAͅ0Ct5t4fHЛbZXO!vRpQHͅ]T*Xw< ןM*GcPJ }Og[cvf${"?p#g-L3ds]Z)KHxwwJr۵ -A>oUj{%?3}Ch;SO2x^xyC3=EsS>!a8*}7Y70ӵ+]!za`}p&<_Ųsڒ|`QhT6saL*RZd86cse] 'ٍJv})]TzX}xan *Y|2 x93%8KVbY_ YfftoZ0{ngcSl'Y$#\Y6)uwWX~-?.|aE<>OJs_EeIڀ+xcoov8ě|\ {Un]`3zaTĬc…0wl-\ӆt~oxk_>ڄG-O,ۙw>z~]:9nNOߔ1{C=gs݋8fslc!<,b#g?}ct g3{W<9kl:2w?)fd` cvnlΑ?b] us{\dY,şw LҖ}`,rn1C/AԀ˯I m̽t6Lj IDATyϊ^3UOFQUaMe$(-*A>1FðY[ Gr\[F7QhSzlTsq)NJ)~cQ% Ҿ8ٛkPnjSE V짤Rra,6|$۾㙤Ysm6@TE7?q͇5l|61^]I&Nv짰݃|έ_;>>[z ;@N|}'>O]bg ӂm4t#tay 88c~-w( _kX|$o߯n W '/+c۱}k9d2{n|)wO9o/C.iVRd%rs)E^y^>7:d%޿KY<~Ma 0*?R3Y_K{_>3#4K,¥i>/N8fn£nF߄UGFf:L4I4勯 _,ۭjp#s>Fn| SnO1! eX"Qg~5|/ɓIKK+رcL4^{5\E}E,1)i~/j~$D[zK\uΩ޺u ӧO},O?iӦ3u*{>&ͧiJ:OwN;Uq"ۄ7<5;mz~i]_S6Nah BvRswnϲ^^ v Nπ3Yge }+6mɑɖ0^46iF7#0MXᤶL~i4>^<ߐI,ٿ򶪶o,,@d EؒoΎ(h$_}P)ԭZ_(,64ޟZ"`= 3긠NL|| 86m.xYvUةSő?zUSbLpVC  )XR `";y?"֮[XbE 9MVFa1>SQ{J)j֗%OPY)u8&Zrg>sEftTFZrnNco_GbXZ/b?/p\VOh,fu]A9їwCәᎲ,I^\;N޽;G%dao1ܽf1{K\[{gfH_gj2c %Xӵ_~{ڽxq6mDtI^ǎׇ`9ɒdiOBM/9jHL :H߸5k 6_tA_9fq Ot=Va_ſA]ʹe+Ҧ"j_nIy-fLgFt?-c\CB|QAۊ> 1h~@ZHX#]n؛F(^ۏ8n p6M6)w'tYn1h-u&ύw[I@]2wr`Kydf:4iRn-Xf-ofj\lUVlM#?ҡOss@T,}d9w"z;>ӎ]/6l3O~)0>O]Mzq2cß&HϰtTں4VݍFF ۖbrKu>^錨J)>Їf&!ٜOؤ/ [&@䨢 c[qO7zܳoGf1|+K ɪPh"ӯH~Kehu*CJyJ|;_+}i_eu~e\W86w m; Tf&&}GV O/cFF| jSb>`j!O/ ]f֯[M"K"hhSĉ<'ĕrqrYT^C04񙙜|=zj)%.;r-b ʹ*5K}*"7JX2\ӁlǤ p,`1=+^ʹn:F7*/KK2rLɟrKc\ I0{`Y9v'K;3}P@ʩ h,Tѹ\rz..Sx $Zk@_bc;t%2VAe$VGY,\z%y';vżK(-).wG =8?/R~( 7v;M.- DKM-k:^E6!$o@ODZ%:((u"E#bi>$Tu6Me[\h?bKL0%YYI%U b\֠~Nc7fMte3_~]}M i ٸNjZlg؟'y"Nlx[ꑡא>5OyRR~JY,{7.ͯ0DgDJʐ:[w瑎ay9M|sg~=̓_sJ$7]%|1 ƺLFtTʖ3)^)a޽wx@PZQWzcʥy?=ﭤu oBI!!Hs֯W8OguC8~xGأ {dtxu< @mBxv?ޯiL=mTgZm \Q$hDXdȢ Ts\U u1m'@u TNPPx;.]q_A#P2{^ђh .:ؼ?RCzno [,cDU~S` -fek*_͕Iu46*Mr^.mwT"n F:W/#[7vTRD?WtbϷ} q޽: &wW~ʟzQ}9(&^Ũ1m@QJC ]be\lӛø| F\VBzAY%8_JrD OpKNMUbC(C)+@K8/,!KN$]Z,Vw N¡ vf4MF0˯1zA"cKȼ׿0^RH?68_R"T2I\Qya-6+!Gi0F=ݸWx:% 2pwg 2/mx]K?:&3t`ƹA4ɠǃhf:C^0ī|ܧҦň?:l ٭=e(YTĹ~erpkb~]43Go!2dc,ó^E߼w6]”{<6wٛZwǷwfˈ;Srd܆ ԔNd ЙžF2x3FY8 u:Ąxf8OK\gK,u 4i24TQ&utڕs璖mn%//&JZZΥk׮~Ss^ݕDӧ߫ U_RMT\AK~s88F H1-nC6P2 p)ۥT{'q_ЁCm.mI̵=,g2JE${.WGٓ67o3^Ǫ෿SX[r2[2c3z# Qczf? xei%¬p؄}_`=dYE$GCkꪽ#Q3vgQ䧔 N-ڸ&Qe !7RgESU<ѝjlh;٠e,ݮ@?t\n=Ov|ƓJELl|"D xx׌*ݓbț8zQ<dzjm_ӡ};s;} qH:U|\-CbŢ yQXX^/OT<Ɗe?йSxpsNZ.:*c8m \kKxj3,}:=Is||k)2t`ȱ5s oCA7r=9NWM82{ok!bim(~͌Hg7΋`"ZvgJb&<$G&}̮Q8:y9N>DǙ߅X?fuQҲ7;z-ٲ;ABv!l(hKǏ-PJ@l(/-eBY-VBB$d/ ;Oz?$O,˶,y8~{ιoΨiN|I30{+F M1f,6mC~pyQ#wB8& ߏjX "9ج6iv :]u\\. tnIkx=l6Kkq=*@)ROgz9iJO:GBHAA.͓@p`^g$  8&AAA42I   B+D G6xVjc=T=UTPRUiL vz*(`טC (%44ꨨjP#{ߔUŽn0Pyƴ@yJx䫍P=kuWq<~Hv3ՈQ&!|1+!5Êo~4g*x5s+~=+ BJ9 jk_0R1~E%XJކ ) uԠUM/Z #=˂ C\UƷNTix++(3J+>(P#)G DSJX@QuT[Fi86\FxPKHU64fGJWJ X' /&6e54?EA {+C&74P۞hn9Jc1akią mvU(LӴxp%PRF}C* -\M{4d>Ѥ7+?R34XݤB &850pC}-4;*rZ`ё*Ci"hwEL/H -M7ؤ#fx|xz3ŚRz1팦wjMH z5%ޭG&zcMƢ#ލ1=Ai3t }jhb4PLH9+݂n(z± [jf›Jz g#uZLA{rh$[j@d >8)1gP#ݗ   =6Todؤ]?l5AzT{Uz ={$[ A u5 IӋܒzAAAc?bT=Ek   $AAAZ!0Tg`gAh P[տNSAAJuʡvY-C]z)|`\ 9luAAAH2 S$ɇ9|ЛUCMo3`H>  BF) 'n=cj*gU)1a **N7?@U0,Mu)͵zfG%A!AAA:N--geWjq|$H}>b^җg*VOmyC8M~]ĺPO^)TlJ!4_AAwzg'TşZ\S{=eVKCFzr3Ph"k)?gvj\~!XU';>Ó䬻4+QWoe8Ttǜt _4”2Cٮ-||Q˿Vqg`X}'_ .,}y3'{㏏o;?tkx‹[]DK㿿Mn%L;]ɉ7v>]ˏs{#v1g/K߯albC2cuɗLO` KG6ژ}N>FMeԯx :%\{L> ^b,?~[__e~'X0[0oyއx|7rX{xɦnr&QHP}7xec} [~ƴ ]x_pEg^1˵ӗ7<dG| v[az9۱ܽ:$ӡp;7~*ߨwV6 `wq5n-j@zc{pf]3 'ܫǮ\1gnoNc 9g)0$81\XqɷȝC$צsTObqAb3Xea}1RȨel;cr*y N|~8)BZzչ/3~(ulb!x+6Y}m9s5U[ȝ[x} g1>!FеRZKHZ^uf&uOb7k9-6׮ӆX ʷ}E'=K ʎ9̦yZ;7TS6 3i{%^V2y=_T7׽K\Q[Ãt_j}.0!V<Y]O///+e8p}$nK辴t{vʝ;͜J=)?O~ٗO_%xOGWseo;b&tQyx{k.xm-pI%xdJ}bg@QԈ> ~+Ǯ=DՃ.Due V:n Ctٺ0ea((}sXUJp@#WRk-s9.#R.*|r 87Drs<0;vJml6$GUib^ßX9b:ygxwcRc: p+owOdэHC:|x+)dX=Tթ) Z| lJ߆:emFkz[;JsgJju֬_>'܌GD+K 15ڽ-_Nk>#HcU-RGW[`fޏ&w`*޺}ߌbX=sP.{iTb/sx'쨻$/b՚oq0nZLv~̅\ fٿZ}E{i QYſ>ǻKTf|Ss__MndiWsv r*Sq9i}\{V&c<bYa7WR[J[sO9ꪣ컷x `C"m1yf{a+Tw_XG2ܾzJ> /}~EI'kY^Vt{;|DG,[=._=}M9ȏ0(urO_lF=1!E ۧLܪwȪ:Q|*~?(FsIJ{Ї('^1-zX¿X?1I@'ZǾ 9-{MMQ!6L 1] ݟ&vӼZ>z'CN|v3[*^*QOp_w\i˙%F[82+>$MХqۮՐ1,qFy_σ?W,B[Xΐۦͼ?50}nd? GW۫.}3t<-( &2rmD-gOqE{ǘh'8q/9mS9ԡQ:_lOw1Ny \=lJO3RpףsCi]K3q|voסҟ.DJd˸7+` :4P~'ܧV|ᾃ7!ث&4O.뮻#Ax`Kut)4;[|4ׯ+)ylH?WV<]ݾCpϕׇ?m;}㢜dGj/ /7^g=^w8! ruoeOQ:rKNu^0|ZKB7Mvgg-n ŀ͚/fܝX-6 q=K?^pN[Aqy>) B-Cft]_jHEl;jA?b&KPs*S ғ.qk|EiUÇz*jEAU;ngڴdffȨEA.M,=O{6q R(ٸs }s7g%[ }K/qW/L|t'%y .y5.6y?=Z?}Aƪ|q#lČ;ک1& |EuJKvmKKط{< = b͚o4i"991$ $hӞ=#p.üxѱ$!ACk!"<CyA:JG](bw"v;*ءU{ψ#(--]_(jelݺ =OiA:4_k0mX^4Թ\XVTUl17[1ʚ   mܧV/XiPMC^e.cIlۺ%Zkعdu` #Fbݺ_xP=|2UAф?MiF p (j`v;0ROEmLD%(l(&OC RTcװQe5,_m(? NE_ J)A@V4O4 jC퐎 AՃBC:oJD6 B7@%(M;uFi$LWEA AQPB7E"(5ʔP!A]!AYjC9:iC%ڴ~`FqJ &Tjt >E#7h{zB_%l,Lq=1P`-cŲd 6{Ql#2F;d?IYլ\VLqvviͣp|W (pSrz8:T>V|ScLFoT/_ \Z{ B>ݒ/#bƝx 8L-|{Nrt8ʵ)3Ycwb& v̴e͖>Y8ϋq̛JAŖ k0lx u%;t{5J}dGv<3etOGv:´ # ƩosꫡC9!sS㷡g`ҥaUa#E#us1~@էpbP08HWBofdBJnu%TΣtiiͥ˒@:PTt C[1:N 42KϽ?CUIQ>}5!v'^؝xxjhҪ>(hy tv`p *~T XÖ́T]҆eUz6* ?NRo~qa)-[ "p2tZ<֕ eAڎmwh/bwCNjw<1f2gKb%xoy}L8@ 5Ven,Pi/UCQdj~,H %JmUÏ g?n PNeoIgi>2?B=fUm0( x]^_qڝWQ60ڟvo>/BRl=N2`/:k#$Ewm i ʨb `2Le×1͟HAm~rs(t}ir5IA0Dg_O*|قSm'ۀ,` `֙Z`5tvgW|7;BN;(Gl]*<}Ұ[>F-.g䪋P޿ 6‹grʓs0]|r[FS8 yA?[eUȨd ϲ緗q_+[xڱ{.~Oi#JXK ^ouTw6/[~{pĜ'j`bͥpz.}d2m~& )F}x;l8KUU|<)d[FOE66HA!ݤOC~P؁t~X[bV=v@?0zp8szW=>Ũ7% \;ƺjJ>?Ewm"͛Sp]yo.|.c^  rIWEGq;/MOʷbW붗Yޟx8JXw-1Q|9xIťGGv$)/[NqzO痿ycC?*2FOŦkdsp%OCt0X8l*jZL'0lRc *m 4Y1f1dJ#c?{W)1z+4n\ԏt"+"/)-bc5  ,2ovUņ~2C]?*p+5m)zm?f0*=NG '-!=S#Pj7䶤C(.8?k& **K7?w)IVWV٪LF}@}(MJolMБ2wSQgb7~ZÜ1i|Yȴj>UK#T.O3Fn:MEW~ϧg .p,aձ1knbx!/[Nl;㴮 #G⡇>}Q~xyu=}םĐ ߢŋ1^a:FS`BYדQ* ~^fTj+#yS^}iPir(w5ʬk5}~Ti2HyܻׯĞC adk):akD-&#Eab%AlLj{p 6RB~qJl;/;^K/LZZ>eG|TVOsgK/sے?5ϨҺ?r'mmH-p7(T+`w*ԣ~RQgfxMÀ*bF3äPIi 9qX,FS9?Z2/։/'q ;n Nm +GN=z3)*aߣ>Ha>/n=fKo v(3zw=%Bvn`>+cTKL{L?{r||S!҄JF<ko:[GUn^\k4\Vk%xbwe݉-v'^vzJ]mu;bywqv-ҥsN^1Tm[zIiƏ@ _a?z^ rt!FiqdǫT?_2b0.xcNԱ ¿{;a,4sԚZ%$϶XvN2n:q]$%<>*kjf M^\&١,#w gm0} Gsќ3oY1l jZJ?dAMOb ;Bo"us~"Se݉-v'^؝xr>/fP-}Wb\ǩh: BII ]=JVv֮e֬Y-X[)BeTGAAGF~} ߫ 4 @? 1f},!U[kg_9e C S1f.}Lnj0῕ ;ώ{Vp6T5ݐĥ̈́77y v/S{ .Jށ8ҌFN?'%}k9#=lt)8I+w}ߩ6^wc_K8>P"$Aw0/w?P9&% ;\tyt\2O&Q'^؝xbwe݉x-O;/D㤝vX^4Թ\XV<(u*0g?`D%ڷ20j ~P>: 4~, ^Q1MCgW>Û=B=f2O9^輠>Ӟʓ?0 ۋlYzwFnj" g =g))ׇzG Q7s@jNlJ1/j?_?DDtLt؏MQ'ӬSʒ˜'& |q/\tfI x5eBф(6F?}?Y~({mFlNS\ISe݉-v'^؝x;^9NΊP_Ť-$b !7ŷ,n[?n Tyʶө\/) ө /[Nl;hpC)޳r5m?Vrr9q֒8#_¶T_ *_΁ D Ug'?+. )J> /A}4RRrЭ+zp=dMCOu EAE$8|nbwe݉-vۨw/3&`4|~~?>:6oJmu ԃK noR@ϏOp4 !مN $`Z@0.T6luE-Sf3!U1 7xfXˏG-:,{;"v3>~Eu&1dޫɑ-v'^؝xbwe`[QS[1u͝8ѣX|9V%`ȾM*Xɲ9)[A0;u6j_:ӧ8HͶUlٰ<{q @CP3:ںK4R(Pu6-Mٷ*y;\FGA8|$ގm#L虯=hA7_sY=b6zZ("3 _4"Ysf.m͵tIA۝`4ŽlgM>I-v'^؝xbwewn- -oa9N1ŚK\ dL!"l{SwhpL#\5xRrɶ(If    G,8t-4G.Ξ   Bh;DZst)p^MɄbrcS}&S{gL7A~\9͆h44" $y BU+U8M8۶jm EYYS&OM*   ĉ8qj ߢŋ1^fFS`B>d#  B"heHCT?8h=5`o%AF BhNiP[tܹÁW իcۖx^l^AAA "zJU[֭cȐ!oj%+;uk2sOAD"\A!^4Yƶ 򪚨zM [vcZ{PraZx+S} GSj=LTk+`̱d&7*$#A_Cy8sf S1ۇ#7q*}4i 2-[ /A}4=[ )y֕P=8ƦII y mQt1EՃ~S'&ΞFES0P)&LBmpʂeGEi,K6")W@ +pA!\A!^4jPz_1 C+Y6'{+T]wQ q[Ŗ 9tQ^= |x\W>z  BA!9Nhiٹs'KWǶ-[zXd#  BK8'B*PnC w}}XVXv-3f̐N  D BhUxayզQ"n7VW8Mu.VHw Bg /hm^XT1. kعdu߲   ,Z_)z)QNJb댲;HWGAAN`xuZ]N6m҃ǘc(bnؽ(SnNäjV.+8;Q8>+S} GSj=LTk+`̱d&7JAAgA@WINi**ލ;Lc;7~OPnΜo$u M vdڂL˩fN!)M%h#/JS}8KQ7t&G+@+ ċIQ IPZQˡLԹPuvR00*Hϰ䑺?p8b1( +7@([WBFAAAV5 ӎNS. 8@Q`=.t)Cm&@ŏ $T6luE-Sf3!U1 WQ{ D BP۲6o#r,x^廨#l{زa7G.ʋgaׁpT3:l9y*rAA;жz hE :+Ǝj}f1b@RtGƐƠ* p\&Z6|d̙ض'7׊BAAg^)H/uCk.s)#i3 =L1:3ߡg1u_sI%ۢ$mtšU   $V']x%o9EEe9r60AHt'A!QAxѶI[jdrbXp\mP4>PeA=դ2m6.1 H  %8D$NA&Mmj[颬)䣧 Bg /t8i6o04Ͷ ,Z5kxW`4:e .O?w Bg /8mY) z=GY|IxmӴ*JK0VEAAxpD)囌Fz}+  B >CZ8fӖmTUW6jF!--1Fb6tzL& -s^qpx ҷAAAC{z ͓n7ُ܎iN'6nb1΋CU`\N*Vtlٲ;>/$d /ܵeHZNٿ CoF)))TbOKcI  &tR__i}>*п-77#d  efT0d=m@ j$-5#GN__WG~TVTnOTY BpAEMs"D04 '=͎jEUZIB>Tqr.na]co(3u   BiG%*0T/R8rUUcv~|O=S-BHvߏ_8UԔ ̲z8=\k0 7$9Jx8BA /ڸSyu÷Uy9T(=rQܥAǩGEE 8T ɯɑ\sU<ϫ8'b{Oxgs8;?{Wuuhfԛ-[rŸN0c `z KMKI6 !eBz ,Lƀ1r{,VMnj*$dz?Pts93<|9͍Q_?6"""I>pSk}dcׯg~3 &ÜØ=ۀ-|z;K;wGuLc{Wٺ N"""""GS)m:lR_Բ7ޜR'Sܰ" ", &/ ,0d8$>onTrqX4cDDy+""gJòe)CQڬG EꅒwqBN^zCZ633lZ㤟"""""GdA+bn=醯#N4oT=FlDSmg&!/ u+?=wc::%#w9dz3ijTk|*2IvV6~)~ 騼|</-^LvV6IDDDD/K"Tpc'#$'/.vrsh \X aGUU5ӎd 6vZٻeo4\Ri(p'""&TFqC{r IDATAayˇHXaC|׀ F!Cعk'o |g0d eedef&ADDDDDzlk4,AkX{K}LÀ#P\L[zdgx"9O""=C[I&h5Ufܘ{#xRޜM`""G=sED$]ڧ#O}3"ֽ2I$b;)ΐ￿3Ǟ`ɢ ̜}jWww)""ݠ/%ODDz&h9U/V5NtC=R?ta:EDDDDowquu+""""""GFt)ds!Zݚ7""sthV}!6\"""".ͪz%NꘈHoձ} O3 9d"""".̪rXCNq^qRrSDDb3WDDҥݐSҭ굼7]#N""""""IvIGnhti9Y @#N"""""$MRV=l-q:ED$6=sED$].q8ͪ>pv?/ ?UUGYh63O&)!'G\ICN7q^}LN :n5N suHlz抈HMGm0l#iteWƬˮǞHXk>|^ģgK۬zI8ɪlknsFģgK)qim<Ժ|ӑYQ8nH"""""gt69DOJG."""""G8FiUq""G9=sED$]:Šˆr9-]Z."stϔw{V]uǸH3WDDҥuӱzi:4U/N"""""rtV=#IґUSDDb3WDDҥY8Y.k-NuOVwwWDDZ3WDDŎ/SZ߫ pEDDDDH"zP:^%q*#3zNAIͪ SfŲwPytѬzkﺳnS9z抈Ht0eɪq8"SDDb3WDDҥuӡt-mly)q#\D+""*l2:6U6Nf""""""GӉɗ\SS.3Fq$"""""G9 '+O?&?fϚ5dgep8bL34W't̷s&9YձCAJ68].rdgebfz3:H_zz),ca``1 @0YGICN=WĦgKznw^0p8ᠩȓ' ļ߷uzNI?M"".1bҽ&M.""""""hIYDDDDDDqJqލ^IDD3WDD%sRN)NIDo3WDD%]S{/<_*?p͋<ΉNI#>|HwAn;[ґR=s:=owHDFpV=C EDDDDDZZr%%qE_[9z抈Ha鬳@vP SDDb3WDD%i oݝt.FNz'UODDDDD$H'$anBqIP#9"6mz;;_c==iKYDDD[V-{tDD$ N"""rD|YV֮}HD=#y!"p]FcMi [ }\'xꕈHl Oj8t.""GBiz%%ػw%IDz'pEDDUVcǎp82gɄB~v0`|zڎ75b?z3l&sN\ٺuv7-d w S^Ñѡ~B>^-{1sw7¤{͚سg)Lt3g~g&XVիyv}INN)Ç 'FiWS/j4$"""=nٲãMc^@qXƌ9>n/=v3˹S)RW|_W-KMM<lٲ͛aw8_s^۶xYV[[o5kq5 RGe:y[ځ8p` _3w#a}E'^$W$^5@""eXSo`ڴӊOp^yVC8HWxYγ^ʍ7R+ Zz2v p\k ]ݟ+ɧ98BJ8>9ܓ\>OަJ8HX@m.rrGm:sD]jkwq-/Iy0|l.4h*'^)*Ǔw,[(~7,+Y唦٭XÆgj(/ ~@0òB|{Zw-'rٿelݺ &l߶-}h4pDf#FFnn@#k?j,^{LAv""/ӑ̶m֯پ}1{x(*0a\֡UY?QϾNC@gɒ?rSl0xtf&c~%az:k9Mo䫣JdE=ҡnaO.mw.3"JKOd2/ %0NY m.W&3f|J}gl۶}˖=J( /o7޸'?z}/1dLo ~y ٳc9zճdذY 6Ѽ/ǭHߡ5N"\6s{54a1۷/ӡ۷/'ϧ W&]r44_OEEϘM;/1m E$j֭{)y^inN׿Lcc^oaJuϞL|U4pڵ볔9;ASK'^ *]߲'U-ī~+ng˄ n}/n9H)қ' 8\ʵ<Ӿu/ss9̝<sVZbz>^3k:UO"ʕO 60dv 81`+W>Ɍ񃀖9%XAn]DU ڕۓR}Æ͊y>/oXmwyFDvX=~Ƕ uXQ,/gD)hI$[V$ aLzS\KI:*+ױuBV0XVU> JkW?Kq8̹#N`˖w׿1eʵdg7"ܲeDN>fin`ΏS rrRH> CdȐ̚ä}_y; kEA1I툜RnqscƜOYLo2uu{YvMk~$o*v4zۘ??q]>c߾U 81iuڵ/ɟڎV_>N"m\T3g~]QƧ-DtXN>GfYY8`ӦR Zu5_Dk9K>|D~ǃMS';ӋaXVg :СY v%+k`z;*rR$Ssw$z bwrٿ4{זk:⥘S*+X}>VT,0h]EE pKKGgk׾7"Gh\а?z\T46&SܹϷ#Ktޱǟj2 It9_Ki@w6lx2 3UZV?aˆ &;mwz#FΦMoaҤ+=x_'gYE DgS(.N#)y*~"չ x饯]{g]E$L6izt4h9.g멯ϦMo#fͼ5ԩEl塇fquul=4;IWF'[?eΏij:m[TUU~||G{o"6IQge f+rN~tϓ+4]|Mݑ=:xpEE^kvϕ޶N@EzVCC%ϏMS_?ʘ,ĉWDl!>~zIWrS=ҟ!̞}V_jWn„KYYi:y<,|u,Z+-U>N#N")j;.`$.W&Me{Yٌu_s̙iU/[Fm6n|= ^~.]JKGHYÆ͢hLJe:P(>2k2tڃ= 'I>v3p"M7}DIQhI$tu?%nWҗ '}+w޶>744hw~9mmu_=~h&-[p8ѦfӦТGԣ8-< 8ӃǓGit9\w;qZv0Gg9t<|\Lgnx@!Z>шzq8nu)scҤ+((8lLӁ۝ˀ1q\p?wg(7"UV=br <}Μ))B ޽YSJW=a¥,ZK]ey :qsk9p`=[,dժϓ/Pҥl{/}[8th'=t2s#N`˖,XpG4ӓeʔZݗ.]&ME~Ee3øqR\_ mtCр +_ |@xD4]iƚ5-si?#;չ޶N@EhuV3F8uFhI$E^\,+ǓOQ ̈́ u'S^<kVff17޸ŋϪUOSSMi ̘{Kk?֬y. l"7w%%97Hn;w~Ν'-w?#3z$rt,q'9:{.ytuoffym4̝={dddswrwv޺NEDz08pI?]98AKDDDD[n+bE]^LINN)Ç 'Bi G"G8!n;&'""""""5Jʪ'"""""'$IDҮ""""qI_EDDDDDڲ#{7iIDDz#v8HȪ5k-(<]iE^3<#VVDDzwzt7DDDp'54UODDz+'z""+h^a6׿Lyl߾xm|oH'iw\ψ4˞=yKm+r{)iunҤ+1Q7Mmb0 ILOm%{OoØACHzG7 ӗ@b2ZJJvQY[jcРLx9ÇϦh O>>Ajj6l٣|߰ ۷/fݺcNkkŊ'6d8WCy,XAs'7#OgӦ+W>I'Ѯ+;#Gs(iÇS?ǓO Huu[NJO$ر*3dL ɬY?lWǭ.mw.3"JKOd2/h{Nee3q8\\̘-*yݟmۢvM|56XS@ekbF}v0hT ̙߉INSDDDG?=9킦ti/B*b۶MӃ1ccDyZر\^G0llOÙ El!a2a¥Lpis:=̞fI2wݕ| B*e:R`otj4']4LE$5'"""""'$8$aJ!"""""C8X҈H&h[D4$"""""DxDDDDDD1"4$"""""'$8$IDDDDD$.#'mV 9:9Z<~Z'"""""'$8$IDDDDD$pV=eiǎ rV=_H@D9 HwCDDDל.7@Sʽ u5df{o2Kq^a;tO= ??1GvHDDhN5N""""""Eb%eI")"{G:#"""""ҫDb#N DDDDDK_~D}>QAhHiMG^U]͚p{fftm0@  QPй"""""CpkjXr58%%]/oŔ);>9RJۈSŦ͜rL>tU6C +O?HY p(((tt-%N,ehY)c8_/p:#c|5.ͽo2,|_ר_/l#_L8 5/ҼnfV6OXyȹcc5.qwUDDDDD$JiͪJ k3 j9s=/@sɼU<ʗFRqYmqa:Kut[EDDDDDbJxhvV p3(*aM15+rT];*+zo,o趮5NMM-㣲>@M٥㲱m+3N,P%/$"""""F8pJӀ/#5|'/C}ypO2q޸Mغan<{,TSiY,"""""O$VJۈf5HEztYw7r0eR.ӯ99ʷ{Y4d C&qƏ| ;*qm9'yasQ ,.%-q)(*{X鞄^o\o#o_ >Axz^Ձl,/RcezNWi v鮈H0ҴT89:`)YDDDDDDFݷb3` rpɈ$775qAM0 Jc13]Л73Bm+رm+epEDDDDSR~{)o~9oWOEŌ0ofFj`0@0rr!'7ñcȈl(_C==s "S'Cxto>]O..c+}Lfc5vaٵcu1vMMcߞdR:d(IӦz2ځl"""""ҧّ =k 'E3=66?~=_[}Y*e٘IguO`Td]XrWw1 0xO0N=qb-=+7{E`N^|~ul>ؖu-[}w>&S3;0d;o;0/] ܾFuݳ uS"""""rtӱn:Y#9k~| |>Ɏ-Y7_p3R2` 8$ԉf 0j8AS29gc2tȐ.+"""""}_d.qj9bŋSn9!L>hsng0 i 9qƱvZ>|)e鯺o@U^F°2^{ _/:̬,~?vJ_guYYYձ~ƴ/"""""} `wyS c9p`{[3 s,vlP"Ty<̙$EDDDDh+u~S4~r ǜ9ŹKߦ,q{<:EQFQXXHUUP\TmmHק 4CQFmnoKDDDDDzpG{ѦfÇnoKDDDDDz'#nz6!n7`Cڿ)\;|]"SҘeY&N @'~,UլY of&N ۶1 #F&GAA~ """"""Ʉ8N55\g޽{y2euM&,.nC]dUۭ툈H/8Ul)δn0dB,N8prS,\=8:to'.a:ri0~$ p:8&uK~ɅKZV/ 1DeR7UPPp:NCA֭[P\mH<9/aMaa~޸rX-wM-O>5-+J Ȋ$hhrrbݒV :̈́l‡K)06R(S½IԄ!;.;TTTPUUENN֎nI8N k3 V)po2Ͷ͛(-Ñm|>o&LH{"""""t,p;@+ʽܶ_c#NAee!~: 8f䈴/"""""}O\:W>Cr|Tև)?xRes,b54qڊ ֬Yixt #I"""""rK8:=Ywkk9?Wg{I L^ P]UOa͊WGe-)***xy 0uHLI8y5v[q&<w8P-GCc#@ ͻ\.," f .-bZ [7?틈H3p B֒lju Rɧ4= 6|sId>ۋ t8E?%,SDz, aܿ _ &||1}'JO*?Ap #4 Nv.`-cC5PgnK)EDDDDY$^|maM0ٸ׋+Ӌ *ߌ` ,% EebCt XU#EoW?I:O͚"K,`L;|-d`3H}Â#N1%X+ x=r{=|]^t9eEoij > FP!abL2-BM&A ,`b```aaKԾ/5N Jm9`Ä GG)o愋45O#λG~m i2m;~#p`~9m0Ђ3iCqPCx*VJD틈H?>NVf-  o/^uS4hݵ˲,>˲իؚ!_p0 6X}jVO1N)y68 p` MIK7_߾>qqF8pd]Lє㹻VcJ'r1qTdfhCS y`)FZ!n`'Aj,m_DDDDDNd9 04an˅$s K#DƟb <L7&8GqQ@~Kb9Ցg6ˀ,`>NW,Ըӈ~(L m;a``h\2vc&Çw`OSM B.՘؉E%> @ȴ:ܾdYL0o\ǞvBC8@?A~ۮ`׾O-ҋ <@dcqK֣z""""",8u`ppP8v4U15<酀#&, +o߶XRgGf6tΗBv0 "T$8eY!j͛pfP2炐 S A'] f&x3r5Ȟ8)o+`IDDDDD:/nd&Sρm}*׬goE5V0عݵO˜oz~A4hS~^>U-b/"""""S8pj4M23Yb'͚EPs&zQPԩ|L>23K>a$5u틈Hs4Mr9x7|O`ࠒ7}|ᇸ .='"Ю}Nz Į={Ndg3pJz{O~&Yr0eDB}M=57Yٸ=ni{O `Gn^ݣ'`>%j_DDDDD#ve{a $"""""GpփL;RײmP@s5ut8/>%k_DDDDD'v#`0bpP=]}v p:[v=ѧD틈HѼ<(nT Q#G䣮:p8>j׭# t8bӾOqC O-Vȏ2*֭% U>nˢw߁틈H3y(l`VGSaglwmca6Ӂ+Å3ÉmX!?@(vm~ %'7 fN:}1 LlڶhӉvMMM~lö틈H)qm&,+w$40& SXe8\ 4鴰|6v \զ ۶hjjOat:q:8heYV=\.<ÁoYl;f"""""?ECamYؖ洍00Lˉ01!l0qx]8&i Bxmۈ$JO'۶1MIFFn*x anz8P__/N"Zζۏˁup91&iۄ 0lv &fA ] FyZ0 222p\Ѡy4y^S42331M PXXxm'l_DDDDDH(Hڭ?g^' 6ByuUb v4D?T0vZ2--t:1M34呙4MBP4`FFx^7o^ΒV'j_DDDDDH,jENۻ8%MB @.@\k0zëU477/z@h4Z|gϞ'ٺؒSsSqFҙp>V-0[npWƴ8t,1y}36~dil4pBNWkm͆X IS4% z~4eY.RbP8`PwVR9`v0QŏmUVmr[l -WfZD0-ӨҘ EQ +0Zk|{ケ`-}}}~R`إo(!*v,zAQ%- Aqb}{ x~k3<BxYx믧Fb |>BhVR@ys=sRk} IDAT) yYEDDDDd,NYQ8 r4۶m;ě~[Ⱥ5R=zmZW9!E |8P+V&MП8e2,f_DDDDDd1N&d0`|&N{zj4$G'YeKyشqSmBʘ J/Llٲb7R]tQu6%LN`P@W*ӑ \ի_(R7'N^b\787⸦b!ۊEvm +c*\Pl1/HӹJZr]-O-Yäy `-Zv=y ^ˍ}ʸ،W=U$BSh9*X|yu8& m+fR)RTqb ںPRa!*x27Aa%=Lֲ\,X6%BThQJ&A~?)8&plfNY @WwWq=< W|F$e'iFKiL͟z}/"""""cS.qAqZ[[GÜsi2ի!ʕ+ |Ni/&.1=EDDDDdɧJ58E"M\=ͱ&qȔD"aV"""""268 CsK CL0D25lAE!bM1BNIpSEDDDDd3Ʃ1H$L8M0L1U'/"""""cOCӑDb4$"""""ך$"""""rhp\Z<% ?Ws|iEDDDDdl'Nl6/`D7\"`mdn8bWy5Ì#fO$=LZ|1W7l Ŕ﷘j/"""""cSĩwo3fϡ/G*5|Sn CizƵ kL'u!x7$I&LHWO-SEDDDDdl%N|T*xX/]K///r紏K>#JkrLbV-LiYMR s QKkY !ʐ'bNLʗDDDDD|dd8<8 yxlL&NՋ)w}8O㢫>|l858y,6/^\wsyW/¶G~v~IDDDDDFG   Z6 LgH3>20qo!:o#߾); 9J͘[yn!yQ4:L>4[DDDDDdt uc \ɍrr;г^-{&2]x+\^+wxXk]l;c'Y ktzqP:I,xQ|m7m㣧d:Oc10}?#h?D# 7! ҿ1J k˞sWk4~\c29Ek,y0r!x1թ_DDDDDFp$C'ȵPMjL820ʵ[ۋAOopFs,FI2e2YSEDDDDdlT18aYܺem1Ng<E?!>cxk'9=imm;ͯ'Ͱu&.bu4,lm+ʊ?=Կ>E'B~ni*_:c]eGEhv˭kmkdY24]_L.l|i:{W+b͚=W!"""""L$$M+4c2lt||̎UMxEDDDDd!<"iDDDDDDF&NϿ,I,7^h#H^.ql$¿w"&ٽ7ܱb3$5󃈈BNMZkR?eŝrФY|oso~"""""rPص O4`Ύ|>&Nyȷ N^\}t>>{Alƿ\OLno/n߾c&9^ueը#"""""d2nm ===p۱/|irA~;ܲr˒oo' U=擥].0Ŵp5xt'{WGoT$"""""ݻ:8x˯ 'p|)+ hqR$"""""moe4s.Z[x '%M"""""r{exDISɩU)c-g`-c+ѵT0츲gcs"Ukmb0ORg@@>>eoz`콵%cn_PT_Zib%pY'ay?vqhN<-ߣy\jlncĘ"T.+Y)w&crs3ɟ䢿1%R8]~[lI<%w>ˍ%kHt+25/8c0_gce8G@ɱj,1T 4 )sTؒr*c竸w Edt?X[=l )<[ S\Qr_|e]*>P6]:\|齩N\b(qߛzyXDDDF+?F藘HN!-2#(tn#WOsL~lqp 4_DOOO@h5sDBCDDDDDF\Tc8S*vF&Lɓ1#Hn+̝uyLRL`뎝L?l:ڈF֨lj57}gGL6ለ%NzS(d\kͱp44b Jt("""""g-d?χekoEX hxcM(e',@ӦFo#kJAFbc^q`"""""ûAoowa1W7l }tt<(i/"rs];;غ};&"Ca1m--̜9AHa9%/2M6mdH4^Du:;w<$ ٹ`(6!#C,c玪q&OJWO-E/#hr↋oamcfٻ}1_|7|a^Z|ҹrIz\-uHݔ:zzzH$S?=CCjɛyEiY $d]֗[ ۭzpO`n5Oǟ|^J3we`$S`.M. ª9|a'1e+XsݲUY]qV-!O;D3˸y պ#"2x=?>8'|2K.e+|I1ɏwQ$"o q%~J޹'\vӹ{7袲l~M^ZnHWy]ݜvjѷxϲa+8Ns8]Ӈ}Uϖ-:M"s,C?~( b[xjcyDorKOɟ1c4~?XZ1ĐǞ̡_-b-Np*aCpα7R~u2'Qg㫯g„ z-eU}$JDmOKx]~UJxm:nַ8o1OL}T¡P×ff|>_'p 3GBT-[[}fV&Ls)ns>VGs};|f`]Hluqxﳬ0S `"qMЕȕsaT7Iu*ouN<$>y%ܹ|9^p>W_c|it8;̉}:7\=7|&>{?0w3JDm+b㮝D4yTi|%g_oicjmmUh4vO= FiinՉpjâmU)[-4q'q¬:׼M㤣RGngLD"yz衇Cz$I|>P0@x۟Gvƛ޾>nNXp쐒}@R!5lQ,ZM1޻ox;[p&/=~s9? J렢ZLyǺ%_z\|ٱ4ש;""7^g)p7Ibߦfc1ҙ /]ٳBgX6rEXmlq1 V,[Ȯ_l28Һ菳2p+-!_`sjl%1,[)pWni=Zl E-vk`)t"6lc,u )RxlY|շ:K/45T4B|U\{c$^Z\,p{qfi֭_O89DSs ]3 yT*0>/\oyfڒ/hPp9w?m/1Pw̞K$ΎN^~ >Sڕ8Qhdf""r`0D2cK$a)lʕO[>iLL$(G/+.599''LE!bM1BNSƢ]XZ(mj"W\RSSwl9x8 Ce7,̷8yջC|6zzz3Qk3X$"""""%J3]wE3#c0Mq3} 锒&1&Hl-Itdڵ|3a;8IOs""""""N? &.o#(k39;:/F:&H$Xqy7,u ˳ RDDDDDd$ ^5{vIDATZqD"|3'f3Y<6řT\DDDDD`64ƪwB_>gՃ-='y2Nm/8l#G/IENDB`muse-4.0.0/src/doc/pics/main_window_with_arrangement.png000066400000000000000000005124151404105311000234120ustar00rootroot00000000000000PNG  IHDR{-IDATx]`G۞$HBТťW(+Ԩ-m)^Z"!$\=YfyY{]efz: 0g; A $H ADi#+)?_?IlZB.Sɰ (d~3/qV#V+xQR^DżU=d/hs= _?9^@؋;e#N  370?78|4B 9_2F)\ȅ ^C arXehS.P)=6oriZe|]FfzΓ+E.E!C3(x2+C 8]yyVԺ݃q`\Cqf:t.*HGAր)'vFO$L l/O.ϪC3z\\=ͩH="Þ kZ}10nGo+QCg[_v;m2Bಔ A$륨 Jk GsbS\|)Eh\q W2$_rb:q˹^:80 `~eQ= ]EFwʢ  ?Ȧo"˕%'DҿrBgs/ot.J0r߂0"AXNX8ʩ yszHiJg}q|ޢ0 * c$k JOd "֪s%PռH9h7(;޹U | z T˵bI_ _KqDGt2h;gnRo}x՞yӚ]]wV<ӡ \4Gk) -KVvvĤ~QòҢ"#T*%Hh?{?$ϋHEpqZ +W6rB(Z:)*G<Z;u3dZ\0 (!dG6FĠJtA^`fQ^ɝǣ\8)2en@WK ^R{,Ɇ+% U˖oox!IO/l%:?T2GGfk^++'?08Rr}LuV[@X\GYt  5k p %8$j.^8h$Q!7OB"D t]"*Q_6T'^%ԓ.j_o5rGU֠,zȂsRNn3~:ʕ˩1gkxDQ&x^+O:%jQpn|!uyY VP~a/݉Dzk]1TJ }gkS:Aae$5ژ|[ni5fc(*\8!>RCNű|/LE8U3r ZGT ? PШw+@BrۘNFxDe!\eh(%Gư(<<].i | 'uo.2B0su.S_8eJuxi4!ZAf3vV+k•d yHOolvSf<7}7Lq5~%1^}5e&x,[;-v;ՅIcJ[ъx4l-DyT0jxǚ D.9e_w6_{#쪹c\| MxO.,F8`/ `>ٳRJe$޺m̙6wz)^90a€>|͝7ĉ˖/N\~Qj4́WXTyz*+ϟjXYqY|Td5%X^!t \uNR}Vc'.>.+hFckǍqBXiȪ>@PLn}ՃZc/S=ty౫Ne۰ ^ji4c>Adwأ񰃘zk83CUՃ܃/n}QSV]ŕ씎=WÂ>Ԏ0|yЏW_ϚSj:N{`ɢʜaqf۔Za%풨bP[Q:J+982'w~."?K,g\k};>{Tay@)/4@ rwv7ZʉIM< e6r_[ϟM`{Aw ZacoplܼEϨNmXYjk5]:wSoD(*gta5fgTo-*"r?l6~@ACM?r[]Pɟ-2 %HwuMI=͚kd:=)`Mg"r^1x Q3IF K+Vs)J O6 #1vEjUvS$JLd r͟eUrTj0|XۺO/ikO\j#&mQ6#OӺ1]flƿX!uV[hG΍{>shC='/il.ծԎ?6]XPLevrǂ¬B2}ęOi=-nG,ggϽ<~^ `V'{N*زRd@ω_%ڼ\Zv6hNC4h6-lĩg|jS]9?h! N .ZrO|('z\sDϏgQ(wmfU3fO`mczm3mTh m*?;QlU|2>H3) 6U(L=Gnk+7e*טb(M`-Vpzћ1hȌזiZ쉓}z~:d4yᦛ|8AW@KsaMh V,b~,QBB ;`6܏`ƛ+~`#7lR@jהc3VqB]݃? jâ`Np|D֪~'L+#:He1RZNRպA%ܡ{cFu~}Md h<,,2 w6-Nip*W$'$J`ܬeW{$%[vݣ,̦*6)ŊXaö? 2gϝ{;w :tĉcG*6yR]6+j 3^fw Fù()4]0F5)Eh4GX'J኷֤.(苼OKˌ"h *`6n. .9]ː&\P-C>35eɲe# 3oݴ[BHϖNbܻsf=|pDlo^5k/\i˟J9[]렁93zXJ&辍ޮX–?SWwi k4Vte>:&((̩Ӎ7b ]DGV}M.D!Oxу V DŞg8•U`pp+oOx>.v\g_wTDZfjQ0RXW+檿}J褱[=S;7uOSji; R5ЊmFCY~ ")׶_7n,j<9~gZO4f3f,:s0G<]\L ȿx ]5) +xgɡOciqWV\a-gMB "=B cjTq}9+ Rg<էُ%ct)%Q 6)*,¦{M2XL盾aUݵƷ.e29׻ 1&3Mr!F8L2)R n.@7`֎8hꗙo-k*I-BZ ~Z.+vzAΰxM-'>[._\fl$5 54ԫWw܄9ODFEo]۷p"xy撙Vbk)))gw/nETDj11iw*cۄ޼}d\\' ~"EaQwApDs̀TN S t`PPjd_U ꭘbp*Ώutwfq|E&dZ?>J`;R)\uAسSΙPZ5VW/ xa~Hվ!R$tP×}شz~@t `M$_jcF 7,_k'?&>zF7b׆\q#zkyM"\Kr|u5H?|\E4 o~\X.tc]-XQ?JE,|o&1Yn\UzvSrj=`zz 3гeM r"wn|8Z@^}HȣX]Af@yd9,ݹp H P{CM0'B(7oR_`$'ѴiZ V\. 6N<o?sGz#Nn!bYàVZ*"VWLg wB޾5U\ @f73(a6p0geeT έldW eFah6̄7`u9.^~B:DPH{>%}:pouE\X`}=𒴄AdKC^8–_'`4m{M}滭ڵjWuAzoRdp111:]xDxAAsMfFIn(:::4$D+^ ( rvA ѴYI gv e(C`ts Ք=կvmy)0i O*BC%v?&߁@c|~ AV)L藘 ju8>7ߊ Pp3|~\21 U|y'؈qxM7zZL,@PӃvj\NP=i|i#`9]pCQYv‚Ok rqO(PC2ik-  DL@ʨIl&{270Iiڅ͐ j* Z6#B¨"" צA>'[6\vt|g -" Pg@x^a "_t)n+QOZ /l;!iT))i -˝BF܉ j<׌K‹9)ᕮ ϺY8s|y;W)WI@,YqOc-*h,Hb()q\1.e\Rٹyub Z` c8te#\`uǻJyɗx~%pa7q:%8DzJgUTm9^BdG `NCIxsnL5{ DBYy%wrB|_p uwl{(xe *BPy7N7໿pܺ>zڀB .. 2_l>;*zM9pɫx,|iGEK~RZtΛ{!8l $Xml sIKH L' vsn*+PPH7JNi(A ki# FJaK_Rb!3\.J喗OyJq w9w"8!DY[m(!Qd҂9dA+CUZPzY]ɛ9@o#ņmPSh kRPh ZrP0gͯKIn}A 3h^((!ŀTU_†U7IA)AIZB/@G!m?w]/p{= U%H^O해lK N"/ݬC *@h e1xA &rTA Ct|RA+n(2~^vk i㥊3LLO7Q_Ea  ^ 1 fJWo= P?-\! Tg]x2@- = hӜA P d<. U\)!1`na14Ta@0zBe(v#sD_J SσNH6i0s)?X( @?/ %@#|J9be݂Gl TNl#PeBkM{oo -f1ˌCs8y@YT X>\$1 iǐ +\ Q@l(z[ix*4ʄ(Ph6RCedh3 ;ppJ<P/#:Y%6P+]-DHD'9hn= ϐbYO|,-#^&Do;Me D7E@eQDYK$-!A7DzvyZRovMGv]s#eA珈չjXMbAogwdrUSLL6_9xuNN0_3Pm1cUxO䴱:cݵE?szjAYugnQXD}0.BZT0T6}(>YЖn b,ߤ'VhX5Q Zt[ȓy+nafq*7=3'[/eA-s P EF 4+ݹyxD VE"AQ7HsޘdC)(-SiEc(-^o .$"P^[B‡ A.El`;o/gutS p+Z.%-lؒ}ܓg?ZF{f ̿g|~ڄ_+[Y1B}X-[(0كpX2oZszߵrv=Ǯd{AYwkַwd]w=Ckfo%.|%,+OA{5ג:XVvQ TAB`Ƴ/o|NSΕEa,.L﷬効Mu2翛,9y]TzW=}הUˎj1:%el\ }pvj%V->KZ^LӔ.hv2#@DW(lA2Yk5awD -(\r~զ>nE3A!*,*p0@mEA!-0ԗ1PK򦠤%$Hv3  H:Φ60ڕhI۴k>/+gnI2FLN4vxkêun݃oY/k'mzt[%k?޻Qa=bǭ{, :{J (/Ns<{ݣ#&9짽OZBDΕfEBZ0=8m* T(̩]CF50u5)bYVaο\2끨JŒ|Aa2N1Œ?hg#1[ZD^Ĵ)`VgF!kxLK$ŏ@tAQּ]U⒐ ?BQ n{lL1]ZvɈ3˷;I\x$Lu͌OiƂwyԶK|?qުed+מ=V86e1]ӎ:N?-:]gx5C9o8]O0Uv$_Ve@u[`c[A ^;" 5Lظwv]=/Zu)'jI_ !IZXom3IZ iznDZ^cmO:z7{5 NǥjLjXeI%^y!(qTM[j[Pʰ ˿j-|S=sm]em- 0{'XywqC;ZW SX4mɾG%QV3NqX\p̞sS C_82p^ݑ\? CX{/?C:.ܻܩu;aT\2m><ɫSCmx!m==g;3g!#(=C`d$[Ʌ'-9Vk? va_\_O3E10 E_rF ^ p>zȵT-]7f@DTQE$[ vqc'KvIٱ3 W_Q{~XGe/ZuJ N|vҝ5l'?(yޮ|'e' k'{^L5 ݺ*Mz!w32Ag9:<އ Ub}oLisB׭XTZu0$qK78ӣ?n&2Q_h|8n*aזj }?6k-5Vk7m+kMy HzlUr׎=f9ω>sU8Psx_NS[E>Ր/5Y?55{mQBBI-䂓siFo8QI}԰N~[o/շ^|xu~̦^4 8&6A pID ٛʁb|JB-9Rqe}T'A)_a洿 VAtf!K6ש>(LiDQ+Qfq]sAp`'U4i$mV u'>xjŬ.(;ɇv0?n/P!i8&">H^okAel0WU~-2Mڕ`X ew}WB[k^Oi-Srmλ Jh!N^|drnΜXՏjMX9KxsYk(Ɋn[!0"ŭb c`T&*W`֞K!FjM[vRиl y6r@j~H{@Vo:cKTbu[^ jYuBZ T:XMcZ V_oMKc󍦊vr*"R<0O("Sz*Q܇ z }g&wt /w޾ Ө" 3TeXΗ5.,?O:YPq>S|֝RGxߺoIϺ[bM[v̈́jLm5 }H/f0sU*K5o4|ݩwжX¿k֑udѬz6NS9٠}4Ռ]|;Yƀ5-rNi18X5^z3M!l!E1T(2B4d S @\,HS(rMV(ȹmSn_O'Fg2xUL4e;(7=i~JUo  L^rg'紖g8͢:mKvsE7[%;*0:0ƿoAV!ӕy8Ħ:Fٕ"ن&RT~]ʸm#W-%ihaPPXX~]oz뺞׽_;ޙ|B&\ǿnQq۟IY;i!}/?iN8Kv1#zAZK @oD@s?ec9G<8O)m= ӓz,jla'ՕVܱ 02֗jF!z0`j [p yP1%[oA \\8]j̙׷9bgވ"pPD\|E [Sz =ߊ+kpLѧjOw(9ҿe|͊{@ [~-U\$<_;&E5 {[}G͢.ۺf7;fmS zB)3:׸Չ #gA.v}OKǾ;(2 f='RfTݸiLbe.nߑz 0#W=~ꏵH2ԖGS2dnԯ_-^?RQxe E!f֝X(z mi]_l9o9&mʈC#-ځ9u{OA i $1;l T+e[T|~7K= UjK[nZ:f,Ndm܊ԉZv.{H~T!P9|P<ܞ ^kk~5P$.0QLD5;l× a10 j)׶foR[r-c,na>4elNg-:zb5ôjn>ȷ)l0r]_NJ-(8vʀ'"9ثRE&rEou;؋nw Axd2PYHÑ.OR26ynJasuZ#7]"Z߱BYZfgQ[k+d{QH\S֯`ͺ%8׫\\#:e8 (VCi [ BWgܸCwo(vPte1 NSNPn*Mۗ]yJFE䂌wzÕ'K7}(;}.!?qa?F ˜ӋLTR9(w\!_wZ(bk \L}C]pF807ķjGjBhlMtMOv؈G0 e(fW`̐FØ%R?w.Mb2z,y{_wkV#Mkyv#'I gp`Ψotۥt;TI>{c^&j;z:x>BdQ7(\4(ހ_K*}`?ʑiwׄ),cޘD@uz#c8̽*$!AKκv6M(" _exQ"nT2wGvqgT 0 xx[x(ř-*<6(,w #$!AKwfK y o_pLFoF x\Y*%nw줷g>zKk=,է̷ֵȱ*diz+k2'8*~nOy(;Y5#e1ԗpZe{9e |N Q4n~HBQUeUʆ/4ih>,ROWU ЌpX>h ~Æǎj7o}2ucH&VAFWV@zNAw<裂~,%羠4^ RQ/W$ e5Սh=O%zS_·w@8qR&-0+f/>y℘p휑y`W/C4m$22yNnUnw۷\./l(͉Lx> A)R{>l ~ Ic! ^TXcS^$6Q|J$+|'ލz5BŊ%KHڂ IdT~Q;[ &;44{wAgT%QjJ ,QG 0?Nrxb3t[awjqh 9݅mݽw_@Ζ?b lWR%n2HOw^O?XxjS(@X/v]D pȘjuNIelVZ_7nkzxj9lHy|-_!F*Gjϴy ?PP90FːmO IPMEGEky󠰠YYyt_:th2ah4!(8$HH*p/s pʕ**)8V ={aX%)Ov;2R =o 0v@D(AxB_Gj|K0mlR s{37(Q/ߒ _΁݊D^ёSgKVdfdK $F16.tBJ% .(\"LAnYVB B\<$ > M^SSìJXmւŊ/  T>J,)-޸q: / 7UVkΜ>j۶i=0#[n9kIJW-GSձ];DaϸwuҥݹgEq5%epa\_:uXJe  ; 5w ^$Bb[2sV-QR M{1(tZPrnM#1\IT +))'Orr8}\VLDp^1mhyfa+)N9F8<Vp?},t?So܀/^|vʕ*;'&OLL|PG*Ƿش~\IjPRXd5݋z]sGsè#4^e`Pa iVە@ɔl.'P9(BCmѐhNP&0H e2xu(6[r%T DEqjz-`2n7B%s&])i(9! p9+xb%=$(Xۈ00 ?S ݢnC]~\-Nܱkݺݻwo~5lƗ_80++kuC*R%QS pPPL 7QFMfSmOqb&f?A| P@loh)@hڵkWX1V{{thJ&@m[p:H Q:~,[0"}dq"Gy޷͛jՊ?nݢ94$c&-+b7hEMֶl@crAbkqhȹ=z-fU0affa6 1fYBB5q LQ{ eP!bAF  w5xdb6*((P% Oi)BE*4j -JiAW،6a;(*ܴˈ؀ɑ 7իBf ,IχU;+tC LH Q[(<ٵ+}n9AU))=]1Sʕ+>}#<˿;0U+x&?eޓVTsu:]hH(,U)&x-?avfCn1ѯ6.߾,'BA\\, xVɋa0{ bGјj4ج6m 88mO*_qk{+rr"7Nd)=̈́Ab^P/^?|H`rb~vZ're.Z^̣nKw+QEVzlۺYHXx`[oeVMeʹBL:W3E0r!.]7֭[GGG#2 [l115WۅK^M-!Kо(cϋzSdĔQMT/_߲eKÿ /^j*H4ۻME<_QZpШk׭fiXҮ\ѫx-{v k:3 8M?4 $rcicJZm7Ȭ߿ީӨ4,3k (HVn6c7ϣB:5EEC ,C@ɒ+8\/@*åPR` ņ:>;cBG7;NAsG*-0[J+ A' GO~u"9@&-<ɉC Drg `-rpRȵ'Of]{vZ:U 6(c< V D* .96nw-)w3s`,_2fw@9ٛW;Od @,ZfS6'8(q`DFD\OM {͒qqPT F RCBB@c/AWz3hPNݻ/f>rbv횷hyF {%*|2Ÿp$'W'Oӭ2Re{f4-J 0Not\~+k2-Xп_bē323WYɸqPN̞=;,4K7}*'.)m[B޺ L؇###B@B]ݼQ[*M )77C4M۶mK(j~J#}ǎݻw;w.4/qs- ?~QF! ɱaCF?4mTC'dCwiҡÃt{m֬ N(-似cO>ኰk׮ ,fJ{;  ֣7<p^ڸ?h0 ѣp syH湫OjUFWwmLo* H N O ʔ60{\}VJŊ ?G'r`6UP'yyxT?|͚5I> *R;$A7EA?G"C MQ=}ol[~mBTցk6P')P'X7AA-S|:;AKA#.Ó. &=,H<rqn(> Jj""k4s"#`h`ټj -vy턄ʉ=z$CNxm σhV-[~pl9cdua$pt5%6ŋCɓO>̂bѢEYYY^>}̙5hZBqHrcǎAPϞ={P\kx^:r yu8өS'jrDF)M-[@U? ҩd^h͛7a۷Q OИݘz&h!#}s֬] y_ƍש)RN> .={=xv"$W7Ѩ*aK*'2ViʓuxtAXTfdH k~5qd?-ɑQ6[u`?2ŝ`\EL !iVsAܹr8F,#Dp c%RlL@cJJ뚭+BP?Q?ͻL|af׊;z_!LVգ$H`<$)? ڳ'} FJP Zd^k6l-xQ=Рt!( *ɘB5a'hZl_{ь+ h]x9GorL,\&L5J2P=k4EGDD&-s9LfRhٹ9ejTt W(6]'PQ(} FE= 0AGcyT̖?r駟\\~bbb (/ύp}nw?p&(d7y }@ݷn>wQF]Y 4 *|K^ǙX~(" a8- 07KA2 ۛHgMbA\Q Y2f*#bm$=ǁ0i2  6ܹ3##b@RW(4lաJ$u֭ۿ{ŐgS>$RB}RPPТE(x !0ˆjfggÖBU("CgϞ͜9^z v;|&O0+PQX!4xxshWC)ۀC`xV"qnc {R(Cf# 5@fW{gض` SrʔSf̴2ڿ+u[+SڦlKIqocӽwwϟ-D $FMǨ([*5Z5k|rr6OHJ!^p*4bʄ7A G.M yN:91p*f!irEI4 _/"=)9}ss{^,kh?8"[e1d>rЭDD ƬbǬAg(b #m~ɖpVzlS.YTflde@QTp һ , r)!R`F=]}s؝p5ۉncT}7a6'~ Jj޽{ O>ē'O@8}}}62xʕ˗/!~%vyqZ $ܸq4A UVVuǎGriTJO&Z't?DC3pQsĉw;rʔD~C/0m^N@u-4ƶ!~ $SNJPصG=MWޠٳ=X,ˮxKނPuÇ))Hݺ̀׊k׶oo!+mK$&R$9X1w1[b YG؅ \ - Y؎zJSH7oA\>a1tr%Y[ѩ)&UD!1 {`1%X\ Ԃk=:_i={Uq_v- M[.Z}jg*ͪ64\V c'R˕ ? E=ٴz.f_&-Ts%dR>|djE?$2嘄dbK[+') #BnA\9u{ }"\I܉z'>۴)gϋs>+2YʭqTr1 1] JxCMWp;xdf}\qrލsBK 0ƍ8e*)%KdHAuwA+Zi[5hR3ANd ʙvr1vNp0w={۷o߼y KtJlWPD":t(h>{F dZ?@pʭ[߿ߦM/_',, ԫFiG{ jN_ 6P1QO&<lxx8"I=f޿c3,&1א:ؒ{iOwI.Tc6ͫBd!DE97!vY6'Ph0$3Nf/ 9~Է*T7oQfG?91\LJl#"9L EvAŧ>!7@QOD:J_+OfGzj]b_p(0Bh={RGj6[d\m jB("݌C=;AFsUXti83$jtFGEG\~26θTcjԘ`5@-FjB䀒aE,j7"ɮU!8{̠}lxN$E2dh(̬p2ꌡTfիQO>O* aѿ_)l۶*FO&:܌餵wlt©'6v-6MZ˻…9;aY|$zxqxQk/L_( h2GD4rEVB( /GTZ{.]7n>'7 * !@6$$ZjJZv|>9}9̎0غuk`r@U@F1#nyɋ/۷͛S644 590Np -W#&w5P_Ђ'P̿7oG K:\簡z <ʝ@ IZw¸Pีg]6KC6! r)f1u1 "X2NzGz ȓRM]?LJT"6Cu#jL;dua;!Wo$Jwk&F29UDDuU8h,Q}1u01FNCzpPc\GA=y<(DT o) -x`RRt'jJC<&"{fۦ9ZLkdoB>"{! p@iW5l[dϞXܕ+H^ĠA%݌m  oچHDhSR š! !!PIlh@MOKd\ODHG@1g .a|8@dKU<`)!@Y'GU6׮^oկwA3LˤTl V%J뢞\Z,&+"<<",\]RNNvdDM!?|r9&L&_es0hš=@eT^Mt>eBN?i҆ #32NΎ]rsƍT}w͛5߀~{IxHkmsJ#d߿[uXvي۶mРp'2;Çoݺ0y{DYw[2\r6;Nj: NT|cJ,vZßaÆUTټyL&?+0m`(ZBK:<q#T}v;R[h8~c(ܠ@@bܨ' yX!U[nժS |ɓ')G3+`˒-oEMY5쭰qa0)Lb9!Y_ߥT`ݿ[U"*{~P6囨(W^<}}kOQ;v1￉ dwB̬@ύP+Sfc>ُщHI t!O9١EDhsJTM?:dE GKY\O飴>B9:aÆ1cƀRo-09fr56p& U48W\ VgMŋZglXF$j[14"QG=ZXI4\BLTlw3F."&.\ܚzʷӟ=(^ڵ!!  7nzHrr޿Vt@fͪV_Bضc'p'ׯm۶w L u ܾ}? <ȡcݖԓofOEgd8l4sh]|}7mrįݻ7BɧMAr`ذa+\ؾ8؆81ح[7*Z"W n]C:@SD z˦m2CRߠ>|pϞ=i5_}mV`lVsP{s {#o<{ޭ\E3TL' W*%FDX |D,XT\=n@ >Tfm{XR)MIPN h99[Ga>M$ܵX,4ml` RSÈ;$P)|6aLE=!FQ/^ăDj+1 "#< J INFFH/$R)e,6cux)԰lמ&|L_ "av"/E(8z H˗ϞF/_jRDݸvh\l_Oo^T[z#[։=e%}2{LJxÀbvn߶trpHq@SqF*jێ] +WSKyv)J=V+.AxI]nsÇ-[A,2x7Gnj7Ov /vaC֫[Gڬ jŽx꯿zÃ%P 6ppYM*'ZF\~5koݺDMbʗhx+ghs]Eo+NbM><`UL]BB5i d !joܜ=Nzq2\%mG'[.M!uRQbFxjj5˦@IHO'0DhzrQ;!%m $)G;q|_gp'uP\9 D~LbxHgkAlrr(!Ï,doL}D"5-]1 Iso߉H0Z/Bxe! ;fwY[ɼCͦR+=4/_LMP57H&vatMڬk>'p*WRA< ENvL&U)9RY~o-j.kϺ".$gYLKS׵nޝȣt/Ջr*>}X]R~~ `*,æ~VG7V6r$rJ7m.͛WV#G5{8$Hsr޾};k, &Oa'.`)(촥&~6ԱCƍYǂfUm>oog͜9dhfħO@UFoWvm+(%{}ϛ7Ϝ;?gH~# ]qZ/e1<ԓr'(ꡃ6O$! Wy`H 5SB2,61%O ,cip=I*oC P&3E*MdҐ>y bZ!JlDEaN.j…7*wr21{dB-FAϔK)⠨W j4-x2{_7+X HY tP@~~'rUZ[G&#)5SͶ%#>C $!'[yX q F;܁<9Nja >=-4>\~7PJ~Q'@ln175?_^ٽ{/],1R{*/_7 Pit'w,Fp\ԟ `WGgO,_ԶpޮW-=&"D!Z\2'!24 <<<| 96Ŵrbb&Nlg?DGm>>I Й)wFy?o !~ڴ}np`@,;p@3gy{7wvK>U~fM׬^==#0=0 ͱ*eneʔ{DjvhhHlUT8'дp\j$?nv\lR%ݻo(V+W)z.]O~V}B֨d_M:Jn^B5=0<c ڒ(+_Fj&]xi(%85Vf0gX-0wLNEHɡѫ,n!n6f#OF6I6=H(i[˲B '/BL=p+r{;ĎA ^K36ҊydPLIL'!iDB4WT 2e9npqGaԙa&t X/Vp8V_6EvBz0>t8/A :p=)U+V#@3"$|@Q@ PV9QA999\.מ50 \1HTyV#s /@ s lF *J!xE= j B BQ8prVB$R$ B bal@ @ b/p@ 8֣uYY". @ @Rt7ʺpl>k@ |fX,ԵfF!)@ @(8N'@ b 00FT [z8l6tnOHM@ CQ+;u^aWJb١BuG"|JLT)Um )HӜ A RNQl |ej *YJ.O Z%JzЖʯ5}\8n P),&Fj~~~ymͩ`L9-PA Ha%ΓN vj,[>F&(" h"aaO?͏KU|ZT7oW;ǧu1m &nkxifn]gA HABƩa b'UhEbqzj*ޓd ˋd\'Ojƕ+_^$\k O<[Nab"M96 qϬ@ ~f6\0Ls& 5?@ftɒ.4wyyٲeMҁѥJzPSs-3+2he$zhj6t P^ٰfoUˆMVɫP2no/J8V!c&jϾlϵ7,&jb]sg9'͝f?>aϗ')gTqAq:%W՟|;~PCOh[ҝ_ppDo~r`"kdfL߿oAg͞mPU,.@FUwef 9Og|l]@V9!Õ-K{B6Uۨ)ʹC&d`]m07ahc04\井.>IQ"'LYŋemb~#) LNM!}g_ZE [DOç<N kA&U /JOҕDdŶDG<:=]ibS8T'mՅmU 34c]c~}6Br3z^O(#F%]LVb%mDц&dUD#N&:#,\0nz[o٣w36o" #sU])G Lh>U K/SGjv|Q{)WOB2ꉷ{%o#ߤ45G7ɴ(4Ti}}yY4]V!_l׈^WlMͽ2/h4ʔ kt[da 6>Dׅ[i[S|~Yz M;(ɑZV";F/]qX~mD_'מ뙷H˝+zݿ]ea4~tk|ڌڎd v"QPX[ӌWn(5пGU!VXmT uՉ~K,-|m!ThYJkeʋtqUmۖ) &IAh=N7s;S N/Y*YғC`<*{=w ށ龣t;/`,^zF5:kzH.8Ǿ=|o!-c?3 d?x^1\")y \/E\+6Ꝑ+wLiyf͡TJBB!CbbbV\=kh˸c<+~ e/{sە\Ǘ)Sfێbx}m[1l/GUZ5 \`Cp:aY雇Tћ M~ځ9Bۏ5e/ {r LpoMtf8WюЄhJ "nٶvuy'˸{W}*ȜR%UvkHBذ'MT q̌]s@ZL_2 =2BD ^ &Ϋqgm m)YhB:lޡiݷ5~aוP,{fvF1Ö:LŎlqH?=?y3_O/><#}MA^}cZ#)@pb`'-w9־;a٫{[n}J1|u{Su//?<0,}mYm L)gyp 1dR]"h]^g#LS,_L,ڳWL;p4Y-]AI|Oy᠈LO{~Cs%{ \-p2e,5í~y8Losp; OϺŴ,3x*,8gMk-[ppm& F +_٬ hs/$}"Lɱ K#G*9,͇c L=cӛu] U|~_8R`.L@.Q/݌Ƭ6k~\7 \OƑN|GR%?` r, G "N6]VB7ou]: G73i\2(>PIP}6nMwGKZέ, Wq.7s,埫Kq2hnGY@ . Ÿ.ԅ bFԈ~K^nYOsp:{R9+SfŌF^V*/@qI%&F?ϔKp㳌x1 v*%`%:I+ܦUrBO BCc\>n2929z7_<{+FCd M~ QO_QЭ(nc,Gz ݉ps,dҊ+̝7ow`""A`rX j&~.;\X[Sa$:Z6YR麝Qa!g$ /*.Z73T,-h Oզ}J2jLhj(2#4:Ó||jpv3TbTD459Yx8mYRvh :m ުtTi)j8ՑMv¥sL8nYfhK:9RVs-2cf91}睸I圗KF̄exM){@ < ='ĕwTYbYwwYrEm#eH o S8t̵5N },9RyaYpK(5,,CS_HD@?prW!=ewqUSo*PaD\2U¾)?Y6 $6I&R{,4oyrgf뻘 x P W$8߭_)Wɮdxx'=쬖-[<}d]F2ҍ4] |*ܴaa/v~be}/TLL0g'w:**JyyVTzϵWL}NSHr17.?֜FVWJa6OdN:kna)&Sˋx!G-d;=wʉ+6pNc?i*Ꮢw<]ig t뛚گOJ5uڷNJ4W-N~Rc'y1Ђ9qK0b9Y bt cWGpyzS']8kM.0!`Pyzp|Z( ۼl%Q87R,1C=6I*w!p9]̍ {8 mme׫}}qMڴW %eVtvrrXyp< k7 AHs^#mu=B h;2|.݌zBvX˗iF5`Z#_""RP قXWR1='xP&% @go:ǧb)?y1L".ծgًzN l]AtvϢ1Ǒ. Y*YT7bk'M!<~[Cl9WA#+/io\ϴ/(ސːV?15V~Xb0[Q#+/b?'s1 Go/I4A-F$zTwry܈tVp!?v gሒ.l ~]w}֙7 E: ':KժU7l4h{ٷ{nێtbrU}oP՗kB"OW13'CUЫU6+np?|IJ"ZR+OtA,^t?]+ŎxVߧ2k 1*#j /N]7:Ox|"VwFp>L̓ Jg!KPzYڢ꿯*33b1} uWν{s}vϠi?31$y*BTAu'1L'=42=\xN, ,ƚi=MlL6'%0|.10DQPmOU$J»z;`4KO? =+_zi ڣe$0<f7dGEQ&)c,4Wg:cklfCW"@kɰ!6lA~m2fUFd$vX@&X3h0#i˭i޼ 6vlEug{+. 4#1Y~ځ9bo-} ; n༸:x5S5!+Xܯ.9V6x8`xR'T4:n 3|rⴙ Xq{ ̓ahsi-KUo^x3؆sO7/ʉ?ךcN0Q/U*={AS{ͧ F&G=axػNGz}~6剥FT#w{f#8eZO"Xbz//ԔUA||>N^vZ5-<7k.؟;njkZC>`*ߤզM䓚jtT 9C:,=ai\:?NK6-ƅ6e);rrjYJTTF7h+q.]"X]x姠 jx[.=)i ӎ_Fs ){@  UNgfgOŸ E&)yQ&>~x_:UPeEuQJ9v!c1gA ffs 1!(\/37ܧ@ >pT$wU,g?A]% WjJ'NT^-00!Gpܽ^?}|-tΏ_>OsEQ-x5j5PUk56 e*T&nxefVȃzvRY@ Ekk=AwD(FDF{3=AAa7 Pb6jJ#IJ .luhp[s*r74 u?E>7'}t(QLn:ٺQD]^6S{&F{\Ʃ(j4XL } H>5قܶߨ 9&NUXBt ] Y&jn&.P]#Zaz7D rY++LžP5*5+l2C7-/cDzF/F&Zlfjt`XECm'HtC1FwkU9(K܅%2}ڛ b;LcLQ{}O@D__Ƃv'"݉u2S+tkZo%_I_g"mҪj!wT!Z-ioΓhle.Q/ֆ¢h3m$9p< G=R@ @ې%.S6<Qa)4"## [ St QmdS~_ ~w@ RDFD"(R[EWswEƦvmrӇY [@ DfjPs<@ (󢫹[BQ8O12tݲbxxEP!& (t@ gnjVϣ0"uDRBW*br4{5 XSa@ UStEWsDQ ;ɚH RDFrf 9BFQ5UCw@ q[\ÝF 7#gt\3֣&<EE;Ɉ] jyeZapw?'uu?3ǿBQE"#[ l5h`|},.}uvә[Оe?9y+(gd͖FĦ)Q{9X/$mάի%o ;4F˫[ZbfmBv 6.tk]gKvB׆Xͣϣv䯴M|䆳`C.9*@ 2ٮ+bxg )BFهxPQ0~M'snJLd#vPU̯ϸjte +nxky|uC."83=(vX|SW?7L8H&RG\H}/2?Q36S*pš:=htKH;nfDK1ɏFTA+80 uw^xi45ۙ--F^%;pc* mW>waZl> b]jۜl[]!1 JY#P2Ί|v0: ͠am 90L%1OAL LOmvXêW\x׶oh٩ޝ=;2Σ9yȃ*y 0F@ _Bx_A.g^[7 ԊJѥ}j[>;t|"]w,]}g#Ƥ,>gPaLpm^jmCfx2A5r& 8!fMg+T,RʧS㋙fr3ˎ\ί NvtP7u:ڛ}[SK? m>mQx!58X$*)ΉK/###W 4$MMɓxjƕ?==xEpǴJ !onb5*S2k5EڟsJz]A&Z>;FNͳ mPyU1eW/j>}Ks"7Ӿy1kj5ׯ]-&*$4*w9V>,Zw]5dmܷ[ٶOra-5Qs{֏Mvmec )I^Y1㜑N4pdT \eK2-Jψ#l-dݟlp5- O`lԊUk-epurǯ0ωnoW[~|Va1^O4㶬e6oݾAko栙.gI9훈t.[oС]@볈5ʄ+Pj^]wU^i jddLR1Lqu/hU?gX3$fvh 1qbbNH Vߙ_T{.9[ܹ{ʟdUaq K(Ep[K/2fH +>d^*-NM'@kśˏ5YhH_'յٝ@\W)U15 GHp퉸ppʱm^*#"&y-/}ZR+>upQu_-W^ ;qGgژM iŻ.d Uf."6T_kךiշۡx> zP}C__ubkAs-Ŗ0{HP 5;R"p-s/uc ^*RE5*-";3z' oƏ>} g6+ B#@fp?ٞ?8j';Qvw=q?-oM5PAxb,Kkg$, *R~co8J~T̛vjUsu3_ \ ȸu/?o~?yͦ3xԓKZP7_%q!)$%J +ZņΖ CLwEn`ͧ+'.::sn-ҧ]s`45HiLi HG~F1,VJ<Zu[oo4f(V$y "[37m6hR"T5-Vgܺck@b1!(K4H&jX²CbNaQsMn?as-ۙwf<an9^abK[ݽӳ `q8b/ʾz* VHquj(V[nsY鉨Ёwv a43}t #IaNiZeDg޸xmDŽU A+uvu:,TF@~ߎ,Q:(1f빶s}_yǍ`Τvks+t4#ִb+aՠ~?ϯ8uF%>X[!=[;;U'|3zi MÚ^d+bC2pɾ"GTsΚۘ;b#r zT`Weyčr`ΦĻjSP働LOk`bsr@^q~Gu+7ËǞ9̀v7>3m+֏!LlP9nIST>m,ȟ|=oyBoCIJS,[͙H)P:= j+X=?&hd'egbg:x'}dkAt%v[aύx"knQ(^HBb͢k!GhpEw b;b wѼ[XTJ(_HefC MHRrq=Ѥx-AjG [WSkp=nov«_m?FR#vDL0iEHa64wbkCPli cQ)FzA!/u@/n"VG@Q]$^"QQ7f@ DlmNzKWG"N˒n<0yz '{R*fX̰۷TR9 _Sio[OnV@^KȻHtp{N͛n/V,-5`w?۫>Ϩ!,*HüܯB3$;|:N 4_-@-=2Ç'O@ݾ Q+~po݉O,l5򀑵0-?oy!c?&vhKǺUVYؚ֬8N 5wbkI"Pli cQ)tBJxBLRJ%%%)B)ٳ@ w* ӝ@ (?B;Fa+-0!w ɀ8syܼd @ @Qll7&;[qLwQ]xvNH.$ %bZA,( "X@@Q@D* zHH!~~e/].776zJw# ݺ ,:*.dښXj3!dB9gU%v5 @2%%[2y%Z"J$6I.i #2B:3D8%!D{jL HP#SB{#gm. )QIҚ |7Cyڅs"Ws雞)d, #3b`EqN,8,x0G篿?qa{_]w2'Z$r m6>/m&yF6 EcJhyp/% BͲHaM3%!t.h%'IDmd(gl!s攼(vݚidA$5>vR@&Dθ LPӃ464L%iz"y5%GxgF퉣;(T4]"@[[B ,X蠈KZ]ף}j?v֖[&hTЇYZv9YЂ,~+)R SZp3wBoYRʧy'A1-wE`;l~aj2Mutm궈jR2&:%5F-Lm`(hY8Drm}@:)e긲0Qډ~zYBBy.^. 5iʂgz}H> 5[Dio [τ)4 ӄL=w,H;#$7]k}m:,Xhg1[v˼q)HWv ԠSP^u*1 B%|؃oK`"x>B.$KK $(>_V|T8sL @VDqHLO<(H˂ŽV^`EHrA#2wrc>e|owely-Lʙ2o昡ANHH  :} s،# 3;R eH] h΁> c$CmD&nܢ QN_ L`"Ѐ!r55,XPP,sYNQa2w֓g;EQma!z7?A/VG%NxN㡌3} `X) XOݠP A̤8rT&h&9 -iV@S \h?6c~ p=1\^GX`h0h_L[g;E9qyW2o̝+T9`0̝"Ϩ0UnG1ZYvPc:Kj@Y\pﺜ+4[ 6\8ß ĹAA"-<54[̐A"a2‘.f`\X#rB5Z WE~cʁ;G8ysPYˎVosg׻En_a;.2oܹvt([0̝"Ϩ0Uތ0 Erycm#,((wP9 #T +, M(7K&MB' Zt4<T `̜䣘EE8 ;Bf= `s$hjcT{ BJ0K1zʞ??d#]zk#fE,淭{gn"pxyժ4O֪B3։#Znn@`H$btRUYY3 pEX7XB@2X "J< g, 2(dNRK]]nY53n|X";x́ﺌ"i| eG 27>R(4Ҩ)8 *TVwq?v>EŁ~L_+jsєiJk7m_.uꮽomċٷwkc٪?ʝ;yz _5o:R췘Q8.2wr9-X}3w|vop;J.]:f/Λ圸.4mZdexgtֳo_xԔAlzU/9p6A g>zpj :"0*9E9Uof@IB^ d3p{ 6|}(87_r\ fV[HXqBŕ0C24:D1X5)wE|Ph"It:;[KB.;#[.)cK;b8 UdةZΐg;E9˜EḼݗ9Sy[6lXlYM],kEmͪիG> ?IS Eac E;(ְʁp+a|  4x RcKb$D#@i8<['øOaJ" D (n|0h uc]r ,\oD-Iyq)IAo1бArBJЄ$peS&HIϿPZ"|ݷkj}6[K8zj wG CpV[\_V-[ZVJ yV4}:'sSMuh,!RywF?NM"Z)N<4T>D6)E<)Y`- ݷ_̙ʻآ@ z:R %Pr6;GFFa[ɦ݁?ϝׯ_ӫY茝ЬlFC`v#!H=%. K1{'@:W[%x >Z 5ᅼ~#W$)QR).8 Nۉ0`ߵ#yj HPG=pba Duu=-gdE 2l]U4#҃-U5#WYVj!-V2_ꍂ%?_CR=7băYy!:?a~r~`@1uK N]yǟKLI,s(ٙ)f~2g*=uZY$Lyr)jE"*Y#=-U-aJ0 BH? ѭk\u-Ik@ßF;\3 w?]  5x5byOO}9׫ˇCdEx?>_qewes_4w*%*{1Jd.JQ,0G~ ě <հP=PgRI{˺gxZx/A'o' <- M~㷐IIo q NdM͙(g;E9˜qy̙ʻGa$e? g(s<&p$QeDw50;gQ `}xo$0e<1`4A~Y->e@=Vln3(%\҇7x{`'x'g?ީ?_ I3uypX'<zj}YjWG.n:wwk'L ƫqk@((Co?Њ۱9c- YNQ2kQ8.reTif6=?NEص[+mx-I@yHkͳ1T6O6Vsݼ[# .Oz7զSRzѬ8;w&it>#͍- F r9-X˻yowe\y;oYNgT[*A I'bZW&DNKj[> YNQ2p\}/s.RjkTj9aGH(xtK%T =i* K hʯxӧ *A鷏^Яkޱ3:CMAuD 왫^3{d,H p":F.+QzrN lNAxxMc~Söi6`I5q+\ϔO}gOg;Eye̶O90ݗ9Sy̾S^QTxJ#6TJ!00bQ4Σ@̤0ŝ򪣷.VQX/|CXKa,P\#B0jIewJu]=8m>[,X;3ۙY`;.(̙ʻGryj6Sh|BE`Z@QW[NLL0Ңh n'$fWWW ("(ES~9t:T*Ҩd2Ao$h\vPc:Kj@S]sx&v sU~҆ s{s!_84(H$7zj!!!D"p8RFMɲ`qbcۛ ,X`j뫈DbfgfDA͑/maXE& %r&J!)FhwckYX&Fvh)H0C%x̜lh40ܬ&,X-ɋZ/q &&Y Wɷr'>?r20ńm]['(irKecЁ3'maJ, Ai| B,6%1JJTq{?NP@S \y.:F ?湌\. ]9 ^kV5pys eӡo- / y,s(Z]!0Hrʼńm^o]`$|b3#yAan|͟mYB X}Z(7dVTJRn5_?NO}iw*b1ƙ#J0C!G q6#Fpxar-!BUWbăCavPz}Ȼu  1OleI` H[Jp|_hyTpVo }:}]Z eR*~/W>n~wO~22g}ޯ)6RCO{q]|_n;r)m~עؿPcxu4W!X<)L 7oJ,LUhΈ@nL0%kWm17'aۉG, X W7M´aqU#ʛ0v6 3nG y"r2~=o!aiQ9Em@owӄ#{Ѽro˛!zh,S 3*f}7o7*ls櫶7*g[㋱0y=<,*4Lqyحъy[9KGH"`<~r15 GLRNV90 Xז:cYHX Zߋf7Ԗ]1uM1ZB$ױOJZ3s*0yi s25/+i[eAUnVBUh0K7Ǜ0-sڶ~Vi,l7lvDxϜ鴎dJxԔAhz.,}0 "a\3 .US+\8+~qMy}I_/U11dNu*--ᦽfnАK̉s<۾b^u!c:}}M͊8pf,4|wzC~(B+&N /E iSk2 Ӻ]rV~ɻi6|,D=5 ;3#NEǷ(xueMfbؑjLȈ UIS%L9a,J9d8muؑ&"iB*xG[1XUFczx2Lx}K2k kUmԧ4P_z/194} IK̗'-6VHLS.3?i،֌&%or+tom\xUKMesa,hZ*!cIP.@q R@L ^`0kIL̬@N_d2霹cbG=(v{o7L:+#*++U*DB7+fԜ飙Uzx9/z6S_|p:gV9\8BiUR 5osr৉ZuI{W; 1;5yK~dQX3!k2y%B9Gͪaa}ى3Hʻt+朼m u9Ut+cK?vPS=aFGu*ҖX /$ Gō>eqed볫>{rlSw8fY+Š[Bs/8pf€OLTJ$S)ǃr>xqEBx.c,YgˍkS*`>6!Ԗp{nŎlȂl*HÞ]M%F%I+&-CFĘ{?{# { (m9kW [̀$P;ci*`_7״唫4k5h2`u/X"n63{HExi+ffFy307/w ङ(.g91c ꭟ͗+?mJi$)O+lC4A[o%6ÖERQ[Yq}|dddYY-ܹ}4}d 8'Nq: ,pGoN >, lY֍4 ̜Ȼn\{$‹L#rs sZo L{nhҺ c^'y]>`h*.WB3c";jH]L}N`cLsI•M9 L3hPXC_5UK+bGNJ0n5. O R]Zx˥DE/k_JYƍ{ofژ S2dߓVAjI"e'O}󳐝ɸff3*lK9!C5Lk7gcmwYXw[iܯmVgYQ ̵6q\ N(QND/Fo}+S^{qz5l%OPehtV&iE[}[3,L3ZV$9G ҼE V'1 8%hpiD =ϽvpsSg0Ur6fN0ZVsOZbڂ:go;O^ϿzH} !yW%ʐ_V8^!n*H0soJފ~ >&X %dęKydQ`B +wݾ*þ(fH[[.TtkĢOJ_&d2G ^[l3x 8bou)Y1G]4ؐl)^ &j q=\; oe9=%.وc]˰NEá!6zVgʛ!/f^&1ƈk6^f<éG2S'7&V#́{7=6WMx.muE[QT<7Ƿ7~v^gmEk8?XD)SLEeQ27o"y^8~^٢rЁ̇QԡJeff=smu*R?ܯ{_ٲo[lB1V<.^Aԩ\7g6Z"?ȣk!j`,xwb p!Oysﻯs}̦6ٶ(eGw@ӟ/zdo~^<+ 5ܩ3Ǡ(o:_~_mn:,Ajs\nR]Cm&aeBY-H]dQ{ %]t^ß"zH[F͵i`k$0% DӠI86YiG7Q_+|˜ NV>hk-mZ؜M*.͓Y d`D`_z|Tg+&2oW5# {!s J~+HA|#f^^|ے Mu:4 (ea᯶:R[a`^XbļX>٢r۰P>Om#)R¦F:j_X~2$0IIOH=:FgmCܻkuiɔ2L[%Sd3f5R!oE*H&M zZ:=( ج͵-dpd'⬺ZʕkRñLdā(T4T6/lFC[-Y:Bo]k DĒ)iq ?.]Bt71oѣVXIZ_I^RΣuJ.Pohp];^CV~yWT*ڂ!=km6y橭2~ūNǽW3]tn˾Y4ZB:&Uu `ZV6!`X;B!@nt2d j`|?Ѭ*:P_$|F+f"3GsiG޾&{/1ܖ`a>vC]`Np>K z8:î4$JEԣz}5*+?7tUv@ݳ+wz0J!O3pΫKo-Oun!o'y;0~,K(  ,:)㩹]#@Yy~o֔# |*k]WJ_ #A:fyml'N\~W?zo{xR+1ߎ'7u1볱"O{@C)^2am]K9$h:&u;7K;Ov C%@[}Ǵ)G Y3Ƽld \{oK@9~I12&wiik뉩C۷Oa]N"kɞC}uе;7N0 v 7dn\@ <*h/oΩ s'$"w-_[4xC1 }qւxL3*^1ՉC} E;h}a[rf6g ~HS,,64Yd?XP^q+sh/Tumr޾z#çO uHn3ogYsyf۽g6#ڷSck s{|=FK Ṗ~:n¸ofdMI7OGosW7xZKYw5g /sw=.EA$KXEYUڳzܑ ya?-5kFP;;aɞOGlmviuyLH|e XHhvEAyRȨmyi$H(|uXB$ S^:͎z" ꩋ7}z~e x<30TG + Ξ?G"@.*n)c$O'F}!w{&&3%P|)@t lXXdBbfH @cNy_ }燡L`2y2f<fRxF{ 3((%q:?SMipBGceA sWXiw{,ΜZ̅^zDSZ!I  gbQ~B㨪lǿ' gy{ܢ$ڢO+ɹC$`(Q$oͻOVG5֖x')!8FzITwHH1o ' ~*ߧ`W}`J8P&> ,-F\SLO^ۼzuH5#FrL0$` yɬI>)g HߘmfQI{"NοCl@=?&MPVV嘵  \.<!]zeaa! |{vQ4?͉84\WCέ΂[g ːEJmHz%^[-5Ѐ@"]@x)"4C֙/ L.o0,K?jd&ݲ]@T8j IE@Wb7.n˫}?`ABrwgg>t,+Z/č~mʒ^rV3P+*?JDh@$I/z~EHzT@a jI T u̢דrTDI|"EM88eZ-1F™Kc~sw»_MKԹCwj{/*///Žۂ9U3}v z }'7|~ ~w'{yvM~9y'-c3ˀ/T.==3yBճw#~1[R"1o"Ӌ?&{O%wNt!bS{3M#13-X;E.!R?,XXrԓ;%%Prhy iksW/hVhK'xeJ v曅}pF;^_)_{9̛npCwܑOnE$)5םc[✟." .7^;EYD_Y.PUJ Q0-XT}ku;٭5@ 8B χ}meȤF=;F'cdUuẢ^iQuw-HǞ4/7nhczԪZGn)5peǜZ7gZ]N8]ogS5E Ϭmyb',QЀd %J4G~ĚJ}EeIfI f?+xVJ"!3P0/Fcb@ <=zR$`h$P٤nE#{1ʼ|j?,*zɓRFy;U`|l m6mQ7s;ȷm!è7¸m ;qH(L諲+=S|p&PȪ%y&$L2nJ ܱڎ3#XtVٲ.|۹L\ %.\?ܵ-_tjvKKneju9 egw|>_R Bu qRp`W}z oKX,8x.n=`50~k H"r᛫5c ,X`}Am {$_  D.ʔ檷 {9 ,X8̛o\/I^g`s82wrc -*\tv+G3gT[*7 ۿxqXdexgtVngxԔAko.-c qIŠx-av_6  0g`.)gӂe+vwXΕg0iYNgT[*Ӷ83ۀ"(ι;'H#L_/fPf8ۇn/uw5R? ݴ>gŋ[{㹰p:\t ?^],wwF7;pl>zJDODYSg]; M5_Wޅ@wcY`;.J-sPad;EQmaMb׮rНF=s^"0 jK[W ʹy..{3Pu|fDOp\ K_T]^9w݆nS"yĕ S:Bs}8\T^X>Yrf,=mץiw|U{ qySi(w/˜|Q_ ?Ȳ2FHf?w.==-wsFPQ"<0*ɮ"QOUuɄ ;=OOPy~7?!}_!0fuD9Cߛ:8 ʮ\s{_>g1>4룅z<>yخ[, w9,sYNQa2w B{P?SܧYu3!%Eώxޕ{|׶/<%q3zJ̗P &:NXً.}>Fp\e,sZ> K2g*qG=zjF9S v ?Oӵa]&rB5BnNC& qy;zSߖ /B uT?Pql 39L0YݩZLtΌⱤjc…-ԣp˾=:e$e~"#>Þ kz6VlMR. |Re+h sZ˳̝eN ֢p\}˜|ǵ(4> ʲWju\YpBRB~jqljzD2 WE $c9T9_{2H"QUɱS5}V; ?FG(8BϦ;<}𒅅2Z?Ƃ%8"/ѡdt@)`V .ܵb&F0_թ(_hғ7CōA:ަQVn,pr@YVlE4۠ڳ,s(gӂ(w~2g*B9Q_ׅe 4WO]S Ew]/KH5:"m\YpU=rQq8wK#Q}b=9 j9"dZ\&;ɉ0٭Ja2K%UA=_nx FxEB.$KK | IP0|>"Nοǽm˦}/ 9&wyxM%e[ٽ_W8tGpa!s^H-^'!UKw(ESgc}#^"ׯͮ6DT:{wQ~I W  nxs={Y^ |7sv76b.,1<$Ih YN:>6a G<)Y`}˻~eT2? H/VG%NxƗ"u} `X) X΀r``&ők°4)Ԗ{EtxCb߉l"ܗGfNXj5u#0X̌qPpL]2dO`k2mj~1 >n`?h-ڰdŨ YNQ2p\}/sGA("G܀@Hd_JL陊P o ;0M4 )݃pDzj;Ό4cѵ^]"KKږ̔WH9ܦ4Qy1oEL3jYNgX[!sQ#@V;}NmvMMIo^jdd3sqI&dݔAcYgFݱ} 7[\&,X `n;m}=ޛclzr85YC>8l\ *SqZmox(a9x2# N4HG1ς 'bcۛ=Ӗݒu+g$H0,(9go % zD" ʓ25Tz^,Xp.hpǙM|Gcp2ļOO}ŇA;[zCYzLL$Jy\nMMm}}}Q } `!|h\tFk^ĉWX0sbȐ!oT<[_\#9~E{uDjT%A0EK" (gEع0NW0g T9`B1w_i 6Ra:,sSo( `+vBaPU$ދNP3$`HpWl)s=TRD2$9Zrz2(G#kJ4|+1sA vuKԼi=ݎjN-_}}sWϜ1DvϩSΏ]>#|%4_tb-T`'C;&;s޺nk0oauXΕPmEQẗ3'<(fQDi`,t^{M1qDX|@8(5ڲU#{lr@.QBY//rss7-BXYV T]'l٥2__+[aPڔ8|l]eOf6'NoM_j~;JUnMsg;s9޻EܲwVe\{ eaoGacK`t- I\p PPu]l?f?`3gΐ7#=:u߹"3(PfEkTេu!XUBòʾ\ 4?tӻ|%.e?c´nw(GCSABB~y Q '#G^S&]Du}c- nYq{ ea ,KX*2^iuNxk wpM\wBKDFHUB*U^uՅ:j ZnGXGݸA!ɭ]! 0J}?6{~~=CL %@ JڧE">99٪4?2'ޡgϞrwU&'szlm)B09+ˁP6,u,vh5{.%V=&^2 nZzL)dtQB`Q'[W7A`'[ZnxG}J-N+d_rV v日eo^дĩk?'Ung3nJ/wda/v[O&P&_b 3Ҡ+* 8ǭbN"d)- OIs͛SK'nq`^MN;jʪݡ3eil*Y5zsa=j, gT;~x%9[-o pzcnaMF vm;yktW@[#߲2YaAal_Ǐb8Bt>dR]1@ Pfp"m`sRHOz{)s:ɔ\: W'08b AY4ڐDa\N3yp}zwq6CPXҀת[;ZD-Jj:|g_+c<< Oi"iGov5eUH\ImTK2-TN`*t;ֽ2<ѫ,Aa1X2"!a\Ncٸ& ,OiEQEEڱADDĦm0t KkK**$r4j.ئ/ftvY^^q|4~7咬8ZUy5\-D暒rม{|jst " ^Faz쀋3nwYR׹]ۮ[hosG/&ߩ9%Rlw">ow#ۈĩRr/8EveCX |RWS-%]߻@׀|5ݑQ'ztNef\hM]ʎ s2%@8 àXmWhv8 0B.{>0nZ4>=/lZg3ȓ 0'uޅ/l[WG!qOLҙքauY AUI#R~"3'j8XU];AsҊ>zN@m`O_2Ggq?f#RqyEL߽ղ(x?gkktz_%''nx-C ~ɱÂoE J?y4ԩgxb.r^\oM"[Ԕrfm6}5;׋W?>g莯KmXodzxzUH=Fei8P{<''gѭ? ysEnas?Ȧ]6,e61~C9P5C풓99?͸6҅5b|^cy-$'k߲5Gհ'AaXRE y?_}RS*ֶؒI ;,=.fv#A|O.±8d>eK , zJFHHMwxBPvH$ IŊ|RJ `*ݼ(aLOkF[w :)4ⅬN1}f®cbgJ8j? ~cO,*1,<ܭ,m,ޗcЇ Vm\:A{TNܻw]{9c4,Dw؋7r{ˇG. m!VP}FĮ֙C 0|;VkUB8$ˡ .bd)Iwʎ6T ^mO]kz7`ev+vWRڥ'F{`p~r 2fFp/Q5PUdT;";kҝe?'U U羯ڞqevwRy>-n鷪::gd٨<[C;2q\^iYy6tѪ_>tcI3&AXzBԱS\kMY Uinm0 @)BOx͚z3SPUɫWT]ibFWwCNsjԻi^U fB0MG0b'z!z:dkQiJK$, z3 anғW>;>8z)pw[%rALjՙPnQtDN. Nu<l ߥꯢcױ[M9+?ݜ`p1瓂-B $qQ~0 !WSo_0SG섨gۆxB QCt.8yWq=g{9|Jvg^:]<`V\]~Ry-؍cuVƆǟbޟj(]'&2ٚ%H]${pDὃ_+QKO$ȯ8f] [}) .ذct' PrFHV=!PC!k["q!{q>U RY[Ǫ::b:\ShZKC@|MԹjmT!+L9Ylc4n55XP@GsM^ꪵ֣TkQUS7JF#E#`%34]5k c#;F}g׎C(CRU^EAȸv.ϞE_6fDžn_:pp?] [h}u%|%!j vs3'uN4 qB7,P<꺕ovR ,bt00/v1}  J'ǎQWP*(`B"1~05hv컼m dJ(_G w!оέ9A/'p[pB{%n^o}/wauүjnXh[nkwuO߬P?fUWM bGSRj(oI'$# WvguHЫjkrjWUGs0+֊ sHkGB8"!Halb@lʕ g8ɄN8Eo:Eq[E8A|F3R$ُ\2wfGQ#z;|]J>^)|Pή˓'V&V}^YBߡBKۿr$ݵ7nϺXbun[Ƣ{f24*lRoғڷ&B;Uk (*j+lp.;AFaùI[E9h\;qB)~SӂۚT9>QFYO1#m#V0 ;21s (VvbU|uƩ^ֱ,'2>rR/]~^j#{|dk-{7L?ncwd± ]}"*ɑR51ǢP+o[.Ztm]4|CS NL+^Cիf6lM[;f^`dUCt(pլ'2@'"-?:#ׯl!-mYxw-N"DH" ";Yl.*nRzS304\LԞ&eBߊ N=If|BQ[9'ɪ!5d+0 [ n޼ѣp,.*0M*6<Pb)̑<USOۮfz5;bed!B|!,HErĘH+_͡HoRn3)75-Ks'NZCowYjwf23XkÁH,.J f#"+Eyl6c090J12rfɾs ]aC.ڋ$mhs {d% $L~o 5cƌٱd2'"##>a_s6 (h=L*uP ,z'Npc '!Ř(zwS=iMMsBr캧oVO(CalCpLZLbZRiB. uqB{s-))cUD"gn-(UcJJRH0p#7L<"+ ǀ4h.FTkDdDx'Ss~iq!K2'6R8SDΩ2qVLIа͋f&orOVLiQڱBcM7<|Dž:"1b?;byg(#l/Fwi;12FրZ}7ijޓ+4\,-K̮{f23g6ae%EԿFP2Y".L^VGi|_deTRY%~JS愘CRpP:¨@ `do5;ߍ3F \|(=ʬC}Qip8Q Aa"{MZ#0CYVW#/=?0oCzߥ߯2}h|-[bܑڳ24OKxH**s΁uo;7w΍?أ*}~tKZIKb<gCmV77#<{O͛QMtx9Vj E{`i|Sr:֘ꞾY= c KwШ@x5/Yk$jLzJ^`St 1"P]jʧ֭[S[IWrT!*W=w4,yϚb&,Q\quG?K.lT-tP<NxH%7oy.0~$G{Ws/1qTpcfN!cW UNG!vO(zbI*$^ۯ}}f_(Ck=0WyUfXu/\YW_+;ROܲ7Yo̊O̧lxRTjƍ\iў,1siG6,KNu i-\9-31vM L w)(}A쾔eHy22"PuUA]Qq(}F#9{t<ݺ&Q0v6oN-i+y%6e;>ܪ)vHZ0f CˌOWFx }S_׳{g?nqY-xͅd8=|v10[A^;ϣ?D5V3׫{lNy)Wd'VMI]~!ک_ ;0lI &ݎ[Zn"MYr#awsyI3ڱ:|lHyT~,{Fs %66]2-@/~j@jBԿu޿{@#5^ƎoP47XhYoV BiNodWmܠWWizyAF[ 6<̉zUP]1@ Pfp"m`sRHOz{)s:ɔ\: (dqà,TmHh0^tW]DB Nm!`A?EKǾX-_poN]0 1ºa7A4pNмŢpO}<ϊmS}QPiQg<KZuky>TȐEUQ{J +7B ?T-Pxz&:IĂSpOg):E Gw괝oͫ5!<-ʜPh":4z*M'}_XS72ﵣ(!-PV-4T:559%Đn׬`@5Uܨ:SX=Ct?1XܠpmͫK痡R=8z4d`' q ڨ"d]Kjr U7S@"QޕNvvP1٥e-_~HǏ^J;DSAXIN)cW|W|}#G:EѼ@v|Bi$$rp*#7;ϚVP`*t{ֽ2<ѫ,Aa1X2"!a\N{eٸ& ,OiEQEEڱADDĦm0t KMDI84hL\o_hWd1nΫyԻquSrIV_[J3 V:ѧ4_eij D`hj- }ԥw]1=(FRT5饟$zS=GA U Aic>ݍ>7}qC@Dh'L-gNz>J`$߆,8g~.Y!fH=? RC:k53{kU )qt$ ]NZC@kf!3y'WΘEsb)D*HRGc6 {ǎ˾ژ:UJ/s'={W³i`ڢwrx0x v|7oz\-,=@3'؛:\{/;0J݆DtNu]VK1a.Q$q .G!Fw-Qf?~c٧GMk,sy^wλ-v( NSzY|IImf(oxv؄Zponx\}ZIAUI#R~"3'j8X[3I+8 j=mV~u{vPJi?1~SWˢIztbЋ-u}ܮѭ鷙VINN,[|cߊ+!h~hRS==\弸YCH]Pijoy96lGrBˏ.3cmoѫ,k)Iuҥ |48j-Kd *?~ߓ8Դ7A6ww-l0{@لM>.~vǣg{ذB}jTK=a\Lg+vSϸUJn_B@JBк$ yr-S+VH#PbO(CuLS9Ƞ. l#0 e񄠈..[(H4c3H19R)UyQ"]aq :)4ⅬN1}f®cbgғY8>1Ɵ?U?fooI.84e#X=htˢa႕o,mo^9 Ӌ!IK˳\sNw {x Ұe8~#|xPpb `ߛ ߟgD`Q8t czg3a0xU"@IOu$qGQd9E,xs"%^)|Pή˓'V&vS}ҁW +sKV 5ͨEaq[.Zۻe,oVO(C?H(֤}QJhljK+EA\40(V`U|uƩ^ֱ,'2>rR/]~^j#{|dk-{7L?ncwd±<دg^_i[x5sL([斋9ݻ%-w[+P9E```h~Z@ъ?].(hò6[K_gQ葓rq@lZF'oߒYg.c\ۥ{.Քq_byZD][G gxo}-PsB\N5MISʧ>i>u[f71 .M-l!XW I-M& S3|D?Nvz;r۠|TN<[$~v%p E cbun˙1oVO(Cz}C.&e:Uf15ER3t6I%VIH}|Q Za4j ,TyYI>L,Λ!+Ur3*joEA ͞rIA[^WgHkgܦ d BAF```@Я_Vv";Yl.*nRzS304\kO=MDsSˤƿM^d Ůt$y$ib̑<Uu?]o Q`"C"VBȔ/$"% H\N3P/9YRg=\&%63fw/vC&oju4_CݲzTNc|bˁYhIږ0@J@FI|0PS pJz'!䤂 ˄$މQGY_$zӴwb-k^s#CɌm:f=E BwS{ьV],S2'(|+FZ#|%{މà| (\0 &, +fӅx/vH?? T>=Uv$oG穭yG$V1#~6ěkf.m{C~9ԼդO+^ oS3ֺ6E`?ImQPIAҕs7 i0x cD$(.QjB+h&Y"]5aO3->)0maAx[;=AR#g߼B5 0=8xF0@~H>>Vַߐ!ɇ "dEV*ro?3f=1ÿ:xfW?.D?ș(P?%i&* bˮ}u+X(B09})ˁP6CNr}w=~W/KN7v-_j&2:vŨ@QtB`Q'[MR!r.NiчW`6֣|іRfXu/K "Wn׊luFtT ԵOʂXşq#WZ{' {svz :byp]C~b Wz@ L]wv5St & on_/%O8rj'[ Vfzb(]3IR@ @@0 mZ5vEDs,.BbmtD=gۼ9t\۔?Zso:#Yvhɦ˜U3T0!R׳{g?nqY-䖷 ͅd8=|v10[&C^;ϣ?D5V3׫{lNy)Wd'V>^[W?C湵Sv`V[mۓ'M`D(F>ZrfNcagaOqiI1Ԛ`(jYTD Yd1pXi\Ӣ qEa A44k^קwwmN8T> ų{w2zO': "._b}rpP_u?v.ȝ_,B!ROCtxιۆ:e9/棠Ңp!d@3O5QgoD,8ys롑-ivWxc, &H3(Jmrs$@ C(Q @@֥>tjc(FfڡBmc$"dBb~MK!O?z)Ma%9]峭;_y7/"Hi2X8O(3c e([4CIyڑgn+(of0GBK]_A^ggϿFkae _۰,0.JltX\H'Ŵ~]O" ""bӶ@ĊJKYތ˥$r4\m⽋ūC'HN mQ: M^TN]* Jz' K@ӃX `!%@X|P]q0ݑ pwwwceMs9pЁ=b>yq9:IFj/SK#0=vvřS^,Bg }=iأHd2 ǹ<sA 1{?GQl2hR׹]ۮ[˯PHQ;z:}`WiSZq|{LE^ɕ3ortQ휘4GB R"ј ޱ76bb]*gܹbמ=QA6L{gϯmc da0!|wrۙ*ΘFiv#A <D>A̭[&!t);&%ʔ@8 àXmWhv8 0B.{>8bP4>=/lZg3ȓ 0'uޅ/l[WG!qOLj5Squrqc.VФ@UI#R~"3'j8X[3CI+89=mV~u{vPJi?1~SWˢIztbЋ-u}ܮѭ鎛VINN,[|cߊ+!h~hRS==\弸YCH]Pijoy96lGrq'ߑڅGiex[NfF:ԿYN0O8Ȧ:K"DBjj 5;XO*@  0Ree ,z]0FW p´NG)r !᫼x!>{pLٹkؙڏB'fO}bhpn`Y< >\n;ұ \+'aUbz1X:$iiyxݻX.={ısDLob\B Bt{ss_]3# N~aL,w&Q}3*~A n8(A "F9tnO{ι xv݊{{v="vln~lډ\-D Q0~ ֓qlLw+ S][wep1瓂-B $qQ~0 !WוAo_0SG섨gۆxB QCݪWmƽs9{T{cAlx:HZ>pqtIc7&IZ>yXwDЀTC${pDὃ_+QKO$ȯ8f] [}) .ذct' P@mc.9bTQ«aVe````h4/BzWu`q?I6uN4 qB7,P<@J7;)pNr1`UJA>OMik`Y%cѨoFY(c0c$&#l{ akА)n7wy ȔP|#nC}ŝ[s-^N<hiqk>DH4!Mk3c 3֣qఏܳ`'#?2;1$4fEZdiH(`2QPr9"@ B mI[ 'Mó|wwV/>- 2A'1PgI$۝d̎ G,vvu%|ӽ8qSB(~š]'OLaw,ڲ3nn 0d1CSD>\)s򤉺Q4-I#A>3.5b FF<a(qwd@#c$P¢`UQMu,Kx){K^%}K{; f:xqplC, x^<;gIp0ߍܢ Ȋ0ҐGL̠Szс߄ٯģw-N?%-|%Q_!\6+viΑUG=֧M&}:Xi]o(,:lG3vn1Ä.v~KzJ^`St 1"P]FBۊ@5Xt/̈́`>K^&l}iF=%lØM㭟oIT"}£ځk{<* Z;@o]"[?vLZY.~C'~0 A#0C5 ?FaF, nZ/ڃݙ(P?%rp ʳX+kʨ>00 LNw_r ,(++&)A\]fϥdߪ ?RS]Wᅳ;]1*PT9>5?l֕ʬN!-8| uza4_N#ȕub3,-~c N<1 DIYP_3nJ/wda/v[X\d+3@MY`````hNhQX´' }D @`'Ik' ZK3zĂmZ5vED9nFѫ{t<ݺ&Q0v6oN-i+y%6e;>ܪ)vHZ0f msB5D8YOXKVQc0gT;~x%9[- pzcna Ƚv9ޟG#vm;ykfW@[#ZrfNcagauOÿ:L{jFR]1@ Pfp"m`sRHO&taP 6$4рw ,yz\]EM;P,QG˨Q<ힰ$\S*T yBe,}y翮]A!ROCtxιۆ:e9/棠ҢSts=XE ͸CQ HP '#?6]lS^ @6P:VLqIV.ɥKWVqp6336F"BLviY(/w{I?$]G/ $Ⱬ|>:|g_+c<+Ij)\Jy춂fs,Tqq+^m`Zż ?? t+ML7QŅdyRL+jon/׵.ҎǜJDDĦm0t KMDI84h\÷l Y^bv1֩@c{4E{Bul((hWl.eDLJ`%,:hGA U Ai/ www7V&|9 #WgT`24cg\9už"lkmb!+GR\qH_vm:zfo- B!E"nD=>4өTkh-،8D|=b"/7v9vNL#Ł!Q)tQhD`qzZ>aNo3I|1kNj va3W[ [!fHf`````u]ݩ9MO. H lx Xy%|nFGRvLK)5p@AڮRqBa\p|p(h76}{d_ش2g:'A~aNx _NbzʏB89בtFkqyfŋ?ӎ47Ǡ`t/~_̉8>E9(p`au̱AsҊ>zN@m`O_2GgPJi?1~SWˢIztbЋ-u}ܮѭ국&''nx-C ~ɱÂoE J?y4ԩgxb.r^\,R!ǀ֮b45}w|̀y_MN# A}Z-0(~wcg,y.Vtvk8d>eK , z"z6 DQ' N2͋x Q.@\-FAiVϏSCWyB]}`>sa13% Ox'v|bY55c-io14 8ރFY,z_؎A.XIrn.*1,)(@"R@p)aHWg+9  q:99zDN]~6m?7,u=MO;H;KeGjA{{~0߯3~ f3/._Aj􁋣O*w1qN3AS ؾĄ_&#0{'ͤ?ЫpI}y?{tVuxRJl]w-K;>þk*_3M^s^572}\S˄|qK}w,S,sRClywpfy;s{f8Ϝ3y ɋr_wBB+)R,˜߿_'wV~yck=aO̶VQ;yluӮثP jMy\"e҄#Q .*I=4b$IhjwvDLb&YVIw'TO-jlTy8F444FֽCw29⪏w~w)i ,2qϵJC,*= Z4Ǜ=_°=hθAS-:ތ`nVef;'3' @sW\\!SkY"K`4rےP7A 5js=LFV(X>G8?\}TS gNo]쵱Q&(Mߑn.ֺٛbL1#OW|(yW1BD4A*z$3vL J6ej59k?aPpK4WBrE;i`(ADA>`ʱ֙_<*ww=4v~7t`>ĩb~Ůy|nf%fn{1+3G,˭yg- Фff;ќ'?(A'uAETYc\Ƿkf-j7w UHd-wƃs)WO=󥂝 (I^t%06d?sEW+e~m 4(< .)f=Yc՛սӾ{ Z5f<;⥫;N{f O+RI;p<1B%>34bI=o%|UN/P)9(7K}Z*Α%AM-j-:2)(&Je_s7G`X^T 2m1z5`B$hl0+8[`΍ʱrooO嵾r[UFPn{Z럤Wz<NaS]q?3 o\~>#Dd|}i@z.==J=%/xl Ӻ{ +'UWU"Nl`) W~+=eն H"fش95f/ltߥ{ܥuMMTM~so5k_'[va嘝ћTս#o%iALm'"sfO[1yfiR;@P^{P^絾r[ѡ#(ER&zlmx<+ `Xd` :xq&$0+Fhh(Ap|T^zH =7߽%9}N }6IUi#wē*cS VKK_Bs˼H qc4-yQm!{m5cgLӫkR/nܐ;gNpG>W'J;~vLГwlQO.UE=(sPn~-oc&2F5}r;v ݏm$:i{q?r}c a;h[_T3MwD(A>ro3yO XΙ~;&FfOhՖ,@:y^{-<tBs=+OJ e@tō"6Gk{Nȇ "}w6>cO2dlҾkt7'bD6qE_-GDQA SDyOV8@HA]2}TݸqYf))"l19[A W%/")i-bMx]j[[616'ۺ:๫ւ|ҽlO?hO\~ϖcg Y^{9PRU^Zx6T,7UFnX/ 3]<+<~L{%c|y4t䜘)W)mћNd_SF,uyoۨV$7fwu4҇s$ 3?lDѩų|ncW*qvP_̞Nc\jGzbpŐsE|=}hGIDUyNX 7ht"Nc" -0|O>R+BU>"VgA*QtR.թu'I`pͺK[\90H}~|eS+{ZXU_6KHul唹·/讪,.Ls*TlꔔK{wו1zۃr8f1;7R&;D (ƾrs=g*h߾%[ bpe]Xdy°[;G\s=ĥ]bT:KqB$|s>}׿cU5{GbKZԚ޼U aNm>aZr(W~| {aP٥X?=jH%\KRaDq w\2(! A2Ӄݟn&f1{ UŻ40qt)Ww,gHEOT8sr!(l" 8w~*" @`;H QCz!0zĆ0LLy)$(6VƦ^80xʌ/@+VpZFQ-SBK.6 jfOjcx3;; 58 i%Sf|>v^Z)PN穭۷S(}C=u r<)=C AG楲z5%RՈD0U8wo- K;cVԛ.6.#ӔlW~E+{:>P"6e~ܹγX^:~uYABzRDڢ2={n}zG+6etOݠjv'/j;NԲ/0:›(:= \J(%1WR 3{x.<ãjaH6hl;qn 9)9zܿ|I]? @ܫ#?5wd(yk?7{yU9GM&q[jole!(v--E999.G&??':Ǻu0jM4V"GK-sɍW|9~+FO{oY Oظ16N*{xi[fV"[}fTŃpc֤f46p8 \)=MH|B_gm@DQ{Pn,Qq厫R{G(ƀ r5TTTPDFC8G ,{e)4c>33}oVt7B[vӦ6q8쯀ۃ8dފe?AM-j-:j1l<;ۏ pXDT$}I ݺY;'W_|蚁~ugX؍-0FQ{Pn,0FQ{q[jc0(-wf ΀"Xg9q5e@8 ,A({fv%3Ƞ 5` %"#qFPU)0Drzw ·!5|g&ϸRC'*-?3彯zl]N{ϧ.唁qN[~eVy57݊_4K K/>6ڛ(9NK&r>vz[ 6gTս#o%iA[- ɍ3`LhbAp|8*"#, Q3hxvx@Cl)2 ř$p׸qĹ{/-AɭݨO_5|qÝNwGjmwV3vz֛q=& }pCf&Gw 5čw~ӴEͶB߉XvcݓB,U5`k({D5 DBRt CE|Ŏ{U"`00Bk`*e5hEj)}а /%?{ٮ̠קvNgd_gEIVe@tō"6Gk{Nȇm뀨^ xD!Cd]6NRh<(0e-h πE!d̬`W ں>u+<a ݽ{I^!moSwLcg Y^{9P‡NE.^\'rT᪇QXӴ#Op|.D{~@=6f)g)xV|J*ʴ^r;ƷF`i$uO>:zS۩Tkʈ.mՊ6%/@H03_YOfn\4ᣏ$BZcG9S,8lm;'*r̽ ?*N\vpї2?};^+ Ō5wD'5.V=D I}DL[aKWolStܸR(:t)Ժ2j׬?OsZJ*W.^=2-1Ue󱄴]&^N|"BN=BǦNI gI &)\ƃ"GCP+Ő.r4#AD0- ugzilLDAct&OIP*!&h3~o,V=N=_>ݒILHH9)ڣf[G\ /Zl{};tiƇwkB =(48]ᮟKE^ $!H~zPҲ3-ߤ,>2f~zoc;:%P8gūOd뻇WO^'HDĹl9k{fqQ#tY s"0$buk*F#bխg`A$hT#3 SN!sUt?;&OeyT׶~R͹;-ZTe_ b_}"KSQ 8C3c4H.~_rN&hfXAR;C{ȸ_-oWeaw>iic+W-Uh|4jJ߼]߷[C[s}z'`ʂ̈́Ww6/ҷg:8c՚:O QzNT ӻ((ؕk7ntb;Ŏ[Â\E\0&DSw;B.BGX۸Ďz~Ê|Vk@%|WP)ܥ F7BtNN"hqۏV?Q(oߛccG O,}3jb*3E~x|}ڸa7ˈǜBxw}~Di֙و7}ARG!/S7膭' =*/5ȻF 'Ϩ6ΐ:lfwr\SP^cA0W4tT(:bYSm?]0pZ:4?tsB>`}p1bs e9Φݺe71QJ%萸3! /eJ@ $.a:'ǿe~K|zˌYK޼6>q7MyFABƤ~18uW̸g-OziY)d-z1o* 4wvHչKHWeYhѐc*\d^æ!jAc"/%1ɋ/MP(8o;AÓGTFO˼^W ɀ/}s}@q/7G{!YsKؽnx`$;%IrGb%b߲*:|$DSɋ^4qLwGRF LbC5PҢ~+MG[8N0UkÕO[sM^)`K+!|{{Fͧe!/h5&҈> M̨_ y̚1LƦ$+n>+YOSzĴ+6w0alH-FE4C0HTJERık5Db,p)?G?/afU< o0Og="Tl(8g2=qJD+9w%q.ެ.28E(F(NA#W-i uC]3Я>wϴ1nKԘ4PΘnJPeUBN)TqY :\Ŕ/YvZOΒGMpرM^Si߽xNE՝n->If=DYdw,biEKOa$'Uiȳ#^:3Ӛ4e?+'TmX^FtVt)3fBscq–v &aD*ŗqkɉ0"hM aN:MC`pIhJ@:ר.9(={jNR{Zέ1N1FQ!1 hB:Ի췷]Z˜!E1Gt1 R#9Fr4komP7sAaZ!`"9@"bdp9Υso } :O8=iϥȯ)S2Q4G4A8¹(fA8 `QkJDF,`U~ )],<+BH#_3yuJ{{y+Ss+nʼn–]X9fgt+NW|a.3.4nhol{;-`y$o3lڜQ]TS1nMȜٓ`G(Xb@8N9>2Bs {,&xGc|DP@[ Aq&)= /# el;*v/tBtNwGjmwV3vz֛q=& [ށ7MӒ5 }'>w`فώ z&w|3;[{ڦ [#D|/݀hQ H\H!2RqqW(a`T.H] jЊSV N}%mq7}w6>cv:#s=+O7H.+n8\sB>tmpGXDZk'2/D "ڰ9v @Dc v_M!ұH¢<6Q[wGTT=~``i0J1$v'y,E B O1Ǝ+Sd-{MX>@ :]غxrQKFNGa׶Nӎ\>!I*\IS]D:rNLیsvp?HųSPKpG?Wu{15ZK#mћNd_SF,uyoۨV$7fwu4wO.xBȄtz⡊MO^rY+b-yn+#.Zd+lF q89kg!/ZKoˆ_|T0nIğPud]i&/9޸"]>fn\4ᣏ$BZcG9S,8lm;'*r̽ ?*N\p! x iգ+ߎQJMNT L$F>!{&?EǍk+ CrN+fI`pͺK[\90H}~|eS+{ZXU_6KHul唹·/讪,.Ls*TlꔔKpaWh8|?2\)|XtYH "iQªa& >Dʃ$L6K<QUBLfފY(zkzP˿}?+%5<~xc1RsV7"uSou˯<JRC^;GP٥XjLީͧ;LKngj-wkB =(48]ᮟKE^ $!H~zPҲ3-ߤ,>2f~zocs:%P8gūOd뻇WO^'HDĹl9k{h`G}ȣ(G>2x!89D`HĠ DUn== Q"SaJ0;Z\2WYEL|lofggC1.+ǤGƾ~sNbBpeW\4(B!cˆq1|s`tJ$? C9]ԋ]MW43\ r}T䡽sNdwϷO6g=4<<-ml{eJ #%@MW)vxGT-A1N4FT" NB*r* Je$YV"^|\O02͛7O)3 ;t`y$qzLLMxw{f|yû3 2]x_b3ě A͇ B>MGM]%ի)J\`qZ;CUl&zT}/={M֤yjs ?/2l ޝGq.N4A(ؑukXp Ɛ;Bw;B.BGX۸Ďz~Ê|Vk@%|WA]*bNps.D䔺q-"OhZ_Iiu]ϋr)<0Q` |'?`QU)Bݹ)yG y ;EP>o4lě>| r)ʣYxtVPmY]# ws[֓gTT fM}'.M!55t9 sGC IBܫ#?5wH:}@cvl|p%t,!\acYa6inM9GRt :$. zdDG4KG<a| G7H>.g^+Xf\yoG<5Xlʣ0"J2&%`~+qSDQ澛F"N]*DunG-K*E[TQŴ"*6ůfϞګL8d$/7E*oC3 O^QmF=y/{]-$Np=v&X! bNu BRܑX츊Qx)r"*!0=s4MQ~q8Pf}=7=G*~}6μL=yoG)EG' gk36QiYH.es:>Z={4⭶&H-G>3ׂv&}L5Ӈ'IJmBsE&Lbj|C-Zp6qdbTI>sD%T$EkPӈA$ƒ '3}offY%ßP?-{ef`zfVrJ\Y]+dp` 1Qb QFv[8蕱f`'֟i3vc ̘@(8'⮉! 匉UV%dM5眵0{ϥ{{+!^L9pNGmqŠ-=[ת4[/]ioQ`=cb,;UVZת4[/EiznB *ͶJ,qy_:q\ Z:XQE8ER$-8ILˆT/֒aD0њ -¸yMC`pIhJ@:ר.9(={jNR{Zέ5=:Wfbc !1ڕe*la,҈9XHŐ*p1:Y{kqaZ!`"9@"bdp9Υso A :O8=iϥȯ)S2Q4G4Q+FH(EqK(3hkU)0Drzw ·!5|g&ϸRC'*-?3彯.[va嘝ћZs a/ltߥ{ܥuMMTM~soUs=?_-RIH1:~Jܴ '7Rdάn ;`(cӂpUEGXfc6;c$ڇ*"Rd3 NI| Q(cS}/ث/nY8_m<@]1d࿭f^7{▙{zuM %2cg~;{A*TYS?ws옠'or7㱵m!8‰(^8j@C;e PucpU5xq \ ՠ1̄>c;\^1{0s=+O7H.+n8\sB>tm{+? .UUs ߵCB Mwm؜Nr;Ip”ˢ)s?B:IX1]&8jNH⃊ 1/mF0޽$/ŶAh7;&رtje (C [/C.9*yp(iڑ'DT" 9'm9Xh`;bYY)A%w+:zɽ_!yӿuZԿ^ȧOnv$5eRjEҿoycfwYOh}q  $LHw')_")֋*-yn+#.Zd+l홭F q89kg!/ZKoˆ_|w+K _O\Rq.4YW8K7H~۽/M_/QN+= N-l[!6s]#{vzt寚5 )҉$@g2$^q K^<6~L&ol_BxuKuj]5K{k]ʧ9MI-%eE+C/{Zޖª*QXBڮc/]w>|AwUeWv!mdScS\x^W`G( %!J1âbDHQ,LV6!HT$ >FgXyjb61=תG֩g[R397/8^Y>y"qo yQql_#tY s"0$buV0n<{{"Dy0ÔPa1 vS"d)c?θc]WIEe}? )ܝ-@=d^13.>8o)NCbiOҢg<| i᯳/x*.VTN*ށ9'2~WUYXl>yz;{h5 ݇JMqE.1 'WOurJϸߧG~W4F| w(0BL>?`ͨ`P»S/&J|FCWKIVڶG_l?w؎ I@{x.HR%eOM~tRjD_@\x}p%t,!\acYa6inM9GRt :$. zdDG4KG<a| G!<|[]':W̘ŋt{k3=~#yjؔGaD8.dLS'փFn;:wv׹j>ӮYwV/6 +yz7c?*6ů ݵW B[CFGa @+>yS6m.0NfDaѓ2/B2K_i\j85놷 qHXS]!w$V"v-k$ R)EUB`z /h8&QLp#&f1ZU{(iozZ&T#ǭly'{Q=c;BOBw)K=;vCw I^=^VGȖvUk];Y>A$s6! Q8cӮثP jMy\"e҄#Q .*I=4b$IhjwvDLb&,yesLl@2J]ry7s NA"f9@,"SnK@26xC 1Y{b6c7mccD9EfvSCM J6ej59k?aPpK4WBrE;QlllSDVXg&wuh)GV "ώxN _ƖCL<5;&&"{SeiK+Zz %9!xNEYğ֬7mmY*{WyڱV2l+DF(0^qHwPjo$5$IḐPAi%%24^p^Vl16&Er8Qr[[|:YI[joQkԹ)f=5M||J]]iop8N1AaB,AAai N 9^w&yU^k&ܶVt(.ޢbs(R9#0,/*jTy{yڶ=_eB$hl0+8cg_ -unT^y&AywwUn[{K#K>AM-j-:2րD?vǩ?0`oq^Hed)ǧ BsUwO.xAfWoo83g\yXIp[va嘝ћ*r /wwiqF{%gyڗ[)g@1æEu[I?%nZVK[{_9 ,E̞\дGP^{P^絾r[ѡ#(ER(QEPlB n'I?8׋>N.sHdgOJY[G>:/78FνwY[,nSft,}v:!{m5cgLӫkRp۩7dΙÇ5Uon]?!n%/jN|ck/UE=(sPn~-w1 =dϮoX$HN0? EJjϯ\Cی9oǤ;1BjuhB_@%lpy}JktF (zV@ȿˀ8El/4]"5J:y.^{-B Mwm؜Nr;Q9(7 Dw~*" 8}{P``W qq*n7nh֬YpppJH&dLΖ{PDA}Um|PliǶAh7;&_?wEn9o(ڜGo:Ӛ6箚ڣbZY8Zs K?-cg Y^{9PRe^?2af"pĉ6fg)xV47B/`_K}-MK#$|uuS'YeהK]6I卙e?M\Czny_ӓW\pq֊Xj-yn+#.Z6ٓ&Λr̶9j2͎R{PaG11}lvvӧl7< .Vv4YW8K.*2;_tj5߅ogò˳Uy,p+?0 3}Q٥Xڿ`Nm>aZr;S"\KRaDq w\2(! A2Ӄs ]8gūOd뻇WO^'HDĹl9DwoM;Iq[jd# θQ4"VB趡C. BB3ˆ0LLy)$(6VƦ^80xʌ/@+V/pZF0Rp /Ҩ.R>P1b~>؏73n(d{;Y~gOK^ B9$C)1N.,P"XeHu0y *WȒ9qZ<߮"M6$-U鄑LSn鲼{}0GXEAMr@DQ{;rKlDa"Feq%''YfHتÇd)7A$GX Q'_JA=w7JT3Px e:)qV_NKBGa9-zc/5T+GY\\sʌ+q_k>Uqrl9U_p닥o^up~G]Mo.AB`5Ŵg!T!2@yj}6YjD"}Nr `!UIMoOhA!>9J =(sPn(orUn=Dy8NYT6tL;;; mOO?ۺ5\0.&goTV_OSԳ]Q?]@ B$2q瞛: c]{MXeG=)5uqf"hqۏVL fM}'.M!5O0wj_MfxӇ^A.%Yy2VOݠjvj+Ry%A5ܖ~pUZ.s='uah/JipXt[OL;s%7{yU9GM&q[jـyhڵtxV} s\&p C_ 4X􋟷-pPυ&7^ɛռk~fjęqɃ'cFo+!o'7*n:+ ff^JQ0D' s!#f#yI^]bqA}7agDޝ^Ը ׹j>Ӯut-K*E[T1G_JMy]{ 1ռmHUfE6 y'hxj30~j!BEAMrE~UnQFA-+0 Y5X./0 IfưDAU`|PI|շ#b6OfQ?.zf#2a ^hy̚1}@}xp=cs5Rz:w͉GTI~G}1gFpdQόxЕ46p8 \)=MH|B_gm@DQ{Pn,Qq厫RQw1F r5TTTPDFC8G ,{e)4c>33}oVßP?-ݴ nܤv8!+.k}w7Y嶵CYFPn{Zm3Fk6 Z-ӡҒז-gg.HJD/)7[!1AH8^+ENz@MQgZ&ZBP8H#.c5$Sp"Ca$X6ɕsݸ v>Y")(ip#\0OD m8v+x㍏??Éy ,+eÉ$w"Xg9q5e@8=,g# [Y")>&$`8WD138L >Z33ۗeM "Mr{رKL@/1Ɗ$7&DMԨ1KlHFz]ew3י9e9,7<iAv;h\G U}z=NWx~ G9:؞(K9NƳq_l?z=2Wd}?yҕ CkBuٞՀFY@ B R U9940΀wڑR0ȑ#e2 n;CA~^qm[8cs0j0)6%p.8̟|Rsy{=PGo4E|hΡmtBC/N{L2GĥwoJ>}`¢H㢽G.n}]߈[wQ5dz9JyA^dsV6E@ hA^(g;@k'f|䪨~jR [Eh1gLd]VGxѼjof9@ Bghqv (݈'k>|2!{%_ED+ 'Nqzs-tţ !'K2L-zv˭vl'oVF}wU/mjesQ:6؊+m*Ȗ]0b rx_0~+L-~q]v\ψ rqYb4OKgXW3gQcʓ(]' 0YÌܥkODoV˃17U%%OmÿEа o2/MnWۃ9dLCY{OT> HTw]4 yRxu$[*,0dA|oc;8%PqO Iw+Oҧ@WppzKOkBEq-EYʹl S)h:E#ЂE @6@sd,i΂a,X|K䄱xiÉR;h9UL2äL#`&8RZ٧ NؗpVZZҍ{!8P>WV?VyJi.Z_VV^kE4K#fh+q-iILX_ ]53\$%HOoGٮSuw?[v W ޢR{X(& Ę6OCUHw֥}ES={nȃZXoO.q5?R]ON^`ȼo݊m *9Fm2ssvC@ ͋]w3C U,a4 <)RSTiS %;!X^TahWWI9e~+r)؊;NԴIߜ곤g),J,0 -"G[]kR^"4\qXc6u1w F! QA-L>J2zOĠ‚̈́][zN%|AtXhhP,gN Μr@l;/PFn%LUSl"k!~vu{-CJnʥteb>?ܢ^-54_c-hbGjPW"* ҁ `wqa,UPl)86=r҃\xIWG؏8K<[Nʲ {[V,uJTovq={eOdrя#D6͸ooWa_~к_+Q~(<-ni?]q=N='DvE9$ OI7t͜\*#sRUpkP_8D;CRgXe68.eߞ='Ӊ a BKGCohݠ_l'NaWJZI_9~C̸?!{P7뙬c`w}XIv&; VBw?b_O1aW 4=I~Qg}G28o<34ʹe:nd|@ foy䀳!,.!?p1 ?g*E5RJ?*|´Qd,&"8 g z0`B9SszI7v9+r勒x1ˮ?6[3sqT"gQ?\ 63}_*ș]-EC1`DyKwQ |SvW{N̊T5,)"HNqڳ'r#4!y4ȥCqB.,rsE0r߁^3;sXʀJ30eġWGJ1vFxOcX~#K4I2 GY.wOUðns~Im3;[=7]v}`q='D^wq { kF\\maōgz#p\fh9@ ^|Xhy1'a^* b`NuD|\uJu.:L!\;h '1:d1("An񣅎YL`bCUUof޸q1S}%[nU- 1(ׇhj gm=aw?˟,Fg>\ͰݧF<\yQ>N}߈xgRuv8hD/JS͐4˒~>,'{ϗz}ſWyH.;Xgwc۟ش3o>=[y>"+us[bk'nニI|MOo١_=Z@ BKT&f<[rCkm9=|' 1,IC`d(r%aDCF~}xt#_{>nTT 'lq'=VhiK٭>e@wDu4p#zޜm[4y Vm|]2{o&%7PѺ:ϥQr@4Zhr>}(xl\퓙6aD-{.۳ݨwlN#CRћ~W&:P9ɧSb}p";~[6u >n]!"+wo`~硿OR j%CuW&㛡h.*l$+L^ڶ0H Y\a}g+sOM uj Wݢgj{O-_|ٷjF-0W0aQ~qVL#ھOoĭ(ϚqMpgj@44- @9;w@:@b `U#ZϿ&30\*a=W5eO=F) !l_2RM>,oJk f:#}<ķgwB%n0>8h9"U{?}?.I)=htB쵡s;d*@ Ek@[(p1pg =+^m;c'**n?t/H!F ̚j, wwcNׂuq+.SmKW+vltFsmۜ+XeJXEc H| itlKS ,mt$pg(-M>tѺ(Gr~RʫP}vUکaCm󎭘b{ΉަliE #VL '8^o`_K#1M:#AĈµzFT@'Z&cyZ>$0ĺrϝ={ Vm6.fOcEUh] {wsS6V ^D> 5 -lD.4h8㰺Ƕm>Խ?ũ{|Da)\ӥ>S2:pAdiYC itCy˥ =2'Hi_  Zy*M3B]2\WReAoٞs0OqE!QꈥbwpQFnA_l@4=-sZl0mgbf̕%$UDAL8P4gwNz]8`Z/%;~9RI^eɟ^?򭙹si*ɏˆ3bFt.,(|Qb־o[2-ǀ- ߙGоmNۑ_%91+SհxP"tnb";ũ aߥ L9oG. va-*0"WTj2xЫ#z% \|Z@ 3ӲO RXu$ d3)OWct>++ "cx KQDX/G ĆJ%+v73̲qeցc68J0EȽݪX+^pŞJ IQ±-gU=,_}2c~sޓӇ> -h=be;Wv>~TSG7"ޙT]*]4@4 xl HQ)*/U:n H6e BA E8tpZ(1h(A1S`A>:Œ^xK??ր,uWO&!x` 1Li!Q( ڂasXK{F4NA,bX(!)PmI[vi7l4_Zqa-+77o-SmٞMŎ. dv,BkH[fEl<B?"E.D!S,0!)RHZVSX{ R9r;g4sŭw-SkkUk3euXg h@gN+|(%>,` hE,PŌR9+T`.,&`iRmC @ "=|Ky9f C0l9cS08''AuHѱz-.x#Jv}}f1P< @  JRU09! 8s!,@ $`Y*| *G{8''!GİYy呍-~f9Lգvoʔ)qt{;-}BN;_IW"6/ kFX e]F Ѩ[X6 q,c?h0/Վ|X(``7 Sm:kaK\ (,GO0W_~ќC0DY׼nd_:;zO.|Dk1Oxh |"ӟڞ#_s.A~ᑔ>AރI'^:L"h L*Q<hKE?brZ/Uж1 9)hYS-_7v,_Z5nŜt*8m]e#" S熴M:S7 )emP83Jt>@Ԇ'Hc'^|uQU1HR еծCJ;uz:7>Znmݭ#gMmTec;ώ$<뉃,*ݮ҉ [Eh1Jq0+-HQhh34ʸ^; vZKm~Ăar>`S+m` ɏ SbV6r7 UQaqY\Ԇ;b;'z ){m(X1zvmztZimN(Äb4OK?#jϓOy'3.&; dܳǨ1m%6aQxbɣ\5{i7^!m eI8u߁2֐}+֭%!OcEUh] {wkЃ͙2ŝS<Ÿ^֎.2W]Cpx/ŋYv#ߚ;(<#xy`@gǀ- ߙGоKSs;\='fE|JX\VrML|8U!r#4!y4ȥCqB.,rsE0r߁^3;sXʀJ30eġWGJ1v@ Dӂ<9'yt,:EkYuWᔌ'1:d1("An񣅎YL`bCUUof޸q1S}%[nO޳U F45GkhĥOf `/{q:`r'G>5lΣǏqj#F;Kܶ븘@ <1E^J `ar B(H@.T %27 40F@b ,gW@ko0ѷr[ -}E*A E`T-e3SFH#pʂ`Ҟgl>c$!0JHr (C9djׇϩd9:קF^@4c:%Q }X#AQ>&oS퓙6aD-{.۳ݨwl@ Ίbw;0@ @4;L(0}͘Ƣz 3{)hlS7G6@  xf)?שk)mR 9ޫƳN57[W'g=7mq>Ƙk<@s3n7%u&uZCKCf]@!X$4& ґj͊<1BxD`I)lduou\)YiGr9ջby]שԵzr0.gʻ6z@KtԻRۮRƀV Uh B-Pb1,Ř6@ v8E@[ F0[ Ap gc99XD3lpQ4$Q @ ^PL(ZJ@*_ 䜐 F,`>y[#=@#yjbجUku?3W{xFYQ;|U#sb߼`y,n_cf'RNG~Ӳlۏ^1-d#YOt%b+knP]5`@ fQ,TyRr8 1b`jGSD a, D0j0)6%p.Sݣ'/|hΡm~aN~ i8f53Ύӫ$Bٿ) "?bnu}#nEܝGyk҇?;UӠׄ@ 1) @9;w@:@bњ*ak`2å]?sUZYfB͌M7A:y,I5HgwB%n0>8h9"U{?}?.I)=htB쵡s;d*@ ~P 0y;2Ж : =+^mf ljۏFhb̬~b{}/zx;}-H[rbN:߶t宿2hˆ)OGa4׶͉)r\eI*sC&j80'f ۲o B,+]?QZ|uQD ;H8tkЭNQ-|6֑sߦ6*1gG~h?Aތ\uVMnWL-"w^S}h3&[!hXZghqv (䚽\+m` ɏ S]6r7 UQaqY\Ԇ;b;'z ){m(X1zvmztZRMGrqYb4OKgXWn2LcT蘶n(̘]*D@`VQ,`xy_qڸC楩*sm9azd;qW`T'ᱢ*]^=;S Wd_Z,mlJ#>"!X_?#in~I [wi/i@ zg&l0؁ 8X 0p=/ ( $:}/s$*z$3L&O0^yFUT)-t JKKqO1;D`m57b>~&=͍@2)ǦVj-Z ӒB eu)qL++kfH. K#|z?<Юsʬ?F~WO=$6RVOU]DljڤoNY S rei/m!em%rA?mYzW`RUnT}㗻d8dA 3Ky9/X$Р XRʝ9岁w^ h@ 1SXCMX|GjPW"* ҁ `wqauDTzP˔{4L,M'ԩd>uTӲ(5;Au6醮KzdNJӾnjgUhgH:`tP+ TgХs:?S\AQHT:bh-~CԶ[@ Mm!8i "`s IN612WPT=0!@ќ)S\9CXuဉkd~c'"^*ы:kG53w9M%2QyFh"#[3}-:#J̥sbVħaeEL!DwS՞=aߥ L9oG. va-*0"WT)':?Wb危ˇ!9@ 0/(tK$Je2PȲ,aITkЕcmmU?'yt,:EkYuYW1zYYAKX"Ě~?ZXD &6T*Y%.X][1f+1W)E+AVsT2DccO8Of `/{q:`r'G>5lΣǏqj#F;Kܶ븘@ CtptlXj&++:vloTֻST5^t &7Al@)p ,BP,cP@c4+|vu~~},uWO&!x0*;4ݾF^ ųs72C_p>sjfJr.@Y Z3Lͧq` bÒ0F IVNlK"C9djׇϩd9:קF^@4_0F!pk{LKxdnos޴fB6:>fs<:&r 2òtkt~fP}XUTm6>i@ܺ=K!ލ{ h:LΞ2Mst]b9O `[ø2xۅEG?mxz-j@ @43\}0?>AoΨy{n(.\YO#OGĺz}Lq^ j:Vng:@;9X0mf=dc:q YCl,Ҏe@BcbZ`-i֬#TAGdryA MLa#@(H+]+j koVC[P/sM{W4k`K{n߫.~cʷp `@<*/Ƙ36s ı_$gR9K7d+icI ţ@ @p($qJ VBgnclؗz80{Q>eW.ƁkL8|GAu2?Z tS"&VmՀA \ aN`$[͢VgP8?99488r&ͺ\+l^;32W+c6$9>js s :}+򈆦i߼`y,n_cf.[)<NƳq_Tn'OyaȳG͍<V@  }zLw+((8{wO;k[;7~{]9 X}djag߬zrRo,,,G U9940΀wA e aBZ8) g՗buw_4ЃeCK[[y\uvŭc4DC/N{L2GĥwoJ>}`¢'Doĭ(ϚqMpgj@4JR rrr8]ѠV=2YFzq;E  @w%\.TT0;_.芰B֚2ofl)%Ld?tHJi[Mk8h9"U{?DA ׆\@ Q? 41y;2Ж : =+^m;c'**n?}Ks0R,33Z!`qoX mks)8m]e#" S<W`4׶͉)r\e!mZnmݭ#gMmTects`Odz8›񑫢[٪I*)e^yJDf@[=G`D{YV`0Z"n#Tf_\W<+@%= mV;oA7e;*6|{I GZmwlۃwN6dKS.Pbj9ݵ=FiۜDe3O:\\20+$q!֕L&AvAv(Wb~ڍsqȻ|FaBkqqY꥿:Nw}c'}5dtߊuknIٕ0RԼgvS'cv9#M6%z@[Q`Ried"CA3ꢢ̢‚*0j(8N`j $PB~Ze%"JA IHo$.[!=u!5ؽeP}o˻Nj%`߇KݽsמlSǷtα-';Ll ^;041KS+U$J|+ Bh@=pQJnIUX`ȂXqJ=.=WO;z jm}]"aF^PEu]\^qk?1J*s?JkWڸscgGl(p~! Y+_1Q@Iu_sY@A&ar0ix3"Ji& NؗpVZZҍ{!8xm{=o3nnuR/t>W^2)ǦVj-Z ӒB }I",Wvx$)[yۛ*,, pd֭UbbFԶV.Jb1PuiA߮ex@QT@  Ů^^1aMӶ[&|رQ~5N6pۺbuQFCQ$E/-BBa׵{Y5:/UDb!J~P*0*$s'd8]{yT/vuum䝝Sf7z{">~~1 !zșOMͩ>KzZYǶm>u!U_NL , j(R#qx2jU)eV7YzN ˼?fF!lq w Ltu6醮KC?x a)\ӥ>S2:pAdiYCiMI)RzWq­BqcJ ICgb%ڼ* }{O'3y+( WG, v6!n@%h:Ky\`\ ;31ɵ6y(5 L)Bb\ L\'$;~9RI^eɟ^?򭙹si*ɏˆ3bFtyAw5fE|JX\vǀ- ߙGоmNۑ_%9Oitnb";ũ/?aߥ <%hۑK-ℂ]X@a(.gvȕfDaCΏ蕘/pyc!nHN a6ze Ͳhc vB2`fÝC+6f9u﮵W%_i $>GX~_*-O_[+єdef O5>^7KcA ש.X͸d5lΣǏqj#F;Kܶ븘@ 3ӏKo}9~Cg0W9cN˖?sCGAܦoh[k!]_ktg[nirfdRI>ah|FST5^t &7Al@w&pQU?w}}GEQWRZfVjjjZfj i3 \ff`@t>o  $(EjpGCK2JzwD="o3aޫ'8,;@YĘA .ɟO I Z~ @NGGG9w>4$ǫh<- 3dr9p "XDrXEg-i@88ёcWx~ـ r̽E8k@ ģ( @ޤ_/K_37!M hɾ[PxsmFs-#3UTm\h4\P>-69^!N{'.ڞ(zvg9@ (H#Iߑ~vn*Y'"]M8Rirr޽{+ @ !E!Vg0ixK'#<=$R^x'K"""||| |}5رƛ9YӾ{.f<7Ƣsf^:@ BLn  _KrETn6+.y*W/qBWqH .f<7Ƣs[5YM[R=z"󲯻7Wie4뾶Ur'|uK@XXӧGl -8HLˆTG Ɖ#0"iA a x`U>r HBS:wչVS|(r8G޾># n`2N]?"jRmqy Cm.v b ??_ f'Z2QE02N-]:4 b`Vk2'R1 `!FrZM/A @ 7vb =MmYW+`$ ^l)ǹU7Ç0HvpF~'`t '˯,+Q3QN-@ P((,LK*dr9)qaXm^)Fho3?rFdPH -p6%#pms0W!Œ Vm=t\rpe_]͵lGms>(z 7^>R<:Ϯڹvo=Ztn̴=)on k{Е\#7>ݑT}k҅ak{USL5Ӝ 9W@~vC¢K$$ to~?~zO@Lh-3Ӕ vyQFC9)栢0X0eP :"X50E.%p2!|B}58owU=}cO2բ WsZfXj7aDcO4ٜ3gnyRP7vHx]('l2\]?këLbZ;5Ѝ{r;w@Bnx@ ~wsOաS'JL4iXqB* Z2t#q q~Rt< R\REcz&FP7ҩl bň_d/Y^.BM ?kQH]9 ž $ҴB6:ʟ5jلA^O#+ ZimzX0 YۿWi$ߒJyڴuqBrrO|9/@Mг@‚\L3+؏[Fvݰ-T  .DCbݫ e1n kli---dp |N7,Xl&Ȕƪ*Ffҽd\xiƷJ?`)_碶Z 7DQ9Ne*XrA2ݵ}g?tuQ4zʠ%I-:t5ネ#Po/ ]pC ?EJp3RWzPEMY3NJ,cά|g+󗌉f@ XQJ7lOrحC_Zy|l@_+3.W{^*Ϝ{n,ڬiSP-2'vίD\{,Xz_լ+0JA%ջfݴkd}t|ׇzzq+a+_CË'.cIJ9]+-l#'`#yΰgs7fC>iFeO`zBR1kl xQyIcZ/>1EfSZ$gvټϟԶ&))KҜpρ=/>XujoͥuڲW׽G+9jdҼS^~vn,@ z!>G{ |!lJB*5B JӀDAAJ)`6<'Kc4ɩ1sb@HQWvP (AHF/ PW=IDMT&h9v{ـ=jKǐ_nB$&O^m)*ݏĥ0}Z>v[66 Ƀhp0a`V%U?)=h)X*F&.W.W~ˏ!SºuO]c^K \LY$C֡3T*qIP(t:n@ !4X..{?*H>PHqpܸ28֍ѭCBf^eƷMSKF?@'9 p~n: ]=~${s1 [L0fWѴ O%TdIqa@דw5kj·+` EB}d13->9FDrq52gL=yyY *5Xo"~`z篷nmdsnwxUFUZUb+ē)oXdx('.F 8'Yʤ*r&aͥ2YPh[8=h4>-›ۧ[DOŖ@xt hհ^7QfB]x;eNBkkM>̟$>2@s(ڿ2)8DoW/4'󒢅bāѾl5-} U TUNGNXQEXlj }&y`bg/uS ~\4yֲ涷j"8)T0F jEUd{U(b@aX zi7k j2`J Os>IZٳ^ose6:qt :CVVVZZ:3ؔymxdˋĀ7ww!S,[6.A5EۣcuFP [U @- .0 'N9r{%,3-+^JnBW>g&< D@;mu|Ȕ1xyZΨUzJ N.B4bqZ?<;fb\ 90G?F?Y}CQŞ6ZwlעOO8j1=uѩ$x)6ԦRͶS:14vJX( idXvySnH!~4 @ w!J/.(0m! vg4ۡ̍H$ʍ),ȳ..n޼Idxxsnʉ~ <9.߿2+Z9p1`ó ]uKyPSt1-.`D4Cy;$*0&qzQ[yط:-=bzNÃ{2d,D1lLԨ Aţ>`¯'DvD="n1pdݜOg/2q#:>ǴuPeV-ޛ ^)aF&E: o¿sf&K7q+NY0 ~)k(+)b<3w#F,y8Ҝa.!O]pzB .0̶;+J2ӂBCE"1}{MlTVl?dU48nͨ3mHOlg{?޽{wӧTZziii]6U/^7eFu1YX qd"(\1 ̀M%*P9YLbf 0fOM[Դw{w1`)ۻWsL̵ά#A"u}۞ETz47J;gbPǽSMzldٛ WgQ2HMH= {fTmܪzB T]_aaᩣEFo?xr'$& z}A~nnqC.Wzz[QB:x`ii)qU 3®f̻6Ɇxta"PAsŸH-5hhcI@IUGm5{ugǽzjcx@ q(_\ rsspo~4M+vp,۶c^^gΝh4{nٲefT&M 7222|tҮje<!a V\.\SkY"K`4%mG0:r/|Z"ܹ4 @ yRM\̼7 ph 5FkT$3*aXd Kw_`މ':mN=|usQO@X+8ߺo~~Nt0Ir_MC *P: @ *L3~_-`ǺorfQO=4Ʋ]yz=xflX` 1Ρ.9*W/qBWqH .f<7Ƣs[5YM[R=z"󲯻7Wie4뾶&EaMӄHT&w(:.HB̸1;&gۇoiDZ@`F<0NO Pp/#Wȕ.tn" MUZME^{/yt8Qt5vDKU5( L:Af"#t+ Ȱ̫Wa.$AXg}v{GFf@C7lɬ9XsKZ#)8!U 0c,G@ F]:FQTAD,2ʧ,cu5˜N3.;#cm+`$ ^l)ǹU7PȯD3N8re%p8FQ @ (&Ea1 RY\Tq?hÎoBaW>ZUʧJv0^0jKfDhQBXgXB H(0`:xR,[̟`EPu %c<l@ ½T(Xˉ KV1 ;GV8nѢWP\8xPz*.3&W1G ~lGL8ء`:8pYb8q`yRYW`,C{/瓡#g~ߠ'|bڋvͦ:0fIxXʳy#?m7!LRԇ ~~ه?Sc,#\Z-ǟ[>{y+Hq%Wmv8Z~9@ D=+ $`jWMo| #.9&/\(8NvLJr@sh0K?NfgIjB_3ӣ7[WC`D0F,8'$ЌE(xH@ B|0Hw0zfqH"tm5A˱ۿ5T]:x?_Rt*'1yoM5?{H\>Nh_\YD#nnc& ̣Ԫ?k XV ߻羷0t}}q@GxPBQidR-r2P^aH.s1UG!a%H .&V,CXs!x$:e 09m㬦JBƍ1FCCaj=3p>~=Z!ܻX&rSgپoOHzu&~1Nr1  `4 >v?*8wo"*VAf*`'5!ijVVz?C)$~դ0 JɻÚ5sw ?{Ļq*̵ 5 !̴cdrXS8H2?O ys~[f*.VTQ'_o7baܜlVnW'_Ea6_C[$}'-S@=o< SZ|珍GͻvK Sg('}N՜is ``0ubʵMvT:;=?*bKX yk  e@@lGJ4ad.4 pU6(NՙٔymxdˋĀ7Nx.qe Xx_u"HGꌠ)c;Z]*vaNp/-;rJK۳Yo+>[ȟFn?q%7GoL\di[D@;mu|F?;_8F< ;8Byw6* \ oWmCQWZbSŞ6p$En1q@;czSI Rqm~M?>m'tb.i!P^5t+i|Ȱze@ DxYz ef2H.DݛlAl@0PQv\]IFoPf](rK:;'Wgښ{//sPK0 6._gpvյk75EhFLp@30C¡!Hp1ӫ`cǾiɬ.1W4֛wZ)ba qİ1QR:(ةOGq#[~|;8{_wԱ5$Ihw@#6:pbr{Z> @ GDz[α$pIL̎m"k#W-4Ҕ* `uy\iOuniFH!C伕,y{8W%b-8 Zu~e"~5+FM\+BFȕյeL/dFcaFrڷ&X. t$%I 45MzX/cnr]u04A8C-Q䵷'@#wΧꋮ]wq*2eAHW,eqPbFdʚ_ $0- r  J0eϒ~x'RI;s(~ @0?Ҵǃx7(ۣF^ 6rנA|(rػT[\u.Ea5Lnrs= 3IzHv?RR>jS^999d sv^^ I8a]"r~~V|^{ \{y)t# ø3kQ\wչ)rgC9y{]u\bDޥǫ†B.믿 @Qqpn=szeeL*?yEhT\TTk6,8Nݺ3_8#=LG Z`MwhW耏L[ʖ\ٳm\gFZq%֚coae]^ۻYU5-:vgqݟ3mt,xʛ;,:*oߚtayWuU1Gf[h+j7sCP䵷ȫt7k__(rػT[\u^E!@Qxzz֏Ns@X֯sO<)"Ϡ K  v` C)L&& 27ˉ7?)eA$0aYإZl=nΊ*p߳_͝ŔXEpu ~35ʋjԔB7~үn$0fmH[?zܟ9~F^'EZkMFAG'v.yQUorLH=P}ƭT#PEQnq"w꣨}F}tfL& {ɓK4ncR*Ca.ủi?Rch$:@0q q Мpn4 3 VdS&DUiPKt!XcgX]?fON $MHח_y`ocrΜ=fI ;=$]VF@{yA󟷍 vUר!Cdv\ny]W EQ{{[EoF}V<a*bNow{dRRH%xC;`LJLJKK \\$5MΖ _R݃,?ƁlibճlX9wX)p=_ն4)=ˆ#k!eN:R!_~?,\,pX1o2/%{(aidD˜Q[VUw ekz?9*&sTD@'컪СBu^"6lN7׿>C=뿝V$TȊ|.Cg)K~vI ~VEgN|>3KyΞDE 6r43fAx#wվVL&J%n3Qw9P 0|;w~:zp`*7XjnAi3χnڵO.*>}WL .<`1'TbTi)+G8kwXaY_6c³?;Ml(UԒ3+V5KO:zYц;uNJZ0BH!)^H lgM{ۺn^l6աy$gvټϟԶ&))KҜpρ=/>XujoͥuڲW]eNJN,p4orNgC}G9! BhxWzl=p P<8'ڶmpXm4dYut\rS$O&*z٫arvWL}B4Md$D_Ϳ\7Z5UqU>K<}%/BUU.S=>L!F dmKу=h)ZP~h$?R=l"#< (P(42uv[~Pֽt{ M?woS,KÇ"%X cICgU$<nEEQnq"wRo VQmwD `- w JQ _Zz$ FGW+RO$d2Sk)׉U \"'x">|M.al<+ݮ\/%U OjApg:O#{M}dV>cdrXaB>*\ R#x;wBCCUo{\ɖP4C$t.->}f7^}n_(*L._tQZ/)Hl]?I Ϟ(y,9\LtI`ܼ޷XS&WKIW"$ցJKSq-CTXɿs2,29V@z~҇] ?\0G?F?Y}C^};8Byw6~ř#+(Y MZ[7Q8:/v3v*qB'N|5H#{2Эϛ #o'Q䵷Gy̮s`77]o3ŰA))7 AEDd^ s! :;]#7XhSwtd(yTF6/N=WL h~`"2(yjp7~)kMXijԷ.za363 1){o%/!o$12ih;6ͳs?1GߊaMA.U'Lyl䧿y捧AwRC@i⽙jސ2Me-l=i EF :>DŽVw"]$;jFhXw^tʂiJ*dE̠g<3nĈ%qV#0_32䉱^hV?&>ۣEGQ{KUG! rW.h(ljd]\?1b  ͺeN)*!W-3:vW`pC TY, l$c$E 70OiEEw);P o$\,p}M\w`" 9߾4.Q-?>޳%L_giYTN%GqTL긷,FEzldٛ{vl(+ӊԄԃg&9Joq7[s (jo"wsC}m7]oB@hVL҅("K1~ ~=&\XEXIZrI"RLr2,m,g\gh4zzy\T 6u޽"#iksFe?H_kVΡi`0@0ѣЉ AdkPP0FDVK8G ,{e)xs48Hѝ?xbov~oӗIА0gՁc,;=<;  o'JF^AEj? 6rנA#-.WJQ8 : (k[c8$ab0 p LbR44cY= +5 s;z&M! E"LAzTX7Xt:Ljuq~gXؒ d$.P\sVk|wXcB.NYu8ёcWx~ـ ^&Z"{(r8G;Q޾x#wվQQ+r,ky:$p!c|3 48qB&"$Pv`lci+`߂PKbDBD  B&$x2++KReHZjSWf^ \a[XȿUcQs6S9";qDG)Dгw>[1w$(r8o5s`f}xbi!"HH+Ђ#98)ld#.G@%ernQׁ9;"H BG0IۣEGQ{KUGQ͞٦k? $bR*cM~W_DXlF1]g\KH!!xQߜ Prx r"zd`偁f?w\ꅮfQAcY].ynE<kZC( uk9Rkym/r'.lU.}1`bmq5Rp1·:ϑ㤂$"\%$81aZ5M3nKmF a2o~@5eJtReqD"+^x@Gaߵڂ6\ ]͢!=4Ʋ]%neW7@@]㍼;\fjwWGa-V= Ap".bJ,8q"ģX9̕ڜ #R> 49;AN@TV>IizĉTXOct}@Y~r$Hy2^a8#"1~F/C@ S`MTAr*9oӟ={7V۷YFrϟ=7EiP> {zLڻT[\u^bTGaZϵ=0🈿Ν;ϟ'M  apwF[[+G ~svA E|jPW_~P9T劂(`;\P̈ ΦP`NTgHdo1 zJ@zlb:.92b jeKN8jsu f̽ݯBu4wDMy"u]s8{߈{ܘi{e)Saxn]5ySIu۷&]v^W5+9W] ͩs]Py;qx#Ty-D^jѩDKU3یl `U6Z <'K6<. Q5v}f0_9^[l0Qz( 3EF儧",s? }h)) D;@Ͳj`2]NK0%дqjKqΟvgٗٵvfz1Ƭi>}G9gH֫h1LnZ0u)W?ؙ?vj{?'v.yQUorW^(T[FPTcVll4ȁ+AEx˼6Wlt9h0jRmqy=+mVCxY¹RpLGLY? 143žug_egCs(..7nR!PKٕQ8.nJ7:-pN'EǓ.8P.8REc ;2R_4L;k5jلA^O1HQщ4ʣSHQѩD}=( Wi"\ >S< 0A: vsTʖeG6c` ^dTw4;aСCway楗^2$@a,JQ'(@SF,  㖑]7lv@Qay\.00Zߋ.DCbݫ e1n kli---dp ,,G6edWcUj.oJqqVe/~V'+j€.b*^(sQٔeUDbyƧ.0fWn-#`,҃eBbqh\U擖b~:U\REMT} )|EO9q'V< ܜ9rgZ6N疏:o_\fBTTG8iYqv̘0A61,c@؏e~*uh r/m6[?x=6O.t(x-=;RX)~Sg}_ΐ[o?E I#DLev/x[?{6a:iL+)<&|bڋvͦ:0fIxXʳy#?m7!LRR9႟'{_'}>~KߚK]esg<;ʜĕ^Y2~hi)o/? wQE+1M4^&GvqY0|EoOFRYSa_pjt>;(ye^y* ܾ̫-_%UkU8''rw9we5-ϩz^'ڦ|,] ?xTQV:&/c&Q.~.E?gv?E3/ίd^4#D?,Z@ B|0Hw0zfqH"tm5A˱F4T]:x?_Rt*'1yoM|iGRqF/Û`u -lao_)^8 D`pPYjUշ{xvSSZKMoIO T( Lj]]54B.uo#]ޟBT,JÇ"%X cICgU$<nVr@) ?mZ:XUE;綗9r&&&:@U@oreq(jOF. 4 S巇TeXyZuX8Q}kM$r';U۝\؛TX?Cq8?ݜ0SsOg./6,3+((<ᨧI <Ik׮RD"1r`BϢDQ~1Nr1  cѵǏTnsơ c/NjZ+B)+%L_լ4StIn)&ٵ*4i;. Rzf͜!0o"cgS˪.VT{R'_o7baܜlVnW'_E1Ri5E}[[<2/kOEeV(paӓOz>d0WwӄTQ~Yzx1#W֮gm9RǏ7:59M.t~xޙo>YLa w|uZ C3xPIP c(0BB~>ΰ;ږճ{1Kau,kdSʲVmtZjz6'KJJJMQT> ުF~' ~*“KH@@܏%0C~, e4iO淧^^la$q0hvʴǦLX]݇P#g.Xm??Bd@oX{*x>*X֕ SM l><0`'Y8ŐEg-kn{+iM* "H1BV$y^۟5 WX?׾3\km}& #ά9s1e{?8FH/ˌjj Q>빪]/Lﻫt9#*Xݖ9H6 0{X &Æ׮Vt11=!h3rL)\F0 "$D\. 1c."( AVņ 2 lyp.n7q言ĥXk"HG~1@- .0 'N9r{%, OJ#{ן#&Y4#C !Zs^'ԙ~g AQK?'S4oU2Qo3 MZ[ ֌ѩ$x)6ԦAͶS:14vJX( idXvyS[ad[#G\=U8!~/TtB <{jx+Mx揻$%Lj)t?=NվKg=k|1?\?Vb@9WLŅy`g;|4PRgJ$-#n\WMBbD\/.P#vөA0j /ݽlJ)RG[eFA(DFAd^Z2dZnJO.^h{/oYJl.ŲTSz=&|We~*nr.Yr~rȟ|^צ0PgKŲb`]\SkMC5 aHFWu.CA2L%F%((?hNmSOE9kBRrW̢| [P ^ X`APr՚;'T) ahpʔ񞾔ǃx\'16帆EwkG"v㾼=}pIG f_qay.޴L^ ?}ް*0HUésPidjۥeisX}W0?KYϸqC8O ljuI=gĮ /C! $ d# wkzLY,wGĻ.AjfjE,#?{zZDv?N=g}Mί{g{o͍Kήss+ Q爨/ub?Ж=u vj5\N __pdtn};e-tҮ%/Udf]iw}\!b|7侦6꾰Lllg4#0ikҷ{Cz9pe8'.a;x۟9ϸ{lʒ dʘ_l* 5`UNTRZy*.ԪrN:׳Ϗ?Bsט[VhoK/8R(8r)/Ub5Yg;HeY@BM2gMyrpN pNqmg*D;,N 2}NL*&xL['p8PN2m̠mÑٌ~9<,IW_A. v<G_p-Fs<9DMݒDD % ᪌b f6iUIC[VCcw`rDР緿g  W`nQEM6 :/>}:7<|pnиKYm;5HS|.%;Bo|0Q:ꯑ)_#E4 )F}潨F on_Tvuh^kk y){,:53m<1Ӻ{xEhA:'cՉD0jœ?5utsn[6{,4<03Q&J3o]Q0H^˝e-7Qnn0e>.泵}H$YN|#מGT"n`J qk?>s^s:)?DΚN%w]8Y@ES6hH#1˺Kť;b;eOlr"ynt.·UìEF%-3Uf$r}HIfWI%Pnh9$EaK<ϭ0lџ(V~BʾF/>qUU1" v&ȝG]pt-hG}5m"iϐ=I$1,л7m)"+QޔGq Q@j9CQPNRI0hX/ b $A¬>aSrD~=L F PO\YA?dÖR, (p@4 =ZBRRRi,6ێ< 6~.zFΟ/r^_qAsk8@@` z* P' Οu\:^ڼg|@CG?i+/Z(I]Z;jF _]DeQ\K_@z5ߥL(Sf06?ZC)!П6:=!FIC H(,.T]Zŕc_؍$55Umԩ7,x\񟇋$G6H`@ՈE"W9E l[Tս:vh Ԍ#߈0z"~Q!uĴO`])Gߛvc,D?hS ~㉠a6!O8x9m(̿wxCg'SA>6V|+p6[_y%\Ԡأ<|df@\/:R/ o}/J[ |cad` ΏG)C[)%f5dQGԌ)b:y$ĉ־ ’$IYoCA H@Z_3SuyqL* ~w\d_;'Ҁ2Q8C%ݨAآO 0r#q3#F-EƗu@] };NBR|BIi'U$A@\!WZHwIJ JV(Ցi/;56mUOY5U5bSJ[`"|l2zvѠdUY_ -98iקwL=( ;z1une'1GA@IӆFzr ?N謹0Ҋm<ן[sdIymy@ o cs%~P*T|+Gk._-<;b}[29h@<_xPSeLEDA+IF M h"U8_  ʯI|שSV'[O>bX;|bW@L't-Ca񱓚0vͩ#J*$B O'G`56d8nɹ8p Bq p%OVS=T": Rj/\܇A_RO0] YY-ZoΞ@uzbddТlOꏙlt7zX=eB*,GT$~!rtkEU.~\ߡ9e!t{yPĉ2y!ً_wt'-t)h=#wr&NI!̓;h!E F/cz|Ӹ6yQ@ya#Cr&; ~}aqҁyRI'J10nY03nvyr6~c9oġ=\JlPq؇A$h㓿/[ YN#5㯄=íi]E~ܺȫM^OugC[@j(lCQ4 )1Us'z2+,8&؆ mY7jc7wM?rG$wcٔE (E8Hܢ@.*e5^XZ~d*%P!l N2ӗx0t^kKAZϟmSkXtv/"i7wz aQh w^ֻ>NG:;Gr <}j[16ésPidjۥeisCݛ}W0?KC3a3nw7}@`.m:xcsGq>=~]fRsص"e(\$zD!Ұ1nMp#3##w \r~<שE_o's@C}&}~lah:77*u<~eZސG`]6srҧ\;a!)Yގ;Qѝqf]iw}\!b|7侦6 o^V&Z|ts/~pdt}1 R2ǖ=u vj5BW[+#%vTb'B/[c]"\.W(d "n?^{9c7/SRyrc+= 0'pۦ,gX@^7?zpD% gѡ<?5Y5% $: R(`Wed 0]wIME<0ܲD#+wү' /W`nQEM6 :/>}:7,V@'>4y[ޠmvOdP#qG荘Z:JFk$1YqW8IR7E5RxsR۬CZw]S\suHgIǨMOyl58v߫/vd5U6hH#GdD=yY:>kW[ preSPmq9mFGk.l7s~בϣbfw_VQ4k矇x j]VBo2.)&v]j#OQ\mƶ61k6Ԫ@7< AXPVlz|W$qy\ht&g"<<.%T(F72T yUy6ۥ&f]qI[ͳDK%gQJsOgDGpŤ #9~80!}@ Ď-3,6W㳎]*@ Hu WIIu)IZ[kUdlph> m^l}Xuk(ĴcI@!1BGU/$b%'H\ACm{Tz9e{9ٟlw Ŭr˦Z:ԋUM-hy[N5_2+TyJ9m޺uibM-lLg, P@ X@rm ܉hIy Y($5(;Kľ8jM͟X@j CKav۫ǭCU>R/Vb0A -Xn]^% ;qsu!$ v:5 |R < P%(> -LZ 4s_*8t @> ܨScLf#+#DLix'Ȼp薰uh1sBː:]*H.B%C?>iX ;r:-"wtx?ƍvF֬BQ`I̶ [av۫*,6w}k,/bU[+V 3rUZUnQϫ i6Y-,j`1 =~/-)Pr'(T@*xJ 7{'5D@Aod'b$R䄡ʣEĝ=Gx̩_4 ch=67 XBLHz>-jB]^?%n{6cq#'{V'FL?3V9q2"ډ6.ިwwP?Q%pDG+x9)^4UM5v[KX/KfsPm,Wޮbrs]aF?.qS'M_P:om9T-]4(_ PzPt8 $p1 ^SU n C/mAQ,3:A,E ?Ve k^-||fk~Y8UmV-˗@7A<@z#oD#7oo?"賬i ]I{Hbaא޽wmKA T'I}nGQ~[/+/-gaU8wT*yOk4OO>WWcFkhB~mPwDߑ9ۗ d?]Mj]f`F PO\YA?dÖLd(p@4 #j6kZ&A=-6yM^˥\ח6|?d5xP윁-Q*8dĨ o?k# nqc]J/ϐ +RY_Z;n_LM9/L8xὡn&Obޓm=eua6WA>,NO6M9g=%vHZB}o_z|`lQDub\˔lAR% >Fo7]}e*P/`(lm [;F$̤$g]]1XbkICw69}aQFa}=ڸ>z2ʖ<קkbbnB[nxj%e(nIt[Xm1;.VkdRnYy,٥j;S`r+(f)-:m[Ӓ"ǔ;ARe ?ss 챶 gB~c׼M|@CG?i+/*".X#^F˯:η y֏-ZVj]υ~["@ZJ302췬dm܊rkwQ6uEa&(ɞRؘsᖁ̉\eO2DQ[,RK)<%c1QCeM4PQۮ5o9NNfZvFRQ)$?bamH%z" $=+,Ф_ܼj뱧|_~Ŗ'fCT9'z|)426 R]$0wj"sJ"HG X6-g^ 'P3~#v F\ЋE>A;tP|w:o ۍA=L*P<'ֆ9UƖ?˫t[Y|+0nzӏL`IsOeAGxgm3̀h?>^t^hyu n_8>x1_Dz02#”^VPQcetBj,'N a=Jyr%w`.99vYn=V*A QH킖8) m dDქ0?p?(:Cխ7huNzXQ༕}a糖cWNuyq D@i@ X(I@ l!v@4[X8ۑ|=`T0>>^U_ו6]LvﻳkXN2ѥ9̫OoPe4ĹRRTS+QC `RQB'@UA A;Qo 딼N)'[O>bX;|bW@L'tV/a񱓚0vͩ#lr 5ގ6 HͅI QlU>.qHc^4!`\Wrvt[mCAkX  &lƜ#^9Aܯ. ]sØ|1R$bۀ \ZmEG 6*[~_Oz(Yv)T?9=q,wA˫/hܙIlk4fe? kT&^! r}L>4~z'?KssPYTl=tL8$8>I_$êGi_d-ͪ86?L @  + r+1}S{D P7! rBJDbe6)UOu;䊅 @3w#B$'qf8 `k1v dTcz&rt0Ay]E 9"KWk8Ir3+V+-Ө:=1Gb2uhQ6KL6rJUMy=,뎞2g3(wm95~㢪u?Gy2_yĽ͖oT֥ o>\]y>1#568.e_x#3ǥ4BA$H/[ YN#5㯄7zz92$W)է՛1;uҳcL7ۺz*Y b/FEz[~L5)þ M>{O|kLj\IzwRHq=yHknز4M{^X,M""{>v\y9!߼ueh}-|U^%eu*.N􋷒Cp'6\-迏BzyecS{t|_.^-K>Q4 \Q2%pFB9xE7\ APbUBBekAUkr,R* P)S{R"pmD#Ƶ?ۦװ|(^Dnܗ<);Ì04^qJkJ#tZ~A͎ޒl}ͥ`j^v#M_5o\0w,]fE< MxwӚ=Ҧ7lOkr~>s?v{kn4]rtv]:GD|K,smN:׳Ϗ?Ϲk- e=TgD.hde̦yک[>9x9˥ENBQWܲsqߣWO>{gk.TAK4W ꁝ◄*Ez\"1u(hxn~$DI@*&rMZmno/ _y`e%84vGh V%lJT Wh0&o_rf O>}87h\Cf/VAN  *m5tɾFG#1tNFk$@ N,7 pL۠$gk:ryTAuw-;툞{^Qz7V}M!"t֌w/+qtelАF($I{aa3VNtE¦*\?u"p\LGOI22Ԇ$k۪!ֿ2i2,gE2QgM՛an^^OI-f$\X].F^&J,כa%K=j($Z /PIӥQO}潨F on_TvuhS\RYt1jDgxcu j.tk-]?hEu\ަ[K7"emBS2yoM-7ڮ,)3KoxA!&0]R\Sbރ ii..)8;k`Mە-{g[nCf1}E;}\VIf0b/jof$E)$8 ^'j0W OeP:kXI*c+tVN@ ;|C O ?)৫$|Rnc8X*ҡe+r"SY-jQ\ZUn.,qV>tOWL7MOO_v-ѣ0gm7@ !nf;BԄ:`?;l?T}r Ak왶ܕd%&Q料hBκvrFH}kC(Э?vD {\ۿJ^O]Uiy}T+_U\I/uKw25l'ߨ^2L(啐-*oWiWpnvs|;uo-͟꒑jҝ(Êue^&QEļ<\vj0/XOסjږm2mpTN}ZDy}@O<ՆQx@=aE9á ?(*DEayi~m1JtX5RExx]T(F72T yUy6ۥ&f]btj`!3wxRN߾R)kt}Ԉҗ6>rYf|Su At@{_.MZM=Y|aRn­ ,2-DwoEc,3}'<3ڌsC )2Iѓ03  B91%@cjOg]핷ثܺpnvv w/[/GLDʢtM-= IƆb-eL(!H_>@kNjwt2S,aaiZJlyI0a TF|B @ `f.E#n0%oC/ -xAۆV;+Gʳ^k]2*´L@ R$Iฺhg6bZQTk/eX$n߂@ @*F^u>R(/LKzѨY޼ϓR߽Z6U ^B;} @ 5 R/??/#M7'ǫ( ެrmerJ]2D TA\.I)u޴-xJJˤ$$,|p"(pys l c5<Ɓy>P^P}V7hp2HT! XDR~ӆ@ ˤ4 A*p\SaKq|.SiA  AJ27⥯O7mq*;/Z$̪Z {PGq5q~ʀ6 XL_⻦_T JWǵX&Z]Ox\.O*oWPi P PEhPnBnS{?z`0 o_x)7;G"q8Щ@ @=bzoRMy8 M*7<@QՌ[ ?&8},KOrVTJ #vFqؔh 0깟?T(JYf6Uݯ3h~,= )  R(Y|3]!Oohy5LjGCap4[ĺf`uB[Q&r@j/\\~T*S8AY,T5Pi܂jE&Ey@ (Q%ɔg 8b萠֭*0m bţ0=actͿ7/6 с_RmcƍR{{/N869jLFBD'[Mcb;"}`{#gF~?9Nksr%1>{ Hp]_~CʋJoZ̓ =jf` V nA^/&k 1 zFNJ$v[+/vdY5{۾p nnٞ1eWҜYg}}DӿWnՀ@!RԶ)^\|re5\1y?m鷭yWnzn꺩.yZQDE歸BR\M[{DHvqqqOWEkTGkU}Ao(i S(g$ܵk[HȠ go߽W=T99:EYC8A\tABinN`=TRͻT4(uBT.ԍ^lB$/%^qqTTdի_6jk< 7s栠y:R>xq$dC9[v\xh35)Ӓ~zxFm4A ci_HUQ U(-P@z ԓ\M? )<~poew{BTԪ<.׺=ѓSbed6A~>UΝB|eTh0K;na!\SGN~ǯ ўW~̺!>jzYE' JLȡ".)=@B۶!C]vg5vxlӿ6 In5X원w3sssFݲU˼76P\.'-z+ܼ7rKy#$NX^kfȳ0ռ:`ƽtQo.(_#U)5eז}=|dk j 4D2&_I>B( qXr`)Y?nݺg͚=w_ssuX,ռ= @ Wӭ? IJN^tyfVւ(ᾹT*ʣ5KSG%b(ʉQI뽞Ffg iuy' G:6@uVM9W~G2nn`,s rx_O:^m|swz_'ۂoHR<ݰ_V3zԬg}7ٙ9f9a VPTlVj:@JSOr@+}=ii _7lnѝ(olj=!K޴zb$< ]^'6^l]Z=j6+Cd{}i]Ibt=kN::(:aT mubz|zaعŋ bHR$$ ,PTt-6zu GQ(D, ECP,~e"Pc_Oo٪)S0Oz 3`IP4t&(-Y٢nrmhdF@ā,m ِiC@@Tڢe} Y: Hd+߾,Zc֯>{ZX8RYJOqdR( \`Ϸxc_+'>6;Q6PHOKDن$--.@ĸnGfsXl]SX]eCN@Jnv C_hڢ%æ^<ΎU f,ORv88upvi:Äz a 7o Cd 77 <.+T0',QJyyyo@,kpBaF@ M =z[Q*vsT+zH,X,ʝishB*d︺3c;gN1LZS= 챺ɔ $spp5 @QPQ);@IA~Wߴ|/ԳgO3bݍ_B׬jNaa![LՎ -UIB(Ul@ "X"p J&㊄".]#}n]Q∰F@ R3>q-o֪ЩSg_ @ˁg!S@ djaHnv֓|G>oonO@ @j4|>}n7ѽy9_$C& @ eY;uNh`Qb/iϒ5]!ja m@ Rڱ')7;@7{,y>Y@ TWR e,(hPIENDB`muse-4.0.0/src/doc/pics/main_window_with_automation.png000066400000000000000000003017161404105311000232670ustar00rootroot00000000000000PNG  IHDR \sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATx]w|ni)tBH[h* =쾧ς,@HIhRt)[zfwrf@r 9-̽3&.?Dt:8ʨʨʨʨʨD@P.,_ n-7!XTB@@H[YF辆swE6kVJɨ/VQ6j$߇':ڟHDR&IF5h0t (x)_/#9gg0/׫`g7!VDs"nK*GgyKzT+.sI@[k%ub1<||zMktI/XzE)OA5"9oK| TWS,W @hpT'M$3"+OA'RFivɔZRep=<BdXI{IWk jR"{+ˊ(IhIPkOaTd&+9T2@X9zBX47:a W ! @ӕmW]3zfPrSyղS*y rO\䑕ӅSF)ɗ.j| Ryy:ቤy['9bsHjN?ՁTsEVAJyŹjA"|o3eE|B| ʨ$}۠s'G#~$<^ 5Bp:! D\ln_;`C9 r`ZaX"@^ r!00,S8PekWhYM]>P,9\+ |2BXVk\UPW{R.*mE)_ghȧ QR_WɅp?R#:x huAH-h R&=y5R ,P> PLkTZQ.%bY$ cu.Q>iYVu{RH k`w\.O\эн_vjժN=#ą{k14{C gA/M-g-.z0'%>A:dY\@XCd|7BwC#*p=+ v̌s8u T͆{YYhԨ AzmRXGCHHzρ+Ex,^ૼ#]Qa2䓨CFHKG U.T!sg}%OH`'Dοs8[u$2@-6OupYTD&`Pf=HA(SRWS @^D1џbj!6+zHo(Ia6: ɣpUÌ1tP'XBt.} 0FpQnݺLVY^>řS'QJzM:wK~ya5^_H#1xP$"o0"PX}`|driAe/@Ѡn].$RnITMu e/1%s8z~)Z@/Hєf{-8De?[ޫX!ST>kibSWv; pul()rM@OyApE=+!}V,]wК30ٝ8~όjۦTPPaQd za 4D]%Ꜫ * mġO##HPX6$̗!\žpt7xtl>dmh' uVo-h@"!b={PWOKߖ/K!hPl$" ؼy׭P8ש#9$w[h̞)W o1Qx+h "QaKdy/%OjNrN&oli (*e],`cB}.$=$ %*a:uʯo-Z,(a>3V  !91bh_t:={A!)Bׯ3gϢkD<^3Ds N'eC a`Tل^=8Nz$j?ҡ|J D],5Cj B<ɶu [T.Rybbbb2 sssjJDGGC>'vH-@c (*X,KOGnn@>7'+W@tt4"4r#^H7lԋZ LS^Z OP,3 Yÿ'24왳fxw`6{o } Dœ{|SٳpYJEߗ r!?/AAAYhŋ4ՊPR %8)*e >N8Iӧ ǃ",ǥhWX} B/F#4Tw>z"Qȧ;)|~[ֺ Fgs;yò7JHߙF]SдQ# 0|fnnnbNܦ ʓ+(Q1})ưvԫ+ fj$&QЯ@;P,2/]7 s=ɉh]`HNj У[W^ mZ}Om&WgqD&W<2s&jOmAʾ'OFVЧw/y?v,RSѬIc|{ B@$9dijvA>wЧ0*|IOEI'3IeZkb/彴>g]x O\0W_ Y׬Ws_ƲeXx0 ;44 K-Ǚg1bs8Wh [)7f12=zu7왳!8s \t\v ~(Wb(tf,۾ЫW/c@NE6;s}zAn& \,Lnq5W~_! V ;Q`OKt4a%i惩ƣ8*: ztn w~W@6##ŀq8hٲ%~/^Dk… v*$$r qcߘ`?|},^jƍ,ӨAΆ?NZq;~ځtݭ۴Ƣ… Y&.'^2v+Ym(.,MjzSgr.@nۺGJѺMl۶w|ɓqc?+C]Mpеk|>{6ddwƲ+@޽8p0""##T᚛Sv^dxd#a ;qŗJ%G#d+w#sU*©A6'oAP4o^¬W_śoc8(gaO?a1֭Kbh`ӫX<; hժ%֮))p)䔰cJPQpw- \Eկ^z}Os+ y&'/?6+(*.=NC(*nnׄHû5k9O:9=|RQyD| !Q'@եO:tQy8^2=٭EK QJbYA!<0_mcxvũS >Ŕ Sz%i.vlߎʕ*2 x1ލݰAn+ԩԗݯ*[Xfym{;Prn^:Crظ~=A\:w빲!"I qBu?CпOodggsq`8w,W]wa̙ B>PPW22ʬYh0cFym QȨHG}]'~MS<,F+g zx}6m J$l~JmR>ᡌgZ(6C#G.Rxee—. :i!Y s!::Gb*gcwߣJJ|#{\ɭHG ~FLt> F?B` 2F2l ^5Hsd3xDII|Ԋ!b. Qq(284j2'Cփ"Zc7 pexҰR劸~/6ܼq 0[mp\O1aDحo˖-~}{h۶-~=:xPP4GmP~pA{-" O &N "(r ,}lV,3N'-̈́~n4=Ŗ۸?~@ad^ȌS-< <]xGUSA "nqIr )mACKr>kA~<{s+. /7k}ϳzYkWpbcm+V\#]X^a3!u`6}xշP~=>u ߊf>0q#GQ)"2 > زA!? @B1?S]VV<>'^O# ouB!"NVd<[J&.e$C8/zf3@6i/b6E\pf~꫘lQ\8}4hL25ۤaCu:\2U%Dxl Fq%جVB`XvW_/>͂?qi1}'{%o~σ֣}R;we ϋIT/<ǏdB a?"91իU^gY ^{G`(,,DݺxiJ$'+0۱9 L~ /#͔$m\RI&) |rРNtu'hxIgؿw$av'@An޹s&"kwnc;@?Y Mso^9]?DQa$lmP)J56t< CX&$~r! AA ALXv4iU*W) 4o'b.+uʼCiӂO^R,hp)$(ʔVGG#:|!_q'C< St 3TX@prp@BpQlRcjҨ!28] ٬B @HH(,H@@ .ٙ"(-!p8p:C \7^ppHC@A@=af0b6 dL'F|qA=pAx=ʻpUX*IXWVNv;]!'+ 5+f"8$o˗:uj*_+#yzPѩ6Ye8m{AWPZ?(HE &>u~v}2njڨwa|pN4ghs#pi aCֿPz5mvN7m=~\*SAG ]x,yKouI~m*˂PI 8c׎h4{V)~QQQrrrp ܾ}(r8#2* *T@ʕ>u*#_A GyDV!T~XH0P`i (^rKl*&@ >nwP&qO˜N'6\.FDFqymä)P`94*y,P2)n Zy{ k=lၦYlA0Zh8yN .wQaS<=}֫Y.dj}aGPPP12<08ŐcꃢG{?V!< Y\ B]-7GGt=u!j1W.|t~YAI[]IO&ٟ 22 8 ŕ18/ 4QI:|'4۴vԮ퓫ސNCpp0w2rC4i2m0KmALI?]O޽D-L OIN"K(@_H&[A(H D;w`BnDe-:[2ZKozKa:~Tq#_eK  T  i I seWT>ÂTf25b<ޑP,-8R[P!I ds0ت/oepSY(֧taEdq5i-tqN:oq)/o t`CFloМxn T ïT(D՚oK{^q?~ILRZfֻRiT]q]h ڇ4ڤuŁ5PUJע/0W< IDAT]hu:JO5EP\+˩>?ْ/َZe(y,H̽- r;Q> 78gA^ r%GA^F1jKFFKYx-,L D_WOx2$>J9 -$Bi[48JZإ^mQ}x[,ْ<`ޮDse@$|̥BADv5>=r,&R{Qv1A\.󅷢"j~PPQ'pN*o&p6N NW'KXADK*~f\ʭ\tMŕ&@U SLh TxTF?ŗ3ewVC+"KY¨5 /rl6^&Y[ q++P"9./EN2~{+O*#AJU"$4cpu8$CuWTdTGʕW"nߺI'N8< #{;}e'!IԱԃz| KuguO!S×8Z !D+\mYpsI~)\-?хp^Jk*ϊ9[ǟ9ċ@ĥȩ_jf=H$k se~olbtk*F𻂏R2!Ӆd*@A>gz<^Ν=^/x-8z=U 6ȁwPw5Ņ? vqBBC6Mo28C{?<ɋ/T<C!IM/)T$y)BK1:OO(28n(|95 fA ,?퐓cP4ZËe)TxIT,bJ&q2z< %n^!嗭!K*D/vyG0x//rz)//UVCa~lv.Qd UVC^^D? pSOm6p:P qE< ',̀m? w8غ};BqܱF2z4=١C qLNPBͼfbUvI_M^Xn'{"efK BjbY%6xI*'BN z@@oqRlT*V!?) deZ R0J5PzLء+W I'VJ4~66ڱ`Mgy Xi Ӊ ampfEXXsrt:9 } "a2bihҴZlg̀lBTAz='/ÍCqncG!#çJe!ޠ6&y{wI=MELt4+nk lk$~ LI(%nA&]͢b,w(ze>J$'rX܄Nʯ{(lR~('zq+Mmd͌$ LrG?5[t{.}_l˷N nC9~<r`X..&B#þlX`Xل3gsE\aXN>00P`' E^ ꂜշ(-_A%-JF:AXb%~?0j*Ca5!@'һq\NB??KY.U]qCwç^zP,U( ՝:u={ѧOFQQoߎύ0كN:uNAj x'33oxyLCߔqv, ^{u4j.B8P? +>iRځʛu/ fBn1}thYL@uGF>}?#C yɸs:vJ6A}v C}_^n.^utcnj3gdq]L6x/M} oҤo]1l8vQ/ıcۓK@\ .!H퐊Ls<[6oAjj*oΥM&dffcq!%9p1/z'm݆޽{wyϼF0zBƾ}½wԩֵ+"n߼lt& ftsąK / 91 Az/WSNY<{.L.`ɸu&@\(Lضu+z==y? 2н[wBо];|WiCЭKWlݲS;֭pIKzo))ɸ˗.y]/^Bvv6RSRBOq޽:u*RSR0i$ ;+ )I0lF玝tl*DrR@{+%:~ÁJ*_~mbcbQdsOiukػgN!hۦ6&0L:u*v;8qBP6J[W֭K.!-- ;uN=mZsƏ$=7R>{*)L&L”D > /[nMj\Bp?III;f n޸B6:b֥֭ :w숭[`uڹ3tm[ru~A <~ǁqa$m6jW .¬<HNJ¸cqMfLХS'lټݺtA.m6,\ad;wǎ ƽ 7SNغe+'SXXcGS 'Ƌn]b-ѭ;w톟n˗1|0t {e sO޹ɓ&cjL?nD,&l21ٌ.;lfAɄ)¯!%)Ç ñGycx9,w]@LL |' d'r;Ccj yWBӦM1o<hР8 ##V{ҷ$$$ &&FR .}vf^GU_Y͚5üy@|l6… HO_iӦav`Vv:ߜ9sUV'a=&n u+6AAAݫn*ٲu+z}ރPVMlܴ 0 \7X,mS2аaCPli[L֐~ }33<$s9sk0A8W0":TU+WbpZt:$[}&OƎ;0|| X8K 嗳ѰQClڼ [4ǜ/ =L&<V̗g믾ۈt<(W<7i{ܹZ>cq?dۇƍ\x^en2bʕXj\$''cժ8wsEÄX~=5o~ӄ߿14## l?o~F<7_|*TO$xO>Xh.\Қ@~_غu 9 ڣEcK;vxC_~%j|k֮EX7 ;w._Q#1w5<,Yr̝RRNf{ve>?lڴÆ ܹ_)m~!->^`` V+.^˗sNX`_xP( 8!G!((.z##2""((10ٳa6[`/PcLAAyn8ty/fFjjKtQph#GbР ÀqA_TkIlɓ'ѳg/ tݯM t:8 <O?4n߾ϸ~K 4aa8` F<pYxx8'`bbb{g7k^Dci!ݛzӧOlӃLn͛oBTt,+( p@ a6YPjuyOذi3BCBQjuLظn@fǦ a0vx"&|_}&LՂCa;qqqs.*W,[|DGG v? b6}W_ɄhWg1{7_XA pq4mǏGc& ={䮃@@ lCX`ƌق{E(om !ixwѱSG,?f}^XXHQQ+FI9yӦ͸u7nDZZ>溰Qێvhu׮PnMle۸iC >W5::Lj@׳}e=r=/_fcHE]h#UTW9_΁lݻ1fX:waqQ+RaHlcƽA;a(??h#֣1()9 | 3:yaqZ8x L࣏>c">>SNAM\4>R;bE>}:oS|/^q#../NyMy6ܹ!бctȼ{ li;׿O q䭏}``6Lbǧ {=,Y&LD||<&MH}$F# 22fo6>hd0Pz \z3WT իU`ʯZ!!!hڬ9W`۷nTyb}5&&-SBɹ/6A{eWttQ\9FfB+VČ1}4ر-KW xJ/V 4o˗3ۧhܸ/CfuFdgg 2Fc 1VZŨWTڵk@ڵ9'aÆN :DbDd0ݻ*b>͋9ڏ䍏 H0 E e$HJrrqYypxT/ԺukX6 [l/jT^=l޼ k8 IDAT7o~qG~:G0X|Ґڴi˗;w¨Q<2hk֬jŊ+ѢEsM&O~;wDJ k&R)mKSNr*tM ֵkW,_pi~zش~[lիWj`hѢ%3OU۷OĚ5,W@aɒO%KormժV^ՊWe˖x_nuņ Ph2aJ=>b{vrViKӗb` 2u DlWZrIIXrRSSe4nۡa ڵk18m0wޠAq,f 6nȴҾr͠F5WV8|wa=0LXx1;&0;bƆ ,-{6brX,f,]ZR,C0 xի!2"> -=0e(W]"%KЪU+MĿQWX шW:^ ;SPW[cXH_rE[s(w٥K,K_=atK,A.Ex<(oР֯_Ō 7@qUVXr+/c #55VBbb"OWb)1<Ǎǯ Éʕ*sOtU> 4h ֭[A$lFz2I '6:)gf͘>K/FNv6NѤIcNM6X|l66oތO(r8@׮QT+:խ[6mlkXc3y$>Nƍa6~:4lذT,m `@ QԣB0 Nq7S?dGϣ_XrF<7ӘGm]9cFc1dPԮSpOk6FO?ŰaQv- ]1ck~Xf̘![}= '8s5z~-zx)Nݰ=Ziիif-[Zjh٪6^"=;۷Pn}=f4Ν;1ckNOkڵoӉveY&׿bJϿ<4sGLgϝŘcbU2v;7lĐ!h.K1؞49E~cϞ=׿?\"9;vΝ=e1rHIp:x};:=N'9e. .đLIIEƍ*yOm۶o߾S.$b6JMk֮aSuk׮ ƏÇ}v:0 p\?q"n݊>nz.iǏggN ,XfgϽڵ+֬] Bq?BP--ǟ|i/MCg{Eq|[Ϝ z1uT|4i}ÈAXӧMG$u5nx?~x?AMA^ ^'ڢFhޢeV^:Z;" zh|'HZkm#GbѢEڭ>Cxc#$'PG<6Μ5y#GB.]1r(L&ьӧO_X=?@n]Ѽys,K_ ri)'K 6I'aaS`K jQt# *`K+q/#^bO܏$AB0%~ 8y/t͚5eQA}.2BW4*QMG~ $@Ѓ=/b*1ŧ3=_ ++ u+[ {j  M^lvbvh=TZ"Vooy4-eV4:hbvq"}{ov-L(f`=o74e!z/;^u s,ԮS3ge>VRuw &mU*Tԯ \ӡ;]ރKWۯ!/g` zJWJ}}7@BjBуN'޽UIRFeTTRV&H:꩘|$D(pu] @Bh%V3;Z;*oceIm'l)%Ư.%򠪫[4 y{P0le, Dn2[m~p [hySi]— R$Rϔ;|_m(D~%Jc+J( ,@"0me* ʨ|%&p(ke&%P§H;ТS&ɛ:P"m(%"Zx` /WOڊm$L@Yq!6VN'n\Uj!2zt^Nׯ]C޺u ǎCaA|4o*V[VߊPI R8>BnٺQ>[J ׮^Ajթƍ8zwDFFE˖XRj+U:zį7"y&9%hѼ*PuM;_]͛7G4]pu5k֜#!nݺcǎuuy3Kϝ: <u\ϔ.^Μ)A]ggϖg u+I]_#5/ nc~Ѹ6Ǿ0,RTT"#5[r5 10"ŘDGL6 YYٰ% !@tT"p8p]8NDGG#88DDyf#//AAzC49^TV!`>8R߿8_yo`-O/XkBVGJYJ\8N\zUUcJAKãuOoQf+SJu y~ F"#J38'ׯ ,-Zt -N91:0_8} P콷nm鼳CvN"ѨQcϞtBr`pU?wQQRώw圯N=E횵>x2Z'سk7\pzMc0>bxZ{eӯu\}yeiˁ}{ԛ:[~I۟~^FW0KjL1uP?X/ős\l۾2njY?E fT'۷`_0u}Dr*Za3ut|nsA-[0teddea.-Uŝ EqX`VJhcAh h$*KZ .RR.D iE1h IZMd{б#4iF4aFl*Ӂ ݿ/> h ;ȨCWZ_A1/yi4Z-z I: 15h ֯__kaIw)i嗣5Tn =JѾ7!tpQ)*.f о.{H-(jp8bh{EQرs7mQ:.vFe~߲ݻѩcw{  q` 3 ׎pN_iesb;܄ fԀ шn¡* K.UCc!E8x`R> p0[Ԕdw֕QD&dENظwdq2@h!'o)@~~:i8((yบW&6lD1!@Qhk.vꇨWdLMg{w0QvEa͚qlt^_%ִth4GHJ?g^F##FiӦL&UWn'//~UאCVC}ιG8g0v\[Ԭ߭Mfså ~)k А@+Mq .Ter "dm5 ЊZEWw(//W A@PJ(APn'!!]ΧмԼ--+OzGp^F7LA HKp\HTgс8Չ_t#⁈FAvNف= B]b"9R[:>vٶo߁j6m$ ՊjUbbb\m洡%ضm[/жm[-z/LM~FGhzO6m6lhMAgeez*W,^THDEKCn^> rNی)p55OCvٳ[oDfxGxi58 Y~yW޽iiiucj8#k?oZ7އB9fgۺ22e8}Z>ʱw<, (km29+ vu߾kL&?-ݮg3k/TnNF#Ā٬f -ZTݱm_QRET"`pjq0Ms__ EMT^/@u`k5iq$ Ҁը$,K*ō7ңG/!\AŠ4MMc!7aCe$**K]\9}Ds_ر6m0tPRRR0 2M6M6|رT.8]Nۻ m K"ʇp[XԃZa}tdpP7W-Ǵ N=/ M-?­Ԥ+Xmť2H=a%'eŸ$_!VhD$4Z-  Mo7eV$g(!Ƞ+wb$2 V h" $#I, [a.-U 9DE=)ۿ 2ɡhDJLb:@5dbŊHj9&\Ct,nBl6cXqh4JfDA$.. v Q'!컘iסrѰFQdx $q<'#F0` ֭}z=9 ':*={v3h`AgnuӭT#Bq{nqDջ)0hja4Bx?Ȩ/gyw񦛰l̘1I'ҴzHϒÏ/r~3ܮ Svh<2&qh)3dĥddfa.) Ą U ,y 1D'пz5[d7ljĉ"Ah/E]^SROfѪ`$Ai?%*܂sw5?W'T.4ig;Aw7Wp;j; ƪB 1FV;hjJE pM7d:>Ǝ띷4 [lK.Ȳp҆:ъ K64HAAQ$'cXp: :O{6:}^2iR3FÇdBy=Ċ+k1ck֭Z nsmJ PBPn} QQQ[}M杸~~ǵ9w{HU8v#)z5r_dsǚ*L' YXIXZn*ϭBx0;a*M_zb#tX4F#7܊ƤImiLҞ=t]0\&Mj3n$* x`4唖zŲ{vUN%ڴh8رs9f͚b x (umV|"[oUQTT4u-4x&mt[fv{'\wbL@+mB!݄ݮ*˹ٗ{`ؼbB2$UdHrqp:%l֭WYYQ9]8%UAئ]*1 P{qdeEe :Cm;ط21ŷiP\8$4Iaְb6IOKC!eldlf4kIRyUvVFqVPq2-߿nCx˗ <<;f k8VLʹ_|oױ^uEϞ=rޢgϋ 2+ IBtؘhb(,-{\ `.eŊo={6w}7ݻwGRRRxgiݲW\~yo̶DCfqogUޢ)biqbKC}r}YqN<dY@n)(/Ɨ_~ cbڵ,X0n~k5 :ǫ\qmBD *j$&W*Kwymk6T7) i 1/jìYt|.rV^V̞=>Xʍ7\_\ U3;" =YHӓ<99 4I*J&O=D3=~L),k-V*;䓍@g8vHTTFtII=!8ReT28r{ĔcxGV@{5AB%pA>s^lq70,{ tdffVh`05 ..&î2~Z: Ƥ+P$Eb NڝӖI{KpӪez{6 Y*t"2:tuFv9s&|LEY9v-[46޹k''K\ҿe5ԥ-Tp< p ? ϫ>ʗriZy#\Yʲ ZSr=74HQ駟> DG#RDT.7./ŢU?"( .GBqp%ؖ( Ij~aXIh͊dRu܉#Orrz 뺓-B m YO)/J)SXq}5! ?ѯ_2ϟnK?x9:%S^f.}]U9>Yvy, ll&&&|Wߊr2&x]+CLZ~m^~UyThh4܎;$T+F [NAEff<'F?Ep:cP!-9ls6ߏF2AGaР,\Xsnؠ:zEp5@/)UpP=A ^#bJG/pY Z|9 PY*hd([mFCG "˖g֬Y{̜ߗh;w.SN` MbU#tC_eeڟTx}׃@ԩSPp6:U8ά|gs9̜91ڃХKΝjez|)-k Lt$$~}pj,bh2Rnn.~",'',$IbTh:yYQ7 遇ܜ((2(NYN͛AС8N%IMQE ݷgm} SdO"l/5+X& "bQd':¼q7QUo^OfVQg0|߰uXR]hO3×_,gԕW67¤Ga6R`*}&Nw9}Ͻج6G~^T}qv={h|Ûo̤bsh2&cƜR4*Z.hߌÇawjYp#/3|r0ۼ"Ic^͂[oexMnmR/N rrrjQ-4f'RD_ⰃC@ʊ8晐 "-,WT4!**rs(+؊$I8 r|sIR$ +!U }m6p8T%\TKIڞΚ=U UQ:J*AL,h UgH){̨D` ǯv#:qv#?SN%1Rĉ^}}߾#X^qw%* M+@dl5N?)kWTOd;X :ϖe˸^=2e ˖-crɍbOURRVp J4Tc*@[>68g$jUiK̤8tJ,˔( ֭Z}Rp0WXSUӓ[ʽy(Xl $K8$ Ӂ,+TTSRRr%*,\N'.IBӡiO`o<\@ԀP hQlv AdR X]=̇_ת8\_!!{W!י(lтni{}1a$8Ӧyß6?EMYp>/{yȐp9_;tt.c2L, |={K.aڹq w1*vgf0~ܵDU2}8еq""b R ]@Pp4|c(Ҽ `O@/fkFY F,Ò%c*D haDP\0udNUT_'i[<@5!CWE'T? H@u]'%RCkesJU= ^w:T݆P-/icc#@iiWaMʥׅZ֪ϕE`qA P DB])SXNǢOHd̙L<_?$CR`ӧ7{g9r;4zRAAfb׹3}>89%)- 5@j!D !wΘZqTT_ ~<V+08Rq ,SRnQM*%CRGp$|( A8!m2HKmFA~ZɅ]tT8c^ʰTXTouط{'m۫۶;ky"t^8^u2xɩk^wz+CqQ =F/p4/Fqn8vCJk(z>l%m)?q&[/)wE\x̞3A=b0uN߆xb~yy9eee>|iӦry0kvIii)e IB8`N 4 BάÈYT VB1 Wv/.nNzQ,(.6 }2MQTj%DQFG\v\.ɿrto\VXR\IJp1jM))I%(v[F^Ũ{vazkbO.)(PTT3R(pHG%.˗sKxrs=ޢA@6 FYf++"J6XjU_^u] ( ,_軌*xR$KrjmX˯|w_"..4DQѣ`4}(]@8}.`4hGnuz۽{߶}Gމ0x)9n_{5*m\ >\.E HNJ`1@l8EGҠp2}QW~h'tMxرcQeu;q{󜄜8qbdYn/ ju=}wU$!66 bkҩӹXmVvIzXbcc!dh4Չh8sg|A`?mۚNڶm]wa#PGnj$&IG MSRuH6;S2bZ߹uV~- xI6]+F!1FFWKQ$Da@/+85 k\:'J ól'INV R iz:ш:Z ;MIJ$I&9!(*q cצp9*P'4ŶѢPE @+T?ub%IreYx;N\.p!Rb (~ NKRTNC^ @q) "(paf (43V+ ^;PSaGkQEDL@Miٲz*UKɦ[ #$0"yE:y9γf;]0>r2Z:~I[h$m;I IDATn!P[c;dYЏUZBffFXhːmk@~CrRÝFj9X$*"66F!"hh 03/e;]e^YuU[giP G+EULl6[}JfѺuM7NN8w+j^g^^\+)G_TUʠ4(ج6 UEV#%%шc4qɫ1pVɃ $'ZέNWl8ʤXDp`07Tu0; ŭ|oYq5ArD dWq ݆bE8 W$ƐWmPIqܒ$IdMFADADAx|Dmq֭[[o1Ltޝ~f8]t7ߤw>5ߺu+N[ԲտKbb">'N&Mq<^rڧϟ)8V.[nI$r IR JӧOy7b~c233yj,]+{d zo=ao={KU=ͭW^tCE]`a+]Э˅|'N`AT\S1o= >{ްrxIFO/**'c6^ȑ݇#`cǎ#rWqwk.oX6ilXx &q7ѯ_?t:)))uם<8;p0v5,Y>Fয়~b[+x"..0NGRRwy'jx.*3III8FA?~{>1Sω'>|-SO=ŨQλسgOVZ5\ױf͏at=.;W_=5k0~u\s͵^+uCz@k|,<ob֜9<3O?=ԩYh˖-cɒ8agO=$g>3g7|&A AX\W_k׮|2r>M[{5v:BZZsAFF:x }z~uZ-VE裏@Q9c&c|٧\wx̙}oС=;ojb4@Q0 X**῎aP5,5h@k֬êlOVbp-\=…o5InO>fܵ0cLP4f= /OU.gĉ̙=}zK{߽q r ?"jZȒĪիj]fCQT\Ѓp8z~_1s͚5̌ -ڳ޵}`[cVILL,G`Uf^y^k1)),d21㿯|~^N#&:^={?T /ݻqqAuo)J>w^.wq`ث=g6BQddYb֬Y;oڧ߇-4yb̘||l6>sƌ ׁ]އ {Ƅk[&== EQHJJ"#3oڍbKה;z(w7n]GSe$k(M~~xO9"ؼ#Ica}zk|,eVlڸF֭q8-[ء7,X4jjVشa= 66$ IKK%{i,]{Jrrwq'PQP͕LDQӐܛhaN NCԣ 2`@=ʪUyyʉ...h?&;;V~b  uge<3̝;7Ό38tvi7zod>LBBf&t!** Ftt4VzSɮGZZW]u%|IHOo޽{0ztUs$&&R^^Ryty& 4)&L.>'ׯ 0p(u,fc̘1w0q"[m"7o)4vF#6 So}q!D:9 x7' \3'xΝO?c竄 =z/3JLW ]t!!>:&]zcdB\\^ L 9FNhy'm}`aCcaڎ/"_|5fFzé}Q믿 *-6 N'z[_zҥ:}Q-Žs뭓x /67E1yOСC{%++St0J5k֌Gr8z(͚5wXCXҥK)..z}TyвeKZh?&>>^/D%Vʗ_~ENjM/,Kxb:u}޺ukZlaw%!!#5oZQ}wλj7\tޝ?η~mhjՊoKFF)ɷ:phw\svHR)EhEzҫ"WA@PKqwIGlMI.Ny<;̦N׺t?V^ݺbƴrHHH̙3QJ ]kκb֭xܹs;v,*xfO۷oS)Ro`6h{'0x;kׅ\}Wp/XHmSS1ut{6kĘ2yW~,f9`sw>J.-ǚ5k1h@ǨQWn>,T*Ξ=+ !>|g`ah9sb:QծJ'G+VDjݻݺ1|W߾}r*v_T\ v|;wҥ0jH|T?~cǎ!##tÇqa罙쑒2W\Ő!Cꫝ0x;X)S0bH<ر 1VP{Ʊc0`@ I7iM47CϞ=q]̙33|p5 %N֭+zc#' *W'YѶ+G~}qrx'݇zKR[K6/С/I[J֌0?=rnOSǎxn3.?ɓb,^_(7'-- K.۷Q3;v, . Ne޼pjժI&ZbԨQXp!?Vz %%扽;cƤC+lVlݺKՉT.`, st:ܾRKn3hа BCvZ#a/o*phR[~A?}6̛;>w6p藲 t:O̝;&MD:u,{p^.x꺐܆A8 g8]lܖT*_O 4<!~t#44 !8yJr䘽 ӟx_?ym@r7_͑~:uƂ h$r(˅Ү_]BiwDpg* Qeܖ_\NKgQn=~؟~?u jU*WF!o [nT鲨X/p:OBRz\Oܾse˔ׁ\( BwNO^RR@\b_8W周zN;oT2P$^ ܼuw ؘ,Ue˔C\\On|{ŋxRG7`{zfg;xp>#-W:PB ~7;"ڇ.E,p-o|nT*bK Ga{ûBdDcbSDDT*>DfFOd$Z5 @ ^. _@i"*]ҷD*on(8z/PT@Dxx@)0sXX8詖ׁR\[H ,fj E{~H7)" tAJ7gݾ} ݖ AAA%`w*m   Ff5GVP(<õAAAȄq,^   §FAAAlh,.2 M0mhi|Ņe|.oZg[Zd͘BpxX7Κa-X4nFXpE޼&Kɲ`4a51ek 2`xuL\F0f2lֱXOMsdc 5fj/cɬbLzb>SikNX12 ΟeLPS*~<c|caXC^eo!`\5S_uyfredp,crr1sV[)*p]t_Qq80ig>#ksߠXL1웰[0^Dt“]|}  d!$0p=& !$$n{`|0f<œ>3esI_{хu">$o!  `5_` Ĩ#B18N_=hP9> ~3cg;~2 <4뼈}Aa'ɯP(< zEz-haȊa^:$BdHzl wU"NÂQ1q  8dJĨD $ı+oOm̧?s0|ͩMuAiS _|)c͗z k&f/y/^ Ƌu_DjP1֮Qpzi@:EݐVqXGzܽ}%KZ. rR]U=)7XFHWAaM(9qPAM[o΃;(WU+U{|W\Cdx$&0`Ɏ%˲Z*J,?xVTu*Cߵ+{p]/J+ʭS ut0Ю} B͚`(Y°#۷_꥗)ٳP?)܅ 0n>h^>mpYddd <<իWG߷re߰}uDFFsϡdJ#Eڵt~EšvZп?-t~S1wdrT]_H믏b>lڵ{šLrYt ~LAN#d9PS?7ݐO{ۧOz|5vv.ډwo`ⲉөBBq@гzyW(Gp><|,$,qva=C8`>ҥKx"@ҥ꥗Q>"&F_ ^=ݟ8`^^fAFXĉ_W_cʢ2q<.Z#FQHO޽{1jHIuӉ\%p.RȲ,222gdR,X0n޺3iݦq}d(Zw<_y@b.^:+|޽;Fڍ4|xC<~=Ĺ6tNbDGGk.ػr7G.RjQD Ohy>ɓGAAü_E  ,,Uhժh:ӽx^|Ŋ; Sΐ}FNkn|Μ96Ν;(^8<IIXlΜ9e`ȑVMZW@УGwQ䪋 ..=ztG>o>Kp%TTƍEmQbEܿw>}z,zJ 7`ㆍPk48p[庒ŀ1dP_^: ᾏIkW{mRPP7{aC`t4h 6o e1x@ t7c±8I[4;fR0wsFux^{ f&'WOV㛍]gϧa6RоC{̝;M6J˲`LJ76_|ü7o'N@ףRŊ(W션 W^ŰCE%[DHHrssgؽ /ubغu `-P&NOcXjEO<9sxcGؿ3ԬYۺm1ct|^l>&N`PYfi>Rp5lݺN-[ w+%sNPTҥ >la8c6z&O>ŕWe&7^[oݺBe#V~Nn.^&XT]kHHHz 1#i:z$,h8hs OYڧPğ(Yz&_؄v.8/o귱Hg7MiA٬Y+(0Q$0 *U ?~^KAeQdIЦMkC-ko?bMk޽{#&:ZT ŊQNajWjtu[v-EXboaՈG&MдI/fΜ]M~||̙X*DDDc1$9pRNk_Cdd$4i ]iq:ded.dee!::&#Dn&cx ,뿮jѽkDA( @XtQ tiW%b{~$&&E6qt˖-Nj/~Ä 0 7o?s} _P8>>Dɒ%E){O\\sزe+n޼O,=yDtP8~x>}bŊI.Jf233AFfys}ȅzJ1vP`EvѣѹKW.]:;]6i4CƢbWI;a׮]4iX '~bdggcܹ믑ƍwER|-bz*fΜSNeY$%%a̙gEaa ]s}Mxp'bUڜ|hsϯ_b7ar>)>uo6qVyXrd:|7n޴sRMzt/z~ N?*UFٳ`ѢO=]{ԭ[~=ztQnq)۷xbngqR'u&3grY)SQQQ8s,֭… ĉW Zǵ֫3m"XkKwU?4 =m^2Gwy:tc0i$ocǎȑ#Ѷm[5j^z%z)9RZ!!!h׮O:yKyɥ'hK\+Puıg$&&_~q9@֑,;(QGc=.aE1w\@800 ^yYcj Qn͚5áCЬY35jn݆aÆ#++ QQQhܸ$*VLɓ۱r*DEE~:trbHM(9 `طo/aѢ-.]#G oڠAcy;w=}-[o@bb"Ǝk7̙bƌΝT3XqDΤ=|0Zl!e8r0֭3 $ , alL!ҝ}Տ=͵kH6)vСCt 4@NNm/VPPP8lڴ {ѣG #7@vU&wTqt:3ؾ}{d dzlٲÇ?233Rо}{?hҤ ͛'ٕZi:2 p'_طGeGa:t? 9ǒAH'O|Aujz'e1n8ԬYJ+!!ŊÎ;$eσ 6cuÆ C~~>6l؀_~\ՓuĹ_lk֬ADDy:rRRRpI~@g UV !!!8|0=ٳg}XfSpZGySL\a7fG Z׬8O,7}i 87`J*yyy7n7n^z˗c0RYrUg9pƎ N3f8e4X##HKKÞ={0i$96mr)@ѦMжm[DEEWŋ7xgvQI:DIJ8x  |;3<}GݾA?xa'nݺٳ.sNoߞ L]p ;jׯG~\OOJZF.]?KyN:[,MHH 'u$+۬+MG4=9\c}/fep'A~"Qz lIAHǕg={6:tnݺ9ݻcĈh߾=ߏUmr/40uThZ\ҭ@>=)MG;wk.ޗgu)@0w;wqSzÍkʡ2{՞1\D/¨ !;7Os, CTD$ K{o/h""_/XF0?wTzŹNԩS:u*\+bSGnYBw͟ozRO̚5 NBhh(ׯ3gBkH,/u?3f6m`ԩݻ#fe EM7#/ eʖukO_%q:j>["A=B3<4ʕ+III8|lS.Ii:Q>cYTəW F.!0G:I':!*99([| ŊǍסV ם >ƃ?Ap范FRr*ERn  BfD8\m߁J+RFq%- * rk[їbA8VvҬ,qi,X㿆 0|c y)YDƜY`LyNfY ,fMBYlx,B& ˚1$g&Y3Le;ecō3+W˚6/}40 a Q30[d :M\c7x|5ưgqX 518A9|z61łO lҏVfa̲?c X5gu>~aԯ +(Op`ؾLN5O99}5 OR3Ff}7+m,pB7jAor}Fz ~6qmo䶲kdXYPIʁ6 Lv?˙VlC gKg󲌙>03Y>IˇizZ?`[g'viFxىiWp˯bth ʗ/e+s hӪ߸@_@:dJ$ի@]p%7nFr>سwzz/kΏѽ[W)0[絲 \fXfEA`|va0Ln:܌ñѽk(AAAʗ-ڵŖmѵӫT'AբZժ|Ç_Obİ!r!UTZiW|rx-of؉(K?\p;wrW9k.b,m#61n-$,[a&@A8Dž˓pHOvj4P`YժU0(((@bb50 V˧h!Iv6mڸ>jTOĩӧѱ};] cź|-]Fy&lź^aT`YA H'tZaa;V:fpxt;ݺ} &:/(@Dx8bccQhQ8~ U՘Åk=iՑr InCŸJ|۶i>ٍ\:!?#i(J8ѢU+ܸz-ZqClW#-£G&@VGzz  D%#!Iի۾ޅNvpp>\Ԯma`nEqܜ\z>0 "~Uʼnل-\agD} H'qU^ѢK {wjڂd?B=G͈؎5AA40&I 8,*9.\1:YHG =9"P\2# -޿"&qDLllsLOGNn`΢A޻I/X.0Fj.R97oZ + l &"A,!I$*ɇ14I2 yK7{)cؔeh0of)eW`Nxґ4HOW%^eYTRa,Z횄A2޻IsƂk;+\v2vMؼd* } :! lgrsrq)T@65d'vG *Av|rX=C˯'1b@rv*U+WQl9DEFʭ6QR! -0u`䨛SœdE^bMաPovÑkR^;r;W`hzt:hZtx pC^BhUl^J ^ב8pCzz}ÐPZ˲V*AAAahZ>]ń '|iFtgQ=NFhMAO@ֻً>ݻvFR,31u3;`1|X~A"E)"ANc䨟Rp&Y P[?{#fXZ'oK_opTuܖRMut:-o0SBMI3`>!Fb>uDmZD-46@ x׮V͚(T(  *\3& ~L%c!`H9ݓ=!&Xl@vU rȻk 7`1 (29C(e+p9B3qU6e^E<1cP]+]>WT~H5ޝVޝ! o>&8#H zRDϏ3 ={tGhh[rAJkXGw>&;7`?Dy rz 2/!5}˖Cڌ d:Y9ӵ @0n9F?cj̜2k99<û]ϳ.83歶~+;iuULn_&+f'q=oyU P"vsM3 w slƲ=.|;}jd>e hG5g޾ԃ9Cj9jm^9Dp$WbV}t}"72rY#$=fy(wT\=#ߎl/eq8 qW"6.L^t_AA#EZYGr"Oۑ|λHUIri'瀸ޜaڜUs4m*Ԭ|\o`U6`&99uGF\ A@n8/=ua4gHi#y^Sg;7tWGGxzpR]y\sy ̔p*()-792`/Aj1uJZ)ui(srH?ae=fRnԋ)׆u}?Dm$*EFF"&66`9zd, ALΙ6 VxD1lι̝>ZZ>웞);\\vUIC|c4ZF"EsE! @ {nl9er̛1'e E m;whLܥ2wUۆ9@,Y Y FJPZ[_ $8\L\ye$Uj7cmL\{+Pz֘ZA\p2ѺΛ1Fr^]н3eɩ'O꫾-]!Ř6q]ǎ0>v~nePA?@r>+I@,G0,(_@( =2LJ> 9Ro\#`Ҍn`Xq׹lyG^=)w]-vn޼"mӊd {\57b݆Mq&U?a͸cǿŊs`KW7~;8wo܌||'.D} ^qW_TI9 @ M7\ ^qAʇ| %RODJJ_>$t`Yz;f?] WGܷ PlYԮ,lێ^Ezؼ}z# +WiWZ*Hr˖CTd $ٽ?@˧,`4jhfXZA 9D9XQa%50q,5aY߼e1]8!==>aH@VeYTV à0 Z-bBl>۴q#|3ԨSOcvZ`n40">Wf<\P 3aPyyDh_h8yUR } c7-Ƣ=*99\) Rڇ# o]`ĸ8bx 4fL3"?soݾw"&:/(ZFll,-'C|\bbb8Zg+f4fE>" !?6yOl 7TD K/)׶;ߗ$2o4qw;j< _=vǤ#pϿ}- b}ڥ+2p %SL?]u7wt:Ẍ́iEK9 g: )u[6_U DԺy]ȁr+U~t:t:m}-/N8/Ο>ݺtNCV-l*>nƍqиQ#>P]Y% ZX*`TlSښ#P(20j`X4m?`hXw;ݎ ` ܸrM(wo=?q=>G6,ZZ>K]:`˒5>vR 8 aau Ǡ#v2ze a/w?n<R>.r抬r'wl>k9]] p`5 zv}Fv@;!FRЪEst:L4o!!!s$X"KĉoBbuiԼ~9MA*q+_FQhx[int:>\YP&apR۵M[O6lM(W)$bS`s Y2wE )ao(: zȍRoO?~Ȩ(VG5W i]׻|L"z~ܘQ٣;BCC*Ǖ^}9`0aidT䘝SfD`0a6 ,TY2$3 qm>.ZٙO<":=n_7װE2i]D՚m…Rt/|5iƵ:WzMĉïi0w XENV_A_tx+#Ѿ{gI.o7Ҟ2zBgj9uĒ=rF.}!X\ P1*jh4(Z=,fG񒥑@@QCŨl)n=H. :k`ۥa&+pXz :4ӀPY2B0 <`e Dу(QExtzqO䔡PőL YW8$5iu {v3~t|nL}'EQγ(d bhW 7SB"uOLe'=t$Lo.DtC/~7E^mqt"-MgT tZ- 2[x,3u8nj]*Uwp]T^&NR%0p `ǣDZfR{`~O 6>YY*TH0Nl|l_r+8u/R:9X62L'kc&͓43iúc8H]ppsx~& gԝPR!$DAӂQ0ZehHƗ1s:hZyi+R4;X4B@th8YG2x&]:<Ņ iu rs4'_vwν_.S?Ӝ\|>-o -o݁@ ϳsVK-Qq_4L7buI1q3c ZelXT|2}8t(^y^*OsC_ R@Ib>=C_cnJ*(r YyJ@=ܭ30 Et(D#.&q11< *P! UÇWXҞ/| c$AÆCFhaqXxv7lS!ki l< @/WM(Kf㓍c&BZ50el*Wc@ol^jҳ˲` 3}h#~2߁eYakGQL)TCOT^ %˥wc,aג{a X;o),t Cي0{_)VfN}aJ ()h"x 'RY` FOTOJϱ\JOz_ O[_ȕsJèDh9"'Wqa]_kU)pXDHhp0%3y )5 '`amQVzGUzU./mڶ7kӂ\rE,hݴd$x""'MZrG4{ ^hjS a2jl=bX+؍1q?]yw+MS0+MQ<ټM ٠6X*RQ^ AJfT0Gr|VrP5"*<)6k.Rapdmt 0L~WaF$F͛vM4޵KWp]4̛J3W;; թ5aӲcfs0pK\"## U*zd, d  0}zB& /=QT:e,6,\",<&N…Ov!:OKtl 1,]IE9Y̻Y#W}ۺ6nKWG~L^|q`{:qt`/:}PPPeˢvgevt*'IjV*'&cד1l\rHrժVAڕ(_"#] Н $~>eWg¤Ob@<)p&q>Q2gͲww':fm;rXfhǻorCzz}ÐPZ˲V*AAAahZ>]ń '|iFtgQ=NFhMA'2O/a :%eTzQޠnJ4/Nˑ& >>C 0=u2?[ VEq  K!!/~ GxV>CbyH64X_J:k|מ:t~::m۴Gy p˯, }/࣏?A.ЦUK,[ۤqc14nԈAA(ۇVv9\NQ*X|-Ӽ= K,d'$TH.|y{۴n6[i5lF @AkU& р &R[넽6rZ9-à#) 9+/O3xTIXr65]כzxo)QG9 i U5>;x 9w/̫ 4@tX:~&RP|G$2#SuJu[rLP[Jaê9NC^1q\qcFg uW BE GlX5ץtF"ߠ#W=u8җua\km%ut&`R[2uVs:'qe kb)>᭶W%pBi k*@A(ޤ u4pdlZ=_B⽶ܴzlVIkS҃כTz#kD'y` ƂA,Vxr JVxA@/tlM94B>o3`dl^xشz> `)ro7w˅XxK6QAVjJubHOOG*V; de W%ˁLrS?t]0;n!;A~Lv[#|:!\O ض~{gBPkЗ}^p\F4 ;vA@w:8j:tZop:G>3/;*Ow{X1UI.Ao {G)m#eIG}ú۶~eno{sȍoI-ʚ&DJpA H6T|J K ׍m _a8ZY)=#.#;;`" Ejʈ@EDd*VphKFd%;X0<:ؾqyx>&ԞƕџOW%9k7P2 X8YYOXMƃVEHhU9Yx)k5kӿA A쁅g\Bɲ@)AIN{reDzn+O vܺsUUA%ش 8Lv"W%`FSIŨPpQDFFyL(+\7]tH@DD?X<AzFb=VA ,(*u=`E2umz;'6\œ7(z#۱!<"S3:u ~D <"gY>^^L|EET4lP*vaȌ$PA ϺN4>ܲ¥l;WeBʓ{Α* )WR~q8Y=Å.ڄsԚ7V?|cSCsڐA1Eh(UGJWhmz%G[`r˯W7WW%'dN]^?W;w8\wNhNΐF[Eǎdɒزe3dT*L< IIIyA  ut%߻*6R:<%i71ܕYhW%&ց}{7 ?o:un.LT_l(Nɓ';6zk̙֬30 Vz֭A<-txH?}HܶJr\қFڏ: zW%#Eܭsbbq&^Uq] 6&Ow h(MG=*Pt|EuU{XaO%sOwk {+s,%KQjXIx7+(*ww>~='RC_pHEfXױwy9Q~ 8;tAĂ;?;EDHŎ] `ł]T#&d+d33g=isλPܹTXs"]k1s,aܸ暫#! GV N\0qRй^L^u%CNj`f+8;R䮯YJ_l 0Uc5fQئյz ~^[B6n2^/jkЮm!8 #7/_9ZfQhm8\n_}r`   "9O ^rlz߷|^ǖ,ϾC`p =ZXֽи<ʧX:~q}Sqp\X()i< p]٬ԩ݃XzUGqQ:u;AVZtziByOhЛN#RxGAA>,փQԮvG<%ج6b69E}bjP*Kjڠ5dK4SIR6j ͫ~j AMpLC5x,VXս<s*BbV$ӥXRYJuP$[J͖Qм꧓t*YIU hLbcaD5TOc t)TRԴAk2ɖhhetl {^Z V Z jKjZmZ&\xˡތf-^F F &YYJuȤJJ& Tnޛ|tZkXdj>ZT8!Bk~} ]qS\X-ny7Έx<*TdԐTRԴAk2ɖhhetlԀZc%1S[c5 2ZN5>uPxQ@=D 1lr" 0yǁ,ꐢH\ 5euL%4! ʫ L$y5ZCTaNTQ_nRR=Kreb';I,C'*P*Kjڠ5dK4SIR6jW^fZ#huB >úkGR1Uq bIqЊ$zR: ꠦ ZIDC+;-eayErbg8um#ǁ ;ER&ˬ 5d(T5mКL%Z٩$l)50rDT gÇK{__ ɰf{:1@&Frг5=:hP%N|=ID&Gg6WD(xzUH)RP9q $ *a2UEՉ'2ɖhFWOJǙ$ă(=)_l~2u˱BJTQ ץjP*Kjڠ5dK4SIR6jW#OO76h.1#tqXTFN*ADK *{5j1"=5ҘY,ɠ9:]U  ZP{r%ԹO=$H1߹x/%B99X%E,u t8;brصM%tPVVXQVVÇdtO1iS5щ=C,(a9iыHUt8AD$8N^?vd|Q'"+Ij8 J: rܙuI˅-8۷?# /(@A~ _s> |GCSQW_:aDhAA$Mh6|=pxOW.Jg>dI M/N\]+I?4\BRِ1Dtݲ Yk0n̘8AjH+kC6Np'7, r |5,}-koiDs""]OՐ' -s"0 0TVVl6Hee줛=l6l6b};N\8T XDrBa aEAmoZjRT"텈te~q@<7炰kWxUM/q8qb~o};.c" @h%1XyVxӐDC$Od6^g#q79["rNx9jwvU2MhݵMEYA[D3&Ɗ5F2RH=ظ)3ʾr~ IhjKXdPk8D*A e0|pa"zB!Roʓ/|-=N, buN0r\C4J]q@=AD GP+'O{pn ^m)R:Іd'T0؄6CaN[KZzB:8\.TTT"5q].ԐTRԴAk2ɖhhetl@WAHǑ‹ =ʈϿ&>Gr!]m)4~xÑtУ1 =g'yK[fnlFAApף>|:\.yJ*T%/!C,:id-N%fK٨4 -&tq/ʆCR_8ȇƗyq CaAmUթHԁT́#:^ojAN^:j͍}L5U(K XM;YYJu{+A"d-N%fK٨4(9h-蟖Qf7cge^L؁9#C$j4gBS KIG/H賰i7[z2ߑQ^lz\' ,AՐTRԴAk2ɖhhetl 8(#sXS.{3Żf4ƗVh0{!p_L71ZDkuO#&S*A,eܒջ٨:tU%/KRY* PCRYJuP$[J͖Q+"dp|wN 8{ C;"_:BD g"x&)2C85oAyoΛz455zud(T5mКL%Z٩$l)5"BIwx{,_\nNo =V=T?c G[<=C,F R3~&߳7,1P `˖-8^8m QРJm۶WA?5t0i$[J͖Q+"D+xc*[asLziRz:e*y%ω>c GGƞUv-YtN}*|5WU4p>.ZQbG gmgA}*}Oogsf]ޅԲ-8J ]y?oOOL5eer L 0zY03xѺ+CO 00&~K$Q2ɖhrMroD*Y^'fk3q-Kv9b󫘱gYz t!#G )i4p@صc ov48h2x4[.) c>ud ^TAk2ɖhљ Jgbynpp|[}/8oV@75p(F^3P!> {8yo.zCACw樁}9p8CU%&`eF [_#`/UAk2ɖhљ JYfgU30x} eq?۸H^#mo@Y:yo@z7qFy? lo}N]oq*eIo 8 ^=!eoZzm8J2˛$[A= #q9XrN]f֮'sx_oqP:_ |-^XvDQ;q!<>{~[tN&vɹsй{wxy &p#^&[ HΡ ɌcNZt>)Hq~]KXd[鋢d@q9P~`?ihdD [kZ4]tV2 ~e:ۢ&r+v^zHKI9yHjN2<"2=n~d08` |%us9NI\]L u 9ncvQuJv;^/>AIyx>zsb7_p/nǒiS0}y::ႄ KIDzrthm 'v]Ζڢb~5e1n KpB*i0`3jr8r:΃gHuCzXx85җʔKCkj%GM^^eu!=CAp4x,^zDAo^2FN؇":LjPti[cbti-??=mKTwqgcɔpws-qF^A#!涫HZ_BwM Bq4֨ܜ 98W_ ޏW_PZғKŕK_']`sI\~? ;n&H8#Dt9z IDAT|E e@=xT1sӕX3gₛ&G o (,*My X_'g”cn1M7o,=5xp;cQFzlzz<⃚JHr{=xizB<7\9U<7n{ũ`<Ԥep*ĉpD_+/Nk?%|hqKм.UYdISs8I\ Cd(!YY[AH9Ȏ!˟x˟Dl}$4qzvb>%g'=@D_4ư+a*&s5E,eBS_c#N qI{q0 !wy03Ygqɦl'1ԫ7fA7r!'?,X(k[#O#OQPbq穏P'VhH7|1zXh// xZ:`\<>*l^A0z, ϭ ZIDC+;Sk4v"|Eܶ wn綿ж][zt\*.n9?+BzdS KNƥWKNN(^h+;kR~R߷ GMLˮZ ZzUpt4⠨ Tڲf&+1 8`U^nfm ZyG2ɖhhet<.6/.xw$PWoֶjcΘutzh??\|,^0/l9MݻSO`8X+xy\ub<a2y2#igF|7ҏ'0E5d(T5mIN%CgAOrԠ=_#Gl6[oOJ}{q`> |h44ԣ( YD?ٵ??`?#+py$=ڟYNا쮾~1~<'S9zEM6YYJuȤ?L%Z٩$l)XP8C*XqgIJo& MUa2R6hM& Tn:tֲJRRfkݻvaƔ|0[~ x@,lqHv>Z:{+Z%ފVЮ6"PVVvm(Y#ci'QJgڙxRlQcQ?9 z}k~L5e *LTCRYJuP$[J͖!@^)J+a*[۷/w=hmkґƉGv:$b8J7-WVeUCHå"CqjEr@?90x*Qs^h8uL%\AdQyi䳰MJj ee﫚v $(.!:Es na<-YXy|-#T ꠦ ZIDC+;ڐ $H:ޕ)KKO$ 1+cڶUUصo]GDG\ѫ@˱_Od-ѠXe@=z$# R*k QXPD’L |HH'VTM҂9eC!*^/,T)5 PLId-N%\gzH&Go%[IeZ/Az"l8jI|bS;vX߰gqFؽpYce%_y7p? .R3&jLpvXCTdC{4$(K.;&?Eǫ!D8 8_Od- !qLNfb}"u< =8 صs':wsNCeE%|!l۶ ݻwmނ⢢f!~[B ^׃awf9 7^H勲1HÝ䜋xQz $ӓcb9y7`'͟w6d0bYWyT\)&YYJuP$[JMކTTTՒ9z(} \t81|+̻o.ciVQЪ /C^~Ҏtz_ѷLm^~9cyԄ+:d2Eu޽?!***0}ƽ8eIػ{W,TԥKE'ΗŸW"K~ |(yfz rsPq`ltZJzh&~_w I<ˏ%smpog &82 D<q0-pŹJbsP94<#[d_tx᥸kЫWOلqftD?EDJJPY~ %ՊEŨ,?K䄡ㄡN$ [V8>l2l@Xix6ylz8vo闥J(Yxe;u5g{-Nxܨ(ߋo7oTN(;xݕس_v7W)Sف?мb,Zhd4@CxC̊juE98C`E noB޽(_$iICt9Oc̫Qضw=>uZm&8l6 ?Du˷/0ϭAqL{f62sӘ]z-1|8L44asǶf$wq>` Nu>`;ЦM1Ɵ} ڷ`hhh5\Gy1FtMxvq&Mgyv=J#.EQaKO A:ǁ>ʐu]K. ?ƍ=}t k4vVCϜt~s^}u5fZN>;w#`9Իb=)lѣओ?ņ#O>qtP(LM3g= фO~NƎ  5?O/7c̸{зo_z8#cl߾7A@aٰo>n;wĂ ԔrM!8h@DZ` &A'eaW4l0<upt.(Ű :5|wg֜g#VĶn7OwM~߁sλ: J{DzW⫯B֭;<.}F#0K$裏 ׯkY< aw݄]{ʫիWcx뭷ocŊ8q"n݊cڵܹ3rrrRR xvk]IP~PXF`hCyxxq=X҃y!,ln}{ 37WCJ1ճ</.><賸Aq;:,y\8*+kQYY÷bY]]t CXz<σ1l}ԠC`2444`a49Wq5{Lعs'N9l6c̙/СCb1bD4 v5#@g 2j<U}ǟ9 U>9݀V=VZ&tAqO5)qHeA r4z!Pc' qaNވKӯbғ7aku8eal'8'82➻{Z5߹</̞Ռ76ҙ7WaoAXlo/~׀1Ii5=e˖5}Wa{%8ôs@qq~g7XNnGmm\|?Vz_t*~~Nv:n 4?#9L>C Pd;`HƂՄ^D<@f%6 ́/sQ\ڶؑ<<^v܁r tTS/sx!\vWسǡcv82-"~x`2pcӏͿhc_ओ^s,Y?}>6> ӧ]!IΫKpcϞ jF ;3 u58o!j~ǜY7}{˱],5uV舮8:Z IDATOJ Je)AM&lVv*I7:G,+QS] Ǔr=q9qy.d,8|qknp(O:`4b6K.xm=/Nv1##M.))m\ߣGg ;a@ 4NϞѳg^ 8|ԡ|ԡh:N2180Z F O,SyfC o~EQq'T,C,:id-N%C Yǡuuu UsMDNA@>O48_ nNgݸ m\^X6Ǽ/@[DIO:b?iRENz;HPCJ,#BO;Mȵ7d֖6 ɋי/o8q5~ ,EsX&C}i0]DG@۶PQ?6ph[T M LhjAN^ki8Dۣ=j$I, 2 qU?xi8f8NΨV lV.G zHd~Ľn7ANJj@=A˱J0[,QeeO?;SԀ\e~[V\#͒Pbρg#RԶ-^Zn ˞y}N>iDzz^vkWLаʊrvl6Y[ )e?'\|RJ,#BW1l6Gn7fd8yyyyν?pmP^Q6mp3Чw/x<ιo 3#L~8M޿><ZU8iiicAݎ={baL<,f 99=D v & $!*jEmm-|mɧx`4qܹ0^/,Vkj؈?ވw \[nƍysJqz|ìN'݇{ޅs7y7eaojĎļ|&ofxvfؾ?_^rIx_>ښ)qĶ7Ɓpasssz HcO<ʪjLq/ =|3hjEmue\i556ua[rrrOADAצP%rHBkӛa%֭+vًE#Gg9 fs jҊǬ1oάr,|a74b#`Ƞ#}۟11؈:؈;1NADBCJ,#BO0{ ׃Z,^mڴEq7\6N:)).Bqq<^/6~ rsQQ_+}*r /m!|SᇗCp"Yd2}eA>V(..ŋ!ϫH Ai` &#(Aj|sLD#BJGUENLfθb]߫7A;3{ĩFx|t [l;R9S; !GBIA2ĽhXFkBz СCgXlܼ",`0g^*+ݻw`4:_{@AăeAA@qy8ov؁;vY8l90t:k2 7/7 $ R6/]rHcUҳ@TW&% fFAA ֦dHD9 ް:W"//999 *T|a%\?+d<=^|()*F~nn<*G3+>99 rEqW\ `87|@cq8XObaǜ8,4'Ʉb446`58餑hWn ^ǯيоC{d2). "~hrtJ,#B/x<8.YN7]_Hhl^^v{jEN`6rcNoA.]ХkWlA rRbAzb6rd Z]< нG| >Fy(,l~Gqqoϫ߃l 񭪤$ ǁ TopјcCyyyyZ(*j&;&x Fl9ɱfho?Sq ,@hAqk@ڒ~occ4\ˮjؕAD#SADd[ 0z49-J `0< @hCJ,#Boxk`1}PSW6AݵV P#pAY{_ I1āx-hTOjP)&6Adb/q  R֊Qa "[UI8Ad84VPZU %^ vC` ADhUXFA$@}aZP&x={ASS'lVkč$D bǁ6AxPW_P[];j+a4w㑕'7 I=) J9 L ETU^SclwCۯ?M:{F1`iP A-9   ՆƆ؀&8M 4uNkRSAzchllDNaoi?Ft YS@Nǁ APW_6p9 9Nmۢ hm ADk:!/7Gq2QrII89GMYA:D NFyTAd ADf׮رVQݺx]Aj5 W[TA"QƘ ?Mlȑ654<] sScK:\NYqsJ q7" TEYю`0g *#vp*)Y/S>AQ0NޭV9nVn.vm#'qX~xG.(S_e?CKTAz8f d6?~f`AF`Q^m 9q4jhJb>\WxK, " cϞݨD^^\q,jPQQ*Rܗ7A LAX_PTT?xulGb "򑍵uص{,9r;y2q\JGSt|Ajd2xp#Ѯ.O]o0`2[q`~|G(i_bLpAJˣ./ o|h&dTS]U͛Dn] B/X'/7]:y-nS>}c0poB.A Ws%l^U1MDL|˱2evٍCh6 -ߡoޚAA4k_Pc7_cx^ 械뇢v퐓UfsWFٜADC\Uz{nތyGà(A}1`8b_P^K B#xG^^V+ڵm vF6 6[l6+F?~oԜ "{𭪤PjE^y.)S!-\\;wᗟe^yْ{ шt=qҬ9sR&AXф!"TsA;w!77MMMx<+޽qDP|;w[q0+&iAAd1;BKCMuJwg8b{c|a񊊋d{#d6'^muUbAAqZѮ] !]Lv7}6,=35W]3<V5^:#?? ?~%yc U+=+AAᜳVZ {EBaM_E;fX;poiiiE,^/j*ϟ [N Bk^c8d`0|M ">V<8J5D&jlL BrWaEѪukL&n}hlhfw ?\-MD6ll!BADx<8.;N{m$N ެ]uU\˪몫*D@YAA JHHi2~{9<̝rܙI2ܙ{y$>GcY>R$XheXl9D7ceBQeXA|BL*۶{‰h(zHr]\&J݉is Dcql.6ɏ@1oa]7hhjpMömҩDx,J,!V!&:FXuqSIg+Hcbʣyu+סm`:?bBpصz<f[@9fBPP@ @TX4J<'M"kBqhHx-Oo%1i~8R!e -3YtYu]zx^jjjH$&!`gYl!* VJXtDlT-xY[|i9-Sڅ#6k:Z^jHu,_c:?`B:پ};A!&Dv*pHg466K1SA1|0'57gع~#[m4]g1VXɺ\W~S !!hM[_{s=t:5d=^/oڄ߲U)T5սS[n*+~so'|>"0u"f[o$%Acxx<ib&a:{u]5DZٶmn~J RS[K(!a:pv:;;|(ݼ>:;UZaAQN&_ջ﹔o~l} ?\>/)]7h={===Jai`b1ML0 i:T}{]u̱tFd8!`fV?V^YGP08ٗ SF~M NN-~{P3!AI'=IsAA4F]]sq5-4Zq3f`&---b~ B!aZs3x}\pӚf2{Rhaxmmm$[Mt]sS]u\E.(LŪ\Ho/`0H}HCCtUB+PiA|> $I$`~?YV+4_}W/S⡛B tHvClh8@ͲkNny[mVtkfu" L!I8" *{8>BLC2p)[-sNvΛ`dK:ߏrJT Owe,XE`@U08n!po**\鶷+S !BQVuri,_8M{>\W^\DziSѨoh`ٲ̞3FfϞ-_ܭHQҲrA"<mԄ,B!SZɡbࠣdQkjڵ|GxIϟ3;vre.bpM KCE#|_`QG[^=ߢ`na5[b:W)M6l-̄BR9G1˲?ηg?y\;] }W@4W0S_>RSH" `& tvv+t="taqR J} ̜58v2 IDAT+FB1"v[h1mM0 Yah!HiVW7n ._w׿DQ{e|xPRD{i3M,\ӉfPգW:4Xpq,?g)0o,0BI.T@09sff15={'T^ JbUtFwR<'"y['F.yذ~_W*hNSs33oغ۱!7waUem)e0 -_^yg8p3O=et?!#FUɶm2Y[IbutbʣIlzu>r\V7Tv!a -.Ah8@}7|;_770{;|XquIF#l߾|>/Y6vqllqlqr9ϲ8z1X޽{yYt)j!(RlY&Hr[NFӠt*k(ŵmҩ$ݝ 'L, krNM!**--.{qGxGy'yO<[O95v5Od~ΤI'lذT* 䂇e˖cH$dBu457k޻w/f>˗.L!hJCHRX?~%ǨkhgYS~k\Y3wJ18K?%˨kh@7 ^< A6wuquwN:=]֕-\RO>+VL3>cƈ.ge4/]VE!D*'hX.d<Μز5,˒BL->9'Ae;~ˇxehe kٺu 9֭#8ƊJ$! jk)uD#466UΤilj"PWWaU+T4]1r&Tax>ޡM4Cs]-|!/eLxm6lygq橧@h!heC:U\cNqSjO:UIQIQEF}MpX<-yu 4m^0fx YA!5rd `T)WUC!DvCiCA¡dE!hiٳ:Ҏ=suäunL@!R͎O<t@/h._!t*Ձ4Mx wөzګ|ѣm(W֩J)%22 ۶̞78]@SyȦ3ޱt21h|B?] lٺΎB N{{;K,2'!0򷇲5fq0<^,[N2'K9 dɲlڸbg+byxWThy,YGz-3XZ!tzVX4܅ H$ ue('NXBQEVVJa&M!NOqmM0=&`Njj466afٮP/jAGaqDX~?ޞQ%{q3µuD"Q0BQ=#̚ƦfXٳz}d2vMJgg;fb lA)E*W;l"X@ <xߕ.DP(4lJA*.>5M\~̠۴~ !X!ϛG(gDq0 @ @}}=GuM~Pm9((0*o${?>+஫;o:gSAǢ/\+c[!R%[y 464L%I-?+Т/=L:UMkB԰k*Hq]{%ox[UֽwyTbZY) 2/W;|BT[x$aA292e A Uzb+BCްܲ۞}{;Xhj\#^͢E;سooŴƒ?J p*#Уi7MBIOyd2?jk}OU&C&7ZaG;;T89q !UScBF0ơ|z̝;:0ǪyapYW~i6$oCр,2\Bq uÃ# *,FTU 2+<'IGBJN\rAC\cB!:a@~GBJ [*Xjw<ԟYt1yWl执̮]xiLAs\W$}-q rC!x1 B1Zzߑbx&TP{χzIy @:ǟxˇz_ջ*;!#r-!9̵!',J@i1$nBTPFUGoW?|syiO:c2{L>{T]XuϹ88A?}W!q]h4[N;L:E2x|"rI'gqB1A:ukrYg$<5k ?Fφ FBQ% %N QxF tߪZY{B4ԆkѺGc=o A5^::xєb9 x_)_!UTRD"KO P2ge,)TU%7\+g֌GbRp(DSS3-3f`|BL uƢ455U5IL4Ð.B1^ gʏq45a|^M Rq)eY^h/BUQ.tc⺣{\cNqOJSڅ#gBY]Ӱ~|5nMmb++J*tU(WJ >J&QB֌@S<]1ÅM=ٕ_!0LVI; ݻwcEjdb:-#-BTWEt]Yq=pxlzu#B __Y&aa8J:v6;.1=t(;`._!BO%4]' ƛ x6姽,^MץA.|0-öm|R Mgؽcd4'$BQ%" ˯|L|Yd6~Ok&T;k $`1`x,Yd2N"1>ӱɒeٴqO7W!D5U2 ;k7̖f͜kmU#aqDX~?ޞAf5nh^Hp bZUdRMw=/=7!\[G$%? bŊ\M0rJ>pPJ:@ߏ8$ R/rW!Di.Gts% Sk'H`.c%464,4MjSg*_b㠔"IP4@40}Lf3+MaԘQ [׆LS)HeVJkiX4Mt]qp8L0$Ͳw1BjfCk~|4בLHR8aX߲,|x'~S| !1J740&ޠ߃3P¿$6+CՃ:"&zNƣMs$@ @8χ븮i~jjj47iӦM*o!4 L3H @GB7l!Z4-3Scuuؗ֬x$uo) ԇNL}~c O à߬HD"NIWWJ),`vqJ- lllUI1&PqY1C<`cI ݋r8(ZVCTɭ0PZ\%Ͳe2 K. /{l6K2$L⺮8! 84)bq6ڇjW denU \pmq 5ơPxud2X۷sNfΜrkRZ\ו1Bqs]7`ȶQB5<)KdzjA1!q(5tמ|ؠcYX_w.*(q\}wJT}+V i&ZZZ3-9C6%JW%!hzFP/u]T:MmC#sͣK&ݻvՉ:iPE}?5ipBT21ºkE\&yľgGAiܔZ^'wUݩܱ;v`Y@Mp]7xx\:&J*ɝQ!&*qp][N?@ @*$hN0O" c(0*9NUҝL1c몤rql&Osg{7'pKPq7a-U0<@n샦iOb1tUBC83Yι"2]+%mۤSI }.k#Cc>1bpU8,*P>LH`ȇݑF[05 v$rnpRd2IOOMMM~XiŠ0!͢i77tUB3̬JRĢ1N;\2Dbrx  I3O>AMM zVܥbL+PJ {Y~ҩd*8(ʼnh>|sgVQ(7Ww3.nA9::;'Gw]8CmۄB!~k:.N.dH&ض=lB!|4N# o8PWWQҲǨkh$T[C:oYU,T%B0d~֭]gITFt [ -7E+܇T.@ʻ5k  ذa:aiI)5h(F+z =燪*H$u$~6lWi6~+,_?`b^^WqZu꒰A1C:v?c[^y M`^::xєb9 |_xqW!p]X,Fss3b8N1hh?p~vm}˙4ӈbkBQ]î`̚¾6[Xl\ hjje /B*R[?VK&*==Š`߾}DQjjj^L:7v(Q= !Deq5pMCSCTj\ `Y`e6W!q]E*_<4_qcFK +7g\553ƻIeW!q5 gYC |BTO1anmi̧n9s^n\@7Lvڅa(MJ!HgU|dW';!c ݔ>6/y;txxxD"jkktMpzyuz,=wR5 {i"'WB!QʨAW/饣m?ʱi߿X4RY8}{ 2BLpl6;.x ˲|e[\pG'G$(GU㠔"NNO#sfN7~*r]T:U/Ip]u`6V?H}%};b򙆁ݗeZn!|: =ơuA[CtiXo³d2].hU'½)-z˩gcgR=I!& B|^zr C Ve3kw{K7}§B4A3<4ՅG5uMMMDQ^zY>xŅ\/Pz?;(=A IDAT!I"!L+3933w+?Ĺg_|/0sQux7|g}{vsWo}R|~~)<ڲ#BLy qD2 P(8` [q]f=x<t};aЇf_g~2 ů,tonn]7NOO˧OO}:~TVbK*YBqhO~Ə6} -擟,*7>{c:w*8v9w.oYRdiΙC6_$] E}}>_Q٬au]Ѳ,:i%  [F,O1 0fV5 / !B!1p9wyc}V'">m o$.+82twvpbΛ㺤ܖَì9s5w=C,œkqpuZ{zxuZfbi]LO׿B}mb6\Î<sq44Fg}C?gϑA!8E àc?_\W|ʣD#f_z3f?JMm-rs4+=B|Xz2naӺ{tuub&_ݿ=W\o /}O} 74ßGzGz-Q.MW!b]̜5%˖cgY,h}_xNS?gvLtm?h:%&fi̜1e 5}mm<XU B4552cV`-B!O=w%\E4K_pyλs9/"i5zWw~l ՇM_ˮ`ǎ'?‹.ݗ]?8eY̜5˗Xb%y*4}++c޶ COk55|^I Z," ,fg]ׄy=454T=˲x+׶?Syp 'Dؿo5.Zįot*3oG׊Gsӿ}t:+/+/Tܷe|F:b/3'laҩ׿uݢ0Mltb2k~˲F5hhVviX~?qȿRB!}=]s-^xwy$qbb |x<' sl{ϜhIp !D5Dcq¡7R9]ɞBx ݺu]q%t[^x|2$E ׌8x,FMm-=݃Ec1 h4zjKU3ݑ`B:OGg% st!#Bj8laݵsq&>SO?}-ٯn^O7osqeCs4 s+#IGgdΑM0pJ6v&=ܡC7M ֽgK^^7w~C\ƷC6^yaz/FTw~/|S5]!NO$m̜.>ۇ㥶&\܊\glZ-P+@ssTB!-ӦK޳_ y@xCa9,|_챣IZd!|Dݭ{3̳?®={Xd#PaѲclƷ1Vlzd{dPL]cլfHt~yBTɑ!.|>\t۷lfپu.bib^`֠4 ֖=ݬx/t'dq݉ 7@mSdI!Bqsy׊?g3l{{%D[6=?À7&Յ%yAC(j"]]'8BqW^9I%BNRL$ke|rV\ z-)bNIc[Rd}*dsSq[B!BCA*4d_A.pPivo' ^: ^,t^bᘠp|lk*WڄBAźbjFC5y>( Q˛ >rlHR|4Rkc˲#8\9uy)TA!B1-4v\l.LJtvso !K8 ~3sL>=6*:o!"Bc"hz*yaWQ^{fx4j՗ sZ%bɽZ!Pa+GW'5~fWʀo;.Ӌy(ģ^Ԇ4Y`iU-B!bxCTT;`pŪ^2T}Qfo(zwZm>%-$B!(h?@{Gǐioz_A1nЛH3* BVx#ͨ΀[z}ROoŹW4=|IJFW<KB1#S ^?ji^{ UKK!swo7}+hZ[njjx6< AUԙV乚jj Bk x|͓UKji !pngfm'?%Dq793+ 9Aujٵxِ8 2WH<-8KO`n}#>SB!;wLKe&.,_n^:>mWU,;jb𰿭GD"WO_8?~3\s]w9'W"e Go|'?>7ή.I%N=߄V(y ٯyXJjh`qz DDV H&7D׎XBq(P N=xxg%.ܕiI\ϩ떘} }') 'Tbj*R JCi}T?J̿A> ,S h|*9}m~_^JQz|lPhT!Kjw4r@4r4pFx! 4^{BمKs_N_J4-ﷴLRߗV;Gzqfx`I/^VGI%GQAe-k/YuCsֻwaѨ H4??!iN0b}bڠWz.="%K  ryƒKS&})(B*!V=Pnpב#3N;ˎfт1BJr=8~Zd[!&|ȝ}vǝ0o pSeGڿч?D!ރ 2bX& ",7I$7@0gԄ[B#P&Mz˘u2 !9e8 ʜs$u f&Mvqb¹J\}߶mR S=$il&˄BLyY*o~,Oss~L&{v.BTr{c-$ܛ}[mX3>T&ǜZt&cSE~,e 4]J!͒ڸx<&8tb\W Ț@ rR k3EBqo~6oLMm RdY4==W~/OrƙHp8Hg2<ZLàTbT:ͦ͛A̜9ksHr]x"A*f`^l;COWolٌ70aM>~kiBLZw

f̘lXjkև;*UQw_zEso^!)^}p=Ŗb7w?]s5 F]kp\w]z9d]FU+ذ5:{-/>C*t6K&kd5UVk jxץ-xzVh1~Tcol{{ﻏ\ARK/e \q(aed3W^b%^y^ֽ ~=}+ܕ.~ۯF].% !c^|_r`˲Y>?)躎iXŁc.ʕXD"cNGT_}]-gq*0pH/ӦiZ8,R@?#^[Nd2Io4‰'t|iYV<]߲a8 u\A&}tT3kI $ JO݊)pXAj[zj+]cAl ]݊ڵ[* "*R)g dzOa5e5֠䕼M23γdɼ$>Ý͝!g߽ +W~| /wknWzJ&.>5젱}f_vYƹ-bQ~dZ4ekshYuYd1ws/˖/S[hjY..壖I!Zk_CձtZt(C1cC)ˊTxW:/](&^^KCPѣog>f/[~/֚={ٗCg~޽94jbRb~׳ifZZ)PJe<WJ%)l"S&[1tކt6 #-*UZ|p>7[nS2;S^۶TY! ^g0O]jhp ?n7LGg'.]:'^mA gĎmh$_fۦEIIIޮh@ Eմig2YLw:r::,y\N'hͶ^;v&歷fSOqY`> O;_06r_:=?ZnWz<,?On8[6opӍ7.N `BMM7x|>jzh;mal zV&dlbÆ /_c^}=Κ5|uwwSTT9H0(RR[i\)Vd6N879MYٳ~^i#(+/8&}0q\___¢Bz{r~pBo_W[֬(˝68 7wʲަWczF9A&W)o&sRޑUH 6L38.ʥc|kJrU秔r埡=L%PN"!<w%0tvx9Zt:)dR:dӨ!DU)-[e̟YR,[d_Dw)JKg?ӧd?[~z{(.o~yBE_o+n_N6m[RQ^v˕ )mD"U1)-kHpɮHc !#?bE4CmLLA5c:JR;vLO[RZZǰ455TUz3|i08!u_MM5ӦZW淋5@UU%WСW7nw/ԖMɟuL֭[ƍOHq?38\yҥ_W9s>#¡5,Lg-<Ӧ@tyTwPHċR GWgǹΊ8+cFjoogӓ[mۙ1Ɲ۲<Բ jf;S,fyu3,+or17!!40@k NtUEp?8>kW?Ⱦwlm44U1kWgm_5H$OlG~c yٺ'ܹjg_wY7pI!1]f#@4 ‚BX,r8p:Mɯ_}%^KYEԇtwwk4MN%SX Ȝsd]4t+é[EG0 {zz!>U 6R|>X(y|hãԭ BYlko{b"JeZb0c֥LRZF{{-ZGtWh9M p]2AdZ300L-+TYq٧/McLdd66ΟGd0Me0M vG000%3VY5[!ƼIDAT>=kf eOXŔ%]eTN eq!8Uqq:;ք#‘H~f&n K4(+`ZM-'>:ٙtii çrJN4 ,ۦ?SSSEUEOe&Zqo#5^˲<5rz F`8hBL4XcZH\_M),+@_ 9x<**qtvvrM5a6ay>ҩL).tAcVh4ø* ˲FD2  l!P,_'`h(1=' ǮZQcu !B!D0(zE5([Occ#}G B̓!B!HmF+dv翀fˤД>B!Bz{S0Tdz_dnn!B!.r݃!9BAq G:٩}]a*\B!B\L ŘB6V-Q8 [˚|SA !B!&Fab3d 5IENDB`muse-4.0.0/src/doc/pics/main_window_with_midi_automation.png000066400000000000000000003047721404105311000242760ustar00rootroot00000000000000PNG  IHDRtlt>9sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATxwTUꦫ@Er%JfI1!ܙ;\ IEADJFP t?*ϩS?뜽WիW ߏͦ;$\KrI.%$?8v"iу֋͚Ӥqc`R 9+6هu;9LU\9NP[mZ31Ml~rPt?nƋձFW1gHk-^{%];WK"C9LQX _BWOO5Η >__;dIES pc<Օ%! –Υ4?^[w.5)JϻiڀNABd&Vg1 C8;]sдYs^/O HK?˽94VL8?5&kF\Tye#?RRU 5'@:)fϗv._}%>D#y6Ԧҋ #zKI[+ 31oRL szBsV㑵xƦ*44I I5%K5H ;`}>>N6m}ҥkTWUW,S; ʗg(,Hn2\Go Q[&u#D.)X+tm&+oCKt:IdN׮ҙ|R0Ob!_n$=3f x@'yXZ]S iVt 'ꋂ̩#sBD~,O:OL _fx;V!V0M@KXG Թ먭% q\"yJn7Av:Zw-"; ҨI ߎl4eG=dH{U:ӈ,C0;cw\KEE-[Q#lAN>ѣG###Ca^P$-OP2 ~tr%kBE_d@:ffk|}#VGnd<*ҨF D8ѦWO)6mTGXy5 Ღ`@=N<(3D"e2R~Ip86L0]*ܹ Z6oNK>ѣr M,z9WQA-اO∔ėeK#TƑ#`458T]S~Wv!άF͊is >:y-Zqm_|ȎYoTrw#݁哕ͮ];iܨ X'L dMuuS&0NBJ1*A\/^R4j cy 5ǪO~|b:T;А2K-zHH$A[o?ev2Jc~D/t/ L;D$_u19T1͗fD41DʹNA'ӧֵ!˧cV6wqFruOiTXv\T@VVv  9K\NA]>6#3o=Nɓt-T:{tw@ɷiѢ%G$dHZeoɃ5=CuaER/?s&u(^͓#T/F[օ|$$_XSpTLJl#n0^xhΌg- #*__L@M(amxS7!L/?єǟ|Jg "Mo2ˏ)j2'mHe3'sVp")޹I0OL~?'Ns.@|.Ѯ}JKCQ'OҥKWK;t`ݴhBSVrښvN/РA&;vm55r=Ǖ=tbּOh:mX0g6OҦ˭UUUde5$]kzqddt"?_~_b"'NI]|͸ ƖVM:sfUB:N/aW|mTG&T!o&N#lngZ'pUb:  :t[;PR’şČc#UNqN_u~g-)=}_spMtEo|hQYK㩣~>Zn;KM$`4Џ7 E[ |9à̈́cU9kFs9^Rd.nr},r7kZUU8YqLUUUUUgΞUG]x] ε]m[6oaŖM ]c()GVΝ;cuRZ}n3p4:u` +R~7wQ0Y5NäIeC|,ØH,9iS1}MKj[u~pщp.hd"vFqSqGtN';wlUdf:vuunvI!&-S"F"zT*8.78NoF.'333j_vcdf&vSh|̝O"ٳkw`oб3R`du"N ̣4+DO$+Jyժnzߞ ۋXe>M\ Ӊ]pksF|w6);v͛6qUWѹs]I?_S"@]7O̚ի_?JOoX6%t:Ԟ;4;wb:wDI>غfr.鷙@0HeEkjtUd7lJNW*O$κX!Uq.R:)IRޣT/",# WB_kЧwm@񈑜)?(ףj!>rwfqۄ̛;`0H۶>̾>]Iԟܤl}E}zq0v`RMo(qd0?I 9wDl2+fʹK;[έ'y|WpI;z׫Br^ 6&s6j̄'sV=II859w)w'ȪWD_2laVZtƙOmΝ;PR{KhC٩:;WHzL'O0zJD&#='Nrnv84#)Hg3u;!H^MVWm'ŧ $oTB|=$b&Ke(SIF(E($VD;iYd##nHj>/M6%7iByiNZZ^' ]Hqشa{v[n׹ݒ*%yԹJrGf}l޴OQӢhB'7Hܛ7n`Ν}9tۘ8w̙3Lr'^'Na{ok.:u;\4kҲ uX@N9!۹3gxn'//;vbOzniv{v&/7ڱdɍBaNAc(l5Cu3 D̵u=D':qK)@!}!D4K:BD~x"qt"';⛥FGU@0ȵn:?/_hkRTĎ;[!B'eV=cGCH"qDs[:qF[>D"uv*ߟ` SIQ;oJOU1*4 694Aɷtxγ-淴j}%{ǯ8B>|G٣m26o\륬d hۮ=-[ʩR*+*8~{~45eƊmuە} E+G=YQ/j׊>0CEBz1_u R&.ZPbDwRq }nρ}{8wV%1{G ^M==QݻwgSdyfLz'e'9~=wѺU_7@m=t܉oۯұN\5Ama 87l]2o[ʢiOJcjBз1~HOc!=ԭIDXǺ^+v,eW/ ,IJ|UND KJ;t^/6DU M4wm-AF%ٸn-5m?[Ω22338x(ʚ}T=РWw.f<⩫C Bb4b.kV~|)p: />73igޠ;ѣGZhaЩdʺ|PN/u=+%k˵Yψ-NZ/ƥƱaǦC qkctlѠiio` "Bɠ++K[[[cT__Έ;].<:ѠAfbZZG"`t,=#\!e:]:^FF Bux=.WbS_ O \YKBaUWV55S4nAɷכQԌz K@V𹍉^y/<cr 5[Ovh)v I$DT{qz1b@cǎ}vLuI`رvK+{ТE8PgϞ㔖RUY' ;'"7oN~~~m"L.:k8V'ՋY׵BbIѓg`RFBP`Y%z!]ɇu;ɡU WVT[(\r%.7ȫ骅LfD*+ǚ* }W g d\l4Mr5ΪN 瞧U9|>Μ=(=^t9994)*KXCxUIv„=\;}J)]I=*V"VSSϏ#݁33,uX*H])$v ֙Z`$vRc.h%͵J;u'x!D!z1f7q 盳%+-:sΩH`-fB ".8uMyZH\(9q d\}m1UpZw~/.'öS;YPDDwJ^Ml6222꯴F*: <4_Or5j٧W(7z_8 IDAT,QzzCkdPSB %礂j'%D.9I~z*X&՚I9N|{KvV ]F BSYx$sTD Ob$# ȉ]RC4AŌitMȝ)J?4VU)F~D:I9$ϲNN> zdӛmUg:Dͪwσ 7ˌDzkR%ZItCֈ!r&doRB RETZ AS$Ho hڢ 8:Ǐt |5k2JO#~ys 4 }*q"%i%%D`$w^srO*,4\1>V|r(mDL[; btkJnߘjxO hl5Xh$OWg` (|/Uhي +**hѲ?@VVC3jE[ȡu2Ų~?K?L'"?B DZ&9{1dD<@hHĤ E;&> `e0IrښR&BN4&񉚼c X'z?MOu7TaHp05h9XCo mCs]R:I?7Õ[)Nk3oh+{V$mgΜrzSٳgΞ%aKMM53|W^E޽y!'7KaN.lF0mFv^!R~\D1$w9Fq 2UdţlK_$}iV&^syV$I20UttLE8KItBNߣ'=ғm 127NJvB˸LRhaJǷ`N)-+*+iGklJ(;M)!1Zx`@:; #}"d~Xvʢgy3g$(<<3<pfsG#? U8iik3c D0HVV6/+jC$BW5)u!۷mo*]U{K.uh"Ӈ K|a_ݧo}-Y#?$ͦ>㏹Y`^גC?&Y&u/BUNagIGV*i6F+pw}7 ,PeWUV1Ǐl>#=`A_4ZRCpo^n`OL'FZLl6̝;oGj+O6ô ;ISN0'8˜U8%/B)~ ŤE5N_}U9rHOᎬW+m,f+@#GU+W1JfAFFꕫT˖/lRU5jXv, A4r۶m/nKr+FMiJ:<ɿO>Y`æ,=f og駟Jbɒ%>q"ظy%fFTc)s%(u2ȈoڲE4 %ifʤ3id.XDi,5q&oLkѨ| 0q$>]>ˊ#+Cggԇ> %YVMG[KS*9rWV|/_Yz5#F`U:0)Ո/jJ Cmjc,^DݚH $==G*}a;$== "8`*wN.]` .D QllÊyg;f,3AuUu !8a~: ׇlA6tBy6too|A'CpA  ^/7x˗-[nᖛo>U;ǿW9)--eȠ7AS<5}ғ'A|^/7p˗-喛on>`W_1ed+w֭[OAN 6D?bpvAUeeToǟ0|p-]իf۶m j_},t)7^7ϖ-:tcưv͚y9UVƣJΔ3hjjjBYSðC 0pM1߂ӼY3Xv z{&O#m>}~T)o'xˮ;U:չL4Q#FwQeee<4}:Cfiv&A][RPyz<ڹCqmkTkAGɓ\{5k *+чx0>O@Px9b˖-g,[-c3f4˗-zzCaIlڸ林]Ǭg!`uvxG1|8{۷n#+[ۢUAƬY,^qh.cFyy9t閞Sew)쫪i$_&==O'"Ր?NΞ;_j˯_g$==S`EL.r,O.]x뭷.k|  ; B׿ V(($%;w_>o|Wi۶~ݼ WX+^%$:]ve\yՕ@he#;{]|ђ?/Krw23u˭O%5jܘWtgmx`]x^-[6cubƍ޽;֭G/ngŸ׍F `Q:b۷O_k.,X0+_E~~>]taӦM_:6l؈6nD׮]^䷿-K,;`W$}D.Z7qڶo/"˖-3MKK#a6| -==WÆ|e IKKePSYK/Lm:?Ug/đ/(xHKT9M3 VL~~#CUIxj"|ot2amlܸ)זcƵ{nnf\.cǎ5EPڬzGɓp9 ?(߰q#&Lt2po &r7n۷il~LEQ Yj%).o̒ ZjZ:K 6dbnvsx-[@zurH6BqOc*N)sN:ō7ܨ:&]N6m۶! 6իV#\aÆrJŚΞ="z&ضl¤IqB`֭P9\ 80v5z \Ѱa'M8[)Iq_"D0TN/dܸ[5'>efN'SLaڵ /ʕVXIqq1+Zj*0.E,^!_|9i&\J߱cǏtr7Y!DfD͛7GǠ1c7PYY!S;-M7Ed-_3idN&֭[ {_؇df͌3`lݺU_`%i޳e&mnc۶m>a#0uT\N ; ɀT}wk!==#Fr ]*Wd믿r?~<۷mW-SDǖ(0NȧFxb&L@NvǏ 5k9r$ZStN9^ ؽk7| .믿^qCs pBs:tk׮E׬fȐ!^׬YÐ!CA~~>Wa̚.>F!(㩣)2.SNAA>^KSYYCNntbNjcU?^Dmf-[tXl9/^BfLlMuM |He/cNj0d6$ Mp:] xp9"5 e,Jh.]m6ٓ[+WǑW|Y qaC5knw-6n䭿Ž܋]ƍy'E0e~~ψ#5kW|~uU9ـ^}eŜTE])usr5cH $4xpxܫe}߾Gym۶3O>rusu|ry9@ 歷͖[xWx衇se}w}QF<أC^I7o~˿s3t jM].G嗟-77f͚ѺU+\.~iРW^Փ&EE@۶zƉ{mI~~>o7`ΧdeeQUUM^^.gϞM@ѠD=ȝ$l6|~?GBqrL97F"<***}:vv@fxgxi}ɼyse욓; yyyT}VD|&+V*rss)++U)Dk-UoG<}BQ@AAV1˧}ӥKZhIǎ[Cg׉гg/x/tҫW/jZaÆTIyC 2{j!ng4i"64~3gD"YUYI^^gLܹP>WkgbLxhhڑGxg(+C8N0NCÎ8V< f1rHSՕNy$+E^={NxBG+xolz21h.<=ʋy9 %2//v;oөS'ZhAyi߾}@fxx|sޟ7&+Sʮ~ǐF^^4-jGZ".l233uKgR/os}qxqK"۷/s駟 O:uĒ%q,YO5jԈcǎK4[XXu먩aΜ햰ǜ9sfŊs=(1|>[]>Ypuuu̝>{_6yhC|W5o]Vvf#Gds{XU֘c={6#F@gt.t҅E/]'|:a>}0|u̙=޽{mÊ1| @0h ϛzō@1~m-[칳;njBЧO͝fӧOI% K}:wG-ߟw5ڵnKNuWM"\s .nw}c>,6sF/ 2sR<8nLm 2e .dʔ)uҥ }!nw-(<>50Z͛C/kjժ5 ٲkBV^CMM-nhLv҅?Zw-a}eܹkk{v&O-ނ錑pVVhذ!_+ʰfZjjjxp7#|^.5kQ.֭Zxѣ eV IDATG1˰]޽Y0u:ޟNzTe̝;1cǨ4GÜs1b w,2$"]:^bsСCX`! `Р,X0oCÏՊ5ozm/&s[&sIKKrQTԄ\&݊r X}SaBp?_}~Əysw%5\CW\<cIti'~-7}$Aؿ3vXsxjiy !2_F/qVQqꪤl (˶-R>8-X@5'$y}.u`Í01l$q[>s ={ɓ&TW -E΂*qG!d>B?Yar>~)&~a'~0o/j5tBK&EX|.6zOJljHRJB` ;q0 yz=e^4˘iZI/ ˟ӧر#oBkhAWgM$)zf]N O$'$XSɠ MVF5D":ߤ-1'ȗ_}׊շ%cʥK2m0:x)5Ѫ `J¢L2r|8R3+i` 'SZBNJjifZ\uC^"o?WQ_rd_|AVH6V[KvҮ<+=*L(Gwծ+UMߟˢ@yr˹{.z^lJdpI6 %tn.CYGxHH}][$C@葤Kɮ说&:wKՇ7.=pȏJP 牨G¦3q/&y$aD~c0;^={RԴ.'N}k*}5U(jZ+'ٶm+)uՕ=)jZtё縘ʠ"tǏe 5&F.䅀9vׯ#{"j_M=k>>=ʱ#GXv #F@eRZzM7RXX@3\jjjشq&qBR3_R.]եy Xph?(,,Ht'|>ܦME}eS}m޼MDJٲy4O-[6s}iԸI| (?q1!ȑ23=|X;c_۽ݍQ#-- A]]NΔ (¯Wk-:3gs砬 һΗQ Um8}Ak }]lb9db;c^Zq·,4M*:W+͹vޓT%r^ O->j I=^ݯ*ñlmoԗ\۴Snem?iӢ_g2h\܂ ұ>`0DQ]SùgG 8z[ mq׹iޢ9۴Kn*bň#4jيחO3R$s=na'u2Qf-ZڋvUotk(~5ܹ+Ymlx+ϱӜA+>!%%ػgrYV;WRJU=AL6m@L'cƎ1x5ܼE+CQQqV>g2P}ėX}8p 843ի`;dn~lHuΦbk.?֔w޽-';>jmlI ZHQ"Hf) ٱ\@)bE+*ziK }f?fwnv 8Ϗ!;sΜ^yO(3ˌFѣGӹs=3'@7#^A٬xLCpäS4:$0!k%%F+d1"ehr*#"9وN%.NRҥh4h4t>BlJ @$v{l~:@ÆJ Ղ f" ¯/=?"́oa|ßQFC~Aȫyvm{|_( -\4,? U_t40lH=QI?uQj9ԈZ:;j4:<{O?0KE;Ǐm?)*hE@7Qp 2;th%͚?r)2%9999rՊc>_Dz}4]GOy8 IXc1"m.j噧0y]᯼Ȳ\>C\ڣ/щ Ѕk,F$K0 CX:O}a,nڸcGбcZP1ǑҪ۷c޽dS'O vZ  U2dRZUJ/^/oV~])&!!T[lvїA]$1` vl0p$ď,rt}hH;7~Ǹҭ{OxqKSçzq~UJ2|(LE z zLCdrH:iG*rV)]OpM w)- KUb0XNtt  л3g>O|E)p e(8w”MN梢 .ITʵt|v,sT@ο ^o/ZO7V΂e3os^* ].hl#Q#B xYS'N&h/O+s+~ 14:>wlv.RZZn ûNNCX[K+ƑtoE/#oDEE !L)I.3ѕ$(IEݎ(A:񥩫s5'4\*Hv &c/b9 KHFir I:-tۇd6mEɄfs FGGimII.ĩ*K TWSD~NKKs uiiiއAP*ö_v5I﫮M6Ï 6]M2[fӦmPJ^~>[Q)+5akA#h(-Fn6mڔrAOAƍw<4ueF#O= O>qqq+ѝMk')g/OӢYS=T2dՃr/LFϧ(/uBQ8gXHslqR,HzTz W Sf8vL&7WbQ䩿>˖mAP[,2_|XW&?_P\x͛=oV=AnW;'@}(  ̔ɓ:dW; 1H݅ !FtݻH1eS;>];~I L[.2ɎaTRc!Ks4 qNf_.dذQa1߅] %r>D#6JK p!PNGDD"t'0/D#)(1!A`,4J(QbDbT9 h<(!"$!;ՠE8zoy o{ c342`Srt;]\.UÇӼY2FW\\hfj)*.F#hA׻r+x5%5A:TIjErүoO["6gcEddwn Nrk$EEEqA?w0p@:HTT$ȾӨ:deecŀ.NFn1 Mf"", FØQXg|xMf.|ӦѨ񘓓G .._{8]3FqK8,/<0ѼnœlD^*6#M6嚱ccvrҽ[7~XVR/ /VRazwtb6[0ZDta" L 9U7ց N<2L (,c1+ec9pu11S\\Baa!&zwiM#j 8A$)Љ,6v]測Kj1YNE$Q ׀/]%UYsa:6K)`&Qi/иX_f}OLre:ʬ$Ei9=ncjuvF޽hZbj4huZvVss IT``Դ>;~W^^+_۾ \9bi]޾NGjjI߂eRRpA@8t 8̂VE<]CXD$Đܴ9:gRZj@=2y(pB&Le䥗_7dtԉgy&M1e KqOG!5K#Ε%BfPQϔ#PzZCqAFǏ. } ,X,lvkUe2q)FkS$!N9L]:MlslRHf&*蜓CA=_0UYDaDXTaxY9i"bS1ĥѨcؤDh`a]AfMhJKh֢9bvAƍEUˑ$A>$6t\TӍ$~;}Qe59_e:íۿ\WIs_֓'ܹt+|= _lӴQê3Ľz^Ά~bŋަW,x$IHf\b&ѭk'!X=7b|,^;=z III<3jќѣ|MTh&5kwS;vx_u>z.ȕlJ`F.̪:JZ(O\ʡ|׀R'lqyʰ{hFȱR6:AP꫷i!'O*ۗثZ ܂Xʅ6oñ\![lŠ<: IDATJ&^;^СCگ Ȩ(.[_>7|˝w̩{LO-8eLib^/gfܬnA@mb,0dvcl- " .̦`7Rj`ctq=¢7EhJDdׅ/!, s65RU_gA0py,$k߻Y: nb>˞,Ⱥd}VS1&m;^Ccv&%348Bt}}nׂVmiώ+.=J8[r!ZXXVNE%]=~l`2};fVIVl( Ic4I杅c Ox},E&NeghwZAǟE]FChP{Y\*C 7NbU⧘O8ߓS\3~Mq̹Et k*S.byfZb"IHR c^nDTrr ;[Y68]eq6=$(^;ul&7,CW;!h9s :NGdd$5PP\\J0Z) |mTa.Yo>! g0W51xneBN;xmkV&- I:L&:˚5ۀ8V!Im+)+ۄNfu?GfN (e DcWRN%9[+0s #ʬATRCw<,xE]՗ҒJvBݮm6^ƌ𣏸i٢yݫ8ϋ+OaQVܟW k~I58OTtrSlgTj3@d@խv &G2  QQXE +*G+NTJ&,ce EkǴ@le9Jb мy fVNc玜:y84hVu@P loZLsW]qR[+[‹L50wirw+c~ɕ}tڵQf,4H$7h%=Nَׅd\ޫ7CeŜ=NYfλK;?[:zh_J4ؠI`Oh}"I"͛ ᄅEPRJv3 ӯߟdff13k`X;z}v_œAP"ͅse5q龜d߾t$e8 (ͅM@F Q֜ 0Z-/.$qAN:d$<, FS^*9etaQzދ`/фª1PV_zJ"))dfdVEӒوp5MSe(kB]͇Z}lTE nG<5lؐxZ2aVb1Y̽{ 3|P#">‚"-5ןAmBdJ QQއ^ᥗ_a׏ŋSTϴ[n`/Qogҥ}A.AhA QH&+ħw[-heݽ 8fF)(MK()Yټ`ѣGjY\FdŊ@ 9غu PQ~Br0Q S~2~\ pEFp+@!cc3f"OAjޮcǎQUڎ&1ʮNL5bIiU:D糌 5kFJJ NYi1M6 p\%Ɋ@&J(_2HCAt;?%e)+V+&];vkNr`Z)++ײ~ꪪx^oAFZc M;J AFjD" GS@svOG!(ŵ*j*.wܕ}z,_l$))&?IΜy?e?~a6+z+{90\vU>Ɵ_S|}2G ra2`"P$m~UXX@IIqʓEHLfsr!hZ DV/ lpG/g͜-'~Wv5)Zq[$dYĉcҪejj).39D"nu:.{ h0$aE~dvQj"I2eebZ]Z2͆]: @ 9RrTB[( ȲL7!X`P6q 4s>Զ] % лWOZ%cKc 0Y,˴hޜSo ᄏ+1u Μ=˓O<2'k^7j{+ҿ_wWm?5  nlJW[бc'ƏK4IF#k~A^ׯ˖-+iÙ},+̜9ŋǟ0e$"}z  2ZlEZLn}x)WgEuY##hAƒVܘ#ZI>OddNq'VeeMFe/\(o[S)3Q*$S?`"vLvϚź_aHx7um\u]K)2(*ֈO?<:A$ đ#G9CF]P%R#8:cECVVNC56ӴIcrsrtl6.NXXl6UZRɤF]ZR̈,U/#Ni(m{-6&T]hyWVKLL FXۉE֫FXK}]~Cy)!2_Οf ՟q,˴ll Nwf3Z **;]~\84tj&TNTt["/7ѹT JI!,"kg\:wrarnf \جV"##r^5qGv,1MjsY.TT.ܫ=>Ɏה0lhuzZ$<TO.zq>ZKb, tal#"R8oO[TT qHaa!SNupj̅PCkPUq.CSYU(RPP@ln`0PPP@|\9_Ttq\ڕ6m."2*}.0j9v0]tԈ ukPUq.CsfUW&h$I VVwFАҚsoE?~zyMXRSR9~H.\h.}R/kWE}KïeFcy"7x]u!@TttȲ3O=Ŵ[nf}ڋ!2*vP@}?zQӿ*KT66}vVx'O_έ$.~v/K.qMҭ@vv6{aȐ!?T$y,[]vQTTDDD:t`ڴi "CCED]]5 tڕsҬys1lp~a}]^b2>C+eڴ?lpfu74toG`0УG~7iӭkWz-zS]v1}4vgT̗.nV6ḷv]mMIeBۭ@tkFrkU{e2e2_|zH/_~]Ǐ'!!}ȉ'jlVXXHiii;5x'i޼9o_+Wǀr˲qع;wu6VZEj",,(|X{/^D&Mx=̝dffȯ`:RzQ|Tg﹬[Wwٳ 8hŭoC7gs7ѷo_z=III̙3{e◀j2aD>cƌ/´i5j46z/77xc1o={֧;ɡqt:ɩYQQiii~20CNuN<Ʉ ҠAZ-111 <;PĚ8q7nd)L4͛7ߵkfc1c&;vcDZeƍԩ* )NHL䦛nرc[neĉՋYX, ߟw}>{D[fΘΩSO<3xjܱÏ0f5<.۶qz7ٳoJ^Y[A=ؾ#%5EK!6.EC-{5LL: b0u4k_y,cΜ9^Obb"Ϟ˯anܬ7lxۯj ϔ)9|빳?;Ξ=˰ayu3ffϞáC<۸q'Nbʔؼ?A.Hm εN`Lr'NbӦM_jj}R˟O=ɛo/bђ%<ēϟ|{ xȷUo@',Ӵi2'%%Uu?N޽+=^d2!K92o,|?ė_린d{˖-}v|G\r,[̧;.LDx82Lj{?TT Yfl޼S'O*s_uZ-FcǎފyX1ct֮YIY e fcǎG2x Wy y\DٳgXx1W\qK/]wl[ngE%t:-& Iٸic%s{,˼j2{?Zh1))|ڷo{*/ݻvC67l#alڸ1#DGGt V,]^RFtTzdO<̿+zE\lr+A,CʾWVY>&LK#vdYBD-ZĄ }N}yἊ Yj;wduGYaLfe}RSSt*n^}=FcX 3GU e y`1L;z˗q]wۋ8uUkgvA׮8s&_"Fpkׯ?˗-o:_SY]gf#ʾHFv;#G <, ~7п?o]?nkÞ{} I\j4$ɺ5 sNM1w݈/[~b)z FVV]Ƹ=Tj6/HfF&z¢"x!yiӺ8î]8qkǏgmE ̸Vf4mɤ:tC /Ě/ѢJ~g lTu?~_[;_|qWK2KllY'{Ykժ͚5oAe6l@bb"))^ݮ/ޝaïv]nM^^ϛл۶mwWџȤIOޕF,.Hmks9 IDAT (LpݛL_?R˟Y-پm;-[jO3_Y|PU$5%e 駟)_*fee} :hͣi&;q'|U>宻ҠA">/ (c߱qq(K}| Fjc0ʝC?` lܸg{//ȀVSlKlgEEGc4}]v@Nsrrtէ>ӧOӲeKN}Yx'2dK/T.tߥD3Q4Zm.Hm w`(OGo퐯QU/|U/o=1g6/~#GwƏg{W_nfgĈȨ*s2*5:t-[}(..&>>Jh`l6[S`82M6ekXfM@ZynVzq˴HHH4Rb|& (SNeĈ=w}7}E \NǓG|i3z5; (X,"""0YVk'htA)K-&N>FΝX ,xWUU]v_5ՉSn_dvJ|\<:n]xcP>TRPPZJ겎ve~T՟ylȐ s(K/W|KWS@WyY‹{Q4_xYZ~4i"~)6mjRTTg}3n*DL&W=Ö-[E&Mx v֕ua6Yf-]^T"$%%qv;gΜ!))f>cYj?eJhт͛cXz5qqqdeewc2oرcC'I"\;jՊ-Z`Z'=TCyJX̞3̧`ѣWb?09#e˖v]Vf&)))ߊ݇+뮻lyi;3s2mttkM71T6S1ïL6#G2euYo1) ooX[{AL&ΘN~~>̜>̽V6kĉطo?&Md\**d-z2ids=K^=<ʚ䒋*駟1c:\wu|'{=!.Hm׋P;GMs˛@o!..+esn]JJjC?gΤo߾d_XfԋeUUUr|gQ֯瑇^zoSh 9|$,,\Z':gV~R >y}='(^9 Q9p-fBvU\(>P^꺎e+B~UoQl6ިL.?g~UQ)oJXmh'GD}DGŸ9ߚr˴i|-L1nɫ=1暱< L>.]m\(PQO]ՋUԗruG~UmJKK9r($sɥ]x>s];wiIkӆ:8d:33͹]cE?w[^/I>@,7kV! -B=>TO5BEv']u;Ӹi36l&ef 2iPo$ItO(U>C5X1DYf0h(. lun'2@c ԇr*sԋ5\"9&5MnVpNp 6d 4  "AAxO|܇ -jW \5DGs~ZRzR]tlLv8_WzO՛𫊊Jŧ@j"yIŭ*m%^=P^'n=/%n;M*旷g>=|Wyd*o{U[;***:ro<#އg/Ȟ# Ar`W|y_)/g=Rv5_J\*{RxF^)fbx ;䎮[5 C(P\HqG]ų&S&inߣFqMN5kM7 ;+۷1h0"#Oѽ[JfoV^^W`(**"""3iӦMaرc+WS=1c:qqqštqP0b䨠 6.Ýx.bfΘAÆ vGgQgUe дoU"0^}5F09 EKE?SfQ8PBQVDQ>u"***p<ս*))᫯__Ir2eJ*ygT@믿[׮auz׽/zzӉh4u6~'n7 {_g),,b͚5Jν;8KPu`"ظ3,STTė_~_[5O0 ߪEao{5|A tH`gBmUY?OWbhUTTTBX0?C#v&o+p=߾}; l*:u/

/ֲ{n/yӧOӨQ#n5nݺP\\˯ѼY3nڶvW_{V-[1iDaU\!L4n}zhe?7|S[g֭DTZhǏsǜ9~^dB%%K>m:^څg|M/_ҥHMIeCsן,\<̳cF>gqEQQ_|%:ur=[bڵ_^+㡇aaa!j@ʀF,_;de^o ~{ qwytx+sfݗ_S'Ή?璔 2^DM\Z]1r#GfÏ2wEiҤ 7n_%!!-[zw݉(˯zmKJ CQQq~HEv;#06]^π>S>}Ǝ!>{WϜdРAU s;r0b(q'yyw=.ݻbܸ F޽{ g e@E&L@dd${"''nYY0pK<E ^`w^0ydٳg]+n3qA{.G:F0nXm^SQQQMs/^Bv0%; ?W^y~뎏^I'^CHBA.HQ4П"EA.ʋHHP ((H `=By-6g̷MٙfvVo4[ àcǎ7WbP:.Xl9މe?=v K~˗Soݽ{J3a\r}VJCVVsC+->n"wAmNܼy?"J߿Ͼēg2OM&rW+ƖھAn^6'XvXf &NBH^m999xO?ju֘>}:*UЊ .\z !͚5[oUّPnsÞ-[z2O{k$/?_FÆ K",, 5P9*V>}̙ӊz ֯Rq0o߾Xl[Ntt4n߹#i{괩< | I*F~iJl… yhغmu+0 dd}*_9@D?VsWbccnݷl{mڴQ~(*%K N8}">>]t={u<ÈGΝcǎb[~}@bƌXr%^}Ư7۷o믿-ꩨ믿={`믿bرʊ:^f3f}ʦW\ݺG-[GSlM<k֬EFFܹիW#11I1`o{nXVdffbݺux}әTZUVn+WZhu#'';^ܿSN4icƌ;+ $..4iظq#4i,)[L\rvoSM(9rV/J*đ#_K.#//ׯhdeKAʵe Տ}+͛7`XcB^E٘7o.U3Çꫯbĉ~?n81GŴi&O['f3z!hCU=:Zv-Ν;|-[={/[E)jժٳ'M3 -h۶-m۶|)[cƌqݬ|LycƌERR"zꅗ_~s"-- ªU1z4-[СC{])}vF6 e,^?رgϞELL ~;@멨֑oի_1:fsr'\M!:tQ4*'B(|ʑ#11=ъmըQʕòeT=.s-L<_Oc u~'9V9w-멨ב%''cVQSǏRعk7g{bѣѬY3 Νѣ1uT?S]_w_fôi|"sBhYOEk׮:$СC! 1l0t ֭[~GqQ :V\^zF5j@ip5ؿ?ƍш g+@z*juhDl:| :t* Խ;ݻ7}рm=cذ0ƍQ}x"vEu>`ʔ)5kV@+svT_~[.<Ց{.dH:tԿ (O:*^*JjՂ۷ϧwylB:1$10^:uP߸lθGVo\qWtСCGNV;&f[-߸en:3Uja#P[y]d`dljs]v!,, QQQBdd$N(8E*g ~KJhJ&t'جV<:x@ԖdBHH9f^ONN_a0 eT9-)m*ɊtB 7su>hJJ%?Aٌ6®qѪU+4?<PLw-IΗk20'I5 D|ˢ p:8y8: M0zM Hȑ9B7d}_~.;w1VWcG`HזeMЯE3޹Z{OC[a9u}m^Z>x MaYaJZ9#-ݬLz5fNy,B1δK ߾ҥwF N qMmahEVvf7R4]P_ SΡTt)TR{JdggcӆoqU\p'xO 8O-Η.Bcfpx+6=W_Cǎӱtyt8 N;N|~t"yn*Ēo`PrG^?cxMkhܷs3 ?>$N8NmZ1kd|Բ)4muTux!~z B##Ѽ]{wl?w^*Bl1x}[L1 ׮\Lxfn^~-ageFX/} wn^ׯ233!lY`𻯜>~O42Jq?&oݼi#_m` #cN /?(.;G-g#؇g.ӹg$f6a-M{[?۾(;p8ݧ? R/j쑖z v)yuvvyp5/ #_[k?{OZ*OXR`@~^;vKf~G/wBCXf}`xtp,|}̌ HNLDCek߫7vCݤ$u^`w8`0p?K73#.Alן~: X4h̝Áw+xk܏ǟAnؼm;{kVyăݺco׭[p9J'ԤRɥ3?FZh6IHXyapI=tes?ږ$IF"FӒ| :9rf#<<\BKnx|x^[Kp|yp:@!iO?4p u?dbm6t v3`Zwڻ3)$ڵCvx^N SOCފr= f|(ɯ?\| ;6b}`@:uPv8usW_ ~n=v |<~:~ -WK`w8܄װ/'-T >~I?䱿]'XB?sX:mkPIyEt/i\xuMӸF>[1anW*V˧,n[NV<χzE6TL,#:.YwTL,\zē񨕐mkVc}S9}E 5\ݥp,RtMFݯ!,"q6J#mDDEQ %jI!"KD%cKεR%O2*,̓й7j-Ⱦ~䎰1ཇ]t6֬ `ԨQ;w.U.,, +W^E0f v;W)}\ժXصa=JJj*WVy y965}[N]a70a[3 RU7:|5h07m?oGvemyF#qf o?_OĶ5QY3V'lY\IME a2eFXkF51g$T[Wg?*6ÃBTt4 -@] 5Z^+Ruxغz?܉;w`v= x j%&t=l%`P͖*Z%K0i^fyi`:Ӫ݂ݎs0W=$=vYmrk p7vXL&;wÍ7`2`20zhB&aڣ/ٯ{{6D>}$Gr^j`C`:ŽJhtBWX.:>쁜C(j#r8p5t:jl6v9>+F?~/eR>EgA$bV9{/MUDq tߟ N DWY&?hkk޿Y)ERY׸U@KmluU2!J{NK&4:+`BW*;M⅙E|YlQCq*UN…h'- QT NW5 Z!B(8E*g ~KJh((|Wq +VJnَYhT"44@cв Fq*U@z]L5BFt2EЩ)"T:;v^|#oj_P"}˵xCFiK4ưG_K ]C[%Ar-J{xRN芛5 vRE -/J(NeZUɄgbN97vV<Թ'-߬\B|=dԫ[Ӯ {z:w  ,y:ZV1hY`8E*g ~KJhhw'-L5 /mP~3' 㫥еK2f͜ QJm_dJv%֎qۄ@|tb]S@0V1hY`8E*g ~KJh(Ukl2xc 7jIld$]?d@ʕM|EX{Z6G7&!Ϸ? tF_*51J 8/Ж@ӡC:4v;H9w ը#"2 ۓsTLЇ5?PlYL NgbEQ* &?EjӡC:O:c0`0Fj$93KʗGxX >ݻ7#1s8nү/1gmT' 443?|KV}xq +T%ONJ::tQ$AtBTt {׭C5ʷXt8aYXv5jBz7n1I&|w̞5 #*,Oܹzy{DKբСCGIaޅB`\jR.bW>1 wQzuu Ǝp^6 1118VנǭX0SEk"a452 vd:d_ oСC" IY9ض}1iȹ'Oc>4w9 Ǝ3gaYh۩'0| i-슫׮kX@po>@CzСC{*҅G@Hv2{nf:iZ`4 leVhѦ3n_U16i+?l_Z}M)O ݽ 5:vs'@cв Fq*U@z]QPqYT#.<7}yhxbJW!7CQj=J5AC R:As]b#4%]ޖ돿_ zwh@me6SYrⷤ늎IBʟ;; Ԛ~:tСآl%mS"{tСCabi̡,ʂ%&s({Hk|p#O //J(NeCߒ6Z@+:=k ^ ǿ+!( (nB+, vz]hobbKRh -Ѕ'~k]EdIQp{7̂A'xqHJ$|*~)x! mcњZ+/.!b@3J>BD܋>czMAA]":dT+s*N~&O`2H$'q52l FO1 -ɾ͐&O֒\I3#HKm 6SjOB–?䐨 `*Pm/yԏҤȎA0;)%8dqEAɠA\'B@$ISȓ+'/) txKDy|e3pg@a13H1<^7ni'JiG2QnBWʗ!stȑ99HȵJ2)/۶2JhO}49piLҳc5Ck+IT,H&4LYQ Ɓ83RC ٌPXa:7V=Ў)y-IT#6.neeLJPAaN5#_q%>1ЉBɓ;o@x%TObO I5|g<|LIru9I-%'z2rj :N', V+FjɇnEHHUٶ }T)|mv~6!fe^P`C218TV&u,BO`60mPcOz,77#J=8x'S7A"#IBj VYV J- 1An4"$$);櫩 I⚳3*꫑Q/_멊VIˑ6|9Rp3ɑ:qD;J>qh @ ډ OJKY$#MԔQ(ϣFT99]mJ볩J7k>8Ms+mZ*+tv3E\33QUfNfTE[n 52b63fyN{aU8闎;Y(UeἘ|Ц=a[qx&w׀'TX,u+II0MZ۽ѱqY 'NCeJj%&tJT#%VI­ ΰIM·$sɒ:y.âVnBqDrN@d1|)!y?H^ IDAT f/=-+˱Z2<~Q's M2IG6g25}90zb͛(;FDQx~iXpV:p1+#=wlxACI1ǎl2# U>q*ġ8v*{A |+kX8MF,z1Q!h@C]\v׮]E K~dF:uqߓZ*L& Ν#)HF:6QdLIBYt)~uFԹQ [$}/m 7(Ѫ[Jx M ZX9CƠ,qg>,T5NP't|qoD\R2JfwBO/&(!-ƎJ5_ܐmv;^zTS_kթ3OqWv]xusw:uqITREl[/, jEDD$ԫOPe[PjF'5H,0Uk ik4m`ѷ۱j6o8jýcuȭ\Ԩnvv6""#a0 *yd2!<,Yْ6׉\.)Bӧѣg{ԬUJDŽnB呟բxXrsP\Yߺ! Vv(mD ųjIH$HDA$TEs"tY=~JzP v2HNL `Jc5ɸELL J~\9Wٔ #3t`˗q =q+Vl„q8)@D91C F'.([f@r]~ ͜3>7&NK O;!C#8'mڶkpQ=&]b5#U/A>Jϖ_|J)(een*%,C!Á(W;GVV&oNFsw~%.;o 6`A(sw9Jtx^éOwAnn>1ك)o./c[e/wwdK?Q@ 1#&܈܌ qiߺmwfx8Ndedl6Ò/p!%E0dffا_xj.\2ς Yԯ jPd$bGړ/I°#0\^9s0ͶfLRBۊ7GNиax1^|X,C"NNW(QFfֻgOl | ۡaB<ԣ;۳ԯrhn믾B)l6=F !㏀WhѬ)Kvc3ѵS'4iC ⍫݅ ?'fy+]'/F0hMR.op'6pժU$n'U۶m  ׎ϫlRkL+x׎T>%U,Ch2ޑP^F4bR <2Q`~]|^}Udedf"]L8 -̯{WJL{ӆX0>f͙իVc;+#47񂰰p\>!C}b՚Δ]VI9Y-Py}N{ P.5og񃏢\ H,V8W7<WHjjAS"5L9 SE;y5>]2~>){ev+GA׬gbUE)'unb8} gazC)뙽s׆+v͚7%rjq95 t:s[.C\Zkʕ4ׄIJhq)+aw& qݯr 4l؈|jddd`SObwoDFBFF:Yaq*]iF?v {3>BZī;S)؆̏qM,_e˖cK; `n޽'YV͚1nxhذ!h?7˖t (+W>t:() EI'':g2|>!I %uǃB+qPTR(+#SZW*plV:7+cĉ㙰xOz]j._lV ;vEWXO= @},VHfXaضfZ-cMH NwedmaU"B'0mvbKpf'!pAԏ܁ґ@MZ-l6m6ja6V+,V+g[9KoWgdOyr#!-nx}z"N9o^9J U`D3 t.Vn@sl^ x2Y5f f2 |:{6F}YYY}QԟZ?ʕ*8sp<]`?&6h[ c09s@@wЧWO ovu"#B&:W=9|cFBlL,~ 9p:{҄zS[iQ|90 PRE5x"ܹ AY"Ø//<v:xcdk7c0z8rMv=%%~={㝷_b a5%KTUb=xqAeKNmq*' (Q(6eԩĘίYXK`f ͯVߟE`W_*+bYFcŪ0s7 l[-|ػw6}Χt=uJ r%iR*q’[￧Q9x٩褿Bl6!,"׮]/ߥQq:qUl6S Se}"HDr2|~%6|GF EBd%r|`&J<6$t@H֥]@mVCM/.jEbR"yZGM4yyE~^pg)p)wL>%M͘ a^yl1 "ifXE.3"9w+͘!/q8@r{z*vhڬ8JB` L5O!r<]Krk>2c$EN.r"e^iiƑp)ghRj _-[ԋ̼Û_#\+9f3x)T\i/j(gyo;"f&lBhx|Gc5(S4VZ .?-a[{ FCWj[,ػs ~}it fܾ}|?*5xF0Oxф'N"66FQ"ZBYxXe2Q1Qex]Fb8>e8!\YorG[z!lR/ϒӁ^ٳK/aw'okV%?y\N8[ヌ{BCpZ ?/|X-m6^v;l.BPreݻViZ?w?nFժ rA8:j[׮]+ڬH+Orrg/W_.YP6kO>蘜 f\^Cժt\j N^pMzxn]OCpv:rk0~ }Zq%*o& %PeDdNK䋲$d`4NL;} -\.Gfz$bcca40Éc޹¹HȿpI}1rs7`0\cmjy?֮[ܻx,ܺ߮GY'N~t€r,W.rs³!>>Ndmp$E/h+CSFU!SILSx2d }/3 YG8y(oF##NI|{G2/uBFq6rs\?kϱI1<#aC޴Sq)qA8m(,GwU!c⍒>ҍ0/z9ioLNEzz:l q}1xP6Q]Ni^Ë\Ozf~AO?[P^=]5p:vs>Q]|5/U6v E+rK,l6IfxwI^22`DO79DL HȈ[{WcBR;!<))[=<$%iC2HI-H̽j>7 ڐ%q'yLJ:l6|rE5Ɍ0DFFpWepwDfsG_]*mKD_`PAM@SU³Z0 BBB8un}_1ZmrN 䧰OVU"eAE WOZ<-_ 5N\P|dNO}OC\zJAGB#qje"rzRSI`ą .7]VZ"_n_@0 ^bы*-:d KKt뎁clp%_C19s%IGC8U6ڲQhGL!8bWr"Y:",#S++M>T!(HD%M,T}'cdiOKJc?INpSū忁R^o22(/ʢ>AQ^.Vц  'VKQ7jƉu%,!-̟rQT\dE1o5NrNG-&$j dFBD5)\<`f$V&.ɉ-VB82*Ԓ=r(GvTFzOI;12嫾]i7PC U(^U/|G J2?I'Y-s#rʼTDx@z\J&n~9%C㗮 UYO&Iކ Jd&?LydiUi ]jvF$%x|~Bb(|鲩*~S18D(^)?aP&RD$$Lؾ9|wRF3}!}9͔M|KP!\.XE.xU i=~zPftEM'e"!/!]|S&n.)?ɛ*ZG1 p"rFN">ђp**7cb!r u|#t\-(hH|S@Wة&JƧwd&wT(%HQ(Plc,Mh(d J<G7+OUTlRy&Ttf U)Kj,"&q~`*d>QDE=y_T:~:rFJmlQΒU⇾H22vO1i 2}b v;6 8vL*Zc ܰ Zg%dyG8d,%  mP}@'zqOX z|2_8!aZơ% ,ŧA @8R"ySl L I&%IX"WP {uǖ4$v/;X+8X IDATΥ$Ȉ'5`rZ ;^<,-JWDuѓgO]WIo > `3#Hi+DH&u%%p2A q*E8@HtC+&t \A[H)Q&wt$C8 M@{(r[;GԮqERry"1)GPۜl'֥tb@UD!$/3iS>IPUQH7 /E#|GN!r8 jQ$Z{4}bڴy+ .vǃmde7Z?jy I ==~Bv퐖va 8@VíV/!J$ ULB{aAhۦ w튷Oǝ۷Uؗk;0o\mlQ<~ >n]O؟J*\o4]u(T򺕪^2UV}Z+bEtG$ q9|gW8Lup`isNMt/^Mٳ @}vv31ȖKH|/|ņY=Wo!Xv-y|:w[ E {o»d'n%toN7xm۵Ý;XlϟSSH`M_v _v i fW0[-Y^BFoLʛ7Q&K2Q+3KDwҶlytEu rf5JP>l8ܷ93@^2nܸɝ/N'tWbK9w"S'B`ZѷO_ؾG~Y̌ L|}"ztÆSĉ^|7_GdB\+tn[7ob옱٣'F׮fcm߯/틟 p~ ~itCC%!#'`8}Kڶs}kۖz? |i[nEp: ĉ>ȖCamxWo~~ھk׮]xѱC 8R}PVzݺvőGŖ~@׮]m6Vݻ8|v8 h՚Gغe3=wǎmظ/^O?ݻc{Ip ѩCGpN!7.@rs%9y ''۵CzzLNױoѩS2BfT(_ƽɓ`x/cxa\r Vݺt͛ѣ[7 ;w!lt0h`[8N6&)|?ԏ/k۷ʤhy9KVz\۴kZZOoI[ʞ{>-s/E!grl%Ѿ};,_ gϞ̛7sxgwmDWhDnn.Χ1f,YY`!j׮7`С5kvޅaÞ@\q,s4HhסqEw>+VرcdW`֬Yx7uV <WI k Vw22|r4j<.͟?XnN? H`4sa7n-ZLL> ?9s i$-rˣaÆسǵRxYW_hĉۻ 6DreB<]&L… `>?:u ]d7whڴ)ϛHLLľ@={ƑҥD=ww[cyHLL͛ѴY3̛7Lr={7l EDA0Oѥkln;j[-tu ç-Zß8|-cs]SmS񒉄D(DQDA^҃,4RH4$%!R{E6ԃ_ Vц%fRtF|mΝ.N8>}"""=;(gKl I EiT Ă@γܝxSإ  1BBif-cv73%~p<;<=Y(N}s@P`ذ4l̜9 Ç߁>ЉDB8y$fΘ B{ϜwATbذ;pZ9udd$ߏ*=g3V#yQU]/`% 8{,vJ̜5ym|Ìe¾Pu cǎ5ֲ af9z'}@~93f`ǎi`0aMzt:|( 1:AgΜӧCPbԩ,4?9́RY3qI4ƌo^3{M7QSSHkG8~3e^>J%FVs}>s O̞3#úNP;x`]ve2숵J/ƝƜ"uT*O؍iL2UxWt~2n GW3ڷ-Rݿf]5miD92rhjjBHh(h+zzFQHQ\.~RWz_ }QTx'ѧOoyw}y~V)_@LV:Әer 7W/-Btt4z)c{ M5YΤ5 }hQXx1œ˞ɓ'|rl,Vv sܞ_Gp3=+k>c[p] ,@KG?=͕#DGGڵkHHH.:Yu3l\wccc8 \! k29SC )vnTv1b9#?U wl^ EV#JAZ3&@mm(hu:jjD~^xDD `RC83?[bYxؙi?-C={H$LeVSJECT ׯYlaǜ9s3EzD`%ovA@ppzoR:SpHA@MuEQ__g>HKKgnݻ!!1ϑnvȬ={Zʼ='QRDcchf!NMrɪGeUf_#ӹzuŁIGEi7:: n씜{c?)Td :kff&o ---ؾ}ݹ7(++N;T*0ظa#R)aÆhjj޽{GDtt4JʠCeVh4lܸ jiqǰg^z`;Ŷ̔fnK3Cf5֯_TjmCSs36o֘س>.YwNC\|\{7wdž 0i$ &NowӜd޽~{m(lǀif-Xn= `9fl޴ټx&kHl޴ cƌa}_~%v܉ֶV/ /4bMo֙uZ:ퟻuq /elwDnfqsp8Ƒ]Ǎ{h̰v<8mՉuEಌ7j.du-1 >S5.x2Ec¶}E sAZZ̀ŋm?G> V2y3tzd]Us?o7iJg: ]vE߾}-?r daӹl9>v{###ƾ%>ӧOcŒe9zwh 9z#F41n8L$ OXp~āzua5`-jEQvg'Lx봇Nw$m#̟{IqF~=ݷUd>^jWDV#Zo^8Πc)sz~<ճ'F8pSg.:CGNuq꽳Lp/_rR9H!VޙϮvXG<1Nq|mxCnJȧ6Oo:mSyW8o.:N7imE'!1? m_GW͑7;)8bXbye'/!Cu{0H9b&L-8s\I3?7F_T G,镙yjz">!bȫx?Uze%HHrq|u+ĹǕ'O@cC#X4ͼ_vPqqhaB!dJ_Kz=JK -޹nyCn qyHhmn<0G{ʡr^yqc:{ODmJ!!O_t?_ņEǕֳs"a""Voyn.Ra e%%C;.DEEٕg WXkO]YyGRE!.6f477#;>dbbbw\= C+0ѮXBOK#CRmHHiC'34Pd]>rެv= * |x܌cǎbРN_@9v2-o|B"Ǝ I))[_ёj7KHL w!)9 uͪ]YXRx IDT )1AA ^ALLȗ$j"K0)hlj\;,i"T*ZR FsS3jG#JV8Z.]\oes%OïRmyK-ֳ5Փ(m+׸1'txD(3^3sNYY)L9LJ Z$ZJPU?dL uS+D'M""]4{pTWW4 DDD@BR׮P/MK hɓ1;W}ٹ$cjU!/NzR2 -Y*+--Eq522҉Cu\aRJKNOKMA *?m!#-ͮ3BRI ̟/fS1|˝+WpA0Q*&R)aM z[V:%AqB$RHecKcc#.׫RE!%9 vu uPT\jTU1=@*DTHNJBhh ~eiU]S*c˄BnAYEP[WGDXTr_moqtV ѐʂv^ArÆ*"}8eXUIi3uvezb)tՠߢ{ NA@X( 5Q4=({hF~E <<H@]]=]C~n郀 FBƻW)陖gI2PN]jzfܙZdz˙Kf΁ L{r jk%=N[。礔4Ʃ߱'!a{6ʠF $_RRPo(׫jͺuݻuȑ!l̉s>>D"UD«7i#G\ ?q@ݙ^0q!'Gѕ=wwI\=f_׉0p@ͭfq2^ȁZUxyգ;Rv)_HT*ǠAO݌Ys ah k--4Q5uy|pc)4]ACܹ! ` j4PD,\?s?FqAI2A4ً6=ztG`@ExDu/MԔ^)OH^+gdr:i υ#g/5=ӦWz奥7FDF0R/Gt\2:u5Y·Yَ_rj3gb?0r`7ωm?* JNAhh(8wwlΏ}Q93OB(+)Aq5Rz'H2{nhZ5t]N$W .Jki  &sP(H$hmmEyy9~Wn`\rHhƮA^EJI)Ə뺮_~E>]ރRt>Nu}v v_{c mnڀqžrH[4Zt ׆kEŅfsۖ z $s a`5z"!*)*(96n`ϟM驩P@բu% IDAT{ qY[ׅ 9E޽s%]XP[łBtR\r-d < JV/CZtz_jоݱe\:bo Gsoz]4/>"뚜ce F JBkm@c14T-694)>!HDT۾:\GbBFju :-R) $ \nqb'|>W8sft0?ס-\={qr KJZ3o\f7-fO'#ob"?sn=u:(zTS0aD<c󚔺qc ?FRB~,X7JH>UUU;VRAVMpy;mr M%p&A8vFzuѬ⺺zAAA?~5@q eQ(4f $6$z\\yz=4 jjkQ_uK T*BCC؄6mkV@0yMEBLzftc(#Xr:eB0trtr2t:h9mL{̽s"v47FvB\\f͞ 7~vGəK);奙c6׭z\ɵerpl߾R+-t]֖q:w9_w!Up,t}x/3<˗[jhh@Dfؾc8B| 6cǏ[@w.;'OQqCg G8f4+ JαBs{m[dBP,6t cF@:m:Rt:&L!C ŸJQ*@*BӢ |gy< sSLJ-%=î:wvFX}tG!ɐa/#9q$|Ev3x u(u7oG\'\ ogy{G=.qb^Z^\rh4TW31j_~%V^ׯw[kltiZԠeee())+Wlt]vM.92 k?+>#gXCHe%\1_UL4\%1 8HlC#(,4tN]%h Q!ё8sJ- HL.DB ]P(ЀǏ#&3zfxy= JvWNh։sc56G=e|ʕLytupaa!sfKm #ٳp?`?ojDfg.1.i\(*tk7/uux_ ǟȿx~˞x\,G%D;Top{VZZPH$ :u Ŏ;W EA" * e(-Եw^ٳeeeݻW_!?SСCjCII @.cڴi.몸ZiUUUFUUڠP(lUqR.HIIϾB&;祚r(++ɓ'!Uvux.}/WmZre2+S`eEwP~cWm3,R=hY(hy8Z }AeIаNSyYyrR9n[DS^$Y@K\$a lQ(dH QhmmExX8N D ɫ ^pcϜ[ʁ';~}lN=1c6^̟лSgLZ y~3f෿ \CcGc.!3iޓq*pثy#ZR"#= y_ ~t%eਞzMlbcbQT\ V \xw~zDFFBPi \z]5i$"//2e ״L|M>gϝիWgv@ 44+WСCk UUU`0`̙hnnƚ5kJl]O<f̘+ŋ+S'[ ADD$ 0])HX?/W1M q\_k>0YLaw:E"ev㖾Ϡk?P`IhiÐRBKK >yֺ H0o< s΅^]״i .4wWW }BG}Yٺr92{,X^oיܻ\ E׫~  3HLh7|/vZ_n[bece@" @ : "ՅH8[yc$ Ht=IDb^`rHQdka~|wBa~`[ 7 .k}p!frowu19sw  <6l޸uuo0ԩ-_:z7My"a4469T*Ѣi JM%5Q(;`&VCu3@F AMu_͡i6+SBQka+|1Aʙ*ϕ0N=ư¼ y99/&<-{{'A0DgsB1;sC{T>1;ueeeH0)jt $R)rdV&w\_cGgA"?jv;GztN˃pرg C\\$ ***PWW [=ÆqݾauQfs78= b@YIrFv)d$yE(5f4:2C cQ ( 4_FFL\k?kkkp`>yl<( G E3Ę3j^ M* ;( r8mB:|3!BnV#@.?Z^EO#sg475!$$J( q;s \_2 wA&<&&.EaCѭ[&NzI·t9tf n^z=jkkT(ѴtP(EDx8 7[WT\Dפd(J_sӢV#@.Gqq1ks6 "<w5qDʿ=:ơ dpsaz`!^ͼ6fk$]nAM4jT*ηK.(+:#Gf'M0qD)ePb~hV=@ 4wDvz%ןP(PڌHAq!!PR$''c_/EnD'cy>p1||2J% EV'OׯyaP^^'ObҤI.K;_[}MUU5֮]SNAAA޽;ϟT_=tv^',~7 XضmT*K8pLnԁ+^orI;i St\ +W\TdǏ]vzʼEׯUEe׼pg7)1+sjx0x`ao駟O(:ĢQQQׯ._BdffV[[F%0!׼|8r9:u[j̍o9 3f7߬ԩS`)Xx Μ9kNWUU3g}9\vͮ.*++LXszwx;Eސjk._3CRA*"44cǎ37rV3g~s0klٳǜԩSXQL2 ,0=L1mڽ7o>]丙sΙ۹c&Nhgnc뫰p#Rr˗/cX֛6i"4gCSO=k׮a s9wޭ|(,(@TTƌJBjkj?zAaR[[SN"88\.GT*q32.555xᅕ:n<y;Q9^_1rpΜCg|1 HJJf+/_ƌBDА3?9NUUSY.QTTC8.`͛ƻ+V,ۊ?МnOЭ[&֯{Oڕu (0iBvXrRwKaB!=j{˗^pT ZK.>O<> -bxwT"FK1vs}`qw}igXaa!.qp}Mw?=,4 {Yր ³<6`C!,, )zT<zMsFTTWU#.kﯾ 6l؈'=* >z444 ,<4а0ەÅD"NT*n9ʣb#l߾8E!k(dok7x-,, 4L8)Ϝ9O>%%h4,l!!PռˣqYc⤉P(~z*Gh0t`y j&Od/aжq aG++6_^ ?<2N\~y 6λu[,6W,g0!xC7ъU{a꫽V1re{`!n[ ?t(5 /WlذoDDD_7ihh02 3:?\gݻcNsc`{x駰~:L>W6vsBT疖0!xC'1qDAAp=w#//WU=Lúu`-6륩Ih΃+,66M͠ 4 zSn';GF{̝ 7>N6M][]Eh ǣ8;H\ KGju0(عsv܁m~p[& Cgl D]f(gΏx5k&6n܈ݻw شi3q|O?C#66ְ`~}C`˖6AĠ:q;L,)+^ P[[k~;úuѳ'5]tAbb"Z-[sS߾}ZZZmۏõ}e Ν;؀ 6~vӻ{47aٓO⋯C١Bs* OG|P[>\s5Lސ|9s4MHMIa< O_+xA;|A (/}r!Lސ|GfڊGa0oLpaby(((Ĝ9a˖xAw.Ԗ-[GqEn <~wԈxxPWdHVkr'k׮O̙3hllDpp0 (TWW7DAA233AR`*Ν;#a_GBjSPbϔ?թ3~232+ ~9t  0tst:8~55RЗɷa?mfO۶{CapU8~̼#٨Gc9[HKKs[NAώ\Fha(l1bbb @QP=EQ|zW]C>󻦦ȱ[azf`6,G\|<;wʕ/8R[97!GJ%A@@݅@\^Lwڿ 'S&ass5&poq5bccѣǭp<~y֞=m9yncT\J :6!8vFB"4$ǎڿWW8d٢ğ{Z[V'~$%'CV))53ջ#ƚ/Վ.6,{ wqu`z"##P]S?޷END;\~ NT&EddK=l?v΄T !!E{CDd$g8MӨBBenL[X,sC@ wJ#BB[ 6WĄ.P*[BS]=*eR!00 ~PȨ( 4TQQMG7w;zfq6@XH$DD#H1Px~ŧ( !(0Pp) H$jk=ϡT*kS^]\|B0 dJ #HT( ހ(o/00A6@?od񽳢6˵}z@ \uTHG @\xl+27?`Wz] .ː@ :2#twr Jd. @ @$FN޹g @ k<ʕE:ACQ[nz @%y@ [ZM6)̶% @ {03̰ۖe@ =ѹ!"Wg@ 5Љ E!W@ 7O%#!@x̡k@ @ 8>t@ BG#3@ )@ 9ο @ s<БD@Qsƣ"=l|m@ ?$_9tEA b_B!u H=#xi|m'gk@ 8{,tyS]6n܈d2KXr%>}`ɒ%Y"̛7{F^T*zj$%%LjF0]1~@II h?xy}֭Ǝ$ӧ1et cƌ?klزe_54o&֭[gyk֬]|r_3W:˗//ۇ~fmH=sZ 6@"ĮgzUxt:rwٲFYE%O=ΝSNg+|jg}-™3g/cbB`ǎ6Â?r'NĀǎU lڴ 6lx񢏭:z 7oFffͰAz~ٳg7zvke%ŗliO?@V}b]vT*^VELLOb1Ν_շF`?oڴ |^nfu^֭[&MZR\`0`ڵx'pa_ذ\#N'凾ʙcc4M7O@PL6)..F=|ggbPUt:,vHeocŊf{b_OЧO|?k|?nq>RnVZҥKzfmH=6f\xpgK|m'}mS-֐z:v^w 9tV@ >xΡ:@ 7 P[l Ņ:^q2yd?1o`"R*4L¼VMY|o`Oi6˦h 4e|EiР@Ѵ1} LZcFkRM0364Jum21 v1̘iNDKAQT+h4e+ެk 7Hs|X?loMS(l# ʼ !3EѸis:3f6}XݨTչ0}Y(ź|6b.rg`N$kLdGS&`-NjnAU0m黹KYX۝8fc.ƾ7le`dXeo Xj 0eG:v(6U&-^0h^0ʥ)cj+֢Tvg"Dc-,Tǎ"&&r H$Pe^kЫg/z@4;yCe> aQ( ݢ[DtµX , lwRenfQNs";Vg"8Ν|9-O9Vj;I&Oi;z??˳xu 0oR/K#;_V߭rIcqʶ]%iyIYɃuYs}紁wCyu@ ^s1{u+}VVqBEyøp*O<~y7sWu']|Yfb7^||<ʝ1ĶeFx|egz.F;be|9S_}ݩ-b_eVVt@a3=ti8J i>s&(S㔙@ t@@ nhΜ9 ߓs4..ax~~8|.u',oe2.>['璏mz.Q]5iƧ~;w"5%WԞ:2O!{s?k@~Ν; O?aI>J|_~%5ᅬKϿ@*kD؇@ Mnn.~]$%%a?kexBb.xw/HEw':@7l@hh(YnjRMnB\ @ pMU^u:HXimmjZ|lxǐyh@ EڢVf{[k+Dy}b%x{/gݢx*:INboumks)E/ÓxY$}Oz( de@ c0@Ӵ7CH%P_ySplzL ^3gpy4AQ`vU( 8X^={[H=7`-οN^eeeuT@ 1 pN<Lf|mO\'NDB!#;:_<;J %(B"!!fFhH(SBۺ**PTTn=٢@jy9xrf! _ϥ:I\<=|ls)ΎZkWBo0ٻ(Mv !Ԁb99Z,'HؑlX.vγz* ]A4Vz!!fwf~ldmIvvgךݙgyff<<$Il1* ee8Q9ɲ > 7$tnrWQCoFC%ʅ$K((̇drRNATꟉVT^n,{ U\+[n:ǠsS=cQT=$d>Pedsm(j0ÖxS授9qtb̀v>E 7""Cg08+Y!K2*/Y Yq]Roؒ ۷ow Q]krn2iYaDnyl2kTq 􈈈NpUWIR2*C(\Ȁ^^`,x宗KgŗE$pqKo`է:.]f|̢^+=25,٫ǏlK|p;wAA^׼(Ym˵k!8wm<O_\9gjN9| 2_iZZ6~i篿rѩk7s }k^۷o+55]YQ/_#/},VbF**,@Nvrιeg EE@Sa+5k8tDDDD,?\eF^oq&""u̡( >h>6oقv pC:E1#"""5)_o-[Gݦ?Rаɕ;wEзo_|ݧ.\~1E@S:r&"""hxgh۶-^z=q/*ZnwfƁjF:Q To%]DGGYP $ ̚|jU[^L"LDDD s/ш"H6fAa[PVRRz\;Sٳg&yz'߆ɓo)`gΜ9S@5~z=}-/IK'}ާsb+Gir>qmmybԪG Pdjj_:ݻ?X)--EDD_Wnn?5MZھNDbrfTKgJDDD %Sy`Ok(V} PK疂矈NCw4&W66oxMk:_\ާ/_I{+##K:_Z`ײզ\:gv^@^w-}MϿ>>g./e>C}ԏT.u?j\SYo%ڮ$""{Q^nʼnGy6mۦYV| l:Op^?{6ɍW0w,pw,zt8} 4Sg@@6S+@V`lDEE!:*8}m6VRw:mNQS! 0!L&#VϱCNbSKA6I ̖r( I|A!]E\ RSS}B,(.)lUE-rOر#0(hnt#""" "ID}`{MŜuv:`/?#'7 >iOğMv69smk_6^KiZR6^K3T'9}VyyyE޽`)/D~g!11ɕϩQ"W Ѫ/Ʀ ka2RPMDDDT9dYFaa:vr3$I,ر3Ξ>Ty:?k^ 0#""sHtCy,y^uÁD!T `zfl 4@lf^!D*).+?n\""""dYe(&`.5CVW#!{m0XCQ`\9tzg3 u r[ pY:S~C Wrz5t󌈈&NH/D6r/@/Bdd$Ƣ9S%99""" O?YIi&5[{˵DZp۷k0PQ/_#/},VbHQ30h֬ڵo֭[!+;۶lO>W_~HIivۣYf0.ᇝHv M Z(`\&’2th0X-% &Ɗ\1dpȑZj/jÖ$""" 8NHDL=AjLJ r8tDDDDK架:Q4C^ls%""BqMe˹\BrJo8y^^3vAt&JD3KI޹s{_Hdt^luD!l›a9>|/MN~ 0s~ Ԙ$IBfVz I^_KgʡK$ _u5ZiE|~(8{N䙊\Cˆ-_`̧xA3220x`׼,XItg|M-M-]rZ5 2뛗_r׼|s֬EKdddYN=vtu9g? EP-e,f^oNv7w~DDDDUi1ÖXm6""qQ7VI, (+-AW5?"""j ]56 pt E(.*)+^nz]`+@_;?""""4XszۢB E(/@%^}N(*.T)?"""jXj"u.{Tt4$-%:>,F⚫<#8XDDPPPI\7q$\z?pՕWcmPZR2?"""S©VC'@pk%ElH*:1T}Qlܸwnl޲=~7̞S:Ef $Eqs?p#//,#/?w:DQ, aa.#""şsZ)RPyVo=K.^ÕWM 9sWXi$SO?Ōz gϞO<-Q(Q-S*U9Tdtp=b钥طw$T#Y5M^.L4~مos~UJNNƍ7ވ'NuYE: M=ꪫߟpm{_j|/-m4ڶmӧO#>>6eoEf믿O>v?O!SeW||<U7^àAqYNz~+W±cФI"p5xʯ*Nq~WCBBbccquՊT4o˗/GӦMѦM|>wDDDD؃[RSwÉ]deeQ߯9s{n;}tK.w}禆s=,Xl`SNŶm۰uVZ 6mԩSٌyĉxyNwnTUYg,Ү;`1[7zY7esϽ1w1v ; 4s#M1!`X!&& ,!:: O=$I}ee.sY~E(ŪUТE Ǻm۶gϞ CDDb"<<)Ps/u[\}͵;sY)yfxtYMOĶ-[`C[ܾm۶e^o'NΝ;ѫW/GPGDDD(LvHgPFQcN`5ˑ̾{^'?"жm D;())L&̚5 qqqGQQy|Z ࢭd4e8{,6]b򊎎B&IhѲLFj+GyW^y%k!==_~9[ee2p](*)S`ʔ)N*{1xZIDDD{mYh!~W/sCE&Í>936"?"""j<.Z"EJZ}@ixDDDD䍪5tZoo)Pt /|NDDCӨб38 5QFPW.!ڎi*:mRE7IB\|" :Ȓr%E0px_A {T~DQDмUk6 r}83dE uz:bbfDYq!e~8j/4^aaK1ED"v(3u8J jQ ! * dZy$" /?H3T JE"i2~ Ȓ6aDȈ @ bW"ՋADDDK\!)3EDs8y͆l߸ F`:HjZfPE l6!*< :H ku^scCF?uǡezQѱ[70' 8~=/,Clm=h jAʡ8qQ&)(B"AE&"5>yV8}iRn7K.Ų%ѱSg4m^؟e: Yau$u%"ňc?_^͟gO_SsʹE[#I( ? ǠTf7$ NQ! B"\(yR"rr8 5X=CW&΄&Im6GoVAA1i8r0N<%/S!ۗ& 1EQP(y6"?Z ]m+Ql0F@&Uu77;*vR5CMvIJC^AaO|v>=Xyegg#11QQ>uYg$m6@rd6a">;/B֭ѤIRT(0FڿDDD7'NK>K>jRSo> *(VԂb@wpǬك̙|:QP&KcEFDD$"""-o Z!7ʫ:%^$U %YheL7݄G@bDGGAȲZ\`Xhb5(eLDA֘Oui,fMф*Ym`,PQqǝw" Q!IdY,ːeJE`W5lD!vh<6TSDeoCnB|ifjf_6+&:(@ɲ ](E05;m-iv9{Tr|mr--.36v B!0("W4ڃ9WA1LH&31`C DDDtir-)*Dt\㳠` ˕ MRRUsA$KnpuqthN#$""" .xmzdkz^6Kq)KRT+v_><ԗ<_}۷{{/lOsE@=OO_ҵCݧDDD~vRA3N>&׃cL&7CDY+i(I6΀ƤI:a`/?%HDD50#]?a-Ruajr VF ͂]""EaK,Vj6#aZHl۶n vQ|'O(:5=ٿ} }nAPRZ Օzd MQ50DDF]E@#V\xP+P5WF :_zr0"zshAeTnrui󔐚xͩ^Q;j"\ùx9{y圈QcgMm=OmRjܶ6Mv1z֨)~z=}d9ƌ\?#OK0jLKxGwޛ͟o7^W_nõCaƽw8!/8ӏxgZ=r( DQ[7;-6,C.]i-|m+Sٽi׾=vdllw|>=xS9EDDO_{`ꑜRaK}Yv0< r2:xm>EEEu',hs/l9Uv6]3"PZjgqISZo|=O+Qk(@O/W?b SnSM_-J$DԹ Exa1oͤeeeDqqb}r5hڎ ]KzGK!"Οφ(^8|$%5N:~-((@^n4 ,u*Þ=q5",, =i;o;q>` ѯ`1x*7H krUۆukpM7cE;o۸{a2vpV}f ԺG˺՟aIxאeK|5|k?[wLU·TQ:.@1QHRqTm~Y{ߙOSx'qO;qT\|5ӎ3RF:WUڮ$"""rM1L-T?JcTt?dQ014+WTzw3'O%[و}>cA7abHr.,!";+ ,շt:b-HK // (j"lQ-`_)K~A᧟Brr2 :=E @F$dfeg${,բu ~߽WgʅKj/?C=6Sq]s?>ǣw޸HJJ No0phKiG v1{NY@`ڶm+@QOW_k5^9##4R,XIX^; P|?EmU?gjӟ>}Ir׼|s* 222*v}~]:-5]]َmo($Xq^8z-8x iich" >py ìYq ,\{/>"""r,`5mtGl6 1aP;nL6#ݻ`Qqc(|Λv9jdff:JnQTT[nܹ@\"""X֊g;?'OlrzޱϾ<@DDD{r;E( [ۮS...N?+יL&#&&糳k_5{ӧOG~ QR\>:EipDDD,Tv[7}^>[ ٌO?_|c]NqFbժ<Ӻukn 6+V|X9sFi=Qq`Ю:u*/xM8p n}cOoĉгgyt?FϞ=0j(L~'""FQCu)ͪILLt;F]N`|@~~>ӱcG|*}b .zLDDD䊊\]5Y3g=JK˰| t%E""""5iQ۰aaw:ࡇ vHUZ \ȝ//wZ(J8eAמ4dӽ?o$ 8tDDDD1ޜ`/Taz\jeLDDD`WXaK?sB :"""ǀtDDDTIB@ats=.3#"Т;g9s&N:t={v5R} IDATz 숈(`4i6;o:vM6a޼y8<ӱcG`ǠHc]fփ8:شi^|Ex}"`0| L""PSD1`ǎإ=:t#M1|ٹe 26lvȐuZGDDD \[n8t#{r3;0qDGZ}ݺHKKÆ k-VZMs:>:tFHDEE9y{(--0}ػwvXI购uDDDM|ϛZ3t>vU}ܹs1m44i/rssyyyܡCcܻmq֭[c ƍסkΎ|!qbժ.:%ןȟϛ"L;Ex~7|toӦMskҤ tۣ[n}(..[\\_ w}7W]},]¢"DGGكF C?viG#""?:7m/w{wS;v8Ɵa|7Nwyyy(**BTT|mۊ,5i֭[1vX@II#XtQ#PGESÖ#c>|85e [-.ܰa#̙֎eOODZZA@DDiQi.tpU;78v:F0NlQQѧOt 9F͛7Cҫ:"""`Sm:OJػ镘:`ȑHOOG.]jÆ]cM:_~%&M:`08ΕCg"""8?:ڻDj\5>n9r$F 袋0g΅0k׮qΕM>g"""l(~\*?)CDDTVo]-}6Qcq_~(h;j"(0D+Q]2k(L!Z@ǡڈHB!XzMSDDD4@jۻ:>CuadR(,WA. @*A ۈ8ދĊeX&VN_"oε iI@\A(}`jPmY`2zbhDe S`[=PbeXˮ[wA\&W,g,:zA 흈!PՇTm-w#""+r;獐R'Qh Sq""" %DDDDHqM`-Q- 5m/jǠmۋGƺ54z wޛ{> uWyCiM[KZ??Ɵ-Ztk49$"""5h}h3;E4,C M Dz)Csǃ<l|#֯] EQpW`܍7A$t~G@Ll,&Lv;PVV g_x_} ލ>}/1iO@vvV,]Ct5*Z[Xrҳkgd>lWf#]݄-=X]XLEֹLȒ}乷~^ ^y 0E[7olt[7bm[ѽGOuz=ݑ (**D"66cHsjC=6S LN5&Cy,b|v -)Ȳo4K:>l6튊w: ?|\ 7 >>_V))|>|^*^!+uN/rW?"Exa1]:%[Xh! .o8vMsQtϙgO_\5/_g뜵Ji Ji]s~kΟ|MWscfr:lI _y\y|U071pp*FG|$%5N=V޳g7Z_n>ȣ{ɥEgCClLDDDfΞ=oQic1jt8tzY27muϽ0Lx=ng?q5bْEnnX7t3&N,]B\kcx=9}y W\\\ߝ2ۻTmI +'"" k#"""J1#""" q5Юv={""""x>:Ws)dƌמBQ%'"";3t_]e9 WFSQc@G՞e7"""`|tiϛWADDDys]PU+{g<V^rիCy~Q.C^Ν:cNj\5ѣG,Xб?B۶mxǸ1jlꫯ>u*.2|Y\6lQQQ=t"""m7h:~SQ|c`41fh.?0z(L&1X/`0 ^5z([Q.ٌ5kba""""w5Pšcؽ{7ztFΝ( JK1qdGz\ƊEEQQ(,,t+).FTdcs_Ӥ$m?W\7lذ}zFĆ#"{`v$9-SI@F%Klƒe0bǺ8,_k׬5O몖766EEP`bcccbrp^06- }Xv-҂r~"""j]QSNx7`Zѡ}{Ǻ}Vb`mxɧ\ѻwo[f^޽z9]z%XjJJJ7_2D={6 Ю];m=ڷk6mRxDDDU0m^ Ì9;w#G8-0a9 'a5喛]n?qmسw&NÇc u'M™g0qd,_ny<䓘g0eTDz4ݻcǎQ_}"`pV*j(5N(՚OݑdQj>1g:xRRh"""lVngN@!ŊbAJְZkW-s r&Iߏ9sg4y9Ѯ}EYYZmEt:^Sмek=uF^)r)v7t3bcc1qٳQ^"" wLK>Qu h t^׫fEIQ`ư%O]""k9~K͹\e"\fB@:ƍןo*ntيycODDUQq "}N&W"""" 9}6sra2c|F\^{"" E*>B$|Jp>'>%tdH׸Q 48y[#aZ>{Cbc=n=(_A◂(3ǀۏ;1p (--sZ@I""""[p\s8z`/lV+܏kA! ٹlEADDDzR8V+Z^Gc7\Z>R'5>DDD5e<EQ`1=P<\.\+CT? AQY2sg[۴O=.ӧƺ}ǟpm|{$=Ǩ#k[n; kƕF1SDՠg-ܦtӻwx׭P}3|0]e@=#cFi_DDDԸ7E ꫨV)'M#I6m] Xfƛoc]ڸ( 1qlߞSoǤ)S;( ޯrp)}8y{~O=[߆ǟxYYYtUi\;01qd|5n^SQ3xQ\\kڗH^X8֭_+SS1uC`e=hZtnއbmh׬(--űw0}Xtcݐk3kNcǎ`t /Z\c08vf> >Ng4GyÏMJ >`:vKjDDDxN0[,1|״-[Drr2gqqhݪSx}`q.+F^ @$d]CU?`ȵ_9bL&]wSѣ/..Ƹc]?Ęѣa41juWGDDD]vWް%T vHҏ5̚cٲsiڽ ~ӧObx/&>^wK&..:޽{ѭ[7ٻ;uBll,1EGGȧ{e^SZZw\8V_$""jTk-Yg@Ӥkm6n3t ]vt޽b1BFti&FysǢ8sîò+`| ~c]LL @AaQ!bbb\#l jU?a X ,[W9ðZm(*.bֻA$<0c ]'?z(RRRRQ#Gb-^.ַl-jk̳58j;vق7zV /jX׫WOl9,f ֮]^={'>>?3J˰fZt:_}`͚u-/1s3DDD$&„_| YYx()) '7/*":o;#?Vۏᢶma2T6[pQLc֮[nNCDD԰Mx$=Gqqv}v`GϽzt@UU}3 8gײFj 2111x4.GDDX"€~WԄ Xǘ&K>M"XaB,>>_V))nQh9VfO&{ob݅E"T|RT\qn;6 R( Kq>(O۱KgA(?!T{mEPr"""""Z;Ec<rvh);+o _rE1=#Im:3O(% 6lµ_~]ADQѧwomQY6E$XVX6Ȳ Qa0a0Kx l6Mhզ ;<|!yP#G?D0u:n@ѶE!US'2JJKaXЩKW@V|:p#"#" օM3c(+-FiIQ ƭ7Z""SPTXO?YCh-I_P ,<͚5Crf?Xiǀ=/%%El6 .z>^|΄(DE`4~ HYVs0e$&$R^ΠEdDDT)(/():]v6mn! E!X\ {@"$ E0L(хk/rAD4Mn> 0~ٵ -\HFyL&$ GJwQVVB\r`@ֽڶ&Gq[C( @V(^z; rI_ʳUv~Wsb7s{X4u\\p^rcTnmIu] ]swLxfZ!'E/ħF6m0yQ!,, ]M؉h<Mݦf}eYFqI St:M5w}HM{VhvdeCf9F[mV4Il kC@'Ih0iY:Պs!YCQM61+[3#gbP͍\uu'&Wztw/6oي5kᖛor׊K/haQ6l܈YO>SYP{77˫:&.T+\++8>9/wu\<^Xު"\/J-b x+rRxtU-9x‹/ġ8$XYi) ŠˑXD򙺼t urJԌ*ysY^\o~~t7߈֬_~݅Ɉ.o6nFy)~S1GᮻXݘ`!23!) L^zj 'NE4q7$UϾ=GϺ\W]GJA{sy.!<ʲWY|[x/|x?x+t"  &K/ME/]]QKR_Чo_D(,yDt!`pO [0I6]IDAT!`6!`@xX8pˡEQ`04 [ނl ໝ;61^5t҉kܮv4&&&"++| aܹcGG0[oBף 'N[o,mt]UzP*ۻئ3s|l;{N*k r*:`ҭ&mUڭ4jIjҶVSNJ5JXA@HH IL;!y~'9{r~t^gք(vΝ59a)pBɫk/t:1lCUU)۸m_e(~_fA]4Ŕ2\"_!dnfD*jmع+Q0S1\YRpk%eS}sOM! * Y ;h¤(sݕ;c9gv5U \م}ѠC(.dRDIBOVklRiSӁb̓˴O|z=4s_l,,,B!ny";vɐAK;PGGMwG3 P()FUt k!\wǬFMX+rӆ"ϸ8>'H`PX +b~QO#ʿyYk3^VU*P$rssS*UM90ĉb Q3A=o:A5.bPm8曐9;IJ @B"Ċ1|*R @Fa, v@z$m80<6@E~tq1.nSn}LC%Of]Rq }*Nu@@ jKs%;DatZxTz}^TVU˵R.%%? |~}߻@޶sr^tӺIm'}vBK3=9Xb_vٻ >=;fMO3@:QVcA,x>ǃY7phS>]C:6(p(\Q>QN})pi9t<jw/o+xKwD^Gz?(+p`˥7/];h(K*e˖PV[(+z\rpMr<3ہ C;+^|i@ ,҃'D:1!vfYֿpB$ =&U3}G؟GX6Vc`43Y_O+a2fO@03u6`řpٶ&<0ݵ*NO׉Nrg/_+8qyרaʈv^2oIc(ul+zTۼ@Paͮ]Y\%C2> % ]Ŗ h͔չΧ#&a° @pٙb8 1Ŭ~Ŧ<v#TFhCb d'ПP۔> 0uh 0x-i0 69;5gԝ5Gg ˠY٩8:I\NQnnb'Vީ2eN-[{6[qGZ,G8k.E6cT]]#yQ"7ww fqqyKiL8{aʢ4yGib3ebm|2 p0+߽҇3y)5s[T_su{4 C Y ܷ>pڈh7-ȗnj_Ḿq͞zyΦ_viN0Q~1] "oC_R3 #`vj,Ә5>k6OusSzZ4x#$v ֶLw8 E8BTTT8$qשsge1+lMuMYX9T'|bέ7n5A6?;,ޝMqҦb Ng8Vz>%NΥ1r4bC3@j)i'yG` ۷ 03 bBPIH[|Hx|܈ͤf.h=(!Q}[ݙ&4o,kZhxBh<<͓x9aYC70~b~ 湁P|_BBc/nX_->Yݢo~ZfԴa|E3zpdiu Dk+BF5c}GaRvc; <# šs-w1Ig0LYYbD)cd/$!04mt^%N@y~v4&ݷw[1S&s i=JVKqRPPp5!Ca=oԨ]NUjNhITtZyuMMQC=xJ Docb]>uV7^A"")cћU|>hx~12/h?$ȧӘ2D*Ҽ{y?m Ny6}*]%v V4h?6^\P&i4K;j"h~%% x21@0?RJĈ[$xNԡq` FlCf<Mnb^`0r DU?<`0*VԹv3wJm^+TZ:Gt~y?^-Nu>_/% /[ i|ՠCV,#Q5뎟Si38t&GHlr䰼 Y'7r!is$*.MUytE?4f*[b!>Qx.~}wܱ#z=tc` DAiǵ6\UJ^e.,,aA >axLzdCvOrϥ^{Za6Ka xwؾowj9p$u_p>,hԶ};v9}NYY*#իW4ib8q`sJߗ5k߭x^ gX4(09Kx|oo+&%'2W\iڴ)gHfeXِ s1BkGzn& =lXH CR&>OF`IR艵yQ{IR:({H.Э۷Y7*Z!uMqx9cY0g3d+@תq2Sԩy:ϻu[p Y4i+^UA/L {۾\]X)K=gmBTSdw~Sf051 ~ [|;ǦʮYHHf,10uX+qLSXk,3 C:uټ6rv(E_ִ_z zڀ{tUvmYO / MjA+YJف Ajc-B ͰsVt#ũqoK;]%LjCp)$ǯaAɣ"PP4lԨ LTVF_;K8y49y~lyb5jVyEB-++KTǷ\%fq v-p柦)eg,ԝER(A`^s-2@hUqnӢfOԡC?hթ^0/y CMU)toq0FRgvs C 0zل{2:RYzƄ}Z0Hw<: 4hJ 8;aPk0$(u1&؍YZ THt$&dg10~!`ZUn=u&LV*(j\miժjbG6BW"HX)C0OKܫTl ӨN#= 5gntt#:ݠ0x) jzJA HAkjU\T۔]#ؿY~}OcJ9ZW[OEu”F2*8x˟<o3H)$LPq3A GqAq }P4sݣw.:u؟@ (Dˤ=*(j#6yHpJP8. ,0fE#VCv(Oq6@,xMl 2,d g!j0dq\%`/&SBQͪKP#ˆ$F:ZWV14 (e̻fKj ncӆ*u:f~Ǡ?朓?UD֔ԝ@rѹ֣񥘈c<  {{{5T*ZRjϯ>%P1^^ {[lz/0[=.&@Ma` y4$2@vl( .Pgr@9I=t*hPq%`u9[l2wFQaVR Y?i0n{=g32lE*vAĻ>~\fYp pdVy;dLNaL,7W20m2kS#26S/66<U.5g&lm@%r?hJm~8lWQg4GϩY S ..NO &w%ό1= 8\|HU88e(> )^M*`1'Ʌ(:,0 5Է[@a.le:"P!~ ZW&c`6r'y䒫!= @oa1 @W~Ȥ@h00BeL1k4=5Ľ>BY !UX@׋큙X̘61ɡ-TCfɬgdvO*s V=6& mF.@t>"ehdϣS.n(ĂXIn7EKm#X7F(&% jV"Hn-bN :6^H&p(i [q#BG8~[r\a$ېvOR  1gh7q~7\#2@wݐ 1jfӇ+hd!\aG"!\BW:x(7ߠ' % J`U#Vi TF@j@g3,aM jĹk elزg~M4v:"9 .^t 5Bi0 ;@QewJ.oL p.@l!`Mg (`f9B$}zrr=ed5"5jjz,% t0(^u<N.I3hB4!PuU \3 3`f.ᅶa w`7ycWڋ}VӼcc #2?oC,NzM:z.lv { ?۴qښJJ)_ h}u_oԏn}ED 4k%5Uכk'Br|Rb8K'>&0 iPDAld s"b BhpB}dE%@ͦn ba )؊%*>@a^!:օ.,٘UCu?!;e@Z!M[{Ք۱ 52wc&͚˫Juj:4,<5???%.qt ĽIGy5oo !v{[c޻.Z˻T(|٬gΜbQ(VFa̽Hʊ_mi9}ĦAk 3 RF)YVD)k ? aokHN%[=~t7"՜=t6 h5iN[~;[xNkVU>_ne[So촏.zU-7;́,W ӲV񫋅Q2>=9O|wZPq… Ԟ]^X [;~k吥s; aȆRU)X/% 2‾:sg:,ڸ0TM> ?֠uC7}e<*#̔;qɿ%{M#G ޱP*=<,4p͈ $C H3P9_qBi}lWk4fzhN(PF~xb+0jO̞婓6Xכ+ig_^LsT}Jn+6›zPS0H (2]kگuKB2MCE1s%m}B:v Y\ԝ[ʯeXPTs (:1W#QTɫ!<K#%֥GkC]. kpX+zMiޕ_3u^/4%VxrYz]Emo ~"yKP[e|u|rg6Sx}ŹOiƴ,uT7?CKl7DX0YWó1q7j u%χwp ?3\S sɒ;oڸya<~V"e|%棼wk/B@29dӔ[d+^4`&U^u>&"a-5r{ҁ]>gt)3s&,wwE6m-S #tvÄU@pค-m=7vNcH7ab}ɀ0{mz 1|9E-my"~(wa14Cm&g&?>ZS.J}oCAɳ !'0 ;:TP> TRr,@T^ql7KR3WJeYZO[ uJ+-pD {t.ݼA>ؿCF3;e 7'mC 'ba/ƆkSۑ_a2ՖʵqXkYP=R[|QT ;g,sAYt E1c._2~ 8~ᬔQBjB-Z1h.E"7`{T{bMtؠZ~@pIW]%)5hGGG]~w !$J_ۧfZ |xsO^O Xh!7U;V^ַ.UWYE&ʑҘbDiuh zs匿F Թߏʿǹi1qN͌&7`hսCB)oQ|7-:zҽkmzܷgVrsgДs^]kd̰/kg)($]S}cۈs~sh{PݫvzͻxQ̟4cV2X A0[v{alJ;qz0o؆!뗃`tgنQߋ̷⥤BFBs{7$|Ko6/BjЍ+AZ_vٟ99Vs|x *4J-tR'?^S@$@Wߺ:iv\&)5lD[96e~ މ?{qf6dU)2|8ӿK[ξ`:Âq>E͜z?$D5ߏޑ#ȴUN&9:ŵ÷|*Ca')ƏTܿ5=7{-H(BxqTτ/TKi<թfj5`čj~#6Qݠ O~=5E]&PK`1YǙbY_NM]_:3i(P__1՝~VbǼW=|/x}ΞR= O7}f3 ,~ `ԷwnIypPZsd kM/洦p7U6niÜ+o"ڏok}|0߳˼,|B;)= &mҗ[1O!0SgU rt cGgm {x̅oyv7:Y)y{ awq=Qt.a!ɏ!s?Yr֯䍶`cv߿?W6<)I~|kXvA\H-k8M0㕕 BJUqs/7Rk+22M:`-_Z*󬨬0+RJ+N5룯.qSpCuŹ{A6&yv7{?A@O#T*9_l\r&cº0k$qV^Jh1.KeuDz|io%xRHSX͙W>Y`EsWiQ'Oܲ/Lޅ?OAIVGŖl&YZqTmܸ &\1,ܿ4qG:zҐXOg쳥HՅs'k&D#Vܓ{!N9 9φ/݋d d| "|al /8(D!^=`YS?ܼsy("/! uKږ;X7e} ݮp8 Xrm$0_7IŊk@ӿh"Ffnʌxw3y>p&cQV%;HN-q-ϱ˖\IYmqPŁf<;/ɓw `H~fVqu>^MAHMDې3&+$_ jѕ%; OO7v=tEMROW_4奵&E 1:+Kw\0ue[>_8SsEHظXDܤgui)VyFh̸PJ9{7\N,+V_G ^&L֜^+X4qvmqP@̗S^RKOXئk˚ogGgO#N>P,=sH͙ |o?|b꧿܍9rFim{-wiShvy%rw745u$;ooHG<)}76ꔖұUM8/q/9sN]K֗!)jN÷Uզٗg;-KʳVe[tjyu8fYwn|o[oǘ{)-?ao~@bk&{YuYm$9om̞Vs;y@a1oېAwA< 0#.0}J(h.[nt|CC$$8t[.wz=J"'y*MH NiMKoJ$}tg }g Bmg/>:Ɗ?c_hI֥9i ):E]=Vh ֨[\ګ}>|'{:HC1*5_uFt;{|ceEU(E{]w_uP&奼28c34 6VXus)<N1T8STo%d +'j񶟹X>ґã V>du0kwѻ.E ݊=vO7T#ê:{I_GA W볫[&yLaZO%ݷh07R itF\PS>}JTso$-ebkN EgBέ==|H{uYU^7DiE+^iSҾ{H_-&tb(:6~F}WSm&|*o>SS$'BqWR+JVkk߱mkiaz瞽taee k1Z0QR9abYY)(~@'/}taK6Zp2WZ_ Jx>]]Mq-~te 7GERg)82V|^F\l\ i-rrb˦{nM g >z[:XCʚ;["=]g׵GwoF&H;eZ 0VwX2>V`["zՈ>]qK  qх/r%ޑłcE {NٳfRSv.Fx^=%]G0N'atn ޸4smfc#m)ぜ} ћ`׉;I!xW t5 +OFNGö#tV/=iX֐/I͐B @&3t5>)h 3yc:T<-Ff֜~ޞT({9_蝿Ư}݄Va<Ư9}go#X!XY1SZ93,6T=o'}FL8w-%OF$IÈ _UUE) jֹŷN .^8V)BxRs[ҕ*#Fxu%r;pk7)M|wLl\ SWd9{aA[E梴gYގ՜d܏c>V5}m\oӁv@ڋ:+M?R?0 =ӫj`4Mg'B@l}oSavh 1X#Vo@H A 0$~2>[P+Sc o u؂2[ܽ a|/~<xx,Х_.AIlzQ{{͋G6c)SXd|@jL0(:=_PV]|صWWbykuSwU7v|e7iM{[bOڭ?}[\>Q8Y auy.w~WqNksRvQkTdS5Yd@> hֳ-X! =Jܭmiwe`Sd:~ou骲>g֊gCO @-ٖxR߀\2)14L3~B+4@O+0~;4swMxn<4=vK>^멡K/Lz @\Ϲ֪旃y_fmeDspIMSZ|%tT0&n, K3;&5]0[+];Ά[`&B_"P޾ qPy4_DQ$ݙ*Pb۵_k=݂F]c+7fI\qMcm⦄V!Na.2oף?(y}/2ʀ1$x~uS^iHUZɤڌ礭e=ӸP  www-EQچQ{c%`&1ZOɨgC.J 8y/xVpax&8Q*0#yAC|m*&.j`ٸ?ȓȤDm;Q}A#ӛJ.j$&~sB^`DxN+k|0܄5^Ͼ}ʶRA== b!gyJᭃ%rAĠsK~o:D֨۫?*m=᭸9 ZﵾMe9<{/GV&N]Q.㷣 Tak 0wwfqE@X?qfs_|ٷkIӳcd>#(s&|W|C0bȐ[r^wgpXO~P^M/K'}2w΋/4d! giSLLzgͭ0ĺ:`S,V/ ͗Lwf^LrMF}uIgW^Kk|FZp?jMzҴv I|k5 nG~Di>U_Y =p8pi=uhEdz%0Fg 3&p %L 1ҳL}=G}1faH?j!Xҝx*6}Á/|5}A{g8c[fZ{Z,ڿa ^Nӧ>&x-sW3<ɳg'+J|/dzL5w;߉Z;]04yą٣]*Ld0^OYۖt61m->4a;-?xw!^Lv?]gc,1[ ,}g56uZMvq*! ]2EKgsZIN!$0'F.YG"!imCV w쥉_ȚoUvbx=j=vƃȵbK?+Yf^C5 y^_u51$&/qRILX<֯}5D+-K=n#-hjdT t@ӏ!iwN 䌽ɦ$y=]^7FS2+Q6oxs$Oa4kWFnooM(,ɯoO2?xWKL'ܝl+v[Րx>|p}?6s0ԫ͸om>4tz%/7F̱||:NOtV'Ҥ;̿ډzdK*IӌF %SsK?H<}ٔ+Fs g Ŵdik6FL-)RȲ{;,i5?1TGv! ,6=PO(8-FvVgӰV=8d-2YѠgBE G1)v> йE B)BvvMM b,HHhdIp|ÇϟϮv06mATHl'94E4~h/80a ~AAmzcqzN_ð@]]`$zC =x 9+Ǎ;dN<٣WooKfA...>wOH0/ڲI\uC wiQK BZb1`uKu}|},%Ξ> fQpp̓m̙S;u TyTTta>uSΆꋀS0ȸjvC&;az9t48d},k\UW3ܢ|ZBx'JsYeȉ>n o0@ ɩOF11>Ȩ2uk߼y#44O,vJR.q㦋@~U+@`aϝ]]Yaɼ~zgp lǵᵫp@^uCas 8 :tB^E5ULꎠDŽfsEU*Et_F2cTVTl˙K8B`ú!]TҮ=2@xw'[h#8JUVV֨'p&/O v+i<ȍTF~"QRWUU yz t= ?_DO`jr\8ZRk6tJ`Efw\É-P"4$Y81B0||*Q <@-sF`:WTӆ˳rb! [4\ޣs/oNܾߪu먨iR_ onډnou:FCqjrhHQxEUݘ&; (yyht0^T ro4 44;F/.)zշnnn4c9 ܼug 8e&nV 5[#8M#^#J^Pz{Hur*ıJ5{VWWlpR*3ܱ ,{9 %4Wbc;|{A}OK.R^QøP4V -n(McYR NgfP,[ z#bz`:(7F}|)CTs4H]aX3z-[|B 4<~7D](*wzLb:3N#DWY aVzD EXeȹJDq2aq tR@aO0ʃ 2p'$.!$ZA|2juZ9<(eJyVaIxW.AjHRDz mT!ujAGB_Pp*x}}}}y9bwwJ&8A҇=eK:pCj |F~ΝRp(2CXώCP]AaaeuuEe8P$8X"âʪjp%B(r(, t9tVkνE>phDŽL᡼ yxz>@p9} ߳Jr"M,2.4$/Ԫyf9_}`砎hȆ-24CˣTBY4htz1F#Ds\j "$4$"2ylL[~+?_t 1 >$ WT <&V,yzn(Օ93*!-&" ͛7]t?2"dVVV߼~#>>^$g>ޫw/& IY8$//ϘnY4x Ps}z;FN8eYj9WTWҋΝ,.DpխvӦ2݅`ne ѿqqtw$1 0vc ͒ wFU$M ʨa|FzLcZͺBB{yi4'=&hEyCHtZHYi\8$r<0sjDinn }ҥKi7FIG89t H5>i0xׂw$ ϸ-6oR "9yA^ /òE|Q,^[-qLW$gtn겵EY8@uص~ߦ8 _aqgt30\|8KEXG Z]^^]nBDi;$tꠠȠԫ_{HZr3a1kPKK:V*ccD|@9&:* *{`,(2Ϧ$7ܭSKJd;v cڴ^tn޼PXZ'ͻӤqܽgwޝ7/>( ?t֮[?aaaUFq|s&Aw"ZXZP8(@Bx.9fw.'{K|5=v߾}潙7o,BF^o ԰!QTٲr*"A* ΋%`% r1&-e2 A<  6k5]ƌTRaNN\cT†]\IX iVtfURn')WtO}p{X)rr6rҎJcRΦR %hMВ?F?C4&!f4hڴ)p8!AhR'Ei:PhF?'._ $sqq1`KoN._,H_~_s//xCjd& ߾MoK.@ɳlK̋/^|(;Xh~o޹ icoݹcM>w .©Si4T**8*~b!\G#dvtp j w[#A!%(JBZv g}Pȴ`Qm QQD rT!IsA`8Н]kP fZr/^JĢDb\.a@`S4aAHW-aXn8ػ@ ?yֵNb .y֡\ةc܀K.I B,k %R53;3#CzY7cƎ5ghK-Zq?GGEy{yiZܸۓ86:u{.-,Xex(IД(f.uf9>PF>I6{PQ{=f~ 4ujpT<`f@KKD={Z6!4 7_ZvtPR']QBmID*4+D,FMw^ƘNL$- !6Hf&W ܥSGN]um!M+Z{>-[r#O=Pk;vtwwg2 /ԩSԯe|.1iwWaäIoߞ3g a!zך3/Ѣ2]8ꩀG!fIe1z FC/^ޚ9~O7)Xr]US>_&EWW4-ܞnEXϫXO4ߵINb*3˗GEGo۾cر˗-ԙ<^Ku˜D*zBnQ\?ూ ؠqń(% %θ,n}<''α͔Io KJ#>)^BJ०BNr޾'gPKR >>΁\"CC } DT,}w`^0r.xn L߱()񭟯H$+@ ӭ &nZvdbM?u:%GT~LOI.]ڶk-7 rƦ_ ^VvAh(6  7du[LK*B@\.twRtĉ 4GEFF  #H8 8# .RT\h0s^^hK\]] k`ȋtՄ0i=TҀ+L3,+*.~_ wˋf Izs 8pÆ 'sIH ޾}E ^zM66,))ࡱG&N 巬/^aig+Q7oyyxDPԒXqqQ17oǏ3.nCYQ'{^K=+kAAtU.S=TO3}NtuiԻ+Hy l5O}4" R'BLf ޲m?s}Dv9Q^ٝmhn ._wp_\^)F.mLQ*ĥB x@,AP/9uz{cCрy`KdJ' ҼtbS*\kZ[߽ZncDwJHVycE ?> 4ғO/((hʕ2hYRVKh tKNhjஂL)-Ng6MR w{VlC2X`]+#Q*ERJ7?f6'1_"O Qh jt۷Q=ZYB'3O A-ac᫄@C`wjƍψM&Nk3,OcyɜZf=XlS_|ٱ};Sȡmh%'0ukG 1 Yf?mΝ_ׯ_Nirz-@U+l:ԩ8ω9˗/ ž={޿t'6IZJC٠ڵkÆ x\U ȠJ/].&U*`+G<ϱjʺ.ᣙPZU&<o=DD 84ukoUxwçuuP'0.pܞ=BJa RP~l(_J5lXЄR 0ܼ AG`&$|X*)L| H'Bi]X0IҨ|'0=ܘighgaM V Q l;z?؜`1 k4pХKg23@A0&P~7@3 lV9mMܳƨQ7֬yڨy"yu}M۶UWbOI1, ׉i)EΎ\ez?ܰaɓA\vƍ _dIo޽l`pc֐J:{`z'h]X!T8a% ־J_@`U!?覓cD֮: ܰfb 07@|W bC ?Y@I!*r `k2\y aᾏ!e`_*b;]pA 9uV` C 9rȠA:\yreI,@!6dދ ݾIhحtYⰹ{KzX.c騿eȤ<28~ssse2'[E`ٳ .޽X!< `k׮(`vN6A`'Fæ!NC@x?~\bEӦMǎ0'/ Q^f-ZZ?g.$ J' Ha#3>0_v- iޫWnMi>Zhqj/|?g_f5LbQżAˎa>X3uFjY=qMaͩV>SyamkƒJn2f%^EWW%lW{^S>7f5YfhGpz9T]_ Y*E Qn2""9AFVhUeSӰ{zXM^ i< q0nƺ%N䍽 f!=m\ |[wJ%]H$!uE#i{<(T.WJd &Ce?1R- m& P2d$%- # әNaᲤYl0 {9! 7gWH z{ӰiGDM@EHыU 5ưie6iòe/7l<ܔ)uΜI:pzBb6mr?T_66sށTK|NNԶa+n];0&NبQ#;5Ip3O@}#u4iy'6jT}BÆ8Ԗ-;wCURnZxtxh(B>R2|eUX1KFj5X1,VO 9`|*@RuL6êyB٭aKށ҆$ +dV<*ڻXD~\j6~R}nȣ!-cNH6\&>Kpq^M4o0)(XwE곑P.BSH -/Y BJL::?z(58!jOΞ»P}:ɰR)xO]%JM#VA0DA1D 7HN31u] IװZ |@{K7fCY7P$KYį(Di|( b2b4.Q.̠שla $R&0om :nYg8q㭥T(D"hJIO^Ye#s$''899i-0AP(ӧXz O<ѣGRRpTh <71[xS~"tr ȭK,:Cbq  #,Lcua]4;RJ6PT~? $Vfb7׎x?R勵gQf̙TNJ`UE2RjWQ\ kmS䁥g5ywGF(*1= R.Xo ržA7 #nfs@G<B*::8"Vpmf.h-*:\F(eR"x㿟[_/_ `oݺcÇ~ev^X2ҍ,"3Ru f1 0"tq$ˡ(>[8 Y(T*[ BrB&3n72?y~XPTmX,ýW?+`j(\|6񧫫\[;06PAHA#e  Q, ^PRG@2|% ɤd 0  l0`:)t)e5ƎsaCW'WrJ A{Yƍ>fQ|7;;ےw ={*fĊ+V|۷odNJYN5=#SC'I|pi 2zVq✖!M|n`+8 YbŽp L{|4t SXѧp6ZS&¼R%ģ"b9σ qfK$@OQPHvmWbT/>s!J/R@nR>2&%&;~"Pf,֩S"&}ץ}aR <`<1% NA``ҚDxh7R]r`!?6UzdmD ^S!+I{0t?*rzc~i??D ,1\Q7"C+(!޸lC0Mmj c;6 @8b+^-}&hժu2p Hw=DL x:diB(QAmڬyNݟ ;vؾspmLb=X$El9IC'pDžDG).`JPQTi(##￿`{f:@v?˃ Ϭ,b1UxGǤL "؏XugŀJ L';![40t(6zj¡CڱX !Ynv*>0ZHf @oukvhg^M4>F./||=lSooou,]tyyyEEE,]&.8pP(ܴ apС!64iҀ l1!!!..Z߼O&YVCar;:1cFsmbl.p P &- 9A, GI,Up(-wΪ_Z4-y蝘› aAk NNmͳڝ2E?Z)߳w:jB1}2Z2>l1G~;^1cɴ3`*/Q(rss%,_ ,ۉ 86 A6Qu?D=ʔ&m#UV'B޼y*@+,XLhʏ.c47 rz`4XQ)0+šn7zAAD*b*`z~l y b0,sy&2&ۨ3,_TTS>.{Hϔ)Sd2lԅ`_sN,&s 9=|=V_Ҳ}XXSK1pb 3ۜL `e}Q^+Wrn  ٳg ॸ 8jaɌUTȔEa&<[1/?xժUWR#Eר L=dv+?2BNc/("JeAt8?9urJpHH$d(xl`ؒǷa,vhȶԲE3{㈧pvww;kiS9T.pbFrzFI%b ŋVBfϙ[7mwq7l;ӕ KK_? ܳKN ZΤT*ݽg/pZnk׮Ը>{'3f̘ݻwoqc *LH b PBrhO0 Qi(Lԩ j 8G`gdDh' ={>/\&ehfN(TRI--=BͰ.[Xoa+As:#hp1ouk U [ ~w)(|aT]@$L:tΪb#NYH_7%d= EX G,h_BDZ F9~9{ 宮;v8yѣGal"5P' m7wR "Z'0^Opػwo"ZNmZEG&إcggVea+zm{d ͛4xo6tPRO>|6V?[ֱَި™1xaӨa@e: >}n*uփOOTpA0`tf^.VSy8!:"2Ȃ6F P\\ Pu`b8@ n옲t/$ALD~M9 u Ϝ޶ ƍZO'x 15&ybһ;wN0ۛHpIDlٲYxǪO0:a mq9lP. (n۶-%%%f&CLs͖jp$|N}kvx{UbuSybm|HHHPȤ3O3nٺիWökb6]g{!r%R&*TGTL ,EdK. Zh0BSL"Bp,ܣRi'єx{Ao_y[f Ƃ~`~i 77[7 ڦTMPcٷs! InP2] 9%4Ai] S!9jft_c |'t G`/CPz@@IYX^ =brl݅}ؾmEo_ P=#4,e1tpp'07@"}g@}+od2]:vhIZiu}zt.Z{yy@;kհ)f߹v{*D9\91|1ܾ{:07+'"0c[)>Yd sߥkȨ(r?`3vUG{wk߹)\B/,(+i簼0;@%X6EF7Waþڿ@ZZ:hkcsr9p.Bp ,$Suhf6#5`k0\s mB!]爈:bW>>?x<`Xl zuN9 g‚nNоS`!WǹZ w7w+)ݽW>ҹƌ;;;mS|<Ei6-PRTDd !TXA\hIj?X %!;.΋J0tbAL%kh5j1ÝМAok<ՠx֠|481TD-%rHha& w@9Ob3Eovش-drHl-EwоIFE垞6lQ0MeZ=zAϷYƑ AokM? BB|mOatdÐ^isؠn:kд;љ2 ˉAO٥˹!n@,6_S%lTAqQZ{7O/fUA2l7'ez8GLfDI s[„د_N0}˖}mY)rUZPc&t1i[ЛX{3lA3@RtH!}OwH,҅:'Y|`xHc8:z z hPESg H f4GN&x#HY<e܁ɴ$602:vho[W&!`IjavfథDO7o?O ? _ABP7ax4{AYaϾ4 I*|;0dye|@<Us(Ev ȭ?/YqUd0?+Oy#PŸ[@:`R?sLL\T]| ը@Q4 08+#]I $I``Ug5LK9Ui `I,З/ZUj|yQP(\/ 0?/Wi%AA K&$p5@T});U`0\V0MQEsX/>@5*r8ըI!(["WRdw `5 lRxBU9jT>5QVF5셪/ZUO*3H@Fhi6^jTըF5QCyP]u0 [F5QjTըhT`/UW 0?@5QjTըƧa~Z>|5h@W Ӡ9yJ2+3ۧzΨըF5QjT P(=+~j>V]W zk|ܻs@b>-[ը \nKƱJNɎ; WPeZ~Q/+I٩ػw!jƠ~ZQT= (n'J3t:SR S d)0$QTT_64Vʧf×gyF 4AQJ$P*\V-g'ϚaZzZ*1C})Q m( kԔɤSC*V/4ZE-[*htF%C ۪\.!VxM'4K%RȏmRRUmRu 8j+*|UWTTZu?%z:::9 3L!Q,ԮSO*"H$h+`Ы\]\*/_SӶC uqt0Y*@kPe6TM2V5JU޾lTDZ،/So0$$~B6DB!NO~032ô/lzQaa!͖Hğ3P*l6pXKS4 iۡYJ'%6hSBXLfՔP5jXլ+mTzűTԪ`~e%<ߝkڬ^ ,*0J#f 6 $mE!h@VZ;4  58(8럊1c d % TIT؆O+uUyۗ Mv^jXê:$Z%iOyi&ytzuo?s/9ZL3KT朝څ0}]iKn[Ydl,UeTz{  +Y 5}=MJAbN\2fEƅrv ȪqoM!|7@%:`?b=׎[k7gڂ^DM?\jgy7pc97ls5iUoW5'GY:h-"j0ׯ}PgήQоik?l ;ہ#Å=h6cFd,=*!hν VyeI $),3~m*$UW VGhN=y٣~ ߟ5cFPpml>x@ӧ;p022eKUa@Ҵ[h Bv0;9nqa;mpp!O!E~cuߘFi" E\K ?Lu ps R]eWaxtNұߟ6^}6fg$hN֬;v+UZaf&VGﶤF q]t RY`$A "x=WK!Np)+0Kwh&--_ѵۭLȯe#'VNkF"L@tU.S=<*!:y}B@,{wԤT_ nmjZ6%)ԐoVtJwT֬uoRFjH? ܱ{h}_\0vWi6hZi#63mu;΅N%; hp3m4'db"&S2NX)pWf5۹{ ú{7vRJAa<*0}.AAyZ MLh!U { S۲QܿٹSsn=&捯/:ȳښ[֚۔j]E @rjsBhwggh R]eW-h 9Шu>a!p$`Ē}Sw~vK]s>béNWLyeyLћ3V6]yiɢmmVg}YIɃmN4@Tvozk\>ø ۞t]EC6{;7M7n3lTXP0ctL6e2pTE Thqm`_߼O7h!.Cú4aO5kXP55` StǃAHMKF&'Ye 7.Z:4 7RvGήǒ>~NfUU&aXX]罾0&%'vU'y߾9(ؑv.Hӭw1{cSHdyFWhN~>*.c:H[%],õvlt^{7tA7dP}k폴5 R=NݟۇJ :V"'o_f/5l=t5E LCQ&8;md9O-_:٪ cŪU *VHߞhfqPc('YeZ)J}vѫliCt<&΋622ˮ v J&:fn5`HRןaN m=4a[,u@BTbQnil`3B mB#:7:5@"id]2fh**!:^uɭB7L_Xۦ^ ʒ?_p:ebpD rS0Iܨ)S7rm;gMӄl`uZn c{5$3q,:V QdB`XuĔA@s^<.~7-IBcIɻ 'w|,,,ZRZ@] 0^833;ey%_R@T+8}iD6܏TĿT} sXeN(!kVJacQlH^ ʠ P@5 鎾N{YtqPI,ZJGtgN]w˕5萲G{.҆È&lA.4o6{˗|Er3QJa'Ku]J|D+TdX] ML_h-0xͭ *#7L஡l9!LW,q[rh׿}X۾3oRzya[Ԓ@`V5ʏ@Y#ʊqJ갹m3tg1rʘ5PcY~ Iy`p_ :INMء"+fbTw%UDZh^d_}FL~E0^zן0Z *4)sͻ_c+V<#ldQ̋z?j:e̛2ƾ[` H#+R|߽{j^:P|RXk:x:?(?g npqVLneJH=v雲f?rvՁueI EJOU斐yj$d IUPdYIDoIǎT8%?x:u ']~ozSUb3D||/dԋ5mF9[ڼy͸X^).Or7'o'I\/6^NETr[/5g᣻:?Od#₾, qa<صbqYA"oU.xrEo._(Ueros[/k[\|f o5hć։F5؈(M c"8G~TnZR셝i_.MܩSZ-$XQe*8ΊMCo8\(+bDϛچjM=9iJj52-dhμ&q/ K=m`[c.% .!FXʀ%2ˠ0bn̍iϻq>l!J/R@nǤ+)6S-p ۓ5=ƬzזrUƬ}e$Ug n[dw="6zRD[sB,FR%cUe|L@=α'sF6~#wqq7fT"6}K/?wh3GUbcGgH ߅$g j3ld"7m^TfC<ק2OkJa?GaνC qO鷹Ĺހt(\`'/n]TUK @Ԁ4R]U>`-< a܉c$7\tX_Wn-ى6?뷌5tz֢p>`©9vo4Z}R}bدˈds KCCE%O텚m <ϗ|QHn>kj=g:͝s|Ig﹵*egPݻI!j.ePN`&iDq6tY܂~%n}ѓ'$ݺ[3[`Oyab#pq#/gQӷYC,64i;ߗ?T`#KY꘦-q-a*a#7%,i7 FR ݼ vfE*WnԒwÍj GO)/jUcm9YGxL^uI:˕~>逄)mZqd{,8 JM;_'E-e!c^~':'%nC6[YJRuX0ḉ07#0zCRD[sI쑧jV2"##WyIm۹sB̌[o%\*0DR=|{hF4_y\šeG]FumhvW*LZM ƍ-G+/`F#]_oS޻;b"l8 ~F@e- %Pz"5!l[z< ^l>O^.*[iEXht@ZuXǭ;mNX4bDGs)# <(t7eK4mQ\Ю}6o0Fj2 @Ɨ*R_`0 PV)0?|Ƕ.O&YӘunIBVE򑫙SWIyqLvW]B IS 79ztzz}ƌ7lXl,]Ua0j|P(<.Ax~O9Ggn/ǧXbifa=\:o|ց{wkˎYdr6|*@0T9dÎg0̹x}V6<* `EL9P8g//|%nDk `ǎz|䲟G-8G5!QunqQD"rw[Ti䯀Tf䖗Kג[r8 J1V5+x[wآr,R}hgfU8sKe)>#W-/,zZRwb-nq[^IKAn)w#]V6,y +R%@.3lK߲.腻-.2> uB@*˖WTwY/Ncb`/quW ⒢;CP{ GyE3p@q1_.>|'U1UQ_P\?v9L41sPt` *,NĈ@Q7 ¤p ^#$x*$ֲVϨZdE#W9O*hJt7ۅMG`uXe&ςQՄDtzPf::S_t3gAj9afMg;YM[EKlbe f~0qsp 7s= [}1S(!/(vcB&- X$x$(Z +-naܮ`!b!x*ۉ8a;bE ԐI?Bڳ֛i`[Y蛃{M4HJfÄ(V,FЅbx>`Ԅ#8"?m8qP.Pɥ S@8#z97ϋ:\OqsT,JR{jE(E˒>̴kq6 FVkT*UZF*`R b QA=X Β|lgqw6 BMȜQV^n4X:(4:ܧd+G-t&`Ml$Ѝea, v@zm800j5v ]J-=I(득&p9|)8T Żv4+[,s{x98?{[ / ʖDM爜Ũ}!z`+?mQMj: :'wԆOZK^!R٬6 M%џܶPm2ʀ9T*84 -Vtx o ;GŗAXN+#ND D,'jO&ij@PJcj2V@隰n0 Dz$7g&]$@>@FZ ,ķXv(K|O?zN7!ܽEn_ 0d=8a7NZztڵ,"jxXh_S*=_%A*p nnIA/_ a|A9ϊةu C\͛ HLr T-('z8I$Cb&p~,+gd1،9lHUs}f#ܾx^=ٖӄJa S@:QH*~sɚHO'ED)sJqƵr٧Ԏ<*A$a!za:*=%qOծ]*lE"qZ17R WŽ!I /[ÖhÔΧɪ9hs ;c!1;2Γ;7)ߊ8TE(|T #H>5! ,ŧ>ߺִd\60Ͳdy> 8A`7gNmGg I粲S)N % 9W(JV'D괮 k{Pue~*: 8S|KΎo"^=W;*F^صu˕%1\mAAS(JK ZF, L5dqqWM^&%с?O>"S,X!1fhLp&VS_` ?K>BCÛsXo7v0X@鏻Xo+C0q[ A"JHP@|nFK97PJ^䔢@\3rɷc/mWT+M&\!օ +3n?ˌk/0}MqȪm`pl#Z\01C'SIFFF5s"O?X%,_, r;*]u1{N0F*:8_ Z'f+)oJ Q17oڭͥzJ 47 /^<*-YtAAA^`ή1|4bGR%K1!`Oruv&@p1SL1뙄2XτqS Va-w Z4GlR?շx$Π_ZPIItT>M;ǓKnU]d "8 (ɼUFBB:=՜C @Zy`O7^W7 Pi#f|g=Kf섿}]<>[FDt7ó<ݐ;UDJ @u;<\r[xD" K8y ҹVqgϮ}z>tӱ{AhheW r_`gW4m(sm*H+U@Qb,쀮;MXg0,LQ/ 1u3 \,qtqE u0ns_F /yc滊[n=wlKQPm-&4I7[Lm'*C=۷t/k:o0h:cW|)/x+`bw0!QPxw}QKoF}&VMڪ]y^1n=oa` X`(hPu 㕓wzVۧ B &[?!yVG(7&8gp1NCnHhDX +;h`/f͟7O.c`֬<<Pk?~dܷS[z[%ߺgw,IbƭZxdϖ͓MGۿ_vĖF ZIҐrꎘOo$g4{Yƥnu>+~ko5 N\ ,sW3ł7n΅}S'.^0#-`u@a͂WT詼a=738 40}I~0uYZ7ZnܹМ?k֬YqcZ6nl-%0k.ʿwhF?a?L, I閎,EE|L{J,ZYr-U4J 2*lbgauӵjPj TAn K7ޢ`z#y+=P(Q9ki4Nru..s6+Gj:5>=P4}J%w |vT14?LZ>ɻz3UGc:OӶOkxx}_[·~'_txTx^eg~nSX,O?ВͺMVH$?4_ ORo2-C B޸C甸QH2a6T&@JH ބ&~?ӄ!y&TJdtG%#"ԅAK֞C=>Pm J@bD[AQ;u? gR 2o UzNg(ʛ~09İͤ&  `dYٺ硉??61>8w] v Vgno6'{Ъ=Ƶ<:yW 2/Wtj^XHܹp02&T=!7 /{VX\n &S?xoKdg#sAYX֣فUn[]|eێ"`֭[ѣGVABp)hT+Q`y]1zZ؍'C:T!r*8 @b1|{u><ՠ򧏬~B*9k Jd2kժ;\,BmHve&!`cfB"q=_o7 52R6!t\y$y 1 b|ޠnvσTS5CdlۓjHgiqj(s#ϯi!CXs/WofqN\NSV P1>9^|G$(|\ YLC>-Ƒ7Op5% ڮP9]^F|@"Ep8ehӬ-߭6uRD&}{_=x顤X;$kªWxnz[NSb$5:>\)g{ Ml~!2sy Q vG C[; 742nZíEE7$39a=4kISJ ^<{m΃VPОqQ^J?'?v;%O(|&~ZdkO_;5u!_' gJcb?1U*5K3}ȩՎMMMqZUTJ ~~~ .׍+gxz$'_T(&p؟ݐ !\`~fw&@12'[g=jއ90'V2@hvȗE*; d % _2ihEP;V)WM% jJzkvh5:7XEZܶQ1P~2z(kENC_K B E[YvXsڭS ]jP}s~՗K)'$1 _?(,Z抯lu6'jiٙu(_>Τkd=>]?L `w.7d` hRiYrzފ!XrG^4G,:6SPIf s=ye=yH&iSV'X;ϋ}b*׾ؾrk^FGl2ca*\酩_,ԢƟg6lI1m-RPBicnyU%Ui\jͺ)}_kx俉{yyEFD\rui5jFw bX,&''Ta>4!\$Eps*GQr[Y of'rI^CK1ďU%&ygMIkh\š}t ubk ӀեF36k4KqI.uѫդq,GZcO%6`~K]tjI V@Řji5ZȂ"A8јvQIB`D|#bDm,hj)H2^SF"'enPka2 fbPT:j$HПm8)ބYPNK6{b/ [ Q\Ġ_vbR#BlC@{̻?zzw!c.juCDGGbHxYY~~qu`:/˪,bІ`6 еEr(C!`-&GHd0!?:ȁA ̔թ+ZFKKWA*ZHkqEK,] Z5ДR::^GNիUV5|q(PxV }R:Rv\QTKZVuR5CN֩ZLjեZ5Y dUpɟt$SѿMs8(ys򍅧T7S@;)ʳBBT)&%} |w)#؂KKs5۽6hZ#oAfoM_m⫈Ņ'W6pStޜyZ1MgO(݉%iW.>>TS8%JChB)c,fi\fH㻏2.E{=~08$DH LmİNrC-g?{}5bOH}\ D>M"0ww_+O4QFY]#1JZ8Чy:lI3zHǮ[v|ƿ<ޢ_8 WxOiu:HW;jY[P;Ye=NCn(Oq6<@&92o$ԡ>Z1(9`F vB%Q}de&JcXQF98vpV '`qv{jHUe;7yCˉ[&0S@q! F]y} J[:#+(ōa|w[=7yg^DV|WLbPTqaʠJ0OEɣpgPAB=%[E,rC4\[/BA5=BRHY2~ _D춾I(>TuΎuэgw7qCΩq 3 ,Zp{sj ]:oZŠ=sΌC|?=Bg-[M BQ7|.@m{Opjǖ=d>[9*aU Rt:,E&+ gi˶*vw1߬-}ûح\fFnK1 P[1B?m`?+0Cb&cvAJkev\>DÌ]Kq Y>a {f? wW0mE8T(o\rA]Pe>V>N]Hbvpf#ۿፉd%a-D׬]sVY<`" !8+J,#1~C*p oL-) 6LȬMw (e c"?m "9 x:+0X?%.Sh},T3~MhT2#~;Ѵ|_١?D0/@;?\vX4l+0S@q 쀐U{Oη9}y[y\S{z.<lbئX}F !=E4D2wb/7 ޻R|Tq%=GTD rrd2c8u>$*j:.<;`p=R*=o ="4O[jVkVX,!u;aU2v.Ytv1Wk=^ PpueۓAu`aƞcЍC[ȓ]Uܠ dp˃{p!‚4fKwZ#L?`jO$(EevFJ0йwJѭWl SƎX{BW׀C>LB)QBڸ+b/Y|vۼi q^g"׾ü# k[w7֭% jPoΆo^pʥ=+~Y3:WWT@*X iv-a5jzcZx?2`#BbDᓍl|A ͽV7oQ3zqWVHhHbEbTULD~LC#  its a,r0AhJx ! @TmPc5+Qb#b$f/8 uJR(P2;a>5Vۧ]CZh,`oH1މ`_~`yɽ"5_=ҢCoD!mS^t(Yf~P@9P >-NY3-|+ah|{BKN]PǷ1Uc0ȼ32yu'VvIxRb\xĖz瞼j,AHj&JdXJ`j>sx=Lh(77SYP %4~ x`d5`t6  ,=`sXT.#tKX v:y79E}W$}֝.1jf` ԇul0zozMrgȎcWNN5@Q@Qę3f,=5c 1Çw` 1&۹y^mFQp/ ځc*V$imժތ HZpjk)` b94ˮSwo9ڪנ< rTCim"n~sq1KL@]v M`D:E.CVH(M n(Xؼ>K@hS>@Uw2УС,ǒxC64{mQw ^3h$ec.EghB4!Pep g>f||3ma?4F^\Q΍2ziv[Hg hˑk㗤_zҝ"Aa3 ؞a=}zW@ vЁ7eq(Ikw(P $mzhze8;^ \bo,՘WGq<?)?`$@^Rq?\-NS~g5q*s"ՕmXs_Q9ABzۛY-.h9oXBFo~MڡQv%aI$M04} |a8 5M][\&$&\-m.#xuIw^r L"hGMbxL=Ch݁"v^sn ٩Z4K&O*l@?DiO2{}mX yCy{nyuDZg}>FSg-*+ⓓ^uZTAlYJ+xp`5PF5bk*CSVk)C uq"Ptj}.Io|ɀ&_+~hqJ=Zp>\hp49{8.0h,\7pX/=@i߀y;.n[ál>,i2~v5~^o - "§nd<\Rţ2a|6[ ۮBy 9$K/CCu%ho>o Vv2 o\7ëTGcK[56NDz) 쁚Ӹ:=;c>87ZOH!mۈ7sl^8d Q(2Gh|$ $Kz0-I~`.q_e']8ݱlkI?&.le Kڂe{P*ͽvq<*lG 8{årY>>en)PH"J )c<~>YjX!v<2 ޡqK @}DGQά;iP ucc"'#, 0b(nCNc ]ޛ~}Ԣp@iD%D 7'@8A (*}z$xаvX=٧KB(E8&=,o4Nskx/-9bf¡e4p]Lȋ(OUD{3.opCuՕ!lܣn4!>uO3/6a<4ƀGPahfߕ8)÷F^auCX3VLM ~zw2Ҵ፠o`J,CWvE]44ܤnǚZDh#F@щ})}ua􂀎VNK"/sgݞ"IZ)J\o,º9$?St抴#_=8\M[5xVmRWZ?擂O~~/X ǯgn  ۼ>w;pWZ @zua?ifiI]>wh 9g jw۵!RMYNuX $قP̳MX}%A^E5ؕ\',owT P<;uӶM$gpr]ֵ&nq 1gJ֌o mAed+}Oъа0AžE C^nF;wN-%EWfHMhEU/6o܊I_YŠZh}Dɂ6$<̈́Ko(zw}st[Z}e>ZxnŲԄV;ͬjOo .,hл7yttEZ4!L+cnW~5ep(bж2:.Ir~sJ:wNxc?4q7|Y2yV ؂5Hd@TٮE[)s/.;WDP|k)PPB#:Yx,Ln0y&>/ EY(%aUsϛLY )\s9gPߓ 瞁KgiꆿH>Y?-+$b]z3q[yQ>^0jE捙_sľ\n:𢋫Vw^~̒wf lL</X| ~89Z9^e\j \g^';g;< Ò0>OϢ )8S׵MngE{-22 ҒIe<G/P^uXoQ 0qHϖGP?UBZ$EwN|PQt٪Kj;R,P4n\w4Sբ>ԧ4ylñk$Gdz# k;Ơh{)j`܎"s$ū;bi+Ce1'aQ%ڮ>I};xtx+C)h7b`SXc(a(`}?T+ؤd#g󅿦lPyI_kS=u.1Wc\pU,M 4rHgFc[,HX\qiI_TLOyU*+|K}T/|Z,m?b/nH"U^ŧ(猏~b'uHԛ˖tEo,+|\,jBim4`3Hpgӂ{ /BlSbTP% /+1 BI{*Qxɝjk[w*ʿB1Z9U6uuEϱ*GW -&9*h]  L_U[P'uPlc ЉIu^>.o4Bs ؊v8|N=^X/֧pV<54S\eQ;c nTR>?&бyOV3ƿID][%Ɏۊ<O S/~Vw^T?u_}&4$iD{NZ gbT פ}7j >l_G!䔸STplyG#`ۢƀGx6b$<`$0y1 V>T-c xuz<;9ix K#,pRq[U/WѶvw;νp(+V(Ш g'\l10oY7{\xڇySƞϋ}K'$n/ ebQTeBI)Bi߱BL@oپcQXZ ȵ] 熾otwzUӥd tjVbGw;>kQaCGSgXړo6s,(,s[)NI>|; ^KqY%c,yιFe>j݅TKqR"l2~/&6+G0tڠh @^wyח0efsOŧƠ~uwtycM(0e̜gkFt";1fG~<<+le=k7 2xwi$@`BɅ_?ի_=bD߹4זa5?uvsO܃8ycϟ5h,xW7kVUĨ1QxU #È'%|`n߽8K y BTgP6AO&1(7ov | Qv}o@Jfװ|'3 BNbYpO.`rܱ3`ղ]V5o{[^Ymv\!3\-(j DMcH{rHfXw"g6\wP/^>5]?nKߩ|>Cr jIѐ&.<І=t[hPDY(%暉qvYH^C{p8^kW $Djѡlw4L|Z__s,* MDU>ݖioH14xh59$пdz`x73k0ٸo}#v y_%:OX5kpKE+c"ĻȥFތ. hΪJy=㞴=G!D$B#mFE$q) 3\Ci xc; {o5ju%NY>%rna0s&_$jC$,谲_8'p@mx{tb$;ad9*#n3.>ԫ+6HW47/sJ| Fāujx"UٵAB" |WKAw[ES_nd7+\ΑFjBBz41ÃX+F ~Ttnq ^3٤%4^!avNE ?f#% rRfoNL'PZzsS}&%@EV=nF(C *,.3W$bhC ༝h\C~3e2'eʍte8ijo`Qݢe>ԷD$2u0xXTݐ'TD(xD%8QC}E Lشmlߟvه872;s+Yp2Sz4UczNC0 M7; CʏD$`i?Lhazyu`chxJeRKC.U!>V#C-?eƎr~RMK'q#?˴ҫ߿_2Q>yGG7v-"e"Sz@f0i6l[IGPh5.G\fcV\`2|3>p@]cn)'ܜ\_?_{ 8j((qv+qյg m\ɿ?I O?:0(jAK/ɚL؅0R [ <~KEEz' @OG U\E #cXYv< F;X7haA*" ժR^Q#Bʁ<| gFVu84>z/]t8† W_ш]C/~9 ̐ ,qx>ݻ0mdggWV p"]/0QTà8bLW0X!J AIR FL: Mν\a:Ǡ E|ƁVb@IhC_ Ϟ>ݱK7bZO>poؖ⢢͚5 |5iYJ 8W~S';vKkc^/C&݈fA6I 4*G#ZN[}6Ǐ;s W '|-[VṞpɣsgϖ%s6o"J>yS~~([gu7wsl,222tK@&t:UTPE"sa0ĊE P?^ y8U !7(A cLgQAo@ǜ^50 FDE弨X*F5v+444_&s5~BT*Lu+h|*1 @DdtA^6~p@y0bFZP(eqS9_nL-OF Αo,{7o VSW&Vka}ā@y))>퓒dO)RiCoo/2A*% @^^[n1F}&^?  փ : v>Djy!%n?9\pg/|E (bp_] 3ֿAoȫt"E(xc砠Ț1;Eaf+@PP("۪q+`%Y>(}dǹpyw# b-@j d_٪5* 9?PaIGP'EPP⧏ j}>87wKEi>wdI.]=3Iٝu7~z4e/qu^ZR9\&̟%v$b o'K  u"3 y\(@(3敀 CPK- 1ORgIhhAuii` sJm5!1,L`zD1M8M%Q1\2\*wq"\&q!kj;kGtͱƍv4V ~vӧ:v Y6CKTm?M(+(ܥQDPVc;_x9^8s{#d >x( 7/ ZU4yГ#BF?/v}|CDx~8_??KSA6ǎۤo;hzC#>̍Rg?m߱3^<}'bV[ӻF ZŲ0#yRWZ#!@ncN:.óQ75C HI>2xXl8zyY_)PݽrdؚDOWR&@_1(%~12(k ׈(`ׂtI-]ES{]Fi2!IƔ Ft:'OĒ{΢(zA;'lC $`yNͫh1m['D3k?j ;0c!F | pUxJJs狋1MtM|bfM։1Y^pC#̺{-FII;ŭQՙRuj{ ߹{/-,B@)Ywp3=NlLdpH(CBG#Lxx_=}wwn6[yHi!7wK@XR4j îw:~^ޕsJvG++)e<ŗH9 xP`\~2|/V>E_&KxkN5H'Fb(d֞*^Zu?aDct(9G)OMc>w솕E`6<ƛԱ̘v߈ˎ8Sdq ?yuB2=}ݒ& 2c' E x~1HH>>ÇUnݺj3`UQF>Y㯿nJd]v |Ç{ݺv aqVv'Ϟ4Sg=z$y3CJ߶#ҙ? 8{b{bcǎ4G9wϛ7n <חL0DW]A%Ƭi-kN]<(> =잳80HL~tW9 0/MaZ5P  $yڹr 'H0P_QK WC1SHS|&Ab *v7eWvlialA(ȵ }ky\ni'trbiOs5WЀ5jiNXE7yϯ)-MQ*[i?;w0 _҈6T|Pn0!tHֽB!oۦ5%h.2emjS{QKׅ9K+WbDNR)>A =_SZA}0V\a}g;aZ1@5xEx;3Wuo>L׷FT$#gE|oax~2w >m@>ڱ-%SӪa?ݽzčʈ`Ft%& 5=D@8cTj$ H|@&zxDcw7/\)l4$$@Ēhϣ%V,yvMaСKxďM^([5)K;\{z_ҩkN:в4 8T 4Fu;{`Gt=VX| JRXCEd Y-NlW&u%88X.bc J([|9^aӧ0.gѣm۶uaaaѹ S e}xl׮B`rjl\TT|5,*QEاO+WbjAA,L!$44-e ^J>VP1oc|sgǞ={V$ 2MԑGzFFee޽{`ǃ~_j1Zs zQ5崴4端ھ}{@))]rfVVUUըQt*eD"## w*Gպ[ \nXppmMavv6(:@]&A? X,|r5Y$uᰣ"۴ G(una׽iW%w wmm^1ã3AP(5K`6ݸiȶDn&YƻImBxeҴݻC&O~N Ar; зj*Q[,6g޽8.L.̏|bK4J!<~?r90C=<XC0'7Ϥu ǽ{.++.Juu50暴Y *Rp^㯆 lF[QxbڵqqqӦMxdhP- Ik.\*\ R]]d)P8G@eCt8eL_>GN.##8ܒ 0E=|t4QjWH|PVSפ'0sG~S@:lxwל]v $ x:EvȸA](3~ʂGG۷ȱ{gg;~xyPYɦCb7@յD܎FN5)̕@}d6ӧ-V>^zMnjc܈'lFRh$ưm[X][<1du3wW3 %9#޽c`l6 ?ܹs0Ϟ=T܁"cru4!|þ+Wc6lv#06bीã!lVBx)ǏOJJ3gN54 \``B*e@pXXdxXVx4= 'KL&3' P鳧<>kcx\Rƪr0??YfXX87ex=J$0'6yϞyߜ|q_f**jv-=gI e`n*󓞲;$)6_eͳ{~YM -D3\fwYȨ8ARn&Z7N`j7oLM@jѣL&Ho5sEwjl8l6ڌ݌XuS6`w<Ar"<++_bϟ={ DP@/^Y5aE"(߿3g?yJi4yA[nݻwoQrc{yxxxzzPr{j`Pde}mН۷QԤֆ[gڊD*1w̃=AήpNNPPv_+ ˤ@¶Wo(S i}egdX҆^jZ=ײۙF>7 !@d=M&Х߀.Y f pFgJ(Eys{Ml&Aqo|8P㞣R 6uꁃ76:&YB0::vyQ(X,KQ Ad2`:C "6AR&H]vĉwΛ7o`"hSNn Ng0\.;p6nFWˡ6H'|"94q(ty{V̷*_O8"EC"W $;zO3̲"HaJg ynBS_ dN5^@y~hz9]@G`L)WA0pkԦ^ dQKX8rgEH;ARA4~jJbH,v8`KNd>ԱCIu+BG)M @pj`k'ZD4|7D"і-[q4ah|wG“6z3gl@\\?IwIM>M}T.2\`A߾59g5bM{)NNiN ,!G`%j*:"\ ; $tzˍe5q +]<~;4 s !T~< U',ea1! {ݰ^4sHzCQ`yKԤpCyETFM74 XVu,~iYVk Ûyo`x>LrcM Y_^i`Wƽ:41<2҇dNTT~א͛7 g3&&fǎd>4mGGEi2L`[O<0aDCpĈDӉcK z A!:@XH)2$n^wJ 84!AA7$oAPLt8ܹs' *4?wTR;7$ XG?|bp T@/šıjBnSv) . nrsМ#4[nnki0& a3mtT@zgrc̴M$eHڰ7֥u3ݺvÆ x2i?G@);sƟ#͖H!")HY7xƍDos''zÇǏJha$0k,pӧ# utrDM9NCG&Z_`6 pذaDBWuMp`{̙)Ù3g+z2<8?!s=:n8Sϟ?۫N&nR:W дP~|$H ^(+|:f\ T]2*}-Iq7ѬRVڵ_.<80k[ݼMw9\ʖ"=ǽjnWfA'˫L@Yd=n@Z#ah,m'`bH@/l5qeB|܉4n0;fmظ|&O2Ie Y8 K _pͳf6Q^-~DzY G ?sJ_C^I޼r?>駟|`qs3??6k6O >֝s/5$`0۩CU&C>xН_rJTTTա};¶}(U-NQ@cOo|݀'eh>m ZBwy[`V4!Oq:)!BCY]QbYTz܏ΰcP\ UBn#ӗ'KC\m0|# (ȦTmIַ-aFN /%]64bD\g7_GH))^rldds߷OlNeennn/p c Єݺvi&,)nve+>sLL{g'3% ;v |Q^AA^^(pwkRviaȈmph úis+lQDWX? F5շڕh]*L=n|F64 j)˟^Mw.E `ˍVp-z$RsB=wO)=0JveϺ |ֆw_3Q'%թ>wo/?}T)Tke3VWCLJ"+R1qQD.i|Z Qܱ=e h(PrUJ%2͹< ,qy$Ȟ\t+lst')A~'_:X `tP^^ް\"8M,mX wPFF(0$n,N=2TUKdRz͘TC%6_b6jP*.ׯ_Xi94?ɔˬ\C}@/=I3<@&TjsڂD4FimX x``dxϋJH= +СrV8e3#r#W١n<7/-xښG<,ZJE˒&ص{b] !Vj rZ;N 2)g *Q8!e3$A!lP]SY^p}koj5Ǵi_.-<Wu5eiPlϏ(tHZuI(aJ.((r0˴. iҡ@{Ulo-ƛsY? $; 50Gh`[Ie`oKIrg1Cœ݉X>eEKQʯ@PN)iRvBb8_BZ:W"(΂d<giVu$H@j+Ph~ŕsɝB]i^gjf a؁Pa%O3yXw!K,c;&]vpW0U[] З)JT*U~^'9CJ |)R3XB={4^ls? #d5;7hS.2Fթ^ ;۝waûS 5n`T)C ,<"GDTem>:ݾEGUߓ?_'&8ۼo۽;n#)P>O ךXS]g:L!6yz~li^Hh( E 1 l\-Fj+ߟ8سgϛcG!" O]"o 0D/n9{,|m?w#]aH2aNl=g9+޿Ng͌UbUԤ.=[:z3;nŽ<;qUx{'=ewHtSt1 z?y!AP((T/li^A&dhDfFhԱO=IMF'\Pp -k9 mBtno1ӛ 2BBΰkͅ^@\XVqh- aKD339‘oB::9AB} 8B {\VLv+Q6c%hbiCK/a5KSiχ3/6$A|(JBB    MINL6gR3Xs*[QQ(QxDPҼ4eeFDF*o***?yݾeYyY۶Mv07hr=f8H}> t<@6ɞ\`*@>4=M/g )*c7k~?6J@mnMWg-mbiCK/a5K_ƻ)L· UTVd(jw{0矒ѱٮKj+`Z`e>k>&((4/X$sFzZvV"+پC~db;BM{8::xM;f.|vs<@yS J`>_.gyM/VT!C}ydOX˦cBahMS/A=rO>ٳL&k%A"MYVVfRm*b&f[hR3(/PTzcYuv*# R*) t[wҒ*0NNN~>,6M*@0 4wD0G תgkq:gOӉBVÁȌI; icC1b5V=Vʺ?cAMA(ށ,_Cщcxbv[ L&N]q`r!iڙړXSt:]9O@ջwׯj`{4M|($(gx#lӤfx[L=[)/$ gr|P*[""#9v\,pk> [l̸<9 ]]U@_7 Z@„`' ɩez657zb܀1Lg-]\\ܣG $P$ {!$~ ǘw!hE4=" \}D2a k?-֭AVʒn4TV> =\BT[PTtƤ/H!. UYxn/U*y7lq 'v|˥ DuYxЛ_Pڪ{_9zvlKO+0nPY+?/$]00,눇wGtGP˙ٵWQj3}ˆY Np\& g:p?WWה\a˛5tjuOF/e]MTɃU'Xw d7۟n-^Ȓ °>^ǝZ &[w $?|wISlݱ ѿ&:r HuivU_]cn]#[ij en/ V}jOQuZ[nmY5$ RTܽ}'+>Ex?^/@tg7#~ש]m0 -uxݗ^Zrvݚƥ(*ٲͳ#3oSh$i>Xp,m=!bޢu>HSdGǟizaD3 `ER.+z]Г&tN!./97ߜk=JIJ%!AA*J,( rDڐ )7[@_vfS?#"k&_d7E:?;b9翚|?vV+Nt.3}wv8QGFBLL?XfM݌i֬ӳ[u'˟.$z+V7UXk IZ%mU͛跼M*L{`d TcZ#V{!8:| 9(  M >X,?a„&&&ϣF3C W֝g74Ī%紉o $4}&I>x|g^Ío-|4{٬M}E0iq¦AyP.CibZzH,\Zgو*mԬ_/VɩE"t35ZSc7Ur.-M3ʛfv<\ w:-=dƢ=&/.MqNw~)aD}tzi>Oϡ}}ܐ =56ʂ+ +j2۷n-Z? `۷;;ƌz}8ȿ*.#{;3plf}Y*JVO7 M(#@@ @UBݨx!AeT݌A7'/y1={BgG- d){nOZ0CjAPsgY{j|V]Ԯ21 ?L9~=z +xpK 1=;>g;Z$s"^_Bo^jJƻ@%2Sm BcQ$%BԏS!ѮBޑ^w K+LCa Bp<z/t}zT +Ckc08¹>ͿN? :>|kX}soȀ'ΥW9Z^T< Gf oeP7OW2l NfYZ=;j’/DlKvN#׬^,#vx%YYYf kaF^2 0> *b /ə o(yv13B @(UyܤEUj7@T-R(nnngqq1,))Zn4a^ _&I;^̓/^=ĵ_Y4s:W |TJV=,Ļ֨m73bl'\__qܝ&vpttǣGi7kX_ZAe7w3~Q~y4z1G)]f!Cؔ@o4M;5MW6JZ~bY#}05z0Jn RVi7 S@J`?9QBEB7x> :T} &߿ر=z4b(nݍ?_g0&Fsхţo7rߎk}-u_bv:ozy*qͯӐhuʾŻ$S>ՙ`avIbC]m 0óOŦ>GQqE:E6lÈWf~fF.ZfLznr )Zv!x7.vowPpa ?JusJ#+/_Fq#V)C+@cIb%cOv=cxOpz-4 K{lXZ'h6Ptf ?9ll巷vyj\ӣcͧ P?%BMzڼ_мOl翿`i ,ض}tHOtZdR3Oxc֎lA@P{R^]5xq$TJ1k BEڟ7Q r}f]Wcxzͤr8cPվ;zo\mQѽ];2z=x׌;)zjVdjVy&YHSZdn 0WU&_|0(ԔSg cW%Z^e%%rl1jr(Q0630 Q2pq)Mm8BdjXh$۞m_y_MkǢS߾Q5UI6f|3s+{nM&h=k\ N0mˡ!q}I㢵[Fd.,(4 0ya n`)5^B#`ҜGPH|!fܹwaQŽWk8Peo;༔;ܽ)[>^! 5:ޓ@=h8mW͗.}w!%/g\*ڥ˙Αfi~ 1*Q{C>s6\ J9'|vhf-]du/)((ظysP``={aR3($4A!a;3,~_,CrTt'F]Wph@9E^^w\U*ѪrᱜɬЈr`f\ٳwܾ+v᠜8j\7_.^Sxo\:@z>OtVYprߝWnfgQL{dmR7{y{YH@r{LrnF]QSnR2 `$}雿<Z|v߁oMnٰT8s{1!#2 b!G,AvN(0JqmX% :DE1GQ`!CƉq+!uPb1xk"A=99Avg #˸xi7_$r⣶,-;ΔU^^ޭJsڌVQޮUE)Ç3|(XLT-}LYяWYjPLy,k_Cc π砥?_m?+ေ뿜΁UR D tEZ.F뷪wT:S#NA.$z*C*QV-ڙF ?G*8GD (*dF ,G__J9UOppkQc_X[$>~;{͏_GQX=Ɋ/t?IRg"_'n&rOTƾ; GSiGs4+rPz"o^(. .]f>S\/ېfߘ2}Z5|b*CR"Q8g0fR2|4A1|Vn~ !PFo۶hA#4gff&qg*D/}>ωR$BI+3 1aA_7|b+lu;9lTƁ)e۱^& tSyf&B+|wsS7vʪ"J.F ߍOwUJy06}[_U$ҮqڹgnaK 3_ot"@2ՠ"*#  J Zs!X QVf_|$0@# @sNgL&lL//C JUٽӿ,QxQeQ5¦5U\uzAO dW _ 3PVQ>Rmap0Sg.|%w| abUI^*.i9 ]ߕU;^Ea *) L&VxUVu@J@!3c jvK/}Xie_OZ%wT%OK-f{+Fq7z - eoaq|]Z{'ʬ#5aث]GGkUjĥ Ǖg[;JO8MT|4 T:rȜO2eR{dH3p]:O}5^WUE!~!zq+=L%M/|>N|.L&FTkC ihVT?Em =6%=ӧlLqrW}~螑r%䌫mDm3(>`izZڑ~ly1fF"!mg6vpP8NLYX(*}N )%}?}AwbŖ'A[ko|64BT㑓İesi 0fDQ4D;( Q}BJFt7~=~?iALӦAk3A;xvdg k-/p,Tln%W/yϧl3܄ОgH V5bq=k62mɔ򴿶nz:s7G2 MyFax季>:6혵x9;وL8auoSyZVWʒnTPf0]̄/ 6LBz}53eq0ًOTgT";Ȧre|B㯠iNX7]^ti׷M03wxűFqTyOΞ84ia)Rx=zt)ksĵ\#7gD^g=kc4z=5)!PكNTtDm.UyimCq@3@jYߔF` 1I g;W<-mDl@JLnv&/{lA#%X_(i^dŇE2Wh1}Pl:֦Sw骜S˒> ZV/ Sb%ƿ9cCGrtt,..>u*H_v{s(_3ҫ` UQL azE6sQqcwNftJ #^&2)ttORG8:9s}z`{ CBʃL~@R mty8|Ŷm{͛7b21رcܹs5%(}_E/d]I.'(f.zS.}ӲR kLMˆZ k!ψH7l[zQYy篝4?OnKzyXΨ/kD oͼjȭU¨Q >l vs)3?} ` βKT+ɃJ9t|nmlSqᑢT) 3$&ҙz ;͏o߰h*SĠLol ;ϚfGܨ רo.bEM!w}k+<9#W;[4̀}jc`Wffސ( 3%y6fP, W\w̰@3 T"Jxn*‘,qLe05X,61sKvn0( X|;S3 cYC˨ S>(aχ>ѿ'ww\tQ4Osk{tׄf߳m{t5ְW 6 |̙yy[mn#si?}uuNAW)}nЪXCާGf X*m][*" Om ]_5]X*8 B\|rFQ<WT=g ѢIAq.o7<}]ݚ"܉ ]ZעtwbDc - 4CbuBaHi_8F" !ǿCBϠ6Р`#Di@ϷHi{JY/6 7,t1*z*-4ÏVY\'˗WrzoOd#3“~rbYf+%zvM aHOHӷo1D>nvt-~S ^R8rNĺT|C!R b@* l΅ͩ2ء_QQI Jv7&EUtEM=!G^ ^\9)$ܵԈA xCg Bʹ1aCcJ~*5','Ae \={0ۛye"kpu:4R5/VE d\(ȷEnm]~W_A/ ]2'ΈYՈ0|{K&ni0\4-hP*UA^nCi7DctL9~0sG~S@:lxwל]v $ x:EvȸA](3~ʂGG۷ȱ{gg[15 xɤ1DPOOB뻖L a0c\ E(t()-1...Mǚ(,*{ nVi[O@^ ulr-FTY^<㩫;ͱϝtގ:i@"WV;;~oP4~o1Ne^l.蕶 N&)- X<VWW7ex=J$0'6yϞyߜ|q_f**jv-=gI e`n*󓞲;$m=y"-DQx0a1~ 9$*Cya#;Cs=<(4W$H47,ONn?livA{y{&sg\ %50Gh*e+P,&Ғb`=4/R ^cXL`E#))ÖV1fH[gz F*1w̃=Aήp K :,m՛i%ʔlBuJ!ѴP'5|(E r@Ad1\mF0>|&́@fƅAW@[` f#$EV=2;e1_Zn.-{r l!kaeiz^^rSU Zn`3?~ymVe6a7ͳsfOЗs VXf1%R+H` ]A eAʟDSCs. }^\ߑOB]#PE~‚_6f wo E#4%N^cQt -,v=j|| iFDKL@C噙ϟCN`[I ӕp[G_7/aCX_S8~~~ w KatXu 1MYح&K`.HLk=μRK7CJ Aj+vLhR Hh(Rr[Jc(2s]'ħgz$ťGgH\̐3$HFh={C E@ʟa7XQ-0@-H/'rHmH m h8zI5Tٹ#? ۣfڀRë˨:g[w{I$x/%& $H AK\ өܰfr#bH Ѥx0b;˼I =IpAT>f#b4Lf"'A $4hӨP(!ܑ8!A nl A $H4џڨ * TZ'MY#+t_M`5p9)?ZYY'OTBhNɿ!|vQAE6aaNs>Eg?BЬ)7$L8Auu5;"2۷[.J2fTA`_laM Ohi^EEEErJrHhHL<9QTx_ hKTOj2L).~za׆WV/!į=W&EB&ŘAn2вk?ޏgٷ>%Nh%U0,hщ6mh׼OQtue5eIn$H$B4 P*t/lrY/Mk@3̚k \,=M"(^+ Ddq4Wo:߻LV+ͥiAe:A.;ǫ.LLRz+%9'2P{oenҷ\GBS[7o6ÏMZyWzrI\$1RCS_s>W¢B}t*> XOh-{ G9#,ɡUȽz[0^V{%wPPŽ=n*1C3ќv'7$^&~j4[UUUdAyC}K"޷NW*E_.XePHحkW֗DCI *s7//phlƝؽ[|/'/UG#ّs7-ãKۃ._o]*`^lBFwbԼrEI\s Q?LsOߺVqEKWOs,>܂t:=6.iOk _SaGl"js(7qΫb?C2vlFSJsW]3ڙk;]ڰtÑM $O)\[Vt7dB{t+Ջ?> |ss]t[}FZUmM_r_VXh(,ĜҒ܌%Q4:$tm9V >XATJUyE*/-nE e2#'8`e%6R9Ԕ: 7(88?sڌlIwmڑ"yYzSw#}C0z;9l1\(B2Bp] зE\7Gk0Kk6ӵ[wW52/Uṭ"s./>k ʙF=Pt |+#zA5 6^_v'&0;Nd`vV<2ΓJC~VKv[,^K:"dc^Žcﻘ)TCUh%wBjdKkěBM&vBS$H4)tBԋaCk*a2 C%Ưg~ퟓ'O0RY&-HQ5ըnI7z7맆{=15ҙD+c[]g9ԸzbP8!0IylRZ泴'∑D)3-*t{#Ӎ3Rhl}?D$H:q5zM9(B1¢5[hRt!|YQԃC= f@?!JiZu#aHZUD!BSSA:B [ِZN=X 錻LG0[>wc'bq@GUa \֫i'Fju?ovWG5OJp5ͫkGrȩnC/_hL] -V %Ī@v)4v.&۽wM.mFt-вZ6Ֆ C}_~P/ Sā"@8-;ir%iK\3/yycyr#}~;ygHm>!2?Z uNVsP62O|쭮EJEօ3ۑz|:wk1,i`e}9P3ZA{Ňv3%)Նv=bqt6,<0z?0!o~qg턴ͰtD-:(U|?O!(Et>)*Ltt1aؐvwo-k@dddIǎ9z~)) ڛo[&,i#|\\qI듬O*"%w7;BiVW5HD8ml9pk5Lh6?o7i` 4l۾FƐ;q$ ۵m3'MS@ )/a-)yK\OY+ՠQNzq-kVZxl~N&ٲxm kİ}xD\l7ƹĵHo}`ֶm_~>/wjdu]1S^ժ0wZwMՁbSy .c5ZLf'>!*+RBۤh5@F#J_O}xļ\l7F1 bQPUTR$I5v{|x̷{@)*,s/*fO{l_y39評_>Ucu@ZPP34.@NA$4!ˋ n(U*9mѽG؈Odc|\QXp#TܻÏۇNUhvkm7W85:(c.e蟇ߒzj4Uv9vdٚ{u*m؈Ew9{6CHJ#m"]"겲ܜluTL@\J;f1 ZMuQaQjkÏۇNUhvvp]a1 )2<,};)9E,aj ^Ljw>UXPA$"͜柕ίRnI4WV~~_v0>&>ޜ34}|BWB#K+]! ԗ`ɦFp6{y ܅ke֭srrLJղEL\lI ܜIl-bbج =$93wjBL&gP mtnFs@<+TO T2y|Ltt-SJR&q0Iu9Ky "HJ!SȥrGg&LR'<˳gpWQ]^ؠ\ p46(mdP&>DgFҭUKK2Z$W6I3b_z]yZQ\xT}*̙C *+2L%iښU_̉JQY^| n$+..;\ue5Cpa^يݺa#.q3E<˂ׄhM`%2]~" RdNot6p%h <>Pk|͡(--7eA^Z73#k Km+?+' osIڜenN.N{upE$avɑ68/]}%EV *X!j}uĞ]h\*$\~ig(,n4`Zh)2&:FQ2OV];пAz÷MbϨ⢢HՏ (i8șu E M=;4((H;@tn-Uai)wΕ0U؀4h`CHw#[$e}z gsFw>3н LWZR~lDdÆ]ɺt;v͙dYRx8gT5 T@bjȤR$Eo$RUrE6!l)%~|I-cgQyx>'j:O.qQw\FtawW;@0lHr\=.dNW1s_ i\D6[dӕƹm޵x@JKp+w-1Ћon_S&6Sgi_"Uw`c$W?C5Zmqư_n{! _5'APpFA6%=gaA%U^~51}Ĺs?dDv3tԔRya8Hn0t#}E=u=>>eߵw>;u3C2Ž/. }hO.yjwvP@ ZHLp 01/F@;ߡw7NLJ~<^VK&%/dWϺX4}3Ud=gb_CCC{յ{K >=ɜεϩKqP# htY[uR:zODvbW/owOk"A ybڴ1eY[TOm-qnX[ 4| u :w-wulfjҳQJǎc# xXZ_]Yy1"vz,[xb_<[};}L3]_hV&w{f@Sqf\ݭ\mG۴k/I d=gd@уGz]MT#l b/[ .T[~`*sh2;wluR~;?f \Ӝy/c_ᗿFo/KԞ_Lre{<2{ɭ,MCCK)WWx' B@ww%8{۽b_;_ߙ)LsbM+9iYۡ3V7-)̗mrh;F)=w-s_]]_|36O e' i1jj|:@a Zޘþ z?}@Z|c` Uuk?$AM;hߔ>>om=U~<އ;Yy/|}]H"ۆ򗐺ƪO; kHsr /G+J"Uٷ6ŸBpbڛ6PոW:KLs7|k NLy0C ZU, 3~4EFoJ2q;C.Uhak K}ju"xMէ4cUy n|5T4|!u6#p K|i1i_Ռn-EݕDߛ|){dKW)Zs>1 nzK}?}W͂\0OMя JW<<{a\ 7cHjDԆie̪]2i}h%K`wև@ S-,_>U'Ǽߧ;mm;{[?Q_csKgm{[~ZJMxIJh'&s $!Qӱz pb' 4>J+A8_x|RfyS 8_vIY94]wmȬDd,Atyj{ˁrɏ=A!z_[˽8]_ppN^'PlTmpϷv%ќf>CB$|n+ZNLO9 zVp>$e:AyozmG<'Pw|iLɯ?.m.G{WUw%{[bN>NL{÷{:@t`<̗vy|Y|TL~oGy~W7G_ҥ9+p栖]ա>ma@A̹7ﺝ8%KyBT\1?hz=) ڽJSUXac{GBw*= U[z,kER]YA4S rjPQ^-rǮ$fo֕(ؐ#ubBM׏#HaF81& 5 <r)`Qd # AOIU"?`ti9K=^\p!㗡i}qI"DQY^J9 7< ɵ6ݥF]cXie0jW!\f`Lr70 6'q,/[nK qEL9&I´J&1G$g0s1 y׮Z_gT_e=eڷdmZ>˯n%Y>缬 u)uu!j0OuaΧ$XlJ2Mw3Κb٦M\;t#_3/[?|3iZ[mIr>$c^~oYoIldvE_9rв1-%|P_ m2c>Hv|֟pIϝ8b2m8Ip2;˪M!k5RnOi*u2o.u2&iш\jUeZS[AOLm~!b&Q_9Ӣuk063. ²BF>7o-XyosNy Hei:6o1Z1u[$-b,5fy$A_hnnڍ\WBg&8޼eo.rl~l~Q01Sqt餴Vf=H;V5pқ ''{x֙*k1xyS"dgpa=6!+,CԯϽ6g7UKvj9{Vk8<խvlw#Yֳtޅ=`?jS;kXz .T',?}vV΋ 7^vja=O4>X`<ҙ-Ɲ0s!lb6do8DHW0+ctz3XY]7K}1X]`*|ӷ,~NJݳf8nNGr~ؕկ֏o8މ*?'+). UVUVXl,*,h0bAG1 _xod9D J=?"xE5 ęFjwQ䘢_i m Pr)߳o;3{MMM5Inذy7[K$[TK><<_8~M!I}59~M3'9Zŧ(H$lxС-c[[#l=~ݷ/H.iYKLw4X$vPu~n6"_Qv2H)3.IFɫ~/1R6vW OߟOc>|a_ZK͓f,W܄_~Ģ%3ňV0i`$AdM󣿁.kœ~w߾}8[:ZgEFS6{=[K$[TK><{ӓ勏?ӓb#׬_!;.##JwlN$SUUeI`Ac҄u]b#s_`uQdңsVjv3oWI%DPRvnحƒɖrOͼk",_y/Fuc?0` 1N3i#b msޓޏ*|n]Ҙ2 1/Fp5$u \+ƕ~?.- Mݿ!===11{?xb\L.I$TqaQTZպgƃ[4_Y7y2BC,6i{~ӡC%9/+U 1D" U:t`#HLոͰ//W!J o 7/^wu3k<:ɋ06(EQƳ]I))11^0 %ԯ_]CBC(Ç 8P"pWlEIϞճW B|fO  RSl )NӢM(]?V0EIJ?D}J5 CLmسCBG0̂?ۼ)g;wq t)󯿜"huXT&AyF.\V %"@Pf0YPJ*_ oϜI[~ĉTJ"LMicpO9*.#ʏ4S AwMK/NNIY㩓'/^06.nHmY&|͔oT& C+J@N9;6Gc$Z^oeýwy#/ΚjUk<Eh,}{r/[na_tZwR rss}V_ tbsUs/fE{^-#mX6m{ĉ];wI[YS+;hpY,l +0'gHw߯9QJSm],Gpս///P~.]*++333tکS'R/>$_rbcc322W%ȟ'*Ongvd I}x )ѓlqȿ"FSq|ܸz;Æ c9|,[jP*7R^ZQ4*/3)`A 2jHD">}i&*piL&n` -,Ca p9ڙȒ⌳۶S9?஛ kҒqm<4!s ȏӴB.:7cؠ.dA})3*~|PʀsLؠl8K}0ip7>@揶ø*,LaD@'|F{/S cݲj=}x 孶 !1 MzKkK${?08 ڻ9H :r D҅󚪪`ﲲW:T<4]x-ob( z}Tڣg22+:q\ rܹs- bl|Xp={%Ih$Oݰa׳uDOIuDOC8ޛ/_|L? bC59~{჎Vq #߈)e{WpyyyAelռ 55:;|022%¿E \`{/nl0̾} Z~gʳ"ORii%z-O%zRßI}|aSk/?r?6D\FÜRBQV\^56,"$,L%H\K"('^\9M=Q `Ќ FSmsKnג;̈́ Ѝn>`o- ^%bWAaq<6,5λo:_էW'mi[d]  3)]$lՖw9  v RU>r_mdyrܸ[sG " ͤ6s`K줝v2Suԅ ȊΦ>i@ttrsxX^^g7R^A}PA}b}b6@,Ё>HSm yg\nY``FsS7 Gg hsGL&u 娞lp)~y08ҤBmG]f~cdg;]A~/e t˘x6+'+AR:R]VRhp; ]AP(Znݷ_nJ"E\l&ί !!!~:e; 4p!!ԧ KD1ځx,{ո) SejS_ MR)IL\:Xt:L.oѲEIxE~ȥf=0z`;maz)ؠAQB!e Y4qPF_Fo:@ $()uD:֔AsoOlllFFʳ$O)l.ѓl}a=O|?<%z-Wh*K  .MVEE'ҹJY|L'IL1 PwgvΕv*@%h>|Ѐ%TlE3{tQ @{q^B;vVYuYfmޔس玝Il:v|iJ:c& UL\z#n .gyYTWD}PA}PXXGMFT1}Tj'YjtMMkW|NPv>aM3iO8VC"LMcYH"}+5dWV[SSc~!۽t䔔k>:yE cFzbT\ ]0aKbS3ՒWq#G^5+>!a5a֬yxfH۴9))R"\4h:hN ?3u3]7iy9tYn6fiGrϷO'x}wrRvvRV3z{OȩU -w;YK ;ɥf=0z彶b3m^zAԽb- v_ Jdt2,sعKx~!TNmSr^OIl;iwX soOlllFFʳ$O)l.ѓl}a=O|?<%z-Wh*YxVq-]FD1. L%5htl*e~+dfflexK/>Dn_ .5yؠڍΉ#]3Cԉd^+{=:u-۶ꂂ)&i53g9\AKTaPE$y-IQL (P9xHWpawHgDv{줤eߙܳOMz'MͽUWI$,)=`5ajjj֓P$A0mHa6{+6}>Q^yu΢ Ka'ߘ;w;ANg>@b.:XF- !@TTTNi"Uc]`_JKbZ&Nɹ1-m)oZVZJ4aW1=Wn*'LyynIRº!pdKQђ˯94|T*Фzm:VSTH]0 ʻwGR]F"s) @q"#J Ξ86CXDEth6$jي_ɓ=w[}> v:Q$ѱSggySbϞ;v&ԩS'teؠM9 094P(*tiu&̙'LMeL>"o(bK/NNIY㩓'/^06.nȑne '97҆ __+4S9Y V _fcF0sFڦIII^*˃ }SР>a3w!]ַ[v| ^:r]\T]>""Tƌ^޻6ZUv9qiahll:"D-&ܲ`U NK-#)*..2}|Hч6>~! 10{H7Vh )9-x$Ķc G.¸@q={4Rnrs^y%33c/Ǜ\x&&&ry]ػw/՘_-Óij"6lznI1=/6,j?'@<^ f1DpawHgDv{줤eߙܳOMz'MͽUq)g( PV{u p C[iMI)#Gz9./|cܡM`hlnoYUD1&_nMoJJbZ&Nɹ1-m)oZZZR[1K QD͞-`o6v,e2٥(DWugrJ|Z̔Je͟{6@ ~X֞%ŅiߡSxd$EyI$~sh%%E޻l/^ VPuu5!\.( ?ȏӴ\.J,@}CD8ut=E@rx2H2U(). 4p!!E(6|xXX8b/(J"0 ~}H2'1CqqѯI˿v-:b L-Ƿ8|BgTϫƃ,< >(1> .8(xOcQI#4bbbL;-cRO}HA@/E(RM?H8㚰A N.iDǎrOgV}@$(P7~\iYY+2\0_2Ɨ5UUZmu|vaa*vn=, M=ۜJAr (c @J$10m0|(FF%/P R嗲R TR£wRچbR"\4 718ƍ*&ܧ3JD ~8/ի֭GHhO+@C hsVlž9q{Ng&.5ـ!77gu8}pS0psN]Taa>-aySbϞ;v&Ew% VB@Ub @ RN8}3g֯0q"7U QP,(gEQ ݝ{c~7'^ɓ/Z7bH}K"gathmp LD10?3ݷ2u*ݥ F$6W<3Xm5f~v.OT.F/"9Y V _fcF0sFڦIII pܵ͒#G/poLc9s^J5v˵1ێOӽ=zޖ{n5 ?yMO5똫ݫ X._T*׮[aVwקmΉJ۠P:9&}>&'~f|K/[L=ee=&5Drܷ?=7ʇ+Q ץ˖g>>~]4k" 05,inOfZ""HJNi51>M>R͂" PrvW$}FD=c?9n-gs>5mk657ꇫVʷRSS1(@3`} `0Sܒn_ms״4!wn8p'桃:rԨW^hႁR7:4M;{S8,yBb--)nѲO 0qbNΕii&Oytp1?'NMMu~a\4h21*T*q.**ZۣWugrJ|Zg+,9%^]jֳCnn6p b7g@DDDqQщ#:uT7[$Ish.-.t>82"G!HpDJ.Q$Q/ӴB.:zȘ-'666##}YQϊyvnIذԇPO>=O+i4ǏswPo@"o7 >& } #" @9Jʲsrd"( C h4U۶ SYM ] +.]?hP:o##voT5m?0oyo36pt1c7ȹz wO 4M>thTT̜:v[׺KbnC?-W(ܹ*,L0̂?ۼ)g;wq 8}u # Q^ `\Bl_ oϜI[~ĉTeX"DX I<nŋSRVxɋ-1r݅?\'oMs~_-Óij"6lznI1=/6,j?'@">Ϝ4v˵1ێOӽ=zޖ{}:KG˗+ʵGDDj}И+{w}ƆwmG g@ Qfk!K/[L=e"Ar.[FRT\\eN;w\*-CtDw7pG0@aSF(@a8ת{Ͻh+DEҖkwzJb۱HOӶCcuym@ dQ )SI {h R{y̌ۿlosŇh/[UU^9zF_;x`q:keϚ={¸GNevZ]PP0e$F3c,Ǖ'ʽV+B\Q @ɴC7ݰ$3"y⃽VzRRҲL٧M[vӦ^pժ}8^IDg 47x>8PVms}RQ^yu΢ Ka'ߘ;w; 4dVNI@ ⢖b„srlLK4yʣۤ9 dΝ q꜑LNIϟ;'J˦+ vAcyyyWHu{EA}Piml&AD1L^\TxE G.DүK /f]R\]~y04"2E7nzVe]jx17ROx>@S!/$l볺QP㗳K:?/ ?b\@ sVFb\ E,(gE@NU6=1$4ԧ4}СJAPղ{4Ņ:v 7t:=EQ$ArTuvd;AȻHQٹsWUXOKaySbϞ;v&Ew>4'yWZܭ{P1z]ey9_  }Z6`ߤ9~ʰ0EHIk?ע"w.^zS'O^hal\܈#r C MS~V<\aĦ>g&Bwu׌E]x{%#G^5+>!a5a֬yxfH۴9))ɽzZD.=7˳,F+>>OSOlcr 32vlP""Tƌ^޻6z#zH`Qcm_ݿ}vRDudOیyQv,}դHPg]p::zeHic.#yǟa~eF5J[t ~h+DEҖkhIc^_y~Ћ~o|3^OYjI ԧ v;JNN^jSO<˯ԑꤾ}]ZI!e*AcBc*e{?iec:齝oצa(0W233n}|yΥ~hlbb;}\kֳCnnV_>uŋxuii3ž!ߵ3B ~J{Y3CԉT֓Koƍ]=lݭ ;k :yҖmjuAAI͌< d$nZ\ޭR]"/^+&Mx{<ԓ뒂4"@n]?y]!m&kgDwv䬃ߞ?AJJJZ=Դi׮}rܫZջԍ=+xVϓ?PuDOņ>~Χ_hqhH+:Tn򅶪2?*O`1`}%46n MC |t:M⿍+ڪJ!55c֍$IDGsg糍hGk3 `]wh mlܗ^"yε7ZN|z) 1γg Z0s_ oU#55q@H3_x_=rgP)eee+mB̘96! Tj'YjDMM QXax'pY *(-YV̓>\+BdFcA(EL7y:2塧җ~tV㬫O;?/@(+-i:v//]x9s^{=6& U%k͹/\y[kNy'Mȍ=+xVϓ?PuDOņ>~ΧN߸y7'쟒s'O(u+ێ H$N{^-B%6Heԗ#mOگٕ$*音 [?{>89hдLȪS[Ay{[Y)R9tW?ȑb:V؟yP}PxvQUzM齦x1ڜ->"WZȦ.j8񩷞'_/ iP d_ Ⱦtu06($rMAMJ ^g0AHc'#&tvoL֕5]46[L+ʜj=/OgO|G. 7@S0LYIk'4n@wbjC!M_f3DE$iik3?p b'_VfQg2(uTLC%!XQ @ ͒cϮ?uEME[LN?YRB*,ݝc'=w%@S &7 gJm o}rVըI"p B;x[S(ݲy+#ԁRT1eiV(GTq:so Wqx;3zp"w-\6aħИT"H LL;$!+R*^7Agc;o~3é>yJ?-7dž"(g=owXC^~3z @t}:)鈀i[}qqk-w}oW虷;k)/ὕ$M|K}b \XΆdV,4vź Zq'<(ʃ<(OG,Orsvbhh5D)cy>|"w;ނTBwϸ#"~3UK|~hc}et}JzTߗ ׭I7 (  b@WTr:q#|wusDQ$k)u]Goxʝ8/PRFP}hޢ(`FR߻w̸2[uhÀDPz0\p! 1@Vwo7cO&;=C8_:Q}:'`(T.l`2]DQ6dHdЊ='N ;y>ࢭζWlK¶<.ږoO+%[?D?ѼkAgдVexfE8 g+jItqQ$aBd уyX: Nvt) XQ*~+@2@P}:)x-?\ŋuhODyPAyPXmT(&Ca.L 1 E1:ydjԍwVx*QIq6rmehwصT SFE1 |T;+W)~ 9=l$`pB:(fhwP)T}[I3o#4K6'ﱻTzo;k,%h3iYv[}е X/O|_{ ^oS[9$9˱E$''8q<HPh:V_-ٶ ۖh[?ؖlqbq4{OSGN>Az?̾%~\:~H;_Rr6CL|K>z_ "WvpN-<;1:zs/̘p稷^_Ը;VB9StW 8g uM7# GHvLܨS̚nWxȾo.y1!&wO%Y İHk;HSnx[w{G}gp0ut`aQE1@cFR8 |Z1@H v?:# uc) ?@EjHp)TJ1@XC| b REp8dC" е8u(0: oۇ/NQr_BR1 !F6֐;bdMV$REFFzzyy (bΠ~8mݺiϣ481p~ʐg+%G[}Ŷlϫ=M? /lࣝg6":#in߸>(@ q~ه:Dbc#VRh"FXD> <m۶[m=|g+%G[}Ŷ'O-?6q>_̶=^G;?IOM궫!ÆҴ_v6mT**t(ĚG)tB ̖zڝ57F"tѣRBUVV(k͐QJ{?z PmجTV\aݧb[)cWkc,'6Ft~s|.Бb q-Pee{w-C4sj_8mH@hWhJNDQ B! Va{v6|x֤.(t`Ya(tt ڪncY9/rlծHC 6 yQQ|?Z2/֬>D ;0.r/JKRSNd4M> OE7boJR|/Xt #G/[vztFBo ΎU*#i)D2#={dDd(IEǹΙ;dE?0}W_INIvҤ :&Rתj݃ssFQ T*]$Z(ϬQYvƔ&T6}5[ .,X[<1st ѨϜ=wϞ!CpTyJ8J%4#g]vY,IG1 ɴ'_ƺisvޜ7O,^LדlOe[\|E^Tƍ܊.^X A+DAyPIn\o8Һ+1!Q*'O1]RIt}?hg.;"<}gcGg4%klb#:0)))u{2ub(_YoZHPtILbYuXZ81iYo^8k3L[6o7~|/ O "T* JEl[:`;/Yb6\qymfDx۶n~'L`Yfm $`pBia||E1/E^#F w_O ..n;wƌKVѣ CkCg (hD_:[tK_}v꣓|l/---''СCk׬!Oz&)ۓ|  CN g.l[}Ŷd/l[mFpb[OQ?aN pJA ѫ>FQ@'姾SQ^vp}+h4ཇ_ORYQil@`L0:324UR\\QVbpnQlﮝ=eh4fT"4( .olZn/.8}tϻ=m6\Sю1DX[u9sr YO+))Nd6Ch H(-=n48sxKJۡO1tPµ3<7yƧ޿oߓfu}Ez˔'fXr۩1I۶m?~ϣ޳!5=|chmےmb[Ĵ r<=-<7/l^6O1Rxa KmrcٖlOg\\6gM(xΫ?Qg~RAzs誩uM7pXDcFnyxz+y[}t3wwR˪ 7ޟ} E1tNtKĖ%fj7-7f&HT8p֭o~ ,l6|Bs&HUU&RYUQVKg͎w%w=s=+v]@r8r=Sl[rlYwnN,߿?:Oz>rQ\~a CP{1b}! sǎaرj=`0Mhs)k'ڴFQItQ2> "x&hxz=1om_Dvȃh~Rs<(@_ hī*+MTL;iI>sСk֐ssSO[yNRTŹE{҅ms#ρ 3>:ukqɌ+ss}xlQ&^y+M1|{>_I*+ʓ$YYɓY{իQcEy vC~Bֲ}^exdrzKQIYhf&1_oS/wOj?ŗ6 BE>ye*4GQ vƲ$arPNNZZZ\||ǝ:~˛9_ Og%?Y[OmywK5wQ͐dmpcomۋO}3C'ǯk??CQ Ul;Qt-Co8c+uMQ@o"{v7vݒ=~?`VbO<1~=:OSCQ_혗R'9ϭEN*V ?̽9k1IOwQ5N=P($~xO?5%;.8]7w tAlYō)G_r86c߉78²HXho ehy5<_س)&?3/>m7Y9APwPs<;1{ѡU|?qUwL}6>+`Gɾ w>_݌8C ؙW+btr9KX3Vʟ=2H#;R}vMqAR^y~NO?ٝ9m#/]1T; CK{DOK?Χ!wN9MWbZ_WW ? .oRnnm $0J\nUKo|O+a-(iw5D'H)ҲRrJsO|&*J`*骻o/X ܙ Q@shƱkY}${RJr#G< )v4,t GE'IJOI,?52ﯥɝ6^W>ZP<)sv@EQ @x<[2 ?0jr޽cƍ5U<#ۈ$f}m[)D\*kڿWMO-OSAF5Z~ˏ^8&PQws*C/ū+ѫ4$60]8Rٓ嚽Has >k~oii#e_m I3|ڈذt{0Q53 lMn8(SCnH~kZҺzm9 l#$;Lpzf՗_}ڜGNx 4лALM[vއ940E{li[屛r}f6D:NMQf[y]ǡ'7$Gro(IqLJPtNH\#G\$oee{w-Cxpm^6iNef RPh*BNږsH>[|qb?>l{׊]ޅRӗ읾LY?ɟI͑3H0 K tRyCIB! V1m {v6|B֤.(t`Y=f#rR*n!<7D&rǾVHL!@$DQ j,Cљ:]8 /GVfŚZdf; U&V-h7˴}mTç@#.oJ&Xt #G/[vztJUbY$Z;Zq edHY_WE,Y IE|=wΜ!C.zJrJʵ&EX)*ZQYvƔ&T6ܺ{r֬nݻ/XH`Ѣ[oW:vrrr:vtpnaʽ6SZ b@#I]p̬,rɬ޽{許naJ?'&mC X=}x||DtPNNZZZ\||ǝ:~=OI v߹  bt111eGo00Ct|B+~=KeE>~PJ/@m@R,CS%ee,J+m ,b+, ga"+<6FQ :q+@h`<@f4jUk Rt h $Lk2*Bb]E)@bZRuZm3߆  ddʽ[F\'*ʖLr$U f/|.Бb AE!WQVw24gp` CW r b&&\hyzZy5{a&yzN+Tq3B! ViMj-=lpIÅI]/Oc6|e | E黧i27Y'Ǎ&3?zPWd>X%IezRZ3D3`1[Ӆ (Zݿkz"wp) Lf&a tFa41NuŒ2|(E(;ԿR"J^!lG,_{>锪0$Z;ZO 1`^ wYs2d>}Ϋ$\;iRdKU ڮPA@9 Θxuf[Wx}OΚխ{ bb,Zt-S9cULh@li IZV~7mW~,e\7#7z=yj0[5eWd=?؊>X(gsqEf?s>3MEv?:/^}glC4ä5kV%\\($-3=W>HQjӆF,볉ԆWd%?=1rlss*ȕn\\aǴXQ"e@y-2,ɓ'zիW$0*i#hUji $R)%Kp}DŎ.QM/~?w:yNO_ :y+47֗SǏry3hp˵A99iiiq7'I {~[ORKkW111eGo0y-7x^{&>_Вy,//؉-Hڷ)F|BB%: ehJRd-?tAY~EorRGOk˛(ei~p 0C|>.NdqfZ-fTUm6"*R9ѵ\VYQh-[j4(PЉX R3'uZ.9y/[NM.R18j맊K J$$FݴqUW_J35ߒ۵mۤ.xE"$%SeFG2+/4Z}\&6T.g*SAaRS&nW65=-U6-ˤ2}?^}T`ZN=Շ^][uCwE&He(ov\BⱟV|:VODp2 9*"y(+ۻkgnM\ MdRJ<ϑ{UQD9 C_ vUG_ٿ6Oي6]T Q?nh2zvJR!^aJZM]mZLUJ]߲=B"NB! Va{v6|B֤.(t`Y=f(Mr\bt@9'O1XgŻ˿Ϸ?unS+9vfM[7W[-;uRjׄ.ɌӺ R&cZ!KeY"eVIRRHw('׏א33{kuA|?Z2/֬>D ;~S*TZM}XB?4pLjvFxۭC=] K=gդp3;|僕G<6H(䔸lBѣw?Z@.HTm׉6\,N3/ѬCTS*_òKH@69|ٲFSTmrv- v҂@sgT%#g9\vyrn.̰Tݝe2Y>~:DAR)HPF9V!*9 Θxuf[Wx}OΚխ{ bb,Zt-S9cUnDHܾ%6D-57'uvӤԮJFB@*#WTxK/2"#chr95Ur0V鵚 g~RAzsX(.bA~Ͷ!x׹jkQ=ʳRW]33RkFZRٶ[8v.NyؒF,Uiu?&~N pZm̙'<]L^Mx#|AGBVh&PybLZQD$؜'MS͟8C{Fk[d2mټ966v! @4 G ̦*6PT݃y|1$pf_mVrφL*XӐ%fj7-7f&HT mozem6y@ G @ѴlJROu]w 0g^ .p 4DyBv& CpYR{1b}! sǎaرj=`0Ϥ8ۮh'zF`(u}< \-bM0@E^+yg !r&]WG_?$KKK9t5kC)O=Zt\ bcUJøf'nχ-ydRmجT]"vM*y;əiH JZpwwew6$F<_Sx\&]YQ%9 EʲX,'OݻW^+ݫR\ hULfsCG5ĵ1ӊvgYmT[2K:QeQvpM?Sme6˲OHZ5(''---.>~N?k F; :UB훭Rۘ5 t`YlRku&ckTE]D xjza "s cIu/k111eGo00C=,//B*%bc33{ku39W>HCmy惻k<5xWд:]fV#=Oͦ*mL%JCbnRߗ릈m <@$B[\@lw+24UR\\QVbp #2k%`B`C{< T.^XVTYRt0u+SC<>ANR0piQ"mB$f;%e(Q0vMJ\,HHMdPL DLZ;#t*J=:Ό!~% R3 n;hyIiq bt4 6'nkh_'YFz<*^]sN,.ȏET]Kg NZ}€~}ea+€A PN/sRR_ut!KlZ?jyO9H)c2ׇ0_i\T$rpur~MKINNJl 8TVS߈J# &5ӱ> 3{m7E66b]W5NєuDB㴚=]~L喪JVf*LNK$fsجbJk5 bY@Xevqή.ϗ[r;]NGQxF1@t{L[~/^ӱk'>:ɵ?OçT#q F9UwMc6UMJYTRzBZJ2 |V¯G^R)).v:9՜Eir$kzrj׋qD} ؛NM!Ь e@L.: e6P)i}J>8dBA )m:vV&K csT*IeR=.6VI!:]"_.@"Vf:Vk5fh2UTv8e2N;+uxiZQ TLf gr E5)dD`.et^TZVR*3{vtY8c u i[H8gګRtNdZ]]ZV5 ;D81fHyb[ pWlKy4XVVvԩ㯺TxlƳA8m^4/l^6O1Rxa }>b%hs? l\O*ۨRSGx^"g[]mZmT"~>rR.WUU.%\ ‹e2yNr4#6@*@ivƙ#]:P&0L ZM<ǵ=SNpXE!Gz\NH|>6vq;nܷ7fTH 6+5݈tB w;7W |UIT͞ɧ[8phc$[V+AS\JJYkϜs:ϜLڵ%Tgj?bQmJ" ҙfJ0 ߒS0Yժh"V zЁuJQտr;3o6>O  Yl4/{o^ӓǫ?;畴4tt Α Vj=1oj1S8,o@cf  oMS8ΏV^ycyD.h6UWpgbDuhjݰ;'H_"ZmJe̬.4-QNsL&d;zxF~_xU{$ Dwy㎝-SnHtxuoL}-á;,GųGˤϷk ډT"c TifqJRR"T-VnWIrsTW:q\*.c4D>$&\ueNJMyJv.Z$SyC_ h Xv햗Ց'nŰ}Y3&OM^c63MUU%&NV rNU #<_i4jc #F?@w}X-g.]$%w9WQYX ym4G$///==}ܸs'Ssa xAֈy4W8OTߚbX֋ӻe,I^ 7MvTW&_ht7EC4ݱDԻz|uտx+.)^)7ԣG6CE(}8 >!栆jZ D+ex@'/A-xvapI^ `4V^ YBC:BQ\tUUĤd=*#% %t;;KJ]RR[ jK!Æ>iRPT>tZ͏0`b?deɤo[Ly"뀽K9/ͺ? 9>+Ru&8F鍏tb>)3ոi|/ 9enX֗I1jQV3p;pF R'!>~ŪUy䑅a'\uU q|qIѥb: -؝5F"$vcǎ I IRi>5iSn⯾jyI@jwHf|GOΜh~w+0cLw}Uf\#JP[w=lMC--LNISb-= bRwOVj?a7ٷso-4 Uӏ$;Lk(s,eA)[O)[:Pp5 k.x^V4UVZ+{oP`s1pظ}KK䵓/vm  D5MΒAA"L&Kk(/k]&?j-L"o?fG'//2n(I1 ڂN|O̜`Ӄ,\TV^>T?=PT چQn+zP' v*ce^Y:O>۽[WO?NH=‚ex~giRQ. >qcF=} U]3ILK{a24}b״ޭp%Iq\ %ł΄51H+*Yn]e!uԔf*ҲRmh /:3_FjSO?=wΜOxKKs{yڕ# 7os~HϽmYy4Zns^-ϾD^;3'In~=ٳG nڿڕ j jy"u>JU^Vt uoYs<Ͽ=u%9B!>kFE9?wOyG2ʽ`$1 9AB0p8)%Eu èU{7TYfe JU>~۶$[LѮZe%cF(U~xϽ:4{C?}+u)jֻ\gJ]&1@%$xɲ v@L&iF-[ƍ:qmdRL&Qi (UA,~U( xe4ԇ夢_Zt;38u݃ssFQ T*]8D@trW ՝-8ۍ].SRzw<T=ϳW}zٔ>!KmK;5'f.FtSCO 㪌Gc(M|/CI JI@+vT5=9łGxU[ D"7đKOqT"q8K~>~\隖Jt9/:-+ 6?󹶜BШUr745n8QnWEXY6Ssܥi]3[׸`2pb3B;8dsvILbYԉSϓ0@Rdbc$M aBtuj1-MuC?ven;NMMmvqe%+-RO%:by0U `-U]] P}bΚ @T_U{aGӞϓ mámvqIZR*Ihwςw'Bc0ګV.W'LByT\S&N*5^4Z'Xxk@R/g 2A/:o7%kLރcX*-#us={H8gګRi(3t\e??8qr@'U]]T*ɣ#ă%xIsV8e#K_>iabgT`3+U6.yϭnyS!%o{z1$=zBJ3lxh4B_ PFR^Q*v[uA ,Y'rk>PRwU T$WTlOLCjF ՙ:of@3 DWM2h1j1(&f7dduG dUQ<9澧﫪UW==C0]W_Cx^zCG"@J ,^fvyܡab:4K˗ )y/_ i!ےw$2Op89B4l2ARP ` I}ETH dkl_̞sIn~ 7C8?Gϻޠ%B'?߰D |YqIK[ۛ{v4bvx'Fi٤iss=OA93ftO )^9srӮtDWI#i^ x̊&%'XW2,xȾ?F|M`5@ p=V糨Mi#Y#XN#/W_++z=%Gs>_C]RG `;sN;,e0ZmV٤aY%>M6;GhHOk~%2$Tl6p8@ (SHܜ?3aih$|NrR?X, 05 6xBg~ޚm6Sraxӕ~dW#M&S$'Q{Ƿ+J73$ R$ Ko" m{v4J0Cr^jΜ;^|v݆[_Y\wf:tdTl;VVmWn;M ׯw?|c׮H%sDAXM_}Q FV!40lWzȼ SSz[ܑ_y$CQ ctV+ cju^O4Υ^./(߹!(m~GIC ήHdq+WTV57:;Hz'TTH\0 ehlhʔk d(cǎ  Yb>u0'-YV/[et&5nWTX 2tk5v{yM&RHJ gˊKZܳp 9h/().‘ƹVgд9۬" q&c1Idd6ut,NR Iѳɨy2Ȏ蠸Fy}zFi2f!B#= `4vvux~EeD`l"pݝ]]yyI#LDA#!h d8Vk_ӟZZZ]ٔ3ZN_Z^ Ҩd[>Fo0B.WCc%'gO>ط/v75]4$\Q34ŰU.r;N[9dO=y$aq`YGW^s;{Ӏԉ+V_ΝݕΡaO>ip |>(J+NX,N`T+ 3'}]r=y<\`<ӝ|S,9Fɔ&q$I^՝.ǒ^_ CI$X>1G7{LMQw>*Z5>)HHqH_(@r~{+{{ŚGoɻm{дh]+:>NWl#yԸ,fv['YضdFxj;:]\$YruՓ7P#ȘCApx%#(Dt1XTM@Dk)-|wk/YYS)eE/+kG Cb~Ʃ>6n!8'`w4Sn( ?s`۫i}4nl|Ǯ ~]O͖dN=ݝ$ᴢ\@^RXP?'*+oW=%`&`/Zcz_Ft4 );# 3;o7ܝ[6Fm l޲EC5@[[oϜ5+HC z=RTwwׁ7eB G|ǖ۞g?A'mqs,R3 haV|#[{7iJ ;;;i٤is<083ftGt.HWGŋ7no/[8!iuȨy=9JA`4jn W^ /LmNRA) Ʋ3MٖWo=o $fOpa4(A'y _THDKvM+2+څR^Q״*|n<"+ .1%i憛pl)nE%3?HHMwv C?Oe(R~՗O`,!&vrОnٴr_"N&O+f + AnchJ.kӡcCZ+-z.rDT1)eC+m|e[^\bf !9R+;4'(c{tuthmHE$4f-ީKA@Ȃ ͊4v`%DfD"1NwTPTgwOUdMŎ*Ã*)ttCTӑ@PƖѺROU͇m)4T/SlH-h$UL+iя H~RCW*,BL#CR BoA"RGW~r $C=lerKwSTJzÔ9YkS*FEO?2O?xyṠ%P/5@~Y%0)*O8l2"SrɘnwklX^nG$I$pz0 M3Cbj։ y~3:n7o&g:DA?ät.c_2gNUe2K ea'jV˲lfܼ\lA&ɷ46e:DI9zT! 9JIbUQm;].{fY>kqNe $tbjx>xңGu/B@k<lƨ@~q>?\I>kl0p!D\S.d2p1~Iv^{Թ Ԩ|]ko)'wޢš{HN/...*)>rpjs_ğ,E_  տԇt+mz֐1384cp7rO(/iJ('kvwg6Ch4gsh:qcK <-IӧOϘy'OnN=@5@tA, >MzC4HUoY9NRC^>ۚ3Xah5:^8mp)vHkͿ27I@Ϙ9뭿9nlN?@#uׁϬf^?8ǿ\xz/ye˖tǷV=V'GBla]'v<uSy[O^;qToc5U{~B?nô߾+,^󩣳-bUkΜԫ-1+>( GCtCY SlUCv/i#$t\Z9;#Ӌy/o9g8쇞sB~ЉFtkn7%ITmm?n(sIR'}rÊ1+kN tEEͰqS?c$4Fj9W?{ [S~^g띇~Bc}y޶~y7NRgv֫e1Q~"1oxs<^X܃r[y|=[})QySRZbǏy=ٴGQ)<–{˜=@C򤍃ѪL#QʜqFxےjGW^YLE~|΢`cqkOY{[W/-)N3\q5_\^_WƼ :2Bg$PtqIpŌ@ I*vb٥Y7=MizoxD6~E-mX۸|yFM%tu!\o{'K̠e9 wL F$$2daG,NLލIv%0I5/vϝlo!$6%_9'M}r]XY~bsԣg|/8Zcsf8Wñ^)󣤴`9'jՄrMC#E?: xa>ற`M1?39b[؜#) z59+LșRJ7rNr>rB GΔ ي*Mr^{Ody Scұ@iֶ'W8ܙ`s֜?nUW`7H/b͗+XUA3ʚ{5`Nˌ/|}c˜FDsC}^A^՗juf7֛oV/__XM@?hweXh4zn_[5=_xqOG{wgWMm}ϝ7r 4I`0RN{,^#MvU#~C'>/%ԖVoAP Θgm"ȏM{`H#g7"'p#}vŊS\N'JgDݶ_lcV'Mxtcqdӵv[0d4f䟭;w/\s{EFaiF۷tN$=q#MTR&77awj1Id;rshD1xa|{Sq h4򊊪Iۚ&nlN35{>,Mz8FKt"v'Y@P[`6].DeX Y^? n y=0~rGNf Ì#g(#V <k'Zj GooG{j y<>rVj9}5+2IeYkcB@?:[:ϒo4Xqj %|˿ުL?My\YIENDB`muse-4.0.0/src/doc/pics/main_window_with_midi_editor_vam.png000066400000000000000000003767161404105311000242560ustar00rootroot00000000000000PNG  IHDR{sRGBbKGDC pHYs+tIME:!;# IDATxw\vX{ XX@)57{ozQ{5ɛc{EAJQ: lߙc`]Kg3<̙3ssJ)iw0ɝ yKBVbVuNb@ Ba?[[W-BcŬ 1Z5ݳRf1#=?l)3<E#X*_8bL5P[s]{`Y.Kar^ b =%s": ]uSǞ7j'YVNP4tBDuueII\&srrr4M^`ߚ lbTD  o> Út"@€_x7 7i0f3 GH90%K|0ҍbC~e ;"^ݮ oXAmVeVKB߄+lgm 2;]Y)!B âOiJ]%F zXֱsYMOm?yF%m?GZmuMMDb2bL 0 wdx;;;;\YqXUnsioj;\Zw|̱ͮ-iV4[H6,X-9"fE+5΀0:LٳU1-4;J.U 3+Ne 7w3H;=@s!ɓ}V1 왛ݱcGHhL+جJ8怨65LͱfԘ -V6W@ HKvLfWP;[Hyb&R%uF}Da&L;>%V{bw5_1;0Ւmü|軯$wVZF`R>å2#ì2ݕJPH=㸳 zk%0pkӮݻx*JuF5u"ZýsrŚ b$M8g(4~>~l!D/pk&G1j7| z$ӣIq3"pŒ+in:AA-.3 W/_{4(޾&Z $K?=k+>> E=_Y\NkY: :]r˻Z3M$Μ:</1ןFn*ٯก:?/ݭS._-[YVB]H3WlmCbwBN<2g;sZ$-zsa}Ys{=eZңS'A=,~k*gggJEy#b?s3iپu4_zB%f{Ml<&VK3 ˼ψkK[$40uÆuč3G%I3fd3fJNDpp7:nةSnNN`eSCScEDڅހUލJ|`ttZ^7'#<.tNtv\\1ʜB<MHɗp3nBLdև>fXUnц~g9JdY5;jFxقJA+,ߤLMMjjzLX'OѨMXѤt%ԉuOJ+ bL84j͘&<5FkZ:vѣ$:m`Ux^NNpp0"P^rdR[$q,P( '!q^BḡKΝI'^Ԃ=zwARiEbB-58 ;X1ʊx T1ْ͓D3TIJi0kkEI3ٕ.{͜\VHq9M>C,xΘrMdfW~eΥKq+cOZN+@nn^w?mefrԎX,rH0+gQxHrFF{r80@-֣ sR;!(PѠ4(^YdҚ+0+^Ĭa10u8n~*N~~55T@:995;x9izQ+.dgO~fZUaHF OD\ֳ%Kr;wL֜V]MMM%%%%%%CH*v9wLҌ~|m 22"ǑXD|.[A^?ѓc`GU3NK63+_6)gy<`Zh`i 罜4'N*.R)-MɀA }YCx\^0tV)yPXT[[<{N9km[fճ'R~h w^y! ;jk&tF@(d;BPӱBQvvL&w8>jͳˣg3Ȉ3AMdbm(BLwX8fBW9E؏ rvZހ1:4Bf1c܀ -nH=)/ԩIJ㩴1MZMZ7Vc2ϟJ^7?m@ xsqe%;wPvQ޽{t/t˽gRAT=^R[2^ȝtz^|bl!O;ZUQѠPQ5eڨhG ݄92>̀h]fws(ӛ VAIĔe#3fV+s0klP˞<5~~є+).4B]y飂ktL3IG)suܙK bܯJyw^rr]?_*5(l*suqf654qs~T*r`jXLkXgb]le(/˫)~Xe|XbzG`h"ߺVDѸ])|N8^UYe<_eu۫MBq:hLܠ>VAxvqGq$U$<"%Fhu-8X2bϞ=oߺ5dPnJBQt:\1?yr1gϜڱ7V;cV2b Y*pL>]QVX0mFV1Nq?مhJ|VU>ʊkPpUe>-{or0tDw](y.z_XA8eΒJ$5uz @ ;ngittс/`3e-<-&on8b֭k-6-|Wղ*n*1pswWUӞt.N-+VNNy"Bޠ7ՕARb[oZjr["jم.D"qKI)vr7HT dZ'''uЪ3B8H(:V"U#6wBH,`$J*<.uuU)/@$ݻw3gLB!a8k5jpusW65R}sJNN;6Whx'+8b0{J|;ƀp| > bY<:FxYA: &[_Vqrvx򤰸ۻG:upCT^9}>ZJcAh|M -UV4B#YڮԪJ bǍsLDȁ&߯zsvTUFV> =[%\ ?JlB쐈ywm9~Ԩ)#'& ^_WWW^^^QQР7"rX,v4#Z(\a+8q$pC-1ۿCln=oxmK Y6R&bENf]%R@-/P (ZW15>[tP5 nUD{c4X nq$ .` PQF7sdY !F3/LRFmYf_y|[@CHnN`ɋ7PָZ; pAi`}!Dca`N.!V̠S'"vx\8"*<ŷ9nVզݛUw ӣ>$f%xog+ cM VG:vP(wuuh0ƌ9|:J\]K i$'q' 'NH$vW)?> O~,fɕEvDX~/B昋X)1';"V5Tq\NηZeoՂE6,wIDA3Yg}X|߳,4zG}khY~2nVw (8INKC+j$Im]d|Hh0==ʦիV 8hСYR)=>b%Wu2,GSLx@lfEPO+Mj,&LejG <Z ?Q U ZMҨjZR)׮]'߸@IJ7\vK*hd|ri22@?|sՈ \]oܠV5km yvs_v3˹PdZ) ,طoq11!Ɛ,\ЪizʕŋǏ6uڧ*+7mJ""2%LJݹs%1v&X+PXh˗h J 3Eş}񹪩 TNhJ1riIS˔P~!qq/rsN'O;o.ێ35M[w|'׬3fL]]Ν;;kߡ_{=m۶?ϤVx_ͧ`ZҴԝ3fͤ=vXJJ :{,Q\XaHgϞ#F`RSSSb1"#3d ˼pxfXF -mϬY{}yTf,Ln\|/&&ޝ7-mZ4BȣGii=2*rq6KY$q|3fGG^`r̙DWo5D"Ѯ]F< ܱ؞\x֤}oݻao\.oR*/df8p`lvʒGG%&t>#kkG?j[l`X+y%8&bHw]|'|zΙ;$vrbKijп_!23c*X ULt -BMuͺ'_juScmvG-`i.^4F#e11?3m=ǖmN7'O4mکȣ`|E ɨj'L|{Op!|GIKeeKFEF.Y͉ K@0Z%ݻT*WXd1}F #DkO<9|\dIdD B:66:ѣ qGǍwQ2W$' 4K@'OF F Nbc.ZqY"FsqqqqGAaCܿqoݺ@ƍ;r0tƍx@hI=rP¸q ;z)qqϵ\TYQtȱ.z\flxRDThU˝^[SsӦNNyzա7! N[ ^^|d2F3j4O9}FرsfϾqq\7^sPqqqr,ooo@z*2ZMuߎYlYccBhEaa7mZpQEEExhl:n„ G}~g?n;6|-C6SN!T*Uaaі-Xxٳ{޼_ G6mܧOԝؼG|3g?`Ci}}}%V]|P(ɹ/_ׯųcyډ޽{_zԬh._W\ӧ\.~vz044|ӦM}ٵ+u7oPR?# vǏ6o.%qȋ"bqO,vݸv] _";yʽ ֯W5*droXlOͅL% ߭_#X[-3nܸ9kLD2ctYljv'O"JǏoK1 3 ɳRihhhEEEG+WO.Ho.ļuIRtԩYYY:rkjjurHlllFF&q$wwS#"""##.\Ȍ^&Qn޼5}tT:m۷['%%Y*`{߽S\]]O(DG9ugϞE@ Ν=7ydZ_ydD2s P222###ϟ;2gDEEuNstҡC#G @^^AZZ;wL6U"L43! ^o޼hP()JJ&J&ODɵD]>+9Y"N1͛&3)hcC㕫W# 7nP( i^v-%%Y*Μ9͛dsq|+H$ ]QQAmcƈN)F>D۾vZrJD"1s7Caܹ$<<†y3=s B))3g<|BpلD00wp`yyɅB1UJu`.vqvЩKRy\دh-^"{I$҅jT&6~N9[J5c͵Ɩ]S <==~x#)dD"ѫݝOZLA\'|o|-zk6WDM6L`%Kdee!=,C͛رZ~ƍzkFvoE˫:00e\rSPX!R???., ջw;ׯ\.* TS:;vO?V]dRF}˗m=̬f?O?Yhd655OO"fkqQqኊhjHrJ )SrijjrssGYzA,;Y2")))qLl͛׮[kިL64g`o4fxYLڼ%}Jˎ?~ߒ)SP_ryeyxz"Qep@ ϜIFFFvڴ$JWKOK6?wwwJŐ5[T*ڥ룒ӧOOإsgTJ{Ngg灃@œ'otmIEHM%'zWW&̳\<҆!,Q[[kQL&khh03 `͚իW:u?;sg\]]ͭ;w4hϋX'"BB^zuر{[n =slJJ2>d2ECUdWTڤTzzxTUUz̰DCؤM6.]<\.J]t%CeݻSN5ZX@d^<WW׆LVWWˌX:@0}5(( ())ywݻ3g0@ 0 "%# \\kk܀`2^!2Icdi{mn,ңG֭]<+@ zHf'/z\Pԓ._"<<ð(s6nNݽT:tذ-[< !pss8p_ :T"2MKD.777/L&SS,ɻwK>}:vԣg_z)D:t|}ǎݷZ 4x`O$ɐCZ@[ِ}uhc(|ʋɠ;$MOھ};jl"H9YIRãx<00pڵ#N8ǟJe*Z2OoB!]]]mkZ3@ d...7*<<]\\_uuT<~[R5ש><5uV=r;וּ{UCj#1 ;}%Jsg*#FܹsgSSә3g_Q!Ch4 ̡sٲgΜq<00ro޽;11sgjllL[jD"""@xxXZHs۶m;yNWر㣏>jU'MVL2d0fv={:xPT޳NJn?w>j?u={d#vܩh:z*2aTdTڞ |l=iQQ;S`]Yc֬Y>k,d) ӧρ*0 zni׮K$AƘ;uvss~:PmM(n}z*Ѽf kWZھ}۰ag Bׯ]H%AA]]tvwwv}>Rں7PH-))SwTmm6lXs@ۮ]vYmݺE& __^G9|ԝ;jնm >Ktۺ_BBm&Nh 0q֭[yZg'B Dng߾U*0 #SSSUjmۆ k.sLL̮+55&&vႅN2pGLR172d殊 %9e޽)))흙RM(-\tiСC LTmKykMmkjk$9rСo6קL#_2^o zyAR{nc%KNO7x`@ X֗Loj[%?ZëW1?p@R~ brJ~]~~RԜAߖT ef)ܸ1v<`KZp'gϞӽGw'''Ե|̜5˶Lߘ_+sCHRݙnᢅ%ӧزu˚5k̋ZK ߹{g,^}_~e„}ի#:w

?}{9'۷!Xhg~6c{HRCK;6=)GNNN8"{</^rv֤rs/YVdPiQE/^駟%%%C*@իVM<Zt۷'Nͥd.@`^|+_%%%}e.B_z{IWgɒoߞ0~BܥK HkFw !~РA;vӧcJǽ+;؊’a0b z1=L   + θ ,վb f(%r(>IsAχ ھc;3zۊmM:E.le 5K,S픣C*S'߰~CMMM=֮['1 a 2&[ch(Q,43, ʲIR,9_=1EJxhy`s * 'ZFf&iPZ*duNԕQϞÛL0#985)dۊTuINl kKⶱցe LǬT6|ҷ.ՅŬ]?{oZd徭z`?5ض0:!$h# /&]2W_kh3UB]aec,%`?I*Gjv˺-8]mEUkby76]r:бmuC^}kw2ͅ|YL-bIjD8!Li daV=3v2ZCp(Ԧm7SŁqP}(i%m#&ZP ~.BPR:;;wiN<}`WaA^ɣ/^O4_!ωp׈W\"WVJemm݈}|#MC(Zxp'"IۇU٢YaqŌq }O\xh-}ؓK/US.:^K.=ۓQ爐+/'zREŏNbg0̸kϜes"iFCOS-ZMuund\@ V[RRRѱcXYYUaq&u1vdiM A]7?o1JH^ Xm>(Hy^UzNm*.ڇr\Uդ\}&Kd=l˴ѣG55 L&U"FF&aysE,qi;K\\(Wʞ7۝;wC>l'P@@3Ǐ1 Y2,S$+Z˱gm |r'D[ZRr13#.a= @@3.vG+r 1`@ŠZ[QU*K绸_y%g߾ryopZ#E"H `0j04'BR[E!+6: A}=TVBY_:KG/})1D}11)ae(H<j`N"@ )D& k"H$ H$@Rt8}kI} UxBQ(u  x@]TALgEii r"Dv hj5[ |y:V Z,\ccSQqqMmmuu5x{yu rww-G![7R?ohW\)(Jl㔋>oooo](d$JjQT>E U' gl@ֹ v)6>=Y8H @ ZfSUGlUuu:w NƖZ.qBzIPiYYΝ/kޠ֨;t%(O~Fb0#6cΝ:?(,|t i+5E.Rܳ2v57u k\:_Ъo~~A޽{2gP.P_8p˵˗%x LaC]l~^䚑yA$v:|FcEe .zxx!;^CCô/selQq 6"sr?+W,JQRR}z4qbڂcXTٙͦE йsl zӧY|TNHH $H쎋052*Hi$h $h0=!bR?l0xExߺ@$- ӁF" K~ `\ ,t@"@)@2Hd$Wb񦈎|2ُ`&"o?(j9o4mmw)6!1[p}]-3 5۶@ ݫP6K 4*_ tZ@Ϲ{uR)H< nܼ5|о}z[}PXXN=JeT liK}u}%{yoT* v֝Cr<BSds>3ywmhk?fLISI  Ua;vjiu`#.5^e OR9A;OMv@Bz{xQD,Bt:(y tZ :Tf}j T;bԻw7 8Z@LdU~zmb^zFӷog''cL)yedu)kH^Ff%|mT{!uCzXhۡ#G'+wFxttׇYYY11i{XIْJq~Aa֚QKJJ>2-)ÇG/ xfێK/ܹ &۠aQTk2KHmgg8D: BBR"@4;i׍p!A,0DuvvM aUFsqA!!>>^xcBxLPc cqZPA^ 3Eʼpe @D3`5bs2eBP@hYؚk6qP[5E,;<4B(#Ga`>V4њTB'֑42W(+t8vI(d26fÿU&>}Z$&ĿF BNB;q2?%!!O"V[^^~SKJKbyR 0l /\"tgUB8~X,)LOO?{|1-;b}CC'`?nݩ;ӫ˅ їO=f,r~ȬTtͩztwgH :-a֩e+&@!ua@ V+Ɇ PpL«Cˆzr!\]]==<1 $N—|O]B"aq8%s+P}ߤA0X"@zdhi Nb;oַ]'Z#$qZj5!!@m~ ڽ{w\w/[ۮ}Ϟٮǎ 9~丸XłCBN:>88lY6JFk=0Acٸ!wyo1R=T*'_հCZfaހn Ο;Vi%10 ;AߒAk6λJr' C!hP! L?z;iBT]Z 7DnnO? !j޽-3kkbpq77hhJJS w4_)/l%52* o<ۍ /q$ZV$qP(,$@@ 0 W^>|8B|@,-|Bl|Lcbg͊K+L>~Tn(Pj?ɺs7'^HHH\\P(0 ??} 8? ?VXpwݟI fܻ۵kטFw/ϯvDC{I[ ^{CRMم10;9i@C]u$\SknRpB_T{~FT n "p}SctjD"H$#GwCU%yuuU^ϡA>phT + +nVdV)[\y*aUZg q@8"ӇvYFHt:Fiu{쯻|VPB9Tubuu |}UjF,++)+//++ollh4Fәsy+_+_H^d޽{͏1r| 奥woߖH$Q1.U\yy111YngJ]A08t>~\Ǥ"'IiPRkZA!?!&4ai˖-3f dMzgZOOoF9ud@x?HfONA ehHWcw+&=tk"{ⳛR(T)bXX( 6SO?yݎ]XO=<^%!=[~,ĐtY^vg͛vv޼yy׌ۨ9{4ELzAqq_P]lF:{yuq?=wns t? A*b+,=%``g"\Z @4"*)4YM ٲ@ Ђ6Jk4R 8. #O߲<GQ(;v2d)ߣG]SmC:WPkgLҸn>5UÞh^^\ãRi1JD޾GֲdWDr-.&c>oggd2Ϟ=nܼ%He,ǏSkժUuvvv h"yeUِ`EEr_C޼y֯\RwX޽{СClwJ [$,y#89?zH'N=|4׻6r;(`"jYjJ ^Vd}EQiYe qiW^=k%o͎ұ1\a8RBzD<.711;nVϥ/KSO8j-n/=b;#htD?5h q#L mD9p4ZmQa"$N>DR*Tٳ۸B"{nrfqWRLR999 By\(fkj(ok]>I٬Z:) `/t+/ an9/o@">\@JQj& 9EyVfq귩ϟ_r< H2pWQQ|u(T r9xzG__hvR斜띯v\ $0 @8F@* CGOαWڵ&ջvmԩ ][1kV3Za̦&31Ǣ5M-(* 0 KIN0̬q\fux>)J >qG5hD/c^I~թSٳ7oQ}W_}u-X}`0*axLLd3+Q)"GuIM5#Gdq+"?b2x055ԧx[Kӣ'+L&Ze^ ;pc@2F: ըU(Rr(* Eb{H"Ԫ-}k󴄶$sNJbupmAh1\0 'pJ51\69.̷IZ DͩR˓mԩ7g?L='tc(ʼBlM_laZ c0u׷0Pd`Nժ- ;\5hdNکmu٬ݖ.}w_ݻOg2>*D3d\ll=w $Yٹr83l.͵v!˕ֿ1|I7n mߡÏ?q4mt.ujf*~f5}\b,LVT8ry&Hdfeek!Q($&wcDy:aGZ?;8,(6@ XvNGBmQ<\~]+٬euP P htFy{9uACWޝ"2U`[_gᅬ \iӳ;9ZuSv}_lSsq eoobj5\]To}lllFFsuup8"fH$:LQ^FqجH&R_#(9cP\OW۷7z#`rD<.A2Օb("/R{zx"uq s>pk\:Z,eZƍÇ;Snؠtuj9Vl/]8m AmҡCJsHL%v Ң[llF}?>s\dd̙3۶m իz߯lmeR6Km\r}zU]n<4z!t$MeIa0;;=`z<ҥӧ)uqƞ=87% PJ%ԯP@IXG2Vb ,% T%ʥ" @ pdq,ZP 724\n73b2'"ݮ{n߾ q_ϙY3gG4P(A?NJJzչsbbbө?vrrJ3ZVk׮]z5==]*FEEqqquQ9t:D믿X ffej<ϐ̬rٹ'O|ќ9sZjU3$`\ (qLcQYVG1)T_5>@ޟlfAQA%+.[Zz:)bq=\C _a2*+Z@ " _21+ ߷oD/~Wō۬_/vJ3oDfq^]xKLId*JeX+rX,&R,$IbLjj ^.r^W{IqjZ6) ۷uu(8TyuRQʵq S*ViW+<|J}(+;G*SdH><\R=w?pΝ;_zGݻB|ON6kYF^q2, ڗ00(kU2yA2uV6`DٳL@С}kW`Dj BDVt$j;"GG I(**eBVdgezMJ{R@%Ȉl<9'W>w* hI#Xj[;XQ MQY@-ՂMMM̌(JDO<߿|||LL'AU!իԌLWWW77*r?NNNH$uurrRYO0AU/^2<|&pS!uq^2 * *V{+sXd @Z `ن Z}5r#Vry?]d |#-+HUhEo|޸E NX=IT$~.ö6sm_$NRO̖g_so9p-g);@=3LF,8@2lIC#@ZF ةwP%;[.vv+VQ6kA>!r/ ;|p5IJ gB5/c1[M:~Yb2}YddCnjYbأF6-iQZj3qPʖ!{Ar,׈#&S] @@DPD1 3 &XWmֶۚj"JRR V6KVV-1jY@+k%EɄ  04Zb|||~ ٔzp\c-H8wNVl& >HG> 41[2a:]~&N?GUt .u\t:A,ݽM6 4شix<IAdeeyzxT`~4MllÀ!Ixׯ7n܎;͛7l0ǭ'\)-=COfs8@NϡK> ȀX\!k;~'<}jn4-N8!0;;g^Ud|Ƈ7[Z^Kd ]\ $fQzhErVz,A 3VJ٬>0u{QR5:96kJ٬鳮]?K|vg*GJ%&g@ձ+YRS1C:tӧOdddNV,**:uΝ;w=yR@oZ""" y:(`ae}zX0-*qdn@ ~ oN$y+߁۶mY:Z[ / !/23!;- fL&l 6A! @SeQnpڊ2(gҤzԯߒ]q˝ݠN@ bl(a,CVغu#G6lؠWULQU!Rk,WWWtPlٲN:YF.'Hj2$HDXXBؼy355VYBCC@,X"""8;(jZ6- jެ2 X, .\ثW/wy򥞠ӘQl-FF,**rqS9]+Xukdc R TRU8d2-xȒћbw$nK`Fqt ؔJa1fma@yAQa@2vuxG0 *aEy~vDo4*EpXHGScJYJ%U: UV…֭Hܪfjڽ5 .Ν;WٰaCPP.3!|<ͮͺذK!Yy.|+@V=sW`gj ǟ~7``n"##%3iӦڵǟ~^pu~V>lԯEv.vyJ R8 2xgs ѡ r\]۩YPEG(o^+‘PJLB!Dnn@QeYmR;cX ^IHL_eezKJFf='9 d=W+xE޶mٳ7md+=p cǎsΥVK%>YprAj"3}&,[bEfW]?qD7md8tرCREFFgL(;v,U!BO<:(F ܾ'c#dX ׯD;vXM4ӧOS&w0ވ]vq8MWE"2^l l R;рBcB.suu%3٘9 ' H pQ$xQ$TN@NjZJ|ssrZBH$8Q Sp O,3J5]6K628'KHHk~~Bv3NM}b3 @QjU [Y'A٬]:ttIiIrMggI_|THpY;M(#?qظ˗/׷/_۸i'BHMOYOO9o]+%1ubB oz:jfk~~gwNKM3zaC'NlШ-[Q{#=H=\i<. yn>`(& Rw*0L,AzzktFާO.L.((X?4Zp =D$*O y$ :]h'xI^جLuRvG.H*h dl&#X)b@gf^STzy1y,''3gujh:+Už3qfjhX?x8UjuAPP",6jcԻWvvÇRBtxhO: A{9998 VZP(NAYOݺt.ʆuPz +9tw,??ѣeqx>sppl zנL,1vƙz}Sh*t,**"I2)MAAWݺ|7V*$ XxS,3PUL8=qNKB!H$Zr*JLpb1Y*iĿ ۨt ( A$Ics[q)ǫaP;\6kU" :t8ubprrH>4wչS/- 0);'gT+>{^v}{wO;wZ |}h=0nTGhҤ6VQ*ǎб[nw~ХpOO?S?1|6ZDYt H~!C'e U8Ǒ!8-P(0aG["NQ4a09G5DbZ-`|u:6VچbOQ{78".yxKA(x ( 4pZc2Q3 J3Txgh`18L`T*TAƢ쬌iS)))FoߞԬiUnuhE]]KW >_FPⳣlVDޠWzel֯5 Vjc1J]ǹ6/ `!+$ ($BPĆETY p\]1LN_al6["J /TJV+/*R*Ż Yi5l ~ |b 蛯"[e 4fH^XP`/>F,̌6mX`_f(iS#rsrӶor(y\ N1#7'U˖wT>mڴ+;tryQQQrr+0fт.Z0[T'w]~jlo x6(KIh+r p`"'9tjQa{R 2;MT2Qd2q%A0La>fE]T>> H8I:bGG@h(rK(t[ )qĂX Ip@K   HpP_ejI(٢L6֦V bd2[ pkT%u~U x @VȑA p(dH,qj WZLf^=۷m[()4,S&x[9.!ҥsF>z85----<<ڷoUɩ'خMk?_/_fdeeddKV͛5k(6Cqةc@?~bHCZ@в;4`qn2^۳Uް\q\Px MLLRk5iٹԩSX@TAg9R2!( AV~uzZKT*=;h8&*r~_.pscҮ 7ڨF0<=2+ 0OMKmӦmUҨj_ZD t5x;xԔ$Ϻ*Ӕl9j]81D)+CUKK2 Deui}pv~U/M6mfN5Yn0 8Ue1L(1 l&X=6Mf ApOyvժіC^UHw{2$zJ'.&IRV{yy5?ȌhM i4-+>VC1 fU\d:kd\֖GQӵ&X)\N "Hp"I@hժqh{es8hI |AjJbz&)AVle҂>Ѩ55-6E4O j}@ԔO9ޅ 4hРAF 0[^r 4hРA 5[{{A 4hРa`$H(,q<3#͝Qf4hРA 4hT#cdef߻{gPH( u:c E,´њC 4D)ͽgPoA~.- :dpm7F*Q0O\h͡AOl5  0h$3_1/^`dlqq$I2Yl?G$ 6t:-H i-ͩfe{" J|d6h4aiph4^1/99: H/c^6iCgM5Ĵقt}Mh=Olc>n8^XXj2a<^,~+[W>ΖKP07[lIk-hNM7(6iРVЙ>JPA,H|"P$+;*+|}i-ͩ)V4vc4h|(P(\>>$AN1VZ5aCL v|ZlDsj8Xs{" ,,e޽|~L,( Þ>}*Jbq-^tɐZ޽YYYϞ=3Z2E^^ݻ?~,J\nƍ'LW.(?w\wQ͠ n^<&TZDZhFw- <m۶-ױ-[tǏO0GJ-ZRV-C}[[W4BA4hxo@ 3 <|ݺF~-[6={Ov蘔T%hܸq:u/Hr+Wؼyӧ׽{{>x0޽C.Y|7HIMIKK7*LMMMKM3b=֛7CB7'O.wֵnރZ[ #uy0:f>&O|ƍwe2 8l6j%EFnsqqY{#-==z%&ƏӬqc}p jcOY?w]k6."''w #yĥKSǖ-[B\|W!!C/JQw;bGܾ}*)((2%BRx~8YYY_|xРӦMꕅ:Ҡl5pqvڕb9;;Ϙ1}:jay`p &? "bӧn/XۃXg"xێTG\>AK4MxTjVCLYQu}3fbM?0orttj_s _~m8͝;/'''((x߾ ʾN>#..N+QaaFyru܋m(=kЫW12,lXTT1l05q޽v4+4[rm۶o_beqʕ.P=Ho^͛7?Zh~02re˾TǍU& ?K֭9q|۷oܽ{WF Ew6P\HTRGJr+ͶWݺ|\=w{իWSIЗ3 R͛}{̚5s}T֭&Mx_Çmܸ Hj͛7WϞlMNXNNΝ;S%?|9^:~k֐$d2T*W`Z^paAaIy ,js/ $Wc۶HoÇ6jp}eVF=a$7x V/?$vKqttܵg//Fa IhI; IDAT]@/]֗:ucHdVCLYaE_ҵKYC#G8FA۶m 5"RSC*:tY?mp\q@;w9|И17lH]h79cƞ={շ+/ޯ@ rņ4ʠlRToݳ{Y[nKIN޷wϬY^Vc:[ϓǏ/YjT/B3[ݦm[YpMLڷ_r,~ƞPȅQ;wv:wtn4UXPPФqF I>zP.Wtҥ]xH?~iѳGСC8lv`n>H@{[ H((q<-5qjz`֯__ {`͛J܄OxA>{.#AFQ(Yua}! [,eeUsq捛_,^2|M7d2p :uwZپ];գG,l<obckۮ]vwyϘ9C:ubXAA%u!CB5D~tT%YYY?S=_ߊ?~}AY@Ȑ!L&ðԹ4[ñOzz>5kU !CB>yZ>q!!!ei0+mD9NԡoP///ww$˗/;::zxy?v(s:mz~~ݧFΝ:2LJ :u0GZZ+ئFY8R=hĈF 2-cҔ)V lgݺݻ{N]//VV^SCGr[]&:k׭2Kή ˗Ϟ=suutw5sFՀ V*eLɜ~UԇvG5PYG4(0 w{  y._]ʕ+:<{\''iӦ7o d2H,&"T*58A,ܼ<{{{i=}^LMw+W֬]wCH$"L]vjC>) JҴ.daX~7.5>txժU;vnܸ1)9IրMN/]~ݺ}i۶`:ېUVP*#FY, kP?oCGz7Bl ,Ӛڵ}||nwSNoUٙ,CCfYM>33nݺzu;!Ir+WݻK.Sͱϕ'r\ D$`Xox%"YZ2S*kf,4_._>st?m떾=lA ׮ ۵kԠA~mm+V| Z$Vɓ'YAy>rk._ ȬY3;W+0aš5kk6 C}Ŋo):fHdp'ǧ /q7n>|XYU&YXXhTؚ7o.IιzX,d20#.#xjOs[.[E"(`퉋/NF^t:6ML:@9)ۿqㆉ'-_ A~&>|}"""WIII|ٳ}}}-Tב }&T*P((a>jR#GW̼8::޽{WR1͛w ]\\[}zϞ=ٳg .1c5F4*c=222-ڵ;z\qԩ*Gnݺo[Ө)YPo,K򛯖7l6;7'0,}{bBB~^7T INN-[lb 7nܨQCfP}iUog͚ٮ][}|:ҠVȸ}W,Y|ٱcLJ :uZjjʬY> 6^/3;~<8䧟~;w_9H%Hnݺ%ɼ*ZjQ2˗-ue5wU+#,X0͛g 8hi b`)S&/[l9͚503JWwرQQQ'O*(رc;ueȑYYYWi֬YsΝ0ab˖-2v8UR!룔ՔU2aBx||ˆ#;>f̘)KwڥRN;V~.^RLXPPyyy'NԨTgB/^VCkHidnnn۶m6lx^׮]a̘F*wޢEݻ;ͩ U_}c`Ҥii#Gg#9O^mS*ݳLCB 3#wڕ]}ʢhj6l8}J:tpVŖzǎ7nT*7o `x͛7?ԩS55*q9r*f]{پ}{BPu)yRbhDnN@ms3O5l@ﮨF9EMB¢ b^Ο;{V˖5yϟoٶG^u9z~~č7?믿Z!jNM./9}:tظy]Өʍ=V}֑ 7K q1/E8gfd4lOo׾#0]b#`9*;~ޑګO0\Ί{]:fŊch._xLL̚5k.]bhWjZ5FۻӨwgBPrAFV԰lEv\66S˃ޏ+RY%|p0a0qd7%sР}~ BM-z?c4$74>q{[hr<>!$_ސCW6FzzGlĸqÇ۴1 ׯb223M |DfF;r(AD"MKO̖dƞX$rqio/~G1JQkjv!(jmR(r2rs<> EVsjٲFrHƧjz\r E,r(֩]G >'λ6ɀAkчҜoQm'Ҡ)۪T|| rsljhc/cw-[/ D4jњC z[ T(+ͱh=DiZD 4hi3Hp/g`-@ 4hРA6@٫n={ҡA 4hРaS(e2@`o4hРA 4ho3^ѠA 4hШV4hРA 5LZ4l6B@<-4hРIaзf+Kև-ҍ@Z24hРAH28U{@˟FEuۛ 4ho߾aCKVWW}}smBI8MH$ 5/ZaTj+WA0dN2ƍ9Hfq 4hРQQ%q"33gRi̫-Z[a8$gO6i,k+UM/5maL"xyfbqkE 4>iU NLNjϯ>Ϥ+ &۲E"4YқZ}&S*,&31!˭^71 4hf+mH2G( 8~Ν ð\(y{ygtG6#"k2NNLuhРA' pHKKqB?#i<;I$իVM?.l_B!/`j+d|;m'5ۏU7hРAʘdJ"lYػg?ϟ߻׼Po-eknP0 4hРAS @^Fխg}3=lOiX2bXX ~U(ƏӬqc1{Awή_u6th͛7CC&NL_tС/?J?Ǐ/YjT2dȔSy<+/ޯ@ rŔ ʽ2~'@k^*={y<_|SRR `0BaϞ=g̘n$􈈩aaî\2xpI:nĈQQQF9r ɓg 1Ç0ܾ}oFεZ#<8xp֯P˗ZlU\ii?!0[MrߤA 4>9;aÆ&V߼Iܽ{̙"#޽k̙ CV'&&o={ܳ8oϞ 6ߛ7o J?)SL f֭[[Sii斚bz ʽ2ñOzza>5kYP׮]OII1+k>oWbu: L&ST%&&޽kƌT+@d'=z$,,t/?S7nY,KRya_&ȭ[GS\9ijûl[hРA jsW 22\(jdfzm[y\n,Dp:v nݺÇQ}u#IqzUɓ͛6Ϟ=H+V-GI_rŮ{5 RTܻ,úBYϙ=nc v2vXPh(/Ιm4̙3T`j P[aJ9El HҴ+$;'==V[-R8Bnl[MzsW4hР`Ŋ@/Kf@VFƃz [JTFKX,}H$& ]d2 ~?@Ybd2.H$ɪb^4!|u4J\Frjgvf/(.HNJB #O^rr2 u뚱A@̌zuݺ֭]cxB lXo}+<}l=iijBuoUV[[6O<]hsE A\t_}nI%a04h|Xx{{HIӟ3ōɶll*fTf/(.X嗯_Wɽo ִiӽ%(W<~Q9S("0//2K?yҤP 0dJ$*>ׯ߸~xLp_6DGG;99>VM4hРAS %8Amڴ9vFpˬ͝ sT*?g4iܘ*oԩjZ]CC{iXثw3gˋΜWަwܻ,c*jʤ7yDZ=Bכ>"Hp/,,Ç}||hBlٲvڵlrԩLh|X$&&hѢyǏ٪5#V^>~\|B¨ѣO{Q?ënqï^:qd|/_3&>>><<\e~!A%ԟ'7m@x{zP::m{?}$b4C ԉ ʺJ)))#"$ɴ)lТe9͝&1qYCL1SP.?ϸE_pq)͚6LyI˿zyM5߯Y`|q`q6m0q/3]VV[d;w-5nSNM?ŋA5jԨW^nݲM&nܸ1 5BIlZnԨ݋/h:o߾C\=D]E7"5~4Hj?a¸`Fv (rCkr_z͝wF ЦRڂUֱaܖˌ)9s&44kNTښrRL^pa CթS6;w;f/~Z,?{wG;R=v@ػ1cK4jkL3$ĮEFPD@p{w ,3ofggg#G@UkI[uHBzeF!/ݹeʓ*2^K$׳۽GdRVK'ennׯ ⋩U탱)+WʹeLxl'Gqu-ǾQD -[6iҤ۶m}ɓw9s_m됶YBpp0DEE-X'u|jՊ[7 "J:f ,*(HKI*).ťP(lѲ%p/).JOM.*(J=Hۿe6mX IDATD[dIլΝ;uJ87s nݚ>}P(\fMeV[tHO>_իWm}"*Zn߲\[]ߨFwѢE{1 0`ɽz:p@hhhլdjj9sh4+WVYfuT>{!lF6lhFĞ?qBY7007Bwmw:ro{LIY&b2 "\~׷U޽{sIKK \lY&MT`AJƎl!n%im[J0Cݢ2- v` ` ` P$ۺb` V O\4ij]x@ Tm$cwJXwI2ʲ,iOŗetX1E#yȌ3V (eTH}ϊJ +2":M-HL߈Fwa0 $a! "%Fjt] @0+ʪC)"WV ׷̥FakĈhTV]T^9  +_'9zꁕ!>F.o;z1}k$[ a"Ҩmx05gנψ!Rbą@ߪ aB# !+;aD ~>$Zgz!q11Y陁3CS0Fa&,0f>tOGzP;46m0ӑQLѶ9ª3BcsC8 1EtDi<7?-&JEI {*I:iReJkynL7!ٛ) & `iw2qFdQzᙡ*dH~OA:ԁD!ƣ4*GX}fƘFgm ӟ@1$m'HƳ54/s(̊ Dl-J;b,s*Ns-FjsIb$!0rjB'@I~O :3g:F.4~*=3LlOMz_M`pAN?J(D0EBL" ċYL2+ sڔmx3+W}|qgJ1U1}^AzLS82m{!C.{XHپQ3V@픅+~HD9e/J<Ӓ]&8T*UjBTUvvou5i6Nr#0+3Ai iy1; 1  Ӓ+dV"DrelH4jbN;Ӵ"(6TZIjZXiTp*; W&xeUYVf޲YL988* ZmooggTW@+u_0ojugP xM<lZ<`a&3A9fWOVqE[\7T" @}!s*jT 2f W9oWE+2WYrxm. a?1"Wq2YR:XpJX`ӷȬTcU+r:+"-]z>R k{RgLΤpLfÐ@'ǝ\ݭ B:mkeVV$#yEbWEPk$~_=8{0zR|ރU*yy 6ىT* wۣ8//ɭY)\Mt•3]cfU( 2y3+FlPWE.\$ܰe#CnвmFիP*h|Dv!=Mx'8V )\X2}[ōU'B9r WZlK+ծ0V "V^&R)Pjl)@yz1+ ŦBWm\dY>F#ąq({B3lGˬZ}$òF3bW`UT,֠A%UB"VURΌB핁艂ͬkB_5m Ja6: nwrױ;>xwJ0WPBE )2JeJE"X\\\̩_U4s qwO֙TXV߲F[ !ZuE bmWM72Z1#F'NCN矆 (Ys2^ @DXXDX1ePLtTF 7o Ҕ]?In׶ bj3B 8EQ)-##4a!5%ut>|QD8"Ag3O3S ̊h]ef5{hk:`bSaBBB~;ܹ \R~S mHśsq9 ށ8x=^1Mz8t6;Jp&&&-A?MLޣ'\?KP8V˭_?L(ŵ8S)ZwrJP(4`X(\4)-Srѹ2hUsSq(kee~w+{(K 4V4$]((Ͻ{iVNd)((}IbqH˯%krZ:IGGlپMx$)_ڸqÌ7;Pt]2d"d̙cǎ]hь3~]֍~]R2 [2Oǎ۸qƯ09R8w3x#?F8{>;|j5lupOR"Չ qqr)].>UhgWR%qu;>G&BO<owySܤ]?Zi>{!8(ȀH2~ǜxyyegg̘5 jun߁3t%}׷oFӧCǎBߺm;kg͘F# wR޽/D,nVXMjWό8ʘj7T,S:ZnMjjUⲨJuΝ3zܹ?FYteqQ,F O:a-۶׫W/daCcUJҢEFuKS˵.P@Iݜ+_NN->wxT,)xDmssJ\95~.Ņ&8S)jZV1cƽzST*ᠺf;RY&նbc$Ud1} 6@qm ʌ11Ede%Ӭ5G}ў*ӠRy#Fd~513-Λ$a ”b &ObW\b_9)S#iUK,2dhtL, yVU_ A*vUÇ-GL.tI]!;R*SLӧqq&IyСQ& CGyxHB>7n(-)1sVclYVw:NW3gΟ;wI[nlJmhce">zoŗPL(G*+F)#P!ffyrHn*UnJsfCMG}7#?eGn$ njDѶnZ^r6{B  Vӧ!/⏽ySҤ]?:lnBD8.^(.$~_CỤL[VҦj1X8Ah16up>jYڴGF"f|dx"{\T(TJe~h4VCdajպvJz7MVcg͘No`Oa]v]xˬ**#3h;sTUEҿ9ϿcVZݺuk@85AgALJMKNK'X69-nh ܅> TF@GS&Y:%+O+Z T@AZyZ6{<{Ŋ Sm‚|.e*LOIx_f>ONL+uV{[!+yw;vd>[.BGL(:8,?g'G99{ð>#tt?癍ܱK p/9a}+).O_Dd_5nѸaD㈈D HΟs;Ռ6uuPH7{Μir"kjX4lĈG믙hr֯f̜`XofKC6u~iqmXxVm[ZBqZv=6|'GJqz^ƽDҒҒҒb(-)F|PWAtVnh]T#0vVJ~ePVaՙ<.g‚W.8Ye*i٦MqA~LJ:;+;+}lB*J$ ÄBgÜŎťoJ5gnƿߥuDXO~r !ZׯDo KjJZ525a6``@;2! @b/x ? 0WtLŵZ'P "G&r=k !BHQkjd [wP.RZO~ &T(ty@]bv0 qI FXQS6INKWe'Yf}TVRvvs͟`aYAJ%F4lܨ ŒܔirYKKJLjKY齗K6V&Xq5 NYB.Y4Ye*4h!oޔ=|>5[ ;QPh[]%::gvY6_@Ie0WQJT8עIU@MT)9_~~Fuʠ!D£G5k֌<:tخ|_㌋,iݪOFd۷o>u:E<~ǟ~5*]F拴gϤRixxOM hT+Xv7 x蔞5]֮];"ݻe}GT6t(!kqiTAR}<?`}a\܆ jx{?YfPTTqqq~~~S&O 5l@͊֩3`@m?=N#/v6]<[1}../_~hD"W7Zjyxx^H,$WZ]Tp]<+^-P.bVW –6匰)Mk424nBj&VW2 yUR峘UB\د IDAT Xe`H{@4HtiċVQM\JJd7}'/uhη~ח@d[~8(+_w+TUjY- !={F˲,0hFU,kR-2C8oq}˴=[FN|z= 0@֪Ab+QU!jӧO#kFuV' 0UUsWJ$ڵkg4qK^a&PvJ[Daa!ajXTi0Fӽ{=u5߹sVk~]ضmmfgg/\GD[J!!,({{{{{{zv5f,x`3 -cᕏUm x(cU4kFP,g5lxJԖqz_5mo ʅS% UU01`-X+VZXb@VE)Ԑٯs3>.,J# ի׹s'<{{/}>hʕ;vؾcOd0S:uW.,^D[G^^mmYTA"όL%ɋxiI ߆V(^8eU?p֡2&7@s~N!!c6 NfNmi>>ў^P1&@g$`NxbC1F:ŠV"!Yarh_i!- 6鷄 <∼++"[L'ɍD+ǐ:L&.tɒ%OVTmڴo}}} ,XpV۷o߹sRf؊+K`dLiUWٶw1ԝy#pSzO>9sCBugge|oԨظQ#)>>ߛMVʕ3.2NQO?֭[a6` "-?KJ5wΜҒdڵ?*|h`\JG:V 0E{Jzh?-b(i`LӆJ,o@Ghl-/BƝ5Ȳi&fIYFЄz.8=LBzj R*؂bU0>"`}טKww1B.oݺuXjUAAyƎK沶aΠKrU:^-7{&vvvS&OZbҥK&裟~^UQ.g3w^ƍuZTT"7o^v'O9׳j PF9|䈻{VVo-?lڽGgH 'OZt˖1Bˁ]F;ٛTx&bc&l-Y cNz`~^3!=F@cFؙW{sx¸J|LՎ\˒/ӛEqAU0!0ajk ƚ3e ŒA2"us1j+Hsy۩Vzp ;;={xvx"tUR͛7ȑ#,Z(pM~{*lwvo_LBё۵kw?:`qIn2i򔒒gg֭[T"f͜eի-7|gLzɓ'a6yԿd?תUkI,-[PTK~xB \ᕤdįԇq!]zD,"%=njWC}'W0ҭRl`iF*HD ~̘.@*1`ZaC&UHE -y4xdBH DUjI"Q_Il$6f߼w/:u8ػw/ M!MὰM w{'N[V{9SWe敇2'/ƚDJ?阩`Ic }>Ckt?XEj~eJ+*_ 5Ɖ|~LY2&*C +ߕX"lcƧȞN>=qDJ~QF]paӧ B\K sEUsÆT׶9s[͊Wۙ+՚ :,CnrF`}cE9Vm60v4ZW0+JG+ ]΢F'&Q/$βOo3k`n5P# T4˚@Nkq ֯!,\pK8̭(wWA +%:thZv~gݻT*uޗ7z)ʴ`&[ r.li@ WsjXٮ?+0\6xh4j%&,΂TfNC2"YOs7\9RE+RŊՊU`_W6`nݺES׬YC0+H~ҥnիF:cf],lQ<7w|ӡC7oؾ}ڵg6Zer_eoݲ3Ѝ7fRiL-lWc·"Ep·x GVgekrj*TbFL!@N "HPP$;;gF?\La:c>ӷOׯEGEB&NzutTa^h cJ>O׷_}Ϟ9K-,(=z;.S*EGE~_kꔩٯ^2?ӧNӷogϜ!d^pqݺ}2 GBD2l谤$Uwԩ,;w82?>|xN8ѭk7P S'O|~ӧ!хs=jGJ~5&..Hpј'NPRR5ճN߫wNe^eeBj{n?ֳ{'O@ 8 ڴnpp 5::t@֠DpϰyqjS3Lrl*p}KYrFK57Sb`1-O Ɍ.tqi`t Ԍ&z)Me82ȫq 坅S<[,Dg);VРqS[H5 X{Ӧ;v߿M9믫cΞ=SW d2YZZm[o߾a(AAsι{/uLUڰac-ڳgφ̙CLMM۾}۷ׯ +VXK.]v!e/Wۻ&LukEu8p T*ySySwq֭u~˗!!/^\뚍6*FXyy{{7l굫J/~ǏGDDܸ~aÆQKHXZZgϞ7o]k׮ۺ:u4x={4Rk׮ vرn ߸q#**իrkQQׯ_D$EhJJW_}U+AcY&<"ܾ}5k.\(JKKSSR7nb]|mo\/o Z*:&fȐ!֮UT$[nKgmY{27;vO`,t|-5m*JmL!u 0Dj=}0'[0 #ޓB>N,?6o=jd4$!#ǏO6],s-֭P0L 1Ϛ5K$~or?7);Q @QQ/]%ŋQQe^|9::2{xzsΖ-[b>߸8`2Pu_e ήnz37NFEetakJj?'O 0wu^ru摏't<#3.QN6i/+#54o6l=j便6m::8T\jm.:5]D:W%%%\ 2BB2[dΝ;&LkҤ7btz9&z9Oq蓚pQf ڹp'۷T*2er&MrΝ;[N.]|yر &:O83gD yqJJJJ\\] 7w7I%ͽtyMr6n7v\.y攩ST*ɩUqg.**rH77BT%ʲp@F,JJJ5}ޞ)B<|4!"3]{ej-T3ϩf*cjwV9? +b38w€}Ό5RVoܨn#mcs^hhX2L8j׮^/4$5-mgB(}8WfᛑofWj᜝K$JЭD"%yF%H$"ݿ30B||}M>mڴ3g.]t6OaFD:0RW3fL 9}f%5E4+vvls׮MŹq;wl&v ,BٹX"QD")(,((,H$D" ٲyKXXhh͛CJ$IagϜ>bqJ$DkDZ?r!@blf }?F^^^/^YH$VR͛Gٳ?jzϞ=Fsm~}S"##?0t落{EF6c!I&߿}v|Zމie^k޼]B!1֫Wѣ}_mzlG&L|:tS2=vmEK[;ӣgs~[Dq0Z iР#2c)h}1b͛ QQW4yԱSukM2xN>l]ûK.2cO }2e+7WWOZ- kxyݺsC"qssZ58 $KH\$!!vBL t"bF~CƌsCHdo=p ġBiG:_~>|DpHX,H>;nlFffm6}t*R~:GHJNt ?͛{?L6lI0"ַď??/:tXeИc=6lذFCyvk1ѣ=/[lW L˳V+KުU:u4mL'!yuhQkBLϺrʎƍ֫gooE4fظ> 11qqDtIնo!СVةࠅ ޽'[#={6mzløħ>Od)U-(;?#0I&D:_WZ.7^#~L17CjX%0\ Yho+ý?SIw!,';UtwiT)EN"T rT[?s`Pa)~s͛7{M6ݽkw /FWX2J3ڏ\ bceĸu7GϞ;vl0*Xƥ"1VhZ93@C ~0P4NU-YgC ZƺٰOh|_~}X Zà*݀ @ 9%Az .JqRlښ58fhx IDATDPPrN;E뢟v?\%al+r8S>Y1!,ND Rzz꼼ЙfrP(Z k2lKlD Dݻ+sssիl&Y VeBpE&jrZdnjzs)ӚjC*LvxxHL`+Vǹ3ir,vD:i[-!T @P8h *5(W@RlcGN[?|ܲXZ;(W/h=y>UMu ɷ3f+b[jmv>샌[Y3Yg/^T&ɛ^UMmv%+M튘CfƬHhfCR>DkEFjjhw"%%ŋ.͛a+C1@ ҍj%2+D(~D2*;)JG!]t`'g>KKJo3Y0;/!N&|38IҠ#Wu_lC[Xt˻9'dM:|Ų641ɼ lT\˿+ @Vn,CWsQռͦ*ymʆU[9dFF/ŋL??lѷVlHDP(ʬ 0pR:2mk>@0@bYq})Y{O?Tu?Yz*? )Ua+bj r@OaSq#Z8\s(pAV > e[z+HʇQ#)Xʺ fw%smIKKU;|8%%KJ;鈨Ԋb4FYUt%(0@[c MZUrr Pu P SIQKZZTb{ lQ,T1Y8L3}C;"۩0shJWo&=Vo=65+Q0qdnZQQӧI]ғ;GEsӧl6Io$.`U\083@Ty-\+[s<ȹN+f+r"@norj)pp{4"Z~eOjWaSV}OmW^=x𠤸X!a˿..͚F&U,k- _L Z63:ԓr+[ *%nZƚGYYܿWl#·ȺCm;{g888t=;VzѭG#ddfDsV\.˫c+/ނHu.Te)*V¶uAe:js{(P/x{(7(Aʒ7k|ԅ~\;{ad3Li5HTlQEcb{H$ W }yU_g߹}[*cFojݺw*8kE+AcN$ϯ]ܵ{O//>`[܂@ DN 1aPPvv[7RO_ VGYA0<&`u蘓BF);`Jz[u)aۊ[谵RC-nzfJDR(>WdAT>S4եSh .:|o8yڭF-ݽ_%~%-VLl7yբծ~0,11K*utt4CBpttzWLkR9D0.KIWv= V@0@T|cDV.2#ɉOZ'>}] 6ݤ7yZ_-T*r^2پL[A,WMVNj dr9ӾR})VU# B(pRK jOvoؼgVߨ#3bk`Ĥ1b'K͌/9_^*vrC;O>]փP(H U6|+Zgzr2f@S,'S,KqTZRb;4_[qYYk"$ R/iqQV]ܓA^&P*L`x V{}C +,Gimc%YƍD@k$@J:6EKBT*dIK=<$RP~8JVҡSBj8 p6Khevݱ{]uٱ8qTAŋ[ׯp7jekq?(K2rje H,+QbA^8IBͫBIqrD"ԭ$B.GĠ{`0/ @ 35C@a H C07ptrtprP'GG݇V:B, ŇGB h C*{HAo))iY-sJ^)VEզW+Zړ4<$AXs+. wZgP"+**7;v7 ԩ&!YER)'u㄄n gggx)wkݪeDx^.8BP>-؊"oݾc,Z 䲜!'2L'05-F ^2b;w5|4{l#33k wr={Brr]LL E/^2++KgVߠ&njck=BQYvftk'$<)bX0ahzr-Tv2[ؼu[Eg.@M8$C^4niTʜtG-;O4 ;[`eȯqB. j2\Vrpqq!``ؚ%MS>J:)YdI\9fr,$Wz3BBBr ټy;G- rcsێO:ݭ[WېFc/_^_  ^99}ի1{xzzQ3**zA=bQNS,wԉ{uRNa\ ]vvpp 8^F>~۴Icvhq&$dnqL |Ӥqc..09%[*E`\'4i܈Gڽ{.QW^k^[< w"ߚJO<&=e !l# 0 K2@ll-Je2JWa6M)6jnr39w /9::899%BH?~|VxR@&}*^t"ڵC999988 tn:h  .tP똬aqqɭ.,{|| HÂK9ssb"+uh^({AQ{h4gt%{Xh`o("܁Rr\w}ٛyw߾3󎏳LKJnbze,Æ _n%tr%PP־ Ĕ3| qЁ)f?AڲQzkcAT+&vQ&#JKKO?J,N|3]0cmv-vh p)&T*_oV-[صgvmݾ]Z jr߹;lIIM  LIMqvq"EX[X%D N+:s<)T eRuB:ǷO^㚞[r c:Yښf1 #M2JvwoaX|.[PP` &p@p ipMJ4,S*# ,Llbs5dՠh*NƯ[tҥ#Gs;qS4VRJSQuqq)?´8.wgϞx R_VӞoD C=/..?~|Æ ϝ;'/_N^z &L0nܸ 6$<%$<#$X7o<O$G PUfXu{K_nl\5Vmtܞ: a:+_4iԸqpAmI_@׿/ԗg&oŔ*5y='+K7u w)((rAas!\W=!obY,\.'=z_V*<Ãd]=%f߻w̙3ؿkΝ{#Pd~OH-͛={,**ھ}{YYѣrh^`WU*UaaׯU*@QT#0+ :C!H֯_߱cǩS  9sQ}?в+Ar7b\^bSl6fT"rF6|->^4E[XҲRW7ěC[]\\bxŨÀ愌##$* ջ}SM(E;~Y67+$&ʉMJs98Jʡ\ .KvK[u^{QOz0pAXe/N  OBE9lPAjZF`3_Ϙ% g0//ǺY@ yw'XVVJՆmL< LsV+eAY/˧`P#*R\=ۘ#x<L&YfBg94m5Ԥj[VYXu(s}xTj0s7N!̜ynm$gX7%i ޮ 9Vm^m\3"4GS IDAT#GBBBzE@@Pn;ď[1/'Ozxx U:wW_8/H:u`p===<xllT*ݴi34/ʪ͛7|p k֬1c֯"I#ٙ`(J6MNlӲ9DQT( KFEEq,ʡ{Äg4ƍAJnS2NcFk7%FN뮬zS]49 Vc+ bŲ%/[ iVCᵐ pF$(+)Yd[ ǟmW˰lY:@0m4jQK! rI'(oaʡ+\ /ɪc~#Sz -$s@UTT:+WޤK7['xZY5|ԖT[rLg۲SAfͪ$=!w NִѲek8q"((hƍum۶ݶmL&۲e n٢ jj@.`0&MY3~x ê#0&& 6myn&7okn۶MnhX +Ǐ,}4͛53gΐ&Ota8^haɓލ?*7;;D9ber%5X$|Wa'5j'fN &vss+ĩӇ7n1p>?sC vss3GR&U_ {&DްM-5әr9SppÇv kߦMkMW~nRo'W!1*V^{ٓ E3WC-Kmjfnj|srܫݎ WYfAZ҄\I 0 PcfL <@&%r*Y-W)=Ex8"&Zswlt/͹GX\;/GTssENNY1õEV33V N(֎tM~C[qf}k¦+CMmd5U%(-ɲaº9Dl|MgkRO Wu(wh#S4oU*^V.y攃PI4իsjHXXݙJi~ Fl6mQ ryFh4Fj~݄&LÒj,eؓk][yZ״" =U˞j8I^wﮐ847G|5aҎO?mO[uN6=}hf͆-YfӠa j۷QAkLm9b5csJzoor3m0jYF*alZקusIz'0h~7+QAU5R_hw]dL&: )U<ɆsUԡj~Rb Vn`w.XMT4g% *B:z~[iРA_4hZ 4>H5*5s+ %Y_sgkB>h+gd:v0LVlѡhɇ=SdQda ҳgxAy_[t>ZT*:EjiOv~'PcjeFe W1j5 e4mٙ.Ԧ~'s\ T$ %e?yyy28+a Կ'uX,ͮ3A.:v3{[E9KJ6oP(p LaP(|=Ox℄A!B~TtբEӕ]G-gZL>V,[b0 V}h8M='"surrb2x% f;wt{?i+"y<\V2Vx"g*+{lii͚hhr6޺uk8ފ~,<,tׯ딗[ۀɹ0hx;w?~}F}رÇP}A۶mqצ}#\vǺ 3Zܥ_||UdEz%2䖘{}XdL4I{l HT-낂777ONv{^oQ?u|p|_k7eC[u=lCV2>sim\'Lkhnxqۈ;9ݛP0#飴ug xIQS~4{fU:}Ǎ Pإk7K|GG~MA8ޢ~,w;l(իYӋiZ%a&jF!k+P(kP̍XcGJDĚ-[#"3onGDٸɶmIR>];&eG))|rQtsI**7_}rhn&O[Gڀfݻs8pЈ#ڼJ}ŋo߾@$={"֥(WdsMJJ5_c0tի?NNN5s6.\ڶva֮>p۷o5~MʕQcQiP\\f6~>}i֏5jڵuTg~}%+ׯwGa ' K<՞M%J&`jW@ 2]}57@?д%p8z liivō?}yC@@Ъ}p?۴m&yYi|إ{CGH;-&R%+ vb.۴iɓ'66/^`Eޞ>}n׋-ԩD"9rΝ;/^\kIIIquu HII (R֥H?S.׭[{~a˖->SNӦMuvv.//xB``m|iG&%%pcǎqysoڸq=V++;^^U?@,}֓"ɡ.bdJBWP&eQ rrܿ?` KeQ#W,~Kw~,5tw+BN?dCDqq'֯oф[Z&WxowZVZ׿VFx2X,C,d2ff @ P^Pl2W=`3Aһ$ʠ(JCfZZ=݇:ua=+&<܉;wYsŢGYȱEiy9.{ӦgΜ޳gwֱR8|Ȅ ubׯ?gl g&ϕJ폎ׯO| **jܹdo;ʕ+#G1bիW>9s֠ASNwI bbN49== kӧOV?߿2WvﮎuuӦNԄdddL:o[jmj#I3`E^ӧ{ j^AD6%٫[JJEflz u=|}Xծ}AQQVc郿ETS?8;;5˗]N,..OgϞ\BʕFzZ=O>MR2$`2RRR2-Z˄,+88I&͚56r8,pvpC3'`P&UKO{:AC:X^@ڗX>l+`"#ΙC^ *jHKKnjL9ɔd9rb?ZGںu+sǎMؿU˖;Osd2.pʥRKtJAu<6ow&MBvMFDQLtRm ={→Wffd8 ghyyyjj];͛2y)Ǐ9"7A \.OMMݿHq1zhuSRRRSRz5f贴TK,˖.-,*$ %Jr*Ҙ̼k"UpVK`O? 'M`P J' <<=+, H$!MҳQ*"9 Ёtf;t`0^&2[ X8ުA)H<آEsK:߶:xpܸ6Ld0i{v;gΝ;kH:uZnJ%Z2ˈ +|>I(dI8H$2ACɢ*6ҥ@@U9uc-:3][ӳxÙ:1T.:yRBWO(Kw@ dй^:֬tqo#:X(: +4yxz=#>>>>>+vѤI短9|Ȏ;?hvBW!H{FDVПfˍuvv#",_6x̝Jz`0XbTRU3W>ܶu 6t肅/09V;Xpk׮_eVǻu6q'''F:]mk셞={~Ïh7hSOS,++{w~qF388po.^rŊ|6nlVl~6׉y&߹sWk[RX`,u .uHW7lCOWap%)9|f mM:yoܨpE۶m[p!H>~޾Tjթc.,O/N]}݇*:Ï?x?1 ]"}hui8wmBe_~Gϝ;Ofj <<܍:s3ygk<4B77YflJJJB@T\\J Ca(b8(aaWi֥"W(//5b5ҎyϟZ=zDbʕ֮+>X?~}l\7 Jop,mupp??=fADTvʕ׭۩J#wz0~k|E#k M@dh"Ӽv"Xjꁖ}U/ smH&~8"υ =IW{ IDATYY@@Qt페~Qo06I܌5d@ PuV]P]h\hTW^s[,X@A`m>geߢUACϞ:Μ9:߷/c$:u<}|TT}#HOO}f=zO}ڪU_ТEAΞ= owqqqӦML[ s.IױkO+nM=aqAHT*ZMYl6p8'Oi]^Ϛ/0\ߟիW۷xqiiCxxɓȵS_>999$$dennn仕cd* 5MJ")AEڃop`q&ops[)NQ|ܹ[wj{ ]چ_=sF[3gshrss߻K%s;8]ZgT*խxxS$n{ngR 07/Ws/*( |/xpqs۾e3Ep-4g];4xIT*w-v3tptжwn.TV5 9yysܝ?w6z8[ܓ ` *%H[3Hœ@ V2%V iа 0 l6u>aت!sO-dvQ*@chmzEpwwo֬g ][hXab- .w)*}/YP%"88AJLJ!c52Kv艓M`Ѐv"֜^1O4hc`0wnG u{ Qw F'ZKbU*lKd///OOOhOJxϋڪ𗤘...Cxxxh霐1sDbARqBAzsqvwg#&7,MnEKZz dY\XFKiР3ȷsAa?lզm#z(uqhtoua>Ay|GG (6kDaA>WqYM5w] y'wL&k.wT&\$!٣'OƎaXȞ?K S~;kf-x_AI,Mh-ΒA`^9CPݪiWx>9YW.],.) Y:9{4xIήna0J k8+rIaAYqI=WW1ϡvQTQTCS `5Z0w&V'zIF!c0 juZzz`ubQ;#{8:ڠ`8; rn8Pp9.]*Z,nŲ%/[ i4ChGixA, 믒Lq  ҠAOmճ2hP`@NX\7P8.H(.{4hРAL&y:f!!|BzjvmxOW>euý 4ha?a"燄4V"fqpp 4oj(S8p(4h-淕aΓ.\KoSFV>HAyOâAk Tw@%wEF-kB\77-? K놟4hݻG~`ѠQ[<<<#{iP:.gH2f6@Ď~@T!AAsً)2$99@Z/22Jp"((Y(ܾu 5 KvNv,qOle2Y(HIa@QC< bg...~ygH$ϞyaC: <>؁wpt3vܾu ҽS}'T&l140L]\\ 9c69J9+..05)*V|>LWY7G\u/ڹc? =v%EEU7H 4hРҹS)*Ao8A s}ڇ pP8F`8stֽ[111_5v8+p~}vWkGӧ}}C2Gmܷw%%%ǏkѴJK `6Y\8nuݹwM~uEc~ݸq 'hJ&:\zu-ډ|m."mk ˗.[VsknРAF>-b}0^4]ܷN}o@ //>Ug 9;w<@;ϟ?e˺u~ᣉ&rM;vN7(̦2{w._mNBm|||-YL?|^6l8yΝ;"">xVeSP8|Zm ~=}XϭA fT+ d#F%Z\Z4x8ݺy3v#Fݻ{ !aw@*U3W,w\nx9`aa ]rT*J;ڵkcƌ5jիW/^4fQF_v 0 6lׇ2ejFF)`ʕÆ _zE;⣇?\R'0f3gxt2 pt7}Q#Me?~HWJwx?:t+ 22rΜ:Zə1cfl+W CJ5jT\\ܘ1cGC٩3g)2-_';}ڴ?~SL» ݓ/xglݲYzgg粲2~e+ ! //oʤ_~6GMX$4P`6i裗/=֨2mZj͛7U9j }qp'-/w LgioMکSGdjBK"Ht«Y$ةӡ#GmRj޻^sKmҠAFʒ,!)9lcR aA4l ?`h?}mڴڹcۑ&OLîQ9xNv6fIJޮ]cǎ+.}$AΝ?/<{YӦd`6mN>#ˏ8ѦMk`0n+tqZk2y#YrM>\%#rI'$;ĉcǎ.^:f?qĄg cǍKNN4i&f&jjNS@x/Ysf߹}SXǏ͘5K[yb0TѶmo!33sd֌"QΏ6jݚ-\6g!1Cgϙ[.-_xnLx⥩ӦhܴiӦ 5o|s>[>?a"L8aML x>"9X;wZrkm>}:++1 nҤITTԍ7l.ѣG jҤIddKj);VC~_dɦM.^b Z+VܸqcͿh"Zh a]K3al͕@X G| -fg2جvaan*R_Px'>ԍ|RY6;?oʨ'VgNt9o Z\.dϝrS'WJڵ/O׬L %P(\:8vH$g 6|Zԁa9 zdϯGj~C sU߭48: BpnՓ/]VHt< TW\O,:;ѼV/];X,pxf>}nZPPhºy<8,2-jǰ\Q˵R6 l yӍ7_޽{m.???ӧϯB믿>cXr%ƍgϞ} ?>}X r9N'-!R (7(ӳg>kz3ŵD"IMNr;8yEQ}XCq/E%ID >,}}|WbIQjR߱Ƌ̠ebsKn؜P(˖-ԩرckH OI aÆ h :ujҥӧZ?9rdʕ ,hFx IDATWUAM˧a_ _iHֵLx<)*((-.wj>Ǯ`ȸ Kꎶ+uv!˧M3lذ_^=&9lذ߯1b=~tfl+ܾ}{ҥ(/5YiSYrKF\jxwOf TnTvI~~~6w3t+WU~I۷n?/X`ܹM[}G={vDDķ~KЫ"`0ѹF 3T7Wd9b@O> 'Q۶ʻpriUQFMWOMMQΝՐh]E5mc2&0Gɲ@d503Q5ݗРA 4h'@n^ұ[_F%AVR3bJ>T fڿJRss4? c>rbociа:v"""ڵk*hРa #Ť@=FUWDٙdVn7V/@.]U~nݾìsʰA'J_!koL :ZrbQ0ixWРA snok=FQQыDTF @z%AW c8HYjeHM<R@ڮE'.tkz[[z-,.,#Vڧ^S`$*/ȼjBTS%Ioii^^x'ɹ8Zgkd+Dk("J$ YS $ dDx"湺]*...AJjӶ5 m+wJZNJr0ƶB,B$F` ib$nnԛgvO!Dm$BY>`wF?9\o/7=E$mӹgI_G]dasv-Eǿ|2ܫAWϬQ]7ݬNN jGd- Ǭ{ {Sk0<}s(~B-Q jaWWόC2 01Q..>>>!'meYWE,z)7/BGKdXUƺ^oh{n3lv/_|[CvI~C~B)^hJICتz,[fgB.$Ӕb3/OKOMA,&IAϰ,+*.."|e9Tb,`Fm̳$en_`D&ܫ-3;+#αӿ/^X?̍ML{3[?7Oh޻(άvl[|e֕}Z8J3j"(q.$ 8gt:ڣ^^43%uЋ]ۯ{yzV4JH/~7=:q+l帒a kW\qC0J 77A-7mV#o߾YfQJSh4/駟ڷo?|___$K޽{ ,t444tSYbźu^9l/̿NUJ Rt. mL淍f0n\v)RNNE$ruqqsڋ.s`0P{S>x[7n_&Mt +WmҤIΝO8a믿X|ݻNvǏϜ9YE2s?s֭>ꗕΓZv޽{Ӿ\⭨u7H#zwhib$7b ߽T)ԩӫoBi2ÇI&892v?{ͪ<˖-_lc]Mzf*8޼呭ZzID(#zGfOUPrssG٫W޽{&Tooo޽{/^6޽{UJ%hϟD_|43҈H$ի׬YN>]IrҥK.]o=zY3}P񑭄8YITm]xMQ"L>=<.H~'o-ƾ}N87omi||͔7}4ҳg7n۷fTIr ?wRVI|[:Y՚*ԓHH##:.1!!?7բjžʵPx T-ž={veyy]jռy̙S~e˖UBG?~՗*ҥK.\8p@XnJMaXZ {z\7m hB$5n+zK)m(RWf羾{c =z32*{d21S??L<3>K0PH 8+Rc\2I!ϝy, 4ZlpF7Xճm*8+XO>V5VB)o?ꫯFb,W\pއElٲ5x0Wb"< I[v Y|L!LE*%#SLfZgzV //jc1JOe0 f ɤx#%MZX)Z"$sa1!3lLó8,lkR3$ /yF 7=4/eV 1+Ӊi4/_֥{[)kd"-إݮkvv0u ׭;v,G 'FRR˲k6u|aH:uJ"V/xzPeVm&tX\xdb[bX,K$ӿ:.//k@DFʚ7oK/DС##mܸ?f1+WF[ĉ DFN޽{׮wر)S>??gϞ(:v@Yr +VH{_ԭSvx`5^Yn2<BlNzWKm54nxx8)jg`-V8F.1>rIz t(?J\&O%sݡq_t?h*So:Q1|UgLxpkڴsg9ʝ;In 3t-W! mlU/ ݰ5k>5QNJeRTjM252W9sVZzGOΜ13//)K m')KK@'{`c0XGҘ J__B?~btO9z`~Qt=`vv6(qڶb{^^^K_~˔rLUgriԯtG͛afF朹s?~jUL+@ IYc߇޽{tlR_e2Y͚59[nʕQ0'ONM}Z,~hҤNmٲ%rssڴ1kK{z2ÏZl)!I*YW/يVg3 9JP̙;w SJ0I|w1rĤIG٫WRr1z>dYvEvDռva]FaPh&Ȋn6~#իW^ g~Aѽ[.ݺLX1jȒ7xݻ4dG@1nnݺ7lL/=Ѩ6:kR.7n6oތeʄcٚ*TrչZRX6iXuTH[;+#D*KMIeJZXCRJ?qA˖..k0cVXR-7BjZ]drLVVF$)Ν;өKW*Y 4\kҢi1j՚I.r`zKIMAOE"ў}t9 -lh*WYEoӪ:1-Cx]GbZiu&_rs)G)J'[[χ,ҪnYFoSUZ)`t@U!/OWFGs&sBoBjZ햝PjRVjZݧwY@o+Ur)p ` j !dddߍgD.g_ QJoASR3 Wԃ0*Bo+(I}#Nbˣ]ѥG,%I,!GvMl:y-ZoJ6\bSjV* !#HNN@'{`SqͲ,qN-[dPh᮰QR{xX6=-=ۻFfV-wMOIMmڬёR0LRRR+4%EO{H O\j 0fg:wc.\8OFZɓ.^P"[}l͟nӦ5KBHvZ5k^r˜Xk$@aVmMbX,ʔ IDATK$ӿ:.//k \> -Zԩ?~S$777 Aw]zͰ#[M17ߐH$!;nmS~Æl:<ՈQebkfT~x׿F:4~u$Y%5jm_ϗ_yjڎ/e,G3uN";p !6= $$t롃mTʥDhl x(okUS֭|#BBBCM"x Ed*]mu,|VgՐ{`Fۍ7nذaq2w;z___qqqyY|+e (eVBĈSɄA^d+z[ jwT01td!h\n DRG?o}#\j8f\/ۡN2D,&b1HH^^_D7Gљ33==P}7o"}ǩ2hq0HV"D$0WDiJJfZ] 2T,` cSj TA*7HhTh6<`wv"m3J^h5r(rqiJ<rG _UK*ܜL~M d擎O"iJJ5=.NY00 5LX|W_"X Qˬpq t(?sScuxj/%áVg6Vgq$?//;[H.ߝ>r77l|YVd_-0? Xt],x t"=8=mbwkQ CFfL.LBy:WFfJ,ڰRT&i*tS(DlE=m!^T[܍t`@^/K-mҼUT*]rss<*ÅPN]*JV`fJQXpwáz{$&%Ԕ_O}C9C-aY/O WoΊV̌5jXK3ä$ 8hnEMV6;8D~ݕIujN~|#YjuS)5}j֮][&P' Ys=gN[\.xۛma@Ykf*6S,V`Os_XuWRONnNIeJB"2??O"֬˖ieU3rru\KS&'j![̍:.òRT"W0 K Jpۚm)k1*\/[`VP-•sc& ͦ:њ#KUL+˲-X`-VD!.2"eV&FJ&jʼnX87P Tn)) ul0OFffD"SJIRG5wWCRni/\h԰!,Gkpmt <mӶqr77A#2 ϝkۮ]e] Kkך7kc[AM P~eH@@RwE ޵yPСGL*U@[ׯRnт(->z&u-<\[$]^{`=NeO:T`AjOINZ!¿޺eׯo.P*err2VѾ~n㱣˗. pRpp%O?dVcl+ov32qpa "uk{1&fzd7lT߸ӦNٶsWHH{)kHj*p*` Hd+pxVF1sq+Vٷ*aYJPlAڼ5_ޟ&zvy( .j![@qz.]!8Z.bXvFza|ݭPV"H] F#)ZazW0UYK X!^u+˲-X`-VD!.2"eV&FJ&Wq$ CjzBVsgϾ};n.߯oM j׊a,T;9mlh|qBimfC4jzyy>\;ej;T8J9eM?'0,k(d+pqf͞3:b˰lV!!!QVO8alDĆ͛Dz׷lBCrﮝ7_i*Ji^^^3 2 #\]]\D.ʵyk08W0yFPϜz{=gŋڵ#|4~]`0X6o1>>>˖,޼u6t+/42btn]=6dίvM[ ʰCѭkS>=rjƍ@|F7a°3ԨQ#/?XS V`WJ*+3çf-A#:lXR҃۶ 1r{Ki\e5Z휑#!#9|ϙ^y9'77a%ίmΙӧ5nԈ?1???4$M֟b>G`53O}ZN^}+JTV>tIrX\l@@DbA͎J%wxߺ̚3{CŠ woK$R ZcпΜ6FC)%r܊aZM8noޭ^yҤq#BHfffKʖ@jB7 z{tIiOg]ǹ)x_~">_tUV`6i7/_@ vWDmژpCVfF /O6I !=s:Cdffy7oּyx>/[nfS{ m~/T*exk=..4/˾Oce[\YQ~P~P~*UYѱv] ,:޽ж%9^80R<JMw![S%yԺmM5`0?wmv5j4 7\iִIKSK@٫ V6lA,8@ ,a*~GO)]p];:|KR5l+г+:ԢՄ TSװ^$L0VLP v>zݻvBn\-:BeU r X<+KN8N[ǎ._44,K!K,>qxʣ]~s zl-ZijIA`?pcv۷c0/L_*w7M:%66 y3mʨ3fΚ6}ʨ sͪU bhB2O Uf;ڃOs]B>p\Űlڵ.>0LO04XM Nv?<F#)ZzWP-d+`z,9! 0YN箛VU ZqΝ=O.U2`|) ^{`=fO]-J)[,V*8㿜<٦m[k9cFNNΨ!#Ni:nm;wC>own4Ҟ:{ΞuJ$&).8l0kիV~f3eHHHԪՉc#"222Dz>k٪#|Rw>J&*j-qJho+ 0 qFPϜ㯿4mj1{˗c۵v꼏>ԹK Uu W&L`Soܼe<`kcFǟ5͂O󅋖hZ/*#b8(YBG1tذ!CB9|33-"kϞ&N4qÇ ݲ o߹ͭQ``leBk5 _U@b z p6Rɽ;q^5D"a9sx=4,AO'HJ;1--ǛO!b{칯mhzk}Ͽ> jVRt, [GJ!C噑vbL,+(~HԺMS`LONFzQsrrΞ;߱}B1cOd}3O#,^<3#ݣOԨRVv+<Pj`,n.d+pDR ˒Ԕ47RiACWu~]g.; 0?oދ.קwΝ?6-zƷ|c朹+--da(c}N>LRa(gd{0ɫb:XjN5I)ŝۢ&[l*mєЗ_Ky;ve(-> ӸnBіDFSVL-i:E7A'YNĘqy>H ˲"O'z }j/;eXo aecdLnS$''WKa=ǩ!0 ᙑ~A~,DhZa.ڥ[$Ī31TChQ=)N˲.nrT,1 2 |"rgYOSkJ08(YYJdR Q0\ȿ|ԹZNJDFp6.R˲ ÈDaYq SK؎o:lEosQbc~WV߻־]JOKx\@BЩG?S~8x0 ˲ ÖCX]r!_E.&n߾Ir77A6 ϝkۮ]R[BئM;|ҙ}l06ӭN* r*[+q'js @o\g+aaFJJ)]p];:|KR6ltr,uG jJ{r6Co+pD$2B)[vIqÊ,P2YUMAF񔫫u+˲-X`-VD!.2"eV&FJ&U]un1HN׬?;|а 71bžkwY㫭!8T"d[no\б}]){V-wI8.rVk6̋11##_a}ƍ~~~ӦN*ȖwPi@dRs%\q{eԊ3gM>ceԊK/{ಥK!7m4=\n65V)[=<<!^^^Fd?]󜆔W'}0n޼E9:z܄_1SJ.\8z܄}5z}DN',{#"+_+*m84uJ d+ptzkܾqz.]vvڵ"[Z64,0GE}}ѥG/YZvv;i;pa܀O> !;v`r`Æz~`5+!|lFec[=aR;m٫hr9Tєܝ8yr^=gϙiƠZN1AJ٢3^BCɓBb1!-QggBpE.Rڪe0%Wc.B1S [AUMzBQrw䨈">]:b={:r;ֹg߾y{w׷y۶ј>>cGWD^7"!!!;;Dsk"PFb:*J6H  NHLlӡ]JOK9WBLz.]I~l՜۴дI3 εmN$kk@(wҥ&yQFZFGV p*JՍeJJF颅 w<(#^J*0KUUR{W0Dy[IpH ǕFoݲ{NBȍ׷EGUTR }e1Ef+eh6Vjw{PB(-mᗭK]x)88d'7c/dgjÏ1G*RWc3Y  1`Ka=pRzN![(F̅ ۷ؾŘky1&fzd7lT߸o)f X0`BŰА D捄yزgSdXd,x t"=8=d++eXxeԊ3gM>ce kEf*Byk!khLk&,s[^zi3Θuᅪ]p\Űlڵ.>”"֭Qɮ[&CswѣamokϾQ#5m΀~}<ӪK ! /_ڍ rnp )ArJFVz-:!Do00C)mJIMMd2BXjRR![-Z\ 'N\F`c0pXǏrdmo999F OKK#>\;ejdiU5_ݺwu&xL}}MC4Ww?q]=ml~PT/3kXݴe Zz c#"6l<&bԣGXeVZhqEW,3|Uc^ !ؾ} //2` _ Ko8jV{]xQaԹAK;qrÀ vwWWmJFzZ|baIIo6|A .曙&h웿\'tN@{RڇR4�}D.YsvzhXX1w^%Im>3˫^U x t"=8=>{zgRR8!_֋Dmڰl`0<=w&#=ya mnC* Iy$Sإ7dRX\RZl_AjX,%HJ7׭pbP'c[=aA\J4<<YfaTCpei$@ԭ6B5?Y\\\i^}-h!kٷ' q!.21T2a !e_`pP2I$r7LO)eFп|9ZMVb:*0(HP(8JU"e$z[}1?U}?~/yzg gj_OP 8z}_=GŮb7(bT5{yH~XN&M]\*kjkVVHRJnٽk'!ۢfPd2ߔ:U 7햍?;|а /,]=K~v1_p~&,Rcpt %Mm箐 <$c"F:j5iFV* !#HNN@'{`ScݸQ ?0qV\2WF1sִ3VF(d.[yA3Y !kVR(F{xxB6odƲ1^{c\^߿Y/^4uڤ)1/QJL7#g̺x)vc'N95vO MT55|.Zewc0H8:5n߸~s.]u~Z]g`-VVZˣ>|R#,lLVVb7zԸ1|}aN0 r 4k9vDzl~~~-'OM,jaC#s88'B'&5d{RޱF.SJ5M݉'s6m jڪ34d8z-:!Do003HR]5 7ViA jْa|F !YYzCvO:\T!.RR3-a~XBI*Z˧D)EwFox[Wb.]:z۶MQMlР]L Ph|_NlӶm]BŘRٸqcB49cFNNΨ!#Niـ>z>JN r/7֕ҿou_nrӦ}X>7vlĨbbp=T*|yr=}~Is2L#>$m_]oƒY猎2-[!vZ={GEB>~<0CBBVRqcM]BCCC_mXƠ P̱ض0 ݳ)K___qqqyY|+e (eVBĈSɄ5]dɣh촧i5)" 0/Ws]\;0B{=gŋڵ#|4~]L0 ;14iOuF6уh %#=M(6dPB#WL D* 99Ղ `VJ)T>"a9sxTYzK$P]` wwϤpBD۴1]`0ަ)))0>>v1 =LL@Xf%9$d`>|h洩5}j.>~bjz&9gy[CV! wn#$`~0f0uI']a)J"++kaԩ]]LlA-bAUM TTrْe˧M~æc"ذiZڧ.vTOZk׍3n'BaKUZǎ7; defԫ;c֬wΘp>L{0eC9OR?1&b].׬-dabAB5We_'Z2ZB\fe.DˬL(?(?(?Lb.OSܡ}vxذvm’~w ^7k\._׬,~scO̚٠~m;v9wa{v~)_ ?A畗Mw_+W^޾eQoڤyHi֮RVW7^Վ! jjދL5ޱ1$Rp_B~˵p={ h0ù}Slgƌ3zC,K5aBW7B88nqcF vx7ޕ. '**==Tl0̣dBȹڭj/8kբ0쒻,YF]-DZ,S5 7;ҬtGc[}a 74$?O-++ߍEaLwiS&,ZU̷خQ#{xP1E5SJj T6nЧWM[?BZR=oYԪ޽zvڴYs,}.;;S=YFe~*H҉㊬15nԱK;.}{Sbh|e%;02mfCH#$jQϳYr*~*r) (*`<FӣTDEPR623DŽe6m7̼33g:UK!j0lB!Z$)ϛG!jcw [Q[1r8pCBey ԑ([322~Cq@5?ݾ}{!NzH6o1B[un}L<B: V [QCHJ(x(._ òL-Њa+mQ222BQJرcG!NfͺH6o1B[ =UFFcی?^b؊j~|&ATjMbrJ _]{B{daějqQdʔi. `9.{oG?DzBpޝbaAaRRdff VԜhun=`a!P9iYrGL&< !LA .X,_DGG@;'fjFU>Q4m6^!PX:NRJKJJBa+jNU3RJ=>/HTf߽vgfE'_7ftKF$BU!j^EQy #sJe$6N|gf>jgfBp,Dzp,P4u)W:VԼw_cKm}tNNm SpBHߍ:E-S =zrFk [B s[Q [Ŗ\rvQ P5FeǎZaAR_D322Zn#mE0F!'-[M7[薶W(}Bilb0Xm60vBmJٿx:cߺp1 tKL\ǎFBv)qqqN?~xMB 0s01$1[<y`#zǰA !z^TXfǃr@:FLmj!Z [Q \yZX2!qgo뉶^8Bb%QWdRJ8d{u[/GXivaFyKUD322p BV9"B$I@?yՇ1 3iݻu\#;`WdVmݽgٽ{16_!j'7x [1lE%Htם=Cdǟ4{ֺO>Ǭ{oO0 Y^ֺ^->..ǏiV蹡bcVznFc6Dc6fxO#l cVԜ+2]/;Vу>u֊ՃܷU|cV{xaVԎ*vڽ[*7ݫ碠_0 k-)U(^;09/j5?_?YR%^)*.b]pYB1 wnNo^[>H=#1-0V2DEju}r!F0>o /PcVBUdsٲz%]-ޘRUi]Z;:ڑoаը7ݶekfKZW(}BilbRK6(۷h* !,a ŬWuZ cu_~a3xb&k _v^i:ױ#Q7ZL1TE6CEtDQ>OňUwwž~81ahs="LK;%E iЭ.ԪSE!DoЫ xNQ/GO^--+&7'2}m-T-.B nruJL0!9R4###D^5.)jǎ!҆:5"s}o1\RWOo3BOvrzav{ѳmUFS"3ge>XYv霬0@^h+.B6N$٥^Oka+t)p\[pyֽFܑrvIp!'Io|k9ѣwRtZƓFM(Z3[B5Nc))):xPV3DQQJK;Xt:mRd:vba=Oc/aX$:uK"Va):Z7r "q\Navյ[f~BΟuFGGbĞݻ].aZƒ [ZiʰC "˄*-&Fͳ}&]A!4sتP(KLrrO8VZL+h1m/+qmCmX;YYPU?nܰB ຉD\\U&?^ӬsC' Ƭ6'llϟFX [k%4k‚Js>糗^]Tn|^ڃklǸn"%5t8q"[]+wj;kXX!PRUn+m2Nܫgo0LJZt8.r]FoMcVSUCp$MOOLLX*Mo_:!j{SzEfΙ3zo6>f']~V&Od9d Ft;]Na+D\vVM@bm#b V:x!P[ӐBJZOFwwnMH0,^ZPisŅeŅRQJ9_kWSKĦ!8y0!ˆ8e(PeYDAQ]V[=u'ظ{*Ǭ*ҭZcB,)oeQk_%! RZBVC0ƭ!ZB!6QD(akԣ@}c]RanS{U,2 CJKa+rRVV$t*?R/dd+BE绲k3g:X:t5n4@c$! [Ya.zjDZ0,!Ra C(R$Q(O&^[$?"]OMDQ'&iv[Pj>I9qdMcT%KL_cZ˲,rqj2 0 C!JJgP2̆jE&8a}B|$Z sO*).=fLYxr[5OMge9.c c !Dm!D"0D!Zi+$I2(8.<ڤ2=/?Oj 0@)#*orz\N }+˯k!P빹:aa>[la P@3FѺvPq\ݎecw88tXTYvjڷKdN*/&V2\qB ta^o<}|0)TDAB$I *Al*^#JWbs8dhZ6Ef+v Rc${BS(v^ūUJ%I%&Yw%P!i*8J![Mo0Dt9={b OYCJj#?dwBaMî|5?p(g_UJGe ȕ $M2=Q ( ba^vr⏳gN@XӨ_8܀RDwƬ#︯op7i,,*4G\3w;Y~yW~y^}ߔ)s~k:%%(c$i]~Ax/rcB) + ֥ɓ#F]s!l7~>#RfYm@TyĨk;u³{5ق[^Iն^S(>|^aD@2D"Ww'UP^@H "6e.ͅ 8Y}e:Owe3 F;&Nԓk֮ׯ_j3z{@0b̸s رcԨQ5% 17t5kw=a_m ":s)Yxznil>f[:?!?^g;?0wm=C)#ak[pWjvxF[NBne)K*]LR-IZ1v~<8x8k޲eeW}_wo-ZV\e2l6XY֪56x¨jX}k萫k26ovϝw@>ͷO?u!+Q*zU(e ,Ҙ!ڪůwN]7JiiJYH a侮*t)rEB*Q<ye6Gjl4)9ͽan-.۫gټZ LI I#B+w}NI ^~aCx<+/{}=ͦ?Rw RkU*Tyw6'vq!P}1U~/oͲ6tİ?bݦ4V*7 *oj% (W$kyKJb[lΙdҥ{}\X<85}M&XuX}r}Wdﳿk8p0O>n7HԷw8e쑦TFNd *)(asUr$IH*¥IB5@uj.\=$I gT^b32Sp;솨hNr%Q,-.P2 e"1. ے\Zۨ$@.b9 Ŷ-??UWsµCYYeeenj &`ۿ5p,U>3@^_fAő4ٳYԮ)qaW΃d986Q^9iYrG5+!jP= r1c;?^b璉JL1eŅK,g[⥟tPVVBBBۻOQwzڹS^{ךR&kqߤ`-7_ɓfs.̫TL>(6W^ܩ$EE .Itg~1tW;vyՃ?;K>Xg'ʭP}3?^Eznws59B!/! OiZ%Ba+Mi19;mRvFoݢJ .jRiz%E(;׻G[xv50pǎ>xVpeTl%&&7l~Ъz@.SN@߾}8>|C xjZ,>;1o/¼_xq uPsZBm&e@e"Gº6Bc-gYiYqzc'H퀧EVZ ԔH֭k׮S$t{?'N +H۫g)OG}lO?8񶫮PSbZl brr2hXW }]]q!PTMSǎY,1,=Ocs;'%ͫ4b qK<}Q&SZj ~ym@MCy蛞mT~m k_k[~5k>:zGԓO|lݖ{d2qj\Ytt]wݵjժ7v[y%*77`Jtپ}{/;j{#Um5%cϾ힖&ϕ d{<үrIqaXMjT"DV!]I{iŢg r X/ۭE^#^,,:{-<"YXON'z밮:%I4 3f<^!W_xq?N_5ͥe|>xVմ=,]40\.O<~\%^'0L\>3IIIn8y$Bo. !Of2A!UmU%5՝ j^P*s,Q)J}>Q Q:(sϝ{a݀6*a$6F+g ))I&ݷU5-YϾ6/.W q2?~xjjɓ'n:dȐ}?U`ʔ)=\䯾ロP(Lb4oF;w… /_zu=;v|QO?o+{ر￟y(BՄTʦ(U)1&cQRfEAqJ8!rxl2Q;%aC\ͷ>z гGZ~yVaQћoezJ'gz%K͛jlϙ=ؾ}.]xe˖QJ{}?o)S[j' 0trغn:8qlro6 _}wu˖-0i$<B%kNODW$wX-9N?v0,׳[5ÇvrƬm).@ZRP*Ewu{=?X)S?C>mx2R9}W_}uԩVRN:۱cZ馛=6l{u]kϞ= 6RBFgΜQF%''oܸjb jʹ ԚdJІ;u8,GWf4Z^c=MD_M]N)1G~m˄ר:ƝxG%:ױ#Q7>m@||[o /8Yf9kw߭ٳg̘1O̚5k߾}/\^űcVM/^x{gܹ}]q}B5 wJAY2{t\5r)~; 9cS}8{faQQf٭5k.ctõLBJ͡~y^(yTޘ֯__u7|7߬4=UJdɒ%KTMxŋהpF 1bw}x)"P8vU!lu p 4~ՂsQ)K#هM&~24{65EQ^~9ztͪU Z]"#IyW?|>ɓii=h PP]O'77oڃ~DeDi>WS*<(f̤7ϛ}m͵4`$4~M&ݗ]un0D!Ԧ2Z݈8>OggϞ7p}`#?}?]?GJE5[1 ケp'+CyW\lyh\y 3zru9_дC޻o%ˮwݗ_}4FBP[lậT[ؿjG>o5FJ\JѠ}M[jiMW7_|?8Z\ܷWJ acRΝذB'Z-ne쿿c|%IZç'?p$I M ծ!BqޘNJrm$Qmߵ;77w&C<}Сpmw>(ڹٵs(jj|o;鞻$I;_3ߏ״Ҫ?!PWJ_~;oi%r5oNR9۱Yg? !slAۻwGس{:ll-l+M5|T:ļւӧsL&cՕ B!a+ؘÏf<\ƨ# ]{n߱PO)hǎ>xVpeTl%&&7l~/ Ka%J~:2EJ{41 #B3ld[<V޺{U*=8?g90*-v&RRSN'Ruڵke%֠jYYG{\o˄2tZc4Yvخը ߇#x !jakME4jn5|ǟȑ#0[cBl PCԐAc,"鉉K }d2EoE_B֪rbPƯ5֨( nz!ςt+|nm^ذ1x`HIv@tky^Zȍ|>)fz3\B{8@1tVpKZt虧|˖gewL0'׬]ׯ_!:x=wڵ{ȐJv, Gm}af+B !RZ.ñJ)X1@@Jհ@: ؉JɃC]?!8U*q È(OZ;wN)JZQlفCU[t+WɹfyUOV0DJ+n4ر롇ݷٲLJCM:IBb HzEfOI@J0˩9VP+K>9|<-J8)C_O5^7 *aI!j`0B=JСBuٲyӬ瞧@6~po9gk/X@&>>>0%s/6l y4))Rn'%&ΛO s{W|a+"j+2[TV.$ax+h>m@b`gPsc*'eYYxbgB<l6ۀ@TVHl0wΜ%KB}f /[9)QS׋dPOj :x]PIԝ)IJIQ8GXr'(JNP(VEQEA f08 f&;w.\bĞݻ].afsmgXu⢅ H_>Ÿ~$"Ba[0t9tYF)Q@)PI%A$~B="TfVZ@I$?~󥦦ZV7nn햰͡5WG;YYPU?nܰB DQx֒Rz܍ȼZST\08LBxo:m @O@&AM   JT$Qh}-c$I|]t9s֭[ >>^Tу*Imm?JKDJjjJJ vֵkJs˂vaFe/ *h>tHQ^"𗢚B W %JeKgK^ +ψ Ju*ǯR j=4oqqqr(~ߏڞ<1%师@htAN;RaQo;m>}͖8 ֢o7}C(MLPZJE!Zj+ jcMIzkɎ9gV:kuIn26REKLLZKK&.x:cߺAu:%&cG^UdBݰr;IOb|"F1H$*c.%*7Q +RJk-*tE~޽{@5/΢u8.K$YR}PūT ‚f-AռJT0 zRa1=OVNQӯ h1 5Ce5 RIBljO}`m\{ml\t]PI*)ڱ8czA!VS[逪õ&iDee6{^~J՚})%DU޴\ z}. Rk厱(B ~lNa0J%_|\hA"T8].0XA!P[[Vr\t߄ͱQ;gr :,*,;PbfۥsN+YEU(S\,=? kt^ ! ըyJ0T[8 !"^!@ h+Pm˕%A.w.i{hڈn_Ń <;b;$t8{Tyn=`i+H>Ӗܥ&) R,܅\'3YA!Dpf 1D^ ))CN g nG~D6W%ں3l6^g1ܴj "B5}jxUW V~ѣkV4Wg0@yARXQ b@BAdQIn+tb6jFn?`=Ǐ7.t*I\5OfO>ѯ_z=F@!&EW9lp*B*QGSJm;]qB%/Q7.k :-[vPvu֢E:nUrs(ټb'Nxg[|"T]*@%IZlΝ ӦfdgcwBmU=Ã㎿ߎ>nu>6#0!P7Wu;,uGcGOK2Q~M1`o8.z~^>[^}vq_"ZԶ͚=Gf;~=h}{ٽ{{vv\C 3͵>xMÇ>D~םI*-BFUB Ο;w2뽟\~[oŗ# Ի:^{K^gm7]w? Nxq1Zq׏w}ʬ *KLLLOO7nnX!dZRT^;,Wk@!֐BQ8U >[o#kߎ׫kZJ~}w,xm*E/o⹻oUW@^XrQσUGi5c\D7t:O8ڭ[׮]+-+ŬV>rnlRk&ˎ۵5`u!RU%OR#'J};]q++5:"UXLZHIbku_'kؼ+Jന yԱcK %'&&F[, p1J[zCNm[nAG@MJc0lE!Ԯ(ʆ-YVQYWgī|fk_?OHW;;u: w/xcvX766oX0%d63Tb "b㿗XM]buBǸ/~us;tKL\ǎF_7Bve9N'"5fN؊ Ta2[BW_ [)=yaҤ{_.}$ISW +y/-- cTT4h(H矞 \]n* `W%Ͳ(yUCCޠW)T`:F !u#jW\BY@?R))U2 jΞ9seJjʗׯ_UWl2N8"j5ꬃY#FYa;\RZzOQF`Kv[[;0~Zʕ"7D! )8[ v5p EQ'3B;A!cG{,$!j./^P$b0T*%::,:*^jb@5I&B!ԊTnV K@f٭a&!Pdnw8:m֦ 1D($!j _(*j+c jg1l 4D!Z .]Ӓ 7@$bP0,[Ɖ ,$!l.\M_+JA_UNOLV \r#tiM2Bft3EE 1ǃGe+dMuEUQJᴵ^r8ʓD!/IDATcq,BnVJ5f'~s7n,Z5 [ZO&BK>>OĤ5Lxn TU]d"B(cF~|O2/cUa(6]VX[y}(f1uhBE_.=C) rךI%;n=>mgfd7܉@WDQH XhKm}۷}V"ZZT.-(ZXW T nv7{yl6$K|?̜9s9ym}P{.zF%[O/N>ߧjBok_[:-=L沿NC 7"z$bΌ}z M*Rpӵ7Me!WOW\b!#oEx=2KvWOWs }e ݝi )n&Ń-sMs=VR;TU+WlnUꫯ7nz}&3=' [-ٺN?R씔gϹ63|u"[g= / U O>~MKoMK.9$h4utwBa"3P9WlN-5`lqlHWS5D@d2*Ai4<o3R)@ʌ%ߘ{6$3v_YeY#uu!D0d,GIXg{{42d9!&$=slyW~cnjӓ ]P]C ̞_VLk CB43eZeY:UB*,˜ p$0NW/ZyϽ'7NsmRU@žD<7N;`0\YUc$kOG:Ѳ Ϻ0d+E4[O0B!Aڔ LM($I$JQ2,;y=)KYYF˅XʆE| "ǾD oJ"V'5}RL/>o D`}α.L6J^ò,.>H4UnP(Nh0X~^kE^ZGV5csd+EQZQܳfy;dǎN oGaB"hq9Ģd$WjvL)S.uzA04ֿcwt:x[a)hq|Y!D" N(U43ؕ^,_ϬR˕hxjSO-#4Б>Gq4+V03oֳ5BP] 쎞ښ*eUU9rX8 pE?qC H82GrVHz^Z 64v%BBH' ӤX43Cɧ43. #.s۞~yĊ 䖭!^**NLJRY,[y/ AxrWnV}cY ϓxNc=#p`"61x?xA#q) j]߭xjM79}YFfX:S{ϋxN*z_jB?~jyz]YmXok8\Exky^9a0jPwHzkl0 56N[vۭn++Ok_}mI+濾v݆7jF:e40IR,ǃK&Gr_l YdvfHC־,qYڜfriޠ2l`f5f[:]% #qQ7gYu)GJb)+{y͚ys/5W/{yQOj0x~Q)OѪ⣏>:>Q,Ǖ[-VK9tfupÚW^ϝys)yA'f+:]>tn¹s[1B) >8Kl tuϫ9OXSlN|ٜ|`݆?,^q9'NR%&KeV)&dR2K9L&qʐ$pRWlBa\rq%GNt!00X-4m!4k9:nWSs&1It0&[`ۋt`^iJ]4MDJNܚhQjOR:.Q>!-̧wqVBx7RK&!Lܾ}R}&#*+=8ROOy9Np8{V>?kփyx<_M]=PN0Bapkkrx9@ʲ\ QURYUUQY#4}iu\Cˆczt $B0][1%>X8ЁgBX4ƈī&Q>Zc|4}aZě1gDC2W/ZD_{m~7@To{g˻?ϞveÏa&#낫xFÓĸ :}֦9sʬp8V^۶ ò .UEM3Cz!&NXF%)rӑ}> ܹ3¡`peIsH,Z<[TWmb)82wqpz,e]u'MvֲC2eYn:/5nm Ϛ';mzY3ώ RUY|kW0T_g+(&2hkmnc:0h\#YQiZ d,O.hsG…RA4ʶmVrL->gԯ14Aaa!hx?U5{WG>)H8ci\*rw76O;lfh: _ok̥Ŷz 5׽_?`\zgGž,K@ f=<_a-h4n@( M "_W]]Zj.1x/U 2v%lBB%c\'iEa$^n8|_Jo(X84jfxJ$jPUa;[{p9u2WcvzܮE!^4/e=gss՘i( _]#)HfssE߼sdvcRCR&BT8k`R.-]W~=gXu hEcx-c/l64PEs ħQ{cW* %0FCΐ,<4DHX_ATw8P8 C;wM%1?K!Yi5",:3ۛoκ`ՙfGQ5k^{sֿ6lᰎNbϟc@G)pyyD]4wEU+[ش!XxշĬUջO׬~)3Эe\麨4\:(2Ay4=~ڣj)eeYeL.m7i㺵k6{vjޭKO?!\y_ee]w-2yrj!,?355ߺzQ|$IKoYvқ^˄[o邻/Bhwoܴq+ZZZ&%O LL./ !8 %dumEʭaƲq"[ Lj/[D"!/vwO?|]!_}|wժ{d!G~Y̿xCP{{gV޽祗WϞ}k-?#S&qӛ/fM#v{kp_.6&IŖ>;%#L =SNd۟!sc72nD9؄3ǝs9, Dq7n)5W*q;GG',>7(=.BEUr3EQnyZfIaKJB$9N 5]>$oY/bc"O>h| V\{|cr3s,%b`ݩ(c#R _x"xG_yqɒX߻VQ~KӠ3Μ~S^o?ӷzǔ)';Vh8n(X\h;4ضi\>n!WT{y^WwʲtΝ;Ϝ1# I"(;?X>ĢEKޖ=ʪJ+;)uw3&(ᤃ_~ 0¼`|aF54LUUúTxA  ۖݞGzMNIENDB`muse-4.0.0/src/doc/pics/main_window_with_tracks.png000066400000000000000000003574011404105311000224000ustar00rootroot00000000000000PNG  IHDRnasBIT|d pHYs+tEXtDescriptionWindow Class: muse2l17zTXtTitlex--vRKR-*H-JKM.QU--vRp,*JKO- E\ IDATxwTEֆ"Ir΂"b5aNku\W̮YsF sΈdn}tg>[u[ Tr'HF2d$#HF2dZN˱1ʳ-ɫD*UU2,t=HFN$W9bzX'StN)О&.X)h1brtD'GKr(EyP8giğ'tO*K$fWYzlWW/;*XĈ$'KU /~&1n椒2iHia*xNE&twtSZll'oN2>6/I O$sIiɉs: aO;@MHr w}5%:O0J+,dXh+ W3䲿T"ڴijPj5 X~-UNVVVZGbÊ+݄^R2q*o@)a2b >e5zYږ:Mo qۈ`yt"EႶjDWVzi0%S: Դd鄐i/:YNE'Ԇm Nxv29tt$9̭ѓ*عQm5pêeLu:<3__!dWvZe}ƑcSppҴISK'$ߏbФi3:H8WF*]$$1^ /ˀ*bxN1?%D$%s$PG$wdq$~ aa|mnX[BOGv$ "?0|BK$"Z_'dmWA*T[7nEƍh>YzR) shUʔKq]4o҄MeFIi4djmQSS&:Rh:)AkkGvft9eOeЍ5KH)Dnd2$a]f!.Y39r".)..).N ۔TWەnhӱ+zR77n:lSSMGюGYb{A|sּGJ6]'bo?1B7,Ae %un6(^9C RgDON$3޴qcb?<9r$~`PK@4O*^EO,b'`0 b)(OȾ2N.ÙQ:f-DžgGv;{XI>X XZCC:oɖ,8$y֭C۱m[yϳz޾m(};mִS!a :c m) 5fEb*~ փөbU_>宩N,ɨ w1t[ mc^­x1;+VVT ņpd;;`j6n^178+Z%5B@ڵ٥Q;'萭< 6=ܟ/| "t_G>.o0g^뼌gF0 mO]^[V2 ~1L1ncftd's!C-ɓs7y&[I\|6lYä lbŚm#mb7^x~66&r4 39p۸iO7`S/*h-d[Gvi5ƓQ Oʛ/blܸG]C-reЌx'"އrCw[}-2z0no/EPD%bcs>L;uny]u"##'ªvh% %}?݂q!k7E|a~ ڞ Q=m֐_~ᣬ^̿uePoHFsd"/asC5(b~ 3,ڹ"1aw1soEMq" mR΄{q xha!?8`g>->Ӕ)|gۗϿ'_~jǎ-DdΣ R`/@͛#͛bo>XH;LbɃ'  4ҷz;2> )gB&Tlr`LIN|c miC=ytt4) /B2z:eQUmÉefTt 3ߜ!A~}s&﹈b7ؙΪtm>8ɶ iQEe4ԕA{%09ayINQƼ3'\iqc*W-7L(/.Mϩ>3jy&wlJJ|1~N-\'cvF>xd1c"5j 3~OdEl޲M7ӸG|\vfPqq8p~ w8NZ̖5Nh )vKO@@Z>E9h?諻ߩ]w?lmA=цawvuXp0lH15 E)!I6DXXʫO]?G6v)19 套QCܖI\_XF$'qF)LX"6`-^Qv ⋝d>Fq̣&\彉W[f56ig yʾ/luY=+>s+L>"߃W f}=/۱X3^ u-Cbjn +I Ya KӈWUBxJ\b/]DznW9&ͭN4;[(aDh"04nygG\ '{|+ξ?vqG&TW]E0d8j~xwn+ Yz ʸ +xSƶF~f͚iȢ@ o\qkaV 6TuoR V-|25Q( >żck? [ô 懨&JZ-MIIEOQ-~ǒ2s"ɏ/:Ӊ%.݃9 xJ{8mg J}åRvn8@sgnM-yX~vT*eYl8&?ΖiS#+5{,=Ȓ/>i┑xEd7|\<ٺPT׻g}UވMͣ&dȳs069O?&h "9ǻ/gxEݶҰfX*.YcxE۶WX@v+.1/~únB7G# r`Cn;W("LG]M"@avd pԴJnRvupv&|CB@]Ppٛ9chnkM#mg,ucٹ~;%zv3%ÙAfۘ`L.)LY9\<=lNMjy14X&ygK\iq1۷ld̻+Jaܶ?vTa ҥ _}-^O)wlLzvlEeUOom|S@dZHa6}TՌ=/OJjfŪd( Y9,_ܼ\U͖dJbaSvAJyV6iaNT'?D_^xR0U=EФ MV'+d&|f<*a?@9\3 lр7ѿsG:R.gt|ajͭڇбu'7>Y2 iwq؇'!w.繳sA/ڇ{}Ni׌*V|tkے/7Eh%KVm ϲ Y 4D^pō㇟cv t}:uɷk .x):~{teOȆEUXs׀t܃K߃ 'W+zPt1"yV?1Uġ\/y RЈ&UC7HNu$#3>W\|EfQTTX&>w"v.PR\ā}{ :xCҳw-W|D\׭1͝8о?]= u a݊*ϲspQ?]Hľ}{Yf-yjՒ`O( ,IX6Ngk׭Ӯ][j׮՚!9$zKqYoWcIY JU/JtL"R5M6}e JCXlyXab&77[ȊpK;yy8m@"ع IDAT;9ygBx]ʱwɂGNN{RK0,j'++͂#dc$F,6r^a{wRР5s= n[FN+,Iȫˆ3AbǙm'vB~?6YrXܳy8KV!EŊbtbZe ,JT0%F 93[,NHR@4bsG^84Dݳyg }buD}$>due!ֱՁ3ˑL$ lV|ˏ?(!φ{699؅7qՑK#ƙ ʾn''A|k;96gYYvQ?/aa#'?a{wQacNsNmFnVo|x^B8ȫĥ^{w^0K<6)P:g5QtS5׉DFF$稩֠$ө㕑*!9yr"_ {}*.nL5ZRVSΨnY;\Uh B#V3ΨE0f̘֭1FQf BN{cnWFV8ZBSH'rQr;9k>"*BP8>k-3M&Ѩd['",rgu",O-&d!K:` u2VAEMyqxu:qBxKї >w0gQ"u3 y]R"9@w~x6>Zw064=_F"L6"y?%d"-H9B~^Y@H>mE/*Gy|&M!|Y5Jcy\{:3j'h"J|ހjM@EۅZ"[<ʁpЃ+6K]AM@ ic-U5OI6IO'e}iz(񹂚qAQ !5< p'H'M,>t:fR\U20͐錜|9|t:&x-c6u]]9C/K꼑B!><`(n'83kզZIF2'IZ B˨SIw9E'w:^'8kRN+F/JUע ^],pfE/LH kLӐ$b#Kӝ.)#:[l8sb;Ǒ7ܼ ji.iؤH4LkG=A^7~@ JAt'#x AVKvdP6"n VduFr"_;S2ޱmk~]8Zm *ZAn>P(n';;\G dOS\: S&&RR2> H Bv*Zr Be%AZ[­Myhrj+rLwvUꞨ]$I?LG!/Nu*|pGۿcҨI 5Y&bt:q:@ HFN>2b""Fj=4JG!1ԕR[LSI`F~YHv8BCd $[@^7ы 9`/\QCe "pȳ iC䘑$%Ս+VjNFr"_;A ޚd$.?5xǴ|r]&sI6?T}KG_gQ}gŢŢ0(OTP$  JoSr)N7I&;2Ҕb<0XЧcu7p\&qVA,S ԩqro>YNZˆELN.AStyq2d:#91R3"!JR# &F:uƛДҴʗ=aG6BOzbEbuv㺪W^RQmM~zEiDD6`;BkGg_#KPngK jT:RQD" X&}$ }c6U:$ZҎ)OuM;5J42I~ɤ"tEdtF2rr$C3*1r=Ln$uLqTZ0-rmb^YU)¢ыh)-z4K;J02TEAcz\m4}^ڔd$[VDp"8Rin~w%h0̉P:W^53&&sK 6'5)]4 tJR4HqʻIAuS#ǦOԉq ϷLi'S!$:Tl2^X,ge}F3[=ÔpL 2/e`5 -m6Ciw$2C 4$D[av,G4#Pg@N-&d`zI'k:&~f/"=9 *a) Ŋ'`)hUTH^~Y232HD8&&f)+IpjbXWN}1j 8g$#ї rB>.SÔtTڅVlSL~da84WFZY%!ZSN5vd[Ep'쒜Hn;)%FhE/:Ra0%]{P.9xRkn$:5rl뛔0*+$㦚e$Ӡ3k XmԪ]]ρ8]kÇ9t`p仚v<5yvȌÌdO !הDe{Bדc%'٘ kVxT/4{1zdAO[Ǵ4Z_mU՟@J͕hTfirE9OzZRYv4HJ+)6K~R tTI q5OB+HF+AA>hHF %6n]Jiq]=Nغe3K(}ؼ~ v5C9Lifnǎj?8YY}pbC|?<#!}W)96sOgzC>I6VnƉCDET⟙.*]ˍ46jt 4BF' "KѩYj$umT_/篏IҨEH ߰d-dI?l5sziG6{TPQ3m]9BoX;iCaV$EqM 4筌dTbjש?tCUZB:u)..ٹ|NUP(D9s0Y<ؽSc˯M'BL6SdHuF2r*Il!HyO[-=_tWzib4[iMU b%R:>†΢^֦JRgyc+_mkb&B&'=NS9hgIEUg@y*C[c94tF럼\רAl2pS)Io302u4X.Sf{ qd/'pc!#Iaɡ1pa (\ňmtԉ|MU8ڏN~,[,n\j5U%=R^ #X_DO2l %hNl(pڡ,{7Ƹ=!Ytc-*UvB{qXtB+o=x CKo繯S\$_4縢_νo,\j"9XP`$] KJ%H?ɽ ~ 7Ew%LF8(aFz!&liK\k>y.5<|Ka~vq0JP(/vh8bY_:<@pנ;R-6:md(VGץ/FQzB,>H|q4KB^`$(}ocvi:+Q<>wsdv}UƦYVC k6.}E>`!uTa[H* ^Bw,]QsG.ӹ tQ]`K|m\s_?fo 1'~D݃d^AdN0x]-5O3<;^[ )zpRW`ɧ2+;g$#H'!Ա HR$''>??!'/WñZa;b1b#x\3 '"q'葧؃&<$ԧ$'ٻT6,.|8ҧsˏre3cmaܝ#0G/.UmyJ)t'x[N1>z΃ Gy46+R2^y9mCO'i=^[F.r7<%82Sd_Χ!z]ڛ8E9k$s"6b`/=rߓ7$H?upe;-TȂ6&l8nsU_lE^[NGֵFuLP*,R!'lЦl?nC"eЪ*#<+xz' 7ru&SwIY@,Z C+uR>9pjxP!)\]Lłq_rJ.^U+Jj1CHa K/CcX2u*W)Blv)dnVJw*_ Yտy8W?X1LE Dc[B~ 5?DZaO2bhKjP@zd =Xgѿ8,Dvw[~Fgq;{vUiLrnNw"}t,bX) 4zRYōC5³ KTkQ!qTiFM盽[5D:ZORܽX-@_щ[nN{bZ U;,aG]AMƆ FFHnD{mLv5sERߙ^DڹjK,YLAJ^RTm_L=$];x`!83QPP}0(<=0퍞LXaΦvFvtEM>fcÑhRUO]D߫K6tԨST$`:xJxISËwa(یdXR8'u_O=q]J;ի7 t KaJ]{]n՚f-Z1R8)H$I ]k]Z]J>z eb-{/e+yxch qHָ0XNoR0b-s~8]h8%NTyHVCWr^\v,- '6/ȡ OVG:?ma}B:R{zݛNd (^Ϝ(;z2jwm1~O$weog2mTea8,>=|M4vmz8ZR $ p1cﺄA_S|m+—2+")7^&[|RPG=^xž@;S*8mǽuZ%B-rP:|[Dޒ, >v8hUt1]csܻ˯tw{sKpo>^=o!aw[*!Dh ;>ݺ3,*  B-P5{Jz^Bw)?LA0~ss?#+Ýssì,]?#Q{roŢPp}\޳ gW_2 d'[{Ǿىt6+KWpۙ[*AlvYKx܅baJg>W;ft<ǧ'`P?[tV #$,:ta)tl5ܦوYvH߽dl}>?Zх\v#_m-"x! …KxΡ܅nł0·W/OvGu>xB>W_*|N7t9;xoi\ zgvο)\t܀,s>%d9[]v?-=F8֏dozcY8Nq>rD;[k]?>El):/;ҳk7.c,ˋP:=JJtsʏd^ib=ɍӣE<ѱ.\ۆgU<8'֣={`E2kOy@죨3US![o }?e}{3Q,82 }O~+?ibdB&Ղj(8N9F̾"+;)b8NF<0x|xM¡Y9;~La# Gg18NSKڵ dXx0ʏ":>o3oOڙ7Qʊ1 L }siߎ'Zqkbłތr ՚导r7|] |oN棛lw$)(eQl_^6.fY$!Za&cknՋuǐ6b4a=Q]8ѳEk!e$pA=vy{\hPUD%quYUʠÎWkT{)5vCC\|c 6~1Z]M.V~8?2>v ng}z,e"„8rG(f\uOU}HMTШ:#3yMPO2QR~gO?N=0`JY&n܋26xTz (a/kJX+2?w.[o,XVWȄw#faY4<ˠ.D8oOyqz偐JX?ϛ{ck \c/ϳ,}e>z6OOOcsaK@`=5?e6K|koxyiH!Xu~7c (.{~coЛ|ۙ[2'͕R<˺b1 =cQhyΟck (qE6%_y -0Yh/BY:w>,?OOa|rK>92_OiEYgz'SC83n6Z Lbi||k>?cy䑁]ֵ*R#ѐkj?W3?݌%z_eecy8fΟlW[@hz.;-z%J-L'ڲX"2ǎyd/gEǜp-fP̜3nc o53l\V{ӻw,s d)͞;o1#E IDATsgūdԹ;y{̲9PwM4=}&w%s6VO9r9S76f/I~Yu8e6Laڊ}I hƁ9R6;J%ZQ Pn5l5Uo>[{Gx_[K^s6セ} XmFz/ΨV9k*U/ɬMR =~δjexߦMegܫׇǨy{Q蛾%>a'Mx{ *uŖi9>Ԩ_N"emZl?{E̖lK !@BBPTU<{=.X<wXNr,X6@*!1f7ـ%ٙgy晙|9 H4.gg0i*Π_S|@8$Q4*C LM_+,yP$eqNvv~Fm:2ߊ -̣D)w&yaci"E% ݍn.9QAyLXv`/cw\n!h vx z!Vm_:Q/yu0ZtOKY2. Ar< GIc@% ܍.Ο݀CFhYF #i`6bO?鼅 :3nZ&>@ڔIt '`Z>` 26Vo 1%G+Pf]DO*I2ߞ8 :>]_VW/e'0 =:E "-_|}-7\.sAP `Qf@+Xr,?<{??VW-e#Cы6*H~5|!674,)Pc:I;(f zFU)+ L_kOM!R%Ʋpfw~CHif])S&_\z0uH-.Au.asЃk⫃4laP@ZVèAi])3v ΢)D>ѷ]u/q[2eC9;$iR|5D]{߹g3ou)e7]Goc,XٕnAЂ>}O.L_9d2:R0n1,xҀ)33qX=b]hDiޥ/X+M[My z2mBZ0sRAf^cwsλuΦO0 Ay _ͺw7uY6K=vC㏧}1[Y4>FUnvٿM_S<&3fLuH@Fiuno醶-g_NwQ3i'M#C߃qewQAy-䯼irG]i(5^X8?f拔 4z>qK!QB'70un+7:$Ak_<4#lQKE&3~x&f0ܓ;ojP0{Ou|u| VSnthYё=t -#L#FfAAEsW2m8 ӝ  x>Zh_Sђ=n&Loal_xgy<;v]+f:atšv#mYP &;qDQ'2I5#ĩE$QEQPBEKNLe\ ovi!l q^6,cĴʍ_CW(/;iaj:mom~Z''_uᤴC~{ fՋ?#-NhŪ/ڦQuW`͞4qbiWYeJ%nU;\w˿ OhQۚ/ׄpAŃRF)!d(# %fǍ6kIv ;ąQ!%`Rh"p11z(2څ'4*51]kAq~سSa,ŜY/:n8\Т?&Nh> #ʏ䶾ʎo @śߍ,@WqU`nWRMGbWߵ;&8<9b[QV V" Cmnuf D<#l%A;Ё4˝Y}7@:zwVLâ(Iu[Hoږu]lƎAWKZYuL9~vB&m7Kdo'}IM4*q-Kؤ~!ܞޞZѿ@:о81Tw"TgC\`!b Xݪe.ܭRǕ#b neN:<Y{a;f.'A;Ё2t:ЁAB#quIw4:|1SWE9_q ̭1hAphڈ$@w8,Ւ$QZRLh4|kj/-1n @ATA% E͟_mR_Oܼ4gFHBBn#@ ee_FdEOtS=>9 2LRROv:Gёÿ|y3i-klhO'ӯXGB 3>B#l! :Ću_3uY;v6s0b(tqÆv52HWY16nXߎRٱ26?Aڹ\~ux~5kkH28?enG5v}3:cp_#ʏ7>l퍪j w")2ZQ`~\.(pJ߾Nwl<hӕ)$/?t:sůD*=䐑Ev;=:q!,۪؄F;z- !) wnݘ:,󩫭 8kv{vEVfqڵ{WڵsYv.Bk _jhzGgؽk7YYAMsw>@CJjgp(8_Þ>l퉚ZvA~IIM=9騩fǎ:`PHF!%%ʈ . 'YQ JZ701e@QCZZu55HDIQ9XAQ9hʠ &.j%Wv"W]CEA#RWS$I=q"'8*bӡhp9/m_4dUzGM'E9DV96h=1';;'Ft:cРAkAA %ZRRf;Cr;CGu qJAu*Ԩ-};9#g,\Ik늵|\]\wMĺKr2bVdYpPTT=$'%ӥդq3$$$0u,z4uNr Zu=o!nk@$-YTHt"+2uTaS^^@ԊFOSf&Sކ9Beeuuu( U' IDATN^n.FχnFnj0I0g FF^}~~~Nرc(B9GEň\[vUU ii)-pC{RCPz:Byq:8ft "ABH%*$eI* 4=5h̀V]P^C=t \qN-;Ծh4vjh.DQ vۭnS[ku`]FPOHTUV 55P^%%PZʖt/]/T|{ ۆuї| d Q_ X-t^WU}#ZV(hZt:VjxժU , Lс}d2Z8Fu? Cҡ Gk!+I-fn V'.W ">t$&~r9C{p"]Gt85 Uu^96w7.,]Qp\446RS]MJJ* %%t֭U.CnF.t~:ނDS[n?pHڟP{wA%?9bڍF#ٹ3.ƺ:2331t^Tm))*h2ӵ[6IQK]$''L^RQ^Ξ݅ 8MH0>czaưӹ=طoogv'_[Vgu挸7 vٱs7u{ob}.TGVݱs'~|j)@V+?$f̀)S=+jab3tƔg_@]Mu6Zv'C V=F]/cuѬK&EA*Hv-7֥VCrN!Y T(FAp21v^{ N922<р vJu:HHPPS<w*:fdTR/,D~#`5~i4Z-z>XKN x6'MĦM|mgi7(g:3 J7Mh"!޹sO 7feVx j4؜n^}nQyy t(ޛ'4_(jp:btEQعk7lJcc#(bXv.DeJSɃ闬sV5@eBNw yߤ?R:kdJ#X/~ Rz!vR&)nP eLgX0YB"FN34 шRHEAoP鮤ЏX2z$6#$P䍲7"uV'_stU<QD!ϲп_?-O|{yj.,yMw ݺumeܱ@== @&zN[Ck@WԴ4jkC<<(\LKJ\dCVܢqOz{H(.ߑ7$$\1`A!9J Ndd8'$ :夤 z[ G4p P_0!HvoF%gL[CXP<I ~Ϯh}67WJ#( t:oPkLj#عs/M,8VԽ(eO37?( 55ꪖeep˂[jrƎVƉ}#mk4\NgTEQٳg/3fhTf;Ū3}V-~mC[Ե~f;Ƙ:k5n6UW|duE)RnwQkus"NS}eܛZY]<ԏDqݏe8?zMJ l≛n/`S3 Lܾ)m,rb^ŋ ujEmAB-XI=OQĄ["_ħ>*.DCCJAQ\A%Z-AJJ CFef%KO.ah0$'%#`k85Q$˸% ۍ$I-g&dn.9]Ԟ,Ȳj AIKMȑC#رJ$IjrTb<ų8ĺgϞ>bݳgO^~vӵer-)6;fFGAA~9ӦN /(/ TUאw$I46q5U "CKp.ݛ=\5w.YYYwr¿6fZq}̿{aC%3(JhBoNkڥw BMUWT%R_r jxA޳aEPu@؜TGxȑ̚u3(TT(8*ݽF#X,/F}U; dsqUj`TEEEеkӽHL~P5h7^T%(M[B:BMLT;) b|;F-tm,4[ 2(+N?]Z#=HR䭄BIqq z͌3猑C֝Yd22tV0gidxb?s. :u*:u"!!A.ȲLff&,[;wݹ3O;C{gQڻo?ӫg̺Xg{@;t 4L~+\Zwp2z)0v ?Swsr~D źWn7`G)7ѫ\/> d)|y39N#0aPrC1dP[<̝C.]6=*4}{|.]rT=5bYc@T1})׎{IϞMŽlyoAMI"IY~#&f=Ly=[\LR:=7Ȁ pz+SٳOTUaȐ;Q]>ϊ7bBBe6+IP[nK{+)Qyq5II)r~;P9/}~B~c*pCwrLv7_(b tfF#*(dDQwmP۶mApb`TZP:9oVDBBB-=-9l4 M~x3^}BnQˈ555k ӧOK.$%%ߛL&f3Z˗sb4YTH;222Mlfͭ6o: ֵyׇLm+x/5WQTUWd2EKy'U85OBg o9}B%ʟnɋO#ˬEgc }2v7n̔K͢Cfa=i1ȉ[lN^<"yf"uj4|슎QAN!Ar:׭48kܼy:k_00 +Knl uRj`:|JtfLT[JHu$*AvK2vD $ XH6'pqUK,}Hw_$7ڬ#^x֑FEE:.! )R l& ?~<|>tN$IvjNbb" 8]N*+TOPHhnTz˲}ishH~1v8S͹oۤgȐ\ܼb$׿y?OsߟxN\|х!;α"׵|aLÍ`iz#uo]؎ 4i<,y"j/?0b4&!!H!Z֥ٺu+~o>㥗^B]Yjռ ,Yͺ˛r\TUUq1JJJ(**ȑ#tǤ+V?_x/*}_~1yo !!z+ l{m~|SwjLO>>K??'yu ~C^qz[ Yv` ]9 >Po/_33P<#wEO(` 5H1W2 J]Kv :#8 rDywOnCZc%ɝ!ԄK='Ox[.()@+.jF[FGAEQp8jI]J+)sde$ITrh-ȊT\vj)?x7`wqIjY㶔@A2ۋ[At:r!#FN:v}R&3Gc`ՑVCH}][n%++IR;\=YVRYE{α޿?~sߜPOyAv>_[v&#F >o@y?znbk!2u V2s|-fRR,Tֳc> <%.kџu Xɳ>ˍ7ȰaN:нkΞX:ΙPIv$xEԟL7 YEzOr1.ٓ0VcdY@0˗PR:pXv-/T[@rjZW/XA:ZTj^ZQwAˠXBS@3M߁t%&hڪ'^!=:R]$Iaۯ-ܹPh\fj+;Z HQc}B!9%?֯_Y'̝V5DDQDe;:ݻYbv_?UדNqHt1Zf WZVZūʞ=xسg֭rQSSCQQt{uX3]TTT**p:=uXLbE~~^\ ZiӦELKm۶1u㈵(l:#zhTXɈhEtp1|.b,-27w(1<{"n{Dr-Z3&Wt TvWŬ1 5BL'sSaW!+BPd{5(F:|t PssA`WBwf%i_pŝ/fH/fsf`JL=t phpȘ-8Vw\[ЦdMLBam9XVD4H߯>DvXZXT uݧ7%ť4LG{UV ˒Kt=t:-Z QFҔdNFAѿș15ēX(+ph^;8Nnye|qdAFŖo8/8V j"W+5eUv:GJkyڳ|ɓ&7xٽ[Dի"9[A|^B%SdܻV(G9Mi! Rl\|Db ,dm_ 4#GsranIcꔗh)y*} CTXdD h &TN4$Ub|]AoDc_u[ǸdY ͥ+n) K'X!>9QCM3K5Çx[B-L~&g^R FXwj6w^9}u}-'[~HnmA@q+T8FYUP>agEKX[ ѿ8ڂh @bb}OiJ㽗/+I]]sAQEzzlNo^F?ArR2g G4KBRhОQJi75gܝ_J*3z# |J[0ݒ$aX|XfI6'5$[MmNlN8ٱ0.p1ഁ}٬Z V˸qxgYMҘ\ςOdE7M|h% =^0z{:7E,k-6ꏬcyox"c"˽3hl\Vr2BxZ 3HTR9pH3RI;/|{"ȬuH]$T9-Cm~X/PqIū/ {n^:A+=z`0rɭHpt0G%ՒN0ׂ z^yU@ T+a'{7~?g/Y¥^B_~ՙreNGNNCw<,YTF##nZ9s&BRSS5kop,eehZkyY‹| <|ρV7,-8iɲD;nwzzP?kW o՝bw݁b0#^BST V='ZbkT;Qݾ<U:~ǒd ~==/RN^AG]Z֬Y믗`HHH`_:ej*BBD] p ^F}Nu{}^b `C5@󢡾ca;,(`䰡mp2"B.㏔#}݌=o444 202CdY /z .gn .d.\ȼyڬ+S'v eXHIIAEN'zf"dcN1~8Bimh171T} M"GV>ȟ>#O_FtP?Mw6 %qކ#L<+o7p4r[?Fܔrss9T.mgFqs뒯9ֱtVUKlHib%˹fUۤQQQW]nnoQGd!8;CH~:RmZ9~8v:繮LNKPbMAP-J9#*dl R=aխ-7/_`츱,SXXȑ#G۬$]Tݿci/'ZܼvWG[unܘ#bNLt:ߔSϟө) CB;s2y8cyUtƔ{c=o\ c0۾|f3~x}Yj{j\wu <ǸbAn b}̝mk@9u %X-5 ~-%@VweAQ,TWCvv=a?[jh48=D2D&M1߼RRTa~QWε.]4VbN_Johr5wI,- Hou5Z C:HH1Ls!ҥxgykZ_E1|_0Xr{wz0Ǔ!3 Bʹl %>ē@DDSz͗{Ջ'|;z„  W ݽwpΓvw3$5qkmV oyyoŊQ<-&v>CƄSFO7{7-Q |#ْ1x`c̿&7^x]Cii yN80jYWRR7709;CmE+..&??ISXHcCy1[reE%Ų ꨳ`  9(oQg=F(]._jw[ зo_rrrp\MhZ슂vD[}{v۳~}a>L?ոK45ZdYAPd4r1Y,1[#?Ն(FD BukYJ[sϽf3?x=ЩS',u&O9m[\R7݌fg:?)%. p x ԓtɉ$6"uysK:L4 Zbi 4es8efΚ͸qx).:¼ku.|^Z2&^z'4\=MF4ρ΅W8!.XХF BBT獍LAIe 0kwHi1cLCQ"I a90%@ipnS XVR ]W5" &ΙENG%ۡ\ j2Hg-ԡFm8g:!F]zh ?>~xYiwb;Sd@Tt4ɞ6+s;Rm6IOOGeΐt 'HZfu7PcYe ۺo`,[>VY:b4Fd"ǐ,AJJ 999̬,F ZJ*ʩ24:| YPBej֪UdW@L&E6qXP?i-jGڟHD.ٲya]jU|g2uԡv]tݻuc37~"W^ϟQzuVXo.demխ[WN:ͮ]8}-ZЭ[BūվmΞ;sXM4^ _c7S^T-[G͠*Sq?./7< '+,G9z^ƎOBBKFm=ON£ON%~ѣGIMMڵkddd-0PzߊӘ &)|҈*P DƜ*1J¬2!!͸|ONvKdeArr'T^#+KB!RP(̘?b60oTіaahb`۰~$ z(wr4obڈ!wZVh)*KJ\\b@m,wrqv4m3l#=-/B`Ҳr*$$ALyec, ߃;2#׮'qV/JT#S\O?jݰM:~-D$XڍqE ߯d^DhXXF DHwYAǜ@ڶkl6s%ˬA7rg=ol h$9)FVJ@@{8BY9 YVbLVºvjP+m%#DFvUz|2Mn2}`lի$%%c6t|fE_Hq?wZcsŠ.&BCC$$8w~f͚ѣԎ$;;PBBBj ʠh٢ӟ$IY6l癆 )?,3aYQV"H }Y3a8߶GUfcL3rLXer A9'YƠR¨6 !GΖ --é_ODTѨQB@Tr0qDUJ˲p.+x>*@om``+P]HMX>װX*,[aKXS]&d2gَd`0`4F*@p5rdPۆVZ 1@fRIn]iݲ%iyqVRO3(XF|>I$nލM'$p:ujӥSGڶmCUVhá#Gx2/^Viق͚]Z+7Af5s</~r~ľnrkú8ta_nfTRPFMb6KRI{7fHzTMb|h)L223rlǝf3!ԬQțndE܌lgJle$7EBH 6 kзoGyp5 TɾK4]WAuZenH4ehcqb2|* u]2L$$$Ю}{.PDX"`p+j?GzQ[ EEQ_ujMVG""AvJbKOϿ*^C)HEh4;nʿϸ;,,_s4=ӯcCul~BѸ=Uo<⫼TpmDEE^ʕ+}(oag* Vߵ"SPbHXR0 (UjE# 7z=1 ҎPQꃐ@ G="" $p)dIFwB˲$Ie99z04jԐJ7|ZF%שaB%h,CF[T*hظ1Gh4RΩv?qYQfCV`ABiTTTTPV_zG?]+*i;~PrҎO$'qAԔ0 늘V'FLRҸyKz=fs:vd2[?j*%T=B-MCVR9r[4' |Y"Շ ԣR_*JyRQG]+*qhӮ6"(8Y5Q*UU />RnR^Õ|I(;;ڠ`CBJ;vdY^`gkhޢ_mwfVĴz܂6E#3+1HqGlzWYp!VXQ`? >)p@DLGݮKS5WY~XjY0Hf9-"o}o?u[dsB/y,_e cJ֭C5/n<u/?PC?=o{mm_ͣUH+euNG|.&Ȗoӵ,z7=ovek70oL*"Nel]x$@pWZVxѷg͛Sv}ݸn$vk|@+J})uW);kR歌 s:DmzO//-plLWa";ލ\'6v4nEcxh gўng\͈: 8`*@E׍O:c^7@/eFJwڂkߞ={ѦM[2?lV_L\ݳVݨ)k26ڪL&c2ʗGR-Ly2051y`;jjUkn`O OFMy>2oT%<@\[#c_SlןعѭWW^#_Amte>Xq,DǑ՟>[T'X% MG3ޜ8Z%TA4ލqƵ2gq=$ N.燣g9qh+~եG(ouU>'[S~;xj:.;.h<},|_qO壤6~Ou:DxǢ|r>ݜ}o/b_&`o+wRDD9V79Yzrc妭|}&+ous*_Ŀ#2 Q tjfΘATt4K>p|1uO~<2]g_4EV^4Ru>i-Eaq4АWwNf{>yQTc^S}3A5}{͹~:O5NPrn,!5$J@ qGP.fUM*[(?ґ_=JL)6|ud6EY_mX),]p핕8nc2/o=oQF2w2s[;' ^cB#en==FZގv|hJqѰGYޟ'\{ 钯O:/1><3½>3n~DL@ֿK y$T<υoL%!Uiɯ9(n\ڝP_m[1oK6ۮjq~4d'y*1U*TAt5Z'~lfQ*_7ڔo;69yOxӕ<|QP]57@6M>bQg\.]t҇64E୅o²OV؏R >YVe;o9;+&iZjTV%VH֟hx % $~8FLe+#ˢ7#el ޻/m^s{]_`_K,]t.4eW WRiPd'mfA]%&};qϭ`)o>y [[:KTQ}#'3Oٺc/O]EovxNδjm2ϰ~.f/Y`MXÑ,{Y| >7G{0{-k {ѫ=ڷCt}'/ݏrj?ԓV_06N־?1j>NǓ3f,I~ӧӞRW/sW|Aďzw$+2Ty= {O<;F>^ yK/:tk{1FBײ+um'ί|lQqMz|6r˲9'SIJ*i]5#2z8WsʐLd3ƬZa3.si_F kY,]J|XW|<>o2ͼasQE] QMU:^KB U8'YZO_Oatk|}Xo+)vd|J-oe_糜y'  lygM?5/J+P]sh -jquItWR)I< !_v%pav(Wؾr7 D=UUe$t8u޿l2B`8F/yQQ xL:(ۡ?L|n}NW@]Urp'mv1 o|g~7莮dy]mQ2&_nLݺu׏Myy>"vxW{\]Ze~;Ɩ!$]wZ]$ dE5U$0֡M;~EnOwB@= u&x:7eɥgPdܼF\ƜumGnE_$,ۑ,~'=,t~JG×iz|6GϾ">N_ӷx._1&sd*}A.*l,xyl݄~<;:z%lQBAdd}[g;$G4>?@g*Mu$x)̅M}A8-dl`d97۔C6r\ȥߑ> w@7;ϱP &D;}n BȜENEh`n׺֢J$G0ޛ=bP'Xʛ=+v\}VF9_hOޓaS{Ї }wmO-ax=|ܻϣ`Q⼖3t3~e-:5c z BgxȕI";l܎ǧgk@U^H/x"3yŠgW~s}Va{~iR=牱d˓n#**h.EϟIssl=B|0;׽jApEtσ,F̔Q*gQNB9!gpnJՊ]Feeŋ{}oOhhP?4$#rܞ\9;?Ӫ{\]~+HU 3s/.g(Es7,ɔIR@Y.,\Qj 43L6Ѫ)=ޒ MHF?x;ͫ)-7֟崮'>- in0Z=~CȂum*r7Sq=q-nK:ڕTȀ::&o2MT>r[B\ oȊiS=Ns!20j@J-~jɡ[.̚3,`A}9:th!{VL\rG e=E[RSx }/@o9iͮRMd5kE:Fa=oy+~H7I9x1@GRv>|h}R5MVsla4b$%ۄꬤ1Vi@5UэjbުnTIҹ@RQ,cvס)CG}ڣ`[b)(JKx_x+|OM;cY w;-gl"G&Pm͡uɹRY^c1h4oWU5e=Ș}fYY'U}Y׹\*}ŌgDH3ߙiMmޖb;jƣs1'VpF旣n#..p . QQQ~kʕ _*td*i`i#a5S9U+o>4"9xG%ڿ\\ 2-s$zy$$֠#w.Ǵ:ʊ"- "-S4GG[nh"L?/8-5}&X$2]z;ȕU35|3?Z/~! rtkב¤Bҽ?N#hTEǕtY)4Z7o_@eUCz1k- iWF?/Ɏ \tt<~ﳢ2`4Ocg)sv*uͲYI[-2 挆fA8,^cذa3q3n/ *Uk￳\߾BâH]xQ֌mFZ**MnD_p$ @&Oh.Qj4zΝ)9#ws.'79zoWUaLmw6cW. N\+K'QvM]U9 rsEG`6/AU)uak}W%SA(HSKFxu=Ӷdֆ/Yp{ !* &.7rghhFNk{tfYϗnrwKYX>9{^lِ(kb_V~bڜJ֝aQ4J'˯Dc>nᔾ('9~2IboOWWƏz^dq jXf;m'4+ޣ~j#s.+gϜaa{|Н{кrVSJ-w NѰ!n~w8Irv&W~śX ql}FN{jsNxx2T BlNPMy\ܿ7SϗeUZ5mĔ uhOD7oM\?OcƜđ6N_~A¦i fy6䐑~$ǀ!ٓn-p8x,,_n͕L6OyYC*;3O>_$@s.sJ(nC6U8y-G :Uᡇ&;+">=[*јo)|1nR) &M&mOݩVT}ꩢ7zEEY߸J=Z$~,;!<@ͭ+8e=w)JɐA_YQ]6!<ah$99wIDoos߄ey7,,HZT"!%pMY1VoI͢N+ Z_y7EᭌB P~x|MfWPF&3tӪi:vU& (tMνnUV>yk -eiҲ. MWufĦ).=MɈNrd9]ºZ'<9k6<0qIII\vI&xb|qmXjCW]NrHjn^} rA 2g{0kJ{:̝1ۺ_!à Gxۇ2bfڍ]ѵ+#BFֶ;p`/zPk2n⧿ D9dά0:MN}B'R_xg',Sŏ2^2 dw Ln|O%-9X6~^2&9'n'!.}xJZlӓ(tNy1n=:LIg{+O`U{xU]iN}m۶-Ο?C'”p^V[ЩH+DbE8+y3^1**Ӥ=< nr۶-U:y|ꣴn݆-c҄>t,ᖾ5 4i8.mbL{w,Ѷ+|ZJSEzK[7Y n~w>xΕKޔ?wA6tx+#x#Gsx# 6=5n;.3߱d~>I #s,L/.adh3YOGKGxͳԘLo׬kgŌ>~Rʂ2qӟAFty'!ա|NٞTe2kN޽ջ7ws +keu~4O`EPj*EEwskİb3W2mQX:%yާ0,N9magՊK͟yNӶoVZ 9UZ,T< %R z&G&k(g:J777=҇P<ۿ?O==W^~FU0=،o' jXCY:ꮪ5QM)Rt3 "4u孌|*?U}of>R#EUYS`7/TrTwܐ˟w/ƒ걯u7 N`som~X9OJ2<,|>~֠΃{eU N.l7ovF}ioP[7mr{]FZN IDAT*hT-h0vxYj&Nrkd%΅͚jG" PٳMuM@ٽPE=:deyH?ǎeg9}Yxy]6rqy8ki=pwny"00K.`69^\o=3gvn6|ѨiLEOSP&##SO#eZiKDDDiGo$&^}P4lЀÅ ԊC&M˥Qc2ؿoڷwcGx"uj.TʂRnR^ĿӶ] ]>㨇6~? jwGj׾bX};Ļ5mnt;6;;[^#":5#kRf3LfF:/^Zb"nXWZWcծ8oze*jEFTLRRM#H^$\gT%<,jPv]Z6%IUHKNܙSTY`2ڐCTҡcURQÕR>(w:sߪs FBuwzpr3' S+-NFukQnWʠP(j<%$I#J֐Ut*J}(+/D=*[w 1^I3nLWV+"h?fo@͋9@ x!Iv,ĪHN/k9$+9xc?~dy]=~%kUeJnIZ/9i /owzU`l!;`;T 1$~*w\-=lemHgnLes#栿;ˌM*9R`ݻAqOGgJ5Q%.1sb.ݼv*ow^yT۰ro7@ @ qN@ @ IJQ6 U})2;N$,[]brI*AUfjU/@ AfGۧ;VkrmqoئD#X'_`~5k8@ @ LvMJӦA+kEp`p EdOpH0.K;:@ @ (OXjFm:͔Zٽ'?ygֱ yלKr2gLS2qky}q5ux,.׆ji؎W @ ިXS\j[+z?vL`Y'oQ4p/ \ϰqƏKXZz=5_Y]{z@ @ (Zi!mPjը<6,N?H4jӈҾe9hѝ5G2.\> 頎x]djۼw{ +/}qNZXk¤9?1 0la7@F@ݛV# Xa.P^cVK|ocU?pש#<=3W )Lڌ0/潍Ru:%mʷlofˆ%LkqA|"{كNs+3dNpl|ھ 5WV|֭|lnӭeɒx{^z z _w|*>4«X}3r*eV˳d_vѣU"E)dvo9ALNlރK?ϸ܅WyUZMV@  Q8ju>Rq-`+hvKhPkX:lD;'[[d7LTΥQEw;bPG;t q7ԇNmF=o 7i Up DSsƪ֌mԋv='W0ow$E== ߊ:tKo8l]O৙ڿ)T'zvs|LD)PUq.wY$e|Oop \l1i}kv\1>SaN++@ Ņۍ F6vGtlFS8y&CP.s ^"޵: !]RgsSU31^%vZ[?g3Eo9q7Ocp(k=]GN4v~݆@=y J i)IIzڨN$ (5`HmtRvzڝ_b݊J֒SO`2{CN;h2ӔymXi(cd͜i5Pɖ-yq ^;3A[bƠzhsApZ[5{Lŕ39Vd un1Qba2`+w7sѡ9J?9 _^_`o_`BYo2Wu^Q9{;Mj1hd֏ mjiecz/u4R߾EpSY8⦞s\9~^FwD_9>K}qGJ&|A#V %űCP!2#d 9( ^, iv1s H܌n-Yx %C zւ3D4vr;ҽA=T6:C-{[i@G][@ut_#yi߳؅gj8'F>y7^|{֣}&# 8ʮ愳AK/-)pβ{/<:26|9 _`3UH9ޟ@C_3`YPRuɔ1:#h2ukm^D(+?9>3p_]o:ij('7>>Hr|TZh>n' nxί\NἸf1%(n+ǡNng࡞ 3!Ncgh^d*Ri_U0a*VDn%3s~(m" Rk}S|t yo0﷖<1@}<!dzrr۸e%m$J^ISoy^cH G*eA|ZevȖk8<,t쭬HdFw~d+b+2a8n|[pǬ/V~#i6~w|e=g~w4m,.)04ȰĽ;!MMK>! K~IUZwwTC}WَS/%}GLdߦ.±IBŦz'2'D;?ֽ:> $RP[d"׼.y?{\g_iKOo3Cx5|,C|7m؋r(TUHHI-ur?;+۫Eq&+):pV_3N59 {٭wnjm61sX#vBK{=ͷ{j5?]h {+ z&-"C8ƍ&o5zK^FߺACi->OƐ (r;V2&w?׏RIkؗ_Z[,EwaUf}yQ lfYzǟpQ~m~ ]d 6E+*)\^N[u0^TAk{!ji6?6בk`J6Nɻg 0l9TpL~ o%]۵jLy합t 4<_eWHk{ r_Ɔגv>/5A]d]vw2j RV1#ŷ/gW-ae"#xX4`X%7x˫ם,J̩t|1 -|IATKzu^|qo^ Lv֜ix$;4_;m|cƿoD7/{Z2634ᴍܣVV{߉sչVLٽrфw`T36c7G.>J ^|md3aY :4*4j6m={~BOIt3ox@?GH*IPɷ<6%&*I2b}\,n{1eo=/|]ZooZYΛ8ms *뫉4>-hfV,4˫?:>@ ѱ~W6z-[^ =CBa]ҡREFM-XM{CcjMڗwa,:zѿ.CY&wȨFM'-%Np Zw=od_χ['H;<߾ʮzVC\;Lκ{1+h=]k9,t1JYunW/Q@ 1y0ӗof:@&q ҇C2OqC אa42쏴 OWJط>Rї3kޤJehoCdgȷ}w!_*B\-D51>~-vՎǍ)D ZŖÅ`П ݮMGDs IDAT?U(tm X,9@;Y cCOomp!\;<_eW(kjV|{r>|ٹv (.,F@rp*$'h/`'C:!5@ j>^qmoVn2CAox}8Ǥeq˱NA$o,u)=Ẓ'y|;g>oKBKO|7e8n^!b3p$ie˸DŽhVU]4:t]1H^JϳxB () `z9>ۢMChDَ;cʋȓAkl@gIOn SB\rM/*nǽUҷ-H`6{~UPņd j8^{-;u_ Ej_+*D/-K v).8a)~$Ka\E2{'@w5E&@ :h4"20 ч+fdR @ 5HYSݷ-=4>o/[N EgctۛN/`ߓN۝qO㉢|uN`$^ 6S-mʭIkT4~E+,])VXuEb]A.WSRYkǫ*R>Zˣ\cѱ.H*Z:UE-_/WڇZVU%MQUQ˥TJ.SIjI*S,}䔇c]RUח.颖Qnmڕ!96ghf]TW 뮭_j]q=܏ &U{$1Xq>m/^;Uˣ(ӱT&SS~P^HMm}i s)_lS!KNa GyS(Is.,^qHe'SZ:_;$IEv ][TGRpey8d].IqN!)eͿ`ptZ7HHzt#niİW8 .'e2,;S;K,;[z?vP4F[L٭@)ۭ᷷4@ p <_ l**22>Eՙ+h.>$ԉ+n [ݷ5%T~ %AP60@ EEM(qڛL'IB}ESdOZ dĘ|Ϝٟf.|?0јJUlS''ɍVm~cwDծ~hT2#м= K+^jQ ɵ*(3tW[ A"sO5ѿpi• 2(5wDM+QCL\|@ooowuy݄o+.kyiݚbP\vC$-:lxe 5lZvC6i`rm3ٱh %HlۏbB2H$a$VxmO"ewN$ P pFӛ 0J؊m(EP-BTF_[_[_;X}b橮bDs*#No:4{;ʦ3#xa/$,) npWHWf7U@kmv;v]\?.ܶ($dh@kY!S1; P)#/l0YtjaĚ/;V붞GW3пymYXVssϢRGZޙ[2)%FGƑf5+5`cho$7=4e4U7g>Aok> 9|Srl<0?![yՌ`ߛqHo~Vy"؛I^,·C_;X}_͠R]ʞWS]a¦9kmmI0A"D\J m 8dM3 <փGV a`]os*pˡp,0(9[ty^{>5c;7pz$Vfԏ^Ւ|K|s =/έRz3>:I^ӊ~^jK/]FBե4߿Az=KgGtO[L#~tP{-g]k8f Ԛ;'65ժzij k k@V>*KBbߕ>P E`DQ]',F22 #1*ی,ɀςgs`ђt:Nӈ6Y9V| O)փк^VqwC<.1]\K64DŽI}Nj/#kLݪnDFt=Wڒ,CO~O'j$"m j}#|-|-|K9 bcbݧB(?N,HwPmq|6P})JAVVDZayZ]OFƈӂi#k%`]c|JU,ֺ58GIobl&vNo1mhQ)1w dW?_>4T$m4`!ؐsZ4#Ѥ'VTbR;Y֑$p?wD o|X%:2Noq]K#cZ޻7k#^7 Lgl8d6_^[I]I/gaOtǟD:o:>U!|-|-|կ[z;v ~f͚U}$c`WU")Hl ȪOAa$hZmu3!sLREMu.[:p<@Ê[ *IUu"ܒ+ŭ_u8͇}\ӕsi98$)37kd]??T>8cnշ q53׿7K/:8c9ʖtQzg#P?j7···;P`n})>>{3OH`ۑ 3ffb6w'..R8ڿꟍH&U -b%#ٲ;R&;U (D#aq`Sfm;sHCN7BD|bĖZFA/ÔD% ĸPJcOhI (uVC^ ja־.MzdA\昷Pqˀbfr|), щ3f&ozW<'^Ȓ.dk)r)c )1ڟT-w^Hݼzmr7uD4𭿶𭿶vTklo8r(f?I֙vϝʫU+9z(]x(Lu ʖ7fB!eKhL6(Y88# JYzD:Zo†:ۅA|8+0*FZkTvl5L)={8 èT+9]H *6rRnJwhJUR 퐠R|{bLL" 1> Tk!6cab:Я">}!+Uސ<,95^zz1]k 4 v5,)Dpm#Gҙi__lx\΋jk k k@ƶooWb6>{MKo+- /cZ9{r>6<բR#8d*m%HNr-VU)UrIsS'k1=kZmH1cǚhhL̴? W-tnO +[h;y]hI#\+JTX Nަ/+f?C"=.MJG7}hDs<= o>@i Zi-GܑHǂWw4^ +վV߁us)Pj}ٲht"##CC#R<<6UVN,`@& E*i Œk̫I6 !Sx9!mDp ͌M#'cm?ÿX Ng㞈wng!*bܜO3IX9u=͑ t(c0zyy1I۫YgGҾ}FQR|r k vӕl%K7h7lÚ;ڗd${!ִ[g/KԵ7sWۍ,[S#ًw}]ednU4{7I߳~ӔEņx:]':]Rzv+o-o~7)w{`S൴5Gyz5A8QW~ѽԾ@ZV10ϔ3 I0A>Pm[m[m[`~]^7;*`Sv;6Q\G‰lZjEQ檫_ #lѭu5T!!c=WlmMe$S &#5"mI:nۘLF$hGe7ŴR<?r+U emsirл&6*2#м= K+^jQ ŵ*q  t@Px.dg*֖U.\( )9g~ KOn C#-Sh4`}ٱߖSh$/!qa}a14O emlIc[3#adя|UzuO~0!KG5@kvuEKWމ A2wrS-|-|-|-_;|[/KpkZy3!C6;{<#o (viǎeU6Ri1s{H'}R /oɻa5&}aU֜]}+X#NvlO=;,{}T^qeg-_F G@-!S[m[m[`~[\KnUPTWEj jqLvqlsZ;C<=O&G_g8Үο\YbB5NCk]˧˙{c?hy ڌZ(<ε"}#|-|-|ದwYzioOU 6bS(~^`7Z ߽Y ? -~LFI,J<:jOy`%_C{D-^gH1;;M_rY?3NUlDЬ<8EgcOkl #AT΋y}#|-|-|կfЁҾUR X6tުi\jFIH>1fNP5?:֟}qwılSx7/Cˣ]#Q\΋jk k k@}m-iw*b@OAjeS-2@.Zjp g_LJuVc[ ,HgT[yh҇Kbi7Gg΀$C&e _`'xn&R$铦ѯmP>#YύkBEղ!:s?q:gH TqDȀ!~c( r7~󸥅Ӆwli@x㞌{`:vCvi `#¼ؑ8G+r~K_{r<'6q>qDjk k k@VjҶ+l.`%}0˚zSQ{wQ6Lc;}@~V~9yW/1cN:?ދoyEe| k\MRI}G[ D+ vdF.ɣa~#c} !H 0!Ӛ].܌Y+-ٽShnv(;7[R]Ȼ}<2C/҅#`Sw/fƢtoY-d۫01id.Dzj7···;P.oQ+?{SͿ&.>)DkUQ˫Өu^6`6A@ bٲ/mc_Dw(-M+Fikɗ"ڈ2p0QK/B{2TU[.GcT=kooN'0o#dž[pZ_FGՕM]ߊTbnJpٸuM{|0qf?y0ͣ1-G,|(w2t@9bBG37···;P wUKMkb/䂂PUR*|xeɸךjC ^^iRrx ZGq<Gz7|[^`ІGF V(9ʀ{z nie o2O])s|lYs揧_uOoooowu]c+|O@eJm>R~T25` 1)'TjyZ_hv|MվV߁u텚A1+ٺKn6n(E#l̴2iІX5Bzr劆8*I5^5ҥ9uv#֮x! ɣu#˾F[#0VƔn`Fed]h0`0VYr@ DP[m[m[`~.kI;}STVS 4Ic@,^mFEJU-ŤuVپG,䥵ZcsGn-6бm21R#bͿ&TdY"$ڑ`E-L)J"Eq+fQZp7( L@[K[KyWPs}u7K; U˗ſ?Ҋ I0A>Pm[m[m[`~]^7ỚGjmUP8+_04PjM Jb{n)Rײxo:m<ۯlҡ-AFBh4`}X8p-8D^x*aq&SŤWh۳Ȋs%7 "f4Lxh^CMzΥ}ԕ@t/(ZWNb@ OO4]MRi7$I/}th*'7d&y|g'cM4FΡxR0Dv[T@{نe)Wկ1Wi@17*8'bI`fl#LvNzJLR*D<@׺+j@gna0\ܔ>𭯶𭿶vT=K9-*;?t5wwy'vs3.p/yI ]mxZV6r'~Wv^L1 S̤/uv)DCU@p)"TF_[_[_;X}}kB;(}9NDF r$88[H zill yJ-TB_[_[_;X}e h55{P#"wP%4cjzș@onݛpsp+ݲd`T-$`Ϣ$:h}JѡGJ .qԂJ\ݫZ/`bhw$wL@W%0meg?=Y|S;'߯qHCyPkL%𭿶𭿶vTABHPZZqOF`B:t-H~ֱxc# `[ZŻyüGXnzZeHqZrU/O] ]k8%{3Y?=% ,=G^+ʹk8fZwx㼍BlTY#|-|-|կ[\Uubd<lqOA "^FJ1م^6 4kK.#)R3w,xwt'~Y_g=?ЍF;%UOX~si~jƄ?GcykD]9 DMoooowƶwSmِЪTCI;ZpLrVk`=fV ۂn`A"?>Gb,C>>]K<:v$*7-[_8]=snR*6#"nGA HOF6_YύkBEղ!:s?q:gHrrݐL|CwKXm9wF̌yˆL;~{6| l qO=0[!4ya[H#OP9/]uǓD !TF_[_[_;X}_5·sng]IU#H`* yyyu} k` eM( &1| Ν>h??Ȝk2ƾ4PZVWL]֔/Ee`b^t7`ۛyk(OVQUT$w!0lɏ_RNҞo#dž[pZ_FGՕM]ߊTbnJpٸuM{|0qf?y0ͣ1-G,|(w2t@9bBzu.Eok k k@tMhw**,a0PU}ŰImظvJnnn؀hh4 RQLY򻔊&ߠeuYh2AצxkL9x.o$%Yp7@C RfH㈔BQɥ&9 1iiyjf& mV_ll5O.\V~1$!ITjm2IIIH.q gSIl\\V}D&Zulv/u|JS5RL)l{Drb3˖ĩ K%p ڽǖ؆dPj&*B U蝹) X;M=1֧ C|mXrؿ5bz/{е^;PK-(Q)D߭IBJg"CcE𭿶𭿶vT֖v0eHTdafv,#I`];LF#RdY4_JBbbĤ$NSU2@k (ݘ<ݖߔ3`j' 7oM46cz^lp5K4LTB+JTw- xm+37w!`H[w;["_zOAiRH2Ty[p>ZGq<G{m=țw>O-/0}hC##r+@?oe==Di2ENDŽ`hg.`9v6,9Ӽ}}#|-|-|կ[*0TUAe$Ikr,* ަv0Azt1Ko8ȫLBE0]PF h5w,L=o;iEug?v22`pE҄5gPS̻c&̠sՒ_Y2w-a,ٯDQ7L )ݯ'[`0ҵ7dco[n'{O&wcl @E \iXk&>t%/ΞƘۀD.l6<[d{Ʀg2ydw!oC;eĞϝx1Geʾe}MǛgi0a>pŦ[0jt˨rJ-@PDtY|zh$>—笱 vb>Ϳ}#|-|-|կ[atipV)<5~Ʀ s-SOޛHJyo}G9#~>z"W>on$Ge:kqqß܇p[׳dmڟd }A G-Ԃeڒ;s <}[vMK1E\΋jk k k@6}PH]e`0 ?|}c&{Otd/Ěv efjekq|jw${Γ,[icjv;vfƽyǓ6m;c)atQ^uHwZ6?qfY?R$![m[m[`~] ^X;ZP]<?r+ՆH:M&FjD7$*u"u60Hd2Z'D;,)% hem[}HdY@<7···;Pz\֒v0Z@ea14O eml$[~{$Fd$e 2-.B9H^DCd$ՆݚmϿAiv7֛[K[KyWPs}u7K; U׌sKzCQ$b&a-|-|-|կ&|_An)0A>0a (v;v<-+M{&c/'XHӬ|§H@ÞD+`4>b 8@ j҃ p.{}GPתG+-~Ĵ)5cj@>TYYSg%$Sn^^BKqAFs}騈 DQ_UW,,EEE .{vzwmSc&s3$ɜLf3ɜ9''S>ksd*TL.On¿se4Pzqj6g*CҐc1kzZ(r+σ7/^@G#(<#RV5x@ j)7Hck,R[0cN #(=>~j ^z-5KuNU3PoKrnܤ[7 ,"pՉ v5AsYo IDATk^>Ш[u^z,+IcZXsqnܤ[<7^ݼqn0-TV0C0SŀXoCE~~j ^zDw{eS=-kOْnܼA?nƧ?ܩu'zukAT&¹Ixn-;y~j ^z.+MZc eǞt&IxH#pT+)GNΝbjc4 ”#'[>S͛o^>zGPzyx-F<X=I'lIXn^{6ؓnq( l ‘'!b b=06 'à`G}ȃ7/^@G#(<#RVT~&¹Ixn-;y|t_[68R܄c=ƌéy2/%aF$,KhmƢL?Qogk*ݛߥҶ[,7=fKrH7;m g:xF4L[E(pTcOT6֤[87MsW7/VuK~ T&Sȃ7/^@G#(<#.{uIpn-tn^\Vu֧c @ unIIߴ7{^n-t&ê?r#{o{5MiȤ3>SFȲ+*_@  .aMsnܤ[b[pvNWͨ8t0q;RU 4F[k+%B p\h5 y)K_&xyIxn-tn^ ˍt;߹O˅4Ɛ탦i. s+:?-vΙő_r@ f|ણau;3/@ jn{&)מt{%byI7۰cO+6;ߚ>bysß?)#-'{57/&NhrEK C=坖I&Ixn-Av>T;܌T?9}ฝs.3MSJ&B>ptK +z 3o܁Oj|Y1/@ jmw'q)1.Z@ n4MR<7=fKrړnoaǞtWR#]Y3NiKcNc`-3~sǺ 0;flٵ Kox08+&T&¹Ixn-;y}Ķ2]>r oiW]>S]*|L-4 MV51 TyC\;glNiK?.K -Ak n! |3Hxn-tn^_BU-T_xGcOI~ehǢqճq/Y__?=W͋?5QoJ/oň*jg.0oHSx9Ixn-tn^˭t4yGu{mZettD,C_oc&a\+v睞nlj!^Gsd ::u6̐ցp??_5/^5cՅzGPzyx-F<hw k-t&ë?Ӡç[l2p0Je,Up~8fdX;[Z: ;^:~EŃ:յ TWҞt{&¹Ixn9P[9\)zçۮS,7,a@[oߎ<瞄!CGd!;}ۏ~f0lHr~?x Ϡ\HA*zGPzyow]V{[j8rJ t'lIXn^ uw҆MWSmFccF]Ÿ?D"ቴZ[[1|0= x@uȃ7/^@G#(<#YVP3Hxn-tn^_˔/tk,9gZoaCjhlhDKk X3Օh_Qoߵ ]I{ɚt &IBYn.Y ̝]/ӿuw}Yo@C,^1'$Iʿ4<_I~'^MSCy?-A᭷@xY|iyI7[-מt{ <6e XR Ǖ{c,[ledO>{m+S4毕Wj>ee4i@ )"6B #% @i$&Y5QoWj6,+.aMsnܤ[ ,z-TV_V|4R]ڶu/[#a1 jU۠~iwEUݫxH7_vИVd,kFީ7k:j QٗBsS2ׁkسGZ[2\oh3 }o>4j!*F}W.-J_ #d E,h >̐#xMXsOU0!IzT6,+Z[r}vF/YG o c.5amslYWr z_Kd8/K6wg2[ǻPߐ~:󰽳KB,H,;ȞoLbYE.7 /~k~}8}{ @04BC $u?wv>U^7yC2|on@;2R&+9w%^-B;F-n&? ] .<~6*>CuNaW=/_᪗~6Ν}~gOڐFškI8k):ى;kΘ5_^{c7\W}5oNeYuVo]sV͵PM6Ą72l?Zyrrʳ<%~S}}ÛK]F}ǒ[bᄁ )4Cd k5-@`19\%bk@jv&=Z(Ft|cIn}8?lj-ykAn1z,H~S݄>?C\|)Y€r-7ㅷcnz#oiʨ//|xg` Geٟlĉކa2cR|N|}fc+{sGە̘.e煻,+)vv qpW^_s6s]NVÊu fNgX>/#h:~n0cj fL+ۘ7zm5s:soq3sEuBCi>}u+)y:qX.7=3^7 {_A)dϛw9yfeo+;?y䇗r^;-^ZI__*39#J!ne`n#>;3~Y᥮XnSC VlcC: 9#ǜQ7hKN>ۦ7elSvʘ]O 2$rOe &b}j׼t;օKe']"ؽ{%/COlg^|菱5"W3pIsq/>s <~B|)?zpX-SU@@$SIEo~֩0leE#||hJ+dTGmr7tb`%LjޕǠnlYmgA'p@^=fx4Ɛ |`vkRׯ'E'/Ӂ ck2we4ە[_ˉ,u.fVmmK%20Rufk\筸 ׏U]gkS]KX Fh4  ӀxQd4x ڹ`_ t-7c/ᴍ @f|c4ۄ8"W=^y߻a4VaдN^y ߻!lMz_#/ō9%;*E _ЃCꍎÜ9Crli/Ƙ?"qȽ0h]X>)G QcCdIc,{c?LEDz;c>FF $^Ͽ<$y|fе7G÷>э7)։?5"} |xྥ>Xmq;ŪJlgg>g#U9XO7>XxXΨWs:>aΧ&ONeeM6ksꜱ8+or(u͕c3Xm,R׫U9v̍\_OjR4fLs;=X-hҙ][b.OcY]vy3syY뺭V^+؝N\Fs:lܶ68NQ/܌S]A4h. Pp,Ae@ k4C3MDBKKyn^E}?Gmܿ _vVNs 3kSMM"G҈Q1 z-F̘3 q#>x'_t=Gx穯=hEi='O]u* WVuTT9[ڪ*4f `,;%YKÎC0vZqedڌ9}cLǡW3s_ڏ &#f':i+i_}O܈ b>0 \}&F61وvnkKRyD dD0phZ7 >v5 ok윻1[u|#f;1VwA/+IMeX̾[[Xszv̼v`G)tT漜`pZx*ǚQt(okt]/m]:ƌ՛>F/*{s}ǪL`UyWkI+>l^~q9]|}+scrmQN}4*f?sd?y}`* >T@+PMH0=)5`Y h 3*Pp@ Wn!u  `ҸQ5k{f Zeģ4a/o~:_w/c&ۄ鋘oyW+Ae뵪"xlF6Kir+L a 27ECL*}9 FJ->7lӊ50ƠEFco;_}7P#ɘV i~z;;K'б{)=~'H4i`Ǻ-1L,8ngcѱvsNsҭ^iJY9_+Cg|h\Nyϟ?߬##xvVYۛfh]%R[u/un#U=dS01w0.69c}a>|uۘkڨT}1r3:UeX?ΙJgϨXI'mVױ}T4h׾ycԆYkf8֮1S*W~ڵ~#$R2ȯ9:fN5^0}H@T0 ]-[jL dWQ䝦3fL1/c``Pc翌=m/emF5jH4 fc_v3┯6ˇRog~:]yZՌzѩlVuT2a4ԁ_IdLAUhݫBm6Nbs%@c0w=X.>\|Ot\<} ߿0va , y8dU56|6>pbZ/Mt}f[m]zn}&|cD9T{WYT"ʱcȾqooڤAc4! )hF\@U#P5 zd ]PHryn^摽PsTi,B'εW𤔆0ܲ l1cض柁Y3cǰ7w>6'Hbwї»:>ؖ#)(2yN|yC4=071?Y°ykUGŕ<d%۝Bo?_NA-2cƎ}IZ'mBbd X& bsoԙhL$w.ď r Z @oǀ-ۯ_n{s sއ1S0CƮ#N`3_ vNN­x˲|S6Aj/JV4ĵcG`Oӿ?+p   #i 29[Mm i ҙF0T,R GMvJAʭ.ų|Qau_c3pgOn>~/4nMm]{-Ɯq2h)C>IxS/ 5yӷO ٸO?¬沋)4NZQO`mQ 1|!3-2Sh>d_NqW:aPJOHN]͘OHu9ww I_ 71h݆t``MG̣WKġi5BІcoʨ IDATSq/poAg)Olј?OC2`8}|scViLscn 1/8M=m DuY3|`^ Il/jkԦm_Pص_u:mNoJwcfi/ުN]沲vTTmCfeg5 vy9-Gz|'{~B3G`XNx7jmփH4h4L:t:L:L_0ɓ'/:X X x3L"J?>gÏ>}'с 2bD!A /;FzXO:WtۘP;%QࠂaRR-#QMY]-X&K@i)b4g 6{ߟǍ_^~ߌ=?!"3 eqӟKV#&N@Q"Eb/mqyg;Z1/fm|3ӊM8Y6skW?Ͷv}2m鸝n#]9H;Eϩ~9~_c:;]F\),7ޓBU3E׷Ѯ5f,Ȓ㓖.nڮRim[ir:Tם|6_:ΙvCSRr~ 2o<^: Νu>Q%IdY$IPY={b 'BS+in_اT[ Wqܿ~u|1  )4Zᢿ*k;Qv8)euO8,#Ew!H"*X<}@G#(.Tcnv<\4Nqjx9~\;5:RM#V4'K?'ַ0ilIc{iga~nfdm|}9 "y?*z\ʮЏ2w;il$Is72UpB1'{aXӦۜΩӼGTV6V_֍uizN\c=a?wqVy딹mqjWdven*E{|^}3^m׮e` ɷkvַqs8YSFq*oTgcήQS:]:ĕ79 soߪVϿX%mm yEFA->aerZ#1@S$di 0@]zUcUS9u ܃=/h\ T3tӜO;qLBmc<g~idLӍm4˨]N{wt>r;pAROf>Tk&fnkc-7s݅6 #eMoSKosQwyf2^cvqu|qfVtMe,sڴM.q;+淫/fdTnvZwAVЯQg筸`C`^VGX1ةv ڌ.׼ہanSʳmI $T-A b擿O72 wØ%tQ>3y1aTw> Jw&3[+Ny2^ywQ2ʭCaT7T^wI-&KQʾ,|6I3^mQؾ S1851WJ`a=ngOOm*/9s[/߂򷉙U9FJ\yܶʱ;n^hv9R].aׇzv[jnwǻ-y:qI}W%0f!g9H6s/wpRq3sT?_5/^UzGPzyx-F<]VvcLiǎL]cmVeYiNo{ ̱^A;2wb',i;2;WYnfJW5b7Xٖ /zu|Trژ]_Y`#jWj*!L-PY"؅ܧqWbPܵyTvu R7|ίA ѺnjH;-s>~s,˱jXwn'?]]xWmȃ7/^@G#(<#/vw[M[ר;]O]z? cu>^ݼq3 _vx8u|ܾ;[7~ӝn=ع-ﳝqJ>omvw̱@#EŃFk4R]Y{fKrړno; m5aص2]uV#aم'FG >5Pм.sy?-A᭷ Ȳ֤[87MsW7/#ؚi5,-:5[ZjSMd\]xߵ ˊg2z᪰-=-td[?r{Atk,To}ՅzMm9Mt'lIXn^ uw҆Mo-TܖgaIm|x׋y냟xbee%Ipn-tn^pN\+СSsT95Jx͋?5QoJ/oň4 [-מt{%byܤ[j Rة. Ņ냟xbăjȐ!4R]št &Ixm.dHLكkVAQ4 mmhmkEkkbhkktvv/@WO' t&:D"ʈg` 96"L  G#.lIXn^{͖t浧jov.ψu1,}s9g>e˗~+Aj@eMLs9R10AǞt&IxHc*ӫ"CgсX,ѩB&Z@e <ch Ixn-;yü v-K]C 5\b1b1d2T*4!2t;l.lIXn^{͖t浧HgSb)1ĕxgRI"}' Be%Ipn-tn^0/UC*Z,Y@ uT&¹Ixn-;yܹW#۩LCA\{yVz PYyY&)מt{%byI7۰cO{`˴'TH5@ h2gnܤ[<7Vݼվ`Wì jT*c^yـg ֤[87MsW7/Aײns'9ֆV mhmaىD"D:Huvvz_F n4MR<7=fKrړnoaǞtC~o_xT8v&¹Ixn-;y}Ķ2ծL_r8R}#~~j ^z,+T&¹Ixn-;ysY)p. @--TLu!N=˕W/΃7/^@G#(<#QVwM p?k~Wj@mc{Ճ BWwG 1dpNo[Po,וf(&tI R=/7Msnadn{W9gGcqތ}M څj$yJ99yIxn-tn^ ˍtCSG:|0gɾR˰ hB"ьܫZHޜWj47i1H^Lp.cMsnܤ[+>r3Ʊ@ ՆvFAsnܤ[RP8i]T ^K H3Hxn-tn^0ֲnI]g,T3RM  -t&ê?̫`ײnŷŪ :P?p{f 紷оϕt'ݞIptCt5}#BFAsnܤ[5ʘfѫ=Izb`LBCO hlf() i`'Jכn풕Y{7ץA^ęR(9/=[\*_ը jSͱfCTd7+Wh2K"uO+Co3CF6a= NT$t&P1Izhqj][7.ӊ1n0궲-7 u?8ru/Ypq7VsgԖn>!T}+3C/bCD -pnJKٿ]xH]975ti݄׺}r5tqyR|_BQu%V[?Y3/c z|cXk):w~GY^stN6 i̷Uy.Ywƪ]ov*(_cXϱX{[-׌ojlJTyqB ͐0DFi$YZMA "Pa=XvuDZ'ڟ?IA.! 4?Mn)\ݘ,x]SqJ['ᅑ'bK{~i󩩈@zyd p3F*Z,+#+?z\Qn5ݏ+/]H:nHTrY<6U6#a .xPxGF/7܅R@] maNJq R˯Eئ!TN|S>tn䲓S 2$r%vj.1hT6aX/!ӗoڻOB׻2E{nww.z_`s!wS/$sɵ7Y,I?~7r:-˳ G}f?;/ 5*x_wJb1h)Z4*q6,~zRkƣoeg~W_cl8dŹG;ι+՛,֝ơqUg/P-ek\|XY]jX }x/QMqX &s ڇJvq K48/uMV~ctaڮ܌q0Cc,}+N Aj<#4`ZYddi@<@ 2|Fu;lߎw6˸wV q&` ~wӈ^3ˡؘ֡_EeܗYy `GS0)L0tJp X"8B[UR_^\F5aҸQ/~k1/Q#c^K~kGMNcݖ-O+cnk>n=jro+mqmLo$/`ۘNo+mvzlJkÕkN/oUIbu (o|f?syصEtnc}.U~kan''?0oLXk_ߩ=˘Y+vJƶ͖|sn:NyaWo݊.v,w9ȍVe#">  Z {V3|oJI,QHV"$4cƌƗف}Vg+ۮ4}HLiF-׹@q7 5a#¡WI}؝yj}<C8its.DZ!w^uCO1xei'O'w܏U}DZ7veLh1M4tn}ᔀ`D%~vf&'/~Z/I[z|ds̉umn}"3TAr/^]A*~vj v4gIs=ovBA 4$,"PT HDYCTx\'P4GҜ{8RR;ƷX?)!+ xaz/6oScc_}$qLoۋd;_^3s JNo!,TEsE PbLmvfb0< :c~# MM@̘7_Xg?Zڈ~"|0 roG4mS]سz)3D 1XoB&Ӎ .VaJ٭&'/~Z/I[z|ds̉u*nWOdbr?]N~Xū+Y\-V暙cYb1b1d2T*4WZuX#%`]` iMCHefcMc42b`$ dT 8j" P" @NJ8\3E87yN>4Ljܘ='b՟\s8S/| GWM…WLg| \UQ?r}]dŰY21\`dfO:uTX ce`0V{ /ncG7; 0g|?NhF񻩛W;W}|_l4KpUcշ?9yclmK'٤<[*x:hqoy1ΘN$`L˧<nCH%Y*/){cf>~ȫc@JJed2 8[d H1 R<}X?Sa@Pzyx8|Rx_;~7!"no+U;:ӄIczcz:/(9tk_;+X/JK-og}>]4aU\/^o^4o>7mݱm(n[#xtƘY]kV̶V6pC?x: ?a欮?#r}٪]{_3[i‚y'CQ,YF $,C$(JPسw/=8jvJ_ǀ ͚õ3R])ܓ(_>k B -ktm'7e WeD;h.d ZDg{yB8pfǭuS˟{oamY5L.n<՝ vR^5r; }+/V3sZ]C/CTN=hLfuwVh-HݹR^BHp-x${#_n`?J;vdh$+w]JS#<6rY-ཾU#ֻ֣nP-5?H:վn_#"my#iJݻNԩ&B]kعkuRku h/w5ͫpXtוUAXt1fDA+ͣ?EՊNݹjխNuu F ۛ4co裆Ʈ}ҍenwy~>"yuv{[è;W;VncX~삼y1˪Sk!4RM BGG.+1jQn]tMmF5gliZH!}SQQߠN5@  nj,6NyJ4^{^n+rUn[7.wdןnot;ۆȡA֗ruy/FݾQf@tF _6`ʯU v)TӪ"Ixr < pt{DV*N5@ nPտz5z]t-tտIwJ!j%BTQM\ -t&ê-]ya Vk o@ ۛt\5Msn7HN5@ .@# Ixn-;yqVwu{ASMõ@Hu k-t&ëFlEq[wYljhf h[<7MsU7/GlUD{@ Ixn-;yqla53RM !f {Nn-t&C?r#P3jM 7jgnܤ[<7VݼaW^P3j@ ܀vMsnagUP@ u7iZ%W<7Msn|yü v.5ө@pvMsnazj@ jgnܤ[<7Vݼa~^uGH5@ n\T3yz.{Nn-t&C?r#ݕEoo/ЖϾZ7U>@# Ixn-;yrn9i \B^wtp):&NةS͡-,JLmQHAF3$ 1 ) G@fH `\>k}F Q,B( gmRгe9lyӋ9[c0% ߹ }~u݂mwݎ욊SvilÏs1Dۋ ?4T <n9ۡils%&F]<w?>t>L"ސNt.ѹԠipڻٻ* VPa]X*VlRZmm]_]jpҺRGKE@&!!Ir=~Ip紐cθ8$\V\M7RQVF %"1Ԅdޱ>eBL@}6q-:Mء$:6{ .8(1l/ s~8fdbg1`ۇl w˄M9 J4|Qީ>}9]Kiw}h,JŬa粇vf1+?<_Elg7t8歳ECn v5Dߚ^.Y 6PBІCZk{ȮtMƏ 7UL*7(,,l[osՙ?bcWpea}أӧf8~ j:V0ٜ0s:Ǟ VUǜK;5_|̷Ҷ!K2Jc9pL:Cg;3[sge"P0@-'wQŹ'GNbuT5Cx-\r16_Kgm\uU] `q̘9\c+Jq!s^{@s'pnI1ex9ftN/,ۥ""Dn{{C{j- imv[g5IVT)Xl8yk6 v z#h^Mk5 '~`g5\=Ϸ`>穹/?!G.'gUU<^9?f*ֿx+ƍFVK5\3.W~0k27'xy uX *]y;YżG>`^˞xs1WgIv7Vz=W뽕nv{o:BmE&EoŒy2 s=l D{*"g E 8u֋1aC3ֽ4h ~T@&$G m<;=v` owzl׼竸9E*& * eYX;@ YeK(ir]u CU:0>} %a tu=L4Hs10݇7P|5GpHcj60G|? =BSZHޅʢUǽL:76VZ.ģiGu?qS;"nPV=|^2 *)G _iXXA ]?iωSofw7{mrEmL%;)_ذn5CpSΎJ4 ? _#T˖7TPc|wzs7ZHނI]p+c_X!}s>R;iO.`L>c1X\E[mzL; >Nj5g%͢ٽQkUrׇp\(At1].5bE 5D7n:?/xꗿP&>? F^KucAA3\g1w)m<@J6I,IߓyVb A*TpO^ۗA#s S{橿>ꂣ*+o syW^|.+`­dGEt]ޓ4r _㵅Goj}"""ɬm~:`rsN79%0qmN`(59g>cXɔ9gWNjK7t`r5J<0 ؊& p1.%.j.wO/­ C`8<.j놻X?oD0-Wk|Vɡ2y`1ڪ^ׂC6V`Lޙ?d8~Gnu&hxz;ܗ@x;/j W2鎬p8a~_JO@>CPV~}NYP[apȤ\xhC.W?]O7YU{z/<6/R"ː аxd~k0ͯksێ7ODD$90*Mg1.{ '̞O]FX+/JeLt@dj^;3ճǒ.c2&Rvf6l.ܽ\qSav`wt j4TL5@Oӣ$F/9L 3_ZkW ^k lہ;0L#Vp%qgpRMqnQ0n  &s_8K[g5tvߛ:m4/#FG\x&j'ޓmI"p2+`]ó"+`pq6毘¥^p~2_^8%~ɩ7F/rI{YtS?V>VͱQ06}wpG쬡yg)yΦ OVG{SPVw`1z̶l(-(4Ȥ)(k_S4 # Kh7%@.-_ՕlXsS⑶"Rfam`'l`~F5aX`\`3%'qϳ\3p9O]P9nZm syb|s32Hm؋q"H0#|3p2?jzClx1ܨ DDDFjˍpjj&2vxތ='[z?T?Oi(GM۳z@}=I?q_\Ƴc@ئ f港HA͛w MŌqNFq\OeMĎ6L&S7u͊H7 vGnGVBX #bk^ ic0$©'/[NW{7v%8 V|n'i.^?;>61=Cq=2Y˲}}$~_ c92^/)iv<_U9㸸f\r!,7 v.K3-=6càAu;v^ԭIZv`7((o-֨D1##8dz^+AuvRo-[Ѷ::ΜtUH۟QG$گ j03{N )nG˕G4Z^`;05hΦ|m.4.Ts0_zpjb$S:eLl6{knmut 9ϣۑ~?j뎍umhy1nO޿h;n.[w܌vNC8Z$t{ )F!gG`uAP +jy""Wgh UW>;R׹=}kމ[N|ߚwWKKzoW١)%Z+D/G՝6NUeE btey8.^EDDDDdw+ޙu˿86ב;rnGo˼wUt|*WL$|,Ƈ"x[횀Yy D,jף*6}|4V-'sgxDlS-""""*k,صO_SBe/fbzw]{&&c#18u8[f4Z;,Lbam2qǾ-*檙ә*Ox*3۞G裻8mV'Ci^Rg+'~5gNgIp-aMn弟ϧЩ=g؎S[+m_y5gҕji.s/ o'qϳwfvD:[횾;rnGo˼ +[KIwsrlPZ{&` 2 n6EKe5%AG[|To0uA`T{G3(G"].oWpPZW K]:NO'L%eZJcCkgQ,f <=6Y)5w͌*b;u[gbq \LLlڅ !صpؑ];&֍o *TnQXXض+3ļǮGOq]"~zJtw`ލ9at=*O?<]-}H8 satg;3[sge"P0@-'wQŹ'GNbuT5Cx-\r16G_Kgmi6{g.yu<Yz z[_Ln2t9Ӯ v쟺zn:[4nOnjo,R84p穹/?!G.'gUU<^9? X.GpCgV1)oԫ`Q^o?f5{xe:d]ݺ٭h=xߵMQ>]taY|GĢ՝GH"utg3NuvbC~ .u/ x,'U.P7?Ia7H~#Na›qG zG/W$<kl,#k!l %6MNa]'Ƨ3 Q)a!#}TA|5e߁U.XܵSZAG$?铘V6ze<9#}s>翻v r_o74>7?-OT>EXr >LJBBKtScQ/g!Yu\סgS/2 W0F^ v0?J>;OKis9;x/Ow3_r7h=WMay}Vb A*TpO^ۗAUF\S}G3&sUV\V[ջ[Ɏ8<7'M׫iWOk _EDDYt›orJa㶅+(|z"l\z;FX.l2f%>%*o8e[1ojf:x`@.M@bq]J\00i{]z*^[2psy] w~ވ`Zׯ%<#;(C'Oeb?2U2mL o3p,Gi/3&MQ'_Wm%ҔUCnuǜs=r/yKghVo=)J~sWq92);ZƐ~xGS4n~;䤥aV잁 ϾKc~ȡ2$4,<2Z=dڅI}1J-~|Y gSʋRc*S9)%OxsYQ+^+۟J?YÕCWşRXV4MX0 &2vKͅ ]h 3'ԁf~J=(:0 cK-嫻vMvE@ ^~LC`+X>L0q\H=K.9o?jp SY5#_'_[ yW s#0YէDϠǁ`سuC |8*ѷbk{hUc^bQ0n  &sOSpΕ'r=06όk7uhN _xE?GzӿB,L>9~N|'q/Nm&""DmdFWg;DlWm_1K'Q&e p KSo>>_傓1~|c` 3mFqYCI\&}߯\^AvЧHuظlj/1aDILި 0wZ7v$q M:}NexY5fm[+X [3Qgz#LI=<d9;cNS5s۟V6s߆ 67&uh=9;ʈcS)ԕhf.cCLt8-?77$ v?q8V3w0EBeE#;T{M,\/.'R]-n\x F^rB3};2_ã̏? 7̰׃Dq\8pJ\̝_>?Vm{Oԑ˃j*歈wV7*k[ ;X=->(3򓂇6y-| ڐ `":;?7̏^F5^b} 76s"""ML{bϳ E07cO O_70OS al=lc#F`XoFЎ2|P/7uE@ᮿ햡nj(S2M]{ ᑛ}å+q0#n8HXZ?nCvwL$p#8K,z| #ODDDHAphxK$R\"n=3Cgln*Bg-ve}ep7xqz֧;\9WVOٳxc~7x/f1 u;q/6P: 9Cf#[ohn|WƤr &>\,`S{6DbʚflwetPL%Yδ:tIG6:VGЙs+iά-""""""ѠSh)""""""f8:i2"""""""fƾ_L˿EDDDDD$єj8 u(:HVZDDDDDD$N EDDDDDDգٗڧZDDDDDDC&V2I EDDDDDDMAHA*oIXĈII>IZ˿EDDDDD$QojE""""""`T$ `*T&""""""Vq2"""""""q՞kEDDDDDD*C'MZuDDDDDD$j]V-"""""">II [DDDDDD-ijĉh&VZDDDDDD-ijD3T[DDDDDD=2"""""""qպXDDDDDD풬PH%MP""""""hѠP2S2IZ˿EDDDDD$T[DDDDDDT[DDDDDDCA*oI(Zj8) Z-""""""q2"""""""qJZDDDDDD$ђ&I4"""""""qS/oIh4LHT0 aZ-"""""" SBeيBe"""""""ZDDDDDD$NBe]= }I*SoI EDDDDDDiTk$ZP*Y,C4jDKZ˿EDDDDD$qd"""""""ZDDDDDD$NѠZkEDDDDDDڬܷ2"""""""q2!9 vP$Jm,[fVGlZ""jӺ_IN&{{ݤ:&<]-ㅛfƲϫ6ꙣYYΤBFVii;IDDZ""""gTƪ|ƎAGѫh[`+6l .}N 03y,~p˗|fuWCDDDLDw'PQ>  fM9 ?| +]rɘ9׬ 䐢MDD[DDDN Y]׭` TGdƉٜr8$7`Fs/gp΄ 4x-ߺYƶm۬_` EDDDDDDeDq}YReQ1ʗYseKgu,Zg/jwPZR̆Rv!""""""^T|c;;ɸ؎CjJ2""""""TAIڰJDDDDDD$NLugSiWP@N~>~0 lۦPʊr*wQUQ탓)bvD0@J*9M 6eOyNB-"""""">wս%#'w-x%?٭\!W0} fn>U廰5$8:Lu n '_BJ[]0MCrzHZ]; v 3(PYH~rM"ƿ|/Ũir!cg'+(-䣎wKM'-'d!=)Ҳ0Ah6<g&""""""Ss cGp01_J",}a>Z.߮cOWF9{bo7yx뺘ipƩL[dMJj\"Cϊo""""""ߪMƟ 徟:SД<&KM'CFzsnV=WVU^}ңg/6.6-9q'cY~χ|eaY>`*F0.km٩Aaj"ٿ_;m$?jK}"^7>aӗM~pب6'N 1-_l*Vpӳ/`#ȢŽYb9':8>3< a{iaY䤧x'=r1ͽtA$λzO=.qȬ[UmbKACQ0*? S,L2_Mӊ.7LLJOgUTtp""""""?꼠ߋF? SHئiѧ lngoһO mc`Y҆i`ib&i+fg.3x10~ьF4v 3}xdbPQj{ """"""*ۋr)-?% 41ko&OV{bdc@{:Z0pL='+SDDDDDDm2̦/P4<űmԱv#Wnᅦ=>6˥'p))T[DDDDDDڦ6얙j Ƕ=b:c;ض8o ewDm-~5}EDDDDDDʄˣa@ x ]qlƱml;ӳW/JJJp]<.apݤw>EDDDDDDZUٮrtRR ڈeVUPY|Mu5eŮǶ1M50ӳ0 7@4PWy8 k. . f+>ՠ/6&W*v9ۑ#d[gzu[eA4ήhN}uc ] ϭdaWEODDDDDDW4nPW[0=2Py\Q#GPYQ/$\ H ?@X=tM v۬ؕҵ麥NFu4LzEdee"""""""*31>|$YYXl=ˠ[_)ǎİpS[9'3+#F6|Vm\;8j7:z]uxYqډ7sڀs5[ 鑿w2""""""Z SSSllbӅrm?^i67Z}w.gڼѬ,-m,RWU6;T86vwکoN]P8eWw]| joQDuM yxEDDDDDdsnm*Q=wWl!&pp>~&<ϣl'@pH yxk(]u[um 8.hQǫ0l"""""""{Iz5TԀßYSNu1͖E&16 v]<ê:Ž؍2k;ܮ]&u[pA7n"b~DDDDDDDbc|Po;? ꈇ dm?* 0Z?ljmըץ|5S;>σp;i~w4஽lRL3β]X#Fqg1z8udz>*Dd="&.yclxF/H{p}|@/rNΒf5 𛘆eW><Qw}ۯϫzk. ֒ק7}zl[&""""""H˿ Yc7*Ȍ98x f9E;2j/q@,]9m۬_` H N$BIy>my6Sa;[TXM8Y7zLF'""""""6Βb.C"'/ 0ʗYse3UO➦59qKZJw -%:l+)%'=T  `>߸ЯOoFa4?oƓ.N 1 zv؎CjJ 꺱, QMfZjؾW"ѣ[DDDDDD:ѭOl**0 $3#tjavUT"RHuM cܡ(t>4$'3el5555ض~21l(yڇZDDDDDD:ٴadfCT4n^yBѰiImV3նmLMA= sDj|i5U!|Mw[_h1`TUU UXF>JH-+""""""V]M˕U3pBrHu͎ 4t0 뿥~EDDDDDDBekW9)Tg}<09UNff&@GsU,n\<5zxF***HKK4M^yƩK("##cmxTkhߞ=RB@*Pռ+կȞF{ܼuVo7%? zx^ K~p97vчS8< # .}(9;h:ɒ́#3@"WW|`rF9tQRmêm;soaժU|[ڐVZ?m;ZD!G%Üea V9!Y<',q =bBqEDDDDDD2FRmGm)l6)' sRZ~mexN]SN,?R=@/>qDzҁ]S8#ځ)1 k+VqSV}_˗sp]0 ʪ0u]NÐ0&`h]݈˙x;` ;w6oL,3v܈[ݭo;go`.&qȅ!!cx {!`8ˬVu]A xUv([zڵ$I{9-ZT<r\n±EDDDDDDF@v5!ao}Ð)R_Z3?B)CW3~*gG;vC-6m<$555aƍe2t⊈T3G^o o !N6}s+,??@8;BKtl(IxᇁnZ>ȤU)s^[\"`V3U1܈!h!v0a=Z ]]]444n:<#sZ[[Ώ""""""2IT;K.^Lz|'!!6 "naB`a>1!` FvI;qwaG}ZDqL&SB+ C 288cBR]:CM2O<ޮ҃[уq c0Oe %Ym 6mGL=yl痿c- .+&?#SWDDDDDDdv ,\Ď.zZYp!^2W+""""""2^ŤzΎ1RcQ#NOI<ϣ&IpJL⊈e(uJ-Da\z?ތ?Z\sjt㋈TkjEDDDDDDf1[Ck ? D8[橊?V\D K',]x<4Zl-6 )8IDAT*XqEDDDDDDF5@e~Fcz LI=HV 9"WW+""""""2^N},_zea8%\6K7+VG_o^_-xEl K$1f̝OOO/T `įWDDDDDDd,C>Wm%@h pSO;e49 !-D}e{ wuרqEDDDDDDƫj!Nϛ P>{'&;DO??/w\G6Zi-aXF>y?QZ[9`{| W9>P,'?x>xZ6U|=<Zsw}_xzg3$~?b~<.']s׹{wYgDܛnшr.xGXxZEDDDDDfH4AyCn%~6Ϳ^<˖?a#G?kXyg;~q?+xGv=|֖w[9Mp//qK}IٴjY.~tl8ݥ ywsli/g?Y|x٧bFiOM m'}g:T[_/ֆ-"""""2EcIoZ_8{<#etA|/sGz*~z-,^c=aZ?|yw}zyW{z,ޝU>=_xu#caH"Y[a3r'qE4#,[7ll/`>yYsK$ihqq+>;"^]\R\|?Dz}fbTH d3/[F.-.7DZPLn|Dl}.n DI7OPL8Θ'b¥vmn\5|;;Yj؏ d2Gz,[Β+QVqشu zkX}iLa=yl痿c- .+&şh}dqEDDDDDd۱e3,kD9{{ٱus\F-\"=١=*jRC.E<a<Æ ,Q[$y8%ا:~"""""""Oy1xqVfOc>G+""""""So_?ښ13uÙt㋈̷mG3$ XqѹDDDDDDdkkA;F]ɘ-աAY _g"(~jY-,Zqٶch9uQ5}H,+y!ƍ5Xcgl6ǎ["q`-[S?w.ǟp"O?'x"]l޶U$G1*W.p\[>{fDo*6~q鮒emqʫ/>K/O?mH.PM} {{}`dYBx*^'dW^#mϹlm̻}~^}WL x"AΩ@6~n-==RykXkɤ{g szö֒ΦpA3|Շ8 º,|7JɯKh!MO<""""""PgݐGݫY5v7ȋG}_7o"1 VK{KTɮT6D1eݹ+q G!6us_}?|zK^~%G''*"""""""'G;cm>r}8$0 1^=v"Gpw/amr } ;7(ayץ-~(P ȷL=ɼD8;ͷʶ۹Kijl*.oni*X?W#w42P8$<ԓϝKKLMH3~ds6a.Sr>ߺz[]O\r饣_8Iձ{3{|0WZ@ggp]&%""""""P]2?].|No Vݿ=^2CWgTm-,Z/9ZEDDDDDdl=]XO3|_O]yloX9ƐKEi7dW=<ϣ&Ip }EDDDDDdhkcaرu˘*9c ^"AGc0JEDDDDDؾe3yv4ΟW6k1%""""""2m~eRsgDR7IȾZKO S6j+k~9fX Xm-d:*bm[Xo`6g)nc7oQRPwm 3dYu4~Y Ηa(PS!@Xk1fW򿣒xrޟQ8_f]&1)Xkb0bP2_n^5g(koX< u6cJU}RfalR_n[71pe t4ȘbuqL[p&gIN֊K'pp5p\DjhҺU8uXZBrmL~ҺS;[dXᯇ‰d%'䄵ľ$:;>xh9[(^+TvqJk[,C-lE :lQX36TBeJ.v/3tshbJ~fK/ܕBaمXk)XrQp0T[,- ?Z鏶UaLSHKa'g}h;'[DDdxIrUd_ޏ'Nǽcy{~o:-""""""2CyZ{zyW/u}j V ԥRS7}ܘIuo_ne鲥R{N}uoXYv""2bÐ0T6q qp4p>!ɤ;yhll ~A۶l]f6loK/`:t$A@]Yq\%GiuF-Xqf rr>a8hh4]n:;8yL&J?dyc鮊hn矧.:SZK.c0k'~LwDfR_?'vV)tlK:{$ CHg2qDb~.6<^ܣ&q^7fR<9J^ͳ:+o"o<|1+7ں2>|O] =\)S3 /(HnTXl3%0~x9MQ6LKoO筴hv>552I r,q/Ζm:I͝ǑK)ނX<… Yh!Ϯ_ή.̙uSbOGW܇w|43W*7ںROo>K6Mn#[DDf/?9 ?o^:Sux3v(k-ah 3yA6NwDf:xu& -ƘYn"A=]ѾwJÛow+0ɵZKWw7Tk֬&Tvڃxfܹ'eWo\,75N,Q[DD?$?fjkkUUzziZcL8?sfu\wJ%5&|_pMvuG2 519t0n7nږlWX68WO9t_{!J?ΕX;╾xQŸ_`y?Gs^O$ ?Vjw7+V[]OG62p 3zD^g.kz~T-%%+cwZ/ GvL%+5bhǤ}!lPx/3YC~jlhG{7rgr'V|?V\6C;x2,8pܐ yᇧօh,ƂF^c&1ˡuI~ Ŀ~9v?/j9u6;ob뉟\]ʡA^~̇DZ,қy\EO}s͟⪋>ϛ^<gt:I(j+|^z !?۹9#kٶ'6` C>U1OeIƮĒdۖ@"2j2ں&]%=/xߋ33gN m->m9 'm-47ySA@0\6>44. Q__?-.r9[p0,\hČ86Z[ɖz4NI\o%:(%8xG~APSaf~ud!qؑ$oy2GrPʪEDf6nW_/puL>~ So' B]|xL(Px¦q:::ٶu+_܈D\Dcҥ̟?sgMB æM.#I⺳ll&;U׉fZ"""{y4EK10O:!\$5x=dpphM{: 6=@6&; EDDDDDdFq]dD" tf[f`S($!0Z""""""29,jSOM>yрQB-""""""2}5sTw?8Q= }9sH$Si6sq{}ևl&4J q5'H9DZS}5N?&mgN7 +ˣ?;IENDB`muse-4.0.0/src/doc/pics/midi_config_window.png000066400000000000000000002561001404105311000213130ustar00rootroot00000000000000PNG  IHDRaUW0sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1,tEXtTitleMidi Port and Soft Synth Configuration棆 IDATxwE=a#.KDN ", 9(*f󝂈HT$r^O%aabLPgwv/f穧 ;"@ `0 Ax+ ]Xw^$X,XH{ɨ0i^j/(lu0R"S|e3KOjIIJY`ڟ2($E(:PN?u Ӊ<ԩSUUYl "32Μ9*))^C@pC]O)-YyM*T0|"Ă%"p+Z*͗vbJˣ$&D ğȉ@N,/#s=u32%H& gT/4S cJMz3 c*%V邱S6\[nEr~-۶svY^ ;de!rU'V9ۈm.)ù,^{ž1`q8BӦatJLNIEbbۋjժ"66Vk=1d d AvqCo!D5I dm c( @2@0. '_=΅1@Hi B rj\$rF}" g]5J9 ~ Yb>%UG!\ɫ(&\)֩df7k`3@(xu?7aPp}u4ꁸJU`?JNģn\xXI Nds8ԭ_/^d=O)/z1 k2"aLTMd 25M'q$(\k}ZQQu邢"3xt+D{;HU5HBI fOR C2_bYBGD$gOo4o{4런h( m36x]hL߿Cmފ; (-Ki/I?2 Э˽.0-vdBߑIVH>%/0T*>G m~e˸;+K葜<<]}ALor]r -2!&ܕ쇵XNxnB޽P`OfnQ|$&VjCIq\NV\T͆kψJuIU Y) mkV9-kAp"ɧO  0Rk[ukѢys]l'4& )(v Zm-Z[{_Gl9eH;SBA%IC\E;wbЀY3ծ{P;"0i/Į{/@pGF=VkN 4m;۶ųO?wM?֟>@0d@Ҵ)hچV/O{ǎ?2";@MI|ءAtI>Щ(>tB"{> O F)=?*eZ^acB}WR6&"5 F!-̟ghjW_+/bN8̜1}VmR\ KLm89rR4lAqMUps8}x<dfeF())PEQ:22xT+%K">]) _v.@%MޤuR]aTWXF(O*&UUV"77O^d MOH>?ΞŞA||l1vL~'>,f>fe] +䈸,IHU0#c=X DSP/6U ܳψʺwjβA800nl۹ ~=!DI]I.̱c0x X8 >>ۦ;>$h#i ˤQ7cWJC} :#_g?"r DGA>*e{{r2˔H;2r\RKPV3O]WO" 7GQ&&>4ѫvGfĉq_alWƃ#F`Eb I?CoB|g&~wL{%];(9k6lhذ9Ç`OsЮqK#N~Y`^nQ\"|O8!I+))Allrs*gDQ81P!w3xL@ ]CVd%S+>$wݗ)<3s>G >!>2wgAݛ2\ƣmFF >ߞQÁx] 20~8dƍCVfVqЩcGڼլq8.q ѧWOi _,^lA]F4o}ƾ}{3˗.cЪeK 8(Ejj*8Cjвe+W21vjq+34׋&QhԠ~7ixZ*, U-oC.]p ;v{v4m{޽{xu4nP?NЬqcg }lL6 11HZwuW^3\#cZb#cp%J8wddMq_>ؿo^6iA9xIm[Ѷ-:,Xf&Mx-4o4?;uB[nA޽>}.#KBpڼnm6o|?2GqG۶xGAÆwnàp`9PR=x 2G hW5)2SG^CcB2bIP& }D_!5>WE8w~{#8@f%_♧A^N\N' 0e̝Yض]N'֬|`6/[%K4︸x,]ȑbۖX+$k].厡DJ8$p_9| zQ=ZWqP=ߦKt8t9%ۦ͛P(9xJBIȔ]NzV]&(SRƳ@)27 NNN |{ `xɧ'}Bm݆qƃA&C(WҼed`1X,xAlٲ ã`|zqp]HJZ\T" |/~ z莁իW׃  #Gu?1v;VDp.]S&`M3v,bcb0f#ؼy"Gp#4"Z uEL3֯'#Xqp GȖ@JJ ,‘ÇC3u͛7am=f,6o$s@4x=*8hKzޯNzv VECr!etrD}>={DNA~^p -[ѣãGc-}] cǎA\;h eO -t_T>Ƈd(9qHLY|%J'+v]Htu?wlJsGnq5늡9v~^_d3n/Ņah稝S'JJ\[ض6oނ5|'UwlP|Z톈Q{a#[Á v qx<^_>!I#^/.\x6M0 K4imѮ%HUW.OdGbD-5IMNъ'AKrfBdB |%%ys8hB8N4j%%:ў'M2Cn;q_˯ʕrn .Sh@.q<^/doOb~KVrU-V? x==xp9Y ~`y=!MQؖ8P\{౉10Gqaܾ}{|8{6QTT(+'><#(;h`hIOOǖ͛t8p9np\p:^!@KorQTX(t?g qF9sXGwby((,}Eؼy3'Ν=') eF=s%%X'_Y^N;㟯zHlzz:6oӁ u;<> 7WfVq'̟B,!Cp& uy9٦>9%|qm5Z5J p1dg^W+FuV JzYՊ_[ #^Z%H9P͌`".1'bbl1qصs'P55ah Ĵ\85K/ݾIUl1u23$+Ȅ譩{uױ͚؉h᠔x?dgҹ36kFS Hh”)8x :s>S'K0u4km[Lf`{Ixtxdn믿BE`;yTchڬm3vmrWx{LL:GpL:UZ nN^~o O~2xڶi/{o/tM&{~+hۺ5&? x70k,@޽v&OSǻqAL:5h)X'Oŗcԡ^l6[PukqgvDb7ijMNm_n/_o/NG&OơÇеK>r/LLnOxGӴ~T[G"U9j FCUQF}5} m;aUD3+ANUmߞl\8N/] gq]u-FȸcsNWAe45;!-$TAaA>V /S9C\|b?:m`l&$V r*jqcc!~,=F(vz}11X,z}&= XNl"bVG >zcAwU͆js{Ѫ@ݲI; V}Ep:8{;,tMQ&%%ťq)TZG:c,F-6]es{0F5+ S8Z2wdaK399uydvUA !12R&z37Ѓ8|, 7i_yR[ IDAT]WGCtedx_T^FgEDK/mE,ڈ9SA,1uF#/ߵѱ/y. 8<.]5]~ ]t^LJȢ"kpKl_Ns5C$׺mA_bߦU8z.܂7G޽uB\. Q\R ݆8$&&aT||v71ӽ*eRl0ʖX(Q!5( K?(kOOp}oGCrnfp"ge+cӴ+$ۓ+*V^d3Wd,$ _0x`N$ײ㈚^^cԭ5z=3T1:<m#'F``f~ Bew_ʧS %M"o EN]"I'`<_Eєd"H\D([2g& QyI'2R?vhFv<' giT7ǫ' /ФexQe" _,*3"9Gg |AA+ ) eG䋲^%Kd |dW!e:v2%T{*nYPCȨjNFjr8üԆ(ꗉ#gRLu"H6hz ?h9Hmax@H̒0ŰURN"%3c0e;qe1f/3,X0r?Vt-S~PBYs+ K͟4GD~T9itYaQB '[D?(5+*@eI"G ?ј({Rn5A):uL C[)K]5i9uԀI~Ij7Z4E<'i ; |4YIJ%cF%f;_Ҁa@j]ǰ(UBu}MzZ:%=Y@C.qY p<)*28 vZ@*er%xR PhnjKκ?t4̉tQ_\OP݅z֚\z⻄+O&YVYszo<0ȈRCXhY()NRK/S0-,e,$˿\jYK*ȅ>i] ,J)J OdH(}F?M xA I)8SzP&LU!%vWmvf%ET=0n$ݘ2zoW@Dv&7/welm`]F05Q?e*ٖz@t"n 1\2`El5d❰2iv:*AHhL D.2&ac1 P\SRZA8EmNIm)'Jĥw.f]f=23bp|0Y/ .Ibi]t0.t`a?̨8PVcl5fUC-q=5LQU)h ФVʴTU8Q0DdMy@QE K/&K r?#s3*>iJ@3? k(TPՃ2'CRʎҦ'A晐:IF('#(5 3> zooM޾/1* 7ͥK/fKi t( t|Ig.&D;eH)irsx\0BFrX+annE^n 1`-f&~ Ϙ)6͞sJs2YUdUí,ZɩvK:"ԌOF %1c.[Dx19pM8v8f< +E )_eD1lra_LTdSd^+Bnrg;69tUAw(e*N(&Q4L%. 3Xp (`K"ς.KbZ[thJ@&- kj}\:@ N1.i&d\ wdUU&-%VfPi KJz >r3Z'A(u.X` iDQC꩔kMΖJ3z?d@PrdaQER}x +|"F4 ϦĂs46c0‚I;a >P*el3Hg )Inj@MŗDRV\q%*e-XJn3&[Q%1`0̂a F$==2cx^؍tp&)è1 MjLyEM9GjEGZZgsװa#"2S5>h e[+,b0- @LHH=`#Y- sK x ?hgܮ,rªn1w"E Ekʦ" Bנּc#& iDqH1 B#`/a0k$ mA IMmJZ厃q\;jR&id6`떭<Bvٍ֭[ʅO{Շ=Aonv[蟦 Usiqhfv5`&w8"a䚹H$`:զʦhEp4l6V\m`o۾]MrмHgjӧohcDoQ6~$0Q"\G}qDmeW,VI+ڌ`Xv3j"bi^a 2l=py )Z{DzeM-ncㆍ͛7#55N6ՠSNbaڥ ~7+R9lCK`PXXcǎ_Ias@&9A.nfOhҸ OΗn݊IIϿ_I }y{ ظaz!{l8͛Gs?/SPPʕ+5#))\A~ nj+,,DϞ=ݻ#33S/JhvӼ3`ԨQH_F  R=gԺ9"dI+6 l1,s=#51RBBϙmB/D^zG?C/7| -Z@JJ > 3g*U< ++K''%%Q5/''UTAN/Ԓe˖#!!A⛒/c)zLKOÔ)S0yd[*֬YC-[+_{KjN-IX !˝ b4 [{ GEg=OO+ X#"Æի1|pգhԨmK,AAA֯_?w㫯VK~VZx*ޗ_.AII1[L&_WgrIW(..ҥK~3f~=k׆1oQ+)瘶>(q_Ti | C3:ɈQ GN&۝:uBѱS'Pg£bڵׯ7hx ȉG{ OF=`L2%7~ٳ={Ç1G |69=z`ٲe=z&_f͚~}4رcܹS'ɓKm"[y1H:hn1"Y`0LpqۮRt\8|0uY ڦ~JS/5>kklhK%UPsmu ajձѰaCv]Zjaƌ w9jzZzmӵn&Yc̙>|՘;Sᣏ>2#i6mN,[˖-EFFL"N\r+VҥKeRF)>X>ԩ?/Bǎw :9my}-EqF{!pp^/.'k,~h!G8))):t;L¤Ig˗N—_.E>}Oڵ-Z,+/ʖW+]vR |AꫦZt2 2zBBBЧOK~Y A~z 2ƍFzz#޽@>H]. 7ba4h06l؀k},ի2e*IGAAj}lh8ԪU <0Ò%Ik,tBtY~^z } VڙA~dYݻwcܸݻF]v0U4om|cVj+::9\_+0fX6P8řY IDAT0h` : 7u`VcVcZ*V+*WΝ;@Cs1FL[z.8$7Hn7%p" h%77˗@ӦMiGÆ lR4o v/V\C=(eYɓhܲVC$;MEzf'NĢE cEٳgcʔX?6l>SY\\'N`8q">xNDs|o+V,GÆ h"Uߕfн{wݻ'Okjժo ^=o ժUp7aŊ1bErx^hq+q?ѣGc1d`|l6vr%hRȍ_4ԁq\qhEC˸]bXx{Q,Xj#Z!ƌ:Sضm6nO}Vrb֖'TJJW4 P|#q#++=LPf>šo>طo?@8ܞ=z 6tӥnFY_^ʕ ^U) K-+ړٴGIzT*A^k!>.fRRլ,t>p6^4^ CP}ؽ{7ewh]Ki"ѶQRb" TۣM6 ^Oۦu;vǐ;ڵ'9J~>ڶ}=:]OQ=_1i2xfϞ\:d%Жpx "amoSf?֬ƺ?)!99\A~JJrrqRa}ݮw)vF.^yȷG6| G1xљT\ZZ;ӈ!Ch"{A޽{ټy8s,JJJ$fj(KkǍPQꋆqE/ZXժh'!Ƭ:8t;~h… X~曘1}:UGç^[r}7Dx^.jqXY@hDfDrr2r򐒒\I/8>s6ӥ*WD-EXn~a<`jժ8} nᆠ̱#--M-Pv7oo 7 nVI9FՂR[k֪'zO'qFxm|z>q\n7l65)OIIgE||.ߩ$ml-[Gq%dfe… WdJJJlTVMRZ;٤iSlٺ{Mo5cǎE۶~GUo=(7>(]OJ6~SZqc R_c:"vb̨#\?k}}ePu<^[m١Sg!.vl߆{6Bh6ڢ6|A f X+7l| JJJ׫ѢŭUV%_KK/excuXlt:qa˂ٳqi{5jm-AÇa͚oAQQWcذVz)xlPF\J=b"זFʨ]6 ʕ*|ߥ]z%._cGA GK.E˅#GG^r7\7`0 ЊƩ٭-rpxlS0Kh Rea'C\ZwߡAuxw `0 C+qqqσK%K'[[b0 `0'4Qn }ߋP`0 `0?x %̗Ӎ4LWnנ`0 \.N+;iр 0π7]\X`0 <b@ŮJ4!~틀 S+a=f+a `0 C7. ?-5/_o)NJbD?2 `0 Ƶ BF$it!w#2 `0 E_^^M '!lFp& ].ʅ]`0 Lh!^ٍ[UX3iZ7B7/twMoK16//3;umv`0lLdu x ]0/ nᯈc§>s:t(dA߃>:ׇE)?iKapq.|Z Qh+%2 ,@ RɎ]Ol܈Y3gȑHLLDF۶xW:|ɗhߡDؿN9>jpwW+0g7<`nNg6TR۴<aaXtn|7xqAz^^&>(v܅Z݆?ʕ+G/~]խ~ nrun= %"ymÌqL"YrcQy?̷g`nku;&7oٳg0jxC2C,%$H"!@||few>|+a\ dލ_\nDuI ::ɾ}Ab2}ϢlZPf-$UO 7{z^$]km_Q}6<ԓ0~֭Ml6,^/?111eU"?c/>61LlN9Ʒ?Fc8rn;'N0ҍ?md`h9Sg7ھpYu`!Rcby"RuPn0=z]}.t3F?V{/  ͆g]TY Uknkւ~Zf?eecڅa1h݋g>sgvι⬿ƒHMYf~fbNE Ը ROsGիIق9ۍ뮻? QZu8Nղ*jpw߾/(^!-oi?,+wE <M6pzfXpuS'n}}zs:x7mBAݛɖt#i?{ϞT2v ׇggJu`DHz&RuPn}Lxqx`$*UJ*Faر{T͛6gs?]O0J`Qj5aUX8o }> 5(GaMxK1|AXŸQm.&v]gpGÆ&bTqEWxI\?eu[bX07׭\zGP^}]P0c 0[nENN2wډ#DbJ:lM0zX$VFm ɂծMq& 11c>6hбS'~q挱 p 66L\ F$1{L4\/M"q]HzZj!WtZ0>ޝ5 /_6% p!&&+%"92R*#%) )­RJcx+ _ȐQNlk cpq *W A.}篂+`/\%HK,.[GԾ2qxFII ޝ71Tlzwʕ<.3lBii.,eT.=,RKq-r%HMQdE20 3 x=&{}ss=s ڵʽweZ_bB66@{AxlmW%..[Ν888hOJJ ڵhjFllL9ՓFW UT4?~L*ULZN_;{Ԍ'oPQo癦БݧC"!! 7$ L_f2YK0HSZ=a/Whə"iZKɉS$UBXVc%S՛CBbĝ܊@8>$=πv[M*Цm[l=k*Jڶkg6T9mߟ6n#y#eO?ioddff<孭I|zp{p?y|̢l3:jwttJX6koG1@N 0bȭN:}ߙIgϷ 1Dc6S*ڱs|w}y  D$>t< a{`|ܗtiZEzj쬚scz5}S|sm_{vVvl(-, 'k'xn%0l?~^`Qk?V UVպu'V@+˟=9IӦl޴ Z-[t<ˋk֐ª|L]Yt ?o˜?NԹ3+W-<;99AFF}^]WW32^;{{"""Knݺo^طo/Ż/r0LW Bi0Vh`BE0v8֬^5kHNNB̆X*1cuUÆחbBc/ +noמՇl(µ 'CIGw/pvvf,?.HMMم>> V`búVI¯0r+nnܰwWw㧍hߡMS&0mLJ%I2sƌU, B;/IѴTLbw -K*UЪukl%YI P5fW=>֮[P_8w"_LϠAXYY3o$Iv]g >5#GnӚ%˾/q.g}ΊjsA(-[ ʣzafϝmܴ_rXZZ1zG^7W #_{Jײ$Z׮>FG]g e_V燐S]jzAAT2228ݼ֤$c-T^IʾAccջ/MR+۳8gng +tWu/W  FʾQ{9W7jtlHyAAA}t/IY#&Τ  PJ~.#AAA(E" AAA( 3Fe1ʚAAAxY{7?|Z! X%C2U2.#FPre툈Yo5~=zy3X8G֤ˬ }'BQ5VSu'^55Y<#3 i;zO>O1v\ gø3/,'Yk n͏177/r]>[p0꺺翿~>oe-ŪK7o,t~<.?4S&^}ʕy{>4hȀ+Ƴg&^s}}٫CC9w7o &<Y}kgeGP@GE!W)KrY HR8  rjX(ޕ˗3It8::2XfM233]6yvTA}7x7Mp`޳~:zrVJ㫯֮G}k б}wڵfUrLzf`^g IDAT Пһ/}Ō=_?,-hYq'ge*:濲A SnܸWɗӦu(FT0c DKj,=t#wK24$c-%2mo<]NDMU/' [ȑx, Y oMƾ:Wΰ0vVe}6('O-\_W`XΛ;7z؉?|ۗC{Wׯ_wF.JxXΜcޜ3g6*gCiݦ ZMVV.]&d lI Eo5"&SMjfu-v'{[27G@^xuyY ym*ubRŮ/6&[[[.kyF: wzzmCE`ohYQZZ2p`͙7'Oo^@fMω.@JKK lj  DiiЀN>`JDFFC||T^[<ݺQՕaCW߃׻R/~)o<2va:}]"#0z0fHDff&Ғ=zuO;>r'gȑ(J># Mwrr"ѣ<#-ͅhJ8:b1$̐r g׳ܥj|| ,ZqUm=s-:QKX3L [27e#*x@AY?U2>*H#oy1 1C}71!k {_"xo_VZM ܽGF[N66$%=3>>[yUmmSI_WzmO^FO'}zG}껹(b3q!OV(deei'*DKO.k\Ռc2߿}i,}0d J+LA2D$aR(msE~MtErJ-<9[$Mk)9|JQêPvcPdzsHH윸[(R62[~ۇ͌ykrwڹi;[;fϚ 1C}ښħ{=  L>'&$`mmnooϣg;`JzqE}ݷ4x0. ҫ\A~ݶg:xuԻ l'=arl((b3q Eɺ8::r%,\e HɘڶkM:6<0I>~>CHN6c8 ޵K4-zj쬚scz5}S|sm_{vVvl(-, 'k'xn%#--1l?~^`qzG|X(rӧ}4mMPlݲEg24d@_Yx1-[.#I?m܈:9Ѽ/ {yvRRRXddd8/xvyIMIsC_ ~f%u c>X2VF]WW32^;{rDž'֩sgW$55[63xF0cǍcլ]$lXի3Vw;lp||}@JJJ+cOr7k?YB?=˥:gQ%S՛k  O&^̶;X0]:z }|62={ņu뭒_ac?ON9cFb`aaaFɗ-X șGn$m[Ύ5N;})|$&%q|wڅFaguv8̞KI?3&M7JKK\\\ m#}߯9BVRV[e/E.g Bi3FŴi 5,@$ލk׍i<}}6q-TͨYfϛktW6mf9,?~NMZ%G~ĊUXXXFB9'_{JײnX fddpu6ѹn,+.ҭW e_V燐S]jz^ 4jI&c/[^: 39}Ϲ$I+ccyEb߮s;Grdݙ3aSwUrU   \ T&  P݋g$`bRDAA Tw_]3a   J IL   BigAAAJQ0$#A$BX.AAc*I~QVyL=vM{sכopbVtbn51Wi=5IIeVF!5=UF btb? <#Dy%Y?7 3ɓ>oMܾ',hJ_Hە/S˹ffY;Z60/7~Ʌ;Ȑ2P&, \M րRTT&*w"ۊ"c.Q*iۖ3fRfM2.k~Ot)_g޻j?Ӿ=T9?GGG3ol8q8*WgL0.^3/JUd3=]j.gkkK֭t<4S7t2I]\Y)6(Ο;p<[ɓiҤvݻ Y(C%ͅdj4QW O1v\ gø3/,'Yk n͏177/r]>[p0꺺翿~>oe-ŪK7o,tH腋 `ŪWr%yFD;޷ 2~FQ*Shz >Ջӡ Ż7~C^D9_Ϝ!2Uݕ?% fm\2ˬ^;+;8z( L\R@:P-]SòF\M{7077ё }ƪ5k,Iڵ3#Gppp$==uUo5ܸݼlڄCMͽ pիWG.SjU_}v8[ծŪ3,,ݺkY_? ɓ'@uA W)־卞=9v._s!夦Ʃ3gx7g̙JPZiüs077GVŅK ٽ>4qҤc@홨Ǐ~+Jf}=ysfɂys=g.Jr:hNg7FAL'Μ>)a5ϧL5F@PDxXGg׳Y8v>r_͜A}ΞJ3N:E>Mv_No5"&SMjfu-v'{[27G@^xuyY ym*ubRŮ/6&[[[.kyF: wzzmCE`ohYQZZ2p`͙7'Oo^@fMω.@JKKCQZZ24 ӧ$_*Us_g&_Nݭu]]6ԟz=xk++ޯ666z[\0va:}]"#0z0fHDff&Ғ=zuO;>r'gȑ(J># Mwrr"ѣ<#5Yfߐ+W,'==\N_ktoE+m:G~Cs]'*1{)早!pkQl2R%h4(8k\<'겳^g0r^ p}-?fQ7|8#"0~ȡo&&$`mcdK]d2oU =yϩƆgvck=-qqqy988#p|}=g. 63(G__;wn}(1ēI^=iQn&}\ YYYEɺ%%%SKZ4W5#66F ekkGtttboog&+CG~w> < S$);q7ҙ08ȏW+َWhə"iZKɉS$UBXVc%S՛CBbĝ܊@8>^^5 ;ڴmgBYEIv چ!kmmMӃӃggg ygNӳf0%=ðz6t7o1h`]WmtXe Q* ccGb>ʥa&Lq\(HQ}.\ ~zR2ڱs|w}y  D$>t< %-J7Qn/G`ϦMظq_?޸Y=v5vV͹r1=ݚž)K¶vҽ Wo;+;6@̃Xlلt<[laős6ԟS?/Lٸa=#>,f{ӾM6eMjn٢32h /]ʃ,]Zi$6nDhXf ))) wɉ222 ?Y^ !##9據ktt4˖.[*chPQOtus+X}Dž'֩sgW$55[63xF0cǍcլ]$lXի3Vw;lp||}@JJJ+Pu]](k?YB?=˥:gQ%S՛k  O&^̶;X0]:z }|62={ņu뭒_ac?ON9cFb`aaaFɗ-X șGn$m[Ύ5N;})|$&%qB˘wڅFaguv8̞KI?3&M7JKK\\\ m#}߯9BVRV[e/Em+nyA'~1m>aeeͼ $7w#u#Gk>.rM@ U3j|J zafϝmܴ_rXZZ1zG^Ei,sR4-kS7+V\vK=Dx_tBwٗTn^A0ᡧ  82228o7 _k$gWL$es.Ifff>yX\tqU!;W]\̑;s0M m4hd1zAARr{O   By|G-Je4⬖ &&.EA ٹahϡQo9sAAA1D}*#L?N8TO*(R%F1U   4 g^TQQܹsUTkkHIyL\\bHْAnȫ rX8͝_~g9\κ[[0k~\yLi~е\D]˛o݉PNm>Auݼy?,[Fm8y4mڴeA(L'' +гW/Nr:4n Lxxx|>sH^6N9JXO˖Ċ~`jz~XZZaiitw &NUxuP:J[vSٙ x?Ɔڵ딸Mtt,vvsZNr~ιP&ammEttlכ͓Amq-Q8yΑе{-e˖ib|~{BvϬM4ލ^XfM")vlPQO9}S>KW9jOjڍ(!cB ))0;άgp||]ZQ徚9=Jg:u}zu`[9L&W4@Х IDAT-kX:^{_?y.Ե~]r_{t&6&Ej늱)JZZ 55X3va:}]"#_/<$iB%{ *v~aZaN?Έ#Q*|!G6n#"#D£GyG[&: ]Xw}}]jqTQ>3L8#gLFRRժ9z[6bqQqH 4qJ5.l^magg p}-?fQ7|8#Ws?з}$ry#rspx^6{Umm+VIIIT={y{=b̽={ˈJ*Ǐ\E`:1fx?ӫ'P.] 3QgBAtqttJXoOSvعc{CvѾC<ӆ"I|:gIPA-[Xd1qqqq9 g ,8C6nXO^_Ec>ۤiS6oڄZf-ܙ^^]V9VVNNfϞѸIlDrrkb|ddd8߻[wKrrۻxS X}u]]I x 퉈0~&`rԹ3+W-<}UƱfj֮YCrrd6_U{DaÇRRRt.'XI C O$az:t.664lAh|rǕ;qv:5ƺեk  wygggmKы 0iD:uQ ,ӳ[hђ2h1Cns:mCƯaaaQ02iΜ9MKO.Ť\76i2OWv4lRmҔlټ:вU2yk׭g[(pGFriڴl_/G/市1/Mw zIVKcO|˗/BՌdxǦKzذi3V͛Ӳ'6S׵KNx=#%ႠR4~_~fddp }{^+tm e_Gcffg3亷#++L233INNp$IcEi0UPQ=|.:וe  ddd.<6B&;dիWGa&GОTȹ&'OK7zy^qva6κ{ȡ{!I^^v:G3vvv|=xy77Ws/~4UP ]vXf*UY$ B"/z"H F&ONܽ{7oi\hmmMժ6xyse] T fiZ,&CAA'c<'t4?cZ( 666&ILU w]^.^a Pd]BgAAAҖ;/) +u^A4>CNTVA*Yq  25A (gIPhܹGժTdYYY<&..(y5 AAA*8ŋW\]qrrBBJkcxJYkT"YĔIJEAA*I&Y(A2h .ki_ӿ>sv$i4jTʕ+!IeffFJT+Wťii};Ҩ1[1׎󫴞њԤI(&FCjz^|x PqAx㎰_ +1Cu,P ~% ju j<{h4ڤLhի88أTV)b /t+_ s=̲xw88`Yѣ=JYr:— dHQdLZ&Fk@) iaV;mEY% TBi9 4oђ&OI&e\j""nrwF珯~CK3dx6|^]w +ٷ, TZDurv$5k:QJd22 333̴.oFFrkkrq6lپe_O|g͸FcHdT+[.H òen݆gNӦM[}Y˜w)))tUc8Uw5PSY#82ӟ3:<A0p\( W\ׇzq:4ӡxwo`Ë(3DVfJ[n=',),DƌE П{VNr/\Mr0'O@R2<0Z ~D~͛ {Qﮤ$Q,o0kzͫuj#\h8q];qӤQCvBBդ1!;wufٴ BBt٫Vx :d* 豣( 2rI"K@ BwYN :W|3qyUؾ=_o4OiQqbw`sg$&}K,f1aiiCX'8a<+WձS*@JpѹKF3giب3M3Zѱj9;92ELѱFV f7wÃhcWǜnnI\D}DM:hب_/ҠaC9}S>KW9jO B %%W13ӧqu;άsX8u޽I5ky[tdPAܺ~ 9rOe!KAoɓݳKqfK#DŒ匯ϫ[ڡqpFؐl9gr$1羰M&+j7BNU*chey1jR3km;3uݚ]gWo]za:}]"#?LKDff&~JKKG=v(g6Ұ~6%m 'kX:^ < S@&aRn +ɦNĆPlw0Byaφy2IHLL>_+qo4D)*ErB1sO0&MyܹCM;z>P.] 38ވȻyF"SFEu...]IH윸[(R62[~ۧ}P`q+P1P{{{(>>_#WµDžk4ZLEiۮ;wl7}w.wsx` $ɧ?}|,SbX-g_͛iӍV37oFV䙞sYg$I4cqqq6Zzj쬚scz5}S|sm_{K$^kiذrMW77꺺AQؽug߾$''o^Ίʎ#7 %}6dWb T8.k׬!%%U]:uLʕuf|ߨqfqYk֐:9 ױzU0c~ܰex@))):=M²(o#6z5~~} ?Dj55 zz.߼NȮ]NF 6ZTh7<[蝀ͱk/Ub4|wϞl ??^əӧiӲ]ȇ}T:+-X0d>ˇ/Au+PqPIpiZzt/&=8._D U3V.cæ?Z5oN~6nL] i/^OR4~5:dddp?n?"o;kWV_"/Fѣ133ӳUrݗEff&$''sj8$ѱc<#(燐S]ju(  P,B=d2z(Hړ%9ccF{K]vm77WsHr8$Ixyڵ9;..iC>|Hd]\WϽ\&`U:AAA 2&ONܽ{7oihmmMժ6xyƦ#AANJ:޽!clllD%  / yNXia͂   Bh/GyjJ?CfAAA0\JPV?C<'LyFY,R  PIXO#   " Q1mن~Cv:FqcTRT*~AxF Eyw!7UTg8v( ceKˍ_r2 ԨI2KBmF-W#ł5@4հIJC/KZDD-uWX*~A0DYۦ& 3x7m.effB#o{_=}۱+$wƆz Xw)))tUc8Uw5PSY#82ӟ3:"FRug9Bwջ ׏/BPQMm`Z7o,o˿SgC"WYIƀd*΍hjF7zύ%O(;+;8z( L\R@:P-]SòF޺y;wIl<ݴqccc<8A騚4f4W5óYSvhU.--f_cpwGn٣;/^,0t4jȮZT4&dNnٜO&yɽnnx6m¡ԛ{9]L }{V7YG>0Awtt4G}ǽwUN~dx={}*6r1UljJ}7W^܉cG7mP)=}&=\j SΝhZ# g>3="> Rv4 C<]_}s\sjc܁LXXG[̜> %ɠ6ur((00C,x>8[ծŪs׳ݻ'NsiAj {xpT* @PDǬgkVZMVV.].0 pO>SB %%W13ӧqxfOn卞=9v._T6z)öҳGw>̢nFڵo{ޜ٨T*N u6̛;Xt'v۳nF#gK}Ә\Q|TnD  ʢ7%C'd lI ӵnjYкt}fҥ>c|N1./_{JrVzɍnNܾY e3 {jݺ$%%ѶU+.T`\W&(RD?&,.JVc TTMsau&]:uv<&0<|{<.rgP@r(WITL4-W4rE[ᒚK.-▖z++MM&uCPAvf ì kb99ssoOx/PߏhV?owkؔ]rb}o/Y&}|HHHͭ<)׬Fjo=ݼۿʕڵKe֎ӽ:qO$(_9'ww/l.mس=ۛ-15Ʀ;XOEzto=#{S`}3w[RmQjU.* P*( \\\K빖HHN_QS/-_~?jeKl\v?0U+v#5k WQ\3è h5~zfQ^ZJ#m{Y`Ϯ{{{O*.]"۷nQR%랿|//B_xVX 9'Ld4iHСԫW}d*yydV ̙,_a܄ IQsNxY;w yߑm);XOE18{O1vEO[@Wx-4lAղOdyRD-p!.uҋ0h4hZ S]LܥyS\N{{{suo)OOORnescn3j$fVr21+qu~g̨Qs MBŞsׇ3hp$ɲs=Gdm{{[-LJN'{S`)ea̓#8j_~atYɧ4fݰ$HDΆ~Y|5nIMղjŊۂ۷gҥdddiˬ&%%}G!84aiFbqpqq{c00EfkӮV$==˖Ѧm[ҙE ;y{2; !JD?#;'eKC\\+<.3jOjՈ#''д;XOE1J*{7i,YJUpSʳ|\l̖ҬU:s/6op~-\b^!(UyL jۖ~IQ8qMdrƌk^V2")~1s/=Ŗ1y [6om4l$nnny+Yp!!|yYNf?_&yqbf:?B<1'9ma}SO?Mֹk88.yߙLg5 3O(4S4iILD6yaC4M/L#ާ9KshVYW,n9.gmaQܼqB IDATAήB!DU^3-Q[XY+x,-_njaKYg3aF/!04r0w"-5%114 ,>B!D9S3_psKΫS 7ӧ1y! !B'==b0`{Lf%p!!rB!D1=Sb<fk.2B!s„B!Bªe E|/B!%KԗLZEYXB!BE,aMx7Mm} FCV>c&kۧNYA?r;rqƜXVͼME //o(t: j N91gWٹ@֭[|1ftvUʵq/#==A6 $vժV| P~-$tzj5|<19z'bO=0}T*V C!kX\]]Qp}{ҫgO<_zeCI͉c?DˋJGqa@+ 'La>V,_˽zQjU),MtD4?jRJBl HJbuu{b] ܥ+2n\Y`?NGZر}{ۻʕ}ζ_cK1G@nn.۶nnZ7ƍ ;;ƍW4lJM]v>{ѰA}zW._6+]bb"aa<^/>{mر\ CCپm69lG>b5G3}G=/+WS& +4襤$"A?ݺtرck>۷ɓ \84n_}% >6?geeѴѓ|bٳt,͚4~ɖ9q̝ `KOO-[4x]iߌ-rTPո丠LUB2p`d<[YyұS' ?Ezx|om$Go"Kgz:ZiZFr*6~Ysy]9x0ϴlɜٳJoЀC%00mڰz:/f=v—_|nB)ord,ÆgJd6Fgdm{;x6jĉ;h>z1oQk>ϝ+w#dee{+iiir 8̙=WN8ncK=.*,Q^qϏ?3O쪈;":F#%ҩPf(QVMJ&B0ێ7;Ą~SfW~~;t+yݿW##Ѹ30"~7+_~aah4Ga=ի^z̏&z"7nBw!!‹=tkog`m{̱o^DХkWnQlէ("|@<<B͞sׇ3hp$X8bw&k@vϷ0V @|||mJN'eӓ۷Ѯrʸ5 3,AxȂ7놭%F"7w6[O5j3lڸ>IIIdee# 8I&r֭]C!6̳8н} hҵi׎U+WehӶY۷gҥdddi+oۢh `ێ<߽Je>/(!yϏ-YqqqPGd=ժU#..BvҘIIIawѹcYZQl\T-V(J*{7i,YJ*"иI6_OZZ6n,=+_8)1s1_Fҩ_ 2Wc hgUk拯6sq:3qxw`ᅦ }yZx瞷 {ý:f;qRqrh 'EqqZ6e3vl޶/6oCHBQ8{̉oNFxX_Oӿu6oDž1;Zx2")~1s/0+_5Զ-O=tm&NbJ$ڵi hg8DM–͛i۪% =Y9E?w2PZuпiUgZsgѳ׿po|Z8B!p7oܠCp뤳P999v3~(J߹UvQP/T(ܗVs-1,~Mz ؼi#~~MU,L#9h<f:v˚KK!B!ʸٽ{i,i`TBl-J61InY&B!(u ci$$$dF_`޽9A5R:O!B!BB~fhB!B \hժ(r*M!Ba P($ B!BǺ化b;^!B!JZg 6%|ɓDMșӧ WsDÆ%^ad0ɴ(MuBCDQ^cUf?n,#"x{k&N;]rk꺩xEiuђF*E"x( *Ѿj0:-)6}{=>} FCV>c&kۧNYA?E.Y@ȑ e3:jo,r\p~a}Y~C^0U+VpY9=aX$Ξ^xz^~g82q/#==A6 $vժV| P~-$tzj5|<19z'bO=0}T*V C!7V\ vkkiq׾{ճZ`/sZBLΟ?__%棏8p0@bmdŝevy/?tz Zvv5oo#PԨjT \5d@UC ݫҘOH.]quuחq&|t:jժŎ ~޽TKvv[9jrssٶu+χvc1n6n毾y`S5m7_ڵk݋ rY  z݋mcƎeOm͵G͉Og;Rߏ91;yGw^BZ5Y0o^y'%%1? ֥3ǎ+6_Sؾ=M4ނSVͼ.?OH`7|yN/YYY4m$q8w,];=K&,iߝ `% g`<_yiSgkߌ-rTPո丠LUB2p`d<[YyұS' ?Ezx|om$Go"Kgz:ZiZFr*6~Ysy]9x0ϴlɜٳJoЀC%00mڰz:/f=v—_|nB)ord,ÆgJd6Fgdm{;x6jĉ;h>z1oQk>ϝ+w#dee{o+iiir 8̙=WN8ncK-sb %/M}s 'Nߧ4y2&١!V#xҒPT(3n&x%{\!mǛ_bB^^^y?שU+??(m[Rٷ2Ӹ20gl,\Ă W`* t: ݝ]pꕼϫhn/0t04 #wϞU^=GGpsޛmi7 ! {ω~C\]]yŞ\tс{3}G=طw/Ghҵk >t EJp*TÇѿ4fv?8=Vq y0b /2?0/ؓcG!WaI(23 hR46aC,;<?*L䐡,^LF/`ljKYfq?a"cnjf¸q>}}'x/LNN˸IIIfj<,:j<) yi&G1n$(_9'ox. SA5?{8gw$kc[XR۬hZ*U2~p]G̮#@ʕ S8ܾgŊW`wӗ{>[6o&33oނ$P`-\C$bGmE`,ж];>ߴ5l o/oޝ5 Úr}=ǡoX;v&(81F28oVZn%'p+9W쩼%`p`pƌEPp;w@߰RԱ8 C9~2A5?{)1P~-FA_`RDp٢q$mc>EKN_Vlh֮YMFOOS 9=Y7l- 7ѸzyV8oeƍIJJ"++?aL<4Cn"eŅ=;gEGMvZt/[FmJܾ=˖.%##M7_yEG3``v=P*IBd9ߟaْ%xg ;= EqļLSZ5)4m,~;55nIMղjŊ۪TwҘ%TE?84n҄ דƦK _Xފ8&\+_5jԘm;v:„~-\r :YGڵ̛;AȠv:GĠA }y֮^CsR=^BBRIQ:O5 i` ov ")5j<»s͛BcN|s4틇'sCPЭs'bO8⸐#}g2՞U00)Z-G/mԔ)9M[.s/0+_5Ç|2ޞ?]mIL~´3h4 B(S5y #˘[V07Ze8eqΟDV[V!BQ7n!8uUqv~T*ٽ{'UVEB0^P(pqq/^Zb"ڵQ7~ 竍x1&*C5Y.Qw=B!9ݻwʒ:J%KsX;)erD!B!Qa̜>lԈ ̻w[8 0.yoaqU0!B!, V% B!Bƻd]g!B!(A*4B!BqE˖%-^hy8Q!Ba [W_%\U^e̕˗7 =SO?Kj[̜LTPWٙBCD!JC_~VԙNsveqa[?0, 2}{~<ƽ0t(TZ O_O1@ B@.׮u6jZ-bc_λ̟;7/%00L˖̙=۬toϜA 8Q lkݦ ׮#zbCh.|v=!ăsӈ&O2lpDMvl#ld>pƼHֶogOXh3?XޜUls2 +{9BVVVFnn.GLÜٳĉ;Vt'yYz3f;wҺ3VAV!Wa<iit*JTUp Ķ/1!//ԪY?~_} [[zi\bq3{6 .b…ԫW? :׆GN.]zJ_H4 J_:lCػg}W󣣉^9Ͷ儽]?CHbO.]ۇ=#Xsۻ#h4tڕwoq)C^<[4 k{NN B q8|@NafcmDS3au{؉hӮ>+;J~H$ ԙj I4)j갡gwz}&rP/ZDff& 0dk6,80cF3a8N>]>ހ&''eVˋ$iZjHZ5iؔĄc:# 9bw&SYz rFM#hؔu2w򵪞Q9|˗/YUy&LĔIL1F`(425Q0bp>YyѸOISA^._bEqGRw)z:`ți=g@qz'-.qKB!vt :߿,^믨]1<*V$./]R@TP(p'2 hǨZ>^'bg=VY$jjͻg +Ƈ,B!ܻ7[fIL M]c0uzR*lcAJE~Ԭ]V.cwæ|a5iB!ҧo3O#!!'5b~0cר].hKzLfcVa@ ̆*@B!(,_A?INNd3#Βbr&K!B!wɺB!B֭[]/GrFk/B!B{0px<44iؾeZ}0 B!B%%4oќ?Ê: k 2s2-JSA]T6i PjK¡? 66C!$y晖|k#&NrB-˯rQr9EKKi4 h**T̪D,봤ؼK)4 -[bTQ#o:jfgߗrd"GB5ΘԪwqe %VN#z|VX3g7զp9*yyܾgNEpܶgxx;Qe;z~sA]vhĸƑNP P;jU >a\gl=oQl`ь=>ujT*?]Uhr'ڭ^WϞhyzٷi !ʆK3:|>2*yF.]|^ϵ˗ԭ+j5W㭋_(_,r˗^:kŋ%U-q7OUjJ5* ut H*Y!ՋwiL 'Ls˸ Xret:jb~o^*W%;;~-5lۺCj 7l7bW_<)͚6ᛯKw5EK\|٬txEJJJ޶1cDzr't emZsIA9lG>b5G3}G=/+WS& +4襤$"A?ݺtرck>۷ɓ 㙨.?OH`7|yN/YYY4m$q8w,];=K&,Nث7tt9$%&b޴iuwτr, 777222\3˅3PBW2U u_uGl]cccةYhz=`>z|Ie#7ڥ3{aYv۴@Vj9O?¬weܹyi.C !4gNFԔ79~2aÇ3%jca#{3}G=_`<5~B~Y4xq=Ƙ7b5UYXYݻ`=JZZ=~0=sfϦkh(?'X'JHŋdee}Vf&._$)1I5#oY e_XGZ;PB^'gff۷a<iit*JTUp Ķ/1!//ԪY?~_} [[zi\bq3{6 .b…ԫW? :׆GN.]zJ_H4 J_:lCػg}W󣣉^9Ͷ儽]?CHbO.]*Wس9;1Ǿ{<8FC]ٺ}GW>t EJp*TÇѿ4fv?8=qFvfYY H T06>(WKN^r:u-oa~H$ ԙj I4)j갡gwz}&rP/ZDff& 0dk6,80cF3a8N>]>wVR.LNN˸IIIfj<,:j<) yi&G1n$(_9'ox. SA5?{8gw$kc[XR۬hZ*U2~p]G̮#@ʕ S8ܾgŊ|1΋V*Nٶm+ZmʳϚwp  …8jHJ/vߖ:sm׎7mEMF-[˛wgBV[[Ն=ǡoX;v&(81F28oVZn%'p+9W쩼%`p`pƌEPp;w@߰RԱ8 C9~2A5?{)1P~-FA=.Rwoԍ'eӓ۷ѮVB;]m _ٽ6|}x筷hؔ?<[orvfŪO[\KV׮1tZ'09%B!DY!$Sg-}1gWiƎ@vN .&Ί)o z!:1oSIOwJ!BQU{s撐'55s -XWE'w)h%/B!61Pϯ.8s,>s3ͩԩYT֪%/[0!B!VRy%<m݊۷ZFlME!B!(_'b@tJjUl#YΆks܍-ݷ0j.\dE0 ddZpϗhyW#: ߙ.9͕ób%ɰ敛Ŧ+z:`{(^x:vFܙ*<334 tV!B!(?T*  JÎԨQ Ywm s( z=^nnR!B!D9医rzYfP*+B!B\= ʵ )T~ci(JrzZX!B!ʞ\}.J,AB>WAV1B!BzB†m'LT9bL!B!(az.YGgz!B!x(-Qoۙ0 B!B(:uwVGL!BQu:6PqDo)S(P( L΄ !B!}n. Ba%-`@P L!BQuƇ5[gd{%4*J:uB!͵rǏ1 s0!B!DZ#Nn[)0 B!3aaaܹZJ^oKB!BQf\x91t36X~:MP0B!B\}^,dg;0 B!B#zg Ѻ{ <#L!BQn rD!B!cB!B/-67 9Y!B!x)B!) r*L!B!w垰\\]N=B!P(]\,^JKB!cRy`CF:5j>1;0W*_|V!etYT}n(%*_|j1(6tvǸumB+>ƹ3\\L/%!b1HjHMnݼAvBWvv6I7QZmqI!XzL2Ι0hC޾WF !ee))Z<<ޟ̞;bz6lDyo*νe+#B8; !GP왿p!-7_X_<==ȟg> h2ͤ$[V._@xUʶM?sxB!J8!, gr2oM+#8]>3Xqׇ͈֩[2cO`Pq& {/@޽,*+--)SY`j:/MVV6'E[3;~"vgiۦ5oNq_ _§R1 ڴn @[|>7~~7߸7V*?@ᆵc 6w8[}wNNCn]ͷ[‘$ C蹿ˉb5\oe" IFŖj%u$իWH?. QݹQVeOm8+/_)Y m؀?K?'6S|;뭼 +Kмy3|k-7ӓ w",^īٰsBV*%>`1`Myy~z->ZKbg-_#Ff z^ж̜>7|>c@x?:=۱U* \|B؟J*E_V}~a}tr{wXaVk}Fڏz(O >`nyɫ_CBxk; :OVBs_<߭+UpkΜ=Ov;.ue1@Vf&vn]Q`P7jތ7nY^?C*y3_{N8rh޶J+rAhӪs}d$t:BBpU>*/9.ɫw d7&MPTkӚfq1֕ nn<ݢy2>.K^ee퓗%JPжM%,z9mj*x&[2>ZCTܵO6'/PTuww\\\ 웞IƱo2u:^^^D  fRY˔IP*T+zB(bX֭_ϕdee]FjZMy$Dq矖헓>dP*yy(T(] w*'̝C_ y(Uɷo}_m9. Q=a̚l|-oFsm6o 'ٹ{غm;O6lXh^^\KH@Y]V:uh-G/nԪUӬvWv~M4fmdeeg>f^޶7K*U-4u4_mBQ=ԨQkn6<==PlMw[IKO0nbT6{(_q]΄ 0i2ii<<ռ9_O?fȰcİ +jfϙGzF˗|lUYٰ4ŕ_&fr Ne"__/̪k|}+3dg>V^fLA&BQ=;fh䫠P01j2kV4YFXX :ժU!$Dyꀡ_R/ZPq>vkeo`[ήUk4| gWG!Bb*òvZ/_jOA=|P!D)f5 !}|ݛA" fb%$1ckV"J5j$V.UKQ#-WkUhTCؑ!+7<~{?s?sB!LSIߑK; IDATy!B!(J,dB!Bf2YB!Bv<&B!H2!KB!JI !B!D1FB!B ;L)4zI!Di9B!i/B!JBfr'L!B!Qz#L~2B!B"ȲFT2BRD%!tGB!Bb!B!"oNB!B#E +褘1c%ЫT*+JVָ`oHQS5l@x5E/lhdLXjXRRi@l\PGG*B%%&qy<<W!^[be)RIE7}k/^AL8+` k$?H@{5{.ޟ_RRRxcy(^;JԮ (+Veo0i=z`оN``ܽK'07bܹ{ҧw/3lN7[BY?kqMVʈChV䩯3r0~\:]Ç !55 :İOHN]6[שUa}Tubcciۦuajj*S^_om'+>BOkѓl<{bb"cԶ8.e^*riii8UsҶukgbooV5zt4c34RTzoiX^ \\\;eVZ\;npF8XD5l>T\6-[BY˷de["^-s!-dgGrJ Wl"؁6l2Sb?R^ zE2r06|+\/3gLei:Owҥc>'+Y_{ }&'%q%2E0j_Dtp}*׀tFCΝ9xѵKg,,,r 5 t:V~!>{+zJr2)9^Ò XWPjrV/w]e+:HCiwk7lMV,^0˗lqqql۾Fnu?mHpb7⧍;LW݊H] }Mq.,g}}ƷΜ)RǛ~Cmrr2ڍ7~ w¥}iQb?R^JEZZ퉍 ^-[p]C=OILL ڵ㽙]Δ1l FJJjϢyfh4:oϭ۷Pٞ}szO.ѵsg8a8wz=]:* VKΝ*%IS.QF pfh;~{{{jժc|{.Nf'wwfGy%CxXy|}{7.ȵL>([%K-(pgFnnu/\"ӗO>!ղT8@k@Olش)^aKH/ h3}]ZHxJʴ?RAŊiIQцţK)ýqc*ٱnzlpog+TAB*U"66VVdG|B*`hgZ&1q=,h9fMEM˯Ǿ,`iI0@]ԩG+WQץgH\]”O)oVt:Vʕq"wg,4u5kԤFhi*Uč7.\?6Orr T-53t{gΐF  oRb"Μy1+8.ze^*L=}}m^zf5}$%'6xxsn_51}`E(~WyNSE*3jIٞ_ֵkb8t/ `X~*hy3߯leI^o:y2K?V?,--߯/Zmaݐ`2f͙C#77wY]3etƟǻ|*W.SɾMz2cf_pYț3JvD^,w@PѶM\ҶM|4y>oM ]s c xWN zq<*IܹS"A5#zJ8ƙ7:VRxrp^#GceNBd!ʷ4ߏvmgt:~ Z-'MIg$g2]KIy4sִERFy!0Jx&0OW.]>x[M62/hzs_rI_TEiCy&j45XYYQ갲?gNIIT}/Yeu;h]/ SOlGbb";=ŭ[jތM7ګ%Z`͆x׻;]ҭKgW/^7Z4g]FVj5.L:,_N@XWFё]MݻvlU·gO~۽[EtSxxzRF FcaiiɃ;wgޞ>XdU^#G1.0ߞ=@׳db.5~cV^+bccye͚5cUvIK2A FLZL5 ;J >'Ofs!~gf>W_Pt{Ľ1]m+}қVR\Z}$xkN՜8~Yq`ccc2D_oҥ jc`D]+ص!׮^e qL=nyC#死7NtNoUGqPreuMFFy&eHJJbs}3+>h|[;;&:Çt(3611`ݻwGSRR4qΥzFiƎiӸ{._~ƛI'?sƣ {>o29_~&uˣ9Es ףv-[7Q./E@la?|~{~ {Q7B9 .]Ea4oт+ܸq+WмE :YءcGTٳhK'T$ߒʢϞ5zxȔ0Z͏?@BBAkUֆwymv5iwwwnjif4nؤ5tuAÆRjU"""Jy? ~yhVɆÇ+}Z[nbrڶkgV%x 4tqnGy̟I`["nnF7h= 2qtǕJif>u*M (Āϒ ,p+&߭V\SOu*" Qf(4OXA VK h؝ڲmVޚhԶkLǎ57ǎ1n̙J^hۮ#G3޴N;ФYS G|uAd|:?cZE? ,>>ݝ8,]fV%0cGҡm;N:E8*R~gޙ1\ڽk9ÊK',\?E_cZ7kyQޯz?G6m;'4aeZpnIIT}qf%j\o_n]nE]WA> h֠Em[H[CJ(BQRSSڵꐦ_Ij$nFGiM^zZmxTZpsFXvBr-I#<&Qaq(0NJά WfNYhZ} /@Ͼ.E1B!JFI((ɣNNNL1]{֬8J|VAS ([m}d gFɿ* H(Kţ~L z=3.c1lC!DWEFre&A_Jȫ_tI|ʪȫ\|xxx2x.\`2 6$d·_dCǺ (aʕo߁ǎҡCG>Y%S ǎ o:O? 'Nbԋ/1}[^DU2w|ӧyXݝƮ ֥3 @rr2-5jOBB۷ckN:oGBBB!'Z SNet@˗-58իhpttd?gYq{.;Jow+0ϩ'yi9yƎB ۗ܎ySϣ>Y7HޗxDVcaaFa]h:Qj5j'j"Eeh4h4͜Y!(/'k hR>;g6}eL` '99٬[PϹYdTNľiw VVV$$$8yQ>̞3Y`>sym{%e6]-glJ%\I͚5YW( a ~aǫU+܎ySϣ/Qzuߔ2MRaѱ21wiÆ]5@ePZ5<ȇ/?- !PZN\gm\,XJ*1znM >&jԨADD4tuAÆRjU"2 *-7ʼ I|-7 =EGGrG.W&O6>tX۷jܺu˗Ӷ];(ۧۈ#4t>/3ol cܼΣ{qQkyq^BQw}U9m:AkN}ͷ1-~I<46ſv /}6=s&ÆҧW/ڶkȑxG_;}|k*{o{ykse̷ԭ3?v0-Zaa_űd2(a܄;zmq))Q'\]Yںe +>Zn4WFC+cޔzu9BQʩ}}DncysZiZ.E/7k?KK<o,ݹ} BZθ ?;JUbԋ/1: ?4+B!e/f hҒKKK|>}d[߷3S"WkyETg6B}%T_WjUt~O>_Ç(\Z}$xkN՜8~Yq`ccCrr2IIITXQe$%%t~L(q<ի-ݭ[7 wWlB!( ӗg‚E9_ɓLJWZ5;oϿ aܹcg @Nû9u?gr [./tX۷jܺu˗Ӷ];(ۧۈ#4t>/F@n cs /gI|W|WǑwkof+╅sZ^uFyB/GGT$_ghMeE&{i={6^-YL:6m 6mHmhҤQ v-m[yqy]EOad\?1cFiM9~+G^sg/<8-6a/yŤ B\7ŠQ|9~}K.g -"*._akh( 8 .GL پw}.(Ȭ8JXr%wtБOV}hB9o:~_}+++k%ȫٖungF{ifL}uB!D+0м'[6o^͛98uAkiՒV-efCty6Gn9|8mtM9׷Q׮QϹ,Z͛76 Fn ǾٳswfL5ѤIncm<Ͽ\'_7o YGcww> V~ 2{\HHH@qL6tpzx^c]tixxzꖭo)qpj$5kfZD^^Q ķgO,,,,Y?dͷSbccdǴjݺ"Bp`&ƕT*Zq㰵/ǏcIד&N0wrrqZϯo!Wݿakk˘@c(sjj*GcccSd7n ٺxo,:z?koAʕMd&11QRׅM70h nۆS5'SzgZruqj5{xG)Z!Bn.EyªT _,--tx_~ XA>Qzuf>ڷ/2Ѷuga AX\r٨Lj~~~/_&xF&ǏAL!)) +*(cymӷK|yQ) M?#άwgҫ~i&(B 8;`bcyu"Vj;5ݥ\':=Lu套GsB)GE?{W uؑ*0wll+'MSTOqLn ^XEB!D QftDURʯo 4QйK>3YaC^Pl[Jf劏i]LlX?|>^۷oju+/mvfQOBCGh6}_|^\Oqbż6$%%tq /Q$P}!C_ghMeEkx۶ͷqxd_0eTŶyu~ǎ~E? ,>>ݝ8,]fV%0cGҡm;N:Eq/̗ۼYmڴWr /ǜ2& ]]>b̘TBQ}}D?ҽ"Jj>ܸeѻ Y X9B(#55ԪUzXZhPҟSTXXXdKF͛nOԬ;hP>MΏ3^zZxTZpsF#S !DyDym 0N}ڬ@ !B!D(Cyu/*ׄ9R!e4 _(}B!DYQFB!B"1c5YBɚbBMIM֬W;PXdBI~/dIk6 Jr!(݊>*UGGUE2wռcp>,(Ntt4S&O]V֯G3OFg͊Dccc?:OQpyӢ>M9Cy!`x'LDU.]P<<<,?g,^0t̞3jY`>swfLgʏ9w^5ku69ܥ  Z˰!/Y#~Z+':W[hZnݺŊiۮYqӃm ooEů)\y8/KB5Ugm!(Jxl#7bƴx'l[[[z= ̝>o5 J`I_ghMeE<ߞ9aCӫm۵cQ<ۣ` Fӱ};6o1ΛoM3jϿ`ԩ<+[g8~A7aZ・+݉cefQ¸ 9v(ڶSS0_ 2gVAGS|^ !z؆S ~{Jj>܈2/-@T5z??EnB7W* J"[ڴ4oޤuFLD:޹ͬ[QuQ'kU N)MVGxL  Z )uRnbͅD&B!IaB!BQVV4F÷|/Wceeex]#L~e"&ٰnA%;;.X`s?^{s 87#*7?W72?7F2^&B!w)))4oɖ͛iWflf]ZZ{Ulټ{h(]D ѽG6i;wQ#qwsY:u͐hޘp ;h]DEEV”SeK azh40p ϙcV%޵g{ή={݊/jqvvfF;NNHII1+'^WXYYQV-f?h9'Uή^|XaʝJɩcnRBQ~FXHFXZZΝc}̙7= IDATY$pطsxBCwfLgʏ9w^t𺟟wb̈́S(Hj֬ @Z,ݾ1Xd iii@zw%?jQ?aaaȑ#'8GS˝Nƍ,[ݺU~!B?(P* VKqח1q$I';998UJ~~gv;܅-={۳!llxӷ@e]%7_]\^[[fBѫsT\8JHNNkkk[(+.u]شq@m8UsͭQ?wnߦSqPרQ11*7d.ZjoBQN=L SJ*UO %:oݻzrcaݣr;Vݻw T6UH\Qqr2NЪj9IT͉{1%lllHNN )) +*P^@X>\$.ُ?Ol !-- tOnUƲ-$=OW_ؙBV?@iպu1Wtt4+W|OxedCܾ} V˭[X|9m۵3+}z8BChh 060?'L+ ޴d޹'V1>&*_^_}E|| |[V9Lc;3^ݻwYr%-Z4B!('k.SauzY<kSpI͘KhՒ?d ao5cǎҶgΜi.VB 4nNvmٶu+oMBȘ[g8~A7aZ・+݉cefQ¸ 9v(ڶSKZ湲r7WhӦ-z?cxawV})fxwp}4MFǵڵnM֭_ǵ4hذ@uk]ƪ>3ܥZ'WLB!D0jG`pݬZKKt5=*2<C!2RSS ݴjT^o㣪LzH$@BDH( ґ ${룋 QQwWPHQ*( BQ`H'LcȘI&Nf>o_d93~9`rѾy4VG ġ/GLw( yZv̄@x*8XqL5vEd} [bRS>xe_&p*4 =$""0;K"""j/&Ww"""""6$IO 80a>d!"juߡk?Z=e8 ""xk.ֺ̝=Yձ[ɋpEgZ7GÇ/cg~ 7mF>}?uE_{U7)Xnm:"&Ek_'^{Uy]xxx Fg6,K fQIד&Oơa4F78ڒG YGD +3S2H:%))3v,r9DQě+V`귰is>g? Z[ɓ's5>qdlX:QW׆kM2ͦGSA^Gʒ%1cPVVG}:77ǜ?889yy8iM 0닙f7Ϟ?v ?= N4JKK#FJPT$ظ8DEGaO!8$=-9ixKY^yYBBBmҥ 3u&N:0@PPh4``0n:yҦ`Q5AAc"=88ئI%33âLLI'kᇟ`{GZbW)xzzBjkk-Iz)֪U7@3Ο籔uB^^^텅ܹu&""C9sgQxǟyQPTX(;w6k FyWxpB=sUy빹`.&F~;:u˖>R?-ǭ)ñ{W!ԟ waW=Ykm3+ýcI1uo0[M8`@TTM0}۶&HK.HOONqXᇨwه$%ɋSe&$/N.y,ey/p:CTVV7mĺϏZ/n.FO֞߰73{ u[ؽkF> }J:ws_q4o.nr+OAq9ҧCõZzݒ.|4Ԣo 3jzaLHTTT͕#%?ӧNaؐ8w,RH;ܑ:ZnuƍjOK籔?_~_ <C[>=bc˞DDD1?)'۵^GZ5$C쑛S7ߟՔw6;N];2ǎEXXrtO 5 +(ClaP'4}y=bCPXڸn #Ou4z#iq<.cyDN;5`KLj'/ך[f:.Qf_{p`/ADD1kTm/"j;wIDDDMX]K"""j7bDDDDDD$z*""r-AZX)JZQYr,.1l\ՎCDDQzx~DDD]]CGt;z6NEtE<<أ:xǠ_bϛ_~ٮ2 Qx'09W4]r)B@L>x%HØcաC[l?V5oi`رEoX¦q4+UWV 4ڣN|^yi~7FDn;!_~_36n;DxD-o]26d4y1##w#-G۶ۜG h4Dmm-%;69Grb<_1a$| ʫ8ix[;q E) EBbeٿ3DDDB{:( |OHNYX)KrݵΎ`ĝw5,3gZQQQ ALL' vt ꄗ-n>}8Z:ufk => >ں{u¬#TUUam}=xFMM og1b#6Z_Թ3}E޽h!^z*//yl߆??yY35(**^Gaa!֬^!CڕG IF#5u*++II$=>9GbSɋSKy4uc퍸x{􀿿?;&I9MKD5T,{¬xo7@Bx'moSOc]q0*-})~!t:ځ&G_cBtdWЈHzt @Z 3:cx=DDMވ+eZϭXa=Ĥ}`eM]OYpwzN ?/}!"jY9ŋ%<Y9()sZDDD$S,MSY =O?eD7 W5WVV"-=AAq̏{e|,|}|\V?"""k\/Y'&Yӕ认]sYD -W]@Ra1J N촫H;A~~!|lx!b͎̮' 32 >*:=:ٙ2~VW#;Ϋ#QK\/L0~aHDDPx=h5kkjvE"""r37z$ #"֡4""ߥ&_i`~X.^ȓ{CB\KLys?ەlV1\M0źRsӘniߟ>m-OKз-xYYd;b"af˵R-·GZlQ,N^4>[׭}'NDDDts p /kҕ8z;L4۷m+{v08} Æݎ'( lܰ޼y}~R?YǑoo~x/7ʷe&,X[neX5z%#X:^\O.]kN<`u%""a{4 no~ ǏØ{G9ruph4kjjp;P^n}}x%HØcաC[l?V5o DrJ ~Ų's3>F~ќ)Ӧcޮ~)SK.&L@A~>^bq+W~EQa!wCu%""=azݽӦ|L3ၪ*F9og#F=ҧpq$~sߴqFF@@ղt>}pYy8w,:Y`$I'0 HHLD\\|U[H!;; de6nFc$%cƎ\.(xs \6m`'VŠ-o޸ %C.cQr lބ?sf#Fشe9MTbyصk'Kw܉9Axߺ|N^?""rm/nj=aLQ t3w<==_ s)S,\ ~`U]]׭âEk$;{p9l jז`0 u>̜1og~v呂FJT*Hz|Vl\k}8YtZo=ӧN'N{SfLg}2sμy8jߏs!""OF>w8~^q]uCD|zFy'#"=Ȥ$={pL .3IDATߩL&äɓOg #-+<==Jkkk݆ ÒCSUP[[oō )Ο?)U1crsr;>1+.9t'IHLDmm-SS'Ob\KG^P"‚:+}*^֨?lGƂE88rK੥KQRR֮EʒZ-7ᷫVmݲb|n:yHS\Vjj%`ul6nފe|W飏\T3"""ašGl,:׿Щsg޽{#eBzjx o~O 1Oe2//yl߆??yY35(**^Gaa!֬^!CڕG IF#5u*++II$=>9GbWɋSKyxxx`+E퍵;oo[ޣq1=V~]O> NUk1/VoCo0>mϞy9̛= ƍÐCѿP_!|4<CضSW/`̨Q1I#QQQ7W+<N:aCٳHY"pG_57>zo}Aϟظ8̙;>4`M2mռSM-5 o^0+-GA~~=TVUa;|8<%ܿ8ߣ^kH5y99vW\z?Jeh gٷw/֭}{>&7:v <=AXm<nܰ8lKKw̄@x*n"DD7ZWʴ8[mZxǰ%&5EW\0SNߗ>KE{̙gO>QGQx8 g<M *4̟7GAxX(r2]]%""".kv+iךL8"u?'3va%""jm/L%Jvf$"4Q.wil`\"""[*^(gp`|$"j@d^?""rm/L M$ɑy7WODDH Ljk.k!;]UUpHs8T}aq"\x"O^^Q <qc/1/?lW)~jE%ho 9b5ӧ-iM=Zk<??0_nqDbHslz?tʯ̀BDDNz$Z'̱5TWVC`0:9\ >Pyzya]\'z$ #"e1lIMÆ_^Je>FQ4h4:VD7'LAA&DDDDΤLa4p5- ?9Fd2r9v~CmDixyy!WoDv A""|2E\MsC`pu:\ xyT*+h˅ DDEF:DDDd'] >0"-= L@|Ϟ:c&|ZKDZOwlCV+|5#f~&Ҭo=L=|@! k5&M0=5zuY6v˴/̍/6_H&xcX[ ?+˗cF࡯0s{ ""Y޾8w q=buSIhX>khD6QoE+~u~7FXP7R(hz=mJqq1{qO PUvJnf.oUՈ?\|qRYQoowe h푇 ZSk㭷FQq yyw"5gWR%D}c"b w/).+-j "/oo*\O*((+-`Az #zQ8gj#E3(/7A1q:DD>+P\*.X_iq:-ޑvWhjh4 &6+P=]XSGP 4$*ws(B &:z]O ŪUo!;#e%ΪۻϿ ;{6DD,kqT?FXk?oI8$Οm]]Z^zC) ^/YDdFPFP3d "bܜ&5u=mڦ|q)L#WmluT==4s`)B:i١{n kI\DFwǭ(>VRٻZK&iPDQ]YlaaPADD\j!6@) hB""""""%IOu%""w¸DDDL~9+w!I#2""r'KDDFDDDDDԆqzE""r'KDDr:aFNjtJ%d29d2BDTRØGDHf^L@h0- 'Ms~{"44"D1^\ 44e4fM]`@UM5IZFvN.Yo//#3; {w8Y 4|lt?"##}&XFfv<ѣ{4 gf"wO@FV6<=#ׯ#+'Q]=K ǼhDuu5$YYܩXoode{hx{{~ 9Үeږi ʋF^!E :ޜ ;A&olIkHE@&V5) C"( {[?QDBD (0zir5B!V @А`%{?xߠiuZlZ7$3** EQDUU51MJJpۋb7`0@ 0F22Kkn2u=G!?~ R]-Gߧ P[1n`0/̯z0$Y'}99텠 ~ o:/ Vc^F,dz=~~/(Q4 !0FcQ2M!dMEE%3b)FLt ˠPȡ,h.5WZ0]QZVl$􎷺_zF"K!CBT6B@d#""{ ߥK(2驲S-]7|] zS,3TAњ72v<"Śzm2br>(**FHpgǧ^|S!+;*y1m)jR[2x{{#449(*.A׈. yZ[|uCB:#/Y9P*bNk0 +aa!L] Bĕ"6UFXh0s@׈ Fnn.^6 bZ?C\C׈.ZN7uo:44QT|^^5ܜ냬\Dvp8>0˖pJu4א4j ggٵմ4sȸ]eW_!G(.=Y]EЗ#{+%jcIŖԔ l1;bw2A٦\PRR \rf"qcc#"wQDD_پކL&/Ξ=CB]ZiA6_`':q>oHDȄ1ܑ0AB4GA> ~#IN|+( we&"jK4䣹Evl1fSLjAz22;G3zyy#"<oEEeekAvEhh»˛DFSi5ĘGDR5&Yp#L h2~~P)5V _<=M7rv)"rsisd\"kHjĤw h_xz{Ai fA0< hn5qb#"@+#dr VAhxJ""DQ1BlIi=a2S e-O/θDDDfkLj{興ɌFŰ2k-Z~HDDNfpD%""r&Ɇ#::L&@49vh4B&k90.71dF#~S0%""r&[c}o8䃈˾ሌKDD<X7G&N4mq֘ԜV&'""2El5KDDlƤ8Po*`BEDDNdZU-ZŚF.IDDe4lȸDDDdkLj=aAQ4=9(;M`\""`KLjƑYLb9(d-%ŸDDDfkLFM1J2ѱ% ""3-iKDDL$k,a U^##版h S#=aDDDDDDe0"""""" 0"""""6# 8S q8"T@QE """"""kZg PxD""""""g7i*o0H'aDDDDDDP.GZ~5:) O`_4kj558y|S ;\RN=#"""""jj?ƽ#G:-3>2.Q#pz$#"""""r^)?VxGfz:7K%Q(cwX?#IENDB`muse-4.0.0/src/doc/pics/midi_routing_matrix.png000066400000000000000000000431571404105311000215400ustar00rootroot00000000000000PNG  IHDR S")sBIT|d pHYs+ IDATxu`SWw:`ˆð1CCý6!C.Mڦm?ڤI4iJwޕs9ݾܼ{h9rDGA)E *V2-3fΞ9@4iP%KF^ AH,a0^|ɋ)Y "' gϜVgAhP+ B考ܽ{R˨ ѡhAA@ jg2g&MZ^<{JA!444۳GqJ"\Qi  RLף  DCדC" B1${AA08F]Oywn^M  $  6Wș;/M rX(A(4`8~6,A!&g̝oooΞ=K*OOAHDԽ{wuFQÇ A4߀ߌꕫ'R/kU[N0P^D[$I$})rCB(̓&MdɒqqMF_|ƍظq*gȑܾ};Mv YvׯKAOȉbўt5nݸfTtYΞ=wuZҨq#]\pvqIӦlْ+Waz= 64jԘ;q/_7#_C#@Hh(͚5g-ZyhGHh(M4-[YgΝekԨi={y?q"21EOo޾e̘ԯ߀޽pUc/:t 6b!oGM0+# uT1ӦIC@@1( M4ٙ&secݹhݺj5-[ɓ(0ڴi3*VӧPz֭[о};˲`<ҿ4ia[z̎SNѲE j*WĂ|:w-LMp}>YqFТys%I$I>9 }\yo]WʛI&Ymc'/^ SLf^&eʔfe@Znc'Oi+..hZ?^A DA|.]vL4\ynatY:wh͛ټe @l6m]v*Q .d+]46n$XcΝ 1lii-V; bΝT*+Wo @XWdI6oBPp07n;v͛;v dvoI$Iy'_=سNqFAmai7Zٳ̑=;'Oci݆6p1LL,YΞ=w`޽Xף{wn\NKNvM{޽{ء#y!E(6ٳ(*___NZ<p{8y,+V`׮]fڵcĈxإjժߞ9uΖ; Ҥb_(+]ygE='3??N``o0A[k͞[MYLc޼|-d  Ud  BL"Wd  B zP /RT*T)7P):E*%'|T(ST} 0`hlDʪL%"RE FMQQ$oyYDQ&(c_*w2| Gy7%>cZUFMe ùWL5lEB=*uS *ToT*EQJq1*Sp2"슼H+XGmk0ЏI{L%]8nߤkWa^7DC2|:E2G$*f8w &qVLb2UsD5,09s!j+#JLic , LogVC|4ɛ5i2tYGV4biEͬ&_F eO  4GAADp4dbۣ  A wAAd`m  ;nf40kD0EeA>1LQ!!0' QVcEAHLQADLkH`ACSİoF[l>~1Aw=$J^ ׀~\ \Kb}׃`?4y7iVTa=>Edɘ:W֬\NhX`>="a~xþMԩ`1w?iKoʔ)ܭٲ{})^$y4i*v<|b?+Tdxe@}defvxxzҩK7r;Y'L8Q#̔7f$?Nj͍]/=}U+{UAA ~>GolwmVZɉ'sMfϙ˝;w_߾x{)w5͛{JO]Pݶ3_!CqcQÆϦ\XXΞ͡Co>TTѦ\|ӧu <$MΝی5ׯ8.,, '''Td6b?2ɉgOиis9wߍ!klfvhՆG2aܹsFM>OlȖ=;͛7fM[Ç1Mb'cӴcvvD^@ ZJ-]JdÔhР6S:{]rg̠Ylܸ޽0_S‘GdjYb1~KnƍL+W0x .{)Nlǎ=jմ3^3ջ77ng̘9lԩêU+ɔ)V~qbJrɕ+'VKΞ1gM|ǝ5}Gkw7@63&=syRNmWT:]p0 G\ olz/~肃9y捲ёjQo9w1+wn<.8cG_ ~tG"Et"ٲ [\x1F? a:cꤟl?#y䎓̜9U&)Rp%`|Uz}iҤO_4ly| %rFSdIzRܛ7oXf-}'@ĝԩSfSݻG͚5pvvqF:ݲٳh֬)4jԈ3g%gϘ&Κ>{ƣ5[mGKr;Kr3" {F>,,+ uZju]+:V\@@E}ɒ%l̚>XB`6?ƬZ)aaa\xu-G/ZE;b!> woɓC;f~_;m۴K6JBÆH*?]ro޼k&NvZ#00q͛d͚+gNr?ѣ 64i0d (`w+VU8v0bpFVݝɓ&ٲfe>WƑ#Gx{ׯ_.mZPҧ˗TgOm;xx󸋔3G($"n..fXwՕ*\woscYPP..yOϸFbDwCH(B*_VSj5nߺi{-EIf:ۄ1L.=ƦT^NN|Y6׮\ji?+Vfޡb*899QR%?_jԬM)P25v]:vʃ-^tBR3nM§AHH?0ҥKS~= \Y׮_KnΝ 8 PACw{n(P'''ׯǃK:5˗ɉ/Wѣvߟjժ25ZMjոzZ䣓?~,f)],3ԩSkAc9!X&O ^dAqjߠAC}h[z6l.l~r dΜ/_8~? 3ɒ%M9V˴i8ٲecȐȑî>5kիⴉƍ̞=5kV@Q|C  |(K  DGQG8 `( `y2  1GAL  |Dd  60yӐ@RA_=:RA"sdQ%  D-=AA9q`k¸qsc~zmM6cQwFͶ[bߟsүo_֭khҴ/0N…i׶ݯ5w8`#L1[6kZ~>+WR@~F n*Yi,ؚ&9 q3mxO?Nk^z3Ņ3J٩cK*v߉#T~ (@հh2d`۷DT*?Ӯwܵƍk(>m[( Ϟ=c-&M${lqѣxpܽcg6RPaF ΝݼN;%Lc#.]JOΩ=5.)gK%ZrR6/N\RCNbGR;jhqsuZߤi3^+=Ǐ7ksRzzk_7o 00n]իW=L]1ϴ/wiێc36EP^ӮM4nĺum^zߏu;M ߌ4iڌUVpDVל;wΪׯ]SX:g>MŊpqv|>}l оCG;0q<{¬~f*֫ǚuЇ5kW.kΈ dhܤ1;wKk+VRBy-\[LXA|y}o)\+VZǒjҥyߖ={v/|rSLXu>Bn:wիhެ .jGƍ3ҤII'Od0 ֝;,?}g ?`br<|HNG̙INΝćjի| rbF2eؿ?wl2Ry>E*fc&Ȟ˗ѦM,Xî@|hѢEquNIΞ쉏N{1.TpGTɒhތK|ș#>>>Y2eˋp1Rzz5KbSpyW BUt߈ot賞WPn]jjըaŋ4ng?\]]jQ.\i&hҨgΜ,MY(Uқ䎎T O>}>,BrGGPR$ON{9[c#zi֤1j5ʔAвEscuQbEc^QׯZMuv7wk1oܨ!%<`/7lh_n\SL vϜ=KF Q;9Q\Y2̮O2fXWJ{ْTY|rI[Nl1{R eʔY(aZu2c5do>Y2f_s꣗K*vwbmDA=-;wf-j&k4nl4]pg,I IDATgʘ WW.^ NO&(0[6O<ٳ` +VApmX1y6-[0.֭ ֱw?|ԩy aa@̙ɔ)aalٲ ?~2pqA;vҥKq&C}TTLJb?عs?ʑٲ1irE٢鏰o/Kg%ٲeZG1h3rE?&M7om=}lF?X(3y8&ӧ>b-~'ߘ=ey\ 0pfZdr+T`]TPfvZժU{(V(:gt gg5qSǎL1?q#Δ3c,uAy4p@5k  hSю:0gxrI;q䒊o@TXgK |HlMϿ_Qzy  `P@k%AA18AAy  {AQӓ>{Qp!m۴Q#)^XW1n_plIv^fڵ{_Dyi׶59rs;i$3jgYh7oˋ>z'Oz+g 88?o ayqfϝǺ5ԦNUX¤Hy%;đK*v|MLJcM{CĂsh׶ k׭]+1vIɍkV[.vӦ SƌL'V.9fRb?`ӏǏZpb-…ԭ]K~Ko|l?AIc$y5޻qW-mJ©L*qg,'UBQ0KLurI[NT1YzߑcG\../_'7oӾXד>}:N:e?IhhE2b(z˙sg,{2UJ9$͕*U*ӭkatރc'NеGO ½/_⇟~K|?a_iܨK~[+Jzsa3TɒR}sgJ Yn]: uV![,^0͛jL4ؙ˔x(Aɩ\"Ϟ?AzܽǸIVӱc.ԫk_fMYKn޼eS'iPj2K3y9O/TnV;/',TG.6>9Vej5_TZDzuٱsW4ǂP7m{FSYñV7w7PAŊXO>ѣ̚=l㡻 ..5 nunn_]N]Xv}2ݻ-[Vkǟ&ݷcb+6uptȻ󆥣3<sҥKϏE :Mj{@:c@pp0iҦ&6#)`E|},rI;q䒊\ҥMKpP1m (;VIpBfя]\\jAoZm=yOOڵ#x h5<h@Mww39*]V_ftڅŊǰw _V&f,Ӯ|?I2%^;Sṣ^̟;>ooK x;iؠC4ڥ=+11tC b޼<4f32>d 2_rUcQUk\$&咊~[Xz=-[#ǎeǮ]*X0֯^֯k8w; %;o;q6]zPuBu3o>H9?;ufʨ} eʒ/?e˔5kom6̙;;vѧWO'ONxծwYoFB%ۻ,3Ͱƺ6Z1wv~5S!(0ň˱'P-[f2 OOOVYˀ*Zh%K(Z*k<Ϸ8y:uCؙЮ'Oٙ^={*F-aL:Ǝ1zȒ9 _ Lԩ4`]:۵i k2d@=,Ux BBB>Wƌ~:u86!n'Ռy7X%;đK*v|Rd >z^_K}.YCQP;q;vbq ` X888>c&Nd $z+W|*ڶnAރȧOb>ֳ7m݊ ~СsuW'AK(= D.=.-= ? Kg9/K  D'Vp҃bL  |(&䎂  10:p4L~3) g| KAsFAAAΟAA;b\~0yʹlQA|> AA0GGcAA,= `bݛlo>V2E Ǩ~?MbңWz,{ 7m+ɉ`#cL]:ulV:88w>GGG3o׹Ș!?E >eʕ#c7O<ߗSp!ΚA_lJW'SSXc:I*&*.&ef:B1%;o;6|'3dM;wҾM.=zz=iӦ3gڜp w,=zߏAC8w{uk3%Tpqqb tlE0}8}S@^a_k~</^(J_ן ?OWtыMZG[6ɓ\2<} E`CFK( 7NGxx8sg1Xd1-5*+(E޶VP;svl݄^FtSX!`ݿԉ?ѥcV]ˣǏ:g:w-[+Om:?ߧAL\yիVZv];w"<{FXXu7Qb 'N",,:8q]r EΜyQEKl3%cdȐ0=;wݝϞ=[6dɞ-;Y2g6oɝ.ŊeǮrӌ7̞ٳSPAwy`3Fq> 'dEcK*vwbW_2eJu]{6mL2@Yȇ~~ڕ#nGѵ 7?F|#.AeΔߌ͍!6lތ>K4t8rwVm:p 3f&0(93Ǩ?yoE᏿˦L OmH޽PT4k҄˖Sx1jU9  i&溼ؙҶuKM3:wFR ,?zu0}毌1()E#F:U*.[`N//wlѮŊʜy 1tHMlωC~w "TG.U*Ǐ4 + Sf# ǁ2dqՃ ؃>zKW8i Ȉcy^ro^bԷ2bXBCCچh8s,fwߛ鷤@FÌ_лW|n߹cѮ0-vXoʭwhټvB0\Od,uqX䒊_YB8|:'OQ \kTƖm(l޺իYc͔.Yi'r8ad*S6K ]fD}Ʋ,TG.6y(67*thߖ[߸mZ>c OA3xxx)SF2oޤFjjVwAݺ8;)ͫWoaaaTZ)pԪZf/z]}ܸyիLZ5no[?:* }x8AV]8Z/_Z7nlƼ(Kj55W[~0bEHrxcbIDATw_NeEQ"㢦Z/uMqNZ(Uқ̞/^0qT*U,O_ZWH|/',TG.!Vxmw">SXj5-7Br<7VbԈԭS (W,Abm+nnnVnۻN!YkcVkFc \#ҤNǰ hU֞s ƌaޟ{D{WW-^z7t!!4iҐ5Ks%… $uuq!((ȦN[%~<.4Xm#  ; ظ~&*Tɉ qm2͍7vr`wqv&PݻwV%fED|hj"hQC;^o6ZMP`ćׯĮ`O.\DJOOҧKgVn j8;[]؇5k2k4̝mVWnmXIFn??թmV5S.nn}֪|hE+#ğ8o-_$\RCNbLJ[1(md؉r)2f`J:w uԲ6g8t`CG}' o?:]P`Ac]ʔs<صdQ;rÇ o 𑣔3YvmQ ɐ!aazvڍ;O= {ldɒs=[vdl`Çɓ;]:+ʎ] ߧ;n=9gpt҅s. Q4sQX䒊X~n"D,=m>KG.|%׮%ctɬ˔.;(Stjќ|G]Iݝ-ܨ_cQނ B0[zȜ. !:YzAJEN<|H]/AG!bn,$$$MA{BBBHF.Ķ   A:aaa$sJ( .$IֺM[.$Զ   ZmJ_H  |ڼ~A1El-6*8%On.AA>mtJ2f-[d`g"M,onA$UJOzm67?[G=?PIENDB`muse-4.0.0/src/doc/pics/mixer.png000066400000000000000000001567541404105311000166170ustar00rootroot00000000000000PNG  IHDRbݳIDATx`g!PA`{A^ *"X U tB{folۻ۝yfIE[{ $  &aJ㻺v%njǴ:}t"[ k= ܬg]U#lc| _)FΰomU@ڀ QjYu4N.h[)_Fv5űVMF\YͭRSU1N<{븨: K;0F$hp'8h b'NB H:xPơ˓&(ݙaC_TgHM;:@cc8ps% vj7;E(IL 3Ӆ9i\oq>,9GGL:}<雞>3g.uYYzaDU•FKw=4ܫ"t)" \'~"7q No2%?Fs7-p igb-zJ2׀iVʙŃ .#[Htg ρnm0f!@y|f+5 ^ Pi'Nѝu9s^ߔ߻iJ|>|hBzFl6*!F4bKt%uV)DtKSEKokG]Yb\^l1o6+*ZA]TTiR@)B:b=otte o<Ҟn !SҸt}IdL+fKR>ӇANeڛ\PoàI2&t2"0\F&b<`Om8yByS0Y(kU',QPNx(灘3b</YYn~-7nŰǝx~rp*Y{Y, >X<瀟(9l F8yl(9T!sgNq«AH5RYQ!ɨ&IRA^ߢg;lߡ-8嫟K#qEeŵ )|UjMX"1"&T&W*UPSqD8Хo *" WϽP$!5#hrp008:FcpesV讓v)b {4HsW5ǫ~6"ҡfݼ?p<!{u;ٽ]]:$5` $#t9JWby@;5ޢ:q>fSwtmqTz; URkk{͌$/Oc_{߬ L8o7{;BȉWU).m-ܽdw{]w9곿a+O ^94J Eܑ ;=ژ<1 g/`_ 3!lx/o9c挏5[,͙SYQZr_# n۶믿aŊ6+VzpxAGjTaR5tݸa1lcŪ յx{ۦ=wݶXMUE5X DKO>t^^0D}&v%yn5]J& 9 ҕ rT ,@ũILai`9YN1s C_, U"xܫHwdܪ'Ҧ2Qt}cǃ|5{iǮL@2EЩŸu=!kN ~'R1&y(Ex1<^ЙV,f?\p(!J’m!sTRoPͧQkHWUE~=II\UQ e6uz ccb]54" %T#Zhqfɍ70eʌ~ᅩ"B8G}xl[wg sÿI"ØGg#TT* s5lv_v&&k|Mx&[x*AQˬws?4Sr }tS{NƠ6=4nȩo3VٔP'K5vE5k?~bO?K$k֌7nүQ~#Ks;::%%yD͚szNQgLd|[c$u:ݩg][SåP(O: 9Mx2ji {:PM5UxhΗGa1j{${J99͞ІwGa`3U(򞩝?#DTs_w'>Aܺ ;uoV[V;#u#gA5sWljbO#;SL:\ yZ$h7Gk3I\gN^$y`"N?}to' /?үΟάvjbiA+SȽE5" H_|ښʒB}Hڟ?a8؛<}dښK47f ̙glM4CR:E?[XR9v} {piuUUP@p( vt#6m`)Egh 9^uܹ2;9I@wfiP(R?CF|Ow>KL%j6Z/n"MF j# +ւ?zcw6Z9~ou]p_^>ɾ3`Ԥ=/3[n~fh#]#Shex>3I(2j/yryΗuwfrf[hٓ\IK8=Tšz}JyvNxLrRա!!N7FuQYQZT `L߷B~#m{LI,k}hZ4[(**l^paǎ]p8Iٹ]st xrlGp\%l}xʳ Byn`x rR!HzN{ޅԆ!) yd#I> 84j;Fؑl5g,e3Vjtj1ԚZCl._*?8x=ݙ[:7}E4A8R|Hs߭[Su+4mZzTd49OFl =;ǿG {Pbֽ=o1L-4jMn^NA~YfE*hDdž%Q +SCyWloA~Nu S!g< Cz&!=R;s`PϦC3M!TX<;{nU< At:u͉v/t' ܜfY*CxE;U Bz`}跎i)We\8K$a:e| LQ(j w]Q Tca4J2zG0t.aLp>=#Od.( *5yagV4i{vpl .&(63e.që%/r9 /@x5}5Bm&!\vܗP' [w)E)/1ϕoy <4 %=Zwj3 Z'S|gvi#4<3TqӜ.J!0yyo* &, rrqP'QP3wu:gq@\nTk2.am &>6܆ @ܛP`!ZXMEz@ҨP.g#n?~&#+u)Qn5jFe4  * "=%2"XNB}={aTk<ȧUHB0ZnsWǻsk :S?BT'4g% <@TOe-*l[j|nmbUms7yx=ˡA-􀃾(la=ra{ A=&G|OAd)^31:G)RZRF(w7H;^t>΋D[=*cgVrd d9*NGZPk0 I6Ϲ/R9>-)dx 0kl[tI&Ly~L;-+=Ƞ ÂafO'Aagwt "F<_pBطxY465@PG' o&z;`#B_{jʲ@=qso~ bpVѨrpZN5$?O*k3텣&5 Nt9>B]@S|EXWo#6lG爐SA>凯0i'4\@/o]{l;:Q0Yv&Sk?də'8qlrv)D߾Y}[yd! U{_ƬBUC|B IOox+rOO|R\wYL遃ExO'x,g W u4FfprӛL^[+uSXg2 ;VS[ n{*xpTΕ |g5 x^\Bm"59~HXʲSc}wꮞ"߭?dEi/=N*ӕs7JbK}_jo GiˆG>꯼cb`3;~L $kQQjߴoOB_tP18~0ה_rߚt[/3T6? Y ] j. qVkD)[isVX y[wPO5gqQދۧ^#ȩUIQ|sMDUΑo4{.ȡ+roGA=0s"ޭBC1`8AWbUctySא-ޙ:!UT)o}='ݽ_.3%Ma~}CiK:ߺpә!N[}BK2K St+93 _އ {&y%=`q 5f9ɢ>rk!^  J:4H(|z>f7}0;3. omL?6ëTqtګ{2RVg G)QJ=ˁpʅY51sZdGNae:d7O0 *4)c\,͏` ٝ CNGwagO}3{*Q>8,ig(Z~@GI.xyv Vиaϫ'BAN p)$@%5 ѽ)ˡоM_ 9e= fOLle'<^ylem8 ZySկ81i=}>=LR>kҀU 8Onߩ˜w)կؾv5OZtjEm۷^ ?K|Z7;uYK{t}|g%pQQ>1Xx`?4 I fz>-Yx(:^<sDϕ̥s>P7ϯL?J1e֧Qo=?t3S5R u]ʩDYƦO<o*P0o|ޯ 41 3UmjYʷ[}M"(s?5ih+kSbytV)}=4B|Z9t\xF}h /G ;^QG4f@*0ijsZл>DTDS@R[ވhYj]x$~̻o긏k00 ݟ9OI{?1sTOL;3F֯<,k75gc~C*C>sm"$Q=-ߓ%J 2Wu#Ok/ o@vx誔l +2ַqt2,#@!^]yaC]XN-O\BgNN=Y,[,/W-f;bAt޲duO6vB?hw/8$?p=||y#%sY<;wlt]4H=a>ul 9@',}Z)ߙ)3ބ>ޜOᄆ/,ZtqgNz;HC| <۔w<ԧSr(Y{xp >~B^a}!-zS*؈pZԇӕ7xW6wF{h{]{@St #)#V_{O3Azf+8~ It_gyğɸyp©<~TAL8<ڠ0EؤQ#s 0$3Ѭ˨r>NmK~ӈqG޻aOF RR^eq꿺6"DC}Nn4_`_A R ʈ֫mwHu=Ԕ{ah@ -odp"7 EJ'Ϯ) j{+t/ Ind'n7Eȍfko$1Cwb{!u$;_* \@  x]!1l/DbUWh*8Xa,?=@D*PIqz Rdr**3l+*OԨ:'SgZT~oldHenI/n껦5Gf/5&DcOp^&b^yLHԱYnl:ǘ.ã,xEu(GRǴV$✢b{S3M,<^$DOɫ^k~6n5@ArFMyN"gWJD9׭kޭ{3ѳ^ߚU=P(21٪-4]i)]jL;+㇄*ÙI89n;gj_ ebya!uj٢)3=~4!8"S-~l3UZ5,yhHiҰo{Yu`?j[k 4#bC ϛiiSN2HYyվRh+&<"f3S3 [W?to#}P?Kv~*T)+o͗\y]qA5$š smwyRZF I A~j7.=j8)Hb;AG$Ú=: U߯9IF*ÈQUuKfy,0R}w~{Nػ[HC]mYJԈ"2z3-d}-8PÆEvl%5VgDМm5 ~V\U4SE-H%֗X55SEg>/*̲5 ķn/ g!6Qc1W+ *ra'G$X >{nQ^ЌdBrVPÅA"Z)9Hra P&@._-CČEsɺW9c_^>O\7>1T4X hI[,_}z)Ҋ׊kŬpJ".l}N*e]8۾t;gcc6^Vw\.`ξ}H1^?{ʟj6Uf饺dBN,j<#C/v=ϗ%]{s-sF 9n4ʯ\{ν,xBe9)xaO%az諥W|0*[6R[ӏ1б_ɪ օ$\xﮆNxg>>156:䚹GpKKX$4T Wo6S \ bښ%v]aB E賿2+R@9ipP+jV;I4Zr1uR,Cal1!qxl#֔~"gkɲg:=fXn%}I.XR8Tlm~y0e-+*ʕ&͎*shfE.?6C܃1~$EZ-yyYmML ׁ,5P%Vj)nزKE{twJ$wH/QZ;; 3梅'+} 9 9V,sra!}D%zᆈvY;~1Ήoh qx}ϯ9u~Hͧ/}ꔒ;*G^\y֜ikޗ;~ϪR}to*gAB .~{*m2ٶU5]썭`Tʉ,cr&%* L6y%^quզSW2l'1Y{s~`ҭ9U@gsLWev-Zogj; {۩C'o'CرME N׸/sslIҀ)UX^6V#j*N/J^wL!fev: 18-&^%xYŧ:X?lA$xQq‹ʟ&$|.w;&u2ԪdoXpTXXE[6S/䩩JUmwq1;{/@|y,߬owj%lU퓒=1dݵŏb*P.Cdž]9/OOvbw1H/Y08lpC(~~v9_[P)ark[p\mۖU)K֏<RIvTe__x{rHЫ%"8:L}04:dUNB?sJjRV|)Q{qNǾ`|}ѣظ3U .?2VUX_4 D"&7<^ȃ~ݥ-UTDLMVD*./lVƽDI2N[驩h[nՕS"+ɻ=uMF{sV8H`![>"*3؞E&]4"XJfn _$ 98nֹhrXF[ nA D[W[QMhvτ7;kEe'gZY8#Ҫ}U2~eb{oޓÞZw86.ωڋ2CӃ)m68] 2Xt8*R-yEfL$N%ADoφ_BPJj=C 0MM=RjOݢܧA>Ϣxȋ#Bد(&T RpCՕ6#ՄW"-nHכ)mwWiה_@8@__uw-x 9;oO򶖕7[O5'5I#;+w_ >sg#tr_m%vuW7FڅKæHd&*f޾ !ܐb/@{c=_SP *J:q!i{@kC:|Ʌݸvn_>rTށH1T-j TF:I!Fs]fGiU njjC*ݖs |m^r}gH}uTvaUd#B{JzxA n:UGvaz]^XKRP 2\QvHoྼwԸmzLzI,Vh2Ԋu98[uK?@$rh}9֚jN`=J <æ(Gp 7hV ^!.XlRyy#,Fۏ|W/۰h1WhepJxXHPۖu\IqAڼ"B1adlq_$L| *Tf"="kGώ$ET"CKsUp"ԩgJ"'H{1JERuŸV  Bqen8HO۰z~tk^vJmqtap'-TZVhN.xE3FVtsLZ.65[,s-jm eζOA ſ /b{<<9҆ğeh{xvg.s-m(+FLo6S_DO^XkNN}Ɠ#u Dcn/ R|Y)]d! g/G`:_?tm""ɇ{/E{_x=]?? Fnj53PF(Q pV>q:ʨ>f#}e߂JUkʿ~{)=ⴌ,ՌՔ^.(=SV?qrpȫ-/"Nȱ yyn9b8Jpy! ϹDbx U8U!xթeV#KW-P W朇 *̵8MO(|2ƄKWkԪ|Kˉ"">YzkOG4liPWZyuv߶J/2`x]{qi3DM 1%;YjTU\pI=xW|!Zkd,RO2Y>)Z\SJ>I Jk% .ZOm-D^XR0ʳ_3L\&⦴;zFhdb/޸+&6$5,Qu:qM$ZsSƷ q]};RU!G('H9p'CFeXm%‹r|!scWO-|G AȯV^h4Z$^^qiK#ªSd3WA+ 0?^ VXbMFI.ѭ}s}G`p xV!uY~} ]Bse!T,׉ oXX ?l.rS톥k.Ao-x$<>,sP3mx[sYw蚤0iq DE"nbx@22b01PfnΚQϏjcRZyb<;"OEco*#]B# BSn1FZ 6@4<i3 t˕ $\˅ija9SF匈0&X$q=d^nd;_vExOH}7N'ACSpjk3ڣE7j>)x"8Yp D䘵,xIG|^ECod^O]re }&wZ0q$6!`wa*o+%u#zx1Kl.0S+Bh ΕNoND}y5N\,}ihl}M҂0bxNqSÈFO7bz1YT=$Һ`_QU։dU]ƴo9EcϦ'T.K obv=٭\W~9]҉kVQ~XI?Ym/&jc*\ۙغ{  r2?ظGsw E#F[JꆲQSzwKkǭ 2yfE3+zte>y~sxa>b'. b%42(e}[fߞ;Iboj9{%"GkBSrbZV n;ƒ_iւJdv`ٿ[\ɷ7kN~+P/k}9rG++k;YEqQ]WNN p 06=5vWiX1(+..\\fu+Ma翇Q$8̘w!mJ~PxZ^;@?01 nɛ1$yK;]8hVf21+j.c)Qi飭=ZI &6d"Sa΍+p*S{A_~E=FԙW(,dij}6Vh,%; aaVъhV{SFF@nXm c O~_tXpLO>CR /ުgRxxl2Meg&hC%/e'QO 6Ce<"*)w\5%$?T⯏~|Y<:sr^DWC/^XxJmf]3XFuEƓ.o+_ۋĉO|zFzCf#f!Fv1M+UKߛ=.&]z=|Gb%BTk@ii~~z:m'k>a+7TZJ.u-tb@KY+3P6j(?o;"Zk&hg6 Q)歄g"'zpQI$򊱘.WT|z9@ix %cG 6k!af>òAl"c.2 #Y>U s-8sIS_Ji1'JLh+fjZ lf xd{fhZqGw't+;fO HLYo/0uם}l؏ +oS8!& YdWsF\ŰNUO-fAvن7"2GF:զ(*ɦ *k᱑߷=Vcp} ;D5ƒ62ƪd?#dBF`2m=7n¹b߀M)\^ج,#U;4nDFl!Lx=uaVׅ_GG6SJGFqÐ~}ht4f,T|msqaW'dm.IL&F;^} Nb#_2[c?-ʩwnӘYAu˦¿^mNAqd;Q@[ϽFM1ֽ=O38;lL3np4ly7GB*Fى? ,fSt{$oǽL~𢉅 2~d|;Z-F5V#$i[^FlձCs!vm|jW6Zmwm4sݚٵHQ,&[=h R^&0TIYpX7hf_H%բm5hŖab97hN>?:삋'6WSy}BD\h qE1׆/|thIm'ॺPsLOGMmxє W/_ALDa"t,M+xV;_?Wz`-B6u3M"_pFPDИ!{~'+Q/R^ciS-|o8a=qoNg+|~(+Q7EO6:=֌ t{67<F(w>lIaJQra6nX&B&x|7ݻhvGQgļ Y욶9 HSX,CuJ;TTz O^$ J0KJJϝ=[[Sc28ÍfK@T7M&Aڰ.)b>\i&BFSUEy~vJ RX&'N^x[_xѐ^x?_~YMm"Wv1Z^4x-x9 *9Fq GF#YWxԯUA:\\^"E/HctU,cD~fd1g]>#sSxQQ* Il\[nCCrvϻԝeg9 JTL,^UYA&%]H&X+W(6h,U[UiSjuF1<4U/FxAMv"6.>?$%<\uE[ X"$wߡ+"&."*Fd2nCRA!Q%E%E0j1ז˝^4Fxd R}g$䝤lj4 f_HD4R,Yo'S^4x)Җw 6mϴU.:ekV6bʎ!*UiJƋ$Q E4ZE>EF&8 /ҷkK FT1^FxtryAuõSsvlFu9mRBX/"0*!m} \1'rG2#G:~\ DINϐ+0]DPW BK8.jY0p*bʾ Hŭ/^h(!{j,طt6i~K0m1]~yߍ7Z]ثSkօOrAxB*%*b1T{b\l" T* )͂I" y-^FxayoE5wh$ψ"W}OhwܳӺp͗{[XN_^u[YuxgԜ1{5/ (SGމpSo,X8 T7ۻ܍S5BH$RI@X &.<"#jZ윜ؘŭ/^h0,{sK]WF?7;bv"0kS^lgNYOu¬=&OH1]5ˏ]Vu.7 / Hj)~%Z&c 1B!Ux$C/"̽DUKꖶK(L`bHӺO_?бZI/ /C eqi`obxƂdSoYc\$"JJK}^4FxaEлV^VdI}O Ɔ Ijz~AP%5-Ut8K:vxʝRS/C0V]UInC*8hbjϙvUJPWKTFnH%&yqk/LpOѓ SWt%>R"iƋu0^N؀uuXhfaJ 1f篠ߏ;bm%am9^ ܏?ŗJ{ǜ©y]]] /^4AhXv#6+֝ pbOs^eW/thr𢢢̙3izP^VvԩviZc\&ITPJOXWTWh!$JLL(Jbm\ g69~E`P*m xg^"E3-] ov xrڵNۨzH$W.^wMF:(nTUmB 'ޑV[Y Qy9R/Jg2***B/^x^r R^^ϘP#zϿ?ӧhZs0lzi̋l=]DٖUV ^HZ6AZ}Օ!QdUUu,]RP3*Jj1zPKVQ\VŸ fht5=|ODa~-\EYIVw{T gv՞-/A6Tc<&/i^wLp`gĦK<  %ā$If4Wk{e{_?WiW>kfFD3J` 1sZ>_ Y{ZF~l*ې_51mm@!e N/8E5(3ٕ>HykABLzbïܺ?T,\sc7,5uןt톥퓞Yae.ZTж{IaӿsKpKNJ{8 ^Zysy9_t4)߃jGҬ=HyM喍m8o٣k3QdE6OMT:)>rE\DfXXɛlp4rT8(&t:l 'viKr0t:<)RWaё.WL=VrƜ=-W7oq6}]R~dR D0j/.o_X\R)0YԡHO뢷6{~>N=ƾoZDvP0#?gDF%ʡojYqW|NLDHI7` V˵C'{ _`@Q{M =IJ'_1E=TB  &ƚo9kO-z5‚ ՚>zP@|ǥ-T13b$tڋ(-Y(&RXuu*{/ol!UG-[گYzst1=|onUj| ҡHbZgGG{9:>1ATVsU^a@)=M9 ׾]W:n2m&Ú7,L6vk 6pB_w>>h_zU({_dt(,^p QPF‘pmc Y9PJ?1oN&*cXSuL$v 'Q#F  jKjV81\ ~/v9f bs t(,t&FGF|^Tj:d4[IE.#PTQ/(*(3=^n=8ęݷDK82lP4AxūOt֡?͵_Q+Z^ܕFҡLf=1s|B zyݮ>h;HJAʚa >_P%. X.V׳Wv^p,lVFx6eѹǮIAa|oyqaG9EZ?8m~oZ=z&xK[No}=>h-Jh ;PW}(@J̬L&?EdߟرBޘi5"K#~O=)șg_s@sb (j4Eaϥ J9Їvut)@Q/ɐTx݃ʜB.A116fLzBR4x/}w~o u]l%OϻniSqn',Vۛo{ީ3m>QHRg ̑O(4A?SzGT~h` (O!ZkQpݔ%B*yP"-;YV8_[W?p//d4>kF\rO$&! *X9C( C+ &KBmKE#@!} yPHGv  }ܶքB "2|,FT} Dj[TMQ'PLM hW&E7$PviKr0t:<)j!V;:"`JN>ڋ(br;rGN>esj **%ɫ _E5u(j4o٥ɉu6{~>6PBNnED4aGrũxr )bz}:^2P$=Q><(jDE9~iIR=b( PC(j!?K0okP')Fnr]w\:6{¸1xq,@!M yPC(Li +_dO3.kҡbXj;9 s>_|+/ZٮnyeT_~䓏~jҔkbbׇ;7øP hu\P JT>c^Q;Vˬ AũD#aPH- PHd EfE~}:v~3Gz.\dOl8뛗˛.[ÿuՑxrVw^y5~wE;;۫[tCŁp=:JS_A{?#@o#h^f9CrhhE+J$ۋDYB)n4(Jv'VPx,j`dµoDnŕxI}pfMo3#aȦnc f2ϲ2[`KO*BV<,PHnT$aгמo"cḪ}}YCFu}qٚ[zG~hxx+h8{c85ͼü#w yB&_PTNQx(PTelX}J?ݎ$> F  jKjV81\ ~/v9f'B :No0ػzϟ_߫ƼEd0g<%yQc569|nmk[2FRMt z=<ϨɈikm )Sp~@!N7YWw{["ڎ%gEf g #?6.^}ϰ?hzZzP6bERWn]{*fh\vIϬ03ZTж{IaӿsKpKNJ{8 ^Z9sxS/("3Q\t=+/~ejb>femwoSX{ylE4/ :Îr )@!B=|K }7=pƖ[6~eD|EIJg4`0קpZZZ.b( +x+yF#Qk9{IA! WbgeJ?vІ7&EZ H=^EGv{^13*Z31)īnz׾VPDaǒ,a^D] wJ S` (2*y!>uJ}5݅S3Ͼ PPϧ٘-esoۇѯ48(4!HɌjYqW|NLʏn82?DLk/‘O=k@1)(0YE5)vOvh,?o9kO-z5‚ 2 ՚6l2zP@Rs<.ΠhIA#P$އڋ(.>&_p H?>sdA-=0PS(Vڷֶvв=}䍻N.-TFEQ)ݖ.Ōڂ% <ܺd4=?0{,QP`PHd N@QOɆ~ɫ$ͯ(꠲Z 1]Хi*upw*@,W no`0qe{1S)R~ ()E+YmT!NHyP?fP5}d2ChDM*%$I{`(脛s̟?HL Ք_`@Q{M5 ^胁 zUK,Ĥ EgG{{[ĨszG W=GMX3o~[kkkK^@QV A1UmNOLWnIԃ@QwBkfa4J)èM7{@Qj@!K6ertZ"E.>MsSx\.>FNg2';E݄‰a~`ȈV9#JcFlU(ДDEY/֚BlTujKcjm@#%Na|(l2ks[{b7>]C}:bwJcd|*##yPZS"|H^_1E=$%hvn?ZYxYK8ѩ7wn}e3tF I* PS}(@QMzeU:x>M\PH"-:? ?O$(g(?lz)Hbk2jm4a2 ͊zlD"V /w (C! KjW뻎m^EZ}ZGY}'ǚX|_7|~kdl_<{7~k7j/mS%;[ ҥ'EJh5Ɯ1'EvPL yPX[O?c<̳߰;w~pMӺG6>4-`˯aXϜsIΧ[yvetpc/kϾئt(Ҋú%{u0~,{=ȶi7/=fUo0"QM^@8~bjٲ8v$FrYZ9FP(Rf (&<(B\1Όch" <6E(J{Ks"0*w)@b3LNUձۧt:HUxz#jl|πoǥm5EgFM;3)zGE>(C!aZftō_i"˴@ϾKUt ,6[o{ީ/C1T3h&PnbkB b>` #l 7i}un1S5I:GGB q% 4SO?vІ7&EZZHb4ɜ؛| Q@mAnG 8hRVmR/"-CEQ@x} )w?@kiu:f*y/Jx<Hbx R_E4U(X $I-$i= A)v(4[&1O?(Z\~Ay&vW$hu\P JT>c^Q;VˌTJ~(b BZ(L!hP|77ϞMʏ?T<޹sg̜hn.vPMe)8j1={t* e*O>9p(7nfAA43GZx_ rUWd{(5+0?P;m{vt)@Q7M Ne`Xt⮙_\˞hR5 Ra5iubE}ćB]Ƣ D={?X/ 2"#]]{?{lby\fn3 >CCm]Fé_o 89sB 'vn"ZDbʿ`(w ?Wy7`4ZhD ƀϋ1pk[ۚ5X,l*ې_o&#:% )S` ($Pdd`JRr`E6Kl[^ky;ٻf;ڽ3ݰ4}3+%̦mF#a݁ v9) q^|.`;x2%\0{9k}szfVB]@!MR\2GҬ=HyM喍m8o٣k3QdE6OQBM' ''\ .X" pXɛlp4r2 R@ q]+Ws$# ɳD+>K\ʂEU foNё.WL=^8TPFīnz׾V=YH7]è" lJUtsE (R(r>3ϥl7JΙzRS4U53gVzH߁pHYtRꚥְwSe a(:(\dQ3ڧl1m~"J!0}ޜ= dF6e _M8i*f01O$BJc2^ G>ٳo֬3:5PHB~@!._cdzw]3MF}sEz?۔T@ TlUd];?@Q&SD(̝|wP hׇsՇEY i4~B([NeS .^a_:d4zP@R"gZgPkR~Π*jP( 'j/r̢E%N ҡ_E2)0PC TE#==[[[ZmY uZ{E?5YUeb-wdzȅ(Ǣc\1}f׮gٰzQ`( $٧zeu{cw\`;,ڋ(].;[ 9L(L*%0KyuɊi9zQD)݃ҡ_E2) U"?אg*HY/, o!#TAr NsF" 4\"2gj]㢭-v{.RV yw.FM㞨DjTL1fQ*CCh\EJE xq˖&{zBZ PUB@gnN?Z cO95+/'' 2Qq* rVeww-㑗 tLr3w2g 'pJ'衇3^qe΅̲w&{Y`jxu }wЈspx8IR$$``(}A7wJ,33PHB~@!EvQzi9{r8=/BNnf\T󯖎<~UOO'+((@9{P첵%ΖRkY'wY_BmY=(?A=(QYr~OS0=ZtJF (#;0XO3S63B;VU*#}Sh.ҍ3{vE)>7\E ҖY.RAzOOYuz果4IBӾ?Xq<=Tct̯c>|+nٻ\gNpIWRnqK.VOIUO ٔEJ.=uL)iAU)x~`L; UJHnxF~PAn/W?M> Ze'J5YLCC@x/VqV>^'QCN*gw?̯ϹW.ߎHϣ(o~omy)8W}(0YEa*̷SBM(4:|,ERR_PBf/ӂרձhTd @=\Փg={g oz{Gʦ>?-K-mc A_-D1Ne&8!PH"m19>}??;y}[_<ۖbotj<嶋n~mѭv<̹^cLϡir!޲>d&P lf"9^QG.\hiSt(*4MV?$Hl`_lO7HrPET"bbda EBNd#͹@!A*T2PNe/ һK(MUEvs;4s/{c~xI8G,)fh2xTRzz1RDP&)gPhN a8>2;=sNhiU'vv6 "GDtx_ _~&Y/f'r!brطw՚54X W\u(cHd`n([Y(gā_SNr!!+m t< KDxw,tϚǻ`rcf)7 ]vŕZ67`\s!f3k2r6w9GRrE!?0Ѫ;myeVE;v_T Qd,6SޢF F#mG+t $m6بzbã##ík.\Q -'_{Gݻ=nwkKsyFֶfejh&ȒƟ( B]MT)F[ZzGF#-Z.G|CuiM2{֬z*Iq%k@MR/*EERrźWcшu:O=<y]Vs$aN7N07>-ont:/ZXo"-, (-hJGn~!-EVĭ76ykc> K7>zfN2vT扐;"b?ON>o^T(wq r Ջh4{_>'>sڴbU? u-z(HRSSpJ!%%h8Brh`_߱B/Z@p P31sj-lbŊȔJ9TDzG himu\@呝) 5X&b9F#(\8κQ<#<)-[O_I͠]u @p `e'2V(OQ(Xz}Q٬Vħ}9mdlEQ^9r[( :m,{pςR1}K/sJg;v EY}C L>SjOj>֋+y_T)aA@!e2^8>tǫbVQe┒h$ ŎG,O޽WN Y@RYZRP [$9{޾!!PH"ɩY cHNv̮vTj;9oޝ;wzٳaF(y A)E=Q:d +b؄PԌeKl`P PxnM5K.nɱ/w>!PHUH/98PFJRɈd(#;|X(hoL Uk>;@^_N!7ش7 Z-Ix*%@JKL8.:eg)[RTTV\ _^TEՙ(HwrC@!U &(zzK&tv8DƘ?wnۼ|C+hklӵW~#;EPWzevBA DEYE ggBUaY@$_ܹJ~1d*hW(Lbܞ).Rm2R|:~3aeo~+4!0EGba(u.(*/dG}_>b;5/~T[Ci+UP4"+-6dG`80m΀klcmS$f1B1EB\ r (J"S_dI}ǀB,2^ sR1Irp Y'K(Hba(S,@ Aʌ PH ;qT XJe!)X@Q/UBjٕ l Z"Po㍤_YԮLXZة2EnZ>!PH";OV#D@0 [FbbSS,HA2PTlS/,NJ٨2y}uьad ۣ*Sݦ#܊YLUSL]FF ( 託B>т٫L#BBIIRS, 6JGҦ_T|%U8@Q%ś۷L,E_,Uҥ5][d="QDߩ [W}B(aS)dVt>B;ң_䦭"W]:@!ľZgKuы)UQ}BIIRS,@_ "$ RL(f%$5W{Xe7Jf6i %%IQL $}.jTb$xo,Baua>R;_䥥i'Sйi(Cr=]06kǢaS,@ AȔZ B`pa.UٓHp0v /I$Nr9֢4/"wЋ#a&EYYLPL(+Av @QQLe(h\eE)rfnłM0}ٿ^i+N(0bj4ʢve?\ '_yKz]bIP%W(TYTSR~h;Y8Q$+BBIIRS,@_T\T (Ģ 圤J *-z) 2VP ` Eʔe,_hE)flEd{ HrDWa*zju7cr6Hl'[0z)RKHU@QL9"K;S_~%&QQԌ{rvXxkO_"bSS,@ AJb3oh4f`0pW}> 5ދ.h1%T=) 5/(& "gg>d}0p|`I83y DD7@ 0;r\2PTnGJ H_Q#$))U\EzdO~QETXuZ^ (xY@~; |>% DL6H Eʔe,_Pi3zNt5D\E Tf8帕J+3y Š(+J!] SeܴkQt BbtF ˱ %%IQLyPH9{ xޚbK!] Se &l(ɭ=fJŒŔ* I+P#{ )Ḃ]dv!a)I!] Sej tl$uҀ@ fjt($CQQ`KRS,&cRΞĹ@SY\}mM+AQV|7B~eQ2c(ja2@ti;BփQs,BBIIRS,&cRΞĹ@SY.bʔPb$,x)W+S>v* ~@6s̲;X!a)I!bQ՝],;@Q˦~@Ag:jru(xY@~0/ @Jg'o(B\Ų՟l@ti\0/ :($Jd/IX@*P%EH()IbJ< PA CHTPPCfˉo+ڕ)@Q ;Y(`MЋ!!P4"ۘAf@ Ф JB(ֹ2LREM(0t F#J!] Se*ɁBlTfW-lCRe!DuД QrZHxP)TYRS0]=$))^ PA/(2U_(jJGRe!)X@Q/TB:iI*&&Ǵ DL6H Eʔe,_&,CU'Sec3ė>iٜ(ƧNDZة2PNe/(fny ^ a敮>a*J!] Se :`=zHRS,&cRΞ(2(HRS,&cRΞĹ@SY!IQL $+6v=Vk[$EHX,'bFRȯ,ǗeԎ(^jK(HAg5c փ@ HXl#!PH" |!` Mלr"o ʢve2PNe/ YO g#E_c ʱ( %%IQLyPH9{MeA=DX\}%JRS,&cRΞ(2CeR;Ң_E5eI| KMU[!9VIˉk+ڕTcB~Q89P*%&Ӆ tjˉo+ڕ)@Q ;Y LZMWSZґ(TYeLzdO~@0zE_IbTP LA$E1b29_@!I 4dV|!ex#)W+S>v* ~O!%sBPBP6pf}b.by6tB1F}!xgvq'YLbPȯ, :`"Q` _^U yx*"PHiR]fM=)2drB.>9n67g&8aEyYLPȯ,f:F#9<9p IB~@!E[j3x!MGW5eW!JBA D_YּFD:U?]S0T sG'LQX$ ¡h[= Jf\>nlla< (F!\eE)2O .qVi|k((TYL ep3P1=q5vA I^\ H==~Vj G4.Y ``ƴi&!@@@r(G_f}i^o/^jGE{f0*(%@!(ՔH!d=&W!B @K+ޛ7JY9E*==s&.o}VWۗ{U9G{_9o٣k?{5<`=2[>:ˇWi6(Jg={Dƶq_@Gr_E)EBx?M+VcH$x~9ϼwYggߺ*;`:m8_yэxr5!ɤj+dYD)zn=ϯUcm!<(>xƒFgsǶx(u%澌 >{7ɚxf# sO9sg|S&| ~~ֵ=b{aorUuןt톥퓞YQx},(@\S;:Ղx}/" > }ȝZ࿮7;=.]0 ?k3NסĀB,U$GҬ=HyM喍mHwە (x# yP H(bm[ڨBQBʠN؏:}혎Nzl&v2OqtcPnx<^:PH":;%龧Q',.s(@Eoimc5 mh8lېmAkfyW/"˞7|2)PHBcEk=%r|$7 yPPT(zZO ĔO<|7.ٙAkn:񂫏%LNo[qG[/S2bRhz/K]v<{ՈTdH! #:cd6[&{{W\6/~Ȗ޵i]{Nxx<ۿ3Ęl4e|7eu{cw\`dI!Lqa.zZMsddbl;ǵR*hE  >  > LϛJZYNd~*T@S@ (B\MAӏ](DPHI@!t TV3ٻ$!Pؚ,vh&J cX8 ~}^ZbyPHG@!tQ (+G/Jenjioأ?>ZsENo4VuO:<PHG@!tT+%9at=g|ӻ4Uq虿h;+{|E/Ѥ3Zp (((@1(tuxݯ]y~=(N-7SA[dVk鞿 E1sa' jdBA { 'Eu]X48=Sz}~Za_x/2=b؞e`{Z 6~k@4w6 f԰FTF5t9G(5  Yz] EK4GkAEq au#O[c\tӒp˷䝁 ȘF|YrAۖvb8NF=W7*3/ Xx@W9F8 %Vb' R]w Q G0R-h%t%(9>qYpZU(Ê#P*J*3 tcHΠX8,f(((bPڨz}hئ?}>vmnj.ō/@l$B R ! A> Е;.糚*Z(((Q;D)FSU-cmՁ%GItsʽmn 8IRgP)*NSMPsTjleT>XV A*lJcP(tՔLv=wT¾;?]ħه '3փ F / x,@8vc(v%q*RA1?@@RMd(h@Hu[}/mkBL3|9: ǎ0AuσQ P>$(ȠEWtFyϸmɜK(TA1n8h9Q) @!(ĕ:.Ee?aSՒJѷ?Ȏ/Sq‘9܀] *I@@@!`3W8DMIrT,&E8;05>kqWW$Wx8\ߩ(((G)Dւb&S8*2̸A}"N[`Re ?ԯ5ZAl >9#?L0áTrՙH$,#L<'*%[a ,T'bx@@@SKxhgً᥉o9`0t8Ԡ?T9 J)9.~x)'\;/ @@@!_htB/UZZQys砷~Wo4y.fIŃ& Oʋ%k.me=xdm&5ͼ#{{BVzڳM_@dl @)̙}> Z`4bo:.[7]\^Q &V㶃>jUQ\cNkqO<ןUUh8l z=ܘCTAmVzn=ϯUc" _P,1a2e(sX e랰i}SӫZuOOM<)M!,E(&%/" ENjVDǀ3F@,~Gtf>^*R%04eFH4:c4B=|K }7=pƖ[6~eD<SB~!Y .EsgL}[@6**EVCʠN_Q䰤riAgx,FG-*Kb 7z#ϡX@@d nstv+v-+ LN~!ApERTS"RVQ51bo?ihGaۆl ZSEK=1' 45[1:fd "0?(t>  G>ٳ(& Ekl1m~"Gg=J (_PC}UET^V䓦KO<|7.ٙ]v8~uJB9!!,@t:g糍6>ps8ag1Q$!$ U89tޞڝ%zj߷~=]~;"ՙMK%u:.d0$#!&.lv:'_j\?ԈX"Q; OӁD QrP}g>9~lB)X@)a]H!: k۳͓oءow;w3G~$}jQ?G?NO$N$KT Y])N>u j1f@ QlBBleSTʾA #B߲Vxug˪Qՠ l2>`pHPŒӑ('";7)’RfsTm(Tz%^A(&PbURf?DH 沲(.ˆ2"02WknHQ_RfsTM( O򼍘;w3v͛OjV\Q3!ׯ\RfKp\I̴){b&Qr_mYRu?QHEQ!Ei1GՆDXڏ^re@`<> ") r$ZU\ b.|#{I61~)ꋢ6BcEzC+Fmm/_zɒ%.7ڰfDyh )JN:.SᔙI1h#,f+,!REmٯU &5:&z]WC:~ȯV:>|j1SD0R ˶e*C 9FCBlR{e"[)ꋢ6Bc 1|W.q.Y|O~ruܴr1vT>Ra B)uZd nBX^+D!REm%ۯU e]zzEsFq_g WwO75"Bʠб:zϒc q9ANu[F _)rʒ96d{&ze.aM. Ģi) -k׮,˖i͠XȥlYr1L _Syшe@"AidCj^ߡ#z9ZIQ_)i ~E9)P=%@Q5dǻqmO|}+P?%f4U"[~ˆ/з^ٲ*5rT5(het[bXIyv9x_ܦBleSTʾA #B߲Vxug˪QUPrDa6~ 8y>EIL# )2" 0 )-W܋A_Rd96Ter! RNj5(j#0x BP(T0I]Gdd 68N{+ER[}_Ho^}!zU bd@>D"}R65oI>Wk\E8t ZR':FlB @ @ 0]6LZ@0:9~ǠЇ]G'uvzLfΊFÑH8F!I9OB #B߲VHaR[֪)z^ǏGփxw:7 aɂ@haOǂ)Є>K#׿  ;`,@*ǎ_i?`0cda>/X8nssbƹ8zfHv6p/@ @ @ PhU=(#8tc\'xH $E>-ࡺ+Jw< `|>{S}ݜM$B\l1)))< S[G0v޽$8q~KPgT,WHTN_Bv֭PsS֭ J&23 F͈ 2N˹$>xp ^P"ѰQ!խ i.)))4">XfAwzR45#QgGe8ۗH$O˟yz4x©#ȱ{á(:dZˢ$%&)qIXR R R y(P';ݤdhTs?H?TVR |AMQg>Cp8 )V1ZƊ*?uDFUݪ52jdj=#R R R&Ee]vmڴ>Kwu`Awٍ!Gڳg췷*%ENU`縸)~Dd%.?%U+fV@ @ @ Pj|8}~… !oIu*sòF :ty=8@X,U1:"0gL9"c7Hsq9&9QJ$SM.!R R R &o< BPz&zu޽ԬeSg9%[9͖/զ$ k2E5Q(}Zc[抢y뒲$MrҡU"z='N|DQB=ә{pAwS6\6! z*`9.Ն[R^rB }iYQVekQE~r46狓*sW%odߡ>JHhZc>ȩEs5reGt'X`FjNAH,WS'6JW4ARzY|9I/~P\}Umc[!Gz{TUQWw:#=GKI|9VQtp^#kjsΙc_˧MeE}Mm)Qt6lx!BsSgΣƏ$ͫneڐ={3w}_~|koW,_tl6Nf͚vLr-=د ?׀NQޣ 뮹`XOrҡ_R3{BR^S?k)_?ʛ4{KKo$̰ՀvzG<6l .N(Bi<ѕH7񂫻4E+/+? y7/~L? <=22|Cn~,1l* Ƒ^(,pFcۆ{i=kI%/kik ϰ:rxʴi}}idR?w^cs jhh8_e!D6]hyd .[~b1{8]ȩS]M/]:xH) -k׮Բ){{+CdYYy?cp8(޳}峯=veK,>\ įK¤kUNǛJUa:QLw=xHM߿kǎqMԓOEڹ}[ f)/l /ljdLm!݁v[bƴ~Pב^]=M-jO`lK/22sU)ܶr2B{,#9),p UwlZR*(ikz .eN~βyp:, qcL=r/>NޗBn LkOqmO|}+Pd^5(*%/з5(q0G[֪sTqzѳ NGKssuuxzNO/a ;f7tt$-;nW9ǭu:-MMˡe(*%/зe(,p0G[֪.sTC,VpeͶ,~N^AQ_Ho^t|Y纨=o9Ërv=HRWI-( # RKmQՖu~ kPT5G_x< BPnV![68[HQHŽq k\HQm)M㈢hs8;:':xQ1HU"<~KLj")#EA8Sm)ʼncGD)R E(%YDápB"+$ؖV)j)ɓ#᠙)85 G7拖%):a7_(O44z ^y$$)xCі>5 ,F{|>KxEQb)f;j{_?/Yf^#@3F."(2)j'E#IR E)44(r%_[G^;m?m0SB[yA PM+0q] RTPX7T0N^dzƉnzOvW͸Ż?pt?0<)*+kPft˪'C\;TSopFl~g/_Ȃ>_g i^HQYYw7_FXWK $Y`xHVEY}A_sȚj )LBXPA SA W[qs̿bof|%@{~ՄAAP1,@a)T!Ee ^*'+Wxw=ͼ;mR QJWSHa )#.z/\2eÞ(gY\k?k?(*sޗ~4OL~R }v=^BՖ =Hau)!aPZDQY3,vny hYioܸq$eYW,RV.bۅ zcfhz{R)q] E-e= f$p';FgT7p791cP%ulzt!(U)f7i#f]ष m3)LNa)RT,I2ŻqmOXvM06ay=moҧ)-} k\HQKY"t!Yg)tW:昉_"*nnw BL]l˻ ۚROpVXBP3\OD.v?~"$P( *EhTn߱e˶^uDog;yP( ʩt=4ӄϝ%7g$Q( :W,[f=;KHz=o}|1lxlCo2s( BL.i {h3IENDB`muse-4.0.0/src/doc/pics/mixer_strip.png000066400000000000000000000315611404105311000200240ustar00rootroot00000000000000PNG  IHDR?qK 38IDATx]|ul6=!&ESN ϊwꉨw)!z*HQD t^7f&'/0~~ޛ߼߂[&JC׉*xat()EFUAA]{֓[̭ %No 8 OtdΏ^#o?O:2:VsF_I8Lme;G 5\!U1eV(T9 nT5l<¢q=rɢ&Eue *9=rJ6J:_>0M:>!N$ a)l0w|]Ʒ7G #gu dOӄG۟[ò+Oŝk0#GI)X'^q,UP:X س1,yļ랝=^Rﮫ1bxEi+ۼ1O{CLs,loi;{SJuzqs;`R+ʐɃNf.S}s۫]6hbaD.ALc#DtݺÜ%5+_w~]wMKV.8jviiFTlݼె V=1&{__CQDr<y}/TD;[1LU:>CNIhsq00&.yFt݆7>\+4}WbjE!1Tzjq3pԖ@l.%u?pLW rAE/Mƪ/M}S눧>Y| }w,;!UO<̅c/xbLg: w,g[0L7&'6;h_T&Q]*wC로$N3{<,9T8?NS\fNRg Cde?XzлN]6'&e{#N_ёeYt\ 翿t,ay]D%,[[tn7Ү޲ BW۾I8l[Ua_5q_fӡ4E꟮zh/,ᚣ} SJҋd9h䞾Gm$VO gluRֶcGZ@o3 hd%qwzll^H"o>h0>gN3BpcirVUV1㣧jdde9t窪|zNHDvsA}^A $aJd2֚cU҂^l~7ź2g1Wl^z*cY?}g[b^~Xw0֍GE3]۸LJdXú UVs]ӪMW|EYu97OLհ{]Con^bS?ޝu*onj?qo,a.P~ƙQWV!uC!= ئ?1c`RpQRIV%:|n8-MLZA0$b_4>u-.^D k=^JO~Urg;1q#T)>hθnIB;1/Μ6WC@Fv'M|'C>.A9苠!(Ѡ3$ʜ_Ϥ@B._xEߛ1L^pŝ p رAƍ%NIbL"z|=àKZqUGܔ(`KDZU{Â\Ԡ9 ]WEp~жgIR7Yb4ޱ:mLDƋ9ZZ4qqy%-V+|zRLt~[Q0z\hOk>>!6XBbW Gϐk2ap3?3xo3Qs!'d) xG5 xEž!?_ b 3d-r4x]ϰ(/~47$h) x=E рg- x ?K 0pm+a;gϐO=r :EԝaA~MGR}s*.,eHT=&gPœ&9>%R"2'I `]$zo?3U0|J(d>'Lf3>KܵGB(<8}GNS$˗HnwTUqm\?vLev Jegj'¢bb5[:.? )~ܹꂂ,$3IdZ2b`0+e1ApbLhb-/ۗϡgpNP(c!Qsmrd0p(Uj`Ѩ8 mdRT*ԞS?\В* 3xavi˽=FwY&1 kNr7+YyըWz{\oy/,ad,WOv}h*$(niK5%]ٯ󫎋F˷ٌ~L~ͷ39[L2F^"na\f?jXˊ{-^Il pkB KJ,]I0m$ٓ'񓧪k<4K($X0A>z&\ v3f&-*9R sI8m"S28kew{cC|u8ӯ%a|+Z<]ǟyUU4珋|ckV5}|> *3#XI!Am-9$% f_x¤!_+C`C I|Bb{{;A~_9cNK~k$ ׮];& 3cp}\a`l=}!aRlh'|0+QӧOѣI]FORS_rI2cua9lv/$r,3#feC }ir И r}hR.))ZRI$NVZOA2XQ%vCEM>kAj5X2`c תГ%'hVp 6d4Z,NdX蝟![R32wWDh@P_o0K B$.\3=:Z٩2C>eי(ӳ}g,=FNn.LgΜ#B& 6eSSNZ:9 UP.*tpIU8~!Lf n}/ (Y&%&F W5-c]8'\!>?۽53~W= 3?zKi*c½Pz >tu^йCll6$$$蒓+B1rݻv 5}0+߄]d:Y~ }Ej/C\I$*T;t:-`VR"ʓ1aMNC&HKK=;'f(KKGmݺED* gL_o;c}`'1ΞFӞig}:3),0Y| H"~EC kq7 "t:y2RK+e>+GtoNx?Aw|̽uOMxw5\an̴Ki@NR$A\vhۅ1$  UQQ1dh* 'O467&'hL傻a5jX%50͛[o$G ( I~dNQ ,z$082'fjk32ð#6_Bzʘ?»}7?/xKcpRk|iuZ#лAugAX֥q(ƆBR z747 i`Pzm1T+4N4(ް`A{J9'h :0䝂qn]Чv 0%h$r:BF-mǓ40@x $0nV9_}e8}Cd2&0'/`z=fwF^I0w񵿽R[[S@6I{řp^+ 0iZ1-$AM7^{]]-@o26>5U6x9 ?ubQq 1vv[kej6`@?`~!g^Kii_-崯 4yn/455J%9[U5z`ňޒÇ-CDơf3,V}}@$2Y! /.Jq~k#77O]&`afqjAuϽ`]nv`0anч"Tx)(~*{a[XX&mRRSo.Tw]%4Ι`ᙒj)1݊<0ښS-A|Ql3p9 +۸a}~^^@,GtV.hG"7| 7:q=j+=7Zvhjj3 |ݸwbuq;G>|a_ޜbM._ZR]}.+;?2{=Х=׮]:RT'h4$JagHhhǂ (#mPN3>`9P|ݏE 0\~ኬ̌ jRg+ K0րX, /H'I^q#FPf kdSuP f]T>\`eWB|CYJuހ ^]u$zJ %pm6n>hg9#uuƻw"„|r2q*E xIjt #h 뙬cŧ#a)F葐R!I3ZZ\ ZqK$`2CfS@^]ދx):mrRt`q0$)K鼢"2 FJ%.J$`r9ȴ4Z-C"5>Ōb[KPpn>J9ɧ%v5>.>A *Ԉ+bsȺZBΰp;zD$ Km cRUHo|A7b OmA-T7orr1:T,;:'^yVc@[i_O=y6&%[7x)EߌFv16Xя^$GQ`҅$1V+Mb3nt X*@G Ol=VZ]]ÿ$ uM*Σf7,fM(cRLB޵glPGԚ]wͼff3UHNssX]T .Q۝T?_,KNJ %[# ;H`T# z>ǢEa>9tHH+ &RlMU AwQl N>D)ab:Ic8!Nn*v c˜*vq2pv﵉0 \85ozPу~옱~8amQAqc a&|aseA,=SRRv;RexT* FdRzSe47&gX]|g0T~-5c@˒ &=`!"u<ïޅ_U{:W71_&ӝUN X*^&vN,c[@ߝ%&4c>nT"z*$g@xҀѯ躐oV]P|]80jǀ;>JTϵL Z7*s_ roin՘T1~R`cv'Q;>**с`b{2k/Aԇ IL}s|gX7`!G1 jL.ɺ/6fdgfF>R+h49{v{gޠl2D*9յ5$ /%9lHa'pЛyfyf>҇Xz߄ ׏O0?zQbǩ?ʡeT&UYfer}s+gv|vfm&vvju/?Ծ8^w9!I kklX'ny|e;U&ڕW,u\8Xa&;FNH v佗aw1y.<=Fd ٸqJLۜlX)%t62e |aȃk=(-W6zW1[[ΜC/ҳsR32s]uW?nfLPUxu|b4A;x]痭-MA:Ur*.1Ih5\7 t޺oC{k\8#Gktq((*IL9NϷrU8ь3ںWW\qOI  B˦\B5MU5)+$jAf@rV  ynHr.,|uGos!Koxfȕ:K;vouA16ӧ.-CFRcۥD" }.E_R;E x8W y؛{Z 9iړw8Z?=8Cdz.Js e %JΝoxm4tni6I.I&L 9//lڷ͇*U58BM~իa`sd |y)gdpZRzVcut?z#)N9]Cq-7,0""|7(G00)48eT[\þ:dWA/JU}ʱTp>CuD;$uOPpo aXDz%6_`p̛|T ]X;c{3n{}$y1/F ^t%xX]S-+@rpP/6%+# {~eΊ4b { (85LgΎpaQb}ܑBJ==_`^.k$[m6HG.(1 3́}{`r!OV9wTn&,@|Sp=&0zJc8;[͞<-UuByŇaN Co u0zCwt[9up:P\:=c>L|8 sP0O)8x`:(gy:"-aE"6Guz%N œPa|SpP` #_y,h3q錴dӏ^"T;4.8> I@z gh4m-e /jSN`X0̅O81N(L <]܇YdzMi?n`IP" ^C#h >@o#D$|Sp@N{C0%A A9Qe1a[ )8x%aG· MaJ }ǐAUJJE*P3.L`à'B; ~p$2yFv6Q.lvǹJBQ?; R$.RdEFHVG\Aw &qHi-hL3ٹ.AzJQ*Xclus;>v8T8`%=bpuL&\u7u sb|?ABY'FT6>>cA6[]]Opuӯơ3|Wzy⌌p(JXtZR OAaE#fp  r8\w_YIIW];`a(X,8? Eƙ0@XTb4Xky9{5uӼn}} Ý l_͝3:Pq`@׈@S1ek@c /}^A|`{.ըUݰP YD7õ\t9C"g IVaePK#|0*~p^rE/ŸӖ+8Ab(FT٤iz4L%4^.#vmBp;"fw[?lB{,9olߴ`L>cjJ S#q'4݊^HH Fnefϼd҄O?h(3yHpȆ#VHct 8DwЉ$M/ʸwHO{߆T&oni_]̇.lc0 ` 1ogeM(䲽{L"fn@^&NeOJ;Ϩ \4Dz0>Irרԕ~ذq Z~ 8${_!GF#9R,=%aFSJjRSRm{*XMH٢xGRR!K>q;qp$Ri"$VA9?܍Խxi X D"Q4*oX eO.3ڢdzΐ4*z^I4jԘ mmύ+r%5[F=;B9w“(jy i$ws]CdH*${ᶻ_&x(^\\ ; u7ؗ2G"0")8ɞ#Q$)'х0wui4 t`$Z}vs t:kkku#F!2Jd"BE(SC^-b 7PD M9x$顃zJ`СC!;\$ȼC! )ID"WJXq!i{]D+$73gΜ={JҸ+Y疋t>}T29'nYB3v .'H t h GTNt%G@rKpORg^8箴G(3vOո]nN} \Nee%hIhή=un-K}aЈt;hb.B4V\Id],-21h55K4.9dpzl'<A{̗f6'cd C¬l!t斨@R]M~ꭊK_}T"!FXKGO~샧2[^YC/]"XI22~l Uʉe+H\:ttV{«hLԩ"%BJ)\'g{M{rlo_Dds2\A.ün&("ivj度m g4.`4ww}.I XWC=@f`MkXŸzY6$8 @`f߯/7avk

@Wiwخ`8߯mkڴCu(gzta-{6HxNt6~ۖb_s~mRRRjrڞ` ıtkx}K_׾+QĂ:_W6GIENDB`muse-4.0.0/src/doc/pics/mixer_with_one_input.png000066400000000000000000000752001404105311000217140ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1 tEXtTitleMixer A" IDATxw?ھ{۾slLhC L@!!R~Tɛ Ho-;T1.wWJcW;o43j<3̬?'mذ*Nn9 /}rœ+DIiK1H:ù" ^gVuu1ͮ7}Bڳ<P[ȫmͪvrkpɶ&qJh ̦EQ4f lIOUBHQ$„qiwt yiL<[$/*7ۀm ɔIQ S3݇Sʮ4]])JL@*$S{$R;[Pc܂e.Q; Ӌ,8NB}}}:VE[k{afaV[+[ZZ.Vi B/Z_:Aq#d A"(B@oo_:30Ҷ{{eG!8r3"a;Boo/t!a3p9]R~U֐[Kl֍Z[7hьp^(݋ge! bttt0aB-&ON"x@mmmljcfƂIDpl :H-)yw#HDI>BjuZhPsUNwqts(mmTUV0ٶLyE+1θq㨫hPnZillmnXNj(ȲŽ;(--rpqH80UOiv6U2eoշzz ;?ۺjwV^~>>]s /ݥH8BΝL>b"(pH8R\R”hll" kKiEиsgTnb:dۤ/Su@D%Gw""_:ZPDFLBwn(., ^bv{Dhhh`: mQq1&NH8eHƦFNܔ2mtvDLnP&#v60s,JJ m1sl[MHja֬^x_~nyz&W{<ނRxi( ,,F2ML:!  DF#! !ISNeG\5v C0QːNQhy+Oy3%D͟HK6'^CYfTm2HeT+lPv߇auiksϿ] .;gr z}׭]khѭ.7&>$PP+ Kgi@ i::K`jDN;4$o3!;g.o`2mgL"2--TTVF=}TVVڬ1e׶Z1s0rd(Yћ \?fRB;嘽Ag\&2U}mrEDz̙;l{8!7Ugp™̝5/^*B%"GrG](i $msNv]͛϶dY6yYimm"<補sםwf*Z4& ۭTVVm_n:o [oW\]]]Ck[+z])0MIoP9s;g7gݺu,&8χo~wfZgم& gj[lD^:DI]}"L  5k?"CzZBTY ֊LoeYw BDw-5Xaɕ1+G[y?pn7qu;<=?j ٲT^4o.H׷Aڟsw]o.'"/.c.Դ2]g{`Whޞ.et϶%]P"+t}ox#<sOV ʍ9\թٰa8,_|b.\;J(-9׃[(T;p9Cw3^*FQh$L4:~m"ш/DD#]ytq5nUM*)dUBttfqwbݿg3ݹ{"S+5w'6R͏y# v,K9gwM{y1GVʦP"-(`B_~@Y-.b~^h'ʯ*$Ϡrg8Ţ)*.%m\~DcQڬgF>[A4A~n6.8>|!8h4,.]]DB, 4662PttilqYe;, ա$2E[B !P`9`9|y35vG"ʼnbYՅ}'_Lj%3OkZZ, 0!,1q*3/B ^C/Sqw8~x<> ,&PG VU <m0pfl=09f1g`WoJJJ&.u}vg?[wimλ06RZ\=?ڎLˢYF"\u,{I6}9W\~yLLj%fe-9a#gv~BOš;wo`ݺ̨ V]) ]n|?;Q9}[7e)BSS~ǭ{X;T]a4 FFLܩV*}Jd@ vQv" SQg+P!}&KHIb~˟D?7oIkB AZ?z7aa,{otvˬz5c). ˍ(ygSvغ39λٱ} ]V3'x<|~m9k_eo%ذ?8[Z?n۴Xn:w}7+W~}>Dyy>[>Ҷ DxG3k6֭O4=,:Lj&3ϓpPVZp:]9GRd횵p8t22W ET"-:u0"ԯ\ŧ@pə d:/ׯ>]})l /^yōxmVvl[ʏ7%^GRfZ8<[|0ϡg\i\]8N*+x'x~xJ tBݪ,ﴧZLzV_dؚe MzxR͉k90vb"3fϳfMfnβG;_QLZx>D$q^/X"zp<Ĺ.;1JhDF∅*߉ADDi6߇#G(]j+,l߶>ܹsL5,އ=+i;.S+>SnVYh!)٭WLvg'P1o9sR;|f'켃f'Kp%_b_ᠣNCV_"NÎ455&&\S3nuTVrMD5"Kd4+o3eV25^jN*!DvX(/V\X4J[{;ill+H4vQ\\̸q㩭KLu.q8mmm7Գ`X<墨qjP[KyyJ\FWv8mhhn8n"jjj02\.W?)q1pw[`B?]Z3Hr)S .~?=L!N&F:OFRTO!X8i!!ҢK)Tq"TC-K?5yzJ kV˥;3(T +gչRNJ_(x.~BnwW2wdC}r %t(((Nno*L )s*w*~BěΗ>|<\ve[ÿKm Ț?tĽ«?fa7g매8LX7Rga2AL4Mt"@QÄ5"uMI:Svq+5{R) O8&xq88ßF+;x;M'w>cG_uao|gy;.*?e]nJ.a7龜5yqT?w%W31Z6G_sf5@sدE'iK ..V}0_g'o]\w ;6Bi|%' A}>>2x̏筏"Ѭֶۘ*?.yuU]B6rN"<m"*HVbH:~W Rk41ܯ_$/S|{ë[]='{pQ fvt0OcQ|k|l1_ƺV%!ZZW(ݏ' 1Ә׾ g5yw p簯cO}ݬywQOc'P]G:>y2@UfTDP9EN YbS]֟~:}0q LtAJS": *t"DӋP^KKo 'ߚRL$%##zS+wA!w5V kj"ұF"ɭtP'4ERGX_sӸ_eva󍧴6_Q4vj~ G횺L{G^Ȕ=R"TθNVj}h{WTua.wf'|MU m j IDAT -8ଊXXYwbaev&uQDuK/ᴒҿ&zqB-x?'Z(P>& qUl|) 25qop>5 IJ_d[,JOD8 >p,}I ^ zsb? m$R8j8khfny_%iRD:f4CCT6s]RH>ZǦcE!XTusм}]Dϡ>L%KS"_8W¤ص"3-ɾ{ݿ?S2tW9xќ)oߜa!P gp̅2[d˯kPQ9.o~jjoGMMroq p߾˩Q@qRYx Dfբ%x#\ɷl2k? _ΣHA+/qst_څlav's=,RDm叿8h]р͇o>zq x~2?!gG[8kB']:"0 j39@^;fw&g0t`k 7}^R8G5(F_cWL:E>iG t^c+ +٫|6$SN/)6' +h,7]]I8 㸓kʽK(Ug8EeI([[\39^/Aส ilw)bH=%;TOayFBܻnth?6>n7mo݂n:2xs_AOo%tor0x7""r!ƣnf_"?}#Ղza1^߭4G7iO~cOw;b]֔ oO;.xp]l} ύ1}U嚋z*eAyaء@me1^6AsUpIxUs{i R?^y(wޔt#@@m묧[WA©߸n{29=I.'%L5mqbB7B{jYT[;`)s>o=R7HTqzyC#ޙ̺5WPx̯_ϺTGA4;Tx^pV87H 9Fjx^<HX&|ReRN)^u\s|wppD/uM܋UK?Rq?ob7nkzcf13\X!gz(Uۖm+ɧ"ЧU+%3RcW0k~vGqԢ)@n_8{ mf{OW?5KJypK-f޷doY L|lY;n;hjJ~0˾`^MK5o=MrH LFr* 5!^m$;65 Ԫ[/ZJ1Cl[CwQ#G7d}}̸io|ϮTwDW!)*r+9LoEx اɶDܟ{nwxwoTe}d=׿%޵,x{VBr]iy*ʜyDaZJibqܕ=LUl!% bPy\bN$ǓO =qG/XT"6!0N 'G~aGTiæ8 :PRTgo/Ea2 M|*Kj+z@r: h\>7.uWQ&𻑐jnfrEr8=b7xgW_ˈT]%d||:iOo<#1X#;JϥmyTy35ȑn+`Po&:g1RNiWc WWlJ(,d}%G{=x/zލs[mrrB/E=g8fcOf[W=R W o렙"ί*Uhg% c< VRiic;ά*D fgA> &Q5 ɼ3!9%UhYK 1Oac=TA kx2^:#o4}SJ=8]=!vl,R<"o|9>Nc-y5G-0,M%[XDT1smPb nAz2kWsn)B 6SOJzn6v=,kԶl௛B)xܰl;!}Mqªx/64֖lEQ{/"ԯ],Y/un EC<%W3 0L)XG\w+w7jsιsx>7QUDwy띗؂|~$το{wz>iWO}}@$Fߦf>:DpEFVTFCaZSB3]8iZ"b?`h$C(B4AP Jl$$'G2R*cAm@ j7oE)uUQ$qD?W}&eLP"x kǽ[ާEh Vixwk[7j+*'zFBz8b +-U{Qz7QL8w_-䕇/_YBWw|/p+\r.aQx 7„'uUs"?ŻXqI \D۾û3k;Ǣt^ ' E P@K97/ r&6^)kq_Lgx!ӎyj&8a\!~G 1-|rZ*^IͦH_õ+i{3bd"cjz YEE8Dw0G^>{AH>!*fF-,WG],=WB4ud"D :ճ6D$pQYNP@)1'UWtXx p ''b0;xUgv]5[a8w@Ĉ'_SͻNN8FBX"!E!B`@Y :o9CɤIiߙ,1I8  ' sʇӳ1ٵrTI5CΞޗW>?<(gO)?M[X$N: 7*ԝP+Pt'o2Z6׍dDAQr)xtoR>00xMOA$%0TM>`<#X0?\u)kvIV̯˄DkZ*ӿWƎ'i Jq2Bo89|H) ) +ڪI5ui'_\rwwUv8 #0WoOޓMwxX,/3V(o?V ]>ùj$grG(zp=?̍=UU7tDdfG w6l|kZV{G;׭/{RG_HC7o>ee66RE[nPp(^^yjB$pP[Wñg.Eh<ƶ[kœ]i.!nҤ=پmV7P#>RHɡ<_LJӑXr f5Ápt8ML oXCψ#w0 ]yl0`GE'Pn'qaFo)Y͇6Z$l}g-Bye4}<9f6==+Y>镈vWri_YJ'Lk}ߏ{+;^-]9wD<^cf|e)Go |haDRRy:NNM;88T8G6 ;@cpt] %K߱ͽD58av+}`^㩘EjW#Di]OL)p&c]DP[7yrnw_;RX N~\N7e^[H5HNq75֍[y-l>w. =WP5u _Y\-S$GB!´uqL+/p2n\ϼegGV]E9Nl>$EePddYNrrYF6ci Ӌpp2͛˂m|0|ʑ--ݰC~]Ex'ru_[2Tvg 8D؄qRV?}wDw"-FȲ~ -l>Hi*nH 59.be!\L9\(ѧJ݄]uݠ`͇&Hmx 58|i>Ji@zaıAu|ha@JR.@~x<ΖI.>԰b,ՀFiI)FP:;;C6JJm>4]|>4Bt5aE_(D_C ۳6F"~KJ~(tuu|;;n%%cEe{yCWQ^A}_}^|WTW/6*$! %C -v-l>vH`, wB6c\39"%Z|ha󑄽eD2 j|haEzW"’pyf0_߱Gy{ˀAC -,$<-l>{Q{I6Z|$*e}^͇6) r6 |haEr.e@a͇m̺)!1$\ dT3D^ --^"͇ 6ݳ6Fn%[h?azI, ۓ[d`oE|׏!8V[hĊj%Z|ha@o, -l>^͇6Z8 pEiX.J6))>rL汰$<|haE%Z|ha`׃K=l>F^͇6)fr{I6Z|`o0$C}hu͇c\svS{qL, 6Z|hw^"͇Z=h/C {ˀ$>*e98c6ZwJv'- 2@%[h/4ceIU|ha󡅽e aGBe@>{Kµ#w%Z|haEޝJ{I͇͇u$?4c3ƘYn6Z|$ ԳyuXYnur͇c\}ހ %7y|ɡf밗ka͇ykaGmi/ C -cX -l>ƣ6Z|hau&ĒpaME0e\.fΘF}CD~~|hcǎ=z|`yoEu53OrntcL2~|haf&x:erHx)x}8]N+Ʒy ry'nO͇6H¥H/Cw=fYp\\d@"N(,ar3G͇#T)kMXgg'>^ ,Z1$ =k%%Yb>L7|UrYu$((a؂ߞ,ttvv ۽SFAIQMlßR\ gq'd3FtzY!ʀ~>f Bc LPCO&fsK K?vQxQMj}u ?Rx' ]O3_EQDpa]2R吟pEsh}|Ϲr7_cbc|LJf/W_zit;d IDATX.\a\C)"¦oz!92S;"JfoOWVGvm>lIy>I.#2N'eɧZr0GyS]NmuFN&V݆|?ɚh_H{D^֖}-l>=gӡGI.dY6||G o_oCe:IBB{oExs^@a-_NAA!bGj F-`B,y'N[okniꯇ,Nx,$~͛7̓OϾRUYe%yW^~IL?tִ͇6ȼx,륪4< GF#ĺ:v44s P;61[ l>|,D8p8ޭ"STTT;|h1I,e7cֆh $IJ=У>T&VsH39 /# ca8"#Azxvv2c1bXxos M.ؼڲj/>IAog/bۍ͚=lF 81=X~4vҖ6m$>r"&OFގp]b0~7a͇dkf`vF>҃#l0β;}#+PYUjW`P-F P"&6BAeZ -+i0l>,|+1d߆>]NBQQEST\DQQ1OQzC`0H8MO_`7 Ȳ?~ m>l>r'C\|9G,[< ja͇6Zh?4[@Kk jܶZ̧lǓF5ᴕml> 1B0tL!*En]b YW?Lbv=vl>-|軈.q"t͡_ vGc"˝M+4a$ÚݑG/* btvl>e|A4|.@-l>oax(..6XTMww`0{shaaL?͇$`8o<ǡ9Kaa#㗚@Jyo8(49Lm=X|X;3JsB`#HaU/͇݃%#\ K-UVVZ/^fm>0FR`_q([6L4 -|haG"`ǏQTTUi7@wwzz ñEtw'ݜq܋ٲȇHY۹AxpxYch4WJ[V>p|X9k&Ǯ@Sq?KrѰ]b1>4AikwCGtͥ z\iݡe%͇u#,{s%)#g6P*CikwCGj&Lj"wl>|f~i lKahce?1E4l859Lae%͇U#KO ^T aU͇6R ELp=ᰕCse)w@z~Q7Z%W0Tp6m$>.bvl> |ѣ.pm7w%l> ||9:8r|Xe9O%Qđ4C N[ 6Z>";  Cl-FX.ݹy*9 *blYIaaCNqSl#* ؠj elYI@aaHB1]F\ц= ARE fJ6G6M؈kFÊ] -F.j~sL;}ns$ad/ 0HlTf=iUe%͇e#MXz0vl>m|س-]bz7o)#I{bfEacD<CWs\ꢲ(sss;:~TRJw2ϼ2IW>yy<>gi6:ɲlB:el)<>1K պFVz3tߧ iҫD"Uȴ>mx|8NpìNEက܂G9f-׋86ߧi6-7UJ#r 5|̃MAIxp MoN%ֻ j7j2FyDŽJ.|nqq#r m|LҋXD)<25ߧiP-W@'^ڛ7ٲum43F11YBA2u&ζ(l8p[(l`r m|fӷ<~TɾFz33nRZN|6<>|TCuEDBSQ3%p [´lAȴ?x̖ (M됅5n]Xϴn)iULKsz\RH8;S5:5BDAw}iUG9<>tG.؈9tI7t!ݵsHph/?Ǭ[Դ(vrbtv_E\*l|  퀹q/ ~3zA7qPM0e rD"ɖ[y '1]gayI.>hQ$_y|c"rf;x'j*Bp3L8Bo׮eQ{=XYI 3N lr$M7N#l qzQlpWV47vJ@QL5TNAWpwM4уgS^{M]Gb1fr|TSBɡ]I-$gOp߆Eξ?"mchyy BǮy/w=o 8Eld7dپ6 om:Fh<˩7__L>ʛFy.?ؽ~i5r2>juӗP1 )Ǧ~kKOpYI4j&%>DGu@2gq`͟?n8:{~wS;'2Oczx|$ >XyO7>DŽ^4LR/?E| JmGym_$_BD=Bn9.<>qQ28(J_~n$'m\a,oOz^}8%?t1"H_/×y9\阢oF4^y885quN/ ."\5wNDIj:c#l {v(';=ПN )"~gh_*`BC[`@AW㍂(wQ}@S}.n4{|4$98ץf3<.1\AE:{"!|iY24dT"N&gU([|4m1!̙L:Ůe8y/C1"mD:HN1pҁY޹,Y(k}{WUJblDi!I&Gϫ5ٷN!"V_x[^t2A: #`j(GKQS^eojvq#.>&!jiy$3W=ugxy|שM9^6E~R&/YM!{]i%IAKg wvB۽ ]cF:.`(~ AGi8 m/c ͤ˹χ:d=7^ٓƐM#jc>%W ~@ ȣQK4E0M 0tZI iJ?{,‘(CXe4 tMCu Ø [q^ d)L͊RG*V!Zhn1[:H7 e6$yIZ(G+!OL!WǧHl:.c^"0+ k¢V(r<%բдri<]711 Ѫ $Z%iD`l%x|(uc;(PCv("NMY8ބ.r,gRj. t єpW)a@^ [M$&>N뎦x|x|L,Lm6;$:::^VFY,9dxh&"mөߚ@#hݫ0^%۟ (g޵|^%P{9G9<>8u8sǝp"Vnڎ9`ܳ\|x"I8%YSA>t+&/#sYX"Alzrx|TP9Xl wp& "mH%FG4 ]5M5 5zV׬M^h:F\*qF6E81#FeLlxMMd#.2$T]޻Ξ>DD :BTӴcY{c8p39w۹dnأ^ͻY[ץpݽcߋөgi춏%IJ&P"%W񥍗RŒJ׍BieWUr1;wh<1> ;`]Fku'k `qz< Y$R4 l'f_ɴޭ@/!k:dž={Py-\>*U;('Q UUuq4ȥ#mϳmd $fޅ8\KbnC'ˑddgLJǤN8:2 (,-Y ˶#ysPsldxRz^EϫQB ؾ*Hki9MhPd(i=Uwlw}oy./^|+XτܭinDe0&%PH>*j. VJMNG{;/sLLJLJXK/CKU^x `vPǒe+il܎3:_y={SvIgHbNeeU_㻉<-?n]LbwtZ$!`2O`AY';A ܅sUuWQ (Bvj`4`(|>$p]'O$Cu͟Ǣ 7%l:hH7:F*&ˡi!%H,c\gvn13X FEI8Qk=a^6|h&m>ܶjm RNi!/Zdwj6 SycVr4 n}|h#,[L&E:trYݐ$e+W҆hZy?n]mp#N}䒉 ,&NÛhH_Z^%Nh!$s n}OǪR'PAcS^;/zk U%N< )tm:f3J$6Sf04ɪ9(Ld2I(d1 0 tRmBV͕i2 Zzq#&K][6Z9k>r?a\@Ĵ{G;o2:0iܸ{# {x|XhZ4qwskY~#eŽy~]䲹i\n1UÎaِc"[fvɭҥK,?4Xt)v;;vl|Ժ0 +jژ` s6(It=qDf[PU3OmxN/hеŢyPUov 1+6cm͈bIq rXLҪ0.NIDATn:.\ȩ$Iխa Iz .dݺu5Ӛ-pZda<+;c"*(}^+i ΞjrGk2,zfx|LD]{4׊??poxXP|>uC>yϪdpZ b;CzLRXya!⥗\apprq駑SUyG\zE|?*$J^I  D,9'S#="N~b_OW_Q~q-\NᔓNǟࡇehh(}==5Ӛ-pZ|G X?Ha,;xO>2 zt9W%{;n?=\}G8=D>D|LuM(>P&T#~qLk}E翗N>5YSO.8<.z{c|3hXԏ{!D8Z&# l>y|SCJ0о[ nQ71]!ڻy-!ӱ3S8< 6yLEڢ1~8(:W z7<{Ӥo`n}Ϣ@x!Pysk>F"tw0wPp \|mnڻ4; DA $[Y#( p|mn}{EA@  (JB %:m+QQ8]#mpWzpq'g ؽI 헮֮ϭyҾV3TҦ%hooE-~Bw`O,NEAi{5^[qJ<>ZP[GU>Jq &]BP+W( >χG${iJ>GUU{3͛7eP(DgW';vǦ ì^ںYF$;3(B! Nf<ThS葑DIDDg($SȊiY=ͨ#<>Z:Gn)LcG$! |$I躎d2r\pN @(;YQ@ ,ˈwhp8L>gΝ9ҜЦ,!|> AA`" d&)(8u(;bhKW IDQB] y4T I&WKcP(D4%E0`0H,C6l؀ ]+rP h%a42>'Gkvӛ3KQdYb fIRttte4QUuGut]G4dppN:UVŐeBİC[ֽR&Zbkb,/"CCCtuuٽ4vie$֫& 5\@(0 O<{!ڍbWVA@A l |BҢ!$vBx|2v LbymSb5(lڴ[{ng,%GګFeZMy;T}K4M4m (D:/}# 4 LI:Bk[J3UQh>ɾfsK$Ib׮]l޼\.W6No`bzmIѶay^yTUe pgswd2d2˷h/LR4;m" ~ su MG Y3[c?u>nQҋ fN1!t d'qh^uLGu| $fMju~'xr$ItsB" DW DUGM(/-@G۔HiLii #W(gy|TwNXe&G=ˑdiRc=IfhF6EUU46cҎ*uq(ݜtI(bhy; cccVFi;Kj5ic% ð{|4M# kr9;|-֢Ź6QhB@"H#ofnbCaFF' mz|)G5 tvvJ$Rt7c`X7?~o(NL$Hف ZdL&_a[X-H$B栆vOA& @Qٴ(޺i=\ |8j=i 3ooMiFA  #{*a5O?RBBQ |@!L&Ws9–pWmZRxPsY\sDkmaVmp۟BxmmqēTZ&,?nۯmMD]39Ž& ;KjZ.r (u mGm?n/P": Y5M#" iVL *%yڞ_ǡ_$+~׈XU˳yjƎ:km> m6:C+Qy hh"IT*EI4$E Kj<Ñ/mZˊ‘ygU[D(Lؗu;E]q@d2YuH`T "a ;9f^{ Iۓ*!86&G*^ Ռ޽;wU=/㎻;4~D}zَ5o|v~eӹM}ͷL+>Jܪi:|T}->}xZ|4:͔?v͌G1OJ9*Yɇˎ*GPV-[o:K;{}o})Tm;⼉apN.9 ~sö]|/q'f1ӗFvpe|8 3s>J|f .5XÜ}λʎ}|/_7`h]3urtwwH$XSgqNjKq4fq6>13ΔjiğQ?ԙغ\'O>Uݛ>u~Y^뛞h_]UQmo]o>DzkN:xIt|T9O=(;w\·k^>o~};pj>fQ󑫘t6K/]!ˏyEQ-<Dx2?%_eO} "}~Wn))4(䣨A'j`oyˉt>/XNjس{ʐMjT7S7.d\e(xc;<"y I뮻&<>1EtDC,%JuY9dR^}e4]A1e£S HbajP UU18ʋjjPx|`壶&ǚ`kP 5ṘcMZ£B 3}vҧ O]1m&6LȪ/yԽyD~F{CQMS8˹C/"lffsqrx||EӋVBysG"É`7s׾mpϲcfnmCdbՇB M }]A%_&Dž8J.xmfa&0R3]>=)t]/]mbV7 ӊuGg(D8C2eMy mvg ^9ͽHFՖiB)EԩѬF_Eu+Dg^Joa薺cO(DXm0v?(J!MZkQ=G_q4`y\1"GӼZ ZU L~!D|]E¡ϯ}4ZJ0Hp(TC㣈E,j.m7^K̿; cF{{kjPx|x|Mbvujikn{ih$BOOshPx|qQk֥"JEU\yncz-5gJL̩{h5 Q0K`ղcT5?Y"LxA"D\,Vo҈Z ͣ*[.;("gBŹ0L \xtnP9wO=xUHIGɞ=HVd21~=RWR{7j|*<֭g( "]b^C8##@ӧO(nÁN "Wy*2_ikcmR'(>0E&a Dy%t ?cf8*קZcH[幭{XTx)T'}\قYgX?U7~N`IENDB`muse-4.0.0/src/doc/pics/mixer_with_one_input_buttons.png000066400000000000000000000211711404105311000234700ustar00rootroot00000000000000PNG  IHDRب5sBITO pHYs+tEXtDescriptionWindow Class: muse2l1 tEXtTitleMixer A" IDATxyU}LϹ.{+ (G<@BxgQ_WA|bE9"x  & Sw(GRcy4MD5XqVȬіF|$mdw`1;(@(llʳRL,,;eQ67-LeՋRw>fw `O:kKVus\@:~ӷç'&d+Id%k}}`kϿS?9cƉ=;lMWcts%uz4O!68G+|/F[Y,aN(Ig{TVVq| ?u|Dizzܲlqpq _β? CoaY;2]cٲgXPLE{X$b\a5L:s(5Lޞ~ilDw]E #8ӓqblhe&%]e%k.h^q&X*宯; ˜+,pKxAt]BJ0~٧e6F-*Ҳ(B) `H{OeߟVfdw8WI >E+FY,&.nf˻zu USUMUeo?9\Ҋ*WY"t5\&b 1b3xZˬnkyA.E VDbI%k4[Ck%DBA4X> 9skȾ%N&[BJ0 AVӭ-aWu@{״w/~~nu7[_|P7<\z-K)i-uo)`k'VM3ƢmadtVx{ѵ`/gw f˨].)%g\oT*?G\ L]d|њUTUvlM-;#&cl?p|+-GQJi0@0IbFn{KAYbbZEg~ߎ*,_Z#W)p Ut^:M^ 0uzdo>a$1!`2Fid$q16{DׁQ zVblb)_uU#Dk/,,{HS?io41͚;8Ø3(D811aSNJ+kz;;cPBt_1=b),=wZf Oxb`՛W˜mG;.7se |0Pg@,9I5/yJ ^D(maX!H;8.34%s—E]t1) re4gq*#MK,|Ye*HܘTPT! fFPo-H[F#IHS'^a1UjdL*E $ xQq 3:pNA:A6tMY,&bUINt{ay g[bAiCM,{[,CHx'Q*1~URf|t]'DJA lB0wy*GXl hD!=+^J\GdיC% IΒ2ӥcU53Ll \''b( 3F !eKq$KK$DϮ|6Ez7|g~ٟ7{@cS^i|>R8tBOdGNA,^4U #WmG&5 ye4ڶ+bCTڱS$S~5C);9^r1VCkq  }ǺUM8bs,f`Ʀa!bVeЯ펌I d5P0M@$rBA큠retOXl}RcUbݼ |'nƤ"J4Utvޞc=Xl=+}7֤b)NS.pUBT>}Uow5w^܆Պow댆YKZ[ޓĤBDH2ހp$;'chjXQh@ yYiiiɁ-d8c̐܁#aqƲdL`X BD|3CǺw4նL^zFX ?Θ.vNmd1\UʘTdJt; |mGTM=%%WgIc|ڊ~O(& "sdw2~R aX2SY1 `KKJufw4Ld3eZX=t@X $\s_'0 E ~;8񮝺eRf,G,r)k*t/]SAy~Y%^5K>xb(Ņɒ<ϯz.2MUu|X5y&5~TcEry&-qҌʢOuF; KR lp"?KJV̉zǭIg_'f;վmm7z1J?^}֞h-͑r͒bd˜-s@뉖9+Zdٲv8^ܪ۶Ed)mmW>* ]fItuqЈuBjܰ0e1nxٕMMM]s5ec(]suSS3Ϯ<|#&7#%QUb {H8,e1 fiœOz³K`Ǵ==5sN[^Y r!j&,%ojxb-x1ʢr͒}5>XB&njYk~8n|-ż!i4 ƓOR,L>ɿ}<3~Y}ILbfrk= U;Hh̰";?kHS5褩[n+--MdSNs̟cƘ,۶ȾRl,Sj/&GU<`#ZpHD^ejH%a]{?vp9sLy8qdbN0nB`(ڱcGQ%oX̒W?3d>068c@@c!@0`21p$l61Ƅcv:utt?%o9K,3c0b\$9΋N`cُz..B1qV!Q]. E&u5|Z>Kq1`#?ئ ?+ 9ݭ.cm>Byl9FvoOց2LgLgTgP`W7a `DQl;v0n6l3HcI:MY,ʒ v !;Z E !",Lq`[RMݫ{{{ .x5MSEQf\Yc0b!@]ULt`: T0ұB)UUu̙?x Չі "Kf!@?Qf9&{A,L0$LNhN~jE͜9p|P[[kܾB4M4͌}YPY+QJT<Ɂxr몮ڰupM`apI餔۷5B!j{?ܝS-oΛRXh+n~.Kj_}@շۏf_'p 竟 sQs[KlEƤҚ`[,U _ĉlyK}M5mK03Ũv0(铋~,ʝDį~tT5=w-g2^wWۓ4Q]wr8ѹfDZ,TLeWyEM 5ϹG +}3;~tqsa(Ieb)~^ =?8]~>x_[{~`ĵoX矍59Ϙ݉VXy=zHGǑai/U/pWzo#oβo--{W,t+5SOǦjGs,"a1c0УٖGlߵoǵLkwqˮ8^yuC߹ͪNStuv,y'ʫ k`7ޣ)O9,] o?z kN /.BG~z;|s a_YY)6s^x pQ~є-g1HV1luB_{˩{ O7?z`"?/xg%΍nNC/{q{koM6S_{ږR,&ƌ~(=}G 0N)KaD=ׯb}җN7ֽwĚhWc$v)}D,bcl1)nOSV!GkqUb{_N`ߌj&4ZxM>Ѷ,}Dr/:/&v]WI|D,"d1UuBs6)7\SD'$RŲ2Q;,h}BTUW0q]qsX XQMi۽}b.2W"Pwۃ !`433bY\xSl|\}b(DP<Ŭ\:1(2vɘ]pUg6ʏTNn\7ζb`m/&SZH;G -n ,~lmǭzO|{_G\lhϿX(XPs |~ aLz?+s˗NuӶ+/ƶba w(dPl>2Hz32DEbe#(Dd։;tJ)%Z}r5G#af(%  M8%}{.ڰ Sν=dR€1J(sqe%ޅ2ŘzyQTG= pE6wQFኻo0;e$} }D,8*4 FbA;۷٤ "nwرpX X.CaS >oڷ:Q;@ooϖoq#bKјkt^?Hэ׍!r\UUU5I|D,(%GYIDAT[1~{ȵLX,yUj!b}kG$B JbƿQR ++2yR%=_\cۋM'gD%k!r #w |گ>:+8bQAO4a0: b]ws 7MQXQZ z ^Yũ(tǯ9ʣ۫7 ƻ EHA֝9+e1#KS*EKйKHc@iigR"m:#OӶ]6CPRx_[\%%`](`O*\,}(J]~AeNLy 4~vPe鮀M☈@}jiJ3yɽ[y(w?pt!v{͍=Z@,eG('m`ci=mKl6@p HϊtsVe;vr;NvN IENDB`muse-4.0.0/src/doc/pics/muse2.png000066400000000000000000002765711404105311000165260ustar00rootroot00000000000000PNG  IHDRN%sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE - [MusE: Arranger] IDATxw|UǿR& 4)U:(&v]W]^pk[u ]tQwD@zo@(IHf2ɴL{<Dy=wέsy 2qVg)MiJSҔ4)MiJSҔ85vŽmp|A}lv;v[4ȖX'e݂MQܚc?ML%c8dfruH܊nvZ^Q[DcjG>}F ɕUmr1c?jKJ)M,[8CTzym[4g~7!\tP Mi:dfuj ac]eJZYB[KY*oEaHc;ƐLГ :ccO24CƓD %gdkj͜sQKF:04iqS]{y6'@QU B fiJS}? NRUb*f&"&$F䢺j`yH[1(O*c56&4[c%v$+7Ԩh<#WUy:K?,M9ŕSErJxș`=}1͠>P"( H i[Lьԅj=R:(cOz`gdg>xpeK9UӦ8p( 0 \eR$I~*+}HN'~?>?? |lGk.S?2e%S(2js* Y;5`^s,A􀁦d'K#]πAeO1 ѱ ODHzZ YiVu^XZER4qkҮxtd}cav!>鱎B"n/ ۡN|MIYd4Bڹs$C.dOI +g~śYS1RE~?JJ9q\ivj5lHfffJ4|ر׹[]VJt [(`0pSa1@OEYe:νYA)yq`$P)6yvDavjDVVjc05vH@G۳[}l%(47Yna 8@T(y眧#HӯAG'`u?Ț YGvTM|${'@uڄ/K yN;tdhaIGjYW9JdM0PxNAF$jp x0zyf1c5L \2#4!$AY4!zsy~_12}֙T¬:uGkTTx͋<Q#*_%Nl7KiJo~c]e"U[m 6)oMjicTA EٛF<"CKp5DaR uz*Szq˅@csj(U++fpEǎt؁.;йc:w+3=#PNU;ز# .ܝ}ULX/BZzv4쟁z .k"IX8wݕv$0`0sܙC.l66mL'#SlܴѧMkǮMJDI!ט*/?iUҸqcJJ!IRLSu(YL2X3͚WߪkcޛEDxNy@(xnSWAK|< xzb]P;*Պagwc#rQ!ju%`P0,h'`H:8'><ߙh+d\,0Dr!bR)?ZElt!N%.ZKYCC TWQ" +giOI;u 池Kd]_(4\}>mY*T kп0N/9cc8MimOcv**~am)f&qe{þ{ذayQ|<_xvtꚞpQ?ہxStԑM6q&N}]ȮZKqÔᢺZٹc&&+3r)~[Eʣ۠CZL.nS!ݓ 5%{<Br?a֣<~qR|?:Q_CJシ#i䪾W0ʾMY:ѵS'4%B@w) S2M/dd OW9xgKL^|/-TLޗ2Bh[ӣ=:O:/P#N&>^݌r7~̂HQ^z4N('t@Ᏺ~?#ХcojB |+xԑ.:ңZ^^R.]+cGѻs'vΐ '_,P2g(wtz/-=5v {t.]8iY&e(F6tc(`6ɼLvJ #|3"^`RGUI2>QG'tDt3u ЖAk uHHӯAz> ;(/'Pr?H'5w%Ǐq㏙8abvVf&?M6qWd>S׫,0O?@~^L'}6`M 7lDHճtTQZۀ 8YG 3@H@0I .P~~?R8L0btJUÖJxBX!!K:9<, 5U{t"Yf|h1|6Bͣm˧1~\~sQ{'/)H<%xP`AyG'ϺcD@[8k L\ B/>IQlDQqrƄ߸įO$`е͊9dƛVV™۞M՛şelQ!? .ŗ$~JeDWՏ?'|=V80C4Cvq^|}> \]-ydF6nZ'W{l TŒWW~cy#ea|3;M_G"tVSڔ%aX$曆Oe1^T >yjhz r՜]dm K%_+ TuK2,]<v݅).u @H)1NtmI~r￟@0{,%T)Sx7x:t(|!| ~YWUҳWoGדN)DYYEZnG*R0&?U@I:u:u%s0K~ѻ]7PH KNO'j!bj[tcYNlc miC>T<:|uu BmaSۨ˧SN[%*MQ>ĔrCcT/Y=]n=݊NlGx؛GVLmwnb B!41>f}F#ĕO ڱ G-gQmG ȩydE^.]yPWY#_\cPLgm|Ƥ)κ<:'Int2h65m_ H[/F uS:URü;ԯ_kP8>WI'Uc4o֌۶_> {q/ZW'mV;`CG kޙ /rsNtԉ6aFNlSYW=h'Evv6"I!~޹Z'IdggiJo"Rٱ!dHVIMڥ|*2~k `ȧkζ^çk+c>a'TGt,eKDxDh@M"4d$[lLJW$?@c-{Y_5d{K1.[@t+S$[p;pc'c]X$B*UHȟ b/3C6x${%UokOV\- C |noP-󻳙NyZNjnn?$/3tM]/IN]rrAQJ KaP(b!_矲P]P8em=7Nz >aSS`PwTWVTfͼ[w*{vpYJ>ŗ\7; ۻlZc\>Lcv^{=%l@NT,9 ;afϚ)z6Ꜭ# Yf=폱d;Xv-+L|OEp@&kr+9nxÙ _$K7Su!t3EVE2w,:0u$sd tCTVKH4[zdbz<6 )#(EՂɗ( Hņadž454<] zwN6*"#{θ'_^D[Π]T"9=m/P sc ^ν^MsZT3ٽse)z۷lb w]lٸ}Ͽa-:'ΠASvo7h_VyaA:wذ|TSБp)((wZ'uS^nȟW?eVqSPPaHS~$ƻ'GR J(kS^2R _ګ3,,:p2EVEMMQ(2A=X!˵.BDyt0d@bV;46=>yp ZRIB-' wYAG@Q-;1j_{-,u3vI\[u<o|nn>n+>û Mؤ+6"1Bx ?l#~@qqwO%n`6N}sy/㖾0 *1ۙ6}ɉ*<7I U=p3VoOy{xaVv?g-ި)a(Wu @Nky%~u:Ug2U0&h,#ϣ`&5 ՀnE >XtP M'2IVIWO~Trp1 8xHے.|qi')_(޵9_ N X7He2Hʘ9B|cy=l_a6{ľ YY8y@PBddgˇ# úVsٌdde Ց j[lN\.챘""Ȍ-B! a:N& ; @P)R"f$)qf: e9ȐJvg&.{@}bf]W#}ymq\_?ȵoqeftD0E83dfgm5~s(P7G҉hj{u[X6(j:(I6ЀoMGiIc!Ջ'QP4moe /lټҒRڵkG`q1vaA;te Ju DM:qb%=JAH:@NhxvnkOi.*BUھ7>8q#G)oM ^KW]kA~Q}!CxgsmX@?^\ HY[g:`@@*f>%4]:>o;Oᣇs6SC3/dB!JKػoxBNrhҴ ͎oNAANg)M}:1_yQ>~a5H3 V%QT5 x4P\f)NWUNZdT;FG,2p9" `XٜddG8D?ID1-VN$XbHcI0*o "HSH۶l`ؼc?Ih8묔*&!`JUB!N'YYq\7iJAj5pjsdSIkPkB ^k dG6` Ԃ($S:Z',C8h,Ȥ8ꁀ(O29Q:̔HHR5piJSJt$}c;h׾3?P#fAFF_iJP/&D M"Fls`ӽ4JG!1o\,)Eq^&4 ,SëŒ|쥅Uc/TF0P T؄h0r^j@cqS;MMJg|Kd2SScm{n7RVg \@}t <1 'njde@ u伺ڪ4ӑIy00)4)MEP6l9SN5Ⱞ%#Z}*>| IDATVmK_7R76*@Q%ʟ7.X蟚 _BE\Ro*( d=y"?8)Sb':%ฃXsl!(`OөU~SlXE+`N 5_) P[t@ MiMS47K (X uG&B^{Sꕻ"{:76yEE f§"d:'⼪W^Xl M}wxUwIT6ȩmZy=:)9} 层TD䥵Bu +]do,oo"\G"et@芚&u)MqNt` MiS,@Lt:)LM^N6 _Klhڒ bW R4M$}i6}i@ůXR (TPMt*i2~˧C4\*0;W r<a;f>`2),UK5Rp-H_ ^ߚofhݹ 3T$'S'cqISTSJҔ;d9}d-(oBe 5D*N'' O (1N$ (dԆu ~86 n2Y+VX>ZbTnA \ Z@`t0*{ em:k{)MQ$p8L0-w\8v{Ry!!ݎfK*_8N-(y;ȝڔ5/<ȀC95sU&ASLdN\P 'ZWiCgO$P:?)`_#00 $|ә9)!v:`}R~:5:A1^FIy/&1Lrݺ &?3^Tid誗iNbE.E+Ӳ H c(^2ׯ⤱[N)4"Y7=Y&3YtmeWgf0`R0,ӪG/qZ~.210HNU.Q,7+R\Ҕ? ӼEK^O[\#kVrrrir|sB_a4kNvNwndgP(Ĵ6ʼns>MiCQl3Km {45[tw)bq6VjMUz%R*67ƢޚMnXБZղflrr5q狼 Ld;#NkS-dע$v*j?RЩ Ld Fx0j56!?zk9Q-4d[)h3oi+yVtX^aB,MiJ0ee~$)lj)-+#]NPrsJՋz θڎNnǸl sO+o`ډʅQXD&h:$$KOPAґȶGjB~Le <1ܷأZ uWʖ鈷;^R,,bcgk)x cQ­uUƗ|ნsXʟx3ǔi~,}lFZG@Rn}>Ny>Jrz82/]m'mS7Xx# ^Tg*疑73 ;GFj>}|?zt/^KZ TZ]qnFWr~-?k?ΉK${Ygt8ct @Uw0G1%7m⫴9ҡyb֕6@;I'+>+ˊlJXڏiφOQrh u~@r1A*  _h(+:JH%,dOp:D_Ux>Ea#0 ͫ r]FL\ g0lbn 5GNI?SKryf%2s#9lߠs`\ NCھ=TkAG]ں-i/pFĤ7 KbS_zA{>foM;y4~YG//z]3?+7ϭaI_Npڧ)[c\P\4Ӵ@+5́CiO:4Քpm6\N&N`,Z~O'qac8RX˓O=cFٙ2m>!PTI7XT2i]؃?QW,Z˨Gn1R?ͫam5%kߟHٹqǙ]hM~tp3s 91 ZSxG&( '!yg_݃=|Vj 9Ѱ#HI$p_)_.&R<=֙ lSNmnhL&ғ|̽!cCPTd '"N=kgɪ54듞Nխ@Fݗu1 y.##%<ɼչy䭞Ǯ؝p f}D[,S $@d1i!̘i;U6=;Tf_ׄsEz.v;Y9d UzBp%9.a/!v209ٌ0o Pi,RPfjNhؒ* : MQVT 0^| Cilܦ)Mi1G.WN˯p-7z4h0^v. WF 1p-7qRדعs3,Dւa; 7zT۫V2 &+-ꬥM1Ky>bjU:fq⁳bu8¶)Ku]g^kQTgYL(V??/"F^>X]AC*[;w]̨\pk,- 't sQ3.wFl])粶\! ;z+XV!k{ERk'~7jꍼ||+7U.)gVG>^2Uu5 ,?-՜a?K՛g+yݷ{f]]w%g3qiBl%*Mdt: *]gݮ[o'rm9^. .z=%m{*II 1b/.Z@շ~f7^N*Tx1ma V{@eKB DU񏋊8_?Nz}g.7 Qx<xpcQstvY ۏQ׽ȑ?߆^‹eE}g LG 6$1:)Hz`e{0w1KEoZH_dowdl̉<90^x\V؇pԽq‡5#9f* II"\Wo<0Yx(^3wws{!$׹伧Y_`3wÐnJ DtCMcCѭWD (CIu`ߴ9O_1Ed >t6}F&|`tH{?7++:˖п_?NaIJcY%|5v ?6>b+I^`ǘ4x^^{y)GɍU.'x>3-:hu9Ȝ ^ePu'~)CB8'^gB ɤߍ"xY W!Y>9|1]J}MckU#N֜sUg6|8}!"Fh3&:o}Jo݌i!23[1EʐDˣt/0jOC@Fa utVo ?!SaٸYI,Cw}&ˢ=-Ϊ |9ƈsړE'5I &,$<ͷSgcZ"*XӬ_Ϟ+S!)ջ߷/unȠ#}tg܃x'ۓ'AD9yfʼv=\&/)@{{77f3n,}qzq;̚?ޜj`^$=zČ I#EO=2uL8w]HEۘW>Ǿ×t>RŒ <3xD 26Z3w5 W !]=tEgʼ"tobKd̥\Lul?2̛?d΢yu:ʜ<=:槿̠ƃyI~x&֯ۘ_1|1/ǞG yY8Ƿ{{a/:ۼKy?3h|̙o]7M>0:O;z {)6$/^̿}xu^) z~ 'iK L4iQ`'s2d 4geqmW0j2_lUϢݐiϵ*{I_4X깇8qh;2Iuo4Ղb R%k'1ƿ9kpd7gpUY[&ulm+V|ʅ-Gqfb>]Q#;Gn+.Mfb^Wqi{9kfZGhT$/EgG r4g/e岽zo4ά)؆\}z:۪@Y'rhE>4+/27G$,@s㧓ydD 9|:a|aөvBu)&2' "g VEYei΢iQuE{3K1ʦ읹*5ixEFk(WqvZJha:qe"ܶm\UcNĝ݂W_L]mQkQ4 9lHlʢ]쟵P+WrQd%RV:HdfbI2cS&k|tP?QՒd^Og66~4̨3v9a&ONQy=yb/QFI7kK?z/+}Q˂lVqӌS ٵl݊_fuK<'28vfe`U&+ ъ]3k2S!k|٭tԝyzSTh+=x||ϗi#6f,DxNBʙ6uWeOW:/6 n6J K#^K=s8`@tDpPPЀ@ϴoPQQIۯ ((hPOz2*oъl7~=_O,ho7Y!gǔ'S]7Eiа1n{*C9Pˡ+y<{O*BpT{9XEߜH#8OSH۳i {8PiJnJJ\ˊ`28Sf9s{?%dk_^xXx!\rrwF籽4̼_Z1Sd}0ulsS gN&n6r;ٮyl9b~q+F1wSٰ{ZR1Wk!YkoUm`g״(䡸I(^댶{y+n:^sZ@ B_%=8f'06ޞEsEǾ[ S["roN }NŢ8V3i,۸ֱ;ӣ޻\ԋUf6ΪD@mSH #h;n[KC}û#g4⇍3ּUM=ʢ8%s!_cu*2l#sNm͘{f24dō͕x'oH~"W*cOYk6B8i^8?⇝g"7E)Ee$lixk0\k%u:O~L= [!\'|+xuE-{i2>zw{6 rT*`*ʹ#85#*jι>3w~Hގǿu'chޛ7?,̃<K$l@-._ 0HZNGiW:4MrݜvͬY3(//_>ef(I<{ȱ l0 ~/`6Ji7lm\]"vry~_OwǼR!Y\͇1WFdBR蜍4bӷEl~PG8VU˸xF$"O4DdkܓGG{J r_nJ|H U"ˮ2=ʼn>Z^6O}}?̾xҚX^{3L RU㰗CZ+UmB={056y f @:4nOVVM4@vS^YYYeeyڴnc(_8U ==^BJ/.v"3ZSحoو%}8-' [5e:#Z}g30d?+gc\|l `^ \Eػ| NX(WzNE#|w6v_idP]dm FAlTr<-KPHw:Zݬ- ? \ 9o&K?i{&.G͔֗*)^O>NMKg5̶aƬT١7kCm9|w`dzO @T'>XB={gdG"տnJ21;nh-]$dG,نL:6N>c[vyG@`7Sg^J<'i_ϔ:e;dv0cQ1~~)[c-E=ʙ<,)۪"20af/Ie?.e3dfdtk+""ξyJYzpY)_o' d3}Q1~/,IYg {5ejDfv-gx>,]0NȔyu;]e\p?ѣA =#6Tj ^EI?؁7-úfT"]:KJ\+ɇAJ)Fb˂-4֓qo0FȾ+ދ>ˮ30g9_o^~[eYm032cxMG#zw2d֝WCzܓ x{|z4qRJ!i\w6cM>o G)s ¼ ǘl?ſQ7_IћɪKTƒS{6" EK_ٗ L{5(GUϚ%#U)eD)!GU;f0K.?FDK

ُ<2 ̘!;>i:ww;d\Z'ԋI;# g2җi;#(]$_ce]cG8‡ ϥ0~y&L{r}6L]IVmHRĘ\ןE3\vF?kδDOѶg>((hȚt>9^̜-Cez&zd4) 0?=27pI\vFaG>qc}8v'+뾐̘{n#g 6i23~N!Y;ƾL-oU߄A+zHm˦<#lB2g~g93]W? zj+9s3KxdHw:&0[w/;:Dn{T^R3@2}\[tl[־*tCDCb& ֠) g=j{P7VDLX,u򅨻Ɏt {:1&& u1<$14 YW[*$ @.oMJ-R~i qsſ aMvox1m/rWѕ@:ЁXǛ WtcO^v5O_q}BiHD܇fSMX`k:J4e8nyQmQn$w#ȏ"fTQlEF3y-i$慉oINRmo<fVd(E9,2?!%caSxY-Tܖv)]ѢYLSQB"Mu@W iSE=ͷߴ)3AKyoCPnm$?}-D҆\N$=A'j9բ`9S[N÷VcϖJBO`)-J_zU^H]eP^n)t\5変#&nf2BhȈ[D QQv cBKs=~|_. Ri椠]\hhH K&NrhڵQ3Q4yJf։ ~? QS5;PbIV:ЁSK7)7ɥÕ _E%.M$%!va syAX|\ |ErHے `mjJDqOh21/ANJSW\d&zy}á U:ЁhUA?6oƫl9"+ }[jji]QKAE:,@M[ؤkh&m@Ğhj5ޖ-r,)1\Ii$ܞEC{*T}t?oJЁt"uNeA8&*L@9$5PKDsZo-n kSedX ĕvG.h]OA@bAb%NNA4aN {" hم!q k Sfi~b)Z|%`ߪ+N!WAzhڨYPt@k$"uF9YyK;Q_W4@EA_gM2|2doWD7)U ]I(.,{Ϟa߃R~G$%%1b(fumҒmqj5YY~ڮ\~@II ?3W\%l\]#pXAuC$ = te-^F.Fހ`6N%@Y1oċ=GcA҂lڰi3#55d8 ԗhe-//͛HOb&jGI 7 Qw[H3 a:26nXߎ\ 26og.Jظ=>_7gúLy>As-8yiGN@EYtrYnX%@S+:,lFUu5yV(@| ǃ( 8rJᐪ~L.6M]]=99ت*OHF:$I3%ZY~-VNƄ9N**+ihh`g'DfڎvU?+8n $srUZ,ؙ8yR _WnWI'nիۑkĵ꿈DI'!I6[ 99Z>ОXIaF#h :,l];w2pRNvvN:lۻӆ%%@{鼱p+F!55[UeTsǃg .vVؾYW%TNbRDRurQYYn~(($'&Kyq$I"%%$%%tRnwP\Zj[ i)&'=+ 9|mMDGRmȠS+ZI޽fGv\jlU͓;jkzNEA$MU2W!?͎V!%KEo9ADQL@=d40#IzwB6oANjL&hhSUt`0i ?JRcH޾ˊP{ w`[Ѡj!It!9æMphϫw(:3Jף54,6qAdٔ `<UKo{?~}NF AйHRr+z~OӃh'JZsbNX̋So1dP>Ӱ,V+f뉵*A\F)B ޑV SƑUdrfTJ+WLZ{k9Gݸ h| YQ2x0?l?Lrlb24RL-PjFAac*W[ܮnp9-rC8@SVyp IDAT_`XիW@y8,"f޽zQQY;zV5jjN'S>* 233ٽg7ܜ>沆ÉVc0v\9jwpXVX%$H9 +2SNeۏ[9Rqݱc'SN壏>hp(*.®|n¢"r=ʣ=SSش:ēছPYY{żҳG+fa9}!I(.*{=HKK |&U(\e[xz4N(xE-7u$Pb/` ( g̘1rs;өҹ[IҠ]CjڦrKR* =^p Az=թ,'i/Cͨ *w>j=#Ѡ5  uxJb4C4.^hDà~8묳سgO M<;V6Q^ }T ?'5qIOOoQ)ZF&rJN7'7Ï6h4xEQoؿF3gdBE\.%%%|7|rEE̘>}Vu6s$h\7m1Ɵ5/VsAt:ӦM ˵j*[R,iXUT\x<72^Yz3cUE RPgUseB$Mg{Of2_/ffzBx7ُe3v׻Amcw84L_vm>-mMOK@A~>;ow/7I;x< FV5蛛S"'Z9зo߀ro߾[ƵORv[,8\7;}ZB9|ERU߳K4$I˫Q&ݲп?ytC}3ˁy'HIIF$kfAN߹]ݳ[:ش^uб OPДxE޷LQEPu@L1"³L 1k>PQr} 0jx5h4jq].?^PUZ t`4 *Qa!Ѩ9NJ;,h(m=hM4u1X"Z:BMLQ;?y]nZwO1PӔ+2YzzzȲ(!ߊ~3T~k\̏ (jFr6:gv圩S/bæJJNl63y҄V/'_ȱJV[q|wŞ={et>dY&33\>KV׮ ;.<āb:p32Я)]]w>zԩSr}gݻ. ˪"`Ɂoю$s7ތ[ >f܈A|(wG7_?5q?e}o5JsX`ҕ"V?}3wכKocgd(%|[qNh87"H~rbǗ+?Fx:g֍sgDXnO-BnU>}~#n~ŀFlEQb¡q'*E=ST~ ( 䡾 9jGAb41hZRSӘ1y (/̣8xHL͎q}w)gW~9qn$IH:ռ7hN[r@׳a&jjj"{Q[~¶o%(|\f]S>X.72 ~ܽ*/&:wԍ{62olbUŢEgfk'/c*iWOU>7f´m;M(i]qȉ[l O^Z^Z={ڻD=O.h4$] l%{Xa[pw3m Ulmkfpq˰cQ_+5;|~IɘF9,24+sBP|eY{%["j ,FR,FRR,\/^j1 m?C{mHb/~HRmp8T}M ***8ZPHEeP1 ?~<|G#INjjjj;dddD}]=n*uH@YUʁVСCɧ$ߙ0q"?deeqɥ3rr٨蝝͗_$ NKB7bj1R\^GEU8qWPUy^}.gOwߣj>S.2Fr_^ye\9vtҹe!a4"raQ$K"[U^)kq3CR`ǎԠl\Vzwˇ{}ETWB]_6ɤZgTE@}=Z 4uɁa`BY`%h+A7hEǒ&{osXC˱5(a0\}/V^9^yzkhHpC3'7eWǕfQ3B Ս&*$IѸ%f0OaϞ=Aeґ +  Z% ǜٻ.ty+DQdǎw'%%kV{d2a0ظq#˗/X|7iv(ʕ+Y|9:\67G᪭`xe08p/;v@$\3gZ- ߮`\j>-jjkk+&/7lݺ5\aa!-?qx65X/9w\qkl}C%wq2ro|;.>ӯO w]˥wȆѱl{_fsuǥYb`aOsԀ'[$([®e6?=0n%m}rbߞy+- kK1L<['8!7P_i-o!՜FlFj@%zЙ:#(84I5HrR)a٨t0vX&7;HW H8\^ W܁MpTŀ,IȒg'")' -Gl2fN dvdL4 لr$F# **+ux^^/̙3;v,99Fh5x@D%r  H(W8_Kҷ_?Mؾe Z>9m3yyLrS T Wǂ h.=5UrvaqEG {eLW_= Xh'/s/2O=ɓpN,^Ƣ^ $G@izjksn rqfxJ[',uuum.WiiOհvGÿz!\:mPSy̿A; }A!vz[^fW_Œ}VX޽{)++8OdddP)d\WfժUSSSʕ+y7ٿ_H.n:<6B}Yt:^ziJˎ5*))Buvc .Wiٱ6 %?Nuu5~)۶m{am(Wi ͷcDA+SReү(Wzz:e-ʏ/X\\̶mИ{R`/}*:&~W>+9kNZ: ?[9c_;^V HVݿ|XzX8k ٺ X\zz[gO&õq4Y7IzcщGj8i5e景N$DV&Զ']Q"v%xts'aV%([!*4ShV&PO \*F&diR67l U_-Y71T7:]2-ծVYhQQQ &p8RVH,mo.~>Mmu_9p*b/@).*f5GVVɗ% ,vt:-ZgЇVF ).Q?_ՠ!Lc&SӸO"obz_~9>x@)P%;)7ݘnIXS;Պ$I YYQ4b5a1hpwAr-b(\.|1COƅ^ʕ+jL8Eq2-lA~q")VRa~+ >յy,k-jۮ"ihXV%Hxm~5SoC] )~7x}`o0 ]b j$ t3 ԭ%ib砚'TaEFѧ 4z>F#!r+^^nTnjׂ Vz^y+ NHLq-D&o~{n˜o歷ٳԘ=:ѵKWxeNG5jg…a2P|eeeq/ڔ .rGZZf͢.'KQL&_Yd iii{\yH>puAQqI^`0`Z޽;f9k4o6Yf6+s& |<Ͻ $S %ysCu1zAq8Q)C4/I<߽ BXAvf3iFuNDɔ%q]Pp7=<Ƭn}7f)y^HJѵiw*Zڤ8ĈفG+`Ȑ!,}㟭F+6,2 x0곖_TŚf IDAT8DPc24,(HR+j~MsS֮]Kaa bXdUUjԅ@=X #Q&^*+рWVyH w4Ӂd 2 :F X( = ~$Z蓓<ҧp0`^w) F`00gNn 'QW0 W U] _9`ԃ\(qѴ)9r$z} ϴs{wcAm8á[,vEYYYYY,@RRo7nGe***l),\q444 /0{v..rRRRX`sm3WΝٗ?j(nz=͸_pRgSHJJ`ڴim)W׮]ؽgOWƵKp)<:v> ߯D+q|zf/# /&yia>\qsOaw8_KP870.TAһs1KWƧ[ rAvex/=7jva 4ZZCrbăz[{@zz:r͸NVߑD!hM]4 .a,v;ǏTcbZi P[߱@L㜑co^A5:읝͚0aℐ YˣÎAƭA>U] /M<E޽T{^;|eɦ(VF9Npi,+4Z_$\Cu\n –W^`00d2Dﲺg67[/gpjYSKT˅00w^$QoNc?= /#+ywIPW%c.{ #}-T3ϔ3rLt/zξg_߸NWr+-Hߖĉ#FgsY+6VJJK/qam3m b~ظaU[U"rrr$yy4גݻw3겢eT_I ,9EQEp,xv;?m ޽;'IVSQހTe<_ d5p0BzzF/@heQ@(l6ٽ1[q[''݁(![Eg$qxV@k_)p3Vg??.$`ѹsg<K/hwӍm-*.λprg%7__T_=k'o~rsrb BztMlTٝ{^eԸ4׳o 2bVr׹`EL8W_}zKcy൥c6yX9mr+H4by=45 Q@ h  hfs P\<E95'$ISQQ?EQ I ~90-D^tۭ!,.}KNG}xiePt1j 4*}3QQ @F5=S+6@ .paF/v"{/'-͢<`u= o\?'E4*lk{ I2fL߷fl6ۥo~ESh23221}˖-kd2\]El63o<-[ơCT?8+ ˲h7.QfmJKKeoæ\3O#-5kܹ iiiL4ϿXٳѣ̺|Z䊧/;c84CH__g wF"ex\@>~&}4^i6~ &1?3'HCm@cd2@!7iѸo_wH’._"ɭ,6+rhZ3go~$g1 /X9S5K뷖?!c?IU 8>}0db@e|sSUUA^j[ :TWH?/>RȾbwDꕤb+I=ndY͆X4x=VCӶSc~X DB%hQYYBdD$n@R cO? Qddd0X(у7Ђ˯0~8zniyl,`]tק_SEM=ëK| e\€9MVɶ|ǜ5f,&MW_e=ͬ 6 -Zy_^u%fK$bytZJ\SAp ^z+Չ"*2!> uu .`4- o}fs%`@U[5n_&Q4MS'ume &b`` ݷ?η5E*ЍV0~+06QF#Au 9#Сu8gk 1t|H}ysquب>VvEn7O?&qf4.\ȼsxv I:>sƍClڴn6KM_z%:iC2nV뿕kԈ9z4f3n|'\GɁزe ̝;7,ŋg!}f˂O6NMRV׫Љ@bͯI ͖1M33V qn7Ϲk.l:#?[;G(H74erՕ/!};x Ǐѥs&Lm2zo5:q!YA{[i Am2څhZ<Gl!u8vEA:]# [zH 0s&Q{uu7w/nZ,e\sh*۵V+!25uJF{uuЧ6ZH$?x=~\ V(rqpFC+•47tP47jԕ bI P; ^пmĥ 4(* A`_*A8ֶΙ( |g7q%0}vv ]u7裏LVV(RZZfS/ƶ?_%\~lq֬e09}gy{^\}6n|?NRR]vEEʨh4rх0vYy7U {3J@{oe,*A8p"**(?A(QP=dϲ*-I3G6I4is99s=x ˗_I_F ']QN=&[#5 ߯ȶd/`VÕK /@Ov㹩%Daxn&M۶.- W.]-oBn~v'^mҷ"‹d"%9,&*G',XKJȲu&l oHލUbe;wK.ѨITTgUV, W\!99łAً=%~߲LG]N",,LBCBxwiڴl>L͘ #44`VWN͛3HĬ_~z_?}o, [Y\T&o'bxjt4Cʮݻ^θT_^~?W|X3SLP Uh K d3 K Q ImD1AG`Ζ!--k"Sg6'OVGTFRՂFc 5*+'f",-ƿmRie+} +`0k@p&@"plMCºEui Pe1y3r[mnٌhd2u .PURY/l)xԶI^b9FvSl{R__:oOժU+RYXg h>N$wJƍسg/Ο ֤siӚ*QQŎ[ ա][׭ˁCHtDjTFi޴E 4ڥ3 6`}ԡ=[*,o~F=ȌUC̙0.x3w0g\{Sn.9Pn۫6k[*Gzs R픊 (CJ%\lflXK%4r6N4P ddf_c s nO$X,PZboێ=wb]#nim1)o$!I $DѻcsrrHJJ";;ȼڗ̌ rlSD%kccb*GK약dU(ڵZP<.e9a:,ѻt;@qe26PumJ>JDWDF7s5!"_Q19 V)6QK{ũ[||\̉~ӯCAAA.MrETe&VD@JP&?fа0Je~dDX0 FMP#ޤ<p@wk4Z4$;GC (dYvxz}zWSN+DX!8ǰ/,ARw<"<ᰔ@E` %:(}kIW%ZMtt4AZ-9_J"VU",A!I Lf#*ڶǔ_<"]J :?h hGps7Uٶ^A4KU*""{\1 `pq@pQ@%*a GFopMh69g;O @ ^:":r,H @ 7"^:2K bj֤o{>@ @PqW_2Gm%A$ YQ*t Ԉg @ |n{. :m%AEEe%^Lmaa9 łlhq:ZRDQ]7 ws+ Xd [ێ1LquIv4$`4QԎF- 0겎(Aؿ@ x}=\P>m%פ\Ή'%AAAy,R/s1EADFD:/9O  .(uL&5hHNjR~Æ>xɄJU6'p1dYF.YQ >1 ƍY&io)?9A嗊A?БgRS8r-Z&RW\OI4mҘp;KVwǡV&ii4iXf qDU\qHMMC4m֢dp=%CЬy3"|(+ʃ@G_E~!#$Mu4߀WdeVGTqZl{( @%ĔAc6III!XC.f NGJJ aӴj݆z*NA YT*N8NV::"CYۿ|(%PLG^:R_JfmKΝ]9ÉcG|7d=8c~JiSPX,l,ú@C<9K;)$qq  xN'6$>ș[y+SlA̭L~B8'CJȔv}h4}*B>*N~)yAΔty&ظ:@Ppn+Z.߽2A&㟩 s}ͥcg՗8xK/fر>a<%g2nULYmfSp$n?57Ywc3F|Mcu[9cɪxt"Xk`O_%vX[+>b-?#>G<Ye0X)˲EX{ݲ{{N5[gą3/Wyr}3zfCzY5 =_X֯z-{}7m(ğ_wضglxd̲l!l}6^1;anfﱫ,t3?~?v9"!]x{MWxW`Xo{ҤV,quZ6^*%_s^W'y֟ kSXjcy콯Яq_В,W~pMckuaї-)WNW64onBB+E s+SW0;ns:#s'5RҤKJPqБ7hoTIP=0]`I./?Ĝٳiۮ;vu6~u~]_\ٱ]eBʚ1`l6sYf697\&jBuogn"9EQ1'gNR]BRz=zFnp/|yc4 OAžu(LFwO* ԊaA($%X:8/BN=IAPxm3YgH)Fsg1 IDAT9Ksq 1 ga铏>B`C[y孴ze1/ Je5iXRvqK$Vn1#6|Nq*Kiԭ+seZfi6s8s2>ͱo.:DY#q'<;u3oal:-"?P}JZ?9 )C'S{oͦgӛy,Id+uA(9Rv1$^_NXz?~o⍷䚥-~N/pAyC=Կ{kӠ:w)S磏G RV-4{#5NqPWLYqȝUI99sO5{O?z:¹wwV{/?{Q.D}/nI%j35vXt)$_NX*Ct03=R0Mza(%/%|Oa8ST9 (`Ūؕ)CYaM9<1¹w\&r^~xudb#B&/3zO ]`cBr u2l&ub Imi}83=^7HX8#~a0<8Ξ;p̙3|9oHN*8ΣFR,`c=\P.p#ov$KNŠ/vm&_ۙ.{> )ۋ;{|_oc}*e}P>lqq+W{^c) UW}TVu H ԨC[x9F\%HGw 쏞Go$.g`ֿ5' 2Xرwy?佁Yv-.Q;ӜMAIHP~*Ev Fn&5Ҳ2JO{ *.f_>O]_OO^)SHNIF-$_Kb0qޱvrx+%=CǬi-o>UNUL w<U9ܬόs}/_'y rZ"Uy!U zdA~v 6qƨFi̦%۩wV\u;%1E;gt{fh#Кͼ1u%'zV^CZ׉c5>M/|I;hiNV;8 ܝ6ciSR@RIS]∪,)M,3}|y ?#a|6!jm;fN;ɲC+2On{E JBF$B_&nvULbx,]`yԏCLtepK;8IpO Bo<ۀ֠ȾNiI4!Esi|;KKt_I[d'žT)µ]aq? Rݧt]=<-(fVK<J1 Qa{{{Q۾`ߞ[}$~}Oz[1=󎚱Ers@ݘPo3sEFʶ+DvnF[ K'!لߣ.g'N-5"/RGF9G|JLRv+:*d@҆ξFSz8M960j4e u>92 `NǺ?I>@BFkF:d2srڥ3f` t+3_xaqC]ڃ9x}@^ϽhC>]z[iԓ7?1͗XO[;/>^燐v(c3o:}p?Lk %%/-r&w9+ tjwߎ`"dNYNa?P%? +BbPPjJCXpBB6C<_ݳSVI:=TK.),2/C&1sN<:Kg7}xsbY3ONd̀>4 ?=E[&E2wUG[63|O'&5νӔ'g4 Ȳ}¥%^\&/,Ys[ GciS:3X9ex5UNmԷ)gjy*ۋ1sb N[="Ji3 (gΜA$j׮ٓl:]^OxWeE;6jXԉSS=[`wL].7d%4Rb?PƄ/d^2JZ#/ِYWkPGӠvkc\*<}=/zrU콼1~O7M2MgYO灣<1ڏ3f6db JX!_H";\Y Yu!#NiɨO![ir?qz cr`r"*-ynO7l8*2&##L׋222(8H}_31=ib?DξН8ڐ )~r9\ A(FT˧ >awvʕ e0 &+)Cl!ۘl2 W.c$$p9ngp٢{^MKާ|EHxQ/!F3~fڔ%] XPE׹;6r*GGւ^NA 9-g~ vl'|:ޛod(`_/%Y?{i($PzHU٥Wz=S,Հ)B7: 2(,khθJ6Sշ=]c|Y<[AMMG> F13NiOba?'*U ,k萋S f*cEGQCЮy~=NJv&hxWbUJ6mBNv"~='9Y8^xLE(RS4u8%˔Md4NmozQjcf\6aI֝H橑O|y$(ں4_l9d\M`Ho7aʱaٓ+*?۹I?[jݛ:j CiYN \0ԇ+svW33xj~?NvVg}B[-Kd/pcpnK%W,&,f _/̗m^HM:>jRSolZp˝,%a.lwעQzUU5\Y sP[ZӧE:o:IZ%]= tyo;|iT]SÈ/Op:)ûзVL=̴dggHNN ))ƍà)aZmt5S Sb\u/?ۛ3y7twʴxmkÐkx/۬!wMO1x9HIO6#oΰyf} <"5o&;n!cxu[CRG dneڈ:/R8K>aCpm3⡗Jf<Ѿ׵G[s1u6Gt#޾SW^xj]OkAax):e4U Go^'i՟LXl "\mJXH4YsI >y~rlɿ7q><<զ[U(R^AQ]EGG;_so籛IC&c8m` (' 'ƲkD+=Eŭ|GK!~ 2]\KW1DO=MJX/L$ZO>qc9x >sxGo\k.3O:J3,9HiM1y(id-cR8qsY[%i G? 5ѭG2{iar1]dʹ9q|& AĕMۚ2*.m̕3ypsFyg5١%/Clib.2._-޽{ӻ4FM$$)f" cɒ%9bX,W1ia^4_13XN:-o%(_cżky_5W9on|]YUj5Ljf鿧 JTP*$_K&((e W&T~c!a`ҍҺuk˚իhӶmN-2Nf)e-?HUc0082w<#m0NbHh.^Rz`s3L>ݥlR4zeJV[Ǻm)AdTv*$48y"Ӳu:J~+ݽJIz -8\p14hԸB6dYZRQU2ٽڹg䗊gf3{M۶?\lk@gM[,Y{[]J`]15  -eb!3#KHzVV}@ȪR9&Wals15Q*|JY A [αXXazj֬ENCWTTΜ:A5A*+$IƱb!;3+.v:U*W.7ʃJ%;t(p?KEч3Jvhxlt'&VNEkI^/xd"X#H7#*1 Ip1;wl- .Z( "##TZYV)^ENB!|ied2{_^ P_J> (p&P#o ]8GPV*FJj`euJL*Ve64 ::7+$Ir9 !PCy!QBCPpOB[I pөSb3Uc#WPt!! pqW%􁰛&!l"ym%֎YFl M Se@ R.(? АU{~51UF @ (,Y"$(h+  imNqsB@ @ lKIrJ#%BnNy& {srK)?}d6e;\dۃR؅+av~Hvy$7e^@ctr#/Ln@ >.AWs_4**d77`a IDATya}o$#}mU_E~[ݝ%`wŽvnrVsU>Ek1l˶ZD~mfj~|Hvh |?w׮>hOJ9/A;7WU^:*|ޟݾx;Г|] 9L%78!8+ ` -}u,S(ضwch,"v|qtsrpDs ѽ[K޻9UP7Zv. xl}ۡ0rV;ݼqƵyrw{gw/uɎ V q-P@6m7va_<ع< A7ǣͩ _T>vӏçC\Ud<#wuzP!q@&F. .Q#ۛn{e˴ϥ/Mgl/3Ʈab?'JD.4Cxl]T)= CHBE-Ğ}Щp:o,m6/-湎a^wb+m}!L?捲+_Wa @ Td$kWS Pkը$#TW$gx,@TI#9 bx"ah݄LJ#) D:AQFQYevKH ^\~\O?1A>)>Kڹ^_}HrQ~-N:6}kūXn ~4[xdɞ` =Y=wi/:/d( B:揶t_<-P%Aٞ]Y?y5yw<.~|{.v^؟Wq}1˱5;p*Ǵ0C::ʪ|ɵ@ AA$.mjr;lth `U \& |O5,SQC+_yLTEZ&=:0T([UJdYajt+UOlᔭ[=&v\Աl]Ɣw~&u/jmί?F1p@Khcp{[S9rR?DŽ߇GM8 Rt! B. ;'}mYH0x/wpf&d}iu6_2^SaMo+@ prtp1~?Aչ!=GJ]lTW1pqG(+Ӡ $]Pչ L * LPPJ8̵Vu3MVMl6lt/qjxgӮ9wD46_ lql=ik:EHHvBx/3. %kډxowl= Z,SnM`I7sbeUpiiZ`q(׊pWRӇوYRZ˷۽izSf>@HcEss?Y,J^o`†wxx\0VwܫB8 'ѤJ]t3xw9-r{~>m=k ʟ+H,7Oyԫx8e/L!w"/r y 2 c?8:K|{J'|ao@ (9he@:WF,s_؊cV"N$]VP#ps\RDȑBhr /'rh_>HNԧqfQQa+fŒ'/t1&Sγ履xum'&N{KB]كn+tr$7`8 LyVsgīqϺڴѝmSzUH`L\F7WrVc72;_,x-N{P>+Epu<i:S~Lʖt\0~zN}6\+Nч)GV#tvo;YH~F:iF4><5^=}O})IB>Jyi:@s@pg\cAaz:kyu41峏P| ӗ)f] _Ѽ/eRbyWl?*"B}q/B ! 4r(t9lc F)O"橦 vveʦbĘ#Quf[F̀y n\*Lj`F{S<7ٍQ;;Ю\otYߍQ;Ҥh1LX̖nWF fЭ4x({NeS]5D&G =DɡFѭsWH㑼r>u+xv`]6B֏Wȥ:\!2nRfNL7'3l9]:j,_,G쏳y~spBވsqkp1hy3Eq|};T[P릧# 3LfBV.VQt &#Z> 4JEPM GLr2ȤTZ^BInˍR9(<]YٰZQ֫*Iy cXF굾U1 0-~6h*T( «Z~b==zĸqWǰwǼ< =*ܵ8c ?ɽgAH=GQye(3{~Fa`WuǭnRCo>גt)1tL=&_je_fhC{}#?ʠR<=Ɯ M|D}9ŘF鋼sIGqWMFVEWS{jwgSm8PVӨ|XA/Oô%J\[񖄄3^w@?۵1XB¸w CɸcEAۘ5l|5b5G i>;opǬ }sg#P9j^dj0|L;B-HCI'M0@!i[ʿ7@W@_y*\#\McrM4`~e˲'LxjbD %.nϝ6rY z%VW\jl9u{CY\:V5.ȕ۔[.S}n`7.Ow+"➾Z<̿tf۹l"+;K0t>47ECZA+ʭ |ƥ3hZ |1f /Gq~۴ Or8e7r+8 rIsRgrd+93#/>`ٯP@Pm@.}(HLcS/i̿xG^A+~CRIch9g2{Z͡!T !߸DO|pDϑ= !/owoq`܁W湗;aJ0d g<eGTvZ,Z1YYq"A.mޝGULo*;XօVQAElEmץֶj[*u^wV*K+"&;B9dL&lI&'as{2r∈H7QΝ\>%cWk3{~3(H3XJV/Sy|mr)y)8dws•͠Ọ? PipٸIf^|~r:c~Ҵ%СT~uX₩g^G]n+}?کw'L>8a[> ̊ 6jX薟LbTexk;(n{ 0ka y8riʷUP? #yh[Y4i23hɻ铧%av$Mۛb4}4mNX6WyKm[cfM˦NyU :Aǹ)~SFFKܦKS--yZri4}Ghzߴ-ahMy~d?~U NP»old ,nI "" 4 !@}hE%>!濩sy_~h;M_6c~ FA"/fῨ9|ΏR[Wcύ/K龂>˝7!6yFHu6['v$n0y )[:Ly#>=nl VynmC6kn9ԭK^\APN"/O-z±6Vs\n29+C<-ٸijgRv_%7%S=kg;:b)JRAS~39ED$){ NDDD7ij~(ˑ snrxm0kȣƷǼc+^e1ZWqԷy2.Ve5']?q3rx}d?bMuZwaиwoY_oym?xX}߳ds\Y=tҧ%4ȇk9T^gd5*LSD1<]Dc_]N )d c$ [R?rYd8SkDQa\wupR IDAT^2e@eX;`4P >d K{'HU}~FHͱJlO?

an^qL <ٜG"5a4~ҵ@=x_js|6 u MMIN`kP\v9ۖ絷>worWdI9@u%_~ '_o5n0?cƘo|~}U9%=Z_(&m>6erѼ@S=Z6>o6Vu~n_< N 3xdžOyw7*曏݇N9M>??(2EgO˻q%|g,r¢w*7Ov;KW!{n`j:$$aiV[N~l;U7~2ۮ]ٻg/UٳaÆuwD5@oQ \ p2aa6- ( ';q#F<YeXe k68w~^.,d-= ,_Ycܚ(s2o{C6q ۇ m|q w=?쿙8)S[TO[}-J~1(&m}%N\va*YX1ss۶.q.}51-j'DQ;U.O7S_S>|A{0?p+|a[Txkb}b\ OI sL\$7تwcɏz'?vOwu:Ķm4x0]p/lٲ';LQÀx`xD7p1ɇx9eSi@ ָexpRAG"Ĝ ^GlȽQkCv/%CzLailbܸ/y"ܯJ{'<$~,/69mzXxK"imӆ }*ޣGQ\ұ?#׏0ؾ-SL|ԅzkL? {4\ _akLƛAU}ƿV50[Y5t2Zʩi՝!9&ىmPV[N~l;U7~2ѣl)} ʙg|̳XlS<絛>RH۲hw{/ 8"\:CmpaFSq'Jس9xe'th,gVfKUCυzCuqfUQR:Ġ}+l6;Ggtoc>i?}0Ï%? vb>w҃kxS[8z/LsbePV[N~l;U7~2ˑIfw/.{v2uʔv7J`3饺:jwq{;d. '^,&XpO`z*-j+oқ p64=R @/7553ľgoXx=Lp^(艹 x?~W\Ϥ؏U<9|BQR@}N֪*o 7]^*k| ŮfߑL)f~lv8߶p,,0piLۗč5sO)U pPz%<}3B䀁gc?40fDnڽU/Wz8|꽽8ϖϲK6jj2cV M8DzkR"ywĴ8Nis>ϒ2i4XӿRg?(0%i]|R7w$2{ 7r{0jPY漏cΛOŘocj|Ϡ/k&]xxy tXsiWyĂ6A£PIX ~ޯ6*c͝Y_{'_F~~0ڠcecOX?P9<Lr6~wl9$SD@C ~l;UV;8n.t0'ƥ^0s8#8i?Y[x'2th4 4-{rU 5f,>9gf_e;έ pgnwB}fzXPw}c [xFdQ?G% dY&+bx'V_"S=AOXz7Gʥ<Y_Q~=߳ ^uܘ ~W^}9ߺᶘ.Ngl Uبɏz'?تwlP%?L7M%Ár{$DzT7u acxn;L+iZIƌJgM>تwcɏz'?=f&֯gƍ`F-a@>L4c_ZaKh@C ᄆc'?ΦӖ; |cC<:&a{%ߙG}}oyVkc↩oe\9jN~><6gG ʁ>~>ngKudH$"x į<+2$HU}~FHű1-1>'""";oLˎ4>k}>][SMAsA`B8Np2osGe7|\N80Z=Ӂ/cjFXۨHہa`88rFqꘕs$]Y 9[؀e{la;\Լ$Iyd<'Ozg !4m䦈Hc~Ƴ|;heky=vߺeVm_y4`b2ㆭrO`LNVN i4aNbZ# DDDDDDDzX滚S'h~ԺɺiavA{Yxwl&Ͼ~FuRHϦyI'഍ue2F1QEYF2\"""""""+L$"9/"""&7F5ǀtgQ2ͪ~K-;"""""""ݎr}5 ߷/F4ؖEEEE!"""""""o0zuں:j.Wϡѧ""M"""NyvW<{xfmA"""N::{O %&/??m,ʊ4!""""""ҹ KFIBjMuQDDDDDDD( $p"j?Y:4@D$s^DDDImڜ:^t6uۼ̱)L~¦S'awȌlkI""M"""N6pj-q}`~o5t /Ԣw(kacO:ɕo&.:3"o;L<{&+^a7\rY^<ճ0kp,.8d6Hz缈pv6N'N97 u0k\3_gC|ū;0Z3ùWpln1Sg}~N)v;1&""""""NlO( ZWY 8J{ FF1k "ϳFߘDWWӁ*b¨B GAz鄂VϦ=tVΎ<0"w.P|78wTvAg|ĉcp82f Ӊ#gYɳ/5<.wk"""""""1aea6to&Ŝ|Hx!wy2s6O41 vyCI.'9bW # }ɼyc=N0c:Bib|b""M"""N:*7VS6>ǀiNճ[j7LLF}ܰ}9\ oii1M:IL+v4H>ղs|nZضE^,[⳯~]&eDDg缈D-W0_˲I(4 h7 Ht]S b}_Iot J """""""``jI3v% ~ѥI6;"""""""Lp+v&RH۲Hu1DDDDDDDR`aOк]ںd+B+""E"""N:}u[mm9/""" ;&mDC Oy,ˢ"{At0|_ip:+,vTEDDDDDD$~: Dk2+z4jUDgN3q:mMa{H<`'&awȌlYDDқ>EDD$A0mӺ780M7Xr{yj;5ذ'ʷxuq\WGEo7Ɲ&=}R.,/yY 58߁yxEsLQ uQD$s^DDDIE Z쐭h0e hoӉ.7v0t |l~m80c/;̤<}P>_* s|np0>(aDp.[9lh>k}d@.!}֋H:tv99*`3q @b&)"DgǍ.1UĄQ21Ӊ }AbkM{ 92FGCDDDDDDDyÀ;} (>;*;ՠ3>rā18 W3FE@ ۏđ3SǬٗv6Sq 4 `&Ŝ|Hx!wy2s6Op IDAT41 vyCI.'9bW # }ɼyc=N0c:Bib^jUhA gŷnjo na+;re"Ӯ9c6 uVh\t*"9/"""$ɓY0:ɺiavA{Yxwl&Ͼ~FH;beŖ{(4 h7 Ht]S b$""""""k4 a_  Z $Ia ""s^DDD҉m&x@@}¥ 4 t2>!DDDDDDDzVN~?kKdv]E^Æ1ط}bImYTTT0vhΈY]BxJx_Pѣp2شL?#SЯuIwt #ry>L4Cs)yc ܽ28ydfe8"""""""q@K@dz[z]DItaN+ NxjzNHk"""Nµ 4=tBc6AI߲,L'4_W Ñv0D#0`!} ZW=ʶۨ 슁{lƎM~^^~p1x23[fy4x|VjkpuH: 3 dހ6äm:/{2RNUUVk1ܖOÄS2q].'O6R〈B ;10.[֯>YޱcmifP]UKx\׋eW2zxV}CDDDDDD$9bY08Lwᠸ_}-aPܯ;w75MGʢb{MT=J^nnHI'-Whzi;1EfVv5m^{eNfVY-=,ۦ˲,m극q֏X @S^~cٝ0XQDDDDDD$]5Oae[CƂ˫)f 07 rbcSܓ|RnoEDDD'{ME֛C[ض_eFzW"O1`XvtOB~{U9olɘ‚_0*'}ƋH:i&Z/a\9d; - zh[q2Ͱ7qygp޵#0WX鼳}k K:;GDDDDDDk1A;B;=7hencsԦykɓrlc|xy,CQWDDDDDDCCwe(Avt ˞]ŗ 4-10W- Vk&#1qrj7l:&""=>EDD$ `QD0\|ɼ n0(6W&h}ui﵌cp<=RnSOG-"""""""鯱@u~fߖ<ˮ`æߖ8N|^/ ?/;w8ē(,F`D<76ሢYEDDz[ H =ٙmLzdf2`.noYu4׷z=++!C~^~y777 2tS- m^k?Q`W'P_=yyx2E!+3|>~GU5490Kn}r8_cԐvx<444zihhaS8q5U]ut@жH_UC^*OjJJTU) P[V659qc%""""""7Ϻ_7pgxcv7w9ۛ?i$ ⛍Ϳ~.KDDS no;~ߠ6rFGM鑨NnUGwi^"""""""鮩- ?9;Ȉ+; |߶4$2/I3tn}ҧvUNo]He «#wBjI%HvdDD$s^DDDI+ HP.vUcőI[pYo HK]ޏ<إ`J 1ȼDDDDDDDDZm4h_}qm>9r4^RJKKٺu+o)SpYgqglfڵ<|v5M ***ؾ};o̚5.$"")5 H>|{2lذ.:I#mOiӦiY֭cݺu,Zs2o޼h@iϧ~ʃ>ƍʧ~%K0{lTJyGs&zpxN"/XzuGuY"\v{?O(+GA:,Zn!F`il2ꪴY#"i&S wtQW^+O׬YeyHrضͲe7eeeg?C]{*n/^ ܱ*//?!~{R=O=ٰaCb]6%ED:~}7nS.'_@Z~=?xyWS]|,[,e-W)+HWPÀD lܸwQnݚD$y~?sO:}QQQbDr9rz(I(&Ur z>/]]uUZbwNH^x_uu5=XBj=+ rss;^x={ĕH\œ6F˭nmϣi\m>Kn']H^O<1!yitT>},z*N383< &.TTT駟o`YVL1^z%*s9Z}}=O?tLin7s??1b֯_ҥKӲ,x ~T&u٫㴁{W{%Rq2&D%"PJw1pNt{իWҎ7;Çfz꩜z\ydQ|d޼y13^y=uA#Fzp'2c /_… nXy׸k)**\"" ,`܇>n^s h"۷o<|JJJطo_J$"~SN (ȑ#9cc+t /D&++{M@00W7ͨ7M3KO$0 "](sHyN]wEfffT鲳NVVV17oLMMMaǎ12dHDΛ7#GFW^間JD)<7VÀ0CjDG;a)"d_:10`_|q,+Q /xW\u{h!"%Zf ͖!JJJX->*m}{WsFkcZp_G~~2Lk?յ9|d#泭oWY-Ҫ"F]WjmQ5. 0OD'I2228{ƔβF3Έŗ&WH::"x4 %Xn55k[SSuZfKDǴi"޷$v<ӧO9$V,W?xrrr;tPuҸvȟzQq1[jUiDDR,]v p )XiYa3 E$5OU֏>(qFcڴik1Œ׳eo޽{9x eeeTWWS__iضvM~~> 8aÆ1vXÑi$~;v:͔)SkÆ ,tqN?84NS邏NW^*ͧ~J}}}=DDӶ@dkOvd=|n7n澡|+H4= 0`1 .O>dTi9~&$ƍyJӷo_~$$~r^{5y>~y0}tN;4/J2ٶSoѣG'$~~~~i$z7n:1IɓNc&[lIHH|"ohF'\Ý/vq;x6 No}covY^"">|8w_v-eE}4ͨ 80 TTTiӦ USSuD7~qV\ƀ`ռۼ+yB:I>˲馛(--#G4?.//Dz 䒶x<)ݞ͛7Gc9^qq1TUUEN "lw>)}Ӟ)1#[W%"0>}:zDWUUuVƏU͛7GյW $''Lx@8uuu,]˗s7r9`ZRs:\paYEyyy҄5^UTD?OT^^^Bb'm1 Ljge0:thԍ۶m9Hwچ-W("=Դi"nphbF ]g]Iukjj~dž ˕?;Aaa!7.۶ͮ]NV49rHL[AE0Ip"r E$D{I~,ickUVV}/%/_Νwޙ 3ڵGFn]Pź|b. Nw޸bD-sؐ U-^#Gpȑ#FWİaׯ_ij~oذ!YO#.߿Kt|>wqGL˦uw}{w[oU hVMi8Sx%}UU555'""KDCt呛Kn^yy咛xi,Zee%UUUTUURUYEeU%UUVVjI;aD5'0cƌ߰a>/@zY~}/ɓ9sR]F|>/RL"hGsf͚.-Dkwiddd0rH @~~>0hhh;wƴ}qƜGJJJ+#_=vZmTVVGt/} JXVVv7;;;tjmG't܃mq2&D%"CRXXU[rNVVVu֨ʑ%Τ#GoG0 :,;<&MDFFFKRSSu_z%,XHWcF"ե݆miO?=]!D:KWQYYwlHu翓:nI֪tDD4@lێhFkbۑ(iAywY\.~q3}N W_ͣ>S#t^/^.&1bi(x+G?Q\s _BK^b<"" w|~l:/h[PD_|8+++ٶmƍk](++8#v}o5j(6mSZǻ˻ q8>}:&M";;;E^;ŜDZu]RuD E'@D$bMgmsl"1 "i0 M< MEJ޴ixז-[زe O?4q1e&Mɓ#z7^TTwr|ˉv":$@ "rv}vA1 "i"z-a_r)x}.1˲0d}k_me\s衇xWS .]Z00UjnёGY'K2T&TFcpB-\Pv$[O>~EP;\KJ:U=yTdY|xyNʤx +V|}?*+ȝsd֖}b\uoEr)*0eNE@E  LZ=.L7n,(رuV}_-bvaEv:;;ue.+){r?<Ї> T{{ 7P=X\Py U__kOSAuwwK/qu6me]VN?2`W֭Z˗xWuΝ;k]խުgJ$. epQGyD\r.䒢2vܩ+y(~V|bxS.0p+LV{g]p`@ʖ}S__k{صknF|˻.-\Ҋɬt$TTG%r(f~*zj}E]/N?tmذAK,Q8GmV̋B} 7PT#3Lѿ^~岬eݺuZreYr1Q KxTr,(իW577]z~}_]S___5XnN9yEẮ6o\ 9p}_v[1,^XtuUuXL~UPNް1(SgG[R|LWrs9*󶴴P_җ&vXv6lPe`f`t'~<^lmrGwy\SK|[:_i7{Ǒ{OS h>`+uSov<#8B'tRѽ}# 袋u֊W:J_|qQ^Œ |G\pAXxާ>/XBNH ٥sd)83geߧ,˒j*L\R˗+qE/~Q2W<??MMMMC=tի}OPK(}j/aƚ={~Na.b|2,c 94w\ΝVj~뼽橶&p($k13Ng? M|y%rZzu4Oc U__Wb̛7OtAUl455+W\QRρr8S/9AK^ƌҢ7k_^|Ŋw'_cᰜ-dY1m{ nV[DqhhHT*q'+6QG[nk +ԥ^W_}r)x<^W4%\N8A7nT{|ʭA^xN>Iy~F477kƍꪫ"sضO?N) Xc$>*#,:q|}͛7O?Ou572Lc.^Xf͚2-D"g?twhpp11:uYgcݫ{TF}Lg}z!}zG500PC:(tIZ~ThtG}~_{7߬M65eYz;ޡ>[s YZlIky$?5V]|=-[Mdr:V-]TWֱ 8餓tI'U8Zv֮]L&^zI=^yi׮]3{²,544Ђ t!hZreEmOڟ9g9s̙r~,҆ aܹS=~iKڱcǨPHy߭*XFZn֭['I}_DBDB)vJfqXB+V+LjppPLFqEQbqT2w\y:3%I못[DB* )[.12(VX,6-f"۶Ԥj/$BX.0PzJ 쎘fIV,I04 h<@\,RS *rgLd `0%I~ 1@J@ r$SJ@dwNWu5מ~Ѻ'k?/_}owIcӧ/kPDPsr}s'USPɟ,I2^ z `0XUJ@uUBP~.@)F`nI1* M `N>j%IN&[;wjП-iOɗ-%TR,[J i8cΩ`eK SJ@LR0{zJg ; ZJ@Օk>XUIer|"w`I(% (%ȝ0=dTy(%  ` xܩՙ  d `o,X@U3(%(% ,I2># c@E !`{ @XX60@N=1@ |S x0KLU&0& c$5  Օ T |#0@T,XT,ixT թUs*gI14 8"w@)k>XUI5|,{*AW&1@q\aE 遌VRG% (ùJ.@)Ʃ320X60@k~% c$Td `WX;`z$& "w@F`"w`I1Fll,RfIO2 cH Xc8@)AE R,uNՑ1@ & x 8"w@}fIRRJ@dd `\`: P-4  `ֹ:8#0@UTP-ST+(% S H3Kbs @ 1@ 1P;`z c#0@e> K6@F`$S9* F4sß)%(% r 0h>@YWҞT,|JVP-1, @>3J 0Ksf8@)AE |utt0>Hթ^~Yx0xg7j)ǩ ;@01(%G)ز:]}G%<S AA) fLӃc_7ʣ^K.+B,X,ڑuu ;uO+?u29Xmu.^. *s*mnosԴX?mɑ/h.҆ꝟI}0UeD+B_Q!I%PTP#H>uOEX>ϬZU@E/O/۶=_ži_~:)/\Ⱥ[z6cБY_7 =]`)-l=_DzvuLF/o٢H8<XSf͚=C:dr546jEiEc]=;_}nxsk󻮫 Tì JHF"(òq]Ţ`\í˒1 IpDpd)e L6˲ܬ&͒ I(h(`">jO_jEfiB`.XĀvlmk/^߮$}hFz͚MRWU:U`uY\=zQ_NNӭ[TS[9ݤ^eRCr&G)0~)]~?k$FK>p5\W$cYZb~BmZ>%TSW_5/`2}'SJƲժՠs8j:`LK[!+VU?Im翟Ux\CEJ.%p3idN=koӀo騏+Uʿ+3Oz GTФKVR&o &$IeeB͙*nG6Fc "իxK=o}rb2=ڴBv^L ΓաXDKο5$_sӿeWRHGoyjs|P e+ʲL.ad`vɸg7Q&vlU&zJ˵C2"P?mt5oztӟoiWji6nhNw[#% 鐥OGR6(tՑbm[e#ݖ߷4gڽMn&-LZݩ4̒eUAS%I{K>CV]>Cߒ]D9E;S,l"BPQ(R( q8,ےmٲlK XR(Ry`$K\7oi& (_P^!=UeuByS{-*m۲[B#gDclɁeKpDf_zz*E (۾V?Y&7}^ztu\3f &Ўk4h~˖m B2dj`#ϘlP}{4--nF~Lgٌ)fyCSvlmuc,˖ў ~~/Ihje#>,KxQO_dSJJ ʨnw][?kϓ/{LF}Jt顐 HDkχC}Ly%t'R_oH4 NpΖkH\̀|فIW H%5N͸Z2Zdcj/uAsuգ=m?Q{#>dG J}5{׽&t݌܌({7p_ +sA*iLFS%)bj冝:'ߺAm#{׌P[_XMM>Y못-nF  {>d|`uAhy͟b'u8{Ե0 換17Cۛz5`܌YfCO>9=wA,SuS_t\G0u١W_|A]j0:lߓ1kLZ}4ߧ1i[z}v-תK;0TZF#YFzknuutTdU,QhZ[&Rw*%uB$ۧ;JuH 4az+(8 GȸIGjтZtB 0´%[:5ժ >uk eH BӪeK8[I.00j16^0 70c}yL:]8,ۖ5F#J?ѼL&HF -R$3(I顔|u &S_'q*2+;֒+488D" mGKnR&;5/A2fG>xJ$I{^EtJDr&m HF͡w]W}b.h_&IG?r~_Rՠ>IRbyQ+<_M8HjjjdY;$IYsy|?<_z>sjk:8+ޡAI<%5hVF\q_uDƫJ~ǟxRl,MO~иcp/Yu:RLZQδ<5KZ&Gɗ+18c @dl=mvWi'R>ŋ_]m;;V5JS\F¾憥ŖyJ)je<ͷ$Ǔ>uQ + cZR)afyϾC?N'oXT*3 ]R2Xն4+w7'8rK [R’RY%IƘ1_yޘgyFx֭;^m< G<ϓmZxxzgr3xҲd%%˓,_HJzR$#%\),KQIWu~ L6 T]uϿn y~k%5'U#'yFjH`~tTJ3ޯxkZ]jk$I?~s}V-s;V;B-"6ٚ%UF}<{+lxc&_wnIR[[>wgtNEu͏[AOסF-i|.^'KiS'YLp/#IFcJ0QXT9:cuݯC<$Cgs>!pB!]wSvW-ɖf>^->|Ls|&ɟn,˒|I|oe3ceEgB 1*8|r1Feɲ-\ו=4ɨuۧh,=o8rNdٖmO=6 Er|W*;Qnyx^*Y S]]$9<ʕ+wٶ @˲,(u]% %I=S{=ڼɨL I/F{dIcˉlǒLYo|[%H}Fˌ:KP]7qEQE"9#˲n:qim߾=}w^aO%F0[-'(( Ɋ-_T5$+wmX}^GVTsx_SS:+Ȳ,y'cbeѳ>+c8B`00{d Iԅ-E_7jxV,̱$R 'Z rc>kmV4mڵk^ cKRoo֬Y#I o8 dM2BwdR3j3[(YQ֓+k#֑þ3(VPHJr]W*ɨGwݻukŊ8=/3'8 #cȄ,H*c}7^zQ}] 5,vٙ:Tc5JSFs?.%m[Cjjjyj׮]J&Vs0Y=,ǒXtڊsO]}ȓP&g|y_IcB!E"aE"=S >޽[3 2L p$~$KLh3[4?( 幮+6!W7FBgxt:_|QTJK.Ugg$SOM7ݤt:A <2ОXħQor$[221K&`v$/'yOJ yRV\^{Mo$i޼y ÒWϣ#9l<(it\XQV^V߿!?$G-^F'~6rJ矗$O*p]WtZt&1M1Y)|B.[$ɑG6$/>NTFTzF9Æ3$_W4UMM$d’,.""*K\{"\("裢F> >zEIf&o=Kye{z0If׫S]T9']:爈NW3=勈V6.EDDDDDD攱f%$iG FJǧ\(k| R)!B(3X5%K!7E_XT2Iͬ|yBlLH%yejzN8 *_FCFLr]VV3004JFϗ.?")vNqdI$8e툠lԫ^Riy t"RS] @5/D|6/7lQcGNE]ݔ!""""""rXV¥5:d2ZPYQށ 뛶rS8 DkjO:Pc C!0FcBC>rç:1ƐA/-`V E c6_>h WnvoigJ aR: """"""`0Ň ҁkJ1`Ǔ{/xsAoO[ay./1QV/ͣzw<.ԡT~o0$^T@DDDDDD1`UgeՕ z'93ߞ(;cMpΙ']6pL*0P<&+ݳ{|ijx䱧 { 9S#O<ȡS.ƽ<‘2|%uoC  [p1X//Xz#snlۗKl+*Hvvlſ ?~5!0orEDDDDDDvTeٻ` eC"giCdbD #醴mh4BF .$)|>?ׯ,qb&mc|++[4B3Ԁ1xײ~z|>?d*%pi<)8 """"""vV˖zx<H.x2;;rJ1`6UtuwܳϲhbzSטnl~r**my=r%n̚SN2g&ZTO#+WDDDDDDPмK8URJ%룹d] ‘0Kc[-4nƖW_*YPWG…#\zWfx᝼W Y#TQ>U9`Z[hkmyUHJLh[BA2T4Tq(!a[yU$`4J`4ZP\`ebc]h[pq0%SRu۲pEId0rm{DDDDDDDE}E's0`YӞ;:qaG#mwtc=ƽc3uI% i|9uE颋xWbOs3@qn#HR!,#褧>!H$4m"OGAg cw^9ulg[{w4eDfN>qu| 3;=H.~a """"""NB Iv||:;YY<8j1 J3)H^4[*L?z"(_zdfd"۸+ G?(E.}az:3-OPU]Fooc9ޅ1P;^zcFC`<7oS|;#ܷ=^b}J BG&55I{760r|1cp~|xOOu]DDDDDDD%f$=f Ŝpvl79ٻO~[Zqe>0rGF|жm;UՄ~90lڴh$m;A熏}d27ヌ~z{{]w.pØJ`6t4G1Km݂tC| tvvϢN9$UD|_c7\|owA$/>81D,^XOsk =\a(/+c,Z'2eO}r 7|S|榛Y4mwoV۲((' 0g`9CY4Bq3*DDDDDDDEm8Si~S֞DsqMF.- '&83rCeYX ,ww#׼*?и}wBEȡq6+hܱm |JȁakR ůRpۯ"3`1uc0yS`2|1&r [;{Lzf1Lf=3!]7ϟpcX"SL#sa~3pbٲr.Qa&&^P^L*o8w}\|Ab=ƀeFyO vx1.;LrT`0L\qαc=("""""""s@Oow ;%YqaTO@DDDDDDF >XƦ&4,lupgW[9v)3yx3Ͷ-,޸[D#aQ+g:""2ee\te.gF Y,H{^, 90ԍ1t.`ժI&nj1<&{z: lw?:D5. \G(0}c̸6K㞽B).r㰯u RDDdP``(8v}w äR.vbTVLorkO!=(`YIVs,gNu.{:}m{oK64 3|׻}npkeXb`2>}k(xw?+/Zc{5n"=i'{l\Y>ϱ6vx/|'kJ#""sC[{{[Zo/@&=m2 ۅ"`\i<,kY|U`(ySeZ̳P{-XpxaW^X{$ 䭗agn/ZQK*岯}ɒxGg;x27lG…~=l3|ᾏsuq=NO^\i~p|zz_qI ""؞f;XT_9˶'>44rC+p=sN2yy_.s^HY#2fU{K6?cD{W$W-ΛcaJ80޼>+҇*wgAą]#47H$_pJJKK+(+=$RI֑L$ \y $--X,\耔 5{6zm5k;p: ys!NzE1q~wݛʳYP`@DdN2D}R.wu8|EKN'`, u,"ўK D;% //d֗']?7uo5hX;:_uٵk'l²R1-" Һβe'u'TI&h޳םe<7ܕ`n0;ҿu%Oswݷ}kuyZ93G9+x&d,L"8.|rnѣyAQy\͖~kjg3}uED䐳ww#FL(+'##>yu !""tuuG(+ y=wgPa…ritmJhom}zzzIclذ׬a06Pϲ,"xT2Iw[&1Au7 Qj}m_\Oh+gx<=,?"""@EeKW+/:t5DDDdǡ.0 b⸞8!"(N(4'd (KϺ4Hp8< -ƞk}sp`ҁ!4ne3`4w>' msȜAj=WnmꛛS再VVto]}elث[DDDDDDDfA6?,J0;o*-"""""""SSw~񥷟eA^` 7I """"""2T]}0Q)ˬIENDB`muse-4.0.0/src/doc/pics/no_audio.png000066400000000000000000000767031404105311000172630ustar00rootroot00000000000000PNG  IHDRi sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleMusE fatal error83 IDATxw߳nrEE X * D#XQc5{{7&XP, (Uz88oNyfv ޗp3Ϸ>eSff,t+߉͍IkrQQQԱϿK1/}khW֞.P|>}"=m&]5ǡ8-G<45.D=D5i0$.3Gw9Ϝ$kM%ATPqm]Tp[sE;GYB%N&92JgrAIFzwDq۸6CStR=;ۃ&.).mֆd^'9L"uHl9h^9֧S}d t''DsJ7J Φ' eyreϡx̅tpV_YxF)1VweUu jql. Q?[;quiCz|Xgk7T#vWڳGYvJ,'-JBR!4a34n|} ptrnveK0öҦe[Z~*tp]H D]ߜ@^N(7 oZ;l /Emg%IIvo[߉]{wW0L@1GL&Yb%U[u[5>BA($5^]]uh׮~sTU%QЀ~"I26rY=g׉%yHyf6Y?[p:Βl@}`9̶x; fMMu碧*h ~S76FS(ilb;2[I|CżϦPS]K|V^KɮEH$+(--rڰ^ǯܦx,FuM ݺ7/kG0 I} B>!r=H$`jWZ\BN|;g.ee<]4eKUC# ̍VQ(kߞb.\@SX&W͢gcHs䃶k |]Ð$<ԓ"/d555 >\VOd 7of,^v__bG?@(T@mľ{M:SSB'KnltPsIJlL3JtCȘxס{r$+$x u2k Mduv^#jkVF%&`7 .r|mI*"[!/KOL{ I v!Yjkk),*  6pu\wD#);KҶ s94R_]5;!QW%O$+okV2hp q&1ߞp@{xa˖- 8HdR͚d"AΝX|JK|c-mMi ߲Oo*NޮoYw78m+-PH:U${Z:BH_ {5"e3`ﶇRL K@,Asw2m}'o[Yn֍2<^Pt_oƋ/љ*xܙG+XTۏIB"}1PA2Lpf [,2[#mW…D^F!c)!Jx`@ZхC)"rRgf6`,<ϜxhZR)7=UMeK%dktЁAX,͛޽;DΝ;|UUr el_z~t\z٥L}h*_ϚO>I,lԉeKҮXb[ Z[M̻P|4'j6m6*iZ޶} (D?ҥښDhUh4B(?DmMaùAgmYmwZ: yIjMHbqifn[Q91[\R:94W"uH3i±̫E2щc釾FՋY=MoAouߝux>b|\'@3=2d@x' :$1#ԏ@s8{r?Р2Q;<`Rf$?ܺΝ'ezU*_%ڣ8cJcßE?_~Ljgq^`@60U,[e+_ 7;RS|d7̺kbǙhnû7<Ͳ@$61.p-/dWfX҆mk7#㳹3wp5P̜y&vfi̷ECk̳݋F'^}O{1*Yvg L>I.# ,x&p#ӳq0\CII _~[kct-&J&#eXSh=܋7С&ض* 6jȳ?X(i.{al7|2=*l1(L ӟ4EQb8x6{?3H$DxcZ KG1^rH:yER^}!J S)()$֓[j'## |e􅍟IvaYީ,m>>m<{.NSO8MeSc']vz)$I}E2:KX1rۭ՗3[obr}2's ܲ-;Q]$1cͥwcv>|0򳏧u]}ABl؀&RMXzhĔ*+ާ[ۑ;\fY̧΍] 68o~c7|j]Nqha5+ EgַKv`w\SiISg5K˷|0$TX抍ifʥ<>s|ʫ煗_f5<ȣӦS*GQS[QAMU`>GY2_p}bQ?|>? ,]>Ei1 I"#!LM⨏{ eJY6e&Ӧ|k$1+hZMyޭ2Vc4D:sع{_9 dq9/gձ$pDʞHֱr::+n:~}V9^Y2ͻXQ)~$' |= w&S%aE^/FTzEFc}떮.aZX7 Z3~ڍ|3t(rܧЩe$|} ;clx^^۹3o*o8$}B\)J 𫮦K#n jmu/ٟ܌Pظ`^GɫpcW,cۖͼk}>[CƦ=z+y y_ ;3fdci0t|Q*l鏓!+ѡs~O~AAˑ_Pw͡~d!HRYBn?uhg'͗f?&}wnJo]#?ݦ3ʢm.jKÑ[)3t#u";_!B@2B}TK/V^7UFꈪ aͧRd1=' 7.> fc^b翞͈ a.ap8gޓ'dĨ Xܡ>v̻d { ۓ.xQ|O BØ41g&;;Ť m/&\BҎ@o>|={̺Cϥڛ|Ϙj ie Mwq"|cpdKx _pk )onϰdS>DZ$`_'Or lUSSW3>a* ~'1-R}'0ߍܛښl\XMٴ';`R^^_4c% o(~]te]/%AQP\O]Qǟ-+6G5b(*,dȮCǢ$b1'$ϼFc6|7w.όć =U"$Z6ɵ%LkJi&?{AJor"2MŞ騒8fG:ՍZ: "HLv9_>X8*@çYƉEcO0"HSQbq+-5A, )Z hQ^'$"t><~MGqb8'/ dQ䅂t} "-W& /#} ,9uIeɑ/d}S|]Dsxo|9/&/|IUUUP"4~):~9аq=?ӰqWW󈄣ntMd9O}Gv݈)GY_9Ѡg] J\Ak ;@}DP_KS >Baa!y++Yp1`E%I 7ld 8h0ݺu`Y|, 7_<ww'%6x`lc|%}d݀vO$t)DD*uI~?}> uojFе@ B gaz6Ulbݺ %% 2]Q^^!F;zi"70ˈL"SgHqsk+֤=BqdsAՠ/ Y@߼ՠ ԭYFZ_X3r?N|fIrϋ So0sjefYL.q`wx]َ\>6P^=(.,fƟW{Os`0H=(*.fÆ lڴkטl<.]R޾G̱zc%o_KFRH dcufگ546 GH$AB! :f2G*86{n,2U_n3s RIƥ麂7!#R|~r694vo0HQ6gۮzY,<66;t˭53 ? ]^-)6k)q?p5 s1qm#~eTuU@$!Od(ʼnrzBU=Y[e+c0'gbSkیbLc 8hO3Uae@802Pw ai KG`;ye!dh%k͍WM#iݼKdrnmY u 7_xo9cXNu]c W)եu <="<qz-(oSm*[$DJAղ涸]A#S k'^ gn `Aq[wꊄ̹9^x M*<`\wMܵvZ%|/ պ#{JxƷ ,av 笣#, O#o/<͗e0Uƍor'622 f7+AH ./\|(bw/L6"k4wh'6Os;5/{2薭w߶NBΗ0-KUr;;H4'wGb0ۀʛw;vF5{}@AK.trTktmn7jʒd3m:BvBx̅A7p3 e fsEao>p'Ƕ$Sj~t 8'2 IDATB|5s1@gw9ptvn%ֻYF@P$o*^e%yaɜlә:ԩqұlcrY2 ,gkI_*ch*]f f \ >2pR =A_ߎNsL/x3E΀Nr"p4 dDq1@`82?-',6qAMiv#Mu-%0$G*gq{洹]z6Zvx~ =o4wRs{3mp`ǙͿ}fY:7x4 Aܲ~%fvVYm5 {?x8-sGw%C\гq g?_0L*^X/H5K=Uo(}}gZSFtU}_.oM.Kh> Y}fvZU^^[Oq7.o,nrk!n/-}3]xd?W= 8I r]y$Ie2mOKd+_o=JיUf<#(ǷM=ĕfўWfSoiݫgwM~[ -^: s`*g)Wvs~އRW; .g佽&'3Hyn'M̙5ۺMH M+e9SڄY5Ei@ՆMS+=vH^ElS-: 9{O݁]Kr(csiŹ4ex+e~Ng 0H4Rz?k+e%o6oa+` FffH--sĦ9yǺ7K-1{$Oz\EEz6p4w),UWq8n Xa.eKm~);ƦZi`ȧc'k: Qo6p7vkY1zP MeK`7mopw\ݴԯu#L?'gM|hi ]W.1bÃ)XZSsth=0y&uQ,jcZ/إRM4o?2 w#;QΠo怾A):Iv~7]Yep@0waIqкlVrOzUX"ۣ#6-th_ 1Djѳ' blzodŧոANdlEШ:%:D(ѦLԖنl#G=zB:0Ԥ}MQ)jٕ2Vi}5FժŬ|1ut-3xY,ډ#T7g٨sËw3.蝍1b^x}&Q"Lji?Ga;Pd~B[$ؓ1;W0让=?=kƂ,i^%lh+Y˦:\DAH5پ<=EѐeԲy#[2aex6PUH-J a]jiٹU>;eYްݦދUa)ϖ'9 h4ܐь]81`5WYLs,-ng`Q/c =˩iPjj3Ȧ$c1|~?߯R=C4/ ϧikKq9kLdRKKdR_Lmh Y0F:۷A݃#'J|ޫgٽX2c&%LG?'w1(A'wr-өBF .3;J$l&\/憷bx莧8Pe&u'DafJg87u .ˉu xxq:}|\~!+g0wNEOxfr_M"Pٕ=+n< 4ït;_=N~c_?=c2?#];Du7XE+{qWrNYSow%|yH?s_-~?\\vg?,E+zɿΘzM{vj3#D#Vv)/?p8@̻ϝ w\3d5/~/m3CJ<:xz~`}'o.vrÚY{3ɸGߒ􊢠.YF(w PT_(@a!"EEX..&$7Rʴ}{B#}4O5t߁BB!|ݛ=P7T,~~t] ]>Jb\t}].o'1| w^.wVb*t'P;j]{'/o';Aezu2=ݧ(n\۱Tnʸ<ےaqiIM-v+-%m{ر/SbUk֐_VcGu⢦?؍ ۠A, BϼMi0ރCsC ,7ɨ;E{/ > ܗ'>aEd 'kO$*>YО?ڗ?O>ޟ͒w~BEXͺkF 97xof}yOlK/ݏ69tt|vE<4|^4>I>LyE*}<74:Ͼ8_},a-2|gЏCB?~do|҅ 7'FV?nd۱p.G"yZ?GW`10('GtCx®rB|el K^y'WnJXi7jt(YT,:%ffr7ώRyƮٗʝ]WWk(b+WHٶ[P޽)?VϜIaj;Ր ɹ,t3On0{!L1wu'(*6CoB G\u kWFB ߆)TPox Ыa|=Ј?qYatf]HԱ`9p0T0@ >ShԷ$*$_U DјpRS"Hs_e!͆P!:*!)JP J4f/]%YӰǩLG K__Hq9gA̅ *j)e?A TLBET*SV!Y$ugWլ(}(kS<|h浇zpE7s6~/:@h23J2*,ڍzdg{ 4A7- AZd\W/ʈѶ_lK63|FVOƒe_\~*>v='d|JB,^RcyL-sqrgY3gp`UD"|yu/w9.ȼW^d)>ٽXɇ-;S5o:?[Ʋ>d%돇'O%/N>˴8 tcFmj%s3~SF{ Q`˒*ُ<@2ޝ9r8褧X7ii48gUwgȮnvmT`ä]?m%Ffw#{/V[KmaDG\}a۶PYI6[?`Q;Ѝt%@FI{pޅ{𶓘p̕L+K}4o|?}G&߶=.<= S6qQr!Rlp(qt TubܛH)aKn}x0. 0pbk?} J'_y-^w WϢ|߹(V"4Ig[VSNbqS9H9n8AꏯNq#?]8[a?1Wy7%麳>QAΥLgq֣[z?ľYQߓnGrRog_?:h6v'\w [D×\|%l[̛ N깂F) tmb?#vS{'[ =0'q><?OJÜ9bs<ӬTz >4JO2+h{kOGf72;=)49FwKͰ+phX2JG6e=5VW#H/;jiN9NWUΆc j 3p Ӧ ˥4Y-9ie?UͥKCUA=+-g˛n@zh"xznvZm^-ujm<#)V,馴f ''c1nԂo:UY^>òIAI>Com6-9i4i] MڢNhsGhD,L$L]Z _= @(J3QܟBz:cC-mٚF LM_.{υ nɥgNrxٹY-vz.[Ľk~Z @DZurݍ QSQb$TUUӠ;,} A"$L|S kKKj"т-E2 My7n?}ཱུύ#ίsygYC˂pze?M67$IzLDQ,yyR- b4Yr(> [A4᝹$mA)Mg)0hތݮBqH5CiPh>>FhՄkg|FQH ,G"#/СrU$TzѨMǤmh'@{(,d嗓HD" DH QS^~qdyZgDNd=էXwc;$!~9Іs!{v,bhm'͉vpJgH=D1e eÆvuDIF[#nh^'qȴ a!D @"%AO2rAas[s 8 aPX rVUI-c%H櫥vv)04ƥ-S>BL$}i^=o/d4JqQ@ m'ajx4j=EE \{-j$?zL~3۰Ğbr6M#Uwb0[o56ʐЋ, IDATkm'`zi&mF.? Pc1T}l8o«$hާJ#KJ{hH$!jvqZ]MSO;1HfߦWOWM$J[k֜[;qd<1.:rS^ch n~ڨ\Hj3zU%OE~UE{Fwj6x8RZTD$c[m-=e(*bM7ySU3z=~} 0coRR0mGrh#fVzخf[c?׬oi֤T?2^ˑ X:[[fΤ>x]~Y<o4O$ ycJ=ؓ;Z f%;hc礬Y@s{-ٷQ5R?jr~.,[tW |Jܹ̿f7PY|dKs\?OPQN$@j[ z]m_eV^ϲ$yYK;sєV\j6Hv9JQh0q^AE{uYڷ/駏>bPRTD =lg̮fn2(e|hU6}7h{'/͙쫮b[nsu|һ]q1L"22tȥohB6n6J:j{j -9̗D4 O b% usͥ2;=Z,N:PA gjj2^mWZŴ1JmHH}ڣo-a<}[?H&!O$ B@i^9sP%z+|-|3C!J~WIcO"DLv 7#mFm0}KަYq8d7#*d @(Jf@CߓWɬpD8aRQJ E>0?mޗQw!1Dϧf y19 yyT ;l8)-Z$b:e*o V}~yu/'w7-H^7Wl^=(TB:*¥(kzl7^.=[[y$FJn63Srl=h ˳v^4bؚTLo_g]FH8]j H\Dꄠ@^Uqm ;ˠ6/UEozzLJ5?mpbZRIְr.8ZECBɟ0w[)*>1WsAT.t$&ɟNFRPXc#w"%<<2m~8 N>ZO/U'sPX;4l' ;dIȗZ"Fs)2a⑜xT>3?&3a\b z)|- q9 ,T} 5cm;ڬK,u{\tQyciBw[l s[y[881K9&7zr)rZn_ns e5SJVvlCzRus}Iܺt#.a!h?e }o3ȿ|y<3ago?v,z|0 :/D:N hrэ,ڑvFibIPu~U|>~Dd=s7w`xm)gX|DxǡWYΰX+3b7yv>V{=X4,~kZDݖQLsC=̰LXu]Wodd!hE&.'0e^"==/>{?RmF92kreu}#xDjKS2ֺM;f+xWyXs,R&r=xSݽk^&._Fn-JT-e}~zu{qO. Xy\ȚAR\rrwovfDZyyjO$Pn?_7MJl)_F(|H+bsMȪ/YA J,g҇~$kWRm;7Lާ;%Y< ƬoW׽PT؉'IST.yڟ٧g)||jj̡H652q9깔eI _?E'^佻QR؁OO߰6Ҝrb|_ݏI݋ ^@QGt7gUDXx<2e3:<=عP+2/uF.m9Pb bxhw`&Ыz11ՙu/eޙ7Ue^6IBee7qw\`dpQwGGP72+( (eVB)]4M~$iymRJ=_>$}{w߽I- M4vԊa:5Plf=4 7{2KH?n a ޽Цռ?7εkq9UZNNicԪl3v{j8= G:vA$+^[!NS ъpǜ, Qh*<6I$+ ۯL9ЛmgHСwI}p?ebTl4c\5?@mee\}5|ݞ (3,E!((ѣ8ۣ?y?G_MygcQnRisjyI>Dr ,f+֬XkyGJFs$^{2l_M 6 .qF;%P'KUNsF=?u5Y*.ҽ1ACDz)qYH4lf1uI!ˈ&oh3 m8@||ޘ%3']c}mXN>Uǖs>b'W`m9y^]WtىXM:W+#V!y={e^t#LzޝEGVAY_Ǝ0hs]Ct.C)q2i8TcZ|l[1)ҼkÎX|=\P) S_zכ)MNhCv+mg'Q88^/C TXV70`ߛEe c='Lt( ]њF0RQHa1 ~D{ T{Z{i׭ $Ga "Rv S<$FgV|eq!+ctFt:̗lÍymQЁ$Ch#ջNABtvxfiWP_̶ȌLQwS iY1ݎs8~!/M[CbNg@\|a[ {y_1'NRƮ-vh[gƮ듆ZZGUҼ+|>-3$}?}M1| RMW9kHڧܞu^jeTWqc9peɬ癝ӊa]cA&QmJl0E=3_;9%zzu1dݾO,sh퇼s &,nZHઋ1`2ԱsB{ضt/){Xg9#q₤ t辡C4{%x5(nJرϛW5il8ܹWk~UvdW2h|ڴA)?ѧ](Fw/x{b#Ԟgp×=,2KCNiT]-Sr',yyF7fND9OS:Ndzg.%+=9UCq}׳wKbZeqָqu'#(9bsS7űu4p#P23Q-84'k*Rp]׳q^1,3NkUh]TMWek'7ݯ~%ѷ]:VwR9Dn\}UG&OϗI=9_׻mbhDžw]֗8dH6&>j;%)ϚĄѥ(؆qۢU7F<Ӫ7qS]`+ -[poB;E!(x|=ysUQ}U`*rrHR_9jJcA_ G}0^i5êwщèA^]ƁˮCKF? =1S#C{nͻE?Ws7 A*ukǮ}|?2\[QpxUAjy݃;^.cǀ3}jwQ9reUM"p!} hԐ}~:_9 .j/z}^qb|Aw農\ٛKWr=e-U\1:_ 7z. wAey7T8/  7c+U޿zNe*{;i޽BP|双yO>0W4{VAf[ۏgk^A> {{ m;nȝo/޿]4 ~G8tAA{y7Qe^A8a{Հltbj`^' ]`o'`^v*W.ԣT͞rUc|PQF5E٨ ‰HdFV:˾Ĭa5T2&'C@ߗ=O0ll<=u_j^33^#8vA&'`^Qmًw5žM*cMܵN{>hzоas/ه#pVWC/c h(J5F_+T ‰F`qY\>ݓҼd9bv')!eA y|j8^xH< 2;w#P١QA8!ǯ_ZQ6u-fgphf S?KM ɹkZ:7Mee U6tANLk{7NUST}Ngp[]ǍՋdꁢ Ae^A8q Z^xx?C~A{M|cAeأܗUA_LNt  Hvk  4{AAhaxgë' BA FA]$Tbe8t:k\F'\W5ӓRPw]9Ҟ8_#ʬ_6V{4FOk!>[1*3i&N>|C :f\G;_Gc#3&drc +E#١uĞBxˎ} M?'CM_CM!m" <6Z&0jv8rQvi|$2h:OC|A}Vo{;mrl64͜X+b!2T$q򝓸}d F*O`eoe2W|8sxw;$=4K5'.ߤ- ׾OR)?76>獷?}60c{tcY:_N:=3IDAT]ƮOz;}& `v>ww5Mk!Xw”7 S2O7b zǩZͼovC<]5 SO^ɧƓܛﺋ˻Ǣh:~@-ᱱis\̃c{+/(Zm+ci0Q%sxe"rJԪ'x'WO@3g=&i @1~zZxhl 8Z17;=D;|rZ]O&ώrՖ]X(\YM%lΣWc6vn Kxq\kVGe]CojZ kfyݷrf]K} A r21wlm^{yY3eȱד/4ie7'ޝɋW[ͅ쪨t˯E&fg~9?'qJ^eEQҮa8LG#ձpk^{^8'#H|>gdz=D*;&w,*òLVW#o}̘=7yfCf5{MĒ糽xǒZGG/a띧Du 9#iot =7-Dspŏ>w0}82BG~X{w1Vwq_ٛ)tB:@#c_z{F!h!})J{")˧ct&^46,AXLfZu nj WF^np&j$b6-g6]xN;=fsC.=:9`6I;nfk,LnIw/Y:nj#ԡ]k,SOfԱZ QmA$&Z+Gfm_kO 'sȅl ?S.{3O{b;.7 Ro'9 0Y1*ЧS1_9YˍMtGR|z{` ->5]5D|~Cκr;/ҞKWul&96WcSab%<-'gZQ4poon^>{w.,fjeh(1hP^]jg2BlodڭeڋҊZ(pD3b 3ENzȷXJ~j)_W}|+Ys\ڥ/|9zxjYT&.έ!S55Mka4Y{ha8B2O.>xND3M(mڑ֪ 3"(7X[~@…%C-9jcK Wo"k I?y7: <3u.}u Y%(q]1K~ؓ<.)n5K'y7\>7Ys9҃oLbtnUќ6ܖ z6ҷ1LBr>(cV l:r188\nW-rL霜Uʢs泯S;'S/_5{gd 6l;B_^Ί0+VLFalnvCwhgŷwPT^Υ8ڛZd6V;7FzeNx' )iSZb_JNǓ\8lX4>ؕ1(Q w?nÍYHMgԬgQ6]/GR%o+TL <ۏZ[(؁Ǐ?1{Vޖt1ڷ7N+YS1hFR $IŠݝbpxkǾ0r-gPV̐p]SYtN#L| ⿚ £99綋hÖ_GwUGVC赫| w\9Zd6V;7FzeC$eygYYրޞ`L~:9%݅/ !yYqoeM7p͍Ts6iw+{Ǔ8E A9R`iEd^AZuia-|AANBGy 2tA.#zAAhQC(Ͽ#y8}5O+Meչ.DnCڨ)PL?{6M~^O~f^\rm\-H-ryDnʻ7^2}hv/ο羫zD} τKW0Njby[}|ιΝ}|?Ei*vÓ~c/pc(\2nyr wip3O(_ѡu={ơn_3,٭XƩ<wS߽ [F`[ރG&ۨiC{⽥;2~^l߸v-Zſ*j2cl$_,ߙΔK,9}dY{p1 8cx3gN繱h>Ylnu-O?|Bzllט|YSrx-5S氡9aXNG}=66|!?3xnNqGKЫo]Y7km_Lw9zW$5KOpT MOv \ƚMEx47G6(%l>KptZ]v -tk#mw=j:ϱCx`|S{>⎗jkme06 fC˻s"0mTpSO moׇXm $ͺ{76'-bIax3P0gs/VfM8QΈ$%*: _aŨBR@ WAWmng ,f!UyqcŖh^Q2v^\Q8~1ooZLq:bľ$|opf 9zk ~A٨.l]k}ؽ~ ŰyU!kO^=V Kz:DRH]OImkyU^i=+GE>5F>_ӵ\жoc5Cxۭ\yI| àI'5Uz5vk7x&1֜yۍ{}kJQFwT[x;? wg]9\&6:'Ze^G6=)q#hJ!x8bW0Tě:_L{_mEU3ianP-CRN*X3o1v|\K~f~KcDN:G%'/IkjzadԵJT5f{FOh=zVF mH㶑փa}[8% }-VzTaH :gWtx~>UvG ܋Am^1ԝ;'=̰ؐCRXUܶh K-DS~y-2]-v~;'>}kէv[Ws _Ȳz%Yt {sm5e~";v/د j ^{Ct 1u0#%?v(Wn_5UzMԪJȃL_Jٌ(RNQYwU΂ fPx~G@ V6` y1ʁCe6.. L޽ŋs}^g?Jgx7'sp[|vUԤCgk *î`x'oW!uGזrBSZUWϲӕc{GE6ITkE?Np_jzGzGbψHbtqokUPݎUcLadr0|iTY[]):E41Ԟ o87ޙmoDF2^yُNҕfΌ"~yWJThX^y6[f\}/|nRSp:^:eTT혴Q >~Gr P<  c&{GAAhHA4OՍ{AANpWS В?X~Ae«AAAhQ@7    (۽=z " x^A0t/A^AZ&Mm$ qqUT~r h2 J++5@  S\.+231LwnWr'{wnh4n ʬ{A9r1# x$ Fbu\ zL{AQZRB!CJp\[&qbBνnrpVSn;>zdS䮻 "xֽyAyG_OMY<̘|n>~oMa$^sCÒTC `ǂ-EHz#WޣאjA'@?u7orSEV^@91"9C/s6D/k,ܲ x/=<4^Wx BsFE<r3$$X9wuo㏾k/י˲.2SՏ).,_Fn.ƎӘ[z>I,u?5 B%?ouyz%:TW?⃗ѹc[2 Udtq߷#N|#< YMړ/λM"Q^Ahh]Կs*kVЁCltŦ{IRb\v?~=Oy!=2 ׅJM\N*|Oh 4;;NcN៸.s)og[ߣ B30]ןOkٺm#'u)JkSQ0Rxزq OWzuP7D:$)rĨA<"\I4xTx9,*goD-OOwL[AzAx"J :c_hLQID[pؘ6>=:]!UvrV;V~x7I3xYn=}bQ^EhZ6$  4qt'${bRR2;ucEN  ;;W/$wW窼jn!_]'ry٦VAhD2tȔ1#7]~'/act z/%a\5j't44x]@l5w͝#1u*&45|1r ]M8Q P\.9vqEwz4Jiѱ5hzdAcj!=-ۯ))-=*5 q6- ~#ݽN"  Q8L$'p8JMl&6JٌZf^ B3EU ff7EQa { Bsq5pe<<ʥAAh.jGpz9 Вj UADSɍ渑ʚ㭏  )5Ƹ `RbqVKAYQ+ܣ?Akx( BU֔Q=<3TjX%iPAdt_GOXIENDB`muse-4.0.0/src/doc/pics/organ_synth.png000066400000000000000000002011411404105311000200030ustar00rootroot00000000000000PNG  IHDRsBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1 tEXtTitleOrgan-0 3 IDATxwTǿvw"bK5Fck%F1jDcET{PI}=3;s˹efg x|ys޹AsZli]p… .GQ%O﷢[I)˺z*mmoHȢHOc^O/k'X1cq1H܁(*T>@Ni@^<k+h{ \o;kI|< +OB7G=?t$ ijS3 pfv NKB$9O=T]m-:>b3fyba,;QV}K=9~_e!c17f(Vʮ%QyI ӅK/ɌUt~jjGhCK4jmp)*OځWid' y3IuiuӔ惺?R@2 L% U6_}a(Lt,e'XG ɩVdF4)MnqnFri[HJi2uXAaIdG- [MPAb =k1fPo{D1V\IeUU5xyyRjjje:u+⎢(B!Q磥^M]aZA.lq-ha!-iw9LzX][ˡH{'Cz~^4>vԿN։\{l`ɌhOծ"PwԕelHS C̘j(П$'Nb16nȐ!Cƺ&Xzk:d#C cӦMqTg{& PW[+]W[=|86۶%Z,xjkkDSl޲Vtr:b,:Co]]~ZæF>?yAdp6luM'A}d$'jRamda4Kʴ d\QJOf`$Є,d!YuN6qODh HcF-RT}'ɨa礻F1fd)[Ve$,mw8_2 |9e1J6 ͦ3/՘iTZQXDVF\WWG0 XG]}#X t<8&uuu%m_\{5ZZ$me^ SWWo\[_K~Oq4ԇ%/ޣCw`49 &$1(TTVгgOZCFzIEE"#iVu ީ%%\ļ"OH C\,_XTjBǠk/LuA>*ĊĨx/z-]oʆN/on%!?ֺ2|{T~=~!y+fim MR KzKkoo'ɮEj' 4-S; caz=7 bU1{Ɓ:|>łƄ\HFߕE@fgB~;>B 4O cpq [TU[g?M!1l#K$aȧ5̩l.3ʾ~j4Ϩ\cy)wc?=o; Xp~Hs>'\OialٲCAQP5(H$H ypP(DLDL&ItLj_ғI- B*#&mi~Z*@\U8a6* FVF 2cGFaA%Y¦v-4meEQ H/E [".}s K/\ĘJ޺^y /.\ΒOo+S5 DOOaBDu ,"_6UTEi+Ԅ!%$[C'r\kX׫$jh{˴Y 4[牱|9zX-X0qdQPLbU3gRTTWJfΜI8ouF"1ʫ#ēO~?3<ÿ}Vk;hf$l+Bخ&ȮM`ƍ;(ՕۊXh,q(EɩǷ|Ɋx7/yز"mx)P(D8!v38LQmz,! G~ju5 RY}"f+#U*[|TKOF.ƈ~15! PIX6YWScM?G+ ,J֌\h Ԓ8}kzVCy)()׼׾ǙLrxZ].Ɋ =9zPD!##CkDۜ2bG`x>5^%Hзh\^/'tryjS]jU˗A]Ƿq*?1& y1gU+RhH4? MK3/u2f%/SGCn8ӈb}Wj:WᡇmEXo=7޳S?_`~xDͬ]5_~9^X+Cx֭-[>z},^%%x_A2N۠+ (M,JdXM K*leh,ch\.<ז|ȽK]HʠI!aќb'+$R!I&{9]f<#l 5{8}|ѣCV˗Qmm^?11m 么   $ws|s49!9G=LK}֨Mf fed`>'٘$c>O{ ;J-cDFjEzb.#I& ^ҒR,ƺZ~q zWP[]uEKQdꕚعa/,_ζ*J:R'NyzVXNuw*ۋ~] Ӽp59< w > I3.cR*G!ʎA) }r| ^VO&ls3Bk> ē$ 7$KeڑxXh" 6 z.ĺ|bDvk*إMB4TkWJHV'C7y0qeo벧ij ^}{فo\o'A_wyBvGjWWHl/'N GsE;^h'aQKr!!1g6U2f[/$#.#r-C;$Sc̾'tGScZSI&cYgߐX[;޽܂||u5|{TUUb]AAݻcӏe8j|xV}Vq:07mdu3y_r| D%c|!Ns(#sj `:{/?B=;t9P'o E4> !集^S4cta5fYӂU2V:?)䇋 r* GEgіj.bĢh*XyĢ1m - No5ٿWP{;dݙ IER"raTsP~I]~ ц$ӉA90O6ɅD''/Xĺ'gT2л@~~>yV;'zs0ͷߑQ(#`K,0*/n'&M=yxJ$w23dlxE6Ak&ח‚6lMXn-hOQQnjOt =ĪM,tS)"p23[SsH$|Jʥ$TIO`F9~Ke UYBJ>2HmJ4# ]J@nԢ)UJBy3""_duә1ɵLEX.z~ĈFªg6gҽXE"վ$CkGj';6v?ld4YQyָT 4 [vZR8}rc(Pٲn+m 0?4jf,gdIA4,d%X %ini! Z:c:GiiiIV~㟧!&ZZFפ;Ѱ8\Vكc[owZ7CNN999 ?ԉw@v;Oۨ:hs$29YEt";fa-gtҘ-T-LJD޳`Hf 'RH`4`wav|mI8Ӹv; Cʜܖ9&ؑ SK]h$d `ЁΎ ѩd6.imddua5A|!WBAe'qoM-G:U5ɤY g@ΓyJ섬ǢDž&KHq2xfuAUi{IƁb"hKNHߴꜩ$:UіA%HTV[-y1˞I8'$Nk#tInzzYPh:XOw#sV}^LǙǸ# 8rB:7-Ij),mZ$$m&mBufI.GY"B0tJ2$2R5 K44A4tRi7#qvIgʉG+m與N![ģȣ/K.I2NA?S- LdjeASzj0-8&"*YG)}xz ]"wL@8֓oFw4 %EZԖP!7$yB*A6A,d+39@ ٽ0]uD$%o23,ȅ k3ŤsaÝ)a@,35?tiM Hg/ eL< {b&0O0d-cׇvbɚávFN -)2l̫ȇXaE> (DJ*8~[X !!  ́xtHzJDp;! C{>Xup,H'^.d<%;d8qhdGc{ ${!֒8o*dܐ6`l>;B IDATQd֓CY1DD'YϙMj;RWlVfJC6u2"* GB%a并zN Im d{NI&FdĦ8D;7y;zlC%#z$lɇ鎆f_|~(B,#`ZsCr:Yٶ1ő&`3B.>dPX!Y ƈDmi:B%VpTmOΛ2C_mdrK24 :@ ||ъN%ձcv=w-Bඋ;n1H:U/ \$AQ=z$/TyQ~uZs nii?@N%#DJTcBN5FIvuAgvtZZ& qbguHt^"*uJrL>dC}EW斁CTFi I>b)msRS]il# GY>M7s$i%O)}G;/h&zņukٳ|Mu5}X,ց/JjftF뷍X8NǑ, Cb$ùjAψDTWS޷\J-* |Svln{B7k6FJ`P0J9#\AoAXJFِzJVY´zb@i,Cgm IE=(ETTVRRRbzFd4Fҧmq83~idž=tj(#=e]4I6(`%=6"Sܭ*Ju'5^WRЖ-@\D>JôɇEƸg="tѣ08P4FSs1EI/ZhJ(HČv=9blEIwOB(456(J#.%N%.Stlo'U;Ra_rOrj@^ۖ5~[b%b3s-;]d!EQc۸b,tSm)J/m7/ܸC.% )M)e22L0mwO!#6xmHG 6^adm`BAE?e3,-">;rCc#=c..c[H[nt*0'xچD*yf19Uk I67+*a{2;N:43/UFs7 B^!RCSv8,3.Z4dVp S, iٜ46d!xHk!Q8Vnء=ȇH~h3(68;-各dH Μ\w=&DaV>ByA2s#H8v5bX[#tik_oK2h}!azoٖ $a[C4lFQ-;L1bHɪ{&0MZ L{ğ.{ևM̼5>g74Ǚa<Of3u1mZQfjG>!Ց>}$tK9q`GgODtވ 8[jhQR!x66*T֐ hi&s1őaPVڹ!gaCwﲹ zN05R<&;!#JL6K (Up?9m= oeW}{YONѐuq4}͵'>A͔B)a&a 2TUUkSQ6d"B@n\(,YZZ}jiH̠~mk :!/Vs}h4C;8ߝztz}ʎ^9qxρے(I㱐aHJNX<¿'n߁QSĨ 2?Fg棬t˭X+4PQHH [U]yX3m/Mlڳ.eXXK !X؎HF~o8~{8C#OƷo+_|ҳ% \f&&[&D<޹R<1ֽ.a39tL;S~wYpGG?}&L`ߞˣ֢ }L>)B'&{,Wg- s֔K.2uU}v⓪X s֔yS9cF?Gbmr6CI3]Ykv RI=*fDf} OLN>p"&H$~< G94JWs y9R%d%yg9I}/ܳF D-v3&kG;>Uq <|ٿY=w}A >GyO?C{x6Iv3'CG羻>N<]qӆ .Сu.z cdf@*|vA@fk]gL+D(a"J—=6Ⱥ%8%Ѽ 2i/`Dts80??GAQC"Qju)( RǾ.|~ }ӳ{~q,` Gm+#k 'ʾ+D}+Έݸ/Kg6 y#ɓ)/cǽap L CE ղ FLJg*L"&G7Yق-+xgq7NDaN>`۬hq̠"O9neEs\ﭯ#//N-}]=m(y.}Rȧ|}(\*V3Ű'q7 G qC)JiIHyx&p0\_uXI3,JMS 6P {@ }LV]q\4QP8kB<:65xxrN$&ãX@{^y|g/]񜠨(OʦŹ^P\MSn/nM6c[L J#C+k=H-.k9V'$0RS6 uots ֍pR6Sѹm)x$r~~o^14FP~MD r%' z*=!4gY".tmtmFQP9. SidkXIeW7WF8q9= nc.l. W㫳8;k =_% q4*w:ڟOS2) Fk!(- DSjOόs} 6 KPyIƣUms-M ]SSwidSz sgj7Q=[@`&<gk} |(͵D {J{ߟhߴV sIb/Jh~ ||={c%mE"ۋۯYR3 ܪJ#nv62{$-1OӼSQX?/87TKChSTIST@%7o%7ሣ?ߡsH""uT&//% \O3U!!L[1>~ +DfJ62,+1𜖤m$iy61 _Q0eWʼ@`f]=h??íHcjxU4FY'4?5[~_O}†VMb`LMb?q#iV op4DYԏُ! ]mHP w # m j;xgvu䴺rݧp /sタ|J̸Lፋ 䫎oןAF|^bLipy0cXp2 _xsEC9 T޿ۏe3wSF?\AdbB,&λ0 l̯v3V T~W8I+7=ɖ>zL܁_}#GJ׉2leo[ FS΍I޲M()xDEej9ɬ sWcVߎ]SZ}hfGu0=ivK._ݎu7:N:7bcnH)k[IVRvil7ܺ(!Jmod Y$F S\ta$&l_&Rgy!h-P,sYQ}i78h]:-C1 m+p_ys@d}LĢk]7:gI9mgBbR%:NCmZTn%q+Ze:E(ڷ.NѼ*bvB[ZMUЖi=eUYc;'"phHvHR."އujBEF 6;#tdfU$Vە$}B)H^D?ˈw3$:Y+k;Jgl1lHpI ^~KE{X=^Q/j鐓Vy&HMt]#xebёW…[*q!llh-,$Л~->фYyKmvl]IqoPRqw8\d';ʚR :=9v=ۏ(E{o/EfB,- bVJOؽCӲ}+Z1-%.: T\t.|߁[)m&ҭ{KqרK$\tuS],{{0[w۟ztr:fI K"\lмÅ.ОN ͝ g#R[/iZO۟m˺.\t\W!,mA&ݾo0p TRu,.arkE{FbU.:v-O]tF.\t^B%IE+,_mbӇ8Z]p:z!h\p… >x!˱.\p… Y~-E'BQbHV6z 9.\搸'D$A;g+ e&<@?Ixx<~wغZaѢ >_yf…8u+lw\t!$~+.:55 2Fb#h4JSc#{a功ѣ{;{EzXrcwǠ/(lw:MbJvu׌lb1BpX,#''\|>_=vQPx8\/bUUU)ZG:UU+v7*JKKN\&1wiܻ.>PT\ٮ;b_|9{L$E?kVgd HaQ1@H$B}]-k֬BxlCмÅ(44SGkhiiK.eҥrr~ [Q|)ύ} %F~ $C`hNVv!7\=4cƎ_X[ˮv"XAnn={ҽGO.Y֭[ٳK:1$jd/\l~@'eҥ׏#8Yj~L: m^KM >Z?"ru_!՟ɨBCnhv492H~7UPjbT Lw69ؙcsx1s̱Vp~JLrk^aCG DaxĈQR%tq?H$n4X̞|@Qq1g?H^0g#[[Ja_G .fCgs)\K_/qE/9fcU6xdIbuM׸uxnqoFm766b+#$ FL>a"F֊ P3 Wt6IETUUꫯqai饗?_~>z(#- iuAv'ټvxؑVMO)akMnz^cQl|v.IzόZ,]6-L@t;<~ ͷ5?Ϻ+H.~Sn79S7NڹXJ#?̝Ƀ'0(nn(`wX\эIR%o㇬ Dqr^~uV3c'<Ȍ簨a%n;/"">E6猛}MrSXXȣϡ$Uײ2}|8sם̙dFEawۣ  oQާ79ZvkٳYsЭ8;U̅7cϹg3|}=ì ڏl@-J[IUTa@o) x~Y]p.7\sϗ%q|t<Cez_ԃ/.^soK>}4_jmh4ʦ-zCo*Ex}^~+9ǯᰒxvA;]lOr,vBx<8CC0k^;?ܕ;gEi;nPE7oLPyExtugGsƅ2F1 SbxPs+x~L9^>]+R޷oFyN0h`B-!|>|47K^vu! P>?yyAZtag8:(.Rb1cGEUe}2ki` u|KM|5Sҩ}()TES&υ3(|2*}ܧ=ÁX=Lp0}gM?0O|Í+@S=#7h_FP_1xAJ% `Ws EYi[z5^~֬пl+JyD4jJa_Ahç+AQf4wh7 T\t>/UF"(J s;n.'jFO&6gjڔyNFO=kߙǙ-;i>yd%tqrqЃ̈́<N6oʭ^ĢT/Gx{A-V;T|h*kϼ ]AWwu lNjb]MzwvbMD{Ď@35ٿ4fwog/3a'w21?{5+Y'0p`2}ʕc3fLp|>rYGL,5}@?34hsiBQ--[-6JYBֳ~뛉$×[@aQ1%=2tq#K^/EE,&O"KK;i#Z O(-+1"`k' Q%7so.N%nses^yq/\(ω)\*>GJA.^tV;TsoaIET|x;>ڃYL i"g([޸3‘Uwd\ExW'98M#н_Գ Y%_)'Mg N>PK ]|Iƶ}>EEg2aZ;|>Sx=2EhO|Yz+-j6HmF,wP:b<y/Θ˚d\-YWiދy}Q_{ƍgϞ@LJ'Ó06oo#7/2|^{"~y񋾜x{}.~%<#qI3-;P{^ۡ:"C%x,l]UIN}NdhhC8a=u(zg}peO.?\ؓG<']3.fQ[C}I1s\p|a؁#}}2Əϭ>gGgazv7ΨP@b{ǥ|g9q‘0B4y |ɧZB1nZ s/M`O5|mR#o}//:[Fs9 ΈGt# _xDvlݔΊP]]/kװqFjjS[7zM~(-puݛ^/#F He:&'vmsp\{-'xA.G0l0ǶXv-V1GѣW>?X-[+%tރAү_?xEư['~;ZkE3ky[}-YZ{q\̉v$gaּp3.z ㎽ij?iD~A>݊sɆ.PKvO8N9T֭[˓s2ӥd9ׯy 7mbæM,_h$?ৠaCÎ;ҳGllKHBn42-3w}K_3מƗ]fL6(~ W? mY!:vKQS^f{y( ףڡH^H~(1#GU {R^!Ʈ&WʭsAzZFc>A(O9ֺ$ 5<[#$v|!멫\ϊt|R*L\Uk7s50BAy\ukllWdh|Q|wm6+{PWS˦(ރ`p*DŽw7 !hinZzxy}Q_|UH_8V^%TګERrd~KW>cG2R飬wϧ+Sg䃗xRnǹdfgප?'-8o})9ݏBcAzh4J0?ܼ6kk_ EѮn }K8є''c:߽p??[%r%s=ש8AM檛^g}g… GKFp… o7!V̿6y\\qddCwv9v&wE>Y/B?9USllp…tS<9_^GrW ?>z /䵹Ȗ/x˼t̛;DhjjD"x .\h-z^zu7NE[ڏo܇Xl+xwWW5h5_~}D"C>:؂Bl޸X,f..\pѵN 꾙˼29V~7rr_.ڃ$/h.k<|$Blt\95i ;ֳvC4%1``XZۏ2p`"ј#M.\p!Jt>ᘋ9vH{SaN zc*;{>/=Sg_̂h>;x3]<\46F_{D]^ͤ.\p]nD־ɋ?K2rvB{ |:H96b p\NJd  ]8ne?8X*vϧ xW9#7`UUU$^C'\xU|I̥׸#`#?gbob]UUU,]h4O3=VD>G~]o wB޿gR AGpܨg_x}@:&0೹d! +ؿIS4*RoKJ,FKsC8p.Kiψ?i/pfͿ3A,x{i*eٺZn@ϭ4٘^PSSŋ>r%@[S]͢E tV(Xn/#S0ϡ#зzQ0{oZlS60]9bƹә6]]\b\,]~qGeP@?Hv8=V4O퀕VӸq 4_WSc#999\;֋bB!01ECnn|G6XBmoDz~)f[?3<;t@;Ǟz*w 3 x)*.$8/G ufsż ̼~:vUCijKrg4:/]Hx EŝJ#石ǤIR"PT\̀e*gz4x0>"5^B ;.$zϓ]U_$3d/wեxዕmo}'L唫fǮɿ"w|U׀ B*ދAł * `EJ!JĆ"6)R (tH'M3 dl [6;wΝ3gΜ{xIIN(HtkX*z=x`T knI$5@`0|ug p'Ȳt~s6;ھڵyK?ѭ`ٰUt3MӘ6u*C;Vz6??l~{zE!!!M:Z- Ɉ$˘LFiӾnM%!!R};~cǂ! 0]`ܘ,[҈tLbɪl S Jh$**&M[xf:.s<3=e[9(IVOE:B܇08 EUeK2[HNJ&V$<vWA*!o]'xg9-d>*s)sDLQe8LSHPO c>(8=e;̣ݰZ]Gdq!#F-双c ĀSkFsc_pbyZ#Ξ,%(35$$EKr?o.Ks}r.#qJuB͚0LG.ǎ##Ѫ]ro>jLҼ:f!Zq֨1dVtѫN&9) рDUdYDz3glM@vX F# gu2Z?o{R}-gXa}~#}. VEYiSt{YnuDB[RU)r/?ȻI\4fSjXcd!$oWqCoV G0gMcݳDӵq ?pFfLj2^,~K?}wMMI$g7_Љ5=IJ4&x7a{78z-5σ8i0>^y) 9k,qXVI*z?/. GV&,:ܒN'ZehVrEh4Ӵ, +lppxUIWqC0$q ܐe?v={.EQ c9K+0_hh4e6 wں0gm w2Yvg魍EI-:S.cG-,aPu7llHv+hGqka5HX{Oݰ.#>|51ݮ#v9/fM‡H߁=Rit_\f@v1X ٓ:6f7Q7@6%_>P߰2סwk+}ձqrkՊPUT gf6&ɧQh:,G&Fф2Wu4M`0(BVfO^ʝ *=czY'^MNGpHII|W4oђjrg:FOHM8˞]0WjUB n^2 霱%5V;?9foΝH5Iu=eӵ[5>NBi/&FU%o͝f OT@@2Y.KleЛs84| S[[nģf\[R;v(NGTTTcǐ$h Μ=MZQ]^T!q $4M9^W:, \6'hL]32 /Wp+*hcth,;^ Ю|ãsʳyS_~m+118qדdlԌITT46ʿ^hp2<nl[VBٷg ͲbQvku{m-ඛq̟ft~͉kiW A^0W&F͵r cMLuC'Cq$oF  !.od7bY?c\ڗbޣ'rA>3bbc/?r0wyM4)Q2N':*l}ќ,$F'OGkCw\N2T(BOyFK Ɉ͈E/lf{nf ,Y  b6Sz kJ@@fYDWjܜ0_ӯhql.s(7)"TͳI4KC!r?-{kLޞˁv UG+o~O%߅ܜ]7^bW'@_]܌p3F } m@5jL=cVRBϖ&p{>l(>ypP=flt֭[hۮvWD$V+lh4q.-tLPx[(Ώj63NBB+&r4^<^ eYj`Q}ɒ'(M7lJ37.!>_>@;\D5v叩g0EvᾉPר#mͬwpSvM:܍ږyڧV6|{4۱Y㗳?)O/Ļ?'h=b =%Hͮ28 ԄGfc.:__C7<|jՊYhο hޢNODDM8m[ء#lTUŭȒdddeeRAWpRK%a&:(0w[*PA)&8E^'/j!PNHdOpaœ ]Mi>Ԗ|̃JLD]gk# O轨KڞY{O22}m_{-U񷎐qv_8i2/MFmxjZnz^Oq\9vի?q&qf;8Ⱦ vԨ^]t\ŔrAp i(-0 0$*.W} OQރ2~Cp _Amԃj%22™3g!\n7F:^:aaXb站N?VRNVwUV0=!ie1JЇK7_HMšas=8UoOj/PUEQp0`0*b )I+~wevcX1]Bb.qWnrh;"ڣ&+?/_A ?~V^Ʈ/[ob.݈֟b|]-Ҹ\.$YGDHL܄KI ~gg;S(.\LfLrv Wy/bc z)oZΆ3u;VF|qݸuP7i)9Bup+"@ \J_]`;9Wqj2[~q/ER[֫{CP֤Q+2 {9.tE}.7tjEFT Wyph\rt|E?ݞ .Ts_Jwxszۃp*HiKQɱ*"yn{J _QZ-MJr2_5}oGLL,zXoeLgSSM2r6].>@_5U_Q GQz"le4!WwLc˳h/l R&7Ameh CUU2QէSVVOMh#=۫jCZ00cC3w3b @ F[ȡtz{C_gSBǻnI md uRbAeϭy󯿈_[ʯq +"I>zc܈<0lxIiSз<gy}q"TJș!.dӦMƂM5Wr/g=2{t@M{SgrO2fCh([HNI_VUzÅ99%*w.n Y'ӨaC*RP.̥R&]z"nы F=N<ҷ1HN9ǟ_-`;?sṶeuJ%4HNNۯ妾7|l_jGz:!A(Nórƈ%cI~ i싏a&rU$5%}4k֔@at8?_ۅb 9ΏȔ1; g l3wHN >m_OrRR %YPvh}-.z{͝KpH+`i4W3zTaZ}#٭h",XpalIuڪvn`0ph٪)Bvv6NUUeшdiͯv.-L*̉,DOne[YienфqQDo1Mvf:gNq!d=fA_NOLr6& w6pxrh|IyEGw^df:ʩAEJgv3<5 \fY? R(_~Cǎ^ bc8vo*dԮ]z=ns9r? ?t"UMmEϕP*Xby/ƌ/.ӑKIvo8 /{eQ<=ʥEj&K0lPƏGqM71dS_]r PUe6#bjz_LT,6+6?nFiӦNe4iڔ_XyPD,6+;v"7ðG?V=pABBaaaqRȝW(7|ZY@Z~l 7a!(l\h i,!$4&V$<,s3*: ٙغSM"0JYgc\ n;NWp\ dl?b֓ { ś(>@Ê?LԴ,z7M"mБGϧ=¤YQ4jv LHbcqЙ?c`T$});DF%Yb9YtiO@h~F8NNNua& PU_.J"7SĴ%0Wa,xo#{DbN֑`D٫rhz  7`['[{{4-k6wz('ƄKIYRIT͛ Ӂӕьtn^p_O cHmd`"$o'(5ox /nl ׹l^9ٳcђ aђyϛe~Ttcbq=هA'Q DPPHhݶ{=%t"(+V+118q sn@͚5j/* 8x/kbh׳;4vd4ukW+#ᏽ8kNa.6TY A%|_3V<–yitaJژc:|G*r~O0s(^(/H Csl"ܺIX;cߠSh pnGkn/f!2-ek&3+MO%)$^bhI/EQ0 >}ߜ_TrA"2AAAfWÇXJ@?fYQ+~qj~۱nQ)ͼ_D/uȪ>9: _1]%bmǝ;Q*CVS?u l<_vps9ȈsI4.KVd~cŷPUM,f't:YYLFf~h` Q2.-V (GdYjbXMq!U%}\Elj֝Zͺs?0rr9v9̟ n6̛Ð&'gTRyq{3J [Yud1+uf{[+;~:/$HoؒQO:wME_90J3l@sgYC<=3J}iѿμO9X###IyIj&ӯÂz=gU.Ws$'$ijX/飯FL\1{lD's/yN pMpHbn$ˋ!o]hWx+9lz-~07tcSK]0o<ЦX:%#n016d*h1N!ǡ8p7 pQͦ )m~#-->\saΆ {>^Ϗ{Bq~$ &#'O{kCKEUN'{F3.ʋA~J":iƵ-SÆ;__Gz[o۪;3̳C,|{=kh ;tsq. 9;ghѽ]8%[+nǝi#˙y{#Y4 lU1~niL0IxݟI@ Bfnݍۀk'!7bݎS\;>=jnn Ed'fI} Ԭys~G~޴}`2MP͚y?'# @mJmtϻ"I[`-3xnrG C5M0,Su_;I#@“/eLFMtZ[locnY#ehc1tmpæt:X1~q 7;kԶ,c65?6Z?mX=w=&;#I6n4 TXܝ Io1pcfGV}RݟoɃg1y{J 5ՠey}j{|pҾCmʖ͛ڭpйKBBBJ|*lݺ:FU܊^˙O̖-[̢~T鎥۵!"֒!2 rq!n 8^11|ԯ_u!pCsm9qAv}w֭[nq=u,ˋԩS9vd4iJjt2.E%1!} %.&ZjjW 鉣r5zB GUA;̂ʏsP:ULns!7hpd:|zXV"jh6s=uCvج6ԮKxꄅbZ]9Jr4*J(*Aql1p?*-[IhXrUUI8sɌ/`ZeIpP def( :łV5'WՔDϏRPSEe"48si8zjիcZixHDX h S Yt?lViyVe@Rb )IɤUts*[bb2<1WeUu!dd*J @ ( J A3?rB A$Ɛ= C A!T@ 9b1A~LpT!9%*w..YhؠAAAu)W(_UXI咚ƾiܤ)Aݜ %5%}iڴ)@L&7*zL#GhѲujUts*ݎ^ӲEKCQ8]NTEA0LƜ+D"1=@ \&$:]Q :v .6]UU%=#N DovIKM?`YIBrTD/A*r]`У鄂\Xm6l~~݌2G4MiҴ)Xu=χ($&%aإKk'Fd2^:ժW'~^ ގI̥"_s dUׅKBI}մݼ?VM!]<} l_z>az(?8q}19cRvٳcђkhR}eϋ*M{ U-vYQQ3DPP(!.'N Kд)ގ8д CW+'%EQBQKig{EE*68Ҫo|V%lt EҼyk'^f=1=ޚ^K9)\L?皛'jg0{$ Ƕwsa/1 ]Ha&c`Tw2՟"^yH9(?}wMMI$g7_0\$ّ@F}!휏姱 6$ oS{k9g~ƉOsKuYHLDDEcbcYŚ{rs6,2:|j$e^bh4(N9KRz'r/),sn%_.Z^^.L0EҾ?+z<<ڷd>׵cͱPc%4ο[3ɛn}N]@/C[oX`CV^jcwՊ2_0[đUgAqߥqd90 &^*i j6jt~eD *-dN"}]6w3r\ܮU| ]B!m7˦_"oPPA`\>ٕm2vs{p<>4JXkyiidƄx>I'A<4sw<6-o+?#Jį9-I'bJay5Q$;%r+ZJ~ozv}#⸽)}z _\|[cY؆TL9tO{6 6¬*ܱX IDATѬ!7P3 G=Ȳ`Cˠ7phܷ܈G?[͸ѣ:QQQˎC$/nITlO&&&ˇ.Iɓ Ih*KjDWs_D{s1 eHtbyp[nPΰxjzF;"7T>Zԟf3uo4|"NƪgCH4 s>|XE,ڙȝHصfYV9bfv8?CJ߶oI=%yQ$M-=Cf2*sbLZۄo s%p0ݻ9*-_`fg;!f0G VdG `-bonήd1r+cYkйnݨOaߺ HQf^1x ~g/Ăc'LA2|PV})!!!$&&Сdge1zK[lfۖ-m݁`IZmlٲAvTtA!FJV߾n{(d{c,bG2~!WkQ<1)$g7{RIF T`rRX נNZt:똋JQ*.I9#ߨlݭGϟFܼ/}͎ ܏(ƅHEoʰ-;^nu֠g9XO^˳Yn ^OͦM8;5~9S.Nz ~#гQn(ުO^&<2ns͟tiժf#x{"1=ś )?SQuV:v˙zKz=:Y`0yfԯW?!( y3E'Eg~r-q?zub뿝yk(H9.gxw֫lv@`,U]h߶|&tYǩ4{Ηs~2a+tPGۘ΅堶^XS h> .xLU'ֿH홵$9eھLZv=]o!/ Sq 70qd^>mӻ5UUD tqqV&MR-4A !1} vLu+ r @]*rF>c;o^nEv|,w?L1tkpMݿѠ &KϦN@;S? a;&fI(g0@JVv}MÞQM(s2Ay  jѠ~}jɓl 6mpԿԮ]QV՚ ?pp|! |Iu2I FN9Mfu*y]Cɂ@a?}E^#C&'RZvhpvO{J'팙 4i=>gᖩɘjuqwQ+Oh:?pjNZRG!M]u%㫃"֩fNA>ӓ&s 7ҺM媪pL˃j&AdddVuZ-t*+kW*89EhܐeOpmg>3xh2cZϗ2mJ/udԅvdUvAy`r(8n̷>.nDHHRS9zoרjCNmvn8Jhh%fE44MC$$I\e$I`vK!A$%%W_ҢUU ˭>p?G @2R\v~{1D赫"`9rd`6$HNL$-5S(n7tYkd䥪וNz\*o:]NPXjɓ'aݷ+>L`@k 22o{5*^ycng1^%Ǡi*T\LTLF&|*, UDQ*-8$@$KE$0שCxx8GfX1LH,mx^-Ǡ( 6_Lbv!#>/"K.׋\Vh(˕ @pRQ KEPbdYl)G[9V1SnɄ@ (^Op\j'AC$%ˍ!*QB&/zrR:B#$]误MJ"dB 'r;.x8_υ*:TLU􄖷KcN/hH:)gJc!/UUt!AU8=%C9gdE7FP֨\lns!AեX=q(%%@eTEϫNZȄ@Pu)VOξ\>T&d9}.xy8LUDYEF*g|o!AW=!OnE h3xy8LUDRNxC,y&4B(hCȄ@PuMO-v=$p%MQTG2!( !GyߗW>ͨ.1y[BOx](L{<Asةg= >+ߞ<ĻS12W6C &w~&,הqׅnno_[󡩬s.'@ (uۙ< ÓBC)o=Ipj^OB0xr~i{XV?;R {<2!=kg;vǥW!(YYJ.R ";Q+͘GQ⫽ Ԏo0*w $aZ?{>F|m$Eci\ j7 '_^/奻hd\Gkhkk7/o ټgN z{y3捷fgto2Gd6H}{|VDyvi\!/ZmXl>tʑ!I ȻCH@mXQzwdifw%񛃤_ӈez@kO]ۖLܭq}`{iӡ5lq9\X9w-60X;eiO!_W&t =QH)p::5m2N~-d!k]R??@-8K uavc^7mG杰}4̫crПCe,c]cv4c$NSW/~^,s(ZOh9HRf@%O#{9ѿrј[ׅ'Omg"h/OZg:?󎏢{v$4B!tHB) ֳ] (l O==N, 4=ɦfH lHyZ33><<5CIڅGm*'sc|)+{Y: іã#I۽MvՕQVPݔ[WkU БU|%>njP]Z-p4ȡm#Cdߏ) t֥ fc88]zJa0z&P__-b[K7ŕ dM}X9y7dss{Ӎ H(fS'ȰNvokq WbKI<+?\yhrm:_!p9] I7Џ3olam)i(>?uV:i)#Yj͔Ⱦ:v (~0W;kJcDNKz 9'Tݷ`ͶQ=`tͶH񩣱u.tdkyBXFO}җ' ''r?@eSF=Ib{&@R~?p0?KgY~LڿmɼOo-0[PG?}Q5Ls347@r0?ɛka\5lꡦY-A^Ú'׫p=[āF K#mreObȽiֈB_i.*XM̋o:a@0ȦMsIڨv:Ơ4vBrsxD g'(XU\R9]D 5/t @ Za/PiB)CC8 x{hwQ/:?؀T"@@a'צa$@ ]xKHu-G"\MىuHRs9HvBT@ }j|g@ hej4:RU4ZMET ;! JyA8@ cDm/ _}mqV_SkGuN&@h :Vo@ hN\V[ב[eel޺h26hZM| YYDGGG|'S^:FgЁ$ͥ~u$;!Z&D\qfD}^QQMGLll[ŸJKYgzI#ɴP% [P4 YҰzdb F nNi1$dYӯ_Fi_( HlH*HS'Ӣ#$Ơa@ ujIJ׎V +;[EQXt)ÆGö;fïuƾn,1ĩmFdIl/O,* H0hux!DK 阊d~:hMxᇙxU\t;XToUzxlDT_x30Z[wUn'p7pN#cy5jO̽f}5QIz4>~1^"6i8rgb@-7۩99J?6qI]΂o;onZpĉ+;|2ڙ9@>fPɎXA\;ZI#FC[Cy[$x<\/&2|ϬY;!V+m"w.z>Ȓt&SR>)h b~s 'J_x%E/xSͯwnzacx^==DmKf]7d_` s>O̜IAL4 9%N?A4 "JbPB$'~7\i柅6VIdu W4Esz;***}rs;X5V.D4e'jMid{7Όzm~mtVXý.̬,m%[pX>L *Nf*kr@܉or"ܯTN9nvΨa_(Qr+Wi^'*:W^K_S0wˋPc|CMWZ%v?US)]#l\N蘂h$%9,Fc[SR}; GNu֭__Q~?@UUt FcHm!YȹenfE7eN!d1dqŽY-?QZSUPU%2bH1'.G3&&Dxh3`ۙ3bcc3_gQ0ﭰ%״2h=>T%(C d5|z+z{ 1QGg&sN$ szuq:%:DVV u}UU*=sAo0 (+wiFV v@J11G G'3/੗NU[ArhV'kM{+9)(z?"iJ5??͊O'aF^=s tT)M򉧞BHIIi>=#/@ OUB_{  jxC%# a0VEoN(/&GK^._Z,^ }mGLgw8ZEQ(-uahu4 D9$֬YMIq NhN ?2$>KLbiȍ'8Yl:O岛/W?T:>Xq -\GLW ÚU)u &h2[lC˛$cesΠOq~mZԑ8vI4[. sW34Y_|s?xJuYc"3RUU$IBxaOMjqFUAM* IDAT?H*tX/f-@P Qt\TU[Z]2)-)nhhj+>Dlx%1K޵p׿8o.s4o̻ϼXgۊ #*+#o`|ۛXR^a m.+X8ښӣhC { WS9th1zMv3 Sࡨ,5:SB0?Ia,ŊqEp:3N\+IYG_}osxi-$IMVdd1bcN zqѽZ;>ӏ 7wGrNRUr+CGyW䟞"Ӈ] mE\[UU$#+ߏ]Seb =G 's!yM/\fzI ]qe-ATOz#ַG^Moh[bN3ivPO0}pROGD܉Ac OEźϿD:~R¸o ŋ;n& ˷( yB^XF9*=oq!VIXvnE"Q-'bA#@jMf`E;8 y'Y wz__ojըm//yҕW-bʵWPpSێT^~i=&$[۟~XT?pss.6!Fw?.ӌrݭJys.y>{kL9[Gƅ뷐 ]Qʪ*ҳ(jVEVIJJh2#-FH 9=['&D]=88[lk#9vi|Q} +F\m=nqsƘ4K~Np I6θw_NwPqs4f?>I}m_7z'71qqq 6,aQ|d 'Hl3KhdG[JS+8#цUUQAdNl]2VIv$ AgK$Fߓ F!\O~kzC >p3Ǽ>eFsj5|z fF Jnn.Vbv#7HgCٿLx4KG-n£ʌa;c %z$h6qD`'ٯrǬ:GSiRFsj,\SA NxȲ> Ͽz !cV4&.OĢk?;?{7HZ朷[0ȎèI+{AJCHaNܝV>81[?2{* fߏӡÇTdY[`d4a6WKNH##D'-S.m]F鑓fƍIvvvt*%nvFIƖjMZw&>zȲ(tI0Ib6AqJ\K5ؤ[WID槔W.fnH7(U{tXJ׳#j+ŹǨi^5->vEQع}&k/I`B#n ά' z.o z8mz-z&Lzz1K(;On&vpGͽ}8/V{(-,_>>s)C9=ydivBY%h7K Yzqms+8i0[xc4>WPa33yĻLcݬVs=qgdXu."rp`Z-Rpސk&v #ǐ^)ߛ*5Bj\%T?ߥ>GtRraZvK.U̒$FUU*+ٹ򲲈t`ԁ#`"n@U90NY'4vE_2~Ne8m$/eU~TtrvЮ.}N1Q,vJprOC12#d'3s.Wgy}#Z()S(_Yq86~SZ.IX6_TwWsR ) HOYii[ɌhjƵzY4lj b-̅|X4H|~ ë?C{dLml̈́?g "NJyK.eI!\|dA׳{4ξ,3846>n0L}˸mi =$r ʃ3r-7suڜ9\dvK€Cu$&&if.]aC|(ȲN1LR>]8򭮮f=UVF>C )))f߈uƐڍ$,=z RىK[[^T/@ KE'RTI׆t6 P, )I&/h6n" ZdfdХKF{XVV׷4G~W_E^h:Q+(b2>D$`4dF# 1DIi)-@z#6Mc)[|^XH&^wN<+xpK0g.lI9Cܻ`'p[SGso,bC#ln)&t@2y }jdc$`[y\=qb9iF F^ջkVBNղ%hrn!!v:,!xԣhTBjRq{>5;Ұxj"z41MMDzofsʇx1\^IKx:!^zTcآ޼i_O"QŒY23٧቙34^ɓ&1$p駇>&P*|&@>}/'%B ǁ!lڸApv`zk*-eO+#[ޏ' Z>AQ>P?}48c_:>|E| dra&^)8ȝ+'J唙f7錺fOe)M2WXӦ+NTt4:;Na)JEe%|B%_O9 F#IIe`0ں8Eb[xI Z@H_4_]rSJXc!Դpl_%H1j gr]{tjM*C7h'4T̻S@JYἼrg/ڝTS!k#۬o [yfP0γ0'z I#%% >ٚ`0M%j}5obh46똂(Dbib֠N;oӷ_?> d:Á^( .v{!Q 1$$$%1kXS$u`8ϐ.z!Ju뿟wVpiKMb\gKCn<7Ob)~l.|Kxn:rnVmwfY+vtwh]SS[1TUC4|^F1^S]]`d4a6;;a0HBTt em]<+xp3k[;ͨjb>5NNuԭ;2y[{84M*lӻ~Ѵ={eݚոགྷ̑FkXV؅Sn$=TB D[Z@9ގ0Gp߾%>7st.j&ˀ/#q|'oUh-ւ>4kl]dQ$)X ~4vuz^}HB0xH]tdTU#[)i׬6z6և;_Km/ _^+/7v Q=7gڏSmEszع=J\76 =囯lF}M/^*arfᘚp)\MUmX5ݩ ΀lL_ 8O $+~rƝrJǢ/-t2z̘;5q:ƚ5Iڕ` /d4ٵg/G&#=tAK x)ٷc{$y6[O^`u=GjS khཉ:*UdddQ#g_R'$ЊBFzC7McMX'wk:J6|'"^;ŷG^Moh[bN3ivPO0}pROGD܉Ac OEźϿD:~R<7_ͷ3v8L&^o/FQHR^ ŲeKq7_?~USY]+Ah7E7+ڴ5unZ^Xz/#mwi6j_q0's ȿۍ1i(@lq~9Ct i~|41ȿn*'O##ob9_1t0~{[Fݒ%x<Fx"kjaЅ[l2 CU%,eF.=+ dA1GsVo5-teµI:/ÚΑw4'eK_㯯'ɢ9faQ/~GJs2Cz/ŜGجmΩ#CF z{1,ʹwŷca,gBo'ijc\1y}-=i  UWA@I' {+@HսrmNs84Ij4In,θqθ%T ":_r[T !'͌g3KMM%77UV`|kȔ,w' RT/7{&.>RZRtFF|xdYh/AqjDi,Y R;` }}IjbeI InQusu(j4LKq:+Ⱦ' ϭ}|YdGa٤ KO%ӡq$0ܝV>81[?{SuFj^W[Bd$$oNh t$*+1uIhb4Jn77n$gOCwU(/(?LrR"&bv͛*AJFF::c[G 8(J3k̪[WসZ^ן}˚-;(ܲ^ -{8) ?)mM U@"9+u:u_u96;|8Cݹlse'h Y\X Ih0|ytDL}K'aGv ;as6}:W_y']{|Hll,\{5^L֬dYpkyv}MUTTk:51Ff4mlAQrW;( qJY~[v 0YNH,<:nc'VI,Ӯc'ƳIkA6o)ƾ=rA{#!)/933mvBy?ϸ#`k69"L-xAH+(X~ |o5tR TA'"UTViV j2WHNvwVT/1J'jQACҳWo6[[x$o( @@ d@ ht^*$=vPeK2l𐎀#*旈n~1^"6i8rgb@-7۩99J?6qIIb ɻ kWP'6r>'Mp0o4m^Xf .Wql JdC[CVxI;QJ<ƭM`F*O%^>t= :,B?u^7-=/l; rC"4xu|.#'2 d|AQ,[rM.Rq;-VNۙ)3Gnu9̞ %RT\;M##3W^{h^yu.!wL¼!//iTUL~NjE'㌏端.DvM`hC#cwzkP#xB UN; 4-i`߷M,q$2Sj:~('VmTڽI#ouzk=_W(FޣQQdP^bMX-Vl6xAԽRx.7gͭdp>'F5Dzc C8)ߣlJ" S"Ib_"|?)afvbƎd(BC_&ݎncT% eKт*UeeԶDvH9^8X])oڎ<[^|[pcY#P%;ȏSMM.UQ"=3@fpD!xo wF'AZZ1;Ov-GrN8\24vlA#Ҹyk } EvsLa4c-OicP.%;$8 |9rBA9Әhb$ TN꽟 FGsԉca|d #G%Kx<8Db[n[Y 4ߋ(AY/M&/[{fa@p 8Zbu}9 _8Kȸ/@LsNƨc0E h ^P YV8~gGcʚ>9m>1 O?^hXN̟/ !E<:_r[T !'͌g3eHMM%77UV`|-'O7AK. E;w'ӳqqqe=j/%%.~7bcbHOF.]0;Dv*; #o+z|ʘ{2u/|e&PÁr7k>^{ OJKM.~QOTWW {+P_H=Bii:JCT%1(=rrplܸ=<f3IIɘL&Kسk7[6oAQdYjF|\Ng,fZ\RAѕ. q5rB F:!6`6(ܲvKjj*q!+ 7b4F[f DEENpy8f͆lnD 8iNC!h-E&eڵ %LO[B-:11X'2qK5$IwrzUQ(/+c͔t),cٰZ,hv`r%I5;X]9C|r'aϧwsSIQhd K澅k_0E&eÁao}QU^­:}qH&: URi1JPl6yutA~?A縭gK1t~\^s:PE$rϽ= ޛJ m6tX^#xBލ,_VncyHM&&!(9N6m-V0~膈t%i&^/%h'/{[+xz))vۆpFG֍."ҁq8N@ .Vo "El&9) DqII}%,Z ޝX K" ǫ0Ml۲݋[H6o`4ETf3 DEEȲ\+x?^WgRcMX}ev'[. njfaNMQ@ l6IۏӦ(As GE'8B,q >q\ 4BGm۷c00-f[!FTTT[^JYt$;! uVi8**+ټu+CldhWi)?XNv,vG[G #RQTĠի7wV%i H5@IH2# Rf0wΝ[23{JULh[7ֵn^y|O~Î-ro<6|aē3gӯo ++3+tn23^2{UZih.V2yYv'َ'@V/}x>ۼa1Q4(ߞ|a@Ƥ7hӉnca[Yj ;ιRp6ސE$dMx~|q؍@îӿHKR2sϴigd0wޫDFE1W;2:&;;;K42R3 & +1|՗#"Dvh2@dfgFܓ%K͎dq=fҫKghJ"u(-I36XDst;/~[yc:WLK"h%{4We4(boyϊ]#dˑ(m2D'c984g_Ktt崿111_c.cY.|wMQ^QA3M ¡uHd6-F@C!K~JըIu/?^ (w8_z> gO?pA|[\9/p33v2l_8m@E釅EUUL&#GOyQ )\|%5[?ęЯOWRHKWGEr8 f9O\C:^?")Z5G/eǐ}~&|4>7xh~.ϼlr&c|]?wKOl?k6e+Dټm >3S0}cw$ b+Kd9+ؔ{+wޓlhtBT t-)uJNncʪl65DQ^&ɌbnqEuF#]:tRrS"ar1/?m-[Mhԑ.f[%ѷ߭8ybnc*W=_;Bʿ>Wכ֜Ee`!lR +BA-J}_6`Ya7@.;|\`&U< 6De1Q\q ?8';b33*//G$"ꘐ^z껗FEh0pȯtLH d$ $`kIt:%C٤Vl$f 1s_6]n$s$C6Ͽłg>BRNV~{ ؿ{t2|ĈWQ"l6nNc$IM&~%--Ah낁^ÅZ,ޭ{=N1x͆gK yuAITFLϱHY7m &9}-1g..:},x3g]'ElbJEDbUu0 .震H q7W_~+W2r(f3^WDUUzӨKPA=֑]Q"$aX4uKaa!*'ILL E!}NQQ$'w"11IRr  u78!B '":vo)fw GKJ萐pO^]u;w-3@ii ۬:vLb1sC޵U (l6+t:EeC8eZKWUpuv h,fv$&.EiSErr2qqu>ig<a{ٌC"*+z}(X,f"lX-ʉZRhkKZ|-rRR\֓ڹ r!Iq:ڮ*%.EELՌP{D6]uZH$1 pZT iMMde8a\šwhԠ3ʅ .J IDATH455Mfi1Z}z>⫰  L䢍H~ *"NPuOY 65F,&B("Np*FU$AA8);Wz(h`Z uIZ^Gzj*y+QZShn94G<}XihYYpֱZk*.fút93x Ckͭĝ bPbP޵ҽ{lt̝l=={t{?UU4MCeF#&rZsA][;͡KhmnQYҥ+}* RU5k8hPGd$]t4Ms׮8#A5@=~fa3 B3k-q94wڏQ*al% e7c>;o/W޼tYV5WUWI Hb|: 'ijmqFGh%q;v DU%GxμGbu6iu*(xP><5ϽFZYȽnzwOtoId31f|]<ۊ,q=BzJ2}FߗZILdp OY~]:][07נ:RL6}~ʴJֽFWIiJ BKdkN..&u^d֫ X8Q9(N(FY=_zg06{*蒂dFjOشboS%>~n3^cOULh[7ֵn^y|O~Î-ro<6|aē3gӯo ++3+u߫*<\G]t  tWi O?⧱Nd7=[O..O WUK\s5~K ` ?&Z!Sz貪-d^͘g(D.{0GQ4q^vwoeɊB.;0$8:~JszCݓp5MOYƹ3b7i bfO"/I d*sϴigd0wޫDFE1W;2:&;;w5M |?lYшeʯGDd"1!dSmR;x7Os#oۃnb 9];v T£k*ف%ϱjGmIg.M,Ebo3ftNK?3xK(h`wOiks>H:eu~\s9eD+櫧׷#ۋBn%ه]ЉDVlc/"MP2E6vdzv{ 8jԨ$}O>4,tt_s*^MN,Ղdj1GUUL&#GEd>e'lˆ!y-,0WӸQ9kTrϽ|=mF,_8~^~LèA_ ͦcоHzc[8Ȼ,a5IJz9;9D]@HNF#lxIfO\FjCg]}ز\s"k,t6m[…b) j,r|ICʒ<|+Y 6ʝd9~Ti:uuSrr:~لFJEQ^&Ɍf(rc4HTRW<ة{1c'3]" 0"RKp0RX`d?|ˆ_`_?MܱKh*p|o?}g}_kua `5ĐXGQ+3Lͷq_L%*π `'犫HmI4<ĭb33*//G$"U2( _JRǎHDB$t]#l=ㅓi:SHJNCOJD1DH:FV:ŀ,[q0t}9vNz{܃{;}{"+ )))۷IHMMm# m~|J%t|x-_r /ݿ]&JW*RNeϽoHp V# ŔDx7%A"}H Xn:*eee|g8NqT`0GDm6 w$ /fࡤc0>m]0vsMNiw{mZoe! 94$\\?e4MFŀ5փΝ;~lݻ{իVb><}}c%?M!ʪ~AĀT@fqd4 c-|5n ƷFgmxr{`~ i ;~?YIw+2!Ak2D+>G:!BTW_~+W2r(f3^WDUUzSwM`w8p#XnYYYxubJfc]ZtME* $^u݆D71c#'P8u13.I#aG5dƚ6}:㯽Io`wȑ#x ^LVkھw_; KVұݙ6~լav_ūa*{VS=`^.AUySJ2os&qϴW)qc{v I\8>~5qӘp%;ɹi*q &:/w#TX ȷ|U:lWv3xbn'!>]{~{|hh "ɘfpFFFnAhN&41bmO_ŝSH߻@a.s%fs s6nxtRe{&-DnRXXHAAJJ]dvNLTUپ}ΨhHLLji.B'+>szC_[chn!4xc#p͵j 7v mC8QfMp}GKJ萐pϤ^]u;w-3@ii $&$`68RCi7A5A1`YIOM!.6Ӊln=OuiVw ((7.䆉7Yـc>ThEd1bbQYY$''Wj0ޝ;0dX0Iׇ(b&b} )ZNs9 ׯ8:*-֓ڹ Qhd}JD\kJi}vQZRBLp「cu]YJU} ©:Dsk-q9(BΪdb>TR EZA3 U| '8:łR9hc$˵'nuVNs8%Q5S*zd2Es +wn]ydIjC AAZ:H pʅ?< A'AOeCD APU @ @4dYh4b2ŰVz`J- 8!4iTx Gb")->]?aZ0VjyQߏlb{q꺎$I-_Ǎ#=5f^AZ y~8n#+q6cSWps'$:*)!"299[Mb&!!xnB˅3:Zv"uFNhY߀8ZK*.bڵt93tm;SUt"~E澾-ҽmJJ1ldfv#[6{nބgX+ TUhss(At[D>&s'$[7nGa離*@h(B'ak(cob6Prň,|+{KV 6Ѵ՚4bJhQ$.]∌<ݧ UU[YpDFҵkW~ش1{_3ٹKf];ES8't}P߶kexy,GE1y4r*W#!=`Ib;eT&v>`N˟^ShQ^VFZz:MUC#+q\Ϛh2ORN_CD[aZ;Zv Bu{Qy{z,gwDbYC>_uY9/M?LWdrif@weǘ`aתgx;Oq~>GXpV$E^]ʎ![;IfpXW汅&4rR"2hZU%C&h˚$pP!2)xǯ#_O.X'nx=rUOZd.t YA-;ia| ^g(qt{ g:$9wLaWN2G]]:][07&Y6ԬS\R2٭*Y,)د-G[t̹&#RW7ۂd]"glְ߃n 3rS!WݔHf_%_' o[xapcN> Aχ|vZҠ(جk*&#$j'w,|Y$gzT-T7#?0GEyi ԣ;Y߰eg7<3!sos"Q.y.jE u [ߏl6RQ׃hl2ckKuF#]rP}Z-5kzo dO?A8 QdE!?vs?9mta!I 8Ț&',BܙI4]}1OڗZsϴigd0wޫDFE1W;2:&;;;K4r32cʕ"N~|mV: ԲxbjFbGeo.7P.xOr9_Xc+~~Lg6} =,E!S*&C@}$IP!H XB4N)tLNC#Ψpu]E2;*)2~Mv\xK(h`CSQn"'09'AbP&Ct2C+)#FY>ng\a\ssf"w'\Tu :$RRN*$*ky7 O3s`%A2{d~2O|ڕ jP96z HR-^nol~,oţADY-ȞRdm9[h1 X6oyF GTI0LaJZZ:a .,DӴS^ k צ|I7駧jǴC.gl"+Tz( Fy?s"I;w2O={;MnZ$}O>4,tt_?^x^4 u,_[TLJ)6fNzX3j(~&'"+/cs{y$)@9W_݋y/DG'ղr?C L΋>̍h0}!m?YacYYvYYxu$If./](sSƗ,娪p4ϻGuc\[zȾ \ 95u>x6m[…b) j,r|ICʒ<|+Y 6ʝd9.А"8NMz>:~ 8]ڐl9t[Luf^HDD`?M(EQ0 zW:vON'kO{VGVb8=Ġ(=qQQ$(ZR$ `M0B_fSjw2vDy8"$D9 j*lD1%;MAI*q"}H Xn:*eee|g8Nq>{M$aZٲy+II?9̀T?E6/qWPxj#+`06.a Ad">>*sDQ XmR!:ډd$8A+Z9˒ o~t `h=_q|xlxݶ'ǜ 纸` ͜uiOߋ)~"dʿV)H4غss")D{Q}/zJFlʕJ>}j@"+#86֮%;;;ds~5qӘp%;ɹi*q &:/w 94?667߰z*Up 2&,6I{Yn-7&Ct£*|b'!ajkՉkID-V8q!Uz7:7H}{Z: '6^:̹W>= /x/99,6nȲKɩ5r*GU{Lll `޳;ΘÑ"~qё$w"66YO_HhMt*NBcHHk 7NTU8/(Je.%$JK\'$SWnݨ`Νt̤K.u+-qUQZ$&&b9RTٵsqԔcOE,)G40J[[5'j"hJ:LFvo8C˝̬,q5d1a{D8|h(Xo)~1mk 4Pqpo׭(7ydtF3Đ$jmWURW1{vUjFptU  '8k4'dYA`50LH\Gib11ՕZOhYt=Jk 4Pq2 Re3i+l2[0E?A8Y#ˡ1N<aU84MͥivC Ah!%T|svG5d)na $UNj C!RȩE+TBeB !˕T.Ս2VK>"B{z#&qBڣqaQkT)eEnVMšE AhB YWCukQDsAhw4r[D+t19Z=ueUB{ij_IDAT8!R8!5R**S5,2 Bk\ AhB I1JEzS,ܻ m뵦ɯ~: U]2V$L*픮wqBڥA9,Vu^^Aq6mތn*8AAdyaF U  4Vyi)XА$k(IDATx]@[]nN~vX?"*b xv؁bl?.'s=3sι]geK@vC &(e Ҙޓ(DR|`EC~ :5 ^^[}XAbѣ&AP$\g8e) xNNu^nѣHD k*1VVV$Lֶ!z4|܈!)VV֪鿻׎ [iffײm@`faOd?e)ײXl8UKת9z(fΚ Yl2wZH$ k =jb JT槂\b!dyVuG;fN`~~xvobOoyyNN5 ?ӥ݆*Ŏ75efGO %<֣d0OOkP 0!` ӯ$oq/m732/k̓;ծ!U,Եگ酟ʚnx !w`1r^K|-Kw ^ZS#v]&_f]vkbOSH&da67{"}<:5szR EcΒf$9솨"aQ?[\hzp MBGMѹW/sw|%QT׮mGbfB'MlK'ᐷzƠ*D" ,sbo88EXlGlJx;[Z5h~%;_~k Vz/Q7`clI| s˾!zanvN7e.CM% _*(ΤzjʞH]E =/Rin]tD۴4ͿWg\:ekbى@aںxEK)a5ňb.tOc>\AZg4q M'ߌz p"W4~v%%n8j\k^^^0󩛈}&'+԰021lS ^>WTGnC( .B{׵z`ZF O^ l3?7硞%ɦf*ңZaLk[["*{saA>d7<u-115'1 Ν?ߪUKXVVX50ܓAN_P0$M8niV}"krY.;oDRfMka#Ѳ6V+ KXXEWu Eqg]"t.NYhLJEBFMDauppڵ_kRwr=ܾ`ɞFsb9I߶{X^w#phd(@llWG܍K5=&ϵ{Ԃ"TgK-OJMG/ Tu<rCIqMqI{~Y48{@z* θMȸQk7@i_ִn'?~|?wBmeO>t0ONIQr+Onzre vu6~yuTq$dž9-s2ns}9%ŕv8jiWr3SG5o.NCz|r]{G$OZ 4!t4ˣ]gv]y>}1uǛnMHM2`im"+Nyw`q4,l"*}+l!hO8mxqْc]N[QPiB=;M{%"ΰ'HP%*>\|(IAy@c]d!zM p@`h ڢPfC!-xqCW?B|7Xov9QcuROLEN!Mixqe$2:ӕ'vhz^zkiYO;񅫯'&CݮV;zuʕE̚0~ pP;fܸ&2_oyNԧ)ɳV } 4pojlڕoS4K@haP|]@8dH6%# "yӢ\x,O/$ TZ!#|HQ j&^z=k" i[F;9sEBaTTX*!Bʬ5&ߜK9](2y7tԩ/v=jFoĦ/T j\Ы+v1:N)XGl ?ό;{F=f\% 9 $+I58 E5]igM5wƖۭ52\`k%$\q{잟okgt89NifcDl2 ߕ:q MA!"-`pTDrjf)P֙ųJ\QIq=oGe]X( I? 4i}WKH>>&z!o AS'iͿGPkm8"g|T _vC)1[Z*j][+s@ w+V>cQ!7]BK:ͬŬU =ې7P4Q%N ݲWX`?>j2Ӆ#;<+D*`ݜ1H]_Jfw]Dl0plRTcFv[vlTZyeMEݻ{<_6xykYPUlIvgF֗5sD,l]1Qj265+*(yT ))iy~^PPH221-.*Ԓf@FC~olgLk[wL8Q)a}""D613'SopYL+[;M|^qa>Hdfz`Z- <.O87H$ZC5H+Py]%w֯s93PLϨnp8Hʀ \9fp/:+~o춄Fv_hnAs*zkT1F U1<ʇ@?z۰>m\mnVE|kZ |bb.|?/%MYr a9 9i@1+D:⅟vMXȝnd}닙=#Sqѣ?)03oVԠ,/oxް͚Ǭ:F{~u'3P;# Tu:*ii&C@QGM\~uJ\>ƅ2 hd͛>kuEtҾЍ2/ВcǕﲰݿ.j2ĥZZ+GՉv!]WO]i>swsPj n# :=Q߉, DT.Er?\~$fs~W4غgP{*7s"v$:t̑G41<|#7[PwD.cD:OqELHN5v%;ȇt)9h R[B pkn-[bQwuMω1I:?AŒ*Eo1fڧSx"|4&v_?Jݹ}=D"V}rt͜C#D'J;Q.`dg {8y,\@&)*|q~e˻'f*b-ݒM8|Pԥ|X ֫Ogܸ[OrG}$Q.|* [ ՄG[qf 'j8 74:]ʇ"3ٶ-U$U3澳S45><09Eei> ھa\%!0|f.C@SQMbPƇ5vx۱3i9f> /ڮ4b7vD:FXLI'.w`¤T*.^pf{N,wBTI!0b";5}e KfZϱp?7l/5>N鵂9t&M"oyͱh1 g~pIuyoȉX3s ҸC[4貃oF[Quz͟ܦJVZAcc-$>_,P Mi$i\D >=8-BQ P CF| 6t­!Go_ChjpbR3c|Ju"Q|F2~@wuHΔ7ʫ/k* [WCV㇄2 !O d> _|=՛|%P/:X:쿥$CK ^eyy86015+CESa@~~odoYb&R$hdU2`cD$ Ͼ#@o5!yLYSaiDvU2BW-= f 5miN:!0L82!jx\nA^.^< ۓ ` u֞/fq1SsSkr{\¬1u;ׂ'eS}c.j+@ T-K [v0r*5c*l难!jBwGPZ=PBz`z{yZHvakУ40ed !&jWVKp8l-·{sQa!AD=fQ5!y2>WA M̘ZZaqV|cmIX%*͝kh!]FMdVeΆV ee`8OVL$ei.\ٌ3W,j[Em%WXCg|xȔazp+>yGҊ:!\Y5}e)APSA7ǯ6ߪ~x=V&9"w'!|w%,ȻwA׫C/D8`lf˧5#ǺmEԥ{ye<)A~拃+(͚ёj*",|ujMO3,TƇ'< 2::ҋ*K2.3w!;K f$]%U{؎kI$2Wΰ.Y׬j>&uKWNa !܍9#E{\Dt%hau?'iT?)»z.5zP63 [5yuJFЂZؒ*ٯ8uIzG!|ؿaC rKe3)h |2wt6oy5=@Nf=JKlVŬ:vH®huYqy<*]Y)f[$]RGt<{k!4)>6ԎM4u4ӏ^ .9UwCLjuNsLE{l@K|"y=#7jK%v:oBRvG[n-̽iVڣmvuIEKﵚ8sJ֢ kc :}@ \g+ڏ:Q4K@$_GȿXBR>dHҺ*olg4bq.J L <#,;s6ۦER5A|b7.ŔAD^e$81X`!H Ia .[dy7hJĬHBI\1͜ӠP$.?qJB({C@qD[Ch փ>Kw#au_x>8~r4js y|:Mċw+3'4Ic5cLaA͘ʺwӲ>-l,xori7{%P3†V=TVzV_Pktfa=_z{؃kൿ0 ,xVpج<>} D265!ajjy\iV6||Iχ=_A\.\lljbWp]dB̫}gŇV]/KOSF~KeOt &ZA8-.+_K|ܧ+l ʹjWk+ޯ@.`| p?Gh@ɩl!hB!\]j>mt79CHb<7^>u wꪡuj./[37y9^C^QBv>.Wص7p4~ʼnlӦ3NibW}#{uWӔ9CMU_ f)ofFCcw *gS<2i;y:‡t5@eemܞs5-ܫ{wVkZf+QMʈJ~vu67qmܔ!kGܘ[t0}|5Ҏ(A\>k]R+`=u4d m}r/"λKoBz&zu[%O]aPl05dŅoxDѥzh`M&hiHH7$H>d4unֵA6:Y;NsJǞǼ}g;[\N3!B]cRE"ͪ|?_c._hn¹_m)"=IՌ> ƀCU|;u+% pbNf(<&&knʒQME(EH>!O"CU(& N={ <:~Q-O}H4r; t%eA&+fLʩXnŹ72L)=8CYT.#B&OL%B$F^oL!r>)Ǣ|ec)x:܋uǷ#I94ah0ӿ_<4ES'96 2Ȼan9C>C捇49ۀa.h bYZʎwZD1 Du6νƷZ<5rrt u>\)NYRdmfQAX6gf!*dptG{\I+u˦HiD;52ӽ# ˷j^Vkgח+F'9}Ra^$P@ 7g]>Kp29j ڣ!Bt.V2 \|d2" O;}"6E1ɠ%NEg&vWh8$aUjȢ!<яUߠ{CXR"Imϒ l*QzFmn3h`%iUuE x3jƖBA8lvnOFU.=GHޜd `^[*IQs-cYs|5b*OxV6{7g%`4+%/fL ;rDMVD]XC-F=?}Dz Ե7ky=z,x*%iD >DUHʕedr@|ʈe!,"\֠PL Bu*PJ(i"Ae/䚥T 4Yb*)P6E]FFc%L( Qk)$k+(ЀVՃIgA#`k~qڶD?˾w1൲ Rߡ7B[Z=(V9@]xޝ =x-S I-/Iz©Q Mz٘p>9?`k%(k~S̛ԲBΣ'VVk G'] 4<~М~ֳHfZrݎW7\ۇכj}Z4Ju$6Fsgvkه^Yy,<"w`1r^K|-Kw ^ZS#v]&_f]vkbOSH&da67{U>$<:ZQYTU:I!fg7Dm 8Bփ[OxXl:jju菉B$|iC3T.ȥvm+8;5g:iBe;^WBFPƱ#VBd8X|xv%JXlGlJQ>w$j".(K$ _=dw!@ȸAG>iz7zZb  *21 ya$>P }bkg}Nɖ-Hsڧӧ'RUަqor=)^+W(|u CEQ$Z5h3|̏뽶jx/zl߼M;63%uoh~q7#H+F`Ie%A~v%n?q`!ϧn"F? pHwPcÞ#9ϯ=9U0 sGŭo(}曆?P5C'4;]_V9ř7W(y#cۻW-O?"*hҌ vѻ?ZRXC2׎1MУ6/,T(Cu!/FoǀU2BP"SXX Oa"I&&d (A٧V \.Ͷ$й.|>AؑTjV]>gdbk "hdlb2ZG7G3}tϟxyG*.a>0DP,Rck52 ZSfX:XCS[T>YwU]Zbt!E5REUP߻ `7ת?(%V:L*8w͍;(jQ;ˡK;$Y8<9<(j\?ۿjݑ]'MC ۛwلY}'Sξ479xD]*|ԧ}xuۍ1 Ȥ>Wץ{/ POڎj]Xqq#<4{}t_Zeo}tAKQugT FV;Ki[CW2y+maBv>jk!c-| Y(i>GA/~ngReve.Ej^lk+Ԭ~cR7? 6ݬe~wӘMcDV&|1|%񳎮?G#0Zʼ݇^5CF3lΘ~%dL}?MNb?ݵ;ai&7oeeʼnO(j5ԛhI6ҝ}qmhB:xUp * Sc2@$:u #x^n:P+ 6Th;j vN?u' Hy[IQ+g?NDS|hBq5lL(s$ۓ͝Yw䴳ѾAZ*u] xIDpK;p&#~hcx[w}KY2p>x%Bj W81u]=H\/`}tXCIPfc.5ΨFEX[ā_p#2 o?US!%t^J?nvS?VG> wÁ({yh^avwly5A}gBƎ8Mο*/ ^&Q g?ӺT@" # n%o=|=&|dz+"7E2]H1ruWExF~7tArɎG&4 5} [ v$W^CYSaPwy2ycA.'x"A:h7!+ AuGءc:s\i3eaBU^e2-,rjHdYa~N_vsNrҖ Z;!E $μULj+d; vUj`@y*od9l]xLfS*@JѠzGMQcVXe1ZzNE Zؒc8N f~w2+BM]TM[V!5V]qV?UxtLӣ֌ F|Q6c9AGU_T.qMDiM^5c*l]觪P|z>j~Vǟo@ƪ zգ4zQXZ}e7DŽFZ=_G_=_JTP\p"}* H226!+Luq-*< EZV#ruHDbxXh\`0Dx|i>N94yLG$-TbH1.IX,ޘvY;[۶Zh̉8la#*^*:_B*c{9?qÍu[S/^+zK e@tCȽyˁoǡ{!sNffoc3 sL´ 1pu+f8XfݭӄI=>Eӎ1uɘ+6\1;p3<"p D]*|»C|c& DV'H7Tjډ_96ZQME/l̸I}e|Bэ[}#66v\w7,Qyst^^i}= [*"@;Pu|@"qM/]rY8rHX 2|65te Ü;o9п.&kӂζ%nTs }oC*$բQHt=CѤi6yi۞Mݠo#!js4)7]wlw}Cܾo"*y{Z#_]_M~-b)ƅw7\ ;q?^A,OBz8WVZvCoݺehd鳝;111֖lvU =ỗ[6!Cp q?YztR|_n!|Ac?=\:t ![vm!ϡlQFi#̎u+9Lj.}%v{Rq]w>}IhۯH}(xp|JCidݘ [{ED>p6*gck5s*ҹ˽{?~dYA֮_/  0Heo'Uq#wn]~Z:t3 @s/Sn8p$#9K-bf&*O2hf" h@KYf5'‚xCLERT>~}6ǵ#f*r]4 R(An!GA]cCX λӯ:?icc]BG />%Sq=} xft,0V\1u4qDZѽG~^ d8Co]fOHw5Gw 6wʕoVi xiC$O-yJ$j#fE!Ph8D~7qCmcru4S|rYH>!WL1@!Y!22A.V-># Ç[tf GրV:Vx7zT\ $\>zI9?ˈ钻YGSHc8˟sCN. @^Kʇ@ u>` )+WN\g|~n]7rKQaqפٸa rsTe`PG>.#;:~G-[y{i") cˬS)}۾J=u1"τ6xnM~<72o߼#ℚpݶlNqc=|h 7뵋F$DP z9O*z{OL`݌IS7(.䗁xm˥(ҾzzBEcM,WvNSײVyy9<.͛CuL65,ݻ$u/e89N& Y#d9{*@.@1ľ4N3F́h1zX[TXe=ҡIdI>:D͠徥nK .SVڪZKspHw f9L07k73E'%bލta{E&-G-m|t*¥bUk|M]HT~Wej.e ɳ';`rmPuY9,@kmQQaQQM!1YKn9ohX^Mm`hH34*ه<׬(*T6pGoǦvҖ\j"+@nrC}eWgHxL \e^ Xs EbQ^z#imrT> ZG Ns K'!ytdg/iS`3E"MrzH{<#1Z߀*DU]k m#/r#[MS BfhT _ɚ| Hz>,C1Kss [v0?``(X.cܖ'Dtd&@ke ·/_Pׁix<677kC0 =f!|\ Uyުnlj>Rxvο]6󗊘y_ktc#c4np 5! F,·y}AE'uanơ.>(6~-kOk$9?ZjD2IӚ[M#t\~G@5# M OΟZUHSk~pL=qSS̋iiUEϡBq-VpOlWU1evMvMT| l[?-pVo- VanJ@wg;pnntqwvIs:__ˉ&Yݲ k_ ' S1#ƌio%Q][9`hɛ.~j/S^.yįr>C6tz{&molkDTSUʺ_61t-:cy S#&S4!ČQT ^; X%eOȕa*dUQ3W̺$ٷ3~=>{|Hf;5k ,sigM;(M`[*^lp>#S2ψ'^ ;pET$ٶ}` NRύ~ Ǎz5ɧ%‡Ϫpidۥ ;2cfjohB&'5<ڗf 9TմZuu/.*wj^镴0|>(*%j@76aJo|u;VczCA?nIk̑YRbvAޫS+ +qTY!HZW@ֆH4ܾ|mw07vw˰zHMR5#bd6n޴JFBx Yu]nئӏ]2dɨ"v>‡STulD D2Rf񚥇G ̇PX_/f秺MZ2;T\wmx첞b4oljn`hDB*)u=`b1Ņ#H z>om~CC, )d[,CL<}{Nd:C@2^1K  ]+'‡ڦϜۡ|/gN? ɸU>S݃mXAn*OSzo22>u]@ZAƮG'IspÙ 'ᄄLH}SdA*/_ "%ww~-%DF6Ch =sdY?\.{)AVv6'UL$85.)h!T!h)CЍ%qH [KCXFR!Xzy($E6 &I(4^m^mg{L!<|q6<;ZO_ Qc~Mȟ'LE[}N1(uݷ KY[D:c)>=bz3 Jul=;ǧ cf&W*y?ԺdS!%/g+lF o%mj+eee.K"yRhIȠ1(bM-`G%[KbeAn.E &f5jYW8=MglfJ #3U$k=VI7aJ9#̉ OW)k*Qi׸JFݳWTb/tA>dFg7AkS/=9UFna ih@ /8˺z(n@HX'^IA>gT YZhpr#*Py:cٍ!S+] mhC#WhFoT휜 e/MZEOkqe!8S,'/YTTȔ*EJT+v&9qjԒ*&^}CZU%mǐ4|% aww#\pL/ZX$)%KA'=%%6--THFL \7DזG8Fc4R9xeeJ_ q]8xV$*#8bIXz#87163%K~lj 䄪oSAiiFwӍ>y-;604R p- {&JTɆлP^'b1LDzQ֘t|~\=TPs@P9ǵ2z,++T_J]UTyz`BaF0r}V$E i;eHm,+Vzk5Q:5"˅YuaEvY `ͺ RqO݁xm&F@@ lHR4у'*dM,Cb2htC˛(!88!kVE!+|9NqqOhhhD dHgY) BK,ãHLX(|v2 M-Ȁt TTԶ ]IMsy<.cffkC`28{ԣb@[Y.gMėX&HkԈ&Čj@g3pZcbn!c ؂yJ p˫ky\!N;݈f֊BդKѫri *[_bbo h&K$c3 dUˡPi$ E"g2dYyo,BWp%e2xD;QڼtH씿A qcw1M|eE4(6[!|eη/v kK%gNGŪbˇW lɺo"bM|eq(C{QL8P ~~2E^%^ (k[iu+rքU n[zWr0haÉ$Q=>2u-{91I6gOxydnjS!Dpmev猌aJ2> 1"|ګţN-TdwlvBf6DTSpe/ CPSG^OS!B+y2>^I5! xֶǍt!Z5L_bE1ԩ|((F< r Lh 'He$%-}b6~. ʽ 'sۖ%,OIll ^'a8r+N·p5‡ͺQCأ‡ [!RzJ/ >>)[L"C8\ʇNd8ʇp_n yZs>E_.5DL/YKoT:VQ MԈ)d) B撩x`\>-94z>_C0xd. ^`M|PL!Bo-rPGV\jD2p@(rG$,0f.R~A~=hPH6{ YS#8D(a'B A"bFonZ$RE^SAG>q}>e bǞ%(*<1D`alTd '22i| ;=˅5l'{rr#XYtCLM]+_=hpĐnL7`E FA1DАjfjAv !:.?!̇Pͨǰ hOd5,g1L-, rdyqntCpUE-ܰG~ˎ@sZVUł@ʚLFp¼\6H0dj\ j&QNN2>, N#V%o޼n,&Uz>B]#]=88: Ye>{* ͿC([Jm!0  lHXkB1-;խA1k[w,}q1L0oݷO/bb>nd gKéxzQA| ]]IdRb3g!ll *5|l=YxJvj|n /t%{9tA慰䉓_o_( .+&\6a~TH_>^+A[5 ˫cvJ'OÉhs\5>LOl@]HB3$ljL /ʗ>$X\RT2 PN,F'% 2r@E␀+qw 2gQNY%E*=%@C@ `!'@!PP"y Zu=>t2}P=[^=99:T䆨 ek6\}f5Hq܇`uW*O'<9j}ZZ0mBCCQ+Mh R@ mC鿝<@x YQ` llWsn[t?zMc=9Lmo-pq ?xQu_](ã~}?wt.enV8>Qߨs[Udܾ`6$!C3uP(n Qv쉱Tty֯NY]kQ+fuc7^mr RT+Og pUz^}k;ZA 0(̜'z3]R7-g/4#fݞVsYXO[Vb,=g a>zSK̥sdwoQH[?'CE >u*Pdq=J[$Eȋ'Lۿu/6vq҅ G,3=SB>q8vbewG s0ѽzlBW/ߖ\JI5$mJ3Meb Qc?z_1"pQTӯ9bɘ~l%>>Se7Nw->1ս_嗛wñ}vIGHYy遃iױ/ qV`=R$wr/O\ڑ*dֶ-&J'ϲjS]; RPY\J_w6Pe6N?ƨ59FU th/r"}{>T@Vthx8D[ (?w=̈B;<. \;*å3^1gnXYk_<0q}4%]2q>Q/ޮu 4ܼ|1ȿo3e,<"W(drj36e:/0=Q !Q}nߎoӹ>#1%AyhWLݎoz+Mw:gYI?t-WgƏIYCiIƩsگ((pHKߠ+R֧$ JݽUv+[M߳=l0o*3'J#Ɣ2^aQ0$ƔclLt]H$9 W\N< U/HCrV*p8j!AC[oC7D޹k+ |3O;fL;k1vAo4]*c>_EbAŘ)dC^r-7nѣG(㓒`_Ű:IgvhlBGGᶦLk3ۼ-Ií.g~ضcf"r܋ 3#rIZrZ'SݼXZļ@#Ycca$ ?U+&tĤݥee3g S6@LHd?a 9bH։Y]KC]KӬ:dk/By};kKFgj8V/n<2p)G>Zϐqz__6=E!4WVw-5Kзʹ'HprnkSX͕Pp( fgggf3n wR~I̽D[Kje@f[ fY_nB| `Pi}8'CD" ͲT b}ܽW-eoÇf'Nu agZAf%Q NhG)y@(FO6s1?߂+dm@| YNg0׭5'J)x7 MV9WDftMe)&)QR ljz30r,p]jo~2B[u} 7m }ECLJl !e0 #c06DR3MuF-cH7R YjzذaPBehS4(UUU-%T297(`yI,8V'z3|%a_=rJGua#tSf^5D cP ݵK nwr+}bbEo<;\D`XecqBU#;`, `WPdCՑ*Q<`?(eWFNS¿<0D_CZAlM{|q;!p ϙ1"BU+ǧ6y?j1<}b^7[Џ?NNv-MՕZ(L1I"(3A5+vߐ(U~S"a%~ۗY ٳY[y 6a#_{o [zd?gIF%,]7_[,ruź#I̋ ܀ļ.3 &!=fl^8Ǎ̖qs 3-#DN( +(ŃDm!xyn٘z oA\txl˰v-%V..$If )EZmJjţ@DaR*$̄E&(PX(*m۷Z#ǞUJQj4fHNA[aC{]+oL"컳g?ۮ}$i#MQS #:v/$I };0t(IY,{[y:߆MX UcNx~c )=F 7 HPPZ'4|cW1 7LcVONGojҦ+H(hV];BE)ARI-RGko$4( EZo0?~c""i.oLDęfk9#M7Ker 0^`]-E$aS牰~cźtfZCa7}:aGHۄ[_m$†`nd"Or/Om2,jWjq+6ݽ\DJsZԀKw<@fLl{Nڰ墪iCjQeҙ(Z0fR4M4;zmYDcXu80^=Ɔ>zRW ;c]YDcjtGQeQgGmD_Itg V3""UVUR Ze7M~bbmeJ} EEy4 T^I\r&]n#ea&4T67)w*_ߐxMƂ=k9\yAoDl~c"\9IENDB`muse-4.0.0/src/doc/pics/project_my_first_song.png000066400000000000000000001555301404105311000220720ustar00rootroot00000000000000PNG  IHDR?Ð"sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1tEXtTitleCreate Project-{_ IDATxw`E?{%ɥC'(bWT, (`W{E P+ V,PH =?mKh_w;<<3;3wgvbpEz16ق ,X`Cs,D/9'7rlڤX a7Q-S-rz*p?:B5rXQJ=4)|/]aįk6ci7= Dʫ⁑O}ܥ]kS @AHb1{HXX.C7e$cHQ$.#$1pFy C/ Z*1$ Z%A7G3_'I+L6jQc Ò:Ch*Ck<%!5=mkQ]~rys-'O2+Qfױ<+P،9X}t/Z}A_l@vNr ޤ EYeA p{1^(9Y%l#b19ہ>gd3-LU0ư ѺQ I D5Td9*+hl5Č`FR&@|4'>:GnP$¦K!)݄=eg!>+4jWӟxFƳm&.Aą(~Erm.uTm!l_f@VW:ۊW%z T!/#3rr>1-"IGEeڵ#??A\˲Lyy9t:h"xzM CL+~\<3?tP$Ǔ1ӌ}}=?KpyF_|k~Wʏ)Tbdb_fcNlU>LD#)rfL<b$ >TG'O|#tĕi8:rd%|>+*h۶ :vT]唔(6j튊J hwKJJ 6~1vz1,st]mFnn.NCӬ@YeS?gWE5Y2#m*wO:|~')Uy?lZZic֮ 9PV}p:x 9gPr-7'55s-یSfCT2樖1mSjs' r PȪUP\TQ1 &h}399`)}D4GLF ϥZɞLu1EW~+g+~HKUOhxңfDz )&52)H<3ڰ}9wѫW/ؘ+=UVҲE$6ƃ㡬}č+Wnc<3)*ZA]k}k{Ŋ4oBz،*T7syiƗvlשeao+oʗױ" wB(}vvCЀF< @aa7JKKDWC}=S7A[z2ɱe c44d1 CIV?_9ЂgJuރZQ#TG_eT!1e00jH/zuFC3aTJY9ږ,G4*8}xr?= za'>p^ RhcYb:1o;/'nL_4{y{#R*IuJd/+NtL^X`9jG͋IQh5 m%'I/K7I(RZZJ]±lN~>H|v]vll0."KڵTZXHIi&mvFiim-c; #P]Ui Aֽ;ulG?:{a :lyuַ̘ }) NIWdiy={12>dddy u8\iTWW+ć0oeےdT&GaGvdMD[AcBV(e3Fn\Y7?tEt%eݯ-/qx8bu&׾g-I"JQ^"# t" U>:]11쿦V{b23l4bԢN jq&+e 7gQCA3PДWk2,N$)vWuu5.Kkiݪ5ӦOk)bcjZZ86jCNOH:Ytm۫VԶR6ǰxۍ/H|~In ]CT<~;w& fZ+Fj,ՓkpʩйK] زiN iޢZ^O?w}-Z<挆X#nmQJP*d]jdGM51kY5!c4LJd'@03|DE~_Z4 dä#oW9\3m*7k(>=21A9<>w'p:a?7ztgȸ"z2tA#bYqv|B&Lj^Q}Q=q^W{dS77k{0Qu}nMm96Q0OFȁ27WƖZrM7ѡcGNqK.Q-)//th]N摸q ] 5mf1]x!~[;*Gt^ yV⑇IO?͏? nٲv#J[zB'%IN}pٳ'@^êUmFZr\NB$*RR*ƹiFU&-5*$Cs4 qXʤxe*$HjʠQRMFTI3Ls`n"n7KE5 *k*sO (Y=;;Ɉ6DIBddIBװ4 ~>8li'_qӥ yǟyu:?VH)CsHt:Vr?;kRҨj= ޙ&W!]XpUطYm3n䖏t^p7׼O.AYԫgTk>o?/݇FʦKmgAD%%rWLҒm4M)/ ^jHt%_edPܐvYgdM[:Յ8vr:vK2Z~)'zOJ&J?ڋr8APA Px'G[nŇlsL~@NE7-8P=-2JQ&?%ˁ @.f{+SO԰s!9@r_H%:vH)U^ Y-i[ȕmUNZw ):YJbND%*ԐٱYD ?!k|MP ::(r+B$x5sOc;&/\2$ĄeTR6<^xys܁x!q 6zz}Vܭdereӵip|ae]exim˦oޒwޟ7sPxe?oYd-['}ƍ_}'r7`5Nz^- zJWϞXz -=ԹsFUq:\^${6oZJ$q j#NCq1KDEW1*IÆ@2^($֞oem9ްB}Mu*޹C(/X5e¢19Tm3v3FA!SUl dd6 uZm[GfBR&3ҙv1-]@Elc'#KԼVmI3/Isތ^J7V"O}Fj;k uxLdd͠fRĶ. }Q}J5C}%@!ռuBZXEDhذhڹq~"P824f،!+aL|@&$͕غy#uu5ظ};i.N3H!Nteܭbӆ ro=Pv\ Yw:bb9K6ضu^ҒdeɥNg/mO>%Kh,wg`ӦLJvh0&?zxeѝU]~}ݵ ,RRM9=>Lqd4Nϙb|h:EB9mq];1ծBUeI=6yy,Z2ThEyyl6@c eܭ5+2lZOUEEL- ][Ǒ/6֭a _Kma"_S>C`Gkش~;9kGuT*m`sy*ΛO<VdId_*M6=`b+N~f%\cR]/33o>L@944.k| >TDuOKU9r:ӕ*phlHbO!s#Le1Ad-U- by><84] _ځ7I=2gq8\Bvep {NgpA~T@Ingl{L8xθyE 1v/-d vڞ5Ϫ絋3|ܓl/OSe_r1)nǏeqqsMӨ>%)8iKѭŁ4ucr&J@O.>a1ʣSBcV6*Ō\Lf#I{xGI瓖%KUqaCz}ظx_gfg!d;5#Kq?~G]*%`b+#fyQ1]fHI>!TWx^KQȒGoLvvaܛ/B[Vf?T+mJ6n"; 3(3o_%m,[ }zz{}fǙęq{ 8c}fqvNB 1v/0dU{` k#VNL3k'>!M#S}O[Ȕ|8Ɇݙn#*!~dύWoi't&K>|^?`Ln@(at*xŨ)Z]>QbÑ6G N ]SEt} 蓪S?RlNG-D<P^ɏ%PnX7sGĀUrGHO\<ccNm ]9 kD甙sr^L0AEdR\iiK?6Xgb.YB+޽c A%KtҸ1}x<^:_X }v= П:|y,83%(G+ҨزFLyy -$@6A ==Դ4Xj5vL|>IiI)-Y+-zSPPá{DD$Ȝ~$H?j$HÏ匕4<2k+2d()ӄ,E?߇E GrvYF_Ի8dY ӡ+R̠lLAȢQ 2 (6] Gܬe,g>}c -.7J+),~o0Ϗ,KE|C2$G)7NΞ'jut5E@LR$&& aJ%ő=cYjl /hv@#C[ {:E{&tL '.92(44UddC0i"?j%f$Q75ɉlgHvbr'< L!%/b`",扐B(!<-!CQ"-3v\m=<=B##= }n }c/ե&M q>Ab(2Y4ꎆ~Ax $5Hͦ`/IO*H| 7K U@d HҶDee -341 {j"fK~U( gze(ʡF" 尬9RI|LjPEXGޜCtL~& XH1KvGv4b ^ <K^ =JսKzBs#-p~ Z01׏:zʌ%gƏ!ƈ\D5*Ҥ(L5fTmO)5Nj ";ldnIGROjF˓ėx7k+isBa\=cD7ɬ1ىk X֠fq I(Ń>h iILbbn{i)H s$(PxGb+I1^J2Ch Ig*hK. %G%kT$,;ilEjyUEѦVkGFH9hP AY:e#&Ɛ-ĕ>1/%Jv [Iט'<}Kxt%׷DOmcNsĐ H%N"+ILn\;XObD(JOMV^퉾 R_ ADSdMY-ym5Qm)D h"q$b D틉޼LL zp%gL#a=+gQeE:7GxcOba&mC9n߯"*ArT@AA 8)ˎ:*yLm@t#C,!-yBS@) %UQ~ 'fOܾīET/XU@be+x"9L2|Ɛi&DIHxҍ!-%Lچ+'lK,J?HyM-;v24eմ > [%CQ4nu(b(!*KTe3entXGUN\<򥅸V Nƒ&f;Q'GxDD'e ]&$;]mm!}FD(W"mE4E6 ԺrLn22Pm)V@E &&HZLY11h T"<'HO90⡱DH&l1_ډ5mƊΕs^5;9׈jJagK̏dҾ}MM:&97n2@6KnKq4vI@RD'NnBbCI2%Iڶ'W4L6:vbPDXDQ~Ӑ#+Ӱɑa9%QJa-1 #ꤚ$bMI{j=֯)P6dU(ʎ"CYR$58HH!H7I6mha\}6,{j&MҒݦ 7" QDI=LtTBMEv|Gl ' $IkӉnf%o$X9з̠iH܎9{2wWj*wY>O>3χnWϞƍV>fmLImx<J!|8ΤlB @nrU(櫦JVXN^5,X`Bee%E+VЯ__rrc/JϋmT.Xj%H.RsCb!e T ߸ХsW32pւ ,XHuu86_Au*޳7>IbwE_TrVlZ4o%">CA]}^ˆ^k,ƒacNdeg' ,X$ԩ36nЕE%%%t-,JBE lڼUVSߡ=:uҵr\v{Ruh]C|Ȳk\e %a W` L3u%In@p -U˟-χG|nhٲ%eelظZ7Uu^j}EIV~@,jZ` Bsge[; nl^ݛϾkjԉsK0 ^ΓOa#͓ĪF=͸pօT/oD}[q ?i Z_WP껛8ilj ęyoꐏwď7`ൿP{!OrtTj0Bxyoz Oev@*2:;ͫ1\-4=cqCԾ'ܘ֮ N -0 ,H祡&@@!͆ntb#r\tkmh. EV~5(ˀ♣[0}nA'=U)/ ol "kB&IWNn ޜs2GHjNXƼe\05.0h I }Fߣ, I4IJMCF%D@0_jۃԏLg'{%/m-#8ImYŧo/Ƨżv |VI3 u ~}p[-Xp@ tn',|j+$)B+@\v;,N>f02x*Do^؎~|!j姩^0 }/O0җxN?_^Bƌ9ƌeSih`ӗpKkWbO8ruO3g;DsXN9gPnzlU˞+eY}?}:g3.WeWB\#6b>r]?c+e30f;j&N_FueYj l,˘[hேp9y3\&4_ep?؃ܫvpBGsu5?;<% dР8xaC7@ Лu Wc԰Cpy>p<1ѷБpoX6Lq̉ȷ;"kr=] }{pcuՅHTAp}W2ù8mPO$ipHVdd*}sO0jX݃zD0z."wsgP>ՓCN+OZZ`_ʏ(P_W'Hzn<9~e˗,]168o IzvEdlm`sn&28e+$t7>„x_CAH<=371Yc^gy<&?; Gr&kKJߛ:fy~WV3=>~: 7R%➧0 <:SS~9 7=z+Z,aIu@H=Ou#bG2իhC?'{×ML9)An۾,ſ֨<216T‡nFX%^5c:m}9@01K+N5`oӏ"c|VFnvsg!N'E_XMM7,K;5ÎAW̧8M ep]mqIV>b?K&V9xV~G..A RY>` B-ѺmK~ ӿ\ȴ/~X[IYjG*3Pw>x[j :}͚5&38!t(dnH{\:;7#=ĺ]ԑAȥ==ty Z|gJxSIѰɕ+dFp倘"!`ny>9ozrGq7( "VYنc HʈdsbCFE*ڐ5;>9]aasر}9=4jJ,x=pȬ+}S^g'+7-lמтL@nW RF`όB2|27nKȫYW-ǝzZlޑՊLyⴁ!--ZA.GbqvC|;j8rŻLO/N e29}>XX``L[ŗ_(JxC]6kbл g5Ñ%4 xɤZGE-|WTزg23ɳƬTdc- k {(߶ykUɮGsѝGs~*Ww?¤9+FZv jwx FV^"ͤQ ݆Fp埰n^n'eRX)6d1#9r|0-~ɟN3Hq,_YB?+/n7b'%ŕI{N=w˸K~k.᪇ݍ-Las+_d7'j9wQ[\z=L|2CXS!|h1dCHu7EͿl[`P^^KV;2`A"W0-x dA/5ݪڛfX2|/׏$dE p1/X]'5+gGg-Dx~fO;UK݆PE&KpӮ+8ggt8Ma]/ 7Y>s2ͽ_|?D߯M_ sk}kq 2NƧspE~Fė@0:@Br;Z 8b ~@D ewzKR8ӳG;+џ2|q3_a-Bü'΅b^̣CEr >2x󺸓/;Q'-=Z*H=?,X#0E@z̟P7ƗѻhXG;F?p&Kc}:g6tnG.,#er) a5qO0QO{!n ҐC_y- /O Ƞq7r~BUXFn%셣y>=-efpCyv{^Bo?m{Y0 ˅&Pk75UUs$Kl}5މkڦ _zxAS>laqztЁ۶2o\rrrhӦ۵'=] O{Y@Cjj>ARyPrcsa"*݊'l撖FVo"--|cက uϑ$_.T n`& IDAT _l fjt |Sox`aBk8p7"֖W{oIJkȌ$.Mi/  n'k#3y,"cBb5n,X0X=paM{y`ao!0ZX݁M X$ł+?Xs, ,C` ` ,X"v¿nc ,X7 eYIq8S/zY֧w?C=zؖ$ QqeNvm? C>UU\I\-XgQYYIQrKNvi=χ}x(.ކt6e U=b=uaƍ 8]ݱ`B p8lX70eV؍:jvvc~?5[sfԩ Y ,h +;Ν:iߦu٧/uub-/:w7Ke ]T[\ddfk7,X`,ͭ̊HEE.#~EEE(XE`Ǻǂ :$In@e9oo[,[_dnG'ˆmn!BmԤ:4ڗ-~j~/.fԹ|3w;|w7˞ / x­\x:,ο~s6ӃDwxYu:{REny|UUW?Dz 'myqXR>ćyu~*ƍQwc|w2 nF^ϻ{n ǜŵS~fsMp񸳸ٟ(~V/'͙c欳/޷;=߰!N?n~K[w28_Fϸg ؁99lwo`ꉝ9k _<7zt/(~ELcCqҵ_q%1e6x1>Ap}vo擻G1ooE!gf<ƱYK; &-7^cΠ}߫'rWt[dhzP) 1=N7'r ڟCG=ȂrZ4KIq̙K#5e6Ԃ==U l;-?Sr̓|4^vnbaxm fx yoءL?p^ȯy.㞱mH{#/$>hX/Ҏߞ{Pwlŝ}&ޞ=Of<[Ԭ&$ܷW7՜Ͽko?xbښ?zWCӸp S?vf;y bH~Kmc`ӳ٬ھ;:u#g+i<6 Hz6LnI.? 6̕/eoxj޼l>)o.)KP2c_-nɅ:ptx"z `ˡJef``BJA]VvAuH 첌lmi-B<۾ceͱA{,.6dRxX=6]JAnwzw3;ėu߱zKa(Og@at)-ƺV Ei)\QK/b#Ӻy<4{=E_".>!ҧv+-p漜vN$ņ $:?9IBj"'v$4湆%NrL"־x{FshB`,щ #X95vA²58:+lB8 bǔ~E5NNՀI&0}P)$k6AuNBh^eֲ7<|sk| L;<=NقziLpW*"gR4Ix虱*Ag"kHՙDd3bnIw'ٱ+9EDi*A9d2|D ە, xV5`2kCQnTt@O?gcLA)M}Ո%Pu1Mw7f=cD׼$6" -^Gג-枠:s,$Vh^E0M~rCX:#_k#=kvKbSf Vmx4^1+9]EL]װ C_nqT4k)ͤppy0z|l.-JQ8]@A? OldDV+;I\tMG gؾ2&0ssL"*Q`~uo=ݖMEաI/jT*7':ý- Uwzw@خsHfpgoG}H=4ΧW#tD88 v>,S?R-8-Z"gz֧#$izQ`<4'c q8QH96աyYKUIV})r#y䨪h=wɡ` 22UGmva4=]JViݶS1:dvHӻ؊V-Zi^TIauxoȫ,5g0LWIf0#nV Պ9͛)vg}X=:mhoMzϻSrc,L5|vGZy~YW4{E$BߤǸ=t1t9BjP5ڴgE|aMݕ*fT ޥ*7 `dFw\Kh^xs?W2!8dTZY17F#y:3t]_?q.^سՆOdЂ|wXVk2pUCN#Bsz=^'R{[KKТ](G^!OsSq^e0-]ҰIV@OC{+mx7g.Zao$+"0Vg)aFyw!#ߥk 5`45}DnDTdTHXWhߺ46DHUUf}ԪS`47ؿc>7,ڐ_UƲJ6Cu\uB#?u{Mnܑ_vRL_!G?-] ?E[ȁ.jSwX_Ĕ /e=[2㻆n*|9+Gv3V {x%/|1wyȈW.i.%nr"TDV|q^e9S'b=wF􆎼4"+7n ZLW6u K$ToBboDN>{#b6 JNd%Ⱦݗ$Utﺓf >{ ;UK?_9}TgbKs!99?mL}LmuRlG/^uІNr&] r1Nlߒ{oAX*0xT7%}(Mƌ>^&Ug#Iq*&S.;eCUUɃd&$$vg͊bۂ{@_VS'qAq+LI5$VԔ ɋbyx D")aMN"U"#1͘Lw>yz(Jz#.Hn"ɯ0ͨBLL4qqZD Ӂbi9H,i&<<9D"H$w'D"H$'4H$D"}CH$Gi8N[ƠףNaSsD"H:v@0mn v ڦ]ݓRc Q{j!DOD`EΡ]0ӱvq S|,oByQWblN@a>x!} H:5 7']+y %15s&;wLJ3ga6B>O׿p0UDQvic:j,8S!1dhӧawːn;h6 >O@acP0s?cs : Ѷ_76e%u{[0(N̦SQ}PlǙeqg0.^W`7jEݜv)hڔDc'SpF'^"cIL*'Z΁tOoJ>Kzz.:Gi;%k1{X0~[N& '|Uh7'u0wRѽޟԃ|0~fN& ^|w~cgx6KDӓf?xIkI 6!yCeE!kezNFZO. Tyvu!Iwpƿ5WNZ88+^(O ^1ɓz~!WuϜ@bK>dKddKpSʡȳ1 sVCFPЋxlxΞY>o/׵D3a%,֗'!zVʸQ0W_-a(up*#ghqݗBq9qG~#@-Μ^97r%V-a+{~<[ n㓽|Fo1Mx1Wb&|;Gd?&K{}eK&ұS=_x(7 ~$D~#s̥WD#ٻɓ'r=s*FGۺN!4+VOjâ hXc,xa@r j>}ƃm.Uy|2rʯ}10['|bZɚtT,>OH]xhM&xmmljv 0Vu&E̲~mY6v4q)rA!?D d`ٹgNwq:ڠ;w&9),Y2,~##x`DĮc,ߜBYLnymsP+-Aј:υ* X4i IDAT\C%&4"m,(3!D5)D'^IRT"xbS 81,J91? *^hf(ר+uEX/gGRe!aTF|egSysϑ3oC!OFP?LyujŔIQt(DaO^=Ǔaد95BϱjX*[1Q|f5y>G` ?abiN} 6\I.!,eSe(30DRvuQ&ţ7;OFg R6[j7f Wa^ ]sd)TЄSѬqX5HfG@)\i #ؒRq^5"/oq BX93F-8p"]"H!:JLt @s:QEK].s#՚DvM@dd$:-L7)DܙjۭQUXv[Zn,P FQz>qO:VVAaSSP|zJVq%'e3o})Z}8U.N!P^ݡH]қi6pyFk/sχrp<%k|~TjQyEKZhC5^ЂF=kR$s:o<.oO$>Zœ'{)&CߣFݦ5b$߸;J{D QG,D"p:5._Lrr2Ŗ2x#ۗލF#[ooOBz$H8-Xk6T_H$N'|wTC|yV埴Dx %*yb2 !6[f}|0yxR#)r#H$UU͘M&&^QE+H$wJZq ~DED"y(p$I$l(z#D"HSpoD"H$TGO%D"rK"H$ܳs (d]qF{2ܤp)4wB%\ !\ք"]@׳H{uOFL ۹u3 \"+-ɝ&M\LRg FdRfJG2-;s]a+y 1-[5NX):oٵ"F"<((7M^z&VpHh7͍PһMx^z~?.ƍn\NF˞H%ZT%]DɲBS4JYIU Jzw&svw63AFZ!wyοH7-eΜFfINLbGM+Q/#ȗ-ӳ3rX^ݾqV Jևrp\3Ȝ9gp~;[\{Y3$k,HC3oUsq:n ~_vo~=!=(2.p=.ݹ\;47o"|3wPd{_!m_"^;4pg}i9n!ǸiO>?7 =Q%D"HϏX D"H$6p<яD"H$;\%H$?GAH$D"W^##5?D"H#=i8NǣE"<zt:*%=d"F"'v;zőH$9_8ݖ`xH#5?Gr8RRb}"I$U8z/z7s:`ؾ$}GOȖc N5iG.Ty&e=/WUj},q{G;ߩJI0Muk琟ԓ3hR"r8C̦Tm2ө| Ո7Ǫ 4swcsկQR“NKCz,?pvH$L^uQvRUpmȄazLqc[@(J.M3/GV_:g=oRң p>,dkdžN4vžsWtUP|Բ(hO`ͳ$Az1$I2N@)GCTqyoHG8w.[r/ hK1^1~cɜX3OCR{ 狡ӫG3x/\ &\=YE-s^؎3j=8~[1M6//ؘSG9&p": mypVv3y% jZ#qn:Cp"&R"{5+zKzMA//o={f?.\{n|$&MA ݿK$@a1w*z߸\c4!&Pf-YĒSHD6N/ s.G/\8 ZOL<|,_S?e_G5ˇLOO6Gdoa+1 gWq%K/f\r NAQu)'[wLcy5q]AzZ2XYG|~HMEUl>Ơ|QZ/Lx%I|¢hz~Qhh -%ncBMLXd9>\Є@9c/yMb,R}r IAk뵄۷͇%ePV_LL8ZkcN[;KX'Bѣgђ1sbVsWbD8hv-M;o!VϦe">}d!P W;D4>1?Q$onw_!x`}[&L;sc?y8m$΄ 7|3?nU#~DN:I1߰qV~F䫳6lk>ƮcLi A*Tt`lKWy;S_x4^^ɬ_bp/{m| ͥcuI1ƻ6s9gFy5X;x;rIˬk[>Ϻ4(aB <KrP? Kt#h% P~8~ŢBm NQ7sGqk >g5,Q`KHcc7OѯRRR8}bbcф ::fO7<#< w|?kk:WǮz^ 3vrrΟ ur3~[8;wDPUK*сA츐J9QWC鷘h4 ybNWX!נ8Aۿ@T3!/5v\ 4*RooFp툢k)e#N+*8~g + C~jȏQh胊hZE1 #+q6B Jቆ0jkv P1u @8;Ҡ0&4<hП#! &9zLA:T , ٴ7ND`):cwG2ǿAr.,"LSZ]=|kP'XJ5i ]*@(s41׮FAC)ׯƓChѹ&y BBͶQwoXr2:LC P(T%J S|֬U{>рG@ x_KZf ʅѕDuN lҖAoÙej4ax{ᥧ}PC |8{yuwvGQ"PˡiWR=oAQxQƌK`zNH"nl$=~;9< Ҝ'a^჏ZRIjb9bHBWF3aGMI;%Ĭ6аٍXXc̾҆nO/=`UZcpYAz7:!)&0QCJv&~ٽ-2+T~qTM\JaIv/uEZG JhJJԲ-p:5B(FTMP(4x4K}OO=XJoBIEQ,R8`wuNq-TՀYvk :EimEEu˩ck,BŜ p&GƑ{pXQ EQA3lZ&uy[`@*=эcXj&"9oe=u4),9SH%!O{(۴&(Wh b4t*#>DEviȊLe-Y%6ن1&|A"Y|ϊGʋ\ӳ2K5KQ_tixˮĻjme/ʞj0w@`pEh-5)$;"Kh!  ?zU M@'FR(DjL"p\'hX zCh'<9#acg ^42Iя̑hX'.3|,F,b lĘ9oQ1|IqT2e*G/9.M;D(!px" ҸS]jL{<)&TIAdya !Hdl$$z0]>s`L͙7nP֢vbc̀a) |5 :#GZf2ł}=oFUƒͿ-&X4pq5ޑELඝH;-O P&^_#qI8%5d oHlqHPiq%-Lg6grN +=D2דz#dӂIIIi&tb:vDMP%:Pt](?L8oK̆0jT79jL8UeR~co؅F_y~Pt*ZJDq[l$oN4A:k-?֝j:0R[|+|SغX8 " m' eUHeSWl Rxvt=;/'7sDj!_u'~3pθ712o,JæE93g,K_! )8[fdil%ھNdfJJ=O,qTIs#ŇBNcIʹ-9h|KxPwx8/úC\w ɧas8>C.|YU'oeWHgj8[?m$G&sM.P F.jh'yWĩyP*}ƳVB`ܡUp'ei &6-㙑 P~+b͙̩o2d\wzPjq5;̉Vr~2];x۰4-osk!Lv69Nbv'YKW!3s4Ȧ-'I=zͥN~KfB|&a ٓwHJJᮌևxb~QR2x`-To4RpzX2:RŨӡ7Lj1`e&E)6,F^0ԄV:Ew N5Bs9`W0}x9D9+ɌY`GnyD'śow蹼n!~!%r ]tG!3eOD S*Y/ԊOz9}hg{Ə{†X?iک$u<$W[g =>Ju YhtUhF37&^ȺZGlo2J՛7y)HX-߮Rh%.C>)xeL*x4 5c<~bv."@\˩MK_0&>xcb1>C_/Q W/g]i6ѕ^!__?憴~f3:4bPO؟/ /`WT>X{1&m<;hI^z)eĠb ևoNKݳgZ<ϬPd0MF V>]]>O[$J܀.t޸]dKW]ׅ{y{o(n>@Xhs)R.T9wrXҧ4494-KF0mu95p^N7ݿ.TZ$=s+tXS2b;>7\TAP鶴㧾F=<8yq}㸴.r4C]a ,}=R91ϾⱵEBݷk(YܯH IDATQ#rN bދiB(Rc\=c蹄cIܼRΘ,2m^zHTbbb0G_@cN<r[D"y0:K"Oh(# 33gU_[6@$SD#DX ĊI$$}#dH!D)&N /7pm3",E,Q)@"H3B#7k*fi 1!xڅ`8`WD=QA.H$+N oh$aBm$$J$+9H$\% )Bo>1,YH|; ޤ!?9#H$ &wЦvMh8o ˎK&Y98-O;&-/6m@Y|j+?KDZFúkېΥ9s;>~8#b|4nݟE{WѣDk8I_G\i nkcZ4hsfa˖-*4rDظ{}2ܢ-GDލrm?ye\,5.gѣeSw!)v~s?{}Z̯LڇG>KxJ'sϣmݒ3RO^ɵ.Yԛ.ɔ{i{Qv"TpF0q)nt%ag2/J_|j V+n(Oř>bғY{[IdiLˏSn-c,BspZJ>fM>6=OOS.{:ܹm=]m֎ U1/kYQ6L]̎ 6t81MޯS%ݘ9Jc]fǂ,qT!2{˅L(h)Ht u:}з?q'ONYӝ\<,C+0cb5xc?ޮȥ5 z¨q3dϜGF|3@kld8+/Ƣ7ᭇ1UN̵AE>L9 cVLS XޚAyݕד 8*cЧo!Oo3cd Go/Kۘ=y ?_HT5gߖ<)19k~ DR/3co2ؔp$:Qqf*1uC]wRFՏ'V7)$śKf[9i.WCj. oc #7%NǑ*|(m1/{\8pn*Ы_5uMgvD#j&N#V/Q=㺸jivɬ~)VTYz (.V^di,"ѻ( }*|zwM~TMd]Sj~l+NnXþW\CѼuPrK\+-w9jB@m=2wCw}xAIzq=N/QO/ɢrѮ W#!K}HKVӻ.,ı}牍– lM93_zu[qo*rms.0OŃ&>ێŶF|0{U!d> P:lwsH=ч'SS/\/&l%"*Z8l^3˹qy/D hQ;8|;M˙׷({M`_ocL;e=\cKte.bŲ|3/@yBm0m`5l|9M[ʯ>N:m#jAy@ ˆWa}#y,K 兟єl8j6vL__-`d=}Nrb(f9_wqR?3}t*" b`vPDEnEQ1@;oo{%w{(w7yq;3'gH_M,E1KF?~z"M{s1=;xG xnoŚQ4cp3ߑ9<3\X+~΄~S7)yo6!LrK׎_Z^J{aP|7|gL&^yUbJTu~%l0 ; Q &U[9Y|Tb2*Y{GQ'sǸ3?0w+js'o|1v5'6^l_,EjeOp_hN/HM>=;t~Kx+֣Z"]l~ g1R ie捽1@L~n>K'Y7.ڮγ_g8f7n|_{;gc@|\zo9~e~|\+oj{ruKSWbe?OGC(g^9~ɢ!<‹L{}#rdaVٰ2f|7s*p+=S{EY:@O! ßW>]lnjp1P9^{ўA=5,WA:uXo"^#oxôu!b5 JӁ!w9y3l2K**QIڬ8^pUMX^$#[N'8Br^,8L wNwL XIɡcS9~]y(X䜖٬\zOϩ#^˶smлylL,^NChP00S:6p' 9VIȚ >VEܰ//촗AΖXgg՘Μsf,lԘ6<ڱ8KA~̙3kx1 +ʔ)<Ӵi lT$_K[yLڔr``n6̰Eà'g1D=G/_}2(K9Bd~vlos`[s൳c2\I1]8lF4NfT&[ Wmduz}mU%#CV<(&*vӰ{lU9,3fΠMKc>67u$WNܓf"r֠zs-GcgkӟN1 b^hM=QJb|ʼQMf&J}3:ޓ({OƑp(y`Sn>=Gϓv^yhfk SI ah4ןAUN0 S@%mGcj_Angs y yAbDafáo!ӭKI37wt^ Wf.[4M14i>͆s6AAѳbj([aa`mXSJ+D?y>~. iGzFT\*A~g(M},\FlhLIztx6ؘ? PC̉ԩ QF˯s}7wqO>$@n3+YUj8|;IuQD`Vǻ[f}9܇ t $.Kkݫb+rV,>BC'^s?N'1 s{n3\ `w(,e\mOɇOkd\RV)VcIS|^F @JFG' o8;F^:yl3|;VTb mTM݊Q Eľck͇p(wTxȫ`3 I\ћIH Ƅ~1%dF9pB{`xH;Xigxsx~ⅴv(}ø[z+Dd Oz.~66d8:nHitv= +>x^:t@.Z 7n5 @vJT%*lyvu4 b0s\wv}Og90zhr*NKSl?e%)1νo n3a~'xa> M`Iti%^qe:)].2=EnZt? DTb$wdSpRnG|-F~٢_G>OxTXŎ-ޝ$F8X$OHζ]J'D$U$UH4hNJ (䤒!Uc-"T,y((x^zJ>KN+~2]`AUC xoYϺ׮1jܭ̛>%t#,~9:Hc7KpH+, 's=tr~I_[!{2gi >C'{<8E,ըRi.uP׮/\긳\U4w9H7Ōsv0@Q5_.>\n^ @ڪlwRMKEe+-²+~ dC/xiO߮'3`?njVůO-#I10x}0u>2ER'KZzY{˹,8xv9 ~4>ޅ@:x2wEԳ7#4׍an@vvd/=3p"O'ZD]b4vix/{B &o< YXr 0ܻY ]JٟNEu"[%Q4]wz SCF<8WsU.TU%0ߟ7Zhy#zna`D䌫n T[ߗ+oez oa =No0vrn(CV&tR:<cGGЌ~W\Ud&{пηq˙pwtgm΀f&=r /8G𡡂o|+[x0t?0˧>ŵyP00¨. ?MySyoi ZB.FG*ιrg3ջكri%5>7u ]Nܜv}^``5D2Hc1-x~?hgNj/=ދ37`L+@QJ{i SbOgpχix{NYmKrtXgrev|ON2A}%|+Fx~uyp .KKotRG|v戧n~>*|a¡\Io0KcV Z#F{_y+FߨҚ^ бywTIIyVC36U+r&u&e0Cx~4?cSFZ DtnC4^ ]75.1 OM[|*y uiҋS:G/QӰji,M>YX]KZbaQǫAUCӾ33gNֶMd;]wZfِ&꯭Z\{~,#QD膂cwry3C*%Jaf˄i[.#/#'dOVe [\]>I1~bUq"y,gḃ]xqʥ46qo9u<&ppw޹s?ϐjN7o~7䉞l s TW&0?jQkgZŁlmGqEˍ;c sh8EiCru=W{ǭO{y@ .3rzj-~7]ǖp^TJpMmhPih?fDC7·dPϙL[+44+蠜ǠdmxIHN|gq~:?5%Fy2Jv*=Ws ᑖ ԙr-w(^AګI"(uHA;3Pe)$4`V)֛H!ıTY+sg!j8 U6@c>*h .88?B4CKW:(eDA!"%? !ʟ0G!5Y(QO!*646B!D Wtz,$Bq" F;]!t]'5NeL&4MCU+6sv+0o+ݰU/bǎ( Z00PE^U^U^U^+!ӦUKbbb}N|>vjl{=wqa6lahҬ6[p#{T^|w-x++;m۶ԭ;qqU[B!*%3#kVӮ][|G5iס#nW.Z4v:a>?&SH~Tmř]LnӖ-7nXT޽{֣'mNj#"kB!DE9_7ӹS2ߗCמ't&{o~RTT$ V1麎aҢUk֮\A|9tLJ3߇+/ߏ*撓rc`upDEEQlϋMӦشaq3hEzo/UQhѲQeGB!8iٲ5=H IDATl)=@,v;9Y?Lq8" n{}N'͚6-u>h23Fӊϵ]AvV:vFQ6;& gnVh, x^N'2IѴ%:6>ݘ[pڭlv;QQ]B!*)2* Qu]qut@7 ݼVd:|~~?>MvV'#3{#;Ϗ/P0p{\^vn CЃ&6:׃!>>8l6*68pޱ݊ V{U.]BAbOF(XHeE1(J!5MԱs\o۶aob挆(E~R[x^n7@EQ0Ll6L&11s0? .~WGx>|l{i }uRU2M+qw k}!e\֚Ӟڀ'_gHa||(]!=}snQewזYྈOW=OOg禡(jY)rydm*U !JU#.r3u_d2Ťix<"#ڿ'r "-(`~?yyy`{#E r $"d^cy Q٥ÿ3/ #;-gѻcKZǭm &r퀮t֝]O>c,+7_H}-3”/b䙽rLߐLg_?r!}:uG.q7W4EHgGc|Ox!<{ :{u&k\>йc:̠^cyfһe"cޅt?^{G63Ӹr _"~40ibif{ 2aJ=v\+y["%5~y&m}S0֡#I;xo⧑w7?sVhfIØ\sNĈǾgrgqF$;1_FAoՊSwPfa7]w53X9 {v}w-}FN/d'[f9=:ԩ=;ƕ.P9Q֥3y}ÜGһStlOS V>grr$zÔiz/Y8yHZ*c}>>gnXf-F3UkI[zd?n{5\-}3nt!/Y[xnz6iI\\Х=#g]yW.ϒ4<κ[R~) wO8mۼr֏ۂ#T\ӉӞXO#9l7JRRWzt;k'-$5t{?x:?%8`i ~oիY~='г;䧒5 ׋Au4Ml6aQ1Ѵj*8f,!TE%/Uĩ1Kh:iKf2GsoO5 X _r7kVbt[|?7 / ['|ϼ~6imbڔ8q饫<:V^;#w5X*\Xi3ro|0Vu`vCSHr7mfyYg9_-N q\>Ɏb³}>{F|3[Ku<nb<.M V1GX ^q *ͦiv7lbU/%я/ۋm( ͵f|#|v 9О,f*Bǟ=?gqfYcjU3&VN3 %HтV"x~g۸uҚ2wpN=cuSlnYXݓK6*7,2rV+kM~m[6~'睭=~+шg7|Z8m5f=ևs2@܋XO{YHz9s n 4hO]K}ݞLqhbbNsj|gOToXg=̢#u׸e깴Dd-gΗ1ctRwô "BcX͡e斌8?й̽)D( #ex ѹk9_б5`K⪋Y9kdWu SuYOHϛ *:Kn W1 PjщDO~Gv%'ǡbZvifY/Y?O۹$ pʼnt9p!CAuvZޛw6g$fdF$7-x&$zTANF :.Ivn.9yǢ:Pih&@7 s,<9*s0gsNUQI 7 9 ?k[ դY雔`6{d :uKts'K^RS#*V^-@gZzD<kԉ Gx?n(]Zwe<=TYϤ^b+"KST="!䁏}?)VHa]druMxfZlک7pf>>ܔ*5Ј:S6˪rzKN])@ U瀊I50 P1f_Mo?~qnj8JUܗ/39/NN;PFْәWb99 ^Sswp8D\G1"cwڈj%Vh&5+7\%&Z$m߰1M ^Xh}U44%rI cuB+I*˺'X"l^~LˁȺfm"-:guR2p{-qћP\ߒ_ro$iQD5hBVAJJR8PCNqr8%NnQ`A&hּ)[4Ef4oь͚%-5 CױX͘MhjtUF.^ǰYs'oh8ơ$ `6D7 27S c7n&m_Zidy B>f2Y`qg:y?r $1i|9^䦧V{i ?x]<}T6Vvt.,>#;]M812\݋bPP0':OCTSy7]歿d9Atga~8^L^Iz  c_fJ*[75=k;Y2^z!EGMQ_G{ gTJD 壕Mߺn%DCGr9&#CzL/n^nFk@7_c:9|vI V}XcRnT_V.~iTnBL9=Y xt^*U bm9i{сfpؐW"Mac߲xv3o"rP:I|"+磭9ytL^A[ݓh0%ҮucNdcSd=Nnb. ǵ}.%ZheߦC#w#s6*0ﶡ\&:h׶.V=^nt`f[Ԅ՚::_Ņ12w0.po#C~L\ˎ4R(7,oy;g~g`i6i斜}v}vy\ةL z3z;7_1mnr0anNކ*h|]OzIKsp *&M'/-{(:2=A%S7`鼿0L_5OEDsOגg>>~"Rgu 0!#|t/N "/ :$$ƣ44U`2,VS z{UsҜ|qt6=vvq}Fqy';s?0t8miRbd.Y;rx(Zth&?Й߯Fn}FQ=s];Շ.;4g3/a#'+QIC4w8à=hPax%fKhS2Mhg_ęg gMw^Z3n9Wsj$uH7[ho24iLjZӗzU-ڷa Wu^R] !ܩ]N! ZKGOnYnMνaյI:pII.rc$zte&2~3+wOa֛sޗ ㇬PQw=}k7<œ7xzw SϢW$H sEW`isjzAܹ-Wʯxe|!)eřc2^IIt~SS{0zʹqTUt@A⳩ '* /) Xm""Xm)(t@3i`8U3^Q.̸[=W)wɰsjf^ϓFU{fmD'mh؃OIx;~v6ĢR][4R~7;.HN]'U '̡EwEط HL|] (rL@hb5*qJML 7mWJV0_a>#ٽs[0hӡw hf-29n}vsRW:69XuŪj_)y&s(8/Lzf==DDKJpxW{:N~yo |=O %m攇K*=fv 1&.U@ 1B=!]`CRje߸fwv啛'8݆Zd|vۭ"#j0:XO| +ȌL~//c*Gwh Cqjj Oqi> ܶBUeSw8luVԥwSZu=ֶ7W֐yʘ>`oU_fq9eJpwo]VQ-s5M#22˗rFdrBPD%/QŘ#TvxS4%2:DŽBqlNNvHJ V ['Nw=z"!11:ʼGRSYb :- "/Ng.;wFWvNv+TV^IUh٢9"zpjlVҎVu!rvBgnqF$''oΩEGEQ^]6ln+svƍ| ??&ۭ_. 5n1oXV,Y!8{~+Jtt4V:up\|"ׇ}qsp^鳦|},] >'w/i[ˆ[V?F=laEs4fB1_qQmҳ|q ӄ8p:|ҙ&<羻ײII=LNl_nJŨO܌az8q!5Xs&66S#xzSL>]:оU},qԋ3wL,Ff#yKJE# KU^xqX\l}k` ƞSL9̝?xVCAt@:*9+'swgsifykfTNb۝V=ltI\p'-t:(:07K + Mg6rhxa,F\408JԀ>ĦaNE]39)55{DVc),",b :hzHƞ2<'^D{.2=XD֭)V"T.D `|iUS00tn3QceU Q f%b¦Pً W0YiR4l*@02'`S*T=Hz I1V` !e{{t&2шמ3/aaGOF.ѱ'8­\itN*04bR#g?|Y熧'򋓘y4+U$B*ShoJ,|;&}`mu}_hI~ݰn^@ǵ)6\PRtѹ?Hp䧯2J'e\D-UO_”؈vʪ'AQ4fʿ ",_[{mսrzy1x8 9NG/O;/~7Ot8¢R(tf '?"nkNЛ:W:a6BDRUgv:\!kl5/fܛIo/!ֿEvKп4osSt B! 簵> Tcߗ=yɋ8ɚņ+mr>M*BB(~r?Ln=3x5Gӹ3#~BQ%xT!>n"K?N!BcP aB!j^՚]!DNB!qb  !S`WK"!Bt%z{UGfV&;vrc(Z(****U~Jmvbdxd⫝ !BH9tUUJ`L0 滨Х[O7o#"jB!~6^ImViǠˢ4mڌC !B&*:fMujK6:UaؤG!8GDV),_B!8T5f }NJB!NѳW3B!U=fQdn/!B*m~G!uǢ*XS^B!'b!pLz{ !BR#B(2QCB!SbB!D]}BqbzRګkB!8~B!Dũ r(BN)>ȡB!ĉa~BQKJ}BQi,B!T٨F՗ !xNu,R#B_VlC%B%BQaj;B!lj!V roB!81("B!N UY!B!NՍYAWw BqT&40B!%BZE!B*^Ho/!B &3!B*&a !x bb@^2B!j8B!D- ~d!BtpGJ~BQɽBqfb:fkB!8.{{EB!B B!B!+: r(B<%FB!8^AuB!D-wL_%B!NՋYB!jr!BLYJ~BQH#BZ%ݐz/!B\UR5UsB!U~B:KB!j`cHB!j8^%BN)21&C!⸨n ~B!NTB!TŘh!BB!BEK~K!5PJ~BQT !xN̢^Ll*aB!NUYB!j! ~BQEBQÅ2!B*!vUtI#BZE!B*2B!j^oD!j/!B*R%BZEJ~BQFxS!BOSdbS!BBG!,BH)B!D͗HɏB!j]!VG!Jhn/i#B-.S)B!D"!V0d!BF6?v2B!iGBQFxG!5.BZHBQ;Hg!Bp2ȡB!jPg!B͐B!BB!DͦP*BQIB!j! ~BQ+sIB!j)B!DPB!D s{ !v0B!j!T!CMK!5T{ !6 V{^B!%TÐj/!BlT{ !V ?R%B.wB!D-!BQ <B!)2B!jB!D-$ !VP"^B!D gHB!j`"^B!dC!B2B!jj?-B!iՊ;B ?ըRTvT}B!gߨj+=j'F!4o=8"".ToC݆f˺lY]׫(!B|=V Zwn7A'6XlX,D!dHo/!BFGq~BQBwQǿLICPW*?إRD~ kCT)Y)ABf3)DT$$眜f}9+ $?B!hPJUB!YeU'{ !+wu !,MтA~]wM&TS& 1F5%؟-BѠV-0CJhp}~H;zO1F 5F =Ji#!a L$uL)Tk #I9p5Քƾ2$B‚R.@]wV4źFB6ݵ~!ǨT6zlBQ `,V+yuݝ:|D:/ 22ï*#-7'w|>ݠbo׎CrVP.8žBg>K{ ! ]J@uFuvz[e(w=K/hEQ(<};u`OH1 ιb_Y2%J4zs>??>~hv1tgͤ!m6nHɓXfO$mnSA~5-4%!aҵdf3k%ws+wM$d4}Dpt+JB~ʂO9Mr=ʇ3p's@9% m#-/sp 3ٳq3Cv@.7ُMĭI 9sѳQō\`9+n%54FmqmrWVXb0<8Q˘V;^]!D0tV}?5]Z7{+13VP_4%3#H^kO,1a29v͏߼3yvX2vP4̞ ̞K;OfNV&=q}Do2qex\n2>B|z{|xqv*h]&5ni{*q(T_,|-&3m% %Jɉ)~~G/ӛ?g{o6X|~ӗc/믲?}qٔnʏB* l۽$%wܖ訦At/̌1бmJ]bƆofNqv]H@ rzFPM5%{;gfa+"% Ew>1gX_9n08*F"]9K(1\7m W*6h`i%MFƵ( c,1+ʼnAECyaَQܐdMǚؗib=Tzj#^B!G84FD8C*_)B'So9{1z;d*8; _, HkEBbWcl5 453#sBTTǟ@-:5 F.nS=ՠc3*['Tp [ })r~w\{9V, EG5t]G1Y0)`PAӂYCb7EȡƲ#v@Tzh^k~ʄ<B!ƴ+v:vSD4"ϏCI:v 4B_1Znk}](u= ^@\R+f;7L˩I3}_a ‰%OWou(ۋ>n&ݿ/>W-F0%*q]|vh #m|-Vf_^'Y0=Mٞ1- M5 M5;fE_CS݇iv엏f)&ߑqjgYqtawe!><7uk"FF>@kL|Hz!]r5ikJWi|:Yuwqa&?YQɓKfEHF`1~P/?Pvt,G۲&ĿFtôc6/Atu'}6С5si/!URթgIsy?{Ī:b kWeXALju GL^-ilhޗMgʜ,{QWDSGe]8cR6NxO?Mrs v+yo9 ])TK?U| %#g0u*VGDԝg0(% c7wq'NgQ4N]pxIK!8#qD?_] KzrgOPt&-[nӐcT"! JHdú((,.՚Hx5mrjJec_!(D6a".&[]5FłICQMl+C!aSՊb)?!PPi1)ľ"䧖 !T_I{(z(=YG!\YϙB!D}Ϩ4BJG!QQdB!9t+ BQ)JHG!ܙɏ*!Bc<)Ȃg!B_{&?ޠ<)B~}1Bɏ1$Dn^ !BԐ¼x=^l6K z'%>JO zwy$[%fDͦR3ΆW$}`50ƘT|5}wJ 3' .$t$m 4CcKB4" r0|z= Q8=|~u^pb- G`51-d(?Hi{$4Frrr ičx V4(X@-/@O$ I <@y2/ &Dj0wF+XPQCdfAlA>e;奺s+pP<ГTBÿT2(U G٪yD ~$A/@Dmqb9;b{j:ߏFxe@rjbKM{TLf`=Vnf"tm{ywe8Ҝ,fI 5֛B!ϣSgp, XhӶ <9aDMx@+H0ejځZ`,@x_PP aD@y0hId"9I!Q!iPA߾pur* W 0`A%I=8h;| {SSEU$4(q=HِB3UdzIPB8_zu}>Ump PZz:k@NzyRwRWTiGNN*F}P A~e\ WcފUt;\pupX.UZp!A0P=7f l)6I7FFf~G̓ Qr;j6҉N#]Bg$"yyN\C UԼUAeT@'dG{CI(Q>8z$8  ];|bЮv@жd)FʼE~|"'ˠ)ƙ$YQ e+gW$yE$"jyJ3Pyjvk_ EWRhPbl1, RKں:TPm)va( }?,|^/~?^}L{(][3H'2cc0 >+ZE=i B:thCchB^N:Μ< eQ^qYYYFƍPQ^e 7?F Tx4ʼn"ɣL^l"ꓝZɇfK0reb/PS]-aEp8  (sb+&RH\?kj$AuU5.XۭAZ [vrn]ZVѱtW4x0zT:^(WOxTZM$ RERH<)D\1fΘ ghͣN%O BI)b5n$'FpΖ/9<}cEy9#ǒ%Kpw>UF((ˆAq'Cҋ/q^ņ g7jV:ADROL:thС=>Cc73pMؼ`ҟ qlSDn'OP|>$'%,+W*5t)M52XN(8 ÃE6W*_4Yk"Ə+q""źğ}?mތ:LSAG97EsA9R(FW5SAqpm>RXn꫺z \ӧM5PYY?0z쉛o { qFx09`Dz|HZ=]Q~|@LcP eP`5L/] SD/}0QYw.nhUС6={U0v8uv DGuu5x+.ǎ]ٟ‹/ ,W?dB.p?GͷO7_/lܰ[mGZz:dY㨹H9PZYOuďȝKP 1 ..7<@@r:z U,?Wן}=G?o:7|Q[[+))KXG޽񏿿~6my[| ?_=k"=|]SSG|g"e^{ k׬AN nQ}u~l 0t0,_Lb=-첧FKQ#1PYYȊ $Y!?~<9"`[#6 IV+,fP>`ӟD炎2^;zT[H  #=-ڏ#68_J6=#'cNȴ-1'\G/^"_ZRW+ti4}r<~?jGp` 0kx 2{2l~6 9͚ԉp930g|X,RvxĦMҥ8~8*zFB;B9`K!:1Сh؏:c~_)`Zbx4q8wl6Vk?:DڲDqJZ<ʼePIZU#I,|/r|>>#yb:,lZw꒿|̛;x.C@z3Hyֵ^/>~t 7Zp%=%r>0);s<Ѓ?BPVZm۶7@a~>y)l߶ JˤK !z3ޟ[E^ƛ`0(DzB ˄߸I}I[rVk[hVW(Ab6i*}c(b>>O8?z^;m*蒩prHIG8ęQS]%8p.S_R]9sgQt}f3'+` -SSǏ",\ ]"Lmq'/ {ytЮ8\=ߴdfe"%-;wF~~{ԇi;vfGfV&0 {O5(|ʫ~A"'}y]0yVN ] *epxd=Y L49FJ:dy>jWÚ"t-Ut_/ tGO^V^O?͆N"=tǟuu.5;}O^/@t'@qKx?L>gS|"ƧҢui".+\Z>=CCfj><(xV M 0d64s Q_o Zzv{j*23`2e]H32aKK;wM ؾT%ɄLwSGuUxpB#o48maiKC eQRR={fKAΝx qL&$$%`ҥ rrr4 CHS_!px}C#Je-Fx3)?mb`6adFRrL&3)))A'i [K,"{Rmv;>Ld2!99^GXC! kR2NBl@ͮ$x^RR2!Y,f~va&2O*#%̀9{j*<`4hӦ-ʕ|g|l`6[0 X/ݓ:M.]snCA e ,Kt` (( ZFy9,ˢY޽)))(G(K%&'cϞ=Cҥ34zd#ݻwfKAA~Gu{ѹsgM,ˢ\ ͆ؾ@tFN3:^~:C'+=o罈+-he6ġgd @T"DΞ-ѣGPY^hԸ1II(++ɓvڡyi1@FXeuoq9 $mIV`G%/nD^C`kjȒ I)SN9N;IcZNww݅d2cAh~e -A%>]Ey. aE%%jDF 饺"C4!ѫ4@ gqaT#u.7i χdSF[2xxGAeEr[F&MDإ8yѶm;4oRw**xaĩSpnj+w`Ȉ!6`/߮Z>E7uc5"D Pr.`Vhܤ r 3%y ŀ6c2tcr &#pLH/bZ.V(4q/ ,x>c2hu"1D&GJMb~'t2*[Ἦ&𠁮M`5}nC#43t#.M|*peeeE0bE#M4AӜd\Zt BIIolK7ш=wPW[`(ł f:؀,Έбw"9UW:v7T)OԄAnx}>!XRR ?ėU F&~=KLP dEB0YT` .x^٩qؓ.;_`0> JW )pNSQ'HsBnQc$@dS# DD"N =hwˆ3Q#jZ*BcQ ;@Ѽ@O1L oę"0A/1t,H@`L%pO߂ _$%ޗibUcMa'4>jbXl(@=[UlS<OAGjlb(j7T#híg@L *.Ic@+`G@P`,p8)2daq#4V3c75'DQ'PUD'wԩHe˷c&"amtҬ>P8/)rƔE\P⢮rV%; R(lW6[*O_$ɂ Us>Z}9bN36uoʵ9w b  =kp@T/UF"O5h@c_ 3FnXa6a2GX"10 KX^8j'co'Lae9&C/GڲxJˬԥ×^Laף+ΠU/$&N(`CTL0$./ c͚!9F(=W)~b 0&4nePvDx!lAf͐ G(Tf0c2If('Nx@ v1BSa. ŠJ_O(* Pfh<(I;XPpjDD{e¨*$t/A(-+ɅFo!67Tw)6hAS` 0,30(d]j-pM1h!Y<]. !bѶ]ugdX*7G0? bYоݰX,hDZB*/^@]!/ǎaB؎!u'9% DH{rPx? )'c\c Uʊ %A *ȇnԍZB$*|RD-ц˨e*?\d¼))TF&c+J]LE ZDGc,*N%CE/JpG&*|Qk mq$z:/=A)#'> .u ԠYNs c9P_Wf9-PSS#o3'!55 M  5נw^`YMS'\*/ilv`YV biDZ)?Lť>uR/Uʼ!-O&SL@ʉL\ SV㌵IvhꆢРqCP )UT9W2hQWX.SIV18TƍWB5NFiÀ˲nG DZGfCeeX˲bA(B=(:w ݺw?Ǎ @VUU,ʪ*X-fB!e8m6A6k.;]8@tePdwy9r&ǠHǑmuw:hٲ:kd;C'%b|<%P?TXC!ry $h4JM'41u a&uLzD܁DD$d5S[P]3=&$c|!n eGW^+[l@NAz%qz=X>H0|^}^^lxܘ6Imx{tp>iӦW^- ׿s [j*L&`{c=qHMM߁S#3L=)AEDGh ͛E(O<Xҗ6BД'IzrQIN}A-)VYQ}w`EںZ %%%Q_K//޽)(W! }zRzA]bD'M7;Dk UV T0C,wy}=n>Ջ'Mœٳysf =wO-)H?z͛6I˜vF=={SݳU҇}zƗK71μ5}6<K?4>5TP;RNN:ɫcI0.QƐm01 +,Xw*w=k8vN<Ƭr8@+k֮+/.L{߯U]Őtx_n%I1˓]X~']\\,aZ~p\%]dmBdpbX7nzjAZ { 1|~>e@DrZ+2h.]gy}CUUf9fL_n)H'O?׬mۄqJW2aΙn]r 9i۷ Sb٨dXp[y_R5lٺU#}[VkD,^ ފ/ 76߯ŽCS@./X,x<?~s| ?c aҥ xwwN,B5{ر#K\* 3n7?g kxwb][oAX$P"G7jN:aƍ=χG{fW6mFN(H -Ӟ{',CN>hb:|X ѵkW̘>.` ذa#^/6nBx; r>vҢ<;(((Ċ+ѵ[7; Xp8z(/Y'Mû3fug8֛ob!jWطw/_jc`ʕoVĘ1c?z?Aſ "!Ʉ ر ZjM=- ?oߎtL|:vl 5 W&ܵ5xx o NW,VD^jGǕC፷BMe9j+ocРpUq$e3T[2|8~q/[ & #=ƍêoV XG1B2vq׷ʄ a=;v vnؾc&z+l6;ƍ;wTVRlvM W&j ֺȤ5uuX|B23]00t!cehJlv\-X4k~%n|tD^lcSWVcf∉5k76"\I+^Dyá\XaA̅<$9%!##CrZ89LlG.]%gQE1c x_MC;'Gԟ$aX酳둖np[t p82PSO>֭[Ga[|4_DEƅ eQ1\NMM '22;Hb7z8 /^b;b׮]ڵ+v܉N:rRmq=jjM (T3g`-q-[裏RmV&`m^СC0sx'A` ~i~+5Db aPQ$g劕[f.흞:^8'mu4JD*հ"1NAcEqSz |_(8wnG+ęb]_w/84mWh=-q~dt+7}Yi)vډ@ @O4N[H'. ՘hu!PNU1PN L&NbJ*(;_:TTVqFv.r-PDsࣈW4œO> B;‹XxzbSѭ[7̝3W]uRsΘ3g6v & D 'i {PFDuu.kt:.i~ :w@9hۮ->m̃EZXX5];'M]t:\ռN\KZnee|2[xGQG3331o|'p:]hӶ->#'9ڵkO>tѭkW8N۰֭;ek"rN&'J/œ9sЪU+!Lq1䓸e-`& PETXUx q? J8X|8i}sϡh@tM ?礤&WPqv;22ґ؊TU*Zj*hOJ={Ă 7']z"k۷NJ+z7+U_B!|hCM6`%#":}PW_[-R^x=z`ybޭ;5 sSׂeCh֬J!>_s*`a?>F#Q0w\ 6\j IDgp=wDzx|rQ .ysѣGcAX`! BbႅV aE ;0k,^ 3~igϞXx<^̝Iʌ:`үv/SLÎz_G @ >ӧ̙ ϋ˖ᑇ `  ` q&%K0T{u cWKk0 CT>@q>o͆ZJpZh4ر @VV6nۃsl6 Xl<^ϔ-ѽ{wt RJHep԰ (DU1ST\֩(4y}BOd2nIh٪%hҤ1vjDDJf'9xr~ѣ;v.\IwM2ӈM4"Gg}wᶉ!M$%%#xꩧq-9p1՗<1qmmvjW7)(..7݌Y´iOyW0mړ7n<8&c=馛qaa |>|ϗĴ'I~DC*-[U] An=+D^=2[v>V^cǢmv|s*vރ믻S  ˲׿_/) o//͛CnSNݻq8t>`nuxsF'``e7vtϣAC܂u?[n;C1j(̛7{> )#ClOQ tEEtSzj3څ n/iDh/Z+ >*;||W o}׿ǣM۶9!V^qƣ][܇={nÇqߔ) ;"&/>qCgKI}=6 % q}ٚjEbGxʼnva.|"p"Čd 'V%qjnjtcaDqމzRa]V1ѸK[41ЊPUUn0Av]2",C7\WP"FYt3lT9mk^͈$>%D:$iu:r'H! (lY AR M8R)ژx`Qp~({KR"ʫ .fCb 4b4(ƠW0$K!{@#>0X/Ct~8~Pҳ .yGiHPuH*x4{`MÓI\ V CC]~-צ7Ds_=xu]xgv5|{͵<6m 4sm7=4lKtuHt@LT)oA%psQvQjG(z0 ӄp$8~l܉ٮi z.B&DP\`1@b3ľ R Jh8&1:O'EUb IDAT QBV)x)Ō Ht]f+tSoz=^cEDsEC[P-cq C7x%H"]CJc.M8)ѫD~ ~Q vo/]H%/Ֆ5l wLA=j*}]Ag=kdaKlC,tV# lJ!`!vL:DoAC(\=X`u_E`!ŵja0{h^6,ȈrQCj*@{>BG%$C/6+'Ĕn14Ȏ 1q-`D eY+9f9UߓN [Q(1꩑U 1˲(9[-D T|ɲ,JѼE ;w;wZ36&_\W,nݻ㊦M [UjdȠKca[' Gc#Ο_\WqtE:ܯ \YlټC S7 0zb\=[6nĐ#i|r*~q7%++--[W\aZ}rOsh$2^ CS\(JnfI&_WRCGb)<].I^YYoʏ۷oC^Шqcq2/_S¿hkXE]]=ZZxoKQ~ "6eY֢UCȪF/. BV KH|aupqQ?k( ~ll6tBs$ " }{Mj*ExQQQ@0.#d2-B!\p,p ))YDajAvv6,#[0fYY˲8+`A+3_Яn{R%زHc.JV-&9QJd(sf2Zm]INêÁDeUv:VBq|~?qa822,n'~E3]$''c&7/ꃘh9O3 ?eKd2-?kS^8~TqxyG'*\gbKx%,sFQ2pl)(?%B}HJ`1Avaw[l3(x5 rQlnzljhbqLRlK̝m)H^m:Or}٥\ ؽg/ :vŒI D]ty3豓VVTT!+˥HֆSy 'D{%gbM˩LJvHa$d4alI܆AfT! NUs0b.>i9ass})H 2Ѹ6~CLW'PZ^"o {[e4i!G%eUu`F8vV5+z6py2 rߣMe.6[o#UhׂA`,Dž_kA Qv N gKJpe_0SP0ϋf9pe\w,8g uAhѼ8@ O%@B@?:Qk;OH9l^ ͚7ǐ#ѪujD˜:n2,\W>{!:t舔\$e`"P[Cmuz #rD.&8t08ӁWTWBi)8vErJ ))6=JזӼVְW_c|cرcyy@971[n7mBzz:Vۏ:ss|:?98qY),[?T]3@x<^OX6'靨 T`~CT `+` /u7bֺRE\xǎC }{NbR:~;Gρ8TVV#)iBWY&d_;l(,SB0Q_]UAG*AWFG?eflaN`@5N@ &A3ೂJb+O'\(a ^;z~NFd<_QZZJ=f[-&e>J?x)1tHXw\ qcV"x}Aȹ>a"@zla`?;:/ؠ  'D,ˡS*U]LX,˂8s׶=9DWDx0D3u#y/D{E>^Ir $zދ&T)Eذ"(HUG/ҤCzz˵qwrI.%;ٙggI'AId}1K` %jmV«"bSG9ܤ¹Z%lҐNvрR$-= 88Zm/&Q&@ :u[4/BI(}U'|{];ѻ]6l$旝hL&n&$p1zKnϿԱ}Ξ=C9v0ݻw3-Kωntòeksbl!*gko5j:gܘDDZ>Xn߾ͪ0} BBB aŻ5L(#rxn5k?sܶ.U,,AhzNI5 J<+J q1b dggcKly0.uM+6r%އCdSyuO߇=]4 oQaMF\}֪( T*Aht\!1)Ƶ*ӱ)-DϺlݦ& ɌNo"4#$@_B|11-=fg&U>ΎpᾺi.QBAHH0)IFݜ>1>ks5#?.jBj`+~װxҏ)K '8 .lqe|jL&:jX"AAdfd7IJJ&;[NC׻5JPR0]d,âCt;… RO eNvt~vDEE?p0_ ghт۷SF I)$%PF ;oE.F+r 3֙Ph '073HLrmgy6l()II,[бS']=II$'afDLL ΝKpP #JN8 ac8j.֧DN%MuU>tqn6Ѱif8u/(<e,Nrpx(Oȑ#իdff9u:vc|~`T(@?y$(,]рuf Z CBIO 55:-$RǬz\ fM017M^:&9%l2a6R$WJFYa/.0@!'72D T5]>bE";1C%9IRHLLGh`4R0׏;HJJh@ףڴtmV)B ^rKNm e.Pб߉e,-_N:u0DZQTt:.QDgе[7vٳg,n\rdb L |DtT$3U\JfVsEǥ~afcȐ!iۖ~Ƽ_CѨQ#y*WdpϞŹMpŅ Uw􎰑Wp<0,/?m .qrӥKW.Y¶y<@ +V$))]vsʕ"URy8,G\__{T| UDꁰ):@| ^8<oAT +C`E F4a2\ę qdg뤡271'p-La0VskEt#.l6{mdVCY^gr9||ԾԮ ARȚ0j/bCŰ>Z(YөGBPIjhHOOTd,kl K"X{HIڸp2‹v7m&685o5gԩ4ڲ?LʼDEV,Vڶe/0a$A`W_Үm[{L&3TRE@hh i>APl*RWw^~z)VW&(bgRZQUCg#qfjjtZ4 JJE GHh*T@TJғɢ 39b [Pƾhۮ=5kj7aۦLɣ@L^I?Щsg.Yn;u̱E^,K_r f+'F#Y:#f;k,]ӻwo.\ȭ7PTddd0a/^K\špk@0 PkvljբO{˘OStU"wc07+Vr& ];Z?KХK.\HjJ2PTL8%K's>BQBP *kY4@0hf ؔ U$ JW)qc2\|Oiذt"RyR97ZfB`9s\]PbCv-!!*Uh$+3BM/"&3&R!znS6fr瞦e4X,z=l:Ç}6z,RSSIJLSTɺTޕF]ژJ(8oABJ }5Q$%]VgШP[&~|}}Q},kXgٗmߘ ,EѣѩC+tt؁[,lDDD0v .dgj2l6Tڏ]xdp֯_̙ ~ gΜeYa#C bjժ0sq.pRYi< MUo4oָrrr"?4￟K=?C<¾رc[_,]NN¶"*|#HFRcO~j˰і3JD-{gRSSHsd2V P*IIMOʵ bQ cV#]Ǖ`v6aOt*ӏXR\ IDATW:Df222E˗/HՋ^o$=Kk}02d̂EP(d\Ϗd 0L zfLRSSRoEVv6FɄJFVlL|9}pE%B߂B  X )+7 h, `Ěf1)|^){›@vmٲyHH6B Ls"ժV'Kء3Ocqܼuo)~M9~JX>ŋ09MBA1 ,]Rz4l؈RnMZvv67lmt҅KSz|}}4q2dzpB[=#.ve@.`JOQbHS"(DF%?fZ [u A gvIkL0[6hOHh_|~;e^NG`D ( Xm z.b-`0LsX=6[q4d27 f61ܺu&\:(x"i^y-gN(%DPPYYY٧siذZOJt4Zm6AAA)p_Ƽ8} Sv|~j׮ѻ/(Ƞ"鄈DT,>)Έ`9t0ѕ#ۧOaƎ; g.DFx6â(L;PU5aQb  4# 0sClBaPbc1(0 F)ȃC~T*zAVHIM^RRŘu'i}pWBVz7ʃd">!-[y} p7i*FzU[EUv;ٝ_S8+](ROQ.22jӧNT!#+ H3dd#r;)EZAE׈Eyexu:5jj22%]8͑) FVARmLFv-ZM|b{@n.c"#sc'N]2Elh5SAn&+b#n/{{8&x4Fw8(YYe%L2DXKLl2IFFFFFFbX)"mDrmL&ׯ]%*=8]ڞ׮r`>zKpPP &#HM&w%j RY dmv { 'DDE[6bjtw1 (UjT׷h;&'rrr`(22wz;ݍMoi;$+2h,B:usJurIFcmғΜ=(>(pG0ԯW2)[os7Rŝ~4څ yCIKOAl;L&rrrV#'S!{;CzJZZNa&w͋֞TN8Nƍ )kqCwj)vDC2ed"%%N[xшF!%%АR7VtM5V:jڥAvV*ѴiӲXM뽷SO a?!ΐ)mlKܚrfBvR bbj\z ľ{iۮӲ &6&KΗtnkeYEym?]X!NIOp-^p9hْӬYs.۱kiر]:%yÇy?~CwW^-VZ!=='G=N xr?ӽEi O*mܹ3j&O / ɫ9z=C V1`ƌ˃>OOK1%ĴiӹuxqMPTT\Y _[jj*sga:5 oÝ+\!PBJ%AAAٓgyFСصkÇ`ذRG2aDzhƍСCE18p{aР=˗/(pFŋgСo׎HNNݺveth+JĜ0~X_rGa/€3cKdffzt/<>}\*76<ȌiӈE_W_Mj՘ )/LF^wcHKKs[]ݻ’\ɓ'ӥkWj5L4Oug4R];w:\? Gחz A eĈ;wNn{Lm۶ckמ\PLzHIW V+CXEѻ/11K.ѡ}|pzVjf3k׮Qy_lڸ#Gh")ҥK_߯&%K]j(KvVGn11!!Çٱc{E]ݥ|ssut֍}{믿we,]TŋY|> ˗ܿ+ƍˆ>l(}R@qEV}-={V4fd֭,Z;J?C+O>$.hFRj1Lڽ+_E9^xq:Ӧ(INJdڋ/ N Jme;j!11Y=c ƱGyiB=\JO?@,[.M gɲh4}i@kkۖtp߲e ۷#$8mvu^<55cAh4ҿƟErr2ݺve/$GcAh4~xOp3쭀 &;$wnVZޝ N:U CQQNd=0㥙 >/'==-aÆhоC׋Ҧukj5ݻwƍ(<<`zϜ)Pg_3[uִmӖ=ۙd5Сj>}p5)``AE{ԭ[9|Մ̚9Դ4f47n'PfMy( Ge!h4<xt/bT8R#& 6{ift:6nAս+,yPbb"ҹMVF T?(ܹpbbc]^=} 'i< {A=ұCwdbذӡC{n.ܒ DEEM|\\n11,]_~qzkUyok8=z%((pn'&Uw<$4T:=MV^sSRB8'N,v b2g( V*z.M5~׮S_ե EM[4}ݺѵ[7_ή]yx%oR<$,YB||:._l3 0l!Z9{ۿ(Iof5[|>tyqert +C͆ACv4k̙ÃXd_} Zj!O7xw5 oGEE1pÜ9s`93p V sOKK, yDp_dќ:yJk1b~'vY#揷8GNyl(FÀDq꿰qof-N&33,nE3( Ca޼yaFi(z{nݚ-n2~7oޜАP>s4 -מ[NCϏˇNCSg|Qr2ݮ\!"/Ĝ>o?3pb?a1 m 65kְ{nz=iiiZy])|xx8Cղnz <ٳDʕ1uhќ͛7X~͛7s3*U1\~J*yǏgriQa/|%Y^fդH+ZeUFժU1 []Xm~Fղu4lذD&RRYr% 6רQjժ7J||\>g}F3zh>|O>9Zjh\v7 8O>ԩS9q$#G>ʹsSȹ5k$%%ݻwF͚5=r۶m۶m#>>;wA Ҵ[4 OQ\/p%&M̀3aDSOW^иqczA&M,רQQF{nƏW`|U۷@ر#Gƍےg}S2fX6m# fԨ'zJʤVX33sE߾cƌ 17{t/kjG+9ɓ6lh>7W[.o櫯̙3 6={{Ү][1fݭ[74nݺ9u/zm~-^{u{Rƍ%!*#Gdժy罖V^য়~g^^ Zƍ%99DƏKN /zvܹ3i.'>&DL&ZjJ=22ťo~.61 9{|GD-ԫ[ԷF>w<ҍr|B SB:u{Bvk0]Weسg6l>si.,w[;(z- 4CѶ][_@@)cv>,M6cђ%;'N૯ޣߒj wŶE[°q;vs;RArR2>>>.D|||};ҵ-MIcUXgTZf͚qQ6o@-)Ð70L>}ZȬY3wA-<{oa~Xͷe&K}yWywh۪%of>.$D): mȔ ؏iw>>8AnrKlF:uRN֫Gο?`13K<9f O<$cǍG9vaæ7X3֠Aڐ) 2339YIDDDH[=|JIZ,|^M **uտ#(H2͜4׮_j*e wAyޢđÇiѲSJ]5r&:u$L&姠df3i$\y`\ rT%VFhhH5ܲEh˷`n3nhWݹ]٥(Xa'tnwarV]neddGCa&`i^4ZA,u97UK\lm^={[R@AMA$/XSI2 ecU@zJyҵ`{$a"Zmed}vKdnˑ H<3fޚw6P˖c6|(k1_˵Jrw{eddddddq艨]aYQnxnꥸT IDATL|Y!#sT"ȸ!&&˗/; lXis.kdddddd?l"}Pi"######sb퉐-ddddddd Ӟ*wz dl,s Z#Keddddd>$%¾/Blwu#kѲvҖDO?HT&8i*?Y&4ބk'} oHY######s`]p'"ݔu <(e<npѤ{qEF ~2P6v,Kv6S -Z5R~edddddd<ǹo鉈KM ,<Z2^dư6=z6;O<@:-q¿xDxPs)/S֏sJ A!ԨQQ"lt ZI=6?`4Y5,߾l 1-??6x?ba޼wuI=Cyg[r Y\G-%YaR GEM%lR@F.mN 埮`ɫKGR#O#Ol~/>?7h}|}}%9zN݆Xn=?vDDE&NONwy(ozF=$~5?8v }׉d ht>;cj*L2uh4gQz Zr$ 2lPF=t=))>sQf-OFddd򎍍ӹHbbb.wF;?'@4( },/[Ha(궔8%0i3'2|؋=^oOz C^Uݖ4Vo~˚mkqm1Dz~o0nrz)!++kѠ~}']ϻGQ(^,0]~Lf35ccVC;.]3'WypZFbf5 -b옱4k֔~lR,YJlL,f#̟?y>w333wy;0ࡇJ=OHKKcM4jHlrի֭g̜RCY{̷eKU$ U,u&_A?S>15ecH~zE pG ENe`эdAY&11̘1]rZժT\]vӫWOOB^?WqMyY~w/z1fΝ;1j(ʐFhh< wh~W_~)w֕O?L:?x_.FGоC|7nd 4^zN&PR.",,- K׎_CCs.g-##so'"RdP*Ti-T 8…WݺuǓ1wtr?|>wY\ еkWv}uv+sϜu+CxXIIIT\m> ge$$ģ8$(((opg鈈(1Y KI܏J  88tw.)֬YìYEZ}'سgOw{SSS={6&믣T*ZU=tgÈH-={v2TҾ/˜ǘgdǡl?-Yd)~~~פI4 vTb={p4`tpuo>oaڵaINIqyW_}>2oIge !-=HKO'$$ey^`˖-#aLzz:+Vٳٸq#gfXe˖X%z>sLO+K~wy' "`a2bFYwSӥ&dIҨJ,yYO ZԩS;Sԭ[9:P#&G}0b^}U4iJ> "==ӨQe2d/+VcǎhZ~Ξ=+\RvEYݏf͚eV ƍi֬i㌉Q|a/_RlY+=@;zBouVz2dg̟???nנ8w\VVDVdJ&PiSXJǂ_u,YPþSNOtɩNʲeyg vy$Cll /<+Ve˖LC|3cڴiŎ++8fQ(ߟ+Wо}{{r5Vf(>>tڕٳgtJqq  TZfԔdFqq4j|_Y$#sOLەW4haj߽B&MWpW4h_zv8CfgSzFI|\JE6)l/!#sg;;ÙJPRh:qPܽy)AܮX{a s{6,,22即NHiu6E # @~q_4 X9gEh)}͖DKd&!mk(9:] ?p|vcmI`1kOT: 8l?Q/ۉ;w/pk k/Vr(lJW$َK훎ayɘ+Ww콷*xz[X⿌W&6{22Ev}ǩx JC>l%B>5|kB^70y尓5ǿȽ4}r` SStsRѪ:Q~E1}\[SH=CKQaq&[ɰQȫR!_;Bx~T^A-8qzt#.>[iִ 9-7':xgϝvb" ^OvF=7g#.ZoRҖa$##sc]D7NƎ~@Uir_c._вEs,[A-0ԭSi<;}3=Uƅ[6.^zjJ]S-tSŎqWoA7涝Xq-}g7Lr꟣^V e~c RT*Eu z"FQ kʒvhזu7Ѡ~=9Cl0ddd,|E ' 7mɈRY4o9E^η)rno[׮#AA5Ϗ"+VdCޓ[Xpfxab-n-k.|~eoagQ&Fo|Z-Zw~>jժ (aLI"n /ha[Oĭ[8ȑ}Fm-G/\Ǜ6rE<Ӭ[g.^00ijy55(C$0 RfZܲ M/_rjظi3|8ڹc,'gGY'GgF7] X?Ca¡'^.L"rvy,O 01}7sسّ-.~Vc-kǧhlifgFiޱ=Sf@34}@Q"ʹe^ݿlm!hB,MDصc{ ̾TUW~aB(<4[W?4@!B Q*2ODq˖zR׻X4sg]gEuuC&kMzB>uV@8Aos\߄(eK_vmD@WKDZC .}zZ~#G6şH9sO+ύ7\5/wM;QzQܲ/~.i9TEQ6$iOQn]'@ `L-Mog46~uq!Vto-[]K]]Yz5֭#ԁ()URQ}e)sy=l[n<~ .dpAk;j@b1N=l%N;\N[v.{yxW939iGɴ =1-_ڷرc' =ߟw{.<ꊒ0cl6kYj%pӉpr:q:p(ʕ+y=HhF9TE%1nXnů<֮[GFVYG~s[zZ-=?MM;y9eXvR-RA<>tݺ:z;#t ٸi{oq 9Agg6Orǝɏy^˛z]rғ(>?;ʹeK_vi =SI2@&`'۶ifM̛t#H~kYTP`s/p7f އ'ys?1uOƓz}K7j|IcML27nbܘTI}W1Du^o`u+?F(i||`irI'8%$3OcUx1cx6L#|ondjHHLt:{D55*+ o-[]K]^/v-啦I{{9sSQ[[˲0rLRFo>Od6E*Hb^oEmM UUXucB3VSkk5۩jQJNݻeqDlw.uF#)CZu\2]5凙qrAJlw.u3Mӆ*:HKDq˖zR;#<{W]A"i\BB@e!.rمΘg?Dt:wP8L(uϽ,=$ϛ˝w/g4S0}4S|+_?oWK;7nb|qƌB )Qܲ/Τ^N$V~+Ӊ4MN(b1M(hLwt$%|>$3OcUx!Jlw.u37E3zֲl)92xtl݆(T}X Kuu5xͷ*SPcȣм =; Wyٚ\T[ԻeYrM{ ?+(:xerPL5wؐGyzvAl>!DLL~05E(b!ٱ2=->ѕyW`gD!AZ]+*ut w5eNaOMӆ*fn!%5/p10|QQ&w7W^tt:2yweaRB!NQ\N554GEQyɰaP2Ly"̜*B0p|'Bώ\N*>n*5}hˉ$J "!oOvώK|(n^i*XvBQULðZ!RFg♃離z0 !ݺ"B(J33 bt4wϣ}yw&g!3 s`tD p:9~$Ĵu+߇bpv5cZ3ced n/+CUNXrƍ_c,^x4[>nd}7g_n̛׬eSyn /8G{9;Ye-)|Z?|r׼Lz{u< ՗=JGiPUaoD!!M!D\x&["^|e-=X \xb1ƍþ{e'1\{9ϟiL2Ӧ䟞k_yM_R|ر|qSLÍ7f,\0;OTrfu^_hvaCל%6 Ѵ&Va8w=y>N_rUWUC+˖"\"2u"hii:ִԩSPX,ƴiSQMӒ0ゃ'di\_R>z| د'*-;uhdJ>yof]K&QHols٪IpMɓou]O?7g;ؗylz}LӴٞTW!5Tzmm-˖ȑ#3I<P@ /zfxC55TUU\B48 o(@ єG;x@x{:|{R^X(hVgNKAw*|Y됺/rq{_eop-76r$^-t0ײ75n~{L9GZ,o~#Nĩ9z5g^?w&x1r/8w.-CƝXAJi`XG$%M:ό1%G|ˆYN &+1+\?U8͝|]|9t+G3 &Wuss~OcO1vdY r˕3x=s>5pKMJke>axːW|9]# )NO}9HbX+ABWKD>g ab삉C/zΉ*/Ai 3 1c3S943?e%,8EQʒ&TVWDa?!lom8 kǗs\&g%ϢC.ΰj6V0M=;uo6>/cAǺDw.صg@QdTv,tdbIͫs(`Bx0 -fPˉCU3]->ͫs{E~B>TF-lpaLL[~LDu%c_ޓ&zNM)D#lP.gzR݆ʎ< ͫsp(sBdg 3ڂl 'T܂v>1{K Hha9:l-t2k^Z43%(|f@W`gD!(#i3dV&O^Gd ipǖ>+}ii1ˍFu `ҔiQ[FC_RZ< ߼ =; '!Dn k+V@-ru]G]VvTPS[OKk+U#Đ%]w3A39S{{OOdݟ^7aDuyC*444pUDYeyY!(_E0[@2J-7|Cϸտs~ʵ\um+_ϫ>`bfƖO==OOч3oanEQ0MRQY* M6܊RQYC !J.ۼ0Q m".f{rbns)MLC?ݎoǾ[?ͺ;   UUl޼|D}sg/81lU: W9Gv-[]K^6mƢQ<^/#F5`Ma&ٙ2zhnʟ~`0hk@Æ1zh|>o N[ԻeITU*ǻ7/>@%[DQ&%eK_vmx<^nOWO$2͟B@e[ҥ,-e=oI]Caﯛ[HB6 Ez=vK)=fN7nNNN zb1bZ,C$2bD@ @M_ rx[h"/FT`IK GynXyֿ381qڔ'|]׼n⁗+O1-.~٥[ނ@ŒnuhEzh-a T*VJ<::\hFn5|uW^+'z*6~kaˇmD .Ҽ]ܲ M/_~;i'f`]g==fҶ+݃WHP=Lc~)Nj}& "Hzޘu租~u0 T Tw"6:#ACmۂ u_\1yKD:52PH$=]m`F灳/y% 4fJбZ"D3ng>*e4VjpY{z̹s/sge>$VT]p8nZ!LZ"[Իevq;ôQ2?|h19|iJzJ~8y1fNULDr+bY N/>ЃK.jMگ|@%G~v!^هyuuu.q)#J6* Q$(nR]zۡ3Zs*y٦l!Ow/[5ӞwμR }"AIzQvvijT7Bֈ X8!Z2ގ1qMۗ<߽:F|_<;o5}x>LDQ5kwݓq#;su޿Ҽ]ܲ/WDnxv%+JևμRܰa%ߘ<'V ~YnXDގ#VTXC9Ga? X#8*z5iممɟ[|tI{sgM+i(vR]z-ϢAK%D$ O LUj^ |B\۰fԁ-hʰsSC-RMY( IDATgΛh&I22r(iױMXX*mX8aQ)!d-.~٥+!n7UUU' ' ='jkk#1fxغgg^&F';F\fЀX߯2Şώ(nR]zs6~oc(M˱ d^BҐ]hzw+0+BZgg^B|ّ-[]K]oFgyh!H%L[vK;Z"J5S&82R3!bQKB! dgBB>;B .|{NMtv=/AQ6y_B!G>;B .vEY;c@j&B }*Xx B!DPҖ!B ^ {{d 1y !F7 v65rcPyiG(%[#*K'|}R"vEq)}"Jagv%苺Z?hƎWSBŞ("'YgA"~H4QBQ!B nͻw[3>!D ăiBbOe]{Gg/*z_CQ[f>y !w^P_pqZ[. o)#׋nVK!O{]e<B1${mWk[O+u4M>CtB3@j !" 'Yl v>,8Gtfx_L0ʒB1%;kj g>/@ WiSKvB!Đf猕* SL#PUHV\.CUzO&M|҄w6?\t-PUŔz!BY}"/Ke~7s+1 n9xq#֮Wʪ*|m!B^Æyڵ,\5kzf~sG?̘^UUҿA!(;Ȧߏittt8~ʝwMgg'wqGڱ cB!DD|o Be1'9i65k|!u5B!D_Dnf^#0gxn 0M8 Κի3R>uhܷB!D ,wߥagzW^].bTE去{_t'|^7؄cx-yilx]<O+#BXDIuu5wט8e:5uu̞?_wִK?%\ښ]iiߣew355B!zX.ÞU;v{.;My=8*Bv{p6B1Yq `B!'bj q\(hFp(DG{``r!Bör=~oFK8-l}ڃmyϽ\tx}~*Uk'غP 3|B! xˈ8u:m-Ͽk玌EĢQ-|ڸeaTE#E>s!b豆x}q//~53K*Sf̢~pֿ-6)}=H=?P?Pb@B!菴!G|/mMW2s+lkMs85PYUwւ ;l#㫮Eaʭ !q3Q8Xp|`G8sgr))3fWF8dM*Ga^Zvz}\*U ߋ];iݵ4ѵ]8}(鰭B!Ğ /^ҋ/;;vpusCdҤ\{g7)1rÆOf⫨DUdJjp:AZa>;5bAB\ B! b$ %/a0glE!ޞw.S[p8(`)Aӑ:4x|>ǎ-Z 4@2U2BPLf=`]hikmIvTUfp9\N\.. ˅3e/աPU UQq\ԏ<]hCp#B+r)'sÍ7 ,]--JX.'!B MfO{th%x}Vacbf|_iZx0_p]N'!!BEOD oTbhGә  CtPMEIL$񖉞\(U!iB|$u;CӴMӈncbu"$$$^k&PLtӽL!Bֶ 4WQD7V>&CtB3@ ?ىJFDTUE7]AD5`DזCa I4hN!e/DdhGPjkv77kbTVZ#&¡^?D;B\TӁɡ@WDfU01LXrN2Bx{@Ζƭp! 1}&9Ј0x{[L6 *-6*U7֭ %u RZ>tEZ0a9 B'BEaʔip8Z].࿫VYGl&PUŔ&jHU->S5tMG5tMCo[x]<1 ѴaB!ĐOKeJ_n9ΕM7?_׼ U+ڃNˮ6 4Z3Q&&Jv[0ttammAt]y !"x/^nZ.Zce5y-M&ilMM]; -[VMjHHa-蚝rm3:aT!%nćx槣ߏitt1d QY[?PE(h-s[#F0׉t LtLHֈŢ;l"1~ؼK!%^xYQQA(BӴHT@' ѵZ,Ǝ-ԏ㉯]1QRQh`sFP͍[=s.gYN)B5k/ḧF0gxn 0M8t43ע|5Zvac-TTRYUc-a pͺoDywFƌxB k}a8N\zŨwݕ|݄cqc$hƆwd<3E$jO@Lׇ嶦O$Fvv M 0c>qB! "ݼU)ә9s&|=^F=LҼGfjk4P.bXAaeMj&67nę̘: B!-;w`]rzp9_u:P<.ZQt]ǭ*hm e6gΐ[B! B>>vgPla5մ ]8]N\N'jYtpg'mAv:njf҉R!(Pޣ3ʍ*V T5dw[;HMPEUUYӦR_W+@!6tAD(*+T̚B!" uXl@r:Y:9T+Br3~|rX](xE|y3pg U~o !w3CJMqL>pP}@Np2e YMt8R~rBыxܐa{0ȸ{ bQLME 8`hg+W!-@uڂx}>[[C? gw#%P]C[[@|B!DnJ|jLӤ3Z 7~TU+eK1 P(u23Loчo}_xguB!D~9Rٷ-[/̟ati&/\|ٲ|[rό}1HOB!M1D~aZk֥-te׬㭕x+IJs߬9ɀAɲ3kNB!D l8udV~L4 /8Z~;a\xyL4? lۚ3%#s#B%0sܳ~( >@7{jA 裈Fܳ-},7GK*[j+Ё[߇rBћgq]z5ƍ#p_k&98P^:g^}-?9[& "@!o!9:m0K rHK؟otՌHm + 8",_!vF ٳyy2ud\.'KO91.xeN?mi dK  .Z$ XAD">B!}zaFܳvgǎDQ9H9H"(;vॗܳșW_o) gK!}zm0s̥0b0.~~u~+.Æ̫赿C QZ'-1DUB!2+vԓ&;<‹lٲe^̩'-tz;օD"L҃!B#1_KOcjkӖgƈqԘ+MW;'V 4 7(*Be "TA<^pL\.'cFjNӴ Uozjc5Ac})W!ɸT}v[vH秺W^y UB{/C !"?["TU:PƏyw~5nYdĘɋ@\!B "2 Z}k$۶oWloTV0{+ZB!DnfoMB*9Ke׋r_eVB!OD(x}><^/90s,(\ !"3[Bѓ&B!D8Mib:Z,60'r:/A!4!猕3~<JbDxf"Ngim q&BCQnp2}p t8L>w֮Ab8]Gq㨬>jnj2PikB!Đ` 5-ؤٳPIw!doUUQTZ00 sƇ%Ҍ(!` ~k%555c۶}ĉ{3lX>5ì/uyp8F& !K1#2̉sI(:TAabZ+f:\^7N0vF#[0-eDkhk p[#Uo[ىQ~:6~b)B ykiƖ4F01-tp(N#((880P J\3˙Iffi+RA(X,YED uW.~ D Z dS("M$>Lf9|̒d&[.5ٞs9M1=iAe\}csIDATlCETsUX؎R`E>DdN{yX/ ?PLO D(GĖ6B!@ϱ Ƕ ߍ W3vS E8[c8Q%Xv<Θ;߭TtmwVOzFybA5%U4`Ňc˭!bzL:Cc)츃,q[ "8 A 'bRw"&ZA;Y+yǒs=i,`+Fj[D!ӣ,iA*GaGByrADhKX]1% DZԐJ59bevWG3`J#vep#VJ!BLgJ]7kEFlG88(pyu0R+9nrwpl=mtuwFr˲0#b'},<TB9óqN_yWCp"vZXBhzrB-В]/M {O*yQgg^ }A4Mwi;D(P^6M/0B!FsO]) iji^AYEMrG`oF3@wOO>RT7d>Ӈeve~7vv&_X.S! v4>khg&<00#SQQIMu5ߗ.Hs4e$1W!mi7eeD)i&z(B!ԍS4LiXwHMFB!oq"@.B!MtI!N&0ber6Ιr #o{?^\!B-8ieolMʴ:8MMĆ"\9L/B! 1N ˶1H$@hF b\,9ܸJ2P\!B'7|a fE H;gqBP\!BL Fψm6P#=ㅈ6%eBan/B!3ưJ)8 'Ϝ< Uλ8ʁh<)2Vɺмg~z̸B!qƒV8=QV^` WL1y\XA ƠjqBQXְ9`z3w*6&OwlI}3߹~&]Bw"{FvA{9B19Ǝmy?ڇ2O}94]%_lm?{N~Tܻgqλbjkk$B1)g۹w +tLwߊGGYrR/[!.`w^ۋ-gR#ٱWC[ɺǟ2q?CXTa!<&T3gc宔+/<'|*g'3Yh@cN4~EEp"g~'ʐ/nJ9-B6ngRvxWXy^Yw!oߺ}7o+jXIU)Ƌ/͜vGsy-'O=\ɓϽR!sa(..Ʊm0vb<[ zzp{<ĢC9,;ycv=+c|9 pvQ dy}~JJ+xoCqOFUu=k׮0\$֘qBٮKq!Œe5,nA#}ك?544?AHQ2 @wOO>RT7d.cşlW^zqqBٮy;u,9.W`0e{3L::x  zSQQIMu5L/W! :;hҾ#fM4^2n4MxL=^f+BI&5 k36iud !sQx`@qфO&\KwOB!fֶv{|,[hmӗdiM(Bq]tu{=^QS(cXĮr/r CC!B!vwPS['O:}ֶ6n%@}X]<,R[߈Lvє> 'hтr?bB1 F"4t<>f_~cVO=Vу19umnp L=rp6oz˶q2jB>qn'V"wc'|oH5[Hn©>hd*$¶mB0^ޞ)^ S:P(L G![L67^AJ)((ǙG_?gp8nÏpYg|?xtB!VѡQ D֥qisTP5_Wu?w!Bѡ(ǙkOԓOﯾrFB\xQ3B! ܉*p8L0q!}_wO3B!Ԥ(|/±B[ KyY}}s& B 3kSOs'^+r⌌)w BϸC5DW'?/2޻|s⌌) B鷳s'SYQ1j]gg'QY9z]Z$B qy< t*ʯ_,}?#BeJ!sϽ?@kk+߼rW,oɏ|+?9NĈu]gkR:o^*#P2S2B˯$uBzl_TڐjXNuJAzs|~m}gШ?_!sNZ묹g|咋wM܂!* v3l(.j1f(B!fPu5|㪫kW65tuj]r%-}<84xꚆ5Y7ih<!hyǟ@ey<\0XS\pBn[}qM D|ҋ`B!ػ)NjTdJ! B!Ɠȹ3Jӊ *BgS /5$Zn"ʬYC`7Pm2%KʮQY*cn,K.@&eNzxOJ"G-g>5S=֮a:?J? @mx tWh-DˬN~g oOξ2u9@GO}wd+9 ciY^[Zg>{f;!Ĝ7:Ctt TDd9j"d9 ~;圠=*hhj6 lgή* YL#s44>~2Zsk*"'}J&*u-$VSh42ljYRYiV2JhU*H'#ɷRZ\鲩d'Yp93eH']8jbn}*D) !D69'!g;_R"{@ !B_%wc!c= !bJƝ ?ݭ[D"Zu6?E~-"_BJ{=FK[ZhX@qqn+```m-,8P#Ĭ(rGZ^t]CuR`Vmm,Z |5QϤ1k9Cm Wu†5mD"A"a8vp${gkwS~2ZZtQhھ ˶t.r7…;0oK\,+N_O75)ّ0MD:oNizy#Fu;|^/9vFI9Mۚtei,2վHx_wضRm~8td 륺j6}}̹6$Je3SN/ϣfZf6w?>8=xbˠ:BLH,hvg;q,fK6QsCCCCN 4-{8 >ӜSD.-HpsI'oֱxcF~;iokr~%_BV8P(ͷʆ +_˖.ul˲ |ՙuD %p˻QJq>ϪUG s{n6oŋ~q?b:AIi)䉼F\0DKi^}c@Z8eϻ2uZ·ܥ;\ܷ.ϛ{4 D4D6)Mۛc,wJ$n8|pq hp{Vv-J)^[{Q>i܏-(^jjkȱDz7EKO&f;6ce$69nel1/cEU+Zz5>m[Yw"[thēNmm jk/w`&]m&n%(O"q&D6UxJKKHۈD"A{{:55%w"r`Ycɺ.2?tz>o1#gZN>$sˣɿէ49øFmaFnZ|ɉ8˯fϗS1)A{8f*6m|D_~g&dC3s]>Xw$2 ]< ,O˿\Ӵ{' -9qn}̫^iFVω~'Dx Z<@gw%%%MDoo/@,x F,6vi躎0L?K_ζ- g9lۦMÈŰqp, t<^/2,ZHMU2.掭TkGFmi +eY++peSJӇdb !fDu][le%roh`thřh4JZ[ )/+eXMWw7|-J%US= 4twmdu^:C)aY6}̱_%Bf9crWukMhk޾O'iR= Kww--l޴˶q>Ic}=e+)3 O$B+ ;HseLj{(Bzݝ;934M+ D"D1l LӋ_Ή*dCXR iXEOt !b1 "ϗ M4]ߧ9 6𑏟R:D R!i00 cN&qL?rCW\#0wrB1E~tD(so?(m}#}B1Jd(vh(K/LQI)^n?(MpkrȔDB!CC6l@qI)>{mדm)3:W'S:+cXR!s]QךvjؤHU)F=U="B?;9yx^|IENDB`muse-4.0.0/src/doc/pics/vam_synth.png000066400000000000000000002241251404105311000174670ustar00rootroot00000000000000PNG  IHDR0)sBIT|d pHYs+tEXtDescriptionWindow Class: muse2l1 tEXtTitlevam-0O i IDATxwUa I(F]Lݷ5U@W S׷bEE%q{f:+[̜/bs{Ovu7B< "    $"BD'&=p8SBZr%D=(ɩ>FFnaɬI1E;CdI#*gSԦшCl$枢~WաM9YZ"I,# Cp t*F/q?vUv Ѹ8UF Mi9At؛W .gic2Qmql$HmU.`5zKr ;W-z}рe/uE+Z\T/gDՆrёofNYl+'n?^&t\9^zYΥb +s]4UPqjlOql7nOtN|}ԉ},# C(t@Q!"At22X.%,^QzFV2y].U"/RUjU+P1h {uSmEC3-[q D7S7PMjsDΪ(s41Pnb pŽ1Gv(Ԍ+E# PU_p޸sqhS 󎝨Fѣp8xkjjw_9 "%&Er&pN}ϸ35V%Ffk  ß)װ8cq[mpV]\oVʱ3W L@9ۉyfMsϱ;c#StǀK(^Fl,#D"=.,˔zh {Z^οѐZHh59LF`39KUg_~Y!0o' OX߽g/:u555ǟw"?? (**4#~uݻ7:@/Q +GQa!LG6qgC''kـ162V.{Q" sxGU;Z<*1L1.QuKAoTzlMV<}#dو6fi`ghvM+Hcr*7[hA`dx;l\t=HfnA@US.qcW(*.F(BNnvBSB]5P]U!ڑTVUaذp]շ#77~%%m(vF#P)*` V5qs M`71C2>ljc#۠\3WjpeN\_GV%N<߆z|Db׶ `(eh&匭5K hnSkS#paߞLVb{!=-'}!<χFȮ|64c-;f 8tٱf~ A0p`:t`P.~nKY`(PڹPa#*~-_E)׼rk岗rK ZEV`Q J,e6"&r"Vn/* x18֢(Ҁi:_UeDr638_~d\Z$/\+E_8g{zzDU~U 9쪹9"{YU*!p9Y%1꘱LŎwmٛ֠XS=Z#رsGLh u;Ӆ&~?wi5r!E}}OZή[ok5м*0Q;# c#۠qD>Z b_/h +r+Wj2rRTtFT֣? KQKS{I,Z 1}:N8~(fθ c"]qY{ܰQđf^ObˊL'N9;^4ς_.@>/ $g]5׎`_xYw+fetIL25Vg{ԄK {Q̙=Xí9E/AްQ_ׂ" /ć|U4$EVV; PYUݺ~kD.]PYUP({"7-9eorׯXOxg] k\+#^} VyAAwrU"FTǐr;QYF9_gڵ+5;? sİg{~0lx3.a|UQ2hg`em/c&L{!*h]e^ώFr>FkIz,c K3ܹuu2X__;~6믿kŋ,QV9sǃ<1lذS5{NM gyꪎѣ{X3f4=Fح}avYniΝm0~6Iw5/&/ x)bg\,%xfU`v-F5fu?E /"zQnQ ri_u-m#mtuc) ϛ8Ibq.u,^0.ѣqС?O>~K0t(&M?*v{1kܘ9yq̴D={YH}LYUv]uV;6"r Ӭ;^fv:^yK {e@DU옍Nh7Za/Lls4K^w܉~464w=)ӣz {WCG[ex<} F@W g‘Ç0?fh#oi?W-44-B5E4 [o]`LDE4#j1qbBNK@]η11ÎQ:+9QG> ~OSŽcrIY6PD3ś EynXvqPr.^dĕQo\w9!"!ƝsNwx7|k}|X ڼO]Cի'DQD^=q!,{_!}EV[$>,0`' 9fhAn#?2Y u8|e}X1~*/-?̼HK3jv}ƌYm@ v^6HNy(3TgRt}~LCF]LYI-Zu1ȶJme$IA~(}やei*0Sԫ{cİQ\mGPd2Dl>IOybƍ5W";bܸqE.~u1e?W^Z.Rڹ3f;tȱEz=ÑvaC׺Ѣ>Q$;N^Ʋa{l}Q#Ukr1/cYYGU#yVN Ĵc #c8*~꒶W uz~|_"JBkN~p_2f?_X̘1`KӚQEWؽ'+r%҂<6nzMعJy׋PӼU&\ޖX~=&>PK^=vC"g߻gjviWiȻ5u_~UI22+Wpۧ hʎZ&=;ƅ\U}4]273bkmDfGP !DFxi-<kEL@uT)86,;%Gr%eyc+Y<([F[6ɣFBG |Ez^uvvb"D\NBXO;wŖݻc_>o^C& ؋,-|Jt\={`S5{c/U>DżmUb,Ź,7*9bdqɓt8c; ~qIYoin-m''VU^3θ G/`&9:"]k蝲vu`g >8#Y29@҈[ m =ṽɯ*Z~c}C|JT'Ojw5Ywv˿ky|/~]±Gr*^k?he^y5uYbk Sfynd]F2ĵ"-8\uOZ 'sq1<شkdyϵ.Y|z\\n{L0auf_<-_8vkjڬa/E.f1"`Th tY1oXЃi$ cUre#b9sqӍ7Q#ѷoX]h3Pqw6VU3Rs@W εe(cSmg XgGU^9D+ H*EmH϶zM;2wcT `hc$:ehO'Zn5G-idQVSƌA}Qxw>:{Х؅+{"B؏[›E0G G8۝wv6nيfO^={{EHUiWǜ/;q_l*E*;}v + ڶհQ۱ψS.K;q@ufƱ_5hp8AKK3­Op:]p"Ԗf<9^47bZpVV60뷆5A]bB@>bPX!h32*o}.Wd=aTjJD=&QߎfL2*%1R/#1+ڣ$S3 p{O<ݜ*=Ph {Qzp {>Eu44uAQ1((,Ádg{PQY.yp8p kxrr0dqٳgQxiW;1.vxAҾa6N{|ĎGߞn>Ϊf("H.p``!ɯ:P(`0P/Ts/ 2Z"tR.*͋u-;H2zdsGUvl$W3GBl)? ozz|_7cF_Tra8_  deeczS\8p}p\СCѳgOu85MV|4U%.2df _7]ӎm#_^ez4lv +]$H KE]Tx*4fEqBu`#l^d٪ӏ*p0m(3IPxkm@VEQm6boq6$[-D-;y-bQȐ:BFK-mv$إ$S3حFVSТ("Mp]x ۝sHM>2ξkj )}7j1KƫtfulͲw Fg>?؜$\1Ǭ@,Rlqɚ\VS'#14QnFL[cX|s܎EdjLMK!|#2nigA@VV}MƎw@(ZP"SpcuKWŐ&ug6UQ,fAkIDG$r\iEDXѬ,H9̘qNz5"vٶ ʼ*0Dz#@ *a!0޲6ެRk%)L '-zּfi Bļ%YZoyѴH7ht޺ɐTMHEVz%Cs6h!9m5n͟H~=btVc_՘&8t"2c'PEu10dbF1sې3MX":^ zj jm1k퍮4P om2:.bTs+eb\@fg%FE_Ph`8*es ^ gs+ Ś1fH$QcIG/LjZSD*;?b%/v8>*[ }Vb̾Zj_QM~kتEq*!2 k1!/ۤ%n`hZ1q rS-ml[Z gBqCE:;ٙxv)'^.#e~疠~it { r\U"CKElo~XoP_<VCkƔG.U'2̸9WGȫ}D~ża{[Ыd"d/͛}egfAn^Di_HKҙ7 CI+F6:W^c TdؖY;fف &zhƽ-bGxl)3{Xo/u+T^JTsjDI|W^(߂%J>jq.2ļ2=Z8~uMSr/I^G ^~mփ +]_5ld]l6*55ıMs7&TX u@:,mSeGB͐HTkB_v4œW Sc"ޘb7-ʄ`^[sXm7R:n$B݄u*E:[o0CK^"t8 G}!}kOJKJJ3#j-FiPJ eA3f'TF5~ul3i]_oL4󭌉uVߜOd7!UHlzEzcm= !҃YV8#8j+ DقH{/=ٕ+/gʙ' Șk >\j|SeVWZ)95j9ڔm[L08j'C\)AjVp[:E#v8d+$ DlU&# !H'g1i 0; h U|x5Ypp82KICkNGsuK {Iҽf>)XeIەSEJ'dPy=X'^KU @ t 9!8"b8v'ſIi8͙U9! sIxr<̔'z_׎~jDrsL8|p݆m@@׮ݐi i?8&@2ͨqBTw= " aR֠>`̢[` %3̛3L3,D`R D"9u͆[Egf7cܬa3b칁솣8].;w ''W Z (Z45JC]?f4^qۯ?~Gqc`֠GިA0 r"Ux01E$nZSݺPP(1N HHT/ؚk6{WnKަk)TfF{Ꭰfy}x`mY(DC}uAUx֠[E(4Z-m_-,FPEg$-@uR k}/ QEuϾYmbD`k[fFn ;EFm@0D^~*QPTө.kd- Cv1MgĤ;??ѿN$>daL'#1H}mﯨ8[[p /脪*t2B!TUWc\Vk V:` Pu( FspMVC iBJ$IdB}!PN+b/`c.>W! dgMj&_¡0 A?ԄP8 D?S,oYMhI=dIA*A՞hsv$PIfahj41)m Lqc؈z&!2fRy 'ߨض&ՁpY=)*J$)WZ$J?יf7VSMԱMKvr*S&Oo&X [D."h~Ow6=b/$$y{[fm;<KJ!o-YysYb^!قp zI֫VLŌ\c'<[(L+ Lҝ 61OL)wFD$ԉ6zQ9a'Q[ 0)#b茻.yc456WLuv^=I){`IkVzEbuhnm@fކa}FNH{vD:މ|95 \:FMP%1ż$@ m7pHBuq1@ ȟ00bSbi]f/ނƚ&̺+/&eu f8 ½'Ţ{[a R$?N\Ԫd6D2$5͸w 8낂x≘>}:JKKev},X]v!77cǎ-܂˜Ͷmsa۶mÇc֬Y2df֮]ׯn,C',/-dΜ[z_)jZyy7Bձ4@=m 6 KZ>9utuAAFfb2O?Νpi݊¢ͷ~Γo w=<zGEgBk X9 $ҥj(..ƈ#p-k׮)kߎ'|2epfφ '[n;woٲ{/Z^/"rss1O?ٳgcܸqx7oOǝwމ;vpoc_ff>;A$X0QLx=3l,\M7܈?lz3=(+uᅬŋÛgy6fqms[VUpƙg['_"ĩw}_~9#Vϐr7ìKї 4&[ִ헅;Ld\KlfÆ ذa֯_ŋGxRچ}>Be3fN={p8pB<_~3f ֭Yŋk%\ve/&7x#.]> jP'h%(/v c%ڤdi̻}tg1k&/O<#eKb p֜нf5[b1l*.ض˯ x#3[|/~?,L38eee5kf"u8"\tEشiSlYgyf?^}ĉ믹~7 ɚMpJy:A;fm\%w[cӁObTAa4eljSl= כ1aDI'E /۝}aÆ<967>j&6nFJ,[n im`& 555Xt)phjj(xwO>$JKK[oǂ b=^]eeexce<fҍCȌ{ۄ[?z($CΝUۻt邺:h n̋jy^+:&->GE]rM) ԏ-2w~~>b( еgn<xo3(ރd%ӞQT,Rx;Z;Z~ם;wƩ?O"@SLAvv6૯ҥK79sf̿}0qDL81VD+Z-_N`j;(QTT޹sg9r={(,,đ#GЫW/JY,3Hb~dex]Zoth?ak*3䚶r,qII 3ƻS9Aqv؁s!e>-E7-œM)M_+9?Si`Æ ([袋ben;V׿K,3PRRn 't>8Dw3Z$S."(ժU0Sbۇ ׭5\Í1{믗m_v-Fi?d|\0l7ߠO88u%,qݤ[F_cC457NȻz zj=l0v-:#;`WxwbΜ93v |rOΊ[)ɢxCz9 tDcٲez{sŚիq_S֊CDzvnt{K/ƪU0UrAklR~=~ॗ^}DeeexW𿯾F|>XbcwRFtv[ΒJJoMz;m$-i2vt"+*`ѢExFٴiZ,Yn,',[c6M+VW^_{ +/4I,%xibdv):_ȌgzHaOY젌=˗/Gss3z-vmfºu гgOaC~]tAyy9n"uNpfa=N:$<ݻw?/Xv1c0{v)O<Ǿ2|p<ӧn9E.@?EȐJQZ pa3[^ic<998餓gE޽c[:p x!Y<Ñ0v,3'fowsca]w~-8D hH ?~L:2VU&qH#U V[T\5テ! fx8z*tIw,"3z0/.Y!Ctrad>Xş|9쬻hiObضg[*f6U- ж}7Qikqm=r Boh$MTad3y%Ȋ%TdnHSd2X#a9f*M#l& Pe d^I'=" {O%gu[[dL(g+I-vL@S#vO1z;At$̌D圐-2@Mr2QutACpAiBqH 4m2LR{L% AMAa bzz & ND!NDzC%OAqZ;%3 0d &`rT=E ?Zns I!sA$ָÖ/W H('!rA$Hja*}~3A01#+)kAirAGo@q'v('!rA؃  "qAAACqOpP@ M!.nNO64vHNF ]ulyp@sK >!(',okh( v(ߌ D '0E ҌAA 44Ipo!\.JvC9}bf#=Zt`9a*޵ ¤b ДOmM,EKs3PCBaѣ'v؁ζŦrB O[=q3P]v_R {z{N92icl" {O@ϗ!B r^TWWN3Mj7PNh荝`0@׈zݘN}?>sBD>})ѷO_9%fD܉p8f¡0=hS0 w;55_ZFBAC |I,fډ6777Vw(E(F qm4('tXc' ǟpkM W*7ӺcS[#%rI6m^{5ٶ:L0mכu]`( `ĉfqq≲e¹sСCOv~h8Zbz@9Ay('߅7vB|EXXVfڶmnvL4 &Mٳ?o0pc۸a, ~u}ҤIذ~lۚ5kf 7`⤉Kxl(e |&|&|+xe%z?lY +&-{Yo^ۧڞJ('ď9Z6v(wϘ3gƍx:N;4u]عs~JDY&L[>Wcձmزe &N\7mڄie0qD\{͵/PYQqgC]]]q砲UUU}l70| ::N:$,X@ٹMMM"ֿrԄ//gSvލ;?_|q̎uuu_*((@mEE-((o}F8̿Qd̘|纶J?V.Ĵ2twΞ#F*Ϗdee1B_~?N;s>~9y2o5 ?-rBf#&TTTW^V?%"Ipȑ#|rtIm}NkN0|p8zc>wn~w`Ȑ!8x *++q 2@tRY EQQjkkU?Mk38Y_Z"@ Buu'Gp7{=oߛbXBvRC"4K.Ayy9^ZӮ]K475i('hmB9!$e$'իW뮓m_v-F& H/'wfҤIXr%.ɓ'c4il{߾}q1  b(,,!Gy ]TG+VVlȑ#kD[ IDAT+WLZrӻtׯGcc#-]~t]`={D(3˖-CSSoom%=Kx2thrL];brnqԩSꫯχ7x+Wĵ^kTDxꩧO>/^O>}Qdo&̞=3fqG,;v, ;6f?sL㏘2e V\U^fΜ[bʔ)ؾ};fΜɽ]lm?F7xˣK1h x^a><9{ιxqwnFla;&""~O -[| (-_c]jǢ@9#-vC@uU\nWk?0nTTT}~;6l؀_W⋱a<8cLS+9  ?CUoߎ&_#GliL[&DŽI磺v\kVǽ[7-{\ r('xc' |~ᬳF߂@@ywVYYp݇(**D^2S lބ /"L&` 'igdşN ;UUp0DVV**+pP*H/Zct6TTΆ˝ G*W! 8cE;A˩&nŽ! //g=O YYY`OPTTCX}./FNnn틽֡F"ѳ9P`_>O(DgX*++oOD.]D+_Aplz3=Y4('Ͽp8PTTw_OeěEA~><} F t[lzdZ['S.돽סF Eaatv/r;cQhSNC9}bt8Nxssb=m MB~7n4w[ͰtO ҅EA% cGD [phAl^/rrrꈌU('?hjZݖwz+$2crBq B0 h   2T  hЗS z7MH[o_nA ('a+ِp'Yne\@ AM2~9URDG)Zh-\,v4\7mxc969rA@98zFq'6Eb/ -JYHr*=PB|>jjjPUU|>Bt7-mfӬD[r qI(FCC=ѿtχD B}m vޅNЩSy8RƂz̶ǮSN r=dnNAuĔp7[(FI(BEe%<9:u4=LEdg;ݵ+:w}#RR1?'`+ZL԰@$ Փ9r ,;}wlWGEE&]2h_aTUVrb![ d  Ӊ*)6  ?/~"#5xWC'}h46PYY3Ec'A;>9^/:D"PN B9 "NDHɳA@aQ!j\~;|nf9|;\{ OZ<#fw͹ C sXf ?rp-7c475a7[w>t7' hll'##Fn^.^]rsspwo;+ ǟp"<99%PN V&AGKkjj+Nm `޼y7n>t@)/O"? عc'/_/>,&`.ƶobxA oYYY' vCŴݻvÏ>~=wsƍøǡ_~l @9CL3kr#PN0F{ A#.>{RAO"uAAХK3Rf :n(.Ai-D1 ߋW@} @'ѯ8MM>Ʉ=^(ys PN03t@Bj)&#ڔݺuѣ|r\wu/x,\"7v&K(Bscn뗤#`w-1h`SFy~=~"˾0~?|gnc+v֙[@9s-D=/yhllon|\r)z9s?9hjlDSccFQFfB^6nDvv65n,('tt('ADg~3eSZZe˖׿>j(̦ ?={'1f[Z rpggaÆhniƱ{̂ra Aq\}DEd. ݻuC0ݻEp\PQQoۆΝKP:`BADrAD*Cfzݻ7<>য়F0B׋A[n())MwSJٴ2khPNІrAt"3Yzza3\xPX\z455! r"''fsS0y;sve?NZߙ@$ |('D`}>|8ʦacʚ3g{480r1xqK7~UUU())i3?ѡ@rADd׽ży0tڵːo$$}>TUTcoA ,~[1PYYF/M'L@90  8){ ޣ;Xd ?_ŷ~! ߏN:᷷Gsk_ޏō\q1|ͷ܌k֑l[w>ז hll'# F^^'b9rssmasge޽qܰxlKD@9A Aqӫ JeLƌ3c۟}Y -[_x;ܱ˖-_| Ä֋4d[w^x<S 2P`ݺ?؂p(t8ѣg/degZ QIţA96EџsysqrA sq+%u塱1i& _otLA q啿駟:p!ɘ/Ke*~.p  !8\Nxs<ʲ=PnIE5ݻG?ž={}ŸqgF~Le:t@w)UFQXX[|cnr삂=:ʼn'vӯI @@ˍf89t9kBˍlx<ٶGEn/RwOǺuLW~1X3ә 8-'&-wŨQ#cEEEXEx^],ѣG ihiiƠ!t|^8tBÀ |>(5x}Q # ֞pXDзt|e(',('AIXGnѾ&Z^YY{yr V|W^[￿[qo%%%(//Gnrйsgl F<+V #u$/5| h2 vڅ-[`P %pee|?:w˾p'fcDr9('+SsAt}6ԑ'<'xzO>=.2twq{G466^'|MzEQ#g:I8Ɔrᓍ1Qhjl YYF]V"PN` A1Z{>}6xeXdql}ҤI4iRl}„ ܺZuC~~>z?lĘ1conA8B0;ۃl؈f;xt 3PN +N<\.u@ {v?]J:t۶}Ν1)ujAd Pf#2׋޽{8?~s^DnPR9_.LS˦fD ('PN "BV7̋dOOF E^MMp9] /^o>۾\Բ(y%dE(=\/AXr|޸9UL}[ӁN͍|0CA$OȴgO{ʦNET1єq4c^W)'Ƀrl sqCاS myO{.M  :2ND"zӬt7 rAN?@tt37M2C9lD[GLɵ}i   2V[e&$̭7j7n(' \?M9=%: Awq_:WGbg !h;;*-RFKb6ef  -%%{y۷%؉}˷lSo=d=/c0nN4n7TWWS[[FӴT7MtsY W&RK2A$d=qO Milhl& R_[?|ĕMn("dBشؓ7ϣz|hƮ]U8\>P+0Z_XȺkر}yf.Jźhzz6J&$;I&D 2H ׯMـDv?vZ~M72nܸ}Y~=]i ]שb`!L0lH֭]MuUyVBJ2A2A!DZvՓU&Fl6k1v}@,^Œŝf׮*&NB J ?ÁJ^CH!kqOɃ %|r8bB}Orrr )k[,4M~4=f&JVv#li’AyyCf9ddBLBѦ{WjO#&d0 | *^n5&a446ү@A'դSRZJJNNkM@6bRo${T֙d,dBzL]LK-BBrt9ꨣwxۗy]9g 77d4/ <*RDB4, ۶mgLH&G2!-I&!RT +{ス=ԩs뭷1yDx ~_\mnz٬ˬf&2Vl6kZ*1 Biy_wS$s%]muO;X—uGK&tD2Le=Cy sD<3Lc⡇n1qY5c1[ؾce#鏫(l޴  oK **S1p0Ɔ>3:TY$HLM2!-I&tPL'4]v8QضN=ŭ~|-\}U 7:1*NWӷ`0z EM0H*lb]46Gꌶ=f-dB!^<3Bt0"[boߝ8i58#Xd)GqD3UU"+;UV0nAO( +V`Xе UoiJ2A2A!c%;qqFP7:ʢzV\nGU[UfgŊ47PRRBVVV*-DRH&H&!2RZq)f3EE㏼UNa~>ٹk'}I}9d盘o==<&t ٜ< =dd]0\*D<\zT7AF$w,J z_]~EZ%Do'Β97(qB!"Rz  ]vE嗇Bz ).$3Dwtzqu+=As֋m#FK&H&H&^']\']$$k}zB~zaesanuY2Af .G2CtwJR};ze>Ž7BM2Ahrkj%GW PSWGUu55uu4=5-mG|%ާ^SY浥}nɄ-vL'ǺnqHuijr3p`Ajjoq:d\rD!z!]Ѽnw<ǵQ=i쪪[c0Y؊(,.bعo7xllrAծj6C ᛤ( 9jW5LrAmO:^wVjr$$zɄ;sޅ>bI97Y֛\.~&M|Ypݬ]uY25Mө>t-i˫,tJ4aȠ j9=dB$ғd"q]q+ۖŌ9EQ(((`Ĉ|gy||>.b. 﫯6t(1|8]jkNcc#$ZyLJqY V+999ү5M6n܈Ichd_b*LlLM2!=I&N zD~΍}# ώ9\Ǝ=+u)S|Jy$~N;Wf&'t2>Cet&xhŤa6X*RDB4, ۶`D3dugLM2!=I&t\.]܅h.7C￴,̷ GrJ:L&Mą^fZ.,O膁x}V+ ;Y5-X3lZ0 BIY9wS7>c;]BdBh I2A*qO11cK/1z(N`r91b-b̘1ڙ\fՄbf픕Ϥؼy(  ߰u~TDCC}dȗ9Ʉt$YsY⏹=[13+2y2\Mӣ/75W_rRM5NX}:(  6lᓦпY4MQ_WG6q*>ꌶ=!|fɄ6$zɄ _&,DH0Oqc)//g?(/+LQˢ*YYYdgfr : /( +W`QU];h!ҐddBK`'|#f3 k+rEaը YPS[+L*5@A1Xv/_N]]%%%defB$ddBtqc6),,$ x1F$?/Ō]X~=}e t=dBxB8vJ˰ll߱M6_f3NPTXH^<v{,ٳgT&O2A$d=Qo7WCx=hA լbq\8nu_uAc;âsfEߞm Un9dB/$v9 "[oi>]UFFyjRtp:a(({nc._ϑY3kvs(Ft$zɄϼ")&Qe]$RYX

/Aeѻ$pPV{wa(j#Ddk}gyg;,={gEwÕrtLp9Bλ.A ͽَ7BM2Ae a.Hx:iZ2Z "Q$viU'4660hsAjkq:\.dBhq͞2>4MlO8 0YSTT̚_sNPU5]u!TѶ'^&ɄI2!L]۾]C:ǟpb9s[ٵkR,mSO;m۶{ͷ?uyu~9j"U 5NUu5^@2A2A!Dlo.o,]Ktwqw0y$>YI?#~QWɓ'`6Ytin^{5,Kb^LMMTW0lH>@y߇eQTTnjJYH&!۽g(F#y+dgg35oO/#?dƌn֭[/  EEŬXǷ@ԯ)4]{5=<&UOn>K^ )}[ӄ%#RjhT|BzY;F2C =dBo&~ ls뒺 uuu꫌1E|?OL /\W_}СCak]  7ad0 .\_7HFcSDt2Vrrr_klڴ 0l䨈IgKb*L/dB!T{B$[B?vm\<~uٯZs0iD.w]_}=Sλx,|f!\|qB^C CGafif3&/k-S" bPRZ墩1M',$BLHO Bi= p0f^~eFt2|p^~y1Gn3QGf3z踵+b1[ؾ}+ee( lڼeh8`0]$o֤9i"wddB:LHr2A% LS&O뮻G}7p#W]}Uv۸׿Nt32͸\NX} C_( V6nS_Yٜ6^Mӂ}>kkLLH7 BTsqj,;-zÕxX5rd=r>ca?vDRoL\ZqDSSc+dQM ]۷uT]3hrzcCL#dB&arp^twFW$Mc1<"wG,UU;O CQ;0j5*|si"7LKeTSTΊWPW@qq )k<-Rm#}}L' Xʢm/A,ѹ`MLaa` 6ˋ9jyU3^];Xn=}Rٿ9.D% "ZFu_R6w`|7h ٌ`'.OׅYfvZgȘ͉#;I&D )Jr>mD77݆,z=ffr[n"aϐ쿣 'aɬY3mp'%‘L$B/G$+tQ*.N00 EQPEnc.l̙̚93QL$ DϤK$ rw L$pr9.h~HϊK&!wy$="uq;#F \r%nB\k]LdBtˑݝAr};ri" Mіd%雇TWF1lԘT7.h><͜9Y3gD<%Β{ܓA΄ ,’^g>~Zi[xLl9B~λ]M"xz<iZ2Z "Q$x؈eСde`6 T_q\r3!z!]L"4M̬w0ɄdddP\RJQQ1kVfWUy}j[-H!2w#tx8Sxeׇ=lj'Dt]UXN [>@0Q#ɰUU)i<-RmɄ} !]&DE^ź{ 1Ar}k2N:D>e[l {wvS][È#z<}>~>χaȑTvB$ddB$w}q/~W_}͐!pobΝ|ʩLzWiٳYf-<[m?z{k׮K.ӆ`0Ȃ{_~q {S__πA\lyR\NmM JKK0`0֌T7GL7dB:LBڞ>]qjo't"'̲eڲ[8SyW;{v#;LƌxAЃ<+|;ou^hll䦛oC㏏FGuICkrM ך&7mƄa#'C]O)L7z&rZH&G2!-I&L'9d #655z.|ɓ&h"t"Zƽ nyĉ,Ϲwl6==vn߾?p#|Gyd^Qh` 6ҚɄd¢8622HN:4 Ž;2,խ9$$+!DʴoU&wehsNΘ6|RDXn?$6m+kllbc5z)((2tO`0p@~~ϹƛΎFN5qb5}%|oE!#M9ITVVb6}ҁ yF#e*/3n. НI&t\ޕL'sޕP50k[L4I&l2Fu-ӟijj'#K._^+^x9 I&b +sE/QeD\f lܸW^^#+Ǭv%77 [!z!]e6;%mVv͛oЂATA),'Ov{, Œ;3TYg~Ʉ}I&N .G2CtwJrwv%EdgTE4TUnr:-7WgHSOdƌiyI27L!zDiM0UUq\8 0 EAQ貙ӧ3s! I2Ae8HE'b2s&P޷NuL%dB' z^2y'ZBpES݄q!@t`H'wJbwm++㳊 q!c6  -GUU6Hٜm_CB!R*7/M?~~$7//)a(L#kB<`le^4?QHϋK&!IN }QWddX1Ldbxض q]ѽH7 ѱ۝Τ+Dw2LVhv / /vX6c4fLLBėdB|#Doew!Wm=d=WQ_kIi~ L&2,222Z(zd Ͽ"ӧu|-TYgBh_H&DL2L!zM}qJ]ilj0xQ-@Zk;.S(D/ "\HCQU]MfvJi@(STXĺkUUE^>rMtFL=dыL-]ewΘsff&ÇcƴiTTTttfUN ?ta#FTUW'g?Tx(TѶ'7[LLM$!zU()bȭLeElذ!UDMMmC:}^AMM-Mnw.DRH&H&!^FQBOxa" jjjXp}A7Wt6Z{eݺur0h ֮]ǣ?֭[),(saРA\XpםX,}> n\č: 8ߏ#'/w^&}[ӈ%#Ғb**8 LH? BQxpgr.GrW>OPѯWJ2>?'|ʊ~\yůb|o[x895|zƍ壿}Tk~:LWbo:M $zy4U ך&7o( oHD$W+//3Y2!4Ʉ$;%D|Kymtn}~ڵ,lV'pֲvlV|0|pkwѺ̉&p}'p&>HEdRn_+<^uDi`RLXT&Gr҄iX,vŠmX-rɄ$ғdBe!zpM95Hݻ|dgg>v9[-KkYcc#N:֭_ϓO=͛|uPQQ'?9`|kG&/?/١:^cVx, +650 BqI)lϓvuSYRGBu&hɄ$zɄ} B{hd||NN ݾ?F ʒK﯋5FOUUf۷QZVN  m75guQ֯?U;SMk/VE/qP^VFYi)`7|,nFIq1_?nV O~}fRVޗ?rzEQXlںCHeefsR,Z0롦j'(d¾$җd"ZtpYO;VÇ?^OqQQ|gs'?? Z6g,Zp7b.ֲ3Ϝ]x_q%.| O:?p#ICd2rr9Yr%apX|igIiH 9H&H& ]W腰deeqWuXuò>z(Oss1|xLW&EE||ɧ蚎QU3&DU+p7y4h̤STLLB>(:54M믿W23*yA6mȫˆ#l6xź_''~(+i^(IwVɄH&b i BLbuu3fΜٌ92%mp8씕`رsX IDAT7mo! b6qOaA>}rsOtW^Oi:K&K2wLrZGhu}i9mmeESSMPU͆njڽ"6;6uZeӦzqɞxɄ$zɄ!R#z U`u*ӧN ?H "$ٚᄓ rw L$&G܅K/&!҈d"Zv;=y瞛&ą,D|H&$f9B^mqΫ, B+P[W?bjv'}+˿^?';++>MyhU&ց܅cIe^4 u0A )koƍ#'77 B5552xw-7`nl}Zw_DוH&ўdB = 2ekHwS&1bDrwd !pZ_\/.zòiS`3..K&L2!娊€Ȍi 3+՗뒲QeHPؽ7/|zqK ۘ !C2nǕfWf&v#)u@¢W4 K}}=542n;;LBQe .z]ilj6ujkoqmnu!#teʤ$b^2A="c'wpB"Q]SCVV6 Ҽ:&" Yn-UEUT7[ By]u\ өYs[V+ ̬_Y} 7rohک3fgT àN;tp֮jDpIu!gmL݅dBWz&.!ҍ8Cnsz7gߟ>sϙץeoٺ-ie-n7յuL7v#Fn!B2A!Dbv~@Duqߴy3:V+N#GLhoڌYf#O>g{j8>|ٵkSgSOai[ǟxm۶QPbK8̹<3rQ l#[n#HH}X'MB'L5UTPk6=lV~9 B2kKw!Hd#38.<|a|/uʊ~u8o<Ƶ\EJV})O=[no3Sd ϵO2glF+#q=w[baFYp_^ ewm زe+6 ˕:Dd4M RXP/@`#k&\Yl#x$;x,/R]iˑLLH %#Z&K=BeovlM=UbJV})O<$np衇0c423oru: <4i|vƍλ"PULv\̔b~]-`(> fsV7OX6m @yyz-jH2A2!]H&!իvޫSP8|۷o/;~~y{/7WWߒ':Æu8/ϰy|>߾ms+ 0fXÌ@Vv#FbdX, &&"|taS@ ?fV|>lv$L忾E?Ǵ; J}[~FM1Eχ?rtq:/. .$"L\ y }e^ǃݳW;ݵΜ3qcǢ(0w9tl 8[0d#JmUTBM.`U5O2{V* ]^܅#qwm߆HRW?FF'rhhh%K6th4ߟ3z(^[k{N88~rA;٧OmN~~fRJKKlYYYl۶DiFS9p?6? G֬^MEe%`WkFk֬f[C\H&H& ɄIW%!Q$t59!@ۮ2 rхY̳\qU465p88hX.ifNC{ygfa0}<ēspZ+q=<9g6F?EQUW4ڪ]Ԧ-w}h:~ =OSSs(`ժUdf?rH&H&ɄP_0"j gȣ tMG![~^ҐM׿%?ptF`a/>L|mws\. O9y}躆 ͨ KU|̀q\}F5Mm]Z$Dw!c]jcߑ'OnEӂl;քm7 T@w#I=܅H-UU'i&^{uF^|,f]|M~}GUոTެBɄاg&P[S͗koesŽ/D*F}m-ښrss}|s<ZQ D)-.fk.6o·|:|`#^vuawy}tr(Ғ ;L\2nkUΝDF!RI dX3"!=weǽyDXc]ԲmgISS^MPU͊n=v4#'L9ÒgO`/ŝNIt{/tY$[iH&t޾ekF֌w~H;ќ};)C^KUU\N'0Z/~T%ah~z8 !G2Ad]z 'B=!Djs&!D.h~ H# BT!DΙ7/MB!D" .]xB .CM2A!: ѴT.Rjԓz.MmiOWh7Fje#-z&t٥gޙg%':[%h%@TdX[YedC1P %6m߇lNH=YoBwe+Du>r'`0bR<& I@5ֱ.':vܻHQf+B}YIoˋyv>Zm\e!I&_NcS#F-dt&Moe%]ܜNU__O۳Of#7㻾FZ 0 (oϭ{'7TfKz"fZYU#zz0FVgvN6B;ei٩KS\]۶mcAD ix=~شqnwee1~dKaּU Z-4@xbi7xPZWBd8؟w=^B˾z4MR_@mm x^4MKuD7ّpe"$DtL ׇFC7۵3HU` @0D  x^)+Msh ذq`2JR\Z()*$`~oaVOqI9ťeQ^ޗ X‹oͥ,}s` ߧ7qHup{< z;>P Sb@-zt]xPM 5 nj x׽x<~?}#y0 7FS=xFZނ?POg>m ?`}f֭^#b'8q{˨2Fst::d?:s.a=c\50 à !C~kjG7B 3ī I&^ ;!9#''ge/ [MC.ai^ 4נz녛frr0*>+c&s/ڰ0 WD躆i^D#07un u\>C1!hnh{^g,8whޑ7Z5| @0z:K{C{M|׼;1}:v:KcߧTW)Qeimlldp_PSWyCO( >vӑF B !H&^* 6p7`Z/'ObIUWpǝwestrWcN=DOʙs.MͿc(-]eٵk'k֮rNŤx?]͋5rq~ ccpG75yɰɰ-4 wno S=m~Mqn/,`_2.=nzz8]T&ח,a[ۗ+.ů /.y?t?4g`XG}w #=տʊ >3^ 7뎎465QI@ÏaRMa 'G@ [QΕn ?o:K&&sH&tEgQZˌ;\tx9+W=a?P̞Iff&WwaN+nP={I3Q\ZҿA^nЇ,Ř(6sXznߴWCKn|,C0VnY%jS=ޛif3CaC1h,_#aL8N]xqͧE}[r 6;jnƃ[vv6dge1CءQ`0E NP0T̪n###,"94Mb*dB iI2!0}'Lτ ضm;rmO欳mb[fk׷;4MڍĄ[@}(t Ll&WVFTf4V* U9bD-3qb'U-k :2"m= u<W7W=DgԺ^h>̊o֓5߀H7x) Ƿ+w{<\x%wv 2t_,|Ylيk7o~}^{r֙s6thTuGJo2Ѹf!bafv "> bPXTBVN.9Hgf̞QPe&dBzLH$##͊ 8cx[ុonYjG;o&rHҼn2@evk*`(A=SnQ}Yeoc /./c0jHq5w1>-ܵg~1#0hwK|z-GL:pmw護rÐ ._NsЬ1z=cG !]>stnf*+*(--i-[nf3O۟CM{=k&c<sο,?s6=+WqsO)puGˤؾ};o( ۶lE0tQ~e})-ǎm["\tPuF۞Φ=ɄI& {+ ]C7t Oaȑyd2].x% 6u+.SNb|rp}}a˖7k-mݻ]akAZvwuo~ t UT6Ӄȧ8 L㑷\ }ƍCuTܜ|>/~@"Gt~?>]ywYz5L?)zv/@>}Z+BJ|͜gaw==Dބѥ2&(ǃ?﮽ݘѣY[x Z+WkgPӕRRRB0wd?=ƍ~PU3NS^^ HQfXٺ}'?9p**c6Kt ضO_D}R&ꌶ=Ok&Sϥd¾$zɄ= {- p\~ŕ466p88蠱\|͜9xsλ<nk5\ɟo ã<ڮ] zt=G PPP:|}tZ2qK̟yt:I&@ _]ociFFF]F#G7t~(zz=x^|>_\{h^zyΘ6 8磌44]g;sZcoHכPbqWIi>n<6%9y2=$ӧC{.9yyy;l9tg[nasW_[oqi^tIOrs?2:Tx`N#A} IDAT]bԪ]? :q񱄇G*QNsYY5Uuk,q'uB%u:AQ=r֭[s!؊J~猡NHI(tv><ԅzak$MlL,F[|4u 4$..ߎBP`ibلdr6˧֋^7hwNAKi4Rt F߅vAlr SdIUULo.uŃ Bd[M&rWtoBM܏g:A"O!"<hYlEQI Dֹ{ݤ'GƸ+Į QԩvP.H D uYuJcg5!^WU BR'u\7I5G6:1̓HR'QI D$T&YN_N(4՞jH D!uYu*#ߤ(v#HH D'uYuJBe%ϧ&B=MܾM!>:wW0B! zB!yBB!BpB!p4!B! !7jݎj͍ !2xxs<2"{ 9olP  MQQQ#@bK%&`0Jߟ" rtvkDH̭r !0™Sc1]$!@v񊎊F: Fb4څB,11(W";%K" Q,$7ճnۉd"M$M!(lKb^w!LI#Bh2*yJqB!pĸ4EB!HnjXNSB!wc]!B"+=rsECX ғrSOz:N>@5xoBBsB!D1+  ! W+2L;?L2}g`?0qʇkTX1*B'I KEU/G!Ck:ÆM_̄VF!xq|pB\q_hw!z2=::%Kϯ111.+B>Bs.(2qbq2eS~-|}}vфB"1SN-4!l׭S8@S~!խ#BMZnN̩BgWi=uwǛ[E`֛RO!9$ !Rsg1 /jXp/Q6(H !"r']z҄(VUZUkfBqܥ'M!BBQe]!B9Az܅B!rrB!n9-B!DA<.!B!pC.S" !BqWIB!dT!B! 䶺*#B!D!;1c/B!]K3JB!w !BQ 128iz.B3w1BdCrpK !Dq3~̨.f]E(TUEUUVk~@@ӡHBT(^=(?$6_y݃a&6l2=ѳ{<,Qa0=)_<.NdML %&`0HIR)hBSPї!DOglظ)MJ3-SA:!իClLT~@'xrĸ繠ޜVM][HXU]S+^:_J ͆^E+kBoMލU˗M۴ gnn_P-22Z fX65 s*U2$HV/AjUqˮ? F~"J}ƽ^TL!zQGQGݷaӗ#u b]}v""1u#S੉(EdT$%JHB^ HT\8obsT)Xo:6zcG-7n'@oǨS]\ݴJUU@45S(h'ܨ*DAT}R{n%X5©xOF❚ /DCSuPb`nKų|)%9: R>Y_u|Vd)ǭRKQHqE.MqZC}=.&z9ʏ7Q nӥ v+.QB Aܰ*p):]NKF13-&&fIvoس&~NdYa#;vb颅ti[SSd2+nEb2yRV- Dժ!a L qqqʐS?\2jS;>[yG:֓je܄ҹ_}rM.XMժ!؆qc=V+C=l2@ci7n`8qի/9m~7w!{>xAA/] ߢj^|,aС}[vC玡i;9o?C;ow}Muz={^:in&ߵ{!:O>ͅݺe˶ԬQGͷ߲u w]|u,[N{spЏ,^~+G}8{v PUsf|J*VƳif֬[ϰ!Xv-5mC]yVVkF*mS<X w& / >%)j5i Q\XV.Cb]t+ծUezJ~~~Ԩ^D ;gV*?N+U e>ԭ[xR>+Us4WQR%Jٳӭϗ}8}bV>[u;&gA'!!r_Yd)xMj  ֭[OGPU*UPBtGII366s3oL~_Xshu? ~FJyYn?_Q#M>?ƍˤISy7[L43>漈I֬[ϥ0ʕ-|ҥ0"##iڤ1xko͙g\nP:?Lƍ:$sգ;[mϴ+Olq{=oAf,QOBܼupiB0n ^^^7q 9GJ8q$+XÕWg`ԫ[6YӦzZ6okָyE.5]3}Fvb4z'Nɓ:w⍷uԩ<1|&'ͩN՚tڱY3mOEQxM0be(&v;\bJnnUbn-9zߒ}ӵsG|ځZa2>s[6gEF{rYԫ˹sgitOC2S?O0w1<ځWxpɓ6YΞNq3 fhJ-q,=$mgأ<=Ic>h3_i4]y4y {Y~4)Squf|)|c+Ed`0жM1UC}qoNG玡|T wmd朹4nNil+F}2,CdpJ%}!ڵk4EQ8ϿL2{nF0UPByƏӗ П7^}#Gt~VZM=S6f+:y"-7p krŝcIrC=#%|}1v)~_ەSCaZm=FhS:(.4MÃ2A(ǭ]4ޛ8^=!wIo7^u/Y&Zx{e ߗ'OĈamxm-5c7$Jϕ*ʒgNEuʟ&K(G'b2ҬEl\>DFZQt5D.i1رZ3yQSzxL݊BAU5WH v)'Ov7qre6[6[mvxyG=[Fu޺:ujg͙ˀ~}i|o#z`]ƍ7z!<%_h|}}35qTf!!ҥ#_mޒj݌NdLx*f)uˍkۚ/:ljhEJWߧNSFuժVu8xqq4;4kz̐rFՆAbb>d_RݸvsXn=aaS}1Hiy|;/Yƫ/C*r( 6L*7_nᇺr<#$3Cg7v]ݷizu\ 7l.{aEi⋫>4-樂;!DaZ!]đYv7+i-[` ş1n;nLMJӫcm :4}{~:F;?[칮u bcVQXwٓXUub/sA8Bpo֯O٠ԮY+UZ:u(Tzuz_Ǚ4f3# ^}MϟG dl~jsWyٓ9s3TZOLwl=O}g#Xr {bb &Me̘'R)ӛy3r*ҥX|y+⮩ RLBB"-jQF'Ob2yj)'N!p;2bF?1"٨aC=Fv8r4lLSoTz;~l߹t?qOs*Uq#Ae f{^, W^L` wWTM#};re1UJ%A)u1Kv }Zp-,.ϵAGgӼ)D$*PNBW+KB\vTT ^ibS%im?KS?FP V+(8?؎cF1ONqcFӮL/i1{6x0Nfێ,3˗/hRp2e>d0Ud|ʳdl.ѸI|7*Lf/tfʕ2lqay ឣSu:=ݞVOE#6JE7;3CO+xT4i !ܒuEnon.T6Nˋ;z[u#8iE|~,^^u 3P!P.L4խY-rhQCR繕bS :F= V^}{44,mkc,S~IQ|->D\]{ߠ!~eiɣ\_g4sinwo!cw-08. h<* c* : .(6}9׻?hp0yyQlY._fߞ]DE9Pe0I$=n!;qN={ݧa :@Q=b&Bl뺯ۣG\#=i)⃧GJ6ő䉏7F)i"r% "9:ҹ}.\BD3?CVܤSL^^x Lf҃*:]}? גk+B#ǤvPܛHr=ii4ص#Gƫbqygs\Wy|,c}Ms'Ϭ*e IDAT7}{Y0gv5t(ԯ[7Mکӧ`9.{zc&rpW H>}B+M);TCܵ'݆C?ҵs'u#+w|)}o?C;owpmlݾٟz={^:iHڽ7aH0733((I2Z>M-B*N)w5išujKˆii3ϿHlٶ*Бx䡮-M/v'Ndk3__խi9q݋|3gR51[f_>^ з͛ݗva.B3QeڶiÑcǜ7$kџN7EQl6m۴hr^6 `0ТY3]6 o-s򯿉qnӵKgL&iHxxD=e (́C8r+W6mc?sMwz#GY ;8ҬST$]!(NTUCW>K%J>u5ߧV*Kfɿbͺ]&!1ѭm~8xFLo)O>|N1A\lIi~~n;}{sP~fMcW2p<3)l;Odɒ ח5;ӋRqR )#!ņcwP ھ=7nO_ӧw/u(\*mݛF4Eq3-۾o3wR'ǜw HiBtL 3d277ndZfw TfM,>>Y'kLs^: aC3tơÇh18c( lǸQ0B!rjP!$$$2bVU*Wv{RJ:n`0Pd ~9~umNefg޽X,\Jt}IL&Tv2e2s_ԩ]eͳn޳va]ԩ]ۙVJe=|{}n\3+:w eŌ~bD{')46"tig|~{D &c !ňY)oE3,m܄ |dBKj}SMef(o˂972)N-[4g߷3mgݙpO?+1Lv1EB!D1㘀IE9s_϶;EHnTB!i@+VZ[7\2# "  k.BQ4_'b7h&!B!DHjKB!RRpĸ !Xs@o//W΀~}(eH(Kno˯LԪY}/Ŭw|}}]&}_R^$Wج؝B! :H(߰pgҋXSX3 Le}| ~j\u?]Y3^~Q 0%lP  MQQQ#@bK%&`01;w9_y /ӄ3alj'O5Wد/ȱc߰2h@jլ>._fܺyÇ<;{l6yЀ}м}Q٬V+<"B;e6̙<۶GnJG\(Z,<ܵ+Ͼ _W]6S98׉EB!DAa4R9$3nǠwrEll,[ZkVb7leæ/4m]bjIڵ9t0>_XJ{f*Vȱc|v-z\~#MCٹ{OWυpYpwGFe3 $$$*f?Ӆ:x`Ŏ]QZH+GDGEQ|yn_'11 dEYdE"$&&r#:*T$:*lQ 9aOfGs-FL tӓNC9oz6 `0ТY3]L豟y&hlΜ}1LyU]Qv;]td2螆GGFeSF6m0=[8'Rp-F-Jq:Ndd&QnrBQY)Uʟ(, BBT Efy;gjŚu LBbꨄg&<Ŗx,YRFL7@FpvX7 h֠<2;/]'L4a<)O]Id;@M4ASsHB!( T S5 {1壏R2ee{`2\nj ҷwo'Ǎw`ؐ qa/ZNt^f311X,"##]-g]gydv^7.שS8@=R^GyE9]AEYdEYR ̢ϖx{t}.njAe f}N, W^ཉQTU%tiTUukUOHl(,Y_'>>m;v׹!*wKz=}l%*:ʙg4yZ.B exMk\aCգ;,c³ߏҿz}i1{6x0`,?+X͂şϰg=;!;wfСxx8\kޝWЯL MU.ͫ/xIHL$04m۴!};UxMQ]6%b҆6!׮\KB!DAP{wo'JpR*ҧNlT!B!Dp|MVr<1pzt__j3=ߛ87^}%o )BS9U&ފ֍*BGkWHbҵvyaaar !y$WBe{BYd%g ?>``u_}-^2;Mޟp &ko ܞ Cv\)B~604_>{ad[^~јFޟH^W !DS[CѨaC"##ٶs6nbyV+ʂE1Lu!_z3q?[uj9a~9ٲm;5k`|lݾ^=gjőĸ",ր%Jd"V\+|T(~"KΗbtQ6n`Ϗj߸qSL2pʓӑ?^yGLl,/V5U^kܼuqO?-[rWo2v38xQcǡ%u(g=Ϗ?8F `Ql%G#( *VtXTL~ Wҩ#Ff5kδ_~c6h[iY99 EEY u2=~~r*vEQaCnFb}In!,p"6:k7ljH03MVݰNGBb".^dEs֮߈6C}4!%|}IHH@UUf͘&kӬiMęgZmNQp?NXasޥг{74ұפ1G0V;'BZ_TRq5\XIjLBl{4y c@>s9F֭ݳg>P!/2* ___8qN0Lthߎ>L4a2hAΞ; @󍳧lch>PΘ&mԈ }9U ұVVgθ\ eVYKwfZp4=<#Bw݈2=ߕ+W; g+!(hJ(7(]tD7ѩ-G7qqSBuj7_bN?||G➢Ȩ(, uw/QQ\gbnd5X6Ft+>%i\9C!0Z5.3\b!:&_gC=Ytt44R @6WݻӌQHd".6o"nѥαf͛Lt&AzJ FAeA@\8ʥwYdEYd,C]:e6#V(nԭ]{S3MQxjժ:*WD t*WL*S*ų{^T\zBTTdVd$\n=lAӤ5jؐ#GϑǸa\8q܅B!QB>96x4_o{ѫGw-\=OHH0F O܋/c OJҹӦW̝v3?|ϭ힛0g|Jl\3O#$8g&?A46o|m)HΝ3>vf<9 :\8Rz/9 iD̥,mh8{B;qT?ȱc<5ItZhFƍșH,;/0 !B%rV|1|fΙK-]&۶jppдIcڲ /RB7|\v&ޛDȥ(M!"F} _{vYڶXj57oݤrʌ6$[et1EKK/b43\711ϖ.gq ,2B!Dq$2BPR&0B!IjKB!r<;",",d),\1Y78n4]a#Ǝs^V;d'?IyƱˎ䶺 !HJ݆Wnl jȢ q%i !ظq&-TT#{'Бx䡮ލ#ǎ~&#"(ȠUp{W^qr5y6g}z:4`@>4ova/3{|nݼňCl6&<<g՚}bZYq1__~N?0IKB!Ck7ljH03MVݰ9 E ٽ?_ɓF2Ly+_]fqn]zx.\ȇ>`ߴənF{/S'OM]B|<^gb58 ~VZz0w'<ԥ>_Vّy6 $$$*fȕ{ \qSEhܢU~!ƏB<$uUqI} L&ڷwp]____G튢(vvd= 3Sg0l L&mhŦ6NUҹsp׮wvݢY3/YV,(( 6m`4zd9;eF*# c M&3JŕUCtL >>>x{Lx-[)Y$fwlr #(s:mX;/֬[OXe?=1yd;d&pEB^ ߸ W#O!({V<==-cbXbɸ!W: aC3tơÇh189/LlL lgݛF4Eqsev3krϺ^yȭW NB+?O7qI"cŹzdԯONTZՙVvmv]c.ԮL+UM?? MDPۈҥQU5?*|vhw?VlPY޻•W)9J,/ǏSN؉^ޭs \qW$=Gv;V+VUUtxxx`@w z߰rvr{[Ԥ}zbUlٶJ+δ^=3o"~yBB5vs&Ocit1V|?#ߟaefLYi7oeFX2\FZoܼu ?BN^*/wˑz 0r0B۷Oɼ>Ϯ~{[׊B]%{ɗbg2lϚ~p?zvӠ^4ʶ;0d{. bcbqZ!!! XY6?ߏӄ]/_7r鬷kv̞ڮ]T&mQǺJ=`?哏q\3OrJ=ws9*W_RtО?M470ڽǹUWSZU^s|e ֬a¸q5kiSڅm۷JtL4&ZQU77v;`4RdяZ\ CQr.N*nf/WvK.t<ԥ3uA? IDAT6T7+eK/g@~ԫ[%|:cVJ+SO￳%Lz?L111L 5mBu Mq;u&1p}>iuR\m`=f&MɬY(ۨgϝA=w{5rU|y<ئ5?%JPBTe?2f3;uW_s}4Fl6Ӯm[mx'$ m(t: z=^fF1v<<<A*!oo2卉g!(~6}9լswnsLq`3)14mڔMi<ܵ+iC(n[]U|t9bzjQn 8ܳn嗼Oqӗ޻sf;p +'O{ҵKg wݲ ?y b ]|hIj7n~XSߦFjfΜ=#?jʜyi٢9֯gaiܷQQQδM^nS5ۻ(㿽].K/BbhI(WUȕRMƩPNa(I.j5WӶ}Qj8`L1kWk[:}Fs[gN` :SO42o//AR:((:X-AOBb"Dlgv=Ivɼ߶36wa_קO`0x_DjVfOHV}"3a0zpM}醍8a!!;q۷3cګ[|i-hܨ˞}HJJ&$.];ww1d`^a9J%Fc_oqS|NuQ q}پcw7;?N{Y(LV1;~vwsNs︗Z\o!cmFGG`ѻL0@iۆ_~=m޷w/_Qc "1Gͻg^F;+]WRb1܉?v-[b(t-ItY& ._8wV9{(*3Gy<}Pv_7͚F2e$̝G1˖l_ޚ=ΕÆ!IS`%K5f >s%:%f啗xBmV۪syi9 o6 V#?3k08~G z=i=m~É2zpqu[(t:7L{khZZ` Ѵlт\.-7+/#?#ʔW2Jf.^Ğ]{hԸިJY$%%oAhH>>>(sn'ƍV n czw320gdwpSח'ѣXs.2>@V &S6 WK0׮_˓[ѻW2GrR͛7o ]߿yE.JP\sjVzR W89V J ΕP|}|U" @KV-OU'gێD6i/X.w7ib$U|QT̜5N;_ʠ߰7orW BdӉ[IxzzȻfϾ(-x, 獷fR[wǭnܼ޽bfʥ.q|mӺWa ?,SS憫V+qBEA ?а{tw5u[ 'ڪ~x8=TYj5'N~pF g߉Ʊ`{dp>ݟ|M[rZ|}|WkۖSO]A}۷;Xݻ~CΔ}вEs7a+< {?ADD[u'ZwTH!J6X,=G:vꕒm+f!˲J Ŵ#4/<-/~ur^ G??Ʊ'xnB+rQ#; Q>p/^+N?3rƾx)ڵuLLeҤaYbLUQN굠Dm# mUԺU k_HHp;P]*,*IOY|2٤1];wqm_.p5ZE PYƎQE(qB"ڪKP0rPV[+S&*w+x+<*.;ѥs ͷ2dggvF "VM`H(b  Ni TinD)*FAA߾ePŴPE( CU 1]Ѽ9#h?;t2h֔޽FyŝQy  ׅ ]W^_Ծj|{# !$$]Κupy7dffl*Ԏώkas/_y<6euT>wH POv㓝ѝ]D3P+zYr E n޼ɞ_';2~(& l\ÇVסbf6E׮Ȼ0qX(7ndkصg/6ّ#_{>{=toWϠ&CenP%)PD[Uv{O>⤤bΟ?Ohh(# Ƙ2h 6l]ˋgŬ7^埩$yL}i2LNaמ=\D7oۧ7lĊ%dlRIpO6̜5d ݺҳS;~BCBzaar*siJmlp=?1cpuՒ%W;238$5kjjbfZ7np3- cVJBRj+Qr|}"Q-پcY45dOP*EΛ˽aǟբ9}}t+Z`d2aXhmm1m[by,f~\!I}fϦ|#XO2sg8~GڴjU}m*cOg:u} Ixyy9׮]s*,33e+V2q=o>IXt8-oUDdV+7oq=) ߀@F6i(F6ϟkIIܼjV]L9j9#'WڪfZa3g~SGjqciެi0??_S  )9pА$I&˸á*"6-tȺJ4z#F b2E3ggѨqRRS4Vvх;d lo͜Ahh(3|/^bۂ $ݺtfמ=f3iii޻˖D6i/b2ط n&I_,~5 DBC {FpݺN^0>јŁ/:/^^$^bדvDzMEO};Z4ov[XT;~1cOвEso&db>ڨ|3_Tp;uʮ{罹d ~7"""x tgIJN" !qw$x2Ξ;G0= ___f ώK\lܲؿ?oӧYa# 2(n͛5+v4XgȈaC_}fNXz؜xHOׇ f?SVS' u Y|||׼y֋r4,ٮzv+سojȨq uh' aC;YDSL>۵ _q!L?۶og^)^^XGٸy3fhl!cOQڦ6UQHEFYd[eZD9}1HDp7jX[m:^sgSIh`zUL4ǎm]E[Uh WW-/L_ⰰP\Zy>ZK&KDXh(!=S{{6Zn]:kCn`x oމi~s&Z-KNcZynjK{{۳bj~bYhZZu!?BVV6ɔ(I( TJ%:W-jڹ)Պ ד2i̴vTˢzΝ|z$Id2w~fZ,PۈJ(-Ɵɦ=bxethߞ&g>"Ëߨ[vŋj.\k."Dqߺ1g7 "COFF?7:/0O?W^{//O@ƍ>vww'-ya*]gΰy6._tsEmQG-7lSکJ%YYF..hZMTg,@GQڦ6UB:t%KKff͘;?K#,,^-6Zf wN^W\;;]R2bPXɴ/ۯZ4oήݻy8|w{^}%>n]:ӺU+l6d<|{M##o[8rQN }_|AJBjт۵gz䧓'ٱSPGR@g['$ īi۾aaT5{l&+3 ^a"a:ʫcܓSR8a>tnQrT/PBCBxgBիGpݺeEjV P*|2̠:<Ѓ]ruy+VbԘaAz䃍Y|~ 4q^x2K9©2ۗAaC5nׯOo_Qc#0w{YC~J^ݍchٲ% k%IBqh> Ks &1}T$8_equn]y{eۇe|k7;v~ʀ~T*L&6ŋVzMQeS*Y}"sgɟO;6nfqp nnQXi ʝQ{)JpuuEed$)˿W==wgGDJ$ڪ7D$BCB |9 o6 V#?3a,N9[3fhhGxkrX,&Bm %wPb ?cNvаݟOV#AD[%BY߷\;vcAJ$m˖ 8A>TPS<3V+f3f͆BK.MܱSѣ8g)m՝hm T-⎻pl62222R{Q)]XH?@+j PK$^1lb PJJj*:Q5dP+&)9/OR}/mtvʊ'BWyU X~4Z] lаSdg|= de!e vl_`!/N|}h&7ߞÈ!C ԲTSBn陸25ʨ1ϱ|*n99sgZٶ# ^oȲLJ :5Ĝmœ9;fs9l6nVCJZE’yvx °Q;2/n`B_fc d0Qܼq1bؐbNHHp~d>۵˗[ ϫdpN'N$%'π~4nƘ2h 6l]%n] ~ZiX,MD\l?|Jl>f>d',۷m0f̠a#x[Wzvj:~20zY[޸5?θ1t Ǘ?V1ЫMwWmo (8V t^O{ '%w@?<3=wzaa5___&M~ a%w1a¥lFzF9Z6 o=r"K ҥKLu*;:n^p=NQclIII984n\si>ظD|}8?͛5-bٱߏM[,?{۷+~Ժ-|mBBIV+ƻOHVdfxѢ:-Q-[иQ#=LHp]vc8} 73q8BCB8v[ogƴWQ*EΛ˾/O8~,lwݤ =71Y(JLYY\oz?bM̝'>Ňlδ/; ˳FpioČi:L0E?m\cF)rW}m*cOԺ{^Qqp 3xb5\x}: dKnE&`FDD0i|k7[>cF5]Lt"W$jq&cߜaZɶǜmfsj/+ JBBÜʯ4Q`y۷oǏ/6 ɡs`<ꅅr47o孙3 =oDȑYn=.x@Jȅ h37n`0+S0{h5Z:uӦ:8zt2OXdggR*0HP(]Tt:'%\ǽc}%x.] @ݺAӦ m[!8W<#?@|16lڌ,˴mӚgz=s`رxsO];VZ}[\ ݺhKK|>Ժ-|m$E|V㶪^o(/4nBb",Y_~l6tra4=Ǯ{,<==ۇF ﲇ9 z˯GsJB vndžNQ223qss+4$i:1/LKKw9ɚ(瓸c<==IIN߯\KOOG9qIKK/t+7w7..5j\ ٦,Z V'~jV*deѨ5 ˋ)).SٲbW Kpp h_x%!Ikǽrh'^ʷKx)nyxyz2p@_> [m+Ŏ;nmQPB&Jguh2vqd&!*\tCxyyyk0<~ Ǡ8e5,=<==@fF:WWVO^DlĨ1cK/MG΍7*S``hR[MW CQx1ɹçT*q z2c~[߼Ihx}* PԜ΍,@HHBHp΃=ő$+ +tQ:?ڑ{st1| Yl9/ZXhڋ.%o_OCF#PPv* F0ݴ=wߏT eܸy4`X %S_N\Xٷ9k6:v$&: ;y$ICЩ#k4 C`` IzퟣtϧN)Iݺt懣|4)&9#9շWV*Ϊho7m[ƘšCpYƏ͹CM|^J=M///ARɾ/㏱k>"#﮶#e,V W Chx\\n7W]YV:tP,û$VxڶϏ+WuE,MYsn_ٽ{6st6 tLV1amE?uX'/`IHL$ r9V\SiР>|筪s[U珳g긷kۖSO]}m?$nc嚵08,bu8%`/}ˤ&' "-AXXHöz\u:Z6oΐ4k֭`@lFdID3cf&˗,O^,^gǎAD}FY l ׮*(. dcj13ЋDIt;lbLO2.gxQʣE9t([>^y t:1ь1ܩs=`&X/owӿo_f͙ՕC I+nu뾔su[J~ W綪iͱ'h:gV_/ƕoO?aC 64n~BӓeV1хΜR/,%3MMx -׎ fά7 ݍ_hyk:_3!J~ǏHvm Tpimެ͛5+2C edԗ M'=1~ ϳ8~ѭb6bfPPJ  G4^mV+/3aGHM?6 WU˖_7utImD%n/\pΤ˨T*&=?_d$&&"CdtILZp׮*6NsO-TR/.4joo/TJ%F#IIoxyxDJ'/AP0rPV[+S&X=u+ I{ΝҹSEVf 6VOx :Fz+pXVTJ%:WꅆプyOUX呗 UGE_BzBbGrj+Z͌ڦ|:Gpde3htnhe뻨:(L}V=ٱs';>)|A=lRW*/V P3ƽ5RMU,ېJLXzTĶ=zO9]y+8g(KU $5bPrw O yemD[%BY^٥R;zDeAX,4] *G\GAdb+f;ٱyܻ3/|3lPK]vbbEnp]4$dgggsE̦,T*@eM8}Y.?X_h؎O?uU^AXT~Wn~]z |'PVVLٌfCPvqťl+ ';?-r&Ga TQ] ϐFpX7R1gel7R &=-")B7l6i$$W'#iCr=9tl6[eUAYeʖ`wʍL+m~&/naw*8nBiYVRRS !ARC@?gRRSuD5}_Vd^Ζ"+Bg0,c4elCK~wMh&E<7>(q%5{\^2STqv:/GJQtRݭussQÆϯQdY&9Fb6a&:ɩ7Vrb T-9cܥ2^e{xrK|+ gJ8n]ˈKMKY +vࠂh+1=#_`يLej233IQ-1HM曯UMR B%s<3H"B%~N7[,<7~cm IF}~"˗,͍L $VoQ>ǏCa6yn:wgvBRRӫg;/UIE*p6oʄqcd?55#2:sg7;;^y[z\׭Zɩgذy30  --Vq/0|ꇇߖba5+gF.ٽwAWHGQ$7&(oFZZ:c2gcZW҆/w771PTEK _P;[rɔFqF7ޙ~:ɖmw s4oڔs-YJHpϏ>g;aFpݺU{AuP'0_~^'1*hޜ{ҳS;?ߜi6<f{3l.IIDAT0 N~@GT1t VZ)CG9gINh_HNRU'ObqXw$IE&1mKXX=TbPf$^R/L@Ŏ=q;i6[N@O( )$P3QH RRRpQkvv+պt= (PJDhK0~{3n<>>> PdZ&gd,}wQ-Uq^\Z_c-t܉ ʔES3y\]u 4Ӧf2<4KWb 0lРBG0ywbBjK3(062bz()6nUaȠΟ# nnt'** cfF$It:8b\p T{5d:H͆bI5TϼV V+Ӫ-ׯ&fx,u F*;+z/9tW1U\.h1צrgZqww#U+&ذY,(T*Fcd3_uŌ2B;zeú5%S\y}9q(AuĹ+ Տu[6TFPbZ^q+ < z}/f[흳B\']DJiPUYm66m³F>UJ¥K./oTJ%F''/e0"#V9rOeb T2qW ˶b@k\]ȕ ӋM[uѸV"@BAU 9^3zҤq#Q?FC?II\x?ՊJU%,.>>xzzhjݟ|ȼݪx6UPJP135'B0h.xctbȅVjxjRTv;PvZ-j?? zdLجVJ%Z7ZF.wPܐa'ʜf B)Pw!6Vɻkq7&B@sEEِ{]auDm*?*Jܕb   T%7op61oW+FsAAʕi4`4fr4>7OaEEZyAAJb4frI= }ȑeŗRAA<V[*Z{LIENDB`muse-4.0.0/src/find_manual_plural.sh000077500000000000000000000013211404105311000174300ustar00rootroot00000000000000#!/bin/bash # this script tries to find all "bad" code like # (number > 1 ? tr ("tracks") : tr("track")) # that is, all manual plural diversifications # you should replace them by: # tr("processed %n track(s)", "", number) # the "" is a translator comment. you may write what you want # # you have to create appropriate translations for this (even for # english!). linguist will ask you for the singular, plural, # and in some language even paucal form then. # # this script is not perfect. it misses some "bad" things, and # finds some "good" things. { find . -iname '*.cpp' -print0 | xargs -0 grep -E '[^:]: *tr *\("[^"]*".*\)' find . -iname '*.cpp' -print0 | xargs -0 grep -E '\? *tr *\("[^"]*".*\)' } | sort | uniq muse-4.0.0/src/find_translation_concatenation.sh000077500000000000000000000007231404105311000220440ustar00rootroot00000000000000#!/bin/bash # this script tries to find all "bad" code like # tr("the file ") + your_file + tr(" could not be loaded!") # you should replace it by: # tr("the file %1 could not be loaded!").arg(your_file) # # this script is not perfect. it misses some "bad" things, and # finds some "good" things. { find . -iname '*.cpp' -print0 | xargs -0 grep -E 'tr *\("[^"]*" *\) *\+'; find . -iname '*.cpp' -print0 | xargs -0 grep -E '\+ *tr *\("[^"]*" *\)'; } | sort | uniq muse-4.0.0/src/fix_make_paths.pl000066400000000000000000000032341404105311000165610ustar00rootroot00000000000000#!/usr/bin/perl -- use strict; my @files = split(/\n/, `find . -name build.make -print`); foreach my $file(@files) { open(my $fh, "< :encoding(UTF-8)", $file) || die "ERROR: Cannot open $file for reading: $!"; my $content = do { local $/; <$fh> }; close($fh) || die "ERROR: Cannot close $file opened for reading: $!"; $content =~ s/\/d //g; $content =~ s/\\/\//g; $content =~ s/\/([\r\n])/\\$1/g; open(my $fh, "> :encoding(UTF-8)", $file) || die "ERROR: Cannot open $file for writing: $!"; print $fh $content; close($fh) || die "ERROR: Cannot close $file opened for writing: $!"; } my @files = split(/\n/, `find . -name link.txt -print`); foreach my $file(@files) { open(my $fh, "< :encoding(UTF-8)", $file) || die "ERROR: Cannot open $file for reading: $!"; my $content = do { local $/; <$fh> }; close($fh) || die "ERROR: Cannot close $file opened for reading: $!"; $content =~ s/\\/\//g; $content =~ s/\/([\r\n])/\\$1/g; open(my $fh, "> :encoding(UTF-8)", $file) || die "ERROR: Cannot open $file for writing: $!"; print $fh $content; close($fh) || die "ERROR: Cannot close $file opened for writing: $!"; } my @files = split(/\n/, `find . -name Makefile -print`); foreach my $file(@files) { open(my $fh, "< :encoding(UTF-8)", $file) || die "ERROR: Cannot open $file for reading: $!"; my $content = do { local $/; <$fh> }; close($fh) || die "ERROR: Cannot close $file opened for reading: $!"; $content =~ s/\\/\//g; $content =~ s/\/([\r\n])/\\$1/g; open(my $fh, "> :encoding(UTF-8)", $file) || die "ERROR: Cannot open $file for writing: $!"; print $fh $content; close($fh) || die "ERROR: Cannot close $file opened for writing: $!"; } muse-4.0.0/src/grepmidi/000077500000000000000000000000001404105311000150405ustar00rootroot00000000000000muse-4.0.0/src/grepmidi/CMakeLists.txt000066400000000000000000000023701404105311000176020ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB grepmidi_source_files grepmidi.cpp ) ## ## Define target ## add_executable ( grepmidi ${grepmidi_source_files} ) ## ## Install location ## install(TARGETS grepmidi DESTINATION ${CMAKE_INSTALL_PREFIX}/bin ) muse-4.0.0/src/grepmidi/grepmidi.cpp000066400000000000000000000273211404105311000173510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: grepmidi.cpp,v 1.1.1.1.2.1 2009/03/09 02:05:17 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #define XCHG_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) #ifdef __i486__ #define XCHG_LONG(x) \ ({ int __value; \ asm ("bswap %1; movl %1,%0" : "=g" (__value) : "r" (x)); \ __value; }) #else #define XCHG_LONG(x) ((((x)&0xFF)<<24) | \ (((x)&0xFF00)<<8) | \ (((x)&0xFF0000)>>8) | \ (((x)>>24)&0xFF)) #endif #if __BYTE_ORDER == __LITTLE_ENDIAN #define BE_SHORT(x) XCHG_SHORT(x) #define BE_LONG(x) XCHG_LONG(x) #else #define BE_SHORT(x) x #define BE_LONG(x) x #endif int cpos; bool printName = false; const char* curName; //--------------------------------------------------------- // readLong //--------------------------------------------------------- int readLong(FILE* f) { int format; if (fread(&format, 4, 1, f) != 1) { printf("read long failed\n"); exit(1); } cpos += 4; return BE_LONG(format); } //--------------------------------------------------------- // readShort //--------------------------------------------------------- int readShort(FILE* f) { short format; if (fread(&format, 2, 1, f) != 1) { printf("read short failed\n"); exit(1); } cpos += 2; return BE_SHORT(format); } /*--------------------------------------------------------- * getvl * Read variable-length number (7 bits per byte, MSB first) *---------------------------------------------------------*/ int getvl(FILE* f) { int l = 0; for (int i = 0;i < 8; i++) { int c = getc(f); ++cpos; if (c == EOF) break; c &= 0xff; l += (c & 0x7f); if (!(c & 0x80)) return l; l <<= 7; } printf("Variable Len too long\n"); return -1; } //--------------------------------------------------------- // skip //--------------------------------------------------------- void skip(FILE* f, int n) { while (n--) { ++cpos; if (getc(f) == EOF) { printf("skip %d failed\n", n); exit(1); } } } //--------------------------------------------------------- // grepTrack //--------------------------------------------------------- int grepTrack(FILE* f, int trackno) { // printf("TRACK %d\n", trackno); int mtype, mlen; char* buffer; char tmp[4]; fread(tmp, 4, 1, f); if (memcmp(tmp, "MTrk", 4)) return -5; int len = readLong(f); int endpos = cpos + len; int runstate = -1; for (;;) { /* int nclick = */ getvl(f); int me = getc(f); ++cpos; int a = -1; if ((me & 0x80) == 0) { if (runstate == -1) return -6; a = me; me = runstate; } switch (me & 0xf0) { case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xe0: getc(f); ++cpos; // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case 0xc0: case 0xd0: if (a == -1) { a = getc(f); ++cpos; } runstate = me; break; case 0xf0: switch(me & 0xf) { case 0: // SYSEX mlen = getvl(f); skip(f, mlen); break; case 1: case 2: case 3: case 4: case 5: case 6: if (printName) printf("%s: ", curName); printf("unknown Message\n"); break; case 7: // EOX case 8: // timing clock case 9: // undefined case 0xa: // start case 0xb: // continue case 0xc: // stop case 0xd: // undefined case 0xe: // active sensing if (printName) printf("%s: ", curName); printf("RT Message??\n"); break; case 0xf: // META mtype = getc(f); ++cpos; mlen = getvl(f); buffer = new char[mlen+1]; if (mlen) { if (fread(buffer, mlen, 1, f) != 1) { if (printName) printf("%s: ", curName); printf("---meta %d too short (%d)\n", mtype, mlen); exit(1); } cpos += mlen; } switch(mtype) { case 0x2f: delete buffer; goto end; case 1: case 2: case 3: case 4: case 5: case 6: // Marker case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: buffer[mlen] = 0; if (printName) printf("%s: ", curName); printf("%02d Meta %0d: <%s>\n", trackno, mtype, buffer); default: break; } delete buffer; break; } break; } } end: if (cpos != endpos) { if (printName) printf("%s: ", curName); printf(" %d zu kurz\n", cpos - endpos); } if (endpos-cpos > 0) skip(f, endpos-cpos); return 0; } //--------------------------------------------------------- // grep //--------------------------------------------------------- int grep(FILE* f) { char tmp[4]; fread(tmp, 4, 1, f); if (memcmp(tmp, "MThd", 4) != 0) return -2; int len = readLong(f); if (len < 6) return -3; cpos += 8; int format = readShort(f); int ntracks = readShort(f); readShort(f); // division if (len > 6) skip(f, len-6); /* skip the excess */ int rv = 0; switch (format) { case 0: rv = grepTrack(f, 0); break; case 1: for (int i = 0; i < ntracks; i++) { rv = grepTrack(f, i); if (rv != 0) return rv; } break; default: return -4; } return 0; } //--------------------------------------------------------- // grep //--------------------------------------------------------- int grepMidi(const char* name) { curName = name; const char*p = strrchr(name, '.'); FILE* f; if (p && strcmp(p, ".gz") == 0) { char buffer[512]; snprintf(buffer, 512, "gunzip < %s", name); f = popen(buffer, "r"); } else { p = 0; f = fopen(name, "r"); } if (f == 0) return -1; cpos = 0; int rc = grep(f); if (p) pclose(f); else fclose(f); return rc; } //--------------------------------------------------------- // usage //--------------------------------------------------------- void usage(const char* fname, const char* txt) { fprintf(stderr, "%s: %s\n", fname, txt); // fprintf(stderr, "usage:\n"); } //--------------------------------------------------------- // main //--------------------------------------------------------- int main(int argc, char* argv[]) { int c; while ((c = getopt(argc, argv, "f")) != EOF) { switch (c) { case 'f': printName = true; break; default: usage(argv[0], "bad argument"); return -1; } } argc -= optind; ++argc; const char* p = 0; for (int i = 1; i < argc; ++i) { switch (grepMidi(argv[i])) { case 0: break; case -1: p = "not found"; break; case -2: p = "no 'MThd': not a midi file"; break; case -3: p = "file too short"; break; case -4: p = "bad file type"; break; case -5: p = "no 'MTrk': not a midi file"; break; case -6: p = "no running state"; break; default: printf("was??\n"); return -1; } } if (p) printf("Error: <%s>\n", p); return 0; } muse-4.0.0/src/libdivide_LICENSE000066400000000000000000000047131404105311000162650ustar00rootroot00000000000000 libdivide Copyright (C) 2010 ridiculous_fish libdivide is made available under two licenses. You may choose either of the following licenses when using libdivide. zlib License ------------ This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Boost License ------------- Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. libdivide@ridiculousfish.com muse-4.0.0/src/libs/000077500000000000000000000000001404105311000141715ustar00rootroot00000000000000muse-4.0.0/src/libs/CMakeLists.txt000066400000000000000000000024331404105311000167330ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # CMakeLists.txt # (C) Copyright 2014 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ADD_SUBDIRECTORY(evdata) ADD_SUBDIRECTORY(memory) ADD_SUBDIRECTORY(midi_controller) ADD_SUBDIRECTORY(midnam) ADD_SUBDIRECTORY(mpevent) ADD_SUBDIRECTORY(string) ADD_SUBDIRECTORY(sysex_helper) ADD_SUBDIRECTORY(xml) ADD_SUBDIRECTORY(time_stretch) ADD_SUBDIRECTORY(wave) ADD_SUBDIRECTORY(plugin) muse-4.0.0/src/libs/evdata/000077500000000000000000000000001404105311000154355ustar00rootroot00000000000000muse-4.0.0/src/libs/evdata/CMakeLists.txt000066400000000000000000000033561404105311000202040ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # evdata/CMakeLists.txt # (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB evdata_source_files evdata.cpp ) ## ## Define target ## add_library ( evdata_module SHARED ${evdata_source_files} ) ## ## Compilation flags and target name ## set_target_properties( evdata_module PROPERTIES OUTPUT_NAME muse_evdata_module ) ## ## Linkage ## target_link_libraries(evdata_module memory_module sysex_helper_module ) ## ## Install location ## install(TARGETS evdata_module DESTINATION ${MusE_MODULES_DIR} ) # if ( ${MODULES_BUILD} STREQUAL SHARED ) # install(TARGETS # evdata_module # DESTINATION ${MusE_MODULES_DIR} # ) # endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/libs/evdata/evdata.cpp000066400000000000000000000252131404105311000174100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // sysex_processor.cpp // (C) Copyright 2017 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "evdata.h" #include "midi_consts.h" #include "sysex_helper.h" #include #include namespace MusECore { //--------------------------------------------------------- // SysExInputProcessor // Special processing of system exclusive chunks. //--------------------------------------------------------- SysExInputProcessor::State SysExInputProcessor::processInput(EvData* dst, const unsigned char* src, size_t len, size_t frame) { if(!src || len == 0) return _state; switch(_state) { case Clear: case Finished: if(*src == ME_SYSEX) { // Mark the starting frame as the given frame. _startFrame = frame; // Is this block of data a single chunk? if((*(src + len - 1) == ME_SYSEX_END)) { // Aside from the start and end bytes, is there any useful data? if(len >= 3) { // It is a single chunk sysex - no need to queue, set the EvData directly. _state = Finished; dst->setData(src + 1, len - 2); } else // It's a useless chunk, no point in storing it. _state = Clear; } else { // Reset or clear the queue (prefer simple reset but memory may grow - clear later). //_q.reset(); _q.clear(); _state = Filling; // It's one chunk of others, queue it. Don't include the start byte - // our EvData is designed to strip the start/end bytes out. if(len >= 2) _q.add(src + 1, len - 1); } } else { _state = Clear; fprintf(stderr, "SysExInputProcessor: State is Clear or Finished:%d but chunk start is not ME_SYSEX\n", _state); } break; case Filling: if(*src == ME_SYSEX) { fprintf(stderr, "SysExInputProcessor: State is Filling but chunk start is ME_SYSEX. Finishing sysex.\n"); // Finish what we got. _state = Finished; dst->setData(this); // Reset or clear the queue (prefer simple reset but memory may grow - clear later). //_q.reset(); _q.clear(); } else if((*(src + len - 1) == ME_SYSEX_END)) { // Finish what we got. Don't include the end byte - // our EvData is designed to strip the start/end bytes out. if(len >= 2) _q.add(src, len - 1); _state = Finished; dst->setData(this); // Reset or clear the queue (prefer simple reset but memory may grow - clear later). //_q.reset(); _q.clear(); } else { // Add what we got and continue in Filling state. _q.add(src, len); } break; } return _state; } //--------------------------------------------------------- // SysExOutputProcessor // Special processing of system exclusive chunks. //--------------------------------------------------------- size_t SysExOutputProcessor::dataSize() const { return _evData.dataLen(); } size_t SysExOutputProcessor::curChunkSize() const { switch(_state) { case Clear: case Finished: fprintf(stderr, "SysExOutputProcessor: curChunkSize called while State is not Sending.\n"); return 0; break; case Sending: { // The remaining number of data bytes (minus any start/end byte). size_t sz = 0; if((int)_curPos < _evData.dataLen() ) sz = _evData.dataLen() - _curPos; // Are we on the first chunk? Leave room for the start byte. if(_curPos == 0) ++sz; // Should there be more chunks? That is, will the data so far - // plus an end byte - not fit into a chunk? if(sz > (_chunkSize - 1)) // Limit the final size. sz = _chunkSize; else // Leave room for the end byte. ++sz; return sz; } break; } return 0; } void SysExOutputProcessor::clear() { // Release any reference to the data. _evData = EvData(); _state = Clear; _curPos = 0; } void SysExOutputProcessor::reset() { _state = Clear; _curPos = 0; } SysExOutputProcessor::State SysExOutputProcessor::setEvData(const EvData& src, size_t frame) { if(!src.constData() || src.dataLen() == 0) return _state; switch(_state) { case Clear: case Finished: // Keep a reference to the data so that it doesn't disappear between calls, // so that we can step through the data block. _evData = src; _curPos = 0; // Mark the starting frame as the given frame. _curChunkFrame = frame; _state = Sending; break; case Sending: fprintf(stderr, "SysExOutputProcessor: processOutput called while State is Sending.\n"); break; } return _state; } bool SysExOutputProcessor::getCurChunk(unsigned char* dst, int sampleRate) { if(!dst) return false; switch(_state) { case Clear: case Finished: fprintf(stderr, "SysExOutputProcessor: getCurChunk called while State is not Sending.\n"); return false; break; case Sending: { unsigned char* p = dst; bool is_chunk = false; // The remaining number of data bytes (minus any start/end byte). size_t sz = 0; if((int)_curPos < _evData.dataLen() ) sz = _evData.dataLen() - _curPos; // Are we on the first chunk? Leave room for the start byte. if(_curPos == 0) ++sz; // Should there be more chunks? That is, will the data so far - // plus an end byte - not fit into a chunk? if(sz > (_chunkSize - 1)) { // Limit the final size. sz = _chunkSize; is_chunk = true; } // Are we on the first chunk? if(_curPos == 0) { // Add the start byte. *p++ = ME_SYSEX; --sz; } // Besides any start byte, is there any actual data to copy? if(sz != 0) { // Copy the data to the destination. memcpy(p, _evData.constData() + _curPos, sz); // Advance the pointer. p += sz; // Advance the current position. _curPos += sz; } // Are there no more chunks to follow? if(!is_chunk) { // Add the end byte. *p = ME_SYSEX_END; // We are finished. _state = Finished; // Release any reference to the data. _evData = EvData(); } // Estimate the number of audio frames it should take (or took) to transmit the current midi chunk. // Advance the current chunk frame so that the driver can schedule the next chunk. // Do it even if the state has Finished, so the driver can wait until the last chunk is done // before calling Clear() or Reset() (setting the state to Clear). _curChunkFrame += sysexDuration(sz, sampleRate); } break; } return true; } size_t SysExOutputProcessor::stageEvData(const EvData& evData, unsigned int frame) { // Cannot do if already sending. if(_state == SysExOutputProcessor::Sending) return 0; // Set the event data, and proceed only if state changed to Sending. if(setEvData(evData, frame) != SysExOutputProcessor::Sending) return 0; // Return the current (first) chunk size. return curChunkSize(); } //--------------------------------------------------------- // EvData // variable len event data (sysex, meta etc.) //--------------------------------------------------------- void EvData::resize(int l) { // Setting the data destroys any reference. Dereference now. // The data may still be shared. Destroy it only if no more references. if ( _refCount && (--(*_refCount ) == 0)) { delete _refCount; _refCount = 0; if( _data ) delete[] _data; } // Clear the data variable. _data = 0; if(l > 0) { _data = new unsigned char[l]; // Setting the data destroys any reference. Create a new reference now. _refCount = new int(1); } _dataLen = l; } void EvData::setData(const unsigned char* p, int l) { // Setting the data destroys any reference. Dereference now. // The data may still be shared. Destroy it only if no more references. if ( _refCount && (--(*_refCount ) == 0)) { delete _refCount; _refCount = 0; if( _data ) delete[] _data; } // Clear the data variable. _data = 0; if(l > 0) { _data = new unsigned char[l]; memcpy( _data, p, l); // Setting the data destroys any reference. Create a new reference now. _refCount = new int(1); } _dataLen = l; } void EvData::setData(const SysExInputProcessor* q) { // Let's not risk unterminated data: Accept a queue with a Finished state only. if(q->state() != SysExInputProcessor::Finished) return; // Setting the data destroys any reference. Dereference now. // The data may still be shared. Destroy it only if no more references. if ( _refCount && (--(*_refCount ) == 0)) { delete _refCount; _refCount = 0; if( _data ) delete[] _data; } // Clear the data variable. _data = 0; const size_t l = q->size(); if(l > 0) { // Create a contiguous memory block to hold the data. _data = new unsigned char[l]; // Copy the non-contiguous chunks of data to the contiguous data. q->copy( _data, l); // Setting the data destroys any reference. Create a new reference now. _refCount = new int(1); } _dataLen = l; } } // namespace MusECore muse-4.0.0/src/libs/evdata/evdata.h000066400000000000000000000142111404105311000170510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: evdata.h,v 1.2.2.2 2008/08/18 00:15:23 terminator356 Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __EVDATA_H__ #define __EVDATA_H__ #include "memory.h" namespace MusECore { class SysExInputProcessor; //--------------------------------------------------------- // EvData // variable len event data (sysex, meta etc.) //--------------------------------------------------------- class EvData { private: int* _refCount; unsigned char* _data; int _dataLen; public: EvData() { _data = 0; _dataLen = 0; _refCount = 0; } EvData(const EvData& ed) { _data = ed._data; _dataLen = ed._dataLen; _refCount = ed._refCount; if( _refCount ) (*_refCount )++; } EvData& operator=(const EvData& ed) { if ( _data == ed._data ) return *this; if ( _refCount && (--(*_refCount ) == 0)) { delete _refCount; if( _data ) delete[] _data; } _data = ed._data; _dataLen = ed._dataLen; _refCount = ed._refCount; if( _refCount ) (*_refCount )++; return *this; } ~EvData() { if ( _refCount && (--(*_refCount ) == 0)) { if( _data ) { delete[] _data; _data = 0; } delete _refCount; _refCount = 0; } } const unsigned char* constData() const { return _data; } unsigned char* data() { return _data; } int dataLen() const { return _dataLen; } // Allocates enough space for n bytes. Does not fill. void resize(int l); // Copies the data. void setData(const unsigned char* p, int l); // Copies the data. void setData(const SysExInputProcessor* q); }; //--------------------------------------------------------- // SysExInputProcessor // Special processing of system exclusive chunks. //--------------------------------------------------------- class SysExInputProcessor { public: enum State { Clear = 0, Filling = 1, Finished = 2 }; private: MemoryQueue _q; State _state; size_t _startFrame; public: SysExInputProcessor() : _state(Clear), _startFrame(0) { } // Returns the state of the queue. State state() const { return _state; } // Returns the frame that the sysex started at. size_t startFrame() const { return _startFrame; } // Returns the size of the queue. size_t size() const { return _q.curSize(); } // Clears the queue. void clear() { _q.clear(); _state = Clear; } // Resets the queue. void reset() { _q.reset(); _state = Clear; } // Process some input. Return the state. Only when state is Finished will dst hold valid data. State processInput(EvData* dst, const unsigned char* src, size_t len, size_t frame); // Copies the queue to a character buffer. // Returns number of bytes copied. size_t copy(unsigned char* dst, size_t len) const { return _q.copy(dst, len); } }; //--------------------------------------------------------- // SysExOutputProcessor // Special processing of system exclusive chunks. //--------------------------------------------------------- class SysExOutputProcessor { public: enum State { Clear = 0, Sending = 1, Finished = 2 }; private: size_t _chunkSize; State _state; size_t _curChunkFrame; EvData _evData; size_t _curPos; public: SysExOutputProcessor() : _chunkSize(256), _state(Clear), _curChunkFrame(0), _curPos(0) { } // Returns the state of the queue. State state() const { return _state; } // Sets the chunk size to be used. Default is 256. // It may be necessary to adjust the size depending on the driver and its limits. void setChunkSize(size_t sz = 256) { _chunkSize = sz; } // Returns the frame that the current chunk should be scheduled at. size_t curChunkFrame() const { return _curChunkFrame; } // Returns the size of the data (minus any start/end bytes). size_t dataSize() const; // Returns the size of the current chunk (including any start/end bytes). size_t curChunkSize() const; // Clears the processor. Releases any reference to data in _evData. Sets state to Clear. void clear(); // Resets the processor. Resets _curPos. Sets state to Clear. void reset(); // Set up the processor with a complete output data block. Return the state. // State will change to Sending. Afterwards, call getCurChunk each cycle until state is Finished. State setEvData(const EvData& src, size_t frame); // Fills destination with the current chunk. The destination must have at least curChunkSize bytes. // Afterwards it moves on to the next chunk until the state changes to Finished. Returns true on success. bool getCurChunk(unsigned char* dst, int sampleRate); // Convenience method: Performs a state check, calls setEvData, and returns curChunkSize(). size_t stageEvData(const EvData& evData, unsigned int frame); }; } // namespace MusECore #endif muse-4.0.0/src/libs/memory/000077500000000000000000000000001404105311000155015ustar00rootroot00000000000000muse-4.0.0/src/libs/memory/CMakeLists.txt000066400000000000000000000033051404105311000202420ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # memory/CMakeLists.txt # (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB memory_source_files memory.cpp ) ## ## Define target ## add_library ( memory_module SHARED ${memory_source_files} ) ## ## Compilation flags and target name ## set_target_properties( memory_module PROPERTIES OUTPUT_NAME muse_memory_module ) ## ## Linkage ## # target_link_libraries(memory_module # ) ## ## Install location ## install(TARGETS memory_module DESTINATION ${MusE_MODULES_DIR} ) # if ( ${MODULES_BUILD} STREQUAL SHARED ) # install(TARGETS # memory_module # DESTINATION ${MusE_MODULES_DIR} # ) # endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/libs/memory/memory.cpp000066400000000000000000000161121404105311000175160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: memory.cpp,v 1.1.1.1.2.2 2009/12/19 23:35:39 spamatica Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "memory.h" #include // NOTE: Keep this code in case we need a dimensioned pool! #if 0 Pool audioRTmemoryPool; Pool midiRTmemoryPool; //--------------------------------------------------------- // Pool //--------------------------------------------------------- Pool::Pool() { for (int idx = 0; idx < dimension; ++idx) { head[idx] = 0; chunks[idx] = 0; grow(idx); // preallocate } } //--------------------------------------------------------- // ~Pool //--------------------------------------------------------- Pool::~Pool() { for (int i = 0; i < dimension; ++i) { Chunk* n = chunks[i]; while (n) { Chunk* p = n; n = n->next; delete p; } } } //--------------------------------------------------------- // grow //--------------------------------------------------------- void Pool::grow(int idx) { int esize = (idx+1) * sizeof(unsigned long); Chunk* n = new Chunk; n->next = chunks[idx]; chunks[idx] = n; const int nelem = Chunk::size / esize; char* start = n->mem; char* last = &start[(nelem-1) * esize]; for (char* p = start; p < last; p += esize) reinterpret_cast(p)->next = reinterpret_cast(p + esize); reinterpret_cast(last)->next = 0; head[idx] = reinterpret_cast(start); } #endif //--------------------------------------------------------- // MemoryQueue //--------------------------------------------------------- MemoryQueue::MemoryQueue() { _endChunk = 0; _curSize = 0; _curOffest = 0; // Preallocate. grow(); // Remember the very first chunk. _startChunk = _endChunk; // Start writing from the first chunk. _curWriteChunk = _startChunk; } //--------------------------------------------------------- // ~MemoryQueue //--------------------------------------------------------- MemoryQueue::~MemoryQueue() { // Delete all chunks. Chunk* n = _startChunk; while(n) { Chunk* p = n; n = n->_next; delete p; } } //--------------------------------------------------------- // clear //--------------------------------------------------------- void MemoryQueue::clear() { // Delete all chunks except the first one, keep it around. That avoids an initial grow. if(_startChunk) { Chunk* n = _startChunk->_next; while(n) { Chunk* p = n; n = n->_next; delete p; } } _endChunk = _startChunk; // Reset the writer. reset(); } //--------------------------------------------------------- // reset //--------------------------------------------------------- void MemoryQueue::reset() { // Start writing from the first chunk. _curWriteChunk = _startChunk; _curSize = 0; _curOffest = 0; } //--------------------------------------------------------- // grow //--------------------------------------------------------- void MemoryQueue::grow() { Chunk* n = new Chunk(); //Chunk* n = new Chunk(_chunkSize); n->_next = 0; if(_endChunk) _endChunk->_next = n; _endChunk = n; } //--------------------------------------------------------- // add // Return true if successful. //--------------------------------------------------------- bool MemoryQueue::add(const unsigned char* src, size_t len) { if(!src || len == 0 || !_curWriteChunk) return false; const unsigned char* pp = src; size_t remain = len; size_t bytes; while(true) { bytes = Chunk::ChunkSize - _curOffest; if(remain < bytes) bytes = remain; memcpy(_curWriteChunk->_mem + _curOffest, pp, bytes); _curSize += bytes; _curOffest += bytes; if(_curOffest == Chunk::ChunkSize) { _curOffest = 0; // Does the next chunk exist? if(_endChunk->_next) { // Advance the current write chunk to the next existing chunk. _curWriteChunk = _endChunk->_next; } else { // Create a new chunk. grow(); // The _endChunk changed. Advance the current write chunk to it. _curWriteChunk = _endChunk; } } remain -= bytes; // No more remaining? Done. if(remain == 0) break; // Advance the source read pointer. pp += bytes; } return true; } //--------------------------------------------------------- // copy // Return true if successful. //--------------------------------------------------------- // Return true if successful. size_t MemoryQueue::copy(unsigned char* dst, size_t len) const { if(!dst || len == 0 || _curSize == 0 || !_startChunk) return 0; // Limit number of requested bytes to actual available size. if(len > _curSize) len = _curSize; unsigned char* pp = dst; size_t remain = len; size_t bytes; // Start reading at the very first chunk. Chunk* read_chunk = _startChunk; while(true) { bytes = Chunk::ChunkSize; if(remain < bytes) bytes = remain; memcpy(pp, read_chunk->_mem, bytes); remain -= bytes; // No more remaining? Done. if(remain == 0) break; // The next chunk must already exist. if(!read_chunk->_next) break; // Advance the read chunk to the next existing chunk. read_chunk = read_chunk->_next; // Advance the destination write pointer. pp += bytes; } return len - remain; } #ifdef TEST //========================================================= // TEST //========================================================= struct mops { char a, c; int b; mops(int x) : b(x) {} }; typedef std::list > List; typedef List::iterator iList; //--------------------------------------------------------- // main // 2.8 s normal 0.7 vector // 2.5 s RTalloc // 1.18 all optimisations (0.97) //--------------------------------------------------------- int main() { List l; for (int i = 0; i < 10000000; ++i) l.push_back(mops(i)); return 0; } #endif muse-4.0.0/src/libs/memory/memory.h000066400000000000000000000155361404105311000171740ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: memory.h,v 1.4.2.3 2009/12/15 22:08:50 spamatica Exp $ // // (C) Copyright 2003-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MEMORY_H__ #define __MEMORY_H__ #include #include // NOTE: Keep this code in case we need a dimensioned pool! #if 0 // most of the following code is based on examples // from Bjarne Stroustrup: "Die C++ Programmiersprache" //--------------------------------------------------------- // Pool //--------------------------------------------------------- class Pool { struct Verweis { Verweis* next; }; struct Chunk { enum { size = 4 * 2048 }; Chunk* next; char mem[size]; }; // Gives about 300 bytes maximum request @ 8 bytes item size. enum { dimension = 40 }; Chunk* chunks[dimension]; Verweis* head[dimension]; Pool(Pool&); void operator=(Pool&); void grow(int idx); public: Pool(); ~Pool(); void* alloc(size_t n); void free(void* b, size_t n); }; //--------------------------------------------------------- // alloc //--------------------------------------------------------- inline void* Pool::alloc(size_t n) { if (n == 0) return 0; int idx = ((n + sizeof(unsigned long) - 1) / sizeof(unsigned long)) - 1; if (idx >= dimension) { printf("panic: alloc %zd %d %d\n", n, idx, dimension); exit(-1); } if (head[idx] == 0) grow(idx); Verweis* p = head[idx]; head[idx] = p->next; return p; } //--------------------------------------------------------- // free //--------------------------------------------------------- inline void Pool::free(void* b, size_t n) { if (b == 0 || n == 0) return; int idx = ((n + sizeof(unsigned long) - 1) / sizeof(unsigned long)) - 1; if (idx >= dimension) { printf("panic: free %zd %d %d\n", n, idx, dimension); exit(-1); } Verweis* p = static_cast(b); p->next = head[idx]; head[idx] = p; } extern Pool audioRTmemoryPool; extern Pool midiRTmemoryPool; #endif //--------------------------------------------------------- // TypedMemoryPool // Most of the following code is based on examples // from Bjarne Stroustrup: "Die C++ Programmiersprache" //--------------------------------------------------------- template class TypedMemoryPool { struct Verweis { Verweis* next; }; struct Chunk { enum { size = itemsPerChunk * sizeof(T) }; Chunk* next; char mem[size]; }; Chunk* chunks; Verweis* head; TypedMemoryPool(TypedMemoryPool&); void operator=(TypedMemoryPool&); void grow() { const int esize = sizeof(T); Chunk* n = new Chunk; n->next = chunks; chunks = n; const int nelem = Chunk::size / esize; char* start = n->mem; char* last = &start[(nelem-1) * esize]; for(char* p = start; p < last; p += esize) reinterpret_cast(p)->next = reinterpret_cast(p + esize); reinterpret_cast(last)->next = 0; head = reinterpret_cast(start); } public: TypedMemoryPool() { head = 0; chunks = 0; grow(); // preallocate } ~TypedMemoryPool() { Chunk* n = chunks; while (n) { Chunk* p = n; n = n->next; delete p; } } void* alloc(size_t items) { if(items == 0) return 0; if(items != 1) { printf("panic: TypedMemoryPool::alloc items requested:%u != 1\n", (unsigned int)items); exit(-1); } if(head == 0) grow(); Verweis* p = head; head = p->next; return p; } void free(void* b, size_t items) { if(b == 0 || items == 0) return; if(items != 1) { printf("panic: TypedMemoryPool::free items requested:%u != 1\n", (unsigned int)items); exit(-1); } Verweis* p = static_cast(b); p->next = head; head = p; } }; //--------------------------------------------------------- // MemoryQueue // An efficient queue which grows by fixed chunk sizes, // for single threads only. //--------------------------------------------------------- class MemoryQueue { struct Chunk { enum { ChunkSize = 8 * 1024 }; Chunk* _next; char _mem[ChunkSize]; }; Chunk* _startChunk; Chunk* _endChunk; Chunk* _curWriteChunk; size_t _curSize; size_t _curOffest; MemoryQueue(MemoryQueue&); void operator=(MemoryQueue&); void grow(); public: MemoryQueue(); ~MemoryQueue(); // Static. Returns whether the given length in bytes needs to be chunked. static bool chunkable(size_t len) { return len > Chunk::ChunkSize; } // Returns current size in bytes. size_t curSize() const { return _curSize; } // Deletes all chunks except the first (to avoid a preallocation), and calls reset. void clear(); // Resets the size and current write position, but does not clear. // Existing chunks will be used, and new ones will be created (allocated) if required. // This saves having to clear (which deletes) before every use, at the expense // of keeping what could be an ever increasing memory block alive. void reset(); // Return true if successful. bool add(const unsigned char* src, size_t len); // Copies the queue to a character buffer. // Returns number of bytes copied. size_t copy(unsigned char* dst, size_t len) const; }; #endif muse-4.0.0/src/libs/midi_controller/000077500000000000000000000000001404105311000173565ustar00rootroot00000000000000muse-4.0.0/src/libs/midi_controller/CMakeLists.txt000066400000000000000000000031321404105311000221150ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # midi_controller/CMakeLists.txt # (C) Copyright 2020 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB midi_controller_source_files midi_controller.cpp ) ## ## Define target ## add_library ( midi_controller_module SHARED ${midi_controller_source_files} ) ## ## Compilation flags and target name ## set_target_properties( midi_controller_module PROPERTIES OUTPUT_NAME muse_midi_controller_module ) target_link_libraries(midi_controller_module xml_module ${QT_LIBRARIES} ) ## ## Install location ## install(TARGETS midi_controller_module DESTINATION ${MusE_MODULES_DIR} ) muse-4.0.0/src/libs/midi_controller/midi_controller.cpp000066400000000000000000001042141404105311000232510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midictrl.cpp,v 1.17.2.10 2009/06/10 00:34:59 terminator356 Exp $ // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "midi_controller.h" namespace MusECore { static const char* ctrlName[] = { "BankSelMSB", "Modulation", "BreathCtrl", "Control 3", "Foot Ctrl", "Porta Time", "DataEntMSB", "MainVolume", "Balance", "Control 9", /*10*/ "Pan", "Expression", "Control 12", "Control 13", "Control 14", "Control 15", "Gen.Purp.1", "Gen.Purp.2", "Gen.Purp.3", "Gen.Purp.4", /*20*/ "Control 20", "Control 21", "Control 22", "Control 23", "Control 24", "Control 25", "Control 26", "Control 27", "Control 28", "Control 29", /*30*/ "Control 30", "Control 31", "BankSelLSB", "Modul. LSB", "BrthCt.LSB", "Control 35", "FootCt.LSB", "Port.T LSB", "DataEntLSB", "MainVolLSB", "BalanceLSB", "Control 41", "Pan LSB", "Expr. LSB", "Control 44", "Control 45", "Control 46", "Control 47", "Gen.P.1LSB", "Gen.P.2LSB", /*50*/ "Gen.P.3LSB", "Gen.P.4LSB", "Control 52", "Control 53", "Control 54", "Control 55", "Control 56", "Control 57", "Control 58", "Control 59", "Control 60", "Control 61", "Control 62", "Control 63", "Sustain", "Porta Ped", "Sostenuto", "Soft Pedal", "Control 68", "Hold 2", "Control 70", "HarmonicCo", "ReleaseTime", "Attack Time", "Brightness", "Control 75", "Control 76", "Control 77", "Control 78", "Control 79", "Gen.Purp.5", "Gen.Purp.6", "Gen.Purp.7", "Gen.Purp.8", "Porta Ctrl", "Control 85", "Control 86", "Control 87", "Control 88", "Control 89", "Control 90", "Effect1Dep", "Effect2Dep", "Effect3Dep", "Effect4Dep", "Phaser Dep", "Data Incr", "Data Decr", "NRPN LSB", "NRPN MSB", /*100*/ "RPN LSB", "RPN MSB", "Control102", "Control103", "Control104", "Control105", "Control106", "Control107", "Control108", "Control109", "Control110", "Control111", "Control112", "Control113", "Control114", "Control115", "Control116", "Control117", "Control118", "Control119", "AllSndOff", "Reset Ctrl", "Local Ctrl", "AllNoteOff", "OmniModOff", "OmniModeOn", "MonoModeOn", "PolyModeOn" }; #if 0 static const char* ctrl14Name[] = { "BankSel", "Modulation", "BreathCtrl", "Control 3", "Foot Ctrl", "Porta Time", "DataEntry", "MainVolume", "Balance", "Control 9", "Pan", "Expression", "Control 12", "Control 13", "Control 14", "Control 15", "Gen.Purp.1", "Gen.Purp.2", "Gen.Purp.3", "Gen.Purp.4", "Control 20", "Control 21", "Control 22", "Control 23", "Control 24", "Control 25", "Control 26", "Control 27", "Control 28", "Control 29", "Control 30", "Control 31", }; #endif //--------------------------------------------------------- // ctrlType2Int // int2ctrlType //--------------------------------------------------------- static struct { MidiController::ControllerType type; QString name; } ctrlTypes[] = { { MidiController::Controller7, QString("Control7") }, { MidiController::Controller14, QString("Control14") }, { MidiController::RPN, QString("RPN") }, { MidiController::NRPN, QString("NRPN") }, { MidiController::RPN14, QString("RPN14") }, { MidiController::NRPN14, QString("NRPN14") }, { MidiController::Pitch, QString("Pitch") }, { MidiController::Program, QString("Program") }, { MidiController::PolyAftertouch, QString("PolyAftertouch") }, { MidiController::Aftertouch, QString("Aftertouch") }, { MidiController::Controller7, QString("Control") }, // alias }; //--------------------------------------------------------- // ctrlType2Int //--------------------------------------------------------- MidiController::ControllerType ctrlType2Int(const QString& s) { int n = sizeof(ctrlTypes)/sizeof(*ctrlTypes); for (int i = 0; i < n; ++i) { if (ctrlTypes[i].name == s) return ctrlTypes[i].type; } return MidiController::ControllerType(0); } //--------------------------------------------------------- // int2ctrlType //--------------------------------------------------------- const QString& int2ctrlType(int n) { static QString dontKnow("?T?"); int size = sizeof(ctrlTypes)/sizeof(*ctrlTypes); for (int i = 0; i < size; ++i) { if (ctrlTypes[i].type == n) return ctrlTypes[i].name; } return dontKnow; } //--------------------------------------------------------- // midiCtrlNumString //--------------------------------------------------------- QString midiCtrlNumString(int ctrl, bool fullyQualified) { int h = (ctrl >> 8) & 0xff; int l = ctrl & 0xff; QString s1 = QString("%1").arg(h); QString s2 = ( l == 0xff ? QString("* ") : QString("%1 ").arg(l) ); MidiController::ControllerType type = midiControllerType(ctrl); switch (type) { case MidiController::Controller7: if(fullyQualified) return s2; else return QString(); case MidiController::Controller14: return s1 + QString("CF") + s2; case MidiController::RPN: return s1 + QString("R") + s2; case MidiController::NRPN: return s1 + QString("N") + s2; case MidiController::Pitch: // Don't show internal controller numbers. case MidiController::Program: case MidiController::Velo: case MidiController::PolyAftertouch: case MidiController::Aftertouch: return QString(); case MidiController::RPN14: return s1 + QString("RF") + s2; case MidiController::NRPN14: return s1 + QString("NF") + s2; } return s1 + QString("?") + s2; } //--------------------------------------------------------- // midiCtrlName //--------------------------------------------------------- QString midiCtrlName(int ctrl, bool fullyQualified) { int h = (ctrl >> 8) & 0xff; int l = ctrl & 0xff; QString s1 = QString("%1").arg(h); QString s2 = ( l == 0xff ? QString("*") : QString("%1").arg(l) ); MidiController::ControllerType type = midiControllerType(ctrl); switch (type) { case MidiController::Controller7: if(fullyQualified) return s2 + QString(" ") + QString(ctrlName[l]); else return QString(ctrlName[l]); case MidiController::Controller14: return s1 + QString("CF") + s2; case MidiController::RPN: return s1 + QString("R") + s2; case MidiController::NRPN: return s1 + QString("N") + s2; case MidiController::Pitch: return QString("Pitch"); case MidiController::Program: return QString("Program"); case MidiController::Velo: return QString("Velocity"); case MidiController::PolyAftertouch: return QString("PolyAftertouch"); case MidiController::Aftertouch: return QString("Aftertouch"); case MidiController::RPN14: return s1 + QString("RF") + s2; case MidiController::NRPN14: return s1 + QString("NF") + s2; } return s1 + QString("?") + s2; } //--------------------------------------------------------- // MidiController //--------------------------------------------------------- MidiController::MidiController() : _name(QString("Velocity")) { _num = CTRL_VELOCITY; // Zero note on vel is not allowed now. _minVal = 1; _maxVal = 127; _initVal = _drumInitVal = 0; _showInTracks = ShowInDrum | ShowInMidi; updateBias(); } MidiController::MidiController(const QString& s, int n, int min, int max, int init, int drumInit, int show_in_track) : _name(s), _num(n), _minVal(min), _maxVal(max), _initVal(init), _showInTracks(show_in_track) { // If drumInit was given, use it otherwise set it to the normal init val. if(drumInit != -1) _drumInitVal = drumInit; else _drumInitVal = _initVal; updateBias(); } MidiController::MidiController(const MidiController& mc) { copy(mc); } //--------------------------------------------------------- // copy //--------------------------------------------------------- void MidiController::copy(const MidiController &mc) { _name = mc._name; _num = mc._num; _minVal = mc._minVal; _maxVal = mc._maxVal; _initVal = mc._initVal; _drumInitVal = mc._drumInitVal; _bias = mc._bias; _showInTracks = mc._showInTracks; } //--------------------------------------------------------- // operator = //--------------------------------------------------------- MidiController& MidiController::operator=(const MidiController &mc) { copy(mc); return *this; } //--------------------------------------------------------- // type //--------------------------------------------------------- MidiController::ControllerType midiControllerType(int num) { if (num < CTRL_14_OFFSET) return MidiController::Controller7; if (num < CTRL_RPN_OFFSET) return MidiController::Controller14; if (num < CTRL_NRPN_OFFSET) return MidiController::RPN; if (num < CTRL_INTERNAL_OFFSET) return MidiController::NRPN; if (num == CTRL_PITCH) return MidiController::Pitch; if (num == CTRL_PROGRAM) return MidiController::Program; if (num == CTRL_VELOCITY) return MidiController::Velo; if ((num | 0xff) == CTRL_POLYAFTER) return MidiController::PolyAftertouch; if (num == CTRL_AFTERTOUCH) return MidiController::Aftertouch; if (num < CTRL_NRPN14_OFFSET) return MidiController::RPN14; if (num < CTRL_NONE_OFFSET) return MidiController::NRPN14; return MidiController::Controller7; } //--------------------------------------------------------- // midiCtrlTerms2Number //--------------------------------------------------------- int midiCtrlTerms2Number(MidiController::ControllerType type, int ctrl) { ctrl &= 0xffff; switch(type) { case MidiController::Controller7: return ctrl & 0xff; case MidiController::Controller14: return CTRL_14_OFFSET + ctrl; case MidiController::RPN: return CTRL_RPN_OFFSET + ctrl; case MidiController::NRPN: return CTRL_NRPN_OFFSET + ctrl; case MidiController::Pitch: return CTRL_PITCH; case MidiController::Program: return CTRL_PROGRAM; case MidiController::Velo: return CTRL_VELOCITY; case MidiController::PolyAftertouch: return CTRL_POLYAFTER; case MidiController::Aftertouch: return CTRL_AFTERTOUCH; case MidiController::RPN14: return CTRL_RPN14_OFFSET + ctrl; case MidiController::NRPN14: return CTRL_NRPN14_OFFSET + ctrl; default: printf("MusE: unknown ctrl type in midiCtrTerms2Number()\n"); return ctrl; } } //--------------------------------------------------------- // isPerNoteMidiController //--------------------------------------------------------- bool isPerNoteMidiController(int ctl) { const int n = ctl & 0xff0000; return ctl == CTRL_POLYAFTER || ((ctl & 0xff) == 0xff && (n == CTRL_RPN_OFFSET || n == CTRL_NRPN_OFFSET || n == CTRL_RPN14_OFFSET || n == CTRL_NRPN14_OFFSET)); } //--------------------------------------------------------- // isPerNoteController //--------------------------------------------------------- bool MidiController::isPerNoteController() const { return isPerNoteMidiController(num()); } //--------------------------------------------------------- // type //--------------------------------------------------------- MidiController::ControllerType MidiController::type() const { return midiControllerType(num()); } //--------------------------------------------------------- // updateBias //--------------------------------------------------------- void MidiController::updateBias() { // If the specified minimum value is negative, we will // translate to a positive-biased range. // Cue: A controller like 'coarse tuning', is meant to be displayed // as -24/+24, but really is centered on 64 and goes from 40 to 88. int b; int mn, mx; ControllerType t = midiControllerType(_num); switch (t) { case RPN: case NRPN: case Controller7: b = 64; mn = 0; mx = 127; break; case Controller14: case RPN14: case NRPN14: b = 8192; mn = 0; mx = 16383; break; case Program: b = 0x800000; mn = 0; mx = 0xffffff; break; case Pitch: b = 0; mn = -8192; mx = 8191; break; //case Velo: // cannot happen //case PolyAfter: //case After: default: b = 64; mn = 0; mx = 127; break; } // TODO: Limit _minVal and _maxVal to range. if(_minVal >= 0) _bias = 0; else { _bias = b; if(t != Program && t != Pitch) { // Adjust bias to fit desired range. if(_minVal + _bias < mn) _bias += mn - _minVal + _bias; else if(_maxVal + _bias > mx) _bias -= _maxVal + _bias - mx; } } } //--------------------------------------------------------- // MidiController::write //--------------------------------------------------------- void MidiController::write(int level, Xml& xml) const { ControllerType t = midiControllerType(_num); if(t == Velo) return; QString type(int2ctrlType(t)); int h = (_num >> 8) & 0x7f; int l = _num & 0x7f; QString sl; if (isPerNoteController()) sl = "pitch"; else sl.setNum(l); xml.nput(level, ""); } //--------------------------------------------------------- // MidiController::read //--------------------------------------------------------- void MidiController::read(Xml& xml) { ControllerType t = Controller7; _initVal = CTRL_VAL_UNKNOWN; int drum_init = -1; // -1 = Not set yet. static const int NOT_SET = 0x100000; _minVal = NOT_SET; _maxVal = NOT_SET; int h = 0; int l = 0; bool ok; int base = 10; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto mc_read_end; case Xml::Attribut: { QString s = xml.s2(); if (s.left(2) == "0x") base = 16; if (tag == "name") _name = xml.s2(); else if (tag == "type") t = ctrlType2Int(xml.s2()); else if (tag == "h") h = xml.s2().toInt(&ok, base); else if (tag == "l") { // Support instrument files with '*' or 'pitch' as wildcard. if ((xml.s2() == "*") || (xml.s2() == "pitch")) l = 0xff; else l = xml.s2().toInt(&ok, base); } else if (tag == "min") _minVal = xml.s2().toInt(&ok, base); else if (tag == "max") _maxVal = xml.s2().toInt(&ok, base); else if (tag == "init") _initVal = xml.s2().toInt(&ok, base); else if (tag == "drumInit") drum_init = xml.s2().toInt(&ok, base); else if (tag == "showType") _showInTracks = xml.s2().toInt(&ok, base); } break; case Xml::TagStart: xml.unknown("MidiController"); break; case Xml::TagEnd: if (tag == "Controller") { _num = (h << 8) + l; switch (t) { case RPN: if (_maxVal == NOT_SET) _maxVal = 127; _num |= CTRL_RPN_OFFSET; break; case NRPN: if (_maxVal == NOT_SET) _maxVal = 127; _num |= CTRL_NRPN_OFFSET; break; case Controller7: if (_maxVal == NOT_SET) _maxVal = 127; break; case Controller14: _num |= CTRL_14_OFFSET; if (_maxVal == NOT_SET) _maxVal = 16383; break; case RPN14: if (_maxVal == NOT_SET) _maxVal = 16383; _num |= CTRL_RPN14_OFFSET; break; case NRPN14: if (_maxVal == NOT_SET) _maxVal = 16383; _num |= CTRL_NRPN14_OFFSET; break; case Pitch: if (_maxVal == NOT_SET) _maxVal = 8191; if (_minVal == NOT_SET) _minVal = -8192; _num = CTRL_PITCH; break; case Program: if (_maxVal == NOT_SET) _maxVal = 0xffffff; _num = CTRL_PROGRAM; break; case PolyAftertouch: if (_maxVal == NOT_SET) _maxVal = 127; if (_minVal == NOT_SET) _minVal = 0; _num = CTRL_POLYAFTER; break; case Aftertouch: if (_maxVal == NOT_SET) _maxVal = 127; if (_minVal == NOT_SET) _minVal = 0; _num = CTRL_AFTERTOUCH; break; case Velo: // cannot happen break; } if (_minVal == NOT_SET) _minVal = 0; // No drum init val given? Use normal init val. if(drum_init == -1) _drumInitVal = _initVal; else _drumInitVal = drum_init; updateBias(); return; } default: break; } } mc_read_end: _drumInitVal = _initVal; } //--------------------------------------------------------- // genNum //--------------------------------------------------------- int MidiController::genNum(MidiController::ControllerType t, int h, int l) { int val = (h << 8) | (l & 0xff); switch(t) { case Controller7: return l & 0xff; case Controller14: return val + CTRL_14_OFFSET; case RPN: return val + CTRL_RPN_OFFSET; case NRPN: return val + CTRL_NRPN_OFFSET; case RPN14: return val + CTRL_RPN14_OFFSET; case NRPN14: return val + CTRL_NRPN14_OFFSET; case Pitch: return CTRL_PITCH; case Program: return CTRL_PROGRAM; case PolyAftertouch: return CTRL_POLYAFTER; case Aftertouch: return CTRL_AFTERTOUCH; default: return -1; } } //--------------------------------------------------------- // MidiControllerList //--------------------------------------------------------- MidiControllerList::MidiControllerList() { _RPN_Ctrls_Reserved = false; } MidiControllerList::MidiControllerList(const MidiControllerList& mcl) : std::map() { for(ciMidiController i = mcl.begin(); i != mcl.end(); ++i) { MidiController* mc = i->second; add(new MidiController(*mc)); } update_RPN_Ctrls_Reserved(); } bool MidiControllerList::add(MidiController* mc, bool update) { const int num = mc->num(); if(!_RPN_Ctrls_Reserved && update) { const bool isCtl7 = ((num & CTRL_OFFSET_MASK) == CTRL_7_OFFSET); const bool isCtl14 = ((num & CTRL_OFFSET_MASK) == CTRL_14_OFFSET); if(isCtl14 || isCtl7) { const int l = num & 0xff; if(l == CTRL_HDATA || l == CTRL_LDATA || l == CTRL_DATA_INC || l == CTRL_DATA_DEC || l == CTRL_HNRPN || l == CTRL_LNRPN || l == CTRL_HRPN || l == CTRL_LRPN) _RPN_Ctrls_Reserved = true; } if(!_RPN_Ctrls_Reserved && isCtl14) { const int h = (num >> 8) & 0xff; if(h == CTRL_HDATA || h == CTRL_LDATA || h == CTRL_DATA_INC || h == CTRL_DATA_DEC || h == CTRL_HNRPN || h == CTRL_LNRPN || h == CTRL_HRPN || h == CTRL_LRPN) _RPN_Ctrls_Reserved = true; } } return insert(MidiControllerListPair(num, mc)).second; } void MidiControllerList::del(iMidiController ictl, bool update) { erase(ictl); if(update) update_RPN_Ctrls_Reserved(); } MidiControllerList::size_type MidiControllerList::del(int num, bool update) { MidiControllerList::size_type res = erase(num); if(update) update_RPN_Ctrls_Reserved(); return res; } void MidiControllerList::del(iMidiController first, iMidiController last, bool update) { erase(first, last); if(update) update_RPN_Ctrls_Reserved(); } void MidiControllerList::clr() { clear(); update_RPN_Ctrls_Reserved(); } //--------------------------------------------------------- // update_RPN_Ctrls_Reserved // Manual check and update of the flag. For convenience, returns the flag. // Cost depends on types and number of list controllers, so it is good for deferring // an update until AFTER some lengthy list operation. JUST BE SURE to call this! //--------------------------------------------------------- bool MidiControllerList::update_RPN_Ctrls_Reserved() { if(find(CTRL_HDATA) != end() || find(CTRL_LDATA) != end() || find(CTRL_DATA_INC) != end() || find(CTRL_DATA_DEC) != end() || find(CTRL_HNRPN) != end() || find(CTRL_LNRPN) != end() || find(CTRL_HRPN) != end() || find(CTRL_LRPN) != end()) { _RPN_Ctrls_Reserved = true; return true; } int num, h, l; // Search: Get a head-start by taking lower bound. for(iMidiController imc = lower_bound(CTRL_14_OFFSET); imc != end(); ++imc) { num = imc->second->num(); // Stop if we went beyond this ctrl14 block. if((num & CTRL_OFFSET_MASK) != CTRL_14_OFFSET) { _RPN_Ctrls_Reserved = false; return false; } h = (num >> 8) & 0xff; l = num & 0xff; if(h == CTRL_HDATA || l == CTRL_HDATA || h == CTRL_LDATA || l == CTRL_LDATA || h == CTRL_DATA_INC || l == CTRL_DATA_INC || h == CTRL_DATA_DEC || l == CTRL_DATA_DEC || h == CTRL_HNRPN || l == CTRL_HNRPN || h == CTRL_LNRPN || l == CTRL_LNRPN || h == CTRL_HRPN || l == CTRL_HRPN || h == CTRL_LRPN || l == CTRL_LRPN) { _RPN_Ctrls_Reserved = true; return true; } } _RPN_Ctrls_Reserved = false; return false; } //--------------------------------------------------------- // Catch all insert, erase, clear etc. //--------------------------------------------------------- MidiControllerList& MidiControllerList::operator=(const MidiControllerList& cl) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiControllerList::operator=\n"); #endif _RPN_Ctrls_Reserved = cl._RPN_Ctrls_Reserved; // Let map copy the items. std::map >::operator=(cl); return *this; } //========================================================= #ifdef _MIDI_CTRL_METHODS_DEBUG_ void MidiControllerList::swap(MidiControllerList& cl) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiControllerList::swap\n"); #endif std::map >::swap(cl); } std::pair MidiControllerList::insert(const std::pair& p) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiControllerList::insert num:%d\n", p.second->num()); #endif std::pair res = std::map >::insert(p); return res; } iMidiController MidiControllerList::insert(iMidiController ic, const std::pair& p) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiControllerList::insertAt num:%d\n", p.second->num()); #endif iMidiController res = std::map >::insert(ic, p); return res; } void MidiControllerList::erase(iMidiController ictl) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiControllerList::erase iMidiController num:%d\n", ictl->second->num()); #endif std::map >::erase(ictl); } MidiControllerList::size_type MidiControllerList::erase(int num) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiControllerList::erase num:%d\n", num); #endif size_type res = std::map >::erase(num); return res; } void MidiControllerList::erase(iMidiController first, iMidiController last) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiControllerList::erase range first num:%d second num:%d\n", first->second->num(), last->second->num()); #endif std::map >::erase(first, last); } void MidiControllerList::clear() { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiControllerList::clear\n"); #endif std::map >::clear(); } #endif // ========================================================= //--------------------------------------------------------- // perNoteController // Returns per-note controller if there is one for the given ctl number. // Otherwise returns null. //--------------------------------------------------------- MidiController* MidiControllerList::perNoteController(int ctl) const { const int n = ctl & 0xff0000; if(((ctl | 0xff) == CTRL_POLYAFTER) || (n == CTRL_RPN_OFFSET || n == CTRL_NRPN_OFFSET || n == CTRL_RPN14_OFFSET || n == CTRL_NRPN14_OFFSET)) { // Does the list have a per-note controller to match the controller number? const_iterator imc = find(ctl | 0xff); if(imc != cend()) // Yes, it's a per-note controller. Return a pointer to it. return imc->second; } return nullptr; } MidiController* MidiControllerList::findController(int ctl) const { // Find a controller for the verbose ctl number. const_iterator i = find(ctl); if(i != cend()) return i->second; // Find a per-note controller for the ctl number. return perNoteController(ctl); } //--------------------------------------------------------- // searchControllers //--------------------------------------------------------- iMidiController MidiControllerList::searchControllers(int ctl) { const int type = ctl & CTRL_OFFSET_MASK; int n; // Looking for Controller7? See if any Controller14 contains the number and should be used instead. if(type == CTRL_7_OFFSET) { const int num = ctl & 0xff; for(iMidiController imc = lower_bound(CTRL_14_OFFSET); imc != end(); ++imc) { n = imc->second->num(); // Stop if we went beyond this ctrl14 block. if((n & CTRL_OFFSET_MASK) != CTRL_14_OFFSET) break; if(((n >> 8) & 0xff) == num || (n & 0xff) == num) return imc; } } // Looking for RPN? See if any RPN14 also uses the number and should be used instead. else if (type == CTRL_RPN_OFFSET) { const int num = ctl & 0xffff; for(iMidiController imc = lower_bound(CTRL_RPN14_OFFSET); imc != end(); ++imc) { n = imc->second->num(); // Stop if we went beyond this RPN14 block. if((n & CTRL_OFFSET_MASK) != CTRL_RPN14_OFFSET) break; if((n & 0xffff) == num) return imc; } } // Looking for NRPN? See if any NRPN14 also uses the number and should be used instead. else if (type == CTRL_NRPN_OFFSET) { const int num = ctl & 0xffff; for(iMidiController imc = lower_bound(CTRL_NRPN14_OFFSET); imc != end(); ++imc) { n = imc->second->num(); // Stop if we went beyond this NRPN14 block. if((n & CTRL_OFFSET_MASK) != CTRL_NRPN14_OFFSET) break; if((n & 0xffff) == num) return imc; } } // Looking for any other type? Do a regular find. return find(ctl); } //--------------------------------------------------------- // ctrlAvailable // Check if either a per-note controller, or else a regular controller already exists. //--------------------------------------------------------- bool MidiControllerList::ctrlAvailable(int find_num, MidiController* ignore_this) { MusECore::ciMidiController imc; for(imc = begin(); imc != end(); ++ imc) { // Ignore this controller. if(ignore_this && imc->second == ignore_this) continue; int n = imc->second->num(); if(((find_num & 0xff) == 0xff) && ((n | 0xff) == find_num)) break; if(imc->second->isPerNoteController() && ((find_num | 0xff) == n)) break; if(find_num == n) break; } return imc == end(); } } // namespace MusECore muse-4.0.0/src/libs/midi_controller/midi_controller.h000066400000000000000000000664121404105311000227250ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midictrl.h,v 1.16.2.8 2009/11/25 09:09:43 terminator356 Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDI_CONTROLLER_H__ #define __MIDI_CONTROLLER_H__ #include #include #include "midictrl_consts.h" #include "xml.h" //#define _MIDI_CTRL_DEBUG_ // For finding exactly who may be calling insert, erase clear etc. in // the controller list classes. (KDevelop 'Find uses'.) //#define _MIDI_CTRL_METHODS_DEBUG_ namespace MusECore { //--------------------------------------------------------- // MidiController //--------------------------------------------------------- class MidiController { public: // // mapping of midi controller types to // controller number: // enum ControllerType { Controller7, // num values from 0 - 0x7f Controller14, // values from 0x10000 - 0x12fff RPN, // registered parameter 0x20000 - NRPN, // non registered parameter 0x30000 - RPN14, // registered parameter 0x50000 NRPN14, // non registered parameter 0x60000 - Pitch, // num value = CTRL_PITCH Program, // num value = CTRL_PROGRAM PolyAftertouch, // num value = CTRL_POLYAFTER Aftertouch, // num value = CTRL_AFTERTOUCH Velo // not assigned }; enum ShowInTrackType { ShowInDrum=1, ShowInMidi=2 }; protected: QString _name; int _num; // Controller Number int _minVal; // controller value range (used in gui) int _maxVal; int _initVal; // Special for drum mode, for controllers such as program. int _drumInitVal; int _bias; int _showInTracks; void updateBias(); public: MidiController(); // If drumInit = -1, it means don't care - use the init val. MidiController(const QString& n, int num, int min, int max, int init, int drumInit, int show_in_track = (ShowInDrum | ShowInMidi)); MidiController(const MidiController& mc); void copy(const MidiController &mc); MidiController& operator= (const MidiController &mc); // Convert given controller double value to integer. static inline int dValToInt(double v) { // TODO: Decide best choice here. //return int(round(v)); //return lrint(v); return int(v); } // Whether the given integer value is CTRL_VAL_UNKNOWN. static inline bool iValIsUnknown(int v) { return v == CTRL_VAL_UNKNOWN; } // Whether the given double value is CTRL_VAL_UNKNOWN. static inline bool dValIsUnknown(double v) { return iValIsUnknown(dValToInt(v)); } const QString& name() const { return _name; } int num() const { return _num; } ControllerType type() const; void setName(const QString& s) { _name = s; } void setNum(int v) { _num = v; } void write(int level, Xml& xml) const; void read(Xml& xml); int minVal() const { return _minVal; } int maxVal() const { return _maxVal; } int initVal() const { return _initVal; } inline bool initValIsUnknown() const { return iValIsUnknown(_initVal); } void setInitVal(int val) { _initVal = val; } int drumInitVal() const { return _drumInitVal; } inline bool drumInitValIsUnknown() const { return iValIsUnknown(_drumInitVal); } void setDrumInitVal(int val) { _drumInitVal = val; } void setMinVal(int val) { _minVal = val; updateBias(); } void setMaxVal(int val) { _maxVal = val; updateBias(); } int bias() const { return _bias; } int showInTracks() const { return _showInTracks; } void setShowInTracks(int i) { _showInTracks = i; } bool isPerNoteController() const; static int genNum(ControllerType, int, int); }; //--------------------------------------------------------- // MidiControllerList // this is a list of used midi controllers created // - explicit by user // - implicit during import of a midi file //--------------------------------------------------------- typedef std::map > MidiControllerList_t; class MidiControllerList : public MidiControllerList_t { bool _RPN_Ctrls_Reserved; public: MidiControllerList(); MidiControllerList(const MidiControllerList& mcl); // NOTE: There is no destructor here, this container does not // delete its own contents, that is done in ~MidiInstrument // because some controls are shared (the ones in // defaultMidiController and defaultManagedMidiController). // However, an inheritor such as the class MidiNamCtrls // found in the MidNam module DOES delete its own. // Like find() which finds a verbose ctl number, but this version also finds a per-note // controller if there is one for the given ctl number, if no verbose one was found. // Returns null if no controller found. The ctl number can be the 'real' controller number, // ie the low byte can be the actual note number and does not have to be 0xff. MidiController* findController(int ctl) const; // Returns per-note controller if there is one for the given ctl number. // Otherwise returns null. The ctl number can be the 'real' controller number, // ie the low byte can be the actual note number and does not have to be 0xff. MidiController* perNoteController(int ctl) const; // Like 'find', finds a controller given fully qualified type + number. // But it returns controller with highest priority if multiple controllers use the // given number such as {Controller7, num = 0x55} + {Controller14, num = 0x5544}. // Note if given number is one of the eight reserved General Midi (N)RPN controllers, // this will only return Controller7 or Controller14, not anything (N)RPN related. // That is, it will not 'encode' (N)RPNs. Use a MidiEncoder instance for that. iterator searchControllers(int ctl); // Check if either a per-note controller, or else a regular controller already exists. bool ctrlAvailable(int find_num, MidiController* ignore_this = 0); // Returns true if any of the EIGHT reserved General Midi (N)RPN control numbers are // ALREADY defined as Controller7 or part of Controller14. Cached, for speed. // Used (at least) by midi input encoders to quickly arbitrate new input. bool RPN_Ctrls_Reserved() { return _RPN_Ctrls_Reserved; } // Manual check and update of the flag. For convenience, returns the flag. bool update_RPN_Ctrls_Reserved(); // NOTICE: If update is false or these are bypassed by using insert, erase, clear etc. for speed, // then BE SURE to call update_RPN_Ctrls_Reserved() later. // Returns true if add is successful. bool add(MidiController* mc, bool update = true); void del(iterator ictl, bool update = true); size_type del(int num, bool update = true); void del(iterator first, iterator last, bool update = true); void clr(); #ifdef _MIDI_CTRL_METHODS_DEBUG_ // Need to catch all insert, erase, clear etc... void swap(MidiControllerList&); std::pair insert(const std::pair& p); iterator insert(iterator ic, const std::pair& p); void erase(iterator ictl); size_type erase(int num); void erase(iterator first, iterator last); void clear(); #endif // Some IDEs won't "Find uses" of operators. So, no choice but to trust always catching it here. MidiControllerList& operator=(const MidiControllerList&); }; typedef MidiControllerList::iterator iMidiController; typedef MidiControllerList::const_iterator ciMidiController; typedef std::pair MidiControllerListPair; extern MidiController::ControllerType midiControllerType(int num); extern int midiCtrlTerms2Number(MidiController::ControllerType type, int ctrl = 0); extern bool isPerNoteMidiController(int num); extern const QString& int2ctrlType(int n); extern MidiController::ControllerType ctrlType2Int(const QString& s); extern QString midiCtrlName(int ctrl, bool fullyQualified = false); extern QString midiCtrlNumString(int ctrl, bool fullyQualified = false); typedef std::map > MidiCtl2LadspaPortMap; typedef MidiCtl2LadspaPortMap::iterator iMidiCtl2LadspaPort; typedef MidiCtl2LadspaPortMap::const_iterator ciMidiCtl2LadspaPort; // // REMOVE Tim. midnam. Added. // // template, // // class Alloc = std::allocator > > // // class CompoundMidiControllerList_t : public MidiControllerList_t // class CompoundMidiControllerList_t : public MidiControllerList // { // private: // // typedef std::multimap vlist; // // typedef MidiControllerList_t vlist; // typedef MidiControllerList vlist; // //typedef typename std::list::const_iterator cil_t; // // //MidiControllerList** _p_other; // //MidNamMIDIName* _midnamDocument; // // protected: // //Pos::TType _type; // MidiControllerList** _p_other; // // public: // typedef typename vlist::iterator iCompoundMidiControllerList_t; // typedef typename vlist::const_iterator ciCompoundMidiControllerList_t; // // typedef std::pair cMixedPosListRange_t; // // typedef std::pair MixedPosListInsertPair_t; // typedef std::pair CompoundMidiControllerListInsertPair_t; // // // class iterator : public vlist::iterator { // protected: // const MidiControllerList* _p_list; // MidiControllerList** _p_other; // bool _isFromOther; // // public: // iterator() : vlist::iterator(), _p_list(nullptr), _p_other(nullptr), _isFromOther(false) {} // iterator(vlist::iterator i) : vlist::iterator(i), _p_list(nullptr), _p_other(nullptr), _isFromOther(false) {} // // void setLists(const MidiControllerList* this_list, MidiControllerList** other, bool isFromOther) // { _p_list = this_list; _p_other = other; _isFromOther = isFromOther; } // // // MidiController* operator*() { // // //return (MidiController*)(**((vlist::iterator*)this)); // // return (MidiController*)(**((vlist::iterator*)this->second)); // // } // iterator operator++(int) { // return iterator ((*(vlist::iterator*)this).operator++(0)); // } // iterator& operator++() { // vlist::iterator i_this = *this; // i_this++; // if(_p_other && !(*_p_other)->empty()) // { // // } // // (iterator&) ((*(vlist::iterator*)this).operator++()) // // // return (iterator&) ((*(vlist::iterator*)this).operator++()); // } // }; // // class const_iterator : public vlist::const_iterator { // protected: // const MidiControllerList* _p_list; // MidiControllerList** _p_other; // bool _isFromOther; // // public: // const_iterator() : vlist::const_iterator(), _p_list(nullptr), _p_other(nullptr), _isFromOther(false) {} // const_iterator(vlist::const_iterator i) // : vlist::const_iterator(i), _p_list(nullptr), _p_other(nullptr), _isFromOther(false) {} // const_iterator(vlist::iterator i) // : vlist::const_iterator(i), _p_list(nullptr), _p_other(nullptr), _isFromOther(false) {} // // void setLists(const MidiControllerList* this_list, MidiControllerList** other, bool isFromOther) // { _p_list = this_list; _p_other = other; _isFromOther = isFromOther; } // // // const T operator*() const { // // return (T)(**((vlist::const_iterator*)this)); // // } // std::pair operator*() const { // return **((vlist::const_iterator*)this); // } // // const MidiController* operator->() const { // // return (**((vlist::const_iterator*)this)->second); // // } // }; // // class reverse_iterator : public vlist::reverse_iterator { // protected: // const MidiControllerList* _p_list; // MidiControllerList** _p_other; // bool _isFromOther; // // public: // reverse_iterator() : vlist::reverse_iterator(), _p_list(nullptr), _p_other(nullptr), _isFromOther(false) {} // reverse_iterator(vlist::reverse_iterator i) // : vlist::reverse_iterator(i), _p_list(nullptr), _p_other(nullptr), _isFromOther(false) {} // // void setLists(const MidiControllerList* this_list, MidiControllerList** other, bool isFromOther) // { _p_list = this_list; _p_other = other; _isFromOther = isFromOther; } // // // T operator*() { // // return (T)(**((vlist::reverse_iterator*)this)); // // } // std::pair operator*() { // return **((vlist::reverse_iterator*)this); // } // }; // // class const_reverse_iterator : public vlist::const_reverse_iterator { // protected: // const MidiControllerList* _p_list; // MidiControllerList** _p_other; // bool _isFromOther; // // public: // const_reverse_iterator() : vlist::const_reverse_iterator(), _p_list(nullptr), _p_other(nullptr), _isFromOther(false) {} // const_reverse_iterator(vlist::const_reverse_iterator i) // : vlist::const_reverse_iterator(i), _p_list(nullptr), _p_other(nullptr), _isFromOther(false) {} // // void setLists(const MidiControllerList* this_list, MidiControllerList** other, bool isFromOther) // { _p_list = this_list; _p_other = other; _isFromOther = isFromOther; } // // // T operator*() { // // return (T)(**((vlist::const_reverse_iterator*)this)); // // } // std::pair operator*() { // return **((vlist::const_reverse_iterator*)this); // } // }; // // // // // //CompoundMidiControllerList_t(Pos::TType type = Pos::TICKS) : vlist(), _type(type) {} // CompoundMidiControllerList_t(MidiControllerList** other = nullptr) // : vlist(), _p_other(other) { } // // virtual ~CompoundMidiControllerList_t() {} // // //inline Pos::TType type() const { return _type; } // //inline void setType(const Pos::TType& t) { _type = t; rebuild(); } // // iterator begin() noexcept // { // iterator i_this = vlist::begin(); // i_this.setLists(this, _p_other, false); // if(_p_other && !(*_p_other)->empty()) // { // iterator i_other = (*_p_other)->begin(); // i_other.setLists(this, _p_other, true); // if(vlist::empty() || i_other->first < i_this->first) // return i_other; // } // return i_this; // } // // const_iterator begin() const noexcept // { // const_iterator i_this = vlist::begin(); // i_this.setLists(this, _p_other, false); // if(_p_other && !(*_p_other)->empty()) // { // const MidiControllerList* mcl_other = *_p_other; // const_iterator i_other = mcl_other->begin(); // i_other.setLists(this, _p_other, true); // if(vlist::empty() || i_other->first < i_this->first) // return i_other; // } // return i_this; // } // // const_iterator cbegin() const noexcept // { // const_iterator i_this = vlist::cbegin(); // i_this.setLists(this, _p_other, false); // if(_p_other && !(*_p_other)->empty()) // { // const_iterator i_other = (*_p_other)->cbegin(); // i_other.setLists(this, _p_other, true); // if(vlist::empty() || i_other->first < i_this->first) // return i_other; // } // return i_this; // } // // reverse_iterator rbegin() noexcept // { // reverse_iterator i_this = vlist::rbegin(); // i_this.setLists(this, _p_other, false); // if(_p_other && !(*_p_other)->empty()) // { // reverse_iterator i_other = (*_p_other)->rbegin(); // i_other.setLists(this, _p_other, true); // if(vlist::empty() || i_other->first > i_this->first) // return i_other; // } // return i_this; // } // // const_reverse_iterator rbegin() const noexcept // { // const_reverse_iterator i_this = vlist::rbegin(); // i_this.setLists(this, _p_other, false); // if(_p_other && !(*_p_other)->empty()) // { // const MidiControllerList* mcl_other = *_p_other; // const_reverse_iterator i_other = mcl_other->rbegin(); // i_other.setLists(this, _p_other, true); // if(vlist::empty() || i_other->first > i_this->first) // return i_other; // } // return i_this; // } // // const_reverse_iterator crbegin() const noexcept // { // const_reverse_iterator i_this = vlist::crbegin(); // i_this.setLists(this, _p_other, false); // if(_p_other && !(*_p_other)->empty()) // { // const_reverse_iterator i_other = (*_p_other)->crbegin(); // i_other.setLists(this, _p_other, true); // if(vlist::empty() || i_other->first > i_this->first) // return i_other; // } // return i_this; // } // // // // ------------------------------------------------------------------- // // With these end iterators, we have a choice of either container. // // We could ask which container's end is more relevant by comparing // // the decremented end iterators, or we could simply just take the // // first container's. Which ever way we choose, it MUST be // // consistent with the way our operator++/-- work. // // ------------------------------------------------------------------- // // iterator end() noexcept // { // iterator i_this = vlist::end(); // i_this.setLists(this, _p_other, false); // // if(_p_other && !(*_p_other)->empty()) // // { // // iterator i_other = (*_p_other)->end(); // // i_other.setLists(this, _p_other, true); // // if(vlist::empty() || i_other->first > i_this->first) // // return i_other; // // } // return i_this; // } // // const_iterator end() const noexcept // { // const_iterator i_this = vlist::end(); // i_this.setLists(this, _p_other, false); // // if(_p_other && !(*_p_other)->empty()) // // { // // const_iterator i_other = (*_p_other)->end(); // // i_other.setLists(this, _p_other, true); // // if(vlist::empty() || i_other->first > i_this->first) // // return i_other; // // } // return i_this; // } // // const_iterator cend() const noexcept // { // const_iterator i_this = vlist::cend(); // i_this.setLists(this, _p_other, false); // // if(_p_other && !(*_p_other)->empty()) // // { // // const_iterator i_other = (*_p_other)->cend(); // // i_other.setLists(this, _p_other, true); // // if(vlist::empty() || i_other->first > i_this->first) // // return i_other; // // } // return i_this; // } // // reverse_iterator rend() noexcept // { // reverse_iterator i_this = vlist::rend(); // i_this.setLists(this, _p_other, false); // // if(_p_other && !(*_p_other)->empty()) // // { // // reverse_iterator i_other = (*_p_other)->rend(); // // i_other.setOtherList(_p_other, true); // // if(vlist::empty() || i_other->first < i_this->first) // // return i_other; // // } // return i_this; // } // // const_reverse_iterator crend() const noexcept // { // const_reverse_iterator i_this = vlist::crend(); // i_this.setLists(this, _p_other, false); // // if(_p_other && !(*_p_other)->empty()) // // { // // const_reverse_iterator i_other = (*_p_other)->crend(); // // i_other.setLists(this, _p_other, true); // // if(vlist::empty() || i_other->first < i_this->first) // // return i_other; // // } // return i_this; // } // // bool empty() const noexcept // { // return vlist::empty() && (!_p_other || (*_p_other)->empty()); // } // // size_type size() const noexcept // { // return vlist::size() + (_p_other ? (*_p_other)->size() : 0); // } // // iterator find(const int& key) // { // iterator i_this = vlist::find(key); // i_this.setLists(this, _p_other, false); // if(i_this == vlist::end() && _p_other) // { // iterator i_other = (*_p_other)->find(key); // i_other.setLists(this, _p_other, true); // if(i_other != (*_p_other)->end()) // return i_other; // } // return i_this; // } // // const_iterator find(const int& key) const // { // const_iterator i_this = vlist::find(key); // i_this.setLists(this, _p_other, false); // if(i_this == vlist::end() && _p_other) // { // const MidiControllerList* mcl_other = *_p_other; // const_iterator i_other = mcl_other->find(key); // i_other.setLists(this, _p_other, true); // if(i_other != mcl_other->end()) // return i_other; // } // return i_this; // } // // iterator lower_bound(const int& key) // { // iterator i_this = vlist::lower_bound(key); // i_this.setLists(this, _p_other, false); // if(i_this == vlist::end() && _p_other && !(*_p_other)->empty()) // { // iterator i_other = (*_p_other)->lower_bound(key); // i_other.setLists(this, _p_other, true); // if(i_other != (*_p_other)->end() && (vlist::empty() || i_other->first < i_this->first)) // return i_other; // } // return i_this; // } // // const_iterator lower_bound(const int& key) const // { // const_iterator i_this = vlist::lower_bound(key); // i_this.setLists(this, _p_other, false); // if(i_this == vlist::end() && _p_other && !(*_p_other)->empty()) // { // const MidiControllerList* mcl_other = *_p_other; // const_iterator i_other = mcl_other->lower_bound(key); // i_other.setLists(this, _p_other, true); // if(i_other != mcl_other->end() && (vlist::empty() || i_other->first < i_this->first)) // return i_other; // } // return i_this; // } // // iterator upper_bound(const int& key) // { // iterator i_this = vlist::upper_bound(key); // i_this.setLists(this, _p_other, false); // if(i_this == vlist::end() && _p_other && !(*_p_other)->empty()) // { // iterator i_other = (*_p_other)->upper_bound(key); // i_other.setLists(this, _p_other, true); // if(i_other != (*_p_other)->end() && (vlist::empty() || i_other->first < i_this->first)) // return i_other; // } // return i_this; // } // // const_iterator upper_bound(const int& key) const // { // const_iterator i_this = vlist::upper_bound(key); // i_this.setLists(this, _p_other, false); // if(i_this == vlist::end() && _p_other && !(*_p_other)->empty()) // { // const MidiControllerList* mcl_other = *_p_other; // const_iterator i_other = mcl_other->upper_bound(key); // i_other.setLists(this, _p_other, true); // if(i_other != mcl_other->end() && (vlist::empty() || i_other->first < i_this->first)) // return i_other; // } // return i_this; // } // // // // // // // Returns an iterator that points to the inserted event. // // // Note that the pair's key part is IGNORED. // // // All information is gathered from the pair's value type T. // // // Returns end() if an error occurred. // // iCompoundMidiControllerList_t insert (const CompoundMidiControllerListInsertPair_t& v) // // { // // return add(v.second); // // } // // // // // TODO: // // // template iMixedPosList insert (P&& v) { return vlist::insert(v); } // // // iMixedPosList insert (ciMixedPosList pos, const T& v) { return vlist::insert(pos, v); } // // // template iMixedPosList insert (ciMixedPosList pos, P&& v) { return vlist::insert(pos, v); } // // // template // // // void insert (InputIterator first, InputIterator last) { return vlist::insert(first, last); } // // // void insert (std::initializer_list il) { return vlist::insert(il); } // // // // // Returns an iterator that points to the added event. // // // Returns end() if an error occurred. // // iMixedPosList_t add(const T& v) // // { // // const unsigned v_frame = v.frame(); // // const unsigned v_tick = v.tick(); // // ciMixedPosList_t pos = vlist::end(); // // cMixedPosListRange_t r; // // // // // If list type is ticks, compare frame. If list type is frames, compare tick. // // switch(type()) // // { // // case Pos::TICKS: // // r = vlist::equal_range(v_tick); // // for(pos = r.first; pos != r.second; ++pos) // // if(v_frame < pos->second.frame()) // // break; // // return vlist::insert(pos, MixedPosListInsertPair_t(v_tick, v)); // // break; // // // // case Pos::FRAMES: // // r = vlist::equal_range(v_frame); // // for(pos = r.first; pos != r.second; ++pos) // // if(v_tick < pos->second.tick()) // // break; // // return vlist::insert(pos, MixedPosListInsertPair_t(v_frame, v)); // // break; // // } // // return vlist::end(); // // } // // // }; } // namespace MusECore #endif muse-4.0.0/src/libs/midi_controller/midictrl_consts.h000066400000000000000000000101621404105311000227270ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // midictrl_consts.h // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDICTRL_CONSTS_H__ #define __MIDICTRL_CONSTS_H__ namespace MusECore { const int CTRL_HBANK = 0x00; const int CTRL_LBANK = 0x20; const int CTRL_HDATA = 0x06; const int CTRL_LDATA = 0x26; const int CTRL_DATA_INC = 0x60; const int CTRL_DATA_DEC = 0x61; const int CTRL_HNRPN = 0x63; const int CTRL_LNRPN = 0x62; const int CTRL_HRPN = 0x65; const int CTRL_LRPN = 0x64; const int CTRL_MODULATION = 0x01; const int CTRL_PORTAMENTO_TIME = 0x05; const int CTRL_VOLUME = 0x07; const int CTRL_PANPOT = 0x0a; const int CTRL_EXPRESSION = 0x0b; const int CTRL_SUSTAIN = 0x40; const int CTRL_PORTAMENTO = 0x41; const int CTRL_SOSTENUTO = 0x42; const int CTRL_SOFT_PEDAL = 0x43; const int CTRL_HARMONIC_CONTENT = 0x47; const int CTRL_RELEASE_TIME = 0x48; const int CTRL_ATTACK_TIME = 0x49; const int CTRL_BRIGHTNESS = 0x4a; const int CTRL_PORTAMENTO_CONTROL = 0x54; const int CTRL_REVERB_SEND = 0x5b; const int CTRL_CHORUS_SEND = 0x5d; const int CTRL_VARIATION_SEND = 0x5e; // Channel Mode controllers: // Same as other 7-bit controllers, but "implements Mode control and // special message by using reserved controller numbers 120-127" (MMA). // const int CTRL_ALL_SOUNDS_OFF = 0x78; // 120 const int CTRL_RESET_ALL_CTRL = 0x79; // 121 const int CTRL_LOCAL_OFF = 0x7a; // 122 const int CTRL_ALL_NOTES_OFF = 0x7b; // 123 const int CTRL_OMNI_MODE_OFF = 0x7c; // 124 All notes off as well. const int CTRL_OMNI_MODE_ON = 0x7d; // 125 All notes off as well. const int CTRL_MONO_MODE_ON = 0x7e; // 126 All notes off as well. const int CTRL_POLY_MODE_ON = 0x7f; // 127 All notes off as well. // controller types 0x10000 - 0x1ffff are 14 bit controller with // 0x1xxyy // xx - MSB controller // yy - LSB controller // RPN - registered parameter numbers 0x20000 - // NRPN - non registered parameter numbers 0x30000 - // internal controller types: const int CTRL_INTERNAL_OFFSET = 0x40000; const int CTRL_PITCH = CTRL_INTERNAL_OFFSET; const int CTRL_PROGRAM = CTRL_INTERNAL_OFFSET + 0x01; const int CTRL_VELOCITY = CTRL_INTERNAL_OFFSET + 0x02; const int CTRL_MASTER_VOLUME = CTRL_INTERNAL_OFFSET + 0x03; const int CTRL_AFTERTOUCH = CTRL_INTERNAL_OFFSET + 0x04; // NOTE: The range from CTRL_INTERNAL_OFFSET + 0x100 to CTRL_INTERNAL_OFFSET + 0x1ff is reserved // for this control. (The low byte is reserved because this is a per-note control.) const int CTRL_POLYAFTER = CTRL_INTERNAL_OFFSET + 0x1FF; // 100 to 1FF ! const int CTRL_VAL_UNKNOWN = 0x10000000; // used as unknown hwVal const int CTRL_PROGRAM_VAL_DONT_CARE = 0xffffff; // High-bank, low-bank, and program are all 0xff don't care. const int CTRL_7_OFFSET = 0x00000; const int CTRL_14_OFFSET = 0x10000; const int CTRL_RPN_OFFSET = 0x20000; const int CTRL_NRPN_OFFSET = 0x30000; const int CTRL_RPN14_OFFSET = 0x50000; const int CTRL_NRPN14_OFFSET = 0x60000; const int CTRL_NONE_OFFSET = 0x70000; const int CTRL_OFFSET_MASK = 0xf0000; } // namespace MusECore #endif muse-4.0.0/src/libs/midnam/000077500000000000000000000000001404105311000154365ustar00rootroot00000000000000muse-4.0.0/src/libs/midnam/CMakeLists.txt000066400000000000000000000030761404105311000202040ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # midname/CMakeLists.txt # (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB midnam_source_files midnam.cpp ) ## ## Define target ## add_library ( midnam_module SHARED ${midnam_source_files} ) ## ## Compilation flags and target name ## set_target_properties( midnam_module PROPERTIES OUTPUT_NAME muse_midnam_module ) target_link_libraries(midnam_module xml_module midi_controller_module mpevent_module ${QT_LIBRARIES} ) ## ## Install location ## install(TARGETS midnam_module DESTINATION ${MusE_MODULES_DIR} ) muse-4.0.0/src/libs/midnam/midnam.cpp000066400000000000000000004750151404105311000174230ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // midnam.cpp // (C) Copyright 2019, 2020 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "midnam.h" #include "midi_consts.h" #include // Channel range is from 1 to 16. #define MIDNAM_MAX_CHANNEL 16 namespace MusECore { //---------------------- // TODO MTCQuarterFrame //---------------------- // bool readMTCQuarterFrame( // MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, // int defaultPort = 0 // ) // { // } bool readSystemReset( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0 ) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readSystemReset"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "SystemReset") { ev = MidiPlayEvent(time_ms, defaultPort, 0, ME_RESET, 0, 0); return true; } default: break; } } return false; } bool readActiveSensing( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0 ) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readActiveSensing"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "ActiveSensing") { ev = MidiPlayEvent(time_ms, defaultPort, 0, ME_SENSE, 0, 0); return true; } default: break; } } return false; } bool readStop( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0 ) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readStop"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "Stop") { ev = MidiPlayEvent(time_ms, defaultPort, 0, ME_STOP, 0, 0); return true; } default: break; } } return false; } bool readContinue( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0 ) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readContinue"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "Continue") { ev = MidiPlayEvent(time_ms, defaultPort, 0, ME_CONTINUE, 0, 0); return true; } default: break; } } return false; } bool readStart( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0 ) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readStart"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "Start") { ev = MidiPlayEvent(time_ms, defaultPort, 0, ME_START, 0, 0); return true; } default: break; } } return false; } bool readTimingClock( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0 ) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readTimingClock"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "TimingClock") { ev = MidiPlayEvent(time_ms, defaultPort, 0, ME_CLOCK, 0, 0); return true; } default: break; } } return false; } bool readTuneRequest( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0 ) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readTuneRequest"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "TuneRequest") { ev = MidiPlayEvent(time_ms, defaultPort, 0, ME_TUNE_REQ, 0, 0); return true; } default: break; } } return false; } bool readSongSelect( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0 ) { int number = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readSongSelect"); break; case MusECore::Xml::Attribut: if (tag == "Number") { number = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "SongSelect") { if(number < 0) return false; // ev = MidiPlayEvent(time_ms, defaultPort, 0, ME_SONGSEL, number, 0); return true; } default: break; } } return false; } bool readSongPositionPointer( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0 ) { int position = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readSongPositionPointer"); break; case MusECore::Xml::Attribut: if (tag == "Position") { position = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "SongPositionPointer") { if(position < 0) return false; // ev = MidiPlayEvent(time_ms, defaultPort, 0, ME_SONGPOS, position, 0); return true; } default: break; } } return false; } bool readSysEx( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, int defaultChannel = 0) { int chan = -1; QByteArray bytes; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readSysEx"); break; case MusECore::Xml::Attribut: break; case Xml::Text: { // // // // // // // // // // // I found only TWO examples on the net that use SysExDeviceID, // others don't use SysExDeviceID in the SysEx string: // // Example from https://github.com/Ardour/ardour/blob/master/libs/pbd/test/ProtoolsPatchFile.midnam // F0 41 42 12 40 00 7F 00 41 F7 // // Example from https://github.com/Ardour/ardour/blob/master/patchfiles/Korg_iSeries.midnam // F0 42 39 4E 06 F7 // // Notice in that example there is NO SPACE between 42 and ', "> "); // Simplify to get rid of extra space before splitting. ba = ba.simplified(); const QList vals = ba.split(' '); bool ok; bool in_tag = false; for(QList::const_iterator il = vals.cbegin(); il != vals.cend(); ++il) { const QByteArray& l_ba = *il; if(in_tag) { if(l_ba.endsWith('>')) in_tag = false; continue; } else if(l_ba.startsWith('<')) { in_tag = true; continue; } const unsigned int c = l_ba.toUInt(&ok, 16); // Anything higher than a byte is an error. if(!ok || c >= 0x100) continue; bytes.append(c); } } break; case MusECore::Xml::TagEnd: if (tag == "SysEx") { if(chan == 0 || chan > MIDNAM_MAX_CHANNEL || bytes.isEmpty()) return false; if(chan < 0) chan = defaultChannel; else --chan; // Now create the data. ev.setTime(time_ms); ev.setPort(defaultPort); ev.setChannel(chan); ev.setType(ME_SYSEX); ev.setData((const unsigned char*)bytes.constData(), bytes.size()); return true; } default: break; } } return false; } bool readNRPNChange( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int nrpn = -1; int value = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readNRPNChange"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "NRPN") { nrpn = xml.s2().toInt(); } else if (tag == "Value") { value = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "NRPNChange") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || nrpn < 0 || value < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; // 14-bit control changes: // Value is 0..16383 // ControlChange14's Control is MSB 0..31 // RPNN and NRPN are 0..16383 nrpn = ((nrpn & 0x3f80) << 1) + (nrpn & 0x7f) + CTRL_NRPN_OFFSET; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, nrpn, value); return true; } default: break; } } return false; } bool readRPNChange( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int rpn = -1; int value = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readRPNChange"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "RPN") { rpn = xml.s2().toInt(); } else if (tag == "Value") { value = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "RPNChange") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || rpn < 0 || value < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; // 14-bit control changes: // Value is 0..16383 // ControlChange14's Control is MSB 0..31 // RPNN and NRPN are 0..16383 rpn = ((rpn & 0x3f80) << 1) + (rpn & 0x7f) + CTRL_RPN_OFFSET; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, rpn, value); return true; } default: break; } } return false; } bool readControlChange14( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int control = -1; int value = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readControlChange14"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Control") { control = xml.s2().toInt(); } else if (tag == "Value") { value = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "ControlChange14") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || control < 0 || value < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; // 14-bit control changes: // Value is 0..16383 // ControlChange14's Control is MSB 0..31 // NOTE: This does not support our own separate high and low control numbers, // only standard MSB and implied LSB. control = ((control + 0x20) << 8) + control + CTRL_14_OFFSET; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, control, value); return true; } default: break; } } return false; } bool readPolyMode( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readPolyMode"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "PolyMode") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, CTRL_POLY_MODE_ON, 0); return true; } default: break; } } return false; } bool readMonoMode( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int value = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readMonoMode"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Value") { value = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "MonoMode") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || value < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, CTRL_MONO_MODE_ON, value); return true; } default: break; } } return false; } bool readOmniOn( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readOmniOn"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "OmniOn") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, CTRL_OMNI_MODE_ON, 0); return true; } default: break; } } return false; } bool readOmniOff( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readOmniOff"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "OmniOff") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, CTRL_OMNI_MODE_OFF, 0); return true; } default: break; } } return false; } bool readLocalControl( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int value = -1; QString val_txt; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readLocalControl"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Value") { val_txt = xml.s2(); if(val_txt.compare("on", Qt::CaseInsensitive) == 0) value = 127; else if(val_txt.compare("off", Qt::CaseInsensitive) == 0) value = 0; } break; case MusECore::Xml::TagEnd: if (tag == "LocalControl") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || value < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, CTRL_LOCAL_OFF, value); return true; } default: break; } } return false; } bool readResetAllControllers( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readResetAllControllers"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "ResetAllControllers") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, CTRL_RESET_ALL_CTRL, 0); return true; } default: break; } } return false; } bool readAllNotesOff( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readAllNotesOff"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "AllNotesOff") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, CTRL_ALL_NOTES_OFF, 0); return true; } default: break; } } return false; } bool readAllSoundOff( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readAllSoundOff"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "AllSoundOff") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, CTRL_ALL_SOUNDS_OFF, 0); return true; } default: break; } } return false; } bool readPitchBendChange( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int value = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readPitchBendChange"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Value") { value = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "PitchBendChange") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || value < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; // PitchBend's Value is 0..16383 // Our MPEvent range is -8192..8191 in data A. value -= 8192; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_PITCHBEND, value, 0); return true; } default: break; } } return false; } bool readChannelKeyPressure( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int pressure = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readChannelKeyPressure"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Pressure") { pressure = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "ChannelKeyPressure") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || pressure < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_AFTERTOUCH, pressure, 0); return true; } default: break; } } return false; } bool readProgramChange( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int number = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readProgramChange"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Number") { number = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "ProgramChange") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || number < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_PROGRAM, number, 0); return true; } default: break; } } return false; } bool readControlChange( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int control = -1; int value = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readControlChange"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Control") { control = xml.s2().toInt(); } else if (tag == "Value") { value = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "ControlChange") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || control < 0 || value < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_CONTROLLER, control, value); return true; } default: break; } } return false; } bool readPolyKeyPressure( MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int note = -1; int pressure = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readPolyKeyPressure"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Note") { note = xml.s2().toInt(); } else if (tag == "Pressure") { pressure = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "PolyKeyPressure") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || note < 0 || pressure < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_POLYAFTER, note, pressure); return true; } default: break; } } return false; } bool readNoteOff(MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int note = -1; int vel = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readNoteOff"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Note") { note = xml.s2().toInt(); } else if (tag == "Velocity") { vel = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "NoteOff") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || note < 0 || vel < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_NOTEOFF, note, vel); return true; } default: break; } } return false; } bool readNoteOn(MusECore::Xml& xml, MidiPlayEvent& ev, int time_ms, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0) { int chan = -1; int note = -1; int vel = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readNoteOn"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Note") { note = xml.s2().toInt(); } else if (tag == "Velocity") { vel = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "NoteOn") { if((channelRequired && chan < 0) || chan == 0 || chan > MIDNAM_MAX_CHANNEL || note < 0 || vel < 0) return false; if(chan < 0) chan = defaultChannel; else --chan; ev = MidiPlayEvent(time_ms, defaultPort, chan, ME_NOTEON, note, vel); return true; } default: break; } } return false; } //======================================================================== bool readMIDIDelay(MusECore::Xml& xml, int& delay) { int ms = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("readMIDIDelay"); break; case MusECore::Xml::Attribut: if (tag == "Milliseconds") { ms = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "MIDIDelay") { if(ms < 0) return false; delay = ms; return true; } default: break; } } return false; } // Writes element name, and channel if not zero. void writeMIDICommandElementPrefix(int level, MusECore::Xml& xml, const QString& element, int chan = 0) { xml.nput(level, "<%s ", Xml::xmlString(element).toLocal8Bit().constData()); if(chan != 0) // MidName Channel range is 1..16 xml.nput(level, "Channel=\"%d\" ", chan + 1); } void writeMIDICommand(int level, MusECore::Xml& xml, const MidiPlayEvent& ev, int cur_time_ms) { const int chan = ev.channel(); const int type = ev.type(); const int dataA = ev.dataA(); const int dataB = ev.dataB(); const int ev_time_ms = ev.time(); if(ev_time_ms > cur_time_ms) xml.put(level, "", ev_time_ms - cur_time_ms); switch(type) { case ME_NOTEON: writeMIDICommandElementPrefix(level, xml, "NoteOn", chan); xml.put(level, "Note=\"%d\" Velocity=\"%d\" />", dataA & 0x7f, dataB & 0x7f); break; case ME_NOTEOFF: writeMIDICommandElementPrefix(level, xml, "NoteOff", chan); xml.put(level, "Note=\"%d\" Velocity=\"%d\" />", dataA & 0x7f, dataB & 0x7f); break; case ME_POLYAFTER: writeMIDICommandElementPrefix(level, xml, "PolyKeyPressure", chan); xml.put(level, "Note=\"%d\" Pressure=\"%d\" />", dataA & 0x7f, dataB & 0x7f); break; case ME_CONTROLLER: switch(dataA) { case CTRL_ALL_SOUNDS_OFF: writeMIDICommandElementPrefix(level, xml, "AllSoundOff", chan); xml.put(level, " />"); break; case CTRL_RESET_ALL_CTRL: writeMIDICommandElementPrefix(level, xml, "ResetAllControllers", chan); xml.put(level, " />"); break; case CTRL_LOCAL_OFF: writeMIDICommandElementPrefix(level, xml, "LocalControl", chan); xml.put(level, "Value=\"%d\" />", dataB & 0x7f); break; case CTRL_ALL_NOTES_OFF: writeMIDICommandElementPrefix(level, xml, "AllNotesOff", chan); xml.put(level, " />"); break; case CTRL_OMNI_MODE_OFF: writeMIDICommandElementPrefix(level, xml, "OmniOff", chan); xml.put(level, " />"); break; case CTRL_OMNI_MODE_ON: writeMIDICommandElementPrefix(level, xml, "OmniOn", chan); xml.put(level, " />"); break; case CTRL_MONO_MODE_ON: writeMIDICommandElementPrefix(level, xml, "MonoMode", chan); xml.put(level, "Value=\"%d\" />", dataB & 0x7f); break; case CTRL_POLY_MODE_ON: writeMIDICommandElementPrefix(level, xml, "PolyMode", chan); xml.put(level, " />"); break; default: // 14-bit control changes: // Value is 0..16383 // ControlChange14's Control is MSB 0..31 // RPNN and NRPN are 0..16383 if (dataA < CTRL_14_OFFSET) { // 7 Bit Controller writeMIDICommandElementPrefix(level, xml, "ControlChange", chan); xml.put(level, "Control=\"%d\" Value=\"%d\" />", dataA & 0x7f, dataB & 0x7f); } else if (dataA < CTRL_RPN_OFFSET) { // 14 bit high resolution controller const int dt = (dataA >> 8) & 0x7f; // NOTE: This does not support our own separate high and low control numbers, // only standard MSB and implied LSB. Use our MSB but discard the LSB. if(dt < 0x20) { writeMIDICommandElementPrefix(level, xml, "ControlChange14", chan); xml.put(level, "Control=\"%d\" Value=\"%d\" />", dt, dataB & 0x7f); } } else if (dataA < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller writeMIDICommandElementPrefix(level, xml, "RPNChange", chan); xml.put(level, "RPN=\"%d\" Value=\"%d\" />", (((dataA & 0x7f00) >> 1) | (dataA & 0x7f)) , dataB & 0x7f); } else if (dataA < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller writeMIDICommandElementPrefix(level, xml, "NRPNChange", chan); xml.put(level, "NRPN=\"%d\" Value=\"%d\" />", (((dataA & 0x7f00) >> 1) | (dataA & 0x7f)) , dataB & 0x7f); } else if (dataA == CTRL_PITCH) { writeMIDICommandElementPrefix(level, xml, "PitchBendChange", chan); // Our MPEvent range is -8192..8191 in data B. // PitchBend's Value is 0..16383 xml.put(level, "Value=\"%d\" />", dataB + 8192); } else if (dataA == CTRL_PROGRAM) { const int hb = (dataB >> 16) & 0xff; const int lb = (dataB >> 8) & 0xff; const int pr = dataB & 0xff; if (hb != 0xff) { writeMIDICommandElementPrefix(level, xml, "ControlChange", chan); xml.put(level, "Control=\"%d\" Value=\"%d\" />", CTRL_HBANK, hb); } if (lb != 0xff) { writeMIDICommandElementPrefix(level, xml, "ControlChange", chan); xml.put(level, "Control=\"%d\" Value=\"%d\" />", CTRL_LBANK, lb); } if (pr != 0xff) { writeMIDICommandElementPrefix(level, xml, "ProgramChange", chan); xml.put(level, "Number=\"%d\" />", pr); } } else if ((dataA | 0xff) == CTRL_POLYAFTER) { writeMIDICommandElementPrefix(level, xml, "PolyKeyPressure", chan); xml.put(level, "Note=\"%d\" Pressure=\"%d\" />", dataA & 0x7f, dataB & 0x7f); } else if (dataA == CTRL_AFTERTOUCH) { writeMIDICommandElementPrefix(level, xml, "ChannelKeyPressure", chan); xml.put(level, "Pressure=\"%d\" />", dataB & 0x7f); } // UNSUPPORTED: // else if (dataA < CTRL_NRPN14_OFFSET) { // // RPN14 Controller // writeMIDICommandElementPrefix(level, xml, "ControlChange", chan); // xml.put(level, "Control=\"%d\" Value=\"%d\" />", dataA & 0x7f, dataB & 0x7f); // } // else if (dataA < CTRL_NONE_OFFSET) { // // NRPN14 Controller // writeMIDICommandElementPrefix(level, xml, "ControlChange", chan); // xml.put(level, "Control=\"%d\" Value=\"%d\" />", dataA & 0x7f, dataB & 0x7f); // } else { writeMIDICommandElementPrefix(level, xml, "ControlChange", chan); xml.put(level, "Control=\"%d\" Value=\"%d\" />", dataA & 0x7f, dataB & 0x7f); } } break; case ME_PROGRAM: writeMIDICommandElementPrefix(level, xml, "ProgramChange", chan); xml.put(level, "Number=\"%d\" />", dataA & 0x7f); break; case ME_AFTERTOUCH: writeMIDICommandElementPrefix(level, xml, "ChannelKeyPressure", chan); xml.put(level, "Pressure=\"%d\" />", dataA & 0x7f); break; case ME_PITCHBEND: writeMIDICommandElementPrefix(level, xml, "PitchBendChange", chan); // Our MPEvent range is -8192..8191 in data A. // PitchBend's Value is 0..16383 xml.put(level, "Value=\"%d\" />", dataA + 8192); break; case ME_SYSEX: { // TODO: Insert SysExDeviceID and (rare, old) SysExChannel tags. // We ignore them and do not generate them for now. int len = ev.len(); if(len > 0) { const unsigned char* data = ev.constData(); // Do we want to start it on a new line? Nah, not here... //xml.put(level, " "); //xml.nput(level, ""); xml.nput(level, " "); for (int i = 0; i < len; ++i) { if (i && ((i % 16) == 0)) { xml.nput("\n"); xml.nput(level, ""); } xml.nput("%02x ", data[i] & 0xff); } xml.nput("\n"); xml.etag(level, "SysEx"); } } break; case ME_MTC_QUARTER: // TODO: MTCQuarterFrame break; case ME_SONGPOS: xml.put(level, "", dataA & 0x3fff); break; case ME_SONGSEL: xml.put(level, "", dataA & 0x7f); break; case ME_TUNE_REQ: xml.put(level, ""); break; case ME_CLOCK: xml.put(level, ""); break; case ME_START: xml.put(level, ""); break; case ME_CONTINUE: xml.put(level, ""); break; case ME_STOP: xml.put(level, ""); break; case ME_SENSE: xml.put(level, ""); break; case ME_RESET: xml.put(level, ""); break; default: fprintf(stderr, "writeMIDICommand event type %x not supported\n", ev.type()); break; } } void MidiNamMIDICommands::write(int level, MusECore::Xml& xml) const { if(empty()) return; int time_ms = 0; xml.tag(level++, _isPatchMIDICommands ? "PatchMIDICommands" : "MIDICommands"); for(ciMPEvent i = cbegin(); i != cend(); ++i) { writeMIDICommand(level, xml, *i, time_ms); time_ms += i->time(); } xml.etag(--level, _isPatchMIDICommands ? "PatchMIDICommands" : "MIDICommands"); } bool MidiNamMIDICommands::read( MusECore::Xml& xml, bool includeSysEx, int defaultPort, bool channelRequired, int defaultChannel) { int time_ms = 0; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: { MidiPlayEvent ev; if (tag == "MIDIDelay") { int d; if(readMIDIDelay(xml, d)) time_ms += d; } else if (tag == "NoteOn") { if(readNoteOn(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "NoteOff") { if(readNoteOff(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "PolyKeyPressure") { if(readPolyKeyPressure(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "ControlChange") { if(readControlChange(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) { // If it is a bank high or low controller, // remember the values. if(ev.type() == ME_CONTROLLER) { const int ctl = ev.dataA(); const int val = ev.dataB(); if(ctl == CTRL_HBANK) { _bankH = val & 0x7f; _hasBankH = true; } if(ctl == CTRL_LBANK) { _bankL = val & 0x7f; _hasBankL = true; } } add(ev); } } else if (tag == "ProgramChange") { if(readProgramChange(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "ChannelKeyPressure") { if(readChannelKeyPressure(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "PitchBendChange") { if(readPitchBendChange(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "AllSoundOff") { if(readAllSoundOff(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "ResetAllControllers") { if(readResetAllControllers(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "LocalControl") { if(readLocalControl(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "AllNotesOff") { if(readAllNotesOff(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "OmniOff") { if(readOmniOff(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "OmniOn") { if(readOmniOn(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "MonoMode") { if(readMonoMode(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "PolyMode") { if(readPolyMode(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "ControlChange14") { if(readControlChange14(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "RPNChange") { if(readRPNChange(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "NRPNChange") { if(readNRPNChange(xml, ev, time_ms, defaultPort, channelRequired, defaultChannel)) add(ev); } else if (tag == "SysEx" && includeSysEx) { if(readSysEx(xml, ev, time_ms, defaultPort, defaultChannel)) add(ev); } // TODO //else if (tag == "MTCQuarterFrame") { // if(readMTCQuarterFrame(xml, ev, time_ms, defaultPort)) // add(ev); //} else if (tag == "SongPositionPointer") { if(readSongPositionPointer(xml, ev, time_ms, defaultPort)) add(ev); } else if (tag == "SongSelect") { if(readSongSelect(xml, ev, time_ms, defaultPort)) add(ev); } else if (tag == "TuneRequest") { if(readTuneRequest(xml, ev, time_ms, defaultPort)) add(ev); } else if (tag == "TimingClock") { if(readTimingClock(xml, ev, time_ms, defaultPort)) add(ev); } else if (tag == "Start") { if(readStart(xml, ev, time_ms, defaultPort)) add(ev); } else if (tag == "Continue") { if(readContinue(xml, ev, time_ms, defaultPort)) add(ev); } else if (tag == "Stop") { if(readStop(xml, ev, time_ms, defaultPort)) add(ev); } else if (tag == "ActiveSensing") { if(readActiveSensing(xml, ev, time_ms, defaultPort)) add(ev); } else if (tag == "SystemReset") { if(readSystemReset(xml, ev, time_ms, defaultPort)) add(ev); } else xml.unknown("readMIDICommands"); } break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "MIDICommands") { _isPatchMIDICommands = false; return true; } else if (tag == "PatchMIDICommands") { _isPatchMIDICommands = true; return true; } default: break; } } return false; } //======================================================================== void MidiNamAvailableChannel::write(int level, MusECore::Xml& xml) const { // MidNam Channel range is 1..16 xml.put(level, "", _channel + 1, _available ? "true" : "false"); } bool MidiNamAvailableChannel::read(MusECore::Xml& xml) { int chan = -1; // Default is true. bool available = true; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("MidiNamAvailableChannel"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "Available") { const QString& avail = xml.s2(); if(avail.compare("true", Qt::CaseInsensitive) == 0) available = true; else if(avail.compare("false", Qt::CaseInsensitive) == 0) available = false; } break; case MusECore::Xml::TagEnd: if (tag == "AvailableChannel") { // Channel range is 1 .. 16 if(chan <= 0 || chan > MIDNAM_MAX_CHANNEL) return false; --chan; _channel = chan; _available = available; return true; } default: break; } } return false; } //---------------------------------------------------------------- MidiNamAvailableForChannels::MidiNamAvailableForChannels(const MidiNamAvailableForChannels& m) : std::map() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidiNamAvailableChannel(*i->second)); } MidiNamAvailableForChannels::~MidiNamAvailableForChannels() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } bool MidiNamAvailableForChannels::add(MidiNamAvailableChannel* a) { return insert(MidiNamAvailableForChannelsPair(a->channel(), a)).second; } void MidiNamAvailableForChannels::write(int level, MusECore::Xml& xml) const { if(empty()) return; xml.tag(level++, "AvailableForChannels"); for(const_iterator i = cbegin(); i != cend(); ++i) i->second->write(level, xml); xml.etag(--level, "AvailableForChannels"); } void MidiNamAvailableForChannels::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "AvailableChannel") { MidiNamAvailableChannel* n = new MidiNamAvailableChannel(); if(!n->read(xml) || !add(n)) delete n; } else xml.unknown("MidiNamAvailableForChannels"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "AvailableForChannels") return; default: break; } } } //---------------------------------------------------------------- void MidiNamChannelNameSetAssign::write(int level, MusECore::Xml& xml) const { // MidNam Channel range is 1..16 xml.put(level, "", _channel + 1, Xml::xmlString(_name).toLocal8Bit().constData()); } bool MidiNamChannelNameSetAssign::read(MusECore::Xml& xml) { int chan = -1; QString nameset; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("MidiNamChannelNameSetAssign"); break; case MusECore::Xml::Attribut: if (tag == "Channel") { chan = xml.s2().toInt(); } else if (tag == "NameSet") { nameset = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "ChannelNameSetAssign") { // Channel range is 1 .. 16 if(chan <= 0 || chan > MIDNAM_MAX_CHANNEL || nameset.isEmpty()) return false; --chan; _channel = chan; _name = nameset; return true; } default: break; } } return false; } bool MidiNamChannelNameSetAssign::gatherReferences(MidNamReferencesList* refs) const { return refs->channelNameSetAssignObjs.add(const_cast(this)); } const MidiNamPatch* MidiNamChannelNameSetAssign::findPatch(int channel, int patch) const { // Go directly to the reference object. if(!_p_ref) return nullptr; return _p_ref->findPatch(channel, patch); } const MidiNamPatchBankList* MidiNamChannelNameSetAssign::getPatchBanks(int channel) const { // Go directly to the reference object. if(!_p_ref) return nullptr; return _p_ref->getPatchBanks(channel); } const MidiControllerList* MidiNamChannelNameSetAssign::getControllers(int channel, int patch) const { // Go directly to the reference object. if(!_p_ref) return nullptr; return _p_ref->getControllers(channel, patch); } bool MidiNamChannelNameSetAssign::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; // Go directly to the reference object. if(_p_ref) return _p_ref->getNoteSampleName(drum, channel, patch, note, name); return false; } //---------------------------------------------------------------- MidiNamChannelNameSetAssignments::MidiNamChannelNameSetAssignments(const MidiNamChannelNameSetAssignments& m) : std::map() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidiNamChannelNameSetAssign(*i->second)); _hasChannelNameSetAssignments = m._hasChannelNameSetAssignments; } MidiNamChannelNameSetAssignments::~MidiNamChannelNameSetAssignments() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } bool MidiNamChannelNameSetAssignments::add(MidiNamChannelNameSetAssign* a) { return insert(MidiNamChannelNameSetAssignmentsPair(a->channel(), a)).second; } void MidiNamChannelNameSetAssignments::write(int level, MusECore::Xml& xml) const { if(empty()) return; xml.tag(level++, "ChannelNameSetAssignments"); for(const_iterator i = cbegin(); i != cend(); ++i) i->second->write(level, xml); xml.etag(--level, "ChannelNameSetAssignments"); } void MidiNamChannelNameSetAssignments::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "ChannelNameSetAssign") { MidiNamChannelNameSetAssign* n = new MidiNamChannelNameSetAssign(); if(!n->read(xml) || !add(n)) delete n; } else xml.unknown("MidiNamChannelNameSetAssignments"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "ChannelNameSetAssignments") { _hasChannelNameSetAssignments = true; return; } default: break; } } } bool MidiNamChannelNameSetAssignments::gatherReferences(MidNamReferencesList* refs) const { for(const_iterator i = cbegin(); i != cend(); ++i) i->second->gatherReferences(refs); return true; } const MidiNamPatch* MidiNamChannelNameSetAssignments::findPatch(int channel, int patch) const { if(!_hasChannelNameSetAssignments) return nullptr; const_iterator ia = find(channel); if(ia == cend()) return nullptr; const MidiNamChannelNameSetAssign* cns = ia->second; return cns->findPatch(channel, patch); } const MidiNamPatchBankList* MidiNamChannelNameSetAssignments::getPatchBanks(int channel) const { if(!_hasChannelNameSetAssignments) return nullptr; const_iterator ia = find(channel); if(ia == cend()) return nullptr; const MidiNamChannelNameSetAssign* cns = ia->second; return cns->getPatchBanks(channel); } const MidiControllerList* MidiNamChannelNameSetAssignments::getControllers(int channel, int patch) const { if(!_hasChannelNameSetAssignments) return nullptr; const_iterator ia = find(channel); if(ia == cend()) return nullptr; const MidiNamChannelNameSetAssign* cns = ia->second; return cns->getControllers(channel, patch); } bool MidiNamChannelNameSetAssignments::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; if(_hasChannelNameSetAssignments) { const_iterator ia = find(channel); if(ia != cend()) { const MidiNamChannelNameSetAssign* cns = ia->second; return cns->getNoteSampleName(drum, channel, patch, note, name); } } return false; } //---------------------------------------------------------------- void MidiNamNote::write(int level, MusECore::Xml& xml) const { xml.put(level, "", _number, Xml::xmlString(_name).toLocal8Bit().constData()); } bool MidiNamNote::read(MusECore::Xml& xml) { int number = -1; QString name; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("MidiNamNote"); break; case MusECore::Xml::Attribut: if (tag == "Number") { number = xml.s2().toInt(); } else if (tag == "Name") { name = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "Note") { if(number < 0 || name.isEmpty()) return false; _number = number; _name = name; return true; } default: break; } } return false; } //---------------------------------------------------------------- MidiNamNotes::MidiNamNotes(const MidiNamNotes& m) : std::map() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidiNamNote(*i->second)); _noteGroups = m._noteGroups; } MidiNamNotes::~MidiNamNotes() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } bool MidiNamNotes::add(MidiNamNote* a) { return insert(MidiNamNotesPair(a->number(), a)).second; } bool MidiNamNotes::addNoteGroup(MidiNamNoteGroup* a) { return _noteGroups.add(a); } void MidiNamNotes::write(int level, MusECore::Xml& xml) const { _noteGroups.write(level, xml, this); for(const_iterator i = cbegin(); i != cend(); ++i) { // If the note is part of a group, the group will take care of writing it instead. const int note = i->second->number(); ciMidiNamNoteGroups ig = _noteGroups.cbegin(); for( ; ig != _noteGroups.cend(); ++ig) { if(ig->second->find(note) != ig->second->cend()) break; } if(ig != _noteGroups.cend()) continue; i->second->write(level, xml); } } bool MidiNamNotes::getNoteSampleName( bool /*drum*/, int /*channel*/, int /*patch*/, int note, QString* name) const { if(!name) return false; const_iterator i = find(note); if(i == cend()) { // It has a note name list but no note was found. *name = QString(); return true; } *name = i->second->name(); return true; } //---------------------------------------------------------------- void MidiNamNoteGroup::write(int level, MusECore::Xml& xml, const MidiNamNotes* notes) const { xml.tag(level++, "NoteGroup Name=\"%s\"", Xml::xmlString(_name).toLocal8Bit().constData()); for(const_iterator i = cbegin(); i != cend(); ++i) { // Write only the notes that are part of the group. ciMidiNamNotes in = notes->find(*i); if(in == notes->cend()) continue; in->second->write(level, xml); } xml.etag(--level, "NoteGroup"); } void MidiNamNoteGroup::read(MusECore::Xml& xml, MidiNamNotes* notes) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "Note") { MidiNamNote* n = new MidiNamNote(); if(n->read(xml)) { if(notes->add(n)) insert(n->number()); else delete n; } else { delete n; } } else xml.unknown("MidiNamNoteGroup"); break; case MusECore::Xml::Attribut: if (tag == "Name") { _name = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "NoteGroup") return; default: break; } } } //---------------------------------------------------------------- MidiNamNoteGroups::MidiNamNoteGroups(const MidiNamNoteGroups& m) : std::multimap() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidiNamNoteGroup(*i->second)); } MidiNamNoteGroups& MidiNamNoteGroups::operator=(const MidiNamNoteGroups& m) { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } clear(); for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidiNamNoteGroup(*i->second)); return *this; } MidiNamNoteGroups::~MidiNamNoteGroups() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } bool MidiNamNoteGroups::add(MidiNamNoteGroup* a) { insert(MidiNamNoteGroupsPair(a->name(), a)); return true; } void MidiNamNoteGroups::write(int level, MusECore::Xml& xml, const MidiNamNotes* notes) const { for(const_iterator i = cbegin(); i != cend(); ++i) i->second->write(level, xml, notes); } //---------------------------------------------------------------- void MidNamNoteNameList::write(int level, MusECore::Xml& xml) const { if(isReference()) { xml.put(level, "", Xml::xmlString(_name).toLocal8Bit().constData()); } else { xml.tag(level++, "NoteNameList Name=\"%s\"", Xml::xmlString(_name).toLocal8Bit().constData()); _noteList.write(level, xml); xml.etag(--level, "NoteNameList"); } } bool MidNamNoteNameList::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "NoteGroup") { MidiNamNoteGroup* n = new MidiNamNoteGroup(); n->read(xml, &_noteList); _noteList.addNoteGroup(n); } else if (tag == "Note") { MidiNamNote* n = new MidiNamNote(); if(!n->read(xml) || !_noteList.add(n)) delete n; } else xml.unknown("MidNamNoteNameList"); break; case MusECore::Xml::Attribut: if (tag == "Name") { _name = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "NoteNameList") { _isReference = false; // This is an actual note name list, // regardless if it is empty. _hasNoteNameList = true; return true; } else if (tag == "UsesNoteNameList") { _isReference = true; return true; } default: break; } } return false; } bool MidNamNoteNameList::gatherReferences(MidNamReferencesList* refs) const { return refs->noteNameListObjs.add(const_cast(this)); } bool MidNamNoteNameList::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; const MidNamNoteNameList* l = objectOrRef(); if(!l->hasNoteNameList()) return false; return l->noteList().getNoteSampleName(drum, channel, patch, note, name); } //---------------------------------------------------------------- void MidiNamVal::write(int level, MusECore::Xml& xml) const { xml.put(level, "", _number, Xml::xmlString(_name).toLocal8Bit().constData()); } bool MidiNamVal::read(MusECore::Xml& xml) { int number = -1; QString name; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("MidiNamVal"); break; case MusECore::Xml::Attribut: if (tag == "Number") { number = xml.s2().toInt(); } else if (tag == "Name") { name = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "Value") { if(number < 0 || name.isEmpty()) return false; _number = number; _name = name; return true; } default: break; } } return false; } //---------------------------------------------------------------- MidiNamValNames::MidiNamValNames(const MidiNamValNames& m) : std::map() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidiNamVal(*i->second)); _name = m._name; _p_ref = m._p_ref; _isReference = m._isReference; } MidiNamValNames& MidiNamValNames::operator=(const MidiNamValNames& m) { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } clear(); for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidiNamVal(*i->second)); _name = m._name; _p_ref = m._p_ref; _isReference = m._isReference; return *this; } MidiNamValNames::~MidiNamValNames() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } bool MidiNamValNames::add(MidiNamVal* a) { return insert(MidiNamValNamesPair(a->number(), a)).second; } void MidiNamValNames::write(int level, MusECore::Xml& xml) const { if(isReference()) { xml.put(level, "", Xml::xmlString(_name).toLocal8Bit().constData()); } else { xml.tag(level++, "ValueNameList Name=\"%s\"", Xml::xmlString(_name).toLocal8Bit().constData()); for(const_iterator i = cbegin(); i != cend(); ++i) i->second->write(level, xml); xml.etag(--level, "ValueNameList"); } } void MidiNamValNames::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "Value") { MidiNamVal* n = new MidiNamVal(); if(!n->read(xml) || !add(n)) delete n; } else xml.unknown("MidiNamValNames"); break; case MusECore::Xml::Attribut: if (tag == "Name") { _name = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "ValueNameList") { _isReference = false; return; } else if (tag == "UsesValueNameList") { _isReference = true; return; } default: break; } } } bool MidiNamValNames::gatherReferences(MidNamReferencesList* refs) const { return refs->valNamesObjs.add(const_cast(this)); } //---------------------------------------------------------------- void MidiNamValues::write(int level, MusECore::Xml& xml) const { xml.nput(level, ""); } else { xml.put(level++, " >"); _valueNames.write(level, xml); xml.etag(--level, "Values"); } } bool MidiNamValues::read(MusECore::Xml& xml) { bool have_min = false; bool have_max = false; int min = 0; int max = 0; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "ValueNameList") _valueNames.read(xml); else if (tag == "UsesValueNameList") _valueNames.read(xml); else xml.unknown("MidiNamValues"); break; case MusECore::Xml::Attribut: if (tag == "Min") { have_min = true; min = xml.s2().toInt(); } else if (tag == "Max") { have_max = true; max = xml.s2().toInt(); } else if (tag == "Default") { _default = xml.s2().toInt(); } else if (tag == "Units") { _units = xml.s2().toInt(); } else if (tag == "Mapping") { _mapping = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "Values") { if(!have_min || !have_max) return false; _min = min; _max = max; return true; } default: break; } } return false; } //---------------------------------------------------------------- void MidiNamCtrl::writeMidnam(int level, MusECore::Xml& xml) const { const char* type_str = "7bit"; // Strip off the type. int number = _num & 0xffff; switch(midiControllerType(_num)) { case MidiController::Controller7: type_str = "7bit"; number &= 0x7f; break; case MidiController::Controller14: type_str = "14bit"; // "ControlChange14's Control is MSB 0..31" // We want our high number here. number = (number >> 8) & 0x7f; break; case MidiController::RPN: type_str = "RPN"; // "RPNN and NRPN are 0..16383" number = ((number & 0x7f00) >> 1) | (number & 0x7f); break; case MidiController::NRPN: type_str = "NRPN"; number = ((number & 0x7f00) >> 1) | (number & 0x7f); break; default: return; } xml.nput(level, ""); } else { xml.put(level++, " >"); _values.write(level, xml); xml.etag(--level, "Control"); } } bool MidiNamCtrl::readMidnam(MusECore::Xml& xml) { int type_offset = CTRL_7_OFFSET; int number = -1; QString name; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "Values") { MidiNamValues n; if(n.read(xml)) _values = n; } else xml.unknown("MidiNamCtrl"); break; case MusECore::Xml::Attribut: if (tag == "Type") { const QString& type_txt = xml.s2(); if(type_txt.compare("7bit", Qt::CaseInsensitive) == 0) type_offset = CTRL_7_OFFSET; else if(type_txt.compare("14bit", Qt::CaseInsensitive) == 0) type_offset = CTRL_14_OFFSET; else if(type_txt.compare("RPN", Qt::CaseInsensitive) == 0) type_offset = CTRL_RPN_OFFSET; else if(type_txt.compare("NRPN", Qt::CaseInsensitive) == 0) type_offset = CTRL_NRPN_OFFSET; } else if (tag == "Number") { number = xml.s2().toInt(); } else if (tag == "Name") { name = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "Control") { if(number < 0 || name.isEmpty()) return false; if(type_offset == CTRL_14_OFFSET) { if(number > 0x1f) return false; // Synthesize our high and low numbers. number = (number << 8) | (number + 0x20); } else if(type_offset == CTRL_RPN_OFFSET || type_offset == CTRL_NRPN_OFFSET) { if(number > 0x3fff) return false; // Synthesize our high and low numbers. number = ((number & 0x3f80) << 1) | (number & 0x7f); } else { if(number > 0x7f) return false; } _num = type_offset | number; _name = name; _minVal = _values.minVal(); _maxVal = _values.maxVal(); _initVal = _drumInitVal = _values.defaultVal(); updateBias(); return true; } default: break; } } return false; } //---------------------------------------------------------------- MidiNamCtrls::MidiNamCtrls(const MidiNamCtrls& mcl) : MidiControllerList() { for(const_iterator i = mcl.cbegin(); i != mcl.cend(); ++i) { MidiNamCtrl* mc = static_cast(i->second); add(new MidiNamCtrl(*mc)); } update_RPN_Ctrls_Reserved(); } MidiNamCtrls::~MidiNamCtrls() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } void MidiNamCtrls::writeMidnam(int level, MusECore::Xml& xml) const { if(isReference()) { xml.put(level, "", Xml::xmlString(_name).toLocal8Bit().constData()); } else { xml.tag(level++, "ControlNameList Name=\"%s\"", Xml::xmlString(_name).toLocal8Bit().constData()); for(const_iterator i = cbegin(); i != cend(); ++i) static_cast(i->second)->writeMidnam(level, xml); xml.etag(--level, "ControlNameList"); } } void MidiNamCtrls::readMidnam(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "Control") { MidiNamCtrl* ctrl = new MidiNamCtrl(); if(!ctrl->readMidnam(xml) || !add(ctrl)) delete ctrl; } else xml.unknown("MidiNamCtrls"); break; case MusECore::Xml::Attribut: if (tag == "Name") { _name = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "ControlNameList") { _isReference = false; // This is an actual control name list, // regardless if it is empty. _hasMidiNamCtrls = true; return; } else if (tag == "UsesControlNameList") { _isReference = true; return; } default: break; } } } bool MidiNamCtrls::gatherReferences(MidNamReferencesList* refs) const { return refs->ctrlsObjs.add(const_cast(this)); } const MidiControllerList* MidiNamCtrls::getControllers(int /*channel*/, int /*patch*/) const { const MidiNamCtrls* ctrls = objectOrRef(); if(ctrls && ctrls->hasMidiNamCtrls()) return ctrls; return nullptr; } //----------------------------------------------------------- void MidiNamPatch::write(int level, MusECore::Xml& xml) const { xml.nput(level, ""); } else { xml.put(level++, " >"); _patchMIDICommands.write(level, xml); _channelNameSetAssignments.write(level, xml); _noteNameList.write(level, xml); _controlNameList.writeMidnam(level, xml); xml.etag(--level, "Patch"); } } bool MidiNamPatch::read(MusECore::Xml& xml) { QString number; QString name; // Start with what we've got, ie. say from the ctor. int patch_num = _patchNumber; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "PatchMIDICommands") { if(_patchMIDICommands.read(xml, false)) { if(_patchMIDICommands.hasBankH()) patch_num |= (patch_num & 0x00ffff) | (_patchMIDICommands.bankH() << 16); if(_patchMIDICommands.hasBankL()) patch_num |= (patch_num & 0xff00ff) | (_patchMIDICommands.bankL() << 8); } } else if (tag == "ChannelNameSetAssignments") _channelNameSetAssignments.read(xml); else if (tag == "UsesNoteNameList") _noteNameList.read(xml); else if (tag == "NoteNameList") _noteNameList.read(xml); else if (tag == "UsesControlNameList") _controlNameList.readMidnam(xml); else if (tag == "ControlNameList") _controlNameList.readMidnam(xml); else xml.unknown("MidiNamPatch"); break; case MusECore::Xml::Attribut: if (tag == "Number") { number = xml.s2(); } else if (tag == "Name") { name = xml.s2(); } else if (tag == "ProgramChange") { // Set high and low bank to 'unknown' (default) at first. patch_num = 0xffff00 | (xml.s2().toInt() & 0x7f); } break; case MusECore::Xml::TagEnd: if (tag == "Patch") { if(number.isEmpty() || name.isEmpty() // # IMPLIED /*|| patch_num < 0*/) return false; _number = number; _name = name; _patchNumber = patch_num; return true; } default: break; } } return false; } bool MidiNamPatch::gatherReferences(MidNamReferencesList* refs) const { _channelNameSetAssignments.gatherReferences(refs); _noteNameList.gatherReferences(refs); _controlNameList.gatherReferences(refs); return true; } const MidiControllerList* MidiNamPatch::getControllers(int channel, int patch) const { if(_channelNameSetAssignments.hasChannelNameSetAssignments()) { return _channelNameSetAssignments.getControllers(channel, patch); } else { return controlNameList().getControllers(channel, patch); } } bool MidiNamPatch::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; if(_channelNameSetAssignments.hasChannelNameSetAssignments()) { return _channelNameSetAssignments.getNoteSampleName(drum, channel, patch, note, name); } else { return noteNameList().getNoteSampleName(drum, channel, patch, note, name); } } //---------------------------------------------------------------- MidiNamPatchNameList::MidiNamPatchNameList(const MidiNamPatchNameList& m) : std::map() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidiNamPatch(*i->second)); _name = m._name; _p_ref = m._p_ref; _isReference = m._isReference; } MidiNamPatchNameList::~MidiNamPatchNameList() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } bool MidiNamPatchNameList::add(MidiNamPatch* a) { return insert(MidiNamPatchNameListPair(a->patchNumber(), a)).second; } void MidiNamPatchNameList::write(int level, MusECore::Xml& xml) const { if(isReference()) { xml.put(level, "", Xml::xmlString(_name).toLocal8Bit().constData()); } else { xml.tag(level++, "PatchNameList Name=\"%s\"", Xml::xmlString(_name).toLocal8Bit().constData()); for(const_iterator i = cbegin(); i != cend(); ++i) i->second->write(level, xml); xml.etag(--level, "PatchNameList"); } } void MidiNamPatchNameList::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "Patch") { MidiNamPatch* n = new MidiNamPatch(); if(!n->read(xml) || !add(n)) delete n; } else xml.unknown("MidiNamPatchNameList"); break; case MusECore::Xml::Attribut: if (tag == "Name") { _name = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "PatchNameList") { _isReference = false; return; } else if (tag == "UsesPatchNameList") { _isReference = true; return; } default: break; } } } bool MidiNamPatchNameList::gatherReferences(MidNamReferencesList* refs) const { for(const_iterator i = cbegin(); i != cend(); ++i) i->second->gatherReferences(refs); return refs->patchNameListObjs.add(const_cast(this)); } const MidiNamPatch* MidiNamPatchNameList::findPatch(int patch, int /*bankHL*/) const { const MidiNamPatchNameList* n = objectOrRef(); const_iterator i; // Are we looking for a grand default patch? if(patch == CTRL_VAL_UNKNOWN) { i = n->find(CTRL_PROGRAM_VAL_DONT_CARE); } else { // Try looking for the verbose patch number. i = n->find(patch); if(i == n->cend()) { // Verbose patch number was not found. Try looking for defaults... const int bankH = (patch >> 16) & 0xff; const int bankL = (patch >> 8) & 0xff; const int prog = patch & 0xff; // See if there are defaults for each individual member... if(bankH != 0xff) i = n->find(patch | 0xff0000); if(i == n->cend() && bankL != 0xff) i = n->find(patch | 0xff00); if(i == n->cend() && prog != 0xff) i = n->find(patch | 0xff); // See if there are defaults for combinations of individual members: if(i == n->cend() && bankH != 0xff && bankL != 0xff) i = n->find(patch | 0xffff00); if(i == n->cend() && bankH != 0xff && prog != 0xff) i = n->find(patch | 0xff00ff); if(i == n->cend() && bankL != 0xff && prog != 0xff) i = n->find(patch | 0xffff); // And finally, see if there's a grand default patch. if(i == n->cend()) i = n->find(CTRL_PROGRAM_VAL_DONT_CARE); } } if(i == n->cend()) return nullptr; return i->second; } bool MidiNamPatchNameList::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name, int bankHL) const { if(!name) return false; // REMOVE Tim. midnam. // FIXME: This may not be right. // It is possible there may be only a bankH in the patch bank and // only a bankL in each patch. Need to let the patch/patchlist // sort of, kind of, search first. Test later with real midnam files. // const_iterator i; // // // Are we looking for a grand default patch? // if(patch == CTRL_VAL_UNKNOWN) // { // i = find(CTRL_PROGRAM_VAL_DONT_CARE); // } // else // { // // Try looking for the verbose patch number. // i = find(patch); // if(i == cend()) // { // // Verbose patch number was not found. Try looking for defaults... // const int bankH = (patch >> 16) & 0xff; // const int bankL = (patch >> 8) & 0xff; // const int prog = patch & 0xff; // // // See if there are defaults for each individual member... // if(bankH != 0xff) // i = find(patch | 0xff0000); // else if(bankL != 0xff) // i = find(patch | 0xff00); // else if(prog != 0xff) // i = find(patch | 0xff); // // See if there are defaults for combinations of individual members: // else if(bankH != 0xff && bankL != 0xff) // i = find(patch | 0xffff00); // else if(bankH != 0xff && prog != 0xff) // i = find(patch | 0xff00ff); // else if(bankL != 0xff && prog != 0xff) // i = find(patch | 0xffff); // // And finally, see if there's a grand default patch. // else // i = find(CTRL_PROGRAM_VAL_DONT_CARE); // } // } // // if(i == cend()) // return false; // // const MidiNamPatch& mp = i->second; // return mp.getNoteSampleName(drum, channel, patch, note, name); const MidiNamPatch* mp = findPatch(patch, bankHL); if(!mp) return false; return mp->getNoteSampleName(drum, channel, patch, note, name); } //---------------------------------------------------------------- void MidiNamPatchBank::write(int level, MusECore::Xml& xml) const { xml.nput(level, ""); } else { xml.put(level++, " >"); _MIDICommands.write(level, xml); _patchNameList.write(level, xml); xml.etag(--level, "PatchBank"); } } bool MidiNamPatchBank::read(MusECore::Xml& xml) { QString name; bool ROM = false; // Set bank to 'unknown' (default) at first. int bankHL = 0xffff; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "MIDICommands") { if(_MIDICommands.read(xml, true)) bankHL = (_MIDICommands.bankH() << 8) | _MIDICommands.bankL(); } else if (tag == "UsesPatchNameList") _patchNameList.read(xml); else if (tag == "PatchNameList") _patchNameList.read(xml); else xml.unknown("MidiNamPatchBank"); break; case MusECore::Xml::Attribut: if (tag == "Name") { name = xml.s2(); } else if (tag == "ROM") { ROM = xml.s2().toInt(); const QString& rom_s = xml.s2(); if(rom_s.compare("true", Qt::CaseInsensitive) == 0) ROM = true; else if(rom_s.compare("false", Qt::CaseInsensitive) == 0) ROM = false; } break; case MusECore::Xml::TagEnd: if (tag == "PatchBank") { // #IMPLIED //if(name.isEmpty()) // return false; _name = name; _ROM = ROM; _bankHL = bankHL; return true; } default: break; } } return false; } bool MidiNamPatchBank::gatherReferences(MidNamReferencesList* refs) const { return _patchNameList.gatherReferences(refs); } const MidiNamPatch* MidiNamPatchBank::findPatch(int patch) const { return _patchNameList.findPatch(patch); } bool MidiNamPatchBank::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; // REMOVE Tim. midnam. // FIXME: This may not be right. // It is possible there may be only a bankH in the patch bank and // only a bankL in each patch. Need to let the patch/patchlist // sort of, kind of, search first. Test later with real midnam files. // const bool has_bankH = _MIDICommands.hasBankH(); // const bool has_bankL = _MIDICommands.hasBankL(); // // int fin_patch = patch; // // // Does the bank have its own bank number midi commands? // if(has_bankH || has_bankL) // { // int patch_bank_hl; // // Are we looking for a grand default patch? // if(patch == CTRL_VAL_UNKNOWN) // { // patch_bank_hl = 0xffff; // fin_patch = CTRL_PROGRAM_VAL_DONT_CARE; // } // else // { // patch_bank_hl = (patch >> 8) & 0xffff; // } // // // // If it is not the bank number we're looking for, return. // if(patch_bank_hl != _bankHL) // return false; // // Set the bank high and low to 'unknown' (default) so that the _patchNameList can find it. // // Patches can have their own midi controller commands, but if there are bank controller commands, // // in this case that would interfere with the bank number. So likely if patches have their // // own controller commands, they should not have bank controller commands, and therefore // // should all match on bank == 0xffff. // fin_patch |= 0xffff00; // } // // return _patchNameList.getNoteSampleName(drum, channel, fin_patch, note, name); return _patchNameList.getNoteSampleName(drum, channel, patch, note, name, _bankHL); } //---------------------------------------------------------------- MidiNamPatchBankList::MidiNamPatchBankList(const MidiNamPatchBankList& m) : std::map() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidiNamPatchBank(*i->second)); } MidiNamPatchBankList::~MidiNamPatchBankList() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } bool MidiNamPatchBankList::add(MidiNamPatchBank* a) { return insert(MidiNamPatchBankListPair(a->bankHL(), a)).second; } void MidiNamPatchBankList::write(int level, MusECore::Xml& xml) const { for(const_iterator i = cbegin(); i != cend(); ++i) i->second->write(level, xml); } bool MidiNamPatchBankList::gatherReferences(MidNamReferencesList* refs) const { for(const_iterator i = cbegin(); i != cend(); ++i) i->second->gatherReferences(refs); return true; } const MidiNamPatch* MidiNamPatchBankList::findPatch(int patch) const { const_iterator i; // Are we looking for a grand default patch? if(patch == CTRL_VAL_UNKNOWN) { // See if this bank has a grand default bank. i = find(0xffff); } else { // Try looking for the verbose bank number. const int patch_bankHL = (patch >> 8) & 0xffff; i = find(patch_bankHL); if(i == cend()) { // Verbose bank number was not found. Try looking for defaults... const int bankH = (patch_bankHL >> 8) & 0xff; const int bankL = patch_bankHL & 0xff; // See if there are defaults for each individual member... if(bankH != 0xff) i = find(patch_bankHL | 0xff00); else if(bankL != 0xff) i = find(patch_bankHL | 0xff); // And finally, see if there's a grand default bank. else i = find(0xffff); } } if(i == cend()) return nullptr; return i->second->findPatch(patch); } bool MidiNamPatchBankList::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; const MidiNamPatch* p = findPatch(patch); if(!p) return false; return p->getNoteSampleName(drum, channel, patch, note, name); } //---------------------------------------------------------------- void MidNamChannelNameSet::write(int level, MusECore::Xml& xml) const { xml.nput(level, ""); } else { xml.put(level++, " >"); _availableForChannels.write(level, xml); _noteNameList.write(level, xml); _controlNameList.writeMidnam(level, xml); _patchBankList.write(level, xml); xml.etag(--level, "ChannelNameSet"); } } bool MidNamChannelNameSet::read(MusECore::Xml& xml) { QString name; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "AvailableForChannels") _availableForChannels.read(xml); else if (tag == "PatchBank") { MidiNamPatchBank* n = new MidiNamPatchBank(); if(!n->read(xml) || !_patchBankList.add(n)) delete n; } else if (tag == "UsesNoteNameList") _noteNameList.read(xml); else if (tag == "NoteNameList") _noteNameList.read(xml); else if (tag == "UsesControlNameList") _controlNameList.readMidnam(xml); else if (tag == "ControlNameList") _controlNameList.readMidnam(xml); else xml.unknown("MidNamChannelNameSet"); break; case MusECore::Xml::Attribut: if (tag == "Name") { name = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "ChannelNameSet") { if(name.isEmpty()) return false; _name = name; return true; } default: break; } } return false; } bool MidNamChannelNameSet::gatherReferences(MidNamReferencesList* refs) const { _noteNameList.gatherReferences(refs); _controlNameList.gatherReferences(refs); _patchBankList.gatherReferences(refs); return refs->channelNameSetObjs.add(const_cast(this)); } const MidiNamPatch* MidNamChannelNameSet::findPatch(int channel, int patch) const { if(_availableForChannels.find(channel) == _availableForChannels.end()) return nullptr; return _patchBankList.findPatch(patch); } const MidiNamPatchBankList* MidNamChannelNameSet::getPatchBanks(int channel) const { if(_availableForChannels.find(channel) == _availableForChannels.end()) return nullptr; return &_patchBankList; } const MidiControllerList* MidNamChannelNameSet::getControllers(int channel, int patch) const { const MidiNamPatch* p = findPatch(channel, patch); if(p) { const MidiControllerList* mcl = p->getControllers(channel, patch); if(mcl) return mcl; } return _controlNameList.getControllers(channel, patch); } bool MidNamChannelNameSet::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; if(_availableForChannels.find(channel) == _availableForChannels.end()) return false; // FIXME Whether to return or not... Need an enum return value. if(_patchBankList.getNoteSampleName(drum, channel, patch, note, name)) return true; return _noteNameList.getNoteSampleName(drum, channel, patch, note, name); } //---------------------------------------------------------------- MidiNamChannelNameSetList::MidiNamChannelNameSetList(const MidiNamChannelNameSetList& m) : std::map() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidNamChannelNameSet(*i->second)); } MidiNamChannelNameSetList::~MidiNamChannelNameSetList() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } bool MidiNamChannelNameSetList::add(MidNamChannelNameSet* a) { return insert(MidiNamChannelNameSetListPair(a->name(), a)).second; } void MidiNamChannelNameSetList::write(int level, MusECore::Xml& xml) const { for(const_iterator i = cbegin(); i != cend(); ++i) i->second->write(level, xml); } bool MidiNamChannelNameSetList::gatherReferences(MidNamReferencesList* refs) const { for(const_iterator i = cbegin(); i != cend(); ++i) i->second->gatherReferences(refs); return true; } const MidiNamPatch* MidiNamChannelNameSetList::findPatch(int channel, int patch) const { for(const_iterator i = cbegin(); i != cend(); ++i) { const MidiNamPatch* p = i->second->findPatch(channel, patch); if(p) return p; } return nullptr; } const MidiNamPatchBankList* MidiNamChannelNameSetList::getPatchBanks(int channel) const { for(const_iterator i = cbegin(); i != cend(); ++i) { const MidiNamPatchBankList* p = i->second->getPatchBanks(channel); if(p) return p; } return nullptr; } const MidiControllerList* MidiNamChannelNameSetList::getControllers(int channel, int patch) const { const MidiNamPatch* p = findPatch(channel, patch); if(!p) return nullptr; return p->getControllers(channel, patch); } bool MidiNamChannelNameSetList::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; for(const_iterator i = cbegin(); i != cend(); ++i) { // FIXME Whether to return or not... Need an enum return value. if(i->second->getNoteSampleName(drum, channel, patch, note, name)) return true; } return false; } //---------------------------------------------------------------- void MidNamDeviceModeEnable::write(int level, MusECore::Xml& xml) const { if(_MIDICommands.empty()) return; xml.tag(level++, "DeviceModeEnable"); _MIDICommands.write(level, xml); xml.etag(--level, "DeviceModeEnable"); } bool MidNamDeviceModeEnable::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "MIDICommands") _MIDICommands.read(xml, true); else xml.unknown("MidNamDeviceModeEnable"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "DeviceModeEnable") return true; default: break; } } return false; } //---------------------------------------------------------------- void MidNamDeviceModeDisable::write(int level, MusECore::Xml& xml) const { if(_MIDICommands.empty()) return; xml.tag(level++, "DeviceModeDisable"); _MIDICommands.write(level, xml); xml.etag(--level, "DeviceModeDisable"); } bool MidNamDeviceModeDisable::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "MIDICommands") _MIDICommands.read(xml, true); else xml.unknown("MidNamDeviceModeDisable"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "DeviceModeDisable") return true; default: break; } } return false; } //---------------------------------------------------------------- void MidNamNameList::write(int level, MusECore::Xml& xml) const { _patchNameList.write(level, xml); _noteNameList.write(level, xml); _controlNameList.writeMidnam(level, xml); _valueNameList.write(level, xml); } bool MidNamNameList::read(MusECore::Xml& xml) { const QString& tag(xml.s1()); if (tag == "PatchNameList") _patchNameList.read(xml); else if (tag == "NoteNameList") _noteNameList.read(xml); else if (tag == "ControlNameList") _controlNameList.readMidnam(xml); else if (tag == "ValueNameList") _valueNameList.read(xml); else return false; return true; } bool MidNamNameList::gatherReferences(MidNamReferencesList* refs) const { _patchNameList.gatherReferences(refs); _noteNameList.gatherReferences(refs); _controlNameList.gatherReferences(refs); _valueNameList.gatherReferences(refs); return true; } //---------------------------------------------------------------- void MidNamDeviceMode::write(int level, MusECore::Xml& xml) const { if(isReference()) { xml.put(level, "", Xml::xmlString(_name).toLocal8Bit().constData()); } else { xml.nput(level, _isCustomDeviceMode ? ""); } else { xml.put(level++, " >"); _deviceModeEnable.write(level, xml); _deviceModeDisable.write(level, xml); _channelNameSetAssignments.write(level, xml); if(!_isCustomDeviceMode) _channelNameSetList.write(level, xml); _nameList.write(level, xml); xml.etag(--level, _isCustomDeviceMode ? "CustomDeviceMode" : "StandardDeviceMode"); } } } bool MidNamDeviceMode::read(MusECore::Xml& xml) { QString name; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "DeviceModeEnable") _deviceModeEnable.read(xml); else if (tag == "DeviceModeDisable") _deviceModeDisable.read(xml); else if (tag == "ChannelNameSetAssignments") _channelNameSetAssignments.read(xml); else if (tag == "ChannelNameSet") { MidNamChannelNameSet* n = new MidNamChannelNameSet(); if(!n->read(xml) || !_channelNameSetList.add(n)) delete n; } // Special, different from other reads: // For convenience, hand the tag off to // the NameList to check for several names. // Returns true if recognized and processed. else if (!_nameList.read(xml)) xml.unknown("MidNamDeviceMode"); break; case MusECore::Xml::Attribut: if (tag == "Name") { name = xml.s2(); } break; case MusECore::Xml::TagEnd: if (tag == "StandardDeviceMode") { if(name.isEmpty()) return false; _name = name; _isCustomDeviceMode = false; // Point it to this, it's not a reference. _p_ref = this; return true; } else if (tag == "CustomDeviceMode") { if(name.isEmpty()) return false; _name = name; _isCustomDeviceMode = true; _isReference = false; return true; } else if (tag == "SupportsStandardDeviceMode") { if(name.isEmpty()) return false; _name = name; _isCustomDeviceMode = false; _isReference = true; return true; } default: break; } } return false; } bool MidNamDeviceMode::gatherReferences(MidNamReferencesList* refs) const { _channelNameSetAssignments.gatherReferences(refs); _nameList.gatherReferences(refs); _channelNameSetList.gatherReferences(refs); return refs->deviceModeObjs.add(const_cast(this)); } const MidiNamPatch* MidNamDeviceMode::findPatch(int channel, int patch) const { const MidNamDeviceMode* dm = objectOrRef(); ciMidiNamChannelNameSetAssignments ia = dm->channelNameSetAssignments().find(channel); if(ia == dm->channelNameSetAssignments().cend()) return nullptr; const MidiNamChannelNameSetAssign* cns = ia->second; // FIXME Whether to return or not... Need an enum return value. const MidiNamPatch* p = cns->findPatch(channel, patch); if(p) return p; if(!_isCustomDeviceMode) return _channelNameSetList.findPatch(channel, patch); return nullptr; } const MidiNamPatchBankList* MidNamDeviceMode::getPatchBanks(int channel) const { const MidNamDeviceMode* dm = objectOrRef(); ciMidiNamChannelNameSetAssignments ia = dm->channelNameSetAssignments().find(channel); if(ia == dm->channelNameSetAssignments().cend()) return nullptr; const MidiNamChannelNameSetAssign* cns = ia->second; // FIXME Whether to return or not... Need an enum return value. const MidiNamPatchBankList* p = cns->getPatchBanks(channel); if(p) return p; if(!_isCustomDeviceMode) return _channelNameSetList.getPatchBanks(channel); return nullptr; } const MidiControllerList* MidNamDeviceMode::getControllers(int channel, int patch) const { const MidNamDeviceMode* dm = objectOrRef(); ciMidiNamChannelNameSetAssignments ia = dm->channelNameSetAssignments().find(channel); if(ia == dm->channelNameSetAssignments().cend()) return nullptr; const MidiNamChannelNameSetAssign* cns = ia->second; // FIXME Whether to return or not... Need an enum return value. const MidiControllerList* p = cns->getControllers(channel, patch); if(p) return p; if(!_isCustomDeviceMode) return _channelNameSetList.getControllers(channel, patch); return nullptr; } bool MidNamDeviceMode::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; const MidNamDeviceMode* dm = objectOrRef(); ciMidiNamChannelNameSetAssignments ia = dm->channelNameSetAssignments().find(channel); if(ia == dm->channelNameSetAssignments().cend()) return false; const MidiNamChannelNameSetAssign* cns = ia->second; // FIXME Whether to return or not... Need an enum return value. if(cns->getNoteSampleName(drum, channel, patch, note, name)) return true; if(!_isCustomDeviceMode) return _channelNameSetList.getNoteSampleName(drum, channel, patch, note, name); return false; } //---------------------------------------------------------------- MidNamDeviceModeList::MidNamDeviceModeList(const MidNamDeviceModeList& m) : std::map() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidNamDeviceMode(*i->second)); } MidNamDeviceModeList::~MidNamDeviceModeList() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } bool MidNamDeviceModeList::add(MidNamDeviceMode* a) { return insert(MidNamDeviceModeListPair(a->name(), a)).second; } void MidNamDeviceModeList::write(int level, MusECore::Xml& xml) const { for(const_iterator i = cbegin(); i != cend(); ++i) i->second->write(level, xml); } bool MidNamDeviceModeList::gatherReferences(MidNamReferencesList* refs) const { for(const_iterator i = cbegin(); i != cend(); ++i) i->second->gatherReferences(refs); return true; } //---------------------------------------------------------------- void MidNamDevice::write(int level, MusECore::Xml& xml) const { xml.put(level, "", Xml::xmlString(_name).toLocal8Bit().constData(), _uniqueID); } bool MidNamDevice::read(MusECore::Xml& xml) { QString name; int uniqueID = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("MidNamDevice"); break; case MusECore::Xml::Attribut: if (tag == "Name") { name = xml.s2(); } else if (tag == "UniqueID") { uniqueID = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "Device") { if(name.isEmpty() || uniqueID < 0) return false; _name = name; _uniqueID = uniqueID; return true; } default: break; } } return false; } //---------------------------------------------------------------- void MidNamModel::write(int level, MusECore::Xml& xml) const { xml.strTag(level, "Model", _model); } bool MidNamModel::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("MidNamModel"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::Text: _model = tag; break; case MusECore::Xml::TagEnd: if (tag == "Model") return true; default: break; } } return false; } //---------------------------------------------------------------- MidiNamModelList::MidiNamModelList(const MidiNamModelList& m) : std::map() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) add(new MidNamModel(*i->second)); } MidiNamModelList::~MidiNamModelList() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->second) delete i->second; } } bool MidiNamModelList::add(MidNamModel* a) { return insert(MidiNamModelListPair(a->model(), a)).second; } void MidiNamModelList::write(int level, MusECore::Xml& xml) const { for(const_iterator i = cbegin(); i != cend(); ++i) i->second->write(level, xml); } //---------------------------------------------------------------- void MidNamManufacturer::write(int level, MusECore::Xml& xml) const { xml.strTag(level, "Manufacturer", _manufacturer); } bool MidNamManufacturer::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("MidNamManufacturer"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::Text: _manufacturer = tag; break; case MusECore::Xml::TagEnd: if (tag == "Manufacturer") return true; default: break; } } return false; } //---------------------------------------------------------------- void MidNamAuthor::write(int level, MusECore::Xml& xml) const { xml.strTag(level, "Author", _author); } bool MidNamAuthor::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: xml.unknown("MidNamAuthor"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::Text: _author = tag; break; case MusECore::Xml::TagEnd: if (tag == "Author") return true; default: break; } } return false; } //---------------------------------------------------------------- void MidNamExtendingDeviceNames::write(int level, MusECore::Xml& xml) const { xml.tag(level++, "ExtendingDeviceNames"); _manufacturer.write(level, xml); _modelList.write(level, xml); _device.write(level, xml); _nameList.write(level, xml); xml.etag(--level, "ExtendingDeviceNames"); } bool MidNamExtendingDeviceNames::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "Manufacturer") _manufacturer.read(xml); else if (tag == "Model") { MidNamModel* n = new MidNamModel(); if(!n->read(xml) || !_modelList.add(n)) delete n; } else if (tag == "Device") _device.read(xml); // Special, different from other reads: // For convenience, hand the tag off to // the NameList to check for several names. // Returns true if recognized and processed. else if (!_nameList.read(xml)) xml.unknown("MidNamExtendingDeviceNames"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "ExtendingDeviceNames") return true; default: break; } } return false; } bool MidNamExtendingDeviceNames::gatherReferences(MidNamReferencesList* refs) const { return _nameList.gatherReferences(refs); } //---------------------------------------------------------------- MidNamExtendingDeviceNamesList::MidNamExtendingDeviceNamesList(const MidNamExtendingDeviceNamesList& m) : std::list() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) push_back(new MidNamExtendingDeviceNames(*(*i))); } MidNamExtendingDeviceNamesList::~MidNamExtendingDeviceNamesList() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(*i) delete *i; } } void MidNamExtendingDeviceNamesList::write(int level, MusECore::Xml& xml) const { for(const_iterator i = cbegin(); i != cend(); ++i) (*i)->write(level, xml); } bool MidNamExtendingDeviceNamesList::gatherReferences(MidNamReferencesList* refs) const { for(const_iterator i = cbegin(); i != cend(); ++i) (*i)->gatherReferences(refs); return true; } //---------------------------------------------------------------- void MidNamMasterDeviceNames::write(int level, MusECore::Xml& xml) const { xml.tag(level++, "MasterDeviceNames"); _manufacturer.write(level, xml); _modelList.write(level, xml); _device.write(level, xml); _deviceModeList.write(level, xml); _channelNameSetList.write(level, xml); _nameList.write(level, xml); xml.etag(--level, "MasterDeviceNames"); } bool MidNamMasterDeviceNames::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "Manufacturer") _manufacturer.read(xml); else if (tag == "Model") { MidNamModel* n = new MidNamModel(); if(!n->read(xml) || !_modelList.add(n)) delete n; } else if (tag == "Device") _device.read(xml); else if (tag == "CustomDeviceMode") { MidNamDeviceMode* n = new MidNamDeviceMode(); if(!n->read(xml) || !_deviceModeList.add(n)) delete n; } else if (tag == "SupportsStandardDeviceMode") { MidNamDeviceMode* n = new MidNamDeviceMode(); if(!n->read(xml) || !_deviceModeList.add(n)) delete n; } else if (tag == "ChannelNameSet") { MidNamChannelNameSet* n = new MidNamChannelNameSet(); if(!n->read(xml) || !_channelNameSetList.add(n)) delete n; } // Special, different from other reads: // For convenience, hand the tag off to // the NameList to check for several names. // Returns true if recognized and processed. else if (!_nameList.read(xml)) xml.unknown("MidNamMasterDeviceNames"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "MasterDeviceNames") return true; default: break; } } return false; } bool MidNamMasterDeviceNames::gatherReferences(MidNamReferencesList* refs) const { _deviceModeList.gatherReferences(refs); _channelNameSetList.gatherReferences(refs); _nameList.gatherReferences(refs); _channelNameSetList.gatherReferences(refs); return true; } const MidiNamPatch* MidNamMasterDeviceNames::findPatch(int channel, int patch) const { if(!deviceModeList()->empty()) { // We currently can only deal with one list. const MidNamDeviceMode* dm = deviceModeList()->cbegin()->second; return dm->findPatch(channel, patch); } return _channelNameSetList.findPatch(channel, patch); } const MidiNamPatchBankList* MidNamMasterDeviceNames::getPatchBanks(int channel) const { if(!deviceModeList()->empty()) { // We currently can only deal with one list. const MidNamDeviceMode* dm = deviceModeList()->begin()->second; return dm->getPatchBanks(channel); } return _channelNameSetList.getPatchBanks(channel); } bool MidNamMasterDeviceNames::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; if(!deviceModeList()->empty()) { // We currently can only deal with one list. const MidNamDeviceMode* dm = deviceModeList()->begin()->second; return dm->getNoteSampleName(drum, channel, patch, note, name); } return _channelNameSetList.getNoteSampleName(drum, channel, patch, note, name); } //---------------------------------------------------------------- MidNamMasterDeviceNamesList::MidNamMasterDeviceNamesList(const MidNamMasterDeviceNamesList& m) : std::list() { for(const_iterator i = m.cbegin(); i != m.cend(); ++i) push_back(new MidNamMasterDeviceNames(*(*i))); } MidNamMasterDeviceNamesList::~MidNamMasterDeviceNamesList() { for(const_iterator i = cbegin(); i != cend(); ++i) { if(*i) delete *i; } } void MidNamMasterDeviceNamesList::write(int level, MusECore::Xml& xml) const { for(const_iterator i = cbegin(); i != cend(); ++i) (*i)->write(level, xml); } bool MidNamMasterDeviceNamesList::gatherReferences(MidNamReferencesList* refs) const { for(const_iterator i = cbegin(); i != cend(); ++i) (*i)->gatherReferences(refs); return true; } //---------------------------------------------------------------- bool MidNamReferencesList::resolveReferences() const { for(MidNamNoteNameListRefs_t::const_iterator ir = noteNameListObjs.cbegin(); ir != noteNameListObjs.cend(); ++ir) { MidNamNoteNameList* lobj = *ir; // Looking for objects not references... // Make sure it has a name to reference to! if(lobj->isReference() || lobj->name().isEmpty()) continue; for(MidNamNoteNameListRefs_t::const_iterator iref = noteNameListObjs.cbegin(); iref != noteNameListObjs.cend(); ++iref) { MidNamNoteNameList* lref = *iref; // Looking for references not objects... if(!lref->isReference()) continue; // Do the names match? Set the reference's object. if(lobj->name() == lref->name()) lref->setObjectOrRef(lobj); } } for(MidiNamValNamesRefs_t::const_iterator ir = valNamesObjs.cbegin(); ir != valNamesObjs.cend(); ++ir) { MidiNamValNames* lobj = *ir; // Looking for objects not references... // Make sure it has a name to reference to! if(lobj->isReference() || lobj->name().isEmpty()) continue; for(MidiNamValNamesRefs_t::const_iterator iref = valNamesObjs.cbegin(); iref != valNamesObjs.cend(); ++iref) { MidiNamValNames* lref = *iref; // Looking for references not objects... if(!lref->isReference()) continue; // Do the names match? Set the reference's object. if(lobj->name() == lref->name()) lref->setObjectOrRef(lobj); } } for(MidiNamCtrlsRefs_t::const_iterator ir = ctrlsObjs.cbegin(); ir != ctrlsObjs.cend(); ++ir) { MidiNamCtrls* lobj = *ir; // Looking for objects not references... // Make sure it has a name to reference to! if(lobj->isReference() || lobj->name().isEmpty()) continue; for(MidiNamCtrlsRefs_t::const_iterator iref = ctrlsObjs.cbegin(); iref != ctrlsObjs.cend(); ++iref) { MidiNamCtrls* lref = *iref; // Looking for references not objects... if(!lref->isReference()) continue; // Do the names match? Set the reference's object. if(lobj->name() == lref->name()) lref->setObjectOrRef(lobj); } } for(MidiNamPatchNameListRefs_t::const_iterator ir = patchNameListObjs.cbegin(); ir != patchNameListObjs.cend(); ++ir) { MidiNamPatchNameList* lobj = *ir; // Looking for objects not references... // Make sure it has a name to reference to! if(lobj->isReference() || lobj->name().isEmpty()) continue; for(MidiNamPatchNameListRefs_t::const_iterator iref = patchNameListObjs.cbegin(); iref != patchNameListObjs.cend(); ++iref) { MidiNamPatchNameList* lref = *iref; // Looking for references not objects... if(!lref->isReference()) continue; // Do the names match? Set the reference's object. if(lobj->name() == lref->name()) lref->setObjectOrRef(lobj); } } for(MidNamDeviceModeRefs_t::const_iterator ir = deviceModeObjs.cbegin(); ir != deviceModeObjs.cend(); ++ir) { MidNamDeviceMode* lobj = *ir; // Looking for objects not references... // Make sure it has a name to reference to! if(lobj->isReference() || lobj->name().isEmpty()) continue; for(MidNamDeviceModeRefs_t::const_iterator iref = deviceModeObjs.cbegin(); iref != deviceModeObjs.cend(); ++iref) { MidNamDeviceMode* lref = *iref; // Looking for references not objects... if(!lref->isReference()) continue; // Do the names match? Set the reference's object. if(lobj->name() == lref->name()) lref->setObjectOrRef(lobj); } } for(MidNamChannelNameSetRefs_t::const_iterator ir = channelNameSetObjs.cbegin(); ir != channelNameSetObjs.cend(); ++ir) { MidNamChannelNameSet* lobj = *ir; // Unlike the other referencing classes, MidNamChannelNameSet is not referenceable. // Here we are looking for a separate corresponding MidNamChannelNameSet. // Make sure it has a name to reference to! if(/*lobj->isReference() ||*/ lobj->name().isEmpty()) continue; for(MidiNamChannelNameSetAssignRefs_t::const_iterator iref = channelNameSetAssignObjs.cbegin(); iref != channelNameSetAssignObjs.cend(); ++iref) { MidiNamChannelNameSetAssign* lref = *iref; // Here we are just looking for the name of a separate corresponding MidNamChannelNameSet. //if(!lref->isReference()) // continue; // Do the names match? Set the reference's object. if(lobj->name() == lref->name()) lref->setObjectOrRef(lobj); } } return true; } //---------------------------------------------------------------- void MidNamMIDINameDocument::write(int level, MusECore::Xml& xml) const { xml.tag(level++, "MIDINameDocument"); _author.write(level, xml); _masterDeviceNamesList.write(level, xml); _extendingDeviceNamesList.write(level, xml); _standardDeviceModeList.write(level, xml); xml.etag(--level, "MIDINameDocument"); } bool MidNamMIDINameDocument::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return false; case MusECore::Xml::TagStart: if (tag == "Author") _author.read(xml); else if (tag == "MasterDeviceNames") { MidNamMasterDeviceNames* n = new MidNamMasterDeviceNames(); if(n->read(xml)) _masterDeviceNamesList.push_back(n); else delete n; } else if (tag == "ExtendingDeviceNames") { MidNamExtendingDeviceNames* n = new MidNamExtendingDeviceNames(); if(n->read(xml)) _extendingDeviceNamesList.push_back(n); else delete n; } else if (tag == "StandardDeviceMode") { MidNamDeviceMode* n = new MidNamDeviceMode(); if(!n->read(xml) || !_standardDeviceModeList.add(n)) delete n; } else xml.unknown("MidNamMIDINameDocument"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "MIDINameDocument") return true; break; case MusECore::Xml::Proc: default: break; } } return false; } bool MidNamMIDINameDocument::gatherReferences(MidNamReferencesList* refs) const { _masterDeviceNamesList.gatherReferences(refs); _extendingDeviceNamesList.gatherReferences(refs); _standardDeviceModeList.gatherReferences(refs); return true; } bool MidNamMIDINameDocument::resolveReferences() { MidNamReferencesList refs; gatherReferences(&refs); return refs.resolveReferences(); } const MidiNamPatch* MidNamMIDINameDocument::findPatch(int channel, int patch) const { // Which of the three exclusively possible device lists is dominant (has stuff in it)? if(!_masterDeviceNamesList.empty()) { // We currently can only deal with one list. const MidNamMasterDeviceNames* mdn = _masterDeviceNamesList.front(); if(!mdn->deviceModeList()->empty()) { // We currently can only deal with one list. const MidNamDeviceMode* dm = mdn->deviceModeList()->begin()->second; return dm->findPatch(channel, patch); } } else if(!_extendingDeviceNamesList.empty()) { } else if(!_standardDeviceModeList.empty()) { } return nullptr; } const MidiNamPatchBankList* MidNamMIDINameDocument::getPatchBanks(int channel) const { // Which of the three exclusively possible device lists is dominant (has stuff in it)? if(!_masterDeviceNamesList.empty()) { // We currently can only deal with one list. const MidNamMasterDeviceNames* mdn = _masterDeviceNamesList.front(); if(!mdn->deviceModeList()->empty()) { // We currently can only deal with one list. const MidNamDeviceMode* dm = mdn->deviceModeList()->begin()->second; return dm->getPatchBanks(channel); } } else if(!_extendingDeviceNamesList.empty()) { } else if(!_standardDeviceModeList.empty()) { } return nullptr; } const MidiControllerList* MidNamMIDINameDocument::getControllers(int channel, int patch) const { // Which of the three exclusively possible device lists is dominant (has stuff in it)? if(!_masterDeviceNamesList.empty()) { // We currently can only deal with one list. const MidNamMasterDeviceNames* mdn = _masterDeviceNamesList.front(); if(!mdn->deviceModeList()->empty()) { // We currently can only deal with one list. const MidNamDeviceMode* dm = mdn->deviceModeList()->begin()->second; return dm->getControllers(channel, patch); } } else if(!_extendingDeviceNamesList.empty()) { } else if(!_standardDeviceModeList.empty()) { } return nullptr; } bool MidNamMIDINameDocument::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; // Which of the three exclusively possible device lists is dominant (has stuff in it)? if(!_masterDeviceNamesList.empty()) { // We currently can only deal with one list. const MidNamMasterDeviceNames* mdn = _masterDeviceNamesList.front(); if(!mdn->deviceModeList()->empty()) { // We currently can only deal with one list. const MidNamDeviceMode* dm = mdn->deviceModeList()->begin()->second; return dm->getNoteSampleName(drum, channel, patch, note, name); } } else if(!_extendingDeviceNamesList.empty()) { } else if(!_standardDeviceModeList.empty()) { } return false; } //---------------------------------------------------------------- void MidNamMIDIName::write(int level, MusECore::Xml& xml) const { _MIDINameDocument.write(level, xml); } bool MidNamMIDIName::read(MusECore::Xml& xml) { // Be sure to clear the whole thing before reading. clear(); for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: return false; case MusECore::Xml::End: // We've reached the end of the file, without error. // Everything's fine. // Be sure to resolve references! resolveReferences(); return true; case MusECore::Xml::TagStart: if (tag == "MIDINameDocument") { if(!_MIDINameDocument.read(xml)) return false; // We have successfully loaded a valid document. // It's no longer empty. _isEmpty = false; } else xml.unknown("MidNamMIDIName"); break; // No attributes or tag ends to look for here. case MusECore::Xml::Attribut: case MusECore::Xml::TagEnd: default: break; } } return false; } const MidiNamPatch* MidNamMIDIName::findPatch(int channel, int patch) const { return _MIDINameDocument.findPatch(channel, patch); } const MidiNamPatchBankList* MidNamMIDIName::getPatchBanks(int channel) const { return _MIDINameDocument.getPatchBanks(channel); } const MidiControllerList* MidNamMIDIName::getControllers(int channel, int patch) const { return _MIDINameDocument.getControllers(channel, patch); } bool MidNamMIDIName::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; return _MIDINameDocument.getNoteSampleName(drum, channel, patch, note, name); } //---------------------------------------------------------------- void MidNamMIDINameDocumentList::write(int level, MusECore::Xml& xml) const { for(const_iterator i = cbegin(); i != cend(); ++i) (*i).write(level, xml); } bool MidNamMIDINameDocumentList::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: return false; case MusECore::Xml::End: // We've reached the end of the file, without error. // Everything's fine. return true; case MusECore::Xml::TagStart: if (tag == "MIDINameDocument") { MidNamMIDINameDocument n; if(n.read(xml)) push_back(n); } else xml.unknown("MidNamMIDINameDocumentList"); break; // No attributes or tag ends to look for here. case MusECore::Xml::Attribut: case MusECore::Xml::TagEnd: default: break; } } return false; } //---------------------------------------------------------------- } // namespace MusECore muse-4.0.0/src/libs/midnam/midnam.h000066400000000000000000001215531404105311000170630ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // midnam.h // (C) Copyright 2019, 2020 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDNAM_H__ #define __MIDNAM_H__ #include #include #include #include #include "xml.h" #include "mpevent.h" #include "midi_controller.h" namespace MusECore { struct MidNamReferencesList; class MidiNamMIDICommands : public MPEventList { private: bool _isPatchMIDICommands; // If bank high or low control commands are detected, // these values are set to them. Otherwise they are // set at 'unknown' (default) 0xff. int _bankH; int _bankL; bool _hasBankH; bool _hasBankL; public: MidiNamMIDICommands() : _isPatchMIDICommands(false), _bankH(0xff), _bankL(0xff), _hasBankH(false), _hasBankL(false) { } bool isPatchMIDICommands() const { return _isPatchMIDICommands; } void setIsPatchMIDICommands(bool v) { _isPatchMIDICommands = v; } int bankH() const { return _bankH; } int bankL() const { return _bankL; } bool hasBankH() const { return _hasBankH; } bool hasBankL() const { return _hasBankL; } void write(int level, MusECore::Xml& xml) const; bool read( MusECore::Xml& xml, bool includeSysEx = true, int defaultPort = 0, bool channelRequired = false, int defaultChannel = 0); }; //------------------------------------------------------------------- class MidiNamAvailableChannel { private: int _channel; bool _available; public: MidiNamAvailableChannel() : _channel(0), _available(false) { } MidiNamAvailableChannel(int channel, bool available) : _channel(channel), _available(available) { } int channel() const { return _channel; } bool available() const { return _available; } void setAvailable(bool available) { _available = available; } bool operator<(const MidiNamAvailableChannel& n) const { return _channel < n._channel; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; //------------------------------------------------------------------- class MidiNamAvailableForChannels : public std::map> { public: MidiNamAvailableForChannels() { } MidiNamAvailableForChannels(const MidiNamAvailableForChannels& m); ~MidiNamAvailableForChannels(); bool add(MidiNamAvailableChannel* a); void write(int level, MusECore::Xml& xml) const; void read(MusECore::Xml& xml); }; typedef MidiNamAvailableForChannels::iterator iMidiNamAvailableForChannels; typedef MidiNamAvailableForChannels::const_iterator ciMidiNamAvailableForChannels; typedef std::pair MidiNamAvailableForChannelsPair; //------------------------------------------------------------------- class MidNamChannelNameSet; class MidiNamPatch; class MidiNamPatchBankList; class MidiNamChannelNameSetAssign { private: int _channel; QString _name; // Points to a reference. MidNamChannelNameSet* _p_ref; public: MidiNamChannelNameSetAssign() : _channel(0), _p_ref(nullptr) { } MidiNamChannelNameSetAssign(int channel, const QString& nameSet) : _channel(channel), _name(nameSet), _p_ref(nullptr) { } int channel() const { return _channel; } const QString& name() const { return _name; } void setName(const QString& nameSet) { _name = nameSet; } // NOTE: Unlike the other referencing classes, this always returns the reference object and can be NULL. MidNamChannelNameSet* objectOrRef() { return _p_ref; } void setObjectOrRef(MidNamChannelNameSet* l) { _p_ref = l; } void resetObjectOrRef() { _p_ref = nullptr; } bool gatherReferences(MidNamReferencesList* refs) const; bool operator<(const MidiNamChannelNameSetAssign& n) const { return _channel < n._channel; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int channel, int patch) const; const MidiNamPatchBankList* getPatchBanks(int channel) const; // Find the list of controllers for a channel and/or patch. // If channel is -1 or patch is don't care, it looks for defaults. const MidiControllerList* getControllers(int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; class MidiNamChannelNameSetAssignments : public std::map> { private: bool _hasChannelNameSetAssignments; public: MidiNamChannelNameSetAssignments() : _hasChannelNameSetAssignments(false) { } MidiNamChannelNameSetAssignments(const MidiNamChannelNameSetAssignments& m); ~MidiNamChannelNameSetAssignments(); bool hasChannelNameSetAssignments() const { return _hasChannelNameSetAssignments; } bool add(MidiNamChannelNameSetAssign* a); bool gatherReferences(MidNamReferencesList* refs) const; void write(int level, MusECore::Xml& xml) const; void read(MusECore::Xml& xml); // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int channel, int patch) const; const MidiNamPatchBankList* getPatchBanks(int channel) const; // Find the list of controllers for a channel and/or patch. // If channel is -1 or patch is don't care, it looks for defaults. const MidiControllerList* getControllers(int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; typedef MidiNamChannelNameSetAssignments::iterator iMidiNamChannelNameSetAssignments; typedef MidiNamChannelNameSetAssignments::const_iterator ciMidiNamChannelNameSetAssignments; typedef std::pair MidiNamChannelNameSetAssignmentsPair; //------------------------------------------------------------------- class MidiNamNotes; class MidiNamNoteGroup : public std::set { private: // Optional. QString _name; public: MidiNamNoteGroup() {} MidiNamNoteGroup(const QString& name) : _name(name) { } const QString& name() const { return _name; } void setName(const QString& name) { _name = name; } bool operator<(const MidiNamNoteGroup& n) const { return _name < n._name; } void write(int level, MusECore::Xml& xml, const MidiNamNotes* notes) const; void read(MusECore::Xml& xml, MidiNamNotes* notes); }; typedef MidiNamNoteGroup::iterator iMidiNamNoteGroup; typedef MidiNamNoteGroup::const_iterator ciMidiNamNoteGroup; typedef std::pair MidiNamNoteGroupPair; // The note group name is optional, may be blank. class MidiNamNoteGroups : public std::multimap> { public: MidiNamNoteGroups() { } MidiNamNoteGroups(const MidiNamNoteGroups& m); ~MidiNamNoteGroups(); MidiNamNoteGroups& operator=(const MidiNamNoteGroups& m); bool add(MidiNamNoteGroup* a); void write(int level, MusECore::Xml& xml, const MidiNamNotes* notes) const; }; typedef MidiNamNoteGroups::iterator iMidiNamNoteGroups; typedef MidiNamNoteGroups::const_iterator ciMidiNamNoteGroups; typedef std::pair MidiNamNoteGroupsPair; typedef std::pair MidiNamNoteGroupsRange; //------------------------------------------------------------------- class MidiNamNote { private: int _number; QString _name; public: MidiNamNote() : _number(0) { } MidiNamNote(int number, const QString& name) : _number(number), _name(name) { } int number() const { return _number; } const QString& name() const { return _name; } void setName(const QString& name) { _name = name; } bool operator<(const MidiNamNote& n) const { return _number < n._number; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; class MidiNamNotes : public std::map> { private: MidiNamNoteGroups _noteGroups; public: MidiNamNotes() { } MidiNamNotes(const MidiNamNotes& m); ~MidiNamNotes(); MidiNamNoteGroups& noteGroups() { return _noteGroups; } const MidiNamNoteGroups& noteGroups() const { return _noteGroups; } bool isEmpty() const { return _noteGroups.empty() && empty(); } bool add(MidiNamNote* a); bool addNoteGroup(MidiNamNoteGroup* a); void write(int level, MusECore::Xml& xml) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; typedef MidiNamNotes::iterator iMidiNamNotes; typedef MidiNamNotes::const_iterator ciMidiNamNotes; typedef std::pair MidiNamNotesPair; typedef std::pair MidiNamNotesInsPair; //------------------------------------------------------------------- class MidNamNoteNameList { private: QString _name; MidiNamNotes _noteList; // Points to a reference. MidNamNoteNameList* _p_ref; bool _isReference; bool _hasNoteNameList; public: MidNamNoteNameList() : _p_ref(nullptr), _isReference(false), _hasNoteNameList(false) { } MidNamNoteNameList(const QString& name) : _name(name), _p_ref(nullptr), _isReference(false), _hasNoteNameList(false) { } bool hasNoteNameList() const { return _hasNoteNameList; } // Outside of these classes, always use this method to get the real list. const MidNamNoteNameList* objectOrRef() const { return (_isReference && _p_ref) ? _p_ref : this; } MidNamNoteNameList* objectOrRef() { return (_isReference && _p_ref) ? _p_ref : this; } void setObjectOrRef(MidNamNoteNameList* l) { _p_ref = l; } void resetObjectOrRef() { _p_ref = nullptr; } bool gatherReferences(MidNamReferencesList* refs) const; bool empty() const { return _noteList.isEmpty(); } const MidiNamNotes& noteList() const { return _noteList; } const QString& name() const { return _name; } void setName(const QString& name) { _name = name; } bool isReference() const { return _isReference; } void setIsReference(bool v) { _isReference = v; } bool operator<(const MidNamNoteNameList& n) const { return _name < n._name; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; //------------------------------------------------------- class MidiNamVal { private: int _number; QString _name; public: MidiNamVal() : _number(0) { } MidiNamVal(int number, const QString& name) : _number(number), _name(name) { } int number() const { return _number; } const QString& name() const { return _name; } void setName(const QString& name) { _name = name; } bool operator<(const MidiNamVal& n) const { return _number < n._number; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; class MidiNamValNames : public std::map> { private: QString _name; // Points to a reference. MidiNamValNames* _p_ref; bool _isReference; public: MidiNamValNames() : _p_ref(nullptr), _isReference(false) {} MidiNamValNames(const QString& name) : _name(name), _p_ref(nullptr), _isReference(false) { } MidiNamValNames(const MidiNamValNames& m); MidiNamValNames& operator=(const MidiNamValNames& m); ~MidiNamValNames(); MidiNamValNames* objectOrRef() { return (_isReference && _p_ref) ? _p_ref : this; } void setObjectOrRef(MidiNamValNames* l) { _p_ref = l; } void resetObjectOrRef() { _p_ref = nullptr; } bool add(MidiNamVal* a); bool gatherReferences(MidNamReferencesList* refs) const; bool isReference() const { return _isReference; } void setIsReference(bool v) { _isReference = v; } const QString& name() const { return _name; } void setName(const QString& name) { _name = name; } void write(int level, MusECore::Xml& xml) const; void read(MusECore::Xml& xml); }; typedef MidiNamValNames::iterator iMidiNamValNames; typedef MidiNamValNames::const_iterator ciMidiNamValNames; typedef std::pair MidiNamValNamesPair; typedef std::pair MidiNamValNamesInsPair; class MidiNamValues { private: int _min; int _max; int _default; int _units; int _mapping; MidiNamValNames _valueNames; public: MidiNamValues() : _min(0), _max(127), _default(0), _units(0), _mapping(0) {} MidiNamValues(int min, int max, int def, int units, int mapping) : _min(min), _max(max), _default(def), _units(units), _mapping(mapping) {} int minVal() const { return _min; } void setMinVal(int v) { _min = v; } int maxVal() const { return _max; } void setMaxVal(int v) { _max = v; } int defaultVal() const { return _default; } void setDefaultVal(int v) { _default = v; } int units() const { return _units; } void setUnits(int v) { _units = v; } int mapping() const { return _mapping; } void setMapping(int v) { _mapping = v; } MidiNamValNames& valueNames() { return _valueNames; } bool empty() const { return _valueNames.empty(); } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; //------------------------------------------------------- class MidiNamCtrl : public MidiController { private: MidiNamValues _values; public: MidiNamCtrl() : MidiController() {} MidiNamCtrl(int number, const QString& name) : MidiController(name, number, 0, 127, 0, -1) {} bool operator<(const MidiNamCtrl& n) const { return _num < n._num; } void writeMidnam(int level, MusECore::Xml& xml) const; bool readMidnam(MusECore::Xml& xml); }; class MidiNamCtrls : public MidiControllerList { private: QString _name; // Points to a reference. MidiNamCtrls* _p_ref; bool _isReference; bool _hasMidiNamCtrls; public: MidiNamCtrls() : _p_ref(nullptr), _isReference(false), _hasMidiNamCtrls(false) { } MidiNamCtrls(const QString& name) : _name(name), _p_ref(nullptr), _isReference(false), _hasMidiNamCtrls(false) { } MidiNamCtrls(const MidiNamCtrls& mcl); // We require a destructor here because MidiControllerList // does not delete its contents. ~MidiNamCtrls(); bool hasMidiNamCtrls() const { return _hasMidiNamCtrls; } const MidiNamCtrls* objectOrRef() const { return (_isReference && _p_ref) ? _p_ref : this; } MidiNamCtrls* objectOrRef() { return (_isReference && _p_ref) ? _p_ref : this; } void setObjectOrRef(MidiNamCtrls* l) { _p_ref = l; } void resetObjectOrRef() { _p_ref = nullptr; } bool gatherReferences(MidNamReferencesList* refs) const; const QString& name() const { return _name; } void setName(const QString& name) { _name = name; } bool isReference() const { return _isReference; } void setIsReference(bool v) { _isReference = v; } void writeMidnam(int level, MusECore::Xml& xml) const; void readMidnam(MusECore::Xml& xml); // Find the list of controllers for a channel and/or patch. // If channel is -1 or patch is don't care, it looks for defaults. const MidiControllerList* getControllers(int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; }; //----------------------------------------------------------------- class MidiNamPatch { private: QString _number; QString _name; int _patchNumber; MidiNamMIDICommands _patchMIDICommands; MidiNamChannelNameSetAssignments _channelNameSetAssignments; MidNamNoteNameList _noteNameList; MidiNamCtrls _controlNameList; public: MidiNamPatch(int patchNumber = CTRL_PROGRAM_VAL_DONT_CARE) : _patchNumber(patchNumber) {} MidiNamPatch(const QString& number, const QString& name, int patchNumber = CTRL_PROGRAM_VAL_DONT_CARE) : _number(number), _name(name), _patchNumber(patchNumber) {} int patchNumber() const { return _patchNumber; } MidiNamMIDICommands& patchMIDICommands() { return _patchMIDICommands; } const MidiNamMIDICommands& patchMIDICommands() const { return _patchMIDICommands; } MidiNamChannelNameSetAssignments& channelNameSetAssignments() { return _channelNameSetAssignments; } const MidiNamChannelNameSetAssignments& channelNameSetAssignments() const { return _channelNameSetAssignments; } MidNamNoteNameList& noteNameList() { return _noteNameList; } const MidNamNoteNameList& noteNameList() const { return _noteNameList; } MidiNamCtrls& controlNameList() { return _controlNameList; } const MidiNamCtrls& controlNameList() const { return _controlNameList; } bool gatherReferences(MidNamReferencesList* refs) const; const QString& name() const { return _name; } void setName(const QString& name) { _name = name; } bool operator<(const MidiNamPatch& n) const { return _patchNumber < n._patchNumber; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); // Find the list of controllers for a channel and/or patch. // If channel is -1 or patch is don't care, it looks for defaults. const MidiControllerList* getControllers(int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; class MidiNamPatchNameList : public std::map> { private: // Optional. QString _name; // Points to a reference. MidiNamPatchNameList* _p_ref; bool _isReference; public: MidiNamPatchNameList() : _p_ref(nullptr), _isReference(false) {} MidiNamPatchNameList(const QString& name) : _name(name), _p_ref(nullptr), _isReference(false) { } MidiNamPatchNameList(const MidiNamPatchNameList& m); ~MidiNamPatchNameList(); bool add(MidiNamPatch* a); const MidiNamPatchNameList* objectOrRef() const { return (_isReference && _p_ref) ? _p_ref : this; } MidiNamPatchNameList* objectOrRef() { return (_isReference && _p_ref) ? _p_ref : this; } void setObjectOrRef(MidiNamPatchNameList* l) { _p_ref = l; } void resetObjectOrRef() { _p_ref = nullptr; } bool gatherReferences(MidNamReferencesList* refs) const; const QString& name() const { return _name; } void setName(const QString& name) { _name = name; } bool isReference() const { return _isReference; } void setIsReference(bool v) { _isReference = v; } bool operator<(const MidiNamPatchNameList& n) const { return _name < n._name; } void write(int level, MusECore::Xml& xml) const; void read(MusECore::Xml& xml); // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int patch, int bankHL = 0xffff) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name, int bankHL = 0xffff) const; }; typedef MidiNamPatchNameList::iterator iMidiNamPatchNameList; typedef MidiNamPatchNameList::const_iterator ciMidiNamPatchNameList; typedef std::pair MidiNamPatchNameListPair; //----------------------------------------------------------------- class MidiNamPatchBank { private: QString _name; bool _ROM; MidiNamMIDICommands _MIDICommands; // If bank high or low control commands are detected, // this value is set to them. Otherwise it is // set at 'unknown' (default) bank high and low 0xffff. int _bankHL; MidiNamPatchNameList _patchNameList; public: MidiNamPatchBank() : _ROM(false), _bankHL(0xffff) {} MidiNamPatchBank(const QString& name, bool ROM) : _name(name), _ROM(ROM), _bankHL(0xffff) {} MidiNamPatchNameList& patchNameList() { return _patchNameList; } const MidiNamPatchNameList& patchNameList() const { return _patchNameList; } MidiNamMIDICommands& MIDICommands() { return _MIDICommands; } const MidiNamMIDICommands& MIDICommands() const { return _MIDICommands; } int bankHL() const { return _bankHL; } bool gatherReferences(MidNamReferencesList* refs) const; const QString& name() const { return _name; } void setName(const QString& name) { _name = name; } bool operator<(const MidiNamPatchBank& n) const { return _bankHL < n._bankHL; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int patch) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; class MidiNamPatchBankList : public std::map> { public: MidiNamPatchBankList() { } MidiNamPatchBankList(const MidiNamPatchBankList& m); ~MidiNamPatchBankList(); bool add(MidiNamPatchBank* a); bool gatherReferences(MidNamReferencesList* refs) const; void write(int level, MusECore::Xml& xml) const; // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int patch) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; typedef MidiNamPatchBankList::iterator iMidiNamPatchBankList; typedef MidiNamPatchBankList::const_iterator ciMidiNamPatchBankList; typedef std::pair MidiNamPatchBankListPair; //----------------------------------------------------------------- class MidNamChannelNameSet { private: QString _name; MidiNamAvailableForChannels _availableForChannels; MidNamNoteNameList _noteNameList; MidiNamCtrls _controlNameList; MidiNamPatchBankList _patchBankList; public: MidNamChannelNameSet() {} MidNamChannelNameSet( const QString& name) : _name(name) {} const QString& name() const { return _name; } void setName(const QString& nameSet) { _name = nameSet; } bool gatherReferences(MidNamReferencesList* refs) const; bool operator<(const MidNamChannelNameSet& n) const { return _name < n._name; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int channel, int patch) const; const MidiNamPatchBankList* getPatchBanks(int channel) const; // Find the list of controllers for a channel and/or patch. // If channel is -1 or patch is don't care, it looks for defaults. const MidiControllerList* getControllers(int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; class MidiNamChannelNameSetList : public std::map> { public: MidiNamChannelNameSetList() { } MidiNamChannelNameSetList(const MidiNamChannelNameSetList& m); ~MidiNamChannelNameSetList(); bool add(MidNamChannelNameSet* a); bool gatherReferences(MidNamReferencesList* refs) const; void write(int level, MusECore::Xml& xml) const; // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int channel, int patch) const; const MidiNamPatchBankList* getPatchBanks(int channel) const; // Find the list of controllers for a channel and/or patch. // If channel is -1 or patch is don't care, it looks for defaults. const MidiControllerList* getControllers(int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; typedef MidiNamChannelNameSetList::iterator iMidiNamChannelNameSetList; typedef MidiNamChannelNameSetList::const_iterator ciMidiNamChannelNameSetList; typedef std::pair MidiNamChannelNameSetListPair; //----------------------------------------------------------------- class MidNamDeviceModeEnable { private: MidiNamMIDICommands _MIDICommands; public: MidNamDeviceModeEnable() {} const MidiNamMIDICommands& MIDICommands() const { return _MIDICommands; } MidiNamMIDICommands& MIDICommands() { return _MIDICommands; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; class MidNamDeviceModeDisable { private: MidiNamMIDICommands _MIDICommands; public: MidNamDeviceModeDisable() {} const MidiNamMIDICommands& MIDICommands() const { return _MIDICommands; } MidiNamMIDICommands& MIDICommands() { return _MIDICommands; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; //----------------------------------------------------------------- class MidNamNameList { private: MidiNamPatchNameList _patchNameList; MidNamNoteNameList _noteNameList; MidiNamCtrls _controlNameList; MidiNamValNames _valueNameList; public: MidNamNameList() {} bool gatherReferences(MidNamReferencesList* refs) const; bool empty() const { return _patchNameList.empty() && _noteNameList.empty() && _controlNameList.empty() && _valueNameList.empty(); } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; //----------------------------------------------------------------- class MidNamDeviceMode { private: QString _name; // Whether this is a StandardDeviceMode or CustomDeviceMode. bool _isCustomDeviceMode; MidNamDeviceModeEnable _deviceModeEnable; MidNamDeviceModeDisable _deviceModeDisable; MidiNamChannelNameSetAssignments _channelNameSetAssignments; MidNamNameList _nameList; // Used only with StandardDeviceMode. MidiNamChannelNameSetList _channelNameSetList; // Points to a reference (a SupportsStandardDeviceMode reference to a StandardDeviceMode). MidNamDeviceMode* _p_ref; bool _isReference; public: MidNamDeviceMode() : _isCustomDeviceMode(false), _p_ref(nullptr), _isReference(false) {} MidNamDeviceMode( const QString& name) : _name(name), _isCustomDeviceMode(false), _p_ref(nullptr), _isReference(false) {} const MidNamDeviceMode* objectOrRef() const { return (_isReference && _p_ref) ? _p_ref : this; } MidNamDeviceMode* objectOrRef() { return (_isReference && _p_ref) ? _p_ref : this; } void setObjectOrRef(MidNamDeviceMode* l) { _p_ref = l; } void resetObjectOrRef() { _p_ref = nullptr; } bool gatherReferences(MidNamReferencesList* refs) const; bool isCustomDeviceMode() const { return _isCustomDeviceMode; } void setCustomDeviceMode(bool v) { _isCustomDeviceMode = v; } const MidNamDeviceModeEnable& deviceModeEnable() const { return _deviceModeEnable; } MidNamDeviceModeEnable& deviceModeEnable() { return _deviceModeEnable; } const MidNamDeviceModeDisable& deviceModeDisable() const { return _deviceModeDisable; } MidNamDeviceModeDisable& deviceModeDisable() { return _deviceModeDisable; } const MidiNamChannelNameSetAssignments& channelNameSetAssignments() const { return _channelNameSetAssignments; } MidiNamChannelNameSetAssignments& channelNameSetAssignments() { return _channelNameSetAssignments; } const MidNamNameList& nameList() const { return _nameList; } MidNamNameList& nameList() { return _nameList; } const MidiNamChannelNameSetList& channelNameSetList() const { return _channelNameSetList; } MidiNamChannelNameSetList& channelNameSetList() { return _channelNameSetList; } const QString& name() const { return _name; } bool isReference() const { return _isReference; } void setIsReference(bool v) { _isReference = v; } bool operator<(const MidNamDeviceMode& n) const { return _name < n._name; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int channel, int patch) const; const MidiNamPatchBankList* getPatchBanks(int channel) const; // Find the list of controllers for a channel and/or patch. // If channel is -1 or patch is don't care, it looks for defaults. const MidiControllerList* getControllers(int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; class MidNamDeviceModeList : public std::map> { public: MidNamDeviceModeList() { } MidNamDeviceModeList(const MidNamDeviceModeList& m); ~MidNamDeviceModeList(); bool add(MidNamDeviceMode* a); bool gatherReferences(MidNamReferencesList* refs) const; void write(int level, MusECore::Xml& xml) const; }; typedef MidNamDeviceModeList::iterator iMidNamDeviceModeList; typedef MidNamDeviceModeList::const_iterator ciMidNamDeviceModeList; typedef std::pair MidNamDeviceModeListPair; //----------------------------------------------------------------- class MidNamDevice { private: QString _name; int _uniqueID; public: MidNamDevice() : _uniqueID(0) {} MidNamDevice(const QString& name, int uniqueID) : _name(name), _uniqueID(uniqueID) {} const QString& name() const { return _name; } int uniqueID() const { return _uniqueID; } bool operator<(const MidNamDevice& n) const { return _uniqueID < n._uniqueID; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; //----------------------------------------------------------------- class MidNamModel { private: QString _model; public: MidNamModel() {} MidNamModel(const QString& model) : _model(model) {} const QString& model() const { return _model; } bool operator<(const MidNamModel& n) const { return _model < n._model; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; class MidiNamModelList : public std::map> { public: MidiNamModelList() { } MidiNamModelList(const MidiNamModelList& m); ~MidiNamModelList(); bool add(MidNamModel* a); void write(int level, MusECore::Xml& xml) const; }; typedef MidiNamModelList::iterator iMidiNamModelList; typedef MidiNamModelList::const_iterator ciMidiNamModelList; typedef std::pair MidiNamModelListPair; //----------------------------------------------------------------- class MidNamManufacturer { private: QString _manufacturer; public: MidNamManufacturer() {} MidNamManufacturer(const QString& manufacturer) : _manufacturer(manufacturer) {} const QString& manufacturer() const { return _manufacturer; } bool operator<(const MidNamManufacturer& n) const { return _manufacturer < n._manufacturer; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; //----------------------------------------------------------------- class MidNamAuthor { private: QString _author; public: MidNamAuthor() {} MidNamAuthor(const QString& author) : _author(author) {} const QString& author() const { return _author; } void clear() { _author.clear(); } bool operator<(const MidNamAuthor& n) const { return _author < n._author; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; //----------------------------------------------------------------- class MidNamExtendingDeviceNames { private: MidNamManufacturer _manufacturer; MidiNamModelList _modelList; MidNamDevice _device; MidNamNameList _nameList; public: MidNamExtendingDeviceNames() {} bool gatherReferences(MidNamReferencesList* refs) const; MidNamManufacturer& manufacturer() { return _manufacturer; } MidiNamModelList& modelList() { return _modelList; } MidNamDevice& device() { return _device; } MidNamNameList& nameList() { return _nameList; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; class MidNamExtendingDeviceNamesList : public std::list { public: MidNamExtendingDeviceNamesList() { } MidNamExtendingDeviceNamesList(const MidNamExtendingDeviceNamesList& m); ~MidNamExtendingDeviceNamesList(); bool gatherReferences(MidNamReferencesList* refs) const; void write(int level, MusECore::Xml& xml) const; }; typedef MidNamExtendingDeviceNamesList::iterator iMidNamExtendingDeviceNamesList; typedef MidNamExtendingDeviceNamesList::const_iterator ciMidNamExtendingDeviceNamesList; //----------------------------------------------------------------- class MidNamMasterDeviceNames { private: MidNamManufacturer _manufacturer; MidiNamModelList _modelList; MidNamDevice _device; MidNamDeviceModeList _deviceModeList; MidiNamChannelNameSetList _channelNameSetList; MidNamNameList _nameList; public: MidNamMasterDeviceNames() {} bool gatherReferences(MidNamReferencesList* refs) const; MidNamManufacturer& manufacturer() { return _manufacturer; } MidiNamModelList& modelList() { return _modelList; } MidNamDevice& device() { return _device; } const MidNamDeviceModeList* deviceModeList() const { return &_deviceModeList; } MidiNamChannelNameSetList& channelNameSetList() { return _channelNameSetList; } MidNamNameList& nameList() { return _nameList; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int channel, int patch) const; const MidiNamPatchBankList* getPatchBanks(int channel) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; class MidNamMasterDeviceNamesList : public std::list { public: MidNamMasterDeviceNamesList() { } MidNamMasterDeviceNamesList(const MidNamMasterDeviceNamesList& m); ~MidNamMasterDeviceNamesList(); bool gatherReferences(MidNamReferencesList* refs) const; void write(int level, MusECore::Xml& xml) const; }; typedef MidNamMasterDeviceNamesList::iterator iMidNamMasterDeviceNamesList; typedef MidNamMasterDeviceNamesList::const_iterator ciMidNamMasterDeviceNamesList; //----------------------------------------------------------------- // T is a pointer to one of the reference lists, such as MidNamNoteNameList. template, class Alloc = std::allocator > class MidNamReferenceList_t : public std::set { private: typedef std::set vlist; public: typedef typename vlist::iterator iMidNamReferenceList_t; typedef typename vlist::const_iterator ciMidNamReferenceList_t; typedef std::pair MidNamReferenceListPair_t; bool add(const T t) { // Make sure it has a name! if(t->name().isEmpty()) return false; MidNamReferenceListPair_t pr = vlist::insert(t); return pr.second; } }; typedef MidNamReferenceList_t MidNamNoteNameListRefs_t; typedef MidNamReferenceList_t MidiNamValNamesRefs_t; typedef MidNamReferenceList_t MidiNamCtrlsRefs_t; typedef MidNamReferenceList_t MidiNamPatchNameListRefs_t; typedef MidNamReferenceList_t MidNamDeviceModeRefs_t; typedef MidNamReferenceList_t MidiNamChannelNameSetAssignRefs_t; typedef MidNamReferenceList_t MidNamChannelNameSetRefs_t; struct MidNamReferencesList { MidNamNoteNameListRefs_t noteNameListObjs; MidiNamValNamesRefs_t valNamesObjs; MidiNamCtrlsRefs_t ctrlsObjs; MidiNamPatchNameListRefs_t patchNameListObjs; MidNamDeviceModeRefs_t deviceModeObjs; MidiNamChannelNameSetAssignRefs_t channelNameSetAssignObjs; MidNamChannelNameSetRefs_t channelNameSetObjs; bool resolveReferences() const; }; //----------------------------------------------------------------- class MidNamMIDINameDocument { private: MidNamAuthor _author; MidNamMasterDeviceNamesList _masterDeviceNamesList; MidNamExtendingDeviceNamesList _extendingDeviceNamesList; MidNamDeviceModeList _standardDeviceModeList; public: MidNamMIDINameDocument() {} void clear() { _author.clear(); _masterDeviceNamesList.clear(); _extendingDeviceNamesList.clear(); _standardDeviceModeList.clear(); } bool gatherReferences(MidNamReferencesList* refs) const; // This gathers all references and objects and resolves the references. // Run this AFTER the document has been fully read. bool resolveReferences(); MidNamAuthor& author() { return _author; } MidNamMasterDeviceNamesList& masterDeviceNamesList() { return _masterDeviceNamesList; } MidNamExtendingDeviceNamesList& extendingDeviceNamesList() { return _extendingDeviceNamesList; } MidNamDeviceModeList& standardDeviceModeList() { return _standardDeviceModeList; } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int channel, int patch) const; const MidiNamPatchBankList* getPatchBanks(int channel) const; // Find the list of controllers for a channel and/or patch. // If channel is -1 or patch is don't care, it looks for defaults. const MidiControllerList* getControllers(int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; class MidNamMIDIName { private: MidNamMIDINameDocument _MIDINameDocument; // Whether the document contains anything // ie MIDINameDocument tag was found and loaded OK. bool _isEmpty; public: MidNamMIDIName() : _isEmpty(true) {} bool isEmpty () const { return _isEmpty; } void clear() { _MIDINameDocument.clear(); _isEmpty = true; } // This gathers all references and objects and resolves the references. // Run this AFTER the document has been fully read. bool resolveReferences() { return _MIDINameDocument.resolveReferences(); } void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); // Like find but if bank high or low are valid, // it searches for a match using them. // Whereas find searches for an exact match. const MidiNamPatch* findPatch(int channel, int patch) const; const MidiNamPatchBankList* getPatchBanks(int channel) const; // Find the list of controllers for a channel and/or patch. // If channel is -1 or patch is don't care, it looks for defaults. const MidiControllerList* getControllers(int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; class MidNamMIDINameDocumentList : public std::list { public: void write(int level, MusECore::Xml& xml) const; bool read(MusECore::Xml& xml); }; typedef MidNamMIDINameDocumentList::iterator iMidNamMIDINameDocumentList; typedef MidNamMIDINameDocumentList::const_iterator ciMidNamMIDINameDocumentList; //----------------------------------------------------------------- } // namespace MusECore #endif muse-4.0.0/src/libs/mpevent/000077500000000000000000000000001404105311000156475ustar00rootroot00000000000000muse-4.0.0/src/libs/mpevent/CMakeLists.txt000066400000000000000000000033701404105311000204120ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # mpevent/CMakeLists.txt # (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB mpevent_source_files mpevent.cpp ) ## ## Define target ## add_library ( mpevent_module SHARED ${mpevent_source_files} ) ## ## Compilation flags and target name ## set_target_properties( mpevent_module PROPERTIES OUTPUT_NAME muse_mpevent_module ) ## ## Linkage ## target_link_libraries(mpevent_module evdata_module sysex_helper_module ) ## ## Install location ## install(TARGETS mpevent_module DESTINATION ${MusE_MODULES_DIR} ) # if ( ${MODULES_BUILD} STREQUAL SHARED ) # install(TARGETS # mpevent_module # DESTINATION ${MusE_MODULES_DIR} # ) # endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/libs/mpevent/mpevent.cpp000066400000000000000000000265721404105311000200450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mpevent.cpp,v 1.6.2.2 2009/11/25 09:09:43 terminator356 Exp $ // // (C) Copyright 2002-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //========================================================= #include #include #include "mpevent.h" #include "midictrl_consts.h" #include "muse/midi_consts.h" namespace MusECore { template TypedMemoryPool audioMPEventRTalloc::pool; template TypedMemoryPool seqMPEventRTalloc::pool; //template TypedMemoryPool MPEventRTalloc::audio_pool; //template TypedMemoryPool MPEventRTalloc::seq_pool; //--------------------------------------------------------- // MEvent //--------------------------------------------------------- MEvent::MEvent(unsigned t, int port, int tpe, const unsigned char* data, int len) { _time = t; _port = port; edata.setData(data, len); _type = tpe; _loopNum = 0; setChannel(0); } //--------------------------------------------------------- // sortingWeight //--------------------------------------------------------- int MEvent::sortingWeight() const { // Sorting weight initially worked out by Tim E. Real // Sorted here by most popular for quickest response. switch(_type) { case ME_NOTEON: if(_b == 0) // Is it really a note off? return 7; return 98; case ME_NOTEOFF: return 7; case ME_PITCHBEND: return 25; case ME_CONTROLLER: switch(_a) { case CTRL_PROGRAM: return 21; default: return 24; } case ME_PROGRAM: return 20; case ME_CLOCK: return 0; case ME_MTC_QUARTER: return 1; case ME_TICK: return 2; case ME_SENSE: return 3; case ME_SYSEX_END: return 4; case ME_AFTERTOUCH: return 5; case ME_POLYAFTER: return 6; case ME_STOP: return 8; case ME_SONGSEL: return 9; case ME_SYSEX: return 18; case ME_META: switch(_a) { case ME_META_TEXT_2_COPYRIGHT: return 10; case ME_META_TEXT_1_COMMENT: return 11; case ME_META_PORT_CHANGE: return 12; case ME_META_TEXT_9_DEVICE_NAME: return 13; case ME_META_CHANNEL_CHANGE: return 14; case ME_META_TEXT_3_TRACK_NAME: return 15; case ME_META_TEXT_F_TRACK_COMMENT: return 16; case ME_META_TEXT_0_SEQUENCE_NUMBER: return 17; case ME_META_TEXT_4_INSTRUMENT_NAME: return 19; case ME_META_END_OF_TRACK: return 99; default: return 97; } case ME_TUNE_REQ: return 22; case ME_SONGPOS: return 23; case ME_START: return 26; case ME_CONTINUE: return 27; } fprintf(stderr, "FIXME: MEvent::sortingWeight: unknown event type:%d\n", _type); return 100; } //--------------------------------------------------------- // operator < //--------------------------------------------------------- bool MEvent::operator<(const MEvent& e) const { // Be careful about being any more specific than these checks. // Be sure to examine the add() method, which might be upset by it. if (time() != e.time()) return time() < e.time(); if (port() != e.port()) return port() < e.port(); // play note off events first to prevent overlapping // notes if (channel() == e.channel()) return sortingWeight() < e.sortingWeight(); int map[16] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 14, 15 }; return map[channel()] < map[e.channel()]; } //--------------------------------------------------------- // translateCtrlNum //--------------------------------------------------------- int MEvent::translateCtrlNum() const { const int da = dataA(); int ctrl = -1; switch(type()) { case ME_CONTROLLER: switch(da) { case CTRL_HBANK: ctrl = CTRL_PROGRAM; break; case CTRL_LBANK: ctrl = CTRL_PROGRAM; break; case CTRL_PROGRAM: ctrl = CTRL_PROGRAM; break; default: ctrl = da; break; } break; case ME_POLYAFTER: { const int pitch = da & 0x7f; ctrl = (CTRL_POLYAFTER & ~0xff) | pitch; } break; case ME_AFTERTOUCH: ctrl = CTRL_AFTERTOUCH; break; case ME_PITCHBEND: ctrl = CTRL_PITCH; break; case ME_PROGRAM: ctrl = CTRL_PROGRAM; break; default: break; } return ctrl; } //--------------------------------------------------------- // add // Optimize to eliminate duplicate events at the SAME time. // It will not handle duplicate events at DIFFERENT times. // Replaces event if it already exists. //--------------------------------------------------------- void MPEventList::add(const MidiPlayEvent& ev) { MPEventListRangePair_t range = equal_range(ev); for(iMPEvent impe = range.first; impe != range.second; ++impe) { // Note that (multi)set iterators are constant and can't be modified. // The only option is to erase the old item(s), then insert a new item. const MidiPlayEvent& l_ev = *impe; // The type, time, port, and channel should already be equal, according to the operator< method. switch(ev.type()) { case ME_NOTEON: case ME_NOTEOFF: case ME_CONTROLLER: case ME_POLYAFTER: // Are the notes or controller numbers the same? if(l_ev.dataA() == ev.dataA()) { // If the velocities or values are the same, just ignore. if(l_ev.dataB() == ev.dataB()) return; // Erase the item, and insert the replacement. erase(impe); insert(ev); return; } break; case ME_PROGRAM: case ME_AFTERTOUCH: case ME_PITCHBEND: case ME_SONGPOS: case ME_MTC_QUARTER: case ME_SONGSEL: // If the values are the same, just ignore. if(l_ev.dataA() == ev.dataA()) return; // Erase the item, and insert the replacement. erase(impe); insert(ev); return; break; case ME_SYSEX: { const int len = ev.len(); // If length is zero there's no point in adding this sysex. Just return. if(len == 0) return; } break; case ME_CLOCK: case ME_START: case ME_CONTINUE: case ME_STOP: case ME_SYSEX_END: case ME_TUNE_REQ: case ME_TICK: case ME_SENSE: // Event already exists. Ignore the event to be added. return; break; case ME_META: // TODO: This could be reset, or might be a meta, depending on MPEventList usage. break; } } insert(ev); } //--------------------------------------------------------- // add // Optimize to eliminate duplicate events at the SAME time. // It will not handle duplicate events at DIFFERENT times. // Replaces event if it already exists. //--------------------------------------------------------- void SeqMPEventList::add(const MidiPlayEvent& ev) { SeqMPEventListRangePair_t range = equal_range(ev); for(iSeqMPEvent impe = range.first; impe != range.second; ++impe) { // Note that (multi)set iterators are constant and can't be modified. // The only option is to erase the old item(s), then insert a new item. const MidiPlayEvent& l_ev = *impe; // The type, time, port, and channel should already be equal, according to the operator< method. switch(ev.type()) { case ME_NOTEON: case ME_NOTEOFF: case ME_CONTROLLER: case ME_POLYAFTER: // Are the notes or controller numbers the same? if(l_ev.dataA() == ev.dataA()) { // If the velocities or values are the same, just ignore. if(l_ev.dataB() == ev.dataB()) return; // Erase the item, and insert the replacement. erase(impe); insert(ev); return; } break; case ME_PROGRAM: case ME_AFTERTOUCH: case ME_PITCHBEND: case ME_SONGPOS: case ME_MTC_QUARTER: case ME_SONGSEL: // If the values are the same, just ignore. if(l_ev.dataA() == ev.dataA()) return; // Erase the item, and insert the replacement. erase(impe); insert(ev); return; break; case ME_SYSEX: { const int len = ev.len(); // If length is zero there's no point in adding this sysex. Just return. if(len == 0) return; } break; case ME_CLOCK: case ME_START: case ME_CONTINUE: case ME_STOP: case ME_SYSEX_END: case ME_TUNE_REQ: case ME_TICK: case ME_SENSE: // Event already exists. Ignore the event to be added. return; break; case ME_META: // TODO: This could be reset, or might be a meta, depending on MPEventList usage. break; } } insert(ev); } //--------------------------------------------------------- // put // return true on fifo overflow //--------------------------------------------------------- bool MidiRecFifo::put(const MidiRecordEvent& event) { if (size < MIDI_REC_FIFO_SIZE) { fifo[wIndex] = event; wIndex = (wIndex + 1) % MIDI_REC_FIFO_SIZE; ++size; return false; } return true; } //--------------------------------------------------------- // get //--------------------------------------------------------- MidiRecordEvent MidiRecFifo::get() { MidiRecordEvent event(fifo[rIndex]); rIndex = (rIndex + 1) % MIDI_REC_FIFO_SIZE; --size; return event; } //--------------------------------------------------------- // peek //--------------------------------------------------------- const MidiRecordEvent& MidiRecFifo::peek(int n) { int idx = (rIndex + n) % MIDI_REC_FIFO_SIZE; return fifo[idx]; } //--------------------------------------------------------- // remove //--------------------------------------------------------- void MidiRecFifo::remove() { rIndex = (rIndex + 1) % MIDI_REC_FIFO_SIZE; --size; } } // namespace MusECore muse-4.0.0/src/libs/mpevent/mpevent.h000066400000000000000000000267631404105311000175140ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mpevent.h,v 1.8.2.5 2009/11/25 09:09:43 terminator356 Exp $ // // (C) Copyright 1999-2002 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MPEVENT_H__ #define __MPEVENT_H__ #include #include "evdata.h" #include "memory.h" #include // Play events ring buffer size #define MIDI_FIFO_SIZE 4096 // Record events ring buffer size #define MIDI_REC_FIFO_SIZE 256 namespace MusECore { //--------------------------------------------------------- // MEvent // baseclass for MidiPlayEvent and MidiRecordEvent //--------------------------------------------------------- //--------------------------------------------------------- // MEvent //--------------------------------------------------------- class MEvent { unsigned _time; EvData edata; unsigned char _port, _channel, _type; int _a, _b; int _loopNum; // The loop count when the note was recorded. public: MEvent() : _time(0), _port(0), _channel(0), _type(0), _a(0), _b(0), _loopNum(0) { } MEvent(const MEvent& e) : _time(e._time), edata(e.edata), _port(e._port), _channel(e._channel), _type(e._type), _a(e._a), _b(e._b), _loopNum(e._loopNum) { } MEvent(unsigned tm, int p, int c, int t, int a, int b) : _time(tm), _port(p), _channel(c & 0xf), _type(t), _a(a), _b(b), _loopNum(0) { } MEvent(unsigned t, int p, int type, const unsigned char* data, int len); MEvent(unsigned t, int p, int tpe, EvData d) : _time(t), edata(d), _port(p), _type(tpe), _loopNum(0) { } virtual ~MEvent() {} MEvent& operator=(const MEvent& ed) { _time = ed._time; edata = ed.edata; _port = ed._port; _channel = ed._channel; _type = ed._type; _a = ed._a; _b = ed._b; _loopNum = ed._loopNum; return *this; } int sortingWeight() const; int port() const { return _port; } int channel() const { return _channel; } int type() const { return _type; } int dataA() const { return _a; } int dataB() const { return _b; } unsigned time() const { return _time; } int loopNum() const { return _loopNum; } void setPort(int val) { _port = val; } void setChannel(int val) { _channel = val; } void setType(int val) { _type = val; } void setA(int val) { _a = val; } void setB(int val) { _b = val; } void setTime(unsigned val) { _time = val; } void setLoopNum(int n) { _loopNum = n; } const EvData& eventData() const { return edata; } unsigned char* data() { return edata.data(); } const unsigned char* constData() const { return edata.constData(); } int len() const { return edata.dataLen(); } void setData(const EvData& e) { edata = e; } void setData(const unsigned char* p, int len) { edata.setData(p, len); } bool isNote() const { return _type == 0x90; } bool isNoteOff() const { return (_type == 0x80)||(_type == 0x90 && _b == 0); } bool operator<(const MEvent&) const; bool isValid() const { return _type != 0; } // Returns a valid source controller number (above zero), // translated from the event to proper internal control type. // For example // ME_CONTROLLER + Data(A = CTRL_HBANK) = CTRL_PROGRAM // ME_CONTROLLER + Data(A = CTRL_LBANK) = CTRL_PROGRAM // ME_PROGRAM = CTRL_PROGRAM // ME_PITCHBEND = CTRL_PITCH // ME_CONTROLLER + Data(A = ctrl) = ctrl // Otherwise returns -1 if the event is not translatable to a controller, // or an error occurred. int translateCtrlNum() const; }; //--------------------------------------------------------- // MidiRecordEvent // allocated and deleted in midiseq thread context //--------------------------------------------------------- class MidiRecordEvent : public MEvent { private: unsigned int _tick; // To store tick when external sync is on, required besides frame. public: MidiRecordEvent() : MEvent() {} MidiRecordEvent(const MidiRecordEvent& e) : MEvent(e), _tick(e._tick) {} MidiRecordEvent(const MEvent& e) : MEvent(e), _tick(0) {} MidiRecordEvent(unsigned tm, int p, int c, int t, int a, int b) : MEvent(tm, p, c, t, a, b) {} MidiRecordEvent(unsigned t, int p, int tpe, const unsigned char* data, int len) : MEvent(t, p, tpe, data, len) {} MidiRecordEvent(unsigned t, int p, int type, EvData data) : MEvent(t, p, type, data) {} virtual ~MidiRecordEvent() {} MidiRecordEvent& operator=(const MidiRecordEvent& e) { MEvent::operator=(e); _tick = e._tick; return *this; } unsigned int tick() {return _tick;} void setTick(unsigned int tick) {_tick = tick;} }; //--------------------------------------------------------- // MidiPlayEvent // allocated and deleted in audio thread context //--------------------------------------------------------- class MidiPlayEvent : public MEvent { public: MidiPlayEvent() : MEvent() {} MidiPlayEvent(const MidiPlayEvent& e) : MEvent(e) {} MidiPlayEvent(const MEvent& e) : MEvent(e) {} MidiPlayEvent(unsigned tm, int p, int c, int t, int a, int b) : MEvent(tm, p, c, t, a, b) {} MidiPlayEvent(unsigned t, int p, int type, const unsigned char* data, int len) : MEvent(t, p, type, data, len) {} MidiPlayEvent(unsigned t, int p, int type, EvData data) : MEvent(t, p, type, data) {} virtual ~MidiPlayEvent() {} MidiPlayEvent& operator=(const MidiPlayEvent& e) { MEvent::operator=(e); return *this; } }; //--------------------------------------------------------- // MidiRecFifo //--------------------------------------------------------- class MidiRecFifo { MidiRecordEvent fifo[MIDI_REC_FIFO_SIZE]; volatile int size; int wIndex; int rIndex; public: MidiRecFifo() { clear(); } bool put(const MidiRecordEvent& event); // returns true on fifo overflow MidiRecordEvent get(); const MidiRecordEvent& peek(int = 0); void remove(); bool isEmpty() const { return size == 0; } void clear() { size = 0, wIndex = 0, rIndex = 0; } int getSize() const { return size; } }; //--------------------------------------------------------- // audioMPEventRTalloc //--------------------------------------------------------- template class audioMPEventRTalloc { private: static TypedMemoryPool pool; public: typedef T value_type; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; pointer address(reference x) const { return &x; } const_pointer address(const_reference x) const { return &x; } audioMPEventRTalloc() { } template audioMPEventRTalloc(const audioMPEventRTalloc&) {} ~audioMPEventRTalloc() {} pointer allocate(size_type n, void * = 0) { return static_cast(pool.alloc(n)); } void deallocate(pointer p, size_type n) { pool.free(p, n); } audioMPEventRTalloc& operator=(const audioMPEventRTalloc&) { return *this; } void construct(pointer p, const T& val) { new ((T*) p) T(val); } void destroy(pointer p) { p->~T(); } size_type max_size() const { return size_t(-1); } template struct rebind { typedef audioMPEventRTalloc other; }; template audioMPEventRTalloc& operator=(const audioMPEventRTalloc&) { return *this; } }; //--------------------------------------------------------- // seqMPEventRTalloc //--------------------------------------------------------- template class seqMPEventRTalloc { private: static TypedMemoryPool pool; public: typedef T value_type; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; pointer address(reference x) const { return &x; } const_pointer address(const_reference x) const { return &x; } seqMPEventRTalloc() { } template seqMPEventRTalloc(const seqMPEventRTalloc&) {} ~seqMPEventRTalloc() {} pointer allocate(size_type n, void * = 0) { return static_cast(pool.alloc(n)); } void deallocate(pointer p, size_type n) { pool.free(p, n); } seqMPEventRTalloc& operator=(const seqMPEventRTalloc&) { return *this; } void construct(pointer p, const T& val) { new ((T*) p) T(val); } void destroy(pointer p) { p->~T(); } size_type max_size() const { return size_t(-1); } template struct rebind { typedef seqMPEventRTalloc other; }; template seqMPEventRTalloc& operator=(const seqMPEventRTalloc&) { return *this; } }; //--------------------------------------------------------- // MPEventList // memory allocation in audio thread domain //--------------------------------------------------------- typedef std::multiset, audioMPEventRTalloc > MPEL; class MPEventList : public MPEL { public: // Optimize to eliminate duplicate events at the SAME time. // It will not handle duplicate events at DIFFERENT times. // Replaces event if it already exists. void add(const MidiPlayEvent& ev); }; typedef MPEventList::iterator iMPEvent; typedef MPEventList::const_iterator ciMPEvent; typedef std::pair MPEventListRangePair_t; //--------------------------------------------------------- // SeqMPEventList // memory allocation in sequencer thread domain //--------------------------------------------------------- typedef std::multiset, seqMPEventRTalloc > SMPEL; class SeqMPEventList : public SMPEL { public: // Optimize to eliminate duplicate events at the SAME time. // It will not handle duplicate events at DIFFERENT times. // Replaces event if it already exists. void add(const MidiPlayEvent& ev); }; typedef SeqMPEventList::iterator iSeqMPEvent; typedef SeqMPEventList::const_iterator ciSeqMPEvent; typedef std::pair SeqMPEventListRangePair_t; } // namespace MusECore #endif muse-4.0.0/src/libs/plugin/000077500000000000000000000000001404105311000154675ustar00rootroot00000000000000muse-4.0.0/src/libs/plugin/CMakeLists.txt000066400000000000000000000066541404105311000202420ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # plugin/CMakeLists.txt # (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB plugin_rdf_source_files plugin_rdf.cpp ) file (GLOB plugin_scan_source_files plugin_scan.cpp ) file (GLOB plugin_list_source_files plugin_list.cpp ) file (GLOB plugin_cache_reader_source_files plugin_cache_reader.cpp ) file (GLOB plugin_cache_writer_source_files plugin_cache_writer.cpp ) ## ## Define target ## add_library ( plugin_rdf_module SHARED ${plugin_rdf_source_files} ) add_library ( plugin_scan_module SHARED ${plugin_scan_source_files} ) add_library ( plugin_list_module SHARED ${plugin_list_source_files} ) add_library ( plugin_cache_reader_module SHARED ${plugin_cache_reader_source_files} ) add_library ( plugin_cache_writer_module SHARED ${plugin_cache_writer_source_files} ) ## ## Compilation flags and target name ## set_target_properties( plugin_rdf_module PROPERTIES OUTPUT_NAME muse_plugin_rdf_module ) set_target_properties( plugin_scan_module PROPERTIES OUTPUT_NAME muse_plugin_scan_module ) set_target_properties( plugin_list_module PROPERTIES OUTPUT_NAME muse_plugin_list_module ) set_target_properties( plugin_cache_reader_module PROPERTIES OUTPUT_NAME muse_plugin_cache_reader_module ) set_target_properties( plugin_cache_writer_module PROPERTIES OUTPUT_NAME muse_plugin_cache_writer_module ) target_link_libraries(plugin_rdf_module ${QT_LIBRARIES} ) target_link_libraries(plugin_scan_module #xml_module ${QT_LIBRARIES} ) target_link_libraries(plugin_list_module #xml_module plugin_scan_module #${QT_LIBRARIES} ) target_link_libraries(plugin_cache_reader_module plugin_scan_module plugin_list_module xml_module #${LRDF_LIBRARIES} #${QT_LIBRARIES} ) target_link_libraries(plugin_cache_writer_module plugin_rdf_module plugin_scan_module plugin_list_module plugin_cache_reader_module xml_module ${LRDF_LIBRARIES} #${QT_LIBRARIES} ) if(LV2_SUPPORT) target_link_libraries(plugin_cache_writer_module ${LILV_LIBRARIES}) endif(LV2_SUPPORT) ## ## Install location ## install(TARGETS plugin_rdf_module plugin_scan_module plugin_list_module plugin_cache_reader_module plugin_cache_writer_module DESTINATION ${MusE_MODULES_DIR} ) muse-4.0.0/src/libs/plugin/plugin_cache_reader.cpp000066400000000000000000001017051404105311000221420ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // plugin_cache_reader.cpp // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 // For sorting port enum values. #include #include #include #include "plugin_cache_reader.h" #include "xml.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_PLUGIN_SCAN(dev, format, args...) // std::fprintf(dev, format, ##args); //#define PORTS_ARE_SINGLE_LINE_TAGS 1 namespace MusEPlugin { void setPluginScanFileInfo(const QString& filename, PluginScanInfoStruct* info) { if(filename.isEmpty()) return; const QFileInfo fi(filename); // AppImage: Adjust dynamic path prefix of internal plugins const QByteArray appDir = qgetenv("APPDIR"); QString path = fi.path(); QString absPath = fi.absolutePath(); if (!appDir.isEmpty()) { const QString intLibPath = QString(LIBDIR); int idx; if ((idx = path.indexOf(intLibPath)) > 0) { path.remove(0, idx); path = appDir + path; } if ((idx = absPath.indexOf(intLibPath)) > 0) { absPath.remove(0, idx); absPath = appDir + absPath; } } info->_completeBaseName = PLUGIN_SET_QSTRING(fi.completeBaseName()); info->_baseName = PLUGIN_SET_QSTRING(fi.baseName()); info->_suffix = PLUGIN_SET_QSTRING(fi.suffix()); info->_completeSuffix = PLUGIN_SET_QSTRING(fi.completeSuffix()); info->_absolutePath = PLUGIN_SET_QSTRING(absPath); info->_path = PLUGIN_SET_QSTRING(path); // info->_absolutePath = PLUGIN_SET_QSTRING(fi.absolutePath()); // info->_path = PLUGIN_SET_QSTRING(fi.path()); info->_fileTime = fi.lastModified().toMSecsSinceEpoch(); } //--------------------------------------------------------- // readPluginScanInfoPortEnum // Returns true on error //--------------------------------------------------------- bool readPluginScanInfoPortEnum(MusECore::Xml& xml, PluginPortEnumValue* enum_val) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return true; case MusECore::Xml::TagStart: xml.unknown("readPort"); break; case MusECore::Xml::Attribut: // As attributes on one line... if (tag == "label") enum_val->_label = PLUGIN_SET_QSTRING(xml.s2()); else if (tag == "val") enum_val->_value = xml.s2().toFloat(); break; case MusECore::Xml::TagEnd: if (tag == "enumVal") { return false; } return true; default: break; } } return true; } //--------------------------------------------------------- // readPluginPortEnumValMap // Returns true on error //--------------------------------------------------------- bool readPluginPortEnumValMap(MusECore::Xml& xml, PortEnumValueMap* val_map) { unsigned int port_idx = 0; // Map for ensuring the enumerations are sorted by increasing value. std::map > sort_map; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return true; case MusECore::Xml::TagStart: if (tag == "enumVal") { PluginPortEnumValue enum_val; if(!readPluginScanInfoPortEnum(xml, &enum_val)) sort_map.insert(std::pair(enum_val._value, enum_val)); } else xml.unknown("readPort"); break; case MusECore::Xml::Attribut: // As attributes on one line... if (tag == "idx") port_idx = xml.s2().toUInt(); break; case MusECore::Xml::TagEnd: if (tag == "portEnumValMap") { // Copy the sorted enumeration values to a new vector enumeration list. EnumValueList val_list; for(std::map>::const_iterator iel = sort_map.begin(); iel != sort_map.end(); ++iel) { val_list.push_back(iel->second); } // Insert the new value list into the map. // Ther can be only one value list per port index. if(!val_list.empty()) val_map->insert(PortEnumValueMapPair(port_idx, val_list)); return false; } return true; default: break; } } return true; } //--------------------------------------------------------- // readPluginScanInfoPort // Returns true on error //--------------------------------------------------------- bool readPluginScanInfoPort(MusECore::Xml& xml, PluginScanInfoStruct* info) { PluginPortInfo port_info; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return true; case MusECore::Xml::TagStart: #ifndef PORTS_ARE_SINGLE_LINE_TAGS // As a tag with attributes... if (tag == "flags") port_info._flags = PluginPortInfo::PortFlags_t(xml.parseInt()); else if (tag == "valFlags") port_info._valueFlags = PluginPortInfo::PortValueFlags_t(xml.parseInt()); else if (tag == "min") port_info._min = xml.parseFloat(); else if (tag == "max") port_info._max = xml.parseFloat(); else if (tag == "def") port_info._defaultVal = xml.parseFloat(); else if (tag == "step") port_info._step = xml.parseFloat(); else if (tag == "smallStep") port_info._smallStep = xml.parseFloat(); else if (tag == "largeStep") port_info._largeStep = xml.parseFloat(); else #endif xml.unknown("readPort"); break; case MusECore::Xml::Attribut: // As attributes on one line... if (tag == "name") port_info._name = PLUGIN_SET_QSTRING(xml.s2()); else if (tag == "idx") port_info._index = xml.s2().toULong(); else if (tag == "type") port_info._type = PluginPortInfo::PortType_t(xml.s2().toInt()); else if (tag == "symbol") port_info._symbol = PLUGIN_SET_QSTRING(xml.s2()); #ifdef PORTS_ARE_SINGLE_LINE_TAGS else if (tag == "flags") port_info._flags = PluginPortInfo::PortFlags_t(xml.s2().toInt()); else if (tag == "valFlags") port_info._valueFlags = PluginPortInfo::PortValueFlags_t(xml.s2().toInt()); else if (tag == "min") port_info._min = xml.s2().toFloat(); else if (tag == "max") port_info._max = xml.s2().toFloat(); else if (tag == "def") port_info._defaultVal = xml.s2().toFloat(); else if (tag == "step") port_info._step = xml.s2().toFloat(); else if (tag == "smallStep") port_info._smallStep = xml.s2().toFloat(); else if (tag == "largeStep") port_info._largeStep = xml.s2().toFloat(); #endif break; case MusECore::Xml::TagEnd: if (tag == "port") { info->_portList.push_back(port_info); return false; } return true; default: break; } } return true; } //--------------------------------------------------------- // readPluginScanInfo // return true on error //--------------------------------------------------------- bool readPluginScanInfo(MusECore::Xml& xml, PluginScanInfoStruct* info, bool readPorts, bool readEnums) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return true; case MusECore::Xml::TagStart: if (tag == "uri") info->_uri = PLUGIN_SET_QSTRING(xml.parse1()); else if (tag == "filetime") info->_fileTime = xml.parseLongLong(); else if (tag == "fileIsBad") info->_fileIsBad = xml.parseInt(); else if (tag == "type") info->_type = PluginScanInfoStruct::PluginType(xml.parseInt()); else if (tag == "class") info->_class = PluginScanInfoStruct::PluginClass(xml.parseInt()); else if (tag == "uniqueID") info->_uniqueID = xml.parseUInt(); else if (tag == "subID") info->_subID = xml.parseInt(); else if (tag == "name") info->_name = PLUGIN_SET_QSTRING(xml.parse1()); else if (tag == "description") info->_description = PLUGIN_SET_QSTRING(xml.parse1()); else if (tag == "version") info->_version = PLUGIN_SET_QSTRING(xml.parse1()); else if (tag == "maker") info->_maker = PLUGIN_SET_QSTRING(xml.parse1()); else if (tag == "copyright") info->_copyright = PLUGIN_SET_QSTRING(xml.parse1()); else if (tag == "apiVersionMajor") info->_apiVersionMajor = xml.parseInt(); else if (tag == "apiVersionMinor") info->_apiVersionMinor = xml.parseInt(); else if (tag == "pluginVersionMajor") info->_pluginVersionMajor = xml.parseInt(); else if (tag == "pluginVersionMinor") info->_pluginVersionMinor = xml.parseInt(); else if (tag == "pluginFlags") info->_pluginFlags = xml.parseInt(); else if (tag == "portCount") info->_portCount = xml.parseUInt(); else if (tag == "inports") info->_inports = xml.parseUInt(); else if (tag == "outports") info->_outports = xml.parseUInt(); else if (tag == "ctlInports") info->_controlInPorts = xml.parseUInt(); else if (tag == "ctlOutports") info->_controlOutPorts = xml.parseUInt(); else if (tag == "evInports") info->_eventInPorts = xml.parseUInt(); else if (tag == "evOutports") info->_eventOutPorts = xml.parseUInt(); else if (tag == "freewheelPortIdx") info->_freewheelPortIdx = xml.parseUInt(); else if (tag == "latencyPortIdx") info->_latencyPortIdx = xml.parseUInt(); else if (tag == "requiredFeatures") info->_requiredFeatures = xml.parseInt(); else if (tag == "vstPluginFlags") info->_vstPluginFlags = xml.parseInt(); else if (tag == "uiFilename") info->_uiFilename = PLUGIN_SET_QSTRING(xml.parse1()); else if (tag == "port") { if(readPorts) readPluginScanInfoPort(xml, info); } else if (tag == "portEnumValMap") { if(readEnums) readPluginPortEnumValMap(xml, &info->_portEnumValMap); } else xml.unknown("PluginScanInfo"); break; case MusECore::Xml::Attribut: if (tag == "file") { setPluginScanFileInfo(xml.s2(), info); } else if (tag == "label") { info->_label = PLUGIN_SET_QSTRING(xml.s2()); } break; case MusECore::Xml::TagEnd: if (tag == "plugin") { return false; } return true; default: break; } } return true; } //--------------------------------------------------------- // readPluginScan // return true on error //--------------------------------------------------------- bool readPluginScan(MusECore::Xml& xml, PluginScanList* list, bool readPorts, bool readEnums) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return true; case MusECore::Xml::TagStart: if (tag == "muse") break; else if (tag == "plugin") { PluginScanInfoStruct info; // read returns true on error. if(!readPluginScanInfo(xml, &info, readPorts, readEnums)) { // We must include all plugins. list->add(new PluginScanInfo(info)); } break; } else xml.unknown("readPluginScan"); break; case MusECore::Xml::Attribut: if (tag == "version") { int major = xml.s2().section('.', 0, 0).toInt(); int minor = xml.s2().section('.', 1, 1).toInt(); xml.setVersion(major, minor); } break; case MusECore::Xml::TagEnd: if (tag == "muse") { return false; } return true; default: break; } } return true; } //--------------------------------------------------------- // pluginGetLadspaDirectories //--------------------------------------------------------- QStringList pluginGetLadspaDirectories(const QString& museGlobalLib) { QStringList sl; // Add our own LADSPA plugin directory... sl.append(museGlobalLib + QString("/plugins")); // Now add other directories... #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QString ladspaPath = qEnvironmentVariable("LADSPA_PATH"); #else QString ladspaPath = QString::fromLocal8Bit(qgetenv("LADSPA_PATH")); #endif if(ladspaPath.isEmpty()) { QString homePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); if(!homePath.isEmpty()) homePath += QString("/ladspa:") + homePath + QString("/.ladspa:"); ladspaPath = homePath + QString("/usr/local/lib64/ladspa:/usr/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa"); } if(!ladspaPath.isEmpty()) // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 sl.append(ladspaPath.split(":", Qt::SkipEmptyParts, Qt::CaseSensitive)); #else sl.append(ladspaPath.split(":", QString::SkipEmptyParts, Qt::CaseSensitive)); #endif return sl; } //--------------------------------------------------------- // pluginGetMessDirectories //--------------------------------------------------------- QStringList pluginGetMessDirectories(const QString& museGlobalLib) { QStringList sl; // Add our own MESS plugin directory... sl.append(museGlobalLib + QString("/synthi")); // Now add other directories... #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QString messPath = qEnvironmentVariable("MESS_PATH"); #else QString messPath = QString::fromLocal8Bit(qgetenv("MESS_PATH")); #endif if(messPath.isEmpty()) { QString homePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); if(!homePath.isEmpty()) homePath += QString("/MESS:"); messPath = homePath + QString("/usr/local/lib64/MESS:/usr/lib64/MESS:/usr/local/lib/MESS:/usr/lib/MESS"); } if(!messPath.isEmpty()) // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 sl.append(messPath.split(":", Qt::SkipEmptyParts, Qt::CaseSensitive)); #else sl.append(messPath.split(":", QString::SkipEmptyParts, Qt::CaseSensitive)); #endif return sl; } //--------------------------------------------------------- // pluginGetDssiDirectories //--------------------------------------------------------- QStringList pluginGetDssiDirectories() { QStringList sl; #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QString dssiPath = qEnvironmentVariable("DSSI_PATH"); #else QString dssiPath = QString::fromLocal8Bit(qgetenv("DSSI_PATH")); #endif if(dssiPath.isEmpty()) { QString homePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); if(!homePath.isEmpty()) homePath += QString("/dssi:") + homePath + QString("/.dssi:"); dssiPath = homePath + QString("/usr/local/lib64/dssi:/usr/lib64/dssi:/usr/local/lib/dssi:/usr/lib/dssi"); } if(!dssiPath.isEmpty()) // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 sl.append(dssiPath.split(":", Qt::SkipEmptyParts, Qt::CaseSensitive)); #else sl.append(dssiPath.split(":", QString::SkipEmptyParts, Qt::CaseSensitive)); #endif return sl; } //--------------------------------------------------------- // pluginGetLinuxVstDirectories Linux vst plugins (*.so) //--------------------------------------------------------- QStringList pluginGetLinuxVstDirectories() { QStringList sl; #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QString vstPath = qEnvironmentVariable("LXVST_PATH"); #else QString vstPath = QString::fromLocal8Bit(qgetenv("LXVST_PATH")); #endif if(vstPath.isEmpty()) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QString vstPath = qEnvironmentVariable("VST_PATH"); #else QString vstPath = QString::fromLocal8Bit(qgetenv("VST_PATH")); #endif if(vstPath.isEmpty()) { QString homePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); QString fin_path; if(!homePath.isEmpty()) { // On win, vst is usually where *.dll files are found. We don't want that with Linux *.so vst files. // Otherwise on Linux for example, vst is where Linux vst *.so files are found. // On win, lxvst should be safe, likely where Linux vst *.so files might be found (if that's even a thing!). #ifndef Q_OS_WIN fin_path += homePath + QString("/vst:"); #endif fin_path += homePath + QString("/lxvst:"); #ifndef Q_OS_WIN fin_path += homePath + QString("/.vst:"); #endif fin_path += homePath + QString("/.lxvst:"); } #ifndef Q_OS_WIN fin_path += QString("/usr/local/lib64/vst:"); #endif fin_path += QString("/usr/local/lib64/lxvst:"); #ifndef Q_OS_WIN fin_path += QString("/usr/local/lib/vst:"); #endif fin_path += QString("/usr/local/lib/lxvst:"); #ifndef Q_OS_WIN fin_path += QString("/usr/lib64/vst:"); #endif fin_path += QString("/usr/lib64/lxvst:"); #ifndef Q_OS_WIN fin_path += QString("/usr/lib/vst:"); #endif // No last colon. fin_path += QString("/usr/lib/lxvst"); vstPath = fin_path; } } if(!vstPath.isEmpty()) // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 sl.append(vstPath.split(":", Qt::SkipEmptyParts, Qt::CaseSensitive)); #else sl.append(vstPath.split(":", QString::SkipEmptyParts, Qt::CaseSensitive)); #endif return sl; } //--------------------------------------------------------- // pluginGetVstDirectories Win vst plugins (*.dll) //--------------------------------------------------------- QStringList pluginGetVstDirectories() { QStringList sl; #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QString vstPath = qEnvironmentVariable("VST_PATH"); #else QString vstPath = QString::fromLocal8Bit(qgetenv("VST_PATH")); #endif if(vstPath.isEmpty()) { // TODO: Refine this, and below, for Q_OS_WIN. Where exactly do we look though? QString homePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); if(!homePath.isEmpty()) { // On win, vst is usually where *.dll files are found. We don't want that with Linux *.so vst files. // Otherwise on Linux for example, vst is where Linux vst *.so files are found. #ifdef Q_OS_WIN homePath += QString("/vst:") + homePath + QString("/.vst"); #else homePath += QString("/vst win 32bit:") + homePath + QString("/.vst win 32bit"); #endif } vstPath = homePath; } if(!vstPath.isEmpty()) // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 sl.append(vstPath.split(":", Qt::SkipEmptyParts, Qt::CaseSensitive)); #else sl.append(vstPath.split(":", QString::SkipEmptyParts, Qt::CaseSensitive)); #endif return sl; } //--------------------------------------------------------- // pluginGetLv2Directories //--------------------------------------------------------- QStringList pluginGetLv2Directories() { QStringList sl; #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QString lv2Path = qEnvironmentVariable("LV2_PATH"); #else QString lv2Path = QString::fromLocal8Bit(qgetenv("LV2_PATH")); #endif if(lv2Path.isEmpty()) { QString homePath = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); if(!homePath.isEmpty()) homePath += QString("/lv2:") + homePath + QString("/.lv2:"); lv2Path = homePath + QString("/usr/local/lib64/lv2:/usr/lib64/lv2:/usr/local/lib/lv2:/usr/lib/lv2"); } if(!lv2Path.isEmpty()) // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 sl.append(lv2Path.split(":", Qt::SkipEmptyParts, Qt::CaseSensitive)); #else sl.append(lv2Path.split(":", QString::SkipEmptyParts, Qt::CaseSensitive)); #endif return sl; } //--------------------------------------------------------- // pluginGetDirectories //--------------------------------------------------------- QStringList pluginGetDirectories(const QString& museGlobalLib, PluginScanInfoStruct::PluginType type) { switch(type) { case PluginScanInfoStruct::PluginTypeDSSI: case PluginScanInfoStruct::PluginTypeDSSIVST: return pluginGetDssiDirectories(); break; case PluginScanInfoStruct::PluginTypeMESS: return pluginGetMessDirectories(museGlobalLib); break; case PluginScanInfoStruct::PluginTypeLADSPA: return pluginGetLadspaDirectories(museGlobalLib); break; case PluginScanInfoStruct::PluginTypeLinuxVST: return pluginGetLinuxVstDirectories(); break; case PluginScanInfoStruct::PluginTypeLV2: return pluginGetLv2Directories(); break; case PluginScanInfoStruct::PluginTypeVST: return pluginGetVstDirectories(); break; case PluginScanInfoStruct::PluginTypeUnknown: case PluginScanInfoStruct::PluginTypeNone: case PluginScanInfoStruct::PluginTypeAll: break; } return QStringList(); } //--------------------------------------------------------- // pluginCacheFileExists // Returns true if the cache file(s) for the given type(s) exist. //--------------------------------------------------------- const char* pluginCacheFilename(PluginScanInfoStruct::PluginType type) { switch(type) { case PluginScanInfoStruct::PluginTypeDSSI: case PluginScanInfoStruct::PluginTypeDSSIVST: return "dssi_plugins.scan"; break; case PluginScanInfoStruct::PluginTypeMESS: return "mess_plugins.scan"; break; case PluginScanInfoStruct::PluginTypeLADSPA: return "ladspa_plugins.scan"; break; case PluginScanInfoStruct::PluginTypeLinuxVST: return "linux_vst_plugins.scan"; break; // SPECIAL for LV2: Obsolete. Cache file not used any more. // Keep so we can delete old files. case PluginScanInfoStruct::PluginTypeLV2: return "lv2_plugins.scan"; break; case PluginScanInfoStruct::PluginTypeVST: return "vst_plugins.scan"; break; case PluginScanInfoStruct::PluginTypeUnknown: return "unknown_plugins.scan"; break; case PluginScanInfoStruct::PluginTypeNone: case PluginScanInfoStruct::PluginTypeAll: return ""; break; } return ""; } //--------------------------------------------------------- // pluginCacheFileExists //--------------------------------------------------------- PluginScanInfoStruct::PluginType pluginCacheFileExists( const QString& path, PluginScanInfoStruct::PluginType type) { const QFile targ_qfile(path + '/' + QString(pluginCacheFilename(type))); if(targ_qfile.exists()) return type; return PluginScanInfoStruct::PluginTypeNone; } //--------------------------------------------------------- // pluginCacheFilesExist //--------------------------------------------------------- PluginScanInfoStruct::PluginType_t pluginCacheFilesExist( const QString& path, PluginScanInfoStruct::PluginType_t types) { PluginScanInfoStruct::PluginType_t res = PluginScanInfoStruct::PluginTypeNone; if(types & (PluginScanInfoStruct::PluginTypeDSSI | PluginScanInfoStruct::PluginTypeDSSIVST)) { if(pluginCacheFileExists(path, PluginScanInfoStruct::PluginTypeDSSI) == PluginScanInfoStruct::PluginTypeDSSI) res |= (PluginScanInfoStruct::PluginTypeDSSI | PluginScanInfoStruct::PluginTypeDSSIVST); } if(types & PluginScanInfoStruct::PluginTypeMESS) res |= pluginCacheFileExists(path, PluginScanInfoStruct::PluginTypeMESS); if(types & PluginScanInfoStruct::PluginTypeLADSPA) res |= pluginCacheFileExists(path, PluginScanInfoStruct::PluginTypeLADSPA); if(types & PluginScanInfoStruct::PluginTypeLinuxVST) res |= pluginCacheFileExists(path, PluginScanInfoStruct::PluginTypeLinuxVST); if(types & PluginScanInfoStruct::PluginTypeLV2) res |= pluginCacheFileExists(path, PluginScanInfoStruct::PluginTypeLV2); if(types & PluginScanInfoStruct::PluginTypeVST) res |= pluginCacheFileExists(path, PluginScanInfoStruct::PluginTypeVST); if(types & PluginScanInfoStruct::PluginTypeUnknown) res |= pluginCacheFileExists(path, PluginScanInfoStruct::PluginTypeUnknown); return res; } //--------------------------------------------------------- // readPluginCacheFile //--------------------------------------------------------- bool readPluginCacheFile( const QString& path, PluginScanList* list, bool readPorts, bool readEnums, PluginScanInfoStruct::PluginType type ) { if(!pluginCacheFileExists(path, type)) return false; bool res = false; const QString targ_filepath = path + "/" + QString(pluginCacheFilename(type)); QFile targ_qfile(targ_filepath); // Cache file already existed. Open it for reading. if(!targ_qfile.open(QIODevice::ReadOnly | QIODevice::Text)) { std::fprintf(stderr, "readPluginCacheFile: targ_qfile.open() failed: filename:%s\n", targ_filepath.toLatin1().constData()); } else { MusECore::Xml xml(&targ_qfile); // Returns true on error. if(readPluginScan(xml, list, readPorts, readEnums)) { std::fprintf(stderr, "readPluginCacheFile: readPluginScan failed: filename:%s\n", targ_filepath.toLatin1().constData()); } DEBUG_PLUGIN_SCAN(stderr, "readPluginCacheFile: targ_qfile closing filename:%s\n", filename.toLatin1().constData()); targ_qfile.close(); res = true; } return res; } //--------------------------------------------------------- // readPluginCacheFiles //--------------------------------------------------------- bool readPluginCacheFiles( const QString& path, PluginScanList* list, bool readPorts, bool readEnums, PluginScanInfoStruct::PluginType_t types) { bool res = true; if(types & (PluginScanInfoStruct::PluginTypeDSSI | PluginScanInfoStruct::PluginTypeDSSIVST)) { if(!readPluginCacheFile(path, list, readPorts, readEnums, PluginScanInfoStruct::PluginTypeDSSI)) res = false; } if(types & PluginScanInfoStruct::PluginTypeMESS) { if(!readPluginCacheFile(path, list, readPorts, readEnums, PluginScanInfoStruct::PluginTypeMESS)) res = false; } if(types & PluginScanInfoStruct::PluginTypeLADSPA) { if(!readPluginCacheFile(path, list, readPorts, readEnums, PluginScanInfoStruct::PluginTypeLADSPA)) res = false; } if(types & PluginScanInfoStruct::PluginTypeLinuxVST) { if(!readPluginCacheFile(path, list, readPorts, readEnums, PluginScanInfoStruct::PluginTypeLinuxVST)) res = false; } // SPECIAL for LV2: No need for a cache file. Do not read one here. //if(types & PluginScanInfoStruct::PluginTypeLV2) //{ // if(!readPluginCacheFile(path, list, readPorts, readEnums, PluginScanInfoStruct::PluginTypeLV2)) // res = false; //} if(types & PluginScanInfoStruct::PluginTypeVST) { if(!readPluginCacheFile(path, list, readPorts, readEnums, PluginScanInfoStruct::PluginTypeVST)) res = false; } if(types & PluginScanInfoStruct::PluginTypeUnknown) { if(!readPluginCacheFile(path, list, readPorts, readEnums, PluginScanInfoStruct::PluginTypeUnknown)) res = false; } return res; } } // namespace MusEPlugin muse-4.0.0/src/libs/plugin/plugin_cache_reader.h000066400000000000000000000106371404105311000216120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // plugin_cache_reader.h // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PLUGIN_CACHE_READER_H__ #define __PLUGIN_CACHE_READER_H__ #include #include #include "config.h" #include "plugin_scan.h" #include "plugin_list.h" namespace MusECore { class Xml; } namespace MusEPlugin { //----------------------------------------- // Public plugin cache functions //----------------------------------------- void setPluginScanFileInfo(const QString& filename, PluginScanInfoStruct* info); // TODO Depending on our needs later, make some of these private static in the cpp file. // The museGlobalLib is where to find the application's installed libraries. QStringList pluginGetLadspaDirectories(const QString& museGlobalLib); QStringList pluginGetMessDirectories(const QString& museGlobalLib); QStringList pluginGetDssiDirectories(); QStringList pluginGetLinuxVstDirectories(); QStringList pluginGetLv2Directories(); QStringList pluginGetVstDirectories(); QStringList pluginGetDirectories(const QString& museGlobalLib, PluginScanInfoStruct::PluginType type = PluginScanInfoStruct::PluginTypeNone); // Returns the name of a cache file, without path. const char* pluginCacheFilename( // The type of plugin cache to check. PluginScanInfoStruct::PluginType type = PluginScanInfoStruct::PluginTypeNone ); // Returns the given type parameter if the cache file for the given type exists. PluginScanInfoStruct::PluginType pluginCacheFileExists( // Path to the cache file directory (eg. config path + /scanner). const QString& path, // The type of plugin cache to check. PluginScanInfoStruct::PluginType type = PluginScanInfoStruct::PluginTypeNone ); // Returns plugin type true if the cache file(s) for the given type(s) exist. PluginScanInfoStruct::PluginType_t pluginCacheFilesExist( // Path to the cache file directory (eg. config path + /scanner). const QString& path, // The types of plugin caches to check. PluginScanInfoStruct::PluginType_t types = PluginScanInfoStruct::PluginTypeAll ); // Return true on error bool readPluginScanInfoPortEnum(MusECore::Xml& xml, PluginPortInfo* port_info); // Return true on error bool readPluginScanInfoPort(MusECore::Xml& xml, PluginScanInfoStruct* info); // Return true on error bool readPluginScanInfo(MusECore::Xml& xml, PluginScanInfoStruct* info, bool readPorts = false, bool readEnums = false); // Return true on error bool readPluginScan(MusECore::Xml& xml, PluginScanList* list, bool readPorts = false, bool readEnums = false); // Read the plugin cache text file to a plugin list. bool readPluginCacheFile( // Path to the cache file directory (eg. config path + /scanner). const QString& path, // List to read into. PluginScanList* list, // Whether to read port information. bool readPorts = false, // Whether to read port value enumeration information. bool readEnums = false, // The type of plugin cache file to read. PluginScanInfoStruct::PluginType type = PluginScanInfoStruct::PluginTypeNone ); // Read all plugin cache text files to a plugin list. bool readPluginCacheFiles( // Path to the cache file directory (eg. config path + /scanner). const QString& path, // List to read into. PluginScanList* list, // Whether to read port information. bool readPorts = false, // Whether to read port value enumeration information. bool readEnums = false, // The types of plugin cache files to read. PluginScanInfoStruct::PluginType_t types = PluginScanInfoStruct::PluginTypeAll); } // namespace MusEPlugin #endif muse-4.0.0/src/libs/plugin/plugin_cache_writer.cpp000066400000000000000000003145461404105311000222250ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // plugin_cache_writer.cpp // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include #include "muse_math.h" #include "plugin_rdf.h" #include "plugin_cache_writer.h" #include "plugin_cache_reader.h" #ifdef HAVE_LRDF #include #endif // HAVE_LRDF #ifdef LV2_SUPPORT #include #include // Disable warnings for parentheses. Did not work! // #if defined(__clang__) // # pragma clang diagnostic push // # pragma clang diagnostic ignored "-Wparentheses" // #elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) // # pragma GCC diagnostic push // # pragma GCC diagnostic warning "-Wparentheses" // #endif // #include "lilv/lilv.h" // #if defined(__clang__) // # pragma clang diagnostic pop // #elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) // # pragma GCC diagnostic pop // #endif #include "lv2/lv2plug.in/ns/ext/data-access/data-access.h" #include "lv2/lv2plug.in/ns/ext/state/state.h" #include "lv2/lv2plug.in/ns/ext/atom/atom.h" #include "lv2/lv2plug.in/ns/ext/midi/midi.h" #include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h" #include "lv2/lv2plug.in/ns/ext/event/event.h" #include "lv2/lv2plug.in/ns/ext/options/options.h" #include "lv2/lv2plug.in/ns/ext/parameters/parameters.h" #include "lv2/lv2plug.in/ns/ext/patch/patch.h" #include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h" #include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/state/state.h" #include "lv2/lv2plug.in/ns/ext/time/time.h" #include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h" #include "lv2/lv2plug.in/ns/ext/urid/urid.h" #include "lv2/lv2plug.in/ns/ext/worker/worker.h" #include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" #include "lv2/lv2plug.in/ns/ext/atom/forge.h" #include "lv2/lv2plug.in/ns/ext/log/log.h" #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #include "lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h" #include "lv2extui.h" #include "lv2extprg.h" #include #endif // LV2_SUPPORT // Forwards from header: #include "xml.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_PLUGIN_SCAN(dev, format, args...) // std::fprintf(dev, format, ##args); //#define PORTS_ARE_SINGLE_LINE_TAGS 1 namespace MusEPlugin { bool scanLadspaPorts( const LADSPA_Descriptor* ladspa_descr, PluginScanInfoStruct* info, bool #ifdef HAVE_LRDF do_rdf #endif ) { info->_portCount = ladspa_descr->PortCount; unsigned long ip = 0, op = 0, cip = 0, cop = 0; for(unsigned long k = 0; k < ladspa_descr->PortCount; ++k) { PluginPortInfo port_info; port_info._name = PLUGIN_SET_CSTRING(ladspa_descr->PortNames[k]); port_info._index = k; const LADSPA_PortRangeHint range_hint = ladspa_descr->PortRangeHints[k]; LADSPA_PortRangeHintDescriptor range_hint_desc = range_hint.HintDescriptor; // if(LADSPA_IS_HINT_LOGARITHMIC(range_hint_desc)) // port_info._valueFlags = PluginPortInfo::LogVal; // else if(LADSPA_IS_HINT_TOGGLED(range_hint_desc)) // port_info._valueFlags = PluginPortInfo::ToggledVal; // else if(LADSPA_IS_HINT_INTEGER(range_hint_desc)) // port_info._valueFlags = PluginPortInfo::IntegerVal; // else // //if(!LADSPA_IS_HINT_INTEGER(range_hint_desc) && // // !LADSPA_IS_HINT_LOGARITHMIC(range_hint_desc) && // // !LADSPA_IS_HINT_TOGGLED(range_hint_desc)) // port_info._valueType = PluginPortInfo::LinearVal; if(LADSPA_IS_HINT_LOGARITHMIC(range_hint_desc)) port_info._valueFlags |= PluginPortInfo::LogVal; if(LADSPA_IS_HINT_TOGGLED(range_hint_desc)) port_info._valueFlags |= PluginPortInfo::ToggledVal; if(LADSPA_IS_HINT_INTEGER(range_hint_desc)) port_info._valueFlags |= PluginPortInfo::IntegerVal; // if(LADSPA_IS_HINT_TOGGLED(range_hint_desc)) // { // port_info._min = 0.0f; // port_info._max = 1.0f; // port_info._valueFlags |= PluginPortInfo::HasMin; // port_info._valueFlags |= PluginPortInfo::HasMax; // } // else { if(LADSPA_IS_HINT_SAMPLE_RATE(range_hint_desc)) port_info._flags |= PluginPortInfo::ScaleBySamplerate; if(LADSPA_IS_HINT_BOUNDED_BELOW(range_hint_desc)) { port_info._min = range_hint.LowerBound; // * m; port_info._valueFlags |= PluginPortInfo::HasMin; } else { //port_info._min = 0.0f; } if(LADSPA_IS_HINT_BOUNDED_ABOVE(range_hint_desc)) { port_info._max = range_hint.UpperBound; // * m; //port_info._flags |= PluginPortInfo::BoundedAbove; port_info._valueFlags |= PluginPortInfo::HasMax; } else { //port_info._max = 1.0f; } } if(LADSPA_IS_HINT_HAS_DEFAULT(range_hint_desc)) port_info._valueFlags |= PluginPortInfo::HasDefault; LADSPA_Data default_val = PluginPortInfo::defaultPortValue; if(LADSPA_IS_HINT_DEFAULT_MINIMUM(range_hint_desc)) default_val = range_hint.LowerBound; else if(LADSPA_IS_HINT_DEFAULT_MAXIMUM(range_hint_desc)) default_val = range_hint.UpperBound; else if(LADSPA_IS_HINT_DEFAULT_LOW(range_hint_desc)) { if(LADSPA_IS_HINT_LOGARITHMIC(range_hint_desc)) default_val = std::exp(std::log(range_hint.LowerBound) * .75f + std::log(range_hint.UpperBound) * .25f); else default_val = range_hint.LowerBound*.75f + range_hint.UpperBound*.25f; } else if(LADSPA_IS_HINT_DEFAULT_MIDDLE(range_hint_desc)) { if(LADSPA_IS_HINT_LOGARITHMIC(range_hint_desc)) default_val = std::exp(std::log(range_hint.LowerBound) * .5f + std::log(range_hint.UpperBound) * .5f); else default_val = range_hint.LowerBound*.5f + range_hint.UpperBound*.5f; } else if(LADSPA_IS_HINT_DEFAULT_HIGH(range_hint_desc)) { if(LADSPA_IS_HINT_LOGARITHMIC(range_hint_desc)) default_val = std::exp(std::log(range_hint.LowerBound) * .25f + std::log(range_hint.UpperBound) * .75f); else default_val = range_hint.LowerBound*.25f + range_hint.UpperBound*.75f; } else if(LADSPA_IS_HINT_DEFAULT_0(range_hint_desc)) default_val = 0.0f; else if(LADSPA_IS_HINT_DEFAULT_1(range_hint_desc)) default_val = 1.0f; else if(LADSPA_IS_HINT_DEFAULT_100(range_hint_desc)) default_val = 100.0f; else if(LADSPA_IS_HINT_DEFAULT_440(range_hint_desc)) default_val = 440.0f; // No default found. Make one up... else if(LADSPA_IS_HINT_BOUNDED_BELOW(range_hint_desc) && LADSPA_IS_HINT_BOUNDED_ABOVE(range_hint_desc)) { if(LADSPA_IS_HINT_LOGARITHMIC(range_hint_desc)) default_val = std::exp(std::log(range_hint.LowerBound) * .5f + std::log(range_hint.UpperBound) * .5f); else default_val = range_hint.LowerBound*.5f + range_hint.UpperBound*.5f; } else if(LADSPA_IS_HINT_BOUNDED_BELOW(range_hint_desc)) default_val = range_hint.LowerBound; else if(LADSPA_IS_HINT_BOUNDED_ABOVE(range_hint_desc)) { // Hm. What to do here... Just try 0.0 or the upper bound if less than zero. //if(range.UpperBound > 0.0f) // default_val = 0.0f; //else // default_val = range.UpperBound; // Instead try this: Adopt an 'attenuator-like' policy, where upper is the default. default_val = range_hint.UpperBound; } port_info._defaultVal = default_val; const LADSPA_PortDescriptor pd = ladspa_descr->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) { port_info._type = PluginPortInfo::AudioPort; if(pd & LADSPA_PORT_INPUT) { port_info._type |= PluginPortInfo::InputPort; } else if(pd & LADSPA_PORT_OUTPUT) { port_info._type |= PluginPortInfo::OutputPort; ++op; } } else if(pd & LADSPA_PORT_CONTROL) { port_info._type = PluginPortInfo::ControlPort; if(pd & LADSPA_PORT_INPUT) { port_info._type |= PluginPortInfo::InputPort; ++cip; } else if(pd & LADSPA_PORT_OUTPUT) { port_info._type |= PluginPortInfo::OutputPort; ++cop; const QString pname = PLUGIN_SET_CSTRING(ladspa_descr->PortNames[k]); if(pname == QString("latency") || pname == QString("_latency")) { info->_pluginFlags |= PluginScanInfoStruct::HasLatencyPort; info->_latencyPortIdx = k; } } } #ifdef HAVE_LRDF if(do_rdf) { lrdf_defaults *defs; defs = lrdf_get_scale_values(info->_uniqueID, k); if(defs) { // Map for ensuring the enumerations are sorted by increasing value. std::map > enum_list; for(unsigned int i = 0; defs && i < defs->count; i++) { DEBUG_PLUGIN_SCAN(stderr, "%f = '%s'\n", defs->items[i].value, defs->items[i].label); PluginPortEnumValue enum_val(defs->items[i].value, PLUGIN_SET_CSTRING(defs->items[i].label)); enum_list.insert(std::pair(enum_val._value, enum_val)); } lrdf_free_setting_values(defs); // Copy the sorted enumeration values to the vector enumeration list. EnumValueList dst_list; for(std::map>::const_iterator iel = enum_list.begin(); iel != enum_list.end(); ++iel) { dst_list.push_back(iel->second); } if(!dst_list.empty()) { info->_portEnumValMap.insert(PortEnumValueMapPair(k, dst_list)); port_info._valueFlags |= PluginPortInfo::HasEnumerations; } } // Yikes. A bit too much information to store. Maybe let's skip this and // only read presets when the user actually wants to load one? // // lrdf_uris * set_uris; // set_uris = lrdf_get_setting_uris(info->_uniqueID); // if(set_uris) // { // for(unsigned int i = 0; i < set_uris->count; ++i) // { // defs = lrdf_get_setting_values(set_uris->items[i]); // // // lrdf_free_setting_values(defs); // } // } } #endif // HAVE_LRDF info->_portList.push_back(port_info); } info->_inports = ip; info->_outports = op; info->_controlInPorts = cip; info->_controlOutPorts = cop; if((info->_inports != info->_outports) || LADSPA_IS_INPLACE_BROKEN(ladspa_descr->Properties)) info->_requiredFeatures |= MusECore::PluginNoInPlaceProcessing; return true; } bool scanLadspaDescriptor( const char* filename, const LADSPA_Descriptor* ladspa_descr, PluginScanInfoStruct* info, bool do_ports, bool do_rdf) { setPluginScanFileInfo(filename, info); info->_type = PluginScanInfoStruct::PluginTypeLADSPA; info->_class = PluginScanInfoStruct::PluginClassEffect; info->_uniqueID = ladspa_descr->UniqueID; info->_label = PLUGIN_SET_CSTRING(ladspa_descr->Label); info->_name = PLUGIN_SET_CSTRING(ladspa_descr->Name); info->_maker = PLUGIN_SET_CSTRING(ladspa_descr->Maker); info->_copyright = PLUGIN_SET_CSTRING(ladspa_descr->Copyright); if(LADSPA_IS_REALTIME(ladspa_descr->Properties)) info->_pluginFlags |= PluginScanInfoStruct::Realtime; if(LADSPA_IS_HARD_RT_CAPABLE(ladspa_descr->Properties)) info->_pluginFlags |= PluginScanInfoStruct::HardRealtimeCapable; // DEBUG_PLUGIN_SCAN(stderr, "scanLadspaDescriptor: name:%s info->_name:%s cstring:%s info->_label:%s cstring:%s\n", // ladspa_descr->Name, // info->_name.toLatin1().constData(), // MusEPlugin::getCString(info->_name), // info->_label.toLatin1().constData(), // MusEPlugin::getCString(info->_label) // ); if(do_ports) { scanLadspaPorts(ladspa_descr, info, do_rdf); } else { info->_portCount = ladspa_descr->PortCount; unsigned long ip = 0, op = 0, cip = 0, cop = 0; for(unsigned long k = 0; k < ladspa_descr->PortCount; ++k) { const LADSPA_PortDescriptor pd = ladspa_descr->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) { if(pd & LADSPA_PORT_INPUT) { ++ip; } else if(pd & LADSPA_PORT_OUTPUT) { ++op; } } else if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) { ++cip; } else if(pd & LADSPA_PORT_OUTPUT) { ++cop; const QString pname(PLUGIN_SET_CSTRING(ladspa_descr->PortNames[k])); if(pname == QString("latency") || pname == QString("_latency")) { info->_pluginFlags |= PluginScanInfoStruct::HasLatencyPort; info->_latencyPortIdx = k; } } } } info->_inports = ip; info->_outports = op; info->_controlInPorts = cip; info->_controlOutPorts = cop; if((info->_inports != info->_outports) || LADSPA_IS_INPLACE_BROKEN(ladspa_descr->Properties)) info->_requiredFeatures |= MusECore::PluginNoInPlaceProcessing; } return true; } bool writeLadspaInfo ( const char* filename, LADSPA_Descriptor_Function ladspa, bool do_ports, int level, MusECore::Xml& xml) { bool do_rdf = false; #ifdef HAVE_LRDF if(do_ports) { const QString sch("file:///"); QStringList rdfs; lrdf_init(); scanLrdfPlugins(&rdfs, false); const int rdfs_sz = rdfs.size(); QByteArrayList ba_rdfs_uris; const char* uris[rdfs_sz + 1]; for(int i = 0; i < rdfs_sz; ++i) { ba_rdfs_uris.append((sch + rdfs.at(i)).toLocal8Bit()); uris[i] = ba_rdfs_uris.at(i).constData(); } uris[rdfs_sz] = NULL; if(lrdf_read_files(uris)) std::fprintf(stderr, "writeLadspaInfo: lrdf_read_files() Failed\n"); else do_rdf = true; } #endif // HAVE_LRDF const LADSPA_Descriptor* descr; for(unsigned long i = 0;; ++i) { descr = ladspa(i); if(descr == NULL) break; PluginScanInfoStruct info; if(!scanLadspaDescriptor(filename, descr, &info, do_ports, do_rdf)) continue; writePluginScanInfo(level, xml, info, do_ports); } #ifdef HAVE_LRDF if(do_ports) lrdf_cleanup(); #endif // HAVE_LRDF return true; } bool scanMessDescriptor(const char* filename, const MESS* mess_descr, PluginScanInfoStruct* info) { setPluginScanFileInfo(filename, info); info->_type = PluginScanInfoStruct::PluginTypeMESS; info->_class = PluginScanInfoStruct::PluginClassInstrument; info->_uniqueID = 0; //info->_label = MusEPlugin::setString(mess_descr->Label); info->_label = PLUGIN_SET_CSTRING(mess_descr->name); info->_name = PLUGIN_SET_CSTRING(mess_descr->name); //info->_maker = MusEPlugin::setString(mess_descr->Maker); //info->_copyright = MusEPlugin::setString(mess_descr->Copyright); info->_description = PLUGIN_SET_CSTRING(mess_descr->description); info->_version = PLUGIN_SET_CSTRING(mess_descr->version); info->_apiVersionMajor = mess_descr->majorMessVersion; info->_apiVersionMinor = mess_descr->minorMessVersion; info->_portCount = 0; info->_inports = 0; info->_outports = 0; info->_controlInPorts = 0; info->_controlOutPorts = 0; info->_pluginFlags |= PluginScanInfoStruct::HasGui; return true; } bool writeMessInfo(const char* filename, MESS_Descriptor_Function mess, bool do_ports, int level, MusECore::Xml& xml) { const MESS* mess_descr = mess(); if(mess_descr) { PluginScanInfoStruct info; if(scanMessDescriptor(filename, mess_descr, &info)) { writePluginScanInfo(level, xml, info, do_ports); return true; } } return false; } #ifdef DSSI_SUPPORT QString getDssiUiFilename(PluginScanInfoStruct* info) { if(PLUGIN_STRING_EMPTY(info->_absolutePath) || PLUGIN_STRING_EMPTY(info->lib())) return QString(); const QString libr = PLUGIN_GET_QSTRING(info->lib()); const QString guiPath(PLUGIN_GET_QSTRING(info->dirPath()) + "/" + libr); const QDir guiDir(guiPath, "*", QDir::Unsorted, QDir::Files); if(!guiDir.exists()) return QString(); const QStringList list = guiDir.entryList(); const QString plug = PLUGIN_GET_QSTRING(info->_label); QString lib_qt_ui; QString lib_any_ui; QString plug_qt_ui; QString plug_any_ui; for(int i = 0; i < list.count(); ++i) { QFileInfo fi(guiPath + QString("/") + list[i]); QString gui(fi.filePath()); struct stat buf; if(stat(gui.toLatin1().constData(), &buf)) continue; if(!((S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))) continue; // FIXME: Qt::CaseInsensitive - a quick and dirty way to accept any suffix. Should be case sensitive... if(!libr.isEmpty()) { if(lib_qt_ui.isEmpty() && list[i].contains(libr + QString("_qt"), Qt::CaseInsensitive)) lib_qt_ui = gui; if(lib_any_ui.isEmpty() && list[i].contains(libr + QString('_') /*, Qt::CaseInsensitive*/)) lib_any_ui = gui; } if(!plug.isEmpty()) { if(plug_qt_ui.isEmpty() && list[i].contains(plug + QString("_qt"), Qt::CaseInsensitive)) plug_qt_ui = gui; if(plug_any_ui.isEmpty() && list[i].contains(plug + QString('_') /*, Qt::CaseInsensitive*/)) plug_any_ui = gui; } } // Prefer qt plugin ui if(!plug_qt_ui.isEmpty()) return plug_qt_ui; // Prefer any plugin ui if(!plug_any_ui.isEmpty()) return plug_any_ui; // Prefer qt lib ui if(!lib_qt_ui.isEmpty()) return lib_qt_ui; // Prefer any lib ui if(!lib_any_ui.isEmpty()) return lib_any_ui; // No suitable UI file found return QString(); }; bool scanDssiDescriptor( const char* filename, const DSSI_Descriptor* dssi_descr, PluginScanInfoStruct* info, bool do_ports, bool do_rdf) { const LADSPA_Descriptor* ladspa_descr = dssi_descr->LADSPA_Plugin; if(!ladspa_descr) return false; if(!scanLadspaDescriptor(filename, ladspa_descr, info, do_ports, do_rdf)) return false; info->_type = PluginScanInfoStruct::PluginTypeDSSI; info->_apiVersionMajor = dssi_descr->DSSI_API_Version; info->_apiVersionMinor = 0; if(PLUGIN_GET_QSTRING(info->_completeBaseName) == PLUGIN_GET_QSTRING("dssi-vst")) { info->_type = PluginScanInfoStruct::PluginTypeDSSIVST; info->_requiredFeatures |= MusECore::PluginFixedBlockSize; info->_requiredFeatures |= MusECore::PluginCoarseBlockSize; } if(dssi_descr->run_synth || dssi_descr->run_synth_adding || dssi_descr->run_multiple_synths || dssi_descr->run_multiple_synths_adding) info->_class |= PluginScanInfoStruct::PluginClassInstrument; info->_uiFilename = PLUGIN_SET_QSTRING(getDssiUiFilename(info)); if(!PLUGIN_STRING_EMPTY(info->_uiFilename)) info->_pluginFlags |= PluginScanInfoStruct::HasGui; return true; } bool writeDssiInfo(const char* filename, DSSI_Descriptor_Function dssi, bool do_ports, int level, MusECore::Xml& xml) { bool do_rdf = false; #ifdef HAVE_LRDF if(do_ports) { const QString sch("file:///"); QStringList rdfs; lrdf_init(); scanLrdfPlugins(&rdfs, false); const int rdfs_sz = rdfs.size(); QByteArrayList ba_rdfs_uris; const char* uris[rdfs_sz + 1]; for(int i = 0; i < rdfs_sz; ++i) { ba_rdfs_uris.append((sch + rdfs.at(i)).toLocal8Bit()); uris[i] = ba_rdfs_uris.at(i).constData(); } uris[rdfs_sz] = NULL; if(lrdf_read_files(uris)) std::fprintf(stderr, "writeDssiInfo: lrdf_read_files() Failed\n"); else do_rdf = true; } #endif // HAVE_LRDF const DSSI_Descriptor* dssi_descr; for(unsigned long i = 0;; ++i) { dssi_descr = dssi(i); if(dssi_descr == 0) break; PluginScanInfoStruct info; if(!scanDssiDescriptor(filename, dssi_descr, &info, do_ports, do_rdf)) continue; writePluginScanInfo(level, xml, info, do_ports); } #ifdef HAVE_LRDF if(do_ports) lrdf_cleanup(); #endif // HAVE_LRDF return true; } #endif // DSSI_SUPPORT #ifdef VST_NATIVE_SUPPORT static VstIntPtr currentPluginId = 0; //static sem_t _vstIdLock; //static QSemaphore _vstIdLock; //----------------------------------------------------------------------------------------- // vstHostCallback // This must be a function, it cannot be a class method so we dispatch to various objects from here. //----------------------------------------------------------------------------------------- VstIntPtr VSTCALLBACK vstNativeHostCallback(AEffect* effect, VstInt32 opcode, VstInt32 /*index*/, VstIntPtr /*value*/, void* ptr, float /*opt*/) { // Is this callback for an actual instance? Hand-off to the instance if so. //VSTPlugin* plugin; if(effect && effect->user) { /* VstNativeSynthOrPlugin *userData = (VstNativeSynthOrPlugin*)(effect->user); //return ((VstNativeSynthIF*)plugin)->hostCallback(opcode, index, value, ptr, opt); return VstNativeSynth::pluginHostCallback(userData, opcode, index, value, ptr, opt); */ return 0; } // No instance found. So we are just scanning for plugins... DEBUG_PLUGIN_SCAN(stderr, "plugin_scan: vstNativeHostCallback eff:%p opcode:%ld\n", effect, (unsigned long)opcode); switch (opcode) { case audioMasterAutomate: // index, value, returns 0 return 0; case audioMasterVersion: // vst version, currently 2 (0 for older) return 2300; case audioMasterCurrentId: // returns the unique id of a plug that's currently // loading return currentPluginId; case audioMasterIdle: // call application idle routine (this will // call effEditIdle for all open editors too) return 0; case audioMasterGetTime: // returns const VstTimeInfo* (or 0 if not supported) // should contain a mask indicating which fields are required // (see valid masks above), as some items may require extensive // conversions return 0; case audioMasterProcessEvents: // VstEvents* in return 0; case audioMasterIOChanged: // numInputs and/or numOutputs has changed return 0; case audioMasterSizeWindow: // index: width, value: height return 0; case audioMasterGetSampleRate: //return MusEGlobal::sampleRate; return 44100; case audioMasterGetBlockSize: //return MusEGlobal::segmentSize; return 512; case audioMasterGetInputLatency: return 0; case audioMasterGetOutputLatency: return 0; case audioMasterGetCurrentProcessLevel: // returns: 0: not supported, // 1: currently in user thread (gui) // 2: currently in audio thread (where process is called) // 3: currently in 'sequencer' thread (midi, timer etc) // 4: currently offline processing and thus in user thread // other: not defined, but probably pre-empting user thread. return 0; case audioMasterGetAutomationState: // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write // offline return 0; case audioMasterOfflineStart: case audioMasterOfflineRead: // ptr points to offline structure, see below. return 0: error, 1 ok return 0; case audioMasterOfflineWrite: // same as read return 0; case audioMasterOfflineGetCurrentPass: case audioMasterOfflineGetCurrentMetaPass: return 0; case audioMasterGetVendorString: // fills with a string identifying the vendor (max 64 char) strcpy ((char*) ptr, "MusE"); return 1; case audioMasterGetProductString: // fills with a string with product name (max 64 char) strcpy ((char*) ptr, "NativeVST"); return 1; case audioMasterGetVendorVersion: // returns vendor-specific version return 2000; case audioMasterVendorSpecific: // no definition, vendor specific handling return 0; case audioMasterCanDo: // string in ptr, see below return 0; case audioMasterGetLanguage: // see enum return kVstLangEnglish; case audioMasterGetDirectory: // get plug directory, FSSpec on MAC, else char* return 0; case audioMasterUpdateDisplay: // something has changed, update 'multi-fx' display return 0; case audioMasterBeginEdit: // begin of automation session (when mouse down), parameter index in return 0; case audioMasterEndEdit: // end of automation session (when mouse up), parameter index in return 0; case audioMasterOpenFileSelector: // open a fileselector window with VstFileSelect* in return 0; case audioMasterCloseFileSelector: return 0; #ifdef VST_FORCE_DEPRECATED #ifndef VST_2_4_EXTENSIONS // deprecated in 2.4 case audioMasterGetSpeakerArrangement: // (long)input in , output in return 0; case audioMasterPinConnected: // inquire if an input or output is being connected; // index enumerates input or output counting from zero: // value is 0 for input and != 0 otherwise. note: the // return value is 0 for such that older versions // will always return true. //return 1; return 0; // VST 2.0 opcodes... case audioMasterWantMidi: // is a filter which is currently ignored return 0; case audioMasterSetTime: // VstTimenfo* in , filter in , not supported return 0; case audioMasterTempoAt: // returns tempo (in bpm * 10000) at sample frame location passed in return 0; // TODO: case audioMasterGetNumAutomatableParameters: return 0; case audioMasterGetParameterQuantization: // returns the integer value for +1.0 representation, // or 1 if full single float precision is maintained // in automation. parameter index in (-1: all, any) //return 0; return 1; case audioMasterNeedIdle: // plug needs idle calls (outside its editor window) return 0; case audioMasterGetPreviousPlug: // input pin in (-1: first to come), returns cEffect* return 0; case audioMasterGetNextPlug: // output pin in (-1: first to come), returns cEffect* return 0; case audioMasterWillReplaceOrAccumulate: // returns: 0: not supported, 1: replace, 2: accumulate //return 0; return 1; case audioMasterSetOutputSampleRate: // for variable i/o, sample rate in return 0; case audioMasterSetIcon: // void* in , format not defined yet return 0; case audioMasterOpenWindow: // returns platform specific ptr return 0; case audioMasterCloseWindow: // close window, platform specific handle in return 0; #endif #endif default: break; } DEBUG_PLUGIN_SCAN(stderr, " unknown opcode\n"); return 0; } static void setVstParameterInfo( AEffect *plugin, unsigned long i, PluginPortInfo* port_info ) { VstParameterProperties props; if(plugin->dispatcher(plugin, effGetParameterProperties, i, 0, &props, 0)) { if(props.flags & kVstParameterIsSwitch) port_info->_valueFlags |= PluginPortInfo::ToggledVal; if(props.flags & kVstParameterUsesIntegerMinMax) { port_info->_min = props.minInteger; port_info->_max = props.maxInteger; port_info->_valueFlags |= (PluginPortInfo::HasMin | PluginPortInfo::HasMax); } if(props.flags & kVstParameterUsesFloatStep) { port_info->_step = props.stepFloat; port_info->_smallStep = props.smallStepFloat; port_info->_largeStep = props.largeStepFloat; port_info->_valueFlags |= (PluginPortInfo::HasMin | PluginPortInfo::HasStep); } if(props.flags & kVstParameterUsesIntStep) { port_info->_step = props.stepInteger; // No such member. Makes sense I suppose. //port_info->_smallStep = props.smallStepInteger; port_info->_largeStep = props.largeStepInteger; port_info->_valueFlags |= (PluginPortInfo::HasMin | PluginPortInfo::HasStep); } char label_buf[VstLabelLen + 1]; label_buf[0] = 0; std::strncpy(label_buf, props.label, VstLabelLen); label_buf[VstLabelLen] = 0; port_info->_name = PLUGIN_SET_CSTRING(label_buf); char shlabel_buf[VstShortLabelLen + 1]; shlabel_buf[0] = 0; std::strncpy(shlabel_buf, props.label, VstShortLabelLen); shlabel_buf[VstShortLabelLen] = 0; port_info->_symbol = PLUGIN_SET_CSTRING(shlabel_buf); } else { char buf[256]; buf[0] = 0; plugin->dispatcher(plugin, effGetParamName, i, 0, buf, 0); port_info->_name = PLUGIN_SET_CSTRING(buf); } } //--------------------------------------------------------- // scanLinuxVstDescriptor //--------------------------------------------------------- bool scanLinuxVstPorts(AEffect *plugin, PluginScanInfoStruct* info) { info->_portCount = plugin->numInputs + plugin->numOutputs + plugin->numParams; info->_inports = plugin->numInputs; info->_outports = plugin->numOutputs; info->_controlInPorts = plugin->numParams; info->_controlOutPorts = 0; if((info->_inports != info->_outports) || !(plugin->flags & effFlagsCanReplacing)) info->_requiredFeatures |= MusECore::PluginNoInPlaceProcessing; unsigned long k = 0; for(int i = 0; i < plugin->numInputs; ++i) { PluginPortInfo port_info; port_info._index = k; port_info._type = PluginPortInfo::AudioPort | PluginPortInfo::InputPort; info->_portList.push_back(port_info); ++k; } for(int i = 0; i < plugin->numOutputs; ++i) { PluginPortInfo port_info; port_info._index = k; port_info._type = PluginPortInfo::AudioPort | PluginPortInfo::OutputPort; info->_portList.push_back(port_info); ++k; } for(int i = 0; i < plugin->numParams; ++i) { PluginPortInfo port_info; port_info._index = k; port_info._type = PluginPortInfo::ControlPort | PluginPortInfo::InputPort; // Notice the 'i' not 'k'. setVstParameterInfo(plugin, i, &port_info); info->_portList.push_back(port_info); ++k; } return true; } //--------------------------------------------------------- // scanLinuxVstDescriptor //--------------------------------------------------------- bool scanLinuxVstDescriptor(const char* filename, AEffect *plugin, long int id, PluginScanInfoStruct* info, bool do_ports) { char buffer[256]; int vendorVersion; int vst_version = 0; if(plugin->flags & effFlagsHasEditor) { info->_pluginFlags |= PluginScanInfoStruct::HasGui; DEBUG_PLUGIN_SCAN(stderr, "Plugin has a GUI\n"); } else { DEBUG_PLUGIN_SCAN(stderr, "Plugin has no GUI\n"); } if(plugin->flags & effFlagsCanReplacing) { DEBUG_PLUGIN_SCAN(stderr, "Plugin supports processReplacing\n"); } else { DEBUG_PLUGIN_SCAN(stderr, "Plugin does not support processReplacing\n"); } plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0); buffer[0] = 0; plugin->dispatcher(plugin, effGetEffectName, 0, 0, buffer, 0); if(buffer[0]) info->_label = PLUGIN_SET_CSTRING(buffer); buffer[0] = 0; plugin->dispatcher(plugin, effGetVendorString, 0, 0, buffer, 0); if (buffer[0]) info->_maker = PLUGIN_SET_CSTRING(buffer); buffer[0] = 0; plugin->dispatcher(plugin, effGetProductString, 0, 0, buffer, 0); if (buffer[0]) info->_description = PLUGIN_SET_CSTRING(buffer); vendorVersion = plugin->dispatcher(plugin, effGetVendorVersion, 0, 0, NULL, 0); buffer[0] = 0; sprintf(buffer, "%d.%d.%d", vendorVersion >> 16, vendorVersion >> 8, vendorVersion & 0xff); if(buffer[0]) info->_version = PLUGIN_SET_CSTRING(buffer); setPluginScanFileInfo(filename, info); // Some (older) plugins don't have any of these strings. We only have the filename to use. if(PLUGIN_STRING_EMPTY(info->_label)) info->_label = info->_completeBaseName; if(PLUGIN_STRING_EMPTY(info->_description)) //info->_description = info._completeBaseName; info->_description = info->_label; info->_name = info->_label; // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far. vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f); info->_type = PluginScanInfoStruct::PluginTypeLinuxVST; info->_class = PluginScanInfoStruct::PluginClassEffect; info->_uniqueID = plugin->uniqueID; info->_subID = id; info->_apiVersionMajor = vst_version; info->_apiVersionMinor = 0; info->_pluginVersionMajor = (vendorVersion >> 16) & 0xff; //info->_pluginVersionMinor = (vendorVersion >> 8) & 0xff; info->_pluginVersionMinor = vendorVersion & 0xffff; //info->_pluginVersionMicro = vendorVersion & 0xff; if(plugin->flags & 32 /*effFlagsProgramChunks*/) info->_pluginFlags |= PluginScanInfoStruct::HasChunks; if(do_ports) { scanLinuxVstPorts(plugin, info); } else { info->_portCount = plugin->numInputs + plugin->numOutputs + plugin->numParams; info->_inports = plugin->numInputs; info->_outports = plugin->numOutputs; info->_controlInPorts = plugin->numParams; info->_controlOutPorts = 0; if((info->_inports != info->_outports) || !(plugin->flags & effFlagsCanReplacing)) info->_requiredFeatures |= MusECore::PluginNoInPlaceProcessing; } // TODO // if() // { // info._requiredFeatures |= MusECore::PluginScanInfo::FixedBlockSize; // } // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far. if(vst_version >= 2) { if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstEvents", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canReceiveVstEvents; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstEvents", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canSendVstEvents; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstMidiEvent", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canSendVstMidiEvents; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstTimeInfo", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canSendVstTimeInfo; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstMidiEvent", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canReceiveVstMidiEvents; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstTimeInfo", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canReceiveVstTimeInfo; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"offline", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canProcessVstOffline; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"plugAsChannelInsert", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canUseVstAsInsert; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"plugAsSend", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canUseVstAsSend; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"mixDryWet", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canMixVstDryWet; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"midiProgramNames", 0.0f) > 0) info->_vstPluginFlags |= MusECore::canVstMidiProgramNames; } if((plugin->flags & effFlagsIsSynth) || (vst_version >= 2 && plugin->dispatcher(plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0)) { info->_class |= PluginScanInfoStruct::PluginClassInstrument; } plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0); return true; } bool writeLinuxVstInfo( const char* filename, LinuxVST_Instance_Function lvst, bool do_ports, int level, MusECore::Xml& xml) { // sem_wait(&_vstIdLock); // _vstIdLock.acquire(); currentPluginId = 0; // bool bDontDlCLose = false; AEffect *plugin = NULL; plugin = lvst(vstNativeHostCallback); if(!plugin) { std::fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\"\n", filename); //goto _end; return false; } else { DEBUG_PLUGIN_SCAN(stderr, "plugin instantiated\n"); } if(plugin->magic != kEffectMagic) { std::fprintf(stderr, "Not a VST plugin in library \"%s\"\n", filename); //goto _end; return false; } else { DEBUG_PLUGIN_SCAN(stderr, "plugin is a VST\n"); } if(plugin->dispatcher(plugin, 24 + 11 /* effGetCategory */, 0, 0, 0, 0) == 10 /* kPlugCategShell */) { // bDontDlCLose = true; std::map shellPlugs; char cPlugName [256]; do { memset(cPlugName, 0, sizeof(cPlugName)); VstIntPtr id = plugin->dispatcher(plugin, 24 + 46 /* effShellGetNextPlugin */, 0, 0, cPlugName, 0); if(id != 0 && cPlugName [0] != 0) { shellPlugs.insert(std::make_pair(id, std::string(cPlugName))); } else break; } while(true); for(std::map::iterator it = shellPlugs.begin(); it != shellPlugs.end(); ++it) { if(plugin) { // TODO: Is effClose necessary? Try removing. //plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0); plugin = NULL; } currentPluginId = it->first; plugin = lvst(vstNativeHostCallback); if(!plugin) { std::fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\", shell id=%ld\n", filename, (long)currentPluginId); //goto _end; } else { PluginScanInfoStruct info; if(scanLinuxVstDescriptor(filename, plugin, currentPluginId, &info, do_ports)) writePluginScanInfo(level, xml, info, do_ports); } currentPluginId = 0; } } else { PluginScanInfoStruct info; if(scanLinuxVstDescriptor(filename, plugin, 0, &info, do_ports)) writePluginScanInfo(level, xml, info, do_ports); } //plugin->dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0); // if(plugin) // plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0); // _end: // if(handle && !bDontDlCLose) // dlclose(handle); // // sem_post(&_vstIdLock); // _vstIdLock.release(); return true; } #endif // VST_NATIVE_SUPPORT //--------------------------------------------------------- // writeUnknownPluginInfo //--------------------------------------------------------- bool writeUnknownPluginInfo ( const char* filename, int level, MusECore::Xml& xml) { PluginScanInfoStruct info; setPluginScanFileInfo(filename, &info); info._type = PluginScanInfoStruct::PluginTypeUnknown; //info._fileIsBad = true; writePluginScanInfo(level, xml, info, false); return true; } //--------------------------------------------------------- // writePluginScanInfo //--------------------------------------------------------- void writePluginScanInfo(int level, MusECore::Xml& xml, const PluginScanInfoStruct& info, bool writePorts) { xml.tag(level++, "plugin file=\"%s\" label=\"%s\"", MusECore::Xml::xmlString(PLUGIN_GET_QSTRING(info.filePath())).toLatin1().constData(), MusECore::Xml::xmlString(PLUGIN_GET_QSTRING(info._label)).toLatin1().constData()); if(!PLUGIN_STRING_EMPTY(info._uri)) xml.strTag(level, "uri", PLUGIN_GET_CSTRING(info._uri)); if(info._fileTime != 0) xml.longLongTag(level, "filetime", info._fileTime); if(info._fileIsBad) xml.intTag(level, "fileIsBad", info._fileIsBad); xml.intTag(level, "type", info._type); xml.intTag(level, "class", info._class); if(info._uniqueID != 0) xml.uintTag(level, "uniqueID", info._uniqueID); if(info._subID != 0) xml.intTag(level, "subID", info._subID); if(!PLUGIN_STRING_EMPTY(info._name)) xml.strTag(level, "name", PLUGIN_GET_CSTRING(info._name)); if(!PLUGIN_STRING_EMPTY(info._description)) xml.strTag(level, "description", PLUGIN_GET_CSTRING(info._description)); if(!PLUGIN_STRING_EMPTY(info._version)) xml.strTag(level, "version", PLUGIN_GET_CSTRING(info._version)); if(!PLUGIN_STRING_EMPTY(info._maker)) xml.strTag(level, "maker", PLUGIN_GET_CSTRING(info._maker)); if(!PLUGIN_STRING_EMPTY(info._copyright)) xml.strTag(level, "copyright", PLUGIN_GET_CSTRING(info._copyright)); // Optimize out all numeric values if they are zero. // PluginScanInfoStruct initializes them to zero anyway. if(info._apiVersionMajor != 0) xml.intTag(level, "apiVersionMajor", info._apiVersionMajor); if(info._apiVersionMinor != 0) xml.intTag(level, "apiVersionMinor", info._apiVersionMinor); if(info._pluginVersionMajor != 0) xml.intTag(level, "pluginVersionMajor", info._pluginVersionMajor); if(info._pluginVersionMinor != 0) xml.intTag(level, "pluginVersionMinor", info._pluginVersionMinor); if(info._pluginFlags != 0) xml.intTag(level, "pluginFlags", info._pluginFlags); if(info._portCount != 0) xml.uintTag(level, "portCount", info._portCount); if(info._inports != 0) xml.uintTag(level, "inports", info._inports); if(info._outports != 0) xml.uintTag(level, "outports", info._outports); if(info._controlInPorts != 0) xml.uintTag(level, "ctlInports", info._controlInPorts); if(info._controlOutPorts != 0) xml.uintTag(level, "ctlOutports", info._controlOutPorts); if(info._eventInPorts != 0) xml.uintTag(level, "evInports", info._eventInPorts); if(info._eventOutPorts != 0) xml.uintTag(level, "evOutports", info._eventOutPorts); if((info._pluginFlags & PluginScanInfoStruct::HasFreewheelPort) || info._freewheelPortIdx != 0) xml.uintTag(level, "freewheelPortIdx", info._freewheelPortIdx); if((info._pluginFlags & PluginScanInfoStruct::HasLatencyPort) || info._latencyPortIdx != 0) xml.uintTag(level, "latencyPortIdx", info._latencyPortIdx); if(info._requiredFeatures != 0) xml.intTag(level, "requiredFeatures", info._requiredFeatures); if(info._vstPluginFlags != MusECore::vstPluginNoFlags) xml.intTag(level, "vstPluginFlags", info._vstPluginFlags); if(!PLUGIN_STRING_EMPTY(info._uiFilename)) xml.strTag(level, "uiFilename", PLUGIN_GET_CSTRING(info._uiFilename)); // Make sure the actual list has that many ports. if(writePorts) { if(info._portList.size() != info._portCount) { std::fprintf(stderr, "writePluginScanInfo: Error: port count:%u != port list size:%u\n", (unsigned)info._portCount, (unsigned)info._portList.size()); } else { for (unsigned long i = 0; i < info._portCount; ++i) { const PluginPortInfo& port_info = info._portList[i]; // Optimize out all values if they are default. // PluginScanInfoStruct initializes them to defaults anyway. #ifdef PORTS_ARE_SINGLE_LINE_TAGS // As attributes on one line... QString s = QString("port"); if(!PLUGIN_STRING_EMPTY(port_info._name)) s += QString(" name=\"%1\"").arg(MusECore::Xml::xmlString(PLUGIN_GET_QSTRING(port_info._name))); if(!PLUGIN_STRING_EMPTY(port_info._symbol)) s += QString(" symbol=\"%1\"").arg(MusECore::Xml::xmlString(PLUGIN_GET_QSTRING(port_info._symbol))); s += QString(" idx=\"%1\"").arg(i /*port_info._index*/); s += QString(" type=\"%1\"").arg(port_info._type); if(port_info._flags != PluginPortInfo::NoPortFlags) s += QString(" flags=\"%1\"").arg(port_info._flags); if(port_info._valueFlags != PluginPortInfo::NoValueFlags) s += QString(" valFlags=\"%1\"").arg(port_info._valueFlags); if((port_info._valueFlags & PluginPortInfo::HasMin) || port_info._min != PluginPortInfo::defaultPortMin) s += QString(" min=\"%1\"").arg(port_info._min); if((port_info._valueFlags & PluginPortInfo::HasMax) || port_info._max != PluginPortInfo::defaultPortMax) s += QString(" max=\"%1\"").arg(port_info._max); if((port_info._valueFlags & PluginPortInfo::HasDefault) || port_info._defaultVal != PluginPortInfo::defaultPortValue) s += QString(" def=\"%1\"").arg(port_info._defaultVal); if((port_info._valueFlags & PluginPortInfo::HasStep) || port_info._step != PluginPortInfo::defaultPortStep) s += QString(" step=\"%1\"").arg(port_info._step); if((port_info._valueFlags & PluginPortInfo::HasStep) || port_info._smallStep != PluginPortInfo::defaultPortSmallStep) s += QString(" smallStep=\"%1\"").arg(port_info._smallStep); if((port_info._valueFlags & PluginPortInfo::HasStep) || port_info._largeStep != PluginPortInfo::defaultPortLargeStep) s += QString(" largeStep=\"%1\"").arg(port_info._largeStep); s += QString(" /"); xml.tag(level, s.toLatin1().constData()); #else // As a tag with attributes... xml.tag(level++, "port name=\"%s\" symbol=\"%s\" idx=\"%s\" type=\"%s\"", MusECore::Xml::xmlString(PLUGIN_GET_QSTRING(port_info._name)).toLatin1().constData(), MusECore::Xml::xmlString(PLUGIN_GET_QSTRING(port_info._symbol)).toLatin1().constData(), QString::number(i).toLatin1().constData(), QString::number(port_info._type).toLatin1().constData() ); if(port_info._flags != PluginPortInfo::NoPortFlags) xml.intTag(level, "flags", port_info._flags); if(port_info._valueFlags != PluginPortInfo::NoValueFlags) xml.intTag(level, "valFlags", port_info._valueFlags); if((port_info._valueFlags & PluginPortInfo::HasMin) || port_info._min != PluginPortInfo::defaultPortMin) xml.floatTag(level, "min", port_info._min); if((port_info._valueFlags & PluginPortInfo::HasMax) || port_info._max != PluginPortInfo::defaultPortMax) xml.floatTag(level, "max", port_info._max); if((port_info._valueFlags & PluginPortInfo::HasDefault) || port_info._defaultVal != PluginPortInfo::defaultPortValue) xml.floatTag(level, "def", port_info._defaultVal); if((port_info._valueFlags & PluginPortInfo::HasStep) || port_info._step != PluginPortInfo::defaultPortStep) xml.floatTag(level, "step", port_info._step); if((port_info._valueFlags & PluginPortInfo::HasStep) || port_info._smallStep != PluginPortInfo::defaultPortSmallStep) xml.floatTag(level, "smallStep", port_info._smallStep); if((port_info._valueFlags & PluginPortInfo::HasStep) || port_info._largeStep != PluginPortInfo::defaultPortLargeStep) xml.floatTag(level, "largeStep", port_info._largeStep); xml.tag(level--, "/port"); #endif } if(!info._portEnumValMap.empty()) { for(ciPortEnumValueMap ipev = info._portEnumValMap.begin(); ipev != info._portEnumValMap.end(); ++ipev) { // As a tag with attributes... const unsigned long idx = ipev->first; xml.tag(level++, "portEnumValMap idx=\"%s\"", QString::number(idx).toLatin1().constData()); const EnumValueList& evl = ipev->second; for(ciEnumValueList ivl = evl.begin(); ivl != evl.end(); ++ivl) { const PluginPortEnumValue& pev = *ivl; QString s = QString("enumVal"); s += QString(" val=\"%1\"").arg(pev._value); if(!PLUGIN_STRING_EMPTY(pev._label)) s += QString(" label=\"%1\"").arg(MusECore::Xml::xmlString(PLUGIN_GET_QSTRING(pev._label))); s += QString(" /"); xml.tag(level, s.toLatin1().constData()); } xml.tag(level--, "/portEnumValMap"); } } } } xml.tag(level--, "/plugin"); } //--------------------------------------------------------- // pluginScan // If debugStdErr is true, any stderr content received // from the scan program will be printed. // Returns true on success //--------------------------------------------------------- static bool pluginScan( const QString& filename, PluginScanInfoStruct::PluginType_t types, PluginScanList* list, bool scanPorts, bool debugStdErr) { const QByteArray filename_ba = filename.toLocal8Bit(); QTemporaryFile tmpfile; // Must open the temp file to get its name. if(!tmpfile.open()) { std::fprintf(stderr, "\npluginScan FAILED: Could not create temporary output file for input file: %s\n\n", filename_ba.constData()); return false; } // Get the unique temp file name. const QString tmpfilename = tmpfile.fileName(); const QByteArray tmpfilename_ba = tmpfilename.toLocal8Bit(); // Close the temp file. It exists until tmpfile goes out of scope. tmpfile.close(); if(debugStdErr) std::fprintf(stderr, "\nChecking file: <%s>\n", filename_ba.constData()); QProcess process; QString prog; const QByteArray appDir = qgetenv("APPDIR"); if (!appDir.isEmpty()) prog = appDir + QString(BINDIR) + QString("/muse_plugin_scan"); else prog = QString(BINDIR) + QString("/muse_plugin_scan"); QStringList args; args << QString("-t") + QString::number(types) << QString("-f") + filename << QString("-o") + tmpfilename; if(scanPorts) args << QString("-p"); process.start(prog, args); bool fail = false; // if(!process.waitForStarted(4000)) // { // std::fprintf(stderr, "pluginScan: waitForStarted failed\n"); // // return false; // fail = true; // } // if(!process.waitForReadyRead(4000)) // { // std::fprintf(stderr, "pluginScan: waitForReadyRead failed\n"); // // return false; // fail = true; // } if(!process.waitForFinished(6000)) { std::fprintf(stderr, "\npluginScan FAILED: waitForFinished: file: %s\n\n", filename_ba.constData()); while (1) { QMessageBox::StandardButton btn = QMessageBox::warning( NULL, QMessageBox::tr("Plugin Scanner"), QMessageBox::tr("Checking Plugin %1 is taking a very long time, do you want to keep waiting for it to finish, or skip this plugin?").arg(filename), QMessageBox::Retry|QMessageBox::Abort, QMessageBox::Retry); if (btn == QMessageBox::Retry) { if (process.state() == QProcess::NotRunning) { break; // success } if(process.waitForFinished(6000)) { break; // success } } else { fail = true; break; // failure } } } if(!fail && debugStdErr) { QByteArray out_array = process.readAllStandardOutput(); if(!out_array.isEmpty() && out_array.at(0) != 0) { // Terminate just to be sure. out_array.append(char(0)); std::fprintf(stderr, "\npluginScan: Standard output from scan:\n%s\n", out_array.constData()); } QByteArray err_array = process.readAllStandardError(); if(!err_array.isEmpty() && err_array.at(0) != 0) { // Terminate just to be sure. err_array.append(char(0)); std::fprintf(stderr, "\npluginScan: Standard error output from scan:\n%s\n", err_array.constData()); } } if(process.exitStatus() != QProcess::NormalExit) { std::fprintf(stderr, "\npluginScan FAILED: Scan not exited normally: file: %s\n\n", filename_ba.constData()); fail = true; } if(process.exitCode() != 0) { std::fprintf(stderr, "\npluginScan FAILED: Scan exit code not 0: file: %s\n\n", filename_ba.constData()); fail = true; } if(!fail) { // Open the temp file again... QFile infile(tmpfilename); if(!infile.exists()) { std::fprintf(stderr, "\npluginScan FAILED: Temporary file does not exist: %s\n\n", tmpfilename_ba.constData()); fail = true; } if(!fail && !infile.open(QIODevice::ReadOnly /*| QIODevice::Text*/)) { std::fprintf(stderr, "\npluginScan FAILED: Could not re-open temporary output file: %s\n\n", tmpfilename_ba.constData()); fail = true; } if(!fail) { // Create an xml object based on the file. MusECore::Xml xml(&infile); // Read the list of plugins found in the xml. // For now we don't supply a separate scanEnums flag in pluginScan(), so just use scanPorts instead. if(readPluginScan(xml, list, scanPorts, scanPorts)) { std::fprintf(stderr, "\npluginScan FAILED: On readPluginScan(): file: %s\n\n", filename_ba.constData()); fail = true; } // Close the temp file. infile.close(); // Now going out of scope destroys the temporary file... if(!fail) return true; } } //--------------------------------------------------------------- // Plugin failed scanning. Add it to the list but mark it as bad! //--------------------------------------------------------------- PluginScanInfoStruct info; setPluginScanFileInfo(filename, &info); info._type = PluginScanInfoStruct::PluginTypeUnknown; info._fileIsBad = true; // We must include all plugins. list->add(new PluginScanInfo(info)); return false; } //--------------------------------------------------------- // scanPluginDir // This might be called recursively! //--------------------------------------------------------- static void scanPluginDir( const QString& dirname, PluginScanInfoStruct::PluginType_t types, PluginScanList* list, bool scanPorts, bool debugStdErr, // Only for recursions, original top caller should not touch! int recurseLevel = 0 ) { const int max_levels = 10; if(recurseLevel >= max_levels) { std::fprintf(stderr, "scanPluginDir: Ignoring too-deep directory level (max:%d) at:%s\n", max_levels, dirname.toLocal8Bit().constData()); return; } DEBUG_PLUGIN_SCAN(stderr, "scan plugin dir <%s>\n", dirname.toLatin1().constData()); QDir pluginDir( dirname, QString("*.so"), QDir::Name | QDir::IgnoreCase, QDir::Drives | QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot); if(pluginDir.exists()) { QFileInfoList fi_list = pluginDir.entryInfoList(); QFileInfoList::iterator it=fi_list.begin(); while(it != fi_list.end()) { const QFileInfo& fi = *it; if(fi.isDir()) // RECURSIVE! scanPluginDir(fi.filePath(), types, list, scanPorts, debugStdErr, recurseLevel + 1); else pluginScan(fi.filePath(), types, list, scanPorts, debugStdErr); ++it; } } } //--------------------------------------------------------- // scanLadspaPlugins //--------------------------------------------------------- void scanLadspaPlugins(const QString& museGlobalLib, PluginScanList* list, bool scanPorts, bool debugStdErr) { QStringList sl = pluginGetLadspaDirectories(museGlobalLib); for(QStringList::const_iterator it = sl.cbegin(); it != sl.cend(); ++it) scanPluginDir(*it, PluginScanInfoStruct::PluginTypeAll, list, scanPorts, debugStdErr); } //--------------------------------------------------------- // scanMessPlugins //--------------------------------------------------------- void scanMessPlugins(const QString& museGlobalLib, PluginScanList* list, bool scanPorts, bool debugStdErr) { QStringList sl = pluginGetMessDirectories(museGlobalLib); for(QStringList::const_iterator it = sl.cbegin(); it != sl.cend(); ++it) scanPluginDir(*it, PluginScanInfoStruct::PluginTypeAll, list, scanPorts, debugStdErr); } //--------------------------------------------------------- // scanDssiPlugins //--------------------------------------------------------- #ifdef DSSI_SUPPORT void scanDssiPlugins(PluginScanList* list, bool scanPorts, bool debugStdErr) { QStringList sl = pluginGetDssiDirectories(); for(QStringList::const_iterator it = sl.cbegin(); it != sl.cend(); ++it) scanPluginDir(*it, PluginScanInfoStruct::PluginTypeAll, list, scanPorts, debugStdErr); } #else // No DSSI_SUPPORT void scanDssiPlugins(PluginScanList* /*list*/, bool /*scanPorts*/, bool /*debugStdErr*/) { } #endif // DSSI_SUPPORT //--------------------------------------------------------- // scanLinuxVSTPlugins //--------------------------------------------------------- #ifdef VST_NATIVE_SUPPORT void scanLinuxVSTPlugins(PluginScanList* list, bool scanPorts, bool debugStdErr) { #ifdef VST_VESTIGE_SUPPORT std::fprintf(stderr, "Initializing Native VST support. Using VESTIGE compatibility implementation.\n"); #else std::fprintf(stderr, "Initializing Native VST support. Using Steinberg VSTSDK.\n"); #endif // sem_init(&_vstIdLock, 0, 1); QStringList sl = pluginGetLinuxVstDirectories(); for(QStringList::const_iterator it = sl.cbegin(); it != sl.cend(); ++it) scanPluginDir(*it, PluginScanInfoStruct::PluginTypeAll, list, scanPorts, debugStdErr); } #else void scanLinuxVSTPlugins(PluginScanList* /*list*/, bool /*scanPorts*/, bool /*debugStdErr*/) { } #endif // VST_NATIVE_SUPPORT #ifdef LV2_SUPPORT #define NS_EXT "http://lv2plug.in/ns/ext/" #define NS_LV2CORE "http://lv2plug.in/ns/lv2core" #define LV2_INSTRUMENT_CLASS NS_LV2CORE "#InstrumentPlugin" #define LV2_F_BOUNDED_BLOCK_LENGTH LV2_BUF_SIZE__boundedBlockLength #define LV2_F_FIXED_BLOCK_LENGTH LV2_BUF_SIZE__fixedBlockLength #define LV2_F_POWER_OF_2_BLOCK_LENGTH LV2_BUF_SIZE__powerOf2BlockLength // BUG FIXME: 'coarseBlockLength' is NOT in the lv2 buf-size.h header file! // #define LV2_F_COARSE_BLOCK_LENGTH LV2_BUF_SIZE__coarseBlockLength #define LV2_F_COARSE_BLOCK_LENGTH LV2_BUF_SIZE_PREFIX "coarseBlockLength" #define LV2_P_SEQ_SIZE LV2_BUF_SIZE__sequenceSize #define LV2_P_MAX_BLKLEN LV2_BUF_SIZE__maxBlockLength #define LV2_P_MIN_BLKLEN LV2_BUF_SIZE__minBlockLength #define LV2_P_NOM_BLKLEN LV2_BUF_SIZE__nominalBlockLength #define LV2_P_SAMPLE_RATE LV2_PARAMETERS__sampleRate #define LV2_F_OPTIONS LV2_OPTIONS__options #define LV2_F_URID_MAP LV2_URID__map #define LV2_F_URID_UNMAP LV2_URID__unmap #define LV2_F_URI_MAP LV2_URI_MAP_URI #define LV2_F_UI_PARENT LV2_UI__parent #define LV2_F_INSTANCE_ACCESS NS_EXT "instance-access" #define LV2_F_DATA_ACCESS LV2_DATA_ACCESS_URI #define LV2_F_UI_EXTERNAL_HOST LV2_EXTERNAL_UI__Host #define LV2_F_WORKER_SCHEDULE LV2_WORKER__schedule #define LV2_F_WORKER_INTERFACE LV2_WORKER__interface #define LV2_F_UI_IDLE LV2_UI__idleInterface #define LV2_F_UI_Qt5_UI LV2_UI_PREFIX "Qt5UI" #define LV2_UI_HOST_URI LV2_F_UI_Qt5_UI #define LV2_UI_EXTERNAL LV2_EXTERNAL_UI__Widget #define LV2_UI_EXTERNAL_DEPRECATED LV2_EXTERNAL_UI_DEPRECATED_URI #define LV2_F_DEFAULT_STATE LV2_STATE_PREFIX "loadDefaultState" #define LV2_F_STATE_CHANGED LV2_STATE_PREFIX "StateChanged" //uri cache structure. typedef struct { LilvNode *atom_AtomPort; LilvNode *ev_EventPort; LilvNode *lv2_AudioPort; LilvNode *lv2_ControlPort; LilvNode *lv2_InputPort; LilvNode *lv2_OutputPort; LilvNode *lv2_connectionOptional; LilvNode *host_uiType; LilvNode *ext_uiType; LilvNode *ext_d_uiType; LilvNode *lv2_portDiscrete; LilvNode *lv2_portEnumeration; LilvNode *lv2_portContinuous; LilvNode *lv2_portLogarithmic; LilvNode *lv2_portInteger; LilvNode *lv2_portTrigger; LilvNode *lv2_portToggled; LilvNode *lv2_TimePosition; LilvNode *lv2_FreeWheelPort; LilvNode *lv2_isLive; LilvNode *lv2_HardRealtimeCapable; LilvNode *lv2_InPlaceBroken; LilvNode *lv2_SampleRate; LilvNode *lv2_CVPort; LilvNode *lv2_psetPreset; LilvNode *lv2_rdfsLabel; LilvNode *lv2_actionSavePreset; LilvNode *lv2_actionUpdatePresets; LilvNode *end; ///< NULL terminator for easy freeing of entire structure } CacheNodes; static CacheNodes lv2CacheNodes; LV2_Feature lv2Features [] = { {LV2_F_URID_MAP, NULL}, {LV2_F_URID_UNMAP, NULL}, {LV2_F_URI_MAP, NULL}, {LV2_F_BOUNDED_BLOCK_LENGTH, NULL}, {LV2_F_FIXED_BLOCK_LENGTH, NULL}, {LV2_F_POWER_OF_2_BLOCK_LENGTH, NULL}, {LV2_F_COARSE_BLOCK_LENGTH, NULL}, {LV2_F_UI_PARENT, NULL}, {LV2_F_INSTANCE_ACCESS, NULL}, {LV2_F_UI_EXTERNAL_HOST, NULL}, {LV2_UI_EXTERNAL_DEPRECATED, NULL}, {LV2_F_WORKER_SCHEDULE, NULL}, {LV2_F_UI_IDLE, NULL}, {LV2_F_OPTIONS, NULL}, {LV2_UI__resize, NULL}, {LV2_PROGRAMS__Host, NULL}, {LV2_LOG__log, NULL}, #ifdef LV2_MAKE_PATH_SUPPORT {LV2_STATE__makePath, NULL}, #endif {LV2_STATE__mapPath, NULL}, {LV2_F_STATE_CHANGED, NULL}, {LV2_F_DATA_ACCESS, NULL} //must be the last always! }; #define SIZEOF_ARRAY(x) sizeof(x)/sizeof(x[0]) void scanLv2Ports(const LilvPlugin *plugin, PluginScanInfoStruct* info, bool /*debugStdErr*/) { // Does this plugin have a 'freewheel' port? if(const LilvPort *lilvFreeWheelPort = lilv_plugin_get_port_by_designation( plugin, lv2CacheNodes.lv2_InputPort, lv2CacheNodes.lv2_FreeWheelPort)) { info->_pluginFlags |= PluginScanInfoStruct::HasFreewheelPort; info->_freewheelPortIdx = lilv_port_get_index(plugin, lilvFreeWheelPort); } if(lilv_plugin_has_latency(plugin)) { info->_pluginFlags |= PluginScanInfoStruct::HasLatencyPort; info->_latencyPortIdx = lilv_plugin_get_latency_port_index(plugin); } info->_portCount = lilv_plugin_get_num_ports(plugin); float pluginControlsDefault[info->_portCount]; float pluginControlsMin[info->_portCount]; float pluginControlsMax[info->_portCount]; lilv_plugin_get_port_ranges_float(plugin, pluginControlsMin, pluginControlsMax, pluginControlsDefault); unsigned int aip = 0; unsigned int aop = 0; unsigned int cip = 0; unsigned int cop = 0; unsigned int eip = 0; unsigned int eop = 0; for(unsigned long k = 0; k < info->_portCount; ++k) { PluginPortInfo port_info; port_info._index = k; const LilvPort *lilvPort = lilv_plugin_get_port_by_index(plugin, k); LilvNode *nPname = lilv_port_get_name(plugin, lilvPort); const LilvNode *nPsym = lilv_port_get_symbol(plugin, lilvPort); char cAutoGenPortName [1024]; char cAutoGenPortSym [1024]; memset(cAutoGenPortName, 0, sizeof(cAutoGenPortName)); memset(cAutoGenPortSym, 0, sizeof(cAutoGenPortSym)); snprintf(cAutoGenPortName, sizeof(cAutoGenPortName) - 1, "autoport #%u", (unsigned)k); snprintf(cAutoGenPortSym, sizeof(cAutoGenPortSym) - 1, "autoport#%u", (unsigned)k); const char *_portName = cAutoGenPortName; const char *_portSym = cAutoGenPortSym; if(nPname) _portName = lilv_node_as_string(nPname); if(nPsym) _portSym = lilv_node_as_string(nPsym); if(_portName && _portName[0]) port_info._name = PLUGIN_SET_CSTRING(_portName); if(_portSym && _portSym[0]) port_info._symbol = PLUGIN_SET_CSTRING(_portSym); const bool optional = lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_connectionOptional); if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.lv2_InputPort)) { port_info._type = PluginPortInfo::InputPort; } else if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.lv2_OutputPort)) { port_info._type = PluginPortInfo::OutputPort; } else { DEBUG_PLUGIN_SCAN(stderr, "plugin has port with unknown direction - ignoring\n"); if(nPname) lilv_node_free(nPname); continue; } bool isCVPort = lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.lv2_CVPort); if(isCVPort) port_info._flags |= PluginPortInfo::IsCVPort; if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.lv2_ControlPort) || isCVPort) { port_info._type |= PluginPortInfo::ControlPort; // port_info._valueType = PluginPortInfo::LinearVal; // if(lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_portDiscrete)) // port_info._valueFlags = PluginPortInfo::IntegerVal; // else if(lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_portInteger)) // port_info._valueFlags = PluginPortInfo::IntegerVal; // else if(lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_portTrigger) // || lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_portToggled)) // port_info._valueFlags = PluginPortInfo::ToggledVal; // else if(lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_portLogarithmic)) // port_info._valueFlags = PluginPortInfo::LogVal; if(lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_portDiscrete)) port_info._valueFlags |= PluginPortInfo::IntegerVal; if(lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_portInteger)) port_info._valueFlags |= PluginPortInfo::IntegerVal; if(lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_portToggled)) port_info._valueFlags |= PluginPortInfo::ToggledVal; if(lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_portTrigger)) port_info._valueFlags |= PluginPortInfo::TriggerVal; if(lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_portLogarithmic)) port_info._valueFlags |= PluginPortInfo::LogVal; // "If a port doesn't have a minimum, maximum or default value, // or the port's type is not float, the corresponding array element // will be set to NAN." if(std::isnan(pluginControlsDefault[k])) pluginControlsDefault[k] = 0; else port_info._valueFlags |= PluginPortInfo::HasDefault; if(std::isnan(pluginControlsMin[k])) pluginControlsMin[k] = 0; else port_info._valueFlags |= PluginPortInfo::HasMin; if(std::isnan(pluginControlsMax[k])) pluginControlsMax[k] = 1; else port_info._valueFlags |= PluginPortInfo::HasMax; // Is it a specialized audio control port? if(isCVPort) { // Audio ports don't usually have range information, so make up a range. pluginControlsDefault[k] = 1; pluginControlsMin[k] = 0; pluginControlsMax[k] = 1; port_info._valueFlags |= (PluginPortInfo::HasDefault | PluginPortInfo::HasMin | PluginPortInfo::HasMax); } // It's a normal control port. Do the values depend on the current sample rate? else if (lilv_port_has_property (plugin, lilvPort, lv2CacheNodes.lv2_SampleRate)) { port_info._flags |= PluginPortInfo::ScaleBySamplerate; } port_info._min = pluginControlsMin[k]; port_info._max = pluginControlsMax[k]; port_info._defaultVal = pluginControlsDefault[k]; // Read any value enumerations... LilvScalePoints* scale_points; scale_points = lilv_port_get_scale_points(plugin, lilvPort); if(scale_points) { // Map for ensuring the enumerations are sorted by increasing value. std::map > enum_list; LilvIter* sp_it = lilv_scale_points_begin(scale_points); while(!lilv_scale_points_is_end(scale_points, sp_it)) { const LilvScalePoint* sp = lilv_scale_points_get(scale_points, sp_it); const LilvNode* vn = lilv_scale_point_get_value(sp); const LilvNode* ln = lilv_scale_point_get_label(sp); if(lilv_node_is_float(vn) && lilv_node_is_string(ln)) { const float v = lilv_node_as_float(vn); const PluginPortEnumValue ev(v, PLUGIN_SET_CSTRING(lilv_node_as_string(ln))); enum_list.insert(std::pair(v, ev)); } sp_it = lilv_scale_points_next(scale_points, sp_it); } lilv_scale_points_free(scale_points); // Copy the sorted enumeration values to the vector enumeration list. EnumValueList dst_list; for(std::map>::const_iterator iel = enum_list.begin(); iel != enum_list.end(); ++iel) { dst_list.push_back(iel->second); } if(!dst_list.empty()) { info->_portEnumValMap.insert(PortEnumValueMapPair(k, dst_list)); port_info._valueFlags |= PluginPortInfo::HasEnumerations; } } } else if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.lv2_AudioPort)) { port_info._type |= PluginPortInfo::AudioPort; } else if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.ev_EventPort)) { if(lilv_port_supports_event(plugin, lilvPort, lv2CacheNodes.lv2_TimePosition)) { port_info._flags |= PluginPortInfo::SupportsTimePosition; info->_pluginFlags |= PluginScanInfoStruct::SupportsTimePosition; } port_info._type |= PluginPortInfo::MidiPort; } else if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.atom_AtomPort)) { if(lilv_port_supports_event(plugin, lilvPort, lv2CacheNodes.lv2_TimePosition)) { port_info._flags |= PluginPortInfo::SupportsTimePosition; info->_pluginFlags |= PluginScanInfoStruct::SupportsTimePosition; } port_info._type |= PluginPortInfo::MidiPort; } else if(!optional) { DEBUG_PLUGIN_SCAN(stderr, "plugin has port with unknown type - ignoring plugin:%s\n", name.toLatin1().constData()); if(nPname != 0) lilv_node_free(nPname); return; } if(nPname != 0) lilv_node_free(nPname); if(port_info._type & PluginPortInfo::AudioPort) { if(port_info._type & PluginPortInfo::InputPort) ++aip; else if(port_info._type & PluginPortInfo::OutputPort) ++aop; } if(port_info._type & PluginPortInfo::ControlPort) { if(port_info._type & PluginPortInfo::InputPort) ++cip; else if(port_info._type & PluginPortInfo::OutputPort) ++cop; } if(port_info._type & PluginPortInfo::MidiPort) { if(port_info._type & PluginPortInfo::InputPort) ++eip; else if(port_info._type & PluginPortInfo::OutputPort) ++eop; } if((info->_pluginFlags & PluginScanInfoStruct::HasFreewheelPort) && k == info->_freewheelPortIdx) { port_info._flags |= PluginPortInfo::IsFreewheel; } if((info->_pluginFlags & PluginScanInfoStruct::HasLatencyPort) && k == info->_latencyPortIdx) { port_info._flags |= PluginPortInfo::IsLatency; } info->_portList.push_back(port_info); } info->_inports = aip; info->_outports = aop; info->_controlInPorts = cip; info->_controlOutPorts = cop; info->_eventInPorts = eip; info->_eventOutPorts = eop; if((info->_inports != info->_outports) || lilv_plugin_has_feature(plugin, lv2CacheNodes.lv2_InPlaceBroken)) info->_requiredFeatures |= MusECore::PluginNoInPlaceProcessing; } static void scanLv2Plugin(const LilvPlugin *plugin, PluginScanList* list, const std::set& supportedFeatures, bool do_ports, bool debugStdErr) { // LV2 does not use unique id numbers and frowns upon using anything but the uri. // static unsigned long FakeLv2UniqueID = 1; LilvNode *nameNode = lilv_plugin_get_name(plugin); const LilvNode *uriNode = lilv_plugin_get_uri(plugin); if(!nameNode) return; if(!lilv_node_is_string(nameNode)) { lilv_node_free(nameNode); return; } const char *pluginName = lilv_node_as_string(nameNode); const char *lfp = lilv_file_uri_parse(lilv_node_as_string(lilv_plugin_get_library_uri(plugin)), NULL); LilvNodes *fts = lilv_plugin_get_required_features(plugin); LilvIter *nit = lilv_nodes_begin(fts); bool shouldLoad = true; MusECore::PluginFeatures_t reqfeat = MusECore::PluginNoFeatures; while(true) { if(lilv_nodes_is_end(fts, nit)) break; const LilvNode *fnode = lilv_nodes_get(fts, nit); const char *uri = lilv_node_as_uri(fnode); bool isSupported = (supportedFeatures.find(uri) != supportedFeatures.end()); if(isSupported) { if(std::strcmp(uri, LV2_F_FIXED_BLOCK_LENGTH) == 0) reqfeat |= MusECore::PluginFixedBlockSize; else if(std::strcmp(uri, LV2_F_POWER_OF_2_BLOCK_LENGTH) == 0) reqfeat |= MusECore::PluginPowerOf2BlockSize; else if(std::strcmp(uri, LV2_F_COARSE_BLOCK_LENGTH) == 0) reqfeat |= MusECore::PluginCoarseBlockSize; } else { shouldLoad = false; } nit = lilv_nodes_next(fts, nit); } lilv_nodes_free(fts); //if (!shouldLoad || !isSynth) if(!shouldLoad) //load all plugins for now, not only synths { lilv_free((void*)lfp); // Must free. lilv_node_free(nameNode); return; } const QString name = QString(pluginName) + QString(" LV2"); MusEPlugin::PluginScanInfoStruct info; setPluginScanFileInfo(lfp, &info); if(uriNode) info._uri = PLUGIN_SET_CSTRING(lilv_node_as_string(uriNode)); info._type = PluginScanInfoStruct::PluginTypeLV2; info._class = PluginScanInfoStruct::PluginClassEffect; // Fake id for LV2PluginWrapper functionality. // LV2 does not use unique id numbers and frowns upon using anything but the uri. // info._uniqueID = FakeLv2UniqueID++; //info._label = MusEPlugin::setString(label); info._label = PLUGIN_SET_QSTRING(name); info._name = PLUGIN_SET_QSTRING(name); //info._maker = MusEPlugin::setString(ladspa_descr->Maker); //info._copyright = MusEPlugin::setString(ladspa_descr->Copyright); info._requiredFeatures |= reqfeat; const LilvPluginClass *cls = lilv_plugin_get_class(plugin); const LilvNode *ncuri = lilv_plugin_class_get_uri(cls); const char *clsname = lilv_node_as_uri(ncuri); if(strcmp(clsname, LV2_INSTRUMENT_CLASS) == 0) { info._class |= PluginScanInfoStruct::PluginClassEffect; } if(LilvNode *nAuthor = lilv_plugin_get_author_name(plugin)) { info._maker = PLUGIN_SET_CSTRING(lilv_node_as_string(nAuthor)); lilv_node_free(nAuthor); } if(do_ports) { scanLv2Ports(plugin, &info, debugStdErr); } else { // Does this plugin have a 'freewheel' port? if(const LilvPort *lilvFreeWheelPort = lilv_plugin_get_port_by_designation( plugin, lv2CacheNodes.lv2_InputPort, lv2CacheNodes.lv2_FreeWheelPort)) { info._pluginFlags |= PluginScanInfoStruct::HasFreewheelPort; info._freewheelPortIdx = lilv_port_get_index(plugin, lilvFreeWheelPort); } if(lilv_plugin_has_latency(plugin)) { info._pluginFlags |= PluginScanInfoStruct::HasLatencyPort; info._latencyPortIdx = lilv_plugin_get_latency_port_index(plugin); } info._portCount = lilv_plugin_get_num_ports(plugin); unsigned int aip = 0; unsigned int aop = 0; unsigned int cip = 0; unsigned int cop = 0; unsigned int eip = 0; unsigned int eop = 0; for(unsigned long k = 0; k < info._portCount; ++k) { const LilvPort *lilvPort = lilv_plugin_get_port_by_index(plugin, k); const bool optional = lilv_port_has_property(plugin, lilvPort, lv2CacheNodes.lv2_connectionOptional); bool is_output = false; if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.lv2_OutputPort)) { is_output = true; } else if(!lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.lv2_InputPort)) { DEBUG_PLUGIN_SCAN(stderr, "plugin has port with unknown direction - ignoring\n"); continue; } bool isCVPort = lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.lv2_CVPort); if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.lv2_ControlPort) || isCVPort) { if(is_output) ++cop; else ++cip; } else if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.lv2_AudioPort)) { if(is_output) ++aop; else ++aip; } else if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.ev_EventPort)) { if(is_output) ++eop; else ++eip; if(lilv_port_supports_event(plugin, lilvPort, lv2CacheNodes.lv2_TimePosition)) info._pluginFlags |= PluginScanInfoStruct::SupportsTimePosition; } else if(lilv_port_is_a(plugin, lilvPort, lv2CacheNodes.atom_AtomPort)) { if(is_output) ++eop; else ++eip; if(lilv_port_supports_event(plugin, lilvPort, lv2CacheNodes.lv2_TimePosition)) info._pluginFlags |= PluginScanInfoStruct::SupportsTimePosition; } else if(!optional) { DEBUG_PLUGIN_SCAN(stderr, "plugin has port with unknown type - ignoring plugin:%s\n", name.toLatin1().constData()); return; } } info._inports = aip; info._outports = aop; info._controlInPorts = cip; info._controlOutPorts = cop; info._eventInPorts = eip; info._eventOutPorts = eop; } // Look for optional features we support (now or later)... if(lilv_plugin_has_feature(plugin, lv2CacheNodes.lv2_isLive)) info._pluginFlags |= PluginScanInfoStruct::Realtime; if(lilv_plugin_has_feature(plugin, lv2CacheNodes.lv2_HardRealtimeCapable)) info._pluginFlags |= PluginScanInfoStruct::HardRealtimeCapable; if((info._inports != info._outports) || lilv_plugin_has_feature(plugin, lv2CacheNodes.lv2_InPlaceBroken)) info._requiredFeatures |= MusECore::PluginNoInPlaceProcessing; // Make sure it doesn't already exist. if(list->find(info)) { lilv_free((void*)lfp); // Must free. lilv_node_free(nameNode); return; } list->add(new PluginScanInfo(info)); lilv_free((void*)lfp); // Must free. lilv_node_free(nameNode); } #endif // LV2_SUPPORT //--------------------------------------------------------- // scanLv2Plugins //--------------------------------------------------------- #ifdef LV2_SUPPORT void scanLv2Plugins(PluginScanList* list, bool scanPorts, bool debugStdErr) { std::set supportedFeatures; // REMOVE Tim. lv2. Added. //LV2_Feature* feats[SIZEOF_ARRAY(lv2Features) + 1]; unsigned long int feat = 0; for(; feat < SIZEOF_ARRAY(lv2Features); feat++) { // REMOVE Tim. lv2. Added. //feats[feat] = &lv2Features[feat]; supportedFeatures.insert(lv2Features [feat].URI); } // REMOVE Tim. lv2. Added. //feats[feat] = nullptr; LilvWorld *lilvWorld = 0; lilvWorld = lilv_world_new(); if(!lilvWorld) return; lv2CacheNodes.atom_AtomPort = lilv_new_uri(lilvWorld, LV2_ATOM__AtomPort); lv2CacheNodes.ev_EventPort = lilv_new_uri(lilvWorld, LV2_EVENT__EventPort); lv2CacheNodes.lv2_AudioPort = lilv_new_uri(lilvWorld, LV2_CORE__AudioPort); lv2CacheNodes.lv2_ControlPort = lilv_new_uri(lilvWorld, LV2_CORE__ControlPort); lv2CacheNodes.lv2_InputPort = lilv_new_uri(lilvWorld, LV2_CORE__InputPort); lv2CacheNodes.lv2_OutputPort = lilv_new_uri(lilvWorld, LV2_CORE__OutputPort); lv2CacheNodes.lv2_connectionOptional = lilv_new_uri(lilvWorld, LV2_CORE__connectionOptional); lv2CacheNodes.host_uiType = lilv_new_uri(lilvWorld, LV2_UI_HOST_URI); lv2CacheNodes.ext_uiType = lilv_new_uri(lilvWorld, LV2_UI_EXTERNAL); lv2CacheNodes.ext_d_uiType = lilv_new_uri(lilvWorld, LV2_UI_EXTERNAL_DEPRECATED); lv2CacheNodes.lv2_portContinuous = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__continuousCV); lv2CacheNodes.lv2_portDiscrete = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__discreteCV); lv2CacheNodes.lv2_portEnumeration = lilv_new_uri(lilvWorld, LV2_CORE__enumeration); lv2CacheNodes.lv2_portLogarithmic = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__logarithmic); lv2CacheNodes.lv2_portInteger = lilv_new_uri(lilvWorld, LV2_CORE__integer); lv2CacheNodes.lv2_portTrigger = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__trigger); lv2CacheNodes.lv2_portToggled = lilv_new_uri(lilvWorld, LV2_CORE__toggled); lv2CacheNodes.lv2_TimePosition = lilv_new_uri(lilvWorld, LV2_TIME__Position); lv2CacheNodes.lv2_FreeWheelPort = lilv_new_uri(lilvWorld, LV2_CORE__freeWheeling); lv2CacheNodes.lv2_isLive = lilv_new_uri(lilvWorld, LV2_CORE__isLive); lv2CacheNodes.lv2_HardRealtimeCapable= lilv_new_uri(lilvWorld, LV2_CORE__hardRTCapable); lv2CacheNodes.lv2_InPlaceBroken = lilv_new_uri(lilvWorld, LV2_CORE__inPlaceBroken); lv2CacheNodes.lv2_SampleRate = lilv_new_uri(lilvWorld, LV2_CORE__sampleRate); lv2CacheNodes.lv2_CVPort = lilv_new_uri(lilvWorld, LV2_CORE__CVPort); lv2CacheNodes.lv2_psetPreset = lilv_new_uri(lilvWorld, LV2_PRESETS__Preset); lv2CacheNodes.lv2_rdfsLabel = lilv_new_uri(lilvWorld, "http://www.w3.org/2000/01/rdf-schema#label"); lv2CacheNodes.lv2_actionSavePreset = lilv_new_uri(lilvWorld, "http://www.muse-sequencer.org/lv2host#lv2_actionSavePreset"); lv2CacheNodes.lv2_actionUpdatePresets= lilv_new_uri(lilvWorld, "http://www.muse-sequencer.org/lv2host#lv2_actionUpdatePresets"); lv2CacheNodes.end = NULL; lilv_world_load_all(lilvWorld); const LilvPlugins *plugins = lilv_world_get_all_plugins(lilvWorld); LilvIter *pit = lilv_plugins_begin(plugins); while(true) { if(lilv_plugins_is_end(plugins, pit)) { break; } const LilvPlugin *plugin = lilv_plugins_get(plugins, pit); if(lilv_plugin_is_replaced(plugin)) { pit = lilv_plugins_next(plugins, pit); continue; } // REMOVE Tim. lv2. Added. // // Test instantiating the plugin. TODO: Maybe try to pass the correct sample rate. // LilvInstance* handle = lilv_plugin_instantiate(plugin, 44100, feats); // if(!handle) // { // pit = lilv_plugins_next(plugins, pit); // continue; // } // // // // TODO: Maybe do some stuff with the plugin. // // // // No crash so far. Done with instance. Close it - free it. // lilv_instance_free(handle); // Now go ahead and scan the textual descriptions of the plugin. scanLv2Plugin(plugin, list, supportedFeatures, scanPorts, debugStdErr); pit = lilv_plugins_next(plugins, pit); } for(LilvNode **n = (LilvNode **)&lv2CacheNodes; *n; ++n) lilv_node_free(*n); lilv_world_free(lilvWorld); lilvWorld = NULL; } #else void scanLv2Plugins(PluginScanList* /*list*/, bool /*scanPorts*/, bool /*debugStdErr*/) { } #endif // LV2_SUPPORT //--------------------------------------------------------- // scanAllPlugins //--------------------------------------------------------- void scanAllPlugins( const QString& museGlobalLib, PluginScanList* list, bool scanPorts, bool debugStdErr, PluginScanInfoStruct::PluginType_t types) { if(types & (PluginScanInfoStruct::PluginTypeDSSI | PluginScanInfoStruct::PluginTypeDSSIVST)) // Take care of DSSI plugins first... scanDssiPlugins(list, scanPorts, debugStdErr); if(types & (PluginScanInfoStruct::PluginTypeLADSPA)) // Now do LADSPA plugins... scanLadspaPlugins(museGlobalLib, list, scanPorts, debugStdErr); if(types & (PluginScanInfoStruct::PluginTypeMESS)) // Now do MESS plugins... scanMessPlugins(museGlobalLib, list, scanPorts, debugStdErr); if(types & (PluginScanInfoStruct::PluginTypeLinuxVST)) // Now do LinuxVST plugins... scanLinuxVSTPlugins(list, scanPorts, debugStdErr); if(types & (PluginScanInfoStruct::PluginTypeLV2)) // Now do LV2 plugins... scanLv2Plugins(list, scanPorts, debugStdErr); } typedef std::map > filepath_set; typedef std::pair filepath_set_pair; //--------------------------------------------------------- // findPluginFilesDir // This might be called recursively! //--------------------------------------------------------- static QString findPluginFilesDir( const QString& dirname, PluginScanInfoStruct::PluginType_t types, filepath_set& fplist, bool debugStdErr, // Only for recursions, original top caller should not touch! int recurseLevel = 0 ) { const int max_levels = 10; if(recurseLevel >= max_levels) { std::fprintf(stderr, "findPluginFilesDir: Ignoring too-deep directory level (max:%d) at:%s\n", max_levels, dirname.toLocal8Bit().constData()); return QString(); } DEBUG_PLUGIN_SCAN(stderr, "find plugin dir <%s>\n", dirname.toLatin1().constData()); QDir pluginDir( dirname, QString("*.so"), QDir::Name | QDir::IgnoreCase, QDir::Drives | QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot); if(pluginDir.exists()) { QFileInfoList fi_list = pluginDir.entryInfoList(); QFileInfoList::iterator it=fi_list.begin(); while(it != fi_list.end()) { const QFileInfo& fi = *it; if(fi.isDir()) { // RECURSIVE! findPluginFilesDir(fi.filePath(), types, fplist, debugStdErr, recurseLevel + 1); } else { fplist.insert(filepath_set_pair(fi.filePath(), fi.lastModified().toMSecsSinceEpoch())); } ++it; } } return QString(); } //--------------------------------------------------------- // findLadspaPluginFiles //--------------------------------------------------------- static void findLadspaPluginFiles(const QString& museGlobalLib, filepath_set& fplist, bool debugStdErr) { const QStringList sl = pluginGetLadspaDirectories(museGlobalLib); for(QStringList::const_iterator it = sl.cbegin(); it != sl.cend(); ++it) findPluginFilesDir(*it, PluginScanInfoStruct::PluginTypeAll, fplist, debugStdErr); } //--------------------------------------------------------- // findMessPluginFiles //--------------------------------------------------------- static void findMessPluginFiles(const QString& museGlobalLib, filepath_set& fplist, bool debugStdErr) { const QStringList sl = pluginGetMessDirectories(museGlobalLib); for(QStringList::const_iterator it = sl.cbegin(); it != sl.cend(); ++it) findPluginFilesDir(*it, PluginScanInfoStruct::PluginTypeAll, fplist, debugStdErr); } //--------------------------------------------------------- // findDssiPluginFiles //--------------------------------------------------------- #ifdef DSSI_SUPPORT static void findDssiPluginFiles(filepath_set& fplist, bool debugStdErr) { const QStringList sl = pluginGetDssiDirectories(); for(QStringList::const_iterator it = sl.cbegin(); it != sl.cend(); ++it) findPluginFilesDir(*it, PluginScanInfoStruct::PluginTypeAll, fplist, debugStdErr); } #else // No DSSI_SUPPORT static void findDssiPluginFiles(filepath_set& /*fplist*/, bool /*debugStdErr*/) { } #endif // DSSI_SUPPORT //--------------------------------------------------------- // findLinuxVSTPluginFiles //--------------------------------------------------------- #ifdef VST_NATIVE_SUPPORT static void findLinuxVSTPluginFiles(filepath_set& fplist, bool debugStdErr) { const QStringList sl = pluginGetLinuxVstDirectories(); for(QStringList::const_iterator it = sl.cbegin(); it != sl.cend(); ++it) findPluginFilesDir(*it, PluginScanInfoStruct::PluginTypeAll, fplist, debugStdErr); } #else // Not VST_NATIVE_SUPPORT static void findLinuxVSTPluginFiles(filepath_set& /*fplist*/, bool /*debugStdErr*/) { } #endif // VST_NATIVE_SUPPORT // SPECIAL for LV2: No need for a cache file. // Do not find and compare LV2 library files here. // This caused problems with identically named plugins // being excluded, and triggering rescans every time. // LV2 frowns upon using anything but the URI ! #if 0 //--------------------------------------------------------- // findLv2PluginFile //--------------------------------------------------------- #ifdef LV2_SUPPORT static void findLv2PluginFile(const LilvPlugin *plugin, filepath_set& fplist, const std::set& supportedFeatures, bool /*debugStdErr*/) { // LV2 does not use unique id numbers and frowns upon using anything but the uri. // static unsigned long FakeLv2UniqueID = 1; LilvNode *nameNode = lilv_plugin_get_name(plugin); if(!nameNode) return; if(!lilv_node_is_string(nameNode)) { lilv_node_free(nameNode); return; } const char *lfp = lilv_file_uri_parse(lilv_node_as_string(lilv_plugin_get_library_uri(plugin)), NULL); LilvNodes *fts = lilv_plugin_get_required_features(plugin); LilvIter *nit = lilv_nodes_begin(fts); bool shouldLoad = true; while(true) { if(lilv_nodes_is_end(fts, nit)) break; const LilvNode *fnode = lilv_nodes_get(fts, nit); const char *uri = lilv_node_as_uri(fnode); const bool isSupported = (supportedFeatures.find(uri) != supportedFeatures.end()); if(!isSupported) shouldLoad = false; nit = lilv_nodes_next(fts, nit); } lilv_nodes_free(fts); //if (!shouldLoad || !isSynth) if(!shouldLoad) //load all plugins for now, not only synths { lilv_free((void*)lfp); // Must free. lilv_node_free(nameNode); return; } if(lfp && lfp[0]) { QFileInfo fi(lfp); if(fi.exists()) fplist.insert(filepath_set_pair(fi.filePath(), fi.lastModified().toMSecsSinceEpoch())); } lilv_free((void*)lfp); // Must free. lilv_node_free(nameNode); } #endif //--------------------------------------------------------- // findLv2PluginFiles //--------------------------------------------------------- #ifdef LV2_SUPPORT static void findLv2PluginFiles(filepath_set& fplist, bool debugStdErr) { std::set supportedFeatures; unsigned long int feat = 0; for(; feat < SIZEOF_ARRAY(lv2Features); feat++) { supportedFeatures.insert(lv2Features [feat].URI); } LilvWorld *lilvWorld = 0; lilvWorld = lilv_world_new(); if(!lilvWorld) return; lilv_world_load_all(lilvWorld); const LilvPlugins *plugins = lilv_world_get_all_plugins(lilvWorld); LilvIter *pit = lilv_plugins_begin(plugins); while(true) { if(lilv_plugins_is_end(plugins, pit)) break; const LilvPlugin *plugin = lilv_plugins_get(plugins, pit); if(lilv_plugin_is_replaced(plugin)) { pit = lilv_plugins_next(plugins, pit); continue; } findLv2PluginFile(plugin, fplist, supportedFeatures, debugStdErr); pit = lilv_plugins_next(plugins, pit); } lilv_world_free(lilvWorld); lilvWorld = NULL; } #else static void findLv2PluginFiles(filepath_set& /*fplist*/, bool /*debugStdErr*/) { } #endif #endif // 0 //--------------------------------------------------------- // findPluginFiles //--------------------------------------------------------- static void findPluginFiles(const QString& museGlobalLib, filepath_set& fplist, bool debugStdErr, PluginScanInfoStruct::PluginType_t types) { if(types & (PluginScanInfoStruct::PluginTypeDSSI | PluginScanInfoStruct::PluginTypeDSSIVST)) { // Take care of DSSI plugins first... findDssiPluginFiles(fplist, debugStdErr); } if(types & (PluginScanInfoStruct::PluginTypeLADSPA)) { // Now do LADSPA plugins... findLadspaPluginFiles(museGlobalLib, fplist, debugStdErr); } if(types & (PluginScanInfoStruct::PluginTypeMESS)) { // Now do MESS plugins... findMessPluginFiles(museGlobalLib, fplist, debugStdErr); } if(types & (PluginScanInfoStruct::PluginTypeLinuxVST)) { // Now do LinuxVST plugins... findLinuxVSTPluginFiles(fplist, debugStdErr); } // SPECIAL for LV2: No need for a cache file. // Do not find and compare LV2 library files here. // This caused problems with identically named plugins // being excluded, and triggering rescans every time. // LV2 frowns upon using anything but the URI ! //if(types & (PluginScanInfoStruct::PluginTypeLV2)) //{ // // Now do LV2 plugins... // findLv2PluginFiles(fplist, debugStdErr); //} } //--------------------------------------------------------- // writePluginCacheFile //--------------------------------------------------------- bool writePluginCacheFile( const QString& scanOutPath, const QString& filename, const PluginScanList& list, bool writePorts, PluginScanInfoStruct::PluginType_t types) { bool res = false; const QString targ_filepath = scanOutPath + "/" + filename; const QDir scanOutDir(scanOutPath); if(!scanOutDir.exists()) { std::fprintf(stderr, "Creating plugin cache directory:%s\n", scanOutPath.toLatin1().constData()); scanOutDir.mkpath("."); } QFile targ_qfile(targ_filepath); if(!targ_qfile.open(QIODevice::WriteOnly | QIODevice::Text)) { std::fprintf(stderr, "writePluginCacheFile: targ_qfile.open() failed: filename:%s\n", filename.toLatin1().constData()); } else { MusECore::Xml xml(&targ_qfile); int level = 0; xml.header(); level = xml.putFileVersion(level); for(ciPluginScanList ips = list.begin(); ips != list.end(); ++ips) { PluginScanInfoRef inforef = *ips; const PluginScanInfoStruct& infos = inforef->info(); // Look only for the specified type(s). if(infos._type & types) writePluginScanInfo(level, xml, infos, writePorts); } xml.tag(1, "/muse"); DEBUG_PLUGIN_SCAN(stderr, "writePluginCacheFile: targ_qfile closing filename:%s\n", filename.toLatin1().constData()); targ_qfile.close(); res = true; } return res; } //--------------------------------------------------------- // createPluginCacheFile //--------------------------------------------------------- bool createPluginCacheFile( const QString& path, PluginScanInfoStruct::PluginType type, PluginScanList* list, bool writePorts, const QString& museGlobalLib, PluginScanInfoStruct::PluginType_t types, bool debugStdErr) { // Scan all plugins into the list. scanAllPlugins(museGlobalLib, list, writePorts, debugStdErr, type); // Write the list's cache file. if(!writePluginCacheFile(path, QString(pluginCacheFilename(type)), *list, writePorts, types)) { std::fprintf(stderr, "createCacheFile: writePluginCacheFile() failed: filename:%s\n", pluginCacheFilename(type)); return false; } return true; } //--------------------------------------------------------- // createPluginCacheFiles //--------------------------------------------------------- bool createPluginCacheFiles( const QString& path, PluginScanList* list, bool writePorts, const QString& museGlobalLib, PluginScanInfoStruct::PluginType_t types, bool debugStdErr) { if(types & (PluginScanInfoStruct::PluginTypeDSSI | PluginScanInfoStruct::PluginTypeDSSIVST)) createPluginCacheFile(path, PluginScanInfoStruct::PluginTypeDSSI, list, writePorts, museGlobalLib, PluginScanInfoStruct::PluginTypeDSSI | PluginScanInfoStruct::PluginTypeDSSIVST, debugStdErr); // NOTE: Because the dss-vst library installs itself in both the dssi AND ladspa folders, // we must include dssi-vst types in the search here. // The result is that the ladspa cache file will contain BOTH the ladspa folder dssi-vst file scan // and the dssi folder dss-vst file scan. if(types & PluginScanInfoStruct::PluginTypeLADSPA) createPluginCacheFile(path, PluginScanInfoStruct::PluginTypeLADSPA, list, writePorts, museGlobalLib, PluginScanInfoStruct::PluginTypeLADSPA | PluginScanInfoStruct::PluginTypeDSSIVST, debugStdErr); if(types & PluginScanInfoStruct::PluginTypeLinuxVST) createPluginCacheFile(path, PluginScanInfoStruct::PluginTypeLinuxVST, list, writePorts, museGlobalLib, PluginScanInfoStruct::PluginTypeLinuxVST, debugStdErr); if(types & PluginScanInfoStruct::PluginTypeMESS) createPluginCacheFile(path, PluginScanInfoStruct::PluginTypeMESS, list, writePorts, museGlobalLib, PluginScanInfoStruct::PluginTypeMESS, debugStdErr); // SPECIAL for LV2: No need for a cache file. Do not create one here. Read directly into the list later. //if(types & PluginScanInfoStruct::PluginTypeLV2) // createPluginCacheFile(path, PluginScanInfoStruct::PluginTypeLV2, list, writePorts, // museGlobalLib, PluginScanInfoStruct::PluginTypeLV2, debugStdErr); if(types & PluginScanInfoStruct::PluginTypeVST) createPluginCacheFile(path, PluginScanInfoStruct::PluginTypeVST, list, writePorts, museGlobalLib, PluginScanInfoStruct::PluginTypeVST, debugStdErr); if(types & PluginScanInfoStruct::PluginTypeUnknown) createPluginCacheFile(path, PluginScanInfoStruct::PluginTypeUnknown, list, writePorts, museGlobalLib, PluginScanInfoStruct::PluginTypeUnknown, debugStdErr); return true; } //--------------------------------------------------------- // checkPluginCacheFiles //--------------------------------------------------------- bool checkPluginCacheFiles( const QString& path, PluginScanList* list, bool writePorts, bool alwaysRecreate, bool dontRecreate, const QString& museGlobalLib, PluginScanInfoStruct::PluginType_t types, bool debugStdErr ) { filepath_set cache_fpset; bool res = true; bool cache_dirty = false; //----------------------------------------------------- // Read whatever we've got in our current cache files. //----------------------------------------------------- if(!readPluginCacheFiles(path, list, false, false, types)) { cache_dirty = true; std::fprintf(stderr, "checkPluginCacheFiles: readAllPluginCacheFiles() failed\n"); } // Check if cache is dirty. Don't bother if we already know it is dirty. if(!dontRecreate && !cache_dirty) { //----------------------------------------------------- // Gather the current plugin files. //----------------------------------------------------- filepath_set fpset; findPluginFiles(museGlobalLib, fpset, debugStdErr, types); //------------------------------------------------------------------------- // Gather the unique (non-duplicate) plugin file paths found in our cache. //------------------------------------------------------------------------- for(iPluginScanList ips = list->begin(); ips != list->end(); ++ips) { PluginScanInfoRef inforef = *ips; const PluginScanInfoStruct& infos = inforef->info(); cache_fpset.insert(filepath_set_pair(PLUGIN_GET_QSTRING(infos.filePath()), infos._fileTime)); } //--------------------------------------- // Check for missing or altered plugins. //--------------------------------------- for(filepath_set::iterator icfps = cache_fpset.begin(); icfps != cache_fpset.end(); ++icfps) { filepath_set::iterator ifpset = fpset.find(icfps->first); if(ifpset == fpset.end() || ifpset->second != icfps->second) { cache_dirty = true; if (debugStdErr) { std::fprintf(stderr, "Setting cache to dirty due to missing or modified plugins:\n"); if(ifpset == fpset.end()) std::fprintf(stderr, "Missing plugin: %s:\n", icfps->first.toLatin1().data()); else std::fprintf(stderr, "Modified plugin: %s (Cache ts: %ld / File ts: %ld)\n", icfps->first.toLatin1().data(), icfps->second, ifpset->second); } break; } // Done with the current plugin file path. Erase it. fpset.erase(ifpset); } //------------------------- // Check for new plugins. //------------------------- // Any remaining items in fpset must be 'new' plugins. if(!cache_dirty && !fpset.empty()) { if(debugStdErr) { std::fprintf(stderr, "Setting cache to dirty due to NEW plugins:\n"); for (auto &plug: fpset) { std::fprintf(stderr, "New plugin %s:\n", plug.first.toLatin1().data()); } } cache_dirty = true; } } // If ANY of the cache files do not exist we will recreate all of them. // The reason is that it might be good to show the user plugins found // in folders which might not really belong there but ended up there // perhaps by accident, like mixing your vst and linux vst plugins. // For that we must rescan everything even if only one cache file is missing. // If ANY of the caches are dirty or we are forcing recreation, create them now. if(!dontRecreate && (alwaysRecreate || cache_dirty)) { if(debugStdErr) std::fprintf(stderr, "Re-scanning and creating plugin cache files...\n"); list->clear(); if(!createPluginCacheFiles(path, list, writePorts, museGlobalLib, types, debugStdErr)) { res = false; std::fprintf(stderr, "checkPluginCacheFiles: createPluginCacheFiles() failed\n"); } } // SPECIAL for LV2: Get rid of old cache file. Not used any more. const QString targ_filepath = path + "/" + QString(pluginCacheFilename(PluginScanInfoStruct::PluginTypeLV2)); QFile targ_qfile(targ_filepath); if(targ_qfile.exists()) { std::fprintf(stderr, "Deleting obsolete LV2 plugin cache file:%s\n", targ_filepath.toLatin1().constData()); if(!targ_qfile.remove()) std::fprintf(stderr, "Error: Deleting obsolete LV2 plugin cache file failed!\n"); } // SPECIAL for LV2: No need for a cache file. // Bring LV2 plugins directly into the list, after all the cache scanning and creation. if(types & PluginScanInfoStruct::PluginTypeLV2) scanLv2Plugins(list, writePorts, debugStdErr); return res; } } // namespace MusEPlugin muse-4.0.0/src/libs/plugin/plugin_cache_writer.h000066400000000000000000000174261404105311000216670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // plugin_cache_writer.h // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PLUGIN_CACHE_WRITER_H__ #define __PLUGIN_CACHE_WRITER_H__ #include #include "config.h" #include "globaldefs.h" #include "plugin_scan.h" #include "plugin_list.h" #include "synti/libsynti/mess.h" #include #ifdef DSSI_SUPPORT #include #endif // DSSI_SUPPORT #ifdef LV2_SUPPORT #include "lilv/lilv.h" #endif // LV2_SUPPORT #ifdef VST_NATIVE_SUPPORT #ifdef VST_SDK_QUIRK #define __cdecl #endif // VST_SDK_QUIRK #include "aeffectx.h" #ifdef VST_VESTIGE_SUPPORT #ifndef effGetProgramNameIndexed #define effGetProgramNameIndexed 29 #endif #endif // VST_VESTIGE_SUPPORT #ifdef VST_VESTIGE_SUPPORT namespace MusEPlugin { enum VstStringLengths { VstNameLen = VestigeMaxNameLen, VstLabelLen = VestigeMaxLabelLen, VstShortLabelLen = VestigeMaxShortLabelLen, VstCategoryLabelLen = VestigeMaxCategLabelLen, VstFileNameLen = VestigeMaxFileNameLen }; } // namespace MusEPlugin #else // VST_VESTIGE_SUPPORT namespace MusEPlugin { enum VstStringLengths { MaxVstNameLen = 64, MaxVstLabelLen = 64, MaxVstShortLabelLen = 8, MaxVstCategLabelLen = 24, MaxVstFileNameLen = 100 }; } // namespace MusEPlugin #endif // VST_VESTIGE_SUPPORT #ifndef VST_2_4_EXTENSIONS #ifndef VST_VESTIGE_SUPPORT typedef long VstInt32; typedef long VstIntPtr; #else typedef int32_t VstInt32; typedef intptr_t VstIntPtr; #define VSTCALLBACK #endif // VST_VESTIGE_SUPPORT #endif // VST_2_4_EXTENSIONS // A handy typedef consistent with that used for ladspa, dssi, mess etc. typedef AEffect* (*LinuxVST_Instance_Function)(audioMasterCallback); #endif // VST_NATIVE_SUPPORT // Forward declarations: namespace MusECore { class Xml; } namespace MusEPlugin { //----------------------------------------- // functions //----------------------------------------- // TODO Depending on our needs later, make some of these private static in the cpp file. bool scanLadspaPorts(const LADSPA_Descriptor* ladspa_descr, PluginScanInfoStruct* info, bool do_rdf); bool scanLadspaDescriptor(const char* filename, const LADSPA_Descriptor* ladspa_descr, PluginScanInfoStruct* info, bool do_ports, bool do_rdf); bool writeLadspaInfo(const char* filename, LADSPA_Descriptor_Function ladspa, bool do_ports, int level, MusECore::Xml& xml); bool scanMessDescriptor(const char* filename, const MESS* mess_descr, PluginScanInfoStruct* info); bool writeMessInfo(const char* filename, MESS_Descriptor_Function mess, bool do_ports, int level, MusECore::Xml& xml); #ifdef DSSI_SUPPORT QString getDssiUiFilename(PluginScanInfoStruct* info); bool scanDssiDescriptor(const char* filename, const DSSI_Descriptor* dssi_descr, PluginScanInfoStruct* info, bool do_ports, bool do_rdf); bool writeDssiInfo(const char* filename, DSSI_Descriptor_Function dssi, bool do_ports, int level, MusECore::Xml& xml); #endif #ifdef VST_NATIVE_SUPPORT bool scanLinuxVstPorts(AEffect *plugin, PluginScanInfoStruct* info); bool scanLinuxVstDescriptor(const char* filename, AEffect *plugin, long int id, PluginScanInfoStruct* info, bool do_ports); bool writeLinuxVstInfo(const char* filename, LinuxVST_Instance_Function lvst, bool do_ports, int level, MusECore::Xml& xml); #endif #ifdef LV2_SUPPORT void scanLv2Ports(const LilvPlugin *plugin, PluginScanInfoStruct* info, bool debugStdErr); #endif bool writeUnknownPluginInfo(const char* filename, int level, MusECore::Xml& xml); void writePluginScanInfo(int level, MusECore::Xml& xml, const PluginScanInfoStruct& info, bool writePorts); // The museGlobalLib is where to find the application's installed libraries. void scanLadspaPlugins(const QString& museGlobalLib, PluginScanList* list, bool scanPorts, bool debugStdErr); void scanMessPlugins(const QString& museGlobalLib, PluginScanList* list, bool scanPorts, bool debugStdErr); void scanDssiPlugins(PluginScanList* list, bool scanPorts, bool debugStdErr); void scanLinuxVSTPlugins(PluginScanList* list, bool scanPorts, bool debugStdErr); void scanLv2Plugins(PluginScanList* list, bool scanPorts, bool debugStdErr); void scanAllPlugins(const QString& museGlobalLib, PluginScanList* list, bool scanPorts, bool debugStdErr, PluginScanInfoStruct::PluginType_t types = PluginScanInfoStruct::PluginTypeAll); //----------------------------------------- // Public cache writer functions //----------------------------------------- // Create (or overwrite) a cache file. bool createPluginCacheFile( // Path to the cache file directory (eg. config path + /scanner). const QString& path, // The type of plugin cache file to write. PluginScanInfoStruct::PluginType type, // List to read into and write from. PluginScanList* list, // Whether to write port information. bool writePorts, // Where to find the application's installed libraries. const QString& museGlobalLib = QString(), // The types of plugins to write into this one file. PluginScanInfoStruct::PluginType_t types = PluginScanInfoStruct::PluginTypeAll, // Print some stderr text bool debugStdErr = false ); bool createPluginCacheFiles( // Path to the cache file directory (eg. config path + /scanner). const QString& path, // List to read into and write from. PluginScanList* list, // Whether to write port information. bool writePorts, // Where to find the application's installed libraries. const QString& museGlobalLib, // The types of plugin cache files to create. PluginScanInfoStruct::PluginType_t types = PluginScanInfoStruct::PluginTypeAll, // Print some stderr text bool debugStdErr = false ); // Checks existence of given cache file types. // Writes ALL the given types of cache files if ANY are not found. // Returns true on success. bool checkPluginCacheFiles( // Path to the cache file directory (eg. config path + /scanner). const QString& path, // List to read into and write from. PluginScanList* list, // Whether to write port information. bool writePorts, // Force it to create every time. bool alwaysRecreate = false, // Do not re-create. bool dontRecreate = false, // Where to find the application's installed libraries. const QString& museGlobalLib = QString(), // The types of plugin cache files to write. PluginScanInfoStruct::PluginType_t types = PluginScanInfoStruct::PluginTypeAll, // Print some stderr text bool debugStdErr = false ); // Write the list of plugins to a plugin cache text file. bool writePluginCacheFile( // Path to the cache file directory (eg. config path + /scanner). const QString& scanOutPath, // Cache file name. const QString& filename, // List to write. const PluginScanList& list, // Whether to write port information. bool writePorts, // The types of plugins to write. PluginScanInfoStruct::PluginType_t types = PluginScanInfoStruct::PluginTypeAll); } // namespace MusEPlugin #endif muse-4.0.0/src/libs/plugin/plugin_list.cpp000066400000000000000000000074661404105311000205410ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // plugin_list.cpp // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "plugin_list.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_PLUGIN_LIST(dev, format, args...) // std::fprintf(dev, format, ##args); namespace MusEPlugin { //--------------------------------------------------------- // find //--------------------------------------------------------- PluginScanInfoRef PluginScanList::find(const PluginInfoString_t& file, const PluginInfoString_t& uri, const PluginInfoString_t& label, PluginScanInfoStruct::PluginType_t types) const { const bool f_empty = file.isEmpty(); const bool u_empty = uri.isEmpty(); const bool l_empty = label.isEmpty(); for(ciPluginScanList i = begin(); i != end(); ++i) { const PluginScanInfoRef& ref = *i; if((ref->info()._type & types) && //(!u_empty || f_empty || file == ref->info()._completeBaseName) && (!u_empty || f_empty || file == ref->info().filePath()) && (u_empty || uri == ref->info()._uri) && (!u_empty || l_empty || label == ref->info()._label)) return ref; } //fprintf(stderr, "Plugin <%s> not found\n", name.toLatin1().constData()); return PluginScanInfoRef(); } PluginScanInfoRef PluginScanList::find(const PluginScanInfoStruct& info) const { //const bool f_empty = info._completeBaseName.isEmpty(); const bool f_empty = info.filePath().isEmpty(); const bool u_empty = info._uri.isEmpty(); const bool l_empty = info._label.isEmpty(); for(ciPluginScanList i = begin(); i != end(); ++i) { const PluginScanInfoRef& ref = *i; if((info._type == ref->info()._type) && //(!u_empty || f_empty || info._completeBaseName == ref->info()._completeBaseName) && (!u_empty || f_empty || info.filePath() == ref->info().filePath()) && (u_empty || info._uri == ref->info()._uri) && (!u_empty || l_empty || info._label == ref->info()._label)) return ref; } //fprintf(stderr, "Plugin <%s> not found\n", name.toLatin1().constData()); return PluginScanInfoRef(); } //--------------------------------------------------------- // add //--------------------------------------------------------- bool PluginScanList::add(PluginScanInfo* info) { // Ignore duplicates. // NOTE: For now we don't ignore anything down at this low-level. Ignoring is done more at higher levels. // PluginScanInfoRef psi = find(info->info()._completeBaseName, info->info()._label); // if(!psi) // { // fprintf(stderr, "PluginScanList::add: Ignoring %s: path:%s duplicate path of %s\n", // info->info()._label.c_str(), // info->info().fileName().c_str(), // psi->info().fileName().c_str()); // return false; // } // Takes ownership of the object. push_back(PluginScanInfoRef(info)); return true; } } // namespace MusEPlugin muse-4.0.0/src/libs/plugin/plugin_list.h000066400000000000000000000044511404105311000201750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // plugin_list.h // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PLUGIN_LIST_H__ #define __PLUGIN_LIST_H__ #include "plugin_scan.h" #include #include namespace MusEPlugin { //------------------------------------------------- // PluginScanListRef // A reference counted pointer to PluginScanInfo //------------------------------------------------- typedef std::shared_ptr PluginScanInfoRef; //----------------------------------------- // PluginScanList //----------------------------------------- class PluginScanList : public std::list { public: PluginScanList() {} bool add(PluginScanInfo* info); // Each argument optional, can be empty. // If uri is not empty, the search is based solely on it, the other arguments are ignored. PluginScanInfoRef find(const PluginInfoString_t& file, const PluginInfoString_t& uri, const PluginInfoString_t& label, PluginScanInfoStruct::PluginType_t types = PluginScanInfoStruct::PluginTypeAll) const; // If uri is not empty, the search is based solely on it, the other info members are ignored. PluginScanInfoRef find(const PluginScanInfoStruct& info) const; }; typedef PluginScanList::iterator iPluginScanList; typedef PluginScanList::const_iterator ciPluginScanList; } // namespace MusEPlugin #endif muse-4.0.0/src/libs/plugin/plugin_rdf.cpp000066400000000000000000000112641404105311000203300ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // plugin_rdf.cpp // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "plugin_rdf.h" #include "config.h" #ifdef HAVE_LRDF #include #endif // HAVE_LRDF #include #include // For debugging output: Uncomment the fprintf section. #define DEBUG_PLUGIN_RDF(dev, format, args...) // std::fprintf(dev, format, ##args); namespace MusEPlugin { //--------------------------------------------------------- // scanLrdfDir //--------------------------------------------------------- void scanLrdfDir(const QString& dirname, QStringList* rdfs, bool debugStdErr, int recurseLevel) { const int max_levels = 10; if(recurseLevel >= max_levels) { std::fprintf(stderr, "scanLrdfDir: Ignoring too-deep directory level (max:%d) at:%s\n", max_levels, dirname.toLocal8Bit().constData()); return; } DEBUG_PLUGIN_RDF(stderr, "scan lrdf dir <%s>\n", dirname.toLatin1().constData()); QDir pluginDir; pluginDir.setPath(dirname); pluginDir.setFilter(QDir::Drives | QDir::Files | QDir::AllDirs | QDir::NoDotAndDotDot); pluginDir.setSorting(QDir::Name | QDir::IgnoreCase); if(pluginDir.exists()) { pluginDir.setNameFilters(QStringList() << "*.rdfs" << "*.rdf"); QFileInfoList fi_list = pluginDir.entryInfoList(); QFileInfoList::iterator it = fi_list.begin(); while(it != fi_list.end()) { const QFileInfo& fi = *it; if(fi.isDir()) // RECURSIVE! scanLrdfDir(fi.filePath(), rdfs, debugStdErr, recurseLevel + 1); else { const QByteArray fi_fp = fi.filePath().toLocal8Bit(); const QString fi_bn = fi.completeBaseName(); const int rdfs_sz = rdfs->size(); int i = 0; for( ; i < rdfs_sz; ++i) { const QFileInfo s_fi = (rdfs->at(i)); if(s_fi.completeBaseName() == fi_bn) { if(debugStdErr) std::fprintf(stderr, "Scanning LRDF directory: Ignoring RDF file: %s duplicate file of: %s\n", fi_fp.constData(), s_fi.filePath().toLocal8Bit().constData()); break; } } // Not found. if(i >= rdfs_sz) rdfs->append(fi.filePath()); } ++it; } } } //--------------------------------------------------------- // scanLrdfPlugins //--------------------------------------------------------- void scanLrdfPlugins(QStringList* rdfs, bool debugStdErr) { QString lrdfPath = std::getenv("LRDF_PATH"); if(lrdfPath.isEmpty()) { QString share_rdf_dir(SHAREDIR); const QByteArray appDir = qgetenv("APPDIR"); if (!appDir.isEmpty()) share_rdf_dir = appDir + share_rdf_dir; if(!share_rdf_dir.isEmpty()) share_rdf_dir += "/rdf:"; QString homePath = std::getenv("HOME"); if(!homePath.isEmpty()) homePath += QString("/lrdf:"); lrdfPath = // Our own rdf files (and fixes) take priority. share_rdf_dir + // Then the usual home place. homePath + // Then the system. QString("/usr/local/share/ladspa/rdf:/usr/share/ladspa/rdf"); } if(!lrdfPath.isEmpty()) { // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 QStringList sl = lrdfPath.split(":", Qt::SkipEmptyParts, Qt::CaseSensitive); #else QStringList sl = lrdfPath.split(":", QString::SkipEmptyParts, Qt::CaseSensitive); #endif for(QStringList::const_iterator it = sl.cbegin(); it != sl.cend(); ++it) scanLrdfDir(*it, rdfs, debugStdErr); } } } // namespace MusEPlugin muse-4.0.0/src/libs/plugin/plugin_rdf.h000066400000000000000000000027001404105311000177700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // plugin_rdf.h // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PLUGIN_RDF_H__ #define __PLUGIN_RDF_H__ #include #include namespace MusEPlugin { // This might be called recursively! void scanLrdfDir(const QString& dirname, QStringList* rdfs, bool debugStdErr, // Only for recursions, original top caller should not touch! int recurseLevel = 0); void scanLrdfPlugins(QStringList* rdfs, bool debugStdErr); } // namespace MusEPlugin #endif muse-4.0.0/src/libs/plugin/plugin_scan.cpp000066400000000000000000000062021404105311000204750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // plugin_scan.cpp // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "plugin_scan.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_PLUGIN_SCAN(dev, format, args...) // std::fprintf(dev, format, ##args); namespace MusEPlugin { const float PluginPortInfo::defaultPortValue = 0.0f; const float PluginPortInfo::defaultPortMin = 0.0f; const float PluginPortInfo::defaultPortMax = 1.0f; const float PluginPortInfo::defaultPortStep = 0.0f; const float PluginPortInfo::defaultPortSmallStep = 0.0f; const float PluginPortInfo::defaultPortLargeStep = 0.0f; // PluginScanInfoStruct::~PluginScanInfoStruct() // { // std::fprintf(stderr, "PluginScanInfoStruct::~PluginScanInfoStruct: name:%s label:%s\n", getCString(_name), getCString(_label)); // } // // PluginScanInfo::~PluginScanInfo() // { // std::fprintf(stderr, "PluginScanInfo::~PluginScanInfo: name:%s label:%s\n", getCString(info()._name), getCString(info()._label)); // } const char* PluginScanInfoStruct::typeString() const { switch(_type) { case PluginTypeLADSPA: return "ladspa"; break; case PluginTypeDSSI: return "dssi"; break; case PluginTypeDSSIVST: return "dssi_vst"; break; case PluginTypeVST: return "vst"; break; case PluginTypeLinuxVST: return "linux_vst"; break; case PluginTypeLV2: return "lv2"; break; case PluginTypeMESS: return "mess"; break; case PluginTypeUnknown: return "unknown"; break; case PluginTypeNone: case PluginTypeAll: break; } return 0; } const char* PluginScanInfoStruct::classString() const { switch(_class) { case PluginClassEffect: return "effect"; break; case PluginClassInstrument: return "instrument"; break; case PluginClassAll: break; } return 0; } void PluginScanInfoStruct::dump(const char* prefixMessage) const { fprintf(stderr, "%s plugin:%s type:%s class:%s name:%s label:%s required features:%d\n", prefixMessage, PLUGIN_GET_CSTRING(filePath()), typeString(), classString(), PLUGIN_GET_CSTRING(_name), PLUGIN_GET_CSTRING(_label), _requiredFeatures); } } // namespace MusEPlugin muse-4.0.0/src/libs/plugin/plugin_scan.h000066400000000000000000000273311404105311000201500ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // plugin_scan.h // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PLUGIN_SCAN_H__ #define __PLUGIN_SCAN_H__ // Whether or not to use QString instead of std::string. // (And maybe QFileInfo and QProcess instead of strings and pthreads etc.) #define PLUGIN_INFO_USE_QT 1 #include #include // #include // #include #include #ifdef PLUGIN_INFO_USE_QT #include typedef QString PluginInfoString_t; #else #include typedef std::string PluginInfoString_t; #endif // PLUGIN_INFO_USE_QT #include "config.h" #include "globaldefs.h" namespace MusEPlugin { const char* const VST_OLD_PLUGIN_ENTRY_POINT = "main"; const char* const VST_NEW_PLUGIN_ENTRY_POINT = "VSTPluginMain"; //----------------------------------------- // PluginPortEnumValue //----------------------------------------- struct PluginPortEnumValue { float _value; PluginInfoString_t _label; PluginPortEnumValue() { _value = 0.0; } PluginPortEnumValue(float value, PluginInfoString_t label) : _value(value), _label(label) { } }; typedef std::vector EnumValueList; typedef EnumValueList::iterator iEnumValueList; typedef EnumValueList::const_iterator ciEnumValueList; typedef std::map > PortEnumValueMap; typedef PortEnumValueMap::iterator iPortEnumValueMap; typedef PortEnumValueMap::const_iterator ciPortEnumValueMap; typedef std::pair PortEnumValueMapPair; //----------------------------------------- // PluginPortInfo //----------------------------------------- struct PluginPortInfo { enum PortType { UnknownPort = 0x00, AudioPort = 0x01, ControlPort = 0x02, MidiPort = 0x04, InputPort = 0x08, OutputPort = 0x10 }; typedef int PortType_t; enum ValueFlags { NoValueFlags = 0x00, IntegerVal = 0x01, ToggledVal = 0x02, TriggerVal = 0x04, LogVal = 0x08, HasEnumerations = 0x10, HasMin = 0x20, HasMax = 0x40, HasDefault = 0x80, HasStep = 0x100 }; typedef int PortValueFlags_t; enum PortFlags { NoPortFlags = 0x00, // Indicates min and max should be scaled by the current samplerate. ScaleBySamplerate = 0x01, // Indicates min is valid, and should be scaled by the current samplerate // if ScaleBySamplerate is true. Otherwise min is set to zero. //BoundedBelow = 0x02, // Indicates max is valid, and should be scaled by the current samplerate // if ScaleBySamplerate is true. Otherwise max is set to one. //BoundedAbove = 0x04 // Whether the port is intended for latency reporting. IsLatency = 0x02, // Whether the port is an audio port which is used for high-speed control signals. IsCVPort = 0x04, SupportsTimePosition = 0x08, IsFreewheel = 0x10 }; typedef int PortFlags_t; static const float defaultPortValue; static const float defaultPortMin; static const float defaultPortMax; static const float defaultPortStep; static const float defaultPortSmallStep; static const float defaultPortLargeStep; PluginInfoString_t _name; PluginInfoString_t _symbol; unsigned long _index; PortType_t _type; PortValueFlags_t _valueFlags; PortFlags_t _flags; float _min; float _max; float _defaultVal; float _step; float _smallStep; float _largeStep; PluginPortInfo() { _index = 0; _type = UnknownPort; _valueFlags = NoValueFlags; _flags = NoPortFlags; _min = defaultPortMin; _max = defaultPortMax; _defaultVal = defaultPortValue; _step = 0.0; _smallStep = 0.0; _largeStep = 0.0; } float min(float sampleRate) const { return _flags & ScaleBySamplerate ? _min * sampleRate : _min; } float max(float sampleRate) const { return _flags & ScaleBySamplerate ? _max * sampleRate : _max; } }; typedef std::vector PluginPortList; typedef PluginPortList::iterator iPluginPortList; typedef PluginPortList::const_iterator ciPluginPortList; //----------------------------------------- // PluginScanInfoStruct //----------------------------------------- class PluginScanInfoStruct { public: enum PluginType { PluginTypeNone = 0x00, PluginTypeLADSPA = 0x01, PluginTypeDSSI = 0x02, PluginTypeVST = 0x04, PluginTypeDSSIVST = 0x08, PluginTypeLinuxVST = 0x10, PluginTypeLV2 = 0x20, PluginTypeMESS = 0x40, PluginTypeUnknown = 0x8000, PluginTypeAll = PluginTypeLADSPA | PluginTypeDSSI | PluginTypeVST | PluginTypeDSSIVST | PluginTypeLinuxVST | PluginTypeLV2 | PluginTypeMESS | PluginTypeUnknown}; typedef int PluginType_t; enum PluginClass { PluginClassNone = 0x00, PluginClassEffect = 0x01, PluginClassInstrument = 0x02, PluginClassAll = PluginClassEffect | PluginClassInstrument }; typedef int PluginClass_t; enum PluginFlags { NoPluginFlags = 0x00, HasGui = 0x01, HasChunks = 0x02, Realtime = 0x04, HardRealtimeCapable = 0x08, HasFreewheelPort = 0x10, HasLatencyPort = 0x20, SupportsTimePosition = 0x40 }; typedef int PluginFlags_t; //private: //protected: public: //QFileInfo _fi; PluginInfoString_t _completeBaseName; PluginInfoString_t _baseName; PluginInfoString_t _suffix; PluginInfoString_t _completeSuffix; PluginInfoString_t _absolutePath; PluginInfoString_t _path; // Like "http://zynaddsubfx.sourceforge.net/fx#Phaser". PluginInfoString_t _uri; // The file's time stamp in milliseconds since epoch. int64_t _fileTime; // Whether the file failed scanning. bool _fileIsBad; PluginType _type; PluginClass_t _class; unsigned long _uniqueID; long _subID; // vst shell ID etc. PluginInfoString_t _label; PluginInfoString_t _name; PluginInfoString_t _description; PluginInfoString_t _version; PluginInfoString_t _maker; PluginInfoString_t _copyright; int _apiVersionMajor; int _apiVersionMinor; int _pluginVersionMajor; int _pluginVersionMinor; PluginFlags_t _pluginFlags; unsigned long _portCount; unsigned long _inports; unsigned long _outports; unsigned long _controlInPorts; unsigned long _controlOutPorts; unsigned long _eventInPorts; unsigned long _eventOutPorts; // Freewheel port index ff HasFreewheelPort is true. unsigned long _freewheelPortIdx; // Latency port index if HasLatencyPort is true. unsigned long _latencyPortIdx; // Port number to control input index. Item is -1 if it's not a control input. // TODO: Not used yet. //std::vector rpIdx; //std::vector _pIdx; //control port numbers //std::vector _poIdx; //control out port numbers //std::vector _iIdx; //input port numbers //std::vector _oIdx; //output port numbers MusECore::PluginFeatures_t _requiredFeatures; MusECore::VstPluginFlags_t _vstPluginFlags; PluginInfoString_t _uiFilename; PluginPortList _portList; PortEnumValueMap _portEnumValMap; public: PluginScanInfoStruct() : _fileTime(0), _fileIsBad(false), _type(PluginTypeNone), _class(PluginClassNone), _uniqueID(0), _subID(0), _apiVersionMajor(0), _apiVersionMinor(0), _pluginVersionMajor(0), _pluginVersionMinor(0), _pluginFlags(NoPluginFlags), _portCount(0), _inports(0), _outports(0), _controlInPorts(0), _controlOutPorts(0), _eventInPorts(0), _eventOutPorts(0), _freewheelPortIdx(0), _latencyPortIdx(0), _requiredFeatures(MusECore::PluginNoFeatures), _vstPluginFlags(MusECore::vstPluginNoFlags) { }; //~PluginScanInfoStruct(); #ifdef PLUGIN_INFO_USE_QT #if defined(_WIN64) || defined(_WIN32) PluginInfoString_t filePath() const { const PluginInfoString_t fn = fileName(); return fn.isEmpty() ? _path : _path + '\\' + fn; } #else PluginInfoString_t filePath() const { const PluginInfoString_t fn = fileName(); return fn.isEmpty() ? _path : _path + '/' + fn; } #endif // defined(_WIN64) || defined(_WIN32) PluginInfoString_t fileName() const { return _completeSuffix.isEmpty() ? _baseName : _baseName + '.' + _completeSuffix; } PluginInfoString_t lib(bool complete = true) const { return complete ? _completeBaseName : _baseName; } PluginInfoString_t dirPath(bool complete = true) const { return complete ? _absolutePath : _path; } #else // PLUGIN_INFO_USE_QT #if defined(_WIN64) || defined(_WIN32) std::string filePath() const { const std::string fn = fileName(); return fn.empty() ? _path : _path + '\\' + fn; } #else std::string filePath() const { const std::string fn = fileName(); return fn.empty() ? _path : _path + '/' + fn; } #endif // defined(_WIN64) || defined(_WIN32) std::string fileName() const { return _completeSuffix.empty() ? _baseName : _baseName + '.' + _completeSuffix; } std::string lib(bool complete = true) const { return complete ? _completeBaseName : _baseName; } std::string dirPath(bool complete = true) const { return complete ? _absolutePath : _path; } #endif // PLUGIN_INFO_USE_QT bool inPlaceCapable() const { return !(_requiredFeatures & MusECore::PluginNoInPlaceProcessing); } const char* typeString() const; const char* classString() const; void dump(const char* prefixMessage = 0) const; }; //----------------------------------------- // PluginScanInfo //----------------------------------------- class PluginScanInfo { protected: PluginScanInfoStruct _info; public: PluginScanInfo() { }; PluginScanInfo(const PluginScanInfoStruct& info) : _info(info) { }; //~PluginScanInfo(); const PluginScanInfoStruct& info() const { return _info; } }; //----------------------------------------- // functions //----------------------------------------- #ifdef PLUGIN_INFO_USE_QT #define PLUGIN_STRING_EMPTY(x) (x).isEmpty() #define PLUGIN_GET_CSTRING(x) (x).toLatin1().constData() #define PLUGIN_GET_STDSTRING(x) (x).toStdString() #define PLUGIN_GET_QSTRING(x) (x) #define PLUGIN_SET_CSTRING(x) QString(x) #define PLUGIN_SET_STDSTRING(x) QString::fromStdString(x) #define PLUGIN_SET_QSTRING(x) (x) #else // PLUGIN_INFO_USE_QT #define PLUGIN_STRING_EMPTY(x) (x).empty() #define PLUGIN_GET_CSTRING(x) (x).c_str() #define PLUGIN_GET_STDSTRING(x) (x) #define PLUGIN_GET_QSTRING(x) QString::fromStdString(x) #define PLUGIN_SET_CSTRING(x) ((x) ? std::string(x) : std::string()) #define PLUGIN_SET_STDSTRING(x) (x) #define PLUGIN_SET_QSTRING(x) (x).toStdString() #endif // PLUGIN_INFO_USE_QT } // namespace MusEPlugin #endif muse-4.0.0/src/libs/string/000077500000000000000000000000001404105311000154775ustar00rootroot00000000000000muse-4.0.0/src/libs/string/CMakeLists.txt000066400000000000000000000032771404105311000202500ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # CMakeLists.txt # (C) Copyright 2014 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB muse_string_source_files strntcpy.cpp ) ## ## Define target ## add_library(muse_string ${MODULES_BUILD} ${muse_string_source_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${muse_string_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## ## set_target_properties( muse_string ## PROPERTIES OUTPUT_NAME muse_helper_str ## ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS muse_string DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/libs/string/strntcpy.cpp000066400000000000000000000031371404105311000200750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // strntcpy.cpp // (C) Copyright 2014 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "strntcpy.h" namespace MusELib { // Copies at most size bytes, always null-terminating the destination. It does not null-fill remaining destination bytes. // NULL source is accepted. Returns the destination, or zero if size is zero. // Note that there is a similar strlcpy() in BSD (libbsd in linux), as well as QT's qstrncpy(). char* strntcpy(char *dest, const char *src, int size) { if(size == 0) return 0; if(!src) { dest[0] = '\0'; return dest; } int i = 0; while(i < size) { dest[i] = src[i]; if(src[i++] == '\0') break; } dest[--i] = '\0'; return dest; } } // namespace MusELib muse-4.0.0/src/libs/string/strntcpy.h000066400000000000000000000025231404105311000175400ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // strntcpy.h // (C) Copyright 2014 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __STRNTCPY_H__ #define __STRNTCPY_H__ namespace MusELib { // Copies at most size bytes, always null-terminating the destination. It does not null-fill remaining destination bytes. // Note that there is a similar strlcpy() in BSD (libbsd in linux), as well as QT's qstrncpy(). extern char* strntcpy(char *dest, const char *src, int size); } // namespace MusELib #endifmuse-4.0.0/src/libs/sysex_helper/000077500000000000000000000000001404105311000167035ustar00rootroot00000000000000muse-4.0.0/src/libs/sysex_helper/CMakeLists.txt000066400000000000000000000034021404105311000214420ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # sysex_helper/CMakeLists.txt # (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB sysex_helper_source_files sysex_helper.cpp ) ## ## Define target ## add_library ( sysex_helper_module SHARED ${sysex_helper_source_files} ) ## ## Compilation flags and target name ## set_target_properties( sysex_helper_module PROPERTIES OUTPUT_NAME muse_sysex_helper_module ) ## ## Linkage ## # target_link_libraries(sysex_helper_module # ) ## ## Install location ## install(TARGETS sysex_helper_module DESTINATION ${MusE_MODULES_DIR} ) # if ( ${MODULES_BUILD} STREQUAL SHARED ) # install(TARGETS # sysex_helper_module # DESTINATION ${MusE_MODULES_DIR} # ) # endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/libs/sysex_helper/sysex_helper.cpp000066400000000000000000000037301404105311000221240ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // sysex_helper.cpp // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "sysex_helper.h" namespace MusECore { //--------------------------------------------------------- // sysexDuration //--------------------------------------------------------- unsigned int sysexDuration(unsigned int len, int sampleRate) { // Midi transmission characters per second, based on standard fixed bit rate of 31250 Hz. // According to ALSA (aplaymidi.c), although the midi standard says one stop bit, // two are commonly used. We will use two just to be sure. const unsigned int midi_cps = 31250 / (1 + 8 + 2); // Estimate the number of audio frames it should take (or took) to transmit the current midi chunk. unsigned int frames = (len * sampleRate) / midi_cps; // Add a slight delay between chunks just to be sure there's no overlap, rather a small space, and let devices catch up. frames += sampleRate / 200; // 1 / 200 = 5 milliseconds. // Let's be realistic, spread by at least one frame. if(frames == 0) frames = 1; return frames; } } // namespace MusECore muse-4.0.0/src/libs/sysex_helper/sysex_helper.h000066400000000000000000000027351404105311000215750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // sysex_helper.h // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SYSEX_HELPER_H__ #define __SYSEX_HELPER_H__ namespace MusECore { // Expected duration in frames, at the current sample rate, of the // given length of sysex data. Based on 31250Hz midi baud rate in // 1-8-2 format. (Midi specs say 1 stop bit, but ALSA says // 2 stop bits are common.) A small gap time is added as well. // If the data includes any start/end bytes, len should also include them. extern unsigned int sysexDuration(unsigned int len, int sampleRate); } // namespace MusECore #endif muse-4.0.0/src/libs/time_stretch/000077500000000000000000000000001404105311000166635ustar00rootroot00000000000000muse-4.0.0/src/libs/time_stretch/CMakeLists.txt000066400000000000000000000037101404105311000214240ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # CMakeLists.txt # (C) Copyright 2020 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB time_stretch_source_files time_stretch.cpp ) ## ## Define target ## # add_library(muse_time_stretch_module ${MODULES_BUILD} add_library(time_stretch_module SHARED ${time_stretch_source_files} ) ## ## Linkage ## target_link_libraries(time_stretch_module xml_module ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${time_stretch_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties ( time_stretch_module PROPERTIES OUTPUT_NAME muse_time_stretch_module ) ## ## Install location ## install(TARGETS time_stretch_module DESTINATION ${MusE_MODULES_DIR} ) # if ( ${MODULES_BUILD} STREQUAL SHARED ) # install(TARGETS time_stretch_module # DESTINATION ${MusE_MODULES_DIR} # ) # endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/libs/time_stretch/time_stretch.cpp000066400000000000000000001050571404105311000220710ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // time_stretch.cpp // Copyright (C) 2010-2020 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include "time_stretch.h" #include "xml.h" // For debugging output: Uncomment the fprintf section. #define ERROR_TIMESTRETCH(dev, format, args...) fprintf(dev, format, ##args) #define INFO_TIMESTRETCH(dev, format, args...) // fprintf(dev, format, ##args) #define DEBUG_TIMESTRETCH(dev, format, args...) // fprintf(dev, format, ##args) namespace MusECore { //--------------------------------------------------------- // StretchList //--------------------------------------------------------- StretchList::StretchList() { _isStretched = false; _isResampled = false; _isPitchShifted = false; _startFrame = 0; _endFrame = 0; _stretchedEndFrame = 0; _squishedEndFrame = 0; _stretchRatio = 1.0; _samplerateRatio = 1.0; _pitchRatio = 1.0; // Ensure that there is always an item at frame zero. insert(std::pair (0, StretchListItem(1.0, 1.0, 1.0, StretchListItem::StretchEvent | StretchListItem::SamplerateEvent | StretchListItem::PitchEvent))); // Technically it is normalized now, since StretchListItem // constructor fills in zeros for the frame values. _isNormalized = true; } StretchList::~StretchList() { } void StretchList::add(StretchListItem::StretchEventType type, MuseFrame_t frame, double value, bool do_normalize) { // Some '1.0' values will be filled in if neccessary by normalize() below. double str = 1.0; double srr = 1.0; double psr = 1.0; switch(type) { case StretchListItem::StretchEvent: str = value; break; case StretchListItem::SamplerateEvent: srr = value; break; case StretchListItem::PitchEvent: psr = value; break; } std::pair res = insert(std::pair (frame, StretchListItem(str, srr, psr, type))); // Item already exists? Assign. if(!res.second) { // Set the type's value. But leave the others alone. switch(type) { case StretchListItem::StretchEvent: res.first->second._stretchRatio = value; break; case StretchListItem::SamplerateEvent: res.first->second._samplerateRatio = value; break; case StretchListItem::PitchEvent: res.first->second._pitchRatio = value; break; } // Combine the type. res.first->second._type |= type; } // Mark as invalidated, normalization is required. _isNormalized = false; if(do_normalize) normalizeListFrames(); } void StretchList::add(MuseFrame_t frame, const StretchListItem& e, bool do_normalize) { std::pair res = insert(std::pair (frame, e)); // Item already exists? Assign. if(!res.second) { //res.first->second = e; res.first->second._stretchRatio = e._stretchRatio; res.first->second._samplerateRatio = e._samplerateRatio; res.first->second._pitchRatio = e._pitchRatio; } // Mark as invalidated, normalization is required. _isNormalized = false; if(do_normalize) normalizeListFrames(); } void StretchList::del(int types, MuseFrame_t frame, bool do_normalize) { // Do not delete the item at zeroth frame. if(frame == 0) return; iStretchListItem e = find(frame); if(e == end()) { ERROR_TIMESTRETCH(stderr, "StretchList::del(%ld): not found\n", frame); return; } del(types, e, do_normalize); } void StretchList::del(int types, iStretchListItem item, bool do_normalize) { // Do not delete the item at zeroth frame. if(item->first == 0) return; // We must restore any previous event's ratio to 1.0. // This is crucial so that when the last user marker is finally removed, the special zeroth marker (non-user) // will be set to 1.0 ratio and the converters are not required anymore and can be deleted. if(types & StretchListItem::StretchEvent) { iStretchListItem prevStretchTyped = previousEvent(StretchListItem::StretchEvent, item); if(prevStretchTyped != end()) prevStretchTyped->second._stretchRatio = 1.0; } if(types & StretchListItem::SamplerateEvent) { iStretchListItem prevSamplerateTyped = previousEvent(StretchListItem::SamplerateEvent, item); if(prevSamplerateTyped != end()) prevSamplerateTyped->second._samplerateRatio = 1.0; } if(types & StretchListItem::PitchEvent) { iStretchListItem prevPitchTyped = previousEvent(StretchListItem::PitchEvent, item); if(prevPitchTyped != end()) prevPitchTyped->second._stretchRatio = 1.0; } item->second._type &= ~types; if(item->second._type == 0) erase(item); // Mark as invalidated, normalization is required. _isNormalized = false; if(do_normalize) normalizeListFrames(); } void StretchList::normalizeFrames() { _stretchedEndFrame = stretch(_endFrame); _squishedEndFrame = squish(_endFrame); } void StretchList::normalizeRatios() { } void StretchList::normalizeListFrames() { double dtime; double factor; double duntime; MuseFrame_t dframe; MuseFrame_t thisFrame, prevFrame; double prevNewFrame; double prevNewUnFrame; double prevNewStretchFrame; double prevNewUnStretchFrame; double prevNewSamplerateFrame; double prevNewUnSamplerateFrame; double prevStretch; double prevSamplerate; double prevPitch; // If any intrinsic value has a stretch or samplerate other than 1.0, // the map is stretched, a stretcher or samplerate converter must be engaged. _isStretched = (_stretchRatio != 1.0); _isResampled = (_samplerateRatio != 1.0); _isPitchShifted = (_pitchRatio != 1.0); for(iterator ise = begin(); ise != end(); ++ise) { thisFrame = ise->first; StretchListItem& se = ise->second; // The policy is such that if there are user items (non zeroth item) of a given type, // the list is said to be in that state (stretched, resampled, shifted etc), even if all // the items' ratios are 1.0. // Ignore the special zeroth frame. // If the zeroth frame is the only item, its ratios must (should) all be at 1.0 right now // so they will be ignored. if(thisFrame != 0) { if(((se._type & StretchListItem::StretchEvent))) //&& se._stretchRatio != 1.0)) _isStretched = true; if(((se._type & StretchListItem::SamplerateEvent))) //&& se._samplerateRatio != 1.0)) _isResampled = true; if(((se._type & StretchListItem::PitchEvent))) //&& se._pitchRatio != 1.0)) _isPitchShifted = true; } if(ise == begin()) { prevFrame = prevNewUnFrame = prevNewFrame = prevNewStretchFrame = prevNewUnStretchFrame = prevNewSamplerateFrame = prevNewUnSamplerateFrame = se._finSquishedFrame = se._finStretchedFrame = se._stretchStretchedFrame = se._stretchSquishedFrame = se._samplerateStretchedFrame = se._samplerateSquishedFrame = thisFrame; prevStretch = se._stretchRatio; prevSamplerate = se._samplerateRatio; prevPitch = se._pitchRatio; } else { dframe = thisFrame - prevFrame; factor = (_samplerateRatio * prevSamplerate) / (_stretchRatio * prevStretch); dtime = double(dframe) * factor; se._finStretchedFrame = prevNewFrame + dtime; prevNewFrame = se._finStretchedFrame; duntime = double(dframe) / factor; se._finSquishedFrame = prevNewUnFrame + duntime; prevNewUnFrame = se._finSquishedFrame; factor = 1.0 / (_stretchRatio * prevStretch); dtime = double(dframe) * factor; se._stretchStretchedFrame = prevNewStretchFrame + dtime; prevNewStretchFrame = se._stretchStretchedFrame; duntime = double(dframe) / factor; se._stretchSquishedFrame = prevNewUnStretchFrame + duntime; prevNewUnStretchFrame = se._stretchSquishedFrame; factor = (_samplerateRatio * prevSamplerate); dtime = double(dframe) * factor; se._samplerateStretchedFrame = prevNewSamplerateFrame + dtime; prevNewSamplerateFrame = se._samplerateStretchedFrame; duntime = double(dframe) / factor; se._samplerateSquishedFrame = prevNewUnSamplerateFrame + duntime; prevNewUnSamplerateFrame = se._samplerateSquishedFrame; prevFrame = thisFrame; if(se._type & StretchListItem::StretchEvent) prevStretch = se._stretchRatio; else se._stretchRatio = prevStretch; if(se._type & StretchListItem::SamplerateEvent) prevSamplerate = se._samplerateRatio; else se._samplerateRatio = prevSamplerate; if(se._type & StretchListItem::PitchEvent) prevPitch = se._pitchRatio; else se._pitchRatio = prevPitch; } } // TODO normalizeFrames(); // Mark as validated, normalization is done. _isNormalized = true; #ifdef DEBUG_TIMESTRETCH dump(); #endif } void StretchList::normalizeListRatios() { } //--------------------------------------------------------- // clear //--------------------------------------------------------- void StretchList::clear() { StretchList_t::clear(); // Ensure that there is always an item at frame zero. insert(std::pair (0, StretchListItem(1.0, 1.0, 1.0, StretchListItem::StretchEvent | StretchListItem::SamplerateEvent | StretchListItem::PitchEvent))); // Technically it is normalized now, since StretchListItem // constructor fills in zeros for the frame values. _isNormalized = true; } //--------------------------------------------------------- // eraseRange //--------------------------------------------------------- void StretchList::eraseRange(int types, MuseFrame_t sframe, MuseFrame_t eframe) { if(sframe >= eframe) return; iStretchListItem se = lower_bound(sframe); if(se == end()) return; iStretchListItem ee = upper_bound(eframe); for(iStretchListItem ise = se; ise != ee; ) { // Do not delete the item at zeroth frame. if(ise->first == 0) { ++ise; continue; } ise->second._type &= ~types; if(ise->second._type == 0) { iStretchListItem ise_save = ise; erase(ise); ise = ise_save; } else ++ise; } // Mark as invalidated, normalization is required. _isNormalized = false; normalizeListFrames(); } //--------------------------------------------------------- // read //--------------------------------------------------------- void StretchList::read(Xml& xml) { bool ok; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::Attribut: ERROR_TIMESTRETCH(stderr, "stretchlist unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::Text: { int len = tag.length(); int i = 0; for(;;) { while(i < len && (tag[i] == ',' || tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; QString fs; while(i < len && tag[i] != ' ') { fs.append(tag[i]); ++i; } if(i == len) break; MuseFrame_t frame = fs.toLong(&ok); if(!ok) { ERROR_TIMESTRETCH(stderr, "StretchList::read failed reading frame string: %s\n", fs.toLatin1().constData()); break; } while(i < len && (tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; QString stretchStr; while(i < len && tag[i] != ' ' && tag[i] != ',') { stretchStr.append(tag[i]); ++i; } double stretchVal = stretchStr.toDouble(&ok); if(!ok) { ERROR_TIMESTRETCH(stderr, "StretchList::read failed reading stretch ratio string: %s\n", stretchStr.toLatin1().constData()); break; } while(i < len && (tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; QString SRStr; while(i < len && tag[i] != ' ' && tag[i] != ',') { SRStr.append(tag[i]); ++i; } double SRVal = SRStr.toDouble(&ok); if(!ok) { ERROR_TIMESTRETCH(stderr, "StretchList::read failed reading samplerate ratio string: %s\n", SRStr.toLatin1().constData()); break; } while(i < len && (tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; QString pitchStr; while(i < len && tag[i] != ' ' && tag[i] != ',') { pitchStr.append(tag[i]); ++i; } double pitchVal = pitchStr.toDouble(&ok); if(!ok) { ERROR_TIMESTRETCH(stderr, "StretchList::read failed reading pitch ratio string: %s\n", pitchStr.toLatin1().constData()); break; } while(i < len && (tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; QString typeStr; while(i < len && tag[i] != ' ' && tag[i] != ',') { typeStr.append(tag[i]); ++i; } int typeVal = typeStr.toInt(&ok); if(!ok) { ERROR_TIMESTRETCH(stderr, "StretchList::read failed reading type string: %s\n", typeStr.toLatin1().constData()); break; } // Defer normalize until tag end. add(frame, StretchListItem(stretchVal, SRVal, pitchVal, typeVal), false); if(i == len) break; } } break; case Xml::TagEnd: if (tag == "stretchlist") { normalizeListFrames(); return; } default: break; } } } //--------------------------------------------------------- // write //--------------------------------------------------------- void StretchList::write(int level, Xml& xml) const { if(empty()) return; xml.tag(level++, "stretchlist"); int i = 0; QString seStr("%1 %2 %3 %4 %5, "); for (const_iterator ise = cbegin(); ise != cend(); ++ise) { xml.nput(level, seStr.arg(ise->first) .arg(ise->second._stretchRatio) .arg(ise->second._samplerateRatio) .arg(ise->second._pitchRatio) .arg(ise->second._type) .toLatin1().constData()); ++i; if (i >= 3) { xml.put(level, ""); i = 0; } } if (i) xml.put(level, ""); xml.etag(level--, "stretchlist"); } //--------------------------------------------------------- // dump //--------------------------------------------------------- void StretchList::dump() const { INFO_TIMESTRETCH(stderr, "\nStretchList: isNormalized:%d\n", _isNormalized); for(const_iterator i = cbegin(); i != cend(); ++i) { INFO_TIMESTRETCH(stderr, "frame:%6ld StretchRatio:%f SamplerateRatio:%f PitchRatio:%f " "stretchedFrame:%f squishedFrame:%f\n", i->first, i->second._stretchRatio, i->second._samplerateRatio, i->second._pitchRatio, i->second._finStretchedFrame, i->second._finSquishedFrame); } } // ------------------------------------------ // Intrinsic functions: //------------------------------------------- void StretchList::setStartFrame(MuseFrame_t frame, bool do_normalize) { _startFrame = frame; // Mark as invalidated, normalization is required. _isNormalized = false; if(do_normalize) normalizeListFrames(); } void StretchList::setEndFrame(MuseFrame_t frame, bool do_normalize) { _endFrame = frame; // Mark as invalidated, normalization is required. _isNormalized = false; if(do_normalize) normalizeListFrames(); } void StretchList::setStretchedEndFrame(MuseFrame_t frame, bool do_normalize) { _stretchedEndFrame = frame; // Mark as invalidated, normalization is required. _isNormalized = false; if(do_normalize) normalizeListFrames(); } void StretchList::setSquishedEndFrame(MuseFrame_t frame, bool do_normalize) { _squishedEndFrame = frame; // Mark as invalidated, normalization is required. _isNormalized = false; if(do_normalize) normalizeListFrames(); } double StretchList::ratio(StretchListItem::StretchEventType type) const { switch(type) { case StretchListItem::StretchEvent: return _stretchRatio; break; case StretchListItem::SamplerateEvent: return _samplerateRatio; break; case StretchListItem::PitchEvent: return _pitchRatio; break; } return 1.0; } void StretchList::setRatio(StretchListItem::StretchEventType type, double ratio, bool do_normalize) { switch(type) { case StretchListItem::StretchEvent: _stretchRatio = ratio; break; case StretchListItem::SamplerateEvent: _samplerateRatio = ratio; break; case StretchListItem::PitchEvent: _pitchRatio = ratio; break; } // Mark as invalidated, normalization is required. _isNormalized = false; if(do_normalize) normalizeListFrames(); } // ------------------------------------------ // List functions: //------------------------------------------- iStretchListItem StretchList::findEvent(int types, MuseFrame_t frame) { iStretchListItemPair res = equal_range(frame); for(iStretchListItem ise = res.first; ise != res.second; ++ise) { if(ise->second._type & types) return ise; } return end(); } ciStretchListItem StretchList::cFindEvent(int types, MuseFrame_t frame) const { const StretchList* sl = this; ciStretchListItemPair res = sl->equal_range(frame); // FIXME Calls non-const version unless cast ?? for(ciStretchListItem ise = res.first; ise != res.second; ++ise) { if(ise->second._type & types) return ise; } return sl->end(); } iStretchListItem StretchList::previousEvent(int types, iStretchListItem item) { iStretchListItem i = item; while(i != begin()) { --i; if(i->second._type & types) return i; } return end(); } ciStretchListItem StretchList::cPreviousEvent(int types, ciStretchListItem item) const { ciStretchListItem i = item; while(i != cbegin()) { --i; if(i->second._type & types) return i; } return cend(); } iStretchListItem StretchList::nextEvent(int types, iStretchListItem item) { iStretchListItem i = item; while(i != end()) { ++i; if(i->second._type & types) return i; } return end(); } ciStretchListItem StretchList::cNextEvent(int types, ciStretchListItem item) const { const StretchList* sl = this; ciStretchListItem i = item; while(i != sl->end()) { ++i; if(i->second._type & types) return i; } return sl->end(); } //--------------------------------------------------------- // ratioAt //--------------------------------------------------------- double StretchList::ratioAt(StretchListItem::StretchEventType type, MuseFrame_t frame) const { // If the zeroth frame is the only item, its ratios must (should) all be at 1.0 right now // so they will be ignored. if(size() == 1) return 1.0; const_iterator i = upper_bound(frame); if(i == cbegin()) return 1.0; --i; switch(type) { case StretchListItem::StretchEvent: return i->second._stretchRatio; break; case StretchListItem::SamplerateEvent: return i->second._samplerateRatio; break; case StretchListItem::PitchEvent: return i->second._pitchRatio; break; } return 1.0; } //--------------------------------------------------------- // setRatioAt //--------------------------------------------------------- void StretchList::setRatioAt(StretchListItem::StretchEventType type, MuseFrame_t frame, double ratio, bool do_normalize) { add(type, frame, ratio, do_normalize); } void StretchList::setRatioAt(StretchListItem::StretchEventType type, iStretchListItem item, double ratio, bool do_normalize) { item->second._type |= type; switch(type) { case StretchListItem::StretchEvent: item->second._stretchRatio = ratio; break; case StretchListItem::SamplerateEvent: item->second._samplerateRatio = ratio; break; case StretchListItem::PitchEvent: item->second._pitchRatio = ratio; break; } // Mark as invalidated, normalization is required. _isNormalized = false; if(do_normalize) normalizeListFrames(); } void StretchList::addRatioAt(StretchListItem::StretchEventType type, MuseFrame_t frame, double ratio, bool do_normalize) { add(type, frame, ratio, do_normalize); } void StretchList::delRatioAt(int types, MuseFrame_t frame, bool do_normalize) { del(types, frame, do_normalize); } //--------------------------------------------------------- // stretch //--------------------------------------------------------- double StretchList::stretch(MuseFrame_t frame, int type) const { MuseFrame_t prevFrame; double prevNewFrame = frame; double prevStretch; double prevSamplerate; double dtime = 0.0; const_iterator i = upper_bound(frame); if(i == cbegin()) return frame; --i; prevFrame = i->first; prevStretch = i->second._stretchRatio; prevSamplerate = i->second._samplerateRatio; const MuseFrame_t dframe = frame - prevFrame; // Full conversion requested. if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent)) { prevNewFrame = i->second._finStretchedFrame; dtime = double(dframe) * (_samplerateRatio * prevSamplerate) / (_stretchRatio * prevStretch); } // Stretch only. else if(type & StretchListItem::StretchEvent) { prevNewFrame = i->second._stretchStretchedFrame; dtime = double(dframe) / (_stretchRatio * prevStretch); } // Samplerate only. else if(type & StretchListItem::SamplerateEvent) { prevNewFrame = i->second._samplerateStretchedFrame; dtime = double(dframe) * _samplerateRatio * prevSamplerate; } return prevNewFrame + dtime; } double StretchList::stretch(double frame, int type) const { MuseFrame_t prevFrame; double prevNewFrame = frame; double prevStretch; double prevSamplerate; double dtime = 0.0; const_iterator i = upper_bound(frame); if(i == cbegin()) return frame; --i; prevFrame = i->first; prevStretch = i->second._stretchRatio; prevSamplerate = i->second._samplerateRatio; const double dframe = frame - (double)prevFrame; // Full conversion requested. if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent)) { prevNewFrame = i->second._finStretchedFrame; dtime = dframe * (_samplerateRatio * prevSamplerate) / (_stretchRatio * prevStretch); } // Stretch only. else if(type & StretchListItem::StretchEvent) { prevNewFrame = i->second._stretchStretchedFrame; dtime = dframe / (_stretchRatio * prevStretch); } // Samplerate only. else if(type & StretchListItem::SamplerateEvent) { prevNewFrame = i->second._samplerateStretchedFrame; dtime = dframe * _samplerateRatio * prevSamplerate; } return prevNewFrame + dtime; } double StretchList::squish(MuseFrame_t frame, int type) const { MuseFrame_t prevFrame; double prevNewUnFrame = frame; double prevStretch; double prevSamplerate; double dtime = 0.0; const_iterator i = upper_bound(frame); if(i == cbegin()) return frame; --i; prevFrame = i->first; prevStretch = i->second._stretchRatio; prevSamplerate = i->second._samplerateRatio; const MuseFrame_t dframe = frame - prevFrame; // Full conversion requested. if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent)) { prevNewUnFrame = i->second._finSquishedFrame; dtime = double(dframe) * (_stretchRatio * prevStretch) / (_samplerateRatio * prevSamplerate); } // Stretch only. else if(type & StretchListItem::StretchEvent) { prevNewUnFrame = i->second._stretchSquishedFrame; dtime = double(dframe) * (_stretchRatio * prevStretch); } // Samplerate only. else if(type & StretchListItem::SamplerateEvent) { prevNewUnFrame = i->second._samplerateSquishedFrame; dtime = double(dframe) / (_samplerateRatio * prevSamplerate); } return prevNewUnFrame + dtime; } double StretchList::squish(double frame, int type) const { MuseFrame_t prevFrame; double prevNewUnFrame = frame; double prevStretch; double prevSamplerate; double dtime = 0.0; const_iterator i = upper_bound(frame); if(i == cbegin()) return frame; --i; prevFrame = i->first; prevStretch = i->second._stretchRatio; prevSamplerate = i->second._samplerateRatio; const double dframe = frame - (double)prevFrame; // Full conversion requested. if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent)) { prevNewUnFrame = i->second._finSquishedFrame; dtime = dframe * (_stretchRatio * prevStretch) / (_samplerateRatio * prevSamplerate); } // Stretch only. else if(type & StretchListItem::StretchEvent) { prevNewUnFrame = i->second._stretchSquishedFrame; dtime = dframe * (_stretchRatio * prevStretch); } // Samplerate only. else if(type & StretchListItem::SamplerateEvent) { prevNewUnFrame = i->second._samplerateSquishedFrame; dtime = dframe / (_samplerateRatio * prevSamplerate); } return prevNewUnFrame + dtime; } //--------------------------------------------------------- // unStretch //--------------------------------------------------------- MuseFrame_t StretchList::unStretch(double frame, int type) const { if(empty()) return frame; MuseFrame_t prevFrame; double prevNewFrame = frame; double prevStretch; double prevSamplerate; double factor = 1.0; const_iterator e; for(e = cbegin(); e != cend(); ++e) { if((type & StretchListItem::StretchEvent) && // Full conversion requested. (type & StretchListItem::SamplerateEvent)) { if(frame < e->second._finStretchedFrame) break; else continue; } else if(type & StretchListItem::StretchEvent) // Only stretch conversion requested. { if(frame < e->second._stretchStretchedFrame) break; else continue; } else if(type & StretchListItem::SamplerateEvent) // Only samplerate conversion requested. { if(frame < e->second._samplerateStretchedFrame) break; else continue; } } if(e == cbegin()) return frame; --e; prevFrame = e->first; prevStretch = e->second._stretchRatio; prevSamplerate = e->second._samplerateRatio; // Full conversion requested. if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent)) { prevNewFrame = e->second._finStretchedFrame; factor = (_stretchRatio * prevStretch) / (_samplerateRatio * prevSamplerate); } // Stretch only. else if(type & StretchListItem::StretchEvent) { prevNewFrame = e->second._stretchStretchedFrame; factor = (_stretchRatio * prevStretch); } // Samplerate only. else if(type & StretchListItem::SamplerateEvent) { prevNewFrame = e->second._samplerateStretchedFrame; factor = 1.0 / (_samplerateRatio * prevSamplerate); } return prevFrame + lrint((frame - prevNewFrame) * factor); } //--------------------------------------------------------- // unStretch //--------------------------------------------------------- MuseFrame_t StretchList::unSquish(double frame, int type) const { if(empty()) return frame; MuseFrame_t prevFrame; double prevNewUnFrame = frame; double prevStretch; double prevSamplerate; double factor = 1.0; const_iterator e; for(e = cbegin(); e != cend(); ++e) { if((type & StretchListItem::StretchEvent) && // Full conversion requested. (type & StretchListItem::SamplerateEvent)) { if(frame < e->second._finSquishedFrame) break; else continue; } else if(type & StretchListItem::StretchEvent) // Only stretch conversion requested. { if(frame < e->second._stretchSquishedFrame) break; else continue; } else if(type & StretchListItem::SamplerateEvent) // Only samplerate conversion requested. { if(frame < e->second._samplerateSquishedFrame) break; else continue; } } if(e == cbegin()) return frame; --e; prevFrame = e->first; prevStretch = e->second._stretchRatio; prevSamplerate = e->second._samplerateRatio; // Full conversion requested. if((type & StretchListItem::StretchEvent) && (type & StretchListItem::SamplerateEvent)) { prevNewUnFrame = e->second._finSquishedFrame; factor = (_samplerateRatio * prevSamplerate) / (_stretchRatio * prevStretch); } // Stretch only. else if(type & StretchListItem::StretchEvent) { prevNewUnFrame = e->second._stretchSquishedFrame; factor = 1.0 / (_stretchRatio * prevStretch); } // Samplerate only. else if(type & StretchListItem::SamplerateEvent) { prevNewUnFrame = e->second._samplerateSquishedFrame; factor = (_samplerateRatio * prevSamplerate); } // FIXME: Hm, lrint? Try returning double. return prevFrame + lrint((frame - prevNewUnFrame) * factor); } StretchListInfo StretchList::testDelListOperation(int types, MuseFrame_t frame) const { // The policy is such that if (after deletion) there are still user items (non zeroth item) of a given type, // the list is said to still be in that state (stretched, resampled, shifted etc), // even if all the items' ratios are 1.0. StretchListInfo info; MuseFrame_t fr; // If any intrinsic value has a stretch or samplerate other than 1.0, // the map is stretched, a stretcher or samplerate converter must be engaged. info._isStretched = (_stretchRatio != 1.0); info._isResampled = (_samplerateRatio != 1.0); info._isPitchShifted = (_pitchRatio != 1.0); for(const_iterator ise = cbegin(); ise != cend(); ++ise) { fr = ise->first; // Ignore the special zeroth frame. // If the zeroth frame is the only item, its ratios must (should) all be at 1.0 right now // so they will be ignored. if(fr == 0) continue; const StretchListItem& se = ise->second; if(((se._type & StretchListItem::StretchEvent) && //(types & StretchListItem::StretchEvent) && (!(types & StretchListItem::StretchEvent) || fr != frame))) //&& //se._stretchRatio != 1.0)) info._isStretched = true; if(((se._type & StretchListItem::SamplerateEvent) && //(types & StretchListItem::SamplerateEvent) && (!(types & StretchListItem::SamplerateEvent) || fr != frame))) //&& //se._samplerateRatio != 1.0)) info._isResampled = true; if(((se._type & StretchListItem::PitchEvent) && //(types & StretchListItem::PitchEvent) && (!(types & StretchListItem::PitchEvent) || fr != frame))) //&& //se._pitchRatio != 1.0)) info._isPitchShifted = true; } return info; } } // namespace MusECore muse-4.0.0/src/libs/time_stretch/time_stretch.h000066400000000000000000000177771404105311000215510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // time_stretch.h // Copyright (C) 2010-2020 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TIME_STRETCH_H__ #define __TIME_STRETCH_H__ #include #include "muse_time.h" #include "xml.h" #ifndef MUSE_TIME_STRETCH_MAX_FRAME #define MUSE_TIME_STRETCH_MAX_FRAME (0x7ffffffffffffffeL) //#define MUSE_TIME_STRETCH_MAX_FRAME ((1 << (sizeof(MuseFrame_t) - 1)) - 1) #endif namespace MusECore { //--------------------------------------------------------- // StretchEvent //--------------------------------------------------------- struct StretchListItem { // Can be OR'd together. enum StretchEventType { StretchEvent = 0x01, SamplerateEvent = 0x02, PitchEvent = 0x04 }; // Combination of StretchEventType flags. int _type; double _stretchRatio; double _samplerateRatio; double _pitchRatio; // Pre-computed stretch and squish frames. double _finStretchedFrame; double _finSquishedFrame; double _stretchStretchedFrame; double _stretchSquishedFrame; double _samplerateStretchedFrame; double _samplerateSquishedFrame; StretchListItem(double stretchRatio = 1.0, double samplerateRatio = 1.0, double pitchRatio = 1.0, int type = 0) { _stretchRatio = stretchRatio; _samplerateRatio = samplerateRatio; _pitchRatio = pitchRatio; _type = type; _finStretchedFrame = _finSquishedFrame = _stretchStretchedFrame = _stretchSquishedFrame = _samplerateStretchedFrame = _samplerateSquishedFrame = 0.0; } }; //--------------------------------------------------------- // StretchList //--------------------------------------------------------- typedef std::map > StretchList_t; typedef StretchList_t::iterator iStretchListItem; typedef StretchList_t::const_iterator ciStretchListItem; typedef StretchList_t::reverse_iterator riStretchListItem; typedef StretchList_t::const_reverse_iterator criStretchListItem; typedef std::pair iStretchListItemPair; typedef std::pair ciStretchListItemPair; struct StretchListInfo { bool _isStretched; bool _isResampled; bool _isPitchShifted; StretchListInfo(bool isStretched = false, bool isResampled = false, bool isPitchShifted = false) : _isStretched(isStretched), _isResampled(isResampled), _isPitchShifted(isPitchShifted) { } }; class StretchList : public StretchList_t { friend struct PendingOperationItem; // Intrinsic values. MuseFrame_t _startFrame; MuseFrame_t _endFrame; MuseFrame_t _stretchedEndFrame; MuseFrame_t _squishedEndFrame; double _stretchRatio; double _samplerateRatio; double _pitchRatio; // Whether ANY event has a stretch other than 1.0 ie. the map is stretched, a stretcher must be engaged. bool _isStretched; bool _isResampled; bool _isPitchShifted; // Whether the list is already normalized, or else a normalization is required. bool _isNormalized; void add(StretchListItem::StretchEventType type, MuseFrame_t frame, double value, bool do_normalize = true); void add(MuseFrame_t frame, const StretchListItem& e, bool do_normalize = true); void del(int types, iStretchListItem, bool do_normalize = true); void del(int types, MuseFrame_t frame, bool do_normalize = true); public: StretchList(); virtual ~StretchList(); // Returns whether ANY event has a stretch other than 1.0 ie. the map is stretched, a stretcher must be engaged. bool isStretched() const { return _isStretched; } bool isResampled() const { return _isResampled; } bool isPitchShifted() const { return _isPitchShifted; } void normalizeFrames(); void normalizeRatios(); void normalizeListFrames(); void normalizeListRatios(); // Whether the list is already normalized, or else a normalization is required. bool isNormalized() const { return _isNormalized; } void clear(); void eraseRange(int types, MuseFrame_t sframe, MuseFrame_t eframe); void read(Xml&); void write(int, Xml&) const; void dump() const; // ------------------------------------------ // Intrinsic functions: //------------------------------------------- MuseFrame_t startFrame() const { return _startFrame; } MuseFrame_t endFrame() const { return _endFrame; } MuseFrame_t stretchedEndFrame() const { return _stretchedEndFrame; } MuseFrame_t squishedEndFrame() const { return _squishedEndFrame; } void setStartFrame(MuseFrame_t frame, bool do_normalize = true); void setEndFrame(MuseFrame_t frame, bool do_normalize = true); void setStretchedEndFrame(MuseFrame_t frame, bool do_normalize = true); void setSquishedEndFrame(MuseFrame_t frame, bool do_normalize = true); double ratio(StretchListItem::StretchEventType type) const; void setRatio(StretchListItem::StretchEventType type, double ratio, bool do_normalize = true); // ------------------------------------------ // List functions: //------------------------------------------- iStretchListItem findEvent(int types, MuseFrame_t frame); ciStretchListItem cFindEvent(int types, MuseFrame_t frame) const; iStretchListItem previousEvent(int types, iStretchListItem); ciStretchListItem cPreviousEvent(int types, ciStretchListItem) const; iStretchListItem nextEvent(int types, iStretchListItem); ciStretchListItem cNextEvent(int types, ciStretchListItem) const; double ratioAt(StretchListItem::StretchEventType type, MuseFrame_t frame) const; void setRatioAt(StretchListItem::StretchEventType type, MuseFrame_t frame, double ratio, bool do_normalize = true); void setRatioAt(StretchListItem::StretchEventType type, iStretchListItem, double ratio, bool do_normalize = true); void addRatioAt(StretchListItem::StretchEventType type, MuseFrame_t frame, double ratio, bool do_normalize = true); void delRatioAt(int types, MuseFrame_t frame, bool do_normalize = true); double stretch(MuseFrame_t frame, int type = StretchListItem::StretchEvent | StretchListItem::SamplerateEvent) const; double stretch(double frame, int type = StretchListItem::StretchEvent | StretchListItem::SamplerateEvent) const; double squish(MuseFrame_t frame, int type = StretchListItem::StretchEvent | StretchListItem::SamplerateEvent) const; double squish(double frame, int type = StretchListItem::StretchEvent | StretchListItem::SamplerateEvent) const; MuseFrame_t unStretch(double frame, int type = StretchListItem::StretchEvent | StretchListItem::SamplerateEvent) const; MuseFrame_t unSquish(double frame, int type = StretchListItem::StretchEvent | StretchListItem::SamplerateEvent) const; // Whether deleting the item would cause isStretched, isResampled, or isPitchShifted // to become false. StretchListInfo testDelListOperation(int types, MuseFrame_t frame) const; }; } // namespace MusECore #endif muse-4.0.0/src/libs/wave/000077500000000000000000000000001404105311000151335ustar00rootroot00000000000000muse-4.0.0/src/libs/wave/CMakeLists.txt000066400000000000000000000036711404105311000177020ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # CMakeLists.txt # (C) Copyright 2020 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB wave_source_files wave.cpp ) ## ## Define target ## # add_library(muse_time_stretch_module ${MODULES_BUILD} add_library(wave_module SHARED ${wave_source_files} ) ## ## Linkage ## target_link_libraries(wave_module time_stretch_module audio_converter_plugin ${SNDFILE_LIBRARIES} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${wave_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties ( wave_module PROPERTIES OUTPUT_NAME muse_wave_module ) ## ## Install location ## install(TARGETS wave_module DESTINATION ${MusE_MODULES_DIR} ) # if ( ${MODULES_BUILD} STREQUAL SHARED ) # install(TARGETS wave_module # DESTINATION ${MusE_MODULES_DIR} # ) # endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/libs/wave/wave.cpp000066400000000000000000001502711404105311000166070ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: wave.cpp,v 1.19.2.20 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "muse_math.h" #include #include #include "wave.h" #include "type_defs.h" // For debugging output: Uncomment the fprintf section. #define ERROR_WAVE(dev, format, args...) fprintf(dev, format, ##args) #define INFO_WAVE(dev, format, args...) // fprintf(dev, format, ##args) #define DEBUG_WAVE(dev, format, args...) // fprintf(dev, format, ##args) namespace MusECore { const int cacheMag = 128; // static SndFileList* SndFile::_sndFiles = nullptr; AudioConverterPluginList* SndFile::_pluginList = nullptr; AudioConverterSettingsGroup** SndFile::_defaultSettings = nullptr; int SndFile::_systemSampleRate = 0; int SndFile::_segSize = 0; // static void SndFile::initWaveModule( SndFileList* sndFiles, AudioConverterPluginList* pluginList, AudioConverterSettingsGroup** defaultSettings, int systemSampleRate, int segSize) { _sndFiles = sndFiles; _pluginList = pluginList; _defaultSettings = defaultSettings; _systemSampleRate = systemSampleRate; _segSize = segSize; } sf_count_t sndfile_vio_get_filelen(void *user_data) { return ((SndFile*)user_data)->virtualData()._virtualBytes; } sf_count_t sndfile_vio_seek(sf_count_t offset, int whence, void *user_data) { SndFile* sf = (SndFile*)user_data; SndFileVirtualData& vd = sf->virtualData(); if(!vd._virtualData) return -1; switch(whence) { case SEEK_CUR: if(vd._virtualCurPos + offset < 0 || vd._virtualCurPos + offset >= vd._virtualBytes) return -1; vd._virtualCurPos += offset; break; case SEEK_END: if(vd._virtualBytes + offset < 0 || vd._virtualBytes + offset >= vd._virtualBytes) return -1; vd._virtualCurPos = vd._virtualBytes + offset; break; default: if(offset < 0 || offset >= vd._virtualBytes) return -1; vd._virtualCurPos = offset; break; } return vd._virtualCurPos; } sf_count_t sndfile_vio_read(void *ptr, sf_count_t count, void *user_data) { SndFile* sf = (SndFile*)user_data; SndFileVirtualData& vd = sf->virtualData(); if(!vd._virtualData) return 0; if(vd._virtualCurPos >= vd._virtualBytes) return 0; if(vd._virtualCurPos + count > vd._virtualBytes) count = vd._virtualBytes - vd._virtualCurPos; // TODO Defer to AL::DSP for this? std::memcpy(ptr, (const char*)vd._virtualData + vd._virtualCurPos, count); vd._virtualCurPos += count; return count; } sf_count_t sndfile_vio_write(const void *ptr, sf_count_t count, void *user_data) { SndFile* sf = (SndFile*)user_data; SndFileVirtualData& vd = sf->virtualData(); if(!vd._virtualData) return 0; if(vd._virtualCurPos >= vd._virtualBytes) return 0; if(vd._virtualCurPos + count > vd._virtualBytes) count = vd._virtualBytes - vd._virtualCurPos; // TODO Defer to AL::DSP for this? std::memcpy((char*)vd._virtualData + vd._virtualCurPos, ptr, count); vd._virtualCurPos += count; return count; } sf_count_t sndfile_vio_tell(void *user_data) { return ((SndFile*)user_data)->virtualData()._virtualCurPos; } SF_VIRTUAL_IO sndfile_vio { sndfile_vio_get_filelen, sndfile_vio_seek, sndfile_vio_read, sndfile_vio_write, sndfile_vio_tell }; //--------------------------------------------------------- // SndFile //--------------------------------------------------------- SndFile::SndFile( const QString& name, bool installConverter, bool isOffline) : _isOffline(isOffline), _useConverter(installConverter) { _stretchList = nullptr; _audioConverterSettings = nullptr; if(_useConverter) { _stretchList = new StretchList(); // true = Local settings, initialized to -1. _audioConverterSettings = new AudioConverterSettingsGroup(true); // Local settings. if(_pluginList) _audioConverterSettings->populate(_pluginList, true); } finfo = new QFileInfo(name); sf = nullptr; sfUI = nullptr; csize = 0; cache = nullptr; openFlag = false; if(_sndFiles) _sndFiles->push_back(this); refCount = 0; writeBuffer = nullptr; writeSegSize = std::max((size_t)_segSize, (size_t)cacheMag);// cache minimum segment size for write operations _staticAudioConverter = nullptr; _staticAudioConverterUI = nullptr; _dynamicAudioConverter = nullptr; _dynamicAudioConverterUI = nullptr; } SndFile::SndFile( void* virtualData, sf_count_t virtualBytes, bool installConverter, bool isOffline) : _isOffline(isOffline), _useConverter(installConverter), _virtualData(SndFileVirtualData(virtualData, virtualBytes)) { _stretchList = nullptr; _audioConverterSettings = nullptr; if(_useConverter) { _stretchList = new StretchList(); // true = Local settings, initialized to -1. _audioConverterSettings = new AudioConverterSettingsGroup(true); // Local settings. if(_pluginList) _audioConverterSettings->populate(_pluginList, true); } finfo = nullptr; sf = nullptr; sfUI = nullptr; csize = 0; cache = nullptr; openFlag = false; //if(_sndFiles) // _sndFiles->push_back(this); refCount = 0; writeBuffer = nullptr; writeSegSize = std::max((size_t)_segSize, (size_t)cacheMag);// cache minimum segment size for write operations _staticAudioConverter = nullptr; _staticAudioConverterUI = nullptr; _dynamicAudioConverter = nullptr; _dynamicAudioConverterUI = nullptr; } SndFile::~SndFile() { DEBUG_WAVE(stderr, "SndFile dtor this:%p\n", this); if (openFlag) close(); if(_sndFiles) { for (iSndFile i = _sndFiles->begin(); i != _sndFiles->end(); ++i) { if (*i == this) { //DEBUG_WAVE(stderr, "erasing from sndfiles:%s\n", finfo->canonicalFilePath().toLatin1().constData()); _sndFiles->erase(i); break; } } } if(finfo) delete finfo; if (cache) delete[] cache; if(writeBuffer) delete [] writeBuffer; if(_stretchList) delete _stretchList; if(_audioConverterSettings) delete _audioConverterSettings; } int SndFile::getRefCount() const { return refCount; } bool SndFile::isOpen() const { return openFlag; } bool SndFile::isWritable() const { return writeFlag; } bool SndFile::useConverter() const { return _useConverter; } AudioConverterSettingsGroup* SndFile::audioConverterSettings() const { return _audioConverterSettings; } StretchList* SndFile::stretchList() const { return _stretchList; } bool SndFile::isOffline() { if(_staticAudioConverter) _isOffline = _staticAudioConverter->mode() == AudioConverterSettings::OfflineMode; return _isOffline; } bool SndFile::setOffline( bool v) { // Check if the current mode is already in the requested mode. if(isOffline() == v) return false; _isOffline = v; // Delete the current converter, if any. AudioConverterPluginI* converter = staticAudioConverter(AudioConverterSettings::RealtimeMode); if(converter) delete converter; converter = nullptr; if(useConverter() && audioConverterSettings()) { const AudioConverterSettingsGroup* settings = audioConverterSettings()->useSettings() ? audioConverterSettings() : *_defaultSettings; const bool isLocalSettings = audioConverterSettings()->useSettings(); const bool doStretch = isStretched(); const bool doResample = isResampled(); // For offline mode, we COULD create a third converter just for it, apart from the main // and UI converters. But our system doesn't have a third converter (yet) - and it may // or may not get one, we'll see. Still, the operation supports setting it, in case. // So instead, in offline mode we switch out the main converter for one with with offline settings. converter = setupAudioConverter( settings, *_defaultSettings, isLocalSettings, v ? AudioConverterSettings::OfflineMode : AudioConverterSettings::RealtimeMode, doResample, doStretch); } setStaticAudioConverter(converter, AudioConverterSettings::RealtimeMode); return true; } //--------------------------------------------------------- // openRead //--------------------------------------------------------- bool SndFile::openRead(bool createCache, bool showProgress) { if (openFlag) { DEBUG_WAVE(stderr, "SndFile:: already open\n"); return false; } // File based: if(finfo) { QString p = path(); if(p.isEmpty()) return true; sfinfo.format = 0; sfUI = nullptr; sf = sf_open(p.toLocal8Bit().constData(), SFM_READ, &sfinfo); if (!sf) return true; if(finfo && createCache){ sfinfo.format = 0; sfUI = sf_open(p.toLocal8Bit().constData(), SFM_READ, &sfinfo); if (!sfUI){ sf_close(sf); sf = nullptr; return true; } } } // Memory based: else { if(!_virtualData._virtualData) return true; //sfinfo.format = 0; // Supplied by caller via setFormat. sfUI = nullptr; sf = sf_open_virtual(&sndfile_vio, SFM_READ, &sfinfo, this); if (!sf) return true; } if(useConverter()) { _staticAudioConverter = setupAudioConverter( audioConverterSettings(), *_defaultSettings, true, // true = Local settings. isOffline() ? AudioConverterSettings::OfflineMode : AudioConverterSettings::RealtimeMode, isResampled(), isStretched()); if(finfo) _staticAudioConverterUI = setupAudioConverter( audioConverterSettings(), *_defaultSettings, true, // true = Local settings. AudioConverterSettings::GuiMode, isResampled(), isStretched()); } writeFlag = false; openFlag = true; if (finfo && createCache) { QString cacheName = finfo->absolutePath() + QString("/") + finfo->completeBaseName() + QString(".wca"); readCache(cacheName, showProgress); } return false; } AudioConverterPluginI* SndFile::setupAudioConverter( const AudioConverterSettingsGroup* settings, const AudioConverterSettingsGroup* defaultSettings, bool isLocalSettings, AudioConverterSettings::ModeType mode, bool doResample, bool doStretch) const { if(!useConverter() || !defaultSettings || !_pluginList) return nullptr; AudioConverterPluginI* plugI = nullptr; int pref_resampler = (settings && (settings->_options._useSettings || !isLocalSettings)) ? settings->_options._preferredResampler : defaultSettings->_options._preferredResampler; int pref_shifter = (settings && (settings->_options._useSettings || !isLocalSettings)) ? settings->_options._preferredShifter: defaultSettings->_options._preferredShifter; AudioConverterSettingsI* def_res_settings = nullptr; AudioConverterSettingsI* local_res_settings = nullptr; AudioConverterSettingsI* rt_res_settings = nullptr; AudioConverterPlugin* res_plugin = _pluginList->find(0, pref_resampler, AudioConverter::SampleRate); if(res_plugin) { if(isLocalSettings) { def_res_settings = defaultSettings->find( defaultSettings->_options._preferredResampler); local_res_settings = settings ? settings->find(pref_resampler) : nullptr; rt_res_settings = (local_res_settings && local_res_settings->useSettings(mode)) ? local_res_settings : def_res_settings; } else rt_res_settings = settings->find(pref_resampler); } AudioConverterSettingsI* def_str_settings = nullptr; AudioConverterSettingsI* local_str_settings = nullptr; AudioConverterSettingsI* rt_str_settings = nullptr; AudioConverterPlugin* str_plugin = _pluginList->find(0, pref_shifter, AudioConverter::Stretch); if(str_plugin) { if(isLocalSettings) { def_str_settings = defaultSettings->find( defaultSettings->_options._preferredShifter); local_str_settings = settings ? settings->find(pref_shifter) : nullptr; rt_str_settings = (local_str_settings && local_str_settings->useSettings(mode)) ? local_str_settings : def_str_settings; if(rt_str_settings) { } // Dummy for now, to avoid unused warning. } else rt_str_settings = settings->find(pref_shifter); } if(sf && (sampleRateDiffers() || doResample || doStretch)) { // Realtime audio section... // Create a new converter. AudioConverterPlugin* fin_plug = doStretch ? str_plugin : (res_plugin ? res_plugin : str_plugin); AudioConverterSettingsI* fin_set = doStretch ? rt_str_settings : (rt_res_settings ? rt_res_settings : rt_str_settings); if(fin_set && fin_plug && (fin_plug->maxChannels() < 0 || sfinfo.channels <= fin_plug->maxChannels())) { plugI = new AudioConverterPluginI(); plugI->initPluginInstance( fin_plug, _systemSampleRate, sfinfo.channels, fin_set->settings(), mode); } } return plugI; } AudioConverterPluginI* SndFile::staticAudioConverter(AudioConverterSettings::ModeType mode) const { switch(mode) { case AudioConverterSettings::OfflineMode: return nullptr; // TODO ? break; case AudioConverterSettings::RealtimeMode: return _staticAudioConverter; break; case AudioConverterSettings::GuiMode: return _staticAudioConverterUI; break; } return nullptr; } void SndFile::setStaticAudioConverter(AudioConverterPluginI* converter, AudioConverterSettings::ModeType mode) { switch(mode) { case AudioConverterSettings::OfflineMode: return; // TODO ? break; case AudioConverterSettings::RealtimeMode: _staticAudioConverter = converter; break; case AudioConverterSettings::GuiMode: _staticAudioConverterUI = converter; break; } } double SndFile::minStretchRatio() const { double m = 0.0; if(_staticAudioConverter && _staticAudioConverter->minStretchRatio() > m) m = _staticAudioConverter->minStretchRatio(); if(_staticAudioConverterUI && _staticAudioConverterUI->minStretchRatio() > m) m = _staticAudioConverterUI->minStretchRatio(); return m; } double SndFile::maxStretchRatio() const { double m = -1.0; if(_staticAudioConverter && _staticAudioConverter->maxStretchRatio() > 0.0 && (m < 0.0 || _staticAudioConverter->maxStretchRatio() < m)) m = _staticAudioConverter->maxStretchRatio(); if(_staticAudioConverterUI && _staticAudioConverterUI->maxStretchRatio() > 0.0 && (m < 0.0 || _staticAudioConverterUI->maxStretchRatio() < m)) m = _staticAudioConverterUI->maxStretchRatio(); return m; } double SndFile::minSamplerateRatio() const { double m = 0.0; if(_staticAudioConverter && _staticAudioConverter->minSamplerateRatio() > m) m = _staticAudioConverter->minSamplerateRatio(); if(_staticAudioConverterUI && _staticAudioConverterUI->minSamplerateRatio() > m) m = _staticAudioConverterUI->minSamplerateRatio(); return m; } double SndFile::maxSamplerateRatio() const { double m = -1.0; if(_staticAudioConverter && _staticAudioConverter->maxSamplerateRatio() > 0.0 && (m < 0.0 || _staticAudioConverter->maxSamplerateRatio() < m)) m = _staticAudioConverter->maxSamplerateRatio(); if(_staticAudioConverterUI && _staticAudioConverterUI->maxSamplerateRatio() > 0.0 && (m < 0.0 || _staticAudioConverterUI->maxSamplerateRatio() < m)) m = _staticAudioConverterUI->maxStretchRatio(); return m; } double SndFile::minPitchShiftRatio() const { double m = 0.0; if(_staticAudioConverter && _staticAudioConverter->minPitchShiftRatio() > m) m = _staticAudioConverter->minPitchShiftRatio(); if(_staticAudioConverterUI && _staticAudioConverterUI->minPitchShiftRatio() > m) m = _staticAudioConverterUI->minPitchShiftRatio(); return m; } double SndFile::maxPitchShiftRatio() const { double m = -1.0; if(_staticAudioConverter && _staticAudioConverter->maxPitchShiftRatio() > 0.0 && (m < 0.0 || _staticAudioConverter->maxPitchShiftRatio() < m)) m = _staticAudioConverter->maxPitchShiftRatio(); if(_staticAudioConverterUI && _staticAudioConverterUI->maxPitchShiftRatio() > 0.0 && (m < 0.0 || _staticAudioConverterUI->maxPitchShiftRatio() < m)) m = _staticAudioConverterUI->maxPitchShiftRatio(); return m; } void SndFile::setAudioConverterSettings(AudioConverterSettingsGroup* settings) { _audioConverterSettings = settings; } //--------------------------------------------------------- // update // called after recording to file //--------------------------------------------------------- void SndFile::update(bool showProgress) { if(!finfo) return; close(); // force recreation of wca data QString cacheName = finfo->absolutePath() + QString("/") + finfo->completeBaseName() + QString(".wca"); ::remove(cacheName.toLocal8Bit().constData()); if (openRead(true, showProgress)) { ERROR_WAVE(stderr, "SndFile::update openRead(%s) failed: %s\n", path().toLocal8Bit().constData(), strerror().toLocal8Bit().constData()); } } //--------------------------------------------------- // create cache //--------------------------------------------------- void SndFile::createCache(const QString& path, bool showProgress, bool bWrite, sf_count_t cstart) { if(!finfo || cstart >= csize) return; QProgressDialog* progress = nullptr; if (showProgress) { QString label(QWidget::tr("Create peakfile for ")); label += basename(); progress = new QProgressDialog(label, QString(), 0, csize, 0); progress->setMinimumDuration(0); progress->show(); } const int srcChannels = channels(); float data[srcChannels][cacheMag]; float* fp[srcChannels]; for (int k = 0; k < srcChannels; ++k) fp[k] = &data[k][0]; int interval = (csize - cstart) / 10; if(!interval) interval = 1; for (int i = cstart; i < csize; i++) { if (showProgress && ((i % interval) == 0)) progress->setValue(i); seek(i * cacheMag, 0); read(srcChannels, fp, cacheMag); for (int ch = 0; ch < srcChannels; ++ch) { float rms = 0.0; cache[ch][i].peak = 0; for (int n = 0; n < cacheMag; n++) { float fd = data[ch][n]; rms += fd * fd; int idata = int(fd * 255.0); if (idata < 0) idata = -idata; if (cache[ch][i].peak < idata) cache[ch][i].peak = idata; } // amplify rms value +12dB int rmsValue = int((sqrt(rms/cacheMag) * 255.0)); if (rmsValue > 255) rmsValue = 255; cache[ch][i].rms = rmsValue; } } if (showProgress) progress->setValue(csize); if(bWrite) writeCache(path); if (showProgress) delete progress; } //--------------------------------------------------------- // readCache //--------------------------------------------------------- void SndFile::readCache(const QString& path, bool showProgress) { if(!finfo) return; if (cache) { delete[] cache; } if (samples() == 0) return; const int srcChannels = channels(); csize = (samples() + cacheMag - 1)/cacheMag; cache = new SampleVtype[srcChannels]; for (int ch = 0; ch < srcChannels; ++ch) { cache [ch].resize(csize); } FILE* cfile = fopen(path.toLocal8Bit().constData(), "r"); if (cfile) { for (int ch = 0; ch < srcChannels; ++ch) fread(&cache[ch] [0], csize * sizeof(SampleV), 1, cfile); fclose(cfile); return; } createCache(path, showProgress, true); } //--------------------------------------------------------- // writeCache //--------------------------------------------------------- void SndFile::writeCache(const QString& path) { if(!finfo) return; FILE* cfile = fopen(path.toLocal8Bit().constData(), "w"); if (cfile == 0) return; const int srcChannels = channels(); for (int ch = 0; ch < srcChannels; ++ch) fwrite(&cache[ch] [0], csize * sizeof(SampleV), 1, cfile); fclose(cfile); } //--------------------------------------------------------- // read //--------------------------------------------------------- void SndFile::read(SampleV* s, int mag, unsigned pos, bool overwrite, bool allowSeek) { if(!finfo) return; const int srcChannels = channels(); if(overwrite) for (int ch = 0; ch < srcChannels; ++ch) { s[ch].peak = 0; s[ch].rms = 0; } // only check pos if seek is allowed // for realtime drawing of the wave // seek may cause writing errors if (allowSeek && pos > samples()) return; if (mag < cacheMag) { float data[srcChannels][mag]; float* fp[srcChannels]; for (int i = 0; i < srcChannels; ++i) fp[i] = &data[i][0]; sf_count_t ret = 0; if(sfUI) ret = sf_seek(sfUI, pos, SEEK_SET | SFM_READ); else ret = sf_seek(sf, pos, SEEK_SET | SFM_READ); if(ret == -1) return; { const int dstChannels = sfinfo.channels; const size_t n = mag; float** dst = fp; float buffer[n * dstChannels]; size_t rn = 0; if(sfUI) rn = sf_readf_float(sfUI, buffer, n); else rn = sf_readf_float(sf, buffer, n); if(rn != n) return; float* src = buffer; if (srcChannels == dstChannels) { for (size_t i = 0; i < rn; ++i) { for (int ch = 0; ch < srcChannels; ++ch) *(dst[ch]+i) = *src++; } } else if ((srcChannels == 1) && (dstChannels == 2)) { // stereo to mono for (size_t i = 0; i < rn; ++i) *(dst[0] + i) = src[i + i] + src[i + i + 1]; } else if ((srcChannels == 2) && (dstChannels == 1)) { // mono to stereo for (size_t i = 0; i < rn; ++i) { float data = *src++; *(dst[0]+i) = data; *(dst[1]+i) = data; } } } for (int ch = 0; ch < srcChannels; ++ch) { if(overwrite) s[ch].peak = 0; float rms = 0.0; for (int i = 0; i < mag; i++) { float fd = data[ch][i]; rms += fd; int idata = int(fd * 255.0); if (idata < 0) idata = -idata; if (s[ch].peak < idata) s[ch].peak = idata; } s[ch].rms = 0; // TODO rms / mag; } } else { mag /= cacheMag; int rest = csize - (pos/cacheMag); int end = mag; if (rest < mag) end = rest; for (int ch = 0; ch < srcChannels; ++ch) { int rms = 0; int off = pos/cacheMag; for (int offset = off; offset < off+end; offset++) { rms += cache[ch][offset].rms; if (s[ch].peak < cache[ch][offset].peak) s[ch].peak = cache[ch][offset].peak; } if(overwrite) s[ch].rms = rms / mag; else s[ch].rms += rms / mag; } } } //--------------------------------------------------------- // readConverted // The offset is the offset into the sound file and is NOT converted. //--------------------------------------------------------- void SndFile::readConverted(SampleV* s, int mag, sf_count_t pos, sf_count_t offset, bool overwrite, bool allowSeek) { if(!finfo) return; if(!useConverter() || !(_staticAudioConverterUI && _staticAudioConverterUI->isValid() && (((sampleRateDiffers() || isResampled()) && (_staticAudioConverterUI->capabilities() & AudioConverter::SampleRate)) || (isStretched() && (_staticAudioConverterUI->capabilities() & AudioConverter::Stretch))) )) { read(s, mag, offset + pos, overwrite, allowSeek); return; } const int srcChannels = channels(); if(overwrite) for (int ch = 0; ch < srcChannels; ++ch) { s[ch].peak = 0; s[ch].rms = 0; } // only check pos if seek is allowed // for realtime drawing of the wave // seek may cause writing errors // Removed. NOTE Seeking is now done only once in the graphic code. // if(allowSeek && convertPosition(pos) > convertPosition(samples())) // return; if (mag < cacheMag) { float data[srcChannels][mag]; float* fp[srcChannels]; for (int i = 0; i < srcChannels; ++i) fp[i] = &data[i][0]; sf_count_t ret = 0; const sf_count_t n = mag; if(sfUI) ret = _staticAudioConverterUI->process( sfUI, channels(), sampleRateRatio(), stretchList(), pos, fp, srcChannels, n, true); else ret = _staticAudioConverter->process( sf, channels(), sampleRateRatio(), stretchList(), pos, fp, srcChannels, n, true); if(ret != n) return; for (int ch = 0; ch < srcChannels; ++ch) { if(overwrite) s[ch].peak = 0; float rms = 0.0; for (int i = 0; i < mag; i++) { float fd = data[ch][i]; rms += fd; int idata = int(fd * 255.0); if (idata < 0) idata = -idata; if (s[ch].peak < idata) s[ch].peak = idata; } s[ch].rms = 0; // TODO rms / mag; } } else { mag /= cacheMag; sf_count_t rest = csize - ((offset + convertPosition(pos))/cacheMag); sf_count_t end = mag; if (rest < mag) end = rest; for (int ch = 0; ch < srcChannels; ++ch) { int rms = 0; sf_count_t off = (offset + convertPosition(pos))/cacheMag; for (sf_count_t offset = off; offset < off+end; offset++) { rms += cache[ch][offset].rms; if (s[ch].peak < cache[ch][offset].peak) s[ch].peak = cache[ch][offset].peak; } if(overwrite) s[ch].rms = rms / mag; else s[ch].rms += rms / mag; } } } //--------------------------------------------------------- // openWrite //--------------------------------------------------------- bool SndFile::openWrite() { if (openFlag) { DEBUG_WAVE(stderr, "SndFile:: alread open\n"); return false; } // File based: if(finfo) { const QString p = path(); if(p.isEmpty()) return true; sf = sf_open(p.toLocal8Bit().constData(), SFM_RDWR, &sfinfo); } // Memory based: else { if(!_virtualData._virtualData) return true; sf = sf_open_virtual(&sndfile_vio, SFM_RDWR, &sfinfo, this); } sfUI = nullptr; if (sf) { if(writeBuffer) delete [] writeBuffer; writeBuffer = new float [writeSegSize * std::max(2, sfinfo.channels)]; openFlag = true; writeFlag = true; if(finfo) { QString cacheName = finfo->absolutePath() + QString("/") + finfo->completeBaseName() + QString(".wca"); readCache(cacheName, true); } } return !sf; } //--------------------------------------------------------- // close //--------------------------------------------------------- void SndFile::close() { DEBUG_WAVE(stderr, "SndFile::close this:%p\n", this); if (!openFlag) { DEBUG_WAVE(stderr, "SndFile:: alread closed\n"); return; } if(int err = sf_close(sf)) { err += 0; // Touch. ERROR_WAVE(stderr, "SndFile::close Error:%d on sf_close(sf:%p)\n", err, sf); } else sf = nullptr; if (sfUI) { if(int err = sf_close(sfUI)) { err += 0; // Touch. ERROR_WAVE(stderr, "SndFile::close Error:%d on sf_close(sfUI:%p)\n", err, sfUI); } else sfUI = nullptr; } openFlag = false; if(_staticAudioConverter) { delete _staticAudioConverter; _staticAudioConverter = nullptr; } if(_staticAudioConverterUI) { delete _staticAudioConverterUI; _staticAudioConverterUI = nullptr; } if(_dynamicAudioConverter) { delete _dynamicAudioConverter; _dynamicAudioConverter = nullptr; } if(_dynamicAudioConverterUI) { delete _dynamicAudioConverterUI; _dynamicAudioConverterUI = nullptr; } } //--------------------------------------------------------- // remove //--------------------------------------------------------- void SndFile::remove() { if (openFlag) close(); if(finfo) QFile::remove(finfo->filePath()); } QString SndFile::basename() const { return finfo ? finfo->completeBaseName() : QString(); } QString SndFile::path() const { return finfo ? finfo->filePath() : QString(); } QString SndFile::canonicalPath() const { return finfo ? finfo->canonicalFilePath() : QString(); } QString SndFile::dirPath() const { return finfo ? finfo->absolutePath() : QString(); } QString SndFile::canonicalDirPath() const { return finfo ? finfo->canonicalPath() : QString(); } QString SndFile::name() const { return finfo ? finfo->fileName() : QString(); } //--------------------------------------------------------- // isFileWritable //--------------------------------------------------------- bool SndFile::isFileWritable() const { return finfo ? finfo->isWritable() : false; } //--------------------------------------------------------- // sampleRateRatio //--------------------------------------------------------- double SndFile::sampleRateRatio() const { return (double)sfinfo.samplerate / (double)_systemSampleRate; } //--------------------------------------------------------- // sampleRateDiffers //--------------------------------------------------------- bool SndFile::sampleRateDiffers() const { return sfinfo.samplerate != _systemSampleRate; } bool SndFile::isStretched() const { if(!_stretchList) return false; return _stretchList->isStretched(); } bool SndFile::isPitchShifted() const { if(!_stretchList) return false; return _stretchList->isPitchShifted(); } bool SndFile::isResampled() const { if(!_stretchList) return false; return _stretchList->isResampled(); } //--------------------------------------------------------- // convertPosition //--------------------------------------------------------- sf_count_t SndFile::convertPosition(sf_count_t pos) const { double new_pos = pos; if(useConverter() && _staticAudioConverter && _stretchList) { int type = 0; if(_staticAudioConverter->capabilities() & AudioConverter::Stretch) type |= StretchListItem::StretchEvent; if(_staticAudioConverter->capabilities() & AudioConverter::SampleRate) type |= StretchListItem::SamplerateEvent; // Apply sample rate conversion BEFORE unsquishing. if(_staticAudioConverter->capabilities() & AudioConverter::SampleRate) new_pos *= sampleRateRatio(); if(type != 0) new_pos = _stretchList->unSquish(new_pos, type); } return new_pos; } //--------------------------------------------------------- // unConvertPosition //--------------------------------------------------------- sf_count_t SndFile::unConvertPosition(sf_count_t pos) const { double new_pos = pos; if(useConverter() && _staticAudioConverter && _stretchList) { int type = 0; if(_staticAudioConverter->capabilities() & AudioConverter::Stretch) type |= StretchListItem::StretchEvent; if(_staticAudioConverter->capabilities() & AudioConverter::SampleRate) type |= StretchListItem::SamplerateEvent; if(type != 0) new_pos = _stretchList->squish(new_pos, type); // Apply sample rate conversion AFTER squishing. if(_staticAudioConverter->capabilities() & AudioConverter::SampleRate) new_pos /= sampleRateRatio(); } return new_pos; } //--------------------------------------------------------- // samples //--------------------------------------------------------- sf_count_t SndFile::samples() const { if (!finfo || !writeFlag) // if file is read only sfinfo is reliable return sfinfo.frames; SNDFILE* sfPtr = sf; if (sfUI) sfPtr = sfUI; sf_count_t curPos = sf_seek(sfPtr, 0, SEEK_CUR | SFM_READ); sf_count_t frames = sf_seek(sfPtr, 0, SEEK_END | SFM_READ); sf_seek(sfPtr, curPos, SEEK_SET | SFM_READ); return frames; } //--------------------------------------------------------- // samplesConverted //--------------------------------------------------------- sf_count_t SndFile::samplesConverted() const { if(sfinfo.samplerate == 0 || _systemSampleRate == 0) return 0; return samples() / sampleRateRatio(); } //--------------------------------------------------------- // channels //--------------------------------------------------------- int SndFile::channels() const { return sfinfo.channels; } int SndFile::samplerate() const { return sfinfo.samplerate; } int SndFile::format() const { return sfinfo.format; } void SndFile::setFormat(int fmt, int ch, int rate, sf_count_t frames) { sfinfo.samplerate = rate; sfinfo.channels = ch; sfinfo.format = fmt; sfinfo.seekable = true; sfinfo.frames = frames; } //--------------------------------------------------------- // readWithHeap // not as realtime friendly but can retrieve bigger data //--------------------------------------------------------- size_t SndFile::readWithHeap(int srcChannels, float** dst, size_t n, bool overwrite) { float *buffer = new float[n * sfinfo.channels]; int rn = readInternal(srcChannels,dst,n,overwrite, buffer); delete[] buffer; return rn; } //--------------------------------------------------------- // read //--------------------------------------------------------- size_t SndFile::read(int srcChannels, float** dst, size_t n, bool overwrite) { float buffer[n * sfinfo.channels]; int rn = readInternal(srcChannels,dst,n,overwrite, buffer); return rn; } size_t SndFile::readInternal(int srcChannels, float** dst, size_t n, bool overwrite, float *buffer) { size_t rn = sf_readf_float(sf, buffer, n); float* src = buffer; int dstChannels = sfinfo.channels; if (srcChannels == dstChannels) { if(overwrite) for (size_t i = 0; i < rn; ++i) { for (int ch = 0; ch < srcChannels; ++ch) *(dst[ch]+i) = *src++; } else for (size_t i = 0; i < rn; ++i) { for (int ch = 0; ch < srcChannels; ++ch) *(dst[ch]+i) += *src++; } } else if ((srcChannels == 1) && (dstChannels == 2)) { // stereo to mono if(overwrite) for (size_t i = 0; i < rn; ++i) *(dst[0] + i) = src[i + i] + src[i + i + 1]; else for (size_t i = 0; i < rn; ++i) *(dst[0] + i) += src[i + i] + src[i + i + 1]; } else if ((srcChannels == 2) && (dstChannels == 1)) { // mono to stereo if(overwrite) for (size_t i = 0; i < rn; ++i) { float data = *src++; *(dst[0]+i) = data; *(dst[1]+i) = data; } else for (size_t i = 0; i < rn; ++i) { float data = *src++; *(dst[0]+i) += data; *(dst[1]+i) += data; } } else { ERROR_WAVE(stderr, "SndFile:read channel mismatch %d -> %d\n", srcChannels, dstChannels); } return rn; } sf_count_t SndFile::readConverted(sf_count_t pos, int srcChannels, float** buffer, sf_count_t frames, bool overwrite) { if(useConverter() && _staticAudioConverter && _staticAudioConverter->isValid() && (((sampleRateDiffers() || isResampled()) && (_staticAudioConverter->capabilities() & AudioConverter::SampleRate)) || (isStretched() && (_staticAudioConverter->capabilities() & AudioConverter::Stretch))) ) { return _staticAudioConverter->process( sf, channels(), sampleRateRatio(), stretchList(), pos, buffer, srcChannels, frames, overwrite); } return read(srcChannels, buffer, frames, overwrite); } //--------------------------------------------------------- // write // // A hardcoded limiter was added that limits the output at 0.99/-0.99 // libsndfile handles signal betwee -1.0/1.0 with current setting // outside these values there will be heavy distortion // //--------------------------------------------------------- size_t SndFile::write(int srcChannels, float** src, size_t n, bool liveWaveUpdate) { size_t wrFrames = 0; if(n <= writeSegSize) wrFrames = realWrite(srcChannels, src, n, wrFrames, liveWaveUpdate); else { while(1) { size_t sz = (n - wrFrames) < writeSegSize ? (n - wrFrames) : writeSegSize; size_t nrWrote = realWrite(srcChannels, src, sz, wrFrames, liveWaveUpdate); if(nrWrote == 0) // Nothing written? break; wrFrames += nrWrote; if(wrFrames >= n) break; } } return wrFrames; } size_t SndFile::realWrite(int srcChannels, float** src, size_t n, size_t offs, bool liveWaveUpdate) { int dstChannels = sfinfo.channels; float *dst = writeBuffer; size_t iStart = offs; size_t iEnd = offs + n; const float limitValue=0.9999; for (int ch = 0; ch < srcChannels; ++ch) if(!src[ch]) return 0; if (srcChannels == dstChannels) { for (size_t i = iStart; i < iEnd; ++i) { for (int ch = 0; ch < dstChannels; ++ch) if (*(src[ch]+i) > 0) *dst++ = *(src[ch]+i) < limitValue ? *(src[ch]+i) : limitValue; else *dst++ = *(src[ch]+i) > -limitValue ? *(src[ch]+i) : -limitValue; } } else if ((srcChannels == 1) && (dstChannels == 2)) { // mono to stereo for (size_t i = iStart; i < iEnd; ++i) { float data = *(src[0]+i); if (data > 0) { *dst++ = data < limitValue ? data : limitValue; *dst++ = data < limitValue ? data : limitValue; } else { *dst++ = data > -limitValue ? data : -limitValue; *dst++ = data > -limitValue ? data : -limitValue; } } } else if ((srcChannels == 2) && (dstChannels == 1)) { // stereo to mono for (size_t i = iStart; i < iEnd; ++i) { if (*(src[0]+i) + *(src[1]+i) > 0) *dst++ = (*(src[0]+i) + *(src[1]+i)) < limitValue ? (*(src[0]+i) + *(src[1]+i)) : limitValue; else *dst++ = (*(src[0]+i) + *(src[1]+i)) > -limitValue ? (*(src[0]+i) + *(src[1]+i)) : -limitValue; } } else { ERROR_WAVE(stderr, "SndFile:write channel mismatch %d -> %d\n", srcChannels, dstChannels); return 0; } int nbr = sf_writef_float(sf, writeBuffer, n) ; if(liveWaveUpdate) { //update cache if(!cache) { cache = new SampleVtype[sfinfo.channels]; csize = 0; } sf_count_t cstart = (sfinfo.frames + cacheMag - 1) / cacheMag; sfinfo.frames += n; csize = (sfinfo.frames + cacheMag - 1) / cacheMag; for (int ch = 0; ch < sfinfo.channels; ++ch) { cache [ch].resize(csize); } for (int i = cstart; i < csize; i++) { for (int ch = 0; ch < sfinfo.channels; ++ch) { float rms = 0.0; cache[ch][i].peak = 0; for (int n = 0; n < cacheMag; n++) { //float fd = data[ch][n]; float fd = writeBuffer [n * sfinfo.channels + ch]; rms += fd * fd; int idata = int(fd * 255.0); if (idata < 0) idata = -idata; if (cache[ch][i].peak < idata) cache[ch][i].peak = idata; } // amplify rms value +12dB int rmsValue = int((sqrt(rms/cacheMag) * 255.0)); if (rmsValue > 255) rmsValue = 255; cache[ch][i].rms = rmsValue; } } } return nbr; } //--------------------------------------------------------- // seek //--------------------------------------------------------- sf_count_t SndFile::seek(sf_count_t frames, int whence) { return sf_seek(sf, frames, whence); } sf_count_t SndFile::seekUI(sf_count_t frames, int whence) { sf_count_t rn = 0; if(sfUI) rn = sf_seek(sfUI, frames, whence); else if(sf) rn = sf_seek(sf, frames, whence); return rn; } //--------------------------------------------------------- // seekUIConverted // The offset is the offset into the sound file and is NOT converted. //--------------------------------------------------------- sf_count_t SndFile::seekUIConverted(sf_count_t frames, int whence, sf_count_t offset) { const sf_count_t smps = samples(); sf_count_t rn = 0; sf_count_t pos = offset + convertPosition(frames); if(pos < 0) pos = 0; // Clamp it at 'one past the end' in other words EOF. if(pos > smps) pos = smps; if(sfUI) { rn = sf_seek(sfUI, pos, whence); // Reset the converter. Its current state is meaningless now. if(useConverter() && _staticAudioConverterUI) _staticAudioConverterUI->reset(); } else if(sf) { rn = sf_seek(sf, pos, whence); // Reset the converter. Its current state is meaningless now. if(useConverter() && _staticAudioConverter) _staticAudioConverter->reset(); } return rn; } //--------------------------------------------------------- // seekConverted // The offset is the offset into the sound file and is NOT converted. //--------------------------------------------------------- sf_count_t SndFile::seekConverted(sf_count_t frames, int whence, int offset) { if(useConverter() && _staticAudioConverter && _staticAudioConverter->isValid() && (((sampleRateDiffers() || isResampled()) && (_staticAudioConverter->capabilities() & AudioConverter::SampleRate)) || (isStretched() && (_staticAudioConverter->capabilities() & AudioConverter::Stretch))) ) { const sf_count_t smps = samples(); // Do not convert the offset. sf_count_t pos = offset + convertPosition(frames); if(pos < 0) pos = 0; // Clamp it at 'one past the end' in other words EOF. if(pos > smps) pos = smps; const sf_count_t rn = sf_seek(sf, pos, whence); // Reset the converter. Its current state is meaningless now. _staticAudioConverter->reset(); return rn; } return seek(frames + offset, whence); } //--------------------------------------------------------- // strerror //--------------------------------------------------------- QString SndFile::strerror() const { char buffer[128]; buffer[0] = 0; sf_error_str(sf, buffer, 128); return QString(buffer); } //--------------------------------------------------------- // search //--------------------------------------------------------- SndFile* SndFileList::search(const QString& name) { for (iSndFile i = begin(); i != end(); ++i) { if ((*i)->path() == name) return *i; } return nullptr; } // DELETETHIS 170 #if 0 //--------------------------------------------------------- // Clip //--------------------------------------------------------- ClipBase::ClipBase(const SndFileR& file, int start, int l) : f(file) { refCount = 0; for (int i = 1; true; ++i) { _name.sprintf("%s.%d", f.basename().toLocal8Bit().constData(), i); ciClip ic = waveClips->begin(); for (; ic != waveClips->end(); ++ic) { if ((*ic)->name() == _name) break; } if (ic == waveClips->end()) break; // try another name } _spos = start; len = l; deleted = false; lrefs = 0; waveClips->add(this); } //--------------------------------------------------------- // read //--------------------------------------------------------- void ClipBase::read(unsigned srcOffset, float** buffer, int channel, unsigned n) { if (f.isNull()) return; f.seek(srcOffset + _spos, 0); f.read(channel, buffer, n); } ClipBase::~ClipBase() { waveClips->remove(this); } //--------------------------------------------------------- // ClipList::write(level, xml) //--------------------------------------------------------- void ClipList::write(int level, Xml& xml) const { for (ciClip i = begin(); i != end(); ++i) { ClipBase* clip = *i; // only write visible clips if (clip->references()) (*i)->write(level, xml); } } //--------------------------------------------------------- // ClipBase::write(level, xml) //--------------------------------------------------------- void ClipBase::write(int level, Xml& xml) const { xml.tag(level++, "clip"); QString path = f.dirPath(); // // waves in the project directory are stored // with relative path name, others with absolute path // if (path == MusEGlobal::museProject) xml.strTag(level, "file", f.name()); else xml.strTag(level, "file", f.path()); xml.strTag(level, "name", _name); xml.intTag(level, "tick", _spos); xml.intTag(level, "len", len); xml.etag(level, "clip"); } //--------------------------------------------------------- // ClipBase::read //--------------------------------------------------------- ClipBase* readClip(Xml& xml) { SndFileR f = 0; QString name; unsigned spos = 0; int len = 0; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return 0; case Xml::TagStart: if (tag == "file") f = getWave(xml.parse1(), false); else if (tag == "name") name = xml.parse1(); else if (tag == "tick") spos = xml.parseInt(); else if (tag == "len") len = xml.parseInt(); else xml.unknown("Clip"); break; case Xml::TagEnd: if (tag == "clip") { if (!f) printf("clip: file not found\n"); ClipBase* clip = new ClipBase(f, spos, len); clip->setName(name); return clip; } default: break; } } } //--------------------------------------------------------- // search //--------------------------------------------------------- Clip ClipList::search(const QString& name) const { for (ciClip i = begin(); i != end(); ++i) if ((*i)->name() == name) return Clip(*i); fprintf(stderr, "ClipList: clip <%s> not found\n", name.toLocal8Bit().constData()); return Clip(); } //--------------------------------------------------------- // remove //--------------------------------------------------------- void ClipList::remove(ClipBase* clip) { for (iClip i = begin(); i != end(); ++i) { if (*i == clip) { erase(i); return; } } printf("ClipList:remove: clip not found\n"); } //--------------------------------------------------------- // idx //--------------------------------------------------------- int ClipList::idx(const Clip& clip) const { int n = 0; for (ciClip i = begin(); i != end(); ++i, ++n) { if (clip == *i) return n; } return -1; } #endif //--------------------------------------------------------- // SndFileR //--------------------------------------------------------- SndFileR::SndFileR(SndFile* _sf) { sf = _sf; if (sf) (sf->refCount)++; } SndFileR::SndFileR(const SndFileR& ed) { sf = ed.sf; if (sf) (sf->refCount)++; } //--------------------------------------------------------- // operator= //--------------------------------------------------------- SndFileR& SndFileR::operator=(SndFile* ptr) { if (sf == ptr) return *this; if (sf && --(sf->refCount) == 0) { delete sf; } sf = ptr; if (sf) (sf->refCount)++; return *this; } SndFileR& SndFileR::operator=(const SndFileR& ed) { return operator=(ed.sf); } //--------------------------------------------------------- // ~SndFileR //--------------------------------------------------------- SndFileR::~SndFileR() { *this=nullptr; // decrease the refcounter, maybe delete } } // namespace MusECore muse-4.0.0/src/libs/wave/wave.h000066400000000000000000000566031404105311000162600ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: wave.h,v 1.5.2.7 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 1999/2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __WAVE_H__ #define __WAVE_H__ #include #include #include #include #include #include "muse_time.h" #include "time_stretch.h" #include "audio_convert/audio_converter_plugin.h" #include "audio_convert/audio_converter_settings_group.h" namespace MusECore { //--------------------------------------------------------- // SndFileVirtualData // For virtual (memory or stream) operation //--------------------------------------------------------- class SndFileVirtualData { public: void* _virtualData; sf_count_t _virtualBytes; sf_count_t _virtualCurPos; SndFileVirtualData() : _virtualData(nullptr), _virtualBytes(0), _virtualCurPos(0) { } SndFileVirtualData(void* virtualData, const sf_count_t virtualBytes) : _virtualData(virtualData), _virtualBytes(virtualBytes), _virtualCurPos(0) { } }; //--------------------------------------------------------- // SampleV // peak file value //--------------------------------------------------------- struct SampleV { unsigned char peak; unsigned char rms; }; typedef std::vector SampleVtype; class SndFileList; //--------------------------------------------------------- // SndFile //--------------------------------------------------------- class SndFile { QFileInfo* finfo; SNDFILE* sf; SNDFILE* sfUI; AudioConverterPluginI* _staticAudioConverter; AudioConverterPluginI* _staticAudioConverterUI; AudioConverterPluginI* _dynamicAudioConverter; AudioConverterPluginI* _dynamicAudioConverterUI; AudioConverterSettingsGroup* _audioConverterSettings; StretchList* _stretchList; // Whether the converter is in offline mode. // Always use isOffline() to check instead of reading this directly. bool _isOffline; // Whether to use any converter(s) at all. bool _useConverter; SF_INFO sfinfo; SampleVtype* cache; sf_count_t csize; //!< frames in cache // For virtual (memory or stream) operation: SndFileVirtualData _virtualData; float *writeBuffer; size_t writeSegSize; void writeCache(const QString& path); bool openFlag; bool writeFlag; size_t readInternal(int srcChannels, float** dst, size_t n, bool overwrite, float *buffer); size_t realWrite(int srcChannels, float** src, size_t n, size_t offs = 0, bool liveWaveUpdate = false); protected: int refCount; public: // Constructor for file operation. SndFile( const QString& name, bool installConverter = true, bool isOffline = false); // Constructor for virtual (memory or stream) operation. // When using the virtual interface, be sure to call setFormat before opening. SndFile( void* virtualData, sf_count_t virtualBytes, bool installConverter = true, bool isOffline = false); ~SndFile(); static AudioConverterPluginList* _pluginList; static AudioConverterSettingsGroup** _defaultSettings; static int _systemSampleRate; static int _segSize; static SndFileList* _sndFiles; static void initWaveModule( SndFileList* sndFiles, AudioConverterPluginList* pluginList, AudioConverterSettingsGroup** defaultSettings, int systemSampleRate, int segSize); int getRefCount() const; // Whether to use any converter(s) at all. bool useConverter() const; // Whether the converter is in offline mode. // Always use isOffline() to check instead of reading the member directly. bool isOffline(); // Set the converter offline mode. // Returns whether the mode was actually changed. bool setOffline(bool v); // For virtual (memory or stream) operation. SndFileVirtualData& virtualData() { return _virtualData; } void createCache(const QString& path, bool showProgress, bool bWrite, sf_count_t cstart = 0); void readCache(const QString& path, bool progress); // Creates a new converter based on the supplied settings and AudioConverterSettings::ModeType mode. // If isLocalSettings is true, settings is treated as a local settings which may override the // global default settings. // If isLocalSettings is false, settings is treated as the global default settings and is // directly used instead of the comparison to, and possible use of, the global default above. AudioConverterPluginI* setupAudioConverter(const AudioConverterSettingsGroup* settings, const AudioConverterSettingsGroup* defaultSettings, bool isLocalSettings, AudioConverterSettings::ModeType mode, bool doResample, bool doStretch ) const; // When using the virtual interface, be sure to call setFormat before opening. //!< returns true on error bool openRead(bool createCache=true, bool showProgress=true); //!< returns true on error bool openWrite(); void close(); void remove(); bool isOpen() const; // Whether the file was opened with write mode. bool isWritable() const; void update(bool showProgress = true); QString basename() const; //!< filename without extension QString dirPath() const; //!< path QString canonicalDirPath() const; //!< path, resolved (no symlinks or . .. etc) QString path() const; //!< path with filename QString canonicalPath() const; //!< path with filename, resolved (no symlinks or . .. etc) QString name() const; //!< filename // Whether the file itself is writable (ie also in a writable directory etc.) bool isFileWritable() const; // Ratio of the file's sample rate to the current audio sample rate. double sampleRateRatio() const; // Whether the sample rate ratio is exactly 1. bool sampleRateDiffers() const; // Convert a frame position to its resampled or stretched position. sf_count_t convertPosition(sf_count_t pos) const; // Convert a resampled or stretched frame position to its unresampled or unstretched position. sf_count_t unConvertPosition(sf_count_t pos) const; // Returns whether ANY stretch event has a stretch ratio other than 1.0 // ie. the map is stretched, a stretcher must be engaged. bool isStretched() const; // Returns whether ANY stretch event has a pitch ratio other than 1.0 // ie. the map is pitch shifted, a shifter must be engaged. bool isPitchShifted() const; // Returns whether ANY stretch event has a samplerate ratio other than 1.0 // ie. the map is stretched, a samplerate converter must be engaged. bool isResampled() const; sf_count_t samples() const; // Returns number of samples, adjusted for file-to-system samplerate ratio. sf_count_t samplesConverted() const; int channels() const; int samplerate() const; int format() const; int sampleBits() const; void setFormat(int fmt, int ch, int rate, sf_count_t frames = 0); size_t read(int channel, float**, size_t, bool overwrite = true); size_t readWithHeap(int channel, float**, size_t, bool overwrite = true); size_t readDirect(float* buf, size_t n) { return sf_readf_float(sf, buf, n); } size_t write(int channel, float**, size_t, bool liveWaveUpdate /*= false*/); size_t writeDirect(float *buf, size_t n) { return sf_writef_float(sf, buf, n); } // For now I must provide separate routines here, don't want to upset anything else. // Reads realtime audio converted if a samplerate or shift/stretch converter is active. Otherwise a normal read. sf_count_t readConverted(sf_count_t pos, int srcChannels, float** buffer, sf_count_t frames, bool overwrite = true); // Reads graphical audio converted if a samplerate or shift/stretch converter is active. Otherwise a normal read. void readConverted(SampleV* s, int mag, sf_count_t pos, sf_count_t offset, bool overwrite = true, bool allowSeek = true); // Seeks to a converted position if a samplerate or shift/stretch converter is active. Otherwise a normal seek. // The offset is the offset into the sound file and is NOT converted. sf_count_t seekConverted(sf_count_t frames, int whence, int offset); AudioConverterPluginI* staticAudioConverter(AudioConverterSettings::ModeType mode) const; void setStaticAudioConverter(AudioConverterPluginI* converter, AudioConverterSettings::ModeType mode); AudioConverterSettingsGroup* audioConverterSettings() const; void setAudioConverterSettings(AudioConverterSettingsGroup* settings); StretchList* stretchList() const; sf_count_t seek(sf_count_t frames, int whence); sf_count_t seekUI(sf_count_t frames, int whence); sf_count_t seekUIConverted(sf_count_t frames, int whence, sf_count_t offset); void read(SampleV* s, int mag, unsigned pos, bool overwrite = true, bool allowSeek = true); QString strerror() const; // Absolute minimum and maximum ratios depending on chosen converters. -1 means infinite, don't care. double minStretchRatio() const; double maxStretchRatio() const; double minSamplerateRatio() const; double maxSamplerateRatio() const; double minPitchShiftRatio() const; double maxPitchShiftRatio() const; friend class SndFileR; }; //--------------------------------------------------------- // SndFileR // SndFile with reference count //--------------------------------------------------------- class SndFileR { SndFile* sf; public: SndFileR() { sf = 0; } SndFileR(SndFile* _sf); SndFileR(const SndFileR& ed); SndFileR& operator=(SndFile* ptr); SndFileR& operator=(const SndFileR& ed); bool operator==(const SndFileR& c) const { return sf == c.sf; } bool operator==(SndFile* c) const { return sf == c; } SndFile* operator->() { return sf; } const SndFile* operator->() const { return sf; } SndFile* operator*() { return sf; } const SndFile* operator*() const { return sf; } operator bool() { return sf!=NULL; } ~SndFileR(); int getRefCount() const { return sf ? sf->getRefCount() : 0; } bool isNull() const { return sf == 0; } // Whether to use any converter(s) at all. bool useConverter() const { return sf ? sf->useConverter() : false; } // Whether the converter is in offline mode. // Always use isOffline() to check instead of reading the member directly. bool isOffline() { return sf ? sf->isOffline() : false; } // Set the converter offline mode. // Returns whether the mode was actually changed. bool setOffline(bool v) { return sf ? sf->setOffline(v) : false; } // Creates a new converter based on the supplied settings and AudioConverterSettings::ModeType mode. // If isLocalSettings is true, settings is treated as a local settings which may override the // global default settings. // If isLocalSettings is false, settings is treated as the global default settings and is // directly used instead of the comparison to, and possible use of, the global default above. AudioConverterPluginI* setupAudioConverter( const AudioConverterSettingsGroup* settings, const AudioConverterSettingsGroup* defaultSettings, bool isLocalSettings, AudioConverterSettings::ModeType mode, bool doResample, bool doStretch) const { return sf ? sf->setupAudioConverter( settings, defaultSettings, isLocalSettings, mode, doResample, doStretch) : nullptr; } // When using the virtual interface, be sure to call setFormat before opening. bool openRead(bool createCache=true) { return sf ? sf->openRead(createCache) : true; } bool openWrite() { return sf ? sf->openWrite() : true; } void close() { if(sf) sf->close(); } void remove() { if(sf) sf->remove(); } bool isOpen() const { return sf ? sf->isOpen() : false; } // Whether the file was opened with write mode. bool isWritable() const { return sf ? sf->isWritable() : false; } void update(bool showProgress = true) { if(sf) sf->update(showProgress); } QString basename() const { return sf ? sf->basename() : QString(); } QString dirPath() const { return sf ? sf->dirPath() : QString(); } QString canonicalDirPath() const { return sf ? sf->canonicalDirPath() : QString(); } QString path() const { return sf ? sf->path() : QString(); } QString canonicalPath() const { return sf ? sf->canonicalPath() : QString(); } QString name() const { return sf ? sf->name() : QString(); } // Whether the file itself is writable (ie also in a writable directory etc.) bool isFileWritable() const { return sf ? sf->isFileWritable() : false; } // Ratio of the file's sample rate to the current audio sample rate. inline double sampleRateRatio() const { return sf ? sf->sampleRateRatio() : 1.0; }; // Whether the sample rate ratio is exactly 1. inline bool sampleRateDiffers() const { return sf ? sf->sampleRateDiffers() : false; }; // Convert a frame position to its resampled or stretched position. inline sf_count_t convertPosition(sf_count_t pos) const { return sf ? sf->convertPosition(pos) : pos; }; // Convert a resampled or stretched frame position to its unresampled or unstretched position. inline sf_count_t unConvertPosition(sf_count_t pos) const { return sf ? sf->unConvertPosition(pos) : pos; }; // Returns whether ANY stretch event has a stretch ratio other than 1.0 // ie. the map is stretched, a stretcher must be engaged. bool isStretched() const { return sf ? sf->isStretched() : false; } // Returns whether ANY stretch event has a pitch ratio other than 1.0 // ie. the map is pitch shifted, a shifter must be engaged. bool isPitchShifted() const { return sf ? sf->isPitchShifted() : false; } // Returns whether ANY stretch event has a samplerate ratio other than 1.0 // ie. the map is stretched, a samplerate converter must be engaged. bool isResampled() const { return sf ? sf->isResampled() : false; } sf_count_t samples() const { return sf ? sf->samples() : 0; } // Returns number of samples, adjusted for file-to-system samplerate ratio. sf_count_t samplesConverted() const { return sf ? sf->samplesConverted() : 0; } int channels() const { return sf ? sf->channels() : 0; } int samplerate() const { return sf ? sf->samplerate() : 0; } int format() const { return sf ? sf->format() : 0; } int sampleBits() const { return sf ? sf->sampleBits() : 0; } void setFormat(int fmt, int ch, int rate, sf_count_t frames = 0) { if(sf) sf->setFormat(fmt, ch, rate, frames); } size_t readWithHeap(int channel, float** f, size_t n, bool overwrite = true) { return sf ? sf->readWithHeap(channel, f, n, overwrite) : 0; } size_t read(int channel, float** f, size_t n, bool overwrite = true) { return sf ? sf->read(channel, f, n, overwrite) : 0; } size_t readDirect(float* f, size_t n) { return sf ? sf->readDirect(f, n) : 0; } size_t write(int channel, float** f, size_t n, bool liveWaveUpdate /*= false*/) { return sf ? sf->write(channel, f, n, liveWaveUpdate) : 0; } // For now I must provide separate routines here, don't want to upset anything else. // Reads realtime audio converted if a samplerate or shift/stretch converter is active. Otherwise a normal read. sf_count_t readConverted(sf_count_t pos, int channel, float** buffer, sf_count_t frames, bool overwrite = true) { return sf ? sf->readConverted(pos, channel, buffer, frames, overwrite) : 0; } // Reads graphical audio converted if a samplerate or shift/stretch converter is active. Otherwise a normal read. void readConverted(SampleV* s, int mag, unsigned pos, sf_count_t offset, bool overwrite = true, bool allowSeek = true) { if(sf) sf->readConverted(s, mag, pos, offset, overwrite, allowSeek); } // Seeks to a converted position if a samplerate or shift/stretch converter is active. Otherwise a normal seek. // The offset is the offset into the sound file and is NOT converted. sf_count_t seekConverted(sf_count_t frames, int whence, int offset) { return sf ? sf->seekConverted(frames, whence, offset) : 0; } AudioConverterPluginI* staticAudioConverter(AudioConverterSettings::ModeType mode) const { return sf ? sf->staticAudioConverter(mode) : 0; } void setStaticAudioConverter(AudioConverterPluginI* converter, AudioConverterSettings::ModeType mode) { if(sf) sf->setStaticAudioConverter(converter, mode); } AudioConverterSettingsGroup* audioConverterSettings() const { return sf ? sf->audioConverterSettings() : 0; } void setAudioConverterSettings(AudioConverterSettingsGroup* settings) { if(sf) sf->setAudioConverterSettings(settings); } StretchList* stretchList() const { return sf ? sf->stretchList() : 0; } // Absolute minimum and maximum ratios depending on chosen converters. -1 means infinite, don't care. double minStretchRatio() const { return sf ? sf->minStretchRatio() : 1.0; } double maxStretchRatio() const { return sf ? sf->maxStretchRatio() : 1.0; } double minSamplerateRatio() const { return sf ? sf->minSamplerateRatio() : 1.0; } double maxSamplerateRatio() const { return sf ? sf->maxSamplerateRatio() : 1.0; } double minPitchShiftRatio() const { return sf ? sf->minPitchShiftRatio() : 1.0; } double maxPitchShiftRatio() const { return sf ? sf->maxPitchShiftRatio() : 1.0; } sf_count_t seek(sf_count_t frames, int whence) { return sf ? sf->seek(frames, whence) : 0; } sf_count_t seekUI(sf_count_t frames, int whence) { return sf ? sf->seekUI(frames, whence) : 0; } sf_count_t seekUIConverted(sf_count_t frames, int whence, sf_count_t offset) { return sf ? sf->seekUIConverted(frames, whence, offset) : 0; } void read(SampleV* s, int mag, unsigned pos, bool overwrite = true, bool allowSeek = true) { if(sf) sf->read(s, mag, pos, overwrite, allowSeek); } QString strerror() const { return sf ? sf->strerror() : QString(); } }; //--------------------------------------------------------- // SndFileList //--------------------------------------------------------- class SndFileList : public std::list { public: SndFile* search(const QString& name); // void clearDelete(); // clearDelete MUST NOT exist! deleting is handled by the refcounting SndFileRs! // this SndFileList is just for information, consider it as "weak pointers" }; typedef SndFileList::iterator iSndFile; typedef SndFileList::const_iterator ciSndFile; #if 0 class Clip; //--------------------------------------------------------- // ClipBase //--------------------------------------------------------- class ClipBase { protected: QString _name; SndFileR f; int _spos; // start sample position in WaveFile int len; // len of clip int lrefs; // logical references bool deleted; int refCount; public: ClipBase(const SndFileR& f, int start, int len); ~ClipBase(); const QString& name() const { return _name; } void setName(const QString& s) { _name = s; } int spos() const { return _spos; } void setSpos(int s) { _spos = s; } SndFileR file1() const { return f; } void read(unsigned, float**, int, unsigned); void write(int, Xml&) const; int samples() const { return len; } void setSamples(int s) { len = s; } int getRefCount() const { return refCount; } int references() const { return lrefs; } void incRefs() { ++lrefs; } void decRefs() { --lrefs; } friend class WaveEvent; }; //--------------------------------------------------------- // Clip //--------------------------------------------------------- class Clip { ClipBase* clip; public: Clip(); Clip(ClipBase* clip); Clip(const SndFileR& f, int start, int len); Clip(const Clip&); Clip& operator=(const Clip&); bool operator==(const Clip& c) const { return clip == c.clip; } bool operator==(ClipBase* c) const { return clip == c; } ~Clip(); bool isNull() const { return clip == 0; } int getRefCount() const { return clip->getRefCount(); } const QString& name() const { return clip->name(); } void setName(const QString& s) { clip->setName(s); } int spos() const { return clip->spos(); } void setSpos(int s) { clip->setSpos(s); } SndFileR file1() const { return clip->file1(); } void read(unsigned off, float** f, int ch, unsigned nn) { clip->read(off, f, ch, nn); } int samples() const { return clip->samples(); } void setSamples(int s) { clip->setSamples(s); } int references() const { return clip->references(); } void incRefs() { clip->incRefs(); } void decRefs() { clip->decRefs(); } }; //--------------------------------------------------------- // ClipList //--------------------------------------------------------- class ClipList : public std::list { public: int idx(const Clip&) const; Clip search(const QString&) const; void write(int, Xml&) const; void add(ClipBase* clip) { push_back(clip); } void remove(ClipBase*); }; typedef ClipList::iterator iClip; typedef ClipList::const_iterator ciClip; extern ClipBase* readClip(Xml& xml); #endif } // namespace MusECore #endif muse-4.0.0/src/libs/xml/000077500000000000000000000000001404105311000147715ustar00rootroot00000000000000muse-4.0.0/src/libs/xml/CMakeLists.txt000066400000000000000000000032741404105311000175370ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # # xml/CMakeLists.txt # (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB xml_source_files xml.cpp ) ## ## Define target ## add_library ( xml_module SHARED ${xml_source_files} ) ## ## Compilation flags and target name ## set_target_properties( xml_module PROPERTIES OUTPUT_NAME muse_xml_module ) ## ## Linkage ## target_link_libraries(xml_module ${QT_LIBRARIES} ) ## ## Install location ## install(TARGETS xml_module DESTINATION ${MusE_MODULES_DIR} ) # if ( ${MODULES_BUILD} STREQUAL SHARED ) # install(TARGETS # # xml_module # DESTINATION ${MusE_MODULES_DIR} # ) # endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/libs/xml/xml.cpp000066400000000000000000001114311404105311000162760ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: xml.cpp,v 1.17.2.6 2009/12/07 20:48:45 spamatica Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "xml.h" namespace MusECore { const int Xml::_latestMajorVersion = 3; // Latest known songfile major version (as of this release) const int Xml::_latestMinorVersion = 3; // Latest known songfile minor version (as of this release) //--------------------------------------------------------- // Note: // this code is a Q/D hack for reading/parsing and // writing XML-Files // - can only handle the XML subset used by MusE // - may not handle malformed XML (eg. when manually // editing MusE output) //--------------------------------------------------------- // Xml //--------------------------------------------------------- Xml::Xml(FILE* _f) { f = _f; _destStr = nullptr; _destIODev = nullptr; _line = 0; _col = 0; level = 0; inTag = false; inComment = false; lbuffer[0] = 0; bufptr = lbuffer; _minorVersion = -1; _majorVersion = -1; } Xml::Xml(const char* buf) { f = nullptr; _destStr = nullptr; _destIODev = nullptr; _line = 0; _col = 0; level = 0; inTag = false; inComment = false; bufptr = buf; _minorVersion = -1; _majorVersion = -1; } Xml::Xml(QString* s) { f = nullptr; _destIODev = nullptr; _line = 0; _col = 0; level = 0; inTag = false; inComment = false; lbuffer[0] = 0; bufptr = lbuffer; _destStr = s; _minorVersion = -1; _majorVersion = -1; } Xml::Xml(QIODevice* d) { f = nullptr; _destStr = nullptr; _destIODev = d; _line = 0; _col = 0; level = 0; inTag = false; inComment = false; lbuffer[0] = 0; bufptr = lbuffer; _minorVersion = -1; _majorVersion = -1; } //--------------------------------------------------------- // BEGIN Read functions: //--------------------------------------------------------- //--------------------------------------------------------- // next //--------------------------------------------------------- void Xml::next() { if (*bufptr == 0) { if((!f && !_destIODev) || (f && fgets(lbuffer, 512, f) == nullptr) || (_destIODev && _destIODev->readLine(lbuffer, 512) <= 0)) { c = EOF; return; } bufptr = lbuffer; } c = *bufptr++; if (c == '\n') { ++_line; _col = -1; } ++_col; } //--------------------------------------------------------- // nextc // get next non space character //--------------------------------------------------------- void Xml::nextc() { next(); while (c == ' ' || c == '\t' || c == '\n') next(); } //--------------------------------------------------------- // token // read token into _s2 //--------------------------------------------------------- void Xml::token(int cc) { QByteArray buffer; int i = 0; for (; i < 9999999;) { // Stop at a reasonably large amount 10 million. if (c == ' ' || c == '\t' || c == cc || c == '\n' || c == EOF) break; buffer.append(c); i++; next(); } buffer.append(char(0)); _s2 = buffer; // deep copy !? } //--------------------------------------------------------- // stoken // read string token into _s2 //--------------------------------------------------------- void Xml::stoken() { QByteArray buffer; int i = 0; buffer.append(c); i++; next(); for (;i < 10000000*4-1;) { // Stop at a reasonably large amount 10 million. if (c == '"') { buffer.append(c); i++; next(); break; } if (c == '&') { char entity[6]; int k = 0; for (; k < 6; ++k) { next(); if (c == EOF) break; else if (c == ';') { entity[k] = 0; if (strcmp(entity, "quot") == 0) c = '"'; else if (strcmp(entity, "amp") == 0) c = '&'; else if (strcmp(entity, "lt") == 0) c = '<'; else if (strcmp(entity, "gt") == 0) c = '>'; else if (strcmp(entity, "apos") == 0) c = '\''; else entity[k] = c; break; } else entity[k] = c; } if (c == EOF || k == 6) { // dump entity int n = 0; buffer.append('&'); i++; for (;(i < 511) && (n < k); ++i, ++n) buffer.append(entity[n]); } else { buffer.append(c); i++; } } else if(c != EOF) { buffer.append(c); i++; } if (c == EOF) break; next(); } buffer.append(char(0)); _s2 = buffer; } //--------------------------------------------------------- // strip // strip `"` from string //--------------------------------------------------------- QString Xml::strip(const QString& s) { int l = s.length(); if (l >= 2 && s[0] == '"') return s.mid(1, l-2); return s; } //--------------------------------------------------------- // parse //--------------------------------------------------------- Xml::Token Xml::parse() { QByteArray buffer; int idx = 0; again: bool endFlag = false; nextc(); if (c == EOF) { //if (level > 0 || MusEGlobal::debugMsg) if (level > 0) fprintf(stderr, "WARNING: unexpected EOF reading xml file at level %d, line %d, <%s><%s><%s>\n", level, _line, _tag.toLatin1().constData(), _s1.toLatin1().constData(), _s2.toLatin1().constData()); return level == 0 ? End : Error; } _s1.clear(); if (inTag) { //------------------- // parse Attributes //------------------- if (c == '/') { nextc(); token('>'); if (c != '>') { fprintf(stderr, "Xml: unexpected char '%c', expected '>'\n", c); goto error; } _s1 = _tag; inTag = false; --level; return TagEnd; } _s2.clear(); token('='); _s1 = _s2; nextc(); // skip space if (c == EOF) { //if (level > 0 || MusEGlobal::debugMsg) if (level > 0) fprintf(stderr, "WARNING: unexpected EOF reading xml file at level %d, line %d, <%s><%s><%s>\n", level, _line, _tag.toLatin1().constData(), _s1.toLatin1().constData(), _s2.toLatin1().constData()); return level == 0 ? End : Error; } if (c == '"') stoken(); else token('>'); if (c == '>') inTag = false; else --bufptr; _s2 = strip(_s2); return Attribut; } if (c == '<') { //-------------- // parse Tag //-------------- next(); if (c == '/') { endFlag = true; next(); } if (c == '?') { next(); idx = 0; buffer.clear(); for (;;) { if (c == '?' || c == EOF || c == '>') break; buffer.append(c); idx++; // TODO: check overflow next(); } buffer.append(char(0)); _s1 = QString(buffer); if (c == EOF) { fprintf(stderr, "XML: unexpected EOF\n"); goto error; } nextc(); if (c != '>') { fprintf(stderr, "XML: '>' expected\n"); goto error; } next(); return Proc; } else if (c == '!') { // process comment or declaration bool check_start = true; bool startc = false; bool startc_2 = false; bool endc = false; bool endc_2 = false; for(;;) { next(); if(c == '-') { if (check_start) { if(startc) { startc_2 = true; check_start = false; } else { startc = true; } continue; } // Check for end -- only if start -- was found. else if(startc_2) { if(endc) endc_2 = true; else endc = true; continue; } } else { if (check_start) { check_start = false; // It's a declaration, such as 'DOCTYPE' reference to dtd... } // Check for '>' if declaration (no start --) or comment (start and end -- found). if (c == '>' && (!startc_2 || endc_2)) break; if (c == EOF) { fprintf(stderr, "XML: unexpected EOF in comment\n"); goto error; } // It's not a '-' character so that destroys the start of any comment end sequence. endc = endc_2 = false; } } goto again; } idx = 0; buffer.clear(); for (;;) { if (c == '/' || c == ' ' || c == '\t' || c == '>' || c == '\n' || c == EOF) break; // TODO: check overflow buffer.append(c); idx++; next(); } buffer.append(char(0)); _s1 = QString(buffer); // skip white space: while (c == ' ' || c == '\t' || c == '\n') next(); if (c == '/') { nextc(); if (c == '>') return Flag; fprintf(stderr, "XML: '>' expected\n"); goto error; } if (c == '?') { nextc(); if (c == '>') return Proc; fprintf(stderr, "XML: '>' expected\n"); goto error; } if (c == '>') { if (endFlag) { --level; return TagEnd; } else { ++level; return TagStart; } } else { _tag = _s1; --bufptr; inTag = true; ++level; if (!endFlag) { return TagStart; } fprintf(stderr, "XML: endFlag expected\n"); goto error; } } else { //-------------- // parse Text //-------------- if (level == 0) { fprintf(stderr, "XML: level = 0\n"); goto error; } idx = 0; buffer.clear(); for (;;) { if (c == EOF || c == '<') break; if (c == '&') { next(); if (c == '<') { // be tolerant with old muse files buffer.append('&'); idx++; continue; } QByteArray name; int name_idx = 0; name.append(c); name_idx++; for (; name_idx < 9999999;) { // Stop at a reasonably large amount 10 million. next(); if (c == ';') break; name.append(c); name_idx++; } name.append(char(0)); if (strcmp(name, "lt") == 0) c = '<'; else if (strcmp(name, "gt") == 0) c = '>'; else if (strcmp(name, "apos") == 0) c = '\''; else if (strcmp(name, "quot") == 0) c = '"'; else if (strcmp(name, "amp") == 0) c = '&'; else c = '?'; } buffer.append(c); idx++; next(); } buffer.append(char(0)); _s1 = QString(buffer); if (c == '<') --bufptr; return Text; } error: fprintf(stderr, "XML Parse Error at line %d col %d\n", _line, _col+1); return Error; } //--------------------------------------------------------- // parse(QString) //--------------------------------------------------------- QString Xml::parse(const QString& tag) { QString a; for (;;) { switch (parse()) { case Error: case End: return a; default: case TagStart: case Attribut: break; case Text: a = _s1; break; case TagEnd: if (_s1 == tag) return a; break; } } return a; } //--------------------------------------------------------- // parse1 //--------------------------------------------------------- QString Xml::parse1() { return parse(_s1.simplified()); } //--------------------------------------------------------- // parseInt //--------------------------------------------------------- int Xml::parseInt() { QString s(parse1().simplified()); bool ok; int base = 10; if (s.startsWith("0x") || s.startsWith("0X")) { base = 16; s = s.mid(2); } int n = s.toInt(&ok, base); return n; } //--------------------------------------------------------- // parseLongLong //--------------------------------------------------------- long long Xml::parseLongLong() { QString s(parse1().simplified()); bool ok; int base = 10; if (s.startsWith("0x") || s.startsWith("0X")) { base = 16; s = s.mid(2); } long long n = s.toLongLong(&ok, base); return n; } //--------------------------------------------------------- // parseULongLong //--------------------------------------------------------- unsigned long long Xml::parseULongLong() { QString s(parse1().simplified()); bool ok; int base = 10; if (s.startsWith("0x") || s.startsWith("0X")) { base = 16; s = s.mid(2); } unsigned long long n = s.toULongLong(&ok, base); return n; } //--------------------------------------------------------- // parseLongInt //--------------------------------------------------------- long int Xml::parseLongInt() { QString s(parse1().simplified()); bool ok; int base = 10; if (s.startsWith("0x") || s.startsWith("0X")) { base = 16; s = s.mid(2); } long int n = s.toLong(&ok, base); return n; } //--------------------------------------------------------- // parseUInt //--------------------------------------------------------- unsigned int Xml::parseUInt() { QString s(parse1().simplified()); bool ok; int base = 10; if (s.startsWith("0x") || s.startsWith("0X")) { base = 16; s = s.mid(2); } unsigned int n = s.toUInt(&ok, base); return n; } //--------------------------------------------------------- // parseLongUInt //--------------------------------------------------------- unsigned long int Xml::parseLongUInt() { QString s(parse1().simplified()); bool ok; int base = 10; if (s.startsWith("0x") || s.startsWith("0X")) { base = 16; s = s.mid(2); } unsigned long int n = s.toULong(&ok, base); return n; } //--------------------------------------------------------- // parseFloat //--------------------------------------------------------- float Xml::parseFloat() { QString s(parse1().simplified()); return s.toFloat(); } //--------------------------------------------------------- // parseDouble //--------------------------------------------------------- double Xml::parseDouble() { QString s(parse1().simplified()); return s.toDouble(); } //--------------------------------------------------------- // Xml::skip //--------------------------------------------------------- void Xml::skip(const QString& etag) { for (;;) { Token token = parse(); const QString& tag = s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::Text: break; case Xml::TagEnd: if (tag == etag) return; break; case Xml::TagStart: skip(tag); break; default: break; } } } void Xml::dump(QString &dump) { if(f) { fpos_t pos; fgetpos(f, &pos); rewind(f); while(fgets(lbuffer, 512, f) != nullptr) dump.append(lbuffer); fsetpos(f, &pos); } else if(_destIODev) { // Only if not sequential. if(!_destIODev->isSequential()) { const qint64 pos = _destIODev->pos(); _destIODev->seek(0); while(_destIODev->read(lbuffer, 512) > 0) dump.append(lbuffer); _destIODev->seek(pos); } } else if(_destStr) { dump.append(*_destStr); } } //--------------------------------------------------------- // Basic functions: //--------------------------------------------------------- //--------------------------------------------------------- // readGeometry //--------------------------------------------------------- QRect readGeometry(Xml& xml, const QString& name) { QRect r(0, 0, 50, 50); int val; for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; QString tag = xml.s1(); switch (token) { case Xml::TagStart: xml.parse1(); break; case Xml::Attribut: val = xml.s2().toInt(); if (tag == "x") r.setX(val); else if (tag == "y") r.setY(val); else if (tag == "w") r.setWidth(val); else if (tag == "h") r.setHeight(val); break; case Xml::TagEnd: if (tag == name) return r; default: break; } } return r; } //--------------------------------------------------------- // readColor //--------------------------------------------------------- QColor readColor(Xml& xml) { int val, r=0, g=0, b=0; for (;;) { Xml::Token token = xml.parse(); if (token != Xml::Attribut) break; QString tag = xml.s1(); switch (token) { case Xml::Attribut: val = xml.s2().toInt(); if (tag == "r") r = val; else if (tag == "g") g = val; else if (tag == "b") b = val; break; default: break; } } return QColor(r, g, b); } //--------------------------------------------------------- // unknown //--------------------------------------------------------- void Xml::unknown(const char* s) { fprintf(stderr, "%s: unknown tag <%s> at line %d\n", s, _s1.toLatin1().constData(), _line+1); parse1(); } //--------------------------------------------------------- // BEGIN Write functions: //--------------------------------------------------------- //--------------------------------------------------------- // header //--------------------------------------------------------- void Xml::header() { const char* s = "\n"; if(f) fprintf(f, "%s", s); else { if(_destIODev) _destIODev->write(s); else if(_destStr) _destStr->append(s); } } //--------------------------------------------------------- // putFileVersion //--------------------------------------------------------- int Xml::putFileVersion(int level) { nput(level++, "\n", latestMajorVersion(), latestMinorVersion()); return level; } //--------------------------------------------------------- // put //--------------------------------------------------------- void Xml::put(const char* format, ...) { va_list args; va_start(args, format); if(f) { vfprintf(f, format, args); va_end(args); putc('\n', f); } else { const QString s = QString::vasprintf(format, args) + '\n'; va_end(args); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } void Xml::put(int level, const char* format, ...) { va_list args; va_start(args, format); putLevel(level); if(f) { vfprintf(f, format, args); va_end(args); putc('\n', f); } else { const QString s = QString::vasprintf(format, args) + '\n'; va_end(args); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } //--------------------------------------------------------- // nput //--------------------------------------------------------- void Xml::nput(int level, const char* format, ...) { va_list args; va_start(args, format); putLevel(level); if(f) { vfprintf(f, format, args); va_end(args); } else { const QString s = QString::vasprintf(format, args); va_end(args); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } void Xml::nput(const char* format, ...) { va_list args; va_start(args, format); if(f) { vfprintf(f, format, args); va_end(args); } else { const QString s = QString::vasprintf(format, args); va_end(args); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } //--------------------------------------------------------- // tag //--------------------------------------------------------- void Xml::tag(int level, const char* format, ...) { va_list args; va_start(args, format); putLevel(level); if(f) { putc('<', f); vfprintf(f, format, args); va_end(args); putc('>', f); putc('\n', f); } else { const QString s = '<' + QString::vasprintf(format, args) + ">\n"; va_end(args); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } //--------------------------------------------------------- // etag //--------------------------------------------------------- void Xml::etag(int level, const char* format, ...) { va_list args; va_start(args, format); putLevel(level); if(f) { putc('<', f); putc('/', f); vfprintf(f, format, args); va_end(args); putc('>', f); putc('\n', f); } else { const QString s = "\n"; va_end(args); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } void Xml::putLevel(int n) { if(f) { for (int i = 0; i < n*2; ++i) putc(' ', f); } else if(_destIODev) { for (int i = 0; i < n*2; ++i) _destIODev->putChar(' '); } else if(_destStr) { for (int i = 0; i < n*2; ++i) _destStr->append(' '); } } void Xml::intTag(int level, const char* name, int val) { putLevel(level); if(f) { fprintf(f, "<%s>%d\n", name, val, name); } else { const QString s = QString("<%1>%2\n").arg(name).arg(val).arg(name); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } void Xml::longIntTag(int level, const char* name, long int val) { putLevel(level); fprintf(f, "<%s>%ld\n", name, val, name); } void Xml::uintTag(int level, const char* name, unsigned int val) { putLevel(level); if(f) { fprintf(f, "<%s>%u\n", name, val, name); } else { const QString s = QString("<%1>%2\n").arg(name).arg(val).arg(name); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } void Xml::longLongTag(int level, const char* name, long long val) { putLevel(level); if(f) { fprintf(f, "<%s>%lld\n", name, val, name); } else { const QString s = QString("<%1>%2\n").arg(name).arg(val).arg(name); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } void Xml::uLongLongTag(int level, const char* name, unsigned long long val) { putLevel(level); if(f) { fprintf(f, "<%s>%llu\n", name, val, name); } else { const QString s = QString("<%1>%2\n").arg(name).arg(val).arg(name); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } void Xml::longUintTag(int level, const char* name, unsigned long int val) { putLevel(level); fprintf(f, "<%s>%lu\n", name, val, name); } void Xml::floatTag(int level, const char* name, float val) { putLevel(level); if(f) { // using QString to format decimal values since we know that // toLatin1 will make a string with decimal point instead of // decimal comma that some locales use QString s("<%1>%2\n"); fprintf(f, "%s", s.arg(name).arg(val).arg(name).toLatin1().constData()); } else { const QString s = QString("<%1>%2\n").arg(name).arg(val).arg(name); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } void Xml::doubleTag(int level, const char* name, double val) { putLevel(level); if(f) { // using QString to format decimal values since we know that // toLatin1 will make a string with decimal point instead of // decimal comma that some locales use QString s("<%1>%2\n"); fprintf(f, "%s", s.arg(name).arg(val).arg(name).toLatin1().constData()); } else { const QString s = QString("<%1>%2\n").arg(name).arg(val).arg(name); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } void Xml::strTag(int level, const char* name, const char* val) { putLevel(level); if(f) { fprintf(f, "<%s>", name); if (val) { while (*val) { switch(*val) { case '&': fprintf(f, "&"); break; case '<': fprintf(f, "<"); break; case '>': fprintf(f, ">"); break; case '\'': fprintf(f, "'"); break; case '"': fprintf(f, """); break; default: fputc(*val, f); break; } ++val; } } fprintf(f, "\n", name); } else { QString s = QString("<%1>").arg(name); if (val) { while (*val) { switch(*val) { case '&': s.append("&"); break; case '<': s.append("<"); break; case '>': s.append(">"); break; case '\'': s.append("'"); break; case '"': s.append("""); break; default: s.append(*val); break; } ++val; } } s.append(QString("\n").arg(name)); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } //--------------------------------------------------------- // colorTag //--------------------------------------------------------- void Xml::colorTag(int level, const char* name, const QColor& color) { putLevel(level); if(f) { fprintf(f, "<%s r=\"%d\" g=\"%d\" b=\"%d\">\n", name, color.red(), color.green(), color.blue(), name); } else { const QString s = QString("<%1 r=\"%2\" g=\"%3\" b=\"%4\">\n") .arg(name).arg(color.red()).arg(color.green()).arg(color.blue()).arg(name); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } void Xml::colorTag(int level, const QString& name, const QColor& color) { colorTag(level, name.toLocal8Bit().constData(), color); } //--------------------------------------------------------- // geometryTag //--------------------------------------------------------- void Xml::geometryTag(int level, const char* name, const QWidget* g) { qrectTag(level, name, QRect(g->pos(), g->size())); } //--------------------------------------------------------- // qrectTag //--------------------------------------------------------- void Xml::qrectTag(int level, const char* name, const QRect& r) { putLevel(level); if(f) { fprintf(f, "<%s x=\"%d\" y=\"%d\" w=\"%d\" h=\"%d\">\n", name, r.x(), r.y(), r.width(), r.height(), name); } else { const QString s = QString("<%1 x=\"%2\" y=\"%3\" w=\"%4\" h=\"%5\">\n") .arg(name).arg(r.x()).arg(r.y()).arg(r.width()).arg(r.height()).arg(name); if(_destIODev) _destIODev->write(s.toLatin1()); else if(_destStr) _destStr->append(s); } } //--------------------------------------------------------- // strTag //--------------------------------------------------------- void Xml::strTag(int level, const char* name, const QString& val) { strTag(level, name, val.toLocal8Bit().constData()); } void Xml::strTag(int level, const QString& name, const QString& val) { strTag(level, name.toLocal8Bit().constData(), val.toLocal8Bit().constData()); } //--------------------------------------------------------- // xmlString //--------------------------------------------------------- QString Xml::xmlString(const char* s) { return Xml::xmlString(QString(s)); } //--------------------------------------------------------- // xmlString //--------------------------------------------------------- QString Xml::xmlString(const QString& ss) { QString s(ss); s.replace('&', "&"); s.replace('<', "<"); s.replace('>', ">"); s.replace('\'', "'"); s.replace('"', """); return s; } } // namespace MusECore muse-4.0.0/src/libs/xml/xml.h000066400000000000000000000163471404105311000157550ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: xml.h,v 1.8.2.3 2009/11/09 20:28:28 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __XML_H__ #define __XML_H__ #include #include #include #include #include #include namespace MusECore { //--------------------------------------------------------- // Xml // very simple XML-like parser //--------------------------------------------------------- class Xml { // When constructed with a FILE* parameter, this will be valid. FILE* f; // When constructed with a QString* parameter, this will be valid. QString* _destStr; // When constructed with a QIODevice* parameter, this will be valid. QIODevice* _destIODev; int _line; int _col; QString _s1, _s2, _tag; int level; bool inTag; bool inComment; // NOTICE: Whenever the MusE song file structure changes - even for a small reason, bump these up! // To preserve user data please try to add song-loading converters to accompany any changes so no data is lost. // The user will be automatically informed and warned (via GUI) of loading an OLD or FUTURE song file, // and that the song might be converted if saved! // TODO: Always make this equal the MusE version? // But how do we bump up the MusE version safely before releasing, for testing/coding purposes? static const int _latestMinorVersion; // Latest known songfile minor version (as of this release) static const int _latestMajorVersion; // Latest known songfile major version (as of this release) int _minorVersion; // Currently loaded songfile minor version int _majorVersion; // Currently loaded songfile major version char c; // current char char lbuffer[512]; // When constructed with a const char* parameter, this will be valid. const char* bufptr; void next(); void nextc(); void token(int); void stoken(); QString strip(const QString& s); void putLevel(int n); public: enum Token {Error, TagStart, TagEnd, Flag, Proc, Text, Attribut, End}; int latestMajorVersion() const { return _latestMajorVersion; } int latestMinorVersion() const { return _latestMinorVersion; } int majorVersion() const { return _majorVersion; } int minorVersion() const { return _minorVersion; } bool isVersionEqualToLatest() const { return _majorVersion == _latestMajorVersion && _minorVersion == _latestMinorVersion; } bool isVersionLessThanLatest() const { return _majorVersion < _latestMajorVersion || (_majorVersion == _latestMajorVersion && _minorVersion < _latestMinorVersion); } bool isVersionGreaterThanLatest() const { return _majorVersion > _latestMajorVersion || (_majorVersion == _latestMajorVersion && _minorVersion > _latestMinorVersion); } bool isVersionEqualTo(int major, int minor) const { return _majorVersion == major && _minorVersion == minor; } bool isVersionLessThan(int major, int minor) const { return _majorVersion < major || (_majorVersion == major && _minorVersion < minor); } bool isVersionGreaterThan(int major, int minor) const { return _majorVersion > major || (_majorVersion == major && _minorVersion > minor); } void setVersion(int maj, int min) { _minorVersion = min; _majorVersion = maj; } // For writing and reading a FILE. Constructs an xml from a FILE. Xml(FILE*); // For reading from a char array only. Writing may cause error. Constructs an xml from a char array. Xml(const char*); // For writing to a QString only. Reading may cause error. Constructs an xml from a QString. Xml(QString*); // For writing and reading a QIODevice. Constructs an xml from a QIODevice. Xml(QIODevice*); Token parse(); QString parse(const QString&); QString parse1(); int parseInt(); long int parseLongInt(); unsigned int parseUInt(); unsigned long int parseLongUInt(); long long parseLongLong(); unsigned long long parseULongLong(); float parseFloat(); double parseDouble(); void unknown(const char*); int line() const { return _line; } // current line int col() const { return _col; } // current col const QString& s1() { return _s1; } const QString& s2() { return _s2; } void dump(QString &dump); void header(); // Returns level incremented by one. int putFileVersion(int level); void put(const char* format, ...); void put(int level, const char* format, ...); void nput(int level, const char* format, ...); void nput(const char* format, ...); void tag(int level, const char* format, ...); void etag(int level, const char* format, ...); void intTag(int level, const char* const name, int val); void longIntTag(int level, const char* const name, long int val); void uintTag(int level, const char* const name, unsigned int val); void longUintTag(int level, const char* const name, unsigned long int val); void longLongTag(int level, const char* const name, long long val); void uLongLongTag(int level, const char* const name, unsigned long long val); void doubleTag(int level, const char* const name, double val); void floatTag(int level, const char* const name, float val); void strTag(int level, const char* const name, const char* val); void strTag(int level, const char* const name, const QString& s); void strTag(int level, const QString& name, const QString& val); void colorTag(int level, const char* name, const QColor& color); void colorTag(int level, const QString& name, const QColor& color); void geometryTag(int level, const char* name, const QWidget* g); void qrectTag(int level, const char* name, const QRect& r); static QString xmlString(const QString&); static QString xmlString(const char*); void skip(const QString& tag); }; //--------------------------------------------------------- // Basic functions: //--------------------------------------------------------- QRect readGeometry(Xml& xml, const QString& name); QColor readColor(Xml& xml); } // namespace MusECore #endif muse-4.0.0/src/man/000077500000000000000000000000001404105311000140135ustar00rootroot00000000000000muse-4.0.0/src/man/CMakeLists.txt000066400000000000000000000025131404105311000165540ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= # # produce the manpages # configure_file ( muse.1.in ${PROJECT_BINARY_DIR}/man/${MusE_EXEC_NAME}.1 ) configure_file ( grepmidi.1.in ${PROJECT_BINARY_DIR}/man/grepmidi.1 ) install( FILES ${PROJECT_BINARY_DIR}/man/${MusE_EXEC_NAME}.1 ${PROJECT_BINARY_DIR}/man/grepmidi.1 DESTINATION ${SHARE_INSTALL_PREFIX}/man/man1/ ) muse-4.0.0/src/man/grepmidi.1.in000066400000000000000000000036031404105311000163040ustar00rootroot00000000000000.\" grepmidi -- display structure of MIDI files .\" Copyright (c) 2006 Daniel Kobras .\" .\" This manual page 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; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA .\" .\" This manual page was written especially for Debian GNU/Linux. .\" .TH GREPMIDI 1 "July 2006" .SH NAME grepmidi \- display structure of MIDI files .SH SYNOPSIS \fBgrepmidi\fR [ \fI-f\fR ] \fIfile\fR [ \fI...\fR ] .SH DESCRIPTION The \fBgrepmidi\fR utility provides a simple parser for files in raw MIDI format. It is far from complete, but allows to get a grip of the basic song structure in \fIfile\fR. That is, it does not display single note, but only meta information embedded in the MIDI file. \fBgrepmidi\fR can also be useful to check whether a given MIDI file contains errors. .PP More than one \fIfile\fR can be given on the commandline. Option \fB-f\fR tells \fBgrepmidi\fR to prepend each line of output with the filename that is currently checked. .PP \fBgrepmidi\fR is distributed along with \fBMusE\fR, a full-fledged MIDI Music Editor. .SH "SEE ALSO" .BR ${MusE_EXEC_NAME} (1). .SH AUTHORS .B grepmidi was written by Werner Schweer . .PP This manual page was written by Daniel Kobras for the Debian GNU/Linux system (but may be used by others). muse-4.0.0/src/man/muse.1.in000066400000000000000000000056751404105311000154700ustar00rootroot00000000000000.\" MusE -- MIDI/Audio Sequencer .\" Copyright (c) 2006 Daniel Kobras .\" Copyright (c) 2010 Orcan Ogetbil .\" .\" This manual page 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; if not, write to the Free Software .\" Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA .\" .\" This manual page was written especially for Debian but may be used by .\" others of course. .\" .TH MUSE 1 "December 2010" .SH NAME MusE \- MIDI/Audio Sequencer .SH SYNOPSIS .B ${MusE_EXEC_NAME} [ \fIoptions\fR ] [ \fIfile\fR ] .SH DESCRIPTION .B MusE is a MIDI/Audio sequencer that can also record and edit sound files, apply effects and filters from an intuitive graphical interface. It is based on the Linux-specific ALSA interface for audio i/o. The Jack Audio Connection Kit is used for routing. LADSPA-compliant plugins allow to extend the list of available effects. .PP The optional .I file argument denotes a project file in MusE-internal format that is loaded on startup. Project files can also be loaded later on through the graphical menu. .SH OPTIONS .TP .B -a Use a dummy audio backend instead of real audio i/o. .TP .B -d Start in debugging mode without real-time threads. .TP .B -D Enable verbose debugging messages. .TP .B -h Display usage information. .TP .B -I Do not attempt to load any DSSI plugins. .TP .B -l \fIxx\fR Force locale to the language/country code xx. .TP .B -L Do not se LASH. .TP .B -m Provide debugging messages about midi input events. .TP .B -M Provide debugging messages about midi output events. .TP .B -p Do not attempt to load any LADSPA plugins. .TP .B -P \fIn\fR Set scheduling priority of real-time threads to \fIn\fR (Dummy only, default 40. Else fixed by Jack.). .TP .B -s Provide debugging messages about sync events. .TP .B -v Print version information. .TP .B -V Do not attempt to load any VST plugins. .TP .B -y Enable Python control support. .TP .B -Y \fIn\fR Force midi real time priority to n. .SH "SEE ALSO" .B MusE provides an integrated help system in the graphical user interface. .SH AUTHORS .B MusE was written by Werner Schweer, Robert Jonnson, Mathias Lundgren, Frank Neumann, Joachim Schiele, Tim Donnelly, Nil Geisweiller, Orcan Ogetbil and others. .PP This manual page was originally written by Daniel Kobras for the Debian GNU/Linux system (but may be used by others). It was updated later by Orcan Ogetbil for MusE2. muse-4.0.0/src/muse/000077500000000000000000000000001404105311000142115ustar00rootroot00000000000000muse-4.0.0/src/muse/CMakeLists.txt000066400000000000000000000230331404105311000167520ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) set (SubDirs widgets mplugins components arranger cliplist ctrl driver function_dialogs instruments liste marker master midiedit mixer waveedit ) if(LV2_SUPPORT) if(HAVE_GTK2) set ( SubDirs ${SubDirs} lv2Gtk2Support ) endif(HAVE_GTK2) endif(LV2_SUPPORT) if (PYTHON_SUPPORT) set ( REM_LIB remote ) set ( SubDirs ${SubDirs} remote ) endif (PYTHON_SUPPORT) # subdirs (${SubDirs}) ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( muse_moc_headers app.h cobject.h conf.h midieditor.h midi_editor_layout.h plugin.h rasterizer.h scripts.h song.h transport.h value.h steprec.h vst_native.h ) QT5_WRAP_CPP ( wavepreview_moc_headers wavepreview.h ) ## ## Resource files ## QT5_ADD_RESOURCES (muse_qrc_files # muse.qrc ../resources.qrc ) file (GLOB core_source_files app.cpp audio.cpp audio_fifo.cpp audioprefetch.cpp audiotrack.cpp cobject.cpp conf.cpp controlfifo.cpp ctrl.cpp dialogs.cpp dssihost.cpp event.cpp eventlist.cpp event_tag_list.cpp exportmidi.cpp functions.cpp gconfig.cpp globals.cpp help.cpp helper.cpp importmidi.cpp key.cpp keyevent.cpp metronome_class.cpp midi.cpp midictrl.cpp mididev.cpp midieditor.cpp midi_editor_layout.cpp midievent.cpp midifile.cpp midiport.cpp midiseq.cpp mtc.cpp name_factory.cpp node.cpp operations.cpp osc.cpp part.cpp plugin.cpp pluglist.cpp pos.cpp rasterizer.cpp route.cpp scripts.cpp seqmsg.cpp shortcuts.cpp sig.cpp song.cpp songfile.cpp songfile_discovery.cpp stringparam.cpp sync.cpp synth.cpp tempo.cpp thread.cpp ticksynth.cpp track.cpp transport.cpp transport_obj.cpp undo.cpp value.cpp vst.cpp vst_native.cpp wave_helper.cpp waveevent.cpp wavetrack.cpp steprec.cpp ) file (GLOB main_source_files main.cpp ) file (GLOB icons_source_files icons.cpp ) file (GLOB latency_compensator_source_files latency_info.cpp latency_compensator.cpp ) file (GLOB wavepreview_source_files wavepreview.cpp ) ## ## Define target ## add_library ( latency_compensator_module SHARED ${latency_compensator_source_files} ) add_library ( wavepreview_module SHARED ${wavepreview_moc_headers} ${wavepreview_source_files} ) add_library ( core ${MODULES_BUILD} ${muse_moc_headers} ${core_source_files} ) add_executable ( muse ${main_source_files} ${muse_qrc_files} ) add_library ( icons ${MODULES_BUILD} ${icons_source_files} ) set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${main_source_files} ${core_source_files} CACHE INTERNAL "" ) ## ## Directories that will be passed to the compiler with -I flag ## include_directories ( arranger cliplist components driver function_dialogs instruments liste midiedit mixer mplugins remote waveedit widgets lv2Support ) # Make sure to include the uic generated headers paths. include_directories( ${CMAKE_CURRENT_BINARY_DIR}/arranger ${CMAKE_CURRENT_BINARY_DIR}/cliplist ${CMAKE_CURRENT_BINARY_DIR}/driver ${CMAKE_CURRENT_BINARY_DIR}/instruments ${CMAKE_CURRENT_BINARY_DIR}/liste ${CMAKE_CURRENT_BINARY_DIR}/midiedit ${CMAKE_CURRENT_BINARY_DIR}/mixer ${CMAKE_CURRENT_BINARY_DIR}/mplugins ${CMAKE_CURRENT_BINARY_DIR}/remote ${CMAKE_CURRENT_BINARY_DIR}/waveedit ${CMAKE_CURRENT_BINARY_DIR}/widgets ${CMAKE_CURRENT_BINARY_DIR}/components ${CMAKE_CURRENT_BINARY_DIR}/function_dialogs ) ## ## Compilation flags and target name ## set_target_properties( core PROPERTIES COMPILE_FLAGS "${VST_SDK_QUIRK}" OUTPUT_NAME muse_core ) set_target_properties( muse PROPERTIES OUTPUT_NAME ${MusE_EXEC_NAME} ) set_target_properties( icons PROPERTIES OUTPUT_NAME muse_icons ) set_target_properties( latency_compensator_module PROPERTIES OUTPUT_NAME muse_latency_compensator_module ) set_target_properties( wavepreview_module PROPERTIES OUTPUT_NAME muse_wavepreview_module ) ## ## Linkage ## target_link_libraries(wavepreview_module ${QT_LIBRARIES} ${SNDFILE_LIBRARIES} ${SAMPLERATE_LIBRARIES} ) target_link_libraries(core al arranger audio_converter_plugin cliplist ctrl driver evdata_module icons instruments latency_compensator_module liste marker master memory_module midiedit midi_controller_module mixer mpevent_module mplugins muse_string plugin_scan_module plugin_cache_writer_module Threads::Threads synti sysex_helper_module time_stretch_module wave_module waveedit wavepreview_module widgets components widgets_functiondialogs xml_module ${QT_LIBRARIES} ## Bring in the dssi alsa compatibility libraries if required ${DSSI_ALSA_COMPAT_LIBRARIES} ${SNDFILE_LIBRARIES} # Note that this also appears in the audio_convert/src_resampler shared library. ${SAMPLERATE_LIBRARIES} ${REM_LIB} ${FST_LIB} dl ) if(HAVE_LASH) target_link_libraries(core ${LASH_LIBRARIES}) endif(HAVE_LASH) if(OSC_SUPPORT) target_link_libraries(core ${LIBLO_LIBRARIES}) endif(OSC_SUPPORT) if(MIDNAM_SUPPORT) target_link_libraries(core midnam_module) endif(MIDNAM_SUPPORT) # Certain warnings in lv2 headers are causing errors with recent gcc. # It seems there's no way to suppress -Werror from within code. # The slightest warning in some header file can now trigger an error. # Gcc help warns that only some warnings can be suppressed. # 'pragma GCC diagnostic ignored "-Wparentheses"' didn't work. # Looks like no choice but to make the lv2host a module with separate flags. # if(LV2_SUPPORT) ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( lv2host_moc_headers lv2host.h ) file (GLOB lv2host_source_files lv2host.cpp ) add_library ( lv2host_module ${MODULES_BUILD} ${lv2host_moc_headers} ${lv2host_source_files} ) set_target_properties( lv2host_module # Avoid treating warnings as errors here, unlike the rest of the app. PROPERTIES COMPILE_FLAGS "-Wno-error" OUTPUT_NAME muse_lv2host_module ) target_link_libraries(lv2host_module ${LILV_LIBRARIES}) target_link_libraries(lv2host_module ${SORD_LIBRARIES}) target_link_libraries(lv2host_module ${LV2_LIBRARIES}) if(HAVE_GTK2) target_link_libraries(lv2host_module lv2_gtk2_support) endif(HAVE_GTK2) target_link_libraries(lv2host_module ${QT_LIBRARIES}) if(MIDNAM_SUPPORT) target_link_libraries(lv2host_module midnam_module) endif(MIDNAM_SUPPORT) target_link_libraries(lv2host_module midi_controller_module) target_link_libraries(core lv2host_module) endif(LV2_SUPPORT) target_link_libraries(muse midiedit core ) target_link_libraries(icons ${QT_LIBRARIES} ) ## ## Install location ## install( TARGETS muse RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/ ) install(TARGETS latency_compensator_module wavepreview_module DESTINATION ${MusE_MODULES_DIR} ) if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS core icons # latency_compensator_module DESTINATION ${MusE_MODULES_DIR} ) # This conditional fixes an error when LV2_SUPPORT is false: # "install TARGETS given target "lv2host_module" which does not exist." if (LV2_SUPPORT) install ( TARGETS lv2host_module DESTINATION ${MusE_MODULES_DIR} ) endif (LV2_SUPPORT) endif ( ${MODULES_BUILD} STREQUAL SHARED ) foreach(subdir ${SubDirs}) # MESSAGE( STATUS "Adding subdir " ${subdir} ) ADD_SUBDIRECTORY(${subdir}) endforeach() muse-4.0.0/src/muse/app.cpp000066400000000000000000005335301404105311000155060ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: app.cpp,v 1.113.2.68 2009/12/21 14:51:51 spamatica Exp $ // // (C) Copyright 1999-2011 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= 0x050b00 #include #endif #include #include #include #include #include #include "app.h" #include "master/lmaster.h" #include "al/dsp.h" #include "audio.h" #include "audiodev.h" #include "audioprefetch.h" // FIXME Move cliplist into components ? #include "cliplist/cliplist.h" #include "debug.h" #include "components/didyouknow.h" #include "drumedit.h" #include "components/filedialog.h" #include "gconfig.h" #include "globals.h" #include "gui.h" #include "helper.h" #include "wave_helper.h" #include "icons.h" #include "listedit.h" #include "master/masteredit.h" #include "midiseq.h" #include "mitplugin.h" #include "mittranspose.h" #include "components/mixdowndialog.h" #include "mrconfig.h" #include "pianoroll.h" #ifdef PYTHON_SUPPORT #include "remote/pyapi.h" #endif #include "song.h" #include "components/routepopup.h" #include "components/savenewrevisiondialog.h" #include "components/songinfo.h" #include "ticksynth.h" #include "tempo.h" #include "tlist.h" #include "waveedit.h" #include "components/projectcreateimpl.h" #include "widgets/menutitleitem.h" #include "components/unusedwavefiles.h" #include "functions.h" #include "components/songpos_toolbar.h" #include "components/sig_tempo_toolbar.h" #include "songfile_discovery.h" #include "pos.h" #include "wave.h" #include "wavepreview.h" #include "shortcuts.h" #include "rectoolbar.h" #include "postoolbar.h" #include "synctoolbar.h" #ifdef _WIN32 #include #endif // Forwards from header: #include #include #include #include #include #include #include #include #include "track.h" #include "minstrument.h" #include "midiport.h" #include "part.h" #include "synth.h" #include "undo.h" #include "appearance.h" #include "arranger.h" #include "arrangerview.h" #include "amixer.h" #include "bigtime.h" //#include "cliplist.h" #include "editinstrument.h" #include "tools.h" #include "genset.h" #include "mrconfig.h" #include "marker/markerview.h" #include "metronome.h" #include "conf.h" #include "midifilterimpl.h" #include "midiitransform.h" #include "miditransform.h" #include "midisyncimpl.h" #include "scoreedit.h" #include "shortcutconfig.h" #include "transport.h" #include "visibletracks.h" #include "routedialog.h" #include "cpu_toolbar.h" #include "musemdiarea.h" #include "snooper.h" #include "xml.h" #ifdef BUILD_EXPERIMENTAL #include "rhythm.h" #endif namespace MusECore { extern void exitJackAudio(); extern void exitDummyAudio(); extern void exitOSC(); extern void exitMidiAlsa(); #ifdef HAVE_RTAUDIO extern void exitRtAudio(); #endif } namespace MusEGui { extern void deleteIcons(); static pthread_t watchdogThread; //ErrorHandler *error; QStringList projectRecentList; #ifdef HAVE_LASH #include lash_client_t * lash_client = 0; #endif /* HAVE_LASH */ int watchAudioPrefetch, watchMidi; pthread_t splashThread; //--------------------------------------------------------- // sleep function //--------------------------------------------------------- void microSleep(long msleep) { int sleepOk=-1; while(sleepOk==-1) sleepOk=usleep(msleep); } //--------------------------------------------------------- // seqStart //--------------------------------------------------------- bool MusE::seqStart() { if(MusEGlobal::audio) { if(!MusEGlobal::audio->isRunning()) { // Start the audio. (Re)connect audio inputs and outputs. Force-fill the audio pre-fetch buffers for the current cpos. if(MusEGlobal::audio->start()) { // // wait for jack callback // for(int i = 0; i < 60; ++i) { if(MusEGlobal::audio->isRunning()) break; sleep(1); } if(!MusEGlobal::audio->isRunning()) { QMessageBox::critical( MusEGlobal::muse, tr("Failed to start audio!"), tr("Timeout waiting for audio to run. Check if jack is running or try another driver.\n")); } } else { QMessageBox::critical( MusEGlobal::muse, tr("Failed to start audio!"), tr("Was not able to start audio, check if jack is running or try another driver.\n")); } } } else fprintf(stderr, "seqStart(): audio is NULL\n"); // Now it is safe to ask the driver for realtime priority int pfprio = 0; // TODO: Hm why is prefetch priority so high again? Was it to overcome some race problems? Should it be lowest - disk thread? if(MusEGlobal::audioDevice) { MusEGlobal::realTimePriority = MusEGlobal::audioDevice->realtimePriority(); if(MusEGlobal::debugMsg) fprintf(stderr, "MusE::seqStart: getting audio driver MusEGlobal::realTimePriority:%d\n", MusEGlobal::realTimePriority); // NOTE: MusEGlobal::realTimeScheduling can be true (gotten using jack_is_realtime()), // while the determined MusEGlobal::realTimePriority can be 0. // MusEGlobal::realTimePriority is gotten using pthread_getschedparam() on the client thread // in JackAudioDevice::realtimePriority() which is a bit flawed - it reports there's no RT... if(MusEGlobal::realTimeScheduling) { if(MusEGlobal::realTimePriority - 5 >= 0) pfprio = MusEGlobal::realTimePriority - 5; } // FIXME: The realTimePriority of the Jack thread seems to always be 5 less than the value passed to jackd command. } else fprintf(stderr, "seqStart(): audioDevice is NULL\n"); if(MusEGlobal::audioPrefetch) { if(!MusEGlobal::audioPrefetch->isRunning()) { MusEGlobal::audioPrefetch->start(pfprio); // In case prefetch is not filled, do it now. MusEGlobal::audioPrefetch->msgSeek(MusEGlobal::audio->pos().frame(), true); // Force it upon startup only. } } else fprintf(stderr, "seqStart(): audioPrefetch is NULL\n"); if(MusEGlobal::midiSeq) MusEGlobal::midiSeq->start(0); // Prio unused, set in start. return true; } //--------------------------------------------------------- // stop //--------------------------------------------------------- void MusE::seqStop() { // label sequencer as disabled before it actually happened to minimize race condition MusEGlobal::midiSeqRunning = false; MusEGlobal::song->setStop(true); MusEGlobal::song->setStopPlay(false); if(MusEGlobal::midiSeq) MusEGlobal::midiSeq->stop(true); MusEGlobal::audio->stop(true); MusEGlobal::audioPrefetch->stop(true); if (MusEGlobal::realTimeScheduling && watchdogThread) pthread_cancel(watchdogThread); } //--------------------------------------------------------- // seqRestart //--------------------------------------------------------- bool MusE::seqRestart() { bool restartSequencer = MusEGlobal::audio->isRunning(); if (restartSequencer) { if (MusEGlobal::audio->isPlaying()) { MusEGlobal::audio->msgPlay(false); while (MusEGlobal::audio->isPlaying()) qApp->processEvents(); } seqStop(); } if(!seqStart()) return false; MusEGlobal::audioDevice->graphChanged(); return true; } void MusE::addProjectToRecentList(const QString& name) { if (projectRecentList.contains(name)) return; projectRecentList.push_front(name); if (projectRecentList.size() > MusEGlobal::config.recentListLength) projectRecentList.pop_back(); saveProjectRecentList(); } void MusE::saveProjectRecentList() { // save "Open Recent" list QString prjPath(MusEGlobal::configPath); prjPath += "/projects"; QFile f(prjPath); f.open(QIODevice::WriteOnly | QIODevice::Text); if (f.exists()) { QTextStream out(&f); for (int i = 0; i < projectRecentList.size(); ++i) { out << projectRecentList[i] << "\n"; } } } //--------------------------------------------------------- // MusE //--------------------------------------------------------- MusE::MusE() : QMainWindow() { setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize)); setFocusPolicy(Qt::NoFocus); MusEGlobal::muse = this; // hack _isRestartingApp = false; midiSyncConfig = nullptr; midiRemoteConfig = nullptr; midiPortConfig = nullptr; metronomeConfig = nullptr; midiFileConfig = nullptr; midiFilterConfig = nullptr; midiInputTransform = nullptr; #ifdef BUILD_EXPERIMENTAL midiRhythmGenerator = nullptr; #endif globalSettingsConfig = nullptr; arrangerView = nullptr; softSynthesizerConfig = nullptr; midiTransformerDialog = nullptr; shortcutConfig = nullptr; appearance = nullptr; _snooperDialog = nullptr; //audioMixer = 0; mixer1 = nullptr; mixer2 = nullptr; masterEditor = nullptr; routeDialog = nullptr; watchdogThread = 0; editInstrument = nullptr; //routingPopupMenu = 0; progress = nullptr; saveIncrement = 0; activeTopWin = nullptr; currentMenuSharingTopwin = nullptr; waitingForTopwin = nullptr; _lastProjectWasTemplate = false; _lastProjectLoadedConfig = true; appName = PACKAGE_NAME; setWindowTitle(appName); setWindowIcon(*MusEGui::museIcon); MusEGlobal::globalRasterizer = new Rasterizer(MusEGlobal::config.division, this); MusEGlobal::song = new MusECore::Song("song"); MusEGlobal::song->blockSignals(true); MusEGlobal::heartBeatTimer = new QTimer(this); MusEGlobal::heartBeatTimer->setObjectName("timer"); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), MusEGlobal::song, SLOT(beat())); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); connect(this, SIGNAL(activeTopWinChanged(MusEGui::TopWin*)), SLOT(activeTopWinChangedSlot(MusEGui::TopWin*))); connect(MusEGlobal::song, SIGNAL(sigDirty()), this, SLOT(setDirty())); blinkTimer = new QTimer(this); blinkTimer->setObjectName("blinkTimer"); connect(blinkTimer, SIGNAL(timeout()), SLOT(blinkTimerSlot())); blinkTimer->start( 250 ); // Every quarter second, for a flash rate of 2 Hz. saveTimer = new QTimer(this); connect(saveTimer, SIGNAL(timeout()), this, SLOT(saveTimerSlot())); saveTimer->start( 60 * 1000 ); // every minute messagePollTimer = new QTimer(this); messagePollTimer->setObjectName("messagePollTimer"); connect(messagePollTimer, SIGNAL(timeout()), SLOT(messagePollTimerSlot())); // A zero-millisecond poll timer. Oops, no can't do that in the gui thread, // it spikes the CPU usage because it eats up all the idle time. Use say, 50Hz 20msec. messagePollTimer->start(20); //init cpuload stuff clock_gettime(CLOCK_REALTIME, &lastSysTime); lastCpuTime.tv_sec = 0; lastCpuTime.tv_usec = 0; fAvrCpuLoad = 0.0f; avrCpuLoadCounter = 0; fCurCpuLoad = 0.0f; #ifdef PYTHON_SUPPORT //--------------------------------------------------- // Python bridge //--------------------------------------------------- // Uncomment in order to enable MusE Python bridge: if (MusEGlobal::usePythonBridge) { fprintf(stderr, "Initializing python bridge!\n"); if (startPythonBridge() == false) { fprintf(stderr, "Could not initialize Python bridge\n"); exit(1); } } #endif setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea); setCorner(Qt::TopRightCorner, Qt::RightDockWidgetArea); markerDock = new QDockWidget("Markers", this); markerDock->setObjectName("markerDock"); // markerDock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea); markerView = new MusEGui::MarkerView(markerDock); markerDock->setWidget(markerView); addDockWidget(Qt::RightDockWidgetArea, markerDock); markerDock->hide(); masterListDock = new QDockWidget("Mastertrack List", this); masterListDock->setObjectName("masterListDock"); // listMasterDock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea); masterList = new MusEGui::LMaster(this); masterListDock->setWidget(masterList); addDockWidget(Qt::RightDockWidgetArea, masterListDock); masterListDock->hide(); clipListDock = new QDockWidget("Clip List", this); clipListDock->setObjectName("clipListDock"); // clipListDock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea); clipListEdit = new MusEGui::ClipListEdit(clipListDock); clipListDock->setWidget(clipListEdit); addDockWidget(Qt::RightDockWidgetArea, clipListDock); clipListDock->hide(); //--------------------------------------------------- // undo/redo //--------------------------------------------------- MusEGlobal::undoRedo = new QActionGroup(this); MusEGlobal::undoRedo->setExclusive(false); MusEGlobal::undoAction = new QAction(*MusEGui::undoSVGIcon, tr("Und&o"), MusEGlobal::undoRedo); MusEGlobal::redoAction = new QAction(*MusEGui::redoSVGIcon, tr("Re&do"), MusEGlobal::undoRedo); MusEGlobal::undoAction->setWhatsThis(tr("Undo last change to project")); MusEGlobal::redoAction->setWhatsThis(tr("Redo last undo")); MusEGlobal::undoAction->setEnabled(false); MusEGlobal::redoAction->setEnabled(false); connect(MusEGlobal::redoAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(redo())); connect(MusEGlobal::undoAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(undo())); //--------------------------------------------------- // Transport //--------------------------------------------------- MusEGlobal::transportAction = new QActionGroup(this); MusEGlobal::transportAction->setExclusive(false); MusEGlobal::loopAction = new QAction(*MusEGui::loopSVGIcon, tr("Loop"), MusEGlobal::transportAction); MusEGlobal::loopAction->setCheckable(true); MusEGlobal::loopAction->setWhatsThis(tr("Loop between left mark and right mark")); MusEGlobal::loopAction->setStatusTip(tr("Loop between left mark and right mark")); connect(MusEGlobal::loopAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setLoop(bool))); MusEGlobal::punchinAction = new QAction(*MusEGui::punchinSVGIcon, tr("Punch in"), MusEGlobal::transportAction); MusEGlobal::punchinAction->setCheckable(true); MusEGlobal::punchinAction->setWhatsThis(tr("Record starts at left mark")); MusEGlobal::punchinAction->setStatusTip(tr("Recording starts at left mark")); connect(MusEGlobal::punchinAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setPunchin(bool))); MusEGlobal::punchoutAction = new QAction(*MusEGui::punchoutSVGIcon, tr("Punch out"), MusEGlobal::transportAction); MusEGlobal::punchoutAction->setCheckable(true); MusEGlobal::punchoutAction->setWhatsThis(tr("Record stops at right mark")); MusEGlobal::punchoutAction->setStatusTip(tr("Recording stops at right mark")); connect(MusEGlobal::punchoutAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setPunchout(bool))); QAction *tseparator = new QAction(this); tseparator->setSeparator(true); MusEGlobal::transportAction->addAction(tseparator); MusEGlobal::startAction = new QAction(*MusEGui::rewindToStartSVGIcon, tr("Start"), MusEGlobal::transportAction); MusEGlobal::startAction->setWhatsThis(tr("Rewind to start position")); connect(MusEGlobal::startAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(rewindStart())); MusEGlobal::rewindAction = new QAction(*MusEGui::rewindSVGIcon, tr("Rewind"), MusEGlobal::transportAction); MusEGlobal::rewindAction->setWhatsThis(tr("Rewind current position")); connect(MusEGlobal::rewindAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(rewind())); MusEGlobal::forwardAction = new QAction(*MusEGui::fastForwardSVGIcon, tr("Forward"), MusEGlobal::transportAction); MusEGlobal::forwardAction->setWhatsThis(tr("Move current position")); connect(MusEGlobal::forwardAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(forward())); MusEGlobal::stopAction = new QAction(*MusEGui::stopSVGIcon, tr("Stop"), MusEGlobal::transportAction); MusEGlobal::stopAction->setCheckable(true); MusEGlobal::stopAction->setWhatsThis(tr("Stop sequencer")); MusEGlobal::stopAction->setChecked(true); connect(MusEGlobal::stopAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setStop(bool))); MusEGlobal::playAction = new QAction(*MusEGui::playSVGIcon, tr("Play") + " (" + shrtToStr(MusEGui::SHRT_PLAY_TOGGLE) + ")
"+ tr("Restart rec")+" (" + QKeySequence(MusEGui::shortcuts[MusEGui::SHRT_REC_RESTART].key).toString() + ")", MusEGlobal::transportAction); MusEGlobal::playAction->setCheckable(true); MusEGlobal::playAction->setWhatsThis(tr("Start sequencer play")); MusEGlobal::playAction->setChecked(false); connect(MusEGlobal::playAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setPlay(bool))); MusEGlobal::recordAction = new QAction(*MusEGui::recMasterSVGIcon, tr("Record"), MusEGlobal::transportAction); MusEGlobal::recordAction->setCheckable(true); MusEGlobal::recordAction->setWhatsThis(tr("To record press record and then play")); MusEGlobal::recordAction->setStatusTip(tr("To record press record and then play")); connect(MusEGlobal::recordAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setRecord(bool))); MusEGlobal::panicAction = new QAction(*MusEGui::panicSVGIcon, tr("Panic"), this); QMenu* panicPopupMenu = new QMenu(this); MusEGlobal::panicAction->setMenu(panicPopupMenu); MusEGlobal::panicAction->setObjectName("PanicButton"); // MusEGlobal::panicAction->setWhatsThis(tr("Send note off to all midi channels")); // wrong? MusEGlobal::panicAction->setStatusTip(tr("Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help.")); connect(MusEGlobal::panicAction, SIGNAL(triggered()), MusEGlobal::song, SLOT(panic())); MusEGlobal::metronomeAction = new QAction(*MusEGui::metronomeOnSVGIcon, tr("Metronome"), this); MusEGlobal::metronomeAction->setObjectName("MetronomeButton"); MusEGlobal::metronomeAction->setCheckable(true); MusEGlobal::metronomeAction->setWhatsThis(tr("Turn on/off metronome")); MusEGlobal::metronomeAction->setStatusTip(tr("Metronome on/off. Press F1 for help.")); MusEGlobal::metronomeAction->setChecked(MusEGlobal::song->click()); connect(MusEGlobal::metronomeAction, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setClick(bool))); connect(MusEGlobal::song, SIGNAL(clickChanged(bool)), MusEGlobal::metronomeAction, SLOT(setChecked(bool))); //----Actions //-------- File Actions fileNewAction = new QAction(*MusEGui::filenewSVGIcon, tr("&New"), this); fileNewAction->setToolTip(tr("Create new song")); fileNewAction->setWhatsThis(tr("Create new song")); fileNewFromTemplateAction = new QAction(*MusEGui::filetemplateSVGIcon, tr("New from &Template..."), this); fileNewFromTemplateAction->setToolTip(tr("Create new song from template")); fileNewFromTemplateAction->setWhatsThis(tr("Create new song from template")); fileOpenAction = new QAction(*MusEGui::fileopenSVGIcon, tr("&Open..."), this); fileOpenAction->setToolTip(tr("Open song from file")); fileOpenAction->setWhatsThis(tr("Click this button to open an existing song.")); openRecent = new QMenu(tr("Open &Recent"), this); fileSaveAction = new QAction(*MusEGui::filesaveSVGIcon, tr("&Save"), this); fileSaveAction->setToolTip(tr("Save current song")); fileSaveAction->setWhatsThis(tr("Click this button to save the song you are editing. You will be prompted for a file name.")); fileSaveAsAction = new QAction(*MusEGui::filesaveasSVGIcon, tr("Save &As..."), this); fileSaveAsNewProjectAction = new QAction(*MusEGui::filesaveProjectSVGIcon, tr("Save As New &Project..."), this); fileSaveRevisionAction = new QAction(*MusEGui::filesaveRevisionSVGIcon, tr("Save New Re&vision"), this); fileSaveAsTemplateAction = new QAction(*MusEGui::filesaveTemplateSVGIcon, tr("Save As Te&mplate..."), this); fileCloseAction = new QAction(*MusEGui::filecloseSVGIcon, tr("&Close"), this); fileImportMidiAction = new QAction(tr("Import Midi File..."), this); fileExportMidiAction = new QAction(tr("Export Midi File..."), this); fileImportPartAction = new QAction(tr("Import Part..."), this); fileImportWaveAction = new QAction(tr("Import Audio File..."), this); fileMoveWaveFiles = new QAction(tr("Find Unused Wave Files..."), this); quitAction = new QAction(*MusEGui::appexitSVGIcon, tr("&Quit"), this); editSongInfoAction = new QAction(tr("Edit Project Description..."), this); //-------- View Actions viewTransportAction = new QAction(*MusEGui::transportSVGIcon, tr("Transport Panel"), this); viewTransportAction->setCheckable(true); viewBigtimeAction = new QAction(*MusEGui::bigtimeSVGIcon, tr("Bigtime Window"), this); viewBigtimeAction->setCheckable(true); viewMixerAAction = new QAction(*MusEGui::mixerSVGIcon, tr("Mixer A"), this); viewMixerAAction->setCheckable(true); viewMixerBAction = new QAction(*MusEGui::mixerSVGIcon, tr("Mixer B"), this); viewMixerBAction->setCheckable(true); viewMarkerAction = markerDock->toggleViewAction(); viewCliplistAction = clipListDock->toggleViewAction(); toggleDocksAction = new QAction(tr("Show Docks"), this); toggleDocksAction->setCheckable(true); toggleDocksAction->setChecked(true); toggleDocksAction->setStatusTip(tr("Toggle display of currently visible dock windows.")); fullscreenAction=new QAction(tr("Fullscreen"), this); fullscreenAction->setCheckable(true); fullscreenAction->setChecked(false); fullscreenAction->setStatusTip(tr("Display MusE main window in full screen mode.")); // QMenu* master = new QMenu(tr("Mastertrack"), this); // master->setIcon(QIcon(*edit_mastertrackIcon)); masterGraphicAction = new QAction(QIcon(*mastereditSVGIcon),tr("Mastertrack Graphic..."), this); masterListAction = masterListDock->toggleViewAction(); // masterListAction = new QAction(QIcon(*mastertrack_listIcon),tr("List..."), this); // master->addAction(masterGraphicAction); // master->addAction(masterListAction); //-------- Midi Actions midiEditInstAction = new QAction(*MusEGui::editInstrumentSVGIcon, tr("Edit Instrument..."), this); // midiInputPlugins = new QMenu(tr("Input Plugins"), this); midiTrpAction = new QAction(*MusEGui::midiInputTransposeSVGIcon, tr("Input Transpose..."), this); midiInputTrfAction = new QAction(*MusEGui::midiInputTransformSVGIcon, tr("Input Transform..."), this); midiInputFilterAction = new QAction(*MusEGui::midiInputFilterSVGIcon, tr("Input Filter..."), this); midiRemoteAction = new QAction(*MusEGui::midiInputRemoteSVGIcon, tr("Remote Control..."), this); #ifdef BUILD_EXPERIMENTAL midiRhythmAction = new QAction(QIcon(*midi_inputplugins_random_rhythm_generatorIcon), tr("Rhythm Generator"), this); #endif midiResetInstAction = new QAction(*MusEGui::midiResetSVGIcon, tr("Reset Instrument"), this); midiResetInstAction->setStatusTip(tr("Send 'note-off' command to all midi channels.")); midiInitInstActions = new QAction(*MusEGui::midiInitSVGIcon, tr("Init Instrument"), this); midiInitInstActions->setStatusTip(tr("Send initialization messages as found in instrument definition.")); midiLocalOffAction = new QAction(*MusEGui::midiLocalOffSVGIcon, tr("Local Off"), this); midiLocalOffAction->setStatusTip(tr("Send 'local-off' command to all midi channels.")); //-------- Audio Actions audioBounce2TrackAction = new QAction(*MusEGui::downmixTrackSVGIcon, tr("Render Downmix to Selected Wave Track"), this); audioBounce2FileAction = new QAction(*MusEGui::downmixOnSVGIcon, tr("Render Downmix to a File..."), this); audioRestartAction = new QAction(*MusEGui::restartSVGIcon, tr("Restart Audio"), this); //-------- Automation Actions // REMOVE Tim. automation. Removed. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. // autoMixerAction = new QAction(QIcon(*MusEGui::automation_mixerIcon), tr("Mixer Automation"), this); // autoMixerAction->setCheckable(true); autoSnapshotAction = new QAction(*MusEGui::snapshotSVGIcon, tr("Take Automation Snapshot"), this); autoClearAction = new QAction(*MusEGui::clearSVGIcon, tr("Clear Automation Data"), this); //-------- Settings Actions settingsGlobalAction = new QAction(*MusEGui::settingsSVGIcon, tr("Global Settings..."), this); settingsAppearanceAction = new QAction(*MusEGui::appearanceSVGIcon, tr("Appearance..."), this); settingsShortcutsAction = new QAction(*MusEGui::keySVGIcon, tr("Keyboard Shortcuts..."), this); follow = new QMenu(tr("Follow Song"), this); follow->setObjectName("CheckmarkOnly"); QActionGroup *followAG = new QActionGroup(this); followAG->setExclusive(true); dontFollowAction = new QAction(tr("Don't Follow Song"), followAG); dontFollowAction->setCheckable(true); followPageAction = new QAction(tr("Follow Page"), followAG); followPageAction->setCheckable(true); followCtsAction = new QAction(tr("Follow Continuous"), followAG); followCtsAction->setCheckable(true); followPageAction->setChecked(true); rewindOnStopAction=new QAction(tr("Rewind on Stop"), this); rewindOnStopAction->setCheckable(true); rewindOnStopAction->setChecked(MusEGlobal::config.useRewindOnStop); settingsMetronomeAction = new QAction(*MusEGui::metronomeOnSVGIcon, tr("Metronome..."), this); settingsMidiSyncAction = new QAction(*MusEGui::midiSyncSVGIcon, tr("Midi Sync..."), this); settingsMidiIOAction = new QAction(*MusEGui::midiExportImportSVGIcon, tr("Midi File Import/Export..."), this); settingsMidiPortAction = new QAction(*MusEGui::ankerSVGIcon, tr("Midi Ports/Soft Synths..."), this); //-------- Help Actions helpManualAction = new QAction(tr("&Manual (Wiki)..."), this); helpHomepageAction = new QAction(tr("MusE &Homepage..."), this); helpDidYouKnow = new QAction(tr("&Did You Know?"), this); helpReportAction = new QAction(tr("&Report Bug..."), this); helpAboutAction = new QAction(tr("&About MusE..."), this); helpSnooperAction = new QAction(tr("Snooper (Developer Tool)..."), this); //---- Connections //-------- File connections connect(fileNewAction, SIGNAL(triggered()), SLOT(loadDefaultTemplate())); connect(fileNewFromTemplateAction, SIGNAL(triggered()), SLOT(loadTemplate())); connect(fileOpenAction, SIGNAL(triggered()), SLOT(loadProject())); connect(openRecent, SIGNAL(aboutToShow()), SLOT(openRecentMenu())); connect(openRecent, SIGNAL(triggered(QAction*)), SLOT(selectProject(QAction*))); connect(fileSaveAction, SIGNAL(triggered()), SLOT(save())); connect(fileSaveAsAction, SIGNAL(triggered()), SLOT(saveAs())); connect(fileSaveAsNewProjectAction, SIGNAL(triggered()), SLOT(saveAsNewProject())); connect(fileSaveRevisionAction, SIGNAL(triggered()), SLOT(saveNewRevision())); connect(fileSaveAsTemplateAction, SIGNAL(triggered()), SLOT(saveAsTemplate())); connect(fileCloseAction, SIGNAL(triggered()), SLOT(fileClose())); connect(fileImportMidiAction, SIGNAL(triggered()), SLOT(importMidi())); connect(fileExportMidiAction, SIGNAL(triggered()), SLOT(exportMidi())); connect(fileImportPartAction, SIGNAL(triggered()), SLOT(importPart())); connect(fileImportWaveAction, SIGNAL(triggered()), SLOT(importWave())); connect(fileMoveWaveFiles, SIGNAL(triggered()), SLOT(findUnusedWaveFiles())); connect(quitAction, SIGNAL(triggered()), SLOT(quitDoc())); connect(editSongInfoAction, SIGNAL(triggered()), SLOT(startSongInfo())); //-------- View connections connect(viewTransportAction, SIGNAL(toggled(bool)), SLOT(toggleTransport(bool))); connect(viewBigtimeAction, SIGNAL(toggled(bool)), SLOT(toggleBigTime(bool))); connect(viewMixerAAction, SIGNAL(toggled(bool)),SLOT(toggleMixer1(bool))); connect(viewMixerBAction, SIGNAL(toggled(bool)), SLOT(toggleMixer2(bool))); // connect(viewArrangerAction, SIGNAL(toggled(bool)), SLOT(toggleArranger(bool))); connect(masterGraphicAction, SIGNAL(triggered()), SLOT(startMasterEditor())); // connect(masterListAction, SIGNAL(triggered()), SLOT(startLMasterEditor())); connect(toggleDocksAction, SIGNAL(toggled(bool)), SLOT(toggleDocks(bool))); connect(fullscreenAction, SIGNAL(toggled(bool)), SLOT(setFullscreen(bool))); //-------- Midi connections connect(midiEditInstAction, SIGNAL(triggered()), SLOT(startEditInstrument())); connect(midiResetInstAction, SIGNAL(triggered()), SLOT(resetMidiDevices())); connect(midiInitInstActions, SIGNAL(triggered()), SLOT(initMidiDevices())); connect(midiLocalOffAction, SIGNAL(triggered()), SLOT(localOff())); connect(midiTrpAction, &QAction::triggered, [this]() { startMidiInputPlugin(0); } ); connect(midiInputTrfAction, &QAction::triggered, [this]() { startMidiInputPlugin(1); } ); connect(midiInputFilterAction, &QAction::triggered, [this]() { startMidiInputPlugin(2); } ); connect(midiRemoteAction, &QAction::triggered, [this]() { startMidiInputPlugin(3); } ); #ifdef BUILD_EXPERIMENTAL connect(midiRhythmAction, &QAction::triggered, [this]() { startMidiInputPlugin(4); } ); #endif //-------- Audio connections connect(audioBounce2TrackAction, SIGNAL(triggered()), SLOT(bounceToTrack())); connect(audioBounce2FileAction, SIGNAL(triggered()), SLOT(bounceToFile())); connect(audioRestartAction, SIGNAL(triggered()), SLOT(seqRestart())); //-------- Automation connections // REMOVE Tim. automation. Removed. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. // connect(autoMixerAction, SIGNAL(triggered()), SLOT(switchMixerAutomation())); connect(autoSnapshotAction, SIGNAL(triggered()), SLOT(takeAutomationSnapshot())); connect(autoClearAction, SIGNAL(triggered()), SLOT(clearAutomation())); //-------- Settings connections connect(settingsGlobalAction, SIGNAL(triggered()), SLOT(configGlobalSettings())); connect(settingsShortcutsAction, SIGNAL(triggered()), SLOT(configShortCuts())); connect(settingsMetronomeAction, SIGNAL(triggered()), SLOT(configMetronome())); connect(settingsMidiSyncAction, SIGNAL(triggered()), SLOT(configMidiSync())); connect(settingsMidiIOAction, SIGNAL(triggered()), SLOT(configMidiFile())); connect(settingsAppearanceAction, SIGNAL(triggered()), SLOT(configAppearance())); connect(settingsMidiPortAction, SIGNAL(triggered()), SLOT(configMidiPorts())); connect(dontFollowAction, &QAction::triggered, [this]() { cmd(CMD_FOLLOW_NO); } ); connect(followPageAction, &QAction::triggered, [this]() { cmd(CMD_FOLLOW_JUMP); } ); connect(followCtsAction, &QAction::triggered, [this]() { cmd(CMD_FOLLOW_CONTINUOUS); } ); connect(rewindOnStopAction, SIGNAL(toggled(bool)), SLOT(toggleRewindOnStop(bool))); //-------- Help connections connect(helpManualAction, SIGNAL(triggered()), SLOT(startHelpBrowser())); connect(helpHomepageAction, SIGNAL(triggered()), SLOT(startHomepageBrowser())); connect(helpReportAction, SIGNAL(triggered()), SLOT(startBugBrowser())); connect(helpDidYouKnow, SIGNAL(triggered()), SLOT(showDidYouKnowDialog())); connect(helpAboutAction, SIGNAL(triggered()), SLOT(about())); connect(helpSnooperAction, &QAction::triggered, [this]() { startSnooper(); } ); //-------------------------------------------------- // Toolbar //-------------------------------------------------- // when adding a toolbar to the main window, remember adding it to // either the requiredToolbars or optionalToolbars list! // NOTICE: Please ensure that any tool bar object names here match the names // assigned in the 'toolbar' creation section of TopWin::TopWin(), // or any other TopWin class. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). tools = addToolBar(tr("File Buttons")); tools->setObjectName("File buttons"); tools->addAction(fileNewAction); tools->addAction(fileNewFromTemplateAction); tools->addAction(fileOpenAction); tools->addAction(fileSaveAction); QAction* whatsthis = QWhatsThis::createAction(this); whatsthis->setIcon(*whatsthisSVGIcon); tools->addAction(whatsthis); QToolBar* undo_tools = addToolBar(tr("Undo/Redo")); undo_tools->setObjectName("Undo/Redo"); undo_tools->addActions(MusEGlobal::undoRedo->actions()); QToolBar* panic_toolbar = addToolBar(tr("Panic")); panic_toolbar->setObjectName("Panic tool"); panic_toolbar->addAction(MusEGlobal::panicAction); QToolBar* metronome_toolbar = addToolBar(tr("Metronome")); metronome_toolbar->setObjectName("Metronome tool"); metronome_toolbar->addAction(MusEGlobal::metronomeAction); cpuLoadToolbar = new CpuToolbar(tr("Cpu Load"), this); addToolBar(cpuLoadToolbar); cpuLoadToolbar->hide(); // hide as a default, the info is now in status bar too connect(cpuLoadToolbar, SIGNAL(resetClicked()), SLOT(resetXrunsCounter())); QToolBar* songpos_tb = addToolBar(tr("Timeline")); songpos_tb->setObjectName("Timeline tool"); songpos_tb->addWidget(new MusEGui::SongPosToolbarWidget(songpos_tb)); songpos_tb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); songpos_tb->setContextMenuPolicy(Qt::PreventContextMenu); addToolBar(Qt::BottomToolBarArea, songpos_tb); QToolBar* transportToolbar = addToolBar(tr("Transport")); transportToolbar->setObjectName("Transport tool"); transportToolbar->addActions(MusEGlobal::transportAction->actions()); transportToolbar->setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize)); RecToolbar *recToolbar = new RecToolbar(tr("Recording"), this); addToolBar(recToolbar); SyncToolbar *syncToolbar = new SyncToolbar(tr("Sync"), this); addToolBar(syncToolbar); addToolBarBreak(); TempoToolbar* tempo_tb = new TempoToolbar(tr("Tempo"), this); addToolBar(tempo_tb); SigToolbar* sig_tb = new SigToolbar(tr("Signature"), this); addToolBar(sig_tb); PosToolbar *posToolbar = new PosToolbar(tr("Position"), this); addToolBar(posToolbar); requiredToolbars.push_back(tools); requiredToolbars.push_back(cpuLoadToolbar); optionalToolbars.push_back(undo_tools); optionalToolbars.push_back(panic_toolbar); optionalToolbars.push_back(metronome_toolbar); optionalToolbars.push_back(songpos_tb); optionalToolbars.push_back(nullptr); // Toolbar break optionalToolbars.push_back(transportToolbar); optionalToolbars.push_back(recToolbar); optionalToolbars.push_back(syncToolbar); optionalToolbars.push_back(posToolbar); optionalToolbars.push_back(tempo_tb); optionalToolbars.push_back(sig_tb); QSocketNotifier* ss = new QSocketNotifier(MusEGlobal::audio->getFromThreadFdr(), QSocketNotifier::Read, this); connect(ss, SIGNAL(activated(int)), MusEGlobal::song, SLOT(seqSignal(int))); //--------------------------------------------------- // Popups //--------------------------------------------------- // when adding a menu to the main window, remember adding it to // either the leadingMenus or trailingMenus list! // also do NOT use menuBar()->addMenu(QString&), but ALWAYS // create the menu with new QMenu and add it afterwards. // the menu's owner must be this and not this->menuBar()! //------------------------------------------------------------- // popup File //------------------------------------------------------------- menu_file = new QMenu(tr("&File"), this); menuBar()->addMenu(menu_file); leadingMenus.push_back(menu_file); menu_file->addAction(fileNewAction); menu_file->addAction(fileNewFromTemplateAction); menu_file->addAction(fileOpenAction); menu_file->addMenu(openRecent); menu_file->addSeparator(); menu_file->addAction(fileSaveAction); menu_file->addAction(fileSaveAsAction); menu_file->addAction(fileSaveRevisionAction); menu_file->addAction(fileSaveAsNewProjectAction); menu_file->addAction(fileSaveAsTemplateAction); menu_file->addSeparator(); menu_file->addAction(fileCloseAction); menu_file->addSeparator(); menu_file->addAction(editSongInfoAction); menu_file->addSeparator(); menu_file->addAction(fileImportMidiAction); menu_file->addAction(fileExportMidiAction); menu_file->addAction(fileImportPartAction); menu_file->addAction(fileImportWaveAction); menu_file->addSeparator(); menu_file->addAction(fileMoveWaveFiles); menu_file->addSeparator(); menu_file->addAction(quitAction); menu_file->addSeparator(); //------------------------------------------------------------- // popup View //------------------------------------------------------------- menuView = new QMenu(tr("&View"), this); menuBar()->addMenu(menuView); trailingMenus.push_back(menuView); menuView->addAction(viewTransportAction); menuView->addAction(viewBigtimeAction); menuView->addAction(viewMixerAAction); menuView->addAction(viewMixerBAction); menuView->addSeparator(); // menuView->addAction(viewArrangerAction); // menuView->addMenu(master); menuView->addAction(masterGraphicAction); menuView->addAction(masterListAction); menuView->addAction(viewMarkerAction); menuView->addAction(viewCliplistAction); menuView->addSeparator(); menuView->addAction(toggleDocksAction); menuView->addAction(fullscreenAction); //------------------------------------------------------------- // popup Midi //------------------------------------------------------------- menu_functions = new QMenu(tr("&Midi"), this); menuBar()->addMenu(menu_functions); trailingMenus.push_back(menu_functions); menu_functions->addAction(midiEditInstAction); menu_functions->addSeparator(); // menu_functions->addMenu(midiInputPlugins); menu_functions->addAction(midiTrpAction); menu_functions->addAction(midiInputTrfAction); menu_functions->addAction(midiInputFilterAction); menu_functions->addAction(midiRemoteAction); // midiInputPlugins->addAction(midiTrpAction); // midiInputPlugins->addAction(midiInputTrfAction); // midiInputPlugins->addAction(midiInputFilterAction); // midiInputPlugins->addAction(midiRemoteAction); #ifdef BUILD_EXPERIMENTAL midiInputPlugins->addAction(midiRhythmAction); #endif menu_functions->addSeparator(); menu_functions->addAction(midiResetInstAction); menu_functions->addAction(midiInitInstActions); menu_functions->addAction(midiLocalOffAction); panicPopupMenu->addAction(midiResetInstAction); panicPopupMenu->addAction(midiInitInstActions); panicPopupMenu->addAction(midiLocalOffAction); //------------------------------------------------------------- // popup Audio //------------------------------------------------------------- menu_audio = new QMenu(tr("&Audio"), this); menuBar()->addMenu(menu_audio); trailingMenus.push_back(menu_audio); menu_audio->addAction(audioBounce2TrackAction); menu_audio->addAction(audioBounce2FileAction); menu_audio->addSeparator(); menu_audio->addAction(audioRestartAction); menu_audio->addSeparator(); // REMOVE Tim. automation. Removed. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. // menu_audio->addAction(autoMixerAction); //menu_audio->addSeparator(); menu_audio->addAction(autoSnapshotAction); menu_audio->addAction(autoClearAction); //------------------------------------------------------------- // popup Windows //------------------------------------------------------------- menuWindows = new QMenu(tr("&Windows"), this); menuBar()->addMenu(menuWindows); trailingMenus.push_back(menuWindows); //------------------------------------------------------------- // popup Settings //------------------------------------------------------------- menuSettings = new QMenu(tr("Se&ttings"), this); menuBar()->addMenu(menuSettings); trailingMenus.push_back(menuSettings); menuSettings->addAction(settingsGlobalAction); menuSettings->addAction(settingsAppearanceAction); menuSettings->addAction(settingsShortcutsAction); menuSettings->addSeparator(); menuSettings->addMenu(follow); follow->addActions(followAG->actions()); menuSettings->addAction(rewindOnStopAction); menuSettings->addAction(settingsMetronomeAction); menuSettings->addSeparator(); menuSettings->addAction(settingsMidiSyncAction); menuSettings->addAction(settingsMidiIOAction); menuSettings->addAction(settingsMidiPortAction); //--------------------------------------------------- // popup Help //--------------------------------------------------- menu_help = new QMenu(tr("&Help"), this); menuBar()->addMenu(menu_help); trailingMenus.push_back(menu_help); menu_help->addAction(helpManualAction); menu_help->addAction(whatsthis); menu_help->addAction(helpHomepageAction); menu_help->addAction(helpDidYouKnow); menu_help->addSeparator(); menu_help->addAction(helpReportAction); menu_help->addAction(helpSnooperAction); menu_help->addSeparator(); menu_help->addAction(helpAboutAction); menu_help->addAction(tr("About &Qt..."), qApp, SLOT(aboutQt())); //--------------------------------------------------- // Central Widget //--------------------------------------------------- mdiArea=new MuseMdiArea(this); // mdiArea->setOption(QMdiArea::DontMaximizeSubWindowOnActivation); mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); mdiArea->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); mdiArea->setViewMode(QMdiArea::TabbedView); mdiArea->setTabsMovable(true); mdiArea->setTabPosition(QTabWidget::South); QTabBar* tb = mdiArea->findChild(); if (tb) { tb->setExpanding(false); // tb->setAutoHide(true); } // viewArrangerAction->setEnabled(false); setCentralWidget(mdiArea); arrangerView = new MusEGui::ArrangerView(this); // connect(arrangerView, SIGNAL(closed()), SLOT(arrangerClosed())); toplevels.push_back(arrangerView); // arrangerView->hide(); _arranger=arrangerView->getArranger(); connect(tempo_tb, SIGNAL(returnPressed()), arrangerView, SLOT(focusCanvas())); connect(tempo_tb, SIGNAL(escapePressed()), arrangerView, SLOT(focusCanvas())); connect(tempo_tb, SIGNAL(masterTrackChanged(bool)), MusEGlobal::song, SLOT(setMasterFlag(bool))); connect(sig_tb, SIGNAL(returnPressed()), arrangerView, SLOT(focusCanvas())); connect(sig_tb, SIGNAL(escapePressed()), arrangerView, SLOT(focusCanvas())); //--------------------------------------------------- // read list of "Recent Projects" //--------------------------------------------------- QString prjPath(MusEGlobal::configPath); prjPath += QString("/projects"); QFile projFile(prjPath); if (!projFile.open(QIODevice::ReadOnly | QIODevice::Text)) { perror("open projectfile"); projectRecentList.clear(); } else { for (int i = 0; i < MusEGlobal::config.recentListLength; ++i) { if (projFile.atEnd()) { break; } projectRecentList.append(projFile.readLine().simplified()); } } transport = new MusEGui::Transport(this, "transport"); bigtime = nullptr; MusEGlobal::song->blockSignals(false); if (MusEGlobal::config.geometryMain.size().width()) { resize(MusEGlobal::config.geometryMain.size()); move(MusEGlobal::config.geometryMain.topLeft()); } else centerAndResize(); setAndAdjustFonts(); MusEGlobal::song->update(); updateWindowMenu(); } //--------------------------------------------------------- // setAndAdjustFonts //--------------------------------------------------------- void MusE::setAndAdjustFonts() { ensurePolished(); MusEGlobal::config.fonts[0].setFamily(font().family()); MusEGlobal::config.fonts[0].setPointSize(font().pointSize()); MusEGlobal::config.fonts[0].setBold(font().bold()); MusEGlobal::config.fonts[0].setItalic(font().italic()); // init font family with system font (the original sans-serif default looked terrible on KDE) for (int i = 1; i < NUM_FONTS; i++) { if (MusEGlobal::config.fonts[i].family().isEmpty()) MusEGlobal::config.fonts[i].setFamily(font().family()); } if (MusEGlobal::config.autoAdjustFontSize) { int fs = font().pointSize(); MusEGlobal::config.fonts[1].setPointSize(qRound(fs * MusEGlobal::FntFac::F1)); MusEGlobal::config.fonts[2].setPointSize(qRound(fs * MusEGlobal::FntFac::F2)); MusEGlobal::config.fonts[3].setPointSize(qRound(fs * MusEGlobal::FntFac::F3)); MusEGlobal::config.fonts[4].setPointSize(qRound(fs * MusEGlobal::FntFac::F4)); MusEGlobal::config.fonts[5].setPointSize(qRound(fs * MusEGlobal::FntFac::F5)); MusEGlobal::config.fonts[6].setPointSize(qRound(fs * MusEGlobal::FntFac::F6)); } } //--------------------------------------------------------- // centerAndResize //--------------------------------------------------------- void MusE::centerAndResize() { // set sensible initial sizes/positions for mainwin/transport (kybos) // Class QDesktopWidget deprecated as of Qt 5.11 #if QT_VERSION >= 0x050b00 const QRect screenRect = qApp->primaryScreen()->availableGeometry(); #else const QRect screenRect = qApp->desktop()->availableGeometry(); #endif const QSize screenSize = screenRect.size(); int width = screenSize.width(); int height = screenSize.height(); width *= 0.9; // 90% of the screen size height *= 0.9; // 90% of the screen size const QSize newSize( width, height ); setGeometry( QStyle::alignedRect( Qt::LeftToRight, Qt::AlignCenter, newSize, screenRect ) ); MusEGlobal::config.geometryMain = geometry(); if (MusEGlobal::config.transportVisible) { QRect r( geometry().x() + (width / 2), geometry().y() + (height / 10), 0, 0); MusEGlobal::config.geometryTransport = r; // don't position the window here, it's done when file/template is loaded } } //--------------------------------------------------------- // setHeartBeat //--------------------------------------------------------- void MusE::setHeartBeat() { if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: STARTING Heartbeat timer\n"); MusEGlobal::heartBeatTimer->start(1000/MusEGlobal::config.guiRefresh); } void MusE::stopHeartBeat() { if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: STOPPING Heartbeat timer\n"); MusEGlobal::heartBeatTimer->stop(); } void MusE::heartBeat() { if (cpuLoadToolbar->isVisible()) cpuLoadToolbar->setValues(MusEGlobal::song->cpuLoad(), MusEGlobal::song->dspLoad(), MusEGlobal::song->xRunsCount()); if (statusBar()->isVisible()) cpuStatusBar->setValues(MusEGlobal::song->cpuLoad(), MusEGlobal::song->dspLoad(), MusEGlobal::song->xRunsCount()); } void MusE::populateAddTrack() { arrangerView->populateAddTrack(); arrangerView->updateShortcuts(); } void MusE::blinkTimerSlot() { MusEGlobal::blinkTimerPhase = !MusEGlobal::blinkTimerPhase; emit blinkTimerToggled(MusEGlobal::blinkTimerPhase); } void MusE::messagePollTimerSlot() { if(MusEGlobal::song) MusEGlobal::song->processIpcInEventBuffers(); } //--------------------------------------------------------- // setDirty //--------------------------------------------------------- void MusE::setDirty() { MusEGlobal::song->dirty = true; setWindowTitle(projectTitle(project.absoluteFilePath()) + " "); } //--------------------------------------------------- // loadDefaultSong // if no songname entered on command line: // startMode: 0 - load last song // 1 - load default template // 2 - load configured start song //--------------------------------------------------- void MusE::loadDefaultSong(const QString& filename_override, bool use_template, bool load_config) { QString name; bool useTemplate = false; bool loadConfig = true; if (!filename_override.isEmpty()) { name = filename_override; useTemplate = use_template; loadConfig = load_config; } else if (MusEGlobal::config.startMode == 0) { name = !projectRecentList.isEmpty() ? projectRecentList.first() : MusEGui::getUniqueUntitledName(); fprintf(stderr, "starting with last song %s\n", name.toLatin1().constData()); } else if (MusEGlobal::config.startMode == 1) { if(MusEGlobal::config.startSong.isEmpty()) // Sanity check to avoid some errors later { name = MusEGlobal::museGlobalShare + QString("/templates/default.med"); loadConfig = false; } else { name = MusEGlobal::config.startSong; if (name == "default.med") name = MusEGlobal::museGlobalShare + QString("/templates/default.med"); loadConfig = MusEGlobal::config.startSongLoadConfig; } useTemplate = true; fprintf(stderr, "starting with template %s\n", name.toLatin1().constData()); } else if (MusEGlobal::config.startMode == 2) { if(MusEGlobal::config.startSong.isEmpty()) // Sanity check to avoid some errors later { name = MusEGlobal::museGlobalShare + QString("/templates/default.med"); useTemplate = true; loadConfig = false; } else { name = MusEGlobal::config.startSong; loadConfig = MusEGlobal::config.startSongLoadConfig; } fprintf(stderr, "starting with pre configured song %s\n", name.toLatin1().constData()); } loadProjectFile(name, useTemplate, loadConfig); } //--------------------------------------------------------- // resetDevices //--------------------------------------------------------- void MusE::resetMidiDevices() { MusEGlobal::audio->msgResetMidiDevices(); } //--------------------------------------------------------- // initMidiDevices //--------------------------------------------------------- void MusE::initMidiDevices() { //MusEGlobal::audio->msgIdle(true); MusEGlobal::audio->msgInitMidiDevices(); //MusEGlobal::audio->msgIdle(false); } //--------------------------------------------------------- // localOff //--------------------------------------------------------- void MusE::localOff() { MusEGlobal::audio->msgLocalOff(); } //--------------------------------------------------------- // loadProjectFile // load *.med, *.mid, *.kar // // template - if true, load file but do not change // project name //--------------------------------------------------------- // for drop: void MusE::loadProjectFile(const QString& name) { loadProjectFile(name, false, false); } void MusE::loadProjectFile(const QString& name, bool songTemplate, bool doReadMidiPorts) { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); if(!progress) progress = new QProgressDialog(); QString label = "Loading project " + QFileInfo(name).fileName(); progress->setLabelText(label); // progress->setWindowModality(Qt::WindowModal); // REMOVE Tim. Persistent routes. Removed for version warning dialog to take priority. FIXME progress->setCancelButton(nullptr); if (!songTemplate) progress->setMinimumDuration(0); // if we are loading a template it will probably be fast and we can wait before showing the dialog // // stop audio threads if running // progress->setValue(0); qApp->processEvents(); bool restartSequencer = MusEGlobal::audio->isRunning(); if (restartSequencer) { if (MusEGlobal::audio->isPlaying()) { MusEGlobal::audio->msgPlay(false); while (MusEGlobal::audio->isPlaying()) qApp->processEvents(); } seqStop(); // REMOVE Tim. Persistent routes. TESTING. //MusEGlobal::audio->msgIdle(true); } microSleep(100000); progress->setValue(10); qApp->processEvents(); loadProjectFile1(name, songTemplate, doReadMidiPorts); microSleep(100000); progress->setValue(90); qApp->processEvents(); if (restartSequencer) seqStart(); // REMOVE Tim. Persistent routes. TESTING. //MusEGlobal::audio->msgIdle(false); //MusEGlobal::song->connectPorts(); arrangerView->updateVisibleTracksButtons(); progress->setValue(100); qApp->processEvents(); delete progress; progress = nullptr; QApplication::restoreOverrideCursor(); // Prompt and send init sequences. MusEGlobal::audio->msgInitMidiDevices(false); if (MusEGlobal::song->getSongInfo().length()>0 && MusEGlobal::song->showSongInfoOnStartup()) { startSongInfo(false); } } //--------------------------------------------------------- // loadProjectFile // load *.med, *.mid, *.kar // // template - if true, load file but do not change // project name // doReadMidiPorts - also read midi port configuration //--------------------------------------------------------- void MusE::loadProjectFile1(const QString& name, bool songTemplate, bool doReadMidiPorts) { if (mixer1) mixer1->clearAndDelete(); if (mixer2) mixer2->clearAndDelete(); _arranger->clear(); // clear track info if (clearSong(doReadMidiPorts)) // Allow not touching things like midi ports. return; MusEGlobal::recordAction->setChecked(false); progress->setValue(20); qApp->processEvents(); QFileInfo fi(name); if (songTemplate) { if(!fi.isReadable()) { QMessageBox::critical(this, QString("MusE"), tr("Cannot read template")); QApplication::restoreOverrideCursor(); return; } project.setFile(MusEGui::getUniqueUntitledName()); MusEGlobal::museProject = MusEGlobal::museProjectInitPath; QDir::setCurrent(QDir::homePath()); } else { fprintf(stderr, "Setting project path to %s\n", fi.absolutePath().toLocal8Bit().constData()); MusEGlobal::museProject = fi.absolutePath(); project.setFile(name); QDir::setCurrent(MusEGlobal::museProject); } _lastProjectFilePath = name; _lastProjectWasTemplate = songTemplate; _lastProjectLoadedConfig = doReadMidiPorts; QString ex = fi.completeSuffix().toLower(); QString mex = ex.section('.', -1, -1); if((mex == "gz") || (mex == "bz2")) mex = ex.section('.', -2, -2); if (ex.isEmpty() || mex == "med") { // // read *.med file // bool popenFlag; FILE* f = MusEGui::fileOpen(this, fi.filePath(), QString(".med"), "r", popenFlag, true); if (f == nullptr) { if (errno != ENOENT) { QMessageBox::critical(this, QString("MusE"), tr("File open error")); setUntitledProject(); _lastProjectFilePath = QString(); } else setConfigDefaults(); } else { if(songTemplate) { // The project is a template. Set the project's sample rate // to the system rate. // NOTE: A template should never contain anything 'frame' related // like wave parts and events, or even audio automation graphs ! // That is more under the category of say, 'demo songs'. // And here is the reason why: MusEGlobal::projectSampleRate = MusEGlobal::sampleRate; } else { MusECore::Xml d_xml(f); MusECore::SongfileDiscovery d_list(MusEGlobal::museProject); d_list.readSongfile(d_xml); // If it is a compressed file we cannot seek the stream, we must reopen it. if(popenFlag) { pclose(f); f = nullptr; f = MusEGui::fileOpen(this, fi.filePath(), QString(".med"), "r", popenFlag, true); if (f == nullptr) { if (errno != ENOENT) { QMessageBox::critical(this, QString("MusE"), tr("File open error")); setUntitledProject(); _lastProjectFilePath = QString(); } else setConfigDefaults(); } } else { // Be kind. Rewind. fseek(f, 0, SEEK_SET); } // Is there a project sample rate setting in the song? (Setting added circa 2011). if(d_list._waveList._projectSampleRateValid) { MusEGlobal::projectSampleRate = d_list._waveList._projectSampleRate; } else { int sugg_val; QString sugg_phrase; if(d_list._waveList.empty()) { // Suggest the current system sample rate. sugg_val = MusEGlobal::sampleRate; sugg_phrase = tr("The project has no project sample rate (added 2011).\n" "Please enter a rate. The current system rate (%1Hz)\n" " is suggested, and cancelling uses it:").arg(MusEGlobal::sampleRate); } else { // Suggest the most common sample rate used in the song. sugg_val = d_list._waveList.getMostCommonSamplerate(); sugg_phrase = tr("The project has audio waves, but no project sample rate (added 2011).\n" "Please enter a rate. The most common wave rate found is suggested,\n" " the project was probably made with it. Cancelling uses the\n" " current system rate (%1Hz):").arg(MusEGlobal::sampleRate); } bool ok; const int res = QInputDialog::getInt( this, tr("Project sample rate"), sugg_phrase, sugg_val, 0, (10 * 1000 * 1000), 1, &ok); if(ok) MusEGlobal::projectSampleRate = res; else MusEGlobal::projectSampleRate = MusEGlobal::sampleRate; } if (!songTemplate && //MusEGlobal::audioDevice->deviceType() != AudioDevice::DUMMY_AUDIO && // Why exclude dummy? MusEGlobal::projectSampleRate != MusEGlobal::sampleRate) { QString msg = QString("The sample rate in this project (%1Hz) and the\n" " current system setting (%2Hz) differ.\n" "Project timing will be scaled to match the new sample rate.\n" "Caution: Accuracy and sound quality may vary with rate and settings.\n\n" "Live realtime audio sample rate converters will be enabled\n" " on audio files where required.\n" "The files can be permanently converted to the new sample rate.\n\n" "Save this song if you are sure you didn't mean to open it\n" " at the original sample rate.").arg(MusEGlobal::projectSampleRate).arg(MusEGlobal::sampleRate); QMessageBox::warning(MusEGlobal::muse,"Wrong sample rate", msg); // Automatically convert the project. // No: Try to keep the rate until user tells it to change. //convertProjectSampleRate(); } } if(f) { MusECore::Xml xml(f); read(xml, doReadMidiPorts, songTemplate); bool fileError = ferror(f); popenFlag ? pclose(f) : fclose(f); if (fileError) { QMessageBox::critical(this, QString("MusE"), tr("File read error")); setUntitledProject(); _lastProjectFilePath = QString(); } } } } else if (mex == "mid" || mex == "kar") { setConfigDefaults(); if (!importMidi(name, false)) { setUntitledProject(); _lastProjectFilePath = QString(); } } else { QMessageBox::critical(this, QString("MusE"), tr("Unknown File Format: %1").arg(ex)); setUntitledProject(); _lastProjectFilePath = QString(); } if (!songTemplate) { addProjectToRecentList(project.absoluteFilePath()); setWindowTitle(projectTitle(project.absoluteFilePath())); } for (const auto& it : toplevels) { if (it->isMdiWin() && it->type() == TopWin::ARRANGER) { mdiArea->setActiveSubWindow(it->getMdiWin()); break; } } MusEGlobal::song->dirty = false; progress->setValue(30); qApp->processEvents(); viewTransportAction->setChecked(MusEGlobal::config.transportVisible); viewBigtimeAction->setChecked(MusEGlobal::config.bigTimeVisible); viewMarkerAction->setChecked(MusEGlobal::config.markerVisible); // viewArrangerAction->setChecked(MusEGlobal::config.arrangerVisible); // REMOVE Tim. automation. Removed. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. // autoMixerAction->setChecked(MusEGlobal::automation); showBigtime(MusEGlobal::config.bigTimeVisible); // NOTICE! Mixers may set their own maximum size according to their content, on SongChanged. // Therefore if the mixer is ALREADY OPEN, it may have a maximum size imposed on it, // which may be SMALLER than any new size we might try to set after this. // So we MUST RESET maximium size now, BEFORE attempts to set size. As per docs: if(mixer1) { mixer1->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); mixer1->setGeometry(MusEGlobal::config.mixer1.geometry); } if(mixer2) { mixer2->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); mixer2->setGeometry(MusEGlobal::config.mixer2.geometry); } showMixer1(MusEGlobal::config.mixer1Visible); showMixer2(MusEGlobal::config.mixer2Visible); // Loading a file should not manipulate the geometry of the main window (kybos) // resize(MusEGlobal::config.geometryMain.size()); // move(MusEGlobal::config.geometryMain.topLeft()); transport->move(MusEGlobal::config.geometryTransport.topLeft()); showTransport(MusEGlobal::config.transportVisible); progress->setValue(40); qApp->processEvents(); transport->setMasterFlag(MusEGlobal::tempomap.masterFlag()); MusEGlobal::punchinAction->setChecked(MusEGlobal::song->punchin()); MusEGlobal::punchoutAction->setChecked(MusEGlobal::song->punchout()); MusEGlobal::loopAction->setChecked(MusEGlobal::song->loop()); // Inform the rest of the app the song changed, with all flags MINUS // these flags which are already sent in the call to MusE::read() above: MusEGlobal::song->update(~SC_TRACK_INSERTED); MusEGlobal::song->updatePos(); arrangerView->clipboardChanged(); // enable/disable "Paste" arrangerView->selectionChanged(); // enable/disable "Copy" & "Paste" arrangerView->scoreNamingChanged(); // inform the score menus about the new scores and their names progress->setValue(50); qApp->processEvents(); // Moved here from above due to crash with a song loaded and then File->New. // Marker view list was not updated, had non-existent items from marker list (cleared in ::clear()). showMarker(MusEGlobal::config.markerVisible); } //--------------------------------------------------------- // fileClose //--------------------------------------------------------- void MusE::fileClose() { // For now we just don't read the ports, leaving the last setup intact. const bool doReadMidiPorts = false; // if (mixer1) // mixer1->clearAndDelete(); // if (mixer2) // mixer2->clearAndDelete(); // _arranger->clear(); // clear track info if(clearSong(doReadMidiPorts)) // Allow not touching things like midi ports. return; MusEGlobal::recordAction->setChecked(false); //setConfigDefaults(); QString name(MusEGui::getUniqueUntitledName()); MusEGlobal::museProject = MusEGlobal::museProjectInitPath; //QDir::setCurrent(QDir::homePath()); QDir::setCurrent(MusEGlobal::museProject); project.setFile(name); _lastProjectFilePath = QString(); _lastProjectWasTemplate = false; _lastProjectLoadedConfig = true; setWindowTitle(projectTitle(name)); //writeTopwinState=true; MusEGlobal::song->dirty = false; // Inform the rest of the app the song changed, with all flags. MusEGlobal::song->update(SC_EVERYTHING); MusEGlobal::song->updatePos(); arrangerView->clipboardChanged(); // enable/disable "Paste" arrangerView->selectionChanged(); // enable/disable "Copy" & "Paste" arrangerView->scoreNamingChanged(); // inform the score menus about the new scores and their names } //--------------------------------------------------------- // setUntitledProject //--------------------------------------------------------- void MusE::setUntitledProject() { setConfigDefaults(); QString name(MusEGui::getUniqueUntitledName()); MusEGlobal::museProject = MusEGlobal::museProjectInitPath; QDir::setCurrent(QDir::homePath()); project.setFile(name); setWindowTitle(projectTitle(name)); writeTopwinState=true; } //--------------------------------------------------------- // setConfigDefaults //--------------------------------------------------------- void MusE::setConfigDefaults() { MusECore::readConfiguration(); // used for reading midi files MusEGlobal::song->dirty = false; } //--------------------------------------------------------- // MusE::loadProject //--------------------------------------------------------- void MusE::loadProject() { bool doReadMidiPorts; QString fn = MusEGui::getOpenFileName(QString(""), MusEGlobal::med_file_pattern, this, tr("MusE: load project"), &doReadMidiPorts); if (!fn.isEmpty()) { MusEGlobal::museProject = QFileInfo(fn).absolutePath(); QDir::setCurrent(QFileInfo(fn).absolutePath()); loadProjectFile(fn, false, doReadMidiPorts); } } //--------------------------------------------------------- // loadTemplate //--------------------------------------------------------- void MusE::loadTemplate() { bool doReadMidiPorts; QString fn = MusEGui::getOpenFileName(QString("templates"), MusEGlobal::med_file_pattern, this, tr("MusE: load template"), &doReadMidiPorts, MusEGui::MFileDialog::GLOBAL_VIEW); if (!fn.isEmpty()) { loadProjectFile(fn, true, doReadMidiPorts); setUntitledProject(); } } //--------------------------------------------------------- // loadDefaultTemplate //--------------------------------------------------------- void MusE::loadDefaultTemplate() { loadProjectFile(MusEGlobal::museGlobalShare + QString("/templates/default.med"), true, false); setUntitledProject(); } //--------------------------------------------------------- // save //--------------------------------------------------------- bool MusE::save() { if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) return saveAs(); else return save(project.filePath(), false, writeTopwinState); } //--------------------------------------------------------- // save //--------------------------------------------------------- bool MusE::save(const QString& name, bool overwriteWarn, bool writeTopwins) { // QString backupCommand; QFile currentName(name); if (QFile::exists(name)) { currentName.copy(name+".backup"); //backupCommand.sprintf("cp \"%s\" \"%s.backup\"", name.toLatin1().constData(), name.toLatin1().constData()); } else if (QFile::exists(name + QString(".med"))) { QString currentName2(name+".med"); currentName.copy(name+".med.backup"); //backupCommand.sprintf("cp \"%s.med\" \"%s.med.backup\"", name.toLatin1().constData(), name.toLatin1().constData()); } // if (!backupCommand.isEmpty()) // system(backupCommand.toLatin1().constData()); bool popenFlag; FILE* f = MusEGui::fileOpen(this, name, QString(".med"), "w", popenFlag, false, overwriteWarn); if (f == nullptr) return false; MusECore::Xml xml(f); write(xml, writeTopwins); if (ferror(f)) { QString s = "Write File\n" + name + "\nfailed: " + QString(strerror(errno)); QMessageBox::critical(this, tr("MusE: Write File failed"), s); popenFlag? pclose(f) : fclose(f); unlink(name.toLatin1().constData()); return false; } else { popenFlag? pclose(f) : fclose(f); MusEGlobal::song->dirty = false; setWindowTitle(projectTitle(project.absoluteFilePath())); saveIncrement = 0; return true; } } //--------------------------------------------------------- // quitDoc //--------------------------------------------------------- void MusE::quitDoc() { close(); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void MusE::closeEvent(QCloseEvent* event) { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); MusEGlobal::song->setStop(true); // // wait for sequencer // while (MusEGlobal::audio->isPlaying()) { qApp->processEvents(); } if (MusEGlobal::song->dirty) { int n = 0; n = QMessageBox::warning(this, appName, tr("The current project contains unsaved data.\n" "Save current project?"), tr("&Save"), tr("S&kip"), tr("&Cancel"), 0, 2); if (n == 0) { if (!save()) // don't quit if save failed { setRestartingApp(false); // Cancel any restart. event->ignore(); QApplication::restoreOverrideCursor(); return; } } else if (n == 2) { setRestartingApp(false); // Cancel any restart. event->ignore(); QApplication::restoreOverrideCursor(); return; } } seqStop(); MusECore::WaveTrackList* wt = MusEGlobal::song->waves(); for (MusECore::iWaveTrack iwt = wt->begin(); iwt != wt->end(); ++iwt) { MusECore::WaveTrack* t = *iwt; if (t->recFile() && t->recFile()->samples() == 0) { t->recFile()->remove(); } } // Don't use saveGeoemetry for the main window: Qt has issues (data gets invalid) // when both standard MusE and AppImage are used on the same PC // QSettings settings; // settings.setValue("MusE/geometry", saveGeometry()); MusEGlobal::config.geometryMain = geometry(); // must be done here as the close events of child windows are not always called on quit saveStateTopLevels(); saveStateExtra(); writeGlobalConfiguration(); if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Exiting JackAudio\n"); MusECore::exitJackAudio(); if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Exiting DummyAudio\n"); MusECore::exitDummyAudio(); #ifdef HAVE_RTAUDIO if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Exiting RtAudio\n"); MusECore::exitRtAudio(); #endif if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Exiting Metronome\n"); MusECore::exitMetronome(); MusEGlobal::song->cleanupForQuit(); // Give midi devices a chance to close first, above in cleanupForQuit. if(MusEGlobal::debugMsg) fprintf(stderr, "Muse: Exiting ALSA midi\n"); MusECore::exitMidiAlsa(); if(MusEGlobal::debugMsg) fprintf(stderr, "Muse: Cleaning up temporary wavefiles + peakfiles\n"); // Cleanup temporary wavefiles + peakfiles used for undo for (std::list::iterator i = MusECore::temporaryWavFiles.begin(); i != MusECore::temporaryWavFiles.end(); i++) { QString filename = *i; QFileInfo fi(filename); QDir d = fi.dir(); d.remove(filename); d.remove(fi.completeBaseName() + ".wca"); } if(MusEGlobal::usePythonBridge) { fprintf(stderr, "Stopping MusE Pybridge...\n"); if(!stopPythonBridge()) fprintf(stderr, "MusE: Could not stop Python bridge\n"); else fprintf(stderr, "MusE: Pybridge stopped\n"); } #ifdef HAVE_LASH // Disconnect gracefully from LASH. if(lash_client) { if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Disconnecting from LASH\n"); lash_event_t* lashev = lash_event_new_with_type (LASH_Quit); lash_send_event(lash_client, lashev); } #endif if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Exiting Dsp\n"); AL::exitDsp(); if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Exiting OSC\n"); MusECore::exitOSC(); delete MusEGlobal::audioPrefetch; delete MusEGlobal::audio; // Destroy the sequencer object if it exists. MusECore::exitMidiSequencer(); delete MusEGlobal::song; if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Deleting icons\n"); deleteIcons(); if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Deleting all parentless dialogs and widgets\n"); deleteParentlessDialogs(); qApp->quit(); } //--------------------------------------------------------- // showMarker //--------------------------------------------------------- void MusE::showMarker(bool flag) { markerDock->setVisible(flag); } //--------------------------------------------------------- // toggleArranger //--------------------------------------------------------- //void MusE::toggleArranger(bool checked) // { // showArranger(checked); // } //--------------------------------------------------------- // showArranger //--------------------------------------------------------- //void MusE::showArranger(bool flag) // { // if(arrangerView->isVisible() != flag) // arrangerView->setVisible(flag); // if(viewArrangerAction->isChecked() != flag) // viewArrangerAction->setChecked(flag); // if (!flag) // if (currentMenuSharingTopwin == arrangerView) // setCurrentMenuSharingTopwin(nullptr); // updateWindowMenu(); // } //--------------------------------------------------------- // arrangerClosed //--------------------------------------------------------- //void MusE::arrangerClosed() //{ // if(viewArrangerAction->isChecked()) // viewArrangerAction->setChecked(false); // updateWindowMenu(); //// if (mdiArea->viewMode() == QMdiArea::TabbedView) { //// QTabBar* tb = mdiArea->findChild(); //// if (tb) { //// int i = tb->currentIndex(); //// tb->setTabEnabled(i, false); //// tb->setTabButton(i, QTabBar::RightSide, nullptr); //// } //// } // // focus the last activated topwin which is not the arranger view // QList l = mdiArea->subWindowList(QMdiArea::StackingOrder); // for (QList::const_reverse_iterator lit=l.rbegin(); lit!=l.rend(); lit++) // if ((*lit)->isVisible() && (*lit)->widget() != arrangerView) // { // if (MusEGlobal::debugMsg) // fprintf(stderr, "bringing '%s' to front instead of closed arranger window\n",(*lit)->widget()->windowTitle().toLatin1().data()); // bringToFront((*lit)->widget()); // break; // } //} //--------------------------------------------------------- // toggleTransport //--------------------------------------------------------- void MusE::toggleTransport(bool checked) { showTransport(checked); } //--------------------------------------------------------- // showTransport //--------------------------------------------------------- void MusE::showTransport(bool flag) { if(transport->isVisible() != flag) transport->setVisible(flag); if(viewTransportAction->isChecked() != flag) viewTransportAction->setChecked(flag); } #ifdef _WIN32 static float CalculateCPULoad(unsigned long long idleTicks, unsigned long long totalTicks) { static unsigned long long _previousTotalTicks = 0; static unsigned long long _previousIdleTicks = 0; unsigned long long totalTicksSinceLastTime = totalTicks-_previousTotalTicks; unsigned long long idleTicksSinceLastTime = idleTicks-_previousIdleTicks; float ret = 1.0f-((totalTicksSinceLastTime > 0) ? ((float)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0); _previousTotalTicks = totalTicks; _previousIdleTicks = idleTicks; return ret; } static unsigned long long FileTimeToInt64(const FILETIME & ft) { return (((unsigned long long)(ft.dwHighDateTime))<<32)|((unsigned long long)ft.dwLowDateTime); } #endif float MusE::getCPULoad() { #ifdef _WIN32 FILETIME idleTime, kernelTime, userTime; return GetSystemTimes(&idleTime, &kernelTime, &userTime) ? CalculateCPULoad(FileTimeToInt64(idleTime), FileTimeToInt64(kernelTime)+FileTimeToInt64(userTime)) : -1.0f; #else struct rusage ru; struct timespec curSysTime; if(clock_gettime(CLOCK_REALTIME, &curSysTime) != 0) { return 0.0f; } //float fLoad = 0.0f; if(getrusage(RUSAGE_SELF, &ru) != 0) { return 0.0f; } long msSysElapsed = (curSysTime.tv_nsec / 1000000L) + curSysTime.tv_sec * 1000L; msSysElapsed -= (lastSysTime.tv_nsec / 1000000L) + lastSysTime.tv_sec * 1000L; long msCpuElasped = (ru.ru_utime.tv_usec / 1000L) + ru.ru_utime.tv_sec * 1000L; msCpuElasped -= (lastCpuTime.tv_usec / 1000L) + lastCpuTime.tv_sec * 1000L; if(msSysElapsed > 0) { fAvrCpuLoad += (float)((double)msCpuElasped / (double)msSysElapsed); avrCpuLoadCounter++; } lastCpuTime = ru.ru_utime; lastSysTime = curSysTime; if(avrCpuLoadCounter > 10) { fCurCpuLoad = (fAvrCpuLoad / (float)avrCpuLoadCounter) * 100.0f; fAvrCpuLoad = 0.0f; avrCpuLoadCounter = 0; } return fCurCpuLoad; #endif } void MusE::saveAsNewProject() { auto storedProject = project; project = QFileInfo(); auto storedMusEProject = MusEGlobal::museProject; MusEGlobal::museProject = MusEGlobal::museProjectInitPath; saveAs(true); if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) { // change was rejected, restore the old project project = storedProject; MusEGlobal::museProject = storedMusEProject; } } void MusE::saveNewRevision() { if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) { saveAs(); return; } QString newFilePath = ""; QString oldProjectFileName = project.filePath(); MusEGui::SaveNewRevisionDialog newRevision(MusEGlobal::muse, project); newFilePath = newRevision.getNewRevision(); if (newFilePath.isEmpty()) { // could not set revision automatically, open dialog. newFilePath = newRevision.getNewRevisionWithDialog(); } if (newFilePath.isEmpty()) return; bool ok = save(newFilePath, true, writeTopwinState); if (ok) { project.setFile(newFilePath); _lastProjectFilePath = newFilePath; _lastProjectWasTemplate = false; _lastProjectLoadedConfig = true; setWindowTitle(projectTitle(project.absoluteFilePath())); // replace project in lastProjects if (projectRecentList.contains(oldProjectFileName)) projectRecentList.removeAt(projectRecentList.indexOf(oldProjectFileName)); addProjectToRecentList(newFilePath); project.setFile(newFilePath); } } //--------------------------------------------------------- // saveAs //--------------------------------------------------------- bool MusE::saveAs(bool overrideProjectSaveDialog) { QString name; // if this is the initial save and ProjectDialog is enabled, use that. if (overrideProjectSaveDialog || (MusEGlobal::config.useProjectSaveDialog && MusEGlobal::museProject == MusEGlobal::museProjectInitPath)) { MusEGui::ProjectCreateImpl pci(MusEGlobal::muse); pci.setWriteTopwins(writeTopwinState); if (pci.exec() == QDialog::Rejected) { return false; } MusEGlobal::song->setSongInfo(pci.getSongInfo(), true); name = pci.getProjectPath(); writeTopwinState=pci.getWriteTopwins(); } else { name = MusEGui::getSaveFileName(QString(""), MusEGlobal::med_file_save_pattern, this, tr("MusE: Save As"), &writeTopwinState); if (name.isEmpty()) return false; } MusEGlobal::museProject = QFileInfo(name).absolutePath(); QDir dirmanipulator; if (!dirmanipulator.mkpath(MusEGlobal::museProject)) { QMessageBox::warning(this,"Path error","Can't create project path", QMessageBox::Ok); return false; } bool ok = false; if (!name.isEmpty()) { QString tempOldProj = MusEGlobal::museProject; MusEGlobal::museProject = QFileInfo(name).absolutePath(); ok = save(name, true, writeTopwinState); if (ok) { project.setFile(name); _lastProjectFilePath = name; _lastProjectWasTemplate = false; _lastProjectLoadedConfig = true; setWindowTitle(projectTitle(project.absoluteFilePath())); addProjectToRecentList(name); } else MusEGlobal::museProject = tempOldProj; QDir::setCurrent(MusEGlobal::museProject); } return ok; } //--------------------------------------------------------- // saveAsTemplate //--------------------------------------------------------- void MusE::saveAsTemplate() { QString templatesDir = MusEGlobal::configPath + QString("/") + "templates"; printf ("templates dir %s\n", templatesDir.toLatin1().data()); QDir dirmanipulator; if (!dirmanipulator.mkpath(templatesDir)) { QMessageBox::warning(this,"Path error","Could not create templates directory", QMessageBox::Ok); return; } QString name; name = MusEGui::getSaveFileName(QString("templates"), MusEGlobal::med_file_save_pattern, this, tr("MusE: Save As"), &writeTopwinState, MFileDialog::USER_VIEW); if (name.isEmpty()) return; auto finalPath = QFileInfo(name).absolutePath(); if (!dirmanipulator.mkpath(finalPath)) { QMessageBox::warning(this,"Path error","Can't create final project path", QMessageBox::Ok); return; } save(name, true, false); } //--------------------------------------------------------- // startEditor //--------------------------------------------------------- void MusE::startEditor(MusECore::PartList* pl, int type) { switch (type) { case 0: startPianoroll(pl, true); break; case 1: startListEditor(pl); break; case 3: startDrumEditor(pl, true); break; case 4: startWaveEditor(pl); break; } } //--------------------------------------------------------- // startEditor //--------------------------------------------------------- void MusE::startEditor(MusECore::Track* t) { switch (t->type()) { case MusECore::Track::MIDI: startPianoroll(); break; case MusECore::Track::DRUM: startDrumEditor(); break; case MusECore::Track::WAVE: startWaveEditor(); break; default: break; } } //--------------------------------------------------------- // getMidiPartsToEdit //--------------------------------------------------------- MusECore::PartList* MusE::getMidiPartsToEdit() { MusECore::PartList* pl = MusECore::getSelectedMidiParts(); if (pl->empty()) { QMessageBox::critical(this, QString("MusE"), tr("Nothing to edit")); return nullptr; } return pl; } //--------------------------------------------------------- // startScoreEdit //--------------------------------------------------------- void MusE::openInScoreEdit_oneStaffPerTrack(QWidget* dest) { openInScoreEdit((MusEGui::ScoreEdit*)dest, false); } void MusE::openInScoreEdit_allInOne(QWidget* dest) { openInScoreEdit((MusEGui::ScoreEdit*)dest, true); } void MusE::openInScoreEdit(MusEGui::ScoreEdit* destination, bool allInOne) { MusECore::PartList* pl = getMidiPartsToEdit(); if (pl == nullptr) return; openInScoreEdit(destination, pl, allInOne); } void MusE::openInScoreEdit(MusEGui::ScoreEdit* destination, MusECore::PartList* pl, bool allInOne) { if (destination==nullptr) // if no destination given, create a new one { destination = new MusEGui::ScoreEdit(this, nullptr, _arranger->cursorValue()); toplevels.push_back(destination); destination->show(); connect(destination, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); connect(destination, SIGNAL(name_changed()), arrangerView, SLOT(scoreNamingChanged())); //connect(muse, SIGNAL(configChanged()), destination, SLOT(config_changed())); //commented out by flo, because the ScoreEditor connects to all //relevant signals on his own arrangerView->updateScoreMenus(); updateWindowMenu(); } destination->add_parts(pl, allInOne); } void MusE::startScoreQuickly() { openInScoreEdit_oneStaffPerTrack(nullptr); } //--------------------------------------------------------- // startPianoroll //--------------------------------------------------------- void MusE::startPianoroll(bool newwin) { MusECore::PartList* pl = getMidiPartsToEdit(); if (pl == nullptr) return; if (!filterInvalidParts(TopWin::PIANO_ROLL, pl)) return; startPianoroll(pl, true, newwin); } void MusE::startPianoroll(MusECore::PartList* pl, bool showDefaultCtrls, bool newwin) { if (!filterInvalidParts(TopWin::PIANO_ROLL, pl)) return; if (!newwin && findOpenEditor(TopWin::PIANO_ROLL, pl)) return; MusEGui::PianoRoll* pianoroll = new MusEGui::PianoRoll(pl, this, nullptr, _arranger->cursorValue(), showDefaultCtrls); toplevels.push_back(pianoroll); pianoroll->show(); connect(pianoroll, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); connect(MusEGlobal::muse, SIGNAL(configChanged()), pianoroll, SLOT(configChanged())); updateWindowMenu(); } bool MusE::filterInvalidParts(const TopWin::ToplevelType type, MusECore::PartList* pl) { for (auto it = pl->begin(); it != pl->end(); ) { if ((it->second->track()->type() == MusECore::Track::MIDI && type == TopWin::PIANO_ROLL) || (it->second->track()->type() == MusECore::Track::DRUM && type == TopWin::DRUM)) it++; else it = pl->erase(it); } if (pl->empty()) { QMessageBox::critical(this, QString("MusE"), tr("No valid parts selected")); return false; } return true; } bool MusE::findOpenEditor(const TopWin::ToplevelType type, MusECore::PartList* pl) { if (QGuiApplication::keyboardModifiers() & Qt::ControlModifier && QGuiApplication::keyboardModifiers() & Qt::AltModifier) return false; for (const auto& it : toplevels) { if (it->type() != type) continue; MusEGui::MidiEditor* med = dynamic_cast(it); if (med == nullptr) return false; const MusECore::PartList* pl_tmp = med->parts(); if (pl_tmp->size() != pl->size()) continue; bool found = false; for (const auto& it_pl : *pl) { for (const auto& it_pl_tmp : *pl_tmp) { if (it_pl.second->sn() == it_pl_tmp.second->sn()) { found = true; break; } } if (!found) break; } if (!found) continue; med->setHScrollOffset(_arranger->cursorValue()); if (it->isMdiWin()) mdiArea->setActiveSubWindow(it->getMdiWin()); else it->activateWindow(); return true; } return false; } //--------------------------------------------------------- // startListenEditor //--------------------------------------------------------- void MusE::startListEditor(bool newwin) { MusECore::PartList* pl = getMidiPartsToEdit(); if (pl == nullptr) return; startListEditor(pl, newwin); } void MusE::startListEditor(MusECore::PartList* pl, bool newwin) { pl->erase(++pl->begin(), pl->end()); if (!newwin && findOpenListEditor(pl)) return; QDockWidget* dock = new QDockWidget("List Editor", this); // dock->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea); MusEGui::ListEdit* listEditor = new MusEGui::ListEdit(pl, this); dock->setWidget(listEditor); { int bar1, bar2, xx; unsigned x; const auto p = pl->begin()->second; MusEGlobal::sigmap.tickValues(p->tick(), &bar1, &xx, &x); MusEGlobal::sigmap.tickValues(p->tick() + p->lenTick(), &bar2, &xx, &x); dock->setWindowTitle("Event List <" + p->name() + QString("> %1-%2").arg(bar1+1).arg(bar2+1)); } dock->setObjectName(dock->windowTitle()); addDockWidget(Qt::BottomDockWidgetArea, dock); // addTabbedDock(Qt::BottomDockWidgetArea, dock); dock->setAttribute(Qt::WA_DeleteOnClose); connect(MusEGlobal::muse,SIGNAL(configChanged()), listEditor, SLOT(configChanged())); } bool MusE::findOpenListEditor(MusECore::PartList* pl) { if (QGuiApplication::keyboardModifiers() & Qt::ControlModifier && QGuiApplication::keyboardModifiers() & Qt::AltModifier) return false; for (const auto& d : findChildren()) { if (strcmp(d->widget()->metaObject()->className(), "MusEGui::ListEdit") != 0) continue; const MusECore::PartList* pl_tmp = static_cast(d->widget())->parts(); if (pl->begin()->second->sn() != pl_tmp->begin()->second->sn()) continue; if (!d->isVisible()) toggleDocksAction->setChecked(true); d->raise(); return true; } return false; } //--------------------------------------------------------- // startMasterEditor //--------------------------------------------------------- void MusE::startMasterEditor() { if (!masterEditor) { masterEditor = new MusEGui::MasterEdit(this); toplevels.push_back(masterEditor); masterEditor->show(); connect(masterEditor, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); updateWindowMenu(); } else { if (masterEditor->isMdiWin()) mdiArea->setActiveSubWindow(masterEditor->getMdiWin()); else masterEditor->activateWindow(); } } //--------------------------------------------------------- // startLMasterEditor //--------------------------------------------------------- void MusE::showMasterList(bool show) { masterListDock->setVisible(show); } //--------------------------------------------------------- // startDrumEditor //--------------------------------------------------------- void MusE::startDrumEditor(bool newwin) { MusECore::PartList* pl = getMidiPartsToEdit(); if (pl == nullptr) return; if (!filterInvalidParts(TopWin::DRUM, pl)) return; startDrumEditor(pl, true, newwin); } void MusE::startDrumEditor(MusECore::PartList* pl, bool showDefaultCtrls, bool newwin) { if (!filterInvalidParts(TopWin::DRUM, pl)) return; if (!newwin &&findOpenEditor(TopWin::DRUM, pl)) return; MusEGui::DrumEdit* drumEditor = new MusEGui::DrumEdit(pl, this, nullptr, _arranger->cursorValue(), showDefaultCtrls); toplevels.push_back(drumEditor); drumEditor->show(); connect(drumEditor, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); connect(MusEGlobal::muse, SIGNAL(configChanged()), drumEditor, SLOT(configChanged())); updateWindowMenu(); } //--------------------------------------------------------- // startWaveEditor //--------------------------------------------------------- void MusE::startWaveEditor(bool newwin) { MusECore::PartList* pl = MusECore::getSelectedWaveParts(); if (pl->empty()) { QMessageBox::critical(this, QString("MusE"), tr("Nothing to edit")); return; } startWaveEditor(pl, newwin); } void MusE::startWaveEditor(MusECore::PartList* pl, bool newwin) { if (! newwin && findOpenEditor(TopWin::WAVE, pl)) return; MusEGui::WaveEdit* waveEditor = new MusEGui::WaveEdit(pl, this); toplevels.push_back(waveEditor); waveEditor->show(); connect(MusEGlobal::muse, SIGNAL(configChanged()), waveEditor, SLOT(configChanged())); connect(waveEditor, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); updateWindowMenu(); } //--------------------------------------------------------- // startSongInfo //--------------------------------------------------------- void MusE::startSongInfo(bool editable) { MusEGui::SongInfoWidget info; info.viewCheckBox->setChecked(MusEGlobal::song->showSongInfoOnStartup()); info.viewCheckBox->setEnabled(editable); info.songInfoText->setPlainText(MusEGlobal::song->getSongInfo()); info.songInfoText->setReadOnly(!editable); info.setModal(true); info.show(); if( info.exec() == QDialog::Accepted) { if (editable) { MusEGlobal::song->setSongInfo(info.songInfoText->toPlainText(), info.viewCheckBox->isChecked()); } } } void MusE::showDidYouKnowDialogIfEnabled() { if ((bool)MusEGlobal::config.showDidYouKnow == true) { showDidYouKnowDialog(); } } //--------------------------------------------------------- // showDidYouKnowDialog //--------------------------------------------------------- void MusE::showDidYouKnowDialog() { MusEGui::DidYouKnowWidget didYouKnow; QFile file(MusEGlobal::museGlobalShare + "/didyouknow.txt"); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { fprintf(stderr, "could not open didyouknow.txt!\n"); return; } // All tips are separated by an empty line. Lines starting with # are ignored QString tipMessage = ""; while (!file.atEnd()) { QString line = file.readLine(); if (!line.simplified().isEmpty() && line.at(0) != QChar('#')) tipMessage.append(line); if (!tipMessage.isEmpty() && line.simplified().isEmpty()) { didYouKnow.tipList.append(tipMessage); tipMessage=""; } } if (!tipMessage.isEmpty()) { didYouKnow.tipList.append(tipMessage); } std::random_shuffle(didYouKnow.tipList.begin(),didYouKnow.tipList.end()); didYouKnow.show(); if( didYouKnow.exec()) { if (didYouKnow.dontShowCheckBox->isChecked()) { MusEGlobal::config.showDidYouKnow=false; // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); // save settings } } } //--------------------------------------------------------- // startDefineController //--------------------------------------------------------- //--------------------------------------------------------- // startClipList //--------------------------------------------------------- void MusE::showClipList(bool show) { clipListDock->setVisible(show); } //--------------------------------------------------------- // fileMenu //--------------------------------------------------------- void MusE::openRecentMenu() { openRecent->clear(); for (int i = 0; i < projectRecentList.size(); ++i) { if (!QFileInfo(projectRecentList[i]).exists()) continue; QString fileName = QFileInfo(projectRecentList[i]).fileName(); QAction *act = openRecent->addAction(fileName); act->setData(i); } } //--------------------------------------------------------- // selectProject //--------------------------------------------------------- void MusE::selectProject(QAction* act) { if (!act) return; int id = act->data().toInt(); if (id > projectRecentList.size()-1) { fprintf(stderr, "THIS SHOULD NEVER HAPPEN: id(%i) < recent len(%i) in MusE::selectProject!\n", id, MusEGlobal::config.recentListLength); return; } QString name = projectRecentList[id]; if (name == "") return; loadProjectFile(name, false, true); } //--------------------------------------------------------- // toplevelDeleting //--------------------------------------------------------- void MusE::toplevelDeleting(MusEGui::TopWin* tl) { for (MusEGui::iToplevel i = toplevels.begin(); i != toplevels.end(); ++i) { if (*i == tl) { tl->storeInitialState(); if (tl == activeTopWin) { activeTopWin=nullptr; emit activeTopWinChanged(nullptr); // focus the last activated topwin which is not the deleting one QList list = mdiArea->subWindowList(QMdiArea::StackingOrder); for (QList::const_reverse_iterator lit=list.rbegin(); lit!=list.rend(); lit++) if ((*lit)->isVisible() && (*lit)->widget() != tl) { if (MusEGlobal::debugMsg) fprintf(stderr, "bringing '%s' to front instead of closed window\n",(*lit)->widget()->windowTitle().toLatin1().data()); bringToFront((*lit)->widget()); break; } } if (tl == currentMenuSharingTopwin) setCurrentMenuSharingTopwin(nullptr); toplevels.erase(i); if (tl->type() == MusEGui::TopWin::SCORE) arrangerView->updateScoreMenus(); updateWindowMenu(); return; } } fprintf(stderr, "topLevelDeleting: top level %p not found\n", tl); } //--------------------------------------------------------- // kbAccel - Global keyboard accelerators //--------------------------------------------------------- void MusE::kbAccel(int key) { if (key == MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_METRO].key) { MusEGlobal::song->setClick(!MusEGlobal::song->click()); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_REC_RESTART].key) { MusEGlobal::song->restartRecording(); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_REC_RESTART_MULTI].key) { MusEGlobal::song->restartRecording(false); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_PLAY_TOGGLE].key) { if (MusEGlobal::audio->isPlaying()) MusEGlobal::song->setStop(true); else if (!MusEGlobal::config.useOldStyleStopShortCut) MusEGlobal::song->setPlay(true); else if (MusEGlobal::song->cpos() != MusEGlobal::song->lpos()) MusEGlobal::song->setPos(MusECore::Song::CPOS, MusEGlobal::song->lPos()); else { MusECore::Pos p(0, true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p); } } else if (key == MusEGui::shortcuts[MusEGui::SHRT_STOP].key) { MusEGlobal::song->setStop(true); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_GOTO_END].key) { MusECore::Pos p(MusEGlobal::song->len(), true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_GOTO_START].key) { MusECore::Pos p(0, true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_PLAY_SONG].key ) { MusEGlobal::song->setPlay(true); } // Normally each editor window handles these, to inc by the editor's raster snap value. // But users were asking for a global version - "they don't work when I'm in mixer or transport". // Since no editor claimed the key event, we don't know a specific editor's snap setting, // so adopt a policy where the arranger is the 'main' raster reference, I guess... else if (key == MusEGui::shortcuts[MusEGui::SHRT_POS_DEC].key) { int spos = MusEGlobal::song->cpos(); if(spos > 0) { spos -= 1; // Nudge by -1, then snap down with raster1. spos = MusEGlobal::sigmap.raster1(spos, _arranger->rasterVal()); } if(spos < 0) spos = 0; MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); return; } else if (key == MusEGui::shortcuts[MusEGui::SHRT_POS_INC].key) { // Nudge by +1, then snap up with raster2. int spos = MusEGlobal::sigmap.raster2(MusEGlobal::song->cpos() + 1, _arranger->rasterVal()); MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW return; } else if (key == MusEGui::shortcuts[MusEGui::SHRT_POS_DEC_NOSNAP].key) { int spos = MusEGlobal::song->cpos() - MusEGlobal::sigmap.rasterStep(MusEGlobal::song->cpos(), _arranger->rasterVal()); if(spos < 0) spos = 0; MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); return; } else if (key == MusEGui::shortcuts[MusEGui::SHRT_POS_INC_NOSNAP].key) { MusECore::Pos p(MusEGlobal::song->cpos() + MusEGlobal::sigmap.rasterStep(MusEGlobal::song->cpos(), _arranger->rasterVal()), true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); return; } else if (key == MusEGui::shortcuts[MusEGui::SHRT_REC_ARM_TRACK].key) { if (!MusEGlobal::song->record()) toggleTrackArmSelectedTrack(); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_GOTO_LEFT].key) { if (!MusEGlobal::song->record()) MusEGlobal::song->setPos(MusECore::Song::CPOS, MusEGlobal::song->lPos()); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_GOTO_RIGHT].key) { if (!MusEGlobal::song->record()) MusEGlobal::song->setPos(MusECore::Song::CPOS, MusEGlobal::song->rPos()); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_LOOP].key) { MusEGlobal::song->setLoop(!MusEGlobal::song->loop()); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_START_REC].key) { if (!MusEGlobal::audio->isPlaying()) { MusEGlobal::song->setRecord(!MusEGlobal::song->record()); } } else if (key == MusEGui::shortcuts[MusEGui::SHRT_REC_CLEAR].key) { if (!MusEGlobal::audio->isPlaying()) { MusEGlobal::song->clearTrackRec(); } } else if (key == MusEGui::shortcuts[MusEGui::SHRT_OPEN_TRANSPORT].key) { toggleTransport(!viewTransportAction->isChecked()); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_OPEN_BIGTIME].key) { toggleBigTime(!viewBigtimeAction->isChecked()); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_OPEN_MIXER].key) { toggleMixer1(!viewMixerAAction->isChecked()); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_OPEN_MIXER2].key) { toggleMixer2(!viewMixerBAction->isChecked()); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_NEXT_MARKER].key) { if (markerView) markerView->nextMarker(); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_PREV_MARKER].key) { if (markerView) markerView->prevMarker(); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_CONFIG_SHORTCUTS].key) { configShortCuts(); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_PART_NORMALIZE].key) { MusEGlobal::song->normalizeWaveParts(); } else if (key == MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_REWINDONSTOP].key) { rewindOnStopAction->activate(QAction::Trigger); } else { if (MusEGlobal::debugMsg) fprintf(stderr, "unknown kbAccel 0x%x\n", key); } } #if 0 //--------------------------------------------------------- // catchSignal // only for debugging //--------------------------------------------------------- // if enabling this code, also enable the line containing // "catchSignal" in main.cpp static void catchSignal(int sig) { if (MusEGlobal::debugMsg) fprintf(stderr, "MusE: signal %d caught\n", sig); if (sig == SIGSEGV) { fprintf(stderr, "MusE: segmentation fault\n"); abort(); } if (sig == SIGCHLD) { M_DEBUG("caught SIGCHLD - child died\n"); int status; int n = waitpid (-1, &status, WNOHANG); if (n > 0) { fprintf(stderr, "SIGCHLD for unknown process %d received\n", n); } } } #endif //--------------------------------------------------------- // cmd // some cmd's from pulldown menu //--------------------------------------------------------- void MusE::cmd(int cmd) { switch(cmd) { case CMD_FOLLOW_NO: MusEGlobal::song->setFollow(MusECore::Song::NO); break; case CMD_FOLLOW_JUMP: MusEGlobal::song->setFollow(MusECore::Song::JUMP); break; case CMD_FOLLOW_CONTINUOUS: MusEGlobal::song->setFollow(MusECore::Song::CONTINUOUS); break; } } //--------------------------------------------------------- // deleteParentlessDialogs // All these dialogs and/or widgets have no parent, // but are not considered MusE 'top-level', so could not // be handled via the top-levels list... //--------------------------------------------------------- void MusE::deleteParentlessDialogs() { // Appearance is already a child of MusE !!! // if(appearance) // { // delete appearance; // appearance = 0; // } if(_snooperDialog) { delete _snooperDialog; _snooperDialog = nullptr; } if(metronomeConfig) { delete metronomeConfig; metronomeConfig = nullptr; } if(shortcutConfig) { delete shortcutConfig; shortcutConfig = nullptr; } if(midiSyncConfig) { delete midiSyncConfig; midiSyncConfig = nullptr; } if(midiFileConfig) { delete midiFileConfig; midiFileConfig = nullptr; } if(globalSettingsConfig) { delete globalSettingsConfig; globalSettingsConfig = nullptr; } destroy_function_dialogs(); if(MusEGlobal::mitPluginTranspose) { delete MusEGlobal::mitPluginTranspose; MusEGlobal::mitPluginTranspose = nullptr; } if(midiInputTransform) { delete midiInputTransform; midiInputTransform = nullptr; } if(midiFilterConfig) { delete midiFilterConfig; midiFilterConfig = nullptr; } if(midiRemoteConfig) { delete midiRemoteConfig; midiRemoteConfig = nullptr; } #ifdef BUILD_EXPERIMENTAL if(midiRhythmGenerator) { delete midiRhythmGenerator; midiRhythmGenerator = 0; } #endif if(midiTransformerDialog) { delete midiTransformerDialog; midiTransformerDialog = nullptr; } if(routeDialog) { delete routeDialog; routeDialog = nullptr; } } //--------------------------------------------------------- // configAppearance //--------------------------------------------------------- void MusE::configAppearance() { if (!appearance) { // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). appearance = new MusEGui::Appearance(this); appearance->resetValues(); } if(appearance->isVisible()) { appearance->raise(); appearance->activateWindow(); } else appearance->show(); } //--------------------------------------------------------- // startSnooper //--------------------------------------------------------- void MusE::startSnooper() { if (!_snooperDialog) // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). _snooperDialog = new MusEGui::SnooperDialog(); //_snooperDialog->resetValues(); if(_snooperDialog->isVisible()) { _snooperDialog->raise(); _snooperDialog->activateWindow(); } else _snooperDialog->show(); } //--------------------------------------------------------- // changeConfig // - called whenever configuration has changed // - when configuration has changed by user, call with // writeFlag=true to save configuration in ~/.MusE // simple=true Don't bother with theme, style, // and font etc. updates, just emit the configChanged signal. //--------------------------------------------------------- void MusE::changeConfig(bool writeFlag) { if (writeFlag) writeGlobalConfiguration(); updateConfiguration(); emit configChanged(); } //--------------------------------------------------------- // configMetronome //--------------------------------------------------------- void MusE::configMetronome() { if (!metronomeConfig) { // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). metronomeConfig = new MusEGui::MetronomeConfig; metronomeConfig->show(); return; } if(metronomeConfig->isVisible()) { metronomeConfig->raise(); metronomeConfig->activateWindow(); } else { metronomeConfig->updateValues(); metronomeConfig->show(); } } //--------------------------------------------------------- // configShortCuts //--------------------------------------------------------- void MusE::configShortCuts() { if (!shortcutConfig) { // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). shortcutConfig = new MusEGui::ShortcutConfig(); connect(shortcutConfig, SIGNAL(saveConfig()), SLOT(configShortCutsSaveConfig())); } if(shortcutConfig->isVisible()) { shortcutConfig->raise(); shortcutConfig->activateWindow(); } else shortcutConfig->show(); } //--------------------------------------------------------- // configShortCutsSaveConfig //--------------------------------------------------------- void MusE::configShortCutsSaveConfig() { // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. changeConfig(true); } //--------------------------------------------------------- // bounceToTrack //--------------------------------------------------------- void MusE::bounceToTrack(MusECore::AudioOutput* ao) { if (MusEGlobal::audio->bounce()) return; MusEGlobal::song->bounceOutput = nullptr; MusEGlobal::song->bounceTrack = nullptr; if(MusEGlobal::song->waves()->empty()) { QMessageBox::critical(this, tr("MusE: Record Downmix to Track"), tr("No wave tracks found") ); return; } MusECore::OutputList* ol = MusEGlobal::song->outputs(); if(ol->empty()) { QMessageBox::critical(this, tr("MusE: Record Downmix to Track"), tr("No audio output tracks found") ); return; } if(checkRegionNotNull()) return; MusECore::AudioOutput* out = nullptr; // If only one output, pick it, else pick the first selected. if (ao) out = ao; else if(ol->size() == 1) out = ol->front(); else { for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) { MusECore::AudioOutput* o = *iao; if(o->selected()) { if(out) { out = nullptr; break; } out = o; } } if(!out) { QMessageBox::critical(this, tr("MusE: Record Downmix to Track"), tr("Select one audio output track,\nand one target wave track") ); return; } } // search target track MusECore::TrackList* tl = MusEGlobal::song->tracks(); MusECore::WaveTrack* track = nullptr; for (MusECore::iTrack it = tl->begin(); it != tl->end(); ++it) { MusECore::Track* t = *it; if (t->selected()) { if(t->type() != MusECore::Track::WAVE && t->type() != MusECore::Track::AUDIO_OUTPUT) { track = nullptr; break; } if(t->type() == MusECore::Track::WAVE) { if(track) { track = nullptr; break; } track = (MusECore::WaveTrack*)t; } } } if (track == nullptr) { if(ol->size() == 1) { QMessageBox::critical(this, tr("MusE: Record Downmix to Track"), tr("Select one target wave track") ); return; } else { QMessageBox::critical(this, tr("MusE: Record Downmix to Track"), tr("Select one target wave track,\nand one audio output track") ); return; } } // Switch all wave converters to offline settings mode. MusEGlobal::song->setAudioConvertersOfflineOperation(true); // This will wait a few cycles until freewheel is set and a seek is done. MusEGlobal::audio->msgBounce(); MusEGlobal::song->bounceOutput = out; MusEGlobal::song->bounceTrack = track; MusEGlobal::song->setRecord(true); MusEGlobal::song->setRecordFlag(track, true); track->prepareRecording(); MusEGlobal::song->setPlay(true); } //--------------------------------------------------------- // bounceToFile //--------------------------------------------------------- void MusE::bounceToFile(MusECore::AudioOutput* ao) { if(MusEGlobal::audio->bounce()) return; MusEGlobal::song->bounceOutput = nullptr; MusEGlobal::song->bounceTrack = nullptr; if(!ao) { MusECore::OutputList* ol = MusEGlobal::song->outputs(); if(ol->empty()) { QMessageBox::critical(this, tr("MusE: Record Downmix to File"), tr("No audio output tracks found") ); return; } // If only one output, pick it, else pick the first selected. if(ol->size() == 1) ao = ol->front(); else { for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) { MusECore::AudioOutput* o = *iao; if(o->selected()) { if(ao) { ao = nullptr; break; } ao = o; } } if (ao == nullptr) { QMessageBox::critical(this, tr("MusE: Record Downmix to File"), tr("Select one audio output track") ); return; } } } if (checkRegionNotNull()) return; MusECore::SndFile* sf = MusECore::getSndFile(nullptr, this); if (sf == nullptr) return; // Switch all wave converters to offline settings mode. MusEGlobal::song->setAudioConvertersOfflineOperation(true); // This will wait a few cycles until freewheel is set and a seek is done. MusEGlobal::audio->msgBounce(); MusEGlobal::song->bounceOutput = ao; ao->setRecFile(sf); if(MusEGlobal::debugMsg) fprintf(stderr, "ao->setRecFile %p\n", sf); MusEGlobal::song->setRecord(true, false); MusEGlobal::song->setRecordFlag(ao, true); ao->prepareRecording(); MusEGlobal::song->setPlay(true); } //--------------------------------------------------------- // checkRegionNotNull // return true if (rPos - lPos) <= 0 //--------------------------------------------------------- bool MusE::checkRegionNotNull() { int start = MusEGlobal::song->lPos().frame(); int end = MusEGlobal::song->rPos().frame(); if (end - start <= 0) { QMessageBox::critical(this, tr("Render Downmix"), tr("Set left and right markers for downmix range") ); return true; } return false; } #ifdef HAVE_LASH //--------------------------------------------------------- // lash_idle_cb //--------------------------------------------------------- void MusE::lash_idle_cb () { lash_event_t * event; if (!lash_client) return; while ( (event = lash_get_event (lash_client)) ) { switch (lash_event_get_type (event)) { case LASH_Save_File: { /* save file */ QString ss = QString(lash_event_get_string(event)) + QString("/lash-project-muse.med"); int ok = save (ss.toLatin1(), false, true); if (ok) { project.setFile(ss.toLatin1()); _lastProjectFilePath = ss.toLatin1(); _lastProjectWasTemplate = false; _lastProjectLoadedConfig = true; setWindowTitle(tr("MusE: Song: %1").arg(MusEGui::projectTitleFromFilename(project.absoluteFilePath()))); addProjectToRecentList(ss.toLatin1()); MusEGlobal::museProject = QFileInfo(ss.toLatin1()).absolutePath(); QDir::setCurrent(MusEGlobal::museProject); } lash_send_event (lash_client, event); } break; case LASH_Restore_File: { /* load file */ QString sr = QString(lash_event_get_string(event)) + QString("/lash-project-muse.med"); loadProjectFile(sr.toLatin1(), false, true); lash_send_event (lash_client, event); } break; case LASH_Quit: { /* quit muse */ std::cout << "MusE::lash_idle_cb Received LASH_Quit" << std::endl; lash_event_destroy (event); } break; default: { std::cout << "MusE::lash_idle_cb Received unknown LASH event of type " << lash_event_get_type (event) << std::endl; lash_event_destroy (event); } break; } } } #endif /* HAVE_LASH */ //--------------------------------------------------------- // clearSong // return true if operation aborted // called with sequencer stopped // If clear_all is false, it will not touch things like midi ports. //--------------------------------------------------------- bool MusE::clearSong(bool clear_all) { if (MusEGlobal::song->dirty) { int n = 0; n = QMessageBox::warning(this, appName, tr("The current project contains unsaved data.\n" "Load overwrites current project.\n" "Save current project?"), tr("&Save"), tr("S&kip"), tr("&Abort"), 0, 2); switch (n) { case 0: if (!save()) // abort if save failed return true; break; case 1: break; case 2: return true; default: fprintf(stderr, "InternalError: gibt %d\n", n); } } if (MusEGlobal::audio->isPlaying()) { MusEGlobal::audio->msgPlay(false); while (MusEGlobal::audio->isPlaying()) qApp->processEvents(); } microSleep(100000); again: for (const auto& i : toplevels) { MusEGui::TopWin* tl = i; switch (tl->type()) { case MusEGui::TopWin::ARRANGER: break; case MusEGui::TopWin::PIANO_ROLL: case MusEGui::TopWin::SCORE: // case MusEGui::TopWin::LISTE: case MusEGui::TopWin::DRUM: case MusEGui::TopWin::MASTER: case MusEGui::TopWin::WAVE: { if(tl->isVisible()) // Don't keep trying to close, only if visible. { if(!tl->close()) fprintf(stderr, "MusE::clearSong TopWin did not close!\n"); goto again; } } case MusEGui::TopWin::TOPLEVELTYPE_LAST_ENTRY: //to avoid a warning break; } } closeDocks(); microSleep(100000); _arranger->songIsClearing(); MusEGlobal::song->clear(true, clear_all); microSleep(100000); return false; } //--------------------------------------------------------- // startEditInstrument //--------------------------------------------------------- void MusE::startEditInstrument(const QString& find_instrument, EditInstrumentTabType show_tab) { if(editInstrument == nullptr) { editInstrument = new MusEGui::EditInstrument(this); editInstrument->show(); editInstrument->findInstrument(find_instrument); editInstrument->showTab(show_tab); } else { if(! editInstrument->isHidden()) editInstrument->hide(); else { editInstrument->show(); editInstrument->findInstrument(find_instrument); editInstrument->showTab(show_tab); } } } //--------------------------------------------------------- // switchMixerAutomation //--------------------------------------------------------- void MusE::switchMixerAutomation() { // Could be intensive, try idling instead of a single message. MusEGlobal::audio->msgIdle(true); MusEGlobal::automation = ! MusEGlobal::automation; // Clear all pressed and touched and rec event lists. MusEGlobal::song->clearRecAutomation(true); // If going to OFF mode, need to update current 'manual' values from the automation values at this time... if(!MusEGlobal::automation) { MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); ++i) { if ((*i)->isMidiTrack()) continue; MusECore::AudioTrack* track = static_cast(*i); if(track->automationType() != MusECore::AUTO_OFF) // && track->automationType() != MusECore::AUTO_WRITE) track->controller()->updateCurValues(MusEGlobal::audio->curFramePos()); } } MusEGlobal::audio->msgIdle(false); // REMOVE Tim. automation. Removed. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. // autoMixerAction->setChecked(MusEGlobal::automation); } //--------------------------------------------------------- // clearAutomation //--------------------------------------------------------- void MusE::clearAutomation() { QMessageBox::StandardButton b = QMessageBox::warning(this, appName, tr("This will clear all automation data on\n all audio tracks!\nProceed?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); if(b != QMessageBox::Ok) return; // Could be intensive, try idling instead of a single message. MusEGlobal::audio->msgIdle(true); MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); ++i) { if ((*i)->isMidiTrack()) continue; static_cast(*i)->controller()->clearAllAutomation(); } MusEGlobal::audio->msgIdle(false); } //--------------------------------------------------------- // takeAutomationSnapshot //--------------------------------------------------------- void MusE::takeAutomationSnapshot() { QMessageBox::StandardButton b = QMessageBox::warning(this, appName, tr("This takes an automation snapshot of\n all controllers on all audio tracks,\n" " at the current position.\nProceed?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); if(b != QMessageBox::Ok) return; // Could be intensive, try idling instead of a single message. MusEGlobal::audio->msgIdle(true); int frame = MusEGlobal::audio->curFramePos(); MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); ++i) { if ((*i)->isMidiTrack()) continue; MusECore::AudioTrack* track = static_cast(*i); MusECore::CtrlListList* cll = track->controller(); // Need to update current 'manual' values from the automation values at this time. if(track->automationType() != MusECore::AUTO_OFF) // && track->automationType() != MusECore::AUTO_WRITE) cll->updateCurValues(frame); for (MusECore::iCtrlList icl = cll->begin(); icl != cll->end(); ++icl) { double val = icl->second->curVal(); icl->second->add(frame, val); } } MusEGlobal::audio->msgIdle(false); } //--------------------------------------------------------- // updateConfiguration // called whenever the configuration has changed //--------------------------------------------------------- void MusE::updateConfiguration() { fileOpenAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN].key); fileNewAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_NEW].key); fileNewFromTemplateAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_NEW_FROM_TEMPLATE].key); fileSaveAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_SAVE].key); fileSaveAsAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_SAVE_AS].key); fileSaveAsNewProjectAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_SAVE_AS_NEW_PROJECT].key); fileSaveRevisionAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_SAVE_REVISION].key); fileSaveAsTemplateAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_SAVE_AS_TEMPLATE].key); //menu_file->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_RECENT].key, menu_ids[CMD_OPEN_RECENT]); // Not used. fileImportMidiAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_IMPORT_MIDI].key); fileExportMidiAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_EXPORT_MIDI].key); fileImportPartAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_IMPORT_PART].key); fileImportWaveAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_IMPORT_AUDIO].key); quitAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_QUIT].key); //menu_file->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_LOAD_TEMPLATE].key, menu_ids[CMD_LOAD_TEMPLATE]); // Not used. if(MusEGlobal::undoAction) MusEGlobal::undoAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_UNDO].key); if(MusEGlobal::redoAction) MusEGlobal::redoAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_REDO].key); //editSongInfoAction has no acceleration viewTransportAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_TRANSPORT].key); viewBigtimeAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_BIGTIME].key); viewMixerAAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_MIXER].key); viewMixerBAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_MIXER2].key); //viewCliplistAction has no acceleration masterGraphicAction->setShortcut(shortcuts[MusEGui::SHRT_OPEN_GRAPHIC_MASTER].key); masterListAction->setShortcut(shortcuts[MusEGui::SHRT_OPEN_LIST_MASTER].key); viewMarkerAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_MARKER].key); // midiEditInstAction does not have acceleration midiResetInstAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_RESET].key); midiInitInstActions->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_INIT].key); midiLocalOffAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_LOCAL_OFF].key); midiTrpAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_INPUT_TRANSPOSE].key); midiInputTrfAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_INPUT_TRANSFORM].key); midiInputFilterAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_INPUT_FILTER].key); midiRemoteAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIDI_REMOTE_CONTROL].key); #ifdef BUILD_EXPERIMENTAL midiRhythmAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_RANDOM_RHYTHM_GENERATOR].key); #endif audioBounce2TrackAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_AUDIO_BOUNCE_TO_TRACK].key); audioBounce2FileAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_AUDIO_BOUNCE_TO_FILE].key); audioRestartAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_AUDIO_RESTART].key); // REMOVE Tim. automation. Removed. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. // autoMixerAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIXER_AUTOMATION].key); autoSnapshotAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIXER_SNAPSHOT].key); autoClearAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_MIXER_AUTOMATION_CLEAR].key); settingsGlobalAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_GLOBAL_CONFIG].key); //settingsShortcutsAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_CONFIG_SHORTCUTS].key); // This is global now, handled in MusE::kbAccel settingsMetronomeAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_CONFIG_METRONOME].key); settingsMidiSyncAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_CONFIG_MIDISYNC].key); // settingsMidiIOAction does not have acceleration settingsAppearanceAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_APPEARANCE_SETTINGS].key); settingsMidiPortAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_CONFIG_MIDI_PORTS].key); dontFollowAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FOLLOW_NO].key); followPageAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FOLLOW_JUMP].key); followCtsAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FOLLOW_CONTINUOUS].key); helpManualAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_OPEN_HELP].key); fullscreenAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_FULLSCREEN].key); toggleDocksAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_DOCKS].key); //rewindOnStopAction->setShortcut(MusEGui::shortcuts[MusEGui::SHRT_TOGGLE_REWINDONSTOP].key); moved to global shortcuts in MusE::kbAccel //arrangerView->updateMusEGui::Shortcuts(); //commented out by flo: is done via signal updateStatusBar(); } //--------------------------------------------------------- // showBigtime //--------------------------------------------------------- void MusE::showBigtime(bool on) { if (on && bigtime == nullptr) { bigtime = new MusEGui::BigTime(this); bigtime->setPos(0, MusEGlobal::song->cpos(), false); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), bigtime, SLOT(setPos(int, unsigned, bool))); connect(MusEGlobal::muse, SIGNAL(configChanged()), bigtime, SLOT(configChanged())); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), bigtime, SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(bigtime, SIGNAL(closed()), SLOT(bigtimeClosed())); } if (bigtime) { bigtime->resize(MusEGlobal::config.geometryBigTime.size()); bigtime->move(MusEGlobal::config.geometryBigTime.topLeft()); bigtime->setVisible(on); } viewBigtimeAction->setChecked(on); } //--------------------------------------------------------- // toggleBigTime //--------------------------------------------------------- void MusE::toggleBigTime(bool checked) { showBigtime(checked); } //--------------------------------------------------------- // bigtimeClosed //--------------------------------------------------------- void MusE::bigtimeClosed() { viewBigtimeAction->setChecked(false); } //--------------------------------------------------------- // showMixer1 //--------------------------------------------------------- void MusE::showMixer1(bool on) { if (on && mixer1 == nullptr) { mixer1 = new MusEGui::AudioMixerApp(this, &(MusEGlobal::config.mixer1)); connect(mixer1, SIGNAL(closed()), SLOT(mixer1Closed())); mixer1->setGeometry(MusEGlobal::config.mixer1.geometry); } if (mixer1) mixer1->setVisible(on); viewMixerAAction->setChecked(on); } //--------------------------------------------------------- // showMixer2 //--------------------------------------------------------- void MusE::showMixer2(bool on) { if (on && mixer2 == nullptr) { mixer2 = new MusEGui::AudioMixerApp(this, &(MusEGlobal::config.mixer2)); connect(mixer2, SIGNAL(closed()), SLOT(mixer2Closed())); mixer2->setGeometry(MusEGlobal::config.mixer2.geometry); } if (mixer2) mixer2->setVisible(on); viewMixerBAction->setChecked(on); } //--------------------------------------------------------- // toggleMixer1 //--------------------------------------------------------- void MusE::toggleMixer1(bool checked) { showMixer1(checked); } //--------------------------------------------------------- // toggleMixer2 //--------------------------------------------------------- void MusE::toggleMixer2(bool checked) { showMixer2(checked); } //--------------------------------------------------------- // mixer1Closed //--------------------------------------------------------- void MusE::mixer1Closed() { viewMixerAAction->setChecked(false); } //--------------------------------------------------------- // mixer2Closed //--------------------------------------------------------- void MusE::mixer2Closed() { viewMixerBAction->setChecked(false); } QWidget* MusE::mixer1Window() { return mixer1; } QWidget* MusE::mixer2Window() { return mixer2; } QWidget* MusE::transportWindow() { return transport; } QWidget* MusE::bigtimeWindow() { return bigtime; } //--------------------------------------------------------- // findUnusedWaveFiles //--------------------------------------------------------- void MusE::findUnusedWaveFiles() { MusEGui::UnusedWaveFiles unused(MusEGlobal::muse); unused.exec(); } void MusE::focusChanged(QWidget* old, QWidget* now) { if(MusEGlobal::heavyDebugMsg) { fprintf(stderr, "\n"); fprintf(stderr, "focusChanged: old:%p now:%p activeWindow:%p\n", old, now, qApp->activeWindow()); if(old) fprintf(stderr, " old type: %s\n", typeid(*old).name()); if(now) fprintf(stderr, " now type: %s\n", typeid(*now).name()); if (dynamic_cast(now) != nullptr) { QWidget* tmp=dynamic_cast(now)->widget(); if (tmp) fprintf(stderr, " subwin contains %p which is a %s\n", tmp, typeid(*tmp).name()); else fprintf(stderr, " subwin contains NULL\n"); } if(qApp->activeWindow()) { const char *strTid = typeid(qApp->activeWindow()).name(); fprintf(stderr, " activeWindow type: %s\n", strTid); } fprintf(stderr, "\n"); } // NOTE: FYI: This is what is required if, for 'Smart Focus', we try simply calling clearFocus from each relevant control // upon Return/Enter/Escape or whatever. // It's nice to be able to do that, but this was crash-prone and I don't like it. Instead each relevant control connects // signals to slots in the editors which set focus on the canvases AND activate their top windows. // Who knows, this code might be needed in some way. Informational, please keep. Tim. // /* // Allow focus proxy to do its job (if set). if(now == this) { if(mdiArea) { QMdiSubWindow* mw = mdiArea->activeSubWindow(); if(mw && mw->widget()->focusProxy()) // Did we set a focus proxy on the window? //mw->widget()->setFocus(); // Give focus to the window so proxy gets it. mw->widget()->focusProxy()->setFocus(); // Give focus directly to the proxy. } } else if(!now) { QWidget* aw = qApp->activeWindow(); if(aw) { if(aw == this) // Active top-level window is MusE? { if(mdiArea) { QMdiSubWindow* mw = mdiArea->activeSubWindow(); if(mw && mw->widget()->focusProxy()) // Did we set a focus proxy on the window? //mw->widget()->setFocus(); // Give focus to the window so proxy gets it. mw->widget()->focusProxy()->setFocus(); // Give focus directly to the proxy. } } else // Active top-level window is outside the MusE mdi window. { if(aw->focusProxy()) // Did we set a focus proxy on the window? { //aw->setFocus(); // Give focus to the window so proxy gets it. aw->focusProxy()->setFocus(); // Give focus directly to the proxy. if(!aw->focusProxy()->isActiveWindow()) aw->focusProxy()->activateWindow(); } } } } */ QWidget* ptr=now; if (activeTopWin) { if(MusEGlobal::heavyDebugMsg) fprintf(stderr, " activeTopWin: %s\n", typeid(*activeTopWin).name()); activeTopWin->storeInitialState(); } if (currentMenuSharingTopwin && (currentMenuSharingTopwin!=activeTopWin)) { if(MusEGlobal::heavyDebugMsg) fprintf(stderr, " currentMenuSharingTopwin: %s\n", typeid(*currentMenuSharingTopwin).name()); currentMenuSharingTopwin->storeInitialState(); } // if the activated widget is a QMdiSubWindow containing some TopWin if ( (dynamic_cast(ptr) != nullptr) && (dynamic_cast( ((QMdiSubWindow*)ptr)->widget() )!=0) ) { MusEGui::TopWin* tmp = (MusEGui::TopWin*) ((QMdiSubWindow*)ptr)->widget(); if (tmp->initalizing()) { waitingForTopwin=tmp; return; } else { ptr=tmp; // go on. } } while (ptr) { if (MusEGlobal::heavyDebugMsg) fprintf(stderr, "focusChanged: at widget %p with type %s\n",ptr, typeid(*ptr).name()); if ( (dynamic_cast(ptr) != nullptr) || // *ptr is a TopWin or a derived class (ptr==this) ) // the main window is selected break; ptr=dynamic_cast(ptr->parent()); //in the unlikely case that ptr is a QObject, this returns NULL, which stops the loop } MusEGui::TopWin* win=dynamic_cast(ptr); // ptr is either NULL, this or the pointer to a TopWin // if the main win or some deleting topwin is selected, // don't treat that as "none", but also don't handle it if (ptr!=this && (!win || !win->deleting()) ) { // now 'win' is either NULL or the pointer to the active TopWin if (win!=activeTopWin) { activeTopWin=win; emit activeTopWinChanged(activeTopWin); } } } void MusE::activeTopWinChangedSlot(MusEGui::TopWin* win) { if (MusEGlobal::debugMsg) fprintf(stderr, "ACTIVE TOPWIN CHANGED to '%s' (%p)\n", win ? win->windowTitle().toLatin1().data() : "", win); // if ( (win && (win->isMdiWin()==false) && win->sharesToolsAndMenu()) && // ( (mdiArea->currentSubWindow() != nullptr) && (mdiArea->currentSubWindow()->isVisible()==true) ) ) // { // if (MusEGlobal::debugMsg) fprintf(stderr, " that's a menu sharing muse window which isn't inside the MDI area.\n"); // // if a window gets active which a) is a muse window, b) is not a mdi subwin and c) shares menu- and toolbar, // // then unfocus the MDI area and/or the currently active MDI subwin. otherwise you'll be unable to use win's // // tools or menu entries, as whenever you click at them, they're replaced by the currently active MDI subwin's // // menus and toolbars. // // as unfocusing the MDI area and/or the subwin does not work for some reason, we must do this workaround: // // simply focus anything in the main window except the mdi area. // menuBar()->setFocus(Qt::MenuBarFocusReason); // } if (win && (win->sharesToolsAndMenu())) setCurrentMenuSharingTopwin(win); } void MusE::setCurrentMenuSharingTopwin(MusEGui::TopWin* win) { if (win && (win->sharesToolsAndMenu()==false)) { fprintf(stderr, "WARNING: THIS SHOULD NEVER HAPPEN: MusE::setCurrentMenuSharingTopwin() called with a win which does not share (%s)! ignoring...\n", win->windowTitle().toLatin1().data()); return; } if (win!=currentMenuSharingTopwin) { MusEGui::TopWin* previousMenuSharingTopwin = currentMenuSharingTopwin; currentMenuSharingTopwin = nullptr; if (MusEGlobal::debugMsg) fprintf(stderr, "MENU SHARING TOPWIN CHANGED to '%s' (%p)\n", win ? win->windowTitle().toLatin1().data() : "", win); list add_toolbars; if(win) add_toolbars = win->toolbars(); // empty our toolbars if (previousMenuSharingTopwin) { list add_foreign_toolbars; for (list::iterator it = foreignToolbars.begin(); it!=foreignToolbars.end(); ++it) { QToolBar* tb = *it; if(tb) { // Check for existing toolbar with same object name, and replace it. bool found = false; for(list::iterator i_atb = add_toolbars.begin(); i_atb!=add_toolbars.end(); ++i_atb) { QToolBar* atb = *i_atb; if(atb) { if(tb->objectName() == atb->objectName()) { //tb->hide(); if(MusEGlobal::heavyDebugMsg) fprintf(stderr, " inserting toolbar '%s'\n", atb->windowTitle().toLatin1().data()); found = true; insertToolBar(tb, atb); add_foreign_toolbars.push_back(atb); add_toolbars.remove(atb); atb->show(); break; } } } // Remove any toolbar break that may exist before the toolbar - unless there // is a replacement is to be made, in which case leave the break intact. if(!found && toolBarBreak(tb)) { if(MusEGlobal::heavyDebugMsg) fprintf(stderr, " removing break before sharer's toolbar '%s'\n", tb->windowTitle().toLatin1().data()); removeToolBarBreak(tb); } if(MusEGlobal::heavyDebugMsg) fprintf(stderr, " removing sharer's toolbar '%s'\n", tb->windowTitle().toLatin1().data()); removeToolBar(tb); // this does not delete *it, which is good tb->setParent(nullptr); } } foreignToolbars = add_foreign_toolbars; } else { for (list::iterator it = optionalToolbars.begin(); it!=optionalToolbars.end(); ++it) { QToolBar* tb = *it; if (tb) { // Check for existing toolbar with same object name, and replace it. for(list::iterator i_atb = add_toolbars.begin(); i_atb!=add_toolbars.end(); ++i_atb) { QToolBar* atb = *i_atb; if(atb) { if(tb->objectName() == atb->objectName()) { //tb->hide(); if(MusEGlobal::heavyDebugMsg) fprintf(stderr, " inserting toolbar '%s'\n", atb->windowTitle().toLatin1().data()); insertToolBar(tb, atb); foreignToolbars.push_back(atb); add_toolbars.remove(atb); atb->show(); break; } } } if (MusEGlobal::heavyDebugMsg) fprintf(stderr, " removing optional toolbar '%s'\n", tb->windowTitle().toLatin1().data()); removeToolBar(tb); // this does not delete *it, which is good tb->setParent(nullptr); } } } //empty our menu menuBar()->clear(); for (list::iterator it = leadingMenus.begin(); it!=leadingMenus.end(); it++) menuBar()->addMenu(*it); if (win) { const QList& actions=win->menuBar()->actions(); for (QList::const_iterator it=actions.begin(); it!=actions.end(); it++) { if (MusEGlobal::heavyDebugMsg) fprintf(stderr, " adding menu entry '%s'\n", (*it)->text().toLatin1().data()); menuBar()->addAction(*it); } for (list::const_iterator it=add_toolbars.begin(); it!=add_toolbars.end(); ++it) if (*it) { if (MusEGlobal::heavyDebugMsg) fprintf(stderr, " adding toolbar '%s'\n", (*it)->windowTitle().toLatin1().data()); addToolBar(*it); foreignToolbars.push_back(*it); (*it)->show(); } else { if (MusEGlobal::heavyDebugMsg) fprintf(stderr, " adding toolbar break\n"); addToolBarBreak(); foreignToolbars.push_back(nullptr); } } for (list::iterator it = trailingMenus.begin(); it!=trailingMenus.end(); it++) menuBar()->addMenu(*it); currentMenuSharingTopwin=win; if (win) win->restoreMainwinState(); //restore toolbar positions in main window } } void MusE::addMdiSubWindow(QMdiSubWindow* win) { mdiArea->addSubWindow(win); } void MusE::setActiveMdiSubWindow(QMdiSubWindow* win) { mdiArea->setActiveSubWindow(win); } void MusE::shareMenuAndToolbarChanged(MusEGui::TopWin* win, bool val) { if (val) { if ((win == activeTopWin) && (win != currentMenuSharingTopwin)) setCurrentMenuSharingTopwin(win); } else { if (win == currentMenuSharingTopwin) { if (activeTopWin && (win != activeTopWin) && (activeTopWin->sharesToolsAndMenu())) setCurrentMenuSharingTopwin(activeTopWin); else setCurrentMenuSharingTopwin(nullptr); } } } void MusE::topwinMenuInited(MusEGui::TopWin* topwin) { if (topwin == nullptr) return; if (topwin == waitingForTopwin) { if (waitingForTopwin->deleting()) { waitingForTopwin = nullptr; } else { activeTopWin=waitingForTopwin; waitingForTopwin = nullptr; emit activeTopWinChanged(activeTopWin); } } else if (topwin == currentMenuSharingTopwin) { fprintf(stderr, "====== DEBUG ======: topwin's menu got inited AFTER being shared!\n"); if (!topwin->sharesToolsAndMenu()) fprintf(stderr, "====== ======: WTF, now it doesn't share any more?!?\n"); setCurrentMenuSharingTopwin(nullptr); setCurrentMenuSharingTopwin(topwin); } } void MusE::updateWindowMenu() { menuWindows->clear(); // frees memory automatically for (const auto& it : toplevels) { if (it->isMdiWin()) { QAction* temp = menuWindows->addAction(it->windowTitle()); temp->setIcon(it->typeIcon(it->type())); QWidget* tlw = static_cast(it); connect(temp, &QAction::triggered, [this, tlw]() { bringToFront(tlw); } ); if (it->type() == TopWin::ARRANGER) { // should be always on top temp->setShortcut(shortcuts[SHRT_ARRANGER].key); if (toplevels.size() > 1) menuWindows->addSeparator(); } } } bool sep = false; for (const auto& it : toplevels) { if (!it->isMdiWin()) { if (!sep && toplevels.size() > 2) { menuWindows->addSeparator(); sep = true; } QAction* temp = menuWindows->addAction(it->windowTitle()); temp->setIcon(it->typeIcon(it->type())); QWidget* tlw = static_cast(it); connect(temp, &QAction::triggered, [this, tlw]() { bringToFront(tlw); } ); } } } void MusE::resetXrunsCounter() { MusEGlobal::audio->resetXruns(); } bool MusE::startPythonBridge() { #ifdef PYTHON_SUPPORT printf("Starting MusE Pybridge...\n"); return MusECore::startPythonBridge(); #endif return false; } bool MusE::stopPythonBridge() { #ifdef PYTHON_SUPPORT printf("Stopping MusE Pybridge...\n"); return MusECore::stopPythonBridge(); #endif return true; } void MusE::bringToFront(QWidget* widget) { MusEGui::TopWin* win=dynamic_cast(widget); if (!win) return; if (win->isMdiWin()) { win->showMaximized(); mdiArea->setActiveSubWindow(win->getMdiWin()); } else { win->activateWindow(); win->raise(); } activeTopWin=win; emit activeTopWinChanged(win); } void MusE::setFullscreen(bool val) { if (val) showFullScreen(); else showNormal(); } void MusE::toggleRewindOnStop(bool onoff) { MusEGlobal::config.useRewindOnStop = onoff; } list get_all_visible_subwins(QMdiArea* mdiarea) { QList wins = mdiarea->subWindowList(); list result; // always put the arranger at the top of the list, if visible for (QList::iterator it=wins.begin(); it!=wins.end(); it++) if ((*it)->isVisible() && ((*it)->isMinimized()==false)) if (dynamic_cast((*it)->widget())->type()==MusEGui::TopWin::ARRANGER) result.push_back(*it); for (QList::iterator it=wins.begin(); it!=wins.end(); it++) if ((*it)->isVisible() && ((*it)->isMinimized()==false)) if (dynamic_cast((*it)->widget())->type()!=MusEGui::TopWin::ARRANGER) result.push_back(*it); return result; } QString MusE::projectTitle(QString name) { return tr("MusE Project: ") + MusEGui::projectTitleFromFilename(name); } QString MusE::projectTitle() const { return MusEGui::projectTitleFromFilename(project.fileName()); } QString MusE::projectPath() const { return MusEGui::projectPathFromFilename(project.absoluteFilePath()); } QString MusE::projectExtension() const { return MusEGui::projectExtensionFromFilename(project.fileName()); } void MusE::saveTimerSlot() { if (MusEGlobal::config.autoSave == false || MusEGlobal::museProject == MusEGlobal::museProjectInitPath || MusEGlobal::song->dirty == false) { //printf("conditions not met, ignore %d %d\n", MusEGlobal::config.autoSave, MusEGlobal::song->dirty); return; } saveIncrement++; if (saveIncrement > 4) { // printf("five minutes passed %d %d\n", MusEGlobal::config.autoSave, MusEGlobal::song->dirty); // time to see if we are allowed to save, if so. Do if (MusEGlobal::audio->isPlaying() == false) { fprintf(stderr, "Performing autosave\n"); save(project.filePath(), false, writeTopwinState); } else { //printf("isPlaying, can't save\n"); } } } void MusE::toggleTrackArmSelectedTrack() { // If there is only one track selected we toggle it's rec-arm status. int selectedTrackCount = 0; MusECore::WaveTrackList* wtl = MusEGlobal::song->waves(); MusECore::TrackList selectedTracks; for (MusECore::iWaveTrack i = wtl->begin(); i != wtl->end(); ++i) { if((*i)->selected()) { selectedTrackCount++; selectedTracks.push_back(*i); } } MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); for (MusECore::iMidiTrack i = mtl->begin(); i != mtl->end(); ++i) { if((*i)->selected()) { selectedTrackCount++; selectedTracks.push_back(*i); } } if (selectedTrackCount == 1) { // Let's toggle the selected instance. MusECore::PendingOperationList operations; foreach (MusECore::Track *t, selectedTracks) { bool newRecState = !t->recordFlag(); if(!t->setRecordFlag1(newRecState)) continue; operations.add(MusECore::PendingOperationItem(t, newRecState, MusECore::PendingOperationItem::SetTrackRecord)); } MusEGlobal::audio->msgExecutePendingOperations(operations, true); } } //--------------------------------------------------------- // importWave //--------------------------------------------------------- void MusE::importWave() { MusECore::Track* track = _arranger->curTrack(); if (!track || track->type() != MusECore::Track::WAVE) { //just create new wave track and go on... if(MusEGlobal::song) { QAction act(MusEGlobal::song); act.setData(MusECore::Track::WAVE); track = MusEGlobal::song->addNewTrack(&act, nullptr); } if(!track) { QMessageBox::critical(this, QString("MusE"), tr("Failed to import wave track")); return; } } MusECore::AudioPreviewDialog afd(this, MusEGlobal::sampleRate); afd.setDirectory(MusEGlobal::lastWavePath); afd.setWindowTitle(tr("Import Audio File")); /*QString fn = afd.getOpenFileName(MusEGlobal::lastWavePath, MusEGlobal::audio_file_pattern, this, tr("Import Audio File"), 0); */ if(afd.exec() == QFileDialog::Rejected) { return; } QStringList filenames = afd.selectedFiles(); if(filenames.size() < 1) { return; } QString fn = filenames [0]; if (!fn.isEmpty()) { MusEGlobal::lastWavePath = fn; importWaveToTrack(fn); } } //--------------------------------------------------------- // importWaveToTrack //--------------------------------------------------------- bool MusE::importWaveToTrack(QString& name, unsigned tick, MusECore::Track* track) { if (!track) track = _arranger->curTrack(); MusECore::SndFileR f = MusECore::sndFileGetWave(name, true); if (f.isNull()) { fprintf(stderr, "import audio file failed\n"); return true; } track->setChannels(f->channels()); track->resetMeter(); int samples = f->samples(); if (MusEGlobal::sampleRate != f->samplerate()) { QMessageBox mbox(this); mbox.setWindowTitle(tr("Import Wavefile")); mbox.setText(tr("This wave file has a samplerate of %1 Hz,\n" " as opposed to current setting %2 Hz.\n" "A live, real-time samplerate converter can be used on this file.\n" "Or, a copy of the file can be resampled now from %1 Hz to %2 Hz.") .arg(f->samplerate()).arg(MusEGlobal::sampleRate)); mbox.setInformativeText(tr("Do you want to use a converter or resample the file now?")); QPushButton* converter_button = mbox.addButton(tr("Use live converter"), QMessageBox::YesRole); QPushButton* resample_button = mbox.addButton(tr("Resample now"), QMessageBox::NoRole); mbox.addButton(tr("Cancel"), QMessageBox::RejectRole); mbox.setDefaultButton(converter_button); mbox.exec(); if(mbox.clickedButton() != converter_button && mbox.clickedButton() != resample_button) { return true; // this removed f from the stack, dropping refcount maybe to zero and maybe deleting the thing } if(mbox.clickedButton() == converter_button) { samples = f->samplesConverted(); } else if(mbox.clickedButton() == resample_button) { //save project if necessary //copy wave to project's folder, //rename it if there is a duplicate, //resample to project's rate if(MusEGlobal::museProject == MusEGlobal::museProjectInitPath) { if(!MusEGlobal::muse->saveAs()) return true; } QFileInfo fi(f.name()); QString projectPath = MusEGlobal::museProject + QDir::separator(); QString fExt = "wav"; QString fBaseName = fi.baseName(); QString fNewPath = ""; bool bNameIsNotUsed = false; for(int i = 0; i < 1000; i++) { fNewPath = projectPath + fBaseName + ((i == 0) ? "" : QString::number(i)) + "." + fExt; if(!QFile(fNewPath).exists()) { bNameIsNotUsed = true; break; } } if(!bNameIsNotUsed) { QMessageBox::critical(MusEGlobal::muse, tr("Wave import error"), tr("There are too many wave files\n" "of the same base name as imported wave file\n" "Can not continue.")); return true; } SF_INFO sfiNew; sfiNew.channels = f.channels(); sfiNew.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; sfiNew.frames = 0; sfiNew.samplerate = MusEGlobal::sampleRate; sfiNew.seekable = 1; sfiNew.sections = 0; SNDFILE *sfNew = sf_open(fNewPath.toUtf8().constData(), SFM_RDWR, &sfiNew); if(sfNew == nullptr) { QMessageBox::critical(MusEGlobal::muse, tr("Wave import error"), tr("Can't create new wav file in project folder!\n") + sf_strerror(NULL)); return true; } int srErr = 0; SRC_STATE *srState = src_new(SRC_SINC_BEST_QUALITY, sfiNew.channels, &srErr); if(!srState) { QMessageBox::critical(MusEGlobal::muse, tr("Wave import error"), tr("Failed to initialize sample rate converter!")); sf_close(sfNew); QFile(fNewPath).remove(); return true; } float fPeekMax = 1.0f; //if output save file will peek above this walue //it should be normalized later float fNormRatio = 1.0f / fPeekMax; int nTriesMax = 5; int nCurTry = 0; do { QProgressDialog pDlg(MusEGlobal::muse); pDlg.setMinimum(0); pDlg.setMaximum(f.samples()); pDlg.setCancelButtonText(tr("Cancel")); if(nCurTry == 0) { pDlg.setLabelText(tr("Resampling wave file\n" "\"%1\"\n" "from %2 to %3 Hz...") .arg(f.name()).arg(f.samplerate()).arg(sfiNew.samplerate)); } else { pDlg.setLabelText(tr("Output has clipped\n" "Resampling again and normalizing wave file\n" "\"%1\"\n" "Try %2 of %3...") .arg(QFileInfo(fNewPath).fileName()).arg(nCurTry).arg(nTriesMax)); } pDlg.setWindowModality(Qt::WindowModal); src_reset(srState); SRC_DATA sd; sd.src_ratio = ((double)MusEGlobal::sampleRate) / (double)f.samplerate(); sf_count_t szBuf = 8192; float srcBuffer [szBuf]; float dstBuffer [szBuf]; unsigned sChannels = f.channels(); sf_count_t szBufInFrames = szBuf / sChannels; sf_count_t szFInFrames = f.samples(); sf_count_t nFramesRead = 0; sf_count_t nFramesWrote = 0; sd.end_of_input = 0; bool bEndOfInput = false; pDlg.setValue(0); f.seek(0, SEEK_SET); while(sd.end_of_input == 0) { size_t nFramesBuf = 0; if(bEndOfInput) sd.end_of_input = 1; else { nFramesBuf = f.readDirect(srcBuffer, szBufInFrames); if(nFramesBuf == 0) break; nFramesRead += nFramesBuf; } sd.data_in = srcBuffer; sd.data_out = dstBuffer; sd.input_frames = nFramesBuf; sd.output_frames = szBufInFrames; sd.input_frames_used = 0; sd.output_frames_gen = 0; do { if(src_process(srState, &sd) != 0) break; sd.data_in += sd.input_frames_used * sChannels; sd.input_frames -= sd.input_frames_used; if(sd.output_frames_gen > 0) { nFramesWrote += sd.output_frames_gen; //detect maximum peek value; for(unsigned ch = 0; ch < sChannels; ch++) { for(long k = 0; k < sd.output_frames_gen; k++) { dstBuffer [k * sChannels + ch] *= fNormRatio; //normilize if needed float fCurPeek = dstBuffer [k * sChannels + ch]; if(fPeekMax < fCurPeek) { //update maximum peek value fPeekMax = fCurPeek; } } } sf_writef_float(sfNew, dstBuffer, sd.output_frames_gen); } else break; } while(true); pDlg.setValue(nFramesRead); if(nFramesRead >= szFInFrames) { bEndOfInput = true; } if(pDlg.wasCanceled())//free all resources { src_delete(srState); sf_close(sfNew); f.close(); f = nullptr; QFile(fNewPath).remove(); return true; } } pDlg.setValue(szFInFrames); if(fPeekMax > 1.0f) //output has clipped. Normilize it { nCurTry++; sf_seek(sfNew, 0, SEEK_SET); f.seek(0, SEEK_SET); pDlg.setValue(0); fNormRatio = 1.0f / fPeekMax; fPeekMax = 1.0f; } else break; } while(nCurTry <= nTriesMax); src_delete(srState); sf_close(sfNew); f.close(); f = nullptr; //reopen resampled wave again f = MusECore::sndFileGetWave(fNewPath, true); if(!f) { printf("import audio file failed\n"); return true; } samples = f->samples(); } } MusECore::WavePart* part = new MusECore::WavePart((MusECore::WaveTrack *)track); if (tick) part->setTick(tick); else part->setTick(MusEGlobal::song->cpos()); part->setLenFrame(samples); MusECore::Event event(MusECore::Wave); MusECore::SndFileR sf(f); event.setSndFile(sf); event.setSpos(0); event.setLenFrame(samples); part->addEvent(event); part->setName(QFileInfo(f->name()).completeBaseName()); MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddPart, part)); unsigned endTick = part->tick() + part->lenTick(); if (MusEGlobal::song->len() < endTick) MusEGlobal::song->setLen(endTick); return false; } int MusE::arrangerRaster() const { return _arranger->rasterVal(); } int MusE::currentPartColorIndex() const { if(_arranger) return _arranger->currentPartColorIndex(); return 0; } bool MusE::restoreState(const QByteArray &state, int version) { QList docksVisible; for (const auto& d : findChildren()) { if (d->isVisible()) { docksVisible.prepend(d); d->hide(); } } bool ret = QMainWindow::restoreState(state, version); for (const auto& d : findChildren()) { if (d->isVisible()) { if (docksVisible.contains(d)) docksVisible.removeOne(d); else d->hide(); } } for (const auto& d : docksVisible) { d->show(); } // if (!docksVisible.isEmpty()) { // QVector areaDocks; // for (const auto& d : findChildren()) { // if (dockWidgetArea(d) == Qt::BottomDockWidgetArea // && strcmp(d->widget()->metaObject()->className(), "MusEGui::ListEdit") == 0 // && !docksVisible.contains(d)) // areaDocks.prepend(d); // } // if (!(areaDocks.isEmpty() && docksVisible.count() == 1)) { // auto& cur = areaDocks.isEmpty() ? docksVisible.first() : areaDocks.last(); // for (const auto& d : docksVisible) { // if (areaDocks.isEmpty() && d == docksVisible.first()) // continue;; // tabifyDockWidget(cur, d); // QTimer::singleShot(0, [d](){ d->raise(); }); // cur = d; // } // } // } return ret; } void MusE::toggleDocks(bool show) { if (show) { if (!hiddenDocks.isEmpty()) { for (const auto& d : hiddenDocks) d->show(); hiddenDocks.clear(); } } else { hiddenDocks.clear(); for (const auto& d : findChildren()) { if (d->isVisible()) { hiddenDocks.prepend(d); d->hide(); } } } } void MusE::closeDocks() { hiddenDocks.clear(); toggleDocksAction->setChecked(true); for (const auto& d : findChildren()) { if (strcmp(d->widget()->metaObject()->className(), "MusEGui::ListEdit") == 0) d->close(); else if (d->isVisible()) { d->hide(); } } } void MusE::addTabbedDock(Qt::DockWidgetArea area, QDockWidget *dock) { QVector areaDocks; for (const auto& d : findChildren()) { if (dockWidgetArea(d) == area) areaDocks.append(d); } if (areaDocks.empty()) { addDockWidget(area, dock); } else { tabifyDockWidget(areaDocks.last(), dock); // dock->raise(); // doesn't work, Qt problem (kybos) QTimer::singleShot(0, [dock](){ dock->raise(); }); } } void MusE::saveStateTopLevels() { for (const auto& it : toplevels) { if (activeTopWin && (activeTopWin == it)) it->storeInitialState(); it->storeSettings(); } } void MusE::saveStateExtra() { MusEGlobal::config.transportVisible = transport->isVisible(); MusEGlobal::config.geometryTransport.setTopLeft(transport->pos()); // size part not used, transport window has fixed size if (bigtime) { MusEGlobal::config.bigTimeVisible = bigtime->isVisible(); MusEGlobal::config.geometryBigTime.setTopLeft(bigtime->pos()); MusEGlobal::config.geometryBigTime.setSize(bigtime->size()); } if (mixer1) { MusEGlobal::config.mixer1Visible = mixer1->isVisible(); MusEGlobal::config.mixer1.geometry = mixer1->geometry(); } if (mixer2) { MusEGlobal::config.mixer2Visible = mixer2->isVisible(); MusEGlobal::config.mixer2.geometry = mixer2->geometry(); } } void MusE::initStatusBar() { statusBar()->setSizeGripEnabled(false); statusBar()->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); cpuStatusBar = new CpuStatusBar(statusBar()); connect(cpuStatusBar, SIGNAL(resetClicked()), SLOT(resetXrunsCounter())); statusBar()->addPermanentWidget(cpuStatusBar); QString s = QString("%1 | Sample rate: %2Hz | Segment size: %3 | Segment count: %4") .arg(MusEGlobal::audioDevice->driverName()) .arg(MusEGlobal::sampleRate) .arg(MusEGlobal::segmentSize) .arg(MusEGlobal::segmentCount); statusBar()->addWidget(new QLabel(s)); updateStatusBar(); } void MusE::updateStatusBar() { statusBar()->setVisible(MusEGlobal::config.showStatusBar); } void MusE::setStatusBarText(const QString &message, int timeout) { if (MusEGlobal::config.showStatusBar) statusBar()->showMessage(message, timeout); } void MusE::clearStatusBarText() { if (MusEGlobal::config.showStatusBar) statusBar()->clearMessage(); } QMenu* MusE::createPopupMenu() { QMenu* menu= QMainWindow::createPopupMenu(); menu->setObjectName("CheckmarkOnly"); return menu; } } //namespace MusEGui muse-4.0.0/src/muse/app.h000066400000000000000000000410111404105311000151370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: app.h,v 1.34.2.14 2009/11/16 11:29:33 lunar_shuttle Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __APP_H__ #define __APP_H__ #include "config.h" #include "globaldefs.h" #include "cobject.h" #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) #include #endif // Forward declarations: class QCloseEvent; class QMenu; class QToolBar; class QToolButton; class QProgressDialog; class QTimer; class QMdiSubWindow; class MuseMdiArea; class QDockWidget; namespace MusECore { class AudioOutput; class MidiInstrument; class MidiPort; class MidiTrack; class Part; class PartList; class SynthI; class Track; class Undo; class WaveTrack; class Xml; } namespace MusEGui { class Appearance; class Arranger; class ArrangerView; class AudioMixerApp; class BigTime; class ClipListEdit; class EditInstrument; class EditToolBar; class GlobalSettingsConfig; class MRConfig; class MarkerView; class LMaster; class MetronomeConfig; class MidiFileConfig; class MidiFilterConfig; class MidiInputTransformDialog; class MidiSyncConfig; class MidiTransformerDialog; class RhythmGen; class ScoreEdit; class ShortcutConfig; class TopWin; class TopLevelList; class Transport; class VisibleTracks; class RouteDialog; class CpuToolbar; class CpuStatusBar; class SnooperDialog; class MasterEdit; #define MENU_ADD_SYNTH_ID_BASE 0x8000 //--------------------------------------------------------- // MusE //--------------------------------------------------------- class MusE : public QMainWindow { Q_OBJECT enum {CMD_FOLLOW_NO, CMD_FOLLOW_JUMP, CMD_FOLLOW_CONTINUOUS }; //File menu items: enum { CMD_OPEN_RECENT=0, CMD_LOAD_TEMPLATE, CMD_SAVE_AS, CMD_IMPORT_MIDI, CMD_EXPORT_MIDI, CMD_IMPORT_PART, CMD_IMPORT_AUDIO, CMD_QUIT, CMD_OPEN_DRUMS, CMD_OPEN_WAVE, CMD_OPEN_LIST, CMD_OPEN_LIST_MASTER, CMD_GLOBAL_CONFIG, CMD_OPEN_GRAPHIC_MASTER, CMD_OPEN_MIDI_TRANSFORM, CMD_TRANSPOSE, CMD_GLOBAL_CUT, CMD_GLOBAL_INSERT, CMD_GLOBAL_SPLIT, CMD_COPY_RANGE, CMD_CUT_EVENTS, CMD_CONFIG_SHORTCUTS, CMD_CONFIG_METRONOME, CMD_CONFIG_MIDISYNC, CMD_MIDI_FILE_CONFIG, CMD_APPEARANCE_SETTINGS, CMD_CONFIG_MIDI_PORTS, CMD_CONFIG_AUDIO_PORTS, CMD_MIDI_EDIT_INSTRUMENTS, CMD_MIDI_RESET, CMD_MIDI_INIT, CMD_MIDI_LOCAL_OFF, CMD_MIXER_SNAPSHOT, CMD_MIXER_AUTOMATION_CLEAR, CMD_OPEN_HELP, CMD_OPEN_HOMEPAGE, CMD_OPEN_BUG, CMD_START_WHATSTHIS, CMD_AUDIO_BOUNCE_TO_FILE, CMD_AUDIO_BOUNCE_TO_TRACK, CMD_AUDIO_RESTART, CMD_LAST }; // File menu actions QAction *fileSaveAction, *fileOpenAction, *fileNewAction, *fileNewFromTemplateAction; QAction *fileSaveRevisionAction, *fileSaveAsAction, *fileSaveAsNewProjectAction, *fileSaveAsTemplateAction; QAction *fileImportMidiAction, *fileExportMidiAction; QAction *fileImportPartAction, *fileImportWaveAction, *fileMoveWaveFiles, *quitAction; QAction *fileCloseAction; QAction *editSongInfoAction; MuseMdiArea* mdiArea; TopWin* activeTopWin; TopWin* currentMenuSharingTopwin; TopWin* waitingForTopwin; std::list requiredToolbars; //always displayed std::list optionalToolbars; //only displayed when no toolbar-sharing window is active std::list foreignToolbars; //holds a temporary list of the toolbars of a toolbar-sharer std::list leadingMenus; std::list trailingMenus; QList hiddenDocks; // View Menu actions QAction *viewTransportAction, *viewBigtimeAction, *viewMixerAAction, *viewMixerBAction, *viewCliplistAction, *viewMarkerAction; QAction *fullscreenAction, *toggleDocksAction; QAction *masterGraphicAction, *masterListAction; // Midi Menu Actions QAction *midiEditInstAction, *midiResetInstAction, *midiInitInstActions, *midiLocalOffAction; QAction *midiTrpAction, *midiInputTrfAction, *midiInputFilterAction, *midiRemoteAction; #ifdef BUILD_EXPERIMENTAL QAction *midiRhythmAction; #endif // Audio Menu Actions QAction *audioBounce2TrackAction, *audioBounce2FileAction, *audioRestartAction; // Automation Menu Actions // REMOVE Tim. automation. Removed. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. // QAction *autoMixerAction; QAction *autoSnapshotAction, *autoClearAction; // Settings Menu Actions QAction *settingsGlobalAction, *settingsShortcutsAction, *settingsMetronomeAction, *settingsMidiSyncAction; QAction *settingsMidiIOAction, *settingsAppearanceAction, *settingsMidiPortAction; QAction *dontFollowAction, *followPageAction, *followCtsAction; QAction *rewindOnStopAction; // Help Menu Actions QAction *helpManualAction, *helpHomepageAction, *helpReportAction, *helpAboutAction, *helpDidYouKnow, *helpSnooperAction; QString appName; QFileInfo project; QString _lastProjectFilePath; bool _lastProjectWasTemplate; bool _lastProjectLoadedConfig; QToolBar *tools; CpuToolbar* cpuLoadToolbar; CpuStatusBar* cpuStatusBar; // when adding a toolbar to the main window, remember adding it to // either the requiredToolbars or optionalToolbars list! Transport* transport; BigTime* bigtime; EditInstrument* editInstrument; // when adding a menu to the main window, remember adding it to // either the leadingMenus or trailingMenus list! QMenu *menu_file, *menuView, *menuSettings, *menuWindows, *menu_help; QMenu* menu_audio, *menuUtils; QMenu* menu_functions; // *menuScriptPlugins; QMenu* follow; // QMenu* midiInputPlugins; QWidget* midiPortConfig; QWidget* softSynthesizerConfig; MidiSyncConfig* midiSyncConfig; MRConfig* midiRemoteConfig; #ifdef BUILD_EXPERIMENTAL RhythmGen* midiRhythmGenerator; #endif MetronomeConfig* metronomeConfig; MidiFileConfig* midiFileConfig; GlobalSettingsConfig* globalSettingsConfig; MidiFilterConfig* midiFilterConfig; MidiInputTransformDialog* midiInputTransform; ShortcutConfig* shortcutConfig; Appearance* appearance; SnooperDialog* _snooperDialog; AudioMixerApp* mixer1; AudioMixerApp* mixer2; RouteDialog* routeDialog; // NOTE: For deleting parentless dialogs and widgets, please add them to deleteParentlessDialogs(). void deleteParentlessDialogs(); Arranger* _arranger; ToplevelList toplevels; ClipListEdit* clipListEdit; QDockWidget* clipListDock; MarkerView* markerView; QDockWidget* markerDock; LMaster* masterList; QDockWidget* masterListDock; ArrangerView* arrangerView; MidiTransformerDialog* midiTransformerDialog; QMenu* openRecent; QPointer masterEditor; bool writeTopwinState; // Set to restart MusE (almost) from scratch before calling close(). bool _isRestartingApp; bool readMidi(FILE*); void read(MusECore::Xml& xml, bool doReadMidiPorts, bool isTemplate); void processTrack(MusECore::MidiTrack* track); void write(MusECore::Xml& xml, bool writeTopwins) const; // If clear_all is false, it will not touch things like midi ports. bool clearSong(bool clear_all = true); bool save(const QString&, bool overwriteWarn, bool writeTopwins); void setUntitledProject(); void setConfigDefaults(); void readConfigParts(MusECore::Xml& xml); void readToplevels(MusECore::Xml& xml); MusECore::PartList* getMidiPartsToEdit(); MusECore::Part* readPart(MusECore::Xml& xml); bool checkRegionNotNull(); void loadProjectFile1(const QString&, bool songTemplate, bool doReadMidiPorts); // Write global configuration. void writeGlobalConfiguration(int level, MusECore::Xml&) const; // Write song specific configuration. void writeConfiguration(int level, MusECore::Xml&) const; void updateConfiguration(); QString projectTitle(QString name); void toggleTrackArmSelectedTrack(); void centerAndResize(); void closeDocks(); void addTabbedDock(Qt::DockWidgetArea area, QDockWidget *widget); void saveStateExtra(); void saveStateTopLevels(); bool findOpenEditor(const TopWin::ToplevelType type, MusECore::PartList* pl); bool findOpenListEditor(MusECore::PartList* pl); bool filterInvalidParts(const TopWin::ToplevelType type, MusECore::PartList* pl); void updateStatusBar(); void setAndAdjustFonts(); QTimer *saveTimer; QTimer *blinkTimer; QTimer *messagePollTimer; int saveIncrement; timeval lastCpuTime; timespec lastSysTime; float fAvrCpuLoad; int avrCpuLoadCounter; float fCurCpuLoad; signals: void configChanged(); void activeTopWinChanged(MusEGui::TopWin*); void blinkTimerToggled(bool state); private slots: void heartBeat(); void blinkTimerSlot(); void saveTimerSlot(); void messagePollTimerSlot(); void loadProject(); bool save(); void configGlobalSettings(); void quitDoc(); void about(); void aboutQt(); void startHelpBrowser(); void startHomepageBrowser(); void startBugBrowser(); void importMidi(); void importWave(); void importPart(); void exportMidi(); void findUnusedWaveFiles(); void toggleTransport(bool); void toggleBigTime(bool); void toggleMixer1(bool); void toggleMixer2(bool); void configMidiSync(); void configMidiFile(); void configShortCuts(); void configShortCutsSaveConfig(); void configMetronome(); void configAppearance(); void startSongInfo(bool editable=true); void writeGlobalConfiguration() const; void showClipList(bool); void openRecentMenu(); void selectProject(QAction* act); void cmd(int); void startMidiInputPlugin(int); void hideMitPluginTranspose(); void hideMidiInputTransform(); void hideMidiFilterConfig(); void hideMidiRemoteConfig(); #ifdef BUILD_EXPERIMENTAL void hideMidiRhythmGenerator(); #endif void resetMidiDevices(); void initMidiDevices(); void localOff(); void switchMixerAutomation(); void takeAutomationSnapshot(); void clearAutomation(); void bigtimeClosed(); void mixer1Closed(); void mixer2Closed(); void activeTopWinChangedSlot(MusEGui::TopWin*); void setCurrentMenuSharingTopwin(MusEGui::TopWin*); void bringToFront(QWidget* win); void setFullscreen(bool); void setDirty(); void toggleRewindOnStop(bool); void toggleDocks(bool show); public slots: void saveNewRevision(); void saveAsNewProject(); bool saveAs(bool overrideProjectSaveDialog = false); void saveAsTemplate(); void bounceToFile(MusECore::AudioOutput* ao = nullptr); void bounceToTrack(MusECore::AudioOutput* ao = nullptr); void closeEvent(QCloseEvent*event) override; void loadProjectFile(const QString&); void loadProjectFile(const QString&, bool songTemplate, bool doReadMidiPorts); void fileClose(); void toplevelDeleting(MusEGui::TopWin* tl); bool seqRestart(); void loadTemplate(); void loadDefaultTemplate(); void showBigtime(bool); void showMixer1(bool); void showMixer2(bool); void startRouteDialog(); void showMarker(bool); void importMidi(const QString &file); void showDidYouKnowDialogIfEnabled(); void showDidYouKnowDialog(); void startEditInstrument(const QString& find_instrument = QString(), EditInstrumentTabType show_tab = EditInstrumentPatches); void configMidiPorts(); void startEditor(MusECore::PartList*, int); void startScoreQuickly(); void startPianoroll(bool newwin = false); void startPianoroll(MusECore::PartList* pl, bool showDefaultCtrls = false, bool newwin = false); void startWaveEditor(bool newwin = false); void startWaveEditor(MusECore::PartList*, bool newwin = false); void openInScoreEdit(ScoreEdit* destination, MusECore::PartList* pl, bool allInOne=false); void openInScoreEdit(ScoreEdit* destination, bool allInOne=false); void openInScoreEdit_allInOne(QWidget* destination); void openInScoreEdit_oneStaffPerTrack(QWidget* destination); void startMasterEditor(); void showMasterList(bool); void startListEditor(bool newwin = false); void startListEditor(MusECore::PartList*, bool newwin = false); void startDrumEditor(bool newwin = false); void startDrumEditor(MusECore::PartList* pl, bool showDefaultCtrls = false, bool newwin = false); void startEditor(MusECore::Track*); void startMidiTransformer(); void startSnooper(); void focusChanged(QWidget* old, QWidget* now); void addMdiSubWindow(QMdiSubWindow*); void shareMenuAndToolbarChanged(MusEGui::TopWin*, bool); void topwinMenuInited(MusEGui::TopWin*); void setActiveMdiSubWindow(QMdiSubWindow*); void updateWindowMenu(); void resetXrunsCounter(); bool startPythonBridge(); bool stopPythonBridge(); void addProjectToRecentList(const QString& name); void saveProjectRecentList(); public: MusE(); void populateAddTrack(); // Loads a default song according to settings, or filename_override if not empty. // If filename_override is not empty, use_template and load_config are used. void loadDefaultSong(const QString& filename_override, bool use_template, bool load_config); bool loadConfigurationColors(QWidget* parent = 0); bool saveConfigurationColors(QWidget* parent = 0); // Whether to restart MusE (almost) from scratch when calling close(). bool restartingApp() const { return _isRestartingApp;} // Set to restart MusE (almost) from scratch before calling close(). void setRestartingApp(bool v) { _isRestartingApp = v;} Arranger* arranger() const { return _arranger; } int arrangerRaster() const; int currentPartColorIndex() const; ArrangerView* getArrangerView() const { return arrangerView; } QRect configGeometryMain; QProgressDialog *progress; bool importMidi(const QString name, bool merge); void kbAccel(int); // writeFlag: Write to configuration file. void changeConfig(bool writeFlag); void seqStop(); bool seqStart(); // Starts the ALSA midi portion of the sequencer. audioDevice must be valid. // Returns true if successful or already running. // bool seqStartMidi(); void setHeartBeat(); void stopHeartBeat(); void importController(int, MusECore::MidiPort*, int); const QFileInfo& projectFileInfo() const { return project; } QString lastProjectFilePath() const { return _lastProjectFilePath; }; bool lastProjectWasTemplate() const { return _lastProjectWasTemplate; } bool lastProjectLoadedConfig() const { return _lastProjectLoadedConfig; } QString projectTitle() const; QString projectPath() const; QString projectExtension() const; QWidget* mixer1Window(); QWidget* mixer2Window(); QWidget* transportWindow(); QWidget* bigtimeWindow(); bool importWaveToTrack(QString& name, unsigned tick=0, MusECore::Track* track=NULL); void importPartToTrack(QString& filename, unsigned tick, MusECore::Track* track); void showTransport(bool flag); bool restoreState(const QByteArray &state, int version = 0); const ToplevelList* getToplevels() { return &toplevels; } TopWin* getCurrentMenuSharingTopwin() { return currentMenuSharingTopwin; } float getCPULoad(); void initStatusBar(); void setStatusBarText(const QString &message, int timeout = 0); void clearStatusBarText(); void launchBrowser(QString &whereTo); QMenu* createPopupMenu() override; #ifdef HAVE_LASH void lash_idle_cb (); #endif }; extern void addProject(const QString& name); } // namespace MusEGui #endif // __APP_H__ muse-4.0.0/src/muse/arranger/000077500000000000000000000000001404105311000160125ustar00rootroot00000000000000muse-4.0.0/src/muse/arranger/CMakeLists.txt000066400000000000000000000037021404105311000205540ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= # # Expand Qt macros # QT5_WRAP_CPP (arranger_mocs arranger.h arrangerview.h pcanvas.h tlist.h ) # # List of source files to compile # file (GLOB arranger_source_files arranger.cpp arrangerview.cpp pcanvas.cpp structure.cpp tlist.cpp ) # # Define target # add_library ( arranger ${MODULES_BUILD} ${arranger_source_files} ${arranger_mocs} ) # # Append to the list of translations # set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${arranger_source_files} CACHE INTERNAL "" ) # # Compilation flags and target name # set_target_properties( arranger PROPERTIES OUTPUT_NAME muse_arranger ) # # Linkage # target_link_libraries ( arranger ${QT_LIBRARIES} mixer widgets ) # # Install location # if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS arranger DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/arranger/alayout.cpp000066400000000000000000000130701404105311000201750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: alayout.cpp,v 1.8 2004/02/28 14:58:24 wschweer Exp $ // (C) Copyright 2002 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "alayout.h" #include "widget_stack.h" #include "scrollbar.h" #include "splitter.h" #include #include #include #include namespace MusEGui { TLLayout::TLLayout(QWidget *parent, WidgetStack* stack, ScrollBar* sb, Splitter* splitter) : QLayout(parent), _stack(stack), _sb(sb), _splitter(splitter) { _inSetGeometry = false; setContentsMargins(0, 0, 0, 0); setSpacing(0); _stackLi = new QWidgetItem(_stack); _sbLi = new QWidgetItem(_sb); } void TLLayout::setGeometry(const QRect &rect) { if(_inSetGeometry) return; int w = rect.width(); int h = rect.height(); QSize s0; QWidget* widget = _stack->visibleWidget(); if(widget) { s0 = widget->minimumSizeHint(); if (!s0.isValid()) // widget has no geometry management s0 = widget->size(); } else s0 = _stack->minimumSizeHint(); int y2 = h; int range = s0.height() - y2; if (range < 0) range = 0; //fprintf(stderr, "TrackInfoLayout::setGeometry h:%d s0 height:%d range:%d y2:%d\n", // h, s0.height(), range, y2); if (range) _sb->setMaximum(range); const bool vis = range != 0; if(_sb->isVisible() != vis) { if(_sb->isVisible()) { const int sbw = _sb->width(); _sb->setVisible(false); if(_splitter) { _inSetGeometry = true; _splitter->setPosition(1, w - sbw); _inSetGeometry = false; } _stackLi->setGeometry(QRect(0, 0, w - sbw, y2)); if(widget) { QSize r(w - sbw, y2 < s0.height() ? s0.height() : y2); //fprintf(stderr, "TrackInfoLayout::setGeometry hide sb: widget w:%d\n", // r.width()); widget->setGeometry(0, -_sb->value(), r.width(), r.height()); } } else { _sb->setVisible(true); if(_splitter) { _inSetGeometry = true; _splitter->setPosition(1, w + _sb->width()); _inSetGeometry = false; } _stackLi->setGeometry(QRect(0, 0, w, y2)); if(widget) { QSize r(w, y2 < s0.height() ? s0.height() : y2); //fprintf(stderr, "TrackInfoLayout::setGeometry show sb: widget w:%d\n", // r.width()); widget->setGeometry(0, -_sb->value(), r.width(), r.height()); } } } else { const int ww = _sb->isVisible() ? w - _sb->width() : w; _stackLi->setGeometry(QRect(0, 0, ww, y2)); if(widget) { QSize r(ww, y2 < s0.height() ? s0.height() : y2); //fprintf(stderr, "TrackInfoLayout::setGeometry not show/hide sb: widget w:%d\n", // r.width()); widget->setGeometry(0, -_sb->value(), r.width(), r.height()); } } if(widget) _sbLi->setGeometry(QRect(widget->width(), 0, _sbLi->sizeHint().width(), y2)); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize TLLayout::sizeHint() const { return QSize(150, 100); } QSize TLLayout::minimumSize() const { int w = _stack->minimumSizeHint().width(); if(_sb->isVisible()) w += _sbLi->sizeHint().width(); return QSize(w, 50); } //--------------------------------------------------------- // maximumSize //--------------------------------------------------------- QSize TLLayout::maximumSize() const { return QSize(440, 100000); } //--------------------------------------------------------- // itemAt //--------------------------------------------------------- QLayoutItem* TLLayout::itemAt(int i) const { switch(i) { case 0: return _stackLi; break; case 1: return _sbLi; break; //case 2: return _buttonLi; break; } return 0; } //--------------------------------------------------------- // takeAt //--------------------------------------------------------- QLayoutItem* TLLayout::takeAt(int i) { switch(i) { case 0: return _stackLi; break; case 1: return _sbLi; break; } return 0; } //--------------------------------------------------------- // clear //--------------------------------------------------------- void TLLayout::clear() { delete _stackLi; _stackLi = 0; delete _sbLi; _sbLi = 0; } } // namespace MusEGui muse-4.0.0/src/muse/arranger/alayout.h000066400000000000000000000045711404105311000176500ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: alayout.h,v 1.3.2.1 2008/01/19 13:33:46 wschweer Exp $ // (C) Copyright 2002 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ALAYOUT_H__ #define __ALAYOUT_H__ #include #include class QLayoutItem; class QWidget; namespace MusEGui { class WidgetStack; class ScrollBar; class Splitter; //--------------------------------------------------------- // TLLayout // arranger trackList layout manager //--------------------------------------------------------- class TLLayout : public QLayout { Q_OBJECT bool _inSetGeometry; WidgetStack* _stack; ScrollBar* _sb; Splitter* _splitter; // This is not actually in the layout, but used and/or adjusted anyway. QLayoutItem* _stackLi; QLayoutItem* _sbLi; public: static const int numItems = 2; TLLayout(QWidget *parent, WidgetStack* stack, ScrollBar* sb, Splitter* splitter); ~TLLayout() { clear(); } void addItem(QLayoutItem*) { } // Do nothing, it's a custom layout. virtual Qt::Orientations expandingDirections() const { return 0; } virtual bool hasHeightForWidth() const { return false; } virtual int count() const { return numItems; } void clear(); virtual QSize sizeHint() const; virtual QSize minimumSize() const; virtual QSize maximumSize() const; virtual void setGeometry(const QRect &rect); virtual QLayoutItem* itemAt(int) const; virtual QLayoutItem* takeAt(int); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/arranger/arranger.cpp000066400000000000000000001611301404105311000203210ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: arranger.cpp,v 1.33.2.21 2009/11/17 22:08:22 terminator356 Exp $ // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "config.h" #include #include #include "muse_math.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arranger.h" #include "song.h" #include "app.h" #include "midiport.h" #include "mididev.h" #include "utils.h" #include "globals.h" #include "globaldefs.h" #include "icons.h" #include "utils.h" #include "widget_stack.h" #include "audio.h" #include "event.h" #include "midiseq.h" #include "midictrl.h" #include "mpevent.h" #include "gconfig.h" #include "mixer/astrip.h" #include "mixer/mstrip.h" #include "shortcuts.h" #include "ttoolbutton.h" // Forwards from header: #include #include #include #include #include #include #include #include #include "track.h" #include "part.h" #include "xml.h" #include "rasterizer.h" #include "lcombo.h" #include "mtscale.h" #include "arrangerview.h" #include "astrip.h" #include "header.h" #include "poslabel.h" #include "scrollscale.h" #include "spinbox.h" #include "splitter.h" #include "trackinfo_layout.h" #include "tlist.h" #include "raster_widgets.h" #include "pcanvas.h" namespace MusEGui { std::vector Arranger::custom_columns; QByteArray Arranger::header_state; void Arranger::writeCustomColumns(int level, MusECore::Xml& xml) { xml.tag(level++, "custom_columns"); for (unsigned i = 0; i < custom_columns.size(); i++) { xml.tag(level++, "column"); xml.strTag(level, "name", custom_columns[i].name); xml.intTag(level, "ctrl", custom_columns[i].ctrl); xml.intTag(level, "affected_pos", custom_columns[i].affected_pos); xml.etag(--level, "column"); } xml.etag(--level, "custom_columns"); } void Arranger::readCustomColumns(MusECore::Xml& xml) { custom_columns.clear(); for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "column") custom_columns.push_back(readOneCustomColumn(xml)); else xml.unknown("Arranger::readCustomColumns"); break; case MusECore::Xml::TagEnd: if (tag == "custom_columns") { return; } default: break; } } } Arranger::custom_col_t Arranger::readOneCustomColumn(MusECore::Xml& xml) { custom_col_t temp(0, "-"); for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return temp; case MusECore::Xml::TagStart: if (tag == "name") temp.name=xml.parse1(); else if (tag == "ctrl") temp.ctrl=xml.parseInt(); else if (tag == "affected_pos") temp.affected_pos=(custom_col_t::affected_pos_t)xml.parseInt(); else xml.unknown("Arranger::readOneCustomColumn"); break; case MusECore::Xml::TagEnd: if (tag == "column") return temp; default: break; } } return temp; } //--------------------------------------------------------- // Arranger::setHeaderToolTips //--------------------------------------------------------- void Arranger::setHeaderToolTips() { header->setToolTip(TList::COL_TRACK_IDX, tr("Track index")); header->setToolTip(TList::COL_INPUT_MONITOR, tr("Input monitor")); header->setToolTip(TList::COL_RECORD, tr("Recording")); header->setToolTip(TList::COL_MUTE, tr("Mute/Off indicator")); header->setToolTip(TList::COL_SOLO, tr("Solo indicator")); header->setToolTip(TList::COL_CLASS, tr("Track type")); header->setToolTip(TList::COL_NAME, tr("Track name")); header->setToolTip(TList::COL_OCHANNEL, tr("Midi output channel number or number of audio channels")); header->setToolTip(TList::COL_OPORT, tr("Midi output port or synth GUI")); // header->setToolTip(TList::COL_TIMELOCK, tr("Time lock")); header->setToolTip(TList::COL_AUTOMATION, tr("Automation parameter selection")); header->setToolTip(TList::COL_CLEF, tr("Notation clef")); } //--------------------------------------------------------- // Arranger::setHeaderWhatsThis //--------------------------------------------------------- void Arranger::setHeaderWhatsThis() { header->setWhatsThis(TList::COL_TRACK_IDX, tr("Track index")); header->setWhatsThis(TList::COL_INPUT_MONITOR, tr("Enable input monitor. Click to toggle.\nPasses input through to output for monitoring.\n" "See also Settings: Automatically Monitor On Record Arm.")); header->setWhatsThis(TList::COL_RECORD, tr("Enable recording. Click to toggle.\n" "See also Settings: Automatically Monitor On Record Arm.")); header->setWhatsThis(TList::COL_MUTE, tr("Mute indicator. Click to toggle.\nRight-click to toggle track on/off.\nMute is designed for rapid, repeated action.\nOn/Off is not!")); header->setWhatsThis(TList::COL_SOLO, tr("Solo indicator. Click to toggle.\nConnected tracks are also 'phantom' soloed.")); header->setWhatsThis(TList::COL_CLASS, tr("Track type. Right-click to change\n midi and drum track types.")); header->setWhatsThis(TList::COL_NAME, tr("Track name. Double-click to edit.\nRight-click for more options.")); header->setWhatsThis(TList::COL_OCHANNEL, tr("Midi/Drum track: Output channel number.\nAudio track: Channels.\nMid/right-click to change.")); header->setWhatsThis(TList::COL_OPORT, tr("Midi/Drum track: Output port.\nSynth track: Right-click to show GUI.")); // header->setWhatsThis(TList::COL_TIMELOCK, tr("Time lock")); header->setWhatsThis(TList::COL_CLEF, tr("Notation clef. Select this tracks notation clef.")); } //--------------------------------------------------------- // Arranger::setHeaderStatusTips //--------------------------------------------------------- void Arranger::setHeaderStatusTips() { header->setStatusTip(TList::COL_TRACK_IDX, tr("Track index: Double-click to select all tracks (+SHIFT to select all tracks of the same type).")); header->setStatusTip(TList::COL_INPUT_MONITOR, tr("Input monitor: Left click to toggle current/selected, right click for all tracks of same type.")); header->setStatusTip(TList::COL_RECORD, tr("Recording: LMB to toggle current/selected, RMB for all tracks of same type. Audio output: LMB to downmix to a file.")); header->setStatusTip(TList::COL_MUTE, tr("Mute indicator: Left click to mute, right click to switch on/off (+CTRL for all tracks except audio outputs).")); header->setStatusTip(TList::COL_SOLO, tr("Solo indicator: Click to solo (+CTRL for all tracks except audio outputs). Connected tracks are 'phantom' soloed.")); header->setStatusTip(TList::COL_CLASS, tr("Track type (RMB for context menu): MIDI: Switch track types. Synth: Open GUI. Audio output: Downmix.")); header->setStatusTip(TList::COL_NAME, tr("Track name: Double-click to edit. RMB for context menu.")); header->setStatusTip(TList::COL_OCHANNEL, tr("Midi/Drum: Output channel number. Audio: Number of channels. MMB / (CTRL/SHIFT+)RMB / Double-click to change.")); header->setStatusTip(TList::COL_OPORT, tr("Midi/Drum: RMB to set the output port (+CTRL for all tracks of same type). Synth: RMB to show synth GUI.")); // header->setStatusTip(TList::COL_TIMELOCK, tr("Time lock")); header->setStatusTip(TList::COL_AUTOMATION, tr("Automation: RMB to select parameters.")); header->setStatusTip(TList::COL_CLEF, tr("Notation clef: RMB to select this track's notation clef.")); } //--------------------------------------------------------- // Arranger // is the central widget in app //--------------------------------------------------------- Arranger::Arranger(ArrangerView* parent, const char* name) : QWidget(parent) { setObjectName(name); _canvasXOrigin = DefaultCanvasXOrigin; //_minXMag = -2000; _minXMag = -500; _maxXMag = -5; QList rast_cols; rast_cols << Rasterizer::TripletColumn << Rasterizer::NormalColumn << Rasterizer::DottedColumn; _rasterizerModel = new RasterizerModel( MusEGlobal::globalRasterizer, this, 7, rast_cols); _rasterizerModel->setDisplayFormat(RasterizerModel::FractionFormat); _raster = _rasterizerModel->pickRaster(0, RasterizerModel::GotoBar); selected = nullptr; showTrackinfoFlag = true; cursVal = INT_MAX; _parentWin=parent; setFocusPolicy(Qt::NoFocus); //--------------------------------------------------- // ToolBar // create toolbar in toplevel widget //--------------------------------------------------- // NOTICE: Please ensure that any tool bar object names here match the names assigned // to identical or similar toolbars in class MusE or other TopWin classes. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). parent->addToolBarBreak(); QToolBar* toolbar = parent->addToolBar(tr("Arranger")); toolbar->setObjectName("ArrangerToolbar"); QLabel* label = new QLabel(tr("Cursor")); toolbar->addWidget(label); cursorPos = new PosLabel(nullptr, "PosLabel"); cursorPos->setToolTip(tr("Cursor position")); cursorPos->setEnabled(false); toolbar->addWidget(cursorPos); gridOnButton = new QToolButton(); gridOnButton->setIcon(*gridOnSVGIcon); gridOnButton->setFocusPolicy(Qt::NoFocus); gridOnButton->setCheckable(true); gridOnButton->setToolTip(tr("Show grid")); gridOnButton->setWhatsThis(tr("Show grid")); toolbar->addWidget(gridOnButton); connect(gridOnButton, &QToolButton::toggled, [this](bool v) { gridOnChanged(v); } ); _rasterCombo = new RasterLabelCombo(RasterLabelCombo::TableView, _rasterizerModel, this, "RasterLabelCombo"); _rasterCombo->setFocusPolicy(Qt::TabFocus); toolbar->addWidget(_rasterCombo); // Song len label = new QLabel(tr("Bars")); label->setIndent(3); toolbar->addWidget(label); // song length is limited to 10000 bars; the real song len is limited // by overflows in tick computations lenEntry = new SpinBox(1, 10000, 1); lenEntry->setFocusPolicy(Qt::StrongFocus); lenEntry->setValue(MusEGlobal::song->len()); lenEntry->setToolTip(tr("Song length - bars")); lenEntry->setWhatsThis(tr("Song length - bars")); toolbar->addWidget(lenEntry); connect(lenEntry, SIGNAL(valueChanged(int)), SLOT(songlenChanged(int))); label = new QLabel(tr("Pitch")); label->setIndent(3); toolbar->addWidget(label); globalPitchSpinBox = new SpinBox(-127, 127, 1); globalPitchSpinBox->setFocusPolicy(Qt::StrongFocus); globalPitchSpinBox->setValue(MusEGlobal::song->globalPitchShift()); globalPitchSpinBox->setToolTip(tr("Midi pitch")); globalPitchSpinBox->setWhatsThis(tr("Global midi pitch shift")); toolbar->addWidget(globalPitchSpinBox); connect(globalPitchSpinBox, SIGNAL(valueChanged(int)), SLOT(globalPitchChanged(int))); label = new QLabel(tr("Tempo")); label->setIndent(3); toolbar->addWidget(label); globalTempoSpinBox = new SpinBox(50, 200, 1, toolbar); globalTempoSpinBox->setFocusPolicy(Qt::StrongFocus); globalTempoSpinBox->setSuffix(QString("%")); globalTempoSpinBox->setValue(MusEGlobal::tempomap.globalTempo()); globalTempoSpinBox->setToolTip(tr("Midi tempo")); globalTempoSpinBox->setWhatsThis(tr("Midi tempo")); toolbar->addWidget(globalTempoSpinBox); connect(globalTempoSpinBox, SIGNAL(valueChanged(int)), SLOT(globalTempoChanged(int))); QToolButton* tempo50 = new QToolButton(); tempo50->setText(QString("50%")); tempo50->setFocusPolicy(Qt::NoFocus); toolbar->addWidget(tempo50); connect(tempo50, SIGNAL(clicked()), SLOT(setTempo50())); QToolButton* tempo100 = new QToolButton(); tempo100->setText(tr("N")); tempo100->setFocusPolicy(Qt::NoFocus); toolbar->addWidget(tempo100); connect(tempo100, SIGNAL(clicked()), SLOT(setTempo100())); QToolButton* tempo200 = new QToolButton(); tempo200->setText(QString("200%")); tempo200->setFocusPolicy(Qt::NoFocus); toolbar->addWidget(tempo200); connect(tempo200, SIGNAL(clicked()), SLOT(setTempo200())); QVBoxLayout* box = new QVBoxLayout(this); box->setContentsMargins(0, 0, 0, 0); box->setSpacing(0); box->addWidget(MusECore::hLine(this), Qt::AlignTop); //--------------------------------------------------- // Tracklist //--------------------------------------------------- int xscale = -100; int yscale = 1; split = new Splitter(Qt::Horizontal, this, "split"); split->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); box->addWidget(split, 1000); trackInfoWidget = new TrackInfoWidget(split); split->setStretchFactor(split->indexOf(trackInfoWidget), 0); QSizePolicy tipolicy = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); tipolicy.setHorizontalStretch(0); tipolicy.setVerticalStretch(100); trackInfoWidget->setSizePolicy(tipolicy); tracklistScroll = new QScrollArea(split); split->setStretchFactor(split->indexOf(tracklistScroll), 0); QSizePolicy tpolicy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); tpolicy.setHorizontalStretch(0); tpolicy.setVerticalStretch(100); tracklistScroll->setSizePolicy(tpolicy); tracklistScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); tracklistScroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); tracklistScroll->setWidgetResizable(true); editor = new QWidget(split); split->setStretchFactor(split->indexOf(editor), 1); QSizePolicy epolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); epolicy.setHorizontalStretch(255); epolicy.setVerticalStretch(100); editor->setSizePolicy(epolicy); //--------------------------------------------------- // Track Info //--------------------------------------------------- genTrackInfo(trackInfoWidget); tracklist = new QWidget(tracklistScroll); initTracklistHeader(); list = new TList(header, tracklist, "TrackList"); tlistLayout = new QVBoxLayout(tracklist); tlistLayout->setContentsMargins(0, 0, 0, 0); tlistLayout->setSpacing(0); tlistLayout->addWidget(header); tlistLayout->addWidget(list); tracklist->setMinimumWidth(header->length()); tracklistScroll->setWidget(tracklist); connect(header, SIGNAL(sectionResized(int,int,int)), this, SLOT(updateTracklist())); connect(header, SIGNAL(sectionMoved(int,int,int)), list, SLOT(redraw())); // tracklist: // // 0 1 2 // +-----------+--------+---------+ // | Trackinfo | scroll | Header | 0 // | | bar +---------+ // | | | TList | 1 // +-----------+--------+---------+ // | hline | 2 // +-----+------------------------+ // | ib | | 3 // +-----+------------------------+ //--------------------------------------------------- // Editor //--------------------------------------------------- int offset = MusEGlobal::sigmap.ticksMeasure(0); hscroll = new ScrollScale( (_minXMag * MusEGlobal::config.division) / 384, _maxXMag, xscale, MusEGlobal::song->len() + offset, Qt::Horizontal, this, _canvasXOrigin); hscroll->setFocusPolicy(Qt::NoFocus); hscroll->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); bottomHLayout = new QHBoxLayout(); bottomHLayout->addWidget(hscroll); bottomHLayout->setContentsMargins(0, 0, 0, 0); bottomHLayout->setSpacing(0); vscroll = new QScrollBar(editor); vscroll->setMinimum(0); vscroll->setMaximum(20*20); vscroll->setSingleStep(5); vscroll->setPageStep(25); // FIXME: too small steps here for me (flo), better control via window height? vscroll->setValue(0); vscroll->setOrientation(Qt::Vertical); list->setScroll(vscroll); egrid = new QGridLayout(editor); egrid->setColumnStretch(0, 50); egrid->setRowStretch(2, 50); egrid->setContentsMargins(0, 0, 0, 0); egrid->setSpacing(0); time = new MTScale(_raster, editor, xscale); time->setOrigin(_canvasXOrigin, 0); canvas = new PartCanvas(&_raster, editor, xscale, yscale); canvas->setBg(MusEGlobal::config.partCanvasBg); canvas->setCanvasTools(arrangerTools); canvas->setOrigin(_canvasXOrigin, 0); canvas->setFocus(); list->setFocusProxy(canvas); // Make it easy for track list popup line editor to give focus back to canvas. setRasterVal(_raster); connect(canvas, SIGNAL(setUsedTool(int)), this, SIGNAL(setUsedTool(int))); connect(canvas, SIGNAL(trackChanged(MusECore::Track*)), list, SLOT(selectTrack(MusECore::Track*))); connect(list, SIGNAL(keyPressExt(QKeyEvent*)), canvas, SLOT(redirKeypress(QKeyEvent*))); connect(canvas, SIGNAL(selectTrackAbove()), list, SLOT(selectTrackAbove())); connect(canvas, SIGNAL(selectTrackBelow()), list, SLOT(selectTrackBelow())); connect(canvas, SIGNAL(editTrackNameSig()), list, SLOT(editTrackNameSlot())); connect(canvas, SIGNAL(muteSelectedTracks()), list, SLOT(muteSelectedTracksSlot())); connect(canvas, SIGNAL(soloSelectedTracks()), list, SLOT(soloSelectedTracksSlot())); connect(canvas, SIGNAL(volumeSelectedTracks(int)), list, SLOT(volumeSelectedTracksSlot(int))); connect(canvas, SIGNAL(panSelectedTracks(int)), list, SLOT(panSelectedTracksSlot(int))); connect(canvas, SIGNAL(horizontalZoom(bool, const QPoint&)), SLOT(horizontalZoom(bool, const QPoint&))); connect(canvas, SIGNAL(horizontalZoom(int, const QPoint&)), SLOT(horizontalZoom(int, const QPoint&))); connect(lenEntry, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(lenEntry, SIGNAL(escapePressed()), SLOT(focusCanvas())); connect(globalPitchSpinBox, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(globalPitchSpinBox, SIGNAL(escapePressed()), SLOT(focusCanvas())); connect(globalTempoSpinBox, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(globalTempoSpinBox, SIGNAL(escapePressed()), SLOT(focusCanvas())); //connect(this, SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*))); connect(list, SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*))); egrid->addWidget(time, 0, 0, 1, 2); egrid->addWidget(MusECore::hLine(editor), 1, 0, 1, 2); egrid->addWidget(canvas, 2, 0); egrid->addWidget(vscroll, 2, 1); egrid->addLayout(bottomHLayout, 3, 0); connect(vscroll, SIGNAL(valueChanged(int)), canvas, SLOT(setYPos(int))); connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setXMag(int))); connect(vscroll, SIGNAL(valueChanged(int)), list, SLOT(setYPos(int))); connect(hscroll, SIGNAL(scrollChanged(int)), time, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), time, SLOT(setXMag(int))); connect(canvas, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(canvas, SIGNAL(verticalScroll(unsigned)),SLOT(verticalScrollSetYpos(unsigned))); connect(canvas, SIGNAL(horizontalScroll(unsigned)),hscroll, SLOT(setPos(unsigned))); connect(canvas, SIGNAL(horizontalScrollNoLimit(unsigned)),hscroll, SLOT(setPosNoLimit(unsigned))); connect(time, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(list, SIGNAL(verticalScrollSetYpos(int)), vscroll, SLOT(setValue(int))); connect(canvas, SIGNAL(tracklistChanged()), list, SLOT(tracklistChanged())); connect(canvas, SIGNAL(dclickPart(MusECore::Track*)), SIGNAL(editPart(MusECore::Track*))); connect(canvas, SIGNAL(startEditor(MusECore::PartList*,int)), SIGNAL(startEditor(MusECore::PartList*, int))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(canvas, SIGNAL(followEvent(int)), hscroll, SLOT(setOffset(int))); connect(canvas, SIGNAL(dropSongFile(const QString&)), SIGNAL(dropSongFile(const QString&))); connect(canvas, SIGNAL(dropMidiFile(const QString&)), SIGNAL(dropMidiFile(const QString&))); connect(canvas, SIGNAL(toolChanged(int)), SIGNAL(toolChanged(int))); connect(MusEGlobal::song, SIGNAL(controllerChanged(MusECore::Track*, int)), SLOT(controllerChanged(MusECore::Track*, int))); connect(_rasterCombo, &RasterLabelCombo::rasterChanged, [this](int raster) { rasterChanged(raster); } ); configChanged(); // set configuration values showTrackInfo(showTrackinfoFlag); setDefaultSplitterSizes(); // Take care of some tabbies! setTabOrder(list, canvas); } //--------------------------------------------------------- // setDefaultSplitterSizes //--------------------------------------------------------- void Arranger::setDefaultSplitterSizes() { QSettings s; if (split->restoreState(s.value("Arranger/splitState").toByteArray())) return; QList vallist; vallist.append(trackInfoWidget->sizeHint().width()); list->resize(250, 100); vallist.append(tlistLayout->sizeHint().width()); vallist.append(1); split->setSizes(vallist); } void Arranger::storeSplitterSizes() { QSettings s; s.setValue("Arranger/splitState", split->saveState()); } void Arranger::initTracklistHeader() { header = new Header(tracklist, "TrackListHeader"); header->setFixedHeight(31); header->setColumnLabel("#", TList::COL_TRACK_IDX); header->setColumnIcon(*monitorOnSVGIcon, TList::COL_INPUT_MONITOR); header->setColumnIcon(*recArmOnSVGIcon, TList::COL_RECORD); header->setColumnIcon(*muteOnSVGIcon, TList::COL_MUTE); header->setColumnIcon(*soloOnAloneSVGIcon, TList::COL_SOLO); header->setColumnIcon(*tracktypeSVGIcon, TList::COL_CLASS); header->setColumnLabel(tr("Track"), TList::COL_NAME); header->setColumnLabel(tr("Port"), TList::COL_OPORT); //: Channel header->setColumnLabel(tr("Ch"), TList::COL_OCHANNEL); //: Time lock // header->setColumnLabel(tr("T"), TList::COL_TIMELOCK); header->setColumnLabel(tr("Automation"), TList::COL_AUTOMATION); header->setColumnLabel(tr("Clef"), TList::COL_CLEF); for (unsigned i = 0; i < custom_columns.size(); i++) header->setColumnLabel(custom_columns[i].name, TList::COL_CUSTOM_MIDICTRL_OFFSET + i); header->setSectionResizeMode(TList::COL_TRACK_IDX, QHeaderView::Interactive); header->setSectionResizeMode(TList::COL_INPUT_MONITOR, QHeaderView::Fixed); header->setSectionResizeMode(TList::COL_RECORD, QHeaderView::Fixed); header->setSectionResizeMode(TList::COL_MUTE, QHeaderView::Fixed); header->setSectionResizeMode(TList::COL_SOLO, QHeaderView::Fixed); header->setSectionResizeMode(TList::COL_CLASS, QHeaderView::Fixed); header->setSectionResizeMode(TList::COL_NAME, QHeaderView::Interactive); header->setSectionResizeMode(TList::COL_OPORT, QHeaderView::Interactive); header->setSectionResizeMode(TList::COL_OCHANNEL, QHeaderView::Fixed); // header->setSectionResizeMode(TList::COL_TIMELOCK, QHeaderView::Fixed); header->setSectionResizeMode(TList::COL_AUTOMATION, QHeaderView::Interactive); header->setSectionResizeMode(TList::COL_CLEF, QHeaderView::Interactive); for (unsigned i = 0; i < custom_columns.size(); i++) header->setSectionResizeMode(TList::COL_CUSTOM_MIDICTRL_OFFSET+i, QHeaderView::Interactive); // 04/18/17 Time lock remains unused. Disabled until a use is found. // Plans were to use it (or not) when time stretching / pitch shifting work is done. // header->setSectionHidden(TList::COL_TIMELOCK, true); setHeaderToolTips(); setHeaderWhatsThis(); setHeaderStatusTips(); // does not work with Qt 5.9! header->setSectionsMovable (true); header->restoreState(header_state); } //--------------------------------------------------------- // setTime //--------------------------------------------------------- void Arranger::setTime(unsigned tick) { if (tick == INT_MAX) cursorPos->setEnabled(false); else { cursVal = tick; cursorPos->setEnabled(true); cursorPos->setValue(tick); time->setPos(3, tick, false); } } //--------------------------------------------------------- // toolChange //--------------------------------------------------------- void Arranger::setTool(int t) { canvas->setTool(t); } //--------------------------------------------------------- // dclickPart //--------------------------------------------------------- void Arranger::dclickPart(MusECore::Track* t) { emit editPart(t); } //--------------------------------------------------------- // setHeaderSizes //--------------------------------------------------------- void Arranger::setHeaderSizes() { const int fw = 11; header->resizeSection(TList::COL_TRACK_IDX, qMax(header->sectionSizeHint(TList::COL_TRACK_IDX) + fw, 30)); header->resizeSection(TList::COL_INPUT_MONITOR, header->sectionSizeHint(TList::COL_INPUT_MONITOR)); header->resizeSection(TList::COL_RECORD, header->sectionSizeHint(TList::COL_RECORD)); header->resizeSection(TList::COL_MUTE, header->sectionSizeHint(TList::COL_MUTE)); header->resizeSection(TList::COL_SOLO, header->sectionSizeHint(TList::COL_SOLO)); header->resizeSection(TList::COL_CLASS, header->sectionSizeHint(TList::COL_CLASS)); header->resizeSection(TList::COL_NAME, qMax(header->sectionSizeHint(TList::COL_NAME) + fw, 100)); header->resizeSection(TList::COL_OPORT, qMax(header->sectionSizeHint(TList::COL_OPORT) + fw, 60)); header->resizeSection(TList::COL_OCHANNEL, header->sectionSizeHint(TList::COL_OCHANNEL) + fw); // header->resizeSection(TList::COL_TIMELOCK, header->sectionSizeHint(TList::COL_TIMELOCK) + fw); header->resizeSection(TList::COL_AUTOMATION, qMax(header->sectionSizeHint(TList::COL_AUTOMATION) + fw, 80)); header->resizeSection(TList::COL_CLEF, qMax(header->sectionSizeHint(TList::COL_CLEF) + fw, 50)); for (unsigned i = 0; i < custom_columns.size(); i++) header->resizeSection(TList::COL_CUSTOM_MIDICTRL_OFFSET + i, qMax(header->sectionSizeHint(TList::COL_CUSTOM_MIDICTRL_OFFSET + i) + fw, 30)); } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void Arranger::configChanged() { if (MusEGlobal::config.canvasBgPixmap.isEmpty()) { canvas->setBg(MusEGlobal::config.partCanvasBg); canvas->setBg(QPixmap()); } else { canvas->setBg(QPixmap(MusEGlobal::config.canvasBgPixmap)); } setHeaderSizes(); _parentWin->updateVisibleTracksButtons(); gridOnButton->blockSignals(true); gridOnButton->setChecked(MusEGlobal::config.canvasShowGrid); gridOnButton->blockSignals(false); canvas->redraw(); } //--------------------------------------------------------- // focusCanvas //--------------------------------------------------------- void Arranger::focusCanvas() { if(MusEGlobal::config.smartFocus) { canvas->setFocus(); canvas->activateWindow(); } } //--------------------------------------------------------- // songlenChanged //--------------------------------------------------------- void Arranger::songlenChanged(int n) { int newLen = MusEGlobal::sigmap.bar2tick(n, 0, 0); MusEGlobal::song->setLen(newLen); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void Arranger::songChanged(MusECore::SongChangedStruct_t type) { // We must catch this first and be sure to update the strips. if(type & SC_TRACK_REMOVED) { { AudioStrip* w = static_cast(trackInfoWidget->getWidget(1)); if(w) { MusECore::Track* t = w->getTrack(); if(t) { if(!MusEGlobal::song->trackExists(t)) { delete w; trackInfoWidget->addWidget(nullptr, 1); selected = nullptr; switchInfo(0); } } } } { MidiStrip* w = static_cast(trackInfoWidget->getWidget(2)); if(w) { MusECore::Track* t = w->getTrack(); if(t) { if(!MusEGlobal::song->trackExists(t)) { delete w; trackInfoWidget->addWidget(nullptr, 2); selected = nullptr; switchInfo(0); } } } } } // Try these, may need more/less. if(type & ( SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_TRACK_MOVED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | SC_SIG)) { unsigned endTick = MusEGlobal::song->len(); int offset = MusEGlobal::sigmap.ticksMeasure(endTick); hscroll->setRange(_canvasXOrigin, endTick + offset); canvas->setOrigin(_canvasXOrigin, 0); time->setOrigin(_canvasXOrigin, 0); int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(endTick, &bar, &beat, &tick); if (tick || beat) ++bar; lenEntry->blockSignals(true); lenEntry->setValue(bar); lenEntry->blockSignals(false); } if(type & (SC_TRACK_SELECTION | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MOVED | SC_TRACK_MODIFIED | SC_TRACK_RESIZED)) trackSelectionChanged(); // Keep this light, partsChanged is a heavy move! Try these, may need more. Maybe sig. Requires tempo. if(type & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_TRACK_MOVED | SC_TRACK_RESIZED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | SC_SIG | SC_TEMPO | SC_MASTER)) canvas->updateItems(); if(type & (SC_PART_SELECTION)) { // Prevent race condition: Ignore if the change was ultimately sent by the canvas itself. if(type._sender != canvas) canvas->updateItemSelections(); } if (type & SC_SIG) time->redraw(); if (type & SC_TEMPO) setGlobalTempo(MusEGlobal::tempomap.globalTempo()); if (type & SC_DIVISION_CHANGED) { // The division has changed. The raster table and raster model will have been // cleared and re-filled, so any views on the model will no longer have a // current item and our current raster value will be invalid. They WILL NOT // emit an activated signal. So we must manually select a current item and // raster value here. We could do something fancy to try to keep the current // index - for example stay on quarter note - by taking the ratio of the new // division to old division and apply that to the old raster value and try // to select that index, but the division has already changed. // So instead, simply try to select the current raster value. The index in the box may change. // Be sure to use what it chooses. setRasterVal(_raster); // Now set a reasonable zoom (mag) range. setupHZoomRange(); } // Try these: if(type & (SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED | SC_CLIP_MODIFIED | SC_MARKER_INSERTED | SC_MARKER_REMOVED | SC_MARKER_MODIFIED)) canvas->redraw(); // We must marshall song changed instead of connecting to the strip's song changed // otherwise it crashes when loading another song because track is no longer valid // and the strip's songChanged() seems to be called before Arranger songChanged() // gets called and has a chance to stop the crash. // Also, calling updateTrackInfo() from here is too heavy, it destroys and recreates // the strips each time no matter what the flags are ! //updateTrackInfo(type); trackInfoSongChange(type); // Update the arrangerview's actions. // This needs to come after the canvas->selectionChanged() above so that in // selectionChanged(), itemsAreSelected() has the latest citems' selected flags. if(type & (SC_TRACK_SELECTION | SC_PART_SELECTION | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED)) _parentWin->selectionChanged(); } //--------------------------------------------------------- // trackSelectionChanged //--------------------------------------------------------- void Arranger::trackSelectionChanged() { MusECore::Track* track = MusEGlobal::song->selectedTrack(); if (track == selected) return; selected = track; updateTrackInfo(-1); } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void Arranger::writeStatus(int level, MusECore::Xml& xml) { xml.tag(level++, "arranger"); xml.intTag(level, "raster", _raster); xml.intTag(level, "info", showTrackinfoFlag); split->writeStatus(level, xml); xml.intTag(level, "xmag", hscroll->mag()); xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ypos", vscroll->value()); xml.etag(level, "arranger"); } void Arranger::writeConfiguration(int level, MusECore::Xml& xml) { xml.tag(level++, "arranger"); writeCustomColumns(level, xml); xml.strTag(level, "tlist_header", header->saveState().toHex().constData()); xml.etag(level, "arranger"); } //--------------------------------------------------------- // readConfiguration //--------------------------------------------------------- void Arranger::readConfiguration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "tlist_header") { // We can only restore the header state with version-compatible data. // If columns were altered, 'alien' loaded data will not fit! if(xml.isVersionEqualToLatest()) header_state = QByteArray::fromHex(xml.parse1().toLatin1()); else xml.parse1(); } else if (tag == "custom_columns") readCustomColumns(xml); else xml.unknown("Arranger"); break; case MusECore::Xml::TagEnd: if (tag == "arranger") return; default: break; } } } //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void Arranger::readStatus(MusECore::Xml& xml) { int rast = -1; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "raster") rast = xml.parseInt(); else if (tag == "info") showTrackinfoFlag = xml.parseInt(); else if (tag == split->objectName()) { split->readStatus(xml); } else if (tag == "xmag") hscroll->setMag(xml.parseInt()); else if (tag == "xpos") hscroll->setPos(xml.parseInt()); else if (tag == "ypos") vscroll->setValue(xml.parseInt()); else xml.unknown("Arranger"); break; case MusECore::Xml::TagEnd: if (tag == "arranger") { setRasterVal(rast); return; } default: break; } } } //--------------------------------------------------------- // rasterChanged //--------------------------------------------------------- void Arranger::rasterChanged(int raster) { _raster = raster; time->setRaster(_raster); canvas->redraw(); focusCanvas(); } //--------------------------------------------------------- // rasterVal //--------------------------------------------------------- int Arranger::rasterVal() const { return _raster; } //--------------------------------------------------------- // currentPartColorIndex //--------------------------------------------------------- int Arranger::currentPartColorIndex() const { if(canvas) return canvas->currentPartColorIndex(); return 0; } //--------------------------------------------------------- // setRasterVal //--------------------------------------------------------- bool Arranger::setRasterVal(int val) { const RasterizerModel* rast_mdl = _rasterCombo->rasterizerModel(); _raster = rast_mdl->checkRaster(val); time->setRaster(_raster); const QModelIndex mdl_idx = rast_mdl->modelIndexOfRaster(_raster); if(mdl_idx.isValid()) _rasterCombo->setCurrentModelIndex(mdl_idx); else fprintf(stderr, "Arranger::changeRaster: _raster %d not found in box!\n", _raster); canvas->redraw(); return true; } //--------------------------------------------------------- // reset //--------------------------------------------------------- void Arranger::reset() { canvas->setXPos(0); canvas->setYPos(0); hscroll->setPos(0); vscroll->setValue(0); time->setXPos(0); time->setYPos(0); } //--------------------------------------------------------- // cmd //--------------------------------------------------------- void Arranger::cmd(int cmd) { int ncmd; switch (cmd) { case CMD_CUT_PART: ncmd = PartCanvas::CMD_CUT_PART; break; case CMD_COPY_PART: ncmd = PartCanvas::CMD_COPY_PART; break; case CMD_COPY_PART_IN_RANGE: ncmd = PartCanvas::CMD_COPY_PART_IN_RANGE; break; case CMD_PASTE_PART: ncmd = PartCanvas::CMD_PASTE_PART; break; case CMD_PASTE_CLONE_PART: ncmd = PartCanvas::CMD_PASTE_CLONE_PART; break; case CMD_PASTE_PART_TO_TRACK: ncmd = PartCanvas::CMD_PASTE_PART_TO_TRACK; break; case CMD_PASTE_CLONE_PART_TO_TRACK: ncmd = PartCanvas::CMD_PASTE_CLONE_PART_TO_TRACK; break; case CMD_PASTE_DIALOG: ncmd = PartCanvas::CMD_PASTE_DIALOG; break; case CMD_INSERT_EMPTYMEAS: ncmd = PartCanvas::CMD_INSERT_EMPTYMEAS; break; default: return; } canvas->cmd(ncmd); } //--------------------------------------------------------- // globalPitchChanged //--------------------------------------------------------- void Arranger::globalPitchChanged(int val) { MusEGlobal::song->setGlobalPitchShift(val); } //--------------------------------------------------------- // globalTempoChanged //--------------------------------------------------------- void Arranger::globalTempoChanged(int val) { MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetGlobalTempo, val, 0)); } //--------------------------------------------------------- // setTempo50 //--------------------------------------------------------- void Arranger::setTempo50() { MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetGlobalTempo, 50, 0)); } //--------------------------------------------------------- // setTempo100 //--------------------------------------------------------- void Arranger::setTempo100() { MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetGlobalTempo, 100, 0)); } //--------------------------------------------------------- // setTempo200 //--------------------------------------------------------- void Arranger::setTempo200() { MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetGlobalTempo, 200, 0)); } //--------------------------------------------------------- // gridOnChanged //--------------------------------------------------------- void Arranger::gridOnChanged(bool v) { MusEGlobal::config.canvasShowGrid = v; // We want the simple version, don't set the style or stylesheet yet. MusEGlobal::muse->changeConfig(true); } //--------------------------------------------------------- // setGlobalTempo //--------------------------------------------------------- void Arranger::setGlobalTempo(int val) { if(val != globalTempoSpinBox->value()) { globalTempoSpinBox->blockSignals(true); globalTempoSpinBox->setValue(val); globalTempoSpinBox->blockSignals(false); } } //--------------------------------------------------------- // verticalScrollSetYpos //--------------------------------------------------------- void Arranger::verticalScrollSetYpos(unsigned ypos) { vscroll->setValue(ypos); } //--------------------------------------------------------- // clear //--------------------------------------------------------- void Arranger::clear() { { AudioStrip* w = static_cast(trackInfoWidget->getWidget(1)); if (w) delete w; trackInfoWidget->addWidget(0, 1); } { MidiStrip* w = static_cast(trackInfoWidget->getWidget(2)); if (w) delete w; trackInfoWidget->addWidget(0, 2); } selected = 0; } //void Arranger::wheelEvent(QWheelEvent* ev) // { // emit redirectWheelEvent(ev); // } void Arranger::controllerChanged(MusECore::Track *t, int ctrlId) { canvas->controllerChanged(t, ctrlId); } //--------------------------------------------------------- // toggleTrackInfo //--------------------------------------------------------- void Arranger::toggleTrackInfo() { showTrackInfo(showTrackinfoFlag ^ true); } //--------------------------------------------------------- // showTrackInfo //--------------------------------------------------------- void Arranger::showTrackInfo(bool flag) { showTrackinfoFlag = flag; trackInfoWidget->setVisible(flag); updateTrackInfo(-1); } //--------------------------------------------------------- // genTrackInfo //--------------------------------------------------------- void Arranger::genTrackInfo(TrackInfoWidget* trackInfo) { trackInfo->addWidget(nullptr, 1); // AudioStrip placeholder. trackInfo->addWidget(nullptr, 2); // MidiStrip placeholder. } //--------------------------------------------------------- // updateTrackInfo //--------------------------------------------------------- void Arranger::updateTrackInfo(MusECore::SongChangedStruct_t /*flags*/) { if (!showTrackinfoFlag) { switchInfo(-1); return; } if (selected == nullptr) { switchInfo(0); return; } if (selected->isMidiTrack()) { switchInfo(2); } else { switchInfo(1); } } //--------------------------------------------------------- // switchInfo //--------------------------------------------------------- void Arranger::switchInfo(int n) { if (n == 1) { { MidiStrip* w = static_cast(trackInfoWidget->getWidget(2)); if (w) { //fprintf(stderr, "Arranger::switchInfo audio strip: deleting midi strip\n"); delete w; //w->deleteLater(); trackInfoWidget->addWidget(nullptr, 2); } } { AudioStrip* w = static_cast(trackInfoWidget->getWidget(1)); if (w == nullptr || selected != w->getTrack()) { if (w) { //fprintf(stderr, "Arranger::switchInfo deleting strip\n"); delete w; //w->deleteLater(); } w = new AudioStrip(trackInfoWidget, static_cast(selected)); // Broadcast changes to other selected tracks. w->setBroadcastChanges(true); // Set focus yielding to the canvas. if(MusEGlobal::config.smartFocus) { w->setFocusYieldWidget(canvas); //w->setFocusPolicy(Qt::WheelFocus); } // We must marshall song changed instead of connecting to the strip's song changed // otherwise it crashes when loading another song because track is no longer valid // and the strip's songChanged() seems to be called before Arranger songChanged() // gets called and has a chance to stop the crash. //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), w, SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::muse, SIGNAL(configChanged()), w, SLOT(configChanged())); w->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); trackInfoWidget->addWidget(w, 1); w->show(); //setTabOrder(midiTrackInfo, w); } } } else if (n == 2) { { AudioStrip* w = static_cast(trackInfoWidget->getWidget(1)); if (w) { //fprintf(stderr, "Arranger::switchInfo midi strip: deleting audio strip\n"); delete w; //w->deleteLater(); trackInfoWidget->addWidget(nullptr, 1); } } { MidiStrip* w = static_cast(trackInfoWidget->getWidget(2)); if (w == nullptr || selected != w->getTrack()) { if (w) { //fprintf(stderr, "Arranger::switchInfo deleting strip\n"); delete w; //w->deleteLater(); } w = new MidiStrip(trackInfoWidget, static_cast(selected)); // Broadcast changes to other selected tracks. w->setBroadcastChanges(true); // Set focus yielding to the arranger canvas. if(MusEGlobal::config.smartFocus) { w->setFocusYieldWidget(canvas); //w->setFocusPolicy(Qt::WheelFocus); } // No. See above. //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), w, SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::muse, SIGNAL(configChanged()), w, SLOT(configChanged())); w->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); trackInfoWidget->addWidget(w, 2); w->show(); //setTabOrder(midiTrackInfo, w); } } } if (trackInfoWidget->curIdx() == n) return; trackInfoWidget->raiseWidget(n); } //--------------------------------------------------------- // trackInfoSongChange //--------------------------------------------------------- void Arranger::trackInfoSongChange(MusECore::SongChangedStruct_t flags) { if(!selected || !showTrackinfoFlag) return; // Only update what is showing. if(selected->isMidiTrack()) { MidiStrip* w = static_cast(trackInfoWidget->getWidget(2)); if(w) w->songChanged(flags); } else { AudioStrip* w = static_cast(trackInfoWidget->getWidget(1)); if(w) w->songChanged(flags); } } void Arranger::keyPressEvent(QKeyEvent* event) { int key = event->key(); if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) key += Qt::SHIFT; if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) key += Qt::ALT; if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) key+= Qt::CTRL; RasterizerModel::RasterPick rast_pick = RasterizerModel::NoPick; const int cur_rast = rasterVal(); if (key == shortcuts[SHRT_ZOOM_IN].key) { horizontalZoom(true, QCursor::pos()); return; } else if (key == shortcuts[SHRT_ZOOM_OUT].key) { horizontalZoom(false, QCursor::pos()); return; } else if (key == shortcuts[SHRT_HIDE_MIXER_STRIP].key) { showTrackInfo(!showTrackinfoFlag); return; } // QUANTIZE shortcuts from midi editors is reused for SNAP in Arranger // `does not work exactly the same but close enough I think. else if (key == shortcuts[SHRT_SET_QUANT_BAR].key) { rast_pick = RasterizerModel::GotoBar; } else if (key == shortcuts[SHRT_SET_QUANT_OFF].key) { rast_pick = RasterizerModel::GotoOff; } else if (key == shortcuts[SHRT_SET_QUANT_1].key) { rast_pick = RasterizerModel::Goto1; } else if (key == shortcuts[SHRT_SET_QUANT_2].key) { rast_pick = RasterizerModel::Goto2; } else if (key == shortcuts[SHRT_SET_QUANT_3].key) { rast_pick = RasterizerModel::Goto4; } else if (key == shortcuts[SHRT_SET_QUANT_4].key) { rast_pick = RasterizerModel::Goto8; } else if (key == shortcuts[SHRT_SET_QUANT_5].key) { rast_pick = RasterizerModel::Goto16; } else if (key == shortcuts[SHRT_SET_QUANT_6].key) { // this value is not actually defined, adding for completeness but commented out. return; //rast_pick = RasterizerModel::Goto32; } else if (key == shortcuts[SHRT_SET_QUANT_7].key) { // this value is not actually defined, adding for completeness but commented out. return; //rast_pick = RasterizerModel::Goto64; } if(rast_pick != RasterizerModel::NoPick) { const int new_rast = _rasterizerModel->pickRaster(cur_rast, rast_pick); if(new_rast != cur_rast) setRasterVal(new_rast); return; } QWidget::keyPressEvent(event); } void Arranger::horizontalZoom(bool zoom_in, const QPoint& glob_pos) { int mag = hscroll->mag(); int zoomlvl = ScrollScale::getQuickZoomLevel(mag); if(zoom_in) { if (zoomlvl < MusEGui::ScrollScale::zoomLevels-1) zoomlvl++; } else { if (zoomlvl > 1) zoomlvl--; } int newmag = ScrollScale::convertQuickZoomLevelToMag(zoomlvl); QPoint cp = canvas->mapFromGlobal(glob_pos); QPoint sp = editor->mapFromGlobal(glob_pos); if(cp.x() >= 0 && cp.x() < canvas->width() && sp.y() >= 0 && sp.y() < editor->height()) hscroll->setMag(newmag, cp.x()); } void Arranger::horizontalZoom(int mag, const QPoint& glob_pos) { QPoint cp = canvas->mapFromGlobal(glob_pos); QPoint sp = editor->mapFromGlobal(glob_pos); if(cp.x() >= 0 && cp.x() < canvas->width() && sp.y() >= 0 && sp.y() < editor->height()) hscroll->setMag(hscroll->mag() + mag, cp.x()); } void Arranger::updateHeaderCustomColumns() { for (int i = TList::COL_CUSTOM_MIDICTRL_OFFSET; i < header->count(); i++) header->removeColumn(i); header->headerDataChanged(Qt::Horizontal, TList::COL_CUSTOM_MIDICTRL_OFFSET, header->count()); for (unsigned i = 0; i < custom_columns.size(); i++) { header->setColumnLabel(custom_columns[i].name, TList::COL_CUSTOM_MIDICTRL_OFFSET + i); header->showSection(TList::COL_CUSTOM_MIDICTRL_OFFSET + i); } setHeaderSizes(); updateTracklist(); } void Arranger::updateTracklist() { tracklist->setMinimumWidth(header->length()); list->redraw(); } bool Arranger::isSingleSelection() const { return canvas->isSingleSelection(); } int Arranger::selectionSize() const { return canvas->selectionSize(); } bool Arranger::itemsAreSelected() const { return canvas->itemsAreSelected(); } void Arranger::songIsClearing() const { canvas->songIsClearing(); } //--------------------------------------------------------- // setupHZoomRange //--------------------------------------------------------- void Arranger::setupHZoomRange() { const int min = (_minXMag * MusEGlobal::config.division) / 384; hscroll->setScaleRange(min, _maxXMag); } } // namespace MusEGui muse-4.0.0/src/muse/arranger/arranger.h000066400000000000000000000155701404105311000177740ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: arranger.h,v 1.17.2.15 2009/11/14 03:37:48 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ARRANGER_H__ #define __ARRANGER_H__ #include #include #include #include "type_defs.h" #include "tools.h" // Forward declarations: class QKeyEvent; class QPoint; class QComboBox; class QScrollBar; class QVBoxLayout; class QHBoxLayout; class QScrollArea; class QGridLayout; class QToolButton; namespace MusECore { class Track; class Xml; class PartList; } namespace MusEGui { class ArrangerView; class Header; class LabelCombo; class MTScale; class PosLabel; class ScrollScale; class SpinBox; class Splitter; class TrackInfoWidget; class TList; class ArrangerCanvasLayout; class ArrangerHScrollLayout; class PartCanvas; class CompactToolButton; class RasterizerModel; class RasterLabelCombo; //--------------------------------------------------------- // Arranger //--------------------------------------------------------- class Arranger : public QWidget { Q_OBJECT static QByteArray header_state; ArrangerView* _parentWin; QWidget* editor; int _quant, _raster; RasterizerModel *_rasterizerModel; RasterLabelCombo* _rasterCombo; PartCanvas* canvas; ScrollScale* hscroll; QScrollBar* vscroll; QVBoxLayout* tlistLayout; QGridLayout* egrid; QHBoxLayout* bottomHLayout; TList* list; Header* header; MTScale* time; SpinBox* lenEntry; QToolButton* gridOnButton; bool showTrackinfoFlag; TrackInfoWidget* trackInfoWidget; QScrollArea* tracklistScroll; QWidget* tracklist; // The X origin that is applied to any canvases. int _canvasXOrigin; // The X mag minimum that is applied to any ScrollScale controls. // Note that global midi division will also be taken into account. int _minXMag; // The X mag maximum that is applied to any ScrollScale controls. int _maxXMag; MusECore::Track* selected; Splitter* split; PosLabel* cursorPos; SpinBox* globalTempoSpinBox; SpinBox* globalPitchSpinBox; unsigned cursVal; void genTrackInfo(TrackInfoWidget*); void genMidiTrackInfo(); void genWaveTrackInfo(); void switchInfo(int); void trackInfoSongChange(MusECore::SongChangedStruct_t flags); void setHeaderToolTips(); void setHeaderWhatsThis(); void setHeaderStatusTips(); void setHeaderSizes(); void initTracklistHeader(); // Sets up a reasonable zoom minimum and/or maximum based on // the current global midi division (ticks per quarter note) // which has a very wide range (48 - 12288). // Also sets the canvas and time scale offsets accordingly. void setupHZoomRange(); private slots: void rasterChanged(int raster); void songlenChanged(int); void showTrackInfo(bool); void trackSelectionChanged(); void songChanged(MusECore::SongChangedStruct_t); void setTime(unsigned); void globalPitchChanged(int); void globalTempoChanged(int); void setTempo50(); void setTempo100(); void setTempo200(); void gridOnChanged(bool v); void verticalScrollSetYpos(unsigned); void horizontalZoom(bool zoom_in, const QPoint& glob_pos); void horizontalZoom(int mag, const QPoint& glob_pos); void updateTracklist(); signals: void editPart(MusECore::Track*); void dropSongFile(const QString&); void dropMidiFile(const QString&); void startEditor(MusECore::PartList*, int); void toolChanged(int); void setUsedTool(int); protected: virtual void keyPressEvent(QKeyEvent* event); public slots: void dclickPart(MusECore::Track*); void setTool(int); void updateTrackInfo(MusECore::SongChangedStruct_t flags); void configChanged(); void controllerChanged(MusECore::Track *t, int ctrlId); void focusCanvas(); public: enum { CMD_CUT_PART, CMD_COPY_PART, CMD_COPY_PART_IN_RANGE, CMD_PASTE_PART, CMD_PASTE_CLONE_PART, CMD_PASTE_PART_TO_TRACK, CMD_PASTE_CLONE_PART_TO_TRACK, CMD_PASTE_DIALOG, CMD_INSERT_EMPTYMEAS }; const int arrangerTools = PointerTool | PencilTool | RubberTool | CutTool | GlueTool | MuteTool | AutomationTool | PanTool | ZoomTool; struct custom_col_t { enum affected_pos_t {AFFECT_BEGIN, AFFECT_CPOS}; int ctrl; QString name; affected_pos_t affected_pos; custom_col_t(int c, QString n, affected_pos_t a=AFFECT_BEGIN) { ctrl=c; name=n; affected_pos=a; } }; static std::vector custom_columns; Arranger(ArrangerView* parent, const char* name = 0); PartCanvas* getCanvas() { return canvas; } void reset(); void writeStatus(int level, MusECore::Xml&); void readStatus(MusECore::Xml&); void writeConfiguration(int level, MusECore::Xml&); static void readConfiguration(MusECore::Xml&); static void writeCustomColumns(int level, MusECore::Xml&); static void readCustomColumns(MusECore::Xml&); static custom_col_t readOneCustomColumn(MusECore::Xml&); MusECore::Track* curTrack() const { return selected; } void cmd(int); bool isSingleSelection() const; int selectionSize() const; bool itemsAreSelected() const; void setGlobalTempo(int); void clear(); void songIsClearing() const; void setDefaultSplitterSizes(); void updateHeaderCustomColumns(); void toggleTrackInfo(); void storeSplitterSizes(); unsigned cursorValue() { return cursVal; } ArrangerView* parentWin() const { return _parentWin; } int rasterVal() const; bool setRasterVal(int); int currentPartColorIndex() const; TList *getTrackList() { return list; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/arranger/arrangerview.cpp000066400000000000000000001341541404105311000212220ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // arrangerview.cpp // (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sig.h" #include "app.h" #include "arrangerview.h" #include "audio.h" #include "functions.h" #include "gconfig.h" #include "globals.h" #include "helper.h" #include "icons.h" #include "scoreedit.h" #include "shortcuts.h" #include "song.h" #include "structure.h" #include "tb1.h" #include "ttoolbar.h" #include "arrangercolumns.h" #include "tlist.h" #include "synth.h" #include "pcanvas.h" // Forwards from header: #include #include #include #include #include "arranger.h" #include "visibletracks.h" #include "event_tag_list.h" #include "xml.h" #include "tools.h" #include "partcolortoolbar.h" namespace MusEGui { //--------------------------------------------------------- // ArrangerView //--------------------------------------------------------- ArrangerView::ArrangerView(QWidget* parent) : TopWin(TopWin::ARRANGER, parent, "arrangerview", Qt::Window) { isMdiWin() ? setWindowTitle(tr("Arranger")) : setWindowTitle(tr("MusE: Arranger")); setFocusPolicy(Qt::NoFocus); // Already has an object name. visTracks = new VisibleTracks(this); arranger = new Arranger(this, "arranger"); setCentralWidget(arranger); //setFocusProxy(arranger); // Toolbars --------------------------------------------------------- // NOTICE: Please ensure that any tool bar object names here match the names assigned // to identical or similar toolbars in class MusE or other TopWin classes. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). addToolBarBreak(); editTools = new EditToolBar(this, arranger->arrangerTools); addToolBar(editTools); // Make sure name doesn't conflict with other TopWin edit toolbar object names. editTools->setObjectName("arrangerTools"); addToolBar(visTracks); partColorToolBar = new PartColorToolbar(this); partColorToolBar->setObjectName("partColors"); addToolBar(partColorToolBar); connect(partColorToolBar, SIGNAL(partColorTriggered(int)), arranger->getCanvas(), SLOT(setPartColor(int))); connect(partColorToolBar, SIGNAL(partColorIndexChanged(int)), arranger->getCanvas(), SLOT(setCurrentColorIndex(int))); connect(arranger->getCanvas(), SIGNAL(curPartColorIndexChanged(int)), partColorToolBar, SLOT(setCurrentIndex(int))); connect(MusEGlobal::muse, SIGNAL(configChanged()), partColorToolBar, SLOT(configChanged())); connect(editTools, SIGNAL(toolChanged(int)), arranger, SLOT(setTool(int))); connect(visTracks, SIGNAL(visibilityChanged()), MusEGlobal::song, SLOT(update()) ); connect(arranger, SIGNAL(editPart(MusECore::Track*)), MusEGlobal::muse, SLOT(startEditor(MusECore::Track*))); connect(arranger, SIGNAL(dropSongFile(const QString&)), MusEGlobal::muse, SLOT(loadProjectFile(const QString&))); connect(arranger, SIGNAL(dropMidiFile(const QString&)), MusEGlobal::muse, SLOT(importMidi(const QString&))); connect(arranger, SIGNAL(startEditor(MusECore::PartList*,int)), MusEGlobal::muse, SLOT(startEditor(MusECore::PartList*,int))); connect(arranger, SIGNAL(toolChanged(int)), editTools, SLOT(set(int))); connect(MusEGlobal::muse, SIGNAL(configChanged()), arranger, SLOT(configChanged())); connect(arranger, SIGNAL(setUsedTool(int)), editTools, SLOT(set(int))); connect(MusEGlobal::muse, &MusE::configChanged, editTools, &EditToolBar::configChanged); //-------- Edit Actions editDeleteAction = new QAction(*deleteSVGIcon, tr("D&elete"), this); editCutAction = new QAction(*cutSVGIcon, tr("C&ut"), this); editCopyAction = new QAction(*copySVGIcon, tr("&Copy"), this); editCopyRangeAction = new QAction(*copyRangeSVGIcon, tr("Copy in Range"), this); editPasteAction = new QAction(*pasteSVGIcon, tr("&Paste"), this); editPasteCloneAction = new QAction(*pasteCloneSVGIcon, tr("Paste C&lone"), this); editPasteToTrackAction = new QAction(*pasteSelectedTrackSVGIcon, tr("Paste to Selected &Track"), this); editPasteCloneToTrackAction = new QAction(*pasteCloneSelectedTrackSVGIcon, tr("Paste Clone to Selected Trac&k"), this); editPasteDialogAction = new QAction(*pasteDialogSVGIcon, tr("Paste (With Dialo&g)..."), this); editInsertEMAction = new QAction(*emptyBarSVGIcon, tr("&Insert Empty Bar"), this); editDeleteSelectedAction = new QAction(*delSelTracksSVGIcon, tr("Delete Selected"), this); editDuplicateSelTrackAction = new QAction(*duplSelTracksSVGIcon, tr("Duplicate Selected"), this); editMoveUpSelTrackAction = new QAction(tr("Move Selected Up"), this); editMoveDownSelTrackAction = new QAction(tr("Move Selected Down"), this); editMoveTopSelTrackAction = new QAction(tr("Move Selected to Top"), this); editMoveBottomSelTrackAction = new QAction(*dummySVGIcon, tr("Move Selected to Bottom"), this); editShrinkPartsAction = new QAction(tr("Shrink Selected Parts to Events Length"), this); editExpandPartsAction = new QAction(tr("Expand Selected Parts to Events Length"), this); editCleanPartsAction = new QAction(tr("Purge Hidden Events from Selected Parts"), this); addTrack = new QMenu(tr("Add Track"), this); insertTrack = new QMenu(tr("Insert Track"), this); select = new QMenu(tr("Se&lect Parts"), this); editSelectAllAction = new QAction(*selectAllSVGIcon, tr("Select &All"), this); editDeselectAllAction = new QAction(*deselectAllSVGIcon, tr("&Deselect All"), this); editInvertSelectionAction = new QAction(*selectInvertSVGIcon, tr("Invert &Selection"), this); editInsideLoopAction = new QAction(*selectInsideLoopSVGIcon, tr("&Inside Loop"), this); editOutsideLoopAction = new QAction(*selectOutsideLoopSVGIcon, tr("&Outside Loop"), this); editAllPartsAction = new QAction(*selectAllTrackSVGIcon, tr("All &Parts on Track"), this); editRangeToSelection = new QAction(*rangeToSelectionSVGIcon, tr("Set &Range to Selection"), this); select->addAction(editSelectAllAction); select->addAction(editDeselectAllAction); select->addAction(editInvertSelectionAction); select->addAction(editInsideLoopAction); select->addAction(editOutsideLoopAction); select->addAction(editAllPartsAction); select->addSeparator(); select->addAction(editRangeToSelection); scoreSubmenu = new QMenu(tr("Score"), this); scoreAllInOneSubsubmenu = new QMenu(tr("All Tracks in One Staff"), this); scoreOneStaffPerTrackSubsubmenu = new QMenu(tr("One Staff per Track"), this); startScoreEditAction = new QAction(*scoreeditSVGIcon, tr("New Score Window..."), this); scoreSubmenu->addAction(startScoreEditAction); scoreSubmenu->addMenu(scoreAllInOneSubsubmenu); scoreSubmenu->addMenu(scoreOneStaffPerTrackSubsubmenu); updateScoreMenus(); startPianoEditAction = new QAction(*pianorollSVGIcon, tr("Pianoroll..."), this); startDrumEditAction = new QAction(*drumeditSVGIcon, tr("Drums..."), this); startListEditAction = new QAction(*listeditSVGIcon, tr("Event List..."), this); startWaveEditAction = new QAction(*waveeditorSVGIcon, tr("Wave..."), this); editorNewSubmenu = new QMenu(tr("Open Editor in New Window"), this); editorNewSubmenu->menuAction()->setStatusTip(tr("Always open the part(s) in a new editor window")); startPianoEditNewAction = new QAction(*pianorollSVGIcon, tr("Pianoroll..."), this); startDrumEditNewAction = new QAction(*drumeditSVGIcon, tr("Drums..."), this); startListEditNewAction = new QAction(*listeditSVGIcon, tr("Event List..."), this); startWaveEditNewAction = new QAction(*waveeditorSVGIcon, tr("Wave..."), this); editorNewSubmenu->addAction(startPianoEditNewAction); editorNewSubmenu->addAction(startDrumEditNewAction); editorNewSubmenu->addAction(startListEditNewAction); editorNewSubmenu->addAction(startWaveEditNewAction); openCurrentTrackSynthGuiAction = new QAction(*synthGuiSVGIcon, tr("Open Synth Plugin GUI..."), this); midiTransformerAction = new QAction(*midiTransformSVGIcon, tr("Midi &Transform..."), this); //-------- Structure Actions strGlobalCutAction = new QAction(tr("Global Cut"), this); strGlobalInsertAction = new QAction(tr("Global Insert"), this); strGlobalSplitAction = new QAction(tr("Global Split"), this); strGlobalCutSelAction = new QAction(tr("Global Cut - Selected Tracks"), this); strGlobalInsertSelAction = new QAction(tr("Global Insert - Selected Tracks"), this); strGlobalSplitSelAction = new QAction(tr("Global Split - Selected Tracks"), this); //------------------------------------------------------------- // popup Edit //------------------------------------------------------------- QMenu* menuEdit = menuBar()->addMenu(tr("&Edit")); menuEdit->addActions(MusEGlobal::undoRedo->actions()); menuEdit->addSeparator(); QMenu* menuStructure = menuEdit->addMenu(tr("&Global")); menuStructure->addAction(strGlobalCutAction); menuStructure->addAction(strGlobalInsertAction); menuStructure->addAction(strGlobalSplitAction); menuStructure->addSeparator(); menuStructure->addAction(strGlobalCutSelAction); menuStructure->addAction(strGlobalInsertSelAction); menuStructure->addAction(strGlobalSplitSelAction); // menuEdit->addSeparator(); QMenu* menuTracks = menuEdit->addMenu(tr("&Tracks")); menuTracks->addMenu(addTrack); menuTracks->addMenu(insertTrack); menuTracks->addSeparator(); menuTracks->addAction(editDuplicateSelTrackAction); menuTracks->addAction(editDeleteSelectedAction); menuTracks->addSeparator(); menuTracks->addAction(editMoveUpSelTrackAction); menuTracks->addAction(editMoveDownSelTrackAction); menuTracks->addAction(editMoveTopSelTrackAction); menuTracks->addAction(editMoveBottomSelTrackAction); menuEdit->addSeparator(); menuEdit->addMenu(select); menuEdit->addSeparator(); menuEdit->addAction(editDeleteAction); menuEdit->addAction(editCutAction); menuEdit->addAction(editCopyAction); menuEdit->addAction(editCopyRangeAction); menuEdit->addAction(editPasteAction); QMenu* menuPaste = menuEdit->addMenu(tr("Paste &Special")); menuPaste->addAction(editPasteToTrackAction); menuPaste->addAction(editPasteCloneAction); menuPaste->addAction(editPasteCloneToTrackAction); menuPaste->addAction(editPasteDialogAction); menuEdit->addAction(editInsertEMAction); menuEdit->addSeparator(); menuEdit->addAction(startPianoEditAction); menuEdit->addAction(startDrumEditAction); menuEdit->addAction(startListEditAction); menuEdit->addAction(startWaveEditAction); menuEdit->addMenu(editorNewSubmenu); menuEdit->addMenu(scoreSubmenu); menuEdit->addSeparator(); menuEdit->addAction(openCurrentTrackSynthGuiAction); QMenu* functions_menu = menuBar()->addMenu(tr("Fu&nctions")); functions_menu->addAction(midiTransformerAction); functions_menu->addSeparator(); QAction* func_quantize_action = functions_menu->addAction(*quantizeSVGIcon, tr("&Quantize Events")); QAction* func_velocity_action = functions_menu->addAction(tr("Change Event &Velocity")); QAction* func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo")); QAction* func_move_action = functions_menu->addAction(tr("Move Events in Parts")); QAction* func_del_overlaps_action = functions_menu->addAction(tr("Delete Overlapping Events")); QAction* func_erase_action = functions_menu->addAction(tr("Erase Events from Parts")); functions_menu->addSeparator(); QAction* func_notelen_action = functions_menu->addAction(tr("Change Event &Length")); QAction* func_fixed_len_action = functions_menu->addAction(tr("Set Fixed Event Length")); QAction* func_transpose_action = functions_menu->addAction(tr("Transpose")); QAction* func_legato_action = functions_menu->addAction(tr("Legato")); connect(func_quantize_action, &QAction::triggered, [this]() { cmd(CMD_QUANTIZE); } ); connect(func_notelen_action, &QAction::triggered, [this]() { cmd(CMD_NOTELEN); } ); connect(func_velocity_action, &QAction::triggered, [this]() { cmd(CMD_VELOCITY); } ); connect(func_cresc_action, &QAction::triggered, [this]() { cmd(CMD_CRESCENDO); } ); connect(func_transpose_action, &QAction::triggered, [this]() { cmd(CMD_TRANSPOSE); } ); connect(func_erase_action, &QAction::triggered, [this]() { cmd(CMD_ERASE); } ); connect(func_move_action, &QAction::triggered, [this]() { cmd(CMD_MOVE); } ); connect(func_fixed_len_action, &QAction::triggered, [this]() { cmd(CMD_FIXED_LEN); } ); connect(func_del_overlaps_action, &QAction::triggered, [this]() { cmd(CMD_DELETE_OVERLAPS); } ); connect(func_legato_action, &QAction::triggered, [this]() { cmd(CMD_LEGATO); } ); functions_menu->addSeparator(); functions_menu->addAction(editShrinkPartsAction); functions_menu->addAction(editExpandPartsAction); functions_menu->addAction(editCleanPartsAction); functions_menu->addSeparator(); menuScripts = functions_menu->addMenu(tr("&Scripts")); menuScripts->menuAction()->setStatusTip(tr("Scripts for midi processing. Applied to selected midi parts (or else tracks). User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki.")); scripts.populateScriptMenu(menuScripts); //--------------------------------------------------- // Connect script receiver //--------------------------------------------------- connect(&scripts, &MusECore::Scripts::execDeliveredScriptReceived, [this](int id) { execDeliveredScript(id); } ); connect(&scripts, &MusECore::Scripts::execUserScriptReceived, [this](int id) { execUserScript(id); } ); QMenu* menuSettings = menuBar()->addMenu(tr("&Display")); menuSettings->menuAction()->setStatusTip(tr("Display menu: Display options specific to current editor.")); menuSettings->addAction(*mixerSVGIcon, tr("Toggle &Mixer Strip"), this, SLOT(toggleMixerStrip()), MusEGui::shortcuts[MusEGui::SHRT_HIDE_MIXER_STRIP].key); menuSettings->addAction(tr("Configure &Custom Columns..."), this, SLOT(configCustomColumns())); //-------- Edit connections connect(editDeleteAction, &QAction::triggered, [this]() { cmd(CMD_DELETE); } ); connect(editCutAction, &QAction::triggered, [this]() { cmd(CMD_CUT); } ); connect(editCopyAction, &QAction::triggered, [this]() { cmd(CMD_COPY); } ); connect(editCopyRangeAction, &QAction::triggered, [this]() { cmd(CMD_COPY_RANGE); } ); connect(editPasteAction, &QAction::triggered, [this]() { cmd(CMD_PASTE); } ); connect(editPasteCloneAction, &QAction::triggered, [this]() { cmd(CMD_PASTE_CLONE); } ); connect(editPasteToTrackAction, &QAction::triggered, [this]() { cmd(CMD_PASTE_TO_TRACK); } ); connect(editPasteCloneToTrackAction, &QAction::triggered, [this]() { cmd(CMD_PASTE_CLONE_TO_TRACK); } ); connect(editPasteDialogAction, &QAction::triggered, [this]() { cmd(CMD_PASTE_DIALOG); } ); connect(editInsertEMAction, &QAction::triggered, [this]() { cmd(CMD_INSERTMEAS); } ); connect(editDeleteSelectedAction, &QAction::triggered, [this]() { cmd(CMD_DELETE_TRACK); } ); connect(editDuplicateSelTrackAction, &QAction::triggered, [this]() { cmd(CMD_DUPLICATE_TRACK); } ); connect(editMoveUpSelTrackAction, &QAction::triggered, [this]() { cmd(CMD_MOVEUP_TRACK); } ); connect(editMoveDownSelTrackAction, &QAction::triggered, [this]() { cmd(CMD_MOVEDOWN_TRACK); } ); connect(editMoveTopSelTrackAction, &QAction::triggered, [this]() { cmd(CMD_MOVETOP_TRACK); } ); connect(editMoveBottomSelTrackAction, &QAction::triggered, [this]() { cmd(CMD_MOVEBOTTOM_TRACK); } ); connect(editShrinkPartsAction, &QAction::triggered, [this]() { cmd(CMD_SHRINK_PART); } ); connect(editExpandPartsAction, &QAction::triggered, [this]() { cmd(CMD_EXPAND_PART); } ); connect(editCleanPartsAction, &QAction::triggered, [this]() { cmd(CMD_CLEAN_PART); } ); connect(editSelectAllAction, &QAction::triggered, [this]() { cmd(CMD_SELECT_ALL); } ); connect(editDeselectAllAction, &QAction::triggered, [this]() { cmd(CMD_SELECT_NONE); } ); connect(editInvertSelectionAction, &QAction::triggered, [this]() { cmd(CMD_SELECT_INVERT); } ); connect(editInsideLoopAction, &QAction::triggered, [this]() { cmd(CMD_SELECT_ILOOP); } ); connect(editOutsideLoopAction, &QAction::triggered, [this]() { cmd(CMD_SELECT_OLOOP); } ); connect(editAllPartsAction, &QAction::triggered, [this]() { cmd(CMD_SELECT_PARTS); } ); connect(editRangeToSelection, &QAction::triggered, [this]() { cmd(CMD_RANGE_TO_SELECTION); } ); connect(startPianoEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startPianoroll())); connect(startScoreEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startScoreQuickly())); connect(startDrumEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startDrumEditor())); connect(startListEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startListEditor())); connect(startWaveEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startWaveEditor())); connect(startPianoEditNewAction, &QAction::triggered, MusEGlobal::muse, [=](){MusEGlobal::muse->startPianoroll(true);}); connect(startDrumEditNewAction, &QAction::triggered, MusEGlobal::muse, [=](){MusEGlobal::muse->startDrumEditor(true);}); connect(startListEditNewAction, &QAction::triggered, MusEGlobal::muse, [=](){MusEGlobal::muse->startListEditor(true);}); connect(startWaveEditNewAction, &QAction::triggered, MusEGlobal::muse, [=](){MusEGlobal::muse->startWaveEditor(true);}); connect(openCurrentTrackSynthGuiAction, SIGNAL(triggered()), SLOT(openCurrentTrackSynthGui())); connect(midiTransformerAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startMidiTransformer())); //-------- Structure connections connect(strGlobalCutAction, SIGNAL(triggered()), SLOT(globalCut())); connect(strGlobalInsertAction, SIGNAL(triggered()), SLOT(globalInsert())); connect(strGlobalSplitAction, SIGNAL(triggered()), SLOT(globalSplit())); connect(strGlobalCutSelAction, SIGNAL(triggered()), SLOT(globalCutSel())); connect(strGlobalInsertSelAction, SIGNAL(triggered()), SLOT(globalInsertSel())); connect(strGlobalSplitSelAction, SIGNAL(triggered()), SLOT(globalSplitSel())); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(updateShortcuts())); QClipboard* cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged())); connect(cb, SIGNAL(selectionChanged()), SLOT(clipboardChanged())); finalizeInit(); } ArrangerView::~ArrangerView() { } void ArrangerView::closeEvent(QCloseEvent* e) { // fprintf(stderr, "*** Arranger closed event caught ***\n"); // e.g. MMB e->ignore(); } void ArrangerView::storeSettings() { arranger->storeSplitterSizes(); } void ArrangerView::writeStatus(int level, MusECore::Xml& xml) const { xml.tag(level++, "arrangerview"); TopWin::writeStatus(level, xml); xml.intTag(level, "tool", editTools->curTool()); arranger->writeStatus(level,xml); xml.tag(level, "/arrangerview"); } void ArrangerView::readStatus(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "tool") editTools->set(xml.parseInt()); else if (tag == "topwin") TopWin::readStatus(xml); else if (tag == "arranger") arranger->readStatus(xml); else xml.unknown("ArrangerView"); break; case MusECore::Xml::TagEnd: if (tag == "arrangerview") return; default: break; } } } //--------------------------------------------------------- // readConfiguration //--------------------------------------------------------- void ArrangerView::readConfiguration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "topwin") TopWin::readConfiguration(ARRANGER, xml); else if (tag == "arranger") Arranger::readConfiguration(xml); else xml.unknown("ArrangerView"); break; case MusECore::Xml::TagEnd: if (tag == "arrangerview") return; default: break; } } } //--------------------------------------------------------- // writeConfiguration //--------------------------------------------------------- void ArrangerView::writeConfiguration(int level, MusECore::Xml& xml) { xml.tag(level++, "arrangerview"); TopWin::writeConfiguration(ARRANGER, level, xml); arranger->writeConfiguration(level,xml); xml.tag(level, "/arrangerview"); } //--------------------------------------------------------- // tagItems //--------------------------------------------------------- void ArrangerView::tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const { const bool tagSelected = options._flags & MusECore::TagSelected; const bool tagAllItems = options._flags & MusECore::TagAllItems; const bool tagAllParts = options._flags & MusECore::TagAllParts; const bool range = options._flags & MusECore::TagRange; const MusECore::Pos& p0 = options._p0; const MusECore::Pos& p1 = options._p1; if(tagAllParts || tagAllItems) { MusECore::Track* track; MusECore::PartList* pl; MusECore::Part* part; MusECore::Pos pos, part_pos, part_endpos; MusECore::TrackList* tl = MusEGlobal::song->tracks(); for(MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) { track = *it; pl = track->parts(); for(MusECore::ciPart ip = pl->begin(); ip != pl->end(); ++ip) { part = ip->second; if(!tagAllParts && !track->isVisible()) continue; if(tagAllParts || (tagSelected && part->selected()) // || (tagMoving && part->isMoving()) // TODO ) { if(tagAllItems) { if(range) { part_pos = *part; part_endpos = part->end(); // Optimize: Is the part within the range? // p1 should be considered outside (one past) the very last position in the range. if(part_endpos <= p0 || part_pos >= p1) continue; } MusECore::EventList& el = part->nonconst_events(); for(MusECore::iEvent ie = el.begin(); ie != el.end(); ++ie) { const MusECore::Event& e = ie->second; if(range) { // Don't forget to add the part's position. pos = e.pos() + part_pos; // If the event position is before p0, keep looking... if(pos < p0) continue; // If the event position is at or after p1 then we are done. // p1 should be considered outside (one past) the very last position in the range. if(pos >= p1) break; } tag_list->add(part, e); } } else { tag_list->add(part); } } } } } else { // This step uses the tagging features to mark the objects (events) // as having been visited already, to avoid duplicates in the list. if(arranger && arranger->getCanvas()) { MusECore::EventTagOptionsStruct opts = options; opts.removeFlags(MusECore::TagAllItems | MusECore::TagAllParts); arranger->getCanvas()->tagItems(tag_list, opts); } } } void ArrangerView::cmd(int cmd) { // Don't process if user is dragging or has clicked on the parts. // Causes problems/crashes later in Canvas::viewMouseMoveEvent and viewMouseReleaseEvent. if(arranger && arranger->getCanvas() && arranger->getCanvas()->getCurrentDrag()) return; MusECore::TrackList* tracks = MusEGlobal::song->tracks(); int l = MusEGlobal::song->lpos(); int r = MusEGlobal::song->rpos(); MusECore::TagEventList tag_list; const FunctionDialogElements_t fn_element_dflt = FunctionAllEventsButton | FunctionLoopedButton | FunctionAllPartsButton | FunctionSelectedPartsButton; switch(cmd) { case CMD_CUT: arranger->cmd(Arranger::CMD_CUT_PART); break; case CMD_COPY: arranger->cmd(Arranger::CMD_COPY_PART); break; case CMD_COPY_RANGE: arranger->cmd(Arranger::CMD_COPY_PART_IN_RANGE); break; case CMD_PASTE: arranger->cmd(Arranger::CMD_PASTE_PART); break; case CMD_PASTE_CLONE: arranger->cmd(Arranger::CMD_PASTE_CLONE_PART); break; case CMD_PASTE_TO_TRACK: arranger->cmd(Arranger::CMD_PASTE_PART_TO_TRACK); break; case CMD_PASTE_CLONE_TO_TRACK: arranger->cmd(Arranger::CMD_PASTE_CLONE_PART_TO_TRACK); break; case CMD_PASTE_DIALOG: arranger->cmd(Arranger::CMD_PASTE_DIALOG); break; case CMD_INSERTMEAS: arranger->cmd(Arranger::CMD_INSERT_EMPTYMEAS); break; case CMD_DELETE: if (!MusECore::delete_selected_parts()) { QMessageBox::StandardButton btn = QMessageBox::warning( this,tr("Remove track(s)"),tr("Are you sure you want to remove this track(s)?"), QMessageBox::Ok|QMessageBox::Cancel, QMessageBox::Ok); if (btn == QMessageBox::Cancel) break; MusEGlobal::audio->msgRemoveTracks(); } break; case CMD_DELETE_TRACK: // from menu { MusEGlobal::audio->msgRemoveTracks(); } break; case CMD_DUPLICATE_TRACK: MusEGlobal::song->duplicateTracks(); break; case CMD_MOVEUP_TRACK: arranger->getTrackList()->moveSelectedTracks(true, false); break; case CMD_MOVEDOWN_TRACK: arranger->getTrackList()->moveSelectedTracks(false, false); break; case CMD_MOVETOP_TRACK: arranger->getTrackList()->moveSelectedTracks(true, true); break; case CMD_MOVEBOTTOM_TRACK: arranger->getTrackList()->moveSelectedTracks(false, true); break; case CMD_SELECT_ALL: case CMD_SELECT_NONE: case CMD_SELECT_INVERT: case CMD_SELECT_ILOOP: case CMD_SELECT_OLOOP: for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); ++i) { MusECore::PartList* parts = (*i)->parts(); for (MusECore::iPart p = parts->begin(); p != parts->end(); ++p) { bool f = false; int t1 = p->second->tick(); int t2 = t1 + p->second->lenTick(); bool inside = ((t1 >= l) && (t1 < r)) || ((t2 > l) && (t2 < r)) || ((t1 <= l) && (t2 > r)); switch(cmd) { case CMD_SELECT_INVERT: f = !p->second->selected(); break; case CMD_SELECT_NONE: f = false; break; case CMD_SELECT_ALL: f = true; break; case CMD_SELECT_ILOOP: f = inside; break; case CMD_SELECT_OLOOP: f = !inside; break; } p->second->setSelected(f); } } MusEGlobal::song->update(); break; case CMD_SELECT_PARTS: for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); ++i) { if (!(*i)->selected()) continue; MusECore::PartList* parts = (*i)->parts(); for (MusECore::iPart p = parts->begin(); p != parts->end(); ++p) p->second->setSelected(true); } MusEGlobal::song->update(); break; case CMD_RANGE_TO_SELECTION: arranger->getCanvas()->setRangeToSelection(); break; case CMD_SHRINK_PART: MusECore::shrink_parts(); break; case CMD_EXPAND_PART: MusECore::expand_parts(); break; case CMD_CLEAN_PART: MusECore::clean_parts(); break; case CMD_QUANTIZE: { FunctionDialogReturnQuantize ret = quantize_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::quantize_items(&tag_list, ret._raster_index, ret._quant_len, ret._strength, ret._swing, ret._threshold); } break; } case CMD_VELOCITY: { FunctionDialogReturnVelocity ret = velocity_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::modify_velocity_items(&tag_list, ret._rateVal, ret._offsetVal); } break; } case CMD_CRESCENDO: { FunctionDialogReturnCrescendo ret = crescendo_items_dialog(FunctionDialogMode( FunctionLoopedButton | FunctionSelectedLoopedButton | FunctionAllPartsButton | FunctionSelectedPartsButton)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::crescendo_items(&tag_list, ret._start_val, ret._end_val, ret._absolute); } break; } case CMD_NOTELEN: { FunctionDialogReturnGateTime ret = gatetime_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::modify_notelen_items(&tag_list, ret._rateVal, ret._offsetVal); } break; } case CMD_TRANSPOSE: { FunctionDialogReturnTranspose ret = transpose_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::transpose_items(&tag_list, ret._amount); } break; } case CMD_ERASE: { FunctionDialogReturnErase ret = erase_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::erase_items(&tag_list, ret._veloThreshold, ret._veloThresUsed, ret._lenThreshold, ret._lenThresUsed); } } break; case CMD_MOVE: { FunctionDialogReturnMove ret = move_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::move_items(&tag_list, ret._amount); } break; } case CMD_FIXED_LEN: { FunctionDialogReturnSetLen ret = setlen_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::set_notelen_items(&tag_list, ret._len); } break; } case CMD_DELETE_OVERLAPS: { FunctionDialogReturnDelOverlaps ret = deloverlaps_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::delete_overlaps_items(&tag_list); } break; } case CMD_LEGATO: { FunctionDialogReturnLegato ret = legato_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::legato_items(&tag_list, ret._min_len, !ret._allow_shortening); } break; } } } void ArrangerView::scoreNamingChanged() { updateScoreMenus(); } void ArrangerView::updateScoreMenus() { QAction* action; scoreOneStaffPerTrackSubsubmenu->clear(); scoreAllInOneSubsubmenu->clear(); action=new QAction(tr("New..."), this); connect(action, &QAction::triggered, []() { MusEGlobal::muse->openInScoreEdit_oneStaffPerTrack(nullptr); } ); scoreOneStaffPerTrackSubsubmenu->addAction(action); action=new QAction(tr("New..."), this); //the above action may NOT be reused! connect(action, &QAction::triggered, []() { MusEGlobal::muse->openInScoreEdit_allInOne(nullptr); } ); scoreAllInOneSubsubmenu->addAction(action); const ToplevelList* toplevels=MusEGlobal::muse->getToplevels(); for (ToplevelList::const_iterator it=toplevels->begin(); it!=toplevels->end(); it++) if ((*it)->type()==TopWin::SCORE) { ScoreEdit* score = dynamic_cast(*it); action=new QAction(score->get_name(), this); connect(action, &QAction::triggered, [score]() { MusEGlobal::muse->openInScoreEdit_oneStaffPerTrack(score); } ); scoreOneStaffPerTrackSubsubmenu->addAction(action); action=new QAction(score->get_name(), this); //the above action may NOT be reused! connect(action, &QAction::triggered, [score]() { MusEGlobal::muse->openInScoreEdit_allInOne(score); } ); scoreAllInOneSubsubmenu->addAction(action); } } // populate both add and insert menus with track types void ArrangerView::populateAddTrack() { // populate add track menu QActionGroup *addGroup = MusEGui::populateAddTrack(addTrack, true); connect(addTrack, SIGNAL(triggered(QAction *)), SLOT(addNewTrack(QAction *))); int idx = 0; trackAMidiAction = addGroup->actions().at(idx++); trackADrumAction = addGroup->actions().at(idx++); trackAWaveAction = addGroup->actions().at(idx++); trackAOutputAction = addGroup->actions().at(idx++); trackAGroupAction = addGroup->actions().at(idx++); trackAInputAction = addGroup->actions().at(idx++); trackAAuxAction = addGroup->actions().at(idx++); trackASynthAction = addGroup->actions().at(idx++); // populate insert track menu QActionGroup *insertGroup = MusEGui::populateAddTrack(insertTrack, true, true); connect(insertTrack, SIGNAL(triggered(QAction *)), SLOT(insertNewTrack(QAction *))); idx = 0; trackIMidiAction = insertGroup->actions().at(idx++); trackIDrumAction = insertGroup->actions().at(idx++); trackIWaveAction = insertGroup->actions().at(idx++); trackIOutputAction = insertGroup->actions().at(idx++); trackIGroupAction = insertGroup->actions().at(idx++); trackIInputAction = insertGroup->actions().at(idx++); trackIAuxAction = insertGroup->actions().at(idx++); trackISynthAction = insertGroup->actions().at(idx++); // populate right click menu on trackList arranger->getTrackList()->populateAddTrack(); } void ArrangerView::addNewTrack(QAction* action) { MusEGlobal::song->addNewTrack(action, nullptr); // Add at the end } void ArrangerView::insertNewTrack(QAction* action) { auto curTrack = MusEGlobal::muse->arranger()->curTrack(); MusEGlobal::song->addNewTrack(action, curTrack); // Insert before current selected track or at the end } void ArrangerView::updateShortcuts() { editDeleteAction->setShortcut(shortcuts[SHRT_DELETE].key); editCutAction->setShortcut(shortcuts[SHRT_CUT].key); editCopyAction->setShortcut(shortcuts[SHRT_COPY].key); editCopyRangeAction->setShortcut(shortcuts[SHRT_COPY_RANGE].key); editPasteAction->setShortcut(shortcuts[SHRT_PASTE].key); editPasteCloneAction->setShortcut(shortcuts[SHRT_PASTE_CLONE].key); editPasteToTrackAction->setShortcut(shortcuts[SHRT_PASTE_TO_TRACK].key); editPasteCloneToTrackAction->setShortcut(shortcuts[SHRT_PASTE_CLONE_TO_TRACK].key); editPasteDialogAction->setShortcut(shortcuts[SHRT_PASTE_DIALOG].key); editInsertEMAction->setShortcut(shortcuts[SHRT_INSERTMEAS].key); editDuplicateSelTrackAction->setShortcut(shortcuts[SHRT_DUPLICATE_TRACK].key); editMoveUpSelTrackAction->setShortcut(shortcuts[SHRT_MOVEUP_TRACK].key); editMoveDownSelTrackAction->setShortcut(shortcuts[SHRT_MOVEDOWN_TRACK].key); editMoveTopSelTrackAction->setShortcut(shortcuts[SHRT_MOVETOP_TRACK].key); editMoveBottomSelTrackAction->setShortcut(shortcuts[SHRT_MOVEBOTTOM_TRACK].key); //editDeleteSelectedAction has no acceleration trackAMidiAction->setShortcut(shortcuts[SHRT_ADD_MIDI_TRACK].key); trackADrumAction->setShortcut(shortcuts[SHRT_ADD_DRUM_TRACK].key); trackAWaveAction->setShortcut(shortcuts[SHRT_ADD_WAVE_TRACK].key); trackAOutputAction->setShortcut(shortcuts[SHRT_ADD_AUDIO_OUTPUT].key); trackAGroupAction->setShortcut(shortcuts[SHRT_ADD_AUDIO_GROUP].key); trackAInputAction->setShortcut(shortcuts[SHRT_ADD_AUDIO_INPUT].key); trackAAuxAction->setShortcut(shortcuts[SHRT_ADD_AUDIO_AUX].key); trackASynthAction->setShortcut(shortcuts[SHRT_ADD_SYNTH_TRACK].key); trackIMidiAction->setShortcut(shortcuts[SHRT_INSERT_MIDI_TRACK].key); trackIDrumAction->setShortcut(shortcuts[SHRT_INSERT_DRUM_TRACK].key); trackIWaveAction->setShortcut(shortcuts[SHRT_INSERT_WAVE_TRACK].key); trackIOutputAction->setShortcut(shortcuts[SHRT_INSERT_AUDIO_OUTPUT].key); trackIGroupAction->setShortcut(shortcuts[SHRT_INSERT_AUDIO_GROUP].key); trackIInputAction->setShortcut(shortcuts[SHRT_INSERT_AUDIO_INPUT].key); trackIAuxAction->setShortcut(shortcuts[SHRT_INSERT_AUDIO_AUX].key); trackISynthAction->setShortcut(shortcuts[SHRT_INSERT_SYNTH_TRACK].key); arranger->getTrackList()->populateAddTrack(); editSelectAllAction->setShortcut(shortcuts[SHRT_SELECT_ALL].key); editDeselectAllAction->setShortcut(shortcuts[SHRT_SELECT_NONE].key); editInvertSelectionAction->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); editInsideLoopAction->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); editOutsideLoopAction->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); editAllPartsAction->setShortcut(shortcuts[SHRT_SELECT_PRTSTRACK].key); editRangeToSelection->setShortcut(shortcuts[SHRT_LOCATORS_TO_SELECTION].key); startPianoEditAction->setShortcut(shortcuts[SHRT_OPEN_PIANO].key); startDrumEditAction->setShortcut(shortcuts[SHRT_OPEN_DRUMS].key); startListEditAction->setShortcut(shortcuts[SHRT_OPEN_LIST].key); startWaveEditAction->setShortcut(shortcuts[SHRT_OPEN_WAVE].key); startPianoEditNewAction->setShortcut(shortcuts[SHRT_OPEN_PIANO_NEW].key); startDrumEditNewAction->setShortcut(shortcuts[SHRT_OPEN_DRUMS_NEW].key); startListEditNewAction->setShortcut(shortcuts[SHRT_OPEN_LIST_NEW].key); startWaveEditNewAction->setShortcut(shortcuts[SHRT_OPEN_WAVE_NEW].key); openCurrentTrackSynthGuiAction->setShortcut(shortcuts[SHRT_OPEN_PLUGIN_GUI].key); midiTransformerAction->setShortcut(shortcuts[SHRT_OPEN_MIDI_TRANSFORM].key); strGlobalCutAction->setShortcut(shortcuts[SHRT_GLOBAL_CUT].key); strGlobalInsertAction->setShortcut(shortcuts[SHRT_GLOBAL_INSERT].key); strGlobalSplitAction->setShortcut(shortcuts[SHRT_GLOBAL_SPLIT].key); } //--------------------------------------------------------- // clipboardChanged //--------------------------------------------------------- void ArrangerView::clipboardChanged() { bool flag = false; if(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-midipartlist")) || QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-wavepartlist")) || QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-mixedpartlist"))) flag = true; editPasteAction->setEnabled(flag); editPasteCloneAction->setEnabled(flag); editPasteToTrackAction->setEnabled(flag); editPasteCloneToTrackAction->setEnabled(flag); editPasteDialogAction->setEnabled(flag); } //--------------------------------------------------------- // selectionChanged // NOTE: This is received upon EITHER a part or track selection change from the Arranger. //--------------------------------------------------------- void ArrangerView::selectionChanged() { bool pflag = arranger->itemsAreSelected(); bool tflag = MusECore::tracks_are_selected(); editDeleteAction->setEnabled(tflag || pflag); editDeleteSelectedAction->setEnabled(tflag); editDuplicateSelTrackAction->setEnabled(tflag); editMoveUpSelTrackAction->setEnabled(tflag); editMoveDownSelTrackAction->setEnabled(tflag); editMoveTopSelTrackAction->setEnabled(tflag); editMoveBottomSelTrackAction->setEnabled(tflag); editCutAction->setEnabled(pflag); editCopyAction->setEnabled(pflag); editShrinkPartsAction->setEnabled(pflag); editExpandPartsAction->setEnabled(pflag); editCleanPartsAction->setEnabled(pflag); } void ArrangerView::updateVisibleTracksButtons() { visTracks->updateVisibleTracksButtons(); } Arranger* ArrangerView::getArranger() const {return arranger;} void ArrangerView::focusCanvas() { arranger->focusCanvas(); } void ArrangerView::globalCut() { MusECore::globalCut(); } void ArrangerView::globalInsert() { MusECore::globalInsert(); } void ArrangerView::globalSplit() { MusECore::globalSplit(); } // variants only applicable for selected tracks void ArrangerView::globalCutSel() { MusECore::globalCut(true); } void ArrangerView::globalInsertSel() { MusECore::globalInsert(true); } void ArrangerView::globalSplitSel() { MusECore::globalSplit(true); } void ArrangerView::openCurrentTrackSynthGui() { auto curTrack = MusEGlobal::muse->arranger()->curTrack(); if (curTrack->isSynthTrack() || curTrack->isMidiTrack()) openSynthGui(curTrack); } void ArrangerView::configCustomColumns() { auto tmp = Arranger::custom_columns; ArrangerColumns* dialog = new ArrangerColumns(this); int rc = dialog->exec(); delete dialog; if (rc == QDialog::Accepted) arranger->updateHeaderCustomColumns(); else Arranger::custom_columns = tmp; } void ArrangerView::toggleMixerStrip() { arranger->toggleTrackInfo(); } //--------------------------------------------------------- // execDeliveredScript //--------------------------------------------------------- void ArrangerView::execDeliveredScript(int id) { if (MusECore::getSelectedMidiParts()->empty()) { QMessageBox::warning(this, QString("MusE"), tr("Nothing to edit")); return; } scripts.executeScript(this, scripts.getScriptPath(id, true).toLatin1().constData(), MusECore::getSelectedMidiParts(), 0, false); // TODO: get quant from arranger } //--------------------------------------------------------- // execUserScript //--------------------------------------------------------- void ArrangerView::execUserScript(int id) { if (MusECore::getSelectedMidiParts()->empty()) { QMessageBox::warning(this, QString("MusE"), tr("Nothing to edit")); return; } scripts.executeScript(this, scripts.getScriptPath(id, false).toLatin1().constData(), MusECore::getSelectedMidiParts(), 0, false); // TODO: get quant from arranger } } // namespace MusEGui muse-4.0.0/src/muse/arranger/arrangerview.h000066400000000000000000000141141404105311000206600ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // arrangerview.h // (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ARRANGERVIEW_H__ #define __ARRANGERVIEW_H__ #include #include "type_defs.h" #include "cobject.h" #include "scripts.h" // Forward declarations: class QCloseEvent; class QAction; class QGridLayout; class QMenu; namespace MusECore { class TagEventList; struct EventTagOptionsStruct; } namespace MusEGui { class EditToolBar; class VisibleTracks; class PartColorToolbar; class Arranger; class Xml; class ArrangerView : public TopWin { Q_OBJECT private: enum cmd_enum {CMD_CUT, CMD_COPY, CMD_COPY_RANGE, CMD_PASTE, CMD_INSERTMEAS, CMD_PASTE_CLONE, CMD_PASTE_TO_TRACK, CMD_PASTE_CLONE_TO_TRACK, CMD_PASTE_DIALOG, CMD_DELETE, CMD_SELECT_ALL, CMD_SELECT_NONE, CMD_SELECT_INVERT, CMD_SELECT_ILOOP, CMD_SELECT_OLOOP, CMD_SELECT_PARTS, CMD_DELETE_TRACK, CMD_DUPLICATE_TRACK, CMD_EXPAND_PART, CMD_SHRINK_PART, CMD_CLEAN_PART, CMD_QUANTIZE, CMD_VELOCITY, CMD_CRESCENDO, CMD_NOTELEN, CMD_TRANSPOSE, CMD_ERASE, CMD_MOVE, CMD_FIXED_LEN, CMD_DELETE_OVERLAPS, CMD_LEGATO, CMD_RANGE_TO_SELECTION, CMD_MOVEUP_TRACK, CMD_MOVEDOWN_TRACK, CMD_MOVETOP_TRACK, CMD_MOVEBOTTOM_TRACK }; void closeEvent(QCloseEvent*) override; QGridLayout* mainGrid; QWidget* mainw; EditToolBar* editTools; VisibleTracks* visTracks; PartColorToolbar *partColorToolBar; Arranger* arranger; // Edit Menu actions QMenu* select; QMenu* addTrack; QMenu* insertTrack; QAction *strGlobalCutAction, *strGlobalInsertAction, *strGlobalSplitAction; QAction *strGlobalCutSelAction, *strGlobalInsertSelAction, *strGlobalSplitSelAction; QAction *trackAMidiAction, *trackADrumAction, *trackAWaveAction, *trackAOutputAction, *trackAGroupAction; QAction *trackAInputAction, *trackAAuxAction, *trackASynthAction; QAction *trackIMidiAction, *trackIDrumAction, *trackIWaveAction, *trackIOutputAction, *trackIGroupAction; QAction *trackIInputAction, *trackIAuxAction, *trackISynthAction; QAction *editDeleteAction,*editCutAction, *editCopyAction, *editCopyRangeAction; QAction *editPasteAction, *editPasteCloneAction, *editPasteToTrackAction, *editPasteCloneToTrackAction, *editPasteDialogAction; QAction *editInsertEMAction, *editPasteC2TAction, *editDeleteSelectedAction, *editSelectAllAction, *editDeselectAllAction; QAction *editDuplicateSelTrackAction; QAction *editMoveUpSelTrackAction, *editMoveDownSelTrackAction, *editMoveTopSelTrackAction, *editMoveBottomSelTrackAction; QAction *editInvertSelectionAction, *editInsideLoopAction, *editOutsideLoopAction, *editAllPartsAction; QAction *editRangeToSelection; QAction *midiTransformerAction; QAction *editCleanPartsAction, *editShrinkPartsAction, *editExpandPartsAction; QAction* func_quantize_action; QAction* func_notelen_action; QAction* func_velocity_action; QAction* func_cresc_action; QAction* func_transpose_action; QAction* func_erase_action; QAction* func_move_action; QAction* func_fixed_len_action; QAction* func_del_overlaps_action; QAction* func_legato_action; MusECore::Scripts scripts; QMenu* menuScripts; private slots: void globalCut(); void globalInsert(); void globalSplit(); void openCurrentTrackSynthGui(); void globalCutSel(); void globalInsertSel(); void globalSplitSel(); void cmd(int); void addNewTrack(QAction* action); void insertNewTrack(QAction* action); void configCustomColumns(); void toggleMixerStrip(); void execDeliveredScript(int); void execUserScript(int); signals: void isDeleting(MusEGui::TopWin*); void closed(); public slots: void scoreNamingChanged(); void updateScoreMenus(); void clipboardChanged(); void selectionChanged(); // NOTE: This is received upon EITHER a part or track selection change from the Arranger. void updateShortcuts(); void updateVisibleTracksButtons(); virtual void focusCanvas() override; public: ArrangerView(QWidget* parent = nullptr); ~ArrangerView() override; QAction *startScoreEditAction, *startPianoEditAction, *startDrumEditAction, *startListEditAction, *startWaveEditAction; QMenu* editorNewSubmenu; QAction *startPianoEditNewAction, *startDrumEditNewAction, *startListEditNewAction, *startWaveEditNewAction; QAction *openCurrentTrackSynthGuiAction; QMenu *scoreSubmenu, *scoreOneStaffPerTrackSubsubmenu, *scoreAllInOneSubsubmenu; void populateAddTrack(); Arranger* getArranger() const; void writeStatus(int level, MusECore::Xml& xml) const override; void readStatus(MusECore::Xml& xml) override; static void readConfiguration(MusECore::Xml&); void writeConfiguration(int, MusECore::Xml&); void storeSettings() override; // Appends given tag list with item objects according to options. Avoids duplicate events or clone events. // Special: We 'abuse' a controller event's length, normally 0, to indicate visual item length. void tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/arranger/pcanvas.cpp000066400000000000000000005374651404105311000201750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pcanvas.cpp,v 1.48.2.26 2009/11/22 11:08:33 spamatica Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #ifdef _WIN32 #include "mman.h" #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "muse_math.h" #include "fastlog.h" #include "components/tools.h" #include "arranger.h" #include "arrangerview.h" #include "structure.h" #include "pcanvas.h" #include "globals.h" #include "icons.h" #include "ctrl.h" #include "event.h" #include "wave.h" #include "audio.h" #include "shortcuts.h" #include "gconfig.h" #include "app.h" #include "functions.h" #include "filedialog.h" #include "marker/marker.h" #include "menutitleitem.h" #include "mpevent.h" #include "midievent.h" #include "midi_consts.h" #include "midictrl.h" #include "utils.h" #include "dialogs.h" #include "components/pastedialog.h" #include "undo.h" #include "tracks_duplicate.h" #include "name_factory.h" #include "song.h" // Forwards from header: #include #include #include #include #include #include #include #include #include "ctrl.h" #include "xml.h" #include "part.h" #include "citem.h" #include using MusECore::Undo; using MusECore::UndoOp; #define ABS(x) (abs(x)) #define FABS(x) (fabs(x)) #define EDITING_FINISHED_TIMEOUT 50 /* in milliseconds */ using std::set; namespace MusEGui { const int PartCanvas::_automationPointDetectDist = 4; const int PartCanvas::_automationPointWidthUnsel = 4; const int PartCanvas::_automationPointWidthSel = 6; //--------------------------------------------------------- // NPart //--------------------------------------------------------- NPart::NPart(MusECore::Part* p) : PItem(p) { leftBorderTouches = false; rightBorderTouches = false; _serial=_part->sn(); int y = track()->y(); setPos(QPoint(_part->tick(), y)); setBBox(QRect(_part->tick(), y, _part->lenTick(), track()->height())); } //--------------------------------------------------------- // PartCanvas //--------------------------------------------------------- PartCanvas::PartCanvas(int* r, QWidget* parent, int sx, int sy) : Canvas(parent, sx, sy) { setObjectName("PartCanvas"); setAcceptDrops(true); _raster = r; setFocusPolicy(Qt::StrongFocus); // Defaults: lineEditor = nullptr; editMode = false; supportsResizeToTheLeft = true; setStatusTip(tr("Part canvas: Use Pencil tool to draw parts, or double-click to create a new MIDI/drum part between the range markers. Press F1 for help.")); tracks = MusEGlobal::song->tracks(); setMouseTracking(true); drag = DRAG_OFF; curColorIndex = 0; automation.currentCtrlValid = false; automation.controllerState = doNothing; automation.moveController = false; automation.breakUndoCombo = false; updateItems(); } PartCanvas::~PartCanvas() { } //--------------------------------------------------------- // y2pitch //--------------------------------------------------------- int PartCanvas::y2pitch(int y) const { MusECore::TrackList* tl = MusEGlobal::song->tracks(); int yy = 0; int idx = 0; MusECore::ciTrack it; for (it = tl->begin(); it != tl->end(); ++it, ++idx) { int h = (*it)->height(); if (y < yy+h) break; yy += h; } if(it == tl->end()) { while(y >= yy + MusEGlobal::config.trackHeight) { ++idx; yy += MusEGlobal::config.trackHeight; } } return idx; } //--------------------------------------------------------- // pitch2y //--------------------------------------------------------- int PartCanvas::pitch2y(int p) const { MusECore::TrackList* tl = MusEGlobal::song->tracks(); int yy = 0; int idx = 0; MusECore::ciTrack it; for (it = tl->begin(); it != tl->end(); ++it, ++idx) { if (idx == p) break; yy += (*it)->height(); } if(it == tl->end()) { yy += (p - idx) * MusEGlobal::config.trackHeight; } return yy; } //--------------------------------------------------------- // y2height //--------------------------------------------------------- int PartCanvas::y2height(int y) const { MusECore::TrackList* tl = MusEGlobal::song->tracks(); int yy = 0; for (MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) { int h = (*it)->height(); if (y < yy+h) return h; yy += h; } return MusEGlobal::config.trackHeight; } //--------------------------------------------------------- // leaveEvent //--------------------------------------------------------- void PartCanvas::leaveEvent(QEvent*) { emit timeChanged(INT_MAX); } //--------------------------------------------------------- // returnPressed //--------------------------------------------------------- void PartCanvas::returnPressed() { lineEditor->hide(); if (editMode) { //this check is necessary, because it returnPressed may be called //twice. the second call would cause a crash, however! MusECore::Part* part = editPart->part(); // Indicate do undo, and do port controller values but not clone parts. Undo operations; operations.push_back(UndoOp(UndoOp::ModifyPartName,part, part->name(), lineEditor->text())); MusEGlobal::song->applyOperationGroup(operations); editMode = false; editingFinishedTime.start(); } } //--------------------------------------------------------- // viewMouseDoubleClick //--------------------------------------------------------- void PartCanvas::viewMouseDoubleClickEvent(QMouseEvent* event) { if (_tool != PointerTool) { viewMousePressEvent(event); return; } QPoint cpos = event->pos(); curItem = items.find(cpos); bool ctrl = event->modifiers() & Qt::ControlModifier; bool alt = event->modifiers() & Qt::AltModifier; if (curItem) { if ((event->button() == Qt::LeftButton) && ctrl && alt) { deselectAll(); selectItem(curItem, true); emit dclickPart(((NPart*)(curItem))->track()); } else if ((event->button() == Qt::LeftButton) && ctrl) { editPart = (NPart*)curItem; QRect r = map(curItem->bbox()); if (lineEditor == nullptr) { lineEditor = new QLineEdit(this); lineEditor->setFrame(true); connect(lineEditor, SIGNAL(editingFinished()),SLOT(returnPressed())); } editMode = true; lineEditor->setGeometry(r); lineEditor->setText(editPart->name()); lineEditor->setFocus(); lineEditor->show(); } else if (event->button() == Qt::LeftButton) { deselectAll(); selectItem(curItem, true); emit dclickPart(((NPart*)(curItem))->track()); } } // double click creates new part between left and // right mark else { MusECore::TrackList* tl = MusEGlobal::song->tracks(); MusECore::ciTrack it; int yy = 0; int y = event->y(); for (it = tl->begin(); it != tl->end(); ++it) { int h = (*it)->height(); if (y >= yy && y < (yy + h) && (*it)->isVisible()) break; yy += h; } if (pos[2] - pos[1] > 0 && it != tl->end()) { MusECore::Track* track = *it; switch(track->type()) { case MusECore::Track::MIDI: case MusECore::Track::DRUM: { MusECore::MidiPart* part = new MusECore::MidiPart((MusECore::MidiTrack*)track); part->setTick(pos[1]); part->setLenTick(pos[2]-pos[1]); part->setName(track->name()); NPart* np = new NPart(part); items.add(np); deselectAll(); part->setSelected(true); np->setSelected(true); part->setColorIndex(curColorIndex); MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddPart, part)); } break; case MusECore::Track::WAVE: case MusECore::Track::AUDIO_OUTPUT: case MusECore::Track::AUDIO_INPUT: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_AUX: case MusECore::Track::AUDIO_SOFTSYNTH: break; } } } } //--------------------------------------------------------- // moveCanvasItems //--------------------------------------------------------- void PartCanvas::moveCanvasItems(CItemMap& items, int dp, int dx, DragType dtype, bool rasterize) { MusECore::Undo operations; // We need a map of canvas items sorted by Y position. typedef std::multimap y_map_t; y_map_t y_map; for(ciCItem ici = items.cbegin(); ici != items.cend(); ++ici) { CItem* ci = ici->second; y_map.insert(std::pair(ci->bbox().y(), ci)); } int cur_new_track_idx = -1; // Find out how many new track types we need, for the options dialog. int audio_found = 0; int midi_found = 0; int new_drum_found = 0; for(y_map_t::const_iterator ici = y_map.cbegin(); ici != y_map.cend(); ++ici) { CItem* ci = ici->second; ci->setMoving(false); const int x = ci->pos().x(); const int y = ci->pos().y(); const int nx = x + dx; const int ny = pitch2y(y2pitch(y) + dp); QPoint newpos = QPoint(nx, ny); if(rasterize) newpos = raster(newpos); const NPart* npart = (NPart*) ci; const MusECore::Part* spart = npart->part(); const MusECore::Track* track = npart->track(); const unsigned dtick = newpos.x(); // FIXME TODO make subtick-compatible! const int ntrack = y2pitch(ci->mp().y()); const MusECore::Track::TrackType type = track->type(); if (tracks->index(track) == ntrack && (dtick == spart->tick())) { continue; } if (ntrack >= (int)tracks->size()) { // Would create a new track, but only at the first part found on a track. if(ntrack != cur_new_track_idx) { cur_new_track_idx = ntrack; if(type == MusECore::Track::DRUM) ++new_drum_found; else if(type == MusECore::Track::MIDI) ++midi_found; else ++audio_found; } } } int flags = MusECore::Track::ASSIGN_PROPERTIES; if(audio_found != 0 || midi_found != 0 || new_drum_found != 0) { MusEGui::DuplicateTracksDialog* dlg = new MusEGui::DuplicateTracksDialog( audio_found, midi_found, new_drum_found, nullptr, // parent false, // copies true, // allRoutes true, // defaultRoutes false, // noParts false, // duplicateParts false, // copyParts false // cloneParts ); int rv = dlg->exec(); if(rv == QDialog::Rejected) { delete dlg; return; } if(dlg->copyStdCtrls()) flags |= MusECore::Track::ASSIGN_STD_CTRLS; if(dlg->copyPlugins()) flags |= MusECore::Track::ASSIGN_PLUGINS; if(dlg->copyPluginCtrls()) flags |= MusECore::Track::ASSIGN_PLUGIN_CTRLS; if(dlg->allRoutes()) flags |= MusECore::Track::ASSIGN_ROUTES; if(dlg->defaultRoutes()) flags |= MusECore::Track::ASSIGN_DEFAULT_ROUTES; if(dlg->copyDrumlist()) flags |= MusECore::Track::ASSIGN_DRUMLIST; delete dlg; } // Reset. cur_new_track_idx = -1; int num_incompatible = 0; MusECore::Track* dtrack = nullptr; MusECore::TrackNameFactory track_names; for(y_map_t::iterator ici = y_map.begin(); ici != y_map.end(); ++ici) { CItem* ci = ici->second; int x = ci->pos().x(); int y = ci->pos().y(); int nx = x + dx; int ny = pitch2y(y2pitch(y) + dp); QPoint newpos = QPoint(nx, ny); if(rasterize) newpos = raster(newpos); selectItem(ci, true); NPart* npart = (NPart*) ci; MusECore::Part* spart = npart->part(); MusECore::Track* track = npart->track(); unsigned dtick = newpos.x(); // FIXME TODO make subtick-compatible! int ntrack = y2pitch(ci->mp().y()); MusECore::Track::TrackType type = track->type(); if (tracks->index(track) == ntrack && (dtick == spart->tick())) { continue; } if (ntrack >= (int)tracks->size()) { // Create a new track, but only at the first part found on a track. if(ntrack != cur_new_track_idx) { cur_new_track_idx = ntrack; // TODO Make sure the list redraws without this ! // emit tracklistChanged(); if(!track_names.genUniqueNames(type, track->name(), 1)) continue; #if 1 // FIXME Works but height is copied with PROPERTIES - we don't want that but it // can be corrected below, BUT some other things are copied that we may not want. // Do we really want copies, or a fresh blank track? Maybe pop up a dialog and ask ? dtrack = track->clone(flags); #else dtrack = MusEGlobal::song->createTrack(type); #endif if(!dtrack) continue; #if 1 // For the line above, if used. Fix the height instead of using the original track height. dtrack->setHeight(MusEGlobal::config.trackHeight); #endif dtrack->setName(track_names.first()); if (type == MusECore::Track::WAVE) { MusECore::WaveTrack* st = (MusECore::WaveTrack*) track; MusECore::WaveTrack* dt = (MusECore::WaveTrack*) dtrack; dt->setChannels(st->channels()); } // Add at end of list. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTrack, -1, dtrack)); } } else { dtrack = tracks->index(ntrack); if (dtrack->type() != type) { ++num_incompatible; continue; } } if(!dtrack) continue; if(dtype == MOVE_MOVE) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::MovePart, spart, spart->posValue(), dtick, MusECore::Pos::TICKS, track, dtrack)); else { MusECore::Part* dpart; bool clone = (dtype == MOVE_CLONE || (dtype == MOVE_COPY && spart->hasClones())); // Gives the new part a new serial number. if (clone) dpart = spart->createNewClone(); else dpart = spart->duplicate(); dpart->setTick(dtick); dpart->setTrack(dtrack); spart->setSelected(false); dpart->setSelected(true); operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddPart,dpart)); } } if(num_incompatible > 0) { QMessageBox::critical(this, QString("MusE"), tr("Cannot copy/move/clone to different Track-Type")); } MusEGlobal::song->applyOperationGroup(operations); } //--------------------------------------------------------- // raster //--------------------------------------------------------- QPoint PartCanvas::raster(const QPoint& p) const { int y = pitch2y(y2pitch(p.y())); int x = p.x(); if (x < 0) x = 0; x = MusEGlobal::sigmap.raster(x, *_raster); if (x < 0) x = 0; return QPoint(x, y); } void PartCanvas::songIsClearing() { curItem=NULL; items.clearDelete(); } //--------------------------------------------------------- // updateItems //--------------------------------------------------------- void PartCanvas::updateItems() { int sn = -1; if (curItem) sn=static_cast(curItem)->serial(); curItem=NULL; items.clearDelete(); for (MusECore::ciTrack t = tracks->begin(); t != tracks->end(); ++t) { if ((*t)->isVisible()) //ignore parts from hidden tracks { MusECore::PartList* pl = (*t)->parts(); for (MusECore::ciPart i = pl->begin(); i != pl->end(); ++i) { MusECore::Part* part = i->second; NPart* np = new NPart(part); items.add(np); if (np->serial() == sn) curItem=np; if (i->second->selected()) selectItem(np, true); // Check for touching borders. MusECore::Part* pp; for(MusECore::ciPart ii = pl->begin(); ii != pl->end(); ++ii) { pp = ii->second; if(pp == part) // Ignore this part continue; if(pp->tick() > part->endTick()) break; if(pp->endTick() == part->tick()) np->leftBorderTouches = true; if(pp->tick() == part->endTick()) np->rightBorderTouches = true; } } } } redraw(); } //--------------------------------------------------------- // itemSelectionsChanged //--------------------------------------------------------- bool PartCanvas::itemSelectionsChanged(MusECore::Undo* operations, bool deselectAll) { // Whether to deselect all events when clicking on parts. // This is normally undesirable but in the future we may need that ability... const bool deselect_events = false; const bool do_deselect_all_events = deselectAll && deselect_events; MusECore::Undo ops; MusECore::Undo* opsp = operations ? operations : &ops; //Undo operations; bool item_selected; bool obj_selected; bool changed=false; // If we are deselecting all, globally deselect all events, // and don't bother individually deselecting objects, below. if(do_deselect_all_events) { //opsp->push_back(MusECore::UndoOp(MusECore::UndoOp::GlobalSelectAllEvents, false, 0, 0, false)); opsp->push_back(MusECore::UndoOp(MusECore::UndoOp::GlobalSelectAllEvents, false, 0, 0)); changed = true; } for (iCItem i = items.begin(); i != items.end(); ++i) { CItem* item = i->second; item_selected = item->isSelected(); obj_selected = item->objectIsSelected(); // Don't bother deselecting objects if we have already deselected all, above. if((item_selected || !do_deselect_all_events) && ((item_selected != obj_selected) || // Need to force this because after the 'deselect all events' command executes, // if the item is selected another select needs to be executed even though it // appears nothing changed here. (item_selected && do_deselect_all_events))) { opsp->push_back(UndoOp(UndoOp::SelectPart, item->part(), item_selected, obj_selected)); // Here we have a choice of whether to allow undoing of selections. // Disabled for now, it's too tedious in use. Possibly make the choice user settable. // Operation set as not undoable. //operations.push_back(UndoOp(UndoOp::SelectPart, item->part(), item_selected, obj_selected, false)); changed=true; } } if (!operations && changed) { // Set the 'sender' to this so that we can ignore self-generated songChanged signals. // Here we have a choice of whether to allow undoing of selections. if(MusEGlobal::config.selectionsUndoable) MusEGlobal::song->applyOperationGroup(ops, MusECore::Song::OperationUndoMode, this); else MusEGlobal::song->applyOperationGroup(ops, MusECore::Song::OperationExecuteUpdate, this); // For testing... // fprintf(stderr, "PartCanvas::updateSelection: Applied SelectPart operations, redrawing\n"); } return changed; } //--------------------------------------------------------- // resizeItem //--------------------------------------------------------- void PartCanvas::resizeItem(CItem* i, bool noSnap, bool ctrl) { MusECore::Track* t = ((NPart*)(i))->track(); MusECore::Part* p = ((NPart*)(i))->part(); unsigned int newPosOrLen = 0; if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT) { if(i->x() < 0) { newPosOrLen = 0; } else { newPosOrLen = i->x(); if(!noSnap) newPosOrLen = MusEGlobal::sigmap.raster(newPosOrLen, *_raster); } } else { unsigned int endPos = p->tick() + i->width(); if (!noSnap) { endPos = MusEGlobal::sigmap.raster(endPos, *_raster); } newPosOrLen = endPos - p->tick(); if (newPosOrLen == 0) { newPosOrLen = MusEGlobal::sigmap.rasterStep(p->tick(), *_raster); } } // Do not force all clones to be done (false) until we have a key we can use for it. MusECore::resize_part(t, p, newPosOrLen, resizeDirection, false, ctrl); } //--------------------------------------------------------- // newItem // first create local Item //--------------------------------------------------------- CItem* PartCanvas::newItem(const QPoint& pos, int key_modifiers) { int x = pos.x(); if (x < 0) x = 0; if(!(key_modifiers & Qt::ShiftModifier)) x = MusEGlobal::sigmap.raster1(x, *_raster); int len = pos.x() - x; if(len < 0) len = 0; unsigned trackIndex = y2pitch(pos.y()); if (trackIndex >= tracks->size()) return nullptr; MusECore::Track* track = tracks->index(trackIndex); if(!track) return nullptr; MusECore::Part* pa = nullptr; NPart* np = nullptr; switch(track->type()) { case MusECore::Track::MIDI: case MusECore::Track::DRUM: pa = new MusECore::MidiPart((MusECore::MidiTrack*)track); pa->setTick(x); pa->setLenTick(len); break; case MusECore::Track::WAVE: pa = new MusECore::WavePart((MusECore::WaveTrack*)track); pa->setTick(x); pa->setLenTick(len); break; case MusECore::Track::AUDIO_OUTPUT: case MusECore::Track::AUDIO_INPUT: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_AUX: case MusECore::Track::AUDIO_SOFTSYNTH: return nullptr; } pa->setName(track->name()); pa->setColorIndex(curColorIndex); np = new NPart(pa); return np; } //--------------------------------------------------------- // newItem //--------------------------------------------------------- void PartCanvas::newItem(CItem* i, bool noSnap) { if(!i) return; NPart* npart = (NPart*)(i); MusECore::Part* p = npart->part(); if(!p) return; MusECore::Track* part_track = p->track(); if(!part_track) return; int x = i->x(); if (x < 0) x = 0; if(!noSnap) x = MusEGlobal::sigmap.raster1(x, *_raster); p->setTick(x); unsigned trackIndex = y2pitch(i->y()); unsigned int tsize = tracks->size(); if (trackIndex >= tsize) trackIndex = (tsize > 0 ? tsize - 1 : 0); MusECore::Track* track = tracks->index(trackIndex); if(track != part_track) { if(track->type() == part_track->type()) { p->setTrack(track); p->setName(track->name()); } else { MusECore::Part* new_part = nullptr; switch(track->type()) { case MusECore::Track::MIDI: case MusECore::Track::DRUM: new_part = new MusECore::MidiPart((MusECore::MidiTrack*)track); break; case MusECore::Track::WAVE: new_part = new MusECore::WavePart((MusECore::WaveTrack*)track); break; case MusECore::Track::AUDIO_OUTPUT: case MusECore::Track::AUDIO_INPUT: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_AUX: case MusECore::Track::AUDIO_SOFTSYNTH: break; } if(new_part) { new_part->setTick(p->tick()); new_part->setName(track->name()); new_part->setColorIndex(curColorIndex); delete p; npart->setPart(new_part); p = new_part; } } } int len = i->width(); if (!noSnap) len = MusEGlobal::sigmap.raster(len, *_raster); if (len == 0) len = MusEGlobal::sigmap.rasterStep(p->tick(), *_raster); p->setLenTick(len); p->setSelected(true); i->setSelected(true); MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddPart, p)); } //--------------------------------------------------------- // deleteItem //--------------------------------------------------------- bool PartCanvas::deleteItem(CItem* i) { MusECore::Part* p = ((NPart*)(i))->part(); // Invokes songChanged which calls partsChanged which makes it difficult to delete them there MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeletePart, p)); return true; } //--------------------------------------------------------- // splitItem //--------------------------------------------------------- void PartCanvas::splitItem(CItem* item, const QPoint& pt) { NPart* np = (NPart*) item; MusECore::Part* p = np->part(); int x = pt.x(); if (x < 0) x = 0; split_part(p,MusEGlobal::sigmap.raster(x, *_raster)); } //--------------------------------------------------------- // glueItem //--------------------------------------------------------- void PartCanvas::glueItem(CItem* item) { NPart* np = (NPart*) item; merge_with_next_part(np->part()); } //--------------------------------------------------------- // genItemPopup //--------------------------------------------------------- QMenu* PartCanvas::genItemPopup(CItem* item) { NPart* npart = (NPart*) item; MusECore::Track::TrackType trackType = npart->track()->type(); QMenu* partPopup = new QMenu(this); partPopup->addAction(new MenuTitleItem(tr("Part"), partPopup)); QAction *act_cut = partPopup->addAction(*cutSVGIcon, tr("C&ut")); act_cut->setData(OP_CUT); act_cut->setShortcut(Qt::CTRL+Qt::Key_X); QAction *act_copy = partPopup->addAction(*copySVGIcon, tr("&Copy")); act_copy->setData(OP_COPY); act_copy->setShortcut(Qt::CTRL+Qt::Key_C); partPopup->addSeparator(); int rc = npart->part()->nClones(); QString st = QString(tr("S&elect ")); if(rc > 1) st += (QString().setNum(rc) + QString(" ")); st += QString(tr("clones")); QAction *act_select = partPopup->addAction(st); act_select->setData(OP_SELECT_CLONES); partPopup->addSeparator(); QAction *act_rename = partPopup->addAction(tr("Rename")); act_rename->setData(OP_RENAME); QMenu* colorPopup = partPopup->addMenu(tr("Color")); // part color selection for (int i = 0; i < NUM_PARTCOLORS; ++i) { QAction *act_color = nullptr; if (i == 0 && MusEGlobal::config.useTrackColorForParts) act_color = colorPopup->addAction(*tracktypeSVGIcon, tr("Track Color")); else act_color = colorPopup->addAction(MusECore::colorRect(MusEGlobal::config.partColors[i], 80, 80), MusEGlobal::config.partColorNames[i]); act_color->setData(OP_PARTCOLORBASE+i); if (i == 0) colorPopup->addSeparator(); } QAction *act_delete = partPopup->addAction(*deleteIconSVG, tr("Delete")); act_delete->setData(OP_DELETE); QAction *act_split = partPopup->addAction(*cutterIconSVG, tr("Split")); act_split->setData(OP_SPLIT); QAction *act_glue = partPopup->addAction(*glueIconSVG, tr("Glue")); act_glue->setData(OP_GLUE); QAction *act_superglue = partPopup->addAction(*glueIconSVG, tr("Super Glue (Merge Selection)")); act_superglue->setData(OP_GLUESELECTION); QAction *act_declone = partPopup->addAction(tr("De-clone")); act_declone->setData(OP_DECLONE); partPopup->addSeparator(); switch(trackType) { case MusECore::Track::MIDI: { partPopup->addAction(MusEGlobal::muse->arranger()->parentWin()->startPianoEditAction); partPopup->addAction(MusEGlobal::muse->arranger()->parentWin()->startListEditAction); partPopup->addMenu(MusEGlobal::muse->arranger()->parentWin()->scoreSubmenu); partPopup->addSeparator(); QAction *act_mexport = partPopup->addAction(tr("Save Part to Disk...")); act_mexport->setData(OP_SAVEPARTTODISK); } break; case MusECore::Track::DRUM: { partPopup->addAction(MusEGlobal::muse->arranger()->parentWin()->startDrumEditAction); partPopup->addAction(MusEGlobal::muse->arranger()->parentWin()->startListEditAction); partPopup->addSeparator(); QAction *act_dexport = partPopup->addAction(tr("Save Part to Disk...")); act_dexport->setData(OP_SAVEPARTTODISK); } break; case MusECore::Track::WAVE: { QAction *act_wedit = partPopup->addAction(*waveeditorSVGIcon, tr("Wave Edit...")); act_wedit->setData(OP_WAVEEDIT); QAction *act_wexport = partPopup->addAction(tr("Save Part to Disk...")); act_wexport->setData(OP_SAVEPARTTODISK); QAction *act_wfinfo = partPopup->addAction(tr("File Info...")); act_wfinfo->setData(OP_FILEINFO); QAction *act_wfnorm = partPopup->addAction(tr("Normalize")); act_wfnorm->setData(OP_NORMALIZE); act_wfnorm->setShortcut(Qt::CTRL+Qt::Key_N); } break; case MusECore::Track::AUDIO_OUTPUT: case MusECore::Track::AUDIO_INPUT: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_AUX: case MusECore::Track::AUDIO_SOFTSYNTH: break; } act_select->setEnabled( rc > 1); act_delete->setEnabled( true); act_cut->setEnabled( true); act_declone->setEnabled( rc > 1); genCanvasPopup(partPopup); return partPopup; } void PartCanvas::renameItem(CItem *item) { NPart* p = static_cast(item); if (p->isSelected()) { const QString oldname = p->name(); QInputDialog dlg(this); dlg.setWindowTitle(tr("Part Name")); dlg.setLabelText(tr("Enter part name:")); dlg.setTextValue(oldname); const int rc = dlg.exec(); if(rc == QDialog::Rejected) return; const QString newname = dlg.textValue(); if(newname == oldname) return; for (const auto& it : *MusEGlobal::song->tracks()) { for (const auto& ip : *it->parts()) { if (ip.second->selected()) ip.second->setName(newname); } } } else { editPart = (NPart*)(item); QRect r = map(curItem->bbox()); if (lineEditor == 0) { lineEditor = new QLineEdit(this); lineEditor->setFrame(true); connect(lineEditor, SIGNAL(editingFinished()),SLOT(returnPressed())); } lineEditor->setText(editPart->name()); lineEditor->setFocus(); lineEditor->show(); lineEditor->setGeometry(r); editMode = true; } } //--------------------------------------------------------- // itemPopup //--------------------------------------------------------- void PartCanvas::itemPopup(CItem* item, int n, const QPoint& pt) { if(n >= TOOLS_ID_BASE) { canvasPopup(n); return; } MusECore::PartList* pl = new MusECore::PartList; NPart* npart = (NPart*)(item); pl->add(npart->part()); switch(n) { case OP_RENAME: // rename renameItem(item); break; case OP_DELETE: // delete deleteItem(item); break; case OP_SPLIT: // split splitItem(item, pt); break; case OP_GLUE: // glue glueItem(item); break; case OP_CUT: copy(pl); MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeletePart, npart->part())); break; case OP_COPY: copy(pl); break; case OP_GLUESELECTION: MusECore::merge_selected_parts(); break; case OP_WAVEEDIT: // wave edit emit startEditor(pl, 4); return; case OP_DECLONE: // declone { MusECore::Part* spart = npart->part(); MusECore::Part* dpart = spart->duplicate(); // dpart will not be member of any clone chain! Undo operations; operations.push_back(UndoOp(UndoOp::DeletePart, spart)); operations.push_back(UndoOp(UndoOp::AddPart, dpart)); MusEGlobal::song->applyOperationGroup(operations); break; } case OP_SAVEPARTTODISK: // Export to file { const MusECore::Part* part = item->part(); bool popenFlag = false; QString fn = getSaveFileName(QString(""), MusEGlobal::part_file_save_pattern, this, tr("MusE: Save part")); if (!fn.isEmpty()) { FILE* fp = fileOpen(this, fn, ".mpt", "w", popenFlag, false, false); if (fp) { MusECore::Xml tmpXml = MusECore::Xml(fp); // Write the part. Indicate that it's a copy operation - to add special markers, // and force full wave paths. part->write(0, tmpXml, true, true); fclose(fp); } } break; } case OP_FILEINFO: // File info { MusECore::Part* p = item->part(); QString str = tr("Part name: %1\nFiles:").arg(p->name()); for (MusECore::ciEvent e = p->events().begin(); e != p->events().end(); ++e) { MusECore::Event event = e->second; if(event.empty()) continue; MusECore::SndFileR f = event.sndFile(); if (f.isNull()) continue; str.append(QString("\n@") + QString().setNum(event.tick()) + QString(" len:") + QString().setNum(event.lenTick()) + QString(" ") + f.path()); } QMessageBox::information(this, "File info", str, "Ok", 0); break; } case OP_SELECT_CLONES: // Select clones { MusECore::Part* part = item->part(); // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. MusECore::Part* p = part; Undo operations; if(part->hasClones()) { // Here we have a choice of whether to allow undoing of selections. // Disabled for now, it's too tedious in use. Possibly make the choice user settable. operations.push_back(UndoOp(UndoOp::SelectPart, p, true, p->selected(), false)); for(MusECore::Part* it = p->nextClone(); it!=p; it=it->nextClone()) // Operation set as not undoable. operations.push_back(UndoOp(UndoOp::SelectPart, it, true, it->selected(), false)); MusEGlobal::song->applyOperationGroup(operations); } break; } case OP_NORMALIZE: // Normalize { MusEGlobal::song->normalizeWaveParts(item->part()); break; } case OP_PARTCOLORBASE ... NUM_PARTCOLORS+20: { curColorIndex = n - 20; if (item->isSelected()) { //Loop through all parts and set color on selected: for (const auto& it : items) { if (it.second->isSelected()) it.second->part()->setColorIndex(curColorIndex); } } else { item->part()->setColorIndex(curColorIndex); } emit curPartColorIndexChanged(curColorIndex); MusEGlobal::song->update(SC_PART_MODIFIED); redraw(); break; } default: printf("unknown action %d\n", n); break; } delete pl; } void PartCanvas::setPartColor(int idx) { curColorIndex = idx; //Loop through all parts and set color on selected: for (const auto& it : items) { if (it.second->isSelected()) it.second->part()->setColorIndex(curColorIndex); } MusEGlobal::song->update(SC_PART_MODIFIED); redraw(); } void PartCanvas::setCurrentColorIndex(int idx) { curColorIndex = idx; } //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- bool PartCanvas::mousePress(QMouseEvent* event) { if (event->modifiers() & Qt::ControlModifier) { return true; } QPoint pt = event->pos(); Qt::MouseButton button = event->button(); CItem* item = items.find(pt); switch (_tool) { default: break; case PointerTool: case PencilTool: if (item && button == Qt::LeftButton) emit trackChanged(item->part()->track()); break; case CutTool: if (item && button == Qt::LeftButton) splitItem(item, pt); break; case GlueTool: if (item && button == Qt::LeftButton) glueItem(item); break; case MuteTool: { if (item && button == Qt::LeftButton) { NPart* np = (NPart*) item; MusECore::Part* p = np->part(); p->setMute(!p->mute()); redraw(); break; } } break; case AutomationTool: if (button == Qt::RightButton || button == Qt::MidButton) { bool do_delete = false; if (button == Qt::MidButton) // mid-click do_delete=true; else // right-click { QMenu *automationMenu = new QMenu(this); QAction* act; automationMenu->addAction(new MenuTitleItem(tr("Automation"), automationMenu)); act = automationMenu->addAction(tr("Remove selected")); act->setData(0); genCanvasPopup(automationMenu); act = automationMenu->exec(event->globalPos()); if(act) { int n = act->data().toInt(); if(n == 0) do_delete = true; else if(n >= TOOLS_ID_BASE) canvasPopup(n); } delete automationMenu; } if (do_delete && automation.currentTrack) { Undo operations; foreach(int frame, automation.currentCtrlFrameList) operations.push_back(UndoOp(UndoOp::DeleteAudioCtrlVal, automation.currentTrack, automation.currentCtrlList->id(), frame)); if(!operations.empty()) { MusEGlobal::song->applyOperationGroup(operations); // User probably would like to hear results so make sure controller is enabled. ((MusECore::AudioTrack*)automation.currentTrack)->enableController(automation.currentCtrlList->id(), true); } } } else { if (automation.controllerState != doNothing) { automation.moveController = true; // Upon any next operations list execution, break any undo combining. automation.breakUndoCombo = true; newAutomationVertex(pt); } } return false; // break; } return true; } //--------------------------------------------------------- // viewMouseReleaseEvent //--------------------------------------------------------- void PartCanvas::mouseRelease(QMouseEvent*) { // clear all the automation parameters automation.moveController=false; automation.controllerState = doNothing; //automation.currentCtrl=0; automation.currentCtrlValid = false; automation.currentTrack=nullptr; automation.currentCtrlList=nullptr; //automation.breakUndoCombo = false; // Don't touch this here. } //--------------------------------------------------------- // viewMousevent //--------------------------------------------------------- void PartCanvas::mouseMove(QMouseEvent* event) { int x = event->pos().x(); if (x < 0) x = 0; if (_tool == AutomationTool) { event->accept(); bool slowMotion = event->modifiers() & Qt::ShiftModifier; processAutomationMovements(event->pos(), slowMotion); emit timeChanged(MusEGlobal::sigmap.raster(x, *_raster)); return; } event->ignore(); emit timeChanged(MusEGlobal::sigmap.raster(x, *_raster)); //fprintf(stderr, "PartCanvas::mouseMove: x:%d *_raster:%d sigmap.raster(x, *_raster):%d\n", // x, *_raster, MusEGlobal::sigmap.raster(x, *_raster)); showStatusTip(event); } void PartCanvas::showStatusTip(QMouseEvent* event) { static CItem* hoverItem = nullptr; static Tool localTool; CItem* item; item = findCurrentItem(event->pos()); if (item) { if (hoverItem == item && localTool == _tool) return; hoverItem = item; localTool = _tool; QString s; if (_tool & (MusEGui::PointerTool )) s = tr("LMB: Select/Move/Dblclick to edit | CTRL+LMB: Multi select/Move&Copy | CTRL+ALT+LMB: Dblclick to edit in new window | SHIFT+LMB: Select track | MMB: Delete"); else if (_tool & (MusEGui::PencilTool)) s = tr("LMB: Draw to resize | MMB: Delete | CTRL+RMB: Trim length"); else if (_tool & (MusEGui::RubberTool)) s = tr("LMB: Delete | CTRL+RMB: Trim length"); else if (_tool & (MusEGui::CutTool)) s = tr("LMB: Cut part in two"); else if (_tool & (MusEGui::GlueTool)) s = tr("LMB: Merge with following part"); else if (_tool & (MusEGui::MuteTool)) s = tr("LMB: Mute selected part"); else if (_tool & (MusEGui::AutomationTool)) s = tr("LMB: Edit automation events in audio parts"); if (!s.isEmpty()) MusEGlobal::muse->setStatusBarText(s); } else { if (hoverItem != nullptr) { MusEGlobal::muse->clearStatusBarText(); hoverItem = nullptr; } } } //--------------------------------------------------------- // y2Track //--------------------------------------------------------- MusECore::Track* PartCanvas::y2Track(int y) const { MusECore::TrackList* l = MusEGlobal::song->tracks(); int ty = 0; for (MusECore::ciTrack it = l->begin(); it != l->end(); ++it) { int h = (*it)->height(); if (y >= ty && y < ty + h) return *it; ty += h; } return nullptr; } //--------------------------------------------------------- // keyPress //--------------------------------------------------------- void PartCanvas::keyPress(QKeyEvent* event) { event->accept(); int key = event->key(); if (editMode) { // this will probably never happen, as edit mode has been set // to "false" some usec ago by returnPressed, called by editingFinished. if ( key == Qt::Key_Return || key == Qt::Key_Enter ) { event->ignore(); return; } // the below CAN indeed happen. else if ( key == Qt::Key_Escape ) { lineEditor->hide(); editMode = false; return; } } // if returnPressed, called by editingFinished, was executed // a short time ago, ignore this keypress if it was enter or return if (editingFinishedTime.elapsed() < EDITING_FINISHED_TIMEOUT && (key == Qt::Key_Return || key == Qt::Key_Enter) ) { event->ignore(); return; } key = key | event->modifiers(); if (key == shortcuts[SHRT_DELETE].key) { if (getCurrentDrag()) return; MusECore::delete_selected_parts(); return; } else if (key == shortcuts[SHRT_POS_DEC].key) { int spos = pos[0]; if(spos > 0) { spos -= 1; // Nudge by -1, then snap down with raster1. spos = MusEGlobal::sigmap.raster1(spos, *_raster); } if(spos < 0) spos = 0; MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); return; } else if (key == shortcuts[SHRT_POS_INC].key) { int spos = MusEGlobal::sigmap.raster2(pos[0] + 1, *_raster); // Nudge by +1, then snap up with raster2. MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); return; } else if (key == shortcuts[SHRT_POS_DEC_NOSNAP].key) { int spos = pos[0] - MusEGlobal::sigmap.rasterStep(pos[0], *_raster); if(spos < 0) spos = 0; MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); return; } else if (key == shortcuts[SHRT_POS_INC_NOSNAP].key) { MusECore::Pos p(pos[0] + MusEGlobal::sigmap.rasterStep(pos[0], *_raster), true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); return; } else if (key == shortcuts[SHRT_TOOL_POINTER].key) { emit setUsedTool(PointerTool); return; } else if (key == shortcuts[SHRT_TOOL_PENCIL].key) { emit setUsedTool(PencilTool); return; } else if (key == shortcuts[SHRT_TOOL_RUBBER].key) { emit setUsedTool(RubberTool); return; } else if (key == shortcuts[SHRT_TOOL_SCISSORS].key) { emit setUsedTool(CutTool); return; } else if (key == shortcuts[SHRT_TOOL_LINEDRAW].key) { emit setUsedTool(AutomationTool); return; } else if (key == shortcuts[SHRT_TOOL_GLUE].key) { emit setUsedTool(GlueTool); return; } else if (key == shortcuts[SHRT_TOOL_MUTE].key) { emit setUsedTool(MuteTool); return; } else if (key == shortcuts[SHRT_TOOL_PAN].key) { emit setUsedTool(PanTool); return; } else if (key == shortcuts[SHRT_TOOL_ZOOM].key) { emit setUsedTool(ZoomTool); return; } else if (key == shortcuts[SHRT_SEL_TRACK_ABOVE].key) { emit selectTrackAbove(); return; } else if (key == shortcuts[SHRT_SEL_TRACK_BELOW].key) { emit selectTrackBelow(); return; } else if (key == shortcuts[SHRT_EDIT_TRACK_NAME].key) { emit editTrackNameSig(); return; } else if (key == shortcuts[SHRT_MUTE_CURRENT_TRACKS].key) { emit muteSelectedTracks(); return; } else if (key == shortcuts[SHRT_SOLO_CURRENT_TRACKS].key) { emit soloSelectedTracks(); return; } if (key == shortcuts[SHRT_MIXER_STRIP_VOL_UP].key) { emit volumeSelectedTracks(1); return; } else if (key == shortcuts[SHRT_MIXER_STRIP_VOL_DOWN].key) { emit volumeSelectedTracks(-1); return; } if (key == shortcuts[SHRT_MIXER_STRIP_VOL_UP_PAGE].key) { emit volumeSelectedTracks(5); return; } else if (key == shortcuts[SHRT_MIXER_STRIP_VOL_DOWN_PAGE].key) { emit volumeSelectedTracks(-5); return; } else if (key == shortcuts[SHRT_MIXER_STRIP_PAN_LEFT].key) { emit panSelectedTracks(-1); return; } else if (key == shortcuts[SHRT_MIXER_STRIP_PAN_RIGHT].key) { emit panSelectedTracks(1); return; } else if (key == shortcuts[SHRT_MIXER_STRIP_PAN_LEFT_PAGE].key) { emit panSelectedTracks(-5); return; } else if (key == shortcuts[SHRT_MIXER_STRIP_PAN_RIGHT_PAGE].key) { emit panSelectedTracks(5); return; } // Shortcuts that require selected parts from here if (!curItem) { if (items.size()==0) { event->ignore(); // give global accelerators a chance return; } for (iCItem i = items.begin(); i != items.end(); ++i) { NPart* part = (NPart*)(i->second); if (part->isSelected()) { curItem=part; break; } } if (!curItem) curItem = (NPart*)items.begin()->second; // just grab the first part } CItem* newItem = nullptr; bool singleSelection = isSingleSelection(); bool add = false; // Select part to the right if (key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key) { if (key == shortcuts[SHRT_SEL_RIGHT_ADD].key) add = true; MusECore::Part* part = curItem->part(); MusECore::Track* track = part->track(); unsigned int tick = part->tick(); bool afterthis = false; for (iCItem i = items.begin(); i != items.end(); ++i) { NPart* npart = (NPart*)(i->second); MusECore::Part* ipart = npart->part(); if (ipart->track() != track) continue; if (ipart->tick() < tick) continue; if (ipart == part) { afterthis = true; continue; } if(afterthis) { newItem = i->second; break; } } updateSelectedItem(newItem, add, singleSelection); return; } // Select part to the left else if (key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key) { if (key == shortcuts[SHRT_SEL_LEFT_ADD].key) add = true; MusECore::Part* part = curItem->part(); MusECore::Track* track = part->track(); unsigned int tick = part->tick(); for (iCItem i = items.begin(); i != items.end(); ++i) { NPart* npart = (NPart*)(i->second); MusECore::Part* ipart = npart->part(); if (ipart->track() != track) continue; if (ipart->tick() > tick) continue; if (ipart == part) break; newItem = i->second; } updateSelectedItem(newItem, add, singleSelection); return; } // Select nearest part on track above else if (key == shortcuts[SHRT_SEL_ABOVE].key || key == shortcuts[SHRT_SEL_ABOVE_ADD].key) { if (key == shortcuts[SHRT_SEL_ABOVE_ADD].key) add = true; //To get an idea of which track is above us: int stepsize = rmapxDev(1); MusECore::Track* track = curItem->part()->track(); track = y2Track(track->y() - 1); //If we're at topmost (no track above), leave if (!track) return; int middle = curItem->x() + curItem->part()->lenTick()/2; CItem *aboveL = nullptr, *aboveR = nullptr; //Upper limit: song end, lower limit: song start int ulimit = MusEGlobal::song->len(); int llimit = 0; while (newItem == nullptr) { int y = track->y() + 2; int xoffset = 0; int xleft = middle - xoffset; int xright = middle + xoffset; while ((xleft > llimit || xright < ulimit) && (aboveL == 0) && (aboveR == 0)) { xoffset += stepsize; xleft = middle - xoffset; xright = middle + xoffset; if (xleft >= 0) aboveL = items.find(QPoint(xleft,y)); if (xright <= ulimit) aboveR = items.find(QPoint(xright,y)); } if ((aboveL || aboveR) != 0) { //We've hit something CItem* above = nullptr; above = (aboveL != nullptr) ? aboveL : aboveR; newItem = above; } else { //We didn't hit anything. Move to track above, if there is one track = y2Track(track->y() - 1); if (track == nullptr) return; } } emit trackChanged(track); updateSelectedItem(newItem, add, singleSelection); return; } // Select nearest part on track below else if (key == shortcuts[SHRT_SEL_BELOW].key || key == shortcuts[SHRT_SEL_BELOW_ADD].key) { if (key == shortcuts[SHRT_SEL_BELOW_ADD].key) add = true; //To get an idea of which track is below us: int stepsize = rmapxDev(1); MusECore::Track* track = curItem->part()->track();//bottom->part()->track(); track = y2Track(track->y() + track->height() + 1 ); int middle = curItem->x() + curItem->part()->lenTick()/2; //If we're at bottommost, leave if (!track) return; CItem *belowL = nullptr, *belowR = nullptr; //Upper limit: song end , lower limit: song start int ulimit = MusEGlobal::song->len(); int llimit = 0; while (newItem == nullptr) { int y = track->y() + 1; int xoffset = 0; int xleft = middle - xoffset; int xright = middle + xoffset; while ((xleft > llimit || xright < ulimit) && (belowL == nullptr) && (belowR == nullptr)) { xoffset += stepsize; xleft = middle - xoffset; xright = middle + xoffset; if (xleft >= 0) belowL = items.find(QPoint(xleft,y)); if (xright <= ulimit) belowR = items.find(QPoint(xright,y)); } if ((belowL || belowR) != 0) { //We've hit something CItem* below = nullptr; below = (belowL != nullptr) ? belowL : belowR; newItem = below; } else { //Get next track below, or abort if this is the lowest track = y2Track(track->y() + track->height() + 1 ); if (track == nullptr) return; } } emit trackChanged(track); updateSelectedItem(newItem, add, singleSelection); return; } else if (key == shortcuts[SHRT_RENAME_PART].key && curItem) { if (singleSelection) { renameItem(curItem); } return; } else if (key == shortcuts[SHRT_EDIT_PART].key && curItem) { //This should be the other way around - singleSelection first. if (!singleSelection) { event->ignore(); return; } MusECore::PartList* pl = new MusECore::PartList; NPart* npart = (NPart*)(curItem); MusECore::Track* track = npart->part()->track(); pl->add(npart->part()); int type = 0; // Check if track is wave or drum, // else track is midi switch (track->type()) { case MusECore::Track::DRUM: type = 3; break; case MusECore::Track::WAVE: type = 4; break; case MusECore::Track::MIDI: case MusECore::Track::AUDIO_OUTPUT: case MusECore::Track::AUDIO_INPUT: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_AUX: case MusECore::Track::AUDIO_SOFTSYNTH: //TODO break; } emit startEditor(pl, type); return; } else { event->ignore(); // give global accelerators a chance return; } // if no return has caught the event we ignore it, we should never get here! fprintf(stderr, "End of PartCanvas::keyPress - we should never get here!\n"); event->ignore(); } void PartCanvas::updateSelectedItem(CItem* newItem, bool add, bool singleSelection) { // Check if anything happened to the selected parts if (newItem) { //If this is a single selection, toggle previous item if (singleSelection && !add) selectItem(curItem, false); else if(!add) deselectAll(); curItem = newItem; selectItem(newItem, true); //Check if we've hit the left, right, upper or lower boundaries of the window. If so, scroll to new position. if (newItem->x() < mapxDev(0)) { emit horizontalScroll(rmapx(newItem->x() - xorg) - 10); // Leave some room. } else if (newItem->x() + newItem->width() > mapxDev(width())) { int mx = rmapx(newItem->x()); int newx = mx + rmapx(newItem->width()) - width(); emit horizontalScroll( (newx > mx ? mx - 10 : newx + 10) - rmapx(xorg) ); } if (newItem->y() < mapyDev(0)) { int my = rmapy(newItem->y()); int newy = my + rmapy(newItem->height()) - height(); emit verticalScroll( (newy < my ? my - 10 : newy + 10) - rmapy(yorg) ); } else if (newItem->y() + newItem->height() > mapyDev(height())) { emit verticalScroll( rmapy(newItem->y() + newItem->height() - yorg) - height() + 10); } redraw(); } } //--------------------------------------------------------- // keyRelease //--------------------------------------------------------- void PartCanvas::keyRelease(QKeyEvent* event) { const int key = event->key(); // We do not want auto-repeat events. // It does press and release repeatedly. Wait till the last release comes. if(!event->isAutoRepeat()) { // For testing... // fprintf(stderr, "PartCanvas::keyRelease not isAutoRepeat\n"); //event->accept(); // Select part to the right if(key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key || // Select part to the left key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key || // Select nearest part on track above key == shortcuts[SHRT_SEL_ABOVE].key || key == shortcuts[SHRT_SEL_ABOVE_ADD].key || // Select nearest part on track below key == shortcuts[SHRT_SEL_BELOW].key || key == shortcuts[SHRT_SEL_BELOW_ADD].key) { itemSelectionsChanged(); } return; } Canvas::keyRelease(event); } //--------------------------------------------------------- // drawItem // draws an item //--------------------------------------------------------- void PartCanvas::drawItem(QPainter& p, const CItem* item, const QRect& mr, const QRegion&) { const QRect ur = mapDev(mr); int vfrom = ur.x(); int vto = vfrom + ur.width(); MusECore::Part* part = ((NPart*)item)->part(); int pTick = part->tick(); vfrom -= pTick; vto -= pTick; if(vfrom < 0) vfrom = 0; if((unsigned int)vto > part->lenTick()) vto = part->lenTick(); const int uw1 = rmapxDev(1); const int uh1 = rmapyDev(1); const int uh2 = rmapyDev(2); QBrush brush; // This is the actual item drawing rectangle, ignoring any borders we place on it. QRect ubbr = item->bbox(); // Compensation required for our two-pixel wide border. This is due to how Qt draws borders with width > 1. // For example if our top is at y=10, Qt will draw the top border starting at y=9. // Thus we need to make our top y=11 so that it draws starting at y=10. Same with bottom. Just move rectangle down. // The left and right edges do NOT need adjustment here. // FIXME Prefer to do this after the map, but vbbr is needed below. ubbr.moveTop(ubbr.y() + uh1); //QRect rr = p.transform().mapRect(r); // Gives inconsistent positions. Source shows wrong operation for our needs. QRect mbbr = map(ubbr); // Use our own map instead. // This is the update comparison rectangle. This would normally be the same as the item's bounding rectangle // but in this case we have a two-pixel wide border. To accommodate for our border, expand the left edge // left by one, the right edge right by one, and the bottom edge down by TWO. This way we catch the full // necessary drawing rectangle when checking the requested update rectangle. QRect ubbr_exp = item->bbox().adjusted(rmapxDev(-1), 0, uw1, uh2); // For testing... // fprintf(stderr, "PartCanvas::drawItem: vr x:%d y:%d w:%d h:%d vbbr_exp x:%d y:%d w:%d h:%d" // " mr x:%d y:%d w:%d h:%d mbbr x:%d y:%d w:%d h:%d vfrom:%d vto:%d\n", // vr.x(), vr.y(), vr.width(), vr.height(), // vbbr_exp.x(), vbbr_exp.y(), vbbr_exp.width(), vbbr_exp.height(), // mr.x(), mr.y(), mr.width(), mr.height(), // mbbr.x(), mbbr.y(), mbbr.width(), mbbr.height(), // vfrom, vto); // Now check intersection of the expanded comparison rectangle and the requested update rectangle. // Item bounding box x is in tick coordinates, same as rectangle. if((ubbr_exp & ur).isEmpty()) { // For testing... // fprintf(stderr, "...vbbr & vr is empty. Returning.\n"); return; } const bool item_selected = item->isSelected(); p.setWorldMatrixEnabled(false); // NOTE: Optimization: For each item, hasHiddenEvents() is called once in Canvas::draw(), and we use cachedHasHiddenEvents(). // Not used for now. //int het = part->cachedHasHiddenEvents(); DELETETHIS or FIXME or whatever? int het = part->hasHiddenEvents(); int xs_0 = mbbr.x(); int xe_0 = xs_0 + mbbr.width(); int xs_1 = xs_0 + 1; if(xs_1 > xe_0) xs_1 = xe_0; // int xs_2 = xs_0 + 2; // if(xs_2 > xe_0) // xs_2 = xe_0; int xs_j = xs_0 + 8; if(xs_j > xe_0) xs_j = xe_0; int xe_1 = xe_0 - 1; if(xe_1 < xs_0) xe_1 = xs_0; // int xe_2 = xe_0 - 2; // if(xe_2 < xs_0) // xe_2 = xs_0; int xe_j = xe_0 - 8; if(xe_j < xs_0) xe_j = xs_0; int ys_0 = mbbr.y(); int ye_0 = ys_0 + mbbr.height(); // int ys_1 = ys_0 + 1; // if(ys_1 > ye_0) // ys_1 = ye_0; // int ys_2 = ys_0 + 2; // if(ys_2 > ye_0) // ys_2 = ye_0; // int ys_3 = ys_0 + 3; // if(ys_3 > ye_0) // ys_3 = ye_0; // int ye_1 = ye_0 - 1; // if(ye_1 < ys_0) // ye_1 = ys_0; // int ye_2 = ye_0 - 2; // if(ye_2 < ys_0) // ye_2 = ys_0; int mrxs_0 = mr.x(); int mrxe_0 = mrxs_0 + mr.width(); bool lbt = ((NPart*)item)->leftBorderTouches; bool rbt = ((NPart*)item)->rightBorderTouches; int lbx = lbt?xs_1:xs_0; int rbx = rbt?xe_1:xe_0; int lbx_c = lbx < mrxs_0 ? mrxs_0 : lbx; int rbx_c = rbx > mrxe_0 ? mrxe_0 : rbx; QColor partColor; if (part->colorIndex() == 0 && MusEGlobal::config.useTrackColorForParts) partColor = part->track()->color(); else partColor = MusEGlobal::config.partColors[part->colorIndex()]; int gradS = qBound(0, MusEGlobal::config.partGradientStrength, 200); if (item->isMoving()) { QColor c(Qt::gray); c.setAlpha(MusEGlobal::config.globalAlphaBlend); brush = MusECore::getGradientFromColor(c, mbbr.topLeft(), mbbr.bottomLeft(), gradS); } else if (item_selected) { QColor c(Qt::black); c.setAlpha(MusEGlobal::config.globalAlphaBlend); QLinearGradient gradient(mbbr.topLeft(), mbbr.bottomLeft()); // Use a colour only about 20% lighter than black, rather than the 50% we use in MusECore::gGradientFromQColor // and is used in darker()/lighter(), so that it is distinguished a bit better from grey non-part tracks. //c.setRgba(64, 64, 64, c.alpha()); gradient.setColorAt(0, QColor(51, 51, 51, MusEGlobal::config.globalAlphaBlend)); gradient.setColorAt(1, c); brush = QBrush(gradient); } else if (part->mute()) { QColor c(Qt::white); c.setAlpha(MusEGlobal::config.globalAlphaBlend); brush = MusECore::getGradientFromColor(c, mbbr.topLeft(), mbbr.bottomLeft(), gradS); } else { QColor c = partColor; c.setAlpha(MusEGlobal::config.globalAlphaBlend); brush = MusECore::getGradientFromColor(c, mbbr.topLeft(), mbbr.bottomLeft(), gradS); } int h = mbbr.height(); double s = double(h) / 4.0; int y0 = ys_0; int y2 = y0 + lrint(s * 2.0); int y4 = y0 + h; QPoint points[8]; int pts; // Fill the part rectangles, accounting for hidden events by using 'jagged' edges... p.setBrush(brush); p.setPen(Qt::NoPen); if(het) { // TODO: Make this part respect the requested drawing rectangle (rr & mr), for speed ! pts = 0; if(het == (MusECore::Part::LeftEventsHidden | MusECore::Part::RightEventsHidden)) { points[pts++] = QPoint(xs_0, y0); points[pts++] = QPoint(xe_0, y0); points[pts++] = QPoint(xe_j, y2); points[pts++] = QPoint(xe_0, y4); points[pts++] = QPoint(xs_0, y4); points[pts++] = QPoint(xs_j, y2); p.drawConvexPolygon(points, pts); // Help says may be faster on some platforms (X11). } else if(het == MusECore::Part::LeftEventsHidden) { points[pts++] = QPoint(xs_0, y0); points[pts++] = QPoint(xe_0, y0); points[pts++] = QPoint(xe_0, y4); points[pts++] = QPoint(xs_0, y4); points[pts++] = QPoint(xs_j, y2); p.drawConvexPolygon(points, pts); } else if(het == MusECore::Part::RightEventsHidden) { points[pts++] = QPoint(xs_0, y0); points[pts++] = QPoint(xe_0, y0); points[pts++] = QPoint(xe_j, y2); points[pts++] = QPoint(xe_0, y4); points[pts++] = QPoint(xs_0, y4); p.drawConvexPolygon(points, pts); } { // Draw remaining 'hidden events' decorations with 'jagged' edges... int color_brightness; if (MusECore::isColorBright(partColor) && !item_selected) color_brightness=96; //0; // too light: use dark color else color_brightness=180; //255; // too dark: use lighter color QColor c(color_brightness,color_brightness,color_brightness, MusEGlobal::config.globalAlphaBlend); // p.setBrush(QBrush(MusECore::gGradientFromQColor(c, mbbr.topLeft(), mbbr.bottomLeft()))); p.setBrush(MusECore::getGradientFromColor(c, mbbr.topLeft(), mbbr.bottomLeft(), gradS)); } if(het & MusECore::Part::RightEventsHidden) { pts = 0; points[pts++] = QPoint(xe_0, y0); points[pts++] = QPoint(xe_0, y4); points[pts++] = QPoint(xe_j, y2); p.drawConvexPolygon(points, pts); } if(het & MusECore::Part::LeftEventsHidden) { pts = 0; points[pts++] = QPoint(xs_0, y0); points[pts++] = QPoint(xs_j, y2); points[pts++] = QPoint(xs_0, y4); p.drawConvexPolygon(points, pts); } } else { p.fillRect(mbbr & mr, brush); // Respect the requested drawing rectangle. Gives speed boost! } // Draw a pattern brush on muted parts... if(part->mute()) { p.setPen(Qt::NoPen); brush.setStyle(Qt::Dense7Pattern); p.fillRect(mbbr & mr, brush); // Respect the requested drawing rectangle. Gives speed boost! } p.setWorldMatrixEnabled(true); MusECore::Track::TrackType type = part->track()->type(); if (type == MusECore::Track::WAVE) { MusECore::WavePart* wp =(MusECore::WavePart*)part; drawWavePart(p, ur, wp, ubbr, item_selected); } else { MusECore::MidiPart* mp = (MusECore::MidiPart*)part; drawMidiPart(p, ur, mp, ubbr, vfrom, vto, item_selected); } p.setWorldMatrixEnabled(false); // // Now draw the borders, using custom segments... // p.setBrush(Qt::NoBrush); QColor pc((part->mute() || item->isMoving())? Qt::white : partColor); QPen penSelect1H(pc); QPen penSelect2H(pc, 2.0); QPen penSelect1V(pc); QPen penSelect2V(pc, 2.0); penSelect1H.setCosmetic(true); penSelect2H.setCosmetic(true); penSelect1V.setCosmetic(true); penSelect2V.setCosmetic(true); pc = Qt::black; QPen penNormal1H(pc); QPen penNormal2H(pc, 2.0); QPen penNormal1V(pc); QPen penNormal2V(pc, 2.0); penNormal1H.setCosmetic(true); penNormal2H.setCosmetic(true); penNormal1V.setCosmetic(true); penNormal2V.setCosmetic(true); QVector customDashPattern; if(part->hasClones()) { customDashPattern << 4.0 << 6.0; penSelect1H.setDashPattern(customDashPattern); penNormal1H.setDashPattern(customDashPattern); penSelect1V.setDashPattern(customDashPattern); penNormal1V.setDashPattern(customDashPattern); penSelect1V.setDashOffset(2.0); penNormal1V.setDashOffset(2.0); //penHidden1.setDashPattern(customDashPattern); customDashPattern.clear(); customDashPattern << 2.0 << 3.0; penSelect2H.setDashPattern(customDashPattern); penNormal2H.setDashPattern(customDashPattern); penSelect2V.setDashPattern(customDashPattern); penNormal2V.setDashPattern(customDashPattern); penSelect2V.setDashOffset(1.0); penNormal2V.setDashOffset(1.0); //penHidden2.setDashPattern(customDashPattern); // FIXME: Some shifting still going on. Values likely not quite right here. int xdiff = mrxs_0 - lbx; if(xdiff > 0) { int doff = xdiff % 10; penSelect1H.setDashOffset(doff); penNormal1H.setDashOffset(doff); doff = (xdiff / 2) % 5; penSelect2H.setDashOffset(doff); penNormal2H.setDashOffset(doff); } } if(((NPart*)item)->rightBorderTouches) p.setPen(item_selected ? penSelect1V : penNormal1V); else p.setPen(item_selected ? penSelect2V : penNormal2V); if(rbx >= mrxs_0 && rbx <= mrxe_0) // Respect the requested drawing rectangle. Gives speed boost! { QLine l2(rbx, ys_0, rbx, ye_0); // Right p.drawLine(l2); // Right line } if(((NPart*)item)->leftBorderTouches) p.setPen(item_selected ? penSelect1V : penNormal1V); else p.setPen(item_selected ? penSelect2V : penNormal2V); if(xs_0 >= mrxs_0 && xs_0 <= mrxe_0) { QLine l4(xs_0, ys_0, xs_0, ye_0); // Left p.drawLine(l4); // Left line } p.setPen(item_selected ? penSelect2H : penNormal2H); // Respect the requested drawing rectangle. Gives speed boost! QLine l1(lbx_c, ys_0, rbx_c, ys_0); p.drawLine(l1); // Top line QLine l3(lbx_c, ye_0, rbx_c, ye_0); p.drawLine(l3); // Bottom line if (MusEGlobal::config.canvasShowPartType & 1) { // show names QRect tr = mbbr; tr.setX(tr.x() + 3); p.setFont(MusEGlobal::config.fonts[2]); p.setPen(Qt::black); p.drawText(tr.translated(1, 1), Qt::AlignBottom|Qt::AlignLeft, part->name()); p.setPen(Qt::white); p.drawText(tr, Qt::AlignBottom|Qt::AlignLeft, part->name()); } p.setWorldMatrixEnabled(true); } //--------------------------------------------------------- // drawMoving // draws moving items //--------------------------------------------------------- void PartCanvas::drawMoving(QPainter& p, const CItem* item, const QRect&, const QRegion&) { QPen pen; pen.setCosmetic(true); pen.setColor( Qt::black); p.setPen(pen); MusECore::Part* part = ((NPart*)item)->part(); QColor partColor; if (part->colorIndex() == 0 && MusEGlobal::config.useTrackColorForParts) partColor = part->track()->color(); else partColor = MusEGlobal::config.partColors[part->colorIndex()]; QColor c(part->mute() ? Qt::white : partColor); c.setAlpha(128); // Fix this regardless of config.globalAlphaBlend setting. Should be OK. p.setBrush(c); MusECore::TrackList* tl = MusEGlobal::song->tracks(); int yy = 0; int y = item->mp().y(); int ih = item->height(); MusECore::ciTrack it; for(it = tl->begin(); it != tl->end(); ++it) { int h = (*it)->height(); if(y < yy+h) { ih = h; break; } yy += h; } if(it == tl->end()) ih = MusEGlobal::config.trackHeight; p.drawRect(item->mp().x(), item->mp().y(), item->width(), ih); } //--------------------------------------------------------- // drawMidiPart // bb - bounding box of paint area // pr - part rectangle //--------------------------------------------------------- void PartCanvas::drawMidiPart(QPainter& p, const QRect& rect, MusECore::MidiPart* midipart, const QRect& r, int from, int to, bool selected) { drawMidiPart(p, rect, midipart->events(), midipart->track(), midipart, r, midipart->tick(), from, to, selected); } void PartCanvas::drawMidiPart(QPainter& p, const QRect&, const MusECore::EventList& events, MusECore::MidiTrack *mt, MusECore::MidiPart* pt, const QRect& r, int pTick, int from, int to, bool selected) { int color_brightness; QColor eventColor; QPen pen; pen.setCosmetic(true); unsigned int pt_len = 0; { int part_r, part_g, part_b, brightness; if(pt) { pt_len = pt->lenTick(); if (pt->colorIndex() == 0 && MusEGlobal::config.useTrackColorForParts) pt->track()->color().getRgb(&part_r, &part_g, &part_b); else MusEGlobal::config.partColors[pt->colorIndex()].getRgb(&part_r, &part_g, &part_b); } else { if(curColorIndex == 0 && MusEGlobal::config.useTrackColorForParts) mt->color().getRgb(&part_r, &part_g, &part_b); else MusEGlobal::config.partColors[curColorIndex].getRgb(&part_r, &part_g, &part_b); } brightness = part_r*29 + part_g*59 + part_b*12; if (brightness >= 12000 && !selected) { eventColor=MusEGlobal::config.partMidiDarkEventColor; color_brightness=54; // 96; // too bright: use dark color } else { eventColor=MusEGlobal::config.partMidiLightEventColor; color_brightness=200; //160; // too dark: use lighter color } } if (MusEGlobal::config.canvasShowPartType & 2) { // show events pen.setColor(eventColor); p.setPen(pen); // Do not allow this, causes segfault. if(from <= to) { MusECore::ciEvent ito(events.lower_bound(to)); for (MusECore::ciEvent i = events.lower_bound(from); i != ito; ++i) { MusECore::EventType type = i->second.type(); int a = i->second.dataA() | 0xff; if ( ((MusEGlobal::config.canvasShowPartEvent & 1) && (type == MusECore::Note)) || ((MusEGlobal::config.canvasShowPartEvent & (2 | 4)) == (2 | 4) && type == MusECore::Controller && a == MusECore::CTRL_POLYAFTER) || ((MusEGlobal::config.canvasShowPartEvent & 4) && (type == MusECore::Controller) && (a != MusECore::CTRL_POLYAFTER || (MusEGlobal::config.canvasShowPartEvent & 2)) && (a != MusECore::CTRL_AFTERTOUCH || (MusEGlobal::config.canvasShowPartEvent & 16))) || ((MusEGlobal::config.canvasShowPartEvent & (16 | 4)) == (16 | 4) && type == MusECore::Controller && a == MusECore::CTRL_AFTERTOUCH) || ((MusEGlobal::config.canvasShowPartEvent & 64) && (type == MusECore::Sysex || type == MusECore::Meta)) ) { #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)i->first < 0 /*|| (int)i->first < from*/) continue; if((pt && (int)i->first >= (int)pt_len) /*|| (int)i->first >= to*/) break; #endif int t = i->first + pTick; int th = mt->height(); if(t >= r.left() && t <= r.right()) p.drawLine(t, r.y()+2, t, r.y()+th-4); } } } } else { // show Cakewalk Style using std::map; using std::pair; MusECore::ciEvent ito(events.lower_bound(to)); bool isdrum = mt->isDrumTrack(); // draw controllers ------------------------------------------ pen.setColor(QColor(192,192,color_brightness/2)); p.setPen(pen); for (MusECore::ciEvent i = events.begin(); i != ito; ++i) { // PITCH BEND #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)i->first < 0 /*|| (int)i->first < from*/) continue; if((pt && (int)i->first >= (int)pt_len) /*|| (int)i->first >= to*/) break; #endif int t = i->first + pTick; MusECore::EventType type = i->second.type(); if (type == MusECore::Controller) { int ctrl_type=i->second.dataA(); if (ctrl_type == MusECore::CTRL_PITCH) { int val=i->second.dataB(); int th = int(mt->height() * 0.75); // only draw on three quarters int hoffset = (mt->height() - th ) / 2; // offset from bottom p.drawLine(t, hoffset + r.y() + th/2, t, hoffset + r.y() - val*th/8192/2 + th/2); } } } pen.setColor(QColor(192,color_brightness/2,color_brightness/2)); p.setPen(pen); for (MusECore::ciEvent i = events.begin(); i != ito; ++i) { // PAN #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)i->first < 0 /*|| (int)i->first < from*/) continue; if((pt && (int)i->first >= (int)pt_len) /*|| (int)i->first >= to*/) break; #endif int t = i->first + pTick; MusECore::EventType type = i->second.type(); if (type == MusECore::Controller) { int ctrl_type=i->second.dataA(); if (ctrl_type == 10) { int val=i->second.dataB(); int th = int(mt->height() * 0.75); // only draw on three quarters int hoffset = (mt->height() - th ) / 2; // offset from bottom p.drawLine(t, hoffset + r.y() + th - val*th/127, t, hoffset + r.y() + th); } } } pen.setColor(QColor(color_brightness/2,192,color_brightness/2)); p.setPen(pen); for (MusECore::ciEvent i = events.begin(); i != ito; ++i) { // VOLUME #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)i->first < 0 /*|| (int)i->first < from*/) continue; if((pt && (int)i->first >= (int)pt_len) /*|| (int)i->first >= to*/) break; #endif int t = i->first + pTick; MusECore::EventType type = i->second.type(); if (type == MusECore::Controller) { int ctrl_type=i->second.dataA(); if (ctrl_type == 7) { int val=i->second.dataB(); int th = int(mt->height() * 0.75); // only draw on three quarters int hoffset = (mt->height() - th ) / 2; // offset from bottom p.drawLine(t, hoffset + r.y() + th - val*th/127, t, hoffset + r.y() + th); } } } pen.setColor(QColor(0,0,255)); p.setPen(pen); for (MusECore::ciEvent i = events.begin(); i != ito; ++i) { // PROGRAM CHANGE #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)i->first < 0 /*|| (int)i->first < from*/) continue; if((pt && (int)i->first >= (int)pt_len) /*|| (int)i->first >= to*/) break; #endif int t = i->first + pTick; MusECore::EventType type = i->second.type(); if (type == MusECore::Controller) { int ctrl_type=i->second.dataA(); if (ctrl_type == MusECore::CTRL_PROGRAM) { int th = int(mt->height() * 0.75); // only draw on three quarters int hoffset = (mt->height() - th ) / 2; // offset from bottom p.drawLine(t, hoffset + r.y(), t, hoffset + r.y() + th); } } } // draw notes ------------------------------------------------ int lowest_pitch=127; int highest_pitch=0; map y_mapper; if (MusEGlobal::config.canvasShowPartType & 4) //y-stretch? { for (MusECore::ciEvent i = events.begin(); i != events.end(); ++i) { if (i->second.type()==MusECore::Note) { int pitch=i->second.pitch(); if (!isdrum) { if (pitch > highest_pitch) highest_pitch=pitch; if (pitch < lowest_pitch) lowest_pitch=pitch; } else { y_mapper.insert(pair(pitch, 0)); } } } if (isdrum) { int cnt=0; for (map::iterator it=y_mapper.begin(); it!=y_mapper.end(); it++) { it->second=cnt; cnt++; } lowest_pitch=0; highest_pitch=cnt-1; } if (lowest_pitch==highest_pitch) { lowest_pitch--; highest_pitch++; } if (MusEGlobal::heavyDebugMsg) { if (!isdrum) printf("DEBUG: arranger: cakewalk enabled, y-stretching from %i to %i.\n",lowest_pitch, highest_pitch); else { printf("DEBUG: arranger: cakewalk enabled, y-stretching drums: ");; for (map::iterator it=y_mapper.begin(); it!=y_mapper.end(); it++) printf("%i ", it->first); printf("\n"); } } } else { lowest_pitch=0; highest_pitch=127; if (isdrum) for (int cnt=0;cnt<128;cnt++) y_mapper[cnt]=cnt; if (MusEGlobal::heavyDebugMsg) printf("DEBUG: arranger: cakewalk enabled, y-stretch disabled\n"); } pen.setColor(eventColor); p.setPen(pen); for (MusECore::ciEvent i = events.begin(); i != ito; ++i) { #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)i->first < 0 /*|| (int)i->first < from*/) continue; if((pt && (int)i->first >= (int)pt_len) /*|| (int)i->first >= to*/) break; #endif int t = i->first + pTick; int te = t + i->second.lenTick(); if (te < (from + pTick)) continue; if (te >= (to + pTick)) te = lrint(rmapxDev_f(rmapx_f(to + pTick) - 1.0)); MusECore::EventType type = i->second.type(); if (type == MusECore::Note) { int pitch = i->second.pitch(); int th = int(mt->height() * 0.75); // only draw on three quarters int hoffset = (mt->height() - th ) / 2; // offset from bottom int y; if (!isdrum) y = hoffset + r.y() + th - (pitch-lowest_pitch)*th/(highest_pitch-lowest_pitch); else y = hoffset + r.y() + y_mapper[pitch]*th/(highest_pitch-lowest_pitch); p.drawLine(t, y, te, y); } } } } void PartCanvas::drawWaveSndFile(QPainter &p, MusECore::SndFileR &f, int samplePos, unsigned rootFrame, unsigned startFrame, unsigned lengthFrames, int startY, int startX, int endX, int rectHeight, bool selected) { const int endY = startY + rectHeight; const int h = rectHeight >> 1; const int x1 = startX; const int x2 = endX; if((int)startFrame + (int)lengthFrames < 0) return; int hiddenOffset = 0; int evAbsFrameLim = rootFrame + startFrame; if(evAbsFrameLim < 0) { hiddenOffset = -evAbsFrameLim; evAbsFrameLim = 0; } int postick = MusEGlobal::tempomap.frame2tick(evAbsFrameLim); const int event_x = mapx(postick); const int event_ex = mapx(MusEGlobal::tempomap.frame2tick(rootFrame + startFrame + lengthFrames)); if(event_x >= x2 || event_ex < x1) return; int sx = event_x; int ex = event_ex; if(sx < x1) sx = x1; if(ex > x2) ex = x2; // Whether space permits displaying all channels or combining them into one. const bool multichan_disp = h >= 20; const int center = startY + rectHeight / 2; const QColor left_ch_color(0, 170, 255); const QColor right_ch_color(Qt::red); const QColor combo_ch_color(220, 120, 255); int xScale; int pos; int tickstep = rmapxDev(1); int drawoffset; if((x1 - event_x) < 0) { drawoffset = 0; } else { drawoffset = rmapxDev(x1 - event_x); } postick += drawoffset; pos = MusEGlobal::tempomap.tick2frame(postick) - rootFrame - startFrame; QPen pen; pen.setCosmetic(true); unsigned channels = 0; int wav_sx = 0; int wav_ex = 0; int wsx = 0; int wex = 0; bool wave_visible = false; if(!f.isNull()) { channels = f.channels(); if(channels > 0) { const sf_count_t smps = f.samples(); if(-samplePos < smps && samplePos <= smps) { wave_visible = true; wav_sx = -samplePos; wav_ex = smps - samplePos - hiddenOffset; if(wav_sx < 0) wav_sx = 0; wav_sx += evAbsFrameLim; wav_sx = MusEGlobal::tempomap.frame2tick(wav_sx); wav_ex = f.unConvertPosition(wav_ex); if(wav_ex >= (int)lengthFrames - hiddenOffset) { wav_ex = lengthFrames - hiddenOffset; if(wav_ex > 0) --wav_ex; } wav_ex += evAbsFrameLim; wav_ex = MusEGlobal::tempomap.frame2tick(wav_ex); wav_sx = mapx(wav_sx); wav_ex = mapx(wav_ex); wsx = wav_sx < x1 ? x1 : wav_sx; wex = wav_ex > x2 ? x2 : wav_ex; } if (!multichan_disp) { // combine multi channels into one waveform int y = startY + h; int cc = rectHeight % 2 ? 0 : 1; for (int i = sx; i < ex; ++i) { MusECore::SampleV sa[channels]; xScale = MusEGlobal::tempomap.deltaTick2frame(postick, postick + tickstep); if((samplePos + f.convertPosition(pos)) > smps) break; // Seek the file only once, not with every read! if(i == sx) { if(f.seekUIConverted(pos, SEEK_SET | SFM_READ, samplePos) == -1) break; } f.readConverted(sa, xScale, pos, samplePos, true, false); postick += tickstep; pos += xScale; int peak = 0; int rms = 0; for (unsigned k = 0; k < channels; ++k) { if (sa[k].peak > peak) peak = sa[k].peak; rms += sa[k].rms; } rms /= channels; peak = (peak * (rectHeight-2)) >> 9; rms = (rms * (rectHeight-2)) >> 9; int outer = peak; int inner = peak -1; //-1 < 0 ? 0 : peak -1; pen.setColor(MusEGlobal::config.partWaveColorPeak); p.setPen(pen); p.drawLine(i, y - outer - cc, i, y + outer); pen.setColor(MusEGlobal::config.partWaveColorRms); p.setPen(pen); if (MusEGlobal::config.waveDrawing == MusEGlobal::WaveRmsPeak) p.drawLine(i, y - rms - cc, i, y + rms); else // WaveOutLine p.drawLine(i, y - inner - cc, i, y + inner); } // Only if there's something to draw. if(wave_visible && wsx <= wex && wsx < x2 && wex >= x1) { // If two (or more) channels combine blue (cyan) and red into say, magenta. pen.setColor(QColor(channels > 1 ? combo_ch_color : left_ch_color)); p.setPen(pen); p.drawLine(wsx, center, wex, center); } } else { // multi channel display int hm = rectHeight / (channels * 2); int cc = rectHeight % (channels * 2) ? 0 : 1; for (int i = sx; i < ex; ++i) { int y = startY + hm; MusECore::SampleV sa[channels]; xScale = MusEGlobal::tempomap.deltaTick2frame(postick, postick + tickstep); if((samplePos + f.convertPosition(pos)) > smps) break; // Seek the file only once, not with every read! if(i == sx) { if(f.seekUIConverted(pos, SEEK_SET | SFM_READ, samplePos) == -1) break; } f.readConverted(sa, xScale, pos, samplePos, true, false); postick += tickstep; pos += xScale; for (unsigned k = 0; k < channels; ++k) { int peak = (sa[k].peak * (hm - 1)) >> 8; int rms = (sa[k].rms * (hm - 1)) >> 8; int outer = peak; int inner = peak -1; //-1 < 0 ? 0 : peak -1; pen.setColor(MusEGlobal::config.partWaveColorPeak); p.setPen(pen); p.drawLine(i, y - outer - cc , i, y + outer); pen.setColor(MusEGlobal::config.partWaveColorRms); p.setPen(pen); if (MusEGlobal::config.waveDrawing == MusEGlobal::WaveRmsPeak) p.drawLine(i, y - rms - cc, i, y + rms); else // WaveOutLine p.drawLine(i, y - inner - cc, i, y + inner); y += 2 * hm; } } // Only if there's something to draw. if(wave_visible && wsx <= wex && wsx < x2 && wex >= x1) { const int hn = rectHeight / channels; const int hhn = hn / 2; for (unsigned int i = 0; i < channels; ++i) { const int h2 = hn * i; const int finY = startY + hhn + h2; if(finY >= startY && finY < endY) { pen.setColor(QColor(i & 1 ? right_ch_color : left_ch_color)); p.setPen(pen); p.drawLine(wsx, finY, wex, finY); } } } } } } pen.setColor(selected ? Qt::white : Qt::black); QVector customDashPattern; customDashPattern << 1.0 << 2.0; pen.setDashPattern(customDashPattern); const int pix_per_seg = 21; //fprintf(stderr, "x1:%d x2:%d startY:%d endY:%d rectHeight:%d y:%d h:%d event_x:%d event_ex:%d" // " rootFrame:%d startFrame:%d lengthFrames:%d sx:%d ex:%d wav_sx:%d wav_ex:%d wsx:%d wex:%d\n", // x1, x2, startY, endY, rectHeight, y(), height(), event_x, event_ex, // rootFrame, startFrame, lengthFrames, sx, ex, wav_sx, wav_ex, wsx, wex); // Draw the complete line only if there are an even number of channels (space for the line in the middle). // Ensure a complete line is drawn even if there is no sound file or channels. if((channels & 1) == 0 && multichan_disp) { if(sx > event_x) { //fprintf(stderr, "Single line. Setting dash offset:%d\n", (sx - event_x) % 3); pen.setDashOffset((sx - event_x) % 3); } p.setPen(pen); MusECore::drawSegmentedHLine(&p, sx, ex, center, pix_per_seg); } else { // Draw only the required two segments of the line. if(wave_visible) { if(sx < wsx) { if(sx > event_x) { //fprintf(stderr, "Line segment 1. Setting dash offset:%d\n", (sx - event_x) % 3); pen.setDashOffset((sx - event_x) % 3); } p.setPen(pen); MusECore::drawSegmentedHLine(&p, sx, wsx - 1, center, pix_per_seg); } if(wex < ex) { if(sx > wex + 1) { //fprintf(stderr, "Line segment 2. Setting dash offset:%d\n", (sx - wex + 1) % 3); pen.setDashOffset((sx - wex + 1) % 3); } p.setPen(pen); MusECore::drawSegmentedHLine(&p, wex + 1, ex, center, pix_per_seg); } } } // // Draw custom dashed borders around the wave event // if(endY > startY) { // Reset offset back to zero. pen.setDashOffset(0); p.setPen(pen); // Left line: if(event_x >= x1 && event_x <= x2) MusECore::drawSegmentedVLine(&p, event_x, startY, endY - 1, pix_per_seg); // Right line: if(event_ex >= x1 && event_ex <= x2) MusECore::drawSegmentedVLine(&p, event_ex, startY, endY - 1, pix_per_seg); } } // TODO REMOVE Tim. wave. An attempt to re-write this method, in progress... // void PartCanvas::drawWaveSndFile(QPainter &p, MusECore::SndFileR &f, int samplePos, unsigned rootFrame, unsigned startFrame, unsigned lengthFrames, int startY, int startX, int endX, int rectHeight, bool selected) // { // const int endY = startY + rectHeight; // const int h = rectHeight >> 1; // const int x1 = startX; // const int x2 = endX; // const int md_x1 = mapxDev(startX); // const int md_x2 = mapxDev(endX); // const int md_x1_fr = MusEGlobal::tempomap.tick2frame(md_x1); // const int md_x2_fr = MusEGlobal::tempomap.tick2frame(md_x2); // const int event_abs_fr = rootFrame + startFrame; // const int event_abs_e_fr = event_abs_fr + lengthFrames; // if(event_abs_fr >= md_x2_fr || event_abs_e_fr < md_x1_fr) // return; // // int postick = MusEGlobal::tempomap.frame2tick(rootFrame + startFrame); // const int event_x = mapx(postick); // const int event_ex = mapx(MusEGlobal::tempomap.frame2tick(rootFrame + startFrame + lengthFrames)); // if(event_x >= x2 || event_ex < x1) // return; // // int sx = event_x; // // int ex = event_ex; // // if(sx < x1) // // sx = x1; // // if(ex > x2) // // ex = x2; // // int ev_s_fr = event_abs_fr; // int ev_e_fr = event_abs_e_fr; // if(ev_s_fr < md_x1_fr) // ev_s_fr = md_x1_fr; // if(ev_e_fr > md_x2_fr) // ev_e_fr = md_x2_fr; // // int sx = mapx(MusEGlobal::tempomap.frame2tick(ev_s_fr)); // int ex = mapx(MusEGlobal::tempomap.frame2tick(ev_e_fr)); // // // // Whether space permits displaying all channels or combining them into one. // const bool multichan_disp = h >= 20; // const int center = startY + rectHeight / 2; // const QColor left_ch_color(0, 170, 255); // const QColor right_ch_color(Qt::red); // const QColor combo_ch_color(220, 120, 255); // // int xScale; // int pos; // int tickstep = rmapxDev(1); // int drawoffset; // if((x1 - event_x) < 0) { // drawoffset = 0; // } // else { // drawoffset = rmapxDev(x1 - event_x); // } // postick += drawoffset; // pos = MusEGlobal::tempomap.tick2frame(postick) - rootFrame - startFrame; // // QPen pen; // pen.setCosmetic(true); // // unsigned channels = 0; // int wav_sx = 0; // int wav_ex = 0; // int wsx = 0; // int wex = 0; // bool wave_visible = false; // // if(!f.isNull()) // { // channels = f.channels(); // if(channels > 0) // { // const sf_count_t smps = f.samples(); // // if(-samplePos < smps && samplePos <= smps) // { // wave_visible = true; // wav_sx = -samplePos; // wav_ex = smps - samplePos; // if(wav_sx < 0) // wav_sx = 0; // wav_sx += startFrame + rootFrame; // wav_sx = MusEGlobal::tempomap.frame2tick(wav_sx); // // wav_ex = f.unConvertPosition(wav_ex); // if(wav_ex >= (int)lengthFrames) // { // wav_ex = lengthFrames; // if(wav_ex > 0) // --wav_ex; // } // wav_ex += startFrame + rootFrame; // wav_ex = MusEGlobal::tempomap.frame2tick(wav_ex); // // wav_sx = mapx(wav_sx); // wav_ex = mapx(wav_ex); // wsx = wav_sx < x1 ? x1 : wav_sx; // wex = wav_ex > x2 ? x2 : wav_ex; // } // // if (!multichan_disp) { // // combine multi channels into one waveform // int y = startY + h; // int cc = rectHeight % 2 ? 0 : 1; // for (int i = sx; i < ex; ++i) { // MusECore::SampleV sa[channels]; // xScale = MusEGlobal::tempomap.deltaTick2frame(postick, postick + tickstep); // if((samplePos + f.convertPosition(pos)) > smps) // break; // // Seek the file only once, not with every read! // if(i == sx) // { // if(f.seekUIConverted(pos, SEEK_SET | SFM_READ, samplePos) == -1) // break; // } // f.readConverted(sa, xScale, pos, samplePos, true, false); // // postick += tickstep; // pos += xScale; // int peak = 0; // int rms = 0; // for (unsigned k = 0; k < channels; ++k) { // if (sa[k].peak > peak) // peak = sa[k].peak; // rms += sa[k].rms; // } // rms /= channels; // peak = (peak * (rectHeight-2)) >> 9; // rms = (rms * (rectHeight-2)) >> 9; // int outer = peak; // int inner = peak -1; //-1 < 0 ? 0 : peak -1; // pen.setColor(MusEGlobal::config.partWaveColorPeak); // p.setPen(pen); // p.drawLine(i, y - outer - cc, i, y + outer); // pen.setColor(MusEGlobal::config.partWaveColorRms); // p.setPen(pen); // if (MusEGlobal::config.waveDrawing == MusEGlobal::WaveRmsPeak) // p.drawLine(i, y - rms - cc, i, y + rms); // else // WaveOutLine // p.drawLine(i, y - inner - cc, i, y + inner); // } // // // Only if there's something to draw. // if(wave_visible && wsx <= wex && wsx < x2 && wex >= x1) // { // // If two (or more) channels combine blue (cyan) and red into say, magenta. // pen.setColor(QColor(channels > 1 ? combo_ch_color : left_ch_color)); // p.setPen(pen); // p.drawLine(wsx, center, wex, center); // } // } // else { // // multi channel display // int hm = rectHeight / (channels * 2); // int cc = rectHeight % (channels * 2) ? 0 : 1; // for (int i = sx; i < ex; ++i) { // int y = startY + hm; // MusECore::SampleV sa[channels]; // xScale = MusEGlobal::tempomap.deltaTick2frame(postick, postick + tickstep); // if((samplePos + f.convertPosition(pos)) > smps) // break; // // Seek the file only once, not with every read! // if(i == sx) // { // if(f.seekUIConverted(pos, SEEK_SET | SFM_READ, samplePos) == -1) // break; // } // f.readConverted(sa, xScale, pos, samplePos, true, false); // // postick += tickstep; // pos += xScale; // for (unsigned k = 0; k < channels; ++k) { // int peak = (sa[k].peak * (hm - 1)) >> 8; // int rms = (sa[k].rms * (hm - 1)) >> 8; // int outer = peak; // int inner = peak -1; //-1 < 0 ? 0 : peak -1; // pen.setColor(MusEGlobal::config.partWaveColorPeak); // p.setPen(pen); // p.drawLine(i, y - outer - cc , i, y + outer); // pen.setColor(MusEGlobal::config.partWaveColorRms); // p.setPen(pen); // if (MusEGlobal::config.waveDrawing == MusEGlobal::WaveRmsPeak) // p.drawLine(i, y - rms - cc, i, y + rms); // else // WaveOutLine // p.drawLine(i, y - inner - cc, i, y + inner); // y += 2 * hm; // } // } // // // Only if there's something to draw. // if(wave_visible && wsx <= wex && wsx < x2 && wex >= x1) // { // const int hn = rectHeight / channels; // const int hhn = hn / 2; // for (unsigned int i = 0; i < channels; ++i) { // const int h2 = hn * i; // const int finY = startY + hhn + h2; // if(finY >= startY && finY < endY) // { // pen.setColor(QColor(i & 1 ? right_ch_color : left_ch_color)); // p.setPen(pen); // p.drawLine(wsx, finY, wex, finY); // } // } // } // } // } // } // // pen.setColor(selected ? Qt::white : Qt::black); // QVector customDashPattern; // customDashPattern << 1.0 << 2.0; // pen.setDashPattern(customDashPattern); // const int pix_per_seg = 21; // // //fprintf(stderr, "x1:%d x2:%d startY:%d endY:%d rectHeight:%d y:%d h:%d event_x:%d event_ex:%d" // // " rootFrame:%d startFrame:%d lengthFrames:%d sx:%d ex:%d wav_sx:%d wav_ex:%d wsx:%d wex:%d\n", // // x1, x2, startY, endY, rectHeight, y(), height(), event_x, event_ex, // // rootFrame, startFrame, lengthFrames, sx, ex, wav_sx, wav_ex, wsx, wex); // // // Draw the complete line only if there are an even number of channels (space for the line in the middle). // // Ensure a complete line is drawn even if there is no sound file or channels. // if((channels & 1) == 0 && multichan_disp) // { // if(sx > event_x) // { // //fprintf(stderr, "Single line. Setting dash offset:%d\n", (sx - event_x) % 3); // pen.setDashOffset((sx - event_x) % 3); // } // p.setPen(pen); // MusECore::drawSegmentedHLine(&p, sx, ex, center, pix_per_seg); // } // else // { // // Draw only the required two segments of the line. // if(wave_visible) // { // if(sx < wsx) // { // if(sx > event_x) // { // //fprintf(stderr, "Line segment 1. Setting dash offset:%d\n", (sx - event_x) % 3); // pen.setDashOffset((sx - event_x) % 3); // } // p.setPen(pen); // MusECore::drawSegmentedHLine(&p, sx, wsx - 1, center, pix_per_seg); // } // if(wex < ex) // { // if(sx > wex + 1) // { // //fprintf(stderr, "Line segment 2. Setting dash offset:%d\n", (sx - wex + 1) % 3); // pen.setDashOffset((sx - wex + 1) % 3); // } // p.setPen(pen); // MusECore::drawSegmentedHLine(&p, wex + 1, ex, center, pix_per_seg); // } // } // } // // // // // Draw custom dashed borders around the wave event // // // // if(endY > startY) // { // // Reset offset back to zero. // pen.setDashOffset(0); // p.setPen(pen); // // Left line: // if(event_x >= x1 && event_x <= x2) // MusECore::drawSegmentedVLine(&p, event_x, startY, endY - 1, pix_per_seg); // // Right line: // if(event_ex >= x1 && event_ex <= x2) // MusECore::drawSegmentedVLine(&p, event_ex, startY, endY - 1, pix_per_seg); // } // } //--------------------------------------------------------- // drawWavePart // bb - bounding box of paint area // pr - part rectangle //--------------------------------------------------------- void PartCanvas::drawWavePart(QPainter& p, const QRect& bb, MusECore::WavePart* wp, const QRect& _pr, bool selected) { QRect rr = map(bb); // Use our own map instead. QRect pr = map(_pr); p.save(); p.resetTransform(); int x2 = 1; int x1 = rr.x() > pr.x() ? rr.x() : pr.x(); x2 += rr.right() < pr.right() ? rr.right() : pr.right(); if (x1 < 0) x1 = 0; if (x2 > width()) x2 = width(); int hh = pr.height(); //int h = hh/2; int startY = pr.y(); for (auto reverseIterator = wp->events().rbegin(); reverseIterator != wp->events().rend(); ++reverseIterator) { MusECore::Event event = reverseIterator->second; auto f = event.sndFile(); // if (drag == DRAG_RESIZE && resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT && reverseIterator == wp->events().rbegin()) // { // // the last event is subject to live extending // // // _pr is in tick resolution // auto endFrame = MusEGlobal::tempomap.tick2frame(_pr.width()); // // // we're at the last event, extend the wave drawing so it is displayed past the old end of the part // drawWaveSndFile(p, f, event.spos(), wp->frame(), event.frame(), endFrame, startY, x1, x2, hh, selected); // } // else if (drag == DRAG_RESIZE && resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT) { // auto startFrame = MusEGlobal::tempomap.tick2frame(_pr.) drawWaveSndFile(p, f, event.spos(), wp->frame(), event.frame(), event.lenFrame(), startY, x1, x2, hh, selected); } else { drawWaveSndFile(p, f, event.spos(), wp->frame(), event.frame(), event.lenFrame(), startY, x1, x2, hh, selected); } } p.restore(); } //--------------------------------------------------------- // cmd //--------------------------------------------------------- void PartCanvas::cmd(int cmd) { MusECore::PartList pl; for (iCItem i = items.begin(); i != items.end(); ++i) { if (!i->second->isSelected()) continue; NPart* npart = (NPart*)(i->second); pl.add(npart->part()); } switch (cmd) { case CMD_CUT_PART: { copy(&pl); MusECore::Undo operations; for (iCItem i = items.begin(); i != items.end(); ++i) { if (i->second->isSelected()) { NPart* p = (NPart*)(i->second); MusECore::Part* part = p->part(); operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeletePart, part)); } } MusEGlobal::song->applyOperationGroup(operations); break; } case CMD_COPY_PART: copy(&pl); break; case CMD_COPY_PART_IN_RANGE: copy_in_range(&pl); break; case CMD_PASTE_PART: paste(); break; case CMD_PASTE_CLONE_PART: paste(true); break; case CMD_PASTE_PART_TO_TRACK: paste(false, PASTEMODE_MIX, true); break; case CMD_PASTE_CLONE_PART_TO_TRACK: paste(true, PASTEMODE_MIX, true); break; case CMD_PASTE_DIALOG: { unsigned temp_begin = MusEGlobal::sigmap.raster1(MusEGlobal::song->vcpos(),0); unsigned temp_end = MusEGlobal::sigmap.raster2(temp_begin + MusECore::get_paste_len(), 0); paste_dialog->raster = temp_end - temp_begin; if (paste_dialog->exec()) { paste_mode_t paste_mode; switch (paste_dialog->insert_method) { case 0: paste_mode=PASTEMODE_MIX; break; case 1: paste_mode=PASTEMODE_MOVEALL; break; case 2: paste_mode=PASTEMODE_MOVESOME; break; default: paste_mode=PASTEMODE_MIX; // shall never be executed } paste(paste_dialog->clone, paste_mode, paste_dialog->all_in_one_track, paste_dialog->number, paste_dialog->raster); } break; } case CMD_INSERT_EMPTYMEAS: unsigned int startPos=MusEGlobal::song->vcpos(); int oneMeas=MusEGlobal::sigmap.ticksMeasure(startPos); MusECore::Undo temp=MusECore::movePartsTotheRight(startPos,oneMeas); MusEGlobal::song->applyOperationGroup(temp); break; } } //--------------------------------------------------------- // copy // cut copy paste //--------------------------------------------------------- void PartCanvas::copy_in_range(MusECore::PartList* pl_) { MusECore::PartList pl; MusECore::PartList result_pl; unsigned int lpos = MusEGlobal::song->lpos(); unsigned int rpos = MusEGlobal::song->rpos(); if (pl_->empty()) { for (iCItem i = items.begin(); i != items.end(); ++i) { MusECore::Part* part=static_cast(i->second)->part(); if ( (part->track()->isMidiTrack()) || (part->track()->type() == MusECore::Track::WAVE) ) pl.add(part); } } else { for(MusECore::ciPart p = pl_->begin(); p != pl_->end(); ++p) if ( (p->second->track()->isMidiTrack()) || (p->second->track()->type() == MusECore::Track::WAVE) ) pl.add(p->second); } if (!pl.empty() && (rpos>lpos)) { for(MusECore::ciPart p = pl.begin(); p != pl.end(); ++p) { MusECore::Part* part=p->second; if ((part->tick() < rpos) && (part->endTick() > lpos)) //is the part in the range? { if ((lpos > part->tick()) && (lpos < part->endTick())) { MusECore::Part* p1; MusECore::Part* p2; part->splitPart(lpos, p1, p2); part=p2; } if ((rpos > part->tick()) && (rpos < part->endTick())) { MusECore::Part* p1; MusECore::Part* p2; part->splitPart(rpos, p1, p2); part=p1; } result_pl.add(part); } } copy(&result_pl); } } void PartCanvas::copy(MusECore::PartList* pl) { if (pl->empty()) return; bool wave = false; bool midi = false; for(MusECore::ciPart p = pl->begin(); p != pl->end(); ++p) { if(p->second->track()->isMidiTrack()) midi = true; else if(p->second->track()->type() == MusECore::Track::WAVE) wave = true; if(midi && wave) break; } if(!(midi || wave)) return; //--------------------------------------------------- // write parts as XML into tmp file //--------------------------------------------------- FILE* tmp = tmpfile(); if (tmp == 0) { fprintf(stderr, "PartCanvas::copy() fopen failed: %s\n", strerror(errno)); return; } MusECore::Xml xml(tmp); // Clear the copy clone list. MusEGlobal::cloneList.clear(); int level = 0; int tick = 0; for (MusECore::ciPart p = pl->begin(); p != pl->end(); ++p) { // Indicate this is a copy operation. Also force full wave paths. p->second->write(level, xml, true, true); int endTick = p->second->endTick(); if (endTick > tick) tick = endTick; } MusECore::Pos p(tick, true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p); QString mimeString = "text/x-muse-mixedpartlist"; if (!midi) mimeString = "text/x-muse-wavepartlist"; else if (!wave) mimeString = "text/x-muse-midipartlist"; QMimeData *mimeData = MusECore::file_to_mimedata(tmp, mimeString ); QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); fclose(tmp); } MusECore::Undo PartCanvas::pasteAt(const QString& pt, MusECore::Track* track, unsigned int pos, bool clone, bool toTrack, unsigned int* finalPosPtr, set* affected_tracks) { MusECore::Undo operations; QByteArray ba = pt.toLatin1(); const char* ptxt = ba.constData(); MusECore::Xml xml(ptxt); bool firstPart=true; unsigned int posOffset=0; bool fwdOffset = true; unsigned int finalPos = pos; int notDone = 0; int done = 0; bool end = false; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: end = true; break; case MusECore::Xml::TagStart: if (tag == "part") { // Read the part. MusECore::Part* p = 0; p = MusECore::Part::readFromXml(xml, track, clone, toTrack); // If it could not be created... if(!p) { // Increment the number of parts not done and break. ++notDone; break; } // Increment the number of parts done. ++done; if (firstPart) { firstPart=false; if(pos >= p->tick()) { posOffset = pos - p->tick(); fwdOffset = true; } else { posOffset = p->tick() - pos; fwdOffset = false; } // posOffset=pos-p->tick(); } if(fwdOffset) { p->setTick(p->tick()+posOffset); } else { if(p->tick() >= posOffset) p->setTick(p->tick()-posOffset); else p->setTick(0); } if (p->tick()+p->lenTick()>finalPos) { finalPos=p->tick()+p->lenTick(); } p->setSelected(true); operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddPart,p)); if (affected_tracks) affected_tracks->insert(p->track()); } else xml.unknown("PartCanvas::pasteAt"); break; case MusECore::Xml::TagEnd: break; default: end = true; break; } if(end) break; } if(notDone) { int tot = notDone + done; QMessageBox::critical(this, QString("MusE"), (tot > 1 ? tr("%n part(s) out of %1 could not be pasted.\nLikely the selected track is the wrong type.","",notDone).arg(tot) : tr("%n part(s) could not be pasted.\nLikely the selected track is the wrong type.","",notDone))); } if (finalPosPtr) *finalPosPtr=finalPos; return operations; } //--------------------------------------------------------- // paste // paste part to current selected track at cpos //--------------------------------------------------------- void PartCanvas::paste(bool clone, paste_mode_t paste_mode, bool to_single_track, int amount, int raster) { MusECore::Track* track = 0; // If we want to paste to a selected track... if (to_single_track) { MusECore::TrackList* tl = MusEGlobal::song->tracks(); for (MusECore::ciTrack i = tl->begin(); i != tl->end(); ++i) { if ((*i)->selected()) { if (track) { QMessageBox::critical(this, QString("MusE"), tr("Cannot paste: multiple tracks selected")); return; } else track = *i; } } if (track == 0) { QMessageBox::critical(this, QString("MusE"), tr("Cannot paste: no track selected")); return; } } QClipboard* cb = QApplication::clipboard(); const QMimeData* md = cb->mimeData(QClipboard::Clipboard); QString pfx("text/"); QString mdpl("x-muse-midipartlist"); QString wvpl("x-muse-wavepartlist"); QString mxpl("x-muse-mixedpartlist"); QString txt; if(md->hasFormat(pfx + mdpl)) { // If we want to paste to a selected track... if(to_single_track && !track->isMidiTrack()) { QMessageBox::critical(this, QString("MusE"), tr("Can only paste to midi/drum track")); return; } txt = cb->text(mdpl, QClipboard::Clipboard); } else if(md->hasFormat(pfx + wvpl)) { // If we want to paste to a selected track... if(to_single_track && track->type() != MusECore::Track::WAVE) { QMessageBox::critical(this, QString("MusE"), tr("Can only paste to wave track")); return; } txt = cb->text(wvpl, QClipboard::Clipboard); } else if(md->hasFormat(pfx + mxpl)) { // If we want to paste to a selected track... if(to_single_track && !track->isMidiTrack() && track->type() != MusECore::Track::WAVE) { QMessageBox::critical(this, QString("MusE"), tr("Can only paste to midi or wave track")); return; } txt = cb->text(mxpl, QClipboard::Clipboard); } else { QMessageBox::critical(this, QString("MusE"), tr("Cannot paste: wrong data type")); return; } if (!txt.isEmpty()) { unsigned int endPos=0; unsigned int startPos=MusEGlobal::song->vcpos(); set affected_tracks; deselectAll(); MusECore::Undo operations; for (int i=0;isetPos(MusECore::Song::CPOS, p); if (paste_mode != PASTEMODE_MIX) { unsigned int offset; if (amount==1) offset = endPos-startPos; else offset = amount*raster; MusECore::Undo temp; if (paste_mode==PASTEMODE_MOVESOME) temp=MusECore::movePartsTotheRight(startPos, offset, false, &affected_tracks); else temp=MusECore::movePartsTotheRight(startPos, offset); operations.insert(operations.end(), temp.begin(), temp.end()); } MusEGlobal::song->applyOperationGroup(operations); } } //--------------------------------------------------------- // startDrag //--------------------------------------------------------- void PartCanvas::startDrag(CItem* item, DragType t) { NPart* p = (NPart*)(item); MusECore::Part* part = p->part(); //--------------------------------------------------- // write part as XML into tmp file //--------------------------------------------------- FILE* tmp = tmpfile(); if (tmp == 0) { fprintf(stderr, "PartCanvas::startDrag() fopen failed: %s\n", strerror(errno)); return; } MusECore::Xml xml(tmp); int level = 0; part->write(level, xml); //--------------------------------------------------- // read tmp file into QTextDrag Object //--------------------------------------------------- fflush(tmp); struct stat f_stat; if (fstat(fileno(tmp), &f_stat) == -1) { fprintf(stderr, "PartCanvas::startDrag fstat failed:<%s>\n", strerror(errno)); fclose(tmp); return; } int n = f_stat.st_size + 1; char* fbuf = (char*)mmap(0, n, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(tmp), 0); fbuf[n] = 0; QByteArray data(fbuf); QMimeData* md = new QMimeData(); md->setData("text/x-muse-partlist", data); // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can // clean up after the drag and drop operation has been completed. " QDrag* drag = new QDrag(this); drag->setMimeData(md); if (t == MOVE_COPY || t == MOVE_CLONE) drag->exec(Qt::CopyAction); else drag->exec(Qt::MoveAction); munmap(fbuf, n); fclose(tmp); } //--------------------------------------------------------- // dragEnterEvent //--------------------------------------------------------- void PartCanvas::dragEnterEvent(QDragEnterEvent* event) { event->acceptProposedAction(); // TODO CHECK Tim. } //--------------------------------------------------------- // dropEvent //--------------------------------------------------------- void PartCanvas::viewDropEvent(QDropEvent* event) { if (MusEGlobal::debugMsg) printf("void PartCanvas::viewDropEvent(QDropEvent* event)\n"); if (event->source() == this) { printf("local DROP\n"); //event->ignore(); // TODO CHECK Tim. return; } int type = 0; // 0 = unknown, 1 = partlist, 2 = uri-list QString text; if(event->mimeData()->hasFormat("text/partlist")) type = 1; else if(event->mimeData()->hasUrls()) type = 2; else { if(MusEGlobal::debugMsg && event->mimeData()->formats().size() != 0) printf("Drop with unknown format. First format:<%s>\n", event->mimeData()->formats()[0].toLatin1().constData()); //event->ignore(); // TODO CHECK Tim. return; } // Make a backup of the current clone list, to retain any 'copy' items, // so that pasting works properly after. MusECore::CloneList copyCloneList = MusEGlobal::cloneList; // Clear the clone list to prevent any dangerous associations with // current non-original parts. MusEGlobal::cloneList.clear(); if (type == 1) { printf("type1\n"); text = QString(event->mimeData()->data("text/partlist")); int x = event->pos().x(); if (x < 0) x = 0; x = MusEGlobal::sigmap.raster(x, *_raster); unsigned trackNo = y2pitch(event->pos().y()); MusECore::Track* track = 0; if (trackNo < tracks->size()) track = tracks->index(trackNo); if (track) { deselectAll(); MusECore::Undo temp=pasteAt(text, track, x); MusEGlobal::song->applyOperationGroup(temp); } } else if (type == 2) { unsigned trackNo = y2pitch(event->pos().y()); MusECore::Track* track = 0; if (trackNo < tracks->size()) track = tracks->index(trackNo); int x = event->pos().x(); if (x < 0) x = 0; x = MusEGlobal::sigmap.raster(x, *_raster); foreach(QUrl url, event->mimeData()->urls()) { text = url.path(); if (text.endsWith(".wav",Qt::CaseInsensitive) || text.endsWith(".ogg",Qt::CaseInsensitive) || text.endsWith(".flac",Qt::CaseInsensitive) || text.endsWith(".mpt", Qt::CaseInsensitive) ) { if (!track) { // we need to create a track for this drop if (text.endsWith(".mpt", Qt::CaseInsensitive)) { track = MusEGlobal::song->addTrack(MusECore::Track::MIDI); // Add at end of list. } else { track = MusEGlobal::song->addTrack(MusECore::Track::WAVE); // Add at end of list. } } if (track->type() == MusECore::Track::WAVE && (text.endsWith(".wav", Qt::CaseInsensitive) || text.endsWith(".ogg", Qt::CaseInsensitive) || (text.endsWith(".flac", Qt::CaseInsensitive)) )) { unsigned tick = x; MusEGlobal::muse->importWaveToTrack(text, tick, track); } // Changed by T356. Support mixed .mpt files. else if ((track->isMidiTrack() || track->type() == MusECore::Track::WAVE) && text.endsWith(".mpt", Qt::CaseInsensitive)) { unsigned tick = x; MusEGlobal::muse->importPartToTrack(text, tick, track); } } else if(text.endsWith(".med",Qt::CaseInsensitive)) { emit dropSongFile(text); break; // we only support ONE drop of this kind } else if(text.endsWith(".mid",Qt::CaseInsensitive)) { emit dropMidiFile(text); } else { printf("dropped... something... no hable...\n"); } track=0; } } // Restore backup of the clone list, to retain any 'copy' items, // so that pasting works properly after. MusEGlobal::cloneList.clear(); MusEGlobal::cloneList = copyCloneList; } //--------------------------------------------------------- // drawCanvas //--------------------------------------------------------- void PartCanvas::drawCanvas(QPainter& p, const QRect& mr, const QRegion& mrg) { p.save(); p.setWorldMatrixEnabled(false); const ViewRect vr(mr, true); const ViewXCoordinate& vx = vr._x; const ViewWCoordinate& vw = vr._width; const ViewXCoordinate vx_2 = mathXCoordinates(vx, vw, MathAdd); int mx = mr.x(); int my = mr.y(); int mw = mr.width(); int mh = mr.height(); int mx_2 = mx + mw; int my_2 = my + mh; // Since the bounding box is open-ended (extends infintely to the right), // form an end-point from screen coordinates. const ViewXCoordinate vbbx(0, false); const ViewXCoordinate vbbx_2(x() + width(), true); const ViewXCoordinate vdbbw(mathXCoordinates(vbbx_2, vbbx, MathSubtract)); // Turn vdbbw into a width coordinate. const ViewWCoordinate vbbw(vdbbw._value, vdbbw.isMapped()); const int mbbx = asIntMapped(vbbx); int mx0_lim = mbbx; if(mx0_lim < mx) mx0_lim = mx; if(mx0_lim < 0) mx0_lim = 0; ////////// // GRID // ////////// QPen pen; pen.setCosmetic(true); //-------------------------------- // vertical lines //------------------------------- if (MusEGlobal::config.canvasShowGrid) { int rast = *_raster; drawTickRaster(p, mr, mrg, rast, false, false, false, MusEGlobal::config.partCanvasBeatRasterColor, MusEGlobal::config.partCanvasBeatRasterColor, MusEGlobal::config.partCanvasFineRasterColor, MusEGlobal::config.partCanvasCoarseRasterColor); } //-------------------------------- // horizontal lines //-------------------------------- MusECore::TrackList* tl = MusEGlobal::song->tracks(); int myy = -rmapy(yorg) - ypos; int mth; for (MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) { if (myy > my_2) break; MusECore::Track* track = *it; mth = track->height(); if(!mth) continue; const int myy_2 = myy + mth; const ViewYCoordinate vbby(myy, true); const ViewHCoordinate vbbh(mth, true); const ViewRect vbbox(vbbx, vbby, vbbw, vbbh); // For testing... // vbbox.dump("PartCanvas::drawCanvas vbbox"); if((MusEGlobal::config.canvasShowGrid || MusEGlobal::config.canvasShowGridHorizontalAlways) && (track->isMidiTrack() || track->type() == MusECore::Track::WAVE)) { if(compareXCoordinates(vx_2, vbbx, CompareGreaterEqual) && (myy_2 >= my && myy_2 < my_2)) { // For testing... // fprintf(stderr, "... bottom edge in range. Drawing bottom edge at mx0_lim:%d myy_2:%d mx_2:%d myy_2:%d\n", // mx0_lim, myy_2, mx_2, myy_2); pen.setColor(MusEGlobal::config.partCanvasCoarseRasterColor); p.setPen(pen); p.drawLine(mx0_lim, myy_2, mx_2, myy_2); } } if (!track->isMidiTrack() && (track->type() != MusECore::Track::WAVE)) { drawAudioTrack(p, mr, mrg, vbbox, (MusECore::AudioTrack*)track); } myy += mth; } p.restore(); } //--------------------------------------------------------- // drawTopItem //--------------------------------------------------------- void PartCanvas::drawTopItem(QPainter& p, const QRect& mr, const QRegion&) { int mx = mr.x(); int my = mr.y(); int mw = mr.width(); int mh = mr.height(); p.save(); p.setWorldMatrixEnabled(false); MusECore::TrackList* tl = MusEGlobal::song->tracks(); int yoff = -rmapy(yorg) - ypos; int yy = yoff; int th; for (MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) { if (yy > my + mh) break; MusECore::Track* track = *it; th = track->height(); if (!th) continue; if (!track->isMidiTrack()) { // draw automation QRect r(mx, yy, mw, th); if(r.intersects(mr)) { drawAutomation(p, r, (MusECore::AudioTrack*)track); drawAutomationPoints(p, r, (MusECore::AudioTrack*)track); drawAutomationText(p, r, (MusECore::AudioTrack*)track); } } yy += th; } unsigned int startPos = MusEGlobal::extSyncFlag ? MusEGlobal::audio->getStartExternalRecTick() : MusEGlobal::audio->getStartRecordPos().tick(); if (MusEGlobal::song->punchin()) startPos=MusEGlobal::song->lpos(); int startx = mapx(startPos); if(startx < 0) startx = 0; int width = mapx(MusEGlobal::song->cpos()) - startx; if (MusEGlobal::song->cpos() < startPos) { p.restore(); return; // no drawing if we are before punch out } if (MusEGlobal::song->punchout() && MusEGlobal::song->cpos() > MusEGlobal::song->rpos()) { p.restore(); return; // no drawing if we are beyond punch out. } // write recording while it happens to get feedback // should be enhanced with solution that draws waveform also // ^^ done int yPos = yoff; if (MusEGlobal::song->record() && MusEGlobal::audio->isPlaying()) { QPen pen(Qt::black, 0, Qt::SolidLine); pen.setCosmetic(true); for (MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) { MusECore::Track* track = *it; th = track->height(); if (!th) continue; if (track->recordFlag()) { p.setPen(pen); QColor c; if(curColorIndex == 0 && MusEGlobal::config.useTrackColorForParts) c = track->color(); else c = MusEGlobal::config.partColors[curColorIndex]; c.setAlpha(MusEGlobal::config.globalAlphaBlend); QLinearGradient gradient(QPoint(startx,yPos), QPoint(startx,yPos+th)); gradient.setColorAt(0, c); gradient.setColorAt(1, c.darker()); QBrush cc(gradient); p.setBrush(cc); //fprintf(stderr, "startx = %d\n", startx); p.drawRect(startx,yPos, width, th); if(track->type() == MusECore::Track::WAVE){ if(MusEGlobal::config.liveWaveUpdate){ MusECore::SndFileR fp = ((MusECore::AudioTrack *)track)->recFile(); if(!fp.isNull()){ unsigned int _startFrame = MusEGlobal::tempomap.tick2frame(startPos); unsigned int _endFrame = MusEGlobal::song->cPos().frame(); unsigned int _lengthFrame = _endFrame - _startFrame; if(_startFrame <= _endFrame) { drawWaveSndFile(p, fp, 0, _startFrame, 0, _lengthFrame, yPos, startx, startx + width, th, false); } } } } } yPos+=th; } } p.restore(); // draw midi events on yPos=0; if (MusEGlobal::song->record() && MusEGlobal::audio->isPlaying()) { for (MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) { MusECore::Track* track = *it; if (track->isMidiTrack() && track->recordFlag()) { MusECore::MidiTrack *mt = (MusECore::MidiTrack*)track; QRect partRect(startPos,yPos, MusEGlobal::song->cpos()-startPos, track->height()); // probably the wrong rect MusECore::EventList myEventList; if (mt->mpevents.size()) { for (MusECore::ciMPEvent i = mt->mpevents.begin(); i != mt->mpevents.end(); ++i) { MusECore::MidiPlayEvent pe = *i; if (pe.isNote() && !pe.isNoteOff()) { MusECore::Event e(MusECore::Note); e.setPitch(pe.dataA()); e.setTick(pe.time()-startPos); e.setLenTick(MusEGlobal::song->cpos()-pe.time()); e.setC(1); // we abuse this value to determine that this note hasn't been concluded myEventList.add(e); } else if (pe.isNoteOff()) { for (MusECore::iEvent i = myEventList.begin(); i != myEventList.end(); ++i) { MusECore::Event &e = i->second; if (e.pitch() == pe.dataA() && e.dataC() == 1) { e.setLenTick(pe.time() - e.tick()- startPos); e.setC(0); // reset the variable we borrowed for state handling continue; } } } } } drawMidiPart(p, mr, myEventList, mt, 0, partRect,startPos,0,MusEGlobal::song->cpos()-startPos, false); } yPos+=track->height(); } } } //--------------------------------------------------------- // drawAudioTrack //--------------------------------------------------------- void PartCanvas::drawAudioTrack(QPainter& p, const QRect& mr, const QRegion& /*mrg*/, const ViewRect& vbbox, MusECore::AudioTrack* /*t*/) { const ViewRect vr(mr, true); const ViewXCoordinate& vx = vr._x; const ViewWCoordinate& vw = vr._width; const ViewXCoordinate vx_2 = mathXCoordinates(vx, vw, MathAdd); const ViewYCoordinate& vy = vr._y; const ViewHCoordinate& vh = vr._height; const ViewYCoordinate vy_2 = mathYCoordinates(vy, vh, MathAdd); // The gradient is to be on the inside of the border. const ViewRect vbb_gr = adjustedRect( vbbox, ViewWCoordinate(1, true), ViewHCoordinate(1, true), ViewWCoordinate(0, true), // Usually this would be -2 but here we want the bottom extended by 1. ViewHCoordinate(-1, true)); const ViewRect vbr_gr = intersected(vr, vbb_gr); const QRect mbb = asQRectMapped(vbbox); const QRect mbb_gr = asQRectMapped(vbb_gr); const QRect mbr_gr = asQRectMapped(vbr_gr); const int mx = mr.x(); const int mw = mr.width(); const int mx_2 = mx + mw; const ViewXCoordinate& vbbx = vbbox._x; const ViewYCoordinate& vbby = vbbox._y; const ViewHCoordinate& vbbh = vbbox._height; const ViewYCoordinate vbby_2 = mathYCoordinates(vbby, vbbh, MathAdd); const int mbbx = mbb.x(); const int mbby = mbb.y(); const int mbbh = mbb.height(); const int mbby_2 = mbby + mbbh; QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); // For testing... // fprintf(stderr, "\nPartCanvas::drawAudioTrack: mbbox:\nx:%8d\t\ty:%8d\t\tw:%8d\t\th:%8d\n\n", // mbb.x(), mbb.y(), mbb.width(), mbb.height()); // vbbox.dump("vbbox:"); // vr.dump("vr:"); // vbr.dump("vbr:"); // vbr_gr.dump("vbr_gr:"); if(!isViewRectEmpty(vbr_gr)) { // For testing... // fprintf(stderr, "\n...gradient in range. Drawing gradient at:\nmbr_gr.x:%8d\t\tmbr_gr.y:%8d\t\tmbr_gr.w:%8d\t\tmbr_gr.h:%8d\n\n", // mbr_gr.x(), mbr_gr.y(), mbr_gr.width(), mbr_gr.height()); QColor c(MusEGlobal::config.dummyPartColor); c.setAlpha(MusEGlobal::config.globalAlphaBlend); p.fillRect(mbr_gr, MusECore::getGradientFromColor(c, mbb_gr.topLeft(), mbb_gr.bottomLeft(), qBound(0, MusEGlobal::config.partGradientStrength, 200))); } int mx0_lim = mbbx; if(mx0_lim < mx) mx0_lim = mx; if(mx0_lim < 0) mx0_lim = 0; const ViewYCoordinate vbby_is = compareYCoordinates(vy, vbby, CompareLess) ? vbby : vy; const ViewYCoordinate vbby_2is = compareYCoordinates(vbby_2, vy_2, CompareLess) ? vbby_2 : vy_2; if(isXInRange(vbbx, vx, vx_2) && compareYCoordinates(vbby_2is, vbby_is, CompareGreaterEqual)) { // For testing... // fprintf(stderr, "...left edge in range. Drawing left edge at mbbx:%d asMapped(vbby_is):%d mbbx:%d asMapped(vbby_2is):%d\n", // mbbx, asMapped(vbby_is)._value, mbbx, asMapped(vbby_2is)._value); p.drawLine(mbbx, asMapped(vbby_is)._value, mbbx, asMapped(vbby_2is)._value); // The left edge } if(compareXCoordinates(vx_2, vbbx, CompareGreaterEqual)) { if(isYInRange(vbby, vy, vy_2)) { // For testing... // fprintf(stderr, "...top edge in range. Drawing top edge at mx0_lim:%d mbby:%d mx_2:%d mbby:%d\n", // mx0_lim, mbby, mx_2, mbby); p.drawLine(mx0_lim, mbby, mx_2, mbby); // The top edge } if(isYInRange(vbby_2, vy, vy_2)) { // For testing... // fprintf(stderr, "...bottom edge in range. Drawing bottom edge at mx0_lim:%d mbby_2:%d mx_2:%d mbby_2:%d\n", // mx0_lim, mbby_2, mx_2, mbby_2); p.drawLine(mx0_lim, mbby_2, mx_2, mbby_2); // The bottom edge. Special for Audio track - draw one past bottom. } } } //--------------------------------------------------------- // drawAutomation //--------------------------------------------------------- void PartCanvas::drawAutomation(QPainter& p, const QRect& rr, MusECore::AudioTrack *t) { const int bottom = rr.bottom() - 2; const int height = bottom - rr.top() - 2; // limit height p.setBrush(Qt::NoBrush); MusECore::CtrlListList* cll = t->controller(); for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { MusECore::CtrlList *cl = icll->second; if (cl->dontShow() || !cl->isVisible()) continue; MusECore::iCtrl ic=cl->begin(); int oldX = mapx(0); if(rr.right() < oldX) { //p.restore(); return; } int xpixel = oldX; int oldY = -1; int ypixel = oldY; double min, max; cl->range(&min,&max); bool discrete = cl->mode() == MusECore::CtrlList::DISCRETE; QColor line_color(cl->color()); line_color.setAlpha(MusEGlobal::config.globalAlphaBlend); QPen pen1(line_color); pen1.setCosmetic(true); // QString txt; // Store first value for later double yfirst; { double startVal = (ic == cl->end()) ? cl->curVal() : cl->begin()->second.val; if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume yfirst = logToVal(startVal, min, max); // represent volume between 0 and 1 if (yfirst < 0) yfirst = 0.0; } else { yfirst = (startVal - min)/(max-min); // we need to set curVal between 0 and 1 } yfirst = oldY = bottom - rmapy_f(yfirst) * height; } // Check that there IS automation, ic == cl->end means no automation if (ic == cl->end()) { ypixel = yfirst; } else { for (; ic !=cl->end(); ++ic) { double y = ic->second.val; if (cl->valueType() == MusECore::VAL_LOG ) { y = logToVal(y, min, max); // represent volume between 0 and 1 if (y < 0) y = 0.0; } else y = (y-min)/(max-min); // we need to set curVal between 0 and 1 ypixel = bottom - rmapy_f(y) * height; xpixel = mapx(MusEGlobal::tempomap.frame2tick(ic->second.frame)); if (oldY==-1) oldY = ypixel; // Ideally we would like to cut the lines right at the rectangle boundaries. // But they might not be drawn exactly the same as the full line would. // So we'll also accept anything that started outside the boundaries. // A small acceptable speed hit relatively speaking - but far far better than drawing all. if(oldX <= rr.right() && xpixel >= rr.left() && oldY <= rr.bottom() && ypixel >= rr.top()) { p.setPen(pen1); if(discrete) { p.drawLine(oldX, oldY, xpixel, oldY); p.drawLine(xpixel, oldY, xpixel, ypixel); } else p.drawLine(oldX, oldY, xpixel, ypixel); } if (xpixel > rr.right()) break; oldX = xpixel; oldY = ypixel; } } if (xpixel <= rr.right()) { p.setPen(pen1); p.drawLine(xpixel, ypixel, rr.right(), ypixel); } } } //--------------------------------------------------------- // drawAutomationPoints //--------------------------------------------------------- void PartCanvas::drawAutomationPoints(QPainter& p, const QRect& rr, MusECore::AudioTrack *t) { const int bottom = rr.bottom() - 2; const int height = bottom - rr.top() - 2; // limit height const int mx0 = mapx(0); const int pw2 = _automationPointWidthUnsel / 2; const int pws2 = _automationPointWidthSel / 2; MusECore::CtrlListList* cll = t->controller(); // Draw unselected vertices first. for(MusECore::ciCtrlList icll = cll->begin(); icll != cll->end(); ++icll) { MusECore::CtrlList *cl = icll->second; if(cl->dontShow() || !cl->isVisible()) continue; if(rr.right() < mx0) { //p.restore(); return; } double min, max; cl->range(&min,&max); const QColor line_col(cl->color()); const QColor vtx_col1(line_col.red() ^ 255, line_col.green() ^ 255, line_col.blue() ^ 255); QColor vtx_col2(cl->color()); vtx_col2.setAlpha(160); // If we happen to be using 1 pixel, use an inverted colour. Else use the line colour but slightly transparent. const QColor& vtx_col = (_automationPointWidthUnsel == 1) ? vtx_col1 : vtx_col2; QPen pen(vtx_col); pen.setCosmetic(true); p.setPen(pen); for(MusECore::ciCtrl ic = cl->begin(); ic != cl->end(); ++ic) { const int frame = ic->second.frame; if(automation.currentCtrlValid && automation.currentCtrlList == cl && automation.currentCtrlFrameList.contains(frame)) continue; const int xpixel = mapx(MusEGlobal::tempomap.frame2tick(frame)); if(xpixel > rr.right()) break; double y = ic->second.val; if (cl->valueType() == MusECore::VAL_LOG ) { y = logToVal(y, min, max); // represent volume between 0 and 1 if(y < 0) y = 0.0; } else y = (y-min)/(max-min); // we need to set curVal between 0 and 1 const int ypixel = bottom - rmapy_f(y) * height; if(((xpixel + pw2 >= rr.left()) && (xpixel - pw2 <= rr.right())) && ((ypixel + pw2 >= rr.top()) && (ypixel - pw2 <= rr.bottom()))) //p.fillRect(xpixel - pw2, ypixel - pw2, _automationPointWidthUnsel, _automationPointWidthUnsel, vtx_col); // For some reason this is drawing one extra pixel width and height. ??? p.drawRect(xpixel - pw2, ypixel - pw2, _automationPointWidthUnsel, _automationPointWidthUnsel); } } // Now draw selected vertices, so that they always appear on top. for(MusECore::ciCtrlList icll = cll->begin(); icll != cll->end(); ++icll) { MusECore::CtrlList *cl = icll->second; if(cl->dontShow() || !cl->isVisible()) continue; if(rr.right() < mx0) { //p.restore(); return; } double min, max; cl->range(&min,&max); // const QColor line_col(cl->color()); // const QColor vtx_col(line_col.red() ^ 255, line_col.green() ^ 255, line_col.blue() ^ 255); for(MusECore::ciCtrl ic = cl->begin(); ic != cl->end(); ++ic) { const int frame = ic->second.frame; if(!automation.currentCtrlValid || automation.currentCtrlList != cl || !automation.currentCtrlFrameList.contains(frame)) continue; const int xpixel = mapx(MusEGlobal::tempomap.frame2tick(frame)); if(xpixel > rr.right()) break; double y = ic->second.val; if (cl->valueType() == MusECore::VAL_LOG ) { y = logToVal(y, min, max); // represent volume between 0 and 1 if (y < 0) y = 0.0; } else y = (y-min)/(max-min); // we need to set curVal between 0 and 1 const int ypixel = bottom - rmapy_f(y) * height; if(((xpixel + pws2 >= rr.left()) && (xpixel - pws2 <= rr.right())) && ((ypixel + pws2 >= rr.top()) && (ypixel - pws2 <= rr.bottom()))) p.fillRect(xpixel - pws2, ypixel - pws2, _automationPointWidthSel, _automationPointWidthSel, Qt::white); } } } //--------------------------------------------------------- // drawAutomationText //--------------------------------------------------------- void PartCanvas::drawAutomationText(QPainter& p, const QRect& rr, MusECore::AudioTrack *t) { const int bottom = rr.bottom() - 2; const int height = bottom - rr.top() - 2; // limit height p.setBrush(Qt::NoBrush); p.setFont(font()); MusECore::CtrlListList* cll = t->controller(); for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { MusECore::CtrlList *cl = icll->second; if (cl->dontShow() || !cl->isVisible()) continue; MusECore::iCtrl ic=cl->begin(); int oldX = mapx(0); if(rr.right() < oldX) { //p.restore(); return; } int xpixel = 0; int ypixel = 0; double min, max; cl->range(&min,&max); QPen pen1(cl->color()); pen1.setCosmetic(true); const QColor line_col = cl->color(); QColor txt_bk((line_col.red() + line_col.green() + line_col.blue()) / 3 >= 128 ? Qt::black : Qt::white); txt_bk.setAlpha(150); // QString txt; // Store first value for later double yfirst; { if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume yfirst = logToVal(cl->curVal(), min, max); // represent volume between 0 and 1 if (yfirst < 0) yfirst = 0.0; } else { yfirst = (cl->curVal() - min)/(max-min); // we need to set curVal between 0 and 1 } yfirst = bottom - rmapy_f(yfirst) * height; } p.setPen(pen1); for (; ic !=cl->end(); ++ic) { double y = ic->second.val; if (cl->valueType() == MusECore::VAL_LOG ) { y = logToVal(y, min, max); // represent volume between 0 and 1 if (y < 0) y = 0.0; } else y = (y-min)/(max-min); // we need to set curVal between 0 and 1 ypixel = bottom - rmapy_f(y) * height; xpixel = mapx(MusEGlobal::tempomap.frame2tick(ic->second.frame)); if (xpixel > rr.right()) break; if(xpixel + 20 <= rr.right() && ypixel <= rr.bottom()) //if(!automation.currentTextRect.isNull() && // automation.currentTextRect.left() <= rr.right() && // automation.currentTextRect.top() <= rr.bottom()) { if (automation.currentCtrlValid && automation.currentCtrlList == cl && automation.currentCtrlFrameList.contains(ic->second.frame) && automation.currentCtrlFrameList.size() == 1) { QRect textRect = p.fontMetrics().boundingRect(automation.currentText).adjusted(-4, -2, 4, 2); textRect.moveLeft(xpixel + 20); textRect.moveTop(ypixel); if(textRect.right() >= rr.left() && textRect.bottom() >= rr.top()) { p.fillRect(textRect, txt_bk); p.drawText(textRect, Qt::AlignCenter, automation.currentText); } } } } //const int xTextPos = mapx(0) > rmapx(0) ? mapx(0) + 5 : rmapx(0) + 5; // follow window..(doesn't work very well) const int xTextPos = mapx(0) + 5; if(xTextPos <= rr.right() && yfirst <= rr.bottom()) { QRect textRect = fontMetrics().boundingRect(cl->name()); textRect.moveLeft(xTextPos); textRect.moveTop(yfirst); if(textRect.right() >= rr.left() && textRect.bottom() >= rr.top()) p.drawText(textRect, cl->name()); } } } //--------------------------------------------------------- // distanceSqToSegment // Returns the distance, squared, of a point to a line segment. //--------------------------------------------------------- int distanceSqToSegment(double pointX, double pointY, double x1, double y1, double x2, double y2) { double diffX = x2 - x1; double diffY = y2 - y1; if((diffX == 0) && (diffY == 0)) { diffX = pointX - x1; diffY = pointY - y1; return diffX * diffX + diffY * diffY; } const double t = ((pointX - x1) * diffX + (pointY - y1) * diffY) / (diffX * diffX + diffY * diffY); if (t < 0.0) { //point is nearest to the first point i.e x1 and y1 diffX = pointX - x1; diffY = pointY - y1; } else if (t > 1.0) { //point is nearest to the end point i.e x2 and y2 diffX = pointX - x2; diffY = pointY - y2; } else { //if perpendicular line intersect the line segment. diffX = pointX - (x1 + t * diffX); diffY = pointY - (y1 + t * diffY); } return diffX * diffX + diffY * diffY; } //--------------------------------------------------------- // checkIfNearPoint //--------------------------------------------------------- bool checkIfNearPoint(int mouseX, int mouseY, int eventX, int eventY, int circumference) { return (ABS(mouseX - eventX) < circumference && ABS(mouseY - eventY) < circumference); } //--------------------------------------------------------- // checkAutomation // compares the current mouse pointer with the automation // lines on the track under it. // if there is a controller to be moved it is marked // in the automation object // if addNewCtrl is set and a valid line is found the // automation object will also be set but no // controller added. //--------------------------------------------------------- void PartCanvas::checkAutomation(MusECore::Track * t, const QPoint &pointer, bool /*NOTaddNewCtrl*/) { if (t->isMidiTrack()) return; int mouseY; const int trackY = t->y(); const int trackH = t->height(); { int y = pointer.y(); if(y < trackY || y >= (trackY + trackH)) return; mouseY = mapy(y); } const int mouseX = mapx(pointer.x()); int closest_point_radius2 = PartCanvas::_automationPointDetectDist * PartCanvas::_automationPointDetectDist; int closest_point_frame = 0; double closest_point_value = 0.0; //int closest_point_x = 0; //int closest_point_y = 0; MusECore::CtrlList* closest_point_cl = NULL; int closest_line_dist2 = PartCanvas::_automationPointDetectDist * PartCanvas::_automationPointDetectDist; MusECore::CtrlList* closest_line_cl = NULL; MusECore::CtrlListList* cll = ((MusECore::AudioTrack*) t)->controller(); for(MusECore::ciCtrlList icll = cll->begin(); icll != cll->end(); ++icll) { MusECore::CtrlList *cl = icll->second; if(cl->dontShow() || !cl->isVisible()) continue; MusECore::ciCtrl ic=cl->begin(); int eventOldX = mapx(0); int eventX = eventOldX; int eventOldY = -1; int eventY = eventOldY; double min, max; cl->range(&min,&max); bool discrete = cl->mode() == MusECore::CtrlList::DISCRETE; // First check that there IS automation for this controller, ic == cl->end means no automation if(ic == cl->end()) { double y; if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume y = logToVal(cl->curVal(), min, max); // represent volume between 0 and 1 if (y < 0) y = 0.0; } else y = (cl->curVal() - min)/(max-min); // we need to set curVal between 0 and 1 eventY = eventOldY = mapy(trackY+trackH-1 - 2 - y * trackH); } else // we have automation, loop through it { for (; ic!=cl->end(); ++ic) { double y = ic->second.val; if (cl->valueType() == MusECore::VAL_LOG ) { // use db scale for volume y = logToVal(y, min, max); // represent volume between 0 and 1 if (y < 0) y = 0; } else y = (y-min)/(max-min); // we need to set curVal between 0 and 1 eventY = mapy(trackY + trackH - 2 - y * trackH); eventX = mapx(MusEGlobal::tempomap.frame2tick(ic->second.frame)); if (eventOldY==-1) eventOldY = eventY; if(pointer.x() > 0 && pointer.y() > 0) { const int dx = mouseX - eventX; const int dy = mouseY - eventY; const int r2 = dx * dx + dy * dy; if(r2 < closest_point_radius2) { closest_point_radius2 = r2; closest_point_frame = ic->second.frame; closest_point_value = ic->second.val; //closest_point_x = eventX; //closest_point_y = eventY; closest_point_cl = cl; } } const int ldist2 = distanceSqToSegment(mouseX, mouseY, eventOldX, eventOldY, eventX, discrete ? eventOldY : eventY); if(ldist2 < closest_line_dist2) { closest_line_dist2 = ldist2; closest_line_cl = cl; } eventOldX = eventX; eventOldY = eventY; } } if(mouseX >= eventX) { const int d2 = (mouseY-eventY) * (mouseY-eventY); if(d2 < closest_line_dist2) { closest_line_dist2 = d2; closest_line_cl = cl; } } } // Is the mouse close to a vertex? Since we currently don't use the addNewCtrl accel key, vertices take priority over lines. if(closest_point_cl) { QWidget::setCursor(Qt::PointingHandCursor); automation.currentCtrlFrameList.clear(); automation.currentCtrlFrameList.append(closest_point_frame); automation.currentCtrlValid = true; automation.controllerState = movingController; automation.currentCtrlList = closest_point_cl; automation.currentTrack = t; // Store the text. if(closest_point_cl->valueType() == MusECore::VAL_LOG) //closest_point_value = MusECore::fast_log10(closest_point_value) * 20.0; closest_point_value = muse_val2dbr(closest_point_value); // Here we can use the slower but accurate function. automation.currentText = QString("Param:%1 Value:%2").arg(closest_point_cl->name()).arg(closest_point_value, 0, 'g', 3); // FIXME These are attempts to update only the necessary rectangles. No time for it ATM, not much choice but to do full update. #if 0 // Be sure to erase (redraw) the old rectangles. int erase_ypixel = bottom - rmapy_f(y) * height; int erase_xpixel = mapx(MusEGlobal::tempomap.frame2tick(ic->second.frame)); if(!automation.currentTextRect.isNull()) update(automation.currentTextRect); if(!automation.currentVertexRect.isNull()) update(automation.currentVertexRect); // Store the text and its rectangle. double value = closest_point_value; double min, max; closest_point_cl->range(&min,&max); if(closest_point_cl->valueType() == MusECore::VAL_LOG) //closest_point_value = MusECore::fast_log10(closest_point_value) * 20.0; value = muse_val2dbr(value); // Here we can use the slower but accurate function. automation.currentText = QString("Param:%1 Frame:%2 Value:%3").arg(closest_point_cl->name()).arg(closest_point_frame).arg(value); // automation.currentTextRect = fontMetrics().boundingRect(automation.currentText).adjusted(-4, -2, 4, 2); automation.currentTextRect = fontMetrics().boundingRect(automation.currentText).adjusted(0, 0, 4, 4); // automation.currentTextRect.moveLeft(closest_point_x + 20); // automation.currentTextRect.moveTop(closest_point_y); // if (inLog < min) inLog = min; // Should not happen // if (inLog > max) inLog = max; // double linMin = 20.0*MusECore::fast_log10(min); // double linMax = 20.0*MusECore::fast_log10(max); // double linVal = 20.0*MusECore::fast_log10(inLog); const double linMin = muse_val2dbr(min); const double linMax = muse_val2dbr(max); //const double n_value = (value - linMin) / (linMax - linMin); // Normalize automation.currentTick = MusEGlobal::tempomap.frame2tick(closest_point_frame); automation.currentYNorm = (value - linMin) / (linMax - linMin); // Normalize // Store the selected vertex rectangle. Use the selected size, which can be different than the unselected size. automation.currentVertexRect = QRect(closest_point_x - PartCanvas::_automationPointWidthSel / 2, closest_point_y - PartCanvas::_automationPointWidthSel / 2, PartCanvas::_automationPointWidthSel, PartCanvas::_automationPointWidthSel); // Now fill the text's new rectangle. update(automation.currentTextRect); // And fill the vertex's new rectangle. update(automation.currentVertexRect); #else //update(); controllerChanged(automation.currentTrack, automation.currentCtrlList->id()); #endif return; } // Is the mouse close to a line? if(closest_line_cl) { QWidget::setCursor(Qt::CrossCursor); automation.currentCtrlValid = false; automation.controllerState = addNewController; automation.currentCtrlList = closest_line_cl; automation.currentTrack = t; #if 0 // Be sure to erase (refill) the old rectangles. if(!automation.currentTextRect.isNull()) update(automation.currentTextRect); if(!automation.currentVertexRect.isNull()) update(automation.currentVertexRect); #else //update(); controllerChanged(automation.currentTrack, automation.currentCtrlList->id()); #endif return; } if(automation.currentCtrlValid && automation.currentTrack && automation.currentCtrlList) controllerChanged(automation.currentTrack, automation.currentCtrlList->id()); // if there are no hits we default to clearing all the data automation.controllerState = doNothing; automation.currentCtrlValid = false; automation.currentCtrlList = 0; automation.currentTrack = 0; automation.currentCtrlFrameList.clear(); #if 0 // Be sure to erase (refill) the old rectangles. if(!automation.currentTextRect.isNull()) update(automation.currentTextRect); automation.currentTextRect = QRect(); if(!automation.currentVertexRect.isNull()) update(automation.currentVertexRect); automation.currentVertexRect = QRect(); #else //update(); #endif setCursor(); } void PartCanvas::controllerChanged(MusECore::Track* t, int) { redraw((QRect(0, mapy(t->y()), width(), rmapy(t->height())))); // TODO Check this - correct? } void PartCanvas::processAutomationMovements(QPoint inPos, bool slowMotion) { if (_tool != AutomationTool) return; if (!automation.moveController) { // currently nothing going lets's check for some action. MusECore::Track * t = y2Track(inPos.y()); if (t) { checkAutomation(t, inPos, false); } automation.startMovePoint = inPos; return; } if(automation.controllerState != movingController) { automation.startMovePoint = inPos; return; } Undo operations; int deltaX = inPos.x() - automation.startMovePoint.x(); int deltaY = inPos.y() - automation.startMovePoint.y(); if (slowMotion) { deltaX /= 3; deltaY /= 3; } const QPoint pos(automation.startMovePoint.x() + deltaX, automation.startMovePoint.y() + deltaY); const int posy=mapy(pos.y()); const int tracky = mapy(automation.currentTrack->y()); const int trackHeight = automation.currentTrack->height(); const int mouseY = trackHeight - (posy - tracky)-2; const double yfraction = ((double)mouseY)/automation.currentTrack->height(); double min, max; automation.currentCtrlList->range(&min,&max); double cvval; if (automation.currentCtrlList->valueType() == MusECore::VAL_LOG ) { // use db scale for volume cvval = valToLog(yfraction, min, max); if (cvval< min) cvval=min; if (cvval>max) cvval=max; } else { // we need to set val between 0 and 1 (unless integer) cvval = yfraction * (max-min) + min; // 'Snap' to integer or boolean if (automation.currentCtrlList->mode() == MusECore::CtrlList::DISCRETE) cvval = rint(cvval + 0.1); // LADSPA docs say add a slight bias to avoid rounding errors. Try this. if (cvval< min) cvval=min; if (cvval>max) cvval=max; } // Store the text. double displayCvVal = cvval; if(automation.currentCtrlList->valueType() == MusECore::VAL_LOG) displayCvVal = muse_val2dbr(cvval); automation.currentText = QString("Param:%1 Value:%2").arg(automation.currentCtrlList->name()).arg(displayCvVal, 0, 'g', 3); const int fl_sz = automation.currentCtrlFrameList.size(); for(int i = 0; i < fl_sz; ++i) { const int old_frame = automation.currentCtrlFrameList.at(i); const int old_tick = MusEGlobal::tempomap.frame2tick(old_frame); const int new_tick = old_tick + deltaX; const int delta_frame = MusEGlobal::tempomap.deltaTick2frame(old_tick, new_tick); MusECore::ciCtrl iold = automation.currentCtrlList->find(old_frame); if(iold != automation.currentCtrlList->end()) { const double old_value = iold->second.val; // The minimum frame that this selected frame can be moved to is the previous // UNSELECTED vertex frame, PLUS the number of items from here to that vertex... int min_prev_frame = 0; MusECore::ciCtrl iprev = iold; int prev_frame_offset = 0; while(iprev != automation.currentCtrlList->begin()) { --iprev; ++prev_frame_offset; // Stop when we find the previous unselected frame. if(!automation.currentCtrlFrameList.contains(iprev->second.frame)) { min_prev_frame = iprev->second.frame + prev_frame_offset; break; } } // The maximum frame that this selected frame can be moved to is the next // UNSELECTED vertex frame, MINUS the number of items from here to that vertex... int max_next_frame = -1; MusECore::ciCtrl inext = iold; ++inext; int next_frame_offset = 1; // Yes, that's 1. while(inext != automation.currentCtrlList->end()) { // Stop when we find the next unselected frame. if(!automation.currentCtrlFrameList.contains(inext->second.frame)) { max_next_frame = inext->second.frame - next_frame_offset; break; } ++inext; ++next_frame_offset; } int new_frame = old_frame + delta_frame; if(new_frame < min_prev_frame) new_frame = min_prev_frame; if(max_next_frame != -1 && new_frame > max_next_frame) new_frame = max_next_frame; //if(old_frame != new_frame) //{ automation.currentCtrlFrameList.replace(i, new_frame); operations.push_back(UndoOp(UndoOp::ModifyAudioCtrlVal, automation.currentTrack, automation.currentCtrlList->id(), old_frame, new_frame, old_value, cvval)); //} } } automation.startMovePoint = inPos; if(!operations.empty()) { operations.combobreaker = automation.breakUndoCombo; automation.breakUndoCombo = false; // Reset. MusEGlobal::song->applyOperationGroup(operations); // User probably would like to hear results so make sure controller is enabled. ((MusECore::AudioTrack*)automation.currentTrack)->enableController(automation.currentCtrlList->id(), true); controllerChanged(automation.currentTrack, automation.currentCtrlList->id()); } } void PartCanvas::newAutomationVertex(QPoint pos) { if (_tool != AutomationTool || automation.controllerState != addNewController) return; // get the current controller value at the frame pointed at const int frame = MusEGlobal::tempomap.tick2frame(pos.x()); MusECore::CtrlInterpolate ctrlInterpolate; automation.currentCtrlList->getInterpolation(frame, false, &ctrlInterpolate); const double cvval = automation.currentCtrlList->interpolate(frame, ctrlInterpolate); // Keep a string for easy displaying of automation values double displayCvVal = cvval; if(automation.currentCtrlList->valueType() == MusECore::VAL_LOG) displayCvVal = muse_val2dbr(cvval); automation.currentText = QString("Param:%1 Value:%2").arg(automation.currentCtrlList->name()).arg(displayCvVal, 0, 'g', 3); Undo operations; operations.push_back(UndoOp(UndoOp::AddAudioCtrlVal, automation.currentTrack, automation.currentCtrlList->id(), frame, cvval)); automation.currentCtrlFrameList.clear(); automation.currentCtrlFrameList.append(frame); automation.currentCtrlValid = true; automation.controllerState = movingController; automation.startMovePoint = pos; if(!operations.empty()) { operations.combobreaker = automation.breakUndoCombo; automation.breakUndoCombo = false; // Reset. MusEGlobal::song->applyOperationGroup(operations); // User probably would like to hear results so make sure controller is enabled. ((MusECore::AudioTrack*)automation.currentTrack)->enableController(automation.currentCtrlList->id(), true); controllerChanged(automation.currentTrack, automation.currentCtrlList->id()); } } //--------------------------------------------------------- // // logToVal // - represent logarithmic value on linear scale from 0 to 1 // //--------------------------------------------------------- double PartCanvas::logToVal(double inLog, double min, double max) { if (inLog < min) inLog = min; if (inLog > max) inLog = max; double linMin = 20.0*MusECore::fast_log10(min); double linMax = 20.0*MusECore::fast_log10(max); double linVal = 20.0*MusECore::fast_log10(inLog); double outVal = (linVal-linMin) / (linMax - linMin); return outVal; } //--------------------------------------------------------- // // valToLog // - represent value from 0 to 1 as logarithmic value between min and max // //--------------------------------------------------------- double PartCanvas::valToLog(double inV, double min, double max) { double linMin = 20.0*MusECore::fast_log10(min); double linMax = 20.0*MusECore::fast_log10(max); double linVal = (inV * (linMax - linMin)) + linMin; double outVal = exp10((linVal)/20.0); //printf("::valToLog inV %f outVal %f linVal %f min %f max %f\n", inV, outVal, linVal, min, max); if (outVal > max) outVal = max; if (outVal < min) outVal = min; return outVal; } //--------------------------------------------------------- // endMoveItems // dir = 0 move in all directions // 1 move only horizontal // 2 move only vertical //--------------------------------------------------------- void PartCanvas::endMoveItems(const QPoint& pos, DragType dragtype, int dir, bool rasterize) { int dp = y2pitch(pos.y()) - y2pitch(start.y()); int dx = pos.x() - start.x(); if (dir == 1) dp = 0; else if (dir == 2) dx = 0; moveCanvasItems(moving, dp, dx, dragtype, rasterize); moving.clear(); itemSelectionsChanged(); redraw(); } void PartCanvas::setRangeToSelection() { CItem *leftmost = nullptr, *rightmost = nullptr; for (const auto& i : items) { if (i.second->isSelected()) { if (!leftmost) leftmost = i.second; else if (leftmost->x() > i.second->x()) leftmost = i.second; if (!rightmost) rightmost = i.second; else if (rightmost->x() < i.second->x()) rightmost = i.second; } } if (leftmost && rightmost) { int left_tick = leftmost->part()->tick(); int right_tick = rightmost->part()->tick() + rightmost->part()->lenTick(); MusECore::Pos p1(left_tick, true); MusECore::Pos p2(right_tick, true); if (p1 < MusEGlobal::song->lPos()) { MusEGlobal::song->setPos(MusECore::Song::LPOS, p1); MusEGlobal::song->setPos(MusECore::Song::RPOS, p2); } else { MusEGlobal::song->setPos(MusECore::Song::RPOS, p2); MusEGlobal::song->setPos(MusECore::Song::LPOS, p1); } } } int PartCanvas::currentPartColorIndex() const { return curColorIndex; } } // namespace MusEGui muse-4.0.0/src/muse/arranger/pcanvas.h000066400000000000000000000215531404105311000176240ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pcanvas.h,v 1.11.2.4 2009/05/24 21:43:44 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PCANVAS_H__ #define __PCANVAS_H__ #include #include #include #include #include "type_defs.h" #include "canvas.h" #include "undo.h" #include "track.h" // Forward declarations: class QDropEvent; class QMouseEvent; class QKeyEvent; class QEvent; class QDragEnterEvent; class QLineEdit; class QMenu; class QPainter; namespace MusECore { class Xml; class Track; class MidiTrack; class AudioTrack; class Part; class WavePart; class MidiPart; class PartList; class CItem; } namespace MusEGui { //--------------------------------------------------------- // NPart // ''visual'' Part // wraps Parts with additional information needed // for displaying //--------------------------------------------------------- class NPart : public PItem { protected: int _serial; public: NPart(MusECore::Part*); NPart() { } const QString name() const { return part()->name(); } void setName(const QString& s) { part()->setName(s); } MusECore::Track* track() const { return part()->track(); } int serial() { return _serial; } bool leftBorderTouches; // Whether the borders touch other part borders. bool rightBorderTouches; }; enum ControllerVals { doNothing, movingController, addNewController }; struct AutomationObject { QPoint startMovePoint; QList currentCtrlFrameList; bool currentCtrlValid; MusECore::CtrlList *currentCtrlList; MusECore::Track *currentTrack; bool moveController; ControllerVals controllerState; QString currentText; bool breakUndoCombo; //QRect currentTextRect; //QRect currentVertexRect; //int currentTick; //int currentYNorm; }; //--------------------------------------------------------- // PartCanvas //--------------------------------------------------------- enum PartOperations { OP_RENAME = 0, OP_DELETE = 1, OP_SPLIT = 2, OP_GLUE = 3, OP_CUT = 4, OP_COPY = 5, OP_GLUESELECTION = 6, OP_WAVEEDIT = 14, OP_DECLONE = 15, OP_SAVEPARTTODISK = 16, OP_FILEINFO = 17, OP_SELECT_CLONES = 18, OP_NORMALIZE = 19, OP_PARTCOLORBASE = 20, }; class PartCanvas : public Canvas { Q_OBJECT int* _raster; MusECore::TrackList* tracks; QLineEdit* lineEditor; NPart* editPart; int curColorIndex; bool editMode; static const int _automationPointDetectDist; static const int _automationPointWidthUnsel; static const int _automationPointWidthSel; QElapsedTimer editingFinishedTime; AutomationObject automation; void updateSelectedItem(CItem* newItem, bool add, bool singleSelection); virtual void keyPress(QKeyEvent*); virtual void keyRelease(QKeyEvent* event); virtual bool mousePress(QMouseEvent*); virtual void mouseMove(QMouseEvent* event); virtual void mouseRelease(QMouseEvent*); virtual void viewMouseDoubleClickEvent(QMouseEvent*); virtual void leaveEvent(QEvent*e); virtual void drawItem(QPainter&, const CItem*, const QRect&, const QRegion& = QRegion()); virtual void drawMoving(QPainter&, const CItem*, const QRect&, const QRegion& = QRegion()); virtual bool itemSelectionsChanged(MusECore::Undo* operations = 0, bool deselectAll = false); virtual QPoint raster(const QPoint&) const; virtual int y2pitch(int y) const; virtual int pitch2y(int p) const; virtual int y2height(int) const; virtual inline int yItemOffset() const { return 0; } virtual CItem* newItem(const QPoint&, int); virtual void resizeItem(CItem*,bool, bool ctrl); virtual void newItem(CItem*,bool); virtual bool deleteItem(CItem*); virtual void renameItem(CItem*); virtual void moveCanvasItems(CItemMap&, int, int, DragType, bool rasterize = true); virtual void startDrag(CItem*, DragType); virtual void dragEnterEvent(QDragEnterEvent*); virtual void viewDropEvent(QDropEvent*); virtual QMenu* genItemPopup(CItem*); virtual void itemPopup(CItem*, int, const QPoint&); void glueItem(CItem* item); void splitItem(CItem* item, const QPoint&); void copy(MusECore::PartList*); void copy_in_range(MusECore::PartList*); enum paste_mode_t { PASTEMODE_MIX, PASTEMODE_MOVEALL, PASTEMODE_MOVESOME }; void paste(bool clone = false, paste_mode_t paste_mode = PASTEMODE_MIX, bool to_single_track=false, int amount=1, int raster=1536); MusECore::Undo pasteAt(const QString&, MusECore::Track*, unsigned int, bool clone = false, bool toTrack = true, unsigned int* finalPosPtr = NULL, std::set* affected_tracks = NULL); void drawWaveSndFile(QPainter &p, MusECore::SndFileR &f, int samplePos, unsigned rootFrame, unsigned startFrame, unsigned lengthFrames, int startY, int startX, int endX, int rectHeight, bool selected); void drawWavePart(QPainter&, const QRect&, MusECore::WavePart*, const QRect&, bool selected); void drawMidiPart(QPainter&, const QRect& rect, const MusECore::EventList& events, MusECore::MidiTrack* mt, MusECore::MidiPart* midipart, const QRect& r, int pTick, int from, int to, bool selected); void drawMidiPart(QPainter&, const QRect& rect, MusECore::MidiPart* midipart, const QRect& r, int from, int to, bool selected); MusECore::Track* y2Track(int) const; void drawAudioTrack(QPainter& p, const QRect& mr, const QRegion& vrg, const ViewRect& vbbox, MusECore::AudioTrack* track); void drawAutomation(QPainter& p, const QRect& r, MusECore::AudioTrack* track); void drawAutomationPoints(QPainter& p, const QRect& r, MusECore::AudioTrack* track); void drawAutomationText(QPainter& p, const QRect& r, MusECore::AudioTrack* track); void drawTopItem(QPainter& p, const QRect& rect, const QRegion& = QRegion()); void checkAutomation(MusECore::Track * t, const QPoint& pointer, bool addNewCtrl); void processAutomationMovements(QPoint pos, bool slowMotion); double logToVal(double inLog, double min, double max); double valToLog(double inV, double min, double max); void newAutomationVertex(QPoint inPos); void showStatusTip(QMouseEvent *event); protected: virtual void drawCanvas(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void endMoveItems(const QPoint&, DragType, int dir, bool rasterize = true); signals: void timeChanged(unsigned); void tracklistChanged(); void dclickPart(MusECore::Track*); void dropSongFile(const QString&); void dropMidiFile(const QString&); void setUsedTool(int); void trackChanged(MusECore::Track*); void selectTrackAbove(); void selectTrackBelow(); void editTrackNameSig(); void muteSelectedTracks(); void soloSelectedTracks(); void volumeSelectedTracks(int); void panSelectedTracks(int); void curPartColorIndexChanged(int); void startEditor(MusECore::PartList*, int); private slots: void returnPressed(); public: enum { CMD_CUT_PART, CMD_COPY_PART, CMD_COPY_PART_IN_RANGE, CMD_PASTE_PART, CMD_PASTE_CLONE_PART, CMD_PASTE_PART_TO_TRACK, CMD_PASTE_CLONE_PART_TO_TRACK, CMD_PASTE_DIALOG, CMD_INSERT_EMPTYMEAS }; PartCanvas(int* raster, QWidget* parent, int, int); virtual ~PartCanvas(); void updateItems(); void cmd(int); void songIsClearing(); void setRangeToSelection(); int currentPartColorIndex() const; public slots: void redirKeypress(QKeyEvent* e) { keyPress(e); } void controllerChanged(MusECore::Track *t, int CtrlId); void setPartColor(int idx); void setCurrentColorIndex(int idx); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/arranger/structure.cpp000066400000000000000000000401471404105311000205640ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: structure.cpp,v 1.113.2.68 2009/12/21 14:51:51 spamatica Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Robert Jonsson (rj@spamatica.se) // (C) Copyright 2013 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "app.h" #include "track.h" #include "song.h" #include "tempo.h" #include "sig.h" #include "keyevent.h" #include "audio.h" #include "marker/marker.h" #include "structure.h" #include "globals.h" #include using std::set; namespace MusECore { //--------------------------------------------------------- // adjustGlobalLists // helper that adjusts tempo, sig, key and marker // lists everything from startPos is adjusted // 'diff' number of ticks. // If diff is negative it is a 'cut' operation, // otherwise it is an 'insert' operation. //--------------------------------------------------------- void adjustGlobalLists(Undo& operations, unsigned int startPos, int diff) { const TempoList* t = &MusEGlobal::tempomap; const SigList* s = &MusEGlobal::sigmap; const KeyList* k = &MusEGlobal::keymap; const MarkerList* m = MusEGlobal::song->marker(); const bool is_cut = diff < 0; // key // It is important that the order of operations always be delete followed by add... // What needs to be deleted? for (ciKeyEvent ik = k->cbegin(); ik != k->cend(); ++ik) { const KeyEvent& ev = ik->second; const unsigned int tick = ev.tick; if (tick < startPos ) continue; operations.push_back(UndoOp(UndoOp::DeleteKey, tick, ev.key, (int)ev.minor)); } // What needs to be added? for (ciKeyEvent ik = k->cbegin(); ik != k->cend(); ++ik) { const KeyEvent& ev = ik->second; const unsigned int tick = ev.tick; if (tick < startPos ) continue; if (!is_cut || tick >= startPos - diff) operations.push_back(UndoOp(UndoOp::AddKey, tick + diff, ev.key, (int)ev.minor)); } // tempo // It is important that the order of operations always be delete followed by add... // What needs to be deleted? for (ciTEvent it = t->cbegin(); it != t->cend(); ++it) { const TEvent* ev = (TEvent*)it->second; const unsigned int tick = ev->tick; if (tick < startPos ) continue; operations.push_back(UndoOp(UndoOp::DeleteTempo,tick, ev->tempo)); } // What needs to be added? for (ciTEvent it = t->cbegin(); it != t->cend(); ++it) { const TEvent* ev = (TEvent*)it->second; const unsigned int tick = ev->tick; if (tick < startPos ) continue; if (!is_cut || tick >= startPos - diff) operations.push_back(UndoOp(UndoOp::AddTempo, tick + diff, ev->tempo)); } // sig // FIXME: sig still has some issues upon undo. Possibly due to the special raster snap applied to sig? // It is important that the order of operations always be delete followed by add... // What needs to be deleted? for (ciSigEvent is = s->cbegin(); is != s->cend(); ++is) { const SigEvent* ev = (SigEvent*)is->second; const unsigned int tick = ev->tick; if (tick < startPos ) continue; operations.push_back(UndoOp(UndoOp::DeleteSig,tick, ev->sig.z, ev->sig.n)); } // What needs to be added? for (ciSigEvent is = s->cbegin(); is != s->cend(); ++is) { const SigEvent* ev = (SigEvent*)is->second; const unsigned int tick = ev->tick; if (tick < startPos ) continue; if (!is_cut || tick >= startPos - diff) operations.push_back(UndoOp(UndoOp::AddSig, tick + diff, ev->sig.z, ev->sig.n)); } // markers // What needs to be deleted? for(ciMarker i = m->cbegin(); i != m->cend(); ++i) { const Marker& ev = i->second; unsigned int tick = ev.tick(); if (tick < startPos ) continue; if (is_cut && tick < startPos - diff) { // diff is negative, these ticks should be removed operations.push_back(UndoOp(UndoOp::DeleteMarker, ev)); } } // What needs to be added or modified? for(ciMarker i = m->cbegin(); i != m->cend(); ++i) { const Marker& ev = i->second; unsigned int tick = ev.tick(); if (tick < startPos ) continue; if (!is_cut || tick >= startPos - diff) { // Grab a copy but with a new ID. Marker newMarker = ev.copy(); newMarker.setTick(tick + diff); operations.push_back(UndoOp(UndoOp::ModifyMarker, ev, newMarker)); } } } //--------------------------------------------------------- // globalCut // - remove area between left and right locator // - cut master track //--------------------------------------------------------- void globalCut(bool onlySelectedTracks) { unsigned int lpos = MusEGlobal::song->lpos(); unsigned int rpos = MusEGlobal::song->rpos(); if (lpos >= rpos) return; Undo operations; const unsigned int diff = lpos > rpos ? lpos - rpos : rpos - lpos; adjustGlobalLists(operations, lpos > rpos ? rpos : lpos, -diff); // diff is negative meaning cut // Splitting wave parts requires the tempo list be done and EXECUTED beforehand. // FIXME: This and the part splitting results in two separate operations, user must press undo twice to undo them. // Find a way to combine undo ops while leaving them as separate operations? MusEGlobal::song->applyOperationGroup(operations); operations.clear(); TrackList* tracks = MusEGlobal::song->tracks(); for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { Track* track = *it; if (track == 0 || (onlySelectedTracks && !track->selected())) continue; PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { Part* part = p->second; unsigned int t = part->tick(); unsigned int l = part->lenTick(); if (t + l <= lpos) continue; if ((t >= lpos) && ((t+l) <= rpos)) { operations.push_back(UndoOp(UndoOp::DeletePart,part)); } else if ((t < lpos) && ((t+l) > lpos) && ((t+l) <= rpos)) { // remove part tail unsigned int len = lpos - t; if (part->nextClone()==part) // no clones { // cut Events const EventList& el = part->events(); for (ciEvent ie = el.lower_bound(len); ie != el.end(); ++ie) operations.push_back(UndoOp(UndoOp::DeleteEvent,ie->second, part, false, false)); } // TODO FIXME Suspect this section may need a wee bit more rework with the events above... operations.push_back(UndoOp(UndoOp::ModifyPartLength, part, part->lenValue(), len, 0, Pos::TICKS)); } else if ((t < lpos) && ((t+l) > lpos) && ((t+l) > rpos)) { //---------------------- // remove part middle //---------------------- Part* p1; Part* p2; Part* p3; part->splitPart(lpos, p1, p2); delete p2; part->splitPart(rpos, p2, p3); delete p2; p3->setTick(lpos); MusEGlobal::song->informAboutNewParts(part,p1,p3); operations.push_back(UndoOp(UndoOp::DeletePart,part)); operations.push_back(UndoOp(UndoOp::AddPart,p1)); operations.push_back(UndoOp(UndoOp::AddPart,p3)); } else if ((t >= lpos) && (t < rpos) && (t+l) > rpos) { // remove part head Part* p1; Part* p2; part->splitPart(rpos, p1, p2); delete p1; p2->setTick(lpos); MusEGlobal::song->informAboutNewParts(part,p2); operations.push_back(UndoOp(UndoOp::DeletePart,part)); operations.push_back(UndoOp(UndoOp::AddPart,p2)); } else if (t >= rpos) { // move part to the left unsigned int nt = part->tick(); if(nt > (rpos - lpos)) operations.push_back(UndoOp(UndoOp::MovePart, part, part->posValue(), nt - (rpos -lpos), Pos::TICKS )); } } adjustAutomation(operations, track, lpos, rpos, cutOperation); } MusEGlobal::song->applyOperationGroup(operations); MusEGlobal::song->setPos(MusECore::Song::RPOS, lpos); } //--------------------------------------------------------- // globalInsert // - insert empty space at left locator position upto // right locator // - insert in master track //--------------------------------------------------------- void globalInsert(bool onlySelectedTracks) { Undo operations=movePartsTotheRight( MusEGlobal::song->lpos() <= MusEGlobal::song->rpos() ? MusEGlobal::song->lpos() : MusEGlobal::song->rpos(), MusEGlobal::song->lpos() <= MusEGlobal::song->rpos() ? MusEGlobal::song->rpos() - MusEGlobal::song->lpos() : MusEGlobal::song->lpos() - MusEGlobal::song->rpos(), onlySelectedTracks); MusEGlobal::song->applyOperationGroup(operations); } void adjustAutomation(Undo &operations, Track *track, unsigned int lpos, unsigned int rpos, OperationType type) { if (!track->isMidiTrack()) { auto *audioTrack = static_cast(track); auto controllerListList = audioTrack->controller(); auto lFrame = MusEGlobal::tempomap.tick2frame(lpos); auto rFrame = MusEGlobal::tempomap.tick2frame(rpos); // iterate through all indexes in map (all control types) for (auto controllerList : *controllerListList) { // The Undo system will take 'ownership' of these and delete them at the appropriate time. (comment taken from other code) CtrlList* removedEvents = new CtrlList(*controllerList.second, CtrlList::ASSIGN_PROPERTIES); CtrlList* readdedEvents = new CtrlList(*controllerList.second, CtrlList::ASSIGN_PROPERTIES); for (auto controller : *controllerList.second) { // iterate through all events and see if any appear after lpos if (controller.first > lFrame) { removedEvents->add(controller.second.frame, controller.second.val); if (type == cutOperation) { if (controller.first > rFrame) { auto diff = rFrame - lFrame; auto newFramePos = controller.second.frame - diff; readdedEvents->add(newFramePos, controller.second.val); } } else if (type == insertOperation) { auto diff = rFrame - lFrame; auto newFramePos = controller.second.frame + diff; readdedEvents->add(newFramePos, controller.second.val); } } } if(removedEvents->empty() && readdedEvents->empty()) { delete removedEvents; delete readdedEvents; } else { auto undoOp = UndoOp( UndoOp::ModifyAudioCtrlValList, controllerListList, removedEvents, readdedEvents); operations.push_back(undoOp); } } } } Undo movePartsTotheRight(unsigned int startTicks, unsigned int moveTicks, bool only_selected, set* tracklist) { Undo operations; adjustGlobalLists(operations, startTicks, moveTicks); // Splitting wave parts requires the tempo list be done and EXECUTED beforehand. // FIXME: This and the part splitting results in two separate operations, user must press undo twice to undo them. // Find a way to combine undo ops while leaving them as separate operations? MusEGlobal::song->applyOperationGroup(operations); operations.clear(); TrackList* tracks = MusEGlobal::song->tracks(); for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { Track* track = *it; if ( (track == 0) || (only_selected && !track->selected()) || (tracklist && tracklist->find(track)==tracklist->end()) ) continue; PartList* pl = track->parts(); for (riPart p = pl->rbegin(); p != pl->rend(); ++p) { Part* part = p->second; unsigned t = part->tick(); unsigned int l = part->lenTick(); if (t + l <= startTicks) continue; if (startTicks > t && startTicks < (t+l)) { // split part to insert new space Part* p1; Part* p2; part->splitPart(startTicks, p1, p2); p2->setTick(startTicks+moveTicks); MusEGlobal::song->informAboutNewParts(part,p1,p2); operations.push_back(UndoOp(UndoOp::DeletePart, part)); operations.push_back(UndoOp(UndoOp::AddPart, p1)); operations.push_back(UndoOp(UndoOp::AddPart, p2)); } else if (t >= startTicks) { operations.push_back(UndoOp(UndoOp::MovePart, part, part->posValue(), t + moveTicks, Pos::TICKS)); } } adjustAutomation(operations, track, MusEGlobal::song->lpos(), MusEGlobal::song->rpos(), insertOperation); } return operations; } //--------------------------------------------------------- // globalSplit // - split all parts at the song position pointer //--------------------------------------------------------- void globalSplit(bool onlySelectedTracks) { Undo operations=partSplitter(MusEGlobal::song->cpos(), onlySelectedTracks); MusEGlobal::song->applyOperationGroup(operations); } Undo partSplitter(unsigned int pos, bool onlySelectedTracks) { Undo operations; TrackList* tracks = MusEGlobal::song->tracks(); for (iTrack it = tracks->begin(); it != tracks->end(); ++it) { Track* track = *it; if (track == 0 || (onlySelectedTracks && !track->selected())) continue; PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { Part* part = p->second; unsigned int p1 = part->tick(); unsigned int l0 = part->lenTick(); if (pos > p1 && pos < (p1+l0)) { Part* p1; Part* p2; part->splitPart(pos, p1, p2); MusEGlobal::song->informAboutNewParts(part, p1); MusEGlobal::song->informAboutNewParts(part, p2); operations.push_back(UndoOp(UndoOp::DeletePart,part)); operations.push_back(UndoOp(UndoOp::AddPart,p1)); operations.push_back(UndoOp(UndoOp::AddPart,p2)); break; } } } return operations; } } // namespace MusECore muse-4.0.0/src/muse/arranger/structure.h000066400000000000000000000032131404105311000202220ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // structure.h // (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __STRUCTURE_H__ #define __STRUCTURE_H__ #include "undo.h" #include namespace MusECore { enum OperationType { cutOperation, insertOperation, }; Undo movePartsTotheRight(unsigned int startTick, unsigned int moveTick, bool only_selected=false, std::set* tracklist=NULL); Undo partSplitter(unsigned int tick, bool onlySelectedTracks=false); void adjustAutomation(Undo &operations, Track *track, unsigned int lpos, unsigned int rpos, OperationType type); void adjustGlobalLists(Undo& operations, unsigned int startPos, int diff); void globalCut(bool onlySelectedTracks=false); void globalInsert(bool onlySelectedTracks=false); void globalSplit(bool onlySelectedTracks=false); } #endif muse-4.0.0/src/muse/arranger/tlist.cpp000066400000000000000000003520271404105311000176660ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tlist.cpp,v 1.31.2.31 2009/12/15 03:39:58 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include #include #include #include #include #include #include #include #include #include "popupmenu.h" #include "globals.h" #include "icons.h" #include "tlist.h" #include "mididev.h" #include "midiport.h" #include "midictrl.h" #include "midiseq.h" #include "comment.h" #include "track.h" #include "song.h" #include "header.h" #include "audio.h" #include "instruments/minstrument.h" #include "app.h" #include "helper.h" #include "gconfig.h" #include "event.h" #include "midiedit/drummap.h" #include "synth.h" #include "config.h" #include "filedialog.h" #include "menutitleitem.h" #include "arranger.h" #include "midi_audio_control.h" #include "ctrl.h" #include "plugin.h" #include "operations.h" #include "shortcuts.h" #include "drumedit.h" #include "utils.h" #ifdef DSSI_SUPPORT #include "dssihost.h" #endif #ifdef LV2_SUPPORT #include "lv2host.h" #endif // Forwards from header: #include #include #include #include #include #include #include #include #include "xml.h" #include "undo.h" #include "header.h" #include "popupmenu.h" #include "scrollscale.h" using MusECore::UndoOp; namespace MusEGui { static const int MIN_TRACKHEIGHT = 20; //static const int WHEEL_DELTA = 120; QColor collist[] = { Qt::red, Qt::yellow, Qt::blue , Qt::black, Qt::white, Qt::green }; QString colnames[] = { "Red", "Yellow", "Blue", "Black", "White", "Green"}; enum { AUTO_INVALID = -1, AUTO_SHOW_ALL = 251, AUTO_HIDE_ALL = 252, AUTO_CLEAR_AUTO = 253, AUTO_CLEAR_MIDI = 254, AUTO_MIDI_ASSIGN = 255 }; //--------------------------------------------------------- // TList //--------------------------------------------------------- TList::TList(Header* hdr, QWidget* parent, const char* name) : QWidget(parent) // Qt::WNoAutoErase | Qt::WResizeNoErase are no longer needed according to Qt4 doc { setBackgroundRole(QPalette::NoRole); setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_StaticContents); // This is absolutely required for speed! Otherwise painfully slow because we get // full rect paint events even on small scrolls! See help on QPainter::scroll(). setAttribute(Qt::WA_OpaquePaintEvent); setStatusTip(tr("Track list: LMB to select track, CTRL+LMB to add to selection, SHIFT+LMB for range select. RMB in empty area to create tracks. Press F1 for help.")); setObjectName(name); ypos = 0; editMode = false; editJustFinished=false; setFocusPolicy(Qt::ClickFocus); setMouseTracking(true); header = hdr; _scroll = nullptr; editTrack = nullptr; editor = nullptr; chan_edit = nullptr; ctrl_edit = nullptr; mode = NORMAL; _sel3d = true; _curSelBorder = false; _curSelBorderColor = Qt::red; //setBackgroundMode(Qt::NoBackground); // ORCAN - FIXME. DELETETHIS? //setAttribute(Qt::WA_OpaquePaintEvent); resizeFlag = false; connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(redraw())); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(maybeUpdateVolatileCustomColumns())); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void TList::songChanged(MusECore::SongChangedStruct_t flags) { if (flags & (SC_MUTE | SC_SOLO | SC_RECFLAG | SC_TRACK_REC_MONITOR | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_TRACK_MOVED | SC_TRACK_SELECTION | SC_ROUTE | SC_CHANNELS | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED )) update(); if (flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED)) adjustScrollbar(); } //--------------------------------------------------------- // drawCenteredPixmap // small helper function for "draw()" below //--------------------------------------------------------- //static void drawCenteredPixmap(QPainter& p, const QPixmap* pm, const QRect& r) //{ // p.drawPixmap(r.x() + (r.width() - pm->width())/2, r.y() + (r.height() - pm->height())/2, *pm); //} //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void TList::paintEvent(QPaintEvent* ev) { paint(ev->rect()); } //--------------------------------------------------------- // redraw //--------------------------------------------------------- void TList::redraw() { update(); } //--------------------------------------------------------- // redraw //--------------------------------------------------------- void TList::redraw(const QRect& r) { update(r); } //--------------------------------------------------------- // event //--------------------------------------------------------- bool TList::event(QEvent *event) { if (event->type() == QEvent::ToolTip) { QHelpEvent *helpEvent = static_cast(event); MusECore::TrackList* l = MusEGlobal::song->tracks(); int idx = 0; int yy = -ypos; for (MusECore::iTrack i = l->begin(); i != l->end(); ++idx, yy += (*i)->height(), ++i) { MusECore::Track* track = *i; MusECore::Track::TrackType type = track->type(); int trackHeight = track->height(); if (trackHeight==0) // not visible continue; if (helpEvent->pos().y() > yy && helpEvent->pos().y() < yy + trackHeight) { if (type == MusECore::Track::AUDIO_SOFTSYNTH) { MusECore::SynthI *s = (MusECore::SynthI*)track; QToolTip::showText(helpEvent->globalPos(),track->name() + QString(" : ") + (s->synth() ? s->synth()->description() : QString(tr("SYNTH IS UNAVAILABLE!"))) + (s->synth() ? (s->synth()->uri().isEmpty() ? QString() : QString(" \n") + s->synth()->uri()) : (s->initConfig()._uri.isEmpty() ? QString() : QString(" \n") + s->initConfig()._uri))); } else QToolTip::showText(helpEvent->globalPos(),track->name()); } } return true; } return QWidget::event(event); } //--------------------------------------------------------- // paint //--------------------------------------------------------- void TList::paint(const QRect& r) { if (!isVisible()) return; QRect rect(r); QPainter p(this); if (bgPixmap.isNull()) p.fillRect(rect, MusEGlobal::config.trackBg); else p.drawTiledPixmap(rect, bgPixmap, QPoint(rect.x(), ypos + rect.y())); p.setClipRegion(rect); //printf("TList::paint hasClipping:%d\n", p.hasClipping()); // Tested true. int y = rect.y(); int w = rect.width(); int h = rect.height(); int x1 = rect.x(); int x2 = rect.x() + w; QFont fit(font()); fit.setItalic(true); QFont fbd(font()); fbd.setBold(true); //--------------------------------------------------- // Tracks //--------------------------------------------------- QColor mask_edge = QColor(90, 90, 90, 45); QColor mask_center = QColor(240, 240, 240, 175); QLinearGradient mask; mask.setColorAt(0, mask_edge); mask.setColorAt(0.15, mask_center); mask.setColorAt(0.3, mask_center); mask.setColorAt(0.85, mask_edge); mask.setColorAt(1, mask_edge); // Find up to two tracks that are soloed. MusECore::Track* solo_t_1 = nullptr; MusECore::Track* solo_t_2 = nullptr; { MusECore::TrackList* tl = MusEGlobal::song->tracks(); for(MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) { MusECore::Track* t = *it; if(t->internalSolo() || t->solo()) { if(!solo_t_1) solo_t_1 = t; else if(!solo_t_2) solo_t_2 = t; } // Did we find at least two tracks? Done. if(solo_t_1 && solo_t_2) break; } } // const int header_fh = header->fontMetrics().lineSpacing(); // const int svg_sz = qMin(header_fh + 2, qMax(MIN_TRACKHEIGHT - 5, 6)); // ??? const int svg_sz = MIN_TRACKHEIGHT - 4; // 2px margin MusECore::TrackList* l = MusEGlobal::song->tracks(); const MusECore::Track* cur_sel_track = l->currentSelection(); int idx = 0; int yy = -ypos; for (MusECore::iTrack i = l->begin(); i != l->end(); ++idx, yy += (*i)->height(), ++i) { MusECore::Track* track = *i; MusECore::Track::TrackType type = track->type(); int trackHeight = track->height(); if (trackHeight==0) // not visible continue; if (yy >= (y + h)) break; if ((yy + trackHeight) < y) continue; // // clear one row // QColor bg; if (track->selected()) { if (track == cur_sel_track) bg = MusEGlobal::config.selectTrackCurBg; else bg = MusEGlobal::config.selectTrackBg; p.setPen(MusEGlobal::config.selectTrackFg); } else { bg = track->color(); if (MusECore::isColorBright(bg)) p.setPen(Qt::black); else p.setPen(Qt::white); } p.fillRect(x1, yy, w, trackHeight, MusECore::getGradientFromColor(bg, QPoint(x1,yy), QPoint(x1, yy+trackHeight), qBound(0, MusEGlobal::config.trackGradientStrength, 100))); if (track->selected() && _sel3d) { mask.setStart(QPointF(0, yy)); mask.setFinalStop(QPointF(0, yy + trackHeight)); p.fillRect(x1, yy, w, trackHeight, mask); } if (track == cur_sel_track && _curSelBorder) { p.save(); p.setPen(_curSelBorderColor); p.setClipRect(x1 - 1, yy, w + 2, trackHeight); p.drawRect(0, yy + 1, header->length() - 2, trackHeight - 2); p.setClipping(false); p.restore(); } int x = -1; // compensate for the buggy QHeaderView, would even have to be -2 for perfect alignment for (int index = 0; index < header->count(); ++index) { int section = header->logicalIndex(index); int w = header->sectionSize(section); QRect r = p.transform().mapRect(QRect(x + 2, yy, w - 4, trackHeight)); QRect svg_r = p.transform().mapRect( QRect(x + w / 2 - svg_sz / 2, yy + trackHeight / 2 - svg_sz / 2, svg_sz, svg_sz)); if(!header->isSectionHidden(section)) { switch (section) { case COL_TRACK_IDX: if (track->selected()) p.setFont(fbd); p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, QString::number(MusEGlobal::song->tracks()->index(track) + 1)); if (p.font().bold()) p.setFont(font()); break; case COL_INPUT_MONITOR: if (track->canRecordMonitor()) { (track->recMonitor() ? monitorOnSVGIcon : monitorOffSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); } break; case COL_RECORD: if (track->canRecord()) { if (track->type() == MusECore::Track::AUDIO_OUTPUT) (track->recordFlag() ? downmixOnSVGIcon : downmixOffSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); else (track->recordFlag() ? recArmOnSVGIcon : recArmOffSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); } break; case COL_CLASS: { if(const QIcon* icon = MusECore::Track::trackTypeIcon(type)) icon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); } break; case COL_MUTE: if (track->off()) trackOffSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); else { if(!track->internalSolo() && !track->solo() && ((solo_t_1 && solo_t_1 != track) || (solo_t_2 && solo_t_2 != track))) (track->mute() ? muteAndProxyOnSVGIcon : muteProxyOnSVGIcon)->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); else if(track->mute()) muteOnSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); } break; case COL_SOLO: if(track->solo() && track->internalSolo()) soloAndProxyOnSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); else if(track->internalSolo()) soloProxyOnAloneSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); else if (track->solo()) soloOnAloneSVGIcon->paint(&p, svg_r, Qt::AlignCenter, QIcon::Normal, QIcon::On); break; // case COL_TIMELOCK: // unused // if (track->isMidiTrack() && track->locked()) { // drawCenteredPixmap(p, lockIcon, r); // } // break; case COL_NAME: if (track->selected()) p.setFont(fbd); p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, track->name()); if (p.font().bold()) p.setFont(font()); break; case COL_OCHANNEL: { QString s; int n; // Default to track port if -1 and track channel if -1. if (track->isMidiTrack()) { n = ((MusECore::MidiTrack*)track)->outChannel() + 1; } else { // show number of ports n = ((MusECore::WaveTrack*)track)->channels(); } s.setNum(n); p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, s); } break; case COL_OPORT: { QString s; if (track->isMidiTrack()) { int outport = ((MusECore::MidiTrack*)track)->outPort(); s = QString("%1:%2").arg(outport+1).arg(MusEGlobal::midiPorts[outport].portname()); } else if(track->type() == MusECore::Track::AUDIO_SOFTSYNTH) { MusECore::MidiDevice* md = dynamic_cast(track); if(md) { int outport = md->midiPort(); if((outport >= 0) && (outport < MusECore::MIDI_PORTS)) s = QString("%1:%2").arg(outport+1).arg(MusEGlobal::midiPorts[outport].portname()); else s = tr(""); } } else { p.setFont(fit); s = "n/a"; } p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); if (p.font().italic()) p.setFont(font()); } break; case COL_AUTOMATION: { QString s; if (!track->isMidiTrack()) { MusECore::CtrlListList* cll = ((MusECore::AudioTrack*)track)->controller(); int countAll=0, countVisible=0; for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { MusECore::CtrlList *cl = icll->second; if (!cl->dontShow()) countAll++; if (cl->isVisible()) countVisible++; } s = QString(" %1(%2) %3").arg(countVisible).arg(countAll).arg(tr("visible")); } else { p.setFont(fit); s = "n/a"; } p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); if (p.font().italic()) p.setFont(font()); } break; case COL_CLEF: { QString s; if (track->isMidiTrack() && track->type() == MusECore::Track::MIDI) { // no drum tracks! if (((MusECore::MidiTrack*)track)->getClef() == trebleClef) s=tr("Treble"); else if (((MusECore::MidiTrack*)track)->getClef() == bassClef) s=tr("Bass"); else if (((MusECore::MidiTrack*)track)->getClef() == grandStaff) s=tr("Grand"); } else { p.setFont(fit); s = "n/a"; } p.drawText(r, Qt::AlignVCenter|Qt::AlignLeft, s); if (p.font().italic()) p.setFont(font()); } break; default: if (section >= COL_CUSTOM_MIDICTRL_OFFSET) { if (track->isMidiTrack()) { int col_ctrl_no=Arranger::custom_columns[section - COL_CUSTOM_MIDICTRL_OFFSET].ctrl; MusECore::MidiTrack* mt=dynamic_cast(track); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; const int chan = mt->outChannel(); MusECore::MidiController* mctl = mp->midiController(col_ctrl_no, chan); int val; if (Arranger::custom_columns[section - COL_CUSTOM_MIDICTRL_OFFSET].affected_pos == Arranger::custom_col_t::AFFECT_BEGIN) val=mt->getControllerChangeAtTick(0,col_ctrl_no,MusECore::CTRL_VAL_UNKNOWN); else { val=mp->hwCtrlState(mt->outChannel(), col_ctrl_no); old_ctrl_hw_states[mt][section]=val; } if (val!=MusECore::CTRL_VAL_UNKNOWN) val-=mctl->bias(); if (col_ctrl_no!=MusECore::CTRL_PROGRAM) { p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, (val!=MusECore::CTRL_VAL_UNKNOWN)?QString::number(val):tr("off")); } else { MusECore::MidiInstrument* instr = mp->instrument(); QString name; if (val!=MusECore::CTRL_VAL_UNKNOWN) name = instr->getPatchName(mt->outChannel(), val, mt->isDrumTrack(), true); // Include default. else name = tr(""); p.drawText(r, Qt::AlignVCenter|Qt::AlignHCenter, name); } } } break; } } x += header->sectionSize(section); } p.setPen(MusEGlobal::config.trackSectionDividerColor); p.drawLine(x1, yy, x2, yy); } p.drawLine(x1, yy, x2, yy); if (mode == DRAG) { int yy = curY - dragYoff; p.setPen(Qt::green); p.drawLine(x1, yy, x2, yy); p.drawLine(x1, yy + dragHeight, x2, yy+dragHeight); } //--------------------------------------------------- // draw vertical lines //--------------------------------------------------- int n = header->count(); int xpos = -1; p.setPen(MusEGlobal::config.trackSectionDividerColor); for (int index = 0; index < n; index++) { int section = header->logicalIndex(index); xpos += header->sectionSize(section); p.drawLine(xpos, 0, xpos, height()); } } void TList::maybeUpdateVolatileCustomColumns() { MusECore::TrackList* l = MusEGlobal::song->tracks(); int idx = 0; int yy = -ypos; for (MusECore::iTrack i = l->begin(); i != l->end(); ++idx, yy += (*i)->height(), ++i) { MusECore::Track* track = *i; int trackHeight = track->height(); if (trackHeight==0) // not visible continue; int x = 0; for (int index = 0; index < header->count(); ++index) { int section = header->logicalIndex(index); if (section>=COL_CUSTOM_MIDICTRL_OFFSET && track->isMidiTrack() && (Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].affected_pos == Arranger::custom_col_t::AFFECT_CPOS) ) { int w = header->sectionSize(section); QRect r = QRect(x+2, yy, w-4, trackHeight); int ctrl_no = Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].ctrl; MusECore::MidiTrack* mt=(MusECore::MidiTrack*)track; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; int new_val = mp->hwCtrlState(mt->outChannel(), ctrl_no); if (new_val != old_ctrl_hw_states[track][section]) update(r); } x += header->sectionSize(section); } } } //--------------------------------------------------------- // returnPressed //--------------------------------------------------------- void TList::returnPressed() { if(editTrack) { if(editor && editor->isVisible()) { //editor->hide(); if (editor->text() != editTrack->name()) { MusECore::TrackList* tl = MusEGlobal::song->tracks(); for (MusECore::iTrack i = tl->begin(); i != tl->end(); ++i) { if ((*i)->name() == editor->text()) { editTrack = nullptr; editor->blockSignals(true); editor->hide(); editor->blockSignals(false); QMessageBox::critical(this, tr("MusE: bad trackname"), tr("Please choose a unique track name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); setFocus(); return; } } MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackName, editTrack, editTrack->name(), editor->text())); } } editTrack = nullptr; } editMode = false; editJustFinished = true; if(editor && editor->isVisible()) { editor->blockSignals(true); editor->hide(); editor->blockSignals(false); } setFocus(); } void TList::chanValueFinished() { if(editTrack) { // Default to track port if -1 and track channel if -1. const int channel = chan_edit->value() - (editTrack->isMidiTrack() ? 1 : 0); // Subtract 1 from midi channel display. setTrackChannel(editTrack, false, channel, 0); editTrack = nullptr; } editMode = false; editJustFinished=true; if(chan_edit->isVisible()) { chan_edit->blockSignals(true); chan_edit->hide(); chan_edit->blockSignals(false); } setFocus(); } void TList::ctrlValueFinished() { if(editTrack && editTrack->isMidiTrack()) { MusECore::MidiTrack* mt = dynamic_cast(editTrack); //if (mt && mt->type() != MusECore::Track::DRUM) // Default to track port if -1 and track channel if -1. // TODO TEST: Why was DRUM excluded? I want to say just "if(mt)", but will it break something with dynamic columns? // REMOVE Tim. if (mt) { int val = ctrl_edit->value(); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; const int chan = mt->outChannel(); MusECore::MidiController* mctl = mp->midiController(ctrl_num, chan); if (val==ctrl_edit->minimum()) val=MusECore::CTRL_VAL_UNKNOWN; else val+=mctl->bias(); if (val!=MusECore::CTRL_VAL_UNKNOWN) { record_controller_change_and_maybe_send(ctrl_at_tick, ctrl_num, val, mt); } else { MusECore::Undo operations; for (MusECore::iPart p = mt->parts()->begin(); p!=mt->parts()->end(); p++) { if (p->second->tick()==0) { for (MusECore::ciEvent ev=p->second->events().begin(); ev!=p->second->events().end(); ev++) { if (ev->second.tick()!=0) break; else if (ev->second.type()==MusECore::Controller && ev->second.dataA()==ctrl_num) { using MusECore::UndoOp; operations.push_back(UndoOp(UndoOp::DeleteEvent, ev->second, p->second, false, false)); break; } } } } MusEGlobal::song->applyOperationGroup(operations); } } editTrack = nullptr; } editMode = false; editJustFinished=true; if(ctrl_edit->isVisible()) { ctrl_edit->blockSignals(true); ctrl_edit->hide(); ctrl_edit->blockSignals(false); } setFocus(); } //--------------------------------------------------------- // adjustScrollbar //--------------------------------------------------------- void TList::adjustScrollbar() { int h = 0; MusECore::TrackList* l = MusEGlobal::song->tracks(); for (MusECore::iTrack it = l->begin(); it != l->end(); ++it) h += (*it)->height(); _scroll->setMaximum(h +30); redraw(); } //--------------------------------------------------------- // y2Track //--------------------------------------------------------- MusECore::Track* TList::y2Track(int y) const { MusECore::TrackList* l = MusEGlobal::song->tracks(); int ty = 0; for (MusECore::iTrack it = l->begin(); it != l->end(); ++it) { int h = (*it)->height(); if (y >= ty && y < ty + h) return *it; ty += h; } return 0; } void TList::muteSelectedTracksSlot() { bool stateDefined=false; bool setTo; MusECore::PendingOperationList operations; MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) { if ((*t)->selected()){ if (!stateDefined) { setTo = !(*t)->isMute(); stateDefined = true; } operations.add(MusECore::PendingOperationItem((*t), setTo, MusECore::PendingOperationItem::SetTrackMute)); } } MusEGlobal::audio->msgExecutePendingOperations(operations, true); update(); } void TList::soloSelectedTracksSlot() { bool stateDefined=false; bool setTo; MusECore::PendingOperationList operations; MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) { if ((*t)->selected()){ if (!stateDefined) { setTo = !(*t)->soloMode(); stateDefined = true; } operations.add(MusECore::PendingOperationItem((*t), setTo, MusECore::PendingOperationItem::SetTrackSolo)); } } MusEGlobal::audio->msgExecutePendingOperations(operations, true); update(); } void TList::incrementController(MusECore::Track* t, int controllerType, int incrementValue) { MusECore::MidiTrack* midiTrack = static_cast(t); const int channel = midiTrack->outChannel(); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[midiTrack->outPort()]; MusECore::MidiCtrlValListList* mcvll = mp->controller(); MusECore::ciMidiCtrlValList imcvl = mcvll->find(channel, controllerType); MusECore::MidiCtrlValList* mcvl = imcvl->second; MusECore::MidiController* mc = mp->midiController(controllerType, channel, false); int value = mcvl->lastValidHWVal(); int max = 127; int min = 0; int bias = 0; if(mc) { max = mc->maxVal(); min = mc->minVal(); bias = mc->bias(); if (value == MusECore::CTRL_VAL_UNKNOWN) { value = mc->initVal() + bias; //printf("Controller not yet set, resetting to default (%d)\n", value); } } // checking ranges seems to need bias removed value -= bias; value += incrementValue; if (value > max) value = max; if (value < min) value = min; value += bias; mp->putControllerValue(midiTrack->outPort(), channel, controllerType, value, false); } void TList::volumeSelectedTracksSlot(int incrementValue) { MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for (auto t: *tracks) { if (!t->selected()) continue; if (t->type() == MusECore::Track::MIDI || t->type() == MusECore::Track::DRUM) { incrementController(t, MusECore::CTRL_VOLUME, incrementValue*2); } else { MusECore::AudioTrack* at = static_cast(t); float vol = at->volume(); float dbVol = muse_val2dbr(vol); float newVolume = dbVol + float(incrementValue)/2; if (newVolume < MusEGlobal::config.minSlider) newVolume = MusEGlobal::config.minSlider; if (newVolume > 10.0) newVolume = 10.0; at->setVolume(muse_db2val(newVolume)); } } } void TList::panSelectedTracksSlot(int incrementValue) { MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for (auto t : *tracks) { if (!t->selected()) continue; if (t->type() == MusECore::Track::MIDI || t->type() == MusECore::Track::DRUM) { incrementController(t, MusECore::CTRL_PANPOT, incrementValue); } else { MusECore::AudioTrack* at = static_cast(t); float newPan = at->pan() + 0.01 * incrementValue; if (newPan < -1.0) newPan = -1.0; if (newPan > 1.0) newPan = 1.0; at->setPan(newPan); } } } void TList::editTrackNameSlot() { MusECore::TrackList* tracks = MusEGlobal::song->tracks(); if (tracks->countSelected() == 1) { for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) if ((*t)->selected()){ editTrackName(*t); break; } } } void TList::editTrackName(MusECore::Track *t) { int colx = header->sectionPosition(COL_NAME); int colw = header->sectionSize(COL_NAME); int coly = t->y() - ypos; int colh = t->height(); editTrack = t; if (editor == nullptr) { editor = new QLineEdit(this); editor->setFrame(false); connect(editor, SIGNAL(editingFinished()), SLOT(returnPressed())); } editor->setText(editTrack->name()); editor->selectAll(); editor->setGeometry(colx, coly, colw, colh); editMode = true; editor->show(); editor->setFocus(); } void TList::setTrackChannel(MusECore::Track *track, bool isDelta, int channel, int delta, bool doAllTracks) { MusECore::Undo operations; if(track->isMidiTrack()) { MusECore::MidiTrack* mt = static_cast(track); if(!doAllTracks && !track->selected()) { if(isDelta) { channel = mt->outChannel() + delta; if(channel >= MusECore::MUSE_MIDI_CHANNELS) channel = 0; else if(channel < 0) channel = MusECore::MUSE_MIDI_CHANNELS - 1; } channel = qMin(channel, MusECore::MUSE_MIDI_CHANNELS - 1); channel = qMax(channel, 0); if(channel != mt->outChannel()) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, mt, mt->outChannel(), channel)); } else { for (const auto& t : *MusEGlobal::song->midis()) { if (doAllTracks && (t->type() != mt->type())) continue; // only tracks of the same type if(isDelta) { channel = t->outChannel() + delta; if(channel >= MusECore::MUSE_MIDI_CHANNELS) channel = 0; else if (channel < 0) channel = MusECore::MUSE_MIDI_CHANNELS - 1; } channel = qMin(channel, MusECore::MUSE_MIDI_CHANNELS - 1); channel = qMax(channel, 0); if(channel != t->outChannel() && (doAllTracks || t->selected())) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, t, t->outChannel(), channel)); } } if(!operations.empty()) MusEGlobal::song->applyOperationGroup(operations); } else { if(track->type() != MusECore::Track::AUDIO_SOFTSYNTH) { if(!doAllTracks && !track->selected()) { if(isDelta) { channel = track->channels() + delta; if(channel > MusECore::MAX_CHANNELS) channel = 1; else if(channel < 1) channel = MusECore::MAX_CHANNELS; } channel = qMin(channel, MusECore::MAX_CHANNELS); channel = qMax(channel, 1); if(channel != track->channels()) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, track, track->channels(), channel)); } else { for(const auto& t : *MusEGlobal::song->tracks()) { if(t->isMidiTrack()) continue; if (doAllTracks && (t->type() != track->type())) continue; // only tracks of the same type if(isDelta) { channel = t->channels() + delta; if(channel > MusECore::MAX_CHANNELS) channel = 1; else if(channel < 1) channel = MusECore::MAX_CHANNELS; } channel = qMin(channel, MusECore::MAX_CHANNELS); channel = qMax(channel, 1); if(channel != t->channels() && (doAllTracks || t->selected())) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyTrackChannel, t, t->channels(), channel)); } } if(!operations.empty()) MusEGlobal::song->applyOperationGroup(operations); } } } //--------------------------------------------------------- // viewMouseDoubleClickEvent //--------------------------------------------------------- void TList::mouseDoubleClickEvent(QMouseEvent* ev) { if((editor && (editor->isVisible() || editor->hasFocus())) || (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) || (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus()))) { ev->accept(); return; } int button = ev->button(); if(button != Qt::LeftButton) { ev->accept(); return; } int x = ev->x(); int section = header->logicalIndexAt(x); if (section == -1) { ev->accept(); return; } MusECore::Track* t = y2Track(ev->y() + ypos); if(t == nullptr) { ev->accept(); return; } const bool shift = ((QInputEvent*)ev)->modifiers() & Qt::ShiftModifier; int colx = header->sectionPosition(section); int colw = header->sectionSize(section); int coly = t->y() - ypos; int colh = t->height(); if (t) { if (section == COL_NAME) { editTrackName(t); } else if (section == COL_OPORT || section == COL_CLASS) { if (t->isSynthTrack() || t->isMidiTrack()) openSynthGui(t); } else if (section == COL_TRACK_IDX) { if (button == Qt::LeftButton) { if (shift) { // Select all tracks of the same type MusEGlobal::song->selectAllTracks(false); MusECore::TrackList* all_tl = MusEGlobal::song->tracks(); for (const auto tit : *all_tl) { if (tit->type() == t->type()) tit->setSelected(true); } } else { MusEGlobal::song->selectAllTracks(true); } } MusEGlobal::song->update(SC_TRACK_SELECTION); } else if (section == COL_OCHANNEL) { // Enabled for audio tracks. And synth channels cannot be changed ATM. // Default to track port if -1 and track channel if -1. if(t->type() == MusECore::Track::AUDIO_SOFTSYNTH) { ev->accept(); return; } // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. editTrack=t; if (!chan_edit) { chan_edit=new QSpinBox(this); chan_edit->setFrame(false); chan_edit->setMinimum(1); connect(chan_edit, SIGNAL(editingFinished()), SLOT(chanValueFinished())); } if (t->isMidiTrack()) { chan_edit->setMaximum(MusECore::MUSE_MIDI_CHANNELS); chan_edit->setValue(((MusECore::MidiTrack*)editTrack)->outChannel()+1); } else // if(t->type() != MusECore::Track::AUDIO_SOFTSYNTH) { chan_edit->setMaximum(MusECore::MAX_CHANNELS); chan_edit->setValue(((MusECore::AudioTrack*)editTrack)->channels()); } int w=colw; if (w < chan_edit->sizeHint().width()) w=chan_edit->sizeHint().width(); chan_edit->setGeometry(colx, coly, w, colh); chan_edit->selectAll(); editMode = true; chan_edit->show(); chan_edit->setFocus(); } else if (section >= COL_CUSTOM_MIDICTRL_OFFSET) { if (t->isMidiTrack()) { editTrack=t; ctrl_num=Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].ctrl; MusECore::MidiTrack* mt=(MusECore::MidiTrack*)t; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; const int chan = mt->outChannel(); MusECore::MidiController* mctl = mp->midiController(ctrl_num, chan); if (ctrl_num!=MusECore::CTRL_PROGRAM) { if (Arranger::custom_columns[section-COL_CUSTOM_MIDICTRL_OFFSET].affected_pos == Arranger::custom_col_t::AFFECT_BEGIN) ctrl_at_tick=0; else ctrl_at_tick=MusEGlobal::song->cpos(); if (ctrl_edit==nullptr) { ctrl_edit=new QSpinBox(this); ctrl_edit->setSpecialValueText(tr("off")); connect(ctrl_edit, SIGNAL(editingFinished()), SLOT(ctrlValueFinished())); } ctrl_edit->setMinimum(mctl->minVal()-1); // -1 because of the specialValueText ctrl_edit->setMaximum(mctl->maxVal()); ctrl_edit->setValue(((MusECore::MidiTrack*)editTrack)->getControllerChangeAtTick(0,ctrl_num)-mctl->bias()); int w=colw; if (w < ctrl_edit->sizeHint().width()) w=ctrl_edit->sizeHint().width(); ctrl_edit->setGeometry(colx, coly, w, colh); editMode = true; ctrl_edit->show(); ctrl_edit->setFocus(); } } } } ev->accept(); } //--------------------------------------------------------- // synthGUI context menu //--------------------------------------------------------- void TList::showMidiClassPopupMenu(MusECore::Track* t, int x, int y) { if (t->type() == MusECore::Track::AUDIO_SOFTSYNTH) { MusECore::SynthI* synth = static_cast(t); PopupMenu* p = new PopupMenu; QAction* cact = p->addAction(*MusEGui::ankerSVGIcon, tr("MIDI Ports/Soft Synths...")); p->addSeparator(); if(!synth->synth()) p->addAction(tr("SYNTH IS UNAVAILABLE!")); QAction* gact = p->addAction(tr("Show Generic Synth GUI")); gact->setCheckable(true); gact->setEnabled(synth->hasGui()); gact->setChecked(synth->guiVisible()); QAction* nact = p->addAction(tr("Show Native Synth GUI")); nact->setCheckable(true); nact->setEnabled(synth->hasNativeGui()); nact->setChecked(synth->nativeGuiVisible()); #ifdef LV2_SUPPORT PopupMenu *mSubPresets = nullptr; //show presets submenu for lv2 synths if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::LV2_SYNTH) { mSubPresets = new PopupMenu(tr("Presets")); p->addMenu(mSubPresets); static_cast(synth->sif())->populatePresetsMenu(mSubPresets); } #endif // If it has a gui but we don't have OSC, disable the action. #ifndef OSC_SUPPORT #ifdef DSSI_SUPPORT if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::DSSI_SYNTH) { nact->setChecked(false); nact->setEnabled(false); } #endif #endif QAction* ract = p->exec(mapToGlobal(QPoint(x, y)), nullptr); if (ract == gact) { bool show = !synth->guiVisible(); synth->showGui(show); } else if (ract == nact) { bool show = !synth->nativeGuiVisible(); synth->showNativeGui(show); } else if (ract == cact) { MusEGlobal::muse->configMidiPorts(); } #ifdef LV2_SUPPORT else if (mSubPresets != nullptr && ract != nullptr && ract->data().canConvert()) { static_cast(synth->sif())->applyPreset(ract->data().value()); } #endif delete p; return; } // MIDI tracks if (t->type() != MusECore::Track::MIDI && t->type() != MusECore::Track::DRUM) return; int oPort = static_cast(t)->outPort(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[oPort]; PopupMenu* p = new PopupMenu; QAction *switchact = nullptr, *gact = nullptr, *nact = nullptr; #ifdef LV2_SUPPORT PopupMenu *mSubPresets = nullptr; #endif if (t->type() == MusECore::Track::MIDI) switchact = p->addAction(*drumeditSVGIcon, tr("Convert MIDI to Drum Track")); else switchact = p->addAction(*pianorollSVGIcon, tr("Convert Drum to MIDI Track")); if(port->device() && port->device()->isSynti()) { MusECore::SynthI* synth = static_cast(port->device()); if(synth->synth()) { p->addSeparator(); gact = p->addAction(tr("Show Generic Synth GUI")); gact->setCheckable(true); gact->setEnabled(port->hasGui()); gact->setChecked(port->guiVisible()); nact = p->addAction(tr("Show Native Synth GUI")); nact->setCheckable(true); nact->setEnabled(port->hasNativeGui()); nact->setChecked(port->nativeGuiVisible()); // If it has a gui but we don't have OSC, disable the action. #ifndef OSC_SUPPORT #ifdef DSSI_SUPPORT if(port->device() && port->device()->isSynti()) { MusECore::SynthI* synth = static_cast(port->device()); if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::DSSI_SYNTH) { nact->setChecked(false); nact->setEnabled(false); } } #endif #endif #ifdef LV2_SUPPORT if(port->device() && port->device()->isSynti()) { MusECore::SynthI* synth = static_cast(port->device()); //show presets submenu for lv2 synths if(synth->synth() && synth->synth()->synthType() == MusECore::Synth::LV2_SYNTH) { mSubPresets = new PopupMenu(tr("Presets")); p->addMenu(mSubPresets); static_cast(synth->sif())->populatePresetsMenu(mSubPresets); } } #endif } } QAction* ract = p->exec(mapToGlobal(QPoint(x, y)), nullptr); if (ract == gact) { port->showGui(!port->guiVisible()); } else if (ract == nact) { port->showNativeGui(!port->nativeGuiVisible()); } else if (ract == switchact) { if (!t->selected()) changeTrackToType(t, t->type() == MusECore::Track::MIDI ? MusECore::Track::DRUM : MusECore::Track::MIDI); else { MusECore::Track::TrackType curType = t->type(); for (auto const myt : *MusEGlobal::song->tracks()) { if (myt->selected() && myt->type() == curType) changeTrackToType(myt, myt->type() == MusECore::Track::MIDI ? MusECore::Track::DRUM : MusECore::Track::MIDI); } // track for-loop } } #ifdef LV2_SUPPORT else if (mSubPresets != nullptr && ract != nullptr && ract->data().canConvert()) { if (port->device() && port->device()->isSynti()) { MusECore::SynthI* synth = static_cast(port->device()); static_cast(synth->sif())->applyPreset(ract->data().value()); } } #endif delete p; } //--------------------------------------------------------- // audio output context menu //--------------------------------------------------------- void TList::showAudioOutPopupMenu(MusECore::Track* t, int x, int y) { if (t->type() != MusECore::Track::AUDIO_OUTPUT) return; PopupMenu* p = new PopupMenu; QAction* actTrack = p->addAction(*MusEGui::downmixTrackSVGIcon, tr("Render Downmix to Selected Wave Track")); actTrack->setEnabled(!MusEGlobal::audio->bounce()); QAction* actFile = p->addAction(*MusEGui::downmixOnSVGIcon, tr("Render Downmix to a File...")); actFile->setEnabled(!MusEGlobal::audio->bounce()); QAction* ract = p->exec(mapToGlobal(QPoint(x, y)), nullptr); if (ract == actFile) MusEGlobal::muse->bounceToFile(static_cast(t)); else if (ract == actTrack) MusEGlobal::muse->bounceToTrack(static_cast(t)); delete p; } //--------------------------------------------------------- // tracklistChanged //--------------------------------------------------------- void TList::tracklistChanged() { redraw(); } //--------------------------------------------------------- // keyPressEvent //--------------------------------------------------------- void TList::keyPressEvent(QKeyEvent* e) { if ( e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { e->accept(); return; } if (editMode) { if ( e->key() == Qt::Key_Escape ) { if(editor && editor->isVisible()) { editor->blockSignals(true); editor->hide(); editor->blockSignals(false); } if(chan_edit && chan_edit->isVisible()) { chan_edit->blockSignals(true); chan_edit->hide(); chan_edit->blockSignals(false); } if(ctrl_edit && ctrl_edit->isVisible()) { ctrl_edit->blockSignals(true); ctrl_edit->hide(); ctrl_edit->blockSignals(false); } editTrack = nullptr; editMode = false; setFocus(); return; } return; } else if (!editJustFinished) { emit keyPressExt(e); //redirect keypress events to main app. don't call this when confirming an editor } else editJustFinished=false; emit keyPressExt(e); //redirect keypress events to main app } //--------------------------------------------------------- // moveSelection //--------------------------------------------------------- void TList::moveSelection(int n) { MusECore::TrackList* tracks = MusEGlobal::song->tracks(); int nselect = tracks->countSelected(); if (nselect > 1) { // remove selection for all but the first track MusECore::Track* selTrack = nullptr; for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) { if (selTrack == nullptr) { if ((*t)->selected()) selTrack = *t; } else { if ((*t)->selected()) (*t)->setSelected(false); } } } MusECore::Track* selTrack = nullptr; for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) { MusECore::iTrack s = t; if ((*t)->selected()) { if (n > 0) { // Move down while (n--) { ++t; if (t == tracks->end()) { --t; break; } // skip over hidden tracks if (!(*t)->isVisible()) { n++; continue; } selTrack = *t; break; } } else { // Move up while (n++ != 0) { if (t == tracks->begin()) break; --t; // skip over hidden tracks if (!(*t)->isVisible()) { n--; continue; } selTrack = *t; break; } } if(selTrack) { (*s)->setSelected(false); selTrack->setSelected(true); // if selected track is outside of view, enforce scrolling if (selTrack->y() > this->height()+ypos-20) { emit verticalScrollSetYpos(ypos+selTrack->height()); } else if (selTrack->y() < ypos) { emit verticalScrollSetYpos(selTrack->y()); } // rec enable track if expected MusECore::TrackList recd = getRecEnabledTracks(); if (!MusEGlobal::audio->isRecording() && recd.size() == 1 && MusEGlobal::config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection MusEGlobal::song->setRecordFlag((MusECore::Track*)recd.front(),false); MusEGlobal::song->setRecordFlag((selTrack),true); } if (editTrack && editTrack != selTrack) returnPressed(); redraw(); } break; } } if(selTrack) MusEGlobal::song->update(SC_TRACK_SELECTION); } MusECore::TrackList TList::getRecEnabledTracks() { MusECore::TrackList recEnabled; MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) { if ((*t)->recordFlag()) recEnabled.push_back(*t); } return recEnabled; } //--------------------------------------------------------- // changeAutomation //--------------------------------------------------------- void TList::changeAutomation(QAction* act) { if(!editAutomation || editAutomation->isMidiTrack()) return; if (act->data().toInt() == AUTO_INVALID) return; bool setRead = false; if (act->data().toInt() == AUTO_SHOW_ALL) { MusECore::CtrlListList* cll = static_cast(editAutomation)->controller(); for (auto& it : *cll) { MusECore::CtrlList *cl = it.second; if (!cl->dontShow() && !cl->isVisible() && cl->size() > 0) { cl->setVisible(true); setRead = true; } } } else if (act->data().toInt() == AUTO_HIDE_ALL) { MusECore::CtrlListList* cll = static_cast(editAutomation)->controller(); for (auto& it : *cll) { MusECore::CtrlList *cl = it.second; if (cl->isVisible()) cl->setVisible(false); } } else { int colindex = act->data().toInt() & 0xff; int id = (act->data().toInt() & 0x00ffffff) >> 8; // Is it the midi control action or clear action item? if (colindex == AUTO_CLEAR_MIDI || colindex == AUTO_MIDI_ASSIGN) return; if (colindex < 100) return; // this was meant for changeAutomationColor // one of these days I'll rewrite this so it's understandable // this is just to get it up and running... MusECore::CtrlListList* cll = static_cast(editAutomation)->controller(); for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { MusECore::CtrlList *cl = icll->second; if (id == cl->id()) // got it, change state cl->setVisible(act->isChecked()); } setRead = true; } // if automation is OFF for the track we change it to READ as a convenience // hopefully this confuses users far less than not understanding why the // automation does not do anything. if (setRead && static_cast(editAutomation)->automationType() == MusECore::AUTO_OFF) { MusEGlobal::audio->msgSetTrackAutomationType(static_cast(editAutomation), MusECore::AUTO_READ); if (MusEGlobal::debugMsg) printf("Changing automation from OFF to READ\n"); } MusEGlobal::song->update(SC_TRACK_MODIFIED|SC_AUTOMATION); } //--------------------------------------------------------- // changeAutomation //--------------------------------------------------------- void TList::changeAutomationColor(QAction* act) { if(!editAutomation || editAutomation->isMidiTrack()) return; if(act->data().toInt() == AUTO_INVALID) return; int colindex = act->data().toInt() & 0xff; int id = (act->data().toInt() & 0x00ffffff) >> 8; // Is it the clear automation action item? // (As commented below, we should rewrite this to make it easier to understand..) if (colindex == AUTO_CLEAR_AUTO) { if(QMessageBox::question(MusEGlobal::muse, QString("Muse"), tr("Clear all controller events?"), tr("&Ok"), tr("&Cancel"), QString(), 0, 1 ) == 0) { MusECore::AudioTrack* track = static_cast(editAutomation); MusEGlobal::audio->msgClearControllerEvents(track, id); } } // Is it the clear midi control action item? if(colindex == AUTO_CLEAR_MIDI) { MusECore::AudioTrack* track = static_cast(editAutomation); MusECore::MidiAudioCtrlMap* macp = track->controller()->midiControls(); MusECore::AudioMidiCtrlStructMap amcs; macp->find_audio_ctrl_structs(id, &amcs); if(!amcs.empty()) MusEGlobal::audio->msgIdle(true); // Gain access to structures, and sync with audio for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) macp->erase(*iamcs); if(!amcs.empty()) MusEGlobal::audio->msgIdle(false); // Hm, need to remove the 'clear' item, and the status lines below it. Try this: QActionGroup* midi_actgrp = act->actionGroup(); if(midi_actgrp) { QList act_list = midi_actgrp->actions(); int sz = act_list.size(); for(int i = 0; i < sz; ++i) { QAction* list_act = act_list.at(i); ///midi_actgrp->removeAction(list_act); // list_act has no parent now. ///delete list_act; list_act->setVisible(false); // HACK Cannot delete any actions! Causes crash with our PopupMenu due to recent fixes. } } return; } // Is it the midi control action item? if(colindex == AUTO_MIDI_ASSIGN) { MusECore::AudioTrack* track = static_cast(editAutomation); MusECore::MidiAudioCtrlMap* macm = track->controller()->midiControls(); MusECore::AudioMidiCtrlStructMap amcs; macm->find_audio_ctrl_structs(id, &amcs); int port = -1, chan = 0, ctrl = 0; for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) { macm->hash_values((*iamcs)->first, &port, &chan, &ctrl); break; // Only a single item for now, thanks! } MidiAudioControl* pup = new MidiAudioControl(port, chan, ctrl); if(pup->exec() == QDialog::Accepted) { MusEGlobal::audio->msgIdle(true); // Gain access to structures, and sync with audio // Erase all for now. for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) macm->erase(*iamcs); port = pup->port(); chan = pup->chan(); ctrl = pup->ctrl(); if(port >= 0 && chan >=0 && ctrl >= 0) // Add will replace if found. macm->add_ctrl_struct(port, chan, ctrl, MusECore::MidiAudioCtrlStruct(id)); MusEGlobal::audio->msgIdle(false); } delete pup; return; } if (colindex > 100) return; // this was meant for changeAutomation // one of these days I'll rewrite this so it's understandable // this is just to get it up and running... MusECore::CtrlListList* cll = ((MusECore::AudioTrack*)editAutomation)->controller(); for(MusECore::CtrlListList::iterator icll =cll->begin();icll!=cll->end();++icll) { MusECore::CtrlList *cl = icll->second; if (cl->id() == id) { // got it, change color and enable cl->setColor(collist[colindex]); cl->setVisible(true); } } MusEGlobal::song->update(SC_TRACK_MODIFIED); } //--------------------------------------------------------- // colorMenu //--------------------------------------------------------- PopupMenu* TList::colorMenu(QColor c, int id, QWidget* parent) { PopupMenu * m = new PopupMenu(parent, true); QActionGroup* col_actgrp = new QActionGroup(m); m->addAction(new MusEGui::MenuTitleItem(tr("Change color"), m)); col_actgrp->setExclusive(true); for (int i = 0; i< 6; i++) { QPixmap pix(10,10); QPainter p(&pix); p.fillRect(0,0,10,10,collist[i]); QIcon icon(pix); QAction *act = col_actgrp->addAction(icon,colnames[i]); act->setCheckable(true); if (c == collist[i]) act->setChecked(true); act->setData((id<<8) + i); // Shift 8 bits. Color in the bottom 8 bits. } m->addActions(col_actgrp->actions()); //m->addSeparator(); m->addAction(new MenuTitleItem(tr("Midi control"), m)); if(editAutomation && !editAutomation->isMidiTrack()) { QAction *act = m->addAction(tr("Assign")); act->setCheckable(false); act->setData((id<<8) + AUTO_MIDI_ASSIGN); // Shift 8 bits. Make midi menu the last item at 255. MusECore::AudioTrack* track = static_cast(editAutomation); MusECore::MidiAudioCtrlMap* macm = track->controller()->midiControls(); MusECore::AudioMidiCtrlStructMap amcs; macm->find_audio_ctrl_structs(id, &amcs); // Group only the clear and status items so they can both be easily removed when clear is clicked. if(!amcs.empty()) { QActionGroup* midi_actgrp = new QActionGroup(m); QAction *cact = midi_actgrp->addAction(tr("Clear")); cact->setData((id<<8) + AUTO_CLEAR_MIDI); // Shift 8 bits. Make clear the second-last item at 254 for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) { int port, chan, mctrl; macm->hash_values((*iamcs)->first, &port, &chan, &mctrl); //QString s = QString("Port:%1 Chan:%2 Ctl:%3-%4").arg(port + 1) QString s = QString("Port:%1 Chan:%2 Ctl:%3").arg(port + 1) .arg(chan + 1) //.arg((mctrl >> 8) & 0xff) //.arg(mctrl & 0xff); .arg(MusECore::midiCtrlName(mctrl, true)); QAction *mact = midi_actgrp->addAction(s); mact->setEnabled(false); mact->setData(AUTO_INVALID); // Not used } m->addActions(midi_actgrp->actions()); } } m->addAction(new MenuTitleItem(tr("Other"), m)); QAction *act = m->addAction(tr("Clear automation")); act->setCheckable(false); act->setData((id<<8) + AUTO_CLEAR_AUTO); // Shift 8 bits. Make clear menu item 253 connect(m, SIGNAL(triggered(QAction*)), SLOT(changeAutomationColor(QAction*))); return m; } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void TList::mousePressEvent(QMouseEvent* ev) { if((editor && (editor->isVisible() || editor->hasFocus())) || (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) || (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus()))) { ev->accept(); return; } const int x = ev->x(); const int y = ev->y(); const int button = ev->button(); const bool ctrl = ((QInputEvent*)ev)->modifiers() & Qt::ControlModifier; const bool shift = ((QInputEvent*)ev)->modifiers() & Qt::ShiftModifier; MusECore::Track* t = y2Track(y + ypos); TrackColumn col = TrackColumn(header->logicalIndexAt(x)); if (t == nullptr) { if (button == Qt::RightButton) { // Show the menu QAction* act = addTrackMenu->exec(ev->globalPos(), nullptr); // Valid click? if(act) { t = MusEGlobal::song->addNewTrack(act); // Add at end of list. if(t && t->isVisible()) { MusEGlobal::song->selectAllTracks(false); t->setSelected(true); MusEGlobal::song->update(SC_TRACK_SELECTION); adjustScrollbar(); } } } return; } MusECore::TrackList* tracks = MusEGlobal::song->tracks(); dragYoff = y - (t->y() - ypos); startY = y; if (resizeFlag) { mode = RESIZE; int y = ev->y(); int ty = -ypos; sTrack = 0; for (MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it, ++sTrack) { int h = (*it)->height(); ty += h; if (y >= (ty-2)) { if ( (*it) == tracks->back() && y > ty ) { // DELETETHIS, only retain if(foo) break;? //printf("tracks->back() && y > ty\n"); } else if ( y > (ty+2) ) { //printf(" y > (ty+2) \n"); } else { //printf("ogga ogga\n"); break; } //&& y < (ty)) DELETETHIS // break; } } return; } mode = NORMAL; if (button == Qt::LeftButton && col != COL_INPUT_MONITOR && col != COL_RECORD && col != COL_MUTE && col != COL_SOLO) { mode = START_DRAG; // Allow a track drag to start. if (ctrl) { if (tracks->countSelected() == 1 && tracks->currentSelection() == t) return; t->setSelected(!t->selected()); } else if (shift) { if (tracks->countSelected() == 1 && tracks->currentSelection() == t) return; else if (tracks->countSelected() == 0) t->setSelected(true); else { int indexRange = 0, i1 = -1, i2 = -1; int indexClicked = tracks->index(t); for (MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { if ( (*it)->selected() ) { indexRange = tracks->index(*it); if (indexRange < indexClicked) { i1 = indexRange; i2 = indexClicked; break; } } } if (i1 == -1) { i1 = indexClicked; i2 = indexRange; } MusEGlobal::song->selectAllTracks(false); for (MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { if (tracks->index(*it) >= i1 && tracks->index(*it) <= i2 && (*it) != t) (*it)->setSelected(true); } t->setSelected(true); } } else { MusEGlobal::song->selectAllTracks(false); t->setSelected(true); // rec enable track if expected MusECore::TrackList recd = getRecEnabledTracks(); if (!MusEGlobal::audio->isRecording() && recd.size() == 1 && MusEGlobal::config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection MusEGlobal::song->setRecordFlag((MusECore::Track*)recd.front(),false); MusEGlobal::song->setRecordFlag(t,true); } } MusEGlobal::song->update(SC_TRACK_SELECTION); } else { switch (col) { case COL_TRACK_IDX: break; case COL_CLEF: { if (button == Qt::RightButton && t->isMidiTrack() && t->type() == MusECore::Track::MIDI) { QMenu* p = new QMenu; p->addAction(tr("Treble clef"))->setData(0); p->addAction(tr("Bass clef"))->setData(1); p->addAction(tr("Grand Staff"))->setData(2); // Show the menu QAction* act = p->exec(ev->globalPos(), nullptr); if (act) { switch (act->data().toInt()) { case 0: ((MusECore::MidiTrack*)t)->setClef(trebleClef); break; case 1: ((MusECore::MidiTrack*)t)->setClef(bassClef); break; case 2: ((MusECore::MidiTrack*)t)->setClef(grandStaff); break; default: break; } } delete p; } break; } case COL_AUTOMATION: { if (button == Qt::RightButton && !t->isMidiTrack()) { editAutomation = t; PopupMenu* p = new PopupMenu(true); p->disconnect(); p->clear(); p->setTitle(tr("Viewable automation")); MusECore::CtrlListList* cll = static_cast(t)->controller(); QAction* act = nullptr; int last_rackpos = -1; bool internalHeaderDone = false; bool synthHeaderDone = false; p->addAction(new MusEGui::MenuTitleItem(tr("Automation Display"), p)); act = p->addAction(*dummySVGIcon, tr("Show All with Events")); act->setData(AUTO_SHOW_ALL); act = p->addAction(tr("Hide All")); act->setData(AUTO_HIDE_ALL); QList tmpList; for (const auto& icll : *cll) { MusECore::CtrlList *cl = icll.second; if (cl->dontShow()) continue; int ctrl = cl->id(); if(ctrl < AC_PLUGIN_CTL_BASE) { if(!internalHeaderDone) { p->addAction(new MusEGui::MenuTitleItem(tr("Internal"), p)); internalHeaderDone = true; } addAutoMenuAction(p, cl); } else { if (ctrl < static_cast(MusECore::genACnum(MusECore::MAX_PLUGINS, 0))) // The beginning of the special dssi synth controller block. { int rackpos = (ctrl - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; if (rackpos < MusECore::PipelineDepth) { if(rackpos != last_rackpos) { outputAutoMenuSorted(p, tmpList); QString s = static_cast(t)->efxPipe() ? static_cast(t)->efxPipe()->name(rackpos) : QString(); p->addAction(new MusEGui::MenuTitleItem(s, p)); last_rackpos = rackpos; } } tmpList.append(cl); } else { if (t->type() == MusECore::Track::AUDIO_SOFTSYNTH) { if(!synthHeaderDone) { outputAutoMenuSorted(p, tmpList); p->addAction(new MusEGui::MenuTitleItem(tr("Synth"), p)); synthHeaderDone = true; } tmpList.append(cl); } } } } outputAutoMenuSorted(p, tmpList); connect(p, SIGNAL(triggered(QAction*)), SLOT(changeAutomation(QAction*))); p->exec(QCursor::pos()); delete p; } break; } case COL_INPUT_MONITOR: { if(!t->canRecordMonitor()) break; const bool val = !(t->recMonitor()); if (button == Qt::LeftButton) { // apply to selected tracks if (t->selected() && tracks->countSelected() > 1) { MusECore::Undo operations; MusECore::TrackList* tl = MusEGlobal::song->tracks(); for (const auto tit : *tl) { if (tit->selected() && tit->canRecordMonitor()) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackRecMonitor, tit, val)); } if (!operations.empty()) MusEGlobal::song->applyOperationGroup(operations); } else { // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(t, val, MusECore::PendingOperationItem::SetTrackRecMonitor)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } } else if (button == Qt::RightButton) { // enable or disable ALL tracks of this type // This is a major operation not easily manually undoable. Let's make it undoable. MusECore::Undo operations; MusECore::TrackList* all_tl = MusEGlobal::song->tracks(); foreach (MusECore::Track *other_t, *all_tl) { if(other_t->type() != t->type()) continue; operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackRecMonitor, other_t, val)); } if(!operations.empty()) { MusEGlobal::song->applyOperationGroup(operations); // Not required if undoable. //MusEGlobal::song->update(SC_TRACK_REC_MONITOR); } } break; } case COL_RECORD: { if (!t->canRecord()) break; bool val = !(t->recordFlag()); if (button == Qt::LeftButton) { if (t->type() == MusECore::Track::AUDIO_OUTPUT) { if (val && !t->recordFlag()) MusEGlobal::muse->bounceToFile((MusECore::AudioOutput*)t); break; } // apply to selected tracks if (t->selected() && tracks->countSelected() > 1) { MusECore::Undo operations; MusECore::TrackList* tl = MusEGlobal::song->tracks(); for (const auto tit : *tl) { if (tit->selected() && tit->canRecord() && tit->type() != MusECore::Track::AUDIO_OUTPUT) MusEGlobal::song->setRecordFlag(tit, val, &operations); } if (!operations.empty()) MusEGlobal::song->applyOperationGroup(operations); } else { // This is a minor operation easily manually undoable. Let's not clog the undo list with it. if (!t->setRecordFlag1(val)) break; MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(t, val, MusECore::PendingOperationItem::SetTrackRecord)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } } else if (button == Qt::RightButton) { // enable or disable ALL tracks of this type // This is a major operation not easily manually undoable. Let's make it undoable. MusECore::Undo operations; if (!t->isMidiTrack()) { if (t->type() == MusECore::Track::AUDIO_OUTPUT) { return; } MusECore::WaveTrackList* wtl = MusEGlobal::song->waves(); foreach (MusECore::WaveTrack *wt, *wtl) { MusEGlobal::song->setRecordFlag(wt, val, &operations); } } else { MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); foreach (MusECore::MidiTrack *mt, *mtl) { if (mt->type() == t->type()) MusEGlobal::song->setRecordFlag(mt, val, &operations); } } if (!operations.empty()) { MusEGlobal::song->applyOperationGroup(operations); // Not required if undoable. //MusEGlobal::song->update(SC_RECFLAG | SC_TRACK_REC_MONITOR); } } break; } case COL_NONE: mode = START_DRAG; break; case COL_CLASS: { if (button == Qt::RightButton) { if (t->isMidiTrack() || t->isSynthTrack()) showMidiClassPopupMenu(t, x, t->y() - ypos); else if (t->type() == MusECore::Track::AUDIO_OUTPUT) showAudioOutPopupMenu(t, x, t->y() - ypos); } break; } case COL_OPORT: { if (button == Qt::RightButton) { if (t->isSynthTrack()) showMidiClassPopupMenu(t, x, t->y() - ypos); else if (t->isMidiTrack()) MusEGui::midiPortsPopupMenu(t, x, t->y() - ypos, ctrl, this); } break; } case COL_MUTE: { bool turnOff = (button == Qt::RightButton) || shift; bool state = turnOff ? !t->off() : !t->mute(); if (((t->selected() && tracks->countSelected() > 1) || ctrl) && t->type() != MusECore::Track::AUDIO_OUTPUT) { // These are major operations not easily manually undoable. Let's make them undoable. MusECore::Undo operations; if (t->selected() && tracks->countSelected() > 1) // toggle all selected tracks { for (const auto it : *tracks) { if (it->selected() && it->type() != MusECore::Track::AUDIO_OUTPUT) setMute(operations, it, turnOff, state); } } else if (ctrl) // toggle ALL tracks { for (const auto it : *tracks) { if (it->type() != MusECore::Track::AUDIO_OUTPUT) setMute(operations, it, turnOff, state); } } if (!operations.empty()) { MusEGlobal::song->applyOperationGroup(operations); // Not required if undoable. //MusEGlobal::song->update(SC_MUTE); } } else { // toggle the clicked track // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; if (turnOff) operations.add(MusECore::PendingOperationItem(t, !t->off(), MusECore::PendingOperationItem::SetTrackOff)); else if (t->off()) operations.add(MusECore::PendingOperationItem(t, false, MusECore::PendingOperationItem::SetTrackOff)); else operations.add(MusECore::PendingOperationItem(t, !t->mute(), MusECore::PendingOperationItem::SetTrackMute)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } break; } case COL_SOLO: { bool state = !t->solo(); if (((t->selected() && tracks->countSelected() > 1) || ctrl) && t->type() != MusECore::Track::AUDIO_OUTPUT) { // These are major operations not easily manually undoable. Let's make them undoable. MusECore::Undo operations; if (t->selected() && tracks->countSelected() > 1) // toggle all selected tracks { for (const auto it : *tracks) { if (it->selected() && it->type() != MusECore::Track::AUDIO_OUTPUT) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackSolo, it, state)); } } else if (ctrl) // toggle ALL tracks { for (const auto it : *tracks) { if (it->type() != MusECore::Track::AUDIO_OUTPUT) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackSolo, it, state)); } } if(!operations.empty()) { MusEGlobal::song->applyOperationGroup(operations); // Not required if undoable. //MusEGlobal::song->update(SC_SOLO); } } else // toggle the clicked track { // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(t, state, MusECore::PendingOperationItem::SetTrackSolo)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } break; } case COL_NAME: { if (button == Qt::RightButton) { mode = NORMAL; QMenu* p = new QMenu; // Leave room for normal track IDs - base these at AUDIO_SOFTSYNTH. int selCnt = MusEGlobal::song->countSelectedTracks(); QAction *a; a = p->addAction(*duplSelTrackSVGIcon, tr("Duplicate Track")); a->setData(1005); a = p->addAction(*minusSVGIcon, tr("Delete Track")); a->setData(1001); if (selCnt > 1){ p->addSeparator(); a = p->addAction(*duplSelTracksSVGIcon, tr("Duplicate Selected")); a->setData(1004); a->setShortcut(shortcuts[SHRT_DUPLICATE_TRACK].key); a = p->addAction(*delSelTracksSVGIcon, tr("Delete Selected")); a->setData(1003); } if (selCnt > 0) { p->addSeparator(); a = p->addAction(tr("Move Selected Up")); a->setData(1006); a->setShortcut(shortcuts[SHRT_MOVEUP_TRACK].key); a = p->addAction(tr("Move Selected Down")); a->setData(1007); a->setShortcut(shortcuts[SHRT_MOVEDOWN_TRACK].key); a = p->addAction(tr("Move Selected to Top")); a->setData(1008); a->setShortcut(shortcuts[SHRT_MOVETOP_TRACK].key); a = p->addAction(*dummySVGIcon, tr("Move Selected to Bottom")); a->setData(1009); a->setShortcut(shortcuts[SHRT_MOVEBOTTOM_TRACK].key); } p->addSeparator(); a = p->addAction(tr("Track Comment...")); a->setData(1002); p->addSeparator(); a = p->addAction(tr("Set Track Color...")); a->setData(1020); a = p->addAction(tr("Reset Track Color to Default")); a->setData(1021); p->addSeparator(); if (t->type()==MusECore::Track::DRUM) { a=p->addAction(tr("Save Track's Drumlist")); a->setData(1010); a->setEnabled(!static_cast(t)->workingDrumMap()->empty()); a=p->addAction(tr("Load Track's Drumlist")); a->setData(1012); a=p->addAction(tr("Reset Track's Drumlist")); a->setData(1013); a->setEnabled(!static_cast(t)->workingDrumMap()->empty()); a=p->addAction(tr("Reset Track's Drumlist Ordering")); a->setData(1016); a->setEnabled(!((MusECore::MidiTrack*)t)->drummap_ordering_tied_to_patch()); a=p->addAction(tr("Copy Track's Drumlist to All Selected Tracks")); a->setData(1014); a->setEnabled(!static_cast(t)->workingDrumMap()->empty()); // 1016 is occupied. p->addSeparator(); } insertTrackMenu->setTitle(tr("Insert Track")); p->addMenu(insertTrackMenu); QAction* act = p->exec(ev->globalPos(), nullptr); if (act) { //fprintf(stderr, "TList::mousePressEvent act:%p\n", act); int n = act->data().toInt(); if(n >= 1000 && n < MENU_ADD_SYNTH_ID_BASE) { //fprintf(stderr, " n:%d\n", n); switch (n) { case 1001: // delete track MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(t), t)); break; case 1003: // delete track(s) MusEGlobal::audio->msgRemoveTracks(); break; case 1002: // show track comment { TrackComment* tc = new TrackComment(t, nullptr); tc->show(); } break; case 1004: MusEGlobal::song->duplicateTracks(); break; case 1005: MusEGlobal::song->duplicateTracks(t); break; case 1006: moveSelectedTracks(true, false); break; case 1007: moveSelectedTracks(false, false); break; case 1008: moveSelectedTracks(true, true); break; case 1009: moveSelectedTracks(false, true); break; case 1020: { QColor c = QColorDialog::getColor(t->color()); if (c.isValid()) { if (t->selected()) for (const auto& it : *tracks) { if (it->selected()) it->setColor(c); } else t->setColor(c); MusEGlobal::song->update(SC_TRACK_MODIFIED); } } break; case 1021: { if (t->selected()) for (const auto& it : *tracks) { if (it->selected()) it->resetColor(); } else t->resetColor(); MusEGlobal::song->update(SC_TRACK_MODIFIED); } break; case 1010: saveTrackDrummap((MusECore::MidiTrack*)t, true); break; case 1012: loadTrackDrummap((MusECore::MidiTrack*)t); break; case 1013: if (QMessageBox::warning(this, tr("Drum map"), tr("Reset the track's drum map with instrument defaults?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok) { // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the // original lists will be deleted, in the operation following. MusECore::PendingOperationList operations; // Completely blank replacement list. MusECore::WorkingDrumMapPatchList* new_wdmpl = new MusECore::WorkingDrumMapPatchList(); MusECore::DrumMapTrackPatchReplaceOperation* dmop = new MusECore::DrumMapTrackPatchReplaceOperation; dmop->_isInstrumentMod = false; // Not instrument operation. dmop->_workingItemPatchList = new_wdmpl; dmop->_track = static_cast(t); operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } break; case 1016: if (QMessageBox::warning(this, tr("Drum map"), tr("Reset the track's drum map ordering?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok) { ((MusECore::MidiTrack*)t)->set_drummap_ordering_tied_to_patch(true); MusEGlobal::song->update(SC_DRUMMAP); } break; case 1014: copyTrackDrummap((MusECore::MidiTrack*)t, true); break; default: printf("action %d\n", n); break; } } else { t = MusEGlobal::song->addNewTrack(act, t); // Let addNewTrack handle it. Insert before clicked-on track 't'. //fprintf(stderr, " addNewTrack: track:%p\n", t); if(t) { MusEGlobal::song->selectAllTracks(false); t->setSelected(true); MusEGlobal::song->update(SC_TRACK_SELECTION); adjustScrollbar(); } } } delete p; } break; } // case COL_TIMELOCK: // { // if(!t->isMidiTrack()) // { // mode = START_DRAG; // Allow a track drag to start. // break; // } // t->setLocked(!t->locked()); // break; // } case COL_OCHANNEL: { int delta = 0; if (button == Qt::MidButton || (button == Qt::RightButton && shift)) delta = -1; else if (button == Qt::RightButton) delta = 1; setTrackChannel(t, true, 0, delta, ctrl); break; } default: { mode = START_DRAG; if (col>=COL_CUSTOM_MIDICTRL_OFFSET && t->isMidiTrack()) { if (Arranger::custom_columns[col-COL_CUSTOM_MIDICTRL_OFFSET].affected_pos == Arranger::custom_col_t::AFFECT_BEGIN) ctrl_at_tick=0; else ctrl_at_tick=MusEGlobal::song->cpos(); int delta = 0; if (button == Qt::RightButton) delta = 1; else if (button == Qt::MidButton) delta = -1; if (delta!=0) { MusECore::MidiTrack* mt = dynamic_cast(t); if (mt == nullptr) break; int ctrl_num = Arranger::custom_columns[col-COL_CUSTOM_MIDICTRL_OFFSET].ctrl; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; const int chan = mt->outChannel(); MusECore::MidiController* mctl = mp->midiController(ctrl_num, chan); int minval=mctl->minVal()+mctl->bias(); int maxval=mctl->maxVal()+mctl->bias(); int val = mt->getControllerChangeAtTick(0,ctrl_num); int oldval=val; if (ctrl_num!=MusECore::CTRL_PROGRAM) { val += delta; if(val > maxval) val = maxval; if(val < minval-1) // "-1" because of "off" val = minval-1; } else { MusECore::MidiInstrument* instr = mp->instrument(); if (delta>0) val=instr->getNextPatch(mt->outChannel(), val, false); else if (delta<0) val=instr->getPrevPatch(mt->outChannel(), val, false); } if (val != oldval) { if (val!=minval-1) { record_controller_change_and_maybe_send(ctrl_at_tick, ctrl_num, val, mt); } else { MusECore::Undo operations; for (MusECore::iPart p = mt->parts()->begin(); p!=mt->parts()->end(); p++) { if (p->second->tick()==0) { for (MusECore::ciEvent ev=p->second->events().begin(); ev!=p->second->events().end(); ev++) { if (ev->second.tick()!=0) break; else if (ev->second.type()==MusECore::Controller && ev->second.dataA()==ctrl_num) { using MusECore::UndoOp; operations.push_back(UndoOp(UndoOp::DeleteEvent, ev->second, p->second, false, false)); break; } } } } MusEGlobal::song->applyOperationGroup(operations); } } } else // if (delta==0) { ctrl_num=Arranger::custom_columns[col-COL_CUSTOM_MIDICTRL_OFFSET].ctrl; if (ctrl_num==MusECore::CTRL_PROGRAM) { editTrack=t; MusECore::MidiTrack* mt=(MusECore::MidiTrack*)t; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; MusECore::MidiInstrument* instr = mp->instrument(); PopupMenu* pup = new PopupMenu(true); instr->populatePatchPopup(pup, mt->outChannel(), mt->isDrumTrack()); if(pup->actions().count() == 0) { delete pup; return; } connect(pup, SIGNAL(triggered(QAction*)), SLOT(instrPopupActivated(QAction*))); QAction *act = pup->exec(ev->globalPos()); if(act) { int val = act->data().toInt(); if(val != -1) record_controller_change_and_maybe_send(ctrl_at_tick, MusECore::CTRL_PROGRAM, val, mt); } delete pup; } } } } } //end of "switch" } redraw(); } void TList::addAutoMenuAction(PopupMenu* p, const MusECore::CtrlList *cl) { QAction *act = p->addAction(cl->name()); act->setCheckable(true); act->setChecked(cl->isVisible()); QPixmap pix(10, 10); QPainter qp(&pix); qp.fillRect(0,0,10,10, cl->color()); if (cl->size() > 0) { if (cl->color() == Qt::black) qp.fillRect(2, 2, 6, 6, Qt::gray); else qp.fillRect(2, 2, 6, 6, Qt::black); } QIcon icon(pix); act->setIcon(icon); int ctrl = cl->id(); int data = ctrl<<8; // shift 8 bits data += 150; // illegal color > 100 act->setData(data); PopupMenu *m = colorMenu(cl->color(), cl->id(), p); act->setMenu(m); } void TList::outputAutoMenuSorted(PopupMenu* p, QList &tmpList) { if (!tmpList.isEmpty()) { std::sort(tmpList.begin(), tmpList.end(), [](const MusECore::CtrlList* a, const MusECore::CtrlList* b) -> bool { return a->name() < b->name(); }); for (const auto& it : tmpList) addAutoMenuAction(p, it); tmpList.clear(); } } void TList::setMute(MusECore::Undo& operations, MusECore::Track *t, bool turnOff, bool state) { if (turnOff) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackOff, t, state)); else if (t->off()) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackOff, t, false)); else operations.push_back(MusECore::UndoOp(MusECore::UndoOp::SetTrackMute, t, state)); } void TList::loadTrackDrummap(MusECore::MidiTrack* t, const char* fn_) { QString fn; if (fn_==nullptr) fn=MusEGui::getOpenFileName("drummaps", MusEGlobal::drum_map_file_pattern, this, tr("Muse: Load Track's Drum Map"), 0); else fn=QString(fn_); if (fn.isEmpty()) { printf("ERROR: TList::loadTrackDrummap(): empty filename\n"); return; } bool popenFlag; FILE* f = MusEGui::fileOpen(this, fn, QString(".map"), "r", popenFlag, true); if (f == 0) { printf("ERROR: TList::loadTrackDrummap() could not open file %s!\n", fn.toLatin1().data()); return; } MusECore::Xml xml(f); loadTrackDrummapFromXML(t, xml); if (popenFlag) pclose(f); else fclose(f); MusEGlobal::song->update(SC_DRUMMAP); } void TList::loadTrackDrummapFromXML(MusECore::MidiTrack *t, MusECore::Xml &xml) { MusECore::PendingOperationList operations; MusECore::WorkingDrumMapPatchList* wdmpl = 0; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: if(wdmpl) delete wdmpl; return; case MusECore::Xml::TagStart: if (tag == "muse") { } else if (tag == "our_drummap" || // OBSOLETE. Support old files. tag == "drummap" || // OBSOLETE. Support old files. tag == "drumMapPatch") { if(!wdmpl) wdmpl = new MusECore::WorkingDrumMapPatchList(); // false = Do not fill in unused items. wdmpl->read(xml, false); } else xml.unknown("TList::loadTrackDrummap"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "muse") { if(wdmpl) { // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the // original lists will be deleted, in the operation following. MusECore::DrumMapTrackPatchReplaceOperation* dmop = new MusECore::DrumMapTrackPatchReplaceOperation; dmop->_isInstrumentMod = false; // Not instrument operation. dmop->_workingItemPatchList = wdmpl; dmop->_track = t; operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } goto ende; } default: break; } } ende: return; } void TList::saveTrackDrummap(MusECore::MidiTrack* t, bool /*full*/, const char* fn_) { QString fn; if (fn_==nullptr) fn = MusEGui::getSaveFileName(QString("drummaps"), MusEGlobal::drum_map_file_save_pattern, this, tr("MusE: Store Track's Drum Map")); else fn = QString(fn_); if (fn.isEmpty()) return; bool popenFlag; FILE* f = MusEGui::fileOpen(this, fn, QString(".map"), "w", popenFlag, false, true); if (f == 0) return; MusECore::Xml xml(f); xml.header(); xml.tag(0, "muse version=\"1.0\""); t->workingDrumMap()->write(1, xml); xml.tag(0, "/muse"); if (popenFlag) pclose(f); else fclose(f); } void TList::copyTrackDrummap(MusECore::MidiTrack* t, bool /*full*/) { MusECore::PendingOperationList operations; MusECore::WorkingDrumMapPatchList* new_wdmpl; MusECore::WorkingDrumMapPatchList* wdmpl = t->workingDrumMap(); MusECore::MidiTrack* mt; for(MusECore::iMidiTrack it = MusEGlobal::song->midis()->begin(); it != MusEGlobal::song->midis()->end(); ++it) { mt = *it; if(mt == t || !mt->selected() || mt->type() != MusECore::Track::DRUM) continue; // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the // original lists will be deleted, in the operation following. new_wdmpl = new MusECore::WorkingDrumMapPatchList(); *new_wdmpl = *wdmpl; MusECore::DrumMapTrackPatchReplaceOperation* dmop = new MusECore::DrumMapTrackPatchReplaceOperation; dmop->_isInstrumentMod = false; // Not instrument operation. dmop->_workingItemPatchList = new_wdmpl; dmop->_track = mt; operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList)); } if(!operations.empty()) MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // selectTrack //--------------------------------------------------------- void TList::selectTrack(MusECore::Track* tr, bool /*deselect*/) { MusEGlobal::song->selectAllTracks(false); if (tr) { tr->setSelected(true); // rec enable track if expected MusECore::TrackList recd = getRecEnabledTracks(); if (!MusEGlobal::audio->isRecording() && recd.size() == 1 && MusEGlobal::config.moveArmedCheckBox) { // one rec enabled track, move rec enabled with selection MusEGlobal::song->setRecordFlag((MusECore::Track*)recd.front(),false); MusEGlobal::song->setRecordFlag(tr,true); } } // SC_TRACK_SELECTION will cause update anyway, no harm ... update(); MusEGlobal::song->update(SC_TRACK_SELECTION); } //--------------------------------------------------------- // selectTrackAbove //--------------------------------------------------------- void TList::selectTrackAbove() { moveSelection(-1); } //--------------------------------------------------------- // selectTrackBelow //--------------------------------------------------------- void TList::selectTrackBelow() { moveSelection(1); } //--------------------------------------------------------- // mouseMoveEvent //--------------------------------------------------------- void TList::mouseMoveEvent(QMouseEvent* ev) { if((editor && (editor->isVisible() || editor->hasFocus())) || (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) || (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus()))) { ev->accept(); return; } if (ev->buttons() == 0) { // if ((((QInputEvent*)ev)->modifiers() | ev->buttons()) == 0) { int y = ev->y(); int ty = -ypos; MusECore::TrackList* tracks = MusEGlobal::song->tracks(); MusECore::iTrack it; for (it = tracks->begin(); it != tracks->end(); ++it) { int h = (*it)->height(); ty += h; if (y >= (ty-2)) { if ( (*it) == tracks->back() && y >= ty ) { // DELETETHIS and cleanup // outside last track don't change to splitVCursor } else if ( y > (ty+2) ) { //printf(" y > (ty+2) \n"); } else { if (!resizeFlag) { resizeFlag = true; setCursor(QCursor(Qt::SplitVCursor)); MusEGlobal::muse->setStatusBarText(tr("Draw to change the track height. Hold CTRL for all tracks, SHIFT for selected tracks.")); } break; } } } if (it == tracks->end() && resizeFlag) { setCursor(QCursor(Qt::ArrowCursor)); resizeFlag = false; MusEGlobal::muse->clearStatusBarText(); } return; } curY = ev->y(); int delta = curY - startY; switch (mode) { case START_DRAG: if (delta < 0) delta = -delta; if (delta <= 2) break; { MusECore::Track* t = y2Track(startY + ypos); if (t == nullptr) mode = NORMAL; else { mode = DRAG; dragHeight = t->height(); sTrack = MusEGlobal::song->tracks()->index(t); setCursor(QCursor(Qt::SizeVerCursor)); redraw(); } } break; case NORMAL: break; case DRAG: redraw(); break; case RESIZE: { if (sTrack >= 0 && (unsigned) sTrack < MusEGlobal::song->tracks()->size()) { bool shift = ((QInputEvent*)ev)->modifiers() & Qt::SHIFT; bool ctrl = ((QInputEvent*)ev)->modifiers() & Qt::CTRL; if (ctrl | shift) { bool done = false; for (const auto& it : *MusEGlobal::song->tracks()) { if (shift && !it->selected()) continue; int h = it->height() + delta; h = qMax(h, MIN_TRACKHEIGHT); it->setHeight(h); done = true; } if (done) { startY = curY; update(); MusEGlobal::song->update(SC_TRACK_RESIZED); } } else { MusECore::Track* t = MusEGlobal::song->tracks()->index(sTrack); if (t) { int h = t->height() + delta; startY = curY; if (h < MIN_TRACKHEIGHT) h = MIN_TRACKHEIGHT; t->setHeight(h); update(); MusEGlobal::song->update(SC_TRACK_RESIZED); } } } } break; } } void TList::moveSelectedTracks(bool up, bool full) { // Move only visible? But the other track functions also work on hidden tracks // (duplicate, delete...), so this seems more consistent (kybos) MusECore::TrackList *tracks = MusEGlobal::song->tracks(); if (tracks->size() < 2 || tracks->countSelected() == 0 || (!up && tracks->back()->selected()) || (up && tracks->front()->selected())) return; if (MusEGlobal::audio->isPlaying()) { MusEGlobal::muse->setStatusBarText(tr("Operation not available while playing"), 5000); return; } MusECore::TrackList tracksTmp = *tracks; unsigned delta = 1; bool deltaFound = false; if (up) { for (const auto it : *tracks) { if (!it->selected()) continue; if (full & !deltaFound) { delta = static_cast(tracks->index(it)); deltaFound = true; } unsigned sidx = static_cast(tracks->index(it)); unsigned i = delta; while (i--) { std::swap(tracksTmp[sidx], tracksTmp[sidx-1]); sidx--; } } } else { for (auto it = tracks->rbegin(); it != tracks->rend(); it++) { if (!(*it)->selected()) continue; unsigned sidx = static_cast(tracks->index(*it)); if (full && !deltaFound) { delta = static_cast(tracks->size()) - sidx - 1; deltaFound = true; } unsigned i = delta; while (i--) { std::swap(tracksTmp[sidx], tracksTmp[sidx+1]); sidx++; } } } MusEGlobal::song->tracks()->swap(tracksTmp); MusEGlobal::song->update(SC_TRACK_MOVED); } //--------------------------------------------------------- // mouseReleaseEvent //--------------------------------------------------------- void TList::mouseReleaseEvent(QMouseEvent* ev) { if((editor && (editor->isVisible() || editor->hasFocus())) || (chan_edit && (chan_edit->isVisible() || chan_edit->hasFocus())) || (ctrl_edit && (ctrl_edit->isVisible() || ctrl_edit->hasFocus()))) { ev->accept(); return; } if (mode == DRAG) { MusECore::Track* t = y2Track(ev->y() + ypos); if (t) { int dTrack = MusEGlobal::song->tracks()->index(t); if (sTrack >= 0 && dTrack >= 0) // sanity check { const int tracks_sz = MusEGlobal::song->tracks()->size(); if (sTrack < tracks_sz && dTrack < tracks_sz) // sanity check MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::MoveTrack, sTrack, dTrack)); } // inconsistent, better keep the original aux knob order? // - if aux track is moved in the mixer, the aux knobs are also NOT adjusted // - if the drag action is undone, the aux knobs remain in the wrong order // MusECore::TrackList *tracks = MusEGlobal::song->tracks(); // if ( tracks->at(dTrack)->type() == MusECore::Track::AUDIO_AUX) { // MusECore::AuxList auxCopy; // = *MusEGlobal::song->auxs(); // //MusEGlobal::song->auxs()->clear(); // std::vector oldAuxIndex; // for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) { // if ((*t)->type() == MusECore::Track::AUDIO_AUX) { // MusECore::AudioAux *ax = (MusECore::AudioAux*)*t; // auxCopy.push_back(ax); // oldAuxIndex.push_back(MusEGlobal::song->auxs()->index(ax)); // store old index // } // } // // loop through all tracks and set the levels for all tracks // for (MusECore::iTrack t = tracks->begin(); t != tracks->end(); ++t) { // MusECore::AudioTrack *trk = (MusECore::AudioTrack*)*t; // if (!trk->isMidiTrack() && trk->hasAuxSend()) // { // std::vector oldAuxValue; // for (unsigned i = 0 ; i < auxCopy.size(); i++) // oldAuxValue.push_back(trk->auxSend(i)); // for (unsigned i = 0 ; i < auxCopy.size(); i++) // trk->setAuxSend(i, oldAuxValue[oldAuxIndex[i]] ); // } // MusEGlobal::song->auxs()->clear(); // for (MusECore::iAudioAux t = auxCopy.begin(); t != auxCopy.end(); ++t) { // MusEGlobal::song->auxs()->push_back(*t); // } // } // MusEGlobal::song->update(SC_EVERYTHING); // } } } if (mode != NORMAL) { mode = NORMAL; setCursor(QCursor(Qt::ArrowCursor)); redraw(); } if (editTrack && editor && editor->isVisible()) editor->setFocus(); //else // DELETETHIS or add the same for ctrl_edit! //if (editTrack && chan_edit && chan_edit->isVisible()) // p4.0.46 DELETETHIS? // chan_edit->setFocus(); adjustScrollbar(); } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void TList::wheelEvent(QWheelEvent* ev) { emit redirectWheelEvent(ev); } QSize TList::sizeHint() const { return QSize(250, 100); } QSize TList::minimumSizeHint() const { return QSize(100, 100); } //--------------------------------------------------------- // setYPos //--------------------------------------------------------- void TList::setYPos(int y) { int delta = ypos - y; // - -> shift up ypos = y; scroll(0, delta); } void TList::changeTrackToType(MusECore::Track *t, MusECore::Track::TrackType trackType) { // MIDI -> NEW_DRUM or vice versa. added by flo. MusEGlobal::audio->msgIdle(true); t->setType(trackType); MusEGlobal::audio->msgIdle(false); MusEGlobal::song->update(SC_TRACK_MODIFIED); } void TList::instrPopupActivated(QAction* act) { MusECore::MidiTrack* mt = dynamic_cast(editTrack); if(act && mt) { int val = act->data().toInt(); if(val != -1) record_controller_change_and_maybe_send(ctrl_at_tick, MusECore::CTRL_PROGRAM, val, mt); } } void TList::setHeader(Header* h) { header=h; redraw(); } void TList::populateAddTrack() { addTrackMenu = new QMenu; MusEGui::populateAddTrack(addTrackMenu, false, false, true); insertTrackMenu = new QMenu; MusEGui::populateAddTrack(insertTrackMenu, false, true); } } // namespace MusEGui muse-4.0.0/src/muse/arranger/tlist.h000066400000000000000000000147641404105311000173360ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tlist.h,v 1.8.2.5 2008/01/19 13:33:46 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TLIST_H__ #define __TLIST_H__ #include "type_defs.h" #include "track.h" #include #include // Forward declarations: class QKeyEvent; class QLineEdit; class QSpinBox; class QMouseEvent; class QPaintEvent; class QScrollBar; class QWheelEvent; class QMenu; namespace MusECore { class Xml; class Undo; } namespace MusEGui { class Header; class PopupMenu; //--------------------------------------------------------- // TList //--------------------------------------------------------- class TList : public QWidget { Q_OBJECT Q_PROPERTY(bool sel3d READ sel3d WRITE setSel3d) Q_PROPERTY(bool curSelBorder READ curSelBorder WRITE setCurSelBorder) Q_PROPERTY(QColor curSelBorderColor READ curSelBorderColor WRITE setCurSelBorderColor) public: enum TrackColumn { COL_TRACK_IDX = 0, COL_INPUT_MONITOR, COL_RECORD, COL_MUTE, COL_SOLO, COL_CLASS, COL_NAME, COL_OPORT, COL_OCHANNEL, // COL_TIMELOCK, COL_AUTOMATION, COL_CLEF, COL_CUSTOM_MIDICTRL_OFFSET, COL_NONE = -1 }; private: bool _sel3d; bool _curSelBorder; QColor _curSelBorderColor; int ypos; bool editMode; bool editJustFinished; std::map > old_ctrl_hw_states; QPixmap bgPixmap; // background Pixmap bool resizeFlag; // true if resize cursor is shown QMenu *addTrackMenu{}, *insertTrackMenu{}; Header* header; QScrollBar* _scroll; QLineEdit* editor; QSpinBox* chan_edit; QSpinBox* ctrl_edit; int ctrl_num{}; unsigned ctrl_at_tick{}; MusECore::Track* editTrack; MusECore::Track* editAutomation{}; int startY{}; int curY{}; int sTrack{}; int dragHeight{}; int dragYoff{}; enum { NORMAL, START_DRAG, DRAG, RESIZE} mode; virtual void paintEvent(QPaintEvent*) override; virtual void mousePressEvent(QMouseEvent* event) override; virtual void mouseDoubleClickEvent(QMouseEvent*) override; virtual void mouseMoveEvent(QMouseEvent*) override; virtual void mouseReleaseEvent(QMouseEvent*) override; virtual void keyPressEvent(QKeyEvent* e) override; virtual void wheelEvent(QWheelEvent* e) override; virtual QSize sizeHint() const override; virtual QSize minimumSizeHint() const override; void showMidiClassPopupMenu(MusECore::Track*, int x, int y); void showAudioOutPopupMenu(MusECore::Track*, int x, int y); void moveSelection(int n); void adjustScrollbar(); void paint(const QRect& r); void redraw(const QRect& r); MusECore::Track* y2Track(int) const; MusECore::TrackList getRecEnabledTracks(); void setHeaderToolTips(); PopupMenu* colorMenu(QColor c, int id, QWidget* parent); void setMute(MusECore::Undo& operations, MusECore::Track *t, bool turnOff, bool state); void changeTrackToType(MusECore::Track *t, MusECore::Track::TrackType trackType); void editTrackName(MusECore::Track *t); void setTrackChannel(MusECore::Track *t, bool isDelta, int channel, int delta, bool doAllTracks = false); void incrementController(MusECore::Track* t, int controllerType, int incrementValue); void addAutoMenuAction(PopupMenu* p, const MusECore::CtrlList *cl); void outputAutoMenuSorted(PopupMenu* p, QList &); protected: bool event(QEvent *) override; private slots: void maybeUpdateVolatileCustomColumns(); // updates AFFECT_CPOS-columns when and only when the hwState has changed void returnPressed(); void chanValueFinished(); void ctrlValueFinished(); void instrPopupActivated(QAction*); void songChanged(MusECore::SongChangedStruct_t flags); void changeAutomation(QAction*); void changeAutomationColor(QAction*); void loadTrackDrummap(MusECore::MidiTrack*, const char* filename=NULL); void loadTrackDrummapFromXML(MusECore::MidiTrack*t, MusECore::Xml &xml); void saveTrackDrummap(MusECore::MidiTrack*, bool full, const char* filename=NULL); void copyTrackDrummap(MusECore::MidiTrack*, bool full); signals: void keyPressExt(QKeyEvent*); void redirectWheelEvent(QWheelEvent*); void verticalScrollSetYpos(int ypos); public slots: void tracklistChanged(); void setYPos(int); void redraw(); void selectTrack(MusECore::Track*, bool deselect=true); void selectTrackAbove(); void selectTrackBelow(); void editTrackNameSlot(); void muteSelectedTracksSlot(); void soloSelectedTracksSlot(); void volumeSelectedTracksSlot(int); void panSelectedTracksSlot(int); void setHeader(Header*); public: TList(Header*, QWidget* parent, const char* name); void setScroll(QScrollBar* s) { _scroll = s; } MusECore::Track* track() const { return editTrack; } void populateAddTrack(); bool sel3d() const { return _sel3d; } void setSel3d(bool sel3d) { _sel3d = sel3d; } bool curSelBorder() const { return _curSelBorder; } void setCurSelBorder(bool curSelBorder) { _curSelBorder = curSelBorder; } QColor curSelBorderColor() const { return _curSelBorderColor; } void setCurSelBorderColor(const QColor c) { _curSelBorderColor = c; } void moveSelectedTracks(bool up, bool full); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/arranger/trackautomationview.cpp000066400000000000000000000051041404105311000226160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/arranger/trackautomationview.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "trackautomationview.h" #include "muse_math.h" #include // Forwards from header: #include #include "track.h" namespace MusEGui { TrackAutomationView::TrackAutomationView(QWidget *parent, MusECore::Track *t) : QWidget(parent) { printf("created trackautomationview\n"); _t = t; //show(); } void TrackAutomationView::paintEvent(QPaintEvent* e) { QPainter p(this); const QRect &r = e->rect(); // temporary solution, audio track drawing moved here. // best would be to get transparency to work correctly p.setPen(QPen(Qt::black, 2, Qt::SolidLine)); p.setBrush(Qt::gray); p.drawRect(r); int height=r.bottom()-r.top(); if( _t->type()>1) { // audio type double volume = ((AudioTrack*)_t)->volume(); double dbvolume = (20.0*log10(volume)+60) /70.0; // represent volume between 0 and 1 if (dbvolume < 0) dbvolume =0.0; printf("height=%d volume=%f dbvolume=%f\n", height, volume, dbvolume); p.setPen(QPen(Qt::yellow,1,Qt::SolidLine)); p.drawLine(r.left(),r.bottom()-dbvolume*height,r.right(),r.bottom()-dbvolume*height); } printf("paintEvent\n"); } void TrackAutomationView::collectAutomationData() { // here we should collect all automation data that is currently selected for viewing and // prepare an event list that is easy to draw in paintEvent // the main reason being that the event list in it's entirety likely contains too much data to // be processed in the paintEvent. Better to preprocess. // CtrlListList cll =((AudioTrack*)_t)->controller(); // cll.count() } } // namespace MusEGui muse-4.0.0/src/muse/arranger/trackautomationview.h000066400000000000000000000030211404105311000222570ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/arranger/trackautomationview.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef TRACKAUTOMATIONVIEW_H #define TRACKAUTOMATIONVIEW_H #include #include // Forward declarations: class QPaintEvent; namespace MusECore { class Track; } namespace MusEGui { class TrackAutomationView : public QWidget { MusECore::Track *_t; virtual void paintEvent(QPaintEvent *e); std::map automationList; public: TrackAutomationView(QWidget *parent, MusECore::Track *t); MusECore::Track *track() { return _t; } void collectAutomationData(); }; } // namespace MusEGui #endif // TRACKAUTOMATIONVIEW_H muse-4.0.0/src/muse/audio.cpp000066400000000000000000002346351404105311000160330ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: audio.cpp,v 1.59.2.30 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include #include #include "app.h" #include "song.h" #include "mididev.h" #include "alsamidi.h" #include "audioprefetch.h" #include "audio.h" #include "tempo.h" #include "wave.h" #include "midictrl.h" #include "midiseq.h" #include "midi.h" #include "gconfig.h" #include "ticksynth.h" #include "globals.h" #include "large_int.h" // Forwards from header: #include "audiodev.h" #include "sync.h" #include "mididev.h" #include "midiport.h" #include "minstrument.h" #include "track.h" #include "part.h" #include "event.h" #include "mpevent.h" #include "plugin.h" #include "synth.h" #include "undo.h" #include "operations.h" #ifdef _WIN32 #define pipe(fds) _pipe(fds, 4096, _O_BINARY) #endif // Experimental for now - allow other Jack timebase masters to control our midi engine. // TODO: Be friendly to other apps and ask them to be kind to us by using jack_transport_reposition. // It is actually required IF we want the extra position info to show up // in the sync callback, otherwise we get just the frame only. // This information is shared on the server, it is directly passed around. // jack_transport_locate blanks the info from sync until the timebase callback reads // it again right after, from some timebase master. // Sadly not many of us use jack_transport_reposition. So we need to work around it ! //#define _JACK_TIMEBASE_DRIVES_MIDI_ #ifdef _JACK_TIMEBASE_DRIVES_MIDI_ #include "jackaudio.h" #endif // Undefine if and when multiple output routes are added to midi tracks. #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // For debugging metronome and precount output: Uncomment the fprintf section. #define DEBUG_MIDI_METRONOME(dev, format, args...) // fprintf(dev, format, ##args); // For debugging midi timing: Uncomment the fprintf section. #define DEBUG_TRANSPORT_SYNC(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGlobal { MusECore::Audio* audio = NULL; MusECore::AudioDevice* audioDevice = NULL; // current audio device in use extern unsigned int volatile midiExtSyncTicks; } namespace MusECore { void initAudio() { MusEGlobal::audio = new Audio(); } extern uint64_t curTimeUS(); const char* seqMsgList[] = { "SEQM_REVERT_OPERATION_GROUP", "SEQM_EXECUTE_OPERATION_GROUP", "SEQM_EXECUTE_PENDING_OPERATIONS", "SEQM_RESET_DEVICES", "SEQM_INIT_DEVICES", "SEQM_PANIC", "SEQM_MIDI_LOCAL_OFF", "SEQM_PLAY_MIDI_EVENT", "SEQM_SET_HW_CTRL_STATE", "SEQM_SET_HW_CTRL_STATES", "SEQM_SET_TRACK_AUTO_TYPE", "SEQM_SET_AUX", "SEQM_UPDATE_SOLO_STATES", "AUDIO_ROUTEADD", "AUDIO_ROUTEREMOVE", "AUDIO_REMOVEROUTES", "AUDIO_ADDPLUGIN", "AUDIO_SET_PREFADER", "AUDIO_SET_CHANNELS", "AUDIO_SWAP_CONTROLLER_IDX", "AUDIO_CLEAR_CONTROLLER_EVENTS", "AUDIO_SEEK_PREV_AC_EVENT", "AUDIO_SEEK_NEXT_AC_EVENT", "AUDIO_ERASE_AC_EVENT", "AUDIO_ERASE_RANGE_AC_EVENTS", "AUDIO_ADD_AC_EVENT", "AUDIO_CHANGE_AC_EVENT", "AUDIO_SET_SEND_METRONOME", "AUDIO_START_MIDI_LEARN", "MS_PROCESS", "MS_STOP", "MS_SET_RTC", "MS_UPDATE_POLL_FD", "SEQM_IDLE", "SEQM_SEEK", "AUDIO_WAIT" }; const char* audioStates[] = { "STOP", "START_PLAY", "PLAY", "LOOP1", "LOOP2", "SYNC", "PRECOUNT" }; //--------------------------------------------------------- // Audio //--------------------------------------------------------- const int Audio::_extClockHistoryCapacity = 8192; const unsigned int Audio::_clockOutputQueueCapacity = 8192; Audio::Audio() { _running = false; recording = false; idle = false; _freewheel = false; _bounceState = BounceOff; _loopFrame = 0; _loopCount = 0; m_Xruns = 0; _pos.setType(Pos::FRAMES); _pos.setFrame(0); _curCycleFrames = 0; nextTickPos = curTickPos = 0; _precountFramePos = 0; framesBeat = 0; framesBeatDivisor = 0; framesBeatRemainder = 0; precountMidiClickFrame = 0; precountMidiClickFrameRemainder = 0; precountTotalFrames = 0; _syncPlayStarting = false; // Set for way beyond the end of the expected count. _antiSeekFloodCounter = 100000.0; _syncReady = true; midiClick = 0; audioClick = 0; clickno = 0; clicksMeasure = 0; _extClockHistory = new ExtMidiClock[_extClockHistoryCapacity]; _extClockHistorySize = 0; _clockOutputQueue = new unsigned int[_clockOutputQueueCapacity]; _clockOutputQueueSize = 0; _clockOutputCounter = 0; _clockOutputCounterRemainder = 0; syncTimeUS = 0; syncFrame = 0; state = STOP; msg = 0; startRecordPos.setType(Pos::FRAMES); // Tim endRecordPos.setType(Pos::FRAMES); startExternalRecTick = 0; endExternalRecTick = 0; //--------------------------------------------------- // establish pipes/sockets //--------------------------------------------------- int filedes[2]; // 0 - reading 1 - writing if (pipe(filedes) == -1) { perror("creating pipe0"); exit(-1); } fromThreadFdw = filedes[1]; fromThreadFdr = filedes[0]; #ifndef _WIN32 int rv = fcntl(fromThreadFdw, F_SETFL, O_NONBLOCK); if (rv == -1) perror("set pipe O_NONBLOCK"); #endif if (pipe(filedes) == -1) { perror("creating pipe1"); exit(-1); } sigFd = filedes[1]; sigFdr = filedes[0]; } Audio::~Audio() { if(_clockOutputQueue) delete[] _clockOutputQueue; if(_extClockHistory) delete[] _extClockHistory; } //--------------------------------------------------------- // start // start audio processing //--------------------------------------------------------- extern bool initJackAudio(); bool Audio::start() { state = STOP; _loopCount = 0; //MusEGlobal::muse->setHeartBeat(); // Moved below if (!MusEGlobal::audioDevice) { if(initJackAudio() == false) { InputList* itl = MusEGlobal::song->inputs(); for (iAudioInput i = itl->begin(); i != itl->end(); ++i) { if (MusEGlobal::debugMsg) fprintf(stderr, "reconnecting input %s\n", (*i)->name().toLatin1().data()); for (int x=0; x < (*i)->channels();x++) (*i)->setJackPort(x,0); (*i)->registerPorts(); } OutputList* otl = MusEGlobal::song->outputs(); for (iAudioOutput i = otl->begin(); i != otl->end(); ++i) { if (MusEGlobal::debugMsg) fprintf(stderr, "reconnecting output %s\n", (*i)->name().toLatin1().data()); for (int x=0; x < (*i)->channels();x++) (*i)->setJackPort(x,0); if (MusEGlobal::debugMsg) fprintf(stderr, "name=%s\n",(*i)->name().toLatin1().data()); (*i)->registerPorts(); } } else { fprintf(stderr, "Failed to init audio!\n"); return false; } } _running = true; // Set before we start to avoid error messages in process. if(!MusEGlobal::audioDevice->start(MusEGlobal::realTimePriority)) { fprintf(stderr, "Failed to start audio!\n"); _running = false; return false; } // shall we really stop JACK transport and locate to // saved position? MusEGlobal::audioDevice->stopTransport(); MusEGlobal::audioDevice->seekTransport(MusEGlobal::song->cPos()); // Should be OK to start this 'leisurely' timer only after everything // else has been started. MusEGlobal::muse->setHeartBeat(); return true; } //--------------------------------------------------------- // stop // stop audio processing //--------------------------------------------------------- void Audio::stop(bool) { // Stop timer. Possible random crashes closing a song and loading another song - // observed a few times in mixer strip timer handlers (updatexxx). Not sure how // (we're in the graphics thread), but in case something during loading runs // the event loop or something, this should at least not hurt. 2019/01/24 Tim. MusEGlobal::muse->stopHeartBeat(); if (MusEGlobal::audioDevice) MusEGlobal::audioDevice->stop(); _running = false; } //--------------------------------------------------------- // sync // return true if sync is completed //--------------------------------------------------------- bool Audio::sync(int jackState, unsigned frame) { DEBUG_TRANSPORT_SYNC(stderr, "Audio::sync() begin: state:%d jackState:%d frame:%u pos frame:%u\n", state, jackState, frame, _pos.frame()); bool done = true; if (state == PRECOUNT) { DEBUG_TRANSPORT_SYNC(stderr, "Audio::sync: PRECOUNT: _precountFramePos:%u precountTotalFrames:%u\n", _precountFramePos, precountTotalFrames); // Is it requesting a different frame (seek during sync)? // Don't interrupt freewheel mode. if (frame != _pos.frame() && !_freewheel) { DEBUG_TRANSPORT_SYNC(stderr, " state == PRECOUNT, pos frame:%u req frame:%u calling seek...\n", _pos.frame(), frame); seek(Pos(frame, false)); done = MusEGlobal::audioPrefetch->seekDone(); // We're in precount state, so it must have started from stop state. Set to true. _syncPlayStarting = true; // Is the audio prefetch already done? if(done) { // Reset the flag now. _syncPlayStarting = false; // Does the precount start over again? Return false if so, to continue holding up the transport. // This will start the precount if necessary and set the state to PRECOUNT. if(startPreCount()) { _syncReady = false; return _syncReady; } } state = START_PLAY; _syncReady = done; return _syncReady; } _syncReady = _precountFramePos >= precountTotalFrames; return _syncReady; } if (state == LOOP1) { state = LOOP2; } else { State s = State(jackState); // STOP -> START_PLAY start rolling // STOP -> STOP seek in stop state // PLAY -> START_PLAY seek in play state if (state != START_PLAY) { // Is this the first of possibly multiple sync calls? if(_syncReady) { Pos p(frame, false); DEBUG_TRANSPORT_SYNC(stderr, " state != START_PLAY, req frame:%u calling seek...\n", frame); seek(p); } if (!_freewheel) done = MusEGlobal::audioPrefetch->seekDone(); if (s == START_PLAY) { _syncPlayStarting = (state == STOP); DEBUG_TRANSPORT_SYNC(stderr, "state != START_PLAY: done:%d\n", done); // Is this a STOP to START_PLAY transition? if(_syncPlayStarting) { // Stop the timer. _antiSeekFloodCounter = 100000.0; // Is the audio prefetch already done? if(done) { // Reset the flag now. _syncPlayStarting = false; // Does the precount start? Return false if so, to begin holding up the transport. // This will start the precount if necessary and set the state to PRECOUNT. if(startPreCount()) { _syncReady = false; return _syncReady; } } } else { // Start the timer. _antiSeekFloodCounter = 0.0; } state = START_PLAY; } } else { // Is it requesting a different frame (seek during seek)? // Don't interrupt freewheel mode. if (frame != _pos.frame() && !_freewheel) { DEBUG_TRANSPORT_SYNC(stderr, " state == START_PLAY, pos frame:%u req frame:%u calling seek...\n", _pos.frame(), frame); seek(Pos(frame, false)); // Reset timer to start it. _antiSeekFloodCounter = 0.0; } done = MusEGlobal::audioPrefetch->seekDone(); if(_antiSeekFloodCounter < 0.4) { done = false; _antiSeekFloodCounter += (float)MusEGlobal::segmentSize / (float)MusEGlobal::sampleRate; } DEBUG_TRANSPORT_SYNC(stderr, "state == START_PLAY: done:%d\n", done); // Was this a STOP to START_PLAY transition? if(_syncPlayStarting) { // Are the audio prefetch done and anti-flood timer done? if(done) { // Reset the flag now. _syncPlayStarting = false; // Does the precount start? Return false if so, to continue (or begin) holding up the transport. // This will start the precount if necessary and set the state to PRECOUNT. if(startPreCount()) { _syncReady = false; return _syncReady; } } } } } //fprintf(stderr, "Audio::sync() end: state:%d pos frame:%u\n", state, _pos.frame()); _syncReady = done; return _syncReady; } //--------------------------------------------------------- // startPreCount // During sync(), starts count-in state if necessary and returns whether // count-in state has been started. //--------------------------------------------------------- bool Audio::startPreCount() { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; // Since other Jack clients might also set the sync timeout at any time, // we need to be constantly enforcing our desired limit! // Since setSyncTimeout() may not be realtime friendly (Jack driver), // we set the driver's sync timeout in the gui thread. // Sadly, we likely cannot get away with setting it here via the audio sync callback. // So whenever stop, start or seek occurs, we'll try to casually enforce the timeout in Song::seqSignal(). // It's casual, unfortunately we can't set the EXACT timeout amount here when we really need to. if (metro_settings->precountEnableFlag && MusEGlobal::song->click() && !MusEGlobal::extSyncFlag && ((!MusEGlobal::song->record() && metro_settings->precountOnPlay) || MusEGlobal::song->record())) { DEBUG_MIDI_METRONOME(stderr, "state = PRECOUNT!\n"); state = PRECOUNT; int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(curTickPos, &bar, &beat, &tick); int z, n; if (metro_settings->precountFromMastertrackFlag) MusEGlobal::sigmap.timesig(curTickPos, z, n); else { z = metro_settings->precountSigZ; n = metro_settings->precountSigN; } clickno = 0; int clicks_total = z * metro_settings->preMeasures; clicksMeasure = z; int ticks_beat = (MusEGlobal::config.division * 4)/n; // The number of frames per beat in precount state. // framesBeat = MusEGlobal::tempomap.ticks2frames((MusEGlobal::config.division * 4) / n, curTickPos); // The number of frames per beat in precount state. // Tick resolution is less than frame resolution. // Round up so that the reciprocal function (frame to tick) matches value for value. //framesBeat = muse_multiply_64_div_64_to_64( // (uint64_t)MusEGlobal::sampleRate * (uint64_t)MusEGlobal::tempomap.tempo(curTickPos), (MusEGlobal::config.division * 4) / n, // (uint64_t)MusEGlobal::config.division * (uint64_t)MusEGlobal::tempomap.globalTempo() * 10000UL, true); //framesBeat = // (4UL * (uint64_t)MusEGlobal::sampleRate * (uint64_t)MusEGlobal::tempomap.tempo(curTickPos)) / // ((uint64_t)n * (uint64_t)MusEGlobal::tempomap.globalTempo() * 10000UL); const uint64_t framesBeatDividend = (uint64_t)MusEGlobal::sampleRate * (uint64_t)MusEGlobal::tempomap.tempo(curTickPos) * 4; framesBeatDivisor = n * MusEGlobal::tempomap.globalTempo() * 10000; framesBeat = framesBeatDividend / framesBeatDivisor; framesBeatRemainder = framesBeatDividend % framesBeatDivisor; // Reset to zero. _precountFramePos = 0; // How many ticks are required for the precount? const unsigned int precount_ticks = clicks_total * ticks_beat; // How many extra precount ticks are required to 'fill in' for a mid-bar curTickPos? const unsigned int extra_precount_ticks = beat * ticks_beat + tick; // What is the current tick's frame (not the more accurate device frame)? const unsigned int cur_tick_frame = Pos(curTickPos, true).frame(); // How many total frames are required for the precount? precountTotalFrames = MusEGlobal::tempomap.ticks2frames(precount_ticks + extra_precount_ticks, curTickPos); // How many more (or less) frames are required, in case the cursor is on a frame in-between ticks? // If lrint() rounding is used in our tempo routines, the difference between current tick's frame // and current device frame can actually be negative! if(_pos.frame() > cur_tick_frame) { precountTotalFrames += (_pos.frame() - cur_tick_frame); } else { // Basic protection from underflow. if(precountTotalFrames < (cur_tick_frame - _pos.frame())) precountTotalFrames = 0; else precountTotalFrames -= cur_tick_frame - _pos.frame(); } // What is the remainder of the total frames divided by the segment size? // This is the value that the precount frame times should be shifted backwards so that // the very last precount frame + 1 lines up exactly with the first metronome frame. // But since we cannot schedule before the current time, we trick it by shifting // forward and sacrificing one cycle. Therefore this value is referenced to the // start of a cycle buffer, not the end. // The offset ranges from 1 to segmentSize inclusive. unsigned int precount_frame_offset = MusEGlobal::segmentSize - (precountTotalFrames % MusEGlobal::segmentSize); // Start the precount at this frame. precountMidiClickFrame = precount_frame_offset; precountMidiClickFrameRemainder = 0; // Make room in the total frames for the offset as well. precountTotalFrames += precount_frame_offset; DEBUG_MIDI_METRONOME(stderr, "Audio::startPreCount: _pos.tick():%u _pos.frame():%u " "curTickPos:%u cur_tick_frame:%u extra_precount_frames:%d " "clicks_total:%d framesBeat:%u precount_ticks:%u extra_precount_ticks:%u\n" "precountTotalFrames:%u precount_frame_offset:%u precountMidiClickFrame:%u\n", _pos.tick(), _pos.frame(), curTickPos, cur_tick_frame, _pos.frame() - cur_tick_frame, clicks_total, framesBeat, precount_ticks, extra_precount_ticks, precountTotalFrames, precount_frame_offset, precountMidiClickFrame); return true; } return false; } //--------------------------------------------------------- // reSyncAudio // To be called from audio thread only. //--------------------------------------------------------- void Audio::reSyncAudio() { if (isPlaying()) { if (!MusEGlobal::checkAudioDevice()) return; // NOTE: Comment added by Tim: This line is crucial if the tempo is changed during playback, // either via changes to tempo map or the static tempo value. The actual transport frame is allowed // to continue progressing naturally but our representation of it (_pos) jumps to a new value // so that the relation between ticks and frames in all our tempo routines remains correct. // But the relation betweeen actual transport frame and current tick will still be incorrect // for the given new tempo value, that is why this adjustment to our _pos is needed. // Another solution would be to actually seek the transport to the correct frame. // In either solution any waves will jump to a new position but midi will continue on. // A third solution is not jump at all. Instead curTickPos is incremented with a 'delta' rather // than by incrementing _pos and converting to tick. Both midi and audio progress naturally. // But this has a problem in that we cannot use fractional remainder techniques because the // tempo is part of the denominator and changes with every tempo change, so adding incremental // fractions with different denominators together would be tough (cross multiply etc.). // Seems the only way for a delta then is to express it as say microticks (1/1,000,000 of a tick). // But even microticks may not be enough, calculations show nanoticks would be better. // The issue is that the resolution must be very high so that the slight error in the // increment value does not build up over time and cause drift between frame and tick. _pos.setTick(curTickPos); syncFrame = MusEGlobal::audioDevice->framesAtCycleStart(); syncTimeUS = curTimeUS(); } } //--------------------------------------------------------- // setFreewheel //--------------------------------------------------------- void Audio::setFreewheel(bool val) { _freewheel = val; } //--------------------------------------------------------- // shutdown //--------------------------------------------------------- void Audio::shutdown() { _running = false; fprintf(stderr, "Audio::shutdown()\n"); write(sigFd, "S", 1); } //--------------------------------------------------------- // process // process one audio buffer at position "_pos " // of size "frames" //--------------------------------------------------------- void Audio::process(unsigned frames) { _curCycleFrames = frames; if (!MusEGlobal::checkAudioDevice()) return; if (msg) { processMsg(msg); int sn = msg->serialNo; msg = 0; // don't process again int rv = write(fromThreadFdw, &sn, sizeof(int)); if (rv != sizeof(int)) { fprintf(stderr, "audio: write(%d) pipe failed: %s\n", fromThreadFdw, strerror(errno)); } } OutputList* ol = MusEGlobal::song->outputs(); if (idle) { // deliver no audio for (iAudioOutput i = ol->begin(); i != ol->end(); ++i) (*i)->silence(frames); return; } // // resync with audio interface // syncFrame = MusEGlobal::audioDevice->framesAtCycleStart(); syncTimeUS = curTimeUS(); int jackState = MusEGlobal::audioDevice->getState(); //DEBUG_MIDI_TIMING(stderr, "Audio::process Current state:%s jackState:%s sync frame:%u pos frame:%u current transport frame:%u\n", // audioStates[state], audioStates[jackState], syncFrame, _pos.frame(), MusEGlobal::audioDevice->curTransportFrame()); if ((state == START_PLAY || state == PRECOUNT) && jackState == PLAY) { _loopCount = 0; MusEGlobal::song->reenableTouchedControllers(); startRolling(); // REMOVE Tim. latency. Removed. This is way too late to start freewheel mode. // It will be a cycle or two before it even takes effect. // So we do this in MusE::bounceToFile() and MusE::bounceToTrack(), BEFORE the transport is started. // if (_bounce) // write(sigFd, "f", 1); } else if (state == LOOP2 && jackState == PLAY) { ++_loopCount; // Number of times we have looped so far Pos newPos(_loopFrame, false); seek(newPos); startRolling(); } else if ((isPlaying() || state == PRECOUNT) && jackState == STOP) { stopRolling(); } else if (state == START_PLAY && jackState == STOP) { abortRolling(); } else if (state == STOP && jackState == PLAY) { _loopCount = 0; MusEGlobal::song->reenableTouchedControllers(); startRolling(); } else if (state == LOOP1 && jackState == PLAY) ; // treat as play else if (state == LOOP2 && jackState == START_PLAY) { ; // sync cycle } else if (state == PRECOUNT && jackState == START_PLAY) { ; // sync cycle } else if (state != jackState) fprintf(stderr, "JACK: state transition %s -> %s ?\n", audioStates[state], audioStates[jackState]); // fprintf(stderr, "p %s %s %d\n", audioStates[jackState], audioStates[state], _pos.frame()); // // clear aux send buffers // AuxList* al = MusEGlobal::song->auxs(); for (unsigned i = 0; i < al->size(); ++i) { AudioAux* a = (AudioAux*)((*al)[i]); float** dst = a->sendBuffer(); for (int ch = 0; ch < a->channels(); ++ch) memset(dst[ch], 0, sizeof(float) * MusEGlobal::segmentSize); } for (iAudioOutput i = ol->begin(); i != ol->end(); ++i) (*i)->processInit(frames); int samplePos = _pos.frame(); int offset = 0; // buffer offset in audio buffers #ifdef _JACK_TIMEBASE_DRIVES_MIDI_ bool use_jack_timebase = false; #endif for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { MidiDevice* md = (*id); const int port = md->midiPort(); // Gather midi input from devices that need collecting, such as Jack midi. md->collectMidiEvents(); // Process the selected device's external clock history fifo ring buffer. // Quickly transfer the items to a list for easier processing later. // It is possible for the timestamps to be out of order. Deal with it. // Sort all the timestamps. Do not miss a clock, better that it is at least // included in the count. if(port >= 0 && port < MusECore::MIDI_PORTS && port == MusEGlobal::config.curMidiSyncInPort) { // False = don't use the size snapshot. const int clk_fifo_sz = md->extClockHistory()->getSize(false); if(clk_fifo_sz != 0) { for(int i = 0; i < clk_fifo_sz; ++i) { if(_extClockHistorySize >= _extClockHistoryCapacity) { fprintf(stderr, "Audio::process: _extClockHistory overrun!\n"); break; } _extClockHistory[_extClockHistorySize] = md->extClockHistory()->get(); ++_extClockHistorySize; } } } else // Otherwise flush and discard the device's unused ring buffer data. md->extClockHistory()->clearRead(); } //if(MusEGlobal::extSyncFlag && (MusEGlobal::midiSyncContainer.isRunning() || isPlaying())) // fprintf(stderr, "extSyncFlag:%d externalPlayState:%d isPlaying:%d\n", // MusEGlobal::extSyncFlag, MusEGlobal::midiSyncContainer.externalPlayState(), isPlaying()); if (isPlaying()) { if (!freewheel()) MusEGlobal::audioPrefetch->msgTick(isRecording(), true); if (bounce() && _pos >= MusEGlobal::song->rPos()) { // Need to let the resulting stopRolling take care of resetting bounce. //_bounceState = BounceOff; // This is safe in both Jack 1 and 2. MusEGlobal::audioDevice->stopTransport(); return; } #ifdef _JACK_TIMEBASE_DRIVES_MIDI_ unsigned curr_jt_tick, next_jt_ticks; use_jack_timebase = MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO && !MusEGlobal::config.timebaseMaster && !MusEGlobal::tempomap.masterFlag() && !MusEGlobal::extSyncFlag && static_cast(MusEGlobal::audioDevice)->timebaseQuery( frames, NULL, NULL, NULL, &curr_jt_tick, &next_jt_ticks); // NOTE: I would rather trust the reported current tick than rely solely on the stream of // tempos to correctly advance to the next position (which did actually test OK anyway). if(use_jack_timebase) curTickPos = curr_jt_tick; #endif // // check for end of song // if ((curTickPos >= MusEGlobal::song->len()) && !(MusEGlobal::song->record() || bounce() || MusEGlobal::song->loop())) { if(MusEGlobal::debugMsg) fprintf(stderr, "Audio::process curTickPos >= MusEGlobal::song->len\n"); MusEGlobal::audioDevice->stopTransport(); return; } // // check for loop end // if (state == PLAY && MusEGlobal::song->loop() && !bounce() && !MusEGlobal::extSyncFlag) { const Pos& loop = MusEGlobal::song->rPos(); // The extra 'frames' worth is because we are calling the relocation from here within the process routine, // so there will be a one cycle (extra) delay before the next sync call (sync is called just before process). unsigned n = loop.frame() - samplePos - MusEGlobal::audioDevice->transportRelocateOrPlayDelay() - frames; if (n < frames) { // loop end in current cycle unsigned lpos = MusEGlobal::song->lPos().frame(); // adjust loop start so we get exact loop len if (n > lpos) n = 0; state = LOOP1; _loopFrame = lpos - n; Pos lp(_loopFrame, false); // Seek the transport. Note that temporary clearing of sustain // controllers is done by the seek handlers and then startRolling(). MusEGlobal::audioDevice->seekTransport(lp); } } if(MusEGlobal::extSyncFlag) // p3.3.25 { // Advance the tick position by the number of clock events times the division. const int div = MusEGlobal::config.division / 24; int tcks = 0; for(int i = 0; i < _extClockHistorySize; ++i) { if(_extClockHistory[i].isPlaying()) ++tcks; } nextTickPos = curTickPos + tcks * div; } else { #ifdef _JACK_TIMEBASE_DRIVES_MIDI_ if(use_jack_timebase) // With jack timebase this might not be accurate - // we are relying on the tempo to figure out the next tick. nextTickPos = curTickPos + next_jt_ticks; else #endif { Pos ppp(_pos); ppp += frames; nextTickPos = ppp.tick(); } } } process1(samplePos, offset, frames); for (iAudioOutput i = ol->begin(); i != ol->end(); ++i) { (*i)->processWrite(); // Special for audio outputs tracks: Now that processWrite() is // finished using 'buffer', apply output channel latency compensation // to the buffer so that the correct signals appear at the final destination. // Note that 'buffer' should be in phase by now. (*i)->applyOutputLatencyComp(frames); } // REMOVE Tim. latency. Changed. Hm, doesn't work. Position takes a long time to start moving. if (isPlaying()) { //if(isPlaying() || (MusEGlobal::extSyncFlag && MusEGlobal::midiSyncContainer.isPlaying())) { _pos += frames; // With jack timebase this might not be accurate if we // set curTickPos (above) from the reported current tick. curTickPos = nextTickPos; } // If external sync has started but the transport has not started yet, // don't reset the clock history yet, just let it pile up until the transport starts. // It's because curTickPos does not advance yet until transport is running, so we // can't rely on curTickPos as a base just yet... if(!MusEGlobal::extSyncFlag || !MusEGlobal::midiSyncContainer.isPlaying() || isPlaying()) _extClockHistorySize = 0; } //--------------------------------------------------------- // process1 //--------------------------------------------------------- void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames) { // // process not connected tracks // to animate meter display // const TrackList& tl = *MusEGlobal::song->tracks(); const TrackList::size_type tl_sz = tl.size(); const AuxList& aux_tl = *MusEGlobal::song->auxs(); const AuxList::size_type aux_tl_sz = aux_tl.size(); const OutputList& out_tl = *MusEGlobal::song->outputs(); const OutputList::size_type out_tl_sz = out_tl.size(); const MidiDeviceList& mdl = MusEGlobal::midiDevices; Track* track; AudioTrack* atrack; int channels; bool want_record_side; MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; // This includes synthesizers. for(TrackList::size_type it = 0; it < tl_sz; ++it) { track = tl[it]; // For track types, synths etc. which need some kind of non-audio // (but possibly audio-affecting) processing always, even if their output path // is ultimately unconnected. // Example: A fluidsynth instance whose output path ultimately led to nowhere // would not allow us to load a font. Since process() was driven by audio output, // in this case there was nothing driving the process() function which responds to // such gui commands. So I separated the events processing from process(), into this. // It should be used for things like midi events, gui events etc. - things which need to // be done BEFORE all the AudioOutput::process() are called below. That does NOT include // audio processing, because THAT is done at the very end of this routine. // This will also reset the track's processed flag. Tim. track->preProcessAlways(); // Reset some latency info to prepare for (re)computation. track->prepareLatencyScan(); } // This includes synthesizers. for(ciMidiDevice imd = mdl.cbegin(); imd != mdl.cend(); ++imd) { MidiDevice* md = *imd; // Device not in use? if(md->midiPort() < 0 || md->midiPort() >= MusECore::MIDI_PORTS) continue; // Reset some latency info to prepare for (re)computation. md->prepareLatencyScan(); } // Pre-process the metronome. metronome->preProcessAlways(); // Reset some latency info to prepare for (re)computation. static_cast(metronome)->prepareLatencyScan(); static_cast(metronome)->prepareLatencyScan(); //--------------------------------------------- // BEGIN Latency correction/compensation processing // TODO: Instead of doing this blindly every cycle, do it only when // a latency controller changes or a connection is made etc, // ie only when something changes. //--------------------------------------------- if(MusEGlobal::config.enableLatencyCorrection) { float song_worst_latency = 0.0f; //--------------------------------------------- // PASS 1: Find any dominant branches: //--------------------------------------------- for(TrackList::size_type it = 0; it < tl_sz; ++it) { track = tl[it]; // If the track is for example a Wave Track, we must consider up to two contributing paths, // the output (playback) side and the input (record) side which can pass through via monitoring. want_record_side = track->isMidiTrack() || track->type() == Track::WAVE; //--------------------------------------------- // We are looking for the end points of branches. // This includes Audio Outputs, and open-ended branches // that go nowhere or are effectively disconnected from // their destination(s). // This caches its result in the track's _latencyInfo structure // so it can be used faster in the correction pass or final pass. //--------------------------------------------- // Examine any recording path, if desired. if(want_record_side && track->isLatencyInputTerminal()) track->getDominanceInfo(true); // Examine any playback path. if(track->isLatencyOutputTerminal()) track->getDominanceInfo(false); } #if 1 // This includes synthesizers. for(ciMidiDevice imd = mdl.cbegin(); imd != mdl.cend(); ++imd) { MidiDevice* md = *imd; // Device not in use? if(md->midiPort() < 0 || md->midiPort() >= MusECore::MIDI_PORTS) continue; // Examine any playback path on capture devices. if(md->isLatencyOutputTerminalMidi(true /*capture*/)) md->getDominanceInfoMidi(true /*capture*/, false); // Examine any playback path on playback devices. if(md->isLatencyOutputTerminalMidi(false /*playback*/)) md->getDominanceInfoMidi(false /*playback*/, false); } #endif //--------------------------------------------- // Throughout this latency code, we will examine the metronome // enable/disable settings rather than the metronome on/off button(s), // so that the metronome button(s) can be 'ready for action' when clicked, // without affecting the sound. // Otherwise a glitch would occur every time the metronome is simply // turned on or off via the gui toolbars, while latency is recomputed. //--------------------------------------------- //if(MusEGlobal::song->click()) if(metro_settings->audioClickFlag) { // Examine any playback path. if(metronome->isLatencyOutputTerminal()) metronome->getDominanceInfo(false); } if(metro_settings->midiClickFlag) { // Examine any playback path. if(metronome->isLatencyOutputTerminalMidi(false /*playback*/)) metronome->getDominanceInfoMidi(false /*playback*/, false); } //--------------------------------------------- // PASS 2: Set correction values: //--------------------------------------------- // Now that we know the worst case latency, // set all the branch correction values. for(TrackList::size_type it = 0; it < tl_sz; ++it) { track = tl[it]; // If the track is for example a Wave Track, we must consider up to two contributing paths, // the output (playback) side and the input (record) side which can pass through via monitoring. want_record_side = track->isMidiTrack() || track->type() == Track::WAVE; // Set branch correction values, for any tracks which support it. // If this branch can dominate latency, ie. is fed from an Audio Input Track, // then we cannot apply correction to the branch. // For example: // // Input(512) -> Wave1(monitor on) -> Output(3072) // -> Wave2 // // Wave1 wants to set a correction of -3072 but it cannot because // the input has 512 latency. Any wave1 playback material would be // misaligned with the monitored input material. // // Examine any recording path, if desired. if(want_record_side && track->isLatencyInputTerminal()) { const TrackLatencyInfo& li = track->getDominanceInfo(true); if(!li._canDominateOutputLatency) track->setCorrectionLatencyInfo(true, song_worst_latency); } // Examine any playback path. if(track->isLatencyOutputTerminal()) { const TrackLatencyInfo& li = track->getDominanceInfo(false); if(!li._canDominateOutputLatency) track->setCorrectionLatencyInfo(false, song_worst_latency); } } #if 1 // This includes synthesizers. for(ciMidiDevice imd = mdl.cbegin(); imd != mdl.cend(); ++imd) { MidiDevice* md = *imd; // Device not in use? if(md->midiPort() < 0 || md->midiPort() >= MusECore::MIDI_PORTS) continue; if(md->isLatencyOutputTerminalMidi(true /*capture*/)) { // Grab the capture device's branch's dominance latency info. // This should already be cached from the dominance pass. const TrackLatencyInfo& li = md->getDominanceInfoMidi(true /*capture*/, false); if(!li._canDominateOutputLatency) md->setCorrectionLatencyInfoMidi(true /*capture*/, false, song_worst_latency); } if(md->isLatencyOutputTerminalMidi(false /*playback*/)) { // Grab the playback device's branch's dominance latency info. // This should already be cached from the dominance pass. const TrackLatencyInfo& li = md->getDominanceInfoMidi(false /*playback*/, false); if(!li._canDominateOutputLatency) md->setCorrectionLatencyInfoMidi(false /*playback*/, false, song_worst_latency); } } #endif //if(MusEGlobal::song->click()) if(metro_settings->audioClickFlag) { if(metronome->isLatencyOutputTerminal()) { // Grab the branch's dominance latency info. // This should already be cached from the dominance pass. const TrackLatencyInfo& li = metronome->getDominanceInfo(false); if(!li._canDominateOutputLatency) metronome->setCorrectionLatencyInfo(false, song_worst_latency); } } if(metro_settings->midiClickFlag) { if(metronome->isLatencyOutputTerminalMidi(false /*playback*/)) { // Grab the branch's dominance latency info. // This should already be cached from the dominance pass. const TrackLatencyInfo& li = metronome->getDominanceInfoMidi(false /*playback*/, false); if(!li._canDominateOutputLatency) metronome->setCorrectionLatencyInfoMidi(false /*playback*/, false, song_worst_latency); } } //--------------------------------------------- // PASS 3: Set initial output latencies: //--------------------------------------------- for(TrackList::size_type it = 0; it < tl_sz; ++it) { track = tl[it]; // If the track is for example a Wave Track, we must consider up to two contributing paths, // the output (playback) side and the input (record) side which can pass through via monitoring. want_record_side = track->isMidiTrack() || track->type() == Track::WAVE; //--------------------------------------------- // We are looking for the end points of branches. // This includes Audio Outputs, and open-ended branches // that go nowhere or are effectively disconnected from // their destination(s). // This caches its result in the track's _latencyInfo structure // in the _isLatencyOutputTerminal member so it can be used // faster in the correction pass or final pass. //--------------------------------------------- // Examine any recording path, if desired. if(want_record_side && track->isLatencyInputTerminal()) { // Gather the branch's dominance latency info. const TrackLatencyInfo& li = track->getDominanceLatencyInfo(true); // If the branch can dominate, and this end-point allows it, and its latency value // is greater than the current worst, overwrite the worst. if(track->canDominateEndPointLatency() && li._canDominateInputLatency && li._inputLatency > song_worst_latency) song_worst_latency = li._inputLatency; } // Examine any playback path. if(track->isLatencyOutputTerminal()) { // Gather the branch's dominance latency info. const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false); // If the branch can dominate, and this end-point allows it, and its latency value // is greater than the current worst, overwrite the worst. if(track->canDominateEndPointLatency() && li._canDominateOutputLatency && li._outputLatency > song_worst_latency) song_worst_latency = li._outputLatency; } } #if 1 // This includes synthesizers. for(ciMidiDevice imd = mdl.cbegin(); imd != mdl.cend(); ++imd) { MidiDevice* md = *imd; // Device not in use? if(md->midiPort() < 0 || md->midiPort() >= MusECore::MIDI_PORTS) continue; //--------------------------------------------- // We are looking for the end points of branches. // This includes Audio Outputs, and open-ended branches // that go nowhere or are effectively disconnected from // their destination(s). // This caches its result in the track's _latencyInfo structure // in the _isLatencyOutputTerminal member so it can be used // faster in the correction pass or final pass. //--------------------------------------------- // Examine any playback path on capture devices. if(md->isLatencyOutputTerminalMidi(true /*capture*/)) { // Gather the branch's dominance latency info. const TrackLatencyInfo& li = md->getDominanceLatencyInfoMidi(true /*capture*/, false); // If the branch can dominate, and this end-point allows it, and its latency value // is greater than the current worst, overwrite the worst. if(md->canDominateEndPointLatencyMidi(true /*capture*/) && li._canDominateOutputLatency && li._outputLatency > song_worst_latency) song_worst_latency = li._outputLatency; } // Examine any playback path on playback devices. if(md->isLatencyOutputTerminalMidi(false /*playback*/)) { // Gather the branch's dominance latency info. const TrackLatencyInfo& li = md->getDominanceLatencyInfoMidi(false /*playback*/, false); // If the branch can dominate, and this end-point allows it, and its latency value // is greater than the current worst, overwrite the worst. if(md->canDominateEndPointLatencyMidi(false /*playback*/) && li._canDominateOutputLatency && li._outputLatency > song_worst_latency) song_worst_latency = li._outputLatency; } } #endif //if(MusEGlobal::song->click()) if(metro_settings->audioClickFlag) { // Examine any playback path. if(metronome->isLatencyOutputTerminal()) { // Gather the branch's dominance latency info. const TrackLatencyInfo& li = metronome->getDominanceLatencyInfo(false); // If the branch can dominate, and this end-point allows it, and its latency value // is greater than the current worst, overwrite the worst. if(metronome->canDominateEndPointLatency() && li._canDominateOutputLatency && li._outputLatency > song_worst_latency) song_worst_latency = li._outputLatency; } } if(metro_settings->midiClickFlag) { // Examine any playback path. if(metronome->isLatencyOutputTerminalMidi(false /*playback*/)) { // Gather the branch's dominance latency info. const TrackLatencyInfo& li = metronome->getDominanceLatencyInfoMidi(false /*playback*/, false); // If the branch can dominate, and this end-point allows it, and its latency value // is greater than the current worst, overwrite the worst. if(metronome->canDominateEndPointLatencyMidi(false /*playback*/) && li._canDominateOutputLatency && li._outputLatency > song_worst_latency) song_worst_latency = li._outputLatency; } } //---------------------------------------------------------- // PASS 4: Gather final latency values and set compensators: //---------------------------------------------------------- // Now that all branch correction values have been set, // gather all final latency info. for(TrackList::size_type it = 0; it < tl_sz; ++it) { track = tl[it]; // If the track is for example a Wave Track, we must consider up to two contributing paths, // the output (playback) side and the input (record) side which can pass through via monitoring. want_record_side = track->isMidiTrack() || track->type() == Track::WAVE; // Examine any recording path, if desired. // We are looking for the end points of branches. if(want_record_side && track->isLatencyInputTerminal()) { // Gather the branch's final latency info, which also sets the // latency compensators. track->getLatencyInfo(true); // Set this end point's latency compensator write offset. track->setLatencyCompWriteOffset(song_worst_latency); } // Examine any playback path. // We are looking for the end points of branches. if(track->isLatencyOutputTerminal()) { // Gather the branch's final latency info, which also sets the // latency compensators. track->getLatencyInfo(false); // Set this end point's latency compensator write offset. track->setLatencyCompWriteOffset(song_worst_latency); } } #if 1 // This includes synthesizers. for(ciMidiDevice imd = mdl.cbegin(); imd != mdl.cend(); ++imd) { MidiDevice* md = *imd; // Device not in use? if(md->midiPort() < 0 || md->midiPort() >= MusECore::MIDI_PORTS) continue; // We are looking for the end points of branches. if(md->isLatencyOutputTerminalMidi(true /*capture*/)) { // Examine any playback path of the capture device. // Gather the branch's final latency info, which also sets the // latency compensators. md->getLatencyInfoMidi(true /*capture*/, false); // Set this end point's latency compensator write offset. md->setLatencyCompWriteOffsetMidi(song_worst_latency, true /*capture*/); } // We are looking for the end points of branches. if(md->isLatencyOutputTerminalMidi(false /*playback*/)) { // Examine any playback path of the playback device. // Gather the branch's final latency info, which also sets the // latency compensators. md->getLatencyInfoMidi(false /*playback*/, false); // Set this end point's latency compensator write offset. md->setLatencyCompWriteOffsetMidi(song_worst_latency, false /*playback*/); } } #endif //if(MusEGlobal::song->click()) if(metro_settings->audioClickFlag) { // We are looking for the end points of branches. if(metronome->isLatencyOutputTerminal()) { // Gather the branch's final latency info, which also sets the // latency compensators. metronome->getLatencyInfo(false); // Set this end point's latency compensator write offset. metronome->setLatencyCompWriteOffset(song_worst_latency); } } if(metro_settings->midiClickFlag) { // We are looking for the end points of branches. if(metronome->isLatencyOutputTerminalMidi(false /*playback*/)) { // Gather the branch's final latency info, which also sets the // latency compensators. metronome->getLatencyInfoMidi(false /*playback*/, false); // Set this end point's latency compensator write offset. metronome->setLatencyCompWriteOffsetMidi(song_worst_latency, false /*playback*/); } } } //--------------------------------------------- // END Latency correction/compensation processing //--------------------------------------------- //--------------------------------------------- // Midi processing //--------------------------------------------- processMidi(frames); //--------------------------------------------- // Audio processing //--------------------------------------------- // Process Aux tracks first. for(AuxList::size_type it = 0; it < aux_tl_sz; ++it) { atrack = static_cast(aux_tl[it]); if(!atrack->processed()) { channels = atrack->channels(); // Just a dummy buffer. float* buffer[channels]; float data[frames * channels]; for (int i = 0; i < channels; ++i) buffer[i] = data + i * frames; atrack->copyData(samplePos, -1, channels, channels, -1, -1, frames, buffer); } } for(OutputList::size_type it = 0; it < out_tl_sz; ++it) static_cast(out_tl[it])->process(samplePos, offset, frames); // Were ANY tracks unprocessed as a result of processing all the AudioOutputs, above? // Not just unconnected ones, as previously done, but ones whose output path ultimately leads nowhere. // Those tracks were missed, until this fix. // Do them now. This will animate meters, and 'quietly' process some audio which needs to be done - // for example synths really need to be processed, 'quietly' or not, otherwise the next time processing // is 'turned on', if there was a backlog of events while it was off, then they all happen at once. Tim. for(TrackList::size_type it = 0; it < tl_sz; ++it) { atrack = static_cast(tl[it]); if(atrack->isMidiTrack()) continue; if(!atrack->processed() && (atrack->type() != Track::AUDIO_OUTPUT)) { channels = atrack->channels(); // Just a dummy buffer. float* buffer[channels]; float data[frames * channels]; for (int i = 0; i < channels; ++i) buffer[i] = data + i * frames; atrack->copyData(samplePos, -1, channels, channels, -1, -1, frames, buffer); } } } //--------------------------------------------------------- // processMsg //--------------------------------------------------------- void Audio::processMsg(AudioMsg* msg) { switch(msg->id) { case AUDIO_ROUTEADD: addRoute(msg->sroute, msg->droute); break; case AUDIO_ROUTEREMOVE: removeRoute(msg->sroute, msg->droute); break; case AUDIO_REMOVEROUTES: removeAllRoutes(msg->sroute, msg->droute); break; case SEQM_SET_AUX: msg->snode->setAuxSend(msg->ival, msg->dval); break; case AUDIO_SET_PREFADER: msg->snode->setPrefader(msg->ival); break; case AUDIO_SET_CHANNELS: msg->snode->setChannels(msg->ival); break; case AUDIO_ADDPLUGIN: msg->snode->addPlugin(msg->plugin, msg->ival); break; case AUDIO_SWAP_CONTROLLER_IDX: msg->snode->swapControllerIDX(msg->a, msg->b); break; case AUDIO_CLEAR_CONTROLLER_EVENTS: msg->snode->clearControllerEvents(msg->ival); break; case AUDIO_SEEK_PREV_AC_EVENT: msg->snode->seekPrevACEvent(msg->ival); break; case AUDIO_SEEK_NEXT_AC_EVENT: msg->snode->seekNextACEvent(msg->ival); break; case AUDIO_ERASE_AC_EVENT: msg->snode->eraseACEvent(msg->ival, msg->a); break; case AUDIO_ERASE_RANGE_AC_EVENTS: msg->snode->eraseRangeACEvents(msg->ival, msg->a, msg->b); break; case AUDIO_ADD_AC_EVENT: msg->snode->addACEvent(msg->ival, msg->a, msg->dval); break; case AUDIO_CHANGE_AC_EVENT: msg->snode->changeACEvent(msg->ival, msg->a, msg->b, msg->dval); break; case AUDIO_SET_SEND_METRONOME: msg->snode->setSendMetronome((bool)msg->ival); break; case AUDIO_START_MIDI_LEARN: // Reset the values. The engine will fill these from driver events. MusEGlobal::midiLearnPort = -1; MusEGlobal::midiLearnChan = -1; MusEGlobal::midiLearnCtrl = -1; break; case SEQM_RESET_DEVICES: for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { if(MusEGlobal::midiPorts[i].device()) MusEGlobal::midiPorts[i].instrument()->reset(i); } break; case SEQM_INIT_DEVICES: initDevices(msg->a); break; case SEQM_MIDI_LOCAL_OFF: sendLocalOff(); break; case SEQM_PANIC: panic(); break; case SEQM_PLAY_MIDI_EVENT: { const MidiPlayEvent ev = *((MidiPlayEvent*)(msg->p1)); const int port = ev.port(); if(port < 0 || port >= MusECore::MIDI_PORTS) break; // This is the audio thread. Just set directly. MusEGlobal::midiPorts[port].setHwCtrlState(ev); // Send to the device. if(MidiDevice* md = MusEGlobal::midiPorts[port].device()) md->putEvent(ev, MidiDevice::NotLate); // Record?? } break; case SEQM_SET_HW_CTRL_STATE: { MidiPort* port = (MidiPort*)(msg->p1); port->setHwCtrlState(msg->a, msg->b, msg->c); } break; case SEQM_SET_HW_CTRL_STATES: { MidiPort* port = (MidiPort*)(msg->p1); port->setHwCtrlStates(msg->a, msg->b, msg->c, msg->ival); } break; case SEQM_SET_TRACK_AUTO_TYPE: msg->track->setAutomationType(AutomationType(msg->ival)); break; case SEQM_IDLE: idle = msg->a; if(MusEGlobal::midiSeq) MusEGlobal::midiSeq->sendMsg(msg); break; case AUDIO_WAIT: // Do nothing. break; default: MusEGlobal::song->processMsg(msg); break; } } //--------------------------------------------------------- // seek // - called before start play // - initiated from gui // To be called from audio thread only. //--------------------------------------------------------- void Audio::seek(const Pos& p) { // The transport (and user) must NOT be allowed to influence the position during bounce mode. if(!bounce()) { // This is expected and required sometimes, for example to // sync the prefetch system to a position we are ALREADY at. // But being fairly costly, not too frequently. So we warn just in case. if (_pos == p) { if(MusEGlobal::debugMsg) fprintf(stderr, "Audio::seek already at frame:%u\n", p.frame()); } if (MusEGlobal::heavyDebugMsg) fprintf(stderr, "Audio::seek frame:%d\n", p.frame()); _pos = p; if (!MusEGlobal::checkAudioDevice()) return; syncFrame = MusEGlobal::audioDevice->framesAtCycleStart(); #ifdef _JACK_TIMEBASE_DRIVES_MIDI_ unsigned curr_jt_tick; if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO && !MusEGlobal::config.timebaseMaster && !MusEGlobal::tempomap.masterFlag() && !MusEGlobal::extSyncFlag && static_cast(MusEGlobal::audioDevice)->timebaseQuery( MusEGlobal::segmentSize, NULL, NULL, NULL, &curr_jt_tick, NULL)) curTickPos = curr_jt_tick; else #endif curTickPos = _pos.tick(); // Now that curTickPos is set, set the correct initial metronome midiClick. updateMidiClick(); // // Handle stuck notes and set controllers for new position: // seekMidi(); if (state != LOOP2 && !freewheel()) { // We need to force prefetch to update, to ensure the most recent data. // Things can happen to a part before play is pressed - such as part muting, // part moving etc. Without a force, the wrong data was being played. Tim 08/17/08 // This does not wait. // FIXME: Actually it WILL until the the message is sent, but which is usually right away. MusEGlobal::audioPrefetch->msgSeek(_pos.frame(), true); } write(sigFd, "G", 1); // signal seek to gui } } //--------------------------------------------------------- // writeTick // called from audio prefetch thread context // write another buffer to soundfile //--------------------------------------------------------- void Audio::writeTick() { AudioOutput* ao = MusEGlobal::song->bounceOutput; if(ao && MusEGlobal::song->outputs()->find(ao) != MusEGlobal::song->outputs()->end()) { if(ao->recordFlag()) ao->record(); } WaveTrackList* tl = MusEGlobal::song->waves(); for (iWaveTrack t = tl->begin(); t != tl->end(); ++t) { WaveTrack* track = *t; if (track->recordFlag()) track->record(); } } //--------------------------------------------------------- // startRolling //--------------------------------------------------------- void Audio::startRolling() { if (MusEGlobal::debugMsg) fprintf(stderr, "startRolling - loopCount=%d, _pos=%d\n", _loopCount, _pos.tick()); // The transport (and user) must NOT be allowed // to influence the position during bounce mode. if(_bounceState != BounceOn) { if(_loopCount == 0) { startRecordPos = _pos; startExternalRecTick = curTickPos; } if (MusEGlobal::song->record()) { recording = true; WaveTrackList* tracks = MusEGlobal::song->waves(); for (iWaveTrack i = tracks->begin(); i != tracks->end(); ++i) { WaveTrack* track = *i; track->resetMeter(); // If we are in freewheel mode, directly seek the wave files. // Since the audio converter support was added, the wave files MUST be allowed to // progress naturally during play, pulled in a stream manner by the particular converter. // Therefore we no longer seek with EVERY data fetch, so do it here, directly. // We CANNOT do this in Audio::seek in response to a transport relocation, because // the transport (and user) must not be allowed to influence the position during freewheel. // Note that when freewheeling, prefetch is essentially UNUSED. We can ignore it here. if(freewheel() /*&& bounce()*/) { // Might as well do this. Not costly. Prepare for next 'real' non-freewheel seek? track->clearPrefetchFifo(); track->setPrefetchWritePos(_pos.frame()); track->seekData(_pos.frame()); } } } } state = PLAY; if(_bounceState != BounceOn) { write(sigFd, "1", 1); // Play // Don't send if external sync is on. The master, and our sync routing system will take care of that. if(!MusEGlobal::extSyncFlag) { for(int port = 0; port < MusECore::MIDI_PORTS; ++port) { MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiDevice* dev = mp->device(); if(!dev) continue; MidiSyncInfo& si = mp->syncInfo(); if(si.MMCOut()) mp->sendMMCDeferredPlay(); if(si.MRTOut()) { if(curTickPos) mp->sendContinue(); else mp->sendStart(); } } } // Set the correct initial metronome midiClick. // Theoretically shouldn't have to do this here, in seek() should be enough. // But just in case, no harm. updateMidiClick(); // re-enable sustain for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; if(!mp->device()) continue; for (int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if (mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { const MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 127); mp->device()->putEvent(ev, MidiDevice::NotLate); } } } } if(_bounceState == BounceStart) _bounceState = BounceOn; } //--------------------------------------------------------- // abortRolling //--------------------------------------------------------- void Audio::abortRolling() { if (MusEGlobal::debugMsg) fprintf(stderr, "Audio::abortRolling state %s\n", audioStates[state]); state = STOP; // // Clear midi device notes and stop stuck notes: // // Clear the special sync play state (separate from audio play state). MusEGlobal::midiSyncContainer.setExternalPlayState(ExtMidiClock::ExternStopped); // Not playing. Moved here from MidiSeq::processStop() // Stop the ALSA devices... if(MusEGlobal::midiSeq) MusEGlobal::midiSeq->msgStop(); // FIXME: This waits! // Stop any non-ALSA devices... for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { MidiDevice* md = *id; const MidiDevice::MidiDeviceType type = md->deviceType(); // Only for non-ALSA devices. switch(type) { case MidiDevice::ALSA_MIDI: break; case MidiDevice::JACK_MIDI: case MidiDevice::SYNTH_MIDI: md->handleStop(); break; } } // There may be disk read/write fifo buffers waiting to be emptied. Send one last tick to the disk thread. if(!freewheel()) MusEGlobal::audioPrefetch->msgTick(recording, false); // This does not wait. WaveTrackList* tracks = MusEGlobal::song->waves(); for (iWaveTrack i = tracks->begin(); i != tracks->end(); ++i) { (*i)->resetMeter(); } recording = false; if(_bounceState == BounceOff) { write(sigFd, "3", 1); // abort rolling } else { _bounceState = BounceOff; write(sigFd, "A", 1); // abort rolling + Special stop bounce (offline) mode } } //--------------------------------------------------------- // stopRolling //--------------------------------------------------------- void Audio::stopRolling() { if (MusEGlobal::debugMsg) fprintf(stderr, "Audio::stopRolling state %s\n", audioStates[state]); state = STOP; // // Clear midi device notes and stop stuck notes: // // Clear the special sync play state (separate from audio play state). MusEGlobal::midiSyncContainer.setExternalPlayState(ExtMidiClock::ExternStopped); // Not playing. Moved here from MidiSeq::processStop() // Stop the ALSA devices... if(MusEGlobal::midiSeq) MusEGlobal::midiSeq->msgStop(); // FIXME: This waits! // Stop any non-ALSA devices... for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { MidiDevice* md = *id; const MidiDevice::MidiDeviceType type = md->deviceType(); // Only for non-ALSA devices. switch(type) { case MidiDevice::ALSA_MIDI: break; case MidiDevice::JACK_MIDI: case MidiDevice::SYNTH_MIDI: md->handleStop(); break; } } // There may be disk read/write fifo buffers waiting to be emptied. Send one last tick to the disk thread. if(!freewheel()) MusEGlobal::audioPrefetch->msgTick(recording, false); // This does not wait. WaveTrackList* tracks = MusEGlobal::song->waves(); for (iWaveTrack i = tracks->begin(); i != tracks->end(); ++i) { (*i)->resetMeter(); } recording = false; endRecordPos = _pos; endExternalRecTick = curTickPos; if(_bounceState == BounceOff) { write(sigFd, "0", 1); // STOP } else { _bounceState = BounceOff; write(sigFd, "B", 1); // STOP + Special stop bounce (offline) mode } } //--------------------------------------------------------- // recordStop // execution environment: gui thread //--------------------------------------------------------- void Audio::recordStop(bool restart, Undo* ops) { MusEGlobal::song->processMasterRec(); if (MusEGlobal::debugMsg) fprintf(stderr, "recordStop - startRecordPos=%d\n", MusEGlobal::extSyncFlag ? startExternalRecTick : startRecordPos.tick()); Undo loc_ops; Undo& operations = ops ? (*ops) : loc_ops; WaveTrackList* wl = MusEGlobal::song->waves(); for (iWaveTrack it = wl->begin(); it != wl->end(); ++it) { WaveTrack* track = *it; if (track->recordFlag() || MusEGlobal::song->bounceTrack == track) { MusEGlobal::song->cmdAddRecordedWave(track, startRecordPos, restart ? _pos : endRecordPos, operations); if(!restart) operations.push_back(UndoOp(UndoOp::SetTrackRecord, track, false, true)); // True = non-undoable. } } MidiTrackList* ml = MusEGlobal::song->midis(); for (iMidiTrack it = ml->begin(); it != ml->end(); ++it) { MidiTrack* mt = *it; //--------------------------------------------------- // resolve NoteOff events, Controller etc. //--------------------------------------------------- // Do SysexMeta. Do loops. buildMidiEventList(&mt->events, mt->mpevents, mt, MusEGlobal::config.division, true, true); MusEGlobal::song->cmdAddRecordedEvents(mt, mt->events, MusEGlobal::extSyncFlag ? startExternalRecTick : startRecordPos.tick(), operations); mt->events.clear(); // ** Driver should not be touching this right now. mt->mpevents.clear(); // ** Driver should not be touching this right now. } // // bounce to file operates on the only // selected output port // AudioOutput* ao = MusEGlobal::song->bounceOutput; if(ao && MusEGlobal::song->outputs()->find(ao) != MusEGlobal::song->outputs()->end()) { if(ao->recordFlag()) { MusEGlobal::song->bounceOutput = nullptr; ao->setRecFile(nullptr); // if necessary, this automatically deletes _recFile operations.push_back(UndoOp(UndoOp::SetTrackRecord, ao, false, true)); // True = non-undoable. } } MusEGlobal::song->bounceTrack = nullptr; // Operate on a local list if none was given. if(!ops) MusEGlobal::song->applyOperationGroup(loc_ops); if(!restart) MusEGlobal::song->setRecord(false); } //--------------------------------------------------------- // framesAtCycleStart // Frame count at the start of current cycle. // This is meant to be called from inside process thread only. //--------------------------------------------------------- unsigned Audio::framesAtCycleStart() const { return MusEGlobal::audioDevice->framesAtCycleStart(); } //--------------------------------------------------------- // framesSinceCycleStart // Estimated frames since the last process cycle began // This can be called from outside process thread. //--------------------------------------------------------- unsigned Audio::framesSinceCycleStart() const { // Do not round up here since time resolution is higher than (audio) frame resolution. unsigned f = muse_multiply_64_div_64_to_64(curTimeUS() - syncTimeUS, MusEGlobal::sampleRate, 1000000UL); // Safety due to inaccuracies. It cannot be after the segment, right? if(f >= MusEGlobal::segmentSize) f = MusEGlobal::segmentSize - 1; return f; // REMOVE Tim. Or keep? (During midi_engine_fixes.) // Can't use this since for the Jack driver, jack_frames_since_cycle_start is designed to be called ONLY from inside process. // return MusEGlobal::audioDevice->framesSinceCycleStart(); } //--------------------------------------------------------- // curFramePos() // Current play position frame. Estimated to single-frame resolution while in play mode. // This can be called from outside process thread. //--------------------------------------------------------- unsigned Audio::curFramePos() const { return _pos.frame() + (isPlaying() ? framesSinceCycleStart() : 0); } //--------------------------------------------------------- // curFrame // Extrapolates current play frame on syncTime/syncFrame // Estimated to single-frame resolution. // This is an always-increasing number. Good for timestamps, and // handling them during process when referenced to syncFrame. // This is meant to be called from threads other than the process thread. //--------------------------------------------------------- unsigned int Audio::curFrame() const { return MusEGlobal::audioDevice->framePos(); // REMOVE Tim. Or keep? (During midi_engine_fixes.) // Can't use this since for the Jack driver, jack_frames_since_cycle_start is designed to be called ONLY from inside process. //return framesAtCycleStart() + framesSinceCycleStart(); } //--------------------------------------------------------- // updateMidiClick // Updates the metronome tick. // Useful for after seek(), even startRolling() etc. // To be called from audio thread only. //--------------------------------------------------------- void Audio::updateMidiClick() { // Set the correct initial metronome midiClick. int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(curTickPos, &bar, &beat, &tick); if(tick) beat += 1; midiClick = MusEGlobal::sigmap.bar2tick(bar, beat, 0); // REMOVE Tim. latency. Added. TODO: Account for latency, in both the midiClick above and audioClick below ! audioClick = midiClick; } //--------------------------------------------------------- // sendMsgToGui //--------------------------------------------------------- void Audio::sendMsgToGui(char c) { write(sigFd, &c, 1); } } // namespace MusECore muse-4.0.0/src/muse/audio.h000066400000000000000000000357241404105311000154760ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: audio.h,v 1.25.2.13 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __AUDIO_H__ #define __AUDIO_H__ #include #include "type_defs.h" #include "thread.h" #include "pos.h" #include "route.h" #include "event.h" // Forward declarations: namespace MusECore { class AudioDevice; class AudioTrack; class MidiDevice; class MidiInstrument; class MidiPlayEvent; class MidiPort; class MidiTrack; class Part; class PluginI; class SynthI; class Track; class Undo; class PendingOperationList; class ExtMidiClock; //--------------------------------------------------------- // AudioMsgId // this are the messages send from the GUI thread to // the midi thread //--------------------------------------------------------- enum { SEQM_REVERT_OPERATION_GROUP, SEQM_EXECUTE_OPERATION_GROUP, SEQM_EXECUTE_PENDING_OPERATIONS, SEQM_RESET_DEVICES, SEQM_INIT_DEVICES, SEQM_PANIC, SEQM_MIDI_LOCAL_OFF, SEQM_PLAY_MIDI_EVENT, SEQM_SET_HW_CTRL_STATE, SEQM_SET_HW_CTRL_STATES, SEQM_SET_TRACK_AUTO_TYPE, SEQM_SET_AUX, SEQM_UPDATE_SOLO_STATES, AUDIO_ROUTEADD, AUDIO_ROUTEREMOVE, AUDIO_REMOVEROUTES, AUDIO_ADDPLUGIN, AUDIO_SET_PREFADER, AUDIO_SET_CHANNELS, AUDIO_SWAP_CONTROLLER_IDX, AUDIO_CLEAR_CONTROLLER_EVENTS, AUDIO_SEEK_PREV_AC_EVENT, AUDIO_SEEK_NEXT_AC_EVENT, AUDIO_ERASE_AC_EVENT, AUDIO_ERASE_RANGE_AC_EVENTS, AUDIO_ADD_AC_EVENT, AUDIO_CHANGE_AC_EVENT, AUDIO_SET_SEND_METRONOME, AUDIO_START_MIDI_LEARN, MS_PROCESS, MS_STOP, MS_SET_RTC, MS_UPDATE_POLL_FD, SEQM_IDLE, SEQM_SEEK, AUDIO_WAIT // Do nothing. Just wait for an audio cycle to pass. }; extern const char* seqMsgList[]; // for debug //--------------------------------------------------------- // Msg //--------------------------------------------------------- struct AudioMsg : public ThreadMsg { // this should be an union int serialNo; //SndFile* downmix; // DELETETHIS this is unused and probably WRONG (all SndFiles have been replaced by SndFileRs) AudioTrack* snode; AudioTrack* dnode; Route sroute, droute; AudioDevice* device; int ival; int iival; double dval; PluginI* plugin; SynthI* synth; Part* spart; Part* dpart; Track* track; const void *p1, *p2, *p3; Event ev1, ev2; char port, channel, ctrl; int a, b, c; Pos pos; Undo* operations; PendingOperationList* pendingOps; }; //--------------------------------------------------------- // Audio //--------------------------------------------------------- class Audio { public: enum State {STOP, START_PLAY, PLAY, LOOP1, LOOP2, SYNC, PRECOUNT}; enum BounceState { BounceOff = 0, BounceStart, BounceOn }; private: bool _running; // audio is active bool recording; // recording is active bool idle; // do nothing in idle mode bool _freewheel; BounceState _bounceState; unsigned _loopFrame; // Startframe of loop if in LOOP mode. Not quite the same as left marker ! int _loopCount; // Number of times we have looped so far Pos _pos; // current play position unsigned int _curCycleFrames; // Number of frames in the current process cycle. // Simulated current frame during precount. unsigned int _precountFramePos; unsigned curTickPos; // pos at start of frame during play/record unsigned nextTickPos; // pos at start of next frame during play/record // Holds a brief temporary array of sorted FRAMES of clock history, filled from the external clock history fifo. ExtMidiClock *_extClockHistory; // Holds the total capacity of the clock history list. static const int _extClockHistoryCapacity; // Holds the current size of the temporary clock history array. int _extClockHistorySize; // Holds a brief temporary array of sorted FRAMES of clock queue, to be output to midi devices. unsigned int* _clockOutputQueue; // Holds the total capacity of the clock output array. static const unsigned int _clockOutputQueueCapacity; // Holds the current size of the temporary clock output array. unsigned int _clockOutputQueueSize; // Holds a central counter, in ticks, for generating midi clock out events for all device types. unsigned int _clockOutputCounter; // Fractional accumulator for _clockOutputCounter. uint64_t _clockOutputCounterRemainder; //metronome values unsigned midiClick; unsigned audioClick; int clickno; // precount values int clicksMeasure; // Frames per beat, in precount state. unsigned framesBeat; // Frames per beat fractional divisor part. uint32_t framesBeatDivisor; // Frames per beat fractional remainder part. uint32_t framesBeatRemainder; unsigned precountTotalFrames; unsigned precountMidiClickFrame; // Fractional accumulator for precountMidiClick. uint64_t precountMidiClickFrameRemainder; // Indicates transport went from STOP to START_PLAY. // Used for sync to determine whether to start PRECOUNT mode. bool _syncPlayStarting; // Prevents flood of events during seek. float _antiSeekFloodCounter; // Whether the last call to the sync callback was ready to roll. bool _syncReady; uint64_t syncTimeUS; // Wall clock at last sync point in microseconds. unsigned syncFrame; // corresponding frame no. to syncTime State state; AudioMsg* msg; int fromThreadFdw, fromThreadFdr; // message pipe int sigFd; // pipe fd for messages to gui int sigFdr; // record values: Pos startRecordPos; Pos endRecordPos; unsigned startExternalRecTick; unsigned endExternalRecTick; long m_Xruns; // Can be called by any thread. void sendLocalOff(); bool filterEvent(const MidiPlayEvent* event, int type, bool thru); void startRolling(); void abortRolling(); void stopRolling(); void panic(); void processMsg(AudioMsg* msg); void process1(unsigned samplePos, unsigned offset, unsigned samples); void collectEvents(MidiTrack*, unsigned int startTick, unsigned int endTick, unsigned int frames, unsigned int latency_offset); void seekMidi(); // During sync(), starts count-in state if necessary and returns whether // count-in state has been started. Call from audio thread only. bool startPreCount(); // In PRECOUNT state, processes the precount events. // To be called by processMidi() in audio thread only. void processPrecount(unsigned int frames); // Updates the metronome tick. Useful for after seek(), even startRolling() etc. // Call from audio thread only. void updateMidiClick(); // Process midi for a number of frames. Call from audio thread only. // Note that nextTickPos (and friends) will already be set before calling. void processMidi(unsigned int frames); void processMidiMetronome(unsigned int frames); void processAudioMetronome(unsigned int frames); public: Audio(); virtual ~Audio(); // Access to message pipe (like from gui namespace), otherwise audio would need to depend on gui. int getFromThreadFdw() { return sigFd; } int getFromThreadFdr() { return sigFdr; } void process(unsigned frames); bool sync(int state, unsigned frame); // Called whenever the audio needs to re-sync, such as after any tempo changes. // To be called from audio thread only. void reSyncAudio(); void shutdown(); void writeTick(); // transport: // To be called from audio thread only. bool start(); void stop(bool); void seek(const Pos& pos); bool isStarting() const { return state == START_PLAY; } bool isPlaying() const { return state == PLAY || state == LOOP1 || state == LOOP2; } bool isRecording() const { return state == PLAY && recording; } void setRunning(bool val) { _running = val; } bool isRunning() const { return _running; } bool isIdle() const { return idle; } //----------------------------------------- // message interface //----------------------------------------- void msgSeek(const Pos&); void msgPlay(bool val); // For starting the transport in external sync mode. // Starts the transport immediately, bypassing waiting for transport sync, // although sync is still handled. void msgExternalPlay(bool val, bool doRewind = false); void msgExecuteOperationGroup(Undo&); // calls exe1, then calls exe2 in audio context, then calls exe3 void msgRevertOperationGroup(Undo&); // similar. // Bypass the Undo system and directly execute the pending operations. // Do a song update with accumulated flags and extra_flags, if doUpdate is true. void msgExecutePendingOperations(PendingOperationList& operations, bool doUpdate = false, SongChangedStruct_t extraFlags = 0); void msgRemoveTracks(); void msgUpdateSoloStates(); void msgSetAux(AudioTrack*, int, double); void msgPanic(); void sendMsg(AudioMsg*); bool sendMessage(AudioMsg* m, bool doUndo); void msgRemoveRoute(Route, Route); void msgRemoveRoute1(Route, Route); void msgAddRoute(Route, Route); void msgAddRoute1(Route, Route); void msgAddPlugin(AudioTrack*, int idx, PluginI* plugin); void msgSetPrefader(AudioTrack*, int); void msgSetChannels(AudioTrack*, int); void msgLocalOff(); void msgInitMidiDevices(bool force = true); void msgResetMidiDevices(); void msgIdle(bool); void msgAudioWait(); void msgBounce(); void msgSwapControllerIDX(AudioTrack*, int, int); void msgClearControllerEvents(AudioTrack*, int); void msgSeekPrevACEvent(AudioTrack*, int); void msgSeekNextACEvent(AudioTrack*, int); void msgEraseRangeACEvents(AudioTrack* node, int acid, unsigned int frame1, unsigned int frame2); void msgChangeACEvent(AudioTrack* node, int acid, int frame, int newFrame, double val); void msgSetHwCtrlState(MidiPort*, int, int, int); void msgSetHwCtrlStates(MidiPort*, int, int, int, int); void msgSetTrackAutomationType(Track*, int); void msgSetSendMetronome(AudioTrack*, bool); void msgStartMidiLearn(); void msgPlayMidiEvent(const MidiPlayEvent* event); // If instrument is given it will be set, otherwise it won't touch the existing instrument. void msgSetMidiDevice(MidiPort* port, MidiDevice* device, MidiInstrument* instrument = nullptr); void midiPortsChanged(); const Pos& pos() const { return _pos; } // Number of frames in the current process cycle. unsigned curCycleFrames() const { return _curCycleFrames; } const Pos& getStartRecordPos() const { return startRecordPos; } const Pos& getEndRecordPos() const { return endRecordPos; } unsigned getStartExternalRecTick() const { return startExternalRecTick; } unsigned getEndExternalRecTick() const { return endExternalRecTick; } int loopCount() { return _loopCount; } // Number of times we have looped so far unsigned loopFrame() { return _loopFrame; } unsigned tickPos() const { return curTickPos; } unsigned nextTick() const { return nextTickPos; } // Extrapolates current play frame on syncTime/syncFrame // Estimated to single-frame resolution. // This is an always-increasing number. Good for timestamps, and // handling them during process when referenced to syncFrame. // This is meant to be called from threads other than the process thread. unsigned curFrame() const; // Current play position frame. Estimated to single-frame resolution while in play mode. // This can be called from outside process thread. unsigned curFramePos() const; // This is meant to be called from inside process thread only. unsigned framesAtCycleStart() const; // Same as framesAtCycleStart(), but can be called from outside process thread. unsigned curSyncFrame() const { return syncFrame; } // This can be called from outside process thread. unsigned framesSinceCycleStart() const; // Convert tick to frame using the external clock history list. // The function takes a tick relative to zero (ie. relative to the first event in a processing batch). // The returned clock frames occurred during the previous audio cycle(s), so you may want to shift // the frames forward by one audio segment size for scheduling purposes. // CAUTION: There must be at least one valid clock in the history, otherwise it returns zero. // Don't feed this a tick greater than or equal to the next tick, it will simply return the // very last frame, which is not very useful since that will just bunch the events // together at the last frame. unsigned int extClockHistoryTick2Frame(unsigned int tick) const; unsigned int extClockHistoryFrame2Tick(unsigned int frame) const; void recordStop(bool restart = false, Undo* operations = NULL); bool freewheel() const { return _freewheel; } void setFreewheel(bool val); void initDevices(bool force = true); void sendMsgToGui(char c); bool bounce() const { return _bounceState == BounceStart || _bounceState == BounceOn; } long getXruns() { return m_Xruns; } void resetXruns() { m_Xruns = 0; } void incXruns() { m_Xruns++; } }; extern int processAudio(unsigned long, void*); extern void processAudio1(void*, void*); } // namespace MusECore namespace MusEGlobal { extern MusECore::Audio* audio; extern MusECore::AudioDevice* audioDevice; // current audio device in use } #endif muse-4.0.0/src/muse/audio_fifo.cpp000066400000000000000000000167341404105311000170340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: node.cpp,v 1.36.2.25 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "audio_fifo.h" #include "globals.h" #include "al/dsp.h" //#define FIFO_DEBUG namespace MusECore { //--------------------------------------------------------- // Fifo //--------------------------------------------------------- Fifo::Fifo() { muse_atomic_init(&count); nbuffer = MusEGlobal::fifoLength; buffer = new FifoBuffer*[nbuffer]; for (int i = 0; i < nbuffer; ++i) buffer[i] = new FifoBuffer; clear(); } Fifo::~Fifo() { for (int i = 0; i < nbuffer; ++i) { if(buffer[i]->buffer) free(buffer[i]->buffer); delete buffer[i]; } delete[] buffer; muse_atomic_destroy(&count); } void Fifo::clear() { #ifdef FIFO_DEBUG fprintf(stderr, "FIFO::clear count:%d\n", muse_atomic_read(&count)); #endif ridx = 0; widx = 0; muse_atomic_set(&count, 0); } //--------------------------------------------------------- // put // return true if fifo full //--------------------------------------------------------- bool Fifo::put(int segs, MuseCount_t samples, float** src, MuseCount_t pos, float latency) { #ifdef FIFO_DEBUG fprintf(stderr, "FIFO::put segs:%d samples:%ld pos:%ld count:%d\n", segs, samples, pos, muse_atomic_read(&count)); #endif if (muse_atomic_read(&count) == nbuffer) { fprintf(stderr, "FIFO %p overrun... %d\n", this, muse_atomic_read(&count)); return true; } FifoBuffer* b = buffer[widx]; MuseCount_t n = segs * samples; if (b->maxSize < n) { if (b->buffer) { free(b->buffer); b->buffer = 0; } #ifdef _WIN32 b->buffer = (float *) _aligned_malloc(16, sizeof(float *) * n); if(b->buffer == NULL) { fprintf(stderr, "Fifo::put could not allocate buffer segs:%d samples:%lu pos:%u\n", segs, samples, pos); return true; } #else int rv = posix_memalign((void**)&(b->buffer), 16, sizeof(float) * n); if(rv != 0 || !b->buffer) { fprintf(stderr, "Fifo::put could not allocate buffer segs:%d samples:%ld pos:%ld\n", segs, samples, pos); return true; } #endif b->maxSize = n; } if(!b->buffer) { fprintf(stderr, "Fifo::put no buffer! segs:%d samples:%ld pos:%ld\n", segs, samples, pos); return true; } b->size = samples; b->segs = segs; b->pos = pos; b->latency = latency; for (int i = 0; i < segs; ++i) { if(!src[i]) return true; AL::dsp->cpy(b->buffer + i * samples, src[i], samples); } add(); return false; } //--------------------------------------------------------- // peek // return true if fifo empty //--------------------------------------------------------- bool Fifo::peek(int segs, MuseCount_t samples, float** dst, MuseCount_t* pos, float* latency) //const { #ifdef FIFO_DEBUG fprintf(stderr, "FIFO::peek/get segs:%d samples:%ld count:%d\n", segs, samples, muse_atomic_read(&count)); #endif // Non-const peek required because of this. if (muse_atomic_read(&count) == 0) { fprintf(stderr, "FIFO %p underrun\n", this); return true; } FifoBuffer* b = buffer[ridx]; if(!b->buffer) { fprintf(stderr, "Fifo::peek/get no buffer! segs:%d samples:%ld b->pos:%ld\n", segs, samples, b->pos); return true; } if (pos) *pos = b->pos; if (latency) *latency = b->latency; for (int i = 0; i < segs; ++i) dst[i] = b->buffer + samples * (i % b->segs); return false; } //--------------------------------------------------------- // get // return true if fifo empty //--------------------------------------------------------- bool Fifo::get(int segs, MuseCount_t samples, float** dst, MuseCount_t* pos, float* latency) { if(peek(segs, samples, dst, pos, latency)) return true; remove(); return false; } int Fifo::getCount() { return muse_atomic_read(&count); } int Fifo::getEmptyCount() { return nbuffer - muse_atomic_read(&count); } bool Fifo::isEmpty() { return muse_atomic_read(&count) == 0; } //--------------------------------------------------------- // remove //--------------------------------------------------------- void Fifo::remove() { #ifdef FIFO_DEBUG fprintf(stderr, "Fifo::remove count:%d\n", muse_atomic_read(&count)); #endif ridx = (ridx + 1) % nbuffer; muse_atomic_dec(&count); } //--------------------------------------------------------- // getWriteBuffer //--------------------------------------------------------- bool Fifo::getWriteBuffer(int segs, MuseCount_t samples, float** buf, MuseCount_t pos) { #ifdef FIFO_DEBUG fprintf(stderr, "Fifo::getWriteBuffer segs:%d samples:%ld pos:%ld\n", segs, samples, pos); #endif if (muse_atomic_read(&count) == nbuffer) return true; FifoBuffer* b = buffer[widx]; MuseCount_t n = segs * samples; if (b->maxSize < n) { if (b->buffer) { free(b->buffer); b->buffer = 0; } int rv = posix_memalign((void**)&(b->buffer), 16, sizeof(float) * n); if(rv != 0 || !b->buffer) { fprintf(stderr, "Fifo::getWriteBuffer could not allocate buffer segs:%d samples:%ld pos:%ld\n", segs, samples, pos); return true; } b->maxSize = n; } if(!b->buffer) { fprintf(stderr, "Fifo::getWriteBuffer no buffer! segs:%d samples:%ld pos:%ld\n", segs, samples, pos); return true; } for (int i = 0; i < segs; ++i) buf[i] = b->buffer + i * samples; b->size = samples; b->segs = segs; b->pos = pos; return false; } //--------------------------------------------------------- // add //--------------------------------------------------------- void Fifo::add() { #ifdef FIFO_DEBUG fprintf(stderr, "Fifo::add count:%d\n", muse_atomic_read(&count)); #endif widx = (widx + 1) % nbuffer; muse_atomic_inc(&count); } } // namespace MusECore muse-4.0.0/src/muse/audio_fifo.h000066400000000000000000000046211404105311000164710ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // audio_fifo.h // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __AUDIO_FIFO_H__ #define __AUDIO_FIFO_H__ #include "muse_atomic.h" #include "muse_time.h" namespace MusECore { //--------------------------------------------------------- // Fifo //--------------------------------------------------------- struct FifoBuffer { float* buffer; MuseCount_t size; MuseCount_t maxSize; MuseCount_t pos; int segs; float latency; FifoBuffer() { buffer = 0; size = 0; maxSize = 0; pos = 0; segs = 0; latency = 0.0f; } }; class Fifo { int nbuffer; int ridx; // read index; only touched by reader int widx; // write index; only touched by writer muse_atomic_t count; // buffer count; writer increments, reader decrements FifoBuffer** buffer; public: Fifo(); ~Fifo(); void clear(); bool put(int segs, MuseCount_t samples, float** buffer, MuseCount_t pos, float latency); bool getWriteBuffer(int, MuseCount_t, float** buffer, MuseCount_t pos); void add(); bool peek(int segs, MuseCount_t samples, float** buffer, MuseCount_t* pos = 0, float* latency = 0); // const; bool get(int segs, MuseCount_t samples, float** buffer, MuseCount_t* pos = 0, float* latency = 0); void remove(); int getCount(); int getEmptyCount(); bool isEmpty(); }; } // namespace MusECore #endif muse-4.0.0/src/muse/audioprefetch.cpp000066400000000000000000000251401404105311000175410ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: audioprefetch.cpp,v 1.14.2.7 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef _WIN32 #include #endif #include #include #include #include "audioprefetch.h" #include "globals.h" #include "track.h" #include "song.h" #include "audio.h" #include "sync.h" // For debugging transport timing: Uncomment the fprintf section. #define AUDIO_PREFETCH_DEBUG_TRANSPORT_SYNC(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGlobal { MusECore::AudioPrefetch* audioPrefetch = nullptr; } namespace MusECore { void initAudioPrefetch() { MusEGlobal::audioPrefetch = new AudioPrefetch("Prefetch"); } //#define AUDIOPREFETCH_DEBUG enum { PREFETCH_TICK, PREFETCH_SEEK }; //--------------------------------------------------------- // PrefetchMsg //--------------------------------------------------------- struct PrefetchMsg : public ThreadMsg { int pos; bool _isPlayTick; bool _isRecTick; }; //--------------------------------------------------------- // AudioPrefetch //--------------------------------------------------------- AudioPrefetch::AudioPrefetch(const char* name) : Thread(name) { seekPos = ~0; seekCount = 0; } //--------------------------------------------------------- // readMsg //--------------------------------------------------------- static void readMsgP(void* p, void*) { AudioPrefetch* at = (AudioPrefetch*)p; at->readMsg1(sizeof(PrefetchMsg)); } //--------------------------------------------------------- // start //--------------------------------------------------------- void AudioPrefetch::start(int priority, void *) { clearPollFd(); addPollFd(toThreadFdr, POLLIN, MusECore::readMsgP, this, 0); Thread::start(priority); } //--------------------------------------------------------- // ~AudioPrefetch //--------------------------------------------------------- AudioPrefetch::~AudioPrefetch() { } //--------------------------------------------------------- // processMsg //--------------------------------------------------------- void AudioPrefetch::processMsg1(const void* m) { const PrefetchMsg* msg = (PrefetchMsg*)m; switch(msg->id) { case PREFETCH_TICK: if(msg->_isRecTick) // Was the tick generated when audio record was on? { #ifdef AUDIOPREFETCH_DEBUG fprintf(stderr, "AudioPrefetch::processMsg1: PREFETCH_TICK: isRecTick\n"); #endif MusEGlobal::audio->writeTick(); } // Indicate do not seek file before each read. if(msg->_isPlayTick) // Was the tick generated when audio playback was on? { #ifdef AUDIOPREFETCH_DEBUG fprintf(stderr, "AudioPrefetch::processMsg1: PREFETCH_TICK: isPlayTick\n"); #endif prefetch(false); } seekPos = ~0; // invalidate cached last seek position break; case PREFETCH_SEEK: #ifdef AUDIOPREFETCH_DEBUG fprintf(stderr, "AudioPrefetch::processMsg1 PREFETCH_SEEK msg->pos:%d\n", msg->pos); #endif // process seek in background seek(msg->pos); break; default: fprintf(stderr, "AudioPrefetch::processMsg1: unknown message\n"); } } //--------------------------------------------------------- // msgTick //--------------------------------------------------------- void AudioPrefetch::msgTick(bool isRecTick, bool isPlayTick) { PrefetchMsg msg; msg.id = PREFETCH_TICK; msg.pos = 0; // seems to be unused, was uninitialized. msg._isRecTick = isRecTick; msg._isPlayTick = isPlayTick; while (sendMsg1(&msg, sizeof(msg))) { fprintf(stderr, "AudioPrefetch::msgTick(): send failed!\n"); } } //--------------------------------------------------------- // msgSeek // called from audio RT context //--------------------------------------------------------- void AudioPrefetch::msgSeek(unsigned samplePos, bool force) { if (samplePos == seekPos && !force) return; ++seekCount; #ifdef AUDIOPREFETCH_DEBUG fprintf(stderr, "AudioPrefetch::msgSeek samplePos:%u force:%d seekCount:%d\n", samplePos, force, seekCount); #endif PrefetchMsg msg; msg.id = PREFETCH_SEEK; msg.pos = samplePos; while (sendMsg1(&msg, sizeof(msg))) { fprintf(stderr, "AudioPrefetch::msgSeek::sleep(1)\n"); sleep(1); } } //--------------------------------------------------------- // prefetch //--------------------------------------------------------- void AudioPrefetch::prefetch(bool doSeek) { unsigned lpos_frame = 0; unsigned rpos_frame = 0; const bool do_loops = MusEGlobal::song->loop() && !MusEGlobal::audio->bounce() && !MusEGlobal::extSyncFlag; if(do_loops) { lpos_frame = MusEGlobal::song->lPos().frame(); rpos_frame = MusEGlobal::song->rPos().frame(); } WaveTrackList* tl = MusEGlobal::song->waves(); for (iWaveTrack it = tl->begin(); it != tl->end(); ++it) { WaveTrack* track = *it; // Save time. Don't bother if track is off. Track On/Off not designed for rapid repeated response (but mute is). (p3.3.29) if(track->off()) continue; Fifo* fifo = track->prefetchFifo(); const int empty_count = fifo->getEmptyCount(); // Nothing to fill? if(empty_count <= 0) { AUDIO_PREFETCH_DEBUG_TRANSPORT_SYNC(stderr, "AudioPrefetch::prefetch: empty_count <= 0!\n"); continue; } unsigned int write_pos = track->prefetchWritePos(); if (write_pos == ~0U) { fprintf(stderr, "AudioPrefetch::prefetch: invalid track write position\n"); continue; } int ch = track->channels(); float* bp[ch]; AUDIO_PREFETCH_DEBUG_TRANSPORT_SYNC(stderr, "AudioPrefetch::prefetch: Filling empty_count:%d do_loops:%d lpos_frame:%d rpos_frame:%d\n", empty_count, do_loops, lpos_frame, rpos_frame); // Fill up the empty buffers. for(int i = 0; i < empty_count; ++i) { if(do_loops) { unsigned n = rpos_frame - write_pos; AUDIO_PREFETCH_DEBUG_TRANSPORT_SYNC(stderr, " do loops: write_pos:%d n:%d segmentSize:%d\n", write_pos, n, MusEGlobal::segmentSize); if (n < MusEGlobal::segmentSize) { // adjust loop start so we get exact loop len if (n > lpos_frame) n = 0; write_pos = lpos_frame - n; AUDIO_PREFETCH_DEBUG_TRANSPORT_SYNC(stderr, " looping: new write_pos:%d\n", write_pos); track->setPrefetchWritePos(write_pos); track->seekData(write_pos); } } if (fifo->getWriteBuffer(ch, MusEGlobal::segmentSize, bp, write_pos)) { fprintf(stderr, "AudioPrefetch::prefetch: No write buffer!\n"); break; } // True = do overwrite. track->fetchData(write_pos, MusEGlobal::segmentSize, bp, doSeek, true); // Only the first fetch should seek if required. Reset the flag now. doSeek = false; write_pos += MusEGlobal::segmentSize; track->setPrefetchWritePos(write_pos); } } } //--------------------------------------------------------- // seek //--------------------------------------------------------- void AudioPrefetch::seek(unsigned seekTo) { #ifdef AUDIOPREFETCH_DEBUG fprintf(stderr, "AudioPrefetch::seek to:%u seekCount:%d\n", seekTo, seekCount); #endif // Speedup: More than one seek message pending? // Eat up seek messages until we get to the very LATEST one, // because all the rest which came before it are irrelevant now, // and processing them all was taking extreme time, especially with // resampling enabled. // In particular, when the user 'slides' the play cursor back and forth // there are MANY seek messages in the pipe, and with resampling enabled // it was taking minutes to finish seeking. If the user hit play during that time, // things were messed up (FIFO underruns, choppy intermittent sound etc). // Added by Tim. p3.3.20 if(seekCount > 1) { --seekCount; return; } WaveTrackList* tl = MusEGlobal::song->waves(); for (iWaveTrack it = tl->begin(); it != tl->end(); ++it) { WaveTrack* track = *it; track->clearPrefetchFifo(); track->setPrefetchWritePos(seekTo); track->seekData(seekTo); } // Indicate do a seek command before read (only on the first fetch). prefetch(true); // To help speed things up even more, check the count again. Return if more seek messages are pending. (p3.3.20) if(seekCount > 1) { --seekCount; return; } seekPos = seekTo; --seekCount; } } // namespace MusECore muse-4.0.0/src/muse/audioprefetch.h000066400000000000000000000036001404105311000172030ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: audioprefetch.h,v 1.3.2.2 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __AUDIOPREFETCH_H__ #define __AUDIOPREFETCH_H__ #include "thread.h" namespace MusECore { //--------------------------------------------------------- // AudioPrefetch //--------------------------------------------------------- class AudioPrefetch : public Thread { unsigned seekPos; // remember last seek to optimize seeks virtual void processMsg1(const void*); void prefetch(bool doSeek); void seek(unsigned pos); volatile int seekCount; public: AudioPrefetch(const char* name); ~AudioPrefetch(); virtual void start(int, void* pty = NULL); void msgTick(bool isRecTick, bool isPlayTick); void msgSeek(unsigned samplePos, bool force=false); bool seekDone() const { return seekCount == 0; } }; } // namespace MusECore namespace MusEGlobal { extern MusECore::AudioPrefetch* audioPrefetch; } #endif muse-4.0.0/src/muse/audiotrack.cpp000066400000000000000000002774471404105311000170700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: audiotrack.cpp,v 1.14.2.21 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) // (C) Copyright 2013 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 "globals.h" #include "globaldefs.h" #include "track.h" #include "event.h" #include "song.h" #include "audio.h" #include "wave.h" #include "xml.h" #include "undo.h" #include "plugin.h" #include "audiodev.h" #include "synth.h" #include "dssihost.h" #include "vst_native.h" #include "app.h" #include "controlfifo.h" #include "fastlog.h" #include "gconfig.h" #include "latency_compensator.h" #include "ticksynth.h" namespace MusECore { bool AudioAux::_isVisible=true; bool AudioInput::_isVisible=true; bool AudioOutput::_isVisible=true; bool AudioGroup::_isVisible =true; bool WaveTrack::_isVisible=true; // DELETETHIS 40. this caching stuff seems to be not used any more // By T356. For caching jack in/out routing names BEFORE file save. // Jack often shuts down during file save, causing the routes to be lost in the file. // cacheJackRouteNames() is ONLY called from MusE::save() in app.cpp // Update: Not required any more because the real problem was Jack RT priority, which has been fixed. // Keep this around for now. It may come in handy if we want to preserve route names with dummy audio driver! /* typedef std::multimap jackRouteNameMap; std::map jackRouteNameCache; typedef std::multimap ::const_iterator ciJackRouteNameMap; typedef std::map ::const_iterator ciJackRouteNameCache; void cacheJackRouteNames() { jackRouteNameCache.clear(); const InputList* il = MusEGlobal::song->inputs(); for(ciAudioInput iai = il->begin(); iai != il->end(); ++iai) { const RouteList* rl = (*iai)->inRoutes(); if(!rl->empty()) { jackRouteNameMap rm = jackRouteNameMap(); for(ciRoute r = rl->begin(); r != rl->end(); ++r) rm.insert(std::pair(r->channel, r->name())); jackRouteNameCache.insert(std::pair(*iai, rm)); } } const OutputList* ol = MusEGlobal::song->outputs(); for(ciAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) { const RouteList* rl = (*iao)->outRoutes(); if(!rl->empty()) { jackRouteNameMap rm = jackRouteNameMap(); for(ciRoute r = rl->begin(); r != rl->end(); ++r) rm.insert(std::pair(r->channel, r->name())); jackRouteNameCache.insert(std::pair(*iao, rm)); } } } */ //--------------------------------------------------------- // init_buffers //--------------------------------------------------------- void AudioTrack::initBuffers() { int chans = _totalOutChannels; // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. if(chans < MusECore::MAX_CHANNELS) chans = MusECore::MAX_CHANNELS; if(!outBuffers) { outBuffers = new float*[chans]; for(int i = 0; i < chans; ++i) { #ifdef _WIN32 outBuffers[i] = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(outBuffers[i] == NULL) { fprintf(stderr, "ERROR: AudioTrack::init_buffers: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)&outBuffers[i], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif } } for(int i = 0; i < chans; ++i) { if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) outBuffers[i][q] = MusEGlobal::denormalBias; } else memset(outBuffers[i], 0, sizeof(float) * MusEGlobal::segmentSize); } if(!outBuffersExtraMix) { outBuffersExtraMix = new float*[MusECore::MAX_CHANNELS]; for(int i = 0; i < MusECore::MAX_CHANNELS; ++i) { #ifdef _WIN32 outBuffersExtraMix[i] = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(outBuffersExtraMix[i] == NULL) { fprintf(stderr, "ERROR: AudioTrack::init_buffers: _aligned_malloc outBuffersMonoMix returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)&outBuffersExtraMix[i], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign outBuffersMonoMix returned error:%d. Aborting!\n", rv); abort(); } #endif } } for(int i = 0; i < MusECore::MAX_CHANNELS; ++i) { if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) outBuffersExtraMix[i][q] = MusEGlobal::denormalBias; } else memset(outBuffersExtraMix[i], 0, sizeof(float) * MusEGlobal::segmentSize); } if(!_dataBuffers) { _dataBuffers = new float*[_totalOutChannels]; for(int i = 0; i < _totalOutChannels; ++i) { #ifdef _WIN32 _dataBuffers[i] = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(_dataBuffers[i] == NULL) { fprintf(stderr, "ERROR: AudioTrack::init_buffers: _aligned_malloc _dataBuffers returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)&_dataBuffers[i], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign _dataBuffers returned error:%d. Aborting!\n", rv); abort(); } #endif } } for(int i = 0; i < _totalOutChannels; ++i) { if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) _dataBuffers[i][q] = MusEGlobal::denormalBias; } else memset(_dataBuffers[i], 0, sizeof(float) * MusEGlobal::segmentSize); } if(!audioInSilenceBuf) { #ifdef _WIN32 audioInSilenceBuf = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(audioInSilenceBuf == NULL) { fprintf(stderr, "ERROR: AudioTrack::init_buffers: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)&audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) audioInSilenceBuf[q] = MusEGlobal::denormalBias; } else memset(audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); } if(!audioOutDummyBuf) { #ifdef _WIN32 audioOutDummyBuf = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(audioOutDummyBuf == NULL) { fprintf(stderr, "ERROR: AudioTrack::init_buffers: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)&audioOutDummyBuf, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: AudioTrack::init_buffers: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) audioOutDummyBuf[q] = MusEGlobal::denormalBias; } else memset(audioOutDummyBuf, 0, sizeof(float) * MusEGlobal::segmentSize); } if(!_controls && _controlPorts != 0) { _controls = new Port[_controlPorts]; ciCtrlList icl = _controller.begin(); for(unsigned long k = 0; k < _controlPorts; ++k) { double val = 0.0; if(icl != _controller.end()) { // Since the list is sorted by id, if no match is found just let k catch up to the id. if((unsigned long)icl->second->id() == k) { val = icl->second->getDefault(); ++icl; } } _controls[k].idx = k; _controls[k].dval = val; _controls[k].enCtrl = true; } } } //--------------------------------------------------------- // AudioTrack //--------------------------------------------------------- AudioTrack::AudioTrack(TrackType t, int channels) : Track(t) { _totalOutChannels = MAX_CHANNELS; _latencyComp = new LatencyCompensator(); _recFilePos = 0; _previousLatency = 0.0f; _processed = false; _haveData = false; _sendMetronome = false; _prefader = false; _efxPipe = new Pipeline(); recFileNumber = 1; _channels = 0; _automationType = AUTO_OFF; setChannels(channels); addController(new CtrlList(AC_VOLUME,"Volume",0.001,3.163 /* roughly 10 db */, VAL_LOG)); addController(new CtrlList(AC_PAN, "Pan", -1.0, 1.0, VAL_LINEAR)); addController(new CtrlList(AC_MUTE,"Mute",0.0,1.0, VAL_LINEAR, true /*don't show in arranger */)); _controlPorts = 3; _curVolume = 0.0; _curVol1 = 0.0; _curVol2 = 0.0; _controls = 0; outBuffers = 0; outBuffersExtraMix = 0; audioInSilenceBuf = 0; audioOutDummyBuf = 0; _dataBuffers = 0; // This is only set by multi-channel syntis... _totalInChannels = 0; initBuffers(); setVolume(1.0); _gain = 1.0; } AudioTrack::AudioTrack(const AudioTrack& t, int flags) : Track(t, flags) { _latencyComp = new LatencyCompensator(); _recFilePos = 0; _previousLatency = 0.0f; _processed = false; _haveData = false; _efxPipe = new Pipeline(); // Start off with a new pipeline. recFileNumber = 1; addController(new CtrlList(AC_VOLUME,"Volume",0.001,3.163 /* roughly 10 db */, VAL_LOG)); addController(new CtrlList(AC_PAN, "Pan", -1.0, 1.0, VAL_LINEAR)); addController(new CtrlList(AC_MUTE,"Mute",0.0,1.0, VAL_LINEAR, true /*don't show in arranger */)); _controlPorts = 3; _curVolume = 0.0; _curVol1 = 0.0; _curVol2 = 0.0; // Don't allocate outBuffers here. Let internal_assign() call setTotalOutChannels to set them up. _controls = 0; outBuffers = 0; outBuffersExtraMix = 0; audioInSilenceBuf = 0; audioOutDummyBuf = 0; _dataBuffers = 0; _totalOutChannels = 0; // This is only set by multi-channel syntis... _totalInChannels = 0; _recFile = NULL; internal_assign(t, flags | ASSIGN_PROPERTIES); } void AudioTrack::internal_assign(const Track& t, int flags) { if(t.isMidiTrack()) return; const AudioTrack& at = (const AudioTrack&)t; if(flags & ASSIGN_PROPERTIES) { _sendMetronome = at._sendMetronome; _prefader = at._prefader; _auxSend = at._auxSend; _automationType = at._automationType; _gain = at._gain; if(!(flags & ASSIGN_STD_CTRLS)) { // Copy the standard controller block... ciCtrlList icl = at._controller.begin(); ciCtrlList icl_this = _controller.begin(); ciCtrlList icl_end = at._controller.lower_bound(AC_PLUGIN_CTL_BASE); ciCtrlList icl_this_end = _controller.lower_bound(AC_PLUGIN_CTL_BASE); int id, id_this; CtrlList* cl, *cl_this; while(icl != icl_end && icl_this != icl_this_end) { cl = icl->second; cl_this = icl_this->second; id = cl->id(); id_this = cl_this->id(); if(id < id_this) ++icl; // Let id catch up to this id. else if(id > id_this) ++icl_this; // Let this id catch up to id. else { // Match found. Copy properties but not values. cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES); ++icl; ++icl_this; } } // Copy the special synth controller block... const int synth_id = (int)genACnum(MusECore::MAX_PLUGINS, 0); // The beginning of the special synth controller block. const int synth_id_end = synth_id + AC_PLUGIN_CTL_BASE; // The end of the special block. icl = at._controller.lower_bound(synth_id); icl_this = _controller.lower_bound(synth_id); icl_end = at._controller.lower_bound(synth_id_end); icl_this_end = _controller.lower_bound(synth_id_end); while(icl != icl_end && icl_this != icl_this_end) { cl = icl->second; cl_this = icl_this->second; id = cl->id(); id_this = cl_this->id(); if(id < id_this) ++icl; // Let id catch up to this id. else if(id > id_this) ++icl_this; // Let this id catch up to id. else { // Match found. Copy properties but not values. cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES); ++icl; ++icl_this; } } } // This will set up or reallocate the outBuffers. _controlPorts must be valid by now. setTotalOutChannels(at._totalOutChannels); // This is only set by multi-channel syntis... setTotalInChannels(at._totalInChannels); // FIXME: setChannels also called in setTotalOutChannels above, causing redundant efxpipe setChannels. setChannels(at.channels()); // Set track channels (max 2). unsigned long cp = _controlPorts; if(at._controlPorts < cp) cp = at._controlPorts; for(unsigned long k = 0; k < cp; ++k) _controls[k] = at._controls[k]; // Assign the structures. } if(flags & ASSIGN_PLUGINS) { delete _efxPipe; _efxPipe = new Pipeline(*(at._efxPipe), this); // Make copies of the plugins. } if(flags & (ASSIGN_STD_CTRLS | ASSIGN_PLUGIN_CTRLS)) { const int synth_id = (int)genACnum(MusECore::MAX_PLUGINS, 0); // The beginning of the special synth controller block. const int synth_id_end = synth_id + AC_PLUGIN_CTL_BASE; // The end of the special block. ciCtrlList icl, icl_end, icl_this, icl_this_end; int id, id_this; CtrlList* cl, *cl_this; if(flags & ASSIGN_STD_CTRLS) { // Copy the standard controller block... icl = at._controller.begin(); icl_this = _controller.begin(); icl_end = at._controller.lower_bound(AC_PLUGIN_CTL_BASE); icl_this_end = _controller.lower_bound(AC_PLUGIN_CTL_BASE); while(icl != icl_end && icl_this != icl_this_end) { cl = icl->second; cl_this = icl_this->second; id = cl->id(); id_this = cl_this->id(); if(id < id_this) ++icl; // Let id catch up to this id. else if(id > id_this) ++icl_this; // Let this id catch up to id. else { // Match found. Copy properties and values. cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); ++icl; ++icl_this; } } // Copy the special synth controller block... icl = at._controller.lower_bound(synth_id); icl_this = _controller.lower_bound(synth_id); icl_end = at._controller.lower_bound(synth_id_end); icl_this_end = _controller.lower_bound(synth_id_end); while(icl != icl_end && icl_this != icl_this_end) { cl = icl->second; cl_this = icl_this->second; id = cl->id(); id_this = cl_this->id(); if(id < id_this) ++icl; // Let id catch up to this id. else if(id > id_this) ++icl_this; // Let this id catch up to id. else { // Match found. Copy properties and values. cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); ++icl; ++icl_this; } } } if(flags & ASSIGN_PLUGIN_CTRLS) { // Copy all plugin controller blocks... icl = at._controller.lower_bound(AC_PLUGIN_CTL_BASE); icl_this = _controller.lower_bound(AC_PLUGIN_CTL_BASE); icl_end = at._controller.lower_bound(synth_id); icl_this_end = _controller.lower_bound(synth_id); while(icl != icl_end && icl_this != icl_this_end) { cl = icl->second; cl_this = icl_this->second; id = cl->id(); id_this = cl_this->id(); if(id < id_this) ++icl; // Let id catch up to this id. else if(id > id_this) ++icl_this; // Let this id catch up to id. else { // Match found. Copy properties and values. cl_this->assign(*cl, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); ++icl; ++icl_this; } } } } if(flags & ASSIGN_ROUTES) { for(ciRoute ir = at._inRoutes.begin(); ir != at._inRoutes.end(); ++ir) { // Defer all Jack routes to Audio Input and Output copy constructors or assign ! if(ir->type == Route::JACK_ROUTE) continue; // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. _inRoutes.push_back(*ir); } for(ciRoute ir = at._outRoutes.begin(); ir != at._outRoutes.end(); ++ir) { // Defer all Jack routes to Audio Input and Output copy constructors or assign ! if(ir->type == Route::JACK_ROUTE) continue; // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. _outRoutes.push_back(*ir); } } else if(flags & ASSIGN_DEFAULT_ROUTES) { // // add default route to master // OutputList* ol = MusEGlobal::song->outputs(); if (!ol->empty()) { AudioOutput* ao = ol->front(); switch(type()) { case Track::WAVE: case Track::AUDIO_AUX: // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. _outRoutes.push_back(Route(ao)); break; // It should actually never get here now, but just in case. case Track::AUDIO_SOFTSYNTH: // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. // Add an Omni route. _outRoutes.push_back(Route(ao)); break; default: break; } } } } void AudioTrack::assign(const Track& t, int flags) { Track::assign(t, flags); internal_assign(t, flags); } AudioTrack::~AudioTrack() { delete _efxPipe; if(audioInSilenceBuf) free(audioInSilenceBuf); if(audioOutDummyBuf) free(audioOutDummyBuf); if(_latencyComp) delete _latencyComp; if(_dataBuffers) { for(int i = 0; i < _totalOutChannels; ++i) { if(_dataBuffers[i]) free(_dataBuffers[i]); } delete[] _dataBuffers; } if(outBuffersExtraMix) { for(int i = 0; i < MusECore::MAX_CHANNELS; ++i) { if(outBuffersExtraMix[i]) free(outBuffersExtraMix[i]); } delete[] outBuffersExtraMix; } int chans = _totalOutChannels; // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. if(chans < MusECore::MAX_CHANNELS) chans = MusECore::MAX_CHANNELS; if(outBuffers) { for(int i = 0; i < chans; ++i) { if(outBuffers[i]) free(outBuffers[i]); } delete[] outBuffers; } if(_controls) delete[] _controls; _controller.clearDelete(); } //--------------------------------------------------------- // deleteAllEfxGuis //--------------------------------------------------------- void AudioTrack::deleteAllEfxGuis() { if(_efxPipe) _efxPipe->deleteAllGuis(); } //--------------------------------------------------------- // clearEfxList //--------------------------------------------------------- void AudioTrack::clearEfxList() { if(_efxPipe) for(int i = 0; i < MusECore::PipelineDepth; i++) (*_efxPipe)[i] = 0; } //--------------------------------------------------------- // newPart //--------------------------------------------------------- Part* AudioTrack::newPart(Part*, bool /*clone*/) { return 0; } //--------------------------------------------------------- // addPlugin //--------------------------------------------------------- void AudioTrack::addPlugin(PluginI* plugin, int idx) { if (plugin == 0) { PluginI* oldPlugin = (*_efxPipe)[idx]; if (oldPlugin) { oldPlugin->setID(-1); oldPlugin->setTrack(0); int controller = oldPlugin->parameters(); for (int i = 0; i < controller; ++i) { int id = genACnum(idx, i); removeController(id); } } } efxPipe()->insert(plugin, idx); setupPlugin(plugin, idx); } //--------------------------------------------------------- // setupPlugin //--------------------------------------------------------- void AudioTrack::setupPlugin(PluginI* plugin, int idx) { if (plugin) { plugin->setID(idx); plugin->setTrack(this); int controller = plugin->parameters(); for (int i = 0; i < controller; ++i) { int id = genACnum(idx, i); const char* name = plugin->paramName(i); float min, max; plugin->range(i, &min, &max); CtrlList* cl = new CtrlList(id); cl->setRange(min, max); cl->setName(QString(name)); cl->setValueType(plugin->ctrlValueType(i)); cl->setMode(plugin->ctrlMode(i)); cl->setCurVal(plugin->param(i)); addController(cl); } } } //--------------------------------------------------------- // addAuxSend //--------------------------------------------------------- void AudioTrack::addAuxSend(int n) { int nn = _auxSend.size(); for (int i = nn; i < n; ++i) { _auxSend.push_back(0.0); _auxSend[i] = 0.0; //?? } } //--------------------------------------------------------- // addController //--------------------------------------------------------- void AudioTrack::addController(CtrlList* list) { _controller.add(list); } //--------------------------------------------------------- // removeController //--------------------------------------------------------- void AudioTrack::removeController(int id) { AudioMidiCtrlStructMap amcs; _controller.midiControls()->find_audio_ctrl_structs(id, &amcs); for(ciAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++ iamcs) _controller.midiControls()->erase(*iamcs); iCtrlList i = _controller.find(id); if (i == _controller.end()) { printf("AudioTrack::removeController id %d not found\n", id); return; } _controller.erase(i); } //--------------------------------------------------------- // swapControllerIDX //--------------------------------------------------------- void AudioTrack::swapControllerIDX(int idx1, int idx2) { if(idx1 == idx2 || idx1 < 0 || idx2 < 0 || idx1 >= MusECore::PipelineDepth || idx2 >= MusECore::PipelineDepth) return; CtrlList *cl; CtrlList *newcl; int id1 = (idx1 + 1) * AC_PLUGIN_CTL_BASE; int id2 = (idx2 + 1) * AC_PLUGIN_CTL_BASE; int id_mask = ~((int)AC_PLUGIN_CTL_ID_MASK); int i, j; CtrlListList tmpcll; CtrlVal cv(0, 0.0); for(ciCtrlList icl = _controller.begin(); icl != _controller.end(); ++icl) { cl = icl->second; i = cl->id() & AC_PLUGIN_CTL_ID_MASK; j = cl->id() & id_mask; if(j == id1 || j == id2) { newcl = new CtrlList(i | (j == id1 ? id2 : id1), cl->dontShow()); newcl->setMode(cl->mode()); newcl->setValueType(cl->valueType()); newcl->setName(cl->name()); double min, max; cl->range(&min, &max); newcl->setRange(min, max); newcl->setCurVal(cl->curVal()); newcl->setDefault(cl->getDefault()); newcl->setColor(cl->color()); newcl->setVisible(cl->isVisible()); for(iCtrl ic = cl->begin(); ic != cl->end(); ++ic) { cv = ic->second; newcl->insert(CtrlListInsertPair_t(cv.frame, cv)); } tmpcll.insert(std::pair(newcl->id(), newcl)); } else { newcl = new CtrlList(); *newcl = *cl; tmpcll.insert(std::pair(newcl->id(), newcl)); } } for(iCtrlList ci = _controller.begin(); ci != _controller.end(); ++ci) delete (*ci).second; _controller.clear(); for(ciCtrlList icl = tmpcll.begin(); icl != tmpcll.end(); ++icl) { newcl = icl->second; _controller.insert(std::pair(newcl->id(), newcl)); } // Remap midi to audio controls... MidiAudioCtrlMap* macm = _controller.midiControls(); for(iMidiAudioCtrlMap imacm = macm->begin(); imacm != macm->end(); ++imacm) { int actrl = imacm->second.audioCtrlId(); int id = actrl & id_mask; actrl &= AC_PLUGIN_CTL_ID_MASK; if(id == id1) actrl |= id2; else if(id == id2) actrl |= id1; else continue; imacm->second.setAudioCtrlId(actrl); } } //--------------------------------------------------------- // setAutomationType //--------------------------------------------------------- void AudioTrack::setAutomationType(AutomationType t) { // Clear pressed and touched and rec event list. clearRecAutomation(true); // Now set the type. _automationType = t; } //--------------------------------------------------------- // setControllerMode //--------------------------------------------------------- void AudioTrack::setControllerMode(int ctlID, CtrlList::Mode m) { ciCtrlList cl = _controller.find(ctlID); if(cl == _controller.end()) return; cl->second->setMode(m); } //--------------------------------------------------------- // clearControllerEvents //--------------------------------------------------------- void AudioTrack::clearControllerEvents(int id) { ciCtrlList icl = _controller.find(id); if(icl == _controller.end()) return; CtrlList* cl = icl->second; cl->clear(); return; } //--------------------------------------------------------- // seekPrevACEvent //--------------------------------------------------------- void AudioTrack::seekPrevACEvent(int id) { ciCtrlList icl = _controller.find(id); if(icl == _controller.end()){ return; } CtrlList* cl = icl->second; if(cl->empty()) return; iCtrl s = cl->lower_bound(MusEGlobal::audio->pos().frame()); if(s != cl->begin()) --s; MusEGlobal::song->setPos(Song::CPOS, Pos(s->second.frame, false), false, true, false); return; } //--------------------------------------------------------- // seekNextACEvent //--------------------------------------------------------- void AudioTrack::seekNextACEvent(int id) { ciCtrlList icl = _controller.find(id); if(icl == _controller.end()) { return; } CtrlList* cl = icl->second; if(cl->empty()) return; iCtrl s = cl->upper_bound(MusEGlobal::audio->pos().frame()); if(s == cl->end()) { --s; } MusEGlobal::song->setPos(Song::CPOS, Pos(s->second.frame, false), false, true, false); return; } //--------------------------------------------------------- // eraseACEvent //--------------------------------------------------------- void AudioTrack::eraseACEvent(int id, int frame) { ciCtrlList icl = _controller.find(id); if(icl == _controller.end()) { return; } CtrlList* cl = icl->second; if(cl->empty()) return; iCtrl s = cl->find(frame); if(s != cl->end()) cl->erase(s); return; } //--------------------------------------------------------- // eraseRangeACEvents //--------------------------------------------------------- void AudioTrack::eraseRangeACEvents(int id, int frame1, int frame2) { ciCtrlList icl = _controller.find(id); if(icl == _controller.end()) { return; } CtrlList* cl = icl->second; if(cl->empty()) return; iCtrl s = cl->lower_bound(frame1); iCtrl e = cl->lower_bound(frame2); cl->erase(s, e); return; } //--------------------------------------------------------- // addACEvent //--------------------------------------------------------- void AudioTrack::addACEvent(int id, int frame, double val) { ciCtrlList icl = _controller.find(id); if(icl == _controller.end()) { return; } CtrlList* cl = icl->second; // Add will replace if found. cl->add(frame, val); return; } //--------------------------------------------------------- // changeACEvent //--------------------------------------------------------- void AudioTrack::changeACEvent(int id, int frame, int newframe, double newval) { ciCtrlList icl = _controller.find(id); if(icl == _controller.end()) return; CtrlList* cl = icl->second; iCtrl ic = cl->find(frame); if(ic != cl->end()) cl->erase(ic); cl->insert(CtrlListInsertPair_t(newframe, CtrlVal(newframe, newval))); } //--------------------------------------------------------- // useLatencyCorrection //--------------------------------------------------------- bool AudioTrack::useLatencyCorrection() const { return _latencyComp && MusEGlobal::config.enableLatencyCorrection; } //--------------------------------------------------------- // selfLatencyAudio //--------------------------------------------------------- float AudioTrack::selfLatencyAudio(int /*channel*/) const { if(!_efxPipe) return 0.0; return _efxPipe->latency(); } //--------------------------------------------------------- // getDominanceInfo //--------------------------------------------------------- TrackLatencyInfo& AudioTrack::getDominanceInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._canDominateInputProcessed) || (!input && _latencyInfo._canDominateProcessed)) return _latencyInfo; // Get the default domination for this track type. bool can_dominate_lat = input ? canDominateInputLatency() : canDominateOutputLatency(); bool can_correct_lat = canCorrectOutputLatency(); const bool passthru = canPassThruLatency(); bool item_found = false; if(!off() && (passthru || input)) { RouteList* rl = inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO ? } else { Track* track = ir->track; if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } break; default: break; } } // Special for the built-in metronome. //if(!off() && !MusECore::metronome->off() && (passthru || input) && sendMetronome()) if(!MusECore::metronome->off() && sendMetronome()) { const TrackLatencyInfo& li = MusECore::metronome->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off()) { if(input) { _latencyInfo._canDominateInputLatency = can_dominate_lat; } else { _latencyInfo._canDominateOutputLatency = can_dominate_lat; // If any of the branches can dominate, then this node cannot correct. _latencyInfo._canCorrectOutputLatency = can_correct_lat && !can_dominate_lat; } } if(input) _latencyInfo._canDominateInputProcessed = true; else _latencyInfo._canDominateProcessed = true; return _latencyInfo; } //--------------------------------------------------------- // getDominanceLatencyInfo //--------------------------------------------------------- TrackLatencyInfo& AudioTrack::getDominanceLatencyInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._dominanceInputProcessed) || (!input && _latencyInfo._dominanceProcessed)) return _latencyInfo; float route_worst_latency = 0.0f; const bool passthru = canPassThruLatency(); bool item_found = false; float worst_self_latency = 0.0f; if(!input && !off()) worst_self_latency = getWorstSelfLatencyAudio(); if(!off() && (passthru || input)) { RouteList* rl = inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO ? } else { Track* track = ir->track; //if(!off() && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } break; default: break; } } // Special for the built-in metronome. //if(!off() && !MusECore::metronome->off() && (passthru || input) && sendMetronome()) if(!MusECore::metronome->off() && sendMetronome()) { const TrackLatencyInfo& li = MusECore::metronome->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off()) { if(input) { _latencyInfo._inputLatency = route_worst_latency; } else { if(passthru) { _latencyInfo._outputLatency = worst_self_latency + route_worst_latency; _latencyInfo._inputLatency = route_worst_latency; } else { _latencyInfo._outputLatency = worst_self_latency + _latencyInfo._sourceCorrectionValue; } } } if(input) _latencyInfo._dominanceInputProcessed = true; else _latencyInfo._dominanceProcessed = true; return _latencyInfo; } //--------------------------------------------------------- // getWorstPluginLatencyAudio //--------------------------------------------------------- float AudioTrack::getWorstPluginLatencyAudio() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._worstPluginLatencyProcessed) return _latencyInfo._worstPluginLatency; float worst_lat = 0.0f; // Include the effects rack latency. if(_efxPipe) worst_lat += _efxPipe->latency(); _latencyInfo._worstPluginLatency = worst_lat; _latencyInfo._worstPluginLatencyProcessed = true; return _latencyInfo._worstPluginLatency; } //--------------------------------------------------------- // getWorstSelfLatencyAudio //--------------------------------------------------------- float AudioTrack::getWorstSelfLatencyAudio() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._worstSelfLatencyProcessed) return _latencyInfo._worstSelfLatency; // Include the effects rack latency and any synth latency and any port latency. _latencyInfo._worstSelfLatency = getWorstPluginLatencyAudio() + getWorstPortLatencyAudio(); // The absolute latency of signals leaving this track is the sum of // any connected route latencies and this track's latency. _latencyInfo._worstSelfLatencyProcessed = true; return _latencyInfo._worstSelfLatency; } //--------------------------------------------------------- // setCorrectionLatencyInfo //--------------------------------------------------------- TrackLatencyInfo& AudioTrack::setCorrectionLatencyInfo(bool input, float finalWorstLatency, float callerBranchLatency) { const bool passthru = canPassThruLatency(); float worst_self_latency = 0.0f; if(!input && !off()) worst_self_latency = getWorstSelfLatencyAudio(); // The _trackLatency should already be calculated in the dominance scan. const float branch_lat = callerBranchLatency + worst_self_latency; if(!off() && (passthru || input)) { RouteList* rl = inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::TRACK_ROUTE || !ir->track || ir->track->isMidiTrack()) continue; Track* track = ir->track; //if(!off() && !track->off() && (passthru || input)) if(!track->off()) track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } // Special for the built-in metronome. //if(!off() && !MusECore::metronome->off() && (passthru || input) && sendMetronome()) if(!MusECore::metronome->off() && sendMetronome()) { MusECore::metronome->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off()) { if(input) { } else { if(canCorrectOutputLatency() && _latencyInfo._canCorrectOutputLatency) { float corr = 0.0f; if(MusEGlobal::config.commonProjectLatency) corr -= finalWorstLatency; corr -= branch_lat; // The _sourceCorrectionValue is initialized to zero. // Whichever calling branch needs the most correction gets it. if(corr < _latencyInfo._sourceCorrectionValue) _latencyInfo._sourceCorrectionValue = corr; } } //fprintf(stderr, "AudioTrack::setCorrectionLatencyInfo() name:%s finalWorstLatency:%f branch_lat:%f corr:%f _sourceCorrectionValue:%f\n", // name().toLatin1().constData(), finalWorstLatency, branch_lat, corr, _latencyInfo._sourceCorrectionValue); } return _latencyInfo; } //--------------------------------------------------------- // getLatencyInfo //--------------------------------------------------------- TrackLatencyInfo& AudioTrack::getLatencyInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._inputProcessed) || (!input && _latencyInfo._processed)) return _latencyInfo; float route_worst_latency = _latencyInfo._inputLatency; const bool passthru = canPassThruLatency(); if(passthru || input) { RouteList* rl = inRoutes(); // Now that we know the worst-case latency of the connected branches, // adjust each of the conveniently stored temporary latency values // in the routes according to whether they can dominate... for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::TRACK_ROUTE || !ir->track || ir->track->isMidiTrack()) continue; Track* track = ir->track; // Default to zero. ir->audioLatencyOut = 0.0f; if(!off() && !track->off()) { const TrackLatencyInfo& li = track->getLatencyInfo(false); const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. ir->audioLatencyOut = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)ir->audioLatencyOut < 0) ir->audioLatencyOut = 0.0f; } } } // Special for the built-in metronome. // Default to zero. _latencyInfo._latencyOutMetronome = 0.0f; if(!off() && !MusECore::metronome->off() && sendMetronome()) { TrackLatencyInfo& li = MusECore::metronome->getLatencyInfo(false); const bool participate = li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency; if(participate) { // TODO: FIXME: Where to store? We have no route to store it in. // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. // ir->audioLatencyOut = route_worst_latency - ir->audioLatencyOut; // // Should not happen, but just in case. // if((long int)ir->audioLatencyOut < 0) // ir->audioLatencyOut = 0.0f; // Special for metronome: We don't have metronome routes yet. // So we must store this information here just for the metronome. li._latencyOutMetronome = route_worst_latency - li._latencyOutMetronome; // Should not happen, but just in case. if((long int)li._latencyOutMetronome < 0) li._latencyOutMetronome = 0.0f; } } } if(input) _latencyInfo._inputProcessed = true; else _latencyInfo._processed = true; return _latencyInfo; } void AudioTrack::setLatencyCompWriteOffset(float worstCase) { // If independent branches are NOT to affect project latency, // then there should be no need for any extra delay in the branch. if(!MusEGlobal::config.commonProjectLatency) { _latencyInfo._compensatorWriteOffset = 0; //fprintf(stderr, "AudioTrack::setLatencyCompWriteOffset() name:%s worstCase:%f _outputLatency:%f _compensatorWriteOffset:%lu\n", // name().toLatin1().constData(), worstCase, _latencyInfo._outputLatency, _latencyInfo._compensatorWriteOffset); return; } if(_latencyInfo._canDominateOutputLatency) { const long unsigned int wc = worstCase; const long unsigned int ol = _latencyInfo._outputLatency; if(ol > wc) _latencyInfo._compensatorWriteOffset = 0; else _latencyInfo._compensatorWriteOffset = wc - ol; } else { // if(_latencyInfo._outputLatency < 0) _latencyInfo._compensatorWriteOffset = 0; // else // _latencyInfo._compensatorWriteOffset = _latencyInfo._outputLatency; } //fprintf(stderr, // "AudioTrack::setLatencyCompWriteOffset() name:%s worstCase:%f" // " _outputLatency:%f _canDominateOutputLatency:%d _compensatorWriteOffset:%lu\n", // name().toLatin1().constData(), worstCase, _latencyInfo._outputLatency, // _latencyInfo._canDominateOutputLatency, _latencyInfo._compensatorWriteOffset); } //--------------------------------------------------------- // volume //--------------------------------------------------------- double AudioTrack::volume() const { return _controller.value(AC_VOLUME, MusEGlobal::audio->curFramePos(), !MusEGlobal::automation || automationType() == AUTO_OFF || !_controls[AC_VOLUME].enCtrl); } //--------------------------------------------------------- // setVolume //--------------------------------------------------------- void AudioTrack::setVolume(double val) { iCtrlList cl = _controller.find(AC_VOLUME); if (cl == _controller.end()) { printf("no volume controller %s %zd\n", name().toLatin1().constData(), _controller.size()); return; } cl->second->setCurVal(val); } //--------------------------------------------------------- // pan //--------------------------------------------------------- double AudioTrack::pan() const { return _controller.value(AC_PAN, MusEGlobal::audio->curFramePos(), !MusEGlobal::automation || automationType() == AUTO_OFF || !_controls[AC_PAN].enCtrl); } //--------------------------------------------------------- // setPan //--------------------------------------------------------- void AudioTrack::setPan(double val) { iCtrlList cl = _controller.find(AC_PAN); if (cl == _controller.end()) { printf("no pan controller\n"); return; } cl->second->setCurVal(val); } //--------------------------------------------------------- // pan //--------------------------------------------------------- double AudioTrack::gain() const { return _gain; } //--------------------------------------------------------- // setPan //--------------------------------------------------------- void AudioTrack::setGain(double val) { _gain = val; } //--------------------------------------------------------- // pluginCtrlVal //--------------------------------------------------------- double AudioTrack::pluginCtrlVal(int ctlID) const { bool en = true; if(ctlID < AC_PLUGIN_CTL_BASE) { if((unsigned long)ctlID < _controlPorts) en = _controls[ctlID].enCtrl; } else { if(ctlID < (int)genACnum(MusECore::MAX_PLUGINS, 0)) // The beginning of the special synth controller block. { en = _efxPipe->controllerEnabled(ctlID); } else { if(type() == AUDIO_SOFTSYNTH) { const SynthI* synth = static_cast(this); const SynthIF* sif = synth->sif(); if(sif) { int in_ctrl_idx = ctlID & AC_PLUGIN_CTL_ID_MASK; en = sif->controllerEnabled(in_ctrl_idx); } } } } return _controller.value(ctlID, MusEGlobal::audio->curFramePos(), !MusEGlobal::automation || automationType() == AUTO_OFF || !en); } //--------------------------------------------------------- // setPluginCtrlVal //--------------------------------------------------------- void AudioTrack::setPluginCtrlVal(int param, double val) { iCtrlList cl = _controller.find(param); if (cl == _controller.end()) return; cl->second->setCurVal(val); } //--------------------------------------------------------- // addScheduledControlEvent // returns true if event cannot be delivered //--------------------------------------------------------- bool AudioTrack::addScheduledControlEvent(int track_ctrl_id, double val, unsigned frame) { if(track_ctrl_id < AC_PLUGIN_CTL_BASE) { // Send these controllers directly to the track's own FIFO. ControlEvent ce; ce.unique = false; ce.fromGui = false; ce.idx = track_ctrl_id; ce.value = val; // Time-stamp the event. timestamp() is circular, which is making it impossible to deal with 'modulo' events which // slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead: ce.frame = frame; if(_controlFifo.put(ce)) { fprintf(stderr, "AudioTrack::addScheduledControlEvent: fifo overflow: in control number:%d\n", track_ctrl_id); return true; } return false; } else { if(track_ctrl_id < (int)genACnum(MusECore::MAX_PLUGINS, 0)) // The beginning of the special synth controller block. return _efxPipe->addScheduledControlEvent(track_ctrl_id, val, frame); else { if(type() == AUDIO_SOFTSYNTH) { const SynthI* synth = static_cast(this); SynthIF* sif = synth->sif(); if(sif) { int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; return sif->addScheduledControlEvent(in_ctrl_idx, val, frame); } } } } return true; } //--------------------------------------------------------- // enableController // Enable or disable gui controls. // Used during automation recording to inhibit gui controls // from playback controller stream //--------------------------------------------------------- void AudioTrack::enableController(int track_ctrl_id, bool en) { if(track_ctrl_id < AC_PLUGIN_CTL_BASE) { if((unsigned long)track_ctrl_id < _controlPorts) _controls[track_ctrl_id].enCtrl = en; } else { if(track_ctrl_id < (int)genACnum(MusECore::MAX_PLUGINS, 0)) // The beginning of the special synth controller block. _efxPipe->enableController(track_ctrl_id, en); else { if(type() == AUDIO_SOFTSYNTH) { const SynthI* synth = static_cast(this); SynthIF* sif = synth->sif(); if(sif) { int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; sif->enableController(in_ctrl_idx, en); } } } } } //--------------------------------------------------------- // controllerEnabled //--------------------------------------------------------- bool AudioTrack::controllerEnabled(int track_ctrl_id) const { if(track_ctrl_id < AC_PLUGIN_CTL_BASE) { if((unsigned long)track_ctrl_id < _controlPorts) return _controls[track_ctrl_id].enCtrl; return false; } else { if(track_ctrl_id < (int)genACnum(MusECore::MAX_PLUGINS, 0)) // The beginning of the special synth controller block. { return _efxPipe->controllerEnabled(track_ctrl_id); } else { if(type() == AUDIO_SOFTSYNTH) { const SynthI* synth = static_cast(this); const SynthIF* sif = synth->sif(); if(sif) { int in_ctrl_idx = track_ctrl_id & AC_PLUGIN_CTL_ID_MASK; return sif->controllerEnabled(in_ctrl_idx); } } } } return false; } //--------------------------------------------------------- // enableAllControllers // Enable all track and plugin controllers, and synth controllers if applicable. //--------------------------------------------------------- void AudioTrack::enableAllControllers() { // Enable track controllers: for(unsigned long i = 0; i < _controlPorts; ++i) _controls[i].enCtrl = true; // Enable plugin controllers: Pipeline *pl = efxPipe(); PluginI *p; for(iPluginI i = pl->begin(); i != pl->end(); ++i) { p = *i; if(!p) continue; p->enableAllControllers(true); } // Enable synth controllers: if(type() == AUDIO_SOFTSYNTH) { const SynthI* synth = static_cast(this); SynthIF* sif = synth->sif(); if(sif) sif->enableAllControllers(true); } } void AudioTrack::recordAutomation(int n, double v) { if(!MusEGlobal::automation) return; if(MusEGlobal::audio->isPlaying()) _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); else { if(automationType() == AUTO_WRITE) _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); else if(automationType() == AUTO_TOUCH) // In touch mode and not playing. Send directly to controller list. { iCtrlList cl = _controller.find(n); if (cl == _controller.end()) return; // Add will replace if found. cl->second->add(MusEGlobal::audio->curFramePos(), v); } } } void AudioTrack::startAutoRecord(int n, double v) { if(!MusEGlobal::automation) return; if(MusEGlobal::audio->isPlaying()) { if(automationType() == AUTO_TOUCH) _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v, ARVT_START)); else if(automationType() == AUTO_WRITE) _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); } else { if(automationType() == AUTO_TOUCH) // In touch mode and not playing. Send directly to controller list. { // FIXME: Unsafe? Should sync by sending a message, but that'll really slow it down with large audio bufs. iCtrlList cl = _controller.find(n); if (cl == _controller.end()) return; // Add will replace if found. cl->second->add(MusEGlobal::audio->curFramePos(), v); } else if(automationType() == AUTO_WRITE) _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v)); } } void AudioTrack::stopAutoRecord(int n, double v) { if(!MusEGlobal::automation) return; if(MusEGlobal::audio->isPlaying()) { if(automationType() == AUTO_TOUCH) { MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddAudioCtrlVal, this, n, MusEGlobal::audio->curFramePos(), v)); _recEvents.push_back(CtrlRecVal(MusEGlobal::audio->curFramePos(), n, v, ARVT_STOP)); } } } //--------------------------------------------------------- // AudioTrack::writeProperties //--------------------------------------------------------- void AudioTrack::writeProperties(int level, Xml& xml) const { Track::writeProperties(level, xml); xml.intTag(level, "prefader", prefader()); xml.intTag(level, "sendMetronome", sendMetronome()); xml.intTag(level, "automation", int(automationType())); xml.doubleTag(level, "gain", _gain); if (hasAuxSend()) { int naux = MusEGlobal::song->auxs()->size(); for (int idx = 0; idx < naux; ++idx) { QString s("%2\n"); // Aux fix from Remon, thanks. xml.nput(level, s.arg(idx).arg(_auxSend[idx]).toLatin1().constData()); } } for (ciPluginI ip = _efxPipe->begin(); ip != _efxPipe->end(); ++ip) { if (*ip) (*ip)->writeConfiguration(level, xml); } _controller.write(level, xml); } //--------------------------------------------------------- // readAuxSend //--------------------------------------------------------- void AudioTrack::readAuxSend(Xml& xml) { unsigned idx = 0; double val; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::Attribut: if (tag == "idx") idx = xml.s2().toInt(); break; case Xml::Text: val = tag.toDouble(); break; case Xml::TagEnd: if (xml.s1() == "auxSend") { if (_auxSend.size() < idx+1) _auxSend.push_back(val); else _auxSend[idx] = val; return; } default: break; } } } //--------------------------------------------------------- // AudioTrack::readProperties //--------------------------------------------------------- bool AudioTrack::readProperties(Xml& xml, const QString& tag) { if (tag == "plugin") { int rackpos; for(rackpos = 0; rackpos < MusECore::PipelineDepth; ++rackpos) { if(!(*_efxPipe)[rackpos]) break; } if(rackpos < MusECore::PipelineDepth) { PluginI* pi = new PluginI(); pi->setTrack(this); pi->setID(rackpos); if(pi->readConfiguration(xml, false)) delete pi; else (*_efxPipe)[rackpos] = pi; } else printf("can't load plugin - plugin rack is already full\n"); } else if (tag == "auxSend") readAuxSend(xml); else if (tag == "prefader") _prefader = xml.parseInt(); else if (tag == "sendMetronome") _sendMetronome = xml.parseInt(); else if (tag == "gain") _gain = xml.parseDouble(); else if (tag == "automation") setAutomationType(AutomationType(xml.parseInt())); else if (tag == "controller") { CtrlList* l = new CtrlList(); l->read(xml); // Since (until now) muse wrote a 'zero' for plugin controller current value // in the XML file, we can't use that value, now that plugin automation is added. // We must take the value from the plugin control value. // Otherwise we break all existing .med files with plugins, because the gui // controls would all be set to zero. // But we will allow for the (unintended, useless) possibility of a controller // with no matching plugin control. const PluginIBase* p = 0; bool ctlfound = false; unsigned m = l->id() & AC_PLUGIN_CTL_ID_MASK; int n = (l->id() >> AC_PLUGIN_CTL_BASE_POW) - 1; if(n >= 0 && n < MusECore::PipelineDepth) p = (*_efxPipe)[n]; // Support a special block for synth controllers. else if(n == MusECore::MAX_PLUGINS && type() == AUDIO_SOFTSYNTH) { const SynthI* synti = static_cast < SynthI* > (this); const SynthIF* sif = synti->sif(); if(sif) p = static_cast < const PluginIBase* > (sif); } if(p && m < p->parameters()) ctlfound = true; iCtrlList icl = _controller.find(l->id()); if (icl == _controller.end()) _controller.add(l); else { CtrlList* d = icl->second; for (iCtrl i = l->begin(); i != l->end(); ++i) d->insert(CtrlListInsertPair_t(i->first, i->second)); if(!ctlfound) d->setCurVal(l->curVal()); d->setColor(l->color()); d->setVisible(l->isVisible()); d->setDefault(l->getDefault()); delete l; l = d; } if(ctlfound) { l->setCurVal(p->param(m)); l->setValueType(p->ctrlValueType(m)); l->setMode(p->ctrlMode(m)); } } else if (tag == "midiMapper") _controller.midiControls()->read(xml); else return Track::readProperties(xml, tag); return false; } //--------------------------------------------------------- // showPendingPluginNativeGuis // This is needed because OSC needs all tracks with plugins to be already // added to their track lists so it can find them and show their native guis. //--------------------------------------------------------- void AudioTrack::showPendingPluginNativeGuis() { for(int idx = 0; idx < MusECore::PipelineDepth; ++idx) { PluginI* p = (*_efxPipe)[idx]; if(!p) continue; if(p->isShowNativeGuiPending()) p->showNativeGui(true); } } //--------------------------------------------------------- // mapRackPluginsToControllers //--------------------------------------------------------- void AudioTrack::mapRackPluginsToControllers() { // Iterate all possible plugin controller indexes... for(int idx = MusECore::PipelineDepth - 1; idx >= 0; idx--) { iCtrlList icl = _controller.lower_bound((idx + 1) * AC_PLUGIN_CTL_BASE); if(icl == _controller.end() || ((icl->second->id() >> AC_PLUGIN_CTL_BASE_POW) - 1) != idx) continue; // We found some controllers with that index. Now iterate the plugin rack... for(int i = idx; i >= 0; i--) { PluginI* p = (*_efxPipe)[i]; if(!p) continue; // We found a plugin at a rack position. If the rack position is not the same as the controller index... if(i != idx) { (*_efxPipe)[i] = 0; (*_efxPipe)[idx] = p; } p->setID(idx); // It is now safe to update the controllers. p->updateControllers(); break; } } // No matter of the outcome of the above - rack position is not too critical - // making sure that each control has a controller is important. Otherwise they // are stuck at zero can't be adjusted. // Muse med files created before the automation patches (before 0.9pre1) may have broken // controller sections, so this will allow more tolerance of them. for(int idx = 0; idx < MusECore::PipelineDepth; idx++) { PluginI* p = (*_efxPipe)[idx]; if(!p) continue; if(p->id() != idx) p->setID(idx); int j = p->parameters(); for(int i = 0; i < j; i++) { int id = genACnum(idx, i); CtrlList* l = 0; ciCtrlList icl = _controller.find(id); if(icl == _controller.end()) { l = new CtrlList(id); addController(l); } else l = icl->second; // Force all of these now, even though they may have already been set. With a pre- // 0.9pre1 med file with broken controller sections they may not be set correct. float min, max; p->range(i, &min, &max); l->setRange(min, max); l->setName(QString(p->paramName(i))); l->setValueType(p->ctrlValueType(i)); l->setMode(p->ctrlMode(i)); l->setCurVal(p->param(i)); } } // Delete non-existent controllers. for(ciCtrlList icl = _controller.cbegin(); icl != _controller.cend(); ) { CtrlList* l = icl->second; int id = l->id(); // Ignore volume, pan, mute etc. if(id < AC_PLUGIN_CTL_BASE) { ++icl; continue; } unsigned param = id & AC_PLUGIN_CTL_ID_MASK; int idx = (id >> AC_PLUGIN_CTL_BASE_POW) - 1; bool do_remove = false; const PluginIBase* p = 0; if(idx >= 0 && idx < MusECore::PipelineDepth) { p = (*_efxPipe)[idx]; if(!p || (param >= p->parameters())) do_remove = true; } // Support a special block for synth controllers. else if(idx == MusECore::MAX_PLUGINS && type() == AUDIO_SOFTSYNTH) { const SynthI* synti = static_cast < const SynthI* > (this); SynthIF* sif = synti->sif(); // Special for synths: If no sif could be found or loaded (missing pplugin etc.), // do NOT remove the loaded controllers, to preserve data upon re-saving. if(sif) { p = static_cast < const PluginIBase* > (sif); if(param >= p->parameters()) do_remove = true; } } // If there's no plugin at that rack position, or the param is out of range of // the number of controls in the plugin, then it's a stray controller. Delete it. // Future: Leave room for possible bypass controller at AC_PLUGIN_CTL_ID_MASK -1. //if(!p || (param >= p->parameters() && (param != AC_PLUGIN_CTL_ID_MASK -1))) if(do_remove) { // C++11. icl = _controller.erase(icl); } else { ++icl; } } } RouteCapabilitiesStruct AudioTrack::routeCapabilities() const { RouteCapabilitiesStruct s; s._trackChannels._inChannels = s._trackChannels._outChannels = totalProcessBuffers(); s._trackChannels._inRoutable = s._trackChannels._outRoutable = (s._trackChannels._inChannels != 0); return s; } //--------------------------------------------------------- // AudioInput //--------------------------------------------------------- AudioInput::AudioInput() // Default 1 channel for audio inputs. : AudioTrack(AUDIO_INPUT, 1) { for (int i = 0; i < MusECore::MAX_CHANNELS; ++i) jackPorts[i] = 0; } AudioInput::AudioInput(const AudioInput& t, int flags) : AudioTrack(t, flags) { for (int i = 0; i < MusECore::MAX_CHANNELS; ++i) jackPorts[i] = 0; // It is pointless to try to register ports right now since the // track names are currently the same. The registration will fail // due to duplicate port names. // Therefore the caller MUST set a unique track name afterwards, // which does succeed at registering the ports. internal_assign(t, flags); } void AudioInput::assign(const Track& t, int flags) { AudioTrack::assign(t, flags); internal_assign(t, flags); } void AudioInput::internal_assign(const Track& t, int flags) { if(t.type() != AUDIO_INPUT) return; const AudioInput& at = (const AudioInput&)t; if(flags & ASSIGN_ROUTES) { for(ciRoute ir = at._inRoutes.begin(); ir != at._inRoutes.end(); ++ir) { // Defer all Jack routes to these copy constructors or assign ! if(ir->type != Route::JACK_ROUTE) continue; // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. _inRoutes.push_back(*ir); } } } //--------------------------------------------------------- // ~AudioInput //--------------------------------------------------------- AudioInput::~AudioInput() { if (!MusEGlobal::checkAudioDevice()) return; for (int i = 0; i < _channels; ++i) if(jackPorts[i]) MusEGlobal::audioDevice->unregisterPort(jackPorts[i]); } //--------------------------------------------------------- // selfLatencyAudio //--------------------------------------------------------- float AudioInput::selfLatencyAudio(int channel) const { float l = AudioTrack::selfLatencyAudio(channel); if(!MusEGlobal::checkAudioDevice()) return l; void* jackPort = jackPorts[channel]; if(jackPort) l += MusEGlobal::audioDevice->portLatency(jackPort, true); return l; } //--------------------------------------------------------- // getWorstPortLatencyAudio //--------------------------------------------------------- float AudioInput::getWorstPortLatencyAudio() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._worstPortLatencyProcessed) return _latencyInfo._worstPortLatency; float worst_lat = 0.0f; // Include any port latencies. if(MusEGlobal::checkAudioDevice()) { const int track_out_channels = totalProcessBuffers(); // totalOutChannels(); for(int i = 0; i < track_out_channels; ++i) { void* jackPort = jackPorts[i]; if(jackPort) { // true = we want the capture latency. const float lat = MusEGlobal::audioDevice->portLatency(jackPort, true); if(lat > worst_lat) worst_lat = lat; } } } _latencyInfo._worstPortLatency = worst_lat; _latencyInfo._worstPortLatencyProcessed = true; return _latencyInfo._worstPortLatency; } bool AudioInput::canDominateOutputLatency() const { return !off(); } //--------------------------------------------------------- // write //--------------------------------------------------------- void AudioInput::write(int level, Xml& xml) const { xml.tag(level++, "AudioInput"); AudioTrack::writeProperties(level, xml); xml.etag(level, "AudioInput"); } //--------------------------------------------------------- // read //--------------------------------------------------------- void AudioInput::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (AudioTrack::readProperties(xml, tag)) xml.unknown("AudioInput"); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "AudioInput") { registerPorts(); // allocate jack ports mapRackPluginsToControllers(); return; } default: break; } } } RouteCapabilitiesStruct AudioInput::routeCapabilities() const { RouteCapabilitiesStruct s = AudioTrack::routeCapabilities(); // Support Midi Track to Audio Input Track routes (for soloing chain). s._trackChannels._inRoutable = true; s._trackChannels._inChannels = 0; s._jackChannels._inRoutable = false; s._jackChannels._inChannels = totalProcessBuffers(); return s; } //--------------------------------------------------------- // AudioOutput //--------------------------------------------------------- AudioOutput::AudioOutput() : AudioTrack(AUDIO_OUTPUT) { _outputLatencyComp = new LatencyCompensator(); _nframes = 0; for (int i = 0; i < MAX_CHANNELS; ++i) jackPorts[i] = 0; } AudioOutput::AudioOutput(const AudioOutput& t, int flags) : AudioTrack(t, flags) { _outputLatencyComp = new LatencyCompensator(); for (int i = 0; i < MusECore::MAX_CHANNELS; ++i) jackPorts[i] = 0; _nframes = 0; // It is pointless to try to register ports right now since the // track names are currently the same. The registration will fail // due to duplicate port names. // Therefore the caller MUST set a unique track name afterwards, // which does succeed at registering the ports. internal_assign(t, flags); } void AudioOutput::assign(const Track& t, int flags) { AudioTrack::assign(t, flags); internal_assign(t, flags); } void AudioOutput::internal_assign(const Track& t, int flags) { if(t.type() != AUDIO_OUTPUT) return; const AudioOutput& at = (const AudioOutput&)t; if(flags & ASSIGN_ROUTES) { for(ciRoute ir = at._outRoutes.begin(); ir != at._outRoutes.end(); ++ir) { // Defer all Jack routes to these copy constructors or assign ! if(ir->type != Route::JACK_ROUTE) continue; // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. _outRoutes.push_back(*ir); } } } //--------------------------------------------------------- // ~AudioOutput //--------------------------------------------------------- AudioOutput::~AudioOutput() { // FIXME Never runs this on close because device is nulled first. // But possibly it's benign because it may already disconnect before it gets here... if (MusEGlobal::checkAudioDevice()) { for (int i = 0; i < _channels; ++i) if(jackPorts[i]) MusEGlobal::audioDevice->unregisterPort(jackPorts[i]); } if(_outputLatencyComp) delete _outputLatencyComp; } //--------------------------------------------------------- // setChannels //--------------------------------------------------------- void AudioOutput::setChannels(int n) { AudioTrack::setChannels(n); if(useLatencyCorrection() && _outputLatencyComp) _outputLatencyComp->setChannels(totalProcessBuffers()); } //--------------------------------------------------------- // selfLatencyAudio //--------------------------------------------------------- float AudioOutput::selfLatencyAudio(int channel) const { float l = AudioTrack::selfLatencyAudio(channel); if(!MusEGlobal::checkAudioDevice()) return l; void* jackPort = jackPorts[channel]; if(jackPort) l += MusEGlobal::audioDevice->portLatency(jackPort, false); return l; } //--------------------------------------------------------- // getWorstPortLatencyAudio //--------------------------------------------------------- float AudioOutput::getWorstPortLatencyAudio() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._worstPortLatencyProcessed) return _latencyInfo._worstPortLatency; float worst_lat = 0.0f; // Include any port latencies. if(MusEGlobal::checkAudioDevice()) { const int track_out_channels = totalProcessBuffers(); // totalOutChannels(); for(int i = 0; i < track_out_channels; ++i) { void* jackPort = jackPorts[i]; if(jackPort) { // false = we want the playback latency. const float lat = MusEGlobal::audioDevice->portLatency(jackPort, false); if(lat > worst_lat) worst_lat = lat; } } } _latencyInfo._worstPortLatency = worst_lat; _latencyInfo._worstPortLatencyProcessed = true; return _latencyInfo._worstPortLatency; } bool AudioOutput::isLatencyInputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyInputTerminalProcessed) return _latencyInfo._isLatencyInputTerminal; _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } bool AudioOutput::isLatencyOutputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyOutputTerminalProcessed) return _latencyInfo._isLatencyOutputTerminal; _latencyInfo._isLatencyOutputTerminal = true; _latencyInfo._isLatencyOutputTerminalProcessed = true; return true; } //--------------------------------------------------------- // applyOutputLatencyComp //--------------------------------------------------------- void AudioOutput::applyOutputLatencyComp(unsigned nframes) { if(!useLatencyCorrection() || !_outputLatencyComp) return; // Include any port latencies. if(MusEGlobal::checkAudioDevice()) { // We want the audio output track's worst port latency. //const TrackLatencyInfo& li = getLatencyInfo(false /*output*/); //const float route_worst_case_latency = li._worstPortLatency; const float port_worst_case_latency = getWorstPortLatencyAudio(); // 'buffer' is only MAX_CHANNELS deep, unlike some of our allocated audio buffers. const int track_out_channels = MusECore::MAX_CHANNELS; //totalProcessBuffers(); // totalOutChannels(); for(int i = 0; i < track_out_channels; ++i) { if(!buffer[i]) continue; // Prepare the latency value to be passed to the compensator's writer, // by adjusting each channel latency value. ie. the channel with the worst-case // latency will get ZERO delay, while channels having smaller latency will get // MORE delay, to match all the signal timings together. void* jackPort = jackPorts[i]; if(jackPort) { // false = we want the playback latency. const float lat = port_worst_case_latency - MusEGlobal::audioDevice->portLatency(jackPort, false); unsigned long offset = 0; if((long int)lat > 0) offset = lat; // Write the channel buffer to the latency compensator. // It will be read back later, in-place. _outputLatencyComp->write(i, nframes, offset /* + latencyCompWriteOffset() */, buffer[i]); // Read back the latency compensated signal, using the channel buffer in-place. _outputLatencyComp->read(i, nframes, buffer[i]); } } } } //--------------------------------------------------------- // write //--------------------------------------------------------- void AudioOutput::write(int level, Xml& xml) const { xml.tag(level++, "AudioOutput"); AudioTrack::writeProperties(level, xml); xml.etag(level, "AudioOutput"); } //--------------------------------------------------------- // read //--------------------------------------------------------- void AudioOutput::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (AudioTrack::readProperties(xml, tag)) xml.unknown("AudioOutput"); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "AudioOutput") { registerPorts(); // allocate jack ports mapRackPluginsToControllers(); return; } default: break; } } } RouteCapabilitiesStruct AudioOutput::routeCapabilities() const { RouteCapabilitiesStruct s = AudioTrack::routeCapabilities(); // Support Midi Track to Audio Input Track routes (for soloing chain). s._trackChannels._outRoutable = true; s._trackChannels._outChannels = 0; s._jackChannels._outRoutable = false; s._jackChannels._outChannels = totalProcessBuffers(); return s; } //--------------------------------------------------------- // write //--------------------------------------------------------- void AudioGroup::write(int level, Xml& xml) const { xml.tag(level++, "AudioGroup"); AudioTrack::writeProperties(level, xml); xml.etag(level, "AudioGroup"); } //--------------------------------------------------------- // read //--------------------------------------------------------- void AudioGroup::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (AudioTrack::readProperties(xml, tag)) xml.unknown("AudioGroup"); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "AudioGroup") { mapRackPluginsToControllers(); return; } default: break; } } } //--------------------------------------------------------- // write //--------------------------------------------------------- void AudioAux::write(int level, Xml& xml) const { xml.tag(level++, "AudioAux"); AudioTrack::writeProperties(level, xml); xml.intTag(level, "index", _index); xml.etag(level, "AudioAux"); } //--------------------------------------------------------- // getNextAuxIndex //--------------------------------------------------------- int getNextAuxIndex() { int curAux=0; AuxList * al = MusEGlobal::song->auxs(); for (MusECore::iAudioAux i = al->begin(); i != al->end(); ++i) { MusECore::AudioAux* ax = *i; printf("aux index %d\n", ax->index()); if (ax->index() > curAux) { printf("found new index! %d\n", ax->index()); curAux = ax->index(); } } return curAux+1; } //--------------------------------------------------------- // AudioAux //--------------------------------------------------------- AudioAux::AudioAux() : AudioTrack(AUDIO_AUX) { _index = getNextAuxIndex(); for(int i = 0; i < MusECore::MAX_CHANNELS; ++i) { if(i < channels()) { #ifdef _WIN32 buffer[i] = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(buffer[i] == NULL) { fprintf(stderr, "ERROR: AudioAux ctor: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: AudioAux ctor: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) buffer[i][q] = MusEGlobal::denormalBias; } else memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); } else buffer[i] = 0; } } AudioAux::AudioAux(const AudioAux& t, int flags) : AudioTrack(t, flags) { _index = getNextAuxIndex(); for(int i = 0; i < MusECore::MAX_CHANNELS; ++i) { if(i < channels()) { #ifdef _WIN32 buffer[i] = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(buffer[i] == NULL) { fprintf(stderr, "ERROR: AudioAux ctor: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: AudioAux ctor: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) buffer[i][q] = MusEGlobal::denormalBias; } else memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); } else buffer[i] = 0; } } //--------------------------------------------------------- // AudioAux //--------------------------------------------------------- AudioAux::~AudioAux() { for (int i = 0; i < MusECore::MAX_CHANNELS; ++i) { if (buffer[i]) free(buffer[i]); } } //--------------------------------------------------------- // read //--------------------------------------------------------- void AudioAux::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "index") _index = xml.parseInt(); else if (AudioTrack::readProperties(xml, tag)) xml.unknown("AudioAux"); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "AudioAux") { mapRackPluginsToControllers(); return; } default: break; } } } //--------------------------------------------------------- // getData //--------------------------------------------------------- bool AudioAux::getData(unsigned pos, int ch, unsigned samples, float** data) { // Make sure all the aux-supporting tracks are processed first so aux data is gathered. TrackList* tl = MusEGlobal::song->tracks(); AudioTrack* track; for(ciTrack it = tl->begin(); it != tl->end(); ++it) { if((*it)->isMidiTrack()) continue; track = (AudioTrack*)(*it); // If there are any Aux route paths to the track, defer processing until the second main track processing pass. if(!track->processed() && track->hasAuxSend() && !track->auxRefCount()) { int chans = track->channels(); // Just a dummy buffer. float* buff[chans]; float buff_data[samples * chans]; for (int i = 0; i < chans; ++i) buff[i] = buff_data + i * samples; track->copyData(pos, -1, chans, chans, -1, -1, samples, buff); } } for (int i = 0; i < ch; ++i) data[i] = buffer[i % channels()]; return true; } //--------------------------------------------------------- // setChannels //--------------------------------------------------------- void AudioAux::setChannels(int n) { const int old_chans = channels(); AudioTrack::setChannels(n); const int new_chans = channels(); if(new_chans > old_chans) { for(int i = old_chans; i < new_chans; ++i) { #ifdef _WIN32 buffer[i] = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(buffer[i] == NULL) { fprintf(stderr, "ERROR: AudioTrack::setChannels: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: AudioAux::setChannels: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) buffer[i][q] = MusEGlobal::denormalBias; } else memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); } } else if(new_chans < old_chans) { for(int i = new_chans; i < old_chans; ++i) { if(buffer[i]) free(buffer[i]); } } } //--------------------------------------------------------- // setRecordFlag1 // gui part (executed in gui thread) //--------------------------------------------------------- bool AudioTrack::setRecordFlag1(bool f) { if(!canRecord()) return false; if (f == _recordFlag) return true; if (f) { if (_recFile.isNull() && MusEGlobal::song->record()) { // this rec-enables a track if the global arm already was done // the standard case would be that rec-enable be done there prepareRecording(); } } else { if (_recFile) { // this file has not been processed and can be // deleted // We should only arrive here if going from a 'record-armed' state // to a non record-armed state. Because otherwise after actually // recording, the _recFile pointer is made into an event, // then _recFile is made zero before this function is called. QString s = _recFile->path(); setRecFile(nullptr); remove(s.toLatin1().constData()); if(MusEGlobal::debugMsg) printf("AudioNode::setRecordFlag1: remove file %s if it exists\n", s.toLatin1().constData()); } } return true; } //--------------------------------------------------------- // prepareRecording // normally called from MusEGlobal::song->setRecord to defer creating // wave files until MusE is globally rec-enabled // also called from track->setRecordFlag (above) // if global rec enable already was done //--------------------------------------------------------- bool AudioTrack::prepareRecording() { if(MusEGlobal::debugMsg) printf("prepareRecording for track %s\n", name().toLatin1().constData()); if (_recFile.isNull()) { // // create soundfile for recording // const QString fbase = QString("%1/").arg(MusEGlobal::museProject) + QObject::tr("TRACK") + QString("_%1_").arg(name().simplified().replace(" ","_")) + QObject::tr("TAKE"); QFile fil; for (;;++recFileNumber) { fil.setFileName(fbase + QString("_%1.wav").arg(recFileNumber)); if (!fil.exists()) break; } _recFile = new MusECore::SndFile(fil.fileName()); _recFile->setFormat( SF_FORMAT_WAV | SF_FORMAT_FLOAT, _channels, MusEGlobal::sampleRate); } if (MusEGlobal::debugMsg) printf("AudioTrack::prepareRecording: init internal file %s\n", _recFile->path().toLatin1().constData()); if(_recFile->openWrite()) { QMessageBox::critical(NULL, "MusE write error.", "Error creating target wave file\n" "Check your configuration."); return false; } // For bounce operations: Reset these. _recFilePos = 0; _previousLatency = 0.0f; return true; } double AudioTrack::auxSend(int idx) const { if (unsigned(idx) >= _auxSend.size()) { printf("%s auxSend: bad index: %d >= %zd\n", name().toLatin1().constData(), idx, _auxSend.size()); return 0.0; } return _auxSend[idx]; } void AudioTrack::setAuxSend(int idx, double v) { if (unsigned(idx) >= _auxSend.size()) { printf("%s setAuxSend: bad index: %d >= %zd\n", name().toLatin1().constData(), idx, _auxSend.size()); return; } _auxSend[idx] = v; } //--------------------------------------------------------- // height //--------------------------------------------------------- int AudioOutput::height() const { if (_isVisible) return _height; return 0; } int AudioInput::height() const { if (_isVisible) return _height; return 0; } int AudioAux::height() const { if (_isVisible) return _height; return 0; } int AudioGroup::height() const { if (_isVisible) return _height; return 0; } int WaveTrack::height() const { if (_isVisible) return _height; return 0; } } // namespace MusECore muse-4.0.0/src/muse/cleftypes.h000066400000000000000000000020621404105311000163600ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // cleftypes.h // (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CLEFTYPES_H__ #define __CLEFTYPES_H__ enum clefTypes { trebleClef, bassClef, grandStaff }; #endif muse-4.0.0/src/muse/cliplist/000077500000000000000000000000001404105311000160345ustar00rootroot00000000000000muse-4.0.0/src/muse/cliplist/CMakeLists.txt000066400000000000000000000040201404105311000205700ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP (cliplist_mocs cliplist.h ) ## ## UI files ## file (GLOB cliplist_ui_files cliplisteditorbase.ui ) QT5_WRAP_UI ( cliplist_uis ${cliplist_ui_files} ) ## ## List of source files to compile ## file (GLOB cliplist_source_files cliplist.cpp ) ## ## Define target ## add_library ( cliplist ${MODULES_BUILD} ${cliplist_source_files} ${cliplist_mocs} ${cliplist_uis} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${cliplist_source_files} ${cliplist_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( cliplist PROPERTIES OUTPUT_NAME muse_cliplist ) ## ## Linkage ## target_link_libraries ( cliplist ${QT_LIBRARIES} widgets ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS cliplist DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/cliplist/cliplist.cpp000066400000000000000000000204611404105311000203660ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: cliplist.cpp,v 1.6.2.3 2008/08/18 00:15:24 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "cliplist.h" #include "song.h" #include "globals.h" #include "wave.h" // Forwards from header: #include #include #include "xml.h" #include "pos.h" namespace MusEGui { extern int mtcType; enum { COL_NAME=0, COL_REFS, COL_SAMPLERATE, COL_LEN, COL_DATA, COL_STATUS }; ClipListEditorBaseWidget::ClipListEditorBaseWidget(QWidget *parent) : QWidget(parent) { setupUi(this); } //--------------------------------------------------------- // ClipItem //--------------------------------------------------------- class ClipItem : public QTreeWidgetItem { MusECore::SndFileR _wf; //virtual QString text(int) const; public: ClipItem(QTreeWidget*, const MusECore::SndFileR&); MusECore::SndFileR* wf() { return &_wf; } }; ClipItem::ClipItem(QTreeWidget* parent, const MusECore::SndFileR& w) : QTreeWidgetItem(parent), _wf(w) { setText(COL_NAME, _wf.name()); setText(COL_REFS, QString().setNum(_wf.getRefCount())); setText(COL_SAMPLERATE, QString().setNum(_wf.samplerate())); setText(COL_LEN, QString().setNum(_wf.samples())); setText(COL_STATUS, _wf.isOpen() ? QObject::tr("Open") : QObject::tr("Closed")); } //--------------------------------------------------------- // samples2smpte //--------------------------------------------------------- #if 0 // DELETETHIS ? static QString samples2smpte(int samples) { double time = double(samples) / double(sampleRate); int min = int(time) / 60; int sec = int(time) % 60; double rest = time - (min * 60 + sec); switch(mtcType) { case 0: // 24 frames sec rest *= 24; break; case 1: // 25 rest *= 25; break; case 2: // 30 drop frame rest *= 30; break; case 3: // 30 non drop frame rest *= 30; break; } int frame = int(rest); int subframe = int((rest-frame)*100); QString s; s.sprintf("%03d:%02d:%02d:%02d", min, sec, frame, subframe); return s; } #endif /* DELETETHIS //--------------------------------------------------------- // text //--------------------------------------------------------- QString ClipItem::text(int col) const { QString s(""); switch(col) { case COL_NAME: s = _wf.name(); break; case COL_POS: case COL_LEN: break; case COL_REFS: s.setNum(_wf.getRefCount()); break; } return s; } */ //--------------------------------------------------------- // ClipListEdit //--------------------------------------------------------- ClipListEdit::ClipListEdit(QWidget* parent) : QWidget(parent) { setObjectName("ClipList"); QVBoxLayout* vbox = new QVBoxLayout(this); editor = new ClipListEditorBaseWidget; vbox->addWidget(editor); // NOTICE: Please ensure that any tool bar object names here match the names assigned // to identical or similar toolbars in class MusE or other TopWin classes. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). QFontMetrics fm(editor->view->font()); int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth,0, this); // ddskrjo 0 // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = 2 + fm.horizontalAdvance('9') * 9 + fm.horizontalAdvance(':') * 3 + fw * 4; #else int w = 2 + fm.width('9') * 9 + fm.width(':') * 3 + fw * 4; #endif editor->view->setColumnWidth(COL_SAMPLERATE, w); editor->view->setColumnWidth(COL_LEN, w); connect(editor->view, SIGNAL(itemSelectionChanged()), SLOT(clipSelectionChanged())); connect(editor->view, SIGNAL(itemClicked(QTreeWidgetItem*, int)), SLOT(clicked(QTreeWidgetItem*, int))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(editor->start, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(startChanged(const MusECore::Pos&))); connect(editor->len, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(lenChanged(const MusECore::Pos&))); updateList(); } ClipListEdit::~ClipListEdit() { } //--------------------------------------------------------- // updateList //--------------------------------------------------------- void ClipListEdit::updateList() { editor->view->clear(); for (MusECore::iSndFile f = MusEGlobal::sndFiles.begin(); f != MusEGlobal::sndFiles.end(); ++f) { new ClipItem(editor->view, *f); } clipSelectionChanged(); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void ClipListEdit::songChanged(MusECore::SongChangedStruct_t type) { if(type & (SC_CLIP_MODIFIED | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED)) updateList(); } //--------------------------------------------------------- // startChanged //--------------------------------------------------------- void ClipListEdit::startChanged(const MusECore::Pos& /*pos*/)//prevent compiler warning: unsused parameter { // editor->view->triggerUpdate(); DELETETHIS whole function? } //--------------------------------------------------------- // lenChanged //--------------------------------------------------------- void ClipListEdit::lenChanged(const MusECore::Pos& /*pos*/) //prevent compiler warning: unsused parameter { // curClip.setLenFrame(pos.frame()); DELETETHIS whole function? // editor->view->triggerUpdate(); } //--------------------------------------------------------- // clipSelectionChanged //--------------------------------------------------------- void ClipListEdit::clipSelectionChanged() { // ClipItem* item = (ClipItem*)(editor->view->selectedItem()); // if (item == 0) { editor->start->setEnabled(false); editor->len->setEnabled(false); return; /* DELETETHIS and the above two comments } editor->start->setEnabled(true); editor->len->setEnabled(true); MusECore::Pos pos, len; pos.setType(MusECore::Pos::FRAMES); len.setType(MusECore::Pos::FRAMES); pos.setFrame(curClip.spos()); len.setFrame(curClip.lenFrame()); editor->start->setValue(pos); editor->len->setValue(len); */ } //--------------------------------------------------------- // clicked //--------------------------------------------------------- void ClipListEdit::clicked(QTreeWidgetItem*, int) { // printf("clicked\n"); DELETETHIS whole function } QSize ClipListEdit::sizeHint() const { return QSize(minimumWidth(), 400); } } // namespace MusEGui muse-4.0.0/src/muse/cliplist/cliplist.h000066400000000000000000000043771404105311000200430ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: cliplist.h,v 1.3.2.1 2005/12/11 21:29:23 spamatica Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CLIPLIST_H__ #define __CLIPLIST_H__ #include "ui_cliplisteditorbase.h" #include #include "type_defs.h" #include "cobject.h" // Forward declarations: class QCloseEvent; class QTreeWidgetItem; namespace MusECore { class Xml; class Pos; } namespace MusEGui { //--------------------------------------------------------- // ClipListEditorBaseWidget // Wrapper around Ui::ClipListEditorBase //--------------------------------------------------------- class ClipListEditorBaseWidget : public QWidget, public Ui::ClipListEditorBase { Q_OBJECT public: ClipListEditorBaseWidget(QWidget *parent = nullptr); }; //--------------------------------------------------------- // ClipListEdit //--------------------------------------------------------- class ClipListEdit : public QWidget { Q_OBJECT ClipListEditorBaseWidget* editor; void updateList(); virtual QSize sizeHint() const; private slots: void songChanged(MusECore::SongChangedStruct_t); void startChanged(const MusECore::Pos&); void lenChanged(const MusECore::Pos&); void clipSelectionChanged(); void clicked(QTreeWidgetItem*, int); public: ClipListEdit(QWidget* parent); ~ClipListEdit(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/cliplist/cliplisteditorbase.ui000066400000000000000000000074131404105311000222650ustar00rootroot00000000000000 ClipListEditorBase 0 0 600 480 MusE: ClipList 6 0 0 0 0 true true Name Refs Samplerate Len Data Status Clip Properties 6 11 11 11 11 Pos: false true Len: false true Qt::Horizontal QSizePolicy::Expanding 20 20 MusEGui::PosEdit QWidget

posedit.h
muse-4.0.0/src/muse/cobject.cpp000066400000000000000000000601041404105311000163270ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: cobject.cpp,v 1.4 2004/02/02 12:10:09 wschweer Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "cobject.h" #include "gui.h" #include "globals.h" #include "app.h" #include "shortcuts.h" #include "songpos_toolbar.h" #include "sig_tempo_toolbar.h" #include "gconfig.h" #include "helper.h" #include "song.h" #include "icons.h" #include "rectoolbar.h" #include "postoolbar.h" #include "synctoolbar.h" #include #include #include // Forwards from header: #include #include #include #include #include #include "xml.h" // For debugging output: Uncomment the fprintf section. #define ERROR_COBJECT(dev, format, args...) fprintf(dev, format, ##args) #define DEBUG_COBJECT(dev, format, args...) // fprintf(dev, format, ##args) using std::list; using MusEGlobal::muse; namespace MusEGui { int TopWin::_widthInit[TOPLEVELTYPE_LAST_ENTRY]; int TopWin::_heightInit[TOPLEVELTYPE_LAST_ENTRY]; QByteArray TopWin::_toolbarSharedInit[TOPLEVELTYPE_LAST_ENTRY]; QByteArray TopWin::_toolbarNonsharedInit[TOPLEVELTYPE_LAST_ENTRY]; bool TopWin::_openTabbed[TOPLEVELTYPE_LAST_ENTRY]; bool TopWin::initInited=false; TopWin::TopWin(ToplevelType t, QWidget* parent, const char* name, Qt::WindowFlags f) : QMainWindow(parent, f) { _initalizing = true; _isDeleting = false; if (!initInited) initConfiguration(); _type=t; setObjectName(name ? QString(name) : "TopWin"); //setDockNestingEnabled(true); // Allow multiple rows. Tim. setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize)); setWindowIcon(typeIcon(_type)); setAttribute(Qt::WA_DeleteOnClose); subwinAction = new QAction(tr("Tabbed/Floating"), this); subwinAction->setCheckable(true); subwinAction->setStatusTip(tr("Display editor in a tab or in a separate window (preset in Global Settings->Editors).")); subwinAction->setShortcut(shortcuts[SHRT_TABBED_WIN].key); connect(subwinAction, SIGNAL(toggled(bool)), SLOT(setIsMdiWin(bool))); // shareAction=new QAction(tr("Shares Tools and Menu"), this); // shareAction->setCheckable(true); // connect(shareAction, SIGNAL(toggled(bool)), SLOT(shareToolsAndMenu(bool))); fullscreenAction=new QAction(tr("Fullscreen"), this); fullscreenAction->setCheckable(true); fullscreenAction->setChecked(false); fullscreenAction->setShortcut(shortcuts[SHRT_FULLSCREEN].key); connect(fullscreenAction, SIGNAL(toggled(bool)), SLOT(setFullscreen(bool))); mdisubwin = nullptr; _sharesToolsAndMenu=_openTabbed[_type]; if (_openTabbed[_type] && !MusEGlobal::unityWorkaround) { setIsMdiWin(true); _savedToolbarState=_toolbarNonsharedInit[_type]; } if (_sharesToolsAndMenu) menuBar()->hide(); subwinAction->setChecked(isMdiWin()); // shareAction->setChecked(_sharesToolsAndMenu); if (MusEGlobal::unityWorkaround) { _sharesToolsAndMenu=false; // shareAction->setEnabled(false); subwinAction->setEnabled(false); } fullscreenAction->setEnabled(!isMdiWin()); if (_type == ARRANGER) { // shareAction->setEnabled(false); subwinAction->setEnabled(false); } if (!mdisubwin) resize(_widthInit[_type], _heightInit[_type]); //-------------------------------------------------- // Toolbar //-------------------------------------------------- // NOTICE: Please ensure that any tool bar object names here match the names // assigned in the 'toolbar' creation section of MusE::MusE(), // or any other TopWin class. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). QToolBar* undo_tools=addToolBar(tr("Undo/Redo")); undo_tools->setObjectName("Undo/Redo"); undo_tools->addActions(MusEGlobal::undoRedo->actions()); QToolBar* panic_toolbar = addToolBar(tr("Panic")); panic_toolbar->setObjectName("Panic tool"); panic_toolbar->addAction(MusEGlobal::panicAction); QToolBar* metronome_toolbar = addToolBar(tr("Metronome")); metronome_toolbar->setObjectName("Metronome tool"); metronome_toolbar->addAction(MusEGlobal::metronomeAction); QToolBar* songpos_tb = addToolBar(tr("Timeline")); songpos_tb->setObjectName("Timeline tool"); songpos_tb->addWidget(new MusEGui::SongPosToolbarWidget(songpos_tb)); songpos_tb->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); songpos_tb->setContextMenuPolicy(Qt::PreventContextMenu); QToolBar* transportToolbar = addToolBar(tr("Transport")); transportToolbar->setObjectName("Transport tool"); transportToolbar->addActions(MusEGlobal::transportAction->actions()); transportToolbar->setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize)); RecToolbar *recToolbar = new RecToolbar(tr("Recording"), this); addToolBar(recToolbar); SyncToolbar *syncToolbar = new SyncToolbar(tr("Sync"), this); addToolBar(syncToolbar); addToolBarBreak(); TempoToolbar* tempo_tb = new TempoToolbar(tr("Tempo"), this); addToolBar(tempo_tb); SigToolbar* sig_tb = new SigToolbar(tr("Signature"), this); addToolBar(sig_tb); PosToolbar *posToolbar = new PosToolbar(tr("Position"), this); addToolBar(posToolbar); connect(tempo_tb, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(tempo_tb, SIGNAL(escapePressed()), SLOT(focusCanvas())); connect(tempo_tb, SIGNAL(masterTrackChanged(bool)), MusEGlobal::song, SLOT(setMasterFlag(bool))); connect(sig_tb, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(sig_tb, SIGNAL(escapePressed()), SLOT(focusCanvas())); // this is not (longer?) the case, to be tested on KDE (kybos) // what about changing from MDI to top window later? then the parent remains anyway... (kybos) // // NOTICE: It seems after the switch to Qt5, windows with a parent have stay-on-top behaviour. // // But with the fix below, other TopWin destructors are not called when closing the app. // // So there is now an additional fix in MusE::closeEvent() which deletes all parentless TopWin. // // // /* unconnect parent if window is not mdi */ // /* to make editor windows not stay on top */ // if(!isMdiWin()) // { // setParent(nullptr); // } } TopWin::~TopWin() { DEBUG_COBJECT(stderr, "TopWin dtor: %s\n", objectName().toLatin1().constData()); // Toolbars must be deleted explicitly to avoid memory leakage and corruption. // For some reason (toolbar sharing?) they are reparented and thus // not destroyed by the original parent topwin when closed. for (auto& it : _toolbars) { if (it) { delete it; it = nullptr; } } if (mdisubwin) mdisubwin->close(); } //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void TopWin::readStatus(MusECore::Xml& xml) { int x=0, y=0, width=800, height=600; bool wsMinimized = false; bool wsMaximized = false; bool wsFullScreen = false; bool wsActive = false; for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; QString tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "x") x=xml.parseInt(); else if (tag == "y") y=xml.parseInt(); else if (tag == "width") width=xml.parseInt(); else if (tag == "height") height=xml.parseInt(); else if (tag == "wsMinimized") wsMinimized=xml.parseInt(); else if (tag == "wsMaximized") wsMaximized=xml.parseInt(); else if (tag == "wsFullScreen") wsFullScreen=xml.parseInt(); else if (tag == "wsActive") wsActive=xml.parseInt(); else if (tag == "toolbars") { if (!sharesToolsAndMenu()) { if (!restoreState(QByteArray::fromHex(xml.parse1().toLatin1()))) { fprintf(stderr,"ERROR: couldn't restore toolbars. trying default configuration...\n"); if (!restoreState(_toolbarNonsharedInit[_type])) fprintf(stderr,"ERROR: couldn't restore default toolbars. this is not really a problem.\n"); } } else { _savedToolbarState = QByteArray::fromHex(xml.parse1().toLatin1()); if (_savedToolbarState.isEmpty()) _savedToolbarState=_toolbarNonsharedInit[_type]; } } // else if (tag == "shares_menu") // { // shareToolsAndMenu(xml.parseInt()); // } else if (tag == "is_subwin") { setIsMdiWin(xml.parseInt()); } else xml.unknown("TopWin"); break; case MusECore::Xml::TagEnd: if (tag == "topwin") { if (mdisubwin) { QFlags wstate = Qt::WindowMaximized; if (wsActive) wstate |= Qt::WindowActive; setWindowState(wstate); } else { const QRect geo(x, y, width, height); QFlags wstate; if(wsMinimized) wstate |= Qt::WindowMinimized; if(wsMaximized) wstate |= Qt::WindowMaximized; if(wsFullScreen) wstate |= Qt::WindowFullScreen; if(wsActive) wstate |= Qt::WindowActive; setGeometry(geo); setWindowState(wstate); } return; } break; default: break; } } } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void TopWin::writeStatus(int level, MusECore::Xml& xml) const { xml.tag(level++, "topwin"); // the order of these tags has a certain sense // changing it won't break muse, but it may break proper // restoring of the positions xml.intTag(level, "is_subwin", isMdiWin()); QRect geo; QFlags wstate; if (mdisubwin) { wstate = mdisubwin->windowState(); geo = mdisubwin->normalGeometry(); // TESTED on Qt5.3: For MDI geo was invalid (0, 0, -1, -1) when window maximized. // This may be a reported Qt bug I read about. if(!geo.isValid()) geo = mdisubwin->geometry(); } else { wstate = windowState(); geo = normalGeometry(); if(!geo.isValid()) geo = geometry(); } // The order of geo first then state may be important here. xml.intTag(level, "x", geo.x()); xml.intTag(level, "y", geo.y()); xml.intTag(level, "width", geo.width()); xml.intTag(level, "height", geo.height()); if(wstate.testFlag(Qt::WindowMinimized)) xml.intTag(level, "wsMinimized", 1); if(wstate.testFlag(Qt::WindowMaximized)) xml.intTag(level, "wsMaximized", 1); if(wstate.testFlag(Qt::WindowFullScreen)) xml.intTag(level, "wsFullScreen", 1); if(wstate.testFlag(Qt::WindowActive)) xml.intTag(level, "wsActive", 1); // xml.intTag(level, "shares_menu", sharesToolsAndMenu()); if (sharesToolsAndMenu()) xml.strTag(level, "toolbars", _savedToolbarState.toHex().data()); else xml.strTag(level, "toolbars", saveState().toHex().data()); xml.tag(level, "/topwin"); } void TopWin::hide() { if (mdisubwin) mdisubwin->hide(); QMainWindow::hide(); } void TopWin::show() { if (mdisubwin) mdisubwin->showMaximized(); QMainWindow::show(); } void TopWin::setVisible(bool param) { if (mdisubwin) { if (param) mdisubwin->show(); else mdisubwin->hide(); } QMainWindow::setVisible(param); } void TopWin::createMdiWrapper() { if (mdisubwin == nullptr) { mdisubwin = new QMdiSubWindow(); mdisubwin->setWidget(this); mdisubwin->setWindowIcon(typeIcon(_type)); if (_type == ARRANGER) { mdisubwin->setWindowFlags(Qt::CustomizeWindowHint); } else { mdisubwin->setAttribute(Qt::WA_DeleteOnClose); mdisubwin->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint); } } } void TopWin::setIsMdiWin(bool val) { if (MusEGlobal::unityWorkaround) return; if (!val && _type == ARRANGER) return; if (val) { if (!isMdiWin()) { _savedToolbarState = saveState(); createMdiWrapper(); muse->addMdiSubWindow(mdisubwin); if (windowTitle().startsWith("MusE: ")) setWindowTitle(windowTitle().mid(6)); shareToolsAndMenu(true); fullscreenAction->setEnabled(false); fullscreenAction->setChecked(false); { const QSignalBlocker blocker(subwinAction); subwinAction->setChecked(true); } muse->updateWindowMenu(); mdisubwin->showMaximized(); muse->setActiveMdiSubWindow(mdisubwin); } else { if (MusEGlobal::debugMsg) printf("TopWin::setIsMdiWin(true) called, but window is already a MDI win\n"); } } else { if (isMdiWin()) { mdisubwin->setWidget(nullptr); mdisubwin->close(); mdisubwin = nullptr; setParent(muse); setWindowFlags(Qt::Window); if (!windowTitle().startsWith("MusE: ")) setWindowTitle(windowTitle().insert(0, "MusE: ")); shareToolsAndMenu(false); fullscreenAction->setEnabled(true); { const QSignalBlocker blocker(subwinAction); subwinAction->setChecked(false); } muse->updateWindowMenu(); QMainWindow::show(); } else { if (MusEGlobal::debugMsg) printf("TopWin::setIsMdiWin(false) called, but window is not a MDI win\n"); } } } bool TopWin::isMdiWin() const { return (mdisubwin != nullptr); } void TopWin::insertToolBar(QToolBar*, QToolBar*) { printf("ERROR: THIS SHOULD NEVER HAPPEN: TopWin::insertToolBar called, but it's not implemented! ignoring it\n"); } void TopWin::insertToolBarBreak(QToolBar*) { printf("ERROR: THIS SHOULD NEVER HAPPEN: TopWin::insertToolBarBreak called, but it's not implemented! ignoring it\n"); } void TopWin::removeToolBar(QToolBar*) { printf("ERROR: THIS SHOULD NEVER HAPPEN: TopWin::removeToolBar called, but it's not implemented! ignoring it\n"); } void TopWin::removeToolBarBreak(QToolBar*) { printf("ERROR: THIS SHOULD NEVER HAPPEN: TopWin::removeToolBarBreak called, but it's not implemented! ignoring it\n"); } void TopWin::addToolBar(Qt::ToolBarArea, QToolBar* tb) { printf("ERROR: THIS SHOULD NEVER HAPPEN: TopWin::addToolBar(Qt::ToolBarArea, QToolBar*) called, but it's not implemented!\nusing addToolBar(QToolBar*) instead\n"); addToolBar(tb);} void TopWin::addToolBar(QToolBar* toolbar) { _toolbars.push_back(toolbar); if (!_sharesToolsAndMenu || MusEGlobal::unityWorkaround) QMainWindow::addToolBar(toolbar); else toolbar->hide(); toolbar->setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize)); } QToolBar* TopWin::addToolBar(const QString& title) { QToolBar* toolbar = new QToolBar(title, this); addToolBar(toolbar); return toolbar; } void TopWin::addToolBarBreak(Qt::ToolBarArea area) { QMainWindow::addToolBarBreak(area); _toolbars.push_back(nullptr); } void TopWin::shareToolsAndMenu(bool val) { if (MusEGlobal::unityWorkaround) return; if (_sharesToolsAndMenu == val) { if (MusEGlobal::debugMsg) printf("TopWin::shareToolsAndMenu() called but has no effect\n"); return; } _sharesToolsAndMenu = val; if (!val) { muse->shareMenuAndToolbarChanged(this, false); for (const auto& it : _toolbars) if (it) { QMainWindow::addToolBar(it); it->show(); } else QMainWindow::addToolBarBreak(); restoreState(_savedToolbarState); _savedToolbarState.clear(); menuBar()->show(); } else { if (_savedToolbarState.isEmpty()) // this check avoids overwriting a previously saved state _savedToolbarState = saveState(); // (by setIsMdiWin) with a now incorrect (empty) state for (const auto& it : _toolbars) if (it) { QMainWindow::removeToolBar(it); // this does NOT delete the toolbar, which is good it->setParent(nullptr); } menuBar()->hide(); muse->shareMenuAndToolbarChanged(this, true); } } //--------------------------------------------------------- // storeInitialState //--------------------------------------------------------- void TopWin::storeInitialState() const { if (mdisubwin) { _widthInit[_type] = mdisubwin->width(); _heightInit[_type] = mdisubwin->height(); } else { _widthInit[_type] = width(); _heightInit[_type] = height(); } if (sharesToolsAndMenu()) { if (muse->getCurrentMenuSharingTopwin() == this) _toolbarSharedInit[_type] = muse->saveState(); } else _toolbarNonsharedInit[_type] = saveState(); // Store class-specific view settings. storeInitialViewState(); } //initConfiguration() restores default tabbed configuration void TopWin::initConfiguration() { if (initInited) return; for (int i = 0; i < TOPLEVELTYPE_LAST_ENTRY; i++) { _widthInit[i] = 800; _heightInit[i] = 600; _openTabbed[i] = true; } initInited = true; } //--------------------------------------------------------- // readConfiguration //--------------------------------------------------------- void TopWin::readConfiguration(ToplevelType t, MusECore::Xml& xml) { if (!initInited) initConfiguration(); for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "width") _widthInit[t] = xml.parseInt(); else if (tag == "height") _heightInit[t] = xml.parseInt(); else if (tag == "nonshared_toolbars") _toolbarNonsharedInit[t] = QByteArray::fromHex(xml.parse1().toLatin1()); else if (tag == "shared_toolbars") _toolbarSharedInit[t] = QByteArray::fromHex(xml.parse1().toLatin1()); else if (tag == "default_subwin") _openTabbed[t] = xml.parseInt(); else xml.unknown("TopWin"); break; case MusECore::Xml::TagEnd: if (tag == "topwin") return; break; default: break; } } } //--------------------------------------------------------- // writeConfiguration //--------------------------------------------------------- void TopWin::writeConfiguration(ToplevelType t, int level, MusECore::Xml& xml) { if (!initInited) { printf ("WARNING: TopWin::writeConfiguration() called although the config hasn't been\n" " initialized! writing default configuration\n"); initConfiguration(); } xml.tag(level++, "topwin"); xml.intTag(level, "width", _widthInit[t]); xml.intTag(level, "height", _heightInit[t]); xml.strTag(level, "nonshared_toolbars", _toolbarNonsharedInit[t].toHex().data()); xml.strTag(level, "shared_toolbars", _toolbarSharedInit[t].toHex().data()); xml.intTag(level, "default_subwin", _openTabbed[t]); xml.etag(level, "topwin"); } void TopWin::finalizeInit() { MusEGlobal::muse->topwinMenuInited(this); _initalizing=false; } void TopWin::initTopwinState() { if (sharesToolsAndMenu()) { if (this == muse->getCurrentMenuSharingTopwin()) muse->restoreState(_toolbarSharedInit[_type]); } else restoreState(_toolbarNonsharedInit[_type]); } void TopWin::restoreMainwinState() { if (sharesToolsAndMenu()) initTopwinState(); } QString TopWin::typeName(ToplevelType t) { switch (t) { case PIANO_ROLL: return tr("Piano roll"); // case LISTE: return tr("List editor"); case DRUM: return tr("Drum editor"); case MASTER: return tr("Master track editor"); case WAVE: return tr("Wave editor"); case SCORE: return tr("Score editor"); case ARRANGER: return tr("Arranger"); default: return tr(""); } } QIcon TopWin::typeIcon(ToplevelType t) { switch (t) { case ARRANGER: return QIcon(*arrangerSVGIcon); case PIANO_ROLL: return QIcon(*pianorollSVGIcon); case DRUM: return QIcon(*drumeditSVGIcon); case MASTER: return QIcon(*mastereditSVGIcon); case WAVE: return QIcon(*waveeditorSVGIcon); case SCORE: return QIcon(*scoreeditSVGIcon); default: return QIcon(); } } void TopWin::setFullscreen(bool val) { if (val) showFullScreen(); else showNormal(); } void TopWin::resize(int w, int h) { if (isMdiWin()) return; QMainWindow::resize(w,h); } void TopWin::setWindowTitle (const QString& title) { QMainWindow::setWindowTitle(title); muse->updateWindowMenu(); } void TopWin::storeSettings() {} //void TopWin::windowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState) //{ // // Due to bug in Oxygen and Breeze at least on *buntu 16.04 LTS and some other distros, // // force the style and stylesheet again. Otherwise the window freezes. // // Ignore the Qt::WindowActive flag. // if((oldState & (Qt::WindowNoState | Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen)) != // (newState & (Qt::WindowNoState | Qt::WindowMinimized | Qt::WindowMaximized | Qt::WindowFullScreen))) // { // if(MusEGlobal::debugMsg) // fprintf(stderr, "TopWin::windowStateChanged oldState:%d newState:%d Calling updateThemeAndStyle()\n", int(oldState), int(newState)); // MusEGui::updateThemeAndStyle(true); // } //} TopWin* ToplevelList::findType(TopWin::ToplevelType type) const { for (ciToplevel i = begin(); i != end(); ++i) { if((*i)->type() == type) return (*i); } return nullptr; } } // namespace MusEGui muse-4.0.0/src/muse/cobject.h000066400000000000000000000123531404105311000157770ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: cobject.h,v 1.3.2.1 2005/12/11 21:29:24 spamatica Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __COBJECT_H__ #define __COBJECT_H__ #include "config.h" #include #include #include #include // Forward declarations: class QMdiSubWindow; class QFocusEvent; class QCloseEvent; class QToolBar; class QAction; namespace MusECore { class Xml; } namespace MusEGui { //--------------------------------------------------------- // TopWin //--------------------------------------------------------- class TopWin : public QMainWindow { Q_OBJECT public: enum ToplevelType { PIANO_ROLL=0, DRUM, MASTER, WAVE, SCORE, ARRANGER, // no gaps in the enum! #ifdef PATCHBAY M_PATCHBAY, #endif /* PATCHBAY */ TOPLEVELTYPE_LAST_ENTRY //this has to be always the last entry }; ToplevelType type() const { return _type; } static QString typeName(ToplevelType t); static QIcon typeIcon(ToplevelType t); bool initalizing() const { return _initalizing; } bool deleting() const { return _isDeleting; } virtual void readStatus(MusECore::Xml&); virtual void writeStatus(int, MusECore::Xml&) const; static void readConfiguration(ToplevelType, MusECore::Xml&); static void writeConfiguration(ToplevelType, int, MusECore::Xml&); virtual void storeSettings(); bool isMdiWin() const; QMdiSubWindow* getMdiWin() const { return mdisubwin; } TopWin(ToplevelType t, QWidget* parent=nullptr, const char* name=nullptr, Qt::WindowFlags f = Qt::Window); virtual ~TopWin(); bool sharesToolsAndMenu() const { return _sharesToolsAndMenu; } const std::list& toolbars() { return _toolbars; } virtual void addToolBar(QToolBar* toolbar); virtual QToolBar* addToolBar(const QString& title); virtual void addToolBarBreak(Qt::ToolBarArea area = Qt::TopToolBarArea); virtual void insertToolBar(QToolBar*, QToolBar*); virtual void insertToolBarBreak(QToolBar*); virtual void removeToolBar(QToolBar*); virtual void removeToolBarBreak(QToolBar*); virtual void addToolBar(Qt::ToolBarArea, QToolBar*); void resize(int w, int h); // void resize(const QSize&); // static bool _sharesWhenFree[TOPLEVELTYPE_LAST_ENTRY]; // static bool _sharesMenusToolbars[TOPLEVELTYPE_LAST_ENTRY]; static bool _openTabbed[TOPLEVELTYPE_LAST_ENTRY]; private: QMdiSubWindow* mdisubwin; bool _sharesToolsAndMenu; std::list _toolbars; bool _initalizing; void createMdiWrapper(); static void initConfiguration(); protected: QAction* subwinAction; // QAction* shareAction; QAction* fullscreenAction; ToplevelType _type; static int _widthInit[TOPLEVELTYPE_LAST_ENTRY]; static int _heightInit[TOPLEVELTYPE_LAST_ENTRY]; static QByteArray _toolbarNonsharedInit[TOPLEVELTYPE_LAST_ENTRY]; static QByteArray _toolbarSharedInit[TOPLEVELTYPE_LAST_ENTRY]; static bool initInited; QByteArray _savedToolbarState; // Set if close has been called on a TopWin having the WA_DeleteOnClose attribute. // The TopWins and any children should ignore any signals such as songChanged // which may cause a crash while deleting. bool _isDeleting; void finalizeInit(); void initTopwinState(); private slots: void setFullscreen(bool); public slots: virtual void hide(); virtual void show(); virtual void setVisible(bool); void setIsMdiWin(bool); void shareToolsAndMenu(bool); void restoreMainwinState(); void storeInitialState() const; virtual void storeInitialViewState() const { } virtual void setWindowTitle (const QString&); virtual void focusCanvas() { } // virtual void windowStateChanged(Qt::WindowStates oldState, Qt::WindowStates newState); }; //--------------------------------------------------------- // ToplevelList //--------------------------------------------------------- typedef std::list::iterator iToplevel; typedef std::list::const_iterator ciToplevel; class ToplevelList : public std::list { public: TopWin* findType(TopWin::ToplevelType) const; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/000077500000000000000000000000001404105311000163765ustar00rootroot00000000000000muse-4.0.0/src/muse/components/CMakeLists.txt000066400000000000000000000223651404105311000211460ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= #============================================================================= ## NOTICE: ## The 'widgets' folder and library is meant to be kept as ## self-contained and independent as possible, since it might ## be shared with other libraries, for example our SimpleDrums ## MESS synth, which is a plugin and must strive be independent. ## Not all our 'widgets' could be stripped down like this, ## so the 'components' folder and library contains more complex ## controls which are too dependent on other MusE modules or ## controls and dialogs which are really only useful to the ## main app or could never really be publicly exposed. ## ## As more of the 'components' might be stripped down and moved ## into the 'widgets' folder, the original structure of the ## complete widgets cmake file is preserved below for convenience ## and history and comparison. #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP (components_mocs aboutbox_impl.h appearance.h arrangercolumns.h # action.h audio_converter_settings.h background_painter.h bigtime.h canvas.h # checkbox.h choose_sysex.h # clipper_label.h # colorframe.h # comboQuant.h combobox.h comment.h compact_knob.h compact_patch_edit.h compact_slider.h confmport.h copy_on_write.h cpu_toolbar.h # ctrlcombo.h custom_widget_actions.h dentry.h didyouknow.h doublelabel.h # doublespinbox.h editevent.h editinstrument.h elided_label.h filedialog.h genset.h header.h # hitscale.h intlabel.h # knob.h # knob_and_meter.h lcd_widgets.h # lcombo.h # line_edit.h # menutitleitem.h # meter.h # meter_slider.h metronome.h midi_audio_control.h midisyncimpl.h midi_warn_init_pending_impl.h mixdowndialog.h # mlabel.h mtscale.h mtscale_flo.h # mtrackinfo.h nentry.h noteinfo.h partcolortoolbar.h pastedialog.h pasteeventsdialog.h pitchedit.h pitchlabel.h # pixmap_button.h plugindialog.h pluginsettings.h # popup_double_spinbox.h popupmenu.h posedit.h poslabel.h postoolbar.h projectcreateimpl.h raster_widgets.h rectoolbar.h routedialog.h routepopup.h #rubberband_settings.h savenewrevisiondialog.h scroll_area.h # scrollbar.h scrollscale.h shortcutcapturedialog.h shortcutconfig.h sigedit.h siglabel.h sigscale.h sigspinbox.h sig_tempo_toolbar.h slider.h # sliderbase.h snooper.h songinfo.h songpos_toolbar.h # spinbox.h # spinboxFP.h splitter.h #src_resampler_settings.h # swidget.h synctoolbar.h synthdialog.h tb1.h # tempolabel.h # text_edit.h tools.h trackinfo_layout.h tracks_duplicate.h # ttoolbar.h ttoolbutton.h unusedwavefiles.h view.h # vscale.h visibletracks.h vst_native_editor.h warn_bad_timing.h # widget_stack.h #zita_resampler_settings.h ) ## ## UI files ## file (GLOB components_ui_files aboutbox.ui arrangercolumnsbase.ui appearancebase.ui audio_converter_settings_base.ui choose_sysex_base.ui commentbase.ui configmidifilebase.ui copy_on_write_base.ui didyouknow.ui editctrlbase.ui editinstrumentbase.ui editnotedialogbase.ui editsysexdialogbase.ui fdialogbuttons.ui gensetbase.ui metronomebase.ui midisync.ui midi_warn_init_pending.ui midi_audio_control_base.ui mixdowndialogbase.ui # mtrackinfobase.ui pastedialogbase.ui pasteeventsdialogbase.ui plugindialogbase.ui pluginsettings.ui projectcreate.ui routedialogbase.ui #rubberband_settings_base.ui savenewrevisiondialog.ui shortcutcapturedialogbase.ui shortcutconfigbase.ui snooperbase.ui songinfo.ui #src_resampler_settings_base.ui synthdialogbase.ui synthconfigbase.ui tracks_duplicate_base.ui unusedwavefiles.ui warn_bad_timing.ui #zita_resampler_settings_base.ui ) QT5_WRAP_UI (components_ui_headers ${components_ui_files}) ## ## List of source files to compile ## file (GLOB components_source_files aboutbox_impl.cpp appearance.cpp arrangercolumns.cpp audio_converter_settings.cpp background_painter.cpp bigtime.cpp canvas.cpp # checkbox.cpp choose_sysex.cpp citem.cpp # clipper_label.cpp # colorframe.cpp # comboQuant.cpp combobox.cpp comment.cpp compact_knob.cpp compact_patch_edit.cpp compact_slider.cpp confmport.cpp copy_on_write.cpp cpu_toolbar.cpp # ctrlcombo.cpp custom_widget_actions.cpp dentry.cpp # dimap.cpp doublelabel.cpp # doublespinbox.cpp # drange.cpp editevent.cpp editinstrument.cpp elided_label.cpp filedialog.cpp genset.cpp header.cpp # hitscale.cpp intlabel.cpp # knob.cpp # knob_and_meter.cpp lcd_widgets.cpp # lcombo.cpp # line_edit.cpp # menutitleitem.cpp # meter.cpp meter_slider.cpp metronome.cpp midi_audio_control.cpp midisyncimpl.cpp midi_warn_init_pending_impl.cpp mixdowndialog.cpp # mlabel.cpp # mmath.cpp # mtrackinfo.cpp mtscale.cpp mtscale_flo.cpp nentry.cpp noteinfo.cpp partcolortoolbar.cpp pastedialog.cpp pasteeventsdialog.cpp pitchedit.cpp pitchlabel.cpp # pixmap_button.cpp plugindialog.cpp pluginsettings.cpp # popup_double_spinbox.cpp popupmenu.cpp posedit.cpp poslabel.cpp postoolbar.cpp projectcreateimpl.cpp raster_widgets.cpp rectoolbar.cpp routedialog.cpp routepopup.cpp #rubberband_settings.cpp savenewrevisiondialog.cpp # scldiv.cpp # scldraw.cpp # sclif.cpp scroll_area.cpp scrollbar.cpp scrollscale.cpp shortcutcapturedialog.cpp shortcutconfig.cpp sigedit.cpp siglabel.cpp sigscale.cpp sigspinbox.cpp sig_tempo_toolbar.cpp slider.cpp # sliderbase.cpp snooper.cpp songpos_toolbar.cpp # spinbox.cpp # spinboxFP.cpp splitter.cpp #src_resampler_settings.cpp # swidget.cpp synctoolbar.cpp synthdialog.cpp tb1.cpp # tempolabel.cpp # text_edit.cpp tools.cpp trackinfo_layout.cpp tracks_duplicate.cpp # ttoolbar.cpp ttoolbutton.cpp unusedwavefiles.cpp utils.cpp # velocity.cpp ??? view.cpp # vscale.cpp visibletracks.cpp vst_native_editor.cpp warn_bad_timing.cpp # widget_stack.cpp #zita_resampler_settings.cpp ) ## ## Define target ## add_library ( components ${MODULES_BUILD} ${components_ui_headers} ${components_mocs} ${components_source_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${components_source_files} ${components_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( components # PROPERTIES COMPILE_FLAGS "${MUSECXXFLAGS} -I../ -I${PROJECT_SOURCE_DIR}/synti " PROPERTIES COMPILE_FLAGS "${VST_SDK_QUIRK} ${MUSECXXFLAGS} -I../ -I${PROJECT_SOURCE_DIR}/synti" OUTPUT_NAME muse_components ) ## ## Linkage ## target_link_libraries ( components ${QT_LIBRARIES} widgets xml_module icons ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS components DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/components/aboutbox.ui000066400000000000000000000121331404105311000205600ustar00rootroot00000000000000 AboutBox 0 0 624 494 AboutBox 0 MusE 0 0 QFrame::NoFrame QFrame::Plain 0 Qt::Horizontal 40 20 QFrame::StyledPanel QFrame::Raised 4 Qt::Horizontal 40 20 Version info (replaced programmatically) false (C) Copyright 1999-2021 the MusE development team. See https://muse-sequencer.github.io for new versions and more information. Published under the GNU Public License. true &Keep On Rocking! Alt+K Qt::Horizontal Internals System information (useful for debugging): doneHere clicked() AboutBox close() 20 20 20 20 muse-4.0.0/src/muse/components/aboutbox_impl.cpp000066400000000000000000000145721404105311000217570ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/aboutbox_impl.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "aboutbox_impl.h" #include "config.h" #include "icons.h" #include "globals.h" #include "audio.h" #include "midiseq.h" #include "audiodev.h" #ifdef HAVE_RTAUDIO #include "driver/rtaudio.h" #endif namespace MusEGui { AboutBoxImpl::AboutBoxImpl() { setupUi(this); imageLabel->setPixmap(*aboutMuseImage); QString version(VERSION); QString gitstring(GITSTRING); QString verStr("Version: " + version); if (!qEnvironmentVariableIsEmpty("APPDIR")) verStr += " (AppImage)"; if (!gitstring.isEmpty()) verStr += "\ngit: " + gitstring; versionLabel->setText(verStr); /* System info tab */ internalDebugInformation->append("\n*** Build info ***"); if (!gitstring.isEmpty()) { QStringList sl = gitstring.split(" | "); internalDebugInformation->append(QString("Branch:\t\t%1").arg(sl.at(0))); internalDebugInformation->append(QString("Tag:\t\t%1").arg(sl.at(1))); internalDebugInformation->append(QString("Commit timestamp:\t%1").arg(sl.at(2))); } #ifdef LV2_SUPPORT internalDebugInformation->append("LV2 support:\t\tEnabled"); #endif #ifdef DSSI_SUPPORT internalDebugInformation->append("DSSI support:\t\tEnabled"); #endif #ifdef VST_NATIVE_SUPPORT #ifdef VST_VESTIGE_SUPPORT internalDebugInformation->append("Native VST support:\tEnabled (using VESTIGE compatibility layer)"); #else internalDebugInformation->append("Native VST support:\tEnabled (using Steinberg VSTSDK)"); #endif #endif internalDebugInformation->append("\n*** Runtime information ***"); internalDebugInformation->append(QString("Running audio driver:\t%1").arg(MusEGlobal::audioDevice->driverName())); #ifdef HAVE_RTAUDIO if (MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::RTAUDIO_AUDIO) { internalDebugInformation->append(QString("RT audio driver:\t%1").arg(((MusECore::RtAudioDevice*)MusEGlobal::audioDevice)->driverBackendName())); } #endif internalDebugInformation->append(QString("Sample rate:\t\t%1").arg(MusEGlobal::sampleRate)); internalDebugInformation->append(QString("Segment size:\t%1").arg(MusEGlobal::segmentSize)); internalDebugInformation->append(QString("Segment count:\t%1").arg(MusEGlobal::segmentCount)); internalDebugInformation->append("\n*** Timer ***"); if ((MusEGlobal::midiSeq)) { internalDebugInformation->append(QString("Type:\t\t%1").arg(MusEGlobal::midiSeq->getTimer()->getTimerName())); internalDebugInformation->append(QString("Frequency:\t\t%1").arg(MusEGlobal::midiSeq->getTimer()->getTimerFreq())); } else { internalDebugInformation->append("No timer information available as midiSeq is not instantiated."); } internalDebugInformation->append("\n*** Miscellaneous ***"); internalDebugInformation->append(QString("debugMode:\t\t%1").arg(MusEGlobal::debugMode?"true":"false")); internalDebugInformation->append(QString("midInputTrace:\t%1").arg(MusEGlobal::midiInputTrace?"true":"false")); internalDebugInformation->append(QString("midiOutputTrace:\t%1").arg(MusEGlobal::midiOutputTrace?"true":"false")); internalDebugInformation->append(QString("unityWorkaround:\t%1").arg(MusEGlobal::unityWorkaround?"true":"false")); internalDebugInformation->append(QString("debugMsg:\t\t%1").arg(MusEGlobal::debugMsg?"true":"false")); internalDebugInformation->append(QString("heavyDebugMsg:\t%1").arg(MusEGlobal::heavyDebugMsg?"true":"false")); internalDebugInformation->append(QString("debugSync:\t\t%1").arg(MusEGlobal::debugSync?"true":"false")); internalDebugInformation->append(QString("loadPlugins:\t\t%1").arg(MusEGlobal::loadPlugins?"true":"false")); internalDebugInformation->append(QString("loadMESS:\t\t%1").arg(MusEGlobal::loadMESS?"true":"false")); internalDebugInformation->append(QString("loadVST:\t\t%1").arg(MusEGlobal::loadVST?"true":"false")); internalDebugInformation->append(QString("loadNativeVST:\t%1").arg(MusEGlobal::loadNativeVST?"true":"false")); internalDebugInformation->append(QString("loadDSSI:\t\t%1").arg(MusEGlobal::loadDSSI?"true":"false")); internalDebugInformation->append(QString("usePythonBridge:\t%1").arg(MusEGlobal::usePythonBridge?"true":"false")); internalDebugInformation->append(QString("useLASH:\t\t%1").arg(MusEGlobal::useLASH?"true":"false")); internalDebugInformation->append(QString("loadLV2:\t\t%1").arg(MusEGlobal::loadLV2?"true":"false")); internalDebugInformation->append(QString("useAlsaWithJack:\t%1").arg(MusEGlobal::useAlsaWithJack?"true":"false")); internalDebugInformation->append(QString("noAutoStartJack:\t%1").arg(MusEGlobal::noAutoStartJack?"true":"false")); internalDebugInformation->append(QString("populateMidiPortsOnStart:\t%1").arg(MusEGlobal::populateMidiPortsOnStart?"true":"false")); internalDebugInformation->append(QString("realtimeScheduling:\t%1").arg(MusEGlobal::realTimeScheduling?"true":"false")); internalDebugInformation->append(QString("midiRTPrioOverride:\t%1").arg(MusEGlobal::midiRTPrioOverride?"true":"false")); internalDebugInformation->append(QString("realtimePriority:\t%1").arg(MusEGlobal::realTimePriority)); internalDebugInformation->append(QString("midiSeqRunning:\t%1").arg(MusEGlobal::midiSeqRunning?"true":"false")); internalDebugInformation->append(QString("automation:\t\t%1").arg(MusEGlobal::automation?"true":"false")); QTextCursor cursor = internalDebugInformation->textCursor(); cursor.setPosition(0); internalDebugInformation->setTextCursor(cursor); } } muse-4.0.0/src/muse/components/aboutbox_impl.h000066400000000000000000000022741404105311000214200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/aboutbox_impl.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef ABOUTBOXIMPL_H #define ABOUTBOXIMPL_H #include "ui_aboutbox.h" namespace MusEGui { class AboutBoxImpl : public QDialog, public Ui::AboutBox { Q_OBJECT public: AboutBoxImpl(); }; } #endif // ABOUTBOXIMPL_H muse-4.0.0/src/muse/components/appearance.cpp000066400000000000000000001746061404105311000212170ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: appearance.cpp,v 1.11.2.5 2009/11/14 03:37:48 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include "icons.h" #include "appearance.h" #include "app.h" #include "arranger.h" #include "pcanvas.h" #include "event.h" #include "components/filedialog.h" #include "globals.h" // Specify muse here, two reports of "conf.h" pointing to ALSA conf.h ! #include "muse/conf.h" // Forwards from header: #include #include #include #include #include #include #include "gconfig.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_APPEARANCE(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { int BG_ITEM_HEIGHT = 30; class BgPreviewWidget : public QWidget { QPixmap pixmap; QString imagefile; QTreeWidget* t_widget; int text_h; int text_w; protected: virtual void paintEvent(QPaintEvent* event) { QPainter p(this); int w = t_widget->width() - 65; p.drawTiledPixmap(1,1,w,BG_ITEM_HEIGHT-2, pixmap); const QPalette& pal = palette(); QColor dark = pal.dark().color(); // We can also draw a rectangle behind the text: //p.fillRect(QRect(w/2 - text_w/2,6,text_w + 20,text_h+4), dark); QFontMetrics fm = p.fontMetrics(); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 text_w = fm.horizontalAdvance(imagefile); #else text_w = fm.width(imagefile); #endif text_h = fm.height(); // Do the text shadow first p.save(); p.setPen(dark); p.drawText(w/2 - text_w/2 + 1, 7, text_w + 20, text_h+4, Qt::AlignCenter, imagefile); p.restore(); p.drawText(w/2 - text_w/2,6, text_w + 20, text_h+4, Qt::AlignCenter, imagefile); QWidget::paintEvent(event); } public: BgPreviewWidget(QString imagepath, QTreeWidget *treewidget) { pixmap = QPixmap(imagepath); imagefile = imagepath.right(imagepath.length() - imagepath.lastIndexOf("/") - 1 ); t_widget = treewidget; } }; IdListViewItem::IdListViewItem(int id, QTreeWidgetItem* parent, QString s) : QTreeWidgetItem(parent, QStringList(s)) { _id = id; } IdListViewItem::IdListViewItem(int id, QTreeWidget* parent, QString s) : QTreeWidgetItem(parent, QStringList(s)) { _id = id; } int IdListViewItem::id() const { return _id; } //--------------------------------------------------------- // Appearance //--------------------------------------------------------- Appearance::Appearance(QWidget* parent) : QDialog(parent, Qt::Window) { setupUi(this); itemList->setContextMenuPolicy(Qt::CustomContextMenu); connect(itemList, SIGNAL(customContextMenuRequested(QPoint)), SLOT(colorListCustomContextMenuReq(QPoint))); color = nullptr; _colorDialog = nullptr; config = new MusEGlobal::GlobalConfigValues; backupConfig = new MusEGlobal::GlobalConfigValues; _configChangedTimer = new QTimer(this); _configChangedTimer->setObjectName("configChangedTimer"); _configChangedTimer->setTimerType(Qt::CoarseTimer); _configChangedTimer->setSingleShot(true); _configChangedTimer->setInterval(500); connect(_configChangedTimer, SIGNAL(timeout()), SLOT(configChangeTimeOut())); lastSelectedColorItem = nullptr; lastSelectedBgItem = nullptr; fontName0->setToolTip(tr("Main application font, and default font\n" "for any controls not defined here.")); fontName1->setToolTip(tr("Mixer strips and effects racks.")); fontName2->setToolTip(tr("Arranger part name overlay.")); fontName3->setToolTip(tr("Time scale upper, and time signature.\n" "Controller graph and S/X buttons.")); fontName4->setToolTip(tr("Time scale lower.")); fontName5->setToolTip(tr("Tempo scale, and markers.")); fontName6->setToolTip(tr("Mixer labels. Auto-font-sizing up to chosen font size.")); fontSize6->setToolTip(tr("Maximum mixer label auto-font-sizing font size.")); globalAlphaSlider->setToolTip(tr("Global opacity (opposite of transparency).")); // ARRANGER global_bg = new QTreeWidgetItem(backgroundTree, QStringList(tr("Standard")), 0); global_bg->setFlags(Qt::ItemIsEnabled); user_bg = new QTreeWidgetItem(backgroundTree, QStringList(tr("Custom")), 0); user_bg->setFlags(Qt::ItemIsEnabled); colorwidget->setAutoFillBackground(true); aPalette = new QButtonGroup(aPaletteBox); aPalette->addButton(palette0, 0); aPalette->addButton(palette1, 1); aPalette->addButton(palette2, 2); aPalette->addButton(palette3, 3); aPalette->addButton(palette4, 4); aPalette->addButton(palette5, 5); aPalette->addButton(palette6, 6); aPalette->addButton(palette7, 7); aPalette->addButton(palette8, 8); aPalette->addButton(palette9, 9); aPalette->addButton(palette10, 10); aPalette->addButton(palette11, 11); aPalette->addButton(palette12, 12); aPalette->addButton(palette13, 13); aPalette->addButton(palette14, 14); aPalette->addButton(palette15, 15); aPalette->setExclusive(true); // COLORS IdListViewItem* id; IdListViewItem* aid; itemList->clear(); aid = new IdListViewItem(0, itemList, "Arranger"); id = new IdListViewItem(0, aid, "PartColors"); for(int i = 0; i < NUM_PARTCOLORS; ++i) new IdListViewItem(0x600 + i, id, MusEGlobal::config.partColorNames[i]); id = new IdListViewItem(0, aid, "Track List"); new IdListViewItem(0x411, id, "Background"); new IdListViewItem(0x412, id, "Midi background"); new IdListViewItem(0x413, id, "Drum background"); // Obsolete. There is only 'New' drum tracks now. //new IdListViewItem(0x41e, id, "New drum background"); new IdListViewItem(0x414, id, "Wave background"); new IdListViewItem(0x415, id, "Output background"); new IdListViewItem(0x416, id, "Input background"); new IdListViewItem(0x417, id, "Group background"); new IdListViewItem(0x418, id, "Aux background"); new IdListViewItem(0x419, id, "Synth background"); new IdListViewItem(0x41a, id, "Selected track background"); new IdListViewItem(0x41b, id, "Selected track foreground"); new IdListViewItem(0x430, id, "Last selected track background"); new IdListViewItem(0x42b, id, "Section dividers"); // 0x41c - 0x420 is already used (see above) new IdListViewItem(0x41c, aid, "Part canvas background"); new IdListViewItem(0x42c, aid, "Part canvas raster coarse"); new IdListViewItem(0x457, aid, "Part canvas raster beat"); new IdListViewItem(0x42d, aid, "Part canvas raster fine"); new IdListViewItem(0x453, aid, "Markers"); new IdListViewItem(0x454, aid, "Range markers"); new IdListViewItem(0x455, aid, "Position marker"); new IdListViewItem(0x456, aid, "Current mouse position hint"); new IdListViewItem(0x41f, aid, "Ruler background"); new IdListViewItem(0x420, aid, "Ruler text"); new IdListViewItem(0x424, aid, "Ruler current marker space"); new IdListViewItem(0x425, aid, "Part wave peak"); new IdListViewItem(0x426, aid, "Part wave rms"); new IdListViewItem(0x427, aid, "Part midi event for light part color"); new IdListViewItem(0x428, aid, "Part midi event for dark part color"); new IdListViewItem(0x433, aid, "Dummy part color"); id = new IdListViewItem(0, itemList, "BigTime"); new IdListViewItem(0x100, id, "Background"); new IdListViewItem(0x101, id, "Foreground"); id = new IdListViewItem(0, itemList, "Transport"); new IdListViewItem(0x200, id, "Handle"); aid = new IdListViewItem(0, itemList, "Midi Editor"); id = new IdListViewItem(0, aid, "Piano"); new IdListViewItem(0x450, id, "Current key"); new IdListViewItem(0x451, id, "Pressed key"); new IdListViewItem(0x452, id, "Selected key"); id = new IdListViewItem(0, aid, "Drum list"); new IdListViewItem(0x422, id, "Drum list background"); new IdListViewItem(0x440, id, "Drum list font"); new IdListViewItem(0x441, id, "Drum list selection"); new IdListViewItem(0x442, id, "Drum list selection font"); new IdListViewItem(0x421, aid, "Background"); new IdListViewItem(0x431, aid, "Item"); new IdListViewItem(0x432, aid, "Item selected"); new IdListViewItem(0x42a, aid, "Raster bar"); new IdListViewItem(0x429, aid, "Raster beat"); new IdListViewItem(0x42e, aid, "Raster fine"); new IdListViewItem(0x41d, aid, "Controller graph"); new IdListViewItem(0x41e, aid, "Controller graph selected"); new IdListViewItem(0x423, aid, "Controller graph background"); new IdListViewItem(0x42f, aid, "Divider line"); id = new IdListViewItem(0, itemList, "Wave Editor"); new IdListViewItem(0x300, id, "Background"); new IdListViewItem(0x301, id, "Wave peak color"); new IdListViewItem(0x302, id, "Wave rms color"); new IdListViewItem(0x303, id, "Wave peak color selected"); new IdListViewItem(0x304, id, "Wave rms color selected"); new IdListViewItem(0x305, id, "Wave nonselected part"); aid = new IdListViewItem(0, itemList, "Mixer"); id = new IdListViewItem(0, aid, "Track labels (theme-dependent)"); new IdListViewItem(0x501, id, "Midi"); new IdListViewItem(0x502, id, "Drum"); new IdListViewItem(0x504, id, "Wave"); new IdListViewItem(0x505, id, "Audio output"); new IdListViewItem(0x506, id, "Audio input"); new IdListViewItem(0x507, id, "Group"); new IdListViewItem(0x508, id, "Aux"); new IdListViewItem(0x509, id, "Synth"); id = new IdListViewItem(0, aid, "Effect rack"); new IdListViewItem(0x530, id, "Slot background"); new IdListViewItem(0x531, id, "Slot active"); new IdListViewItem(0x532, id, "Font"); new IdListViewItem(0x533, id, "Font active"); new IdListViewItem(0x535, id, "Font mouse hover"); new IdListViewItem(0x534, id, "Border"); id = new IdListViewItem(0, aid, "Midi instrument"); new IdListViewItem(0x550, id, "Background"); new IdListViewItem(0x551, id, "Background active"); new IdListViewItem(0x552, id, "Font"); new IdListViewItem(0x553, id, "Font active"); new IdListViewItem(0x554, id, "Border"); new IdListViewItem(0x555, id, "Patch LCD font"); id = new IdListViewItem(0, aid, "Sliders and knobs"); new IdListViewItem(0x50b, id, "Background"); new IdListViewItem(0x50a, id, "Slider bar (theme-dependent)"); new IdListViewItem(0x50c, id, "Pan slider"); new IdListViewItem(0x50d, id, "Gain slider"); new IdListViewItem(0x50e, id, "Aux slider"); new IdListViewItem(0x511, id, "Audio controller"); new IdListViewItem(0x512, id, "Audio property"); new IdListViewItem(0x513, id, "Midi controller"); new IdListViewItem(0x514, id, "Midi property"); new IdListViewItem(0x515, id, "Knob font (theme-dependent"); // new IdListViewItem(0x500, aid, "Mixer background"); new IdListViewItem(0x50f, aid, "Audio volume"); new IdListViewItem(0x510, aid, "Midi volume"); new IdListViewItem(0x518, aid, "Audio volume handle"); new IdListViewItem(0x519, aid, "Midi volume handle"); new IdListViewItem(0x516, aid, "Audio meter primary"); new IdListViewItem(0x517, aid, "Midi meter primary"); new IdListViewItem(0x520, aid, "Meter background (theme-dependent)"); colorNameLineEdit->setEnabled(false); connect(loadColorsButton, SIGNAL(clicked(bool)), SLOT(loadColors())); connect(saveColorsButton, SIGNAL(clicked(bool)), SLOT(saveColors())); connect(pickColorButton, SIGNAL(clicked(bool)), SLOT(chooseColorClicked())); connect(colorwidget, SIGNAL(clicked()), SLOT(chooseColorClicked())); connect(colorNameLineEdit, SIGNAL(editingFinished()), SLOT(colorNameEditFinished())); connect(itemList, SIGNAL(itemSelectionChanged()), SLOT(colorItemSelectionChanged())); connect(aPalette, SIGNAL(buttonClicked(int)), SLOT(paletteClicked(int))); connect(globalAlphaSlider, SIGNAL(valueChanged(int)), SLOT(asliderChanged(int))); connect(rslider, SIGNAL(valueChanged(int)), SLOT(rsliderChanged(int))); connect(gslider, SIGNAL(valueChanged(int)), SLOT(gsliderChanged(int))); connect(bslider, SIGNAL(valueChanged(int)), SLOT(bsliderChanged(int))); connect(hslider, SIGNAL(valueChanged(int)), SLOT(hsliderChanged(int))); connect(sslider, SIGNAL(valueChanged(int)), SLOT(ssliderChanged(int))); connect(vslider, SIGNAL(valueChanged(int)), SLOT(vsliderChanged(int))); connect(globalAlphaVal, SIGNAL(valueChanged(int)), SLOT(aValChanged(int))); connect(rval, SIGNAL(valueChanged(int)), SLOT(rsliderChanged(int))); connect(gval, SIGNAL(valueChanged(int)), SLOT(gsliderChanged(int))); connect(bval, SIGNAL(valueChanged(int)), SLOT(bsliderChanged(int))); connect(hval, SIGNAL(valueChanged(int)), SLOT(hsliderChanged(int))); connect(sval, SIGNAL(valueChanged(int)), SLOT(ssliderChanged(int))); connect(vval, SIGNAL(valueChanged(int)), SLOT(vsliderChanged(int))); connect(addToPalette, SIGNAL(clicked()), SLOT(addToPaletteClicked())); //--------------------------------------------------- // THEMES //--------------------------------------------------- QDir themeDir(MusEGlobal::museGlobalShare + QString("/themes")); QStringList fileTypes; fileTypes.append("*.qss"); QFileInfoList list = themeDir.entryInfoList(fileTypes); for (const auto& item : list) themeComboBox->addItem(item.baseName()); themeComboBox->setCurrentText(MusEGlobal::config.theme); //--------------------------------------------------- // Fonts //--------------------------------------------------- fontBrowse1->setIcon(*fileopenSVGIcon); fontBrowse2->setIcon(*fileopenSVGIcon); fontBrowse3->setIcon(*fileopenSVGIcon); fontBrowse4->setIcon(*fileopenSVGIcon); fontBrowse5->setIcon(*fileopenSVGIcon); fontBrowse6->setIcon(*fileopenSVGIcon); connect(fontBrowse1, SIGNAL(clicked()), SLOT(browseFont1())); connect(fontBrowse2, SIGNAL(clicked()), SLOT(browseFont2())); connect(fontBrowse3, SIGNAL(clicked()), SLOT(browseFont3())); connect(fontBrowse4, SIGNAL(clicked()), SLOT(browseFont4())); connect(fontBrowse5, SIGNAL(clicked()), SLOT(browseFont5())); connect(fontBrowse6, SIGNAL(clicked()), SLOT(browseFont6())); connect(applyButton, SIGNAL(clicked()), SLOT(applyClicked())); connect(okButton, SIGNAL(clicked()), SLOT(okClicked())); connect(cancelButton, SIGNAL(clicked()), SLOT(cancel())); connect(addBgButton, SIGNAL(clicked()), SLOT(addBackground())); connect(removeBgButton, SIGNAL(clicked()), SLOT(removeBackground())); connect(clearBgButton, SIGNAL(clicked()), SLOT(clearBackground())); connect(partShowevents, SIGNAL(toggled(bool)), eventButtonGroup, SLOT(setEnabled(bool))); //updateColor(); } Appearance::~Appearance() { delete config; delete backupConfig; } QColor* Appearance::globalConfigColorFromId(int id) const { if(id == 0) return nullptr; if(id >= 0x600 && id < (0x600 + NUM_PARTCOLORS)) return &MusEGlobal::config.partColors[id & 0xff]; else { switch(id) { case 0x100: return &MusEGlobal::config.bigTimeBackgroundColor; case 0x101: return &MusEGlobal::config.bigTimeForegroundColor; case 0x200: return &MusEGlobal::config.transportHandleColor; case 0x300: return &MusEGlobal::config.waveEditBackgroundColor; case 0x301: return &MusEGlobal::config.wavePeakColor; case 0x302: return &MusEGlobal::config.waveRmsColor; case 0x303: return &MusEGlobal::config.wavePeakColorSelected; case 0x304: return &MusEGlobal::config.waveRmsColorSelected; case 0x305: return &MusEGlobal::config.waveNonselectedPart; case 0x411: return &MusEGlobal::config.trackBg; case 0x412: return &MusEGlobal::config.midiTrackBg; // Obsolete. There is only 'New' drum tracks now. //case 0x413: return &MusEGlobal::config.drumTrackBg; //case 0x41e: return &MusEGlobal::config.newDrumTrackBg; case 0x413: return &MusEGlobal::config.newDrumTrackBg; case 0x414: return &MusEGlobal::config.waveTrackBg; case 0x415: return &MusEGlobal::config.outputTrackBg; case 0x416: return &MusEGlobal::config.inputTrackBg; case 0x417: return &MusEGlobal::config.groupTrackBg; case 0x418: return &MusEGlobal::config.auxTrackBg; case 0x419: return &MusEGlobal::config.synthTrackBg; case 0x41a: return &MusEGlobal::config.selectTrackBg; case 0x41b: return &MusEGlobal::config.selectTrackFg; case 0x430: return &MusEGlobal::config.selectTrackCurBg; case 0x41c: return &MusEGlobal::config.partCanvasBg; case 0x41d: return &MusEGlobal::config.ctrlGraphFg; case 0x41e: return &MusEGlobal::config.ctrlGraphSel; // 0x41e is already used (between 413 and 414) case 0x41f: return &MusEGlobal::config.rulerBg; case 0x420: return &MusEGlobal::config.rulerFg; case 0x421: return &MusEGlobal::config.midiCanvasBg; case 0x422: return &MusEGlobal::config.drumListBg; case 0x423: return &MusEGlobal::config.midiControllerViewBg; case 0x424: return &MusEGlobal::config.rulerCurrent; case 0x425: return &MusEGlobal::config.partWaveColorPeak; case 0x426: return &MusEGlobal::config.partWaveColorRms; case 0x427: return &MusEGlobal::config.partMidiDarkEventColor; case 0x428: return &MusEGlobal::config.partMidiLightEventColor; case 0x429: return &MusEGlobal::config.midiCanvasBeatColor; case 0x42a: return &MusEGlobal::config.midiCanvasBarColor; case 0x42b: return &MusEGlobal::config.trackSectionDividerColor; case 0x42c: return &MusEGlobal::config.partCanvasCoarseRasterColor; case 0x42d: return &MusEGlobal::config.partCanvasFineRasterColor; case 0x42e: return &MusEGlobal::config.midiCanvasFineColor; case 0x42f: return &MusEGlobal::config.midiDividerColor; case 0x431: return &MusEGlobal::config.midiItemColor; case 0x432: return &MusEGlobal::config.midiItemSelectedColor; case 0x433: return &MusEGlobal::config.dummyPartColor; case 0x440: return &MusEGlobal::config.drumListFont; case 0x441: return &MusEGlobal::config.drumListSel; case 0x442: return &MusEGlobal::config.drumListSelFont; case 0x450: return &MusEGlobal::config.pianoCurrentKey; case 0x451: return &MusEGlobal::config.pianoPressedKey; case 0x452: return &MusEGlobal::config.pianoSelectedKey; case 0x453: return &MusEGlobal::config.markerColor; case 0x454: return &MusEGlobal::config.rangeMarkerColor; case 0x455: return &MusEGlobal::config.positionMarkerColor; case 0x456: return &MusEGlobal::config.currentPositionColor; case 0x457: return &MusEGlobal::config.partCanvasBeatRasterColor; // case 0x500: return &MusEGlobal::config.mixerBg; case 0x501: return &MusEGlobal::config.midiTrackLabelBg; // Obsolete. There is only 'New' drum tracks now. //case 0x502: return &MusEGlobal::config.drumTrackLabelBg; //case 0x503: return &MusEGlobal::config.newDrumTrackLabelBg; case 0x502: return &MusEGlobal::config.newDrumTrackLabelBg; case 0x504: return &MusEGlobal::config.waveTrackLabelBg; case 0x505: return &MusEGlobal::config.outputTrackLabelBg; case 0x506: return &MusEGlobal::config.inputTrackLabelBg; case 0x507: return &MusEGlobal::config.groupTrackLabelBg; case 0x508: return &MusEGlobal::config.auxTrackLabelBg; case 0x509: return &MusEGlobal::config.synthTrackLabelBg; case 0x50a: return &MusEGlobal::config.sliderBarColor; case 0x50b: return &MusEGlobal::config.sliderBackgroundColor; case 0x50c: return &MusEGlobal::config.panSliderColor; case 0x50d: return &MusEGlobal::config.gainSliderColor; case 0x50e: return &MusEGlobal::config.auxSliderColor; case 0x50f: return &MusEGlobal::config.audioVolumeSliderColor; case 0x510: return &MusEGlobal::config.midiVolumeSliderColor; case 0x511: return &MusEGlobal::config.audioControllerSliderColor; case 0x512: return &MusEGlobal::config.audioPropertySliderColor; case 0x513: return &MusEGlobal::config.midiControllerSliderColor; case 0x514: return &MusEGlobal::config.midiPropertySliderColor; case 0x515: return &MusEGlobal::config.knobFontColor; case 0x516: return &MusEGlobal::config.audioMeterPrimaryColor; case 0x517: return &MusEGlobal::config.midiMeterPrimaryColor; case 0x518: return &MusEGlobal::config.audioVolumeHandleColor; case 0x519: return &MusEGlobal::config.midiVolumeHandleColor; case 0x520: return &MusEGlobal::config.meterBackgroundColor; case 0x530: return &MusEGlobal::config.rackItemBackgroundColor; case 0x531: return &MusEGlobal::config.rackItemBgActiveColor; case 0x532: return &MusEGlobal::config.rackItemFontColor; case 0x533: return &MusEGlobal::config.rackItemFontActiveColor; case 0x534: return &MusEGlobal::config.rackItemBorderColor; case 0x535: return &MusEGlobal::config.rackItemFontColorHover; case 0x550: return &MusEGlobal::config.midiInstrumentBackgroundColor; case 0x551: return &MusEGlobal::config.midiInstrumentBgActiveColor; case 0x552: return &MusEGlobal::config.midiInstrumentFontColor; case 0x553: return &MusEGlobal::config.midiInstrumentFontActiveColor; case 0x554: return &MusEGlobal::config.midiInstrumentBorderColor; case 0x555: return &MusEGlobal::config.midiPatchReadoutColor; default: return nullptr; } } return nullptr; } long int Appearance::configOffsetFromColorId(int id) const { QColor* c = globalConfigColorFromId(id); if(!c) return -1; // Memory pointer HACK. return ((const char*)c) - ((const char*)&MusEGlobal::config); } QColor* Appearance::workingConfigColorFromId(int id) const { long int itemOffset = configOffsetFromColorId(id); if(itemOffset == -1) return nullptr; return (QColor*)(((const char*)config) + itemOffset); } QColor* Appearance::backupConfigColorFromId(int id) const { long int itemOffset = configOffsetFromColorId(id); if(itemOffset == -1) return nullptr; return (QColor*)(((const char*)backupConfig) + itemOffset); } //--------------------------------------------------------- // setConfigurationColors //--------------------------------------------------------- void Appearance::setConfigurationColors() { palette0->setStyleSheet(QString("background-color: ") + config->palette[0].name()); palette1->setStyleSheet(QString("background-color: ") + config->palette[1].name()); palette2->setStyleSheet(QString("background-color: ") + config->palette[2].name()); palette3->setStyleSheet(QString("background-color: ") + config->palette[3].name()); palette4->setStyleSheet(QString("background-color: ") + config->palette[4].name()); palette5->setStyleSheet(QString("background-color: ") + config->palette[5].name()); palette6->setStyleSheet(QString("background-color: ") + config->palette[6].name()); palette7->setStyleSheet(QString("background-color: ") + config->palette[7].name()); palette8->setStyleSheet(QString("background-color: ") + config->palette[8].name()); palette9->setStyleSheet(QString("background-color: ") + config->palette[9].name()); palette10->setStyleSheet(QString("background-color: ") + config->palette[10].name()); palette11->setStyleSheet(QString("background-color: ") + config->palette[11].name()); palette12->setStyleSheet(QString("background-color: ") + config->palette[12].name()); palette13->setStyleSheet(QString("background-color: ") + config->palette[13].name()); palette14->setStyleSheet(QString("background-color: ") + config->palette[14].name()); palette15->setStyleSheet(QString("background-color: ") + config->palette[15].name()); } //--------------------------------------------------------- // resetValues //--------------------------------------------------------- void Appearance::resetValues() { *config = MusEGlobal::config; // init with global config values *backupConfig = MusEGlobal::config; // init with global config values updateFonts(); cbAutoAdjustFontSize->setChecked(config->autoAdjustFontSize); setConfigurationColors(); global_bg->takeChildren(); user_bg->takeChildren(); if (config->waveDrawing == MusEGlobal::WaveRmsPeak) radioButtonDrawRmsPeak->setChecked(true); else radioButtonDrawOutline->setChecked(true); QDir bgdir = MusEGlobal::museGlobalShare + "/wallpapers/"; QStringList filters; filters << "*.jpg" << "*.jpeg" << "*.png" << "*.gif"; bgdir.setNameFilters(filters); backgroundTree->model()->setData(backgroundTree->model()->index(0,0), QVariant(QSize(200,BG_ITEM_HEIGHT)), Qt::SizeHintRole); QStringList bglist = bgdir.entryList(QDir::Files, QDir::Name); foreach (const QString &bgfile, bglist) { QTreeWidgetItem* item = new QTreeWidgetItem(global_bg, 0); item->setData(0, Qt::UserRole, QVariant(MusEGlobal::museGlobalShare + "/wallpapers/" + bgfile)); BgPreviewWidget* bgw = new BgPreviewWidget(MusEGlobal::museGlobalShare + "/wallpapers/" + bgfile, backgroundTree); backgroundTree->setItemWidget(item, 0, bgw); if (config->canvasBgPixmap == MusEGlobal::museGlobalShare + "/wallpapers/" + bgfile) backgroundTree->setCurrentItem(item); } foreach (const QString &bgfile, config->canvasCustomBgList) { QTreeWidgetItem* item = new QTreeWidgetItem(user_bg, 0); BgPreviewWidget* bgw = new BgPreviewWidget(bgfile, backgroundTree); backgroundTree->setItemWidget(item, 0, bgw); item->setData(0, Qt::UserRole, QVariant(bgfile)); if (config->canvasBgPixmap == bgfile) backgroundTree->setCurrentItem(item); } removeBgButton->setEnabled(false); backgroundTree->expandAll(); connect(backgroundTree, SIGNAL(itemClicked(QTreeWidgetItem*, int )), SLOT(bgSelectionChanged(QTreeWidgetItem*))); partShownames->setChecked(config->canvasShowPartType & 1); partShowevents->setChecked(config->canvasShowPartType & 2); partShowCakes->setChecked(!(config->canvasShowPartType & 2)); partCakeStretch->setChecked(config->canvasShowPartType & 4); eventNoteon->setChecked(config->canvasShowPartEvent & (1 << 0)); eventPolypressure->setChecked(config->canvasShowPartEvent & (1 << 1)); eventController->setChecked(config->canvasShowPartEvent & (1 << 2)); eventProgramchange->setChecked(config->canvasShowPartEvent & (1 << 3)); eventAftertouch->setChecked(config->canvasShowPartEvent & (1 << 4)); eventPitchbend->setChecked(config->canvasShowPartEvent & (1 << 5)); eventSpecial->setChecked(config->canvasShowPartEvent & (1 << 6)); eventButtonGroup->setEnabled(config->canvasShowPartType & 2); arrGrid->setChecked(config->canvasShowGrid); gridAlwaysShowH->setChecked(config->canvasShowGridHorizontalAlways); gridAlwaysShowBeats->setChecked(config->canvasShowGridBeatsAlways); cbUseTrackColorForParts->setChecked(config->useTrackColorForParts); // qtStyleComboBox->clear(); //// QString cs = MusEGlobal::muse->style()->objectName(); //// cs = cs.toLower(); //// qtStyleComboBox->insertItem(0,tr("Keep Qt system style")); // qtStyleComboBox->insertItems(0, QStyleFactory::keys()); // if (QStyleFactory::keys().indexOf(config->style) == -1) // qtStyleComboBox->setCurrentText("Fusion"); // if none is found use the default // else // qtStyleComboBox->setCurrentText(config->style); // if (QStyleFactory::keys().indexOf(config->style) == -1) // qtStyleComboBox->setCurrentIndex(0); // if none is found use the default // else { // for (int i = 0; i < qtStyleComboBox->count(); ++i) { // if (qtStyleComboBox->itemText(i).toLower() == cs) { // qtStyleComboBox->setCurrentIndex(i); // } // } // } globalAlphaSlider->blockSignals(true); globalAlphaVal->blockSignals(true); globalAlphaSlider->setValue(config->globalAlphaBlend); globalAlphaVal->setValue(config->globalAlphaBlend); globalAlphaSlider->blockSignals(false); globalAlphaVal->blockSignals(false); maxAliasedPointSize->blockSignals(true); maxAliasedPointSize->setValue(config->maxAliasedPointSize); maxAliasedPointSize->blockSignals(false); iconSizeSpin->blockSignals(true); iconSizeSpin->setValue(config->iconSize); iconSizeSpin->blockSignals(false); cursorSizeSpin->blockSignals(true); cursorSizeSpin->setValue(config->cursorSize); cursorSizeSpin->blockSignals(false); trackGradientSpin->blockSignals(true); trackGradientSpin->setValue(config->trackGradientStrength); trackGradientSpin->blockSignals(false); partGradientSpin->blockSignals(true); partGradientSpin->setValue(config->partGradientStrength); partGradientSpin->blockSignals(false); cascadeStylesheetsCheckBox->setChecked(config->cascadeStylesheets); cbShowIconsInMenus->setChecked(config->showIconsInMenus); cbUseNativeStandardDialogs->setChecked(config->useNativeStandardDialogs); // Grab all the colours. updateColorItems(); updateColor(); } QString &Appearance::getSetDefaultStyle(const QString *newStyle) { static QString defaultStyle = "Fusion"; if (newStyle) defaultStyle = *newStyle; return defaultStyle; } //--------------------------------------------------------- // bgSelectionChanged //--------------------------------------------------------- void Appearance::bgSelectionChanged(QTreeWidgetItem* item) { if (item->text(0).length() && lastSelectedBgItem) { backgroundTree->setCurrentItem(lastSelectedBgItem); item = lastSelectedBgItem; } removeBgButton->setEnabled(false); QTreeWidgetItem* parent = item->parent(); if (parent) if (parent->text(0) == user_bg->text(0)) removeBgButton->setEnabled(true); lastSelectedBgItem = item; MusEGlobal::muse->arranger()->getCanvas()->setBg(QPixmap(item->data(0, Qt::UserRole).toString())); } //--------------------------------------------------------- // updateFonts //--------------------------------------------------------- void Appearance::updateFonts() { fontSize0->setValue(MusEGlobal::config.fonts[0].pointSize()); fontName0->setText(MusEGlobal::config.fonts[0].family()); italic0->setChecked(MusEGlobal::config.fonts[0].italic()); bold0->setChecked(MusEGlobal::config.fonts[0].bold()); fontSize1->setValue(config->fonts[1].pointSize()); fontName1->setText(config->fonts[1].family()); italic1->setChecked(config->fonts[1].italic()); bold1->setChecked(config->fonts[1].bold()); fontSize2->setValue(config->fonts[2].pointSize()); fontName2->setText(config->fonts[2].family()); italic2->setChecked(config->fonts[2].italic()); bold2->setChecked(config->fonts[2].bold()); fontSize3->setValue(config->fonts[3].pointSize()); fontName3->setText(config->fonts[3].family()); italic3->setChecked(config->fonts[3].italic()); bold3->setChecked(config->fonts[3].bold()); fontSize4->setValue(config->fonts[4].pointSize()); fontName4->setText(config->fonts[4].family()); italic4->setChecked(config->fonts[4].italic()); bold4->setChecked(config->fonts[4].bold()); fontSize5->setValue(config->fonts[5].pointSize()); fontName5->setText(config->fonts[5].family()); italic5->setChecked(config->fonts[5].italic()); bold5->setChecked(config->fonts[5].bold()); fontSize6->setValue(config->fonts[6].pointSize()); fontName6->setText(config->fonts[6].family()); italic6->setChecked(config->fonts[6].italic()); bold6->setChecked(config->fonts[6].bold()); } //--------------------------------------------------------- // changeTheme // //--------------------------------------------------------- bool Appearance::changeTheme() { QString currentTheme = themeComboBox->currentText(); if (config->theme == currentTheme) return false; printf("Changing to theme %s\n", qPrintable(currentTheme) ); MusEGlobal::config.theme = currentTheme; if (!isColorsDirty()) saveCurrentThemeColors(); QString configColorPath = MusEGlobal::configPath + "/themes/" + currentTheme + ".cfc"; if (!QFile::exists(configColorPath)) { configColorPath = MusEGlobal::museGlobalShare + "/themes/" + currentTheme + ".cfc"; } // We want the simple version, don't set the style or stylesheet yet. MusECore::readConfiguration(qPrintable(configColorPath)); // MusEGlobal::muse->changeConfig(true); backgroundTree->reset(); hide(); return true; } void Appearance::saveCurrentThemeColors() { QDir dir(MusEGlobal::configPath + "/themes/"); if (!dir.exists()) dir.mkpath(MusEGlobal::configPath + "/themes/"); QString lastColorPath = MusEGlobal::configPath + "/themes/" + config->theme + ".cfc"; FILE* f = fopen(qPrintable(lastColorPath), "w"); if (!f) { fprintf(stderr, "Saving configuration colors to <%s> failed: %s\n", qPrintable(lastColorPath), strerror(errno)); } else { MusECore::Xml xml(f); xml.header(); xml.nput(0, "\n", xml.latestMajorVersion(), xml.latestMinorVersion()); xml.tag(1, "configuration"); MusECore::writeConfigurationColors(2, xml, false); // Don't save part colour names. xml.etag(1, "configuration"); xml.tag(0, "/muse"); fclose(f); } } //--------------------------------------------------------- // apply //--------------------------------------------------------- bool Appearance::apply() { bool restart_required = false; if (isColorsDirty()) saveCurrentThemeColors(); if (changeTheme()) { *config = MusEGlobal::config; restart_required = true; } int showPartEvent = 0; int showPartType = 0; if (partShownames->isChecked()) showPartType |= 1; if (partShowevents->isChecked()) showPartType |= 2; if (partCakeStretch->isChecked()) showPartType |= 4; config->canvasShowPartType = showPartType; if (eventNoteon->isChecked()) showPartEvent |= (1 << 0); if (eventPolypressure->isChecked()) showPartEvent |= (1 << 1); if (eventController->isChecked()) showPartEvent |= (1 << 2); if (eventProgramchange->isChecked()) showPartEvent |= (1 << 3); if (eventAftertouch->isChecked()) showPartEvent |= (1 << 4); if (eventPitchbend->isChecked()) showPartEvent |= (1 << 5); if (eventSpecial->isChecked()) showPartEvent |= (1 << 6); config->canvasShowPartEvent = showPartEvent; QTreeWidgetItem* cbgitem = backgroundTree->currentItem(); if (cbgitem) config->canvasBgPixmap = cbgitem->data(0, Qt::UserRole).toString(); else config->canvasBgPixmap = QString(); config->canvasCustomBgList = QStringList(); for (int i = 0; i < user_bg->childCount(); ++i) config->canvasCustomBgList << user_bg->child(i)->data(0, Qt::UserRole).toString(); // config->fonts[0].setFamily(fontName0->text()); // config->fonts[0].setPointSize(fontSize0->value()); // config->fonts[0].setItalic(italic0->isChecked()); // config->fonts[0].setBold(bold0->isChecked()); config->fonts[1].setFamily(fontName1->text()); config->fonts[1].setPointSize(fontSize1->value()); config->fonts[1].setItalic(italic1->isChecked()); config->fonts[1].setBold(bold1->isChecked()); config->fonts[2].setFamily(fontName2->text()); config->fonts[2].setPointSize(fontSize2->value()); config->fonts[2].setItalic(italic2->isChecked()); config->fonts[2].setBold(bold2->isChecked()); config->fonts[3].setFamily(fontName3->text()); config->fonts[3].setPointSize(fontSize3->value()); config->fonts[3].setItalic(italic3->isChecked()); config->fonts[3].setBold(bold3->isChecked()); config->fonts[4].setFamily(fontName4->text()); config->fonts[4].setPointSize(fontSize4->value()); config->fonts[4].setItalic(italic4->isChecked()); config->fonts[4].setBold(bold4->isChecked()); config->fonts[5].setFamily(fontName5->text()); config->fonts[5].setPointSize(fontSize5->value()); config->fonts[5].setItalic(italic5->isChecked()); config->fonts[5].setBold(bold5->isChecked()); config->fonts[6].setFamily(fontName6->text()); config->fonts[6].setPointSize(fontSize6->value()); config->fonts[6].setItalic(italic6->isChecked()); config->fonts[6].setBold(bold6->isChecked()); // if(config->style != (qtStyleComboBox->currentIndex() == 0 ? QString() : qtStyleComboBox->currentText())) // { // restart_required = true; // config->style = qtStyleComboBox->currentIndex() == 0 ? QString() : qtStyleComboBox->currentText(); // } config->canvasShowGrid = arrGrid->isChecked(); config->canvasShowGridHorizontalAlways = gridAlwaysShowH->isChecked(); config->canvasShowGridBeatsAlways = gridAlwaysShowBeats->isChecked(); config->useTrackColorForParts = cbUseTrackColorForParts->isChecked(); config->globalAlphaBlend = globalAlphaVal->value(); config->maxAliasedPointSize = maxAliasedPointSize->value(); config->autoAdjustFontSize = cbAutoAdjustFontSize->isChecked(); if (config->iconSize != iconSizeSpin->value()) { restart_required = true; config->iconSize = iconSizeSpin->value(); } if (config->cursorSize != cursorSizeSpin->value()) { restart_required = true; config->cursorSize = cursorSizeSpin->value(); } if (config->trackGradientStrength != trackGradientSpin->value()) { config->trackGradientStrength = trackGradientSpin->value(); } if (config->partGradientStrength != partGradientSpin->value()) { config->partGradientStrength = partGradientSpin->value(); } if (config->cascadeStylesheets != cascadeStylesheetsCheckBox->isChecked()) { restart_required = true; config->cascadeStylesheets = cascadeStylesheetsCheckBox->isChecked(); } if (config->showIconsInMenus != cbShowIconsInMenus->isChecked()) { restart_required = true; config->showIconsInMenus = cbShowIconsInMenus->isChecked(); } if (config->useNativeStandardDialogs != cbUseNativeStandardDialogs->isChecked()) { restart_required = true; config->useNativeStandardDialogs = cbUseNativeStandardDialogs->isChecked(); } if (radioButtonDrawOutline->isChecked()) config->waveDrawing = MusEGlobal::WaveOutLine; else config->waveDrawing = MusEGlobal::WaveRmsPeak; MusEGlobal::config = *config; *backupConfig = *config; updateColorItems(); // We want the simple version, don't set the style or stylesheet yet. MusEGlobal::muse->changeConfig(true); raise(); return restart_required; } //--------------------------------------------------------- // checkClose //--------------------------------------------------------- bool Appearance::checkClose() { if(QMessageBox::warning(MusEGlobal::muse, QString("Muse"), tr("Style was changed.\nThe program must be restarted for changes to take place.\nRestart now?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) { MusEGlobal::muse->setRestartingApp(true); if(MusEGlobal::muse->close()) return true; } // We want the non-simple version, set the style and stylesheet, and don't save - it's already been done. // And force the style. MusEGlobal::muse->changeConfig(false); // MusEGlobal::muse->updateThemeAndStyle(true); MusEGlobal::muse->setRestartingApp(false); // Cancel any restart. Also cleared in muse->closeEvent(). return false; } //--------------------------------------------------------- // applyClicked //--------------------------------------------------------- void Appearance::applyClicked() { if(apply()) // If required, ask user to restart the application for proper results. checkClose(); } //--------------------------------------------------------- // colorNameEditFinished //--------------------------------------------------------- void Appearance::colorNameEditFinished() { if(!lastSelectedColorItem) return; IdListViewItem* item = (IdListViewItem*)lastSelectedColorItem; int id = item->id(); if(id == 0) return; QString etxt = colorNameLineEdit->text(); QString txt = item->text(0); // We only support part color names, for now. if(id >= 0x600 && id < (0x600 + NUM_PARTCOLORS)) config->partColorNames[id & 0xff] = etxt; if(etxt != txt) item->setText(0, etxt); } void Appearance::loadColors() { if (!MusEGlobal::muse->loadConfigurationColors(this)) { DEBUG_APPEARANCE(stderr, "Appearance::loadColors failed\n"); return; } // resetValues(); *config = MusEGlobal::config; updateColorItems(); setConfigurationColors(); updateColor(); } void Appearance::saveColors() { MusEGlobal::muse->saveConfigurationColors(this); } bool Appearance::isColorDirty(IdListViewItem* item) const { if(!item) return false; int id = item->id(); if(id == 0) return false; QColor* p_gc = globalConfigColorFromId(id); if(!p_gc) return false; QColor* p_bkc = backupConfigColorFromId(id); if(!p_bkc) return false; const QColor& gc = *p_gc; const QColor& bkc = *p_bkc; return gc != bkc; } bool Appearance::isColorsDirty() const { QTreeWidgetItemIterator it(itemList); while(*it) { if(isColorDirty((IdListViewItem*)*it)) return true; ++it; } return false; } void Appearance::setColorItemDirty() { IdListViewItem* item = (IdListViewItem*)itemList->selectedItems()[0]; if(!item) return; setColorItemDirty(item); } void Appearance::setColorItemDirty(IdListViewItem* item) { if(!item) return; int id = item->id(); //if(id == 0 || !color) if(id == 0) return; QColor* p_gc = globalConfigColorFromId(id); if(!p_gc) return; QColor* p_bkc = backupConfigColorFromId(id); if(!p_bkc) return; const QColor& gc = *p_gc; const QColor& bkc = *p_bkc; QFont fnt = item->font(0); //fnt.setBold(bkc != gc); fnt.setWeight(bkc != gc ? QFont::Black : QFont::Normal); fnt.setItalic(bkc != gc); item->setFont(0, fnt); item->setData(0, Qt::DecorationRole, gc); } void Appearance::updateColorItems() { QTreeWidgetItemIterator it(itemList); while(*it) { setColorItemDirty((IdListViewItem*)*it); ++it; } } void Appearance::colorListCustomContextMenuReq(const QPoint& p) { DEBUG_APPEARANCE(stderr, "Appearance::colorListCustomContextMenuReq\n"); IdListViewItem* item = static_cast(itemList->itemAt(p)); bool itemDirty = item && isColorDirty(item); QMenu* pup = new QMenu(this); QAction* act = pup->addAction(tr("Revert changes")); act->setData(0x100); act->setEnabled(itemDirty); act = pup->addAction(tr("Revert all...")); act->setData(0x101); act->setEnabled(isColorsDirty()); act = pup->exec(itemList->mapToGlobal(p)); if(!act) { delete pup; return; } const int res = act->data().toInt(); delete pup; switch(res) { case 0x100: { if(item && isColorDirty(item)) { resetColorItem(item); updateColor(); if(color && _colorDialog) { _colorDialog->blockSignals(true); _colorDialog->setCurrentColor(*color); _colorDialog->blockSignals(false); } // Notify the rest of the app, without the heavy stuff. MusEGlobal::muse->changeConfig(false); // No write, and simple mode. } } break; case 0x101: { if(QMessageBox::question(this, QString("Muse"), tr("Do you really want to reset all colors?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) return; resetAllColorItems(); updateColor(); if(color && _colorDialog) { _colorDialog->blockSignals(true); _colorDialog->setCurrentColor(*color); _colorDialog->blockSignals(false); } //changeGlobalColor(); // Notify the rest of the app, without the heavy stuff. MusEGlobal::muse->changeConfig(false); // No write, and simple mode. } break; } } void Appearance::resetColorItem(IdListViewItem* item) { if(!item) return; int id = item->id(); if(id == 0) return; QColor* p_bkc = backupConfigColorFromId(id); if(!p_bkc) return; QColor* p_gc = globalConfigColorFromId(id); if(!p_gc) return; QColor* p_wkc = workingConfigColorFromId(id); if(!p_wkc) return; const QColor& bkc = *p_bkc; QColor& gc = *p_gc; QColor& wkc = *p_wkc; gc = bkc; wkc = bkc; QFont fnt = item->font(0); fnt.setWeight(QFont::Normal); fnt.setItalic(false); item->setFont(0, fnt); item->setData(0, Qt::DecorationRole, gc); } void Appearance::resetAllColorItems() { QTreeWidgetItemIterator it(itemList); while(*it) { resetColorItem((IdListViewItem*)*it); ++it; } } void Appearance::chooseColorClicked() { if(!color) return; if(!_colorDialog) { DEBUG_APPEARANCE(stderr, "Appearance::chooseColorClicked creating new dialog\n"); _colorDialog = new QColorDialog(this); _colorDialog->setOption(QColorDialog::NoButtons, true); connect(_colorDialog, SIGNAL(currentColorChanged(QColor)), SLOT(colorDialogCurrentChanged(QColor))); connect(_colorDialog, SIGNAL(finished(int)), SLOT(colorDialogFinished(int))); } _colorDialog->setCurrentColor(*color); IdListViewItem* item = (IdListViewItem*)itemList->selectedItems()[0]; if(item) setColorDialogWindowText(item->text(0)); else setColorDialogWindowText(); QRect r = this->geometry(); _colorDialog->move(r.x() + 250, r.y() + 170); _colorDialog->show(); _colorDialog->raise(); } void Appearance::changeGlobalColor() { if(!color) return; // Memory pointer HACK to get to the global config color. unsigned long int itemOffset = ((const char*)color) - ((const char*)config); const char* configOffset = ((const char*)&MusEGlobal::config) + itemOffset; // Change the actual global config item, 'live'. QColor& ccol = *((QColor*)configOffset); if(ccol != *color) { ccol = *color; // Notify the rest of the app, without the heavy stuff. MusEGlobal::muse->changeConfig(false); // No write, and simple mode. } setColorItemDirty(); } void Appearance::changeColor(const QColor& c) { // if(!color) // return; if(color && *color != c) { *color = c; //updateColor(); } _configChangedTimer->start(); // Restart } void Appearance::colorDialogCurrentChanged(const QColor& c) { changeColor(c); //updateColor(); } void Appearance::colorDialogFinished(int /*result*/) { DEBUG_APPEARANCE(stderr, "Appearance::colorDialogFinished result:%d\n", result); if(_configChangedTimer->isActive()) _configChangedTimer->stop(); if(_colorDialog) { _colorDialog->deleteLater(); _colorDialog = 0; } } void Appearance::configChangeTimeOut() { updateColor(); if(color && _colorDialog) { _colorDialog->blockSignals(true); _colorDialog->setCurrentColor(*color); _colorDialog->blockSignals(false); } changeGlobalColor(); } //--------------------------------------------------------- // doCancel //--------------------------------------------------------- void Appearance::doCancel() { MusEGlobal::muse->arranger()->getCanvas()->setBg(QPixmap(config->canvasBgPixmap)); MusEGlobal::config = *backupConfig; // Save settings. Use non-simple version - set style and stylesheet. MusEGlobal::muse->changeConfig(true); // Restore everything possible. // MusEGlobal::muse->updateThemeAndStyle(true); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void Appearance::closeEvent(QCloseEvent* e) { DEBUG_APPEARANCE(stderr, "Appearance::closeEvent\n"); doCancel(); if(_colorDialog) { _colorDialog->deleteLater(); _colorDialog = nullptr; } e->accept(); QDialog::closeEvent(e); } //--------------------------------------------------------- // okClicked //--------------------------------------------------------- void Appearance::okClicked() { DEBUG_APPEARANCE(stderr, "Appearance::ok\n"); if(_colorDialog) { _colorDialog->deleteLater(); _colorDialog = nullptr; } //close(); hide(); if(apply()) // If required, ask user to restart the application for proper results. checkClose(); } //--------------------------------------------------------- // cancel //--------------------------------------------------------- void Appearance::cancel() { DEBUG_APPEARANCE(stderr, "Appearance::cancel\n"); doCancel(); if(_colorDialog) { _colorDialog->deleteLater(); _colorDialog = nullptr; } //close(); hide(); } //--------------------------------------------------------- // removeBackground //--------------------------------------------------------- void Appearance::removeBackground() { QTreeWidgetItem* item = backgroundTree->currentItem(); MusEGlobal::muse->arranger()->getCanvas()->setBg(QPixmap()); user_bg->takeChild(user_bg->indexOfChild(item)); backgroundTree->setCurrentItem (nullptr); removeBgButton->setEnabled(false); } //--------------------------------------------------------- // addBackground //--------------------------------------------------------- void Appearance::addBackground() { QString cur = getenv("HOME"); QString user_bgfile = getImageFileName(cur, MusEGlobal::image_file_pattern, this, tr("MusE: load image")); bool image_exists = false; for (int i = 0; i < global_bg->childCount(); ++i) if (global_bg->child(i)->data(0, Qt::UserRole).toString() == user_bgfile) image_exists = true; for (int i = 0; i < user_bg->childCount(); ++i) if (user_bg->child(i)->data(0, Qt::UserRole).toString() == user_bgfile) image_exists = true; if (! image_exists) { QTreeWidgetItem* item = new QTreeWidgetItem(user_bg, 0); item->setData(0, Qt::UserRole, QVariant(user_bgfile)); BgPreviewWidget* bgw = new BgPreviewWidget(user_bgfile, backgroundTree); backgroundTree->setItemWidget(item, 0, bgw); } } //--------------------------------------------------------- // clearBackground //--------------------------------------------------------- void Appearance::clearBackground() { MusEGlobal::muse->arranger()->getCanvas()->setBg(QPixmap()); backgroundTree->setCurrentItem (0); removeBgButton->setEnabled(false); } //--------------------------------------------------------- // setColorDialogWindowText //--------------------------------------------------------- void Appearance::setColorDialogWindowText(const QString& colorName) { if(!_colorDialog) return; if(colorName.isEmpty()) _colorDialog->setWindowTitle(tr("No current color item")); else { const QString title = tr("Select Color: %1").arg(colorName); _colorDialog->blockSignals(true); _colorDialog->setWindowTitle(title); _colorDialog->blockSignals(false); } } //--------------------------------------------------------- // selectionChanged //--------------------------------------------------------- void Appearance::colorItemSelectionChanged() { IdListViewItem* item = (IdListViewItem*)itemList->selectedItems()[0]; lastSelectedColorItem = 0; if(!item) { colorNameLineEdit->setEnabled(false); setColorDialogWindowText(); updateColor(); return; } int id = item->id(); color = workingConfigColorFromId(id); if(!color) { lastSelectedColorItem = 0; colorNameLineEdit->setEnabled(false); setColorDialogWindowText(); updateColor(); return; } bool enle = false; if(id >= 0x600 && id < (0x600 + NUM_PARTCOLORS)) { lastSelectedColorItem = item; enle = true; } colorNameLineEdit->setEnabled(enle); QString s; if(enle) s = config->partColorNames[id & 0xff]; colorNameLineEdit->setText(s); updateColor(); if(_colorDialog) { _colorDialog->blockSignals(true); _colorDialog->setCurrentColor(*color); setColorDialogWindowText(item->text(0)); _colorDialog->blockSignals(false); } } void Appearance::updateColor() { int r, g, b, h, s, v; rslider->setEnabled(color); gslider->setEnabled(color); bslider->setEnabled(color); hslider->setEnabled(color); sslider->setEnabled(color); vslider->setEnabled(color); rval->setEnabled(color); gval->setEnabled(color); bval->setEnabled(color); hval->setEnabled(color); sval->setEnabled(color); vval->setEnabled(color); colorwidget->setEnabled(color); colorwidget->setEnabled(color); pickColorButton->setEnabled(color); if (color == 0) return; QPalette pal; QColor cfc(*color); colorwidget->setColor(cfc); color->getRgb(&r, &g, &b); color->getHsv(&h, &s, &v); rslider->blockSignals(true); gslider->blockSignals(true); bslider->blockSignals(true); hslider->blockSignals(true); sslider->blockSignals(true); vslider->blockSignals(true); rval->blockSignals(true); gval->blockSignals(true); bval->blockSignals(true); hval->blockSignals(true); sval->blockSignals(true); vval->blockSignals(true); rslider->setValue(r); gslider->setValue(g); bslider->setValue(b); hslider->setValue(h); sslider->setValue(s); vslider->setValue(v); rval->setValue(r); gval->setValue(g); bval->setValue(b); hval->setValue(h); sval->setValue(s); vval->setValue(v); rslider->blockSignals(false); gslider->blockSignals(false); bslider->blockSignals(false); hslider->blockSignals(false); sslider->blockSignals(false); vslider->blockSignals(false); rval->blockSignals(false); gval->blockSignals(false); bval->blockSignals(false); hval->blockSignals(false); sval->blockSignals(false); vval->blockSignals(false); } void Appearance::asliderChanged(int val) { globalAlphaVal->blockSignals(true); globalAlphaVal->setValue(val); globalAlphaVal->blockSignals(false); updateColor(); } void Appearance::aValChanged(int val) { globalAlphaSlider->blockSignals(true); globalAlphaSlider->setValue(val); globalAlphaSlider->blockSignals(false); updateColor(); } void Appearance::rsliderChanged(int val) { int r, g, b; if (color) { color->getRgb(&r, &g, &b); color->setRgb(val, g, b); } updateColor(); _configChangedTimer->start(); // Restart } void Appearance::gsliderChanged(int val) { int r, g, b; if (color) { color->getRgb(&r, &g, &b); color->setRgb(r, val, b); } updateColor(); _configChangedTimer->start(); // Restart } void Appearance::bsliderChanged(int val) { int r, g, b; if (color) { color->getRgb(&r, &g, &b); color->setRgb(r, g, val); } updateColor(); _configChangedTimer->start(); // Restart } void Appearance::hsliderChanged(int val) { int h, s, v; if (color) { color->getHsv(&h, &s, &v); color->setHsv(val, s, v); } updateColor(); _configChangedTimer->start(); // Restart } void Appearance::ssliderChanged(int val) { int h, s, v; if (color) { color->getHsv(&h, &s, &v); color->setHsv(h, val, v); } updateColor(); _configChangedTimer->start(); // Restart } void Appearance::vsliderChanged(int val) { int h, s, v; if (color) { color->getHsv(&h, &s, &v); color->setHsv(h, s, val); } updateColor(); _configChangedTimer->start(); // Restart } //--------------------------------------------------------- // addToPaletteClicked //--------------------------------------------------------- void Appearance::addToPaletteClicked() { QColor new_c = color ? *color : colorwidget->color(); QAbstractButton* button = (QAbstractButton*)aPalette->checkedButton(); // ddskrjo int r, g, b; QColor c; if (button) { int id = aPalette->id(button); c = config->palette[id]; c.getRgb(&r, &g, &b); } if (button == 0 || r != 0xff || g != 0xff || b != 0xff) { for (int i = 0; i < 16; ++i) { c = config->palette[i]; c.getRgb(&r, &g, &b); if (r == 0xff && g == 0xff && b == 0xff) { // found empty slot aPalette->button(i)->toggle(); button = (QAbstractButton*)aPalette->button(i); // ddskrjo break; } } } if (button) { int id = aPalette->id(button); config->palette[id] = new_c; button->setStyleSheet(QString("background-color: ") + new_c.name()); button->update(); //?? } } //--------------------------------------------------------- // paletteClicked //--------------------------------------------------------- void Appearance::paletteClicked(int id) { if (!color) return; QAbstractButton* button = (QAbstractButton*)aPalette->button(id); // ddskrjo if (button) { QColor c = button->palette().color(QPalette::Window); int r, g, b; c.getRgb(&r, &g, &b); if (r == 0xff && g == 0xff && b == 0xff) return; // interpret palette slot as empty *color = c; updateColor(); _configChangedTimer->start(); // Restart } } //--------------------------------------------------------- // browseFont //--------------------------------------------------------- void Appearance::browseFont1() { browseFont(1); } void Appearance::browseFont2() { browseFont(2); } void Appearance::browseFont3() { browseFont(3); } void Appearance::browseFont4() { browseFont(4); } void Appearance::browseFont5() { browseFont(5); } void Appearance::browseFont6() { browseFont(6); } void Appearance::browseFont(int n) { bool ok; QFont f(config->fonts[n]); if (!f.bold() && !f.italic()) f.setStyleName("Regular"); QFont font = QFontDialog::getFont(&ok, f, this, "browseFont"); if (ok) { config->fonts[n] = font; updateFonts(); } } void Appearance::on_pbAdjustFontSizes_clicked() { int fs = fontSize0->value(); fontSize1->setValue(qRound(fs * MusEGlobal::FntFac::F1)); fontSize2->setValue(qRound(fs * MusEGlobal::FntFac::F2)); fontSize3->setValue(qRound(fs * MusEGlobal::FntFac::F3)); fontSize4->setValue(qRound(fs * MusEGlobal::FntFac::F4)); fontSize5->setValue(qRound(fs * MusEGlobal::FntFac::F5)); fontSize6->setValue(qRound(fs * MusEGlobal::FntFac::F6)); } void Appearance::on_pbSetFontFamily_clicked() { fontName1->setText(fontName0->text()); fontName2->setText(fontName0->text()); fontName3->setText(fontName0->text()); fontName4->setText(fontName0->text()); fontName5->setText(fontName0->text()); fontName6->setText(fontName0->text()); } void Appearance::on_helpButton_clicked() { QString s("https://github.com/muse-sequencer/muse/wiki/ui-customizing"); MusEGlobal::muse->launchBrowser(s); } } // namespace MusEGui muse-4.0.0/src/muse/components/appearance.h000066400000000000000000000116601404105311000206520ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/appearance.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __APPEARANCE_H__ #define __APPEARANCE_H__ #include "ui_appearancebase.h" #include #include // Forward declarations: class QTimer; class QColorDialog; class QCloseEvent; class QButtonGroup; class QTreeWidget; class QColor; namespace MusEGlobal { struct GlobalConfigValues; } namespace MusEGui { //--------------------------------------------------------- // IdListViewItem //--------------------------------------------------------- class IdListViewItem : public QTreeWidgetItem { private: int _id; public: IdListViewItem(int id, QTreeWidgetItem* parent, QString s); IdListViewItem(int id, QTreeWidget* parent, QString s); int id() const; }; //--------------------------------------------------------- // Appearance Dialog //--------------------------------------------------------- class Appearance : public QDialog, public Ui::AppearanceDialogBase { Q_OBJECT protected: virtual void closeEvent(QCloseEvent*); private: QTimer* _configChangedTimer; QColor* color; MusEGlobal::GlobalConfigValues* backupConfig; MusEGlobal::GlobalConfigValues* config; QButtonGroup* aPalette; QTreeWidgetItem* user_bg; QTreeWidgetItem* global_bg; QTreeWidgetItem* lastSelectedBgItem; QTreeWidgetItem* lastSelectedColorItem; QColorDialog* _colorDialog; QColor* globalConfigColorFromId(int id) const; long int configOffsetFromColorId(int id) const; QColor* backupConfigColorFromId(int id) const; QColor* workingConfigColorFromId(int id) const; bool isColorDirty(IdListViewItem* item) const; bool isColorsDirty() const; // Sets current (last) item dirty. void setColorItemDirty(); // Sets an item dirty. void setColorItemDirty(IdListViewItem* item); // Update the dirty states of all items. void updateColorItems(); void resetColorItem(IdListViewItem* item); void resetAllColorItems(); void updateFonts(); void updateColor(); void changeColor(const QColor& c); void changeGlobalColor(); void setConfigurationColors(); void setColorDialogWindowText(const QString& colorName = QString()); void doCancel(); // Returns true if restart required (style or stylesheet changed). bool apply(); // Ask to close and if so, tell the main window to close the app and return true. bool checkClose(); bool changeTheme(); void saveCurrentThemeColors(); private slots: void applyClicked(); void okClicked(); void cancel(); void addBackground(); void removeBackground(); void clearBackground(); void colorItemSelectionChanged(); void browseFont(int); void browseFont1(); void browseFont2(); void browseFont3(); void browseFont4(); void browseFont5(); void browseFont6(); void asliderChanged(int); void aValChanged(int); void rsliderChanged(int); void gsliderChanged(int); void bsliderChanged(int); void hsliderChanged(int); void ssliderChanged(int); void vsliderChanged(int); void addToPaletteClicked(); void paletteClicked(int); void bgSelectionChanged(QTreeWidgetItem*); void colorNameEditFinished(); void loadColors(); void saveColors(); void chooseColorClicked(); void colorDialogCurrentChanged(const QColor&); void colorDialogFinished(int result); void configChangeTimeOut(); void colorListCustomContextMenuReq(const QPoint&); public: Appearance(QWidget* parent=0); ~Appearance(); void resetValues(); static QString& getSetDefaultStyle(const QString *newStyle = NULL); private slots: void on_pbSetFontFamily_clicked(); private slots: void on_pbAdjustFontSizes_clicked(); void on_helpButton_clicked(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/appearancebase.ui000066400000000000000000002176771404105311000217130ustar00rootroot00000000000000 AppearanceDialogBase 0 0 562 669 MusE: Appearance settings 0 0 0 Arranger Parts 6 11 2 11 6 Select e&vents to show: Show Ca&kewalk style Qt::Horizontal QSizePolicy::Maximum 30 20 false y-stretch false Show part names Events 11 11 11 11 6 note on poly pressure controller aftertouch pitch bend program change special Background picture true true true false 1 Add Remove Clear Grid Always show horizontal lines Show snap grid Always show beat lines, even if there are no grid lines there Wave drawing in parts 2 6 O&nly outline RMS/PEAK (&traditional) radioButtonDrawRmsPeak radioButtonDrawOutline Part colors Use the color of corresponding track type instead of default part color true Colors 0 0 Global opacity Qt::AlignCenter false 255 0 0 0 255 true Qt::Horizontal QSlider::NoTicks QAbstractItemView::SelectItems true true Items 0 0 Qt::NoFocus Palette 0 0 0 25 25 25 25 false true true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 25 25 25 25 false true 0 0 Choose color... Add to palette 255 255 Qt::Horizontal B Qt::AlignCenter false S Qt::AlignCenter false H Qt::AlignCenter false 255 255 Qt::Horizontal V Qt::AlignCenter false G Qt::AlignCenter false 255 255 Qt::Horizontal 255 Qt::Horizontal 255 Qt::Horizontal 255 0 0 0 255 true Qt::Horizontal QSlider::NoTicks 255 255 R Qt::AlignCenter false Color name: Save all... Load all... Qt::Horizontal 40 20 Themes 0 0 12 12 6 Current theme <html><head/><body><p>If a user style sheet for one of the MusE themes exists (in <span style=" font-style:italic;">~/.../themes</span> folder), it will be merged with the default style sheet for this theme. The user style sheet takes precedence and overwrites the corresponding values in the default style sheet.</p></body></html> Cascade default and user theme style sheets Show icons in menus true Use native standard dialogs true 0 0 0 Toolbar icon size 0 0 12 32 18 0 0 0 Custom cursor size 0 0 18 32 18 6 0 0 0 Track gradient strength 100 0 0 0 Part gradient strength 200 Qt::Vertical 20 40 Fonts 6 12 0 0 ... Italic Bold 0 0 Italic Font 4 false Fill in optimal font sizes in relation to the system font Bold false Bold Font sizes for fonts 1-6 are determined at every program start, to optimally match the currently used system font. Disable if you want to set fixed sizes in the font settings below. Default scaling factors: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% Calculate and apply relative font sizes dynamically at program start true 0 0 ... Maximum aliased size, 0 = no alias 0 0 Font 2 false 0 0 0 0 Font 6 false Font 3 false Font 1 false Font 5 false Insert Bold 0 0 ... Insert Italic false 0 0 Qt::Vertical QSizePolicy::Fixed 20 20 0 0 ... 0 0 0 0 ... Italic Bold 0 0 0 0 Size (pt) false 0 0 0 0 Bold Italic 0 0 ... 0 0 Family false false Italic 0 0 0 0 Italic Distribute current system font family to all other fonts System false At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. 0 100 0 Bold false 0 0 Qt::Vertical 20 40 Qt::Vertical QSizePolicy::Expanding 0 0 6 0 0 0 0 Qt::Horizontal QSizePolicy::Expanding 20 20 Help Apply Ok true Cancel ColorFrame QWidget
colorframe.h
1
TabWidget2 itemList palette0 palette1 palette2 palette3 palette4 palette5 palette6 palette7 palette8 palette9 palette10 palette11 palette12 palette13 palette14 palette15 addToPalette rval rslider gval gslider bval bslider hval hslider sval sslider vval vslider applyButton okButton cancelButton partShowevents partShowCakes eventNoteon eventPolypressure eventController eventAftertouch eventPitchbend eventProgramchange eventSpecial rval valueChanged(int) rslider setValue(int) 20 20 20 20 partShowCakes toggled(bool) partCakeStretch setEnabled(bool) 141 137 159 164 hval valueChanged(int) hslider setValue(int) 20 20 20 20 rslider valueChanged(int) rval setValue(int) 20 20 20 20 gval valueChanged(int) gslider setValue(int) 20 20 20 20 sslider valueChanged(int) sval setValue(int) 20 20 20 20 hslider valueChanged(int) hval setValue(int) 20 20 20 20 vval valueChanged(int) vslider setValue(int) 20 20 20 20 gslider valueChanged(int) gval setValue(int) 20 20 20 20 bslider valueChanged(int) bval setValue(int) 20 20 20 20 vslider valueChanged(int) vval setValue(int) 20 20 20 20 sval valueChanged(int) sslider setValue(int) 20 20 20 20 bval valueChanged(int) bslider setValue(int) 20 20 20 20
muse-4.0.0/src/muse/components/arrangercolumns.cpp000066400000000000000000000146301404105311000223100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: arrangercolumns.cpp, flo93 $ // // (C) Copyright 2012 Florian Jung (florian.a.jung@web.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "arrangercolumns.h" #include "midictrl.h" #include "arranger.h" namespace MusEGui { ArrangerColumns::ArrangerColumns(QWidget* parent) : QDialog(parent) { ignoreSomethingChanged=true; setupUi(this); ctrlType->addItem(tr("Control7"), MusECore::MidiController::Controller7); ctrlType->addItem(tr("Control14"), MusECore::MidiController::Controller14); ctrlType->addItem(tr("RPN"), MusECore::MidiController::RPN); ctrlType->addItem(tr("NPRN"), MusECore::MidiController::NRPN); ctrlType->addItem(tr("RPN14"), MusECore::MidiController::RPN14); ctrlType->addItem(tr("NRPN14"), MusECore::MidiController::NRPN14); ctrlType->addItem(tr("Pitch"), MusECore::MidiController::Pitch); ctrlType->addItem(tr("Program"), MusECore::MidiController::Program); //ctrlType->addItem(tr("PolyAftertouch"), MusECore::MidiController::PolyAftertouch); // Not supported yet. Need a way to select pitch. ctrlType->addItem(tr("Aftertouch"), MusECore::MidiController::Aftertouch); ctrlType->setCurrentIndex(0); initList(); connect(ctrlType,SIGNAL(currentIndexChanged(int)), SLOT(ctrlTypeChanged(int))); // connect(ctrlType,SIGNAL(activated(int)), SLOT(somethingChanged())); // called by ctrlTypeChanged connect(nameEdit,SIGNAL(textEdited(const QString&)), SLOT(somethingChanged())); connect(spinBoxHCtrlNo,SIGNAL(valueChanged(int)), SLOT(somethingChanged())); connect(spinBoxLCtrlNo,SIGNAL(valueChanged(int)), SLOT(somethingChanged())); connect(affectBeginButton,SIGNAL(toggled(bool)), SLOT(somethingChanged())); connect(affectCposButton,SIGNAL(toggled(bool)), SLOT(somethingChanged())); connect(listWidget,SIGNAL(currentRowChanged(int)), SLOT(itemSelected(int))); connect(addBtn,SIGNAL(clicked()), SLOT(addEntry())); connect(delBtn,SIGNAL(clicked()), SLOT(delEntry())); if (listWidget->count()!=0) listWidget->setCurrentRow(0); else itemSelected(-1); ctrlTypeChanged(ctrlType->currentIndex()); ignoreSomethingChanged=false; } void ArrangerColumns::ctrlTypeChanged(int idx) { if(idx == -1) return; MusECore::MidiController::ControllerType t = (MusECore::MidiController::ControllerType)ctrlType->itemData(idx).toInt(); switch (t) { case MusECore::MidiController::Controller7: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(true); break; case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: spinBoxHCtrlNo->setEnabled(true); spinBoxLCtrlNo->setEnabled(true); break; default: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(false); break; } somethingChanged(); } void ArrangerColumns::somethingChanged() { if (ignoreSomethingChanged) return; int row=listWidget->currentRow(); if (row!=-1 && ctrlType->currentIndex() != -1) { MusECore::MidiController::ControllerType t = (MusECore::MidiController::ControllerType)ctrlType->itemData(ctrlType->currentIndex()).toInt(); int hnum = spinBoxHCtrlNo->value(); int lnum = spinBoxLCtrlNo->value(); int ctrl_number = MusECore::MidiController::genNum(t, hnum, lnum); Arranger::custom_columns[row].name=nameEdit->text(); Arranger::custom_columns[row].ctrl=ctrl_number; Arranger::custom_columns[row].affected_pos=(affectBeginButton->isChecked() ? Arranger::custom_col_t::AFFECT_BEGIN : Arranger::custom_col_t::AFFECT_CPOS); listWidget->currentItem()->setText(getListEntryString(row)); } } QString ArrangerColumns::getListEntryString(int row) { return "\""+Arranger::custom_columns[row].name+"\": "+MusECore::midiCtrlNumString(Arranger::custom_columns[row].ctrl, true); } void ArrangerColumns::initList() { listWidget->clear(); for (unsigned int i=0;iaddItem(getListEntryString(i)); } void ArrangerColumns::itemSelected(int i) { ignoreSomethingChanged=true; if (i==-1) { frame->setEnabled(false); delBtn->setEnabled(false); } else { frame->setEnabled(true); delBtn->setEnabled(true); nameEdit->setText(Arranger::custom_columns[i].name); int num=Arranger::custom_columns[i].ctrl; int idx = ctrlType->findData(MusECore::midiControllerType(num)); if(idx != -1) ctrlType->setCurrentIndex(idx); if (spinBoxHCtrlNo->isEnabled()) spinBoxHCtrlNo->setValue((num & 0xFF00)>>8); else spinBoxHCtrlNo->setValue(0); if (spinBoxLCtrlNo->isEnabled()) spinBoxLCtrlNo->setValue(num & 0xFF); else spinBoxLCtrlNo->setValue(0); affectBeginButton->setChecked(Arranger::custom_columns[i].affected_pos == Arranger::custom_col_t::AFFECT_BEGIN); affectCposButton->setChecked(Arranger::custom_columns[i].affected_pos == Arranger::custom_col_t::AFFECT_CPOS); } ignoreSomethingChanged=false; } void ArrangerColumns::addEntry() { Arranger::custom_columns.push_back(Arranger::custom_col_t(0,QString("?"))); listWidget->addItem(getListEntryString(listWidget->count())); listWidget->setCurrentRow(listWidget->count()-1); } void ArrangerColumns::delEntry() { int row=listWidget->currentRow(); if (row!=-1) { std::vector::iterator it=Arranger::custom_columns.begin(); advance(it, row); Arranger::custom_columns.erase(it); initList(); if (listWidget->count()>0) { if (listWidget->count()<=row) row=listWidget->count()-1; listWidget->setCurrentRow(row); itemSelected(row); } else itemSelected(-1); } } } // namespace MusEGui muse-4.0.0/src/muse/components/arrangercolumns.h000066400000000000000000000030001404105311000217420ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: arrangercolumns.h, flo93 $ // // (C) Copyright 2012 Florian Jung (florian.a.jung@web.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ARRANGERCOLUMNS_H__ #define __ARRANGERCOLUMNS_H__ #include "ui_arrangercolumnsbase.h" #include namespace MusEGui { class ArrangerColumns : public QDialog, private Ui::ArrangerColumnsBase { Q_OBJECT public: ArrangerColumns(QWidget* parent); private slots: void ctrlTypeChanged(int idx); void somethingChanged(); void initList(); void itemSelected(int idx); void addEntry(); void delEntry(); QString getListEntryString(int idx); private: bool ignoreSomethingChanged; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/arrangercolumnsbase.ui000066400000000000000000000167771404105311000230140ustar00rootroot00000000000000 ArrangerColumnsBase 0 0 319 485 Configure arranger columns Columns: QFrame::StyledPanel QFrame::Raised Name: Controller type: false Midi controller type Midi controller type H-Ctrl false 10 0 0 Midi controller number high byte Midi controller number high byte 0 127 0 L-Ctrl false 10 0 0 Midi controller number low byte Midi controller number low byte * -1 127 0 Qt::Vertical QSizePolicy::Fixed 20 10 Affect CCs at 6 0 0 begin of song true current position &Add &Delete Done doneBtn clicked() ArrangerColumnsBase accept() 260 383 159 203 muse-4.0.0/src/muse/components/audio_converter_settings.cpp000066400000000000000000000160721404105311000242200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // audio_converter_settings.cpp // (C) Copyright 2016 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; 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 #include #include "audio_converter_settings.h" namespace MusEGui { AudioConverterSettingsDialog::AudioConverterSettingsDialog( QWidget* parent, MusECore::AudioConverterPluginList* pluginList, MusECore::AudioConverterSettingsGroup* settings, bool isLocal) : QDialog(parent), _pluginList(pluginList), _settings(settings), _isLocal(isLocal) { setupUi(this); OKButton->setEnabled(false); fillList(); if(_settings) { int idx; idx = sampleratePreferenceComboBox->findData(_settings->_options._preferredResampler); //if(_isLocal || idx >= 0) if(idx >= 0) sampleratePreferenceComboBox->setCurrentIndex(idx); idx = shiftingPreferenceComboBox->findData(_settings->_options._preferredShifter); //if(_isLocal || idx >= 0) if(idx >= 0) shiftingPreferenceComboBox->setCurrentIndex(idx); } preferencesGroup->setEnabled(!isLocal || (_settings && _settings->_options._useSettings)); //if(_isLocal) useDefaultPreferences->setChecked(!_settings || !_settings->_options._useSettings); useDefaultPreferences->setEnabled(isLocal && _settings); useDefaultPreferences->setVisible(isLocal && _settings); connect(converterList, SIGNAL(itemSelectionChanged()), SLOT(converterSelectionChanged())); connect(offlineSettingsButton, SIGNAL(clicked()), SLOT(offlineSettingsClicked())); connect(realtimeSettingsButton, SIGNAL(clicked()), SLOT(realtimeSettingsClicked())); connect(guiSettingsButton, SIGNAL(clicked()), SLOT(guiSettingsClicked())); connect(sampleratePreferenceComboBox, SIGNAL(currentIndexChanged(int)), SLOT(preferredResamplerChanged(int))); connect(shiftingPreferenceComboBox, SIGNAL(currentIndexChanged(int)), SLOT(preferredShifterChanged(int))); connect(useDefaultPreferences, SIGNAL(clicked()), SLOT(useDefaultsClicked())); connect(OKButton, SIGNAL(clicked()), SLOT(okClicked())); connect(cancelButton, SIGNAL(clicked()), SLOT(cancelClicked())); converterSelectionChanged(); } void AudioConverterSettingsDialog::fillList() { converterList->blockSignals(true); converterList->clear(); converterList->blockSignals(false); // if(_isLocal) // { // sampleratePreferenceComboBox->addItem(tr("Use default settings"), -1); // shiftingPreferenceComboBox->addItem(tr("Use default settings"), -1); // } if(_pluginList) { for(MusECore::ciAudioConverterPlugin ip = _pluginList->cbegin(); ip != _pluginList->cend(); ++ip) { MusECore::AudioConverterPlugin* plugin = *ip; QListWidgetItem* item = new QListWidgetItem(plugin->name(), converterList); item->setData(Qt::UserRole, plugin->id()); const int caps = plugin->capabilities(); if(caps & MusECore::AudioConverter::SampleRate) sampleratePreferenceComboBox->addItem(plugin->name(), plugin->id()); if(caps & MusECore::AudioConverter::Stretch) // TODO: Separate pitch preference? shiftingPreferenceComboBox->addItem(plugin->name(), plugin->id()); } } converterList->setCurrentItem(NULL); } void AudioConverterSettingsDialog::converterSelectionChanged() { enableSettingsButtons(); } void AudioConverterSettingsDialog::enableSettingsButtons() { bool enable = false; if(_pluginList) { QList selections = converterList->selectedItems(); if(!selections.isEmpty()) { QListWidgetItem* sel_item = selections.first(); int id = sel_item->data(Qt::UserRole).toInt(); if(id >= 0 && _pluginList->find(NULL, id)) enable = true; } } offlineSettingsButton->setEnabled(enable); realtimeSettingsButton->setEnabled(enable); guiSettingsButton->setEnabled(enable); } void AudioConverterSettingsDialog::preferredResamplerChanged(int /*idx*/) { // if(idx < 0 || !_settings) // return; // const int id = sampleratePreferenceComboBox->itemData(idx).toInt(); // _settings->_options._preferredResampler = id; OKButton->setEnabled(true); } void AudioConverterSettingsDialog::preferredShifterChanged(int /*idx*/) { // if(idx < 0 || !_settings) // return; // const int id = shiftingPreferenceComboBox->itemData(idx).toInt(); // _settings->_options._preferredShifter = id; OKButton->setEnabled(true); } void AudioConverterSettingsDialog::offlineSettingsClicked() { showSettings(MusECore::AudioConverterSettings::OfflineMode); } void AudioConverterSettingsDialog::realtimeSettingsClicked() { showSettings(MusECore::AudioConverterSettings::RealtimeMode); } void AudioConverterSettingsDialog::guiSettingsClicked() { showSettings(MusECore::AudioConverterSettings::GuiMode); } void AudioConverterSettingsDialog::showSettings(MusECore::AudioConverterSettings::ModeType mode) { if(!_settings) return; QListWidgetItem* cur_item = converterList->currentItem(); if(!cur_item) return; int id = cur_item->data(Qt::UserRole).toInt(); if(id < 0) return; MusECore::AudioConverterSettingsI* setI = _settings->find(id); if(!setI) return; if(setI->executeUI(mode, this, _isLocal) == QDialog::Accepted) OKButton->setEnabled(true); } void AudioConverterSettingsDialog::useDefaultsClicked() { OKButton->setEnabled(true); preferencesGroup->setEnabled(!useDefaultPreferences->isChecked()); } void AudioConverterSettingsDialog::okClicked() { accept(); } void AudioConverterSettingsDialog::cancelClicked() { reject(); } //--------------------------------------------------------- // accept //--------------------------------------------------------- void AudioConverterSettingsDialog::accept() { if(!_settings) { QDialog::accept(); return; } QVariant d; d = sampleratePreferenceComboBox->currentData(); if(d.isValid()) { const int id = d.toInt(); _settings->_options._preferredResampler = id; } d = shiftingPreferenceComboBox->currentData(); if(d.isValid()) { const int id = d.toInt(); _settings->_options._preferredShifter = id; } _settings->_options._useSettings = !useDefaultPreferences->isChecked(); QDialog::accept(); } } // namespace MusEGui muse-4.0.0/src/muse/components/audio_converter_settings.h000066400000000000000000000044741404105311000236700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // audio_converter_settings.h // (C) Copyright 2016 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __AUDIO_CONVERTER_SETTINGS_H__ #define __AUDIO_CONVERTER_SETTINGS_H__ #include "ui_audio_converter_settings_base.h" #include "audio_convert/audio_converter_plugin.h" #include "audio_convert/audio_converter_settings_group.h" namespace MusEGui { class AudioConverterSettingsDialog : public QDialog, public Ui::AudioConverterSettingsBase { Q_OBJECT private: MusECore::AudioConverterPluginList* _pluginList; MusECore::AudioConverterSettingsGroup* _settings; bool _isLocal; void fillList(); void enableSettingsButtons(); void showSettings(MusECore::AudioConverterSettings::ModeType mode); private slots: void useDefaultsClicked(); void okClicked(); void cancelClicked(); void accept(); void converterSelectionChanged(); void preferredResamplerChanged(int); void preferredShifterChanged(int); void offlineSettingsClicked(); void realtimeSettingsClicked(); void guiSettingsClicked(); public: AudioConverterSettingsDialog(QWidget* parent = NULL, MusECore::AudioConverterPluginList* pluginList = NULL, MusECore::AudioConverterSettingsGroup* settings = NULL, bool isLocal = false); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/audio_converter_settings_base.ui000066400000000000000000000147311404105311000250450ustar00rootroot00000000000000 AudioConverterSettingsBase 0 0 467 462 Audio Converter Settings Available audio converters Qt::Vertical 20 40 Offline settings... Realtime settings... Gui settings... Qt::Vertical 20 40 Use default preferences (see Global Settings) Preferences 0 0 When only samplerate conversion is required, use: true Qt::Horizontal 40 20 0 0 When time stretching or pitch shifting is required, use: true Qt::Horizontal 40 20 Qt::Horizontal 40 20 0 0 OK true Cancel muse-4.0.0/src/muse/components/background_painter.cpp000066400000000000000000000075761404105311000227620ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // background_painter.cpp // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "gconfig.h" #include "background_painter.h" // #include // For debugging output: Uncomment the fprintf section. #define DEBUG_BACKGROUND_PAINTER(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { ItemBackgroundPainter::ItemBackgroundPainter(QObject* parent) : QObject(parent) { } void ItemBackgroundPainter::drawBackground(QPainter* painter, const QRect& fullRect, const QPalette& pal, int xMargin, int yMargin, const QRect& onRect, int radius, bool style3d, QColor colSlotActive, QColor colBorder, QColor colSlot) { painter->save(); painter->setRenderHint(QPainter::Antialiasing); painter->setPen(Qt::NoPen); if (!colBorder.isValid()) colBorder = pal.dark().color().darker(130); if (!colSlot.isValid()) colSlot = pal.dark().color(); if (!colSlotActive.isValid()) colSlotActive = pal.highlight().color(); bool onfull = false; if(!onRect.isNull()) onfull = (onRect == fullRect); QRect cr = QRect(fullRect.x() + xMargin, fullRect.y() + yMargin, fullRect.width() - 2 * xMargin, fullRect.height() - 2 * yMargin); painter->fillRect(fullRect, colBorder); if (onRect.isNull() || !onfull) { int cw = fullRect.width(); if(!onRect.isNull()) cw -= onRect.width(); const QRect knobclip(fullRect.x(), fullRect.y(), cw, fullRect.height()); painter->setClipRect(knobclip); painter->setBrush(colSlot); painter->drawRoundedRect(cr, radius, radius); painter->setClipRect(fullRect); } if(!onRect.isNull()) { QRect labeldraw = QRect(onRect.x() + xMargin, onRect.y() + yMargin, onRect.width() - 2 * xMargin, onRect.height() - 2 * yMargin); painter->setBrush(colSlotActive); painter->drawRoundedRect(labeldraw, radius, radius); } if (style3d) { const QColor mask_edge = QColor(110, 110, 110, 55); const QColor mask_center = QColor(220, 220, 220, 55); QLinearGradient mask; mask.setColorAt(0, mask_edge); mask.setColorAt(0.5, mask_center); mask.setColorAt(1, mask_edge); mask.setStart(QPointF(0, cr.y())); mask.setFinalStop(QPointF(0, cr.y() + cr.height())); painter->setBrush(mask); painter->drawRoundedRect(cr, radius, radius); } painter->restore(); } } // namespace MusEGui muse-4.0.0/src/muse/components/background_painter.h000066400000000000000000000037471404105311000224230ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // background_painter.h // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __BACKGROUND_PAINTER_H__ #define __BACKGROUND_PAINTER_H__ #include #include #include #include #include #include #include #include namespace MusEGui { //--------------------------------------------------------- // ItemBackgroundPainter //--------------------------------------------------------- class ItemBackgroundPainter : public QObject { Q_OBJECT public: ItemBackgroundPainter(QObject* parent = nullptr); void drawBackground(QPainter* painter, const QRect& rect, const QPalette& pal, int xMargin = 1, int yMargin = 1, const QRect& onRect = QRect(), int radius = 2, bool style3d = true, QColor colSlotActive = QColor(), QColor colBorder = QColor(), QColor colSlot = QColor()); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/bigtime.cpp000066400000000000000000000367671404105311000205450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/bigtime.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= //#include #include #include "muse_math.h" #include #include #include #include #include "sync.h" #include "sig.h" #include "globals.h" #include "bigtime.h" #include "app.h" #include "gconfig.h" #include "audio.h" namespace MusEGlobal { extern int mtcType; } namespace MusEGui { // // the bigtime widget // display is split into several parts to avoid flickering. // //--------------------------------------------------------- // BigTime //--------------------------------------------------------- BigTime::BigTime(QWidget* parent) : QWidget(parent, Qt::Window | Qt::WindowStaysOnTopHint) // Possibly also Qt::X11BypassWindowManagerHint { _curPos = 0; tickmode = true; dwin = new QWidget(this, Qt::WindowStaysOnTopHint); // Possibly also Qt::X11BypassWindowManagerHint dwin->setObjectName("bigtime-dwin"); dwin->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); dwin->setStyleSheet("QWidget {font-size:10px; font-family:'Courier'}"); // Tim p4.0.8 fmtButton = new QCheckBox(QString(""), this); fmtButton->resize(18,18); fmtButton->setChecked(true); fmtButton->setToolTip(tr("Format display")); fmtButton->setFocusPolicy(Qt::NoFocus); barLabel = new QLabel(dwin); beatLabel = new QLabel(dwin); tickLabel = new QLabel(dwin); //hourLabel = new QLabel(dwin); minLabel = new QLabel(dwin); secLabel = new QLabel(dwin); frameLabel = new QLabel(dwin); subFrameLabel = new QLabel(dwin); sep1 = new QLabel(QString("."), dwin); sep2 = new QLabel(QString("."), dwin); sep3 = new QLabel(QString(":"), dwin); sep4 = new QLabel(QString(":"), dwin); sep5 = new QLabel(QString(":"), dwin); absTickLabel = new QLabel(dwin); absFrameLabel = new QLabel(dwin); barLabel->setToolTip(tr("Bar")); beatLabel->setToolTip(tr("Beat")); tickLabel->setToolTip(tr("Tick")); //hourLabel->setToolTip(tr("hour")); minLabel->setToolTip(tr("Minute")); secLabel->setToolTip(tr("Second")); frameLabel->setToolTip(tr("Frame")); subFrameLabel->setToolTip(tr("Subframe")); absTickLabel->setToolTip(tr("Tick")); absFrameLabel->setToolTip(tr("Frame")); fmtButtonToggled(true); connect(fmtButton, SIGNAL(toggled(bool)), SLOT(fmtButtonToggled(bool))); //oldbar = oldbeat = oldtick = oldhour = oldmin = oldsec = oldframe = -1; oldbar = oldbeat = oldtick = oldmin = oldsec = oldframe = oldsubframe = -1; oldAbsTick = oldAbsFrame = -1; setString(INT_MAX); metronome = new VerticalMetronomeWidget(dwin); configChanged(); setWindowTitle(tr("MusE: Bigtime")); } //--------------------------------------------------------- // fmtButtonToggled //--------------------------------------------------------- void BigTime::fmtButtonToggled(bool v) { if(v) { tickmode = true; barLabel->setEnabled(true); beatLabel->setEnabled(true); tickLabel->setEnabled(true); //hourLabel->setEnabled(true); minLabel->setEnabled(true); secLabel->setEnabled(true); frameLabel->setEnabled(true); subFrameLabel->setEnabled(true); sep1->setEnabled(true); sep2->setEnabled(true); sep3->setEnabled(true); sep4->setEnabled(true); sep5->setEnabled(true); absTickLabel->setEnabled(false); absFrameLabel->setEnabled(false); barLabel->show(); beatLabel->show(); tickLabel->show(); //hourLabel->show(); minLabel->show(); secLabel->show(); frameLabel->show(); subFrameLabel->show(); sep1->show(); sep2->show(); sep3->show(); sep4->show(); sep5->show(); absTickLabel->hide(); absFrameLabel->hide(); } else { tickmode = false; barLabel->setEnabled(false); beatLabel->setEnabled(false); tickLabel->setEnabled(false); //hourLabel->setEnabled(false); minLabel->setEnabled(false); secLabel->setEnabled(false); frameLabel->setEnabled(false); subFrameLabel->setEnabled(false); sep1->setEnabled(false); sep2->setEnabled(false); sep3->setEnabled(false); sep4->setEnabled(false); sep5->setEnabled(false); absTickLabel->setEnabled(true); absFrameLabel->setEnabled(true); barLabel->hide(); beatLabel->hide(); tickLabel->hide(); //hourLabel->hide(); minLabel->hide(); secLabel->hide(); frameLabel->hide(); subFrameLabel->hide(); sep1->hide(); sep2->hide(); sep3->hide(); sep4->hide(); sep5->hide(); absTickLabel->show(); absFrameLabel->show(); } } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void BigTime::configChanged() { setBgColor(MusEGlobal::config.bigTimeBackgroundColor); setFgColor(MusEGlobal::config.bigTimeForegroundColor); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void BigTime::songChanged(MusECore::SongChangedStruct_t flags) { if (flags & (SC_MASTER | SC_TEMPO | SC_SIG)) updateValue(); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void BigTime::closeEvent(QCloseEvent *ev) { emit closed(); QWidget::closeEvent(ev); } //--------------------------------------------------------- // setString //--------------------------------------------------------- bool BigTime::setString(unsigned v) { if (v == INT_MAX) { barLabel->setText(QString("----")); beatLabel->setText(QString("--")); tickLabel->setText(QString("---")); //hourLabel->setText(QString("--")); //minLabel->setText(QString("--")); minLabel->setText(QString("---")); secLabel->setText(QString("--")); frameLabel->setText(QString("--")); subFrameLabel->setText(QString("--")); absTickLabel->setText(QString("----------")); absFrameLabel->setText(QString("----------")); oldAbsTick = oldAbsFrame = -1; //oldbar = oldbeat = oldtick = oldhour = oldmin = oldsec = oldframe = -1; oldbar = oldbeat = oldtick = oldmin = oldsec = oldframe = oldsubframe = -1; return true; } // Quick fix: Not much to do but ignore the supplied tick: We need the exact frame here. unsigned absFrame = MusEGlobal::audio->pos().frame(); int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(v, &bar, &beat, &tick); double time = double(absFrame)/double(MusEGlobal::sampleRate); //int hour = int(time) / 3600; //int min = (int(time) / 60) % 60; int min = int(time) / 60; int sec = int(time) % 60; double rest = time - (min * 60 + sec); switch(MusEGlobal::mtcType) { case 0: // 24 frames sec rest *= 24; break; case 1: // 25 rest *= 25; break; case 2: // 30 drop frame rest *= 30; break; case 3: // 30 non drop frame rest *= 30; break; } int frame = int(rest); int subframe = int((rest-frame)*100); QString s; if(oldAbsTick != v) { s = QString("%1").arg(v, 10, 10, QLatin1Char('0')); absTickLabel->setText(s); oldAbsTick = v; } if(oldAbsFrame != absFrame) { s = QString("%1").arg(absFrame, 10, 10, QLatin1Char('0')); absFrameLabel->setText(s); oldAbsFrame = absFrame; } if(oldbar != bar) { s = QString("%1").arg(bar + 1, 4, 10, QLatin1Char('0')); barLabel->setText(s); oldbar = bar; } if(oldbeat != beat) { s = QString("%1").arg(beat + 1, 2, 10, QLatin1Char('0')); beatLabel->setText(s); oldbeat = beat; } if(oldtick != tick) { s = QString("%1").arg(tick, 3, 10, QLatin1Char('0')); tickLabel->setText(s); oldtick = tick; } //if(oldhour != hour) { // s = QString("%1").arg(hour, 2, 10, QLatin1Char('0')); // hourLabel->setText(s); // oldhour = hour; //} if(oldmin != min) { s = QString("%1").arg(min, 3, 10, QLatin1Char('0')); minLabel->setText(s); oldmin = min; } if(oldsec != sec) { s = QString("%1").arg(sec, 2, 10, QLatin1Char('0')); secLabel->setText(s); oldsec = sec; } if(oldframe != frame) { s = QString("%1").arg(frame, 2, 10, QLatin1Char('0')); frameLabel->setText(s); oldframe = frame; } if(oldsubframe != subframe) { s = QString("%1").arg(subframe, 2, 10, QLatin1Char('0')); subFrameLabel->setText(s); oldsubframe = subframe; } return false; } //--------------------------------------------------------- // setPos //--------------------------------------------------------- #define PI 3.14159265 void BigTime::setPos(int idx, unsigned v, bool) { if (idx == 0) { _curPos = v; int calcV = v%(MusEGlobal::config.midiDivision*2); double rangeAdjuster = PI/double(MusEGlobal::config.midiDivision); metronome->setMetronome(sin(double(calcV)*rangeAdjuster)); //printf("calcV=%d rangeAdjuster %f metronomePosition=%f midiDivision=%d\n",v,rangeAdjuster, metronomePosition, MusEGlobal::config.midiDivision); setString(v); } metronome->update(); } //--------------------------------------------------------- // updateValue //--------------------------------------------------------- void BigTime::updateValue() { int calcV = _curPos%(MusEGlobal::config.midiDivision*2); double rangeAdjuster = PI/double(MusEGlobal::config.midiDivision); metronome->setMetronome(sin(double(calcV)*rangeAdjuster)); //printf("calcV=%d rangeAdjuster %f metronomePosition=%f midiDivision=%d\n",_curPos,rangeAdjuster, metronomePosition, MusEGlobal::config.midiDivision); setString(_curPos); update(); metronome->update(); } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void BigTime::resizeEvent(QResizeEvent *ev) { QWidget::resizeEvent(ev); dwin->resize(ev->size()); QFont f = dwin->font(); QFontMetrics fm(f); int fs = f.pixelSize(); int hspace = 20; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int tw = fm.horizontalAdvance(QString("0000.00.00000")); #else int tw = fm.width(QString("0000.00.00000")); #endif fs = ((ev->size().width() - hspace*2)*fs) / tw; // set min/max if (fs < 10) fs = 10; else if (fs > 256) fs = 256; QString fstr = QString("QWidget {font-size:%1px; font-family:'Courier'}").arg(fs); // Tim p4.0.8 dwin->setStyleSheet(fstr); setBgColor(MusEGlobal::config.bigTimeBackgroundColor); setFgColor(MusEGlobal::config.bigTimeForegroundColor); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int digitWidth = dwin->fontMetrics().horizontalAdvance(QString("0")); #else int digitWidth = dwin->fontMetrics().width(QString("0")); #endif int vspace = (ev->size().height() - (fs*2)) / 3; int tickY = vspace; int timeY = vspace*2 + fs; int absTickY = tickY; int absFrameY = timeY; barLabel->resize(digitWidth*4, fs); beatLabel->resize(digitWidth*2, fs); tickLabel->resize(digitWidth*5, fs); minLabel->resize(digitWidth*3, fs); secLabel->resize(digitWidth*2, fs); frameLabel->resize(digitWidth*2, fs); subFrameLabel->resize(digitWidth*2, fs); absTickLabel->resize(digitWidth*10, fs); absFrameLabel->resize(digitWidth*10, fs); sep1->resize(digitWidth, fs); sep2->resize(digitWidth, fs); sep3->resize(digitWidth, fs); sep4->resize(digitWidth, fs); sep5->resize(digitWidth, fs); barLabel->move( hspace + (digitWidth*0), tickY); sep1->move( hspace + (digitWidth*4), tickY); beatLabel->move( hspace + (digitWidth*5), tickY); sep2->move( hspace + (digitWidth*7), tickY); tickLabel->move( hspace + (digitWidth*8), tickY); minLabel->move( hspace + (digitWidth*0), timeY); sep3->move( hspace + (digitWidth*3), timeY); secLabel->move( hspace + (digitWidth*4), timeY); sep4->move( hspace + (digitWidth*6), timeY); frameLabel->move( hspace + (digitWidth*7), timeY); sep5->move( hspace + (digitWidth*9), timeY); subFrameLabel->move( hspace + (digitWidth*10), timeY); absTickLabel->move( hspace + (digitWidth*0), absTickY); absFrameLabel->move( hspace + (digitWidth*0), absFrameY); metronome->move(0,dwin->height() - dwin->height()/10); metronome->resize(dwin->width(),dwin->height()/10); } //--------------------------------------------------------- // setForegroundColor //--------------------------------------------------------- void BigTime::setFgColor(QColor c) { QPalette newpalette(palette()); newpalette.setColor(QPalette::Foreground, c); setPalette(newpalette); barLabel->setPalette(newpalette); beatLabel->setPalette(newpalette); tickLabel->setPalette(newpalette); //hourLabel->setPalette(newpalette); minLabel->setPalette(newpalette); secLabel->setPalette(newpalette); frameLabel->setPalette(newpalette); subFrameLabel->setPalette(newpalette); absTickLabel->setPalette(newpalette); absFrameLabel->setPalette(newpalette); sep1->setPalette(newpalette); sep2->setPalette(newpalette); sep3->setPalette(newpalette); sep4->setPalette(newpalette); sep5->setPalette(newpalette); } //--------------------------------------------------------- // setBackgroundColor //--------------------------------------------------------- void BigTime::setBgColor(QColor c) { QPalette newpalette(palette()); newpalette.setColor(QPalette::Window, c); setPalette(newpalette); barLabel->setPalette(newpalette); beatLabel->setPalette(newpalette); tickLabel->setPalette(newpalette); //hourLabel->setPalette(newpalette); minLabel->setPalette(newpalette); secLabel->setPalette(newpalette); frameLabel->setPalette(newpalette); subFrameLabel->setPalette(newpalette); absTickLabel->setPalette(newpalette); absFrameLabel->setPalette(newpalette); sep1->setPalette(newpalette); sep2->setPalette(newpalette); sep3->setPalette(newpalette); sep4->setPalette(newpalette); sep5->setPalette(newpalette); setPalette(newpalette); } } // namespace MusEGui muse-4.0.0/src/muse/components/bigtime.h000066400000000000000000000064161404105311000201760ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/bigtime.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __BIGTIME_H__ #define __BIGTIME_H__ #include #include #include #include "muse_math.h" #include "type_defs.h" class QCheckBox; class QLabel; namespace MusEGui { class MusE; class VerticalMetronomeWidget : public QWidget { float metronomePosition; protected: void paintEvent (QPaintEvent *ev ) { QRect rr(ev->rect()); QPainter p(this); //p.fillRect(rr,Qt::yellow); int y = (rr.height() - (rr.height()*fabs(metronomePosition)))-1; if (metronomePosition > -0.05 and metronomePosition < 0.15) { p.setPen(Qt::red); p.drawLine(0,y-1,rr.width(),y-1); } else { p.setPen(Qt::yellow); } p.drawLine(0,y,rr.width(),y); } public: VerticalMetronomeWidget(QWidget* parent) : QWidget(parent) { metronomePosition=0.0; } void setMetronome(float v) { metronomePosition = v; update(); } }; //--------------------------------------------------------- // BigTime //--------------------------------------------------------- class BigTime : public QWidget { Q_OBJECT bool tickmode; MusE* seq; unsigned _curPos; bool setString(unsigned); void updateValue(); VerticalMetronomeWidget *metronome; QWidget *dwin; QCheckBox *fmtButton; QLabel *absTickLabel; QLabel *absFrameLabel; QLabel *barLabel, *beatLabel, *tickLabel, //*hourLabel, *minLabel, *secLabel, *frameLabel, *minLabel, *secLabel, *frameLabel, *subFrameLabel, *sep1, *sep2, *sep3, *sep4, *sep5; //int oldbar, oldbeat, oldhour, oldmin, oldsec, oldframe; int oldbar, oldbeat, oldmin, oldsec, oldframe, oldsubframe; unsigned oldtick; unsigned oldAbsTick, oldAbsFrame; void setFgColor(QColor c); void setBgColor(QColor c); protected: virtual void resizeEvent(QResizeEvent*); virtual void closeEvent(QCloseEvent*); //void paintEvent (QPaintEvent *event ); public slots: void setPos(int, unsigned, bool); void configChanged(); void songChanged(MusECore::SongChangedStruct_t); void fmtButtonToggled(bool); signals: void closed(); public: BigTime(QWidget* parent); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/canvas.cpp000066400000000000000000002417271404105311000203720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: canvas.cpp,v 1.10.2.17 2009/05/03 04:14:01 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // Additions, modifications (C) Copyright 2011-2013 Tim E. Real (terminator356 on users DOT sourceforge DOT net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include "canvas.h" #include #include #include #include #include #include "gconfig.h" #include "song.h" #include "event.h" #include "icons.h" #include "../marker/marker.h" #include "part.h" #include "fastlog.h" #include "menutitleitem.h" #include "shortcuts.h" #include "helper.h" #include "globals.h" #include "app.h" // Forwards from header: #include #include #include #include #include #include #include "undo.h" #define ABS(x) ((x) < 0) ? -(x) : (x) namespace MusEGui { //--------------------------------------------------------- // Canvas //--------------------------------------------------------- Canvas::Canvas(QWidget* parent, int sx, int sy, const char* name) : View(parent, sx, sy, name) { _cursorOverrideCount = 0; canvasTools = 0; itemPopupMenu = nullptr; button = Qt::NoButton; keyState = Qt::NoModifier; _mouseGrabbed = false; canScrollLeft = true; canScrollRight = true; canScrollUp = true; canScrollDown = true; hscrollDir = HSCROLL_NONE; vscrollDir = VSCROLL_NONE; scrollTimer=nullptr; ignore_mouse_move = false; resizeDirection= MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT; supportsResizeToTheLeft = false; supportsMultipleResize = false; scrollSpeed=30; // hardcoded scroll jump drag = DRAG_OFF; _tool = PointerTool; pos[0] = MusEGlobal::song->cpos(); pos[1] = MusEGlobal::song->lpos(); pos[2] = MusEGlobal::song->rpos(); curPart = nullptr; curPartId = -1; curItem = nullptr; newCItem = nullptr; connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(setPos(int, unsigned, bool))); } Canvas::~Canvas() { // Just in case the ref count is not 0. This is our last chance to clear // our contribution to QApplication::setOverrideCursor references. showCursor(); items.clearDelete(); if(newCItem) { if(newCItem->event().empty() && newCItem->part()) // Was it a new part, with no event? delete newCItem->part(); delete newCItem; } } //--------------------------------------------------------- // lassoToRegion // static //--------------------------------------------------------- void Canvas::lassoToRegion(const QRect& r_in, QRegion& rg_out) const { const QRect mr = map(r_in); const int x = mr.x(); const int y = mr.y(); const int w = mr.width(); const int h = mr.height(); const int x_line_off = 0; const int y_line_off = 0; const int line_w = 1; const int line_h = 1; // Clear the given region. rg_out = QRegion(); // Top line. rg_out += QRect(x, y - y_line_off, w + line_w, line_h); // Right line. rg_out += QRect(x + w - x_line_off, y, line_w, h + line_h); // Bottom line. rg_out += QRect(x, y + h - y_line_off, w + line_w, line_h); // Left line. rg_out += QRect(x - x_line_off, y, line_w, h + line_h); // For testing... // const QVector rects = rg_out.rects(); // const int rg_sz = rects.size(); // fprintf(stderr, "Region rect count:%d\n", rg_sz); // int rg_r_cnt = 0; // fprintf(stderr, "Region rect count:%d\n", rg_sz); // for(int i = 0; i < rg_sz; ++i, ++rg_r_cnt) // { // const QRect& rg_r = rects.at(i); // fprintf(stderr, " #%d: x:%d y:%d w:%d h:%d\n", rg_r_cnt, rg_r.x(), rg_r.y(), rg_r.width(), rg_r.height()); // } } void Canvas::showCursor(bool show) { if(_cursorOverrideCount > 1) fprintf(stderr, "MusE Warning: _cursorOverrideCount > 1 in Canvas::showCursor(%d)\n", show); if(show) { while(_cursorOverrideCount > 0) { QApplication::restoreOverrideCursor(); _cursorOverrideCount--; } } else { _cursorOverrideCount++; QApplication::setOverrideCursor(Qt::BlankCursor); // CAUTION } } void Canvas::setMouseGrab(bool grabbed) { if(grabbed && !_mouseGrabbed) { _mouseGrabbed = true; grabMouse(); // CAUTION } else if(!grabbed && _mouseGrabbed) { releaseMouse(); _mouseGrabbed = false; } } //--------------------------------------------------------- // setPos // set one of three markers // idx - 0-cpos 1-lpos 2-rpos // flag - emit followEvent() //--------------------------------------------------------- void Canvas::setPos(int idx, unsigned val, bool adjustScrollbar) { //if (pos[idx] == val) // Seems to be some refresh problems here, pos[idx] might be val but the gui not updated. // return; // skipping this return forces update even if values match. Matching values only seem // to occur when initializing int opos = mapx(pos[idx]); int npos = mapx(val); if (adjustScrollbar && idx == 0) { switch (MusEGlobal::song->follow()) { case MusECore::Song::NO: break; case MusECore::Song::JUMP: if (npos >= width()) { int ppos = val - xorg - rmapxDev(width()/8); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } else if (npos < 0) { int ppos = val - xorg - rmapxDev(width()*3/4); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } break; case MusECore::Song::CONTINUOUS: if (npos > (width()/2)) { int ppos = pos[idx] - xorg - rmapxDev(width()/2); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } else if (npos < (width()/2)) { int ppos = pos[idx] - xorg - rmapxDev(width()/2); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } break; } } int x; int w = 1; if (opos > npos) { w += opos - npos; x = npos; } else { w += npos - opos; x = opos; } pos[idx] = val; redraw(QRect(x-1, 0, w+2, height())); } //--------------------------------------------------------- // drawMarkers //--------------------------------------------------------- void Canvas::drawMarkers(QPainter& p, const QRect& mr, const QRegion&) { const int mx = mr.x(); const int my = mr.y(); const int mw = mr.width(); const int mh = mr.height(); const int my_2 = my + mh; const ViewXCoordinate vx(mx, true); const ViewWCoordinate vw(mw, true); const ViewXCoordinate vx_2(mx + mw, true); QPen pen; pen.setCosmetic(true); MusECore::MarkerList* marker = MusEGlobal::song->marker(); pen.setColor(MusEGlobal::config.markerColor); p.setPen(pen); for (MusECore::iMarker m = marker->begin(); m != marker->end(); ++m) { const ViewXCoordinate xp(m->second.tick(), false); if (isXInRange(xp, vx, vx_2)) { const int mxp = asMapped(xp)._value; p.drawLine(mxp, my, mxp, my_2); } } } //--------------------------------------------------------- // draw //--------------------------------------------------------- void Canvas::draw(QPainter& p, const QRect& mr, const QRegion& mrg) { // printf("draw canvas %x virt %d\n", this, virt()); // For testing... // const QVector rects = mrg.rects(); // const int rg_sz = rects.size(); // fprintf(stderr, "Canvas::draw: virt:%d rect: x:%d y:%d w:%d h:%d region rect count:%d\n", // virt(), mr.x(), mr.y(), mr.width(), mr.height(), rg_sz); // int rg_r_cnt = 0; // for(int i = 0; i < rg_sz; ++i, ++rg_r_cnt) // { // const QRect& rg_r = rects.at(i); // fprintf(stderr, " #%d: x:%d y:%d w:%d h:%d\n", rg_r_cnt, rg_r.x(), rg_r.y(), rg_r.width(), rg_r.height()); // } const int mx = mr.x(); const int my = mr.y(); const int mw = mr.width(); const int mh = mr.height(); const int mx_2 = mx + mw; const int my_2 = my + mh; const ViewXCoordinate vx(mx, true); const ViewWCoordinate vw(mw, true); const ViewXCoordinate vx_2(mx + mw, true); int ux_2lim = mapxDev(mx_2); if(ux_2lim <= 0) //x2_lim = 1; ux_2lim = 0; // Force it to +1 so that all the left-most items, or items immediately to the right, // get a chance to update - this is important since for example the parts on the // part canvas want to draw a two-pixel wide border which for an item at position x=0 // actually begins at x=-1 and needs to include that small adjustment during updates... ux_2lim += rmapxDev(1); std::vector list1; std::vector list2; std::vector list4; if (virt()) { drawCanvas(p, mr, mrg); //--------------------------------------------------- // draw Canvas Items //--------------------------------------------------- // ... and we want the upper bound, not lower bound, so that items immediately // to the right can be updated. iCItem to(items.upper_bound(ux_2lim)); // For testing... // fprintf(stderr, "Canvas::draw: virt:%d x2:%d ux2_lim:%d\n", virt(), mx_2, ux_2lim); // if(to == items.end()) // fprintf(stderr, "...item not found\n"); // else // fprintf(stderr, "...item found\n"); int ii = 0; for(iCItem i = items.begin(); i != to; ++i, ++ii) { CItem* ci = i->second; // NOTE Optimization: For each item call this once now, then use cached results later via cachedHasHiddenEvents(). // Not required for now. //ci->part()->hasHiddenEvents(); // For testing... // fprintf(stderr, "...item:%d bbox x:%d y:%d w:%d h:%d\n", ii, ci->bbox().x(), ci->bbox().y(), ci->bbox().width(), ci->bbox().height()); // Draw items from other parts behind all others. // Only for items with events (not arranger parts). if(!ci->event().empty() && ci->part() != curPart) list1.push_back(ci); else if(!ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) { // Draw selected parts in front of all others. if(ci->isSelected()) list4.push_back(ci); else // Draw unselected parts. list2.push_back(ci); } } // Draw non-current part backgrounds behind all others: drawParts(p, false, mr, mrg); int i; int sz = list1.size(); for(i = 0; i != sz; ++i) drawItem(p, list1[i], mr, mrg); // Draw current part background in front of all others: drawParts(p, true, mr, mrg); sz = list2.size(); for(i = 0; i != sz; ++i) drawItem(p, list2[i], mr, mrg); sz = list4.size(); for(i = 0; i != sz; ++i) drawItem(p, list4[i], mr, mrg); // Draw items being moved, a special way in their original location. to = moving.lower_bound(ux_2lim); for (iCItem i = moving.begin(); i != to; ++i) drawItem(p, i->second, mr, mrg); // Draw special top item for new recordings etc. drawTopItem(p,mr, mrg); // Draw special new item for first-time placement. // It is not in the item list yet. It will be added when mouse released. if(newCItem) drawItem(p, newCItem, mr, mrg); } else { p.save(); setPainter(p); drawCanvas(p, mr, mrg); p.restore(); //--------------------------------------------------- // draw Canvas Items //--------------------------------------------------- for(iCItem i = items.begin(); i != items.end(); ++i) { CItem* ci = i->second; // NOTE Optimization: For each item call this once now, then use cached results later via cachedHasHiddenEvents(). // Not required for now. //ci->part()->hasHiddenEvents(); // Draw items from other parts behind all others. // Only for items with events (not arranger parts). if(!ci->event().empty() && ci->part() != curPart) list1.push_back(ci); else if(!ci->isMoving() && (ci->event().empty() || ci->part() == curPart)) { // Draw selected parts in front of all others. if(ci->isSelected()) list4.push_back(ci); else // Draw unselected parts. list2.push_back(ci); } } // Draw non-current part backgrounds behind all others: drawParts(p, false, mr, mrg); int i; int sz = list1.size(); for(i = 0; i != sz; ++i) drawItem(p, list1[i], mr, mrg); // Draw current part background in front of all others: drawParts(p, true, mr, mrg); sz = list2.size(); for(i = 0; i != sz; ++i) drawItem(p, list2[i], mr, mrg); sz = list4.size(); for(i = 0; i != sz; ++i) drawItem(p, list4[i], mr, mrg); // Draw items being moved, a special way in their original location. for (iCItem i = moving.begin(); i != moving.end(); ++i) drawItem(p, i->second, mr, mrg); // Draw special top item for new recordings etc. drawTopItem(p, mr, mrg); // Draw special new item for first-time placement. // It is not in the item list yet. It will be added when mouse released. if(newCItem) drawItem(p, newCItem, mr, mrg); p.save(); setPainter(p); } QPen pen; pen.setCosmetic(true); //--------------------------------------------------- // draw marker //--------------------------------------------------- //p.save(); bool wmtxen = p.worldMatrixEnabled(); p.setWorldMatrixEnabled(false); drawMarkers(p, mr, mrg); //--------------------------------------------------- // draw location marker //--------------------------------------------------- pen.setColor(MusEGlobal::config.rangeMarkerColor); p.setPen(pen); int mlx; ViewXCoordinate lxp0(pos[0], false); ViewXCoordinate lxp1(pos[1], false); ViewXCoordinate lxp2(pos[2], false); if (isXInRange(lxp1, vx, vx_2)) { mlx = asMapped(lxp1)._value; p.drawLine(mlx, my, mlx, my_2); } if (isXInRange(lxp2, vx, vx_2)) { mlx = asMapped(lxp2)._value; p.drawLine(mlx, my, mlx, my_2); } // Draw the red main position cursor last, on top of the others. pen.setColor(MusEGlobal::config.positionMarkerColor); p.setPen(pen); // For testing... // fprintf(stderr, "...location marker: pos[0]%d x:%d x2:%d\n", // pos[0], x, x2); if (isXInRange(lxp0, vx, vx_2)) { mlx = asMapped(lxp0)._value; // For testing... // fprintf(stderr, "...location marker in range. Drawing line at mx:%d my:%d mx:%d my2:%d\n", // mx, my, mx, my2); p.drawLine(mlx, my, mlx, my_2); } if(drag == DRAG_ZOOM) p.drawPixmap(mapFromGlobal(global_start), zoomAtIconSVG->pixmap(QSize(MusEGlobal::config.cursorSize, MusEGlobal::config.cursorSize))); //p.restore(); //p.setWorldMatrixEnabled(true); p.setWorldMatrixEnabled(wmtxen); //--------------------------------------------------- // draw lasso //--------------------------------------------------- if (drag == DRAG_LASSO) { p.setWorldMatrixEnabled(false); pen.setColor(Qt::blue); p.setPen(pen); p.setBrush(Qt::NoBrush); QRect _r(map(lasso)); p.drawRect(_r); p.setWorldMatrixEnabled(wmtxen); } //--------------------------------------------------- // draw outlines of potential drop places of moving items //--------------------------------------------------- if(virt()) { for(iCItem i = moving.begin(); i != moving.end(); ++i) drawMoving(p, i->second, mr, mrg); } else { p.restore(); for(iCItem i = moving.begin(); i != moving.end(); ++i) drawMoving(p, i->second, mr, mrg); setPainter(p); } } #define HR_WHEEL_STEPSIZE 2 #define WHEEL_STEPSIZE 50 //#define WHEEL_DELTA 120 //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void Canvas::wheelEvent(QWheelEvent* ev) { // NOTE: X allows for Alt key + wheel, which changes from vertical // wheel values to horizontal values! Works in any window. int keyState = ev->modifiers(); QPoint delta = ev->pixelDelta(); // WHEEL_DELTA; int wheel_step_sz = 0; if(delta.isNull()) { delta = ev->angleDelta() / 8; if(delta.isNull()) return; delta /= 15; wheel_step_sz = WHEEL_STEPSIZE; } else { delta /= 2; wheel_step_sz = HR_WHEEL_STEPSIZE; } bool shift = keyState & Qt::ShiftModifier; bool ctrl = keyState & Qt::ControlModifier; if (ctrl) { // zoom horizontally int d = 0; if(delta.x() != 0) d = delta.x(); else if(delta.y() != 0) d = delta.y(); if(d != 0) #if QT_VERSION >= 0x050e00 emit horizontalZoom(d > 0, ev->globalPosition().toPoint()); #else emit horizontalZoom(d > 0, ev->globalPos()); #endif return; } if (shift || delta.x() != 0) { // scroll horizontally int scrolldelta = - delta.x(); if (shift) { scrolldelta = - delta.y(); } int xpixelscale = 5*MusECore::fast_log10(rmapxDev(1)); if (xpixelscale <= 0) { xpixelscale = 1; } int scrollstep = wheel_step_sz * (scrolldelta); scrollstep = scrollstep / 10; int newXpos = xpos + xpixelscale * scrollstep; if (newXpos < 0) { newXpos = 0; } emit horizontalScroll((unsigned)newXpos); } if (!shift && delta.y() != 0) { // scroll vertically int scrolldelta = delta.y(); int ypixelscale = rmapyDev(1); if (ypixelscale <= 0) ypixelscale = 1; int scrollstep = wheel_step_sz * (-scrolldelta); scrollstep = scrollstep / 2; int newYpos = ypos + ypixelscale * scrollstep; if (newYpos < 0) newYpos = 0; emit verticalScroll((unsigned)newYpos); } } void Canvas::redirectedWheelEvent(QWheelEvent* ev) { wheelEvent(ev); } //--------------------------------------------------------- // deselectAll //--------------------------------------------------------- void Canvas::deselectAll() { for (iCItem i = items.begin(); i != items.end(); ++i) i->second->setSelected(false); } //--------------------------------------------------------- // selectItem //--------------------------------------------------------- void Canvas::selectItem(CItem* e, bool flag) { e->setSelected(flag); } //--------------------------------------------------------- // startMoving // copy selection-List to moving-List //--------------------------------------------------------- void Canvas::startMoving(const QPoint& pos, int dir, DragType, bool rasterize) { for (iCItem i = items.begin(); i != items.end(); ++i) { if (i->second->isSelected()) { i->second->setMoving(true); // Give the moving point an initial value. i->second->setMp(i->second->pos()); moving.add(i->second); } } moveItems(pos, dir, rasterize); } //--------------------------------------------------------- // moveItems // dir = 0 move in all directions // 1 move only horizontal // 2 move only vertical //--------------------------------------------------------- void Canvas::moveItems(const QPoint& pos, int dir, bool rasterize) { int dp = y2pitch(pos.y()) - y2pitch(start.y()); int dx = pos.x() - start.x(); if (dir == 1) dp = 0; else if (dir == 2) dx = 0; QPoint cur_item_mp, mp, cur_item_old_mp, old_mp; CItem* item; int x, y, nx, ny; // Inform the classes that an item is about to be moved. // // Simply for consistency with the code below, inform of the current item first. if(curItem) { x = curItem->pos().x(); y = curItem->pos().y(); nx = x + dx; ny = pitch2y(y2pitch(y) + dp); if(rasterize) cur_item_mp = raster(QPoint(nx, ny)); else cur_item_mp = QPoint(nx, ny); cur_item_old_mp = curItem->mp(); if (cur_item_old_mp != cur_item_mp) { itemMoving(curItem, cur_item_mp); } } // Now inform of all the other items except the current item. for (iCItem i = moving.begin(); i != moving.end(); ++i) { item = i->second; if(item == curItem) continue; x = item->pos().x(); y = item->pos().y(); nx = x + dx; ny = pitch2y(y2pitch(y) + dp); if(rasterize) mp = raster(QPoint(nx, ny)); else mp = QPoint(nx, ny); old_mp = i->second->mp(); if (old_mp != mp) { itemMoving(i->second, mp); } } // Move the current item first since other item movements (sounds) // may depend on it being already moved (chords). if(curItem) { x = curItem->pos().x(); y = curItem->pos().y(); nx = x + dx; ny = pitch2y(y2pitch(y) + dp); if(rasterize) mp = raster(QPoint(nx, ny)); else mp = QPoint(nx, ny); old_mp = curItem->mp(); if (old_mp != mp) { curItem->setMp(mp); itemMoved(curItem, old_mp); } } // Now move all the other items except the current item. for (iCItem i = moving.begin(); i != moving.end(); ++i) { item = i->second; if(item == curItem) continue; int x = item->pos().x(); int y = item->pos().y(); int nx = x + dx; int ny; ny = pitch2y(y2pitch(y) + dp); if(rasterize) mp = raster(QPoint(nx, ny)); else mp = QPoint(nx, ny); old_mp = i->second->mp(); if (old_mp != mp) { i->second->setMp(mp); itemMoved(i->second, old_mp); } } redraw(); } //--------------------------------------------------------- // viewKeyPressEvent //--------------------------------------------------------- void Canvas::viewKeyPressEvent(QKeyEvent* event) { keyPress(event); } //--------------------------------------------------------- // viewKeyReleaseEvent //--------------------------------------------------------- void Canvas::viewKeyReleaseEvent(QKeyEvent* event) { keyRelease(event); } //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- void Canvas::viewMousePressEvent(QMouseEvent* event) { if (!mousePress(event)) { cancelMouseOps(); return; } keyState = event->modifiers(); button = event->button(); //printf("viewMousePressEvent buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button()); // special events if right button is clicked while operations // like moving or drawing lasso is performed. if (event->buttons() & Qt::RightButton & ~(button)) { //printf("viewMousePressEvent special buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button()); switch (drag) { case DRAG_LASSO: drag = DRAG_OFF; setCursor(); setMouseGrab(false); redraw(); return; case DRAG_MOVE: drag = DRAG_OFF; setCursor(); setMouseGrab(false); endMoveItems (start, MOVE_MOVE, 0); return; default: break; } } // ignore event if (another) button is already active: if (event->buttons() ^ button) { //printf("viewMousePressEvent ignoring buttons:%x mods:%x button:%x\n", (int)event->buttons(), (int)keyState, event->button()); // Do nothing, even if the mouse is grabbed or we have a moving list. return; } // Cancel all previous mouse ops. Right now there should be no moving list and drag should be off etc. // If there is, it's an error. It likely means we missed a mouseRelease event. cancelMouseOps(); bool alt = keyState & Qt::AltModifier; bool ctrl = keyState & Qt::ControlModifier; bool shift = keyState & Qt::ShiftModifier; start = event->pos(); ev_pos = start; global_start = event->globalPos(); ev_global_pos = global_start; curItem = findCurrentItem(start); if (curItem && (button == Qt::MidButton)) { if (_tool == PointerTool || _tool == PencilTool || _tool == RubberTool) { deleteItem(start); // changed from "start drag" to "delete" by flo93 drag = DRAG_DELETE; setCursor(); } } else if (button == Qt::RightButton) { if (curItem) { if (ctrl && virt() && (_tool == PointerTool || _tool == PencilTool || _tool == RubberTool)) { // Non-virt width is meaningless, such as drums. drag = DRAG_OFF; setCursor(); int dx = start.x() - curItem->x(); curItem->setWidth(dx); start.setX(curItem->x()); deselectAll(); selectItem(curItem, true); itemSelectionsChanged(nullptr, true); resizeItem(curItem, shift, false); redraw(); } else { itemPopupMenu = genItemPopup(curItem); if (itemPopupMenu) { QAction *act = itemPopupMenu->exec(QCursor::pos()); if (act && act->data().isValid()) itemPopup(curItem, act->data().toInt(), start); delete itemPopupMenu; } } } else { canvasPopupMenu = genCanvasPopup(); if (canvasPopupMenu) { QAction *act = canvasPopupMenu->exec(QCursor::pos(), nullptr); if (act) canvasPopup(act->data().toInt()); delete canvasPopupMenu; } } } else if (button == Qt::LeftButton) { switch (_tool) { case PointerTool: if (curItem) { itemPressed(curItem); // Alt alone is usually reserved for moving a window in X11. Ignore shift + alt. if (ctrl && !alt) drag = DRAG_COPY_START; else if (ctrl && alt) drag = DRAG_CLONE_START; else if (!ctrl && !alt) drag = DRAG_MOVE_START; } else drag = DRAG_LASSO_START; setCursor(); setMouseGrab(true); // CAUTION break; case RubberTool: deleteItem(start); drag = DRAG_DELETE; setCursor(); break; case PencilTool: { bool deselect_all = false; if (curItem) { if(!virt()) { // Non-virt width is meaningless, such as drums. itemPressed(curItem); // Alt alone is usually reserved for moving a window in X11. Ignore shift + alt. if (ctrl && !alt) drag = DRAG_COPY_START; else if (ctrl && alt) drag = DRAG_CLONE_START; else if (!ctrl && !alt) drag = DRAG_MOVE_START; setCursor(); setMouseGrab(true); // CAUTION break; } else if (supportsMultipleResize && ctrl) { if (!shift) { //Select or deselect only the clicked item selectItem(curItem, !(curItem->isSelected())); } else { //Select or deselect all on the same pitch bool selected = !(curItem->isSelected()); for (auto &it: items) if (it.second->y() == curItem->y() ) selectItem(it.second, selected); } } else { drag = DRAG_RESIZE; resizeDirection = MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT; if(supportsResizeToTheLeft){ if(curItem->x() + (curItem->width() / 2) > ev_pos.x()){ resizeDirection = MusECore::ResizeDirection::RESIZE_TO_THE_LEFT; } } setCursor(); if (supportsMultipleResize) { if (!curItem->isSelected()) { deselectAll(); deselect_all = true; selectItem(curItem, true); } if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT) resizeSelected(start.x() - curItem->x() - curItem->width()); else resizeSelected(start.x() - curItem->x(), true); } else { if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT) { int dx = start.x() - curItem->x(); curItem->setWidth(dx); } else { int endX = curItem->x() + curItem->width(); end = QPoint(endX, curItem->y()); resizeToTheLeft(ev_pos); } } start = curItem->pos(); if (!supportsMultipleResize) { deselectAll(); deselect_all = true; selectItem(curItem, true); } } } else { drag = DRAG_NEW; setCursor(); curItem = newItem(start, keyState); if (curItem) newCItem = curItem; else { drag = DRAG_OFF; setCursor(); } deselectAll(); deselect_all = true; // selectItem() will be called in viewMouseReleaseEvent(). } itemSelectionsChanged(NULL, deselect_all); redraw(); } break; case PanTool: { drag = DRAG_PAN; setCursor(); if(MusEGlobal::config.borderlessMouse) { // "It is almost never necessary to grab the mouse when using Qt, as Qt grabs // and releases it sensibly. In particular, Qt grabs the mouse when a mouse // button is pressed and keeps it until the last button is released." // // Apparently not. For some reason this was necessary. When the cursor is dragged // outside the window, holding left then pressing right mouse button COMPLETELY // bypasses us, leaving the app's default right-click handler to popup, and leaving // us in a really BAD state: mouse is grabbed (and hidden) and no way out ! // // That is likely just how QWidget works, but here using global cursor overrides // it is disastrous. TESTED: Yes, that is how other controls work. Hitting another // button while the mouse has been dragged outside causes it to bypass us ! setMouseGrab(true); // CAUTION QRect r = QApplication::primaryScreen()->geometry(); ignore_mouse_move = true; // Avoid recursion. QCursor::setPos( QPoint(r.width()/2, r.height()/2) ); //ignore_mouse_move = false; } } break; case ZoomTool: { drag = DRAG_ZOOM; setCursor(); if(MusEGlobal::config.borderlessMouse) { setMouseGrab(true); // CAUTION // screenGeometry() is obsolete. Qt >= 5.6 ? use primaryScreen(). QRect r = QApplication::primaryScreen()->geometry(); ignore_mouse_move = true; // Avoid recursion. QCursor::setPos( QPoint(r.width()/2, r.height()/2) ); //ignore_mouse_move = false; } // Update the small zoom drawing area QPoint pt = mapFromGlobal(global_start); QSize cursize = zoomIconSVG->actualSize(QSize(MusEGlobal::config.cursorSize, MusEGlobal::config.cursorSize)); update(pt.x(), pt.y(), cursize.width(), cursize.height()); } break; default: break; } } } void Canvas::scrollTimerDone() { //printf("Canvas::scrollTimerDone drag:%d doScroll:%d\n", drag, doScroll); if (doScroll && drag != DRAG_OFF && drag != DRAG_ZOOM) { //printf("Canvas::scrollTimerDone drag != DRAG_OFF && doScroll\n"); int modifiers = QApplication::keyboardModifiers(); bool ctrl = modifiers & Qt::ControlModifier; bool meta = modifiers & Qt::MetaModifier; bool alt = modifiers & Qt::AltModifier; bool right_button = QApplication::mouseButtons() & Qt::RightButton; bool scrollDoResize = ((!ctrl && !right_button) || meta || alt) && virt(); // Non-virt width is meaningless, such as drums. int dx = 0; int dy = 0; bool doHMove = false; bool doVMove = false; // If lassoing, update the old lasso region. // Do it BEFORE scrolling. switch(drag) { case DRAG_LASSO: // Update the old lasso region. redraw(lassoRegion); break; default: break; } switch(hscrollDir) { case HSCROLL_RIGHT: switch(drag) { case DRAG_NEW: case DRAG_RESIZE: case DRAGX_MOVE: case DRAGX_COPY: case DRAGX_CLONE: case DRAGY_MOVE: case DRAGY_COPY: case DRAGY_CLONE: case DRAG_MOVE: case DRAG_COPY: case DRAG_CLONE: case DRAG_PAN: emit horizontalScrollNoLimit(xpos + scrollSpeed); canScrollLeft = true; dx = rmapxDev(scrollSpeed); ev_pos.setX(ev_pos.x() + dx); doHMove = true; break; default: if(canScrollRight) { int curxpos = xpos; emit horizontalScroll(xpos + scrollSpeed); if(xpos <= curxpos) canScrollRight = false; else { canScrollLeft = true; dx = rmapxDev(scrollSpeed); ev_pos.setX(ev_pos.x() + dx); doHMove = true; } } break; } break; case HSCROLL_LEFT: if(canScrollLeft) { int curxpos = xpos; emit horizontalScroll(xpos - scrollSpeed); if(xpos >= curxpos) canScrollLeft = false; else { canScrollRight = true; dx = -rmapxDev(scrollSpeed); ev_pos.setX(ev_pos.x() + dx); doHMove = true; } } break; default: break; } switch(vscrollDir) { case VSCROLL_DOWN: if(canScrollDown) { int curypos = ypos; emit verticalScroll(ypos + scrollSpeed); if(ypos <= curypos) canScrollDown = false; else { canScrollUp = true; dy = rmapyDev(scrollSpeed); ev_pos.setY(ev_pos.y() + dy); doVMove = true; } } break; case VSCROLL_UP: if(canScrollUp) { int curypos = ypos; emit verticalScroll(ypos - scrollSpeed); if(ypos >= curypos) canScrollUp = false; else { canScrollDown = true; dy = -rmapyDev(scrollSpeed); ev_pos.setY(ev_pos.y() + dy); doVMove = true; } } break; default: break; } //printf("Canvas::scrollTimerDone doHMove:%d doVMove:%d\n", doHMove, doVMove); if(!doHMove && !doVMove) { delete scrollTimer; scrollTimer=NULL; doScroll = false; return; } QPoint dist = ev_pos - start; switch(drag) { case DRAG_MOVE: case DRAG_COPY: case DRAG_CLONE: moveItems(ev_pos, 0, false); break; case DRAGX_MOVE: case DRAGX_COPY: case DRAGX_CLONE: moveItems(ev_pos, 1, false); break; case DRAGY_MOVE: case DRAGY_COPY: case DRAGY_CLONE: moveItems(ev_pos, 2, false); break; case DRAG_LASSO: // Set the new lasso rectangle and compute the new lasso region. setLasso(QRect(start.x(), start.y(), dist.x(), dist.y())); // Update the new lasso region. redraw(lassoRegion); break; case DRAG_NEW: if(newCItem) { if((doHMove && !scrollDoResize) || doVMove) { int nx = newCItem->x(); int ny = newCItem->y(); if(doHMove && !scrollDoResize) nx += dx; if(nx < 0) nx = 0; if(doVMove) ny += dy; if(ny < 0) ny = 0; const QPoint new_pos(nx, ny); itemMoving(newCItem, new_pos); newCItem->move(new_pos); const QPoint old_mp = newCItem->mp(); // Even though we only move the primary position here, // set the mp as well so note sounding logic can work easier. newCItem->setMp(newCItem->pos()); itemMoved(newCItem, old_mp); } if(scrollDoResize && doHMove) { int w = ev_pos.x() - newCItem->x(); if(w < 1) w = 1; newCItem->setWidth(w); } redraw(); } break; case DRAG_RESIZE: if (curItem && doHMove) { if (supportsMultipleResize) { if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT) resizeSelected(ev_pos.x() - curItem->x() - curItem->width()); else resizeSelected(ev_pos.x() - curItem->x(), true); } else { int w = ev_pos.x() - curItem->x(); if(w < 1) w = 1; curItem->setWidth(w); } redraw(); } break; default: break; } //printf("Canvas::scrollTimerDone starting scrollTimer: Currently active?%d\n", scrollTimer->isActive()); // Make sure to yield to other events, otherwise other events take a long time to reach us, // causing scrolling to take a painfully long time to stop. Try up to 100 ms for each yield: //qApp->processEvents(100); // FIXME: Didn't help at all. scrollTimer->setSingleShot(true); scrollTimer->start(80); // OK, setting a timeout 80 helped. } else { //printf("Canvas::scrollTimerDone !(drag != DRAG_OFF && doScroll) deleting scrollTimer\n"); delete scrollTimer; scrollTimer=nullptr; } } //--------------------------------------------------------- // viewMouseMoveEvent //--------------------------------------------------------- void Canvas::viewMouseMoveEvent(QMouseEvent* event) { if(ignore_mouse_move) { ignore_mouse_move = false; event->accept(); return; } // For testing... //fprintf(stderr, "ypos=%d yorg=%d ymag=%d event->y=%d ->gy:%d mapy(yorg)=%d rmapy0=%d yOffset=%d rmapy(yOffset()=%d\n", // ypos, yorg, ymag, event->y(), event->globalY(), mapy(yorg), rmapy(0), yOffset(), rmapy(yOffset())); // Drag not off and left mouse button not pressed? It's an error. // Meaning likely mouseRelease was not called (which CAN happen). if(drag != DRAG_OFF && !(event->buttons() & Qt::LeftButton)) { // For testing... // fprintf(stderr, "Canvas::viewMouseMoveEvent: calling cancelMouseOps()\n"); // Be sure to cancel any relative stuff. Otherwise it's left in a bad state. cancelMouseOps(); } QRect screen_rect = QApplication::primaryScreen()->geometry(); QPoint screen_center = QPoint(screen_rect.width()/2, screen_rect.height()/2); QPoint glob_dist = event->globalPos() - ev_global_pos; QPoint glob_zoom_dist = MusEGlobal::config.borderlessMouse ? (event->globalPos() - screen_center) : glob_dist; QPoint last_dist = event->pos() - ev_pos; ev_pos = event->pos(); QPoint dist = ev_pos - start; int ax = ABS(rmapx(dist.x())); int ay = ABS(rmapy(dist.y())); bool isMoving = (ax >= 2) || (ay > 2); Qt::KeyboardModifiers modifiers = event->modifiers(); bool ctrl = modifiers & Qt::ControlModifier; bool shift = modifiers & Qt::ShiftModifier; bool meta = modifiers & Qt::MetaModifier; bool alt = modifiers & Qt::AltModifier; bool right_button = event->buttons() & Qt::RightButton; // set scrolling variables: doScroll, scrollRight // No auto scroll in zoom mode or normal pan mode. if (drag != DRAG_OFF && drag != DRAG_ZOOM && (drag != DRAG_PAN || !MusEGlobal::config.borderlessMouse)) { int ex = rmapx(event->x())+mapx(0); if(ex < 15 && (canScrollLeft || drag == DRAG_PAN)) hscrollDir = (drag == DRAG_PAN ? HSCROLL_RIGHT : HSCROLL_LEFT); else if(ex > (width() - 15)) switch(drag) { case DRAG_NEW: case DRAG_RESIZE: case DRAGX_MOVE: case DRAGX_COPY: case DRAGX_CLONE: case DRAGY_MOVE: case DRAGY_COPY: case DRAGY_CLONE: case DRAG_MOVE: case DRAG_COPY: case DRAG_CLONE: case DRAG_PAN: hscrollDir = (drag == DRAG_PAN ? HSCROLL_LEFT : HSCROLL_RIGHT); break; default: if(canScrollRight) hscrollDir = (drag == DRAG_PAN ? HSCROLL_LEFT : HSCROLL_RIGHT); else hscrollDir = HSCROLL_NONE; break; } else hscrollDir = HSCROLL_NONE; int ey = rmapy(event->y())+mapy(0); if(ey < 15 && (canScrollUp || drag == DRAG_PAN)) vscrollDir = (drag == DRAG_PAN ? VSCROLL_DOWN : VSCROLL_UP); else if(ey > (height() - 15) && (canScrollDown || drag == DRAG_PAN)) vscrollDir = (drag == DRAG_PAN ? VSCROLL_UP : VSCROLL_DOWN); else vscrollDir = VSCROLL_NONE; if(hscrollDir != HSCROLL_NONE || vscrollDir != VSCROLL_NONE) { doScroll=true; if (!scrollTimer) { scrollTimer= new QTimer(this); connect( scrollTimer, SIGNAL(timeout()), SLOT(scrollTimerDone()) ); scrollTimer->setSingleShot(true); // single-shot timer scrollTimer->start(0); } } else doScroll=false; } else { doScroll=false; canScrollLeft = true; canScrollRight = true; canScrollUp = true; canScrollDown = true; } switch (drag) { case DRAG_LASSO_START: if (!isMoving) break; drag = DRAG_LASSO; setCursor(); // proceed with DRAG_LASSO: // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case DRAG_LASSO: { // Update the old lasso region. redraw(lassoRegion); // Set the new lasso rectangle and compute the new lasso region. setLasso(QRect(start.x(), start.y(), dist.x(), dist.y())); // printf("xorg=%d xmag=%d event->x=%d, mapx(xorg)=%d rmapx0=%d xOffset=%d rmapx(xOffset()=%d\n", // xorg, xmag, event->x(),mapx(xorg), rmapx(0), xOffset(),rmapx(xOffset())); // Update the new lasso region. redraw(lassoRegion); } break; case DRAG_MOVE_START: case DRAG_COPY_START: case DRAG_CLONE_START: { if (!isMoving) break; int dir = 0; if (keyState & Qt::ShiftModifier) { if (ax > ay) { if (drag == DRAG_MOVE_START) drag = DRAGX_MOVE; else if (drag == DRAG_COPY_START) drag = DRAGX_COPY; else drag = DRAGX_CLONE; dir = 1; } else { if (drag == DRAG_MOVE_START) drag = DRAGY_MOVE; else if (drag == DRAG_COPY_START) drag = DRAGY_COPY; else drag = DRAGY_CLONE; dir = 2; } } else { if (drag == DRAG_MOVE_START) drag = DRAG_MOVE; else if (drag == DRAG_COPY_START) drag = DRAG_COPY; else drag = DRAG_CLONE; } setCursor(); if (curItem && !curItem->isSelected()) { if (drag == DRAG_MOVE) deselectAll(); selectItem(curItem, true); itemSelectionsChanged(nullptr, drag == DRAG_MOVE); redraw(); } DragType dt; if (drag == DRAG_MOVE) dt = MOVE_MOVE; else if (drag == DRAG_COPY) dt = MOVE_COPY; else dt = MOVE_CLONE; startMoving(ev_pos, dir, dt, !(keyState & Qt::ShiftModifier)); break; } case DRAG_MOVE: case DRAG_COPY: case DRAG_CLONE: if(!scrollTimer) moveItems(ev_pos, 0, !shift); break; case DRAGX_MOVE: case DRAGX_COPY: case DRAGX_CLONE: if(!scrollTimer) moveItems(ev_pos, 1, !shift); break; case DRAGY_MOVE: case DRAGY_COPY: case DRAGY_CLONE: if(!scrollTimer) moveItems(ev_pos, 2, !shift); break; case DRAG_NEW: if(newCItem) { if (last_dist.x()) { if(((ctrl || right_button) && !meta && !alt) || !virt()) // Non-virt width is meaningless, such as drums. { int nx = ev_pos.x() - newCItem->width(); // Keep the cursor at the right edge. if(nx < 0) nx = 0; if(!shift) { nx = raster(QPoint(nx, 0)).x(); // 0 is dummy, we want only x if(nx < 0) nx = 0; } newCItem->move(QPoint(nx, newCItem->y())); // Even though we only move the primary position here, // set the mp as well. newCItem->setMp(newCItem->pos()); } else { int w = ev_pos.x() - newCItem->x(); if(w < 1) w = 1; newCItem->setWidth(w); } } if (last_dist.y()) { const int x = newCItem->x(); const int y = ev_pos.y(); const int ny = pitch2y(y2pitch(y)) - yItemOffset(); const QPoint pt = QPoint(x, ny); const QPoint old_pt = newCItem->mp(); itemMoving(newCItem, pt); newCItem->move(pt); newCItem->setHeight(y2height(y)); // Even though we only move the primary position here, // set the mp as well so note sounding logic can work easier. newCItem->setMp(newCItem->pos()); itemMoved(newCItem, old_pt); } if (last_dist.x() || last_dist.y()) redraw(); } break; case DRAG_RESIZE: if (curItem && last_dist.x()) { if (supportsMultipleResize) { resizeSelected(last_dist.x(), resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT); } else { if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT) { int w = ev_pos.x() - curItem->x(); if(w < 1) w = 1; curItem->setWidth(w); } else { resizeToTheLeft(ev_pos); } } redraw(); } break; case DRAG_DELETE: deleteItem(ev_pos); break; case DRAG_PAN: { bool changed = false; if((!shift || (shift && ctrl)) && glob_zoom_dist.x() != 0 && (!doScroll || hscrollDir == HSCROLL_NONE)) // Don't interfere if auto-scrolling { emit horizontalScroll(xpos - glob_zoom_dist.x()); changed = true; } if((!ctrl || (shift && ctrl)) && glob_zoom_dist.y() != 0 && (!doScroll || vscrollDir == VSCROLL_NONE)) // Don't interfere if auto-scrolling { emit verticalScroll(ypos - glob_zoom_dist.y()); changed = true; } if(MusEGlobal::config.borderlessMouse && changed) { ignore_mouse_move = true; // Avoid recursion. QCursor::setPos(screen_center); //ignore_mouse_move = false; } } break; case DRAG_ZOOM: if(glob_zoom_dist.x() != 0) emit horizontalZoom(glob_zoom_dist.x(), global_start); //if(glob_zoom_dist.y() != 0) // emit verticalZoom(glob_zoom_dist.y(), global_start); // TODO if(MusEGlobal::config.borderlessMouse && (glob_zoom_dist.x() != 0 || glob_zoom_dist.y() != 0)) { ignore_mouse_move = true; // Avoid recursion. QCursor::setPos(screen_center); //ignore_mouse_move = false; } break; case DRAG_OFF: if(_tool == PencilTool){ if(findCurrentItem(ev_pos)){ setMouseOverItemCursor(); break; } } else if(_tool == AutomationTool || _tool == StretchTool || _tool == SamplerateTool){ // The PartCanvas and WaveCanvas mouseMove will take care of its own cursor. // Break otherwise there is bad flickering as the 'pointing hand' competes with 'cross' etc. break; } setCursor(); break; } ev_global_pos = event->globalPos(); if(drag != DRAG_ZOOM && (drag != DRAG_PAN || !MusEGlobal::config.borderlessMouse)) mouseMove(event); } //--------------------------------------------------------- // viewMouseReleaseEvent //--------------------------------------------------------- void Canvas::viewMouseReleaseEvent(QMouseEvent* event) { doScroll = false; canScrollLeft = true; canScrollRight = true; canScrollUp = true; canScrollDown = true; // We want only the left mouse release events. Ignore anything else. // Do nothing, even if the mouse is grabbed or we have a moving list. if(event->button() != Qt::LeftButton) { return; } // Immediately cancel any mouse grabbing. // Because for example there are a few message boxes that may appear // in the subsequent code, and the mouse will not work in them if it // is still grabbed. setMouseGrab(false); QPoint pos = event->pos(); bool ctrl = event->modifiers() & Qt::ControlModifier; bool shift = event->modifiers() & Qt::ShiftModifier; bool alt = event->modifiers() & Qt::AltModifier; bool redrawFlag = false; switch (drag) { case DRAG_MOVE_START: case DRAG_COPY_START: case DRAG_CLONE_START: if (curItem && curItem->part() != curPart) { curPart = curItem->part(); curPartId = curPart->sn(); curPartChanged(); } if (alt || !ctrl) deselectAll(); if(curItem) { if (!shift) { //Select or deselect only the clicked item selectItem(curItem, !(ctrl && curItem->isSelected())); } else { //Select or deselect all on the same pitch (e.g. same y-value) bool selectionFlag = !(ctrl && curItem->isSelected()); for (iCItem i = items.begin(); i != items.end(); ++i) if (i->second->y() == curItem->y() ) selectItem(i->second, selectionFlag); } } itemSelectionsChanged(nullptr, !ctrl); redrawFlag = true; if(curItem) itemReleased(curItem, curItem->pos()); itemsReleased(); break; case DRAG_COPY: endMoveItems(pos, MOVE_COPY, 0, !shift); break; case DRAGX_COPY: endMoveItems(pos, MOVE_COPY, 1, !shift); break; case DRAGY_COPY: endMoveItems(pos, MOVE_COPY, 2, !shift); break; case DRAG_MOVE: endMoveItems(pos, MOVE_MOVE, 0, !shift); break; case DRAGX_MOVE: endMoveItems(pos, MOVE_MOVE, 1, !shift); break; case DRAGY_MOVE: endMoveItems(pos, MOVE_MOVE, 2, !shift); break; case DRAG_CLONE: endMoveItems(pos, MOVE_CLONE, 0, !shift); break; case DRAGX_CLONE: endMoveItems(pos, MOVE_CLONE, 1, !shift); break; case DRAGY_CLONE: endMoveItems(pos, MOVE_CLONE, 2, !shift); break; case DRAG_OFF: break; case DRAG_RESIZE: if (curItem) { if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT) { if (!supportsMultipleResize) { QPoint rpos = QPoint(!shift ? raster(pos).x() : pos.x(), curItem->y()); resizeToTheLeft(rpos); } } resizeItem(curItem, shift, ctrl); itemSelectionsChanged(); redraw(); resizeDirection = MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT; // reset to default state or ctrl+rightclick resize will cease to work } break; case DRAG_NEW: if(newCItem) { items.add(newCItem); curItem = newCItem; newCItem = nullptr; itemReleased(curItem, curItem->pos()); itemsReleased(); newItem(curItem, shift); redrawFlag = true; } break; case DRAG_LASSO_START: // Set the new lasso rectangle and compute the new lasso region. setLasso(QRect(start.x(), start.y(), rmapxDev(1), rmapyDev(1))); //fallthrough case DRAG_LASSO: if (!ctrl) deselectAll(); // Set the new lasso rectangle and compute the new lasso region. selectLasso(ctrl); itemSelectionsChanged(nullptr, !ctrl); redrawFlag = true; break; case DRAG_DELETE: break; case DRAG_PAN: if(MusEGlobal::config.borderlessMouse) { pos = global_start; ignore_mouse_move = true; // Avoid recursion. QCursor::setPos(global_start); //ignore_mouse_move = false; } else QWidget::setCursor(*handCursor); break; case DRAG_ZOOM: if(MusEGlobal::config.borderlessMouse) { pos = global_start; ignore_mouse_move = true; // Avoid recursion. QCursor::setPos(global_start); //ignore_mouse_move = false; } break; } //printf("Canvas::viewMouseReleaseEvent setting drag to DRAG_OFF\n"); // Just in case it was somehow forgotten: if(newCItem) { if(newCItem->event().empty() && newCItem->part()) // Was it a new part, with no event? delete newCItem->part(); delete newCItem; newCItem = nullptr; } if(drag == DRAG_ZOOM) // Update the small zoom drawing area { drag = DRAG_OFF; QPoint pt = mapFromGlobal(global_start); QSize cursize = zoomIconSVG->actualSize(QSize(MusEGlobal::config.cursorSize, MusEGlobal::config.cursorSize)); update(pt.x(), pt.y(), cursize.width(), cursize.height()); } // Cancel all previous mouse ops. Right now there should be no moving list and drag should be off etc. cancelMouseOps(); // Be sure to reset the cursor. setCursor(); if (redrawFlag) redraw(); // HACK QMouseEvent e(event->type(), pos, event->globalPos(), event->button(), event->buttons(), event->modifiers()); mouseRelease(&e); //mouseRelease(pos); } //--------------------------------------------------------- // selectLasso //--------------------------------------------------------- bool Canvas::selectLasso(bool toggle) { int n = 0; if (virt()) { for (iCItem i = items.begin(); i != items.end(); ++i) { if (i->second->intersects(lasso)) { selectItem(i->second, !(toggle && i->second->isSelected())); ++n; } } } else { for (iCItem i = items.begin(); i != items.end(); ++i) { QRect box = i->second->bbox(); int x = rmapxDev(box.x()); int y = rmapyDev(box.y()); int w = rmapxDev(box.width()); int h = rmapyDev(box.height()); QRect r(x, y, w, h); r.translate(i->second->pos().x(), i->second->pos().y()); if (r.intersects(lasso)) { selectItem(i->second, !(toggle && i->second->isSelected())); ++n; } } } return n != 0; } //--------------------------------------------------------- // getCurrentDrag // returns 0 if there is no drag operation //--------------------------------------------------------- int Canvas::getCurrentDrag() { //printf("getCurrentDrag=%d\n", drag); return drag; } //--------------------------------------------------------- // deleteItem //--------------------------------------------------------- void Canvas::deleteItem(const QPoint& p) { if (virt()) { for (iCItem i = items.begin(); i != items.end(); ++i) { if (i->second->contains(p)) { selectItem(i->second, false); if (!deleteItem(i->second)) { if (drag == DRAG_DELETE) drag = DRAG_OFF; } break; } } } else { for (iCItem i = items.begin(); i != items.end(); ++i) { QRect box = i->second->bbox(); int x = rmapxDev(box.x()); int y = rmapyDev(box.y()); int w = rmapxDev(box.width()); int h = rmapyDev(box.height()); QRect r(x, y, w, h); r.translate(i->second->pos().x(), i->second->pos().y()); if (r.contains(p)) { if (deleteItem(i->second)) { selectItem(i->second, false); } break; } } } } //--------------------------------------------------------- // setTool //--------------------------------------------------------- void Canvas::setTool(int t) { if (_tool == Tool(t)) return; _tool = Tool(t); setCursor(); MusEGlobal::muse->clearStatusBarText(); update(); } //--------------------------------------------------------- // findCurrentItem //--------------------------------------------------------- CItem *Canvas::findCurrentItem(const QPoint &cStart) { //--------------------------------------------------- // set curItem to item mouse is pointing // (if any) //--------------------------------------------------- CItem *item = nullptr; if (virt()) item = items.find(cStart); else { for (ciCItem i = items.begin(); i != items.end(); ++i) { QRect box = i->second->bbox(); int x = rmapxDev(box.x()); int y = rmapyDev(box.y()); int w = rmapxDev(box.width()); int h = rmapyDev(box.height()); QRect r(x, y, w, h); r.translate(i->second->pos().x(), i->second->pos().y()); if (r.contains(cStart)) { if(i->second->isSelected()) return i->second; else { if(!item) item = i->second; } } } } return item; } void Canvas::setLasso(const QRect& r) { lasso = normalizeQRect(r); lassoToRegion(lasso, lassoRegion); } void Canvas::resizeSelected(const int &dist, const bool left) { for (auto &it: items) { if (!it.second->isSelected()) continue; if (left) { QPoint mp(qMin(it.second->x() + it.second->width() - 2, it.second->x() + dist), it.second->y()); it.second->setTopLeft(mp); } else { it.second->setWidth(qMax(1, it.second->width() + dist)); } } } void Canvas::resizeToTheLeft(const QPoint &pos) { int newX = pos.x(); if(end.x() - newX < 1) newX = end.x() - 1; int dx = end.x() - newX; curItem->setWidth(dx); QPoint mp(newX, curItem->y()); curItem->move(mp); //fprintf(stderr, "newX=%d, dx=%d\n", newX, dx); } void Canvas::setCursor() { showCursor(); switch (drag) { case DRAGX_MOVE: case DRAGX_COPY: case DRAGX_CLONE: QWidget::setCursor(QCursor(Qt::SizeHorCursor)); break; case DRAGY_MOVE: case DRAGY_COPY: case DRAGY_CLONE: QWidget::setCursor(QCursor(Qt::SizeVerCursor)); break; case DRAG_MOVE: case DRAG_COPY: case DRAG_CLONE: // Bug in KDE cursor theme? On some distros this cursor is actually another version of a closed hand! From 'net: // "It might be a problem in the distribution as Qt uses the cursor that is provided by X.org/xcursor extension with name "size_all". // We fixed this issue by setting the KDE cursor theme to "System theme" " QWidget::setCursor(QCursor(Qt::SizeAllCursor)); break; case DRAG_RESIZE: QWidget::setCursor(QCursor(Qt::SizeHorCursor)); break; case DRAG_PAN: if(MusEGlobal::config.borderlessMouse) showCursor(false); // CAUTION else QWidget::setCursor(*closedHandCursor); break; case DRAG_ZOOM: if(MusEGlobal::config.borderlessMouse) showCursor(false); // CAUTION break; case DRAG_DELETE: case DRAG_COPY_START: case DRAG_CLONE_START: case DRAG_MOVE_START: case DRAG_NEW: case DRAG_LASSO_START: case DRAG_LASSO: case DRAG_OFF: switch(_tool) { case PencilTool: QWidget::setCursor(*pencilCursor); break; case RubberTool: QWidget::setCursor(*deleteCursor); break; case GlueTool: QWidget::setCursor(*glueCursor); break; case CutTool: QWidget::setCursor(*cutterCursor); break; case MuteTool: QWidget::setCursor(*mutePartsCursor); break; case AutomationTool: QWidget::setCursor(*drawCursor); break; case DrawTool: // set for prcanvas/dcanvas as they inherit this w/o redefinition QWidget::setCursor(QCursor(Qt::ForbiddenCursor)); break; case PanTool: QWidget::setCursor(*handCursor); break; case ZoomTool: QWidget::setCursor(*zoomCursor); break; default: QWidget::setCursor(QCursor(Qt::ArrowCursor)); break; } break; } } //--------------------------------------------------------- // setMouseOverItemCursor //--------------------------------------------------------- void Canvas::setMouseOverItemCursor() { //showCursor(); QWidget::setCursor(QCursor(Qt::SizeHorCursor)); } //--------------------------------------------------------- // keyPress //--------------------------------------------------------- void Canvas::keyPress(QKeyEvent* event) { event->ignore(); } //--------------------------------------------------------- // keyRelease //--------------------------------------------------------- void Canvas::keyRelease(QKeyEvent* event) { event->ignore(); } //--------------------------------------------------------- // isSingleSelection //--------------------------------------------------------- bool Canvas::isSingleSelection() const { return selectionSize() == 1; } //--------------------------------------------------------- // itemsAreSelected //--------------------------------------------------------- bool Canvas::itemsAreSelected() const { for (ciCItem i = items.begin(); i != items.end(); ++i) { if (i->second->isSelected()) return true; } return false; } //--------------------------------------------------------- // selectionSize //--------------------------------------------------------- int Canvas::selectionSize() const { int n = 0; for (ciCItem i = items.begin(); i != items.end(); ++i) { if (i->second->isSelected()) ++n; } return n; } //--------------------------------------------------------- // tagItems //--------------------------------------------------------- void Canvas::tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const { const bool tagSelected = options._flags & MusECore::TagSelected; const bool tagMoving = options._flags & MusECore::TagMoving; const bool tagAllItems = options._flags & MusECore::TagAllItems; const bool tagAllParts = options._flags & MusECore::TagAllParts; const bool range = options._flags & MusECore::TagRange; const MusECore::Pos& p0 = options._p0; const MusECore::Pos& p1 = options._p1; CItem* item; if(range) { for(ciCItem i = items.begin(); i != items.end(); ++i) { item = i->second; if(!tagAllParts && item->part() != curPart) continue; if((tagAllItems || (tagSelected && item->isSelected()) || (tagMoving && item->isMoving())) && item->isObjectInRange(p0, p1)) { tag_list->add(item->part(), item->event()); } } } else { for(ciCItem i = items.begin(); i != items.end(); ++i) { item = i->second; if(!tagAllParts && item->part() != curPart) continue; if(tagAllItems || (tagSelected && item->isSelected()) || (tagMoving && item->isMoving())) { tag_list->add(item->part(), item->event()); } } } } //--------------------------------------------------------- // updateItemSelections //--------------------------------------------------------- void Canvas::updateItemSelections() { bool item_selected; bool obj_selected; for (iCItem i = items.begin(); i != items.end(); ++i) { CItem* item = i->second; item_selected = item->isSelected(); obj_selected = item->objectIsSelected(); if (item_selected != obj_selected) { item->setSelected(obj_selected); } } redraw(); } //--------------------------------------------------------- // genCanvasPopup // Add the list of available tools to a popup menu // menu parameter can be NULL meaning create a menu here //--------------------------------------------------------- QMenu* Canvas::genCanvasPopup(QMenu* menu) { if (canvasTools == 0) return nullptr; QMenu* r_menu = menu; if(!r_menu) r_menu = new QMenu(this); QAction* act0 = nullptr; r_menu->addAction(new MenuTitleItem(tr("Tools"), r_menu)); for (unsigned i = 0; i < static_cast(EditToolBar::toolList.size()); ++i) { if ((canvasTools & (1 << i))==0) continue; QAction* act = r_menu->addAction(QIcon(**EditToolBar::toolList[i].icon), tr(EditToolBar::toolList[i].tip)); if (MusEGui::EditToolBar::toolShortcuts.contains(1 << i)) { act->setShortcut(MusEGui::shortcuts[MusEGui::EditToolBar::toolShortcuts[1 << i]].key); } // act->setData(TOOLS_ID_BASE + i); act->setCheckable(true); act->setChecked((1 << i) == _tool); if (!act0) act0 = act; } if(!menu) // Don't interefere with supplied menu's current item r_menu->setActiveAction(act0); return r_menu; } //--------------------------------------------------------- // canvasPopup //--------------------------------------------------------- void Canvas::canvasPopup(int n) { if(n >= TOOLS_ID_BASE) { n -= TOOLS_ID_BASE; int t = 1 << n; setTool(t); emit toolChanged(t); } } void Canvas::setCurrentPart(MusECore::Part* part) { curItem = nullptr; deselectAll(); curPart = part; curPartId = curPart->sn(); curPartChanged(); } //--------------------------------------------------------- // cancelMouseOps //--------------------------------------------------------- bool Canvas::cancelMouseOps() { bool changed = false; // Make sure this is done. See mousePressEvent. showCursor(); setMouseGrab(false); // Be sure to clear the moving list and especially the item moving flags! if(!moving.empty()) { for(iCItem i = moving.begin(); i != moving.end(); ++i) i->second->setMoving(false); moving.clear(); changed = true; } if(drag != DRAG_OFF) { drag = DRAG_OFF; changed = true; } redraw(); return changed; } } // namespace MusEGui muse-4.0.0/src/muse/components/canvas.h000066400000000000000000000254001404105311000200230ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: canvas.h,v 1.3.2.8 2009/02/02 21:38:01 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // Additions, modifications (C) Copyright 2011-2013 Tim E. Real (terminator356 on users DOT sourceforge DOT net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CANVAS_H__ #define __CANVAS_H__ #include "citem.h" #include "view.h" #include "tools.h" #include "type_defs.h" #include "event_tag_list.h" #include #include #include // Forward declarations: class QMenu; class QPainter; class QTimer; class QWheelEvent; class QMouseEvent; class QKeyEvent; namespace MusECore { class Undo; } namespace MusEGui { //--------------------------------------------------------- // Canvas //--------------------------------------------------------- class Canvas : public View { Q_OBJECT QTimer *scrollTimer; bool doScroll; int scrollSpeed; QPoint ev_pos; QPoint ev_global_pos; bool ignore_mouse_move; bool canScrollLeft; bool canScrollRight; bool canScrollUp; bool canScrollDown; // CItem *findCurrentItem(const QPoint &cStart); // Whether we have grabbed the mouse. bool _mouseGrabbed; // The number of times we have called QApplication::setOverrideCursor(). // This should always be one or zero, anything else is an error, but unforeseen // events might cause us to miss a decrement with QApplication::restoreOverrideCursor(). int _cursorOverrideCount; protected: enum DragMode { DRAG_OFF=0, DRAG_NEW, DRAG_MOVE_START, DRAG_MOVE, DRAG_COPY_START, DRAG_COPY, DRAG_CLONE_START, DRAG_CLONE, DRAGX_MOVE, DRAGY_MOVE, DRAGX_COPY, DRAGY_COPY, DRAGX_CLONE, DRAGY_CLONE, DRAG_DELETE, DRAG_RESIZE, DRAG_LASSO_START, DRAG_LASSO, DRAG_PAN, DRAG_ZOOM }; enum DragType { MOVE_MOVE, MOVE_COPY, MOVE_CLONE }; enum HScrollDir { HSCROLL_NONE, HSCROLL_LEFT, HSCROLL_RIGHT }; enum VScrollDir { VSCROLL_NONE, VSCROLL_UP, VSCROLL_DOWN }; enum MenuIdBase { TOOLS_ID_BASE=10000 }; CItemMap items; CItemMap moving; CItem* newCItem; CItem* curItem; MusECore::Part* curPart; int curPartId; int canvasTools; DragMode drag; QRect lasso; QRegion lassoRegion; QPoint start; QPoint end; QPoint global_start; Tool _tool; unsigned pos[3]; MusECore::ResizeDirection resizeDirection; HScrollDir hscrollDir; VScrollDir vscrollDir; int button; Qt::KeyboardModifiers keyState; QMenu* itemPopupMenu; QMenu* canvasPopupMenu; bool supportsResizeToTheLeft; bool supportsMultipleResize; void setLasso(const QRect& r); void resizeToTheLeft(const QPoint &pos); void resizeSelected(const int &dist, const bool left = false); virtual void setCursor(); virtual void setMouseOverItemCursor(); virtual void viewKeyPressEvent(QKeyEvent* event); virtual void viewKeyReleaseEvent(QKeyEvent* event); virtual void viewMousePressEvent(QMouseEvent* event); virtual void viewMouseMoveEvent(QMouseEvent*); virtual void viewMouseReleaseEvent(QMouseEvent*); virtual void draw(QPainter& p, const QRect& mr, const QRegion& mrg = QRegion()); virtual void wheelEvent(QWheelEvent* e); virtual void keyPress(QKeyEvent*); virtual void keyRelease(QKeyEvent*); virtual bool mousePress(QMouseEvent*) { return true; } virtual void mouseMove(QMouseEvent* event) = 0; // virtual void mouseRelease(const QPoint&) {} virtual void mouseRelease(QMouseEvent*) {} // Resets all mouse operations if detecting missed mouseRelease event (which DOES happen). // Returns true if reset was actually done. virtual bool cancelMouseOps(); virtual void drawParts(QPainter&, bool /*do_cur_part*/, const QRect&, const QRegion& = QRegion()) { } virtual void drawCanvas(QPainter&, const QRect&, const QRegion& = QRegion()) = 0; virtual void drawTopItem(QPainter& p, const QRect& rect, const QRegion& = QRegion()) = 0; virtual void drawMarkers(QPainter& p, const QRect& mr, const QRegion& mrg = QRegion()); virtual void drawItem(QPainter&, const CItem*, const QRect&, const QRegion& = QRegion()) = 0; virtual void drawMoving(QPainter&, const CItem*, const QRect&, const QRegion& = QRegion()) = 0; virtual bool itemSelectionsChanged(MusECore::Undo* operations = 0, bool deselectAll = false) = 0; virtual QPoint raster(const QPoint&) const = 0; virtual int y2pitch(int) const = 0; //CDW virtual int pitch2y(int) const = 0; //CDW virtual int y2height(int) const = 0; virtual int yItemOffset() const = 0; virtual CItem* newItem(const QPoint&, int state) = 0; virtual void resizeItem(CItem*, bool noSnap=false, bool ctrl=false) = 0; virtual void newItem(CItem*, bool noSnap=false) = 0; virtual bool deleteItem(CItem*) = 0; /*! \brief Virtual member Implementing class is responsible for creating a popup to be shown when the user rightclicks an item on the Canvas \param item The canvas item that is rightclicked \return A QPopupMenu* */ virtual QMenu* genItemPopup(CItem* /*item*/) { return 0; } /*! \brief Pure virtual member Implementing class is responsible for creating a popup to be shown when the user rightclicks an empty region of the canvas \return A QPopupMenu* */ QMenu* genCanvasPopup(QMenu* menu = 0); /*! \brief Virtual member This is the function called when the user has selected an option in the popupmenu generated by genItemPopup() \param item the canvas item the whole thing is about \param n Command type \param pt I think this is the position of the pointer when right mouse button was pressed */ virtual void itemPopup(CItem* /*item */, int /*n*/, const QPoint& /*pt*/) {} void canvasPopup(int); virtual void startDrag(CItem*, DragType) = 0;// {} // selection virtual void deselectAll(); virtual void selectItem(CItem* e, bool); virtual void deleteItem(const QPoint&); // moving void startMoving(const QPoint&, int dir, DragType, bool rasterize = true); void moveItems(const QPoint&, int dir = 0, bool rasterize = true); virtual void endMoveItems(const QPoint&, DragType, int dir, bool rasterize = true) = 0; // Returns true if anything was selected. virtual bool selectLasso(bool toggle); virtual void itemPressed(const CItem*) {} virtual void itemReleased(const CItem*, const QPoint&) {} virtual void itemsReleased() {} // Called to inform before an item will be moved. // When multiple items are moving, all itemMoving() are called before // all itemMoved(), rather than in pairs for each item. (That helps with // the note playing routines.) The current item will inform first before others. // The item's current moving point contains the position before moving, // while newMP contains the position it will be moved to. virtual void itemMoving(const CItem*, const QPoint& /*newMP*/) { } // Called to inform that an item has just been moved. // The item's current moving point contains the position after moving, // while oldMP contains the old position before it was moved. virtual void itemMoved(const CItem*, const QPoint& /*oldMP*/) {} virtual void curPartChanged() { emit curPartHasChanged(curPart); } // If show is true, calls QApplication::restoreOverrideCursor() until _cursorOverrideCount-- is <= 0. // If show is false, calls QApplication::setOverrideCursor with a blank cursor. void showCursor(bool show = true); // Sets or resets the _mouseGrabbed flag and grabs or releases the mouse. void setMouseGrab(bool grabbed = false); CItem *findCurrentItem(const QPoint &cStart); public slots: void setTool(int t); virtual void setPos(int, unsigned, bool adjustScrollbar); void scrollTimerDone(void); void redirectedWheelEvent(QWheelEvent*); signals: void followEvent(int); void toolChanged(int); void verticalScroll(unsigned); void horizontalScroll(unsigned); void horizontalScrollNoLimit(unsigned); void horizontalZoom(bool zoom_in, const QPoint& glob_pos); void horizontalZoom(int mag, const QPoint& glob_pos); void curPartHasChanged(MusECore::Part*); public: Canvas(QWidget* parent, int sx, int sy, const char* name = 0); virtual ~Canvas(); // Converts a lasso-style (one-pixel thick) rectangle to a // four-rectangle region union - enough to cover the four sides. // Clears the given region first. void lassoToRegion(const QRect& r_in, QRegion& rg_out) const; // Whether we have grabbed the mouse. bool mouseGrabbed() const { return _mouseGrabbed; } bool isSingleSelection() const; int selectionSize() const; bool itemsAreSelected() const; // Appends given tag list with item objects according to options. Avoids duplicate events or clone events. // Special: We 'abuse' a controller event's length, normally 0, to indicate visual item length. virtual void tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const; Tool tool() const { return _tool; } MusECore::Part* part() const { return curPart; } void setCurrentPart(MusECore::Part*); void setCanvasTools(int n) { canvasTools = n; } int getCurrentDrag(); virtual void updateItems() = 0; virtual void updateItemSelections(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/choose_sysex.cpp000066400000000000000000000056251404105311000216250ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // choose_sysex.cpp // (C) Copyright 2014 Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; 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 "choose_sysex.h" #include "minstrument.h" namespace MusECore { extern QString sysex2string(int len, unsigned char* data); } namespace MusEGui { ChooseSysexDialog::ChooseSysexDialog(QWidget* parent, MusECore::MidiInstrument* instr) : QDialog(parent) { setupUi(this); sysexList->clear(); _sysex = NULL; _instr = instr; if(_instr) { foreach(const MusECore::SysEx* s, _instr->sysex()) { if(!s) continue; QListWidgetItem* item = new QListWidgetItem(s->name); QVariant v = QVariant::fromValue((void*)s); item->setData(Qt::UserRole, v); sysexList->addItem(item); } } if(sysexList->item(0)) sysexList->item(0)->setSelected(true); connect(sysexList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(sysexChanged(QListWidgetItem*, QListWidgetItem*))); sysexChanged(sysexList->item(0), 0); } //--------------------------------------------------------- // sysexChanged //--------------------------------------------------------- void ChooseSysexDialog::sysexChanged(QListWidgetItem* sel, QListWidgetItem*) { if(!sel) { hexLabel->setText(""); commentLabel->setText(""); return; } MusECore::SysEx* sx = (MusECore::SysEx*)sel->data(Qt::UserRole).value(); if(!sx) { hexLabel->setText(""); commentLabel->setText(""); return; } hexLabel->setText(MusECore::sysex2string(sx->dataLen, sx->data)); commentLabel->setText(sx->comment); } //--------------------------------------------------------- // accept //--------------------------------------------------------- void ChooseSysexDialog::accept() { _sysex = NULL; QListWidgetItem* item = sysexList->currentItem(); if(item) _sysex = (MusECore::SysEx*)item->data(Qt::UserRole).value(); QDialog::accept(); } } // namespace MusEGui muse-4.0.0/src/muse/components/choose_sysex.h000066400000000000000000000034221404105311000212630ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // choose_sysex.h // (C) Copyright 2014 Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CHOOSE_SYSEX_H__ #define __CHOOSE_SYSEX_H__ #include "ui_choose_sysex_base.h" class QWidget; class QDialog; class QListWidgetItem; namespace MusECore { class MidiInstrument; struct SysEx; } namespace MusEGui { //--------------------------------------------------------- // ChooseSysexDialog //--------------------------------------------------------- class ChooseSysexDialog : public QDialog, public Ui::ChooseSysexBase { Q_OBJECT MusECore::MidiInstrument* _instr; MusECore::SysEx* _sysex; private slots: virtual void accept(); void sysexChanged(QListWidgetItem*, QListWidgetItem*); public: ChooseSysexDialog(QWidget* parent = NULL, MusECore::MidiInstrument* instr = NULL); MusECore::SysEx* sysex() { return _sysex; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/choose_sysex_base.ui000066400000000000000000000103121404105311000224370ustar00rootroot00000000000000 ChooseSysexBase 0 0 427 316 Dialog Hex: true 0 0 407 68 QFrame::Panel QFrame::Sunken TextLabel Comment: true 0 0 407 68 QFrame::Panel QFrame::Sunken TextLabel Qt::Horizontal 40 20 &OK true true &Cancel true okButton clicked() ChooseSysexBase accept() 333 237 213 127 cancelButton clicked() ChooseSysexBase reject() 390 237 213 127 muse-4.0.0/src/muse/components/citem.cpp000066400000000000000000000065401404105311000202100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: citem.cpp,v 1.2.2.3 2008/01/26 07:23:21 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "citem.h" #include "undo.h" #include "song.h" #include // Forwards from header: #include "part.h" #include "pos.h" namespace MusEGui { //--------------------------------------------------------- // CItem //--------------------------------------------------------- CItem::CItem() { _isSelected = false; _isMoving = false; } //--------------------------------------------------------- // BItem //--------------------------------------------------------- BItem::BItem(const QPoint&p, const QRect& r) : CItem() { _pos = p; _bbox = r; } //--------------------------------------------------------- // PItem //--------------------------------------------------------- PItem::PItem(const QPoint& p, const QRect& r) : BItem(p, r) { _part = NULL; } PItem::PItem() : BItem() { _part = NULL; } PItem::PItem(MusECore::Part* p) : BItem() { _part = p; } bool PItem::objectIsSelected() const { return _part->selected(); } //--------------------------------------------------------- // EItem //--------------------------------------------------------- EItem::EItem(const QPoint&p, const QRect& r) : PItem(p, r) { } EItem::EItem(const MusECore::Event& e, MusECore::Part* p) : PItem(p) { _event = e; } bool EItem::isObjectInRange(const MusECore::Pos& p0, const MusECore::Pos& p1) const { MusECore::Pos pos = _event.pos(); if(_part) pos += (*_part); return pos >= p0 && pos < p1; } //--------------------------------------------------------- // CItemMap //--------------------------------------------------------- CItem* CItemMap::find(const QPoint& pos) const { CItem* item = 0; for (rciCItem i = rbegin(); i != rend(); ++i) { if (i->second->contains(pos)) { if(i->second->isSelected()) return i->second; else { if(!item) item = i->second; } } } return item; } //--------------------------------------------------------- // CItemMap //--------------------------------------------------------- void CItemMap::add(CItem* item) { std::multimap >::insert(std::pair (item->bbox().x(), item)); } } // namespace MusEGui muse-4.0.0/src/muse/components/citem.h000066400000000000000000000176651404105311000176670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: citem.h,v 1.2.2.1 2006/10/04 18:45:35 spamatica Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CITEM_H__ #define __CITEM_H__ #include #include #include #include #include #include "event.h" // Forward declarations: namespace MusECore { class Part; class Pos; } namespace MusEGui { //--------------------------------------------------------- // CItem // virtuelle Basisklasse fr alle Canvas Item's //--------------------------------------------------------- class CItem { protected: bool _isSelected; bool _isMoving; public: CItem(); virtual ~CItem() {} virtual bool isObjectInRange(const MusECore::Pos&, const MusECore::Pos&) const { return false; } bool isMoving() const { return _isMoving; } void setMoving(bool f) { _isMoving = f; } bool isSelected() const { return _isSelected; } void setSelected(bool f) { _isSelected = f; } virtual bool objectIsSelected() const { return false; } virtual int width() const { return 0; } virtual void setWidth(int) { } virtual void setHeight(int) { } virtual void setMp(const QPoint&) { } virtual const QPoint mp() const { return QPoint(); } virtual int x() const { return 0; } virtual int y() const { return 0; } virtual void setY(int) { } virtual QPoint pos() const { return QPoint(); } virtual void setPos(const QPoint&) { } virtual int height() const { return 0; } virtual QRect bbox() const { return QRect(); } virtual void setBBox(const QRect&) { } virtual void move(const QPoint&) { } virtual void setTopLeft(const QPoint&) { } virtual bool contains(const QPoint&) const { return false; } virtual bool intersects(const QRect&) const { return false; } virtual MusECore::Event event() const { return MusECore::Event(); } virtual void setEvent(const MusECore::Event&) { } virtual MusECore::Part* part() const { return NULL; } virtual void setPart(MusECore::Part*) { } }; //--------------------------------------------------------- // BItem // Boxed canvas item. //--------------------------------------------------------- class BItem : public CItem { protected: QPoint moving; QRect _bbox; QPoint _pos; public: BItem(const QPoint& p, const QRect& r); BItem() { } int width() const { return _bbox.width(); } void setWidth(int l) { _bbox.setWidth(l); } void setHeight(int l) { _bbox.setHeight(l); } void setMp(const QPoint&p) { moving = p; } const QPoint mp() const { return moving; } int x() const { return _pos.x(); } int y() const { return _pos.y(); } void setY(int y) { _bbox.setY(y); } QPoint pos() const { return _pos; } void setPos(const QPoint& p) { _pos = p; } int height() const { return _bbox.height(); } QRect bbox() const { return _bbox; } void setBBox(const QRect& r) { _bbox = r; } void move(const QPoint& tl) { _bbox.moveTopLeft(tl); _pos = tl; } void setTopLeft(const QPoint &tl) { _bbox.setTopLeft(tl); _pos = tl; } bool contains(const QPoint& p) const { return _bbox.contains(p); } bool intersects(const QRect& r) const { return r.intersects(_bbox); } }; //--------------------------------------------------------- // PItem // Event canvas item with a boxed part only. //--------------------------------------------------------- class PItem : public BItem { protected: MusECore::Part* _part; public: PItem(const QPoint& p, const QRect& r); PItem(); PItem(MusECore::Part* p); virtual bool objectIsSelected() const; MusECore::Part* part() const { return _part; } void setPart(MusECore::Part* p) { _part = p; } }; //--------------------------------------------------------- // EItem // Event canvas item base class with a boxed event in a part. //--------------------------------------------------------- class EItem : public PItem { protected: MusECore::Event _event; public: EItem(const QPoint& p, const QRect& r); EItem() { } EItem(const MusECore::Event& e, MusECore::Part* p); bool isObjectInRange(const MusECore::Pos&, const MusECore::Pos&) const; bool objectIsSelected() const { return _event.selected(); } MusECore::Event event() const { return _event; } void setEvent(const MusECore::Event& e) { _event = e; } }; //--------------------------------------------------------- // CItemMap // Canvas Item map //--------------------------------------------------------- typedef std::multimap >::iterator iCItem; typedef std::multimap >::const_iterator ciCItem; typedef std::multimap >::const_reverse_iterator rciCItem; typedef std::pair iCItemRange; class CItemMap: public std::multimap > { public: void add(CItem*); CItem* find(const QPoint& pos) const; void clearDelete() { for (iCItem i = begin(); i != end(); ++i) delete i->second; clear(); } }; //--------------------------------------------------------- // CItemList // Simple list of CItem pointers. //--------------------------------------------------------- typedef std::list::iterator iCItemList; typedef std::list::const_iterator ciCItemList; class CItemList: public std::list { public: void add(CItem* item) { push_back(item); } void clearDelete() { for(ciCItemList i = begin(); i != end(); ++i) { CItem* ce = *i; if(ce) delete ce; } clear(); } iCItemList find(const CItem* item) { for(iCItemList i = begin(); i != end(); ++i) { if(*i == item) return i; } return end(); } ciCItemList cfind(const CItem* item) const { for(ciCItemList i = cbegin(); i != cend(); ++i) { if(*i == item) return i; } return cend(); } }; //--------------------------------------------------------- // CItemSet // Simple set of unique CItem pointers. //--------------------------------------------------------- typedef std::set::iterator iCItemSet; typedef std::set::const_iterator ciCItemSet; class CItemSet: public std::set { public: void add(CItem* item) { insert(item); } void clearDelete() { for(ciCItemSet i = begin(); i != end(); ++i) { CItem* ce = *i; if(ce) delete ce; } clear(); } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/combobox.cpp000066400000000000000000000163061404105311000207200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: combobox.cpp,v 1.4 2004/05/06 15:08:07 wschweer Exp $ // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "combobox.h" // Forwards from header: #include #include #include #include #include #include namespace MusEGui { //--------------------------------------------------------- // ComboBox //--------------------------------------------------------- ComboBox::ComboBox(QWidget* parent, const char* name) : QToolButton(parent) { setObjectName(name); _currentItem = nullptr; menu = new QMenu(this); connect(menu, &QMenu::triggered, [this](QAction* act) { activatedIntern(act); } ); } ComboBox::~ComboBox() { delete menu; } //--------------------------------------------------------- // findAction //--------------------------------------------------------- QAction* ComboBox::findAction(int id) const { QList l = menu->actions(); foreach(QAction* act, l) { if(act && act->data().isValid() && act->data().toInt() == id) return act; } return nullptr; } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void ComboBox::mousePressEvent(QMouseEvent* /*ev*/) { menu->exec(QCursor::pos()); } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void ComboBox::wheelEvent(QWheelEvent* ev) { QList l = menu->actions(); const int len = l.size(); if(len == 0) return; const int i = l.indexOf(_currentItem); if(i >= 0) { const QPoint pixelDelta = ev->pixelDelta(); const QPoint angleDegrees = ev->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; if (delta > 0 && i > 0) activatedIntern(l.at(i - 1)); else if (delta < 0 && i < len - 1) activatedIntern(l.at(i + 1)); } else { activatedIntern(l.at(0)); } } //--------------------------------------------------------- // activated //--------------------------------------------------------- void ComboBox::activatedIntern(QAction* act) { if(!act) return; setText(act->text()); int id = -1; if(act->data().isValid()) id = act->data().toInt(); _currentItem = act; emit activated(id); emit activated(act); } //--------------------------------------------------------- // setCurrentItem //--------------------------------------------------------- void ComboBox::setCurrentItem(int id) { QAction* act = findAction(id); _currentItem = act; if(act) setText(act->text()); else setText(QString()); } //--------------------------------------------------------- // insertItem //--------------------------------------------------------- void ComboBox::addAction(const QString& s, int id) { QAction *act = menu->addAction(s); act->setData(id); } //--------------------------------------------------------- // CompactComboBox //--------------------------------------------------------- CompactComboBox::CompactComboBox(QWidget* parent, const QIcon& icon, const char* name) : CompactToolButton(parent, icon, name) { _currentItem = nullptr; menu = new QMenu(this); connect(menu, &QMenu::triggered, [this](QAction* act) { activatedIntern(act); } ); } CompactComboBox::~CompactComboBox() { delete menu; } //--------------------------------------------------------- // findAction //--------------------------------------------------------- QAction* CompactComboBox::findAction(int id) const { QList l = menu->actions(); foreach(QAction* act, l) { if(act && act->data().isValid() && act->data().toInt() == id) return act; } return nullptr; } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void CompactComboBox::mousePressEvent(QMouseEvent* /*ev*/) { menu->exec(QCursor::pos()); } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void CompactComboBox::wheelEvent(QWheelEvent* ev) { QList l = menu->actions(); const int len = l.size(); if(len == 0) return; const int i = l.indexOf(_currentItem); if(i >= 0) { const QPoint pixelDelta = ev->pixelDelta(); const QPoint angleDegrees = ev->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; if (delta > 0 && i > 0) activatedIntern(l.at(i - 1)); else if (delta < 0 && i < len - 1) activatedIntern(l.at(i + 1)); } else { activatedIntern(l.at(0)); } } //--------------------------------------------------------- // activated //--------------------------------------------------------- void CompactComboBox::activatedIntern(QAction* act) { if(!act) return; setText(act->text()); int id = -1; if(act->data().isValid()) id = act->data().toInt(); _currentItem = act; emit activated(id); emit activated(act); } //--------------------------------------------------------- // setCurrentItem //--------------------------------------------------------- void CompactComboBox::setCurrentItem(int id) { QAction* act = findAction(id); _currentItem = act; if(act) setText(act->text()); else setText(QString()); } //--------------------------------------------------------- // insertItem //--------------------------------------------------------- void CompactComboBox::addAction(const QString& s, int id) { QAction *act = menu->addAction(s); act->setData(id); } } // namespace MusEGui muse-4.0.0/src/muse/components/combobox.h000066400000000000000000000054371404105311000203700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: combobox.h,v 1.3 2004/02/29 12:12:36 wschweer Exp $ // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __COMBOBOX_H__ #define __COMBOBOX_H__ #include #include #include #include "ttoolbutton.h" // Forward declarations: class QWidget; class QMenu; class QIcon; class QAction; class QString; class QMouseEvent; class QWheelEvent; namespace MusEGui { //--------------------------------------------------------- // ComboBox //--------------------------------------------------------- class ComboBox : public QToolButton { Q_OBJECT QAction* _currentItem; QList itemlist; QMenu* menu; virtual void mousePressEvent(QMouseEvent*); virtual void wheelEvent(QWheelEvent*); QAction* findAction(int id) const; private slots: void activatedIntern(QAction*); signals: void activated(int id); void activated(QAction*); public: ComboBox(QWidget* parent = 0, const char* name = 0); virtual ~ComboBox(); void setCurrentItem(int); void addAction(const QString& s, int id = -1); }; //--------------------------------------------------------- // CompactComboBox //--------------------------------------------------------- class CompactComboBox : public CompactToolButton { Q_OBJECT QAction* _currentItem; QList itemlist; QMenu* menu; virtual void mousePressEvent(QMouseEvent*); virtual void wheelEvent(QWheelEvent*); QAction* findAction(int id) const; private slots: void activatedIntern(QAction*); signals: void activated(int id); void activated(QAction*); public: CompactComboBox(QWidget* parent = 0, const QIcon& icon = QIcon(), const char* name = 0); virtual ~CompactComboBox(); void setCurrentItem(int); void addAction(const QString& s, int id = -1); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/comment.cpp000066400000000000000000000070311404105311000205450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: comment.cpp,v 1.2 2004/02/08 18:30:00 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "comment.h" #include "song.h" #include "track.h" #include namespace MusEGui { //--------------------------------------------------------- // Comment //--------------------------------------------------------- Comment::Comment(QWidget* parent) : QWidget(parent) { setupUi(this); } //--------------------------------------------------------- // textChanged //--------------------------------------------------------- void Comment::textChanged() { setText(textentry->toPlainText()); } //--------------------------------------------------------- // TrackComment //--------------------------------------------------------- TrackComment::TrackComment(MusECore::Track* t, QWidget* parent) : Comment(parent) { setAttribute(Qt::WA_DeleteOnClose); setWindowTitle(tr("MusE: Track Comment")); track = t; connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); textentry->setText(track->comment()); textentry->moveCursor(QTextCursor::End); connect(textentry, SIGNAL(textChanged()), SLOT(textChanged())); label1->setText(tr("Track comment")); label2->setText(track->name()); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void TrackComment::songChanged(MusECore::SongChangedStruct_t flags) { if ((flags & (SC_TRACK_INSERTED|SC_TRACK_REMOVED|SC_TRACK_MODIFIED)) == 0) return; // check if track still exists: MusECore::TrackList* tl = MusEGlobal::song->tracks(); MusECore::iTrack it; for (it = tl->begin(); it != tl->end(); ++it) { if (track == *it) break; } if (it == tl->end()) { close(); return; } label2->setText(track->name()); if (track->comment() != textentry->toPlainText()) { disconnect(textentry, SIGNAL(textChanged()), this, SLOT(textChanged())); textentry->setText(track->comment()); textentry->moveCursor(QTextCursor::End); connect(textentry, SIGNAL(textChanged()), this, SLOT(textChanged())); } } //--------------------------------------------------------- // setText //--------------------------------------------------------- void TrackComment::setText(const QString& s) { track->setComment(s); MusEGlobal::song->update(SC_TRACK_MODIFIED); } } // namespace MusEGui muse-4.0.0/src/muse/components/comment.h000066400000000000000000000037361404105311000202220ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: comment.h,v 1.2 2004/02/08 18:30:00 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __COMMENT_H__ #define __COMMENT_H__ #include "ui_commentbase.h" #include "type_defs.h" class QWidget; namespace MusECore { class Xml; class Track; } namespace MusEGui { //--------------------------------------------------------- // Comment //--------------------------------------------------------- class Comment : public QWidget, public Ui::CommentBase { Q_OBJECT private: virtual void setText(const QString& s) = 0; private slots: void textChanged(); public: Comment(QWidget* parent); }; //--------------------------------------------------------- // TrackComment //--------------------------------------------------------- class TrackComment : public Comment { Q_OBJECT MusECore::Track* track; private: virtual void setText(const QString& s); private slots: void songChanged(MusECore::SongChangedStruct_t); public: TrackComment(MusECore::Track*, QWidget*); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/commentbase.ui000066400000000000000000000042151404105311000212340ustar00rootroot00000000000000 CommentBase 0 0 387 205 Form1 6 11 6 0 0 0 Track Comment false 12 75 true QFrame::Box QFrame::Sunken 1 Track 1 false 12 75 true muse-4.0.0/src/muse/components/compact_knob.cpp000066400000000000000000001226231404105311000215470ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // compact_knob.cpp // Adapted from Qwt Lib: // Copyright (C) 1997 Josef Wilgen // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) completely redesigned. // (C) Copyright 2016 - 2017 Tim E. Real (terminator356 on sourceforge). New CompactKnob based on Knob. // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "background_painter.h" #include "popup_double_spinbox.h" #include "compact_knob.h" #include #include "muse_math.h" #include "mmath.h" #include "gconfig.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // For debugging output: Uncomment the fprintf section. #define DEBUG_KNOB(dev, format, args...) //fprintf(dev, format, ##args); namespace MusEGui { //--------------------------------------------------------- // The QwtKnob widget imitates look and behaviour of a volume knob on a radio. // It contains // a scale around the knob which is set up automatically or can // be configured manually (see @^QwtScaleIf@). // Automatic scrolling is enabled when the user presses a mouse // button on the scale. For a description of signals, slots and other // members, see QwtSliderBase@. //--------------------------------------------------------- //--------------------------------------------------------- // CompactKnob //--------------------------------------------------------- CompactKnob::CompactKnob(QWidget* parent, const char* name, KnobLabelPos labelPos, const QString& labelText, const QString& valPrefix, const QString& valSuffix, const QString& specialValueText, const QColor& faceColor) : SliderBase(parent, name) { if(objectName().isEmpty()) setObjectName(QStringLiteral("CompactKnob")); setMouseTracking(true); setEnabled(true); setFocusPolicy(Qt::WheelFocus); //setAutoFillBackground(false); //setAttribute(Qt::WA_NoSystemBackground); //setAttribute(Qt::WA_StaticContents); // This is absolutely required for speed! Otherwise painfully slow because of full background // filling, even when requesting small udpdates! Background is drawn by us. //setAttribute(Qt::WA_OpaquePaintEvent); setBorderlessMouse(false); setCursorHoming(false); //setPagingButtons(Qt::NoButton); setEnableValueToolTips(true); setShowValueToolTipsOnHover(true); _bkgPainter = new ItemBackgroundPainter(this); _hovered = false; _labelHovered = false; _knobHovered = false; _editor = nullptr; _editMode = false; hasScale = false; d_xMargin = 1; d_yMargin = 1; d_borderWidth = 4; d_shineWidth = 1; d_totalAngle = 270.0; d_scaleDist = 1; d_symbol = Line; d_dotWidth = 4; d_maxScaleTicks = 11; d_knobWidth = 30; // unused? _faceColSel = false; //d_faceColor = palette().color(QPalette::Window); //d_rimColor = rimColor; //if(!d_rimColor.isValid()) // d_rimColor = palette().mid().color(); d_faceColor = faceColor; if(!d_faceColor.isValid()) d_faceColor = palette().color(QPalette::Window); d_shinyColor = palette().mid().color(); d_curFaceColor = d_faceColor; d_altFaceColor = d_faceColor; d_markerColor = palette().dark().color().darker(125); l_slope = 0; l_const = 100; d_labelPos = labelPos; d_labelText = labelText; d_valPrefix = valPrefix; d_valSuffix = valSuffix; d_specialValueText = specialValueText; _hasOffMode = false; _valueDecimals = 2; _off = false; d_offText = tr("off"); _showLabel = true; _showValue = true; _style3d = true; _radius = 2; _drawChord = false; setUpdateTime(50); } // Static. QSize CompactKnob::getMinimumSizeHint(const QFontMetrics& fm, KnobLabelPos labelPos, bool showValue, bool showLabel, int xMargin, int yMargin) { // Put some kind of limit so the knobs aren't tiny at small fonts. const int minh = 17; int label_h, label_h_2, fin_label_h; label_h = (fm.height() - fm.leading() - fm.descent()) + 1; label_h_2 = 2 * label_h - 1; if(showValue && showLabel) fin_label_h = label_h_2; else fin_label_h = fm.height() + 5; switch(labelPos) { case Left: return QSize(label_h_2 + 2 * xMargin, label_h_2 + 2 * yMargin); break; case Right: return QSize(label_h_2 + 2 * xMargin, label_h_2 + 2 * yMargin); break; case Top: return QSize(label_h_2 + 2 * xMargin, label_h_2 + fin_label_h + 2 * yMargin); break; case Bottom: return QSize(label_h_2 + 2 * xMargin, label_h_2 + fin_label_h + 2 * yMargin); break; case None: break; } return QSize(minh + 2 * xMargin, minh + 2 * yMargin); } void CompactKnob::processSliderPressed(int) { // bPressed = true; update(); } void CompactKnob::processSliderReleased(int) { update(); DEBUG_KNOB(stderr, "CompactKnob::processSliderReleased trackingIsActive:%d val:%.20f valHasChanged:%d\n", trackingIsActive(), value(), valueHasChangedAtRelease()); // Changed. BUG: Was causing problems with sending changed even though it hadn't. // It should never signal a change on release UNLESS tracking is off, because otherwise the last movement // already sent the last changed signal. FIXME Even this is still flawed. If no tracking, it would likely // still signal a change upon simple press and release even though nothing changed. // if((!tracking()) || valHasChanged()) if(!trackingIsActive() && valueHasChangedAtRelease()) emit valueStateChanged(value(), isOff(), id(), d_scrollMode); } QString CompactKnob::toolTipValueText(bool inclLabel, bool inclVal) const { const double minV = minValue(ConvertNone); const double val = value(ConvertNone); const QString comp_val_text = isOff() ? d_offText : ((val <= minV && !d_specialValueText.isEmpty()) ? d_specialValueText : (d_valPrefix + locale().toString(val, 'f', _valueDecimals) + d_valSuffix)); QString txt; if(inclLabel) txt += d_labelText; if(inclLabel && inclVal) txt += QString(": "); if(inclVal) { txt += QString(""); txt += comp_val_text; txt += QString(""); } return txt; } void CompactKnob::showValueToolTip(QPoint /*p*/) { const QString txt = toolTipValueText(true, true); if(!txt.isEmpty()) { // Seems to be a small problem with ToolTip: Even if we force the font size, // if a previous tooltip was showing from another control at another font size, // it refuses to change font size. Also, if we supply the widget to showText(), // it refuses to change font size and uses the widget's font size instead. // Also, this craziness with ToolTip's self-offsetting is weird: In class CompactKnob // it is best when we supply the parent's position, while in class CompactSlider // it is best when we supply the widget's position - and it STILL isn't right! // Supplying the widget's position to CompactKnob, or parent's position to CompactSlider // actually makes the offsetting worse! if(QToolTip::font().pointSize() != 10) { QFont fnt = font(); fnt.setPointSize(10); QToolTip::setFont(fnt); QToolTip::hideText(); } QToolTip::showText(mapToGlobal(parentWidget() ? parentWidget()->pos() : pos()), txt, 0, QRect(), 3000); //QToolTip::showText(mapToGlobal(parentWidget() ? parentWidget()->pos() : pos()), txt); } } //------------------------------------------------------------ // CompactKnob::setTotalAngle // Set the total angle by which the knob can be turned // // Syntax // void CompactKnob::setTotalAngle(double angle) // // Parameters // double angle -- angle in degrees. // // Description // The default angle is 270 degrees. It is possible to specify // an angle of more than 360 degrees so that the knob can be // turned several times around its axis. //------------------------------------------------------------ void CompactKnob::setTotalAngle (double angle) { if (angle < 10.0) d_totalAngle = 10.0; else d_totalAngle = angle; d_scale.setAngleRange( -0.5 * d_totalAngle, 0.5 * d_totalAngle); } //------------------------------------------------------------ // CompactKnob::setRange // Set the range and step size of the knob // // Sets the parameters that define the shininess of the ring // surrounding the knob and then proceeds by passing the // parameters to the parent class' setRange() function. //------------------------------------------------------------ void CompactKnob::setRange(double vmin, double vmax, double vstep, int pagesize, DoubleRange::ConversionMode mode) { // divide by zero protection. probably too cautious if (! (vmin == vmax || qMax(-vmin, vmax) == 0)) { if (vmin * vmax < 0) l_slope = 80.0 / qMax(-vmin, vmax); else { l_slope = 80.0 / (vmax - vmin); l_const = 100 - l_slope * vmin; } } SliderBase::setRange(vmin, vmax, vstep, pagesize, mode); } void CompactKnob::setShowValue(bool show) { _showValue = show; resize(size()); updateGeometry(); // Required. update(); } void CompactKnob::setShowLabel(bool show) { _showLabel = show; resize(size()); updateGeometry(); // Required. update(); } void CompactKnob::setOff(bool v) { if(v && !_hasOffMode) _hasOffMode = true; if(_off == v) return; _off = v; update(); emit valueStateChanged(value(), isOff(), id(), d_scrollMode); } void CompactKnob::setHasOffMode(bool v) { _hasOffMode = v; setOff(false); } void CompactKnob::setValueState(double v, bool off, ConversionMode mode) { // Do not allow setting value from the external while mouse is pressed. if(_pressed) return; bool do_off_upd = false; bool do_val_upd = false; // Both setOff and setValue emit valueStateChanged and setValue emits valueChanged. // We will block them and emit our own here. Respect the current block state. const bool blocked = signalsBlocked(); if(!blocked) blockSignals(true); if(isOff() != off) { do_off_upd = true; setOff(off); } if(value(mode) != v) { do_val_upd = true; setValue(v, mode); } if(!blocked) blockSignals(false); if(do_off_upd || do_val_upd) update(); if(do_val_upd) emit valueChanged(value(), id()); if(do_off_upd || do_val_upd) emit valueStateChanged(value(), isOff(), id(), d_scrollMode); } //------------------------------------------------------------ //.F CompactKnob::valueChange // Notify change of value // //.u Parameters // double x -- new value // //.u Description // Sets the slider's value to the nearest multiple // of the step size. //------------------------------------------------------------ void CompactKnob::valueChange() { // Turn the control back on with any value set. // Wanted to make this flaggable, but actually we // have to in order to see any value changing, if(isOff()) setOff(false); recalcAngle(); d_newVal++; update(_knobRect); if(_showValue) update(_labelRect); // HACK // In direct mode let the inherited classes (this) call these in their valueChange() methods, // so that they may be called BEFORE valueChanged signal is emitted by the setPosition() call above. // ScrDirect mode only happens once upon press with a modifier. After that, another mode is set. // Hack: Since valueChange() is NOT called if nothing changed, in that case these are called for us by the SliderBase. if(d_scrollMode == ScrDirect) { processSliderPressed(id()); emit sliderPressed(value(), id()); } // Emits valueChanged if tracking enabled. SliderBase::valueChange(); // Emit our own combined signal. if(trackingIsActive()) emit valueStateChanged(value(), isOff(), id(), d_scrollMode); } //------------------------------------------------------------ //.F CompactKnob::getValue // Determine the value corresponding to a specified position // //.u Parameters: // const QPoint &p -- point // //.u Description: // Called by QwtSliderBase //------------------------------------------------------------ double CompactKnob::getValue(const QPoint &p) { double newValue; double oneTurn; double eqValue; double arc; double dx = double((_knobRect.x() + _knobRect.width() / 2) - p.x() ); double dy = double((_knobRect.y() + _knobRect.height() / 2) - p.y() ); arc = atan2(-dx,dy) * 180.0 / M_PI; newValue = 0.5 * (minValue() + maxValue()) + (arc + d_nTurns * 360.0) * (maxValue() - minValue()) / d_totalAngle; oneTurn = fabs(maxValue() - minValue()) * 360.0 / d_totalAngle; eqValue = value() + d_mouseOffset; if (fabs(newValue - eqValue) > 0.5 * oneTurn) { if (newValue < eqValue) newValue += oneTurn; else newValue -= oneTurn; } return newValue; } //------------------------------------------------------------ // //.F CompactKnob::moveValue // Determine the value corresponding to a specified mouse movement. // //.u Syntax //.f void CompactKnob::moveValue(const QPoint &deltaP, bool fineMode) // //.u Parameters //.p const QPoint &deltaP -- Change in position //.p bool fineMode -- Fine mode if true, coarse mode if false. // //.u Description // Called by SliderBase // Coarse mode (the normal mode) maps pixels to values depending on range and width, // such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. //------------------------------------------------------------ double CompactKnob::moveValue(const QPoint &deltaP, bool /*fineMode*/) { // FIXME: To make fine mode workable, we need a way to make the adjustments 'multi-turn'. double oneTurn; double eqValue; const QPoint new_p = _lastMousePos + deltaP; const int cx = _knobRect.x() + _knobRect.width() / 2; const int cy = _knobRect.y() + _knobRect.height() / 2; const double last_dx = double(cx - _lastMousePos.x()); const double last_dy = double(cy - _lastMousePos.y()); const double last_arc = atan2(-last_dx, last_dy) * 180.0 / M_PI; const double dx = double(cx - new_p.x()); const double dy = double(cy - new_p.y()); const double arc = atan2(-dx, dy) * 180.0 / M_PI; const double val = value(ConvertNone); // if((fineMode || borderlessMouse()) && d_scrollMode != ScrDirect) // { // const double arc_diff = arc - last_arc; // const double dval_diff = arc_diff * step(); // const double new_val = val + dval_diff; // d_valAccum = new_val; // Reset. // return d_valAccum; // } const double min = minValue(ConvertNone); const double max = maxValue(ConvertNone); const double drange = max - min; const double last_val = 0.5 * (min + max) + (last_arc + d_nTurns * 360.0) * drange / d_totalAngle; const double new_val = 0.5 * (min + max) + (arc + d_nTurns * 360.0) * drange / d_totalAngle; double dval_diff = new_val - last_val; //if(fineMode) // dval_diff /= 10.0; d_valAccum += dval_diff; DEBUG_KNOB(stderr, "CompactKnob::moveValue value:%.20f last_val:%.20f new_val:%.20f p dx:%d dy:%d drange:%.20f step:%.20f dval_diff:%.20f d_valAccum:%.20f\n", val, last_val, new_val, deltaP.x(), deltaP.y(), drange, step(), dval_diff, d_valAccum); oneTurn = fabs(drange) * 360.0 / d_totalAngle; eqValue = val + d_mouseOffset; DEBUG_KNOB(stderr, " oneTurn:%.20f eqValue:%.20f\n", oneTurn, eqValue); if(fabs(d_valAccum - eqValue) > 0.5 * oneTurn) { if (d_valAccum < eqValue) { d_valAccum += oneTurn; DEBUG_KNOB(stderr, " added one turn, new d_valAccum:%.20f\n", d_valAccum); } else { d_valAccum -= oneTurn; DEBUG_KNOB(stderr, " subtracted one turn, new d_valAccum:%.20f\n", d_valAccum); } } return d_valAccum; } //------------------------------------------------------------ //.- //.F CompactKnob::setScrollMode // Determine the scrolling mode and direction // corresponding to a specified position // //.u Parameters // const QPoint &p -- point in question // //.u Description // Called by QwtSliderBase //------------------------------------------------------------ void CompactKnob::getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction) { if(!_knobRect.contains(p)) { scrollMode = ScrNone; direction = 0; return; } // If modifier or button is held, jump directly to the position at first. // After handling it, the caller can change to SrcMouse scroll mode. else if(modifiers & Qt::ControlModifier || button == Qt::MidButton) { scrollMode = ScrDirect; direction = 0; return; } int dx, dy, r; double arc; r = _knobRect.width() / 2; dx = _knobRect.x() + r - p.x(); dy = _knobRect.y() + r - p.y(); if((dx * dx) + (dy * dy) <= (r * r)) // point is inside the knob { scrollMode = ScrMouse; direction = 0; } else // point lies outside { scrollMode = ScrTimer; arc = atan2(double(-dx), double(dy)) * 180.0 / M_PI; if ( arc < d_angle) direction = -1; else if (arc > d_angle) direction = 1; else direction = 0; } } //------------------------------------------------------------ //.F CompactKnob::rangeChange // Notify a change of the range // //.u Description // Called by QwtSliderBase //------------------------------------------------------------ void CompactKnob::rangeChange() { if (!hasUserScale()) d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); recalcAngle(); SliderBase::rangeChange(); // resize(size()); // repaint(); update(); } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void CompactKnob::resizeEvent(QResizeEvent* ev) { SliderBase::resizeEvent(ev); const QRect& r = rect(); if(_editor && _editor->isVisible()) _editor->setGeometry(r); const int spacing = 0; int x, y, sz = 1; QFontMetrics fm = fontMetrics(); int label_h, label_h_2, fin_label_h; label_h = (fm.height() - fm.leading() - fm.descent()) + 1; label_h_2 = 2 * label_h - 1; if(_showValue && _showLabel) fin_label_h = label_h_2; else fin_label_h = fm.height() + 5; switch(d_labelPos) { case Left: sz = r.height(); x = r.x() + r.width() - sz; y = r.y(); _knobRect.setRect(x, y, sz, sz); _labelRect.setRect(r.x(), r.y(), r.width() - sz - spacing, sz); break; case Right: sz = r.height(); x = r.x(); y = r.y(); _knobRect.setRect(x, y, label_h_2 + 2 * d_xMargin, label_h_2 + 2 * d_yMargin); _labelRect.setRect(r.x() + label_h_2 + 2 * d_xMargin + spacing, r.y(), r.width() - label_h_2 - 2 * d_xMargin - spacing, label_h_2 + 2 * d_yMargin); break; case Top: // TODO sz = r.width(); x = r.x(); y = r.y() + r.height() - sz; _knobRect.setRect(x, y, sz, sz); _labelRect.setRect(r.x(), r.y(), sz, r.height() - sz - spacing); break; case Bottom: sz = r.height(); x = r.x(); y = r.y(); _knobRect.setRect(r.width() / 2 - label_h_2 / 2, y, label_h_2 + 2 * d_xMargin, label_h_2 + 2 * d_yMargin); _labelRect.setRect(x, y + label_h_2 + 2 * d_yMargin + spacing, r.width(), fin_label_h + 2 * spacing); break; case None: sz = MusECore::qwtMin(r.height(), r.width()); x = r.x(); y = r.y(); _knobRect.setRect(x, y, sz, sz); break; } x = _knobRect.x() - d_scaleDist; y = _knobRect.y() - d_scaleDist; int w = sz + 2 * d_scaleDist; d_scale.setGeometry(x, y, w, ScaleDraw::Round); } void CompactKnob::showEditor() { if(_editMode) return; if(!_editor) { DEBUG_KNOB(stderr, " creating editor\n"); _editor = new PopupDoubleSpinBox(this); _editor->setFrame(false); _editor->setContentsMargins(0, 0, 0, 0); _editor->setFocusPolicy(Qt::WheelFocus); connect(_editor, SIGNAL(returnPressed()), SLOT(editorReturnPressed())); connect(_editor, SIGNAL(escapePressed()), SLOT(editorEscapePressed())); } int w = width(); //int w = _labelRect.width(); //if (w < _editor->sizeHint().width()) // w = _editor->sizeHint().width(); //_editor->setGeometry(0, 0, w, height()); _editor->setGeometry(0, _labelRect.y(), w, _labelRect.height()); DEBUG_KNOB(stderr, " x:%d y:%d w:%d h:%d\n", _editor->x(), _editor->y(), w, _editor->height()); _editor->setDecimals(_valueDecimals); _editor->setSingleStep(step()); _editor->setPrefix(valPrefix()); _editor->setSuffix(valSuffix()); _editor->setMinimum(minValue()); _editor->setMaximum(maxValue()); _editor->setValue(value()); _editor->selectAll(); _editMode = true; _editor->show(); _editor->setFocus(); } //------------------------------------------------------------ // paintEvent //------------------------------------------------------------ void CompactKnob::paintEvent(QPaintEvent*) { QPainter p(this); drawBackground(&p); p.setRenderHint(QPainter::Antialiasing, true); if(hasScale) d_scale.draw(&p, palette()); drawKnob(&p, _knobRect); if(d_labelPos != None) drawLabel(&p); d_newVal = 0; } //------------------------------------------------------------ // CompactKnob::drawBackground //------------------------------------------------------------ void CompactKnob::drawBackground(QPainter* painter) { switch(d_labelPos) { case None: case Left: case Right: // Paint a background for the whole control. _bkgPainter->drawBackground(painter, rect(), palette(), d_xMargin, d_yMargin, hasOffMode() && ! isOff() ? _labelRect : QRect(), _radius, _style3d, _style3d ? QColor() : MusEGlobal::config.sliderBackgroundColor, _style3d ? QColor() : MusEGlobal::config.sliderBackgroundColor, _style3d ? QColor() : MusEGlobal::config.sliderBackgroundColor); break; case Top: case Bottom: { // Paint a separate background for the knob. // No, let's not paint a background for the knob. But if you want it, here it is: /* //QRect kr(rect().x(), _knobRect.y(), rect().width(), _knobRect.height()); QRect kr(_knobRect.x() - 2, _knobRect.y(), _knobRect.width() + 4, _knobRect.height()); _bkgPainter->drawBackground(painter, kr, palette(), d_xMargin, d_yMargin); */ // Paint a separate background for the label. _bkgPainter->drawBackground(painter, _labelRect, palette(), d_xMargin, d_yMargin, hasOffMode() && ! isOff() ? _labelRect : QRect(), 2); } break; } } //------------------------------------------------------------ // CompactKnob::drawKnob // const QRect &r -- borders of the knob //------------------------------------------------------------ void CompactKnob::drawKnob(QPainter* p, const QRect& r) { const QPalette& pal = palette(); QRect aRect; aRect.setRect(r.x() + d_borderWidth, r.y() + d_borderWidth, r.width() - 2*d_borderWidth, r.height() - 2*d_borderWidth); int width = r.width() - 2 * d_xMargin; int height = r.height() - 2 * d_yMargin; int size = qMin(width, height); p->setRenderHint(QPainter::Antialiasing, true); if (_style3d) { // // draw the rim // QLinearGradient linearg(QPoint(r.x() + d_xMargin,r.y() + d_yMargin), QPoint(size, size)); linearg.setColorAt(1 - M_PI_4, d_faceColor.lighter(125)); linearg.setColorAt(M_PI_4, d_faceColor.darker(175)); p->setBrush(linearg); p->setPen(Qt::NoPen); p->drawEllipse(r.x() + d_xMargin,r.y() + d_yMargin ,size, size); // // draw shiny surrounding // QPen pn; pn.setCapStyle(Qt::FlatCap); pn.setColor(d_shinyColor.lighter(l_const + fabs(value() * l_slope))); pn.setWidth(d_shineWidth * 2); p->setPen(pn); p->drawArc(aRect, 0, 360 * 16); // // draw button face // QRadialGradient gradient(//aRect.x() + size/2, //aRect.y() + size/2, aRect.x(), aRect.y(), size-d_borderWidth, aRect.x() + size/2-d_borderWidth, aRect.y() + size/2-d_borderWidth); gradient.setColorAt(0, d_curFaceColor.lighter(150)); gradient.setColorAt(1, d_curFaceColor.darker(150)); p->setBrush(gradient); p->setPen(Qt::NoPen); p->drawEllipse(aRect); // // draw marker // if (_style3d) drawMarker(p, d_angle, pal.currentColorGroup() == QPalette::Disabled ? pal.color(QPalette::Disabled, QPalette::WindowText) : d_markerColor); } else { QPen pen(d_faceColor); pen.setWidth(3); p->setPen(pen); p->setBrush(MusEGlobal::config.sliderBackgroundColor); p->drawEllipse(r.x() + d_xMargin, r.y() + d_yMargin, size, size); if (_drawChord) { const int angle = static_cast(d_totalAngle); const int degsubdiv = 16; p->setBrush(d_faceColor); p->drawChord(r.x() + d_xMargin, r.y() + d_yMargin, size, size, (angle / 2 + 90) * degsubdiv, (360 - angle) * degsubdiv); } drawMarker(p, d_angle, d_faceColor); } } //------------------------------------------------------------ //.- //.F CompactKnob::drawMarker // Draw the marker at the knob's front // //.u Parameters //.p QPainter *p -- painter // double arc -- angle of the marker // const QColor &c -- marker color // //.u Syntax // void CompactKnob::drawMarker(QPainter *p) // //------------------------------------------------------------ void CompactKnob::drawMarker(QPainter *p, double arc, const QColor &c) { QPen pn; int radius; double rb,re; double rarc; rarc = arc * M_PI / 180.0; double ca = cos(rarc); double sa = - sin(rarc); radius = _knobRect.width() / 2 - d_borderWidth + d_shineWidth; if (radius < 3) radius = 3; int ym = _knobRect.y() + radius + d_borderWidth - d_shineWidth; int xm = _knobRect.x() + radius + d_borderWidth - d_shineWidth; switch (d_symbol) { case Dot: { qreal dothalf = d_dotWidth / 2.0; p->setBrush(c); p->setPen(Qt::NoPen); rb = qMax(radius - 4.0 - dothalf, 0.0); p->drawEllipse(QPointF(xm - (sa * rb), ym - (ca * rb)), dothalf, dothalf); } break; case Line: pn.setColor(c); pn.setWidth(2); p->setPen(pn); // rb = MusECore::qwtMax(double((radius - 1) / 3.0), 0.0); // re = MusECore::qwtMax(double(radius - 1), 0.0); // rb = MusECore::qwtMax(((double(radius) - 0.5) / 3.0), 0.0); re = MusECore::qwtMax(double(radius) - 0.5, 0.0); p->setRenderHint(QPainter::Antialiasing, true); p->drawLine( xm, ym, xm - int(rint(sa * re)), ym - int(rint(ca * re))); // if (!_style3d) // p->drawEllipse(QPoint(xm, ym), 1, 1); break; } } //------------------------------------------------------------ // CompactKnob::drawLabel // const QRect &r -- borders of the knob //------------------------------------------------------------ void CompactKnob::drawLabel(QPainter* painter) { painter->save(); painter->setRenderHint(QPainter::Antialiasing); const bool has_focus = hasFocus(); if (_style3d) { if (has_focus) { if (_hovered) painter->setPen(QPen(QColor(239,239,239))); else painter->setPen(QPen(Qt::white)); } else if (_hovered) painter->setPen(QPen(QColor(48,48,48))); else painter->setPen(QPen(Qt::black)); } else { if (_hovered) painter->setPen(MusEGlobal::config.knobFontColor.lighter()); else if (hasOffMode() && isOff()) painter->setPen(MusEGlobal::config.knobFontColor.darker()); else painter->setPen(MusEGlobal::config.knobFontColor); } int label_flags = 0; int value_flags = 0; switch(d_labelPos) { case Left: label_flags = Qt::AlignRight | ((_showValue && _showLabel) ? Qt::AlignTop : Qt::AlignVCenter); value_flags = Qt::AlignRight | ((_showValue && _showLabel) ? Qt::AlignBottom : Qt::AlignVCenter); break; case Right: label_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignTop : Qt::AlignVCenter); value_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignBottom : Qt::AlignVCenter); break; case Top: label_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignTop : Qt::AlignVCenter); value_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignBottom : Qt::AlignVCenter); break; case Bottom: label_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignTop : Qt::AlignVCenter); value_flags = Qt::AlignLeft | ((_showValue && _showLabel) ? Qt::AlignBottom : Qt::AlignVCenter); break; case None: return; break; } const QFontMetrics fm = fontMetrics(); const int leading = fm.leading(); const int descent = fm.descent(); const QRect label_br = fm.boundingRect(d_labelText); const int label_bw = label_br.width(); if(_showLabel) { QRect label_r = _labelRect.adjusted(3, -descent + d_yMargin + 1, -2, 0); int label_xoff = (label_r.width() - label_bw) / 2; if(label_xoff < 0) label_xoff = 0; label_r.adjust(label_xoff, 0, 0, 0); painter->drawText(label_r, label_flags, d_labelText); } if(_showValue) { const double minV = minValue(ConvertNone); const double val = value(ConvertNone); const QString val_txt = (val <= minV && !d_specialValueText.isEmpty()) ? d_specialValueText : (d_valPrefix + locale().toString(val, 'f', _valueDecimals) + d_valSuffix); const QRect val_br = fm.boundingRect(val_txt); const int val_bw = val_br.width(); QRect val_r = _labelRect.adjusted(3, -1, -2, descent + leading - d_yMargin - 2); int val_xoff = (val_r.width() - val_bw) / 2; if(val_xoff < 0) val_xoff = 0; val_r.adjust(val_xoff, 0, 0, 0); painter->drawText(val_r, value_flags, val_txt); } painter->restore(); } void CompactKnob::mouseDoubleClickEvent(QMouseEvent* e) { DEBUG_KNOB(stderr, "CompactKnob::mouseDoubleClickEvent\n"); const Qt::MouseButtons buttons = e->buttons(); const Qt::KeyboardModifiers keys = e->modifiers(); if(buttons == Qt::LeftButton && !_editMode) { DEBUG_KNOB(stderr, " left button\n"); if(keys == Qt::ControlModifier) { if(_hasOffMode) { setOff(!isOff()); // Just toggle the off state. emit valueChanged(value(), id()); e->accept(); return; } } // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. else if(keys == Qt::NoModifier && (!_editor || !_editor->hasFocus())) { showEditor(); e->accept(); return; } } e->ignore(); SliderBase::mouseDoubleClickEvent(e); } void CompactKnob::editorReturnPressed() { DEBUG_KNOB(stderr, "CompactKnob::editorReturnPressed\n"); _editMode = false; if(_editor) { if(value() != _editor->value()) setValue(_editor->value()); _editor->deleteLater(); _editor = 0; setFocus(); } } void CompactKnob::editorEscapePressed() { DEBUG_KNOB(stderr, "CompactKnob::editorEscapePressed\n"); _editMode = false; if(_editor) { _editor->deleteLater(); _editor = 0; setFocus(); } } void CompactKnob::keyPressEvent(QKeyEvent* e) { if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. if(!_editor || !_editor->hasFocus()) showEditor(); e->accept(); return; } e->ignore(); SliderBase::keyPressEvent(e); } void CompactKnob::leaveEvent(QEvent *e) { if(_hovered) { _hovered = false; update(); } if(_knobHovered) { _knobHovered = false; update(_knobRect); } if(_labelHovered) { _labelHovered = false; update(_labelRect); } e->ignore(); SliderBase::leaveEvent(e); } void CompactKnob::mouseMoveEvent(QMouseEvent *e) { e->ignore(); SliderBase::mouseMoveEvent(e); if(!_hovered) { _hovered = true; update(); } if(_knobRect.contains(e->pos()) != _knobHovered) { _knobHovered = !_knobHovered; update(_knobRect); } if(_labelRect.contains(e->pos()) != _labelHovered) { _labelHovered = !_labelHovered; update(_labelRect); } } void CompactKnob::mousePressEvent(QMouseEvent* e) { const Qt::MouseButton button = e->button(); const Qt::MouseButtons buttons = e->buttons(); // Only one mouse button at a time! Otherwise bad things happen. if(buttons ^ button) { e->ignore(); // Let ancestor handle the proper thing to do. SliderBase::mousePressEvent(e); return; } if(button == Qt::RightButton) { e->accept(); // Clear everything. setMouseGrab(false); d_scrollMode = ScrNone; d_direction = 0; _pressed = false; emit sliderRightClicked(e->globalPos(), _id); return; } e->ignore(); SliderBase::mousePressEvent(e); } // bool CompactKnob::event(QEvent* e) // { // switch(e->type()) // { // // FIXME: Doesn't work. // case QEvent::NonClientAreaMouseButtonPress: // DEBUG_KNOB(stderr, "CompactKnob::event NonClientAreaMouseButtonPress\n"); // e->accept(); // _editMode = false; // if(_editor) // { // _editor->deleteLater(); // _editor = 0; // } // return true; // break; // // default: // break; // } // // //e->ignore(); // No, this causes duplicates! For ex. skipping tab focus. // return SliderBase::event(e); // } //------------------------------------------------------------ // //.F CompactKnob::setKnobWidth // Change the knob's width. // //.u Syntax //.f void CompactKnob::setKnobWidth(int w) // //.u Parameters //.p int w -- new width // //.u Description // The specified width must be >= 5, or it will be clipped. // //------------------------------------------------------------ void CompactKnob::setKnobWidth(int w) { d_knobWidth = MusECore::qwtMax(w,5); resize(size()); // repaint(); update(); } //------------------------------------------------------------ // //.F CompactKnob::setBorderWidth // Set the knob's border width // //.u Syntax //.f void CompactKnob::setBorderWidth(int bw) // //.u Parameters //.p int bw -- new border width // //------------------------------------------------------------ void CompactKnob::setBorderWidth(int bw) { d_borderWidth = MusECore::qwtMax(bw, 0); resize(size()); // repaint(); update(); } //------------------------------------------------------------ //.- //.F CompactKnob::recalcAngle // Recalculate the marker angle corresponding to the // current value // //.u Syntax //.f void CompactKnob::recalcAngle() // //------------------------------------------------------------ void CompactKnob::recalcAngle() { d_oldAngle = d_angle; // // calculate the angle corresponding to the value // if (maxValue() == minValue()) { d_angle = 0; d_nTurns = 0; } else { d_angle = (value() - 0.5 * (minValue() + maxValue())) / (maxValue() - minValue()) * d_totalAngle; d_nTurns = floor((d_angle + 180.0) / 360.0); d_angle = d_angle - d_nTurns * 360.0; } } //------------------------------------------------------------ // setFaceColor //------------------------------------------------------------ void CompactKnob::setFaceColor(const QColor& c) { d_faceColor = c; // if(!d_faceColor.isValid()) // d_faceColor = palette().color(QPalette::Window); // d_curFaceColor = d_faceColor; if(!_faceColSel) update(); } //------------------------------------------------------------ // setAltFaceColor //------------------------------------------------------------ void CompactKnob::setAltFaceColor(const QColor& c) { d_altFaceColor = c; if(_faceColSel) update(); } //------------------------------------------------------------ // selectFaceColor //------------------------------------------------------------ void CompactKnob::selectFaceColor(bool alt) { _faceColSel = alt; if(alt) d_curFaceColor = d_altFaceColor; else d_curFaceColor = d_faceColor; update(); } //------------------------------------------------------------ // setShinyColor //------------------------------------------------------------ void CompactKnob::setShinyColor(const QColor& c) { d_shinyColor = c; update(); } //------------------------------------------------------------ // setMarkerColor //------------------------------------------------------------ void CompactKnob::setMarkerColor(const QColor& c) { d_markerColor = c; update(); } //------------------------------------------------------------ // setActiveColor //------------------------------------------------------------ void CompactKnob::setActiveColor(const QColor& c) { d_activeColor = c; update(); } //------------------------------------------------------------ // //.F CompactKnob::setMargins // Set distances between the widget's border and // internals. // //.u Syntax //.f void CompactKnob::setMargins(int hor, int vert) // //.u Parameters //.p int hor, int vert -- Margins // //------------------------------------------------------------ void CompactKnob::setMargins(int hor, int vert) { d_xMargin = MusECore::qwtMax(0, hor); d_yMargin = MusECore::qwtMax(0, vert); resize(this->size()); } void CompactKnob::setMargins(QSize s) { setMargins(s.width(), s.height()); } void CompactKnob::setXMargin(int x) { setMargins(x, d_yMargin); } void CompactKnob::setYMargin(int y) { setMargins(d_xMargin, y); } //------------------------------------------------------------ // //.F CompactKnob::sizeHint // Return a recommended size // //.u Syntax //.f QSize CompactKnob::sizeHint() const // //.u Note // The return value of sizeHint() depends on the font and the // scale. //------------------------------------------------------------ QSize CompactKnob::sizeHint() const { QSize sz = getMinimumSizeHint(fontMetrics(), d_labelPos, _showValue, _showLabel, d_xMargin, d_yMargin); DEBUG_KNOB(stderr, "CompactKnob::sizeHint w:%d h:%d\n", sz.width(), sz.height()); return sz; } } // namespace MusEGui muse-4.0.0/src/muse/components/compact_knob.h000066400000000000000000000254041404105311000212130ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // compact_knob.h // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) completely redesigned. // (C) Copyright 2016 - 2017 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __COMPACT_KNOB_H__ #define __COMPACT_KNOB_H__ #include "sliderbase.h" #include "sclif.h" #include #include #include #include #include class QFontMetrics; class QResizeEvent; class QPainter; class QEvent; class QPaintEvent; class QMouseEvent; class QKeyEvent; namespace MusEGui { class PopupDoubleSpinBox; class ItemBackgroundPainter; //--------------------------------------------------------- // CompactKnob //--------------------------------------------------------- class CompactKnob : public SliderBase, public ScaleIf { Q_OBJECT public: enum KnobLabelPos { None, Left, Right, Top, Bottom }; enum Symbol { Line, Dot }; Q_ENUM(Symbol) Q_PROPERTY( QSize margins READ margins WRITE setMargins ) Q_PROPERTY( int xMargin READ xMargin WRITE setXMargin ) Q_PROPERTY( int yMargin READ yMargin WRITE setYMargin ) Q_PROPERTY( int knobWidth READ knobWidth WRITE setKnobWidth ) Q_PROPERTY( double totalAngle READ totalAngle WRITE setTotalAngle ) Q_PROPERTY( int borderWidth READ borderWidth WRITE setBorderWidth ) Q_PROPERTY( QColor faceColor READ faceColor WRITE setFaceColor ) Q_PROPERTY( QColor altFaceColor READ altFaceColor WRITE setAltFaceColor ) Q_PROPERTY( QColor shinyColor READ shinyColor WRITE setShinyColor ) Q_PROPERTY( QColor markerColor READ markerColor WRITE setMarkerColor ) Q_PROPERTY( QColor activeColor READ activeColor WRITE setActiveColor ) Q_PROPERTY( QString labelText READ labelText WRITE setLabelText ) Q_PROPERTY( QString valPrefix READ valPrefix WRITE setValPrefix ) Q_PROPERTY( QString valSuffix READ valSuffix WRITE setValSuffix ) Q_PROPERTY( QString specialValueText READ specialValueText WRITE setSpecialValueText ) Q_PROPERTY( QString offText READ offText WRITE setOffText ) Q_PROPERTY( int valueDecimals READ valueDecimals WRITE setValueDecimals ) Q_PROPERTY( bool style3d READ style3d WRITE setStyle3d ) Q_PROPERTY( int radius READ radius WRITE setRadius ) Q_PROPERTY( Symbol symbol READ symbol WRITE setSymbol ) Q_PROPERTY( bool drawChord READ drawChord WRITE setDrawChord ) private: KnobLabelPos d_labelPos; bool _hasOffMode; QString d_labelText; QString d_valPrefix; QString d_valSuffix; QString d_specialValueText; QString d_offText; int _valueDecimals; bool _off; // Whether to display the label. bool _showLabel; // Whether to display the value. bool _showValue; bool _style3d; int _radius; bool _drawChord; PopupDoubleSpinBox* _editor; bool _editMode; ItemBackgroundPainter* _bkgPainter; private slots: void editorReturnPressed(); void editorEscapePressed(); protected: bool hasScale; // Whether the mouse is over the entire control. bool _hovered; // Whether the mouse is over the knob. bool _knobHovered; // Whether the mouse is over the label. bool _labelHovered; int d_xMargin; int d_yMargin; int d_borderWidth; int d_shineWidth; int d_scaleDist; int d_maxScaleTicks; int d_newVal; int d_knobWidth; int d_dotWidth; Symbol d_symbol; double d_angle; double d_oldAngle; double d_totalAngle; double d_nTurns; double l_const; double l_slope; QRect _labelRect; QRect _knobRect; bool _faceColSel; QColor d_faceColor; QColor d_altFaceColor; QColor d_shinyColor; QColor d_curFaceColor; QColor d_markerColor; QColor d_activeColor; void recalcAngle(); void valueChange(); void rangeChange(); void drawBackground(QPainter*); void drawKnob(QPainter* p, const QRect &r); void drawMarker(QPainter* p, double arc, const QColor &c); void drawLabel(QPainter* p); void showEditor(); virtual void resizeEvent(QResizeEvent*); virtual void paintEvent(QPaintEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual void mousePressEvent(QMouseEvent*); virtual void mouseDoubleClickEvent(QMouseEvent*); virtual void keyPressEvent(QKeyEvent*); virtual void leaveEvent(QEvent*); // virtual bool event(QEvent*); double getValue(const QPoint &p); // Determine the value corresponding to a specified mouse movement. double moveValue(const QPoint& /*deltaP*/, bool /*fineMode*/ = false); void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction ); void scaleChange() { repaint(); } void fontChange(const QFont &) { repaint(); } virtual void processSliderPressed(int); virtual void processSliderReleased(int); // Show a handy tooltip value box. virtual void showValueToolTip(QPoint); signals: // Both value and off state changed combined into one signal. // In typical automation use, this signal should be ignored in ScrDirect scroll mode. // ScrDirect mode happens only once upon press with a modifier. // In ScrDirect mode the slider sends both pressed AND changed signals // since the position jumps to the pressed location. // Note the SliderBase::valueChanged signal is also available. void valueStateChanged(double value, bool off, int id, int scrollMode); public: CompactKnob(QWidget* parent = 0, const char *name = 0, KnobLabelPos labelPos = None, const QString& labelText = QString(), const QString& valPrefix = QString(), const QString& valSuffix = QString(), const QString& specialValueText = QString(), const QColor& faceColor = QColor()); static QSize getMinimumSizeHint(const QFontMetrics& fm, //Qt::Orientation orient = Qt::Vertical, KnobLabelPos labelPos = None, bool showValue = true, bool showLabel = true, int xMargin = 0, int yMargin = 0); void setRange(double vmin, double vmax, double vstep = 0.0, int pagesize = 1, DoubleRange::ConversionMode mode = ConvertDefault); QString labelText() const { return d_labelText; }; void setLabelText(const QString& t) { d_labelText = t; update(); } QString valPrefix() const { return d_valPrefix; }; void setValPrefix(const QString& t) { d_valPrefix = t; update(); } QString valSuffix() const { return d_valSuffix; }; void setValSuffix(const QString& t) { d_valSuffix = t; update(); } QString specialValueText() const { return d_specialValueText; }; void setSpecialValueText(const QString& t) { d_specialValueText = t; update(); } QString offText() const { return d_offText; }; void setOffText(const QString& t) { d_offText = t; update(); } int valueDecimals() const { return _valueDecimals; } void setValueDecimals(int d) { if(d < 0) return; _valueDecimals = d; update(); } int knobWidth() const { return d_knobWidth; } void setKnobWidth(int w); double totalAngle() const { return d_totalAngle; } void setTotalAngle (double angle); int borderWidth() const { return d_borderWidth; } void setBorderWidth(int bw); void selectFaceColor(bool alt); bool selectedFaceColor() const { return _faceColSel; } QColor faceColor() const { return d_faceColor; } void setFaceColor(const QColor& c); QColor altFaceColor() const { return d_altFaceColor; } void setAltFaceColor(const QColor& c); QColor shinyColor() const { return d_shinyColor; } void setShinyColor(const QColor& c); QColor markerColor() const { return d_markerColor; } void setMarkerColor(const QColor& c); QColor activeColor() const { return d_activeColor; } void setActiveColor(const QColor& c); QString toolTipValueText(bool inclLabel, bool inclVal) const; bool showLabel() const { return _showLabel; } void setShowLabel(bool show); bool showValue() const { return _showValue; } void setShowValue(bool show); bool hasOffMode() const { return _hasOffMode; } void setHasOffMode(bool v); bool isOff() const { return _off; } // Sets the off state and emits valueStateChanged signal if required. void setOff(bool v); // Both value and off state changed combined into one setter. // By default it is assumed that setting a value naturally implies resetting the 'off' state to false. // Emits valueChanged and valueStateChanged signals if required. // Note setOff and SliderBase::setValue are also available. void setValueState(double v, bool off = false, ConversionMode mode = ConvertDefault); QSize margins() const { return QSize(d_xMargin, d_yMargin); } int xMargin() const { return d_xMargin; } int yMargin() const { return d_yMargin; } void setMargins(QSize); void setMargins(int x, int y); void setXMargin(int x); void setYMargin(int y); bool style3d() const { return _style3d; } void setStyle3d(const bool style3d) { _style3d = style3d; } int radius() const { return _radius; } void setRadius(const int radius) { _radius = radius; } Symbol symbol() const { return d_symbol; } void setSymbol(const Symbol s) { d_symbol = s; } bool drawChord() const { return _drawChord; } void setDrawChord(const bool draw) { _drawChord = draw; } virtual QSize sizeHint() const; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/compact_patch_edit.cpp000066400000000000000000000201141404105311000227120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // compact_patch_edit.cpp // (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "compact_patch_edit.h" #include "lcd_widgets.h" #include "elided_label.h" #include "gconfig.h" namespace MusEGui { CompactPatchEdit::CompactPatchEdit(QWidget *parent, const char *name, QColor /*fillColor*/) : QFrame(parent) { setObjectName(name); _orient = ReadoutHorizontal; _showPatchLabel = true; _maxAliasedPointSize = -1; _id = -1; _currentPatch = 0; _style3d = true; _radius = 2; _patchNameLabel = new ElidedLabel(nullptr, Qt::ElideNone); _patchNameLabel->setObjectName("CompactPatchEditLabel"); _patchNameLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); _patchNameLabel->setHasOffMode(true); _patchNameLabel->setToolTip(tr("Patch name")); _patchNameLabel->setContentsMargins(0, 0, 0, 0); // _patchNameLabel->setBgColor(MusEGlobal::config.midiInstrumentBackgroundColor); // _patchNameLabel->setBgActiveColor(MusEGlobal::config.midiInstrumentBgActiveColor); // _patchNameLabel->setBorderColor(MusEGlobal::config.midiInstrumentBorderColor); _patchEdit = new LCDPatchEdit(); QVBoxLayout* layout = new QVBoxLayout(this); layout->setSpacing(0); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(_patchNameLabel); layout->addWidget(_patchEdit); connect(_patchNameLabel, SIGNAL(pressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)), SLOT(patchNamePressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers))); connect(_patchNameLabel, SIGNAL(returnPressed(QPoint,int,Qt::KeyboardModifiers)), SLOT(patchNameReturnPressed(QPoint,int,Qt::KeyboardModifiers))); connect(_patchEdit, SIGNAL(valueChanged(int,int)), SLOT(patchEditValueChanged(int,int))); connect(_patchEdit, SIGNAL(rightClicked(QPoint,int)), SLOT(patchEditRightClicked(QPoint,int))); } CompactPatchEdit::~CompactPatchEdit() { if(_patchEdit) delete _patchEdit; } // Static. QSize CompactPatchEdit::getMinimumSizeHint(const QFontMetrics& fm, Qt::Orientation orient, int xMargin, int yMargin) { const QSize ctrl_sz = LCDPatchEdit::getMinimumSizeHint( fm, xMargin, yMargin, orient == Qt::Horizontal ? LCDPatchEdit::PatchHorizontal : LCDPatchEdit::PatchVertical ); // HACK Try to find the size of a label // QLabel* dummyLabel = new QLabel("WWW", this); // dummyLabel->setMargin(yMargin); // const QSize lbl_sz = dummyLabel->sizeHint(); const int lbl_h = fm.height() + 2 * yMargin; const int h = ctrl_sz.height() + lbl_h; switch(orient) { case Qt::Horizontal: return QSize(ctrl_sz.width(), h); // Patch edit is dominant. break; case Qt::Vertical: return QSize(16, h); break; } return QSize(10, 10); } QWidget* CompactPatchEdit::setupComponentTabbing(QWidget* previousWidget) { QWidget* prev = previousWidget; if(_patchNameLabel) { if(prev) QWidget::setTabOrder(prev, _patchNameLabel); prev = _patchNameLabel; } if(_patchEdit) { if(prev) QWidget::setTabOrder(prev, _patchEdit); prev = _patchEdit; } return prev; } // void CompactPatchEdit::keyPressEvent(QKeyEvent* e) // { // switch (e->key()) // { // case Qt::Key_Escape: // break; // // default: // break; // } // // e->ignore(); // return QFrame::keyPressEvent(e); // } void CompactPatchEdit::setReadoutColor(const QColor& c) { _patchEdit->setReadoutColor(c); // Colour was not updating with stylesheet colour. // This trick recommended by help. Tested OK. // Good demonstration of how to apply a stylesheet after a property has been changed. // if(style()) // { // style()->unpolish(_patchEdit); // style()->polish(_patchEdit); // } // update(); } void CompactPatchEdit::setBgColor(const QColor& c) { _patchNameLabel->setBgColor(c); _patchEdit->setBgColor(c); } void CompactPatchEdit::setBgActiveColor(const QColor& c) { _patchNameLabel->setBgActiveColor(c); _patchEdit->setBgActiveColor(c); } void CompactPatchEdit::setBorderColor(const QColor& c) { _patchNameLabel->setBorderColor(c); _patchEdit->setBorderColor(c); } void CompactPatchEdit::setBorderColorPatchEdit(const QColor &c) { _patchEdit->setBorderColor(c); } void CompactPatchEdit::setFontColor(const QColor& c) { _patchNameLabel->setFontColor(c); } void CompactPatchEdit::setFontActiveColor(const QColor& c) { _patchNameLabel->setFontActiveColor(c); } void CompactPatchEdit::setStyle3d(const bool s) { _style3d = s; _patchNameLabel->setStyle3d(s); _patchEdit->setStyle3d(s); } void CompactPatchEdit::setRadius(const int r) { _radius = r; _patchNameLabel->setRadius(r); _patchEdit->setRadius(r); } void CompactPatchEdit::setReadoutOrientation(ReadoutOrientation orient) { _orient = orient; } void CompactPatchEdit::setShowPatchLabel(bool v) { _showPatchLabel = v; } void CompactPatchEdit::setMaxAliasedPointSize(int sz) { _maxAliasedPointSize = sz; _patchEdit->setMaxAliasedPointSize(sz); } // QSize CompactPatchEdit::sizeHint() const // { // return getMinimumSizeHint(fontMetrics(), // _orient == ReadoutHorizontal ? Qt::Horizontal : Qt::Vertical, // frameWidth(), //_xMargin, // frameWidth() //_yMargin // ); // } int CompactPatchEdit::value() const { return _currentPatch; } void CompactPatchEdit::setValue(int v) { if(_currentPatch != v) { _currentPatch = v; //update(); } _patchEdit->setValue(v); } void CompactPatchEdit::setLastValidValue(int v) { _patchEdit->setLastValidPatch(v); } void CompactPatchEdit::setLastValidBytes(int hbank, int lbank, int prog) { _patchEdit->setLastValidBytes(hbank, lbank, prog); } QString CompactPatchEdit::patchName() const { return _patchNameLabel->text(); } void CompactPatchEdit::setPatchName(const QString& patchName) { _patchNameLabel->setText(patchName); } void CompactPatchEdit::setPatchNameOff(bool v) { _patchNameLabel->setOff(v); } void CompactPatchEdit::patchEditValueChanged(int val, int /*id*/) { _currentPatch = val; emit valueChanged(_currentPatch, _id); } void CompactPatchEdit::patchEditDoubleClicked(QPoint /*p*/, int /*id*/, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys) { if(buttons == Qt::LeftButton && keys == (Qt::ControlModifier | Qt::ShiftModifier)) { } } void CompactPatchEdit::patchEditRightClicked(QPoint p, int /*id*/) { emit patchValueRightClicked(p, _id); } void CompactPatchEdit::patchNamePressed(QPoint p, int /*id*/, Qt::MouseButtons buttons, Qt::KeyboardModifiers /*keys*/) { if(buttons == Qt::LeftButton) emit patchNameClicked(p, _id); else if(buttons == Qt::RightButton) emit patchNameRightClicked(mapToGlobal(p), _id); } void CompactPatchEdit::patchNameReturnPressed(QPoint p, int /*id*/, Qt::KeyboardModifiers /*keys*/) { emit patchNameClicked(p, _id); } } // namespace MusEGui muse-4.0.0/src/muse/components/compact_patch_edit.h000066400000000000000000000117351404105311000223700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // compact_patch_edit.h // (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __COMPACT_PATCH_EDIT_H__ #define __COMPACT_PATCH_EDIT_H__ #include class QMouseEvent; namespace MusEGui { class ElidedLabel; class LCDPatchEdit; //--------------------------------------------------------- // CompactPatchEdit //--------------------------------------------------------- class CompactPatchEdit : public QFrame { Q_OBJECT Q_PROPERTY( bool style3d READ style3d WRITE setStyle3d ) Q_PROPERTY( int radius READ radius WRITE setRadius ) bool _style3d; int _radius; public: enum ReadoutOrientation { ReadoutHorizontal = 0, ReadoutVertical }; private: ReadoutOrientation _orient; bool _showPatchLabel; int _maxAliasedPointSize; int _id; int _currentPatch; LCDPatchEdit* _patchEdit; ElidedLabel* _patchNameLabel; private slots: void patchEditValueChanged(int val, int id); void patchEditDoubleClicked(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); void patchEditRightClicked(QPoint p, int id); void patchNamePressed(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); void patchNameReturnPressed(QPoint p, int id, Qt::KeyboardModifiers keys); // protected: // virtual void keyPressEvent(QKeyEvent*); signals: void patchValueRightClicked(QPoint p, int id); void patchNameClicked(QPoint p, int id); void patchNameRightClicked(QPoint p, int id); void valueChanged(int value, int id); public: CompactPatchEdit(QWidget *parent, const char *name = 0, QColor fillColor = QColor()); virtual ~CompactPatchEdit(); static QSize getMinimumSizeHint(const QFontMetrics& fm, Qt::Orientation orient = Qt::Vertical, int xMargin = 0, int yMargin = 0); int id() const { return _id; } void setId(int i) { _id = i; } int value() const; void setValue(int v); void setLastValidValue(int v); void setLastValidBytes(int hbank, int lbank, int prog); // Sets up tabbing for the entire patch edit. // Accepts a previousWidget which can be null and returns the last widget in the control, // which allows chaining other widgets. virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); void setReadoutOrientation(ReadoutOrientation); void setShowPatchLabel(bool); void setReadoutColor(const QColor& c); void setBgColor(const QColor& c); void setBgActiveColor(const QColor& c); void setBorderColor(const QColor& c); void setBorderColorPatchEdit(const QColor& c); void setFontColor(const QColor& c); void setFontActiveColor(const QColor& c); void setStyle3d(const bool s); void setRadius(const int r); bool style3d() const { return _style3d; } // void setStyle3d(const bool style3d) { _style3d = style3d; update(); } int radius() const { return _radius; } // void setRadius(const int radius) { _radius = radius; update(); } // At what point size to switch from aliased text to non-aliased text. Zero means always use anti-aliasing. // Here in CompactPatchEdit, this only affects the CompactSliders so far, not the patch label. // If -1, no value has been set and default is each widget's setting. int maxAliasedPointSize() const { return _maxAliasedPointSize; } // Sets at what point size to switch from aliased text (brighter, crisper but may look too jagged and unreadable with some fonts) // to non-aliased text (dimmer, fuzzier but looks better). Zero means always use anti-aliasing. Default is each widget's setting. // Here in CompactPatchEdit, this only affects the CompactSliders so far, not the patch label. void setMaxAliasedPointSize(int sz); QString patchName() const; void setPatchName(const QString& patchName); // Sets the off state. void setPatchNameOff(bool v); //virtual QSize sizeHint() const; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/compact_slider.cpp000066400000000000000000001376311404105311000221050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // compact_slider.cpp // (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include "mmath.h" #include #include #include #include #include #include #include #include "utils.h" #include "popup_double_spinbox.h" #include "compact_slider.h" // #include "slider.h" //#include "icons.h" //#include "lcd_widgets.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_COMPACT_SLIDER(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { //------------------------------------------------------------- // Slider - The Slider Widget // // Slider is a slider widget which operates on an interval // of type double. Slider supports different layouts as // well as a scale. //------------------------------------------------------------ //------------------------------------------------------------ //.F CompactSlider::Slider // // Constructor // //.u Syntax: //.f CompactSlider::Slider(QWidget *parent, const char *name, Orientation orient = Horizontal, ScalePos scalePos = None, int bgStyle = BgTrough) // //.u Parameters //.p // QWidget *parent -- parent widget // const char *name -- The Widget's name. Default = 0. // Orientation Orient -- Orientation of the slider. Can be CompactSlider::Horizontal // or CompactSlider::Vertical. // Defaults to Horizontal. // ScalePos scalePos -- Position of the scale. Can be CompactSlider::None, // CompactSlider::Left, CompactSlider::Right, CompactSlider::Top, // or CompactSlider::Bottom. Defaults to CompactSlider::None. !!! CURRENTLY only CompactSlider::None supported - oget 20110913 // QColor fillcolor -- the color used to fill in the full side // of the Slider //------------------------------------------------------------ CompactSlider::CompactSlider(QWidget *parent, const char *name, Qt::Orientation orient, ScalePos scalePos, const QString& labelText, const QString& valPrefix, const QString& valSuffix, const QString& specialValueText, const QColor& borderColor, const QColor& barColor, const QColor& slotColor, const QColor& thumbColor) : SliderBase(parent,name) { if(objectName().isEmpty()) setObjectName(QStringLiteral("CompactSlider")); setMouseTracking(true); setEnabled(true); setFocusPolicy(Qt::WheelFocus); setAutoFillBackground(false); setAttribute(Qt::WA_NoSystemBackground); //setAttribute(Qt::WA_StaticContents); // This is absolutely required for speed! Otherwise painfully slow because of full background // filling, even when requesting small udpdates! Background is drawn by us. setAttribute(Qt::WA_OpaquePaintEvent); setBorderlessMouse(false); setCursorHoming(false); setPagingButtons(Qt::NoButton); setEnableValueToolTips(true); //_LCDPainter = new LCDPainter(); //_onPath = 0; //_offPath = 0; _editor = nullptr; _editMode = false; _barSameColor = false; _radius = 4; d_borderColor = borderColor; d_barColor = barColor; d_slotColor = slotColor; d_thumbColor = thumbColor; _maxAliasedPointSize = 8; d_labelText = labelText; d_valPrefix = valPrefix; d_valSuffix = valSuffix; d_specialValueText = specialValueText; _textHighlightMode = TextHighlightShadow | TextHighlightOn; _valueDecimals = 2; _off = false; d_offText = tr("off"); _showValue = true; _detectThumb = false; // unused _autoHideThumb = false; _hasOffMode = false; d_thumbLength = 0; d_thumbHitLength = 0; // d_thumbHalf = d_thumbLength / 2; d_thumbWidth = 16; d_thumbWidthMargin = 0; _mouseOverThumb = false; _hovered = false; _activeBorders = AllBorders; d_scaleDist = 4; d_scaleStep = 0.0; d_scalePos = scalePos; d_xMargin = 1; d_yMargin = 1; d_mMargin = 1; _entered = false; setOrientation(orient); d_valuePixel = 0; d_valuePixelWidth = 0; //updatePixmaps(); getActiveArea(); getPixelValues(); ensurePolished(); d_thumbHalf = d_thumbLength / 2; } //------------------------------------------------------------ //.F CompactSlider::~Slider // Destructor //.u Syntax //.f CompactSlider::~Slider() //------------------------------------------------------------ CompactSlider::~CompactSlider() { //if(_onPath) // delete _onPath; //if(_offPath) // delete _offPath; //if(_LCDPainter) // delete _LCDPainter; } // Static. QSize CompactSlider::getMinimumSizeHint(const QFontMetrics& fm, Qt::Orientation orient, ScalePos /*scalePos*/, int /*xMargin*/, int yMargin) { const int font_height = fm.height(); switch(orient) { case Qt::Vertical: return QSize(16, font_height + 3 + 2 * yMargin); break; case Qt::Horizontal: return QSize(16, font_height + 3 + 2 * yMargin); break; } return QSize(10, 10); } void CompactSlider::processSliderPressed(int) { // bPressed = true; update(); } void CompactSlider::processSliderReleased(int) { QPoint p = mapFromGlobal(QCursor::pos()); getMouseOverThumb(p); update(); DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::processSliderReleased trackingIsActive:%d val:%.20f valHasChanged:%d\n", trackingIsActive(), value(), valueHasChangedAtRelease()); // Changed. BUG: Was causing problems with sending changed even though it hadn't. // It should never signal a change on release UNLESS tracking is off, because otherwise the last movement // already sent the last changed signal. FIXME Even this is still flawed. If no tracking, it would likely // still signal a change upon simple press and release even though nothing changed. // if((!tracking()) || valHasChanged()) if(!trackingIsActive() && valueHasChangedAtRelease()) emit valueStateChanged(value(), isOff(), id(), d_scrollMode); } QString CompactSlider::toolTipValueText(bool inclLabel, bool inclVal) const { const double minV = minValue(ConvertNone); const double val = value(ConvertNone); const QString comp_val_text = isOff() ? d_offText : ((val <= minV && !d_specialValueText.isEmpty()) ? d_specialValueText : (d_valPrefix + locale().toString(val, 'f', _valueDecimals) + d_valSuffix)); QString txt; if(inclLabel) txt += d_labelText; if(inclLabel && inclVal) txt += QString(": "); if(inclVal) { txt += QString(""); txt += comp_val_text; txt += QString(""); } return txt; } void CompactSlider::showValueToolTip(QPoint /*p*/) { const QString txt = toolTipValueText(true, true); if(!txt.isEmpty()) { // Seems to be a small problem with ToolTip: Even if we force the font size, // if a previous tooltip was showing from another control at another font size, // it refuses to change font size. Also, if we supply the widget to showText(), // it refuses to change font size and uses the widget's font size instead. // Also, this craziness with ToolTip's self-offsetting is weird: In class CompactKnob // it is best when we supply the parent's position, while in class CompactSlider // it is best when we supply the widget's position - and it STILL isn't right! // Supplying the widget's position to CompactKnob, or parent's position to CompactSlider // actually makes the offsetting worse! if(QToolTip::font().pointSize() != 10) { QFont fnt = font(); fnt.setPointSize(10); QToolTip::setFont(fnt); QToolTip::hideText(); } //QToolTip::showText(p, txt, this, QRect(), 1000); //QToolTip::showText(p, txt, 0, QRect(), 1000); QToolTip::showText(mapToGlobal(pos()), txt, 0, QRect(), 3000); //QToolTip::showText(mapToGlobal(pos()), txt); //QToolTip::showText(mapToGlobal(parentWidget() ? parentWidget()->pos() : pos()), txt, parentWidget() ? parentWidget() : this, QRect(), 3000); } } void CompactSlider::setActiveBorders(ActiveBorders_t borders) { _activeBorders = borders; resize(size()); updateGeometry(); update(); } //---------------------------------------------------- // //.F CompactSlider::setThumbLength // // Set the slider's thumb length // //.u Syntax // void CompactSlider::setThumbLength(int l) // //.u Parameters //.p int l -- new length // //----------------------------------------------------- //void CompactSlider::setThumbLength(int l) //{ // d_thumbLength = MusECore::qwtMax(l,8); // d_thumbHalf = d_thumbLength / 2; // resize(this->size()); //} //------------------------------------------------------------ // //.F CompactSlider::setThumbWidth // Change the width of the thumb // //.u Syntax //.p void CompactSlider::setThumbWidth(int w) // //.u Parameters //.p int w -- new width // //------------------------------------------------------------ void CompactSlider::setThumbWidth(int w) { d_thumbWidth = MusECore::qwtMax(w,4); resize(this->size()); } //------------------------------------------------------------ //.- //.F CompactSlider::scaleChange // Notify changed scale // //.u Syntax //.f void CompactSlider::scaleChange() // //.u Description // Called by QwtScaledWidget // //------------------------------------------------------------ void CompactSlider::scaleChange() { if (!hasUserScale()) d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); update(); } // //------------------------------------------------------------ // //.- // //.F CompactSlider::fontChange // // Notify change in font // // // //.u Syntax // //.f CompactSlider::fontChange(const QFont &oldFont) // // // //------------------------------------------------------------ // void CompactSlider::fontChange(const QFont & /*oldFont*/) // { // repaint(); // } //------------------------------------------------------------ //.- //.F CompactSlider::getValue // Determine the value corresponding to a specified // mouse location. // //.u Syntax //.f double CompactSlider::getValue(const QPoint &p) // //.u Parameters //.p const QPoint &p -- // //.u Description // Called by SliderBase // If borderless mouse is enabled p is a delta value not absolute, so can be negative. //------------------------------------------------------------ double CompactSlider::getValue( const QPoint &p) { double rv; const QRect r = d_sliderRect; const double val = value(ConvertNone); if(borderlessMouse() && d_scrollMode != ScrDirect) { DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::getValue value:%.20f p x:%d y:%d step:%.20f x change:%.20f\n", val, p.x(), p.y(), step(), p.x() * step()); if(d_orient == Qt::Horizontal) return val + p.x() * step(); else return val - p.y() * step(); } const double min = minValue(ConvertNone); const double max = maxValue(ConvertNone); const double drange = max - min; if(d_orient == Qt::Horizontal) { if(r.width() <= d_thumbLength) rv = 0.5 * (min + max); else { const double dpos = double(p.x() - r.x() - d_thumbHalf); const double dwidth = double(r.width() - d_thumbLength); rv = min + rint(drange * dpos / dwidth / step()) * step(); } } else { if(r.height() <= d_thumbLength) rv = 0.5 * (min + max); else { const double dpos = double(p.y() - r.y() - d_thumbHalf); double dheight = double(r.height() - d_thumbLength); rv = min + rint( (max - min) * (1.0 - dpos / dheight) / step() ) * step(); } } return(rv); } //------------------------------------------------------------ // //.F CompactSlider::moveValue // Determine the value corresponding to a specified mouse movement. // //.u Syntax //.f void SliderBase::moveValue(const QPoint &deltaP, bool fineMode) // //.u Parameters //.p const QPoint &deltaP -- Change in position //.p bool fineMode -- Fine mode if true, coarse mode if false. // //.u Description // Called by SliderBase // Coarse mode (the normal mode) maps pixels to values depending on range and width, // such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. //------------------------------------------------------------ double CompactSlider::moveValue(const QPoint &deltaP, bool fineMode) { double rv; const QRect r = d_sliderRect; const double val = value(ConvertNone); if((fineMode || borderlessMouse()) && d_scrollMode != ScrDirect) { DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::moveValue value:%.20f p x:%d y:%d step:%.20f x change:%.20f\n", val, deltaP.x(), deltaP.y(), step(), deltaP.x() * step()); double newval; if(d_orient == Qt::Horizontal) newval = val + deltaP.x() * step(); else newval = val - deltaP.y() * step(); d_valAccum = newval; // Reset. return newval; } const double min = minValue(ConvertNone); const double max = maxValue(ConvertNone); const double drange = max - min; if(d_orient == Qt::Horizontal) { if(r.width() <= d_thumbLength) rv = 0.5 * (min + max); else { const double dpos = double(deltaP.x()); const double dwidth = double(r.width() - d_thumbLength); const double dval_diff = (drange * dpos) / dwidth; d_valAccum += dval_diff; rv = rint(d_valAccum / step()) * step(); DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::moveValue value:%.20f p dx:%d dy:%d drange:%.20f step:%.20f dval_diff:%.20f rv:%.20f\n", val, deltaP.x(), deltaP.y(), drange, step(), dval_diff, rv); } } else { if(r.height() <= d_thumbLength) rv = 0.5 * (min + max); else { const double dpos = double(deltaP.y()); const double dheight = double(r.height() - d_thumbLength); const double dval_diff = (drange * dpos) / dheight; d_valAccum += dval_diff; rv = rint(d_valAccum / step()) * step(); } } return(rv); } //------------------------------------------------------------ //.- //.F CompactSlider::getScrollMode // Determine scrolling mode and direction // //.u Syntax //.f void CompactSlider::getScrollMode( const QPoint &p, int &scrollMode, int &direction ) // //.u Parameters //.p const QPoint &p -- point // //.u Description // Called by SliderBase // //------------------------------------------------------------ void CompactSlider::getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction ) { // If modifier or button is held, jump directly to the position at first. // After handling it, the caller can change to SrcMouse scroll mode. if(modifiers & Qt::ControlModifier || button == Qt::MidButton) { scrollMode = ScrDirect; direction = 0; return; } // if((!detectThumb() || borderlessMouse())) if(borderlessMouse() && button != Qt::NoButton && d_sliderRect.contains(p)) { // if(button != Qt::NoButton && d_sliderRect.contains(p)) // { scrollMode = ScrMouse; direction = 0; return; // } } else { if(cursorHoming() && button == Qt::LeftButton) { if(d_sliderRect.contains(p)) { scrollMode = ScrMouse; direction = 0; int mp = 0; QRect cr; QPoint cp; int ipos,dist1; double rpos; cr = d_sliderRect; rpos = (value(ConvertNone) - minValue(ConvertNone)) / (maxValue(ConvertNone) - minValue(ConvertNone)); if(d_orient == Qt::Horizontal) { dist1 = int(double(cr.width() - d_thumbLength) * rpos); ipos = cr.x() + dist1; mp = ipos + d_thumbHalf; p.setX(mp); cp = mapToGlobal( QPoint(mp, p.y()) ); } else { dist1 = int(double(cr.height() - d_thumbLength) * (1.0 - rpos)); ipos = cr.y() + dist1; mp = ipos + d_thumbHalf; p.setY(mp); cp = mapToGlobal( QPoint(p.x(), mp) ); } cursor().setPos(cp.x(), cp.y()); return; } } else { int currentPos; if (d_orient == Qt::Horizontal) currentPos = p.x() - d_sliderRect.x(); else currentPos = p.y() - d_sliderRect.y(); if(d_sliderRect.contains(p)) { // if ((currentPos > d_valuePixel - d_thumbHalf) // && (currentPos < d_valuePixel + d_thumbHalf)) if (!detectThumb() || ((currentPos >= d_valuePixel - d_thumbHitLength / 2) && (currentPos <= d_valuePixel + d_thumbHitLength / 2))) { scrollMode = ScrMouse; direction = 0; return; } else if(pagingButtons().testFlag(button)) { scrollMode = ScrPage; if(((currentPos > d_valuePixel) && (d_orient == Qt::Horizontal)) || ((currentPos <= d_valuePixel) && (d_orient != Qt::Horizontal))) direction = 1; else direction = -1; return; } } } } scrollMode = ScrNone; direction = 0; } void CompactSlider::getActiveArea() { const QRect& geo = rect(); if (d_thumbLength > 0){ const int req_thumb_margin = (d_thumbHalf - d_xMargin) > 1 ? (d_thumbHalf - d_xMargin) : 1; d_sliderRect = QRect( geo.x() + d_xMargin + req_thumb_margin + 1, geo.y() + d_yMargin, geo.width() - 2 * (d_xMargin + req_thumb_margin) - 1, geo.height() - 2 * d_yMargin); } else { d_sliderRect = QRect( geo.x() + d_xMargin, geo.y() + d_yMargin, geo.width() - 2 * d_xMargin, geo.height() - 2 * d_yMargin); } // QStyleOptionFrame option; // option.initFrom(this); /* // FIXME: These aren't working for PE_Frame. We just get a 1px-wide square dark grey frame. // For others like PE_FrameLineEdit which looks OK, these have no effect and items // such as rounded frame are determined by the style. option.lineWidth = d_xMargin; option.features = QStyleOptionFrame::Rounded; option.frameShape = QFrame::StyledPanel; option.midLineWidth = 0; option.state = QStyle::State_Sunken; option.state |= QStyle::State_Active; if(isEnabled()) option.state |= QStyle::State_Enabled; if(hasFocus()) { option.state |= QStyle::State_HasFocus; // option.state |= QStyle::State_Selected; } if(underMouse()) { option.state |= QStyle::State_MouseOver; // FIXME: Not working. } // if(hasEditFocus()) // option.state |= (QStyle::State_HasFocus | QStyle::State_HasEditFocus | QStyle::State_Selected); const QStyle* st = style(); if(st) st = st->proxy(); // if(st) if(0) { // FIXME: Can't seem to get true inside area. All these methods return 1px frame width but the visible control has 2px width. // Maybe ask for QStyle::PM_DefaultFrameWidth. // return st->subElementRect(QStyle::SE_FrameContents, &option); // return contentsRect(); d_sliderRect = st->subElementRect(QStyle::SE_ShapedFrameContents, &option).adjusted(1, 1, -1, -1); } else*/ // { // d_sliderRect = QRect( //// geo.x() + d_xMargin + active_thumb_margin, // geo.x() + d_xMargin + req_thumb_margin + 1, // geo.y() + d_yMargin, //// geo.width() - 2 * (d_xMargin + active_thumb_margin), // geo.width() - 2 * (d_xMargin + req_thumb_margin - 1), // geo.height() - 2 * d_yMargin); // } } void CompactSlider::getPixelValues() { const int val_width_range = ((d_orient == Qt::Horizontal) ? d_sliderRect.width(): d_sliderRect.height()); const int val_pix_range = val_width_range - 1; const double minV = minValue(ConvertNone); const double maxV = maxValue(ConvertNone); const double range = maxV - minV; const double val = value(ConvertNone); if(range == 0.0) { d_valuePixel = 0; d_valuePixelWidth = 0; return; } const double val_fact = (val - minV) / range; d_valuePixel = (val_fact * (double)val_pix_range); d_valuePixelWidth = (val_fact * (double)val_width_range); } //------------------------------------------------------------ //.F CompactSlider::paintEvent // Qt paint event // //.u Syntax //.f void CompactSlider::paintEvent(QPaintEvent *e) //------------------------------------------------------------ void CompactSlider::paintEvent(QPaintEvent* /*ev*/) { const QRect& geo = rect(); if(geo.width() <= 0 || geo.height() <= 0) return; QPainter p(this); if (_barSameColor) d_barColor = d_borderColor; if (!d_thumbColor.isValid()) d_thumbColor = d_barColor; const QPalette& pal = palette(); const int label_to_val_margin = 6; const QPen orig_pen = p.pen(); const QColor& margin_color = isEnabled() ? (d_borderColor.isValid() ? d_borderColor : pal.color(QPalette::Active, QPalette::Button)) : pal.color(QPalette::Disabled, QPalette::Button); QColor border_color; if(_textHighlightMode & TextHighlightFocus) border_color = _hovered ? pal.color(isEnabled() ? QPalette::Active : QPalette::Disabled, QPalette::Highlight).lighter() : margin_color; else //border_color = hasFocus() ? pal.color(isEnabled() ? QPalette::Active : QPalette::Disabled, QPalette::Highlight).lighter() : margin_color; border_color = isEnabled() ? (hasFocus() ? margin_color.lighter() : margin_color) : pal.color(QPalette::Disabled, QPalette::Highlight); const QColor c4 = isEnabled() ? (d_slotColor.isValid() ? d_slotColor : pal.color(QPalette::Active, QPalette::Dark)) : pal.color(QPalette::Disabled, QPalette::Dark); const QColor c3 = c4.darker(125); QColor inactive_border_color; if(_textHighlightMode & TextHighlightFocus) //inactive_border_color = _hovered ? pal.color(isEnabled() ? QPalette::Active : QPalette::Disabled, QPalette::Highlight).lighter() : margin_color; inactive_border_color = isEnabled() ? (_hovered ? border_color : c3) : pal.color(QPalette::Disabled, QPalette::Highlight); else //border_color = hasFocus() ? pal.color(isEnabled() ? QPalette::Active : QPalette::Disabled, QPalette::Highlight).lighter() : margin_color; inactive_border_color = isEnabled() ? (hasFocus() ? border_color : c3) : pal.color(QPalette::Disabled, QPalette::Highlight); // Draw margins: if(d_yMargin) { // Top //if(_activeBorders & TopBorder) p.fillRect(geo.x(), geo.y(), geo.width(), d_yMargin, _activeBorders & TopBorder ? border_color : inactive_border_color); // Bottom //if(_activeBorders & BottomBorder) p.fillRect(geo.x(), geo.height() - d_yMargin, geo.width(), d_yMargin, _activeBorders & BottomBorder ? border_color : inactive_border_color); } if(d_xMargin) { // Left //if(_activeBorders & LeftBorder) p.fillRect(geo.x(), geo.y(), d_xMargin, geo.height(), _activeBorders & LeftBorder ? border_color : inactive_border_color); // Right //if(_activeBorders & RightBorder) p.fillRect(geo.width() - d_xMargin, geo.y(), d_xMargin, geo.height(), _activeBorders & RightBorder ? border_color : inactive_border_color); } if (d_thumbLength > 0) { // Extra left margin const int req_thumb_margin = (d_thumbHalf - d_xMargin) > 1 ? (d_thumbHalf - d_xMargin) : 1; //if(_activeBorders & LeftBorder) p.fillRect(d_xMargin, d_yMargin, req_thumb_margin + 1, geo.height() - 2 * d_yMargin, // _activeBorders & LeftBorder ? margin_color : inactive_border_color); d_slotColor); // Extra right margin //if(_activeBorders & RightBorder) p.fillRect( geo.width() - d_xMargin - req_thumb_margin, d_yMargin, req_thumb_margin, geo.height() - 2 * d_yMargin, // _activeBorders & RightBorder ? margin_color : inactive_border_color); d_slotColor); } // const QPainterPath& onPath = *_onPath; // // // Draw corners as normal background colour. Path subtraction. // QPainterPath cornerPath; // cornerPath.addRect(d_sliderRect); // cornerPath -= onPath; // if(!cornerPath.isEmpty()) // //p.fillPath(cornerPath, palette().window().color().darker(125)); // //p.fillPath(cornerPath, palette().shadow().color().darker(125)); // p.fillPath(cornerPath, isEnabled() ? (d_slotColor.isValid() ? d_slotColor : palette().color(QPalette::Active, QPalette::Dark)) : // palette().color(QPalette::Disabled, QPalette::Dark)); int x = d_sliderRect.x(); int y = d_sliderRect.y(); int h = d_sliderRect.height(); const int x1 = x; int w1 = 0; if (d_thumbLength == 0) w1 = d_valuePixelWidth; const int y1 = y; const int y2 = y1 + h - 1; QLinearGradient linearGrad_a(x, y1, x, y2); const QColor c2 = isEnabled() ? (d_barColor.isValid() ? d_barColor : pal.color(QPalette::Active, QPalette::Highlight)) : pal.color(QPalette::Disabled, QPalette::Highlight); const QColor c1 = c2.darker(125); linearGrad_a.setColorAt(0, c1); linearGrad_a.setColorAt(0.5, c2); linearGrad_a.setColorAt(1, c1); QPainterPath onPath; MusECore::addRoundedPath(&onPath, QRect (x1, y, w1, h), _radius, _radius, (MusECore::Corner) (MusECore::CornerUpperRight | MusECore::CornerLowerRight) ); if(!onPath.isEmpty()) p.fillPath(onPath, linearGrad_a); linearGrad_a.setColorAt(0, c3); linearGrad_a.setColorAt(0.5, c4); linearGrad_a.setColorAt(1, c3); QPainterPath offPath; offPath.addRect(d_sliderRect); offPath -= onPath; if(!offPath.isEmpty()) // p.fillPath(offPath, QBrush(d_slotColor)); p.fillPath(offPath, linearGrad_a); const double minV = minValue(ConvertNone); const double val = value(ConvertNone); const QRect bar_area((d_orient == Qt::Horizontal) ? QRect(d_sliderRect.x(), d_sliderRect.y(), d_valuePixelWidth, d_sliderRect.height()) : QRect(d_sliderRect.x(), d_sliderRect.y() + d_sliderRect.height() - d_valuePixelWidth, d_sliderRect.width(), d_sliderRect.height() - d_valuePixelWidth)); // FIXME const QRect bkg_area((d_orient == Qt::Horizontal) ? QRect(d_sliderRect.x() + d_valuePixelWidth, d_sliderRect.y(), //active_area.width() - (active_area.x() + val_pix), d_sliderRect.width() - d_valuePixelWidth, d_sliderRect.height()) : QRect(d_sliderRect.x(), d_sliderRect.y(), d_sliderRect.width(), d_sliderRect.height() - d_valuePixelWidth)); // FIXME // p.setClipPath(onPath); // p.fillPath(onPath, _onPixmap); // p.setClipPath(offPath); // p.fillPath(offPath, _offPixmap); // Restore the clipper to full size. // p.setClipRect(geo); if((!_autoHideThumb || _mouseOverThumb) && d_thumbLength > 0) { DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::PaintEvent _mouseOverThumb:%d\n", _mouseOverThumb); const QRect thumb_rect((d_orient == Qt::Horizontal) ? QRect(d_sliderRect.x() + d_valuePixel - d_thumbHalf, d_sliderRect.y() + d_thumbWidthMargin, d_thumbLength, d_sliderRect.height() - 2 * d_thumbWidthMargin) : QRect(d_sliderRect.x() + d_thumbWidthMargin, d_sliderRect.y() + d_valuePixel - d_thumbHalf, d_sliderRect.width() - 2 * d_thumbWidthMargin, d_thumbLength)); p.fillRect(thumb_rect, isEnabled() ? d_thumbColor : pal.color(QPalette::Disabled, QPalette::Mid)); // p.fillRect(thumb_rect, isEnabled() ? (d_thumbColor.isValid() ? d_thumbColor : pal.color(QPalette::Active, QPalette::Mid)) : // pal.color(QPalette::Disabled, QPalette::Mid)); } const QFont& fnt = font(); QFont aliased_fnt(fnt); // Turn off anti-aliasing for sharper text. if we want it: if(fnt.pointSize() <= _maxAliasedPointSize) { aliased_fnt.setFamily("Sans"); //aliased_fnt.setHintingPreference(QFont::PreferVerticalHinting); //aliased_fnt.setStyleStrategy(QFont::PreferBitmap); aliased_fnt.setStyleStrategy(QFont::NoAntialias); //p.setFont(aliased_fnt); } const QFontMetrics aliased_fm(aliased_fnt); const bool show_val = _showValue; const QFontMetrics fm = p.fontMetrics(); const QRect text_area(d_sliderRect.adjusted(1, 1, -1, -1)); const QString comp_val_text = isOff() ? d_offText : ((val <= minV && !d_specialValueText.isEmpty()) ? d_specialValueText : (d_valPrefix + locale().toString(val, 'f', _valueDecimals) + d_valSuffix)); //const int val_width = fm.width(comp_val_text); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 const int val_width = aliased_fm.horizontalAdvance(comp_val_text); #else const int val_width = aliased_fm.width(comp_val_text); #endif int vx = text_area.width() - val_width; if(vx < 0) vx = 0; const QRect val_area(vx, text_area.y(), val_width, text_area.height()); int lw = text_area.width(); if(show_val) lw = lw - val_width - label_to_val_margin; if(lw < 0) lw = 0; QRect label_area(text_area.x(), text_area.y(), lw, text_area.height()); //const QString elided_label_text = fm.elidedText(d_labelText, Qt::ElideMiddle, text_area.width()); // const QString elided_label_text = fm.elidedText(d_labelText, Qt::ElideMiddle, label_area.width()); const QString elided_label_text = d_labelText; if(!show_val) { const QRect label_br = fm.boundingRect(d_labelText); const int label_bw = label_br.width(); int label_xoff = (label_area.width() - label_bw) / 2; if(label_xoff < 0) label_xoff = 0; label_area.adjust(label_xoff, 0, 0, 0); } //const int label_width = fm.width(elided_label_text); //const bool show_both = (text_area.width() - val_width) > (label_area.width() + 2); //const bool show_both = label_area.width() > label_to_val_margin; const bool show_label = show_val || label_area.width() > label_to_val_margin; const bool on = _textHighlightMode & TextHighlightOn; const bool shd = _textHighlightMode & TextHighlightShadow; const bool spl = _textHighlightMode & TextHighlightSplit; const bool hov = _textHighlightMode & TextHighlightHover; const bool foc = _textHighlightMode & TextHighlightFocus; const bool is_hov = hov && _hovered; const bool is_foc = foc && hasFocus(); // Normal text: p.setPen(Qt::black); //QRect text_bkg = text_area; QRect label_bkg = label_area; QRect val_bkg = val_area; if(shd) { //text_bkg.adjust(1, 1, 1, 1); val_bkg.adjust(1, 1, 1, 1); label_bkg.adjust(1, 1, 1, 1); if(show_val) { p.setFont(aliased_fnt); //p.drawText(text_bkg, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); p.drawText(val_bkg, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); //_LCDPainter->drawText(&p, val_bkg, comp_val_text, Qt::AlignRight | Qt::AlignVCenter); p.setFont(fnt); } if(show_label) //p.drawText(text_bkg, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); p.drawText(label_bkg, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); } if((!shd && ((!on || spl) && !is_hov && !is_foc))) { if(spl) p.setClipRect(bkg_area); else // Restore the clipper to full size. p.setClipRect(geo); if(show_val) { p.setFont(aliased_fnt); //p.drawText(text_area, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); p.drawText(val_area, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); //_LCDPainter->drawText(&p, val_area, comp_val_text, Qt::AlignRight | Qt::AlignVCenter); p.setFont(fnt); } if(show_label) //p.drawText(text_area, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); p.drawText(label_area, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); } // Highlighted text: if(on || is_hov || is_foc) { p.setPen(Qt::white); if(spl && !is_hov && !is_foc) p.setClipRect(bar_area); else // Restore the clipper to full size. p.setClipRect(geo); if(show_val) { p.setFont(aliased_fnt); //p.drawText(text_area, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); p.drawText(val_area, Qt::AlignRight | Qt::AlignVCenter, comp_val_text); //_LCDPainter->drawText(&p, val_area, comp_val_text, Qt::AlignRight | Qt::AlignVCenter); p.setFont(fnt); } if(show_label) //p.drawText(text_area, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); p.drawText(label_area, Qt::AlignLeft | Qt::AlignVCenter, elided_label_text); } } void CompactSlider::mouseMoveEvent(QMouseEvent *e) { e->ignore(); SliderBase::mouseMoveEvent(e); QPoint p = e->pos(); const bool oldv = _mouseOverThumb; getMouseOverThumb(p); if(_autoHideThumb &&_mouseOverThumb != oldv) update(); } void CompactSlider::mouseDoubleClickEvent(QMouseEvent* e) { DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::mouseDoubleClickEvent\n"); const Qt::MouseButtons buttons = e->buttons(); const Qt::KeyboardModifiers keys = e->modifiers(); if(buttons == Qt::LeftButton && _mouseOverThumb && !_editMode) { DEBUG_COMPACT_SLIDER(stderr, " left button\n"); //if(_mouseOverThumb && !_editMode) //{ //if(ctrl_key) if(keys == Qt::ControlModifier) { if(_hasOffMode) { setOff(!isOff()); // Just toggle the off state. emit valueChanged(value(), id()); e->accept(); return; } } // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. else if(keys == Qt::NoModifier && (!_editor || !_editor->hasFocus())) { showEditor(); e->accept(); return; } //} //e->accept(); //return; } e->ignore(); SliderBase::mouseDoubleClickEvent(e); } void CompactSlider::editorReturnPressed() { DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::editorReturnPressed\n"); _editMode = false; if(_editor) { if(value() != _editor->value()) setValue(_editor->value()); _editor->deleteLater(); _editor = 0; setFocus(); } } void CompactSlider::editorEscapePressed() { DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::editorEscapePressed\n"); _editMode = false; if(_editor) { _editor->deleteLater(); _editor = 0; setFocus(); } } void CompactSlider::keyPressEvent(QKeyEvent* e) { if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) { // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. if(!_editor || !_editor->hasFocus()) showEditor(); e->accept(); return; } e->ignore(); SliderBase::keyPressEvent(e); } void CompactSlider::enterEvent(QEvent *e) { _entered = true; if(!_hovered) { _hovered = true; if(_textHighlightMode & TextHighlightHover) update(); } e->ignore(); SliderBase::enterEvent(e); } void CompactSlider::leaveEvent(QEvent *e) { _entered = false; if(!_pressed) { if(_hovered) { _hovered = false; } if(_textHighlightMode & TextHighlightHover) update(); _mouseOverThumb = false; if(_autoHideThumb) update(); } e->ignore(); SliderBase::leaveEvent(e); } bool CompactSlider::event(QEvent* e) { switch(e->type()) { // FIXME: Doesn't work. case QEvent::NonClientAreaMouseButtonPress: DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::event NonClientAreaMouseButtonPress\n"); e->accept(); _editMode = false; if(_editor) { _editor->deleteLater(); _editor = 0; } return true; break; // case QEvent::PaletteChange: // DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::event PaletteChange\n"); // updatePixmaps(); // break; // // case QEvent::EnabledChange: // DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::event EnabledChange\n"); // updatePixmaps(); // break; // case QEvent::ActivationChange: // case QEvent::WindowActivate: // case QEvent::WindowDeactivate: // DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::event ActivationChange, WindowActivate, or WindowDeactivate\n"); // updatePixmaps(); // break; default: break; } return SliderBase::event(e); } // void CompactSlider::updatePixmaps() // { // getActiveArea(); // getPixelValues(); // // int x = d_sliderRect.x(); // int y = d_sliderRect.y(); // int w = d_sliderRect.width(); // int h = d_sliderRect.height(); // // _onPixmap = QPixmap(w, h); // _offPixmap = QPixmap(w, h); // // const int y1 = y; // const int y2 = y1 + h - 1; // // // const QPalette& pal = palette(); // // QLinearGradient linearGrad_a(x, y1, x, y2); // // // const QColor c2 = isEnabled() ? (d_barColor.isValid() ? d_barColor : pal.color(QPalette::Active, QPalette::Highlight)) : // pal.color(QPalette::Disabled, QPalette::Highlight); // const QColor c1 = c2.darker(125); // // linearGrad_a.setColorAt(0, c1); // linearGrad_a.setColorAt(0.5, c2); // linearGrad_a.setColorAt(1, c1); // // if(w > 0 && h > 0) // { // QPainter p; // p.begin(&_onPixmap); // p.fillRect(0, 0, w, h, linearGrad_a); // p.end(); // p.begin(&_offPixmap); // p.fillRect(0, 0, w, h, pal.window()); // p.end(); // } // // updatePainterPaths(); // } //------------------------------------------------------------ //.F CompactSlider::resizeEvent // Qt resize event // //.u Parameters //.p QResizeEvent *e // //.u Syntax //.f void CompactSlider::resizeEvent(QResizeEvent *e) //------------------------------------------------------------ void CompactSlider::resizeEvent(QResizeEvent *e) { SliderBase::resizeEvent(e); d_resized = true; //updatePixmaps(); getActiveArea(); getPixelValues(); if(_editor && _editor->isVisible()) _editor->setGeometry(rect()); } void CompactSlider::showEditor() { if(_editMode) return; if(!_editor) { DEBUG_COMPACT_SLIDER(stderr, " creating editor\n"); _editor = new PopupDoubleSpinBox(this); _editor->setFrame(false); _editor->setFocusPolicy(Qt::WheelFocus); connect(_editor, SIGNAL(returnPressed()), SLOT(editorReturnPressed())); connect(_editor, SIGNAL(escapePressed()), SLOT(editorEscapePressed())); } int w = width(); //if (w < _editor->sizeHint().width()) // w = _editor->sizeHint().width(); _editor->setGeometry(0, 0, w, height()); DEBUG_COMPACT_SLIDER(stderr, " x:%d y:%d w:%d h:%d\n", _editor->x(), _editor->y(), w, _editor->height()); _editor->setDecimals(_valueDecimals); _editor->setSingleStep(step()); _editor->setPrefix(valPrefix()); _editor->setSuffix(valSuffix()); _editor->setMinimum(minValue()); _editor->setMaximum(maxValue()); _editor->setValue(value()); _editor->selectAll(); _editMode = true; _editor->show(); _editor->setFocus(); } void CompactSlider::getMouseOverThumb(QPoint &p) { int scrollMode; int direction; //getScrollMode(p, left_btn ? Qt::LeftButton : 0, scrollMode, direction); // Force left button in case getScrollMode depends on it. // getScrollMode(p, Qt::LeftButton, Qt::KeyboardModifiers(), scrollMode, direction); getScrollMode(p, Qt::NoButton, Qt::KeyboardModifiers(), scrollMode, direction); const bool v = scrollMode == ScrMouse; //if(_mouseOverThumb != v && !(left_btn && !v)) // if(_mouseOverThumb != v && !(bPressed && !v)) if(_mouseOverThumb != v && !(_pressed && !v)) { DEBUG_COMPACT_SLIDER(stderr, "CompactSlider::getMouseOverThumb setting _mouseOverThumb:%d to v:%d\n", _mouseOverThumb, v); _mouseOverThumb = v; } const bool hv = rect().contains(p); // if(_hovered != hv && !bPressed) if(_hovered != hv && !_pressed) _hovered = hv; } void CompactSlider::setShowValue(bool show) { _showValue = show; resize(size()); updateGeometry(); // Required. update(); } void CompactSlider::setOff(bool v) { if(v && !_hasOffMode) _hasOffMode = true; if(_off == v) return; _off = v; update(); emit valueStateChanged(value(), isOff(), id(), d_scrollMode); } void CompactSlider::setHasOffMode(bool v) { _hasOffMode = v; setOff(false); } void CompactSlider::setValueState(double v, bool off, ConversionMode mode) { // Do not allow setting value from the external while mouse is pressed. if(_pressed) return; bool do_off_upd = false; bool do_val_upd = false; // Both setOff and setValue emit valueStateChanged and setValue emits valueChanged. // We will block them and emit our own here. Respect the current block state. const bool blocked = signalsBlocked(); if(!blocked) blockSignals(true); if(isOff() != off) { do_off_upd = true; setOff(off); } // if(value() != v) if(value(mode) != v) { do_val_upd = true; // setValue(v); setValue(v, mode); } if(!blocked) blockSignals(false); if(do_off_upd || do_val_upd) update(); if(do_val_upd) emit valueChanged(value(), id()); if(do_off_upd || do_val_upd) emit valueStateChanged(value(), isOff(), id(), d_scrollMode); } //------------------------------------------------------------ //.- //.F CompactSlider::valueChange // Notify change of value // //.u Syntax //.f void CompactSlider::valueChange() // //------------------------------------------------------------ void CompactSlider::valueChange() { // Turn the control back on with any value set. // Wanted to make this flaggable, but actually we // have to in order to see any value changing, if(isOff()) setOff(false); getPixelValues(); //updatePainterPaths(); QPoint p = mapFromGlobal(QCursor::pos()); getMouseOverThumb(p); update(); // HACK // In direct mode let the inherited classes (this) call these in their valueChange() methods, // so that they may be called BEFORE valueChanged signal is emitted by the setPosition() call above. // ScrDirect mode only happens once upon press with a modifier. After that, another mode is set. // Hack: Since valueChange() is NOT called if nothing changed, in that case these are called for us by the SliderBase. if(d_scrollMode == ScrDirect) { processSliderPressed(id()); emit sliderPressed(value(), id()); } // Emits valueChanged if tracking enabled. SliderBase::valueChange(); // Emit our own combined signal. if(trackingIsActive()) emit valueStateChanged(value(), isOff(), id(), d_scrollMode); } //------------------------------------------------------------ //.- //.F CompactSlider::rangeChange // Notify change of range // //.u Description // //.u Syntax //.f void CompactSlider::rangeChange() // //------------------------------------------------------------ void CompactSlider::rangeChange() { if (!hasUserScale()) d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); getPixelValues(); SliderBase::rangeChange(); // repaint(); update(); } //------------------------------------------------------------ // //.F CompactSlider::setMargins // Set distances between the widget's border and // internals. // //.u Syntax //.f void CompactSlider::setMargins(int hor, int vert) // //.u Parameters //.p int hor, int vert -- Margins // //------------------------------------------------------------ void CompactSlider::setMargins(int hor, int vert) { d_xMargin = MusECore::qwtMax(0, hor); d_yMargin = MusECore::qwtMax(0, vert); resize(this->size()); } void CompactSlider::setMargins(QSize s) { setMargins(s.width(), s.height()); } void CompactSlider::setXMargin(int x) { setMargins(x, d_yMargin); } void CompactSlider::setYMargin(int y) { setMargins(d_xMargin, y); } //------------------------------------------------------------ // //.F CompactSlider::sizeHint // Return a recommended size // //.u Syntax //.f QSize CompactSlider::sizeHint() const // //.u Note // The return value of sizeHint() depends on the font and the // scale. //------------------------------------------------------------ QSize CompactSlider::sizeHint() const { return getMinimumSizeHint(fontMetrics(), d_orient, d_scalePos, d_xMargin, d_yMargin); } //--------------------------------------------------------- // setOrientation //--------------------------------------------------------- void CompactSlider::setOrientation(Qt::Orientation o) { d_orient = o; } Qt::Orientation CompactSlider::orientation() const { return d_orient; } double CompactSlider::lineStep() const { return 1.0; } double CompactSlider::pageStep() const { return 1.0; } void CompactSlider::setLineStep(double) { } void CompactSlider::setPageStep(double) { } // void CompactSlider::updatePainterPaths() // { // if(_offPath) // delete _offPath; // if(_onPath) // delete _onPath; // // _offPath = new QPainterPath; // _onPath = new QPainterPath; // // int x = d_sliderRect.x(); // int y = d_sliderRect.y(); // int w = d_sliderRect.width(); // int h = d_sliderRect.height(); // // const int x1 = x; // const int w1 = d_valuePixelWidth; // // const int x2 = x + d_valuePixelWidth; // const int w2 = w - d_valuePixelWidth; // // MusECore::addRoundedPath(_onPath, QRect (x1, y, w1, h), 4, 4, // //(MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); // //(MusECore::Corner) (MusECore::UpperLeft | MusECore::LowerLeft) ); // (MusECore::Corner) (MusECore::UpperRight | MusECore::LowerRight) ); // // MusECore::addRoundedPath(_offPath, QRect (x2, y, w2, h), 4, 4, // //(MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); // (MusECore::Corner) (MusECore::UpperRight | MusECore::LowerRight) ); // } } // namespace MusEGui muse-4.0.0/src/muse/components/compact_slider.h000066400000000000000000000333141404105311000215430ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // compact_slider.h // (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __COMPACT_SLIDER_H__ #define __COMPACT_SLIDER_H__ //#include #include "sclif.h" #include "sliderbase.h" #include "scldraw.h" class QPainterPath; class QEvent; namespace MusEGui { class PopupDoubleSpinBox; // //class LCDPainter; //--------------------------------------------------------- // CompactSlider //--------------------------------------------------------- class CompactSlider : public SliderBase, public ScaleIf { Q_OBJECT public: enum ScalePos { None, Left, Right, Top, Bottom, Embedded }; // Can be OR'd together. enum ActiveBorders { NoBorders = 0, LeftBorder = 1, RightBorder = 2, TopBorder = 4, BottomBorder = 8, AllBorders = LeftBorder | RightBorder | TopBorder | BottomBorder, AllBordersExceptTop = LeftBorder | RightBorder | BottomBorder, AllBordersExceptBottom = LeftBorder | RightBorder | TopBorder, AllBordersExceptLeft = RightBorder | TopBorder | BottomBorder, AllBordersExceptRight = LeftBorder | TopBorder | BottomBorder, }; typedef int ActiveBorders_t; // Can be OR'd together. enum TextHighlightModes { //TextHighlightNone, //TextHighlightAlways, TextHighlightOn = 1, TextHighlightSplit = 2, TextHighlightShadow = 4, //TextHighlightSplitAndShadow, TextHighlightHover = 8, TextHighlightFocus = 16 //TextHighlightHoverOrFocus }; typedef int TextHighlightMode_t; Q_PROPERTY( double lineStep READ lineStep WRITE setLineStep ) Q_PROPERTY( double pageStep READ pageStep WRITE setPageStep ) Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation ) Q_PROPERTY( QSize margins READ margins WRITE setMargins ) Q_PROPERTY( int xMargin READ xMargin WRITE setXMargin ) Q_PROPERTY( int yMargin READ yMargin WRITE setYMargin ) Q_PROPERTY( bool barSameColor READ barSameColor WRITE setBarSameColor ) Q_PROPERTY( int radius READ radius WRITE setRadius ) Q_PROPERTY( int thumbLength READ thumbLength WRITE setThumbLength ) Q_PROPERTY( QColor thumbColor READ thumbColor WRITE setThumbColor ) Q_PROPERTY( QString labelText READ labelText WRITE setLabelText ) Q_PROPERTY( QString valPrefix READ valPrefix WRITE setValPrefix ) Q_PROPERTY( QString valSuffix READ valSuffix WRITE setValSuffix ) Q_PROPERTY( QString specialValueText READ specialValueText WRITE setSpecialValueText ) Q_PROPERTY( QString offText READ offText WRITE setOffText ) Q_PROPERTY( int valueDecimals READ valueDecimals WRITE setValueDecimals ) private: QRect d_sliderRect; // Whether the mouse is currently over the thumb 'hit' space. bool _mouseOverThumb; // Whether the mouse is over the entire control. bool _hovered; // Cached pixmap values. //QPixmap _onPixmap, _offPixmap; //QPainterPath* _onPath; //QPainterPath* _offPath; bool _detectThumb; bool _autoHideThumb; bool _hasOffMode; int d_thumbLength; int d_thumbHitLength; int d_thumbHalf; int d_thumbWidth; int d_thumbWidthMargin; int d_scaleDist; int d_xMargin; int d_yMargin; int d_mMargin; // Which of the coloured borders are active. // This allows them to be stacked or placed side by side // without the border colours being too bright or annoying // since the joining edges are twice as thick. ActiveBorders_t _activeBorders; QColor d_borderColor; QColor d_barColor; QColor d_slotColor; QColor d_thumbColor; bool _barSameColor; int _radius; QString d_labelText; QString d_valPrefix; QString d_valSuffix; QString d_specialValueText; QString d_offText; TextHighlightMode_t _textHighlightMode; int _valueDecimals; bool _off; // Whether to display the value, along with the text. bool _showValue; PopupDoubleSpinBox* _editor; bool _editMode; //LCDPainter* _LCDPainter; bool _entered; // Set on enter and leave. bool d_resized; bool d_autoResize; double d_scaleStep; Qt::Orientation d_orient; ScalePos d_scalePos; int d_bgStyle; // Two slightly different versions of the same thing: valuePixel is the current value scaled to // a range up to and including the LAST pixel, while valueWidth is the current value scaled to // a range up to and including the last pixel PLUS ONE ie the 'pixel width'. Values start at zero. int d_valuePixel; int d_valuePixelWidth; //void updatePainterPaths(); private slots: void editorReturnPressed(); void editorEscapePressed(); protected: // At what point size to switch from aliased text (brighter, crisper but may look too jagged and unreadable with some fonts) // to non-aliased text (dimmer, fuzzier but looks better). Zero means always use anti-aliasing. int _maxAliasedPointSize; // Determine the value corresponding to a specified mouse location. // If borderless mouse is enabled p is a delta value not absolute, so can be negative. double getValue(const QPoint &p); // Determine the value corresponding to a specified mouse movement. double moveValue(const QPoint &deltaP, bool fineMode = false); // Determine scrolling mode and direction. void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction); // Update internal values. void getPixelValues(); void getActiveArea(); void getMouseOverThumb(QPoint &p); void showEditor(); virtual void resizeEvent(QResizeEvent*); virtual void paintEvent (QPaintEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual void mouseDoubleClickEvent(QMouseEvent*); virtual void keyPressEvent(QKeyEvent*); virtual void leaveEvent(QEvent*); virtual void enterEvent(QEvent*); virtual bool event(QEvent*); void valueChange(); void rangeChange(); void scaleChange(); //void fontChange(const QFont &oldFont); //virtual void updatePixmaps(); virtual void processSliderPressed(int); virtual void processSliderReleased(int); // Show a handy tooltip value box. virtual void showValueToolTip(QPoint); signals: // Both value and off state changed combined into one signal. // In typical automation use, this signal should be ignored in ScrDirect scroll mode. // ScrDirect mode happens only once upon press with a modifier. // In ScrDirect mode the slider sends both pressed AND changed signals // since the position jumps to the pressed location. // Note the SliderBase::valueChanged signal is also available. void valueStateChanged(double value, bool off, int id, int scrollMode); public: CompactSlider(QWidget *parent = 0, const char *name = 0, Qt::Orientation orient = Qt::Horizontal, ScalePos scalePos = None, const QString& labelText = QString(), const QString& valPrefix = QString(), const QString& valSuffix = QString(), const QString& specialValueText = QString(), const QColor& borderColor = QColor(), const QColor& barColor = QColor(228,203,36), const QColor& slotColor = QColor(), const QColor& thumbColor = QColor()); virtual ~CompactSlider(); static QSize getMinimumSizeHint(const QFontMetrics& fm, Qt::Orientation orient = Qt::Vertical, ScalePos scalePos = None, int xMargin = 0, int yMargin = 0); // Which of the coloured borders are active. // This allows them to be stacked or placed side by side // without the border colours being too bright or annoying // since the joining edges are twice as thick. ActiveBorders_t activeBorders() const { return _activeBorders; } void setActiveBorders(ActiveBorders_t borders); // void setThumbLength(int l); void setThumbWidth(int w); void setOrientation(Qt::Orientation o); Qt::Orientation orientation() const; double lineStep() const; double pageStep() const; void setLineStep(double); void setPageStep(double); // QColor borderColor() const { return d_borderColor; } void setBorderColor(const QColor& c) { d_borderColor = c; update(); } // QColor barColor() const { return d_barColor; } void setBarColor(const QColor& c) { d_barColor = c; update(); } // QColor slotColor() const { return d_slotColor; } void setSlotColor(const QColor& c) { d_slotColor = c; update(); } QColor thumbColor() const { return d_thumbColor; } void setThumbColor(const QColor& c) { d_thumbColor = c; update(); } bool barSameColor() const { return _barSameColor; } void setBarSameColor(const bool barSameColor) { _barSameColor = barSameColor; } int radius() const { return _radius; } void setRadius(const int radius) { _radius = radius; } int thumbLength() const { return d_thumbLength; } void setThumbLength(const int l) { d_thumbLength = l; } // Whether the user must click on the thumb or else anywhere in the control to move the value. bool detectThumb() const { return _detectThumb; } // Set whether the user must click on the thumb or else anywhere in the control to move the value. void setDetectThumb(bool v) { _detectThumb = v; update(); } // At what point size to switch from aliased text to non-aliased text. Zero means always use anti-aliasing. Default 8. int maxAliasedPointSize() const { return _maxAliasedPointSize; } // Sets at what point size to switch from aliased text (brighter, crisper but may look too jagged and unreadable with some fonts) // to non-aliased text (dimmer, fuzzier but looks better). Zero means always use anti-aliasing. void setMaxAliasedPointSize(int sz) { if(sz<0)sz=0;_maxAliasedPointSize = sz; update(); } QString labelText() const { return d_labelText; }; void setLabelText(const QString& t) { d_labelText = t; update(); } QString valPrefix() const { return d_valPrefix; }; void setValPrefix(const QString& t) { d_valPrefix = t; update(); } QString valSuffix() const { return d_valSuffix; }; void setValSuffix(const QString& t) { d_valSuffix = t; update(); } QString specialValueText() const { return d_specialValueText; }; void setSpecialValueText(const QString& t) { d_specialValueText = t; update(); } QString offText() const { return d_offText; }; void setOffText(const QString& t) { d_offText = t; update(); } TextHighlightMode_t textHighlightMode() const { return _textHighlightMode; } void setTextHighlightMode(TextHighlightMode_t mode) { _textHighlightMode = mode; update(); } int valueDecimals() const { return _valueDecimals; } void setValueDecimals(int d) { if(d < 0) return; _valueDecimals = d; update(); } QString toolTipValueText(bool inclLabel, bool inclVal) const; bool showValue() const { return _showValue; } void setShowValue(bool show); bool hasOffMode() const { return _hasOffMode; } void setHasOffMode(bool v); bool isOff() const { return _off; } // Sets the off state and emits valueStateChanged signal if required. void setOff(bool v); // Both value and off state changed combined into one setter. // By default it is assumed that setting a value naturally implies resetting the 'off' state to false. // Emits valueChanged and valueStateChanged signals if required. // Note setOff and SliderBase::setValue are also available. void setValueState(double v, bool off = false, ConversionMode mode = ConvertDefault); QSize margins() const { return QSize(d_xMargin, d_yMargin); } int xMargin() const { return d_xMargin; } int yMargin() const { return d_yMargin; } void setMargins(QSize); void setMargins(int x, int y); void setXMargin(int x); void setYMargin(int y); int thumbWidthMargin() const { return d_thumbWidthMargin; } void setThumbWidthMargin(int m) { d_thumbWidthMargin = m; update(); } int thumbHitLength() const { return d_thumbHitLength; } void setThumbHitLength(int l) { d_thumbHitLength = l; } bool autoHideThumb() const { return _autoHideThumb; } void setAutoHideThumb(bool v) {_autoHideThumb = v; } virtual QSize sizeHint() const; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/configmidifilebase.ui000066400000000000000000000432441404105311000225470ustar00rootroot00000000000000 ConfigMidiFileBase 0 0 461 527 MusE: Config Midi File Import/Export true 1 Import Default instrument: 0 0 Qt::Horizontal 164 20 false Device Name metas trump Port metas if both exist Instrument Name metas trump Mode sysexes if both exist Split tracks into parts, or one single part Split tracks into &parts Alt+P Qt::Vertical 20 245 Export 0 0 Format: false 0 0 0 (single track) 1 (multiple tracks) Division: false 0 0 96 192 384 Qt::Horizontal 40 20 0 0 8 Note: Format 0 uses the FIRST midi track's name/comment in the arranger false 0 0 Copyright: false false Enable extended smf format (currently not implemented) Running Status saves space by not repeating event status bytes. If this is off every event will have a status byte, increasing file size. Use Running &Status Alt+S To turn a note off some devices can use note-offs or zero-velocity note-ons or both. When used with Running Status this setting saves space. It is safe to leave this off. Replace note-offs with &zero velocity note-ons Alt+Z Use &2-byte time signatures instead of standard 4 Alt+2 If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum note controllers. If disabled, drum maps are ignored during export. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. See "What's This ?" for more info. If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum controllers. Any Port overrides cause a separate track to be created (requires format 1). Any Channel overrides will still appear in the same track in the exported file, but upon re-import into an app such as MusE, MusE will treat that as a new track and create one. If disabled, drum maps are ignored. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. Upon re-importing into another app, it is up to the other app to redirect the particular drum ports, channels and notes to make the song sound like it was intended. Drum map overrides are unique to MusE. If re-importing into MusE, for faithful reproduction disable this setting and apply a drum map and some overrides later. If re-importing or opening in another midi player, enable this setting so that the player can have a chance at faithful reproduction. Some user adjustment may be required (choice of instruments, ports, etc). Apply drum map Port, Channel, ANote overrides If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. See "What's This ?" for more info. If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. The extra track is automatically created. In this mode all events on a track in the midi file are on the same channel. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. In this mode the track events can be on different channels. But if the file is re-imported into an app such as MusE, MusE will automatically create the extra track. Drum map Channel overrides go to a separate track Export instrument or mode: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter Instrument name metas Mode sysexes Qt::Horizontal 40 20 Export port or device metas: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter Port metas Device name metas Qt::Horizontal 40 20 Export a Port/Device meta for format 0 Qt::Vertical 20 12 6 0 0 0 0 Qt::Horizontal QSizePolicy::Expanding 20 20 &OK true true &Cancel true buttonOk clicked() ConfigMidiFileBase accept() 20 20 20 20 buttonCancel clicked() ConfigMidiFileBase reject() 20 20 20 20 muse-4.0.0/src/muse/components/confmport.cpp000066400000000000000000002141571404105311000211230ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: confmport.cpp,v 1.9.2.10 2009/12/15 03:39:58 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #ifndef _WIN32 #include #endif #include #include #include #include #include #include #include #include "config.h" #include "confmport.h" #include "app.h" #include "icons.h" #include "globals.h" #include "arranger.h" #include "midiport.h" #include "mididev.h" #include "midisyncimpl.h" #include "midifilterimpl.h" #include "ctrlcombo.h" #include "minstrument.h" #include "synth.h" #include "audio.h" #include "midiseq.h" #include "driver/alsamidi.h" #include "driver/jackmidi.h" #include "audiodev.h" #include "menutitleitem.h" #include "utils.h" #include "routepopup.h" #include "operations.h" #include "gconfig.h" #include "song.h" #include "undo.h" // Forwards from header: #include #include #include #include #include #include #include "popupmenu.h" #include "xml.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); // Undefine if and when multiple output routes are added to midi tracks. #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ namespace MusEGlobal { extern MusECore::SynthList synthis; } namespace MusEGui { //--------------------------------------------------------- // SynthItem //--------------------------------------------------------- SynthItem::SynthItem( bool hasUri, QTreeWidget* parent ) : QTreeWidgetItem(parent), _hasUri(hasUri) { } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void MPConfig::closeEvent(QCloseEvent *event) { apply(); QSettings settings; settings.setValue("MPConfig/geometry", saveGeometry()); QWidget::closeEvent(event); } //--------------------------------------------------------- // apply //--------------------------------------------------------- void MPConfig::apply() { MusEGlobal::audio->msgInitMidiDevices(false); // false = Don't force } //--------------------------------------------------------- // okClicked //--------------------------------------------------------- void MPConfig::okClicked() { close(); } //--------------------------------------------------------- // changeDefInputRoutes //--------------------------------------------------------- void MPConfig::changeDefInputRoutes(QAction* act) { QTableWidgetItem* item = mdevView->currentItem(); if(item == 0) return; QString id = mdevView->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if(no < 0 || no >= MusECore::MIDI_PORTS) return; int actid = act->data().toInt(); int allch = (1 << MusECore::MUSE_MIDI_CHANNELS) - 1; int defch = MusEGlobal::midiPorts[no].defaultInChannels(); MusECore::PendingOperationList operations; if(actid == MusECore::MUSE_MIDI_CHANNELS + 1) // Apply to all tracks now. { // Are there tracks, and is there a port device? // Tested: Hmm, allow ports with no device since that is a valid situation. if(!MusEGlobal::song->midis()->empty()) // && MusEGlobal::midiPorts[no].device()) { int ret = QMessageBox::question(this, tr("Default input connections"), tr("Are you sure you want to apply to all existing midi tracks now?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); if(ret == QMessageBox::Ok) { MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { MusECore::MidiTrack* mt = *it; MusECore::RouteList* rl = mt->inRoutes(); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case MusECore::Route::MIDI_PORT_ROUTE: // Remove all routes from this port to the tracks first. if(ir->midiPort == no) operations.add(MusECore::PendingOperationItem(*ir, MusECore::Route(mt, ir->channel), MusECore::PendingOperationItem::DeleteRoute)); break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } // All channels set or Omni? Use an Omni route: if(defch == -1 || defch == allch) operations.add(MusECore::PendingOperationItem(MusECore::Route(no), MusECore::Route(mt), MusECore::PendingOperationItem::AddRoute)); else // Add individual channels: for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { const int chbit = 1 << ch; if(defch & chbit) operations.add(MusECore::PendingOperationItem(MusECore::Route(no, ch), MusECore::Route(mt, ch), MusECore::PendingOperationItem::AddRoute)); } } } if(!operations.empty()) { operations.add(MusECore::PendingOperationItem((MusECore::TrackList*)NULL, MusECore::PendingOperationItem::UpdateSoloStates)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); // MusEGlobal::song->update(SC_ROUTE); } } } } else { int chbits; if(actid == MusECore::MUSE_MIDI_CHANNELS) // Toggle all. { chbits = (defch == -1 || defch == allch) ? 0 : allch; if(act->actionGroup()) { QList acts = act->actionGroup()->actions(); const int sz = acts.size(); for(int i = 0; i < sz; ++i) { QAction* a = acts.at(i); if(a) a->setChecked(chbits); } } } else { if(defch == -1) chbits = 0; else chbits = defch ^ (1 << actid); } MusEGlobal::midiPorts[no].setDefaultInChannels(chbits); mdevView->item(item->row(), DEVCOL_DEF_IN_CHANS)->setText(MusECore::bitmap2String(chbits)); } } //--------------------------------------------------------- // changeDefOutputRoutes //--------------------------------------------------------- void MPConfig::changeDefOutputRoutes(QAction* act) { QTableWidgetItem* item = mdevView->currentItem(); if(item == 0) return; QString id = mdevView->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if(no < 0 || no >= MusECore::MIDI_PORTS) return; int actid = act->data().toInt(); int defch = MusEGlobal::midiPorts[no].defaultOutChannels(); #ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ int allch = (1 << MusECore::MUSE_MIDI_CHANNELS) - 1; MusECore::PendingOperationList operations; #endif if(actid == MusECore::MUSE_MIDI_CHANNELS + 1) // Apply to all tracks now. { // Are there tracks, and is there a port device? // Tested: Hmm, allow ports with no device since that is a valid situation. if(!MusEGlobal::song->midis()->empty()) // && MusEGlobal::midiPorts[no].device()) { #ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(!defch) // No channels selected? Just return. return; #endif int ret = QMessageBox::question(this, tr("Default output connections"), tr("Are you sure you want to apply to all existing midi tracks now?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); if(ret == QMessageBox::Ok) { MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) if(defch & (1 << ch)) { MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; MusEGlobal::audio->msgIdle(true); for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { // Leave drum track channel at current setting. if((*it)->type() == MusECore::Track::DRUM) changed |= (*it)->setOutPortAndUpdate(no, false); else changed |= (*it)->setOutPortAndChannelAndUpdate(no, ch, false); } MusEGlobal::audio->msgIdle(false); MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); // Stop at the first output channel found. break; } #else for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { MusECore::MidiTrack* mt = *it; MusECore::RouteList* rl = mt->outRoutes(); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case MusECore::Route::MIDI_PORT_ROUTE: // Remove all routes from this port to the tracks first. if(ir->midiPort == no) operations.add(MusECore::PendingOperationItem(MusECore::Route(mt, ir->channel), *ir, MusECore::PendingOperationItem::DeleteRoute)); break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } // All channels set or Omni? Use an Omni route: if(defch == -1 || defch == allch) operations.add(MusECore::PendingOperationItem(MusECore::Route(mt), MusECore::Route(no), MusECore::PendingOperationItem::AddRoute)); else // Add individual channels: for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { const int chbit = 1 << ch; if(defch & chbit) operations.add(MusECore::PendingOperationItem(MusECore::Route(mt, ch), MusECore::Route(no, ch), MusECore::PendingOperationItem::AddRoute)); } } } if(!operations.empty()) { operations.add(MusECore::PendingOperationItem((MusECore::TrackList*)NULL, MusECore::PendingOperationItem::UpdateSoloStates)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); // MusEGlobal::song->update(SC_ROUTE); } #endif } } } else { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(actid < MusECore::MUSE_MIDI_CHANNELS) { int chbits = 1 << actid; // Are we toggling off? //if(chbits & defch) if(defch == -1 || chbits & defch) { // Just clear this port's default channels. MusEGlobal::midiPorts[no].setDefaultOutChannels(0); mdevView->item(item->row(), DEVCOL_DEF_OUT_CHANS)->setText(MusECore::bitmap2String(0)); } else { // Multiple out routes not supported. Make the setting exclusive to this port - exclude all other ports. MusECore::setPortExclusiveDefOutChan(no, chbits); int j = mdevView->rowCount(); for(int i = 0; i < j; ++i) mdevView->item(i, DEVCOL_DEF_OUT_CHANS)->setText(MusECore::bitmap2String(i == no ? chbits : 0)); // The group is exclusive. No need to iterate manually. // if(act->actionGroup()) // { // QList acts = act->actionGroup()->actions(); // const int sz = acts.size(); // if(sz > actid) // { // QAction* a = acts.at(actid); // if(a) // a->setChecked(true); // } // } } } #else int chbits; if(actid == MusECore::MUSE_MIDI_CHANNELS) // Toggle all. { chbits = (defch == -1 || defch == allch) ? 0 : allch; if(act->actionGroup()) { QList acts = act->actionGroup()->actions(); const int sz = acts.size(); for(int i = 0; i < sz; ++i) { QAction* a = acts.at(i); if(a) a->setChecked(chbits); } } } else { if(defch == -1) chbits = 0; else chbits = defch ^ (1 << actid); } MusEGlobal::midiPorts[no].setDefaultOutChannels(chbits); mdevView->item(item->row(), DEVCOL_DEF_OUT_CHANS)->setText(MusECore::bitmap2String(chbits)); #endif } } //--------------------------------------------------------- // DeviceItemRenamed //--------------------------------------------------------- void MPConfig::DeviceItemRenamed(QTableWidgetItem* item) { if(item == nullptr) return; if(!item->data(DeviceRole).canConvert()) return; MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); int col = item->column(); QTableWidgetItem* twi = item->tableWidget()->item(item->row(), INSTCOL_NAME); if(!twi) { fprintf(stderr, "synthesizerConfig::DeviceItemRenamed(): row:%d INSTCOL_NAME not found\n", item->row()); return; } QString new_name = twi->text(); // Get the original name. QString orig_name = md->name(); if(new_name == orig_name) return; MusECore::iMidiDevice imd; for(imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) { MusECore::MidiDevice* d = *imd; if(d == md || (*imd)->name() != new_name) continue; break; } switch(col) { case INSTCOL_NAME: { // Only Jack midi devices. if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) return; if(imd != MusEGlobal::midiDevices.end()) { QMessageBox::critical(this, tr("MusE: bad device name"), tr("Please choose a unique device name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); instanceList->blockSignals(true); item->setText(orig_name); instanceList->blockSignals(false); return; } MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures md->setName(new_name); MusEGlobal::audio->msgIdle(false); MusEGlobal::song->update(SC_CONFIG); } break; default: break; } } //--------------------------------------------------------- // rbClicked //--------------------------------------------------------- void MPConfig::rbClicked(QTableWidgetItem* item) { if (item == nullptr) return; QString id = item->tableWidget()->item(item->row(), DEVCOL_NO)->text(); int no = atoi(id.toLatin1().constData()) - 1; if (no < 0 || no >= MusECore::MIDI_PORTS) return; int n; MusECore::MidiPort* port = &MusEGlobal::midiPorts[no]; MusECore::MidiDevice* dev = port->device(); QTableWidget* listView = item->tableWidget(); QPoint ppt = listView->visualItemRect(item).bottomLeft(); int col = item->column(); ppt += QPoint(0, listView->horizontalHeader()->height()); ppt = listView->mapToGlobal(ppt); MusECore::PendingOperationList operations; switch (col) { case DEVCOL_DEF_IN_CHANS: { PopupMenu* pup = new PopupMenu(true); pup->addAction(new MenuTitleItem("Channel", pup)); QAction* act = nullptr; int chbits = MusEGlobal::midiPorts[no].defaultInChannels(); QActionGroup* ag = new QActionGroup(pup); ag->setExclusive(false); for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { act = ag->addAction(QString().setNum(i + 1)); act->setData(i); act->setCheckable(true); act->setChecked((1 << i) & chbits); } pup->addActions(ag->actions()); act = pup->addAction(tr("Toggle all")); act->setData(MusECore::MUSE_MIDI_CHANNELS); pup->addSeparator(); act = pup->addAction(tr("Change all tracks now")); act->setData(MusECore::MUSE_MIDI_CHANNELS + 1); // Enable only if there are tracks, and port has a device. // Allow ports with no device since that is a valid situation. act->setEnabled(!MusEGlobal::song->midis()->empty()); connect(pup, SIGNAL(triggered(QAction*)), SLOT(changeDefInputRoutes(QAction*))); pup->exec(QCursor::pos()); delete pup; } return; break; case DEVCOL_DEF_OUT_CHANS: { PopupMenu* pup = new PopupMenu(true); pup->addAction(new MenuTitleItem("Channel", pup)); QAction* act = 0; int chbits = MusEGlobal::midiPorts[no].defaultOutChannels(); QActionGroup* ag = new QActionGroup(pup); ag->setExclusive(true); act = ag->addAction("None"); act->setData(0); act->setCheckable(true); act->setChecked(chbits == 0); for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { act = ag->addAction(QString().setNum(i + 1)); act->setData(i); act->setCheckable(true); act->setChecked((1 << i) & chbits); } pup->addActions(ag->actions()); // Turn on if and when multiple output routes are supported. #ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ act = pup->addAction(tr("Toggle all")); act->setData(MusECore::MUSE_MIDI_CHANNELS); #endif pup->addSeparator(); act = pup->addAction(tr("Change all tracks now")); act->setData(MusECore::MUSE_MIDI_CHANNELS + 1); // Enable only if there are tracks, and port has a device. // Allow ports with no device since that is a valid situation. act->setEnabled(!MusEGlobal::song->midis()->empty()); connect(pup, SIGNAL(triggered(QAction*)), SLOT(changeDefOutputRoutes(QAction*))); pup->exec(QCursor::pos()); delete pup; } return; break; case DEVCOL_NAME: { // We clicked the 'down' button. PopupMenu* pup = new PopupMenu(false); QAction* act; // REMOVE Tim. Persistent routes. Added. Testing... QMenu* ctxmenu = pup->contextMenu(); act = ctxmenu->addAction(tr("Remove")); act->setData(0); connect(ctxmenu, SIGNAL(triggered(QAction*)), SLOT(deviceContextTriggered(QAction*))); // Could do it this way... //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" input")); //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" output")); //act = pup->addAction(tr("Create") + QT_TRANSLATE_NOOP("@default", " Jack") + tr(" combo")); // ... or keep it simple and let the user click on the green lights instead. act = pup->addAction(tr("Create Jack device")); act->setData(0); typedef std::map asmap; typedef std::map::iterator imap; asmap mapALSA; asmap mapJACK; asmap mapSYNTH; int aix = 0x10000000; int jix = 0x20000000; int six = 0x30000000; for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { if((*i)->deviceType() == MusECore::MidiDevice::ALSA_MIDI) { mapALSA.insert( std::pair (std::string((*i)->name().toLatin1().constData()), aix) ); ++aix; } else if((*i)->deviceType() == MusECore::MidiDevice::JACK_MIDI) { mapJACK.insert( std::pair (std::string((*i)->name().toLatin1().constData()), jix) ); ++jix; } else if((*i)->deviceType() == MusECore::MidiDevice::SYNTH_MIDI) { mapSYNTH.insert( std::pair (std::string((*i)->name().toLatin1().constData()), six) ); ++six; } else fprintf(stderr, "MPConfig::rbClicked unknown midi device: %s\n", (*i)->name().toLatin1().constData()); } if(!mapALSA.empty()) { pup->addSeparator(); pup->addAction(new MusEGui::MenuTitleItem("ALSA:", pup)); for(imap i = mapALSA.begin(); i != mapALSA.end(); ++i) { int idx = i->second; QString s(i->first.c_str()); MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::ALSA_MIDI); if(md) { if(md->deviceType() != MusECore::MidiDevice::ALSA_MIDI) continue; act = pup->addAction(md->name()); act->setData(idx); act->setCheckable(true); act->setChecked(md == dev); } } } if(!mapJACK.empty()) { pup->addSeparator(); pup->addAction(new MusEGui::MenuTitleItem("JACK:", pup)); for(imap i = mapJACK.begin(); i != mapJACK.end(); ++i) { int idx = i->second; QString s(i->first.c_str()); MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::JACK_MIDI); if(md) { if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) continue; act = pup->addAction(md->name()); act->setData(idx); act->setCheckable(true); act->setChecked(md == dev); } } } if(!mapSYNTH.empty()) { pup->addSeparator(); pup->addAction(new MusEGui::MenuTitleItem("SYNTH:", pup)); for(imap i = mapSYNTH.begin(); i != mapSYNTH.end(); ++i) { int idx = i->second; QString s(i->first.c_str()); MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::SYNTH_MIDI); if(md) { if(md->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) continue; act = pup->addAction(md->name()); act->setData(idx); act->setCheckable(true); act->setChecked(md == dev); } } } act = pup->exec(ppt); if(!act) { delete pup; return; } n = act->data().toInt(); delete pup; MusECore::MidiDevice* sdev = 0; if(n < 0x10000000) { if(n <= 2) { sdev = MusECore::MidiJackDevice::createJackMidiDevice(); if(sdev) { int of = 3; switch(n) { case 0: of = 3; break; case 1: of = 2; break; case 2: of = 1; break; } sdev->setOpenFlags(of); } } } else { int typ; if(n < 0x20000000) typ = MusECore::MidiDevice::ALSA_MIDI; else if(n < 0x30000000) typ = MusECore::MidiDevice::JACK_MIDI; else //if(n < 0x40000000) typ = MusECore::MidiDevice::SYNTH_MIDI; sdev = MusEGlobal::midiDevices.find(act->text(), typ); // Is it the current device? Reset it to . if(sdev == dev) sdev = 0; } MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { MusECore::MidiTrack* mt = *it; MusECore::RouteList* rl = mt->inRoutes(); for(MusECore::iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case MusECore::Route::MIDI_PORT_ROUTE: // Remove track routes from an existing port already using the selected device. // Remove all track routes from this port. if((sdev && ir->midiPort == sdev->midiPort()) || (ir->midiPort == no)) operations.add(MusECore::PendingOperationItem(*ir, MusECore::Route(mt, ir->channel), MusECore::PendingOperationItem::DeleteRoute)); break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } } rl = mt->outRoutes(); for(MusECore::iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case MusECore::Route::MIDI_PORT_ROUTE: // Remove track routes to an existing port already using the selected device. // Remove all track routes to this port. if((sdev && ir->midiPort == sdev->midiPort()) || (ir->midiPort == no)) operations.add(MusECore::PendingOperationItem(MusECore::Route(mt, ir->channel), *ir, MusECore::PendingOperationItem::DeleteRoute)); break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } } } MusEGlobal::audio->msgSetMidiDevice(port, sdev); // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); // save configuration file // Add all track routes to/from this port... if(sdev) { const int allch = (1 << MusECore::MUSE_MIDI_CHANNELS) - 1; const int i_chbits = MusEGlobal::midiPorts[no].defaultInChannels(); const int o_chbits = MusEGlobal::midiPorts[no].defaultOutChannels(); // Connect all the specified routes. Do not add input routes to synths. if((i_chbits || o_chbits) && !sdev->isSynti()) { for(MusECore::iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { MusECore::MidiTrack* mt = *it; // All channels set or Omni? Use an Omni route: if(i_chbits == -1 || i_chbits == allch) operations.add(MusECore::PendingOperationItem(MusECore::Route(no), MusECore::Route(mt), MusECore::PendingOperationItem::AddRoute)); else // Add individual channels: for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { const int chbit = 1 << ch; if(i_chbits & chbit) operations.add(MusECore::PendingOperationItem(MusECore::Route(no, ch), MusECore::Route(mt, ch), MusECore::PendingOperationItem::AddRoute)); } #ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // All channels set or Omni? Use an Omni route: if(o_chbits == -1 || o_chbits == allch) operations.add(MusECore::PendingOperationItem(MusECore::Route(mt), MusECore::Route(no), MusECore::PendingOperationItem::AddRoute)); else // Add individual channels: for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { const int chbit = 1 << ch; if(o_chbits & chbit) operations.add(MusECore::PendingOperationItem(MusECore::Route(mt, ch), MusECore::Route(no, ch), MusECore::PendingOperationItem::AddRoute)); } #endif } } } } // Do these always, regardless of operations - the device has changed. operations.add(MusECore::PendingOperationItem((MusECore::TrackList*)NULL, MusECore::PendingOperationItem::UpdateSoloStates)); //if(!operations.empty()) MusEGlobal::audio->msgExecutePendingOperations(operations, true, SC_EVERYTHING); // MusEGlobal::song->update(); return; break; case DEVCOL_INSTR: { //if (dev && dev->isSynti()) // return; PopupMenu* pup = new PopupMenu(false); //MusECore::MidiInstrument::populateInstrPopup(pup, no, false); MusECore::MidiInstrument::populateInstrPopup(pup, no, true); if(pup->actions().count() == 0) { delete pup; return; } QAction* act = pup->exec(ppt); if(!act) { delete pup; return; } QString s = act->text(); const int actid = act->data().toInt(); delete pup; // Edit instrument if(actid == 100) { MusECore::MidiInstrument* instr = port->instrument(); MusEGlobal::muse->startEditInstrument(instr && !instr->isSynti() ? instr->iname() : QString()); } else { item->tableWidget()->item(item->row(), DEVCOL_INSTR)->setText(s); for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { if ((*i)->iname() == s) { MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures port->changeInstrument(*i); MusEGlobal::audio->msgIdle(false); MusEGlobal::song->update(SC_MIDI_INSTRUMENT); break; } } } } return; break; } } //--------------------------------------------------------- // MPConfig::setToolTip //--------------------------------------------------------- void MPConfig::setToolTip(QTableWidgetItem *item, int col) { switch (col) { case DEVCOL_NO: item->setToolTip(tr("Port number")); break; case DEVCOL_INSTR: item->setToolTip(tr("Port instrument")); break; case DEVCOL_NAME: item->setToolTip(tr("Midi device name. Click to edit (Jack)")); break; case DEVCOL_DEF_IN_CHANS: item->setToolTip(tr("Auto-connect these channels to new midi tracks")); break; #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ case DEVCOL_DEF_OUT_CHANS: item->setToolTip(tr("Auto-connect new midi tracks to this channel")); break; #else case DEVCOL_DEF_OUT_CHANS: item->setToolTip(tr("Auto-connect new midi tracks to these channels")); break; #endif default: return; } } //--------------------------------------------------------- // MPConfig::setWhatsThis //--------------------------------------------------------- void MPConfig::setWhatsThis(QTableWidgetItem *item, int col) { switch (col) { case DEVCOL_NO: item->setWhatsThis(tr("Port Number")); break; case DEVCOL_NAME: item->setWhatsThis(tr("Name of the midi device associated with" " this port number. Click to edit Jack midi name.")); break; case DEVCOL_INSTR: item->setWhatsThis(tr("Instrument connected to port")); break; case DEVCOL_DEF_IN_CHANS: item->setWhatsThis(tr("Auto-connect these channels, on this port, to new midi tracks.")); break; case DEVCOL_DEF_OUT_CHANS: #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ item->setWhatsThis(tr("Connect new midi tracks to this channel, on this port.")); break; #else item->setWhatsThis(tr("Connect new midi tracks to these channels, on this port.")); break; #endif default: break; } } //--------------------------------------------------------- // MPConfig::setInstToolTip //--------------------------------------------------------- void MPConfig::setInstToolTip(QTableWidgetItem *item, int col) { switch (col) { case INSTCOL_NAME: item->setToolTip(tr("Midi device name")); break; case INSTCOL_TYPE: item->setToolTip(tr("Midi device type")); break; case INSTCOL_STATE: item->setToolTip(tr("Device state")); break; case INSTCOL_REC: item->setToolTip(tr("Enable reading")); break; case INSTCOL_PLAY: item->setToolTip(tr("Enable writing")); break; case INSTCOL_GUI: item->setToolTip(tr("Enable gui")); break; case INSTCOL_INROUTES: item->setToolTip(tr("Connections from Jack Midi")); break; case INSTCOL_OUTROUTES: item->setToolTip(tr("Connections to Jack Midi")); break; default: break; } } //--------------------------------------------------------- // MPConfig::setInstWhatsThis //--------------------------------------------------------- void MPConfig::setInstWhatsThis(QTableWidgetItem *item, int col) { switch (col) { case INSTCOL_NAME: item->setWhatsThis(tr("Midi device name")); break; case INSTCOL_TYPE: item->setWhatsThis(tr("Midi device type")); break; case INSTCOL_STATE: item->setWhatsThis(tr("Result of opening the device:\n" "OK: Assigned to a port and in use\n" "Closed: Unassigned to a port, or closed\n" "R/W Error: Unable to open for read or write\n" "Unavailable: USB midi unplugged, or external\n" " application not running, or synth plugin\n" " not installed etc.\n" "(Jack Midi devices have 'unavailable ports'\n" " in the routes columns.)\n" "Unavailable devices or ports can be purged\n" " with 'Remove' or with the advanced router.")); break; case INSTCOL_REC: item->setWhatsThis(tr("Enable reading from device")); break; case INSTCOL_PLAY: item->setWhatsThis(tr("Enable writing to device")); break; case INSTCOL_GUI: item->setWhatsThis(tr("Enable Graphical User Interface for device")); break; case INSTCOL_INROUTES: item->setWhatsThis(tr("Connections from Jack Midi ports")); break; case INSTCOL_OUTROUTES: item->setWhatsThis(tr("Connections to Jack Midi ports")); break; default: break; } } //--------------------------------------------------------- // MPConfig::addItem() //--------------------------------------------------------- void MPConfig::addItem(int row, int col, QTableWidgetItem *item, QTableWidget *table) { setWhatsThis(item, col); table->setItem(row, col, item); } //--------------------------------------------------------- // MPConfig::addInstItem() //--------------------------------------------------------- void MPConfig::addInstItem(int row, int col, QTableWidgetItem *item, QTableWidget *table) { setInstWhatsThis(item, col); table->setItem(row, col, item); } //--------------------------------------------------------- // MPConfig // Midi Port Config //--------------------------------------------------------- MPConfig::MPConfig(QWidget* parent) : QDialog(parent) { setupUi(this); QSettings settings; restoreGeometry(settings.value("MPConfig/geometry").toByteArray()); mdevView->setRowCount(MusECore::MIDI_PORTS); mdevView->verticalHeader()->hide(); mdevView->setShowGrid(false); _showAliases = 1; // 0: Show second aliases, if available. QStringList columnnames; columnnames << tr("Port") << tr("Device Name") << tr("Instrument") << tr("Def in ch") << tr("Def out ch"); mdevView->setColumnCount(columnnames.size()); mdevView->setHorizontalHeaderLabels(columnnames); for (int i = 0; i < columnnames.size(); ++i) { setWhatsThis(mdevView->horizontalHeaderItem(i), i); setToolTip(mdevView->horizontalHeaderItem(i), i); } mdevView->setFocusPolicy(Qt::NoFocus); instanceList->verticalHeader()->hide(); instanceList->setShowGrid(false); columnnames.clear(); columnnames << tr("Device Name") << tr("Type") << tr("I") << tr("O") << tr("GUI") << tr("In") << tr("Out") << tr("State"); #ifdef ALSA_SUPPORT addALSADevice->setChecked(MusEGlobal::midiSeq != nullptr); #else addALSADevice->setVisible(false); #endif instanceList->setColumnCount(columnnames.size()); instanceList->setHorizontalHeaderLabels(columnnames); for (int i = 0; i < columnnames.size(); ++i) { setInstWhatsThis(instanceList->horizontalHeaderItem(i), i); setInstToolTip(instanceList->horizontalHeaderItem(i), i); } guiTimer = new QTimer(this); connect(guiTimer, &QTimer::timeout, this, &MPConfig::checkGUIState); guiTimer->start(500); ledOn = *ledGreenSVGIcon; ledOff = *ledOffSVGIcon; connect(instanceList, SIGNAL(itemPressed(QTableWidgetItem*)), SLOT(deviceItemClicked(QTableWidgetItem*))); connect(instanceList, SIGNAL(itemSelectionChanged()), SLOT(deviceSelectionChanged())); connect(instanceList, SIGNAL(itemChanged(QTableWidgetItem*)), SLOT(DeviceItemRenamed(QTableWidgetItem*))); connect(addJACKDevice, SIGNAL(clicked(bool)), SLOT(addJackDeviceClicked())); connect(addALSADevice, SIGNAL(clicked(bool)), SLOT(addAlsaDeviceClicked(bool))); connect(mdevView, SIGNAL(itemPressed(QTableWidgetItem*)), this, SLOT(rbClicked(QTableWidgetItem*))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(synthList, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); connect(addSynthDevice, SIGNAL(clicked()), SLOT(addInstanceClicked())); connect(synthList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(addInstanceClicked())); connect(renameDevice, SIGNAL(clicked()), SLOT(renameInstanceClicked())); connect(removeDevice, SIGNAL(clicked()), SLOT(removeInstanceClicked())); connect(applyButton, SIGNAL(clicked()), SLOT(apply())); connect(okButton, SIGNAL(clicked()), SLOT(okClicked())); songChanged(SC_CONFIG); } MPConfig::~MPConfig() { } void MPConfig::checkGUIState() { for (int i = 0; i < instanceList->rowCount(); i++) { auto item = instanceList->item(i, INSTCOL_GUI); if (!item->data(DeviceRole).canConvert()) continue; MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); MusECore::SynthI* synth = nullptr; if (md->isSynti()) synth = static_cast(md); if (synth && synth->hasNativeGui()) item->setIcon(synth->nativeGuiVisible() ? ledOn : ledOff); } } //--------------------------------------------------------- // selectionChanged //--------------------------------------------------------- void MPConfig::selectionChanged() { addSynthDevice->setEnabled(synthList->selectedItems().isEmpty() ? false : (bool)synthList->currentItem()); } //--------------------------------------------------------- // deviceSelectionChanged //--------------------------------------------------------- void MPConfig::deviceSelectionChanged() { DEBUG_PRST_ROUTES(stderr, "synthesizerConfig::deviceSelectionChanged() currentItem:%p\n", instanceList->currentItem()); bool can_remove = false; bool can_rename = false; int rowSelCount = 0; const int sz = instanceList->rowCount(); for(int i = 0; i < sz; ++i) { QTableWidgetItem* item = instanceList->item(i, INSTCOL_NAME); if(!item || !item->data(DeviceRole).canConvert() || !item->isSelected()) continue; MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); if(!md) continue; ++rowSelCount; switch(md->deviceType()) { // TODO: For now, don't allow creating/removing/renaming them until we decide on addressing strategy. case MusECore::MidiDevice::ALSA_MIDI: // Allow removing ('purging') an unavailable ALSA device. if(md->isAddressUnknown()) can_remove = true; break; case MusECore::MidiDevice::JACK_MIDI: can_remove = true; can_rename = true; break; case MusECore::MidiDevice::SYNTH_MIDI: can_remove = true; break; } // Optimize: No need to check further. if(can_rename && can_remove && rowSelCount >= 2) break; } // Only one rename at a time, for now... renameDevice->setEnabled(can_rename && rowSelCount == 1); removeDevice->setEnabled(can_remove); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MPConfig::songChanged(MusECore::SongChangedStruct_t flags) { if(!(flags & (SC_CONFIG | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_MIDI_INSTRUMENT))) return; #ifdef ALSA_SUPPORT addALSADevice->blockSignals(true); addALSADevice->setChecked(MusEGlobal::midiSeq != NULL); addALSADevice->blockSignals(false); #endif // Get currently selected index... int no = -1; QTableWidgetItem* sitem = mdevView->currentItem(); if(sitem) { QString id = sitem->tableWidget()->item(sitem->row(), DEVCOL_NO)->text(); no = atoi(id.toLatin1().constData()) - 1; if(no < 0 || no >= MusECore::MIDI_PORTS) no = -1; } sitem = nullptr; mdevView->blockSignals(true); mdevView->clearContents(); int defochs = 0; for (int i = MusECore::MIDI_PORTS-1; i >= 0; --i) { mdevView->blockSignals(true); // otherwise itemChanged() is triggered and bad things happen. MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* dev = port->device(); QString s; s.setNum(i+1); QTableWidgetItem* itemno = new QTableWidgetItem(s); addItem(i, DEVCOL_NO, itemno, mdevView); itemno->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); itemno->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* iteminstr = new QTableWidgetItem(port->instrument() ? port->instrument()->iname() : tr("")); addItem(i, DEVCOL_INSTR, iteminstr, mdevView); iteminstr->setFlags(Qt::ItemIsEnabled); QTableWidgetItem* itemname = new QTableWidgetItem; addItem(i, DEVCOL_NAME, itemname, mdevView); itemname->setFlags(Qt::ItemIsEnabled); // Ignore synth devices. Default input routes make no sense for them (right now). QTableWidgetItem* itemdefin = new QTableWidgetItem((dev && dev->isSynti()) ? QString() : MusECore::bitmap2String(port->defaultInChannels())); addItem(i, DEVCOL_DEF_IN_CHANS, itemdefin, mdevView); // Enabled: Use editor (not good). Disabled: Use pop-up menu. #if 0 itemdefin->setFlags((dev && dev->isSynti()) ? Qt::NoItemFlags : Qt::ItemIsEditable | Qt::ItemIsEnabled); # else if(dev && dev->isSynti()) itemdefin->setFlags(Qt::NoItemFlags); else { itemdefin->setFlags(Qt::ItemIsEnabled); itemdefin->setIcon(*dropDownTriangleSVGIcon); } #endif QTableWidgetItem* itemdefout = new QTableWidgetItem(MusECore::bitmap2String(0)); defochs = port->defaultOutChannels(); if(defochs) { for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if(defochs & (1 << ch)) { itemdefout->setText(QString().setNum(ch + 1)); break; } } } addItem(i, DEVCOL_DEF_OUT_CHANS, itemdefout, mdevView); itemdefout->setFlags(Qt::ItemIsEnabled); itemdefout->setIcon(*dropDownTriangleSVGIcon); if(dev && dev->isSynti()) //make deleted audio softsynths not show in ports dialog { MusECore::AudioTrack *_track = static_cast(static_cast(dev)); MusECore::TrackList* tl = MusEGlobal::song->tracks(); if(tl->find(_track) == tl->end()) { for(int __col = 0; __col < mdevView->columnCount(); ++__col) { mdevView->item(i, __col)->setFlags(Qt::NoItemFlags); } } } mdevView->blockSignals(false); if (dev) { itemname->setText(dev->name()); } else { itemname->setText(tr("")); } //if (!(dev && dev->isSynti())) iteminstr->setIcon(*dropDownTriangleSVGIcon); itemname->setIcon(*dropDownTriangleSVGIcon); if(i == no) sitem = itemno; } if(sitem) mdevView->setCurrentItem(sitem); mdevView->blockSignals(false); QString s; synthList->blockSignals(true); synthList->clear(); for (std::vector::iterator i = MusEGlobal::synthis.begin(); i != MusEGlobal::synthis.end(); ++i) { SynthItem* item = new SynthItem(!(*i)->uri().isEmpty(), synthList); if((*i)->uri().isEmpty()) item->setText(0, QString((*i)->baseName())); else item->setText(0, QString((*i)->uri())); item->setText(1, MusECore::synthType2String((*i)->synthType())); s.setNum((*i)->instances()); item->setText(2, s); item->setText(3, QString((*i)->name())); item->setText(4, QString((*i)->version())); item->setText(5, QString((*i)->description())); } synthList->blockSignals(false); instanceList->blockSignals(true); instanceList->clearContents(); instanceList->setRowCount(MusEGlobal::midiDevices.size()); int row_cnt = 0; for (MusECore::iMidiDevice imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) { MusECore::MidiDevice* md = *imd; MusECore::SynthI* synth = nullptr; if(md->isSynti()) synth = static_cast(md); QTableWidgetItem* iitem = new QTableWidgetItem(md->name()); iitem->setData(DeviceRole, QVariant::fromValue(md)); // Is it a Jack midi device? Allow renaming. if(md->deviceType() == MusECore::MidiDevice::JACK_MIDI) iitem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); else iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); addInstItem(row_cnt, INSTCOL_NAME, iitem, instanceList); iitem = new QTableWidgetItem(md->deviceTypeString()); iitem->setData(DeviceRole, QVariant::fromValue(md)); iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); addInstItem(row_cnt, INSTCOL_TYPE, iitem, instanceList); iitem = new QTableWidgetItem; iitem->setData(DeviceRole, QVariant::fromValue(md)); iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); iitem->setTextAlignment(Qt::AlignCenter); addInstItem(row_cnt, INSTCOL_REC, iitem, instanceList); if(md->rwFlags() & 0x2) iitem->setIcon(md->openFlags() & 2 ? ledOn : ledOff); else iitem->setIcon(QIcon(QPixmap())); iitem = new QTableWidgetItem; iitem->setData(DeviceRole, QVariant::fromValue(md)); iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); iitem->setTextAlignment(Qt::AlignCenter); addInstItem(row_cnt, INSTCOL_PLAY, iitem, instanceList); if(md->rwFlags() & 0x1) iitem->setIcon(md->openFlags() & 1 ? ledOn : ledOff); else iitem->setIcon(QIcon(QPixmap())); iitem = new QTableWidgetItem; iitem->setData(DeviceRole, QVariant::fromValue(md)); iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); iitem->setTextAlignment(Qt::AlignCenter); addInstItem(row_cnt, INSTCOL_GUI, iitem, instanceList); if(synth && synth->hasNativeGui()) iitem->setIcon(synth->nativeGuiVisible() ? ledOn : ledOff); else iitem->setIcon(QIcon(QPixmap())); QTableWidgetItem* or_item = new QTableWidgetItem; or_item->setData(DeviceRole, QVariant::fromValue(md)); or_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); QTableWidgetItem* ir_item = new QTableWidgetItem; ir_item->setData(DeviceRole, QVariant::fromValue(md)); ir_item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); if(md->deviceType() == MusECore::MidiDevice::JACK_MIDI) { if(md->rwFlags() & 1) or_item->setIcon(*routeOutMidiSVGIcon); else or_item->setIcon(QIcon()); if(md->rwFlags() & 2) ir_item->setIcon(*routeInMidiSVGIcon); else ir_item->setIcon(QIcon()); } addInstItem(row_cnt, INSTCOL_OUTROUTES, or_item, instanceList); addInstItem(row_cnt, INSTCOL_INROUTES, ir_item, instanceList); iitem = new QTableWidgetItem(md->state()); iitem->setData(DeviceRole, QVariant::fromValue(md)); iitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); iitem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); addInstItem(row_cnt, INSTCOL_STATE, iitem, instanceList); ++row_cnt; } instanceList->blockSignals(false); instanceList->resizeColumnToContents(INSTCOL_NAME); instanceList->resizeColumnToContents(INSTCOL_TYPE); instanceList->resizeColumnToContents(INSTCOL_STATE); instanceList->resizeColumnToContents(INSTCOL_REC); instanceList->resizeColumnToContents(INSTCOL_PLAY); instanceList->resizeColumnToContents(INSTCOL_GUI); instanceList->resizeColumnToContents(INSTCOL_OUTROUTES); instanceList->resizeColumnToContents(INSTCOL_INROUTES); instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_REC, QHeaderView::Fixed); instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_PLAY, QHeaderView::Fixed); instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_GUI, QHeaderView::Fixed); instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_OUTROUTES, QHeaderView::Fixed); instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_INROUTES, QHeaderView::Fixed); //instanceList->horizontalHeader()->setStretchLastSection( false ); instanceList->horizontalHeader()->setSectionResizeMode(INSTCOL_STATE, QHeaderView::Stretch); instanceList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter); deviceSelectionChanged(); synthList->resizeColumnToContents(1); mdevView->resizeColumnsToContents(); mdevView->horizontalHeader()->setSectionResizeMode(DEVCOL_NO ,QHeaderView::Fixed); mdevView->horizontalHeader()->setStretchLastSection( true ); selectionChanged(); } //--------------------------------------------------------- // addInstanceClicked //--------------------------------------------------------- void MPConfig::addInstanceClicked() { SynthItem* item = static_cast(synthList->currentItem()); if (item == nullptr) return; // Add at end of list. MusECore::SynthI *si = MusEGlobal::song->createSynthI( !item->hasUri() ? item->text(0) : QString(), item->hasUri() ? item->text(0) : QString(), item->text(3), MusECore::string2SynthType(item->text(1))); if(!si) return; // add instance last in midi device list for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* dev = port->device(); if (dev == nullptr) { // This is a brand new instance. Set the instrument as well for convenience. MusEGlobal::audio->msgSetMidiDevice(port, si, si); // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); // save configuration file MusEGlobal::song->update(); break; } } } //--------------------------------------------------------- // removeInstanceClicked //--------------------------------------------------------- void MPConfig::removeInstanceClicked() { const int sz = instanceList->rowCount(); if(sz == 0) return; bool doupd = false; // Two passes: One for synths and one for all others (so far). // // Others: // bool isIdle = false; for(int i = 0; i < sz; ++i) { QTableWidgetItem* item = instanceList->item(i, INSTCOL_NAME); if(!item || !item->data(DeviceRole).canConvert() || !item->isSelected()) continue; MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); if(!md) continue; switch(md->deviceType()) { // TODO: For now, don't allow creating/removing/renaming them until we decide on addressing strategy. case MusECore::MidiDevice::ALSA_MIDI: // Allow removing ('purging') an unavailable ALSA device. if(!md->isAddressUnknown()) break; // Fall through. case MusECore::MidiDevice::JACK_MIDI: if(!isIdle) { MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures isIdle = true; } if(md->midiPort() != -1) MusEGlobal::midiPorts[md->midiPort()].setMidiDevice(0); //MusEGlobal::midiDevices.erase(imd); MusEGlobal::midiDevices.remove(md); break; case MusECore::MidiDevice::SYNTH_MIDI: break; } } if(isIdle) { MusEGlobal::audio->msgIdle(false); // Defer update until end, otherwise instanceList is wiped and // rebuilt upon songChanged so next section won't work! doupd = true; } // // Synths: // MusECore::Undo operations; for(int i = 0; i < sz; ++i) { QTableWidgetItem* item = instanceList->item(i, INSTCOL_NAME); if(!item || !item->data(DeviceRole).canConvert() || !item->isSelected()) continue; MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); if(!md) continue; switch(md->deviceType()) { case MusECore::MidiDevice::ALSA_MIDI: case MusECore::MidiDevice::JACK_MIDI: break; case MusECore::MidiDevice::SYNTH_MIDI: { MusECore::SynthI* s = dynamic_cast(md); if(s) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(s), s)); } break; } } if(!operations.empty()) MusEGlobal::song->applyOperationGroup(operations); if(doupd) MusEGlobal::song->update(SC_CONFIG); } //--------------------------------------------------------- // renameInstanceClicked //--------------------------------------------------------- void MPConfig::renameInstanceClicked() { QTableWidgetItem* item = instanceList->currentItem(); if(!item) return; item = instanceList->item(item->row(), INSTCOL_NAME); if(!item) return; // FIXME: How to know if the table is already in edit mode? The useful state() method is protected, // and there don't appear to be any signals we can use. if(item->flags().testFlag(Qt::ItemIsEditable) && item->flags().testFlag(Qt::ItemIsEnabled)) instanceList->editItem(item); } //--------------------------------------------------------- // deviceItemClicked //--------------------------------------------------------- void MPConfig::deviceItemClicked(QTableWidgetItem* item) { if(!item) return; const int col = item->column(); if(!item->data(DeviceRole).canConvert()) return; MusECore::MidiDevice* md = static_cast(item->data(DeviceRole).value()); MusECore::SynthI* synth = nullptr; if(md->isSynti()) synth = static_cast(md); int rwFlags = md->rwFlags(); int openFlags = md->openFlags(); switch(col) { case INSTCOL_REC: if(!(rwFlags & 2)) return; openFlags ^= 0x2; MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures md->setOpenFlags(openFlags); if(md->midiPort() != -1) MusEGlobal::midiPorts[md->midiPort()].setMidiDevice(md); // reopen device // FIXME: This causes jack crash with R+W Jack midi device MusEGlobal::audio->msgIdle(false); item->setIcon(openFlags & 2 ? ledOn : ledOff); return; case INSTCOL_PLAY: if(!(rwFlags & 1)) return; openFlags ^= 0x1; MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures md->setOpenFlags(openFlags); if(md->midiPort() != -1) MusEGlobal::midiPorts[md->midiPort()].setMidiDevice(md); // reopen device FIXME: This causes jack crash with R+W Jack midi device MusEGlobal::audio->msgIdle(false); item->setIcon(openFlags & 1 ? ledOn : ledOff); return; case INSTCOL_GUI: if(synth && synth->hasNativeGui()) { synth->showNativeGui(!synth->nativeGuiVisible()); item->setIcon(synth->nativeGuiVisible() ? ledOn : ledOff); } return; case INSTCOL_INROUTES: case INSTCOL_OUTROUTES: { if(!MusEGlobal::checkAudioDevice()) return; if(MusEGlobal::audioDevice->deviceType() != MusECore::AudioDevice::JACK_AUDIO) // Only if Jack is running. return; if(!md) return; // Only Jack midi devices. if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) return; if(!(md->rwFlags() & ((col == INSTCOL_OUTROUTES) ? 1 : 2))) return; RoutePopupMenu* pup = new RoutePopupMenu(); pup->exec(QCursor::pos(), md, col == INSTCOL_OUTROUTES); delete pup; } return; } } //--------------------------------------------------------- // addJackDeviceClicked //--------------------------------------------------------- void MPConfig::addJackDeviceClicked() { MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures // This automatically adds the device to the midiDevices list. MusECore::MidiDevice* md = MusECore::MidiJackDevice::createJackMidiDevice(); if(md) md->setOpenFlags(3); // Start with open read + write. MusEGlobal::audio->msgIdle(false); if(md) MusEGlobal::song->update(SC_CONFIG); } //--------------------------------------------------------- // addAlsaDeviceClicked //--------------------------------------------------------- void MPConfig::addAlsaDeviceClicked(bool v) { MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures MusEGlobal::config.enableAlsaMidiDriver = v; // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. //MusEGlobal::muse->changeConfig(true); // Save settings? No, wait till close. if(v) { // Initialize the ALSA driver. This will automatically initialize the sequencer thread if necessary. MusECore::initMidiAlsa(); if(MusEGlobal::midiSeq) { // Now start the sequencer if necessary. Prio unused, set in start. MusEGlobal::midiSeq->start(0); // Update the timer poll file descriptors. MusEGlobal::midiSeq->msgUpdatePollFd(); } MusEGlobal::audio->msgIdle(false); // Scan for any changes in ALSA. FIXME: Note there's another another idle here ! MusECore::alsaScanMidiPorts(); // Inform the rest of the app's gui. MusEGlobal::song->update(SC_CONFIG); } else { // Exit ALSA midi. MusECore::exitMidiAlsa(); MusEGlobal::audio->msgIdle(false); // Scan for any changes in ALSA. FIXME: Note there's another another idle here ! MusECore::alsaScanMidiPorts(); if(MusEGlobal::midiSeq) { MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures MusEGlobal::midiSeq->msgUpdatePollFd(); MusEGlobal::midiSeq->stop(true); MusECore::exitMidiSequencer(); MusEGlobal::audio->msgIdle(false); } // Inform the rest of the app's gui. MusEGlobal::song->update(SC_CONFIG); } } //--------------------------------------------------------- // beforeDeviceContextShow //--------------------------------------------------------- void MPConfig::beforeDeviceContextShow(PopupMenu* /*menu*/, QAction* /*menuAction*/, QMenu* /*ctxMenu*/) { DEBUG_PRST_ROUTES(stderr, "MPConfig::beforeDeviceContextShow\n"); } //--------------------------------------------------------- // deviceContextTriggered //--------------------------------------------------------- void MPConfig::deviceContextTriggered(QAction* act) { DEBUG_PRST_ROUTES(stderr, "MPConfig::deviceRemoveTriggered:%s\n", act->text().toLatin1().constData()); if(act) { // TODO: Work in progress. // // PopupMenu* menu = 0; // QAction* action = 0; // QVariant var_val = 0; // // menu = act->data().value().menu(); // action = act->data().value().action(); // var_val = act->data().value().varValue(); // // DEBUG_PRST_ROUTES(stderr, " menu:%p action:%p var:%x md:%p\n", menu, action, var_val.toInt(), MusEGlobal::midiDevices.find(action->text()); } } //--------------------------------------------------------- // configMidiPorts //--------------------------------------------------------- void MusE::configMidiPorts() { if (!midiPortConfig) { midiPortConfig = new MusEGui::MPConfig(this); } midiPortConfig->show(); midiPortConfig->raise(); midiPortConfig->activateWindow(); } } // namespace MusEGui muse-4.0.0/src/muse/components/confmport.h000066400000000000000000000075141404105311000205650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: confmport.h,v 1.3 2004/01/25 11:20:31 wschweer Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CONFMPORT_H__ #define __CONFMPORT_H__ #include #include "ui_synthconfigbase.h" #include "type_defs.h" #include "synth.h" // Forward declarations: class QWidget; class QTreeWidget; class QTableWidget; class QMenu; class QAction; namespace MusECore { class Xml; } namespace MusEGui { class PopupMenu; //--------------------------------------------------------- // SynthItem //--------------------------------------------------------- class SynthItem : public QTreeWidgetItem { private: bool _hasUri; public: SynthItem( bool hasUri, QTreeWidget* parent = nullptr ); bool hasUri() const { return _hasUri; } }; //--------------------------------------------------------- // MPConfig // Midi Port Config //--------------------------------------------------------- class MPConfig : public QDialog, Ui::SynthConfigBase { Q_OBJECT enum InstanceRoles { DeviceRole = Qt::UserRole, DeviceTypeRole = Qt::UserRole + 1}; enum DeviceColumns { DEVCOL_NO = 0, DEVCOL_NAME, DEVCOL_INSTR, DEVCOL_DEF_IN_CHANS, DEVCOL_DEF_OUT_CHANS }; enum InstanceColumns { INSTCOL_NAME = 0, INSTCOL_TYPE, INSTCOL_REC, INSTCOL_PLAY, INSTCOL_GUI, INSTCOL_INROUTES, INSTCOL_OUTROUTES, INSTCOL_STATE }; PopupMenu* defpup; int _showAliases; // -1: None. 0: First aliases. 1: Second aliases etc. QTimer *guiTimer; QIcon ledOn, ledOff; void setWhatsThis(QTableWidgetItem *item, int col); void setToolTip(QTableWidgetItem *item, int col); void setInstWhatsThis(QTableWidgetItem *item, int col); void setInstToolTip(QTableWidgetItem *item, int col); void addItem(int row, int col, QTableWidgetItem *item, QTableWidget *table); void addInstItem(int row, int col, QTableWidgetItem *item, QTableWidget *table); private slots: void rbClicked(QTableWidgetItem*); void DeviceItemRenamed(QTableWidgetItem*); void songChanged(MusECore::SongChangedStruct_t); void selectionChanged(); void deviceSelectionChanged(); void addJackDeviceClicked(); void addAlsaDeviceClicked(bool); void addInstanceClicked(); void renameInstanceClicked(); void removeInstanceClicked(); //void deviceItemClicked(QTreeWidgetItem*, int); void deviceItemClicked(QTableWidgetItem* item); void changeDefInputRoutes(QAction* act); void changeDefOutputRoutes(QAction* act); void apply(); void okClicked(); void beforeDeviceContextShow(PopupMenu* menu, QAction* menuAction, QMenu* ctxMenu); void deviceContextTriggered(QAction*); void checkGUIState(); public slots: void closeEvent(QCloseEvent*e); public: MPConfig(QWidget* parent = nullptr); ~MPConfig(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/control_mapper.cpp000066400000000000000000002172161404105311000221370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // control_mapper.cpp // Copyright (C) 2012 by Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "globals.h" #include "globaldefs.h" #include "mididev.h" #include "midiport.h" #include "midictrl.h" #include "audio.h" #include "synth.h" #include "minstrument.h" #include "app.h" #include "icons.h" #include "control_mapper.h" namespace MusEGui { //typedef class ColorListEditor ColorEditor; typedef class ColorChooserEditor ColorEditor; // Fifteen colors, 16 elements, the last element opens a full QColorDialog. #define __COLOR_CHOOSER_NUM_ELEMENTS__ 16 #define __COLOR_CHOOSER_NUM_COLUMNS__ 4 #define __COLOR_CHOOSER_ELEMENT_WIDTH__ 16 #define __COLOR_CHOOSER_ELEMENT_HEIGHT__ 16 #define __COLOR_CHOOSER_BORDER_WIDTH__ 1 QColor ColorChooserEditor::colorChooserList[] = { Qt::red, Qt::yellow, Qt::green, Qt::cyan, Qt::blue, Qt::magenta, Qt::darkRed, Qt::darkYellow, Qt::darkGreen, Qt::darkCyan, Qt::darkBlue, Qt::darkMagenta, Qt::darkGray, Qt::white, Qt::black }; //----------------------------------- // ColorChooserEditor //----------------------------------- ColorChooserEditor::ColorChooserEditor(QWidget *parent) : QWidget(parent) { setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); setFixedWidth(__COLOR_CHOOSER_ELEMENT_WIDTH__ * __COLOR_CHOOSER_NUM_COLUMNS__ + __COLOR_CHOOSER_BORDER_WIDTH__ * 2); setFixedHeight(__COLOR_CHOOSER_ELEMENT_HEIGHT__ * (__COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__) + __COLOR_CHOOSER_BORDER_WIDTH__ * 2); //setMouseTracking(true); setAutoFillBackground(true); setFocusPolicy(Qt::StrongFocus); // Required or else it closes on click. } ColorChooserEditor::~ColorChooserEditor() { fprintf(stderr, "~ColorChooserEditor\n"); // REMOVE Tim. } QSize ColorChooserEditor::sizeHint() const { return minimumSizeHint(); } QSize ColorChooserEditor::minimumSizeHint() const { return QSize(__COLOR_CHOOSER_ELEMENT_WIDTH__ * __COLOR_CHOOSER_NUM_COLUMNS__ + __COLOR_CHOOSER_BORDER_WIDTH__ * 2, __COLOR_CHOOSER_ELEMENT_HEIGHT__ * (__COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__) + __COLOR_CHOOSER_BORDER_WIDTH__ * 2); } QColor ColorChooserEditor::color() const { //return qvariant_cast(itemData(currentIndex(), Qt::DecorationRole)); return _color; } void ColorChooserEditor::setColor(QColor color) { //setCurrentIndex(findData(color, int(Qt::DecorationRole))); _color = color; } void ColorChooserEditor::paintEvent(QPaintEvent* /*event*/) { QPainter p(this); for(int i = 0; i < __COLOR_CHOOSER_NUM_ELEMENTS__; ++i) { int px = (i % __COLOR_CHOOSER_NUM_COLUMNS__) * __COLOR_CHOOSER_ELEMENT_WIDTH__; int py = (i / __COLOR_CHOOSER_NUM_COLUMNS__) * __COLOR_CHOOSER_ELEMENT_HEIGHT__; // The last element is the QColorDialog opener. if(i == __COLOR_CHOOSER_NUM_ELEMENTS__ - 1) p.drawPixmap(px + __COLOR_CHOOSER_BORDER_WIDTH__, py + __COLOR_CHOOSER_BORDER_WIDTH__, *settings_appearance_settingsIcon); // This icon is 16x16 else { p.fillRect(px + __COLOR_CHOOSER_BORDER_WIDTH__ + 1, py + __COLOR_CHOOSER_BORDER_WIDTH__ + 1, __COLOR_CHOOSER_ELEMENT_WIDTH__ - 2, __COLOR_CHOOSER_ELEMENT_HEIGHT__ - 2, colorChooserList[i]); if(_color == colorChooserList[i]) { QPen pen(Qt::DashLine); p.setPen(pen); p.drawRect(px + __COLOR_CHOOSER_BORDER_WIDTH__, py + __COLOR_CHOOSER_BORDER_WIDTH__, __COLOR_CHOOSER_ELEMENT_WIDTH__ - 1, __COLOR_CHOOSER_ELEMENT_HEIGHT__ - 1); } } } // Draw a one-pixel border. QPen pen(Qt::black); pen.setWidth(__COLOR_CHOOSER_BORDER_WIDTH__); p.setPen(pen); p.drawRect(0, 0, width() - 1, height() - 1); } void ColorChooserEditor::mousePressEvent(QMouseEvent* event) { fprintf(stderr, "ColorChooserEditor::mousePressEvent\n"); // REMOVE Tim. event->accept(); } void ColorChooserEditor::mouseReleaseEvent(QMouseEvent* event) { fprintf(stderr, "ColorChooserEditor::mouseReleaseEvent\n"); // REMOVE Tim. int x = event->x(); int y = event->y(); int px = x - __COLOR_CHOOSER_BORDER_WIDTH__; int py = y - __COLOR_CHOOSER_BORDER_WIDTH__; if(px < 0) px = 0; if(py < 0) py = 0; int column = px / __COLOR_CHOOSER_ELEMENT_WIDTH__; if(column >= __COLOR_CHOOSER_NUM_COLUMNS__) column = __COLOR_CHOOSER_NUM_COLUMNS__ - 1; int row = py / __COLOR_CHOOSER_ELEMENT_HEIGHT__; if(row >= __COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__) row = __COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__ - 1; int element = row * __COLOR_CHOOSER_NUM_COLUMNS__ + column; if(element >= __COLOR_CHOOSER_NUM_ELEMENTS__) // Just in case element = __COLOR_CHOOSER_NUM_ELEMENTS__; // The last element is the QColorDialog opener. if(element == __COLOR_CHOOSER_NUM_ELEMENTS__ - 1) { QColor c = QColorDialog::getColor(_color, this, QString()); if(!c.isValid()) return; _color = c; } else _color = colorChooserList[element]; emit activated(_color); event->accept(); } // bool ColorChooserEditor::event(QEvent* event) // { // if(event->type() == QEvent::MouseButtonPress) // { // QMouseEvent* me = static_cast(event); // fprintf(stderr, "ColorChooserEditor::event: Press X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. // event->accept(); // return true; // } // else // if(event->type() == QEvent::MouseButtonRelease) // { // QMouseEvent* me = static_cast(event); // fprintf(stderr, "ColorChooserEditor::event: Release X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. // //event->accept(); // //return true; // } // else // if(event->type() == QEvent::Close) // { // fprintf(stderr, "ColorChooserEditor::event: Close\n"); // REMOVE Tim. // } // else // fprintf(stderr, "ColorChooserEditor::event: type:%d\n", event->type()); // REMOVE Tim. // // return QWidget::event(event); // } //----------------------------------- // MidiCtrlDelegateEditor //----------------------------------- MidiCtrlDelegateEditor::MidiCtrlDelegateEditor(QWidget *parent) : QWidget(parent) { /// setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); /// setFixedWidth(__COLOR_CHOOSER_ELEMENT_WIDTH__ * __COLOR_CHOOSER_NUM_COLUMNS__ + /// __COLOR_CHOOSER_BORDER_WIDTH__ * 2); /// setFixedHeight(__COLOR_CHOOSER_ELEMENT_HEIGHT__ * (__COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__) + /// __COLOR_CHOOSER_BORDER_WIDTH__ * 2); //setMouseTracking(true); setAutoFillBackground(true); setFocusPolicy(Qt::StrongFocus); // Required or else it closes on click. _hlayout = new QHBoxLayout(this); _typeCombo = new QComboBox(this); _numHiSpinBox = new QSpinBox(this); _numLoSpinBox = new QSpinBox(this); _validIndicator = new QWidget(this); _numHiSpinBox->setMinimum(0); _numLoSpinBox->setMinimum(0); _numHiSpinBox->setMaximum(127); _numLoSpinBox->setMaximum(127); _validIndicator->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); _validIndicator->setFixedWidth(16); _validIndicator->setFixedHeight(16); _typeCombo->addItem("---", -1); _typeCombo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Controller7), MusECore::MidiController::Controller7); _typeCombo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Controller14), MusECore::MidiController::Controller14); _typeCombo->addItem(MusECore::int2ctrlType(MusECore::MidiController::RPN), MusECore::MidiController::RPN); _typeCombo->addItem(MusECore::int2ctrlType(MusECore::MidiController::NRPN), MusECore::MidiController::NRPN); _typeCombo->addItem(MusECore::int2ctrlType(MusECore::MidiController::RPN14), MusECore::MidiController::RPN14); _typeCombo->addItem(MusECore::int2ctrlType(MusECore::MidiController::NRPN14), MusECore::MidiController::NRPN14); _typeCombo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Pitch), MusECore::MidiController::Pitch); _typeCombo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Program), MusECore::MidiController::Program); // TODO Per-pitch controls not supported yet. Need a way to select pitch. //_typeCombo->addItem(MusECore::int2ctrlType(MusECore::MidiController::PolyAftertouch), MusECore::MidiController::PolyAftertouch); _typeCombo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Aftertouch), MusECore::MidiController::Aftertouch); //connect(_typeCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(editorChanged())); _hlayout->addWidget(_typeCombo); _hlayout->addWidget(_numHiSpinBox); _hlayout->addWidget(_numLoSpinBox); _hlayout->addWidget(_validIndicator); } MidiCtrlDelegateEditor::~MidiCtrlDelegateEditor() { fprintf(stderr, "~MidiCtrlDelegateEditor\n"); // REMOVE Tim. } QSize MidiCtrlDelegateEditor::sizeHint() const { //return minimumSizeHint(); return QWidget::sizeHint(); } QSize MidiCtrlDelegateEditor::minimumSizeHint() const { // return QSize(__COLOR_CHOOSER_ELEMENT_WIDTH__ * __COLOR_CHOOSER_NUM_COLUMNS__ + // __COLOR_CHOOSER_BORDER_WIDTH__ * 2, // __COLOR_CHOOSER_ELEMENT_HEIGHT__ * (__COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__) + // __COLOR_CHOOSER_BORDER_WIDTH__ * 2); return QWidget::minimumSizeHint(); } int MidiCtrlDelegateEditor::controlNum() const { int idx = _typeCombo->currentIndex(); if(idx != -1) return MusECore::midiCtrlTerms2Number(qvariant_cast(_typeCombo->itemData(idx)), ((_numHiSpinBox->value() & 0x7f) << 8) | (_numLoSpinBox->value() & 0x7f)); return -1; } void MidiCtrlDelegateEditor::setControlNum(int ctrlNum) { int type = -1; int hnum = 0; int lnum = 0; if(ctrlNum != -1) { type = MusECore::midiControllerType(ctrlNum); hnum = (ctrlNum >> 8) & 0x7f; lnum = ctrlNum & 0x7f; } int idx = _typeCombo->findData(type); if(idx != -1) { _typeCombo->blockSignals(true); // Prevent currentIndexChanged or activated from being called _typeCombo->setCurrentIndex(idx); _typeCombo->blockSignals(false); } _numHiSpinBox->blockSignals(true); _numHiSpinBox->setValue(hnum); _numHiSpinBox->blockSignals(false); _numLoSpinBox->blockSignals(true); _numLoSpinBox->setValue(lnum); _numLoSpinBox->blockSignals(false); } void MidiCtrlDelegateEditor::paintEvent(QPaintEvent* event) { // TODO if required QWidget::paintEvent(event); // QPainter p(this); // // for(int i = 0; i < __COLOR_CHOOSER_NUM_ELEMENTS__; ++i) // { // int px = (i % __COLOR_CHOOSER_NUM_COLUMNS__) * __COLOR_CHOOSER_ELEMENT_WIDTH__; // int py = (i / __COLOR_CHOOSER_NUM_COLUMNS__) * __COLOR_CHOOSER_ELEMENT_HEIGHT__; // // // The last element is the QColorDialog opener. // if(i == __COLOR_CHOOSER_NUM_ELEMENTS__ - 1) // p.drawPixmap(px + __COLOR_CHOOSER_BORDER_WIDTH__, py + __COLOR_CHOOSER_BORDER_WIDTH__, *settings_appearance_settingsIcon); // This icon is 16x16 // else // { // p.fillRect(px + __COLOR_CHOOSER_BORDER_WIDTH__ + 1, // py + __COLOR_CHOOSER_BORDER_WIDTH__ + 1, // __COLOR_CHOOSER_ELEMENT_WIDTH__ - 2, // __COLOR_CHOOSER_ELEMENT_HEIGHT__ - 2, // colorChooserList[i]); // if(_color == colorChooserList[i]) // { // QPen pen(Qt::DashLine); // p.setPen(pen); // p.drawRect(px + __COLOR_CHOOSER_BORDER_WIDTH__, // py + __COLOR_CHOOSER_BORDER_WIDTH__, // __COLOR_CHOOSER_ELEMENT_WIDTH__ - 1, // __COLOR_CHOOSER_ELEMENT_HEIGHT__ - 1); // } // } // } // // // Draw a one-pixel border. // QPen pen(Qt::black); // pen.setWidth(__COLOR_CHOOSER_BORDER_WIDTH__); // p.setPen(pen); // p.drawRect(0, 0, width() - 1, height() - 1); } void MidiCtrlDelegateEditor::mousePressEvent(QMouseEvent* event) { fprintf(stderr, "MidiCtrlDelegateEditor::mousePressEvent\n"); // REMOVE Tim. //event->accept(); } void MidiCtrlDelegateEditor::mouseReleaseEvent(QMouseEvent* event) { fprintf(stderr, "MidiCtrlDelegateEditor::mouseReleaseEvent\n"); // REMOVE Tim. // int x = event->x(); // int y = event->y(); // // int px = x - __COLOR_CHOOSER_BORDER_WIDTH__; // int py = y - __COLOR_CHOOSER_BORDER_WIDTH__; // if(px < 0) // px = 0; // if(py < 0) // py = 0; // // int column = px / __COLOR_CHOOSER_ELEMENT_WIDTH__; // if(column >= __COLOR_CHOOSER_NUM_COLUMNS__) // column = __COLOR_CHOOSER_NUM_COLUMNS__ - 1; // int row = py / __COLOR_CHOOSER_ELEMENT_HEIGHT__; // if(row >= __COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__) // row = __COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__ - 1; // // int element = row * __COLOR_CHOOSER_NUM_COLUMNS__ + column; // // if(element >= __COLOR_CHOOSER_NUM_ELEMENTS__) // Just in case // element = __COLOR_CHOOSER_NUM_ELEMENTS__; // // // The last element is the QColorDialog opener. // if(element == __COLOR_CHOOSER_NUM_ELEMENTS__ - 1) // { // QColor c = QColorDialog::getColor(_color, this, QString()); // if(!c.isValid()) // return; // _color = c; // } // else // _color = colorChooserList[element]; // // emit activated(_color); // // event->accept(); } // bool MidiCtrlDelegateEditor::event(QEvent* event) // { // if(event->type() == QEvent::MouseButtonPress) // { // QMouseEvent* me = static_cast(event); // fprintf(stderr, "MidiCtrlDelegateEditor::event: Press X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. // event->accept(); // return true; // } // else // if(event->type() == QEvent::MouseButtonRelease) // { // QMouseEvent* me = static_cast(event); // fprintf(stderr, "MidiCtrlDelegateEditor::event: Release X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. // //event->accept(); // //return true; // } // else // if(event->type() == QEvent::Close) // { // fprintf(stderr, "MidiCtrlDelegateEditor::event: Close\n"); // REMOVE Tim. // } // else // fprintf(stderr, "MidiCtrlDelegateEditor::event: type:%d\n", event->type()); // REMOVE Tim. // // return QWidget::event(event); // } //----------------------------------- // ColorListEditor //----------------------------------- ColorListEditor::ColorListEditor(QWidget *parent) : QComboBox(parent) { populateList(); } QColor ColorListEditor::color() const { return qvariant_cast(itemData(currentIndex(), Qt::DecorationRole)); } void ColorListEditor::setColor(QColor color) { setCurrentIndex(findData(color, int(Qt::DecorationRole))); } void ColorListEditor::populateList() { QStringList colorNames = QColor::colorNames(); for (int i = 0; i < colorNames.size(); ++i) { QColor color(colorNames[i]); //insertItem(i, colorNames[i]); //setItemData(i, color, Qt::DecorationRole); addItem(QString()); model()->setData(model()->index(i, 0), color, Qt::DecorationRole); } } //----------------------------------- // MapperAssignDelegate //----------------------------------- void MapperAssignDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { // if (index.data().canConvert()) { // StarRating starRating = qvariant_cast(index.data()); // // if (option.state & QStyle::State_Selected) // painter->fillRect(option.rect, option.palette.highlight()); // // starRating.paint(painter, option.rect, option.palette, // StarRating::ReadOnly); // } else QStyledItemDelegate::paint(painter, option, index); } QWidget* MapperAssignDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { // if (index.data().canConvert()) { // StarEditor *editor = new StarEditor(parent); // connect(editor, SIGNAL(editingFinished()), // this, SLOT(commitAndCloseEditor())); // return editor; // } else //if(index.column() == return QStyledItemDelegate::createEditor(parent, option, index); } void MapperAssignDelegate::commitAndCloseEditor() { // StarEditor *editor = qobject_cast(sender()); // emit commitData(editor); // emit closeEditor(editor); } void MapperAssignDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { // if (index.data().canConvert()) { // StarRating starRating = qvariant_cast(index.data()); // StarEditor *starEditor = qobject_cast(editor); // starEditor->setStarRating(starRating); // } else QStyledItemDelegate::setEditorData(editor, index); } void MapperAssignDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { // if (index.data().canConvert()) { // StarEditor *starEditor = qobject_cast(editor); // model->setData(index, QVariant::fromValue(starEditor->starRating())); // } else QStyledItemDelegate::setModelData(editor, model, index); } QSize MapperAssignDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { // if (index.data().canConvert()) { // StarRating starRating = qvariant_cast(index.data()); // return starRating.sizeHint(); // } else return QStyledItemDelegate::sizeHint(option, index); } //----------------------------------- // MapperControlDelegate //----------------------------------- MapperControlDelegate::MapperControlDelegate(QWidget *parent) : QStyledItemDelegate(parent) { _firstPress = true; } //----------------------------------- // getItemRectangle // editor is optional and provides info //----------------------------------- QRect MapperControlDelegate::getItemRectangle(const QStyleOptionViewItem& option, const QModelIndex& index, QStyle::SubElement subElement, QWidget* editor) const { // Taken from QStyledItemDelegate source. QStyleOptionViewItemV4 opt = option; initStyleOption(&opt, index); const QWidget* widget = NULL; const QStyleOptionViewItemV3* v3 = qstyleoption_cast(&option); if(v3) widget = v3->widget; // Let the editor take up all available space if the editor is not a QLineEdit or it is in a QTableView. #if !defined(QT_NO_TABLEVIEW) && !defined(QT_NO_LINEEDIT) if(editor && qobject_cast(editor) && !qobject_cast(widget)) opt.showDecorationSelected = editor->style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, 0, editor); else #endif opt.showDecorationSelected = true; const QStyle *style = widget ? widget->style() : QApplication::style(); // if(editor->layoutDirection() == Qt::RightToLeft) // { // const int delta = qSmartMinSize(editor).width() - r.width(); // qSmartMinSize ??? // if (delta > 0) // { // //we need to widen the geometry // r.adjust(-delta, 0, 0, 0); // } // } return style->subElementRect(subElement, &opt, widget); } //----------------------------------- // subElementHitTest // editor is optional and provides info //----------------------------------- bool MapperControlDelegate::subElementHitTest(const QPoint& point, const QStyleOptionViewItem& option, const QModelIndex& index, QStyle::SubElement* subElement, QWidget* editor) const { QRect checkBoxRect = getItemRectangle(option, index, QStyle::SE_ItemViewItemCheckIndicator, editor); if(checkBoxRect.isValid() && checkBoxRect.contains(point)) { if(subElement) (*subElement) = QStyle::SE_ItemViewItemCheckIndicator; return true; } QRect decorationRect = getItemRectangle(option, index, QStyle::SE_ItemViewItemDecoration, editor); if(decorationRect.isValid() && decorationRect.contains(point)) { if(subElement) (*subElement) = QStyle::SE_ItemViewItemDecoration; return true; } QRect textRect = getItemRectangle(option, index, QStyle::SE_ItemViewItemText, editor); if(textRect.isValid() && textRect.contains(point)) { if(subElement) (*subElement) = QStyle::SE_ItemViewItemText; return true; } return false; } //----------------------------------- // updateEditorGeometry //----------------------------------- void MapperControlDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const { // REMOVE Tim. fprintf(stderr, "ColorChooserEditor::updateEditorGeometry editor x:%d y:%d w:%d h:%d rect x:%d y:%d w:%d h:%d\n", editor->x(), editor->y(), editor->width(), editor->height(), option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height()); // For the color editor, move it down to the start of the next item so it doesn't cover the current item row. // Width and height are not used - the color editor fixates it's own width and height. if(index.column() == ControlMapperDialog::C_NAME) { QRect r = getItemRectangle(option, index, QStyle::SE_ItemViewItemText, editor); // Get the text rectangle. if(r.isValid()) { editor->move(r.x(), option.rect.y() + option.rect.height()); return; } } QStyledItemDelegate::updateEditorGeometry(editor, option, index); } void MapperControlDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { // fprintf(stderr, "MapperControlDelegate::paint row:%d col:%d, rect x:%d y:%d w:%d h:%d showDecorationSelected:%d\n", // index.row(), index.column(), // option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height(), // option.showDecorationSelected); // REMOVE Tim. //QStyleOptionViewItemV4 opt = option; //initStyleOption(&opt, index); //opt.showDecorationSelected = false; // TODO: Don't forget these if necessary. //painter->save(); //painter->restore(); // if (index.data().canConvert()) { // StarRating starRating = qvariant_cast(index.data()); // // if (option.state & QStyle::State_Selected) // painter->fillRect(option.rect, option.palette.highlight()); // // starRating.paint(painter, option.rect, option.palette, // StarRating::ReadOnly); // } else // if(index.column() == ControlMapperDialog::C_NAME) // { // // TODO: Disable all this Style stuff if using a style sheet. // // //QRect disclosure_r = getItemRectangle(option, index, QStyle::SE_TreeViewDisclosureItem); // Get the text rectangle. // //if(disclosure_r.isValid()) // //{ // //} // // QRect checkbox_r = getItemRectangle(option, index, QStyle::SE_ItemViewItemCheckIndicator); // Get the text rectangle. // if(checkbox_r.isValid()) // { // if(option.state & QStyle::State_Selected) // painter->fillRect(checkbox_r & option.rect, option.palette.highlight()); // QStyleOptionViewItemV4 opt = option; // initStyleOption(&opt, index); // Required ? // opt.rect = checkbox_r & option.rect; // QApplication::style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &opt, painter); // //QApplication::style()->drawControl(); // } // // //QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, painter); // // //QApplication::style()->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &option, painter); // // QRect deco_r = getItemRectangle(option, index, QStyle::SE_ItemViewItemDecoration); // Get the text rectangle. // if(deco_r.isValid()) // painter->fillRect(deco_r & option.rect, index.data(Qt::DecorationRole).value()); // // QRect text_r = getItemRectangle(option, index, QStyle::SE_ItemViewItemText); // Get the text rectangle. // if(text_r.isValid()) // { // if(option.state & QStyle::State_Selected) // painter->fillRect(text_r & option.rect, option.palette.highlight()); // QApplication::style()->drawItemText(painter, text_r & option.rect, option.displayAlignment, option.palette, true, index.data(Qt::DisplayRole).toString()); // } // // return; // } QStyledItemDelegate::paint(painter, option, index); //QStyledItemDelegate::paint(painter, opt, index); } QWidget* MapperControlDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { // if (index.data().canConvert()) { // StarEditor *editor = new StarEditor(parent); // connect(editor, SIGNAL(editingFinished()), // this, SLOT(commitAndCloseEditor())); // return editor; // } else int opt_state = option.state; fprintf(stderr, "MapperControlDelegate::createEditor option state:%d\n", opt_state); // REMOVE Tim. // HACK: For some reason when using CurrentChanged trigger, createEditor is called upon opening the dialog, yet nothing is selected. // It suddenly started doing that after working just fine. Can't find what may have changed. //if(!(option.state & QStyle::State_Selected)) // Nope. option.state is always the same, never seems to change. // return NULL; //if(_firstPress) // return NULL; switch(index.column()) { // case ControlMapperDialog::C_SHOW: // //return QStyledItemDelegate::createEditor(parent, option, index); // // This is a checkbox column. No editable info. // //fprintf(stderr, "ERROR: MapperControlDelegate::createEditor called for SHOW column\n"); // return 0; //case ControlMapperDialog::C_NAME: //fprintf(stderr, "ERROR: MapperControlDelegate::createEditor called for NAME column\n"); // This seems to be a way we can prevent editing of a cell here in this tree widget. // Table widget has individual item cell edting enable but here in tree widget it's per row. //return 0; //case ControlMapperDialog::C_COLOR: case ControlMapperDialog::C_NAME: { ColorEditor* color_list = new ColorEditor(parent); //connect(color_list, SIGNAL(activated(int)), this, SLOT(colorEditorChanged())); connect(color_list, SIGNAL(activated(const QColor&)), this, SLOT(editorChanged())); return color_list; } case ControlMapperDialog::C_ASSIGN_PORT: { QComboBox* combo = new QComboBox(parent); // combo->addItem(tr(""), -1); // combo->addItem(tr("Control7"), MusECore::MidiController::Controller7); // combo->addItem(tr("Control14"), MusECore::MidiController::Controller14); // combo->addItem(tr("RPN"), MusECore::MidiController::RPN); // combo->addItem(tr("NPRN"), MusECore::MidiController::NRPN); // combo->addItem(tr("RPN14"), MusECore::MidiController::RPN14); // combo->addItem(tr("NRPN14"), MusECore::MidiController::NRPN14); // combo->addItem(tr("Pitch"), MusECore::MidiController::Pitch); // combo->addItem(tr("Program"), MusECore::MidiController::Program); // //combo->addItem(tr("PolyAftertouch"), MusECore::MidiController::PolyAftertouch); // Not supported yet. Need a way to select pitch. // combo->addItem(tr("Aftertouch"), MusECore::MidiController::Aftertouch); // //combo->setCurrentIndex(0); // combo->addItem(tr(""), -1); // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Controller7), MusECore::MidiController::Controller7); // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Controller14), MusECore::MidiController::Controller14); // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::RPN), MusECore::MidiController::RPN); // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::NRPN), MusECore::MidiController::NRPN); // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::RPN14), MusECore::MidiController::RPN14); // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::NRPN14), MusECore::MidiController::NRPN14); // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Pitch), MusECore::MidiController::Pitch); // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Program), MusECore::MidiController::Program); // //combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::PolyAftertouch), MusECore::MidiController::PolyAftertouch); // Not supported yet. Need a way to select pitch. // combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Aftertouch), MusECore::MidiController::Aftertouch); combo->addItem("---", -1); int port = index.data(Qt::UserRole).toInt(); QString port_name; for(int i = 0; i < MIDI_PORTS; ++i) { MusECore::MidiDevice* md = MusEGlobal::midiPorts[i].device(); //if(!md) // In the case of this combo box, don't bother listing empty ports. // continue; //if(!(md->rwFlags() & 1 || md->isSynti()) && (i != outPort)) if(!(md && (md->rwFlags() & 2)) && (i != port)) // Only readable ports, or current one. continue; //name.sprintf("%d:%s", i+1, MusEGlobal::midiPorts[i].portname().toLatin1().constData()); QString name = QString("%1:%2").arg(i+1).arg(MusEGlobal::midiPorts[i].portname()); combo->addItem(name, i); } connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(editorChanged())); return combo; } case ControlMapperDialog::C_ASSIGN_CHAN: { // QSpinBox* spin_box = new QSpinBox(parent); // spin_box->setMinimum(0); // spin_box->setMaximum(127); // return spin_box; QWidget* widget = QStyledItemDelegate::createEditor(parent, option, index); QSpinBox* spin_box = qobject_cast(widget); if(spin_box) { spin_box->setMinimum(0); spin_box->setMaximum(MIDI_CHANNELS - 1); } return widget; } case ControlMapperDialog::C_MCTL_NUM: { QComboBox* combo = new QComboBox(parent); // combo->addItem(tr(""), -1); // combo->addItem(tr("Control7"), MusECore::MidiController::Controller7); // combo->addItem(tr("Control14"), MusECore::MidiController::Controller14); // combo->addItem(tr("RPN"), MusECore::MidiController::RPN); // combo->addItem(tr("NPRN"), MusECore::MidiController::NRPN); // combo->addItem(tr("RPN14"), MusECore::MidiController::RPN14); // combo->addItem(tr("NRPN14"), MusECore::MidiController::NRPN14); // combo->addItem(tr("Pitch"), MusECore::MidiController::Pitch); // combo->addItem(tr("Program"), MusECore::MidiController::Program); // //combo->addItem(tr("PolyAftertouch"), MusECore::MidiController::PolyAftertouch); // Not supported yet. Need a way to select pitch. // combo->addItem(tr("Aftertouch"), MusECore::MidiController::Aftertouch); // //combo->setCurrentIndex(0); //combo->addItem(tr(""), -1); combo->addItem("---", -1); combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Controller7), MusECore::MidiController::Controller7); combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Controller14), MusECore::MidiController::Controller14); combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::RPN), MusECore::MidiController::RPN); combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::NRPN), MusECore::MidiController::NRPN); combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::RPN14), MusECore::MidiController::RPN14); combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::NRPN14), MusECore::MidiController::NRPN14); combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Pitch), MusECore::MidiController::Pitch); combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Program), MusECore::MidiController::Program); // TODO Per-pitch controls not supported yet. Need a way to select pitch. //combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::PolyAftertouch), MusECore::MidiController::PolyAftertouch); combo->addItem(MusECore::int2ctrlType(MusECore::MidiController::Aftertouch), MusECore::MidiController::Aftertouch); connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(editorChanged())); return combo; } // case ControlMapperDialog::C_MCTL_H: // { // // QSpinBox* spin_box = new QSpinBox(parent); // // spin_box->setMinimum(0); // // spin_box->setMaximum(127); // // return spin_box; // // QWidget* widget = QStyledItemDelegate::createEditor(parent, option, index); // QSpinBox* spin_box = qobject_cast(widget); // if(spin_box) // { // spin_box->setMinimum(0); // spin_box->setMaximum(127); // } // return widget; // } /// case ControlMapperDialog::C_MCTL_H: // case ControlMapperDialog::C_MCTL_L: // { // // QSpinBox* spin_box = new QSpinBox(parent); // // spin_box->setMinimum(0); // // spin_box->setMaximum(127); // // return spin_box; // // QWidget* widget = QStyledItemDelegate::createEditor(parent, option, index); // QSpinBox* spin_box = qobject_cast(widget); // if(spin_box) // { // spin_box->setMinimum(0); // spin_box->setMaximum(127); // } // return widget; // } } return QStyledItemDelegate::createEditor(parent, option, index); } void MapperControlDelegate::editorChanged() { // StarEditor *editor = qobject_cast(sender()); // emit commitData(editor); // emit closeEditor(editor); fprintf(stderr, "MapperControlDelegate::editorChanged\n"); // REMOVE Tim. // Wow, I thought using sender was frowned upon ("breaks modularity"). But hey, it's necessary sometimes. TODO Improve this? //ColorEditor* editor = qobject_cast(sender()); QWidget* editor = qobject_cast(sender()); if(editor) { emit commitData(editor); emit closeEditor(editor); } } // void MapperControlDelegate::commitAndCloseEditor() // { // // StarEditor *editor = qobject_cast(sender()); // // emit commitData(editor); // // emit closeEditor(editor); // } void MapperControlDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { fprintf(stderr, "MapperControlDelegate::setEditorData\n"); // REMOVE Tim. // if (index.data().canConvert()) { // StarRating starRating = qvariant_cast(index.data()); // StarEditor *starEditor = qobject_cast(editor); // starEditor->setStarRating(starRating); // } else //if(index.column() == ControlMapperDialog::C_COLOR) switch(index.column()) { case ControlMapperDialog::C_NAME: { ColorEditor* color_editor = qobject_cast(editor); if(color_editor) color_editor->setColor(index.data(Qt::DecorationRole).value()); return; } case ControlMapperDialog::C_ASSIGN_PORT: case ControlMapperDialog::C_MCTL_NUM: { QComboBox* combo = qobject_cast(editor); if(combo) { int data = index.data(Qt::UserRole).toInt(); int idx = combo->findData(data); if(idx != -1) { combo->blockSignals(true); // Prevent currentIndexChanged or activated from being called combo->setCurrentIndex(idx); combo->blockSignals(false); } } return; } default: QStyledItemDelegate::setEditorData(editor, index); } // if(index.column() == ControlMapperDialog::C_NAME) // { // ColorEditor* color_editor = qobject_cast(editor); // if(color_editor) // color_editor->setColor(index.data(Qt::DecorationRole).value()); // } // else // if(index.column() == ControlMapperDialog::C_ASSIGN_PORT) // { // QComboBox* combo = qobject_cast(editor); // if(combo) // { // int data = index.data(Qt::UserRole).toInt(); // int idx = combo->findData(data); // if(idx != -1) // combo->setCurrentIndex(idx); // } // } // else // if(index.column() == ControlMapperDialog::C_MCTL_TYPE) // { // QComboBox* combo = qobject_cast(editor); // if(combo) // { // int data = index.data(Qt::UserRole).toInt(); // int idx = combo->findData(data); // if(idx != -1) // combo->setCurrentIndex(idx); // } // } // else // QStyledItemDelegate::setEditorData(editor, index); } void MapperControlDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { fprintf(stderr, "MapperControlDelegate::setModelData\n"); // REMOVE Tim. // if (index.data().canConvert()) { // StarEditor *starEditor = qobject_cast(editor); // model->setData(index, QVariant::fromValue(starEditor->starRating())); // } else //if(index.column() == ControlMapperDialog::C_COLOR) switch(index.column()) { case ControlMapperDialog::C_NAME: { ColorEditor* color_editor = qobject_cast(editor); if(color_editor) model->setData(index, color_editor->color(), Qt::DecorationRole); return; } case ControlMapperDialog::C_ASSIGN_PORT: case ControlMapperDialog::C_MCTL_NUM: { QComboBox* combo = qobject_cast(editor); if(combo) { int idx = combo->currentIndex(); if(idx != -1) { model->setData(index, combo->itemData(idx), Qt::UserRole); // Do this one before the text so that the tree view's itemChanged handler gets it first! model->blockSignals(true); model->setData(index, combo->itemText(idx), Qt::DisplayRole); // This will cause another handler call. Prevent it by blocking. model->blockSignals(false); } } return; } default: QStyledItemDelegate::setModelData(editor, model, index); } // if(index.column() == ControlMapperDialog::C_NAME) // { // ColorEditor* color_editor = qobject_cast(editor); // if(color_editor) // model->setData(index, color_editor->color(), Qt::DecorationRole); // } // else // if(index.column() == ControlMapperDialog::C_ASSIGN_PORT) // { // QComboBox* combo = qobject_cast(editor); // if(combo) // { // int idx = combo->currentIndex(); // if(idx != -1) // { // model->setData(index, combo->itemText(idx), Qt::DisplayRole); // model->setData(index, combo->itemData(idx), Qt::UserRole); // } // } // } // else // if(index.column() == ControlMapperDialog::C_MCTL_TYPE) // { // QComboBox* combo = qobject_cast(editor); // if(combo) // { // int idx = combo->currentIndex(); // if(idx != -1) // { // model->setData(index, combo->itemText(idx), Qt::DisplayRole); // model->setData(index, combo->itemData(idx), Qt::UserRole); // } // } // } // else // QStyledItemDelegate::setModelData(editor, model, index); } QSize MapperControlDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { // if (index.data().canConvert()) { // StarRating starRating = qvariant_cast(index.data()); // return starRating.sizeHint(); // } else // if(index.column() == ControlMapperDialog::C_COLOR) // return QSize(__COLOR_CHOOSER_ELEMENT_WIDTH__ * __COLOR_CHOOSER_NUM_COLUMNS__, // __COLOR_CHOOSER_ELEMENT_HEIGHT__ * (__COLOR_CHOOSER_NUM_ELEMENTS__ / __COLOR_CHOOSER_NUM_COLUMNS__)); // return QStyledItemDelegate::sizeHint(option, index); } bool MapperControlDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { if(event->type() == QEvent::MouseMove) { QMouseEvent* me = static_cast(event); fprintf(stderr, "MapperControlDelegate::editorEvent: Move X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. // If any buttons down, ignore. if(me->buttons() != Qt::NoButton) { event->accept(); return true; } } else if(event->type() == QEvent::MouseButtonPress) { QMouseEvent* me = static_cast(event); fprintf(stderr, "MapperControlDelegate::editorEvent: Press X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. _firstPress = false; // HACK QStyle::SubElement sub_element; if(subElementHitTest(me->pos(), option, index, &sub_element)) _currentSubElement = sub_element; //event->accept(); //return true; } else if(event->type() == QEvent::MouseButtonRelease) { QMouseEvent* me = static_cast(event); fprintf(stderr, "MapperControlDelegate::editorEvent: Release X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. // If the element under the mouse is not the one when pressed, eat up these events because // they trigger the editor or action of the element under the mouse at the release position. QStyle::SubElement sub_element = _currentSubElement; if(!subElementHitTest(me->pos(), option, index, &sub_element) || sub_element != _currentSubElement) //QRect r = getItemRectangle(option, index, QStyle::SE_ItemViewItemDecoration); //if(!subElementHitTest(me->pos(), option, index, &sub_element) || // (sub_element != QStyle::SE_ItemViewItemCheckIndicator && sub_element != QStyle::SE_ItemViewItemDecoration)) //if(r.isValid()) { event->accept(); return true; } } else if(event->type() == QEvent::Close) { fprintf(stderr, "MapperControlDelegate::editorEvent: Close\n"); // REMOVE Tim. } else fprintf(stderr, "MapperControlDelegate::editorEvent: event type:%d\n", event->type()); // REMOVE Tim. // switch(index.column()) // { // case ControlMapperDialog::C_SHOW: // // This is checkbox column. No editable info. // //event->accept(); // //return true; // //return false; // return QStyledItemDelegate::editorEvent(event, model, option, index); // // case ControlMapperDialog::C_NAME: // // This is non-editable name. // event->accept(); // return true; // // case ControlMapperDialog::C_COLOR: // { // if(event->type() == QEvent::MouseButtonRelease) // { // QMouseEvent* me = static_cast(event); // fprintf(stderr, " X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. // // } // // event->accept(); // return true; // } // // case ControlMapperDialog::C_ASSIGN: // // This is editable assigned input controller. // return false; // // case ControlMapperDialog::C_MCTL_TYPE: // // This is editable midi control type. // return false; // // case ControlMapperDialog::C_MCTL_H: // // This is editable midi control num high. // return false; // // case ControlMapperDialog::C_MCTL_L: // // This is editable midi control num low. // return false; // } // // return false; return QStyledItemDelegate::editorEvent(event, model, option, index); } bool MapperControlDelegate::eventFilter(QObject* editor, QEvent* event) { if(event->type() == QEvent::MouseButtonPress) { QMouseEvent* me = static_cast(event); fprintf(stderr, "MapperControlDelegate::eventFilter: Press X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. //event->accept(); //return true; } else if(event->type() == QEvent::MouseButtonRelease) { QMouseEvent* me = static_cast(event); fprintf(stderr, "MapperControlDelegate::eventFilter: Release X:%d Y:%d gX:%d gY:%d\n", me->x(), me->y(), me->globalX(), me->globalY()); // REMOVE Tim. //event->accept(); //return true; } else if(event->type() == QEvent::Close) { fprintf(stderr, "MapperControlDelegate::eventFilter: Close\n"); // REMOVE Tim. } else fprintf(stderr, "MapperControlDelegate::eventFilter: event type:%d\n", event->type()); // REMOVE Tim. return QStyledItemDelegate::eventFilter(editor, event); } // ----------------------------------- // ControlMapperDialog // ----------------------------------- ControlMapperDialog::ControlMapperDialog(MusECore::Track* t, QWidget* parent) : QDialog(parent), _track(t) { setupUi(this); if(_track->isMidiTrack()) { fprintf(stderr, "ERROR: ControlMapperDialog ctor: track type is midi - unsupported yet. Aborting!\n"); abort(); } MusECore::AudioTrack* atrack = static_cast(_track); _actrls = atrack->controller(); _mctrls = NULL; if(_track->type() == MusECore::Track::AUDIO_SOFTSYNTH) { MusECore::SynthI* si = static_cast(_track); _mctrls = ((MusECore::MidiInstrument*)si)->controller(); } MusECore::MidiAudioCtrlMap* map = _actrls->midiControls(); // QItemEditorFactory* factory = new QItemEditorFactory; // //QItemEditorFactory* factory = QItemEditorFactory::defaultFactory(); // QItemEditorCreatorBase* colorListCreator = new QStandardItemEditorCreator(); // factory->registerEditor(QVariant::Color, colorListCreator); // //QItemEditorFactory::defaultFactory()->registerEditor(QVariant::Color, colorListCreator); // //QItemEditorFactory::setDefaultFactory(factory); controlList->setColumnCount(C_COL_END); //QStringList control_header_names = QStringList() << tr("Show") << tr("Name") << tr("Color") << tr("Assign") << tr("Midi ctl type") << tr("Ctl H") << tr("Ctl L"); //QStringList control_header_names = QStringList() << tr("Name") << tr("Color") << tr("Assign") << tr("Midi ctl type") << tr("Ctl H") << tr("Ctl L"); ///QStringList control_header_names = QStringList() << tr("Name") << tr("Assign port") << tr("Chan") << tr("Midi ctl type") << tr("Ctl H") << tr("Ctl L"); QStringList control_header_names = QStringList() << tr("Name") << tr("Assign port") << tr("Chan") << tr("Midi control"); controlList->setHeaderLabels(control_header_names); //controlList->setIconSize(QSize(16, 16)); //MapperControlDelegate controls_delegate; //controlList->setItemDelegate(&controls_delegate); _assignDelegate = new MapperAssignDelegate(this); _controlsDelegate = new MapperControlDelegate(this); ///_assignDelegate->setItemEditorFactory(factory); ///_controlsDelegate->setItemEditorFactory(factory); controlList->setItemDelegate(_controlsDelegate); MusECore::AudioMidiCtrlStructMap macm; int port, chan, ctl_num; bool is_output_port; QList new_items; for(MusECore::ciCtrlList icl = _actrls->begin(); icl != _actrls->end(); ++icl) { MusECore::CtrlList* cl = icl->second; if(cl->dontShow()) continue; QTreeWidgetItem* control_item = new QTreeWidgetItem; new_items.append(control_item); control_item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); control_item->setData(C_NAME, Qt::UserRole, cl->id()); //control_item->setData(C_SHOW, Qt::EditRole, cl->isVisible()); //control_item->setData(C_SHOW, Qt::CheckStateRole, cl->isVisible()); // To make a checkbox appear at all, must call setCheckState. //control_item->setCheckState(C_SHOW, cl->isVisible() ? Qt::Checked : Qt::Unchecked); control_item->setData(C_NAME, Qt::CheckStateRole, cl->isVisible() ? Qt::Checked : Qt::Unchecked); control_item->setData(C_NAME, Qt::DisplayRole, cl->name()); // Not editable. //control_item->setData(C_COLOR, Qt::EditRole, cl->color()); //control_item->setData(C_COLOR, Qt::DecorationRole, cl->color()); control_item->setData(C_NAME, Qt::DecorationRole, cl->color()); macm.clear(); map->find_audio_ctrl_structs(cl->id(), &macm); ///int ctl_type = -1; // "" ctl_num = -1; port = -1; chan = 0; is_output_port = false; QString port_name = "---"; QString ctl_type_name = "---"; for(MusECore::ciAudioMidiCtrlStructMap imacm = macm.begin(); imacm != macm.end(); ++imacm) { // TODO: Only one connection to this audio control supported for now. map->hash_values((*imacm)->first, &port, &chan, &ctl_num, &is_output_port); MusECore::MidiController::ControllerType t = MusECore::midiControllerType(ctl_num); if(t == MusECore::MidiController::PolyAftertouch) // TODO Not supported yet. Need a way to select pitch. continue; ///ctl_type = t; ctl_type_name = MusECore::int2ctrlType(t) + ":" + MusECore::midiCtrlNumString(ctl_num); if(port != -1) port_name = QString("%1:%2").arg(port + 1).arg(MusEGlobal::midiPorts[port].portname()); break; } control_item->setData(C_ASSIGN_PORT, Qt::DisplayRole, port_name); control_item->setData(C_ASSIGN_PORT, Qt::UserRole, port); control_item->setData(C_ASSIGN_CHAN, Qt::EditRole, chan); control_item->setData(C_MCTL_NUM, Qt::DisplayRole, ctl_type_name); ///control_item->setData(C_MCTL_NUM, Qt::UserRole, ctl_type); ///control_item->setData(C_MCTL_H, Qt::EditRole, (ctl_num >> 8) & 0x7f); ///control_item->setData(C_MCTL_L, Qt::EditRole, ctl_num & 0x7f); control_item->setData(C_MCTL_NUM, Qt::UserRole, ctl_num); control_item->setData(C_MCTL_NUM, UserRole2, (void*)_mctrls); } controlList->addTopLevelItems(new_items); for(int i = 0; i < C_COL_END; ++i) controlList->resizeColumnToContents(i); controlList->setEditTriggers(QAbstractItemView::AllEditTriggers); connect(controlList, SIGNAL(itemChanged(QTreeWidgetItem*, int)), SLOT(controlsItemChanged(QTreeWidgetItem*, int))); /* controlTypeComboBox->addItem(tr("Control7"), MusECore::MidiController::Controller7); controlTypeComboBox->addItem(tr("Control14"), MusECore::MidiController::Controller14); controlTypeComboBox->addItem(tr("RPN"), MusECore::MidiController::RPN); controlTypeComboBox->addItem(tr("NPRN"), MusECore::MidiController::NRPN); controlTypeComboBox->addItem(tr("RPN14"), MusECore::MidiController::RPN14); controlTypeComboBox->addItem(tr("NRPN14"), MusECore::MidiController::NRPN14); controlTypeComboBox->addItem(tr("Pitch"), MusECore::MidiController::Pitch); controlTypeComboBox->addItem(tr("Program"), MusECore::MidiController::Program); //controlTypeComboBox->addItem(tr("PolyAftertouch"), MusECore::MidiController::PolyAftertouch); // Not supported yet. Need a way to select pitch. controlTypeComboBox->addItem(tr("Aftertouch"), MusECore::MidiController::Aftertouch); controlTypeComboBox->setCurrentIndex(0); _port = port; _chan = chan; _ctrl = ctrl; _is_learning = false; update(); connect(learnPushButton, SIGNAL(clicked(bool)), SLOT(learnChanged(bool))); connect(portComboBox, SIGNAL(currentIndexChanged(int)), SLOT(portChanged(int))); connect(channelSpinBox, SIGNAL(valueChanged(int)), SLOT(chanChanged())); connect(controlTypeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(ctrlTypeChanged(int))); connect(ctrlHiSpinBox, SIGNAL(valueChanged(int)), SLOT(ctrlHChanged())); connect(ctrlLoSpinBox, SIGNAL(valueChanged(int)), SLOT(ctrlLChanged())); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat()));*/ } ControlMapperDialog::~ControlMapperDialog() { // if(_controlsDelegate) // delete _controlsDelegate; // if(_assignDelegate) // delete _assignDelegate; } void ControlMapperDialog::heartbeat() { // if(_is_learning) // { // if(MusEGlobal::midiLearnPort != -1) // { // int port_item = portComboBox->findData(MusEGlobal::midiLearnPort); // if(port_item != -1 && port_item != portComboBox->currentIndex()) // { // _port = MusEGlobal::midiLearnPort; // portComboBox->blockSignals(true); // portComboBox->setCurrentIndex(port_item); // portComboBox->blockSignals(false); // } // } // // if(MusEGlobal::midiLearnChan != -1 && (MusEGlobal::midiLearnChan + 1) != channelSpinBox->value()) // { // _chan = MusEGlobal::midiLearnChan; // channelSpinBox->blockSignals(true); // channelSpinBox->setValue(_chan + 1); // channelSpinBox->blockSignals(false); // } // // if(MusEGlobal::midiLearnCtrl != -1) // { // MusECore::MidiController::ControllerType type = MusECore::midiControllerType(MusEGlobal::midiLearnCtrl); // int idx = controlTypeComboBox->findData(type); // if(idx != -1 && idx != controlTypeComboBox->currentIndex()) // { // controlTypeComboBox->blockSignals(true); // controlTypeComboBox->setCurrentIndex(idx); // controlTypeComboBox->blockSignals(false); // } // // int hv = (MusEGlobal::midiLearnCtrl >> 8) & 0xff; // int lv = MusEGlobal::midiLearnCtrl & 0xff; // // switch(type) // { // case MusECore::MidiController::Program: // case MusECore::MidiController::Pitch: // case MusECore::MidiController::PolyAftertouch: // Unsupported yet. Need a way to select pitch. // case MusECore::MidiController::Aftertouch: // ctrlHiSpinBox->setEnabled(false); // ctrlLoSpinBox->setEnabled(false); // ctrlHiSpinBox->blockSignals(true); // ctrlLoSpinBox->blockSignals(true); // ctrlHiSpinBox->setValue(0); // ctrlLoSpinBox->setValue(0); // ctrlHiSpinBox->blockSignals(false); // ctrlLoSpinBox->blockSignals(false); // break; // case MusECore::MidiController::Controller7: // ctrlHiSpinBox->setEnabled(false); // ctrlLoSpinBox->setEnabled(true); // // ctrlHiSpinBox->blockSignals(true); // ctrlHiSpinBox->setValue(0); // ctrlHiSpinBox->blockSignals(false); // // if(lv != ctrlLoSpinBox->value()) // { // ctrlLoSpinBox->blockSignals(true); // ctrlLoSpinBox->setValue(lv); // ctrlLoSpinBox->blockSignals(false); // } // break; // case MusECore::MidiController::Controller14: // case MusECore::MidiController::RPN: // case MusECore::MidiController::RPN14: // case MusECore::MidiController::NRPN: // case MusECore::MidiController::NRPN14: // ctrlHiSpinBox->setEnabled(true); // ctrlLoSpinBox->setEnabled(true); // if(hv != ctrlHiSpinBox->value()) // { // ctrlHiSpinBox->blockSignals(true); // ctrlHiSpinBox->setValue(hv); // ctrlHiSpinBox->blockSignals(false); // } // if(lv != ctrlLoSpinBox->value()) // { // ctrlLoSpinBox->blockSignals(true); // ctrlLoSpinBox->setValue(lv); // ctrlLoSpinBox->blockSignals(false); // } // break; // default: // printf("FIXME: MidiAudioControl::heartbeat: Unknown control type: %d\n", type); // break; // } // // _ctrl = MusECore::midiCtrlTerms2Number(type, (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value()); // } // } } void ControlMapperDialog::learnChanged(bool v) { _is_learning = v; if(_is_learning) MusEGlobal::audio->msgStartMidiLearn(); // Resets the learn values to -1. } void ControlMapperDialog::resetLearn() { _is_learning = false; learnPushButton->blockSignals(true); learnPushButton->setChecked(false); learnPushButton->blockSignals(false); MusEGlobal::audio->msgStartMidiLearn(); // Resets the learn values to -1. } // void ControlMapperDialog::portChanged(int idx) // { // if(idx == -1) // return; // int port_num = portComboBox->itemData(idx).toInt(); // if(port_num < 0 || port_num >= MIDI_PORTS) // return; // // _port = port_num; // resetLearn(); // } // // void MidiAudioControl::chanChanged() // { // _chan = channelSpinBox->value() - 1; // resetLearn(); // } // // void MidiAudioControl::updateCtrlBoxes() // { // if(controlTypeComboBox->currentIndex() == -1) // return; // MusECore::MidiController::ControllerType t = (MusECore::MidiController::ControllerType)controlTypeComboBox->itemData(controlTypeComboBox->currentIndex()).toInt(); // // switch(t) // { // case MusECore::MidiController::Program: // case MusECore::MidiController::Pitch: // case MusECore::MidiController::PolyAftertouch: // Unsupported yet. Need a way to select pitch. // case MusECore::MidiController::Aftertouch: // ctrlHiSpinBox->setEnabled(false); // ctrlLoSpinBox->setEnabled(false); // ctrlHiSpinBox->blockSignals(true); // ctrlLoSpinBox->blockSignals(true); // ctrlHiSpinBox->setValue(0); // ctrlLoSpinBox->setValue(0); // ctrlHiSpinBox->blockSignals(false); // ctrlLoSpinBox->blockSignals(false); // break; // case MusECore::MidiController::Controller7: // ctrlHiSpinBox->setEnabled(false); // ctrlLoSpinBox->setEnabled(true); // ctrlHiSpinBox->blockSignals(true); // ctrlHiSpinBox->setValue(0); // ctrlHiSpinBox->blockSignals(false); // break; // case MusECore::MidiController::Controller14: // case MusECore::MidiController::RPN: // case MusECore::MidiController::RPN14: // case MusECore::MidiController::NRPN: // case MusECore::MidiController::NRPN14: // ctrlHiSpinBox->setEnabled(true); // ctrlLoSpinBox->setEnabled(true); // break; // default: // printf("FIXME: MidiAudioControl::updateCtrlBoxes: Unknown control type: %d\n", t); // break; // } // } // // void MidiAudioControl::ctrlTypeChanged(int idx) // { // if(idx == -1) // return; // // updateCtrlBoxes(); // // _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); // _ctrl = MusECore::midiCtrlTerms2Number((MusECore::MidiController::ControllerType)controlTypeComboBox->itemData(idx).toInt(), _ctrl); // // resetLearn(); // } // // void MidiAudioControl::ctrlHChanged() // { // if(controlTypeComboBox->currentIndex() == -1) // return; // _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); // _ctrl = MusECore::midiCtrlTerms2Number((MusECore::MidiController::ControllerType)controlTypeComboBox->itemData(controlTypeComboBox->currentIndex()).toInt(), _ctrl); // // resetLearn(); // } // // void MidiAudioControl::ctrlLChanged() // { // if(controlTypeComboBox->currentIndex() == -1) // return; // _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); // _ctrl = MusECore::midiCtrlTerms2Number((MusECore::MidiController::ControllerType)controlTypeComboBox->itemData(controlTypeComboBox->currentIndex()).toInt(), _ctrl); // // resetLearn(); // } void ControlMapperDialog::configChanged() { doUpdate(); } void ControlMapperDialog::doUpdate() { // portComboBox->blockSignals(true); // portComboBox->clear(); // // int item_idx = 0; // for (int i = 0; i < MIDI_PORTS; ++i) { // MusECore::MidiDevice* md = MusEGlobal::midiPorts[i].device(); // if(!md) // In the case of this combo box, don't bother listing empty ports. // continue; // //if(!(md->rwFlags() & 1 || md->isSynti()) && (i != outPort)) // if(!(md->rwFlags() & 2) && (i != _port)) // Only readable ports, or current one. // continue; // QString name; // name.sprintf("%d:%s", i+1, MusEGlobal::midiPorts[i].portname().toLatin1().constData()); // portComboBox->insertItem(item_idx, name, i); // if(_port == -1) // _port = i; // Initialize // if(i == _port) // portComboBox->setCurrentIndex(item_idx); // item_idx++; // } // portComboBox->blockSignals(false); // // channelSpinBox->blockSignals(true); // channelSpinBox->setValue(_chan + 1); // channelSpinBox->blockSignals(false); // // int type = MusECore::midiControllerType(_ctrl); // int idx = controlTypeComboBox->findData(type); // if(idx != -1 && idx != controlTypeComboBox->currentIndex()) // { // controlTypeComboBox->blockSignals(true); // controlTypeComboBox->setCurrentIndex(idx); // controlTypeComboBox->blockSignals(false); // } // // int hv = (_ctrl >> 8) & 0xff; // int lv = _ctrl & 0xff; // // switch(type) // { // case MusECore::MidiController::Program: // case MusECore::MidiController::Pitch: // case MusECore::MidiController::PolyAftertouch: // Unsupported yet. Need a way to select pitch. // case MusECore::MidiController::Aftertouch: // ctrlHiSpinBox->setEnabled(false); // ctrlLoSpinBox->setEnabled(false); // ctrlHiSpinBox->blockSignals(true); // ctrlLoSpinBox->blockSignals(true); // ctrlHiSpinBox->setValue(0); // ctrlLoSpinBox->setValue(0); // ctrlHiSpinBox->blockSignals(false); // ctrlLoSpinBox->blockSignals(false); // break; // case MusECore::MidiController::Controller7: // ctrlHiSpinBox->setEnabled(false); // ctrlLoSpinBox->setEnabled(true); // // ctrlHiSpinBox->blockSignals(true); // ctrlHiSpinBox->setValue(0); // ctrlHiSpinBox->blockSignals(false); // // if(lv != ctrlLoSpinBox->value()) // { // ctrlLoSpinBox->blockSignals(true); // ctrlLoSpinBox->setValue(lv); // ctrlLoSpinBox->blockSignals(false); // } // break; // case MusECore::MidiController::Controller14: // case MusECore::MidiController::RPN: // case MusECore::MidiController::RPN14: // case MusECore::MidiController::NRPN: // case MusECore::MidiController::NRPN14: // ctrlHiSpinBox->setEnabled(true); // ctrlLoSpinBox->setEnabled(true); // if(hv != ctrlHiSpinBox->value()) // { // ctrlHiSpinBox->blockSignals(true); // ctrlHiSpinBox->setValue(hv); // ctrlHiSpinBox->blockSignals(false); // } // if(lv != ctrlLoSpinBox->value()) // { // ctrlLoSpinBox->blockSignals(true); // ctrlLoSpinBox->setValue(lv); // ctrlLoSpinBox->blockSignals(false); // } // break; // default: // printf("FIXME: MidiAudioControl::updateCtrlBoxes: Unknown control type: %d\n", type); // break; // } } void ControlMapperDialog::controlsItemChanged(QTreeWidgetItem* item, int col) { // REMOVE Tim. fprintf(stderr, "ControlMapperDialog::controlsItemChanged col:%d checkstate:%d edit:%s display:%s deco:%x\n", col, item->data(col, Qt::CheckStateRole).toInt(), item->data(col, Qt::EditRole).toString().toLatin1().constData(), //item->data(col, Qt::DisplayRole).toInt(), item->data(col, Qt::DisplayRole).toString().toLatin1().constData(), item->data(col, Qt::DecorationRole).value().rgb()); // int id = item->data(C_NAME, Qt::UserRole).toInt(); // MusECore::iCtrlList icl = _actrls->find(id); // if(icl == _actrls->end()) // fprintf(stderr, "ControlMapperDialog::controlsItemChanged audio control id not found:%d\n", id); switch(col) { case C_NAME: { MusECore::iCtrlList icl = _actrls->find(item->data(C_NAME, Qt::UserRole).toInt()); if(icl != _actrls->end()) { icl->second->setColor(item->data(col, Qt::DecorationRole).value().rgb()); int check_state = item->data(col, Qt::CheckStateRole).toInt(); icl->second->setVisible(check_state == Qt::Checked ? true : false); } } break; case C_ASSIGN_PORT: { int id = item->data(C_NAME, Qt::UserRole).toInt(); MusECore::iCtrlList icl = _actrls->find(id); if(icl != _actrls->end()) { int new_port = item->data(C_ASSIGN_PORT, Qt::UserRole).toInt(); MusECore::MidiAudioCtrlMap* map = _actrls->midiControls(); int port, chan, ctl_num; bool is_output_port = false; MusECore::AudioMidiCtrlStructMap macm; map->find_audio_ctrl_structs(id, &macm); // Is there already something assigned to this audio control? if(macm.size()) { for(MusECore::ciAudioMidiCtrlStructMap imacm = macm.begin(); imacm != macm.end(); ++imacm) { map->hash_values((*imacm)->first, &port, &chan, &ctl_num, &is_output_port); if(port == new_port) continue; map->erase_ctrl_struct(port, chan, ctl_num, is_output_port, id); map->add_ctrl_struct(new_port, chan, ctl_num, is_output_port, MusECore::MidiAudioCtrlStruct(id)); // TODO: Only one connection to this audio control supported for now. break; } } // No assignments found. Create a new assignment if port is valid. else if(new_port != -1) { ///ctl_num = ((item->data(C_MCTL_H, Qt::UserRole).toInt() & 0x7f) << 8) | (item->data(C_MCTL_L, Qt::UserRole).toInt() & 0x7f); ctl_num = _mctrls->findFreeController(); chan = item->data(C_ASSIGN_CHAN, Qt::UserRole).toInt(); if(chan < 0) chan = 0; else if(chan >= MIDI_CHANNELS) chan = MIDI_CHANNELS - 1; map->add_ctrl_struct(new_port, chan, ctl_num, is_output_port, MusECore::MidiAudioCtrlStruct(id)); controlList->blockSignals(true); //item->setData(C_ASSIGN_CHAN, Qt::EditRole, chan); item->setData(C_MCTL_NUM, Qt::DisplayRole, MusECore::int2ctrlType(MusECore::MidiController::Controller7)); item->setData(C_MCTL_NUM, Qt::UserRole, MusECore::MidiController::Controller7); //item->setData(C_MCTL_H, Qt::EditRole, (ctl_num >> 8) & 0x7f); //item->setData(C_MCTL_L, Qt::EditRole, ctl_num & 0x7f); controlList->blockSignals(false); // TODO midi controllers... } } } break; case C_ASSIGN_CHAN: break; case C_MCTL_NUM: { int id = item->data(C_NAME, Qt::UserRole).toInt(); MusECore::iCtrlList icl = _actrls->find(id); if(icl != _actrls->end()) { //int new_type = item->data(C_MCTL_NUM, Qt::UserRole).toInt(); int new_ctl_num = item->data(C_MCTL_NUM, Qt::UserRole).toInt(); MusECore::MidiAudioCtrlMap* map = _actrls->midiControls(); int port, chan, ctl_num; bool is_output_port = false; MusECore::AudioMidiCtrlStructMap macm; map->find_audio_ctrl_structs(id, &macm); // Is there already something assigned to this audio control? if(macm.size()) { for(MusECore::ciAudioMidiCtrlStructMap imacm = macm.begin(); imacm != macm.end(); ++imacm) { map->hash_values((*imacm)->first, &port, &chan, &ctl_num, &is_output_port); ///MusECore::MidiController::ControllerType type = MusECore::midiControllerType(ctl_num); // TODO Per-pitch controls not supported yet. Need a way to select pitch. ///if(type == new_type || type == MusECore::MidiController::PolyAftertouch) /// continue; ///map->erase_ctrl_struct(port, chan, ctl_num, is_output_port, id); // TODO: Only one connection to this audio control supported for now. ///if(new_type == -1) if(new_ctl_num == -1) { map->erase_ctrl_struct(port, chan, ctl_num, is_output_port, id); controlList->blockSignals(true); item->setData(C_ASSIGN_PORT, Qt::UserRole, -1); item->setData(C_ASSIGN_PORT, Qt::DisplayRole, QString("---")); //item->setData(C_ASSIGN_CHAN, Qt::EditRole, chan); //item->setData(C_MCTL_TYPE, Qt::DisplayRole, MusECore::int2ctrlType(MusECore::MidiController::Controller7)); //item->setData(C_MCTL_TYPE, Qt::UserRole, MusECore::MidiController::Controller7); //item->setData(C_MCTL_H, Qt::EditRole, (ctl_num >> 8) & 0x7f); //item->setData(C_MCTL_L, Qt::EditRole, ctl_num & 0x7f); controlList->blockSignals(false); break; // Done. We just wanted to erase. } // TODO Erase and create midi controllers. ///map->add_ctrl_struct(port, chan, MusECore::midiCtrlTerms2Number((MusECore::MidiController::ControllerType)new_type, ctl_num & 0xffff), MusECore::MidiAudioCtrlStruct(id)); map->add_ctrl_struct(port, chan, new_ctl_num, is_output_port, MusECore::MidiAudioCtrlStruct(id)); break; } } // No assignments found. Create a new assignment if port is valid. ///else if(new_type != -1) else if(new_ctl_num != -1) { port = item->data(C_ASSIGN_PORT, Qt::UserRole).toInt(); // Can be -1 indicating not assigned from any input. if(port < -1) port = -1; else if(port >= MIDI_PORTS) port = MIDI_PORTS - 1; chan = item->data(C_ASSIGN_CHAN, Qt::UserRole).toInt(); ///ctl_num = ((item->data(C_MCTL_H, Qt::UserRole).toInt() & 0x7f) << 8) | (item->data(C_MCTL_L, Qt::UserRole).toInt() & 0x7f); if(chan < 0) chan = 0; else if(chan >= MIDI_CHANNELS) chan = MIDI_CHANNELS - 1; ///map->add_ctrl_struct(port, chan, MusECore::midiCtrlTerms2Number((MusECore::MidiController::ControllerType)new_type, ctl_num & 0xffff), MusECore::MidiAudioCtrlStruct(id)); map->add_ctrl_struct(port, chan, new_ctl_num, is_output_port, MusECore::MidiAudioCtrlStruct(id)); //controlList->blockSignals(true); //item->setData(C_ASSIGN_PORT, Qt::UserRole, port); //item->setData(C_ASSIGN_CHAN, Qt::EditRole, chan); //item->setData(C_MCTL_TYPE, Qt::DisplayRole, MusECore::int2ctrlType(MusECore::MidiController::Controller7)); //item->setData(C_MCTL_TYPE, Qt::UserRole, MusECore::MidiController::Controller7); //item->setData(C_MCTL_H, Qt::EditRole, (ctl_num >> 8) & 0x7f); //item->setData(C_MCTL_L, Qt::EditRole, ctl_num & 0x7f); //controlList->blockSignals(false); // TODO midi controllers... } } } break; ///case C_MCTL_H: /// break; ///case C_MCTL_L: /// break; default: break; } } } // namespace MusEGui muse-4.0.0/src/muse/components/control_mapper.h000066400000000000000000000157411404105311000216030ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // control_mapper.h // Copyright (C) 2012 by Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CONTROL_MAPPER_H__ #define __CONTROL_MAPPER_H__ #include "ui_control_mapper_base.h" #include "track.h" #include "ctrl.h" #include "midictrl.h" #include #include #include class QPaintEvent; class QColor; class QSize; class QMouseEvent; class QStyle; class QSpinBox; class QHBoxLayout; class QPixmap; namespace MusEGui { class ColorListEditor : public QComboBox { Q_OBJECT Q_PROPERTY(QColor color READ color WRITE setColor USER true) public: ColorListEditor(QWidget *widget = NULL); public: QColor color() const; void setColor(QColor c); private: void populateList(); }; class MidiCtrlDelegateEditor : public QWidget { Q_OBJECT //Q_PROPERTY(int color READ color WRITE setColor USER true) private: QComboBox* _typeCombo; QSpinBox* _numHiSpinBox; QSpinBox* _numLoSpinBox; QWidget* _validIndicator; QHBoxLayout* _hlayout; protected: void paintEvent(QPaintEvent*); void mousePressEvent(QMouseEvent*); void mouseReleaseEvent(QMouseEvent*); //bool event(QEvent*); QSize sizeHint() const; QSize minimumSizeHint() const; signals: void activated(int ctrlNum); public: MidiCtrlDelegateEditor(QWidget* parent = NULL); virtual ~MidiCtrlDelegateEditor(); int controlNum() const; void setControlNum(int ctrlNum); }; class ColorChooserEditor : public QWidget { Q_OBJECT Q_PROPERTY(QColor color READ color WRITE setColor USER true) private: QColor _color; protected: void paintEvent(QPaintEvent*); void mousePressEvent(QMouseEvent*); void mouseReleaseEvent(QMouseEvent*); //bool event(QEvent*); QSize sizeHint() const; QSize minimumSizeHint() const; signals: void activated(const QColor& color); public: static QColor colorChooserList[]; ColorChooserEditor(QWidget* parent = NULL); virtual ~ColorChooserEditor(); QColor color() const; void setColor(QColor c); }; //----------------------------------- // MapperAssignDelegate //----------------------------------- class MapperAssignDelegate : public QStyledItemDelegate { Q_OBJECT public: MapperAssignDelegate(QWidget *parent = 0) : QStyledItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; private slots: void commitAndCloseEditor(); }; //----------------------------------- // MapperControlDelegate //----------------------------------- class MapperControlDelegate : public QStyledItemDelegate { Q_OBJECT private: QStyle::SubElement _currentSubElement; // Set in mouse press, checked in release to prevent unwanted editor opening. // Need this. For some reason when using CurrentChanged trigger, createEditor is called upon opening the dialog, yet nothing is selected. bool _firstPress; QRect getItemRectangle(const QStyleOptionViewItem& option, const QModelIndex& index, QStyle::SubElement subElement, QWidget* editor = NULL) const; bool subElementHitTest(const QPoint& point, const QStyleOptionViewItem& option, const QModelIndex& index, QStyle::SubElement* subElement, QWidget* editor = NULL) const; public: MapperControlDelegate(QWidget *parent = NULL); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; void setEditorData(QWidget *editor, const QModelIndex &index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; protected: bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index); bool eventFilter(QObject* editor, QEvent* event); void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const; private slots: void editorChanged(); //void commitAndCloseEditor(); }; class ControlMapperDialog : public QDialog, public Ui::ControlMapperBase { Q_OBJECT private: MusECore::Track* _track; MusECore::CtrlListList* _actrls; MusECore::MidiControllerList* _mctrls; MapperAssignDelegate* _assignDelegate; MapperControlDelegate* _controlsDelegate; // int _port, _chan, _ctrl; bool _is_learning; void doUpdate(); void resetLearn(); //void updateCtrlBoxes(); private slots: void heartbeat(); void learnChanged(bool); //void portChanged(int); //void chanChanged(); //void ctrlTypeChanged(int); //void ctrlHChanged(); //void ctrlLChanged(); void configChanged(); void controlsItemChanged(QTreeWidgetItem*, int); public: enum MapperAssignCols { A_NAME=0, A_PORT, A_CHAN, A_MCTL_TYPE, A_MCTL_H, A_MCTL_L, A_MCTL_PASS_THRU, A_COL_END }; //enum MapperControlCols { C_NAME=0, C_COLOR, C_ASSIGN, C_MCTL_TYPE, C_MCTL_H, C_MCTL_L, C_COL_END }; enum MapperControlCols { C_NAME=0, C_ASSIGN_PORT, C_ASSIGN_CHAN, C_MCTL_NUM, C_MCTL_H, C_MCTL_L, C_COL_END }; enum UserRolesExt { UserRole2 = Qt::UserRole+1 }; ControlMapperDialog(MusECore::Track* t, QWidget* parent = NULL); ~ControlMapperDialog(); // int port() const { return _port; } // int chan() const { return _chan; } // int ctrl() const { return _ctrl; } MusECore::Track* track() const { return _track; } }; } #endif // __CONTROL_MAPPER_H__ muse-4.0.0/src/muse/components/control_mapper_base.ui000066400000000000000000000060731404105311000227610ustar00rootroot00000000000000 ControlMapperBase 0 0 475 220 Control mapper 8 QAbstractItemView::NoEditTriggers QAbstractItemView::SelectRows true 1 0 0 &Learn true Qt::Horizontal 40 20 0 0 Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() ControlMapperBase accept() 248 254 157 274 buttonBox rejected() ControlMapperBase reject() 316 260 286 274 muse-4.0.0/src/muse/components/copy_on_write.cpp000066400000000000000000000023371404105311000217670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // copy_on_write.cpp // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "copy_on_write.h" namespace MusEGui { CopyOnWriteDialog::CopyOnWriteDialog(QWidget* parent): QDialog(parent) { setupUi(this); } void CopyOnWriteDialog::addProjDirFile(const QString& s) { projDirList->addItem(s); } } // namespace MusEGuimuse-4.0.0/src/muse/components/copy_on_write.h000066400000000000000000000024531404105311000214330ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // copy_on_write.h // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __COPY_ON_WRITE_H__ #define __COPY_ON_WRITE_H__ #include "ui_copy_on_write_base.h" namespace MusEGui { class CopyOnWriteDialog : public QDialog, public Ui::CopyOnWriteDialogBase { Q_OBJECT public: CopyOnWriteDialog(QWidget* parent = 0); void addProjDirFile(const QString&); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/copy_on_write_base.ui000066400000000000000000000052201404105311000226060ustar00rootroot00000000000000 CopyOnWriteDialogBase 0 0 431 344 Copy Wave Files QFrame::StyledPanel QFrame::Raised Some sound files will be copied to the Project Directory, either because they are not writable or because more than one independent Wave Event shares them. (If you prefer instead that the Wave Events are inter-dependent, try using Clone Parts.) Multiple copies will be made in some cases. If no Project has been created yet, you will be asked to, giving another chance to cancel. true These files will be copied to the Project Directory: Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() CopyOnWriteDialogBase accept() 248 254 157 274 buttonBox rejected() CopyOnWriteDialogBase reject() 316 260 286 274 muse-4.0.0/src/muse/components/cpu_toolbar.cpp000066400000000000000000000150611404105311000214160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // cpu_toolbar.cpp // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include "cpu_toolbar.h" namespace MusEGui { //--------------------------------- // PaddedValueLabel //--------------------------------- PaddedValueLabel::PaddedValueLabel(bool isFloat, QWidget* parent, Qt::WindowFlags f, const QString& prefix, const QString& suffix) :QLabel(parent, f), _isFloat(isFloat), _prefix(prefix), _suffix(suffix) { setObjectName("PaddedValueLabel"); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); _iVal = 0; _dVal = 0.0; _fieldWidth = 2; _precision = 1; updateText(); } void PaddedValueLabel::setFieldWidth(int val) { _fieldWidth = val; if(_fieldWidth < 0) _fieldWidth = 0; updateText(); } void PaddedValueLabel::setPrecision(int val) { _precision = val; if(_precision < 0) _precision = 0; updateText(); } void PaddedValueLabel::setIntValue(int val) { _iVal = val; updateText(); } void PaddedValueLabel::setFloatValue(double val) { _dVal = val; updateText(); } void PaddedValueLabel::updateText() { if(_isFloat) setText(QString("%1%L2%3").arg(_prefix).arg(_dVal, 0, 'f', _precision).arg(_suffix)); else setText(QString("%1%2%3").arg(_prefix).arg(_iVal).arg(_suffix)); } QSize PaddedValueLabel::sizeHint() const { QString s; if(_isFloat) s = QString("%1%L2%3").arg(_prefix).arg(8.8888, _fieldWidth, 'f', _precision, QLatin1Char('8')).arg(_suffix); else s = QString("%1%2%3").arg(_prefix).arg(8, _fieldWidth, 10, QLatin1Char('8')).arg(_suffix); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 const int w = fontMetrics().horizontalAdvance(s); #else const int w = fontMetrics().width(s); #endif const int h = QLabel::sizeHint().height(); return QSize(w, h); } XRunLabel::XRunLabel(QWidget* parent) : PaddedValueLabel(false, parent, Qt::Widget, "XRUNS: ") { ensurePolished(); defTextColor = palette().color(QPalette::WindowText).name(); } void XRunLabel::updateText() { if (_iVal > 0) setStyleSheet("QLabel { color : red; }"); else setStyleSheet("QLabel { color : " + defTextColor + "; }"); PaddedValueLabel::updateText(); } void XRunLabel::mouseDoubleClickEvent(QMouseEvent *event) { Q_UNUSED(event) emit doubleclicked(); } //--------------------------------- // CpuToolbar //--------------------------------- CpuToolbar::CpuToolbar(QWidget* parent) : QToolBar(parent) { init(); } CpuToolbar::CpuToolbar(const QString& title, QWidget* parent) : QToolBar(title, parent) { init(); } void CpuToolbar::init() { setObjectName("CpuLoadToolbar"); _resetButton = new QToolButton(this); _resetButton->setToolButtonStyle(Qt::ToolButtonIconOnly); _resetButton->setIcon(QIcon(":/svg/cpu.svg")); _resetButton->setObjectName("CpuLoadToolbarButton"); _resetButton->setToolTip(tr("CPU load averaged over each GUI update period\nDSP load read from JACK\nNumber of xruns\n(click to reset)")); _cpuLabel = new PaddedValueLabel(true, this, Qt::Widget, "CPU: ", "%"); _cpuLabel->setFieldWidth(5); _cpuLabel->setPrecision(1); _cpuLabel->setIndent(2); _dspLabel = new PaddedValueLabel(true, this, Qt::Widget, "DSP: ", "%"); _dspLabel->setFieldWidth(5); _dspLabel->setPrecision(1); _xrunsLabel = new PaddedValueLabel(false, this, Qt::Widget, "XRUNS: "); _xrunsLabel->setFieldWidth(3); setValues(0.0f, 0.0f, 0); addWidget(_resetButton); addWidget(_cpuLabel); addWidget(_dspLabel); addWidget(_xrunsLabel); connect(_resetButton, SIGNAL(clicked(bool)), SIGNAL(resetClicked())); } void CpuToolbar::setValues(float cpuLoad, float dspLoad, long xRunsCount) { _cpuLabel->setFloatValue(cpuLoad); _dspLabel->setFloatValue(dspLoad); _xrunsLabel->setIntValue(xRunsCount); } //--------------------------------- // CpuStatusbar //--------------------------------- CpuStatusBar::CpuStatusBar(QWidget* parent) : QWidget(parent) { setObjectName("CpuLoadStatusBar"); setToolTip(tr("CPU load averaged over each GUI update period\nDSP load read from JACK\nNumber of xruns\n(click to reset)")); cpuLabel = new PaddedValueLabel(true, this, Qt::Widget, "CPU: ", "%"); cpuLabel->setToolTip(tr("CPU load averaged over each GUI update period.")); cpuLabel->setStatusTip(tr("CPU load averaged over each GUI update period.")); cpuLabel->setFieldWidth(5); cpuLabel->setPrecision(1); dspLabel = new PaddedValueLabel(true, this, Qt::Widget, "DSP: ", "%"); dspLabel->setToolTip(tr("DSP load read from JACK.")); dspLabel->setStatusTip(tr("DSP load read from JACK.")); dspLabel->setFieldWidth(5); dspLabel->setPrecision(1); xrunsLabel = new XRunLabel(this); xrunsLabel->setToolTip(tr("Number of xruns.\nDouble-click to reset.")); xrunsLabel->setStatusTip(tr("Number of xruns.\nDouble-click to reset.")); xrunsLabel->setFieldWidth(3); setValues(0.0f, 0.0f, 0); QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(4, 0, 4, 0); layout->setSpacing(2); layout->addWidget(cpuLabel); layout->addWidget(dspLabel); layout->addWidget(xrunsLabel); connect(xrunsLabel, SIGNAL(doubleclicked()), SIGNAL(resetClicked())); } void CpuStatusBar::setValues(float cpuLoad, float dspLoad, long xRunsCount) { cpuLabel->setFloatValue(cpuLoad); dspLabel->setFloatValue(dspLoad); xrunsLabel->setIntValue(xRunsCount); } } // namespace MusEGui muse-4.0.0/src/muse/components/cpu_toolbar.h000066400000000000000000000067461404105311000210750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // cpu_toolbar.h // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CPU_TOOLBAR_H__ #define __CPU_TOOLBAR_H__ #include #include class QWidget; class QString; class QToolButton; class QSize; namespace MusEGui { //--------------------------------- // PaddedValueLabel //--------------------------------- class PaddedValueLabel : public QLabel { Q_OBJECT protected: bool _isFloat; QString _prefix; QString _suffix; int _fieldWidth; int _precision; int _iVal; double _dVal; virtual void updateText(); // signals : // void doubleclicked(); public: PaddedValueLabel(bool isFloat = false, QWidget* parent = nullptr, Qt::WindowFlags f = Qt::Widget, const QString& prefix = QString(), const QString& suffix = QString()); void setFieldWidth(int val); void setPrecision(int val); void setIntValue(int val); void setFloatValue(double val); virtual QSize sizeHint() const; // void mouseDoubleClickEvent(QMouseEvent* event); }; //--------------------------------- // XRunLabel //--------------------------------- class XRunLabel : public PaddedValueLabel { Q_OBJECT QString defTextColor; void updateText() override; signals : void doubleclicked(); public: XRunLabel(QWidget* parent = 0); void mouseDoubleClickEvent(QMouseEvent* event); }; //--------------------------------- // CpuToolbar //--------------------------------- class CpuToolbar : public QToolBar { Q_OBJECT private: QToolButton* _resetButton; PaddedValueLabel* _cpuLabel; PaddedValueLabel* _dspLabel; PaddedValueLabel* _xrunsLabel; void init(); public: CpuToolbar(QWidget* parent = 0); CpuToolbar(const QString& title, QWidget* parent = 0); void setCpuLabelText(const QString&); void setDspLabelText(const QString&); void setXrunsLabelText(const QString&); void setValues(float cpuLoad, float dspLoad, long xRunsCount); signals: void resetClicked(); }; //--------------------------------- // CpuStatusbar //--------------------------------- class CpuStatusBar : public QWidget { Q_OBJECT private: // QToolButton* _resetButton; PaddedValueLabel* cpuLabel; PaddedValueLabel* dspLabel; PaddedValueLabel* xrunsLabel; public: CpuStatusBar(QWidget* parent = nullptr); void setCpuLabelText(const QString&); void setDspLabelText(const QString&); void setXrunsLabelText(const QString&); void setValues(float cpuLoad, float dspLoad, long xRunsCount); signals: void resetClicked(); }; } #endif muse-4.0.0/src/muse/components/custom_widget_actions.cpp000066400000000000000000000736321404105311000235120ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // // custom_widget_actions.cpp // (C) Copyright 2011-2015 Tim E. Real (terminator356 on users.sourceforge.net) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include "menutitlewidgets.h" #include "icons.h" #include "pixmap_button.h" #include "custom_widget_actions.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { //--------------------------------------------------------- // PixmapButtonsWidgetAction //--------------------------------------------------------- //PixmapButtonsWidgetAction::PixmapButtonsWidgetAction(const QString& text, QPixmap* on_pixmap, QPixmap* off_pixmap, const QBitArray& initial, QWidget* parent) // : QWidgetAction(parent) // { // _onPixmap = on_pixmap; // _offPixmap = off_pixmap; // _current = initial; // _text = text; // // Just to be safe, set to -1 instead of default 0. // setData(-1); // } //QWidget* PixmapButtonsWidgetAction::createWidget(QWidget *parent) //{ // const int channels = _current.size(); // QWidget* lw = new QWidget(parent); // QHBoxLayout* layout = new QHBoxLayout(lw); // layout->setSpacing(0); // QLabel* lbl = new QLabel(_text, lw); // lbl->setAlignment(Qt::AlignCenter); // layout->addWidget(lbl); // layout->addSpacing(8); // layout->addStretch(); // PixmapButton* pb = new PixmapButton(toggle_small_Icon, toggle_small_Icon, 2, lw); // Margin = 2 // layout->addWidget(pb); // layout->addSpacing(6); // connect(pb, &PixmapButton::pressed, [this, channels]() { chanClickMap(channels); } ); // for(int i = 0; i < channels; ++i) // { // bool set = _current.at(i); // PixmapButton* b = new PixmapButton(_onPixmap, _offPixmap, 2, lw); // Margin = 2 // _chan_buttons.append(b); // b->setCheckable(true); // b->setDown(set); // connect(b, &PixmapButton::pressed, [this, i]() { chanClickMap(i); } ); // if((i != 0) && (i % 4 == 0)) // layout->addSpacing(6); // layout->addWidget(b); // } // return lw; //} //void PixmapButtonsWidgetAction::chanClickMap(int idx) //{ // const int channels = _current.size(); // const int buttons_sz = _chan_buttons.size(); // if(idx == channels) // One past end = Toggle all button. // { // int allch = 0; // for(; allch < channels; ++allch) // { // if(!_current.at(allch)) // break; // } // if(allch == channels) // _current.fill(false); // else // _current.fill(true); // // Set and redraw the buttons. // for(int i = 0; i < buttons_sz; ++i) // _chan_buttons.at(i)->setDown(allch != channels); // } // else // { // for(int i = 0; i < channels && i < buttons_sz; ++i) // { // if(_chan_buttons.at(i)->isChecked()) // _current.setBit(i); // else // _current.clearBit(i); // } // } //} //void PixmapButtonsWidgetAction::setCurrentState(const QBitArray& state) //{ // _current = state; // const int channels = _current.size(); // const int buttons_sz = _chan_buttons.size(); // // Set and redraw the buttons. // for(int i = 0; i < channels && i < buttons_sz; ++i) // _chan_buttons.at(i)->setDown(_current.at(i)); //} //--------------------------------------------------------- // RouteChannelArray //--------------------------------------------------------- RouteChannelArray::RouteChannelArray(int cols) { _array = 0; _headerVisible = true; _header = 0; _cols = cols; _colsExclusive = false; _exclusiveToggle = false; _activeCol = -1; _pressedCol = -1; init(); } RouteChannelArray::~RouteChannelArray() { if(_header) { delete[] _header; _header = 0; } if(_array) { delete[] _array; _array = 0; } } void RouteChannelArray::init() { if(_header) { delete[] _header; _header = 0; } if(_array) { delete[] _array; _array = 0; } const int sz = columns(); if(sz == 0) return; _array = new RouteChannelArrayItem[sz]; _header = new RouteChannelArrayHeaderItem[sz]; } RouteChannelArray& RouteChannelArray::operator=(const RouteChannelArray& a) { if(a._cols != _cols) { _cols = a._cols; init(); } _headerVisible = a._headerVisible; _arrayTitleItem = a._arrayTitleItem; _headerTitleItem = a._headerTitleItem; _colsExclusive = a._colsExclusive; _exclusiveToggle = a._exclusiveToggle; const int sz = columns(); if(sz == 0) return *this; for(int i = 0; i < sz; ++i) { _array[i] = a._array[i]; _header[i] = a._header[i]; } return *this; } void RouteChannelArray::setColumns(int cols) { if(cols == _cols) return; _cols = cols; init(); } void RouteChannelArray::setValues(int col, bool value, bool exclusive_cols, bool exclusive_toggle) { if(invalidColumn(col)) return; const bool v = (!exclusive_toggle || (exclusive_toggle && value)); if(exclusive_cols) { for(int c = 0; c < _cols; ++c) _array[c]._value = (c == col && v); return; } _array[col]._value = value; } MenuItemControlWidget::MenuItemControlWidget(RoutingMatrixWidgetAction* action, QWidget* parent) : QWidget(parent) { _action = action; setObjectName("MidiRouteMenuItem"); setMouseTracking(true); ensurePolished(); m_highColor = m_highColor.isValid() ? m_highColor : palette().highlight().color(); } void MenuItemControlWidget::elementRect(QRect* checkbox_rect, QRect* label_rect) const { QSize checkbox_sz(0, 0); if(_action->hasCheckBox()) { QStyle* st = style() ? style() : QApplication::style(); if(st) { QStyleOptionButton option; option.state = QStyle::State_Active | QStyle::State_Enabled | QStyle::State_HasFocus | (_action->checkBoxChecked() ? QStyle::State_On : QStyle::State_Off); // checkbox_sz = st->sizeFromContents(QStyle::CT_CheckBox, &option, QSize(0, 0)); //, q); checkbox_sz = st->subElementRect(QStyle::SE_CheckBoxIndicator, &option).size(); } } const QFontMetrics txt_fm(_action->font()); const QSize txt_sz = txt_fm.size(Qt::TextSingleLine, _action->actionText().isEmpty() ? "8" : _action->actionText()); const int menu_item_h = txt_sz.height() > checkbox_sz.height() ? txt_sz.height() : checkbox_sz.height(); if(checkbox_rect) *checkbox_rect = QRect(0, qMax(0, (menu_item_h - checkbox_sz.height()) / 2), checkbox_sz.width(), checkbox_sz.height()); //menu_item_h); if(label_rect) *label_rect = QRect(0, 0, txt_sz.width(), menu_item_h); } QSize MenuItemControlWidget::sizeHint() const { QRect cb_ctrl_rect; QRect lbl_ctrl_rect; elementRect(&cb_ctrl_rect, &lbl_ctrl_rect); const int cb_w = _action->hasCheckBox() ? (_action->actionHMargin + cb_ctrl_rect.x() + cb_ctrl_rect.width()) : 0; const int l_w = cb_w + _action->actionHMargin + lbl_ctrl_rect.x() + lbl_ctrl_rect.width(); const int cb_h = cb_ctrl_rect.y() + cb_ctrl_rect.height(); const int l_h = lbl_ctrl_rect.y() + lbl_ctrl_rect.height(); const int h = l_h > cb_h ? l_h : cb_h; return QSize(cb_w + l_w, h + 1); // add 1 to height to reduce misalignment against the switch widget } void MenuItemControlWidget::paintEvent(QPaintEvent*) { QPainter p(this); QRect cb_ctrl_rect; QRect lbl_ctrl_rect; elementRect(&cb_ctrl_rect, &lbl_ctrl_rect); if(_action->isSelected()) p.fillRect(rect(), m_highColor); if(_action->hasCheckBox()) { QStyle* st = style() ? style() : QApplication::style(); if(st) { QStyleOptionButton option; option.state = QStyle::State_Active | QStyle::State_HasFocus | (_action->isEnabled() ? QStyle::State_Enabled : QStyle::State_ReadOnly) | // Or State_None? (_action->checkBoxChecked() ? QStyle::State_On : QStyle::State_Off) | (_action->menuItemPressed() ? QStyle::State_Sunken : QStyle::State_None); option.rect = QRect(_action->actionHMargin + cb_ctrl_rect.x(), cb_ctrl_rect.y(), cb_ctrl_rect.width(), cb_ctrl_rect.height()); option.palette = palette(); QCheckBox cb; st->drawPrimitive(QStyle::PE_IndicatorCheckBox, &option, &p, &cb); // st->drawControl(QStyle::CE_CheckBox, &option, &p); } } if(!_action->actionText().isEmpty()) { QPalette pal = palette(); pal.setCurrentColorGroup(_action->isEnabled() ? QPalette::Active : QPalette::Disabled); p.setPen(_action->isSelected() ? pal.highlightedText().color() : pal.text().color()); p.setFont(_action->font()); const int l_x = _action->actionHMargin + (_action->hasCheckBox() ? (_action->actionHMargin + cb_ctrl_rect.x() + cb_ctrl_rect.width()) : 0); const QRect l_r(l_x, lbl_ctrl_rect.y(), lbl_ctrl_rect.width(), lbl_ctrl_rect.height()); p.drawText(l_r, Qt::AlignLeft | Qt::AlignVCenter, _action->actionText()); } } //--------------------------------------------------------- // SwitchBarActionWidget //--------------------------------------------------------- SwitchBarActionWidget::SwitchBarActionWidget(RoutingMatrixWidgetAction* action, QWidget* parent) : QWidget(parent) { _action = action; setObjectName("SwitchBarActionWidget"); setMouseTracking(true); ensurePolished(); m_highColor = m_highColor.isValid() ? m_highColor : palette().highlight().color(); } QSize SwitchBarActionWidget::sizeHint() const { const int cols = _action->array()->columns(); const QRect last_array_h_cell = _action->array()->rect(cols - 1); // Take the height of any horizontal header column - they're all the same. const int hdr_h = _action->array()->headerVisible() ? _action->array()->headerRect(0).height() : 0; const int h = hdr_h + last_array_h_cell.height() + _action->itemVSpacing + 2 * _action->margin; // Take the width of any vertical header row - they're all the same. const int w = last_array_h_cell.x() + last_array_h_cell.width() + 2 * _action->margin; return QSize(w, h); } void SwitchBarActionWidget::paintEvent(QPaintEvent* /*event*/) { QPainter p(this); const int cols = _action->array()->columns(); QString a_txt; const QColor clr1(255, 160, 60); const QColor clr2 = clr1.darker(150); p.setRenderHint(QPainter::Antialiasing, true); for(int col = 0; col < cols; ++col) { const QRect r = _action->array()->rect(col); if(col == _action->array()->pressedColumn()) p.fillRect(r.adjusted(0,-1,0,2), palette().dark()); else if(col == _action->array()->activeColumn()) p.fillRect(r.adjusted(0,-1,0,2), m_highColor); const QPixmap* pm = _action->array()->value(col) ? _action->onPixmap() : _action->offPixmap(); const int pm_w = pm ? pm->width() : _action->maxPixmapGeometry().width(); const int pm_h = pm ? pm->height() : _action->maxPixmapGeometry().height(); int x; a_txt = _action->array()->text(col); if(!a_txt.isEmpty()) { p.setFont(_action->font()); //p.setPen(palette().text().color()); p.setPen(col == _action->array()->activeColumn() ? palette().highlightedText().color() : palette().text().color()); p.drawText(r, Qt::AlignLeft | Qt::AlignVCenter, a_txt); x = r.x() + r.width() - pm_w - _action->groupSpacing; // Leave some space at the end. } else { x = r.x(); if(r.width() > pm_w) x += (r.width() - pm_w) / 2; } int y = r.y(); if(r.height() > pm_h) y += (r.height() - pm_h) / 2; if(pm) p.drawPixmap(x, y, *pm); else { QPainterPath path; path.addRoundedRect(x, y, pm_w, pm_h, 30, 30); if(_action->array()->value(col)) { if (m_checkedColor.isValid()) { p.fillPath(path, m_checkedColor); } else { //QRadialGradient gradient(50, 50, 50, 50, 50); QLinearGradient gradient(x + 1, y + 1, x + pm_w - 2, y + pm_h - 2); gradient.setColorAt(0, clr1); gradient.setColorAt(1, clr2); QBrush brush(gradient); p.fillPath(path, brush); } } if (m_borderColor.isValid()) p.setPen(m_borderColor); else p.setPen(col == _action->array()->activeColumn() ? palette().highlightedText().color() : palette().text().color()); p.drawPath(path); } } if(_action->array()->headerVisible()) { p.setPen(palette().text().color()); for(int col = 0; col < cols; ++col) { const QRect r = _action->array()->headerRect(col); const QString str = _action->array()->headerText(col); if(str.isEmpty()) { p.setFont(_action->smallFont()); p.drawText(r, Qt::AlignCenter, QString::number(col + 1)); } else { p.setFont(_action->font()); p.drawText(r, Qt::AlignCenter, str); } } } } //--------------------------------------------------------- // RoutingMatrixActionWidget //--------------------------------------------------------- RoutingMatrixActionWidget::RoutingMatrixActionWidget(RoutingMatrixWidgetAction* action, QWidget* parent) : QWidget(parent) { _action = action; setMouseTracking(true); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); const int layout_m_l = 0, layout_m_r = 0, layout_m_t = 1, layout_m_b = 1; QHBoxLayout* h_layout = new QHBoxLayout(this); h_layout->setSpacing(0); h_layout->setContentsMargins(layout_m_l, layout_m_t, layout_m_r, layout_m_b); QVBoxLayout* left_v_layout = new QVBoxLayout(); QVBoxLayout* right_v_layout = new QVBoxLayout(); left_v_layout->setSpacing(0); right_v_layout->setSpacing(0); left_v_layout->setContentsMargins(0, 0, 0, 0); right_v_layout->setContentsMargins(0, 0, 0, 0); if(!_action->array()->headerTitle().isEmpty() || !_action->array()->checkBoxTitle().isEmpty()) { QHBoxLayout* left_title_layout = new QHBoxLayout(); left_title_layout->setSpacing(0); left_title_layout->setContentsMargins(0, 0, 0, 0); // Zero because we're already inside a layout. if(!_action->array()->checkBoxTitle().isEmpty()) { MenuTitleLabel* cb_lbl = new MenuTitleLabel(_action->array()->checkBoxTitle(), parent); cb_lbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); cb_lbl->setAlignment(Qt::AlignCenter); left_title_layout->addWidget(cb_lbl); left_title_layout->addSpacing(4); } if(!_action->array()->headerTitle().isEmpty()) { MenuTitleLabel* hdr_lbl = new MenuTitleLabel(_action->array()->headerTitle(), parent); hdr_lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); hdr_lbl->setAlignment(Qt::AlignCenter); left_title_layout->addWidget(hdr_lbl); left_title_layout->addSpacing(4); } left_v_layout->addLayout(left_title_layout); } left_v_layout->addStretch(); _menuItemControlWidget = new MenuItemControlWidget(_action, parent); _menuItemControlWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); left_v_layout->addWidget(_menuItemControlWidget); if(_action->array()->arrayTitle().isEmpty()) { right_v_layout->addStretch(); } else { MenuTitleLabel* lbl = new MenuTitleLabel(_action->array()->arrayTitle(), parent); lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); lbl->setAlignment(Qt::AlignCenter); right_v_layout->addWidget(lbl); } QHBoxLayout* sw_h_layout = new QHBoxLayout(); sw_h_layout->setSpacing(0); sw_h_layout->setContentsMargins(0, 0, 0, 0); sw_h_layout->addStretch(); _switchWidget = new SwitchBarActionWidget(_action, parent); _switchWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); sw_h_layout->addWidget(_switchWidget); right_v_layout->addLayout(sw_h_layout); h_layout->addLayout(left_v_layout); h_layout->addLayout(right_v_layout); } RoutePopupHit RoutingMatrixActionWidget::hitTest(const QPoint& p, RoutePopupHit::HitTestType test_type) { if(_action->isEnabled()) { // The point is relative to this widget. // Check if we hit the left hand portion (the checkbox and text area). if(_menuItemControlWidget->geometry().contains(p)) { switch(test_type) { case RoutePopupHit::HitTestClick: case RoutePopupHit::HitTestHover: if(_action->hasCheckBox()) return RoutePopupHit(_action, RoutePopupHit::HitMenuItem); break; } return RoutePopupHit(_action, RoutePopupHit::HitSpace); } // Check if we hit one of the channel bar channels. const QPoint cb_p(p.x() - _switchWidget->x(), p.y() - _switchWidget->y()); const int cols = _action->array()->columns(); for(int col = 0; col < cols; ++col) { const QRect rect = _action->array()->rect(col); if(rect.contains(cb_p)) return RoutePopupHit(_action, RoutePopupHit::HitChannel, col); } // Check if we hit the channel bar itself. if(_switchWidget->geometry().contains(p)) return RoutePopupHit(_action, RoutePopupHit::HitChannelBar); // Check if we hit this widget. if(rect().contains(p)) return RoutePopupHit(_action, RoutePopupHit::HitSpace); } return RoutePopupHit(_action, RoutePopupHit::HitNone); } void RoutingMatrixActionWidget::actionEvent(QActionEvent* e) { DEBUG_PRST_ROUTES(stderr, "RoutingMatrixActionWidget::actionEvent\n"); if(e->type() == QEvent::ActionChanged && e->action() == _action) { _menuItemControlWidget->updateGeometry(); if(layout()) { DEBUG_PRST_ROUTES(stderr, " layout...\n"); //layout()->invalidate(); //layout()->update(); layout()->activate(); } } e->ignore(); QWidget::actionEvent(e); } //--------------------------------------------------------- // RoutingMatrixWidgetAction //--------------------------------------------------------- const int RoutingMatrixWidgetAction::margin = 1; const int RoutingMatrixWidgetAction::itemHSpacing = 1; const int RoutingMatrixWidgetAction::itemVSpacing = 3; const int RoutingMatrixWidgetAction::groupSpacing = 4; const int RoutingMatrixWidgetAction::itemsPerGroup = 4; const int RoutingMatrixWidgetAction::actionHMargin = 8; RoutingMatrixWidgetAction::RoutingMatrixWidgetAction(int cols, QPixmap* on_pixmap, QPixmap* off_pixmap, QWidget* parent, const QString& action_text) : QWidgetAction(parent) { _actionText = action_text; _hasCheckBox = false; _checkBoxChecked = false; _menuItemPressed = false; _arrayStayOpen = false; _isSelected = false; _onPixmap = on_pixmap; _offPixmap = off_pixmap; _array.setColumns(cols); _smallFont = font(); _smallFont.setPointSize(_smallFont.pointSize() / 2 + 1); if(_onPixmap) { if(_maxPixmapGeometry.width() < _onPixmap->width()) _maxPixmapGeometry.setWidth(_onPixmap->width()); if(_maxPixmapGeometry.height() < _onPixmap->height()) _maxPixmapGeometry.setHeight(_onPixmap->height()); } if(_offPixmap) { if(_maxPixmapGeometry.width() < _offPixmap->width()) _maxPixmapGeometry.setWidth(_offPixmap->width()); if(_maxPixmapGeometry.height() < _offPixmap->height()) _maxPixmapGeometry.setHeight(_offPixmap->height()); } // No pixmaps? Set the size for manually painting. if(!_onPixmap && !_offPixmap) _maxPixmapGeometry = QSize(10, 10); updateChannelArray(); } void RoutingMatrixWidgetAction::updateChannelArray() { const int cols = _array.columns(); const int cellW = _maxPixmapGeometry.width(); const int cellH = _maxPixmapGeometry.height(); // Determine the maximum horizontal header height. int max_h_hdr_h = 0; if(_array.headerVisible()) { for(int col = 0; col < cols; ++col) { const QString str = _array.headerText(col); const QFontMetrics fm(str.isEmpty() ? smallFont() : font()); const QRect brect = fm.boundingRect(str.isEmpty() ? "8" : str); const int r_h = brect.height(); if(r_h > max_h_hdr_h) max_h_hdr_h = r_h; } // Set the horizontal header rectangles. int x = margin + itemHSpacing; int y = margin; for(int col = 0; col < cols; ++col) { if(col != 0 && ((col % itemsPerGroup) == 0)) x += groupSpacing; const QString str = _array.headerText(col); const QFontMetrics fm(str.isEmpty() ? smallFont() : font()); const QRect brect = fm.boundingRect(str.isEmpty() ? "888" : str); int r_w = brect.width(); if(r_w < cellW) r_w = cellW; const int cell_w = r_w + itemHSpacing; const QRect r(x, y, cell_w, max_h_hdr_h); _array.headerSetRect(col, r); x += cell_w; } } // Set the array rectangles. int y = margin + max_h_hdr_h + itemVSpacing; int x = margin + itemHSpacing; const QFontMetrics a_fm(font()); for(int col = 0; col < cols; ++col) { if(col != 0 && ((col % itemsPerGroup) == 0)) x += groupSpacing; const int hdr_w = _array.headerRect(col).width(); int txt_w = 0; int txt_h = 0; if(!_array.text(col).isEmpty()) { const QRect b_rect = a_fm.boundingRect(_array.text(col)); txt_w = b_rect.width() + margin + groupSpacing; // Add a wee bit of space. txt_h = b_rect.height(); } int cell_w = txt_w + cellW; if(hdr_w > cell_w) cell_w = hdr_w; const int cell_h = txt_h > cellH ? txt_h : cellH; const QRect r(x, y, cell_w, cell_h); _array.setRect(col, r); x += cell_w; } } void RoutingMatrixWidgetAction::updateCreatedWidgets() { const int sz = createdWidgets().size(); for(int i = 0; i < sz; ++i) createdWidgets().at(i)->update(); } QWidget* RoutingMatrixWidgetAction::createWidget(QWidget *parent) { RoutingMatrixActionWidget* lw = new RoutingMatrixActionWidget(this, parent); return lw; } void RoutingMatrixWidgetAction::sendActionChanged() { QActionEvent e(QEvent::ActionChanged, this); // This code copied and modified from QActionPrivate::sendDataChanged() source. // Using QAction::setText() calls QActionPrivate::sendDataChanged() which doesn't // send to the created widgets so fails to resize properly on text changes. // Update the created widgets first. for (int i = 0; i < createdWidgets().size(); ++i) { QWidget *w = createdWidgets().at(i); //DEBUG_PRST_ROUTES(stderr, "RoutingMatrixWidgetAction::sendActionChanged created widget:%s\n", w->metaObject()->className()); qApp->sendEvent(w, &e); } // Now update the associated widgets and graphics widgets (popup menus, widgets etc. containing this action)... #ifndef QT_NO_GRAPHICSVIEW for (int i = 0; i < associatedGraphicsWidgets().size(); ++i) { //DEBUG_PRST_ROUTES(stderr, "RoutingMatrixWidgetAction::sendActionChanged associated graphics widget\n"); QGraphicsWidget *w = associatedGraphicsWidgets().at(i); qApp->sendEvent(w, &e); } #endif for (int i = 0; i < associatedWidgets().size(); ++i) { QWidget *w = associatedWidgets().at(i); //DEBUG_PRST_ROUTES(stderr, "RoutingMatrixWidgetAction::sendActionChanged associated widget:%s \n", w->metaObject()->className()); qApp->sendEvent(w, &e); } emit changed(); } void RoutingMatrixWidgetAction::setActionText(const QString& s) { //DEBUG_PRST_ROUTES(stderr, "RoutingMatrixWidgetAction::setActionText\n"); _actionText = s; sendActionChanged(); } RoutePopupHit RoutingMatrixWidgetAction::hitTest(const QPoint& p, RoutePopupHit::HitTestType test_type) { for(int i = 0; i < createdWidgets().size(); ++i) { QWidget* w = createdWidgets().at(i); if(RoutingMatrixActionWidget* maw = qobject_cast< RoutingMatrixActionWidget* >(w)) { // The point is relative to the menu. Translate the point to reference our created container widget. RoutePopupHit hit = maw->hitTest(QPoint(p.x() - maw->x(), p.y() - maw->y()), test_type); if(hit._type != RoutePopupHit::HitNone) return hit; } } return RoutePopupHit(this, RoutePopupHit::HitNone); } RoutePopupHit RoutingMatrixWidgetAction::previousHit(const RoutePopupHit& fromHit) { RoutePopupHit retHit = fromHit; retHit._action = this; const int cols = array()->columns(); switch(fromHit._type) { case RoutePopupHit::HitChannel: { if(cols == 0) { if(hasCheckBox()) { retHit._type = RoutePopupHit::HitMenuItem; retHit._value = 0; } } else { int col = fromHit._value; // The column. if(col > cols) // Greater than. Let the decrement work. col = cols; --col; if(col == -1) { if(hasCheckBox()) { retHit._type = RoutePopupHit::HitMenuItem; retHit._value = 0; } else col = cols - 1; // Wrap around. } if(col != -1) retHit._value = col; // Adjust the current 'last' column setting. } } break; case RoutePopupHit::HitMenuItem: { if(cols != 0) { const int col = cols - 1; // Wrap around. retHit._type = RoutePopupHit::HitChannel; retHit._value = col; } } break; case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: case RoutePopupHit::HitNone: // If it has a checkbox (or there is no channel bar) select the checkbox/text area. if(hasCheckBox() || array()->columns() == 0) { retHit._type = RoutePopupHit::HitMenuItem; retHit._value = 0; } // Otherwise select the first available channel bar column. else { retHit._type = RoutePopupHit::HitChannel; retHit._value = 0; } break; } return retHit; } RoutePopupHit RoutingMatrixWidgetAction::nextHit(const RoutePopupHit& fromHit) { RoutePopupHit retHit = fromHit; const int cols = array()->columns(); switch(fromHit._type) { case RoutePopupHit::HitChannel: { if(cols == 0) { if(hasCheckBox()) { retHit._type = RoutePopupHit::HitMenuItem; retHit._action = this; retHit._value = 0; } } else { int col = fromHit._value; // The column. ++col; if(col >= cols) { if(hasCheckBox()) { retHit._type = RoutePopupHit::HitMenuItem; retHit._action = this; retHit._value = 0; col = -1; } else col = 0; // Wrap around. } if(col != -1) retHit._value = col; // Adjust the current 'last' column setting. } } break; case RoutePopupHit::HitMenuItem: { if(cols != 0) { const int col = 0; // Select the first available channel. retHit._type = RoutePopupHit::HitChannel; retHit._action = this; retHit._value = col; } } break; case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: case RoutePopupHit::HitNone: // If it has a checkbox (or there is no channel bar) select the checkbox/text area. if(hasCheckBox() || array()->columns() == 0) { retHit._type = RoutePopupHit::HitMenuItem; retHit._action = this; retHit._value = 0; } // Otherwise select the first available channel bar column. else { retHit._type = RoutePopupHit::HitChannel; retHit._action = this; retHit._value = 0; } break; } return retHit; } //--------------------------------------------------------- // RoutingMatrixHeaderWidgetAction //--------------------------------------------------------- RoutingMatrixHeaderWidgetAction::RoutingMatrixHeaderWidgetAction(const QString& checkbox_label, const QString& item_label, const QString& array_label, QWidget* parent) : QWidgetAction(parent), _checkBoxLabel(checkbox_label), _itemLabel(item_label), _arrayLabel(array_label) { setEnabled(false); } QWidget* RoutingMatrixHeaderWidgetAction::createWidget(QWidget *parent) { QWidget* lw = new QWidget(parent); lw->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); QHBoxLayout* h_layout = new QHBoxLayout(lw); h_layout->setSpacing(0); h_layout->setContentsMargins(0, 0, 0, 0); if(!_checkBoxLabel.isEmpty()) { MenuTitleLabel* lbl = new MenuTitleLabel(_checkBoxLabel, parent); lbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); lbl->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); h_layout->addWidget(lbl); } if(!_itemLabel.isEmpty()) { MenuTitleLabel* lbl = new MenuTitleLabel(_itemLabel, parent); lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); lbl->setAlignment(Qt::AlignCenter); h_layout->addSpacing(4); h_layout->addWidget(lbl); } if(!_arrayLabel.isEmpty()) { MenuTitleLabel* lbl = new MenuTitleLabel(_arrayLabel, parent); lbl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); lbl->setAlignment(Qt::AlignRight | Qt::AlignVCenter); h_layout->addSpacing(4); h_layout->addWidget(lbl); } return lw; } } // namespace MusEGui muse-4.0.0/src/muse/components/custom_widget_actions.h000066400000000000000000000416051404105311000231520ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // custom_widget_actions.h // (C) Copyright 2011-2015 Tim E. Real (terminator356 on users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CUSTOM_WIDGET_ACTIONS_H__ #define __CUSTOM_WIDGET_ACTIONS_H__ #include #include #include #include #include #include "route.h" class QMouseEvent; class QPainter; class QPaintEvent; class QPixmap; class QString; class QResizeEvent; class QMouseEvent; class QEnterEvent; class QLeaveEvent; class QSize; namespace MusEGui { //class PixmapButton; //--------------------------------------------------------- // PixmapButtonsWidgetAction //--------------------------------------------------------- //class PixmapButtonsWidgetAction : public QWidgetAction { // Q_OBJECT // private: // QString _text; // QBitArray _current; // QPixmap* _onPixmap; // QPixmap* _offPixmap; // QList _chan_buttons; // private slots: // void chanClickMap(int); // public: // PixmapButtonsWidgetAction(const QString& text, // QPixmap* on_pixmap, QPixmap* off_pixmap, // const QBitArray& initial, // QWidget* parent = 0); // QWidget* createWidget(QWidget* parent); // QBitArray currentState() const { return _current; } // void setCurrentState(const QBitArray& state); // }; //--------------------------------------------------------- // RoutingMatrixWidgetAction //--------------------------------------------------------- struct RouteChannelArrayItem { // The switch's value. bool _value; // Accompanying text beside the switch. QString _text; // Rectangle of the swich portion. QRect _rect; RouteChannelArrayItem() { _value = false; } }; struct RouteChannelArrayHeaderItem { // The header item text. QString _text; // Rectangle of the header item text. QRect _rect; }; class RouteChannelArray { private: // The number of columns. int _cols; // Whether the columns are exclusive. bool _colsExclusive; // Whether toggling a column is allowed even when columns are exclusive. bool _exclusiveToggle; // Whether the header items above the array of switches are visible. bool _headerVisible; // Current column the mouse is over. -1 == none. int _activeCol; // Current column being pressed. -1 == none. int _pressedCol; // The array of switches. RouteChannelArrayItem* _array; // Header items above the array of switches. RouteChannelArrayHeaderItem* _header; // Title header item above the checkbox portion. RouteChannelArrayHeaderItem _checkBoxTitleItem; // Title header item above the item text portion. RouteChannelArrayHeaderItem _headerTitleItem; // Title header item above the switch array portion. RouteChannelArrayHeaderItem _arrayTitleItem; void init(); public: RouteChannelArray(int cols = 0); virtual ~RouteChannelArray(); RouteChannelArray& operator=(const RouteChannelArray&); // Returns the number of columns in the array. int columns() const { return _cols; } // Sets the number of columns in the array. void setColumns(int cols); // Whether the given column number is out of range. bool invalidColumn(int col) const { return col < 0 || col >= _cols; } // Whether the columns are exclusive. bool columnsExclusive() const { return _colsExclusive; } // Sets whether the columns are exclusive. void setColumnsExclusive(bool v) { _colsExclusive = v; } // Whether toggling a column is allowed even when columns are exclusive. bool exclusiveToggle() const { return _exclusiveToggle; } // Sets whether toggling a column is allowed even when columns are exclusive. void setExclusiveToggle(bool v) { _exclusiveToggle = v; } // Current column mouse is over. -1 == none. int activeColumn() const { return _activeCol; } // Set current column mouse is over. -1 == none. void setActiveColumn(int col) { if((col == -1 || !invalidColumn(col)) && _activeCol != col) _activeCol = col; } // Current column being pressed. -1 == none. int pressedColumn() const { return _pressedCol; } // Set current column being pressed. -1 == none. // Returns true if any channel was changed (for redrawing). bool setPressedColumn(int col) { if((col != -1 && invalidColumn(col)) || _pressedCol == col) return false; _pressedCol = col; return true; } QString checkBoxTitle() const { return _checkBoxTitleItem._text; } void setCheckBoxTitle(const QString& str) { _checkBoxTitleItem._text = str; } QRect checkBoxTitleRect() const { return _checkBoxTitleItem._rect; } void setCheckBoxTitleRect(const QRect& r) { _checkBoxTitleItem._rect = r; } void setValues(int col, bool value, bool exclusive_cols = false, bool exclusive_toggle = false); bool value(int col) const { if(invalidColumn(col)) return false; return _array[col]._value; } void setValue(int col, bool value) { setValues(col, value, _colsExclusive, _exclusiveToggle); } QRect rect(int col) const { if(invalidColumn(col)) return QRect(); return _array[col]._rect; } void setRect(int col, const QRect& r) { if(invalidColumn(col)) return; _array[col]._rect = r; } QString text(int col) const { if(invalidColumn(col)) return QString(); return _array[col]._text; } void setText(int col, const QString& s) { if(invalidColumn(col)) return; _array[col]._text = s; } QString arrayTitle() const { return _arrayTitleItem._text; } void setArrayTitle(const QString& str) { _arrayTitleItem._text = str; } QRect arrayTitleRect() const { return _arrayTitleItem._rect; } void setArrayTitleRect(const QRect& r) { _arrayTitleItem._rect = r; } bool headerVisible() const { return _headerVisible; } void headerSetVisible(bool v) { _headerVisible = v; } QRect headerRect(int col) const { if(invalidColumn(col)) return QRect(); return _header[col]._rect; } void headerSetRect(int col, const QRect& rect) { if(invalidColumn(col)) return; _header[col]._rect = rect; } QString headerText(int col) const { if(invalidColumn(col)) return QString(); return _header[col]._text; } void headerSetText(int col, const QString& str) { if(invalidColumn(col)) return; _header[col]._text = str; } QString headerTitle() const { return _headerTitleItem._text; } void headerSetTitle(const QString& str) { _headerTitleItem._text = str; } QRect headerTitleRect() const { return _headerTitleItem._rect; } void headerSetTitleRect(const QRect& r) { _headerTitleItem._rect = r; } }; //--------------------------------------------------------- // RoutePopupHit // Structure for action hit tests //--------------------------------------------------------- struct RoutePopupHit { enum HitTestType { HitTestHover, HitTestClick }; enum HitType { HitNone, HitSpace, HitMenuItem, HitChannelBar, HitChannel }; HitType _type; QAction* _action; // Action where the hit occurred. int _value; // Channel number for HitChannel. RoutePopupHit() { _action = 0; _type = HitNone; _value = 0; } RoutePopupHit(QAction* action, HitType ht, int val = 0) { _action = action; _type = ht; _value = val; } bool operator==(const RoutePopupHit& hit) { return _action == hit._action && _type == hit._type && _value == hit._value; } }; class RoutingMatrixActionWidget; class RoutingMatrixWidgetAction; //--------------------------------------------------------- // MenuItemControlWidget // The checkable menu item-like portion of the custom widget action, with text portion as well //--------------------------------------------------------- class MenuItemControlWidget : public QWidget { Q_OBJECT Q_PROPERTY(QColor highColor READ highColor WRITE setHighColor) private: RoutingMatrixWidgetAction* _action; QColor m_highColor; protected: QSize sizeHint() const; void paintEvent(QPaintEvent*); public: MenuItemControlWidget(RoutingMatrixWidgetAction* action, QWidget* parent = 0); // Returns, in the passed rectangle pointers, the rectangles of the checkbox, and/or item text label. void elementRect(QRect* checkbox_rect = 0, QRect* label_rect = 0) const; QColor highColor() const { return m_highColor; } void setHighColor(const QColor c) { m_highColor = c; } }; //--------------------------------------------------------- // SwitchBarActionWidget // The switch bar portion of the custom widget action //--------------------------------------------------------- class SwitchBarActionWidget : public QWidget { Q_OBJECT Q_PROPERTY(QColor highColor READ highColor WRITE setHighColor) Q_PROPERTY(QColor checkedColor READ checkedColor WRITE setCheckedColor) Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor) private: RoutingMatrixWidgetAction* _action; QColor m_highColor; QColor m_checkedColor; QColor m_borderColor; protected: QSize sizeHint() const; void paintEvent(QPaintEvent*); public: SwitchBarActionWidget(RoutingMatrixWidgetAction* action, QWidget* parent = 0); QColor highColor() const { return m_highColor; } void setHighColor(const QColor c) { m_highColor = c; } QColor checkedColor() const { return m_checkedColor; } void setCheckedColor(const QColor c) { m_checkedColor = c; } QColor borderColor() const { return m_borderColor; } void setBorderColor(const QColor c) { m_borderColor = c; } }; //--------------------------------------------------------- // RoutingMatrixActionWidget // Container widget holds checkable menu item-like widget with text portion, // and switch bar widget, for the custom widget action //--------------------------------------------------------- class RoutingMatrixActionWidget : public QWidget { Q_OBJECT private: RoutingMatrixWidgetAction* _action; // The part containing a checkbox, and the item text, together. MenuItemControlWidget* _menuItemControlWidget; // The array of switches. SwitchBarActionWidget* _switchWidget; protected: // For resizing properly on text changes. void actionEvent(QActionEvent*); public: RoutingMatrixActionWidget(RoutingMatrixWidgetAction* action, QWidget* parent = 0); // Performs a hit test on the contents of the widget at the given point, according to the requested hit test type. RoutePopupHit hitTest(const QPoint&, RoutePopupHit::HitTestType); }; //--------------------------------------------------------- // RoutingMatrixWidgetAction // The custom widget action //--------------------------------------------------------- class RoutingMatrixWidgetAction : public QWidgetAction { Q_OBJECT private: // The switch (channel) array. RouteChannelArray _array; // Pixmap used for 'on' indicator. QPixmap* _onPixmap; // Pixmap used for 'off' indicator. QPixmap* _offPixmap; // A smaller font (about half size) than the action's font. QFont _smallFont; // Maximum dimensions of the on/off pixmaps. QSize _maxPixmapGeometry; // NOTE: _hasCheckBox is used instead of QAction::isCheckable()/setCheckable(). bool _hasCheckBox; // Whether the checkbox is currently checked or not. bool _checkBoxChecked; // Whether the label and checkbox area is currently pressed or not. bool _menuItemPressed; // Whether clicking the array closes the menu or not. bool _arrayStayOpen; // Whether the action is highlighted (hovered) or not. bool _isSelected; // NOTE: _actionText is used instead of QAction::text()/setText(). QString _actionText; protected: // Override QWidget* createWidget(QWidget* parent); // Sends ActionChanged events to created and associated widgets. Emits changed(). // For resizing properly on text changes. void sendActionChanged(); public: static const int margin; static const int itemHSpacing; static const int itemVSpacing; static const int groupSpacing; static const int itemsPerGroup; static const int actionHMargin; // Empty area left and right of checkbox and text label. RoutingMatrixWidgetAction(int cols, QPixmap* on_pixmap, QPixmap* off_pixmap, QWidget* parent = 0, const QString& action_text = QString()); // Access to the switch (channel) array. RouteChannelArray* array() { return &_array; } // Updates the structure and/or cached rectangles of the channel array. // When array header text is changed this should also be called. void updateChannelArray(); // Updates (redraws) created widgets. void updateCreatedWidgets(); // A smaller font (about half size) than the action's font. QFont smallFont() const { return _smallFont; } // Pixmap used for 'on' indicator. QPixmap* onPixmap() const { return _onPixmap; } // Pixmap used for 'off' indicator. QPixmap* offPixmap() const { return _offPixmap; } // Maximum dimensions of the on/off pixmaps. QSize maxPixmapGeometry() const { return _maxPixmapGeometry; } // NOTE: Use hasCheckBox() instead of QAction::isCheckable(). bool hasCheckBox() const { return _hasCheckBox; } // NOTE: Use setHasCheckBox() instead of QAction::setCheckable(). void setHasCheckBox(bool v) { _hasCheckBox = v; } // Whether the checkbox is currently checked or not. bool checkBoxChecked() const { return _checkBoxChecked; } // Sets whether the checkbox is currently checked or not. void setCheckBoxChecked(bool v) { _checkBoxChecked = v; } // Whether the label and checkbox area is currently pressed or not. bool menuItemPressed() const { return _menuItemPressed; } // Sets whether the label and checkbox area is currently pressed or not. // Returns true if the menu item section was changed (for redrawing). bool setMenuItemPressed(bool v) { if(_menuItemPressed == v) return false; _menuItemPressed = v; return true; } // Whether clicking the array closes the menu or not. bool arrayStayOpen() const { return _arrayStayOpen; } // Sets whether clicking the array closes the menu or not. void setArrayStayOpen(bool v) { _arrayStayOpen = v; } // Whether the action is highlighted (hovered) or not. bool isSelected() const { return _isSelected; } // Sets whether the action is highlighted (hovered) or not. void setSelected(bool v) { _isSelected = v; } // NOTE: Use setActionText() instead of QAction::setText(). void setActionText(const QString& s); // NOTE: Use actionText() instead of QAction::text(). QString actionText() const { return _actionText; } //QString actionText() const { return text(); } // Does a hit test of type HitTestType, returning a RoutePopupHit structure describing what was hit. RoutePopupHit hitTest(const QPoint&, RoutePopupHit::HitTestType); // Returns the previous hittable item (ie. for left key movement). RoutePopupHit previousHit(const RoutePopupHit& fromHit); // Returns the next hittable item (ie. for right key movement). RoutePopupHit nextHit(const RoutePopupHit& fromHit); }; //--------------------------------------------------------- // RoutingMatrixHeaderWidgetAction // A header action suitable for non-RoutingMatrixWidgetAction items. // NOTE: This is a separate action which can be used to head regular QAction // items instead of the RoutingMatrixWidgetAction's own array headers. //--------------------------------------------------------- class RoutingMatrixHeaderWidgetAction : public QWidgetAction { Q_OBJECT private: QString _checkBoxLabel; QString _itemLabel; QString _arrayLabel; protected: QWidget* createWidget(QWidget* parent); public: RoutingMatrixHeaderWidgetAction(const QString& checkbox_label, const QString& item_label, const QString& array_label, QWidget* parent = 0); }; } // namespace MusEGui #endif // __CUSTOM_WIDGET_ACTIONS_H__ muse-4.0.0/src/muse/components/dentry.cpp000066400000000000000000000201441404105311000204100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dentry.cpp,v 1.1.1.1.2.3 2008/08/18 00:15:26 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include "sliderbase.h" #include "dentry.h" #include "globals.h" #include "gconfig.h" #define TIMER1 400 #define TIMER2 200 #define TIMEC 7 #define TIMER3 100 #define TIMEC2 20 #define TIMER4 50 namespace MusEGui { //--------------------------------------------------------- // Dentry // lineedit double values //--------------------------------------------------------- Dentry::Dentry(QWidget* parent, const char* name) : QLineEdit(parent) { setObjectName(name); _slider = 0; _id = -1; //setAutoFillBackground(true); timer = new QTimer(this); connect(timer, SIGNAL(timeout()), SLOT(repeat())); val = 0.01; connect(this, SIGNAL(returnPressed()), SLOT(endEdit())); setCursor(QCursor(Qt::ArrowCursor)); evx = 1; } //--------------------------------------------------------- // contextMenuEvent //--------------------------------------------------------- void Dentry::contextMenuEvent(QContextMenuEvent * e) { e->accept(); } //--------------------------------------------------------- // endEdit //--------------------------------------------------------- void Dentry::endEdit() { if (isModified()) { if (setSValue(text())) { setString(val); return; } } setString(val); } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void Dentry::mousePressEvent(QMouseEvent* event) { const Qt::MouseButton m_button = event->button(); const Qt::MouseButtons m_buttons = event->buttons(); event->accept(); // Only one mouse button at a time! Otherwise bad things happen. if(m_buttons ^ m_button) { button = Qt::NoButton; timer->stop(); return; } if(m_button == Qt::LeftButton) QLineEdit::mousePressEvent(event); button = m_button; starty = event->y(); evx = event->x(); timecount = 0; repeat(); timer->start(TIMER1); } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void Dentry::wheelEvent(QWheelEvent* event) { event->accept(); const QPoint pixelDelta = event->pixelDelta(); const QPoint angleDegrees = event->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; if (delta < 0) { if(_slider) _slider->stepPages(-1); else decValue(1); } else if (delta > 0) { if(_slider) _slider->stepPages(1); else incValue(1); } } //--------------------------------------------------------- // repeat //--------------------------------------------------------- void Dentry::repeat() { if (timecount == 1) { ++timecount; timer->stop(); timer->start(TIMER2); return; } ++timecount; if (timecount == TIMEC) { timer->stop(); timer->start(TIMER3); } if (timecount == TIMEC2) { timer->stop(); timer->start(TIMER4); } switch (button) { case Qt::LeftButton: if (!MusEGlobal::config.leftMouseButtonCanDecrease) return; // else fall through case Qt::MidButton: if(_slider) _slider->stepPages(-1); else decValue(evx); break; case Qt::RightButton: if(_slider) _slider->stepPages(1); else incValue(evx); break; default: break; } } //--------------------------------------------------------- // mouseReleaseEvent //--------------------------------------------------------- void Dentry::mouseReleaseEvent(QMouseEvent* ev) { ev->accept(); // Don't call ancestor to avoid middle button pasting. //LineEdit::mouseReleaseEvent(ev); button = Qt::NoButton; timer->stop(); } //--------------------------------------------------------- // mouseDoubleClickEvent //--------------------------------------------------------- void Dentry::mouseDoubleClickEvent(QMouseEvent* event) { event->accept(); if (event->button() != Qt::LeftButton) { //mousePressEvent(event); button = event->button(); starty = event->y(); evx = event->x(); timecount = 0; repeat(); timer->start(TIMER1); return; } update(); emit doubleClicked(_id); if(event->modifiers() & Qt::ControlModifier) emit ctrlDoubleClicked(_id); else QLineEdit::mouseDoubleClickEvent(event); } void Dentry::keyPressEvent(QKeyEvent* e) { bool inc = true; switch (e->key()) { case Qt::Key_Escape: if(isModified()) { // Restore the displayed current value. blockSignals(true); setString(val); blockSignals(false); } // Let ancestor have it, such as for yielding focus. e->ignore(); return; break; case Qt::Key_Up: inc = true; break; case Qt::Key_Down: inc = false; break; default: // Let ancestor handle it. e->ignore(); QLineEdit::keyPressEvent(e); return; break; } if(e->modifiers() & (Qt::AltModifier | Qt::MetaModifier | Qt::ControlModifier)) { // Let ancestor handle it. e->ignore(); QLineEdit::keyPressEvent(e); return; } e->accept(); // Do not allow setting value from the external while mouse is pressed. //if(_pressed) // return; const bool shift = e->modifiers() == Qt::ShiftModifier; int val = 1; if(shift) val *= 10; if(inc) { if(_slider) _slider->stepPages(val); else incValue(val); } else { if(_slider) _slider->stepPages(-val); else decValue(val); } // Show a handy tooltip value box. //if(d_enableValueToolTips) // showValueToolTip(e->globalPos()); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void Dentry::setValue(double v) { if (v == val) return; setString(v); #if 0 if (setString(v)) { clearFocus(); if (!drawFrame) QLineEdit::setFrame(false); setEnabled(false); } else { setEnabled(true); } #endif val = v; } } // namespace MusEGui muse-4.0.0/src/muse/components/dentry.h000066400000000000000000000053761404105311000200670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dentry.h,v 1.1.1.1.2.3 2008/08/18 00:15:26 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DENTRY_H__ #define __DENTRY_H__ //#include "line_edit.h" #include class QWidget; class QWheelEvent; class QMouseEvent; class QContextMenuEvent; class QKeyEvent; class QString; class QTimer; namespace MusEGui { class SliderBase; //--------------------------------------------------------- // Dentry //--------------------------------------------------------- class Dentry : public QLineEdit { Q_OBJECT Q_PROPERTY( int id READ id WRITE setId ) Q_PROPERTY( double value READ value WRITE setValue ) SliderBase* _slider; int button; int starty; QTimer* timer; int evx; int timecount; protected: int _id; double val; virtual void wheelEvent(QWheelEvent*); virtual void mousePressEvent(QMouseEvent*); virtual void mouseDoubleClickEvent(QMouseEvent*); virtual void mouseReleaseEvent(QMouseEvent*); virtual void contextMenuEvent(QContextMenuEvent*); virtual void keyPressEvent(QKeyEvent*); virtual void incValue(int steps = 1) = 0; virtual void decValue(int steps = 1) = 0; virtual bool setString(double) = 0; virtual bool setSValue(const QString&) = 0; private slots: void repeat(); protected slots: void endEdit(); signals: void valueChanged(double, int); void doubleClicked(int); void ctrlDoubleClicked(int); public slots: virtual void setValue(double); public: Dentry(QWidget*, const char* name=0); double value() const { return val; } int id() const { return _id; } void setId(int i) { _id = i; } SliderBase* slider() const { return _slider; } void setSlider(SliderBase* s) { _slider = s; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/didyouknow.h000066400000000000000000000047121404105311000207470ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // $Id: didyouknow.h,v 1.0.0.0 2010/11/21 01:01:01 ogetbilo Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __DIDYOUKNOW_H__ #define __DIDYOUKNOW_H__ #include "ui_didyouknow.h" class QDialog; namespace MusEGui { //--------------------------------------------------------- // DidYouKnowWidget // Wrapper around Ui::DidYouKnow //--------------------------------------------------------- class DidYouKnowWidget : public QDialog, public Ui::DidYouKnow { Q_OBJECT int currTip; bool showTeaser; public: QStringList tipList; DidYouKnowWidget(QDialog *parent = 0) : QDialog(parent) { setupUi(this); tipText->setBackgroundRole(QPalette::Text); tipText->setForegroundRole(QPalette::Foreground); tipText->setOpenExternalLinks(true); currTip=0; showTeaser=false; connect(nextButton,SIGNAL(clicked()),SLOT(nextTip())); } public slots: void nextTip() { if (currTip > tipList.size()-1){ currTip=0; } if (currTip==5 && !showTeaser) { tipText->setText("Still not started playing?"); showTeaser=true; } else if (currTip==10 && !showTeaser) { tipText->setText("What are you waiting for? Make music! :)"); showTeaser=true; } else { tipText->setText(tipList[currTip]); currTip++; showTeaser=false; } } void show() { nextTip(); QDialog::show(); } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/didyouknow.ui000066400000000000000000000043241404105311000211340ustar00rootroot00000000000000 DidYouKnow 0 0 386 194 Did you know? QFrame::Panel QFrame::Sunken Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Don't show on startup Qt::Horizontal QSizePolicy::Expanding 121 31 Next tip Close closeButton clicked() DidYouKnow accept() 20 20 20 20 muse-4.0.0/src/muse/components/doublelabel.cpp000066400000000000000000000134331404105311000213600ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: doublelabel.cpp,v 1.1.1.1.2.2 2008/08/18 00:15:26 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include "doublelabel.h" namespace MusEGui { //--------------------------------------------------------- // DoubleLabel //--------------------------------------------------------- DoubleLabel::DoubleLabel(QWidget* parent, const char* name) : Dentry(parent, name), _specialText("---") { min = 0.0; max = 1.0; _off = -1.0; _precision = 3; setValue(0.0); } DoubleLabel::DoubleLabel(double _val, double m, double mx, QWidget* parent) : Dentry(parent), _specialText("---") { min = m; max = mx; _off = m - 1.0; _precision = 3; setValue(_val); } //--------------------------------------------------------- // setOff //--------------------------------------------------------- void DoubleLabel::setOff(double v) { _off = v; setString(val); } //--------------------------------------------------------- // calcIncrement() //--------------------------------------------------------- double DoubleLabel::calcIncrement() const { double dif; if(max - min > 0) dif = max - min; else dif = min - max; if(dif <= 10.0) return 0.1; else if(dif <= 100.0) return 1.0; else return 10.0; } //--------------------------------------------------------- // setString //--------------------------------------------------------- bool DoubleLabel::setString(double v) { if(v <= _off || v > max) { setText(_specialText); return true; } else if(v < min) { setText(QString("---")); return true; } else { QString s; s.setNum(v, 'f', _precision); if (!_suffix.isEmpty()) { s += " "; s += _suffix; } setText(s); } return false; } //--------------------------------------------------------- // setSValue //--------------------------------------------------------- bool DoubleLabel::setSValue(const QString& s) { bool ok; QString sTrimmed = s.trimmed(); if (sTrimmed.contains(_suffix)) sTrimmed = sTrimmed.remove(_suffix).trimmed(); double v = sTrimmed.toDouble(&ok); if (ok && (v != val)) { if (v < min) v = min; if (v > max) v = max; setValue(v); emit valueChanged(val, _id); } return false; } //--------------------------------------------------------- // incValue //--------------------------------------------------------- void DoubleLabel::incValue(int steps) { if(val >= max) return; double inc = calcIncrement() * double(steps); if(val + inc >= max) setValue(max); else setValue(val + inc); emit valueChanged(val, _id); } //--------------------------------------------------------- // decValue //--------------------------------------------------------- void DoubleLabel::decValue(int steps) { if(val <= min) return; double inc = calcIncrement() * double(steps); if(val - inc <= min) setValue(min); else setValue(val - inc); emit valueChanged(val, _id); } //--------------------------------------------------------- // setPrecision //--------------------------------------------------------- void DoubleLabel::setPrecision(int v) { _precision = v; updateGeometry(); setString(val); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize DoubleLabel::sizeHint() const { QFontMetrics fm = fontMetrics(); int h = fm.height() + 9; int n = _precision; ++n; // For some reason I have to add one digit. Shouldn't have to. double aval = fmax(fabs(max), fabs(min)); if (aval >= 10.0) ++n; if (aval >= 100.0) ++n; if (aval >= 1000.0) ++n; if (aval >= 10000.0) ++n; if (aval >= 100000.0) ++n; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = fm.horizontalAdvance(QString("-0.")) + fm.horizontalAdvance('0') * n + 6; #else int w = fm.width(QString("-0.")) + fm.width('0') * n + 6; #endif if(!_suffix.isEmpty()) { // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 w += fm.horizontalAdvance(QString(" ")) + fm.horizontalAdvance(_suffix); #else w += fm.width(QString(" ")) + fm.width(_suffix); #endif } return QSize(w, h); } QSize DoubleLabel::minimumSizeHint() const { return sizeHint(); } } // namespace MusEGui muse-4.0.0/src/muse/components/doublelabel.h000066400000000000000000000063251404105311000210270ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: doublelabel.h,v 1.2.2.3 2008/08/18 00:15:26 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DOUBLELABEL_H__ #define __DOUBLELABEL_H__ #include #include #include "dentry.h" class QWidget; namespace MusEGui { //--------------------------------------------------------- // DoubleLabel //--------------------------------------------------------- class DoubleLabel : public Dentry { Q_OBJECT Q_PROPERTY( double minValue READ minValue WRITE setMinValue ) Q_PROPERTY( double maxValue READ maxValue WRITE setMaxValue ) Q_PROPERTY( double offValue READ off WRITE setOff ) Q_PROPERTY( QString specialText READ specialText WRITE setSpecialText ) Q_PROPERTY( QString suffix READ suffix WRITE setSuffix ) Q_PROPERTY( int precision READ precision WRITE setPrecision ) double min, max, _off; QString _specialText; // text to show if value outside min,max QString _suffix; int _precision; double calcIncrement() const; virtual bool setSValue(const QString&); virtual bool setString(double val); virtual void incValue(int steps = 1); virtual void decValue(int steps = 1); public: DoubleLabel(QWidget* parent = 0, const char* name = 0); DoubleLabel(double val, double min, double max, QWidget* parent = 0); virtual QSize sizeHint() const; virtual QSize minimumSizeHint () const; double minValue() const { return min; } double maxValue() const { return max; } double off() const { return _off; } void setMinValue(double v) { min = v; updateGeometry(); } void setMaxValue(double v) { max = v; updateGeometry(); } void setRange(double a, double b) { _off = a - (min - _off); min = a; max = b; updateGeometry(); } void setOff(double v); int precision() const { return _precision; } void setPrecision(int val); QString specialText() const { return _specialText; } void setSpecialText(const QString& s) { _specialText = s; updateGeometry(); } QString suffix() const { return _suffix; } void setSuffix(const QString& s) { _suffix = s; updateGeometry(); } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/editctrlbase.ui000066400000000000000000000547601404105311000214160ustar00rootroot00000000000000 EditCtrlBase 0 0 601 413 MusE: Edit Controller Event true Time Position false Available Controller: false Create New Controller true true 0 0 0 238 234 222 255 255 255 246 244 238 119 117 111 158 155 147 0 0 0 255 255 255 0 0 0 255 255 255 104 137 236 0 0 0 0 0 128 255 255 255 0 0 0 0 0 0 0 0 0 238 234 222 255 255 255 255 255 255 119 117 111 158 155 147 0 0 0 255 255 255 0 0 0 255 255 255 104 137 236 0 0 0 0 0 128 255 255 255 0 0 192 128 0 128 128 128 128 238 234 222 255 255 255 255 255 255 119 117 111 158 155 147 128 128 128 255 255 255 128 128 128 255 255 255 104 137 236 0 0 0 0 0 128 255 255 255 0 0 192 128 0 128 0 Qt::Vertical QSizePolicy::Expanding 20 40 Controller false QFrame::Sunken textLabel3 false Note false 127 Value false 127 127 Qt::Horizontal H-Bank false L-Bank false Program false off 0 128 0 off 0 128 0 1 128 Qt::Vertical QSizePolicy::Expanding 20 140 pushButton4 6 0 Qt::Horizontal QSizePolicy::Expanding 20 20 &OK true true &Cancel true QFrame::HLine QFrame::Raised 3 2 Qt::Horizontal QSizePolicy::Expanding 60 20 QFrame::VLine QFrame::Raised 3 2 MusEGui::PosEdit QWidget
posedit.h
valSlider valueChanged(int) valSpinBox setValue(int) 20 20 20 20 valSpinBox valueChanged(int) valSlider setValue(int) 20 20 20 20
muse-4.0.0/src/muse/components/editevent.cpp000066400000000000000000001001171404105311000210710ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: editevent.cpp,v 1.12.2.6 2009/02/02 21:38:00 terminator356 Exp $ // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include "posedit.h" #include "song.h" #include "midictrl.h" #include "editevent.h" #include "globals.h" #include "gconfig.h" #include "midiport.h" #include "midiedit/drummap.h" #include "midi.h" #include "popupmenu.h" #include "choose_sysex.h" // Forwards from header: #include #include #include #include #include #include #include "part.h" #include "minstrument.h" #include "intlabel.h" #include "posedit.h" #include "pitchedit.h" namespace MusEGui { //--------------------------------------------------------- // string2qhex //--------------------------------------------------------- QString string2hex(const unsigned char* data, int len) { QString d; for (int i = 0; i < len; ++i) { if ((i > 0) && ((i % 8)==0)) { d += "\n"; } else if (i) d += " "; // Strip all f0 and f7 (whether accidental or on purpose enclosing etc). if(data[i] == MusECore::ME_SYSEX || data[i] == MusECore::ME_SYSEX_END) continue; d += QString("%1").arg(data[i], 2, 16, QLatin1Char('0')); } return d; } //--------------------------------------------------------- // hex2string //--------------------------------------------------------- char* hex2string(QWidget* parent, const char* src, int& len, bool warn = true) { char buffer[2048]; char* dst = buffer; while (*src) { while (*src == ' ' || *src == '\n') ++src; char* ep; long val = strtol(src, &ep, 16); if (ep == src) { if(warn) QMessageBox::information(parent, QString("MusE"), QWidget::tr("Cannot convert sysex string")); return 0; } src = ep; // Strip all f0 and f7 (whether accidental or on purpose enclosing etc). if(val == MusECore::ME_SYSEX || val == MusECore::ME_SYSEX_END) continue; *dst++ = val; if (dst - buffer >= 2048) { if(warn) QMessageBox::information(parent, QString("MusE"), QWidget::tr("Hex String too long (2048 bytes limit)")); return 0; } } len = dst - buffer; if(len == 0) return 0; char* b = new char[len+1]; memcpy(b, buffer, len); b[len] = 0; return b; } //--------------------------------------------------------- // getEvent //--------------------------------------------------------- MusECore::Event EditNoteDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent) { EditNoteDialog* dlg = new EditNoteDialog(tick, event, parent); MusECore::Event nevent; if (dlg->exec() == QDialog::Accepted) { nevent = dlg->getEvent(); } delete dlg; return nevent; } MusECore::Event EditSysexDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent, MusECore::MidiInstrument* instr) { EditSysexDialog* dlg = new EditSysexDialog(tick, event, parent, instr); MusECore::Event nevent; if (dlg->exec() == QDialog::Accepted) { nevent = dlg->getEvent(); } delete dlg; return nevent; } MusECore::Event EditMetaDialog::getEvent(int tick, const MusECore::Event& event, QWidget* parent) { EditEventDialog* dlg = new EditMetaDialog(tick, event, parent); MusECore::Event nevent; if (dlg->exec() == QDialog::Accepted) { nevent = dlg->getEvent(); } delete dlg; return nevent; } //--------------------------------------------------------- // EditEventDialog //--------------------------------------------------------- EditEventDialog::EditEventDialog(QWidget* parent) : QDialog(parent) { QVBoxLayout* xlayout = new QVBoxLayout; layout1 = new QGridLayout; // ddskrjo this xlayout->addLayout(layout1); //--------------------------------------------------- // Ok, Cancel //--------------------------------------------------- QBoxLayout* w5 = new QHBoxLayout; // ddskrjo this QPushButton* okB = new QPushButton(tr("Ok")); okB->setDefault(true); QPushButton* cancelB = new QPushButton(tr("Cancel")); okB->setFixedWidth(80); cancelB->setFixedWidth(80); w5->addWidget(okB); w5->addSpacing(12); w5->addWidget(cancelB); w5->addStretch(1); xlayout->addLayout(w5); setLayout(xlayout); connect(cancelB, SIGNAL(clicked()), SLOT(reject())); connect(okB, SIGNAL(clicked()), SLOT(accept())); } //--------------------------------------------------------- // EditNoteDialog //--------------------------------------------------------- EditNoteDialog::EditNoteDialog(int tick, const MusECore::Event& event, QWidget* parent) : QDialog(parent) { setupUi(this); if (!event.empty()) { epos->setValue(tick); il1->setValue(event.lenTick()); pl->setValue(event.pitch()); il2->setValue(event.velo()); il3->setValue(event.veloOff()); } else { epos->setValue(tick); il1->setValue(96); pl->setValue(64); il2->setValue(100); il3->setValue(0); } } //--------------------------------------------------------- // EditNoteDialog::event //--------------------------------------------------------- MusECore::Event EditNoteDialog::getEvent() { MusECore::Event event(MusECore::Note); event.setTick(epos->pos().tick()); event.setA(pl->value()); event.setB(il2->value()); event.setC(il3->value()); event.setLenTick(il1->value()); return event; } //--------------------------------------------------------- // EditSysExDialog //--------------------------------------------------------- EditSysexDialog::EditSysexDialog(int tick, const MusECore::Event& event, QWidget* parent, MusECore::MidiInstrument* instr) : QDialog(parent) { setupUi(this); sysex = 0; _instr = instr; if (!event.empty()) { epos->setValue(tick); edit->setText(string2hex(event.data(), event.dataLen())); if(_instr) { typeLabel->setText(MusECore::nameSysex(event.dataLen(), event.data(), _instr)); commentLabel->setText(MusECore::sysexComment(event.dataLen(), event.data(), _instr)); } } else { epos->setValue(tick); } connect(edit, SIGNAL(textChanged()), SLOT(editChanged())); connect(buttonSelect, SIGNAL(clicked(bool)), SLOT(selectSysex())); } //--------------------------------------------------------- // ~EditSysexDialog //--------------------------------------------------------- EditSysexDialog::~EditSysexDialog() { if (sysex) delete sysex; } //--------------------------------------------------------- // EditSysExDialog::event //--------------------------------------------------------- MusECore::Event EditSysexDialog::getEvent() { MusECore::Event event(MusECore::Sysex); event.setTick(epos->pos().tick()); event.setData(sysex, len); return event; } //--------------------------------------------------------- // accept //--------------------------------------------------------- void EditSysexDialog::accept() { QString qsrc = edit->toPlainText(); QByteArray ba = qsrc.toLatin1(); const char* src = ba.constData(); sysex = (unsigned char*)hex2string(this, src, len); if (sysex) QDialog::accept(); } //--------------------------------------------------------- // editChanged //--------------------------------------------------------- void EditSysexDialog::editChanged() { if(!_instr) return; QString qsrc = edit->toPlainText(); QByteArray ba = qsrc.toLatin1(); const char* src = ba.constData(); int l; unsigned char* data = (unsigned char*)hex2string(this, src, l, false); // false = Don't warn with popups if(data && l > 0) { typeLabel->setText(MusECore::nameSysex(l, data, _instr)); commentLabel->setText(MusECore::sysexComment(l, data, _instr)); } else { typeLabel->clear(); commentLabel->clear(); } } //--------------------------------------------------------- // selectSysex //--------------------------------------------------------- void EditSysexDialog::selectSysex() { ChooseSysexDialog* dlg = new ChooseSysexDialog(this, _instr); if(dlg->exec() == QDialog::Accepted) { MusECore::SysEx* s = dlg->sysex(); if(s) { edit->setText(string2hex(s->data, s->dataLen)); typeLabel->setText(s->name); commentLabel->setText(s->comment); } } delete dlg; } //--------------------------------------------------------- // EditMetaDialog //--------------------------------------------------------- EditMetaDialog::EditMetaDialog(int tick, const MusECore::Event& ev, QWidget* parent) : EditEventDialog(parent) { meta = 0; setWindowTitle(tr("MusE: Enter Meta Event")); QLabel* l1 = new QLabel(tr("Time Position")); ///epos = new PosEdit; epos = new PosEdit; QLabel* l2 = new QLabel(tr("Meta Type")); il2 = new MusEGui::IntLabel(-1, 0, 127, this, -1); il2->setFixedWidth(100); il2->setFrame(true); il2->setDark(); typeLabel = new QLabel; typeLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); QHBoxLayout* typeLayout = new QHBoxLayout; typeLayout->addWidget(il2); typeLayout->addWidget(typeLabel); typeLayout->addStretch(); hexButton = new QRadioButton(tr("Enter Hex")); hexButton->setChecked(true); connect(hexButton, SIGNAL(toggled(bool)), SLOT(toggled(bool))); edit = new QTextEdit; edit->setFont(qApp->font()); if (!ev.empty()) { epos->setValue(tick); il2->setValue(ev.dataA()); toggled(true); edit->setText(string2hex(ev.data(), ev.dataLen())); } else { epos->setValue(tick); il2->setValue(0); } typeChanged(il2->value()); connect(il2, SIGNAL(valueChanged(int)), SLOT(typeChanged(int))); layout1->addWidget(l1, 0, 0); layout1->addWidget(epos, 0, 1, Qt::AlignLeft); layout1->addWidget(l2, 1, 0); //layout1->addWidget(il2, 1, 1, AlignLeft); layout1->addLayout(typeLayout, 1, 1); //layout1->addMultiCellWidget(hexButton, 2, 2, 0, 1); //layout1->addMultiCellWidget(edit, 3, 3, 0, 1); layout1->addWidget(hexButton, 2, 0, 1, 2); layout1->addWidget(edit, 3, 0, 1, 2); } //--------------------------------------------------------- // typeChanged //--------------------------------------------------------- void EditMetaDialog::typeChanged(int val) { typeLabel->setText(MusECore::midiMetaName(val)); } //--------------------------------------------------------- // toggled //--------------------------------------------------------- void EditMetaDialog::toggled(bool flag) { QString qsrc = edit->toPlainText(); QByteArray ba = qsrc.toLatin1(); const char* src = ba.constData(); edit->clear(); QString dst; if (flag) { // convert to hex dst = string2hex((unsigned char*)src, ba.length()); } else { // convert to string int len; dst = hex2string(this, src, len); } edit->setText(dst); } //--------------------------------------------------------- // ~EditMetaDialog //--------------------------------------------------------- EditMetaDialog::~EditMetaDialog() { if (meta) delete meta; } //--------------------------------------------------------- // EditMetaDialog::event //--------------------------------------------------------- MusECore::Event EditMetaDialog::getEvent() { MusECore::Event event(MusECore::Meta); event.setTick(epos->pos().tick()); event.setA(il2->value()); event.setData(meta, len); // TODO ?? return event; } //--------------------------------------------------------- // accept //--------------------------------------------------------- void EditMetaDialog::accept() { QString qsrc = edit->toPlainText(); QByteArray ba = qsrc.toLatin1(); const char* src = ba.constData(); if (!hexButton->isChecked()) { meta = (unsigned char*)strdup(src); len = ba.length(); QDialog::accept(); return; } meta = (unsigned char*)hex2string(this, src, len); if (meta) QDialog::accept(); } //--------------------------------------------------------- // getEvent //--------------------------------------------------------- MusECore::Event EditCtrlDialog::getEvent(int tick, const MusECore::Event& event, const MusECore::MidiPart* part, QWidget* parent) { EditCtrlDialog* dlg = new EditCtrlDialog(tick, event, part, parent); MusECore::Event nevent; if (dlg->exec() == QDialog::Accepted) { nevent = dlg->getEvent(); } delete dlg; return nevent; } //--------------------------------------------------------- // EditCtrlDialog::event //--------------------------------------------------------- MusECore::Event EditCtrlDialog::getEvent() { MusECore::Event event(MusECore::Controller); event.setTick(timePos->pos().tick()); int cnum = 0; QListWidgetItem* item = ctrlList->currentItem(); if(item != 0) cnum = item->data(Qt::UserRole).toInt(); MusECore::MidiTrack* track = part->track(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; int channel = track->outChannel(); bool isNewDrum = track->type() == MusECore::Track::DRUM; int evnum = cnum; int num = cnum; if((cnum & 0xff) == 0xff) { evnum = (cnum & ~0xff) | (noteSpinBox->value() & 0x7f); num = evnum; if(isNewDrum) { MusECore::DrumMap* dm = &track->drummap()[noteSpinBox->value() & 0x7f]; num = (cnum & ~0xff) | dm->anote; // Default to track port if -1 and track channel if -1. if(dm->port != -1) port = &MusEGlobal::midiPorts[dm->port]; if(dm->channel != -1) channel = dm->channel; } } MusECore::MidiController* c = port->midiController(cnum, channel); MusECore::MidiCtrlValListList* cll = port->controller(); if(cll->find(channel, num) == cll->end()) { MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(num); cll->add(channel, vl); } event.setA(evnum); if(cnum == MusECore::CTRL_PROGRAM) { int hb = hbank->value(); int lb = lbank->value(); int prog = program->value(); if (hb > 0 && hb < 129) hb -= 1; else hb = 0xff; if (lb > 0 && lb < 129) lb -= 1; else lb = 0xff; if (prog > 0 && prog < 129) prog -= 1; else prog = 0xff; int val = (hb << 16) + (lb << 8) + prog; event.setB(val); } else event.setB(valSlider->value() + c->bias()); return event; } //--------------------------------------------------------- // EditCtrlDialog // PosEdit* timePos; // QSlider* valSlider; // QSpinBox* valSpinBox; // QLabel* controllerName; // QListWidget* ctrlList; // QPushButton* buttonNewController; //--------------------------------------------------------- struct CI { int num; QString s; bool used; bool off; bool instrument; CI(int n, const QString& ss, bool u, bool o, bool i) : num(n), s(ss), used(u), off(o), instrument(i) {} }; EditCtrlDialog::EditCtrlDialog(int tick, const MusECore::Event& event, const MusECore::MidiPart* p, QWidget* parent) : QDialog(parent), part(p) { setupUi(this); widgetStack->setAutoFillBackground(true); MusECore::MidiTrack* track = part->track(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; const bool isNewDrum = track->type() == MusECore::Track::DRUM; const bool isMidi = track->type() == MusECore::Track::MIDI; MusECore::MidiCtrlValListList* cll = port->controller(); const int channel = track->outChannel(); const MusECore::MidiInstrument* instr = port->instrument(); int val = 0; int ev_num = 0; int num = 0; int ev_cnum = 0; int ev_note = -1; if (!event.empty()) { ev_num = event.dataA(); num = ev_num; ev_cnum = ev_num; val = event.dataB(); if(port->drumController(ev_num)) { ev_cnum |= 0xff; if(isNewDrum) num = (ev_num & ~0xff) | track->drummap()[ev_num & 0xff].anote; ev_note = ev_num & 0xff; } } MusECore::MidiController* mc = port->midiController(ev_num, channel); ctrlList->clear(); ctrlList->setSelectionMode(QAbstractItemView::SingleSelection); //--------------------------------------------------- // build list of midi controllers for current // MusECore::MidiPort/channel //--------------------------------------------------- std::list sList; typedef std::list::iterator isList; std::set already_added_nums; for (MusECore::iMidiCtrlValList it = cll->begin(); it != cll->end(); ++it) { MusECore::MidiCtrlValList* cl = it->second; const int ch = it->first >> 24; if(ch != channel) continue; MusECore::MidiController* c = port->midiController(cl->num(), ch); const bool isDrumCtrl = (c->isPerNoteController()); const int show = c->showInTracks(); const int cnum = c->num(); const int clnum = cl->num(); isList i = sList.begin(); for (; i != sList.end(); ++i) { if (i->num == cnum) break; } if (i == sList.end()) { bool used = (clnum == num); bool off = cl->hwVal() == MusECore::CTRL_VAL_UNKNOWN; // Does it have a value or is it 'off'? // Filter if not used and off. But if there's something there, we must show it. //if(!used && off && if(!used && //off && (((isDrumCtrl || isNewDrum) && !(show & MusECore::MidiController::ShowInDrum)) || (isMidi && !(show & MusECore::MidiController::ShowInMidi)))) continue; const int patch = port->hwCtrlState(ch, MusECore::CTRL_PROGRAM); const bool isinstr = instr->findController(cnum, ch, patch) != nullptr; // Need to distinguish between global default controllers and // instrument defined controllers. Instrument takes priority over global // ie they 'overtake' definition of a global controller such that the // global def is no longer available. sList.push_back(CI(cnum, isinstr ? MusECore::midiCtrlNumString(cnum, true) + c->name() : MusECore::midiCtrlName(cnum, true), used, off, isinstr)); already_added_nums.insert(num); } } // Add instrument-defined controllers: QListWidgetItem* sel_item = 0; for (isList i = sList.begin(); i != sList.end(); ++i) { // Filter if not used and off. But if there's something there, we must show it. if(!i->instrument && !i->used && i->off) continue; QListWidgetItem* item = new QListWidgetItem(i->s, ctrlList); item->setData(Qt::UserRole, i->num); if(i->num == ev_cnum) sel_item = item; } if(sel_item) ctrlList->setCurrentItem(sel_item); valSlider->setRange(mc->minVal(), mc->maxVal()); valSpinBox->setRange(mc->minVal(), mc->maxVal()); controllerName->setText(mc->name()); if(!event.empty()) { if(ev_num == MusECore::CTRL_PROGRAM) { widgetStack->setCurrentIndex(1); updatePatch(val); } else { widgetStack->setCurrentIndex(0); valSlider->setValue(val - mc->bias()); if(mc->isPerNoteController()) { noteSpinBox->setVisible(true); noteSpinBox->setEnabled(true); noteLabel->setVisible(true); noteLabel->setEnabled(true); if(ev_note != -1) noteSpinBox->setValue(ev_note); } else { noteSpinBox->setEnabled(false); noteSpinBox->setVisible(false); noteLabel->setEnabled(false); noteLabel->setVisible(false); } } } else { noteSpinBox->setEnabled(false); noteSpinBox->setVisible(false); noteLabel->setEnabled(false); noteLabel->setVisible(false); if(sel_item) ctrlListClicked(sel_item); } connect(ctrlList, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(ctrlListClicked(QListWidgetItem*))); connect(buttonNewController, SIGNAL(clicked()), SLOT(newController())); connect(hbank, SIGNAL(valueChanged(int)), SLOT(programChanged())); connect(lbank, SIGNAL(valueChanged(int)), SLOT(programChanged())); connect(program, SIGNAL(valueChanged(int)), SLOT(programChanged())); connect(patchName, SIGNAL(released()), SLOT(instrPopup())); connect(buttonCancel, SIGNAL(clicked()), SLOT(reject())); connect(buttonOk, SIGNAL(clicked()), SLOT(accept())); timePos->setValue(tick); } //--------------------------------------------------------- // newController //--------------------------------------------------------- void EditCtrlDialog::newController() { MusEGui::PopupMenu* pup = new MusEGui::PopupMenu(this); // populate popup with all controllers available for // current instrument MusECore::MidiTrack* track = part->track(); const int portn = track->outPort(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[portn]; const bool isNewDrum = track->type() == MusECore::Track::DRUM; const bool isMidi = track->type() == MusECore::Track::MIDI; MusECore::MidiInstrument* instr = port->instrument(); const int channel = track->outChannel(); const int patch = port->hwCtrlState(channel, MusECore::CTRL_PROGRAM); MusECore::MidiControllerList* mcl = new MusECore::MidiControllerList(); instr->getControllers(mcl, channel, patch); MusECore::MidiCtrlValListList* cll = port->controller(); for (MusECore::ciMidiController ci = mcl->cbegin(); ci != mcl->cend(); ++ci) { MusECore::MidiController* c = ci->second; const int cnum = c->num(); const int show = c->showInTracks(); if((isNewDrum && !(show & MusECore::MidiController::ShowInDrum)) || (isMidi && !(show & MusECore::MidiController::ShowInMidi))) continue; // If it's not already in the parent menu... int idx = 0; for(; idx < ctrlList->count(); ++idx) { if(ctrlList->item(idx)->data(Qt::UserRole).toInt() == cnum) break; } if(idx >= ctrlList->count()) { QAction* act = pup->addAction(MusECore::midiCtrlNumString(cnum, true) + c->name()); act->setData(cnum); } } QAction* act = pup->exec(buttonNewController->mapToGlobal(QPoint(0,0))); if (act && act->data().toInt() != -1) { int rv = act->data().toInt(); int cnum = rv; for (MusECore::ciMidiController ci = mcl->cbegin(); ci != mcl->cend(); ++ci) { MusECore::MidiController* mc = ci->second; if (mc->num() == cnum) { // Create a new controller list if it does not exist. // FIXME: Sorry no per-pitch controller lists created here // (meaning you should only create one 'new' one at a time) // because the user has not had a chance to choose a pitch yet. // They are handled in accept(), where there are more checks and creations. if(!mc->isPerNoteController() && cll->find(channel, rv) == cll->end()) { MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(rv); cll->add(channel, vl); } int idx = 0; for (; idx < ctrlList->count() ;++idx) { QListWidgetItem* item = ctrlList->item(idx); int item_data = item->data(Qt::UserRole).toInt(); if(item_data == cnum) { ctrlList->setCurrentItem(item); ctrlListClicked(item); break; } } if (idx >= ctrlList->count()) { QListWidgetItem* new_item = new QListWidgetItem(act->text(), ctrlList); new_item->setData(Qt::UserRole, cnum); ctrlList->setCurrentItem(new_item); ctrlListClicked(new_item); break; } break; } } } delete mcl; delete pup; } //--------------------------------------------------------- // ctrlListClicked //--------------------------------------------------------- void EditCtrlDialog::ctrlListClicked(QListWidgetItem* item) { if(item == 0) return; int cnum = item->data(Qt::UserRole).toInt(); MusECore::MidiTrack* track = part->track(); int portn = track->outPort(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[portn]; const int chan = track->outChannel(); MusECore::MidiController* c = port->midiController(cnum, chan); int val; if (cnum == MusECore::CTRL_PROGRAM) { widgetStack->setCurrentIndex(1); val = c->initVal(); if(val == MusECore::CTRL_VAL_UNKNOWN) val = 0; updatePatch(val); } else { widgetStack->setCurrentIndex(0); if(c->isPerNoteController()) { noteSpinBox->setEnabled(true); noteSpinBox->setVisible(true); noteLabel->setEnabled(true); noteLabel->setVisible(true); } else { noteSpinBox->setEnabled(false); noteSpinBox->setVisible(false); noteLabel->setEnabled(false); noteLabel->setVisible(false); } valSlider->setRange(c->minVal(), c->maxVal()); valSpinBox->setRange(c->minVal(), c->maxVal()); controllerName->setText(c->name()); val = c->initVal(); if(val == MusECore::CTRL_VAL_UNKNOWN || val == 0) { switch(cnum) { case MusECore::CTRL_PANPOT: val = 64 - c->bias(); break; case MusECore::CTRL_VOLUME: val = 100; break; default: val = 0; break; } } valSlider->setValue(val); } } //--------------------------------------------------------- // updatePatch //--------------------------------------------------------- void EditCtrlDialog::updatePatch(int val) { MusECore::MidiTrack* track = part->track(); int port = track->outPort(); int channel = track->outChannel(); MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); patchName->setText(instr->getPatchName(channel, val, track->isDrumTrack(), true)); // Include default. int hb = ((val >> 16) & 0xff) + 1; if (hb == 0x100) hb = 0; int lb = ((val >> 8) & 0xff) + 1; if (lb == 0x100) lb = 0; int pr = (val & 0xff) + 1; if (pr == 0x100) pr = 0; hbank->blockSignals(true); lbank->blockSignals(true); program->blockSignals(true); hbank->setValue(hb); lbank->setValue(lb); program->setValue(pr); hbank->blockSignals(false); lbank->blockSignals(false); program->blockSignals(false); } //--------------------------------------------------------- // instrPopup //--------------------------------------------------------- void EditCtrlDialog::instrPopup() { MusECore::MidiTrack* track = part->track(); int channel = track->outChannel(); int port = track->outPort(); MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); MusEGui::PopupMenu* pup = new MusEGui::PopupMenu(this); instr->populatePatchPopup(pup, channel, track->isDrumTrack()); if(pup->actions().count() == 0) { delete pup; return; } QAction* rv = pup->exec(patchName->mapToGlobal(QPoint(10,5))); if (rv) { updatePatch(rv->data().toInt()); } delete pup; } //--------------------------------------------------------- // programChanged //--------------------------------------------------------- void EditCtrlDialog::programChanged() { int hb = hbank->value(); int lb = lbank->value(); int prog = program->value(); if (hb > 0 && hb < 129) hb -= 1; else hb = 0xff; if (lb > 0 && lb < 129) lb -= 1; else lb = 0xff; if (prog > 0 && prog < 129) prog -= 1; else prog = 0xff; int val = (hb << 16) + (lb << 8) + prog; updatePatch(val); } } // namespace MusEGui muse-4.0.0/src/muse/components/editevent.h000066400000000000000000000112121404105311000205330ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: editevent.h,v 1.6.2.1 2008/05/21 00:28:53 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __EDIT_EVENT_H__ #define __EDIT_EVENT_H__ #include "ui_editnotedialogbase.h" #include "ui_editsysexdialogbase.h" #include "ui_editctrlbase.h" #include #include "event.h" // Forward declarations: class QLabel; class QGridLayout; class QTextEdit; class QRadioButton; class QListWidgetItem; class QMenu; namespace MusECore { class Event; class MidiPart; class MidiInstrument; } namespace MusEGui { class IntLabel; class PosEdit; class PitchEdit; //--------------------------------------------------------- // EditEventDialog //--------------------------------------------------------- class EditEventDialog : public QDialog { Q_OBJECT protected: QGridLayout* layout1; public: EditEventDialog(QWidget* parent=0); virtual MusECore::Event getEvent() = 0; }; //--------------------------------------------------------- // EditNoteDialog //--------------------------------------------------------- class EditNoteDialog : public QDialog, public Ui::EditNoteDialogBase { Q_OBJECT public: EditNoteDialog(int tick, const MusECore::Event&, QWidget* parent=0); static MusECore::Event getEvent(int tick, const MusECore::Event&, QWidget* parent = 0); virtual MusECore::Event getEvent(); }; //--------------------------------------------------------- // EditSysExDialog //--------------------------------------------------------- class EditSysexDialog : public QDialog, public Ui::EditSysexDialogBase { Q_OBJECT MusECore::MidiInstrument* _instr; unsigned char* sysex; int len; protected: QGridLayout* layout; private slots: virtual void accept(); virtual void editChanged(); virtual void selectSysex(); public: EditSysexDialog(int tick, const MusECore::Event&, QWidget* parent=0, MusECore::MidiInstrument* instr = 0); ~EditSysexDialog(); static MusECore::Event getEvent(int tick, const MusECore::Event&, QWidget* parent = 0, MusECore::MidiInstrument* instr = 0); virtual MusECore::Event getEvent(); }; //--------------------------------------------------------- // EditCtrlDialog //--------------------------------------------------------- class EditCtrlDialog : public QDialog, public Ui::EditCtrlBase { Q_OBJECT const MusECore::MidiPart* part; void updatePatch(int val); private slots: void ctrlListClicked(QListWidgetItem*); void newController(); void programChanged(); void instrPopup(); protected: QGridLayout* layout; public: EditCtrlDialog(int tick, const MusECore::Event&, const MusECore::MidiPart*, QWidget* parent=0); static MusECore::Event getEvent(int tick, const MusECore::Event&, const MusECore::MidiPart*, QWidget* parent = 0); virtual MusECore::Event getEvent(); }; //--------------------------------------------------------- // EditMetaDialog //--------------------------------------------------------- class EditMetaDialog : public EditEventDialog { Q_OBJECT unsigned char* meta; int len; PosEdit* epos; QTextEdit* edit; MusEGui::IntLabel* il2; QRadioButton* hexButton; QLabel* typeLabel; protected: QGridLayout* layout; private slots: virtual void accept(); void toggled(bool); void typeChanged(int); public: EditMetaDialog(int tick, const MusECore::Event&, QWidget* parent=0); ~EditMetaDialog(); static MusECore::Event getEvent(int tick, const MusECore::Event&, QWidget* parent = 0); virtual MusECore::Event getEvent(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/editinstrument.cpp000066400000000000000000004165001404105311000221660ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: editinstrument.cpp,v 1.2.2.6 2009/05/31 05:12:12 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) // (C) Copyright 2012, 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include #include #include #include #include #include #include "midictrl.h" #include "editinstrument.h" #include "globals.h" #include "song.h" #include "xml.h" #include "midi.h" #include "gconfig.h" #include "icons.h" #include "popupmenu.h" #include "dlist.h" #include "drummap.h" #include "editevent.h" #include "operations.h" #include "audio.h" #include "gui.h" #include "app.h" // Forwards from header: #include #include #include #include #include #include #include #include #include #include "minstrument.h" #include "midi_controller.h" #include "header.h" #include "dlist.h" namespace MusECore { extern int string2sysex(const QString& s, unsigned char** data); extern QString sysex2string(int len, unsigned char* data); } namespace MusEGui { enum { COL_CNAME = 0, COL_TYPE, COL_HNUM, COL_LNUM, COL_MIN, COL_MAX, COL_DEF, COL_DRUM_DEF, COL_SHOW_MIDI, COL_SHOW_DRUM }; static const QSize ledSize(16, 16); //--------------------------------------------------------- // InitListItem //--------------------------------------------------------- class InitListItem : public QTreeWidgetItem { public: MusECore::Event _event; MusECore::MidiInstrument* _instr; InitListItem(QTreeWidget* parent, MusECore::Event ev, MusECore::MidiInstrument* ins) : QTreeWidgetItem(parent) { _event = ev; _instr = ins; setData(0, Qt::DisplayRole, colText(0)); setData(1, Qt::DisplayRole, colText(1)); setData(2, Qt::DisplayRole, colText(2)); setData(3, Qt::DisplayRole, colText(3)); } virtual QString colText(int col) const; //virtual QVariant data(int col, int role) const; virtual bool operator< ( const QTreeWidgetItem & other ) const { int col = other.treeWidget()->sortColumn(); InitListItem* eli = (InitListItem*) &other; switch(col) { case 0: return _event.tick() < eli->_event.tick(); break; case 1: return _event.dataLen() < eli->_event.dataLen(); break; case 2: return colText(col).localeAwareCompare(other.text(col)) < 0; break; case 3: return colText(col).localeAwareCompare(other.text(col)) < 0; break; default: break; } return 0; } }; // //--------------------------------------------------------- // // data // //--------------------------------------------------------- // // QVariant InitListItem::data(int col, int role) const // { // if(role != Qt::DisplayRole && role != Qt::EditRole) // return QString(); // return colText(col); // } //--------------------------------------------------------- // colText //--------------------------------------------------------- QString InitListItem::colText(int col) const { QString s; QString commentLabel; switch(col) { case 0: s.setNum(_event.tick()); break; case 1: s.setNum(_event.dataLen()); break; case 2: switch(_event.type()) { case MusECore::Sysex: { int i; for (i = 0; i < 10; ++i) { if (i >= _event.dataLen()) break; s += QString(" 0x"); QString k; k.setNum(_event.data()[i] & 0xff, 16); s += k; } if (i == 10) s += QString("..."); } break; default: break; } break; case 3: switch(_event.type()) { case MusECore::Sysex: { return MusECore::nameSysex(_event.dataLen(), _event.data(), _instr); } break; case MusECore::Controller: s = QObject::tr("Controller !"); break; default: s = QObject::tr("Other !"); break; } break; } return s; } //--------------------------------------------------------- // EditInstrument //--------------------------------------------------------- EditInstrument::EditInstrument(QWidget* parent, Qt::WindowFlags fl) : QMainWindow(parent, fl) { setupUi(this); toolBar->setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize)); workingInstrument = new MusECore::MidiInstrument(); noteOffModeList->addItem(tr("Use note offs"), MusECore::MidiInstrument::NoteOffAll); noteOffModeList->addItem(tr("No note offs"), MusECore::MidiInstrument::NoteOffNone); noteOffModeList->addItem(tr("Convert to 0-vel note ons"), MusECore::MidiInstrument::NoteOffConvertToZVNoteOn); noteOffModeList->setCurrentIndex(0); ctrlType->addItem(tr("Control7"), MusECore::MidiController::Controller7); ctrlType->addItem(tr("Control14"), MusECore::MidiController::Controller14); ctrlType->addItem(tr("RPN"), MusECore::MidiController::RPN); ctrlType->addItem(tr("NPRN"), MusECore::MidiController::NRPN); ctrlType->addItem(tr("RPN14"), MusECore::MidiController::RPN14); ctrlType->addItem(tr("NRPN14"), MusECore::MidiController::NRPN14); ctrlType->addItem(tr("Pitch"), MusECore::MidiController::Pitch); ctrlType->addItem(tr("Program"), MusECore::MidiController::Program); ctrlType->addItem(tr("PolyAftertouch"), MusECore::MidiController::PolyAftertouch); ctrlType->addItem(tr("Aftertouch"), MusECore::MidiController::Aftertouch); ctrlType->setCurrentIndex(0); fileNewAction->setIcon(*filenewSVGIcon); fileOpenAction->setIcon(*fileopenSVGIcon); fileSaveAction->setIcon(*filesaveSVGIcon); fileSaveAsAction->setIcon(*filesaveasSVGIcon); fileCloseAction->setIcon(*exitSVGIcon); actionInfo->setIcon(*infoSVGIcon); viewController->setSelectionMode(QAbstractItemView::SingleSelection); QAction* whatsthis = QWhatsThis::createAction(this); whatsthis->setIcon(*whatsthisSVGIcon); toolBar->addAction(whatsthis); Help->addAction(whatsthis); // populate instrument list oldMidiInstrument = nullptr; oldPatchItem = nullptr; for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { // Imperfect, crude way of ignoring internal instruments, soft synths etc. If it has a gui, // it must be an internal instrument. But this will still allow some vst instruments (without a gui) // to show up in the list. // Hmm, try file path instead... //if((*i)->hasGui()) if((*i)->filePath().isEmpty()) continue; QListWidgetItem* item = new QListWidgetItem((*i)->iname()); item->setData(Qt::UserRole, QVariant::fromValue((void*)(*i))); instrumentList->addItem(item); } instrumentList->setSelectionMode(QAbstractItemView::SingleSelection); if(instrumentList->item(0)) instrumentList->setCurrentItem(instrumentList->item(0)); dlist_header = new Header(dlistContainer, "header"); dlist_header->setFixedHeight(31); //: hide dlist_header->setColumnLabel(tr("H"), DList::COL_HIDE, 20); //: mute dlist_header->setColumnLabel(tr("M"), DList::COL_MUTE, 20); dlist_header->setColumnLabel(tr("Name"), DList::COL_NAME, 120); dlist_header->setColumnLabel(tr("Vol"), DList::COL_VOLUME); dlist_header->setColumnLabel(tr("Quant"), DList::COL_QUANT, 40); dlist_header->setColumnLabel(tr("E-Note"), DList::COL_INPUTTRIGGER, 50); dlist_header->setColumnLabel(tr("Len"), DList::COL_NOTELENGTH, 40); dlist_header->setColumnLabel(tr("A-Note"), DList::COL_NOTE, 50); dlist_header->setColumnLabel(tr("Ch"), DList::COL_OUTCHANNEL); dlist_header->setColumnLabel(tr("Port"), DList::COL_OUTPORT, 70); dlist_header->setColumnLabel(tr("LV1"), DList::COL_LEVEL1); dlist_header->setColumnLabel(tr("LV2"), DList::COL_LEVEL2); dlist_header->setColumnLabel(tr("LV3"), DList::COL_LEVEL3); dlist_header->setColumnLabel(tr("LV4"), DList::COL_LEVEL4); dlist_header->hide(); setHeaderToolTips(); setHeaderWhatsThis(); QFontMetrics fm(initEventList->font()); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int n = fm.horizontalAdvance('9'); #else int n = fm.width('9'); #endif int b = 24; initEventList->setAllColumnsShowFocus(true); initEventList->setSelectionMode(QAbstractItemView::SingleSelection); QStringList columnnames; columnnames << tr("Tick") << tr("Len") << tr("Data") << tr("Name"); initEventList->setHeaderLabels(columnnames); initEventList->setColumnWidth(0, n * 6 + b); initEventList->setColumnWidth(1, n * 6 + b); //initEventList->setSortingEnabled(true); // No sorting - we want same order of event list! //initEventList->sortByColumn(0, Qt::AscendingOrder); connect(initEventList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), SLOT(editInitListItem(QTreeWidgetItem*))); connect(initChangeButton, SIGNAL(clicked(bool)), SLOT(initListChangeClicked())); connect(initAddButton, SIGNAL(clicked(bool)), SLOT(initListAddClicked())); connect(initDeleteButton, SIGNAL(clicked(bool)), SLOT(initListDeleteClicked())); ctrlValidLabel->setPixmap(ledGreenSVGIcon->pixmap(ledSize)); // ctrlValidLabel->setPixmap(*greendotIcon); connect(patchFromBox, SIGNAL(valueChanged(int)), this, SLOT(patchCollectionSpinboxChanged(int))); connect(lbankFromBox, SIGNAL(valueChanged(int)), this, SLOT(patchCollectionSpinboxChanged(int))); connect(hbankFromBox, SIGNAL(valueChanged(int)), this, SLOT(patchCollectionSpinboxChanged(int))); connect(addCollBtn, SIGNAL(clicked()), this, SLOT(addPatchCollection())); connect(rmCollBtn, SIGNAL(clicked()), this, SLOT(delPatchCollection())); connect(copyCollBtn, SIGNAL(clicked()), this, SLOT(copyPatchCollection())); connect(collUpBtn, SIGNAL(clicked()), this, SLOT(patchCollectionUp())); connect(collDownBtn, SIGNAL(clicked()), this, SLOT(patchCollectionDown())); connect(patchCollections, SIGNAL(activated(const QModelIndex&)), this, SLOT(patchActivated(const QModelIndex&))); connect(patchCollections, SIGNAL(clicked (const QModelIndex&)), this, SLOT(patchActivated(const QModelIndex&))); patch_coll_model=new QStringListModel(); patchCollections->setModel(patch_coll_model); patchCollections->setEditTriggers(QAbstractItemView::NoEditTriggers); connect(instrumentList, SIGNAL(itemSelectionChanged()), SLOT(instrumentChanged())); connect(patchView, SIGNAL(itemSelectionChanged()), SLOT(patchChanged())); dlist_vscroll = new QScrollBar(Qt::Vertical, this); dlist_vscroll->setMaximum(128*TH); dlist_vscroll->hide(); dlist_grid = new QGridLayout(dlistContainer); dlist_grid->setContentsMargins(0, 0, 0, 0); dlist_grid->setSpacing(0); dlist_grid->setRowStretch(1, 100); dlist_grid->setColumnStretch(0, 100); dlist_grid->addWidget(dlist_header, 0, 0); dlist_grid->addWidget(dlist_vscroll, 1,1); dlist=NULL; changeInstrument(); connect(viewController, SIGNAL(itemSelectionChanged()), SLOT(controllerChanged())); connect(noteOffModeList,SIGNAL(activated(int)), SLOT(noteOffModeChanged(int))); connect(instrumentName, SIGNAL(editingFinished()), SLOT(instrumentNameReturn())); connect(patchNameEdit, SIGNAL(editingFinished()), SLOT(patchNameReturn())); connect(patchDelete, SIGNAL(clicked()), SLOT(deletePatchClicked())); connect(patchNew, SIGNAL(clicked()), SLOT(newPatchClicked())); connect(patchNewGroup, SIGNAL(clicked()), SLOT(newGroupClicked())); connect(patchButton, SIGNAL(clicked()), SLOT(patchButtonClicked())); connect(spinBoxDefault, SIGNAL(valueChanged(int)), SLOT(ctrlDefaultChanged(int))); connect(defPatchH, SIGNAL(valueChanged(int)), SLOT(defPatchChanged(int))); connect(defPatchL, SIGNAL(valueChanged(int)), SLOT(defPatchChanged(int))); connect(defPatchProg, SIGNAL(valueChanged(int)), SLOT(defPatchChanged(int))); connect(drumPatchButton, SIGNAL(clicked()), SLOT(drumPatchButtonClicked())); connect(spinBoxDrumDefault, SIGNAL(valueChanged(int)), SLOT(ctrlDrumDefaultChanged(int))); connect(defDrumPatchH, SIGNAL(valueChanged(int)), SLOT(defDrumPatchChanged(int))); connect(defDrumPatchL, SIGNAL(valueChanged(int)), SLOT(defDrumPatchChanged(int))); connect(defDrumPatchProg, SIGNAL(valueChanged(int)), SLOT(defDrumPatchChanged(int))); connect(drummapsPatchNames, SIGNAL(clicked()), SLOT(drummapCollectionPatchButtonClicked())); connect(deleteController, SIGNAL(clicked()), SLOT(deleteControllerClicked())); connect(newController, SIGNAL(clicked()), SLOT(newControllerClicked())); connect(addController, SIGNAL(clicked()), SLOT(addControllerClicked())); connect(ctrlType,SIGNAL(activated(int)), SLOT(ctrlTypeChanged(int))); connect(ctrlName, SIGNAL(editingFinished()), SLOT(ctrlNameReturn())); connect(spinBoxHCtrlNo, SIGNAL(valueChanged(int)), SLOT(ctrlNumChanged())); connect(spinBoxLCtrlNo, SIGNAL(valueChanged(int)), SLOT(ctrlNumChanged())); connect(spinBoxMin, SIGNAL(valueChanged(int)), SLOT(ctrlMinChanged(int))); connect(spinBoxMax, SIGNAL(valueChanged(int)), SLOT(ctrlMaxChanged(int))); connect(ctrlShowInMidi,SIGNAL(stateChanged(int)), SLOT(ctrlShowInMidiChanged(int))); connect(ctrlShowInDrum,SIGNAL(stateChanged(int)), SLOT(ctrlShowInDrumChanged(int))); connect(tabWidget3, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); connect(sysexList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), SLOT(sysexChanged(QListWidgetItem*, QListWidgetItem*))); connect(deleteSysex, SIGNAL(clicked()), SLOT(deleteSysexClicked())); connect(newSysex, SIGNAL(clicked()), SLOT(newSysexClicked())); } EditInstrument::~EditInstrument() { delete workingInstrument; } //--------------------------------------------------------- // setHeaderWhatsThis //--------------------------------------------------------- void EditInstrument::setHeaderWhatsThis() { dlist_header->setWhatsThis(DList::COL_HIDE, tr("Hide instrument")); dlist_header->setWhatsThis(DList::COL_MUTE, tr("Mute instrument")); dlist_header->setWhatsThis(DList::COL_NAME, tr("Sound name")); dlist_header->setWhatsThis(DList::COL_VOLUME, tr("Volume percent")); dlist_header->setWhatsThis(DList::COL_QUANT, tr("Quantisation")); dlist_header->setWhatsThis(DList::COL_INPUTTRIGGER, tr("This input note triggers the sound")); dlist_header->setWhatsThis(DList::COL_NOTELENGTH, tr("Note length")); dlist_header->setWhatsThis(DList::COL_NOTE, tr("This is the note which is played")); dlist_header->setWhatsThis(DList::COL_OUTCHANNEL, tr("Override track output channel (hold ctl to affect all rows)")); dlist_header->setWhatsThis(DList::COL_OUTPORT, tr("Override track output port (hold ctl to affect all rows)")); dlist_header->setWhatsThis(DList::COL_LEVEL1, tr("Control + meta keys: Draw velocity level 1")); dlist_header->setWhatsThis(DList::COL_LEVEL2, tr("Meta key: Draw velocity level 2")); dlist_header->setWhatsThis(DList::COL_LEVEL3, tr("Draw default velocity level 3")); dlist_header->setWhatsThis(DList::COL_LEVEL4, tr("Meta + alt keys: Draw velocity level 4")); } //--------------------------------------------------------- // setHeaderToolTips //--------------------------------------------------------- void EditInstrument::setHeaderToolTips() { dlist_header->setToolTip(DList::COL_HIDE, tr("Hide instrument")); dlist_header->setToolTip(DList::COL_MUTE, tr("Mute instrument")); dlist_header->setToolTip(DList::COL_NAME, tr("Sound name")); dlist_header->setToolTip(DList::COL_VOLUME, tr("Volume percent")); dlist_header->setToolTip(DList::COL_QUANT, tr("Quantisation")); dlist_header->setToolTip(DList::COL_INPUTTRIGGER, tr("This input note triggers the sound")); dlist_header->setToolTip(DList::COL_NOTELENGTH, tr("Note length")); dlist_header->setToolTip(DList::COL_NOTE, tr("This is the note which is played")); dlist_header->setToolTip(DList::COL_OUTCHANNEL, tr("Override track output channel (ctl: affect all rows)")); dlist_header->setToolTip(DList::COL_OUTPORT, tr("Override track output port (ctl: affect all rows)")); dlist_header->setToolTip(DList::COL_LEVEL1, tr("Control + meta keys: Draw velocity level 1")); dlist_header->setToolTip(DList::COL_LEVEL2, tr("Meta key: Draw velocity level 2")); dlist_header->setToolTip(DList::COL_LEVEL3, tr("Draw default velocity level 3")); dlist_header->setToolTip(DList::COL_LEVEL4, tr("Meta + alt keys: Draw velocity level 4")); } void EditInstrument::findInstrument(const QString& find_instrument) { if(find_instrument.isEmpty()) return; QList found = instrumentList->findItems(find_instrument, Qt::MatchExactly); if(!found.isEmpty()) instrumentList->setCurrentItem(found.at(0)); } void EditInstrument::showTab(EditInstrumentTabType n) { if(n >= tabWidget3->count()) return; tabWidget3->setCurrentIndex(n); } void EditInstrument::patchCollectionSpinboxChanged(int) { storePatchCollection(); drummapsPatchNames->setText(workingInstrument->getPatchName(0, getDrummapCollectionPatchNumber(), true, false)); } void EditInstrument::storePatchCollection() { using MusECore::patch_drummap_mapping_t; using MusECore::patch_drummap_mapping_list_t; using MusECore::iPatchDrummapMapping_t; int idx=patchCollections->currentIndex().row(); patch_drummap_mapping_list_t* pdm = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. if(pdm && idx>=0 && (unsigned)idxsize()) { iPatchDrummapMapping_t it=pdm->begin(); advance(it,idx); if (patchFromBox->value() == 0) it->setProg(0xff); else it->setProg(patchFromBox->value() - 1); if (lbankFromBox->value() == 0) it->setLBank(0xff); else it->setLBank(lbankFromBox->value() - 1); if (hbankFromBox->value() == 0) it->setHBank(0xff); else it->setHBank(hbankFromBox->value() - 1); workingInstrument->setDirty(true); repopulatePatchCollections(); } } void EditInstrument::fetchPatchCollection() { using MusECore::patch_drummap_mapping_t; using MusECore::patch_drummap_mapping_list_t; using MusECore::iPatchDrummapMapping_t; int idx=patchCollections->currentIndex().row(); patch_drummap_mapping_list_t* pdm = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. if(pdm && idx>=0 && (unsigned)idxsize()) { iPatchDrummapMapping_t it=pdm->begin(); advance(it,idx); patchFromBox->blockSignals(true); lbankFromBox->blockSignals(true); hbankFromBox->blockSignals(true); if(it->programDontCare()) patchFromBox->setValue(0); else patchFromBox->setValue(it->prog() + 1); if(it->lbankDontCare()) lbankFromBox->setValue(0); else lbankFromBox->setValue(it->lbank() + 1); if(it->hbankDontCare()) hbankFromBox->setValue(0); else hbankFromBox->setValue(it->hbank() + 1); patchFromBox->blockSignals(false); lbankFromBox->blockSignals(false); hbankFromBox->blockSignals(false); drummapsPatchNames->setText(workingInstrument->getPatchName(0, it->_patch, true, false)); } } void EditInstrument::patchActivated(const QModelIndex& idx) { using MusECore::patch_drummap_mapping_t; using MusECore::patch_drummap_mapping_list_t; using MusECore::iPatchDrummapMapping_t; if (idx.row()>=0) { using MusECore::DrumMap; patch_drummap_mapping_list_t* tmp = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. if(!tmp) return; iPatchDrummapMapping_t it=tmp->begin(); if ((unsigned)idx.row()>=tmp->size()) printf("THIS SHOULD NEVER HAPPEN: idx.row()>=tmp->size() in EditInstrument::patchActivated()\n"); advance(it, idx.row()); DrumMap* dm=it->drummap; if (dlist) { dlist->hide(); delete dlist; dlist=NULL; } dlist=new DList(dlist_header,dlistContainer,1,dm); dlist->setYPos(dlist_vscroll->value()); connect(dlist_vscroll, SIGNAL(valueChanged(int)), dlist, SLOT(setYPos(int))); dlist_grid->addWidget(dlist, 1, 0); dlist_header->show(); dlist->show(); dlist_vscroll->show(); collUpBtn->setEnabled(idx.row()>0); collDownBtn->setEnabled(idx.row()rowCount()-1); rmCollBtn->setEnabled(true); copyCollBtn->setEnabled(true); patchCollectionContainer->setEnabled(true); fetchPatchCollection(); } } void EditInstrument::addPatchCollection() { using MusECore::patch_drummap_mapping_t; using MusECore::patch_drummap_mapping_list_t; using MusECore::iPatchDrummapMapping_t; int idx=patchCollections->currentIndex().row(); patch_drummap_mapping_list_t* tmp = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. if(!tmp) return; iPatchDrummapMapping_t it=tmp->begin(); advance(it,idx+1); tmp->insert(it,patch_drummap_mapping_t()); repopulatePatchCollections(); patchCollections->setCurrentIndex(patch_coll_model->index(idx+1)); patchActivated(patchCollections->currentIndex()); workingInstrument->setDirty(true); } void EditInstrument::delPatchCollection() { using MusECore::patch_drummap_mapping_t; using MusECore::patch_drummap_mapping_list_t; using MusECore::iPatchDrummapMapping_t; int idx=patchCollections->currentIndex().row(); if (idx>=0) { if (dlist) { dlist->hide(); delete dlist; dlist=NULL; } dlist_header->hide(); dlist_vscroll->hide(); rmCollBtn->setEnabled(false); copyCollBtn->setEnabled(false); patchCollectionContainer->setEnabled(false); collUpBtn->setEnabled(false); collDownBtn->setEnabled(false); patch_drummap_mapping_list_t* tmp = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. if(tmp) { iPatchDrummapMapping_t it=tmp->begin(); advance(it,idx); tmp->erase(it); } repopulatePatchCollections(); patchActivated(patchCollections->currentIndex()); workingInstrument->setDirty(true); } } void EditInstrument::copyPatchCollection() { using MusECore::patch_drummap_mapping_t; using MusECore::patch_drummap_mapping_list_t; using MusECore::iPatchDrummapMapping_t; int idx=patchCollections->currentIndex().row(); patch_drummap_mapping_list_t* tmp = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. if(!tmp) return; iPatchDrummapMapping_t it=tmp->begin(); advance(it,idx); patch_drummap_mapping_t tmp2(*it); it++; tmp->insert(it,tmp2); patch_coll_model->insertRow(idx+1); patch_coll_model->setData(patch_coll_model->index(idx+1), patch_coll_model->index(idx).data()); patchCollections->setCurrentIndex(patch_coll_model->index(idx+1)); patchActivated(patchCollections->currentIndex()); workingInstrument->setDirty(true); } void EditInstrument::patchCollectionUp() { using MusECore::patch_drummap_mapping_t; using MusECore::patch_drummap_mapping_list_t; using MusECore::iPatchDrummapMapping_t; patch_drummap_mapping_list_t* pdm = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. if(!pdm) return; int idx=patchCollections->currentIndex().row(); if (idx>=1) { iPatchDrummapMapping_t it=pdm->begin(); advance(it,idx-1); iPatchDrummapMapping_t it2=it; it2++; //it2 is the element to move, it is the element to put before. pdm->insert(it,*it2); pdm->erase(it2); repopulatePatchCollections(); patchCollections->setCurrentIndex(patch_coll_model->index(idx-1)); patchActivated(patchCollections->currentIndex()); workingInstrument->setDirty(true); } } void EditInstrument::patchCollectionDown() { using MusECore::patch_drummap_mapping_t; using MusECore::patch_drummap_mapping_list_t; using MusECore::iPatchDrummapMapping_t; patch_drummap_mapping_list_t* pdm = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. if(!pdm) return; int idx=patchCollections->currentIndex().row(); if ((unsigned)idxsize()-1) { iPatchDrummapMapping_t it=pdm->begin(); advance(it,idx); iPatchDrummapMapping_t it2=it; it2++; it2++; //it is the element to move, it2 is the element to put before (might be end()) pdm->insert(it2,*it); pdm->erase(it); repopulatePatchCollections(); patchCollections->setCurrentIndex(patch_coll_model->index(idx+1)); patchActivated(patchCollections->currentIndex()); workingInstrument->setDirty(true); } } void EditInstrument::repopulatePatchCollections() { using MusECore::patch_drummap_mapping_t; using MusECore::patch_drummap_mapping_list_t; using MusECore::iPatchDrummapMapping_t; int idx=patchCollections->currentIndex().row(); QStringList strlist; patch_drummap_mapping_list_t* pdm = workingInstrument->get_patch_drummap_mapping(-1, false); // No default. if(pdm) { for (iPatchDrummapMapping_t it=pdm->begin(); it!=pdm->end(); it++) { patch_drummap_mapping_t& pd = *it; // Patch name: Get drum, and no default - we want an exact match. strlist << (it->to_string() + QString(" (") + workingInstrument->getPatchName(0, pd._patch, true, false) + QString(")")); } } patch_coll_model->setStringList(strlist); patchCollections->setCurrentIndex(patch_coll_model->index(idx)); } //--------------------------------------------------------- // helpWhatsThis //--------------------------------------------------------- void EditInstrument::helpWhatsThis() { whatsThis(); } //--------------------------------------------------------- // fileNew //--------------------------------------------------------- void EditInstrument::fileNew() { // Allow these to update... instrumentNameReturn(); patchNameReturn(); ctrlNameReturn(); MusECore::PendingOperationList operations; for (int i = 1;; ++i) { QString s = QString("Instrument-%1").arg(i); bool found = false; for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { if (s == (*i)->iname()) { found = true; break; } } if (!found) { MusECore::MidiInstrument* oi = 0; if(oldMidiInstrument) oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); MusECore::MidiInstrument* wip = workingInstrument; if(checkDirty(wip)) // No save was chosen. Restore the actual instrument name. { if(oi) { oldMidiInstrument->setText(oi->iname()); // No file path? Only a new unsaved instrument can do that. So delete it. if(oi->filePath().isEmpty()) // Delete the list item and the instrument. deleteInstrument(oldMidiInstrument); } } workingInstrument->setDirty(false); MusECore::MidiInstrument* ni = new MusECore::MidiInstrument(s); operations.add(MusECore::PendingOperationItem(&MusECore::midiInstruments, ni, MusECore::PendingOperationItem::AddMidiInstrument)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); QListWidgetItem* item = new QListWidgetItem(ni->iname()); workingInstrument->assign( *ni ); item->setData(Qt::UserRole, QVariant::fromValue((void*)(ni))); instrumentList->addItem(item); oldMidiInstrument = 0; instrumentList->blockSignals(true); instrumentList->setCurrentItem(item); instrumentList->blockSignals(false); changeInstrument(); // We have our new instrument! So set dirty to true. workingInstrument->setDirty(true); break; } } } //--------------------------------------------------------- // fileOpen //--------------------------------------------------------- void EditInstrument::fileOpen() //DELETETHIS? { } //--------------------------------------------------------- // fileSave //--------------------------------------------------------- void EditInstrument::fileSave() { if (workingInstrument->filePath().isEmpty()) { saveAs(); return; } // Do not allow a direct overwrite of a 'built-in' muse instrument. QFileInfo qfi(workingInstrument->filePath()); if(qfi.absolutePath() == MusEGlobal::museInstruments) { saveAs(); return; } FILE* f = fopen(workingInstrument->filePath().toLatin1().constData(), "w"); if(f == 0) { saveAs(); return; } // Allow these to update... instrumentNameReturn(); patchNameReturn(); ctrlNameReturn(); if(fclose(f) != 0) { QString s = QString("Creating file:\n") + workingInstrument->filePath() + QString("\nfailed: ") + QString(strerror(errno) ); QMessageBox::critical(this, tr("MusE: Create file failed"), s); return; } if(fileSave(workingInstrument, workingInstrument->filePath())) workingInstrument->setDirty(false); } //--------------------------------------------------------- // fileSave //--------------------------------------------------------- bool EditInstrument::fileSave(MusECore::MidiInstrument* instrument, const QString& name) { FILE* f = fopen(name.toLatin1().constData(), "w"); if(f == 0) { QString s("Creating file failed: "); s += QString(strerror(errno)); QMessageBox::critical(this, tr("MusE: Create file failed"), s); return false; } MusECore::Xml xml(f); updateInstrument(instrument); instrument->write(0, xml); // Assign the working instrument values to the actual current selected instrument... if(oldMidiInstrument) { MusECore::MidiInstrument* oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); if(oi) { //oi->assign(*workingInstrument); // Now signal the rest of the app so stuff can change... //MusEGlobal::song->update(SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD); MusECore::iMidiInstrument imi = MusECore::midiInstruments.find(oi); if(imi != MusECore::midiInstruments.end()) { // Create a new instrument to be switched to. MusECore::MidiInstrument* ni = new MusECore::MidiInstrument(); // Assign existing values to the new instrument. ni->assign(*workingInstrument); MusECore::PendingOperationList operations; // Operation to erase and delete the existing instrument, and add the new instrument. operations.add(MusECore::PendingOperationItem(&MusECore::midiInstruments, imi, ni, MusECore::PendingOperationItem::ReplaceMidiInstrument)); // Execute the operations. MusEGlobal::audio->msgExecutePendingOperations(operations, true); workingInstrument->assign( *ni ); // ?? Remove, not required? // Set the tree item's data pointer to the new instrument. oldMidiInstrument->setData(Qt::UserRole, QVariant::fromValue((void*)(ni))); changeInstrument(); // ?? Remove, not required? // We have our new instrument! So set dirty to true. //workingInstrument->setDirty(true); } else { // Error: The instrument was not found. Might as well assign to whatever the thing is. oi->assign(*workingInstrument); // Now signal the rest of the app so stuff can change... MusEGlobal::song->update(SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD); } } } if(fclose(f) != 0) { QString s = QString("Write File\n") + name + QString("\nfailed: ") + QString(strerror(errno)); QMessageBox::critical(this, tr("MusE: Write File failed"), s); return false; } return true; } //--------------------------------------------------------- // saveAs //--------------------------------------------------------- void EditInstrument::saveAs() { // Allow these to update... instrumentNameReturn(); patchNameReturn(); ctrlNameReturn(); QString path = MusEGlobal::museUserInstruments; if(!QDir(MusEGlobal::museUserInstruments).exists()) { printf("MusE Error! User instrument directory: %s does not exist. Should be created at startup!\n", MusEGlobal::museUserInstruments.toLatin1().constData()); //path = MusEGlobal::museUser; DELETETHIS? //path = MusEGlobal::configPath; } if (workingInstrument->filePath().isEmpty()) path += QString("/%1.idf").arg(workingInstrument->iname()); else { QFileInfo fi(workingInstrument->filePath()); // Prompt for a new instrument name if the name has not been changed, to avoid duplicates. if(oldMidiInstrument) { MusECore::MidiInstrument* oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); if(oi) { if(oi->iname() == workingInstrument->iname()) { // Prompt only if it's a user instrument, to avoid duplicates in the user instrument dir. // This will still allow a user instrument to override a built-in instrument with the same name. if(fi.absolutePath() != MusEGlobal::museInstruments) { printf("EditInstrument::saveAs Error: Instrument name %s already used!\n", workingInstrument->iname().toLatin1().constData()); return; } } } } path += QString("/%1.idf").arg(fi.baseName()); } QString s = QFileDialog::getSaveFileName(this, tr("MusE: Save Instrument Definition"), path, tr("Instrument Definition (*.idf)")); if (s.isEmpty()) return; workingInstrument->setFilePath(s); if(fileSave(workingInstrument, s)) workingInstrument->setDirty(false); } //--------------------------------------------------------- // fileSaveAs //--------------------------------------------------------- void EditInstrument::fileSaveAs() { // Is this a new unsaved instrument? Just do a normal save. if(workingInstrument->filePath().isEmpty()) { saveAs(); return; } // Allow these to update... instrumentNameReturn(); patchNameReturn(); ctrlNameReturn(); MusECore::MidiInstrument* oi = 0; if(oldMidiInstrument) oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); int res = checkDirty(workingInstrument, true); switch(res) { // No save: case 1: workingInstrument->setDirty(false); if(oi) { oldMidiInstrument->setText(oi->iname()); // No file path? Only a new unsaved instrument can do that. So delete it. if(oi->filePath().isEmpty()) { // Delete the list item and the instrument. deleteInstrument(oldMidiInstrument); oldMidiInstrument = 0; } changeInstrument(); } return; break; // Abort: case 2: return; break; // Save: case 0: workingInstrument->setDirty(false); break; } bool isuser = false; QString so; if(workingInstrument->iname().isEmpty()) so = QString("Instrument"); else so = workingInstrument->iname(); for(int i = 1;; ++i) { QString s = so + QString("-%1").arg(i); bool found = false; for(MusECore::iMidiInstrument imi = MusECore::midiInstruments.begin(); imi != MusECore::midiInstruments.end(); ++imi) { if(s == (*imi)->iname()) { // Allow override of built-in instrument names. QFileInfo fi((*imi)->filePath()); if(fi.absolutePath() == MusEGlobal::museInstruments) break; found = true; break; } } if(found) continue; bool ok; s = QInputDialog::getText(this, tr("MusE: Save instrument as"), tr("Enter a new unique instrument name:"), QLineEdit::Normal, s, &ok); if(!ok) return; if(s.isEmpty()) { --i; continue; } isuser = false; bool builtin = false; found = false; for(MusECore::iMidiInstrument imi = MusECore::midiInstruments.begin(); imi != MusECore::midiInstruments.end(); ++imi) { // If an instrument with the same name is found... if((*imi)->iname() == s) { // If it's not the same name as the working instrument, and it's not an internal instrument (soft synth etc.)... if(s != workingInstrument->iname() && !(*imi)->filePath().isEmpty()) { QFileInfo fi((*imi)->filePath()); // Allow override of built-in and user instruments: // If it's a user instrument, not a built-in instrument... if(fi.absolutePath() == MusEGlobal::museUserInstruments) { // No choice really but to overwrite the destination instrument file! // Can not have two user files containing the same instrument name. if(QMessageBox::question(this, tr("MusE: Save instrument as"), tr("The user instrument '%1' already exists. This will overwrite its .idf instrument file.\nAre you sure?").arg(s), QMessageBox::Ok | QMessageBox::Default, QMessageBox::Cancel | QMessageBox::Escape, Qt::NoButton) == QMessageBox::Ok) { // Set the working instrument's file path to the found instrument's path. workingInstrument->setFilePath((*imi)->filePath()); // Mark as overwriting a user instrument. isuser = true; } else { found = true; break; } } // Assign the found instrument name to the working instrument name. workingInstrument->setIName(s); // Find the instrument in the list and set the old instrument to the item. oldMidiInstrument = instrumentList->findItems(s, Qt::MatchExactly)[0]; // Mark as a built-in instrument. builtin = true; break; } found = true; break; } } if(found) { so = s; i = 0; continue; } so = s; // If the name does not belong to a built-in instrument... if(!builtin) { MusECore::MidiInstrument* ni = new MusECore::MidiInstrument(); ni->assign(*workingInstrument); ni->setIName(so); ni->setFilePath(QString()); MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(&MusECore::midiInstruments, ni, MusECore::PendingOperationItem::AddMidiInstrument)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); QListWidgetItem* item = new QListWidgetItem(so); workingInstrument->assign( *ni ); item->setData(Qt::UserRole, QVariant::fromValue((void*)(ni))); instrumentList->addItem(item); oldMidiInstrument = 0; instrumentList->blockSignals(true); instrumentList->setCurrentItem(item); instrumentList->blockSignals(false); changeInstrument(); // We have our new instrument! So set dirty to true. workingInstrument->setDirty(true); } break; } QString path = MusEGlobal::museUserInstruments; if(!QDir(MusEGlobal::museUserInstruments).exists()) { printf("MusE Error! User instrument directory: %s does not exist. Should be created at startup!\n", MusEGlobal::museUserInstruments.toLatin1().constData()); //path = MusEGlobal::museUser; DELETETHIS //path = MusEGlobal::configPath; } path += QString("/%1.idf").arg(so); QString sfn; // If we are overwriting a user instrument just force the path. if(isuser) sfn = path; else { sfn = QFileDialog::getSaveFileName(this, tr("MusE: Save Instrument Definition"), path, tr("Instrument Definition (*.idf)")); if (sfn.isEmpty()) return; workingInstrument->setFilePath(sfn); } if(fileSave(workingInstrument, sfn)) workingInstrument->setDirty(false); } //--------------------------------------------------------- // fileClose //--------------------------------------------------------- void EditInstrument::fileClose() { close(); } //--------------------------------------------------------- // keyPressEvent //--------------------------------------------------------- void EditInstrument::keyPressEvent(QKeyEvent* e) { if (e->key() == Qt::Key_Escape) { close(); return; } else { e->ignore(); return; } } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void EditInstrument::closeEvent(QCloseEvent* ev) { // Allow these to update... instrumentNameReturn(); patchNameReturn(); ctrlNameReturn(); MusECore::MidiInstrument* oi = 0; if(oldMidiInstrument) oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); int res = checkDirty(workingInstrument, true); switch(res) { // No save: case 1: workingInstrument->setDirty(false); if(oi) { oldMidiInstrument->setText(oi->iname()); // No file path? Only a new unsaved instrument can do that. So delete it. if(oi->filePath().isEmpty()) { // Delete the list item and the instrument. deleteInstrument(oldMidiInstrument); oldMidiInstrument = 0; } changeInstrument(); } break; // Abort: case 2: ev->ignore(); return; break; // Save: case 0: workingInstrument->setDirty(false); break; } QMainWindow::closeEvent(ev); } //--------------------------------------------------------- // changeInstrument //--------------------------------------------------------- void EditInstrument::changeInstrument() { QListWidgetItem* sel = instrumentList->currentItem(); if(!sel) return; oldMidiInstrument = sel; // Assignment // Assign will 'delete' any existing patches, groups, or controllers. workingInstrument->assign( *((MusECore::MidiInstrument*)sel->data(Qt::UserRole).value()) ); workingInstrument->setDirty(false); noteOffModeList->blockSignals(true); noteOffModeList->setCurrentIndex(workingInstrument->noteOffMode()); noteOffModeList->blockSignals(false); // populate patch list patchView->blockSignals(true); for (int i = 0; i < patchView->topLevelItemCount(); ++i) qDeleteAll(patchView->topLevelItem(i)->takeChildren()); patchView->clear(); patchView->blockSignals(false); for (int i = 0; i < viewController->topLevelItemCount(); ++i) qDeleteAll(viewController->topLevelItem(i)->takeChildren()); viewController->clear(); instrumentName->blockSignals(true); instrumentName->setText(workingInstrument->iname()); instrumentName->blockSignals(false); sysexList->blockSignals(true); sysexList->clear(); foreach(const MusECore::SysEx* s, workingInstrument->sysex()) { if(!s) continue; QListWidgetItem* item = new QListWidgetItem(s->name); QVariant v = QVariant::fromValue((void*)s); item->setData(Qt::UserRole, v); sysexList->addItem(item); } if(sysexList->item(0)) sysexList->setCurrentItem(sysexList->item(0)); sysexList->blockSignals(false); sysexChanged(sysexList->item(0), 0); populateInitEventList(); MusECore::PatchGroupList* pg = workingInstrument->groups(); for (MusECore::ciPatchGroup g = pg->begin(); g != pg->end(); ++g) { MusECore::PatchGroup* pgp = *g; if(pgp) { QTreeWidgetItem* item = new QTreeWidgetItem(patchView); item->setText(0, pgp->name); item->setData(0, Qt::UserRole, QVariant::fromValue((void*)(pgp))); for (MusECore::ciPatch p = pgp->patches.begin(); p != pgp->patches.end(); ++p) { MusECore::Patch* patch = *p; if(patch) { QTreeWidgetItem* sitem = new QTreeWidgetItem(item); sitem->setText(0, patch->name); sitem->setData(0, Qt::UserRole, QVariant::fromValue((void*)patch)); } } } } oldPatchItem = 0; QTreeWidgetItem* fc = patchView->topLevelItem(0); if(fc) { // This may cause a patchChanged call. patchView->blockSignals(true); patchView->setCurrentItem(fc); patchView->blockSignals(false); } patchChanged(); MusECore::MidiControllerList* cl = workingInstrument->controller(); for (MusECore::ciMidiController ic = cl->begin(); ic != cl->end(); ++ic) { MusECore::MidiController* c = ic->second; addControllerToView(c); } QTreeWidgetItem *ci = viewController->topLevelItem(0); if(ci) { viewController->blockSignals(true); viewController->setCurrentItem(ci); viewController->blockSignals(false); } controllerChanged(); repopulatePatchCollections(); if (dlist) { dlist->hide(); delete dlist; dlist=NULL; } dlist_header->hide(); dlist_vscroll->hide(); rmCollBtn->setEnabled(false); copyCollBtn->setEnabled(false); patchCollectionContainer->setEnabled(false); collUpBtn->setEnabled(false); collDownBtn->setEnabled(false); } //--------------------------------------------------------- // instrumentChanged //--------------------------------------------------------- void EditInstrument::instrumentChanged() { QListWidgetItem* sel = instrumentList->currentItem(); if(!sel) { return; } MusECore::MidiInstrument* oi = 0; if(oldMidiInstrument) oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); MusECore::MidiInstrument* wip = workingInstrument; // Returns true if aborted. if(checkDirty(wip)) { // No save was chosen. Abandon changes, or delete if it is new... if(oi) { oldMidiInstrument->setText(oi->iname()); // No file path? Only a new unsaved instrument can do that. So delete it. if(oi->filePath().isEmpty()) { // Delete the list item and the instrument. deleteInstrument(oldMidiInstrument); oldMidiInstrument = 0; } } } workingInstrument->setDirty(false); changeInstrument(); } //--------------------------------------------------------- // instrumentNameReturn //--------------------------------------------------------- void EditInstrument::instrumentNameReturn() { MusECore::MidiInstrument* oi = 0; if(oldMidiInstrument) oi = (MusECore::MidiInstrument*)oldMidiInstrument->data(Qt::UserRole).value(); if(!oi) return; QString s = instrumentName->text(); if(s == oldMidiInstrument->text()) return; for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { if((*i) != oi && s == (*i)->iname()) { instrumentName->blockSignals(true); // Grab the last valid name from the item text, since the instrument has not been updated yet. instrumentName->setText(oldMidiInstrument->text()); instrumentName->blockSignals(false); QMessageBox::critical(this, tr("MusE: Bad instrument name"), tr("Please choose a unique instrument name.\n(The name might be used by a hidden instrument.)"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); return; } } oldMidiInstrument->setText(s); workingInstrument->setIName(s); workingInstrument->setDirty(true); } //--------------------------------------------------------- // deleteInstrument //--------------------------------------------------------- void EditInstrument::deleteInstrument(QListWidgetItem* item) { if(!item) return; MusECore::MidiInstrument* ins = (MusECore::MidiInstrument*)item->data(Qt::UserRole).value(); // Delete the list item. // Test this: Is this going to change the current selection? instrumentList->blockSignals(true); delete item; instrumentList->blockSignals(false); // Test this: Necessary? DELETETHIS // if(curritem) // instrumentList->setCurrentItem(curritem); if(!ins) return; // Remove the instrument from the list. MusECore::iMidiInstrument imi = MusECore::midiInstruments.find(ins); if(imi != MusECore::midiInstruments.end()) { MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(&MusECore::midiInstruments, imi, MusECore::PendingOperationItem::DeleteMidiInstrument)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } // Delete the instrument. delete ins; } //--------------------------------------------------------- // noteOffModeChanged //--------------------------------------------------------- void EditInstrument::noteOffModeChanged(int idx) { MusECore::MidiInstrument::NoteOffMode t = (MusECore::MidiInstrument::NoteOffMode)noteOffModeList->itemData(idx).toInt(); if(workingInstrument->noteOffMode() == t) return; workingInstrument->setNoteOffMode(t); workingInstrument->setDirty(true); } //--------------------------------------------------------- // tabChanged // Added so that patch list is updated when switching tabs, // so that 'Program' default values and text are current in controller tab. //--------------------------------------------------------- void EditInstrument::tabChanged(int idx) { QWidget* w = tabWidget3->widget(idx); if(!w) return; // If we're switching to the Patches tab, just ignore. if(QString(w->objectName()) == QString("patchesTab")) return; if(oldPatchItem) { // Don't bother calling patchChanged, just update the patch or group. if(oldPatchItem->QTreeWidgetItem::parent()) updatePatch(workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); else updatePatchGroup(workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); } // We're still on the same item. No need to set oldPatchItem as in patchChanged... // If we're switching to the Controller tab, update the default patch button text in case a patch changed... if(QString(w->objectName()) == QString("controllerTab")) { QTreeWidgetItem* sel = viewController->currentItem(); if(!sel || !sel->data(0, Qt::UserRole).value()) return; MusECore::MidiController* c = (MusECore::MidiController*)sel->data(0, Qt::UserRole).value(); MusECore::MidiController::ControllerType type = MusECore::midiControllerType(c->num()); // Grab the controller number from the actual values showing // and set the patch button text. if(type == MusECore::MidiController::Program) setDefaultPatchName(getDefaultPatchNumber()); } } //--------------------------------------------------------- // patchNameReturn //--------------------------------------------------------- void EditInstrument::patchNameReturn() { QTreeWidgetItem* item = patchView->currentItem(); if (item == 0) return; QString s = patchNameEdit->text(); if(item->text(0) == s) return; MusECore::PatchGroupList* pg = workingInstrument->groups(); for(MusECore::iPatchGroup g = pg->begin(); g != pg->end(); ++g) { MusECore::PatchGroup* pgp = *g; // If the item has a parent, it's a patch item. if(item->QTreeWidgetItem::parent()) { MusECore::Patch* curp = (MusECore::Patch*)item->data(0, Qt::UserRole).value(); for(MusECore::iPatch p = pgp->patches.begin(); p != pgp->patches.end(); ++p) { if((*p) != curp && (*p)->name == s) { patchNameEdit->blockSignals(true); // Grab the last valid name from the item text, since the patch has not been updated yet. patchNameEdit->setText(item->text(0)); patchNameEdit->blockSignals(false); QMessageBox::critical(this, tr("MusE: Bad patch name"), tr("Please choose a unique patch name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); return; } } } else // The item has no parent. It's a patch group item. { MusECore::PatchGroup* curpg = (MusECore::PatchGroup*)item->data(0, Qt::UserRole).value(); if(pgp != curpg && pgp->name == s) { patchNameEdit->blockSignals(true); // Grab the last valid name from the item text, since the patch group has not been updated yet. patchNameEdit->setText(item->text(0)); patchNameEdit->blockSignals(false); QMessageBox::critical(this, tr("MusE: Bad patchgroup name"), tr("Please choose a unique patchgroup name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); return; } } } item->setText(0, s); workingInstrument->setDirty(true); } //--------------------------------------------------------- // patchChanged //--------------------------------------------------------- void EditInstrument::patchChanged() { if(oldPatchItem) { if(oldPatchItem->parent()) updatePatch(workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); else updatePatchGroup(workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); } QTreeWidgetItem* sel = patchView->selectedItems().size() ? patchView->selectedItems()[0] : 0; oldPatchItem = sel; if(!sel || !sel->data(0, Qt::UserRole).value()) { patchNameEdit->setText(""); spinBoxHBank->setEnabled(false); spinBoxLBank->setEnabled(false); spinBoxProgram->setEnabled(false); showPatchInMidiButton->setEnabled(false); showPatchInDrumsButton->setEnabled(false); return; } // If the item has a parent, it's a patch item. if(sel->parent()) { MusECore::Patch* p = (MusECore::Patch*)sel->data(0, Qt::UserRole).value(); patchNameEdit->setText(p->name); spinBoxHBank->setEnabled(true); spinBoxLBank->setEnabled(true); spinBoxProgram->setEnabled(true); showPatchInMidiButton->setEnabled(true); showPatchInDrumsButton->setEnabled(true); int hb = ((p->hbank + 1) & 0xff); int lb = ((p->lbank + 1) & 0xff); int pr = ((p->program + 1) & 0xff); spinBoxHBank->setValue(hb); spinBoxLBank->setValue(lb); spinBoxProgram->setValue(pr); showPatchInMidiButton->setChecked(!p->drum); showPatchInDrumsButton->setChecked(p->drum); } else // The item is a patch group item. { patchNameEdit->setText( ((MusECore::PatchGroup*)sel->data(0, Qt::UserRole).value())->name ); spinBoxHBank->setEnabled(false); spinBoxLBank->setEnabled(false); spinBoxProgram->setEnabled(false); showPatchInMidiButton->setEnabled(false); showPatchInDrumsButton->setEnabled(false); } } //--------------------------------------------------------- // defPatchChanged //--------------------------------------------------------- void EditInstrument::defPatchChanged(int) { QTreeWidgetItem* item = viewController->currentItem(); if (!item) return; MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); int val = getDefaultPatchNumber(); c->setInitVal(val); setDefaultPatchName(val); item->setText(COL_DEF, getPatchItemText(val)); workingInstrument->setDirty(true); } //--------------------------------------------------------- // defDrumPatchChanged //--------------------------------------------------------- void EditInstrument::defDrumPatchChanged(int) { QTreeWidgetItem* item = viewController->currentItem(); if (!item) return; MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); int val = getDefaultDrumPatchNumber(); c->setDrumInitVal(val); setDefaultDrumPatchName(val); item->setText(COL_DRUM_DEF, getPatchItemText(val)); workingInstrument->setDirty(true); } //--------------------------------------------------------- // patchButtonClicked //--------------------------------------------------------- void EditInstrument::patchButtonClicked() { popupControllerDefaultPatchList(false); } //--------------------------------------------------------- // drumPatchButtonClicked //--------------------------------------------------------- void EditInstrument::drumPatchButtonClicked() { popupControllerDefaultPatchList(true); } //--------------------------------------------------------- // drummapCollectionPatchButtonClicked //--------------------------------------------------------- void EditInstrument::drummapCollectionPatchButtonClicked() { popupDrummapPatchList(); } //--------------------------------------------------------- // addControllerToView //--------------------------------------------------------- QTreeWidgetItem* EditInstrument::addControllerToView(MusECore::MidiController* mctrl) { QString hnum; QString lnum; QString min; QString max; QString def; QString drumdef; int defval = mctrl->initVal(); int drumdefval = mctrl->drumInitVal(); int n = mctrl->num(); int h = (n >> 8) & 0x7f; int l = n & 0x7f; if((n & 0xff) == 0xff) l = -1; MusECore::MidiController::ControllerType t = MusECore::midiControllerType(n); switch(t) { case MusECore::MidiController::Controller7: hnum = "---"; if(l == -1) lnum = "*"; else lnum.setNum(l); min.setNum(mctrl->minVal()); max.setNum(mctrl->maxVal()); if(defval == MusECore::CTRL_VAL_UNKNOWN) def = "---"; else def.setNum(defval); if(drumdefval == MusECore::CTRL_VAL_UNKNOWN) drumdef = "---"; else drumdef.setNum(drumdefval); break; case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: case MusECore::MidiController::Controller14: hnum.setNum(h); if(l == -1) lnum = "*"; else lnum.setNum(l); min.setNum(mctrl->minVal()); max.setNum(mctrl->maxVal()); if(defval == MusECore::CTRL_VAL_UNKNOWN) def = "---"; else def.setNum(defval); if(drumdefval == MusECore::CTRL_VAL_UNKNOWN) drumdef = "---"; else drumdef.setNum(drumdefval); break; case MusECore::MidiController::Pitch: case MusECore::MidiController::PolyAftertouch: case MusECore::MidiController::Aftertouch: hnum = "---"; lnum = "---"; min.setNum(mctrl->minVal()); max.setNum(mctrl->maxVal()); if(defval == MusECore::CTRL_VAL_UNKNOWN) def = "---"; else def.setNum(defval); if(drumdefval == MusECore::CTRL_VAL_UNKNOWN) drumdef = "---"; else drumdef.setNum(drumdefval); break; case MusECore::MidiController::Program: hnum = "---"; lnum = "---"; min = "---"; max = "---"; def = getPatchItemText(defval); drumdef = getPatchItemText(defval); break; default: hnum = "---"; lnum = "---"; min = "---"; max = "---"; def = "---"; drumdef = "---"; break; } QString show_midi, show_drum; if(mctrl->showInTracks() & MusECore::MidiController::ShowInMidi) show_midi = "X"; if(mctrl->showInTracks() & MusECore::MidiController::ShowInDrum) show_drum = "X"; QTreeWidgetItem* ci = new QTreeWidgetItem(viewController, QStringList() << mctrl->name() << int2ctrlType(t) << hnum << lnum << min << max << def << drumdef << show_midi << show_drum); ci->setTextAlignment(0, Qt::AlignLeft | Qt::AlignVCenter); ci->setTextAlignment(1, Qt::AlignLeft | Qt::AlignVCenter); for(int i = 2; i < 10; ++i) ci->setTextAlignment(i, Qt::AlignRight | Qt::AlignVCenter); ci->setData(0, Qt::UserRole, QVariant::fromValue((void*)(mctrl))); return ci; } //--------------------------------------------------------- // controllerChanged //--------------------------------------------------------- void EditInstrument::controllerChanged() { QTreeWidgetItem* sel = viewController->selectedItems().size() ? viewController->selectedItems()[0] : 0; if(!sel || !sel->data(0, Qt::UserRole).value()) { ctrlName->blockSignals(true); ctrlName->setText(""); ctrlName->blockSignals(false); return; } MusECore::MidiController* c = (MusECore::MidiController*)sel->data(0, Qt::UserRole).value(); ctrlName->blockSignals(true); ctrlName->setText(c->name()); ctrlName->blockSignals(false); int ctrlH = (c->num() >> 8) & 0x7f; int ctrlL = c->num() & 0x7f; if(c->isPerNoteController()) ctrlL = -1; MusECore::MidiController::ControllerType type = MusECore::midiControllerType(c->num()); int idx = ctrlType->findData(type); if(idx != -1) { ctrlType->blockSignals(true); ctrlType->setCurrentIndex(idx); ctrlType->blockSignals(false); } ctrlShowInMidi->setChecked(c->showInTracks() & MusECore::MidiController::ShowInMidi); ctrlShowInDrum->setChecked(c->showInTracks() & MusECore::MidiController::ShowInDrum); spinBoxHCtrlNo->blockSignals(true); spinBoxLCtrlNo->blockSignals(true); spinBoxMin->blockSignals(true); spinBoxMax->blockSignals(true); spinBoxDefault->blockSignals(true); spinBoxDrumDefault->blockSignals(true); switch (type) { case MusECore::MidiController::Controller7: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(true); spinBoxHCtrlNo->setValue(0); spinBoxLCtrlNo->setValue(ctrlL); spinBoxMin->setEnabled(true); spinBoxMax->setEnabled(true); spinBoxMin->setRange(-128, 127); spinBoxMax->setRange(-128, 127); spinBoxMin->setValue(c->minVal()); spinBoxMax->setValue(c->maxVal()); enableDefaultControls(true, false); break; case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: spinBoxHCtrlNo->setEnabled(true); spinBoxLCtrlNo->setEnabled(true); spinBoxHCtrlNo->setValue(ctrlH); spinBoxLCtrlNo->setValue(ctrlL); spinBoxMin->setEnabled(true); spinBoxMax->setEnabled(true); spinBoxMin->setRange(-128, 127); spinBoxMax->setRange(-128, 127); spinBoxMin->setValue(c->minVal()); spinBoxMax->setValue(c->maxVal()); enableDefaultControls(true, false); break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: spinBoxHCtrlNo->setEnabled(true); spinBoxLCtrlNo->setEnabled(true); spinBoxHCtrlNo->setValue(ctrlH); spinBoxLCtrlNo->setValue(ctrlL); spinBoxMin->setEnabled(true); spinBoxMax->setEnabled(true); spinBoxMin->setRange(-16384, 16383); spinBoxMax->setRange(-16384, 16383); spinBoxMin->setValue(c->minVal()); spinBoxMax->setValue(c->maxVal()); enableDefaultControls(true, false); break; case MusECore::MidiController::Pitch: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(false); spinBoxHCtrlNo->setValue(0); spinBoxLCtrlNo->setValue(0); spinBoxMin->setEnabled(true); spinBoxMax->setEnabled(true); spinBoxMin->setRange(-8192, 8191); spinBoxMax->setRange(-8192, 8191); spinBoxMin->setValue(c->minVal()); spinBoxMax->setValue(c->maxVal()); enableDefaultControls(true, false); break; case MusECore::MidiController::PolyAftertouch: case MusECore::MidiController::Aftertouch: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(false); spinBoxHCtrlNo->setValue(0); spinBoxLCtrlNo->setValue(0); spinBoxMin->setEnabled(true); spinBoxMax->setEnabled(true); spinBoxMin->setRange(0, 127); spinBoxMax->setRange(0, 127); spinBoxMin->setValue(c->minVal()); spinBoxMax->setValue(c->maxVal()); enableDefaultControls(true, false); break; case MusECore::MidiController::Program: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(false); spinBoxHCtrlNo->setValue(0); spinBoxLCtrlNo->setValue(0); spinBoxMin->setEnabled(false); spinBoxMax->setEnabled(false); spinBoxMin->setRange(0, 0); spinBoxMax->setRange(0, 0); spinBoxMin->setValue(0); spinBoxMax->setValue(0); enableDefaultControls(false, true); break; default: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(false); spinBoxMin->setEnabled(false); spinBoxMax->setEnabled(false); enableDefaultControls(false, false); break; } if(type == MusECore::MidiController::Program) { spinBoxDefault->setRange(0, 0); spinBoxDefault->setValue(0); setDefaultPatchControls(c->initVal()); spinBoxDrumDefault->setRange(0, 0); spinBoxDrumDefault->setValue(0); setDefaultDrumPatchControls(c->drumInitVal()); } else { spinBoxDefault->setRange(c->minVal() - 1, c->maxVal()); if(c->initVal() == MusECore::CTRL_VAL_UNKNOWN) spinBoxDefault->setValue(spinBoxDefault->minimum()); else spinBoxDefault->setValue(c->initVal()); spinBoxDrumDefault->setRange(c->minVal() - 1, c->maxVal()); if(c->drumInitVal() == MusECore::CTRL_VAL_UNKNOWN) spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); else spinBoxDrumDefault->setValue(c->drumInitVal()); } spinBoxHCtrlNo->blockSignals(false); spinBoxLCtrlNo->blockSignals(false); spinBoxMin->blockSignals(false); spinBoxMax->blockSignals(false); spinBoxDefault->blockSignals(false); spinBoxDrumDefault->blockSignals(false); ctrlValidLabel->setPixmap(ledGreenSVGIcon->pixmap(ledSize)); enableNonCtrlControls(true); } //--------------------------------------------------------- // ctrlNameReturn //--------------------------------------------------------- void EditInstrument::ctrlNameReturn() { QTreeWidgetItem* item = viewController->currentItem(); if (item == 0) return; MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); QString cName = ctrlName->text(); MusECore::MidiControllerList* cl = workingInstrument->controller(); for(MusECore::ciMidiController ic = cl->begin(); ic != cl->end(); ++ic) { MusECore::MidiController* mc = ic->second; if(mc != c && mc->name() == cName) { ctrlName->blockSignals(true); ctrlName->setText(c->name()); ctrlName->blockSignals(false); QMessageBox::critical(this, tr("MusE: Bad controller name"), tr("Please choose a unique controller name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); return; } } if(c->name() == cName) return; c->setName(ctrlName->text()); item->setText(COL_CNAME, ctrlName->text()); workingInstrument->setDirty(true); } //--------------------------------------------------------- // ctrlTypeChanged //--------------------------------------------------------- void EditInstrument::ctrlTypeChanged(int idx) { QTreeWidgetItem* item = viewController->currentItem(); if (item == 0) return; MusECore::MidiController::ControllerType t = (MusECore::MidiController::ControllerType)ctrlType->itemData(idx).toInt(); MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); int hnum = 0, lnum = 0; switch (t) { case MusECore::MidiController::Controller7: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(true); lnum = spinBoxLCtrlNo->value(); break; case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: spinBoxHCtrlNo->setEnabled(true); spinBoxLCtrlNo->setEnabled(true); hnum = spinBoxHCtrlNo->value(); lnum = spinBoxLCtrlNo->value(); break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: spinBoxHCtrlNo->setEnabled(true); spinBoxLCtrlNo->setEnabled(true); hnum = spinBoxHCtrlNo->value(); lnum = spinBoxLCtrlNo->value(); break; case MusECore::MidiController::Pitch: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(false); break; case MusECore::MidiController::PolyAftertouch: case MusECore::MidiController::Aftertouch: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(false); break; case MusECore::MidiController::Program: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(false); break; // Shouldn't happen... default: spinBoxHCtrlNo->setEnabled(false); spinBoxLCtrlNo->setEnabled(false); spinBoxMin->setEnabled(false); spinBoxMax->setEnabled(false); enableDefaultControls(false, false); spinBoxMin->blockSignals(false); spinBoxMax->blockSignals(false); return; break; } int new_num = MusECore::MidiController::genNum(t, hnum, lnum); MusECore::MidiControllerList* cl = workingInstrument->controller(); // Check if either a per-note controller, or else a regular controller already exists. if(!cl->ctrlAvailable(new_num, c)) { ctrlValidLabel->setPixmap(ledRedSVGIcon->pixmap(ledSize)); enableNonCtrlControls(false); return; } ctrlValidLabel->setPixmap(ledGreenSVGIcon->pixmap(ledSize)); if(t == MusECore::midiControllerType(c->num())) { enableNonCtrlControls(true); return; } cl->del(c->num()); c->setNum(new_num); cl->add(c); enableNonCtrlControls(true); item->setText(COL_TYPE, ctrlType->currentText()); spinBoxMin->blockSignals(true); spinBoxMax->blockSignals(true); spinBoxDefault->blockSignals(true); spinBoxDrumDefault->blockSignals(true); switch (t) { case MusECore::MidiController::Controller7: spinBoxMin->setRange(-128, 127); spinBoxMax->setRange(-128, 127); spinBoxMin->setValue(0); spinBoxMax->setValue(127); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDefault->setValue(spinBoxDefault->minimum()); spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); if(lnum == -1) item->setText(COL_LNUM, QString("*")); else item->setText(COL_LNUM, QString().setNum(lnum)); item->setText(COL_HNUM, QString("---")); item->setText(COL_MIN, QString().setNum(spinBoxMin->value())); item->setText(COL_MAX, QString().setNum(spinBoxMax->value())); item->setText(COL_DEF, QString("---")); item->setText(COL_DRUM_DEF, QString("---")); break; case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: spinBoxMin->setRange(-128, 127); spinBoxMax->setRange(-128, 127); spinBoxMin->setValue(0); spinBoxMax->setValue(127); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDefault->setValue(spinBoxDefault->minimum()); spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); if(lnum == -1) item->setText(COL_LNUM, QString("*")); else item->setText(COL_LNUM, QString().setNum(lnum)); item->setText(COL_HNUM, QString().setNum(hnum)); item->setText(COL_MIN, QString().setNum(spinBoxMin->value())); item->setText(COL_MAX, QString().setNum(spinBoxMax->value())); item->setText(COL_DEF, QString("---")); item->setText(COL_DRUM_DEF, QString("---")); break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: spinBoxMin->setRange(-16384, 16383); spinBoxMax->setRange(-16384, 16383); spinBoxMin->setValue(0); spinBoxMax->setValue(16383); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDefault->setValue(spinBoxDefault->minimum()); spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); if(lnum == -1) item->setText(COL_LNUM, QString("*")); else item->setText(COL_LNUM, QString().setNum(lnum)); item->setText(COL_HNUM, QString().setNum(hnum)); item->setText(COL_MIN, QString().setNum(spinBoxMin->value())); item->setText(COL_MAX, QString().setNum(spinBoxMax->value())); item->setText(COL_DEF, QString("---")); item->setText(COL_DRUM_DEF, QString("---")); break; case MusECore::MidiController::Pitch: spinBoxMin->setRange(-8192, 8191); spinBoxMax->setRange(-8192, 8191); spinBoxMin->setValue(-8192); spinBoxMax->setValue(8191); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDefault->setValue(spinBoxDefault->minimum()); spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); item->setText(COL_LNUM, QString("---")); item->setText(COL_HNUM, QString("---")); item->setText(COL_MIN, QString().setNum(spinBoxMin->value())); item->setText(COL_MAX, QString().setNum(spinBoxMax->value())); item->setText(COL_DEF, QString("---")); item->setText(COL_DRUM_DEF, QString("---")); break; case MusECore::MidiController::PolyAftertouch: case MusECore::MidiController::Aftertouch: spinBoxMin->setRange(0, 127); spinBoxMax->setRange(0, 127); spinBoxMin->setValue(0); spinBoxMax->setValue(127); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDefault->setValue(spinBoxDefault->minimum()); spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); item->setText(COL_LNUM, QString("---")); item->setText(COL_HNUM, QString("---")); item->setText(COL_MIN, QString().setNum(spinBoxMin->value())); item->setText(COL_MAX, QString().setNum(spinBoxMax->value())); item->setText(COL_DEF, QString("---")); item->setText(COL_DRUM_DEF, QString("---")); break; case MusECore::MidiController::Program: spinBoxMin->setRange(0, 0); spinBoxMax->setRange(0, 0); spinBoxMin->setValue(0); spinBoxMax->setValue(0); spinBoxDefault->setRange(0, 0); spinBoxDefault->setValue(0); spinBoxDrumDefault->setRange(0, 0); spinBoxDrumDefault->setValue(0); item->setText(COL_LNUM, QString("---")); item->setText(COL_HNUM, QString("---")); item->setText(COL_MIN, QString("---")); item->setText(COL_MAX, QString("---")); item->setText(COL_DEF, QString("---")); item->setText(COL_DRUM_DEF, QString("---")); break; // Shouldn't happen... default: return; break; } spinBoxMin->blockSignals(false); spinBoxMax->blockSignals(false); spinBoxDefault->blockSignals(false); spinBoxDrumDefault->blockSignals(false); setDefaultPatchControls(0xffffff); setDefaultDrumPatchControls(0xffffff); if(t == MusECore::MidiController::Program) { c->setMinVal(0); c->setMaxVal(0xffffff); c->setInitVal(0xffffff); c->setDrumInitVal(0xffffff); } else { c->setMinVal(spinBoxMin->value()); c->setMaxVal(spinBoxMax->value()); if(spinBoxDefault->value() == spinBoxDefault->minimum()) c->setInitVal(MusECore::CTRL_VAL_UNKNOWN); else c->setInitVal(spinBoxDefault->value()); if(spinBoxDrumDefault->value() == spinBoxDrumDefault->minimum()) c->setDrumInitVal(MusECore::CTRL_VAL_UNKNOWN); else c->setDrumInitVal(spinBoxDrumDefault->value()); } workingInstrument->setDirty(true); } //--------------------------------------------------------- // ctrlShowInMidiChanged //--------------------------------------------------------- void EditInstrument::ctrlShowInMidiChanged(int state) { QTreeWidgetItem* item = viewController->currentItem(); if (item == 0) return; MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); int show = c->showInTracks(); if((show & MusECore::MidiController::ShowInMidi) == (state == Qt::Checked)) return; if(state == Qt::Checked) { c->setShowInTracks(show | MusECore::MidiController::ShowInMidi); item->setText(COL_SHOW_MIDI, "X"); } else { c->setShowInTracks(show & ~MusECore::MidiController::ShowInMidi); item->setText(COL_SHOW_MIDI, ""); } workingInstrument->setDirty(true); } //--------------------------------------------------------- // ctrlShowInMidiChanged //--------------------------------------------------------- void EditInstrument::ctrlShowInDrumChanged(int state) { QTreeWidgetItem* item = viewController->currentItem(); if (item == 0) return; MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); int show = c->showInTracks(); if((show & MusECore::MidiController::ShowInDrum) == (state == Qt::Checked)) return; if(state == Qt::Checked) { c->setShowInTracks(show | MusECore::MidiController::ShowInDrum); item->setText(COL_SHOW_DRUM, "X"); } else { c->setShowInTracks(show & ~MusECore::MidiController::ShowInDrum); item->setText(COL_SHOW_DRUM, ""); } workingInstrument->setDirty(true); } //--------------------------------------------------------- // ctrlNumChanged //--------------------------------------------------------- void EditInstrument::ctrlNumChanged() { QTreeWidgetItem* item = viewController->currentItem(); if (item == 0 || ctrlType->currentIndex() == -1) return; MusECore::MidiController::ControllerType t = (MusECore::MidiController::ControllerType)ctrlType->itemData(ctrlType->currentIndex()).toInt(); int hnum = 0, lnum = 0; switch (t) { case MusECore::MidiController::Controller7: lnum = spinBoxLCtrlNo->value(); break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: hnum = spinBoxHCtrlNo->value(); lnum = spinBoxLCtrlNo->value(); break; // Should not happen... case MusECore::MidiController::Pitch: case MusECore::MidiController::PolyAftertouch: case MusECore::MidiController::Aftertouch: case MusECore::MidiController::Program: case MusECore::MidiController::Velo: return; default: printf("EditInstrument::ctrlNumChanged Error: Unknown control type\n"); return; break; } int new_num = MusECore::MidiController::genNum(t, hnum, lnum); if(new_num == -1) { printf("EditInstrument::ctrlNumChanged Error: genNum returned -1\n"); return; } MusECore::MidiControllerList* cl = workingInstrument->controller(); MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); // Check if either a per-note controller, or else a regular controller already exists. if(!cl->ctrlAvailable(new_num, c)) { ctrlValidLabel->setPixmap(ledRedSVGIcon->pixmap(ledSize)); enableNonCtrlControls(false); return; } ctrlValidLabel->setPixmap(ledGreenSVGIcon->pixmap(ledSize)); enableNonCtrlControls(true); if(cl->del(c->num()) == 0) printf("EditInstrument::ctrlNumChanged Warning: Erase failed! Proceeding anyway.\n"); c->setNum(new_num); cl->add(c); QString s; if(c->isPerNoteController()) item->setText(COL_LNUM, QString("*")); else { s.setNum(lnum); item->setText(COL_LNUM, s); } switch (t) { case MusECore::MidiController::Controller7: item->setText(COL_HNUM, "---"); break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: s.setNum(hnum); item->setText(COL_HNUM, s); break; default: return; } item->setText(COL_TYPE, ctrlType->currentText()); workingInstrument->setDirty(true); } //--------------------------------------------------------- // ctrlMinChanged //--------------------------------------------------------- void EditInstrument::ctrlMinChanged(int val) { QTreeWidgetItem* item = viewController->currentItem(); if (item == 0) return; QString s; s.setNum(val); item->setText(COL_MIN, s); MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); c->setMinVal(val); int rng = 0; switch(MusECore::midiControllerType(c->num())) { case MusECore::MidiController::Controller7: case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: case MusECore::MidiController::PolyAftertouch: case MusECore::MidiController::Aftertouch: rng = 127; break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: case MusECore::MidiController::Pitch: rng = 16383; break; default: break; } int mx = c->maxVal(); if(val > mx) { c->setMaxVal(val); spinBoxMax->blockSignals(true); spinBoxMax->setValue(val); spinBoxMax->blockSignals(false); item->setText(COL_MAX, s); } else if(mx - val > rng) { mx = val + rng; c->setMaxVal(mx); spinBoxMax->blockSignals(true); spinBoxMax->setValue(mx); spinBoxMax->blockSignals(false); item->setText(COL_MAX, QString().setNum(mx)); } spinBoxDefault->blockSignals(true); spinBoxDrumDefault->blockSignals(true); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); spinBoxDrumDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); int inval = c->initVal(); if(inval == MusECore::CTRL_VAL_UNKNOWN) spinBoxDefault->setValue(spinBoxDefault->minimum()); else { if(inval < c->minVal()) { c->setInitVal(c->minVal()); spinBoxDefault->setValue(c->minVal()); } else if(inval > c->maxVal()) { c->setInitVal(c->maxVal()); spinBoxDefault->setValue(c->maxVal()); } } inval = c->drumInitVal(); if(inval == MusECore::CTRL_VAL_UNKNOWN) spinBoxDrumDefault->setValue(spinBoxDrumDefault->minimum()); else { if(inval < c->minVal()) { c->setDrumInitVal(c->minVal()); spinBoxDrumDefault->setValue(c->minVal()); } else if(inval > c->maxVal()) { c->setDrumInitVal(c->maxVal()); spinBoxDrumDefault->setValue(c->maxVal()); } } spinBoxDefault->blockSignals(false); spinBoxDrumDefault->blockSignals(false); workingInstrument->setDirty(true); } //--------------------------------------------------------- // ctrlMaxChanged //--------------------------------------------------------- void EditInstrument::ctrlMaxChanged(int val) { QTreeWidgetItem* item = viewController->currentItem(); if (item == 0) return; QString s; s.setNum(val); item->setText(COL_MAX, s); MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); c->setMaxVal(val); int rng = 0; switch(MusECore::midiControllerType(c->num())) { case MusECore::MidiController::Controller7: case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: case MusECore::MidiController::PolyAftertouch: case MusECore::MidiController::Aftertouch: rng = 127; break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: case MusECore::MidiController::Pitch: rng = 16383; break; default: break; } int mn = c->minVal(); if(val < mn) { c->setMinVal(val); spinBoxMin->blockSignals(true); spinBoxMin->setValue(val); spinBoxMin->blockSignals(false); item->setText(COL_MIN, s); } else if(val - mn > rng) { mn = val - rng; c->setMinVal(mn); spinBoxMin->blockSignals(true); spinBoxMin->setValue(mn); spinBoxMin->blockSignals(false); item->setText(COL_MIN, QString().setNum(mn)); } spinBoxDefault->blockSignals(true); spinBoxDefault->setRange(spinBoxMin->value() - 1, spinBoxMax->value()); int inval = c->initVal(); if(inval == MusECore::CTRL_VAL_UNKNOWN) spinBoxDefault->setValue(spinBoxDefault->minimum()); else { if(inval < c->minVal()) { c->setInitVal(c->minVal()); spinBoxDefault->setValue(c->minVal()); } else if(inval > c->maxVal()) { c->setInitVal(c->maxVal()); spinBoxDefault->setValue(c->maxVal()); } } spinBoxDefault->blockSignals(false); workingInstrument->setDirty(true); } //--------------------------------------------------------- // ctrlDefaultChanged //--------------------------------------------------------- void EditInstrument::ctrlDefaultChanged(int val) { QTreeWidgetItem* item = viewController->currentItem(); if (item == 0) return; MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); if(val == c->minVal() - 1) { c->setInitVal(MusECore::CTRL_VAL_UNKNOWN); item->setText(COL_DEF, QString("---")); } else { c->setInitVal(val); item->setText(COL_DEF, QString().setNum(val)); } workingInstrument->setDirty(true); } //--------------------------------------------------------- // ctrlDrumDefaultChanged //--------------------------------------------------------- void EditInstrument::ctrlDrumDefaultChanged(int val) { QTreeWidgetItem* item = viewController->currentItem(); if (item == 0) return; MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); if(val == c->minVal() - 1) { c->setDrumInitVal(MusECore::CTRL_VAL_UNKNOWN); item->setText(COL_DRUM_DEF, QString("---")); } else { c->setDrumInitVal(val); item->setText(COL_DRUM_DEF, QString().setNum(val)); } workingInstrument->setDirty(true); } //--------------------------------------------------------- // updateSysex //--------------------------------------------------------- void EditInstrument::updateSysex(MusECore::MidiInstrument* instrument, MusECore::SysEx* so) { if (sysexName->text() != so->name) { so->name = sysexName->text(); instrument->setDirty(true); } if (sysexComment->toPlainText() != so->comment) { so->comment = sysexComment->toPlainText(); instrument->setDirty(true); } unsigned char* data; int len = MusECore::string2sysex(sysexData->toPlainText(), &data); if (len == -1) // Conversion unsuccessful? { QMessageBox::information(0, QString("MusE"), QWidget::tr("Cannot convert sysex string")); return; } if(so->dataLen != len || memcmp(data, so->data, len) != 0) { if(so->dataLen != 0 && so->data) delete[] so->data; so->data = data; so->dataLen = len; instrument->setDirty(true); } } //--------------------------------------------------------- // sysexChanged //--------------------------------------------------------- void EditInstrument::sysexChanged(QListWidgetItem* sel, QListWidgetItem* old) { if (old) { MusECore::SysEx* so = (MusECore::SysEx*)old->data(Qt::UserRole).value(); updateSysex(workingInstrument, so); } if (sel == 0) { sysexName->setText(""); sysexComment->setText(""); sysexData->setText(""); sysexName->setEnabled(false); sysexComment->setEnabled(false); sysexData->setEnabled(false); return; } sysexName->setEnabled(true); sysexComment->setEnabled(true); sysexData->setEnabled(true); MusECore::SysEx* sx = (MusECore::SysEx*)sel->data(Qt::UserRole).value(); sysexName->setText(sx->name); sysexComment->setText(sx->comment); sysexData->setText(MusECore::sysex2string(sx->dataLen, sx->data)); } //--------------------------------------------------------- // deleteSysexClicked //--------------------------------------------------------- void EditInstrument::deleteSysexClicked() { QListWidgetItem* item2 = sysexList->currentItem(); if(item2 == 0) return; MusECore::SysEx* sysex = (MusECore::SysEx*)item2->data(Qt::UserRole).value(); workingInstrument->removeSysex(sysex); delete item2; workingInstrument->setDirty(true); } //--------------------------------------------------------- // newSysexClicked //--------------------------------------------------------- void EditInstrument::newSysexClicked() { QString sysexName; for (int i = 1;; ++i) { sysexName = QString("Sysex-%1").arg(i); bool found = false; foreach(const MusECore::SysEx* s, workingInstrument->sysex()) { if (s->name == sysexName) { found = true; break; } } if (!found) break; } MusECore::SysEx* nsysex = new MusECore::SysEx; nsysex->name = sysexName; workingInstrument->addSysex(nsysex); QListWidgetItem* item = new QListWidgetItem(sysexName); QVariant v = QVariant::fromValue((void*)nsysex); item->setData(Qt::UserRole, v); sysexList->addItem(item); sysexList->setCurrentItem(item); workingInstrument->setDirty(true); } //--------------------------------------------------------- // deletePatchClicked //--------------------------------------------------------- void EditInstrument::deletePatchClicked() { QTreeWidgetItem* pi = patchView->currentItem(); if (pi == 0) return; // If the item has a parent item, it's a patch item... if(pi->parent()) { MusECore::PatchGroup* group = (MusECore::PatchGroup*)(pi->parent())->data(0, Qt::UserRole).value(); // If there is an allocated patch in the data, delete it. MusECore::Patch* patch = (MusECore::Patch*)pi->data(0, Qt::UserRole).value(); if(patch) { if(group) group->patches.remove(patch); delete patch; } } else // The item has no parent item, it's a patch group item... { // Is there an allocated patch group in the data? MusECore::PatchGroup* group = (MusECore::PatchGroup*)pi->data(0, Qt::UserRole).value(); if(group) { MusECore::PatchGroupList* pg = workingInstrument->groups(); for(MusECore::iPatchGroup ipg = pg->begin(); ipg != pg->end(); ++ipg) { if(*ipg == group) { pg->erase(ipg); break; } } const MusECore::PatchList& pl = group->patches; for(MusECore::ciPatch ip = pl.begin(); ip != pl.end(); ++ip) { // Delete the patch. if(*ip) delete *ip; } // Now delete the group. delete group; } } // Now delete the patch or group item (and any child patch items) from the list view tree. // !!! This will trigger a patchChanged call. patchView->blockSignals(true); delete pi; if(patchView->currentItem()) patchView->currentItem()->setSelected(true); patchView->blockSignals(false); oldPatchItem = 0; patchChanged(); workingInstrument->setDirty(true); } //--------------------------------------------------------- // newPatchClicked //--------------------------------------------------------- void EditInstrument::newPatchClicked() { if(oldPatchItem) { if(oldPatchItem->parent()) updatePatch(workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); else updatePatchGroup(workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); } MusECore::PatchGroupList* pg = workingInstrument->groups(); QString patchName; for (int i = 1;; ++i) { patchName = QString("Patch-%1").arg(i); bool found = false; for (MusECore::iPatchGroup g = pg->begin(); g != pg->end(); ++g) { MusECore::PatchGroup* pgp = *g; for (MusECore::iPatch p = pgp->patches.begin(); p != pgp->patches.end(); ++p) { if ((*p)->name == patchName) { found = true; break; } } if (found) break; } if (!found) break; } // search current patch group QTreeWidgetItem* pi = patchView->currentItem(); if (pi == 0) return; MusECore::Patch* selpatch = 0; // If there is a parent item then pi is a patch item, and there must be a parent patch group item. if(pi->parent()) { // Remember the current selected patch. selpatch = (MusECore::Patch*)pi->data(0, Qt::UserRole).value(); pi = pi->parent(); } MusECore::PatchGroup* group = (MusECore::PatchGroup*)pi->data(0, Qt::UserRole).value(); if(!group) return; // Create a new Patch, then store its pointer in a new patch item, // to be added later to the patch group only upon save... //Patch patch; //patch.name = patchName; MusECore::Patch* patch = new MusECore::Patch; int hb = -1; int lb = -1; int prg = 0; patch->hbank = hb; patch->lbank = lb; patch->program = prg; //patch->typ = -1; patch->drum = false; if(selpatch) { hb = selpatch->hbank; lb = selpatch->lbank; prg = selpatch->program; //patch->typ = selpatch->typ; patch->drum = selpatch->drum; } bool found = false; // The 129 is to accommodate -1 values. Yes, it may cause one extra redundant loop but hey, // if it hasn't found an available patch number by then, another loop won't matter. for(int k = 0; k < 129; ++k) { for(int j = 0; j < 129; ++j) { for(int i = 0; i < 128; ++i) { found = false; for(MusECore::iPatchGroup g = pg->begin(); g != pg->end(); ++g) { MusECore::PatchGroup* pgp = *g; for(MusECore::iPatch ip = pgp->patches.begin(); ip != pgp->patches.end(); ++ip) { MusECore::Patch* p = *ip; if((p->program == ((prg + i) & 0x7f)) && ((p->lbank == -1 && lb == -1) || (p->lbank == ((lb + j) & 0x7f))) && ((p->hbank == -1 && hb == -1) || (p->hbank == ((hb + k) & 0x7f)))) { found = true; break; } } if(found) break; } if(!found) { patch->program = (prg + i) & 0x7f; if(lb == -1) patch->lbank = -1; else patch->lbank = (lb + j) & 0x7f; if(hb == -1) patch->hbank = -1; else patch->hbank = (hb + k) & 0x7f; break; } } if(!found) break; } if(!found) break; } patch->name = patchName; group->patches.push_back(patch); QTreeWidgetItem* sitem = new QTreeWidgetItem(pi); sitem->setText(0, patchName); patchNameEdit->setText(patchName); sitem->setData(0, Qt::UserRole, QVariant::fromValue((void*)(patch))); // May cause patchChanged call. patchView->blockSignals(true); patchView->setCurrentItem(sitem); patchView->scrollToItem(sitem, QAbstractItemView::EnsureVisible); patchView->blockSignals(false); spinBoxHBank->setEnabled(true); spinBoxLBank->setEnabled(true); spinBoxProgram->setEnabled(true); showPatchInMidiButton->setEnabled(true); showPatchInDrumsButton->setEnabled(true); oldPatchItem = 0; patchChanged(); workingInstrument->setDirty(true); } //--------------------------------------------------------- // newGroupClicked //--------------------------------------------------------- void EditInstrument::newGroupClicked() { if(oldPatchItem) { if(oldPatchItem->parent()) updatePatch(workingInstrument, (MusECore::Patch*)oldPatchItem->data(0, Qt::UserRole).value()); else updatePatchGroup(workingInstrument, (MusECore::PatchGroup*)oldPatchItem->data(0, Qt::UserRole).value()); } MusECore::PatchGroupList* pg = workingInstrument->groups(); QString groupName; for (int i = 1;; ++i) { groupName = QString("Group-%1").arg(i); bool found = false; for (MusECore::ciPatchGroup g = pg->begin(); g != pg->end(); ++g) { if ((*g)->name == groupName) { found = true; break; } } if (!found) break; } // Create a new PatchGroup, then store its pointer in a new patch group item, // to be added later to the instrument only upon save... MusECore::PatchGroup* group = new MusECore::PatchGroup; group->name = groupName; pg->push_back(group); QTreeWidgetItem* sitem = new QTreeWidgetItem(patchView); sitem->setText(0, groupName); patchNameEdit->setText(groupName); // Set the list view item's data. sitem->setData(0, Qt::UserRole, QVariant::fromValue((void*)(group))); //sitem->setAuxData((void*)pgp); // May cause patchChanged call. patchView->blockSignals(true); patchView->setCurrentItem(sitem); patchView->blockSignals(false); oldPatchItem = sitem; spinBoxHBank->setEnabled(false); spinBoxLBank->setEnabled(false); spinBoxProgram->setEnabled(false); showPatchInMidiButton->setEnabled(false); showPatchInDrumsButton->setEnabled(false); workingInstrument->setDirty(true); } //--------------------------------------------------------- // deleteControllerClicked //--------------------------------------------------------- void EditInstrument::deleteControllerClicked() { QTreeWidgetItem* item = viewController->currentItem(); if(!item) return; MusECore::MidiController* ctrl = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); if(!ctrl) return; workingInstrument->controller()->del(ctrl->num()); // Now delete the controller. delete ctrl; // Now remove the controller item from the list. // This may cause a controllerChanged call. viewController->blockSignals(true); delete item; if(viewController->currentItem()) viewController->currentItem()->setSelected(true); viewController->blockSignals(false); controllerChanged(); workingInstrument->setDirty(true); } //--------------------------------------------------------- // newControllerClicked //--------------------------------------------------------- void EditInstrument::newControllerClicked() { QString cName; MusECore::MidiControllerList* cl = workingInstrument->controller(); for (int i = 1;; ++i) { cName = QString("Controller-%1").arg(i); bool found = false; for (MusECore::iMidiController ic = cl->begin(); ic != cl->end(); ++ic) { MusECore::MidiController* c = ic->second; if (c->name() == cName) { found = true; break; } } if (!found) break; } MusECore::MidiController* ctrl = new MusECore::MidiController(); ctrl->setNum(MusECore::CTRL_MODULATION); ctrl->setMinVal(0); ctrl->setMaxVal(127); ctrl->setInitVal(MusECore::CTRL_VAL_UNKNOWN); ctrl->setDrumInitVal(MusECore::CTRL_VAL_UNKNOWN); QTreeWidgetItem* ci = viewController->currentItem(); int l = 0; int h = 0; int hmax = 0x100; // To allow for quick multiple successive controller creation. // If there's a current controller item selected, copy initial values from it. if(ci) { MusECore::MidiController* selctl = (MusECore::MidiController*)ci->data(0, Qt::UserRole).value(); // Ignore internal controllers and wild cards. if(((selctl->num() & 0xff0000) != MusECore::CTRL_INTERNAL_OFFSET) && !selctl->isPerNoteController()) { switch(MusECore::midiControllerType(selctl->num())) { case MusECore::MidiController::Controller7: // Auto increment controller number. l = selctl->num() & 0x7f; *ctrl = *selctl; // Assign. break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: // Auto increment controller number. l = selctl->num() & 0x7f; h = selctl->num() & 0xffffff00; *ctrl = *selctl; // Assign. break; // Don't duplicate these types. case MusECore::MidiController::Pitch: case MusECore::MidiController::Program: case MusECore::MidiController::PolyAftertouch: case MusECore::MidiController::Aftertouch: case MusECore::MidiController::Velo: break; default: printf("error: newControllerClicked: Unknown control type!\n"); delete ctrl; return; } } } bool found = false; for(int k = (h & 0xffff0000); k < MusECore::CTRL_NONE_OFFSET; k += 0x10000) { // Don't copy internal controllers. if(k == MusECore::CTRL_INTERNAL_OFFSET) { found = true; continue; } if(k == 0) // We're currently within the Controller7 group, limit the hi-number loop to one go (j = 0). hmax = 0x100; else // All other relevant controllers use hi and lo numbers. hmax = 0x10000; for(int j = 0; j < hmax; j += 0x100) { for(int i = 0; i < 128; ++i) { int num = ((i + l) & 0x7f) | ((h + j) & 0x7f00) | k; found = false; // First check if there's already a per-note controller for this control number. if(cl->find(num | 0xff) != cl->end()) { found = true; break; // Next outer loop (hi-number) iteration... } // Now check if the actual control number is NOT already taken. if(cl->find(num) == cl->end()) { ctrl->setNum(num); break; } // Actual number was also taken. Keep iterating lo-number... found = true; } if(!found) break; } if(!found) break; } if(found) { QMessageBox::critical(this, tr("New controller: Error"), tr("Error! All control numbers are taken up!\nClean up the instrument!")); delete ctrl; return; } ctrl->setName(cName); workingInstrument->controller()->add(ctrl); QTreeWidgetItem* item = addControllerToView(ctrl); if(viewController->currentItem() != item) { viewController->blockSignals(true); viewController->setCurrentItem(item); viewController->blockSignals(false); controllerChanged(); } workingInstrument->setDirty(true); } //--------------------------------------------------------- // addControllerClicked //--------------------------------------------------------- void EditInstrument::addControllerClicked() { // Add Common Controls not already found in instrument: PopupMenu* pup = new PopupMenu(true); // true = enable stay open. Don't bother with parent. MusECore::MidiControllerList* cl = workingInstrument->controller(); for(int num = 0; num < 128; ++num) { // If it's not already in the parent menu... if(cl->find(num) == cl->end()) pup->addAction(MusECore::midiCtrlName(num, true))->setData(num); } connect(pup, SIGNAL(triggered(QAction*)), SLOT(ctrlPopupTriggered(QAction*))); pup->exec(mapToGlobal(QPoint(0,0))); delete pup; } //--------------------------------------------------------- // ctrlPopupTriggered //--------------------------------------------------------- void EditInstrument::ctrlPopupTriggered(QAction* act) { if(!act || (act->data().toInt() == -1)) return; int rv = act->data().toInt(); MusECore::MidiControllerList* cl = workingInstrument->controller(); if(cl->find(rv) == cl->end()) { int num = rv; // = MusECore::MidiController::genNum(MusECore::MidiController::Controller7, 0, rv); MusECore::MidiController* ctrl = new MusECore::MidiController(); ctrl->setNum(num); ctrl->setMinVal(0); ctrl->setMaxVal(127); ctrl->setInitVal(MusECore::CTRL_VAL_UNKNOWN); ctrl->setDrumInitVal(MusECore::CTRL_VAL_UNKNOWN); ctrl->setName(MusECore::midiCtrlName(num, false)); workingInstrument->controller()->add(ctrl); QTreeWidgetItem* item = addControllerToView(ctrl); if(viewController->currentItem() != item) { viewController->blockSignals(true); viewController->setCurrentItem(item); viewController->blockSignals(false); controllerChanged(); } workingInstrument->setDirty(true); } } //--------------------------------------------------------- // updatePatchGroup //--------------------------------------------------------- void EditInstrument::updatePatchGroup(MusECore::MidiInstrument* instrument, MusECore::PatchGroup* pg) { QString a = pg->name; QString b = patchNameEdit->text(); if (pg->name != patchNameEdit->text()) { pg->name = patchNameEdit->text(); instrument->setDirty(true); } } //--------------------------------------------------------- // updatePatch //--------------------------------------------------------- void EditInstrument::updatePatch(MusECore::MidiInstrument* instrument, MusECore::Patch* p) { if (p->name != patchNameEdit->text()) { p->name = patchNameEdit->text(); instrument->setDirty(true); } signed char hb = (spinBoxHBank->value() - 1) & 0xff; if (p->hbank != hb) { p->hbank = hb; instrument->setDirty(true); } signed char lb = (spinBoxLBank->value() - 1) & 0xff; if (p->lbank != lb) { p->lbank = lb; instrument->setDirty(true); } signed char pr = (spinBoxProgram->value() - 1) & 0xff; if (p->program != pr) { p->program = pr; instrument->setDirty(true); } if (p->drum != showPatchInDrumsButton->isChecked()) { // Midi and drums radio buttons are exclusive. p->drum = showPatchInDrumsButton->isChecked(); instrument->setDirty(true); } } //--------------------------------------------------------- // updateInstrument //--------------------------------------------------------- void EditInstrument::updateInstrument(MusECore::MidiInstrument* instrument) { QListWidgetItem* sysexItem = sysexList->currentItem(); if (sysexItem) { MusECore::SysEx* so = (MusECore::SysEx*)sysexItem->data(Qt::UserRole).value(); updateSysex(instrument, so); } QTreeWidgetItem* patchItem = patchView->currentItem(); if (patchItem) { // If the item has a parent, it's a patch item. if(patchItem->parent()) updatePatch(instrument, (MusECore::Patch*)patchItem->data(0, Qt::UserRole).value()); else updatePatchGroup(instrument, (MusECore::PatchGroup*)patchItem->data(0, Qt::UserRole).value()); } } //--------------------------------------------------------- // checkDirty // return true on Abort //--------------------------------------------------------- int EditInstrument::checkDirty(MusECore::MidiInstrument* i, bool isClose) { updateInstrument(i); if (!i->dirty()) return 0; int n; if(isClose) n = QMessageBox::warning(this, tr("MusE"), tr("The current Instrument contains unsaved data\n" "Save Current Instrument?"), tr("&Save"), tr("&Don't save"), tr("&Abort"), 0, 2); else n = QMessageBox::warning(this, tr("MusE"), tr("The current Instrument contains unsaved data\n" "Save Current Instrument?"), tr("&Save"), tr("&Don't save"), 0, 1); if (n == 0) { if (i->filePath().isEmpty()) { saveAs(); } else { FILE* f = fopen(i->filePath().toLatin1().constData(), "w"); if(f == 0) saveAs(); else { if(fclose(f) != 0) printf("EditInstrument::checkDirty: Error closing file\n"); if(fileSave(i, i->filePath())) i->setDirty(false); } } return 0; } return n; } //--------------------------------------------------------- // getPatchItemText //--------------------------------------------------------- QString EditInstrument::getPatchItemText(int val) { QString s; if(val == MusECore::CTRL_VAL_UNKNOWN) s = QString("---"); else { int hb = ((val >> 16) & 0xff) + 1; if (hb == 0x100) hb = 0; int lb = ((val >> 8) & 0xff) + 1; if (lb == 0x100) lb = 0; int pr = (val & 0xff) + 1; if (pr == 0x100) pr = 0; s = QString("%1-%2-%3").arg(hb).arg(lb).arg(pr); } return s; } //--------------------------------------------------------- // enableDefaultControls //--------------------------------------------------------- void EditInstrument::enableDefaultControls(bool enVal, bool enPatch) { spinBoxDefault->setEnabled(enVal); patchButton->setEnabled(enPatch); if(!enPatch) { patchButton->blockSignals(true); patchButton->setText("---"); patchButton->blockSignals(false); } defPatchH->setEnabled(enPatch); defPatchL->setEnabled(enPatch); defPatchProg->setEnabled(enPatch); spinBoxDrumDefault->setEnabled(enVal); drumPatchButton->setEnabled(enPatch); if(!enPatch) { drumPatchButton->blockSignals(true); drumPatchButton->setText("---"); drumPatchButton->blockSignals(false); } defDrumPatchH->setEnabled(enPatch); defDrumPatchL->setEnabled(enPatch); defDrumPatchProg->setEnabled(enPatch); } //--------------------------------------------------------- // enableNonCtrlControls //--------------------------------------------------------- void EditInstrument::enableNonCtrlControls(bool v) { QTreeWidgetItem* sel = viewController->selectedItems().size() ? viewController->selectedItems()[0] : 0; if(!sel || !sel->data(0, Qt::UserRole).value()) return; MusECore::MidiController* c = (MusECore::MidiController*)sel->data(0, Qt::UserRole).value(); MusECore::MidiController::ControllerType type = MusECore::midiControllerType(c->num()); if(v) { switch (type) { case MusECore::MidiController::Controller7: spinBoxMin->setEnabled(true); spinBoxMax->setEnabled(true); enableDefaultControls(true, false); break; case MusECore::MidiController::RPN: case MusECore::MidiController::NRPN: spinBoxMin->setEnabled(true); spinBoxMax->setEnabled(true); enableDefaultControls(true, false); break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN14: spinBoxMin->setEnabled(true); spinBoxMax->setEnabled(true); enableDefaultControls(true, false); break; case MusECore::MidiController::Pitch: spinBoxMin->setEnabled(true); spinBoxMax->setEnabled(true); enableDefaultControls(true, false); break; case MusECore::MidiController::PolyAftertouch: case MusECore::MidiController::Aftertouch: spinBoxMin->setEnabled(true); spinBoxMax->setEnabled(true); enableDefaultControls(true, false); break; case MusECore::MidiController::Program: spinBoxMin->setEnabled(false); spinBoxMax->setEnabled(false); enableDefaultControls(false, true); break; default: spinBoxMin->setEnabled(false); spinBoxMax->setEnabled(false); enableDefaultControls(false, false); break; } } else { spinBoxDefault->setEnabled(false); patchButton->setEnabled(false); defPatchH->setEnabled(false); defPatchL->setEnabled(false); defPatchProg->setEnabled(false); spinBoxDrumDefault->setEnabled(false); drumPatchButton->setEnabled(false); defDrumPatchH->setEnabled(false); defDrumPatchL->setEnabled(false); defDrumPatchProg->setEnabled(false); spinBoxMin->setEnabled(false); spinBoxMax->setEnabled(false); } ctrlShowInMidi->setEnabled(v); ctrlShowInDrum->setEnabled(v); ctrlName->setEnabled(v); } //--------------------------------------------------------- // setDefaultPatchName //--------------------------------------------------------- void EditInstrument::setDefaultPatchName(int val) { patchButton->blockSignals(true); patchButton->setText(getPatchName(val, false)); // false = non-drums. patchButton->blockSignals(false); } //--------------------------------------------------------- // setDefaultPatchName //--------------------------------------------------------- void EditInstrument::setDefaultDrumPatchName(int val) { drumPatchButton->blockSignals(true); drumPatchButton->setText(getPatchName(val, true)); // true = drums. drumPatchButton->blockSignals(false); } //--------------------------------------------------------- // getDefaultPatchNumber //--------------------------------------------------------- int EditInstrument::getDefaultPatchNumber() { int hval = defPatchH->value() - 1; int lval = defPatchL->value() - 1; int prog = defPatchProg->value() - 1; if(hval == -1) hval = 0xff; if(lval == -1) lval = 0xff; if(prog == -1) prog = 0xff; return ((hval & 0xff) << 16) + ((lval & 0xff) << 8) + (prog & 0xff); } //--------------------------------------------------------- // getDefaultDrumPatchNumber //--------------------------------------------------------- int EditInstrument::getDefaultDrumPatchNumber() { int hval = defDrumPatchH->value() - 1; int lval = defDrumPatchL->value() - 1; int prog = defDrumPatchProg->value() - 1; if(hval == -1) hval = 0xff; if(lval == -1) lval = 0xff; if(prog == -1) prog = 0xff; return ((hval & 0xff) << 16) + ((lval & 0xff) << 8) + (prog & 0xff); } //--------------------------------------------------------- // getDrummapCollectionPatchNumber //--------------------------------------------------------- int EditInstrument::getDrummapCollectionPatchNumber() { int hval = hbankFromBox->value() - 1; int lval = lbankFromBox->value() - 1; int prog = patchFromBox->value() - 1; if(hval == -1) hval = 0xff; if(lval == -1) lval = 0xff; if(prog == -1) prog = 0xff; return ((hval & 0xff) << 16) + ((lval & 0xff) << 8) + (prog & 0xff); } //--------------------------------------------------------- // setDefaultPatchNumbers //--------------------------------------------------------- void EditInstrument::setDefaultPatchNumbers(int val) { int hb; int lb; int pr; if(val == MusECore::CTRL_VAL_UNKNOWN) hb = lb = pr = 0; else { hb = ((val >> 16) & 0xff) + 1; if (hb == 0x100) hb = 0; lb = ((val >> 8) & 0xff) + 1; if (lb == 0x100) lb = 0; pr = (val & 0xff) + 1; if (pr == 0x100) pr = 0; } defPatchH->blockSignals(true); defPatchL->blockSignals(true); defPatchProg->blockSignals(true); defPatchH->setValue(hb); defPatchL->setValue(lb); defPatchProg->setValue(pr); defPatchH->blockSignals(false); defPatchL->blockSignals(false); defPatchProg->blockSignals(false); } //--------------------------------------------------------- // setDefaultDrumPatchNumbers //--------------------------------------------------------- void EditInstrument::setDefaultDrumPatchNumbers(int val) { int hb; int lb; int pr; if(val == MusECore::CTRL_VAL_UNKNOWN) hb = lb = pr = 0; else { hb = ((val >> 16) & 0xff) + 1; if (hb == 0x100) hb = 0; lb = ((val >> 8) & 0xff) + 1; if (lb == 0x100) lb = 0; pr = (val & 0xff) + 1; if (pr == 0x100) pr = 0; } defDrumPatchH->blockSignals(true); defDrumPatchL->blockSignals(true); defDrumPatchProg->blockSignals(true); defDrumPatchH->setValue(hb); defDrumPatchL->setValue(lb); defDrumPatchProg->setValue(pr); defDrumPatchH->blockSignals(false); defDrumPatchL->blockSignals(false); defDrumPatchProg->blockSignals(false); } //--------------------------------------------------------- // setDefaultPatchControls //--------------------------------------------------------- void EditInstrument::setDefaultPatchControls(int val) { setDefaultPatchNumbers(val); setDefaultPatchName(val); } //--------------------------------------------------------- // setDefaultDrumPatchControls //--------------------------------------------------------- void EditInstrument::setDefaultDrumPatchControls(int val) { setDefaultDrumPatchNumbers(val); setDefaultDrumPatchName(val); } //--------------------------------------------------------- // getPatchName //--------------------------------------------------------- QString EditInstrument::getPatchName(int prog, bool drum, bool includeDefault) { if(MusECore::Patch* p = workingInstrument->groups()->findPatch(prog, drum, includeDefault)) return p->name; return "---"; } QMenu* EditInstrument::createPopupPatchList(bool drum) { QMenu* patchpopup = new QMenu; MusECore::PatchGroupList* pg = workingInstrument->groups(); if (pg->size() > 1) { for (MusECore::ciPatchGroup i = pg->begin(); i != pg->end(); ++i) { MusECore::PatchGroup* pgp = *i; QMenu* pm = 0; const MusECore::PatchList& pl = pgp->patches; for (MusECore::ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const MusECore::Patch* mp = *ipl; if(mp->drum != drum) continue; if(!pm) { pm = new QMenu(pgp->name, patchpopup); patchpopup->addMenu(pm); pm->setFont(qApp->font()); } int id = ((mp->hbank & 0xff) << 16) + ((mp->lbank & 0xff) << 8) + (mp->program & 0xff); QAction *ac1 = pm->addAction(mp->name); ac1->setData(id); } } } else if (pg->size() == 1 ){ // no groups const MusECore::PatchList& pl = pg->front()->patches; for (MusECore::ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const MusECore::Patch* mp = *ipl; if(mp->drum != drum) continue; int id = ((mp->hbank & 0xff) << 16) + ((mp->lbank & 0xff) << 8) + (mp->program & 0xff); QAction *ac2 = patchpopup->addAction(mp->name); ac2->setData(id); } } if(patchpopup->actions().count() == 0) { delete patchpopup; return NULL; } return patchpopup; } void EditInstrument::popupDrummapPatchList() { QMenu* patchpopup = createPopupPatchList(true); if(!patchpopup) return; QAction* act = patchpopup->exec(drummapsPatchNames->mapToGlobal(QPoint(10,5))); if(!act) { delete patchpopup; return; } bool ok; int rv = act->data().toInt(&ok); delete patchpopup; if(!ok || rv == -1) return; int hb = (rv >> 16) & 0xff; int lb = (rv >> 8) & 0xff; int pr = rv & 0xff; patchFromBox->blockSignals(true); lbankFromBox->blockSignals(true); hbankFromBox->blockSignals(true); if(hb == 0xff) hbankFromBox->setValue(0); else hbankFromBox->setValue(hb + 1); if(lb == 0xff) lbankFromBox->setValue(0); else lbankFromBox->setValue(lb + 1); if(pr == 0xff) patchFromBox->setValue(0); else patchFromBox->setValue(pr + 1); patchFromBox->blockSignals(false); lbankFromBox->blockSignals(false); hbankFromBox->blockSignals(false); storePatchCollection(); } void EditInstrument::popupControllerDefaultPatchList(bool drum) { QMenu* patchpopup = createPopupPatchList(drum); if(!patchpopup) return; QAction* act = patchpopup->exec((drum ? drumPatchButton : patchButton)->mapToGlobal(QPoint(10,5))); if(!act) { delete patchpopup; return; } bool ok; int rv = act->data().toInt(&ok); delete patchpopup; if(!ok || rv == -1) return; if(drum) setDefaultDrumPatchControls(rv); else setDefaultPatchControls(rv); QTreeWidgetItem* item = viewController->currentItem(); if(item) { MusECore::MidiController* c = (MusECore::MidiController*)item->data(0, Qt::UserRole).value(); if(drum) { c->setDrumInitVal(rv); item->setText(COL_DRUM_DEF, getPatchItemText(rv)); } else { c->setInitVal(rv); item->setText(COL_DEF, getPatchItemText(rv)); } } workingInstrument->setDirty(true); } //--------------------------------------------------------- // populateInitEventList //--------------------------------------------------------- void EditInstrument::populateInitEventList() { initEventList->blockSignals(true); initEventList->clear(); MusECore::EventList* el = workingInstrument->midiInit(); for(MusECore::iEvent ie = el->begin(); ie != el->end(); ++ie) { InitListItem* item = new InitListItem(initEventList, ie->second, workingInstrument); initEventList->addTopLevelItem(item); } if(initEventList->topLevelItem(0)) initEventList->setCurrentItem(initEventList->topLevelItem(0)); initEventList->blockSignals(false); } //--------------------------------------------------------- // initListChangeClicked //--------------------------------------------------------- void EditInstrument::initListChangeClicked() { InitListItem* item = static_cast(initEventList->currentItem()); if(!item) return; editInitListItem(item); } //--------------------------------------------------------- // editInitListItem //--------------------------------------------------------- void EditInstrument::editInitListItem(QTreeWidgetItem* item) { InitListItem* ev = (InitListItem*)item; if(ev->_event.type() != MusECore::Sysex) return; int tick = ev->_event.tick(); MusECore::Event nevent = EditSysexDialog::getEvent(tick, ev->_event, this, workingInstrument); if(!nevent.empty()) { MusECore::EventList* el = workingInstrument->midiInit(); MusECore::iEvent ie = el->find(ev->_event); if(ie != el->end()) el->erase(ie); el->add(nevent); //delete item; //item = new InitListItem(initEventList, nevent, &workingInstrument); //initEventList->addTopLevelItem(item); //initEventList->setCurrentItem(item); populateInitEventList(); workingInstrument->setDirty(true); } } //--------------------------------------------------------- // initListAddClicked //--------------------------------------------------------- void EditInstrument::initListAddClicked() { //MusECore::Event event = EditSysexDialog::getEvent(curPart->tick(), MusECore::Event(), this); // TODO Get current item tick, if any MusECore::Event event = EditSysexDialog::getEvent(0, MusECore::Event(), this, workingInstrument); if(!event.empty()) { workingInstrument->midiInit()->add(event); //InitListItem* item = new InitListItem(initEventList, event, &workingInstrument); //initEventList->addTopLevelItem(item); //initEventList->setCurrentItem(item); populateInitEventList(); workingInstrument->setDirty(true); } } //--------------------------------------------------------- // initListDeleteClicked //--------------------------------------------------------- void EditInstrument::initListDeleteClicked() { InitListItem* item = static_cast(initEventList->currentItem()); if(!item) return; MusECore::EventList* el = workingInstrument->midiInit(); MusECore::iEvent ie = el->find(item->_event); if(ie != el->end()) { el->erase(ie); populateInitEventList(); } //delete item; workingInstrument->setDirty(true); } void EditInstrument::on_actionInfo_triggered() { QString s("https://github.com/muse-sequencer/muse/wiki/instrument-definitions"); MusEGlobal::muse->launchBrowser(s); } } // namespace MusEGui muse-4.0.0/src/muse/components/editinstrument.h000066400000000000000000000132731404105311000216330ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: editinstrument.h,v 1.1.1.1.2.4 2009/05/31 05:12:12 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) // (C) Copyright 2012, 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __EDITINSTRUMENT_H__ #define __EDITINSTRUMENT_H__ #include "ui_editinstrumentbase.h" #include #include #include "globaldefs.h" // Forward declarations: class QMenu; class QCloseEvent; class QKeyEvent; class QGridLayout; class QStringListModel; class QString; class QAction; class QListWidgetItem; class QTreeWidgetItem; class QMainWindow; class QScrollBar; namespace MusECore { class MidiInstrument; class MidiController; struct Patch; struct PatchGroup; struct SysEx; } namespace MusEGui { class Header; class DList; //--------------------------------------------------------- // EditInstrument //--------------------------------------------------------- class EditInstrument : public QMainWindow, public Ui::EditInstrumentBase { Q_OBJECT MusECore::MidiInstrument* workingInstrument; QListWidgetItem* oldMidiInstrument; QTreeWidgetItem* oldPatchItem; Header* dlist_header; DList* dlist; QScrollBar* dlist_vscroll; QGridLayout* dlist_grid; QStringListModel* patch_coll_model; void setHeaderToolTips(); void setHeaderWhatsThis(); virtual void closeEvent(QCloseEvent*); virtual void keyPressEvent(QKeyEvent*); int checkDirty(MusECore::MidiInstrument*, bool isClose = false); bool fileSave(MusECore::MidiInstrument*, const QString&); void saveAs(); void updateInstrument(MusECore::MidiInstrument*); void updatePatch(MusECore::MidiInstrument*, MusECore::Patch*); void updatePatchGroup(MusECore::MidiInstrument*, MusECore::PatchGroup*); void updateSysex(MusECore::MidiInstrument*, MusECore::SysEx*); void changeInstrument(); void populateInitEventList(); QTreeWidgetItem* addControllerToView(MusECore::MidiController* mctrl); QString getPatchItemText(int); void enableDefaultControls(bool, bool); void enableNonCtrlControls(bool); void setDefaultPatchName(int); void setDefaultDrumPatchName(int); int getDefaultPatchNumber(); int getDefaultDrumPatchNumber(); int getDrummapCollectionPatchNumber(); void setDefaultPatchNumbers(int); void setDefaultDrumPatchNumbers(int); void setDefaultPatchControls(int); void setDefaultDrumPatchControls(int); QString getPatchName(int prog, bool drum = false, bool includeDefault = true); QMenu* createPopupPatchList(bool drum = false); void popupDrummapPatchList(); void popupControllerDefaultPatchList(bool drum = false); void deleteInstrument(QListWidgetItem*); private slots: virtual void fileNew(); virtual void fileOpen(); virtual void fileSave(); virtual void fileSaveAs(); virtual void fileClose(); virtual void helpWhatsThis(); void instrumentChanged(); void noteOffModeChanged(int); void tabChanged(int); void patchChanged(); void controllerChanged(); void instrumentNameReturn(); void patchNameReturn(); void deletePatchClicked(); void newPatchClicked(); void newGroupClicked(); void patchButtonClicked(); void drumPatchButtonClicked(); void drummapCollectionPatchButtonClicked(); void defPatchChanged(int); void defDrumPatchChanged(int); void deleteControllerClicked(); void newControllerClicked(); void addControllerClicked(); void ctrlPopupTriggered(QAction*); void ctrlTypeChanged(int); void ctrlNameReturn(); void ctrlNumChanged(); void ctrlMinChanged(int); void ctrlMaxChanged(int); void ctrlDefaultChanged(int); void ctrlDrumDefaultChanged(int); void ctrlShowInMidiChanged(int); void ctrlShowInDrumChanged(int); void sysexChanged(QListWidgetItem*, QListWidgetItem*); void deleteSysexClicked(); void newSysexClicked(); void editInitListItem(QTreeWidgetItem* item); void initListDeleteClicked(); void initListAddClicked(); void initListChangeClicked(); void patchCollectionSpinboxChanged(int); void patchActivated(const QModelIndex&); void addPatchCollection(); void delPatchCollection(); void copyPatchCollection(); void patchCollectionUp(); void patchCollectionDown(); void repopulatePatchCollections(); void storePatchCollection(); void fetchPatchCollection(); void on_actionInfo_triggered(); public: EditInstrument(QWidget* parent = 0, Qt::WindowFlags fl = Qt::Window); virtual ~EditInstrument(); void findInstrument(const QString& find_instrument); void showTab(EditInstrumentTabType); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/editinstrumentbase.ui000066400000000000000000002405221404105311000226530ustar00rootroot00000000000000 EditInstrumentBase 0 0 816 505 0 0 MusE: Instrument Editor 0 0 Qt::Horizontal 2 0 0 Instrument Name: false 0 0 Selected instrument name. 0 0 List of defined instruments. 0 0 Qt::Horizontal 0 0 Note off mode: 0 0 Note off mode Selects how to handle note off events. The instrument can use note off events, or not at all, or convert them to zero-velocity note on events which helps save midi interface bandwidth. Refer to the instrument manufacturer's midi implementation chart for details. true 0 Pa&tches Qt::Horizontal 0 0 List of groups and patches. true true Group/Patch Properties Name: false Group or patch name Group or patch name High Bank: false Patch high bank number Patch high bank number. --- means don't care. --- 0 128 0 Qt::Horizontal QSizePolicy::Expanding 373 20 Low Bank: false Patch low bank number Patch low bank number. --- means don't care. --- 0 128 0 Qt::Horizontal QSizePolicy::Expanding 373 20 Program: false Patch program number Patch program number. --- means don't care. --- 0 128 0 Qt::Horizontal QSizePolicy::Expanding 373 20 Show in tracks: Midi &Drum Qt::Horizontal 40 20 Qt::Vertical QSizePolicy::Expanding 20 90 0 0 Delete group or patch &Delete Alt+D 0 0 New patch New &Patch Alt+P New group New &Group Alt+G Qt::Horizontal QSizePolicy::Expanding 240 20 Drum&maps Qt::Horizontal 0 0 Patch Collections: 0 0 0 2 0 0 &Add false 0 0 &Copy false 0 0 &Remove Qt::Horizontal 40 20 2 false 0 0 &Up false 0 0 &Down Qt::Horizontal 40 20 true 0 0 0 0 2 High Bank true 0 0 Patch high bank number Patch high bank number. --- means don't care. --- false 0 128 0 Low Bank true 0 0 Patch low bank number Patch low bank number. --- means don't care. --- false 0 128 0 Program true 0 0 Patch program number Patch program number. --- means don't care. --- false 0 128 0 Qt::Horizontal 40 20 2 0 0 Patch Name: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 0 0 ??? Qt::Horizontal 6 20 100 0 Contro&llers List of defined controllers List of defined controllers. QFrame::StyledPanel QFrame::Sunken true 30 60 false Name AlignLeading|AlignVCenter Type AlignLeading|AlignVCenter H-Ctrl AlignTrailing|AlignVCenter L-Ctrl AlignTrailing|AlignVCenter Min AlignTrailing|AlignVCenter Max AlignTrailing|AlignVCenter Def AlignTrailing|AlignVCenter Dr def AlignTrailing|AlignVCenter Midi AlignTrailing|AlignVCenter Drum AlignTrailing|AlignVCenter 0 0 Properties 2 2 2 2 2 Name: false Midi controller name 0 0 Create a new controller New &Controller Alt+C 0 0 Add common controller &Add Common... Alt+A 0 0 Delete controller &Delete Alt+D Type: false 0 0 Midi controller type Midi controller type H-Ctrl false 10 0 0 Midi controller number high byte Midi controller number high byte 0 127 0 L-Ctrl false 10 0 0 Midi controller number low byte (* means drum controller) Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and GS/XG type songs and instruments. Allows controllers for each instrument in Muse's drum map. The low byte will be replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. * -1 127 0 W Qt::Horizontal QSizePolicy::Expanding 0 20 Range: false Min Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 10 0 0 Minimum value. If negative, auto-translate. Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. Useful for controllers which should be displayed with zero bias. For example, 'Pan': Minimum: -64 Maximum: 63 True range: Min: 0 Max: 127 (bias = 64) 'CoarseTuning': Min: -24 Max: 23 True range: Min: 40 Max: 87 (bias = 64) Bias is determined from controller type: 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 Type 'Pitch' is the exception. It is biased at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) -16384 16383 Max Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 10 0 0 Maximum value Maximum value -16384 16383 127 Qt::Horizontal QSizePolicy::Fixed 22 20 Show in tracks: Midi true Drum true Qt::Horizontal QSizePolicy::Expanding 0 20 Default: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false Drum default: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 0 0 0 0 Default value. Off: No default. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). off -1 16383 -1 H-Bank false 0 0 off 0 128 0 L-Bank false 0 0 off 0 128 0 Progr. false 0 0 off 0 128 0 true 0 0 0 0 ??? 0 0 0 0 Default value. Off: No default. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). off -1 16383 -1 H-Bank false 0 0 off 0 128 0 L-Bank false 0 0 off 0 128 0 Progr. false 0 0 off 0 128 0 true 0 0 0 0 ??? S&ysEx Qt::Horizontal SysEx List: false 0 0 Name: false Comment: Hex Entry: false Note: The enclosing F0 ... F7 are not required &Delete Alt+D New SysE&x Alt+X Qt::Horizontal QSizePolicy::Expanding 60 20 &Initialization Instrument initialization sequence: 1 Qt::Horizontal 40 20 &Add... &Change... &Delete Tools TopToolBarArea false 0 0 816 21 false &File &Help &New New Ctrl+N fileNewAction false &Open... Open Ctrl+O false fileOpenAction &Save Save Ctrl+S fileSaveAction Save &As... Save As fileSaveAsAction &Close Close fileCloseAction new item new item filenew_itemAction What's this? What's this? whatsThisAction Info Additional information instrumentName instrumentList tabWidget3 patchView patchNameEdit spinBoxHBank spinBoxLBank spinBoxProgram ctrlName ctrlType spinBoxHCtrlNo spinBoxLCtrlNo spinBoxMin spinBoxDefault sysexList sysexName sysexData fileNewAction triggered() EditInstrumentBase fileNew() -1 -1 20 20 fileOpenAction triggered() EditInstrumentBase fileOpen() -1 -1 20 20 fileSaveAction triggered() EditInstrumentBase fileSave() -1 -1 20 20 fileSaveAsAction triggered() EditInstrumentBase fileSaveAs() -1 -1 20 20 fileCloseAction triggered() EditInstrumentBase fileClose() -1 -1 20 20 whatsThisAction triggered() EditInstrumentBase helpWhatsThis() -1 -1 20 20 muse-4.0.0/src/muse/components/editnotedialogbase.ui000066400000000000000000000124751404105311000225740ustar00rootroot00000000000000 EditNoteDialogBase 0 0 231 182 MusE: Enter Note true 11 6 6 0 Qt::Horizontal QSizePolicy::Expanding 20 20 OK 0 true true Cancel 0 true Length: false Time Position: false Pitch: false Velocity On: false Velocity Off: false 1 1000000 127 127 MusEGui::PosEdit QWidget
posedit.h
0
MusEGui::PitchEdit QWidget
pitchedit.h
il1 pl il2 il3 buttonOk buttonCancel pitchedit.h buttonOk clicked() EditNoteDialogBase accept() 20 20 20 20 buttonCancel clicked() EditNoteDialogBase reject() 20 20 20 20
muse-4.0.0/src/muse/components/editsysexdialogbase.ui000066400000000000000000000146151404105311000230000ustar00rootroot00000000000000 EditSysexDialogBase 0 0 433 334 MusE: Enter SysEx true 6 11 11 11 11 6 0 0 0 0 TimePosition: false Qt::Horizontal QSizePolicy::Expanding 20 20 Hex: Note: The enclosing F0 ... F7 are not required Name: false QFrame::Panel QFrame::Sunken Comment: false true 0 0 409 68 QFrame::Panel QFrame::Sunken 6 0 0 0 0 Qt::Horizontal QSizePolicy::Expanding 20 20 &Select... &OK 0 true true &Cancel 0 true MusEGui::PosEdit QWidget
posedit.h
buttonOk clicked() EditSysexDialogBase accept() 20 20 20 20 buttonCancel clicked() EditSysexDialogBase reject() 20 20 20 20
muse-4.0.0/src/muse/components/elided_label.cpp000066400000000000000000000336631404105311000215020ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // elided_label.cpp // (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include #include #include #include "background_painter.h" #include "elided_label.h" namespace MusEGui { ElidedLabel::ElidedLabel(QWidget* parent, Qt::TextElideMode elideMode, Qt::Alignment alignment, //int maxFontPoint, int minFontPoint, bool ignoreHeight, bool ignoreWidth, const QString& text, const char* name, Qt::WindowFlags flags) : QFrame(parent, flags), _elideMode(elideMode), //_fontPointMax(maxFontPoint), _fontPointMin(minFontPoint), _fontIgnoreHeight(ignoreHeight), _fontIgnoreWidth(ignoreWidth), _text(text) { setObjectName(name); setMouseTracking(true); setEnabled(true); setFocusPolicy(Qt::StrongFocus); //setAutoFillBackground(false); //setAttribute(Qt::WA_NoSystemBackground); //setAttribute(Qt::WA_StaticContents); // This is absolutely required for speed! Otherwise painfully slow because of full background // filling, even when requesting small udpdates! Background is drawn by us. //setAttribute(Qt::WA_OpaquePaintEvent); _id = -1; _hasOffMode = false; _off = false; _hovered = false; _alignment = alignment; _style3d = true; _radius = 2; setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); // updateSizeHint(); _curFont = font(); autoAdjustFontSize(); } void ElidedLabel::keyPressEvent(QKeyEvent* e) { switch (e->key()) { case Qt::Key_Escape: // Don't let ancestor grab it, let it pass up the chain. e->ignore(); return; break; case Qt::Key_Enter: case Qt::Key_Return: e->accept(); emit returnPressed(pos(), _id, e->modifiers()); return; break; default: break; } e->ignore(); return QFrame::keyPressEvent(e); } void ElidedLabel::setOff(bool v) { if(v && !_hasOffMode) _hasOffMode = true; if(_off == v) return; _off = v; update(); //emit valueStateChanged(value(), isOff(), id(), d_scrollMode); } void ElidedLabel::setHasOffMode(bool v) { _hasOffMode = v; setOff(false); } void ElidedLabel::paintEvent(QPaintEvent* e) { QFrame::paintEvent(e); if(rect().width() <= 0 || rect().height() <= 0) return; QPainter painter(this); const QRect r = rect(); const QRect ar = r.adjusted(2, 1, -1, -1); ItemBackgroundPainter ibp; ibp.drawBackground(&painter, r, palette(), 1, 1, !hasOffMode() || !isOff() ? r : QRect(), _radius, _style3d, _bgActiveColor, _borderColor, _bgColor); // if (hasFocus()) // { // if (_hovered) // painter.setPen(QPen(QColor(239,239,239))); // else // painter.setPen(QPen(Qt::white)); // } // else if (_hovered) // painter.setPen(QPen(QColor(48,48,48))); // else // painter.setPen(QPen(Qt::black)); painter.setPen((!hasOffMode() || !isOff()) ? _fontActiveColor : _fontColor); painter.setRenderHint(QPainter::Antialiasing); // painter.setFont(_curFont); // QFontMetrics fm = painter.fontMetrics(); //painter.setFont(font()); QFontMetrics fm = fontMetrics(); QString elidedText = fm.elidedText(_text, _elideMode, r.width()); // painter.drawText(QPoint(0, fm.ascent()), elidedText); painter.drawText(ar, _alignment, elidedText); } //--------------------------------------------------------- // autoAdjustFontSize // w: Widget to auto adjust font size // s: String to fit // ignoreWidth: Set if dealing with a vertically constrained widget - one which is free to resize horizontally. // ignoreHeight: Set if dealing with a horizontally constrained widget - one which is free to resize vertically. //--------------------------------------------------------- bool ElidedLabel::autoAdjustFontSize() { // FIXME: Disabled for now, the font modulates back and forth, not very good ATM. // May have to revert to the font-checking iteration loop scheme. // QFont fnt = font(); // This is the maximum font. // int max = fnt.pointSize(); // int min = _fontPointMin; // // // In case the max or min was obtained from QFont::pointSize() which returns -1 // // if the font is a pixel font, or if min is greater than max... // // Limit the minimum and maximum sizes to something at least readable. // if(max < 4) // max = 4; // if(min < 4) // min = 4; // if(max < min) // max = min; // // //qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); // //QRectF r = boundingRect(); // QRectF r = rect(); // //QFont f = painter->font(); // // // //if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. // if(_fontIgnoreWidth || _text.isEmpty()) // Also avoid divide by zero below. // { // if(fnt.pointSize() != max) // { // fnt.setPointSize(max); // // setFont(fnt); // _curFont = fnt; // update(); // } // } // else // { // //qreal aspectRatio = painter->fontMetrics().lineSpacing() / painter->fontMetrics().averageCharWidth(); // qreal aspectRatio = fontMetrics().lineSpacing() / fontMetrics().averageCharWidth(); // // int pixelsize = sqrt(r.width() * r.height() / aspectRatio / (_text.length() * 3)) * aspectRatio; // int pixelsize = sqrt(r.width() * r.height() / aspectRatio / _text.length()) * aspectRatio; // fnt.setPixelSize(pixelsize); // //int flags = Qt::AlignCenter|Qt::TextDontClip|Qt::TextWordWrap; // int flags = Qt::AlignCenter; // //if ((pixelsize * lod) < 13) // // flags |= Qt::TextWrapAnywhere; // QFontMetricsF fmf(fnt); // QRectF tbr = fmf.boundingRect(r, flags, _text); // pixelsize = fnt.pixelSize() * qMin(r.width() * 0.95 / tbr.width(), r.height() * 0.95 / tbr.height()); // // if(pixelsize < min) // // pixelsize = min; // // else if(pixelsize > max) // // pixelsize = max; // fnt.setPixelSize(pixelsize); // const QFontInfo fi(fnt); // const int pointsize = fi.pointSize(); // if(pointsize <= min) // fnt.setPointSize(min); // else if(pointsize >= max) // fnt.setPointSize(max); // // setFont(fnt); // _curFont = fnt; // //painter->drawText(r,flags,stitle); update(); // } // Force minimum height. Use the expected height for the highest given point size. // This way the mixer strips aren't all different label heights, but can be larger if necessary. // Only if ignoreHeight is set (therefore the height is adjustable). if(_fontIgnoreHeight) { // FIXME Disabled for now, as per above. // fnt.setPointSize(max); // const QFontMetrics fm(fnt); const QFontMetrics fm(font()); // Set the label's minimum height equal to the height of the font. setMinimumHeight(fm.height() + 2 * frameWidth()); } return true; } void ElidedLabel::setText(const QString& txt) { if(_text == txt) return; _text = txt; autoAdjustFontSize(); } void ElidedLabel::resizeEvent(QResizeEvent* e) { e->ignore(); QFrame::resizeEvent(e); autoAdjustFontSize(); } void ElidedLabel::mousePressEvent(QMouseEvent* e) { e->accept(); emit pressed(e->pos(), _id, e->buttons(), e->modifiers()); } void ElidedLabel::mouseReleaseEvent(QMouseEvent* e) { e->accept(); emit released(e->pos(), _id, e->buttons(), e->modifiers()); } void ElidedLabel::leaveEvent(QEvent *e) { if(_hovered) { _hovered = false; update(); } e->ignore(); QFrame::leaveEvent(e); } void ElidedLabel::mouseMoveEvent(QMouseEvent *e) { //e->ignore(); //QFrame::mouseMoveEvent(e); e->accept(); if(!_hovered) { _hovered = true; update(); } } void ElidedLabel::mouseDoubleClickEvent(QMouseEvent* ev) { ev->accept(); emit doubleClicked(); } void ElidedLabel::setFontIgnoreDimensions(bool ignoreHeight, bool ignoreWidth) { _fontIgnoreWidth = ignoreWidth; _fontIgnoreHeight = ignoreHeight; autoAdjustFontSize(); } void ElidedLabel::setFontPointMin(int point) { _fontPointMin = point; autoAdjustFontSize(); } QSize ElidedLabel::sizeHint() const { // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 QSize sz(fontMetrics().horizontalAdvance(_text) + 8, fontMetrics().height() + 4); #else QSize sz(fontMetrics().width(_text) + 8, fontMetrics().height() + 4); #endif return sz; } //// ============================================================================== //ElidedTextLabel::ElidedTextLabel(QWidget* parent, // const char* name, // Qt::WindowFlags flags // ) // : QFrame(parent, flags) //{ // setObjectName(name); // setMouseTracking(true); // setEnabled(true); // setFocusPolicy(Qt::StrongFocus); // setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); // //setAutoFillBackground(false); // //setAttribute(Qt::WA_NoSystemBackground); // //setAttribute(Qt::WA_StaticContents); // // This is absolutely required for speed! Otherwise painfully slow because of full background // // filling, even when requesting small udpdates! Background is drawn by us. // //setAttribute(Qt::WA_OpaquePaintEvent); // _elideMode = Qt::ElideNone; // _alignment = Qt::AlignLeft | Qt::AlignVCenter; // _id = -1; // _hasOffMode = false; // _off = false; // _hovered = false; //} //ElidedTextLabel::ElidedTextLabel(const QString& text, // QWidget* parent, // const char* name, // Qt::WindowFlags flags) // : QFrame(parent, flags), // _text(text) //{ // setObjectName(name); // setMouseTracking(true); // setEnabled(true); // setFocusPolicy(Qt::StrongFocus); // setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); // //setAutoFillBackground(false); // //setAttribute(Qt::WA_NoSystemBackground); // //setAttribute(Qt::WA_StaticContents); // // This is absolutely required for speed! Otherwise painfully slow because of full background // // filling, even when requesting small udpdates! Background is drawn by us. // //setAttribute(Qt::WA_OpaquePaintEvent); // _elideMode = Qt::ElideNone; // _alignment = Qt::AlignLeft | Qt::AlignVCenter; // _id = -1; // _hasOffMode = false; // _off = false; // _hovered = false; // setToolTip(_text + _tooltipText); //} //void ElidedTextLabel::keyPressEvent(QKeyEvent* e) //{ // switch (e->key()) // { // case Qt::Key_Escape: // // Don't let ancestor grab it, let it pass up the chain. // e->ignore(); // return; // break; // case Qt::Key_Enter: // case Qt::Key_Return: // e->accept(); // emit returnPressed(pos(), _id, e->modifiers()); // return; // break; // default: // break; // } // e->ignore(); // return QFrame::keyPressEvent(e); //} //void ElidedTextLabel::setOff(bool v) //{ // if(v && !_hasOffMode) // _hasOffMode = true; // if(_off == v) // return; // _off = v; // update(); // //emit valueStateChanged(value(), isOff(), id(), d_scrollMode); //} //void ElidedTextLabel::setHasOffMode(bool v) //{ // _hasOffMode = v; // setOff(false); //} //void ElidedTextLabel::paintEvent(QPaintEvent* ev) //{ // QFrame::paintEvent(ev); // ev->accept(); // if(rect().width() <= 0 || rect().height() <= 0) // return; // QPainter p(this); // p.save(); // const QRect r = rect(); // const QRect ar = r.adjusted(1, 1, -1, -1); // if (hasFocus()) // { // if (_hovered) // p.setPen(QPen(QColor(239,239,239))); // else // p.setPen(QPen(Qt::white)); // } // else if (_hovered) // p.setPen(QPen(QColor(48,48,48))); // else // p.setPen(QPen(Qt::black)); // p.setRenderHint(QPainter::Antialiasing); // QFontMetrics fm = p.fontMetrics(); // QString elidedText = fm.elidedText(_text, _elideMode, r.width()); //// painter.drawText(QPoint(0, fm.ascent()), elidedText); // p.drawText(ar, _alignment, elidedText); // p.restore(); //} //void ElidedTextLabel::setText(const QString& txt) //{ // if(_text == txt) // return; // _text = txt; // setToolTip(_text + _tooltipText); //} //void ElidedTextLabel::setTooltipText(const QString& txt) //{ // if(_tooltipText == txt) // return; // _tooltipText = txt; // setToolTip(_text + _tooltipText); //} //void ElidedTextLabel::leaveEvent(QEvent *e) //{ // if(_hovered) // { // _hovered = false; // update(); // } // e->ignore(); // QFrame::leaveEvent(e); //} //void ElidedTextLabel::mouseMoveEvent(QMouseEvent *e) //{ // e->ignore(); // QFrame::mouseMoveEvent(e); //// e->accept(); // if(!_hovered) // { // _hovered = true; // update(); // } //} //QSize ElidedTextLabel::sizeHint() const //{ //// Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). //#if QT_VERSION >= 0x050b00 // QSize sz(fontMetrics().horizontalAdvance(_text) + 8, fontMetrics().height() + 4); //#else // QSize sz(fontMetrics().width(_text) + 8, fontMetrics().height() + 4); //#endif // return sz; //} } // namespace MusEGui muse-4.0.0/src/muse/components/elided_label.h000066400000000000000000000214241404105311000211370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // elided_label.h // (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ELIDED_LABEL_H__ #define __ELIDED_LABEL_H__ #include namespace MusEGui { //--------------------------------------------------------- // ElidedLabel //--------------------------------------------------------- class ElidedLabel : public QFrame { Q_OBJECT // FIXME: Found some problems here: If this was enabled, text() was // intermittently including accelerator key characters, causing // for example midi controller stream text rapid flickering // between normal and accelerator key text. // Maybe it should be const QString& ? //Q_PROPERTY(QString text READ text WRITE setText) //Q_PROPERTY(Qt::TextElideMode elideMode READ elideMode WRITE setElideMode) // Q_PROPERTY( QColor activeColor READ activeColor WRITE setActiveColor ) Q_PROPERTY( bool style3d READ style3d WRITE setStyle3d ) Q_PROPERTY( int radius READ radius WRITE setRadius ) private: int _id; bool _hasOffMode; bool _off; Qt::TextElideMode _elideMode; Qt::Alignment _alignment; int _fontPointMin; bool _fontIgnoreHeight; bool _fontIgnoreWidth; // leave defaults to background painter QColor _bgActiveColor; QColor _bgColor; QColor _borderColor; QColor _fontColor{Qt::black}; QColor _fontActiveColor{Qt::white}; bool _style3d; int _radius; QString _text; QFont _curFont; // Whether the mouse is over the entire control. bool _hovered; bool autoAdjustFontSize(); protected: virtual void paintEvent(QPaintEvent*); virtual void resizeEvent(QResizeEvent*); virtual void mousePressEvent(QMouseEvent*); virtual void mouseReleaseEvent(QMouseEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual void mouseDoubleClickEvent(QMouseEvent*); virtual void leaveEvent(QEvent*); virtual void keyPressEvent(QKeyEvent*); signals: void pressed(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); void released(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); void returnPressed(QPoint p, int id, Qt::KeyboardModifiers keys); void doubleClicked(); public: explicit ElidedLabel(QWidget* parent = 0, Qt::TextElideMode elideMode = Qt::ElideNone, Qt::Alignment alignment = Qt::AlignLeft | Qt::AlignVCenter, //int maxFontPoint = 10, int minFontPoint = 5, bool ignoreHeight = true, bool ignoreWidth = false, const QString& text = QString(), const char* name = 0, Qt::WindowFlags flags = Qt::Widget); virtual QSize sizeHint() const; int id() const { return _id; } void setId(int i) { _id = i; } Qt::TextElideMode elideMode() const { return _elideMode; } void setElideMode(Qt::TextElideMode mode) { _elideMode = mode; update(); } Qt::Alignment alignment() const { return _alignment; } void setAlignment(Qt::Alignment align) { _alignment = align; update(); } bool hasOffMode() const { return _hasOffMode; } void setHasOffMode(bool v); bool isOff() const { return _off; } // Sets the off state and emits valueStateChanged signal if required. void setOff(bool v); // Both value and off state changed combined into one setter. // By default it is assumed that setting a value naturally implies resetting the 'off' state to false. // Emits valueChanged and valueStateChanged signals if required. // Note setOff and SliderBase::setValue are also available. //void setValueState(double v, bool off = false, ConversionMode mode = ConvertDefault); QString text() const { return _text; } void setText(const QString& txt); // QColor activeColor() const { return _activeColor; } // void setActiveColor(const QColor& c) { _activeColor = c; update(); } void setBgColor(const QColor& c) { _bgColor = c; update(); } void setBgActiveColor(const QColor& c) { _bgActiveColor = c; update(); } void setBorderColor(const QColor& c) { _borderColor = c; update(); } void setFontColor(const QColor& c) { _fontColor = c; update(); } void setFontActiveColor(const QColor& c) { _fontActiveColor = c; update(); } bool style3d() const { return _style3d; } void setStyle3d(const bool style3d) { _style3d = style3d; } int radius() const { return _radius; } void setRadius(const int radius) { _radius = radius; } int fontPointMin() const { return _fontPointMin; } void setFontPointMin(int point); bool fontIgnoreWidth() const { return _fontIgnoreWidth; } bool fontIgnoreHeight() const { return _fontIgnoreHeight; } void setFontIgnoreDimensions(bool ignoreHeight, bool ignoreWidth = false); }; ////--------------------------------------------------------- //// ElidedTextLabel ////--------------------------------------------------------- //class ElidedTextLabel : public QFrame //{ // Q_OBJECT // // FIXME: Found some problems here: If this was enabled, text() was // // intermittently including accelerator key characters, causing // // for example midi controller stream text rapid flickering // // between normal and accelerator key text. // // Maybe it should be const QString& ? // //Q_PROPERTY(QString text READ text WRITE setText) // //Q_PROPERTY(Qt::TextElideMode elideMode READ elideMode WRITE setElideMode) // private: // int _id; // bool _hasOffMode; // bool _off; // Qt::TextElideMode _elideMode; // Qt::Alignment _alignment; // QString _text; // QString _tooltipText; // // Whether the mouse is over the entire control. // bool _hovered; // bool autoAdjustFontSize(); // protected: // virtual void paintEvent(QPaintEvent*); // virtual void mouseMoveEvent(QMouseEvent*); // virtual void leaveEvent(QEvent*); // virtual void keyPressEvent(QKeyEvent*); // signals: // void returnPressed(QPoint p, int id, Qt::KeyboardModifiers keys); // public: // explicit ElidedTextLabel( // QWidget* parent = 0, // const char* name = 0, // Qt::WindowFlags flags = Qt::Widget // ); // explicit ElidedTextLabel( // const QString& text, // QWidget* parent = 0, // const char* name = 0, // Qt::WindowFlags flags = Qt::Widget // ); // virtual QSize sizeHint() const; // int id() const { return _id; } // void setId(int i) { _id = i; } // Qt::TextElideMode elideMode() const { return _elideMode; } // void setElideMode(Qt::TextElideMode mode) { _elideMode = mode; update(); } // Qt::Alignment alignment() const { return _alignment; } // void setAlignment(Qt::Alignment align) { _alignment = align; update(); } // bool hasOffMode() const { return _hasOffMode; } // void setHasOffMode(bool v); // bool isOff() const { return _off; } // // Sets the off state and emits valueStateChanged signal if required. // void setOff(bool v); // // Both value and off state changed combined into one setter. // // By default it is assumed that setting a value naturally implies resetting the 'off' state to false. // // Emits valueChanged and valueStateChanged signals if required. // // Note setOff and SliderBase::setValue are also available. // //void setValueState(double v, bool off = false, ConversionMode mode = ConvertDefault); // QString text() const { return _text; } // void setText(const QString& txt); // // This tooltip text is appened to the normal text, to form a compound tooltip. // QString tooltipText() const { return _tooltipText; } // void setTooltipText(const QString& txt); // // Whether the mouse is over the entire control. // bool hovered() const { return _hovered; } //}; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/fdialogbuttons.ui000066400000000000000000000176341404105311000217740ustar00rootroot00000000000000 FileDialogButtons 0 0 109 370 0 0 fdialogbuttons 1 1 1 1 1 QFrame::StyledPanel QFrame::Raised 0 1 1 1 1 1 1 100 40 Home 24 24 true Qt::ToolButtonTextBesideIcon false Qt::NoArrow 100 40 Global 24 24 true Qt::ToolButtonTextBesideIcon 100 40 User 24 24 true Qt::ToolButtonTextBesideIcon 100 40 Project 24 24 true Qt::ToolButtonTextBesideIcon Qt::Vertical QSizePolicy::Expanding 78 119 QFrame::StyledPanel QFrame::Raised 1 1 1 1 1 8 read Midi Port Configuration true true QFrame::StyledPanel QFrame::Raised 1 1 1 1 1 8 write window states true muse-4.0.0/src/muse/components/filedialog.cpp000066400000000000000000000531441404105311000212100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: filedialog.cpp,v 1.3.2.3 2005/06/19 06:32:07 lunar_shuttle Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include "icons.h" #include "filedialog.h" #include "../globals.h" #include "gconfig.h" #include "helper.h" // In response to github issue 646 "Select Directory" dialogs freezes MusE: // A fix for this bug: QFileDialog freezes with a Gnome environment: // https://bugreports.qt.io/browse/QTBUG-59184?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel&showAll=true // According to the report, a user said he fixed it by NOT supplying a parent. So here we go, until further notice: #define MUSE_FILEDIALOG_NO_PARENT namespace MusEGui { MFileDialog::ViewType MFileDialog::lastViewUsed = GLOBAL_VIEW; //QString MFileDialog::lastUserDir = ""; //QString MFileDialog::lastGlobalDir = ""; //--------------------------------------------------------- // createDir // return true if dir could not created //--------------------------------------------------------- static bool createDir(const QString& s) { QString sl("/"); // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 QStringList l = s.split(sl, Qt::SkipEmptyParts); #else QStringList l = s.split(sl, QString::SkipEmptyParts); #endif QString path(sl); QDir dir; for (QStringList::Iterator it = l.begin(); it != l.end(); ++it) { dir.setPath(path); if (!QDir(path + sl + *it).exists()) { if (!dir.mkdir(*it)) { printf("mkdir failed: %s %s\n", path.toLatin1().constData(), (*it).toLatin1().constData()); return true; } } path += sl; path += *it; } return false; } //--------------------------------------------------------- // testDirCreate // return true if dir does not exist //--------------------------------------------------------- static bool testDirCreate(QWidget* parent, const QString& path) { QDir dir(path); if (!dir.exists()) { if(QMessageBox::information(parent, QWidget::tr("MusE: get file name"), QWidget::tr("The directory\n%1\ndoes not exist.\nCreate it?").arg(path), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) { return true; } if (createDir(path)) { QMessageBox::critical(parent, QWidget::tr("MusE: create directory"), QWidget::tr("Creating dir failed")); return true; } } return false; } //--------------------------------------------------------- // globalToggled //--------------------------------------------------------- void MFileDialog::globalToggled(bool flag) { if (flag) { buttons.readMidiPortsButton->setChecked(false); readMidiPortsSaved = false; if (lastGlobalDir.isEmpty()) lastGlobalDir = MusEGlobal::museGlobalShare + QString("/") + baseDir; // Initialize if first time setDirectory(lastGlobalDir); lastViewUsed = GLOBAL_VIEW; } } //--------------------------------------------------------- // homeToggled //--------------------------------------------------------- void MFileDialog::homeToggled(bool flag) { if (flag) { buttons.readMidiPortsButton->setChecked(true); readMidiPortsSaved = true; setDirectory(QDir::home()); lastViewUsed = HOME_VIEW; } } //--------------------------------------------------------- // userToggled //--------------------------------------------------------- void MFileDialog::userToggled(bool flag) { if (flag) { buttons.readMidiPortsButton->setChecked(true); readMidiPortsSaved = true; if (lastUserDir.isEmpty()) { lastUserDir = MusEGlobal::configPath + QString("/") + baseDir; // Initialize if first time } if (testDirCreate(this, lastUserDir)) setDirectory(MusEGlobal::configPath); else setDirectory(lastUserDir); lastViewUsed = USER_VIEW; } } //--------------------------------------------------------- // projectToggled //--------------------------------------------------------- void MFileDialog::projectToggled(bool flag) { if (flag) { buttons.readMidiPortsButton->setChecked(true); readMidiPortsSaved = true; QString s; if (MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) { // if project path is uninitialized, meaning it is still set to museProjectInitPath. // then project path is set to current pwd instead. //s = QString(getcwd(0,0)) + QString("/"); s = MusEGlobal::config.projectBaseFolder; } else s = MusEGlobal::museProject + QString("/"); // + baseDir; if (testDirCreate(this, s)) setDirectory(MusEGlobal::museProject); else setDirectory(s); lastViewUsed = PROJECT_VIEW; } } void MFileDialog::fileChanged(const QString& path) { bool is_mid = path.endsWith(".mid", Qt::CaseInsensitive) || path.endsWith(".midi", Qt::CaseInsensitive) || path.endsWith(".kar", Qt::CaseInsensitive); if (is_mid) { readMidiPortsSaved=buttons.readMidiPortsButton->isChecked(); buttons.readMidiPortsButton->setEnabled(false); buttons.readMidiPortsButton->setChecked(false); } else { if (!buttons.readMidiPortsButton->isEnabled()) { buttons.readMidiPortsButton->setEnabled(true); buttons.readMidiPortsButton->setChecked(readMidiPortsSaved); } } } //--------------------------------------------------------- // MFileDialog //--------------------------------------------------------- MFileDialog::MFileDialog(const QString& dir, const QString& filter, QWidget* #ifdef MUSE_FILEDIALOG_NO_PARENT /*parent*/, #else parent, #endif bool writeFlag) : QFileDialog( #ifdef MUSE_FILEDIALOG_NO_PARENT nullptr, #else parent, #endif QString(), QString("."), filter) { setOption(QFileDialog::DontUseNativeDialog); readMidiPortsSaved = true; showButtons = false; lastUserDir = ""; lastGlobalDir = ""; if (dir.length() > 0 && dir[0] == QChar('/')) { setDirectory(dir); } else { // We replace the original sidebar widget with our 3-button widget QLayout* mainlayout = this->layout(); QSplitter* spl = (QSplitter*)mainlayout->itemAt(2)->widget(); QWidget* original_sidebarwidget = spl->widget(0); original_sidebarwidget->setVisible(false); baseDir = dir; showButtons = true; spl->insertWidget(0,&buttons); // Qt >= 4.6 allows us to select icons from the theme buttons.globalButton->setIcon(*globalIcon); buttons.userButton->setIcon(*userIcon); buttons.homeButton->setIcon(*userIcon); buttons.projectButton->setIcon(*projectIcon); buttons.globalButton->setAutoExclusive(true); buttons.userButton->setAutoExclusive(true); buttons.projectButton->setAutoExclusive(true); buttons.homeButton->setAutoExclusive(true); connect(buttons.globalButton, SIGNAL(toggled(bool)), this, SLOT(globalToggled(bool))); connect(buttons.userButton, SIGNAL(toggled(bool)), this, SLOT(userToggled(bool))); connect(buttons.projectButton, SIGNAL(toggled(bool)), this, SLOT(projectToggled(bool))); connect(buttons.homeButton, SIGNAL(toggled(bool)), this, SLOT(homeToggled(bool))); connect(this, SIGNAL(directoryEntered(const QString&)), SLOT(directoryChanged(const QString&))); connect(this, SIGNAL(currentChanged(const QString&)), SLOT(fileChanged(const QString&))); if (writeFlag) { setAcceptMode(QFileDialog::AcceptSave); buttons.globalButton->setEnabled(false); switch (lastViewUsed) { case GLOBAL_VIEW: case PROJECT_VIEW: buttons.projectButton->setChecked(true); // Let toggled be called. Don't block these... break; case USER_VIEW: buttons.userButton->setChecked(true); break; case HOME_VIEW: buttons.homeButton->setChecked(true); break; } } else { switch (lastViewUsed) { case GLOBAL_VIEW: buttons.globalButton->setChecked(true); break; case PROJECT_VIEW: buttons.projectButton->setChecked(true); break; case USER_VIEW: buttons.userButton->setChecked(true); break; case HOME_VIEW: buttons.homeButton->setChecked(true); break; } } buttons.readMidiPortsGroup->setVisible(false); buttons.writeWinStateGroup->setVisible(false); } } //--------------------------------------------------------- // MFileDialog::directoryChanged //--------------------------------------------------------- void MFileDialog::directoryChanged(const QString&) { ViewType currentView = GLOBAL_VIEW; QDir ndir = directory(); ///QString newdir = ndir.absolutePath().toLatin1(); QString newdir = ndir.absolutePath(); if (buttons.projectButton->isChecked()) currentView = PROJECT_VIEW; else if (buttons.userButton->isChecked()) currentView = USER_VIEW; switch (currentView) { case GLOBAL_VIEW: lastGlobalDir = newdir; break; case USER_VIEW: lastUserDir = newdir; break; case PROJECT_VIEW: // Do nothing default: break; } } //--------------------------------------------------------- // getOpenFileName //--------------------------------------------------------- QString getOpenFileName(const QString &startWith, const char** filters_chararray, QWidget* parent, const QString& name, bool* doReadMidiPorts, MFileDialog::ViewType viewType) { QStringList filters = localizedStringListFromCharArray(filters_chararray, "file_patterns"); MFileDialog *dlg = new MFileDialog(startWith, QString(), parent, false); dlg->setNameFilters(filters); dlg->setWindowTitle(name); if (doReadMidiPorts) dlg->buttons.readMidiPortsGroup->setVisible(true); // Allow overrides. FIXME - some redundancy in MFileDialog ctor. Make this better. if (viewType == MFileDialog::GLOBAL_VIEW) dlg->buttons.globalButton->setChecked(true); // Let toggled be called. Don't block these... else if (viewType == MFileDialog::PROJECT_VIEW) dlg->buttons.projectButton->setChecked(true); else if (viewType == MFileDialog::USER_VIEW) dlg->buttons.userButton->setChecked(true); dlg->setFileMode(QFileDialog::ExistingFile); QStringList files; QString result; if (dlg->exec() == QDialog::Accepted) { files = dlg->selectedFiles(); if (!files.isEmpty()) result = files[0]; if (doReadMidiPorts) *doReadMidiPorts = dlg->buttons.readMidiPortsButton->isChecked(); } delete dlg; return result; } //--------------------------------------------------------- // getSaveFileName //--------------------------------------------------------- QString getSaveFileName(const QString &startWith, const char** filters_chararray, QWidget* parent, const QString& name, bool* writeWinState, MFileDialog::ViewType viewType) { QStringList filters = localizedStringListFromCharArray(filters_chararray, "file_patterns"); MFileDialog *dlg = new MFileDialog(startWith, QString(), parent, true); dlg->setNameFilters(filters); dlg->setWindowTitle(name); dlg->setFileMode(QFileDialog::AnyFile); if (writeWinState) { dlg->buttons.writeWinStateGroup->setVisible(true); dlg->buttons.writeWinStateButton->setChecked(*writeWinState); } // global view is inactive for saving if (viewType == MFileDialog::PROJECT_VIEW) dlg->buttons.projectButton->setChecked(true); else if (viewType == MFileDialog::USER_VIEW) dlg->buttons.userButton->setChecked(true); QStringList files; QString result; if (dlg->exec() == QDialog::Accepted) { files = dlg->selectedFiles(); if (!files.isEmpty()) result = files[0]; if (writeWinState) *writeWinState = dlg->buttons.writeWinStateButton->isChecked(); } // Added by T356. if(!result.isEmpty()) { QString filt = dlg->selectedNameFilter(); filt = getFilterExtension(filt); // Do we have a valid extension? if(!filt.isEmpty()) { // If the rightmost characters of the filename do not already contain // the extension, add the extension to the filename. //if(result.right(filt.length()) != filt) if(!result.endsWith(filt)) result += filt; } else { // No valid extension, or just * was given. Although it would be nice to allow no extension // or any desired extension by commenting this section out, it's probably not a good idea to do so. // // NOTE: Most calls to this routine getSaveFileName() are followed by fileOpen(), // which can tack on its own extension, but only if the *complete* extension is blank. // So there is some overlap going on. Enabling this actually stops that action, // but only if there are no errors in the list of filters. fileOpen() will act as a 'catchall'. // // Force the filter list to the first one (the preferred one), and then get the filter. dlg->selectNameFilter(dlg->nameFilters().at(0)); filt = dlg->selectedNameFilter(); filt = getFilterExtension(filt); // Do we have a valid extension? if(!filt.isEmpty()) { // If the rightmost characters of the filename do not already contain // the extension, add the extension to the filename. //if(result.right(filt.length()) != filt) if(!result.endsWith(filt)) result += filt; } } } delete dlg; return result; } //--------------------------------------------------------- // getImageFileName //--------------------------------------------------------- QString getImageFileName(const QString& startWith, const char** filters_chararray, QWidget* parent, const QString& name) { QStringList filters = localizedStringListFromCharArray(filters_chararray, "file_patterns"); QString initialSelection; QString* workingDirectory = new QString(QDir::currentPath()); if (!startWith.isEmpty() ) { QFileInfo fi(startWith); if (fi.exists() && fi.isDir()) { *workingDirectory = startWith; } else if (fi.exists() && fi.isFile()) { *workingDirectory = fi.absolutePath(); initialSelection = fi.absoluteFilePath(); } } MFileDialog *dlg = new MFileDialog(*workingDirectory, QString(), parent); /* ORCAN - disable preview for now. It is not available in qt4. We will need to implement it ourselves. dlg->setContentsPreviewEnabled(true); ContentsPreview* preview = new ContentsPreview(dlg); dlg->setContentsPreview(preview, preview); dlg->setPreviewMode(QFileDialog::Contents); */ dlg->setWindowTitle(name); dlg->setNameFilters(filters); dlg->setFileMode(QFileDialog::ExistingFile); QStringList files; QString result; if (!initialSelection.isEmpty()) dlg->selectFile( initialSelection); if (dlg->exec() == QDialog::Accepted) { files = dlg->selectedFiles(); if (!files.isEmpty()) result = files[0]; } delete dlg; return result; } //--------------------------------------------------------- // fileOpen // opens file "name" with extension "ext" in mode "mode" // handles "name.ext.bz2" and "name.ext.gz" // // mode = "r" or "w" // popenFlag set to true on return if file was opened // with popen() (and therefore must be closed // with pclose()) // noError show no error if file was not found in "r" // mode. Has no effect in "w" mode // overwriteWarning // warn in "w" mode, if file exists //--------------------------------------------------------- FILE* fileOpen(QWidget* parent, QString name, const QString& ext, const char* mode, bool& popenFlag, bool noError, bool overwriteWarning) { QFileInfo info(name); QString zip; popenFlag = false; if (info.completeSuffix() == "") { name += ext; info.setFile(name); } else if (info.suffix() == "gz") { popenFlag = true; zip = QString("gzip"); } else if (info.suffix() == "bz2") { popenFlag = true; zip = QString("bzip2"); } if (strcmp(mode,"w") == 0 && overwriteWarning && info.exists()) { QString s(QWidget::tr("File\n%1\nexists. Overwrite?").arg(name)); /* int rv = QMessageBox::warning(parent, QWidget::tr("MusE: write"), s, QMessageBox::Save | QMessageBox::Cancel, QMessageBox::Save); switch(rv) { case 0: // overwrite break; case 1: // quit return 0; } */ if(QMessageBox::warning(parent, QWidget::tr("MusE: write"), s, QMessageBox::Save | QMessageBox::Cancel, QMessageBox::Save) != QMessageBox::Save) return 0; } FILE* fp = 0; if (popenFlag) { if (strcmp(mode, "r") == 0) zip += QString(" -d < \""); else zip += QString(" > \""); zip = zip + name + QString("\""); fp = popen(zip.toLocal8Bit().constData(), mode); } else { fp = fopen(name.toLocal8Bit().constData(), mode); } if (fp == 0 && !noError) { QString s(QWidget::tr("Open File\n%1\nfailed: %2").arg(name).arg(strerror(errno))); QMessageBox::critical(parent, QWidget::tr("MusE: Open File"), s); return 0; } return fp; } //--------------------------------------------------------- // MFile //--------------------------------------------------------- MFile::MFile(const QString& _path, const QString& _ext) : path(_path), ext(_ext) { f = 0; isPopen = false; } MFile::~MFile() { if (f) { if (isPopen) pclose(f); else fclose(f); } } //--------------------------------------------------------- // open //--------------------------------------------------------- FILE* MFile::open(const char* mode, const char** patterns_chararray, QWidget* parent, bool noError, bool warnIfOverwrite, const QString& caption) { QString name; if (strcmp(mode, "r") == 0) name = getOpenFileName(path, patterns_chararray, parent, caption, 0); else name = getSaveFileName(path, patterns_chararray, parent, caption); if (name.isEmpty()) return 0; f = fileOpen(parent, name, ext, mode, isPopen, noError, warnIfOverwrite); return f; } } // namespace MusEGui muse-4.0.0/src/muse/components/filedialog.h000066400000000000000000000071051404105311000206510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: filedialog.h,v 1.2.2.2 2008/01/19 13:33:46 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __FILEDIALOG_H__ #define __FILEDIALOG_H__ #include #include "ui_fdialogbuttons.h" namespace MusEGui { //--------------------------------------------------------- // FileDialogButtonsWidget // Wrapper around Ui::FileDialogButtons //--------------------------------------------------------- class FileDialogButtonsWidget : public QWidget, public Ui::FileDialogButtons { Q_OBJECT public: FileDialogButtonsWidget(QWidget *parent = 0) : QWidget(parent) { setupUi(this); } }; //--------------------------------------------------------- // MFileDialog //--------------------------------------------------------- class MFileDialog : public QFileDialog { Q_OBJECT QString lastUserDir, lastGlobalDir; bool showButtons; QString baseDir; bool readMidiPortsSaved; private slots: void directoryChanged(const QString& directory); void fileChanged(const QString&); public slots: void globalToggled(bool); void userToggled(bool); void projectToggled(bool); void homeToggled(bool); public: enum ViewType { GLOBAL_VIEW, PROJECT_VIEW, USER_VIEW, HOME_VIEW }; static ViewType lastViewUsed; FileDialogButtonsWidget buttons; MFileDialog(const QString& dir, const QString& filter = QString(), QWidget* parent = 0, bool writeFlag = false); }; QString getSaveFileName(const QString& startWith, const char** filters, QWidget* parent, const QString& name, bool* writeWinState=NULL, MFileDialog::ViewType viewType = MFileDialog::PROJECT_VIEW); QString getOpenFileName(const QString& startWith, const char** filters, QWidget* parent, const QString& name, bool* doReadMidiPorts, MFileDialog::ViewType viewType = MFileDialog::PROJECT_VIEW); QString getImageFileName(const QString& startWith, const char** filters, QWidget* parent, const QString& name); FILE* fileOpen(QWidget*, QString, const QString&, const char*, bool&, bool = false, bool = false); //--------------------------------------------------------- // MFile // "Muse" File //--------------------------------------------------------- class MFile { bool isPopen; FILE* f; QString path; QString ext; public: MFile(const QString& path, const QString& ext); ~MFile(); FILE* open(const char* mode, const char** patterns, QWidget* parent, bool noError, bool warnIfOverwrite, const QString& caption); }; } // namespace MusEGui #endif // __FILEDIALOG_H__ muse-4.0.0/src/muse/components/genset.cpp000066400000000000000000000672251404105311000204030ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: genset.cpp,v 1.7.2.8 2009/12/01 03:52:40 terminator356 Exp $ // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 #include "genset.h" #include "app.h" #include "gconfig.h" #include "midiseq.h" #include "globals.h" #include "icons.h" #include "helper.h" #include "filedialog.h" #include "al/al.h" #include "undo.h" #include "song.h" #include "operations.h" #include "audio.h" #include "audio_converter_settings.h" #include "audio_convert/audio_converter_plugin.h" #include "audio_convert/audio_converter_settings_group.h" // Forwards from header: #include #include #include namespace MusEGui { static int rtcResolutions[] = { 1024, 2048, 4096, 8192, 16384, 32768 }; static int divisions[] = { // REMOVE Tim. div. Changed // 48, 96, 192, 384, 768, 1536, 3072, 6144, 12288 48, 96, 120, 192, 240, 384, 480, 768, 960, 1536, 1920, 3072, 3840, 6144, 7680, 12288, 15360 }; static int selectableAudioBufSizes[] = { 16, 32, 64, 128, 256, 512, 1024, 2048 }; static unsigned long minControlProcessPeriods[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 }; //--------------------------------------------------------- // GlobalSettingsConfig //--------------------------------------------------------- GlobalSettingsConfig::GlobalSettingsConfig(QWidget* parent) : QDialog(parent) { setupUi(this); startSongGroup = new QButtonGroup(this); startSongGroup->addButton(startLastButton, 0); startSongGroup->addButton(startEmptyButton, 1); startSongGroup->addButton(startSongButton, 2); recDrumGroup = new QButtonGroup(this); recDrumGroup->addButton(recordAllButton, MusECore::REC_ALL); recDrumGroup->addButton(dontRecHiddenButton, MusECore::DONT_REC_HIDDEN); recDrumGroup->addButton(dontRecMutedButton, MusECore::DONT_REC_MUTED); recDrumGroup->addButton(dontRecBothButton, MusECore::DONT_REC_MUTED_OR_HIDDEN); updateSettings(); projDirOpenToolButton->setIcon(*fileopenSVGIcon); connect(projDirOpenToolButton, SIGNAL(clicked()), SLOT(browseProjDir())); startSongFileOpenToolButton->setIcon(*fileopenSVGIcon); connect(startSongFileOpenToolButton, SIGNAL(clicked()), SLOT(browseStartSongFile())); startSongResetToolButton->setIcon(*undoSVGIcon); connect(startSongResetToolButton, SIGNAL(clicked()), SLOT(startSongReset())); connect(applyButton, SIGNAL(clicked()), SLOT(apply())); connect(okButton, SIGNAL(clicked()), SLOT(ok())); connect(cancelButton, SIGNAL(clicked()), SLOT(cancel())); connect(pluginPathAdd, SIGNAL(clicked()), SLOT(addPluginPath())); connect(pluginPathEdit, SIGNAL(clicked()), SLOT(editPluginPath())); connect(pluginPathRemove, SIGNAL(clicked()), SLOT(removePluginPath())); connect(pluginPathMoveUp, SIGNAL(clicked()), SLOT(movePluginPathUp())); connect(pluginPathMoveDown, SIGNAL(clicked()), SLOT(movePluginPathDown())); connect(audioConvertersButton, SIGNAL(clicked()), SLOT(showAudioConverterSettings())); connect(deviceAudioBackendComboBox, SIGNAL(currentIndexChanged(int)), SLOT(updateBackendDeviceSettings())); for (int i = 0; i < MusEGlobal::numRtAudioDevices; i++){ deviceAudioBackendComboBox->addItem(MusEGlobal::selectableAudioBackendDevices[i],i); } #ifndef HAVE_RTAUDIO deviceAudioBackendComboBox->setDisabled(true); #endif for (int i = 0; i < MusEGlobal::numAudioSampleRates; i++){ deviceAudioRate->addItem(QString::number(MusEGlobal::selectableAudioSampleRates[i]),i); } updateBackendDeviceSettings(); } void GlobalSettingsConfig::updateBackendDeviceSettings() { int currentDevice = deviceAudioBackendComboBox->currentIndex(); if (currentDevice == MusEGlobal::JackAudio) { deviceAudioSize->setDisabled(true); deviceAudioRate->setDisabled(true); } else { deviceAudioSize->setDisabled(false); deviceAudioRate->setDisabled(false); } } //--------------------------------------------------------- // updateSettings //--------------------------------------------------------- void GlobalSettingsConfig::updateSettings() { for (unsigned i = 0; i < sizeof(rtcResolutions)/sizeof(*rtcResolutions); ++i) { if (rtcResolutions[i] == MusEGlobal::config.rtcTicks) { rtcResolutionSelect->setCurrentIndex(i); break; } } for (unsigned i = 0; i < sizeof(divisions)/sizeof(*divisions); ++i) { if (divisions[i] == MusEGlobal::config.division) { midiDivisionSelect->setCurrentIndex(i); break; } } for (unsigned i = 0; i < sizeof(selectableAudioBufSizes)/sizeof(*selectableAudioBufSizes); ++i) { if (selectableAudioBufSizes[i] == MusEGlobal::config.deviceAudioBufSize) { deviceAudioSize->setCurrentIndex(i); break; } } for (int i = 0; i < MusEGlobal::numAudioSampleRates; ++i) { if (MusEGlobal::selectableAudioSampleRates[i] == MusEGlobal::config.deviceAudioSampleRate) { deviceAudioRate->setCurrentIndex(i); break; } } for (unsigned i = 0; i < sizeof(minControlProcessPeriods)/sizeof(*minControlProcessPeriods); ++i) { if (minControlProcessPeriods[i] == MusEGlobal::config.minControlProcessPeriod) { minControlProcessPeriodComboBox->setCurrentIndex(i); break; } } autoSaveCheckBox->setChecked(MusEGlobal::config.autoSave); scrollableSubmenusCheckbox->setChecked(MusEGlobal::config.scrollableSubMenus); liveWaveUpdateCheckBox->setChecked(MusEGlobal::config.liveWaveUpdate); preferKnobsVsSlidersCheckBox->setChecked(MusEGlobal::config.preferKnobsVsSliders); showControlValuesCheckBox->setChecked(MusEGlobal::config.showControlValues); monitorOnRecordCheckBox->setChecked(MusEGlobal::config.monitorOnRecord); lineEditStyleHackCheckBox->setChecked(MusEGlobal::config.lineEditStyleHack); showNoteNamesCheckBox->setChecked(MusEGlobal::config.showNoteNamesInPianoRoll); showNoteTooltipsCheckBox->setChecked(MusEGlobal::config.showNoteTooltips); showTimeScaleBeatNumbersCheckBox->setChecked(MusEGlobal::config.showTimeScaleBeatNumbers); preferMidiVolumeDbCheckBox->setChecked(MusEGlobal::config.preferMidiVolumeDb); warnIfBadTimingCheckBox->setChecked(MusEGlobal::config.warnIfBadTiming); warnOnFileVersionsCheckBox->setChecked(MusEGlobal::config.warnOnFileVersions); midiSendInit->setChecked(MusEGlobal::config.midiSendInit); midiWarnInitPending->setChecked(MusEGlobal::config.warnInitPending); midiSendCtlDefaults->setChecked(MusEGlobal::config.midiSendCtlDefaults); sendNullParamsCB->setChecked(MusEGlobal::config.midiSendNullParameters); optimizeControllersCB->setChecked(MusEGlobal::config.midiOptimizeControllers); guiRefreshSelect->setValue(MusEGlobal::config.guiRefresh); minSliderSelect->setValue(int(MusEGlobal::config.minSlider)); minMeterSelect->setValue(MusEGlobal::config.minMeter); freewheelCheckBox->setChecked(MusEGlobal::config.freewheelMode); denormalCheckBox->setChecked(MusEGlobal::config.useDenormalBias); outputLimiterCheckBox->setChecked(MusEGlobal::config.useOutputLimiter); vstInPlaceCheckBox->setChecked(MusEGlobal::config.vstInPlace); revertPluginNativeGUIScalingCheckBox->setChecked(MusEGlobal::config.noPluginScaling); // openMDIWinMaximizedCheckBox->setChecked(MusEGlobal::config.openMDIWinMaximized); keepTransportWindowOnTopCheckBox->setChecked(MusEGlobal::config.keepTransportWindowOnTop); showStatusBarCheckBox->setChecked(MusEGlobal::config.showStatusBar); deviceAudioBackendComboBox->setCurrentIndex(MusEGlobal::config.deviceAudioBackend); enableLatencyCorrectionButton->setChecked(MusEGlobal::config.enableLatencyCorrection); latencyInBranchUntermButton->setChecked(MusEGlobal::config.correctUnterminatedInBranchLatency); latencyOutBranchUntermButton->setChecked(MusEGlobal::config.correctUnterminatedOutBranchLatency); latencyProjectCommonButton->setChecked(MusEGlobal::config.commonProjectLatency); latencyMonitorAffectingButton->setChecked(MusEGlobal::config.monitoringAffectsLatency); projDirEntry->setText(MusEGlobal::config.projectBaseFolder); startSongEntry->setText(MusEGlobal::config.startSong == "" ? "" : MusEGlobal::config.startSong); startSongGroup->button(MusEGlobal::config.startMode)->setChecked(true); readMidiConfigFromSongCheckBox->setChecked(MusEGlobal::config.startSongLoadConfig); recDrumGroup->button(MusEGlobal::config.newDrumRecordCondition)->setChecked(true); showSplash->setChecked(MusEGlobal::config.showSplashScreen); showDidYouKnow->setChecked(MusEGlobal::config.showDidYouKnow); externalWavEditorSelect->setText(MusEGlobal::config.externalWavEditor); oldStyleStopCheckBox->setChecked(MusEGlobal::config.useOldStyleStopShortCut); moveArmedCheckBox->setChecked(MusEGlobal::config.moveArmedCheckBox); projectSaveCheckBox->setChecked(MusEGlobal::config.useProjectSaveDialog); popsDefStayOpenCheckBox->setChecked(MusEGlobal::config.popupsDefaultStayOpen); lmbDecreasesCheckBox->setChecked(MusEGlobal::config.leftMouseButtonCanDecrease); // rangeMarkerWithoutMMBCheckBox->setChecked(MusEGlobal::config.rangeMarkerWithoutMMB); smartFocusCheckBox->setChecked(MusEGlobal::config.smartFocus); borderlessMouseCheckBox->setChecked(MusEGlobal::config.borderlessMouse); velocityPerNoteCheckBox->setChecked(MusEGlobal::config.velocityPerNote); addHiddenCheckBox->setChecked(MusEGlobal::config.addHiddenTracks); unhideTracksCheckBox->setChecked(MusEGlobal::config.unhideTracks); trackHeight->setValue(MusEGlobal::config.trackHeight); lv2UiBehaviorComboBox->setCurrentIndex(static_cast(MusEGlobal::config.lv2UiBehavior)); pluginLadspaPathList->clear(); pluginLadspaPathList->addItems(MusEGlobal::config.pluginLadspaPathList); pluginDssiPathList->clear(); pluginDssiPathList->addItems(MusEGlobal::config.pluginDssiPathList); pluginVstPathList->clear(); pluginVstPathList->addItems(MusEGlobal::config.pluginVstPathList); pluginLinuxVstPathList->clear(); pluginLinuxVstPathList->addItems(MusEGlobal::config.pluginLinuxVstPathList); pluginLv2PathList->clear(); pluginLv2PathList->addItems(MusEGlobal::config.pluginLv2PathList); pluginRescanButton->setChecked(MusEGlobal::config.pluginCacheTriggerRescan); cbTabPianoroll->setChecked(TopWin::_openTabbed[TopWin::PIANO_ROLL]); cbTabDrum->setChecked(TopWin::_openTabbed[TopWin::DRUM]); cbTabWave->setChecked(TopWin::_openTabbed[TopWin::WAVE]); cbTabScore->setChecked(TopWin::_openTabbed[TopWin::SCORE]); cbTabMaster->setChecked(TopWin::_openTabbed[TopWin::MASTER]); } //--------------------------------------------------------- // showEvent //--------------------------------------------------------- void GlobalSettingsConfig::showEvent(QShowEvent* e) { QDialog::showEvent(e); updateSettings(); } //--------------------------------------------------------- // apply //--------------------------------------------------------- void GlobalSettingsConfig::apply() { int rtcticks = rtcResolutionSelect->currentIndex(); MusEGlobal::config.guiRefresh = guiRefreshSelect->value(); MusEGlobal::config.minSlider = minSliderSelect->value(); MusEGlobal::config.minMeter = minMeterSelect->value(); MusEGlobal::config.freewheelMode = freewheelCheckBox->isChecked(); MusEGlobal::config.useDenormalBias = denormalCheckBox->isChecked(); MusEGlobal::config.useOutputLimiter = outputLimiterCheckBox->isChecked(); MusEGlobal::config.vstInPlace = vstInPlaceCheckBox->isChecked(); MusEGlobal::config.rtcTicks = rtcResolutions[rtcticks]; MusEGlobal::config.warnIfBadTiming = warnIfBadTimingCheckBox->isChecked(); MusEGlobal::config.warnOnFileVersions = warnOnFileVersionsCheckBox->isChecked(); MusEGlobal::config.midiSendInit = midiSendInit->isChecked(); MusEGlobal::config.warnInitPending = midiWarnInitPending->isChecked(); MusEGlobal::config.midiSendCtlDefaults = midiSendCtlDefaults->isChecked(); MusEGlobal::config.midiSendNullParameters = sendNullParamsCB->isChecked(); MusEGlobal::config.midiOptimizeControllers = optimizeControllersCB->isChecked(); MusEGlobal::config.projectBaseFolder = projDirEntry->text(); MusEGlobal::config.enableLatencyCorrection = enableLatencyCorrectionButton->isChecked(); MusEGlobal::config.correctUnterminatedInBranchLatency = latencyInBranchUntermButton->isChecked(); MusEGlobal::config.correctUnterminatedOutBranchLatency = latencyOutBranchUntermButton->isChecked(); MusEGlobal::config.commonProjectLatency = latencyProjectCommonButton->isChecked(); MusEGlobal::config.monitoringAffectsLatency = latencyMonitorAffectingButton->isChecked(); MusEGlobal::config.startSong = startSongEntry->text() == "" ? "" : startSongEntry->text(); MusEGlobal::config.startMode = startSongGroup->checkedId(); MusEGlobal::config.startSongLoadConfig = readMidiConfigFromSongCheckBox->isChecked(); MusEGlobal::config.newDrumRecordCondition = MusECore::newDrumRecordCondition_t(recDrumGroup->checkedId()); int das = deviceAudioSize->currentIndex(); MusEGlobal::config.deviceAudioBufSize = selectableAudioBufSizes[das]; MusEGlobal::config.deviceAudioSampleRate = MusEGlobal::selectableAudioSampleRates[deviceAudioRate->currentIndex()]; MusEGlobal::config.deviceAudioBackend = deviceAudioBackendComboBox->currentIndex(); int mcp = minControlProcessPeriodComboBox->currentIndex(); MusEGlobal::config.minControlProcessPeriod = minControlProcessPeriods[mcp]; int div = midiDivisionSelect->currentIndex(); const int new_div = divisions[div]; MusEGlobal::config.autoSave = autoSaveCheckBox->isChecked(); MusEGlobal::config.scrollableSubMenus = scrollableSubmenusCheckbox->isChecked(); MusEGlobal::config.liveWaveUpdate = liveWaveUpdateCheckBox->isChecked(); MusEGlobal::config.preferKnobsVsSliders = preferKnobsVsSlidersCheckBox->isChecked(); MusEGlobal::config.showControlValues = showControlValuesCheckBox->isChecked(); MusEGlobal::config.monitorOnRecord = monitorOnRecordCheckBox->isChecked(); MusEGlobal::config.lineEditStyleHack = lineEditStyleHackCheckBox->isChecked(); MusEGlobal::config.showNoteNamesInPianoRoll = showNoteNamesCheckBox->isChecked(); MusEGlobal::config.showNoteTooltips = showNoteTooltipsCheckBox->isChecked(); MusEGlobal::config.showTimeScaleBeatNumbers = showTimeScaleBeatNumbersCheckBox->isChecked(); MusEGlobal::config.preferMidiVolumeDb = preferMidiVolumeDbCheckBox->isChecked(); MusEGlobal::config.showSplashScreen = showSplash->isChecked(); MusEGlobal::config.showDidYouKnow = showDidYouKnow->isChecked(); MusEGlobal::config.externalWavEditor = externalWavEditorSelect->text(); MusEGlobal::config.useOldStyleStopShortCut = oldStyleStopCheckBox->isChecked(); MusEGlobal::config.moveArmedCheckBox = moveArmedCheckBox->isChecked(); MusEGlobal::config.useProjectSaveDialog = projectSaveCheckBox->isChecked(); MusEGlobal::config.popupsDefaultStayOpen = popsDefStayOpenCheckBox->isChecked(); MusEGlobal::config.leftMouseButtonCanDecrease = lmbDecreasesCheckBox->isChecked(); // MusEGlobal::config.rangeMarkerWithoutMMB = rangeMarkerWithoutMMBCheckBox->isChecked(); MusEGlobal::config.smartFocus = smartFocusCheckBox->isChecked(); MusEGlobal::config.borderlessMouse = borderlessMouseCheckBox->isChecked(); MusEGlobal::config.velocityPerNote = velocityPerNoteCheckBox->isChecked(); MusEGlobal::config.noPluginScaling = revertPluginNativeGUIScalingCheckBox->isChecked(); MusEGlobal::config.keepTransportWindowOnTop = keepTransportWindowOnTopCheckBox->isChecked(); MusEGlobal::config.showStatusBar = showStatusBarCheckBox->isChecked(); MusEGlobal::config.addHiddenTracks = addHiddenCheckBox->isChecked(); MusEGlobal::config.unhideTracks = unhideTracksCheckBox->isChecked(); MusEGlobal::muse->setHeartBeat(); // set guiRefresh if(MusEGlobal::midiSeq) MusEGlobal::midiSeq->msgSetRtc(); // set midi tick rate MusEGlobal::config.trackHeight = trackHeight->value(); MusEGlobal::config.lv2UiBehavior = static_cast(lv2UiBehaviorComboBox->currentIndex()); MusEGlobal::config.pluginLadspaPathList.clear(); for (int i = 0; i < pluginLadspaPathList->count(); ++i) MusEGlobal::config.pluginLadspaPathList << pluginLadspaPathList->item(i)->text(); MusEGlobal::config.pluginDssiPathList.clear(); for (int i = 0; i < pluginDssiPathList->count(); ++i) MusEGlobal::config.pluginDssiPathList << pluginDssiPathList->item(i)->text(); MusEGlobal::config.pluginVstPathList.clear(); for (int i = 0; i < pluginVstPathList->count(); ++i) MusEGlobal::config.pluginVstPathList << pluginVstPathList->item(i)->text(); MusEGlobal::config.pluginLinuxVstPathList.clear(); for (int i = 0; i < pluginLinuxVstPathList->count(); ++i) MusEGlobal::config.pluginLinuxVstPathList << pluginLinuxVstPathList->item(i)->text(); MusEGlobal::config.pluginLv2PathList.clear(); for (int i = 0; i < pluginLv2PathList->count(); ++i) MusEGlobal::config.pluginLv2PathList << pluginLv2PathList->item(i)->text(); MusEGlobal::config.pluginCacheTriggerRescan = pluginRescanButton->isChecked(); // applyMdiSettings(); TopWin::_openTabbed[TopWin::PIANO_ROLL] = cbTabPianoroll->isChecked(); TopWin::_openTabbed[TopWin::DRUM] = cbTabDrum->isChecked(); TopWin::_openTabbed[TopWin::WAVE] = cbTabWave->isChecked(); TopWin::_openTabbed[TopWin::SCORE] = cbTabScore->isChecked(); TopWin::_openTabbed[TopWin::MASTER] = cbTabMaster->isChecked(); // If the division is to be changed, this will RE-NORMALIZE things like the tempo and signature lists, // and RE-FILL the various editor rasterization (snap) table values. // The various editors will respond and deal with changing each of their local raster values. if(new_div != MusEGlobal::config.division) // True = Operation is non-undoable here. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyMidiDivision, new_div, 0, 0, true)); // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); raise(); } //--------------------------------------------------------- // ok //--------------------------------------------------------- void GlobalSettingsConfig::ok() { apply(); close(); } //--------------------------------------------------------- // cancel //--------------------------------------------------------- void GlobalSettingsConfig::cancel() { close(); } void GlobalSettingsConfig::showAudioConverterSettings() { if(!MusEGlobal::defaultAudioConverterSettings) return; MusECore::AudioConverterSettingsGroup* wrk_set = new MusECore::AudioConverterSettingsGroup(false); // Default, non-local settings. wrk_set->assign(*MusEGlobal::defaultAudioConverterSettings); AudioConverterSettingsDialog dialog(this, &MusEGlobal::audioConverterPluginList, wrk_set, false); // Default, non-local settings. if(dialog.exec() == QDialog::Accepted) { MusECore::PendingOperationList operations; MusEGlobal::song->modifyDefaultAudioConverterSettingsOperation(wrk_set, operations); if(!operations.empty()) { MusEGlobal::audio->msgExecutePendingOperations(operations, true); //MusEGlobal::song->update(SC_); } } else delete wrk_set; } void GlobalSettingsConfig::addPluginPath() { QString path; switch(pluginPathsTabs->currentIndex()) { case LadspaTab: if(pluginLadspaPathList->currentItem()) path = pluginLadspaPathList->currentItem()->text(); break; case DssiTab: if(pluginDssiPathList->currentItem()) path = pluginDssiPathList->currentItem()->text(); break; case VstTab: if(pluginVstPathList->currentItem()) path = pluginVstPathList->currentItem()->text(); break; case LinuxVstTab: if(pluginLinuxVstPathList->currentItem()) path = pluginLinuxVstPathList->currentItem()->text(); break; case Lv2Tab: if(pluginLv2PathList->currentItem()) path = pluginLv2PathList->currentItem()->text(); break; default: break; } QString new_path = browsePluginPath(path); if(new_path.isEmpty()) return; switch(pluginPathsTabs->currentIndex()) { case LadspaTab: pluginLadspaPathList->addItem(new_path); break; case DssiTab: pluginDssiPathList->addItem(new_path); break; case VstTab: pluginVstPathList->addItem(new_path); break; case LinuxVstTab: pluginLinuxVstPathList->addItem(new_path); break; case Lv2Tab: pluginLv2PathList->addItem(new_path); break; default: break; } } void GlobalSettingsConfig::editPluginPath() { QString path; switch(pluginPathsTabs->currentIndex()) { case LadspaTab: if(pluginLadspaPathList->currentItem()) path = pluginLadspaPathList->currentItem()->text(); break; case DssiTab: if(pluginDssiPathList->currentItem()) path = pluginDssiPathList->currentItem()->text(); break; case VstTab: if(pluginVstPathList->currentItem()) path = pluginVstPathList->currentItem()->text(); break; case LinuxVstTab: if(pluginLinuxVstPathList->currentItem()) path = pluginLinuxVstPathList->currentItem()->text(); break; case Lv2Tab: if(pluginLv2PathList->currentItem()) path = pluginLv2PathList->currentItem()->text(); break; default: break; } QString new_path = browsePluginPath(path); if(new_path.isEmpty()) return; switch(pluginPathsTabs->currentIndex()) { case LadspaTab: if(pluginLadspaPathList->currentItem()) pluginLadspaPathList->currentItem()->setText(new_path); break; case DssiTab: if(pluginDssiPathList->currentItem()) pluginDssiPathList->currentItem()->setText(new_path); break; case VstTab: if(pluginVstPathList->currentItem()) pluginVstPathList->currentItem()->setText(new_path); break; case LinuxVstTab: if(pluginLinuxVstPathList->currentItem()) pluginLinuxVstPathList->currentItem()->setText(new_path); break; case Lv2Tab: if(pluginLv2PathList->currentItem()) pluginLv2PathList->currentItem()->setText(new_path); break; default: break; } } QString GlobalSettingsConfig::browsePluginPath(const QString& path) { QString dir = QFileDialog::getExistingDirectory(this, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Select plugin directory")), path); return dir; } void GlobalSettingsConfig::removePluginPath() { switch(pluginPathsTabs->currentIndex()) { case LadspaTab: foreach(QListWidgetItem* item, pluginLadspaPathList->selectedItems()) delete item; break; case DssiTab: foreach(QListWidgetItem* item, pluginDssiPathList->selectedItems()) delete item; break; case VstTab: foreach(QListWidgetItem* item, pluginVstPathList->selectedItems()) delete item; break; case LinuxVstTab: foreach(QListWidgetItem* item, pluginLinuxVstPathList->selectedItems()) delete item; break; case Lv2Tab: foreach(QListWidgetItem* item, pluginLv2PathList->selectedItems()) delete item; break; default: return; } } void GlobalSettingsConfig::movePluginPathUp() { QListWidget* list = 0; switch(pluginPathsTabs->currentIndex()) { case LadspaTab: list = pluginLadspaPathList; break; case DssiTab: list = pluginDssiPathList; break; case VstTab: list = pluginVstPathList; break; case LinuxVstTab: list = pluginLinuxVstPathList; break; case Lv2Tab: list = pluginLv2PathList; break; default: break; } if(list) { int row = list->currentRow(); if(row > 0) { list->insertItem(row - 1, list->takeItem(row)); list->setCurrentRow(row - 1); } } } void GlobalSettingsConfig::movePluginPathDown() { QListWidget* list = 0; switch(pluginPathsTabs->currentIndex()) { case LadspaTab: list = pluginLadspaPathList; break; case DssiTab: list = pluginDssiPathList; break; case VstTab: list = pluginVstPathList; break; case LinuxVstTab: list = pluginLinuxVstPathList; break; case Lv2Tab: list = pluginLv2PathList; break; default: break; } if(list) { int row = list->currentRow(); if(row + 1 < list->count()) { list->insertItem(row + 1, list->takeItem(row)); list->setCurrentRow(row + 1); } } } void GlobalSettingsConfig::browseProjDir() { QString dir = MusEGui::browseProjectFolder(this); if(!dir.isEmpty()) projDirEntry->setText(dir); } void GlobalSettingsConfig::browseStartSongFile() { bool doReadMidiPorts; QString sstr = startSongGroup->button(1)->isChecked() ? QString("templates") : QString(""); QString fn = MusEGui::getOpenFileName(sstr, MusEGlobal::med_file_pattern, this, tr("MusE: Choose start template or song"), &doReadMidiPorts, MusEGui::MFileDialog::GLOBAL_VIEW); if (!fn.isEmpty()) { startSongEntry->setText(fn); readMidiConfigFromSongCheckBox->setChecked(doReadMidiPorts); } } void GlobalSettingsConfig::startSongReset() { startSongEntry->setText(""); readMidiConfigFromSongCheckBox->setChecked(false); } } // namespace MusEGui muse-4.0.0/src/muse/components/genset.h000066400000000000000000000044731404105311000200440ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: genset.h,v 1.3 2004/01/25 09:55:17 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __GENSET_H__ #define __GENSET_H__ #include "ui_gensetbase.h" #include #include #include "cobject.h" #include // Forward declarations: class QButtonGroup; class QShowEvent; class QWidget; namespace MusEGui { //--------------------------------------------------------- // GlobalSettingsConfig //--------------------------------------------------------- class GlobalSettingsConfig : public QDialog, public Ui::GlobalSettingsDialogBase { Q_OBJECT enum PathTab { LadspaTab = 0, DssiTab, VstTab, LinuxVstTab, Lv2Tab }; private slots: void updateSettings(); void apply(); void ok(); void cancel(); void editPluginPath(); void addPluginPath(); void removePluginPath(); void movePluginPathUp(); void movePluginPathDown(); void browseProjDir(); void browseStartSongFile(); void startSongReset(); void showAudioConverterSettings(); void updateBackendDeviceSettings(); protected: void showEvent(QShowEvent*); QButtonGroup *startSongGroup; QButtonGroup *recDrumGroup; QString browsePluginPath(const QString& path); public: GlobalSettingsConfig(QWidget* parent=nullptr); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/gensetbase.ui000066400000000000000000002323141404105311000210620ustar00rootroot00000000000000 GlobalSettingsDialogBase 0 0 607 653 MusE: Global Settings 6 Qt::Horizontal QSizePolicy::Expanding 20 20 &Apply &Ok true &Cancel true 0 0 0 Application Global project folder 6 2 2 ... Program start 2 6 12 Choose initial project or template ... Reset to default ... Initial project Start with &last project true 0 0 Start with &template false Sta&rt with project On launch 2 6 Show splash screen Show "Did you know?" dialog Start template or project: false Read MIDI Ports configuration from file, or else automatically configure Read MIDI ports configuration Warn if opening file version is different than current version Qt::Vertical QSizePolicy::Fixed 20 10 Auto save (every 5 minutes if not playing/recording) Qt::Vertical 20 40 Qt::Vertical QSizePolicy::Fixed 20 10 Editors 2 6 Qt::Vertical QSizePolicy::Fixed 20 10 Open editors in tabs (else in separate windows) 12 9 Piano roll true Drum editor true Wave editor true Score editor true Master track editor true Qt::Vertical 20 40 Plugin Paths 6 12 Ordered by priority. Changes take effect on restart. The plugin cache is used for safety and speed. It automatically rescans when any of the given directories change, for example by installing a new plugin. Click this button to force the cache to recreate upon next restart. &Force plugin rescan and cache recreation on next restart 0 LADSPA true QAbstractItemView::ExtendedSelection DSSI true QAbstractItemView::ExtendedSelection DSSI VST Directories where dssi-vst (if installed) looks for Wine vst plugins (*.dll files) true true QAbstractItemView::ExtendedSelection Linux VST Directories containing Linux vst plugins (*.so files) true QAbstractItemView::ExtendedSelection LV2 NOTE: Ensure the directory containing core LV2 bundles is included (typically /usr/lib/lv2). Otherwise all plugins may not load properly. true QAbstractItemView::ExtendedSelection Qt::Vertical 20 40 0 0 Move up 0 0 Move down 0 0 Add... 0 0 Edit... 0 0 Remove Qt::Vertical 20 40 Audio Audio driver settings (require restart) Sample rate: false Audio backend: false Shorter periods give better midi playback resolution. Note that this cannot be set with Jack devices! 16 32 64 128 256 512 1024 2048 Period size (frames per period): false Note that samplerate cannot be set with Jack devices! Mixer 11 2 11 2 2 6 dB -100 0 10 -60 Minimum meter value false Minimum slider value false dB -100 0 10 -60 Try to use Jack Freewheel false Speeds bounce operations Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Enable denormal protection false Enable output limiter false VST in-place false Enable VST in-place processing (restart required) Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. Minimum control period Minimum audio controller process period (samples). Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. 15 1 2 4 8 16 32 64 128 256 512 1024 2048 External Waveditor 2 2 2 1 0 External Waveditor command false Qt::Horizontal QSizePolicy::Expanding 60 23 2 0 8 true Note: External editor opened from the internal editor. false Sample rate conversion, time stretching and pitch shifting Audio converter settings... Qt::Horizontal 343 20 Qt::Vertical 20 40 Midi true 0 0 565 553 6 6 6 6 Ticks 2 6 6 0 0 5 48 96 120 192 240 384 480 768 960 1536 1920 3072 3840 6144 7680 12288 15360 Warn if timer frequency is inadequate RTC Resolution (Ticks/Sec) false Midi Resolution (Ticks/Quarternote) false 0 0 1024 2048 4096 8192 16384 32768 Instrument initialization 6 Send instrument initialization sequences Warn if instrument initialization sequences pending Send instrument controller default values if none in song, at init or rewind Midi controller behaviour 2 6 Send Null parameters after an (N)RPN value If set, 'null' parameter numbers will be sent after each RPN/NRPN event. (A 'null' parameter number is 127.) This prevents subsequent rogue or accidental 'data' events (without parameters) from corrupting the current RPN/NRPN controller. Normally it is OK to leave this unset, but use it if there is a possibility of such 'data' events. If this is set, the 'Optimize controllers' setting does not affect these controllers' parameters, only the H/L values. Send Null parameters Don't send redundant parameters or values If set, redundant H/L parameters or H/L values are not sent. They are sent only if they changed. This can save midi interface bandwidth. But it is possible the receiving device might miss the previous changes (for example if the device is turned on after they were sent). The 'Panic' button will force them to be resent. Certain operations will also force them to be resent, such as clicking a GUI midi control (but not while adjusting it). Optimize controllers Qt::Horizontal 40 20 Record drum tracks 6 2 6 Record a&ll instruments &Don't record hidden instruments Don'&t record muted instruments Don't record &hidden or muted instruments Qt::Vertical 20 40 Latency Latency (experimental) Enable latency correction/compensation Project latency Whether completely independent branches share a common project-wide latency: true Project-wide latency Unterminated branches Whether unterminated branches participate in latency correction/compensation: true Unterminated input branches Unterminated output branches Track monitoring Whether track monitor buttons affect latency correction/compensation: true Monitoring affects latency Qt::Vertical 20 40 GUI GUI refresh rate false 0 0 /sec 2 100 20 Track height 0 0 px 20 2000 20 LV2 UI open behavior 0 0 Use first available Ask once Ask always Qt::Vertical QSizePolicy::Maximum 17 0 Options 9 Show status bar false 0 0 Use project save dialog 0 0 Move single armed track with selection Whether record-arming a track automatically activates monitoring. Monitor on record-arm automatically Whether to show knobs or sliders in certain places, for example mixer strips Prefer knobs instead of sliders Whether to show knob and slider values in certain places, for example mixer strips. Turn off to reduce clutter. Show knob and slider values Prefer midi volume as decibels instead of 0-127 Show note names on notes in pianoroll Show note and control value tooltips in midi editors Show beat numbers in time scale Qt::Vertical 20 40 GUI Tweaks 6 11 11 11 11 12 Use old-style stop shortcut false 0 0 Some popup menus stay open (else hold Ctrl) 0 0 Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. Use left mouse button for decreasing values 0 0 Allow adding hidden tracks in track list menu Unhide tracks when adding hidden tracks Smart focus false 0 0 Smart focus After editing, controls will return focus to their respective canvas Show newly created midi velocity graphs per-note Borderless zoom/pan mouse (else use alternate method) Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. Scrollable submenus Live update wave parts while recording Style hack: Force line edit widgets to draw a frame Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. This hack forces a frame to be drawn, but may interfere with other styles. Revert native GUI scaling for plugins in HiDPI Revert native UI window HiDPI scaling (some plugins ignore this setting) Keep transport window on top (requires restart) Revert native UI window HiDPI scaling (some plugins ignore this setting) true Qt::Vertical 20 40 TabWidget2 applyButton okButton cancelButton projDirEntry projDirOpenToolButton autoSaveCheckBox warnOnFileVersionsCheckBox showSplash showDidYouKnow startLastButton startEmptyButton startSongButton readMidiConfigFromSongCheckBox startSongEntry startSongFileOpenToolButton startSongResetToolButton pluginRescanButton pluginPathsTabs pluginLadspaPathList pluginDssiPathList pluginVstPathList pluginLinuxVstPathList pluginLv2PathList pluginPathMoveUp pluginPathMoveDown pluginPathAdd pluginPathEdit pluginPathRemove deviceAudioSize deviceAudioBackendComboBox deviceAudioRate minSliderSelect minMeterSelect denormalCheckBox freewheelCheckBox outputLimiterCheckBox vstInPlaceCheckBox minControlProcessPeriodComboBox externalWavEditorSelect audioConvertersButton scrollArea_3 rtcResolutionSelect midiDivisionSelect warnIfBadTimingCheckBox midiSendInit midiWarnInitPending midiSendCtlDefaults sendNullParamsCB optimizeControllersCB recordAllButton dontRecHiddenButton dontRecMutedButton dontRecBothButton enableLatencyCorrectionButton latencyProjectCommonButton latencyInBranchUntermButton latencyOutBranchUntermButton latencyMonitorAffectingButton guiRefreshSelect trackHeight lv2UiBehaviorComboBox projectSaveCheckBox moveArmedCheckBox monitorOnRecordCheckBox preferKnobsVsSlidersCheckBox showControlValuesCheckBox preferMidiVolumeDbCheckBox showNoteNamesCheckBox showNoteTooltipsCheckBox muse-4.0.0/src/muse/components/header.cpp000066400000000000000000000154171404105311000203420ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: header.cpp,v 1.1.1.1 2003/10/27 18:55:05 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "header.h" #include "xml.h" #include "popupmenu.h" #include #include #include #include #include namespace MusEGui { //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void Header::readStatus(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::Text: // We can only restore the header state with version-compatible data. // If columns were altered, 'alien' loaded data will not fit! if(xml.isVersionEqualToLatest()) restoreState(QByteArray::fromHex(tag.toLatin1())); break; case MusECore::Xml::TagStart: xml.unknown("Header"); break; case MusECore::Xml::TagEnd: if (tag ==objectName()) return; break; default: break; } } } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void Header::writeStatus(int level, MusECore::Xml& xml) const { xml.nput(level, "<%s> ", MusECore::Xml::xmlString(objectName()).toLatin1().constData()); xml.nput("%s", saveState().toHex().constData()); xml.put("", MusECore::Xml::xmlString(objectName()).toLatin1().constData()); } //--------------------------------------------------------- // Header //--------------------------------------------------------- Header::Header(QWidget* parent, const char* name) : QHeaderView(Qt::Horizontal, parent) { setObjectName(name); itemModel = new QStandardItemModel; setModel(itemModel); setDefaultSectionSize(30); setMinimumSectionSize(22); } //--------------------------------------------------------- // columnLabel //--------------------------------------------------------- QString Header::columnLabel(int col) { return itemModel->horizontalHeaderItem(col)->text(); } //--------------------------------------------------------- // columnLabel //--------------------------------------------------------- QIcon Header::columnIcon(int col) { return itemModel->horizontalHeaderItem(col)->icon(); } //--------------------------------------------------------- // setColumnLabel //--------------------------------------------------------- void Header::setColumnLabel(const QString & text, int col, int width ) { //printf("column set to %s %d %d \n", text.toLatin1().data(), col, width); QStandardItem *sitem = new QStandardItem(text ); itemModel->setHorizontalHeaderItem(col, sitem); if (width > -1) resizeSection(col, width); } //--------------------------------------------------------- // setColumnIcon //--------------------------------------------------------- void Header::setColumnIcon(QIcon & icon, int col, int width ) { QStandardItem *sitem = new QStandardItem(icon, QString("")); itemModel->setHorizontalHeaderItem(col, sitem); if (width > -1) resizeSection(col, width); } //--------------------------------------------------------- // setToolTip //--------------------------------------------------------- void Header::setToolTip(int col, const QString &text) { QStandardItem *item = itemModel->horizontalHeaderItem(col); item->setToolTip(text); } //--------------------------------------------------------- // setWhatsThis //--------------------------------------------------------- void Header::setWhatsThis(int col, const QString &text) { QStandardItem *item = itemModel->horizontalHeaderItem(col); item->setWhatsThis(text); } //--------------------------------------------------------- // setStatusTip //--------------------------------------------------------- void Header::setStatusTip(int col, const QString &text) { QStandardItem *item = itemModel->horizontalHeaderItem(col); item->setStatusTip(text); } void Header::mousePressEvent ( QMouseEvent * e ) { if (e->button() == Qt::RightButton) { PopupMenu* p = new PopupMenu(); p->disconnect(); p->clear(); p->setTitle(tr("Track Info Columns")); QAction* act = nullptr; for(int i=0; i < count(); i++) { const QIcon& icon = itemModel->horizontalHeaderItem(logicalIndex(i))->icon(); if (!icon.isNull()) { act = p->addAction(icon, "\t - " + itemModel->horizontalHeaderItem(logicalIndex(i))->toolTip()); } else { QString tt = itemModel->horizontalHeaderItem(logicalIndex(i))->toolTip(); if (tt.isEmpty()) tt = tr("Custom column"); act = p->addAction(itemModel->horizontalHeaderItem(logicalIndex(i))->text() + "\t - " + tt); } act->setCheckable(true); act->setChecked(!isSectionHidden(logicalIndex(i))); int data = logicalIndex(i); act->setData(data); } connect(p, SIGNAL(triggered(QAction*)), SLOT(changeColumns(QAction*))); p->exec(QCursor::pos()); delete p; return; } QHeaderView::mousePressEvent(e); } void Header::changeColumns(QAction *a) { int section = a->data().toInt(); if (isSectionHidden(section)) showSection(section); else hideSection(section); resizeSection(section, sectionSizeHint(section)); } void Header::removeColumn(int col) { itemModel->removeColumn(col); } } // namespace MusEGui muse-4.0.0/src/muse/components/header.h000066400000000000000000000037531404105311000200070ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: header.h,v 1.1.1.1 2003/10/27 18:55:03 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __HEADER_H__ #define __HEADER_H__ #include #include #include class QStandardItemModel; class QAction; class QWidget; class QMouseEvent; namespace MusECore { class Xml; } namespace MusEGui { class Header : public QHeaderView { Q_OBJECT QStandardItemModel *itemModel; public: Header(QWidget* parent=0, const char* name=0); void writeStatus(int level, MusECore::Xml&) const; void readStatus(MusECore::Xml&); QString columnLabel(int col); QIcon columnIcon(int col); void setColumnLabel( const QString & s, int col, int width = -1 ); void setColumnIcon(QIcon &icon, int col, int width = -1 ); void setToolTip(int col, const QString &text); void setWhatsThis(int col, const QString &text); void setStatusTip(int col, const QString &text); void mousePressEvent ( QMouseEvent * e ); void removeColumn(int col); private slots: void changeColumns(QAction* a); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/intlabel.cpp000066400000000000000000000100411404105311000206700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: intlabel.cpp,v 1.1.1.1.2.1 2008/08/18 00:15:26 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "intlabel.h" #include "utils.h" namespace MusEGui { //--------------------------------------------------------- // IntLabel //--------------------------------------------------------- IntLabel::IntLabel(int _val, int _min, int _max, QWidget* parent, int _off, const QString& str, int lPos) : Nentry(parent, str, lPos) { specialValue = "off"; min = _min; max = _max; val = _val+1; // don't optimize away off = _off; setValue(_val); int len = MusECore::num2cols(min, max); setSize(len); } void IntLabel::setSpecialValueText(const QString& s) { specialValue = s; setString(val); } //--------------------------------------------------------- // setRange //--------------------------------------------------------- void IntLabel::setRange(int mn, int mx) { min = mn; max = mx; setSize(MusECore::num2cols(min, max)); int v = val; if(val < mn) v = mn; else if(val > mx) v = mx; setValue(v); } //--------------------------------------------------------- // setString //--------------------------------------------------------- bool IntLabel::setString(int v, bool editable) { if (v < min || v > max) { setText(QString("---")); return true; } else if (v == off) { if (editable) setText(QString("")); else setText(specialValue); } else { QString s; s.setNum(v); if (!editable) s += suffix; setText(s); } return false; } //--------------------------------------------------------- // setSValue //--------------------------------------------------------- bool IntLabel::setSValue(const QString& s) { int v; if (s == specialValue) v = off; else { bool ok; v = s.toInt(&ok); if (!ok) return true; if (v < min) v = min; if (v > max) v = max; } if (v != val) { setValue(v); emit valueChanged(val); } return false; } //--------------------------------------------------------- // incValue //--------------------------------------------------------- void IntLabel::incValue(int) { if (val < max) { setValue(val+1); emit valueChanged(val); } } //--------------------------------------------------------- // decValue //--------------------------------------------------------- void IntLabel::decValue(int) { if (val > min) { setValue(val-1); emit valueChanged(val); } } //--------------------------------------------------------- // setOff //--------------------------------------------------------- void IntLabel::setOff(int v) { off = v; setString(val); } } // namespace MusEGui muse-4.0.0/src/muse/components/intlabel.h000066400000000000000000000036551404105311000203520ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: intlabel.h,v 1.1.1.1.2.2 2008/08/18 00:15:26 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __INTLABEL_H__ #define __INTLABEL_H__ #include #include #include "nentry.h" namespace MusEGui { //--------------------------------------------------------- // IntLabel //--------------------------------------------------------- class IntLabel : public Nentry { Q_OBJECT int min, max, off; QString suffix; QString specialValue; void init(); virtual bool setSValue(const QString&); virtual bool setString(int val, bool editable = false); virtual void incValue(int); virtual void decValue(int); signals: void valueChanged(int); public: IntLabel(int, int, int, QWidget*, int _off = INT_MAX, const QString& = QString(""), int lpos = 0); void setOff(int v); void setSuffix(const QString& s) { suffix = s; } void setSpecialValueText(const QString& s); void setRange(int, int); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/lcd_widgets.cpp000066400000000000000000001110161404105311000213720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // lcd_widgets.cpp // (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 #include #include #include #include #include #include #include "muse_math.h" #include "popup_double_spinbox.h" #include "midictrl.h" #include "background_painter.h" #include "lcd_widgets.h" // #include // For debugging output: Uncomment the fprintf section. #define DEBUG_LCD_WIDGETS(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { LCDPainter::LCDPainter() { } void LCDPainter::drawCharacter(QPainter* painter, const QRect& rect, char asciiChar) { const int left = rect.x(); const int right = rect.x() + rect.width() - 1; const int top = rect.y(); const int bottom = rect.y() + rect.height() - 1; const int half = rect.y() + round(double(rect.height()) / 2.0) - 1; const int ileft = left + 1; const int iright = right - 1; const int itop = top + 1; const int ibottom = bottom - 1; const int ihalfp = half + 1; const int ihalfm = half - 1; switch(asciiChar) { case 0x30: painter->drawLine(left, top, iright, top); painter->drawLine(right, top, right, ibottom); painter->drawLine(ileft, bottom, right, bottom); painter->drawLine(left, itop, left, bottom); break; case 0x31: painter->drawLine(right, top, right, bottom); break; case 0x32: painter->drawLine(left, top, iright, top); painter->drawLine(right, top, right, ihalfm); painter->drawLine(left, half, right, half); painter->drawLine(left, ihalfp, left, bottom); painter->drawLine(ileft, bottom, right, bottom); break; case 0x33: painter->drawLine(left, top, iright, top); painter->drawLine(right, top, right, ibottom); painter->drawLine(left, bottom, right, bottom); painter->drawLine(ileft, half, iright, half); break; case 0x34: painter->drawLine(left, top, left, ihalfm); painter->drawLine(left, half, iright, half); painter->drawLine(right, top, right, bottom); break; case 0x35: painter->drawLine(ileft, top, right, top); painter->drawLine(left, top, left, ihalfm); painter->drawLine(left, half, right, half); painter->drawLine(right, ihalfp, right, bottom); painter->drawLine(left, bottom, iright, bottom); break; case 0x36: painter->drawLine(ileft, top, right, top); painter->drawLine(left, top, left, bottom); painter->drawLine(ileft, bottom, right, bottom); painter->drawLine(right, half, right, ibottom); painter->drawLine(ileft, half, iright, half); break; case 0x37: painter->drawLine(left, top, iright, top); painter->drawLine(right, top, right, bottom); break; case 0x38: painter->drawLine(left, top, left, bottom); painter->drawLine(right, top, right, bottom); painter->drawLine(ileft, top, iright, top); painter->drawLine(ileft, half, iright, half); painter->drawLine(ileft, bottom, iright, bottom); break; case 0x39: painter->drawLine(left, top, iright, top); painter->drawLine(right, top, right, bottom); painter->drawLine(left, bottom, iright, bottom); painter->drawLine(left, itop, left, half); painter->drawLine(ileft, half, iright, half); break; case 0x2d: // Minus (dash) '-' painter->drawLine(left, half, right, half); break; } } void LCDPainter::drawText(QPainter* painter, const QRect& rect, const QString& text, int flags) { if(text.isEmpty()) return; int sz = rect.height(); if(sz < 7) sz = 7; const int sz3 = round(double(sz) / 2.8); const int margin = 1 + sz3 / 6; const int chars = text.size(); QRect r; int invidx; char c; int curpos = 0; const int y = rect.y(); // Assume right alignment if unspecified. if(flags & Qt::AlignLeft) { curpos = rect.x(); for(int idx = 0; idx < chars; ++idx) { c = text.at(idx).toLatin1(); r = QRect(curpos, y, sz3, sz); drawCharacter(painter, r, c); curpos += sz3 + margin; } } else { curpos = rect.x() + rect.width(); for(int idx = 0; idx < chars; ++idx) { invidx = chars - 1 - idx; c = text.at(invidx).toLatin1(); curpos -= sz3 + margin; r = QRect(curpos, y, sz3, sz); drawCharacter(painter, r, c); } } } //------------------------------------------ // LCDPatchEdit //------------------------------------------ LCDPatchEdit::LCDPatchEdit(QWidget* parent, int minFontPoint, bool ignoreHeight, bool ignoreWidth, const QString& text, const QColor& readoutColor, Qt::WindowFlags flags) : QFrame(parent, flags), _readoutColor(readoutColor), _fontPointMin(minFontPoint), _fontIgnoreHeight(ignoreHeight), _fontIgnoreWidth(ignoreWidth), _text(text) { if(objectName().isEmpty()) setObjectName(QStringLiteral("LCDPatchEdit")); setMouseTracking(true); setEnabled(true); setFocusPolicy(Qt::WheelFocus); //setFocusPolicy(Qt::NoFocus); // setAutoFillBackground(false); // setAttribute(Qt::WA_NoSystemBackground); // //setAttribute(Qt::WA_StaticContents); // // This is absolutely required for speed! Otherwise painfully slow because of full background // // filling, even when requesting small udpdates! Background is drawn by us. // setAttribute(Qt::WA_OpaquePaintEvent); _orient = PatchHorizontal; _style3d = true; _radius = 2; _enableValueToolTips = true; _editor = nullptr; _editMode = false; _curEditSection = 0; _xMargin = 2; _yMargin = 2; _sectionSpacing = 4; _HBankHovered = false; _LBankHovered = false; _ProgHovered = false; _LCDPainter = new LCDPainter(); _maxAliasedPointSize = -1; _lastValidPatch = _currentPatch = MusECore::CTRL_VAL_UNKNOWN; _lastValidHB = _lastValidLB = _lastValidProg = MusECore::CTRL_VAL_UNKNOWN; _id = -1; setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); _curFont = font(); autoAdjustFontSize(); setToolTip(tr("High bank: Low bank: Program\n(Ctrl-double-click on/off)")); } LCDPatchEdit::~LCDPatchEdit() { if(_LCDPainter) delete _LCDPainter; } // Static. QSize LCDPatchEdit::getMinimumSizeHint(const QFontMetrics& fm, int xMargin, int yMargin, PatchOrientation orient ) { const int font_height = fm.height(); int fin_h = 1; int fin_w = 1; QRect aRect; switch(orient) { case PatchHorizontal: fin_h = font_height + 1 + 2 * yMargin; // +1 for extra anti-aliasing space. aRect.setHeight(font_height); break; case PatchVertical: fin_h = 3 * (font_height + 3) + 2 * yMargin; // +1 for extra anti-aliasing space. aRect.setHeight(font_height); break; } const int sz2 = charWidth(aRect); const int margin = readoutMargin(sz2); const int w = 2 * (sz2 + margin) + 1 + margin; // Three digit spaces: two full and one special slim consideration for leading '1'. const int spacing = 4; switch(orient) { case PatchHorizontal: fin_w = 2 * xMargin + 2 * spacing + 3 * w + 2; break; case PatchVertical: fin_w = w + spacing; break; } return QSize(fin_w, fin_h); } void LCDPatchEdit::setReadoutOrientation(PatchOrientation orient) { _orient = orient; resize(this->size()); update(); } //void LCDPatchEdit::setReadoutColor(const QColor& c) //{ // _readoutColor = c; // update(); //} void LCDPatchEdit::setMaxAliasedPointSize(int sz) { if(sz<0)sz=0; _maxAliasedPointSize = sz; update(); } void LCDPatchEdit::setMargins(int hor, int vert) { if(hor < 0) hor = 0; if(vert < 0) vert = 0; _xMargin = hor; _yMargin = vert; resize(this->size()); } QSize LCDPatchEdit::sizeHint() const { return getMinimumSizeHint(fontMetrics(), _xMargin, _yMargin, _orient ); } void LCDPatchEdit::paintEvent(QPaintEvent* e) { e->ignore(); QFrame::paintEvent(e); e->accept(); if(rect().width() <= 0 || rect().height() <= 0) return; QPainter painter(this); // painter.setFont(_curFont); painter.setFont(font()); const int sec_spc2 = _sectionSpacing / 2; int hb = (_currentPatch >> 16) & 0xff; int lb = (_currentPatch >> 8) & 0xff; int pr = _currentPatch & 0xff; const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; const int hboff = is_unk || hb > 127; const int lboff = is_unk || lb > 127; const int proff = is_unk || pr > 127; const int colon1x = _HBankFieldRect.x() + _HBankFieldRect.width() + sec_spc2 - 1; const int colon2x = _LBankFieldRect.x() + _LBankFieldRect.width() + sec_spc2 - 1; // Arbitrary: Just use hbank. const int colon_y1 = _HBankFieldRect.y() + _HBankFieldRect.height() / 3; const int colon_y2 = _HBankFieldRect.y() + 2 * _HBankFieldRect.height() / 3; QString hbstr, lbstr, prgstr; QColor hbcol, lbcol, prgcol, hbcolaa, lbcolaa, prgcolaa, oncolaa, offcolaa; const QPalette& pal = palette(); QColor offCol(pal.text().color()); QColor onCol(_readoutColor); oncolaa = onCol; oncolaa.setAlpha(180); offcolaa = offCol; offcolaa.setAlpha(180); if(!isEnabled()) { const qreal aafactor = 0.75; onCol.setHsvF(onCol.hsvHueF(), onCol.hsvSaturationF(), onCol.valueF() * aafactor); offCol.setHsv(offCol.hsvHueF(), offCol.hsvSaturationF(), offCol.valueF() * aafactor); oncolaa.setHsvF(oncolaa.hsvHueF(), oncolaa.hsvSaturationF(), oncolaa.valueF() * aafactor, oncolaa.alphaF()); offcolaa.setHsv(offcolaa.hsvHueF(), offcolaa.hsvSaturationF(), offcolaa.valueF() * aafactor, offcolaa.alphaF()); } if(proff) { prgstr = QString("--"); prgcol = offCol; prgcolaa = offcolaa; } else { prgstr = QString::number(pr + 1); prgcol = onCol; prgcolaa = oncolaa; } if(hboff) { hbstr = QString("--"); hbcol = offCol; hbcolaa = offcolaa; } else { hbstr = QString::number(hb + 1); hbcol = onCol; hbcolaa = oncolaa; } if(lboff) { lbstr = QString("--"); lbcol = offCol; lbcolaa = offcolaa; } else { lbstr = QString::number(lb + 1); lbcol = onCol; lbcolaa = oncolaa; } if(_HBankHovered) { hbcol = hbcol.lighter(135); hbcolaa = hbcolaa.lighter(135); } if(_LBankHovered) { lbcol = lbcol.lighter(135); lbcolaa = lbcolaa.lighter(135); } if(_ProgHovered) { prgcol = prgcol.lighter(135); prgcolaa = prgcolaa.lighter(135); } ItemBackgroundPainter ibp; switch(_orient) { case PatchHorizontal: painter.setRenderHint(QPainter::Antialiasing, false); ibp.drawBackground(&painter, rect(), pal, 1, 1, QRect(), _radius, _style3d, nullptr, _borderColor, hboff && lboff && proff ? _bgColor : _bgActiveColor); painter.setPen(offCol); painter.drawPoint(colon1x, colon_y1); painter.drawPoint(colon1x, colon_y2); painter.setPen(offCol); painter.drawPoint(colon2x, colon_y1); painter.drawPoint(colon2x, colon_y2); break; case PatchVertical: ibp.drawBackground(&painter, _HBankRect, pal); ibp.drawBackground(&painter, _LBankRect, pal); ibp.drawBackground(&painter, _ProgRect, pal); break; } painter.setPen(hbcolaa); painter.setRenderHint(QPainter::Antialiasing, true); _LCDPainter->drawText(&painter, _HBankFieldRect, hbstr); painter.setPen(hbcol); painter.setRenderHint(QPainter::Antialiasing, false); _LCDPainter->drawText(&painter, _HBankFieldRect, hbstr); painter.setPen(lbcolaa); painter.setRenderHint(QPainter::Antialiasing, true); _LCDPainter->drawText(&painter, _LBankFieldRect, lbstr); painter.setPen(lbcol); painter.setRenderHint(QPainter::Antialiasing, false); _LCDPainter->drawText(&painter, _LBankFieldRect, lbstr); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(prgcolaa); _LCDPainter->drawText(&painter, _ProgFieldRect, prgstr); painter.setPen(prgcol); painter.setRenderHint(QPainter::Antialiasing, false); _LCDPainter->drawText(&painter, _ProgFieldRect, prgstr); } int LCDPatchEdit::value() const { return _currentPatch; } void LCDPatchEdit::setValue(int v) { if(_currentPatch == v) return; _currentPatch = v; update(); } void LCDPatchEdit::setLastValidPatch(int v) { if(_lastValidPatch == v) return; _lastValidPatch = v; //update(); // Not required. } void LCDPatchEdit::setLastValidBytes(int hbank, int lbank, int prog) { if(_lastValidHB != hbank) { _lastValidHB = hbank; //update(); // Not required. } if(_lastValidLB != lbank) { _lastValidLB = lbank; //update(); // Not required. } if(_lastValidProg != prog) { _lastValidProg = prog; //update(); // Not required. } } //--------------------------------------------------------- // autoAdjustFontSize // w: Widget to auto adjust font size // s: String to fit // ignoreWidth: Set if dealing with a vertically constrained widget - one which is free to resize horizontally. // ignoreHeight: Set if dealing with a horizontally constrained widget - one which is free to resize vertically. //--------------------------------------------------------- bool LCDPatchEdit::autoAdjustFontSize() { // FIXME: Disabled for now, the font modulates back and forth, not very good ATM. // May have to revert to the font-checking iteration loop scheme. // QFont fnt = font(); // This is the maximum font. // int max = fnt.pointSize(); // int min = _fontPointMin; // // // In case the max or min was obtained from QFont::pointSize() which returns -1 // // if the font is a pixel font, or if min is greater than max... // // Limit the minimum and maximum sizes to something at least readable. // if(max < 4) // max = 4; // if(min < 4) // min = 4; // if(max < min) // max = min; // // //qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); // //QRectF r = boundingRect(); // QRectF r = rect(); // //QFont f = painter->font(); // // // //if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. // if(_fontIgnoreWidth || _text.isEmpty()) // Also avoid divide by zero below. // { // if(fnt.pointSize() != max) // { // fnt.setPointSize(max); // // setFont(fnt); // _curFont = fnt; // update(); // } // } // else // { // //qreal aspectRatio = painter->fontMetrics().lineSpacing() / painter->fontMetrics().averageCharWidth(); // qreal aspectRatio = fontMetrics().lineSpacing() / fontMetrics().averageCharWidth(); // // int pixelsize = sqrt(r.width() * r.height() / aspectRatio / (_text.length() * 3)) * aspectRatio; // int pixelsize = sqrt(r.width() * r.height() / aspectRatio / _text.length()) * aspectRatio; // fnt.setPixelSize(pixelsize); // //int flags = Qt::AlignCenter|Qt::TextDontClip|Qt::TextWordWrap; // int flags = Qt::AlignCenter; // //if ((pixelsize * lod) < 13) // // flags |= Qt::TextWrapAnywhere; // QFontMetricsF fmf(fnt); // QRectF tbr = fmf.boundingRect(r, flags, _text); // pixelsize = fnt.pixelSize() * qMin(r.width() * 0.95 / tbr.width(), r.height() * 0.95 / tbr.height()); // // if(pixelsize < min) // // pixelsize = min; // // else if(pixelsize > max) // // pixelsize = max; // fnt.setPixelSize(pixelsize); // const QFontInfo fi(fnt); // const int pointsize = fi.pointSize(); // if(pointsize <= min) // fnt.setPointSize(min); // else if(pointsize >= max) // fnt.setPointSize(max); // // setFont(fnt); // _curFont = fnt; // //painter->drawText(r,flags,stitle); // update(); // } // Force minimum height. Use the expected height for the highest given point size. // This way the mixer strips aren't all different label heights, but can be larger if necessary. // Only if ignoreHeight is set (therefore the height is adjustable). // if(_fontIgnoreHeight) // { // // FIXME Disabled for now, as per above. // // fnt.setPointSize(max); // // const QFontMetrics fm(fnt); // const QFontMetrics fm(font()); // // // Set the label's minimum height equal to the height of the font. // setMinimumHeight(fm.height() + 2 * frameWidth()); // } // return true; } void LCDPatchEdit::setText(const QString& txt) { if(_text == txt) return; _text = txt; autoAdjustFontSize(); update(); } QRect LCDPatchEdit::activeDrawingArea() const { // +1 for extra anti-aliasing space. return rect().adjusted(_xMargin, _yMargin + 1, -_xMargin, -_yMargin); } // Static. int LCDPatchEdit::charWidth(const QRect& aRect) { int sz = aRect.height(); if(sz < 7) sz = 7; return round(double(sz) / 2.8); } // Static. int LCDPatchEdit::readoutMargin(int charWidth) { return 1 + charWidth / 6; } void LCDPatchEdit::resizeEvent(QResizeEvent* e) { e->ignore(); QFrame::resizeEvent(e); e->accept(); autoAdjustFontSize(); const QFontMetrics fm = fontMetrics(); const int font_height = fm.height(); QRect aRect = activeDrawingArea(); switch(_orient) { case PatchHorizontal: break; case PatchVertical: aRect.setHeight(font_height); break; } const int sz2 = charWidth(aRect); const int margin = readoutMargin(sz2); const int rectw3 = (aRect.width() - margin * 2) / 3; const int w = 2 * (sz2 + margin) + 1 + margin; // Three digit spaces: two full and one special slim consideration for leading '1'. int spacing = rectw3 - w; if(spacing < 4) spacing = 4; if(spacing > 16) spacing = 16; _sectionSpacing = spacing; const int indent = aRect.width() / 2 - w / 2; int x0, x1, x2; int recty1 = 0, recty2 = 0, recty3 = 0, recth = 0; int frecty1, frecty2, frecty3, frecth; switch(_orient) { case PatchHorizontal: x0 = aRect.x() + _xMargin; x1 = x0 + w + _sectionSpacing; x2 = x1 + w + _sectionSpacing; recty1 = recty2 = recty3 = rect().y() + _yMargin; recth = rect().height() - 2 * _yMargin; frecty1 = frecty2 = frecty3 = aRect.y(); frecth = aRect.height(); _HBankFieldRect.setRect(x0, frecty1, w, frecth); _LBankFieldRect.setRect(x1, frecty2, w, frecth); _ProgFieldRect.setRect (x2, frecty3, w, frecth); break; case PatchVertical: x0 = x1 = x2 = aRect.x() + indent; // + _xMargin; recth = font_height + 3; recty1 = rect().y() + _yMargin; recty2 = recty1 + recth; recty3 = recty2 + recth; frecth = font_height; frecty1 = aRect.y() + 1; frecty2 = frecty1 + frecth + 3; frecty3 = frecty2 + frecth + 3; _HBankFieldRect.setRect(x0 + _sectionSpacing / 2 + _xMargin, frecty1, w, frecth); _LBankFieldRect.setRect(x1 + _sectionSpacing / 2 + _xMargin, frecty2, w, frecth); _ProgFieldRect.setRect (x2 + _sectionSpacing / 2 + _xMargin, frecty3, w, frecth); break; } _HBankRect.setRect(x0, recty1, w + _sectionSpacing, recth); _LBankRect.setRect(x1, recty2, w + _sectionSpacing, recth); _ProgRect.setRect (x2, recty3, w + _sectionSpacing, recth); update(); } void LCDPatchEdit::mouseMoveEvent(QMouseEvent *e) { //fprintf(stderr, "LCDPatchEdit::mouseMoveEvent\n"); e->ignore(); QFrame::mouseMoveEvent(e); e->accept(); QPoint p = e->pos(); bool doupd = false; if(_HBankRect.contains(p) != _HBankHovered) { _HBankHovered = !_HBankHovered; doupd = true; } if(_LBankRect.contains(p) != _LBankHovered) { _LBankHovered = !_LBankHovered; doupd = true; } if(_ProgRect.contains(p) != _ProgHovered) { _ProgHovered = !_ProgHovered; doupd = true; } if(doupd) update(); } void LCDPatchEdit::mousePressEvent(QMouseEvent* e) { Qt::MouseButtons buttons = e->buttons(); e->accept(); emit pressed(e->pos(), _id, buttons, e->modifiers()); if(buttons == Qt::RightButton) emit rightClicked(e->globalPos(), _id); } void LCDPatchEdit::mouseReleaseEvent(QMouseEvent* e) { e->accept(); emit released(e->pos(), _id, e->buttons(), e->modifiers()); } void LCDPatchEdit::enterEvent(QEvent *e) { //fprintf(stderr, "LCDPatchEdit::enterEvent\n"); QPoint p = mapFromGlobal(cursor().pos()); bool doupd = false; if(_HBankRect.contains(p) != _HBankHovered) { _HBankHovered = !_HBankHovered; doupd = true; } if(_LBankRect.contains(p) != _LBankHovered) { _LBankHovered = !_LBankHovered; doupd = true; } if(_ProgRect.contains(p) != _ProgHovered) { _ProgHovered = !_ProgHovered; doupd = true; } e->ignore(); QFrame::enterEvent(e); e->accept(); if(doupd) update(); } void LCDPatchEdit::leaveEvent(QEvent *e) { //fprintf(stderr, "LCDPatchEdit::leaveEvent\n"); bool doupd = false; if(_HBankHovered) { _HBankHovered = false; doupd = true; } if(_LBankHovered) { _LBankHovered = false; doupd = true; } if(_ProgHovered) { _ProgHovered = false; doupd = true; } e->ignore(); QFrame::leaveEvent(e); e->accept(); if(doupd) update(); } void LCDPatchEdit::setFontIgnoreDimensions(bool ignoreHeight, bool ignoreWidth) { _fontIgnoreWidth = ignoreWidth; _fontIgnoreHeight = ignoreHeight; autoAdjustFontSize(); update(); } void LCDPatchEdit::setFontPointMin(int point) { _fontPointMin = point; autoAdjustFontSize(); update(); } void LCDPatchEdit::editorReturnPressed() { DEBUG_LCD_WIDGETS(stderr, "LCDPatchEdit::editorReturnPressed\n"); _editMode = false; if(_editor) { int hb = (_currentPatch >> 16) & 0xff; int lb = (_currentPatch >> 8) & 0xff; int pr = _currentPatch & 0xff; const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; const int lasthb = (_lastValidPatch >> 16) & 0xff; const int lastlb = (_lastValidPatch >> 8) & 0xff; const int lastpr = _lastValidPatch & 0xff; const bool last_is_unk = _lastValidPatch == MusECore::CTRL_VAL_UNKNOWN; int new_val = _currentPatch; switch(_curEditSection) { case HBankSection: hb = _editor->value(); if(hb == 0) hb = 0xff; else { --hb; if(is_unk) { lb = lastlb; pr = lastpr; if(last_is_unk) { lb = 0xff; pr = 0; } } } new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); break; case LBankSection: lb = _editor->value(); if(lb == 0) lb = 0xff; else { --lb; if(is_unk) { hb = lasthb; pr = lastpr; if(last_is_unk) { hb = 0xff; pr = 0; } } } new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); break; case ProgSection: pr = _editor->value(); if(pr == 0) new_val = MusECore::CTRL_VAL_UNKNOWN; else { --pr; if(is_unk) { hb = lasthb; lb = lastlb; if(last_is_unk) { hb = 0xff; lb = 0xff; } } new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); } break; } if(value() != new_val) { setValue(new_val); emit valueChanged(value(), _id); } _editor->deleteLater(); _editor = 0; } setFocus(); // FIXME There are three sections. Need to clear focus for now. // clearFocus(); } void LCDPatchEdit::editorEscapePressed() { DEBUG_LCD_WIDGETS(stderr, "LCDPatchEdit::editorEscapePressed\n"); _editMode = false; if(_editor) { _editor->deleteLater(); _editor = 0; setFocus(); //FIXME There are three sections. Need to clear focus for now. // clearFocus(); } } void LCDPatchEdit::keyPressEvent(QKeyEvent* e) { switch (e->key()) { case Qt::Key_Return: case Qt::Key_Enter: { // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. if(!_editor || !_editor->hasFocus()) showEditor(); e->accept(); return; } break; default: break; } e->ignore(); QFrame::keyPressEvent(e); } bool LCDPatchEdit::event(QEvent* e) { switch(e->type()) { // FIXME: Doesn't work. case QEvent::NonClientAreaMouseButtonPress: DEBUG_LCD_WIDGETS(stderr, "LCDPatchEdit::event NonClientAreaMouseButtonPress\n"); e->accept(); _editMode = false; if(_editor) { _editor->deleteLater(); _editor = 0; } return true; break; default: break; } return QFrame::event(e); } void LCDPatchEdit::wheelEvent(QWheelEvent* e) { #if QT_VERSION >= 0x050e00 QPoint p = e->position().toPoint(); #else QPoint p = e->pos(); #endif bool doupd = false; if(_HBankRect.contains(p) != _HBankHovered) { _HBankHovered = !_HBankHovered; doupd = true; } if(_LBankRect.contains(p) != _LBankHovered) { _LBankHovered = !_LBankHovered; doupd = true; } if(_ProgRect.contains(p) != _ProgHovered) { _ProgHovered = !_ProgHovered; doupd = true; } if(doupd) update(); int hb = (_currentPatch >> 16) & 0xff; int lb = (_currentPatch >> 8) & 0xff; int pr = _currentPatch & 0xff; const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; const int hboff = is_unk || hb > 127; const int lboff = is_unk || lb > 127; const int proff = is_unk || pr > 127; const int lasthb = (_lastValidPatch >> 16) & 0xff; const int lastlb = (_lastValidPatch >> 8) & 0xff; const int lastpr = _lastValidPatch & 0xff; const bool last_is_unk = _lastValidPatch == MusECore::CTRL_VAL_UNKNOWN; const int lasthboff = last_is_unk || lasthb > 127; const int lastlboff = last_is_unk || lastlb > 127; const int lastproff = last_is_unk || lastpr > 127; const QPoint pixelDelta = e->pixelDelta(); const QPoint angleDegrees = e->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else { e->accept(); return; } int section = -1; int new_val = _currentPatch; if(_HBankHovered) { section = HBankSection; if(delta > 0 || !hboff) { if(hboff) { hb = _lastValidHB; if(lasthboff) hb = 0; if(is_unk) { lb = lastlb; pr = lastpr; if(last_is_unk) { lb = 0xff; pr = 0; } } } else { hb += delta; if(hb < 0) hb = 0xff; else if(hb > 127) hb = 127; } new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); } } else if(_LBankHovered) { section = LBankSection; if(delta > 0 || !lboff) { if(lboff) { lb = _lastValidLB; if(lastlboff) lb = 0; if(is_unk) { hb = lasthb; pr = lastpr; if(last_is_unk) { hb = 0xff; pr = 0; } } } else { lb += delta; if(lb < 0) lb = 0xff; else if(lb > 127) lb = 127; } new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); } } else if(_ProgHovered) { section = ProgSection; if(delta > 0 || !proff) { if(proff) { pr = _lastValidProg; if(lastproff) pr = 0; if(is_unk) { hb = lasthb; lb = lastlb; if(last_is_unk) { hb = 0xff; lb = 0xff; } } new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); } else { pr += delta; if(pr < 0) new_val = MusECore::CTRL_VAL_UNKNOWN; else { if(pr > 127) pr = 127; new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); } } } } else { e->ignore(); return QFrame::wheelEvent(e); } e->accept(); if(value() != new_val) { setValue(new_val); // Show a handy tooltip value box. if(_enableValueToolTips) #if QT_VERSION >= 0x050e00 showValueToolTip(e->globalPosition().toPoint(), section); #else showValueToolTip(e->globalPos(), section); #endif emit valueChanged(value(), _id); } //fprintf(stderr, "LCDPatchEdit::wheelEvent _HBankHovered:%d _LBankHovered:%d _ProgHovered:%d\n", _HBankHovered, _LBankHovered, _ProgHovered); } void LCDPatchEdit::mouseDoubleClickEvent(QMouseEvent* e) { const Qt::MouseButtons buttons = e->buttons(); const Qt::KeyboardModifiers keys = e->modifiers(); if(buttons == Qt::LeftButton && !_editMode) { DEBUG_LCD_WIDGETS(stderr, " left button\n"); if(keys == Qt::ControlModifier) { if(_HBankHovered || _LBankHovered || _ProgHovered) { int hb = (_currentPatch >> 16) & 0xff; int lb = (_currentPatch >> 8) & 0xff; int pr = _currentPatch & 0xff; const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; const int hboff = is_unk || hb > 127; const int lboff = is_unk || lb > 127; const int proff = is_unk || pr > 127; const int lasthb = (_lastValidPatch >> 16) & 0xff; const int lastlb = (_lastValidPatch >> 8) & 0xff; const int lastpr = _lastValidPatch & 0xff; const bool last_is_unk = _lastValidPatch == MusECore::CTRL_VAL_UNKNOWN; const bool last_hb_is_unk = _lastValidHB == MusECore::CTRL_VAL_UNKNOWN; const bool last_lb_is_unk = _lastValidLB == MusECore::CTRL_VAL_UNKNOWN; const bool last_pr_is_unk = _lastValidProg == MusECore::CTRL_VAL_UNKNOWN; int new_val = _currentPatch; if(_HBankHovered) { if(hboff) { hb = _lastValidHB; if(last_hb_is_unk) hb = 0; if(is_unk) { lb = lastlb; pr = lastpr; if(last_is_unk) { lb = 0xff; pr = 0; } } } else hb = 0xff; new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); } else if(_LBankHovered) { if(lboff) { lb = _lastValidLB; if(last_lb_is_unk) lb = 0; if(is_unk) { hb = lasthb; pr = lastpr; if(last_is_unk) { hb = 0xff; pr = 0; } } } else lb = 0xff; new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); } else if(_ProgHovered) { if(proff) { pr = _lastValidProg; if(last_pr_is_unk) pr = 0; if(is_unk) { hb = lasthb; lb = lastlb; if(last_is_unk) { hb = 0xff; lb = 0xff; } } new_val = ((hb & 0xff) << 16) | ((lb & 0xff) << 8) | (pr & 0xff); } else new_val = MusECore::CTRL_VAL_UNKNOWN; } if(new_val != value()) { setValue(new_val); emit valueChanged(value(), id()); } e->accept(); return; } } // A disabled spinbox up or down button will pass the event to the parent! Causes pseudo 'wrapping'. Eat it up. else if(keys == Qt::NoModifier && (!_editor || !_editor->hasFocus())) { int sec = -1; if(_HBankHovered) sec = HBankSection; else if(_LBankHovered) sec = LBankSection; else if(_ProgHovered) sec = ProgSection; if(sec != -1) { _curEditSection = sec; showEditor(); e->accept(); return; } } } e->ignore(); QFrame::mouseDoubleClickEvent(e); } void LCDPatchEdit::showEditor() { if(_editMode) return; if(!_editor) { DEBUG_LCD_WIDGETS(stderr, " creating editor\n"); _editor = new PopupDoubleSpinBox(this); _editor->setFrame(false); _editor->setFocusPolicy(Qt::WheelFocus); _editor->setDecimals(0); _editor->setSpecialValueText(tr("off")); _editor->setMinimum(0); _editor->setMaximum(128); connect(_editor, SIGNAL(returnPressed()), SLOT(editorReturnPressed())); connect(_editor, SIGNAL(escapePressed()), SLOT(editorEscapePressed())); } int w = width(); //if (w < _editor->sizeHint().width()) // w = _editor->sizeHint().width(); QRect r; const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; switch(_curEditSection) { case HBankSection: { r = _HBankRect; int hb = (_currentPatch >> 16) & 0xff; if(is_unk || hb < 0 || hb > 127) hb = 0; else ++hb; _editor->setValue(hb); } break; case LBankSection: { r = _LBankRect; int lb = (_currentPatch >> 8) & 0xff; if(is_unk || lb < 0 || lb > 127) lb = 0; else ++lb; _editor->setValue(lb); } break; case ProgSection: { r = _ProgRect; int pr = _currentPatch & 0xff; if(is_unk || pr < 0 || pr > 127) pr = 0; else ++pr; _editor->setValue(pr); } break; } switch(_orient) { case PatchHorizontal: _editor->setGeometry(0, 0, w, height()); break; case PatchVertical: _editor->setGeometry(0, r.y(), w, r.height()); break; } DEBUG_LCD_WIDGETS(stderr, " x:%d y:%d w:%d h:%d\n", _editor->x(), _editor->y(), w, _editor->height()); _editor->selectAll(); _editMode = true; _editor->show(); _editor->setFocus(); } QString LCDPatchEdit::toolTipValueText(int section) const { const int hb = (_currentPatch >> 16) & 0xff; const int lb = (_currentPatch >> 8) & 0xff; const int pr = _currentPatch & 0xff; const bool is_unk = _currentPatch == MusECore::CTRL_VAL_UNKNOWN; const int hboff = is_unk || hb > 127; const int lboff = is_unk || lb > 127; const int proff = is_unk || pr > 127; const QString off_text = tr("off"); const QString hb_desc_text = tr("High bank"); const QString lb_desc_text = tr("Low bank"); const QString pr_desc_text = tr("Program"); const QString hbtext = hboff ? off_text : QString::number(hb + 1); const QString lbtext = lboff ? off_text : QString::number(lb + 1); const QString prtext = proff ? off_text : QString::number(pr + 1); switch(section) { case HBankSection: return QString("%1: %2") .arg(hb_desc_text).arg(hbtext); break; case LBankSection: return QString("%1: %2") .arg(lb_desc_text).arg(lbtext); break; case ProgSection: return QString("%1: %2") .arg(pr_desc_text).arg(prtext); break; default: return QString("%1: %2\n%3: %4\n%5: %6") .arg(hb_desc_text).arg(hbtext) .arg(lb_desc_text).arg(lbtext) .arg(pr_desc_text).arg(prtext); break; } } void LCDPatchEdit::showValueToolTip(QPoint /*p*/, int section) { const QString txt = toolTipValueText(section); if(!txt.isEmpty()) { QToolTip::showText(mapToGlobal(pos()), txt, 0, QRect(), 3000); } } } // namespace MusEGui muse-4.0.0/src/muse/components/lcd_widgets.h000066400000000000000000000163301404105311000210420ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // lcd_widgets.h // (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LCD_WIDGETS_H__ #define __LCD_WIDGETS_H__ #include class QPainter; class QColor; class QRect; class QString; class QEvent; class QMouseEvent; class QWheelEvent; class QKeyEvent; namespace MusEGui { class PopupDoubleSpinBox; //--------------------------------------------------------- // LCDPainter //--------------------------------------------------------- class LCDPainter { public: enum ScalePos { None, Left, Right, Top, Bottom, Embedded }; enum TextHighlightMode { TextHighlightNone, TextHighlightAlways, TextHighlightSplit, TextHighlightShadow }; private: public: LCDPainter(); void drawCharacter(QPainter* painter, const QRect& rect, char asciiChar); void drawText(QPainter* painter, const QRect& rect, const QString& text, int flags = 0); }; //--------------------------------------------------------- // LCDPatchEdit //--------------------------------------------------------- class LCDPatchEdit : public QFrame { Q_OBJECT //Q_PROPERTY(QString text READ text WRITE setText) // Q_PROPERTY( QColor readoutColor READ readoutColor WRITE setReadoutColor ) public: enum PatchSections { HBankSection, LBankSection, ProgSection }; enum PatchOrientation { PatchHorizontal = 0, PatchVertical }; protected: PatchOrientation _orient; int _maxAliasedPointSize; int _xMargin; int _yMargin; int _sectionSpacing; int _currentPatch; int _lastValidPatch; int _lastValidHB; int _lastValidLB; int _lastValidProg; QColor _readoutColor; QColor _bgColor; QColor _bgActiveColor; QColor _borderColor; bool _style3d; int _radius; LCDPainter* _LCDPainter; int _id; int _fontPointMin; bool _fontIgnoreHeight; bool _fontIgnoreWidth; QString _text; QFont _curFont; bool _enableValueToolTips; QRect _HBankRect; QRect _LBankRect; QRect _ProgRect; QRect _HBankFieldRect; QRect _LBankFieldRect; QRect _ProgFieldRect; bool _HBankHovered; bool _LBankHovered; bool _ProgHovered; PopupDoubleSpinBox* _editor; bool _editMode; int _curEditSection; bool autoAdjustFontSize(); // The total active drawing area, not including margins. QRect activeDrawingArea() const; void showEditor(); // Show a handy tooltip value box. void showValueToolTip(QPoint, int section = -1); virtual void paintEvent(QPaintEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual void wheelEvent(QWheelEvent*); virtual void mouseDoubleClickEvent(QMouseEvent*); virtual void resizeEvent(QResizeEvent*); virtual void mousePressEvent(QMouseEvent*); virtual void mouseReleaseEvent(QMouseEvent*); virtual void enterEvent(QEvent*); virtual void leaveEvent(QEvent*); virtual void keyPressEvent(QKeyEvent*); virtual bool event(QEvent*); protected slots: void editorReturnPressed(); void editorEscapePressed(); signals: void pressed(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); void released(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); void valueChanged(int value, int id); void rightClicked(QPoint p, int id); public: explicit LCDPatchEdit(QWidget* parent = 0, int minFontPoint = 5, bool ignoreHeight = true, bool ignoreWidth = false, const QString& text = QString(), const QColor& readoutColor = QColor(0,255,255), Qt::WindowFlags flags = Qt::Widget); virtual ~LCDPatchEdit(); // The width of a character, not including inter-character space, in a given active area. static int charWidth(const QRect& aRect); // The amount of space between the blocks of digits, for a given character width. static int readoutMargin(int charWidth); static QSize getMinimumSizeHint(const QFontMetrics& fm, int xMargin = 0, int yMargin = 0, PatchOrientation orient = PatchHorizontal ); int id() const { return _id; } void setId(int i) { _id = i; } void setReadoutOrientation(PatchOrientation); void setReadoutColor(const QColor& c) { _readoutColor = c; update(); } void setBgColor(const QColor& c) { _bgColor = c; update(); } void setBgActiveColor(const QColor& c) { _bgActiveColor = c; update(); } void setBorderColor(const QColor& c) { _borderColor = c; update(); } void setStyle3d(const bool s) { _style3d = s; } void setRadius(const int r) { _radius = r; } int value() const; void setValue(int v); void setLastValidPatch(int v); void setLastValidBytes(int hbank, int lbank, int prog); bool valueToolTipsEnabled() const { return _enableValueToolTips; } void setEnableValueToolTips(bool v) { _enableValueToolTips = v; } QString toolTipValueText(int section = -1) const; // At what point size to switch from aliased text to non-aliased text. Zero means always use anti-aliasing. // Here in CompactPatchEdit, this only affects the CompactSliders so far, not the patch label. // If -1, no value has been set and default is each widget's setting. int maxAliasedPointSize() const { return _maxAliasedPointSize; } // Sets at what point size to switch from aliased text (brighter, crisper but may look too jagged and unreadable with some fonts) // to non-aliased text (dimmer, fuzzier but looks better). Zero means always use anti-aliasing. Default is each widget's setting. // Here in CompactPatchEdit, this only affects the CompactSliders so far, not the patch label. void setMaxAliasedPointSize(int sz); const QString& text() const { return _text; } void setText(const QString& txt); int fontPointMin() const { return _fontPointMin; } void setFontPointMin(int point); bool fontIgnoreWidth() const { return _fontIgnoreWidth; } bool fontIgnoreHeight() const { return _fontIgnoreHeight; } void setFontIgnoreDimensions(bool ignoreHeight, bool ignoreWidth = false); void setMargins(int hor, int vert); virtual QSize sizeHint() const; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/metronome.cpp000066400000000000000000001157401404105311000211170ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: metronome.cpp,v 1.2.2.1 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include "metronome.h" #include "midi_consts.h" #include "ticksynth.h" #include "globals.h" #include "gconfig.h" #include "song.h" #include "track.h" #include "audio.h" #include "operations.h" #include "pixmap_button.h" #include "icons.h" namespace MusEGui { MetronomePresetItemWidget::MetronomePresetItemWidget( QIcon* on_icon, QIcon* off_icon, const MusECore::MetroAccentsStruct& mas, bool hasFixedIconSize, int margin, QWidget* parent, const char* name) : QFrame(parent), _onIcon(on_icon), _offIcon(off_icon), _hasFixedIconSize(hasFixedIconSize), _margin(margin), _accents(mas) { setObjectName(name); setAttribute(Qt::WA_TranslucentBackground); _iconSize = QSize(10, 10); } QSize MetronomePresetItemWidget::minimumSizeHint () const { return QSize(10, 10); } void MetronomePresetItemWidget::setMargin(int v) { _margin = v; update(); } void MetronomePresetItemWidget::setOffIcon(QIcon* pm) { _offIcon = pm; update(); } void MetronomePresetItemWidget::setOnIcon(QIcon* pm) { _onIcon = pm; update(); } void MetronomePresetItemWidget::setIconSize(const QSize sz) { _iconSize = sz; updateGeometry(); } QSize MetronomePresetItemWidget::sizeHint() const { const int beats = _accents._accents.size(); // TODO Ask style for margins. const QSize isz = iconSize(); const int fmh = fontMetrics().lineSpacing() + 2; const int iw = isz.width() + 2; const int ih = isz.height() + 2; const int h = (_hasFixedIconSize && ih > fmh) ? ih : fmh; const int w = beats * ((_hasFixedIconSize && iw > h) ? iw : h + 2); const int fin_w = w + 2 * _margin; // For two lines: 5 margins. const int fin_h = 2 * h + 2 * _margin + 1; return QSize(fin_w, fin_h); } void MetronomePresetItemWidget::paintEvent(QPaintEvent* ev) { ev->accept(); QPainter p(this); const int beats = _accents._accents.size(); // TODO Ask style for margins. const QSize isz = iconSize(); const int fmh = fontMetrics().lineSpacing() + 2; const int iw = isz.width() + 2; const int ih = isz.height() + 2; const int ico_h = (_hasFixedIconSize && ih > fmh) ? ih : fmh; const int ico_w = ((_hasFixedIconSize && iw > ico_h) ? iw : ico_h + 2); QIcon::Mode mode; if(isEnabled()) mode = hasFocus() ? QIcon::Selected : QIcon::Normal; else mode = QIcon::Disabled; QIcon::State state; bool acc1, acc2; QIcon* ico; QRect r; for(int i = 0; i < beats; ++i) { const MusECore::MetroAccent& ma = _accents._accents.at(i); acc1 = ma._accentType & MusECore::MetroAccent::Accent1; acc2 = ma._accentType & MusECore::MetroAccent::Accent2; r = QRect(i * ico_w, _margin, ico_w, ico_h - 1); state = acc1 ? QIcon::On : QIcon::Off; ico = acc1 ? _onIcon : _offIcon; if(ico) ico->paint(&p, r, Qt::AlignCenter, mode, state); r.moveTop(_margin + ico_h + 1); state = acc2 ? QIcon::On : QIcon::Off; ico = acc2 ? _onIcon : _offIcon; if(ico) ico->paint(&p, r, Qt::AlignCenter, mode, state); } } //--------------------------------------------------------- // MetronomeConfig //--------------------------------------------------------- MetronomeConfig::MetronomeConfig(QWidget* parent) : QDialog(parent) { setupUi(this); accentPresetsTypeItemActivated(0); updateValues(); // TODO Let's only do this once for now. It may be too slow to call with every update or songChanged signal, // so for now any user-additional files in our metronome waves directory will need a restart... fillSoundFiles(); // Special for these two: Need qt helper overload for these lambdas. connect(accentBeats, QOverload::of(&QSpinBox::valueChanged), [=](int v) { accentBeatsChanged(v); } ); connect(accentPresetTypeList, QOverload::of(&QComboBox::activated), [=](int index) { accentPresetsTypeItemActivated(index); } ); connect(buttonApply, &QPushButton::clicked, [this]() { apply(); } ); connect(midiClick, &QCheckBox::toggled, [this](bool v) { midiClickChanged(v); } ); connect(precountEnable, &QCheckBox::toggled, [this](bool v) { precountEnableChanged(v); } ); connect(precountFromMastertrack, &QCheckBox::toggled, [this](bool v) { precountFromMastertrackChanged(v); } ); connect(audioBeepRoutesButton, &QPushButton::clicked, [this]() { audioBeepRoutesClicked(); } ); connect(volumeSlider, &QSlider::valueChanged, [this](int v) { volumeChanged(v); } ); connect(measVolumeSlider, &QSlider::valueChanged, [this](int v) { measVolumeChanged(v); } ); connect(beatVolumeSlider, &QSlider::valueChanged, [this](int v) { beatVolumeChanged(v); } ); connect(accent1VolumeSlider, &QSlider::valueChanged, [this](int v) { accent1VolumeChanged(v); } ); connect(accent2VolumeSlider, &QSlider::valueChanged, [this](int v) { accent2VolumeChanged(v); } ); connect(radioSamples2, &QRadioButton::toggled, [this]() { switchSamples(); } ); connect(MusEGlobal::song, &MusECore::Song::songChanged, [this](MusECore::SongChangedStruct_t type) { songChanged(type); } ); connect(globalSettingsButton, &QRadioButton::toggled, [this]() { switchSettings(); } ); connect(accentPresets, &QListWidget::itemActivated, [this](QListWidgetItem* item) { accentPresetsItemActivated(item); } ); connect(addAccentPresetButton, &QToolButton::clicked, [this]() { addAccentsPresetClicked(); } ); connect(delAccentPresetButton, &QToolButton::clicked, [this]() { delAccentsPresetClicked(); } ); connect(useAccentPresetButton, &QToolButton::clicked, [this]() { useAccentsPresetClicked(); } ); connect(accentsDefaultsButton, &QToolButton::clicked, [this]() { accentsResetDefaultClicked(); } ); } void MetronomeConfig::songChanged(MusECore::SongChangedStruct_t type) { if(type & SC_METRONOME) { updateValues(); } } void MetronomeConfig::setAccentsSettings(int beats, const MusECore::MetroAccentsStruct& mas) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; // Make a working copy, to replace the original. MusECore::MetroAccentsMap* new_accents_map = new MusECore::MetroAccentsMap(*metro_settings->metroAccentsMap); // Manipulate the working copy... if(mas.isBlank()) { // Don't allow adding if there are no accents set. Erase any existing found. MusECore::MetroAccentsMap::iterator imap = new_accents_map->find(beats); if(imap != new_accents_map->end()) new_accents_map->erase(imap); } else { std::pair res = new_accents_map->insert(std::pair(beats, mas)); if(!res.second) res.first->second = mas; } MusECore::PendingOperationList operations; // Takes ownership of the original which it deletes at the end of the operation. operations.add(MusECore::PendingOperationItem( &metro_settings->metroAccentsMap, new_accents_map, MusECore::PendingOperationItem::ModifyMetronomeAccentMap)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } void MetronomeConfig::accentPresetsItemActivated(QListWidgetItem* /*item*/) { updateAccentPresetAddButton(); updateAccentPresetDelButton(); } void MetronomeConfig::accentPresetsTypeItemActivated(int /*idx*/) { const int beats = accentBeats->value(); fillAccentPresets(beats); // configureAccentButtons(beats); // updateAccentButtons(beats); updateAccentPresetAddButton(); updateAccentPresetDelButton(); } void MetronomeConfig::getAccents(int beats, MusECore::MetroAccentsStruct* mas) const { QLayoutItem* item; IconButton* button; const int count1 = accent1ButtonsLayout->count(); const int count2 = accent2ButtonsLayout->count(); for(int i = 0; i < beats; ++i) { MusECore::MetroAccent ma; if(i < count1) { item = accent1ButtonsLayout->itemAt(i); if(item && !item->isEmpty()) { button = static_cast(item->widget()); if(button && button->isChecked()) ma._accentType |= MusECore::MetroAccent::Accent1; } } if(i < count2) { item = accent2ButtonsLayout->itemAt(i); if(item && !item->isEmpty()) { button = static_cast(item->widget()); if(button && button->isChecked()) ma._accentType |= MusECore::MetroAccent::Accent2; } } mas->_accents.push_back(ma); } } void MetronomeConfig::changeAccents() { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if(!metro_settings->metroAccentsMap) return; const int beats = accentBeats->value(); if(beats <= 0) return; MusECore::MetroAccentsStruct mas(MusECore::MetroAccentsStruct::User); getAccents(beats, &mas); setAccentsSettings(beats, mas); } void MetronomeConfig::clearAccents(MusECore::MetroAccent::AccentType row) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if(!metro_settings->metroAccentsMap) return; const int beats = accentBeats->value(); if(beats <= 0) return; MusECore::MetroAccentsStruct mas(MusECore::MetroAccentsStruct::User); getAccents(beats, &mas); mas.blank(row); setAccentsSettings(beats, mas); } //--------------------------------------------------------- // fillSoundFiles //--------------------------------------------------------- void MetronomeConfig::fillSoundFiles() { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; QDir metroPath(MusEGlobal::museGlobalShare+"/metronome"); QStringList filters; filters.append("*.wav"); QStringList klickfiles = metroPath.entryList(filters); measSampleCombo->clear(); beatSampleCombo->clear(); accent1SampleCombo->clear(); accent2SampleCombo->clear(); measSampleCombo->addItems(klickfiles); beatSampleCombo->addItems(klickfiles); accent1SampleCombo->addItems(klickfiles); accent2SampleCombo->addItems(klickfiles); measSampleCombo->setCurrentIndex(klickfiles.indexOf(metro_settings->measSample)); beatSampleCombo->setCurrentIndex(klickfiles.indexOf(metro_settings->beatSample)); accent1SampleCombo->setCurrentIndex(klickfiles.indexOf(metro_settings->accent1Sample)); accent2SampleCombo->setCurrentIndex(klickfiles.indexOf(metro_settings->accent2Sample)); } void MetronomeConfig::updateValues() { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; volumeSlider->blockSignals(true); measVolumeSlider->blockSignals(true); beatVolumeSlider->blockSignals(true); accent1VolumeSlider->blockSignals(true); accent2VolumeSlider->blockSignals(true); radioSamples2->blockSignals(true); radioSamples4->blockSignals(true); songSettingsButton->blockSignals(true); globalSettingsButton->blockSignals(true); measureNote->blockSignals(true); measureVelocity->blockSignals(true); beatNote->blockSignals(true); beatVelocity->blockSignals(true); accent1Note->blockSignals(true); accent1Velocity->blockSignals(true); accent2Note->blockSignals(true); accent2Velocity->blockSignals(true); midiChannel->blockSignals(true); midiPort->blockSignals(true); precountBars->blockSignals(true); precountEnable->blockSignals(true); precountFromMastertrack->blockSignals(true); precountSigZ->blockSignals(true); precountSigN->blockSignals(true); precountOnPlay->blockSignals(true); precountMuteMetronome->blockSignals(true); midiClick->blockSignals(true); audioBeep->blockSignals(true); if (MusEGlobal::metroUseSongSettings) songSettingsButton->setChecked(true); else globalSettingsButton->setChecked(true); volumeSlider->setValue(metro_settings->audioClickVolume*100); measVolumeSlider->setValue(metro_settings->measClickVolume*100); beatVolumeSlider->setValue(metro_settings->beatClickVolume*100); accent1VolumeSlider->setValue(metro_settings->accent1ClickVolume*100); accent2VolumeSlider->setValue(metro_settings->accent2ClickVolume*100); if (metro_settings->clickSamples == metro_settings->origSamples) radioSamples2->setChecked(true); else radioSamples4->setChecked(true); volumeLabel->setText(QString::number(int(metro_settings->audioClickVolume*99))); measVolumeLabel->setText(QString::number(int(metro_settings->measClickVolume*99))); beatVolumeLabel->setText(QString::number(int(metro_settings->beatClickVolume*99))); accent1VolumeLabel->setText(QString::number(int(metro_settings->accent1ClickVolume*99))); accent2VolumeLabel->setText(QString::number(int(metro_settings->accent2ClickVolume*99))); measureNote->setValue(metro_settings->measureClickNote); measureVelocity->setValue(metro_settings->measureClickVelo); beatNote->setValue(metro_settings->beatClickNote); beatVelocity->setValue(metro_settings->beatClickVelo); accent1Note->setValue(metro_settings->accentClick1); accent1Velocity->setValue(metro_settings->accentClick1Velo); accent2Note->setValue(metro_settings->accentClick2); accent2Velocity->setValue(metro_settings->accentClick2Velo); midiChannel->setValue(metro_settings->clickChan+1); midiPort->setValue(metro_settings->clickPort+1); precountBars->setValue(metro_settings->preMeasures); precountEnable->setChecked(metro_settings->precountEnableFlag); precountFromMastertrack->setChecked(metro_settings->precountFromMastertrackFlag); precountSigZ->setValue(metro_settings->precountSigZ); precountSigN->setValue(metro_settings->precountSigN); precountOnPlay->setChecked(metro_settings->precountOnPlay); precountMuteMetronome->setChecked(metro_settings->precountMuteMetronome); midiClick->setChecked(metro_settings->midiClickFlag); audioBeep->setChecked(metro_settings->audioClickFlag); midiClickChanged(metro_settings->midiClickFlag); precountEnableChanged(metro_settings->precountEnableFlag); switchSamples(); // to disable gui elements // TODO Let's only do this once in the constructor for now. It may be too slow to call with every update or // songChanged signal, so for now any user-additional files in our metronome waves directory will need a restart... //fillSoundFiles(); const int beats = accentBeats->value(); configureAccentButtons(beats); updateAccentButtons(beats); updateAccentPresetAddButton(); updateAccentPresetDelButton(); volumeSlider->blockSignals(false); measVolumeSlider->blockSignals(false); beatVolumeSlider->blockSignals(false); accent1VolumeSlider->blockSignals(false); accent2VolumeSlider->blockSignals(false); radioSamples2->blockSignals(false); radioSamples4->blockSignals(false); songSettingsButton->blockSignals(false); globalSettingsButton->blockSignals(false); measureNote->blockSignals(false); measureVelocity->blockSignals(false); beatNote->blockSignals(false); beatVelocity->blockSignals(false); accent1Note->blockSignals(false); accent1Velocity->blockSignals(false); accent2Note->blockSignals(false); accent2Velocity->blockSignals(false); midiChannel->blockSignals(false); midiPort->blockSignals(false); precountBars->blockSignals(false); precountEnable->blockSignals(false); precountFromMastertrack->blockSignals(false); precountSigZ->blockSignals(false); precountSigN->blockSignals(false); precountOnPlay->blockSignals(false); precountMuteMetronome->blockSignals(false); midiClick->blockSignals(false); audioBeep->blockSignals(false); } //--------------------------------------------------------- // audioBeepRoutesClicked //--------------------------------------------------------- void MetronomeConfig::audioBeepRoutesClicked() { if(MusEGlobal::song->outputs()->size() == 0) return; QMenu* pup = new QMenu; MusECore::OutputList* ol = MusEGlobal::song->outputs(); int nn = 0; for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) { QAction* action = pup->addAction((*iao)->name()); action->setCheckable(true); action->setData(nn); if((*iao)->sendMetronome()) action->setChecked(true); ++nn; } QAction* clickaction = pup->exec(QCursor::pos()); if (clickaction) { nn = 0; for(MusECore::iAudioOutput iao = ol->begin(); iao != ol->end(); ++iao) { if (nn == clickaction->data()) { MusEGlobal::audio->msgSetSendMetronome(*iao, clickaction->isChecked()); break; } ++nn; } } delete pup; audioBeepRoutesButton->setDown(false); // pup->exec() catches mouse release event } //--------------------------------------------------------- // accept //--------------------------------------------------------- void MetronomeConfig::accept() { apply(); QDialog::accept(); } //--------------------------------------------------------- // apply //--------------------------------------------------------- void MetronomeConfig::apply() { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; metro_settings->measureClickNote = measureNote->value(); metro_settings->measureClickVelo = measureVelocity->value(); metro_settings->beatClickNote = beatNote->value(); metro_settings->beatClickVelo = beatVelocity->value(); metro_settings->accentClick1 = accent1Note->value(); metro_settings->accentClick1Velo = accent1Velocity->value(); metro_settings->accentClick2 = accent2Note->value(); metro_settings->accentClick2Velo = accent2Velocity->value(); metro_settings->clickChan = midiChannel->value() - 1; metro_settings->clickPort = midiPort->value() - 1; metro_settings->preMeasures = precountBars->value(); metro_settings->precountEnableFlag = precountEnable->isChecked(); metro_settings->precountFromMastertrackFlag = precountFromMastertrack->isChecked(); metro_settings->precountSigZ = precountSigZ->value(); metro_settings->precountSigN = precountSigN->value(); metro_settings->precountOnPlay = precountOnPlay->isChecked(); metro_settings->precountMuteMetronome = precountMuteMetronome->isChecked(); metro_settings->midiClickFlag = midiClick->isChecked(); metro_settings->audioClickFlag = audioBeep->isChecked(); metro_settings->measSample = measSampleCombo->currentText(); metro_settings->beatSample = beatSampleCombo->currentText(); metro_settings->accent1Sample = accent1SampleCombo->currentText(); metro_settings->accent2Sample = accent2SampleCombo->currentText(); MusECore::PendingOperationList operations; MusECore::metronome->initSamplesOperation(operations); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // reject //--------------------------------------------------------- void MetronomeConfig::reject() { QDialog::reject(); } //--------------------------------------------------------- // midiClickChanged //--------------------------------------------------------- void MetronomeConfig::midiClickChanged(bool flag) { measureNote->setEnabled(flag); measureVelocity->setEnabled(flag); beatNote->setEnabled(flag); beatVelocity->setEnabled(flag); accent1Note->setEnabled(flag && radioSamples4->isChecked()); accent1Velocity->setEnabled(flag && radioSamples4->isChecked()); accent2Note->setEnabled(flag && radioSamples4->isChecked()); accent2Velocity->setEnabled(flag && radioSamples4->isChecked()); midiChannel->setEnabled(flag); midiPort->setEnabled(flag); } void MetronomeConfig::precountEnableChanged(bool flag) { precountBars->setEnabled(flag); precountFromMastertrack->setEnabled(flag); precountSigZ->setEnabled(flag && !precountFromMastertrack->isChecked()); precountSigN->setEnabled(flag && !precountFromMastertrack->isChecked()); precountOnPlay->setEnabled(flag); precountMuteMetronome->setEnabled(flag); //precountPrerecord->setEnabled(flag); // Not supported yet. //precountPreroll->setEnabled(flag); // Not supported yet. } void MetronomeConfig::precountFromMastertrackChanged(bool flag) { precountSigZ->setEnabled(!flag); precountSigN->setEnabled(!flag); } // these values are directly applied, not using the Apply button, it just seems more usable this way. void MetronomeConfig::volumeChanged(int volume) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; metro_settings->audioClickVolume=volume/100.0; volumeLabel->setText(QString::number(int(metro_settings->audioClickVolume*99))); } void MetronomeConfig::measVolumeChanged(int volume) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; metro_settings->measClickVolume=volume/100.0; measVolumeLabel->setText(QString::number(int(metro_settings->measClickVolume*99))); } void MetronomeConfig::beatVolumeChanged(int volume) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; metro_settings->beatClickVolume=volume/100.0; beatVolumeLabel->setText(QString::number(int(metro_settings->beatClickVolume*99))); } void MetronomeConfig::accent1VolumeChanged(int volume) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; metro_settings->accent1ClickVolume=volume/100.0; accent1VolumeLabel->setText(QString::number(int(metro_settings->accent1ClickVolume*99))); } void MetronomeConfig::accent2VolumeChanged(int volume) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; metro_settings->accent2ClickVolume=volume/100.0; accent2VolumeLabel->setText(QString::number(int(metro_settings->accent2ClickVolume*99))); } void MetronomeConfig::switchSamples() { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if (radioSamples2->isChecked()) { metro_settings->clickSamples = metro_settings->origSamples; accent1VolumeSlider->setEnabled(false); accent2VolumeSlider->setEnabled(false); accent1Label->setEnabled(false); accent2Label->setEnabled(false); accent1Note->setEnabled(false); accent1Velocity->setEnabled(false); accent2Note->setEnabled(false); accent2Velocity->setEnabled(false); accentsTab->setEnabled(false); } else { metro_settings->clickSamples = metro_settings->newSamples; accent1VolumeSlider->setEnabled(true); accent2VolumeSlider->setEnabled(true); accent1Label->setEnabled(true); accent2Label->setEnabled(true); accent1Note->setEnabled(midiClick->isChecked()); accent1Velocity->setEnabled(midiClick->isChecked()); accent2Note->setEnabled(midiClick->isChecked()); accent2Velocity->setEnabled(midiClick->isChecked()); accentsTab->setEnabled(true); } } void MetronomeConfig::switchSettings() { //const bool new_val = !MusEGlobal::metroUseSongSettings; const bool new_val = songSettingsButton->isChecked(); MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem( &MusEGlobal::metroUseSongSettings, new_val, MusECore::PendingOperationItem::SwitchMetronomeSettings)); // Let it do a songChanged update. // Let the songChanged handler update all the values and buttons... MusEGlobal::audio->msgExecutePendingOperations(operations, true); } void MetronomeConfig::accentBeatsChanged(int beats) { fillAccentPresets(beats); configureAccentButtons(beats); updateAccentButtons(beats); updateAccentPresetAddButton(); updateAccentPresetDelButton(); } void MetronomeConfig::configureAccentButtons(int beats) { const int count1 = accent1ButtonsLayout->count(); const int count2 = accent2ButtonsLayout->count(); // Nothing to do? if(beats == 0 && count1 == 0 && count2 == 0) return; const int button_count = beats + 1; if(count1 == button_count && count2 == button_count) return; QLayoutItem* item; QWidget* w; QList wl1, wl2; if(count1 != button_count) { for(int i = 0; i < count1; ++i) { item = accent1ButtonsLayout->itemAt(i); if(item /*&& !item->isEmpty()*/) { w = item->widget(); if(w) wl1.append(w); } } const int wl1_sz = wl1.size(); for(int i = 0; i < wl1_sz; ++i) { delete wl1.at(i); } for(int i = 0; i < beats; ++i) { IconButton* b = new IconButton(ledGreenSVGIcon, ledGreenDarkSVGIcon, 0, 0, false, true); b->setCheckable(true); connect(b, &IconButton::clicked, [this]() { changeAccents(); } ); accent1ButtonsLayout->addWidget(b); } // Add one more button - the 'clear all' button. if(beats > 0) { QToolButton *b = new QToolButton(this); b->setIcon(*clearSVGIcon); b->setToolTip(tr("Clear")); connect(b, &QToolButton::clicked, [this]() { clearAccents(MusECore::MetroAccent::Accent1); } ); accent1ButtonsLayout->addWidget(b); } } if(count2 != button_count) { for(int i = 0; i < count2; ++i) { item = accent2ButtonsLayout->itemAt(i); if(item /*&& !item->isEmpty()*/) { w = item->widget(); if(w) wl2.append(w); } } const int wl2_sz = wl2.size(); for(int i = 0; i < wl2_sz; ++i) { //disconnect(wl2.at(i)); delete wl2.at(i); } for(int i = 0; i < beats; ++i) { IconButton* b = new IconButton(ledGreenSVGIcon, ledGreenDarkSVGIcon, 0, 0, false, true); b->setCheckable(true); connect(b, &IconButton::clicked, [this]() { changeAccents(); } ); accent2ButtonsLayout->addWidget(b); } // Add one more button - the 'clear all' button. if(beats > 0) { QToolButton *b = new QToolButton(this); b->setIcon(*clearSVGIcon); b->setToolTip(tr("Clear")); connect(b, &QToolButton::clicked, [this]() { clearAccents(MusECore::MetroAccent::Accent2); } ); accent2ButtonsLayout->addWidget(b); } } } void MetronomeConfig::updateAccentPresetAddButton() { const int beats = accentBeats->value(); if(beats <= 0 || accentPresetTypeList->currentIndex() != UserPresetType) { addAccentPresetButton->setEnabled(false); return; } MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if(!metro_settings->metroAccentsMap) { addAccentPresetButton->setEnabled(false); return; } const MusECore::MetroAccentsMap::const_iterator imap = metro_settings->metroAccentsMap->find(beats); if(imap == metro_settings->metroAccentsMap->cend()) { addAccentPresetButton->setEnabled(false); return; } MusECore::MetroAccentsStruct mas = imap->second; // Don't allow adding if there are no accents set. if(mas._type != MusECore::MetroAccentsStruct::User || mas.isBlank()) { addAccentPresetButton->setEnabled(false); return; } MusECore::MetroAccentsPresetsMap::const_iterator ipm = MusEGlobal::metroAccentPresets.find(beats); if(ipm == MusEGlobal::metroAccentPresets.cend()) { addAccentPresetButton->setEnabled(true); } else { // Change the type to user preset just in case. mas._type = MusECore::MetroAccentsStruct::UserPreset; addAccentPresetButton->setEnabled( ipm->second.find(mas, MusECore::MetroAccentsStruct::AllTypes) == ipm->second.end()); } } void MetronomeConfig::updateAccentPresetDelButton() { QListWidgetItem* item = accentPresets->currentItem(); delAccentPresetButton->setEnabled(item && item->data(PresetTypeRole).toInt() == MusECore::MetroAccentsStruct::UserPreset); } void MetronomeConfig::updateAccentButtons(int beats) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; const int count1 = accent1ButtonsLayout->count(); const int count2 = accent2ButtonsLayout->count(); const MusECore::MetroAccents* accents = nullptr; int accents_sz = 0; if(metro_settings->metroAccentsMap) { const MusECore::MetroAccentsMap::const_iterator imap = metro_settings->metroAccentsMap->find(beats); if(imap != metro_settings->metroAccentsMap->cend()) { const MusECore::MetroAccentsStruct& mas = imap->second; accents = &mas._accents; accents_sz = accents->size(); } } QLayoutItem* item; QWidget* w; IconButton* button; for(int i = 0; i < beats; ++i) { if(i < count1) { item = accent1ButtonsLayout->itemAt(i); if(item /*&& !item->isEmpty()*/) { w = item->widget(); if(w) { button = static_cast(w); button->blockSignals(true); button->setChecked(accents && i < accents_sz && (accents->at(i)._accentType & MusECore::MetroAccent::Accent1)); button->blockSignals(false); } } } if(i < count2) { item = accent2ButtonsLayout->itemAt(i); if(item /*&& !item->isEmpty()*/) { w = item->widget(); if(w) { button = static_cast(w); button->blockSignals(true); button->setChecked(accents && i < accents_sz && (accents->at(i)._accentType & MusECore::MetroAccent::Accent2)); button->blockSignals(false); } } } } } bool MetronomeConfig::addAccentPreset(int beats, const MusECore::MetroAccentsStruct& mas) { // Accept either user presets or factory presets. if(mas._type != MusECore::MetroAccentsStruct::UserPreset && mas._type != MusECore::MetroAccentsStruct::FactoryPreset) return false; QListWidgetItem* new_item = new QListWidgetItem(); MetronomePresetItemWidget* new_widget = new MetronomePresetItemWidget( ledGreenSVGIcon, ledGreenDarkSVGIcon, mas, true, 4, accentPresets, "MetronomePresetItemWidget"); new_item->setData(BeatsRole, beats); new_item->setData(PresetIdRole, qlonglong(mas.id())); new_item->setData(PresetTypeRole, mas._type); // Find the LAST user preset item and insert after it, or else just append: const int sz = accentPresets->count(); int idx = sz - 1; for( ; idx >= 0; --idx) { const QListWidgetItem* it = accentPresets->item(idx); if(it && it->data(PresetTypeRole).toInt() == MusECore::MetroAccentsStruct::UserPreset) break; } accentPresets->blockSignals(true); // If no user presets were found or the one found is the last item, just append. if(idx == -1 || (idx == sz - 1)) accentPresets->addItem(new_item); else accentPresets->insertItem(idx + 1, new_item); accentPresets->setItemWidget(new_item, new_widget); new_item->setSizeHint(new_widget->sizeHint()); accentPresets->blockSignals(false); return true; } void MetronomeConfig::fillAccentPresets(int beats) { accentPresets->blockSignals(true); accentPresets->clear(); accentPresets->blockSignals(false); const MusECore::MetroAccentsPresetsMap::const_iterator ipm = MusEGlobal::metroAccentPresets.find(beats); if(ipm == MusEGlobal::metroAccentPresets.cend()) return; const int type_idx = accentPresetTypeList->currentIndex(); if(type_idx != FactoryPresetType && type_idx != UserPresetType) return; const MusECore::MetroAccentsPresets& mp = ipm->second; const int map_sz = mp.size(); // Sort the visual items: Put the factory presets before the user presets. // TODO: Actually, user presets are more important since the user made them, // so maybe put them first, but then the factory presets would appear // below them, which seems kinda counter-intuitive... // Hm, but when we add a new user preset it wants to go to the end of the list... if(type_idx == FactoryPresetType) { for(int i = 0; i != map_sz; ++i) { const MusECore::MetroAccentsStruct& mas = mp.at(i); // Don't bother adding if there are no accents set. if(mas._type == MusECore::MetroAccentsStruct::FactoryPreset && !mas.isBlank()) addAccentPreset(beats, mas); } } else if(type_idx == UserPresetType) { for(int i = 0; i != map_sz; ++i) { const MusECore::MetroAccentsStruct& mas = mp.at(i); // Don't bother adding if there are no accents set. if(mas._type == MusECore::MetroAccentsStruct::UserPreset && !mas.isBlank()) addAccentPreset(beats, mas); } } } void MetronomeConfig::addAccentsPresetClicked() { const int beats = accentBeats->value(); if(beats <= 0 || accentPresetTypeList->currentIndex() != UserPresetType) return; MusECore::MetroAccentsStruct mas(MusECore::MetroAccentsStruct::User); getAccents(beats, &mas); // Don't bother adding if there are no accents set. // (This is an error because the add button should not be enabled if the accents are blank.) if(mas.isBlank()) return; MusECore::MetroAccentsPresetsMap::iterator ipm = MusEGlobal::metroAccentPresets.find(beats); if(ipm == MusEGlobal::metroAccentPresets.end()) { std::pair res = MusEGlobal::metroAccentPresets.insert( std::pair(beats, MusECore::MetroAccentsPresets())); ipm = res.first; } if(ipm->second.find(mas, MusECore::MetroAccentsStruct::AllTypes) == ipm->second.end()) { // Change the type to a user preset. mas._type = MusECore::MetroAccentsStruct::UserPreset; ipm->second.push_back(mas); addAccentPreset(beats, mas); updateAccentPresetAddButton(); updateAccentPresetDelButton(); } } void MetronomeConfig::delAccentsPresetClicked() { QListWidgetItem* item = accentPresets->currentItem(); if(!item) return; // Looking only for user presets. if(item->data(PresetTypeRole).toInt() != MusECore::MetroAccentsStruct::UserPreset) return; const int beats = item->data(BeatsRole).toInt(); MusECore::MetroAccentsPresetsMap::iterator ipm = MusEGlobal::metroAccentPresets.find(beats); if(ipm != MusEGlobal::metroAccentPresets.end()) { MusECore::MetroAccentsPresets& mp = ipm->second; const std::uint64_t id = item->data(PresetIdRole).toLongLong(); MusECore::MetroAccentsPresets::iterator imap = mp.findId(id); if(imap != mp.end()) { // Looking only for user presets. //if(imap->_type == MusECore::MetroAccentsStruct::UserPreset) { // Erase the preset. mp.erase(imap); // Erase the presets map as well if there are no more presets. if(mp.empty()) MusEGlobal::metroAccentPresets.erase(ipm); } } } delete item; updateAccentPresetAddButton(); updateAccentPresetDelButton(); } void MetronomeConfig::useAccentsPresetClicked() { QListWidgetItem* item = accentPresets->currentItem(); if(!item) return; const int beats = item->data(BeatsRole).toInt(); if(beats <= 0) return; const MusECore::MetroAccentsPresetsMap::const_iterator ipm = MusEGlobal::metroAccentPresets.find(beats); if(ipm == MusEGlobal::metroAccentPresets.cend()) return; const MusECore::MetroAccentsPresets& mp = ipm->second; const std::uint64_t id = item->data(PresetIdRole).toLongLong(); const MusECore::MetroAccentsPresets::const_iterator imap = mp.findId(id); if(imap == mp.cend()) return; MusECore::MetroAccentsStruct mas = *imap; // Change the type to user. mas._type = MusECore::MetroAccentsStruct::User; setAccentsSettings(beats, mas); } void MetronomeConfig::accentsResetDefaultClicked() { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if(!metro_settings->metroAccentsMap) return; MusECore::MetroAccentsStruct::MetroAccentsType type; if(accentPresetTypeList->currentIndex() == FactoryPresetType) type = MusECore::MetroAccentsStruct::FactoryPreset; else if(accentPresetTypeList->currentIndex() == UserPresetType) type = MusECore::MetroAccentsStruct::UserPreset; else return; QMessageBox::StandardButton b = QMessageBox::warning(this, tr("Reset accents:"), tr("Resets all accents to the defaults (first in list)\n of the current preset category (Factory or User).\nProceed?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel); if(b != QMessageBox::Ok) return; // Make a new map, to replace the original. MusECore::MetroAccentsMap* new_accents_map = new MusECore::MetroAccentsMap(); // Fill with defaults. MusEGlobal::metroAccentPresets.defaultAccents(new_accents_map, type); MusECore::PendingOperationList operations; // Takes ownership of the original which it deletes at the end of the operation. operations.add(MusECore::PendingOperationItem( &metro_settings->metroAccentsMap, new_accents_map, MusECore::PendingOperationItem::ModifyMetronomeAccentMap)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } } // namespace MusEGui muse-4.0.0/src/muse/components/metronome.h000066400000000000000000000107161404105311000205610ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: metronome.h,v 1.1.1.1.2.1 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __METRONOME_H__ #define __METRONOME_H__ #include "ui_metronomebase.h" #include "type_defs.h" #include "metronome_class.h" #include class QDialog; class QPaintEvent; class QIcon; namespace MusEGui { class MetronomePresetItemWidget : public QFrame { protected: QIcon* _onIcon; QIcon* _offIcon; bool _hasFixedIconSize; int _margin; QSize _iconSize; MusECore::MetroAccentsStruct _accents; virtual void paintEvent(QPaintEvent* ); public: MetronomePresetItemWidget( QIcon* on_icon, QIcon* off_icon, const MusECore::MetroAccentsStruct& mas, bool hasFixedIconSize = true, int margin = 4, QWidget* parent = 0, const char* name = 0); virtual QSize minimumSizeHint () const; virtual QSize sizeHint() const; bool margin() const { return _margin; } void setMargin(int v); QIcon* offIcon() const { return _offIcon; } void setOffIcon(QIcon*); QIcon* onIcon() const { return _onIcon; } void setOnIcon(QIcon*); QSize iconSize() const { return _iconSize; } void setIconSize(const QSize sz); }; //--------------------------------------------------------- // MetronomeConfig //--------------------------------------------------------- class MetronomeConfig : public QDialog, public Ui::MetronomeConfigBase { Q_OBJECT private: enum AccentPresetsDataRole { BeatsRole = Qt::UserRole, PresetIdRole, PresetTypeRole }; enum AccentPresetTypeIndex { FactoryPresetType = 0, UserPresetType = 1 }; //----------------------- // BEGIN Lambda 'slots'. //----------------------- void apply(); void audioBeepRoutesClicked(); void midiClickChanged(bool); void precountEnableChanged(bool); void precountFromMastertrackChanged(bool); void volumeChanged(int); void measVolumeChanged(int); void beatVolumeChanged(int); void accent1VolumeChanged(int); void accent2VolumeChanged(int); void switchSamples(); void changeAccents(); void clearAccents(MusECore::MetroAccent::AccentType row); void songChanged(MusECore::SongChangedStruct_t type); void accentPresetsItemActivated(QListWidgetItem*); void switchSettings(); void addAccentsPresetClicked(); void delAccentsPresetClicked(); void useAccentsPresetClicked(); void accentsResetDefaultClicked(); // FIXME These replacements don't work in the code. //void accentBeatsChanged(int); //void accentPresetsTypeItemActivated(int); //----------------------- // END Lambda 'slots'. //----------------------- void fillSoundFiles(); bool addAccentPreset(int beats, const MusECore::MetroAccentsStruct& mas); void fillAccentPresets(int beats); void configureAccentButtons(int beats); void updateAccentButtons(int beats); void updateAccentPresetAddButton(); void updateAccentPresetDelButton(); // Safely and synchronously changes the accent settings. void setAccentsSettings(int beats, const MusECore::MetroAccentsStruct& mas); // Fills the given MetroAccentsStruct from the state of the visual buttons. void getAccents(int beats, MusECore::MetroAccentsStruct* mas) const; private slots: void accept(); void reject(); void accentBeatsChanged(int); // See above void accentPresetsTypeItemActivated(int); // See above public: MetronomeConfig(QWidget* parent=0); void updateValues(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/metronomebase.ui000066400000000000000000001146731404105311000216110ustar00rootroot00000000000000 MetronomeConfigBase 0 0 469 407 MusE: Metronome Config true 0 Preferences 12 QFrame::StyledPanel QFrame::Raised 75 true Apply the settings globally or to the current song: false Use global metronome true Use song-specific metronome QFrame::StyledPanel QFrame::Raised 75 true Enable accents for the audio and midi metronomes: false &Two samples (old samples) true Four samples (&new samples, with accents) Hint: Enable metronome in the toolbar or in the Transport panel. false Qt::Vertical 20 41 Audio 12 Audio Beep Choose outputs... true Qt::Horizontal 40 20 9 50 false 50 false 0 100 1 10 50 Qt::Horizontal 50 false 50 false Audio master false 0 100 1 10 50 Qt::Horizontal 0 100 1 10 50 Qt::Horizontal 0 100 1 10 50 Qt::Horizontal 50 false Sample Qt::Vertical QSizePolicy::Fixed 20 5 Meas false Accent1 false Accent2 false 0 100 1 10 50 Qt::Horizontal Beat false Volume Qt::Vertical 20 14 MIDI 12 MIDI Click true 9 12 127 Midi Port false 1 200 127 Beat Velocity false 127 Measure Velocity false 127 1 16 Midi Channel false Accent 2 Velocity false 127 127 Beat Note false Qt::Horizontal 40 20 Accent 1 Velocity false Accent 1 Note false Measure Note false Accent 2 Note false Off 127 Off 127 Qt::Horizontal 40 20 Qt::Vertical 20 51 Accents 1 128 4 Accent sequence beats Qt::Horizontal 40 20 Current accent 1 sequence: (Click to change) 2 10 Qt::Horizontal 40 20 Current accent 2 sequence: 2 10 Qt::Horizontal 40 20 Qt::Horizontal 0 Factory User Preset sequences Qt::Horizontal 40 20 Add current accents to presets Make a new preset from the current accents. + false Delete preset Delete the selected preset. Only user presets can be deleted. - Use preset Copy the preset to the current accents. -> Reset to Factory or User defaults Resets all current accent sequences to default Factory or User presets. ! 2 true QAbstractItemView::ScrollPerPixel Precount 12 true Enable the precount Enable false 6 0 0 0 0 true Number of bars 1 10 2 true Bars false Qt::Horizontal 40 20 true Use Mastertrack time signature From Mastertrack 6 0 0 0 0 true Beats per bar 1 32 4 true / false true Beat note type 2 64 4 true Signature false Qt::Horizontal 40 20 true Enable precount when playing Precount on play true Mute the metronome after precount Mute metronome Qt::Vertical 20 18 Qt::Horizontal QSizePolicy::Expanding 13 20 &Apply Alt+A true &OK Alt+O true true &Cancel Alt+C true audioBeep audioBeepRoutesButton midiClick measureNote measureVelocity beatNote beatVelocity midiChannel midiPort precountEnable precountBars precountFromMastertrack precountSigZ precountSigN buttonApply buttonOk buttonCancel buttonOk clicked() MetronomeConfigBase accept() 20 20 20 20 buttonCancel clicked() MetronomeConfigBase reject() 20 20 20 20 muse-4.0.0/src/muse/components/midi_audio_control.cpp000066400000000000000000000316331404105311000227530ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // midi_audio_control.cpp // Copyright (C) 2012 by Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "midi_audio_control.h" #include "globals.h" #include "globaldefs.h" #include "mididev.h" #include "midiport.h" #include "midictrl.h" #include "audio.h" #include "app.h" #include namespace MusEGui { // ----------------------------------- // MidiAudioControl // Set port to -1 to automatically set it to the port of // the first combo box item (the first readable port). // ----------------------------------- MidiAudioControl::MidiAudioControl(int port, int chan, int ctrl, QWidget* parent) : QDialog(parent) { setupUi(this); controlTypeComboBox->addItem(tr("Control7"), MusECore::MidiController::Controller7); controlTypeComboBox->addItem(tr("Control14"), MusECore::MidiController::Controller14); controlTypeComboBox->addItem(tr("RPN"), MusECore::MidiController::RPN); controlTypeComboBox->addItem(tr("NPRN"), MusECore::MidiController::NRPN); controlTypeComboBox->addItem(tr("RPN14"), MusECore::MidiController::RPN14); controlTypeComboBox->addItem(tr("NRPN14"), MusECore::MidiController::NRPN14); controlTypeComboBox->addItem(tr("Pitch"), MusECore::MidiController::Pitch); controlTypeComboBox->addItem(tr("Program"), MusECore::MidiController::Program); //controlTypeComboBox->addItem(tr("PolyAftertouch"), MusECore::MidiController::PolyAftertouch); // Not supported yet. Need a way to select pitch. controlTypeComboBox->addItem(tr("Aftertouch"), MusECore::MidiController::Aftertouch); controlTypeComboBox->setCurrentIndex(0); _port = port; _chan = chan; _ctrl = ctrl; _is_learning = false; update(); connect(learnPushButton, SIGNAL(clicked(bool)), SLOT(learnChanged(bool))); connect(portComboBox, SIGNAL(currentIndexChanged(int)), SLOT(portChanged(int))); connect(channelSpinBox, SIGNAL(valueChanged(int)), SLOT(chanChanged())); connect(controlTypeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(ctrlTypeChanged(int))); connect(ctrlHiSpinBox, SIGNAL(valueChanged(int)), SLOT(ctrlHChanged())); connect(ctrlLoSpinBox, SIGNAL(valueChanged(int)), SLOT(ctrlLChanged())); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat())); } void MidiAudioControl::heartbeat() { if(_is_learning) { if(MusEGlobal::midiLearnPort != -1) { int port_item = portComboBox->findData(MusEGlobal::midiLearnPort); if(port_item != -1 && port_item != portComboBox->currentIndex()) { _port = MusEGlobal::midiLearnPort; portComboBox->blockSignals(true); portComboBox->setCurrentIndex(port_item); portComboBox->blockSignals(false); } } if(MusEGlobal::midiLearnChan != -1 && (MusEGlobal::midiLearnChan + 1) != channelSpinBox->value()) { _chan = MusEGlobal::midiLearnChan; channelSpinBox->blockSignals(true); channelSpinBox->setValue(_chan + 1); channelSpinBox->blockSignals(false); } if(MusEGlobal::midiLearnCtrl != -1) { MusECore::MidiController::ControllerType type = MusECore::midiControllerType(MusEGlobal::midiLearnCtrl); int idx = controlTypeComboBox->findData(type); if(idx != -1 && idx != controlTypeComboBox->currentIndex()) { controlTypeComboBox->blockSignals(true); controlTypeComboBox->setCurrentIndex(idx); controlTypeComboBox->blockSignals(false); } int hv = (MusEGlobal::midiLearnCtrl >> 8) & 0xff; int lv = MusEGlobal::midiLearnCtrl & 0xff; switch(type) { case MusECore::MidiController::Program: case MusECore::MidiController::Pitch: case MusECore::MidiController::PolyAftertouch: // Unsupported yet. Need a way to select pitch. case MusECore::MidiController::Aftertouch: ctrlHiSpinBox->setEnabled(false); ctrlLoSpinBox->setEnabled(false); ctrlHiSpinBox->blockSignals(true); ctrlLoSpinBox->blockSignals(true); ctrlHiSpinBox->setValue(0); ctrlLoSpinBox->setValue(0); ctrlHiSpinBox->blockSignals(false); ctrlLoSpinBox->blockSignals(false); break; case MusECore::MidiController::Controller7: ctrlHiSpinBox->setEnabled(false); ctrlLoSpinBox->setEnabled(true); ctrlHiSpinBox->blockSignals(true); ctrlHiSpinBox->setValue(0); ctrlHiSpinBox->blockSignals(false); if(lv != ctrlLoSpinBox->value()) { ctrlLoSpinBox->blockSignals(true); ctrlLoSpinBox->setValue(lv); ctrlLoSpinBox->blockSignals(false); } break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN: case MusECore::MidiController::NRPN14: ctrlHiSpinBox->setEnabled(true); ctrlLoSpinBox->setEnabled(true); if(hv != ctrlHiSpinBox->value()) { ctrlHiSpinBox->blockSignals(true); ctrlHiSpinBox->setValue(hv); ctrlHiSpinBox->blockSignals(false); } if(lv != ctrlLoSpinBox->value()) { ctrlLoSpinBox->blockSignals(true); ctrlLoSpinBox->setValue(lv); ctrlLoSpinBox->blockSignals(false); } break; default: printf("FIXME: MidiAudioControl::heartbeat: Unknown control type: %d\n", type); break; } _ctrl = MusECore::midiCtrlTerms2Number(type, (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value()); } } } void MidiAudioControl::learnChanged(bool v) { _is_learning = v; if(_is_learning) MusEGlobal::audio->msgStartMidiLearn(); // Resets the learn values to -1. } void MidiAudioControl::resetLearn() { _is_learning = false; learnPushButton->blockSignals(true); learnPushButton->setChecked(false); learnPushButton->blockSignals(false); MusEGlobal::audio->msgStartMidiLearn(); // Resets the learn values to -1. } void MidiAudioControl::portChanged(int idx) { if(idx == -1) return; int port_num = portComboBox->itemData(idx).toInt(); if(port_num < 0 || port_num >= MusECore::MIDI_PORTS) return; _port = port_num; resetLearn(); } void MidiAudioControl::chanChanged() { _chan = channelSpinBox->value() - 1; resetLearn(); } void MidiAudioControl::updateCtrlBoxes() { if(controlTypeComboBox->currentIndex() == -1) return; MusECore::MidiController::ControllerType t = (MusECore::MidiController::ControllerType)controlTypeComboBox->itemData(controlTypeComboBox->currentIndex()).toInt(); switch(t) { case MusECore::MidiController::Program: case MusECore::MidiController::Pitch: case MusECore::MidiController::PolyAftertouch: // Unsupported yet. Need a way to select pitch. case MusECore::MidiController::Aftertouch: ctrlHiSpinBox->setEnabled(false); ctrlLoSpinBox->setEnabled(false); ctrlHiSpinBox->blockSignals(true); ctrlLoSpinBox->blockSignals(true); ctrlHiSpinBox->setValue(0); ctrlLoSpinBox->setValue(0); ctrlHiSpinBox->blockSignals(false); ctrlLoSpinBox->blockSignals(false); break; case MusECore::MidiController::Controller7: ctrlHiSpinBox->setEnabled(false); ctrlLoSpinBox->setEnabled(true); ctrlHiSpinBox->blockSignals(true); ctrlHiSpinBox->setValue(0); ctrlHiSpinBox->blockSignals(false); break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN: case MusECore::MidiController::NRPN14: ctrlHiSpinBox->setEnabled(true); ctrlLoSpinBox->setEnabled(true); break; default: printf("FIXME: MidiAudioControl::updateCtrlBoxes: Unknown control type: %d\n", t); break; } } void MidiAudioControl::ctrlTypeChanged(int idx) { if(idx == -1) return; updateCtrlBoxes(); _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); _ctrl = MusECore::midiCtrlTerms2Number((MusECore::MidiController::ControllerType)controlTypeComboBox->itemData(idx).toInt(), _ctrl); resetLearn(); } void MidiAudioControl::ctrlHChanged() { if(controlTypeComboBox->currentIndex() == -1) return; _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); _ctrl = MusECore::midiCtrlTerms2Number((MusECore::MidiController::ControllerType)controlTypeComboBox->itemData(controlTypeComboBox->currentIndex()).toInt(), _ctrl); resetLearn(); } void MidiAudioControl::ctrlLChanged() { if(controlTypeComboBox->currentIndex() == -1) return; _ctrl = (ctrlHiSpinBox->value() << 8) + ctrlLoSpinBox->value(); _ctrl = MusECore::midiCtrlTerms2Number((MusECore::MidiController::ControllerType)controlTypeComboBox->itemData(controlTypeComboBox->currentIndex()).toInt(), _ctrl); resetLearn(); } void MidiAudioControl::configChanged() { update(); } void MidiAudioControl::update() { portComboBox->blockSignals(true); portComboBox->clear(); int item_idx = 0; for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { MusECore::MidiDevice* md = MusEGlobal::midiPorts[i].device(); if(!md) // In the case of this combo box, don't bother listing empty ports. continue; //if(!(md->rwFlags() & 1 || md->isSynti()) && (i != outPort)) if(!(md->rwFlags() & 2) && (i != _port)) // Only readable ports, or current one. continue; QString name = QString("%1:%2").arg(i + 1).arg(MusEGlobal::midiPorts[i].portname()); portComboBox->insertItem(item_idx, name, i); if(_port == -1) _port = i; // Initialize if(i == _port) portComboBox->setCurrentIndex(item_idx); item_idx++; } portComboBox->blockSignals(false); channelSpinBox->blockSignals(true); channelSpinBox->setValue(_chan + 1); channelSpinBox->blockSignals(false); int type = MusECore::midiControllerType(_ctrl); int idx = controlTypeComboBox->findData(type); if(idx != -1 && idx != controlTypeComboBox->currentIndex()) { controlTypeComboBox->blockSignals(true); controlTypeComboBox->setCurrentIndex(idx); controlTypeComboBox->blockSignals(false); } int hv = (_ctrl >> 8) & 0xff; int lv = _ctrl & 0xff; switch(type) { case MusECore::MidiController::Program: case MusECore::MidiController::Pitch: case MusECore::MidiController::PolyAftertouch: // Unsupported yet. Need a way to select pitch. case MusECore::MidiController::Aftertouch: ctrlHiSpinBox->setEnabled(false); ctrlLoSpinBox->setEnabled(false); ctrlHiSpinBox->blockSignals(true); ctrlLoSpinBox->blockSignals(true); ctrlHiSpinBox->setValue(0); ctrlLoSpinBox->setValue(0); ctrlHiSpinBox->blockSignals(false); ctrlLoSpinBox->blockSignals(false); break; case MusECore::MidiController::Controller7: ctrlHiSpinBox->setEnabled(false); ctrlLoSpinBox->setEnabled(true); ctrlHiSpinBox->blockSignals(true); ctrlHiSpinBox->setValue(0); ctrlHiSpinBox->blockSignals(false); if(lv != ctrlLoSpinBox->value()) { ctrlLoSpinBox->blockSignals(true); ctrlLoSpinBox->setValue(lv); ctrlLoSpinBox->blockSignals(false); } break; case MusECore::MidiController::Controller14: case MusECore::MidiController::RPN: case MusECore::MidiController::RPN14: case MusECore::MidiController::NRPN: case MusECore::MidiController::NRPN14: ctrlHiSpinBox->setEnabled(true); ctrlLoSpinBox->setEnabled(true); if(hv != ctrlHiSpinBox->value()) { ctrlHiSpinBox->blockSignals(true); ctrlHiSpinBox->setValue(hv); ctrlHiSpinBox->blockSignals(false); } if(lv != ctrlLoSpinBox->value()) { ctrlLoSpinBox->blockSignals(true); ctrlLoSpinBox->setValue(lv); ctrlLoSpinBox->blockSignals(false); } break; default: printf("FIXME: MidiAudioControl::updateCtrlBoxes: Unknown control type: %d\n", type); break; } } } // namespace MusEGui muse-4.0.0/src/muse/components/midi_audio_control.h000066400000000000000000000034141404105311000224140ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // midi_audio_control.h // Copyright (C) 2012 by Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef MIDI_AUDIO_CONTROL_H #define MIDI_AUDIO_CONTROL_H #include "ui_midi_audio_control_base.h" namespace MusEGui { class MidiAudioControl : public QDialog, public Ui::MidiAudioControlBase { Q_OBJECT private: int _port, _chan, _ctrl; bool _is_learning; void update(); void resetLearn(); void updateCtrlBoxes(); private slots: void heartbeat(); void learnChanged(bool); void portChanged(int); void chanChanged(); void ctrlTypeChanged(int); void ctrlHChanged(); void ctrlLChanged(); void configChanged(); public: MidiAudioControl(int port = -1, int chan = 0, int ctrl = 0, QWidget* parent = 0); int port() const { return _port; } int chan() const { return _chan; } int ctrl() const { return _ctrl; } }; } #endif // MIDI_AUDIO_CONTROL_H muse-4.0.0/src/muse/components/midi_audio_control_base.ui000066400000000000000000000157371404105311000236070ustar00rootroot00000000000000 MidiAudioControlBase 0 0 367 156 Midi control 0 0 Port: 0 0 Channel: 0 0 1 16 Control type: 0 0 0 0 0 0 Hi: 0 0 0 255 0 0 0 Lo: 0 0 0 255 0 Qt::Horizontal 40 20 0 0 &Learn true Qt::Horizontal 40 20 Qt::Vertical 20 18 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() MidiAudioControlBase accept() 248 254 157 274 buttonBox rejected() MidiAudioControlBase reject() 316 260 286 274 muse-4.0.0/src/muse/components/midi_warn_init_pending.ui000066400000000000000000000061331404105311000234400ustar00rootroot00000000000000 midiWarnInitPendingBase 0 0 371 207 0 0 Instrument initialization 0 0 MusE should now send some Instrument Initialization Sequences. The sequences (usually System Exclusive messages) are defined by the selected instruments in the Settings -> Midi Ports dialog, such as the GM (default), GS, or XG instruments. Typically you should answer yes here. You can always do it manually from the Midi menu. Continue? Qt::AutoText false 0 0 Don't ask me again 0 0 Qt::Horizontal QDialogButtonBox::No|QDialogButtonBox::Yes false buttonBox accepted() midiWarnInitPendingBase accept() 248 254 157 274 buttonBox rejected() midiWarnInitPendingBase reject() 316 260 286 274 muse-4.0.0/src/muse/components/midi_warn_init_pending_impl.cpp000066400000000000000000000022201404105311000246170ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // midi_warn_init_pending_impl.cpp // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "midi_warn_init_pending_impl.h" namespace MusEGui { MidiWarnInitPendingDialog::MidiWarnInitPendingDialog() { setupUi(this); } } // namespace MusEGui muse-4.0.0/src/muse/components/midi_warn_init_pending_impl.h000066400000000000000000000025531404105311000242750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // midi_warn_init_pending_impl.h // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDI_WARN_INIT_PENDING_IMPL_H__ #define __MIDI_WARN_INIT_PENDING_IMPL_H__ #include "ui_midi_warn_init_pending.h" namespace MusEGui { class MidiWarnInitPendingDialog : public QDialog, public Ui::midiWarnInitPendingBase { Q_OBJECT public: MidiWarnInitPendingDialog(); bool dontAsk() const { return dontAskAgain->isChecked(); } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/midisync.ui000066400000000000000000000334321404105311000205610ustar00rootroot00000000000000 werner schweer midi sync configuration dialog MidiSyncConfigBase 0 0 655 534 MusE: Midi Sync 6 0 0 0 0 Qt::Horizontal QSizePolicy::Expanding 20 20 Apply Ok true Cancel true MTC 6 6 2 6 2 Type: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 0 0 24 25 30D 30N Offset: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false false hour 23 h false false minute 59 m false false second 59 s false false frame 30 f false false subframe Sync receiving and sending This button and other transport related buttons are found in the toolbar or on the Transport Panel. Send start to first clock delay Allows 'slow sync' devices time to synchronize to MusE. This value is the delay from sending start to sending the first clock. ms 0 60000 1 1 Send sync delay Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false 1 true Control MusE timing by external midi clock or MTC sync When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). Slave to external sync false bpm 0.000000000000000 100.000000000000000 0.010000000000000 1.000000000000000 Tempo record quantization Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false Averaging applied to recorded external tempo changes. External midi clock can be very jittery. Tempo is derived from it and recorded. It is usually desirable to average it and limit the number of recorded changes. Tiny: 2 section 4/4 = 8 stages. 1/8T note averaging, may produce jitter. Small: 3 section 12/8/4 = 24 stages. 1/4 note averaging, may still produce jitter. Medium: 3 section 28/12/8 = 48 stages. 1/2 note averaging. Less jitter. Large: 4 section 48/48/48/48 = 192 stages. Use this if the song has only one tempo. Very low quantization values can be used. Large pre-detect: 4 section 8/48/48/48 = 152 stages + first stage large step pre-detector. Use this if you expect sudden large tempo steps. None: Use only if high accuracy is needed for audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. Tempo record averaging Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false Note: Sync delay and MTC sync currently not fully implemented Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter false mtcOffH mtcOffM mtcOffS mtcOffF mtcOffSf applyButton okButton cancelButton muse-4.0.0/src/muse/components/midisyncimpl.cpp000066400000000000000000001162171404105311000216130ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midisyncimpl.cpp,v 1.1.1.1.2.4 2009/05/03 04:14:01 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include "app.h" #include "song.h" #include "midiport.h" #include "midiseq.h" #include "mididev.h" #include "icons.h" #include "globals.h" #include "gconfig.h" #include "midisyncimpl.h" #include "driver/audiodev.h" #include "audio.h" #include "al/al.h" #include "operations.h" namespace MusEGui { enum { DEVCOL_NO = 0, DEVCOL_NAME, DEVCOL_SYNC_TO, DEVCOL_IN, DEVCOL_TICKIN, DEVCOL_MRTIN, DEVCOL_MMCIN, DEVCOL_MTCIN, DEVCOL_MTCTYPE, DEVCOL_RID, DEVCOL_RCLK, DEVCOL_RMRT, DEVCOL_RMMC, DEVCOL_RMTC, DEVCOL_RREWSTART, DEVCOL_TID, DEVCOL_TCLK, DEVCOL_TMRT, DEVCOL_TMMC, DEVCOL_TMTC, /* DEVCOL_TREWSTART, */ }; //MusECore::MidiSyncInfo tmpMidiSyncPorts[MIDI_PORTS]; //--------------------------------------------------------- // MidiSyncConfig::setToolTips //--------------------------------------------------------- void MidiSyncConfig::setToolTips(QTreeWidgetItem *item) { item->setToolTip(DEVCOL_NO, tr("Port Number")); item->setToolTip(DEVCOL_NAME, tr("Name of the midi device associated with" " this port number")); item->setToolTip(DEVCOL_SYNC_TO, tr("Sync to this device. Click to select.")); item->setToolTip(DEVCOL_IN, tr("Midi clock input detected")); item->setToolTip(DEVCOL_TICKIN, tr("Midi tick input detected")); item->setToolTip(DEVCOL_MRTIN, tr("Midi real time input detected")); item->setToolTip(DEVCOL_MMCIN, tr("MMC input detected")); item->setToolTip(DEVCOL_MTCIN, tr("MTC input detected")); item->setToolTip(DEVCOL_MTCTYPE, tr("Detected SMPTE format")); item->setToolTip(DEVCOL_RID, tr("Receive id number. 127 = Global. Double click to edit.")); item->setToolTip(DEVCOL_RCLK, tr("Accept midi clock input")); item->setToolTip(DEVCOL_RMRT, tr("Accept midi real time input")); item->setToolTip(DEVCOL_RMMC, tr("Accept MMC input")); item->setToolTip(DEVCOL_RMTC, tr("Accept MTC input")); item->setToolTip(DEVCOL_RREWSTART, tr("Receive start rewinds before playing")); item->setToolTip(DEVCOL_TID, tr("Transmit id number. 127 = Global. Double click to edit.")); item->setToolTip(DEVCOL_TCLK, tr("Send midi clock output")); item->setToolTip(DEVCOL_TMRT, tr("Send midi realtime output")); item->setToolTip(DEVCOL_TMMC, tr("Send MMC output")); item->setToolTip(DEVCOL_TMTC, tr("Send MTC output")); //item->setToolTip(DEVCOL_TREWSTART, tr("Send continue instead of start")); } //--------------------------------------------------------- // MidiSyncConfig::setWhatsThis //--------------------------------------------------------- void MidiSyncConfig::setWhatsThis(QTreeWidgetItem *item) { item->setWhatsThis(DEVCOL_NO, tr("Port Number")); item->setWhatsThis(DEVCOL_NAME, tr("Name of the midi device associated with this port number")); item->setWhatsThis(DEVCOL_SYNC_TO, tr("Sync to this device. Click to select.\n" "MusE will synchronize to this device's\n clock or MTC quarter-frame,\n if 'slave to external sync' is on.")); item->setWhatsThis(DEVCOL_IN, tr("Midi clock input detected")); item->setWhatsThis(DEVCOL_TICKIN, tr("Midi tick input detected")); item->setWhatsThis(DEVCOL_MRTIN, tr("Midi realtime input detected, including\n start/stop/continue, and song position.")); item->setWhatsThis(DEVCOL_MMCIN, tr("MMC input detected, including stop/play/deferred play, and locate.")); //"Current port actually used is red. Click to force a port to be current.")); item->setWhatsThis(DEVCOL_MTCIN, tr("MTC input detected, including forward quarter-frame sync and full-frame locate")); item->setWhatsThis(DEVCOL_MTCTYPE, tr("Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop\n" "Detects format of MTC quarter and full frame, and MMC locate.")); item->setWhatsThis(DEVCOL_RID, tr("Receive id number. 127 = global receive all, even if not global.")); item->setWhatsThis(DEVCOL_RCLK, tr("Accept midi clock input.\nOnly one port can be used for clock sync.")); item->setWhatsThis(DEVCOL_RMRT, tr("Accept midi realtime input, including\n start/stop/continue, and song position.\n" "Non-clock events (start,stop etc) are\n accepted by ALL enabled ports.\n" "This means you may have several master\n devices connected, and muse will accept\n" " input from them.")); item->setWhatsThis(DEVCOL_RMMC, tr("Accept MMC input, including stop/play/deferred play, and locate.")); item->setWhatsThis(DEVCOL_RMTC, tr("Accept MTC input, including forward quarter-frame sync and full-frame locate.\n" "Only one port can be used for sync.")); item->setWhatsThis(DEVCOL_RREWSTART, tr("When start is received, rewind before playing.\n" "Note: In some cases, such as having many\n project audio tracks, it may be impossible\n to rewind fast" " enough to synchronize\n with the external device.\nManually rewinding the device before\n playing is recommended.")); item->setWhatsThis(DEVCOL_TID, tr("Transmit id number. 127 = global transmit to all.")); item->setWhatsThis(DEVCOL_TCLK, tr("Send midi clock output. If 'Slave to External Sync' is chosen,\n" " muse can re-transmit clock to any other chosen ports.")); item->setWhatsThis(DEVCOL_TMRT, tr("Send midi realtime output, including start/stop/continue,\n" " and song position. If 'Slave to external sync' is chosen,\n" " muse can re-transmit midi realtime input messages to any\n" " other chosen ports. This means you may have several slave\n" " devices connected, and muse can re-send realtime messages\n" " to any or all of them.")); item->setWhatsThis(DEVCOL_TMMC, tr("Send MMC output")); item->setWhatsThis(DEVCOL_TMTC, tr("Send MTC output")); // item->setWhatsThis(DEVCOL_TREWSTART, tr("When transport is starting, send continue instead of start.\n")); } //--------------------------------------------------------- // MidiSyncConfig::addDevice //--------------------------------------------------------- void MidiSyncConfig::addDevice(QTreeWidgetItem *item, QTreeWidget *tree) { setWhatsThis(item); tree->addTopLevelItem(item); } void MidiSyncLViewItem::setPort(int port) { _port = port; if(_port < 0 || port >= MusECore::MIDI_PORTS) return; copyFromSyncInfo(MusEGlobal::midiPorts[port].syncInfo()); } //--------------------------------------------------------- // MidiSyncLViewItem // copyFromSyncInfo //--------------------------------------------------------- void MidiSyncLViewItem::copyFromSyncInfo(const MusECore::MidiSyncInfo &sp) { _idOut = sp.idOut(); _idIn = sp.idIn(); _sendMC = sp.MCOut(); _sendMRT = sp.MRTOut(); _sendMMC = sp.MMCOut(); _sendMTC = sp.MTCOut(); _recMC = sp.MCIn(); _recMRT = sp.MRTIn(); _recMMC = sp.MMCIn(); _recMTC = sp.MTCIn(); _recRewOnStart = sp.recRewOnStart(); //_sendContNotStart = sp.sendContNotStart(); } //--------------------------------------------------------- // MidiSyncLViewItem // copyToSyncInfo //--------------------------------------------------------- void MidiSyncLViewItem::copyToSyncInfo(MusECore::MidiSyncInfo &sp) { sp.setIdOut(_idOut); sp.setIdIn(_idIn); sp.setMCOut(_sendMC); sp.setMRTOut(_sendMRT); sp.setMMCOut(_sendMMC); sp.setMTCOut(_sendMTC); sp.setMCIn(_recMC); sp.setMRTIn(_recMRT); sp.setMMCIn(_recMMC); sp.setMTCIn(_recMTC); sp.setRecRewOnStart(_recRewOnStart); //sp.setSendContNotStart(_sendContNotStart); } //--------------------------------------------------------- // MidiSyncConfig // Midi Sync Config //--------------------------------------------------------- MidiSyncConfig::MidiSyncConfig(QWidget* parent) : QDialog(parent) { setupUi(this); // Remember the current value, for cancelling. _curMidiSyncInPort = MusEGlobal::config.curMidiSyncInPort; _dirty = false; applyButton->setEnabled(false); okButton->setEnabled(false); devicesListView->setAllColumnsShowFocus(true); QStringList columnnames; columnnames << tr("Port") << tr("Device Name") << "s" << "c" << "k" << "r" << "m" << "t" << tr("Type") << "rid" // Receive << "rc" // Receive << "rr" // Receive << "rm" // Receive << "rt" // Receive << "rw" // Receive << "tid" // Transmit << "tc" // Transmit << "tr" // Transmit << "tm" // Transmit << "tt"; // Transmit devicesListView->setColumnCount(columnnames.size()); devicesListView->setHeaderLabels(columnnames); setWhatsThis(devicesListView->headerItem()); setToolTips(devicesListView->headerItem()); devicesListView->setFocusPolicy(Qt::NoFocus); syncRecFilterPreset->addItem(tr("None"), MusECore::MidiSyncInfo::NONE); syncRecFilterPreset->addItem(tr("Tiny"), MusECore::MidiSyncInfo::TINY); syncRecFilterPreset->addItem(tr("Small"), MusECore::MidiSyncInfo::SMALL); syncRecFilterPreset->addItem(tr("Large"), MusECore::MidiSyncInfo::LARGE); syncRecFilterPreset->addItem(tr("Large with pre-detect"), MusECore::MidiSyncInfo::LARGE_WITH_PRE_DETECT); songChanged(-1); //connect(devicesListView, SIGNAL(pressed(QListViewItem*,const QPoint&,int)), // this, SLOT(dlvClicked(QListViewItem*,const QPoint&,int))); connect(devicesListView, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(dlvClicked(QTreeWidgetItem*, int))); connect(devicesListView, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(dlvDoubleClicked(QTreeWidgetItem*, int))); //connect(devicesListView, SIGNAL(itemRenamed(QListViewItem*, int, const QString&)), // this, SLOT(renameOk(QListViewItem*, int, const QString&))); connect(okButton, SIGNAL(clicked()), SLOT(ok())); connect(applyButton, SIGNAL(clicked()), SLOT(apply())); connect(cancelButton, SIGNAL(clicked()), SLOT(cancel())); //connect(syncMode, SIGNAL(clicked(int)), SLOT(syncChanged(int))); connect(extSyncCheckbox, SIGNAL(clicked()), SLOT(syncChanged())); connect(mtcSyncType, SIGNAL(activated(int)), SLOT(syncChanged())); connect(syncRecFilterPreset, SIGNAL(currentIndexChanged(int)), SLOT(syncChanged())); connect(syncRecTempoValQuant, SIGNAL(valueChanged(double)), SLOT(syncChanged())); connect(syncDelaySpinBox, SIGNAL(valueChanged(int)), SLOT(syncChanged())); connect(extSyncCheckbox, SIGNAL(toggled(bool)), SLOT(extSyncClicked(bool))); // Done in show(). //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); //connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); } MidiSyncConfig::~MidiSyncConfig() { } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MidiSyncConfig::songChanged(MusECore::SongChangedStruct_t flags) { // Is it simply a midi controller value adjustment? Forget it. Otherwise, it's mainly midi port/device changes we want. if(!(flags & (SC_CONFIG | SC_MASTER | SC_TEMPO | SC_SIG | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED | SC_MIDI_CONTROLLER_ADD | SC_EXTERNAL_MIDI_SYNC | SC_USE_JACK_TRANSPORT))) return; // Reset dirty flag, since we're loading new values. _dirty = false; if(applyButton->isEnabled()) applyButton->setEnabled(false); if(okButton->isEnabled()) okButton->setEnabled(false); //for(int i = 0; i < MIDI_PORTS; ++i) // tmpMidiSyncPorts[i] = midiSyncPorts[i]; extSyncCheckbox->blockSignals(true); syncDelaySpinBox->blockSignals(true); extSyncCheckbox->setChecked(MusEGlobal::extSyncFlag); syncDelaySpinBox->setValue(MusEGlobal::syncSendFirstClockDelay); syncDelaySpinBox->blockSignals(false); extSyncCheckbox->blockSignals(false); int fp_idx = syncRecFilterPreset->findData(MusEGlobal::syncRecFilterPreset); if(fp_idx != -1) { syncRecFilterPreset->blockSignals(true); syncRecFilterPreset->setCurrentIndex(fp_idx); syncRecFilterPreset->blockSignals(false); } syncRecTempoValQuant->blockSignals(true); syncRecTempoValQuant->setValue(MusEGlobal::syncRecTempoValQuant); syncRecTempoValQuant->blockSignals(false); mtcSyncType->setCurrentIndex(MusEGlobal::mtcType); mtcOffH->blockSignals(true); mtcOffM->blockSignals(true); mtcOffS->blockSignals(true); mtcOffF->blockSignals(true); mtcOffSf->blockSignals(true); mtcOffH->setValue(MusEGlobal::mtcOffset.h()); mtcOffM->setValue(MusEGlobal::mtcOffset.m()); mtcOffS->setValue(MusEGlobal::mtcOffset.s()); mtcOffF->setValue(MusEGlobal::mtcOffset.f()); mtcOffSf->setValue(MusEGlobal::mtcOffset.sf()); mtcOffH->blockSignals(false); mtcOffM->blockSignals(false); mtcOffS->blockSignals(false); mtcOffF->blockSignals(false); mtcOffSf->blockSignals(false); updateSyncInfoLV(); //selectionChanged(); } //--------------------------------------------------------- // heartBeat //--------------------------------------------------------- void MidiSyncConfig::heartBeat() { //inHeartBeat = true; for (int i = MusECore::MIDI_PORTS-1; i >= 0; --i) { MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)devicesListView->topLevelItem(i); int port = lvi->port(); if(port >= 0 && port < MusECore::MIDI_PORTS) { bool sdet = MusEGlobal::midiPorts[port].syncInfo().MCSyncDetect(); if(sdet) { if(!lvi->_inDet) { lvi->_inDet = true; lvi->_curDet = false; lvi->setIcon(DEVCOL_IN, *ledGreenSVGIcon); } } else { if(lvi->_curDet || lvi->_inDet) { lvi->_curDet = false; lvi->_inDet = false; lvi->setIcon(DEVCOL_IN, *ledOffSVGIcon); } } if(port == MusEGlobal::config.curMidiSyncInPort) lvi->setIcon(DEVCOL_SYNC_TO, *ledRedSVGIcon); else lvi->setIcon(DEVCOL_SYNC_TO, *ledOffSVGIcon); sdet = MusEGlobal::midiPorts[port].syncInfo().tickDetect(); if(sdet) { if(!lvi->_tickDet) { lvi->_tickDet = true; lvi->setIcon(DEVCOL_TICKIN, *ledGreenSVGIcon); } } else { if(lvi->_tickDet) { lvi->_tickDet = false; lvi->setIcon(DEVCOL_TICKIN, *ledOffSVGIcon); } } sdet = MusEGlobal::midiPorts[port].syncInfo().MRTDetect(); if(sdet) { if(!lvi->_MRTDet) { lvi->_MRTDet = true; lvi->setIcon(DEVCOL_MRTIN, *ledGreenSVGIcon); } } else { if(lvi->_MRTDet) { lvi->_MRTDet = false; lvi->setIcon(DEVCOL_MRTIN, *ledOffSVGIcon); } } int type = MusEGlobal::midiPorts[port].syncInfo().recMTCtype(); sdet = MusEGlobal::midiPorts[port].syncInfo().MMCDetect(); bool mtcdet = MusEGlobal::midiPorts[port].syncInfo().MTCDetect(); if(sdet) { if(!lvi->_MMCDet) { lvi->_MMCDet = true; lvi->setIcon(DEVCOL_MMCIN, *ledGreenSVGIcon); } // MMC locate command can contain SMPTE format type. Update now. if(!mtcdet && lvi->_recMTCtype != type) { lvi->_recMTCtype = type; switch(type) { case 0: lvi->setText(DEVCOL_MTCTYPE, "24"); break; case 1: lvi->setText(DEVCOL_MTCTYPE, "25"); break; case 2: lvi->setText(DEVCOL_MTCTYPE, "30D"); break; case 3: lvi->setText(DEVCOL_MTCTYPE, "30N"); break; default: lvi->setText(DEVCOL_MTCTYPE, "??"); break; } } } else { if(lvi->_MMCDet) { lvi->_MMCDet = false; lvi->setIcon(DEVCOL_MMCIN, *ledOffSVGIcon); } } if(mtcdet) { if(!lvi->_MTCDet) { lvi->_MTCDet = true; lvi->_curMTCDet = false; lvi->setIcon(DEVCOL_MTCIN, *ledGreenSVGIcon); } if(lvi->_recMTCtype != type) { lvi->_recMTCtype = type; switch(type) { case 0: lvi->setText(DEVCOL_MTCTYPE, "24"); break; case 1: lvi->setText(DEVCOL_MTCTYPE, "25"); break; case 2: lvi->setText(DEVCOL_MTCTYPE, "30D"); break; case 3: lvi->setText(DEVCOL_MTCTYPE, "30N"); break; default: lvi->setText(DEVCOL_MTCTYPE, "??"); break; } } } else { if(lvi->_curMTCDet || lvi->_MTCDet) { lvi->_MTCDet = false; lvi->_curMTCDet = false; lvi->setIcon(DEVCOL_MTCIN, *ledOffSVGIcon); } } } //MusECore::MidiDevice* dev = lvi->device(); //bool sdet = dev->syncInfo().MCSyncDetect(); //if(lvi->pixmap(DEVCOL_IN) != (sdet ? *ledGreenSVGIcon : *ledOffSVGIcon) // lvi->setIcon(DEVCOL_IN, QIcon( sdet ? *ledGreenSVGIcon : *ledOffSVGIcon); } //inHeartBeat = false; } //--------------------------------------------------------- // syncChanged // val = 1 - Master Mode // 0 - Slave Mode //--------------------------------------------------------- void MidiSyncConfig::syncChanged() { setDirty(); } //--------------------------------------------------------- // extSyncChanged //--------------------------------------------------------- void MidiSyncConfig::extSyncChanged(bool v) { extSyncCheckbox->blockSignals(true); extSyncCheckbox->setChecked(v); // if(v) // MusEGlobal::song->setMasterFlag(false); extSyncCheckbox->blockSignals(false); } //--------------------------------------------------------- // extSyncClicked //--------------------------------------------------------- void MidiSyncConfig::extSyncClicked(bool v) { MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(&MusEGlobal::extSyncFlag, v, MusECore::PendingOperationItem::SetExternalSyncFlag)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // ok Pressed //--------------------------------------------------------- void MidiSyncConfig::ok() { apply(); _dirty = false; if(applyButton->isEnabled()) applyButton->setEnabled(false); if(okButton->isEnabled()) okButton->setEnabled(false); close(); } //--------------------------------------------------------- // cancel Pressed //--------------------------------------------------------- void MidiSyncConfig::cancel() { // Restore the current sync port. MusEGlobal::config.curMidiSyncInPort = _curMidiSyncInPort; _dirty = false; if(applyButton->isEnabled()) applyButton->setEnabled(false); if(okButton->isEnabled()) okButton->setEnabled(false); close(); } //--------------------------------------------------------- // show //--------------------------------------------------------- void MidiSyncConfig::show() { songChanged(-1); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); QDialog::show(); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void MidiSyncConfig::closeEvent(QCloseEvent* e) { if(_dirty) { int n = QMessageBox::warning(this, tr("MusE"), tr("Settings have changed\n" "Apply sync settings?"), tr("&Apply"), tr("&No"), tr("&Abort"), 0, 2); if(n == 2) { e->ignore(); return; } if(n == 0) apply(); } disconnect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), this, SLOT(heartBeat())); disconnect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), this, SLOT(songChanged(MusECore::SongChangedStruct_t))); e->accept(); } //--------------------------------------------------------- // apply Pressed //--------------------------------------------------------- void MidiSyncConfig::apply() { // Protect all structures. if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) MusEGlobal::audio->msgIdle(true); MusEGlobal::syncSendFirstClockDelay = syncDelaySpinBox->value(); MusEGlobal::mtcType = mtcSyncType->currentIndex(); // Make sure the AL namespace variables mirror our variables. AL::mtcType = MusEGlobal::mtcType; MusEGlobal::extSyncFlag = extSyncCheckbox->isChecked(); if(syncRecFilterPreset->currentIndex() != -1) { int fp_idx = syncRecFilterPreset->itemData(syncRecFilterPreset->currentIndex()).toInt(); if(fp_idx >= 0 && fp_idx < MusECore::MidiSyncInfo::TYPE_END) { MusEGlobal::syncRecFilterPreset = MusECore::MidiSyncInfo::SyncRecFilterPresetType(fp_idx); MusEGlobal::midiSyncContainer.setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); } } MusEGlobal::syncRecTempoValQuant = syncRecTempoValQuant->value(); MusEGlobal::midiSyncContainer.setRecTempoValQuant(MusEGlobal::syncRecTempoValQuant); MusEGlobal::mtcOffset.setH(mtcOffH->value()); MusEGlobal::mtcOffset.setM(mtcOffM->value()); MusEGlobal::mtcOffset.setS(mtcOffS->value()); MusEGlobal::mtcOffset.setF(mtcOffF->value()); MusEGlobal::mtcOffset.setSf(mtcOffSf->value()); for (int i = MusECore::MIDI_PORTS-1; i >= 0; --i) { MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)devicesListView->topLevelItem(i); int port = lvi->port(); if(port >= 0 && port < MusECore::MIDI_PORTS) lvi->copyToSyncInfo(MusEGlobal::midiPorts[port].syncInfo()); } // Update the current value. _curMidiSyncInPort = MusEGlobal::config.curMidiSyncInPort; if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) MusEGlobal::audio->msgIdle(false); // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. //muse->changeConfig(true); MusEGlobal::song->update(SC_EXTERNAL_MIDI_SYNC | SC_USE_JACK_TRANSPORT); _dirty = false; if(applyButton->isEnabled()) applyButton->setEnabled(false); if(okButton->isEnabled()) okButton->setEnabled(false); // Do not call this. Causes freeze sometimes. Only will be needed if extra pollfds are used by midi seq thread. //midiSeq->msgUpdatePollFd(); } //--------------------------------------------------------- // updateSyncInfoLV //--------------------------------------------------------- void MidiSyncConfig::updateSyncInfoLV() { devicesListView->clear(); for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* dev = port->device(); // Don't show if it is a synthesizer device. // Hmm, some synths might support transport commands or even sync? // If anything, the DSSI or VST synths just might... // TODO: Must test to see if it screws any of them up, especially clock out. // Also, if we do this, we must prevent such messages from reaching // those ports at several other places in the code. //if(dev && dev->isSynti()) // continue; QString s; s.setNum(i+1); MidiSyncLViewItem* lvi = new MidiSyncLViewItem(devicesListView); lvi->setPort(i); // setPort will copy parameters. MusECore::MidiSyncInfo& portsi = port->syncInfo(); lvi->setText(DEVCOL_NO, s); if (dev) lvi->setText(DEVCOL_NAME, dev->name()); else lvi->setText(DEVCOL_NAME, tr("")); if(portsi.MCSyncDetect()) { { lvi->_curDet = false; lvi->_inDet = true; lvi->setIcon(DEVCOL_IN, *ledGreenSVGIcon); } } else { lvi->_curDet = false; lvi->_inDet = false; lvi->setIcon(DEVCOL_IN, *ledOffSVGIcon); } if(i == MusEGlobal::config.curMidiSyncInPort) lvi->setIcon(DEVCOL_SYNC_TO, *ledRedSVGIcon); else lvi->setIcon(DEVCOL_SYNC_TO, *ledOffSVGIcon); if(portsi.tickDetect()) { lvi->_tickDet = true; lvi->setIcon(DEVCOL_TICKIN, *ledGreenSVGIcon); } else { lvi->_tickDet = false; lvi->setIcon(DEVCOL_TICKIN, *ledOffSVGIcon); } if(portsi.MRTDetect()) { lvi->_MRTDet = true; lvi->setIcon(DEVCOL_MRTIN, *ledGreenSVGIcon); } else { lvi->_MRTDet = false; lvi->setIcon(DEVCOL_MRTIN, *ledOffSVGIcon); } if(portsi.MMCDetect()) { lvi->_MMCDet = true; lvi->setIcon(DEVCOL_MMCIN, *ledGreenSVGIcon); // MMC locate command can have SMPTE format bits... if(lvi->_recMTCtype != portsi.recMTCtype()) { switch(portsi.recMTCtype()) { case 0: lvi->setText(DEVCOL_MTCTYPE, "24"); break; case 1: lvi->setText(DEVCOL_MTCTYPE, "25"); break; case 2: lvi->setText(DEVCOL_MTCTYPE, "30D"); break; case 3: lvi->setText(DEVCOL_MTCTYPE, "30N"); break; default: lvi->setText(DEVCOL_MTCTYPE, "??"); break; } } } else { lvi->_MMCDet = false; lvi->setIcon(DEVCOL_MMCIN, *ledOffSVGIcon); } if(portsi.MTCDetect()) { { lvi->_curMTCDet = false; lvi->_MTCDet = true; lvi->setIcon(DEVCOL_MTCIN, *ledGreenSVGIcon); } if(lvi->_recMTCtype != portsi.recMTCtype()) { switch(portsi.recMTCtype()) { case 0: lvi->setText(DEVCOL_MTCTYPE, "24"); break; case 1: lvi->setText(DEVCOL_MTCTYPE, "25"); break; case 2: lvi->setText(DEVCOL_MTCTYPE, "30D"); break; case 3: lvi->setText(DEVCOL_MTCTYPE, "30N"); break; default: lvi->setText(DEVCOL_MTCTYPE, "??"); break; } } } else { lvi->_curMTCDet = false; lvi->_MTCDet = false; lvi->setIcon(DEVCOL_MTCIN, *ledOffSVGIcon); //lvi->setText(DEVCOL_MTCTYPE, "--"); } lvi->setText(DEVCOL_RID, QString().setNum(lvi->_idIn) ); lvi->setIcon(DEVCOL_RCLK, lvi->_recMC ? *ledGreenSVGIcon : *ledOffSVGIcon); lvi->setIcon(DEVCOL_RMRT, lvi->_recMRT ? *ledGreenSVGIcon : *ledOffSVGIcon); lvi->setIcon(DEVCOL_RMMC, lvi->_recMMC ? *ledGreenSVGIcon : *ledOffSVGIcon); lvi->setIcon(DEVCOL_RMTC, lvi->_recMTC ? *ledGreenSVGIcon : *ledOffSVGIcon); lvi->setIcon(DEVCOL_RREWSTART, lvi->_recRewOnStart ? *ledGreenSVGIcon : *ledOffSVGIcon); lvi->setText(DEVCOL_TID, QString().setNum(lvi->_idOut) ); lvi->setIcon(DEVCOL_TCLK, lvi->_sendMC ? *ledGreenSVGIcon : *ledOffSVGIcon); lvi->setIcon(DEVCOL_TMRT, lvi->_sendMRT ? *ledGreenSVGIcon : *ledOffSVGIcon); lvi->setIcon(DEVCOL_TMMC, lvi->_sendMMC ? *ledGreenSVGIcon : *ledOffSVGIcon); lvi->setIcon(DEVCOL_TMTC, lvi->_sendMTC ? *ledGreenSVGIcon : *ledOffSVGIcon); //lvi->setIcon(DEVCOL_TREWSTART, QIcon( lvi->_sendContNotStart ? *ledGreenSVGIcon : *ledOffSVGIcon); addDevice(lvi, devicesListView); } devicesListView->resizeColumnToContents(DEVCOL_NO); //devicesListView->resizeColumnToContents(DEVCOL_NAME); devicesListView->header()->resizeSection(DEVCOL_NAME, 120); devicesListView->resizeColumnToContents(DEVCOL_SYNC_TO); devicesListView->resizeColumnToContents(DEVCOL_IN); devicesListView->resizeColumnToContents(DEVCOL_TICKIN); devicesListView->resizeColumnToContents(DEVCOL_MRTIN); devicesListView->resizeColumnToContents(DEVCOL_MMCIN); devicesListView->resizeColumnToContents(DEVCOL_MTCIN); devicesListView->resizeColumnToContents(DEVCOL_MTCTYPE); devicesListView->resizeColumnToContents(DEVCOL_RID); devicesListView->resizeColumnToContents(DEVCOL_RCLK); devicesListView->resizeColumnToContents(DEVCOL_RMRT); devicesListView->resizeColumnToContents(DEVCOL_RMMC); devicesListView->resizeColumnToContents(DEVCOL_RMTC); devicesListView->resizeColumnToContents(DEVCOL_RREWSTART); devicesListView->resizeColumnToContents(DEVCOL_TID); devicesListView->resizeColumnToContents(DEVCOL_TCLK); devicesListView->resizeColumnToContents(DEVCOL_TMRT); devicesListView->resizeColumnToContents(DEVCOL_TMMC); devicesListView->resizeColumnToContents(DEVCOL_TMTC); devicesListView->header()->setSectionResizeMode(DEVCOL_NO, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_SYNC_TO, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_IN, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_TICKIN, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_MRTIN, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_MMCIN, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_MTCIN, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_RCLK, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_RMRT, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_RMMC, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_RMTC, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_RMTC, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_RREWSTART, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_TCLK, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_TMRT, QHeaderView::Fixed); devicesListView->header()->setSectionResizeMode(DEVCOL_TMMC, QHeaderView::Fixed); } //--------------------------------------------------------- // dlvClicked //--------------------------------------------------------- void MidiSyncConfig::dlvClicked(QTreeWidgetItem* item, int col) { if (item == 0) return; MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)item; int no = lvi->port(); if (no < 0 || no >= MusECore::MIDI_PORTS) return; //MusECore::MidiDevice* dev = lvi->device(); // Does the device really exist? //if(midiDevices.find(dev) == midiDevices.end()) // return; switch (col) { case DEVCOL_NO: break; case DEVCOL_NAME: break; case DEVCOL_SYNC_TO: if(no != MusEGlobal::config.curMidiSyncInPort) { // Turn off the current sync port's light. MidiSyncLViewItem* prev_lvi = (MidiSyncLViewItem*)devicesListView->topLevelItem(MusEGlobal::config.curMidiSyncInPort); if(prev_lvi) prev_lvi->setIcon(DEVCOL_SYNC_TO, *ledOffSVGIcon); // Set the current sync port and turn on the port's light. MusEGlobal::config.curMidiSyncInPort = no; lvi->setIcon(DEVCOL_SYNC_TO, *ledRedSVGIcon); setDirty(); } break; case DEVCOL_IN: break; case DEVCOL_TICKIN: break; case DEVCOL_MMCIN: break; case DEVCOL_MTCIN: break; case DEVCOL_MTCTYPE: break; case DEVCOL_RID: break; case DEVCOL_RCLK: lvi->_recMC = (lvi->_recMC ? false : true); lvi->setIcon(DEVCOL_RCLK, lvi->_recMC ? *ledGreenSVGIcon : *ledOffSVGIcon); setDirty(); break; case DEVCOL_RMRT: lvi->_recMRT = (lvi->_recMRT ? false : true); lvi->setIcon(DEVCOL_RMRT, lvi->_recMRT ? *ledGreenSVGIcon : *ledOffSVGIcon); setDirty(); break; case DEVCOL_RMMC: lvi->_recMMC = (lvi->_recMMC ? false : true); lvi->setIcon(DEVCOL_RMMC, lvi->_recMMC ? *ledGreenSVGIcon : *ledOffSVGIcon); setDirty(); break; case DEVCOL_RMTC: lvi->_recMTC = (lvi->_recMTC ? false : true); lvi->setIcon(DEVCOL_RMTC, lvi->_recMTC ? *ledGreenSVGIcon : *ledOffSVGIcon); setDirty(); break; case DEVCOL_RREWSTART: lvi->_recRewOnStart = (lvi->_recRewOnStart ? false : true); lvi->setIcon(DEVCOL_RREWSTART, lvi->_recRewOnStart ? *ledGreenSVGIcon : *ledOffSVGIcon); setDirty(); break; case DEVCOL_TID: break; case DEVCOL_TCLK: lvi->_sendMC = (lvi->_sendMC ? false : true); lvi->setIcon(DEVCOL_TCLK, lvi->_sendMC ? *ledGreenSVGIcon : *ledOffSVGIcon); setDirty(); break; case DEVCOL_TMRT: lvi->_sendMRT = (lvi->_sendMRT ? false : true); lvi->setIcon(DEVCOL_TMRT, lvi->_sendMRT ? *ledGreenSVGIcon : *ledOffSVGIcon); setDirty(); break; case DEVCOL_TMMC: lvi->_sendMMC = (lvi->_sendMMC ? false : true); lvi->setIcon(DEVCOL_TMMC, lvi->_sendMMC ? *ledGreenSVGIcon : *ledOffSVGIcon); setDirty(); break; case DEVCOL_TMTC: lvi->_sendMTC = (lvi->_sendMTC ? false : true); lvi->setIcon(DEVCOL_TMTC, lvi->_sendMTC ? *ledGreenSVGIcon : *ledOffSVGIcon); setDirty(); break; //case DEVCOL_TREWSTART: // lvi->_sendContNotStart = (lvi->_sendContNotStart ? false : true); // lvi->setIcon(DEVCOL_TREWSTART, QIcon( lvi->_sendContNotStart ? *ledGreenSVGIcon : *ledOffSVGIcon); // setDirty(); // break; } //songChanged(-1); } //--------------------------------------------------------- // dlvDoubleClicked //--------------------------------------------------------- void MidiSyncConfig::dlvDoubleClicked(QTreeWidgetItem* item, int col) { if(!item) return; MidiSyncLViewItem* lvi = (MidiSyncLViewItem*)item; bool ok = false; if(col == DEVCOL_RID) { int val = lvi->_idIn; int newval = QInputDialog::getInt(this, "Muse: Sync info" , "Enter new id number (127 = all):", val, 0, 127, 1, &ok); if(ok) { lvi->_idIn = newval; lvi->setText(DEVCOL_RID, QString().setNum(newval)); } } else if(col == DEVCOL_TID) { int val = lvi->_idOut; int newval = QInputDialog::getInt(this, "Muse: Sync info" , "Enter new id number (127 = global):", val, 0, 127, 1, &ok); if(ok) { lvi->_idOut = newval; lvi->setText(DEVCOL_TID, QString().setNum(newval)); } } if(ok) setDirty(); } //--------------------------------------------------------- // MidiSyncConfig::setDirty //--------------------------------------------------------- void MidiSyncConfig::setDirty() { _dirty = true; if(!applyButton->isEnabled()) applyButton->setEnabled(true); if(!okButton->isEnabled()) okButton->setEnabled(true); } } // namespace MusEGui muse-4.0.0/src/muse/components/midisyncimpl.h000066400000000000000000000077671404105311000212710ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midisyncimpl.h,v 1.1.1.1.2.3 2009/05/03 04:14:01 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDISYNCIMPL_H__ #define __MIDISYNCIMPL_H__ #include "ui_midisync.h" #include #include #include #include "type_defs.h" #include "sync.h" class QCloseEvent; class QTreeWidget; namespace MusEGui { //---------------------------------------------------------- // MidiSyncLViewItem //---------------------------------------------------------- class MidiSyncLViewItem : public QTreeWidgetItem { //MusECore::MidiSyncInfo _syncInfo; //MidiDevice* _device; int _port; //protected: //int _port; public: MidiSyncLViewItem(QTreeWidget* parent) : QTreeWidgetItem(parent) { _port = -1; _inDet = _curDet = _tickDet = false; } //: QListViewItem(parent) { _device = 0; } //MidiSyncLViewItem(QListView* parent, QListViewItem* after) // : QListViewItem(parent, after) { _port = -1; } //virtual QString text(int column) const; //virtual unsigned tick() = 0; //int _port; bool _inDet; bool _curDet; bool _curMTCDet; bool _tickDet; bool _MRTDet; bool _MMCDet; bool _MTCDet; int _recMTCtype; int _idOut; int _idIn; bool _sendMC; bool _sendMRT; bool _sendMMC; bool _sendMTC; bool _recMC; bool _recMRT; bool _recMMC; bool _recMTC; bool _recRewOnStart; //bool _sendContNotStart; int port() const { return _port; } void setPort(int port); //MidiDevice* device() const { return _device; } //void setDevice(MidiDevice* d); //MusECore::MidiSyncInfo& syncInfo() { return _syncInfo; } void copyFromSyncInfo(const MusECore::MidiSyncInfo &sp); void copyToSyncInfo(MusECore::MidiSyncInfo &sp); }; //--------------------------------------------------------- // MSConfig //--------------------------------------------------------- class MidiSyncConfig : public QDialog, public Ui::MidiSyncConfigBase { Q_OBJECT bool inHeartBeat; bool _dirty; int _curMidiSyncInPort; // The original value, for restoring on Cancel. void updateSyncInfoLV(); void closeEvent(QCloseEvent*); void setToolTips(QTreeWidgetItem *item); void setWhatsThis(QTreeWidgetItem *item); void addDevice(QTreeWidgetItem *item, QTreeWidget *tree); private slots: void heartBeat(); void syncChanged(); void extSyncChanged(bool v); void extSyncClicked(bool v); void ok(); void cancel(); void apply(); //void dlvClicked(QListViewItem*, const QPoint&, int); void dlvClicked(QTreeWidgetItem*, int); void dlvDoubleClicked(QTreeWidgetItem*, int); //void renameOk(QListViewItem*, int, const QString&); void songChanged(MusECore::SongChangedStruct_t); public: MidiSyncConfig(QWidget* parent=0); //MidiSyncConfig(); ~MidiSyncConfig(); void show(); void setDirty(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/mixdowndialog.cpp000066400000000000000000000110311404105311000217430ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mixdowndialog.cpp,v 1.1.1.1 2003/10/27 18:55:02 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "globals.h" #include "gconfig.h" #include "mixdowndialog.h" #include "wave.h" namespace MusECore { //--------------------------------------------------------- // sndFileOpen // sf - old soundfile, used to preset file parameters //--------------------------------------------------------- SndFile* getSndFile(const SndFile* sf, QWidget* parent) { MusEGui::MixdownFileDialog* dialog = new MusEGui::MixdownFileDialog(sf, parent); dialog->exec(); MusECore::SndFile* sndFile = dialog->sndFile(); delete dialog; return sndFile; } } // namespace MusECore namespace MusEGui { //--------------------------------------------------------- // MixdownFileDialog //--------------------------------------------------------- MixdownFileDialog::MixdownFileDialog(const MusECore::SndFile* _sf, QWidget* parent, Qt::WindowFlags fl) : QDialog(parent, fl) { setupUi(this); sf = 0; connect(buttonPath, SIGNAL(clicked()), SLOT(fdialog())); if (_sf) { int channels = _sf->channels(); int format = _sf->format(); switch(channels) { case 1: channels = 1; break; case 2: channels = 0; break; case 6: channels = 2; break; } editPath->setText(_sf->path()); comboChannel->setCurrentIndex(channels); comboFormat->setCurrentIndex(format); } } //--------------------------------------------------------- // accept //--------------------------------------------------------- void MixdownFileDialog::accept() { QString oldpath; unsigned channel = comboChannel->currentIndex(); unsigned format = comboFormat->currentIndex(); switch (channel) { case 0: channel = 2; break; case 1: channel = 1; break; case 2: channel = 6; break; // not implemented! } switch (format) { case 0: // 16 bit wave format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; break; case 1: // 24 bit wave format = SF_FORMAT_WAV | SF_FORMAT_PCM_24; break; case 2: // 32 bit float wave format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; break; } QString path = editPath->text(); if (path.isEmpty()) { sf = 0; reject(); return; } if (path.right(4) != ".wav") path += ".wav"; sf = new MusECore::SndFile(path); sf->setFormat(format, channel, MusEGlobal::sampleRate); done(1); } //--------------------------------------------------------- // fdialog //--------------------------------------------------------- void MixdownFileDialog::fdialog() { QString oldpath; if (sf) { oldpath = sf->path(); } if (!MusEGlobal::config.mixdownPath.isEmpty()) { printf("Setting oldpath to %s\n", MusEGlobal::config.mixdownPath.toLatin1().data()); oldpath = MusEGlobal::config.mixdownPath; } QString path = QFileDialog::getSaveFileName(this, 0, oldpath, tr("Wave Files (*.wav);;All Files (*)")); if (!path.isEmpty()) { editPath->setText(path); } // check if the file already exists, which can happen. // Easiest way to get the recording to work as intended we simply remove the old file. if (QFileInfo::exists(path)) { QFile f(path); f.remove(); } MusEGlobal::config.mixdownPath = path; } } // namespace MusEGui muse-4.0.0/src/muse/components/mixdowndialog.h000066400000000000000000000033501404105311000214150ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mixdowndialog.h,v 1.1.1.1 2003/10/27 18:54:28 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIXDOWNDIALOG_H__ #define __MIXDOWNDIALOG_H__ #include "ui_mixdowndialogbase.h" class QWidget; namespace MusECore { class SndFile; extern SndFile* getSndFile(const SndFile* sf, QWidget* parent); } namespace MusEGui { //--------------------------------------------------------- // MixdownFileDialog //--------------------------------------------------------- class MixdownFileDialog : public QDialog, public Ui::MixdownFileDialogBase { Q_OBJECT MusECore::SndFile* sf; private slots: void fdialog(); virtual void accept(); public: MixdownFileDialog(const MusECore::SndFile* f, QWidget* parent = 0, Qt::WindowFlags fl = Qt::Widget); MusECore::SndFile* sndFile() { return sf; } }; } #endif muse-4.0.0/src/muse/components/mixdowndialogbase.ui000066400000000000000000000133261404105311000224420ustar00rootroot00000000000000 MixdownFileDialogBase 0 0 381 116 Set Downmix Wavefile true 11 11 11 11 6 6 0 0 0 0 Qt::Horizontal QSizePolicy::Expanding 20 20 &OK true true &Cancel true File Path false Channel false 6 0 0 0 0 ... Stereo Mono 5.1 wav,16 Bit wav, 24 Bit wav, 32 Bit (float) Format false Qt::Horizontal QSizePolicy::Expanding 20 20 buttonOk clicked() MixdownFileDialogBase accept() 20 20 20 20 buttonCancel clicked() MixdownFileDialogBase reject() 20 20 20 20 muse-4.0.0/src/muse/components/mtscale.cpp000066400000000000000000000764741404105311000205540ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mtscale.cpp,v 1.8.2.7 2009/05/03 04:14:01 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "mtscale.h" #include "song.h" #include "app.h" #include "icons.h" #include "gconfig.h" #include "globals.h" namespace MusEGui { //--------------------------------------------------------- // MTScale // Midi Time Scale //--------------------------------------------------------- MTScale::MTScale(int r, QWidget* parent, int xs, bool _mode) : View(parent, xs, 1) { waveMode = _mode; setStatusTip(tr("Time scale: Set position (LMB) and range markers (left: CTRL+LMB or MMB, right: CTRL+RMB or RMB). Hold SHIFT to set (LMB) or delete (RMB) custom markers.")); barLocator = false; raster = r; if (waveMode) { pos[0] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->cpos()); pos[1] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->lpos()); pos[2] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->rpos()); } else { pos[0] = MusEGlobal::song->cpos(); pos[1] = MusEGlobal::song->lpos(); pos[2] = MusEGlobal::song->rpos(); } pos[3] = INT_MAX; // do not show button = Qt::NoButton; setMouseTracking(true); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(setPos(int, unsigned, bool))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::song, SIGNAL(markerChanged(int)), SLOT(redraw())); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); setFixedHeight(28); setBg(MusEGlobal::config.rulerBg); } void MTScale::configChanged() { setBg(MusEGlobal::config.rulerBg); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MTScale::songChanged(MusECore::SongChangedStruct_t type) { if (type & (SC_SIG|SC_TEMPO|SC_MARKERS_REBUILT|SC_MARKER_INSERTED|SC_MARKER_REMOVED|SC_MARKER_MODIFIED)) { if ((type & SC_TEMPO) && waveMode) { pos[0] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->cpos()); pos[1] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->lpos()); pos[2] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->rpos()); } redraw(); } } //--------------------------------------------------------- // setPos //--------------------------------------------------------- void MTScale::setPos(int idx, unsigned val, bool) { if (val == INT_MAX) { if (idx == 3) { pos[3] = INT_MAX; redraw(QRect(0, 0, width(), height())); } return; } // Why don't we just rasterize here instead of in the callers? // Possibly because we may need fine control over this PROGRAMMED position // versus the position EMITTED by MTScale? //val = MusEGlobal::sigmap.raster(val, raster); if (waveMode) val = MusEGlobal::tempomap.tick2frame(val); if (val == pos[idx]) return; int opos = mapx(pos[idx] == INT_MAX ? val : pos[idx]); pos[idx] = val; if (!isVisible()) return; int tval = mapx(val); int x = -9; int w = 18; if (tval < 0) { // tval<0 occurs whenever the window is scrolled left, so I switched to signed int (ml) //printf("MTScale::setPos - idx:%d val:%d tval:%d opos:%d w:%d h:%d\n", idx, val, tval, opos, width(), height()); redraw(QRect(0,0,width(),height())); return; } if (opos > tval) { w += opos - tval; x += tval; } else { w += tval - opos; x += opos; } //printf("MTScale::setPos idx:%d val:%d tval:%d opos:%d x:%d w:%d h:%d\n", idx, val, tval, opos, x, w, height()); redraw(QRect(x, 0, w, height())); } //--------------------------------------------------------- // setRaster //--------------------------------------------------------- void MTScale::setRaster(int rast) { raster = rast; redraw(); } //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- void MTScale::viewMousePressEvent(QMouseEvent* event) { button = event->button(); viewMouseMoveEvent(event); } //--------------------------------------------------------- // viewMouseReleaseEvent //--------------------------------------------------------- void MTScale::viewMouseReleaseEvent(QMouseEvent*) { button = Qt::NoButton; } //--------------------------------------------------------- // viewMouseMoveEvent //--------------------------------------------------------- void MTScale::viewMouseMoveEvent(QMouseEvent* event) { if (event->modifiers() & Qt::ShiftModifier ) setCursor(QCursor(Qt::PointingHandCursor)); else setCursor(QCursor(Qt::ArrowCursor)); int x = event->x(); if (x < 0) x = 0; if (waveMode) // Normally frame to tick methods round down. But here we need it to 'snap' // the frame from either side of a tick to the tick. So round to nearest. x = MusEGlobal::tempomap.frame2tick(x, 0, MusECore::LargeIntRoundNearest); x = MusEGlobal::sigmap.raster(x, raster); //printf("MTScale::viewMouseMoveEvent\n"); emit timeChanged(x); MusECore::Song::POSTYPE posType; switch (button) { case Qt::LeftButton: if (event->modifiers() & Qt::ControlModifier) posType = MusECore::Song::LPOS; else posType = MusECore::Song::CPOS; break; case Qt::MidButton: posType = MusECore::Song::LPOS; break; case Qt::RightButton: posType = MusECore::Song::RPOS; break; default: return; // if no button is pressed the function returns here } MusECore::Pos p(x, true); if(posType == MusECore::Song::CPOS && (event->modifiers() & Qt::ShiftModifier )) { // If shift +LMB we add a marker const MusECore::iMarker alreadyExists = MusEGlobal::song->getMarkerAt(x); if (alreadyExists == MusEGlobal::song->marker()->end()) { MusEGlobal::song->addMarker(QString(""), x, false); // Note: Song::addMarker() already emits a 'markerChanged'. //emit addMarker(x); } } else if (posType == MusECore::Song::RPOS && (event->modifiers() & Qt::ShiftModifier )) { // If shift +RMB we remove a marker const MusECore::iMarker toRemove = MusEGlobal::song->getMarkerAt(x); if (toRemove != MusEGlobal::song->marker()->end()) MusEGlobal::song->removeMarker(toRemove->second); else fprintf(stderr, "No marker to remove\n"); } else MusEGlobal::song->setPos(posType, p); // all other cases: relocating one of the locators } //--------------------------------------------------------- // leaveEvent //--------------------------------------------------------- void MTScale::leaveEvent(QEvent*) { emit timeChanged(INT_MAX); } //--------------------------------------------------------- // drawTickRaster //--------------------------------------------------------- void MTScale::drawTickRaster( QPainter& p, const QRect& mr, const QRegion& /*mrg*/, int /*raster*/, bool waveMode, bool /*useGivenColors*/, bool /*drawText*/, const QColor& /*bar_color*/, const QColor& /*beat_color*/, const QColor& /*fine_color*/, const QColor& /*coarse_color*/, const QColor& text_color, const QFont& large_font, const QFont& small_font ) { const ViewRect r(mr, true); const ViewXCoordinate& x = r._x; const ViewXCoordinate x_2(mr.x() + mr.width(), true); //p.save(); bool wmtxen = p.worldMatrixEnabled(); p.setWorldMatrixEnabled(false); // Limiter required because MusEGlobal::sigmap.tickValues takes unsigned only ! const ViewXCoordinate lim_v0(0, false); const ViewXCoordinate x_lim = compareXCoordinates(x, lim_v0, CompareLess) ? lim_v0 : x; const ViewXCoordinate x_2lim = compareXCoordinates(x_2, lim_v0, CompareLess) ? lim_v0 : x_2; const int my = mr.y(); const int mh = mr.height(); const int mbottom = mr.bottom(); const ViewYCoordinate bottom(mr.bottom(), true); const int mw1000 = 1000; const int mh2 = 2; const ViewWCoordinate w2(2, true); const ViewHCoordinate h2(2, true); const ViewHCoordinate h_m1(mh - 1, true); const ViewHCoordinate h_m3(mh - 3, true); int bar1, bar2, beat; ScaleRetStruct scale_info; int beat_start_beat; unsigned tick; QPen pen; pen.setCosmetic(true); // For testing... // fprintf(stderr, "View::drawTickRaster_new(): virt:%d drawText:%d mx:%d my:%d mw:%d mh:%draster%d\n", // virt(), drawText, mx, my, mw, mh, raster); if (waveMode) { MusEGlobal::sigmap.tickValues(MusEGlobal::tempomap.frame2tick(asUnmapped(x_lim)._value), &bar1, &beat, &tick); MusEGlobal::sigmap.tickValues(MusEGlobal::tempomap.frame2tick(asUnmapped(x_2lim)._value), &bar2, &beat, &tick); } else { MusEGlobal::sigmap.tickValues(asUnmapped(x_lim)._value, &bar1, &beat, &tick); MusEGlobal::sigmap.tickValues(asUnmapped(x_2lim)._value, &bar2, &beat, &tick); } //fprintf(stderr, "bar %d %d-%d=%d\n", bar, ntick, stick, ntick-stick); int stick = MusEGlobal::sigmap.bar2tick(bar1, 0, 0); int ntick, deltaTick; ScaleRetStruct prev_scale_info; for (int bar = bar1; bar <= bar2; bar++, stick = ntick) { ntick = MusEGlobal::sigmap.bar2tick(bar+1, 0, 0); deltaTick = ntick - stick; int a, b=0; double tpix; if (waveMode) { a = MusEGlobal::tempomap.tick2frame(ntick); b = MusEGlobal::tempomap.tick2frame(stick); tpix = rmapx_f(a - b); } else { tpix = rmapx_f(deltaTick); } scale_info = scale(true, bar, tpix); if(!scale_info._drawBar) { // Only check this on the first starting bar. if(bar == bar1) { int prev_a, prev_b=0; double prev_tpix; int prev_stick; int prev_ntick = stick; int prev_bar = bar1 - 1; for( ; prev_bar >= 0; --prev_bar, prev_ntick = prev_stick) { prev_stick = MusEGlobal::sigmap.bar2tick(prev_bar, 0, 0); if (waveMode) { prev_a = MusEGlobal::tempomap.tick2frame(prev_ntick); prev_b = MusEGlobal::tempomap.tick2frame(prev_stick); prev_tpix = rmapx_f(prev_a - prev_b); } else { prev_tpix = rmapx_f(prev_ntick - prev_stick); } prev_scale_info = scale(true, prev_bar, prev_tpix); // For testing... // fprintf(stderr, // "drawTickRaster: Bar check: bar1:%d bar2:%d bar:%d prev_bar:%d" // " stick:%d prev_stick:%d prev_ntick:%d prev_tpix:%f drawBar:%d\n", // bar1, bar2, bar, prev_bar, stick, prev_stick, prev_ntick, prev_tpix, // prev_scale_info._drawBar); if(prev_scale_info._drawBar) break; } if(prev_bar >= 0) { const int prev_tick = waveMode ? prev_b : prev_stick; drawBarText(p, prev_tick, prev_bar, mr, text_color, large_font); } } continue; } if(scale_info._isSmall) { const int tick_sm = waveMode ? b : stick; const ViewXCoordinate x_sm(tick_sm, false); const int mx_sm = asMapped(x_sm)._value; pen.setColor(text_color); p.setPen(pen); if(scale_info._drawBar) { // For testing... // fprintf(stderr, // "is_small:%d Coarse line: tick_sm:%d mx_sm:%d stick:%d ntick:%d bar1:%d bar2:%d bar:%d\n", // scale_info._isSmall, tick_sm, mx_sm, stick, ntick, bar1, bar2, bar); if(isXInRange(x_sm, x, x_2)) { // For testing... // fprintf(stderr, "is_small:%d Coarse Line is within range." // " Drawing line at mx_sm:%d my:%d mx_sm:%d mbottom:%d...\n", // scale_info._isSmall, mx_sm, my, mx_sm, mbottom); p.drawLine(mx_sm, my, mx_sm, mbottom); } } // For testing... // fprintf(stderr, // "is_small:%d Coarse text: tick_sm:%d mx_sm:%d stick:%d ntick:%d bar1:%d bar2:%d bar:%d\n", // scale_info._isSmall, tick_sm, mx_sm, stick, ntick, bar1, bar2, bar); drawBarText(p, tick_sm, bar, mr, text_color, large_font); } if(!scale_info._isSmall) { int z, n; MusEGlobal::sigmap.timesig(stick, z, n); beat_start_beat = 0; for (int beat = beat_start_beat; beat < z; beat++) { int xx = MusEGlobal::sigmap.bar2tick(bar, beat, 0); // int xx_e = MusEGlobal::sigmap.bar2tick(bar, beat + 1, 0); if (waveMode) xx = MusEGlobal::tempomap.tick2frame(xx); // if (waveMode) // xx_e = MusEGlobal::tempomap.tick2frame(xx_e); const ViewXCoordinate xx_v(xx, false); const int mxx = asMapped(xx_v)._value; int y1; int num; int m_yt = my; ViewHCoordinate h_txt; if (beat == 0) { num = bar + 1; y1 = my + 1; h_txt = h_m1; p.setFont(large_font); } else { num = beat + 1; y1 = my + 6; m_yt = my + mh2; h_txt = h_m3; p.setFont(small_font); } const ViewYCoordinate yt(m_yt, true); pen.setColor(text_color); p.setPen(pen); if(isXInRange(xx_v, x, x_2)) { // For testing... // fprintf(stderr, "is_small:%d Beat Line is within range." // " Drawing line at mxx:%d y1:%d mbottom:%d...\n", // scale_info._isSmall, mxx, y1, mbottom); p.drawLine(mxx, y1, mxx, mbottom); } if (beat != 0 && !MusEGlobal::config.showTimeScaleBeatNumbers) continue; QString s; s.setNum(num); int brw = p.fontMetrics().boundingRect(s).width(); if(brw > mw1000) brw = mw1000; const ViewWCoordinate brw_v(brw, true); const ViewXCoordinate vxx_v = mathXCoordinates(xx_v, w2, MathAdd); const ViewRect br_txt(vxx_v, yt, brw_v, h_txt); // For testing... // fprintf(stderr, // "drawBarText: Bar text: bar:%d beat:%d vx:%d vy:%d" // " vw:%d vh:%d tick:%d br x:%d y:%d w:%d h:%d\n", // bar, beat, vr.x(), vr.y(), vr.width(), vr.height(), tick, // br_txt.x(), br_txt.y(), br_txt.width(), br_txt.height()); if(intersects(br_txt, r)) { // For testing... // fprintf(stderr, // "is_small:%d Beat text: s:%s stick:%d ntick:%d bar1:%d bar2:%d bar:%d\n", // scale_info._isSmall, s.toLatin1().constData(), stick, ntick, bar1, bar2, bar); p.drawText(asQRectMapped(br_txt), Qt::AlignLeft|Qt::AlignVCenter|Qt::TextDontClip, s); } } } } //p.setWorldMatrixEnabled(true); p.setWorldMatrixEnabled(wmtxen); //p.restore(); } //--------------------------------------------------------- // pdraw //--------------------------------------------------------- void MTScale::pdraw(QPainter& p, const QRect& mr, const QRegion& mrg) { //--------------------------------------------- // Changed to draw in device coordinate space // instead of virtual, transformed space. //--------------------------------------------- //p.save(); bool wmtxen = p.worldMatrixEnabled(); p.setWorldMatrixEnabled(false); int mx = mr.x(); int my = mr.y(); int mw = mr.width(); int mh = mr.height(); const QRect vr = devToVirt(mr); int vx = vr.x(); int vy = vr.y(); int vw = vr.width(); const int mx_2 = mx + mw; const int my12 = 12; // const int my12_m1 = my12 - 1; const int my13 = 13; const int mh_m12 = height() - 12; const int vx_2 = vx + vw; const int vtop = mapyDev(0); const int vw10 = rmapxDev(10); const int vw200 = rmapxDev(200); const int vh12 = rmapyDev(12); const int vh13 = rmapyDev(13); QPen pen; pen.setCosmetic(true); p.setRenderHint(QPainter::Antialiasing); // For testing... //fprintf(stderr, "MTScale::pdraw x:%d w:%d\n", x, w); //--------------------------------------------------- // draw Marker //--------------------------------------------------- pen.setColor(MusEGlobal::config.rulerFg); p.setPen(pen); p.setFont(MusEGlobal::config.fonts[5]); p.drawLine(mx, my13, mx_2, my13); MusECore::MarkerList* marker = MusEGlobal::song->marker(); for (MusECore::iMarker m = marker->begin(); m != marker->end(); ++m) { int xp; if(waveMode) xp = m->second.frame(); else xp = m->second.tick(); if (xp > vx_2) break; int mxp = mapx(xp); int xe = vx_2; MusECore::iMarker mm = m; ++mm; if (mm != marker->end()) { if(waveMode) xe = mm->second.frame(); else xe = mm->second.tick(); } const QRect tr(xp, vy, xe - xp, vh13); QRect wr = vr.intersected(tr); // For testing... // fprintf(stderr, "MTScale::pdraw: marker fill: wr x:%d y:%d w:%d h:%d\n", // wr.x(), wr.y(), wr.width(), wr.height()); if(!wr.isEmpty()) { if (m->second.current()) { // For testing... // fprintf(stderr, "...marker fill within range. Filling area at mx:%d my:%d mw:%d mh:%d\n", // map(wr).x(), map(wr).y(), map(wr).width(), map(wr).height()); p.fillRect(map(wr), MusEGlobal::config.rulerCurrent); } int x2_time; if (mm != marker->end()) { if(waveMode) x2_time = MusEGlobal::tempomap.tick2frame(mm->first); else x2_time = mm->first; } else x2_time = xp + vw200; //printf("MTScale::pdraw marker %s xp:%d y:%d h:%d r.x:%d r.w:%d\n", m->second.name().toLatin1(), xp, height(), y, r.x(), r.width()); // Must be reasonable about very low negative x values! With long songs > 15min // and with high horizontal magnification, 'ghost' drawings appeared, // apparently the result of truncation later (xp = -65006 caused ghosting // at bar 245 with magnification at max.), even with correct clipping region // applied to painter in View::paint(). Tim. Apr 5 2009 // Quote: "Warning: Note that QPainter does not attempt to work around // coordinate limitations in the underlying window system. Some platforms may // behave incorrectly with coordinates as small as +/-4000." const int pmw = rmapxDev(7); const int pmx = xp; const int pmx2 = xp + pmw; // For testing... // fprintf(stderr, // "MTScale::pdraw: Marker: x:%d y:%d w:%d h:%d mx:%d my:%d mw:%d mh:%d x2:%d x2_right:%d xp:%d pmw:%d pmx:%d pmx2:%d\n", // r.x(), r.y(), r.width(), r.height(), mx, my, mw, mh, x2, x2_right, xp, pm->width(), pmx, pmx2); if ((pmx >= vx && pmx < vx_2) || (pmx2 > vx && pmx2 <= vx_2) || (vx >= pmx && vx < pmx2) || (vx_2 > pmx && vx_2 <= pmx2)) { // For testing... // fprintf(stderr, "...marker within range. xorg:%d xmag:%d xpos:%d Drawing marker at:%d\n", xorg, xmag, xpos, mapx(pmx)); p.setBrush(MusEGlobal::config.markerColor); p.setPen(MusEGlobal::config.markerColor); p.drawPolygon( QVector{ {static_cast(mapx(pmx)), 1.}, {static_cast(mapx(pmx2)), 6.}, {static_cast(mapx(pmx)), 11.} } ); } const QString s = m->second.name(); const int brw = rmapxDev(p.fontMetrics().boundingRect(s).width()); int w_txt = x2_time - xp; if(brw < w_txt) w_txt = brw; const QRect br_txt = QRect(xp + vw10, vtop, w_txt, vh12); // For testing... // fprintf(stderr, // "MTScale::pdraw: Marker text: x:%d y:%d w:%d h:%d x2:%d x2_right:%d xp:%d x2_time:%d br x:%d y:%d w:%d h:%d\n", // r.x(), r.y(), r.width(), r.height(), x2, x2_right, xp, x2_time, br_txt.x(), br_txt.y(), br_txt.width(), br_txt.height()); if(br_txt.intersects(vr)) { // For testing... // fprintf(stderr, "...marker text within range. xorg:%d xmag:%d xpos:%d Drawing marker text at x:%d y:%d w:%d h:%d\n", // xorg, xmag, xpos, map(br_txt).x(), map(br_txt).y(), map(br_txt).width(), map(br_txt).height()); pen.setColor(MusEGlobal::config.rulerFg); p.setPen(pen); p.drawText(map(br_txt), Qt::AlignLeft|Qt::AlignVCenter, s); } if(xp >= vx && xp < vx_2) { // For testing... // fprintf(stderr, "...marker line within range. xorg:%d xmag:%d xpos:%d Drawing marker line at mxp:%d my12:%d mheight:%d\n", // xorg, xmag, xpos, mxp, my12, mheight); pen.setColor(MusEGlobal::config.markerColor); p.setPen(pen); p.drawLine(mxp, my12, mxp, mh); } } } //--------------------------------------------------- // draw location marker //--------------------------------------------------- if (barLocator) { pen.setColor(MusEGlobal::config.rangeMarkerColor); p.setPen(pen); int xp = pos[1]; int mxp = mapx(xp); if (xp >= vx && xp < vx_2) p.drawLine(mxp, my, mxp, mh_m12); xp = pos[2]; mxp = mapx(xp); if (xp >= vx && xp < vx_2) p.drawLine(mxp, my, mxp, mh_m12); pen.setColor(MusEGlobal::config.positionMarkerColor); p.setPen(pen); // Draw the red main position cursor last, on top of the others. xp = pos[0]; mxp = mapx(xp); if (xp >= vx && xp < vx_2) p.drawLine(mxp, my, mxp, mh_m12); } else { // For testing... //fprintf(stderr, // "MTScale::pdraw: Pos mark: x:%d y:%d w:%d h:%d mx:%d my:%d mw:%d mh:%d x2:%d x2_right:%d h_m12:%d i:%d xp:%d pmw:%d pmw_d2:%d pmx:%d pmx2:%d\n", // r.x(), r.y(), r.width(), r.height(), mx, my, mw, mh, x2, x2_right, h_m12, i, xp, pm->width(), pmw_d2, pmx, pmx2); // For testing... // fprintf(stderr, "...position mark within range. xorg:%d xmag:%d xpos:%d Drawing mark at:%d\n", xorg, xmag, xpos, mapx(pmx)); const qreal mtop = 18.; const qreal mbottom = 26.; const int radius = 8; // draw left range marker { const int xp = static_cast(pos[1]); const int pmw_d2 = rmapxDev(radius); const int pmx = xp - pmw_d2; const int pmx2 = xp + pmw_d2; if ((pmx >= vx && pmx < vx_2) || (pmx2 > vx && pmx2 <= vx_2) || (vx >= pmx && vx < pmx2) || (vx_2 > pmx && vx_2 <= pmx2)) { p.setBrush(MusEGlobal::config.rangeMarkerColor); p.setPen(MusEGlobal::config.rangeMarkerColor); p.drawPolygon( QVector{ {static_cast(mapx(xp)), mtop}, {static_cast(mapx(pmx)), mtop}, {static_cast(mapx(xp)), mbottom} } ); } } // draw right range marker { const int xp = static_cast(pos[2]); const int pmw_d2 = rmapxDev(radius); const int pmx = xp - pmw_d2; const int pmx2 = xp + pmw_d2; if ((pmx >= vx && pmx < vx_2) || (pmx2 > vx && pmx2 <= vx_2) || (vx >= pmx && vx < pmx2) || (vx_2 > pmx && vx_2 <= pmx2)) { p.setBrush(MusEGlobal::config.rangeMarkerColor); p.setPen(MusEGlobal::config.rangeMarkerColor); p.drawPolygon( QVector{ {static_cast(mapx(xp)), mtop}, {static_cast(mapx(pmx2)), mtop}, {static_cast(mapx(xp)), mbottom} } ); } } if (pos[2] > pos[1]) { QColor c(MusEGlobal::config.rangeMarkerColor); c.setAlpha(96); QRect rr(QPoint(mapx(static_cast(pos[1])), static_cast(mtop)), QPoint(mapx(static_cast(pos[2])), static_cast(mbottom))); p.fillRect(mr & rr, c); } // draw position marker { const int xp = static_cast(pos[0]); const int pmw_d2 = rmapxDev(radius); const int pmx = xp - pmw_d2; const int pmx2 = xp + pmw_d2; if ((pmx >= vx && pmx < vx_2) || (pmx2 > vx && pmx2 <= vx_2) || (vx >= pmx && vx < pmx2) || (vx_2 > pmx && vx_2 <= pmx2)) { p.setBrush(MusEGlobal::config.positionMarkerColor); p.setPen(MusEGlobal::config.positionMarkerColor); p.drawPolygon( QVector{ {static_cast(mapx(pmx)), mtop}, {static_cast(mapx(pmx2)), mtop}, {static_cast(mapx(xp)), mbottom} } ); } } } if (pos[3] != INT_MAX) { int xp = pos[3]; int mxp = mapx(xp); // For testing... // fprintf(stderr, // "MTScale::pdraw: Ruler: x:%d y:%d w:%d h:%d mx:%d my:%d mw:%d mh:%d h_m12:%d pos[3]:%d xp:%d mxp:%d\n", // r.x(), r.y(), r.width(), r.height(), mx, my, mw, mh, h_m12, pos[3], xp, mxp); if (xp >= vx && xp < vx_2) { pen.setColor(MusEGlobal::config.currentPositionColor); p.setPen(pen); // For testing... // fprintf(stderr, "...ruler line within range. Drawing line.\n"); p.drawLine(mxp, my, mxp, mh); } } //p.setWorldMatrixEnabled(true); p.setWorldMatrixEnabled(wmtxen); //p.restore(); drawTickRaster(p, QRect(mr.x(), my13, mr.width(), mh_m12), mrg, 0, waveMode, false, true, Qt::red, // dummy color, initialize to a bold color so it will be evident if it is used Qt::red, // -"- Qt::red, // dummy color, initialize to a bold color so it will be evident if it is used Qt::red, // -"- MusEGlobal::config.rulerFg, MusEGlobal::config.fonts[3], MusEGlobal::config.fonts[4]); } } // namespace MusEGui muse-4.0.0/src/muse/components/mtscale.h000066400000000000000000000057461404105311000202130ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mtscale.h,v 1.3 2004/04/27 22:27:06 spamatica Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MTSCALE_H__ #define __MTSCALE_H__ #include "type_defs.h" #include "view.h" namespace MusEGui { //--------------------------------------------------------- // MTScale // scale for midi track //--------------------------------------------------------- class MTScale : public View { Q_OBJECT int raster; unsigned pos[4]; int button; bool barLocator; bool waveMode; private slots: void songChanged(MusECore::SongChangedStruct_t); void configChanged(); protected: virtual void pdraw(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void viewMousePressEvent(QMouseEvent* event); virtual void viewMouseMoveEvent(QMouseEvent* event); virtual void viewMouseReleaseEvent(QMouseEvent* event); virtual void leaveEvent(QEvent*e); virtual void drawTickRaster(QPainter& p, const QRect&, const QRegion& = QRegion(), int raster = 0, bool waveMode = false, bool useGivenColors = false, bool drawText = false, const QColor& bar_color = Qt::cyan, // default to very obvious color to visualize uninitialized drawing const QColor& beat_color = Qt::cyan, const QColor& fine_color = Qt::cyan, const QColor& coarse_color = Qt::cyan, const QColor& text_color = Qt::cyan, const QFont& large_font = QFont(), const QFont& small_font = QFont() ); signals: void timeChanged(unsigned); public slots: void setPos(int, unsigned, bool); void setRaster(int); public: MTScale(int raster, QWidget* parent, int xscale, bool f = false); void setBarLocator(bool f) { barLocator = f; } }; } #endif muse-4.0.0/src/muse/components/mtscale_flo.cpp000066400000000000000000000320361404105311000213760ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mtscale_flo.cpp,v 1.8.2.7 2011/05/19 04:14:01 flo Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "mtscale_flo.h" #include "song.h" #include "app.h" #include "icons.h" #include "gconfig.h" #include "scoreedit.h" #include "globals.h" // Forwards from header: #include #include #include namespace MusEGui { //--------------------------------------------------------- // MTScale // Midi Time Scale //--------------------------------------------------------- MTScaleFlo::MTScaleFlo(ScoreCanvas* parent_editor, QWidget* parent_widget) : View(parent_widget, 1, 1) { pos[0] = MusEGlobal::song->cpos(); pos[1] = MusEGlobal::song->lpos(); pos[2] = MusEGlobal::song->rpos(); xpos=0; xoffset=0; button = Qt::NoButton; setMouseTracking(true); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(setPos(int, unsigned, bool))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::song, SIGNAL(markerChanged(int)), SLOT(redraw())); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); parent=parent_editor; setFixedHeight(28); setBg(MusEGlobal::config.rulerBg); } void MTScaleFlo::configChanged() { setBg(MusEGlobal::config.rulerBg); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MTScaleFlo::songChanged(MusECore::SongChangedStruct_t type) { if (type & (SC_SIG|SC_TEMPO|SC_MARKERS_REBUILT|SC_MARKER_INSERTED|SC_MARKER_REMOVED|SC_MARKER_MODIFIED)) redraw(); } //--------------------------------------------------------- // setPos //--------------------------------------------------------- void MTScaleFlo::setPos(int idx, unsigned val, bool) { if ((val == INT_MAX) || (val == pos[idx])) return; int opos = parent->tick_to_x(pos[idx] == INT_MAX ? val : pos[idx]) + xoffset - xpos; pos[idx] = val; if (isVisible()) { int tval = parent->tick_to_x(val) + xoffset - xpos; int x = -9; int w = 18; if (tval < 0) { // tval<0 occurs whenever the window is scrolled left, so I switched to signed int (ml) redraw(); } else if (opos > tval) { w += opos - tval; x += tval; } else { w += tval - opos; x += opos; } redraw(QRect(x, 0, w, height())); } } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void MTScaleFlo::mousePressEvent(QMouseEvent* event) { button = event->button(); mouseMoveEvent(event); } //--------------------------------------------------------- // mouseReleaseEvent //--------------------------------------------------------- void MTScaleFlo::mouseReleaseEvent(QMouseEvent*) { button = Qt::NoButton; } //--------------------------------------------------------- // mouseMoveEvent //--------------------------------------------------------- void MTScaleFlo::mouseMoveEvent(QMouseEvent* event) { if (event->modifiers() & Qt::ShiftModifier ) setCursor(QCursor(Qt::PointingHandCursor)); else setCursor(QCursor(Qt::ArrowCursor)); int x = event->x(); if (x<0) x=0; int tick = parent->x_to_tick(x-xoffset+xpos); if (tick<0) tick=0; tick = MusEGlobal::sigmap.raster(tick, parent->quant_ticks()); MusECore::Song::POSTYPE posType; switch (button) { case Qt::LeftButton: if (event->modifiers() & Qt::ControlModifier) posType = MusECore::Song::LPOS; else posType = MusECore::Song::CPOS; break; case Qt::MidButton: posType = MusECore::Song::LPOS; break; case Qt::RightButton: posType = MusECore::Song::RPOS; break; default: return; // if no button is pressed the function returns here } MusECore::Pos p(tick, true); if(posType == MusECore::Song::CPOS && (event->modifiers() & Qt::ShiftModifier )) { // If shift +LMB we add a marker const MusECore::iMarker alreadyExists = MusEGlobal::song->getMarkerAt(tick); if (alreadyExists == MusEGlobal::song->marker()->end()) MusEGlobal::song->addMarker(QString(""), tick, false); } else if (posType == MusECore::Song::RPOS && (event->modifiers() & Qt::ShiftModifier )) { // If shift +RMB we remove a marker const MusECore::iMarker toRemove = MusEGlobal::song->getMarkerAt(tick); if (toRemove != MusEGlobal::song->marker()->end()) MusEGlobal::song->removeMarker(toRemove->second); else fprintf(stderr, "No marker to remove\n"); } else MusEGlobal::song->setPos(posType, p); // all other cases: relocating one of the locators } //--------------------------------------------------------- // draw //--------------------------------------------------------- void MTScaleFlo::draw(QPainter& p, const QRect& r, const QRegion&) { int x = r.x(); int w = r.width(); x -= 20; w += 40; // wg. Text //--------------------------------------------------- // draw Marker //--------------------------------------------------- int y = 12; p.setRenderHint(QPainter::Antialiasing); p.setPen(MusEGlobal::config.rulerFg); p.setFont(MusEGlobal::config.fonts[5]); p.drawLine(r.x(), y+1, r.x() + r.width(), y+1); QRect tr(r); tr.setHeight(12); MusECore::MarkerList* marker = MusEGlobal::song->marker(); for (MusECore::iMarker m = marker->begin(); m != marker->end(); ++m) { int xp = parent->tick_to_x(m->second.tick()) + xoffset - xpos; if (xp > x+w) break; int xe = r.x() + r.width(); MusECore::iMarker mm = m; ++mm; if (mm != marker->end()) xe = parent->tick_to_x(mm->first) + xoffset - xpos; QRect tr(xp, 0, xe-xp, 13); QRect wr = r.intersected(tr); if(!wr.isEmpty()) { if (m->second.current()) p.fillRect(wr, MusEGlobal::config.rulerCurrent); int x2; if (mm != marker->end()) x2 = parent->tick_to_x(mm->first) + xoffset - xpos; else x2 = xp+200; if(xp >= -32) { p.setBrush(MusEGlobal::config.markerColor); p.setPen(MusEGlobal::config.markerColor); p.drawPolygon( QVector{ {static_cast(xp), 1.}, {static_cast(xp) + 7., 6.}, {static_cast(xp), 11.} } ); } if(xp >= -1023) { QRect r = QRect(xp+10, 0, x2-xp, 12); p.setPen(MusEGlobal::config.rulerFg); p.drawText(r, Qt::AlignLeft|Qt::AlignVCenter, m->second.name()); } if(xp >= 0) { p.setPen(MusEGlobal::config.markerColor); p.drawLine(xp, y, xp, height()); } } } //--------------------------------------------------- // draw location marker //--------------------------------------------------- int h = height()-12; // for (int i = 0; i < 3; ++i) { // int xp = parent->tick_to_x(pos[i]) + xoffset - xpos; // if (xp >= x && xp < x+w) { // QPixmap* pm = markIcon[i]; // p.drawPixmap(xp - pm->width()/2, y-1, *pm); // } // } const qreal mtop = 18.; const qreal mbottom = 26.; const int radius = 8; // draw left range marker { const int xp = parent->tick_to_x(static_cast(pos[1])) + xoffset - xpos; if (xp >= x && xp < x+w) { // QPixmap* pm = markIcon[i]; // p.drawPixmap(xp - pm->width()/2, y-1, *pm); const int pmx = xp - radius; p.setBrush(MusEGlobal::config.rangeMarkerColor); p.setPen(MusEGlobal::config.rangeMarkerColor); p.drawPolygon( QVector{ {static_cast(mapx(xp)), mtop}, {static_cast(mapx(pmx)), mtop}, {static_cast(mapx(xp)), mbottom} } ); } } // draw right range marker { const int xp = parent->tick_to_x(static_cast(pos[2])) + xoffset - xpos; if (xp >= x && xp < x+w) { const int pmx2 = xp + radius; p.setBrush(MusEGlobal::config.rangeMarkerColor); p.setPen(MusEGlobal::config.rangeMarkerColor); p.drawPolygon( QVector{ {static_cast(mapx(xp)), mtop}, {static_cast(mapx(pmx2)), mtop}, {static_cast(mapx(xp)), mbottom} } ); } } // draw position marker { const int xp = parent->tick_to_x(static_cast(pos[0])) + xoffset - xpos; if (xp >= x && xp < x+w) { const int pmx = xp - radius; const int pmx2 = xp + radius; p.setBrush(MusEGlobal::config.positionMarkerColor); p.setPen(MusEGlobal::config.positionMarkerColor); p.drawPolygon( QVector{ {static_cast(mapx(pmx)), mtop}, {static_cast(mapx(pmx2)), mtop}, {static_cast(mapx(xp)), mbottom} } ); } } //--------------------------------------------------- // draw beats //--------------------------------------------------- p.setPen(MusEGlobal::config.rulerFg); unsigned ctick; int bar1, bar2, beat; unsigned tick; ctick = parent->x_to_tick(x - xoffset + xpos); MusEGlobal::sigmap.tickValues(ctick, &bar1, &beat, &tick); MusEGlobal::sigmap.tickValues(parent->x_to_tick(x+w - xoffset + xpos), &bar2, &beat, &tick); int stick = MusEGlobal::sigmap.bar2tick(bar1, 0, 0); int ntick; for (int bar = bar1; bar <= bar2; bar++, stick = ntick) { ntick = MusEGlobal::sigmap.bar2tick(bar+1, 0, 0); int tpix = parent->delta_tick_to_delta_x(ntick - stick); if (tpix < 64) { // don�t show beats if measure is this small int n = 1; if (tpix < 32) n = 2; if (tpix <= 16) n = 4; if (tpix < 8) n = 8; if (tpix <= 4) n = 16; if (tpix <= 2) n = 32; if (bar % n) continue; p.setFont(MusEGlobal::config.fonts[3]); int x = parent->tick_to_x(stick) + xoffset - xpos; QString s; s.setNum(bar + 1); p.drawLine(x, y+1, x, y+1+h); QRect r = QRect(x+2, y, 1000, h); p.drawText(r, Qt::AlignLeft|Qt::AlignVCenter|Qt::TextDontClip, s); } else { int z, n; MusEGlobal::sigmap.timesig(stick, z, n); for (int beat = 0; beat < z; beat++) { int xp = parent->tick_to_x(MusEGlobal::sigmap.bar2tick(bar, beat, 0)) + xoffset - xpos; QString s; QRect r(xp+2, y, 1000, h); int y1; int num; if (beat == 0) { num = bar + 1; y1 = y + 1; p.setFont(MusEGlobal::config.fonts[3]); } else { num = beat + 1; y1 = y + 7; p.setFont(MusEGlobal::config.fonts[4]); r.setY(y+3); } s.setNum(num); p.drawLine(xp, y1, xp, y+1+h); p.drawText(r, Qt::AlignLeft|Qt::AlignVCenter|Qt::TextDontClip, s); } } } } void MTScaleFlo::set_xpos(int pos) { xpos=pos; redraw(); } void MTScaleFlo::set_xoffset(int o) { xoffset=o; redraw(); } void MTScaleFlo::pos_add_changed() { redraw(); } } // namespace MusEGui muse-4.0.0/src/muse/components/mtscale_flo.h000066400000000000000000000042731404105311000210450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mtscale_flo.h,v 1.3 2011/05/19 22:27:06 flo Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MTSCALE_FLO_H__ #define __MTSCALE_FLO_H__ #include #include #include "type_defs.h" #include "view.h" // Forward declarations: class QWidget; class QPainter; class QMouseEvent; namespace MusEGui { class ScoreCanvas; //--------------------------------------------------------- // MTScaleFlo // scale for midi track //--------------------------------------------------------- class MTScaleFlo : public View { Q_OBJECT unsigned pos[3]; int button; ScoreCanvas* parent; int xpos; int xoffset; private slots: void songChanged(MusECore::SongChangedStruct_t); void configChanged(); protected: virtual void draw(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void mousePressEvent(QMouseEvent* event); virtual void mouseMoveEvent(QMouseEvent* event); virtual void mouseReleaseEvent(QMouseEvent* event); signals: void timeChanged(unsigned); public slots: void setPos(int, unsigned, bool); void set_xpos(int); void pos_add_changed(); void set_xoffset(int); public: MTScaleFlo(ScoreCanvas* parent_editor, QWidget* parent_widget); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/nentry.cpp000066400000000000000000000303611404105311000204240ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: nentry.cpp,v 1.1.1.1.2.1 2008/05/21 00:28:54 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include #include #include #include #include "nentry.h" #include "gconfig.h" #define TIMER1 400 #define TIMER2 200 #define TIMEC 7 #define TIMER3 100 #define TIMEC2 20 #define TIMER4 50 namespace MusEGui { NentryFilter::NentryFilter(QObject* parent) : QObject(parent) { } void Nentry::setText(const QString& s) { edit->setText(s); } //--------------------------------------------------------- // eventFilter //--------------------------------------------------------- bool NentryFilter::eventFilter(QObject*, QEvent* event) { Nentry* e = (Nentry*)parent(); if (event->type() == QEvent::MouseButtonPress) { e->mousePress((QMouseEvent*)event); return true; } if (event->type() == QEvent::MouseMove) { e->mouseMove((QMouseEvent*)event); return true; } if (event->type() == QEvent::MouseButtonDblClick) { e->mouseDoubleClick((QMouseEvent*)event); return true; } if (event->type() == QEvent::MouseButtonRelease) { e->mouseRelease((QMouseEvent*)event); return true; } if (event->type() == QEvent::Wheel) { e->wheel((QWheelEvent*)event); return true; } if (event->type() == QEvent::KeyPress) { return e->keyPress((QKeyEvent*)event); } if (event->type() == QEvent::ContextMenu) { return e->contextMenu((QContextMenuEvent*)event); } return false; } //--------------------------------------------------------- // Nentry // lineedit int values //--------------------------------------------------------- Nentry::Nentry(QWidget* parent, const QString& txt, int _lPos, bool dark) : QFrame(parent) { focusW = 0; lPos = _lPos; edit = new QLineEdit(this); timer = new QTimer(this); filter = new NentryFilter(this); drawFrame = false; edit->installEventFilter(filter); edit->setFrame(drawFrame); connect(timer, SIGNAL(timeout()), SLOT(repeat())); connect(edit, SIGNAL(returnPressed()), SLOT(endEdit())); edit->setCursor(QCursor(Qt::ArrowCursor)); // edit->setFont(font3); val = 0; layout = new QHBoxLayout(this); if (txt == "") { layout->addWidget(edit, 1, Qt::AlignHCenter); } else { label = new QLabel(txt, this); if (lPos == 0) { layout->addStretch(5); layout->addSpacing(5); layout->addWidget(label); layout->addSpacing(5); layout->addWidget(edit); layout->addSpacing(5); layout->addStretch(5); } else { label->setAlignment(Qt::AlignLeft); layout->addWidget(edit, 0, Qt::AlignRight); layout->addSpacing(5); layout->addWidget(label, 100, Qt::AlignRight|Qt::AlignVCenter); } } if (dark) { setDark(); } edit->setFocusPolicy(Qt::NoFocus); } void Nentry::setFocusPolicy(Qt::FocusPolicy policy) { edit->setFocusPolicy(policy); } void Nentry::setDark() { const QPalette& oldpalette = edit->palette(); const QColor& newcolor = oldpalette.color(QPalette::Window); QPalette newpalette(oldpalette); newpalette.setColor(QPalette::Base, newcolor); edit->setPalette(newpalette); } //--------------------------------------------------------- // setSize //--------------------------------------------------------- void Nentry::setSize(int n) { QString s("0000000000000000"); QFontMetrics fm = edit->fontMetrics(); int w; if (n <= 16) // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 w = fm.horizontalAdvance(s, n); #else w = fm.width(s, n); #endif else // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 w = fm.horizontalAdvance('0') * n; #else w = fm.width('0') * n; #endif edit->setFixedWidth(w + 14); } //--------------------------------------------------------- // setFrame //--------------------------------------------------------- void Nentry::setFrame(bool flag) { drawFrame = flag; edit->setFrame(drawFrame); } //--------------------------------------------------------- // endEdit //--------------------------------------------------------- void Nentry::endEdit() { if (edit->isModified()) { if (setSValue(edit->text())) { setString(val, false); return; } edit->setModified(false); } if (focusW) focusW->setFocus(); focusW = 0; edit->clearFocus(); if (!drawFrame) edit->setFrame(false); setString(val, false); } //--------------------------------------------------------- // contextMenuEvent //--------------------------------------------------------- bool Nentry::contextMenu(QContextMenuEvent *e) { e->accept(); return true; } //--------------------------------------------------------- // mousePress //--------------------------------------------------------- void Nentry::mousePress(QMouseEvent* event) { button = event->button(); starty = event->y(); evx = event->x(); if (event->button() == Qt::LeftButton) { focusW = qApp->focusWidget(); edit->setFocus(); edit->setFrame(true); setString(val, true); } else { timecount = 0; repeat(); timer->start(TIMER1); } } //--------------------------------------------------------- // repeat //--------------------------------------------------------- void Nentry::repeat() { if (timecount == 1) { ++timecount; timer->stop(); timer->start(TIMER2); return; } ++timecount; if (timecount == TIMEC) { timer->stop(); timer->start(TIMER3); } if (timecount == TIMEC2) { timer->stop(); timer->start(TIMER4); } switch (button) { case Qt::LeftButton: if (!MusEGlobal::config.leftMouseButtonCanDecrease) return; // else fall through case Qt::MidButton: decValue(evx); break; case Qt::RightButton: incValue(evx); break; default: break; } if (focusW) focusW->setFocus(); edit->clearFocus(); } //--------------------------------------------------------- // mouseRelease //--------------------------------------------------------- void Nentry::mouseRelease(QMouseEvent* event) { button = Qt::NoButton; timer->stop(); if (event->button() != Qt::LeftButton) { if (focusW) focusW->setFocus(); edit->clearFocus(); } } //--------------------------------------------------------- // mouseMove //--------------------------------------------------------- void Nentry::mouseMove(QMouseEvent*) { switch (button) { case Qt::LeftButton: break; case Qt::MidButton: break; case Qt::RightButton: break; default: break; } } //--------------------------------------------------------- // mouseDoubleClick //--------------------------------------------------------- void Nentry::mouseDoubleClick(QMouseEvent* event) { if (event->button() != Qt::LeftButton) { mousePress(event); return; } } //--------------------------------------------------------- // wheel //--------------------------------------------------------- void Nentry::wheel(QWheelEvent* event) { event->accept(); const QPoint pixelDelta = event->pixelDelta(); const QPoint angleDegrees = event->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; if (delta > 0) incValue(delta); else decValue(-delta); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void Nentry::setValue(int v) { if (v == val) return; if (setString(v)) { if (!drawFrame) edit->setFrame(false); edit->setEnabled(false); } else { edit->setEnabled(true); } val = v; } //--------------------------------------------------------- // keyPress //--------------------------------------------------------- bool Nentry::keyPress(QKeyEvent* event) { bool shift = event->modifiers() & Qt::ShiftModifier; bool ctrl = event->modifiers() & Qt::ControlModifier; int key = event->key(); if (shift) { switch(key) { case Qt::Key_Left: case Qt::Key_Right: return false; default: return true; } return true; } if (ctrl) { switch(key) { case Qt::Key_A: case Qt::Key_B: case Qt::Key_C: case Qt::Key_D: case Qt::Key_E: case Qt::Key_F: case Qt::Key_H: case Qt::Key_V: case Qt::Key_X: case Qt::Key_Z: case Qt::Key_Y: return false; default: return true; } return true; } if (event->modifiers()) return true; switch (key) { case Qt::Key_Up: incValue(0); return true; case Qt::Key_Down: decValue(0); return true; case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case Qt::Key_Minus: case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Backspace: case Qt::Key_Home: case Qt::Key_End: case Qt::Key_Delete: case Qt::Key_Return: return false; default: break; } return true; } } // namespace MusEGui muse-4.0.0/src/muse/components/nentry.h000066400000000000000000000057441404105311000201000ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: nentry.h,v 1.1.1.1.2.2 2008/05/21 00:28:54 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __NENTRY_H__ #define __NENTRY_H__ #include #include class QHBoxLayout; class QLabel; class QLineEdit; class QTimer; class QString; class QEvent; class QMouseEvent; class QWheelEvent; class QKeyEvent; class QContextMenuEvent; class QWidget; namespace MusEGui { class NentryFilter : public QObject { Q_OBJECT protected: bool eventFilter(QObject* object, QEvent* event); public: NentryFilter(QObject* parent); }; //--------------------------------------------------------- // Nentry // numerical entry widget with optional label //--------------------------------------------------------- class Nentry : public QFrame { Q_OBJECT int button; int starty; bool drawFrame; QTimer* timer; int evx; int timecount; QHBoxLayout* layout; QObject* filter; QLabel* label; int lPos; // label Position 0 - left, 1 - right QWidget* focusW; protected: QLineEdit* edit; int val; virtual void incValue(int x) = 0; virtual void decValue(int x) = 0; virtual bool setString(int, bool editable = false) = 0; virtual bool setSValue(const QString&) = 0; private slots: void repeat(); protected slots: void endEdit(); public slots: virtual void setValue(int); public: Nentry(QWidget* parent, const QString& txt = QString(""), int lPos = 0, bool dark=false); int value() const { return val; } void setFrame(bool); //void setAlignment(int flag) { edit->setAlignment(flag); } void setText(const QString& s); void setSize(int n); void setDark(); void mousePress(QMouseEvent*); void mouseMove(QMouseEvent*); void mouseDoubleClick(QMouseEvent*); void mouseRelease(QMouseEvent*); void wheel(QWheelEvent*); bool keyPress(QKeyEvent*); void setFocusPolicy(Qt::FocusPolicy); bool contextMenu(QContextMenuEvent*); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/noteinfo.cpp000066400000000000000000000235761404105311000207400ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: noteinfo.cpp,v 1.4.2.1 2008/08/18 00:15:26 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "config.h" #include "noteinfo.h" #include "posedit.h" #include "song.h" #include "globals.h" #include "pitchedit.h" #include "icons.h" #include "pos.h" namespace MusEGui { //--------------------------------------------------- // NoteInfo // ToolBar // Start, Length, Note, Velo on, Velo off //--------------------------------------------------- //NoteInfo::NoteInfo(QMainWindow* parent) NoteInfo::NoteInfo(QWidget* parent) : QToolBar(tr("Note Info"), parent) { setObjectName("Note Info"); _enabled = true; _returnMode = false; deltaMode = false; deltaButton = new QToolButton(this); deltaButton->setFocusPolicy(Qt::NoFocus); deltaButton->setCheckable(true); deltaButton->setIcon(*deltaSVGIcon); deltaButton->setToolTip(tr("Absolute/Delta mode")); deltaButton->setStatusTip(tr("Use Delta mode to edit event parameters relative to their current values (switched on automatically when multiple events are selected).")); addWidget(deltaButton); QLabel* label = new QLabel(tr("Start")); label->setIndent(3); addWidget(label); selTime = new PosEdit; selTime->setFocusPolicy(Qt::StrongFocus); selTime->setObjectName("Start"); addWidget(selTime); label = new QLabel(tr("Len")); label->setIndent(3); addWidget(label); selLen = new SpinBox(); selLen->setFocusPolicy(Qt::StrongFocus); selLen->setRange(0, 100000); selLen->setSingleStep(1); addWidget(selLen); label = new QLabel(tr("Pitch")); label->setIndent(3); addWidget(label); selPitch = new PitchEdit; selPitch->setFocusPolicy(Qt::StrongFocus); selPitch->setDeltaMode(deltaMode); addWidget(selPitch); label = new QLabel(tr("Velo on")); label->setIndent(3); addWidget(label); selVelOn = new SpinBox(); selVelOn->setFocusPolicy(Qt::StrongFocus); // REMOVE Tim. Noteoff. Changed. // selVelOn->setRange(0, 127); selVelOn->setRange(1, 127); selVelOn->setSingleStep(1); addWidget(selVelOn); label = new QLabel(tr("Velo off")); label->setIndent(3); addWidget(label); selVelOff = new SpinBox(); selVelOff->setFocusPolicy(Qt::StrongFocus); selVelOff->setRange(0, 127); selVelOff->setSingleStep(1); addWidget(selVelOff); connect(selLen, SIGNAL(valueChanged(int)), SLOT(lenChanged(int))); connect(selPitch, SIGNAL(valueChanged(int)), SLOT(pitchChanged(int))); connect(selVelOn, SIGNAL(valueChanged(int)), SLOT(velOnChanged(int))); connect(selVelOff, SIGNAL(valueChanged(int)), SLOT(velOffChanged(int))); connect(selTime, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(timeChanged(const MusECore::Pos&))); connect(selLen, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(selPitch, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(selVelOn, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(selVelOff, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(selTime, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(selLen, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(selPitch, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(selVelOn, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(selVelOff, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(selTime, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(deltaButton, SIGNAL(clicked(bool)), SLOT(deltaModeClicked(bool))); } //--------------------------------------------------------- // setEnabled //--------------------------------------------------------- void NoteInfo::setEnabled(bool val) { _enabled = val; selLen->setEnabled(val); selPitch->setEnabled(val); selVelOn->setEnabled(val); selVelOff->setEnabled(val); selTime->setEnabled(val); } //--------------------------------------------------------- // set_mode //--------------------------------------------------------- void NoteInfo::set_mode() { blockSignals(true); selPitch->setDeltaMode(deltaMode); if (deltaMode) { selLen->setRange(-100000, 100000); selVelOn->setRange(-127, 127); selVelOff->setRange(-127, 127); } else { selLen->setRange(0, 100000); // REMOVE Tim. Noteoff. Changed. // selVelOn->setRange(0, 127); selVelOn->setRange(1, 127); selVelOff->setRange(0, 127); } blockSignals(false); } //--------------------------------------------------------- // setReturnMode //--------------------------------------------------------- void NoteInfo::setReturnMode(bool v) { _returnMode = v; selTime->setReturnMode(_returnMode); selLen->setReturnMode(_returnMode); selPitch->setReturnMode(_returnMode); selVelOn->setReturnMode(_returnMode); selVelOff->setReturnMode(_returnMode); } //--------------------------------------------------------- // setDeltaMode //--------------------------------------------------------- void NoteInfo::setDeltaMode(bool val) { if(val == deltaMode) return; deltaMode = val; deltaButton->setChecked(deltaMode); set_mode(); } //--------------------------------------------------------- // lenChanged //--------------------------------------------------------- void NoteInfo::lenChanged(int val) { if (!signalsBlocked()) emit valueChanged(VAL_LEN, val); } //--------------------------------------------------------- // velOnChanged //--------------------------------------------------------- void NoteInfo::velOnChanged(int val) { if (!signalsBlocked()) emit valueChanged(VAL_VELON, val); } //--------------------------------------------------------- // velOffChanged //--------------------------------------------------------- void NoteInfo::velOffChanged(int val) { if (!signalsBlocked()) emit valueChanged(VAL_VELOFF, val); } //--------------------------------------------------------- // pitchChanged //--------------------------------------------------------- void NoteInfo::pitchChanged(int val) { if (!signalsBlocked()) emit valueChanged(VAL_PITCH, val); } //--------------------------------------------------------- // setDeltaMode //--------------------------------------------------------- void NoteInfo::deltaModeClicked(bool val) { if(val == deltaMode) return; deltaMode = val; set_mode(); emit deltaModeChanged(deltaMode); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void NoteInfo::setValue(ValType type, int val) { blockSignals(true); switch(type) { case VAL_TIME: selTime->setValue(val); break; case VAL_LEN: selLen->setValue(val); break; case VAL_VELON: // REMOVE Tim. Noteoff. Added. if(!deltaMode && val == 0) fprintf(stderr, "NoteInfo::setValue: Warning: Value is zero note on velocity!\n"); selVelOn->setValue(val); break; case VAL_VELOFF: selVelOff->setValue(val); break; case VAL_PITCH: selPitch->setValue(val); break; } blockSignals(false); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void NoteInfo::setValues(unsigned tick, int val2, int val3, int val4, int val5) { blockSignals(true); // PosEdit will take care of optimizations. It must check whether actual values dependent on tempo or sig changed... selTime->setValue(tick); if (selLen->value() != val2) selLen->setValue(val2); if (selPitch->value() != val3) selPitch->setValue(val3); if (selVelOn->value() != val4) { // REMOVE Tim. Noteoff. Added. if(!deltaMode && val4 == 0) fprintf(stderr, "NoteInfo::setValues: Warning: Zero note on velocity!\n"); selVelOn->setValue(val4); } if (selVelOff->value() != val5) selVelOff->setValue(val5); blockSignals(false); } //--------------------------------------------------------- // timeChanged //--------------------------------------------------------- void NoteInfo::timeChanged(const MusECore::Pos& pos) { if (!signalsBlocked()) emit valueChanged(VAL_TIME, pos.tick()); } } // namespace MusEGui muse-4.0.0/src/muse/components/noteinfo.h000066400000000000000000000047061404105311000203770ustar00rootroot00000000000000// MusE // Linux Music Editor // $Id: noteinfo.h,v 1.3 2004/01/09 17:12:54 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __NOTE_INFO_H__ #define __NOTE_INFO_H__ #include #include "posedit.h" #include "pitchedit.h" #include "spinbox.h" class QWidget; namespace MusECore { class Pos; } namespace MusEGui { class PosEdit; class PitchEdit; class SpinBox; //--------------------------------------------------------- // NoteInfo //--------------------------------------------------------- class NoteInfo : public QToolBar { Q_OBJECT PosEdit* selTime; SpinBox* selLen; PitchEdit* selPitch; SpinBox* selVelOn; SpinBox* selVelOff; QToolButton* deltaButton; bool _returnMode; bool deltaMode; bool _enabled; void set_mode(); public: enum ValType {VAL_TIME, VAL_LEN, VAL_VELON, VAL_VELOFF, VAL_PITCH }; NoteInfo(QWidget* parent = 0); void setValues(unsigned, int, int, int, int); void setDeltaMode(bool); bool isEnabled() const { return _enabled; } void setReturnMode(bool v); bool returnMode() const { return _returnMode; } private slots: void lenChanged(int); void velOnChanged(int); void velOffChanged(int); void pitchChanged(int); void timeChanged(const MusECore::Pos&); void deltaModeClicked(bool); public slots: void setValue(ValType, int); void setEnabled(bool); signals: void valueChanged(MusEGui::NoteInfo::ValType, int); void returnPressed(); void escapePressed(); void deltaModeChanged(bool); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/partcolortoolbar.cpp000066400000000000000000000056041404105311000224770ustar00rootroot00000000000000#include #include #include "partcolortoolbar.h" #include "icons.h" #include "gconfig.h" #include "config.h" #include "utils.h" namespace MusEGui { PartColorToolbar::PartColorToolbar(QWidget *parent) : QToolBar(tr("Part Colors"), parent) { setObjectName("Part color toolbar"); buttonAction = addAction(tr("Current part color")); // buttonAction->setToolTip(tr("Current part color, used for new parts. Click to set to selected parts.")); if (MusEGlobal::config.useTrackColorForParts) buttonAction->setIcon(*tracktypeSVGIcon); else buttonAction->setIcon(MusECore::colorRect(MusEGlobal::config.partColors[0], 80, 80)); buttonAction->setData(0); colorPopup = new QMenu(this); buildMenu(); buttonAction->setMenu(colorPopup); QToolButton *tb = dynamic_cast(widgetForAction(buttonAction)); if (tb) tb->setPopupMode(QToolButton::MenuButtonPopup); buttonAction->setStatusTip(tr("Current part color, used for new parts. Click button to set color to selected parts. Customize colors and texts in Appearance->Colors.")); connect(colorPopup, &QMenu::triggered, this, &PartColorToolbar::popupActionTriggered); connect(buttonAction, &QAction::triggered, this, [this](){ emit partColorTriggered(buttonAction->data().toInt()); }); } void PartColorToolbar::popupActionTriggered(QAction *a) { buttonAction->setData(a->data()); int idx = a->data().toInt(); if (idx == 0 && MusEGlobal::config.useTrackColorForParts) buttonAction->setIcon(*tracktypeSVGIcon); else buttonAction->setIcon(MusECore::colorRect(MusEGlobal::config.partColors[a->data().toInt()], 80, 80)); emit partColorIndexChanged(idx); } void PartColorToolbar::buildMenu() { colorPopup->clear(); for (int i = 0; i < NUM_PARTCOLORS; ++i) { QAction *act_color = nullptr; if (i == 0 && MusEGlobal::config.useTrackColorForParts) act_color = colorPopup->addAction(*tracktypeSVGIcon, tr("Track Color")); else act_color = colorPopup->addAction(MusECore::colorRect(MusEGlobal::config.partColors[i], 80, 80), MusEGlobal::config.partColorNames[i]); act_color->setData(i); if (i == 0) colorPopup->addSeparator(); } } void PartColorToolbar::configChanged() { buildMenu(); int idx = buttonAction->data().toInt(); if (idx == 0 && MusEGlobal::config.useTrackColorForParts) buttonAction->setIcon(*tracktypeSVGIcon); else buttonAction->setIcon(MusECore::colorRect(MusEGlobal::config.partColors[idx], 80, 80)); } void PartColorToolbar::setCurrentIndex(int idx) { buttonAction->setData(idx); if (idx == 0 && MusEGlobal::config.useTrackColorForParts) buttonAction->setIcon(*tracktypeSVGIcon); else buttonAction->setIcon(MusECore::colorRect(MusEGlobal::config.partColors[idx], 80, 80)); } } // namespace muse-4.0.0/src/muse/components/partcolortoolbar.h000066400000000000000000000010371404105311000221400ustar00rootroot00000000000000#ifndef PARTCOLORTOOLBAR_H #define PARTCOLORTOOLBAR_H #include namespace MusEGui { class PartColorToolbar : public QToolBar { Q_OBJECT QAction* buttonAction; QMenu* colorPopup; void buildMenu(); void popupActionTriggered(QAction *buttonAction); public: PartColorToolbar(QWidget* parent); signals: void partColorTriggered(int); void partColorIndexChanged(int); public slots: void configChanged(); void setCurrentIndex(int idx); }; } // namespace MusEGui #endif // PARTCOLORTOOLBAR_H muse-4.0.0/src/muse/components/pastedialog.cpp000066400000000000000000000104311404105311000213750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pastedialog.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "pastedialog.h" #include "xml.h" #include "gconfig.h" using MusEGlobal::config; namespace MusEGui { int PasteDialog::insert_method = 0; int PasteDialog::number = 1; int PasteDialog::raster = 384; bool PasteDialog::all_in_one_track = 0; bool PasteDialog::clone = 0; PasteDialog::PasteDialog(QWidget* parent) : QDialog(parent) { setupUi(this); button_group = new QButtonGroup; button_group->addButton(merge_button,0); button_group->addButton(move_all_button,1); button_group->addButton(move_some_button,2); raster_spinbox->setSingleStep(config.division); connect(raster_spinbox, SIGNAL(valueChanged(int)), this, SLOT(raster_changed(int))); connect(n_spinbox, SIGNAL(valueChanged(int)), this, SLOT(number_changed(int))); } void PasteDialog::pull_values() { insert_method = button_group->checkedId(); number = n_spinbox->value(); raster = raster_spinbox->value(); all_in_one_track = all_in_one_track_checkbox->isChecked(); clone = clone_checkbox->isChecked(); } void PasteDialog::accept() { pull_values(); QDialog::accept(); } int PasteDialog::exec() { if ((insert_method < 0) || (insert_method > 2)) insert_method=0; button_group->button(insert_method)->setChecked(true); n_spinbox->setValue(number); raster_spinbox->setValue(raster); all_in_one_track_checkbox->setChecked(all_in_one_track); clone_checkbox->setChecked(clone); return QDialog::exec(); } QString PasteDialog::ticks_to_quarter_string(int ticks) { if (ticks % config.division == 0) { return tr("%n quarter(s)", "", ticks/config.division); } else { double quarters = (double) ticks/config.division; bool one = ( quarters > 0.995 && quarters < 1.005 ); if (one) return tr("%1 quarter", "for floating-point arguments like 1.5").arg(quarters, 0, 'f', 2); else return tr("%1 quarters", "for floating-point arguments like 1.5").arg(quarters, 0, 'f', 2); } } void PasteDialog::raster_changed(int r) { raster_quarters->setText(ticks_to_quarter_string(r)); insert_quarters->setText(ticks_to_quarter_string(r*n_spinbox->value())); } void PasteDialog::number_changed(int n) { insert_quarters->setText(ticks_to_quarter_string(n*raster_spinbox->value())); } void PasteDialog::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "insert_method") insert_method=xml.parseInt(); else if (tag == "number") number=xml.parseInt(); else if (tag == "raster") raster=xml.parseInt(); else if (tag == "clone") clone=xml.parseInt(); else if (tag == "all_in_one_track") all_in_one_track=xml.parseInt(); else xml.unknown("PasteDialog"); break; case MusECore::Xml::TagEnd: if (tag == "pastedialog") return; default: break; } } } void PasteDialog::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "pastedialog"); xml.intTag(level, "insert_method", insert_method); xml.intTag(level, "number", number); xml.intTag(level, "raster", raster); xml.intTag(level, "clone", clone); xml.intTag(level, "all_in_one_track", all_in_one_track); xml.tag(level, "/pastedialog"); } } // namespace MusEGui muse-4.0.0/src/muse/components/pastedialog.h000066400000000000000000000034721404105311000210510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pastedialog.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PASTEDIALOG_H__ #define __PASTEDIALOG_H__ #include "ui_pastedialogbase.h" #include #include class QButtonGroup; namespace MusECore { class Xml; } namespace MusEGui { class PasteDialog : public QDialog, public Ui::PasteDialogBase { Q_OBJECT protected: QButtonGroup* button_group; QString ticks_to_quarter_string(int ticks); protected slots: void accept(); void pull_values(); void raster_changed(int); void number_changed(int); public: PasteDialog(QWidget* parent = 0); static int insert_method; static int number; static int raster; static bool all_in_one_track; static bool clone; static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); public slots: int exec(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/pastedialogbase.ui000066400000000000000000000202021404105311000220600ustar00rootroot00000000000000 PasteDialogBase 0 0 307 374 MusE: Paste Parts 6 11 11 11 11 Number and raster Insert times 1 Raster ticks 0 999999 384 384 1 quarter 1 quarter Qt::Horizontal 0 20 Qt::Horizontal 0 20 Move, Merge, Clone false false 11 11 11 11 6 Move everything to the right Move only affected parts to the right Qt::Horizontal 40 20 Put everything into a single track Merge with existing parts true true Insert as clones (where possible) Qt::Vertical 20 40 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel okButton clicked() PasteDialogBase accept() 20 20 20 20 cancelButton clicked() PasteDialogBase reject() 20 20 20 20 muse-4.0.0/src/muse/components/pasteeventsdialog.cpp000066400000000000000000000155561404105311000226370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pasteeventsdialog.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "pasteeventsdialog.h" #include "xml.h" #include "gconfig.h" using MusEGlobal::config; namespace MusEGui { int PasteEventsDialog::number = 1; int PasteEventsDialog::raster = 384; bool PasteEventsDialog::always_new_part = 0; bool PasteEventsDialog::never_new_part = 0; unsigned PasteEventsDialog::max_distance = 3072; bool PasteEventsDialog::into_single_part = 0; bool PasteEventsDialog::ctrl_erase = true; bool PasteEventsDialog::ctrl_erase_wysiwyg = true; bool PasteEventsDialog::ctrl_erase_inclusive = false; PasteEventsDialog::PasteEventsDialog(QWidget* parent) : QDialog(parent) { setupUi(this); raster_spinbox->setSingleStep(config.division); connect(raster_spinbox, SIGNAL(valueChanged(int)), this, SLOT(raster_changed(int))); connect(n_spinbox, SIGNAL(valueChanged(int)), this, SLOT(number_changed(int))); connect(max_distance_spinbox, SIGNAL(valueChanged(int)), this, SLOT(max_distance_changed(int))); connect(ctrl_erase_button, SIGNAL(toggled(bool)), this, SLOT(ctrl_erase_changed())); into_single_part_allowed=true; } void PasteEventsDialog::pull_values() { into_single_part = all_into_selected_part_checkbox->isChecked(); always_new_part = always_new_button->isChecked(); never_new_part = never_new_button->isChecked(); if(no_ctrl_erase_button->isChecked()) { ctrl_erase = ctrl_erase_wysiwyg = ctrl_erase_inclusive = false; } else if(ctrl_erase_button->isChecked()) { ctrl_erase = true; ctrl_erase_wysiwyg = ctrl_erase_wysiwyg_button->isChecked(); ctrl_erase_inclusive = ctrl_erase_inclusive_button->isChecked(); } int temp = max_distance_spinbox->value(); if (temp < 0) max_distance = 0; else max_distance = unsigned(temp); number = n_spinbox->value(); raster = raster_spinbox->value(); } void PasteEventsDialog::accept() { pull_values(); QDialog::accept(); } int PasteEventsDialog::exec() { all_into_selected_part_checkbox->setChecked(into_single_part && into_single_part_allowed); all_into_selected_part_checkbox->setEnabled(into_single_part_allowed); into_single_part_allowed=true; if (always_new_part) always_new_button->setChecked(true); else if (never_new_part) never_new_button->setChecked(true); else sometimes_into_new_button->setChecked(true); max_distance_spinbox->setValue(max_distance); n_spinbox->setValue(number); raster_spinbox->setValue(raster); ctrl_erase_button->blockSignals(true); no_ctrl_erase_button->blockSignals(true); ctrl_erase_inclusive_button->blockSignals(true); ctrl_erase_wysiwyg_button->blockSignals(true); if(ctrl_erase) ctrl_erase_button->setChecked(true); else no_ctrl_erase_button->setChecked(true); ctrl_erase_inclusive_button->setChecked(ctrl_erase_inclusive); ctrl_erase_wysiwyg_button->setChecked(ctrl_erase_wysiwyg); ctrl_erase_button->blockSignals(false); no_ctrl_erase_button->blockSignals(false); ctrl_erase_inclusive_button->blockSignals(false); ctrl_erase_wysiwyg_button->blockSignals(false); const bool en = ctrl_erase_button->isChecked(); ctrl_erase_wysiwyg_button->setEnabled(en); ctrl_erase_inclusive_button->setEnabled(en); return QDialog::exec(); } QString PasteEventsDialog::ticks_to_quarter_string(int ticks) { if (ticks % config.division == 0) { return tr("%n quarter(s)", "", ticks/config.division); } else { double quarters = (double) ticks/config.division; bool one = ( quarters > 0.995 && quarters < 1.005 ); if (one) return tr("%1 quarter", "for floating-point arguments like 1.5").arg(quarters, 0, 'f', 2); else return tr("%1 quarters", "for floating-point arguments like 1.5").arg(quarters, 0, 'f', 2); } } void PasteEventsDialog::max_distance_changed(int d) { max_distance_quarters->setText(ticks_to_quarter_string(d)); } void PasteEventsDialog::raster_changed(int r) { raster_quarters->setText(ticks_to_quarter_string(r)); insert_quarters->setText(ticks_to_quarter_string(r*n_spinbox->value())); } void PasteEventsDialog::number_changed(int n) { insert_quarters->setText(ticks_to_quarter_string(n*raster_spinbox->value())); } void PasteEventsDialog::ctrl_erase_changed() { const bool en = ctrl_erase_button->isChecked(); ctrl_erase_wysiwyg_button->setEnabled(en); ctrl_erase_inclusive_button->setEnabled(en); } void PasteEventsDialog::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "number") number=xml.parseInt(); else if (tag == "raster") raster=xml.parseInt(); else if (tag == "always_new_part") always_new_part=xml.parseInt(); else if (tag == "never_new_part") never_new_part=xml.parseInt(); else if (tag == "max_distance") max_distance=xml.parseInt(); else if (tag == "into_single_part") into_single_part=xml.parseInt(); else if (tag == "ctrl_erase") ctrl_erase=xml.parseInt(); else if (tag == "ctrl_erase_wysiwyg") ctrl_erase_wysiwyg=xml.parseInt(); else if (tag == "ctrl_erase_inclusive") ctrl_erase_inclusive=xml.parseInt(); else xml.unknown("PasteEventsDialog"); break; case MusECore::Xml::TagEnd: if (tag == "pasteeventsdialog") return; default: break; } } } void PasteEventsDialog::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "pasteeventsdialog"); xml.intTag(level, "number", number); xml.intTag(level, "raster", raster); xml.intTag(level, "always_new_part", always_new_part); xml.intTag(level, "never_new_part", never_new_part); xml.intTag(level, "max_distance", max_distance); xml.intTag(level, "into_single_part", into_single_part); xml.intTag(level, "ctrl_erase", ctrl_erase); xml.intTag(level, "ctrl_erase_wysiwyg", ctrl_erase_wysiwyg); xml.intTag(level, "ctrl_erase_inclusive", ctrl_erase_inclusive); xml.tag(level, "/pasteeventsdialog"); } } // namespace MusEGui muse-4.0.0/src/muse/components/pasteeventsdialog.h000066400000000000000000000040351404105311000222720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pasteeventsdialog.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PASTEEVENTSDIALOG_H__ #define __PASTEEVENTSDIALOG_H__ #include "ui_pasteeventsdialogbase.h" #include #include namespace MusECore { class Xml; } namespace MusEGui { class PasteEventsDialog : public QDialog, public Ui::PasteEventsDialogBase { Q_OBJECT protected: QString ticks_to_quarter_string(int ticks); protected slots: void accept(); void pull_values(); void max_distance_changed(int); void raster_changed(int); void number_changed(int); void ctrl_erase_changed(); public: PasteEventsDialog(QWidget* parent = 0); static int number; static int raster; static bool always_new_part; static bool never_new_part; static bool ctrl_erase; static bool ctrl_erase_wysiwyg; static bool ctrl_erase_inclusive; static unsigned max_distance; static bool into_single_part; bool into_single_part_allowed; static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); public slots: int exec(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/pasteeventsdialogbase.ui000066400000000000000000000316061404105311000233170ustar00rootroot00000000000000 PasteEventsDialogBase 0 0 315 614 MusE: Paste Events 6 11 6 11 6 Number and raster Insert times 1 Raster ticks 0 999999 384 384 1 quarter 1 quarter Qt::Horizontal 0 20 Qt::Horizontal 0 20 Paste options false false &Always into existing parts true false &Never into existing parts 0 &Into existing parts if part has not to be expanded by more than true Qt::Horizontal QSizePolicy::Fixed 20 20 true ticks 999999 384 3072 Qt::Horizontal 40 20 true 1 quarter Put everything into the (selected) part Paste midi controller target options 2 2 Keep e&xisting :/svg/paste_ctrl.svg:/svg/paste_ctrl.svg 114 38 0 0 Erase existin&g :/svg/paste_ctrl_erase.svg:/svg/paste_ctrl_erase.svg 114 38 true Erase WYSIWYG :/svg/paste_ctrl_erase_wysiwyg.svg:/svg/paste_ctrl_erase_wysiwyg.svg 114 38 true Erase inclusive :/svg/paste_ctrl_erase_inclusive.svg:/svg/paste_ctrl_erase_inclusive.svg 114 38 Qt::Vertical 20 40 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel okButton clicked() PasteEventsDialogBase accept() 20 20 20 20 cancelButton clicked() PasteEventsDialogBase reject() 20 20 20 20 sometimes_into_new_button toggled(bool) max_distance_quarters setEnabled(bool) 161 201 260 231 sometimes_into_new_button toggled(bool) max_distance_spinbox setEnabled(bool) 161 201 107 231 muse-4.0.0/src/muse/components/pitchedit.cpp000066400000000000000000000054551404105311000210700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pitchedit.cpp,v 1.2 2004/01/09 17:12:54 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "pitchedit.h" #include "helper.h" namespace MusEGui { //--------------------------------------------------------- // PitchEdit //--------------------------------------------------------- PitchEdit::PitchEdit(QWidget* parent) : SpinBox(parent) { setMinimum(0); setMaximum(127); setSingleStep(1); deltaMode = false; } //--------------------------------------------------------- // mapValueToText //--------------------------------------------------------- QString PitchEdit::textFromValue(int v) const { if (deltaMode) { QString s; s.setNum(v); return s; } else return MusECore::pitch2string(v); } QValidator::State PitchEdit::validate(QString &input, int &) const { if (input.isEmpty()) return QValidator::Intermediate; return MusECore::validatePitch(input); } //--------------------------------------------------------- // mapTextToValue //--------------------------------------------------------- int PitchEdit::valueFromText(const QString &s) const { // printf("PitchEdit: valueFromText: not impl.\n"); // //if (text) // //*text = false; // return 0; if (deltaMode) return s.toInt(); else return MusECore::string2pitch(s); } //--------------------------------------------------------- // setDeltaMode //--------------------------------------------------------- void PitchEdit::setDeltaMode(bool val) { if(deltaMode == val) return; deltaMode = val; if (deltaMode) setRange(-127, 127); else setRange(0, 127); } void PitchEdit::midiNote(int pitch, int velo) { if (hasFocus() && velo) setValue(pitch); } } // namespace MusEGui muse-4.0.0/src/muse/components/pitchedit.h000066400000000000000000000032221404105311000205230ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pitchedit.h,v 1.2 2004/01/09 17:12:54 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PITCHEDIT_H__ #define __PITCHEDIT_H__ #include "spinbox.h" namespace MusEGui { //--------------------------------------------------------- // PitchEdit //--------------------------------------------------------- class PitchEdit : public SpinBox { Q_OBJECT bool deltaMode; protected: QString textFromValue(int v) const override; int valueFromText(const QString &s) const override; QValidator::State validate(QString &input, int &pos) const override; public: PitchEdit(QWidget* parent=0); void setDeltaMode(bool); public slots: void midiNote(int pitch, int velo); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/pitchlabel.cpp000066400000000000000000000074161404105311000212210ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pitchlabel.cpp,v 1.2 2004/05/16 16:55:01 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "pitchedit.h" #include "pitchlabel.h" #include "helper.h" namespace MusEGui { //--------------------------------------------------------- // PitchLabel //--------------------------------------------------------- PitchLabel::PitchLabel(QWidget* parent, const char* name) : QLabel(parent) { setObjectName(name); _pitchMode = true; _value = -1; setFrameStyle(WinPanel | Sunken); setLineWidth(2); setMidLineWidth(3); setValue(0); //int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this); // ddskrjo 0 int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); setIndent(fw); //setContentsMargins(0,0,0,0); } //--------------------------------------------------------- // setPitchMode //--------------------------------------------------------- void PitchLabel::setPitchMode(bool val) { _pitchMode = val; } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize PitchLabel::sizeHint() const { QFontMetrics fm(font()); //int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this); // ddskrjo 0 int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); int h = fm.height() + fw * 2; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 // int w = 2 + fm.horizontalAdvance(QString("A#8")) + fw * 4; int w = fm.horizontalAdvance(QString("-9999")) + fw * 2; // must display 14Bit controller values #else // int w = 2 + fm.width(QString("A#8")) + fw * 4; int w = fm.width(QString("-9999")) + fw * 2; // must display 14Bit controller values #endif return QSize(w, h).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void PitchLabel::setValue(int val) { if (val == _value) return; _value = val; QString s; if (_pitchMode) s = MusECore::pitch2string(_value); else s = QString::number(_value); setText(s); } //--------------------------------------------------------- // setInt //--------------------------------------------------------- void PitchLabel::setInt(int val) { if (_pitchMode) setPitchMode(false); setValue(val); } //--------------------------------------------------------- // setPitch //--------------------------------------------------------- void PitchLabel::setPitch(int val) { if (!_pitchMode) { setPitchMode(true); } setValue(val); } } // namespace MusEGui muse-4.0.0/src/muse/components/pitchlabel.h000066400000000000000000000032621404105311000206610ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pitchlabel.h,v 1.1.1.1 2003/10/27 18:54:49 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PITCHLABEL_H__ #define __PITCHLABEL_H__ #include namespace MusEGui { //--------------------------------------------------------- // PitchLabel //--------------------------------------------------------- class PitchLabel : public QLabel { Q_OBJECT bool _pitchMode; int _value; protected: QSize sizeHint() const; public slots: void setValue(int); void setInt(int); void setPitch(int); public: PitchLabel(QWidget* parent, const char* name = 0); int value() const { return _value; } void setPitchMode(bool val); bool pitchMode() const { return _pitchMode; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/plugindialog.cpp000066400000000000000000000420421404105311000215620ustar00rootroot00000000000000#include #include #include "globaldefs.h" #include "popupmenu.h" #include "menutitleitem.h" #include "plugindialog.h" //#include "ui_plugindialogbase.h" #include "plugin.h" #include "gconfig.h" namespace MusEGui { int PluginDialog::selectedPlugType = SEL_TYPE_ALL; int PluginDialog::selectedPlugPortType = SEL_PORT_ALL; int PluginDialog::selectedGroup = 0; QStringList PluginDialog::sortItems = QStringList(); QRect PluginDialog::geometrySave = QRect(); QByteArray PluginDialog::listSave = QByteArray(); //--------------------------------------------------------- // PluginItem //--------------------------------------------------------- PluginItem::PluginItem( bool hasUri, QTreeWidget* parent ) : QTreeWidgetItem(parent), _hasUri(hasUri) { } //--------------------------------------------------------- // PluginDialog // select Plugin dialog //--------------------------------------------------------- PluginDialog::PluginDialog(QWidget* parent) : QDialog(parent) { ui.setupUi(this); // this dlg is called from the mixer strip so it would inherit the small font size setStyleSheet("* {font-size:" + QString::number(MusEGlobal::config.fonts[0].pointSize()) + "pt}"); group_info=nullptr; setWindowTitle(tr("MusE: Select Plugin")); if(!geometrySave.isNull()) setGeometry(geometrySave); //QVBoxLayout* layout = new QVBoxLayout(this); //tabBar = new QTabBar(this); ui.tabBar->setToolTip(tr("Plugin categories.\nRight-click on tabs to manage.\nRight-click on plugins to add/remove from a category.")); ui.tabBar->addTab("All"); for (QList::iterator it=MusEGlobal::plugin_group_names.begin(); it!=MusEGlobal::plugin_group_names.end(); it++) ui.tabBar->addTab(*it); //pList = new QTreeWidget(this); ui.pList->setColumnCount(12); // "Note: In order to avoid performance issues, it is recommended that sorting // is enabled after inserting the items into the tree. Alternatively, you could // also insert the items into a list before inserting the items into the tree. " QStringList headerLabels; headerLabels << tr("Type"); headerLabels << tr("Lib"); headerLabels << tr("Label"); headerLabels << tr("Name"); headerLabels << tr("AI"); headerLabels << tr("AO"); headerLabels << tr("CI"); headerLabels << tr("CO"); headerLabels << tr("IP"); headerLabels << tr("FB"); headerLabels << tr("2B"); headerLabels << tr("id"); headerLabels << tr("Maker"); headerLabels << tr("Copyright"); ui.pList->setHeaderLabels(headerLabels); ui.pList->headerItem()->setToolTip(4, tr("Audio inputs")); ui.pList->headerItem()->setToolTip(5, tr("Audio outputs")); ui.pList->headerItem()->setToolTip(6, tr("Control inputs")); ui.pList->headerItem()->setToolTip(7, tr("Control outputs")); ui.pList->headerItem()->setToolTip(8, tr("In-place capable")); ui.pList->headerItem()->setToolTip(9, tr("Requires fixed block size")); ui.pList->headerItem()->setToolTip(10, tr("Requires power-of-2 block size")); ui.pList->headerItem()->setToolTip(11, tr("ID number")); ui.pList->setRootIsDecorated(false); ui.pList->setSelectionBehavior(QAbstractItemView::SelectRows); ui.pList->setSelectionMode(QAbstractItemView::SingleSelection); ui.pList->setAlternatingRowColors(true); ui.pList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); ui.pList->setContextMenuPolicy(Qt::CustomContextMenu); ui.okB->setDefault(true); ui.okB->setFixedWidth(80); ui.okB->setEnabled(false); ui.cancelB->setFixedWidth(80); switch(selectedPlugPortType) { case SEL_PORT_SM: ui.onlySM->setChecked(true); break; case SEL_PORT_S: ui.onlyS->setChecked(true); break; case SEL_PORT_M: ui.onlyM->setChecked(true); break; case SEL_PORT_ALL: ui.allPlug->setChecked(true); break; } ui.tabBar->setCurrentIndex(selectedGroup); ui.tabBar->setContextMenuPolicy(Qt::ActionsContextMenu); newGroupAction= new QAction(tr("&Create new group"),ui.tabBar); delGroupAction= new QAction(tr("&Delete currently selected group"),ui.tabBar); renGroupAction= new QAction(tr("Re&name currently selected group"),ui.tabBar); ui.tabBar->addAction(newGroupAction); ui.tabBar->addAction(delGroupAction); ui.tabBar->addAction(renGroupAction); if (selectedGroup==0) { delGroupAction->setEnabled(false); renGroupAction->setEnabled(false); } //ui.tabBar->setMovable(true); //not yet. need to find a way to forbid moving the zeroth tab ui.pluginfilterGroup->setToolTip(tr("Select which types of plugins should be visible in the list.
" "Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.
" "Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack.")); ui.pluginType->addItem("All", SEL_TYPE_ALL); ui.pluginType->addItem("DSSI", SEL_TYPE_DSSI); ui.pluginType->addItem("LADSPA", SEL_TYPE_LADSPA); ui.pluginType->addItem("LV2", SEL_TYPE_LV2); ui.pluginType->addItem("VST", SEL_TYPE_VST); // ui.pluginType->addItem("Wine VST", SEL_TYPE_WINE_VST); connect (ui.pluginType,SIGNAL(currentIndexChanged(int)), SLOT(filterType(int))); for (int i=0; i < ui.pluginType->count(); i++) { if (selectedPlugType == ui.pluginType->itemData(i).toInt()) { ui.pluginType->setCurrentIndex(i); //printf("set current index to %d\n",i); break; } } ui.sortBox->addItems(sortItems); fillPlugs(); ui.pList->setSortingEnabled(true); if(listSave.isEmpty()) { int sizes[] = { 80, 110, 110, 110, 30, 30, 30, 30, 30, 30, 30, 50, 110, 110 }; for (int i = 0; i < 12; ++i) { if (sizes[i] <= 50) // hack alert! ui.pList->header()->setSectionResizeMode(i, QHeaderView::Fixed); ui.pList->header()->resizeSection(i, sizes[i]); } ui.pList->sortByColumn(3, Qt::AscendingOrder); } else ui.pList->header()->restoreState(listSave); connect(ui.pList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(accept())); connect(ui.pList, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SLOT(enableOkB())); connect(ui.pList, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(plistContextMenu(const QPoint&))); connect(ui.cancelB, SIGNAL(clicked()), SLOT(reject())); connect(ui.okB, SIGNAL(clicked()), SLOT(accept())); connect(ui.portFilterGroup, SIGNAL(buttonClicked(QAbstractButton*)), SLOT(pluginTypeSelectionChanged(QAbstractButton*))); connect(ui.tabBar, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); //connect(tabBar, SIGNAL(tabMoved(int,int)), SLOT(tabMoved(int,int))); //not yet. need to find a way to forbid moving the zeroth tab connect(ui.sortBox, SIGNAL(editTextChanged(const QString&)),SLOT(fillPlugs())); connect(newGroupAction, SIGNAL(triggered()), SLOT(newGroup())); connect(delGroupAction, SIGNAL(triggered()), SLOT(delGroup())); connect(renGroupAction, SIGNAL(triggered()), SLOT(renameGroup())); ui.sortBox->setFocus(); } void PluginDialog::filterType(int i) { selectedPlugType = ui.pluginType->itemData(i).toInt(); fillPlugs(); } void PluginDialog::plistContextMenu(const QPoint& point) { QTreeWidgetItem* item = ui.pList->currentItem(); if (item) { group_info = &MusEGlobal::plugin_groups.get(item->text(1), item->text(2)); QMenu* menu = new MusEGui::PopupMenu(this, true); menu->addAction(new MusEGui::MenuTitleItem(tr("Associated categories"), menu)); if (ui.tabBar->count()==1) { QAction* tmp=menu->addAction(tr("You need to define some categories first.")); tmp->setEnabled(false); } else { for (int i=1; icount(); i++) // ignore the first tab ("All") { QAction* act=menu->addAction(ui.tabBar->tabText(i)); act->setCheckable(true); act->setChecked(group_info->contains(i)); connect(act, &QAction::toggled, [this, i]() { groupMenuEntryToggled(i); } ); } } menu->exec(mapToGlobal(point)); delete menu; if (selectedGroup!=0 && !group_info->contains(selectedGroup)) // we removed the entry from the currently visible group fillPlugs(); group_info=nullptr; } } void PluginDialog::groupMenuEntryToggled(int index) { if (group_info) { if (group_info->contains(index)) group_info->remove(index); else group_info->insert(index); } else { fprintf(stderr,"THIS SHOULD NEVER HAPPEN: groupMenuEntryToggled called but group_info is NULL!\n"); } } //--------------------------------------------------------- // enableOkB //--------------------------------------------------------- void PluginDialog::enableOkB() { ui.okB->setEnabled(true); } void PluginDialog::newGroup() { MusEGlobal::plugin_groups.shift_right(selectedGroup+1, ui.tabBar->count()); ui.tabBar->insertTab(selectedGroup+1, tr("New group")); MusEGlobal::plugin_group_names.insert(selectedGroup, tr("New group")); } void PluginDialog::delGroup() { if (selectedGroup!=0) { MusEGlobal::plugin_groups.erase(selectedGroup); MusEGlobal::plugin_groups.shift_left(selectedGroup+1, ui.tabBar->count()); ui.tabBar->removeTab(selectedGroup); MusEGlobal::plugin_group_names.removeAt(selectedGroup-1); } } void PluginDialog::renameGroup() { if (selectedGroup!=0) { bool ok; QString newname = QInputDialog::getText(this, tr("Enter the new group name"), tr("Enter the new group name"), QLineEdit::Normal, ui.tabBar->tabText(selectedGroup), &ok); if (ok) { ui.tabBar->setTabText(selectedGroup, newname); MusEGlobal::plugin_group_names.replace(selectedGroup-1, newname); } } } //--------------------------------------------------------- // value //--------------------------------------------------------- MusECore::Plugin* PluginDialog::value() { PluginItem* item = static_cast(ui.pList->currentItem()); if (item) { return MusEGlobal::plugins.find( !item->hasUri() ? item->text(1) : QString(), item->hasUri() ? item->text(1) : QString(), item->text(2)); } printf("plugin not found\n"); return nullptr; } //--------------------------------------------------------- // saveSettings //--------------------------------------------------------- void PluginDialog::saveSettings() { if (!ui.sortBox->currentText().isEmpty()) { bool found = false; foreach (QString item, sortItems) if(item == ui.sortBox->currentText()) { found = true; break; } if(!found) sortItems.push_front(ui.sortBox->currentText()); } QHeaderView* hdr = ui.pList->header(); if(hdr) listSave = hdr->saveState(); geometrySave = geometry(); } //--------------------------------------------------------- // accept //--------------------------------------------------------- void PluginDialog::accept() { saveSettings(); QDialog::accept(); } //--------------------------------------------------------- // reject //--------------------------------------------------------- void PluginDialog::reject() { saveSettings(); QDialog::reject(); } //--------------------------------------------------------- // pluginTypeSelectionChanged //--------------------------------------------------------- void PluginDialog::pluginTypeSelectionChanged(QAbstractButton* ab) { if (ab == ui.allPlug) selectedPlugPortType = SEL_PORT_ALL; else if (ab == ui.onlyM) selectedPlugPortType = SEL_PORT_M; else if (ab == ui.onlyS) selectedPlugPortType = SEL_PORT_S; else if (ab == ui.onlySM) selectedPlugPortType = SEL_PORT_SM; fillPlugs(); } void PluginDialog::tabChanged(int index) { renGroupAction->setEnabled(index!=0); delGroupAction->setEnabled(index!=0); selectedGroup=index; fillPlugs(); } void PluginDialog::tabMoved(int from, int to) { //all the below doesn't work :/ /* static bool recurse=false; if (!recurse) { if (from==0 && to!=0) {recurse=true; tabBar->moveTab(to, from);} if (from!=0 && to==0) {recurse=true; tabBar->moveTab(from, to);} } recurse=false;*/ //if ((from==0 && to!=0) || (from!=0 && to==0)) { tabBar->setMovable(false); tabBar->setMovable(true); } printf("**** %i -> %i\n", from, to); //FINDMICH TODO } void PluginDialog::fillPlugs() { QString type_name; ui.pList->clear(); ui.okB->setEnabled(false); for (MusECore::iPlugin i = MusEGlobal::plugins.begin(); i != MusEGlobal::plugins.end(); ++i) if (selectedGroup==0 || MusEGlobal::plugin_groups.get(*i).contains(selectedGroup)) { unsigned long ai = (*i)->inports(); unsigned long ao = (*i)->outports(); unsigned long ci = (*i)->controlInPorts(); unsigned long co = (*i)->controlOutPorts(); bool found = false; QString sb_txt = ui.sortBox->currentText().toLower(); if(sb_txt.isEmpty() || (*i)->label().toLower().contains(sb_txt) || (*i)->name().toLower().contains(sb_txt)) found = true; bool addFlag = false; switch (selectedPlugPortType) { case SEL_PORT_SM: // stereo & mono if ((ai == 1 || ai == 2) && (ao == 1 || ao ==2)) { addFlag = true; } break; case SEL_PORT_S: // stereo if ((ai == 1 || ai == 2) && ao ==2) { addFlag = true; } break; case SEL_PORT_M: // mono if (ai == 1 && ao == 1) { addFlag = true; } break; case SEL_PORT_ALL: // all addFlag = true; break; } if (found && addFlag) { int plugInstanceType; if((*i)->isDssiSynth() || (*i)->isDssiPlugin()) { if ((*i)->lib() == "dssi-vst") { type_name = tr("Wine VST"); plugInstanceType = SEL_TYPE_WINE_VST; } else { if ((*i)->isDssiSynth()) type_name = tr("dssi synth"); else type_name = tr("dssi effect"); plugInstanceType = SEL_TYPE_DSSI; } } else if((*i)->isLV2Synth()) { type_name = tr("LV2 synth"); plugInstanceType = SEL_TYPE_LV2; } else if((*i)->isLV2Plugin()) { type_name = tr("LV2 effect"); plugInstanceType = SEL_TYPE_LV2; } else if((*i)->isVstNativeSynth()) { type_name = tr("VST synth"); plugInstanceType = SEL_TYPE_VST; } else if((*i)->isVstNativePlugin()) { type_name = tr("VST effect"); plugInstanceType = SEL_TYPE_VST; } else { type_name = tr("ladspa"); plugInstanceType = SEL_TYPE_LADSPA; } // last check if ( selectedPlugType != SEL_TYPE_ALL && plugInstanceType != selectedPlugType ) { continue; } PluginItem* item = new PluginItem(!(*i)->uri().isEmpty()); item->setText(0, type_name); if(!(*i)->uri().isEmpty()) item->setText(1, (*i)->uri()); else item->setText(1, (*i)->lib()); item->setText(2, (*i)->label()); item->setText(3, (*i)->name()); item->setText(4, QString().setNum(ai)); item->setText(5, QString().setNum(ao)); item->setText(6, QString().setNum(ci)); item->setText(7, QString().setNum(co)); if( !((*i)->requiredFeatures() & MusECore::PluginNoInPlaceProcessing) ) item->setText(8, "*"); if( (*i)->requiredFeatures() & MusECore::PluginFixedBlockSize ) item->setText(9, "*"); if( (*i)->requiredFeatures() & MusECore::PluginPowerOf2BlockSize ) item->setText(10, "*"); item->setText(11, QString().setNum((*i)->id())); item->setText(12, (*i)->maker()); item->setText(13, (*i)->copyright()); ui.pList->addTopLevelItem(item); } } } //--------------------------------------------------------- // getPlugin //--------------------------------------------------------- MusECore::Plugin* PluginDialog::getPlugin(QWidget* parent) { PluginDialog* dialog = new PluginDialog(parent); MusECore::Plugin* p = nullptr; int rv = dialog->exec(); if(rv) p = dialog->value(); delete dialog; return p; } } muse-4.0.0/src/muse/components/plugindialog.h000066400000000000000000000042561404105311000212340ustar00rootroot00000000000000#ifndef PLUGINDIALOG_H #define PLUGINDIALOG_H #include "ui_plugindialogbase.h" #include #include #include #include "globaldefs.h" #include "globals.h" namespace MusECore { class Plugin; } namespace MusEGui { //--------------------------------------------------------- // PluginItem //--------------------------------------------------------- class PluginItem : public QTreeWidgetItem { bool _hasUri; public: PluginItem(bool hasUri, QTreeWidget* parent = nullptr); bool hasUri() const { return _hasUri; } }; class PluginDialog : public QDialog { Q_OBJECT enum SelectedPluginPortTypes { SEL_PORT_SM, SEL_PORT_S, SEL_PORT_M, SEL_PORT_ALL }; enum SelectedPluginTypes { SEL_TYPE_DSSI, SEL_TYPE_LADSPA, SEL_TYPE_LV2, SEL_TYPE_VST, SEL_TYPE_WINE_VST, SEL_TYPE_ALL}; public: explicit PluginDialog(QWidget* parent=0); static MusECore::Plugin* getPlugin(QWidget* parent); MusECore::Plugin* value(); public slots: void accept(); void reject(); private slots: void enableOkB(); void pluginTypeSelectionChanged(QAbstractButton* ab); void tabChanged(int); void tabMoved(int,int); void fillPlugs(); void filterType(int); void newGroup(); void delGroup(); void renameGroup(); void plistContextMenu(const QPoint&); void groupMenuEntryToggled(int i); private: // QComboBox* sortBox; // QTabBar* tabBar; // QTreeWidget* pList; // QRadioButton* allPlug; // QRadioButton* onlyM; // QRadioButton* onlyS; // QRadioButton* onlySM; // QPushButton *okB; QAction* newGroupAction; QAction* delGroupAction; QAction* renGroupAction; void saveSettings(); static int selectedPlugPortType; static int selectedPlugType; static int selectedGroup; // 0 means "show all" static QStringList sortItems; static QRect geometrySave; static QByteArray listSave; QSet* group_info; //holds the group-set of the plugin which shall be affected by the plistContextMenu. Ui::PluginDialogBase ui; }; } #endif // PLUGINDIALOGBASE_H muse-4.0.0/src/muse/components/plugindialogbase.ui000066400000000000000000000176171404105311000222620ustar00rootroot00000000000000 PluginDialogBase 0 0 636 411 Dialog 3 3 3 3 3 10 0 0 0 1 16777215 100 Ports: 0 0 0 0 0 0 0 8 Mono portFilterGroup 8 Mono + Stereo portFilterGroup 1 8 Stereo portFilterGroup 8 All portFilterGroup 16777215 14 Plugin type: Qt::Vertical QSizePolicy::Minimum 20 10 16777215 14 Filter: true true Qt::Vertical QSizePolicy::Minimum 20 10 &OK &Cancel QTabBar QWidget
qtabbar.h
1
muse-4.0.0/src/muse/components/pluginsettings.cpp000066400000000000000000000066631404105311000221740ustar00rootroot00000000000000#include "pluginsettings.h" #include "ui_pluginsettings.h" #include "song.h" #include "icons.h" #include "app.h" #include "globals.h" namespace MusEGui { PluginSettings::PluginSettings(MusECore::PluginIBase *plugin, bool globalScaleRevert, QWidget *parent) : QDialog(parent), ui(new Ui::PluginSettings) { ui->setupUi(this); ui->pbInfo->setIcon(*infoSVGIcon); ui->cbFixedSpeed->setChecked(plugin->quirks()._fixedSpeed); ui->cbFixedSpeed->setEnabled(plugin->usesTransportSource()); ui->cbTransportAffectsLatency->setChecked(plugin->quirks()._transportAffectsAudioLatency); ui->cbTransportAffectsLatency->setEnabled(plugin->usesTransportSource()); ui->cbOverrideLatency->setChecked(plugin->quirks()._overrideReportedLatency); ui->sbOverrideLatency->setValue(plugin->quirks()._latencyOverrideValue); ui->sbOverrideLatency->setEnabled(plugin->cquirks()._overrideReportedLatency); ui->labelRevertScalingGlobal->setText(QString(tr("Global setting: ") + (globalScaleRevert ? tr("On") : tr("Off")))); if (plugin->quirks().getFixNativeUIScaling() == MusECore::PluginQuirks::GLOBAL) ui->rbRevertScalingFollowGlobal->setChecked(true); else if (plugin->quirks().getFixNativeUIScaling() == MusECore::PluginQuirks::ON) ui->rbRevertScalingOn->setChecked(true); else ui->rbRevertScalingOff->setChecked(true); settings = &plugin->quirks(); } PluginSettings::~PluginSettings() { delete ui; } void PluginSettings::on_buttonBox_accepted() { bool routeChanged(false); if (ui->cbFixedSpeed->isChecked() != settings->_fixedSpeed) { settings->_fixedSpeed = ui->cbFixedSpeed->isChecked(); routeChanged = true; } if (ui->cbTransportAffectsLatency->isChecked() != settings->_transportAffectsAudioLatency) { settings->_transportAffectsAudioLatency = ui->cbTransportAffectsLatency->isChecked(); routeChanged = true; } if (ui->cbOverrideLatency->isChecked() != settings->_overrideReportedLatency) { settings->_overrideReportedLatency = ui->cbOverrideLatency->isChecked(); if (!ui->cbOverrideLatency->isChecked()) settings->_latencyOverrideValue = 0; routeChanged = true; } else if (ui->cbOverrideLatency->isChecked() && ui->sbOverrideLatency->value() != settings->_latencyOverrideValue) { settings->_latencyOverrideValue = ui->sbOverrideLatency->value(); routeChanged = true; } else if (!ui->cbOverrideLatency->isChecked() && settings->_latencyOverrideValue != 0) { settings->_latencyOverrideValue = 0; routeChanged = true; } if (routeChanged) MusEGlobal::song->update(SC_ROUTE); MusECore::PluginQuirks::NatUISCaling scaleMode; if (ui->rbRevertScalingFollowGlobal->isChecked()) scaleMode = MusECore::PluginQuirks::GLOBAL; else if (ui->rbRevertScalingOn->isChecked()) scaleMode = MusECore::PluginQuirks::ON; else scaleMode = MusECore::PluginQuirks::OFF; if (scaleMode != settings->getFixNativeUIScaling()) settings->setFixNativeUIScaling(scaleMode); } void PluginSettings::on_buttonBox_rejected() { // close(); } void PluginSettings::on_cbOverrideLatency_toggled(bool checked) { ui->sbOverrideLatency->setEnabled(checked); } void PluginSettings::on_pbInfo_clicked() { QString s("https://github.com/muse-sequencer/muse/wiki/HiDPI"); MusEGlobal::muse->launchBrowser(s); } } // namespace muse-4.0.0/src/muse/components/pluginsettings.h000066400000000000000000000011641404105311000216300ustar00rootroot00000000000000#ifndef PLUGINSETTINGS_H #define PLUGINSETTINGS_H #include #include "plugin.h" namespace Ui { class PluginSettings; } namespace MusEGui { class PluginSettings : public QDialog { Q_OBJECT public: explicit PluginSettings(MusECore::PluginIBase *plugin, bool globalScaleRevert, QWidget *parent = nullptr); ~PluginSettings(); private slots: void on_buttonBox_accepted(); void on_buttonBox_rejected(); void on_cbOverrideLatency_toggled(bool checked); void on_pbInfo_clicked(); private: Ui::PluginSettings *ui; MusECore::PluginQuirks *settings; }; } #endif // PLUGINSETTINGS_H muse-4.0.0/src/muse/components/pluginsettings.ui000066400000000000000000000134031404105311000220150ustar00rootroot00000000000000 PluginSettings 0 0 400 329 Dialog Options Fixed speed Transport affects audio latency Override reported audio latency Qt::Horizontal 40 20 Reported audio latency override value 8191 0 Qt::Vertical 20 40 Revert HiDPI scaling of native GUI 1 0 true Set in Global settings->GUI tweaks Global setting Additional information Follow global setting buttonGroup On buttonGroup Off buttonGroup Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok groupBox_2 groupBox buttonBox buttonBox accepted() PluginSettings accept() 248 254 157 274 buttonBox rejected() PluginSettings reject() 316 260 286 274 muse-4.0.0/src/muse/components/popupmenu.cpp000066400000000000000000000470371404105311000211450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: popupmenu.cpp,v 1.1.1.1 2010/07/18 03:21:00 terminator356 Exp $ // // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) // // PopupMenu sub-class of QMenu created by Tim. // (C) Copyright 2010-2015 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; 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 #include #include "popupmenu.h" #include "gconfig.h" #include "route.h" // Forwards from header: #include #include #include #include #include // For debugging output: Uncomment the fprintf section. #define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { //====================== // PopupMenu //====================== PopupMenu::PopupMenu(bool stayOpen) : _stayOpen(stayOpen) { init(); } PopupMenu::PopupMenu(QWidget* parent, bool stayOpen) : QMenu(parent), _stayOpen(stayOpen) { init(); } PopupMenu::PopupMenu(const QString& title, QWidget* parent, bool stayOpen) : QMenu(title, parent), _stayOpen(stayOpen) { init(); } void PopupMenu::init() { _contextMenu = 0; _lastHoveredAction = 0; _highlightedAction = 0; // Menus will trigger! Set to make sure our trigger handlers ignore menus. menuAction()->setData(-1); _cur_menu = this; _cur_menu_count = 1; _max_items_in_breakup = 0; moveDelta = 0; timer = 0; connect(this, SIGNAL(hovered(QAction*)), SLOT(popHovered(QAction*))); if(MusEGlobal::config.scrollableSubMenus) { setStyleSheet("QMenu { menu-scrollable: 1; }"); return; } #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL timer = new QTimer(this); timer->setInterval(100); timer->setSingleShot(false); connect(timer, SIGNAL(timeout()), SLOT(timerHandler())); #endif // POPUP_MENU_DISABLE_AUTO_SCROLL } PopupMenu::~PopupMenu() { if(_contextMenu) delete _contextMenu; _contextMenu = 0; } void PopupMenu::clearAllChecks() const { QList list = actions(); for(int i = 0; i < list.size(); ++i) { QAction* act = list[i]; if(PopupMenu* menu = qobject_cast(act->menu())) menu->clearAllChecks(); // Recursive. if(act->isCheckable()) { act->blockSignals(true); act->setChecked(false); act->blockSignals(false); } } } QAction* PopupMenu::findActionFromData(const QVariant& v) const { QList list = actions(); for(int i = 0; i < list.size(); ++i) { QAction* act = list[i]; if(PopupMenu* menu = qobject_cast(act->menu())) { if(QAction* actm = menu->findActionFromData(v)) // Recursive. return actm; } // "Operator == Compares this QVariant with v and returns true if they are equal, // otherwise returns false. In the case of custom types, their equalness operators // are not called. Instead the values' addresses are compared." // // Take care of struct Route first. Insert other future custom structures here too ! if(act->data().canConvert() && v.canConvert()) { if(act->data().value() == v.value()) return act; } else if(act->data() == v) return act; } return 0; } bool PopupMenu::event(QEvent* event) { DEBUG_PRST_ROUTES(stderr, "PopupMenu::event:%p activePopupWidget:%p this:%p class:%s event type:%d\n", event, QApplication::activePopupWidget(), this, metaObject()->className(), event->type()); switch(event->type()) { #ifndef POPUP_MENU_DISABLE_STAY_OPEN case QEvent::MouseButtonDblClick: { if(_stayOpen) { QMouseEvent* e = static_cast(event); if(e->modifiers() == Qt::NoModifier) { event->accept(); // Convert into a return press, which selects the item and closes the menu. // Note that with double click, it's a press followed by release followed by double click. // That would toggle our item twice eg on->off->on, which is hopefully OK. QKeyEvent ke(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier); //ke.ignore(); // Pass it on return QMenu::event(&ke); } } } break; case QEvent::KeyPress: { QKeyEvent* e = static_cast(event); switch(e->key()) { case Qt::Key_Space: if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this)) break; // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case Qt::Key_Select: case Qt::Key_Return: case Qt::Key_Enter: { if(QAction* act = activeAction()) { const bool stay_open = _stayOpen && (MusEGlobal::config.popupsDefaultStayOpen || (e->modifiers() & Qt::ControlModifier)); // Stay open? Or does the action have a submenu, but also a checkbox of its own? if(stay_open || (act->isEnabled() && act->menu() && act->isCheckable())) { act->trigger(); // Trigger the action. event->accept(); if(!stay_open) closeUp(); return true; // We handled it. } // Otherwise let ancestor QMenu handle it... } } break; default: break; } } break; #endif // POPUP_MENU_DISABLE_STAY_OPEN #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL case QEvent::MouseMove: { if(!MusEGlobal::config.scrollableSubMenus) { QMouseEvent* e = static_cast(event); QPoint globPos = e->globalPos(); int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. if(x() < 0 && globPos.x() <= 0) // If on the very first pixel (or beyond) { moveDelta = 32; if(!timer->isActive()) timer->start(); event->accept(); return true; } else if(x() + width() >= dw && globPos.x() >= (dw -1)) // If on the very last pixel (or beyond) { moveDelta = -32; if(!timer->isActive()) timer->start(); event->accept(); return true; } if(timer->isActive()) timer->stop(); } } break; #endif // POPUP_MENU_DISABLE_AUTO_SCROLL default: break; } return QMenu::event(event); } void PopupMenu::closeUp() { if(isVisible()) { DEBUG_PRST_ROUTES(stderr, "PopupMenu::closeUp() this:%p closing\n", this); close(); } QAction* act = menuAction(); if(act) { DEBUG_PRST_ROUTES(stderr, "PopupMenu::closeUp() this:%p menuAction:%p\n", this, act); const int sz = act->associatedWidgets().size(); for(int i = 0; i < sz; ++i) { DEBUG_PRST_ROUTES(stderr, " associated widget#:%d\n", i); if(PopupMenu* pup = qobject_cast(act->associatedWidgets().at(i))) { DEBUG_PRST_ROUTES(stderr, " associated popup:%p\n", pup); DEBUG_PRST_ROUTES(stderr, " closing...\n"); pup->closeUp(); } } } } // For auto-breakup of a too-wide menu. PopupMenu* PopupMenu::cloneMenu(const QString& title, QWidget* parent, bool stayOpen, bool showTooltips) { PopupMenu* m = new PopupMenu(title, parent, stayOpen); m->setToolTipsVisible(showTooltips); return m; } #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL void PopupMenu::timerHandler() { if(!isVisible()) { timer->stop(); return; } int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. int nx = x() + moveDelta; if(moveDelta < 0 && nx + width() < dw) { timer->stop(); nx = dw - width(); } else if(moveDelta > 0 && nx > 0) { timer->stop(); nx = 0; } move(nx, y()); } #endif // POPUP_MENU_DISABLE_AUTO_SCROLL void PopupMenu::popHovered(QAction* action) { DEBUG_PRST_ROUTES(stderr, "PopupMenu::popHovered action text:%s\n", action->text().toLatin1().constData()); _lastHoveredAction = action; hideContextMenu(); #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL if(action && !MusEGlobal::config.scrollableSubMenus) { int dw = QApplication::desktop()->width(); // We want the whole thing if multiple monitors. QRect r = actionGeometry(action); if(x() + r.x() < 0) move(-r.x(), y()); else if(r.x() + r.width() + x() > dw) move(dw - r.x() - r.width(), y()); } #endif // POPUP_MENU_DISABLE_AUTO_SCROLL } void PopupMenu::mousePressEvent(QMouseEvent* e) { DEBUG_PRST_ROUTES(stderr, "PopupMenu::mousePressEvent this:%p\n", this); if (_contextMenu && _contextMenu->isVisible()) _contextMenu->hide(); e->ignore(); QMenu::mousePressEvent(e); } void PopupMenu::mouseReleaseEvent(QMouseEvent *e) { DEBUG_PRST_ROUTES(stderr, "PopupMenu::mouseReleaseEvent this:%p\n", this); if(_contextMenu && _contextMenu->isVisible()) return; // Removed by Tim. Why not stay-open scrollable menus? // if(MusEGlobal::config.scrollableSubMenus) // { // QMenu::mouseReleaseEvent(e); // return; // } QAction* action = actionAt(e->pos()); if (!(action && action == activeAction() && !action->isSeparator() && action->isEnabled())) action=NULL; #ifdef POPUP_MENU_DISABLE_STAY_OPEN if (action && action->menu() != NULL && action->isCheckable()) action->activate(QAction::Trigger); QMenu::mouseReleaseEvent(e); if (action && action->menu() != NULL && action->isCheckable()) close(); return; #else // Check for Ctrl to stay open. const bool stay_open = _stayOpen && (MusEGlobal::config.popupsDefaultStayOpen || (e->modifiers() & Qt::ControlModifier)); // Stay open? Or does the action have a submenu, but also a checkbox of its own? if(action && (stay_open || (action->isEnabled() && action->menu() && action->isCheckable()))) { DEBUG_PRST_ROUTES(stderr, "PopupMenu::mouseReleaseEvent: stay open\n"); action->trigger(); // Trigger the action. e->accept(); if(!stay_open) closeUp(); return; // We handled it. } // Otherwise let ancestor QMenu handle it... e->ignore(); QMenu::mouseReleaseEvent(e); #endif // POPUP_MENU_DISABLE_STAY_OPEN } //----------------------------------------- // getMenu // Auto-breakup a too-wide menu. // NOTE This is a necessary catch-all because X doesn't like too-wide menus, but risky because // some callers might depend on all the items being in one menu (using ::actions or ::addActions). // So try to avoid that. The overwhelming rule of thumb is that too-wide menus are bad design anyway. //------------------------------------------ PopupMenu* PopupMenu::getMenu(const QString& parentText) { if(!_cur_menu) return 0; // We want the whole thing if multiple monitors. // Reasonable to assume if X can show this desktop, it can show a menu with the same width? int dh = QApplication::desktop()->height(); // [danvd] Due to slow actionGeometry method in qt5 QMenu, only limit PopupMenu to 2 columns without width checking... // [Tim] It seems that sizeHint() is only slightly more costly, use it instead. //int _act_height = _cur_menu->actionGeometry(_actions [0]).height(); int mh = _cur_menu->sizeHint().height(); // If we're still only at one column, not much we can do - some item(s) must have had reeeeally long text. // Not to worry. Hopefully the auto-scroll will handle it! // Use columnCount() + 2 to catch well BEFORE it widens beyond the edge, and leave room for many // TESTED: Not only does the system not appear to like too-wide menus, but also the column count was observed // rolling over to zero repeatedly after it reached 15, simply when adding actions! The action width was 52 // the number of items when it first rolled over was around 480 = 884, well below my desktop width of 1366. // Apparently there is a limit on the number of columns - whatever, it made the col count limit necessary: //if((_cur_col_count > 1 && ((_cur_col_count + 2) * _cur_item_width) >= dw) || _cur_col_count >= 8) if((mh + 100) >= dh) { // This menu is too wide. So make a new one... QString s; if(parentText.isEmpty()) s = (tr(" %1").arg(_cur_menu_count)); else { // Try to avoid unnecessary influence on this menu's width from a long-named child item. // FIXME: What width to use? More items may be added later, so we might not be able use the current width... // But by fortunate coincidence, all these items are placed at the end of the menu, // so taking some current width should be OK. // But how to get the current width of just the text area? For now, set at say, 100? //QFontMetrics fm = fontMetrics(); //s = (QString("<%1>").arg(fm.elidedText(parentText, Qt::ElideMiddle, 100))); // // Let's do the elide based on number of characters rather than a pixel width, // so that it is consistent at different fonts. Say, 20 characters? s = QString("%1 ...").arg(parentText.left(20)); } _cur_menu = cloneMenu(s, this, _stayOpen, toolTipsVisible()); // Make the parent text stand out with bold. QFont fnt = _cur_menu->font(); fnt.setBold(true); _cur_menu->menuAction()->setFont(fnt); ++_cur_menu_count; QMenu::addMenu(_cur_menu); } return _cur_menu; } //---------------------------------------------------- // Need to catch these to auto-breakup a too-big menu... //---------------------------------------------------- QAction* PopupMenu::addAction(const QString& text) { if(MusEGlobal::config.scrollableSubMenus) { return QMenu::addAction(text); } QAction* act = static_cast(getMenu(text))->addAction(text); return act; } QAction* PopupMenu::addAction(const QIcon& icon, const QString& text) { if(MusEGlobal::config.scrollableSubMenus) { return QMenu::addAction(icon, text); } QAction* act = static_cast(getMenu(text))->addAction(icon, text); return act; } QAction* PopupMenu::addAction(const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut) { if(MusEGlobal::config.scrollableSubMenus) { return QMenu::addAction(text, receiver, member, shortcut); } QAction* act = static_cast(getMenu(text))->addAction(text, receiver, member, shortcut); return act; } QAction* PopupMenu::addAction(const QIcon& icon, const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut) { if(MusEGlobal::config.scrollableSubMenus) { return QMenu::addAction(icon, text, receiver, member, shortcut); } QAction* act = static_cast(getMenu(text))->addAction(icon, text, receiver, member, shortcut); return act; } void PopupMenu::addAction(QAction* action) { if(MusEGlobal::config.scrollableSubMenus) { return QMenu::addAction(action); } static_cast(getMenu(action->text()))->addAction(action); } QAction* PopupMenu::addMenu(QMenu* menu) { if(MusEGlobal::config.scrollableSubMenus) { return QMenu::addMenu(menu); } return static_cast(getMenu(menu->title()))->addMenu(menu); } QMenu* PopupMenu::addMenu(const QString &title) { if(MusEGlobal::config.scrollableSubMenus) { return QMenu::addMenu(title); } return static_cast(getMenu(title))->addMenu(title); } QMenu* PopupMenu::addMenu(const QIcon &icon, const QString &title) { if(MusEGlobal::config.scrollableSubMenus) { return QMenu::addMenu(icon, title); } return static_cast(getMenu(title))->addMenu(icon, title); } //---------------- // Context menu //---------------- static void PopupMenuSetActionData(QMenu *context_menu, PopupMenu* menu, QAction* menuAction) { const QListactions = context_menu->actions(); for(int i = 0; i < actions.count(); i++) { QVariant e = actions[i]->data(); // If it's already a PopupMenuContextData, just update the values. if(e.canConvert()) actions[i]->setData(QVariant::fromValue(PopupMenuContextData(menu, menuAction, e.value().varValue()))); // Otherwise bring in the ORIGINAL supplied variant data. else actions[i]->setData(QVariant::fromValue(PopupMenuContextData(menu, menuAction, e))); } } QMenu* PopupMenu::contextMenu() { if(!_contextMenu) _contextMenu = new QMenu(this); return _contextMenu; } void PopupMenu::hideContextMenu() { if(!_contextMenu || !_contextMenu->isVisible()) return; _contextMenu->hide(); } void PopupMenu::showContextMenu(const QPoint &pos) { _highlightedAction = activeAction(); if(!_highlightedAction) { PopupMenuSetActionData(_contextMenu, 0, 0); return; } emit aboutToShowContextMenu(this, _highlightedAction, _contextMenu); PopupMenuSetActionData(_contextMenu, this, _highlightedAction); if(QMenu* subMenu = _highlightedAction->menu()) QTimer::singleShot(100, subMenu, SLOT(hide())); _contextMenu->popup(mapToGlobal(pos)); } PopupMenu* PopupMenu::contextMenuFocus() { return qobject_cast(QApplication::activePopupWidget()); } QAction* PopupMenu::contextMenuFocusAction() { if(PopupMenu* menu = qobject_cast(QApplication::activePopupWidget())) { if(!menu->_lastHoveredAction) return 0; QVariant var = menu->_lastHoveredAction->data(); PopupMenuContextData ctx = var.value(); Q_ASSERT(ctx.menu() == menu); return ctx.action(); } return 0; } void PopupMenu::contextMenuEvent(QContextMenuEvent* e) { if(_contextMenu) { if(e->reason() == QContextMenuEvent::Mouse) showContextMenu(e->pos()); else if(activeAction()) showContextMenu(actionGeometry(activeAction()).center()); e->accept(); return; } QMenu::contextMenuEvent(e); } void PopupMenu::hideEvent(QHideEvent *e) { if(_contextMenu && _contextMenu->isVisible()) { // "we need to block signals here when the ctxMenu is showing // to prevent the QPopupMenu::activated(int) signal from emitting // when hiding with a context menu, the user doesn't expect the // menu to actually do anything. // since hideEvent gets called very late in the process of hiding // (deep within QWidget::hide) the activated(int) signal is the // last signal to be emitted, even after things like aboutToHide() // AJS" bool blocked = blockSignals(true); _contextMenu->hide(); blockSignals(blocked); } QMenu::hideEvent(e); } } // namespace MusEGui muse-4.0.0/src/muse/components/popupmenu.h000066400000000000000000000115721404105311000206050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: popupmenu.h,v 1.1.1.1 2010/07/18 03:18:00 terminator356 Exp $ // // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) // // PopupMenu sub-class of QMenu created by Tim. // (C) Copyright 2010-2015 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __POPUPMENU_H__ #define __POPUPMENU_H__ // Just in case Qt ever adds these features natively, we would need to turn our features off! //#define POPUP_MENU_DISABLE_STAY_OPEN //#define POPUP_MENU_DISABLE_AUTO_SCROLL #include #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL #include #endif #include #include #include // Forward declarations: class QWidget; class QMouseEvent; class QContextMenuEvent; class QHideEvent; class QEvent; namespace MusEGui { /** offers a QMenu-like menu, which stays open once the user * clicked a checkable action. */ class PopupMenu : public QMenu { Q_OBJECT bool _stayOpen; #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL QTimer* timer; #endif int moveDelta; PopupMenu* _cur_menu; // For auto-breakup. int _cur_menu_count; int _max_items_in_breakup; QMenu* _contextMenu; QAction* _lastHoveredAction; QPointer _highlightedAction; void init(); void showContextMenu(const QPoint&); // Auto-breakup a too-wide menu. // If a new menu is created, parentText will be used as the parent item's text. PopupMenu* getMenu(const QString& parentText); private slots: void popHovered(QAction*); #ifndef POPUP_MENU_DISABLE_AUTO_SCROLL void timerHandler(); #endif protected: virtual void mouseReleaseEvent(QMouseEvent*); virtual void mousePressEvent(QMouseEvent*); virtual void contextMenuEvent(QContextMenuEvent*); virtual void hideEvent(QHideEvent*); virtual bool event(QEvent*); virtual void closeUp(); // For auto-breakup of a too-wide menu. Virtual. virtual PopupMenu* cloneMenu(const QString& title, QWidget* parent = 0, bool stayOpen = false, bool showTooltips = false); public: signals: void aboutToShowContextMenu(PopupMenu* menu, QAction* menuAction, QMenu* ctxMenu); public: PopupMenu(bool stayOpen); PopupMenu(QWidget* parent=0, bool stayOpen = false); PopupMenu(const QString& title, QWidget* parent = 0, bool stayOpen = false); ~PopupMenu(); QAction* findActionFromData(const QVariant&) const; bool stayOpen() const { return _stayOpen; } void clearAllChecks() const; QMenu* contextMenu(); void hideContextMenu(); static PopupMenu* contextMenuFocus(); static QAction* contextMenuFocusAction(); // Need to catch these to auto-breakup a too-big menu. QAction* addAction(const QString& text); QAction* addAction(const QIcon& icon, const QString& text); QAction* addAction(const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0); QAction* addAction(const QIcon& icon, const QString& text, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0); void addAction(QAction* action); QAction* addMenu(QMenu* menu); QMenu* addMenu(const QString &title); QMenu* addMenu(const QIcon &icon, const QString &title); }; // A handy structure for use with PopupMenu context menu action data. // The variant holds the ORIGINAL data as set by the programmer. class PopupMenuContextData { private: PopupMenu* _menu; QAction* _action; QVariant _variant; public: PopupMenuContextData() : _menu(0), _action(0), _variant(0) { } PopupMenuContextData(const PopupMenuContextData& o) : _menu(o._menu), _action(o._action), _variant(o._variant) { } PopupMenuContextData(PopupMenu* menu, QAction* action, QVariant var) : _menu(menu), _action(action), _variant(var) { } inline PopupMenu* menu() const { return _menu; } inline QAction* action() const { return _action; } inline QVariant varValue() const { return _variant; } }; } // namespace MusEGui Q_DECLARE_METATYPE(MusEGui::PopupMenuContextData) #endif muse-4.0.0/src/muse/components/posedit.cpp000066400000000000000000000604311404105311000205550ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "sync.h" #include "posedit.h" #include "sig.h" #include #include #include #include #include #include namespace MusEGui { using MusEGlobal::sigmap; //--------------------------------------------------------- // PosEdit //--------------------------------------------------------- PosEdit::PosEdit(QWidget* parent) : QAbstractSpinBox(parent) { _returnMode = false; cur_minute = cur_sec = cur_frame = cur_subframe = 0; cur_bar = cur_beat = cur_tick = 0; validator = new QIntValidator(this); initialized = false; setReadOnly(false); setSmpte(false); } QSize PosEdit::sizeHint() const { if(const QStyle* st = style()) { st = st->proxy(); QStyleOptionSpinBox option; option.initFrom(this); option.rect = rect(); option.state = QStyle::State_Active | QStyle::State_Enabled; const QRect b_rect = st->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxUp); QFontMetrics fm = fontMetrics(); const int fw = st->pixelMetric(QStyle::PM_SpinBoxFrameWidth); int h = fm.height() + fw * 2; int w = fw * 2 + b_rect.width() + 2; // adding 2 pixels so the string is not cut off if (_smpte) // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 w += fm.horizontalAdvance(QString("000:00:00:00")); #else w += fm.width(QString("000:00:00:00")); #endif else // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 w += fm.horizontalAdvance(QString("0000.00.000")); #else w += fm.width(QString("0000.00.000")); #endif return QSize(w, h).expandedTo(QApplication::globalStrut()); } return QSize(20, 20).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // event // filter Tab and Backtab key events //--------------------------------------------------------- bool PosEdit::event(QEvent* event) { if (event->type() == QEvent::KeyPress) { QKeyEvent* ke = static_cast(event); if (ke->key() == Qt::Key_Return) { //printf("key press event Return\n"); //enterPressed(); bool changed = finishEdit(); if(changed || _returnMode) // Force valueChanged if return mode set, even if not modified. { emit valueChanged(_pos); } emit returnPressed(); emit editingFinished(); return true; } if (ke->key() == Qt::Key_Escape) { //printf("key press event Escape\n"); if(lineEdit()) lineEdit()->undo(); // "By default, isAccepted() is set to true, but don't rely on this as subclasses may // choose to clear it in their constructor." // Just to be sure. Otherwise escape will close a midi editor for example, which is annoying. ke->setAccepted(true); emit escapePressed(); return true; } int segment = curSegment(); if (ke->key() == Qt::Key_Backtab) { if (_smpte) { if (segment == 3) { lineEdit()->setSelection(7, 2); return true; } else if (segment == 2) { lineEdit()->setSelection(4, 2); return true; } else if (segment == 1) { lineEdit()->setSelection(0, 3); return true; } } else { if (segment == 2) { lineEdit()->setSelection(5, 2); return true; } if (segment == 1) { lineEdit()->setSelection(0, 4); return true; } } } if (ke->key() == Qt::Key_Tab) { if (_smpte) { if (segment == 0) { lineEdit()->setSelection(4, 2); return true; } else if (segment == 1) { lineEdit()->setSelection(7, 2); return true; } else if (segment == 2) { lineEdit()->setSelection(10, 2); return true; } } else { if (segment == 0) { lineEdit()->setSelection(5, 2); return true; } if (segment == 1) { lineEdit()->setSelection(8, 3); return true; } } } } else if (event->type() == QEvent::FocusIn) { QFocusEvent* fe = static_cast(event); QAbstractSpinBox::focusInEvent(fe); int segment = curSegment(); switch(segment) { case 0: lineEdit()->setSelection(0,4); break; case 1: lineEdit()->setSelection(5,2); break; case 2: lineEdit()->setSelection(8,3); break; } return true; } else if (event->type() == QEvent::FocusOut) { QFocusEvent* fe = static_cast(event); QAbstractSpinBox::focusOutEvent(fe); if(finishEdit()) emit valueChanged(_pos); emit lostFocus(); emit editingFinished(); return true; } return QAbstractSpinBox::event(event); } //--------------------------------------------------------- // setSmpte //--------------------------------------------------------- void PosEdit::setSmpte(bool f) { _smpte = f; if (_smpte) lineEdit()->setInputMask("999:99:99:99;0"); else lineEdit()->setInputMask("9999.99.999;0"); updateValue(); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void PosEdit::setValue(const MusECore::Pos& time) { if(_pos == time) { // Must check whether actual values dependent on tempo or sig changed... if (_smpte) { int minute, sec, frame, subframe; time.msf(nullptr, &minute, &sec, &frame, &subframe); if(minute != cur_minute || sec != cur_sec || frame != cur_frame || subframe != cur_subframe) updateValue(); } else { int bar, beat, tick; time.mbt(&bar, &beat, &tick); if(bar != cur_bar || beat != cur_beat || tick != cur_tick) updateValue(); } } else { _pos = time; updateValue(); } } void PosEdit::setValue(const QString& s) { MusECore::Pos time(s); setValue(time); } void PosEdit::setValue(int t) { MusECore::Pos time(t); setValue(time); } //--------------------------------------------------------- // updateValue //--------------------------------------------------------- void PosEdit::updateValue() { QString s; if (_smpte) { _pos.msf(nullptr, &cur_minute, &cur_sec, &cur_frame, &cur_subframe); s = QString("%1:%2:%3:%4") .arg(cur_minute, 3, 10, QLatin1Char('0')) .arg(cur_sec, 2, 10, QLatin1Char('0')) .arg(cur_frame, 2, 10, QLatin1Char('0')) .arg(cur_subframe, 2, 10, QLatin1Char('0')); } else { _pos.mbt(&cur_bar, &cur_beat, &cur_tick); s = QString("%1.%2.%3") .arg(cur_bar + 1, 4, 10, QLatin1Char('0')) .arg(cur_beat + 1, 2, 10, QLatin1Char('0')) .arg(cur_tick, 3, 10, QLatin1Char('0')); } lineEdit()->setText(s); } //--------------------------------------------------------- // stepEnables //--------------------------------------------------------- QAbstractSpinBox::StepEnabled PosEdit::stepEnabled() const { int segment = curSegment(); QAbstractSpinBox::StepEnabled en = QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled; if (_smpte) { int minute, sec, frame, subframe; _pos.msf(nullptr, &minute, &sec, &frame, &subframe); switch(segment) { case 0: if (minute == 0) en &= ~QAbstractSpinBox::StepDownEnabled; break; case 1: if (sec == 0) en &= ~QAbstractSpinBox::StepDownEnabled; else if (sec == 59) en &= ~QAbstractSpinBox::StepUpEnabled; break; case 2: if (frame == 0) en &= ~QAbstractSpinBox::StepDownEnabled; else { int nf = 23; // 24 frames sec switch(MusEGlobal::mtcType) { //case 0: // 24 frames sec // nf = 23; // break; case 1: nf = 24; // 25 frames sec break; case 2: // 30 drop frame case 3: // 30 non drop frame nf = 29; break; default: break; } //if (frame == 23) if (frame >= nf) en &= ~QAbstractSpinBox::StepUpEnabled; } break; case 3: if (subframe == 0) en &= ~QAbstractSpinBox::StepDownEnabled; else if (subframe == 99) en &= ~QAbstractSpinBox::StepUpEnabled; break; } } else { int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(_pos.tick(), &bar, &beat, &tick); unsigned tb = MusEGlobal::sigmap.ticksBeat(_pos.tick()); unsigned tm = MusEGlobal::sigmap.ticksMeasure(_pos.tick()); int bm = tm / tb; switch (segment) { case 0: if (bar == 0) en &= ~QAbstractSpinBox::StepDownEnabled; break; case 1: if (beat == 0) en &= ~QAbstractSpinBox::StepDownEnabled; else { if (beat >= (bm-1)) en &= ~QAbstractSpinBox::StepUpEnabled; } break; case 2: if (tick == 0) en &= ~QAbstractSpinBox::StepDownEnabled; else { if (tick >= (tb-1)) en &= ~QAbstractSpinBox::StepUpEnabled; } break; } } return en; } //--------------------------------------------------------- // fixup //--------------------------------------------------------- void PosEdit::fixup(QString& /*input*/) const { //printf("fixup <%s>\n", input.toLatin1().constData()); } //--------------------------------------------------------- // validate //--------------------------------------------------------- QValidator::State PosEdit::validate(QString& s,int& /*i*/) const { //printf("validate string:%s int:%d\n", s.toLatin1().data(), i); //printf("validate string:%s\n", s.toLatin1().data()); QStringList sl = s.split(_smpte ? ':' : '.'); QValidator::State state; QValidator::State rv = QValidator::Acceptable; // "By default, the pos parameter is not used by this [QIntValidator] validator." int dpos = 0; if (_smpte) { if(sl.size() != 4) { printf("validate smpte string:%s sections:%d != 4\n", s.toLatin1().data(), sl.size()); return QValidator::Invalid; } validator->setRange(0, 999); state = validator->validate(sl[0], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; validator->setRange(0, 59); state = validator->validate(sl[1], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; int nf = 23; // 24 frames sec switch(MusEGlobal::mtcType) { //case 0: // 24 frames sec // nf = 23; // break; case 1: nf = 24; // 25 frames sec break; case 2: // 30 drop frame case 3: // 30 non drop frame nf = 29; break; default: break; } validator->setRange(0, nf); state = validator->validate(sl[2], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; validator->setRange(0, 99); state = validator->validate(sl[3], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; } else { if(sl.size() != 3) { printf("validate bbt string:%s sections:%d != 3\n", s.toLatin1().data(), sl.size()); return QValidator::Invalid; } int tb = MusEGlobal::sigmap.ticksBeat(_pos.tick()); unsigned tm = MusEGlobal::sigmap.ticksMeasure(_pos.tick()); if (tm==0) return QValidator::Invalid; int bm = tm / tb; validator->setRange(1, 9999); //printf("validate substring 0:%s\n", sl[0].toLatin1().data()); // Special hack because validator says 0000 is intermediate. if(sl[0] == "0000") return QValidator::Invalid; state = validator->validate(sl[0], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; validator->setRange(1, bm); //printf("validate substring 1:%s\n", sl[1].toLatin1().data()); // Special hack because validator says 00 is intermediate. if(sl[1] == "00") return QValidator::Invalid; state = validator->validate(sl[1], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; validator->setRange(0, tb-1); //printf("validate substring 2:%s\n", sl[2].toLatin1().data()); state = validator->validate(sl[2], dpos); if(state == QValidator::Invalid) return state; if(state == QValidator::Intermediate) rv = state; } return rv; } //--------------------------------------------------------- // curSegment //--------------------------------------------------------- int PosEdit::curSegment() const { QLineEdit* le = lineEdit(); int pos = le->cursorPosition(); int segment = -1; if (_smpte) { if (pos >= 0 && pos <= 3) segment = 0; else if (pos >= 4 && pos <= 6) segment = 1; else if (pos >= 7 && pos <= 9) segment = 2; else if (pos >= 10) segment = 3; } else { if (pos >= 0 && pos <= 4) segment = 0; else if (pos >= 5 && pos <= 7) segment = 1; else if (pos >= 8) segment = 2; else printf("curSegment = -1, pos %d\n", pos); } return segment; } //--------------------------------------------------------- // stepBy //--------------------------------------------------------- void PosEdit::stepBy(int steps) { int segment = curSegment(); int selPos; int selLen; bool changed = false; if (_smpte) { int minute, sec, frame, subframe; _pos.msf(nullptr, &minute, &sec, &frame, &subframe); switch(segment) { case 0: minute += steps; if (minute < 0) minute = 0; selPos = 0; selLen = 3; break; case 1: sec += steps; if (sec < 0) sec = 0; if (sec > 59) sec = 59; selPos = 4; selLen = 2; break; case 2: { int nf = 23; // 24 frames sec switch(MusEGlobal::mtcType) { //case 0: // 24 frames sec // nf = 23; // break; case 1: nf = 24; // 25 frames sec break; case 2: // 30 drop frame case 3: // 30 non drop frame nf = 29; break; default: break; } frame += steps; if (frame < 0) frame = 0; //if (frame > 24) //TD frame type? // frame = 24; if (frame > nf) frame = nf; selPos = 7; selLen = 2; } break; case 3: subframe += steps; if (subframe < 0) subframe = 0; if (subframe > 99) subframe = 99; selPos = 10; selLen = 2; break; default: return; } MusECore::Pos newPos(minute, sec, frame, subframe); if (!(newPos == _pos)) { changed = true; _pos = newPos; } } else { int bar, beat, tick; _pos.mbt(&bar, &beat, &tick); int tb = MusEGlobal::sigmap.ticksBeat(_pos.tick()); //int tb = MusEGlobal::sigmap.ticksBeat(_pos.tick()); unsigned tm = MusEGlobal::sigmap.ticksMeasure(_pos.tick()); //unsigned tm = MusEGlobal::sigmap.ticksMeasure(_pos.tick()); int bm = tm / tb; switch(segment) { case 0: bar += steps; if (bar < 0) bar = 0; selPos = 0; selLen = 4; break; case 1: beat += steps; if (beat < 0) beat = 0; else if (beat >= bm) beat = bm - 1; selPos = 5; selLen = 2; break; case 2: tick += steps; if (tick < 0) tick = 0; else if (tick >= tb) tick = tb -1; selPos = 8; selLen = 3; break; default: return; } MusECore::Pos newPos(bar, beat, tick); if (!(newPos == _pos)) { changed = true; _pos = newPos; } } if (changed) { updateValue(); emit valueChanged(_pos); } lineEdit()->setSelection(selPos, selLen); } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void PosEdit::paintEvent(QPaintEvent* event) { if (!initialized) updateValue(); initialized = true; QAbstractSpinBox::paintEvent(event); } //--------------------------------------------------------- // finishEdit // Return true if position changed. //--------------------------------------------------------- bool PosEdit::finishEdit() { // If our validator did its job correctly, the entire line edit text should be valid now... bool changed = false; QStringList sl = text().split(_smpte ? ':' : '.'); if (_smpte) { if(sl.size() != 4) { printf("finishEdit smpte string:%s sections:%d != 4\n", text().toLatin1().data(), sl.size()); return false; } MusECore::Pos newPos(sl[0].toInt(), sl[1].toInt(), sl[2].toInt(), sl[3].toInt()); if (!(newPos == _pos)) { changed = true; _pos = newPos; } } else { if(sl.size() != 3) { printf("finishEdit bbt string:%s sections:%d != 3\n", text().toLatin1().data(), sl.size()); return false; } MusECore::Pos newPos(sl[0].toInt() - 1, sl[1].toInt() - 1, sl[2].toInt()); if (!(newPos == _pos)) { changed = true; _pos = newPos; } } return changed; } } // namespace MusEGui muse-4.0.0/src/muse/components/posedit.h000066400000000000000000000053041404105311000202200ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __POSEDIT_H__ #define __POSEDIT_H__ #include "pos.h" #include namespace MusEGui { //--------------------------------------------------------- // PosEdit //--------------------------------------------------------- class PosEdit : public QAbstractSpinBox { Q_OBJECT Q_PROPERTY(bool smpte READ smpte WRITE setSmpte) bool _smpte; MusECore::Pos _pos; bool initialized; bool _returnMode; int cur_minute, cur_sec, cur_frame, cur_subframe; int cur_bar, cur_beat, cur_tick; QIntValidator* validator; virtual void paintEvent(QPaintEvent* event); virtual void stepBy(int steps); virtual StepEnabled stepEnabled() const; virtual void fixup(QString& input) const; virtual QValidator::State validate(QString&, int&) const; int curSegment() const; virtual bool event(QEvent*); bool finishEdit(); signals: void valueChanged(const MusECore::Pos&); // Choose these carefully, watch out for focusing recursion. void returnPressed(); void escapePressed(); void lostFocus(); // This is emitted when focus lost or return pressed (same as QAbstractSpinBox). void editingFinished(); public slots: void setValue(const MusECore::Pos& time); void setValue(int t); void setValue(const QString& s); public: PosEdit(QWidget* parent = 0); QSize sizeHint() const; MusECore::Pos pos() const { return _pos; } void setSmpte(bool); bool smpte() const { return _smpte; } void setReturnMode(bool v) { _returnMode = v; } bool returnMode() const { return _returnMode; } void updateValue(); }; } #endif muse-4.0.0/src/muse/components/poslabel.cpp000066400000000000000000000147761404105311000207220ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: poslabel.cpp,v 1.2.2.2 2009/04/06 01:24:55 terminator356 Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include #include #include #include "poslabel.h" #include "sync.h" #include "sig.h" #include "tempo.h" #include "globals.h" namespace MusEGlobal { extern int mtcType; } namespace MusEGui { //--------------------------------------------------------- // PosLabel //--------------------------------------------------------- PosLabel::PosLabel(QWidget* parent, const char* name) : QLabel(parent) { setObjectName(name); _tickValue = 0; _sampleValue = 0; _smpte = false; setFrameStyle(WinPanel | Sunken); setLineWidth(2); setMidLineWidth(3); //int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this); // ddskrjo 0 int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); setIndent(fw); //setContentsMargins(0,0,0,0); updateValue(); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize PosLabel::sizeHint() const { const QFontMetrics fm = fontMetrics(); //int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this); // ddskrjo 0 int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); int h = fm.height() + fw * 2; int w; if (_smpte) // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 w = 2 + fm.horizontalAdvance("999:99:99:99") + fw * 4; #else w = 2 + fm.width("999:99:99:99") + fw * 4; #endif else // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 w = 2 + fm.horizontalAdvance("9999.99.99999") + fw * 4; #else w = 2 + fm.width("9999.99.99999") + fw * 4; #endif return QSize(w, h).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // updateValue //--------------------------------------------------------- void PosLabel::updateValue() { QString s; if (_smpte) { double time = double(_sampleValue) / double(MusEGlobal::sampleRate); int min = int(time) / 60; int sec = int(time) % 60; double rest = time - (min * 60 + sec); switch(MusEGlobal::mtcType) { case 0: // 24 frames sec rest *= 24; break; case 1: // 25 rest *= 25; break; case 2: // 30 drop frame rest *= 30; break; case 3: // 30 non drop frame rest *= 30; break; } int frame = int(rest); int subframe = int((rest-frame)*100); s = QString("%1:%2:%3:%4") .arg(min, 3, 10, QLatin1Char('0')) .arg(sec, 2, 10, QLatin1Char('0')) .arg(frame, 2, 10, QLatin1Char('0')) .arg(subframe, 2, 10, QLatin1Char('0')); } else { int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(_tickValue, &bar, &beat, &tick); s = QString("%1.%2.%3") .arg(bar + 1, 4, 10, QLatin1Char('0')) .arg(beat + 1, 2, 10, QLatin1Char('0')) .arg(tick, 3, 10, QLatin1Char('0')); } setText(s); } //--------------------------------------------------------- // setSampleValue //--------------------------------------------------------- void PosLabel::setSampleValue(unsigned val) { if (val == INT_MAX) { setEnabled(false); return; } if(!isEnabled()) setEnabled(true); if (val == _sampleValue) return; _sampleValue = val; updateValue(); } //--------------------------------------------------------- // setTickValue //--------------------------------------------------------- void PosLabel::setTickValue(unsigned val) { if (val == INT_MAX) { setEnabled(false); return; } if(!isEnabled()) setEnabled(true); if (val == _tickValue) return; if (val >= MAX_TICK) { printf("THIS SHOULD NEVER HAPPEN: val=%u > MAX_TICK=%u in PosLabel::setTickValue()!\n",val, MAX_TICK); val=MAX_TICK-1; } _tickValue = val; updateValue(); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void PosLabel::setValue(unsigned val) { if (val == INT_MAX) { setEnabled(false); return; } if(!isEnabled()) setEnabled(true); unsigned oval = _smpte ? _sampleValue : _tickValue; if (val == oval) return; if (_smpte) _sampleValue = val; else _tickValue = val; updateValue(); } //--------------------------------------------------------- // setSmpte //--------------------------------------------------------- void PosLabel::setSmpte(bool val) { _smpte = val; if (val) _sampleValue = MusEGlobal::tempomap.tick2frame(_tickValue); else _tickValue = MusEGlobal::tempomap.frame2tick(_sampleValue); updateValue(); } } // namespace MusEGui muse-4.0.0/src/muse/components/poslabel.h000066400000000000000000000036061404105311000203550ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: poslabel.h,v 1.2 2004/01/11 18:55:37 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __POSLABEL_H__ #define __POSLABEL_H__ #include namespace MusEGui { //--------------------------------------------------------- // PosLabel //--------------------------------------------------------- class PosLabel : public QLabel { Q_OBJECT bool _smpte; unsigned _tickValue; unsigned _sampleValue; void updateValue(); protected: QSize sizeHint() const; public slots: void setTickValue(unsigned); void setSampleValue(unsigned); void setValue(unsigned); public: PosLabel(QWidget* parent, const char* name = 0); unsigned value() const { return _smpte ? _sampleValue : _tickValue; } unsigned tickValue() const { return _tickValue; } unsigned sampleValue() const { return _sampleValue; } void setSmpte(bool); bool smpte() const { return _smpte; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/postoolbar.cpp000066400000000000000000000213171404105311000212720ustar00rootroot00000000000000#include "postoolbar.h" #include "song.h" #include "gconfig.h" #include "audio.h" #include "icons.h" #include #include #include namespace MusEGui { PosToolbar::PosToolbar(const QString &title, QWidget *parent) : QToolBar(title, parent) { setObjectName("Position toolbar"); QLabel *pixlab = new QLabel(this); // QLabel *range = new QLabel(tr("Range"), this); int iconSize = MusEGlobal::config.iconSize; qreal dpr = devicePixelRatioF(); QPixmap pix(iconSize * dpr, iconSize * dpr); pix.setDevicePixelRatio(dpr); pix.fill( Qt::transparent ); QPainter p(&pix); p.setRenderHint(QPainter::Antialiasing); p.setBrush(MusEGlobal::config.rangeMarkerColor); p.setPen(MusEGlobal::config.rangeMarkerColor); qreal pixc = iconSize / 2; qreal off = 0; // qreal off = iconSize / 10; qreal rad = iconSize / 5; p.drawPolygon( QVector{ { pixc + rad, off }, { pixc - rad, off }, { pixc + rad, off + 2 * rad } } ); p.drawLine(QPointF(pixc + rad, off + 2 * rad), QPointF(pixc + rad, iconSize - 2 * off)); pixlab->setPixmap(pix); addWidget(pixlab); markerLeft = new PosEdit(this); markerLeft->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); // markerLeft->setFocusPolicy(Qt::NoFocus); markerLeft->setToolTip(tr("Left marker")); markerLeft->setStatusTip(tr("Left marker position")); addWidget(markerLeft); pixlab = new QLabel(this); pix.fill( Qt::transparent ); p.drawPolygon( QVector{ { pixc - rad, off }, { pixc + rad, off }, { pixc - rad, off + 2 * rad } } ); p.drawLine(QPointF(pixc - rad, off + 2 * rad), QPointF(pixc - rad, iconSize - 2 * off)); pixlab->setPixmap(pix); pixlab->setContentsMargins(2,0,0,0); addWidget(pixlab); markerRight = new PosEdit(this); markerRight->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); // markerRight->setFocusPolicy(Qt::NoFocus); markerRight->setToolTip(tr("Right marker")); markerRight->setStatusTip(tr("Right marker position")); addWidget(markerRight); // QLabel *pos = new QLabel(tr("Pos"), this); // pos->setIndent(2); // addWidget(pos); p.setBrush(MusEGlobal::config.positionMarkerColor); p.setPen(Qt::NoPen); pixlab = new QLabel(this); pix.fill( Qt::transparent ); p.drawPolygon( QVector{ { pixc - 2 * rad, off }, { pixc + 2 * rad, off }, { pixc, off + 2 * rad } } ); p.setPen(MusEGlobal::config.positionMarkerColor); p.drawLine(QPointF(pixc, off + 2 * rad), QPointF(pixc, iconSize - 2 * off)); pixlab->setPixmap(pix); pixlab->setContentsMargins(2,0,0,0); addWidget(pixlab); time = new PosEdit(this); time->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); // time->setFocusPolicy(Qt::NoFocus); time->setToolTip(tr("Current position")); time->setStatusTip(tr("Current position in bars/beats")); addWidget(time); timeSmpte = new PosEdit(this); timeSmpte->setSmpte(true); timeSmpte->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); // timeSmpte->setFocusPolicy(Qt::NoFocus); timeSmpte->setToolTip(tr("SMPTE position")); timeSmpte->setStatusTip(tr("Current position in SMPTE time")); addWidget(timeSmpte); toggleTickFrame = new QAction(*showFieldsSVGIcon, "Toggle ticks/frames"); toggleTickFrame->setCheckable(true); toggleTickFrame->setChecked(false); toggleTickFrame->setToolTip(tr("Show/Hide position in ticks and audio frames")); toggleTickFrame->setStatusTip(tr("Show/Hide additional output fields displaying the current position in ticks and audio frames")); addAction(toggleTickFrame); posTicks = new QLabel(this); posTicks->setObjectName("PosTicks"); posTicks->setToolTip(tr("Current position in ticks")); posTicks->setStatusTip(tr("Current position in ticks")); posTicks->setText("0000000000"); posTicks->setTextFormat(Qt::PlainText); posTicks->setFocusPolicy(Qt::NoFocus); posTicks->setFrameStyle(QFrame::Panel | QFrame::Sunken); posTicksAction = addWidget(posTicks); posTicksAction->setVisible(false); posFrames = new QLabel(this); posFrames->setObjectName("PosFrames"); posFrames->setToolTip(tr("Current position in audio frames")); posFrames->setStatusTip(tr("Current position in audio frames")); posFrames->setText("0000000000"); posFrames->setTextFormat(Qt::PlainText); posFrames->setFocusPolicy(Qt::NoFocus); posFrames->setFrameStyle(QFrame::Panel | QFrame::Sunken); posFramesAction = addWidget(posFrames); posFramesAction->setVisible(false); slider = new QSlider; slider->setFocusPolicy(Qt::NoFocus); slider->setMinimum(0); slider->setMaximum(200000); slider->setPageStep(1000); slider->setValue(0); slider->setOrientation(Qt::Horizontal); slider->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); slider->setToolTip(tr("Current position")); slider->setStatusTip(tr("Current position slider")); // addSeparator(); addWidget(slider); connect(markerLeft, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(lposChanged(const MusECore::Pos&))); connect(markerRight, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(rposChanged(const MusECore::Pos&))); connect(time, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(cposChanged(const MusECore::Pos&))); connect(timeSmpte, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(cposChanged(const MusECore::Pos&))); connect(slider,SIGNAL(valueChanged(int)), SLOT(cposChanged(int))); connect(toggleTickFrame, SIGNAL(toggled(bool)), SLOT(showTickFrameToggled(bool))); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(setPos(int, unsigned, bool))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), this, SLOT(songChanged(MusECore::SongChangedStruct_t))); } //--------------------------------------------------------- // cposChanged //--------------------------------------------------------- void PosToolbar::cposChanged(int tick) { MusEGlobal::song->setPos(MusECore::Song::CPOS, tick); } //--------------------------------------------------------- // cposChanged //--------------------------------------------------------- void PosToolbar::cposChanged(const MusECore::Pos& pos) { MusEGlobal::song->setPos(MusECore::Song::CPOS, pos.tick()); } //--------------------------------------------------------- // lposChanged //--------------------------------------------------------- void PosToolbar::lposChanged(const MusECore::Pos& pos) { MusEGlobal::song->setPos(MusECore::Song::LPOS, pos.tick()); } //--------------------------------------------------------- // rposChanged //--------------------------------------------------------- void PosToolbar::rposChanged(const MusECore::Pos& pos) { MusEGlobal::song->setPos(MusECore::Song::RPOS, pos.tick()); } //--------------------------------------------------------- // setPos //--------------------------------------------------------- void PosToolbar::setPos(int idx, unsigned v, bool) { switch (idx) { case 0: time->setValue(v); timeSmpte->setValue(v); if((unsigned) slider->value() != v) { slider->blockSignals(true); slider->setValue(v); slider->blockSignals(false); } if (posTicksAction->isVisible()) { posTicks->setText(QString::number(v).rightJustified(10, '0')); posFrames->setText(QString::number(MusEGlobal::audio->pos().frame()).rightJustified(10, '0')); } break; case 1: markerLeft->setValue(v); break; case 2: markerRight->setValue(v); break; } } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void PosToolbar::songChanged(MusECore::SongChangedStruct_t) { slider->setRange(0, MusEGlobal::song->len()); } void PosToolbar::showTickFrameToggled(bool checked) { if (checked) { posTicks->setText(QString::number(MusEGlobal::audio->pos().tick()).rightJustified(10, '0')); posFrames->setText(QString::number(MusEGlobal::audio->pos().frame()).rightJustified(10, '0')); posTicksAction->setVisible(true); posFramesAction->setVisible(true); } else { posTicksAction->setVisible(false); posFramesAction->setVisible(false); } updateGeometry(); update(); } } // namespace muse-4.0.0/src/muse/components/postoolbar.h000066400000000000000000000016101404105311000207310ustar00rootroot00000000000000#ifndef POSTOOLBAR_H #define POSTOOLBAR_H #include #include #include #include "pos.h" #include "posedit.h" #include "type_defs.h" namespace MusEGui { class PosToolbar : public QToolBar { Q_OBJECT public: PosToolbar(const QString& title, QWidget* parent = nullptr); public slots: void setPos(int, unsigned, bool); void songChanged(MusECore::SongChangedStruct_t); private: PosEdit *markerLeft; PosEdit *markerRight; PosEdit *time; PosEdit *timeSmpte; QLabel *posTicks; QLabel *posFrames; QAction *posTicksAction; QAction *posFramesAction; QSlider *slider; QAction *toggleTickFrame; private slots: void cposChanged(const MusECore::Pos&); void cposChanged(int); void lposChanged(const MusECore::Pos&); void rposChanged(const MusECore::Pos&); void showTickFrameToggled(bool); }; } #endif // POSTOOLBAR_H muse-4.0.0/src/muse/components/projectcreate.ui000066400000000000000000000165641404105311000216030ustar00rootroot00000000000000 ProjectCreate 0 0 564 407 Create Project Global project folder true Change Global project folder ... Project name Project will be saved in user's Templates folder Project is a template Qt::Horizontal 394 20 Save window sizes and positions with the project Write window state true Qt::Horizontal Main file type Qt::Horizontal 40 20 Path to main file: Qt::Horizontal 40 20 Recommended especially for audio projects to keep all project files in one folder &Create project subfolder Qt::NoFocus true Change folder for project files ... Reset to Global project folder ... Project description: Description will be displayed when project is opened Qt::Horizontal 40 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok projectNameEdit templateCheckBox winStateCheckbox projectFileTypeCB createFolderCheckbox storageDirEdit browseDirButton restorePathButton commentEdit projDirToolButton projDirLineEdit buttonBox rejected() ProjectCreate reject() 316 260 286 274 muse-4.0.0/src/muse/components/projectcreateimpl.cpp000066400000000000000000000161111404105311000226160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/projectcreateimpl.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "projectcreateimpl.h" #include "gconfig.h" #include "globals.h" #include "app.h" #include "helper.h" #include "icons.h" namespace MusEGui { ProjectCreateImpl::ProjectCreateImpl(QWidget *parent) : QDialog(parent) { setupUi(this); directoryPath = MusEGlobal::config.projectBaseFolder; QStringList filters = localizedStringListFromCharArray(MusEGlobal::project_create_file_save_pattern, "file_patterns"); projectFileTypeCB->addItems(filters); QString proj_title = MusEGlobal::muse->projectTitle(); QString proj_path = MusEGlobal::muse->projectPath(); // QString proj_path = directoryPath; QString proj_ext = MusEGlobal::muse->projectExtension(); projectNameEdit->setText(proj_title); bool is_template = proj_path.startsWith(MusEGlobal::configPath + "/templates"); templateCheckBox->setChecked(is_template); projDirPath = proj_path; int type_idx = 0; if(!proj_ext.isEmpty()) { // FIXME Imperfect. Trying to avoid adding yet another series of character strings. p4.0.40 type_idx = projectFileTypeCB->findText(proj_ext, Qt::MatchContains | Qt::MatchCaseSensitive); if(type_idx == -1) type_idx = 0; } projectFileTypeCB->setCurrentIndex(type_idx); projDirToolButton->setIcon(*fileopenSVGIcon); browseDirButton->setIcon(*fileopenSVGIcon); restorePathButton->setIcon(*undoSVGIcon); restorePathButton->setEnabled(false); // Disabled at first. connect(templateCheckBox,SIGNAL(toggled(bool)), this, SLOT(templateButtonChanged(bool))); connect(projDirToolButton,SIGNAL(clicked()), this, SLOT(browseProjDir())); connect(restorePathButton,SIGNAL(clicked()), this, SLOT(restorePath())); connect(browseDirButton,SIGNAL(clicked()), this, SLOT(selectDirectory())); connect(projectNameEdit,SIGNAL(textChanged(QString)), this, SLOT(updateProjectName())); connect(createFolderCheckbox,SIGNAL(clicked()), this, SLOT(createProjFolderChanged())); connect(projectFileTypeCB,SIGNAL(currentIndexChanged(int)), this, SLOT(updateDirectoryPath())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(ok())); projectNameEdit->setPlaceholderText(""); commentEdit->setPlaceholderText(""); updateDirectoryPath(); projectNameEdit->setFocus(); show(); } void ProjectCreateImpl::selectDirectory() { QString dpath = templateCheckBox->isChecked() ? (overrideTemplDirPath.isEmpty() ? (MusEGlobal::configPath + QString("/templates")) : overrideTemplDirPath) : (overrideDirPath.isEmpty() ? directoryPath : overrideDirPath); QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory"), dpath); if(dir.isEmpty()) return; (templateCheckBox->isChecked() ? overrideTemplDirPath : overrideDirPath) = dir; restorePathButton->setEnabled(true); updateDirectoryPath(); } void ProjectCreateImpl::updateProjectName() { QString curExt = projectFileTypeCB->currentText(); if(curExt.isEmpty()) curExt = ".med"; else { curExt = MusEGui::getFilterExtension(curExt); // Do we have a valid extension? if(curExt.isEmpty()) curExt = ".med"; } QString name = ""; if (createFolderCheckbox->isChecked()) { if (!projectNameEdit->text().isEmpty()) name = projectNameEdit->text() + "/" + projectNameEdit->text() + curExt; } else { if (!projectNameEdit->text().isEmpty()) name = projectNameEdit->text() + curExt; } bool is_new = (MusEGlobal::museProject == MusEGlobal::museProjectInitPath); QString dpath = templateCheckBox->isChecked() ? (overrideTemplDirPath.isEmpty() ? (MusEGlobal::configPath + QString("/templates")) : overrideTemplDirPath) : (overrideDirPath.isEmpty() ? (is_new ? directoryPath : projDirPath) : overrideDirPath); QDir proj_dir(dpath); bool is_project = dpath.startsWith(MusEGlobal::config.projectBaseFolder); MusEGlobal::config.projectStoreInFolder && (templateCheckBox->isChecked() ? overrideTemplDirPath.isEmpty() : overrideDirPath.isEmpty()); dpath = proj_dir.absolutePath(); storageDirEdit->blockSignals(true); storageDirEdit->setText(dpath + "/" + name ); storageDirEdit->blockSignals(false); projDirLineEdit->setEnabled(!templateCheckBox->isChecked() && is_project); } void ProjectCreateImpl::updateDirectoryPath() { updateProjectName(); projDirLineEdit->blockSignals(true); projDirLineEdit->setText(MusEGlobal::config.projectBaseFolder); projDirLineEdit->blockSignals(false); } QString ProjectCreateImpl::getProjectPath() const { return storageDirEdit->text(); } QString ProjectCreateImpl::getSongInfo() const { return commentEdit->toPlainText(); } void ProjectCreateImpl::ok() { MusEGlobal::config.projectStoreInFolder = createFolderCheckbox->isChecked(); emit accept(); } void ProjectCreateImpl::createProjFolderChanged() { updateDirectoryPath(); } void ProjectCreateImpl::browseProjDir() { QString dir = MusEGui::browseProjectFolder(this); if(!dir.isEmpty()) { directoryPath = dir; MusEGlobal::config.projectBaseFolder = dir; // Save to config file. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); updateDirectoryPath(); } } void ProjectCreateImpl::templateButtonChanged(bool v) { restorePathButton->setEnabled(v ? !overrideTemplDirPath.isEmpty() : !overrideDirPath.isEmpty()); winStateCheckbox->setChecked(!v); updateDirectoryPath(); } void ProjectCreateImpl::restorePath() { if(templateCheckBox->isChecked()) overrideTemplDirPath.clear(); else overrideDirPath.clear(); restorePathButton->setEnabled(templateCheckBox->isChecked() ? !overrideTemplDirPath.isEmpty() : !overrideDirPath.isEmpty()); updateDirectoryPath(); } bool ProjectCreateImpl::getWriteTopwins() const { return winStateCheckbox->isChecked(); } void ProjectCreateImpl::setWriteTopwins(bool v) { winStateCheckbox->setChecked(v); } } //namespace MusEGui muse-4.0.0/src/muse/components/projectcreateimpl.h000066400000000000000000000035401404105311000222650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/projectcreateimpl.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef PROJECTCREATEIMPL_H #define PROJECTCREATEIMPL_H #include #include "ui_projectcreate.h" namespace MusEGui { class ProjectCreateImpl : public QDialog, Ui::ProjectCreate { Q_OBJECT QString directoryPath; QString overrideDirPath; QString overrideTemplDirPath; QString projDirPath; public: explicit ProjectCreateImpl(QWidget *parent = 0); QString getProjectPath() const; QString getSongInfo() const; bool getWriteTopwins() const; void setWriteTopwins(bool); signals: protected slots: void updateProjectName(); void updateDirectoryPath(); void selectDirectory(); void ok(); void createProjFolderChanged(); void browseProjDir(); void templateButtonChanged(bool); void restorePath(); }; } // namespace MusEGui #endif // PROJECTCREATEIMPL_H muse-4.0.0/src/muse/components/raster_widgets.cpp000066400000000000000000000107071404105311000221350ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // raster_widgets.cpp // Copyright (C) 2020 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "raster_widgets.h" #include #include namespace MusEGui { RasterizerTableView::RasterizerTableView(QWidget *parent) : QTableView(parent) { verticalHeader()->setDefaultSectionSize(22); horizontalHeader()->setDefaultSectionSize(32); setSelectionMode(QAbstractItemView::SingleSelection); verticalHeader()->hide(); horizontalHeader()->hide(); } void RasterizerTableView::reset() { QTableView::reset(); const QAbstractItemModel *mdl = model(); if(!mdl) return; const int cols = mdl->columnCount(); const int rows = mdl->rowCount(); int off_row = -1; int bar_row = -1; // Is it a RasterizerModel? (It should be). const RasterizerModel* rast_mdl = dynamic_cast(mdl); if(rast_mdl) { off_row = rast_mdl->offRow(); bar_row = rast_mdl->barRow(); } // Be sure to reset any previous wide spans to normal single-column span! // Otherwise leftover wide span items appear in the list when it changes! Verified and tested OK. // TODO TEST : How can that happen? Doesn't the table clear first? // Or does it 'economize' (re-use) items? Apparently so. Should we clear first if not already done? clearSpans(); for(int i = 0; i < rows; ++i) { // Special for 'off' and 'bar' rows: Span all columns. if(i == off_row) setSpan(off_row, 0, 1, cols); else if(i == bar_row) setSpan(bar_row, 0, 1, cols); } int w = 0; for(int i = 0; i < cols; ++i) { if(isColumnHidden(i)) continue; resizeColumnToContents(i); w += columnWidth(i); } setMinimumWidth(w); //QTableView::reset(); } //===================================================================== RasterizerListView::RasterizerListView(QWidget *parent) : QListView(parent) { setSelectionMode(QAbstractItemView::SingleSelection); } //===================================================================== RasterizerTreeView::RasterizerTreeView(QWidget *parent) : QTreeView(parent) { setSelectionMode(QAbstractItemView::SingleSelection); } //===================================================================== RasterLabelCombo::RasterLabelCombo(RasterComboType type, RasterizerModel *model, QWidget* parent, const char* name) : LabelCombo(tr("Snap"), parent, name) { // setFocusPolicy(Qt::TabFocus); //setContentsMargins(0,0,0,0); // Arbitrarily high value to show everything. setMaxVisibleItems(50); setSizeAdjustPolicy(QComboBox::AdjustToContents); switch(type) { case ListView: rlist = new RasterizerListView(); break; case TableView: rlist = new RasterizerTableView(); break; case TreeView: rlist = new RasterizerTreeView(); break; } _rlistModel = model; rlist->setModel(_rlistModel); setView(rlist); connect(this, QOverload::of(&LabelCombo::activated), [=](const QModelIndex& mdl_idx) { rasterActivated(mdl_idx); } ); } const Rasterizer *RasterLabelCombo::rasterizer() const { return _rlistModel->rasterizer(); } RasterizerModel *RasterLabelCombo::rasterizerModel() const { return _rlistModel; } void RasterLabelCombo::setRasterizerModel(RasterizerModel *model) { if(_rlistModel == model) return; _rlistModel = model; rlist->setModel(_rlistModel); } void RasterLabelCombo::rasterActivated(const QModelIndex& mdl_idx) { const int raster = _rlistModel->data(mdl_idx, RasterizerModel::RasterValueRole).toInt(); if(raster < 0) return; emit rasterChanged(raster); } } // namespace MusEGui muse-4.0.0/src/muse/components/raster_widgets.h000066400000000000000000000045451404105311000216050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // raster_widgets.h // Copyright (C) 2020 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __RASTER_WIDGETS_H__ #define __RASTER_WIDGETS_H__ #include #include #include #include "lcombo.h" #include "rasterizer.h" class QModelIndex; namespace MusEGui { class RasterizerListView : public QListView { public: RasterizerListView(QWidget *parent = nullptr); }; //=================================================== class RasterizerTableView : public QTableView { private: Q_OBJECT public slots: void reset() override; public: RasterizerTableView(QWidget *parent = nullptr); }; //=================================================== class RasterizerTreeView : public QTreeView { public: RasterizerTreeView(QWidget *parent = nullptr); }; //=================================================== class RasterLabelCombo : public LabelCombo { private: Q_OBJECT public: enum RasterComboType { ListView, TableView, TreeView }; private slots: void rasterActivated(const QModelIndex&); private: QAbstractItemView *rlist; RasterizerModel *_rlistModel; signals: void rasterChanged(int raster); public: RasterLabelCombo(RasterComboType type, RasterizerModel *model, QWidget* parent, const char* name=0); const Rasterizer *rasterizer() const; RasterizerModel *rasterizerModel() const; void setRasterizerModel(RasterizerModel *model); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/rectoolbar.cpp000066400000000000000000000044011404105311000212350ustar00rootroot00000000000000#include "rectoolbar.h" #include "song.h" #include namespace MusEGui { RecToolbar::RecToolbar(const QString &title, QWidget *parent) : QToolBar(title, parent) { setObjectName("Recording toolbar"); recMode = new QComboBox; recMode->setFocusPolicy(Qt::NoFocus); recMode->setToolTip(tr("Record mode")); recMode->setStatusTip(tr("Record mode: Overdub to add new events, Replace to replace overlapping events.")); recMode->insertItem(MusECore::Song::REC_OVERDUP, tr("Overdub")); recMode->insertItem(MusECore::Song::REC_REPLACE, tr("Replace")); recMode->setCurrentIndex(MusEGlobal::song->recMode()); connect(recMode, SIGNAL(activated(int)), SLOT(setRecMode(int))); connect(MusEGlobal::song, SIGNAL(recModeChanged(int)), SLOT(setRecMode(int))); cycleMode = new QComboBox; cycleMode->setFocusPolicy(Qt::NoFocus); cycleMode->setToolTip(tr("Cycle record mode")); cycleMode->setStatusTip(tr("Cycle record mode: Normal to replace range when loop is finished, Mix to add new events, Replace to replace range on first MIDI input.")); cycleMode->insertItem(MusECore::Song::CYCLE_NORMAL, tr("Normal")); cycleMode->insertItem(MusECore::Song::CYCLE_MIX, tr("Mix")); cycleMode->insertItem(MusECore::Song::CYCLE_REPLACE, tr("Replace")); cycleMode->setCurrentIndex(MusEGlobal::song->cycleMode()); connect(cycleMode, SIGNAL(activated(int)), SLOT(setCycleMode(int))); connect(MusEGlobal::song, SIGNAL(cycleModeChanged(int)), SLOT(setCycleMode(int))); addWidget(recMode); addWidget(cycleMode); } //--------------------------------------------------------- // toggleRecMode //--------------------------------------------------------- void RecToolbar::setRecMode(int id) { if (MusEGlobal::song->recMode() != id) MusEGlobal::song->setRecMode(id); if (recMode->currentIndex() != id) recMode->setCurrentIndex(id); } //--------------------------------------------------------- // toggleCycleMode //--------------------------------------------------------- void RecToolbar::setCycleMode(int id) { if (MusEGlobal::song->cycleMode() != id) MusEGlobal::song->setCycleMode(id); if (cycleMode->currentIndex() != id) cycleMode->setCurrentIndex(id); } } // namespace muse-4.0.0/src/muse/components/rectoolbar.h000066400000000000000000000006061404105311000207050ustar00rootroot00000000000000#ifndef RECTOOLBAR_H #define RECTOOLBAR_H #include class QComboBox; namespace MusEGui { class RecToolbar : public QToolBar { Q_OBJECT QComboBox *recMode; QComboBox *cycleMode; public: RecToolbar(const QString& title, QWidget* parent = nullptr); public slots: void setRecMode(int); void setCycleMode(int); }; } // namespace #endif // RECTOOLBAR_H muse-4.0.0/src/muse/components/routedialog.cpp000066400000000000000000004602171404105311000214320ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: routedialog.cpp,v 1.5.2.2 2007/01/04 00:35:17 terminator356 Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) // (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 #include #include #include #include "routedialog.h" #include "globaldefs.h" #include "globals.h" #include "gconfig.h" #include "midiport.h" #include "track.h" #include "song.h" #include "audio.h" #include "driver/jackaudio.h" #include "globaldefs.h" #include "app.h" #include "operations.h" #include "icons.h" // Forwards from header: #include #include #include #include #include #include #include // For debugging output: Uncomment the fprintf section. #define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); // Undefine if and when multiple output routes are added to midi tracks. #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // An arbitrarily large value for size hint calculations. #define _VERY_LARGE_INTEGER_ 1000000 namespace MusEGui { const QString RouteDialog::tracksCat(QObject::tr("Tracks:")); const QString RouteDialog::midiPortsCat(QObject::tr("Midi ports:")); const QString RouteDialog::midiDevicesCat(QObject::tr("Midi devices:")); const QString RouteDialog::jackCat(QObject::tr("Jack:")); const QString RouteDialog::jackMidiCat(QObject::tr("Jack midi:")); const int RouteDialog::channelDotDiameter = 12; const int RouteDialog::channelDotSpacing = 1; const int RouteDialog::channelDotsPerGroup = 4; const int RouteDialog::channelDotGroupSpacing = 3; const int RouteDialog::channelDotsMargin = 1; const int RouteDialog::channelBarHeight = RouteDialog::channelDotDiameter + 2 * RouteDialog::channelDotsMargin; const int RouteDialog::channelLineWidth = 1; const int RouteDialog::channelLinesSpacing = 1; const int RouteDialog::channelLinesMargin = 1; std::list tmpJackInPorts; std::list tmpJackOutPorts; std::list tmpJackMidiInPorts; std::list tmpJackMidiOutPorts; //--------------------------------------------------------- // RouteChannelsList //--------------------------------------------------------- int RouteChannelsList::connectedChannels() const { int n = 0; const int sz = size(); for(int i = 0; i < sz; ++i) if(at(i)._connected) ++n; return n; } // Static. int RouteChannelsList::channelsPerWidth(int width) { // if(width <= 0) // return size(); if(width < 0) width = _VERY_LARGE_INTEGER_; int groups_per_col = (width - 2 * RouteDialog::channelDotsMargin) / (RouteDialog::channelDotGroupSpacing + RouteDialog::channelDotsPerGroup * (RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing)); if(groups_per_col < 1) groups_per_col = 1; return RouteDialog::channelDotsPerGroup * groups_per_col; } // Static. int RouteChannelsList::groupsPerChannels(int channels) { int groups = channels / RouteDialog::channelDotsPerGroup; //if(groups < 1) // groups = 1; if(channels % RouteDialog::channelDotsPerGroup) ++groups; return groups; } int RouteChannelsList::barsPerColChannels(int cc) const { if(cc == 0) return 0; const int chans = size(); int bars = chans / cc; if(chans % cc) ++bars; //if(chan_rows < 1) // chan_rows = 1; return bars; } // Static. int RouteChannelsList::minimumWidthHint() { return RouteDialog::channelDotsPerGroup * (RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing) + RouteDialog::channelDotGroupSpacing + 2 * RouteDialog::channelDotsMargin; } int RouteChannelsList::widthHint(int width) const { const int chans = size(); int chans_per_col = channelsPerWidth(width); // Limit to actual number of channels available. if(chans_per_col > chans) chans_per_col = chans; const int groups_per_col = groupsPerChannels(chans_per_col); return chans_per_col * (RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing) + groups_per_col * RouteDialog::channelDotGroupSpacing + 2 * RouteDialog::channelDotsMargin; } int RouteChannelsList::heightHint(int width) const { const int chans = size(); int chans_per_col = channelsPerWidth(width); // Limit to actual number of channels available. if(chans_per_col > chans) chans_per_col = chans; const int bars = barsPerColChannels(chans_per_col); return bars * RouteDialog::channelBarHeight + connectedChannels() * (RouteDialog::channelLinesSpacing + RouteDialog::channelLineWidth) + 4 * RouteDialog::channelLinesMargin; } //--------------------------------------------------------- // RouteTreeWidgetItem //--------------------------------------------------------- void RouteTreeWidgetItem::init() { _curChannel = 0; setChannels(); //computeChannelYValues(); // A data role to pass the item type from item to delegate. //setData(RouteDialog::ROUTE_NAME_COL, TypeRole, QVariant::fromValue(type())); } bool RouteTreeWidgetItem::setChannels() { bool changed = false; switch(type()) { case NormalItem: case CategoryItem: case RouteItem: break; case ChannelsItem: switch(_route.type) { case MusECore::Route::TRACK_ROUTE: if(_route.track) { MusECore::RouteCapabilitiesStruct rcaps = _route.track->routeCapabilities(); int chans = 0; switch(_route.track->type()) { case MusECore::Track::AUDIO_INPUT: chans = _isInput ? rcaps._trackChannels._outChannels : rcaps._jackChannels._inChannels; break; case MusECore::Track::AUDIO_OUTPUT: chans = _isInput ? rcaps._jackChannels._outChannels : rcaps._trackChannels._inChannels; break; case MusECore::Track::MIDI: case MusECore::Track::DRUM: #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ chans = _isInput ? MusECore::MUSE_MIDI_CHANNELS : rcaps._midiPortChannels._inChannels; #else chans = _isInput ? rcaps._midiPortChannels._outChannels : rcaps._midiPortChannels._inChannels; #endif break; case MusECore::Track::WAVE: case MusECore::Track::AUDIO_AUX: case MusECore::Track::AUDIO_SOFTSYNTH: case MusECore::Track::AUDIO_GROUP: chans = _isInput ? rcaps._trackChannels._outChannels : rcaps._trackChannels._inChannels; break; } if(chans != _channels.size()) { _channels.resize(chans); changed = true; } } break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: break; } break; } if(changed) { _curChannel = 0; //computeChannelYValues(); } return changed; } void RouteTreeWidgetItem::getSelectedRoutes(MusECore::RouteList& routes) { switch(type()) { case NormalItem: case CategoryItem: break; case RouteItem: if(isSelected()) routes.push_back(_route); break; case ChannelsItem: switch(_route.type) { case MusECore::Route::TRACK_ROUTE: if(_route.track) { MusECore::Route r(_route); const int sz = _channels.size(); if(_route.track->isMidiTrack()) { for(int i = 0; i < sz && i < MusECore::MUSE_MIDI_CHANNELS; ++i) { //if(_channels.testBit(i)) if(_channels.selected(i)) { //r.channel = (1 << i); r.channel = i; routes.push_back(r); } } } else { for(int i = 0; i < sz; ++i) { //if(_channels.testBit(i)) if(_channels.selected(i)) { r.channel = i; routes.push_back(r); } } } } break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: if(isSelected()) routes.push_back(_route); break; } break; } } int RouteTreeWidgetItem::channelAt(const QPoint& pt, const QRect& rect) const { // if(!treeWidget()->viewport()) // return false; RouteTreeWidget* rtw = qobject_cast(treeWidget()); if(!rtw) return false; const int col = rtw->columnAt(pt.x()); const int col_width = rtw->columnWidth(col); //const int view_width = rtw->viewport()->width(); const int chans = _channels.size(); const int view_offset = rtw->header()->offset(); // const int x_offset = (_isInput ? view_width - _channels.widthHint(view_width) - view_offset : -view_offset); const int x_offset = (_isInput ? //view_width - _channels.widthHint(rtw->wordWrap() ? view_width : -1) - view_offset : -view_offset); col_width - _channels.widthHint(rtw->channelWrap() ? col_width : -1) - view_offset : -view_offset); QPoint p(pt.x() - x_offset, pt.y() - rect.y()); DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::channelAt() pt x:%d y:%d rect x:%d y:%d w:%d h:%d view_offset:%d x_offset:%d col w:%d header w:%d view w:%d p x:%d y:%d\n", pt.x(), pt.y(), rect.x(), rect.y(), rect.width(), rect.height(), view_offset, x_offset, rtw->columnWidth(col), rtw->header()->sectionSize(col), view_width, p.x(), p.y()); for(int i = 0; i < chans; ++i) { const RouteChannelsStruct& ch_struct = _channels.at(i); const QRect& ch_rect = ch_struct._buttonRect; if(ch_rect.contains(p)) return i; } return -1; } void RouteTreeWidgetItem::computeChannelYValues(int col_width) { //_channelYValues.resize(); if(type() != ChannelsItem) return; //_channelYValues.fill(-1); _channels.fillConnected(false); switch(_route.type) { case MusECore::Route::TRACK_ROUTE: if(_route.track) { //_channelYValues.fill(-1); //_channels.fillConnected(false); #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(_isInput && _route.track->isMidiTrack()) _channels.setConnected(static_cast(_route.track)->outChannel(), true); else #endif { const MusECore::RouteList* rl = _isInput ? _route.track->outRoutes() : _route.track->inRoutes(); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case MusECore::Route::TRACK_ROUTE: //if(ir->track && ir->channel != -1) if(ir->channel != -1) { // Mark the channel as used. _channels.setConnected(ir->channel, true); } break; case MusECore::Route::MIDI_PORT_ROUTE: if(ir->isValid() && ir->channel != -1) { _channels.setConnected(ir->channel, true); } break; case MusECore::Route::JACK_ROUTE: if(ir->channel != -1) _channels.setConnected(ir->channel, true); break; case MusECore::Route::MIDI_DEVICE_ROUTE: break; } } } } break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: break; } const int chans = _channels.size(); int chans_per_w = _channels.channelsPerWidth(col_width); // Limit to actual number of channels available. if(chans_per_w > chans) chans_per_w = chans; //DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint src list width:%d src viewport width:%d\n", // router->newSrcList->width(), router->newSrcList->viewport()->width()); const int x_orig = RouteDialog::channelDotsMargin; int x = x_orig; int chan_y = 2 * RouteDialog::channelDotsMargin; DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::computeChannelYValues() col_width:%d chans_per_w:%d\n", col_width, chans_per_w); int line_y = 2 * RouteDialog::channelLinesMargin + (_isInput ? 0 : (RouteDialog::channelBarHeight + RouteDialog::channelDotsMargin + RouteDialog::channelLinesMargin)); int cur_chan = 0; for(int i = 0; i < chans; ) { const bool is_connected = _channels.at(i)._connected; if(is_connected) { // Replace the value with an appropriate y value useful for drawing channel lines. _channels[i]._lineY = line_y; } if(!_isInput) _channels[i]._buttonRect = QRect(x, chan_y, RouteDialog::channelDotDiameter, RouteDialog::channelDotDiameter); ++i; const bool new_group = (i % RouteDialog::channelDotsPerGroup == 0); const bool new_section = (i % chans_per_w == 0); if(is_connected) line_y += RouteDialog::channelLineWidth + RouteDialog::channelLinesSpacing; if(_isInput) { // If we finished a section set button rects, or we reached the end // set the remaining button rects, based on current line y (and x). if(new_section || i == chans) { x = x_orig; for( ; cur_chan < i; ) { DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::computeChannelYValues() i:%d cur_chan:%d x:%d\n", i, cur_chan, x); _channels[cur_chan]._buttonRect = QRect(x, line_y + RouteDialog::channelLinesMargin, RouteDialog::channelDotDiameter, RouteDialog::channelDotDiameter); ++cur_chan; x += RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing; if(cur_chan % RouteDialog::channelDotsPerGroup == 0) x += RouteDialog::channelDotGroupSpacing; } } } if(new_section) { x = x_orig; // Reset chan_y = line_y; line_y += RouteDialog::channelBarHeight; } else { x += RouteDialog::channelDotDiameter + RouteDialog::channelDotSpacing; if(new_group) x += RouteDialog::channelDotGroupSpacing; } } } bool RouteTreeWidgetItem::mousePressHandler(QMouseEvent* e, const QRect& rect) { const QPoint pt = e->pos(); const Qt::KeyboardModifiers km = e->modifiers(); bool ctl = false; switch(_itemMode) { case ExclusiveMode: ctl = false; break; case NormalMode: ctl = km & Qt::ControlModifier; break; } //bool shift = km & Qt::ShiftModifier; // RouteTreeWidgetItem* item = static_cast(itemAt(pt)); // bool is_cur = item && currentItem() && (item == currentItem()); //if(is_cur) // QTreeWidget::mousePressEvent(e); switch(type()) { case NormalItem: case CategoryItem: case RouteItem: break; case ChannelsItem: switch(_route.type) { case MusECore::Route::TRACK_ROUTE: if(_route.track && _route.channel != -1) { int ch = channelAt(pt, rect); const int ba_sz = _channels.size(); bool changed = false; //if(!ctl) { for(int i = 0; i < ba_sz; ++i) { if(i == ch) { if(ctl) { _channels[i].toggleSelected(); changed = true; } else { if(!_channels.at(i)._selected) changed = true; _channels[i]._selected = true; } } else if(!ctl) { if(_channels. at(i)._selected) changed = true; _channels[i]._selected = false; } } } //e->accept(); return changed; } break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: break; } break; } return false; // QTreeWidget::mousePressEvent(e); } bool RouteTreeWidgetItem::mouseMoveHandler(QMouseEvent* e, const QRect& rect) { const Qt::MouseButtons mb = e->buttons(); if(mb != Qt::LeftButton) return false; const QPoint pt = e->pos(); const Qt::KeyboardModifiers km = e->modifiers(); bool ctl = false; switch(_itemMode) { case ExclusiveMode: ctl = false; break; case NormalMode: //ctl = true; //ctl = km & Qt::ControlModifier; ctl = km & Qt::ShiftModifier; break; } //bool shift = km & Qt::ShiftModifier; // RouteTreeWidgetItem* item = static_cast(itemAt(pt)); // bool is_cur = item && currentItem() && (item == currentItem()); //if(is_cur) // QTreeWidget::mousePressEvent(e); switch(type()) { case NormalItem: case CategoryItem: case RouteItem: break; case ChannelsItem: switch(_route.type) { case MusECore::Route::TRACK_ROUTE: if(_route.track && _route.channel != -1) { int ch = channelAt(pt, rect); const int ba_sz = _channels.size(); bool changed = false; for(int i = 0; i < ba_sz; ++i) { if(i == ch) { { if(!_channels.at(i)._selected) changed = true; _channels[i]._selected = true; } } else if(!ctl) { if(_channels. at(i)._selected) changed = true; _channels[i]._selected = false; } } return changed; } break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: break; } break; } return false; // QTreeWidget::mousePressEvent(e); } bool RouteTreeWidgetItem::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { { if(index.column() == RouteDialog::ROUTE_NAME_COL) { RouteTreeWidget* rtw = qobject_cast(treeWidget()); if(!rtw) return false; switch(type()) { case ChannelsItem: { if(!treeWidget()->viewport()) return false; const int col_width = rtw->columnWidth(index.column()); const int view_width = rtw->viewport()->width(); const int chans = _channels.size(); const int view_offset = rtw->header()->offset(); const int x_offset = (_isInput ? //view_width - _channels.widthHint(rtw->wordWrap() ? view_width : -1) - view_offset : -view_offset); col_width - _channels.widthHint(rtw->channelWrap() ? col_width : -1) - view_offset : -view_offset); DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::paint() rect x:%d y:%d w:%d h:%d view_offset:%d x_offset:%d dev w:%d col w:%d header w:%d view w:%d\n", option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height(), view_offset, x_offset, painter->device()->width(), rtw->columnWidth(index.column()), rtw->header()->sectionSize(index.column()), view_width); // From QStyledItemDelegate::paint help: Necessary? // "After painting, you should ensure that the painter is returned to its the state it was supplied in when this function // was called. For example, it may be useful to call QPainter::save() before painting and QPainter::restore() afterwards." painter->save(); // Need to be able to paint beyond the right edge of the column width, // all the way to the view's right edge. //painter->setClipRect(option.rect); QRect clip_rect(option.rect); clip_rect.setWidth(view_width - option.rect.x()); painter->setClipRect(clip_rect); if(index.parent().isValid() && (index.parent().row() & 0x01)) painter->fillRect(option.rect, option.palette.alternateBase()); int cur_chan = 0; QPen pen; // Set a small five-pixel font size for the numbers inside the dots. QFont fnt = font(index.column()); //fnt.setStyleStrategy(QFont::NoAntialias); //fnt.setStyleStrategy(QFont::PreferBitmap); // -2 for the border, -2 for the margin, and +1 for setPixelSize which seems to like it. //fnt.setPixelSize(RouteDialog::channelDotDiameter - 2 - 2 + 1); fnt.setPixelSize(RouteDialog::channelDotDiameter / 2 + 1); painter->setFont(fnt); for(int i = 0; i < chans; ++i) { const RouteChannelsStruct& ch_struct = _channels.at(i); const QRect& ch_rect = ch_struct._buttonRect; QPainterPath path; path.addRoundedRect(x_offset + ch_rect.x(), option.rect.y() + ch_rect.y(), ch_rect.width(), ch_rect.height(), 30, 30); if(ch_struct._selected) painter->fillPath(path, option.palette.highlight()); //painter->setPen(ch_struct._selected ? option.palette.highlightedText().color() : option.palette.text().color()); //painter->setPen(ch_struct._routeSelected ? Qt::yellow : option.palette.text().color()); painter->setPen(option.palette.text().color()); painter->setRenderHint(QPainter::Antialiasing, true); painter->drawPath(path); //const int ch_num = (i + 1) % 10; if(chans > RouteDialog::channelDotsPerGroup) { //if((i % RouteDialog::channelDotsPerGroup) == 0 || ((i + 1) % 10 == 0)) if((i % RouteDialog::channelDotsPerGroup) == 0) { painter->setPen(ch_struct._selected ? option.palette.highlightedText().color() : option.palette.text().color()); painter->drawText(x_offset + ch_rect.x(), option.rect.y() + ch_rect.y(), ch_rect.width(), ch_rect.height(), Qt::AlignCenter, //QString::number((ch_num + 1) / 10)); QString::number(i + 1)); } } if(ch_struct._connected) { // Need sharp lines here. painter->setRenderHint(QPainter::Antialiasing, false); const int line_x = x_offset + ch_rect.x() + RouteDialog::channelDotDiameter / 2; const int line_y = option.rect.y() + ch_struct._lineY; if(_isInput) { const int ch_y = option.rect.y() + ch_rect.y() -1; DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::paint() input: line_x:%d ch_y:%d line_y:%d view_w:%d\n", line_x, ch_y, line_y, view_width); pen.setBrush((ch_struct._selected && !ch_struct._routeSelected) ? option.palette.highlight() : option.palette.text()); pen.setStyle(Qt::SolidLine); painter->setPen(pen); painter->drawLine(line_x, ch_y, line_x, line_y); painter->drawLine(line_x, line_y, view_width, line_y); if(ch_struct._routeSelected) { pen.setBrush(Qt::yellow); pen.setStyle(Qt::DotLine); painter->setPen(pen); painter->drawLine(line_x, ch_y, line_x, line_y); painter->drawLine(line_x, line_y, view_width, line_y); } } else { const int ch_y = option.rect.y() + ch_rect.y() + ch_rect.height(); pen.setBrush((ch_struct._selected && !ch_struct._routeSelected) ? option.palette.highlight() : option.palette.text()); pen.setStyle(Qt::SolidLine); painter->setPen(pen); painter->drawLine(line_x, ch_y, line_x, line_y); painter->drawLine(x_offset, line_y, line_x, line_y); if(ch_struct._routeSelected) { pen.setBrush(Qt::yellow); pen.setStyle(Qt::DotLine); painter->setPen(pen); painter->drawLine(line_x, ch_y, line_x, line_y); painter->drawLine(x_offset, line_y, line_x, line_y); } } ++cur_chan; } } painter->restore(); return true; } break; case CategoryItem: case RouteItem: { if(const QStyle* st = rtw->style()) { st = st->proxy(); painter->save(); painter->setClipRect(option.rect); const QRect cb_rect = st->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option); const QRect ico_rect = st->subElementRect(QStyle::SE_ItemViewItemDecoration, &option); const QRect text_rect = st->subElementRect(QStyle::SE_ItemViewItemText, &option); // Draw the row background (alternating colours etc.) QPalette::ColorGroup cg = (/* widget ? widget->isEnabled() : */ (option.state & QStyle::State_Enabled)) ? QPalette::Normal : QPalette::Disabled; if(cg == QPalette::Normal && !(option.state & QStyle::State_Active)) cg = QPalette::Inactive; if(!(flags() & Qt::ItemIsSelectable)) { // If it's not selectable then it must be a category item. Draw the category label background. // To support stylesheets, categoryColor is a property. // TODO: Support live stylesheet editing: How to reset to default if the stylesheet // or the 'categoryColor' statement goes away? We'll need to set up // some kind of notification BEFORE we set the app's stylesheet. const QColor cat_col = rtw->categoryColor(); if(cat_col.isValid()) painter->fillRect(option.rect, cat_col); else painter->fillRect(option.rect, option.palette.brush(cg, QPalette::Mid)); } else if((option.state & QStyle::State_Selected) && st->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, &option /*, widget*/)) painter->fillRect(option.rect, option.palette.brush(cg, QPalette::Highlight)); //else if(option.features & QStyleOptionViewItem::Alternate) // Hm, something else draws the alternating colours, no control over it here. // Disabled it in the UI so it does not interfere here. //else if(treeWidget()->alternatingRowColors() && (index.row() & 0x01)) else if((index.row() & 0x01)) painter->fillRect(option.rect, option.palette.brush(cg, QPalette::AlternateBase)); // Draw the item background. st->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter); // Draw the check mark if(option.features & QStyleOptionViewItem::HasCheckIndicator) { QStyleOptionViewItem opt(option); opt.rect = cb_rect; opt.state = opt.state & ~QStyle::State_HasFocus; switch(option.checkState) { case Qt::Unchecked: opt.state |= QStyle::State_Off; break; case Qt::PartiallyChecked: opt.state |= QStyle::State_NoChange; break; case Qt::Checked: opt.state |= QStyle::State_On; break; } st->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &opt, painter); } // Draw the icon. QIcon::Mode mode = QIcon::Normal; if(!(option.state & QStyle::State_Enabled)) mode = QIcon::Disabled; else if(option.state & QStyle::State_Selected) mode = QIcon::Selected; QIcon::State state = option.state & QStyle::State_Open ? QIcon::On : QIcon::Off; option.icon.paint(painter, ico_rect, option.decorationAlignment, mode, state); // Draw the text. st->drawItemText(painter, text_rect, //textAlignment(index.column()) | Qt::TextWordWrap | Qt::TextWrapAnywhere, option.displayAlignment | (rtw->wordWrap() ? (Qt::TextWordWrap | Qt::TextWrapAnywhere) : 0), //treeWidget()->palette(), option.palette, //!isDisabled(), option.state & QStyle::State_Enabled, //text(index.column()), rtw->wordWrap() ? option.text : option.fontMetrics.elidedText(option.text, rtw->textElideMode(), text_rect.width()), //isSelected() ? QPalette::HighlightedText : QPalette::Text (option.state & QStyle::State_Selected) ? QPalette::HighlightedText : QPalette::Text ); // Draw the focus. if(option.state & QStyle::State_HasFocus) { QStyleOptionFocusRect o; o.QStyleOption::operator=(option); o.rect = st->subElementRect(QStyle::SE_ItemViewItemFocusRect, &option); o.state |= QStyle::State_KeyboardFocusChange; o.state |= QStyle::State_Item; QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); st->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter); } painter->restore(); return true; } } break; case NormalItem: break; } } } return false; } QSize RouteTreeWidgetItem::getSizeHint(int column, int width) const { DEBUG_PRST_ROUTES(stderr, "RouteTreeWidgetItem::getSizeHint width:%d col:%d column width:%d\n", treeWidget()->width(), column, treeWidget()->columnWidth(column)); if(column == RouteDialog::ROUTE_NAME_COL) { RouteTreeWidget* rtw = qobject_cast(treeWidget()); if(!rtw) return QSize(); switch(type()) { case NormalItem: break; case ChannelsItem: //fprintf(stderr, "RouteTreeWidgetItem::getSizeHint ChannelsItem w:%d\n", width); return _channels.sizeHint(rtw->channelWrap() ? width : -1); break; case CategoryItem: case RouteItem: { if(!rtw->wordWrap()) return QSize(); if(const QStyle* st = rtw->style()) { st = st->proxy(); QStyleOptionViewItem vopt; vopt.features = QStyleOptionViewItem::None; vopt.text = text(column); vopt.rect = QRect(0, 0, rtw->wordWrap() ? width : _VERY_LARGE_INTEGER_, -1); vopt.displayAlignment = Qt::Alignment(textAlignment(column)); if(icon(column).isNull()) vopt.decorationSize = QSize(); else { vopt.features |= QStyleOptionViewItem::HasDecoration; vopt.decorationSize = rtw->iconSize(); vopt.icon = icon(column); } if(rtw->wordWrap()) vopt.features |= QStyleOptionViewItem::WrapText; vopt.features |= QStyleOptionViewItem::HasDisplay; vopt.font = font(column); vopt.fontMetrics = rtw->fontMetrics(); vopt.state = QStyle::State_Active; if(!isDisabled()) vopt.state |= QStyle::State_Enabled; if(flags() & Qt::ItemIsUserCheckable) { vopt.features |= QStyleOptionViewItem::HasCheckIndicator; vopt.checkState = checkState(column); if(checkState(column) == Qt::Unchecked) vopt.state |= QStyle::State_Off; else if(checkState(column) == Qt::Checked) vopt.state |= QStyle::State_On; } if(isSelected()) vopt.state |= QStyle::State_Selected; QSize ct_sz = st->sizeFromContents(QStyle::CT_ItemViewItem, &vopt, QSize(rtw->wordWrap() ? width : _VERY_LARGE_INTEGER_, -1)); const QRect text_rect = st->subElementRect(QStyle::SE_ItemViewItemText, &vopt); QRect r = st->itemTextRect(//treeWidget()->fontMetrics(), vopt.fontMetrics, text_rect, //textAlignment(column) | Qt::TextWordWrap | Qt::TextWrapAnywhere, vopt.displayAlignment | Qt::TextWordWrap | Qt::TextWrapAnywhere, //!isDisabled(), vopt.state & QStyle::State_Enabled, //text(column)); vopt.text); if(r.height() > ct_sz.height()) ct_sz.setHeight(r.height()); return ct_sz; } } break; } } return QSize(); } bool RouteTreeWidgetItem::testForRelayout(int column, int old_width, int new_width) { switch(type()) { case NormalItem: break; case CategoryItem: case RouteItem: { if(column == RouteDialog::ROUTE_NAME_COL) { // if(const QStyle* st = treeWidget()->style()) // { // st = st->proxy(); // // Works fine with TextWrapAnywhere. The -1 represents 'infinite' vertical space - // // itemTextRect doesn't seem to care in this case with wrap anywhere. // QRect old_r = st->itemTextRect(treeWidget()->fontMetrics(), // QRect(0, 0, old_width, -1), // textAlignment(RouteDialog::ROUTE_NAME_COL) | Qt::TextWordWrap | Qt::TextWrapAnywhere, // !isDisabled(), text(RouteDialog::ROUTE_NAME_COL)); // QRect new_r = st->itemTextRect(treeWidget()->fontMetrics(), // QRect(0, 0, new_width, -1), // textAlignment(RouteDialog::ROUTE_NAME_COL) | Qt::TextWordWrap | Qt::TextWrapAnywhere, // !isDisabled(), text(RouteDialog::ROUTE_NAME_COL)); // return new_r.height() != old_r.height(); // } // return new_sz.height() != old_sz.height(); //if(MusEGlobal::config.routerExpandVertically) if(!treeWidget()->wordWrap()) return false; return getSizeHint(column, new_width).height() != getSizeHint(column, old_width).height(); } } break; case ChannelsItem: { if(column == RouteDialog::ROUTE_NAME_COL) { // // If the width hints are different we must (at least) update the channels' button rectangles. // if(_channels.widthHint(new_width) != _channels.widthHint(old_width)) // computeChannelYValues(new_width); // // If the height hints are different we must trigger a relayout. // return _channels.heightHint(new_width) != _channels.heightHint(old_width); RouteTreeWidget* rtw = qobject_cast(treeWidget()); if(!rtw) return false; if(!rtw->channelWrap()) return false; const QSize old_sz = getSizeHint(column, old_width); const QSize new_sz = getSizeHint(column, new_width); // If the width hints are different we must (at least) update the channels' button rectangles. if(new_sz.width() != old_sz.width()) computeChannelYValues(new_width); // If the height hints are different we must trigger a relayout. return new_sz.height() != old_sz.height(); } } break; } return false; } bool RouteTreeWidgetItem::routeNodeExists() { switch(type()) { case CategoryItem: case NormalItem: return true; break; case RouteItem: case ChannelsItem: return _route.exists(); break; } return false; } //----------------------------------- // ConnectionsView //----------------------------------- ConnectionsView::ConnectionsView(QWidget* parent, RouteDialog* d) : QFrame(parent), _routeDialog(d) { lastY = 0; setMinimumWidth(20); //setMaximumWidth(120); setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); } ConnectionsView::~ConnectionsView() { } int ConnectionsView::itemY(RouteTreeWidgetItem* item, bool /*is_input*/, int channel) const { QRect rect; QTreeWidget* tree = item->treeWidget(); QTreeWidgetItem* top_closed = 0; QTreeWidgetItem* parent = item; while(parent) { parent = parent->parent(); if(!parent) break; if(!parent->isExpanded()) top_closed = parent; } const int line_width = _routeDialog->newSrcList->lineWidth(); //if(parent && !parent->isExpanded()) if(top_closed) { rect = tree->visualItemRect(top_closed); return line_width + rect.top() + rect.height() / 2; } // else // { rect = tree->visualItemRect(item); if(channel != -1) return line_width + rect.top() + item->channelYValue(channel); return line_width + rect.top() + rect.height() / 2; // } } void ConnectionsView::drawConnectionLine(QPainter* pPainter, int x1, int y1, int x2, int y2, int h1, int h2 ) { //DEBUG_PRST_ROUTES(stderr, "ConnectionsView::drawConnectionLine: x1:%d y1:%d x2:%d y2:%d h1:%d h2:%d\n", x1, y1, x2, y2, h1, h2); // Account for list view headers. y1 += h1; y2 += h2; // Invisible output ports don't get a connecting dot. if(y1 > h1) pPainter->drawLine(x1, y1, x1 + 4, y1); // if(1) { // Setup control points QPolygon spline(4); const int cp = int(float(x2 - x1 - 8) * 0.4f); spline.putPoints(0, 4, x1 + 4, y1, x1 + 4 + cp, y1, x2 - 4 - cp, y2, x2 - 4, y2); // The connection line, it self. QPainterPath path; path.moveTo(spline.at(0)); path.cubicTo(spline.at(1), spline.at(2), spline.at(3)); pPainter->strokePath(path, pPainter->pen()); } // else // pPainter->drawLine(x1 + 4, y1, x2 - 4, y2); // Invisible input ports don't get a connecting dot. if(y2 > h2) pPainter->drawLine(x2 - 4, y2, x2, y2); } void ConnectionsView::drawItem(QPainter* painter, QTreeWidgetItem* routesItem, const QColor& col) { const int yc = QWidget::pos().y(); const int yo = _routeDialog->newSrcList->pos().y(); const int yi = _routeDialog->newDstList->pos().y(); const int x1 = 0; const int x2 = QWidget::width(); const int h1 = (_routeDialog->newSrcList->header())->sizeHint().height(); const int h2 = (_routeDialog->newDstList->header())->sizeHint().height(); int y1; int y2; QPen pen; const int pen_wid_norm = 0; const int pen_wid_wide = 3; if(routesItem->data(RouteDialog::ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() && routesItem->data(RouteDialog::ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) { const MusECore::Route src = routesItem->data(RouteDialog::ROUTE_SRC_COL, RouteDialog::RouteRole).value(); const MusECore::Route dst = routesItem->data(RouteDialog::ROUTE_DST_COL, RouteDialog::RouteRole).value(); RouteTreeWidgetItem* srcItem = _routeDialog->newSrcList->findItem(src); if(srcItem) { RouteTreeWidgetItem* dstItem = _routeDialog->newDstList->findItem(dst); if(dstItem) { int src_chan = src.channel; int dst_chan = dst.channel; bool src_wid = false; bool dst_wid = false; switch(src.type) { case MusECore::Route::TRACK_ROUTE: // Don't draw if channel is unavailable. if(src_chan >= srcItem->channelCount()) return; if(src_chan == -1 && src.channels == -1) src_wid = true; break; case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: if(src_chan == -1 && src.channels == -1) src_wid = true; // Support port/device items (no channel bar) to track channel item routes: // Set source channel to -1 so it draws to the vertical middle of the item. src_chan = -1; break; case MusECore::Route::JACK_ROUTE: break; } switch(dst.type) { case MusECore::Route::TRACK_ROUTE: // Don't draw if channel is unavailable. if(dst_chan >= dstItem->channelCount()) return; if(dst_chan == -1 && dst.channels == -1) dst_wid = true; break; case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: if(dst_chan == -1 && dst.channels == -1) dst_wid = true; // Support track channel items to port/device items (no channel bar) routes: // Set dest channel to -1 so it draws to the vertical middle of the item. dst_chan = -1; break; case MusECore::Route::JACK_ROUTE: break; } if(src_wid && dst_wid) pen.setWidth(pen_wid_wide); else pen.setWidth(pen_wid_norm); pen.setColor(col); painter->setPen(pen); y1 = itemY(srcItem, true, src_chan) + (yo - yc); y2 = itemY(dstItem, false, dst_chan) + (yi - yc); drawConnectionLine(painter, x1, y1, x2, y2, h1, h2); } // else // { // fprintf(stderr, "ConnectionsView::drawItem: dstItem not found:\n"); // src.dump(); // dst.dump(); // } } // else // { // fprintf(stderr, "ConnectionsView::drawItem: srcItem not found:\n"); // src.dump(); // dst.dump(); // } } } // Draw visible port connection relation arrows. void ConnectionsView::paintEvent(QPaintEvent*) { //DEBUG_PRST_ROUTES(stderr, "ConnectionsView::paintEvent: _routeDialog:%p\n", _routeDialog); if(!_routeDialog) return; QPainter painter(this); // int i, rgb[3] = { 0x33, 0x66, 0x99 }; int i, rgb[3] = { 0x33, 0x58, 0x7f }; //int i, rgb[3] = { 0x00, 0x2c, 0x7f }; // Inline adaptive to darker background themes... if(QWidget::palette().window().color().value() < 0x7f) for (i = 0; i < 3; ++i) //rgb[i] += 0x33; //rgb[i] += 0x66; rgb[i] += 0x80; i = 0; const int iItemCount = _routeDialog->routeList->topLevelItemCount(); // Draw unselected items behind selected items. for(int iItem = 0; iItem < iItemCount; ++iItem, ++i) { QTreeWidgetItem* item = _routeDialog->routeList->topLevelItem(iItem); //++i; //if(!item) if(!item || item->isHidden() || item->isSelected()) continue; drawItem(&painter, item, QColor(rgb[i % 3], rgb[(i / 3) % 3], rgb[(i / 9) % 3], 128)); } // Draw selected items on top of unselected items. for(int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem* item = _routeDialog->routeList->topLevelItem(iItem); //if(!item) if(!item || item->isHidden() || !item->isSelected()) continue; drawItem(&painter, item, Qt::yellow); } } void ConnectionsView::mousePressEvent(QMouseEvent* e) { e->setAccepted(true); lastY = e->y(); } void ConnectionsView::mouseMoveEvent(QMouseEvent* e) { e->setAccepted(true); const Qt::MouseButtons mb = e->buttons(); const int y = e->y(); const int ly = lastY; lastY = y; if(mb & Qt::LeftButton) emit scrollBy(0, ly - y); } void ConnectionsView::wheelEvent(QWheelEvent* e) { e->accept(); const QPoint pixelDelta = e->pixelDelta(); const QPoint angleDegrees = e->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; DEBUG_PRST_ROUTES(stderr, "ConnectionsView::wheelEvent: delta:%d\n", delta); emit scrollBy(0, delta < 0 ? 1 : -1); } // Context menu request event handler. void ConnectionsView::contextMenuEvent(QContextMenuEvent* /*pContextMenuEvent*/) { } //----------------------------------- // RouteTreeWidget //----------------------------------- RouteTreeWidget::RouteTreeWidget(QWidget* parent, bool is_input) : QTreeWidget(parent), _isInput(is_input), _channelWrap(false) { if(is_input) setObjectName(QStringLiteral("Router_input_tree")); else setObjectName(QStringLiteral("Router_output_tree")); if(header()) connect(header(), SIGNAL(sectionResized(int,int,int)), SLOT(headerSectionResized(int,int,int))); } RouteTreeWidget::~RouteTreeWidget() { } void RouteTreeWidget::computeChannelYValues() { const int ch_w = channelWrap() ? columnWidth(RouteDialog::ROUTE_NAME_COL) : -1; QTreeWidgetItemIterator itw(this); while(*itw) { RouteTreeWidgetItem* item = static_cast(*itw); // item->computeChannelYValues(wordWrap() ? viewport()->width() : -1); //item->computeChannelYValues(wordWrap() ? columnWidth(RouteDialog::ROUTE_NAME_COL) : -1); //item->computeChannelYValues(MusEGlobal::config.routerExpandVertically ? columnWidth(RouteDialog::ROUTE_NAME_COL) : _VERY_LARGE_INTEGER_); //item->computeChannelYValues(wordWrap() ? columnWidth(RouteDialog::ROUTE_NAME_COL) : _VERY_LARGE_INTEGER_); item->computeChannelYValues(ch_w); ++itw; } } void RouteTreeWidget::headerSectionResized(int logicalIndex, int oldSize, int newSize) { DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::headerSectionResized idx:%d old sz:%d new sz:%d\n", logicalIndex, oldSize, newSize); //scheduleDelayedItemsLayout(); //if(!wordWrap()) // return; // Self adjust certain item heights... // NOTE: Delegate sizeHints are NOT called automatically. scheduleDelayedItemsLayout() seems to solve it. // But that is costly here! And results in some flickering especially at scrollbar on/off conditions as it fights with itself. // So check if we really need to do it... QTreeWidgetItemIterator ii(this); //bool do_layout = false; int relayouts = 0; while(*ii) { RouteTreeWidgetItem* item = static_cast(*ii); if(item->testForRelayout(logicalIndex, oldSize, newSize)) { const QModelIndex mdl_idx = indexFromItem(item); if(mdl_idx.isValid()) { QAbstractItemDelegate* id = itemDelegate(); if(RoutingItemDelegate* rid = qobject_cast(id)) { rid->emitSizeHintChanged(mdl_idx); ++relayouts; } } } ++ii; } if(relayouts) { //connectionsWidget->update(); // Redraw the connections. FIXME: TODO: Need to access the dialog } } RouteTreeWidgetItem* RouteTreeWidget::itemFromIndex(const QModelIndex& index) const { return static_cast(QTreeWidget::itemFromIndex(index)); } RouteTreeWidgetItem* RouteTreeWidget::findItem(const MusECore::Route& r, int type) { QTreeWidgetItemIterator ii(this); while(*ii) { QTreeWidgetItem* item = *ii; switch(item->type()) { case RouteTreeWidgetItem::NormalItem: case RouteTreeWidgetItem::CategoryItem: break; case RouteTreeWidgetItem::RouteItem: case RouteTreeWidgetItem::ChannelsItem: { RouteTreeWidgetItem* rtwi = static_cast(item); if((type == -1 || type == item->type()) && rtwi->route().compare(r)) return rtwi; } break; } ++ii; } return NULL; } RouteTreeWidgetItem* RouteTreeWidget::findCategoryItem(const QString& name) { const int cnt = topLevelItemCount(); for(int i = 0; i < cnt; ++i) { RouteTreeWidgetItem* item = static_cast(topLevelItem(i)); if(item && item->type() == RouteTreeWidgetItem::CategoryItem && item->text(RouteDialog::ROUTE_NAME_COL) == name) return item; } return 0; } void RouteTreeWidget::getSelectedRoutes(MusECore::RouteList& routes) { RouteTreeItemList sel = selectedItems(); const int selSz = sel.size(); if(selSz == 0) return; for(int idx = 0; idx < selSz; ++idx) { RouteTreeWidgetItem* item = static_cast(sel.at(idx)); if(!item) continue; item->getSelectedRoutes(routes); } } int RouteTreeWidget::channelAt(RouteTreeWidgetItem* item, const QPoint& pt) { const QRect rect = visualItemRect(item); return item->channelAt(pt, rect); } void RouteTreeWidget::resizeEvent(QResizeEvent* event) { DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::resizeEvent old w:%d h:%d new w:%d h:%d\n", event->oldSize().width(), event->oldSize().height(), event->size().width(), event->size().height()); event->ignore(); QTreeWidget::resizeEvent(event); //if(wordWrap()) //if(MusEGlobal::config.routerExpandVertically) // headerSectionResized(RouteDialog::ROUTE_NAME_COL, event->oldSize().width(), event->size().width()); // ZZZ } void RouteTreeWidget::mousePressEvent(QMouseEvent* e) { const QPoint pt = e->pos(); //Qt::KeyboardModifiers km = e->modifiers(); //bool ctl = km & Qt::ControlModifier; //bool shift = km & Qt::ShiftModifier; RouteTreeWidgetItem* item = static_cast(itemAt(pt)); bool is_cur = item && currentItem() && (item == currentItem()); //if(is_cur) // QTreeWidget::mousePressEvent(e); if(item) { bool changed = item->mousePressHandler(e, visualItemRect(item)); if(changed) { //setCurrentItem(item); //update(visualItemRect(item)); QRect r(visualItemRect(item)); // Need to update from the item's right edge to the viewport right edge, // for the connector lines. r.setRight(this->viewport()->geometry().right()); setDirtyRegion(r); //emit itemSelectionChanged(); } //if(!is_cur) QTreeWidget::mousePressEvent(e); if(changed && is_cur) //setCurrentItem(item); emit itemSelectionChanged(); //e->accept(); return; } QTreeWidget::mousePressEvent(e); } void RouteTreeWidget::mouseMoveEvent(QMouseEvent* e) { const QPoint pt = e->pos(); //Qt::KeyboardModifiers km = e->modifiers(); //bool ctl = km & Qt::ControlModifier; //bool shift = km & Qt::ShiftModifier; RouteTreeWidgetItem* item = static_cast(itemAt(pt)); bool is_cur = item && currentItem() && (item == currentItem()); //if(is_cur) // QTreeWidget::mouseMoveEvent(e); if(item) { bool changed = item->mouseMoveHandler(e, visualItemRect(item)); if(changed) { //setCurrentItem(item); //update(visualItemRect(item)); setDirtyRegion(visualItemRect(item)); //emit itemSelectionChanged(); } //if(!is_cur) QTreeWidget::mouseMoveEvent(e); if(changed && is_cur) //setCurrentItem(item); emit itemSelectionChanged(); //e->accept(); return; } QTreeWidget::mouseMoveEvent(e); } QItemSelectionModel::SelectionFlags RouteTreeWidget::selectionCommand(const QModelIndex& index, const QEvent* e) const { QItemSelectionModel::SelectionFlags flags = QTreeWidget::selectionCommand(index, e); DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::selectionCommand flags:%d row:%d col:%d ev type:%d\n", int(flags), index.row(), index.column(), e ? e->type() : -1); RouteTreeWidgetItem* item = itemFromIndex(index); if(item && item->type() == RouteTreeWidgetItem::ChannelsItem) { if(flags & QItemSelectionModel::Toggle) { flags &= ~QItemSelectionModel::Toggle; flags |= QItemSelectionModel::Select; DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::selectionCommand new flags:%d\n", int(flags)); } } return flags; } void RouteTreeWidget::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { QModelIndexList mil = deselected.indexes(); const int dsz = mil.size(); DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::selectionChanged: selected size:%d deselected size:%d\n", selected.size(), dsz); for(int i = 0; i < dsz; ++i) { const QModelIndex& index = mil.at(i); RouteTreeWidgetItem* item = itemFromIndex(index); if(item && item->type() == RouteTreeWidgetItem::ChannelsItem) item->fillSelectedChannels(false); } QTreeWidget::selectionChanged(selected, deselected); } void RouteTreeWidget::scrollBy(int dx, int dy) { DEBUG_PRST_ROUTES(stderr, "RouteTreeWidget::scrollBy: dx:%d dy:%d\n", dx, dy); int hv = horizontalScrollBar()->value(); int vv = verticalScrollBar()->value(); if(dx) { hv += dx; horizontalScrollBar()->setValue(hv); } if(dy) { vv += dy; verticalScrollBar()->setValue(vv); } } void RouteTreeWidget::getItemsToDelete(QVector& items_to_remove, bool showAllMidiPorts) { QTreeWidgetItemIterator ii(this); while(*ii) { QTreeWidgetItem* item = *ii; if(item) { QTreeWidgetItem* twi = item; while((twi = twi->parent())) { if(items_to_remove.contains(twi)) break; } // No parent found to be deleted. Determine if this should be deleted. if(!twi) { if(!items_to_remove.contains(item)) { RouteTreeWidgetItem* rtwi = static_cast(item); switch(rtwi->type()) { case RouteTreeWidgetItem::NormalItem: case RouteTreeWidgetItem::CategoryItem: case RouteTreeWidgetItem::ChannelsItem: break; case RouteTreeWidgetItem::RouteItem: { const MusECore::Route& rt = rtwi->route(); switch(rt.type) { case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: break; case MusECore::Route::MIDI_PORT_ROUTE: { bool remove_port = false; if(!rt.isValid()) remove_port = true; else if(!showAllMidiPorts) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[rt.midiPort]; if(!mp->device() && (_isInput ? mp->outRoutes()->empty() : mp->inRoutes()->empty())) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(!_isInput) { MusECore::MidiTrackList* tl = MusEGlobal::song->midis(); MusECore::ciMidiTrack imt = tl->begin(); for( ; imt != tl->end(); ++imt) if((*imt)->outPort() == rt.midiPort) break; if(imt == tl->end()) remove_port = true; } else #endif // _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ remove_port = true; } } if(remove_port) items_to_remove.append(item); ++ii; continue; } break; } } break; } if(!rtwi->routeNodeExists()) items_to_remove.append(item); } } } ++ii; } } void RouteTreeWidget::selectRoutes(const QList& routes, bool doNormalSelections) { QTreeWidgetItemIterator ii(this); while(*ii) { RouteTreeWidgetItem* rtwi = static_cast(*ii); switch(rtwi->type()) { case RouteTreeWidgetItem::NormalItem: case RouteTreeWidgetItem::CategoryItem: case RouteTreeWidgetItem::RouteItem: break; case RouteTreeWidgetItem::ChannelsItem: { bool do_upd = rtwi->fillChannelsRouteSelected(false); if(doNormalSelections && rtwi->fillSelectedChannels(false)) do_upd = true; const MusECore::Route& rtwi_route = rtwi->route(); const int sz = routes.size(); for(int i = 0; i < sz; ++i) { const QTreeWidgetItem* routes_item = routes.at(i); const MusECore::Route r = routes_item->data(isInput() ? RouteDialog::ROUTE_SRC_COL : RouteDialog::ROUTE_DST_COL, RouteDialog::RouteRole).value(); if(rtwi_route.compare(r)) { const int chan = r.channel; if(chan >= 0) { //if(!rtwi->channelRouteSelected(chan)) //{ rtwi->routeSelectChannel(chan, true); do_upd = true; //} //if(doNormalSelections && !rtwi->channelSelected(chan)) if(doNormalSelections) { rtwi->selectChannel(chan, true); do_upd = true; } } } } if(do_upd) { QRect r(visualItemRect(rtwi)); // Need to update from the item's right edge to the viewport right edge, // for the connector lines. r.setRight(this->viewport()->geometry().right()); setDirtyRegion(r); } } break; } ++ii; } } //----------------------------------- // RoutingItemDelegate //----------------------------------- RoutingItemDelegate::RoutingItemDelegate(bool is_input, RouteTreeWidget* tree, QWidget *parent) : QStyledItemDelegate(parent), _tree(tree), _isInput(is_input) { _firstPress = true; } void RoutingItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { // DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::paint row:%d col:%d, rect x:%d y:%d w:%d h:%d showDecorationSelected:%d\n", // index.row(), index.column(), // option.rect.x(), option.rect.y(), option.rect.width(), option.rect.height(), // option.showDecorationSelected); RouteTreeWidgetItem* item = _tree->itemFromIndex(index); if(item) { // Required. option is not automatically filled from index. QStyleOptionViewItem vopt(option); initStyleOption(&vopt, index); if(item->paint(painter, vopt, index)) return; } QStyledItemDelegate::paint(painter, option, index); } void RoutingItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::setModelData\n"); switch(index.column()) { default: QStyledItemDelegate::setModelData(editor, model, index); } } QSize RoutingItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { DEBUG_PRST_ROUTES(stderr, "RoutingItemDelegate::sizeHint\n"); if(RouteTreeWidgetItem* item = _tree->itemFromIndex(index)) { const QSize sz = item->getSizeHint(index.column(), _tree->columnWidth(RouteDialog::ROUTE_NAME_COL)); if(sz.isValid()) { //fprintf(stderr, "RoutingItemDelegate::sizeHint w:%d h:%d\n", sz.width(), sz.height()); return sz; } } return QStyledItemDelegate::sizeHint(option, index); } bool RoutingItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { return QStyledItemDelegate::editorEvent(event, model, option, index); } bool RoutingItemDelegate::eventFilter(QObject* editor, QEvent* event) { return QStyledItemDelegate::eventFilter(editor, event); } //--------------------------------------------------------- // RouteDialog //--------------------------------------------------------- RouteDialog::RouteDialog(QWidget* parent) : QDialog(parent) { setupUi(this); // newSrcList->setWordWrap(false); // newDstList->setWordWrap(false); // routeList->setWordWrap(false); // newSrcList->setTextElideMode(Qt::ElideNone); // newDstList->setTextElideMode(Qt::ElideNone); // routeList->setTextElideMode(Qt::ElideNone); filterSrcButton->setIcon(*routeSourceSVGIcon); filterDstButton->setIcon(*routeDestSVGIcon); srcRoutesButton->setIcon(*routeSelSourceSVGIcon); dstRoutesButton->setIcon(*routeSelDestSVGIcon); allMidiPortsButton->setIcon(*ankerSVGIcon); verticalLayoutButton->setIcon(*routeAutoAdjustSVGIcon); routeAliasList->addItem(tr("Normal"), QVariant::fromValue(MusEGlobal::RoutePreferCanonicalName)); routeAliasList->addItem(tr("Alias 1"), QVariant::fromValue(MusEGlobal::RoutePreferFirstAlias)); routeAliasList->addItem(tr("Alias 2"), QVariant::fromValue(MusEGlobal::RoutePreferSecondAlias)); // verticalLayoutButton->setChecked(newSrcList->wordWrap() || newDstList->wordWrap()); //newSrcList->viewport()->setLayoutDirection(Qt::LeftToRight); //_srcFilterItem = NULL; //_dstFilterItem = NULL; srcItemDelegate = new RoutingItemDelegate(true, newSrcList, this); dstItemDelegate = new RoutingItemDelegate(false, newDstList, this); newSrcList->setItemDelegate(srcItemDelegate); newDstList->setItemDelegate(dstItemDelegate); //newSrcList->setItemsExpandable(false); // REMOVE Tim. For test only. //newDstList->setItemsExpandable(false); // REMOVE Tim. For test only. connectionsWidget->setRouteDialog(this); QStringList columnnames; columnnames << tr("Source"); newSrcList->setColumnCount(columnnames.size()); newSrcList->setHeaderLabels(columnnames); for (int i = 0; i < columnnames.size(); ++i) { //setWhatsThis(newSrcList->horizontalHeaderItem(i), i); //setToolTip(newSrcList->horizontalHeaderItem(i), i); } columnnames.clear(); columnnames << tr("Destination"); newDstList->setColumnCount(columnnames.size()); newDstList->setHeaderLabels(columnnames); for (int i = 0; i < columnnames.size(); ++i) { //setWhatsThis(newDstList->horizontalHeaderItem(i), i); //setToolTip(newDstList->horizontalHeaderItem(i), i); } newSrcList->setTreePosition(1); newDstList->setTreePosition(1); // Need this. Don't remove. // newSrcList->header()->setSectionResizeMode(QHeaderView::Stretch); // newDstList->header()->setSectionResizeMode(QHeaderView::Stretch); // newSrcList->header()->setSectionResizeMode(QHeaderView::Interactive); // newDstList->header()->setSectionResizeMode(QHeaderView::Interactive); //newSrcList->header()->setStretchLastSection(false); //newDstList->header()->setStretchLastSection(false); newSrcList->setTextElideMode(Qt::ElideMiddle); newDstList->setTextElideMode(Qt::ElideMiddle); columnnames.clear(); columnnames << tr("Source") << tr("Destination"); routeList->setColumnCount(columnnames.size()); routeList->setHeaderLabels(columnnames); for (int i = 0; i < columnnames.size(); ++i) { //setWhatsThis(routeList->horizontalHeaderItem(i), i); //setToolTip(routeList->horizontalHeaderItem(i), i); } // Make it so that the column(s) cannot be shrunk below the size of one group of channels in a ChannelsItem. newSrcList->header()->setMinimumSectionSize(RouteChannelsList::minimumWidthHint()); newDstList->header()->setMinimumSectionSize(RouteChannelsList::minimumWidthHint()); verticalLayoutButton->setChecked(MusEGlobal::config.routerExpandVertically); if(MusEGlobal::config.routerExpandVertically) { // newSrcList->resizeColumnToContents(ROUTE_NAME_COL); // newDstList->resizeColumnToContents(ROUTE_NAME_COL); newSrcList->setWordWrap(true); newDstList->setWordWrap(true); newSrcList->setChannelWrap(true); newDstList->setChannelWrap(true); newSrcList->header()->setSectionResizeMode(QHeaderView::Stretch); newDstList->header()->setSectionResizeMode(QHeaderView::Stretch); newSrcList->setColumnWidth(ROUTE_NAME_COL, RouteChannelsList::minimumWidthHint()); newDstList->setColumnWidth(ROUTE_NAME_COL, RouteChannelsList::minimumWidthHint()); } else { newSrcList->setWordWrap(false); newDstList->setWordWrap(false); newSrcList->setChannelWrap(true); newDstList->setChannelWrap(true); newSrcList->header()->setSectionResizeMode(QHeaderView::Interactive); newDstList->header()->setSectionResizeMode(QHeaderView::Interactive); } songChanged(SC_EVERYTHING); connect(newSrcList->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), srcTreeScrollBar, SLOT(setRange(int,int))); connect(newDstList->verticalScrollBar(), SIGNAL(rangeChanged(int,int)), dstTreeScrollBar, SLOT(setRange(int,int))); connect(newSrcList->verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(srcTreeScrollValueChanged(int))); connect(newDstList->verticalScrollBar(), SIGNAL(valueChanged(int)), SLOT(dstTreeScrollValueChanged(int))); connect(srcTreeScrollBar, SIGNAL(valueChanged(int)), SLOT(srcScrollBarValueChanged(int))); connect(dstTreeScrollBar, SIGNAL(valueChanged(int)), SLOT(dstScrollBarValueChanged(int))); connect(routeList, SIGNAL(itemSelectionChanged()), SLOT(routeSelectionChanged())); connect(newSrcList, SIGNAL(itemSelectionChanged()), SLOT(srcSelectionChanged())); connect(newDstList, SIGNAL(itemSelectionChanged()), SLOT(dstSelectionChanged())); //connect(newSrcList->verticalScrollBar(), SIGNAL(sliderMoved(int)), connectionsWidget, SLOT(update())); //connect(newDstList->verticalScrollBar(), SIGNAL(sliderMoved(int)), connectionsWidget, SLOT(update())); connect(newSrcList->verticalScrollBar(), SIGNAL(valueChanged(int)), connectionsWidget, SLOT(update())); connect(newDstList->verticalScrollBar(), SIGNAL(valueChanged(int)), connectionsWidget, SLOT(update())); connect(newSrcList, SIGNAL(itemCollapsed(QTreeWidgetItem*)), connectionsWidget, SLOT(update())); connect(newSrcList, SIGNAL(itemExpanded(QTreeWidgetItem*)), connectionsWidget, SLOT(update())); connect(newDstList, SIGNAL(itemCollapsed(QTreeWidgetItem*)), connectionsWidget, SLOT(update())); connect(newDstList, SIGNAL(itemExpanded(QTreeWidgetItem*)), connectionsWidget, SLOT(update())); connect(connectionsWidget, SIGNAL(scrollBy(int, int)), newSrcList, SLOT(scrollBy(int, int))); connect(connectionsWidget, SIGNAL(scrollBy(int, int)), newDstList, SLOT(scrollBy(int, int))); connect(removeButton, SIGNAL(clicked()), SLOT(disconnectClicked())); connect(connectButton, SIGNAL(clicked()), SLOT(connectClicked())); connect(allMidiPortsButton, SIGNAL(clicked(bool)), SLOT(allMidiPortsClicked(bool))); connect(verticalLayoutButton, SIGNAL(clicked(bool)), SLOT(verticalLayoutClicked(bool))); connect(filterSrcButton, SIGNAL(clicked(bool)), SLOT(filterSrcClicked(bool))); connect(filterDstButton, SIGNAL(clicked(bool)), SLOT(filterDstClicked(bool))); connect(srcRoutesButton, SIGNAL(clicked(bool)), SLOT(filterSrcRoutesClicked(bool))); connect(dstRoutesButton, SIGNAL(clicked(bool)), SLOT(filterDstRoutesClicked(bool))); connect(routeAliasList, SIGNAL(activated(int)), SLOT(preferredRouteAliasChanged(int))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); } void RouteDialog::srcTreeScrollValueChanged(int value) { // Prevent recursion srcTreeScrollBar->blockSignals(true); srcTreeScrollBar->setValue(value); srcTreeScrollBar->blockSignals(false); } void RouteDialog::dstTreeScrollValueChanged(int value) { // Prevent recursion dstTreeScrollBar->blockSignals(true); dstTreeScrollBar->setValue(value); dstTreeScrollBar->blockSignals(false); } void RouteDialog::srcScrollBarValueChanged(int value) { // Prevent recursion newSrcList->blockSignals(true); newSrcList->verticalScrollBar()->setValue(value); newSrcList->blockSignals(false); } void RouteDialog::dstScrollBarValueChanged(int value) { // Prevent recursion newDstList->blockSignals(true); newDstList->verticalScrollBar()->setValue(value); newDstList->blockSignals(false); } // void RouteDialog::routeSplitterMoved(int pos, int index) // { // DEBUG_PRST_ROUTES(stderr, "RouteDialog::routeSplitterMoved pos:%d index:%d\n", pos, index); // // } void RouteDialog::preferredRouteAliasChanged(int /*idx*/) { if(routeAliasList->currentData().canConvert()) { bool ok = false; const int n = routeAliasList->currentData().toInt(&ok); if(ok) { switch(n) { case MusEGlobal::RoutePreferCanonicalName: case MusEGlobal::RoutePreferFirstAlias: case MusEGlobal::RoutePreferSecondAlias: MusEGlobal::config.preferredRouteNameOrAlias = MusEGlobal::RouteNameAliasPreference(n); MusEGlobal::song->update(SC_PORT_ALIAS_PREFERENCE); break; default: break; } } } } void RouteDialog::verticalLayoutClicked(bool v) { if(v) { // fprintf(stderr, "RouteDialog::verticalLayoutClicked v:%d calling src resizeColumnToContents\n", v); // newSrcList->resizeColumnToContents(ROUTE_NAME_COL); // fprintf(stderr, "RouteDialog::verticalLayoutClicked v:%d calling dst resizeColumnToContents\n", v); // newDstList->resizeColumnToContents(ROUTE_NAME_COL); MusEGlobal::config.routerExpandVertically = v; newSrcList->setWordWrap(v); newDstList->setWordWrap(v); newSrcList->setChannelWrap(v); newDstList->setChannelWrap(v); newSrcList->header()->setSectionResizeMode(QHeaderView::Stretch); newDstList->header()->setSectionResizeMode(QHeaderView::Stretch); newSrcList->setColumnWidth(ROUTE_NAME_COL, RouteChannelsList::minimumWidthHint()); newDstList->setColumnWidth(ROUTE_NAME_COL, RouteChannelsList::minimumWidthHint()); } else { MusEGlobal::config.routerExpandVertically = v; newSrcList->setWordWrap(v); newDstList->setWordWrap(v); newSrcList->setChannelWrap(true); newDstList->setChannelWrap(true); newSrcList->header()->setSectionResizeMode(QHeaderView::Interactive); newDstList->header()->setSectionResizeMode(QHeaderView::Interactive); } //fprintf(stderr, "RouteDialog::verticalLayoutClicked v:%d calling dst computeChannelYValues\n", v); newDstList->computeChannelYValues(); newSrcList->computeChannelYValues(); //fprintf(stderr, "RouteDialog::verticalLayoutClicked v:%d calling src computeChannelYValues\n", v); connectionsWidget->update(); // Redraw the connections. } void RouteDialog::allMidiPortsClicked(bool v) { // TODO: This is a bit brutal and sweeping... Refine this down to needed parts only. // routingChanged(); // Refill the lists of available external ports. // tmpJackOutPorts = MusEGlobal::audioDevice->outputPorts(); // tmpJackInPorts = MusEGlobal::audioDevice->inputPorts(); // tmpJackMidiOutPorts = MusEGlobal::audioDevice->outputPorts(true); // tmpJackMidiInPorts = MusEGlobal::audioDevice->inputPorts(true); if(v) addItems(); // Add any new items. else removeItems(); // Remove unused items. // newSrcList->resizeColumnToContents(ROUTE_NAME_COL); // newDstList->resizeColumnToContents(ROUTE_NAME_COL); routeList->resizeColumnToContents(ROUTE_SRC_COL); routeList->resizeColumnToContents(ROUTE_DST_COL); // Now that column resizing is done, update all channel y values in source and destination lists. // Must be done here because it relies on the column width. newDstList->computeChannelYValues(); newSrcList->computeChannelYValues(); routeSelectionChanged(); // Init remove button. srcSelectionChanged(); // Init select button. connectionsWidget->update(); // Redraw the connections. } void RouteDialog::filterSrcClicked(bool v) { if(dstRoutesButton->isChecked()) { dstRoutesButton->blockSignals(true); dstRoutesButton->setChecked(false); dstRoutesButton->blockSignals(false); } filter(v ? newSrcList->selectedItems() : RouteTreeItemList(), RouteTreeItemList(), true, false); } void RouteDialog::filterDstClicked(bool v) { if(srcRoutesButton->isChecked()) { srcRoutesButton->blockSignals(true); srcRoutesButton->setChecked(false); srcRoutesButton->blockSignals(false); } filter(RouteTreeItemList(), v ? newDstList->selectedItems() : RouteTreeItemList(), false, true); } void RouteDialog::filterSrcRoutesClicked(bool /*v*/) { if(dstRoutesButton->isChecked()) { dstRoutesButton->blockSignals(true); dstRoutesButton->setChecked(false); dstRoutesButton->blockSignals(false); } if(filterDstButton->isChecked()) { filterDstButton->blockSignals(true); filterDstButton->setChecked(false); filterDstButton->blockSignals(false); } // Unfilter the entire destination list, while (un)filtering with the 'show only possible routes' part. filter(RouteTreeItemList(), RouteTreeItemList(), false, true); } void RouteDialog::filterDstRoutesClicked(bool /*v*/) { if(srcRoutesButton->isChecked()) { srcRoutesButton->blockSignals(true); srcRoutesButton->setChecked(false); srcRoutesButton->blockSignals(false); } if(filterSrcButton->isChecked()) { filterSrcButton->blockSignals(true); filterSrcButton->setChecked(false); filterSrcButton->blockSignals(false); } // Unfilter the entire source list, while (un)filtering with the 'show only possible routes' part. filter(RouteTreeItemList(), RouteTreeItemList(), true, false); } void RouteDialog::filter(const RouteTreeItemList& srcFilterItems, const RouteTreeItemList& dstFilterItems, bool filterSrc, bool filterDst) { bool src_changed = false; bool dst_changed = false; const RouteTreeItemList src_sel_items = newSrcList->selectedItems(); const RouteTreeItemList dst_sel_items = newDstList->selectedItems(); bool hide; QTreeWidgetItemIterator iSrcTree(newSrcList); while(*iSrcTree) { QTreeWidgetItem* item = *iSrcTree; hide = item->isHidden(); if(filterSrc) { hide = false; if(!srcFilterItems.isEmpty()) { RouteTreeItemList::const_iterator ciFilterItems = srcFilterItems.cbegin(); for( ; ciFilterItems != srcFilterItems.cend(); ++ciFilterItems) { QTreeWidgetItem* flt_item = *ciFilterItems; QTreeWidgetItem* twi = flt_item; while(twi != item && (twi = twi->parent())) ; if(twi == item) break; QTreeWidgetItem* twi_p = item; while(twi_p != flt_item && (twi_p = twi_p->parent())) ; if(twi_p == flt_item) break; } hide = ciFilterItems == srcFilterItems.cend(); } } //else if(!filterSrc || srcFilterItems.isEmpty()) { hide = false; //// Is the item slated to hide? Check finally the 'show only possible routes' settings... //if(hide && dstRoutesButton->isChecked()) // Check finally the 'show only possible routes' settings... if(dstRoutesButton->isChecked()) { switch(item->type()) { case RouteTreeWidgetItem::NormalItem: case RouteTreeWidgetItem::CategoryItem: hide = true; break; case RouteTreeWidgetItem::RouteItem: case RouteTreeWidgetItem::ChannelsItem: { RouteTreeWidgetItem* rtwi = static_cast(item); RouteTreeItemList::const_iterator iSelItems = dst_sel_items.cbegin(); for( ; iSelItems != dst_sel_items.cend(); ++iSelItems) { RouteTreeWidgetItem* sel_dst_item = static_cast(*iSelItems); //if(sel_dst_item->type() == item->type() && MusECore::routesCompatible(rtwi->route(), sel_dst_item->route(), false)) if(MusECore::routesCompatible(rtwi->route(), sel_dst_item->route(), true)) { //hide = false; // Hm, Qt doesn't seem to do this for us: QTreeWidgetItem* twi = item; while((twi = twi->parent())) { //if(twi->isHidden() != hide) if(twi->isHidden()) { //twi->setHidden(hide); twi->setHidden(false); src_changed = true; } } break; } } hide = iSelItems == dst_sel_items.cend(); } break; } } } if(item->isHidden() != hide) { item->setHidden(hide); src_changed = true; } ++iSrcTree; } QTreeWidgetItemIterator iDstTree(newDstList); while(*iDstTree) { QTreeWidgetItem* item = *iDstTree; hide = item->isHidden(); if(filterDst) { hide = false; if(!dstFilterItems.isEmpty()) { RouteTreeItemList::const_iterator ciFilterItems = dstFilterItems.cbegin(); for( ; ciFilterItems != dstFilterItems.cend(); ++ciFilterItems) { QTreeWidgetItem* flt_item = *ciFilterItems; QTreeWidgetItem* twi = flt_item; while(twi != item && (twi = twi->parent())) ; if(twi == item) break; QTreeWidgetItem* twi_p = item; while(twi_p != flt_item && (twi_p = twi_p->parent())) ; if(twi_p == flt_item) break; } hide = ciFilterItems == dstFilterItems.cend(); } } //else if(!filterDst || dstFilterItems.isEmpty()) { hide = false; //// Is the item slated to hide? Check finally the 'show only possible routes' settings... //if(hide && srcRoutesButton->isChecked()) // Check finally the 'show only possible routes' settings... if(srcRoutesButton->isChecked()) { switch(item->type()) { case RouteTreeWidgetItem::NormalItem: case RouteTreeWidgetItem::CategoryItem: hide = true; break; case RouteTreeWidgetItem::RouteItem: case RouteTreeWidgetItem::ChannelsItem: { RouteTreeWidgetItem* rtwi = static_cast(item); RouteTreeItemList::const_iterator iSelItems = src_sel_items.cbegin(); for( ; iSelItems != src_sel_items.cend(); ++iSelItems) { RouteTreeWidgetItem* sel_src_item = static_cast(*iSelItems); MusECore::Route& src = sel_src_item->route(); MusECore::Route& dst = rtwi->route(); #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist // but not in any track or port routelist) until multiple output routes are allowed // instead of just single port and channel properties. The route is exclusive. bool is_compatible = false; switch(src.type) { case MusECore::Route::TRACK_ROUTE: switch(dst.type) { case MusECore::Route::MIDI_PORT_ROUTE: if(src.track->isMidiTrack()) is_compatible = true; break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: break; } break; case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: break; } if(is_compatible || #else if( #endif MusECore::routesCompatible(sel_src_item->route(), rtwi->route(), true)) { //hide = false; // Hm, Qt doesn't seem to do this for us: QTreeWidgetItem* twi = item; while((twi = twi->parent())) { //if(twi->isHidden() != hide) if(twi->isHidden()) { //twi->setHidden(hide); twi->setHidden(false); dst_changed = true; } } break; } } hide = iSelItems == src_sel_items.cend(); } break; } } } if(item->isHidden() != hide) { item->setHidden(hide); dst_changed = true; } ++iDstTree; } // Update the connecting lines. if(src_changed) { // QTreeWidgetItemIterator iSrcList(newSrcList); // while(*iSrcList) // { // RouteTreeWidgetItem* item = static_cast(*iSrcList); // item->computeChannelYValues(); // ++iSrcList; // } newSrcList->computeChannelYValues(); } if(dst_changed) { // QTreeWidgetItemIterator iDstList(newDstList); // while(*iDstList) // { // RouteTreeWidgetItem* item = static_cast(*iDstList); // item->computeChannelYValues(); // ++iDstList; // } newDstList->computeChannelYValues(); } if(src_changed || dst_changed) connectionsWidget->update(); // Redraw the connections. QTreeWidgetItemIterator iRouteTree(routeList); // //RouteTreeItemList::iterator iFilterItems; // //filterItems::iterator iFilterItems; // while(*iRouteTree) { QTreeWidgetItem* item = *iRouteTree; //if(item->data(isDestination ? RouteDialog::ROUTE_DST_COL : RouteDialog::ROUTE_SRC_COL, // RouteDialog::RouteRole).canConvert()) if(item && item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() && item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) { //const MusECore::Route r = item->data(isDestination ? RouteDialog::ROUTE_DST_COL : RouteDialog::ROUTE_SRC_COL, // RouteDialog::RouteRole).value(); const MusECore::Route src = item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value(); const MusECore::Route dst = item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value(); // bool hide = false; // // //if(_srcFilterItems.isEmpty()) // // item->setHidden(false); // //else // //{ // RouteTreeItemList::const_iterator ciFilterItems = _srcFilterItems.begin(); // for( ; ciFilterItems != _srcFilterItems.end(); ++ciFilterItems) // if(src.compare(static_cast(*ciFilterItems)->route())) // FIXME Ugly // break; // if(ciFilterItems == _srcFilterItems.end()) // hide = true; // item->setHidden(ciFilterItems == filterItems.end()); // //} RouteTreeWidgetItem* src_item = newSrcList->findItem(src); RouteTreeWidgetItem* dst_item = newDstList->findItem(dst); item->setHidden((src_item && src_item->isHidden()) || (dst_item && dst_item->isHidden())); } ++iRouteTree; } //routingChanged(); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void RouteDialog::songChanged(MusECore::SongChangedStruct_t v) { if(v & SC_PORT_ALIAS_PREFERENCE) { const int idx = routeAliasList->findData(QVariant::fromValue(MusEGlobal::config.preferredRouteNameOrAlias)); if(idx != -1 && idx != routeAliasList->currentIndex()) { routeAliasList->blockSignals(true); routeAliasList->setCurrentIndex(idx); routeAliasList->blockSignals(false); } } if(v & (SC_ROUTE | SC_CONFIG)) { // Refill the lists of available external ports. tmpJackOutPorts = MusEGlobal::audioDevice->outputPorts(); tmpJackInPorts = MusEGlobal::audioDevice->inputPorts(); tmpJackMidiOutPorts = MusEGlobal::audioDevice->outputPorts(true); tmpJackMidiInPorts = MusEGlobal::audioDevice->inputPorts(true); } if(v & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_ROUTE | SC_CONFIG | SC_CHANNELS | SC_PORT_ALIAS_PREFERENCE)) { removeItems(); // Remove unused items. addItems(); // Add any new items. routeList->resizeColumnToContents(ROUTE_SRC_COL); routeList->resizeColumnToContents(ROUTE_DST_COL); // Now that column resizing is done, update all channel y values in source and destination lists. newDstList->computeChannelYValues(); newSrcList->computeChannelYValues(); // newDstList->scheduleDelayedLayout(); // newSrcList->scheduleDelayedLayout(); routeSelectionChanged(); // Init remove button. srcSelectionChanged(); // Init select button. connectionsWidget->update(); // Redraw the connections. } } //--------------------------------------------------------- // routeSelectionChanged //--------------------------------------------------------- void RouteDialog::routeSelectionChanged() { QTreeWidgetItem* item = routeList->currentItem(); if(item == 0) { connectButton->setEnabled(false); removeButton->setEnabled(false); return; } if(!item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() || !item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) { connectButton->setEnabled(false); removeButton->setEnabled(false); return; } const MusECore::Route src = item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value(); const MusECore::Route dst = item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value(); RouteTreeWidgetItem* srcItem = newSrcList->findItem(src); RouteTreeWidgetItem* dstItem = newDstList->findItem(dst); newSrcList->blockSignals(true); newSrcList->setCurrentItem(srcItem); newSrcList->blockSignals(false); newDstList->blockSignals(true); newDstList->setCurrentItem(dstItem); newDstList->blockSignals(false); selectRoutes(true); if(srcItem) newSrcList->scrollToItem(srcItem, QAbstractItemView::PositionAtCenter); //newSrcList->scrollToItem(srcItem, QAbstractItemView::EnsureVisible); if(dstItem) newDstList->scrollToItem(dstItem, QAbstractItemView::PositionAtCenter); //newDstList->scrollToItem(dstItem, QAbstractItemView::EnsureVisible); connectionsWidget->update(); // connectButton->setEnabled(MusECore::routeCanConnect(src, dst)); connectButton->setEnabled(false); // removeButton->setEnabled(MusECore::routeCanDisconnect(src, dst)); // removeButton->setEnabled(true); #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist // but not in any track or port routelist) until multiple output routes are allowed // instead of just single port and channel properties. The route is exclusive. switch(src.type) { case MusECore::Route::TRACK_ROUTE: switch(dst.type) { case MusECore::Route::MIDI_PORT_ROUTE: if(src.track->isMidiTrack()) { MusECore::MidiTrack* mt = static_cast(src.track); // We cannot 'remove' a simulated midi track output port and channel route. // (Midi port cannot be -1 meaning 'no port'.) // Only remove it if it's a different port or channel. removeButton->setEnabled(mt->outPort() != dst.midiPort || mt->outChannel() != src.channel); return; } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: break; } break; case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: break; } #endif removeButton->setEnabled(true); } //--------------------------------------------------------- // disconnectClicked //--------------------------------------------------------- void RouteDialog::disconnectClicked() { MusECore::PendingOperationList operations; QTreeWidgetItemIterator ii(routeList); while(*ii) { QTreeWidgetItem* item = *ii; if(item && item->isSelected() && item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() && item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) { const MusECore::Route src = item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value(); const MusECore::Route dst = item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value(); //if(MusECore::routeCanDisconnect(src, dst)) // operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist // but not in any track or port routelist) until multiple output routes are allowed // instead of just single port and channel properties. The route is exclusive. switch(src.type) { case MusECore::Route::TRACK_ROUTE: switch(dst.type) { case MusECore::Route::MIDI_PORT_ROUTE: if(src.track->isMidiTrack()) { // MusECore::MidiTrack* mt = static_cast(src.track); // We cannot 'remove' a simulated midi track output port and channel route. // (Midi port cannot be -1 meaning 'no port'.) // Only remove it if it's a different port or channel. // if(mt->outPort() != dst.midiPort || mt->outChannel() != src.channel) // operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); ++ii; continue; } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: break; } break; case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: break; } #endif operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); } ++ii; } if(!operations.empty()) { MusEGlobal::audio->msgExecutePendingOperations(operations, true); } } //--------------------------------------------------------- // connectClicked //--------------------------------------------------------- void RouteDialog::connectClicked() { MusECore::PendingOperationList operations; MusECore::RouteList srcList; MusECore::RouteList dstList; newSrcList->getSelectedRoutes(srcList); newDstList->getSelectedRoutes(dstList); const int srcSelSz = srcList.size(); const int dstSelSz = dstList.size(); bool upd_trk_props = false; MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; for(int srcIdx = 0; srcIdx < srcSelSz; ++srcIdx) { const MusECore::Route& src = srcList.at(srcIdx); for(int dstIdx = 0; dstIdx < dstSelSz; ++dstIdx) { const MusECore::Route& dst = dstList.at(dstIdx); #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist // but not in any track or port routelist) until multiple output routes are allowed // instead of just single port and channel properties. The route is exclusive. switch(src.type) { case MusECore::Route::TRACK_ROUTE: switch(dst.type) { case MusECore::Route::MIDI_PORT_ROUTE: if(src.track->isMidiTrack()) { MusECore::MidiTrack* mt = static_cast(src.track); // We cannot 'remove' a simulated midi track output port and channel route. // (Midi port cannot be -1 meaning 'no port'.) // Only remove it if it's a different port or channel. if(src.channel >= 0 && src.channel < MusECore::MUSE_MIDI_CHANNELS && (mt->outPort() != dst.midiPort || mt->outChannel() != src.channel)) { MusEGlobal::audio->msgIdle(true); changed |= mt->setOutPortAndChannelAndUpdate(dst.midiPort, src.channel, false); MusEGlobal::audio->msgIdle(false); //MusEGlobal::audio->msgUpdateSoloStates(); //MusEGlobal::song->update(SC_MIDI_TRACK_PROP); upd_trk_props = true; } continue; } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: break; } break; case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: break; } #endif if(MusECore::routeCanConnect(src, dst)) operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); } } if(!operations.empty()) { operations.add(MusECore::PendingOperationItem((MusECore::TrackList*)NULL, MusECore::PendingOperationItem::UpdateSoloStates)); MusEGlobal::audio->msgExecutePendingOperations(operations, true, upd_trk_props ? (SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)) : 0); // MusEGlobal::song->update(SC_ROUTE | (upd_trk_props ? SC_MIDI_TRACK_PROP : 0)); //MusEGlobal::song->update(SC_SOLO); //routingChanged(); } else if(upd_trk_props) MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); } //--------------------------------------------------------- // srcSelectionChanged //--------------------------------------------------------- void RouteDialog::srcSelectionChanged() { DEBUG_PRST_ROUTES(stderr, "RouteDialog::srcSelectionChanged\n"); MusECore::RouteList srcList; MusECore::RouteList dstList; newSrcList->getSelectedRoutes(srcList); newDstList->getSelectedRoutes(dstList); const int srcSelSz = srcList.size(); const int dstSelSz = dstList.size(); //if(srcSelSz == 0 || dstSelSz == 0 || (srcSelSz > 1 && dstSelSz > 1)) //{ // connectButton->setEnabled(false); // removeButton->setEnabled(false); // return; //} routeList->blockSignals(true); routeList->clearSelection(); bool canConnect = false; QTreeWidgetItem* routesItem = 0; int routesSelCnt = 0; int routesRemoveCnt = 0; for(int srcIdx = 0; srcIdx < srcSelSz; ++srcIdx) { MusECore::Route& src = srcList.at(srcIdx); #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ int mt_to_mp_cnt = 0; #endif for(int dstIdx = 0; dstIdx < dstSelSz; ++dstIdx) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ bool useMTOutProps = false; #endif MusECore::Route& dst = dstList.at(dstIdx); // Special for some item type combos: Before searching, cross-update the routes if necessary. // To minimize route copying, here on each iteration we alter each list route, but should be OK. switch(src.type) { case MusECore::Route::TRACK_ROUTE: switch(dst.type) { case MusECore::Route::MIDI_PORT_ROUTE: if(src.track->isMidiTrack()) { dst.channel = src.channel; #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist // but not in any track or port routelist) until multiple output routes are allowed // instead of just single port and channel properties. The route is exclusive. useMTOutProps = true; MusECore::MidiTrack* mt = static_cast(src.track); if(src.channel >= 0 && src.channel < MusECore::MUSE_MIDI_CHANNELS && (mt->outPort() != dst.midiPort || mt->outChannel() != src.channel)) ++mt_to_mp_cnt; #endif } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } break; case MusECore::Route::MIDI_PORT_ROUTE: switch(dst.type) { case MusECore::Route::TRACK_ROUTE: src.channel = dst.channel; break; case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } QTreeWidgetItem* ri = findRoutesItem(src, dst); if(ri) { ri->setSelected(true); routesItem = ri; ++routesSelCnt; #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(!useMTOutProps) #endif ++routesRemoveCnt; } #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(useMTOutProps) continue; #endif if(MusECore::routeCanConnect(src, dst)) canConnect = true; // if(MusECore::routeCanDisconnect(src, dst)) // canDisconnect = true; } #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // If there was one and only one selected midi port for a midi track, allow (simulated) connection. if(mt_to_mp_cnt == 1) canConnect = true; #endif } if(routesSelCnt == 0) routeList->setCurrentItem(0); //routeList->setCurrentItem(routesItem); routeList->blockSignals(false); if(routesSelCnt == 1) routeList->scrollToItem(routesItem, QAbstractItemView::PositionAtCenter); selectRoutes(false); connectionsWidget->update(); //connectButton->setEnabled(can_connect && (srcSelSz == 1 || dstSelSz == 1)); connectButton->setEnabled(canConnect); //removeButton->setEnabled(can_disconnect); // removeButton->setEnabled(routesSelCnt > 0); removeButton->setEnabled(routesRemoveCnt != 0); } //--------------------------------------------------------- // dstSelectionChanged //--------------------------------------------------------- void RouteDialog::dstSelectionChanged() { srcSelectionChanged(); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void RouteDialog::closeEvent(QCloseEvent* e) { emit closed(); e->accept(); } QTreeWidgetItem* RouteDialog::findRoutesItem(const MusECore::Route& src, const MusECore::Route& dst) { int cnt = routeList->topLevelItemCount(); for(int i = 0; i < cnt; ++i) { QTreeWidgetItem* item = routeList->topLevelItem(i); if(!item || !item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() || !item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) continue; if(item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value() == src && item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value() == dst) return item; } return 0; } void RouteDialog::removeItems() { QVector itemsToDelete; newSrcList->getItemsToDelete(itemsToDelete); newDstList->getItemsToDelete(itemsToDelete); getRoutesToDelete(routeList, itemsToDelete); newSrcList->blockSignals(true); newDstList->blockSignals(true); routeList->blockSignals(true); if(!itemsToDelete.empty()) { int cnt = itemsToDelete.size(); for(int i = 0; i < cnt; ++i) delete itemsToDelete.at(i); } selectRoutes(false); routeList->blockSignals(false); newDstList->blockSignals(false); newSrcList->blockSignals(false); //connectionsWidget->update(); } void RouteDialog::getRoutesToDelete(QTreeWidget* tree, QVector& items_to_remove) { const int iItemCount = tree->topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *item = tree->topLevelItem(iItem); if(item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).canConvert() && item->data(ROUTE_DST_COL, RouteDialog::RouteRole).canConvert()) { const MusECore::Route src = item->data(ROUTE_SRC_COL, RouteDialog::RouteRole).value(); const MusECore::Route dst = item->data(ROUTE_DST_COL, RouteDialog::RouteRole).value(); #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // Special: Allow simulated midi track to midi port route (a route found in our 'local' routelist // but not in any track or port routelist) until multiple output routes are allowed // instead of just single port and channel properties. The route is exclusive. switch(src.type) { case MusECore::Route::TRACK_ROUTE: switch(dst.type) { case MusECore::Route::MIDI_PORT_ROUTE: if(src.track->isMidiTrack()) { MusECore::MidiTrack* mt = static_cast(src.track); // We cannot 'remove' a simulated midi track output port and channel route. // (Midi port cannot be -1 meaning 'no port'.) // Only remove it if it's a different port or channel. if(mt->outPort() != dst.midiPort || mt->outChannel() != src.channel) items_to_remove.append(item); continue; } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: break; } break; case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::JACK_ROUTE: break; } #endif if(!MusECore::routeCanDisconnect(src, dst)) items_to_remove.append(item); } } } void RouteDialog::selectRoutes(bool doNormalSelections) { const QList route_list = routeList->selectedItems(); newSrcList->selectRoutes(route_list, doNormalSelections); newDstList->selectRoutes(route_list, doNormalSelections); } void RouteDialog::addItems() { RouteTreeWidgetItem* srcCatItem; RouteTreeWidgetItem* dstCatItem; RouteTreeWidgetItem* item; RouteTreeWidgetItem* subitem; QTreeWidgetItem* routesItem; // Tried wrap flags: Doesn't work (at least not automatically). //const int align_flags = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextWordWrap | Qt::TextWrapAnywhere; const int align_flags = Qt::AlignLeft | Qt::AlignVCenter; // // Tracks: // dstCatItem = newDstList->findCategoryItem(tracksCat); srcCatItem = newSrcList->findCategoryItem(tracksCat); MusECore::TrackList* tl = MusEGlobal::song->tracks(); for(MusECore::ciTrack i = tl->begin(); i != tl->end(); ++i) { MusECore::Track* track = *i; const MusECore::RouteCapabilitiesStruct rcaps = track->routeCapabilities(); int src_chans = 0; int dst_chans = 0; bool src_routable = false; bool dst_routable = false; switch(track->type()) { case MusECore::Track::AUDIO_INPUT: src_chans = rcaps._trackChannels._outChannels; dst_chans = rcaps._jackChannels._inChannels; src_routable = rcaps._trackChannels._outRoutable; dst_routable = rcaps._jackChannels._inRoutable || rcaps._trackChannels._inRoutable; // Support Audio Out to Audio In omni route. break; case MusECore::Track::AUDIO_OUTPUT: src_chans = rcaps._jackChannels._outChannels; dst_chans = rcaps._trackChannels._inChannels; src_routable = rcaps._jackChannels._outRoutable || rcaps._trackChannels._outRoutable; // Support Audio Out to Audio In omni route. dst_routable = rcaps._trackChannels._inRoutable; break; case MusECore::Track::MIDI: case MusECore::Track::DRUM: #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ src_chans = MusECore::MUSE_MIDI_CHANNELS; #else src_chans = rcaps._midiPortChannels._outChannels; #endif dst_chans = rcaps._midiPortChannels._inChannels; src_routable = rcaps._midiPortChannels._outRoutable || rcaps._trackChannels._outRoutable; // Support Midi Track to Audio In omni route. dst_routable = rcaps._midiPortChannels._inRoutable; break; case MusECore::Track::WAVE: case MusECore::Track::AUDIO_AUX: case MusECore::Track::AUDIO_SOFTSYNTH: case MusECore::Track::AUDIO_GROUP: src_chans = rcaps._trackChannels._outChannels; dst_chans = rcaps._trackChannels._inChannels; src_routable = rcaps._trackChannels._outRoutable; dst_routable = rcaps._trackChannels._inRoutable; break; } // // DESTINATION section: // if(dst_routable || dst_chans != 0) { const MusECore::Route r(track, -1); item = newDstList->findItem(r, RouteTreeWidgetItem::RouteItem); if(item) { // Update the text. item->setText(ROUTE_NAME_COL, track->displayName()); } else { if(!dstCatItem) { newDstList->blockSignals(true); //dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << tracksCat << QString() ); dstCatItem = new RouteTreeWidgetItem(newDstList, QStringList() << tracksCat, RouteTreeWidgetItem::CategoryItem, false); dstCatItem->setFlags(Qt::ItemIsEnabled); QFont fnt = dstCatItem->font(ROUTE_NAME_COL); fnt.setBold(true); fnt.setItalic(true); //fnt.setPointSize(fnt.pointSize() + 2); dstCatItem->setFont(ROUTE_NAME_COL, fnt); dstCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); dstCatItem->setExpanded(true); newDstList->blockSignals(false); } newDstList->blockSignals(true); //item = new QTreeWidgetItem(dstCatItem, QStringList() << track->name() << trackLabel ); item = new RouteTreeWidgetItem(dstCatItem, QStringList() << track->displayName(), RouteTreeWidgetItem::RouteItem, false, r); //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(ROUTE_NAME_COL, align_flags); newDstList->blockSignals(false); //dstCatItem->setExpanded(true); // REMOVE Tim. For test only. } if(QIcon* r_pm = r.icon(false, false)) item->setIcon(ROUTE_NAME_COL, *r_pm); if(dst_chans != 0) { const MusECore::Route sub_r(track, 0, track->isMidiTrack() ? -1 : 1); subitem = newDstList->findItem(sub_r, RouteTreeWidgetItem::ChannelsItem); if(subitem) { // Update the channel y values. //subitem->computeChannelYValues(); // Update the number of channels. subitem->setChannels(); } else // if(!subitem) { newDstList->blockSignals(true); item->setExpanded(true); subitem = new RouteTreeWidgetItem(item, QStringList() << QString(), RouteTreeWidgetItem::ChannelsItem, false, sub_r); subitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); subitem->setTextAlignment(ROUTE_NAME_COL, align_flags); newDstList->blockSignals(false); } // Update the channel y values. //subitem->computeChannelYValues(); } } const MusECore::RouteList* irl = track->inRoutes(); for(MusECore::ciRoute r = irl->begin(); r != irl->end(); ++r) { // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 // After: [src TrackA, Channel 4, Remote Channel 2] dst: TrackB channel 2 Remote Channel 4 // // Ex. // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 MusECore::Route src; MusECore::Route dst; QString srcName; QString dstName = track->displayName(); switch(r->type) { case MusECore::Route::JACK_ROUTE: src = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, -1, -1, -1, r->persistentJackPortName); dst = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, 1, -1, 0); srcName = r->displayName(MusEGlobal::config.preferredRouteNameOrAlias); break; case MusECore::Route::MIDI_DEVICE_ROUTE: continue; break; // Midi ports taken care of below... case MusECore::Route::MIDI_PORT_ROUTE: continue; break; case MusECore::Route::TRACK_ROUTE: src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, r->track, r->remoteChannel, r->channels, -1, 0); dst = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, r->channels, -1, 0); srcName = r->displayName(); break; } if(src.channel != -1) srcName += QString(" [") + QString::number(src.channel) + QString("]"); if(dst.channel != -1) dstName += QString(" [") + QString::number(dst.channel) + QString("]"); routesItem = findRoutesItem(src, dst); if(routesItem) { // Update the text. routesItem->setText(ROUTE_SRC_COL, srcName); routesItem->setText(ROUTE_DST_COL, dstName); } else { routeList->blockSignals(true); routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); if(QIcon* src_pm = src.icon(true, false)) routesItem->setIcon(ROUTE_SRC_COL, *src_pm); if(QIcon* dst_pm = dst.icon(false, false)) routesItem->setIcon(ROUTE_DST_COL, *dst_pm); routeList->blockSignals(false); } switch(r->type) { case MusECore::Route::JACK_ROUTE: // If there is no jack port, warn the user that the port is 'Unavailable' by colouring it. // Otherwise, if the port re-appears at some point, restore the item colour to the original colour. if(r->jackPort) // From help: "Setting a default-constructed brush will let the view use the default color from the style." routesItem->setBackground(ROUTE_SRC_COL, QBrush()); else routesItem->setBackground(ROUTE_SRC_COL, QBrush(Qt::red)); break; case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::TRACK_ROUTE: break; } } // // SOURCE section: // if(src_routable || src_chans != 0 #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ || track->isMidiTrack() #endif ) { const MusECore::Route r(track, -1); item = newSrcList->findItem(r, RouteTreeWidgetItem::RouteItem); if(item) { // Update the text. item->setText(ROUTE_NAME_COL, track->displayName()); } else { if(!srcCatItem) { newSrcList->blockSignals(true); //srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << tracksCat << QString() ); srcCatItem = new RouteTreeWidgetItem(newSrcList, QStringList() << tracksCat, RouteTreeWidgetItem::CategoryItem, true); srcCatItem->setFlags(Qt::ItemIsEnabled); QFont fnt = srcCatItem->font(ROUTE_NAME_COL); fnt.setBold(true); fnt.setItalic(true); //fnt.setPointSize(fnt.pointSize() + 2); srcCatItem->setFont(ROUTE_NAME_COL, fnt); srcCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); srcCatItem->setExpanded(true); newSrcList->blockSignals(false); } newSrcList->blockSignals(true); //item = new QTreeWidgetItem(srcCatItem, QStringList() << track->name() << trackLabel ); item = new RouteTreeWidgetItem(srcCatItem, QStringList() << track->displayName(), RouteTreeWidgetItem::RouteItem, true, r); //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(r)); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(ROUTE_NAME_COL, align_flags); newSrcList->blockSignals(false); } if(QIcon* r_pm = r.icon(true, false)) item->setIcon(ROUTE_NAME_COL, *r_pm); if(src_chans != 0 #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ || track->isMidiTrack() #endif ) { //for(int channel = 0; channel < MusECore::MUSE_MIDI_CHANNELS; ++channel) //{ const MusECore::Route sub_r(track, 0, track->isMidiTrack() ? -1 : 1); subitem = newSrcList->findItem(sub_r, RouteTreeWidgetItem::ChannelsItem); if(subitem) { // Update the channel y values. //subitem->computeChannelYValues(); // Update the number of channels. subitem->setChannels(); } else // if(!subitem) { newSrcList->blockSignals(true); item->setExpanded(true); subitem = new RouteTreeWidgetItem(item, QStringList() << QString(), RouteTreeWidgetItem::ChannelsItem, true, sub_r #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ , track->isMidiTrack() ? RouteTreeWidgetItem::ExclusiveMode : RouteTreeWidgetItem::NormalMode #endif ); subitem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); subitem->setTextAlignment(ROUTE_NAME_COL, align_flags); newSrcList->blockSignals(false); } // Update the channel y values. //subitem->computeChannelYValues(); } } const MusECore::RouteList* orl = track->outRoutes(); for(MusECore::ciRoute r = orl->begin(); r != orl->end(); ++r) { // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 // After: src: TrackA, Channel 4, Remote Channel 2 [dst: TrackB channel 2 Remote Channel 4] // // Ex. // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 // After: (src: TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 MusECore::Route src; MusECore::Route dst; QString srcName = track->displayName(); QString dstName; switch(r->type) { case MusECore::Route::JACK_ROUTE: src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, 1, -1, 0); dst = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, -1, -1, -1, r->persistentJackPortName); dstName = r->displayName(MusEGlobal::config.preferredRouteNameOrAlias); break; case MusECore::Route::MIDI_DEVICE_ROUTE: continue; // TODO break; // Midi ports taken care of below... case MusECore::Route::MIDI_PORT_ROUTE: continue; break; case MusECore::Route::TRACK_ROUTE: src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, track, r->channel, r->channels, -1, 0); dst = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, r->track, r->remoteChannel, r->channels, -1, 0); dstName = r->displayName(); break; } if(src.channel != -1) srcName += QString(" [") + QString::number(src.channel) + QString("]"); if(dst.channel != -1) dstName += QString(" [") + QString::number(dst.channel) + QString("]"); routesItem = findRoutesItem(src, dst); if(routesItem) { // Update the text. routesItem->setText(ROUTE_SRC_COL, srcName); routesItem->setText(ROUTE_DST_COL, dstName); } else { routeList->blockSignals(true); routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); if(QIcon* src_pm = src.icon(true, false)) routesItem->setIcon(ROUTE_SRC_COL, *src_pm); if(QIcon* dst_pm = dst.icon(false, false)) routesItem->setIcon(ROUTE_DST_COL, *dst_pm); routeList->blockSignals(false); } switch(r->type) { case MusECore::Route::JACK_ROUTE: // If there is no jack port, warn the user that the port is 'Unavailable' by colouring it. // Otherwise, if the port re-appears at some point, restore the item colour to the original colour. if(r->jackPort) // From help: "Setting a default-constructed brush will let the view use the default color from the style." routesItem->setBackground(ROUTE_DST_COL, QBrush()); else routesItem->setBackground(ROUTE_DST_COL, QBrush(Qt::red)); break; case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::TRACK_ROUTE: break; } } } //} // // MIDI ports: // const QString none_str = tr(""); dstCatItem = newDstList->findCategoryItem(midiPortsCat); srcCatItem = newSrcList->findCategoryItem(midiPortsCat); for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; if(!mp) continue; MusECore::MidiDevice* md = mp->device(); // Synth are tracks and devices. Don't list them as devices here, list them as tracks, above. //if(md && md->deviceType() == MusECore::MidiDevice::SYNTH_MIDI) // continue; QString mdname; mdname = QString::number(i + 1) + QString(":"); mdname += md ? md->name() : none_str; // // DESTINATION section: // #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ bool non_route_found = false; // Simulate routes for each midi track's output port and channel properties. MusECore::MidiTrackList* tl = MusEGlobal::song->midis(); for(MusECore::ciMidiTrack imt = tl->begin(); imt != tl->end(); ++imt) { MusECore::MidiTrack* mt = *imt; const int port = mt->outPort(); const int chan = mt->outChannel(); if(port != i) continue; non_route_found = true; const MusECore::Route src = MusECore::Route(MusECore::Route::TRACK_ROUTE, -1, mt, chan, -1, -1, NULL); const MusECore::Route dst(i, chan); const QString srcName = mt->displayName() + QString(" [") + QString::number(chan) + QString("]"); const QString dstName = mdname; routesItem = findRoutesItem(src, dst); if(routesItem) { // Update the text. routesItem->setText(ROUTE_SRC_COL, srcName); routesItem->setText(ROUTE_DST_COL, dstName); } else { routeList->blockSignals(true); routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); if(QIcon* src_pm = src.icon(true, true)) routesItem->setIcon(ROUTE_SRC_COL, *src_pm); if(QIcon* dst_pm = dst.icon(false, true)) routesItem->setIcon(ROUTE_DST_COL, *dst_pm); routeList->blockSignals(false); } } #endif // _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ //if(md->rwFlags() & 0x02) // Readable // if(md->rwFlags() & 0x01) // Writeable //if(md->rwFlags() & 0x03) // Both readable and writeable need to be shown // Show either all midi ports, or only ports that have a device or have input routes. if(allMidiPortsButton->isChecked() || md || !mp->inRoutes()->empty() #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ || non_route_found #endif ) { const MusECore::Route dst(i, -1); item = newDstList->findItem(dst, RouteTreeWidgetItem::RouteItem); if(item) { // Update the text. item->setText(ROUTE_NAME_COL, mdname); } else { if(!dstCatItem) { newDstList->blockSignals(true); //dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << midiDevicesCat << QString() ); dstCatItem = new RouteTreeWidgetItem(newDstList, QStringList() << midiPortsCat, RouteTreeWidgetItem::CategoryItem, false); dstCatItem->setFlags(Qt::ItemIsEnabled); QFont fnt = dstCatItem->font(ROUTE_NAME_COL); fnt.setBold(true); fnt.setItalic(true); //fnt.setPointSize(fnt.pointSize() + 2); dstCatItem->setFont(ROUTE_NAME_COL, fnt); dstCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); dstCatItem->setExpanded(true); dstCatItem->setIcon(ROUTE_NAME_COL, *midiPortSVGIcon); newDstList->blockSignals(false); } newDstList->blockSignals(true); item = new RouteTreeWidgetItem(dstCatItem, QStringList() << mdname, RouteTreeWidgetItem::RouteItem, false, dst); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(ROUTE_NAME_COL, align_flags); newDstList->blockSignals(false); } const MusECore::RouteList* rl = mp->inRoutes(); for(MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case MusECore::Route::TRACK_ROUTE: #ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ { if(!r->track || !r->track->isMidiTrack()) continue; const MusECore::Route& src = *r; QString srcName = r->displayName(); QString dstName = mdname; //const MusECore::Route dst(i, -1); const MusECore::Route dst(i, src.channel); if(src.channel != -1) srcName += QString(" [") + QString::number(src.channel) + QString("]"); routesItem = findRoutesItem(src, dst); if(routesItem) { // Update the text. routesItem->setText(ROUTE_SRC_COL, srcName); routesItem->setText(ROUTE_DST_COL, dstName); } else { routeList->blockSignals(true); routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); if(QPixmap* src_pm = src.icon(true, true)) routesItem->setIcon(ROUTE_SRC_COL, QIcon(*src_pm)); if(QPixmap* dst_pm = dst.icon(false, true)) routesItem->setIcon(ROUTE_DST_COL, QIcon(*dst_pm)); routeList->blockSignals(false); } } #endif // _USE_MIDI_TRACK_OUT_ROUTES_ break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } } } // // SOURCE section: // //if(md->rwFlags() & 0x01) // Writeable // if(md->rwFlags() & 0x02) // Readable //if(md->rwFlags() & 0x03) // Both readable and writeable need to be shown // Show only ports that have a device, or have output routes. if(allMidiPortsButton->isChecked() || md || !mp->outRoutes()->empty()) { const MusECore::Route src(i, -1); item = newSrcList->findItem(src, RouteTreeWidgetItem::RouteItem); if(item) { // Update the text. item->setText(ROUTE_NAME_COL, mdname); } else { if(!srcCatItem) { newSrcList->blockSignals(true); //srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << midiDevicesCat << QString() ); srcCatItem = new RouteTreeWidgetItem(newSrcList, QStringList() << midiPortsCat, RouteTreeWidgetItem::CategoryItem, true); srcCatItem->setFlags(Qt::ItemIsEnabled); QFont fnt = srcCatItem->font(ROUTE_NAME_COL); fnt.setBold(true); fnt.setItalic(true); //fnt.setPointSize(fnt.pointSize() + 2); srcCatItem->setFont(ROUTE_NAME_COL, fnt); srcCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); srcCatItem->setExpanded(true); srcCatItem->setIcon(ROUTE_NAME_COL, *midiPortSVGIcon); newSrcList->blockSignals(false); } newSrcList->blockSignals(true); //item = new QTreeWidgetItem(srcCatItem, QStringList() << mdname << midiDeviceLabel ); item = new RouteTreeWidgetItem(srcCatItem, QStringList() << mdname, RouteTreeWidgetItem::RouteItem, true, src); //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(ROUTE_NAME_COL, align_flags); newSrcList->blockSignals(false); } const MusECore::RouteList* rl = mp->outRoutes(); for(MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) { // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 // After: src: TrackA, Channel 4, Remote Channel 2 [dst: TrackB channel 2 Remote Channel 4] // // Ex. // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 // After: (src: TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 //MusECore::Route src(md, -1); //MusECore::Route dst; //QString srcName = mdname; //QString dstName; switch(r->type) { case MusECore::Route::TRACK_ROUTE: { if(!r->track || !r->track->isMidiTrack()) continue; //const MusECore::Route dst = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, -1, -1, -1, r->persistentJackPortName); const MusECore::Route& dst = *r; QString dstName = r->displayName(); QString srcName = mdname; //const MusECore::Route src(i, -1); const MusECore::Route src(i, dst.channel); //if(src.channel != -1) // srcName += QString(" [") + QString::number(src.channel) + QString("]"); if(dst.channel != -1) dstName += QString(" [") + QString::number(dst.channel) + QString("]"); routesItem = findRoutesItem(src, dst); if(routesItem) { // Update the text. routesItem->setText(ROUTE_SRC_COL, srcName); routesItem->setText(ROUTE_DST_COL, dstName); } else { routeList->blockSignals(true); routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); if(QIcon* src_pm = src.icon(true, true)) routesItem->setIcon(ROUTE_SRC_COL, *src_pm); if(QIcon* dst_pm = dst.icon(false, true)) routesItem->setIcon(ROUTE_DST_COL, *dst_pm); routeList->blockSignals(false); } // if(!r->jackPort) // routesItem->setBackground(ROUTE_DST_COL, routesItem->background(ROUTE_DST_COL).color().darker()); } break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: continue; } } } } // // MIDI devices: // dstCatItem = newDstList->findCategoryItem(midiDevicesCat); srcCatItem = newSrcList->findCategoryItem(midiDevicesCat); for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { MusECore::MidiDevice* md = *i; // Synth are tracks and devices. Don't list them as devices here, list them as tracks, above. if(md->deviceType() == MusECore::MidiDevice::SYNTH_MIDI) continue; // QString mdname; // if(md->midiPort() != -1) // mdname = QString::number(md->midiPort() + 1) + QString(":"); // mdname += md->name(); QString mdname = md->name(); // // DESTINATION section: // //if(md->rwFlags() & 0x02) // Readable //if(md->rwFlags() & 0x01) // Writeable if(md->rwFlags() & 0x03) // Both readable and writeable need to be shown { const MusECore::Route dst(md, -1); item = newDstList->findItem(dst, RouteTreeWidgetItem::RouteItem); if(item) { // Update the text. item->setText(ROUTE_NAME_COL, mdname); } else { if(!dstCatItem) { newDstList->blockSignals(true); //dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << midiDevicesCat << QString() ); dstCatItem = new RouteTreeWidgetItem(newDstList, QStringList() << midiDevicesCat, RouteTreeWidgetItem::CategoryItem, false); dstCatItem->setFlags(Qt::ItemIsEnabled); QFont fnt = dstCatItem->font(ROUTE_NAME_COL); fnt.setBold(true); fnt.setItalic(true); //fnt.setPointSize(fnt.pointSize() + 2); dstCatItem->setFont(ROUTE_NAME_COL, fnt); dstCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); dstCatItem->setExpanded(true); dstCatItem->setIcon(ROUTE_NAME_COL, *midiInSVGIcon); newDstList->blockSignals(false); } newDstList->blockSignals(true); //item = new QTreeWidgetItem(dstCatItem, QStringList() << mdname << midiDeviceLabel ); item = new RouteTreeWidgetItem(dstCatItem, QStringList() << mdname, RouteTreeWidgetItem::RouteItem, false, dst); //item->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(ROUTE_NAME_COL, align_flags); newDstList->blockSignals(false); } const MusECore::RouteList* rl = md->inRoutes(); for(MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) { // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 // After: [src TrackA, Channel 4, Remote Channel 2] dst: TrackB channel 2 Remote Channel 4 // // Ex. // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 switch(r->type) { case MusECore::Route::JACK_ROUTE: { const MusECore::Route src = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, -1, -1, -1, r->persistentJackPortName); QString srcName = r->displayName(MusEGlobal::config.preferredRouteNameOrAlias); QString dstName = mdname; const MusECore::Route dst(md, -1); if(src.channel != -1) srcName += QString(" [") + QString::number(src.channel) + QString("]"); if(dst.channel != -1) dstName += QString(" [") + QString::number(dst.channel) + QString("]"); routesItem = findRoutesItem(src, dst); if(routesItem) { // Update the text. routesItem->setText(ROUTE_SRC_COL, srcName); routesItem->setText(ROUTE_DST_COL, dstName); } else { routeList->blockSignals(true); routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); if(QIcon* src_pm = src.icon(true, true)) routesItem->setIcon(ROUTE_SRC_COL, *src_pm); if(QIcon* dst_pm = dst.icon(false, true)) routesItem->setIcon(ROUTE_DST_COL, *dst_pm); routeList->blockSignals(false); } // If there is no jack port, warn the user that the port is 'Unavailable' by colouring it. // Otherwise, if the port re-appears at some point, restore the item colour to the original colour. if(r->jackPort) // From help: "Setting a default-constructed brush will let the view use the default color from the style." routesItem->setBackground(ROUTE_SRC_COL, QBrush()); else routesItem->setBackground(ROUTE_SRC_COL, QBrush(Qt::red)); } break; case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::TRACK_ROUTE: continue; } } } // // SOURCE section: // //if(md->rwFlags() & 0x01) // Writeable //if(md->rwFlags() & 0x02) // Readable if(md->rwFlags() & 0x03) // Both readable and writeable need to be shown { const MusECore::Route src(md, -1); item = newSrcList->findItem(src, RouteTreeWidgetItem::RouteItem); if(item) { // Update the text. item->setText(ROUTE_NAME_COL, mdname); } else { if(!srcCatItem) { newSrcList->blockSignals(true); //srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << midiDevicesCat << QString() ); srcCatItem = new RouteTreeWidgetItem(newSrcList, QStringList() << midiDevicesCat, RouteTreeWidgetItem::CategoryItem, true); srcCatItem->setFlags(Qt::ItemIsEnabled); QFont fnt = srcCatItem->font(ROUTE_NAME_COL); fnt.setBold(true); fnt.setItalic(true); //fnt.setPointSize(fnt.pointSize() + 2); srcCatItem->setFont(ROUTE_NAME_COL, fnt); srcCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); srcCatItem->setExpanded(true); srcCatItem->setIcon(ROUTE_NAME_COL, *midiInSVGIcon); newSrcList->blockSignals(false); } newSrcList->blockSignals(true); //item = new QTreeWidgetItem(srcCatItem, QStringList() << mdname << midiDeviceLabel ); item = new RouteTreeWidgetItem(srcCatItem, QStringList() << mdname, RouteTreeWidgetItem::RouteItem, true, src); //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(ROUTE_NAME_COL, align_flags); newSrcList->blockSignals(false); } const MusECore::RouteList* rl = md->outRoutes(); for(MusECore::ciRoute r = rl->begin(); r != rl->end(); ++r) { // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 // After: src: TrackA, Channel 4, Remote Channel 2 [dst: TrackB channel 2 Remote Channel 4] // // Ex. // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 // After: (src: TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 switch(r->type) { case MusECore::Route::JACK_ROUTE: { const MusECore::Route dst = MusECore::Route(MusECore::Route::JACK_ROUTE, -1, r->jackPort, -1, -1, -1, r->persistentJackPortName); QString dstName = r->displayName(MusEGlobal::config.preferredRouteNameOrAlias); QString srcName = mdname; const MusECore::Route src(md, -1); if(src.channel != -1) srcName += QString(" [") + QString::number(src.channel) + QString("]"); if(dst.channel != -1) dstName += QString(" [") + QString::number(dst.channel) + QString("]"); routesItem = findRoutesItem(src, dst); if(routesItem) { // Update the text. routesItem->setText(ROUTE_SRC_COL, srcName); routesItem->setText(ROUTE_DST_COL, dstName); } else { routeList->blockSignals(true); routesItem = new QTreeWidgetItem(routeList, QStringList() << srcName << dstName); routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(src)); routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(dst)); if(QIcon* src_pm = src.icon(true, true)) routesItem->setIcon(ROUTE_SRC_COL, *src_pm); if(QIcon* dst_pm = dst.icon(false, true)) routesItem->setIcon(ROUTE_DST_COL, *dst_pm); routeList->blockSignals(false); } // If there is no jack port, warn the user that the port is 'Unavailable' by colouring it. // Otherwise, if the port re-appears at some point, restore the item colour to the original colour. if(r->jackPort) // From help: "Setting a default-constructed brush will let the view use the default color from the style." routesItem->setBackground(ROUTE_DST_COL, QBrush()); else routesItem->setBackground(ROUTE_DST_COL, QBrush(Qt::red)); } break; case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::TRACK_ROUTE: continue; } } } } // // JACK ports: // if(MusEGlobal::checkAudioDevice()) { //------------ // Jack audio: //------------ srcCatItem = newSrcList->findCategoryItem(jackCat); MusECore::RouteList in_rl; for(std::list::iterator i = tmpJackOutPorts.begin(); i != tmpJackOutPorts.end(); ++i) { const MusECore::Route in_r(*i, false, -1, MusECore::Route::JACK_ROUTE); item = newSrcList->findItem(in_r, RouteTreeWidgetItem::RouteItem); if(item) { // Update the text. item->setText(ROUTE_NAME_COL, in_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); } else { if(!srcCatItem) { newSrcList->blockSignals(true); //srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << jackCat << QString() ); srcCatItem = new RouteTreeWidgetItem(newSrcList, QStringList() << jackCat, RouteTreeWidgetItem::CategoryItem, true); srcCatItem->setFlags(Qt::ItemIsEnabled); QFont fnt = srcCatItem->font(ROUTE_NAME_COL); fnt.setBold(true); fnt.setItalic(true); //fnt.setPointSize(fnt.pointSize() + 2); srcCatItem->setFont(ROUTE_NAME_COL, fnt); srcCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); srcCatItem->setExpanded(true); srcCatItem->setIcon(ROUTE_NAME_COL, *routeInSVGIcon); newSrcList->blockSignals(false); } newSrcList->blockSignals(true); //item = new QTreeWidgetItem(srcCatItem, QStringList() << in_r.displayName() << jackLabel ); item = new RouteTreeWidgetItem(srcCatItem, QStringList() << in_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias), RouteTreeWidgetItem::RouteItem, true, in_r); //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(in_r)); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(ROUTE_NAME_COL, align_flags); newSrcList->blockSignals(false); } in_rl.push_back(in_r); } dstCatItem = newDstList->findCategoryItem(jackCat); for(std::list::iterator i = tmpJackInPorts.begin(); i != tmpJackInPorts.end(); ++i) { const MusECore::Route out_r(*i, true, -1, MusECore::Route::JACK_ROUTE); item = newDstList->findItem(out_r, RouteTreeWidgetItem::RouteItem); if(item) { // Update the text. item->setText(ROUTE_NAME_COL, out_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); } else { if(!dstCatItem) { newDstList->blockSignals(true); //dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << jackCat << QString() ); dstCatItem = new RouteTreeWidgetItem(newDstList, QStringList() << jackCat, RouteTreeWidgetItem::CategoryItem, false); dstCatItem->setFlags(Qt::ItemIsEnabled); QFont fnt = dstCatItem->font(ROUTE_NAME_COL); fnt.setBold(true); fnt.setItalic(true); //fnt.setPointSize(fnt.pointSize() + 2); dstCatItem->setFont(ROUTE_NAME_COL, fnt); dstCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); dstCatItem->setExpanded(true); dstCatItem->setIcon(ROUTE_NAME_COL, *routeOutSVGIcon); newDstList->blockSignals(false); } newDstList->blockSignals(true); //item = new QTreeWidgetItem(dstCatItem, QStringList() << out_r.displayName() << jackLabel ); item = new RouteTreeWidgetItem(dstCatItem, QStringList() << out_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias), RouteTreeWidgetItem::RouteItem, false, out_r); //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(out_r)); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(ROUTE_NAME_COL, align_flags); newDstList->blockSignals(false); } // const QIcon src_ico(*routesInIcon); // const QIcon dst_ico(*routesOutIcon); for(MusECore::ciRoute i = in_rl.begin(); i != in_rl.end(); ++i) { const MusECore::Route& in_r = *i; if(MusECore::routeCanDisconnect(in_r, out_r)) { routesItem = findRoutesItem(in_r, out_r); if(routesItem) { // Update the text. routesItem->setText(ROUTE_SRC_COL, in_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); routesItem->setText(ROUTE_DST_COL, out_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); } else { routeList->blockSignals(true); routesItem = new QTreeWidgetItem(routeList, QStringList() << in_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias) << out_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(in_r)); routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(out_r)); routesItem->setIcon(ROUTE_SRC_COL, *routeInSVGIcon); routesItem->setIcon(ROUTE_DST_COL, *routeOutSVGIcon); routeList->blockSignals(false); } } } } //------------ // Jack midi: //------------ srcCatItem = newSrcList->findCategoryItem(jackMidiCat); in_rl.clear(); for(std::list::iterator i = tmpJackMidiOutPorts.begin(); i != tmpJackMidiOutPorts.end(); ++i) { const MusECore::Route in_r(*i, false, -1, MusECore::Route::JACK_ROUTE); item = newSrcList->findItem(in_r, RouteTreeWidgetItem::RouteItem); if(item) { // Update the text. item->setText(ROUTE_NAME_COL, in_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); } else { if(!srcCatItem) { newSrcList->blockSignals(true); //srcCatItem = new QTreeWidgetItem(newSrcList, QStringList() << jackMidiCat << QString() ); srcCatItem = new RouteTreeWidgetItem(newSrcList, QStringList() << jackMidiCat, RouteTreeWidgetItem::CategoryItem, true); srcCatItem->setFlags(Qt::ItemIsEnabled); QFont fnt = srcCatItem->font(ROUTE_NAME_COL); fnt.setBold(true); fnt.setItalic(true); //fnt.setPointSize(fnt.pointSize() + 2); srcCatItem->setFont(ROUTE_NAME_COL, fnt); srcCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); srcCatItem->setExpanded(true); srcCatItem->setIcon(ROUTE_NAME_COL, *routeInMidiSVGIcon); newSrcList->blockSignals(false); } newSrcList->blockSignals(true); //item = new QTreeWidgetItem(srcCatItem, QStringList() << in_r.displayName() << jackMidiLabel ); item = new RouteTreeWidgetItem(srcCatItem, QStringList() << in_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias), RouteTreeWidgetItem::RouteItem, true, in_r); //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(in_r)); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(ROUTE_NAME_COL, align_flags); newSrcList->blockSignals(false); } in_rl.push_back(in_r); } dstCatItem = newDstList->findCategoryItem(jackMidiCat); for(std::list::iterator i = tmpJackMidiInPorts.begin(); i != tmpJackMidiInPorts.end(); ++i) { const MusECore::Route out_r(*i, true, -1, MusECore::Route::JACK_ROUTE); item = newDstList->findItem(out_r, RouteTreeWidgetItem::RouteItem); if(item) { // Update the text. item->setText(ROUTE_NAME_COL, out_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); } else { if(!dstCatItem) { newDstList->blockSignals(true); //dstCatItem = new QTreeWidgetItem(newDstList, QStringList() << jackMidiCat << QString() ); dstCatItem = new RouteTreeWidgetItem(newDstList, QStringList() << jackMidiCat, RouteTreeWidgetItem::CategoryItem, false); dstCatItem->setFlags(Qt::ItemIsEnabled); QFont fnt = dstCatItem->font(ROUTE_NAME_COL); fnt.setBold(true); fnt.setItalic(true); //fnt.setPointSize(fnt.pointSize() + 2); dstCatItem->setFont(ROUTE_NAME_COL, fnt); dstCatItem->setTextAlignment(ROUTE_NAME_COL, align_flags); dstCatItem->setExpanded(true); dstCatItem->setIcon(ROUTE_NAME_COL, *routeOutMidiSVGIcon); newDstList->blockSignals(false); } newDstList->blockSignals(true); //item = new QTreeWidgetItem(dstCatItem, QStringList() << out_r.displayName() << jackMidiLabel ); item = new RouteTreeWidgetItem(dstCatItem, QStringList() << out_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias), RouteTreeWidgetItem::RouteItem, false, out_r); //item->setData(ROUTE_NAME_COL, RouteDialog::RouteRole, QVariant::fromValue(out_r)); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); item->setTextAlignment(ROUTE_NAME_COL, align_flags); newDstList->blockSignals(false); } // const QIcon src_ico(*routesMidiInIcon); // const QIcon dst_ico(*routesMidiOutIcon); for(MusECore::ciRoute i = in_rl.begin(); i != in_rl.end(); ++i) { const MusECore::Route& in_r = *i; if(MusECore::routeCanDisconnect(in_r, out_r)) { routesItem = findRoutesItem(in_r, out_r); if(routesItem) { // Update the text. routesItem->setText(ROUTE_SRC_COL, in_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); routesItem->setText(ROUTE_DST_COL, out_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); } else { routeList->blockSignals(true); routesItem = new QTreeWidgetItem(routeList, QStringList() << in_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias) << out_r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); routesItem->setTextAlignment(ROUTE_SRC_COL, align_flags); routesItem->setTextAlignment(ROUTE_DST_COL, align_flags); routesItem->setData(ROUTE_SRC_COL, RouteDialog::RouteRole, QVariant::fromValue(in_r)); routesItem->setData(ROUTE_DST_COL, RouteDialog::RouteRole, QVariant::fromValue(out_r)); routesItem->setIcon(ROUTE_SRC_COL, *routeInMidiSVGIcon); routesItem->setIcon(ROUTE_DST_COL, *routeOutMidiSVGIcon); routeList->blockSignals(false); } } } } } } void MusE::startRouteDialog() { if(routeDialog == nullptr) // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). routeDialog = new MusEGui::RouteDialog; routeDialog->show(); routeDialog->raise(); } } // namespace MusEGui muse-4.0.0/src/muse/components/routedialog.h000066400000000000000000000521241404105311000210710ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: routedialog.h,v 1.2 2004/01/31 17:31:49 wschweer Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) // (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ROUTEDIALOG_H__ #define __ROUTEDIALOG_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include "type_defs.h" #include "route.h" // Forward declarations: class QCloseEvent; class QMouseEvent; class QWheelEvent; class QPainter; class QResizeEvent; class QPaintEvent; class QContextMenuEvent; namespace MusEGui { class RouteDialog; typedef QList RouteTreeItemList; //--------------------------------------------------------- // RouteTreeWidgetItem //--------------------------------------------------------- struct RouteChannelsStruct { // Whether the channel is selected (highlight colour). bool _selected; // Whether the channel is selected as part of one or more complete routes (yellow colour). bool _routeSelected; // Whether the channel is connected to any other route (whether to draw a header connector line). bool _connected; // Rectangle of the channel. QRect _buttonRect; // Contains pre-computed handy y value for drawing connection lines. int _lineY; RouteChannelsStruct() : _selected(false), _routeSelected(false), _connected(false), _lineY(-1) { } void toggleSelected() { _selected = !_selected; } void toggleRouteSelected() { _routeSelected = !_routeSelected; } void toggleConnected() { _connected = !_connected; } }; class RouteChannelsList : public QVector { public: // Returns true if any channel was changed. bool fillSelected(bool v) { bool changed = false; const int sz = size(); for(int i = 0; i < sz; ++i) { bool& sel = operator[](i)._selected; if(sel != v) changed = true; sel = v; } return changed; } bool selected(int c) const { if(c >= size()) return false; return at(c)._selected; } void select(int c, bool v) { if(c >= size()) return; operator[](c)._selected = v; } void toggleSelected(int c) { if(c >= size()) return; operator[](c).toggleSelected(); } // Returns true if any channel was changed. bool fillRouteSelected(bool v) { bool changed = false; const int sz = size(); for(int i = 0; i < sz; ++i) { bool& sel = operator[](i)._routeSelected; if(sel != v) changed = true; sel = v; } return changed; } bool routeSelected(int c) const { if(c >= size()) return false; return at(c)._routeSelected; } void routeSelect(int c, bool v) { if(c >= size()) return; operator[](c)._routeSelected = v; } void toggleRouteSelected(int c) { if(c >= size()) return; operator[](c).toggleRouteSelected(); } // Returns true if any channel was changed. bool fillConnected(bool v) { bool changed = false; const int sz = size(); for(int i = 0; i < sz; ++i) { bool& sel = operator[](i)._connected; if(sel != v) changed = true; sel = v; } return changed; } bool connected(int c) const { if(c >= size()) return false; return at(c)._connected; } void setConnected(int c, bool v) { if(c >= size()) return; operator[](c)._connected = v; } void toggleConnected(int c) { if(c >= size()) return; operator[](c).toggleConnected(); } // Pre-computed handy y value for drawing connection lines. int lineY(int c) const { if(c >= size()) return -1; return at(c)._lineY; } // Set pre-computed handy y value for drawing connection lines. void setLineY(int c, int v) { if(c >= size()) return; operator[](c)._lineY = v; } // How many channels are connected. int connectedChannels() const; // Returns the smallest width that can fit a channel bar. static int minimumWidthHint(); // Returns the minimum width of the array that will fit into the given width constraint. int widthHint(int width) const; // Returns the minimum height of the array that will fit into the given width constraint. int heightHint(int width) const; // Returns a suitable size based on the given width constraint. QSize sizeHint(int width) const { return QSize(widthHint(width), heightHint(width)); } // How many channels fit into the width. static int channelsPerWidth(int width); // How many groups accommodate the given number of channels, all on a single bar. static int groupsPerChannels(int channels); // How many bars accommodate the item's total channels, for the given number of maximum number of channels on a single bar. int barsPerColChannels(int cc) const; }; class RouteTreeWidgetItem : public QTreeWidgetItem { public: enum ItemType { NormalItem = Type, CategoryItem = UserType, RouteItem = UserType + 1, ChannelsItem = UserType + 2}; enum ItemMode { NormalMode, ExclusiveMode }; // A data role to pass the item type from item to delegate. //enum ItemDataRole { TypeRole = Qt::UserRole}; private: bool _isInput; MusECore::Route _route; RouteChannelsList _channels; ItemMode _itemMode; int _curChannel; void init(); public: // Overrides for QTreeWidgetItem constructor... RouteTreeWidgetItem(int type = NormalItem, bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) : QTreeWidgetItem(type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } RouteTreeWidgetItem(const QStringList& strings, int type = NormalItem, bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) : QTreeWidgetItem(strings, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } RouteTreeWidgetItem(QTreeWidget* parent, int type = NormalItem, bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) : QTreeWidgetItem(parent, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } RouteTreeWidgetItem(QTreeWidget* parent, const QStringList& strings, int type = NormalItem, bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) : QTreeWidgetItem(parent, strings, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } RouteTreeWidgetItem(QTreeWidget* parent, QTreeWidgetItem* preceding, int type = NormalItem, bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) : QTreeWidgetItem(parent, preceding, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } RouteTreeWidgetItem(QTreeWidgetItem* parent, int type = NormalItem, bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) : QTreeWidgetItem(parent, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } RouteTreeWidgetItem(QTreeWidgetItem* parent, const QStringList& strings, int type = NormalItem, bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) : QTreeWidgetItem(parent, strings, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } RouteTreeWidgetItem(QTreeWidgetItem* parent, QTreeWidgetItem* preceding, int type = NormalItem, bool isInput = false, const MusECore::Route& route = MusECore::Route(), ItemMode mode = NormalMode) : QTreeWidgetItem(parent, preceding, type), _isInput(isInput), _route(route), _itemMode(mode) { init(); } MusECore::Route& route() { return _route; } // Whether this item should exist or not, based on _route and the item type. bool routeNodeExists(); // Fills a list of routes with selected items' routes. void getSelectedRoutes(MusECore::RouteList& routes); // Returns item exclusive mode setting. ItemMode itemMode() const { return _itemMode; } // Sets item exclusive mode setting. void setItemMode(ItemMode mode) { _itemMode = mode; } // Automatically sets the number of channels. Returns true if channel count was changed. bool setChannels(); // Returns the number of channels. int channelCount() const { return _channels.size(); } // Sets the number of channels. void setChannelCount(int c) { _channels.resize(c); } // Returns true if the channel is selected. bool channelSelected(int c) const { return _channels.selected(c); } // Selects the channel. void selectChannel(int c, bool v) { _channels.select(c, v); } // Toggles the channel selection. void toggleChannel(int c) { _channels.toggleSelected(c); } // Sets all channels' selected state. Returns true if any channel was changed. bool fillSelectedChannels(bool v) { return _channels.fillSelected(v); } // Returns true if the channel is route-selected. bool channelRouteSelected(int c) const { return _channels.routeSelected(c); } // Route-selects the channel. void routeSelectChannel(int c, bool v) { _channels.routeSelect(c, v); } // Toggles the channel route-selection. void toggleChannelRouteSelect(int c) { _channels.toggleRouteSelected(c); } // Sets all channels' route-selected state. Returns true if any channel was changed. bool fillChannelsRouteSelected(bool v) { return _channels.fillRouteSelected(v); } // Returns the channel, based at rect y, whose rectangle contains pt. int channelAt(const QPoint& pt, const QRect& rect) const; // For drawing channel lines: int channelYValue(int c) const { return _channels.lineY(c); } // Computes, and caches, channel y values. May be slow. Don't call on each draw, only when routes change. Use channelYValue() to draw. void computeChannelYValues(int col_width = -1); // Returns current channel. (Unlike being selected.) int curChannel() const { return _curChannel; } // Sets the current channel. (Unlike being selected.) void setCurChannel(int c) { _curChannel = c; } // Handles mouse press events. bool mousePressHandler(QMouseEvent* e, const QRect& rect); // Handles mouse move events. bool mouseMoveHandler(QMouseEvent* e, const QRect& rect); // Handles painting. Returns true if the painting was handled. bool paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; // Returns suggested size of item, that will fit into the given width while expanding vertically. // If width is -1, it uses the view width. QSize getSizeHint(int column, int width = -1) const; // Returns true if the item should be re-laid out (ie. with a tree's scheduleDelayedItemsLayout() or a delegate's sizeHintChanged()). // For channel items, it first adjusts the channel bar width to fit the new width. bool testForRelayout(int column, int old_width, int new_width); }; //--------------------------------------------------------- // ConnectionsView //--------------------------------------------------------- class ConnectionsView : public QFrame { Q_OBJECT private: RouteDialog* _routeDialog; int lastY; int itemY(RouteTreeWidgetItem* item, bool is_input, int channel = -1) const; void drawItem(QPainter* pPainter, QTreeWidgetItem* routesItem, const QColor& col); void drawConnectionLine(QPainter* pPainter, int x1, int y1, int x2, int y2, int h1, int h2); protected: virtual void paintEvent(QPaintEvent*); virtual void mousePressEvent(QMouseEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual void wheelEvent(QWheelEvent*); virtual void contextMenuEvent(QContextMenuEvent*); signals: void scrollBy(int, int); public: ConnectionsView(QWidget* parent = 0, RouteDialog* d = 0); virtual ~ConnectionsView(); void setRouteDialog(RouteDialog* d) { _routeDialog = d; } }; //--------------------------------------------------------- // RouteTreeWidget //--------------------------------------------------------- class RouteTreeWidget : public QTreeWidget { Q_OBJECT Q_PROPERTY(bool isInput READ isInput WRITE setIsInput) // An extra property required to support stylesheets (not enough colours). Q_PROPERTY(QColor categoryColor READ categoryColor WRITE setCategoryColor) private: bool _isInput; bool _channelWrap; QColor _categoryColor; private slots: void headerSectionResized(int logicalIndex, int oldSize, int newSize); protected: virtual void mousePressEvent(QMouseEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex& index, const QEvent* event = 0) const; virtual void resizeEvent(QResizeEvent*); protected slots: virtual void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); public slots: void scrollBy(int dx, int dy); public: RouteTreeWidget(QWidget* parent = 0, bool is_input = false); virtual ~RouteTreeWidget(); void computeChannelYValues(); bool isInput() { return _isInput; } void setIsInput(bool v) { _isInput = v; } bool channelWrap() { return _channelWrap; } void setChannelWrap(bool v) { _channelWrap = v; } RouteTreeWidgetItem* itemFromIndex(const QModelIndex& index) const; RouteTreeWidgetItem* findItem(const MusECore::Route&, int itemType = -1); RouteTreeWidgetItem* findCategoryItem(const QString&); int channelAt(RouteTreeWidgetItem* item, const QPoint& pt); //void clearChannels(); void getSelectedRoutes(MusECore::RouteList& routes); void getItemsToDelete(QVector& items_to_remove, bool showAllMidiPorts = false); //void scheduleDelayedLayout() { scheduleDelayedItemsLayout(); } // Just to make it public. void selectRoutes(const QList& routes, bool doNormalSelections); QColor categoryColor() const { return _categoryColor; } void setCategoryColor(const QColor& c) { _categoryColor = c; } }; } // namespace MusEGui #include "ui_routedialogbase.h" namespace MusEGui { //----------------------------------- // RoutingItemDelegate //----------------------------------- class RoutingItemDelegate : public QStyledItemDelegate { Q_OBJECT private: RouteTreeWidget* _tree; bool _isInput; QStyle::SubElement _currentSubElement; // Set in mouse press, checked in release to prevent unwanted editor opening. // Need this. For some reason when using CurrentChanged trigger, createEditor is called upon opening the dialog, yet nothing is selected. bool _firstPress; QRect getItemRectangle(const QStyleOptionViewItem& option, const QModelIndex& index, QStyle::SubElement subElement, QWidget* editor = NULL) const; bool subElementHitTest(const QPoint& point, const QStyleOptionViewItem& option, const QModelIndex& index, QStyle::SubElement* subElement, QWidget* editor = NULL) const; public: RoutingItemDelegate(bool is_input, RouteTreeWidget* tree, QWidget *parent = 0); virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; // Exposed as public from protected, so that it may be called from the tree widget. virtual void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const { QStyledItemDelegate::initStyleOption(option, index); } // Emits the required sizeHintChanged(index) signal, to notify the tree to relayout the item. virtual void emitSizeHintChanged(const QModelIndex &index) { emit sizeHintChanged(index); } protected: bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index); bool eventFilter(QObject* editor, QEvent* event); }; //--------------------------------------------------------- // RouteDialog //--------------------------------------------------------- class RouteDialog : public QDialog, public Ui::RouteDialogBase { Q_OBJECT MusECore::RouteList tmpSrcList; MusECore::RouteList tmpDstList; MusECore::RouteList tmpRoutesSrcList; MusECore::RouteList tmpRoutesDstList; RoutingItemDelegate* srcItemDelegate; RoutingItemDelegate* dstItemDelegate; RouteTreeItemList _srcFilterItems; RouteTreeItemList _dstFilterItems; virtual void closeEvent(QCloseEvent*); void removeItems(); void addItems(); void getRoutesToDelete(QTreeWidget* routesTree, QVector& items_to_remove); void selectRoutes(bool doNormalSelections); private slots: void routeSelectionChanged(); void disconnectClicked(); void connectClicked(); void srcSelectionChanged(); void dstSelectionChanged(); void songChanged(MusECore::SongChangedStruct_t); void srcTreeScrollValueChanged(int value); void dstTreeScrollValueChanged(int value); void srcScrollBarValueChanged(int value); void dstScrollBarValueChanged(int value); void filterSrcClicked(bool v); void filterDstClicked(bool v); void filterSrcRoutesClicked(bool v); void filterDstRoutesClicked(bool v); void allMidiPortsClicked(bool v); void preferredRouteAliasChanged(int); void verticalLayoutClicked(bool); signals: void closed(); public: RouteDialog(QWidget* parent=0); QTreeWidgetItem* findRoutesItem(const MusECore::Route&, const MusECore::Route&); // Hide all items in the source or destination tree except the filter items, // and hide any items in the route tree whose source or destination route data // matches matches the filter items' routes. // If filter items is empty show all items, in both the source or destination tree // and the route tree. // Hiding items does not disturb the open state of an entire tree. void filter(const RouteTreeItemList& srcFilterItems, const RouteTreeItemList& dstFilterItems, bool filterSrc, bool filterDst); enum { ROUTE_NAME_COL = 0 }; //, ROUTE_TYPE_COL }; enum { ROUTE_SRC_COL = 0, ROUTE_DST_COL }; enum RoutingRoles { RouteRole = Qt::UserRole}; //, ChannelsRole = Qt::UserRole + 1 }; static const QString tracksCat; static const QString midiPortsCat; static const QString midiDevicesCat; static const QString jackCat; static const QString jackMidiCat; static const int channelDotDiameter; static const int channelDotSpacing; static const int channelDotsPerGroup; static const int channelDotGroupSpacing; static const int channelDotsMargin; static const int channelBarHeight; static const int channelLineWidth; static const int channelLinesSpacing; static const int channelLinesMargin; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/routedialogbase.ui000066400000000000000000000332541404105311000221150ustar00rootroot00000000000000 RouteDialogBase 0 0 720 480 0 0 MusE: Routing Qt::Vertical Qt::Horizontal false 0 Qt::Vertical List of available sources. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOn false QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows 16 16 Qt::ElideMiddle false true false 1 0 0 255 255 255 0 0 0 255 255 255 0 0 0 0 0 0 0 0 0 Connections View window. Shows all current connections. Thick lines are Omni Routes. Thin lines are Channel Routes. true 0 List of available destinations. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOn false QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows 16 16 Qt::ElideMiddle false false false 1 Qt::Vertical Itemized list of current connections. true QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows Qt::ElideMiddle false 1 2 Show only selected sources Show only selected sources true Show only destination routes for selected source Show only destination routes for selected source true Qt::Vertical Show only selected destinations Show only selected destinations true Show only source routes for selected destination Show only source routes for selected destination true Qt::Vertical Show all Midi Ports Show all Midi Ports true Qt::Vertical Auto adjust column size Automatically adjusts the source and destination tree widths when the splitters are adjusted. This also turns on text word wrap, which may cause slower response with larger lists. true Preferred route name or alias Preferred route name or alias Qt::Horizontal 40 20 0 0 Connect source to destination Connect source to destination Connect 0 0 Remove selected route Remove selected route Remove MusEGui::ConnectionsView QFrame
routedialog.h
1
MusEGui::RouteTreeWidget QTreeWidget
routedialog.h
1
muse-4.0.0/src/muse/components/routepopup.cpp000066400000000000000000004373551404105311000213450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // RoutePopupMenu.cpp // (C) Copyright 2011-2015 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; 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 #include #include #include "app.h" #include "routepopup.h" #include "gconfig.h" #include "midiport.h" #include "mididev.h" #include "audio.h" #include "driver/audiodev.h" #include "song.h" #include "synth.h" #include "icons.h" #include "menutitleitem.h" #include "globaldefs.h" #include "gconfig.h" #include "globals.h" // Forwards from header: #include #include #include #include #include "track.h" #include "operations.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); #define DEBUG_PRST_ROUTES_2(dev, format, args...) // fprintf(dev, format, ##args); #define _USE_CUSTOM_WIDGET_ACTIONS_ // REMOVE Tim. Persistent routes. Added. Make this permanent later if it works OK and makes good sense. #define _USE_SIMPLIFIED_SOLO_CHAIN_ // REMOVE Tim. Persistent routes. Added. Make this permanent later if it works OK and makes good sense. #define _USE_MIDI_ROUTE_PER_CHANNEL_ // Undefine if and when multiple output routes are added to midi tracks. #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ #define _SHOW_CANONICAL_NAMES_ 0x1000 #define _SHOW_FIRST_ALIASES_ 0x1001 #define _SHOW_SECOND_ALIASES_ 0x1002 #define _ALIASES_WIDGET_ACTION_ 0x2000 #define _OPEN_MIDI_CONFIG_ 0x2001 #define _OPEN_ROUTING_DIALOG_ 0x2002 #define _GROUPING_CHANNELS_WIDGET_ACTION_ 0x2003 namespace MusEGui { void RoutePopupMenu::addGroupingChannelsAction(PopupMenu* lb) { RoutingMatrixWidgetAction* name_wa = new RoutingMatrixWidgetAction(2, 0, 0, this, tr("Channel grouping:")); name_wa->setArrayStayOpen(true); name_wa->setData(_GROUPING_CHANNELS_WIDGET_ACTION_); name_wa->array()->setColumnsExclusive(true); name_wa->array()->setExclusiveToggle(false); name_wa->array()->headerSetVisible(false); name_wa->array()->setText(0, tr("Mono ")); name_wa->array()->setText(1, tr("Stereo ")); switch(MusEGlobal::config.routerGroupingChannels) { case 1: name_wa->array()->setValue(0, true); break; case 2: name_wa->array()->setValue(1, true); break; default: break; } // Must rebuild array after text changes. name_wa->updateChannelArray(); lb->addAction(name_wa); lb->addSeparator(); } //--------------------------------------------------------- // addMenuItem //--------------------------------------------------------- int RoutePopupMenu::addMenuItem(MusECore::AudioTrack* track, MusECore::Track* route_track, PopupMenu* lb, int id, int channel, int /*channels*/, bool isOutput) { if(route_track->isMidiTrack()) return ++id; MusECore::RouteList* rl = isOutput ? track->outRoutes() : track->inRoutes(); const bool circ_route = (isOutput ? track : route_track)->isCircularRoute(isOutput ? route_track : track); MusECore::RouteCapabilitiesStruct t_caps = track->routeCapabilities(); MusECore::RouteCapabilitiesStruct rt_caps = route_track->routeCapabilities(); const int t_chans = isOutput ? t_caps._trackChannels._outChannels : t_caps._trackChannels._inChannels; const int rt_chans = isOutput ? rt_caps._trackChannels._inChannels : rt_caps._trackChannels._outChannels; // Support Audio Output Track to Audio Input Track 'Omni' routes. if(isOutput && track->type() == MusECore::Track::AUDIO_OUTPUT && route_track->type() == MusECore::Track::AUDIO_INPUT) { if(channel != -1 || !t_caps._trackChannels._outRoutable || !rt_caps._trackChannels._inRoutable) return ++id; } else if(!isOutput && track->type() == MusECore::Track::AUDIO_INPUT && route_track->type() == MusECore::Track::AUDIO_OUTPUT) { if(channel != -1 || !t_caps._trackChannels._inRoutable || !rt_caps._trackChannels._outRoutable) return ++id; } else { if(t_chans <= 0) return ++id; if(rt_chans <= 0) return ++id; } #ifndef _USE_CUSTOM_WIDGET_ACTIONS_ // Is it an omnibus route? if(channel == -1) { QAction* act = lb->addAction(route_track->displayName()); act->setCheckable(true); MusECore::Route r(route_track, -1); act->setData(QVariant::fromValue(r)); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == route_track && ir->remoteChannel == r.channel && ir->channel == r.remoteChannel && ir->channels == r.channels) { act->setChecked(true); break; } } if(!act->isChecked() && circ_route) // If circular route exists, allow user to break it, otherwise forbidden. act->setEnabled(false); } else #endif { #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ QAction* act = lb->addAction(route_track->displayName()); act->setCheckable(true); MusECore::Route r(route_track, -1); act->setData(QVariant::fromValue(r)); if(rl->contains(r)) act->setChecked(true); if(rt_chans != 0 && t_chans != 0) { RoutePopupMenu* subp = new RoutePopupMenu(_route, this, isOutput, _broadcastChanges); subp->addAction(new MenuTitleItem(tr("Channels"), this)); act->setMenu(subp); QActionGroup* act_group = new QActionGroup(this); act_group->setExclusive(false); for(int row = 0; row < rt_chans; ++row) { //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(t_chans, redLedIcon, darkRedLedIcon, this, QString::number(row + 1)); RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(t_chans, nullptr, nullptr, this, QString::number(row + 1)); wa->setFont(wa->smallFont()); wa->array()->headerSetVisible(row == 0); r.channel = row; wa->setData(QVariant::fromValue(r)); // Ignore the routing channel and channels - our action holds the channels. for(int col = 0; col < t_chans; ++col) { for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == route_track && ir->remoteChannel == row && ir->channel == col && ir->channels == 1) { wa->array()->setValue(col, true); break; } } } // Must rebuild array after text changes. wa->updateChannelArray(); // subp->addAction(wa); act_group->addAction(wa); } subp->addActions(act_group->actions()); } if(!act->isChecked() && circ_route) // If circular route exists, allow user to break it, otherwise forbidden. act->setEnabled(false); #else // It's not an omnibus route. Add the individual channels... PopupMenu* subp = new PopupMenu(this, true); subp->setTitle(route_track->displayName()); subp->addAction(new MenuTitleItem(tr("Channels"), this)); QActionGroup* act_group = new QActionGroup(this); act_group->setExclusive(false); for(int i = 0; i < rt_chans; ++i) { // QAction* act = subp->addAction(QString::number(i + 1)); QAction* act = act_group->addAction(QString::number(i + 1)); act->setCheckable(true); MusECore::Route r(route_track, i, 1); r.remoteChannel = channel; act->setData(QVariant::fromValue(r)); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == route_track && ir->remoteChannel == r.channel && ir->channel == r.remoteChannel && ir->channels == r.channels) { act->setChecked(true); break; } } if(!act->isChecked() && circ_route) // If circular route exists, allow user to break it, otherwise forbidden. act->setEnabled(false); } subp->addActions(act_group->actions()); lb->addMenu(subp); #endif } return ++id; } //--------------------------------------------------------- // addAuxPorts //--------------------------------------------------------- int RoutePopupMenu::addAuxPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::AuxList* al = MusEGlobal::song->auxs(); for (MusECore::iAudioAux i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } //--------------------------------------------------------- // addInPorts //--------------------------------------------------------- int RoutePopupMenu::addInPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::InputList* al = MusEGlobal::song->inputs(); for (MusECore::iAudioInput i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } //--------------------------------------------------------- // addOutPorts //--------------------------------------------------------- int RoutePopupMenu::addOutPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::OutputList* al = MusEGlobal::song->outputs(); for (MusECore::iAudioOutput i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } //--------------------------------------------------------- // addGroupPorts //--------------------------------------------------------- int RoutePopupMenu::addGroupPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::GroupList* al = MusEGlobal::song->groups(); for (MusECore::iAudioGroup i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } //--------------------------------------------------------- // addWavePorts //--------------------------------------------------------- int RoutePopupMenu::addWavePorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::WaveTrackList* al = MusEGlobal::song->waves(); for (MusECore::iWaveTrack i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } void RoutePopupMenu::addMidiTracks(MusECore::Track* t, PopupMenu* pup, bool isOutput) { const MusECore::RouteList* const rl = isOutput ? t->outRoutes() : t->inRoutes(); const MusECore::MidiTrackList* const mtracks = MusEGlobal::song->midis(); for(MusECore::ciMidiTrack imt = mtracks->begin(); imt != mtracks->end(); ++imt) { MusECore::MidiTrack* const mt = *imt; QAction* act = pup->addAction(mt->displayName()); act->setCheckable(true); const MusECore::Route r(mt, -1); act->setData(QVariant::fromValue(r)); if(rl->contains(r)) act->setChecked(true); } } void RoutePopupMenu::addMidiPorts(MusECore::Track* t, PopupMenu* pup, bool isOutput, bool show_synths, bool want_writable) { #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ const MusECore::RouteList* const rl = isOutput ? t->outRoutes() : t->inRoutes(); MusECore::MidiDevice* md; bool is_first_pass = true; QActionGroup* act_group = nullptr; // Order the entire listing by device type. for(int dtype = 0; dtype <= MusECore::MidiDevice::SYNTH_MIDI; ++dtype) { // Currently only midi port output to midi track input supports 'Omni' routes. #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(isOutput) { // Count the number of required rows. int rows = 0; for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { md = MusEGlobal::midiPorts[i].device(); // This is desirable, but could lead to 'hidden' routes unless we add more support such as removing the existing routes when user changes flags. // So for now, just list all valid ports whether read or write. //if(!md) // continue; if(!md || !(md->rwFlags() & (want_writable ? 1 : 2))) // If this is an output click we are looking for midi writeable here. continue; // Do not list synth devices! if(!show_synths && md->isSynti()) continue; // We only want the sorted device type. if(md->deviceType() != dtype) continue; ++rows; } if(rows == 0) continue; if(is_first_pass) { is_first_pass = false; act_group = new QActionGroup(this); act_group->setExclusive(true); } int row = 0; MusECore::Route r(-1); // Midi port route. for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { md = MusEGlobal::midiPorts[i].device(); // This is desirable, but could lead to 'hidden' routes unless we add more support such as removing the existing routes when user changes flags. // So for now, just list all valid ports whether read or write. //if(!md) // continue; if(!md || !(md->rwFlags() & (want_writable ? 1 : 2))) // If this is an output click we are looking for midi writeable here. continue; // Do not list synth devices! if(!show_synths && md->isSynti()) continue; // We only want the sorted device type. if(md->deviceType() != dtype) continue; //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction( // MusECore::MUSE_MIDI_CHANNELS, redLedIcon, darkRedLedIcon, this, QString("%1:%2").arg(i + 1).arg(md->name())); RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction( MusECore::MUSE_MIDI_CHANNELS, nullptr, nullptr, this, QString("%1:%2").arg(i + 1).arg(md->name())); if(row == 0) { switch(dtype) { case MusECore::MidiDevice::ALSA_MIDI: wa->array()->headerSetTitle(tr("ALSA devices")); break; case MusECore::MidiDevice::JACK_MIDI: wa->array()->headerSetTitle(tr("JACK devices")); break; case MusECore::MidiDevice::SYNTH_MIDI: wa->array()->headerSetTitle(tr("Synth devices")); break; } wa->array()->setArrayTitle(tr("Channels")); wa->array()->headerSetVisible(true); } else wa->array()->headerSetVisible(false); r.midiPort = i; wa->setData(QVariant::fromValue(r)); wa->array()->setColumnsExclusive(true); MusECore::MidiTrack* mt = static_cast(t); if(i == mt->outPort()) wa->array()->setValue(mt->outChannel(), true); // Must rebuild array after text changes. wa->updateChannelArray(); // Make it easy for the user: Show the device's jack ports as well. // This is reasonable for midi devices since they are hidden away. // (Midi devices were made tracks, and midi ports eliminated, in the old MusE-2 muse_evolution branch!) switch(md->deviceType()) { case MusECore::MidiDevice::JACK_MIDI: { const MusECore::Route md_r(md, -1); RoutePopupMenu* subp = new RoutePopupMenu(md_r, this, isOutput, _broadcastChanges); addJackPorts(md_r, subp); wa->setMenu(subp); } break; default: break; } act_group->addAction(wa); ++row; } pup->addActions(act_group->actions()); } else #endif // _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ { // Count the number of required rows. int rows = 0; for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { md = MusEGlobal::midiPorts[i].device(); // This is desirable, but could lead to 'hidden' routes unless we add more support such as removing the existing routes when user changes flags. // So for now, just list all valid ports whether read or write. //if(!md) // continue; if(!md || !(md->rwFlags() & (want_writable ? 1 : 2))) // If this is an input click we are looking for midi readable here. continue; // Do not list synth devices! if(!show_synths && md->isSynti()) continue; // We only want the sorted device type. if(md->deviceType() != dtype) continue; ++rows; } if(rows == 0) continue; // It's an input. Allow 'Omni' routes'... int row = 0; for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { md = MusEGlobal::midiPorts[i].device(); // This is desirable, but could lead to 'hidden' routes unless we add more support such as removing the existing routes when user changes flags. // So for now, just list all valid ports whether read or write. //if(!md) // continue; if(!md || !(md->rwFlags() & (want_writable ? 1 : 2))) // If this is an input click we are looking for midi readable here. continue; // Do not list synth devices! if(!show_synths && md->isSynti()) continue; // We only want the sorted device type. if(md->deviceType() != dtype) continue; MusECore::Route r(i, -1); //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction( // MusECore::MUSE_MIDI_CHANNELS, redLedIcon, darkRedLedIcon, this, QString("%1:%2").arg(i + 1).arg(md->name())); RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction( MusECore::MUSE_MIDI_CHANNELS, nullptr, nullptr, this, QString("%1:%2").arg(i + 1).arg(md->name())); if(row == 0) { wa->array()->setCheckBoxTitle(tr("Omni")); switch(dtype) { case MusECore::MidiDevice::ALSA_MIDI: wa->array()->headerSetTitle(tr("ALSA devices")); break; case MusECore::MidiDevice::JACK_MIDI: wa->array()->headerSetTitle(tr("JACK devices")); break; case MusECore::MidiDevice::SYNTH_MIDI: wa->array()->headerSetTitle(tr("Synth devices")); break; } wa->array()->setArrayTitle(tr("Channels")); wa->array()->headerSetVisible(true); } else wa->array()->headerSetVisible(false); wa->setHasCheckBox(true); if(rl->contains(r)) wa->setCheckBoxChecked(true); wa->setData(QVariant::fromValue(r)); // Ignore the routing channel and channels - our action holds the channels. #ifdef _USE_MIDI_ROUTE_PER_CHANNEL_ for(int col = 0; col < MusECore::MUSE_MIDI_CHANNELS; ++col) { r.channel = col; if(rl->contains(r)) wa->array()->setValue(col, true); } #else // _USE_MIDI_ROUTE_PER_CHANNEL_ int chans = 0; // Is there already a route? for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case MusECore::Route::MIDI_PORT_ROUTE: if(ir->midiPort == i) chans = ir->channel; // Grab the channels. break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } if(chans != 0) break; } if(chans != 0 && chans != -1) { for(int col = 0; col < MusECore::MUSE_MIDI_CHANNELS; ++col) { if(chans & (1 << col)) wa->array()->setValue(col, true); } } #endif // _USE_MIDI_ROUTE_PER_CHANNEL_ // Must rebuild array after text changes. wa->updateChannelArray(); // Make it easy for the user: Show the device's jack ports as well. // This is reasonable for midi devices since they are hidden away. // (Midi devices were made tracks, and midi ports eliminated, in the old MusE-2 muse_evolution branch!) switch(md->deviceType()) { case MusECore::MidiDevice::JACK_MIDI: { //PopupMenu* subp = new PopupMenu(this, true); const MusECore::Route md_r(md, -1); RoutePopupMenu* subp = new RoutePopupMenu(md_r, this, isOutput, _broadcastChanges); addJackPorts(md_r, subp); wa->setMenu(subp); } break; default: break; } pup->addAction(wa); ++row; } } #else // _USE_CUSTOM_WIDGET_ACTIONS_ pup->addAction(new MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Output port/device")), pup)); for(int i = 0; i < MIDI_PORTS; ++i) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* md = mp->device(); // This is desirable, but could lead to 'hidden' routes unless we add more support // such as removing the existing routes when user changes flags. // So for now, just list all valid ports whether read or write. //if(!md) // continue; if(!md || !(md->rwFlags() & (want_writable ? 1 : 2))) // If this is an input click we are looking for midi outputs here. continue; // Do not list synth devices! if(!show_synths && md->isSynti()) continue; // We only want the sorted device type. if(md->deviceType() != dtype) continue; MusECore::RouteList* rl = isOutput ? t->outRoutes() : t->inRoutes(); int chanmask = 0; // To reduce number of routes required, from one per channel to just one containing a channel mask. // Look for the first route to this midi port. There should always be only a single route for each midi port, now. for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::MIDI_PORT_ROUTE && ir->midiPort == i) { // We have a route to the midi port. Grab the channel mask. chanmask = ir->channel; break; } } PopupMenu* subp = new PopupMenu(pup, true); subp->setTitle(md->name()); QAction* act; for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { act = subp->addAction(QString("Channel %1").arg(ch+1)); act->setCheckable(true); int chbit = 1 << ch; MusECore::Route srcRoute(i, chbit); // In accordance with channel mask, use the bit position. act->setData(QVariant::fromValue(srcRoute)); if(chanmask & chbit) // Is the channel already set? Show item check mark. act->setChecked(true); } act = subp->addAction(QString("Toggle all")); //act->setCheckable(true); MusECore::Route togRoute(i, (1 << MusECore::MUSE_MIDI_CHANNELS) - 1); // Set all channel bits. act->setData(QVariant::fromValue(togRoute)); pup->addMenu(subp); } #endif // _USE_CUSTOM_WIDGET_ACTIONS_ } return; } //--------------------------------------------------------- // addSynthPorts //--------------------------------------------------------- int RoutePopupMenu::addSynthPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput) { MusECore::SynthIList* al = MusEGlobal::song->syntis(); for (MusECore::iSynthI i = al->begin(); i != al->end(); ++i) { MusECore::Track* track = *i; if (t == track) continue; id = addMenuItem(t, track, lb, id, channel, channels, isOutput); } return id; } //--------------------------------------------------------- // addJackPorts //--------------------------------------------------------- void RoutePopupMenu::addJackPorts(const MusECore::Route& route, PopupMenu* lb) { if(!MusEGlobal::checkAudioDevice()) return; MusECore::RouteList* rl = nullptr; int channels = -1; std::list ol; MusECore::RouteCapabilitiesStruct rcaps; switch(route.type) { case MusECore::Route::TRACK_ROUTE: ol = _isOutMenu ? MusEGlobal::audioDevice->inputPorts() : MusEGlobal::audioDevice->outputPorts(); rl = _isOutMenu ? route.track->outRoutes() : route.track->inRoutes(); rcaps = route.track->routeCapabilities(); channels = _isOutMenu ? rcaps._jackChannels._outChannels : rcaps._jackChannels._inChannels; break; case MusECore::Route::MIDI_DEVICE_ROUTE: ol = _isOutMenu ? MusEGlobal::audioDevice->inputPorts(true) : MusEGlobal::audioDevice->outputPorts(true); rl = _isOutMenu ? route.device->outRoutes() : route.device->inRoutes(); break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: return; break; } const int sz = ol.size(); if(sz != 0) { #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ //RoutingMatrixWidgetAction* name_wa = new RoutingMatrixWidgetAction(2, redLedIcon, darkRedLedIcon, this, tr("Show aliases:")); RoutingMatrixWidgetAction* name_wa = new RoutingMatrixWidgetAction(2, nullptr, nullptr, this, tr("Show aliases:")); name_wa->setArrayStayOpen(true); name_wa->setData(_ALIASES_WIDGET_ACTION_); name_wa->array()->setColumnsExclusive(true); name_wa->array()->setExclusiveToggle(true); name_wa->array()->headerSetVisible(false); name_wa->array()->setText(0, tr("First ")); name_wa->array()->setText(1, tr("Second ")); switch(MusEGlobal::config.preferredRouteNameOrAlias) { case MusEGlobal::RoutePreferFirstAlias: name_wa->array()->setValue(0, true); break; case MusEGlobal::RoutePreferSecondAlias: name_wa->array()->setValue(1, true); break; case MusEGlobal::RoutePreferCanonicalName: break; } // Must rebuild array after text changes. name_wa->updateChannelArray(); lb->addAction(name_wa); lb->addSeparator(); #else QActionGroup* act_grp = new QActionGroup(this); act_grp->setExclusive(true); act = act_grp->addAction(tr("Show names")); act->setCheckable(true); act->setData(_SHOW_CANONICAL_NAMES_); if(MusEGlobal::config.preferredRouteNameOrAlias == MusEGlobal::RoutePreferCanonicalName) act->setChecked(true); act = act_grp->addAction(tr("Show first aliases")); act->setCheckable(true); act->setData(_SHOW_FIRST_ALIASES_); if(MusEGlobal::config.preferredRouteNameOrAlias == MusEGlobal::RoutePreferFirstAlias) act->setChecked(true); act = act_grp->addAction(tr("Show second aliases")); act->setCheckable(true); act->setData(_SHOW_SECOND_ALIASES_); if(MusEGlobal::config.preferredRouteNameOrAlias == MusEGlobal::RoutePreferSecondAlias) act->setChecked(true); lb->addActions(act_grp->actions()); lb->addSeparator(); #endif #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ QActionGroup* act_group = new QActionGroup(this); act_group->setExclusive(false); int row = 0; for(std::list::iterator ip = ol.begin(); ip != ol.end(); ++ip) { QByteArray ba = (*ip).toLatin1(); const char* port_name = ba.constData(); void* const port = MusEGlobal::audioDevice->findPort(port_name); if(port) { //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(channels == -1 ? 1 : channels, redLedIcon, darkRedLedIcon, this); RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(channels == -1 ? 1 : channels, nullptr, nullptr, this); if(row == 0) { wa->array()->headerSetTitle(tr("Jack ports")); if(channels == -1) { wa->array()->setArrayTitle(tr("Connect")); wa->array()->headerSetVisible(false); } else { wa->array()->setArrayTitle(tr("Channels")); wa->array()->headerSetVisible(true); } } else wa->array()->headerSetVisible(false); char good_name[ROUTE_PERSISTENT_NAME_SIZE]; // Get the preferred display name. MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE, MusEGlobal::config.preferredRouteNameOrAlias); wa->setActionText(good_name); // Get a good routing name. MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE); MusECore::Route r(MusECore::Route::JACK_ROUTE, -1, port, -1, -1, -1, good_name); wa->setData(QVariant::fromValue(r)); if(channels == -1) { if(rl->contains(r)) wa->array()->setValue(0, true); } else { for(int i = 0; i < channels; ++i) { r.channel = i; if(rl->contains(r)) wa->array()->setValue(i, true); } } // Must rebuild array after text changes. wa->updateChannelArray(); // lb->addAction(wa); act_group->addAction(wa); ++row; } } lb->addActions(act_group->actions()); #else QAction* act = 0; if(channels == -1) { if(!MusEGlobal::checkAudioDevice()) { clear(); return; } for(std::list::iterator ip = ol.begin(); ip != ol.end(); ++ip) { act = lb->addAction(*ip); act->setCheckable(true); QByteArray ba = (*ip).toLatin1(); const char* port_name = ba.constData(); char good_name[ROUTE_PERSISTENT_NAME_SIZE]; void* const port = MusEGlobal::audioDevice->findPort(port_name); if(port) { MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE); port_name = good_name; } MusECore::Route dst(MusECore::Route::JACK_ROUTE, -1, NULL, -1, -1, -1, port_name); act->setData(QVariant::fromValue(dst)); if(rl->exists(r)) act->setChecked(true); } } else { for(int i = 0; i < channels; ++i) { QString chBuffer = tr("Channel") + QString(" ") + QString::number(i + 1); MenuTitleItem* titel = new MenuTitleItem(chBuffer, this); lb->addAction(titel); if(!MusEGlobal::checkAudioDevice()) { clear(); return; } for(std::list::iterator ip = ol.begin(); ip != ol.end(); ++ip) { act = lb->addAction(*ip); act->setCheckable(true); QByteArray ba = (*ip).toLatin1(); const char* port_name = ba.constData(); char good_name[ROUTE_PERSISTENT_NAME_SIZE]; void* const port = MusEGlobal::audioDevice->findPort(port_name); if(port) { MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE); port_name = good_name; } MusECore::Route dst(MusECore::Route::JACK_ROUTE, -1, NULL, i, -1, -1, port_name); act->setData(QVariant::fromValue(dst)); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(*ir == dst) { act->setChecked(true); break; } } } if(i+1 != channels) lb->addSeparator(); } } #endif } QList act_list; int row = 0; for(MusECore::iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case MusECore::Route::JACK_ROUTE: if(ir->jackPort == nullptr && MusEGlobal::audioDevice->findPort(ir->persistentJackPortName) == nullptr) { //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(channels == -1 ? 1 : channels, redLedIcon, darkRedLedIcon, RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(channels == -1 ? 1 : channels, nullptr, nullptr, this, ir->persistentJackPortName); wa->setEnabled(false); if(row == 0) { wa->array()->headerSetTitle(tr("Jack ports")); if(channels == -1) { wa->array()->setArrayTitle(tr("Connect")); wa->array()->headerSetVisible(false); } else { wa->array()->setArrayTitle(tr("Channels")); wa->array()->headerSetVisible(true); } } else wa->array()->headerSetVisible(false); MusECore::Route r(MusECore::Route::JACK_ROUTE, -1, nullptr, -1, -1, -1, ir->persistentJackPortName); wa->setData(QVariant::fromValue(r)); if(channels == -1) wa->array()->setValue(0, true); else { for(int i = 0; i < channels; ++i) { if(i == ir->channel) wa->array()->setValue(i, true); } } // Must rebuild array after text changes. wa->updateChannelArray(); act_list.append(wa); ++row; } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: break; } } if(!act_list.isEmpty()) { RoutePopupMenu* subp = new RoutePopupMenu(route, this, _isOutMenu, _broadcastChanges); subp->setTitle(tr("Unavailable")); const int sz = act_list.size(); for(int i = 0; i < sz; ++i) subp->addAction(act_list.at(i)); lb->addMenu(subp); } } //====================== // RoutePopupMenu //====================== RoutePopupMenu::RoutePopupMenu(QWidget* parent, bool isOutput, bool broadcastChanges) : PopupMenu(parent, true), _isOutMenu(isOutput), _broadcastChanges(broadcastChanges) { init(); } RoutePopupMenu::RoutePopupMenu(const MusECore::Route& route, QWidget* parent, bool isOutput, bool broadcastChanges) : PopupMenu(parent, true), _route(route), _isOutMenu(isOutput), _broadcastChanges(broadcastChanges) { init(); } RoutePopupMenu::RoutePopupMenu(const MusECore::Route& route, const QString& title, QWidget* parent, bool isOutput, bool broadcastChanges) //: PopupMenu(title, parent, true), _track(track), _isOutMenu(isOutput) : PopupMenu(title, parent, true), _route(route), _isOutMenu(isOutput), _broadcastChanges(broadcastChanges) { init(); } void RoutePopupMenu::init() { _hoverIsFromMouse = false; connect(this, SIGNAL(hovered(QAction*)), SLOT(routePopupHovered(QAction*))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); } bool RoutePopupMenu::event(QEvent* event) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::event:%p activePopupWidget:%p this:%p class:%s event type:%d\n", event, QApplication::activePopupWidget(), this, metaObject()->className(), event->type()); switch(event->type()) { // Technical difficulties: // "mouseReleaseEvent() is called when a mouse button is released. A widget receives mouse release events // when it has received the corresponding mouse press event. This means that if the user presses the mouse // inside your widget, then drags the mouse somewhere else before releasing the mouse button, your widget // receives the release event. There is one exception: if a popup menu appears while the mouse button is held down, // this popup immediately steals the mouse events." // Unfortunately that's exactly what we don't want. The mouse release events are not being passed to the higher-up menu // if we hold the mouse down and move over another menu item which has a submenu - the (delayed) appearance of that // submenu steals the release. Oddly, if the mouse is moved further - even just once - within the new item, // the release event IS passed on. So to avoid dealing with that distinction, let's just pass on all release events. // Should be OK under normal usage, since it makes some sense that no mouse events should be reaching a submenu anyway - // they have no effect since the cursor position is outside of them ! // NOTE: If a submenu OVERLAPS its higher-up menu, this could be a big problem. In general how to deal with overlapping popups // when we wish to be able to click on items in both a menu and its submenu. Overlapping will only happen with too-wide // menus which should be rare for routing, but we could also defer to the advanced router when the popup becomes too wide. case QEvent::MouseButtonRelease: case QEvent::MouseButtonPress: // Removed. Causes very high CPU usage spikes. // I think I remember adding MouseMove simply for 'good luck' rather than any real usefulness. // Tested OK /without/ this on KUBUNTU 15.10, we don't seem to need it. Retest on 14.04 LTS... // case QEvent::MouseMove: { QMouseEvent* mev = static_cast(event); DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::event type:%d x:%d y:%d gx:%d gy:%d sx:%f sy:%f wx:%f wy:%f lx:%f ly:%f\n", mev->type(), mev->pos().x(), mev->pos().y(), mev->globalPos().x(), mev->globalPos().y(), mev->screenPos().x(), mev->screenPos().y(), mev->windowPos().x(), mev->windowPos().y(), mev->localPos().x(), mev->localPos().y()); QMenu* target_menu = nullptr; const int sz = QApplication::topLevelWidgets().size(); for(int i = 0; i < sz; ++i) { QWidget* w = QApplication::topLevelWidgets().at(i); DEBUG_PRST_ROUTES(stderr, " checking top level widget:%p\n", w); if(QMenu* menu = qobject_cast(w)) { if(menu->windowType() != Qt::Popup) continue; DEBUG_PRST_ROUTES(stderr, " checking hit in menu:%p visible:%d modal:%d\n", menu, menu->isVisible(), menu->isModal()); if(!menu->isVisible() || !menu->geometry().contains(mev->globalPos())) continue; DEBUG_PRST_ROUTES(stderr, " hit\n"); // If we hit the submenu it means the submenu is partially or wholly obscuring the other menu. // We must honour the submenu in this case even if it is only slightly obscuring the other menu. if(menu == this) { DEBUG_PRST_ROUTES(stderr, " menu is this\n"); return PopupMenu::event(mev); } // The menu is a good target - the mouse is within it and it is not obscured. // Regardless, afterward continue watching for THIS menu... if(!target_menu) target_menu = menu; } } if(target_menu) { DEBUG_PRST_ROUTES(stderr, " target_menu:%p\n", target_menu); QMouseEvent new_mev(mev->type(), //mev->windowPos(), // Relative to the widget the mouse is actually over (menu variable). QPointF(target_menu->mapFromGlobal(mev->globalPos())), mev->screenPos(), mev->button(), mev->buttons(), mev->modifiers()); new_mev.setAccepted(mev->isAccepted()); new_mev.setTimestamp(mev->timestamp()); QApplication::sendEvent(target_menu, &new_mev); return true; } DEBUG_PRST_ROUTES(stderr, " no target popup found\n"); } break; case QEvent::KeyPress: { QKeyEvent* e = static_cast(event); switch(e->key()) { case Qt::Key_Space: if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, nullptr, this)) break; // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case Qt::Key_Select: case Qt::Key_Return: case Qt::Key_Enter: { if(activeAction() && (!contextMenu() || !contextMenu()->isVisible())) { if(RoutingMatrixWidgetAction* mwa = qobject_cast(activeAction())) { bool accept = false; if(mwa->hasCheckBox() && mwa->isSelected()) { mwa->setCheckBoxChecked(!mwa->checkBoxChecked()); } else if(mwa->array()->columns() != 0 && mwa->array()->activeColumn() != -1) { mwa->array()->setValue(mwa->array()->activeColumn(), !mwa->array()->value(mwa->array()->activeColumn())); // Reset any other switch bars besides this one which are part of a QActionGroup. // Since they are all part of an action group, force them to be exclusive regardless of their exclusivity settings. QActionGroup* act_group = mwa->actionGroup(); if(act_group && act_group->isExclusive()) { const int sz = act_group->actions().size(); for(int i = 0; i < sz; ++i) { if(RoutingMatrixWidgetAction* act = qobject_cast(act_group->actions().at(i))) { if(act != mwa) { // Set any column to false, and exclusiveColumns and exclusiveToggle to true which will reset all columns. act->array()->setValues(0, false, true, true); //update(); // Redraw the indicators. act->updateCreatedWidgets(); // Redraw the indicators. } } } } if(mwa->arrayStayOpen()) accept = true; } else { // Nothing selected. Do nothing. TODO: Select the first available item, like QMenu does... e->accept(); return true; // We handled it. } mwa->updateCreatedWidgets(); e->accept(); mwa->trigger(); // Trigger the action. // Check for Ctrl to stay open. if(!accept && (!stayOpen() || (!MusEGlobal::config.popupsDefaultStayOpen && (e->modifiers() & Qt::ControlModifier) == 0))) closeUp(); // Close all the popups. return true; // We handled it. } // Otherwise let ancestor PopupMenu handle it... } } break; default: break; } } break; default: break; } return PopupMenu::event(event); } void RoutePopupMenu::resizeEvent(QResizeEvent* e) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::resizeEvent\n"); e->ignore(); PopupMenu::resizeEvent(e); } void RoutePopupMenu::mouseReleaseEvent(QMouseEvent* e) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::mouseReleaseEvent this:%p x:%d y:%d\n", this, e->pos().x(), e->pos().y()); if(contextMenu() && contextMenu()->isVisible()) return; DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent begin: this:%p active action:%p\n", this, activeAction()); bool activate = false; bool accept = false; QAction* action = actionAt(e->pos()); RoutingMatrixWidgetAction* act_mwa = qobject_cast(action); // RoutingMatrixWidgetAction* mwa = 0; // QAction* action = actionAt(e->pos()); // if(action) // { // mwa = qobject_cast(action); // if(mwa) // { // RoutePopupHit hit = mwa->hitTest(e->pos(), RoutePopupHit::HitTestClick); // switch(hit._type) // { // case RoutePopupHit::HitChannel: // { // mwa->array()->setValue(hit._value, !mwa->array()->value(hit._value)); // // // Reset any other switch bars besides this one which are part of a QActionGroup. // // Since they are all part of an action group, force them to be exclusive regardless of their exclusivity settings. // QActionGroup* act_group = mwa->actionGroup(); // if(act_group && act_group->isExclusive()) // { // const int sz = act_group->actions().size(); // for(int i = 0; i < sz; ++i) // { // if(RoutingMatrixWidgetAction* act = qobject_cast(act_group->actions().at(i))) // { // if(act != mwa) // { // // Set any column to false, and exclusiveColumns and exclusiveToggle to true which will reset all columns. // act->array()->setValues(0, false, true, true); // act->updateCreatedWidgets(); // Redraw the indicators. // } // } // } // } // // if(mwa->arrayStayOpen()) // accept = true; // activate = true; // } // break; // // case RoutePopupHit::HitMenuItem: // mwa->setCheckBoxChecked(!mwa->checkBoxChecked()); // activate = true; // break; // // case RoutePopupHit::HitChannelBar: // case RoutePopupHit::HitSpace: // accept = true; // break; // // case RoutePopupHit::HitNone: // break; // } // } // } int ch_hit_clk_idx_min = -1; int ch_hit_clk_idx_max = -1; int ch_hit_clk_ch_start = -1; bool ch_hit_clk_val = false; QActionGroup* ch_hit_clk_act_group = nullptr; const int sz = actions().size(); for(int i = 0; i < sz; ++i) { if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) { bool do_upd = false; // Sanity check: Only activate the item(s) if the action truly is the active one. //if(mwa == activeAction()) if(mwa == action) { RoutePopupHit hit = mwa->hitTest(e->pos(), RoutePopupHit::HitTestClick); switch(hit._type) { case RoutePopupHit::HitChannel: { // Support grouping together of channels. ch_hit_clk_idx_min = i; ch_hit_clk_idx_max = ch_hit_clk_idx_min + MusEGlobal::config.routerGroupingChannels; if(ch_hit_clk_idx_max > sz) ch_hit_clk_idx_min = sz - MusEGlobal::config.routerGroupingChannels; ch_hit_clk_ch_start = hit._value - (i - ch_hit_clk_idx_min); const int ch_diff = mwa->array()->columns() - (ch_hit_clk_ch_start + MusEGlobal::config.routerGroupingChannels); if(ch_diff < 0) { ch_hit_clk_idx_min += ch_diff; ch_hit_clk_idx_max += ch_diff; ch_hit_clk_ch_start += ch_diff; } ch_hit_clk_act_group = mwa->actionGroup(); ch_hit_clk_val = !mwa->array()->value(hit._value); DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent i:%d hit._value:%d ch_hit_clk_idx_min:%d ch_hit_clk_idx_max:%d ch_hit_clk_ch_start:%d ch_hit_clk_val:%d\n", i, hit._value, ch_hit_clk_idx_min, ch_hit_clk_idx_max, ch_hit_clk_ch_start, ch_hit_clk_val); if(mwa->array()->value(hit._value) != ch_hit_clk_val) { DEBUG_PRST_ROUTES_2(stderr, " calling mwa->array()->setValue\n"); mwa->array()->setValue(hit._value, ch_hit_clk_val); do_upd = true; } if(mwa->setMenuItemPressed(false) || mwa->array()->setPressedColumn(-1)) do_upd = true; // // Reset any other switch bars besides this one which are part of a QActionGroup. // // Since they are all part of an action group, force them to be exclusive regardless of their exclusivity settings. // QActionGroup* act_group = mwa->actionGroup(); // if(act_group && act_group->isExclusive()) // { // const int sz = act_group->actions().size(); // for(int i = 0; i < sz; ++i) // { // if(RoutingMatrixWidgetAction* act = qobject_cast(act_group->actions().at(i))) // { // if(act != mwa) // { // // Set any column to false, and exclusiveColumns and exclusiveToggle to true which will reset all columns. // act->array()->setValues(0, false, true, true); // act->updateCreatedWidgets(); // Redraw the indicators. // } // } // } // } if(mwa->arrayStayOpen()) accept = true; activate = true; // // Directly execute the trigger handler. // e->accept(); // routePopupActivated(mwa); } break; case RoutePopupHit::HitMenuItem: { const bool chk = !mwa->checkBoxChecked(); if(mwa->checkBoxChecked() != chk) { mwa->setCheckBoxChecked(chk); do_upd = true; } activate = true; // // Directly execute the trigger handler. // routePopupActivated(mwa); } break; case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: accept = true; // e->accept(); break; case RoutePopupHit::HitNone: break; } } if(do_upd) mwa->updateCreatedWidgets(); } } for(int i = 0; i < sz; ++i) { if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) { bool do_upd = false; // Sanity check: Only activate the item(s) which are not the active one. //if(mwa != activeAction()) if(mwa != action) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent i:%d this:%p inactive mwa:%p\n", i, this, mwa); if(ch_hit_clk_act_group && ch_hit_clk_act_group == mwa->actionGroup()) { DEBUG_PRST_ROUTES_2(stderr, " ch_hit_clk_act_group && ch_hit_clk_act_group == mwa->actionGroup()\n"); if(ch_hit_clk_act_group->isExclusive()) { // Reset any other switch bars besides this one which are part of a QActionGroup. // Since they are all part of an action group, force them to be exclusive regardless of their exclusivity settings. // Set any column to false, and exclusiveColumns and exclusiveToggle to true which will reset all columns. DEBUG_PRST_ROUTES_2(stderr, " calling mwa->array()->setValues (reset)\n"); mwa->array()->setValues(0, false, true, true); do_upd = true; } else if(i >= ch_hit_clk_idx_min && i < ch_hit_clk_idx_max) { const int ch = ch_hit_clk_ch_start + (i - ch_hit_clk_idx_min); // mwa->array()->setValue(ch, !mwa->array()->value(ch)); if(mwa->array()->value(ch) != ch_hit_clk_val) { DEBUG_PRST_ROUTES_2(stderr, " calling mwa->array()->setValue ch:%d\n", ch); mwa->array()->setValue(ch, ch_hit_clk_val); do_upd = true; } // // Directly execute the trigger handler. // e->accept(); // routePopupActivated(mwa); DEBUG_PRST_ROUTES_2(stderr, " ch:%d active col:%d pressed col:%d\n", ch_hit_clk_ch_start + (i - ch_hit_clk_idx_min), mwa->array()->activeColumn(), mwa->array()->pressedColumn()); } } // else if(mwa->setMenuItemPressed(false) || mwa->array()->setPressedColumn(-1)) do_upd = true; } if(do_upd) mwa->updateCreatedWidgets(); } } if(!action || !act_mwa) { e->ignore(); // Defer to PopupMenu, where we handle regular actions with checkboxes. PopupMenu::mouseReleaseEvent(e); DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent defer end: this:%p active action:%p\n", this, activeAction()); return; } if(accept) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent accept\n"); e->accept(); if(activate) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent accept: directly executing trigger handler routePopupActivated(act_mwa)\n"); // Directly execute the trigger handler. routePopupActivated(act_mwa); } DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent accept end: this:%p active action:%p\n", this, activeAction()); return; } // Check for Ctrl to stay open. if(!stayOpen() || (!MusEGlobal::config.popupsDefaultStayOpen && (e->modifiers() & Qt::ControlModifier) == 0)) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent No stay-open\n"); e->ignore(); // If this is the active popup widget let the ancestor activate and close it, otherwise we must close this manually. // // QMenu* m = qobject_cast(QApplication::activePopupWidget()); // // if(m == this) // // PopupMenu::mouseReleaseEvent(e); // // else // // { if(activate) { DEBUG_PRST_ROUTES_2(stderr, " activate true: directly executing trigger handler routePopupActivated(act_mwa)\n"); // Directly execute the trigger handler. routePopupActivated(act_mwa); } // Close all the popups. closeUp(); // // } return; } e->accept(); if(activate) { DEBUG_PRST_ROUTES_2(stderr, " activate true: directly executing trigger handler routePopupActivated(act_mwa)\n"); routePopupActivated(act_mwa); } DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mouseReleaseEvent end: this:%p active action:%p\n", this, activeAction()); } void RoutePopupMenu::mousePressEvent(QMouseEvent* e) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent begin: this:%p active action:%p\n", this, activeAction()); // e->ignore(); // PopupMenu::mousePressEvent(e); // DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent after begin: this:%p active action:%p\n", this, activeAction()); DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::mousePressEvent this:%p x:%d y:%d\n", this, e->pos().x(), e->pos().y()); RoutingMatrixWidgetAction* act_mwa = qobject_cast(actionAt(e->pos())); bool accept = false; int ch_hit_clk_idx_min = -1; int ch_hit_clk_idx_max = -1; int ch_hit_clk_ch_start = -1; QActionGroup* ch_hit_clk_act_group = nullptr; const int sz = actions().size(); for(int i = 0; i < sz; ++i) { if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) { bool do_upd = false; // Sanity check: Only activate the item(s) if the action truly is the active one. DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent this:%p mwa:%p activeAction:%p\n", this, mwa, activeAction()); //if(mwa == activeAction()) if(mwa == act_mwa) { DEBUG_PRST_ROUTES_2(stderr, " is active\n"); RoutePopupHit hit = mwa->hitTest(e->pos(), RoutePopupHit::HitTestClick); switch(hit._type) { case RoutePopupHit::HitChannel: { // Support grouping together of channels. ch_hit_clk_idx_min = i; ch_hit_clk_idx_max = ch_hit_clk_idx_min + MusEGlobal::config.routerGroupingChannels; if(ch_hit_clk_idx_max > sz) ch_hit_clk_idx_min = sz - MusEGlobal::config.routerGroupingChannels; ch_hit_clk_ch_start = hit._value - (i - ch_hit_clk_idx_min); const int ch_diff = mwa->array()->columns() - (ch_hit_clk_ch_start + MusEGlobal::config.routerGroupingChannels); if(ch_diff < 0) { ch_hit_clk_idx_min += ch_diff; ch_hit_clk_idx_max += ch_diff; ch_hit_clk_ch_start += ch_diff; } // Set the pressed value. If the column was already active, update again // so that pressed colour overrides highlighted colour. if(mwa->array()->setPressedColumn(hit._value) || mwa->array()->activeColumn() == hit._value) do_upd = true; ch_hit_clk_act_group = mwa->actionGroup(); DEBUG_PRST_ROUTES_2(stderr, " HitChannel ch:%d active col:%d pressed col:%d\n", hit._value, mwa->array()->activeColumn(), mwa->array()->pressedColumn()); accept = true; } break; case RoutePopupHit::HitMenuItem: if(mwa->setMenuItemPressed(true)) do_upd = true; accept = true; break; case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: if(mwa->setMenuItemPressed(false) || mwa->array()->setPressedColumn(-1)) do_upd = true; accept = true; break; case RoutePopupHit::HitNone: if(mwa->setMenuItemPressed(false) || mwa->array()->setPressedColumn(-1)) do_upd = true; // TODO Close the menu instead of letting QMenu do it (below)? break; } } if(do_upd) mwa->updateCreatedWidgets(); } } for(int i = 0; i < sz; ++i) { if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) { bool do_upd = false; // Sanity check: Only activate the item(s) which are not the active one. //if(mwa != activeAction()) if(mwa != act_mwa) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent this:%p inactive mwa:%p\n", this, mwa); if(ch_hit_clk_act_group && !ch_hit_clk_act_group->isExclusive() && ch_hit_clk_act_group == mwa->actionGroup() && i >= ch_hit_clk_idx_min && i < ch_hit_clk_idx_max) { if(mwa->array()->setPressedColumn(ch_hit_clk_ch_start + (i - ch_hit_clk_idx_min))) do_upd = true; DEBUG_PRST_ROUTES_2(stderr, " ch:%d active col:%d pressed col:%d\n", ch_hit_clk_ch_start + (i - ch_hit_clk_idx_min), mwa->array()->activeColumn(), mwa->array()->pressedColumn()); } else if(mwa->array()->setPressedColumn(-1)) do_upd = true; } if(do_upd) mwa->updateCreatedWidgets(); } } if(accept) { // // e->accept(); // // PopupMenu::mousePressEvent(e); // // return; } DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent before end: this:%p active action:%p\n", this, activeAction()); e->ignore(); PopupMenu::mousePressEvent(e); DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::mousePressEvent end: this:%p active action:%p\n", this, activeAction()); } void RoutePopupMenu::mouseMoveEvent(QMouseEvent* e) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::mouseMoveEvent this:%p\n", this); RoutingMatrixWidgetAction* act_mwa = qobject_cast(actionAt(e->pos())); // Inform the hover handler that it was a mouse hover. _hoverIsFromMouse = true; // Ignore the event and pass it on. Let any new active action and hover signal be generated before the code below is run. e->ignore(); PopupMenu::mouseMoveEvent(e); // Clear the flag. _hoverIsFromMouse = false; int ch_hit_hvr_idx_min = -1; int ch_hit_hvr_idx_max = -1; int ch_hit_hvr_ch_start = -1; QActionGroup* ch_hit_hvr_act_group = nullptr; int ch_hit_clk_idx_min = -1; int ch_hit_clk_idx_max = -1; int ch_hit_clk_ch_start = -1; QActionGroup* ch_hit_clk_act_group = nullptr; const int sz = actions().size(); for(int i = 0; i < sz; ++i) { if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) { bool do_upd = false; // Sanity check: Only activate the item(s) if the action truly is the active one. //if(mwa == activeAction()) if(mwa == act_mwa) { RoutePopupHit hit = mwa->hitTest(e->pos(), RoutePopupHit::HitTestHover); switch(hit._type) { case RoutePopupHit::HitChannel: { // Update the current 'last' hover info. _lastHoveredHit = hit; // Support grouping together of channels. ch_hit_hvr_idx_min = i; ch_hit_hvr_idx_max = ch_hit_hvr_idx_min + MusEGlobal::config.routerGroupingChannels; if(ch_hit_hvr_idx_max > sz) ch_hit_hvr_idx_min = sz - MusEGlobal::config.routerGroupingChannels; ch_hit_hvr_ch_start = hit._value - (i - ch_hit_hvr_idx_min); const int ch_diff = mwa->array()->columns() - (ch_hit_hvr_ch_start + MusEGlobal::config.routerGroupingChannels); if(ch_diff < 0) { ch_hit_hvr_idx_min += ch_diff; ch_hit_hvr_idx_max += ch_diff; ch_hit_hvr_ch_start += ch_diff; } // Set the values. if(mwa->isSelected()) { mwa->setSelected(false); do_upd = true; } if(mwa->array()->activeColumn() != hit._value) { DEBUG_PRST_ROUTES(stderr, " Setting active column:%d\n", hit._value); mwa->array()->setActiveColumn(hit._value); do_upd = true; } ch_hit_hvr_act_group = mwa->actionGroup(); } break; case RoutePopupHit::HitMenuItem: // Update the current 'last' hover info. _lastHoveredHit = hit; if(!mwa->isSelected()) { mwa->setSelected(true); do_upd = true; } if(mwa->array()->activeColumn() != -1) { mwa->array()->setActiveColumn(-1); do_upd = true; } break; case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: case RoutePopupHit::HitNone: if(mwa->isSelected()) { mwa->setSelected(false); do_upd = true; } if(mwa->array()->activeColumn() != -1) { mwa->array()->setActiveColumn(-1); do_upd = true; } break; } // } // else // { // if(mwa->isSelected()) // { // mwa->setSelected(false); // do_upd = true; // } // if(mwa->array()->activeColumn() != -1) // { // mwa->array()->setActiveColumn(-1); // do_upd = true; // } // } if(e->buttons() != Qt::NoButton) { RoutePopupHit hit = mwa->hitTest(e->pos(), RoutePopupHit::HitTestClick); switch(hit._type) { case RoutePopupHit::HitChannel: { // Support grouping together of channels. ch_hit_clk_idx_min = i; ch_hit_clk_idx_max = ch_hit_clk_idx_min + MusEGlobal::config.routerGroupingChannels; if(ch_hit_clk_idx_max > sz) ch_hit_clk_idx_min = sz - MusEGlobal::config.routerGroupingChannels; ch_hit_clk_ch_start = hit._value - (i - ch_hit_clk_idx_min); const int ch_diff = mwa->array()->columns() - (ch_hit_clk_ch_start + MusEGlobal::config.routerGroupingChannels); if(ch_diff < 0) { ch_hit_clk_idx_min += ch_diff; ch_hit_clk_idx_max += ch_diff; ch_hit_clk_ch_start += ch_diff; } // Set the value. if(mwa->array()->setPressedColumn(hit._value)) do_upd = true; ch_hit_clk_act_group = mwa->actionGroup(); } break; case RoutePopupHit::HitMenuItem: if(mwa->setMenuItemPressed(true)) do_upd = true; break; case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: case RoutePopupHit::HitNone: if(mwa->setMenuItemPressed(false) || mwa->array()->setPressedColumn(-1)) do_upd = true; break; } } } if(do_upd) mwa->updateCreatedWidgets(); } } for(int i = 0; i < sz; ++i) { if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) { bool do_upd = false; // Sanity check: Only activate the item(s) which are not the active one. //if(mwa != activeAction()) if(mwa != act_mwa) { if(mwa->isSelected()) { mwa->setSelected(false); do_upd = true; } if(ch_hit_hvr_act_group && !ch_hit_hvr_act_group->isExclusive() && ch_hit_hvr_act_group == mwa->actionGroup() && i >= ch_hit_hvr_idx_min && i < ch_hit_hvr_idx_max) { const int ch = ch_hit_hvr_ch_start + (i - ch_hit_hvr_idx_min); if(mwa->array()->activeColumn() != ch) { DEBUG_PRST_ROUTES(stderr, " Setting inactive column:%d\n", ch); mwa->array()->setActiveColumn(ch); do_upd = true; } } else if(mwa->array()->activeColumn() != -1) { mwa->array()->setActiveColumn(-1); do_upd = true; } // } if(e->buttons() != Qt::NoButton && ch_hit_clk_act_group && !ch_hit_clk_act_group->isExclusive() && ch_hit_clk_act_group == mwa->actionGroup() && i >= ch_hit_clk_idx_min && i < ch_hit_clk_idx_max) { if(mwa->array()->setPressedColumn(ch_hit_clk_ch_start + (i - ch_hit_clk_idx_min))) do_upd = true; } else if(mwa->array()->setPressedColumn(-1)) do_upd = true; } if(do_upd) mwa->updateCreatedWidgets(); } } } void RoutePopupMenu::routePopupHovered(QAction* action) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::popHovered this:%p action:%p _hoverIsFromMouse:%d text:%s\n", this, action, _hoverIsFromMouse, action->text().toLatin1().constData()); // Ignore if this hover was from mouse. // Also, we get this hovered signal even if the hovered action is from another popup, so ignore it. if(!_hoverIsFromMouse && actions().contains(action)) { const int sz = actions().size(); for(int i = 0; i < sz; ++i) { if(RoutingMatrixWidgetAction* mwa = qobject_cast(actions().at(i))) { bool do_upd = false; if(mwa == action) { switch(_lastHoveredHit._type) { case RoutePopupHit::HitChannel: { if(mwa->isSelected()) { mwa->setSelected(false); do_upd = true; } const int cols = mwa->array()->columns(); if(cols != 0) { int col = _lastHoveredHit._value; // The column. if(col >= cols) { col = cols - 1; // Clip it. _lastHoveredHit._value = col; // Adjust the current 'last' column setting. } if(mwa->array()->activeColumn() != col) { mwa->array()->setActiveColumn(col); do_upd = true; } } } break; case RoutePopupHit::HitMenuItem: if(mwa->hasCheckBox() && !mwa->isSelected()) { mwa->setSelected(true); do_upd = true; } if(mwa->array()->activeColumn() != -1) { mwa->array()->setActiveColumn(-1); do_upd = true; } break; case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: case RoutePopupHit::HitNone: // If it has a checkbox (or there is no channel bar) select the checkbox/text area. if(mwa->hasCheckBox() || mwa->array()->columns() == 0) { // Update the current 'last' hover info. _lastHoveredHit._type = RoutePopupHit::HitMenuItem; _lastHoveredHit._action = mwa; _lastHoveredHit._value = 0; if(!mwa->isSelected()) { mwa->setSelected(true); do_upd = true; } } // Otherwise select the first available channel bar column. else { // Update the current 'last' hover info. _lastHoveredHit._type = RoutePopupHit::HitChannel; _lastHoveredHit._action = mwa; _lastHoveredHit._value = 0; if(mwa->array()->activeColumn() != 0) { mwa->array()->setActiveColumn(0); do_upd = true; } } break; } } else { if(mwa->isSelected()) { mwa->setSelected(false); // Reset the checkbox/text active area. do_upd = true; } if(mwa->array()->activeColumn() != -1) { mwa->array()->setActiveColumn(-1); // Reset any active column. do_upd = true; } } if(do_upd) mwa->updateCreatedWidgets(); } } } // Clear the flag. //_hoverIsFromMouse = false; } void RoutePopupMenu::keyPressEvent(QKeyEvent* e) { if(activeAction()) { if(RoutingMatrixWidgetAction* mwa = qobject_cast(activeAction())) { bool do_upd = false; bool key_accepted = false; const int key = e->key(); switch(key) { case Qt::Key_Left: { switch(_lastHoveredHit._type) { case RoutePopupHit::HitMenuItem: // Allow the menu to close. break; case RoutePopupHit::HitChannel: // If we're on the first available channel and there's no checkbox, allow the menu to close. if(_lastHoveredHit._value == 0 && !mwa->hasCheckBox()) break; // Fall through. case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: case RoutePopupHit::HitNone: { // Get the next available item to the left. RoutePopupHit hit = mwa->previousHit(_lastHoveredHit); switch(hit._type) { case RoutePopupHit::HitChannel: { if(mwa->isSelected()) { mwa->setSelected(false); do_upd = true; } if(mwa->array()->activeColumn() != hit._value) { mwa->array()->setActiveColumn(hit._value); do_upd = true; } // Update the current 'last' hover info. _lastHoveredHit = hit; key_accepted = true; } break; case RoutePopupHit::HitMenuItem: if(!mwa->isSelected()) { mwa->setSelected(true); do_upd = true; } if(mwa->array()->activeColumn() != -1) { mwa->array()->setActiveColumn(-1); do_upd = true; } // Update the current 'last' hover info. _lastHoveredHit = hit; key_accepted = true; break; case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: case RoutePopupHit::HitNone: if(mwa->isSelected()) { mwa->setSelected(false); do_upd = true; } if(mwa->array()->activeColumn() != -1) { mwa->array()->setActiveColumn(-1); do_upd = true; } // Update the current 'last' hover info. _lastHoveredHit = hit; key_accepted = true; break; } } break; } } break; case Qt::Key_Right: { switch(_lastHoveredHit._type) { case RoutePopupHit::HitChannel: // If we're on the last available channel, allow any submenu to open. if(mwa->array()->columns() != 0 && _lastHoveredHit._value == mwa->array()->columns() - 1) break; // Fall through. case RoutePopupHit::HitMenuItem: case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: case RoutePopupHit::HitNone: { // Get the next available item to the right. RoutePopupHit hit = mwa->nextHit(_lastHoveredHit); switch(hit._type) { case RoutePopupHit::HitChannel: { if(mwa->isSelected()) { mwa->setSelected(false); do_upd = true; } if(mwa->array()->activeColumn() != hit._value) { mwa->array()->setActiveColumn(hit._value); do_upd = true; } // Update the current 'last' hover info. _lastHoveredHit = hit; key_accepted = true; } break; case RoutePopupHit::HitMenuItem: if(!mwa->isSelected()) { mwa->setSelected(true); do_upd = true; } if(mwa->array()->activeColumn() != -1) { mwa->array()->setActiveColumn(-1); do_upd = true; } // Update the current 'last' hover info. _lastHoveredHit = hit; key_accepted = true; break; case RoutePopupHit::HitChannelBar: case RoutePopupHit::HitSpace: case RoutePopupHit::HitNone: if(mwa->isSelected()) { mwa->setSelected(false); do_upd = true; } if(mwa->array()->activeColumn() != -1) { mwa->array()->setActiveColumn(-1); do_upd = true; } // Update the current 'last' hover info. _lastHoveredHit = hit; key_accepted = true; break; } } } } break; default: break; } if(do_upd) mwa->updateCreatedWidgets(); if(key_accepted) { e->accept(); return; } } } e->ignore(); PopupMenu::keyPressEvent(e); } void RoutePopupMenu::songChanged(MusECore::SongChangedStruct_t val) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::songChanged flags:%ld", (long int)val._flags); if(val & (SC_ROUTE | SC_CHANNELS | SC_CONFIG)) updateRouteMenus(); if(val & SC_PORT_ALIAS_PREFERENCE) preferredPortAliasChanged(); if(val & SC_ROUTER_CHANNEL_GROUPING) routerChannelGroupingChanged(); } bool RoutePopupMenu::updateItemTexts(PopupMenu* menu) { if(!menu) menu = this; QList list = menu->actions(); const int sz = list.size(); bool changed = false; for(int i = 0; i < sz; ++i) { QAction* act = list.at(i); if(RoutingMatrixWidgetAction* wa = qobject_cast(act)) { // Take care of struct Route first. Insert other future custom structures here too ! if(act->data().canConvert()) { const MusECore::Route r = act->data().value(); switch(r.type) { case MusECore::Route::JACK_ROUTE: { if(MusEGlobal::checkAudioDevice()) { char good_name[ROUTE_PERSISTENT_NAME_SIZE]; const char* port_name = r.persistentJackPortName; void* const port = MusEGlobal::audioDevice->findPort(port_name); if(port) { MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE, MusEGlobal::config.preferredRouteNameOrAlias); const QString str(good_name); if(wa->actionText() != str) { wa->setActionText(str); changed = true; } } if(changed) { // wa->updateChannelArray(); } } } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: break; } } } else // Take care of struct Route first. Insert other future custom structures here too ! if(act->data().canConvert()) { const MusECore::Route r = act->data().value(); switch(r.type) { case MusECore::Route::JACK_ROUTE: act->setText(r.displayName(MusEGlobal::config.preferredRouteNameOrAlias)); break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: break; } } } return changed; } // Updates item texts and the 'preferred alias action'. Returns true if any action was changed. bool RoutePopupMenu::preferredPortAliasChanged() { QList list = actions(); const int sz = list.size(); bool changed = false; for(int i = 0; i < sz; ++i) { QAction* act = list.at(i); // Check for custom widget action. if(RoutingMatrixWidgetAction* wa = qobject_cast(act)) { // Check for Route data type. // Take care of struct Route first. Add other future custom structures here too. if(act->data().canConvert()) { const MusECore::Route r = act->data().value(); switch(r.type) { case MusECore::Route::JACK_ROUTE: { if(MusEGlobal::checkAudioDevice()) { char good_name[ROUTE_PERSISTENT_NAME_SIZE]; const char* port_name = r.persistentJackPortName; void* const port = MusEGlobal::audioDevice->findPort(port_name); if(port) { MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE, MusEGlobal::config.preferredRouteNameOrAlias); const QString str(good_name); if(wa->actionText() != str) { wa->setActionText(str); changed = true; } } } } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: break; } } // No Route data type. Check for int data IDs. // Handle future data types above, before this in case those types might be convertible to int. else { bool ok = false; const int n = act->data().toInt(&ok); if(ok) { switch(n) { #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ // Check for the 'preferred port alias' action. case _ALIASES_WIDGET_ACTION_: { int v; if(wa->array()->value(0)) v = MusEGlobal::RoutePreferFirstAlias; else if(wa->array()->value(1)) v = MusEGlobal::RoutePreferSecondAlias; else v = MusEGlobal::RoutePreferCanonicalName; if(v != MusEGlobal::config.preferredRouteNameOrAlias) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::preferredPortAliasChanged setting alias array preferredRouteNameOrAlias:%d\n", MusEGlobal::config.preferredRouteNameOrAlias); switch(MusEGlobal::config.preferredRouteNameOrAlias) { case MusEGlobal::RoutePreferFirstAlias: wa->array()->setValue(0, true); break; case MusEGlobal::RoutePreferSecondAlias: wa->array()->setValue(1, true); break; case MusEGlobal::RoutePreferCanonicalName: // Just set any column to false to clear this exclusive array. wa->array()->setValue(0, false); break; } changed = true; } } break; #endif default: break; } } } } // Not a custom widget action. Check for Route data type. // Take care of struct Route first. Add other future custom structures here too. else if(act->data().canConvert()) { const MusECore::Route r = act->data().value(); switch(r.type) { case MusECore::Route::JACK_ROUTE: { const QString rname = r.displayName(MusEGlobal::config.preferredRouteNameOrAlias); if(act->text() != rname) { act->setText(rname); changed = true; } } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: break; } } } return changed; } bool RoutePopupMenu::routerChannelGroupingChanged() { QList list = actions(); const int sz = list.size(); bool changed = false; for(int i = 0; i < sz; ++i) { QAction* act = list.at(i); // Check for custom widget action. if(RoutingMatrixWidgetAction* wa = qobject_cast(act)) { // Check for Route data type. // Take care of struct Route first. Add other future custom structures here too. if(act->data().canConvert()) { // Nothing to do here yet. } // No Route data type. Check for int data IDs. // Handle future data types above, before this in case those types might be convertible to int. else { bool ok = false; const int n = act->data().toInt(&ok); if(ok) { switch(n) { #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ // Check for the 'grouping channels' action. case _GROUPING_CHANNELS_WIDGET_ACTION_: { int v; if(wa->array()->value(0)) v = 1; else v = 2; if(v != MusEGlobal::config.routerGroupingChannels) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::routerChannelGroupingChanged setting array routerGroupingChannels:%d\n", MusEGlobal::config.routerGroupingChannels); switch(MusEGlobal::config.routerGroupingChannels) { case 1: wa->array()->setValue(0, true); changed = true; break; case 2: wa->array()->setValue(1, true); changed = true; break; default: break; } } } break; #endif default: break; } } } } // Not a custom widget action. Check for Route data type. // Take care of struct Route first. Add other future custom structures here too. else if(act->data().canConvert()) { // Nothing to do here yet. } } return changed; } PopupMenu* RoutePopupMenu::cloneMenu(const QString& title, QWidget* parent, bool /*stayOpen*/, bool showTooltips) { PopupMenu* m = new RoutePopupMenu(_route, title, parent, _isOutMenu, _broadcastChanges); m->setToolTipsVisible(showTooltips); return m; } void RoutePopupMenu::updateRouteMenus() { // NOTE: The purpose of this routine is to make sure the items actually reflect // the routing status. // In case for some reason a route could not be added (or removed). // Then the item will be properly un-checked (or checked) here. // TODO Fix this up a bit. It doesn't quite respond to complete removal, and other situations are a bit odd. // Best to ignore it for now since it only half-works. p4.0.42 /* //printf("RoutePopupMenu::updateRouteMenus\n"); if(!_track || actions().isEmpty() || !isVisible()) return; MusECore::RouteList* rl = _isOutMenu ? _track->outRoutes() : _track->inRoutes(); // Clear all the action check marks. clearAllChecks(); // Take care of Midi Port to Audio Input routes first... if(_isOutMenu && _track->isMidiTrack()) { int port = ((MusECore::MidiTrack*)_track)->outPort(); if(port >= 0 && port < MIDI_PORTS) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::RouteList* mprl = mp->outRoutes(); for (MusECore::ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track && ir->track->type() == MusECore::Track::AUDIO_INPUT) { for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { int chbits = 1 << ch; if(ir->channel & chbits) { MusECore::Route r(ir->track, chbits); //printf("RoutePopupMenu::updateRouteMenus MusECore::MidiPort to AudioInput chbits:%d\n", chbits); // QAction* act = findActionFromData(QVariant::fromValue(r)); if(act) { //printf(" ... Found\n"); // act->setChecked(true); } } } } } } } // Now check the ones that are found in the route list. for(MusECore::ciRoute irl = rl->begin(); irl != rl->end(); ++irl) { // Do MidiTrack to MidiPort routes... if(irl->type == MusECore::Route::MIDI_PORT_ROUTE) { #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ // Widget action handles channels. Look for route with channels ignored and set to zero. MusECore::Route r(irl->midiPort, 0); QAction* act = findActionFromData(QVariant::fromValue(r)); if(act) { //printf("RoutePopupMenu::updateRouteMenus found MidiTrack to MidiPort irl type:%d\n", irl->type); // // Check for custom widget actions first. PixmapButtonsWidgetAction* mc_wa = dynamic_cast(act); if(mc_wa) { //printf(" ... Found custom, setting current state\n"); // mc_wa->setCurrentState(irl->channel); // Set all channels at once. } } #else //printf("RoutePopupMenu::updateRouteMenus MIDI_PORT_ROUTE\n"); for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { int chbits = 1 << ch; if(irl->channel & chbits) { MusECore::Route r(irl->midiPort, chbits); //printf("RoutePopupMenu::updateRouteMenus MidiTrack to MidiPort irl type:%d\n", irl->type); // // If act is a PixmapButtonsWidgetAction, route channel is ignored and is zero. QAction* act = findActionFromData(QVariant::fromValue(r)); if(act) { //printf(" ... Found\n"); // act->setChecked(true); } } } #endif // _USE_CUSTOM_WIDGET_ACTIONS_ } else // Do all other routes... { #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ // Do MidiPort to MidiTrack routes... if(irl->type == MusECore::Route::TRACK_ROUTE && irl->track && irl->track->type() == MusECore::Track::AUDIO_INPUT) { // Widget action handles channels. Look for route with channels ignored and set to zero. MusECore::Route r(irl->track, 0); QAction* act = findActionFromData(QVariant::fromValue(r)); if(act) { // Check for custom widget actions first. PixmapButtonsWidgetAction* mc_wa = dynamic_cast(act); if(mc_wa) { //printf("RoutePopupMenu::updateRouteMenus found custom irl type:%d\n", irl->type); // mc_wa->setCurrentState(irl->channel); // Set all channels at once. continue; } } } #endif // _USE_CUSTOM_WIDGET_ACTIONS_ //printf("RoutePopupMenu::updateRouteMenus other irl type:%d\n", irl->type); if(act) { //printf("RoutePopupMenu::updateRouteMenus found other irl type:%d\n", irl->type); // act->setChecked(true); } } } */ } void RoutePopupMenu::trackRouteActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations) { // Check for custom routing matrix action. RoutingMatrixWidgetAction* matrix_wa = qobject_cast(action); if(!matrix_wa) return; switch(rem_route.type) { case MusECore::Route::TRACK_ROUTE: { // Make sure the track still exists. if(MusEGlobal::song->tracks()->find(rem_route.track) == MusEGlobal::song->tracks()->end()) return; DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::trackRouteActivated:\n"); MusECore::Track* track = _route.track; MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Track types must be same. if((track->isMidiTrack() && !t->isMidiTrack()) || (t->type() != track->type())) continue; // We are looking for the given track alone if unselected, or else all selected tracks. // Ignore other tracks if broadcasting changes is disabled. if(t != track && (!_broadcastChanges || !t->selected() || !track->selected())) continue; const int cols = matrix_wa->array()->columns(); for(int col = 0; col < cols; ++col) { MusECore::Route this_route(t, col, 1); rem_route.channels = 1; const MusECore::Route& src = _isOutMenu ? this_route : rem_route; const MusECore::Route& dst = _isOutMenu ? rem_route : this_route; DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::trackRouteActivated: checking operations\n"); const bool val = matrix_wa->array()->value(col); // Connect if route does not exist. Allow it to reconnect a partial route. if(val && MusECore::routeCanConnect(src, dst)) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::trackRouteActivated: adding AddRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); } // Disconnect if route exists. Allow it to reconnect a partial route. else if(!val && MusECore::routeCanDisconnect(src, dst)) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::trackRouteActivated: adding DeleteRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); } } } } break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: return; break; } } void RoutePopupMenu::jackRouteActivated(QAction* action, const MusECore::Route& route, const MusECore::Route& rem_route, MusECore::PendingOperationList& operations) { // Check for custom routing matrix action. RoutingMatrixWidgetAction* matrix_wa = qobject_cast(action); if(!matrix_wa) return; if(!MusEGlobal::checkAudioDevice()) return; DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::jackRouteActivated: Matrix\n"); const int cols = matrix_wa->array()->columns(); const char* const port_name = rem_route.persistentJackPortName; void* const port = MusEGlobal::audioDevice->findPort(port_name); if(port) { for(int col = 0; col < cols; ++col) { MusECore::Route this_route(route); switch(route.type) { case MusECore::Route::MIDI_DEVICE_ROUTE: this_route.channel = -1; break; case MusECore::Route::TRACK_ROUTE: { this_route.channel = col; MusECore::Track* track = route.track; if(track) { MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Track types must be same. if((track->isMidiTrack() && !t->isMidiTrack()) || (t->type() != track->type())) continue; // We are looking for the given track alone if unselected, or else all selected tracks. // Ignore other tracks if broadcasting changes is disabled. if(t != track && (!_broadcastChanges || !t->selected() || !track->selected())) continue; this_route.track = t; // TODO Lazy identical copy of code below. Streamline somehow... const MusECore::Route r_route(port); const MusECore::Route& src = _isOutMenu ? this_route : r_route; const MusECore::Route& dst = _isOutMenu ? r_route : this_route; const bool val = matrix_wa->array()->value(col); DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: checking operations col:%d val:%d\n", col, val); // Connect if route does not exist. Allow it to reconnect a partial route. if(val && MusECore::routeCanConnect(src, dst)) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: adding AddRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); } // Disconnect if route exists. Allow it to reconnect a partial route. else if(!val && MusECore::routeCanDisconnect(src, dst)) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: adding DeleteRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); } } } // We took care of it. Continue with the next column. continue; } break; case MusECore::Route::MIDI_PORT_ROUTE: if(route.midiPort == -1) return; if(MusECore::MidiDevice* md = MusEGlobal::midiPorts[route.midiPort].device()) { this_route.type = MusECore::Route::MIDI_DEVICE_ROUTE; this_route.device = md; this_route.channel = -1; } else return; break; case MusECore::Route::JACK_ROUTE: break; } const MusECore::Route r_route(port); const MusECore::Route& src = _isOutMenu ? this_route : r_route; const MusECore::Route& dst = _isOutMenu ? r_route : this_route; const bool val = matrix_wa->array()->value(col); DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: checking operations col:%d val:%d\n", col, val); // Connect if route does not exist. Allow it to reconnect a partial route. if(val && MusECore::routeCanConnect(src, dst)) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: adding AddRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); } // Disconnect if route exists. Allow it to reconnect a partial route. else if(!val && MusECore::routeCanDisconnect(src, dst)) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::jackRouteActivated: adding DeleteRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); } } } } void RoutePopupMenu::audioTrackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::audioTrackPopupActivated: action text:%s checked:%d name:%s\n", action->text().toLatin1().constData(), action->isChecked(), action->objectName().toLatin1().constData()); MusECore::Track* track = _route.track; // Check for custom routing matrix action. RoutingMatrixWidgetAction* matrix_wa = qobject_cast(action); if(matrix_wa) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::audioTrackPopupActivated: Matrix\n"); switch(rem_route.type) { case MusECore::Route::JACK_ROUTE: jackRouteActivated(action, _route, rem_route, operations); break; case MusECore::Route::TRACK_ROUTE: trackRouteActivated(action, rem_route, operations); break; case MusECore::Route::MIDI_DEVICE_ROUTE: break; case MusECore::Route::MIDI_PORT_ROUTE: break; } } #ifndef _USE_SIMPLIFIED_SOLO_CHAIN_ // Support Midi Port to Audio Input routes. else if(!_isOutMenu && track->type() == MusECore::Track::AUDIO_INPUT && rem_route.type == MusECore::Route::MIDI_PORT_ROUTE) { MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Track types must be same. if((track->isMidiTrack() && !t->isMidiTrack()) || (t->type() != track->type())) continue; // We are looking for the given track alone if unselected, or else all selected tracks. // Ignore other tracks if broadcasting changes is disabled. if(t != track && (!_broadcastChanges || !t->selected() || !track->selected())) continue; MusECore::RouteList* rl = _isOutMenu ? t->outRoutes() : t->inRoutes(); // Check for custom midi channel select action. PixmapButtonsWidgetAction* cs_wa = qobject_cast(action); if(cs_wa) { const QBitArray ba = cs_wa->currentState(); const int ba_sz = ba.size(); int chbits = 0; for(int mch = 0; mch < MusECore::MUSE_MIDI_CHANNELS && mch < ba_sz; ++mch) { if(ba.at(mch)) chbits |= (1 << mch); } rem_route.channel = chbits; // Restore the desired route channels from the custom widget action state. int mdidx = rem_route.midiPort; int chmask = 0; MusECore::ciRoute iir = rl->begin(); for (; iir != rl->end(); ++iir) if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) { // Is there already a route to this port? chmask = iir->channel; // Grab the channel mask. break; } // Only if something changed... if(chmask != chbits) { if(chmask != 0) { // Disconnect all existing channels. MusECore::Route dstRoute(t, chmask); operations.add(MusECore::PendingOperationItem(*iir, dstRoute, MusECore::PendingOperationItem::DeleteRoute)); } if(chbits != 0) { // Connect desired channels. MusECore::Route dstRoute(t, chbits); operations.add(MusECore::PendingOperationItem(rem_route, dstRoute, MusECore::PendingOperationItem::AddRoute)); } } } else { int chbit = rem_route.channel; MusECore::Route dstRoute(t, chbit); int mdidx = rem_route.midiPort; int chmask = 0; MusECore::ciRoute iir = rl->begin(); for (; iir != rl->end(); ++iir) { if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // Is there already a route to this port? { chmask = iir->channel; // Grab the channel mask. break; } } if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? operations.add(MusECore::PendingOperationItem(rem_route, dstRoute, MusECore::PendingOperationItem::DeleteRoute)); else operations.add(MusECore::PendingOperationItem(rem_route, dstRoute, MusECore::PendingOperationItem::AddRoute)); } } } #endif // _USE_SIMPLIFIED_SOLO_CHAIN_ else { // Make sure the track still exists. if(MusEGlobal::song->tracks()->find(rem_route.track) == MusEGlobal::song->tracks()->end()) return; MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Track types must be same. if((track->isMidiTrack() && !t->isMidiTrack()) || (t->type() != track->type())) continue; // We are looking for the given track alone if unselected, or else all selected tracks. // Ignore other tracks if broadcasting changes is disabled. if(t != track && (!_broadcastChanges || !t->selected() || !track->selected())) continue; MusECore::Route this_route(t, rem_route.channel, rem_route.channels); this_route.remoteChannel = rem_route.remoteChannel; const MusECore::Route& src = _isOutMenu ? this_route : rem_route; const MusECore::Route& dst = _isOutMenu ? rem_route : this_route; // Connect if route does not exist. Allow it to reconnect a partial route. if(action->isChecked() && MusECore::routeCanConnect(src, dst)) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::audioTrackPopupActivated: Route: adding AddRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); } // Disconnect if route exists. Allow it to reconnect a partial route. else if(!action->isChecked() && MusECore::routeCanDisconnect(src, dst)) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::audioTrackPopupActivated: Route: adding DeleteRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); } } } } void RoutePopupMenu::midiTrackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: action text:%s checked:%d name:%s\n", action->text().toLatin1().constData(), action->isChecked(), action->objectName().toLatin1().constData()); MusECore::Track* track = _route.track; if(rem_route.type == MusECore::Route::TRACK_ROUTE && rem_route.track && // Make sure the track still exists. MusEGlobal::song->tracks()->find(rem_route.track) != MusEGlobal::song->tracks()->end() && rem_route.track->type() == MusECore::Track::AUDIO_INPUT) { MusECore::MidiTrackList* tracks = MusEGlobal::song->midis(); for(MusECore::iMidiTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::MidiTrack* mt = *it; // We are looking for the given track alone if unselected, or else all selected tracks. // Ignore other tracks if broadcasting changes is disabled. if(mt != track && (!_broadcastChanges || !mt->selected() || !track->selected())) continue; #ifdef _USE_SIMPLIFIED_SOLO_CHAIN_ // Support Midi Track to Audio Input track soloing chain routes. // Support omni routes only, because if channels are supported, the graphical router becomes more complicated. if(_isOutMenu && rem_route.channel == -1) { const MusECore::Route this_route(mt, -1); operations.add(MusECore::PendingOperationItem(this_route, rem_route, action->isChecked() ? MusECore::PendingOperationItem::AddRoute : MusECore::PendingOperationItem::DeleteRoute)); } #else // Support Midi Port to Audio Input track routes. int chbit = rem_route.channel; int port = mt->outPort(); if(port < 0 || port >= MIDI_PORTS) return; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::Route bRoute(port, chbit); int chmask = 0; MusECore::RouteList* mprl = _isOutMenu ? mp->outRoutes() : mp->inRoutes(); MusECore::ciRoute ir = mprl->begin(); for (; ir != mprl->end(); ++ir) if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == rem_route.track) { // Is there already a route to this port? chmask = ir->channel; // Grab the channel mask. break; } if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? { // disconnect if(_isOutMenu) operations.add(MusECore::PendingOperationItem(bRoute, rem_route, MusECore::PendingOperationItem::DeleteRoute)); else operations.add(MusECore::PendingOperationItem(rem_route, bRoute, MusECore::PendingOperationItem::DeleteRoute)); } else { // connect if(_isOutMenu) operations.add(MusECore::PendingOperationItem(bRoute, rem_route, MusECore::PendingOperationItem::AddRoute)); else operations.add(MusECore::PendingOperationItem(rem_route, bRoute, MusECore::PendingOperationItem::AddRoute)); } #endif } } // Midi track to Midi Port routes. else if(rem_route.type == MusECore::Route::MIDI_PORT_ROUTE) { // Check for custom routing matrix action. RoutingMatrixWidgetAction* matrix_wa = qobject_cast(action); MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; MusECore::MidiTrackList* tracks = MusEGlobal::song->midis(); for(MusECore::iMidiTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::MidiTrack* mt = *it; // We are looking for the given track alone if unselected, or else all selected tracks. // Ignore other tracks if broadcasting changes is disabled. if(mt != track && (!_broadcastChanges || !mt->selected() || !track->selected())) continue; if(matrix_wa) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated midi port: matrix:\n"); #ifdef _USE_MIDI_ROUTE_PER_CHANNEL_ const int cols = matrix_wa->array()->columns(); switch(rem_route.type) { case MusECore::Route::MIDI_PORT_ROUTE: { if(rem_route.isValid() && rem_route.midiPort != -1) { // Do channel routes... for(int col = 0; col < cols && col < MusECore::MUSE_MIDI_CHANNELS; ++col) { const bool val = matrix_wa->array()->value(col); #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // In this case the channel bar (and any other channel bars in a QActionGroup) will be in exclusive mode... if(_isOutMenu) { if(val) { const bool p_changed = rem_route.midiPort != mt->outPort(); const bool c_changed = col != mt->outChannel(); if(p_changed || c_changed) { // Avoid repeated idlings. And remember to un-idle outside of the loop! if(!MusEGlobal::audio->isIdle()) MusEGlobal::audio->msgIdle(true); if(p_changed && c_changed) changed |= mt->setOutPortAndChannelAndUpdate(rem_route.midiPort, col, false); else if(p_changed) changed |= mt->setOutPortAndUpdate(rem_route.midiPort, false); else if(c_changed) changed |= mt->setOutChanAndUpdate(col, false); } break; } continue; } #endif MusECore::Route this_route(mt, col); rem_route.channel = col; const MusECore::Route& src = _isOutMenu ? this_route : rem_route; const MusECore::Route& dst = _isOutMenu ? rem_route : this_route; DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: checking operations\n"); // Connect if route does not exist. Allow it to reconnect a partial route. if(val && MusECore::routeCanConnect(src, dst)) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: adding AddRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); } // Disconnect if route exists. Allow it to reconnect a partial route. else if(!val && MusECore::routeCanDisconnect(src, dst)) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: adding DeleteRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); } } } // Do Omni route... if(matrix_wa->hasCheckBox()) { const bool cb_val = matrix_wa->checkBoxChecked(); MusECore::Route this_route(mt); rem_route.channel = -1; const MusECore::Route& src = _isOutMenu ? this_route : rem_route; const MusECore::Route& dst = _isOutMenu ? rem_route : this_route; DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: Omni checking operations\n"); // Connect if route does not exist. Allow it to reconnect a partial route. if(cb_val && MusECore::routeCanConnect(src, dst)) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: Omni adding AddRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); } // Disconnect if route exists. Allow it to reconnect a partial route. else if(!cb_val && MusECore::routeCanDisconnect(src, dst)) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: Omni adding DeleteRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); } } } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } #else MusECore::RouteList* rl = _isOutMenu ? mt->outRoutes() : mt->inRoutes(); const int cols = matrix_wa->array()->columns(); switch(rem_route.type) { case MusECore::Route::MIDI_PORT_ROUTE: { if(rem_route.isValid() && rem_route.midiPort != -1) { int chmask = 0; // Is there already a route? for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case MusECore::Route::MIDI_PORT_ROUTE: if(ir->midiPort == rem_route.midiPort) chmask = ir->channel; // Grab the channels. break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } if(chmask != 0) break; } int chbits = 0; for(int col = 0; col < cols; ++col) { if(matrix_wa->array()->value(col)) chbits |= (1 << col); } // Only if something changed... if(chmask != chbits) { if(chmask != 0) { MusECore::Route bRoute(mt, chmask); // Disconnect all existing channels. if(_isOutMenu) operations.add(MusECore::PendingOperationItem(bRoute, r, MusECore::PendingOperationItem::DeleteRoute)); else operations.add(MusECore::PendingOperationItem(r, bRoute, MusECore::PendingOperationItem::DeleteRoute)); } if(chbits != 0) { // Connect desired channels. MusECore::Route bRoute(mt, chbits); if(_isOutMenu) operations.add(MusECore::PendingOperationItem(bRoute, r, MusECore::PendingOperationItem::AddRoute)); else operations.add(MusECore::PendingOperationItem(r, bRoute, MusECore::PendingOperationItem::AddRoute)); } } } } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } #endif } else { #ifdef _USE_MIDI_ROUTE_PER_CHANNEL_ MusECore::Route this_route(mt, rem_route.channel); const MusECore::Route& src = _isOutMenu ? this_route : rem_route; const MusECore::Route& dst = _isOutMenu ? rem_route : this_route; // Connect if route does not exist. Allow it to reconnect a partial route. if(action->isChecked() && MusECore::routeCanConnect(src, dst)) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: Route: adding AddRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::AddRoute)); } // Disconnect if route exists. Allow it to reconnect a partial route. else if(!action->isChecked() && MusECore::routeCanDisconnect(src, dst)) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated: Route: adding DeleteRoute operation\n"); operations.add(MusECore::PendingOperationItem(src, dst, MusECore::PendingOperationItem::DeleteRoute)); } #else int chbit = rem_route.channel; MusECore::Route bRoute(mt, chbit); int mdidx = rem_route.midiPort; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mdidx]; MusECore::MidiDevice* md = mp->device(); //if(!md) // Rem. Allow connections to ports with no device. // return; if(md && !(md->rwFlags() & (_isOutMenu ? 1 : 2))) return; int chmask = 0; MusECore::ciRoute iir = rl->begin(); for (; iir != rl->end(); ++iir) { if(iir->type == MusECore::Route::MIDI_PORT_ROUTE && iir->midiPort == mdidx) // Is there already a route to this port? { chmask = iir->channel; // Grab the channel mask. break; } } if ((chmask & chbit) == chbit) // Is the channel's bit(s) set? { // disconnect if(_isOutMenu) operations.add(MusECore::PendingOperationItem(bRoute, rem_route, MusECore::PendingOperationItem::DeleteRoute)); else operations.add(MusECore::PendingOperationItem(rem_route, bRoute, MusECore::PendingOperationItem::DeleteRoute)); } else { // connect if(_isOutMenu) operations.add(MusECore::PendingOperationItem(bRoute, rem_route, MusECore::PendingOperationItem::AddRoute)); else operations.add(MusECore::PendingOperationItem(rem_route, bRoute, MusECore::PendingOperationItem::AddRoute)); } #endif } } // If we are idling, we made some changes. Make sure to un-idle and update. if(MusEGlobal::audio->isIdle()) { MusEGlobal::audio->msgIdle(false); MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); } } // Midi device to Jack port routes - via Midi Track. // NOTE: When a submenu's action is activated, its top-most menu always gets the activation call. // Try this simple (ideal) method, otherwise it requires we abandon the '_route' member and store TWO // routes in EACH action: In this case one is the midi device route and the other is the jack route. else if(rem_route.type == MusECore::Route::JACK_ROUTE) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated rem_route is JACK_ROUTE\n"); if(QAction* act = activeAction()) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated active action:%p\n", act); if(act->data().canConvert()) { const MusECore::Route route = act->data().value(); DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated:Jack route: activePopupWidget:%p this:%p class:%s route type:%d\n", QApplication::activePopupWidget(), this, metaObject()->className(), route.type); if(route.type == MusECore::Route::MIDI_PORT_ROUTE) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::midiTrackPopupActivated Midi port to Jack route\n"); jackRouteActivated(action, route, rem_route, operations); } } } } } void RoutePopupMenu::trackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations) { DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::trackPopupActivated: action text:%s checked:%d name:%s\n", action->text().toLatin1().constData(), action->isChecked(), action->objectName().toLatin1().constData()); MusECore::Track* track = _route.track; // Make sure the track still exists. if(MusEGlobal::song->tracks()->find(track) == MusEGlobal::song->tracks()->end()) return; if(track->isMidiTrack()) midiTrackPopupActivated(action, rem_route, operations); else audioTrackPopupActivated(action, rem_route, operations); } void RoutePopupMenu::routePopupActivated(QAction* action) { if(!action || !_route.isValid() || actions().isEmpty()) return; MusECore::PendingOperationList operations; // Handle any non-route items. if(!action->data().canConvert()) { bool ok = false; const int n = action->data().toInt(&ok); if(ok) { switch(n) { case _OPEN_MIDI_CONFIG_: MusEGlobal::muse->configMidiPorts(); break; #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ case _ALIASES_WIDGET_ACTION_: { // Check for custom widget action. RoutingMatrixWidgetAction* wa = qobject_cast(action); if(wa) { if(wa->array()->value(0)) MusEGlobal::config.preferredRouteNameOrAlias = MusEGlobal::RoutePreferFirstAlias; else if(wa->array()->value(1)) MusEGlobal::config.preferredRouteNameOrAlias = MusEGlobal::RoutePreferSecondAlias; else MusEGlobal::config.preferredRouteNameOrAlias = MusEGlobal::RoutePreferCanonicalName; MusEGlobal::song->update(SC_PORT_ALIAS_PREFERENCE); } } break; case _GROUPING_CHANNELS_WIDGET_ACTION_: { // Check for custom widget action. RoutingMatrixWidgetAction* wa = qobject_cast(action); if(wa) { if(wa->array()->value(0)) MusEGlobal::config.routerGroupingChannels = 1; else MusEGlobal::config.routerGroupingChannels = 2; MusEGlobal::song->update(SC_ROUTER_CHANNEL_GROUPING); } } break; #endif case _SHOW_CANONICAL_NAMES_: { MusEGlobal::config.preferredRouteNameOrAlias = MusEGlobal::RoutePreferCanonicalName; MusEGlobal::song->update(SC_PORT_ALIAS_PREFERENCE); } break; case _SHOW_FIRST_ALIASES_: { MusEGlobal::config.preferredRouteNameOrAlias = action->isChecked() ? MusEGlobal::RoutePreferFirstAlias : MusEGlobal::RoutePreferCanonicalName; MusEGlobal::song->update(SC_PORT_ALIAS_PREFERENCE); } break; case _SHOW_SECOND_ALIASES_: { MusEGlobal::config.preferredRouteNameOrAlias = action->isChecked() ? MusEGlobal::RoutePreferSecondAlias : MusEGlobal::RoutePreferCanonicalName; MusEGlobal::song->update(SC_PORT_ALIAS_PREFERENCE); } break; case _OPEN_ROUTING_DIALOG_: MusEGlobal::muse->startRouteDialog(); break; default: break; } } return; } DEBUG_PRST_ROUTES(stderr, "RoutePopupMenu::popupActivated: action data is a Route\n"); // MusECore::Route rem_route = action->data().value(); // Support grouping together of channels. int act_group_sz = 0; int act_idx = 0; int act_start = 0; // int act_count = 0; bool use_act_list = false; QList < QAction* > act_list; const QActionGroup* act_group = action->actionGroup(); if(act_group && !act_group->isExclusive()) { act_list = act_group->actions(); act_idx = act_list.indexOf(action); if(act_idx != -1) { use_act_list = true; act_group_sz = act_list.size(); // Attempt to optimize by only doing the actions whose channels changed. Flawed. Just do the whole list. // act_start = act_idx; // if((act_start + MusEGlobal::config.routerGroupingChannels) > act_group_sz) // act_start = act_group_sz - MusEGlobal::config.routerGroupingChannels; // if(act_start < 0 ) // act_start = 0; // act_count = MusEGlobal::config.routerGroupingChannels; // if((act_start + act_count) > act_group_sz) // act_count = act_group_sz - act_start; // FIXME TODO: If an external event causes a connection 'behind our back' while the menu is open, we need to update the menu by // detecting the song changed SC_ROUTE flag. Otherwise when the menu checks all the actions against current connections, it finds // a connection and tries to turn it off because we've not updated the menu and that channel is 'off' in the menu right now. } } while(1) { QAction* act = use_act_list ? act_list.at(act_start) : action; DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::popupActivated this:%p act:%p active action:%p act_group_sz:%d act_start:%d\n", // act_count:%d\n", this, act, activeAction(), act_group_sz, act_start); //, act_count); if(!act) break; MusECore::Route rem_route = act->data().value(); switch(_route.type) { case MusECore::Route::TRACK_ROUTE: trackPopupActivated(act, rem_route, operations); break; case MusECore::Route::JACK_ROUTE: break; case MusECore::Route::MIDI_DEVICE_ROUTE: jackRouteActivated(act, _route, rem_route, operations); break; case MusECore::Route::MIDI_PORT_ROUTE: break; } if(use_act_list) { ++act_start; // if(--act_count == 0) if(--act_group_sz == 0) break; } else break; } if(!operations.empty()) { DEBUG_PRST_ROUTES_2(stderr, "RoutePopupMenu::popupActivated: executing operations\n"); MusEGlobal::audio->msgExecutePendingOperations(operations, true); // MusEGlobal::song->update(SC_ROUTE); } } void RoutePopupMenu::prepare() { if(!_route.isValid()) return; connect(this, SIGNAL(triggered(QAction*)), SLOT(routePopupActivated(QAction*))); QAction* route_act = addAction(tr("Advanced Router...")); route_act->setIcon(*routerSVGIcon); route_act->setCheckable(false); route_act->setData(_OPEN_ROUTING_DIALOG_); switch(_route.type) { case MusECore::Route::TRACK_ROUTE: { MusECore::Track* const track = _route.track; if(track->isMidiTrack()) { QAction* act = nullptr; // Warn if no devices available. Add an item to open midi config. int pi = 0; for( ; pi < MusECore::MIDI_PORTS; ++pi) { MusECore::MidiDevice* md = MusEGlobal::midiPorts[pi].device(); //if(md && !md->isSynti() && (md->rwFlags() & 2)) //if(md && (md->rwFlags() & 2 || md->isSynti()) ) // p4.0.27 Reverted p4.0.35 if(md && (md->rwFlags() & (_isOutMenu ? 1 : 2))) // Allow synth as input. break; } if(pi == MusECore::MIDI_PORTS) { if(_isOutMenu) act = addAction(tr("Warning: No output devices!")); else act = addAction(tr("Warning: No input devices!")); act->setCheckable(false); act->setData(-1); } act = addAction(QIcon(*ankerSVGIcon), tr("Midi Ports/Soft Synths...")); act->setCheckable(false); act->setData(_OPEN_MIDI_CONFIG_); } } break; default: break; } addSeparator(); if(_isOutMenu) addAction(new MenuTitleItem(tr("Output Routes:"), this)); else addAction(new MenuTitleItem(tr("Input Routes:"), this)); //addSeparator(); switch(_route.type) { case MusECore::Route::TRACK_ROUTE: { MusECore::Track* const track = _route.track; if(track->isMidiTrack()) { QAction* act = nullptr; addMidiPorts(track, this, _isOutMenu, true, _isOutMenu); if(_isOutMenu) { #ifdef _USE_SIMPLIFIED_SOLO_CHAIN_ // Support Midi Track to Audio Input track soloing chain routes. // Support omni routes only, because if channels are supported, the graphical router becomes more complicated. const MusECore::InputList* const il = MusEGlobal::song->inputs(); if(!il->empty()) { addSeparator(); addAction(new MenuTitleItem(tr("Soloing Chain"), this)); RoutePopupMenu* subp = new RoutePopupMenu(_route, this, _isOutMenu, _broadcastChanges); subp->setTitle(tr("Audio Returns")); for(MusECore::ciAudioInput ai = il->begin(); ai != il->end(); ++ai) { // Add omni route: MusECore::Track* t = *ai; act = subp->addAction(t->displayName()); act->setCheckable(true); const MusECore::Route r(t, -1); act->setData(QVariant::fromValue(r)); if(track->outRoutes()->contains(r)) act->setChecked(true); } addMenu(subp); } #else // _USE_SIMPLIFIED_SOLO_CHAIN_ // Support Midi Port to Audio Input track routes. int port = ((MusECore::MidiTrack*)track)->outPort(); if(port >= 0 && port < MIDI_PORTS) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; // Do not list synth devices! Requiring valid device is desirable, // but could lead to 'hidden' routes unless we add more support // such as removing the existing routes when user changes flags. // So for now, just list all valid ports whether read or write. if(mp->device() && !mp->device()->isSynti()) { MusECore::RouteList* rl = mp->outRoutes(); //int chbits = 1 << ((MusECore::MidiTrack*)track)->outChannel(); //MusECore::MidiDevice* md = mp->device(); //if(!md) // continue; addSeparator(); addAction(new MenuTitleItem(tr("Soloing chain"), this)); PopupMenu* subp = new PopupMenu(this, true); subp->setTitle(tr("Audio returns")); addMenu(subp); MusECore::InputList* al = MusEGlobal::song->inputs(); #ifdef _USE_CUSTOM_WIDGET_ACTIONS_ for (MusECore::ciAudioInput ai = al->begin(); ai != al->end(); ++ai) { // Add omni route: MusECore::Track* t = *ai; act = subp->addAction(t->displayName()); act->setCheckable(true); const MusECore::Route r(t, -1); act->setData(QVariant::fromValue(r)); if(rl->exists(r)) act->setChecked(true); // Add channel routes: RoutePopupMenu* subp = new RoutePopupMenu(_route, this, _isOutMenu, _broadcastChanges); wa_subp->addAction(new MenuTitleItem(tr("Channels"), this)); act->setMenu(wa_subp); //RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(1, MusECore::MUSE_MIDI_CHANNELS, redLedIcon, darkRedLedIcon, this); RoutingMatrixWidgetAction* wa = new RoutingMatrixWidgetAction(1, MusECore::MUSE_MIDI_CHANNELS, 0, 0, this); wa->setData(QVariant::fromValue(r)); // Ignore the routing channel and channels - our action holds the channels. int chans = 0; // Is there already a route? for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case MusECore::Route::TRACK_ROUTE: if(ir->track == t) chans = ir->channel; // Grab the channels. break; case MusECore::Route::MIDI_PORT_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } if(chans != 0) break; } if(chans != 0 && chans != -1) { for(int col = 0; col < MusECore::MUSE_MIDI_CHANNELS; ++col) { if(chans & (1 << col)) wa->array()->setValue(0, col, true); } } // Must rebuild array after text changes. wa->updateChannelArray(); wa_subp->addAction(wa); } #else int chbits = 1 << ((MusECore::MidiTrack*)track)->outChannel(); for (MusECore::ciAudioInput i = al->begin(); i != al->end(); ++i) { MusECore::Track* t = *i; QString s(t->displayName()); act = subp->addAction(s); act->setCheckable(true); MusECore::Route r(t, chbits); act->setData(QVariant::fromValue(r)); for(MusECore::ciRoute ir = mprl->begin(); ir != mprl->end(); ++ir) { if(ir->type == MusECore::Route::TRACK_ROUTE && ir->track == t && (ir->channel & chbits)) { act->setChecked(true); break; } } } #endif // _USE_CUSTOM_WIDGET_ACTIONS_ } } #endif // _USE_SIMPLIFIED_SOLO_CHAIN_ } else { #if 0 // p4.0.17 List ports with no device and no in routes, in a separate popup. PopupMenu* morep = new PopupMenu(pup, true); morep->setTitle(tr("More...")); for(int i = 0; i < MIDI_PORTS; ++i) { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[i]; if(mp->device()) continue; PopupMenu* subp = new PopupMenu(morep, true); subp->setTitle(QString("%1:%2").arg(i).arg(tr(""))); // MusE-2: Check this - needed with QMenu? Help says no. No - verified, it actually causes double triggers! //connect(subp, SIGNAL(triggered(QAction*)), pup, SIGNAL(triggered(QAction*))); //connect(subp, SIGNAL(aboutToHide()), pup, SIGNAL(aboutToHide())); iRoute ir = rl->begin(); for( ; ir != rl->end(); ++ir) { if(ir->type == MusECore::Route::MIDI_PORT_ROUTE && ir->midiPort == i) break; } if(ir != rl->end()) continue; for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { act = subp->addAction(QString("Channel %1").arg(ch+1)); act->setCheckable(true); act->setData(gid); int chbit = 1 << ch; MusECore::Route srcRoute(i, chbit); // In accordance with new channel mask, use the bit position. gRoutingMenuMap.insert( pRouteMenuMap(gid, srcRoute) ); //if(chanmask & chbit) // Is the channel already set? Show item check mark. // act->setChecked(true); ++gid; } //gid = MIDI_PORTS * MusECore::MUSE_MIDI_CHANNELS + i; // Make sure each 'toggle' item gets a unique id. act = subp->addAction(QString("Toggle all")); //act->setCheckable(true); act->setData(gid); MusECore::Route togRoute(i, (1 << MusECore::MUSE_MIDI_CHANNELS) - 1); // Set all channel bits. gRoutingMenuMap.insert( pRouteMenuMap(gid, togRoute) ); ++gid; morep->addMenu(subp); } pup->addMenu(morep); #endif } return; } else { MusECore::AudioTrack* t = (MusECore::AudioTrack*)track; MusECore::RouteCapabilitiesStruct rcaps = t->routeCapabilities(); if(_isOutMenu) { const int t_ochs = rcaps._trackChannels._outChannels; int gid = 0; switch(track->type()) { case MusECore::Track::AUDIO_OUTPUT: { addGroupingChannelsAction(this); addJackPorts(_route, this); if(!MusEGlobal::song->inputs()->empty()) { // // Display using separate menu for audio inputs: // addSeparator(); addAction(new MenuTitleItem(tr("Soloing Chain"), this)); RoutePopupMenu* subp = new RoutePopupMenu(_route, this, _isOutMenu, _broadcastChanges); subp->setTitle(tr("Audio Returns")); addMenu(subp); gid = addInPorts(t, subp, gid, -1, -1, true); // // Display all in the same menu: // //addSeparator(); //MenuTitleItem* title = new MenuTitleItem(tr("Audio returns"), this); //addAction(title); //gid = addInPorts(t, this, gid, -1, -1, true); } } break; case MusECore::Track::AUDIO_INPUT: case MusECore::Track::WAVE: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_AUX: case MusECore::Track::AUDIO_SOFTSYNTH: if(t_ochs > 0) { addGroupingChannelsAction(this); addAction(new RoutingMatrixHeaderWidgetAction(tr("Omni"), tr("Tracks"), QString(), this)); gid = addWavePorts( t, this, gid, -1, -1, true); gid = addOutPorts( t, this, gid, -1, -1, true); gid = addGroupPorts( t, this, gid, -1, -1, true); gid = addSynthPorts( t, this, gid, -1, -1, true); } break; default: clear(); return; break; } #ifndef _USE_CUSTOM_WIDGET_ACTIONS_ switch(_track->type()) { case MusECore::Track::AUDIO_INPUT: case MusECore::Track::WAVE: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_AUX: case MusECore::Track::AUDIO_SOFTSYNTH: if(t_ochs > 0) { addSeparator(); addAction(new MenuTitleItem(tr("Channels"), this)); for(int i = 0; i < t_ochs; ++i) { PopupMenu* subp = new PopupMenu(this, true); subp->setTitle(QString::number(i + 1)); subp->addAction(new MenuTitleItem(tr("Destinations:"), this)); addMenu(subp); gid = addWavePorts( t, subp, gid, i, 1, true); gid = addOutPorts( t, subp, gid, i, 1, true); gid = addGroupPorts(t, subp, gid, i, 1, true); gid = addSynthPorts(t, subp, gid, i, 1, true); } } break; default: break; } #endif } else { if(track->type() == MusECore::Track::AUDIO_AUX) return; const int t_ichs = rcaps._trackChannels._inChannels; int gid = 0; switch(track->type()) { case MusECore::Track::AUDIO_INPUT: { addGroupingChannelsAction(this); addJackPorts(_route, this); if(!MusEGlobal::song->outputs()->empty() || !MusEGlobal::song->midis()->empty()) { RoutePopupMenu* subp; // // Display using separate menus for midi ports and audio outputs: // addSeparator(); addAction(new MenuTitleItem(tr("Soloing Chain"), this)); if(!MusEGlobal::song->outputs()->empty()) { subp = new RoutePopupMenu(_route, this, _isOutMenu, _broadcastChanges); subp->setTitle(tr("Audio Sends")); addMenu(subp); gid = addOutPorts(t, subp, gid, -1, -1, false); } if(!MusEGlobal::song->midis()->empty()) { subp = new RoutePopupMenu(_route, this, true, _broadcastChanges); subp->setTitle(tr("Midi Sends")); addMenu(subp); addMidiTracks(t, subp, false); // // Display all in the same menu: // //addAction(new MenuTitleItem(tr("Audio sends"), this)); //gid = addOutPorts(t, this, gid, -1, -1, false); //addSeparator(); //addAction(new MenuTitleItem(tr("Midi sends"), this)); //addMidiPorts(t, this, gid, false); } } } break; case MusECore::Track::AUDIO_OUTPUT: case MusECore::Track::WAVE: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_SOFTSYNTH: if(t_ichs > 0) { addGroupingChannelsAction(this); addAction(new RoutingMatrixHeaderWidgetAction(tr("Omni"), tr("Tracks"), QString(), this)); gid = addWavePorts( t, this, gid, -1, -1, false); gid = addInPorts( t, this, gid, -1, -1, false); gid = addGroupPorts(t, this, gid, -1, -1, false); gid = addAuxPorts( t, this, gid, -1, -1, false); gid = addSynthPorts(t, this, gid, -1, -1, false); } break; default: clear(); return; break; } #ifndef _USE_CUSTOM_WIDGET_ACTIONS_ switch(_track->type()) { case MusECore::Track::AUDIO_OUTPUT: case MusECore::Track::WAVE: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_SOFTSYNTH: if(t_ichs > 0) { addSeparator(); addAction(new MenuTitleItem(tr("Channels"), this)); for(int i = 0; i < t_ichs; ++i) { PopupMenu* subp = new PopupMenu(this, true); subp->setTitle(QString::number(i + 1)); subp->addAction(new MenuTitleItem(tr("Sources:"), this)); addMenu(subp); gid = addWavePorts( t, subp, gid, i, 1, false); gid = addInPorts( t, subp, gid, i, 1, false); gid = addGroupPorts(t, subp, gid, i, 1, false); gid = addAuxPorts( t, subp, gid, i, 1, false); gid = addSynthPorts(t, subp, gid, i, 1, false); } } break; default: break; } #endif } } } break; case MusECore::Route::MIDI_DEVICE_ROUTE: addJackPorts(_route, this); break; case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_PORT_ROUTE: break; } } void RoutePopupMenu::exec(const MusECore::Route& route, bool isOutput) { if(route.isValid()) { _route = route; _isOutMenu = isOutput; } prepare(); PopupMenu::exec(); } void RoutePopupMenu::exec(const QPoint& p, const MusECore::Route& route, bool isOutput) { if(route.isValid()) { _route = route; _isOutMenu = isOutput; } prepare(); PopupMenu::exec(p); } void RoutePopupMenu::popup(const QPoint& p, const MusECore::Route& route, bool isOutput) { if(route.isValid()) { _route = route; _isOutMenu = isOutput; } prepare(); PopupMenu::popup(p); } } // namespace MusEGui muse-4.0.0/src/muse/components/routepopup.h000066400000000000000000000131431404105311000207730ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // RoutePopupMenu.h // (C) Copyright 2011-2015 Tim E. Real (terminator356 A T sourceforge D O T net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __ROUTEPOPUPMENU_H__ #define __ROUTEPOPUPMENU_H__ #include #include "type_defs.h" #include "route.h" #include "custom_widget_actions.h" #include "popupmenu.h" // Forward declarations: class QWidget; class QAction; class QPoint; class QResizeEvent; namespace MusECore { class AudioTrack; class Track; class PendingOperationList; } namespace MusEGui { class RoutePopupMenu : public PopupMenu { Q_OBJECT // Route describing the caller (a track, device, jack port etc). MusECore::Route _route; // Whether the route popup was shown by clicking the output routes button, or input routes button. bool _isOutMenu; // For keyboard navigation. RoutePopupHit _lastHoveredHit; // To inform a pending hover slot whether the hover was from mouse or not. bool _hoverIsFromMouse; // Whether to propagate changes to other selected tracks. // This includes operating a control or using the universal up/down volume/ pan keys etc. bool _broadcastChanges; void init(); // Prepares (fills) the popup before display. void prepare(); // Recursive. Returns true if any text was changed. bool updateItemTexts(PopupMenu* menu = 0); void addGroupingChannelsAction(PopupMenu* lb); int addMenuItem(MusECore::AudioTrack* track, MusECore::Track* route_track, PopupMenu* lb, int id, int channel, int channels, bool isOutput); int addAuxPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); int addInPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); int addOutPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); int addGroupPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); int addWavePorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); void addMidiPorts(MusECore::Track* t, PopupMenu* pup, bool isOutput, bool show_synths, bool want_readable); void addMidiTracks(MusECore::Track* t, PopupMenu* pup, bool isOutput); int addSynthPorts(MusECore::AudioTrack* t, PopupMenu* lb, int id, int channel, int channels, bool isOutput); void addJackPorts(const MusECore::Route& route, PopupMenu* lb); void jackRouteActivated(QAction* action, const MusECore::Route& route, const MusECore::Route& rem_route, MusECore::PendingOperationList& operations); void trackRouteActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations); void audioTrackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations); void midiTrackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations); void trackPopupActivated(QAction* action, MusECore::Route& rem_route, MusECore::PendingOperationList& operations); private slots: void routePopupHovered(QAction*); void routePopupActivated(QAction*); void songChanged(MusECore::SongChangedStruct_t); protected: virtual bool event(QEvent*); virtual void resizeEvent(QResizeEvent* e); virtual void mouseReleaseEvent(QMouseEvent*); virtual void mousePressEvent(QMouseEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual void keyPressEvent(QKeyEvent*); // Updates item texts and the 'preferred alias action'. Returns true if any action was changed. virtual bool preferredPortAliasChanged(); // Updates items and the 'channel grouping'. Returns true if any action was changed. virtual bool routerChannelGroupingChanged(); // For auto-breakup of a too-wide menu. Virtual. virtual PopupMenu* cloneMenu(const QString& title, QWidget* parent = 0, bool /*stayOpen*/ = false, bool showTooltips = false); public: RoutePopupMenu(QWidget* parent = 0, bool isOutput = false, bool broadcastChanges = false); RoutePopupMenu(const MusECore::Route& route, QWidget* parent = 0, bool isOutput = false, bool broadcastChanges = false); RoutePopupMenu(const MusECore::Route& route, const QString& title, QWidget* parent = 0, bool isOutput = false, bool broadcastChanges = false); bool broadcastChanges() const { return _broadcastChanges; } void setBroadcastChanges(bool v) { _broadcastChanges = v; } void updateRouteMenus(); void exec(const MusECore::Route& route, bool isOutput = false); void exec(const QPoint& p, const MusECore::Route& route, bool isOutput = false); void popup(const QPoint& p, const MusECore::Route& route, bool isOutput = false); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/savenewrevisiondialog.cpp000066400000000000000000000041521404105311000235130ustar00rootroot00000000000000#include "savenewrevisiondialog.h" #include "ui_savenewrevisiondialog.h" #include "helper.h" namespace MusEGui { SaveNewRevisionDialog::SaveNewRevisionDialog(QWidget *parent, QFileInfo projectFileInfo) : QDialog(parent), ui(new Ui::SaveNewRevisionDialog) { ui->setupUi(this); _projectFileInfo = projectFileInfo; } SaveNewRevisionDialog::~SaveNewRevisionDialog() { delete ui; } QString SaveNewRevisionDialog::buildFilePath(QString newBaseName) { QString path = _projectFileInfo.path(); QString suffix = _projectFileInfo.completeSuffix(); return path + "/" + newBaseName + "." + suffix; } void SaveNewRevisionDialog::accept() { QString newFilePath = buildFilePath(ui->projectNameEdit->text()); QFileInfo newFileInfo; newFileInfo.setFile( newFilePath ); if (newFileInfo.exists()) { QString fileName = newFileInfo.filePath(); ui->errorInfo->setText(QString("%1 already exists!\n").arg(fileName)); return; } QDialog::accept(); } void SaveNewRevisionDialog::reject() { QDialog::reject(); } QString SaveNewRevisionDialog::getNewRevision() { QString baseName = _projectFileInfo.baseName(); QRegExp xRegExp("_\\d\\d\\d$"); int index = xRegExp.indexIn(baseName); if(index > 0) { QString newNumberString = baseName.mid(index+1, baseName.size() - index); int newNumber = newNumberString.toInt(); newNumber++; QString newBaseName = baseName.mid(0, index) + QString("_%1").arg(newNumber,3, 10, QChar('0')); return buildFilePath(newBaseName); } return ""; } QString SaveNewRevisionDialog::getNewRevisionWithDialog() { ui->oldPath->setText(QString("%1\n").arg(_projectFileInfo.filePath())); ui->errorInfo->clear(); QString projectFileName = MusEGui::projectTitleFromFilename(_projectFileInfo.baseName()) + "_001"; ui->projectNameEdit->setText(projectFileName); ui->projectNameEdit->setFocus(); show(); ui->projectNameEdit->setCursorPosition(ui->projectNameEdit->text().size()); auto code = exec(); if (code == QDialog::Rejected) return ""; QString newFilePath = buildFilePath(ui->projectNameEdit->text()); return newFilePath; } } //namespace MusEGui muse-4.0.0/src/muse/components/savenewrevisiondialog.h000066400000000000000000000012261404105311000231570ustar00rootroot00000000000000#ifndef SAVENEWREVISIONDIALOG_H #define SAVENEWREVISIONDIALOG_H #include #include namespace Ui { class SaveNewRevisionDialog; } namespace MusEGui { class SaveNewRevisionDialog : public QDialog { Q_OBJECT public: explicit SaveNewRevisionDialog(QWidget *parent, QFileInfo projectFileInfo); ~SaveNewRevisionDialog(); QString getNewRevision(); QString getNewRevisionWithDialog(); private slots: void accept() override; void reject() override; private: QString buildFilePath(QString newName); Ui::SaveNewRevisionDialog *ui; QFileInfo _projectFileInfo; }; } // namespace MusEGui #endif // SAVENEWREVISIONDIALOG_H muse-4.0.0/src/muse/components/savenewrevisiondialog.ui000066400000000000000000000044521404105311000233510ustar00rootroot00000000000000 SaveNewRevisionDialog 0 0 487 161 Dialog Save new revision of song. Suggested number appended to name: old path <html><head/><body><p>Suggested index appended on the format: _000. </p><p>Do not change unless you know what you are doing.</p></body></html> error information Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok buttonBox accepted() SaveNewRevisionDialog accept() 248 254 157 274 buttonBox rejected() SaveNewRevisionDialog reject() 316 260 286 274 muse-4.0.0/src/muse/components/scroll_area.cpp000066400000000000000000000102161404105311000213700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // scroll_area.cpp // (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "scroll_area.h" #include "compact_slider.h" namespace MusEGui { //--------------------------------------------------------- // CompactScrollArea //--------------------------------------------------------- CompactScrollArea::CompactScrollArea(QWidget *parent) : QScrollArea(parent) { } QSize CompactScrollArea::minimumSizeHint() const { if(widget()) return widget()->sizeHint(); return QSize(16, 16); } //--------------------------------------------------------- // CompactControllerRack //--------------------------------------------------------- CompactControllerRack::CompactControllerRack(QWidget *parent, int minItems) : QScrollArea(parent), _minItems(minItems) { _xItemMargin = 0; _yItemMargin = 0; _minItemSize = defaultItemSizeHint(); _minSize = QSize(_minItemSize.width(), _minItems * _minItemSize.height()); // + 1; // TODO Instead of just +1, may need margins etc? } QSize CompactControllerRack::minimumSizeHint() const { //return _minSize; const QSize sz = CompactSlider::getMinimumSizeHint(fontMetrics(), Qt::Horizontal, CompactSlider::None, _xItemMargin, _yItemMargin); return QSize(sz.width() + 2 * frameWidth(), _minItems * sz.height() + 2 * frameWidth()); } QSize CompactControllerRack::defaultItemSizeHint() { // // HACK: Get some idea of the height of each item - even with an empty scroll area. // CompactSlider* dummy_slider = new CompactSlider(this, 0, Qt::Horizontal, CompactSlider::None, "WWW"); // dummy_slider->setValueDecimals(0); // dummy_slider->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum)); // dummy_slider->setEnabled(true); // // const QSize sz = dummy_slider->sizeHint(); // // delete dummy_slider; // // return sz; // HACK: Get some idea of the height of each item - even with an empty scroll area. return CompactSlider::getMinimumSizeHint(fontMetrics(), Qt::Horizontal, CompactSlider::None, _xItemMargin, _yItemMargin); } void CompactControllerRack::updateDefaultItemSizeHint() { _minItemSize = defaultItemSizeHint(); update(); } void CompactControllerRack::updateMinimumSize() { _minSize = QSize(_minItemSize.width(), _minItems * _minItemSize.height()); // + 1; // TODO Instead of just +1, may need margins etc? update(); } void CompactControllerRack::setMinItems(int n) { _minItems = n; _minItemSize = defaultItemSizeHint(); _minSize = QSize(_minItemSize.width(), _minItems * _minItemSize.height()); // + 1; // TODO Instead of just +1, may need margins etc? update(); } void CompactControllerRack::setItemMargins(int hor, int vert) { _xItemMargin = hor; _yItemMargin = vert; _minItemSize = defaultItemSizeHint(); _minSize = QSize(_minItemSize.width(), _minItems * _minItemSize.height()); // + 1; // TODO Instead of just +1, may need margins etc? //resize(this->size()); // FIXME Call something else like updateGeometry etc. updateGeometry(); } } // namespace MusEGui muse-4.0.0/src/muse/components/scroll_area.h000066400000000000000000000041401404105311000210340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // scroll_area.h // (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SCROLL_AREA_H__ #define __SCROLL_AREA_H__ #include class QSize; namespace MusEGui { //--------------------------------------------------------- // CompactScrollArea //--------------------------------------------------------- class CompactScrollArea : public QScrollArea { Q_OBJECT public: CompactScrollArea(QWidget* parent); virtual QSize minimumSizeHint() const; }; //--------------------------------------------------------- // CompactControllerRack //--------------------------------------------------------- class CompactControllerRack : public QScrollArea { Q_OBJECT private: int _minItems; QSize _minItemSize; QSize _minSize; int _xItemMargin; int _yItemMargin; public: CompactControllerRack(QWidget* parent, int minItems = 0); virtual QSize minimumSizeHint() const; int minItems() const { return _minItems; } void setMinItems(int n); QSize defaultItemSizeHint(); void updateDefaultItemSizeHint(); void updateMinimumSize(); void setItemMargins(int x, int y); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/scrollscale.cpp000066400000000000000000000407021404105311000214130ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: scrollscale.cpp,v 1.2.2.2 2009/11/04 17:43:25 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include #include #include #include #include #include #include #include #include "scrollscale.h" #include "icons.h" namespace MusEGui { //--------------------------------------------------------- // stepScale // increase/decrease scale by single step //--------------------------------------------------------- void ScrollScale::stepScale ( bool up ) { setMag(scale2mag(up ? scaleVal + 1 : scaleVal - 1)); } //--------------------------------------------------------- // setScale // "val" - slider value in range 0-convertQuickZoomLevelToMag(zoomLevels-1) //--------------------------------------------------------- void ScrollScale::setScale ( int val, int pos_offset ) { int off = offset(); int old_scale_val = scaleVal; scaleVal = mag2scale(val); //fprintf(stderr, "scaleMin %d scaleMax %d val=%d emit scaleVal=%d\n", scaleMin, scaleMax, val, scaleVal); emit scaleChanged ( scaleVal ); if ( !noScale ) setRange ( minVal, maxVal ); int i = ( scroll->orientation() == Qt::Horizontal ) ? width() : height(); int pos, pmax; if ( scaleVal < 1 ) { pos = ( off-scaleVal/2 ) / ( -scaleVal ); pmax = ( maxVal-scaleVal-1 ) / ( -scaleVal ) - i; } else { pos = off * scaleVal; pmax = maxVal * scaleVal - i; } // Zoom at cursor support... if(pos_offset != 0) { double oscale = old_scale_val; double nscale = scaleVal; if(old_scale_val < 1) oscale = 1.0 / -old_scale_val; if(scaleVal < 1) nscale = 1.0 / -scaleVal; double scale_fact = nscale / oscale; int pos_diff = (int)((double)pos_offset * scale_fact - (double)pos_offset + 0.5); // 0.5 for round-off pos += pos_diff; } if(pos > pmax) pos = pmax; setPos(pos); } //--------------------------------------------------------- // setMag //--------------------------------------------------------- void ScrollScale::setMag ( int cs, int pos_offset ) { scale->blockSignals(true); scale->setValue ( cs ); scale->blockSignals(false); setScale ( cs, pos_offset ); } //--------------------------------------------------------- // setRange // min,max ticks //--------------------------------------------------------- void ScrollScale::setRange ( int min, int max ) { // if ((min != minVal) && (max != maxVal)) // return; minVal = min; maxVal = max; int i = ( scroll->orientation() == Qt::Horizontal ) ? width() : height(); if ( !noScale ) { if ( scaleVal < 1 ) { min = minVal / ( -scaleVal ); max = ( maxVal-scaleVal-1 ) / ( -scaleVal ) - i; } else { min = minVal * scaleVal; max = maxVal * scaleVal - i; } } else max -= i; if ( max < 0 ) max = 0; if ( min < 0 ) min = 0; if ( min > max ) max = min; scroll->setRange ( min, max ); // qt doesn't check this... if ( scroll->value() < min ) scroll->setValue ( min ); if ( scroll->value() > max ) scroll->setValue ( max ); scroll->setSingleStep(20); scroll->setPageStep(i); } //--------------------------------------------------------- // setPos // pos in pixel //--------------------------------------------------------- void ScrollScale::setPos ( unsigned pos ) { scroll->setValue ( pos ); } //--------------------------------------------------------- // setPosNoLimit // pos in pixel //--------------------------------------------------------- void ScrollScale::setPosNoLimit ( unsigned pos ) { //printf ( "ScrollScale::setPosNoLimit pos:%d scaleVal:%d offset ticks:%d\n", pos, scaleVal, pos2offset ( pos ) ); if((int)pos > scroll->maximum()) scroll->setMaximum(pos); scroll->setValue(pos); } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void ScrollScale::resizeEvent ( QResizeEvent* ev) { QWidget::resizeEvent(ev); emit scaleChanged ( scaleVal ); if ( !noScale ) setRange ( minVal, maxVal ); } //--------------------------------------------------------- // ScrollScale //--------------------------------------------------------- ScrollScale::ScrollScale ( int s1, int s2, int cs, int max_, Qt::Orientation o, QWidget* parent, int min_, bool inv, double bas ) : QWidget ( parent ) { noScale = false; _page = 0; _pages = 1; pageButtons = false; showMagFlag = true; scaleMin = s1; scaleMax = s2; minVal = min_; maxVal = max_; up = nullptr; down = nullptr; logbase = bas; invers = inv; scaleVal = 0; scaleVal = cs; const int cur = scale2mag(cs); //fprintf(stderr, "ScrollScale: cs:%d cur:%f\n", cs, cur); scale = new QSlider (o); scale->setObjectName("ScrollScaleZoomSlider"); // Added by Tim. For some reason focus was on. // It messes up tabbing, and really should have a shortcut instead. scale->setFocusPolicy(Qt::NoFocus); scale->setMinimum(0); scale->setMaximum(convertQuickZoomLevelToMag(zoomLevels-1)); scale->setPageStep(1); scale->setValue(cur); scroll = new QScrollBar ( o ); //scroll->setFocusPolicy(Qt::NoFocus); // Tim. emit scaleChanged ( scaleVal ); if ( !noScale ) setRange ( minVal, maxVal ); if ( o == Qt::Horizontal ) { box = new QBoxLayout ( QBoxLayout::LeftToRight); scale->setMaximumWidth ( 70 ); scroll->setMinimumWidth ( 50 ); } else { box = new QBoxLayout ( QBoxLayout::TopToBottom); scroll->setMinimumHeight ( 50 ); scale->setMaximumHeight ( 70 ); } box->setContentsMargins(0, 0, 0, 0); box->setSpacing(0); box->addWidget ( scroll, 10 ); int w = style()->pixelMetric(QStyle::PM_ScrollBarExtent);; scaleUp = new QToolButton; scaleUp->setObjectName("ScrollScaleZoomButton"); scaleUp->setFocusPolicy(Qt::NoFocus); scaleUp->setMaximumSize(w, w); scaleUp->setIcon (*plusSVGIcon); scaleUp->setToolTip(tr("Increase zoom level")); connect(scaleUp, &QToolButton::clicked, this, [this](){ stepScale(true); }); scaleDown = new QToolButton; scaleDown->setFocusPolicy(Qt::NoFocus); scaleDown->setObjectName("ScrollScaleZoomButton"); scaleDown->setMaximumSize(w, w); scaleDown->setIcon (*minusSVGIcon); scaleDown->setToolTip(tr("Decrease zoom level")); connect(scaleDown, &QToolButton::clicked, this, [this](){ stepScale(false); }); box->addSpacing(4); box->addWidget(scaleDown); box->addWidget ( scale, 5 ); box->addWidget(scaleUp); setLayout(box); connect ( scale, SIGNAL ( valueChanged ( int ) ), SLOT ( setScale ( int ) ) ); connect ( scroll, SIGNAL ( valueChanged ( int ) ), SIGNAL ( scrollChanged ( int ) ) ); } //--------------------------------------------------------- // setPageButtons //--------------------------------------------------------- void ScrollScale::setPageButtons ( bool flag ) { if ( flag == pageButtons ) return; if ( flag ) { if ( up == nullptr ) { up = new QToolButton; up->setIcon ( QIcon(":/svg/up_vee.svg") ); down = new QToolButton; down->setIcon ( QIcon(":/svg/down_vee.svg") ); pageNo = new QLabel; QString s; s.setNum ( _page+1 ); pageNo->setText ( s ); down->setToolTip(tr ( "next page" ) ); up->setToolTip(tr ( "previous page" ) ); pageNo->setToolTip(tr ( "current page number" ) ); box->insertWidget ( 1, up ); box->insertWidget ( 2, down ); box->insertSpacing ( 3, 5 ); box->insertWidget ( 4, pageNo ); box->insertSpacing ( 5, 5 ); connect ( up, SIGNAL ( clicked() ), SLOT ( pageUp() ) ); connect ( down, SIGNAL ( clicked() ), SLOT ( pageDown() ) ); } up->show(); down->show(); pageNo->show(); if ( _page == ( _pages-1 ) ) down->setEnabled ( false ); if ( _page == 0 ) up->setEnabled ( false ); } else { up->hide(); down->hide(); } pageButtons = flag; } //--------------------------------------------------------- // showMag //--------------------------------------------------------- void ScrollScale::showMag ( bool flag ) { showMagFlag = flag; if ( flag ) scale->show(); else scale->hide(); box->activate(); } //--------------------------------------------------------- // offset //--------------------------------------------------------- int ScrollScale::offset() const { return pos2offset ( scroll->value() ); } //--------------------------------------------------------- // pos2offset //--------------------------------------------------------- int ScrollScale::pos2offset ( int pos ) const { if ( scaleVal < 1 ) return pos * ( -scaleVal ) + scaleVal/2; else return pos / scaleVal; } //--------------------------------------------------------- // offset2pos //--------------------------------------------------------- int ScrollScale::offset2pos ( int off ) const { if ( scaleVal < 1 ) return ( off-scaleVal/2 ) / ( -scaleVal ); else return off * scaleVal; } //--------------------------------------------------------- // mag2scale //--------------------------------------------------------- int ScrollScale::mag2scale(int mag) const { int mag_max = convertQuickZoomLevelToMag(zoomLevels-1); if(mag < 0) mag = 0; else if(mag > mag_max) mag = mag_max; if ( invers ) mag = mag_max - mag; double min, max; if ( scaleMin < 0 ) min = 1.0/ ( -scaleMin ); else min = double ( scaleMin ); if ( scaleMax < 0 ) max = 1.0/ ( -scaleMax ); else max = double ( scaleMax ); double diff = max-min; double fkt = double ( mag ) /double(mag_max); double v = ( pow ( logbase, fkt )-1 ) / ( logbase-1 ); double scale; if ( invers ) scale = max - v * diff; else scale = min + v * diff; #if 0 if ( scaleMax > scaleMin ) { if ( scale < scaleMin ) scale = scaleMin; else if ( scale > scaleMax ) scale = scaleMax; } else { if ( scale < scaleMax ) scale = scaleMax; else if ( scale > scaleMin ) scale = scaleMin; } #endif int scale_val; if ( scale < 1.0 ) // Floor, rather than simply casting 1.0/scale as a negative int, // was required here due to the unique nature of our negative numbers, // so that the reciprocal scale2mag() matches this mag2scale(). // Tested OK so far, loading and saving, with newly opened windows as well. Tim. scale_val = floor ( 1.0 / ( -scale ) ); else scale_val = int ( scale ); if ( scale_val == -1 ) // nur so scale_val = 1; return scale_val; } //--------------------------------------------------------- // scale2mag //--------------------------------------------------------- int ScrollScale::scale2mag(int scale) const { double min, max; if ( scaleMin < 0 ) min = 1.0/ ( -scaleMin ); else min = double ( scaleMin ); if ( scaleMax < 0 ) max = 1.0/ ( -scaleMax ); else max = double ( scaleMax ); double cmag = ( scale < 0 ) ? ( 1.0/ ( -scale ) ) : double ( scale ); double diff = max-min; const int mag_max = convertQuickZoomLevelToMag(zoomLevels-1); // Do a log in the given logbase (see Change of Base Formula). // Choice of 'log()' base (10, 2 natural etc.) is not supposed to matter. const double fkt = log10( (cmag - min) * (logbase - 1) / diff + 1 ) / log10(logbase); // const double fkt = log( (cmag - min) * (logbase - 1) / diff + 1 ) / log(logbase); // Round up so that the reciprocal function scale2mag() matches. const double cur = ceil( fkt * mag_max ); return cur; } //--------------------------------------------------------- // setOffset // val in tick //--------------------------------------------------------- void ScrollScale::setOffset ( int val ) { int i = ( scroll->orientation() == Qt::Horizontal ) ? width() : height(); int pos, max; if ( scaleVal < 1 ) { pos = ( val-scaleVal/2 ) / ( -scaleVal ); max = ( maxVal-scaleVal-1 ) / ( -scaleVal ) - i; } else { pos = val * scaleVal; max = maxVal * scaleVal - i; } if ( pos > max ) { int min; if ( scaleVal < 1 ) { maxVal = ( pos + width() ) * ( -scaleVal ); min = ( minVal-scaleVal/2 ) / ( -scaleVal ); max = ( maxVal-scaleVal/2 ) / ( -scaleVal ) - i; } else { maxVal = ( pos + width() + scaleVal/2 ) /scaleVal; min = minVal * scaleVal; max = maxVal * scaleVal - i; } if ( max < 0 ) max = 0; if ( min < 0 ) min = 0; if ( min > max ) max = min; scroll->setRange ( min, max ); } setPos ( pos ); } //--------------------------------------------------------- // pageUp // goto previous page //--------------------------------------------------------- void ScrollScale::pageUp() { if ( _page ) { --_page; emit newPage ( _page ); QString s; s.setNum ( _page+1 ); pageNo->setText ( s ); if ( _page == 0 ) up->setEnabled ( false ); if ( _page == ( _pages-2 ) ) down->setEnabled ( true ); } } //--------------------------------------------------------- // pageDown // goto next page //--------------------------------------------------------- void ScrollScale::pageDown() { if ( _page + 1 < _pages ) { ++_page; emit newPage ( _page ); QString s; s.setNum ( _page+1 ); pageNo->setText ( s ); if ( _page == ( _pages-1 ) ) down->setEnabled ( false ); if ( _page == 1 ) up->setEnabled ( true ); } } //--------------------------------------------------------- // setPages //--------------------------------------------------------- void ScrollScale::setPages ( int n ) { _pages = n; if ( _page >= _pages ) { _page = _pages-1; emit newPage ( _page ); QString s; s.setNum ( _page+1 ); pageNo->setText ( s ); } up->setEnabled ( _page ); down->setEnabled ( _page < ( _pages-1 ) ); } int ScrollScale::pos() const { return scroll->value(); } int ScrollScale::mag() const { return scale->value(); } /** * Hardcoded hackish function that corresponds to the values used for the scrollscales in PianoRoll and DrumEditor * since I couldn't easily create any inverse function from the [0,1024]-range to detect where a zoom actually occurs * (mg) */ int ScrollScale::getQuickZoomLevel(int mag) { if (mag == 0) return 0; for (int i=0; i val1 && mag <= val2) return i + 1; } return -1; } /** * Function returning the boundary values for a zoom change, hardcoded corresponding to the values used in PianoRoll * and DrumEditor */ int ScrollScale::convertQuickZoomLevelToMag(int zoomlevel) { int vals[] = { 0, 1, 15, 30, 46, 62, 80, 99, 119, 140, 163, 187, 214, 242, 274, 308, 346, 388, 436, 491, 555, 631, 726, 849, 1024, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, 2100, 2200, 2300, 2400, 2500 }; return vals[zoomlevel]; } int ScrollScale::scaleMinimum() const { return scaleMin; } int ScrollScale::scaleMaximum() const { return scaleMax; } void ScrollScale::setScaleMinimum(int min) { if(scaleMin == min) return; scaleMin = min; if(scaleVal < scaleMin) { scaleVal = scaleMin; emit scaleChanged ( scaleVal ); if ( !noScale ) setRange ( minVal, maxVal ); } repaint(); } void ScrollScale::setScaleMaximum(int max) { if(scaleMax == max) return; scaleMax = max; if(scaleVal > scaleMax) { scaleVal = scaleMax; emit scaleChanged ( scaleVal ); if ( !noScale ) setRange ( minVal, maxVal ); } repaint(); } void ScrollScale::setScaleRange(int min, int max) { if(scaleMin == min && scaleMax == max) return; scaleMin = min; scaleMax = max; if(scaleVal < scaleMin || scaleVal > scaleMax) { if(scaleVal < scaleMin) scaleVal = scaleMin; else if(scaleVal > scaleMax) scaleVal = scaleMax; emit scaleChanged ( scaleVal ); if ( !noScale ) setRange ( minVal, maxVal ); } repaint(); } } // namespace MusEGui muse-4.0.0/src/muse/components/scrollscale.h000066400000000000000000000066451404105311000210700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: scrollscale.h,v 1.2.2.3 2009/11/04 17:43:26 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SCROLLSCALE_H__ #define __SCROLLSCALE_H__ #include class QBoxLayout; class QLabel; class QResizeEvent; class QScrollBar; class QToolButton; namespace MusEGui { //--------------------------------------------------------- // ScrollScale //--------------------------------------------------------- class ScrollScale : public QWidget { Q_OBJECT QSlider* scale; QScrollBar* scroll; int minVal, maxVal; int scaleVal, scaleMin, scaleMax; bool showMagFlag; QBoxLayout* box; bool noScale; bool pageButtons; int _page; int _pages; QToolButton* up; QToolButton* down; QLabel* pageNo; bool invers; double logbase; QToolButton *scaleUp, *scaleDown; virtual void resizeEvent(QResizeEvent*); private slots: void pageUp(); void pageDown(); public slots: void setPos(unsigned); void setPosNoLimit(unsigned); void setMag(int val, int pos_offset = 0); void setOffset(int val); void setScale(int val, int pos_offset = 0); void stepScale(bool up); signals: void scaleChanged(int); void scrollChanged(int); void newPage(int); public: ScrollScale(int, int, int, int max, Qt::Orientation, QWidget*, int min = 0, bool i=false, double vv = 10.0); int xmag() const { return scale->value(); } void setXmag(int val) { scale->setValue(val); } void setRange(int, int); void showMag(bool); void setNoScale(bool flag) { noScale = flag; } void setPageButtons(bool flag); void setPage(int n) { _page = n; } int page() const { return _page; } int pages() const { return _pages; } void setPages(int n); int pos() const; int mag() const; int getScaleValue() const { return scaleVal; } void range(int* b, int* e) const { *b = minVal; *e = maxVal; } int scaleMinimum() const; int scaleMaximum() const; void setScaleMinimum(int min); void setScaleMaximum(int max); void setScaleRange(int min, int max); int offset() const; int pos2offset(int pos) const; int offset2pos(int off) const; int mag2scale(int mag) const; int scale2mag(int scale) const; static int getQuickZoomLevel(int mag); static int convertQuickZoomLevelToMag(int zoomlvl); const static int zoomLevels = 38; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/shortcutcapturedialog.cpp000066400000000000000000000115261404105311000235260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/shortcutcapturedialog.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // Author: Mathias Lundgren , (C) 2003 // // Copyright: Mathias Lundgren (lunar_shuttle@users.sourceforge.net) (C) 2003 // // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // // C++ Implementation: shortcutcapturedialog // // Description: // Dialog window for capturing keyboard shortcuts #include "shortcutcapturedialog.h" #include "shortcuts.h" #include #include #include #include #include #include namespace MusEGui { ShortcutCaptureDialog::ShortcutCaptureDialog(QWidget* parent, int index) : QDialog(parent) { setupUi(this); QKeySequence q = QKeySequence(shortcuts[index].key); oshrtLabel->setText(q.toString()); connect(okButton, SIGNAL( clicked() ), this, SLOT( apply() ) ); connect(cancelButton, SIGNAL(pressed()), this, SLOT(cancel())); shortcutindex = index; grabKeyboard(); okButton->setText(tr("Ok")); cancelButton->setText(tr("Cancel")); } ShortcutCaptureDialog::~ShortcutCaptureDialog() { releaseKeyboard(); } void ShortcutCaptureDialog::keyPressEvent(QKeyEvent* e) { bool shift, alt, ctrl, meta, conflict = false, realkey = false; QString msgString = ""; int temp_key; Qt::KeyboardModifiers mods = ((QInputEvent*)e)->modifiers(); shift = mods & Qt::ShiftModifier; ctrl = mods & Qt::ControlModifier; alt = mods & Qt::AltModifier; meta = mods & Qt::MetaModifier; //printf("Key total: %d, alt: %d, ctrl: %d shift: %d\n",e->key(), alt, ctrl, shift); temp_key = e->key(); QChar keychar(temp_key); //bool ispunct = keychar.isPunct(); //bool issymbol = keychar.isSymbol(); //printf("Key:%x, alt:%d, ctrl:%d shift:%d ispunct:%d issymbol:%d text:%s\n", // e->key(), alt, ctrl, shift, ispunct, issymbol, e->text().toLatin1().constData()); temp_key += (shift ? (int)Qt::SHIFT : 0); // (int) Tim temp_key += (ctrl ? (int)Qt::CTRL : 0); // temp_key += (alt ? (int)Qt::ALT : 0); // temp_key += (meta ? (int)Qt::META : 0); //printf("Final key assembled: %d\n",temp_key); // Check if this is a "real" key that completes a valid shortcut: int k = e->key(); if (k < 256 || k == Qt::Key_Enter || k == Qt::Key_Return || (k >= Qt::Key_F1 && k <= Qt::Key_F12) || k == Qt::Key_Home || k == Qt::Key_PageUp || k == Qt::Key_PageDown || k == Qt::Key_End || k == Qt::Key_Insert || k == Qt::Key_Delete || k == Qt::Key_Up || k == Qt::Key_Down || k == Qt::Key_Left || k == Qt::Key_Right) { key = temp_key; realkey = true; QKeySequence q = QKeySequence(key); //QKeySequence q = QKeySequence(k, mods); QString keyString = q.toString(); if (!keyString.isEmpty()) nshrtLabel->setText(keyString); // Check against conflicting shortcuts for (int i=0; i < SHRT_NUM_OF_ELEMENTS; i++) { if (shortcuts[i].key == key && // use the same key (( shortcuts[i].type & (shortcuts[shortcutindex].type | INVIS_SHRT)) || shortcuts[i].type & GLOBAL_SHRT || shortcuts[shortcutindex].type & GLOBAL_SHRT)) { // affect the same scope msgString = tr("Shortcut conflicts with %1").arg(qApp->translate("shortcuts",shortcuts[i].descr)); conflict = true; break; } } } messageLabel->setText(msgString); okButton->setEnabled(conflict == false && realkey); if (!realkey) nshrtLabel->setText(tr("Undefined")); } void ShortcutCaptureDialog::apply() { //return the shortcut to configurator widget: done(key); } } // namespace MusEGui muse-4.0.0/src/muse/components/shortcutcapturedialog.h000066400000000000000000000034321404105311000231700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/shortcutcapturedialog.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // Author: Mathias Lundgren , (C) 2003 // // Copyright: Mathias Lundgren (lunar_shuttle@users.sourceforge.net) (C) 2003 // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // // C++ Interface: shortcutcapturedialog // // Description: // Dialog window for capturing keyboard shortcuts // // #include "shortcuts.h" #include "filedialog.h" #include "ui_shortcutcapturedialogbase.h" class QKeyEvent; namespace MusEGui { class ShortcutCaptureDialog : public QDialog, public Ui::ShortcutCaptureDialogBase { Q_OBJECT private: int shortcutindex; void keyPressEvent(QKeyEvent* e); int key; private slots: void apply(); void cancel() { reject(); }; public: ShortcutCaptureDialog(QWidget* parent, int index=0); ~ShortcutCaptureDialog(); }; } // namespace MusEGui muse-4.0.0/src/muse/components/shortcutcapturedialogbase.ui000066400000000000000000000055761404105311000242240ustar00rootroot00000000000000 ShortcutCaptureDialogBase 0 0 323 285 Enter shortcut sequence true Press keys to enter shortcut sequence! Qt::AlignCenter false false Old shortcut: false Undefined false New shortcut: false Undefined false false OK Cancel muse-4.0.0/src/muse/components/shortcutconfig.cpp000066400000000000000000000237601404105311000221530ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/shortcutconfig.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // Author: Mathias Lundgren , (C) 2003 // // Copyright: Mathias Lundgren (lunar_shuttle@users.sourceforge.net) (C) 2003 // // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // // C++ Implementation: shortcutconfig // // Description: // Dialog for configuring keyboard shortcuts #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "shortcutconfig.h" #include "shortcutcapturedialog.h" #include "shortcuts.h" namespace MusEGui { ShortcutConfig::ShortcutConfig(QWidget* parent) : QDialog(parent) { setupUi(this); QSettings settings; restoreGeometry(settings.value("ShortcutConfig/geometry").toByteArray()); connect(cgListView, SIGNAL(itemClicked(QTreeWidgetItem*, int )), this, SLOT(categorySelChanged(QTreeWidgetItem*, int))); connect(scListView, SIGNAL(itemClicked(QTreeWidgetItem*, int )), this, SLOT(shortcutSelChanged(QTreeWidgetItem*, int))); connect(scListView, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int )), this, SLOT(assignShortcut())); connect(textFilter, &QLineEdit::textEdited, this, &ShortcutConfig::filterChanged); connect(keyFilter, &QLineEdit::textEdited, this, &ShortcutConfig::filterKeyChanged); okButton->setDefault(true); connect(defineButton, SIGNAL(pressed()), this, SLOT(assignShortcut())); connect(clearButton, SIGNAL(pressed()), this, SLOT(clearShortcut())); connect(textFileButton, SIGNAL(pressed()), this, SLOT(textFileClicked())); connect(applyButton, SIGNAL(pressed()), this, SLOT(applyAll())); connect(okButton, SIGNAL(pressed()), this, SLOT(okClicked())); connect(resetButton, SIGNAL(pressed()), this, SLOT(resetAllClicked())); current_category = ALL_SHRT; cgListView->sortItems(COL_CATVIEW, Qt::AscendingOrder); _config_changed = false; //Fill up category listview: SCListViewItem* newItem; SCListViewItem* selItem = nullptr; for (int i=0; i < SHRT_NUM_OF_CATEGORIES; i++) { newItem = new SCListViewItem(cgListView, i); newItem->setText(COL_CATVIEW, shortcut_category[i].name); if(shortcut_category[i].id_flag == current_category) selItem = newItem; } if(selItem) cgListView->setCurrentItem(selItem); updateSCListView(); scListView->setSortingEnabled(true); scListView->header()->resizeSection(COL_KEY, 120); scListView->header()->resizeSection(COL_DESCR, 360); scListView->header()->resizeSection(COL_CAT, 320); scListView->sortByColumn(COL_DESCR, Qt::AscendingOrder); } void ShortcutConfig::filterChanged(QString) { updateSCListView(); } void ShortcutConfig::filterKeyChanged(QString) { updateSCListView(); } void ShortcutConfig::updateSCListView() { scListView->clear(); SCListViewItem* newItem; QString filterText = textFilter->text(); QString filterKey = keyFilter->text(); for (int i=0; i < SHRT_NUM_OF_ELEMENTS; i++) { if (shortcuts[i].type & current_category) { if ((filterText.isEmpty() || QString(shortcuts[i].descr).contains(filterText, Qt::CaseInsensitive)) && (filterKey.isEmpty() || QString(shrtToStr(i)).contains(filterKey, Qt::CaseInsensitive)) ) { newItem = new SCListViewItem(scListView, i); newItem->setText(COL_KEY, shrtToStr(i)); newItem->setText(COL_DESCR, qApp->translate("shortcuts", shortcuts[i].descr)); QString cats; for (int j = 0; j < (SHRT_NUM_OF_CATEGORIES - 1); j++) { if (shortcuts[i].type & shortcut_category[j].id_flag) { if (!cats.isEmpty()) cats += QStringLiteral("+"); cats += shortcut_category[j].name; } } newItem->setText(COL_CAT, cats); } } } } void ShortcutConfig::assignShortcut() { SCListViewItem* active = static_cast(scListView->selectedItems().at(0)); int shortcutindex = active->getIndex(); ShortcutCaptureDialog* sc = new ShortcutCaptureDialog(this, shortcutindex); int key = sc->exec(); delete(sc); if (key != Rejected) { shortcuts[shortcutindex].key = key; QKeySequence keySequence = QKeySequence(key); active->setText(COL_KEY, keySequence.toString()); _config_changed = true; clearButton->setEnabled(true); } } void ShortcutConfig::clearShortcut() { SCListViewItem* active = static_cast(scListView->selectedItems().at(0)); int shortcutindex = active->getIndex(); shortcuts[shortcutindex].key = 0; //Cleared active->setText(COL_KEY,""); clearButton->setEnabled(false); _config_changed = true; } void ShortcutConfig::categorySelChanged(QTreeWidgetItem* i, int /*column*/) { SCListViewItem* item = static_cast(i); current_category = shortcut_category[item->getIndex()].id_flag; printf("category sel changed %d\n", current_category); updateSCListView(); } void ShortcutConfig::shortcutSelChanged(QTreeWidgetItem* in_item, int /*column*/) { defineButton->setEnabled(true); SCListViewItem* active = static_cast(in_item); int index = active->getIndex(); if (shortcuts[index].key != 0) clearButton->setEnabled(true); else clearButton->setEnabled(false); printf("shortcut sel changed %d\n", index); } void ShortcutConfig::closeEvent(QCloseEvent* /*e*/) // prevent compiler warning : unused variable { closing(); } // NOTE: closeEvent was not being called when Esc was pressed, despite the implication that // it should in the Qt help ("close event can't be ignored"). So this is a workaround, // and the 'recommended' way according to forums. void ShortcutConfig::reject() { closing(); QDialog::reject(); } void ShortcutConfig::closing() { QSettings settings; settings.setValue("ShortcutConfig/geometry", saveGeometry()); if(_config_changed) { emit saveConfig(); _config_changed = false; } } void ShortcutConfig::applyAll() { closing(); // Just call closing to store everything, and don't close. } void ShortcutConfig::okClicked() { close(); } void ShortcutConfig::resetAllClicked() { initShortCuts(); updateSCListView(); _config_changed = true; defineButton->setEnabled(false); clearButton->setEnabled(false); } void ShortcutConfig::textFileClicked() { QString fname = QFileDialog::getSaveFileName(this, tr("Save printable text file"), QDir::homePath() + "/shortcuts.txt", tr("Text files (*.txt);;All files (*)")); if(fname.isEmpty()) { //QMessageBox::critical(this, tr("Error"), tr("Selected file name is empty")); return; } QFile qf(fname); if(!qf.open(QIODevice::WriteOnly | QIODevice::Text)) { QMessageBox::critical(this, tr("Error"), tr("Error opening file for saving")); return; } bool error = false; QString header; for (int i=0; i < SHRT_NUM_OF_CATEGORIES; i++) { if(shortcut_category[i].id_flag == current_category) { header = QString(PACKAGE_NAME) + " " + tr("Shortcuts for selected category: ") + QString(shortcut_category[i].name) + "\n\n"; break; } } if(!header.isEmpty() && qf.write(header.toLatin1().constData()) == -1) error = true; QString info; if(current_category == ALL_SHRT) { info = tr("Legend:\n"); for (int i=0; i < SHRT_NUM_OF_CATEGORIES; i++) { if(shortcut_category[i].id_flag == ALL_SHRT) continue; info += (QString::number(i) + " : " + QString(shortcut_category[i].name) + "\n"); } info += "\n"; } if(!info.isEmpty() && qf.write(info.toLatin1().constData()) == -1) error = true; for(int i=0; i < SHRT_NUM_OF_ELEMENTS; i++) { if(shortcuts[i].type & current_category) { QString s; int pos = 0; if(current_category == ALL_SHRT) { for(int j=0; j < SHRT_NUM_OF_CATEGORIES; j++) { if(shortcut_category[j].id_flag == ALL_SHRT) continue; if(shortcuts[i].type & shortcut_category[j].id_flag) s.insert(pos, QString::number(j)); pos += 3; } s.insert(pos, " : "); pos += 3; } s.insert(pos, QKeySequence(shortcuts[i].key).toString()); pos += 25; s.insert(pos, qApp->translate("shortcuts", shortcuts[i].descr) + "\n"); if(qf.write(s.toLatin1().constData()) == -1) error = true; } } qf.close(); if(error) QMessageBox::critical(this, tr("Error"), tr("An error occurred while saving")); } } // namespace MusEGui muse-4.0.0/src/muse/components/shortcutconfig.h000066400000000000000000000050051404105311000216100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/shortcutconfig.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // Author: Mathias Lundgren , (C) 2003 // // Copyright: Mathias Lundgren (lunar_shuttle@users.sourceforge.net) (C) 2003 // // // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // // C++ Interface: shortcutconfig // // Description: // Dialog for configuring keyboard shortcuts // #ifndef __SHORTCUTCONFIG_H #define __SHORTCUTCONFIG_H class QCloseEvent; #include "ui_shortcutconfigbase.h" namespace MusEGui { class SCListViewItem : public QTreeWidgetItem { private: int index; public: SCListViewItem(QTreeWidget* parent, int i) : QTreeWidgetItem(parent), index(i) { } int getIndex() { return index; } }; class ShortcutConfig : public QDialog, public Ui::ShortcutConfigBase { Q_OBJECT enum KeyViewCols { COL_KEY = 0, COL_DESCR, COL_CAT }; static constexpr int COL_CATVIEW = 0; int current_category; void updateSCListView(); void closeEvent(QCloseEvent *e); void closing(); private slots: void categorySelChanged(QTreeWidgetItem*, int); void shortcutSelChanged(QTreeWidgetItem*, int); void assignShortcut(); void clearShortcut(); void applyAll(); void okClicked(); void resetAllClicked(); void textFileClicked(); void filterChanged(QString); void filterKeyChanged(QString); protected: void reject(); signals: void saveConfig(); public: ShortcutConfig(QWidget* parent = 0); bool _config_changed; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/shortcutconfigbase.ui000066400000000000000000000174461404105311000226450ustar00rootroot00000000000000 ShortcutConfigBase 0 0 1000 600 Configure Keyboard Shortcuts true QLayout::SetDefaultConstraint 0 0 120 200 false false Shortcut Context 0 0 170 230 Qt::StrongFocus Qt::ElideRight false false 120 120 false true Shortcut Description Context Shortcut 0 0 230 0 Qt::Horizontal QSizePolicy::Fixed 20 20 Description &Reset all Qt::Horizontal QSizePolicy::Expanding 150 21 false &Clear Alt+C false &Define Alt+D true Qt::Horizontal QSizePolicy::Expanding 30 21 &Printable file... Alt+P Qt::Horizontal QSizePolicy::Expanding 30 21 &Apply Alt+A &Ok Alt+O false textFilter keyFilter cgListView scListView resetButton clearButton defineButton textFileButton applyButton okButton muse-4.0.0/src/muse/components/sig_tempo_toolbar.cpp000066400000000000000000000161551404105311000226220ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // sig_tempo_toolbar.cpp // (C) Copyright 2012 Florian Jung (flo93@users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "sig_tempo_toolbar.h" #include "tempolabel.h" #include "sigedit.h" #include "song.h" #include "icons.h" #include "pixmap_button.h" #include "sig.h" #include "tempo.h" #include #include namespace MusEGui { //--------------------------------- // TempoToolbar //--------------------------------- TempoToolbar::TempoToolbar(QWidget* parent) : QToolBar(parent) { init(); } TempoToolbar::TempoToolbar(const QString& title, QWidget* parent) : QToolBar(title, parent) { init(); } void TempoToolbar::init() { setObjectName("Tempo toolbar"); _masterButton = new QToolButton(this); _masterButton->setIcon(*masterTrackOnSVGIcon); _masterButton->setCheckable(true); _masterButton->setToolTip(tr("Use mastertrack tempo")); connect(_masterButton, SIGNAL(toggled(bool)), SLOT(masterToggled(bool))); tempo_edit=new TempoEdit(this); tempo_edit->setToolTip(tr("Mastertrack tempo at current position, or fixed tempo")); tempo_edit->setContentsMargins(0, 0, 0, 0); tempo_edit->setFocusPolicy(Qt::StrongFocus); tap_button = new QToolButton(this); tap_button->setText(tr("Tap")); tap_button->setToolTip(tr("Tap this button to set tempo")); tap_button->setStatusTip(tr("Set the tempo to the time difference of two successive clicks. Times out after two seconds.")); tap_button->setContentsMargins(0, 0, 0, 0); blink_timer = new QTimer(this); connect(blink_timer, SIGNAL(timeout()), this, SLOT(tapButtonBlink())); blink_timer->stop(); // addWidget(_externSyncButton); addWidget(_masterButton); addWidget(tempo_edit); addWidget(tap_button); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), this, SLOT(song_changed(MusECore::SongChangedStruct_t))); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(pos_changed(int,unsigned,bool))); //connect(&MusEGlobal::extSyncFlag, SIGNAL(valueChanged(bool)), SLOT(syncChanged(bool))); connect(tempo_edit, SIGNAL(tempoChanged(double)), MusEGlobal::song, SLOT(setTempo(double))); connect(tempo_edit, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(tempo_edit, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(tap_button, SIGNAL(clicked(bool)), SLOT(tap_tempo())); connect(&tap_timer, SIGNAL(timeout()), SLOT(tap_timer_signal())); tap_timer.stop(); song_changed(-1); ensurePolished(); buttonDefColor = tap_button->palette().color(QPalette::Button).name(); } void TempoToolbar::pos_changed(int,unsigned,bool) { song_changed(SC_TEMPO); } void TempoToolbar::song_changed(MusECore::SongChangedStruct_t type) { if(type & (SC_TEMPO | SC_MASTER)) { int tempo = MusEGlobal::tempomap.tempo(MusEGlobal::song->cpos()); tempo_edit->blockSignals(true); tempo_edit->setValue(double(60000000.0/tempo)); tempo_edit->blockSignals(false); } if(type & SC_MASTER) { setMasterTrack(MusEGlobal::tempomap.masterFlag()); } if(type & SC_EXTERNAL_MIDI_SYNC) { syncChanged(MusEGlobal::extSyncFlag); } } void TempoToolbar::syncChanged(bool flag) { tap_button->setEnabled(!flag); tempo_edit->setExternalMode(flag); } void TempoToolbar::tap_tempo() { QDateTime local(QDateTime::currentDateTime()); if (tap_timer.isActive()) { qint64 msecs_tap = last_tap_time.msecsTo(local); double t_tap = (double)60000.0f / (double)msecs_tap; tempo_edit->setValue(t_tap); emit tempo_edit->tempoChanged(t_tap); } tap_timer.start(2000); blinkButtonState = true; blink_timer->start(100); last_tap_time = local; } void TempoToolbar::tap_timer_signal() { tap_timer.stop(); blink_timer->stop(); tap_button->setStyleSheet("QToolButton {background:" + buttonDefColor + "}"); } void TempoToolbar::tapButtonBlink() { blinkButtonState = !blinkButtonState; tap_button->setStyleSheet("QToolButton {background:" + (blinkButtonState ? buttonDefColor : "Crimson") + "}"); } //--------------------------------------------------------- // masterToggled //--------------------------------------------------------- void TempoToolbar::masterToggled(bool val) { emit masterTrackChanged(val); } bool TempoToolbar::masterTrack() const { return _masterButton->isChecked(); } void TempoToolbar::setMasterTrack(bool on) { _masterButton->blockSignals(true); _masterButton->setChecked(on); _masterButton->blockSignals(false); } //--------------------------------- // SigToolbar //--------------------------------- SigToolbar::SigToolbar(QWidget* parent) : QToolBar(parent) { init(); } SigToolbar::SigToolbar(const QString& title, QWidget* parent) : QToolBar(title, parent) { init(); } void SigToolbar::init() { setObjectName("Signature toolbar"); sig_edit=new SigEdit(this); sig_edit->setContentsMargins(0, 0, 0, 0); sig_edit->setFocusPolicy(Qt::StrongFocus); sig_edit->setValue(MusECore::TimeSignature(4, 4)); sig_edit->setToolTip(tr("Time signature at current position")); // label=new QLabel(tr("Signature "),this); // label->setContentsMargins(0, 0, 0, 0); // addWidget(label); addWidget(sig_edit); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), this, SLOT(song_changed(MusECore::SongChangedStruct_t))); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(pos_changed(int,unsigned,bool))); connect(sig_edit, SIGNAL(valueChanged(const MusECore::TimeSignature&)), MusEGlobal::song, SLOT(setSig(const MusECore::TimeSignature&))); connect(sig_edit, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(sig_edit, SIGNAL(escapePressed()), SIGNAL(escapePressed())); song_changed(-1); } void SigToolbar::pos_changed(int,unsigned,bool) { song_changed(SC_SIG); } void SigToolbar::song_changed(MusECore::SongChangedStruct_t type) { if(type & SC_SIG) { int z, n; MusEGlobal::sigmap.timesig(MusEGlobal::song->cpos(), z, n); sig_edit->blockSignals(true); sig_edit->setValue(MusECore::TimeSignature(z, n)); sig_edit->blockSignals(false); } } } // namespace MusEGui muse-4.0.0/src/muse/components/sig_tempo_toolbar.h000066400000000000000000000051721404105311000222640ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // sig_tempo_toolbar.h // (C) Copyright 2012 Florian Jung (flo93@users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SIG_TEMPO_TOOLBAR_H__ #define __SIG_TEMPO_TOOLBAR_H__ #include #include #include #include "type_defs.h" class QWidget; class QLabel; class QString; class QToolButton; namespace MusEGui { class TempoEdit; class SigEdit; class TempoToolbar : public QToolBar { Q_OBJECT private: TempoEdit* tempo_edit; QToolButton *tap_button; QToolButton* _masterButton; QTimer tap_timer; QDateTime last_tap_time; QTimer *blink_timer; bool blinkButtonState; QString buttonDefColor; void init(); public: TempoToolbar(QWidget* parent = nullptr); TempoToolbar(const QString& title, QWidget* parent = nullptr); bool masterTrack() const; void setMasterTrack(bool); signals: void returnPressed(); void escapePressed(); void masterTrackChanged(bool); private slots: void pos_changed(int,unsigned,bool); void song_changed(MusECore::SongChangedStruct_t); void syncChanged(bool); void tap_tempo(); void tap_timer_signal(); void masterToggled(bool); void tapButtonBlink(); }; class SigToolbar : public QToolBar { Q_OBJECT private: // QLabel* label; SigEdit* sig_edit; void init(); public: SigToolbar(QWidget* parent = 0); SigToolbar(const QString& title, QWidget* parent = 0); signals: void returnPressed(); void escapePressed(); private slots: void pos_changed(int,unsigned,bool); void song_changed(MusECore::SongChangedStruct_t); }; } #endif muse-4.0.0/src/muse/components/sigedit.cpp000066400000000000000000000112321404105311000205310ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= // #include "al/al.h" #include "sigedit.h" #include "sig.h" #include #include #include namespace MusEGui { using MusEGlobal::sigmap; //--------------------------------------------------------- // SigEdit //--------------------------------------------------------- SigEdit::SigEdit(QWidget* parent) : QWidget(parent) { initialized = false; slash = new QLabel("/",this); zSpin = new SigSpinBox(this); nSpin = new SigSpinBox(this); zSpin->setFocusPolicy(Qt::StrongFocus); nSpin->setFocusPolicy(Qt::StrongFocus); zSpin->setRange(1,99); nSpin->setDenominator(); nSpin->setRange(1,64); layout = new QHBoxLayout(this); layout->setContentsMargins(0,0,0,0); layout->setSpacing(1); layout->addWidget(zSpin); layout->addWidget(slash); layout->addWidget(nSpin); connect(zSpin, SIGNAL(valueChanged(int)), SLOT(setZ(int))); connect(nSpin, SIGNAL(valueChanged(int)), SLOT(setN(int))); connect(nSpin, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(zSpin, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(nSpin, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(zSpin, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(nSpin, SIGNAL(editingFinished()), SLOT(checkEditingFinishedNSpin())); connect(zSpin, SIGNAL(editingFinished()), SLOT(checkEditingFinishedZSpin())); connect(zSpin, SIGNAL(moveFocus()), SLOT(moveFocus())); connect(nSpin, SIGNAL(moveFocus()), SLOT(moveFocus())); zSpin->selectAll(); } SigEdit::~SigEdit() { delete layout; delete zSpin; delete nSpin; } //--------------------------------------------------------- // moveFocus //--------------------------------------------------------- void SigEdit::moveFocus() { if (zSpin->hasFocus()) { nSpin->setFocus(); nSpin->selectAll(); } else { zSpin->setFocus(); zSpin->selectAll(); } } //--------------------------------------------------------- // setZ //--------------------------------------------------------- void SigEdit::setZ(const int z) { _sig.z=z; emit valueChanged(_sig); } //--------------------------------------------------------- // setN //--------------------------------------------------------- void SigEdit::setN(const int n) { _sig.n=n; if (_sig.isValid()) { nSpin->setStyleSheet(""); emit valueChanged(_sig); } else nSpin->setStyleSheet("QSpinBox { background-color: red; }"); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void SigEdit::setValue(const MusECore::TimeSignature& s) { _sig = s; updateValue(); } //--------------------------------------------------------- // updateValue //--------------------------------------------------------- void SigEdit::updateValue() { zSpin->blockSignals(true); nSpin->blockSignals(true); zSpin->setValue(_sig.z); nSpin->setValue(_sig.n); zSpin->blockSignals(false); nSpin->blockSignals(false); } void SigEdit::paintEvent(QPaintEvent* event) { if (!initialized) updateValue(); initialized = true; QPainter p(this); p.fillRect(event->rect(), p.background()); QWidget::paintEvent(event); } void SigEdit::setFocus() { zSpin->setFocus(); } void SigEdit::checkEditingFinishedZSpin() { if (!nSpin->hasFocus()) emit editingFinished(); } void SigEdit::checkEditingFinishedNSpin() { if (!nSpin->hasFocus()) emit editingFinished(); } } muse-4.0.0/src/muse/components/sigedit.h000066400000000000000000000043111404105311000201760ustar00rootroot00000000000000//============================================================================= // Awl // Audio Widget Library // $Id:$ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __MUSE_SIGEDIT_H__ #define __MUSE_SIGEDIT_H__ #include "sig.h" #include "sigspinbox.h" #include #include #include namespace MusEGui { //--------------------------------------------------------- // SigEdit //--------------------------------------------------------- class SigEdit : public QWidget { Q_OBJECT MusECore::TimeSignature _sig; bool initialized; QLabel *slash; SigSpinBox *zSpin; SigSpinBox *nSpin; QHBoxLayout *layout; virtual void paintEvent(QPaintEvent* event); void updateValue(); signals: void valueChanged(const MusECore::TimeSignature&); void returnPressed(); void escapePressed(); void editingFinished(); private slots: void setN(const int n); void setZ(const int z); void moveFocus(); void checkEditingFinishedZSpin(); void checkEditingFinishedNSpin(); public slots: void setValue(const MusECore::TimeSignature&); void setFocus(); public: SigEdit(QWidget* parent = 0); ~SigEdit(); MusECore::TimeSignature sig() const { return _sig; } void setFrame(bool v) { zSpin->setFrame(v); nSpin->setFrame(v); } }; } #endif muse-4.0.0/src/muse/components/sigedit_OLD.cpp000066400000000000000000000534161404105311000212410ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sigedit.cpp,v 1.1.1.1.2.1 2004/12/28 23:23:51 lunar_shuttle Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include #include #include #include #include ///#include "sig.h" #include "sig.h" #include "sigedit.h" #include "spinbox.h" extern int MusEGlobal::mtcType; namespace MusEGui { bool Sig::isValid() const { if((z < 1) || (z > 63)) return false; switch(n) { case 1: case 2: case 3: case 4: case 8: case 16: case 32: case 64: case 128: return true; default: return false; } } //--------------------------------------------------------- // NumberSection //--------------------------------------------------------- class NumberSection { int selstart; int selend; public: NumberSection(int selStart = 0, int selEnd = 0) : selstart(selStart), selend(selEnd ) {} int selectionStart() const { return selstart; } void setSelectionStart(int s) { selstart = s; } int selectionEnd() const { return selend; } void setSelectionEnd( int s ) { selend = s; } int width() const { return selend - selstart; } }; //--------------------------------------------------------- // SigEditor //--------------------------------------------------------- class SigEditor : public QLineEdit { SigEdit* cw; bool frm; QPixmap *pm; int focusSec; QList sections; int offset; int section(const QPoint&); protected: void init(); virtual bool event(QEvent *e); virtual void resizeEvent(QResizeEvent*); virtual void paintEvent(QPaintEvent*); virtual void mousePressEvent(QMouseEvent *e); virtual void keyPressEvent(QKeyEvent * event ); void applyFocusSelection() {} public: SigEditor(SigEdit* parent, const char* name); ~SigEditor(); void setControlWidget(SigEdit * widget); SigEdit* controlWidget() const; int focusSection() const { return focusSec; } bool setFocusSection(int s); void appendSection(const NumberSection& sec); void clearSections(); void setSectionSelection(int sec, int selstart, int selend); }; //--------------------------------------------------------- // section //--------------------------------------------------------- int SigEditor::section(const QPoint& pt) { if (pm->isNull()) return -1; QPainter p(pm); int fw = frm ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0; int x = 2 + fw; int y = 0; int w = width(); int h = height(); for (int i = 0; i < sections.count(); ++i) { QString s = cw->sectionFormattedText(i); QRect bb = p.boundingRect(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, s); int nx = bb.x() + bb.width(); if (pt.x() >= x && pt.x() < nx) return i; x = nx; if (i < sections.count()-1) { QString s("/"); p.drawText(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, s, -1, &bb); x = bb.x() + bb.width(); } } return -1; } //--------------------------------------------------------- // SigEditor //--------------------------------------------------------- SigEditor::SigEditor(SigEdit* parent, const char* name) : QLineEdit(parent) { setObjectName(name); cw = parent; frm = true; focusSec = 0; pm = new QPixmap; offset = 0; init(); } //--------------------------------------------------------- // ~SigEditor //--------------------------------------------------------- SigEditor::~SigEditor() { delete pm; } //--------------------------------------------------------- // init //--------------------------------------------------------- void SigEditor::init() { setBackgroundMode(Qt::PaletteBase); setFocusSection(-1); setKeyCompression(true); setFocusPolicy(Qt::WheelFocus); } //--------------------------------------------------------- // event //--------------------------------------------------------- bool SigEditor::event(QEvent *e) { if (e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut) { repaint( rect(), false); } else if (e->type() == QEvent::ShortcutOverride) { QKeyEvent* ke = (QKeyEvent*) e; switch (ke->key()) { case Qt::Key_Delete: case Qt::Key_Backspace: case Qt::Key_Up: case Qt::Key_Down: case Qt::Key_Left: case Qt::Key_Right: ke->accept(); default: break; } } return QLineEdit::event(e); } void SigEditor::resizeEvent(QResizeEvent *e) { pm->resize(e->size()); QLineEdit::resizeEvent(e); } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void SigEditor::paintEvent(QPaintEvent *) { if (pm->isNull()) return; const QColorGroup & cg = colorGroup(); QPainter p(pm); p.setPen(colorGroup().text()); QBrush bg = cg.brush(QColorGroup::Base); int fw = frm ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0; int x = 2 + fw; int y = 0; int w = width(); int h = height(); p.fillRect(0, 0, w, h, bg); for (int i = 0; i < sections.count(); ++i) { QRect bb; QString s = cw->sectionFormattedText(i); if (hasFocus() && (int(i) == focusSec)) { QBrush bg = cg.brush(QColorGroup::Highlight); QRect r = p.boundingRect(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, s, -1); p.setPen(colorGroup().highlightedText()); p.fillRect(r, bg); } else p.setPen(colorGroup().text()); p.drawText(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, s, -1, &bb); x = bb.x() + bb.width(); if (i < sections.count()-1) { QString s("/"); p.drawText(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, s, -1, &bb); x = bb.x() + bb.width(); } } p.end(); bitBlt(this, 0, 0, pm); } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void SigEditor::mousePressEvent(QMouseEvent *e) { QPoint p(e->pos().x(), 0); int sec = section(p); if (sec != -1) { cw->setFocusSection(sec); repaint(rect(), false); } } //--------------------------------------------------------- // keyPressEvent //--------------------------------------------------------- void SigEditor::keyPressEvent(QKeyEvent * e ) { switch (e->key()) { case Qt::Key_Right: if (unsigned(focusSec) <= sections.count()) { if (cw->setFocusSection(focusSec+1)) repaint(rect(), false); } case Qt::Key_Left: if (focusSec > 0 ) { if (cw->setFocusSection(focusSec-1)) repaint(rect(), false); } case Qt::Key_Up: cw->stepUp(); case Qt::Key_Down: cw->stepDown(); case Qt::Key_Backspace: case Qt::Key_Delete: cw->removeLastNumber(focusSec); case Qt::Key_Enter: case Qt::Key_Return: cw->enterPressed(); default: QString txt = e->text(); if (!txt.isEmpty() && txt[0] == '/') { // do the same thing as KEY_RIGHT when the user presses the separator key if (focusSec < (signed)(sections.count())) { if (cw->setFocusSection(focusSec+1)) repaint(rect(), false); } } int num = txt[0].digitValue(); //printf("SigEditor::keyPressEvent num:%d\n", num); if (num != -1) { cw->addNumber(focusSec, num); } } } void SigEditor::appendSection(const NumberSection& sec) { sections.append(sec); } void SigEditor::clearSections() { sections.clear(); } //--------------------------------------------------------- // setSectionSelection //--------------------------------------------------------- void SigEditor::setSectionSelection(int secNo, int selstart, int selend) { if (secNo < 0 || secNo > (int)sections.count()) return; sections[secNo].setSelectionStart(selstart); sections[secNo].setSelectionEnd(selend); } //--------------------------------------------------------- // setFocusSection //--------------------------------------------------------- bool SigEditor::setFocusSection(int idx) { if (idx > (int)sections.count()-1 || idx < 0) return false; if (idx != focusSec) { focusSec = idx; applyFocusSelection(); return true; } return false; } //--------------------------------------------------------- // SigEdit //--------------------------------------------------------- SigEdit::SigEdit(QWidget* parent, const char* name) : QWidget(parent) { setObjectName(name); init(); updateButtons(); } SigEdit::~SigEdit() { } //--------------------------------------------------------- // init //--------------------------------------------------------- void SigEdit::init() { ed = new SigEditor(this, "pos editor"); controls = new SpinBox(this); controls->setEditor(ed); setFocusProxy(ed); connect(controls, SIGNAL(stepUpPressed()), SLOT(stepUp())); connect(controls, SIGNAL(stepDownPressed()), SLOT(stepDown())); connect(this, SIGNAL(valueChanged(int,int)),SLOT(updateButtons())); overwrite = false; timerId = 0; typing = false; changed = false; adv = false; sec[0].offset = 0; sec[0].len = 2; sec[0].val = 4; sec[0].voff = 0; sec[1].offset = 3; sec[1].len = 3; sec[1].val = 4; sec[1].voff = 0; ed->clearSections(); ed->appendSection(NumberSection(0,0)); ed->appendSection(NumberSection(0,0)); setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void SigEdit::setValue(const Sig& sig) { sec[0].val = sig.z; sec[1].val = sig.n; changed = false; ed->repaint(ed->rect(), false); } void SigEdit::setValue(const QString& s) { int z, n; sscanf(s.toLatin1(), "%d/%d", &z, &n); Sig sig(z, n); setValue(sig); } Sig SigEdit::sig() const { Sig sig(sec[0].val, sec[1].val); return sig; } bool SigEdit::event(QEvent *e) { if (e->type() == QEvent::FocusOut) { typing = false; if (changed) { emit valueChanged(sig().z, sig().n); changed = false; } } return QWidget::event(e); } void SigEdit::timerEvent(QTimerEvent *) { overwrite = true; } //--------------------------------------------------------- // stepUp //--------------------------------------------------------- void SigEdit::stepUp() { bool accepted = false; int secNo = ed->focusSection(); int val = sec[secNo].val; if (secNo == 0) { if (val < 63) { ++val; accepted = true; } } else { accepted = true; switch(val) { case 1: val = 2; break; case 2: val = 3; break; case 3: val = 4; break; case 4: val = 8; break; case 8: val = 16; break; case 16: val = 32; break; case 32: val = 64; break; case 64: val = 128; break; case 128: accepted = false; break; } } if (accepted) { setSec(secNo, val); changed = true; emit valueChanged(sec[0].val, sec[1].val); } ed->repaint(ed->rect(), false); } //--------------------------------------------------------- // stepDown //--------------------------------------------------------- void SigEdit::stepDown() { bool accepted = false; int secNo = ed->focusSection(); int val = sec[secNo].val; if (secNo == 0) { if (val > 1) { --val; accepted = true; } } else { accepted = true; switch(val) { case 1: accepted = false; break; case 2: val = 1; break; case 3: val = 2; break; case 4: val = 3; break; case 8: val = 4; break; case 16: val = 8; break; case 32: val = 16; break; case 64: val = 32; break; case 128: val = 64; break; } } if (accepted) { setSec(secNo, val); changed = true; emit valueChanged(sec[0].val, sec[1].val); } ed->repaint(ed->rect(), false); } //--------------------------------------------------------- // sectionFormattedText // Returns the formatted number for section sec. //--------------------------------------------------------- QString SigEdit::sectionFormattedText(int secNo) { QString txt = sectionText(secNo); int so = sec[secNo].offset; int len = sec[secNo].len; int eo = so + len; if (typing && secNo == ed->focusSection()) ed->setSectionSelection(secNo, eo - txt.length(), eo); else ed->setSectionSelection(secNo, so, eo); if (secNo == 0) txt = txt.rightJustify(len, ' '); // else // txt = txt.leftJustify(len, ' '); return txt; } //--------------------------------------------------------- // setFocusSection //--------------------------------------------------------- bool SigEdit::setFocusSection(int s) { if (s != ed->focusSection()) { killTimer(timerId); overwrite = true; typing = false; int so = sec[s].offset; int eo = so + sec[s].len; ed->setSectionSelection(s, so, eo); if (changed) { emit valueChanged(sig().z, sig().n); changed = false; } } return ed->setFocusSection(s); } //--------------------------------------------------------- // setSec //--------------------------------------------------------- void SigEdit::setSec(int secNo, int val) { sec[secNo].val = val; } //--------------------------------------------------------- // sectionText // Returns the text of section \a sec. //--------------------------------------------------------- QString SigEdit::sectionText(int secNo) { return QString::number(sec[secNo].val + sec[secNo].voff); } //--------------------------------------------------------- // outOfRange // return true if out of range //--------------------------------------------------------- bool SigEdit::outOfRange(int secNo, int val) const { if (secNo == 0) return ((val < 1) || (val > 63)); switch (val) { case 1: case 2: case 3: case 4: case 8: case 16: case 32: case 64: case 128: // Changed p3.3.43 //return true; return false; default: // Changed p3.3.43 //return false; return true; } } //--------------------------------------------------------- // addNumber //--------------------------------------------------------- void SigEdit::addNumber(int secNo, int num) { if (secNo == -1) return; killTimer(timerId); bool accepted = false; typing = true; int voff = sec[secNo].voff; QString txt = sectionText(secNo); //printf("SigEdit::addNumber secNo:%d num:%d voff:%d txt:%s\n", secNo, num, voff, txt.toLatin1()); if ((unsigned) txt.length() == sec[secNo].len) { //printf("SigEdit::addNumber txt.length() == sec[secNo].len (%d)\n", sec[secNo].len); if (!outOfRange(secNo, num - voff)) { //printf("SigEdit::addNumber accepted\n"); accepted = true; sec[secNo].val = num - voff; } } else { //printf("SigEdit::addNumber txt.length() != sec[secNo].len (%d)\n", sec[secNo].len); txt += QString::number(num); int temp = txt.toInt() - voff; if (outOfRange(secNo, temp)) { //printf("SigEdit::addNumber not accepted secNo:%d txt:%s temp:%d\n", secNo, txt.toLatin1(), temp); txt = sectionText(secNo); } else { //printf("SigEdit::addNumber accepted\n"); accepted = true; sec[secNo].val = temp; } if (adv && ((unsigned) txt.length() == sec[secNo].len)) { setFocusSection(ed->focusSection() + 1); } } changed = accepted; if (accepted) emit valueChanged(sig().z, sig().n); timerId = startTimer(qApp->doubleClickInterval()*4); ed->repaint(ed->rect(), false); } //--------------------------------------------------------- // removeLastNumber //--------------------------------------------------------- void SigEdit::removeLastNumber(int secNo) { if (secNo == -1) return; QString txt = QString::number(sec[secNo].val); txt = txt.mid(0, txt.length() - 1); sec[secNo].val = txt.toInt() - sec[secNo].voff; ed->repaint(ed->rect(), false); } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void SigEdit::resizeEvent(QResizeEvent* ev) { QWidget::resizeEvent(ev); controls->resize(width(), height()); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize SigEdit::sizeHint() const { QFontMetrics fm(font()); int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, this); // ddskrjo int h = fm.height() + fw * 2; int w = 2 + controls->arrowWidth() + fw * 4; w += fm.width('9') * 5 + fm.width('/'); return QSize(w, h).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // updateButtons //--------------------------------------------------------- void SigEdit::updateButtons() { int secNo = ed->focusSection(); int val = sec[secNo].val; bool upEnabled; bool downEnabled; if (secNo == 0) { upEnabled = val < 63; downEnabled = val > 1; } else { upEnabled = true; downEnabled = true; switch (val) { case 1: downEnabled = false; break; case 128: upEnabled = false; break; } } controls->setStepEnabled(isEnabled() & upEnabled, isEnabled() & downEnabled); } //--------------------------------------------------------- // enterPressed //! emit returnPressed //--------------------------------------------------------- void SigEdit::enterPressed() { emit returnPressed(); } } // namespace MusEGui muse-4.0.0/src/muse/components/sigedit_OLD.h000066400000000000000000000053051404105311000207000ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sigedit.h,v 1.1.1.1.2.1 2004/12/28 23:23:51 lunar_shuttle Exp $ // (C) Copyright 2002 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SIGEDIT_H__ #define __SIGEDIT_H__ #include #include "section.h" class QResizeEvent; class QTimerEvent; namespace MusEGui { class SigEditor; class SpinBox; struct Sig { int z; int n; public: Sig(int _z, int _n) : z(_z), n(_n) {} bool isValid() const; }; //--------------------------------------------------------- // SigEdit //--------------------------------------------------------- class SigEdit : public QWidget { Q_OBJECT void init(); QString sectionText(int sec); Section sec[2]; bool adv; bool overwrite; int timerId; bool typing; bool changed; SigEditor *ed; SpinBox* controls; private slots: void stepUp(); void stepDown(); signals: void valueChanged(int, int); void returnPressed(); protected: virtual bool event(QEvent *e ); void timerEvent(QTimerEvent* e); virtual void resizeEvent(QResizeEvent*); QString sectionFormattedText(int sec); void addNumber(int sec, int num); void removeLastNumber(int sec); bool setFocusSection(int s); virtual bool outOfRange(int, int) const; virtual void setSec(int, int); friend class SigEditor; protected slots: void updateButtons(); public slots: virtual void setValue(const Sig& sig); void setValue(const QString& s); public: SigEdit(QWidget*, const char* = 0); ~SigEdit(); QSize sizeHint() const; Sig sig() const; virtual void setAutoAdvance(bool advance) { adv = advance; } bool autoAdvance() const { return adv; } void enterPressed(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/siglabel.cpp000066400000000000000000000135111404105311000206650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: siglabel.cpp,v 1.1.1.1 2003/10/27 18:54:28 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "siglabel.h" #include #define TIMER1 400 #define TIMER2 200 #define TIMEC 7 #define TIMER3 100 #define TIMEC2 20 #define TIMER4 50 #include "globals.h" #include "gconfig.h" #include #include #include namespace MusEGui { //--------------------------------------------------------- // SigLabel // edit Signature Values (4/4) //--------------------------------------------------------- SigLabel::SigLabel(int zz, int nn, QWidget* parent) : QLabel(parent) { z = n = 0; setFocusPolicy(Qt::NoFocus); setAlignment(Qt::AlignCenter); setValue(zz, nn); } SigLabel::SigLabel(const MusECore::TimeSignature& sig, QWidget* parent) : QLabel(parent) { z = n = 0; setFocusPolicy(Qt::NoFocus); setAlignment(Qt::AlignCenter); setValue(sig.z, sig.n); } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void SigLabel::mousePressEvent(QMouseEvent* event) { int button = event->button(); bool zaehler = event->x() < width() /2; int zz = z, nn = n; switch (button) { case Qt::LeftButton: if (!MusEGlobal::config.leftMouseButtonCanDecrease) return; // else fall through case Qt::MidButton: incValue(zaehler, false, zz, nn); break; case Qt::RightButton: incValue(zaehler, true, zz, nn); break; default: break; } if ((zz != z) || (nn != n)) { setValue(zz, nn); emit valueChanged(MusECore::TimeSignature(zz, nn)); } } //--------------------------------------------------------- // incValue //--------------------------------------------------------- void SigLabel::incValue(bool zaehler, bool up, int& zz, int& nn) { if (!up) { if (zaehler) { --zz; if (zz < 1) zz = 1; } else { switch (nn) { case 1: break; case 2: nn = 1; break; case 4: nn = 2; break; case 8: nn = 4; break; case 16: nn = 8; break; case 32: nn = 16; break; case 64: nn = 32; break; case 128: nn = 64; break; } } } else { if (zaehler) { ++zz; if (zz > 16) zz = 16; } else { switch (nn) { case 1: nn = 2; break; case 2: nn = 4; break; case 4: nn = 8; break; case 8: nn = 16; break; case 16: nn = 32; break; case 32: nn = 64; break; case 64: nn = 128; break; case 128: break; } } } } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void SigLabel::wheelEvent(QWheelEvent* event) { #if QT_VERSION >= 0x050e00 bool zaehler = event->position().toPoint().x() < width() /2; #else bool zaehler = event->x() < width() /2; #endif const QPoint pixelDelta = event->pixelDelta(); const QPoint angleDegrees = event->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; int zz = z, nn = n; bool inc = delta >= 0; incValue(zaehler, inc, zz, nn); if ((zz != z) || (nn != n)) { setValue(zz, nn); emit valueChanged(MusECore::TimeSignature(zz, nn)); } } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void SigLabel::setValue(int a, int b) { if (a == z && b == n) return; z = a; n = b; QString sa; sa.setNum(a); QString sb; sb.setNum(b); QString s = sa + QString("/") + sb; setText(s); } //--------------------------------------------------------- // setFrame //--------------------------------------------------------- void SigLabel::setFrame(bool flag) { setFrameStyle(flag ? Panel | Sunken : NoFrame); setLineWidth(2); } } // namespace MusEGui muse-4.0.0/src/muse/components/siglabel.h000066400000000000000000000040471404105311000203360ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: siglabel.h,v 1.1.1.1 2003/10/27 18:54:56 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SIGLABEL_H__ #define __SIGLABEL_H__ #include #include class QWheelEvent; class QMouseEvent; namespace MusEGui { //--------------------------------------------------------- // SigLabel // show/edit time signature //--------------------------------------------------------- class SigLabel : public QLabel { Q_OBJECT virtual void mousePressEvent(QMouseEvent*); virtual void wheelEvent(QWheelEvent*); void incValue(bool zaehler, bool inc, int&, int&); protected: int z, n; signals: void valueChanged(const MusECore::TimeSignature&); public slots: virtual void setValue(int, int); virtual void setValue(const MusECore::TimeSignature& sig) { setValue(sig.z, sig.n); } public: SigLabel(int z, int n, QWidget*); SigLabel(const MusECore::TimeSignature&, QWidget*); void value(int& a, int& b) const { a = z; b = n; } MusECore::TimeSignature value() const { return MusECore::TimeSignature(z, n); } void setFrame(bool); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/sigscale.cpp000066400000000000000000000126171404105311000207030ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sigscale.cpp,v 1.6 2004/04/11 13:03:32 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include "sig.h" #include "globals.h" #include "sigscale.h" #include "song.h" #include "gconfig.h" namespace MusEGui { //--------------------------------------------------------- // SigScale //--------------------------------------------------------- SigScale::SigScale(int* r, QWidget* parent, int xs) : View(parent, xs, 1) { setToolTip(tr("Signature scale")); raster = r; pos[0] = MusEGlobal::song->cpos(); pos[1] = MusEGlobal::song->lpos(); pos[2] = MusEGlobal::song->rpos(); button = Qt::NoButton; setMouseTracking(true); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(setPos(int, unsigned, bool))); setFixedHeight(18); } //--------------------------------------------------------- // setPos //--------------------------------------------------------- void SigScale::setPos(int idx, unsigned val, bool) { if (val == pos[idx]) return; unsigned opos = mapx(pos[idx]); pos[idx] = val; if (!isVisible()) return; val = mapx(val); int x = -9; int w = 18; if (opos > val) { w += opos - val; x += val; } else { w += val - opos; x += opos; } redraw(QRect(x, 0, w, height())); } void SigScale::viewMousePressEvent(QMouseEvent* event) { button = event->button(); viewMouseMoveEvent(event); } void SigScale::viewMouseReleaseEvent(QMouseEvent*) { button = Qt::NoButton; } void SigScale::viewMouseMoveEvent(QMouseEvent* event) { int x = event->x(); if(x < 0) x = 0; x = MusEGlobal::sigmap.raster(x, *raster); emit timeChanged(x); MusECore::Song::POSTYPE posType; switch (button) { case Qt::LeftButton: if (event->modifiers() & Qt::ControlModifier) posType = MusECore::Song::LPOS; else posType = MusECore::Song::CPOS; break; case Qt::MidButton: posType = MusECore::Song::LPOS; break; case Qt::RightButton: posType = MusECore::Song::RPOS; break; default: return; // if no button is pressed the function returns here } MusECore::Pos p(x, true); MusEGlobal::song->setPos(posType, p); } //--------------------------------------------------------- // leaveEvent //--------------------------------------------------------- void SigScale::leaveEvent(QEvent*) { // emit timeChanged(INT_MAX); } //--------------------------------------------------------- // draw //--------------------------------------------------------- void SigScale::pdraw(QPainter& p, const QRect& r, const QRegion&) { // seems the only way to draw the correct background... QStyleOption opt; opt.init(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); int x = r.x(); int w = r.width(); int h = height(); if (x < 0) x = 0; p.setFont(MusEGlobal::config.fonts[3]); p.setPen(palette().color(QPalette::WindowText)); for (MusECore::ciSigEvent si = MusEGlobal::sigmap.begin(); si != MusEGlobal::sigmap.end(); ++si) { MusECore::SigEvent* e = si->second; int xp = mapx(e->tick); if (xp > x+w) break; if (xp+40 < x) continue; p.drawLine(xp, 0, xp, h/2); p.drawLine(xp, h/2, xp+5, h/2); QString s = QString("%1/%2").arg(e->sig.z).arg(e->sig.n); p.drawText(xp+8, h-6, s); } //--------------------------------------------------- // draw location marker //--------------------------------------------------- p.setPen(MusEGlobal::config.rangeMarkerColor); int xp = mapx(pos[1]); if (xp >= x && xp < x+w) p.drawLine(xp, 0, xp, h); xp = mapx(pos[2]); if (xp >= x && xp < x+w) p.drawLine(xp, 0, xp, h); // Draw the red main position cursor last, on top of the others. p.setPen(MusEGlobal::config.positionMarkerColor); xp = mapx(pos[0]); if (xp >= x && xp < x+w) p.drawLine(xp, 0, xp, h); } } // namespace MusEGui muse-4.0.0/src/muse/components/sigscale.h000066400000000000000000000037651404105311000203540ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sigscale.h,v 1.2 2004/01/11 18:55:37 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SIGSCALE_H__ #define __SIGSCALE_H__ #include #include "view.h" class QPainter; class QMouseEvent; class QEvent; class QWidget; class QRect; namespace MusEGui { //--------------------------------------------------------- // SigScale // Time Signature Scale //--------------------------------------------------------- class SigScale : public View { Q_OBJECT int* raster; unsigned pos[3]; int button; signals: void posChanged(unsigned, unsigned); protected: virtual void pdraw(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void viewMousePressEvent(QMouseEvent* event); virtual void viewMouseMoveEvent(QMouseEvent* event); virtual void viewMouseReleaseEvent(QMouseEvent* event); virtual void leaveEvent(QEvent*e); signals: void timeChanged(unsigned); public slots: void setPos(int, unsigned, bool); public: SigScale(int* raster, QWidget* parent, int xscale); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/sigspinbox.cpp000066400000000000000000000051521404105311000212720ustar00rootroot00000000000000#include "sigspinbox.h" #include "sig.h" #include #include #include #include #include #include #include class MyLineEdit : public QLineEdit { public: MyLineEdit(QWidget* parent = 0) : QLineEdit(parent) {}; protected: virtual void mousePressEvent (QMouseEvent* e) { QLineEdit::mousePressEvent(e); selectAll(); } }; SigSpinBox::SigSpinBox(QWidget *parent) : QSpinBox(parent) { setKeyboardTracking(false); _denominator=false; setLineEdit(new MyLineEdit(this)); } void SigSpinBox::keyPressEvent(QKeyEvent* ev) { switch (ev->key()) { case Qt::Key_Return: QSpinBox::keyPressEvent(ev); emit returnPressed(); return; break; case Qt::Key_Escape: emit escapePressed(); return; break; case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Slash: emit moveFocus(); return; break; default: break; } QSpinBox::keyPressEvent(ev); } void SigSpinBox::setDenominator() { _denominator=true; } void SigSpinBox::stepBy(int step) { if (!_denominator) { setValue(value() + step); return; } MusECore::TimeSignature sig(4, value()); if (step == 1) { // make sure that sig is valid then increase if (sig.isValid()) setValue(value() * 2); } else if (step == -1) { // make sure that sig is valid then increase if (sig.isValid()) { int v = value() / 2; if (v < 2) v = 2; setValue(v); } } } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize SigSpinBox::sizeHint() const { if(const QStyle* st = style()) { st = st->proxy(); QStyleOptionSpinBox option; option.initFrom(this); option.rect = rect(); option.state = QStyle::State_Active | QStyle::State_Enabled; const QRect b_rect = st->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxUp); QFontMetrics fm(font()); const int fw = st->pixelMetric(QStyle::PM_SpinBoxFrameWidth); int h = fm.height() + fw * 2; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = fw * 2 + b_rect.width() + fm.horizontalAdvance(QString("00")); #else int w = fw * 2 + b_rect.width() + fm.width(QString("00")); #endif return QSize(w, h); } return QSize(20, 20); } muse-4.0.0/src/muse/components/sigspinbox.h000066400000000000000000000007261404105311000207410ustar00rootroot00000000000000#ifndef __SIGSPINBOX_H__ #define __SIGSPINBOX_H__ #include class SigSpinBox : public QSpinBox { Q_OBJECT bool _denominator; protected: virtual void keyPressEvent(QKeyEvent*); virtual void stepBy(int); public: explicit SigSpinBox(QWidget *parent = 0); void setDenominator(); QSize sizeHint() const; signals: void returnPressed(); void escapePressed(); void moveFocus(); public slots: }; #endif // __SIGSPINBOX_H__ muse-4.0.0/src/muse/components/slider.cpp000066400000000000000000001114361404105311000203720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/slider.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) // (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include "mmath.h" #include #include #include "utils.h" #include "slider.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_SLIDER(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { //------------------------------------------------------------- // Slider - The Slider Widget // // Slider is a slider widget which operates on an interval // of type double. Slider supports different layouts as // well as a scale. //------------------------------------------------------------ //------------------------------------------------------------ //.F Slider::Slider // // Constructor // //.u Syntax: //.f Slider::Slider(QWidget *parent, const char *name, Orientation orient = Horizontal, ScalePos scalePos = None, int bgStyle = BgTrough) // //.u Parameters //.p // QWidget *parent -- parent widget // const char *name -- The Widget's name. Default = 0. // Orientation Orient -- Orientation of the slider. Can be Slider::Horizontal // or Slider::Vertical. // Defaults to Horizontal. // ScalePos scalePos -- Position of the scale. Can be Slider::None, // Slider::Left, Slider::Right, Slider::Top, // or Slider::Bottom. Defaults to Slider::None. !!! CURRENTLY only Slider::None supported - oget 20110913 // QColor fillcolor -- the color used to fill in the full side // of the Slider. If fillColor is invalid (default) it will draw with the palette highlight colour. //------------------------------------------------------------ Slider::Slider(QWidget *parent, const char *name, Qt::Orientation orient, ScalePos scalePos, int grooveWidth, QColor fillColor, ScaleDraw::TextHighlightMode textHighlightMode, QColor handleColor) : SliderBase(parent,name), d_scalePos(scalePos), d_grooveWidth(grooveWidth), d_fillColor(fillColor), d_handleColor(handleColor) { setPagingButtons(Qt::RightButton); d_thumbLength = 16; d_thumbHalf = 8; d_thumbWidth = 16; d_fillThumb = true; d_fillEmptySide = true; d_frame = false; d_radius = 4; d_radiusHandle = 2; d_useGradient = true; d_scaleDist = 4; d_scaleStep = 0.0; d_xMargin = 0; d_yMargin = 0; d_mMargin = 1; horizontal_hint = 40; vertical_hint = 40; // set to sane values to avoid erratic size hint // calculation -> drawing problems (kybos) if (orient == Qt::Vertical) { d_sliderRect.setRect(0, 0, 20, 100); } else { d_sliderRect.setRect(0, 0, 100, 20); } setOrientation(orient); d_scale.setTextHighlightMode(textHighlightMode); } //------------------------------------------------------------ //.F Slider::setSizeHint //------------------------------------------------------------ void Slider::setSizeHint(uint w, uint h) { horizontal_hint = w; vertical_hint = h; } //------------------------------------------------------------ //.F Slider::~Slider // Destructor //.u Syntax //.f Slider::~Slider() //------------------------------------------------------------ Slider::~Slider() { } //---------------------------------------------------- // //.F Slider::setThumbLength // // Set the slider's thumb length // //.u Syntax // void Slider::setThumbLength(int l) // //.u Parameters //.p int l -- new length // //----------------------------------------------------- void Slider::setThumbLength(int l) { // d_thumbLength = MusECore::qwtMax(l,8); d_thumbLength = l; d_thumbHalf = d_thumbLength / 2; resize(size()); } //------------------------------------------------------------ // //.F Slider::setThumbWidth // Change the width of the thumb // //.u Syntax //.p void Slider::setThumbWidth(int w) // //.u Parameters //.p int w -- new width // //------------------------------------------------------------ void Slider::setThumbWidth(int w) { d_thumbWidth = MusECore::qwtMax(w,4); resize(size()); } //------------------------------------------------------------ //.- //.F Slider::scaleChange // Notify changed scale // //.u Syntax //.f void Slider::scaleChange() // //.u Description // Called by QwtScaledWidget // //------------------------------------------------------------ void Slider::scaleChange() { if (!hasUserScale()) d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); update(); } //------------------------------------------------------------ //.- //.F Slider::fontChange // Notify change in font // //.u Syntax //.f Slider::fontChange(const QFont &oldFont) // //------------------------------------------------------------ void Slider::fontChange(const QFont & /*oldFont*/) { // repaint(); update(); } void Slider::drawThumb(QPainter *p, const QRect &r) { p->setRenderHint(QPainter::Antialiasing); QColor thumb_edge; QColor thumb_center; const QPalette& pal = palette(); if (d_handleColor.isValid()) { thumb_edge = d_handleColor; thumb_center = d_handleColor.lighter(); } else { thumb_edge = pal.dark().color(); thumb_center = pal.mid().color(); } QLinearGradient thumbGrad; thumbGrad.setColorAt(0, thumb_edge); thumbGrad.setColorAt(0.5, thumb_center); thumbGrad.setColorAt(1, thumb_edge); const double rpos = (value(ConvertNone) - minValue(ConvertNone)) / (maxValue(ConvertNone) - minValue(ConvertNone)); if(d_orient == Qt::Horizontal) { int crh, thh; // if(d_scalePos == InsideHorizontal) // { // crh = height() - r.y() - d_yMargin - 2 * d_mMargin; // thh = height() - r.y() - d_yMargin - d_mMargin; // } // else { crh = r.height() - 2 * d_mMargin; thh = r.height(); } const QRect cr(r.x(), r.y() + d_mMargin, r.width(), //r.height() - 2*d_mMargin); crh); const int dist1 = int(double(cr.width() - d_thumbLength) * rpos); const int ipos = cr.x() + dist1; markerPos = ipos + d_thumbHalf; // // Draw thumb // QPainterPath thumb_rect = MusECore::roundedPath(ipos, r.y(), //d_thumbLength, r.height(), d_thumbLength, thh, d_radiusHandle, d_radiusHandle, (MusECore::Corner) (MusECore::CornerUpperLeft | MusECore::CornerUpperRight | MusECore::CornerLowerLeft | MusECore::CornerLowerRight) ); // thumbGrad.setStart(QPointF(0, cr.y())); // thumbGrad.setFinalStop(QPointF(0, cr.y() + cr.height())); thumbGrad.setStart(QPointF(ipos, 0)); thumbGrad.setFinalStop(QPointF(ipos + d_thumbLength, 0)); if(d_fillThumb) p->fillPath(thumb_rect, QBrush(thumbGrad)); else { p->setPen(pal.shadow().color()); p->drawPath(thumb_rect); } // center line p->fillRect(ipos + d_thumbHalf, cr.y(), 1, cr.height(), pal.dark().color()); } else { int crw, thw; // if(d_scalePos == InsideVertical) // { // crw = width() - r.x() - d_xMargin - 2 * d_mMargin; // thw = width() - r.x() - d_xMargin - d_mMargin; // } // else { crw = r.width() - 2 * d_mMargin; thw = r.width(); } const QRect cr(r.x() + d_mMargin, r.y(), //r.width() - 2*d_mMargin, crw, r.height()); const int dist1 = int(double(cr.height() - d_thumbLength) * (1.0 - rpos)); const int ipos = cr.y() + dist1; markerPos = ipos + d_thumbHalf; // // Draw thumb // QPainterPath thumb_rect = MusECore::roundedPath(r.x(), ipos, //r.width(), d_thumbLength, thw, d_thumbLength, d_radiusHandle, d_radiusHandle, (MusECore::Corner) (MusECore::CornerUpperLeft | MusECore::CornerUpperRight | MusECore::CornerLowerLeft | MusECore::CornerLowerRight) ); // thumbGrad.setStart(QPointF(cr.x(), 0)); // thumbGrad.setFinalStop(QPointF(cr.x() + cr.width(), 0)); thumbGrad.setStart(QPointF(0, ipos)); thumbGrad.setFinalStop(QPointF(0, ipos + d_thumbLength)); if(d_fillThumb) p->fillPath(thumb_rect, QBrush(thumbGrad)); else { p->setPen(pal.shadow().color()); p->drawPath(thumb_rect); } // center line p->fillRect(cr.x(), ipos + d_thumbHalf, cr.width(), 1, pal.dark().color()); } } //------------------------------------------------------------ // drawSlider // Draw the slider into the specified rectangle. //------------------------------------------------------------ void Slider::drawSlider(QPainter *p, const QRect &r) { p->setRenderHint(QPainter::Antialiasing); const QPalette& pal = palette(); // for the full side const double rpos = (value(ConvertNone) - minValue(ConvertNone)) / (maxValue(ConvertNone) - minValue(ConvertNone)); QColor f_mask_min(d_fillColor.isValid() ? d_fillColor : pal.highlight().color()); QColor f_mask_max(f_mask_min); if (d_useGradient) { f_mask_min.setAlpha(40); //f_mask_max.setAlpha(200); f_mask_max.setAlpha(255); } QLinearGradient f_mask; if (d_orient == Qt::Horizontal) { const QRect cr(r.x(), r.y() + r.height() / 2 - d_grooveWidth / 2, r.width(), d_grooveWidth); QPainterPath clip_path; clip_path.addRoundedRect(cr.x() + d_thumbHalf, cr.y(), cr.width() - d_thumbLength, r.height(), d_radius, d_radius); p->setClipPath(clip_path); // // Draw background // const int dist1 = int(double(cr.width() - (d_fillThumb ? d_thumbLength : d_thumbHalf)) * rpos); const int ipos = cr.x() + dist1; markerPos = ipos + d_thumbHalf; // // Draw groove empty right side // if(d_fillEmptySide) { QPainterPath e_rect = MusECore::roundedPath(ipos + (d_fillThumb ? d_thumbLength : d_thumbHalf), cr.y(), cr.width() - (d_fillThumb ? d_thumbLength : d_thumbHalf) - dist1, cr.height(), d_radius, d_radius, (MusECore::Corner) (MusECore::CornerUpperRight | MusECore::CornerLowerRight) ); p->fillPath(e_rect, f_mask_min); } // // Draw groove full left side // f_mask.setColorAt(0, f_mask_min); f_mask.setColorAt(1, f_mask_max); f_mask.setStart(QPointF(cr.x(), cr.y())); f_mask.setFinalStop(QPointF(cr.x() + ipos + (d_fillThumb ? 0 : d_thumbHalf), cr.y())); QPainterPath f_rect = MusECore::roundedPath(cr.x(), cr.y(), ipos + (d_fillThumb ? 0 : d_thumbHalf), cr.height(), d_radius, d_radius, (MusECore::Corner) (MusECore::CornerLowerLeft | MusECore::CornerUpperLeft) ); p->fillPath(f_rect, QBrush(f_mask)); p->setClipping(false); } else // (d_orient == Qt::Vertical) { const QRect cr(r.x() + r.width() / 2 - d_grooveWidth / 2, r.y(), d_grooveWidth, r.height()); QPainterPath clip_path; clip_path.addRoundedRect(cr.x(), cr.y() + d_thumbHalf, cr.width(), r.height() - d_thumbLength, d_radius, d_radius); p->setClipPath(clip_path); // // Draw background // const int dist1 = int(double(cr.height() - (d_fillThumb ? d_thumbLength : d_thumbHalf)) * (1.0 - rpos)); const int ipos = cr.y() + dist1; markerPos = ipos + d_thumbHalf; // // Draw groove empty upper filling // if(d_fillEmptySide) { QPainterPath e_rect = MusECore::roundedPath(cr.x(), cr.y(), cr.width(), ipos + (d_fillThumb ? 0 : d_thumbHalf), d_radius, d_radius, (MusECore::Corner) (MusECore::CornerUpperLeft | MusECore::CornerUpperRight) ); p->fillPath(e_rect, QBrush(f_mask_min)); } // // Draw groove lower filling mask // f_mask.setColorAt(0, f_mask_max); f_mask.setColorAt(1, f_mask_min); f_mask.setStart(QPointF(cr.x(), markerPos)); f_mask.setFinalStop(QPointF(cr.x(), cr.y() + cr.height())); QPainterPath f_rect = MusECore::roundedPath(cr.x(), ipos + (d_fillThumb ? d_thumbLength : d_thumbHalf), cr.width(), cr.height() - (d_fillThumb ? d_thumbLength : d_thumbHalf) - dist1, d_radius, d_radius, (MusECore::Corner) (MusECore::CornerLowerLeft | MusECore::CornerLowerRight) ); p->fillPath(f_rect, QBrush(f_mask)); p->setClipping(false); if (d_frame) { p->setPen(d_frameColor); p->drawPath(clip_path); } } } //------------------------------------------------------------ //.- //.F Slider::getValue // Determine the value corresponding to a specified // mouse location. // //.u Syntax //.f double Slider::getValue(const QPoint &p) // //.u Parameters //.p const QPoint &p -- // //.u Description // Called by SliderBase //------------------------------------------------------------ double Slider::getValue( const QPoint &p) { double rv; const QRect r = d_sliderRect; const double val = value(ConvertNone); if(borderlessMouse() && d_scrollMode != ScrDirect) { DEBUG_SLIDER(stderr, "Slider::getValue value:%.20f p x:%d y:%d step:%.20f x change:%.20f\n", val, p.x(), p.y(), step(), p.x() * step()); if(d_orient == Qt::Horizontal) return val + p.x() * step(); else return val - p.y() * step(); } const double min = minValue(ConvertNone); const double max = maxValue(ConvertNone); const double drange = max - min; if(d_orient == Qt::Horizontal) { if(r.width() <= d_thumbLength) rv = 0.5 * (min + max); else { const double dpos = double(p.x() - r.x() - d_thumbHalf); const double dwidth = double(r.width() - d_thumbLength); rv = min + rint(drange * dpos / dwidth / step()) * step(); } } else { if(r.height() <= d_thumbLength) rv = 0.5 * (min + max); else { const double dpos = double(p.y() - r.y() - d_thumbHalf); double dheight = double(r.height() - d_thumbLength); rv = min + rint(drange * (1.0 - dpos / dheight) / step()) * step(); } } return(rv); } //------------------------------------------------------------ // //.F Slider::moveValue // Determine the value corresponding to a specified mouse movement. // //.u Syntax //.f void Slider::moveValue(const QPoint &deltaP, bool fineMode) // //.u Parameters //.p const QPoint &deltaP -- Change in position //.p bool fineMode -- Fine mode if true, coarse mode if false. // //.u Description // Called by SliderBase // Coarse mode (the normal mode) maps pixels to values depending on range and width, // such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. //------------------------------------------------------------ double Slider::moveValue(const QPoint &deltaP, bool fineMode) { double rv; const QRect r = d_sliderRect; const double val = value(ConvertNone); if((fineMode || borderlessMouse()) && d_scrollMode != ScrDirect) { DEBUG_SLIDER(stderr, "Slider::moveValue value:%.20f p x:%d y:%d step:%.20f x change:%.20f\n", val, deltaP.x(), deltaP.y(), step(), deltaP.x() * step()); double newval; if(d_orient == Qt::Horizontal) newval = val + deltaP.x() * step(); else newval = val - deltaP.y() * step(); d_valAccum = newval; // Reset. return newval; } const double min = minValue(ConvertNone); const double max = maxValue(ConvertNone); const double drange = max - min; if(d_orient == Qt::Horizontal) { if(r.width() <= d_thumbLength) rv = 0.5 * (min + max); else { const double dpos = double(deltaP.x()); const double dwidth = double(r.width() - d_thumbLength); const double dval_diff = (drange * dpos) / dwidth; d_valAccum += dval_diff; rv = rint(d_valAccum / step()) * step(); DEBUG_SLIDER(stderr, "Slider::moveValue Horizontal value:%.20f p dx:%d dy:%d drange:%.20f step:%.20f dval_diff:%.20f d_valAccum:%.20f rv:%.20f\n", val, deltaP.x(), deltaP.y(), drange, step(), dval_diff, d_valAccum, rv); } } else { if(r.height() <= d_thumbLength) rv = 0.5 * (min + max); else { const double dpos = double(-deltaP.y()); const double dheight = double(r.height() - d_thumbLength); const double dval_diff = (drange * dpos) / dheight; d_valAccum += dval_diff; rv = rint(d_valAccum / step()) * step(); DEBUG_SLIDER(stderr, "Slider::moveValue Vertical value:%.20f p dx:%d dy:%d drange:%.20f step:%.20f dval_diff:%.20f d_valAccum:%.20f rv:%.20f\n", val, deltaP.x(), deltaP.y(), drange, step(), dval_diff, d_valAccum, rv); } } return(rv); } //------------------------------------------------------------ //.- //.F Slider::getScrollMode // Determine scrolling mode and direction // //.u Syntax //.f void Slider::getScrollMode( const QPoint &p, int &scrollMode, int &direction ) // //.u Parameters //.p const QPoint &p -- point // //.u Description // Called by SliderBase // //------------------------------------------------------------ void Slider::getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction ) { // If modifier or button is held, jump directly to the position at first. // After handling it, the caller can change to SrcMouse scroll mode. if(modifiers & Qt::ControlModifier || button == Qt::MidButton) { scrollMode = ScrDirect; direction = 0; return; } if(borderlessMouse()) { if(button != Qt::NoButton && d_sliderRect.contains(p)) { scrollMode = ScrMouse; direction = 0; return; } } else { if(cursorHoming() && button == Qt::LeftButton) { if(d_sliderRect.contains(p)) { DEBUG_SLIDER(stderr, "Slider::getScrollMode cursor homing + left button: ScrMouse\n"); scrollMode = ScrMouse; direction = 0; int mp = 0; QRect cr; QPoint cp; int ipos,dist1; double rpos; cr = d_sliderRect; rpos = (value(ConvertNone) - minValue(ConvertNone)) / (maxValue(ConvertNone) - minValue(ConvertNone)); if(d_orient == Qt::Horizontal) { dist1 = int(double(cr.width() - d_thumbLength) * rpos); ipos = cr.x() + dist1; mp = ipos + d_thumbHalf; p.setX(mp); cp = mapToGlobal( QPoint(mp, p.y()) ); } else { dist1 = int(double(cr.height() - d_thumbLength) * (1.0 - rpos)); ipos = cr.y() + dist1; mp = ipos + d_thumbHalf; p.setY(mp); cp = mapToGlobal( QPoint(p.x(), mp) ); } cursor().setPos(cp.x(), cp.y()); return; } } else { int currentPos; if(d_orient == Qt::Horizontal) currentPos = p.x(); else currentPos = p.y(); if(d_sliderRect.contains(p)) { if((currentPos > markerPos - d_thumbHalf) && (currentPos < markerPos + d_thumbHalf)) { DEBUG_SLIDER(stderr, "Slider::getScrollMode ScrMouse\n"); scrollMode = ScrMouse; direction = 0; return; } else if(pagingButtons().testFlag(button)) { DEBUG_SLIDER(stderr, "Slider::getScrollMode ScrPage\n"); scrollMode = ScrPage; if (((currentPos > markerPos) && (d_orient == Qt::Horizontal)) || ((currentPos <= markerPos) && (d_orient != Qt::Horizontal))) direction = 1; else direction = -1; return; } } } } scrollMode = ScrNone; direction = 0; } //------------------------------------------------------------ //.F Slider::paintEvent // Qt paint event // //.u Syntax //.f void Slider::paintEvent(QPaintEvent *e) //------------------------------------------------------------ void Slider::paintEvent(QPaintEvent* /*ev*/) { QPainter p(this); if(d_grooveWidth != 0) drawSlider(&p, d_sliderRect); if(d_thumbLength != 0) drawThumb(&p, d_sliderRect); if(d_scalePos != None) { // p.fillRect(rect(), palette().window()); p.setRenderHint(QPainter::Antialiasing, false); d_scale.draw(&p, palette(), value()); } } void Slider::adjustSize(const QSize& s) { const QFontMetrics fm = fontMetrics(); const int sliderWidth = d_thumbWidth; // reposition slider if(d_orient == Qt::Horizontal) { switch(d_scalePos) { case Top: d_sliderRect.setRect(this->rect().x() + d_xMargin, this->rect().y() + s.height() - 1 - d_yMargin - sliderWidth, s.width() - 2 * d_xMargin, sliderWidth); d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, d_sliderRect.y() - d_scaleDist, d_sliderRect.width() - d_thumbLength, ScaleDraw::Top); break; case Bottom: d_sliderRect.setRect(this->rect().x() + d_xMargin, this->rect().y() + d_yMargin, s.width() - 2*d_xMargin, sliderWidth); d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, d_sliderRect.y() + d_sliderRect.height() + d_scaleDist, d_sliderRect.width() - d_thumbLength, ScaleDraw::Bottom); break; case InsideHorizontal: d_sliderRect.setRect(this->rect().x() + d_xMargin, this->rect().y() + s.height() - 1 - d_yMargin - sliderWidth, s.width() - 2 * d_xMargin, sliderWidth); d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, //d_sliderRect.y() - d_scaleDist, this->rect().y() + d_yMargin + d_scale.maxHeight(fm) + d_scaleDist, d_sliderRect.width() - d_thumbLength, ScaleDraw::InsideHorizontal); break; default: d_sliderRect.setRect(this->rect().x(), this->rect().x(), s.width(), s.height()); break; } } else // d_orient == Qt::Vertical { switch(d_scalePos) { case Left: d_sliderRect.setRect(this->rect().x() + s.width() - sliderWidth - 1 - d_xMargin, this->rect().y() + d_yMargin, sliderWidth, s.height() - 2 * d_yMargin); d_scale.setGeometry(d_sliderRect.x() - d_scaleDist, d_sliderRect.y() + d_thumbHalf, s.height() - d_thumbLength, ScaleDraw::Left); break; case Right: { d_sliderRect.setRect(this->rect().x() + d_xMargin, this->rect().y() + d_yMargin, sliderWidth, s.height() - 2* d_yMargin); d_scale.setGeometry(this->rect().x() + d_sliderRect.width() + d_scaleDist, d_sliderRect.y() + d_thumbHalf, s.height() - d_thumbLength, ScaleDraw::Right); break; } case InsideVertical: { // d_sliderRect.setRect(this->rect().x() + s.width() // - sliderWidth - 1 - d_xMargin, // d_sliderRect.setRect(this->rect().x() + d_xMargin, // d_sliderRect.setRect(this->rect().x() + d_xMargin + d_scale.maxLabelWidth(fm, false) - sliderWidth, const int mxlw = d_scale.maxLabelWidth(fm, false); const int sclw = d_scale.scaleWidth(); const int sldw = mxlw > sliderWidth ? sliderWidth : mxlw; const int sldoffs = mxlw > sliderWidth ? ((mxlw - sldw) / 2) : 0; const int fh = fm.ascent() + 2; const int fh2 = fh / 2; const int margin = d_thumbLength > fh ? d_thumbLength : fh; const int margin2 = d_thumbHalf > fh2 ? d_thumbHalf : fh2; const int sldymargin = fh > d_thumbLength ? fh - d_thumbLength : 0; const int sldymargin2 = fh2 > d_thumbHalf ? fh2 - d_thumbHalf : 0; // d_sliderRect.setRect(this->rect().x() + (s.width() - 1) - sliderWidth - sclw + sldoffs, // - d_xMargin, d_sliderRect.setRect(this->rect().x() + s.width() - sliderWidth - sclw + sldoffs, // - d_xMargin, // this->rect().y() + d_yMargin, this->rect().y() + d_yMargin + sldymargin2, sliderWidth, // s.height() - 2 * d_yMargin); // s.height() - margin - 2 * d_yMargin); s.height() - sldymargin - 2 * d_yMargin); //d_scale.setGeometry(d_sliderRect.x() - d_scaleDist, // d_scale.setGeometry(this->rect().x() + d_xMargin + d_scale.maxWidth(fm, false) + d_scaleDist, d_scale.setGeometry(this->rect().x() + d_xMargin + mxlw + sclw + d_scaleDist, // d_sliderRect.y() + d_thumbHalf, // d_sliderRect.y(), this->rect().y() + d_yMargin + margin2, // s.height() - d_thumbLength, // s.height() - margin, // d_sliderRect.height(), s.height() - margin - 2 * d_yMargin, ScaleDraw::InsideVertical); } break; default: d_sliderRect.setRect(this->rect().x(), this->rect().x(), s.width(), s.height()); break; } } adjustScale(); } void Slider::adjustScale() { const double range = maxValue() - minValue(); if(range == 0.0) return; int maxMaj = 5; int maxMin = 3; double mstep = scaleStep(); QFontMetrics fm = fontMetrics(); if(d_orient == Qt::Horizontal) { // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int unit_w = fm.horizontalAdvance("888.8888"); #else int unit_w = fm.width("888.8888"); #endif if(unit_w == 0) unit_w = 20; if(hasUserScale()) { if(d_sliderRect.width() != 0) { const int fact = (int)(3.0 * range / (double)(d_sliderRect.width())) + 1; mstep *= fact; } } else { maxMaj = (int)((double)(d_sliderRect.width()) / (1.5 * ((double)unit_w))); if(maxMaj < 1) maxMaj = 1; if(maxMaj > 5) maxMaj = 5; } maxMin = (int)((double)(d_sliderRect.width()) / (1.5 * ((double)unit_w))); if(maxMin < 1) maxMin = 1; if(maxMin > 5) maxMin = 5; } else { int unit_h = fm.height(); if(unit_h == 0) unit_h = 20; if(hasUserScale()) { if(d_sliderRect.height() != 0) { const int fact = (int)(3.0 * range / (double)(d_sliderRect.height())) + 1; mstep *= fact; } } else { maxMaj = (int)((double)(d_sliderRect.height()) / (1.5 * ((double)unit_h))); if(maxMaj < 1) maxMaj = 1; if(maxMaj > 5) maxMaj = 5; } maxMin = (int)((double)(d_sliderRect.height()) / (1.5 * ((double)unit_h))); if(maxMin < 1) maxMin = 1; if(maxMin > 5) maxMin = 5; } DEBUG_SLIDER(stderr, "Slider::adjustScale: maxMaj:%d maxMin:%d scaleStep:%f\n", maxMaj, maxMin, mstep); d_maxMajor = maxMaj; d_maxMinor = maxMin; if(hasUserScale()) d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor, mstep, log()); else d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor, log()); // update(); updateGeometry(); } //------------------------------------------------------------ //.F Slider::resizeEvent // Qt resize event // //.u Parameters //.p QResizeEvent *e // //.u Syntax //.f void Slider::resizeEvent(QResizeEvent *e) //------------------------------------------------------------ void Slider::resizeEvent(QResizeEvent *e) { SliderBase::resizeEvent(e); adjustSize(e->size()); } void Slider::setScale(double vmin, double vmax, int logarithmic) { ScaleIf::setScale(vmin, vmax, logarithmic); // Must adjust the scale. adjustScale(); } void Slider::setScale(double vmin, double vmax, double step, int logarithmic) { ScaleIf::setScale(vmin, vmax, step, logarithmic); // Must adjust the scale. adjustScale(); } void Slider::setScale(const ScaleDiv &s) { ScaleIf::setScale(s); // Must adjust the scale. adjustScale(); } void Slider::setScaleMaxMajor(int ticks) { ScaleIf::setScaleMaxMajor(ticks); // Must adjust the scale. adjustScale(); } void Slider::setScaleMaxMinor(int ticks) { ScaleIf::setScaleMaxMinor(ticks); // Must adjust the scale. adjustScale(); } void Slider::setScaleBackBone(bool v) { ScaleIf::setScaleBackBone(v); // Must adjust the scale. adjustScale(); } //------------------------------------------------------------ //.- //.F Slider::valueChange // Notify change of value // //.u Syntax //.f void Slider::valueChange() // //------------------------------------------------------------ void Slider::valueChange() { update(); // HACK // In direct mode let the inherited classes (this) call these in their valueChange() methods, // so that they may be called BEFORE valueChanged signal is emitted by the setPosition() call above. // ScrDirect mode only happens once upon press with a modifier. After that, another mode is set. // Hack: Since valueChange() is NOT called if nothing changed, in that case these are called for us by the SliderBase. if(d_scrollMode == ScrDirect) { processSliderPressed(id()); emit sliderPressed(value(), id()); } // Emits valueChanged if tracking enabled. SliderBase::valueChange(); } //------------------------------------------------------------ //.- //.F Slider::rangeChange // Notify change of range // //.u Description // //.u Syntax //.f void Slider::rangeChange() // //------------------------------------------------------------ void Slider::rangeChange() { if (!hasUserScale()) d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); SliderBase::rangeChange(); // repaint(); update(); } //------------------------------------------------------------ // //.F Slider::setMargins // Set distances between the widget's border and // internals. // //.u Syntax //.f void Slider::setMargins(int hor, int vert) // //.u Parameters //.p int hor, int vert -- Margins // //------------------------------------------------------------ void Slider::setMargins(int hor, int vert) { d_xMargin = MusECore::qwtMax(0, hor); d_yMargin = MusECore::qwtMax(0, vert); resize(this->size()); } //------------------------------------------------------------ // //.F Slider::sizeHint // Return a recommended size // //.u Syntax //.f QSize Slider::sizeHint() const // //.u Note // The return value of sizeHint() depends on the font and the // scale. //------------------------------------------------------------ QSize Slider::sizeHint() const { int w = 40; int h = 40; const QFontMetrics fm = fontMetrics(); int msWidth = 0, msHeight = 0; if (d_scalePos != None) { msWidth = d_scale.maxWidth(fm, false); msHeight = d_scale.maxHeight(fm); switch(d_orient) { case Qt::Vertical: { h = vertical_hint; const int smw = msWidth + d_scaleDist; switch(d_scalePos) { case Left: case Right: w = 2*d_xMargin + d_thumbWidth + smw + 2; break; case InsideVertical: { const int aw = smw > d_thumbWidth ? smw : d_thumbWidth; w = 2*d_xMargin + aw + 2; } break; case Top: case Bottom: case InsideHorizontal: case None: break; } } break; case Qt::Horizontal: { w = horizontal_hint; const int smh = msHeight + d_scaleDist; switch(d_scalePos) { case Top: case Bottom: h = 2*d_yMargin + d_thumbWidth + smh; break; case InsideHorizontal: { const int ah = smh > d_thumbWidth ? smh : d_thumbWidth; h = 2*d_yMargin + ah; } break; case Left: case Right: case InsideVertical: case None: break; } } break; } } else { // no scale switch(d_orient) { case Qt::Vertical: w = 16; h = vertical_hint; break; case Qt::Horizontal: h = 16; w = horizontal_hint; break; } } return QSize(w, h); } //--------------------------------------------------------- // setOrientation //--------------------------------------------------------- void Slider::setOrientation(Qt::Orientation o) { d_orient = o; ScaleDraw::OrientationX so = ScaleDraw::Bottom; switch(d_orient) { case Qt::Vertical: switch(d_scalePos) { case Right: so = ScaleDraw::Right; break; case Left: so = ScaleDraw::Left; break; case InsideVertical: so = ScaleDraw::InsideVertical; break; case Bottom: case Top: case InsideHorizontal: case None: break; } break; case Qt::Horizontal: switch(d_scalePos) { case Bottom: so = ScaleDraw::Bottom; break; case Top: so = ScaleDraw::Top; break; case InsideHorizontal: so = ScaleDraw::InsideHorizontal; break; case Right: case Left: case InsideVertical: case None: break; } break; } d_scale.setGeometry(0, 0, 40, so); if (d_orient == Qt::Vertical) { setMinimumSize(10,20); } else { setMinimumSize(20,10); } QRect r = geometry(); setGeometry(r.x(), r.y(), r.height(), r.width()); update(); } Qt::Orientation Slider::orientation() const { return d_orient; } int Slider::scaleEndpointsMargin() const { const QFontMetrics fm = fontMetrics(); const int fh = fm.ascent() + 2; const int fh2 = fh / 2; const int margin2 = d_thumbHalf > fh2 ? d_thumbHalf : fh2; return d_yMargin + margin2; } double Slider::lineStep() const { return 1.0; } double Slider::pageStep() const { return 1.0; } void Slider::setLineStep(double) { } void Slider::setPageStep(double) { } } // namespace MusEGui muse-4.0.0/src/muse/components/slider.h000066400000000000000000000132241404105311000200330ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: slider.h,v 1.3.2.2 2008/01/19 13:33:47 wschweer Exp $ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) // (C) Copyright 2015-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SLIDER_H__ #define __SLIDER_H__ #include #include #include #include #include #include #include #include #include #include #include #include "sclif.h" #include "sliderbase.h" #include "scldraw.h" namespace MusEGui { //--------------------------------------------------------- // Slider //--------------------------------------------------------- class Slider : public SliderBase, public ScaleIf { Q_OBJECT Q_PROPERTY( double lineStep READ lineStep WRITE setLineStep ) Q_PROPERTY( double pageStep READ pageStep WRITE setPageStep ) Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation ) public: enum ScalePos { None, Left, Right, Top, Bottom, InsideHorizontal, InsideVertical }; private: Qt::Orientation d_orient; ScalePos d_scalePos; int d_grooveWidth; QColor d_fillColor; QColor d_handleColor; bool d_fillThumb; bool d_fillEmptySide; bool d_frame; QColor d_frameColor; int d_radius; int d_radiusHandle; bool d_useGradient; QRect d_sliderRect; int d_thumbLength; int d_thumbHalf; int d_thumbWidth; int d_scaleDist; int d_xMargin; int d_yMargin; int d_mMargin; bool d_autoResize; double d_scaleStep; int d_bgStyle; int markerPos; uint vertical_hint; uint horizontal_hint; void drawHsBgSlot(QPainter *, const QRect&, const QRect&,const QBrush&); void drawVsBgSlot(QPainter *, const QRect&, const QRect&,const QBrush&); protected: virtual void drawThumb (QPainter *p, const QRect &r); virtual void drawSlider (QPainter *p, const QRect &r); // Determine the value corresponding to a specified mouse location. // If borderless mouse is enabled p is a delta value not absolute, so can be negative. double getValue(const QPoint &p); // Determine the value corresponding to a specified mouse movement. double moveValue(const QPoint& /*deltaP*/, bool /*fineMode*/ = false); // Determine scrolling mode and direction. void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction); // Setup all slider and scale rectangles. void adjustSize(const QSize& s); // Adjust scale so marks are not too close together. void adjustScale(); virtual void resizeEvent(QResizeEvent *e); virtual void paintEvent (QPaintEvent *e); void valueChange(); void rangeChange(); void scaleChange(); void fontChange(const QFont &oldFont); public: Slider(QWidget *parent, const char *name = 0, Qt::Orientation orient = Qt::Vertical, ScalePos scalePos = None, int grooveWidth = 8, QColor fillColor = QColor(), ScaleDraw::TextHighlightMode textHighlightMode = ScaleDraw::TextHighlightNone, QColor handleColor = QColor()); ~Slider(); void setThumbLength(int l); void setThumbWidth(int w); void setOrientation(Qt::Orientation o); Qt::Orientation orientation() const; int scaleEndpointsMargin() const; void setScale (double vmin, double vmax, int logarithmic = 0); void setScale (double vmin, double vmax, double step, int logarithmic = 0); void setScale(const ScaleDiv &s); void setScaleMaxMajor( int ticks); void setScaleMaxMinor( int ticks); void setScaleBackBone(bool v); double lineStep() const; double pageStep() const; void setLineStep(double); void setPageStep(double); void setMargins(int x, int y); int grooveWidth() const { return d_grooveWidth; } void setGrooveWidth(int w) { d_grooveWidth = w; } // QColor fillColor() const { return d_fillColor; } void setFillColor(const QColor& color) { d_fillColor = color; update(); } void setHandleColor(const QColor& color) { d_handleColor = color; update(); } bool fillThumb() const { return d_fillThumb; } void setFillThumb(bool v) { d_fillThumb = v; } bool fillEmptySide() const { return d_fillEmptySide; } void setFillEmptySide(bool v) { d_fillEmptySide = v; } virtual QSize sizeHint() const; void setSizeHint(uint w, uint h); void setRadius(int r) { d_radius = r; } void setRadiusHandle(int r) { d_radiusHandle = r; } void setHandleHeight(int h) { d_thumbLength = h; } void setHandleWidth(int w) { d_thumbWidth = w; d_thumbHalf = d_thumbLength / 2; } void setUseGradient(bool b) { d_useGradient = b; } void setScalePos(ScalePos s) { d_scalePos = s; } void setFrame(bool b) { d_frame = b; } void setFrameColor(QColor c) { d_frameColor = c; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/snooper.cpp000066400000000000000000000550051404105311000205740ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // snooper.cpp // (C) Copyright 2019 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "snooper.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_SNOOPER(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { // 200ms timer interval. const int SnooperDialog::_updateTimerInterval = 200; // 1 seconds. const int SnooperDialog::_autoHideTimerInterval = 1000; QMap SnooperDialog::_eventTypeMap; void SnooperTreeWidgetItem::init() { _isParentedTopLevelBranch = false; _isWindowBranch = false; _flashCounter = 0; _isFlashing = false; _origBackground = background(Name); switch(type()) { case ObjectItem: if(_object) { const QMetaObject* mo = _object->metaObject(); const QString cls_name = QString::fromLatin1(mo->className()); const QString obj_name = _object->objectName(); setText(Name, cls_name + QStringLiteral("::") + obj_name); } break; case PropertiesItem: setText(Name, QObject::tr("")); break; case PropertyItem: if(_object) { const QMetaObject* mo = _object->metaObject(); const int prop_count = mo->propertyCount(); if(_metaPropertyIndex < prop_count) { const QMetaProperty prop = mo->property(_metaPropertyIndex); const int prop_offset = mo->propertyOffset(); const QString prop_str = (_metaPropertyIndex < prop_offset) ? QObject::tr("") : QObject::tr(""); setText(Name, prop_str); setText(Property, QString::fromLatin1(prop.name())); setText(PropertyType, QString::fromLatin1(prop.typeName())); setText(PropertyValue, prop.read(_object).toString()); } } break; default: break; } } void SnooperTreeWidgetItem::startFlash(int interval, const QColor& color, const QEvent::Type& eventType) { _flashCounter = interval; _isFlashing = true; setBackground(Name, color); if(eventType != QEvent::None) { const QString key = SnooperDialog::eventTypeString(eventType); setText(EventType, QString("<%1>: ").arg(eventType) + key); } } void SnooperTreeWidgetItem::resetFlash() { _isFlashing = false; setBackground(Name, _origBackground); setText(EventType, QString()); } bool SnooperTreeWidgetItem::tickFlash() { if(_flashCounter <= 0) return false; if(--_flashCounter > 0) return false; resetFlash(); return true; } SnooperDialog::SnooperDialog(QWidget* parent) : QDialog(parent, Qt::Window) { setupUi(this); //setAttribute(Qt::WA_DeleteOnClose); setObjectName(QStringLiteral("snooper dialog")); _captureMouseClicks = captureMouseClickCheckBox->isChecked(); _captureKeyPress = captureKeyPressCheckBox->isChecked(); _autoHideIntervalCounter = 0; _flashColor = QColor(255, 170, 128); const QMetaObject mo = QEvent::staticMetaObject; const int type_idx = mo.indexOfEnumerator("Type"); if(type_idx >= 0) { const QMetaEnum meta_enum = mo.enumerator(type_idx); const int key_count = meta_enum.keyCount(); for(int k = 0; k < key_count; ++k) _eventTypeMap.insert(meta_enum.value(k), meta_enum.key(k)); } connect(updateButton, &QPushButton::clicked, [this]() { updateTreeClicked(); } ); connect(autoHideCheckBox, &QCheckBox::toggled, [this](bool v) { filterToggled(v); } ); connect(onlyAppCheckBox, &QCheckBox::toggled, [this](bool v) { filterToggled(v); } ); connect(onlyWidgetCheckBox, &QCheckBox::toggled, [this](bool v) { filterToggled(v); } ); connect(onlyPropsCheckBox, &QCheckBox::toggled, [this](bool v) { filterToggled(v); } ); connect(separateParentedTopLevelsCheckBox, &QCheckBox::toggled, [this](bool v) { filterToggled(v); } ); connect(classNameLineEdit, &QLineEdit::editingFinished, [this]() { finishedLineEditing(); } ); connect(objectNameLineEdit, &QLineEdit::editingFinished, [this]() { finishedLineEditing(); } ); connect(captureMouseClickCheckBox, &QCheckBox::toggled, [this](bool v) { captureMouseClickToggled(v); } ); connect(captureKeyPressCheckBox, &QCheckBox::toggled, [this](bool v) { captureKeyPressToggled(v); } ); setFlashInterval(flashTimerValueSpinBox->value()); flashTimerValueSpinBox->setEnabled(useFlashCheckBox->isChecked()); resetFlashButton->setEnabled(!useFlashCheckBox->isChecked()); // Special for this: Need qt helper overload for these lambdas. connect(flashTimerValueSpinBox, QOverload::of(&QSpinBox::valueChanged), [=](int v) { setFlashInterval(v); } ); connect(useFlashCheckBox, &QCheckBox::toggled, [this](bool v) { useFlashTimerToggled(v); } ); connect(resetFlashButton, &QToolButton::clicked, [this]() { resetFlashTimerClicked(); } ); _updateTimer = new QTimer(this); connect(_updateTimer, &QTimer::timeout, [this]() { updateTimerTick(); } ); _updateTimer->start(_updateTimerInterval); } SnooperDialog::~SnooperDialog() { DEBUG_SNOOPER(stderr, "SnooperDialog::dtor\n"); _updateTimer->stop(); disconnectAll(); } void SnooperDialog::disconnectAll() { DEBUG_SNOOPER(stderr, "SnooperDialog::disconnectAll():\n"); qApp->removeEventFilter(this); QObject* obj; QTreeWidgetItemIterator iObjTree(objectTree); while(*iObjTree) { SnooperTreeWidgetItem* item = static_cast(*iObjTree); obj = item->object(); const QMetaObject::Connection& conn = item->connection(); // NOTE QMetaObject::Connection has a boolean operator. if(conn && !disconnect(conn)) fprintf(stderr, "SnooperDialog::disconnectAll(): disconnected failed: obj:%p cls_name:%s obj_name:%s\n", obj, obj->metaObject()->className(), obj->objectName().toLatin1().constData()); ++iObjTree; } } bool SnooperDialog::eventFilter(QObject *obj, QEvent *event) { // Pass the event on to the parent class. const bool ret = QDialog::eventFilter(obj, event); const QEvent::Type event_type = event->type(); if(obj != this && isVisible() && !isHidden() && ((event_type == QEvent::MouseButtonPress && captureMouseClicks()) || (event_type == QEvent::KeyPress && captureKeyPress()))) { putEventBuffer(obj, event->type()); //selectObject(obj, event); // // Restart only if already at end. // if(_autoHideIntervalCounter <= 0) // _autoHideIntervalCounter = _autoHideTimerInterval / _updateTimerInterval; } return ret; } void SnooperDialog::putEventBuffer(QObject *obj, const QEvent::Type& eventType) { HitBuffer::iterator i = _eventBuffer.find(obj); if(i == _eventBuffer.end()) { HitMap hm; hm.insert(eventType, 1); _eventBuffer.insert(obj, hm); } else { HitMap& hm = *i; HitMap::iterator ihm = hm.find(eventType); if(ihm == hm.end()) hm.insert(eventType, 1); else ++ihm.value(); } } void SnooperDialog::showEvent(QShowEvent* e) { e->ignore(); if(!e->spontaneous()) { DEBUG_SNOOPER(stderr, "SnooperDialog::showEvent(): not spontaneous\n"); disconnectAll(); _flashingItems.clear(); objectTree->clear(); updateTree(); if(!_updateTimer->isActive()) _updateTimer->start(_updateTimerInterval); } QDialog::showEvent(e); } void SnooperDialog::closeEvent(QCloseEvent* e) { e->ignore(); DEBUG_SNOOPER(stderr, "SnooperDialog::closeEvent():\n"); _updateTimer->stop(); disconnectAll(); _flashingItems.clear(); objectTree->clear(); QDialog::closeEvent(e); } void SnooperDialog::hideEvent(QHideEvent* e) { e->ignore(); if(!e->spontaneous()) { DEBUG_SNOOPER(stderr, "SnooperDialog::hideEvent(): not spontaneous\n"); _updateTimer->stop(); disconnectAll(); _flashingItems.clear(); objectTree->clear(); } QDialog::hideEvent(e); } // Recursive! bool SnooperDialog::filterBranch(bool parentIsRelevant, QTreeWidgetItem* parentItem) { const QTreeWidgetItem* root_item = objectTree->invisibleRootItem(); const bool is_parent_root_item = parentItem == root_item; bool is_relevant = false; bool this_parent_is_relevant = false; if(is_parent_root_item) { is_relevant = true; } else { const SnooperTreeWidgetItem* parent_snoop_item = static_cast(parentItem); const QObject* object = parent_snoop_item->cobject(); const bool topLevels = separateParentedTopLevelsCheckBox->isChecked(); // Whether to separate parented top levels. if(parent_snoop_item->isWindowBranch() && parent_snoop_item->isParentedTopLevelBranch() != topLevels) { if(!parentItem->isHidden()) parentItem->setHidden(true); return false; } const QMetaObject* mo = object->metaObject(); const QString cls_name = QString::fromLatin1(mo->className()); const QString obj_name = object->objectName(); const bool onlyAppClasses = onlyAppCheckBox->isChecked(); const bool onlyWidgets = onlyWidgetCheckBox->isChecked(); const bool onlyProps = onlyPropsCheckBox->isChecked(); const QString search_class_name = classNameLineEdit->text(); const QString search_obj_name = objectNameLineEdit->text(); const int parent_item_type = parent_snoop_item->type(); const bool search_is_relevant = parentIsRelevant || ((search_class_name.isEmpty() || cls_name.contains(search_class_name)) && (search_obj_name.isEmpty() || obj_name.contains(search_obj_name))); //if(search_is_relevant && !search_class_name.isEmpty() && !search_obj_name.isEmpty()) if(search_is_relevant && (!search_class_name.isEmpty() || !search_obj_name.isEmpty())) this_parent_is_relevant = true; // Auto-hide takes precedence over any other filtering. if(autoHideCheckBox->isChecked()) { if(parent_snoop_item->isFlashing()) is_relevant = true; } else if((!onlyAppClasses || cls_name.startsWith(QStringLiteral("MusEGui::"))) && (!onlyWidgets || object->isWidgetType()) && (!onlyProps || parent_item_type == SnooperTreeWidgetItem::PropertiesItem || parent_item_type == SnooperTreeWidgetItem::PropertyItem) && search_is_relevant) { is_relevant = true; } } SnooperTreeWidgetItem* item; const int child_count = parentItem->childCount(); for(int i = 0; i < child_count; ++ i) { item = static_cast(parentItem->child(i)); if(filterBranch(parentIsRelevant || this_parent_is_relevant, item)) is_relevant = true; } const bool do_hide = !is_relevant && !parentIsRelevant && !this_parent_is_relevant && !is_parent_root_item; if(parentItem->isHidden() != do_hide) parentItem->setHidden(do_hide); return is_relevant; } // Recursive! bool SnooperDialog::addBranch(QObject* object, SnooperTreeWidgetItem* parentItem, bool isParentedTopLevelBranch, bool isWindowBranch) { // Do NOT add anything related to THIS dialog. Hard to manage destroyed signals from itself. if(object == this) return false; const QTreeWidgetItem* root_item = objectTree->invisibleRootItem(); SnooperTreeWidgetItem* item = nullptr; SnooperTreeWidgetItem* prop_parent_item = nullptr; SnooperTreeWidgetItem* prop_item = nullptr; const QMetaObject* mo = object->metaObject(); const QString cls_name = QString::fromLatin1(mo->className()); const QString obj_name = object->objectName(); const bool is_top_item = !parentItem || parentItem == root_item; const bool has_parent_obj = object->parent(); const bool is_widget_type = object->isWidgetType(); // Whether to separate parented top levels. if(has_parent_obj && is_widget_type) { const QWidget* widget = qobject_cast(object); if(widget->isWindow()) isWindowBranch = true; if(is_top_item) isParentedTopLevelBranch = true; } item = new SnooperTreeWidgetItem(SnooperTreeWidgetItem::ObjectItem, object); item->setIsParentedTopLevelBranch(isParentedTopLevelBranch); item->setIsWindowBranch(isWindowBranch); DEBUG_SNOOPER(stderr, "SnooperDialog::addBranch(): adding connection: obj:%p cls_name:%s obj_name:%s isParentedTopLevelBranch:%d isWindowBranch:%d\n", object, mo->className(), obj_name.toLatin1().constData(), isParentedTopLevelBranch, isWindowBranch); QMetaObject::Connection conn = connect(object, &QObject::destroyed, [this](QObject* o = nullptr) { objectDestroyed(o); } ); item->setConnection(conn); const bool show_base_props = true; const int prop_count = mo->propertyCount(); const int prop_offset = show_base_props ? 0 : mo->propertyOffset(); if(prop_offset < prop_count) { prop_parent_item = new SnooperTreeWidgetItem(SnooperTreeWidgetItem::PropertiesItem, object); prop_parent_item->setIsParentedTopLevelBranch(isParentedTopLevelBranch); prop_parent_item->setIsWindowBranch(isWindowBranch); for(int i = prop_offset; i < prop_count; ++i) { prop_item = new SnooperTreeWidgetItem(SnooperTreeWidgetItem::PropertyItem, object, i); prop_item->setIsParentedTopLevelBranch(isParentedTopLevelBranch); prop_item->setIsWindowBranch(isWindowBranch); prop_parent_item->addChild(prop_item); } item->addChild(prop_parent_item); } const QObjectList& ol = object->children(); foreach(QObject* obj, ol) addBranch(obj, item, isParentedTopLevelBranch, isWindowBranch); if(parentItem) parentItem->addChild(item); else objectTree->addTopLevelItem(item); return true; } void SnooperDialog::updateTree() { _updateTimer->stop(); disconnectAll(); _flashingItems.clear(); objectTree->clear(); // const QObjectList& cl = qApp->children(); // foreach(QObject* obj, cl) // addBranch(obj, nullptr); const QWidgetList wl = qApp->topLevelWidgets(); foreach(QWidget* obj, wl) addBranch(obj, nullptr, false, false); qApp->installEventFilter(this); filterItems(); objectTree->resizeColumnToContents(SnooperTreeWidgetItem::Name); _updateTimer->start(_updateTimerInterval); } // Recursive! bool SnooperDialog::destroyBranch(QObject *obj, QTreeWidgetItem* parentItem, bool deleteBranchPending) { bool delete_this_branch = false; if(parentItem != objectTree->invisibleRootItem()) { SnooperTreeWidgetItem* snoop_item = static_cast(parentItem); if(snoop_item->object() == obj) { // Mark this branch for deletion, and this item ONLY if it initiated the deletion. if(!deleteBranchPending) { deleteBranchPending = true; delete_this_branch = true; } } // Remove the item from the flashing list, if it's there. if(deleteBranchPending) _flashingItems.remove(snoop_item); } // Do it in reverse! const int sz = parentItem->childCount(); for(int i = sz - 1; i >= 0; --i) destroyBranch(obj, parentItem->child(i), deleteBranchPending); if(delete_this_branch) delete parentItem; return true; } void SnooperDialog::objectDestroyed(QObject *obj) { DEBUG_SNOOPER(stderr, "SnooperDialog::objectDestroyed(): obj:%p\n", obj); if(!isVisible()) fprintf(stderr, "SnooperDialog::objectDestroyed(): Got objectDestroyed while Snooper is not visible! obj:%p\n", obj); // Enter the 'root branch'. destroyBranch(obj, objectTree->invisibleRootItem(), false); } // Recursive! QTreeWidgetItem* SnooperDialog::findItem(const QObject *obj, QTreeWidgetItem* parentItem, bool noHidden, bool parentedTopLevels) { if(noHidden && parentItem->isHidden()) return nullptr; if(parentItem != objectTree->invisibleRootItem()) { const SnooperTreeWidgetItem* snoop_item = static_cast(parentItem); if(snoop_item->cobject() == obj && (!snoop_item->isWindowBranch() || snoop_item->isParentedTopLevelBranch() == parentedTopLevels)) return parentItem; } QTreeWidgetItem *item; const int sz = parentItem->childCount(); for(int i = 0; i < sz; ++i) { item = findItem(obj, parentItem->child(i), noHidden, parentedTopLevels); if(item) return item; } return nullptr; } // Recursive! const QTreeWidgetItem* SnooperDialog::cfindItem(const QObject *obj, const QTreeWidgetItem* parentItem, bool noHidden, bool parentedTopLevels) const { if(noHidden && parentItem->isHidden()) return nullptr; if(parentItem != objectTree->invisibleRootItem()) { const SnooperTreeWidgetItem* snoop_item = static_cast(parentItem); if(snoop_item->cobject() == obj && //snoop_item->isParentedTopLevelBranch() == parentedTopLevels) (!snoop_item->isWindowBranch() || snoop_item->isParentedTopLevelBranch() == parentedTopLevels)) return parentItem; } const QTreeWidgetItem *item; const int sz = parentItem->childCount(); for(int i = 0; i < sz; ++i) { item = cfindItem(obj, parentItem->child(i), noHidden, parentedTopLevels); if(item) return item; } return nullptr; } QTreeWidgetItem* SnooperDialog::findObject(const QObject* obj, bool noHidden, bool parentedTopLevels) { // Enter the 'root branch'. return findItem(obj, objectTree->invisibleRootItem(), noHidden, parentedTopLevels); } const QTreeWidgetItem* SnooperDialog::cfindObject(const QObject* obj, bool noHidden, bool parentedTopLevels) const { // Enter the 'root branch'. return cfindItem(obj, objectTree->invisibleRootItem(), noHidden, parentedTopLevels); } void SnooperDialog::filterItems() { // Enter the 'root branch'. // Top relevant is false here to keep the routine happy. const bool this_parent_is_relevant = false; filterBranch(this_parent_is_relevant, objectTree->invisibleRootItem()); } void SnooperDialog::updateTreeClicked() { updateTree(); } void SnooperDialog::filterToggled(bool) { filterItems(); objectTree->resizeColumnToContents(SnooperTreeWidgetItem::Name); } void SnooperDialog::useFlashTimerToggled(bool v) { flashTimerValueSpinBox->setEnabled(v); resetFlashButton->setEnabled(!v); } void SnooperDialog::finishedLineEditing() { filterItems(); objectTree->resizeColumnToContents(SnooperTreeWidgetItem::Name); } void SnooperDialog::captureMouseClickToggled(bool v) { _captureMouseClicks = v; } void SnooperDialog::captureKeyPressToggled(bool v) { _captureKeyPress = v; } void SnooperDialog::resetFlashTimerClicked() { if(_flashingItems.isEmpty()) return; for(QSet::iterator i = _flashingItems.begin(); i != _flashingItems.end(); ++i) (*i)->resetFlash(); _flashingItems.clear(); } SnooperTreeWidgetItem* SnooperDialog::selectObject(const QObject *obj, const QEvent::Type& eventType) { // if((eventType != QEvent::MouseButtonPress || !captureMouseClicks()) && // (eventType != QEvent::KeyPress || !captureKeyPress())) // return; QTreeWidgetItem* item = findObject(obj, !autoHideCheckBox->isChecked(), separateParentedTopLevelsCheckBox->isChecked()); if(!item) { // Careful, crash might happen here. Object might not exist? //fprintf(stderr, "SnooperDialog::selectObject() Did not find class name:%s object name:%s\n", // obj->metaObject()->className(), obj->objectName().toLatin1().constData()); //fprintf(stderr, "SnooperDialog::selectObject() Did not find object:%p\n", obj); return nullptr; } SnooperTreeWidgetItem* snoop_item = static_cast(item); //_autoHideIntervalCounter = _autoHideTimerInterval / _updateTimerInterval; snoop_item->startFlash(_flashInterval, _flashColor, eventType); _flashingItems.insert(snoop_item); return snoop_item; } SnooperTreeWidgetItem* SnooperDialog::processEventBuffer() { if(_eventBuffer.isEmpty()) return nullptr; // Suppose we should warn if things are getting busy. if(_eventBuffer.size() >= 32768) fprintf(stderr, "SnooperDialog::processEventBuffer(): Warning: Event buffer size >= 32768. Quite busy?\n"); SnooperTreeWidgetItem* first_item = nullptr; SnooperTreeWidgetItem* item; QObject *obj; for(HitBuffer::iterator i = _eventBuffer.begin(); i != _eventBuffer.end(); ++i) { obj = i.key(); HitMap& hm = *i; for(HitMap::iterator k = hm.begin(); k != hm.end(); ++k) { const QEvent::Type& event_type = k.key(); item = selectObject(obj, event_type); if(item && !first_item) first_item = item; } } // Done with buffer. Clear it. _eventBuffer.clear(); return first_item; } void SnooperDialog::updateTimerTick() { // Avoid repeated filtering. Do it periodically. //if(_autoHideIntervalCounter > 0) { if(_autoHideIntervalCounter <= 0 || --_autoHideIntervalCounter <= 0) { SnooperTreeWidgetItem* item = processEventBuffer(); if(autoHideCheckBox->isChecked()) { //fprintf(stderr, "SnooperDialog::updateTimerTick(): Resetting auto-hide timer\n"); _autoHideIntervalCounter = _autoHideTimerInterval / _updateTimerInterval; filterItems(); } //item->setSelected(true); //objectTree->setCurrentItem(item); objectTree->scrollToItem(item); objectTree->resizeColumnToContents(SnooperTreeWidgetItem::Name); } // Don't tick the flashers until the auto-hide countdown is done. //return; } if(_flashingItems.isEmpty() || !useFlashCheckBox->isChecked()) return; SnooperTreeWidgetItem* item; for(QSet::iterator i = _flashingItems.begin(); i != _flashingItems.end(); ) { item = *i; if(item->tickFlash()) i = _flashingItems.erase(i); else ++i; } } // Static QString SnooperDialog::eventTypeString(const QEvent::Type& eventType) { if(eventType != QEvent::None) { QString key; QMap::const_iterator iet = _eventTypeMap.constFind(eventType); if(iet != _eventTypeMap.constEnd()) return *iet; } return QString(); } } // namespace MusEGui muse-4.0.0/src/muse/components/snooper.h000066400000000000000000000242231404105311000202370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // snooper.h // (C) Copyright 2019 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SNOOPER_H__ #define __SNOOPER_H__ #include #include #include #include #include #include #include #include #include #include #include #include "ui_snooperbase.h" namespace MusEGui { //--------------------------------------------------------- // SnooperTreeWidgetItem //--------------------------------------------------------- class SnooperTreeWidgetItem : public QTreeWidgetItem { public: enum Cols { Name = 0, Property, PropertyType, PropertyValue, EventType }; enum ItemType { NormalItem = Type, ObjectItem = UserType, PropertiesItem = ObjectItem + 1, PropertyItem = PropertiesItem + 1 }; enum ItemMode { NormalMode }; private: QObject* _object; bool _isWindowBranch; bool _isParentedTopLevelBranch; int _metaPropertyIndex; QMetaObject::Connection _metaConnection; //QMap _hitMap; QBrush _origBackground; int _flashCounter; bool _isFlashing; void init(); public: // Overrides for QTreeWidgetItem constructor... SnooperTreeWidgetItem(int type = NormalItem, QObject* obj = nullptr, int metaPropertyIndex = 0, const QMetaObject::Connection& conn = QMetaObject::Connection()) : QTreeWidgetItem(type), _object(obj), _metaPropertyIndex(metaPropertyIndex), _metaConnection(conn) { init(); } SnooperTreeWidgetItem(const QStringList& strings, int type = NormalItem, QObject* obj = nullptr, int metaPropertyIndex = 0, const QMetaObject::Connection& conn = QMetaObject::Connection()) : QTreeWidgetItem(strings, type), _object(obj), _metaPropertyIndex(metaPropertyIndex), _metaConnection(conn) { init(); } SnooperTreeWidgetItem(QTreeWidget* parent, int type = NormalItem, QObject* obj = nullptr, int metaPropertyIndex = 0, const QMetaObject::Connection& conn = QMetaObject::Connection()) : QTreeWidgetItem(parent, type), _object(obj), _metaPropertyIndex(metaPropertyIndex), _metaConnection(conn) { init(); } SnooperTreeWidgetItem(QTreeWidget* parent, const QStringList& strings, int type = NormalItem, QObject* obj = nullptr, int metaPropertyIndex = 0, const QMetaObject::Connection& conn = QMetaObject::Connection()) : QTreeWidgetItem(parent, strings, type), _object(obj), _metaPropertyIndex(metaPropertyIndex), _metaConnection(conn) { init(); } SnooperTreeWidgetItem(QTreeWidget* parent, QTreeWidgetItem* preceding, int type = NormalItem, QObject* obj = nullptr, int metaPropertyIndex = 0, const QMetaObject::Connection& conn = QMetaObject::Connection()) : QTreeWidgetItem(parent, preceding, type), _object(obj), _metaPropertyIndex(metaPropertyIndex), _metaConnection(conn) { init(); } SnooperTreeWidgetItem(QTreeWidgetItem* parent, int type = NormalItem, QObject* obj = nullptr, int metaPropertyIndex = 0, const QMetaObject::Connection& conn = QMetaObject::Connection()) : QTreeWidgetItem(parent, type), _object(obj), _metaPropertyIndex(metaPropertyIndex), _metaConnection(conn) { init(); } SnooperTreeWidgetItem(QTreeWidgetItem* parent, const QStringList& strings, int type = NormalItem, QObject* obj = nullptr, int metaPropertyIndex = 0, const QMetaObject::Connection& conn = QMetaObject::Connection()) : QTreeWidgetItem(parent, strings, type), _object(obj), _metaPropertyIndex(metaPropertyIndex), _metaConnection(conn) { init(); } SnooperTreeWidgetItem(QTreeWidgetItem* parent, QTreeWidgetItem* preceding, int type = NormalItem, QObject* obj = nullptr, int metaPropertyIndex = 0, const QMetaObject::Connection& conn = QMetaObject::Connection()) : QTreeWidgetItem(parent, preceding, type), _object(obj), _metaPropertyIndex(metaPropertyIndex), _metaConnection(conn) { init(); } QObject* object() { return _object; } const QObject* cobject() const { return _object; } bool isParentedTopLevelBranch() const { return _isParentedTopLevelBranch; } void setIsParentedTopLevelBranch(bool v) { _isParentedTopLevelBranch = v; } bool isWindowBranch() const { return _isWindowBranch; } void setIsWindowBranch(bool v) { _isWindowBranch = v; } const QMetaObject::Connection& connection() const { return _metaConnection; } void setConnection(const QMetaObject::Connection& conn) { _metaConnection = conn; } void startFlash(int interval, const QColor& color, const QEvent::Type& eventType = QEvent::None); bool isFlashing() const { return _isFlashing; } // Driven from timer/divider. Returns true if the end was reached. bool tickFlash(); void resetFlash(); }; //--------------------------------------------------------- // Snooper Dialog //--------------------------------------------------------- class SnooperDialog : public QDialog, public Ui::SnooperDialogBase { Q_OBJECT // An extra property required to support stylesheets (not enough colours). Q_PROPERTY(QColor flashColor READ flashColor WRITE setFlashColor) public: typedef QMap HitMap; typedef QMap HitBuffer; protected: void showEvent(QShowEvent*) override; void hideEvent(QHideEvent*) override; void closeEvent(QCloseEvent*) override; bool eventFilter(QObject*, QEvent*) override; private: static QMap _eventTypeMap; // In milliseconds. static const int _updateTimerInterval; QTimer* _updateTimer; int _flashInterval; QColor _flashColor; // In milliseconds. static const int _autoHideTimerInterval; int _autoHideIntervalCounter; HitBuffer _eventBuffer; void putEventBuffer(QObject *obj, const QEvent::Type& eventType); // Also returns the first item processed (so it can be scrolled to). SnooperTreeWidgetItem* processEventBuffer(); bool _captureMouseClicks; bool _captureKeyPress; QSet _flashingItems; // Recursive! // Return true if anything of relevance was added ie. whether the branch should (not) be discarded. // If parentItem is given it adds to that item. Otherwise if null it adds as top level item. bool addBranch(QObject* object, SnooperTreeWidgetItem* parentItem, bool isParentedTopLevelBranch, bool isWindowBranch); // Recursive! bool filterBranch(bool parentIsRelevant, QTreeWidgetItem* parentItem); // Recursive! bool destroyBranch(QObject *obj, QTreeWidgetItem* parentItem, bool deleteBranchPending); // Recursive! Finds a non-hidden item. QTreeWidgetItem* findItem(const QObject *obj, QTreeWidgetItem* parentItem, bool noHidden, bool parentedTopLevels); const QTreeWidgetItem* cfindItem(const QObject *obj, const QTreeWidgetItem* parentItem, bool noHidden, bool parentedTopLevels) const; SnooperTreeWidgetItem* selectObject(const QObject *obj, const QEvent::Type& eventType = QEvent::None); void disconnectAll(); private slots: void objectDestroyed(QObject *obj = nullptr); void updateTimerTick(); void updateTree(); void filterItems(); void updateTreeClicked(); void filterToggled(bool); void finishedLineEditing(); void captureMouseClickToggled(bool); void captureKeyPressToggled(bool); void useFlashTimerToggled(bool); void resetFlashTimerClicked(); public: SnooperDialog(QWidget* parent=0); virtual ~SnooperDialog(); static QString eventTypeString(const QEvent::Type& eventType); // Finds a non-hidden item. QTreeWidgetItem* findObject(const QObject* obj, bool noHidden, bool parentedTopLevels); const QTreeWidgetItem* cfindObject(const QObject* obj, bool noHidden, bool parentedTopLevels) const; bool captureMouseClicks() const { return _captureMouseClicks; } bool captureKeyPress() const { return _captureKeyPress; } void setFlashInterval(int val) { _flashInterval = (1000 * val) / _updateTimerInterval; } QColor flashColor() const { return _flashColor; } void setFlashColor(const QColor& c) { _flashColor = c; } }; } // namespace MusEGui #endif // __SNOOPER_H__ muse-4.0.0/src/muse/components/snooperbase.ui000066400000000000000000000212751404105311000212640ustar00rootroot00000000000000 SnooperDialogBase 0 0 618 568 Snooper 0 0 QTabWidget::North QTabWidget::Rounded 0 16 16 Mode Auto-hide true Separate parented top levels Update tree Qt::Horizontal 40 20 Mouse clicks true Key presses true Qt::Vertical Flash timer true s 1 20 6 Reset Qt::Horizontal 40 20 Filters MusEGui QWidget Properties Qt::Horizontal 40 20 Search: Class name: Object name: Qt::Horizontal 40 20 About This tool helps developers and designers discover objects. Click on anything anywhere or press a key to highlight an item. It will NOT auto-update when new objects are created. Click 'Update tree'. When open, may slow program slightly. This dialog is NOT shown. 0 0 Class name::Object name Property Type Value Event type muse-4.0.0/src/muse/components/songinfo.h000066400000000000000000000027041404105311000203740ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // $Id: songinfo.h,v 1.0.0.0 2010/11/17 01:01:01 ogetbilo Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "ui_songinfo.h" class QDialog; namespace MusEGui { //--------------------------------------------------------- // SongInfoWidget // Wrapper around Ui::SongInfo //--------------------------------------------------------- class SongInfoWidget : public QDialog, public Ui::SongInfo { Q_OBJECT public: SongInfoWidget(QDialog *parent = 0) : QDialog(parent) { setupUi(this); } }; } // namespace MusEGui muse-4.0.0/src/muse/components/songinfo.ui000066400000000000000000000056241404105311000205660ustar00rootroot00000000000000 SongInfo 0 0 403 274 Project description 0 0 0 0 Show on project load Qt::Horizontal QSizePolicy::Expanding 311 20 &Cancel Alt+C &Ok Alt+O true buttonOk clicked() SongInfo accept() 20 20 20 20 buttonCancel clicked() SongInfo reject() 20 20 20 20 muse-4.0.0/src/muse/components/songpos_toolbar.cpp000066400000000000000000000042221404105311000223140ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // songpos_toolbar.cpp // (C) Copyright 2012 Florian Jung (flo93@users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "songpos_toolbar.h" #include "song.h" #include #include namespace MusEGui { SongPosToolbarWidget::SongPosToolbarWidget(QWidget* p) : MTScale(0, p, -100 /* some random scale, will be overwritten immediately */) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), this, SLOT(song_changed(MusECore::SongChangedStruct_t))); song_changed(0); } void SongPosToolbarWidget::song_changed(MusECore::SongChangedStruct_t /*flags*/) { // if(flags == ) // TODO: Filter more stuff out // return; if (width()!=0) setXMag(-(MusEGlobal::song->len()/width())); } QSize SongPosToolbarWidget::sizeHint() const { return QSize(100, minimumSize().height()); } void SongPosToolbarWidget::resizeEvent(QResizeEvent* ev) { song_changed(0); MTScale::resizeEvent(ev); } void SongPosToolbarWidget::paintEvent(QPaintEvent* ev) { View::paintEvent(ev); QPainter p; p.begin(this); p.setPen(Qt::darkGray); p.drawRect(0, 0, width()-1, height()-1); // p.setPen(Qt::lightGray); // p.drawRect(1, 1, width()-1, height()-1); p.end(); } } muse-4.0.0/src/muse/components/songpos_toolbar.h000066400000000000000000000026471404105311000217720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // songpos_toolbar.h // (C) Copyright 2012 Florian Jung (flo93@users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SONGPOS_TOOLBAR_H__ #define __SONGPOS_TOOLBAR_H__ #include "type_defs.h" #include "mtscale.h" namespace MusEGui { class SongPosToolbarWidget : public MTScale { Q_OBJECT protected: virtual void paintEvent(QPaintEvent* ev); public: SongPosToolbarWidget(QWidget* parent); virtual QSize sizeHint() const; virtual void resizeEvent(QResizeEvent*); private slots: void song_changed(MusECore::SongChangedStruct_t); }; } #endif muse-4.0.0/src/muse/components/splitter.cpp000066400000000000000000000104631404105311000207540ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: splitter.cpp,v 1.1.1.1 2003/10/27 18:54:59 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "splitter.h" #include "xml.h" #include #include namespace MusEGui { //--------------------------------------------------------- // Splitter //--------------------------------------------------------- Splitter::Splitter(QWidget* parent, const char* name) : QSplitter(parent) { if(name) setObjectName(name); setOpaqueResize(true); } Splitter::Splitter(Qt::Orientation o, QWidget* parent, const char* name) : QSplitter(o, parent) { if(name) setObjectName(name); setOpaqueResize(true); } //--------------------------------------------------------- // saveConfiguration //--------------------------------------------------------- void Splitter::writeStatus(int level, MusECore::Xml& xml) { QList vl = sizes(); //xml.nput(level++, "<%s>", name()); xml.nput(level++, "<%s>", MusECore::Xml::xmlString(objectName()).toLatin1().constData()); QList::iterator ivl = vl.begin(); for (; ivl != vl.end(); ++ivl) { xml.nput("%d ", *ivl); } //xml.nput("\n", name()); xml.nput("\n", MusECore::Xml::xmlString(objectName()).toLatin1().constData()); } //--------------------------------------------------------- // loadConfiguration //--------------------------------------------------------- void Splitter::readStatus(MusECore::Xml& xml) { QList vl; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: xml.unknown("Splitter"); break; case MusECore::Xml::Text: { //QStringList sl = QStringList::split(' ', tag); // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 QStringList sl = tag.split(QString(" "), Qt::SkipEmptyParts); #else QStringList sl = tag.split(QString(" "), QString::SkipEmptyParts); #endif for (QStringList::Iterator it = sl.begin(); it != sl.end(); ++it) { int val = (*it).toInt(); vl.append(val); } // fix for allowing the arranger split to work nicely with old songs // that have only two splitters, we add a first splitter with the // standard strip width 53 pixels if (objectName() == "split") { if (vl.size() < 3) { vl.prepend(53); } } // // } break; case MusECore::Xml::TagEnd: if (tag == objectName()) { setSizes(vl); return; } default: break; } } } } // namespace MusEGui muse-4.0.0/src/muse/components/splitter.h000066400000000000000000000031461404105311000204210ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: splitter.h,v 1.1.1.1 2003/10/27 18:54:51 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SPLITTER_H__ #define __SPLITTER_H__ #include namespace MusECore { class Xml; } namespace MusEGui { //--------------------------------------------------------- // Splitter //--------------------------------------------------------- class Splitter : public QSplitter { Q_OBJECT public: Splitter(QWidget* parent, const char* name = 0); Splitter(Qt::Orientation o, QWidget* parent, const char* name = 0); void writeStatus(int level, MusECore::Xml&); void readStatus(MusECore::Xml&); void setPosition(int idx, int pos) { moveSplitter(pos, idx); } }; } #endif muse-4.0.0/src/muse/components/synctoolbar.cpp000066400000000000000000000135311404105311000214440ustar00rootroot00000000000000#include #include "synctoolbar.h" #include "song.h" #include "icons.h" #include "audio.h" #include "audiodev.h" #include "gconfig.h" namespace MusEGui { SyncToolbar::SyncToolbar(const QString &title, QWidget *parent) : QToolBar(title, parent) { setObjectName("Sync toolbar"); syncAction = new QAction(*externSyncOnSVGIcon, "External sync", this); syncAction->setCheckable(true); syncAction->setToolTip(tr("External sync on/off")); syncAction->setChecked(MusEGlobal::extSyncFlag); jackTransportAction = new QAction(*jackTransportOnSVGIcon, "Jack Transport", this); jackTransportAction->setCheckable(true); jackTransportAction->setToolTip(tr("Jack Transport on/off")); timebaseMasterAction = new QAction(*timebaseMasterOnSVGIcon, "Timebase master", this); timebaseMasterAction->setCheckable(true); timebaseMasterAction->setToolTip( tr("On: Timebase master\nOff: Not master\nFlash: Waiting. Another client is master. Click to force.")); const bool has_master = MusEGlobal::audioDevice && MusEGlobal::audioDevice->hasTimebaseMaster(); jackTransportAction->setEnabled(has_master); timebaseMasterAction->setEnabled(has_master && MusEGlobal::config.useJackTransport); jackTransportAction->setChecked(has_master && MusEGlobal::config.useJackTransport); timebaseMasterAction->setChecked(has_master && MusEGlobal::config.useJackTransport && MusEGlobal::timebaseMasterState); addAction(syncAction); addAction(jackTransportAction); addAction(timebaseMasterAction); blinkTimer = new QTimer(this); connect(blinkTimer, SIGNAL(timeout()), SLOT(timebaseBlink())); blinkTimer->stop(); connect(syncAction, SIGNAL(toggled(bool)), SLOT(extSyncClicked(bool))); connect(jackTransportAction, SIGNAL(toggled(bool)), SLOT(useJackTransportClicked(bool))); connect(timebaseMasterAction, SIGNAL(toggled(bool)), SLOT(timebaseMasterClicked(bool))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), this, SLOT(songChanged(MusECore::SongChangedStruct_t))); } //--------------------------------------------------------- // extSyncClicked //--------------------------------------------------------- void SyncToolbar::extSyncClicked(bool v) { MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(&MusEGlobal::extSyncFlag, v, MusECore::PendingOperationItem::SetExternalSyncFlag)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // useJackTransportClicked //--------------------------------------------------------- void SyncToolbar::useJackTransportClicked(bool v) { if(!v && MusEGlobal::timebaseMasterState && MusEGlobal::audioDevice) { // Let the operation do this. //MusEGlobal::config.timebaseMaster = v; // Force it. MusEGlobal::audioDevice->setMaster(v, false); } MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(&MusEGlobal::config.useJackTransport, v, MusECore::PendingOperationItem::SetUseJackTransport)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // timebaseMasterClicked //--------------------------------------------------------- void SyncToolbar::timebaseMasterClicked(bool v) { // MusECore::PendingOperationList operations; // operations.add(MusECore::PendingOperationItem(&MusEGlobal::useJackTransport, v, MusECore::PendingOperationItem::SetUseJackTransport)); // MusEGlobal::audio->msgExecutePendingOperations(operations, true); if (!MusEGlobal::audioDevice) return; if (MusEGlobal::config.useJackTransport) { //if(MusEGlobal::timebaseMasterState != MusEGlobal::config.timebaseMaster) MusEGlobal::config.timebaseMaster = v; // Force it. MusEGlobal::audioDevice->setMaster(v, true); } } void SyncToolbar::songChanged(MusECore::SongChangedStruct_t flags) { if (flags & SC_EXTERNAL_MIDI_SYNC) { QSignalBlocker blocker(syncAction); syncAction->setChecked(MusEGlobal::extSyncFlag); } if (flags & SC_USE_JACK_TRANSPORT) { QSignalBlocker blocker(jackTransportAction); QSignalBlocker blockerx(timebaseMasterAction); jackTransportAction->setEnabled(MusEGlobal::audioDevice && MusEGlobal::audioDevice->hasOwnTransport()); jackTransportAction->setChecked(MusEGlobal::config.useJackTransport && jackTransportAction->isEnabled()); timebaseMasterAction->setEnabled(MusEGlobal::audioDevice && MusEGlobal::audioDevice->hasOwnTransport() && MusEGlobal::audioDevice->hasTimebaseMaster() && MusEGlobal::config.useJackTransport); } if (flags & SC_TIMEBASE_MASTER) { QSignalBlocker blocker(timebaseMasterAction); const bool has_master = MusEGlobal::audioDevice && MusEGlobal::audioDevice->hasTimebaseMaster(); if (has_master && MusEGlobal::timebaseMasterState) { blinkTimer->stop(); timebaseMasterAction->setChecked(true); } else if (has_master && MusEGlobal::config.timebaseMaster) { timebaseMasterAction->setChecked(false); if (timebaseMasterAction->isEnabled()) { blinkState = false; blinkTimer->start(250); } } else { blinkTimer->stop(); timebaseMasterAction->setChecked(false); } } } void SyncToolbar::timebaseBlink() { if (!timebaseMasterAction->isEnabled()) return; QSignalBlocker blocker(timebaseMasterAction); blinkState = !blinkState; timebaseMasterAction->setChecked(blinkState); } } // namespace muse-4.0.0/src/muse/components/synctoolbar.h000066400000000000000000000012201404105311000211010ustar00rootroot00000000000000#ifndef SYNCTOOLBAR_H #define SYNCTOOLBAR_H #include #include #include "type_defs.h" namespace MusEGui { class SyncToolbar : public QToolBar { Q_OBJECT QAction* syncAction; QAction* jackTransportAction; QAction* timebaseMasterAction; QTimer* blinkTimer; bool blinkState; private slots: void extSyncClicked(bool v); void useJackTransportClicked(bool v); void timebaseMasterClicked(bool v); void songChanged(MusECore::SongChangedStruct_t); void timebaseBlink(); public: SyncToolbar(const QString& title, QWidget* parent = nullptr); }; } // namespace #endif // SYNCTOOLBAR_H muse-4.0.0/src/muse/components/synthconfigbase.ui000066400000000000000000000201501404105311000221210ustar00rootroot00000000000000 SynthConfigBase 0 0 773 448 Configure midi devices, midi ports, and synthesizers Qt::Vertical Available soft synthesizers Qt::AlignCenter List of available software synthesizers true File Type Inst Name Version Description Qt::Horizontal 0 0 Useable devices Qt::AlignCenter 0 0 QAbstractItemView::ExtendedSelection QAbstractItemView::SelectRows false false 20 false Add: Synth true ALSA true false JACK Qt::Horizontal 40 20 Rename Remove 0 0 Device port assignments Qt::AlignCenter 0 0 QAbstractItemView::SingleSelection QAbstractItemView::SelectRows 20 Qt::Horizontal 40 20 &Apply &OK MusEGui::Splitter QSplitter
splitter.h
1
muse-4.0.0/src/muse/components/synthdialog.cpp000066400000000000000000000320741404105311000214350ustar00rootroot00000000000000#include #include #include "synthdialog.h" #include "gconfig.h" #include "icons.h" #include "app.h" namespace MusEGui { int SynthDialog::selType = SEL_TYPE_ALL; int SynthDialog::selCategory = SEL_CAT_ALL; int SynthDialog::curTab = TAB_ALL; QStringList SynthDialog::filterSavedItems = QStringList(); QRect SynthDialog::geometrySave = QRect(); QByteArray SynthDialog::listSave = QByteArray(); QSet SynthDialog::favs = QSet(); //--------------------------------------------------------- // SynthDialog // select Plugin dialog //--------------------------------------------------------- SynthDialog::SynthDialog(QWidget* parent) : QDialog(parent) { ui.setupUi(this); // setStyleSheet("* {font-size:" + QString::number(MusEGlobal::config.fonts[0].pointSize()) + "pt}"); setWindowTitle(tr("Select Software Synthesizer")); favChanged = false; if(!geometrySave.isNull()) setGeometry(geometrySave); ui.tabBar->addTab("All"); ui.tabBar->addTab("Favorites"); ui.pList->setAlternatingRowColors(true); ui.okB->setDefault(true); ui.okB->setEnabled(false); ui.catButtonGroup->setId(ui.rbAll, 0); ui.catButtonGroup->setId(ui.rbSynths, 1); ui.catButtonGroup->setId(ui.rbEffects, 2); switch(selCategory) { case SEL_CAT_ALL: ui.rbAll->setChecked(true); break; case SEL_CAT_SYNTH: ui.rbSynths->setChecked(true); break; case SEL_CAT_EFFECT: ui.rbEffects->setChecked(true); break; } ui.tabBar->setCurrentIndex(curTab); ui.pluginType->addItem("All", SEL_TYPE_ALL); ui.pluginType->addItem("MESS", SEL_TYPE_MESS); ui.pluginType->addItem("DSSI", SEL_TYPE_DSSI); ui.pluginType->addItem("LV2", SEL_TYPE_LV2); ui.pluginType->addItem("VST", SEL_TYPE_VST); connect (ui.pluginType,SIGNAL(currentIndexChanged(int)), SLOT(filterType(int))); for (int i=0; i < ui.pluginType->count(); i++) { if (selType == ui.pluginType->itemData(i).toInt()) { ui.pluginType->setCurrentIndex(i); break; } } ui.filterBox->addItems(filterSavedItems); fillSynths(); ui.pList->setSortingEnabled(true); if(listSave.isEmpty()) { ui.pList->header()->resizeSection(COL_NAME, 300); ui.pList->header()->resizeSection(COL_TYPE, 50); ui.pList->header()->resizeSection(COL_CAT, 64); ui.pList->header()->resizeSection(COL_AUTHOR, 120); ui.pList->header()->resizeSection(COL_VERSION, 64); ui.pList->header()->resizeSection(COL_URI, 300); ui.pList->sortByColumn(COL_NAME, Qt::AscendingOrder); } else ui.pList->header()->restoreState(listSave); connect(ui.pList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(accept())); connect(ui.pList, &QTreeWidget::itemClicked, [this](){ ui.okB->setEnabled(true); } ); connect(ui.pList, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(listContextMenu(const QPoint&))); connect(ui.cancelB, SIGNAL(clicked()), SLOT(reject())); connect(ui.okB, SIGNAL(clicked()), SLOT(accept())); connect(ui.tabBar, SIGNAL(currentChanged(int)), SLOT(tabChanged(int))); connect(ui.filterBox, SIGNAL(editTextChanged(const QString&)),SLOT(fillSynths())); connect(ui.catButtonGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &SynthDialog::categoryChanged); } void SynthDialog::filterType(int i) { selType = ui.pluginType->itemData(i).toInt(); fillSynths(); } void SynthDialog::listContextMenu(const QPoint& ) { QTreeWidgetItem* item = ui.pList->currentItem(); if (!item) return; QMenu* menu = new QMenu(); if (curTab == TAB_ALL) { QAction *add = new QAction(tr("Add to Favorites"), menu); QAction *remove = new QAction(tr("Remove from Favorites"), menu); isFavItem(item) ? add->setEnabled(false) : remove->setEnabled(false); menu->addAction(add); menu->addAction(remove); QAction *sel = menu->exec(QCursor::pos()); if (sel == add) addToFavorites(item); else if (sel == remove) removeFavorite(item); } else { menu->addAction(new QAction(tr("Remove from Favorites"), menu)); if (menu->exec(QCursor::pos())) removeFavorite(item); } delete menu; } void SynthDialog::addToFavorites(QTreeWidgetItem *item) { QByteArray hash = QCryptographicHash::hash(item->data(COL_NAME, UDATA_NAME).toString().toUtf8() + item->text(COL_URI).toUtf8(), QCryptographicHash::Md5); favs.insert(hash); item->setForeground(COL_NAME, Qt::red); favChanged = true; } void SynthDialog::removeFavorite(QTreeWidgetItem *item) { QByteArray hash = QCryptographicHash::hash(item->data(COL_NAME, UDATA_NAME).toString().toUtf8() + item->text(COL_URI).toUtf8(), QCryptographicHash::Md5); favs.remove(hash); if (curTab == TAB_ALL) item->setForeground(COL_NAME, palette().text().color()); else { ui.pList->takeTopLevelItem(ui.pList->indexOfTopLevelItem(item)); if (!ui.pList->currentItem()) ui.okB->setEnabled(false); } favChanged = true; } bool SynthDialog::isFavItem(QTreeWidgetItem *item) { if (!item) return false; QByteArray hash = QCryptographicHash::hash(item->data(COL_NAME, UDATA_NAME).toString().toUtf8() + item->text(COL_URI).toUtf8(), QCryptographicHash::Md5); return favs.contains(hash); } bool SynthDialog::isFav(MusECore::Synth *synth) { QString urifile = synth->uri().isEmpty() ? synth->completeBaseName() : synth->uri(); QByteArray hash = QCryptographicHash::hash(synth->name().toUtf8() + urifile.toUtf8(), QCryptographicHash::Md5); return favs.contains(hash); } //--------------------------------------------------------- // value //--------------------------------------------------------- MusECore::Synth* SynthDialog::value() { QTreeWidgetItem* item = ui.pList->currentItem(); if (item) { QString uri, base; bool hasURI = item->data(COL_NAME, UDATA_HAS_URI).toBool(); if (hasURI) uri = item->text(COL_URI); else base = item->text(COL_URI); return MusEGlobal::synthis.find(uri, base, item->data(COL_NAME, UDATA_NAME).toString()); } printf("Synth not found\n"); return nullptr; } //--------------------------------------------------------- // saveSettings //--------------------------------------------------------- void SynthDialog::saveSettings() { if (!ui.filterBox->currentText().isEmpty()) { bool found = false; foreach (QString item, filterSavedItems) if(item == ui.filterBox->currentText()) { found = true; break; } if(!found) filterSavedItems.push_front(ui.filterBox->currentText()); } QHeaderView* hdr = ui.pList->header(); if(hdr) listSave = hdr->saveState(); geometrySave = geometry(); } //--------------------------------------------------------- // accept //--------------------------------------------------------- void SynthDialog::accept() { if (!ui.pList->currentItem()) return; saveSettings(); if (favChanged) MusEGlobal::muse->populateAddTrack(); QDialog::accept(); } //--------------------------------------------------------- // reject //--------------------------------------------------------- void SynthDialog::reject() { saveSettings(); if (favChanged) MusEGlobal::muse->populateAddTrack(); QDialog::reject(); } void SynthDialog::tabChanged(int index) { curTab = index; fillSynths(); ui.pbAddFav->setEnabled(curTab == TAB_ALL); } void SynthDialog::fillSynths() { ui.pList->clear(); ui.okB->setEnabled(false); QString type_name, cat_name; if (curTab == TAB_FAV && favs.isEmpty()) return; int index = -1; for (const auto& it : MusEGlobal::synthis) { index++; if (curTab == TAB_FAV && !isFav(it)) { continue; } QString sb_txt = ui.filterBox->currentText().toLower(); if (!(sb_txt.isEmpty() || it->name().toLower().contains(sb_txt))) continue; if (it->synthType() == MusECore::Synth::MESS_SYNTH) { if ((selCategory != SEL_CAT_SYNTH && selCategory != SEL_CAT_ALL) || (selType != SEL_TYPE_MESS && selType != SEL_TYPE_ALL)) continue; type_name = "MESS"; cat_name = "Synth"; } else if (it->synthType() == MusECore::Synth::DSSI_SYNTH) { if ((selCategory != SEL_CAT_SYNTH && selCategory != SEL_CAT_ALL) || (selType != SEL_TYPE_DSSI && selType != SEL_TYPE_ALL)) continue; type_name = "DSSI"; cat_name = "Synth"; } else if (it->synthType() == MusECore::Synth::LV2_SYNTH) { if ((selCategory != SEL_CAT_SYNTH && selCategory != SEL_CAT_ALL) || (selType != SEL_TYPE_LV2 && selType != SEL_TYPE_ALL)) continue; type_name = "LV2"; cat_name = "Synth"; } else if (it->synthType() == MusECore::Synth::VST_NATIVE_SYNTH) { if ((selCategory != SEL_CAT_SYNTH && selCategory != SEL_CAT_ALL) || (selType != SEL_TYPE_VST && selType != SEL_TYPE_ALL)) continue; type_name = "VST"; cat_name = "Synth"; } else if (it->synthType() == MusECore::Synth::LV2_EFFECT) { if ((selCategory != SEL_CAT_EFFECT && selCategory != SEL_CAT_ALL) || (selType != SEL_TYPE_LV2 && selType != SEL_TYPE_ALL)) continue; type_name = "LV2"; cat_name = "Effect"; } else if (it->synthType() == MusECore::Synth::VST_NATIVE_EFFECT) { if ((selCategory != SEL_CAT_EFFECT && selCategory != SEL_CAT_ALL) || (selType != SEL_TYPE_VST && selType != SEL_TYPE_ALL)) continue; type_name = "VST"; cat_name = "Effect"; } QTreeWidgetItem* item = new QTreeWidgetItem(ui.pList); item->setText(COL_NAME, it->description()); item->setText(COL_TYPE, type_name); item->setText(COL_CAT, cat_name); item->setText(COL_AUTHOR, it->maker()); item->setText(COL_VERSION, it->version()); item->setText(COL_URI, it->uri().isEmpty() ? it->completeBaseName() : it->uri()); item->setData(COL_NAME, UDATA_INDEX, index); item->setData(COL_NAME, UDATA_HAS_URI, !it->uri().isEmpty()); item->setData(COL_NAME, UDATA_NAME, it->name()); if (curTab == TAB_ALL && isFav(it)) { item->setForeground(COL_NAME, Qt::red); } } } //--------------------------------------------------------- // getPlugin //--------------------------------------------------------- MusECore::Synth *SynthDialog::getSynth(QWidget* parent) { SynthDialog* dialog = new SynthDialog(parent); MusECore::Synth* p = nullptr; int rv = dialog->exec(); if(rv) p = dialog->value(); delete dialog; return p; } int SynthDialog::getSynthIndex(QWidget* parent) { SynthDialog* dialog = new SynthDialog(parent); int rc = -1; int rv = dialog->exec(); if(rv) rc = dialog->ui.pList->currentItem()->data(COL_NAME, UDATA_INDEX).toInt(); delete dialog; return rc; } void SynthDialog::categoryChanged(QAbstractButton *button) { selCategory = ui.catButtonGroup->id(button); fillSynths(); } void SynthDialog::writeFavConfiguration(int level, MusECore::Xml& xml) { xml.tag(level++, "synthDialogFavorites"); for (const auto& it : favs) xml.strTag(level, "hash", QLatin1String(it.toHex())); xml.etag(--level, "synthDialogFavorites"); } void SynthDialog::readFavConfiguration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag=="hash") favs.insert(QByteArray::fromHex(xml.parse1().toLatin1())); else xml.unknown("readSynthFavConfiguration"); break; case MusECore::Xml::TagEnd: if (tag == "synthDialogFavorites") return; break; default: break; } } } void SynthDialog::on_pbAddFav_clicked() { if (curTab == TAB_FAV) return; QTreeWidgetItem *item = ui.pList->currentItem(); if (item) addToFavorites(item); } void SynthDialog::on_pbRemoveFav_clicked() { QTreeWidgetItem *item = ui.pList->currentItem(); if (item) removeFavorite(item); } } // namespace GUI muse-4.0.0/src/muse/components/synthdialog.h000066400000000000000000000036311404105311000210770ustar00rootroot00000000000000#ifndef SYNTHDIALOG_H #define SYNTHDIALOG_H #include "ui_synthdialogbase.h" #include #include #include #include "globaldefs.h" #include "synth.h" #include "globals.h" namespace MusECore { class Plugin; } namespace MusEGui { class SynthDialog : public QDialog { Q_OBJECT enum SynthCategory { SEL_CAT_ALL = 0, SEL_CAT_SYNTH = 1, SEL_CAT_EFFECT = 2 }; enum SynthType { SEL_TYPE_MESS, SEL_TYPE_DSSI, SEL_TYPE_LV2, SEL_TYPE_VST, SEL_TYPE_ALL}; enum Tab { TAB_ALL = 0, TAB_FAV = 1 }; enum Col { COL_NAME = 0, COL_TYPE, COL_CAT, COL_AUTHOR, COL_VERSION, COL_URI }; enum UserData { UDATA_INDEX = Qt::UserRole, UDATA_HAS_URI = Qt::UserRole + 1, UDATA_NAME = Qt::UserRole + 2 }; public: explicit SynthDialog(QWidget* parent = nullptr); static MusECore::Synth* getSynth(QWidget* parent); static int getSynthIndex(QWidget* parent); MusECore::Synth* value(); static void writeFavConfiguration(int level, MusECore::Xml& xml); static void readFavConfiguration(MusECore::Xml& xml); static bool isFav(MusECore::Synth *synth); public slots: void accept(); void reject(); private slots: void tabChanged(int); void fillSynths(); void filterType(int); void listContextMenu(const QPoint&); void categoryChanged(QAbstractButton *button); void on_pbAddFav_clicked(); void on_pbRemoveFav_clicked(); private: bool favChanged; void saveSettings(); void addToFavorites(QTreeWidgetItem *item); void removeFavorite(QTreeWidgetItem *item); // QByteArray getHash(MusECore::Synth *synth); bool isFavItem(QTreeWidgetItem *item); static int selCategory; static int selType; static int curTab; static QStringList filterSavedItems; static QRect geometrySave; static QByteArray listSave; static QSet favs; Ui::SynthDialogBase ui; }; } #endif // SYNTHDIALOGBASE_H muse-4.0.0/src/muse/components/synthdialogbase.ui000066400000000000000000000221411404105311000221150ustar00rootroot00000000000000 SynthDialogBase 0 0 737 537 Dialog 3 3 3 3 3 2 0 2 10 10 0 0 Qt::CustomContextMenu false false false false true Name Type Category Author Version URI / File Favorites 12 1 12 &Add &Remove Filters 10 20 0 16777215 14 API type 0 0 16777215 14 Name true true 12 12 0 0 &Select true 0 0 &Cancel Category 9 All true catButtonGroup Synths catButtonGroup Effects catButtonGroup QTabBar QWidget
qtabbar.h
1
filterBox rbAll rbSynths rbEffects pluginType pbAddFav pbRemoveFav okB cancelB pList
muse-4.0.0/src/muse/components/tb1.cpp000066400000000000000000000154361404105311000176010ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tb1.cpp,v 1.3.2.2 2007/01/04 00:35:17 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "tb1.h" #include "poslabel.h" #include "pitchlabel.h" #include "raster_widgets.h" #include "icons.h" namespace MusEGui { //--------------------------------------------------------- // Toolbar1 // solo time pitch raster //--------------------------------------------------------- Toolbar1::Toolbar1(RasterizerModel *model, QWidget* parent, int r, bool sp) : QToolBar(QString("Pos/Snap/Solo-tools"), parent) { setObjectName("Pos/Snap/Solo-tools"); pitch = nullptr; showPitch = sp; solo = new QToolButton(); solo->setText(tr("Solo")); solo->setCheckable(true); solo->setFocusPolicy(Qt::NoFocus); //solo->setContentsMargins(0,0,0,0); addWidget(solo); //--------------------------------------------------- // Cursor Position //--------------------------------------------------- QLabel* label = new QLabel(tr("Cursor")); label->setIndent(3); //label->setContentsMargins(0,0,0,0); addWidget(label); pos = new PosLabel(nullptr, "PosLabel"); addWidget(pos); if (showPitch) { pitch = new PitchLabel(nullptr, "PitchLabel"); pitch->setEnabled(false); addWidget(pitch); } gridOnButton = new QToolButton(); gridOnButton->setIcon(*gridOnSVGIcon); gridOnButton->setFocusPolicy(Qt::NoFocus); gridOnButton->setCheckable(true); gridOnButton->setToolTip(tr("Show grid")); gridOnButton->setWhatsThis(tr("Show grid")); addWidget(gridOnButton); connect(gridOnButton, &QToolButton::toggled, [this](bool v) { gridOnButtonChanged(v); } ); //--------------------------------------------------- // Raster //--------------------------------------------------- raster = new RasterLabelCombo(RasterLabelCombo::TableView, model, nullptr, "RasterLabelCombo"); raster->setFocusPolicy(Qt::TabFocus); setRaster(r); //setContentsMargins(0,0,0,0); addWidget(raster); connect(raster, &RasterLabelCombo::rasterChanged, [this](int raster) { _rasterChanged(raster); } ); connect(solo, &QToolButton::toggled, [this](bool v) { soloChanged(v); } ); pos->setEnabled(false); } const Rasterizer *Toolbar1::rasterizer() const { return raster->rasterizer(); } void Toolbar1::setRasterizerModel(RasterizerModel *model) { raster->setRasterizerModel(model); } //--------------------------------------------------------- // rasterChanged //--------------------------------------------------------- void Toolbar1::_rasterChanged(int raster) { emit rasterChanged(raster); } //--------------------------------------------------------- // gridOnButtonChanged //--------------------------------------------------------- void Toolbar1::gridOnButtonChanged(bool v) { emit gridOnChanged(v); } //--------------------------------------------------------- // currentRaster //--------------------------------------------------------- int Toolbar1::currentRaster() const { const QModelIndex mdl_idx = raster->currentModelIndex(); if(!mdl_idx.isValid()) return 1; return mdl_idx.data(RasterizerModel::RasterValueRole).toInt(); } //--------------------------------------------------------- // setPitch //--------------------------------------------------------- void Toolbar1::setPitch(int val) { if (pitch && showPitch) { pitch->setEnabled(val != -1); pitch->setPitch(val); } } void Toolbar1::setInt(int val) { if (pitch && showPitch) { pitch->setEnabled(val != -1); pitch->setInt(val); } } //--------------------------------------------------------- // setTime //--------------------------------------------------------- void Toolbar1::setTime(unsigned val) { if (!pos->isVisible()) { //printf("NOT visible\n"); return; } if (val == INT_MAX) pos->setEnabled(false); else { pos->setEnabled(true); pos->setValue(val); } } //--------------------------------------------------------- // setRaster //--------------------------------------------------------- void Toolbar1::setRaster(int val) { changeRaster(val); } //--------------------------------------------------------- // changeRaster //--------------------------------------------------------- int Toolbar1::changeRaster(int val) { const RasterizerModel* rast_mdl = raster->rasterizerModel(); const int rast = rast_mdl->checkRaster(val); const QModelIndex mdl_idx = rast_mdl->modelIndexOfRaster(rast); if(mdl_idx.isValid()) raster->setCurrentModelIndex(mdl_idx); else fprintf(stderr, "Toolbar1::changeRaster: rast %d not found in box!\n", rast); return rast; } //--------------------------------------------------------- // setGridOn //--------------------------------------------------------- void Toolbar1::setGridOn(bool flag) { gridOnButton->blockSignals(true); gridOnButton->setChecked(flag); gridOnButton->blockSignals(false); } //--------------------------------------------------------- // setSolo //--------------------------------------------------------- void Toolbar1::setSolo(bool flag) { solo->blockSignals(true); solo->setChecked(flag); solo->blockSignals(false); } //--------------------------------------------------------- // setPitchMode //--------------------------------------------------------- void Toolbar1::setPitchMode(bool /*flag*/) { // if(pitch) // pitch->setPitchMode(flag); } } // namespace MusEGui muse-4.0.0/src/muse/components/tb1.h000066400000000000000000000044561404105311000172460ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tb1.h,v 1.2 2004/01/11 18:55:37 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TB1_H__ #define __TB1_H__ #include #include "rasterizer.h" class QToolButton; namespace MusEGui { class PitchLabel; class PosLabel; class RasterLabelCombo; //--------------------------------------------------------- // Toolbar1 //--------------------------------------------------------- class Toolbar1 : public QToolBar { Q_OBJECT QToolButton* solo; PosLabel* pos; PitchLabel* pitch; QToolButton* gridOnButton; RasterLabelCombo* raster; bool showPitch; private slots: void _rasterChanged(int raster); void gridOnButtonChanged(bool v); public slots: void setTime(unsigned); void setPitch(int); void setInt(int); void setRaster(int); void setGridOn(bool flag); signals: void rasterChanged(int raster); void soloChanged(bool); void gridOnChanged(bool v); public: Toolbar1(RasterizerModel *model, QWidget* parent, int r=96, bool showPitch=true); void setSolo(bool val); void setPitchMode(bool flag); const Rasterizer *rasterizer() const; void setRasterizerModel(RasterizerModel *model); int currentRaster() const; // Same as setRaster() but returns the actual value used. int changeRaster(int val); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/thinslider.cpp000066400000000000000000000613161404105311000212560ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/slider.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include "mmath.h" #include #include #include "utils.h" #include "thinslider.h" namespace MusEGui { //------------------------------------------------------------- // ThinSlider - The ThinSlider Widget // // Slider is a slider widget which operates on an interval // of type double. Slider supports different layouts as // well as a scale. //------------------------------------------------------------ //------------------------------------------------------------ //.F ThinSlider::Slider // // Constructor // //.u Syntax: //.f ThinSlider::Slider(QWidget *parent, const char *name, Orientation orient = Horizontal, ScalePos scalePos = None, int bgStyle = BgTrough) // //.u Parameters //.p // QWidget *parent -- parent widget // const char *name -- The Widget's name. Default = 0. // Orientation Orient -- Orientation of the slider. Can be ThinSlider::Horizontal // or ThinSlider::Vertical. // Defaults to Horizontal. // ScalePos scalePos -- Position of the scale. Can be ThinSlider::None, // ThinSlider::Left, ThinSlider::Right, ThinSlider::Top, // or ThinSlider::Bottom. Defaults to ThinSlider::None. !!! CURRENTLY only ThinSlider::None supported - oget 20110913 // QColor fillcolor -- the color used to fill in the full side // of the Slider //------------------------------------------------------------ ThinSlider::ThinSlider(QWidget *parent, const char *name, Qt::Orientation orient, ScalePos scalePos, QColor fillColor) : SliderBase(parent,name) { d_thumbLength = 16; d_thumbHalf = 8; d_thumbWidth = 16; d_scaleDist = 4; d_scaleStep = 0.0; d_scalePos = scalePos; d_xMargin = 0; d_yMargin = 0; d_mMargin = 1; d_fillColor = fillColor; d_sliderRect.setRect(0, 0, 8, 8); setOrientation(orient); } //------------------------------------------------------------ //.F ThinSlider::setSizeHint //------------------------------------------------------------ void ThinSlider::setSizeHint(uint w, uint h) { horizontal_hint = w; vertical_hint = h; } //------------------------------------------------------------ //.F ThinSlider::~Slider // Destructor //.u Syntax //.f ThinSlider::~Slider() //------------------------------------------------------------ ThinSlider::~ThinSlider() { } //---------------------------------------------------- // //.F ThinSlider::setThumbLength // // Set the slider's thumb length // //.u Syntax // void ThinSlider::setThumbLength(int l) // //.u Parameters //.p int l -- new length // //----------------------------------------------------- void ThinSlider::setThumbLength(int l) { d_thumbLength = MusECore::qwtMax(l,8); d_thumbHalf = d_thumbLength / 2; resize(size()); } //------------------------------------------------------------ // //.F ThinSlider::setThumbWidth // Change the width of the thumb // //.u Syntax //.p void ThinSlider::setThumbWidth(int w) // //.u Parameters //.p int w -- new width // //------------------------------------------------------------ void ThinSlider::setThumbWidth(int w) { d_thumbWidth = MusECore::qwtMax(w,4); resize(size()); } //------------------------------------------------------------ //.- //.F ThinSlider::scaleChange // Notify changed scale // //.u Syntax //.f void ThinSlider::scaleChange() // //.u Description // Called by QwtScaledWidget // //------------------------------------------------------------ void ThinSlider::scaleChange() { if (!hasUserScale()) d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); update(); } //------------------------------------------------------------ //.- //.F ThinSlider::fontChange // Notify change in font // //.u Syntax //.f ThinSlider::fontChange(const QFont &oldFont) // //------------------------------------------------------------ void ThinSlider::fontChange(const QFont & /*oldFont*/) { repaint(); } //------------------------------------------------------------ // drawSlider // Draw the slider into the specified rectangle. //------------------------------------------------------------ void ThinSlider::drawSlider(QPainter *p, const QRect &r) { p->setRenderHint(QPainter::Antialiasing); const QPalette& pal = palette(); QBrush brBack(pal.window()); QBrush brMid(pal.mid()); QBrush brDark(pal.dark()); QRect cr; int ipos,dist1; double rpos; int xrad = 4; int yrad = 4; // for the empty side QColor e_mask_edge = pal.mid().color(); QColor e_mask_center = pal.midlight().color(); int e_alpha = 215; e_mask_edge.setAlpha(e_alpha); e_mask_center.setAlpha(e_alpha); QLinearGradient e_mask; e_mask.setColorAt(0, e_mask_edge); e_mask.setColorAt(0.5, e_mask_center); e_mask.setColorAt(1, e_mask_edge); // for the full side rpos = (value() - minValue()) / (maxValue() - minValue()); int f_brightness = 155 * rpos + 100; int f_alpha; int f_edge; if (pal.currentColorGroup() == QPalette::Disabled) { f_alpha = 185; f_edge = 100; } else { f_alpha = 127; f_edge = 0; } QColor f_mask_center = QColor(f_brightness, f_brightness, f_brightness, f_alpha); QColor f_mask_edge = QColor(f_edge, f_edge, f_edge, f_alpha); QLinearGradient f_mask; f_mask.setColorAt(0, f_mask_edge); f_mask.setColorAt(0.5, f_mask_center); f_mask.setColorAt(1, f_mask_edge); // for the thumb QLinearGradient thumbGrad; QColor thumb_edge = pal.dark().color(); QColor thumb_center = pal.midlight().color(); thumbGrad.setColorAt(0, thumb_edge); thumbGrad.setColorAt(0.5, thumb_center); thumbGrad.setColorAt(1, thumb_edge); if (d_orient == Qt::Horizontal) { cr.setRect(r.x(), r.y() + d_mMargin, r.width(), r.height() - 2*d_mMargin); // // Draw background // QPainterPath bg_rect = MusECore::roundedPath(cr, xrad, yrad, (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); p->fillPath(bg_rect, d_fillColor); dist1 = int(double(cr.width() - d_thumbLength) * rpos); ipos = cr.x() + dist1; markerPos = ipos + d_thumbHalf; // // Draw empty right side // e_mask.setStart(QPointF(0, cr.y())); e_mask.setFinalStop(QPointF(0, cr.y() + cr.height())); QPainterPath e_rect = MusECore::roundedPath(ipos + d_thumbLength, cr.y(), cr.width() - d_thumbLength - dist1, cr.height(), xrad, yrad, (MusECore::Corner) (MusECore::UpperRight | MusECore::LowerRight) ); p->fillPath(e_rect, QBrush(e_mask)); // // Draw full left side // f_mask.setStart(QPointF(0, cr.y())); f_mask.setFinalStop(QPointF(0, cr.y() + cr.height())); QPainterPath f_rect = MusECore::roundedPath(cr.x(), cr.y(), ipos + 1, cr.height(), xrad, yrad, (MusECore::Corner) (MusECore::LowerLeft | MusECore::UpperLeft) ); p->fillPath(f_rect, QBrush(f_mask)); // // Draw thumb // QPainterPath thumb_rect = MusECore::roundedPath(ipos, r.y(), d_thumbLength, r.height(), 2, 2, (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); thumbGrad.setStart(QPointF(0, cr.y())); thumbGrad.setFinalStop(QPointF(0, cr.y() + cr.height())); p->fillPath(thumb_rect, QBrush(thumbGrad)); // center line p->fillRect(ipos + d_thumbHalf, cr.y(), 1, cr.height(), pal.dark().color()); } else // (d_orient == Qt::Vertical) { cr.setRect(r.x() + d_mMargin, r.y(), r.width() - 2*d_mMargin, r.height()); // // Draw background // QPainterPath bg_rect = MusECore::roundedPath(cr, xrad, yrad, (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); p->fillPath(bg_rect, d_fillColor); dist1 = int(double(cr.height() - d_thumbLength) * (1.0 - rpos)); ipos = cr.y() + dist1; markerPos = ipos + d_thumbHalf; // // Draw empty upper filling // e_mask.setStart(QPointF(cr.x(), 0)); e_mask.setFinalStop(QPointF(cr.x() + cr.width(), 0)); QPainterPath e_rect = MusECore::roundedPath(cr.x(), cr.y(), cr.width(), ipos + 1, xrad, yrad, (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight) ); p->fillPath(e_rect, QBrush(e_mask)); // // Draw lower filling mask // f_mask.setStart(QPointF(cr.x(), 0)); f_mask.setFinalStop(QPointF(cr.x() + cr.width(), 0)); QPainterPath f_rect = MusECore::roundedPath(cr.x(), ipos + d_thumbLength, cr.width(), cr.height() - d_thumbLength - dist1, xrad, yrad, (MusECore::Corner) (MusECore::LowerLeft | MusECore::LowerRight) ); p->fillPath(f_rect, QBrush(f_mask)); // // Draw thumb // QPainterPath thumb_rect = MusECore::roundedPath(r.x(), ipos, r.width(), d_thumbLength, 2, 2, (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight) ); thumbGrad.setStart(QPointF(cr.x(), 0)); thumbGrad.setFinalStop(QPointF(cr.x() + cr.width(), 0)); p->fillPath(thumb_rect, QBrush(thumbGrad)); // center line p->fillRect(cr.x(), ipos + d_thumbHalf, cr.width(), 1, pal.dark().color()); } } //------------------------------------------------------------ //.- //.F ThinSlider::getValue // Determine the value corresponding to a specified // mouse location. // //.u Syntax //.f double ThinSlider::getValue(const QPoint &p) // //.u Parameters //.p const QPoint &p -- // //.u Description // Called by SliderBase //------------------------------------------------------------ double ThinSlider::getValue( const QPoint &p) { double rv; int pos; QRect r = d_sliderRect; if (d_orient == Qt::Horizontal) { if (r.width() <= d_thumbLength) { rv = 0.5 * (minValue() + maxValue()); } else { pos = p.x() - r.x() - d_thumbHalf; rv = minValue() + rint( (maxValue() - minValue()) * double(pos) / double(r.width() - d_thumbLength) / step() ) * step(); } } else { if (r.height() <= d_thumbLength) { rv = 0.5 * (minValue() + maxValue()); } else { pos = p.y() - r.y() - d_thumbHalf; rv = minValue() + rint( (maxValue() - minValue()) * (1.0 - double(pos) / double(r.height() - d_thumbLength)) / step() ) * step(); } } return(rv); } //------------------------------------------------------------ // //.F ThinSlider::moveValue // Determine the value corresponding to a specified mouse movement. // //.u Syntax //.f void Slider::moveValue(const QPoint &deltaP, bool fineMode) // //.u Parameters //.p const QPoint &deltaP -- Change in position //.p bool fineMode -- Fine mode if true, coarse mode if false. // //.u Description // Called by SliderBase // Coarse mode (the normal mode) maps pixels to values depending on range and width, // such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. //------------------------------------------------------------ double ThinSlider::moveValue(const QPoint &deltaP, bool fineMode) { double rv; const QRect r = d_sliderRect; const double val = value(ConvertNone); if((fineMode || borderlessMouse()) && d_scrollMode != ScrDirect) { double newval; if(d_orient == Qt::Horizontal) newval = val + deltaP.x() * step(); else newval = val - deltaP.y() * step(); d_valAccum = newval; // Reset. return newval; } const double min = minValue(ConvertNone); const double max = maxValue(ConvertNone); const double drange = max - min; if(d_orient == Qt::Horizontal) { if(r.width() <= d_thumbLength) rv = 0.5 * (min + max); else { const double dpos = double(deltaP.x()); const double dwidth = double(r.width() - d_thumbLength); const double dval_diff = (drange * dpos) / dwidth; d_valAccum += dval_diff; rv = rint(d_valAccum / step()) * step(); } } else { if(r.height() <= d_thumbLength) rv = 0.5 * (min + max); else { const double dpos = double(-deltaP.y()); const double dheight = double(r.height() - d_thumbLength); const double dval_diff = (drange * dpos) / dheight; d_valAccum += dval_diff; rv = rint(d_valAccum / step()) * step(); } } return(rv); } //------------------------------------------------------------ //.- //.F ThinSlider::getScrollMode // Determine scrolling mode and direction // //.u Syntax //.f void ThinSlider::getScrollMode( const QPoint &p, int &scrollMode, int &direction ) // //.u Parameters //.p const QPoint &p -- point // //.u Description // Called by SliderBase // //------------------------------------------------------------ void ThinSlider::getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& /*modifiers*/, int &scrollMode, int &direction ) { if(cursorHoming() && button == Qt::LeftButton) { if(d_sliderRect.contains(p)) { scrollMode = ScrMouse; direction = 0; int mp = 0; QRect cr; QPoint cp; int ipos,dist1; double rpos; cr = d_sliderRect; rpos = (value() - minValue()) / (maxValue() - minValue()); if(d_orient == Qt::Horizontal) { dist1 = int(double(cr.width() - d_thumbLength) * rpos); ipos = cr.x() + dist1; mp = ipos + d_thumbHalf; p.setX(mp); cp = mapToGlobal( QPoint(mp, p.y()) ); } else { dist1 = int(double(cr.height() - d_thumbLength) * (1.0 - rpos)); ipos = cr.y() + dist1; mp = ipos + d_thumbHalf; p.setY(mp); cp = mapToGlobal( QPoint(p.x(), mp) ); } cursor().setPos(cp.x(), cp.y()); } } else { int currentPos; if (d_orient == Qt::Horizontal) currentPos = p.x(); else currentPos = p.y(); if (d_sliderRect.contains(p)) { if ((currentPos > markerPos - d_thumbHalf) && (currentPos < markerPos + d_thumbHalf)) { scrollMode = ScrMouse; direction = 0; } else { scrollMode = ScrPage; if (((currentPos > markerPos) && (d_orient == Qt::Horizontal)) || ((currentPos <= markerPos) && (d_orient != Qt::Horizontal))) direction = 1; else direction = -1; } } else { scrollMode = ScrNone; direction = 0; } } } //------------------------------------------------------------ //.F ThinSlider::paintEvent // Qt paint event // //.u Syntax //.f void ThinSlider::paintEvent(QPaintEvent *e) //------------------------------------------------------------ void ThinSlider::paintEvent(QPaintEvent* ev) { QPainter p(this); /* Scale is not supported if (p.begin(this)) { if (d_scalePos != None) { p.fillRect(rect(), palette().window()); d_scale.draw(&p); } drawSlider(&p, d_sliderRect); } p.end(); */ drawSlider(&p, d_sliderRect); p.setPen(Qt::red); p.drawRect(ev->rect()); } //------------------------------------------------------------ //.F ThinSlider::resizeEvent // Qt resize event // //.u Parameters //.p QResizeEvent *e // //.u Syntax //.f void ThinSlider::resizeEvent(QResizeEvent *e) //------------------------------------------------------------ void ThinSlider::resizeEvent(QResizeEvent *e) { SliderBase::resizeEvent(e); d_resized = true; QSize s = e->size(); /* Scale is not supported int sliderWidth = d_thumbWidth; // reposition slider if(d_orient == Qt::Horizontal) { switch(d_scalePos) { case Top: d_sliderRect.setRect(this->rect().x() + d_xMargin, this->rect().y() + s.height() - 1 - d_yMargin - sliderWidth, s.width() - 2 * d_xMargin, sliderWidth); d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, d_sliderRect.y() - d_scaleDist, d_sliderRect.width() - d_thumbLength, ScaleDraw::Top); break; case Bottom: d_sliderRect.setRect(this->rect().x() + d_xMargin, this->rect().y() + d_yMargin, s.width() - 2*d_xMargin, sliderWidth); d_scale.setGeometry(d_sliderRect.x() + d_thumbHalf, d_sliderRect.y() + d_sliderRect.height() + d_scaleDist, d_sliderRect.width() - d_thumbLength, ScaleDraw::Bottom); break; default: d_sliderRect.setRect(this->rect().x(), this->rect().x(), s.width(), s.height()); break; } } else // d_orient == Qt::Vertical { switch(d_scalePos) { case Left: d_sliderRect.setRect(this->rect().x() + s.width() - sliderWidth - 1 - d_xMargin, this->rect().y() + d_yMargin, sliderWidth, s.height() - 2 * d_yMargin); d_scale.setGeometry(d_sliderRect.x() - d_scaleDist, d_sliderRect.y() + d_thumbHalf, s.height() - d_thumbLength, ScaleDraw::Left); break; case Right: d_sliderRect.setRect(this->rect().x() + d_xMargin, this->rect().y() + d_yMargin, sliderWidth, s.height() - 2* d_yMargin); d_scale.setGeometry(this->rect().x() + d_sliderRect.width() + d_scaleDist, d_sliderRect.y() + d_thumbHalf, s.height() - d_thumbLength, ScaleDraw::Right); break; default: d_sliderRect.setRect(this->rect().x(), this->rect().x(), s.width(), s.height()); break; } } */ d_sliderRect.setRect(this->rect().x(), this->rect().y(), s.width(), s.height()); } //------------------------------------------------------------ //.- //.F ThinSlider::valueChange // Notify change of value // //.u Syntax //.f void ThinSlider::valueChange() // //------------------------------------------------------------ void ThinSlider::valueChange() { update(); SliderBase::valueChange(); } //------------------------------------------------------------ //.- //.F ThinSlider::rangeChange // Notify change of range // //.u Description // //.u Syntax //.f void ThinSlider::rangeChange() // //------------------------------------------------------------ void ThinSlider::rangeChange() { if (!hasUserScale()) d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); SliderBase::rangeChange(); repaint(); } //------------------------------------------------------------ // //.F ThinSlider::setMargins // Set distances between the widget's border and // internals. // //.u Syntax //.f void ThinSlider::setMargins(int hor, int vert) // //.u Parameters //.p int hor, int vert -- Margins // //------------------------------------------------------------ void ThinSlider::setMargins(int hor, int vert) { d_xMargin = MusECore::qwtMax(0, hor); d_yMargin = MusECore::qwtMax(0, vert); resize(this->size()); } //------------------------------------------------------------ // //.F ThinSlider::sizeHint // Return a recommended size // //.u Syntax //.f QSize ThinSlider::sizeHint() const // //.u Note // The return value of sizeHint() depends on the font and the // scale. //------------------------------------------------------------ QSize ThinSlider::sizeHint() const { /* Scale is not supported int w = 40; int h = 40; QPainter p; int msWidth = 0, msHeight = 0; if (d_scalePos != None) { if (p.begin(this)) { msWidth = d_scale.maxWidth(&p, FALSE); msHeight = d_scale.maxHeight(&p); } p.end(); switch(d_orient) { case Qt::Vertical: w = 2*d_xMargin + d_thumbWidth + msWidth + d_scaleDist + 2; break; case Qt::Horizontal: h = 2*d_yMargin + d_thumbWidth + msHeight + d_scaleDist; break; } } else { // no scale switch(d_orient) { case Qt::Vertical: w = 16; break; case Qt::Horizontal: h = 16; break; } } */ return QSize(horizontal_hint, vertical_hint); } //--------------------------------------------------------- // setOrientation //--------------------------------------------------------- void ThinSlider::setOrientation(Qt::Orientation o) { d_orient = o; /* Scale is not supported ScaleDraw::OrientationX so = ScaleDraw::Bottom; switch(d_orient) { case Qt::Vertical: if (d_scalePos == Right) so = ScaleDraw::Right; else so = ScaleDraw::Left; break; case Qt::Horizontal: if (d_scalePos == Bottom) so = ScaleDraw::Bottom; else so = ScaleDraw::Top; break; } d_scale.setGeometry(0, 0, 40, so); if (d_orient == Qt::Vertical) setMinimumSize(10,20); else setMinimumSize(20,10); QRect r = geometry(); setGeometry(r.x(), r.y(), r.height(), r.width()); update(); */ switch(d_orient) { case Qt::Vertical: horizontal_hint = 32; vertical_hint = 64; break; case Qt::Horizontal: horizontal_hint = 64; vertical_hint = 32; break; } } Qt::Orientation ThinSlider::orientation() const { return d_orient; } double ThinSlider::lineStep() const { return 1.0; } double ThinSlider::pageStep() const { return 1.0; } void ThinSlider::setLineStep(double) { } void ThinSlider::setPageStep(double) { } } // namespace MusEGui muse-4.0.0/src/muse/components/thinslider.h000066400000000000000000000066001404105311000207160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: slider.h,v 1.3.2.2 2008/01/19 13:33:47 wschweer Exp $ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SLIDER_H__ #define __SLIDER_H__ #include "sclif.h" #include "sliderbase.h" #include "scldraw.h" namespace MusEGui { //--------------------------------------------------------- // Slider //--------------------------------------------------------- class ThinSlider : public SliderBase, public ScaleIf { Q_OBJECT public: enum ScalePos { None, Left, Right, Top, Bottom }; private: Q_PROPERTY( double lineStep READ lineStep WRITE setLineStep ) Q_PROPERTY( double pageStep READ pageStep WRITE setPageStep ) Q_PROPERTY( Qt::Orientation orientation READ orientation WRITE setOrientation ) QRect d_sliderRect; int d_thumbLength; int d_thumbHalf; int d_thumbWidth; int d_scaleDist; int d_xMargin; int d_yMargin; int d_mMargin; QColor d_fillColor; bool d_resized; bool d_autoResize; double d_scaleStep; Qt::Orientation d_orient; ScalePos d_scalePos; int d_bgStyle; int markerPos; uint vertical_hint; uint horizontal_hint; void drawHsBgSlot(QPainter *, const QRect&, const QRect&,const QBrush&); void drawVsBgSlot(QPainter *, const QRect&, const QRect&,const QBrush&); protected: virtual void drawSlider (QPainter *p, const QRect &r); double getValue(const QPoint &p); // Determine the value corresponding to a specified mouse movement. double moveValue(const QPoint& /*deltaP*/, bool /*fineMode*/ = false); void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction); virtual void resizeEvent(QResizeEvent *e); virtual void paintEvent (QPaintEvent *e); void valueChange(); void rangeChange(); void scaleChange(); void fontChange(const QFont &oldFont); public: ThinSlider(QWidget *parent, const char *name = 0, Qt::Orientation orient = Qt::Vertical, ScalePos scalePos = None, QColor fillColor = QColor(100, 100, 255)); ~ThinSlider(); void setThumbLength(int l); void setThumbWidth(int w); void setOrientation(Qt::Orientation o); Qt::Orientation orientation() const; double lineStep() const; double pageStep() const; void setLineStep(double); void setPageStep(double); void setMargins(int x, int y); virtual QSize sizeHint() const; void setSizeHint(uint w, uint h); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/tools.cpp000066400000000000000000000200761404105311000202470ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tools.cpp,v 1.2 2004/04/28 21:56:13 spamatica Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "tools.h" #include #include #include #include #include #include "icons.h" #include "shortcuts.h" #include "globals.h" #include "app.h" namespace MusEGui { //--------------------------------------------------------- // EditToolBar //--------------------------------------------------------- // Icon / tooltip / whatsthis const QVector EditToolBar::toolList = { {&pointerIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Pointer"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Pointer tool:\n" "With the pointer tool you can:\n" " select parts\n" " move parts\n" " copy parts") }, {&pencilIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Pencil"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Pencil tool:\n" "With the pencil tool you can:\n" " create new parts\n" " modify length of parts") }, {&deleteIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Eraser"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Delete tool:\n" "With the delete tool you can delete parts") }, {&cutterIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Cutter"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Cut tool:\n" "With the cut tool you can split a part") }, {&glueIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Glue"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Glue tool:\n" "With the glue tool you can glue two parts")}, {&cursorIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Range"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Range tool") }, {&handIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Pan"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Panning tool") }, {&zoomIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Zoom"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Zoom tool") }, {&drawIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Draw"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Drawing tool") }, {&mutePartsIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Mute Parts"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Muting tool:\n" "Click on part to mute/unmute") }, {&drawIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Edit Automation"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Manipulate automation") }, {&cursorIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Cursor"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Cursor (tracker mode) tool:\n" "With the cursor tool you can:\n" " navigate with arrow keys\n" " use VBNM to place notes\n" " change step with 0 and 9") }, {&audioStretchIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Stretch"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Stretch tool") }, {&audioResampleIconSVG, QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Sample Rate"), QT_TRANSLATE_NOOP("MusEGui::EditToolBar", "Select Sample rate tool") } }; const QMap EditToolBar::toolShortcuts = { {PointerTool , SHRT_TOOL_POINTER}, {PencilTool , SHRT_TOOL_PENCIL}, {RubberTool , SHRT_TOOL_RUBBER}, {CutTool , SHRT_TOOL_SCISSORS}, {GlueTool , SHRT_TOOL_GLUE}, {RangeTool , SHRT_TOOL_RANGE}, {PanTool , SHRT_TOOL_PAN}, {ZoomTool , SHRT_TOOL_ZOOM}, {DrawTool , SHRT_TOOL_LINEDRAW}, {MuteTool , SHRT_TOOL_MUTE}, {AutomationTool, SHRT_TOOL_LINEDRAW}, {CursorTool , SHRT_TOOL_CURSOR}, {StretchTool , SHRT_TOOL_STRETCH}, {SamplerateTool, SHRT_TOOL_SAMPLERATE} }; EditToolBar::EditToolBar(QWidget* parent, int tools, const char*) : QToolBar(tr("Edit Tools"), parent) { setObjectName("Edit Tools"); actionGroup = new QActionGroup(parent); // Parent needed. actionGroup->setExclusive(true); bool first = true; for (unsigned i = 0; i < static_cast(toolList.size()); ++i) { if ((tools & (1 << i))==0) continue; const ToolB* t = &toolList[static_cast(i)]; QAction* a = new QAction(tr(t->tip).toLatin1().data(), actionGroup); a->setData(1 << i); a->setCheckable(true); a->setIcon(**(t->icon)); a->setShortcut(shortcuts[toolShortcuts[1 << i]].key); a->setToolTip(tr(t->tip) + " (" + a->shortcut().toString() + ")"); a->setWhatsThis(tr(t->ltip)); if (first) { a->setChecked(true); first = false; } } actionGroup->setVisible(true); //action->addTo(this); // Note: Does not take ownership. addActions(actionGroup->actions()); connect(actionGroup, SIGNAL(triggered(QAction*)), SLOT(toolChanged(QAction*))); } void EditToolBar::configChanged() { for (const auto& a : actionGroup->actions()) { if (toolShortcuts.contains(a->data().toInt())) { a->setShortcut(shortcuts[toolShortcuts[a->data().toInt()]].key); int idx = a->toolTip().lastIndexOf('('); if (idx != -1) a->setToolTip(a->toolTip().left(idx + 1) + a->shortcut().toString() + ")"); } else printf("Error: EditToolBar configChanged: Tool ID doesn't exist: %d\n", a->data().toInt()); } } //--------------------------------------------------------- // toolChanged //--------------------------------------------------------- void EditToolBar::toolChanged(QAction* action) { emit toolChanged(action->data().toInt()); } //--------------------------------------------------------- // ~EditToolBar //--------------------------------------------------------- EditToolBar::~EditToolBar() { } //--------------------------------------------------------- // set //--------------------------------------------------------- void EditToolBar::set(int id) { for (const auto& action : actionGroup->actions()) { if (action->data().toInt() == id) { action->setChecked(true); toolChanged(action); return; } } } //--------------------------------------------------------- // curTool //--------------------------------------------------------- int EditToolBar::curTool() { for (const auto& action : actionGroup->actions()) { if (action->isChecked()) return action->data().toInt(); } return -1; } } // namespace MusEGui muse-4.0.0/src/muse/components/tools.h000066400000000000000000000042461404105311000177150ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tools.h,v 1.1.1.1 2003/10/27 18:54:49 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TOOLS_H__ #define __TOOLS_H__ #include #include class QAction; class QPixmap; class QWidget; class QIcon; namespace MusEGui { enum Tool { PointerTool=1, PencilTool=2, RubberTool=4, CutTool=8, GlueTool=16, RangeTool=32, PanTool=64, ZoomTool=128, DrawTool=256, MuteTool=512, AutomationTool=1024, CursorTool=2048, StretchTool=4096, SamplerateTool=8192 }; struct ToolB { QIcon** icon; const char* tip; const char* ltip; }; //extern const QVector toolList; //--------------------------------------------------------- // EditToolBar //--------------------------------------------------------- class EditToolBar : public QToolBar { Q_OBJECT QActionGroup* actionGroup; private slots: void toolChanged(QAction* action); signals: void toolChanged(int); public slots: void set(int id); void configChanged(); public: EditToolBar(QWidget* /*parent*/, int /*tools*/, const char* name = 0); // Needs a parent ! ~EditToolBar(); int curTool(); static const QMap toolShortcuts; static const QVector toolList; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/trackinfo_layout.cpp000066400000000000000000000244131404105311000224630ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // trackinfo_layout.cpp // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 #include #include "trackinfo_layout.h" #include "splitter.h" #include "scrollbar.h" #include "widget_stack.h" #include "scrollscale.h" #include "ttoolbutton.h" namespace MusEGui { TrackInfoLayout::TrackInfoLayout(QWidget *parent, WidgetStack* stack, ScrollBar* sb, Splitter* splitter) : QHBoxLayout(parent), _stack(stack), _sb(sb), _splitter(splitter) { _inSetGeometry = false; setContentsMargins(0, 0, 0, 0); setSpacing(0); _sbShowPending = false; _stackLi = new QWidgetItem(_stack); _sbLi = new QWidgetItem(_sb); addItem(_stackLi); addItem(_sbLi); } TrackInfoLayout::~TrackInfoLayout() { } //--------------------------------------------------------- // setGeometry //--------------------------------------------------------- void TrackInfoLayout::setGeometry(const QRect &rect) { QHBoxLayout::setGeometry(rect); // return; // TODO REMOVE Tim. Was experiment? Or not needed now? // // // // if(_inSetGeometry) // return; // // int w = rect.width(); // int h = rect.height(); // // QSize s0; // QWidget* widget = _stack->visibleWidget(); // if(widget) { // s0 = widget->minimumSizeHint(); // if (!s0.isValid()) // widget has no geometry management // s0 = widget->size(); // } // else // s0 = _stack->minimumSizeHint(); // // int range = s0.height() - h; // if (range < 0) // range = 0; // // fprintf(stderr, "TrackInfoLayout::setGeometry sb w:%d visible:%d split count:%d w:%d h:%d s0 height:%d range:%d\n", // // _sb->width(), _sb->isVisible(), _splitter->count(), w, h, s0.height(), range); // // if (range) // { // _sb->blockSignals(true); // _sb->setMaximum(range); // _sb->blockSignals(false); // } // // const bool vis = range != 0; // // // Was a show pending and the scrollbar is now visible? Reset the pending flag. // if(_sbShowPending && _sb->isVisible()) // _sbShowPending = false; // // if(_sb->isVisible() != vis) // { // if(_sb->isVisible()) // { // int sw = w - _sb->width(); // if(sw < 0) // sw = 0; // _sb->setVisible(false); // if(_splitter) // { // //fprintf(stderr, "TrackInfoLayout::setGeometry hide sb: split pos:%d\n", sw); // _inSetGeometry = true; // _splitter->setPosition(1, sw); // FIXME: Causes too wide on first startup, also when maximizing. // _inSetGeometry = false; // } // _stackLi->setGeometry(QRect(0, 0, sw, h)); // //fprintf(stderr, "TrackInfoLayout::setGeometry hide sb: widget:%p w:%d\n", widget, sw); // if(widget) // { // // QSize r(sw, y2 < s0.height() ? s0.height() : y2); // // //fprintf(stderr, "TrackInfoLayout::setGeometry hide sb: widget w:%d\n", // // // r.width()); // // //widget->setGeometry(0, -_sb->value(), r.width(), r.height()); // // widget->setGeometry(0, 0, r.width(), r.height()); // widget->move(0, 0); // } // } // else // { // // If an ancestor is NOT visible this will not happen until the ancestor becomes visible. // // Simply reading isVisible() immediately afterwards (below) would return FALSE. // _sb->setVisible(true); // _sbShowPending = true; // // const int sbw = _sb->isVisible() ? _sb->width() : _sbLi->sizeHint().width(); // // if(_splitter) // { // //fprintf(stderr, "TrackInfoLayout::setGeometry show sb: split pos:%d\n", // // w + sbw); // _inSetGeometry = true; // _splitter->setPosition(1, w + sbw); // FIXME: Causes too wide on first startup, also when maximizing. // _inSetGeometry = false; // } // _stackLi->setGeometry(QRect(0, 0, w, h)); // //fprintf(stderr, "TrackInfoLayout::setGeometry show sb: widget:%p w:%d\n", widget, w); // // _stackLi->setGeometry(QRect(0, 0, w, y2 < s0.height() ? s0.height() : y2)); // if(widget) // { // // QSize r(w, y2 < s0.height() ? s0.height() : y2); // // widget->setGeometry(0, -_sb->value(), r.width(), r.height()); // widget->move(0, -_sb->value()); // } // } // } // else // { // int ww = w; // if(_sb->isVisible() || _sbShowPending) // ww -= _sb->isVisible() ? _sb->width() : _sbLi->sizeHint().width(); // // //fprintf(stderr, "TrackInfoLayout::setGeometry not show/hide sb: widget:%p w:%d\n", widget, ww); // _stackLi->setGeometry(QRect(0, 0, ww, h)); // // _stackLi->setGeometry(QRect(0, 0, ww, h < s0.height() ? s0.height() : h)); // if(widget) // { // // QSize r(ww, y2 < s0.height() ? s0.height() : y2); // // widget->setGeometry(0, -_sb->value(), r.width(), r.height()); // if(_sb->isVisible() || _sbShowPending) // widget->move(0, -_sb->value()); // } // } // // if(_sb->isVisible() || _sbShowPending) // { // const int sbw = _sb->isVisible() ? _sb->width() : _sbLi->sizeHint().width(); // int sbx = w + (_sb->isVisible() ? -sbw : sbw); // if(sbx < 0) // sbx = 0; // //fprintf(stderr, "TrackInfoLayout::setGeometry: sb visible or pending: setting _sbLi: x:%d w:%d\n", sbx, sbw); // _sbLi->setGeometry(QRect(sbx, 0, sbw, h)); // } // else // { // //fprintf(stderr, "TrackInfoLayout::setGeometry: sb not visible nor pending: setting _sbLi: x:%d w:%d\n", w, 0); // _sbLi->setGeometry(QRect(w, 0, 0, h)); // } } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize TrackInfoLayout::sizeHint() const { int w = _stack->sizeHint().width(); // int w = _stack->minimumSizeHint().width(); if(_sb->isVisible()) w += _sbLi->sizeHint().width(); return QSize(qMax(w, 50), 50); // return QSize(150, 300); } //--------------------------------------------------------- // TrackInfoWidget //--------------------------------------------------------- TrackInfoWidget::TrackInfoWidget(QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f) { _stack = new WidgetStack(this, "trackInfoStack", WidgetStack::VisibleHint); _scrollBar = new ScrollBar(Qt::Vertical, true, this); _scrollBar->setObjectName("infoScrollBar"); _trackInfoLayout = new TrackInfoLayout(this, _stack, _scrollBar); connect(_scrollBar, SIGNAL(valueChanged(int)), SLOT(scrollValueChanged(int))); connect(_stack, SIGNAL(redirectWheelEvent(QWheelEvent*)), _scrollBar, SLOT(redirectedWheelEvent(QWheelEvent*))); } void TrackInfoWidget::scrollValueChanged(int val) { if(_stack->visibleWidget()) _stack->visibleWidget()->move(0, -val); } void TrackInfoWidget::doResize(const QSize& newSize) { if(QWidget* widget = _stack->visibleWidget()) { QSize wsz = widget->minimumSizeHint(); if(!wsz.isValid()) wsz = widget->minimumSize(); QSize sz(newSize); if(sz.width() < wsz.width()) sz.setWidth(wsz.width()); if(sz.height() < wsz.height()) sz.setHeight(wsz.height()); if(_scrollBar) { int range = sz.height() - height(); if(range < 0) range = 0; if(range) { //fprintf(stderr, "TrackInfoWidget::doResize sb range:%d\n", range); _scrollBar->blockSignals(true); _scrollBar->setMaximum(range); _scrollBar->blockSignals(false); } const bool vis = range != 0; // We can't do this check. An ancestor might not be visible yet. //if(_scrollBar->isVisible() != vis) { //fprintf(stderr, "TrackInfoWidget::doResize before setting sb visible:%d\n", vis); _scrollBar->setVisible(vis); //fprintf(stderr, "TrackInfoWidget::doResize after setting sb visible:%d\n", vis); } } } } void TrackInfoWidget::doMove() { if(QWidget* widget = _stack->visibleWidget()) { if(_scrollBar->isVisible()) widget->move(0, -_scrollBar->value()); else widget->move(0, 0); } } void TrackInfoWidget::resizeEvent(QResizeEvent* e) { e->ignore(); QWidget::resizeEvent(e); //doResize(e->size()); doResize(_stack->size()); doMove(); } void TrackInfoWidget::raiseWidget(int idx) { _stack->raiseWidget(idx); doResize(_stack->size()); doMove(); _trackInfoLayout->invalidate(); _trackInfoLayout->activate(); _trackInfoLayout->update(); } void TrackInfoWidget::addWidget(QWidget* w, unsigned int idx) { _stack->addWidget(w, idx); doResize(_stack->size()); } QWidget* TrackInfoWidget::getWidget(unsigned int idx) { return _stack->getWidget(idx); } QWidget* TrackInfoWidget::visibleWidget() const { return _stack->visibleWidget(); } int TrackInfoWidget::curIdx() const { return _stack->curIdx(); } } // namespace MusEGui muse-4.0.0/src/muse/components/trackinfo_layout.h000066400000000000000000000062121404105311000221250ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // trackinfo_layout.h // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TRACKINFO_LAYOUT_H__ #define __TRACKINFO_LAYOUT_H__ #include #include #include #include class QLayoutItem; class QSize; class QRect; namespace MusEGui { class Splitter; class ScrollBar; class WidgetStack; //class CompactToolButton; //--------------------------------------------------------- // TrackInfoLayout // For laying out a widget stack and scrollbar which always appears // to the right of the stack instead of intruding into its space. // An optional Splitter will be resized when the scrollbar appears. //--------------------------------------------------------- class TrackInfoLayout : public QHBoxLayout { Q_OBJECT bool _inSetGeometry; WidgetStack* _stack; ScrollBar* _sb; QLayoutItem* _stackLi; QLayoutItem* _sbLi; bool _sbShowPending; // This is not actually in the layout, but used and/or adjusted anyway. Splitter* _splitter; public: TrackInfoLayout(QWidget *parent, WidgetStack* stack, ScrollBar* sb, Splitter* splitter = 0); virtual ~TrackInfoLayout(); virtual QSize sizeHint() const; // virtual QSize minimumSize() const; // virtual QSize maximumSize() const; virtual void setGeometry(const QRect &rect); }; //--------------------------------------------------------- // TrackInfoWidget // Widget for containing a trackinfo layout. //--------------------------------------------------------- class TrackInfoWidget : public QWidget { Q_OBJECT private: WidgetStack* _stack; ScrollBar* _scrollBar; TrackInfoLayout* _trackInfoLayout; void doResize(const QSize&); void doMove(); private slots: void scrollValueChanged(int); protected: //virtual void wheelEvent(QWheelEvent* e); virtual void resizeEvent(QResizeEvent*); public: TrackInfoWidget(QWidget* parent = 0, Qt::WindowFlags f = Qt::Widget); // Wrappers/catchers for stack functions: void raiseWidget(int idx); void addWidget(QWidget* w, unsigned int idx); QWidget* getWidget(unsigned int idx); QWidget* visibleWidget() const; int curIdx() const; }; } #endif muse-4.0.0/src/muse/components/tracks_duplicate.cpp000066400000000000000000000041471404105311000224310ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // tracks_duplicate.cpp // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "tracks_duplicate.h" namespace MusEGui { DuplicateTracksDialog::DuplicateTracksDialog( bool audio, bool /*midi*/, bool newdrum, QWidget* parent, bool copies, bool allRoutes, bool defaultRoutes, bool noParts, bool duplicateParts, bool copyParts, bool cloneParts ) : QDialog(parent) { setupUi(this); standardCtrlsCheckBox->setVisible(audio); pluginsCheckBox->setVisible(audio); pluginCtrlsCheckBox->setVisible(audio); copyDrumlistCheckBox->setVisible(newdrum); copiesSpinBox->setVisible(copies); copiesLabel->setVisible(copies); allRoutesRadioButton->setVisible(allRoutes); defaultRoutesRadioButton->setVisible(defaultRoutes); routesFrame->setVisible(allRoutes || defaultRoutes); noPartsRadioButton->setVisible(noParts); duplicatePartsRadioButton->setVisible(duplicateParts); copyPartsRadioButton->setVisible(copyParts); clonePartsRadioButton->setVisible(cloneParts); partsFrame->setVisible(noParts || duplicateParts || copyParts || cloneParts); connect(okPushButton, SIGNAL(clicked()), this, SLOT(accept())); connect(cancelPushButton, SIGNAL(clicked()), this, SLOT(reject())); } } // namespace MusEGui muse-4.0.0/src/muse/components/tracks_duplicate.h000066400000000000000000000045251404105311000220760ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // tracks_duplicate.h // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TRACKS_DUPLICATE_H__ #define __TRACKS_DUPLICATE_H__ #include "ui_tracks_duplicate_base.h" namespace MusEGui { class DuplicateTracksDialog : public QDialog, public Ui::DuplicateTracksBase { Q_OBJECT protected: protected slots: //void accept(); public slots: //int exec(); public: DuplicateTracksDialog( bool audio, bool midi, bool newdrum, QWidget* parent = 0, bool copies = true, bool allRoutes = true, bool defaultRoutes = true, bool noParts = true, bool duplicateParts = true, bool copyParts = true, bool cloneParts = true ); int copies() const { return copiesSpinBox->value(); } bool copyStdCtrls() const { return standardCtrlsCheckBox->isChecked(); } bool copyPlugins() const { return pluginsCheckBox->isChecked(); } bool copyPluginCtrls() const { return pluginCtrlsCheckBox->isChecked(); } bool allRoutes() const { return allRoutesRadioButton->isChecked(); } bool defaultRoutes() const { return defaultRoutesRadioButton->isChecked(); } bool duplicateParts() const { return duplicatePartsRadioButton->isChecked(); } bool copyParts() const { return copyPartsRadioButton->isChecked(); } bool cloneParts() const { return clonePartsRadioButton->isChecked(); } bool copyDrumlist() const { return copyDrumlistCheckBox->isChecked(); } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/tracks_duplicate_base.ui000066400000000000000000000143241404105311000232540ustar00rootroot00000000000000 DuplicateTracksBase 0 0 327 472 Duplicate tracks 1 100 Number of copies Qt::Horizontal 40 20 Qt::Horizontal QFrame::StyledPanel QFrame::Raised Copy all routes true Default routing No routes Qt::Horizontal QFrame::StyledPanel QFrame::Raised Duplicate all parts Make independent copies of all parts false Make clones of all parts false No parts true Copy drumlist Qt::Horizontal Copy standard (vol, pan) and synth controllers true Copy effects rack plugins false Copy plugin controllers false Qt::Vertical 20 27 Qt::Horizontal 40 20 Ok true Cancel muse-4.0.0/src/muse/components/ttoolbar.cpp000066400000000000000000000027051404105311000207340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ttoolbar.cpp,v 1.1.1.1 2003/10/27 18:54:46 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "globals.h" //--------------------------------------------------------- // syncChanged //--------------------------------------------------------- void syncChanged(bool flag) { MusEGlobal::startAction->setEnabled(!flag); MusEGlobal::forwardAction->setEnabled(!flag); MusEGlobal::rewindAction->setEnabled(!flag); MusEGlobal::stopAction->setEnabled(!flag); MusEGlobal::playAction->setEnabled(!flag); } muse-4.0.0/src/muse/components/ttoolbar.h000066400000000000000000000020141404105311000203720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ttoolbar.h,v 1.1.1.1 2003/10/27 18:54:52 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= extern void syncChanged(bool flag); muse-4.0.0/src/muse/components/ttoolbutton.cpp000066400000000000000000000152521404105311000215040ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ttoolbutton.cpp,v 1.1 2004/02/21 16:53:50 wschweer Exp $ // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; 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 "ttoolbutton.h" #include "gconfig.h" #include "icons.h" namespace MusEGui { //--------------------------------------------------------- // TransparentToolButton //--------------------------------------------------------- //--------------------------------------------------------- // drawButton //--------------------------------------------------------- void TransparentToolButton::drawButton(QPainter* p) { int w = width(); int h = height(); QIcon::Mode mode = isEnabled() ? QIcon::Normal : QIcon::Disabled; QIcon::State state = isChecked() ? QIcon::On : QIcon::Off; const QPixmap pm(icon().pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize), mode, state)); p->drawPixmap(QPoint((w - pm.width())/2, (h - pm.height())/2), pm); } //--------------------------------------------------------- // CompactToolButton //--------------------------------------------------------- CompactToolButton::CompactToolButton(QWidget* parent, const QIcon& icon, bool hasFixedIconSize, bool drawFlat, const char* name) : QToolButton(parent), _icon(icon), _hasFixedIconSize(hasFixedIconSize), _drawFlat(drawFlat) { setObjectName(name); _blinkPhase = false; _scaleDownIcon = false; } QSize CompactToolButton::sizeHint() const { const QSize isz = iconSize(); int rw, rh; // If there is text, use the font metrics to determine a size. // Otherwise, grab the default size hint. if(text().isEmpty()) { rw = 14; rh = 14; } else { // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 rw = fontMetrics().horizontalAdvance(text()); #else rw = fontMetrics().width(text()); #endif rh = fontMetrics().lineSpacing() + 5; } const QMargins mg = contentsMargins(); const int iw = isz.width() + mg.left() + mg.right(); // + 2; const int ih = isz.height() + mg.top() + mg.bottom(); // + 2; // If we are using fixed icon size and the icon is bigger than the starting size, // use the icon size instead. const int w = (_hasFixedIconSize && iw > rw) ? iw : rw; const int h = (_hasFixedIconSize && ih > rh) ? ih : rh; return QSize(w, h); } void CompactToolButton::setHasFixedIconSize(bool v) { _hasFixedIconSize = v; updateGeometry(); } void CompactToolButton::setDrawFlat(bool v) { _drawFlat = v; update(); } void CompactToolButton::setIcon(const QIcon & icon) { _icon = icon; update(); } void CompactToolButton::setBlinkPhase(bool v) { if(_blinkPhase == v) return; _blinkPhase = v; if(isEnabled()) update(); } void CompactToolButton::paintEvent(QPaintEvent* ev) { if(!_drawFlat) QToolButton::paintEvent(ev); QIcon::Mode mode; if(isEnabled()) mode = hasFocus() ? QIcon::Selected : QIcon::Normal; else mode = QIcon::Disabled; QIcon::State state = (isChecked() && (!_blinkPhase || !isEnabled())) ? QIcon::On : QIcon::Off; QPainter p(this); const QRect cont_r = contentsRect(); if(_hasFixedIconSize) { const QSize sz = iconSize(); // Scale the icon down if it doesn't fit. const int iw = (_scaleDownIcon && sz.width() > cont_r.width()) ? cont_r.width() : sz.width(); const int ih = (_scaleDownIcon && sz.height() > cont_r.height()) ? cont_r.height() : sz.height(); const int x = cont_r.x() + (cont_r.width() - iw) / 2; const int y = cont_r.y() + (cont_r.height() - ih) / 2; _icon.paint(&p, x, y, iw, ih, Qt::AlignCenter, mode, state); } else _icon.paint(&p, cont_r, Qt::AlignCenter, mode, state); // TODO Bah! Just want a mouse-over rectangle for flat mode but some styles do this or that but not the other thing. // if(const QStyle* st = style()) // { // st = st->proxy(); // // QStyleOptionToolButton o; // // initStyleOption(&o); // // o.rect = rect(); // // //o.state |= QStyle::State_MouseOver; // // o.state = QStyle::State_Active | // // QStyle::State_Enabled | // // QStyle::State_AutoRaise | // This is required to get rid of the panel. // // QStyle::State_MouseOver; // // st->drawPrimitive(QStyle::PE_PanelButtonTool, &o, &p); // // // QStyleOptionFrame o; // // //initStyleOption(&o); // // o.rect = rect(); // // o.features = QStyleOptionFrame::Rounded; // // o.frameShape = QFrame::Box; // // o.lineWidth = 2; // // o.midLineWidth = 4; // // o.state |= QStyle::State_MouseOver; // // st->drawPrimitive(QStyle::PE_Frame, &o, &p); // // // QStyleOptionFocusRect o; // //o.QStyleOption::operator=(option); // //o.rect = st->subElementRect(QStyle::SE_ItemViewItemFocusRect, &option); // o.rect = rect(); // o.state |= QStyle::State_KeyboardFocusChange; // o.state |= QStyle::State_Item | // QStyle::State_Active | // QStyle::State_Enabled | // QStyle::State_HasFocus | // // //QStyle::State_Raised | // QStyle::State_Sunken | // // QStyle::State_Off | // //QStyle::State_On | // // QStyle::State_Selected | // // //QStyle::State_AutoRaise | // This is required to get rid of the panel. // // QStyle::State_MouseOver; // // // QPalette::ColorGroup cg = // // (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; // // o.backgroundColor = option.palette.color(cg, // // (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); // st->drawPrimitive(QStyle::PE_FrameFocusRect, &o, &p); // // } ev->accept(); } } // namespace MusEGui muse-4.0.0/src/muse/components/ttoolbutton.h000066400000000000000000000053641404105311000211540ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ttoolbutton.h,v 1.1 2004/02/21 16:53:51 wschweer Exp $ // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TTOOLBUTTON_H__ #define __TTOOLBUTTON_H__ #include #include #include namespace MusEGui { //--------------------------------------------------------- // TransparentToolButton //--------------------------------------------------------- class TransparentToolButton : public QToolButton { Q_OBJECT virtual void drawButton(QPainter*); public: TransparentToolButton(QWidget* parent, const char* name = 0) : QToolButton(parent) {setObjectName(name);} }; //--------------------------------------------------------- // CompactToolButton // Supports two icons. //--------------------------------------------------------- class CompactToolButton : public QToolButton { Q_OBJECT private: QIcon _icon; bool _scaleDownIcon; bool _hasFixedIconSize; bool _drawFlat; bool _blinkPhase; protected: virtual void paintEvent(QPaintEvent*); public: CompactToolButton( QWidget* parent = nullptr, const QIcon& icon = QIcon(), bool hasFixedIconSize = true, bool drawFlat = false, const char* name = nullptr); bool hasFixedIconSize() const { return _hasFixedIconSize; } void setHasFixedIconSize(bool v); bool drawFlat() const { return _drawFlat; } void setDrawFlat(bool v); bool scaleDownIcon() const { return _scaleDownIcon; } void setScaleDownIcon(bool v) { _scaleDownIcon = v; } // If _hasFixedIconSize is true, this relies on iconSize(). Be sure to set iconSize to the desired value. virtual QSize sizeHint() const; void setIcon(const QIcon & icon); bool blinkPhase() const { return _blinkPhase; } void setBlinkPhase(bool v); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/unusedwavefiles.cpp000066400000000000000000000105451404105311000223200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/unusedwavefiles.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; 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 #include "unusedwavefiles.h" #include "ui_unusedwavefiles.h" #include "globals.h" #include "app.h" namespace MusEGui { UnusedWaveFiles::UnusedWaveFiles(QWidget *parent) : QDialog(parent), ui(new Ui::UnusedWaveFiles) { ui->setupUi(this); ui->currentProjRadioButton->setChecked(true); connect(ui->currentProjRadioButton, SIGNAL(clicked()), SLOT(findWaveFiles())); connect(ui->allProjRadioButton, SIGNAL(clicked()), SLOT(findWaveFiles())); findWaveFiles(); } void UnusedWaveFiles::findWaveFiles() { ui->filelistWidget->clear(); //printf("MusEGlobal::museProject =%s\n", MusEGlobal::museProject.toLatin1().data()); // QFileInfo proj(MusEGlobal::museProject); // QString projPath = proj.absolutePath(); QDir dir(MusEGlobal::museProject); QStringList filter; filter.append("*.wav"); filter.append("*.ogg"); filter.append("*.flac"); allWaveFiles= dir.entryList(filter); if (allWaveFiles.count() == 0) return; // get med files QStringList medFiles; if (ui->currentProjRadioButton->isChecked()) { medFiles.append(MusEGlobal::muse->projectFileInfo().fileName()); } else { //printf("get ALLL *.med files!\n"); QStringList medFilter("*.med"); medFiles = dir.entryList(medFilter); } foreach (QString medFile, medFiles) { QString fname = MusEGlobal::museProject+"/"+ medFile; //printf("fopen %s\n", fname.toLatin1().data()); FILE *fp =fopen(fname.toLatin1().data(),"r"); QTextStream fileContent(fp); while (!fileContent.atEnd()) { QString line = fileContent.readLine(); if (line.contains(".wav") || line.contains(".ogg") || line.contains(".flac")) { // optimization foreach (QString wav, allWaveFiles) { //printf("checking wav [%s]\n", wav.toLatin1().data() ); if (line.contains(wav)) { //int beforeSize=allWaveFiles.size(); allWaveFiles.removeOne(wav); //printf("removed one from list, %d %d\n", beforeSize, allWaveFiles.size()); break; } } } } fclose(fp); } ui->filelistWidget->addItems(allWaveFiles); update(); } UnusedWaveFiles::~UnusedWaveFiles() { delete ui; } void UnusedWaveFiles::accept() { int ret = QMessageBox::question(this,"Move files", "Are you sure you want to move away the unused files?", QMessageBox::Ok, QMessageBox::Cancel); if (ret == QMessageBox::Ok) { QDir currDir(MusEGlobal::museProject); currDir.mkdir("unused"); foreach(QString file, allWaveFiles) { QFile::rename(MusEGlobal::museProject+ "/"+file, MusEGlobal::museProject + "/unused/" +file); // move the wca file if it exists QFileInfo wf(MusEGlobal::museProject + "/" + file); if (QFile::exists(MusEGlobal::museProject + "/" + wf.baseName()+".wca")) { QFile::rename(MusEGlobal::museProject + "/" + wf.baseName()+".wca", MusEGlobal::museProject + "/unused/" +wf.baseName()+".wca"); } } } QDialog::accept(); } } // namespace MusEGui muse-4.0.0/src/muse/components/unusedwavefiles.h000066400000000000000000000026461404105311000217700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/unusedwavefiles.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef UNUSEDWAVEFILES_H #define UNUSEDWAVEFILES_H #include namespace Ui { class UnusedWaveFiles; } namespace MusEGui { class UnusedWaveFiles : public QDialog { Q_OBJECT QStringList allWaveFiles; public: explicit UnusedWaveFiles(QWidget *parent = 0); ~UnusedWaveFiles(); public slots: void accept(); void findWaveFiles(); private: Ui::UnusedWaveFiles *ui; }; } // namespace MusEGui #endif // UNUSEDWAVEFILES_H muse-4.0.0/src/muse/components/unusedwavefiles.ui000066400000000000000000000062071404105311000221530ustar00rootroot00000000000000 UnusedWaveFiles 0 0 508 241 Dialog List of unused audio files in current project directory: Current project All .med files in current directory Qt::Vertical 20 40 Qt::Horizontal 98 17 Move files to 'unused' subdir true Cancel cancelButton clicked() UnusedWaveFiles reject() 416 331 305 183 moveButton clicked() UnusedWaveFiles accept() 266 321 305 183 muse-4.0.0/src/muse/components/utils.cpp000066400000000000000000001073011404105311000202440ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: utils.cpp,v 1.1.1.1.2.3 2009/11/14 03:37:48 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "muse_math.h" #include #include #include #include #include #include #include #include // Forwards from header: #include #include #include "audio.h" #include "audiodev.h" #include "part.h" #include "utils.h" #include "xml.h" #include "gconfig.h" #include namespace MusECore { //--------------------------------------------------------- // curTimeUS //--------------------------------------------------------- uint64_t curTimeUS() { // No audio device yet? Just get wall clock time. if(!MusEGlobal::audioDevice) { struct timeval t; gettimeofday(&t, 0); //printf("%ld %ld\n", t.tv_sec, t.tv_usec); // Note I observed values coming out of order! Causing some problems. return ((uint64_t)t.tv_sec * 1000000UL) + (uint64_t)t.tv_usec; } // Ask the driver for the system time. // May depend on selected clock source. // With Jack, may be based upon wallclock time, the // processor cycle counter or the HPET clock etc. return MusEGlobal::audioDevice->systemTimeUS(); /* struct timespec t; //clock_gettime(CLOCK_MONOTONIC, &t); //clock_gettime(CLOCK_MONOTONIC_RAW, &t); //clock_gettime(CLOCK_REALTIME, &t); clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t); // Only this one works for me. Could be my older kernel... printf("%ld %ld\n", t.tv_sec, t.tv_nsec); return (double)((double)t.tv_sec + (t.tv_nsec / 1000000000.0)); */ } //--------------------------------------------------------- // dump // simple debug output //--------------------------------------------------------- void dump(const unsigned char* p, int n) { printf("dump %d\n", n); for (int i = 0; i < n; ++i) { printf("%02x ", p[i]); if ((i > 0) && (i % 16 == 0) && (i+1 < n)) printf("\n"); } printf("\n"); } //--------------------------------------------------------- // num2cols //--------------------------------------------------------- int num2cols(int min, int max) { int amin = abs(min); int amax = abs(max); int l = amin > amax ? amin : amax; return int(log10(l)) + 1; } //--------------------------------------------------------- // hLine //--------------------------------------------------------- QFrame* hLine(QWidget* w) { QFrame* delim = new QFrame(w); delim->setFrameStyle(QFrame::HLine | QFrame::Sunken); return delim; } //--------------------------------------------------------- // vLine //--------------------------------------------------------- QFrame* vLine(QWidget* w) { QFrame* delim = new QFrame(w); delim->setFrameStyle(QFrame::VLine | QFrame::Sunken); return delim; } //--------------------------------------------------------- // bitmap2String // 5c -> 1-4 1-6 // // 01011100 // //--------------------------------------------------------- QString bitmap2String(int bm) { QString s; //printf("bitmap2string: bm %04x", bm); if (bm == 0xffff) s = "all"; else if (bm == 0) s = "none"; else { bool range = false; int first = 0; bool needSpace = false; bm &= 0xffff; for (int i = 0; i < 17; ++i) { //for (int i = 0; i < 16; ++i) { if ((1 << i) & bm) { if (!range) { range = true; first = i; } } else { if (range) { if (needSpace) s += " "; QString ns; if (first == i-1) ns = QString::number(first + 1); else ns = QString("%1-%2").arg(first + 1).arg(i); s += ns; needSpace = true; } range = false; } } } //printf(" -> <%s>\n", s.toLatin1()); return s; } //--------------------------------------------------------- // u32bitmap2String //--------------------------------------------------------- QString u32bitmap2String(unsigned int bm) { QString s; //printf("bitmap2string: bm %04x", bm); //if (bm == 0xffff) if (bm == 0xffffffff) s = "all"; else if (bm == 0) s = "none"; else { bool range = false; int first = 0; //unsigned int first = 0; bool needSpace = false; //bm &= 0xffff; //for (int i = 0; i < 17; ++i) { for (int i = 0; i < 33; ++i) { if ((i < 32) && ((1U << i) & bm)) { if (!range) { range = true; first = i; } } else { if (range) { if (needSpace) s += " "; QString ns; if (first == i-1) ns = QString::number(first + 1); else ns = QString("%1-%2").arg(first + 1).arg(i); s += ns; needSpace = true; } range = false; } } } //printf(" -> <%s>\n", s.toLatin1()); return s; } //--------------------------------------------------------- // string2bitmap //--------------------------------------------------------- int string2bitmap(const QString& str) { int val = 0; QString ss = str.simplified(); if (ss.isEmpty()) return 0; if (ss == QString("all")) return 0xffff; if (ss == QString("none")) return 0; QByteArray ba = ss.toLatin1(); const char* s = ba.constData(); // printf("str2bitmap: <%s> ", str.toLatin1); int tval = 0; bool range = false; int sval = 0; while (*s == ' ') ++s; while (*s) { if (*s >= '0' && *s <= '9') { tval *= 10; tval += *s - '0'; } else if (*s == ' ' || *s == ',') { if (range) { for (int i = sval-1; i < tval; ++i) val |= (1 << i); range = false; } else { val |= (1 << (tval-1)); } tval = 0; } else if (*s == '-') { range = true; sval = tval; tval = 0; } ++s; } if (range && tval) { for (int i = sval-1; i < tval; ++i) val |= (1 << i); } else if (tval) { val |= (1 << (tval-1)); } return val & 0xffff; } //--------------------------------------------------------- // string2u32bitmap //--------------------------------------------------------- unsigned int string2u32bitmap(const QString& str) { //int val = 0; unsigned int val = 0; QString ss = str.simplified(); if (ss.isEmpty()) return 0; if (ss == QString("all")) return 0xffff; if (ss == QString("none")) return 0; QByteArray ba = ss.toLatin1(); const char* s = ba.constData(); // printf("str2bitmap: <%s> ", str.toLatin1); int tval = 0; //unsigned int tval = 0; bool range = false; int sval = 0; //unsigned int sval = 0; while (*s == ' ') ++s; while (*s) { if (*s >= '0' && *s <= '9') { tval *= 10; tval += *s - '0'; } else if (*s == ' ' || *s == ',') { if (range) { for (int i = sval-1; i < tval; ++i) //for (unsigned int i = sval-1; i < tval; ++i) val |= (1U << i); range = false; } else { val |= (1U << (tval-1)); } tval = 0; } else if (*s == '-') { range = true; sval = tval; tval = 0; } ++s; } if (range && tval) { for (int i = sval-1; i < tval; ++i) //for (unsigned int i = sval-1; i < tval; ++i) val |= (1U << i); } else if (tval) { val |= (1U << (tval-1)); } //return val & 0xffff; return val; } // //--------------------------------------------------------- // // autoAdjustFontSize // // w: Widget to auto adjust font size // // s: String to fit // // ignoreWidth: Set if dealing with a vertically constrained widget - one which is free to resize horizontally. // // ignoreHeight: Set if dealing with a horizontally constrained widget - one which is free to resize vertically. // // Returns false if text would not fit even at min size. // // Caller should enable word wrap (if available) if false is returned, or disable word wrap if true is returned. // // Otherwise if word wrap is enabled all the time, there is the possibility it will break the line prematurely. // //--------------------------------------------------------- // // bool autoAdjustFontSize(QFrame* w, const QString& s, bool ignoreWidth, bool ignoreHeight, int max, int min) // { // // In case the max or min was obtained from QFont::pointSize() which returns -1 // // if the font is a pixel font, or if min is greater than max... // // if(!w || (min < 0) || (max < 0) || (min > max)) // if(!w) // return false; // if(min > max) // min = max; // // // Make the minimum about 3/4 the maximum font size. // min = int(double(max) * 0.7); // // // Limit the minimum and maximum sizes to something at least readable. // if(max < 6) // max = 6; // if(min < 6) // min = 6; // // QRect cr = w->contentsRect(); // QRect r; // QFont fnt = w->font(); // // An extra amount just to be sure - I found it was still breaking up two words which would fit on one line. // // int extra = 4; // int extra = 0; // // int fin_sz = max; // // Allow at least one loop. min can be equal to max. // // for(int i = max; i >= min; --i) // for( ; fin_sz >= min; --fin_sz) // { // fnt.setPointSize(fin_sz); // QFontMetrics fm(fnt); // r = fm.boundingRect(s); // // Would the text fit within the widget? // if((ignoreWidth || (r.width() <= (cr.width() - extra))) && (ignoreHeight || (r.height() <= cr.height()))) // break; // } // //printf("autoAdjustFontSize: ptsz:%d widget:%s before setFont x:%d y:%d w:%d h:%d\n", fnt.pointSize(), w->name(), w->x(), w->y(), w->width(), w->height()); // // // Here we will always have a font ranging from min to max point size. // w->setFont(fnt); // // w->setStyleSheet(MusECore::font2StyleSheet(fnt)); // //printf("autoAdjustFontSize: ptsz:%d widget:%s x:%d y:%d w:%d h:%d frame w:%d rw:%d rh:%d\n", fnt.pointSize(), w->name(), w->x(), w->y(), w->width(), w->height(), w->frameWidth(), cr.width(), cr.height()); // // // ----------------------------------------------------------- // // This is an alternate faster method. But the one below is better. // // ----------------------------------------------------------- // // // QFont fnt = w->font(); // // //const int req_w = w->fontMetrics().width(s) + 4; // // const int req_w = w->fontMetrics().boundingRect(s).width() + 4; // // if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. // // { // // if(fnt.pointSize() != max) // // { // // fnt.setPointSize(max); // // w->setFont(fnt); // // } // // } // // else // // { // // float factor = (float)w->rect().width() / (float)req_w; // // //if((factor < 1) || (factor > 1.25)) // // if((factor < 1) || (factor > 1)) // // { // // //qreal new_sz = fnt.pointSizeF() * factor; // // int new_sz = (float)fnt.pointSize() * factor; // // bool do_check = true; // // if(new_sz < min) // // { // // new_sz = min; // // do_check = false; // // } // // else if(new_sz > max) // // { // // new_sz = max; // // do_check = false; // // } // // // // //if(fnt.pointSizeF() != new_sz) // // if(fnt.pointSize() != new_sz) // // { // // //fnt.setPointSizeF(new_sz); // // fnt.setPointSize(new_sz); // // if(do_check) // // { // // const QFontMetrics fm(fnt); // // const int check_w = fm.boundingRect(s).width() + 4; // // if(check_w > w->rect().width()) // // { // // --new_sz; // // fnt.setPointSize(new_sz); // // } // // } // // w->setFont(fnt); // // } // // } // // } // // // // ----------------------------------------------------------- // // This is an alternate faster method. The accuracy is poorer // // than the top method, and somewhat unreliable. Maybe with more tweaking... // // ----------------------------------------------------------- // // // //qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); // // //QRectF r = boundingRect(); // // QRectF r = w->rect(); // // //QFont f = painter->font(); // // QFont fnt = w->font(); // // // //if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. // // if(ignoreWidth || s.isEmpty()) // Also avoid divide by zero below. // // { // // if(fnt.pointSize() != max) // // { // // fnt.setPointSize(max); // // w->setFont(fnt); // // w->setStyleSheet(MusECore::font2StyleSheet(fnt)); // // } // // } // // else // // { // // //qreal aspectRatio = painter->fontMetrics().lineSpacing() / painter->fontMetrics().averageCharWidth(); // // qreal aspectRatio = w->fontMetrics().lineSpacing() / w->fontMetrics().averageCharWidth(); // // int pixelsize = sqrt(r.width() * r.height() / aspectRatio / (s.length() * 3)) * aspectRatio; // // fnt.setPixelSize(pixelsize); // // //int flags = Qt::AlignCenter|Qt::TextDontClip|Qt::TextWordWrap; // // int flags = Qt::AlignCenter; // // //if ((pixelsize * lod) < 13) // // // flags |= Qt::TextWrapAnywhere; // // QFontMetricsF fm(fnt); // // QRectF tbr = fm.boundingRect(r,flags,s); // // pixelsize = fnt.pixelSize() * qMin(r.width() * 0.95 / tbr.width(), r.height() * 0.95 / tbr.height()); // // // if(pixelsize < min) // // // pixelsize = min; // // // else if(pixelsize > max) // // // pixelsize = max; // // fnt.setPixelSize(pixelsize); // // const QFontInfo fi(fnt); // // const int pointsize = fi.pointSize(); // // if(pointsize <= min) // // fnt.setPointSize(min); // // else if(pointsize >= max) // // fnt.setPointSize(max); // // w->setFont(fnt); // // w->setStyleSheet(MusECore::font2StyleSheet(fnt)); // // //painter->drawText(r,flags,stitle); // // } // // // // // Force minimum height. Use the expected height for the highest given point size. // // This way the mixer strips aren't all different label heights, but can be larger if necessary. // // Only if ignoreHeight is set (therefore the height is adjustable). // if(ignoreHeight) // { // fnt.setPointSize(max); // const QFontMetrics fm(fnt); // // Set the label's minimum height equal to the height of the font. // w->setMinimumHeight(fm.height() + 2 * w->frameWidth()); // } // // // return true; // // // If the text still wouldn't fit at the min size, tell the caller to turn on word wrap. // return fin_sz >= min; // } //--------------------------------------------------------- // autoAdjustFontSize // w: Widget to auto adjust font size // txt: String to fit // targetFont: Font input and output variable. Widget font is NOT automatically set due to stylesheets // completely overriding them, therefore stylesheet must be 'composed' from the resulting font along with OTHER settings. // ignoreWidth: Set if dealing with a vertically constrained widget - one which is free to resize horizontally. // ignoreHeight: Set if dealing with a horizontally constrained widget - one which is free to resize vertically. // Returns false if text would not fit even at min size. // Caller should enable word wrap (if available) if false is returned, or disable word wrap if true is returned. // Otherwise if word wrap is enabled all the time, there is the possibility it will break the line prematurely. //--------------------------------------------------------- bool autoAdjustFontSize(QFrame* widget, const QString& txt, QFont& targetFont, bool ignoreWidth, bool ignoreHeight, int max, int min) { // In case the max or min was obtained from QFont::pointSize() which returns -1 // if the font is a pixel font, or if min is greater than max... // if(!w || (min < 0) || (max < 0) || (min > max)) if(!widget) return false; if(min > max) min = max; // Make the minimum about 3/4 the maximum font size. min = int(double(max) * 0.85); // Limit the minimum and maximum sizes to something at least readable. if(max < 7) max = 7; if(min < 7) min = 7; QRect cr = widget->contentsRect(); QRect r; // QFont fnt = widget->font(); // QFont fnt = targetFont; // Make a copy for later. // Force minimum height. Use the expected height for the highest given point size. // This way the mixer strips aren't all different label heights, but can be larger if necessary. // Only if ignoreHeight is set (therefore the height is adjustable). if(ignoreHeight) { targetFont.setPointSize(max); const QFontMetrics fm(targetFont); // Set the label's minimum height equal to the height of the font. // w->setMinimumHeight(fm.height() + 2 * w->frameWidth()); widget->setMinimumHeight(fm.height() + 2 * widget->frameWidth()); } // An extra amount just to be sure - I found it was still breaking up two words which would fit on one line. // int extra = 4; int extra = 0; int fin_sz = max; // Allow at least one loop. min can be equal to max. // for(int i = max; i >= min; --i) for( ; fin_sz >= min; --fin_sz) { // fnt.setPointSize(fin_sz); targetFont.setPointSize(fin_sz); // QFontMetrics fm(fnt); QFontMetrics fm(targetFont); r = fm.boundingRect(txt); // Would the text fit within the widget? if((ignoreWidth || (r.width() <= (cr.width() - extra))) && (ignoreHeight || (r.height() <= cr.height()))) // if((ignoreWidth || (r.width() <= (sz.width() - extra))) && (ignoreHeight || (r.height() <= sz.height()))) break; } //printf("autoAdjustFontSize: ptsz:%d widget:%s before setFont x:%d y:%d w:%d h:%d\n", fnt.pointSize(), w->name(), w->x(), w->y(), w->width(), w->height()); // Here we will always have a font ranging from min to max point size. // w->setFont(fnt); // w->setStyleSheet(MusECore::font2StyleSheet(fnt)); //printf("autoAdjustFontSize: ptsz:%d widget:%s x:%d y:%d w:%d h:%d frame w:%d rw:%d rh:%d\n", fnt.pointSize(), w->name(), w->x(), w->y(), w->width(), w->height(), w->frameWidth(), cr.width(), cr.height()); // ----------------------------------------------------------- // This is an alternate faster method. But the one below is better. // ----------------------------------------------------------- // QFont fnt = w->font(); // //const int req_w = w->fontMetrics().width(s) + 4; // const int req_w = w->fontMetrics().boundingRect(s).width() + 4; // if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. // { // if(fnt.pointSize() != max) // { // fnt.setPointSize(max); // w->setFont(fnt); // } // } // else // { // float factor = (float)w->rect().width() / (float)req_w; // //if((factor < 1) || (factor > 1.25)) // if((factor < 1) || (factor > 1)) // { // //qreal new_sz = fnt.pointSizeF() * factor; // int new_sz = (float)fnt.pointSize() * factor; // bool do_check = true; // if(new_sz < min) // { // new_sz = min; // do_check = false; // } // else if(new_sz > max) // { // new_sz = max; // do_check = false; // } // // //if(fnt.pointSizeF() != new_sz) // if(fnt.pointSize() != new_sz) // { // //fnt.setPointSizeF(new_sz); // fnt.setPointSize(new_sz); // if(do_check) // { // const QFontMetrics fm(fnt); // const int check_w = fm.boundingRect(s).width() + 4; // if(check_w > w->rect().width()) // { // --new_sz; // fnt.setPointSize(new_sz); // } // } // w->setFont(fnt); // } // } // } // ----------------------------------------------------------- // This is an alternate faster method. The accuracy is poorer // than the top method, and somewhat unreliable. Maybe with more tweaking... // ----------------------------------------------------------- // //qreal lod = option->levelOfDetailFromTransform(painter->worldTransform()); // //QRectF r = boundingRect(); // QRectF r = w->rect(); // //QFont f = painter->font(); // QFont fnt = w->font(); // //if(ignoreWidth || req_w == 0) // Also avoid divide by zero below. // if(ignoreWidth || s.isEmpty()) // Also avoid divide by zero below. // { // if(fnt.pointSize() != max) // { // fnt.setPointSize(max); // w->setFont(fnt); // w->setStyleSheet(MusECore::font2StyleSheet(fnt)); // } // } // else // { // //qreal aspectRatio = painter->fontMetrics().lineSpacing() / painter->fontMetrics().averageCharWidth(); // qreal aspectRatio = w->fontMetrics().lineSpacing() / w->fontMetrics().averageCharWidth(); // int pixelsize = sqrt(r.width() * r.height() / aspectRatio / (s.length() * 3)) * aspectRatio; // fnt.setPixelSize(pixelsize); // //int flags = Qt::AlignCenter|Qt::TextDontClip|Qt::TextWordWrap; // int flags = Qt::AlignCenter; // //if ((pixelsize * lod) < 13) // // flags |= Qt::TextWrapAnywhere; // QFontMetricsF fm(fnt); // QRectF tbr = fm.boundingRect(r,flags,s); // pixelsize = fnt.pixelSize() * qMin(r.width() * 0.95 / tbr.width(), r.height() * 0.95 / tbr.height()); // // if(pixelsize < min) // // pixelsize = min; // // else if(pixelsize > max) // // pixelsize = max; // fnt.setPixelSize(pixelsize); // const QFontInfo fi(fnt); // const int pointsize = fi.pointSize(); // if(pointsize <= min) // fnt.setPointSize(min); // else if(pointsize >= max) // fnt.setPointSize(max); // w->setFont(fnt); // w->setStyleSheet(MusECore::font2StyleSheet(fnt)); // //painter->drawText(r,flags,stitle); // } // // Force minimum height. Use the expected height for the highest given point size. // // This way the mixer strips aren't all different label heights, but can be larger if necessary. // // Only if ignoreHeight is set (therefore the height is adjustable). // if(ignoreHeight) // { // fnt.setPointSize(max); // const QFontMetrics fm(fnt); // // Set the label's minimum height equal to the height of the font. // // w->setMinimumHeight(fm.height() + 2 * w->frameWidth()); // widget->setMinimumHeight(fm.height() + 2 * widget->frameWidth()); // } // return true; // If the text still wouldn't fit at the min size, tell the caller to turn on word wrap. return fin_sz >= min; } QGradient gGradientFromQColor(const QColor& c, const QPointF& start, const QPointF& finalStop) { int h, s, v, a; c.getHsv(&h, &s, &v, &a); const int v0 = v + (255 - v)/2; const int v1 = v - v/2; const QColor c0 = QColor::fromHsv(h, s, v0, a); const QColor c1 = QColor::fromHsv(h, s, v1, a); QLinearGradient gradient(start, finalStop); gradient.setColorAt(0, c0); gradient.setColorAt(1, c1); return gradient; } QGradient getGradientFromColor(const QColor& c, const QPoint& start, const QPoint& stop, const int strength) { QLinearGradient gradient(start, stop); gradient.setColorAt(0, c.lighter(100 + strength/3)); gradient.setColorAt(.5, c); gradient.setColorAt(1, c.darker(100 + strength/3*2)); return std::move(gradient); } bool isColorBright(const QColor& c) { return getPerceivedLuminance(c) > 140; } int getPerceivedLuminance(const QColor& c) { // a fairly good approximation of perceived luminance (kybos) return qSqrt(c.red() * c.red() * .241 + c.green() * c.green() * .691 + c.blue() * c.blue() * .068); } QPainterPath roundedPath(const QRect& r, int xrad, int yrad, Corner roundCorner) { return roundedPath(r.x(), r.y(), r.width(), r.height(), xrad, yrad, roundCorner); } QPainterPath roundedPath(int x, int y, int w, int h, int xrad, int yrad, Corner roundCorner) { QPainterPath rounded_rect; rounded_rect.addRect(x, y, w, h); if (roundCorner & CornerUpperLeft) { QPainterPath top_left_corner; top_left_corner.addRect(x, y, xrad, yrad); top_left_corner.moveTo(x + xrad, y + yrad); top_left_corner.arcTo(x, y, xrad*2, yrad*2, 180, -90); rounded_rect = rounded_rect.subtracted(top_left_corner); } if (roundCorner & CornerUpperRight) { QPainterPath top_right_corner; top_right_corner.addRect(x + w - xrad, y, xrad, yrad); top_right_corner.moveTo(x + w - xrad, y + yrad); top_right_corner.arcTo(x + w - xrad * 2, y, xrad*2, yrad*2, 90, -90); rounded_rect = rounded_rect.subtracted(top_right_corner); } if (roundCorner & CornerLowerLeft) { QPainterPath bottom_left_corner; bottom_left_corner.addRect(x, y + h - yrad, xrad, yrad); bottom_left_corner.moveTo(x + xrad, y + h - yrad); bottom_left_corner.arcTo(x, y + h - yrad*2, xrad*2, yrad*2, 180, 90); rounded_rect = rounded_rect.subtracted(bottom_left_corner); } if (roundCorner & CornerLowerRight) { QPainterPath bottom_right_corner; bottom_right_corner.addRect(x + w - xrad, y + h - yrad, xrad, yrad); bottom_right_corner.moveTo(x + w - xrad, y + h - yrad); bottom_right_corner.arcTo(x + w - xrad*2, y + h - yrad*2, xrad*2, yrad*2, 270, 90); rounded_rect = rounded_rect.subtracted(bottom_right_corner); } return rounded_rect; } void addRoundedPath(QPainterPath* path, const QRect& r, int xrad, int yrad, Corner roundCorner) { addRoundedPath(path, r.x(), r.y(), r.width(), r.height(), xrad, yrad, roundCorner); } void addRoundedPath(QPainterPath* path, int x, int y, int w, int h, int xrad, int yrad, Corner roundCorner) { QPainterPath& pp = *path; pp.addRect(x, y, w, h); if (roundCorner & CornerUpperLeft) { QPainterPath top_left_corner; top_left_corner.addRect(x, y, xrad, yrad); top_left_corner.moveTo(x + xrad, y + yrad); top_left_corner.arcTo(x, y, xrad*2, yrad*2, 180, -90); pp -= top_left_corner; } if (roundCorner & CornerUpperRight) { QPainterPath top_right_corner; top_right_corner.addRect(x + w - xrad, y, xrad, yrad); top_right_corner.moveTo(x + w - xrad, y + yrad); top_right_corner.arcTo(x + w - xrad * 2, y, xrad*2, yrad*2, 90, -90); pp -= top_right_corner; } if (roundCorner & CornerLowerLeft) { QPainterPath bottom_left_corner; bottom_left_corner.addRect(x, y + h - yrad, xrad, yrad); bottom_left_corner.moveTo(x + xrad, y + h - yrad); bottom_left_corner.arcTo(x, y + h - yrad*2, xrad*2, yrad*2, 180, 90); pp -= bottom_left_corner; } if (roundCorner & CornerLowerRight) { QPainterPath bottom_right_corner; bottom_right_corner.addRect(x + w - xrad, y + h - yrad, xrad, yrad); bottom_right_corner.moveTo(x + w - xrad, y + h - yrad); bottom_right_corner.arcTo(x + w - xrad*2, y + h - yrad*2, xrad*2, yrad*2, 270, 90); pp -= bottom_right_corner; } } //--------------------------------------------------------- // colorRect // paints a rectangular icon with a given color //--------------------------------------------------------- QIcon colorRect(const QColor& color, int width, int height) { QPainter painter; QPixmap image(width, height); painter.begin(&image); painter.setBrush(color); QRect rectangle(0, 0, width, height); painter.drawRect(rectangle); painter.end(); QIcon icon(image); return icon; } int get_paste_len() { QClipboard* cb = QApplication::clipboard(); const QMimeData* md = cb->mimeData(QClipboard::Clipboard); QString pfx("text/"); QString mdpl("x-muse-midipartlist"); QString wvpl("x-muse-wavepartlist"); QString mxpl("x-muse-mixedpartlist"); QString txt; if(md->hasFormat(pfx + mdpl)) txt = cb->text(mdpl, QClipboard::Clipboard); else if(md->hasFormat(pfx + wvpl)) txt = cb->text(wvpl, QClipboard::Clipboard); else if(md->hasFormat(pfx + mxpl)) txt = cb->text(mxpl, QClipboard::Clipboard); else return 0; QByteArray ba = txt.toLatin1(); const char* ptxt = ba.constData(); Xml xml(ptxt); bool end = false; unsigned begin_tick=-1; //this uses the greatest possible begin_tick unsigned end_tick=0; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: end = true; break; case Xml::TagStart: if (tag == "part") { Part* p = 0; p = Part::readFromXml(xml, NULL, false, false); if (p) { if (p->tick() < begin_tick) begin_tick=p->tick(); if (p->endTick() > end_tick) end_tick=p->endTick(); p->unchainClone(); // just for safety; shouldn't be chained anyway. delete p; } } else xml.unknown("PartCanvas::get_paste_len"); break; case Xml::TagEnd: break; default: end = true; break; } if(end) break; } if (begin_tick > end_tick) return 0; else return end_tick - begin_tick; } //--------------------------------------------------------- // getUniqueFileName // Sets newAbsFilePath to origFilepath or a new version if found // Return true if success //--------------------------------------------------------- bool getUniqueFileName(const QString& origFilepath, QString& newAbsFilePath) { QFileInfo fi(origFilepath); if(!fi.exists()) { newAbsFilePath = fi.absoluteFilePath(); return true; } QString pre = fi.absolutePath() + QString('/') + fi.baseName() + QString('_'); QString post = QString('.') + fi.completeSuffix(); for(int i = 1; i < 100000; ++i) { fi.setFile(pre + QString::number(i) + post); if(!fi.exists()) { newAbsFilePath = fi.absoluteFilePath(); return true; } } printf("Could not find a suitable filename (more than 100000 files based on %s - clean up!\n", origFilepath.toLatin1().constData()); return false; } QString font2StyleSheetFull(const QFont& fnt) { // QColor tt = qApp->palette().brush(QPalette::ToolTipBase).color(); QString ss("* {" + MusECore::font2StyleSheet(fnt) + "}"); // ss += "* {background-color: " + QColor(MusEGlobal::config.mixerBg).name() + "}"; ss += "QToolTip {font-size:" + QString::number(qApp->font().pointSize()) + "pt}"; // ss += "QToolTip {background-color: " + tt.name() + "}"; // ss += "QToolTip {font-size:" + QString::number(qApp->font().pointSize()) + "pt}"; return ss; } QString font2StyleSheet(const QFont& fnt) { QString st; switch(fnt.style()) { case QFont::StyleNormal: st = "normal"; break; case QFont::StyleItalic: st = "italic"; break; case QFont::StyleOblique: st = "oblique"; break; } QString wt; switch(fnt.weight()) { case QFont::Normal: wt = "normal"; break; case QFont::Bold: wt = "bold"; break; default: // QFont::weight() : "Qt uses a weighting scale from 0 to 99..." // Stylesheets : "The weight of a font:" // normal // | bold // | 100 // | 200 // ... // | 900 wt = QString::number( (int)(((double)fnt.weight() / 99.0) * 8) * 100 + 100 ); break; } QString sz; if(fnt.pointSize() > 0) sz = QString("%1pt").arg(fnt.pointSize()); else if(fnt.pixelSize() > 0) sz = QString("%1px").arg(fnt.pixelSize()); return QString("font: %1 %2 %3 \"%4\"; ").arg(wt).arg(st).arg(sz).arg(fnt.family()); } void drawSegmentedHLine(QPainter* p, int x1, int x2, int y, int segLength, int /*offset*/) { const int num_segs = (x2 - x1) / segLength; const int fin_seg = (x2 - x1) % segLength; int seg_x = x1; for(int i = 0; i < num_segs; ++i) { p->drawLine(seg_x, y, seg_x + segLength - 1, y); seg_x += segLength; } p->drawLine(seg_x, y, seg_x + fin_seg, y); } void drawSegmentedVLine(QPainter* p, int x, int y1, int y2, int segLength, int /*offset*/) { const int num_segs = (y2 - y1) / segLength; const int fin_seg = (y2 - y1) % segLength; int seg_y = y1; for(int i = 0; i < num_segs; ++i) { p->drawLine(x, seg_y, x, seg_y + segLength - 1); seg_y += segLength; } p->drawLine(x, seg_y, x, seg_y + fin_seg); } } // namespace MusECore muse-4.0.0/src/muse/components/utils.h000066400000000000000000000077311404105311000177170ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: utils.h,v 1.1.1.1.2.3 2009/11/14 03:37:48 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __UTILS_H__ #define __UTILS_H__ #include #define MAX(a,b) (((a)>(b))?(a):(b)) #define MIN(a,b) (((a)<(b))?(a):(b)) #include #include #include #include #include #include #include // Forward declarations: class QFrame; class QWidget; class QPainter; namespace MusECore { enum Corner { CornerUpperLeft = 0x1, CornerUpperRight = 0x2, CornerLowerLeft = 0x4, CornerLowerRight = 0x8, CornerAll = 0xF }; extern QString bitmap2String(int bm); extern int string2bitmap(const QString& str); extern QString u32bitmap2String(unsigned int bm); extern unsigned int string2u32bitmap(const QString& str); extern bool autoAdjustFontSize(QFrame* w, const QString& s, QFont& targetFont, bool ignoreWidth = false, bool ignoreHeight = false, int max = 10, int min = 4); extern QGradient gGradientFromQColor(const QColor& c, const QPointF& start, const QPointF& finalStop); extern QGradient getGradientFromColor(const QColor& c, const QPoint& start, const QPoint& stop, const int strength); extern bool isColorBright(const QColor& c); extern int getPerceivedLuminance(const QColor& c); extern int num2cols(int min, int max); extern QFrame* hLine(QWidget* parent); extern QFrame* vLine(QWidget* parent); extern void dump(const unsigned char* p, int n); extern uint64_t curTimeUS(); extern QPainterPath roundedPath(const QRect& r, int xrad, int yrad, Corner roundCorner); extern QPainterPath roundedPath(int x, int y, int w, int h, int xrad, int yrad, Corner roundCorner); extern void addRoundedPath(QPainterPath* path, const QRect& r, int xrad, int yrad, Corner roundCorner); extern void addRoundedPath(QPainterPath* path, int x, int y, int w, int h, int xrad, int yrad, Corner roundCorner); extern QIcon colorRect(const QColor& color, int width, int height); extern int get_paste_len(); extern bool getUniqueFileName(const QString& filename, QString& newAbsFilePath); extern QString font2StyleSheet(const QFont& fnt); extern QString font2StyleSheetFull(const QFont& fnt); // These two functions are intended to solve an apparent BUG in Qt's patterned line drawing. // Artifacts are left over at the end of the line, causing smearing when scrolled. // It only happens with long 1 pixel wide patterned lines. 2 or more pixels wide, or solid lines, or short lines, are OK. // The solution is to break the line up into segments. // segLength should be a multiple of the pattern length so that the segments are drawn cleanly and contiguous. // For example with a pattern of one dot and two dashes, 21 is a good value. // segLength should be a fairly low value, say in the 20's, because the bug only occurs with longer lines. // Thus by segmenting the line into say 20's, it never gets a chance to exhibit the artifacts seen with longer lines. extern void drawSegmentedHLine(QPainter* p, int x1, int x2, int y, int segLength, int offset = 0); extern void drawSegmentedVLine(QPainter* p, int x, int y1, int y2, int segLength, int offset = 0); } // namespace MusECore #endif muse-4.0.0/src/muse/components/verticalmeter.cpp000066400000000000000000000256741404105311000217660ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: meter.cpp,v 1.4.2.2 2009/05/03 04:14:00 terminator356 Exp $ // redesigned by oget on 2011/08/15 // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include #include #include "verticalmeter.h" #include "gconfig.h" #include "fastlog.h" #include "mmath.h" #include "utils.h" namespace MusEGui { //--------------------------------------------------------- // VerticalMeter //--------------------------------------------------------- VerticalMeter::VerticalMeter(QWidget* parent, MeterType type) : Meter(parent, type) //Qt::WNoAutoErase { setBackgroundRole(QPalette::NoRole); setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_StaticContents); // This is absolutely required for speed! Otherwise painfully slow because we get // full rect paint events even on small scrolls! See help on QPainter::scroll(). // Commented out for now. Orcan 20110911 //setAttribute(Qt::WA_OpaquePaintEvent); mtype = type; overflow = false; val = 0.0; maxVal = 0.0; minScale = mtype == DBMeter ? MusEGlobal::config.minMeter : 0.0; // min value in dB or int maxScale = mtype == DBMeter ? 10.0 : 127.0; yellowScale = -10; redScale = 0; xrad = 4; yrad = 4; setLineWidth(0); setMidLineWidth(0); } //--------------------------------------------------------- // setVal //--------------------------------------------------------- void VerticalMeter::setVal(double v) //, double max, bool ovl) { //overflow = ovl; bool ud = false; if(mtype == DBMeter) { double minScaleLin = muse_db2val(minScale); if((v >= minScaleLin && val != v) || val >= minScaleLin) { val = v; ud = true; } } else { if(val != v) { val = v; ud = true; } } // if(maxVal != max) // { // maxVal = max; // ud = true; // } if(ud) update(); } //--------------------------------------------------------- // resetPeaks // reset peak and overflow indicator //--------------------------------------------------------- void VerticalMeter::resetPeaks() { maxVal = val; overflow = val > 0.0; update(); } //--------------------------------------------------------- // setRange //--------------------------------------------------------- void VerticalMeter::setRange(double min, double max) { minScale = min; maxScale = max; update(); } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void VerticalMeter::paintEvent(QPaintEvent* /*ev*/) { // TODO: Could make better use of event rectangle, for speed. QPainter p(this); p.setRenderHint(QPainter::Antialiasing); double range = maxScale - minScale; int fw = frameWidth(); int w = width() - 2*fw; int h = height() - 2*fw; /* QRect rect = ev->rect(); int w = rect.width() - 2*fw; int h = rect.height() - 2*fw; */ int xv; if(mtype == DBMeter) xv = int(((maxScale - (MusECore::fast_log10(val) * 20.0)) * w)/range); else { xv = int(((maxScale - val) * w)/range); } if(xv > w) xv = w; // Draw the red, green, and yellow sections. drawVU(p, w, h, xv); // Draw the peak white line. /* int xcenter; if(mtype == DBMeter) xcenter = maxVal == 0 ? 0 : int(((maxScale - (MusECore::fast_log10(0) * 20.0)) * w)/range); else xcenter = maxVal == 0 ? 0 : int(((maxVal) * w)/range); p.setPen(peak_color); p.drawLine(xcenter, 0, xcenter, h); */ // Draw the transparent layer on top of everything to give a 3d look QPainterPath round_path = roundedPath(0, 0, w, h, xrad, yrad, (MusECore::Corner) (MusECore::UpperLeft | MusECore::UpperRight | MusECore::LowerLeft | MusECore::LowerRight ) ); maskGrad.setStart(QPointF(0, 0)); maskGrad.setFinalStop(QPointF(0, h)); p.fillPath(round_path, QBrush(maskGrad)); } //--------------------------------------------------------- // drawVU //--------------------------------------------------------- void VerticalMeter::drawVU(QPainter& p, int w, int h, int xv) { if(mtype == DBMeter) { double range = maxScale - minScale; int x1 = int((maxScale - redScale) * w / range); int x2 = int((maxScale - yellowScale) * w / range); darkGradGreen.setStart(QPointF(x2, 0)); darkGradGreen.setFinalStop(QPointF(w, 0)); darkGradYellow.setStart(QPointF(x1, 0)); darkGradYellow.setFinalStop(QPointF(x2, 0)); darkGradRed.setStart(QPointF(0, 0)); darkGradRed.setFinalStop(QPointF(x1, 0)); lightGradGreen.setStart(QPointF(x2, 0)); lightGradGreen.setFinalStop(QPointF(w, 0)); lightGradYellow.setStart(QPointF(x1, 0)); lightGradYellow.setFinalStop(QPointF(x2, 0)); lightGradRed.setStart(QPointF(0, 0)); lightGradRed.setFinalStop(QPointF(x1, 0)); QPainterPath p_left = roundedPath(0, 0, x1, h, xrad, yrad, (MusECore::Corner) (MusECore::UpperLeft | MusECore::LowerLeft ) ); QPainterPath p_right = roundedPath(x2, 0, w-x2, h, xrad, yrad, (MusECore::Corner) (MusECore::LowerRight | MusECore::UpperRight ) ); if(xv < x1) { QPainterPath p_light_green = roundedPath(0, 0, xv, h, xrad, yrad, (MusECore::Corner) (MusECore::UpperLeft | MusECore::LowerLeft ) ); p_left = p_left.subtracted(p_light_green); // Green section: p.fillPath(p_light_green, QBrush(lightGradGreen)); // light green p.fillPath(p_left, QBrush(darkGradGreen)); // dark green // Yellow section: p.fillRect(x1, 0, x2-x1, h, QBrush(darkGradYellow)); // dark yellow // Red section: p.fillPath(p_right, QBrush(darkGradRed)); // dark red } else if(xv < x2) { // Green section: p.fillPath(p_left, QBrush(lightGradGreen)); // light green // Yellow section: p.fillRect(x1, 0, xv-x1, h, QBrush(lightGradYellow)); // light yellow p.fillRect(xv, 0, x2-xv, h, QBrush(darkGradYellow)); // dark yellow // Red section: p.fillPath(p_right, QBrush(darkGradRed)); // dark red } else //if(xv <= x3) { QPainterPath p_dark_red = roundedPath(xv, 0, w-xv, h, xrad, yrad, (MusECore::Corner) (MusECore::LowerRight | MusECore::UpperRight ) ); p_right = p_right.subtracted(p_dark_red); // Green section: p.fillPath(p_left, QBrush(lightGradGreen)); // light green // Yellow section: p.fillRect(x1, 0, x2-x1, h, QBrush(lightGradYellow)); // light yellow // Red section: p.fillPath(p_right, QBrush(lightGradRed)); // light red p.fillPath(p_dark_red, QBrush(darkGradRed)); // dark red } p.fillRect(x1,0, 1, h, separator_color); p.fillRect(x2,0, 1, h, separator_color); } else { darkGradGreen.setStart(QPointF(0, 0)); darkGradGreen.setFinalStop(QPointF(w, 0)); lightGradGreen.setStart(QPointF(0, 0)); lightGradGreen.setFinalStop(QPointF(w, 0)); // We need to draw the meter in two parts. The cutoff for the left rectangle can be // anywhere between xrad and w-xrad. Without loss of generality we pick the lower limit. int cut = xrad; QPainterPath p_left = roundedPath(0, 0, cut, h, xrad, yrad, (MusECore::Corner) (MusECore::UpperLeft | MusECore::LowerLeft ) ); QPainterPath p_right = roundedPath(cut, 0, w-cut, h, xrad, yrad, (MusECore::Corner) (MusECore::LowerRight | MusECore::UpperRight ) ); if(xv < cut) { QPainterPath p_light = roundedPath(0, 0, xv, h, xrad, yrad, (MusECore::Corner) (MusECore::UpperLeft | MusECore::LowerLeft ) ); p_left = p_left.subtracted(p_light); // left section: p.fillPath(p_left, QBrush(darkGradGreen)); // dark green p.fillPath(p_light, QBrush(lightGradGreen)); // light green // bottom section: p.fillPath(p_right, QBrush(darkGradGreen)); // dark green } else { QPainterPath p_dark = roundedPath(xv, 0, w-xv, h, xrad, yrad, (MusECore::Corner) (MusECore::UpperRight | MusECore::LowerRight ) ); p_right = p_right.subtracted(p_dark); // left section: p.fillPath(p_left, QBrush(lightGradGreen)); // light green // right section: p.fillPath(p_dark, QBrush(darkGradGreen)); // dark green p.fillPath(p_right, QBrush(lightGradGreen)); // light green } } } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void VerticalMeter::resizeEvent(QResizeEvent* ev) { Meter::resizeEvent(ev); } } // namespace MusEGui muse-4.0.0/src/muse/components/verticalmeter.h000066400000000000000000000033621404105311000214210ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: meter.h,v 1.1.1.1.2.2 2009/05/03 04:14:00 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __VERTICALMETER_H__ #define __VERTICALMETER_H__ #include "meter.h" class QPaintEvent; class QResizeEvent; class QMouseEvent; class QPainter; namespace MusEGui { class VerticalMeter : public Meter { Q_OBJECT private: MeterType mtype; bool overflow; double val; double maxVal; double minScale, maxScale; int yellowScale, redScale; void drawVU(QPainter& p, int, int, int); virtual void paintEvent(QPaintEvent*); virtual void resizeEvent(QResizeEvent*); public slots: void resetPeaks(); void setVal(double); public: VerticalMeter(QWidget* parent, MeterType type = DBMeter); void setRange(double min, double max); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/view.cpp000066400000000000000000001733321404105311000200650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: view.cpp,v 1.3.2.2 2009/04/06 01:24:55 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "view.h" #include "gconfig.h" #include #include #include "tempo.h" #include "muse_math.h" #include "sig.h" // Forwards from header: #include #include #include #include #include #include // Don't use this, it was just for debugging. // It's much slower than muse-1 no matter how hard I tried. // The left/right pixmap shifters in seXPos setYPos // just ate up all the time no matter what I tried. //#defines VIEW_USE_DOUBLE_BUFFERING 1 namespace MusEGui { //--------------------------------------------------------- // View::View // double xMag = (xmag < 0) ? 1.0/-xmag : double(xmag) //--------------------------------------------------------- View::View(QWidget* w, int xm, int ym, const char* name) : QWidget(w) { setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_StaticContents); // This is absolutely required for speed! Otherwise painfully slow because we get // full rect paint events even on small scrolls! See help on QPainter::scroll(). setAttribute(Qt::WA_OpaquePaintEvent); setObjectName(QString(name)); xmag = xm; ymag = ym; xpos = 0; ypos = 0; xorg = 0; yorg = 0; _virt = true; setBackgroundRole(QPalette::NoRole); brush.setStyle(Qt::SolidPattern); brush.setColor(Qt::lightGray); #ifdef VIEW_USE_DOUBLE_BUFFERING pmValid = false; #endif } //--------------------------------------------------------- // setOrigin //--------------------------------------------------------- void View::setOrigin(int x, int y) { xorg = x; yorg = y; redraw(); } //--------------------------------------------------------- // setXMag //--------------------------------------------------------- void View::setXMag(int xs) { xmag = xs; redraw(); } //--------------------------------------------------------- // seqYMag //--------------------------------------------------------- void View::setYMag(int ys) { ymag = ys; redraw(); } //--------------------------------------------------------- // setXPos // x - phys offset //--------------------------------------------------------- void View::setXPos(int x) { int delta = xpos - x; // - -> shift left xpos = x; #ifdef VIEW_USE_DOUBLE_BUFFERING if (pm.isNull()) return; if (!pmValid) { //printf("View::setXPos !pmValid x:%d width:%d delta:%d\n", x, width(), delta); redraw(); return; } int w = width(); int h = height(); QRect r; if (delta >= w || delta <= -w) r = QRect(0, 0, w, h); else if (delta < 0) { // shift left //bitBlt(&pm, 0, 0, &pm, -delta, 0, w + delta, h, CopyROP, true); QPainter p(&pm); p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::HighQualityAntialiasing, false); //printf("View::setXPos x:%d w:%d delta:%d r.x:%d r.w:%d\n", // x, w, delta, r.x(), r.width()); p.drawPixmap(0, 0, pm, -delta, 0, w + delta, h); r = QRect(w + delta, 0, -delta, h); } else { // shift right //bitBlt(&pm, delta, 0, &pm, 0, 0, w-delta, h, CopyROP, true); QPainter p(&pm); p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::HighQualityAntialiasing, false); //printf("View::setXPos x:%d w:%d delta:%d r.x:%d r.w:%d\n", // x, w, delta, r.x(), r.width()); p.drawPixmap(delta, 0, pm, 0, 0, w-delta, h); r = QRect(0, 0, delta, h); } QRect olr = overlayRect(); QRect olr1(olr); olr1.translate(delta, 0); r |= olr; r |= olr1; //printf("View::setXPos x:%d w:%d delta:%d r.x:%d r.w:%d\n", x, w, delta, r.x(), r.width()); //printf("View::setXPos paint delta:%d r.x:%d r.y:%d r.w:%d r.h:%d\n", delta, r.x(), r.y(), r.width(), r.height()); paint(r); update(); #else scroll(delta, 0); QRect olr = overlayRect(); // Is there an overlay? if(!olr.isNull()) { // Are we shifting right (moving left)? if(delta >= 0) { // Translate not good - need to set x to delta. //olr.translate(delta, 0); olr.setX(delta); olr.setWidth(olr.x() + olr.width() + delta); } else // We are shifting left (moving right). { // Translate not good - need to limit x to 0. //olr.translate(delta, 0); olr.setX(olr.x() + delta); } if(olr.x() < 0) olr.setX(0); if(olr.right() > width()) olr.setRight(width()); if(olr.y() < 0) olr.setY(0); if(olr.bottom() > height()) olr.setBottom(height()); //printf("scroll X update: x:%d y:%d w:%d h:%d\n", olr.x(), olr.y(), olr.width(), olr.height()); update(olr); } #endif } //--------------------------------------------------------- // setYPos //--------------------------------------------------------- void View::setYPos(int y) { int delta = ypos - y; // - -> shift up ypos = y; #ifdef VIEW_USE_DOUBLE_BUFFERING if (pm.isNull()) return; if (!pmValid) { //printf("View::setYPos !pmValid y:%d height:%d delta:%d\n", y, height(), delta); redraw(); return; } int w = width(); int h = height(); QRect r; if (delta >= h || delta <= -h) r = QRect(0, 0, w, h); else if (delta < 0) { // shift up //bitBlt(&pm, 0, 0, &pm, 0, -delta, w, h + delta, CopyROP, true); QPainter p(&pm); p.drawPixmap(0, 0, pm, 0, -delta, w, h + delta); r = QRect(0, h + delta, w, -delta); } else { // shift down //bitBlt(&pm, 0, delta, &pm, 0, 0, w, h-delta, CopyROP, true); QPainter p(&pm); p.drawPixmap(0, delta, pm, 0, 0, w, h-delta); r = QRect(0, 0, w, delta); } QRect olr = overlayRect(); QRect olr1(olr); olr1.translate(0, delta); r |= olr; r |= olr1; //printf("View::setYPos paint delta:%d r.x:%d r.y:%d r.w:%d r.h:%d\n", delta, r.x(), r.y(), r.width(), r.height()); paint(r); update(); #else scroll(0, delta); QRect olr = overlayRect(); // Is there an overlay? if(!olr.isNull()) { // Are we shifting down (moving up)? if(delta >= 0) { // Translate not good - need to set y to delta. //olr.translate(0, delta); olr.setY(delta); olr.setHeight(olr.y() + olr.height() + delta); } else // We are shifting up (moving down). { // Translate not good - need to limit y to 0. //olr.translate(0, delta); olr.setY(olr.y() + delta); } if(olr.x() < 0) olr.setX(0); if(olr.right() > width()) olr.setRight(width()); if(olr.y() < 0) olr.setY(0); if(olr.bottom() > height()) olr.setBottom(height()); //printf("scroll Y update: x:%d y:%d w:%d h:%d\n", olr.x(), olr.y(), olr.width(), olr.height()); update(olr); } #endif } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void View::resizeEvent(QResizeEvent* ev) { QWidget::resizeEvent(ev); #ifdef VIEW_USE_DOUBLE_BUFFERING //pm.resize(ev->size()); //printf("View::resizeEvent width:%d height:%d\n", // ev->size().width(), ev->size().height()); if(pm.isNull()) { //printf("View::resizeEvent pixmap is null\n"); pm = QPixmap(ev->size().width(), ev->size().height()); } else pm = pm.copy(QRect(QPoint(0, 0), ev->size())); pmValid = false; #endif } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void View::paintEvent(QPaintEvent* ev) { //printf("View::paintEvent x:%d width:%d y:%d height:%d\n", // ev->rect().x(), ev->rect().width(), ev->rect().y(), ev->rect().height()); #ifdef VIEW_USE_DOUBLE_BUFFERING if (!pmValid) paint(ev->rect(), ev->region()); //bitBlt(this, ev->rect().topLeft(), &pm, ev->rect(), CopyROP, true); QPainter p(this); //p.setCompositionMode(QPainter::CompositionMode_Source); p.drawPixmap(ev->rect().topLeft(), pm, ev->rect()); #else paint(ev->rect(), ev->region()); #endif } //--------------------------------------------------------- // redraw //--------------------------------------------------------- void View::redraw() { //printf("View::redraw()\n"); #ifdef VIEW_USE_DOUBLE_BUFFERING QRect r(0, 0, pm.width(), pm.height()); //printf("View::redraw() r.x:%d r.w:%d\n", r.x(), r.width()); paint(r); #endif update(); } //--------------------------------------------------------- // redraw //--------------------------------------------------------- void View::redraw(const QRect& r) { //fprintf(stderr, "View::redraw(QRect& r) r.x:%d r.w:%d\n", r.x(), r.width()); #ifdef VIEW_USE_DOUBLE_BUFFERING paint(r); #endif update(r); } //--------------------------------------------------------- // redraw //--------------------------------------------------------- void View::redraw(const QRegion& r) { #ifdef VIEW_USE_DOUBLE_BUFFERING paint(r); #endif update(r); } //--------------------------------------------------------- // paint // r - phys coord system //--------------------------------------------------------- void View::paint(const QRect& r, const QRegion& rg) { #ifdef VIEW_USE_DOUBLE_BUFFERING if (pm.isNull()) return; #endif QRect rr(r); //printf("View::paint x:%d width:%d y:%d height:%d\n", r.x(), r.width(), r.y(), r.height()); #ifdef VIEW_USE_DOUBLE_BUFFERING if (!pmValid) { pmValid = true; rr = QRect(0, 0, pm.width(), pm.height()); } QPainter p(&pm); #else QPainter p(this); #endif // For testing... // const QVector rects = rg.rects(); // const int rg_sz = rects.size(); // int rg_r_cnt = 0; // fprintf(stderr, "View::paint: virt:%d rect: x:%d y:%d w:%d h:%d region rect count:%d\n", // virt(), r.x(), r.y(), r.width(), r.height(), rg_sz); // for(int i = 0; i < rg_sz; ++i, ++rg_r_cnt) // { // const QRect& rg_r = rects.at(i); // fprintf(stderr, " #%d: x:%d y:%d w:%d h:%d\n", rg_r_cnt, rg_r.x(), rg_r.y(), rg_r.width(), rg_r.height()); // } p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::HighQualityAntialiasing, false); if (bgPixmap.isNull()) p.fillRect(rr, brush); else p.drawTiledPixmap(rr, bgPixmap, QPoint(xpos + xorg + rr.x(), ypos + yorg + rr.y())); p.setClipRegion(rg); //printf("View::paint r.x:%d w:%d\n", rr.x(), rr.width()); pdraw(p, rr, rg); // draw into pixmap //p.resetMatrix(); // Q3 support says use resetMatrix instead, but resetMatrix advises resetTransform instead... p.resetTransform(); // resetMatrix() is deprecated in Qt 5.13 drawOverlay(p, r, rg); } //--------------------------------------------------------- // keyPressEvent //--------------------------------------------------------- void View::keyPressEvent(QKeyEvent* event) { viewKeyPressEvent(event); } //--------------------------------------------------------- // keyReleaseEvent //--------------------------------------------------------- void View::keyReleaseEvent(QKeyEvent* event) { viewKeyReleaseEvent(event); } //--------------------------------------------------------- // viewKeyPressEvent //--------------------------------------------------------- void View::viewKeyPressEvent(QKeyEvent* event) { event->ignore(); } //--------------------------------------------------------- // viewKeyReleaseEvent //--------------------------------------------------------- void View::viewKeyReleaseEvent(QKeyEvent* event) { event->ignore(); } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void View::mousePressEvent(QMouseEvent* ev) { QMouseEvent e(ev->type(), mapDev(ev->pos()), ev->globalPos(), ev->button(), ev->buttons(), ev->modifiers()); viewMousePressEvent(&e); } //--------------------------------------------------------- // mouseDoubleClickEvent //--------------------------------------------------------- void View::mouseDoubleClickEvent(QMouseEvent* ev) { QMouseEvent e(ev->type(), mapDev(ev->pos()), ev->globalPos(), ev->button(), ev->buttons(), ev->modifiers()); viewMouseDoubleClickEvent(&e); } //--------------------------------------------------------- // mouseMoveEvent //--------------------------------------------------------- void View::mouseMoveEvent(QMouseEvent* ev) { QMouseEvent e(ev->type(), mapDev(ev->pos()), ev->globalPos(), ev->button(), ev->buttons(), ev->modifiers()); viewMouseMoveEvent(&e); } //--------------------------------------------------------- // mouseReleaseEvent //--------------------------------------------------------- void View::mouseReleaseEvent(QMouseEvent* ev) { QMouseEvent e(ev->type(), mapDev(ev->pos()), ev->globalPos(), ev->button(), ev->buttons(), ev->modifiers()); viewMouseReleaseEvent(&e); } //--------------------------------------------------------- // dropEvent //--------------------------------------------------------- void View::dropEvent(QDropEvent* ev) { // From Q3 support: // "Sets the drop to happen at the given point. You do not normally need to use this // as it will be set internally before your widget receives the drop event." // But we need to remap it here... //ev->setPoint(mapDev(ev->pos())); QDropEvent nev(mapDev(ev->pos()), ev->possibleActions(), ev->mimeData(), ev->mouseButtons(), ev->keyboardModifiers(), ev->type()); //viewDropEvent(ev); viewDropEvent(&nev); } //--------------------------------------------------------- // setBg //--------------------------------------------------------- void View::setBg(const QPixmap& bgpm) { bgPixmap = bgpm; redraw(); } //--------------------------------------------------------- // devToVirt //--------------------------------------------------------- QRect View::devToVirt(const QRect& r) const { return mapDev(r); } void View::devToVirt(const QRegion& rg_in, QRegion& rg_out) const { mapDev(rg_in, rg_out); } //--------------------------------------------------------- // pdraw // r - phys coords //--------------------------------------------------------- void View::pdraw(QPainter& p, const QRect& r, const QRegion& rg) { //printf("View::pdraw virt:%d x:%d width:%d y:%d height:%d\n", virt(), r.x(), r.width(), r.y(), r.height()); if (virt()) { setPainter(p); draw(p, r, rg); } else draw(p, r, rg); } //--------------------------------------------------------- // setPainter //--------------------------------------------------------- void View::setPainter(QPainter& p) { //p.resetMatrix(); // Q3 support says use resetMatrix instead, but resetMatrix advises resetTransform instead... p.resetTransform(); // resetMatrix() is deprecated in Qt 5.13 p.translate( -(double(xpos) + double(xorg)) , -(double(ypos) + double(yorg))); double xMag = (xmag < 0) ? 1.0/double(-xmag) : double(xmag); double yMag = (ymag < 0) ? 1.0/double(-ymag) : double(ymag); p.scale(xMag, yMag); } void View::drawBarText(QPainter& p, int tick, int bar, const QRect& mr, const QColor& textColor, const QFont& font) const { QPen pen; pen.setCosmetic(true); pen.setColor(textColor); p.setPen(pen); p.setFont(font); QString s; s.setNum(bar + 1); ViewRect r(mr, true); const int mw1000 = 1000; const int mw2 = 2; const int my = mr.y(); const int mh_m1 = mr.height() - 1; int mbrw = p.fontMetrics().boundingRect(s).width(); if(mbrw > mw1000) mbrw = mw1000; const ViewRect br_txt = ViewRect( mathXCoordinates(ViewXCoordinate(tick, false), ViewWCoordinate(mw2, true), MathAdd), ViewYCoordinate(my, true), ViewWCoordinate(mbrw, true), ViewHCoordinate(mh_m1, true)); // For testing... // fprintf(stderr, // "drawBarText: Bar text: bar:%d vx:%d vy:%d" // " vw:%d vh:%d tick:%d br x:%d y:%d w:%d h:%d\n", // bar, vr.x(), vr.y(), vr.width(), vr.height(), tick, // br_txt.x(), br_txt.y(), br_txt.width(), br_txt.height()); // fprintf(stderr, // "drawBarText: Bar text: bar:%d mx:%d my:%d" // " mw:%d mh:%d tick:%d br x:%d y:%d w:%d h:%d\n", // bar, mr.x(), mr.y(), mr.width(), mr.height(), tick, // br_txt._x._value, br_txt._y._value, br_txt._width._value, br_txt._height._value); if(intersects(br_txt, r)) { const QRect br_text_qr = asQRectMapped(br_txt); // For testing... // fprintf(stderr, "...bar text within range. xorg:%d xmag:%d xpos:%d" // " Drawing bar text at x:%d y:%d w:%d h:%d\n", // xorg, xmag, xpos, br_text_qr.x(), br_text_qr.y(), br_text_qr.width(), br_text_qr.height()); // p.drawText(mbr_txt, Qt::AlignLeft|Qt::AlignVCenter|Qt::TextDontClip, s); p.drawText(br_text_qr, Qt::AlignLeft|Qt::AlignVCenter|Qt::TextDontClip, s); } } View::ScaleRetStruct View::scale(bool drawText, int bar, double tpix, int raster) const { ScaleRetStruct ret; ret._drawBar = true; int n = 1; if(drawText) { //fprintf(stderr, "drawText: tpix < 64:%f\n", tpix); if (tpix <= 2) { //fprintf(stderr, "drawText: tpix <= 2\n"); n <<= 5; } else if (tpix <= 4) { //fprintf(stderr, "drawText: tpix <= 4\n"); n <<= 4; } else if (tpix < 8) { //fprintf(stderr, "drawText: tpix < 8\n"); n <<= 3; } else if (tpix <= 16) { //fprintf(stderr, "drawText: tpix <= 16\n"); n <<= 2; } else if (tpix < 32) { //fprintf(stderr, "drawText: tpix < 32\n"); n <<= 1; } ret._newRaster = raster * n; ret._isSmall = tpix < 64; if(!ret._isSmall) return ret; if (bar % n) ret._drawBar = false; } else { //fprintf(stderr, "tpix < 32:%f\n", tpix); n = 1; if (tpix <= 0.01) { //fprintf(stderr, "tpix <= 0.01\n"); n <<= 11; } else if (tpix <= 0.03125) { //fprintf(stderr, "tpix <= 0.03125\n"); n <<= 10; } else if (tpix <= 0.0625) { //fprintf(stderr, "tpix <= 0.0625\n"); n <<= 9; } else if (tpix <= 0.125) { //fprintf(stderr, "tpix <= 0.125\n"); n <<= 8; } else if (tpix <= 0.25) { //fprintf(stderr, "tpix <= 0.25\n"); n <<= 7; } else if (tpix <= 0.5) { //fprintf(stderr, "tpix <= 0.5\n"); n <<= 6; } else if (tpix <= 1.0) { //fprintf(stderr, "tpix <= 1.0\n"); n <<= 5; } else if (tpix <= 2.0) { //fprintf(stderr, "tpix <= 2.0\n"); n <<= 4; } else if (tpix <= 4.0) { //fprintf(stderr, "tpix <= 4.0\n"); n <<= 3; } else if (tpix <= 8.0) { //fprintf(stderr, "tpix <= 8.0\n"); n <<= 2; } else if (tpix <= 32.0) { //fprintf(stderr, "tpix <= 32.0\n"); n <<= 1; } ret._newRaster = raster * n; ret._isSmall = tpix < 32; if(!ret._isSmall) return ret; // FIXME: Hack? if (bar % (n > 1 ? (n >> 1) : n)) ret._drawBar = false; } return ret; } //--------------------------------------------------------- // drawTickRaster //--------------------------------------------------------- void View::drawTickRaster( QPainter& p, const QRect& mr, const QRegion& /*mrg*/, int raster, bool waveMode, bool /*useGivenColors*/, bool /*drawText*/, const QColor& bar_color, const QColor& beat_color, const QColor& fine_color, const QColor& coarse_color, const QColor& /*text_color*/, const QFont& /*large_font*/, const QFont& /*small_font*/ ) { const ViewRect r(mr, true); const ViewXCoordinate& x = r._x; const ViewXCoordinate x_2(mr.x() + mr.width(), true); //p.save(); bool wmtxen = p.worldMatrixEnabled(); p.setWorldMatrixEnabled(false); // Limiter required because MusEGlobal::sigmap.tickValues takes unsigned only ! const ViewXCoordinate lim_v0(0, false); const ViewXCoordinate x_lim = compareXCoordinates(x, lim_v0, CompareLess) ? lim_v0 : x; const ViewXCoordinate x_2lim = compareXCoordinates(x_2, lim_v0, CompareLess) ? lim_v0 : x_2; const int my = mr.y(); const int mh = mr.height(); const int mbottom = mr.bottom(); const ViewYCoordinate bottom(mr.bottom(), true); const ViewWCoordinate w2(2, true); const ViewHCoordinate h2(2, true); const ViewHCoordinate h_m1(mh - 1, true); const ViewHCoordinate h_m3(mh - 3, true); int bar1, bar2, beat, beatBar; ScaleRetStruct scale_info; int rast_x, next_rast_xx, rast_xx, raster_scaled; unsigned tick, conv_rast_xx, conv_next_rast_xx; double rast_mapx; QPen pen; pen.setCosmetic(true); // For testing... // fprintf(stderr, "View::drawTickRaster_new(): virt:%d drawText:%d mx:%d my:%d mw:%d mh:%draster%d\n", // virt(), drawText, mx, my, mw, mh, raster); if (waveMode) { MusEGlobal::sigmap.tickValues(MusEGlobal::tempomap.frame2tick(asUnmapped(x_lim)._value), &bar1, &beat, &tick); MusEGlobal::sigmap.tickValues(MusEGlobal::tempomap.frame2tick(asUnmapped(x_2lim)._value), &bar2, &beat, &tick); } else { MusEGlobal::sigmap.tickValues(asUnmapped(x_lim)._value, &bar1, &beat, &tick); MusEGlobal::sigmap.tickValues(asUnmapped(x_2lim)._value, &bar2, &beat, &tick); } //fprintf(stderr, "bar %d %d-%d=%d\n", bar, ntick, stick, ntick-stick); int stick = MusEGlobal::sigmap.bar2tick(bar1, 0, 0); int ntick, deltaTick; for (int bar = bar1; bar <= bar2; bar++, stick = ntick) { ntick = MusEGlobal::sigmap.bar2tick(bar+1, 0, 0); deltaTick = ntick - stick; int a, b=0; double tpix; if (waveMode) { a = MusEGlobal::tempomap.tick2frame(ntick); b = MusEGlobal::tempomap.tick2frame(stick); tpix = rmapx_f(a - b); } else { tpix = rmapx_f(deltaTick); } scale_info = scale(false, bar, tpix, raster); if(scale_info._drawBar) { const int tick_sm = waveMode ? b : stick; const ViewXCoordinate x_sm(tick_sm, false); if(compareXCoordinates(x_sm, x_2, CompareGreaterEqual)) break; if(compareXCoordinates(x_sm, x, CompareGreaterEqual)) //if(isXInRange(x_sm, x, x_2)) { const int mx_sm = asMapped(x_sm)._value; const ScaleRetStruct scale_info_text_lines = scale(true, bar, tpix, raster); if (scale_info_text_lines._drawBar) { // highlight lines drawn with text pen.setColor(coarse_color); } else { pen.setColor(bar_color); } p.setPen(pen); // For testing... // fprintf(stderr, // "is_small:%d tpix:%.15f Coarse line: tick_sm:%d mx_sm:%d stick:%d ntick:%d bar1:%d bar2:%d bar:%d\n", // scale_info._isSmall, tpix, tick_sm, mx_sm, stick, ntick, bar1, bar2, bar); // fprintf(stderr, "is_small:%d Coarse Line is within range." // " Drawing line at mx_sm:%d my:%d mx_sm:%d mbottom:%d tpix:%.15f\n", // scale_info._isSmall, mx_sm, my, mx_sm, mbottom, tpix); p.drawLine(mx_sm, my, mx_sm, mbottom); } } if(!scale_info._isSmall) { // If the raster is on 'bar' or is greater than a full bar, we limit the raster to a full bar. if (raster >= 4 && raster < deltaTick) { for (rast_xx = stick; rast_xx < ntick; /*rast_xx += raster*/) { next_rast_xx = rast_xx + raster; if(waveMode) { conv_rast_xx = MusEGlobal::tempomap.tick2frame(rast_xx); conv_next_rast_xx = MusEGlobal::tempomap.tick2frame(next_rast_xx); } else { conv_rast_xx = rast_xx; conv_next_rast_xx = next_rast_xx; } rast_mapx = rmapx_f(conv_next_rast_xx - conv_rast_xx); // NOTE: had to add this 4.0 magic value to draw the restart closer to how it used to be // without it not every point where you can put a note would have a line on many zoom levels rast_mapx = rast_mapx * 4.0; const ScaleRetStruct scale_info_fine_lines = scale(false, bar, rast_mapx, raster); raster_scaled = scale_info_fine_lines._newRaster; // Infinite loop protection. if(raster_scaled < 1) raster_scaled = 1; const ViewXCoordinate x_sm(conv_rast_xx, false); if(compareXCoordinates(x_sm, x_2, CompareGreaterEqual)) break; if(compareXCoordinates(x_sm, x, CompareGreaterEqual)) //if(isXInRange(x_sm, x, x_2)) { // Ignore already drawn bar lines. if(!scale_info._drawBar || rast_xx != stick) { // If the line happens to be exactly on a beat, emphasize that it's a beat line. // Don't bother drawing the beat lines if we want to always draw them, // since that is already done below. // FIXME: This loop doesn't know EXACTLY whether all beat lines will be drawn below. // So it is possible that in some low raster conditions the lines won't be drawn. // But to avoid redundant duplicate drawing of beat lines we do this anyway. // If you really want all the lines drawn, remove the second part of this conditional. // Since canvasShowGridBeatsAlways is false by default, most users won't notice. MusEGlobal::sigmap.tickValues(rast_xx, &beatBar, &beat, &tick); if(tick != 0 || (tick == 0 && !MusEGlobal::config.canvasShowGridBeatsAlways)) { if(tick == 0) pen.setColor(beat_color); else pen.setColor(fine_color); p.setPen(pen); rast_x = mapx(conv_rast_xx); // For testing... // fprintf(stderr, // "is_small:%d Fine line: raster:%d raster_scaled:%d rast_xx:%d" // " rast_x:%d stick:%d ntick:%d bar1:%d bar2:%d bar:%d\n", // scale_info._isSmall, raster, raster_scaled, rast_xx, // rast_x, stick, ntick, bar1, bar2, bar); p.drawLine(rast_x, my, rast_x, mbottom); } } } rast_xx += raster_scaled; } } } // If we want to show beat lines always, do them here instead of above. // If the raster is on 'bar' or is greater than a full bar, we limit the raster to a full bar. if(MusEGlobal::config.canvasShowGridBeatsAlways && raster > 0 /*&& raster < deltaTick*/ && !scale_info._isSmall) { int z, n; MusEGlobal::sigmap.timesig(stick, z, n); for (int beat = 0; beat < z; /*beat++*/) { int xx = MusEGlobal::sigmap.bar2tick(bar, beat, 0); int xx_e = MusEGlobal::sigmap.bar2tick(bar, beat + 1, 0); if(waveMode) { xx = MusEGlobal::tempomap.tick2frame(xx); xx_e = MusEGlobal::tempomap.tick2frame(xx_e); } rast_mapx = rmapx_f(xx_e - xx); // FIXME: Arbitrary value, hope this is satisfactory. rast_mapx = rast_mapx * 4.0; const ScaleRetStruct scale_info_beat_lines = scale(false, bar, rast_mapx, 1); raster_scaled = scale_info_beat_lines._newRaster; // Infinite loop protection. if(raster_scaled < 1) raster_scaled = 1; // Ignore already drawn bar lines. if(!scale_info._drawBar || beat != 0) { const ViewXCoordinate xx_v(xx, false); if(compareXCoordinates(xx_v, x_2, CompareGreaterEqual)) break; if(compareXCoordinates(xx_v, x, CompareGreaterEqual)) //if(isXInRange(xx_v, x, x_2)) { const int mxx = asMapped(xx_v)._value; // For testing... // fprintf(stderr, "is_small:%d Beat Line is within range." // " Drawing line at mxx:%d my:%d mbottom:%d...\n", // scale_info._isSmall, mxx, my, mbottom); pen.setColor(beat_color); p.setPen(pen); p.drawLine(mxx, my, mxx, mbottom); } } beat += raster_scaled; } } } //p.setWorldMatrixEnabled(true); p.setWorldMatrixEnabled(wmtxen); //p.restore(); } //--------------------------------------------------------- // map //--------------------------------------------------------- QRect View::mapDev(const QRect& r) const { return QRect(mapxDev(r.x()), mapyDev(r.y()), rmapxDev(r.width(), true), rmapyDev(r.height(), true)); } QPoint View::mapDev(const QPoint& r) const { return QPoint(mapxDev(r.x()), mapyDev(r.y())); } void View::mapDev(const QRegion& rg_in, QRegion& rg_out) const { #if QT_VERSION >= 0x050800 for(QRegion::const_iterator i = rg_in.begin(); i != rg_in.end(); ++i) rg_out += mapDev(*i); #else const QVector rects = rg_in.rects(); const int sz = rects.size(); for(int i = 0; i < sz; ++i) rg_out += mapDev(rects.at(i)); #endif } #if 0 // // Calculations using integer rounding methods... // QRect View::map(const QRect& r) const { int x, y, w, h; //printf("View::map xmag:%d xpos:%d xorg:%d\n", xmag, xpos, xorg); if (xmag < 0) { x = r.x()/(-xmag) - (xpos + xorg); // round down w = (r.width()-xmag-1) / (-xmag); // round up } else { x = r.x()*xmag - (xpos + xorg); w = r.width() * xmag; } if (ymag < 0) { y = r.y()/-ymag - (ypos + yorg); h = (r.height()-ymag-1) / (-ymag); } else { y = r.y() * ymag - (ypos + yorg); h = r.height() * ymag; } return QRect(x, y, w, h); } QPoint View::map(const QPoint& p) const { int x, y; if (xmag < 0) { x = p.x()/(-xmag) - (xpos + xorg); // round down } else { x = p.x()*xmag - (xpos + xorg); } if (ymag < 0) { y = p.y()/-ymag - (ypos + yorg); } else { y = p.y() * ymag - (ypos + yorg); } return QPoint(x, y); } int View::mapx(int x) const { if (xmag < 0) { return (x-xmag/2)/(-xmag) - (xpos + xorg); // round } else { return (x * xmag) - (xpos + xorg); } } int View::mapy(int y) const { if (ymag < 0) { return (y-ymag/2)/(-ymag) - (ypos + yorg); // round } else { return (y * ymag) - (ypos + yorg); } } int View::mapxDev(int x) const { int val; if (xmag <= 0) val = (x + xpos + xorg) * (-xmag); else val = (x + xpos + xorg + xmag / 2) / xmag; if (val < 0) // DEBUG val = 0; return val; } int View::mapyDev(int y) const { if (ymag <= 0) return (y + ypos + yorg) * (-ymag); else return (y + ypos + yorg + ymag / 2) / ymag; } // r == relative conversion int View::rmapx(int x) const { if (xmag < 0) return (x-xmag/2) / (-xmag); else return x * xmag; } int View::rmapy(int y) const { if (ymag < 0) return (y-ymag/2) / (-ymag); else return y * ymag; } int View::rmapxDev(int x) const { if (xmag <= 0) return x * (-xmag); else return (x + xmag/2) / xmag; } int View::rmapyDev(int y) const { if (ymag <= 0) return y * (-ymag); else return (y + ymag/2) / ymag; } #else // // Calculations using more accurate methods... // QRect View::map(const QRect& r) const { return QRect(mapx(r.x()), mapy(r.y()), rmapx(r.width(), true), rmapy(r.height(), true)); } QPoint View::map(const QPoint& p) const { return QPoint(mapx(p.x()), mapy(p.y())); } void View::map(const QRegion& rg_in, QRegion& rg_out) const { #if QT_VERSION >= 0x050800 for(QRegion::const_iterator i = rg_in.begin(); i != rg_in.end(); ++i) rg_out += map(*i); #else const QVector rects = rg_in.rects(); const int sz = rects.size(); for(int i = 0; i < sz; ++i) rg_out += map(rects.at(i)); #endif } int View::mapx(int x) const { if (xmag < 0) return floor(double(x) / double(-xmag)) - xpos - xorg; else return x * xmag - xpos - xorg; } int View::mapy(int y) const { if (ymag < 0) return floor(double(y) / double(-ymag)) - ypos - yorg; else return y * ymag - ypos - yorg; } int View::mapxDev(int x) const { if (xmag <= 0) return (x + xpos + xorg) * -xmag; else return floor(double(x + xpos + xorg) / double(xmag)); } int View::mapyDev(int y) const { if (ymag <= 0) return (y + ypos + yorg) * -ymag; else return floor(double(y + ypos + yorg) / double(ymag)); } //----------------------------- // r == relative conversion //----------------------------- QRect View::rmap(const QRect& r) const { return QRect(rmapx(r.x()), rmapy(r.y()), rmapx(r.width(), true), rmapy(r.height(), true)); } int View::rmapx(int x, bool round) const { if (xmag < 0) { if(round) return ceil(double(x) / double(-xmag)); else return floor(double(x) / double(-xmag)); } else return x * xmag; } int View::rmapy(int y, bool round) const { if (ymag < 0) { if(round) return ceil(double(y) / double(-ymag)); else return floor(double(y) / double(-ymag)); } else return y * ymag; } int View::rmapxDev(int x, bool round) const { if (xmag <= 0) return x * (-xmag); else { if(round) return ceil(double(x) / double(xmag)); else return floor(double(x) / double(xmag)); } } int View::rmapyDev(int y, bool round) const { if (ymag <= 0) return y * (-ymag); else { if(round) return ceil(double(y) / double(ymag)); else return floor(double(y) / double(ymag)); } } QPoint View::rmapDev(const QPoint& p, bool round) const { int x, y; if (xmag <= 0) x = p.x() * (-xmag); else { if(round) x = ceil(double(p.x()) / double(xmag)); else x = floor(double(p.x()) / double(xmag)); } if (ymag <= 0) y = p.y() * (-ymag); else { if(round) y = ceil(double(p.y()) / double(ymag)); else y = floor(double(p.y()) / double(ymag)); } return QPoint(x, y); } #endif double View::rmapx_f(double x) const { if (xmag < 0) return x / double(-xmag); else return x * double(xmag); } double View::rmapy_f(double y) const { if (ymag < 0) return y / double(-ymag); else return y * double(ymag); } double View::rmapxDev_f(double x) const { if (xmag <= 0) return x * double(-xmag); else return x / double(xmag); } double View::rmapyDev_f(double y) const { if (ymag <= 0) return y * double(-ymag); else return y / double(ymag); } //-------------------------------------------------- // Lossless intersections (in x and/or y, depending) // for any magnification. //-------------------------------------------------- void ViewRect::dump(const char* header) const { if(header) fprintf(stderr, "%s\n", header); else fprintf(stderr, "ViewRect dump:\n"); fprintf(stderr, "x:%8d map:%d rel:%d\t y:%8d map:%d rel:%d\t w:%8d map:%d rel:%d\t h:%8d map:%d rel:%d\n\n", _x._value, _x.isMapped(), _x.isRelative(), _y._value, _y.isMapped(), _y.isRelative(), _width._value, _width.isMapped(), _width.isRelative(), _height._value, _height.isMapped(), _height.isRelative()); } // Returns the intersection of the rectangles. The coordinate x, y, and w, h are mapped // if the view's xmag > 0, and unmapped if not. The coordinate w, h are mapped if the // view's ymag > 0, and unmapped if not. ViewRect View::intersected(const ViewRect& r1, const ViewRect& r2) const { int fx_1, fw, fy_1, fh; const bool xunmap = xmag <= 0; const bool yunmap = ymag <= 0; if (xunmap) { const int vr1x = r1._x.isMapped() ? mapxDev(r1._x._value) : r1._x._value; const int vr1x_2 = vr1x + (r1._width.isMapped() ? rmapxDev(r1._width._value) : r1._width._value); const int vr2x = r2._x.isMapped() ? mapxDev(r2._x._value) : r2._x._value; const int vr2x_2 = vr2x + (r2._width.isMapped() ? rmapxDev(r2._width._value) : r2._width._value); fx_1 = vr1x > vr2x ? vr1x : vr2x; const int fx_2 = vr1x_2 < vr2x_2 ? vr1x_2 : vr2x_2; fw = fx_2 - fx_1; // For testing... // fprintf(stderr, "View::intersected: xmag:%d vr1x:%d vr1x_2:%d vr2x:%d vr2x_2:%d fx_1:%d fx_2:%d\n", // xmag, vr1x, vr1x_2, vr2x, vr2x_2, fx_1, fx_2); } else { const int mr1x = r1._x.isMapped() ? r1._x._value : mapx(r1._x._value); const int mr1x_2 = mr1x + (r1._width.isMapped() ? r1._width._value : rmapx(r1._width._value)); const int mr2x = r2._x.isMapped() ? r2._x._value : mapx(r2._x._value); const int mr2x_2 = mr2x + (r2._width.isMapped() ? r2._width._value : rmapx(r2._width._value)); fx_1 = mr1x > mr2x ? mr1x : mr2x; const int fx_2 = mr1x_2 < mr2x_2 ? mr1x_2 : mr2x_2; fw = fx_2 - fx_1; // For testing... // fprintf(stderr, "View::intersected: xmag:%d mr1x:%d mr1x_2:%d mr2x:%d mr2x_2:%d fx_1:%d fx_2:%d\n", // xmag, mr1x, mr1x_2, mr2x, mr2x_2, fx_1, fx_2); } if (ymag <= 0) { const int vr1y = r1._y.isMapped() ? mapyDev(r1._y._value) : r1._y._value; const int vr1y_2 = vr1y + (r1._height.isMapped() ? rmapyDev(r1._height._value) : r1._height._value); const int vr2y = r2._y.isMapped() ? mapyDev(r2._y._value) : r2._y._value; const int vr2y_2 = vr2y + (r2._height.isMapped() ? rmapyDev(r2._height._value) : r2._height._value); fy_1 = vr1y > vr2y ? vr1y : vr2y; const int fy_2 = vr1y_2 < vr2y_2 ? vr1y_2 : vr2y_2; fh = fy_2 - fy_1; // For testing... // fprintf(stderr, "View::intersected: ymag:%d vr1y:%d vr1y_2:%d vr2y:%d vr2y_2:%d fy_1:%d fy_2:%d\n", // ymag, vr1y, vr1y_2, vr2y, vr2y_2, fy_1, fy_2); } else { const int mr1y = r1._y.isMapped() ? r1._y._value : mapy(r1._y._value); const int mr1y_2 = mr1y + (r1._height.isMapped() ? r1._height._value : rmapy(r1._height._value)); const int mr2y = r2._y.isMapped() ? r2._y._value : mapy(r2._y._value); const int mr2y_2 = mr2y + (r2._height.isMapped() ? r2._height._value : rmapy(r2._height._value)); fy_1 = mr1y > mr2y ? mr1y : mr2y; const int fy_2 = mr1y_2 < mr2y_2 ? mr1y_2 : mr2y_2; fh = fy_2 - fy_1; // For testing... // fprintf(stderr, "View::intersected: ymag:%d mr1y:%d mr1y_2:%d mr2y:%d mr2y_2:%d fy_1:%d fy_2:%d\n", // ymag, mr1y, mr1y_2, mr2y, mr2y_2, fy_1, fy_2); } return ViewRect(ViewXCoordinate(fx_1, !xunmap), ViewYCoordinate(fy_1, !yunmap), ViewWCoordinate(fw, !xunmap), ViewHCoordinate(fh, !yunmap)); } // Returns the mapped intersection of the mapped rectangle and the unmapped rectangle. QRect View::intersectedMap(const QRect& mapped_r, const QRect& unmapped_r) const { int mfx_1, mfw, mfy_1, mfh; if (xmag <= 0) { const int vmx = mapxDev(mapped_r.x()); const int vmx_2 = vmx + rmapxDev(mapped_r.width()); const int vx = unmapped_r.x(); const int vx_2 = vx + unmapped_r.width(); const int vfx_1 = vmx > vx ? vmx : vx; const int vfx_2 = vmx_2 < vx_2 ? vmx_2 : vx_2; mfx_1 = mapx(vfx_1); mfw = rmapx(vfx_2 - vfx_1); } else { const int mx = mapped_r.x(); const int mx_2 = mx + mapped_r.width(); const int mvx = mapx(unmapped_r.x()); const int mvx_2 = mvx + rmapx(unmapped_r.width()); mfx_1 = mx > mvx ? mx : mvx; const int mfx_2 = mx_2 < mvx_2 ? mx_2 : mvx_2; mfw = mfx_2 - mfx_1; } if (ymag <= 0) { const int vmy = mapyDev(mapped_r.y()); const int vmy_2 = vmy + rmapyDev(mapped_r.height()); const int vy = unmapped_r.y(); const int vy_2 = vy + unmapped_r.height(); const int vfy_1 = vmy > vy ? vmy : vy; const int vfy_2 = vmy_2 < vy_2 ? vmy_2 : vy_2; mfy_1 = mapy(vfy_1); mfh = rmapy(vfy_2 - vfy_1); } else { const int my = mapped_r.y(); const int my_2 = my + mapped_r.height(); const int mvy = mapy(unmapped_r.y()); const int mvy_2 = mvy + rmapy(unmapped_r.height()); mfy_1 = my > mvy ? my : mvy; const int mfy_2 = my_2 < mvy_2 ? my_2 : mvy_2; mfh = mfy_2 - mfy_1; } return QRect(mfx_1, mfy_1, mfw, mfh); } // Returns the unmapped intersection of the mapped rectangle and the unmapped rectangle. QRect View::intersectedUnmap(const QRect& mapped_r, const QRect& unmapped_r) const { int vfx_1, vfw, vfy_1, vfh; if (xmag <= 0) { const int vmx = mapxDev(mapped_r.x()); const int vmx_2 = vmx + rmapxDev(mapped_r.width()); const int vx = unmapped_r.x(); const int vx_2 = vx + unmapped_r.width(); vfx_1 = vmx > vx ? vmx : vx; const int vfx_2 = vmx_2 < vx_2 ? vmx_2 : vx_2; vfw = vfx_2 - vfx_1; } else { const int mx = mapped_r.x(); const int mx_2 = mx + mapped_r.width(); const int mvx = mapx(unmapped_r.x()); const int mvx_2 = mvx + rmapx(unmapped_r.width()); const int mfx_1 = mx > mvx ? mx : mvx; const int mfx_2 = mx_2 < mvx_2 ? mx_2 : mvx_2; vfx_1 = mapxDev(mfx_1); vfw = rmapxDev(mfx_2 - mfx_1); } if (ymag <= 0) { const int vmy = mapyDev(mapped_r.y()); const int vmy_2 = vmy + rmapyDev(mapped_r.height()); const int vy = unmapped_r.y(); const int vy_2 = vy + unmapped_r.height(); vfy_1 = vmy > vy ? vmy : vy; const int vfy_2 = vmy_2 < vy_2 ? vmy_2 : vy_2; vfh = vfy_2 - vfy_1; } else { const int my = mapped_r.y(); const int my_2 = my + mapped_r.height(); const int mvy = mapy(unmapped_r.y()); const int mvy_2 = mvy + rmapy(unmapped_r.height()); const int mfy_1 = my > mvy ? my : mvy; const int mfy_2 = my_2 < mvy_2 ? my_2 : mvy_2; vfy_1 = mapyDev(mfy_1); vfh = rmapyDev(mfy_2 - mfy_1); } return QRect(vfx_1, vfy_1, vfw, vfh); } // Returns true if the mapped rectangle intersects the unmapped rectangle. bool View::intersects(const QRect& mapped_r, const QRect& unmapped_r) const { int vfx_1, vfx_2, vfy_1, vfy_2; if (xmag <= 0) { const int vmx = mapxDev(mapped_r.x()); const int vmx_2 = vmx + rmapxDev(mapped_r.width()); const int vx = unmapped_r.x(); const int vx_2 = vx + unmapped_r.width(); vfx_1 = vmx > vx ? vmx : vx; vfx_2 = vmx_2 < vx_2 ? vmx_2 : vx_2; } else { const int mx = mapped_r.x(); const int mx_2 = mx + mapped_r.width(); const int mvx = mapx(unmapped_r.x()); const int mvx_2 = mvx + rmapx(unmapped_r.width()); vfx_1 = mx > mvx ? mx : mvx; vfx_2 = mx_2 < mvx_2 ? mx_2 : mvx_2; } if (ymag <= 0) { const int vmy = mapyDev(mapped_r.y()); const int vmy_2 = vmy + rmapyDev(mapped_r.height()); const int vy = unmapped_r.y(); const int vy_2 = vy + unmapped_r.height(); vfy_1 = vmy > vy ? vmy : vy; vfy_2 = vmy_2 < vy_2 ? vmy_2 : vy_2; } else { const int my = mapped_r.y(); const int my_2 = my + mapped_r.height(); const int mvy = mapy(unmapped_r.y()); const int mvy_2 = mvy + rmapy(unmapped_r.height()); vfy_1 = my > mvy ? my : mvy; vfy_2 = my_2 < mvy_2 ? my_2 : mvy_2; } return vfx_1 < vfx_2 && vfy_1 < vfy_2; } // Returns true if the rectangles intersect. bool View::intersects(const ViewRect& r1, const ViewRect& r2) const { int fx_1, fx_2, fy_1, fy_2; if (xmag <= 0) { const int vr1x = r1._x.isMapped() ? mapxDev(r1._x._value) : r1._x._value; const int vr1x_2 = vr1x + (r1._width.isMapped() ? rmapxDev(r1._width._value) : r1._width._value); const int vr2x = r2._x.isMapped() ? mapxDev(r2._x._value) : r2._x._value; const int vr2x_2 = vr2x + (r2._width.isMapped() ? rmapxDev(r2._width._value) : r2._width._value); fx_1 = vr1x > vr2x ? vr1x : vr2x; fx_2 = vr1x_2 < vr2x_2 ? vr1x_2 : vr2x_2; // For testing... // fprintf(stderr, "View::intersects: xmag:%d vr1x:%d vr1x_2:%d vr2x:%d vr2x_2:%d fx_1:%d fx_2:%d\n", // xmag, vr1x, vr1x_2, vr2x, vr2x_2, fx_1, fx_2); } else { const int mr1x = r1._x.isMapped() ? r1._x._value : mapx(r1._x._value); const int mr1x_2 = mr1x + (r1._width.isMapped() ? r1._width._value : rmapx(r1._width._value)); const int mr2x = r2._x.isMapped() ? r2._x._value : mapx(r2._x._value); const int mr2x_2 = mr2x + (r2._width.isMapped() ? r2._width._value : rmapx(r2._width._value)); fx_1 = mr1x > mr2x ? mr1x : mr2x; fx_2 = mr1x_2 < mr2x_2 ? mr1x_2 : mr2x_2; // For testing... // fprintf(stderr, "View::intersects: xmag:%d mr1x:%d mr1x_2:%d mr2x:%d mr2x_2:%d fx_1:%d fx_2:%d\n", // xmag, mr1x, mr1x_2, mr2x, mr2x_2, fx_1, fx_2); } if (ymag <= 0) { const int vr1y = r1._y.isMapped() ? mapyDev(r1._y._value) : r1._y._value; const int vr1y_2 = vr1y + (r1._height.isMapped() ? rmapyDev(r1._height._value) : r1._height._value); const int vr2y = r2._y.isMapped() ? mapyDev(r2._y._value) : r2._y._value; const int vr2y_2 = vr2y + (r2._height.isMapped() ? rmapyDev(r2._height._value) : r2._height._value); fy_1 = vr1y > vr2y ? vr1y : vr2y; fy_2 = vr1y_2 < vr2y_2 ? vr1y_2 : vr2y_2; // For testing... // fprintf(stderr, "View::intersects: ymag:%d vr1y:%d vr1y_2:%d vr2y:%d vr2y_2:%d fy_1:%d fy_2:%d\n", // ymag, vr1y, vr1y_2, vr2y, vr2y_2, fy_1, fy_2); } else { const int mr1y = r1._y.isMapped() ? r1._y._value : mapy(r1._y._value); const int mr1y_2 = mr1y + (r1._height.isMapped() ? r1._height._value : rmapy(r1._height._value)); const int mr2y = r2._y.isMapped() ? r2._y._value : mapy(r2._y._value); const int mr2y_2 = mr2y + (r2._height.isMapped() ? r2._height._value : rmapy(r2._height._value)); fy_1 = mr1y > mr2y ? mr1y : mr2y; fy_2 = mr1y_2 < mr2y_2 ? mr1y_2 : mr2y_2; // For testing... // fprintf(stderr, "View::intersects: ymag:%d mr1y:%d mr1y_2:%d mr2y:%d mr2y_2:%d fy_1:%d fy_2:%d\n", // ymag, mr1y, mr1y_2, mr2y, mr2y_2, fy_1, fy_2); } return fx_1 < fx_2 && fy_1 < fy_2; } //-------------------------------------------- // Lossless comparisons for any magnification. //-------------------------------------------- // Compares x1 and x2. x1 is the left operand and x2 is the right. For example x1 < x2. bool View::compareXCoordinates(const ViewXCoordinate& x1, const ViewXCoordinate& x2, const CoordinateCompareMode& mode) const { int xx1, xx2; if (xmag <= 0) { xx1 = x1.isMapped() ? mapxDev(x1._value) : x1._value; xx2 = x2.isMapped() ? mapxDev(x2._value) : x2._value; } else { xx1 = x1.isMapped() ? x1._value : mapx(x1._value); xx2 = x2.isMapped() ? x2._value : mapx(x2._value); } switch(mode) { case CompareLess: return xx1 < xx2; case CompareGreater: return xx1 > xx2; case CompareLessEqual: return xx1 <= xx2; case CompareGreaterEqual: return xx1 >= xx2; case CompareEqual: return xx1 == xx2; } return false; } // Compares y1 and y2. y1 is the left operand and y2 is the right. For example y1 < y2. bool View::compareYCoordinates(const ViewYCoordinate& y1, const ViewYCoordinate& y2, const CoordinateCompareMode& mode) const { int yy1, yy2; if (ymag <= 0) { yy1 = y1.isMapped() ? mapyDev(y1._value) : y1._value; yy2 = y2.isMapped() ? mapyDev(y2._value) : y2._value; } else { yy1 = y1.isMapped() ? y1._value : mapy(y1._value); yy2 = y2.isMapped() ? y2._value : mapy(y2._value); } switch(mode) { case CompareLess: return yy1 < yy2; case CompareGreater: return yy1 > yy2; case CompareLessEqual: return yy1 <= yy2; case CompareGreaterEqual: return yy1 >= yy2; case CompareEqual: return yy1 == yy2; } return false; } // Compares w1 and w2. w1 is the left operand and w2 is the right. For example w1 < w2. bool View::compareWCoordinates(const ViewWCoordinate& w1, const ViewWCoordinate& w2, const CoordinateCompareMode& mode) const { int ww1, ww2; if (xmag <= 0) { ww1 = w1.isMapped() ? rmapxDev(w1._value) : w1._value; ww2 = w2.isMapped() ? rmapxDev(w2._value) : w2._value; } else { ww1 = w1.isMapped() ? w1._value : rmapx(w1._value); ww2 = w2.isMapped() ? w2._value : rmapx(w2._value); } switch(mode) { case CompareLess: return ww1 < ww2; case CompareGreater: return ww1 > ww2; case CompareLessEqual: return ww1 <= ww2; case CompareGreaterEqual: return ww1 >= ww2; case CompareEqual: return ww1 == ww2; } return false; } // Compares h1 and h2. h1 is the left operand and h2 is the right. For example h1 < h2. bool View::compareHCoordinates(const ViewHCoordinate& h1, const ViewHCoordinate& h2, const CoordinateCompareMode& mode) const { int hh1, hh2; if (ymag <= 0) { hh1 = h1.isMapped() ? rmapyDev(h1._value) : h1._value; hh2 = h2.isMapped() ? rmapyDev(h2._value) : h2._value; } else { hh1 = h1.isMapped() ? h1._value : rmapy(h1._value); hh2 = h2.isMapped() ? h2._value : rmapy(h2._value); } switch(mode) { case CompareLess: return hh1 < hh2; case CompareGreater: return hh1 > hh2; case CompareLessEqual: return hh1 <= hh2; case CompareGreaterEqual: return hh1 >= hh2; case CompareEqual: return hh1 == hh2; } return false; } // Returns true if x is >= x1 and x < x2. bool View::isXInRange(ViewXCoordinate x, ViewXCoordinate x1, ViewXCoordinate x2) const { return compareXCoordinates(x, x1, CompareGreaterEqual) && compareXCoordinates(x, x2, CompareLess); } // Returns true if y is >= y1 and y < y2. bool View::isYInRange(ViewYCoordinate y, ViewYCoordinate y1, ViewYCoordinate y2) const { return compareYCoordinates(y, y1, CompareGreaterEqual) && compareYCoordinates(y, y2, CompareLess); } //-------------------------------------------- // Lossless math for any magnification. //-------------------------------------------- // Returns a ViewXCoordinate of the math operation on x1 and x2. The result is mapped if the view's xmag > 0, and unmapped if not. ViewXCoordinate View::mathXCoordinates(const ViewXCoordinate& x1, const ViewXCoordinate& x2, const CoordinateMathMode& mode) const { if(xmag <= 0) return ViewXCoordinate(doCoordinateMath(asIntUnmapped(x1), asIntUnmapped(x2), mode), false); return ViewXCoordinate(doCoordinateMath(asIntMapped(x1), asIntMapped(x2), mode), true); } // Returns a ViewXCoordinate of the math operation on x1 and w1. The result is mapped if the view's xmag > 0, and unmapped if not. ViewXCoordinate View::mathXCoordinates(const ViewXCoordinate& x1, const ViewWCoordinate& w1, const CoordinateMathMode& mode) const { if(xmag <= 0) return ViewXCoordinate(doCoordinateMath(asIntUnmapped(x1), asIntUnmapped(w1), mode), false); return ViewXCoordinate(doCoordinateMath(asIntMapped(x1), asIntMapped(w1), mode), true); } // Returns a ViewWCoordinate of the math operation on w1 and w2. The result is mapped if the view's xmag > 0, and unmapped if not. ViewWCoordinate View::mathXCoordinates(const ViewWCoordinate& w1, const ViewWCoordinate& w2, const CoordinateMathMode& mode) const { if(xmag <= 0) return ViewWCoordinate(doCoordinateMath(asIntUnmapped(w1), asIntUnmapped(w2), mode), false); return ViewWCoordinate(doCoordinateMath(asIntMapped(w1), asIntMapped(w2), mode), true); } // Returns a ViewYCoordinate of the math operation on y1 and y2. The result is mapped if the view's ymag > 0, and unmapped if not. ViewYCoordinate View::mathYCoordinates(const ViewYCoordinate& y1, const ViewYCoordinate& y2, const CoordinateMathMode& mode) const { if(ymag <= 0) return ViewYCoordinate(doCoordinateMath(asIntUnmapped(y1), asIntUnmapped(y2), mode), false); return ViewYCoordinate(doCoordinateMath(asIntMapped(y1), asIntMapped(y2), mode), true); } // Returns a ViewYCoordinate of the math math operation on y1 and h1. The result is mapped if the view's ymag > 0, and unmapped if not. ViewYCoordinate View::mathYCoordinates(const ViewYCoordinate& y1, const ViewHCoordinate& h1, const CoordinateMathMode& mode) const { if(ymag <= 0) return ViewYCoordinate(doCoordinateMath(asIntUnmapped(y1), asIntUnmapped(h1), mode), false); return ViewYCoordinate(doCoordinateMath(asIntMapped(y1), asIntMapped(h1), mode), true); } // Returns a ViewHCoordinate of the math math operation on h1 and h2. The result is mapped if the view's ymag > 0, and unmapped if not. ViewHCoordinate View::mathYCoordinates(const ViewHCoordinate& h1, const ViewHCoordinate& h2, const CoordinateMathMode& mode) const { if(ymag <= 0) return ViewHCoordinate(doCoordinateMath(asIntUnmapped(h1), asIntUnmapped(h2), mode), false); return ViewHCoordinate(doCoordinateMath(asIntMapped(h1), asIntMapped(h2), mode), true); } // Performs math operation on x1. The result is mapped if the view's xmag > 0, and unmapped if not. // Returns a reference to x1. ViewXCoordinate& View::mathRefXCoordinates(ViewXCoordinate& x1, const ViewXCoordinate& x2, const CoordinateMathMode& mode) const { if(xmag <= 0) x1 = ViewXCoordinate(doCoordinateMath(asIntUnmapped(x1), asIntUnmapped(x2), mode), false); else x1 = ViewXCoordinate(doCoordinateMath(asIntMapped(x1), asIntMapped(x2), mode), true); return x1; } // Performs math operation on x1. The result is mapped if the view's xmag > 0, and unmapped if not. // Returns a reference to x1. ViewXCoordinate& View::mathRefXCoordinates(ViewXCoordinate& x1, const ViewWCoordinate& w1, const CoordinateMathMode& mode) const { if(xmag <= 0) x1 = ViewXCoordinate(doCoordinateMath(asIntUnmapped(x1), asIntUnmapped(w1), mode), false); else x1 = ViewXCoordinate(doCoordinateMath(asIntMapped(x1), asIntMapped(w1), mode), true); return x1; } // Performs math operation on w1. The result is mapped if the view's xmag > 0, and unmapped if not. // Returns a reference to w1. ViewWCoordinate& View::mathRefXCoordinates(ViewWCoordinate& w1, const ViewWCoordinate& w2, const CoordinateMathMode& mode) const { if(xmag <= 0) w1 = ViewWCoordinate(doCoordinateMath(asIntUnmapped(w1), asIntUnmapped(w2), mode), false); else w1 = ViewWCoordinate(doCoordinateMath(asIntMapped(w1), asIntMapped(w2), mode), true); return w1; } // Performs math operation on y1. The result is mapped if the view's ymag > 0, and unmapped if not. // Returns a reference to y1. ViewYCoordinate& View::mathRefYCoordinates(ViewYCoordinate& y1, const ViewYCoordinate& y2, const CoordinateMathMode& mode) const { if(ymag <= 0) y1 = ViewYCoordinate(doCoordinateMath(asIntUnmapped(y1), asIntUnmapped(y2), mode), false); else y1 = ViewYCoordinate(doCoordinateMath(asIntMapped(y1), asIntMapped(y2), mode), true); return y1; } // Performs math operation on y1. The result is mapped if the view's ymag > 0, and unmapped if not. // Returns a reference to y1. ViewYCoordinate& View::mathRefYCoordinates(ViewYCoordinate& y1, const ViewHCoordinate& h1, const CoordinateMathMode& mode) const { if(ymag <= 0) y1 = ViewYCoordinate(doCoordinateMath(asIntUnmapped(y1), asIntUnmapped(h1), mode), false); else y1 = ViewYCoordinate(doCoordinateMath(asIntMapped(y1), asIntMapped(h1), mode), true); return y1; } // Performs math operation on h1. The result is mapped if the view's ymag > 0, and unmapped if not. // Returns a reference to h1. ViewHCoordinate& View::mathRefYCoordinates(ViewHCoordinate& h1, const ViewHCoordinate& h2, const CoordinateMathMode& mode) const { if(ymag <= 0) h1 = ViewHCoordinate(doCoordinateMath(asIntUnmapped(h1), asIntUnmapped(h2), mode), false); else h1 = ViewHCoordinate(doCoordinateMath(asIntMapped(h1), asIntMapped(h2), mode), true); return h1; } // Adjusts the x, y, w and h of the rectangle. Returns a reference to the rectangle. ViewRect& View::adjustRect(ViewRect& r, const ViewWCoordinate& dx, const ViewHCoordinate& dy, const ViewWCoordinate& dw, const ViewHCoordinate& dh) const { mathRefXCoordinates(r._x, dx, MathAdd); mathRefYCoordinates(r._y, dy, MathAdd); mathRefXCoordinates(r._width, dw, MathAdd); mathRefYCoordinates(r._height, dh, MathAdd); return r; } // Returns a rectangle with the x, y, w and h adjusted. ViewRect View::adjustedRect(const ViewRect& r, const ViewWCoordinate& dx, const ViewHCoordinate& dy, const ViewWCoordinate& dw, const ViewHCoordinate& dh) const { return ViewRect(mathXCoordinates(r._x, dx, MathAdd), mathYCoordinates(r._y, dy, MathAdd), mathXCoordinates(r._width, dw, MathAdd), mathYCoordinates(r._height, dh, MathAdd)); } } // namespace MusEGui muse-4.0.0/src/muse/components/view.h000066400000000000000000000571431404105311000175330ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: view.h,v 1.2.2.1 2008/01/26 07:23:21 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __VIEW_H__ #define __VIEW_H__ #include #include #include #include #include #include // Forward declarations: class QDropEvent; class QKeyEvent; class QMouseEvent; class QPaintEvent; class QPainter; class QResizeEvent; namespace MusEGui { //--------------------- // ViewCoordinate //--------------------- class ViewCoordinate { public: enum CoordinateFlags { NoFlags = 0x0, IsVertical = 0x01, IsMapped = 0x02, IsRelative = 0x04, IsMappedAndRelative = IsMapped | IsRelative }; typedef int CoordinateFlags_t; int _value; CoordinateFlags_t _flags; ViewCoordinate(int value = 0, CoordinateFlags_t flags = NoFlags) : _value(value), _flags(flags) { } bool isMapped() const { return _flags & IsMapped; } void setMapped(bool m) { m ? _flags |= IsMapped : _flags &= ~IsMapped; } bool isRelative() const { return _flags & IsRelative; } bool isVertical() const { return _flags & IsVertical; } }; class ViewXCoordinate : public ViewCoordinate { public: ViewXCoordinate(int value = 0, bool is_mapped = false) : ViewCoordinate(value, NoFlags | (is_mapped ? IsMapped : NoFlags)) { } }; class ViewYCoordinate : public ViewCoordinate { public: ViewYCoordinate(int value = 0, bool is_mapped = false) : ViewCoordinate(value, IsVertical | (is_mapped ? IsMapped : NoFlags)) { } }; class ViewWCoordinate : public ViewXCoordinate { public: ViewWCoordinate(int value = 0, bool is_mapped = false) : ViewXCoordinate(value, is_mapped) { _flags |= IsRelative; } }; class ViewHCoordinate : public ViewYCoordinate { public: ViewHCoordinate(int value = 0, bool is_mapped = false) : ViewYCoordinate(value, is_mapped) { _flags |= IsRelative; } }; //--------------------- // ViewRect //--------------------- class ViewRect { public: ViewXCoordinate _x; ViewYCoordinate _y; ViewWCoordinate _width; ViewHCoordinate _height; ViewRect() { } ViewRect(const ViewXCoordinate& x, const ViewYCoordinate& y, const ViewWCoordinate& width, const ViewHCoordinate& height) : _x(x), _y(y), _width(width), _height(height) { } ViewRect(const QRect& r, bool is_mapped) : _x(ViewXCoordinate(r.x(), is_mapped)), _y(ViewYCoordinate(r.y(), is_mapped)), _width(ViewWCoordinate(r.width(), is_mapped)), _height(ViewHCoordinate(r.height(), is_mapped)) { } void dump(const char* header = 0) const; }; //--------------------------------------------------------- // View // horizontal View with double buffering //--------------------------------------------------------- class View : public QWidget { Q_OBJECT public: // CAUTION: Since one or both of the integer operands might get (un)mapped in an expanded way (multiplied), // an exact equality test might never be true! Try to avoid equality tests when in doubt, use the others. enum CoordinateCompareMode { CompareLess, CompareGreater, CompareLessEqual, CompareGreaterEqual, CompareEqual }; enum CoordinateMathMode { MathAdd, MathSubtract, MathMultiply, MathDivide, MathModulo }; private: QPixmap pm; // for double buffering bool pmValid; QPixmap bgPixmap; // background Pixmap QBrush brush; bool _virt; protected: int xorg; int yorg; int xpos, ypos; int xmag, ymag; struct ScaleRetStruct { bool _drawBar; bool _isSmall; int _newRaster; }; ScaleRetStruct scale(bool drawText, int bar, double tpix, int raster = 0) const; //void drawBarText(QPainter& p, int tick, int bar, const QRect& vr, const QColor& textColor, const QFont& font) const; void drawBarText(QPainter& p, int tick, int bar, const QRect& mr, const QColor& textColor, const QFont& font) const; virtual void keyPressEvent(QKeyEvent* event); virtual void keyReleaseEvent(QKeyEvent* event); virtual void mousePressEvent(QMouseEvent* event); virtual void mouseDoubleClickEvent(QMouseEvent* event); virtual void mouseMoveEvent(QMouseEvent* event); virtual void mouseReleaseEvent(QMouseEvent* event); virtual void dropEvent(QDropEvent* event); virtual void draw(QPainter&, const QRect&, const QRegion& = QRegion()) {} virtual void drawOverlay(QPainter&, const QRect&, const QRegion& = QRegion()) {} virtual QRect overlayRect() const { return QRect(0, 0, 0, 0); } virtual void drawTickRaster(QPainter& p, const QRect&, const QRegion& = QRegion(), int raster = 0, bool waveMode = false, bool useGivenColors = false, bool drawText = false, const QColor& bar_color = Qt::cyan, // default to very obvious color to visualize uninitialized drawing const QColor& beat_color = Qt::cyan, const QColor& fine_color = Qt::cyan, const QColor& coarse_color = Qt::cyan, const QColor& text_color = Qt::cyan, const QFont& large_font = QFont(), const QFont& small_font = QFont() ); virtual void pdraw(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void paintEvent(QPaintEvent* ev); void redraw(const QRect&); void redraw(const QRegion&); void paint(const QRect&, const QRegion& = QRegion()); virtual void resizeEvent(QResizeEvent*); virtual void viewKeyPressEvent(QKeyEvent*); virtual void viewKeyReleaseEvent(QKeyEvent*); virtual void viewMousePressEvent(QMouseEvent*) {} virtual void viewMouseDoubleClickEvent(QMouseEvent*) {} virtual void viewMouseMoveEvent(QMouseEvent*) {} virtual void viewMouseReleaseEvent(QMouseEvent*) {} virtual void viewDropEvent(QDropEvent*) {} QRect map(const QRect&) const; QPoint map(const QPoint&) const; void map(const QRegion& rg_in, QRegion& rg_out) const; QRect mapDev(const QRect&) const; QPoint mapDev(const QPoint&) const; void mapDev(const QRegion& rg_in, QRegion& rg_out) const; int mapx(int x) const; int mapy(int y) const; int mapyDev(int y) const; int mapxDev(int x) const; int rmapy(int y, bool round = false) const; int rmapyDev(int y, bool round = false) const; QPoint rmapDev(const QPoint&, bool round = false) const; QRect devToVirt(const QRect&) const; void devToVirt(const QRegion& rg_in, QRegion& rg_out) const; double rmapx_f(double x) const; double rmapy_f(double y) const; double rmapxDev_f(double x) const; double rmapyDev_f(double y) const; void setPainter(QPainter& p); inline int doCoordinateMath(const int& v1, const int& v2, const CoordinateMathMode& mode) const { switch(mode) { case MathAdd: return v1 + v2; break; case MathSubtract: return v1 - v2; break; case MathMultiply: return v1 * v2; break; case MathDivide: return v1 / v2; break; case MathModulo: return v1 % v2; break; } return 0; } public slots: void setXPos(int); void setYPos(int); void setXMag(int xs); void setYMag(int ys); void redraw(); public: View(QWidget*, int, int, const char* name = 0); void setBg(const QPixmap& pm); void setBg(const QColor& color) { brush.setColor(color); redraw(); } void setXOffset(int v) { setXPos(mapx(v)); } int xOffset() const { return mapxDev(xpos)-xorg; } int xOffsetDev() const { return xpos-rmapx(xorg); } int yOffset() const { return mapyDev(ypos)-yorg; } int getXScale() const { return xmag; } int getYScale() const { return ymag; } void setOrigin(int x, int y); void setVirt(bool flag) { _virt = flag; } bool virt() const { return _virt; } QRect rmap(const QRect&) const; int rmapxDev(int x, bool round = false) const; int rmapx(int x, bool round = false) const; //-------------------------------------------------- // Lossless intersections (in x and/or y, depending) // for any magnification. //-------------------------------------------------- // Returns true if the mapped rectangle intersects the unmapped rectangle. bool intersects(const QRect& mapped_r, const QRect& unmapped_r) const; // Returns true if the rectangles intersect. bool intersects(const ViewRect& r1, const ViewRect& r2) const; // Returns the mapped intersection of the mapped rectangle and the unmapped rectangle. QRect intersectedMap(const QRect& mapped_r, const QRect& unmapped_r) const; // Returns the unmapped intersection of the mapped rectangle and the unmapped rectangle. QRect intersectedUnmap(const QRect& mapped_r, const QRect& unmapped_r) const; // Returns the intersection of the rectangles. The coordinate x, y, and w, h are mapped // if the view's xmag > 0, and unmapped if not. The coordinate w, h are mapped if the // view's ymag > 0, and unmapped if not. ViewRect intersected(const ViewRect& r1, const ViewRect& r2) const; //-------------------------------------------- // Lossless math for any magnification. //-------------------------------------------- // Returns a ViewXCoordinate of the math operation on x1 and x2. The result is mapped if the view's xmag > 0, and unmapped if not. ViewXCoordinate mathXCoordinates(const ViewXCoordinate& x1, const ViewXCoordinate& x2, const CoordinateMathMode& mode = MathAdd) const; // Returns a ViewXCoordinate of the math operation on x1 and w1. The result is mapped if the view's xmag > 0, and unmapped if not. ViewXCoordinate mathXCoordinates(const ViewXCoordinate& x1, const ViewWCoordinate& w1, const CoordinateMathMode& mode) const; // Returns a ViewWCoordinate of the math operation on w1 and w2. The result is mapped if the view's xmag > 0, and unmapped if not. ViewWCoordinate mathXCoordinates(const ViewWCoordinate& w1, const ViewWCoordinate& w2, const CoordinateMathMode& mode) const; // Returns a ViewYCoordinate of the math operation on y1 and y2. The result is mapped if the view's ymag > 0, and unmapped if not. ViewYCoordinate mathYCoordinates(const ViewYCoordinate& y1, const ViewYCoordinate& y2, const CoordinateMathMode& mode = MathAdd) const; // Returns math operation on y1 and h1. The result is mapped if the view's ymag > 0, and unmapped if not. ViewYCoordinate mathYCoordinates(const ViewYCoordinate& y1, const ViewHCoordinate& h1, const CoordinateMathMode& mode) const; // Returns a ViewHCoordinate of the math math operation on h1 and h2. The result is mapped if the view's ymag > 0, and unmapped if not. ViewHCoordinate mathYCoordinates(const ViewHCoordinate& h1, const ViewHCoordinate& h2, const CoordinateMathMode& mode) const; // // Returns a ViewWCoordinate of the math operation on x1 and x2. The result is mapped if the view's xmag > 0, and unmapped if not. // ViewWCoordinate mathWCoordinates(const ViewXCoordinate& x1, const ViewXCoordinate& x2, // const CoordinateMathMode& mode = MathAdd) const; // // Returns a ViewHCoordinate of the math operation on y1 and y2. The result is mapped if the view's ymag > 0, and unmapped if not. // ViewHCoordinate mathHCoordinates(const ViewYCoordinate& y1, const ViewYCoordinate& y2, // const CoordinateMathMode& mode = MathAdd) const; // Performs math operation on x1. The result is mapped if the view's xmag > 0, and unmapped if not. // Returns a reference to x1. ViewXCoordinate& mathRefXCoordinates(ViewXCoordinate& x1, const ViewXCoordinate& x2, const CoordinateMathMode& mode = MathAdd) const; // Performs math operation on x1. The result is mapped if the view's xmag > 0, and unmapped if not. // Returns a reference to x1. ViewXCoordinate& mathRefXCoordinates(ViewXCoordinate& x1, const ViewWCoordinate& w1, const CoordinateMathMode& mode) const; // Performs math operation on w1. The result is mapped if the view's xmag > 0, and unmapped if not. // Returns a reference to x1. ViewWCoordinate& mathRefXCoordinates(ViewWCoordinate& w1, const ViewWCoordinate& w2, const CoordinateMathMode& mode) const; // Performs math operation on y1. The result is mapped if the view's ymag > 0, and unmapped if not. // Returns a reference to y1. ViewYCoordinate& mathRefYCoordinates(ViewYCoordinate& y1, const ViewYCoordinate& y2, const CoordinateMathMode& mode = MathAdd) const; // Performs math operation on y1. The result is mapped if the view's ymag > 0, and unmapped if not. // Returns a reference to y1. ViewYCoordinate& mathRefYCoordinates(ViewYCoordinate& y1, const ViewHCoordinate& h1, const CoordinateMathMode& mode) const; // Performs math operation on h1. The result is mapped if the view's ymag > 0, and unmapped if not. // Returns a reference to h1. ViewHCoordinate& mathRefYCoordinates(ViewHCoordinate& h1, const ViewHCoordinate& h2, const CoordinateMathMode& mode) const; // // Performs math operation on w1. The result is relative mapped if the view's xmag > 0, and relative unmapped if not. // // Returns a reference to w1. // ViewWCoordinate& mathWCoordinates(ViewWCoordinate& w1, const ViewXCoordinate& x2, // const CoordinateMathMode& mode = MathAdd) const; // // Performs math operation on h1. The result is relative mapped if the view's ymag > 0, and relative unmapped if not. // // Returns a reference to h1. // ViewHCoordinate& mathHCoordinates(ViewHCoordinate& h1, const ViewYCoordinate& y2, // const CoordinateMathMode& mode = MathAdd) const; // Adjusts the x, y, w and h of the rectangle. Returns a reference to the rectangle. ViewRect& adjustRect(ViewRect& r, const ViewWCoordinate& dx, const ViewHCoordinate& dy, const ViewWCoordinate& dw, const ViewHCoordinate& dh) const; // Returns a rectangle with the x, y, w and h adjusted. ViewRect adjustedRect(const ViewRect& r, const ViewWCoordinate& dx, const ViewHCoordinate& dy, const ViewWCoordinate& dw, const ViewHCoordinate& dh) const; //-------------------------------------------- // Lossless comparisons for any magnification. //-------------------------------------------- // Compares x1 and x2. x1 is the left operand and x2 is the right. For example x1 < x2. bool compareXCoordinates(const ViewXCoordinate& x1, const ViewXCoordinate& x2, const CoordinateCompareMode& mode = CompareEqual) const; // Compares y1 and y2. y1 is the left operand and y2 is the right. For example y1 < y2. bool compareYCoordinates(const ViewYCoordinate& y1, const ViewYCoordinate& y2, const CoordinateCompareMode& mode = CompareEqual) const; // Compares w1 and w2. w1 is the left operand and w2 is the right. For example w1 < w2. bool compareWCoordinates(const ViewWCoordinate& w1, const ViewWCoordinate& w2, const CoordinateCompareMode& mode = CompareEqual) const; // Compares h1 and h2. h1 is the left operand and h2 is the right. For example h1 < h2. bool compareHCoordinates(const ViewHCoordinate& h1, const ViewHCoordinate& h2, const CoordinateCompareMode& mode = CompareEqual) const; // Returns true if x is >= x1 and x < x2. bool isXInRange(ViewXCoordinate x, ViewXCoordinate x1, ViewXCoordinate x2) const; // Returns true if y is >= y1 and y < y2. bool isYInRange(ViewYCoordinate y, ViewYCoordinate y1, ViewYCoordinate y2) const; //-------------------------------------------- // Conversions, utilities. //-------------------------------------------- bool isViewRectEmpty(const ViewRect& r) const { return r._width._value <= 0 || r._height._value <= 0; }; // Returns a mapped version of the x coordinate. inline ViewXCoordinate asMapped(const ViewXCoordinate& x) const { return ViewXCoordinate(x.isMapped() ? x._value : mapx(x._value), true); } inline int asIntMapped(const ViewXCoordinate& x) const { return x.isMapped() ? x._value : mapx(x._value); } // Returns an unmapped version of the y coordinate. inline ViewYCoordinate asMapped(const ViewYCoordinate& y) const { return ViewYCoordinate(y.isMapped() ? y._value : mapy(y._value), true); } inline int asIntMapped(const ViewYCoordinate& y) const { return y.isMapped() ? y._value : mapy(y._value); } // Returns a relative mapped version of the w coordinate. inline ViewWCoordinate asMapped(const ViewWCoordinate& w) const { return ViewWCoordinate(w.isMapped() ? w._value : rmapx(w._value, true), true); } inline int asIntMapped(const ViewWCoordinate& w) const { return w.isMapped() ? w._value : rmapx(w._value, true); } // Returns a relative mapped version of the h coordinate. inline ViewHCoordinate asMapped(const ViewHCoordinate& h) const { return ViewHCoordinate(h.isMapped() ? h._value : rmapy(h._value, true), true); } inline int asIntMapped(const ViewHCoordinate& h) const { return h.isMapped() ? h._value : rmapy(h._value, true); } // Returns an unmapped version of the x coordinate. inline ViewXCoordinate asUnmapped(const ViewXCoordinate& x) const { return ViewXCoordinate(x.isMapped() ? mapxDev(x._value) : x._value, false); } inline int asIntUnmapped(const ViewXCoordinate& x) const { return x.isMapped() ? mapxDev(x._value) : x._value; } // Returns an unmapped version of the y coordinate. inline ViewYCoordinate asUnmapped(const ViewYCoordinate& y) const { return ViewYCoordinate(y.isMapped() ? mapyDev(y._value) : y._value, false); } inline int asIntUnmapped(const ViewYCoordinate& y) const { return y.isMapped() ? mapyDev(y._value) : y._value; } // Returns a relative unmapped version of the w coordinate. inline ViewWCoordinate asUnmapped(const ViewWCoordinate& w) const { return ViewWCoordinate(w.isMapped() ? rmapxDev(w._value, true) : w._value, false); } inline int asIntUnmapped(const ViewWCoordinate& w) const { return w.isMapped() ? rmapxDev(w._value, true) : w._value; } // Returns a relative unmapped version of the h coordinate. inline ViewHCoordinate asUnmapped(const ViewHCoordinate& h) const { return ViewHCoordinate(h.isMapped() ? rmapyDev(h._value, true) : h._value, false); } inline int asIntUnmapped(const ViewHCoordinate& h) const { return h.isMapped() ? rmapyDev(h._value, true) : h._value; } // Returns a mapped version of the rectangle. inline ViewRect asMapped(const ViewRect& r) const { return ViewRect(ViewXCoordinate(r._x.isMapped() ? r._x._value : mapx(r._x._value), true), ViewYCoordinate(r._y.isMapped() ? r._y._value : mapy(r._y._value), true), ViewWCoordinate(r._width.isMapped() ? r._width._value : rmapx(r._width._value, true), true), ViewHCoordinate(r._height.isMapped() ? r._height._value : rmapy(r._height._value, true), true)); } // Returns an unmapped version of the rectangle. inline ViewRect asUnmapped(const ViewRect& r) const { return ViewRect(ViewXCoordinate(r._x.isMapped() ? mapxDev(r._x._value) : r._x._value, false), ViewYCoordinate(r._y.isMapped() ? mapyDev(r._y._value) : r._y._value, false), ViewWCoordinate(r._width.isMapped() ? rmapxDev(r._width._value, true) : r._width._value, false), ViewHCoordinate(r._height.isMapped() ? rmapyDev(r._height._value, true) : r._height._value, false)); } // Returns a mapped QRect version of the rectangle. inline QRect asQRectMapped(const ViewRect& r) const { return QRect(r._x.isMapped() ? r._x._value : mapx(r._x._value), r._y.isMapped() ? r._y._value : mapy(r._y._value), r._width.isMapped() ? r._width._value : rmapx(r._width._value, true), r._height.isMapped() ? r._height._value : rmapy(r._height._value, true)); } // Returns an unmapped QRect version of the rectangle. inline QRect asQRectUnmapped(const ViewRect& r) const { return QRect(r._x.isMapped() ? mapxDev(r._x._value) : r._x._value, r._y.isMapped() ? mapyDev(r._y._value) : r._y._value, r._width.isMapped() ? rmapxDev(r._width._value, true) : r._width._value, r._height.isMapped() ? rmapyDev(r._height._value, true) : r._height._value); } // Returns the x + w - (mapped 1) coordinate. inline ViewXCoordinate rectRightMapped(const ViewRect& r) const { return mathXCoordinates(mathXCoordinates(r._x, r._width, MathAdd), ViewWCoordinate(1, true), MathSubtract); } // Returns the y + h - (mapped 1) coordinate. inline ViewYCoordinate rectBottomMapped(const ViewRect& r) const { return mathYCoordinates(mathYCoordinates(r._y, r._height, MathAdd), ViewHCoordinate(1, true), MathSubtract); } // Returns the x + w - (unmapped 1) coordinate. inline ViewXCoordinate rectRightUnmapped(const ViewRect& r) const { return mathXCoordinates(mathXCoordinates(r._x, r._width, MathAdd), ViewWCoordinate(1, false), MathSubtract); } // Returns the y + h - (unmapped 1) coordinate. inline ViewYCoordinate rectBottomUnmapped(const ViewRect& r) const { return mathYCoordinates(mathYCoordinates(r._y, r._height, MathAdd), ViewHCoordinate(1, false), MathSubtract); } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/visibletracks.cpp000066400000000000000000000132051404105311000217500ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tools.cpp,v 1.2 2004/04/28 21:56:13 spamatica Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Robert Jonsson (rj@spamatica.se) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "visibletracks.h" #include "icons.h" #include "track.h" #include "synth.h" #include "app.h" #include "globals.h" namespace MusEGui { static const char* midiTrack = QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show midi tracks"); static const char* waveTrack = QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show wave tracks"); static const char* outputTrack = QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show output tracks"); static const char* groupTrack = QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show group tracks"); static const char* inputTrack = QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show input tracks"); static const char* auxTrack = QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show aux tracks"); static const char* synthTrack = QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show synth tracks"); const QVector visTrackList = { {&pianorollSVGIcon, QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show midi tracks"), midiTrack }, {&waveeditorSVGIcon, QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show wave tracks"), waveTrack }, {&trackOutputSVGIcon, QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show output tracks"), outputTrack }, {&trackGroupVGIcon, QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show group tracks"), groupTrack }, {&trackInputSVGIcon, QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show input tracks"), inputTrack }, {&trackAuxSVGIcon, QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show aux tracks"), auxTrack }, {&synthSVGIcon, QT_TRANSLATE_NOOP("MusEGui::VisibleTracks", "Show synth tracks"), synthTrack }, }; //--------------------------------------------------------- // VisibleTracks //--------------------------------------------------------- VisibleTracks::VisibleTracks(QWidget* parent, const char*) : QToolBar(tr("Visible Track Types"), parent) { setObjectName("Visible track types"); actions = new QActionGroup(parent); // Parent needed. actions->setExclusive(false); int i = 0; for (const auto& t : visTrackList) { QAction* a = new QAction(tr(t.tip).toLatin1().data(), actions); a->setData(i++); a->setIcon(QIcon(**(t.icon))); a->setToolTip(tr(t.tip)); a->setWhatsThis(tr(t.ltip)); a->setStatusTip(tr(t.ltip)); a->setCheckable(true); a->setChecked(true); } actions->setVisible(true); //action->addTo(this); // Note: Does not take ownership. addActions(actions->actions()); connect(actions, SIGNAL(triggered(QAction*)), SLOT(visibilityChanged(QAction*))); } //--------------------------------------------------------- // updateVisibleTracksButtons //--------------------------------------------------------- void VisibleTracks::updateVisibleTracksButtons() { actions->actions().at(0)->setChecked(MusECore::MidiTrack::visible()); actions->actions().at(1)->setChecked(MusECore::WaveTrack::visible()); actions->actions().at(2)->setChecked(MusECore::AudioOutput::visible()); actions->actions().at(3)->setChecked(MusECore::AudioGroup::visible()); actions->actions().at(4)->setChecked(MusECore::AudioInput::visible()); actions->actions().at(5)->setChecked(MusECore::AudioAux::visible()); actions->actions().at(6)->setChecked(MusECore::SynthI::visible()); } //--------------------------------------------------------- // toolChanged //--------------------------------------------------------- void VisibleTracks::visibilityChanged(QAction* action) { // printf("update visibility\n"); switch (action->data().toInt()) { case 0: MusECore::MidiTrack::setVisible(action->isChecked()); break; case 1: MusECore::WaveTrack::setVisible(action->isChecked()); break; case 2: MusECore::AudioOutput::setVisible(action->isChecked()); break; case 3: MusECore::AudioGroup::setVisible(action->isChecked()); break; case 4: MusECore::AudioInput::setVisible(action->isChecked()); break; case 5: MusECore::AudioAux::setVisible(action->isChecked()); break; case 6: MusECore::SynthI::setVisible(action->isChecked()); break; default: break; } // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); emit visibilityChanged(); } //--------------------------------------------------------- // ~VisibleTracks //--------------------------------------------------------- VisibleTracks::~VisibleTracks() { } } // namespace MusEGui muse-4.0.0/src/muse/components/visibletracks.h000066400000000000000000000036031404105311000214160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tools.h,v 1.1.1.1 2003/10/27 18:54:49 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Robert Jonsson (rj@spamatica.se) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef VISIBLETRACKS_H #define VISIBLETRACKS_H #include class QAction; class QPixmap; class QWidget; namespace MusEGui { class Action; struct VisibleToolB { QIcon** icon; const char* tip; const char* ltip; }; extern const QVector visTrackList; //--------------------------------------------------------- // EditToolBar //--------------------------------------------------------- class VisibleTracks : public QToolBar { Q_OBJECT QActionGroup* actions; private slots: void visibilityChanged(QAction* action); public slots: void updateVisibleTracksButtons(); signals: void visibilityChanged(); public: VisibleTracks(QWidget* /*parent*/, const char* name = 0); // Needs a parent ! ~VisibleTracks(); }; } // namespace MusEGui #endif // VISIBLETRACKS_H muse-4.0.0/src/muse/components/vst_native_editor.cpp000066400000000000000000000213201404105311000226300ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // vst_native_editor.cpp // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // Some of the editor window coding was adapted from QTractor (by rncbc aka Rui Nuno Capela) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "config.h" #ifdef VST_NATIVE_SUPPORT #include "vst_native_editor.h" #include "vst_native.h" #include "gconfig.h" #include #if defined(Q_WS_X11) #include #endif #include namespace MusEGui { //--------------------------------------------------------------------- // Helpers for editor widget. //--------------------------------------------------------------------- #if defined(Q_WS_X11) static bool g_bXError = false; static int tempXErrorHandler ( Display *, XErrorEvent * ) { g_bXError = true; return 0; } static XEventProc getXEventProc ( Display *pDisplay, Window w ) { int iSize; unsigned long iBytes, iCount; unsigned char *pData; XEventProc eventProc = NULL; Atom aType, aName = XInternAtom(pDisplay, "_XEventProc", false); g_bXError = false; XErrorHandler oldErrorHandler = XSetErrorHandler(tempXErrorHandler); XGetWindowProperty(pDisplay, w, aName, 0, 1, false, AnyPropertyType, &aType, &iSize, &iCount, &iBytes, &pData); if (g_bXError == false && iCount == 1) eventProc = (XEventProc) (pData); XSetErrorHandler(oldErrorHandler); return eventProc; } static Window getXChildWindow ( Display *pDisplay, Window w ) { Window wRoot, wParent, *pwChildren; unsigned int iChildren = 0; XQueryTree(pDisplay, w, &wRoot, &wParent, &pwChildren, &iChildren); return (iChildren > 0 ? pwChildren[0] : 0); } #endif // Q_WS_X11 VstNativeEditor::VstNativeEditor(QWidget *parent, Qt::WindowFlags wflags) : QWidget(parent, wflags), #if defined(Q_WS_X11) _display(QX11Info::display()), _vstEditor(0), _vstEventProc(0), _buttonPress(false), #endif _sif(nullptr) { setAttribute(Qt::WA_DeleteOnClose); m_fixScaling = false; // TODO TEST Test if these might be helpful, especially opaque event. //setBackgroundRole(QPalette::NoRole); //setAttribute(Qt::WA_NoSystemBackground); // setAttribute(Qt::WA_StaticContents); // // This is absolutely required for speed! Otherwise painfully slow because of full background // // filling, even when requesting small udpdates! Background is drawn by us. //setAttribute(Qt::WA_OpaquePaintEvent); // //setFrameStyle(QFrame::Raised | QFrame::StyledPanel); } VstNativeEditor::~VstNativeEditor() { } //--------------------------------------------------------------------- // open //--------------------------------------------------------------------- void VstNativeEditor::open(MusECore::VstNativeSynthIF* sif, MusECore::VstNativePluginWrapper_State *state) { _sif = sif; _pstate = state; // Start the proper (child) editor... long value = 0; void *ptr = (void *) winId(); #if defined(Q_WS_X11) value = (long) _display; #endif MusECore::VstRect* pRect; AEffect *vstPlug = _sif ? _sif->_plugin : _pstate->plugin; vstPlug->dispatcher(vstPlug, effEditOpen, 0, value, ptr, 0.0f); const MusECore::PluginQuirks& quirks = _sif ? _sif->cquirks() : _pstate->pluginI->cquirks(); m_fixScaling = quirks.fixNativeUIScaling(); if(vstPlug->dispatcher(vstPlug, effEditGetRect, 0, 0, &pRect, 0.0f)) { int w = pRect->right - pRect->left; int h = pRect->bottom - pRect->top; if (w > 0 && h > 0) { if (fixScaling() && devicePixelRatio() >= 1.0) { w = qRound((qreal)w / devicePixelRatio()); h = qRound((qreal)h / devicePixelRatio()); setFixedSize(w, h); } else { QWidget::setMinimumSize(w, h); if ((w != width()) || (h != height())) setFixedSize(w, h); } } } //int rv = _sif->dispatch(effEditOpen, 0, value, ptr, 0.0f); //fprintf(stderr, "VstNativeEditor::open effEditOpen returned:%d effEditGetRect rect l:%d r:%d t:%d b:%d\n", rv, pRect->left, pRect->right, pRect->top, pRect->bottom); // REMOVE Tim. #if defined(Q_WS_X11) _vstEditor = getXChildWindow(_display, (Window) winId()); if(_vstEditor) _vstEventProc = getXEventProc(_display, _vstEditor); #endif QString windowTitle = "VST plugin editor"; if(_sif && _sif->track()) { windowTitle = _sif->track()->name() + ":" + _sif->pluginLabel(); } else if(_pstate && _pstate->pluginI->track()) { windowTitle = _pstate->pluginI->track()->name() + ":" + _pstate->pluginWrapper->_synth->name(); } setWindowTitle(windowTitle); //_sif->editorOpened(); if(!isVisible()) show(); raise(); activateWindow(); ///_sif->idleEditor(); // REMOVE Tim. Or keep. //resizeTimerId = startTimer(500); } #if defined(Q_WS_X11) //--------------------------------------------------------------------- // x11EventFilter //--------------------------------------------------------------------- bool VstNativeEditor::x11EventFilter(XEvent *pEvent) { if(_vstEventProc && pEvent->xany.window == _vstEditor) { // Avoid mouse tracking events... switch (pEvent->xany.type) { case ButtonPress: _buttonPress = true; break; case ButtonRelease: _buttonPress = false; break; case MotionNotify: if(!_buttonPress) return false; // Fall thru... default: break; } // Process as intended... (*_vstEventProc)(pEvent); return true; } else return false; } #endif //--------------------------------------------------------------------- // showEvent //--------------------------------------------------------------------- void VstNativeEditor::showEvent(QShowEvent *pShowEvent) { QWidget::showEvent(pShowEvent); if(_sif) _sif->editorOpened(); if(_pstate) _pstate->editorOpened(); } //--------------------------------------------------------------------- // closeEvent //--------------------------------------------------------------------- void VstNativeEditor::closeEvent(QCloseEvent *pCloseEvent) { /*if(resizeTimerId) { killTimer(resizeTimerId); resizeTimerId = 0; }*/ if(_sif) { _sif->dispatch(effEditClose, 0, 0, NULL, 0.0f); _sif->editorClosed(); _sif->editorDeleted(); _sif = NULL; } if(_pstate) { _pstate->plugin->dispatcher(_pstate->plugin, effEditClose, 0, 0, NULL, 0.0f); _pstate->editorClosed(); _pstate->editorDeleted(); _pstate = NULL; } QWidget::closeEvent(pCloseEvent); } //--------------------------------------------------------------------- // moveEvent //--------------------------------------------------------------------- void VstNativeEditor::moveEvent(QMoveEvent *pMoveEvent) { QWidget::moveEvent(pMoveEvent); #if defined(Q_WS_X11) if(_vstEditor) { XMoveWindow(_display, _vstEditor, 0, 0); //QWidget::update(); // REMOVE Tim. Or keep? Commented in Qtractor. } #endif } void VstNativeEditor::resizeEvent(QResizeEvent *pResizeEvent) { //setFixedSize(pResizeEvent->size()); pResizeEvent->accept(); } void VstNativeEditor::timerEvent(QTimerEvent *event) { if(event->timerId() == resizeTimerId) { MusECore::VstRect* pRect; bool bGotRect = false; if(_sif) bGotRect = _sif->dispatch(effEditGetRect, 0, 0, &pRect, 0.0f); else if(_pstate) bGotRect = _pstate->plugin->dispatcher(_pstate->plugin, effEditGetRect, 0, 0, &pRect, 0.0f); if(bGotRect) { int w = pRect->right - pRect->left; int h = pRect->bottom - pRect->top; if (w > 0 && h > 0) { if((w != width()) || (h != height())) { setFixedSize(w, h); } } } } } } // namespace MusEGui #endif // VST_NATIVE_SUPPORT muse-4.0.0/src/muse/components/vst_native_editor.h000066400000000000000000000054201404105311000223000ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // vst_native_editor.h // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // Some of the editor window coding was adapted from QTractor (by rncbc aka Rui Nuno Capela) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __VST_NATIVE_EDITOR_H__ #define __VST_NATIVE_EDITOR_H__ #include #include "config.h" #ifdef VST_NATIVE_SUPPORT #if defined(Q_WS_X11) #include #include #include #undef Bool #undef Status #undef None #undef KeyPress #undef KeyRelease #undef FocusIn #undef FocusOut #undef Type #undef FontChange #undef CursorShape #undef Unsorted typedef void (*XEventProc)(XEvent *); #endif namespace MusECore { class VstNativeSynthIF; class VstNativePluginWrapper; class VstNativePluginWrapper_State; } #endif // VST_NATIVE_SUPPORT namespace MusEGui { class VstNativeEditor : public QWidget { Q_OBJECT #ifdef VST_NATIVE_SUPPORT #if defined(Q_WS_X11) Display* _display; Window _vstEditor; XEventProc _vstEventProc; bool _buttonPress; #endif MusECore::VstNativeSynthIF* _sif; MusECore::VstNativePluginWrapper_State* _pstate; int resizeTimerId; bool m_fixScaling; protected: virtual void showEvent(QShowEvent *pShowEvent); virtual void closeEvent(QCloseEvent *pCloseEvent); virtual void moveEvent(QMoveEvent *pMoveEvent); virtual void resizeEvent(QResizeEvent *pResizeEvent); virtual void timerEvent(QTimerEvent * event); public: VstNativeEditor(QWidget *parent, Qt::WindowFlags wflags = Qt::Widget); ~VstNativeEditor(); void open(MusECore::VstNativeSynthIF* sif, MusECore::VstNativePluginWrapper_State *state); //void close(); bool fixScaling() { return m_fixScaling; } #if defined(Q_WS_X11) // Local X11 event filter. bool x11EventFilter(XEvent *pEvent); #endif //MusECore::VstNativeSynthIF* sif() const { return _sif; } #endif // VST_NATIVE_SUPPORT }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/warn_bad_timing.cpp000066400000000000000000000021541404105311000222300ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // warn_bad_timing.cpp // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "warn_bad_timing.h" namespace MusEGui { WarnBadTimingDialog::WarnBadTimingDialog() { setupUi(this); } } // namespace MusEGui muse-4.0.0/src/muse/components/warn_bad_timing.h000066400000000000000000000025571404105311000217040ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // warn_bad_timing.h // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __WARN_BAD_TIMING_H__ #define __WARN_BAD_TIMING_H__ #include "ui_warn_bad_timing.h" namespace MusEGui { class WarnBadTimingDialog : public QDialog, public Ui::warnBadTimingBase { Q_OBJECT public: WarnBadTimingDialog(); bool dontAsk() const { return dontAskAgain->isChecked(); } void setLabelText(const QString& s) { label->setText(s); } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/components/warn_bad_timing.ui000066400000000000000000000053221404105311000220630ustar00rootroot00000000000000 warnBadTimingBase 0 0 371 207 0 0 Bad timing 0 0 Message here Qt::AutoText false 0 0 Don't warn me again 0 0 Qt::Horizontal QDialogButtonBox::Ok false buttonBox accepted() warnBadTimingBase accept() 248 254 157 274 buttonBox rejected() warnBadTimingBase reject() 316 260 286 274 muse-4.0.0/src/muse/components/wtscale.cpp000066400000000000000000000244761404105311000205610ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: wtscale.cpp,v 1.3 2004/04/11 13:03:32 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "wtscale.h" #include "midieditor.h" #include "globals.h" #include "gconfig.h" #include "song.h" #include "../marker/marker.h" #include "icons.h" namespace MusEGui { //--------------------------------------------------------- // WTScale // Wave Time Scale //--------------------------------------------------------- WTScale::WTScale(int* r, QWidget* parent, int xs) : View(parent, xs, 1) { QToolTip::add(this, tr("bar scale")); barLocator = false; raster = r; pos[0] = int(MusEGlobal::song->tempomap()->tick2time(MusEGlobal::song->cpos()) * sampleRate); pos[1] = int(MusEGlobal::song->tempomap()->tick2time(MusEGlobal::song->lpos()) * sampleRate); pos[2] = int(MusEGlobal::song->tempomap()->tick2time(MusEGlobal::song->rpos()) * sampleRate); pos[3] = -1; // do not show button = Qt::NoButton; setMouseTracking(true); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(setPos(int, unsigned, bool))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::song, SIGNAL(markerChanged(int)), SLOT(redraw())); setFixedHeight(28); setBg(QColor(0xe0, 0xe0, 0xe0)); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void WTScale::songChanged(MusECore::SongChangedStruct_t /*type*/) { } //--------------------------------------------------------- // setPos //--------------------------------------------------------- void WTScale::setPos(int idx, unsigned val, bool adjustScrollbar) { val = int(MusEGlobal::song->tempomap()->tick2time(val) * sampleRate); if (val == pos[idx]) return; int opos = mapx(pos[idx] == -1 ? val : pos[idx]); pos[idx] = val; if (!isVisible()) return; val = mapx(val); int x = -9; int w = 18; if (opos > val) { w += opos - val; x += val; } else { w += val - opos; x += opos; } redraw(QRect(x, 0, w, height())); } //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- void WTScale::viewMousePressEvent(QMouseEvent* event) { button = event->button(); viewMouseMoveEvent(event); } //--------------------------------------------------------- // viewMouseReleaseEvent //--------------------------------------------------------- void WTScale::viewMouseReleaseEvent(QMouseEvent* event) { button = Qt::NoButton; } //--------------------------------------------------------- // viewMouseMoveEvent //--------------------------------------------------------- void WTScale::viewMouseMoveEvent(QMouseEvent* event) { int x= MusEGlobal::song->tempomap()->time2tick(double(event->x())/double(sampleRate)); x = MusEGlobal::song->raster(x, *raster); if (x < 0) x = 0; emit timeChanged(x); int i; switch (button) { case Qt::LeftButton: i = 0; break; case Qt::MidButton: i = 1; break; case Qt::RightButton: if ((MusEGlobal::config.rangeMarkerWithoutMMB) && (event->modifiers() & Qt::ControlModifier)) i = 1; else i = 2; break; default: return; } MusEGlobal::song->setPos(i, x); } //--------------------------------------------------------- // leaveEvent //--------------------------------------------------------- void WTScale::leaveEvent(QEvent*) { // emit timeChanged(MAXINT); } //--------------------------------------------------------- // draw //--------------------------------------------------------- void WTScale::pdraw(QPainter& p, const QRect& r, const QRegion&) { int x = r.x(); int w = r.width(); x -= 20; w += 40; // wg. Text // // draw Marker // int y = 12; p.setPen(Qt::black); p.setFont(font4); p.drawLine(r.x(), y+1, r.x() + r.width(), y+1); QRect tr(r); tr.setHeight(12); MarkerList* marker = MusEGlobal::song->marker(); for (iMarker m = marker->begin(); m != marker->end(); ++m) { int xp = mapx(int(m->second.time() * sampleRate)); if (xp > x+w) break; int xe = r.x() + r.width(); iMarker mm = m; ++mm; if (mm != marker->end()) { xe = mapx(mm->first); } QRect tr(xp, 0, xe-xp, 13); if (m->second.current()) { p.fillRect(tr, Qt::white); } if (r.intersects(tr)) { int x2; iMarker mm = m; ++mm; if (mm != marker->end()) x2 = mapx(mm->first); else x2 = xp+200; QRect r = QRect(xp+10, 0, x2-xp, 12); p.drawPixmap(xp, 0, *flagIconS); p.drawText(r, Qt::AlignLeft|Qt::AlignVCenter, m->second.name()); } } //--------------------------------------------------- // draw location marker //--------------------------------------------------- int h = height()-12; if (barLocator) { p.setPen(Qt::blue); int xp = mapx(pos[1]); if (xp >= x && xp < x+w) p.drawLine(xp, y, xp, h); xp = mapx(pos[2]); if (xp >= x && xp < x+w) p.drawLine(xp, y, xp, h); // Draw the red main position cursor last, on top of the others. p.setPen(Qt::red); xp = mapx(pos[0]); if (xp >= x && xp < x+w) p.drawLine(xp, y, xp, h); } else { for (int i = 0; i < 3; ++i) { int xp = mapx(pos[i]); if (xp >= x && xp < x+w) { QPixmap* pm = markIcon[i]; p.drawPixmap(xp - pm->width()/2, y-1, *pm); } } } p.setPen(Qt::black); if (pos[3] != -1) { int xp = mapx(pos[3]); if (xp >= x && xp < x+w) p.drawLine(xp, 0, xp, height()); } int ctick = MusEGlobal::song->samples2tick(mapxDev(x)); int bar1, bar2, beat, tick; MusEGlobal::song->tickValues(ctick, &bar1, &beat, &tick); MusEGlobal::song->tickValues(MusEGlobal::song->samples2tick(mapxDev(x+w)), &bar2, &beat, &tick); //printf("bar %d %d-%d=%d\n", bar, ntick, stick, ntick-stick); int stick = MusEGlobal::song->bar2tick(bar1, 0, 0); int ntick; for (int bar = bar1; bar <= bar2; bar++, stick = ntick) { ntick = MusEGlobal::song->bar2tick(bar+1, 0, 0); int a = MusEGlobal::song->tick2samples(ntick); int b = MusEGlobal::song->tick2samples(stick); int tpix = rmapx(a - b); if (tpix < 64) { // don�t show beats if measure is this small int n = 1; if (tpix < 32) n = 2; if (tpix <= 16) n = 4; if (tpix < 8) n = 8; if (tpix <= 4) n = 16; if (tpix <= 2) n = 32; if (bar % n) continue; p.setFont(font3); int x = mapx(b); QString s; s.setNum(bar + 1); p.drawLine(x, y+1, x, y+1+h); QRect r = QRect(x+2, y, 0, h); p.drawText(r, Qt::AlignLeft|Qt::AlignVCenter|Qt::TextDontClip, s); } else { int z, n; MusEGlobal::song->timesig(stick, z, n); for (int beat = 0; beat < z; beat++) { int xx = MusEGlobal::song->tick2samples(MusEGlobal::song->bar2tick(bar, beat, 0)); int xp = mapx(xx); QString s; QRect r(xp+2, y, 0, h); int y1; int num; if (beat == 0) { num = bar + 1; y1 = y + 1; p.setFont(font3); } else { num = beat + 1; y1 = y + 7; p.setFont(font1); r.setY(y+3); } s.setNum(num); p.drawLine(xp, y1, xp, y+1+h); p.drawText(r, Qt::AlignLeft|Qt::AlignVCenter|Qt::TextDontClip, s); } } } } } // namespace MusEGui muse-4.0.0/src/muse/components/wtscale.h000066400000000000000000000040021404105311000202050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: wtscale.h,v 1.2 2004/01/11 18:55:37 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MTSCALE_H__ #define __MTSCALE_H__ #include "type_defs.h" #include "view.h" namespace MusEGui { //--------------------------------------------------------- // WTScale // scale for wave track //--------------------------------------------------------- class WTScale : public View { Q_OBJECT int* raster; unsigned pos[4]; int button; bool barLocator; private slots: void songChanged(MusECore::SongChangedStruct_t); protected: virtual void pdraw(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void viewMousePressEvent(QMouseEvent* event); virtual void viewMouseMoveEvent(QMouseEvent* event); virtual void viewMouseReleaseEvent(QMouseEvent* event); virtual void leaveEvent(QEvent*e); signals: void timeChanged(unsigned); public slots: void setPos(int, unsigned, bool); public: WTScale(int* raster, QWidget* parent, int xscale); void setBarLocator(bool f) { barLocator = f; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/conf.cpp000066400000000000000000004241041404105311000156470ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: conf.cpp,v 1.33.2.18 2009/12/01 03:52:40 terminator356 Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include "app.h" #include "transport.h" #include "icons.h" #include "globals.h" #include "functions.h" #include "drumedit.h" #include "pianoroll.h" #include "scoreedit.h" #include "master/masteredit.h" //#include "listedit.h" #include "cliplist/cliplist.h" #include "arrangerview.h" #include "marker/markerview.h" //#include "master/lmaster.h" #include "bigtime.h" //#include "arranger.h" #include "conf.h" #include "gconfig.h" #include "pitchedit.h" #include "midiport.h" #include "mididev.h" #include "instruments/minstrument.h" #include "driver/audiodev.h" #include "driver/jackmidi.h" #include "driver/alsamidi.h" #include "waveedit.h" #include "midi_consts.h" #include "midisyncimpl.h" #include "midifilterimpl.h" #include "midictrl.h" #include "ctrlcombo.h" #include "genset.h" #include "midiitransform.h" #include "synth.h" #include "audio.h" #include "sync.h" #include "wave.h" #include "midiseq.h" #include "amixer.h" #include "track.h" #include "plugin.h" #include "audio_convert/audio_converter_settings_group.h" #include "filedialog.h" #include "al/al.h" #include "shortcuts.h" #include "synthdialog.h" namespace MusECore { extern void writeMidiTransforms(int level, Xml& xml); extern void readMidiTransform(Xml&); extern void writeMidiInputTransforms(int level, Xml& xml); extern void readMidiInputTransform(Xml&); //--------------------------------------------------------- // readController //--------------------------------------------------------- static void readController(Xml& xml, int midiPort, int channel) { int id = 0; int val = CTRL_VAL_UNKNOWN; for (;;) { Xml::Token token = xml.parse(); QString tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "val") val = xml.parseInt(); else xml.unknown("controller"); break; case Xml::Attribut: if (tag == "id") id = xml.s2().toInt(); break; case Xml::TagEnd: if (tag == "controller") { MidiPort* port = &MusEGlobal::midiPorts[midiPort]; val = port->limitValToInstrCtlRange(id, val, channel); // The value here will actually be sent to the device LATER, in MidiPort::setMidiDevice() port->setHwCtrlState(channel, id, val); return; } default: return; } } } //--------------------------------------------------------- // readPortChannel //--------------------------------------------------------- static void readPortChannel(Xml& xml, int midiPort) { int idx = 0; //torbenh for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; QString tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "controller") { readController(xml, midiPort, idx); } else xml.unknown("MidiDevice"); break; case Xml::Attribut: if (tag == "idx") idx = xml.s2().toInt(); break; case Xml::TagEnd: if (tag == "channel") return; default: break; } } } //--------------------------------------------------------- // readConfigMidiDevice //--------------------------------------------------------- static void readConfigMidiDevice(Xml& xml) { QString device; int rwFlags = 3; int openFlags = 1; int type = MidiDevice::ALSA_MIDI; for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; QString tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "name") device = xml.parse1(); else if (tag == "type") type = xml.parseInt(); else if (tag == "openFlags") openFlags = xml.parseInt(); else if (tag == "rwFlags") // Jack midi devs need this. rwFlags = xml.parseInt(); else xml.unknown("MidiDevice"); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "mididevice") { MidiDevice* dev = MusEGlobal::midiDevices.find(device, type); if(!dev) { if(type == MidiDevice::JACK_MIDI) { if(MusEGlobal::debugMsg) fprintf(stderr, "readConfigMidiDevice: creating jack midi device %s with rwFlags:%d\n", device.toLatin1().constData(), rwFlags); dev = MidiJackDevice::createJackMidiDevice(device, rwFlags); } #ifdef ALSA_SUPPORT else if(type == MidiDevice::ALSA_MIDI) { if(MusEGlobal::debugMsg) fprintf(stderr, "readConfigMidiDevice: creating ALSA midi device %s with rwFlags:%d\n", device.toLatin1().constData(), rwFlags); dev = MidiAlsaDevice::createAlsaMidiDevice(device, rwFlags); } #endif } if(MusEGlobal::debugMsg && !dev) fprintf(stderr, "readConfigMidiDevice: device not found %s\n", device.toLatin1().constData()); if (dev) { dev->setOpenFlags(openFlags); } return; } default: break; } } } //--------------------------------------------------------- // readConfigMidiPort //--------------------------------------------------------- static void readConfigMidiPort(Xml& xml, bool onlyReadChannelState) { int idx = 0; QString device; // Let's be bold. New users have been confused by generic midi not enabling any patches and controllers. // I had said this may cause HW problems by sending out GM sysEx when really the HW might not be GM. // But this really needs to be done, one way or another. // FIXME: TODO: Make this user-configurable! QString instrument("GM"); int rwFlags = 3; int openFlags = 1; int dic = -1; int doc = -1; int trackIdx = -1; MidiSyncInfo tmpSi; int type = MidiDevice::ALSA_MIDI; bool pre_mididevice_ver_found = false; for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; QString tag = xml.s1(); switch (token) { case Xml::TagStart: // onlyReadChannelState added so it doesn't overwrite midi ports. p4.0.41 Tim. // Try to keep the controller information. But, this may need to be moved below. // Also may want to try to keep sync info, but that's a bit risky, so let's not for now. if (tag == "channel") { readPortChannel(xml, idx); break; } else if (onlyReadChannelState){ xml.skip(tag); break; } if (tag == "name") device = xml.parse1(); else if (tag == "type") { pre_mididevice_ver_found = true; type = xml.parseInt(); } else if (tag == "record") { // old pre_mididevice_ver_found = true; bool f = xml.parseInt(); if (f) openFlags |= 2; } else if (tag == "openFlags") { pre_mididevice_ver_found = true; openFlags = xml.parseInt(); } else if (tag == "rwFlags") // Jack midi devs need this. { pre_mididevice_ver_found = true; rwFlags = xml.parseInt(); } else if (tag == "defaultInChans") dic = xml.parseInt(); else if (tag == "defaultOutChans") doc = xml.parseInt(); else if (tag == "midiSyncInfo") tmpSi.read(xml); else if (tag == "instrument") { instrument = xml.parse1(); //MusEGlobal::midiPorts[idx].setInstrument( // Moved below // registerMidiInstrument(instrument) // ); } else if (tag == "trackIdx") { trackIdx = xml.parseInt(); } else if (tag == "midithru") { pre_mididevice_ver_found = true; xml.parseInt(); // obsolete } //else if (tag == "channel") { // readPortChannel(xml, idx); // Moved above // } else xml.unknown("MidiDevice"); break; case Xml::Attribut: if (tag == "idx") { idx = xml.s2().toInt(); } break; case Xml::TagEnd: if (tag == "midiport") { if(onlyReadChannelState) // p4.0.41 return; if (idx < 0 || idx >= MusECore::MIDI_PORTS) { fprintf(stderr, "bad midi port %d (>%d)\n", idx, MusECore::MIDI_PORTS); idx = 0; } MidiDevice* dev = MusEGlobal::midiDevices.find(device, pre_mididevice_ver_found ? type : -1); if(!dev && type == MidiDevice::JACK_MIDI) { if(MusEGlobal::debugMsg) fprintf(stderr, "readConfigMidiPort: creating jack midi device %s with rwFlags:%d\n", device.toLatin1().constData(), rwFlags); dev = MidiJackDevice::createJackMidiDevice(device, rwFlags); } if(MusEGlobal::debugMsg && !dev) fprintf(stderr, "readConfigMidiPort: device not found %s\n", device.toLatin1().constData()); MidiPort* mp = &MusEGlobal::midiPorts[idx]; mp->setDefaultOutChannels(0); // reset output channel to take care of the case where no default is specified // Just set the generic instrument for now. mp->changeInstrument(genericMidiInstrument); // Set references to be resolved later... mp->setTmpFileRefs(trackIdx, instrument); if(dic != -1) // p4.0.17 Leave them alone unless set by song. mp->setDefaultInChannels(dic); if(doc != -1) // p4.0.17 Turn on if and when multiple output routes supported. #if 0 mp->setDefaultOutChannels(doc); #else setPortExclusiveDefOutChan(idx, doc); #endif mp->syncInfo().copyParams(tmpSi); // p3.3.50 Indicate the port was found in the song file, even if no device is assigned to it. mp->setFoundInSongFile(true); if (dev) { if(pre_mididevice_ver_found) dev->setOpenFlags(openFlags); MusEGlobal::audio->msgSetMidiDevice(mp, dev); } return; } default: break; } } } //--------------------------------------------------------- // loadConfigMetronom //--------------------------------------------------------- static void loadConfigMetronom(Xml& xml, MetronomeSettings* metro_settings) { for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; QString tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "premeasures") metro_settings->preMeasures = xml.parseInt(); else if (tag == "measurepitch") metro_settings->measureClickNote = xml.parseInt(); else if (tag == "measurevelo") metro_settings->measureClickVelo = xml.parseInt(); else if (tag == "beatpitch") metro_settings->beatClickNote = xml.parseInt(); else if (tag == "beatvelo") metro_settings->beatClickVelo = xml.parseInt(); else if (tag == "accentpitch1") metro_settings->accentClick1 = xml.parseInt(); else if (tag == "accentpitch2") metro_settings->accentClick2 = xml.parseInt(); else if (tag == "accentvelo1") metro_settings->accentClick1Velo = xml.parseInt(); else if (tag == "accentvelo2") metro_settings->accentClick2Velo = xml.parseInt(); else if (tag == "channel") metro_settings->clickChan = xml.parseInt(); else if (tag == "port") metro_settings->clickPort = xml.parseInt(); else if (tag == "precountEnable") metro_settings->precountEnableFlag = xml.parseInt(); else if (tag == "fromMastertrack") metro_settings->precountFromMastertrackFlag = xml.parseInt(); else if (tag == "signatureZ") metro_settings->precountSigZ = xml.parseInt(); else if (tag == "signatureN") metro_settings->precountSigN = xml.parseInt(); else if (tag == "precountOnPlay") metro_settings->precountOnPlay = xml.parseInt(); else if (tag == "precountMuteMetronome") metro_settings->precountMuteMetronome = xml.parseInt(); else if (tag == "prerecord") metro_settings->precountPrerecord = xml.parseInt(); else if (tag == "preroll") metro_settings->precountPreroll = xml.parseInt(); else if (tag == "midiClickEnable") metro_settings->midiClickFlag = xml.parseInt(); else if (tag == "audioClickEnable") metro_settings->audioClickFlag = xml.parseInt(); else if (tag == "audioClickVolume") metro_settings->audioClickVolume = xml.parseFloat(); else if (tag == "measClickVolume") metro_settings->measClickVolume = xml.parseFloat(); else if (tag == "beatClickVolume") metro_settings->beatClickVolume = xml.parseFloat(); else if (tag == "accent1ClickVolume") metro_settings->accent1ClickVolume = xml.parseFloat(); else if (tag == "accent2ClickVolume") metro_settings->accent2ClickVolume = xml.parseFloat(); else if (tag == "clickSamples") metro_settings->clickSamples = (MetronomeSettings::ClickSamples)xml.parseInt(); else if (tag == "beatSample") metro_settings->beatSample = xml.parse1(); else if (tag == "measSample") metro_settings->measSample = xml.parse1(); else if (tag == "accent1Sample") metro_settings->accent1Sample = xml.parse1(); else if (tag == "accent2Sample") metro_settings->accent2Sample = xml.parse1(); else if (tag == "metroUseSongSettings") MusEGlobal::metroUseSongSettings = xml.parseInt(); else if (tag == "metroAccPresets") MusEGlobal::metroAccentPresets.read(xml); else if (tag == "metroAccMap") { if(metro_settings->metroAccentsMap) metro_settings->metroAccentsMap->read(xml); } else xml.unknown("Metronome"); break; case Xml::TagEnd: if (tag == "metronom") return; default: break; } } } //--------------------------------------------------------- // readSeqConfiguration //--------------------------------------------------------- static void readSeqConfiguration(Xml& xml, MetronomeSettings* metro_settings, bool skipMidiPorts) { for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; const QString& tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "metronom") loadConfigMetronom(xml, metro_settings); else if (tag == "mididevice") readConfigMidiDevice(xml); else if (tag == "midiport") readConfigMidiPort(xml, skipMidiPorts); else if (tag == "rcStop") MusEGlobal::rcStopNote = xml.parseInt(); else if (tag == "rcEnable") MusEGlobal::rcEnable = xml.parseInt(); else if (tag == "rcRecord") MusEGlobal::rcRecordNote = xml.parseInt(); else if (tag == "rcGotoLeft") MusEGlobal::rcGotoLeftMarkNote = xml.parseInt(); else if (tag == "rcPlay") MusEGlobal::rcPlayNote = xml.parseInt(); else if (tag == "rcSteprec") MusEGlobal::rcSteprecNote = xml.parseInt(); else if (tag == "rcForward") MusEGlobal::rcForwardNote = xml.parseInt(); else if (tag == "rcRewind") MusEGlobal::rcBackwardNote = xml.parseInt(); else if (tag == "rcEnableCC") MusEGlobal::rcEnableCC = xml.parseInt(); else if (tag == "rcStopCC") MusEGlobal::rcStopCC = xml.parseInt(); else if (tag == "rcRecordCC") MusEGlobal::rcRecordCC = xml.parseInt(); else if (tag == "rcGotoLeftCC") MusEGlobal::rcGotoLeftMarkCC = xml.parseInt(); else if (tag == "rcPlayCC") MusEGlobal::rcPlayCC = xml.parseInt(); // else if (tag == "rcInsertRest") // MusEGlobal::rcInsertPauseCC = xml.parseInt(); else if (tag == "rcForwardCC") MusEGlobal::rcForwardCC = xml.parseInt(); else if (tag == "rcRewindCC") MusEGlobal::rcBackwardCC = xml.parseInt(); else xml.unknown("Seq"); break; case Xml::TagEnd: if (tag == "sequencer") { return; } default: break; } } } //--------------------------------------------------------- // readConfiguration //--------------------------------------------------------- void readConfiguration(Xml& xml, bool doReadMidiPortConfig, bool doReadGlobalConfig) { if (doReadGlobalConfig) doReadMidiPortConfig=true; int mixers = 0; for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; QString tag = xml.s1(); switch (token) { case Xml::TagStart: /* the reading of configuration is split in two; read "sequencer" and read ALL. The reason is that it is possible to load a song without configuration. In this case the chapter in the song file should be skipped. However the sub part contains elements that are necessary to preserve composition consistency. Mainly midiport configuration and VOLUME. */ if (tag == "sequencer") { readSeqConfiguration(xml, doReadGlobalConfig ? &MusEGlobal::metroGlobalSettings : &MusEGlobal::metroSongSettings, !doReadMidiPortConfig); break; } else if (tag == "waveTracksVisible") WaveTrack::setVisible((bool)xml.parseInt()); else if (tag == "auxTracksVisible") AudioAux::setVisible((bool)xml.parseInt()); else if (tag == "groupTracksVisible") AudioGroup::setVisible((bool)xml.parseInt()); else if (tag == "midiTracksVisible") MidiTrack::setVisible((bool)xml.parseInt()); else if (tag == "inputTracksVisible") AudioInput::setVisible((bool)xml.parseInt()); else if (tag == "outputTracksVisible") AudioOutput::setVisible((bool)xml.parseInt()); else if (tag == "synthTracksVisible") SynthI::setVisible((bool)xml.parseInt()); else if (tag == "bigtimeVisible") MusEGlobal::config.bigTimeVisible = xml.parseInt(); else if (tag == "transportVisible") MusEGlobal::config.transportVisible = xml.parseInt(); else if (tag == "mixer1Visible") MusEGlobal::config.mixer1Visible = xml.parseInt(); else if (tag == "mixer2Visible") MusEGlobal::config.mixer2Visible = xml.parseInt(); else if (tag == "markerVisible") // I thought this was obsolete (done by song's toplevel list), but // it's obviously needed. (flo) MusEGlobal::config.markerVisible = xml.parseInt(); // else if (tag == "arrangerVisible") // // same here. // MusEGlobal::config.arrangerVisible = xml.parseInt(); else if (tag == "geometryTransport") MusEGlobal::config.geometryTransport = readGeometry(xml, tag); else if (tag == "geometryBigTime") MusEGlobal::config.geometryBigTime = readGeometry(xml, tag); else if (tag == "Mixer") { if(mixers == 0) MusEGlobal::config.mixer1.read(xml); else MusEGlobal::config.mixer2.read(xml); ++mixers; } else if (tag == "geometryMain") MusEGlobal::config.geometryMain = readGeometry(xml, tag); else if (tag == "trackHeight") MusEGlobal::config.trackHeight = xml.parseInt(); else if (doReadMidiPortConfig==false) { xml.skip(tag); break; } else if (tag == "midiInputDevice") MusEGlobal::midiInputPorts = xml.parseInt(); else if (tag == "midiInputChannel") MusEGlobal::midiInputChannel = xml.parseInt(); else if (tag == "midiRecordType") MusEGlobal::midiRecordType = xml.parseInt(); else if (tag == "midiThruType") MusEGlobal::midiThruType = xml.parseInt(); else if (tag == "midiFilterCtrl1") MusEGlobal::midiFilterCtrl1 = xml.parseInt(); else if (tag == "midiFilterCtrl2") MusEGlobal::midiFilterCtrl2 = xml.parseInt(); else if (tag == "midiFilterCtrl3") MusEGlobal::midiFilterCtrl3 = xml.parseInt(); else if (tag == "midiFilterCtrl4") MusEGlobal::midiFilterCtrl4 = xml.parseInt(); else if (tag == "mtctype") { MusEGlobal::mtcType= xml.parseInt(); // Make sure the AL namespace variables mirror our variables. AL::mtcType = MusEGlobal::mtcType; } else if (tag == "sendClockDelay") MusEGlobal::syncSendFirstClockDelay = xml.parseUInt(); else if (tag == "extSync") MusEGlobal::extSyncFlag = xml.parseInt(); else if (tag == "useJackTransport") MusEGlobal::config.useJackTransport = xml.parseInt(); else if (tag == "timebaseMaster") { MusEGlobal::config.timebaseMaster = xml.parseInt(); // Set this one-time flag to true so that when setMaster is called, // it forces master. audioDevice may be NULL, esp. at startup, // so this flag is necessary for the next valid call to setMaster. MusEGlobal::timebaseMasterForceFlag = true; if(MusEGlobal::audioDevice) // Force it. MusEGlobal::audioDevice->setMaster(MusEGlobal::config.timebaseMaster, true); } else if (tag == "syncRecFilterPreset") { int p = xml.parseInt(); if(p >= 0 && p < MidiSyncInfo::TYPE_END) { MusEGlobal::syncRecFilterPreset = MidiSyncInfo::SyncRecFilterPresetType(p); MusEGlobal::midiSyncContainer.setSyncRecFilterPreset(MusEGlobal::syncRecFilterPreset); } } else if (tag == "syncRecTempoValQuant") { double qv = xml.parseDouble(); MusEGlobal::syncRecTempoValQuant = qv; MusEGlobal::midiSyncContainer.setRecTempoValQuant(qv); } else if (tag == "mtcoffset") { QString qs(xml.parse1()); QByteArray ba = qs.toLatin1(); const char* str = ba.constData(); int h, m, s, f, sf; sscanf(str, "%d:%d:%d:%d:%d", &h, &m, &s, &f, &sf); MusEGlobal::mtcOffset = MTC(h, m, s, f, sf); } else if (tag == "midiTransform") readMidiTransform(xml); else if (tag == "midiInputTransform") readMidiInputTransform(xml); // don't insert else if(...) clauses between // this line and "Global config stuff begins here". else if (!doReadGlobalConfig) { xml.skip(tag); break; } // ---- Global and/or per-song config stuff ends here ---- // ---- Global config stuff begins here ---- else if (tag == "pluginLadspaPathList") // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginLadspaPathList = xml.parse1().split(":", Qt::SkipEmptyParts); #else MusEGlobal::config.pluginLadspaPathList = xml.parse1().split(":", QString::SkipEmptyParts); #endif else if (tag == "pluginDssiPathList") #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginDssiPathList = xml.parse1().split(":", Qt::SkipEmptyParts); #else MusEGlobal::config.pluginDssiPathList = xml.parse1().split(":", QString::SkipEmptyParts); #endif // Obsolete. Replaced with one below. else if (tag == "pluginVstPathList") xml.parse1(); else if (tag == "pluginVstsPathList") #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginVstPathList = xml.parse1().split(":", Qt::SkipEmptyParts); #else MusEGlobal::config.pluginVstPathList = xml.parse1().split(":", QString::SkipEmptyParts); #endif // Obsolete. Replaced with one below. else if (tag == "pluginLinuxVstPathList") xml.parse1(); else if (tag == "pluginLinuxVstsPathList") #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginLinuxVstPathList = xml.parse1().split(":", Qt::SkipEmptyParts); #else MusEGlobal::config.pluginLinuxVstPathList = xml.parse1().split(":", QString::SkipEmptyParts); #endif else if (tag == "pluginLv2PathList") #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginLv2PathList = xml.parse1().split(":", Qt::SkipEmptyParts); #else MusEGlobal::config.pluginLv2PathList = xml.parse1().split(":", QString::SkipEmptyParts); #endif else if (tag == "pluginCacheTriggerRescan") MusEGlobal::config.pluginCacheTriggerRescan = xml.parseInt(); else if (tag == "audioConverterSettingsGroup") { if(MusEGlobal::defaultAudioConverterSettings) MusEGlobal::defaultAudioConverterSettings->read(xml, &MusEGlobal::audioConverterPluginList); } else if (tag == "preferredRouteNameOrAlias") MusEGlobal::config.preferredRouteNameOrAlias = static_cast(xml.parseInt()); else if (tag == "routerExpandVertically") MusEGlobal::config.routerExpandVertically = xml.parseInt(); else if (tag == "routerGroupingChannels") { MusEGlobal::config.routerGroupingChannels = xml.parseInt(); // TODO: For now we only support maximum two channels grouping. Zero is an error. if(MusEGlobal::config.routerGroupingChannels < 1) MusEGlobal::config.routerGroupingChannels = 1; if(MusEGlobal::config.routerGroupingChannels > 2) MusEGlobal::config.routerGroupingChannels = 2; } // else if (tag == "qtStyle") // MusEGlobal::config.style = xml.parse1(); else if (tag == "autoSave") MusEGlobal::config.autoSave = xml.parseInt(); else if (tag == "scrollableSubMenus") MusEGlobal::config.scrollableSubMenus = xml.parseInt(); else if (tag == "liveWaveUpdate") MusEGlobal::config.liveWaveUpdate = xml.parseInt(); else if (tag == "audioEffectsRackVisibleItems") MusEGlobal::config.audioEffectsRackVisibleItems = xml.parseInt(); else if (tag == "preferKnobsVsSliders") MusEGlobal::config.preferKnobsVsSliders = xml.parseInt(); else if (tag == "showControlValues") MusEGlobal::config.showControlValues = xml.parseInt(); else if (tag == "monitorOnRecord") MusEGlobal::config.monitorOnRecord = xml.parseInt(); else if (tag == "lineEditStyleHack") MusEGlobal::config.lineEditStyleHack = xml.parseInt(); else if (tag == "preferMidiVolumeDb") MusEGlobal::config.preferMidiVolumeDb = xml.parseInt(); else if (tag == "midiCtrlGraphMergeErase") MusEGlobal::config.midiCtrlGraphMergeErase = xml.parseInt(); else if (tag == "midiCtrlGraphMergeEraseInclusive") MusEGlobal::config.midiCtrlGraphMergeEraseInclusive = xml.parseInt(); else if (tag == "midiCtrlGraphMergeEraseWysiwyg") MusEGlobal::config.midiCtrlGraphMergeEraseWysiwyg = xml.parseInt(); else if (tag == "museTheme") MusEGlobal::config.theme = xml.parse1(); else if (tag == "useOldStyleStopShortCut") MusEGlobal::config.useOldStyleStopShortCut = xml.parseInt(); else if (tag == "useRewindOnStop") MusEGlobal::config.useRewindOnStop = xml.parseInt(); else if (tag == "moveArmedCheckBox") MusEGlobal::config.moveArmedCheckBox = xml.parseInt(); else if (tag == "externalWavEditor") MusEGlobal::config.externalWavEditor = xml.parse1(); // else if (tag == "font0") // MusEGlobal::config.fonts[0].fromString(xml.parse1()); else if (tag == "font1") MusEGlobal::config.fonts[1].fromString(xml.parse1()); else if (tag == "font2") MusEGlobal::config.fonts[2].fromString(xml.parse1()); else if (tag == "font3") MusEGlobal::config.fonts[3].fromString(xml.parse1()); else if (tag == "font4") MusEGlobal::config.fonts[4].fromString(xml.parse1()); else if (tag == "font5") MusEGlobal::config.fonts[5].fromString(xml.parse1()); else if (tag == "font6") MusEGlobal::config.fonts[6].fromString(xml.parse1()); else if (tag == "autoAdjustFontSize") MusEGlobal::config.autoAdjustFontSize = xml.parseInt(); else if (tag == "globalAlphaBlend") MusEGlobal::config.globalAlphaBlend = xml.parseInt(); else if (tag == "palette0") MusEGlobal::config.palette[0] = readColor(xml); else if (tag == "palette1") MusEGlobal::config.palette[1] = readColor(xml); else if (tag == "palette2") MusEGlobal::config.palette[2] = readColor(xml); else if (tag == "palette3") MusEGlobal::config.palette[3] = readColor(xml); else if (tag == "palette4") MusEGlobal::config.palette[4] = readColor(xml); else if (tag == "palette5") MusEGlobal::config.palette[5] = readColor(xml); else if (tag == "palette6") MusEGlobal::config.palette[6] = readColor(xml); else if (tag == "palette7") MusEGlobal::config.palette[7] = readColor(xml); else if (tag == "palette8") MusEGlobal::config.palette[8] = readColor(xml); else if (tag == "palette9") MusEGlobal::config.palette[9] = readColor(xml); else if (tag == "palette10") MusEGlobal::config.palette[10] = readColor(xml); else if (tag == "palette11") MusEGlobal::config.palette[11] = readColor(xml); else if (tag == "palette12") MusEGlobal::config.palette[12] = readColor(xml); else if (tag == "palette13") MusEGlobal::config.palette[13] = readColor(xml); else if (tag == "palette14") MusEGlobal::config.palette[14] = readColor(xml); else if (tag == "palette15") MusEGlobal::config.palette[15] = readColor(xml); else if (tag == "partColor0") MusEGlobal::config.partColors[0] = readColor(xml); else if (tag == "partColor1") MusEGlobal::config.partColors[1] = readColor(xml); else if (tag == "partColor2") MusEGlobal::config.partColors[2] = readColor(xml); else if (tag == "partColor3") MusEGlobal::config.partColors[3] = readColor(xml); else if (tag == "partColor4") MusEGlobal::config.partColors[4] = readColor(xml); else if (tag == "partColor5") MusEGlobal::config.partColors[5] = readColor(xml); else if (tag == "partColor6") MusEGlobal::config.partColors[6] = readColor(xml); else if (tag == "partColor7") MusEGlobal::config.partColors[7] = readColor(xml); else if (tag == "partColor8") MusEGlobal::config.partColors[8] = readColor(xml); else if (tag == "partColor9") MusEGlobal::config.partColors[9] = readColor(xml); else if (tag == "partColor10") MusEGlobal::config.partColors[10] = readColor(xml); else if (tag == "partColor11") MusEGlobal::config.partColors[11] = readColor(xml); else if (tag == "partColor12") MusEGlobal::config.partColors[12] = readColor(xml); else if (tag == "partColor13") MusEGlobal::config.partColors[13] = readColor(xml); else if (tag == "partColor14") MusEGlobal::config.partColors[14] = readColor(xml); else if (tag == "partColor15") MusEGlobal::config.partColors[15] = readColor(xml); else if (tag == "partColor16") MusEGlobal::config.partColors[16] = readColor(xml); else if (tag == "partColor17") MusEGlobal::config.partColors[17] = readColor(xml); else if (tag == "partColorName0") MusEGlobal::config.partColorNames[0] = xml.parse1(); else if (tag == "partColorName1") MusEGlobal::config.partColorNames[1] = xml.parse1(); else if (tag == "partColorName2") MusEGlobal::config.partColorNames[2] = xml.parse1(); else if (tag == "partColorName3") MusEGlobal::config.partColorNames[3] = xml.parse1(); else if (tag == "partColorName4") MusEGlobal::config.partColorNames[4] = xml.parse1(); else if (tag == "partColorName5") MusEGlobal::config.partColorNames[5] = xml.parse1(); else if (tag == "partColorName6") MusEGlobal::config.partColorNames[6] = xml.parse1(); else if (tag == "partColorName7") MusEGlobal::config.partColorNames[7] = xml.parse1(); else if (tag == "partColorName8") MusEGlobal::config.partColorNames[8] = xml.parse1(); else if (tag == "partColorName9") MusEGlobal::config.partColorNames[9] = xml.parse1(); else if (tag == "partColorName10") MusEGlobal::config.partColorNames[10] = xml.parse1(); else if (tag == "partColorName11") MusEGlobal::config.partColorNames[11] = xml.parse1(); else if (tag == "partColorName12") MusEGlobal::config.partColorNames[12] = xml.parse1(); else if (tag == "partColorName13") MusEGlobal::config.partColorNames[13] = xml.parse1(); else if (tag == "partColorName14") MusEGlobal::config.partColorNames[14] = xml.parse1(); else if (tag == "partColorName15") MusEGlobal::config.partColorNames[15] = xml.parse1(); else if (tag == "partColorName16") MusEGlobal::config.partColorNames[16] = xml.parse1(); else if (tag == "partColorName17") MusEGlobal::config.partColorNames[17] = xml.parse1(); else if (tag == "partCanvasBg") MusEGlobal::config.partCanvasBg = readColor(xml); else if (tag == "dummyPartColor") MusEGlobal::config.dummyPartColor = readColor(xml); else if (tag == "partCanvasFineRaster") MusEGlobal::config.partCanvasFineRasterColor = readColor(xml); else if (tag == "partCanvasBeatRaster") MusEGlobal::config.partCanvasBeatRasterColor = readColor(xml); else if (tag == "partCanvasCoarseRaster") MusEGlobal::config.partCanvasCoarseRasterColor = readColor(xml); else if (tag == "trackBg") MusEGlobal::config.trackBg = readColor(xml); else if (tag == "selectTrackBg") MusEGlobal::config.selectTrackBg = readColor(xml); else if (tag == "selectTrackFg") MusEGlobal::config.selectTrackFg = readColor(xml); else if (tag == "selectTrackCurBg") MusEGlobal::config.selectTrackCurBg = readColor(xml); else if (tag == "trackSectionDividerColor") MusEGlobal::config.trackSectionDividerColor = readColor(xml); // else if (tag == "mixerBg") // MusEGlobal::config.mixerBg = readColor(xml); else if (tag == "midiTrackLabelBg") MusEGlobal::config.midiTrackLabelBg = readColor(xml); // Obsolete. There is only 'New' drum tracks now. else if (tag == "drumTrackLabelBg2") /*MusEGlobal::config.drumTrackLabelBg =*/ readColor(xml); else if (tag == "newDrumTrackLabelBg2") MusEGlobal::config.newDrumTrackLabelBg = readColor(xml); else if (tag == "waveTrackLabelBg") MusEGlobal::config.waveTrackLabelBg = readColor(xml); else if (tag == "outputTrackLabelBg") MusEGlobal::config.outputTrackLabelBg = readColor(xml); else if (tag == "inputTrackLabelBg") MusEGlobal::config.inputTrackLabelBg = readColor(xml); else if (tag == "groupTrackLabelBg") MusEGlobal::config.groupTrackLabelBg = readColor(xml); else if (tag == "auxTrackLabelBg2") MusEGlobal::config.auxTrackLabelBg = readColor(xml); else if (tag == "synthTrackLabelBg") MusEGlobal::config.synthTrackLabelBg = readColor(xml); else if (tag == "midiTrackBg") MusEGlobal::config.midiTrackBg = readColor(xml); else if (tag == "ctrlGraphFg") MusEGlobal::config.ctrlGraphFg = readColor(xml); else if (tag == "ctrlGraphSel") MusEGlobal::config.ctrlGraphSel = readColor(xml); else if (tag == "drumTrackBg") MusEGlobal::config.drumTrackBg = readColor(xml); else if (tag == "newDrumTrackBg") MusEGlobal::config.newDrumTrackBg = readColor(xml); else if (tag == "waveTrackBg") MusEGlobal::config.waveTrackBg = readColor(xml); else if (tag == "outputTrackBg") MusEGlobal::config.outputTrackBg = readColor(xml); else if (tag == "inputTrackBg") MusEGlobal::config.inputTrackBg = readColor(xml); else if (tag == "groupTrackBg") MusEGlobal::config.groupTrackBg = readColor(xml); else if (tag == "auxTrackBg") MusEGlobal::config.auxTrackBg = readColor(xml); else if (tag == "synthTrackBg") MusEGlobal::config.synthTrackBg = readColor(xml); else if (tag == "sliderBarDefaultColor") MusEGlobal::config.sliderBarColor = readColor(xml); else if (tag == "sliderDefaultColor2") MusEGlobal::config.sliderBackgroundColor = readColor(xml); else if (tag == "panSliderColor2") MusEGlobal::config.panSliderColor = readColor(xml); else if (tag == "gainSliderColor2") MusEGlobal::config.gainSliderColor = readColor(xml); else if (tag == "auxSliderColor2") MusEGlobal::config.auxSliderColor = readColor(xml); else if (tag == "audioVolumeSliderColor2") MusEGlobal::config.audioVolumeSliderColor = readColor(xml); else if (tag == "midiVolumeSliderColor2") MusEGlobal::config.midiVolumeSliderColor = readColor(xml); else if (tag == "audioVolumeHandleColor") MusEGlobal::config.audioVolumeHandleColor = readColor(xml); else if (tag == "midiVolumeHandleColor") MusEGlobal::config.midiVolumeHandleColor = readColor(xml); else if (tag == "audioControllerSliderDefaultColor2") MusEGlobal::config.audioControllerSliderColor = readColor(xml); else if (tag == "audioPropertySliderDefaultColor2") MusEGlobal::config.audioPropertySliderColor = readColor(xml); else if (tag == "midiControllerSliderDefaultColor2") MusEGlobal::config.midiControllerSliderColor = readColor(xml); else if (tag == "midiPropertySliderDefaultColor2") MusEGlobal::config.midiPropertySliderColor = readColor(xml); else if (tag == "midiPatchReadoutColor") MusEGlobal::config.midiPatchReadoutColor = readColor(xml); else if (tag == "knobFontColor") MusEGlobal::config.knobFontColor = readColor(xml); else if (tag == "audioMeterPrimaryColor") MusEGlobal::config.audioMeterPrimaryColor = readColor(xml); else if (tag == "midiMeterPrimaryColor") MusEGlobal::config.midiMeterPrimaryColor = readColor(xml); else if (tag == "meterBackgroundColor") MusEGlobal::config.meterBackgroundColor = readColor(xml); else if (tag == "rackItemBackgroundColor") MusEGlobal::config.rackItemBackgroundColor = readColor(xml); else if (tag == "rackItemBgActiveColor") MusEGlobal::config.rackItemBgActiveColor = readColor(xml); else if (tag == "rackItemFontColor") MusEGlobal::config.rackItemFontColor = readColor(xml); else if (tag == "rackItemFontActiveColor") MusEGlobal::config.rackItemFontActiveColor = readColor(xml); else if (tag == "rackItemBorderColor") MusEGlobal::config.rackItemBorderColor = readColor(xml); else if (tag == "rackItemFontColorHover") MusEGlobal::config.rackItemFontColorHover = readColor(xml); else if (tag == "midiInstrumentBackgroundColor") MusEGlobal::config.midiInstrumentBackgroundColor = readColor(xml); else if (tag == "midiInstrumentBgActiveColor") MusEGlobal::config.midiInstrumentBgActiveColor = readColor(xml); else if (tag == "midiInstrumentFontColor") MusEGlobal::config.midiInstrumentFontColor = readColor(xml); else if (tag == "midiInstrumentFontActiveColor") MusEGlobal::config.midiInstrumentFontActiveColor = readColor(xml); else if (tag == "midiInstrumentBorderColor") MusEGlobal::config.midiInstrumentBorderColor = readColor(xml); else if (tag == "extendedMidi") MusEGlobal::config.extendedMidi = xml.parseInt(); else if (tag == "midiExportDivision") MusEGlobal::config.midiDivision = xml.parseInt(); else if (tag == "copyright") MusEGlobal::config.copyright = xml.parse1(); else if (tag == "smfFormat") MusEGlobal::config.smfFormat = xml.parseInt(); else if (tag == "exp2ByteTimeSigs") MusEGlobal::config.exp2ByteTimeSigs = xml.parseInt(); else if (tag == "expOptimNoteOffs") MusEGlobal::config.expOptimNoteOffs = xml.parseInt(); else if (tag == "expRunningStatus") MusEGlobal::config.expRunningStatus = xml.parseInt(); else if (tag == "importMidiSplitParts") MusEGlobal::config.importMidiSplitParts = xml.parseInt(); else if (tag == "importDevNameMetas") MusEGlobal::config.importDevNameMetas = xml.parseInt(); else if (tag == "useLastEditedEvent") MusEGlobal::config.useLastEditedEvent = xml.parseInt(); else if (tag == "importInstrNameMetas") MusEGlobal::config.importInstrNameMetas = xml.parseInt(); else if (tag == "exportPortsDevices") MusEGlobal::config.exportPortsDevices = xml.parseInt(); else if (tag == "exportPortDeviceSMF0") MusEGlobal::config.exportPortDeviceSMF0 = xml.parseInt(); else if (tag == "exportDrumMapOverrides") MusEGlobal::config.exportDrumMapOverrides = xml.parseInt(); else if (tag == "exportChannelOverridesToNewTrack") MusEGlobal::config.exportChannelOverridesToNewTrack = xml.parseInt(); else if (tag == "exportModeInstr") MusEGlobal::config.exportModeInstr = xml.parseInt(); else if (tag == "importMidiDefaultInstr") MusEGlobal::config.importMidiDefaultInstr = xml.parse1(); else if (tag == "showSplashScreen") MusEGlobal::config.showSplashScreen = xml.parseInt(); else if (tag == "canvasShowPartType") MusEGlobal::config.canvasShowPartType = xml.parseInt(); else if (tag == "canvasShowPartEvent") MusEGlobal::config.canvasShowPartEvent = xml.parseInt(); else if (tag == "canvasShowGrid") MusEGlobal::config.canvasShowGrid = xml.parseInt(); else if (tag == "canvasShowGridHorizontalAlways") MusEGlobal::config.canvasShowGridHorizontalAlways = xml.parseInt(); else if (tag == "canvasShowGridBeatsAlways") MusEGlobal::config.canvasShowGridBeatsAlways = xml.parseInt(); else if (tag == "useTrackColorForParts") MusEGlobal::config.useTrackColorForParts = xml.parseInt(); else if (tag == "canvasBgPixmap") MusEGlobal::config.canvasBgPixmap = xml.parse1(); else if (tag == "canvasCustomBgList") #if QT_VERSION >= 0x050e00 MusEGlobal::config.canvasCustomBgList = xml.parse1().split(";", Qt::SkipEmptyParts); #else MusEGlobal::config.canvasCustomBgList = xml.parse1().split(";", QString::SkipEmptyParts); #endif else if (tag == "bigtimeForegroundcolor") MusEGlobal::config.bigTimeForegroundColor = readColor(xml); else if (tag == "bigtimeBackgroundcolor") MusEGlobal::config.bigTimeBackgroundColor = readColor(xml); else if (tag == "transportHandleColor") MusEGlobal::config.transportHandleColor = readColor(xml); else if (tag == "waveEditBackgroundColor") MusEGlobal::config.waveEditBackgroundColor = readColor(xml); else if (tag == "rulerBackgroundColor") MusEGlobal::config.rulerBg = readColor(xml); else if (tag == "rulerForegroundColor") MusEGlobal::config.rulerFg = readColor(xml); else if (tag == "rulerCurrentColor") MusEGlobal::config.rulerCurrent = readColor(xml); else if (tag == "waveNonselectedPart") MusEGlobal::config.waveNonselectedPart = readColor(xml); else if (tag == "wavePeakColor") MusEGlobal::config.wavePeakColor = readColor(xml); else if (tag == "waveRmsColor") MusEGlobal::config.waveRmsColor = readColor(xml); else if (tag == "wavePeakColorSelected") MusEGlobal::config.wavePeakColorSelected = readColor(xml); else if (tag == "waveRmsColorSelected") MusEGlobal::config.waveRmsColorSelected = readColor(xml); else if (tag == "partWaveColorPeak") MusEGlobal::config.partWaveColorPeak = readColor(xml); else if (tag == "partWaveColorRms") MusEGlobal::config.partWaveColorRms = readColor(xml); else if (tag == "partMidiDarkEventColor") MusEGlobal::config.partMidiDarkEventColor = readColor(xml); else if (tag == "partMidiLightEventColor") MusEGlobal::config.partMidiLightEventColor = readColor(xml); else if (tag == "midiCanvasBackgroundColor") MusEGlobal::config.midiCanvasBg = readColor(xml); else if (tag == "midiCanvasFineColor") MusEGlobal::config.midiCanvasFineColor = readColor(xml); else if (tag == "midiCanvasBeatColor") MusEGlobal::config.midiCanvasBeatColor = readColor(xml); else if (tag == "midiCanvasBarColor") MusEGlobal::config.midiCanvasBarColor = readColor(xml); else if (tag == "midiItemColor") MusEGlobal::config.midiItemColor = readColor(xml); else if (tag == "midiItemSelectedColor") MusEGlobal::config.midiItemSelectedColor = readColor(xml); else if (tag == "midiDividerColor") MusEGlobal::config.midiDividerColor = readColor(xml); else if (tag == "midiControllerViewBackgroundColor") MusEGlobal::config.midiControllerViewBg = readColor(xml); else if (tag == "drumListBackgroundColor") MusEGlobal::config.drumListBg = readColor(xml); else if (tag == "drumListFont") MusEGlobal::config.drumListFont = readColor(xml); else if (tag == "drumListSel") MusEGlobal::config.drumListSel = readColor(xml); else if (tag == "drumListSelFont") MusEGlobal::config.drumListSelFont = readColor(xml); else if (tag == "pianoCurrentKey") MusEGlobal::config.pianoCurrentKey = readColor(xml); else if (tag == "pianoPressedKey") MusEGlobal::config.pianoPressedKey = readColor(xml); else if (tag == "pianoSelectedKey") MusEGlobal::config.pianoSelectedKey = readColor(xml); else if (tag == "markerColor") MusEGlobal::config.markerColor = readColor(xml); else if (tag == "rangeMarkerColor") MusEGlobal::config.rangeMarkerColor = readColor(xml); else if (tag == "positionMarkerColor") MusEGlobal::config.positionMarkerColor = readColor(xml); else if (tag == "currentPositionColor") MusEGlobal::config.currentPositionColor = readColor(xml); else if (tag == "maxAliasedPointSize") MusEGlobal::config.maxAliasedPointSize = xml.parseInt(); else if (tag == "iconSize") MusEGlobal::config.iconSize = xml.parseInt(); else if (tag == "cursorSize") MusEGlobal::config.cursorSize = xml.parseInt(); else if (tag == "trackGradientStrength") MusEGlobal::config.trackGradientStrength = xml.parseInt(); else if (tag == "partGradientStrength") MusEGlobal::config.partGradientStrength = xml.parseInt(); else if (tag == "cascadeStylesheets") MusEGlobal::config.cascadeStylesheets = xml.parseInt(); else if (tag == "showIconsInMenus") MusEGlobal::config.showIconsInMenus = xml.parseInt(); else if (tag == "useNativeStandardDialogs") MusEGlobal::config.useNativeStandardDialogs = xml.parseInt(); //else if (tag == "midiSyncInfo") // readConfigMidiSyncInfo(xml); /* Obsolete. done by song's toplevel list. arrangerview also handles arranger. else if (tag == "arranger") { if (MusEGlobal::muse && MusEGlobal::muse->arranger()) MusEGlobal::muse->arranger()->readStatus(xml); else xml.skip(tag); } */ else if (tag == "drumedit") MusEGui::DrumEdit::readConfiguration(xml); else if (tag == "pianoroll") MusEGui::PianoRoll::readConfiguration(xml); else if (tag == "scoreedit") MusEGui::ScoreEdit::read_configuration(xml); else if (tag == "masteredit") MusEGui::MasterEdit::readConfiguration(xml); else if (tag == "waveedit") MusEGui::WaveEdit::readConfiguration(xml); // else if (tag == "listedit") // MusEGui::ListEdit::readConfiguration(xml); // else if (tag == "lmaster") // MusEGui::LMaster::readConfiguration(xml); else if (tag == "arrangerview") MusEGui::ArrangerView::readConfiguration(xml); else if (tag == "dialogs") MusEGui::read_function_dialog_config(xml); else if (tag == "shortcuts") MusEGui::readShortCuts(xml); else if (tag == "enableAlsaMidiDriver") MusEGlobal::config.enableAlsaMidiDriver = xml.parseInt(); else if (tag == "division") { MusEGlobal::config.division = xml.parseInt(); // Make sure the AL namespace variable mirrors our variable. AL::division = MusEGlobal::config.division; } else if (tag == "guiDivision") // Obsolete. Was never used. xml.parseInt(); else if (tag == "rtcTicks") MusEGlobal::config.rtcTicks = xml.parseInt(); else if (tag == "curMidiSyncInPort") MusEGlobal::config.curMidiSyncInPort = xml.parseInt(); else if (tag == "midiSendInit") MusEGlobal::config.midiSendInit = xml.parseInt(); else if (tag == "warnInitPending") MusEGlobal::config.warnInitPending = xml.parseInt(); else if (tag == "midiSendCtlDefaults") MusEGlobal::config.midiSendCtlDefaults = xml.parseInt(); else if (tag == "midiSendNullParameters") MusEGlobal::config.midiSendNullParameters = xml.parseInt(); else if (tag == "midiOptimizeControllers") MusEGlobal::config.midiOptimizeControllers = xml.parseInt(); else if (tag == "warnIfBadTiming") MusEGlobal::config.warnIfBadTiming = xml.parseInt(); else if (tag == "warnOnFileVersions") MusEGlobal::config.warnOnFileVersions = xml.parseInt(); else if (tag == "lv2UiBehavior") MusEGlobal::config.lv2UiBehavior = static_cast(xml.parseInt()); else if (tag == "minMeter") MusEGlobal::config.minMeter = xml.parseInt(); else if (tag == "minSlider") MusEGlobal::config.minSlider = xml.parseDouble(); else if (tag == "freewheelMode") MusEGlobal::config.freewheelMode = xml.parseInt(); else if (tag == "denormalProtection") MusEGlobal::config.useDenormalBias = xml.parseInt(); else if (tag == "didYouKnow") MusEGlobal::config.showDidYouKnow = xml.parseInt(); else if (tag == "outputLimiter") MusEGlobal::config.useOutputLimiter = xml.parseInt(); else if (tag == "vstInPlace") MusEGlobal::config.vstInPlace = xml.parseInt(); else if (tag == "deviceAudioSampleRate") MusEGlobal::config.deviceAudioSampleRate = xml.parseInt(); else if (tag == "deviceAudioBufSize") MusEGlobal::config.deviceAudioBufSize = xml.parseInt(); else if (tag == "deviceAudioBackend") MusEGlobal::config.deviceAudioBackend = xml.parseInt(); else if (tag == "enableLatencyCorrection") MusEGlobal::config.enableLatencyCorrection = xml.parseInt(); else if (tag == "correctUnterminatedInBranchLatency") MusEGlobal::config.correctUnterminatedInBranchLatency = xml.parseInt(); else if (tag == "correctUnterminatedOutBranchLatency") MusEGlobal::config.correctUnterminatedOutBranchLatency = xml.parseInt(); else if (tag == "monitoringAffectsLatency") MusEGlobal::config.monitoringAffectsLatency = xml.parseInt(); else if (tag == "commonProjectLatency") MusEGlobal::config.commonProjectLatency = xml.parseInt(); else if (tag == "minControlProcessPeriod") MusEGlobal::config.minControlProcessPeriod = xml.parseUInt(); else if (tag == "guiRefresh") MusEGlobal::config.guiRefresh = xml.parseInt(); else if (tag == "userInstrumentsDir") // Obsolete MusEGlobal::config.userInstrumentsDir = xml.parse1(); // Keep for compatibility else if (tag == "startMode") MusEGlobal::config.startMode = xml.parseInt(); else if (tag == "startSong") MusEGlobal::config.startSong = xml.parse1(); else if (tag == "startSongLoadConfig") MusEGlobal::config.startSongLoadConfig = xml.parseInt(); else if (tag == "newDrumRecordCondition") MusEGlobal::config.newDrumRecordCondition = MusECore::newDrumRecordCondition_t(xml.parseInt()); else if (tag == "projectBaseFolder") MusEGlobal::config.projectBaseFolder = xml.parse1(); else if (tag == "projectStoreInFolder") MusEGlobal::config.projectStoreInFolder = xml.parseInt(); else if (tag == "useProjectSaveDialog") MusEGlobal::config.useProjectSaveDialog = xml.parseInt(); else if (tag == "popupsDefaultStayOpen") MusEGlobal::config.popupsDefaultStayOpen = xml.parseInt(); else if (tag == "leftMouseButtonCanDecrease") MusEGlobal::config.leftMouseButtonCanDecrease = xml.parseInt(); // else if (tag == "rangeMarkerWithoutMMB") // MusEGlobal::config.rangeMarkerWithoutMMB = xml.parseInt(); else if (tag == "addHiddenTracks") MusEGlobal::config.addHiddenTracks = xml.parseInt(); else if (tag == "drumTrackPreference") // Obsolete. There is only 'New' drum tracks now. // drumTrackPreference is fixed until it is removed some day... //MusEGlobal::config.drumTrackPreference = (MusEGlobal::drumTrackPreference_t) xml.parseInt(); xml.parseInt(); #ifdef _USE_INSTRUMENT_OVERRIDES_ else if (tag == "drummapOverrides") MusEGlobal::workingDrumMapInstrumentList.read(xml); #endif else if (tag == "unhideTracks") MusEGlobal::config.unhideTracks = xml.parseInt(); else if (tag == "smartFocus") MusEGlobal::config.smartFocus = xml.parseInt(); else if (tag == "borderlessMouse") MusEGlobal::config.borderlessMouse = xml.parseInt(); else if (tag == "velocityPerNote") MusEGlobal::config.velocityPerNote = xml.parseInt(); else if (tag == "plugin_groups") MusEGlobal::readPluginGroupConfiguration(xml); else if (tag == "synthDialogFavorites") MusEGui::SynthDialog::readFavConfiguration(xml); else if (tag == "mixdownPath") MusEGlobal::config.mixdownPath = xml.parse1(); else if (tag == "showNoteNamesInPianoRoll") MusEGlobal::config.showNoteNamesInPianoRoll = xml.parseInt(); else if (tag == "showNoteTooltips") MusEGlobal::config.showNoteTooltips = xml.parseInt(); else if (tag == "showTimeScaleBeatNumbers") MusEGlobal::config.showTimeScaleBeatNumbers = xml.parseInt(); else if (tag == "noPluginScaling") MusEGlobal::config.noPluginScaling = xml.parseInt(); // else if (tag == "openMDIWinMaximized") // MusEGlobal::config.openMDIWinMaximized = xml.parseInt(); else if (tag == "keepTransportWindowOnTop") MusEGlobal::config.keepTransportWindowOnTop = xml.parseInt(); else if (tag == "showStatusBar") MusEGlobal::config.showStatusBar = xml.parseInt(); // ---- the following only skips obsolete entries ---- else if ((tag == "arranger") || (tag == "geometryPianoroll") || (tag == "geometryDrumedit")) xml.skip(tag); else if ((tag == "lmaster") || (tag == "listedit")) xml.skip(tag); else if (tag == "mixerVisible") xml.skip(tag); else if (tag == "geometryMixer") xml.skip(tag); else if (tag == "txDeviceId") xml.parseInt(); else if (tag == "rxDeviceId") xml.parseInt(); else if (tag == "txSyncPort") xml.parseInt(); else if (tag == "rxSyncPort") xml.parseInt(); else if (tag == "syncgentype") xml.parseInt(); else if (tag == "genMTCSync") xml.parseInt(); else if (tag == "genMCSync") xml.parseInt(); else if (tag == "genMMC") xml.parseInt(); else if (tag == "acceptMTC") xml.parseInt(); else if (tag == "acceptMMC") xml.parseInt(); else if (tag == "acceptMC") xml.parseInt(); else if ((tag == "samplerate") || (tag == "segmentsize") || (tag == "segmentcount")) xml.parseInt(); else xml.unknown("configuration"); break; case Xml::Text: fprintf(stderr, "text <%s>\n", xml.s1().toLatin1().constData()); break; case Xml::Attribut: if (doReadMidiPortConfig==false) break; else if (tag == "version") { int major = xml.s2().section('.', 0, 0).toInt(); int minor = xml.s2().section('.', 1, 1).toInt(); xml.setVersion(major, minor); } break; case Xml::TagEnd: if (tag == "configuration") { return; } break; case Xml::Proc: default: break; } } } //--------------------------------------------------------- // readConfiguration //--------------------------------------------------------- bool readConfiguration() { return readConfiguration(nullptr); } bool readConfiguration(const char *configFile) { QByteArray ba; if (configFile == nullptr) { ba = MusEGlobal::configName.toLatin1(); configFile = ba.constData(); } fprintf(stderr, "Config File <%s>\n", configFile); FILE* f = fopen(configFile, "r"); if (f == nullptr) { if (MusEGlobal::debugMsg || MusEGlobal::debugMode) fprintf(stderr, "NO Config File <%s> found\n", configFile); if (MusEGlobal::config.userInstrumentsDir.isEmpty()) MusEGlobal::config.userInstrumentsDir = MusEGlobal::configPath + "/instruments"; return true; } Xml xml(f); bool skipmode = true; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto read_conf_end; case Xml::TagStart: if (skipmode && tag == "muse") skipmode = false; else if (skipmode) break; else if (tag == "configuration") readConfiguration(xml,true, true /* read global config as well */); else xml.unknown("muse config"); break; case Xml::Attribut: if (tag == "version") { int major = xml.s2().section('.', 0, 0).toInt(); int minor = xml.s2().section('.', 1, 1).toInt(); xml.setVersion(major, minor); } break; case Xml::TagEnd: if(!xml.isVersionEqualToLatest()) { fprintf(stderr, "\n***WARNING***\nLoaded config file version is %d.%d\nCurrent version is %d.%d\n" "Conversions may be applied!\n\n", xml.majorVersion(), xml.minorVersion(), xml.latestMajorVersion(), xml.latestMinorVersion()); } if (!skipmode && tag == "muse") { fclose(f); return false; } default: break; } } read_conf_end: fclose(f); return true; } //--------------------------------------------------------- // writeMetronomeConfiguration //--------------------------------------------------------- static void writeMetronomeConfiguration(int level, Xml& xml, bool is_global) { MusECore::MetronomeSettings* metro_settings = is_global ? &MusEGlobal::metroGlobalSettings : &MusEGlobal::metroSongSettings; xml.tag(level++, "metronom"); xml.intTag(level, "premeasures", metro_settings->preMeasures); xml.intTag(level, "measurepitch", metro_settings->measureClickNote); xml.intTag(level, "measurevelo", metro_settings->measureClickVelo); xml.intTag(level, "beatpitch", metro_settings->beatClickNote); xml.intTag(level, "beatvelo", metro_settings->beatClickVelo); xml.intTag(level, "accentpitch1", metro_settings->accentClick1); xml.intTag(level, "accentpitch2", metro_settings->accentClick2); xml.intTag(level, "accentvelo1", metro_settings->accentClick1Velo); xml.intTag(level, "accentvelo2", metro_settings->accentClick2Velo); xml.intTag(level, "channel", metro_settings->clickChan); xml.intTag(level, "port", metro_settings->clickPort); // Write the global metroUseSongSettings - ONLY if saving song configuration. if(!is_global) xml.intTag(level, "metroUseSongSettings", MusEGlobal::metroUseSongSettings); // Write either the global or song accents map. if(metro_settings->metroAccentsMap) metro_settings->metroAccentsMap->write(level, xml); // Write the global user accent presets - ONLY if saving global configuration. if(is_global) MusEGlobal::metroAccentPresets.write(level, xml, MusECore::MetroAccentsStruct::UserPreset); xml.intTag(level, "precountEnable", metro_settings->precountEnableFlag); xml.intTag(level, "fromMastertrack", metro_settings->precountFromMastertrackFlag); xml.intTag(level, "signatureZ", metro_settings->precountSigZ); xml.intTag(level, "signatureN", metro_settings->precountSigN); xml.intTag(level, "precountOnPlay", metro_settings->precountOnPlay); xml.intTag(level, "precountMuteMetronome", metro_settings->precountMuteMetronome); xml.intTag(level, "prerecord", metro_settings->precountPrerecord); xml.intTag(level, "preroll", metro_settings->precountPreroll); xml.intTag(level, "midiClickEnable", metro_settings->midiClickFlag); xml.intTag(level, "audioClickEnable", metro_settings->audioClickFlag); xml.floatTag(level, "audioClickVolume", metro_settings->audioClickVolume); xml.floatTag(level, "measClickVolume", metro_settings->measClickVolume); xml.floatTag(level, "beatClickVolume", metro_settings->beatClickVolume); xml.floatTag(level, "accent1ClickVolume", metro_settings->accent1ClickVolume); xml.floatTag(level, "accent2ClickVolume", metro_settings->accent2ClickVolume); xml.intTag(level, "clickSamples", metro_settings->clickSamples); xml.strTag(level, "beatSample", metro_settings->beatSample); xml.strTag(level, "measSample", metro_settings->measSample); xml.strTag(level, "accent1Sample", metro_settings->accent1Sample); xml.strTag(level, "accent2Sample", metro_settings->accent2Sample); xml.tag(level--, "/metronom"); } //--------------------------------------------------------- // writeSeqConfiguration //--------------------------------------------------------- static void writeSeqConfiguration(int level, Xml& xml, bool writePortInfo) { xml.tag(level++, "sequencer"); // If writePortInfo is true we are writing SONG configuration, // and if writePortInfo is NOT true we are writing GLOBAL configuration. // Write the global user accent presets - ONLY if saving global configuration. writeMetronomeConfiguration(level, xml, !writePortInfo); xml.intTag(level, "rcEnable", MusEGlobal::rcEnable); xml.intTag(level, "rcStop", MusEGlobal::rcStopNote); xml.intTag(level, "rcRecord", MusEGlobal::rcRecordNote); xml.intTag(level, "rcGotoLeft", MusEGlobal::rcGotoLeftMarkNote); xml.intTag(level, "rcPlay", MusEGlobal::rcPlayNote); xml.intTag(level, "rcSteprec", MusEGlobal::rcSteprecNote); xml.intTag(level, "rcForward", MusEGlobal::rcForwardNote); xml.intTag(level, "rcRewind", MusEGlobal::rcBackwardNote); xml.intTag(level, "rcEnableCC", MusEGlobal::rcEnableCC); xml.intTag(level, "rcStopCC", MusEGlobal::rcStopCC); xml.intTag(level, "rcRecordCC", MusEGlobal::rcRecordCC); xml.intTag(level, "rcGotoLeftCC", MusEGlobal::rcGotoLeftMarkCC); xml.intTag(level, "rcPlayCC", MusEGlobal::rcPlayCC); // xml.intTag(level, "rcInsertRest", MusEGlobal::rcInsertPauseCC); xml.intTag(level, "rcForwardCC", MusEGlobal::rcForwardCC); xml.intTag(level, "rcRewindCC", MusEGlobal::rcBackwardCC); if (writePortInfo) { for(iMidiDevice imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) { MidiDevice* dev = *imd; // TODO: For now, support only jack midi devices here. ALSA devices are different. //if(dev->deviceType() != MidiDevice::JACK_MIDI) if(dev->deviceType() != MidiDevice::JACK_MIDI && dev->deviceType() != MidiDevice::ALSA_MIDI) continue; xml.tag(level++, "mididevice"); xml.strTag(level, "name", dev->name()); if(dev->deviceType() != MidiDevice::ALSA_MIDI) xml.intTag(level, "type", dev->deviceType()); // Synths will not have been created yet when this is read! So, synthIs now store their own openFlags. if(dev->openFlags() != 1) xml.intTag(level, "openFlags", dev->openFlags()); if(dev->deviceType() == MidiDevice::JACK_MIDI) xml.intTag(level, "rwFlags", dev->rwFlags()); // Need this. Jack midi devs are created by app. p4.0.41 xml.etag(level--, "mididevice"); } // // write information about all midi ports, their assigned // instruments and all managed midi controllers // for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { bool used = false; MidiPort* mport = &MusEGlobal::midiPorts[i]; MidiDevice* dev = mport->device(); // Route check by Tim. Port can now be used for routing even if no device. // Also, check for other non-defaults and save port, to preserve settings even if no device. if(!mport->noInRoute() || !mport->noOutRoute() || // p4.0.17 Since MidiPort:: and MidiDevice::writeRouting() ignore ports with no device, ignore them here, too. // This prevents bogus routes from being saved and propagated in the med file. // Hmm tough decision, should we save if no device? That would preserve routes in case user upgrades HW, // or ALSA reorders or renames devices etc etc, then we have at least kept the track <-> port routes. mport->defaultInChannels() != (1<defaultOutChannels() || (!mport->instrument()->iname().isEmpty() && mport->instrument()->midiType() != MT_GM) || !mport->syncInfo().isDefault()) used = true; else { MidiTrackList* tl = MusEGlobal::song->midis(); for (iMidiTrack it = tl->begin(); it != tl->end(); ++it) { MidiTrack* t = *it; if (t->outPort() == i) { used = true; break; } } } if (!used && !dev) continue; xml.tag(level++, "midiport idx=\"%d\"", i); if(mport->defaultInChannels() != (1<defaultInChannels()); if(mport->defaultOutChannels()) xml.intTag(level, "defaultOutChans", mport->defaultOutChannels()); const MidiInstrument* mi = mport->instrument(); // FIXME: TODO: Make this user configurable. if(mi && !mi->iname().isEmpty() && mi->iname() != "GM") { if(mi->isSynti()) { // The instrument is a synthesizer. Store a reference to // the synthesizer track so it can be looked up upon loading. const SynthI* si = static_cast(mi); const int idx = MusEGlobal::song->tracks()->index(si); if(idx >= 0) xml.intTag(level, "trackIdx", idx); } else { // The instrument is not a synthesizer, it is one of our own // (loaded from an *.idf file). Just store a string identifier, // since we don't have unique indexes for .idf instruments. TODO ??? xml.strTag(level, "instrument", mi->iname()); } } if (dev) { xml.strTag(level, "name", dev->name()); } mport->syncInfo().write(level, xml); // write out registered controller for all channels MidiCtrlValListList* vll = mport->controller(); for (int k = 0; k < MusECore::MUSE_MIDI_CHANNELS; ++k) { int min = k << 24; int max = min + 0x100000; bool found = false; iMidiCtrlValList s = vll->lower_bound(min); iMidiCtrlValList e = vll->lower_bound(max); if (s != e) { for (iMidiCtrlValList i = s; i != e; ++i) { int ctl = i->second->num(); if(mport->drumController(ctl)) // Including internals like polyaftertouch ctl |= 0xff; // Don't bother saving these empty controllers since they are already always added! if(defaultManagedMidiController.find(ctl) != defaultManagedMidiController.end() && i->second->hwVal() == CTRL_VAL_UNKNOWN) continue; if(!found) { xml.tag(level++, "channel idx=\"%d\"", k); found = true; } xml.tag(level++, "controller id=\"%d\"", i->second->num()); if (i->second->hwVal() != CTRL_VAL_UNKNOWN) xml.intTag(level, "val", i->second->hwVal()); xml.etag(level--, "controller"); } } if(found) xml.etag(level--, "channel"); } xml.etag(level--, "midiport"); } } xml.tag(level, "/sequencer"); } void writeConfigurationColors(int level, MusECore::Xml& xml, bool partColorNames) { for (int i = 0; i < 16; ++i) { xml.colorTag(level, QString("palette") + QString::number(i), MusEGlobal::config.palette[i]); } for (int i = 0; i < NUM_PARTCOLORS; ++i) { xml.colorTag(level, QString("partColor") + QString::number(i), MusEGlobal::config.partColors[i]); } if(partColorNames) { for (int i = 0; i < NUM_PARTCOLORS; ++i) { xml.strTag(level, QString("partColorName") + QString::number(i), MusEGlobal::config.partColorNames[i]); } } xml.colorTag(level, "partCanvasBg", MusEGlobal::config.partCanvasBg); xml.colorTag(level, "dummyPartColor", MusEGlobal::config.dummyPartColor); xml.colorTag(level, "partCanvasCoarseRaster", MusEGlobal::config.partCanvasCoarseRasterColor); xml.colorTag(level, "partCanvasBeatRaster", MusEGlobal::config.partCanvasBeatRasterColor); xml.colorTag(level, "partCanvasFineRaster", MusEGlobal::config.partCanvasFineRasterColor); xml.colorTag(level, "trackBg", MusEGlobal::config.trackBg); xml.colorTag(level, "selectTrackBg", MusEGlobal::config.selectTrackBg); xml.colorTag(level, "selectTrackFg", MusEGlobal::config.selectTrackFg); xml.colorTag(level, "selectTrackCurBg", MusEGlobal::config.selectTrackCurBg); xml.colorTag(level, "trackSectionDividerColor", MusEGlobal::config.trackSectionDividerColor); // xml.colorTag(level, "mixerBg", MusEGlobal::config.mixerBg); xml.colorTag(level, "midiTrackLabelBg", MusEGlobal::config.midiTrackLabelBg); xml.colorTag(level, "newDrumTrackLabelBg2",MusEGlobal::config.newDrumTrackLabelBg); xml.colorTag(level, "waveTrackLabelBg", MusEGlobal::config.waveTrackLabelBg); xml.colorTag(level, "outputTrackLabelBg", MusEGlobal::config.outputTrackLabelBg); xml.colorTag(level, "inputTrackLabelBg", MusEGlobal::config.inputTrackLabelBg); xml.colorTag(level, "groupTrackLabelBg", MusEGlobal::config.groupTrackLabelBg); xml.colorTag(level, "auxTrackLabelBg2", MusEGlobal::config.auxTrackLabelBg); xml.colorTag(level, "synthTrackLabelBg", MusEGlobal::config.synthTrackLabelBg); xml.colorTag(level, "midiTrackBg", MusEGlobal::config.midiTrackBg); xml.colorTag(level, "ctrlGraphFg", MusEGlobal::config.ctrlGraphFg); xml.colorTag(level, "ctrlGraphSel", MusEGlobal::config.ctrlGraphSel); xml.colorTag(level, "drumTrackBg", MusEGlobal::config.drumTrackBg); xml.colorTag(level, "newDrumTrackBg",MusEGlobal::config.newDrumTrackBg); xml.colorTag(level, "waveTrackBg", MusEGlobal::config.waveTrackBg); xml.colorTag(level, "outputTrackBg", MusEGlobal::config.outputTrackBg); xml.colorTag(level, "inputTrackBg", MusEGlobal::config.inputTrackBg); xml.colorTag(level, "groupTrackBg", MusEGlobal::config.groupTrackBg); xml.colorTag(level, "auxTrackBg", MusEGlobal::config.auxTrackBg); xml.colorTag(level, "synthTrackBg", MusEGlobal::config.synthTrackBg); xml.colorTag(level, "sliderBarDefaultColor", MusEGlobal::config.sliderBarColor); xml.colorTag(level, "sliderDefaultColor2", MusEGlobal::config.sliderBackgroundColor); xml.colorTag(level, "panSliderColor2", MusEGlobal::config.panSliderColor); xml.colorTag(level, "gainSliderColor2", MusEGlobal::config.gainSliderColor); xml.colorTag(level, "auxSliderColor2", MusEGlobal::config.auxSliderColor); xml.colorTag(level, "audioVolumeSliderColor2", MusEGlobal::config.audioVolumeSliderColor); xml.colorTag(level, "midiVolumeSliderColor2", MusEGlobal::config.midiVolumeSliderColor); xml.colorTag(level, "audioVolumeHandleColor", MusEGlobal::config.audioVolumeHandleColor); xml.colorTag(level, "midiVolumeHandleColor", MusEGlobal::config.midiVolumeHandleColor); xml.colorTag(level, "audioControllerSliderDefaultColor2", MusEGlobal::config.audioControllerSliderColor); xml.colorTag(level, "audioPropertySliderDefaultColor2", MusEGlobal::config.audioPropertySliderColor); xml.colorTag(level, "midiControllerSliderDefaultColor2", MusEGlobal::config.midiControllerSliderColor); xml.colorTag(level, "midiPropertySliderDefaultColor2", MusEGlobal::config.midiPropertySliderColor); xml.colorTag(level, "midiPatchReadoutColor", MusEGlobal::config.midiPatchReadoutColor); xml.colorTag(level, "knobFontColor", MusEGlobal::config.knobFontColor); xml.colorTag(level, "audioMeterPrimaryColor", MusEGlobal::config.audioMeterPrimaryColor); xml.colorTag(level, "midiMeterPrimaryColor", MusEGlobal::config.midiMeterPrimaryColor); xml.colorTag(level, "meterBackgroundColor", MusEGlobal::config.meterBackgroundColor); xml.colorTag(level, "rackItemBackgroundColor", MusEGlobal::config.rackItemBackgroundColor); xml.colorTag(level, "rackItemBgActiveColor", MusEGlobal::config.rackItemBgActiveColor); xml.colorTag(level, "rackItemFontColor", MusEGlobal::config.rackItemFontColor); xml.colorTag(level, "rackItemFontActiveColor", MusEGlobal::config.rackItemFontActiveColor); xml.colorTag(level, "rackItemBorderColor", MusEGlobal::config.rackItemBorderColor); xml.colorTag(level, "rackItemFontColorHover", MusEGlobal::config.rackItemFontColorHover); xml.colorTag(level, "midiInstrumentBackgroundColor", MusEGlobal::config.midiInstrumentBackgroundColor); xml.colorTag(level, "midiInstrumentBgActiveColor", MusEGlobal::config.midiInstrumentBgActiveColor); xml.colorTag(level, "midiInstrumentFontColor", MusEGlobal::config.midiInstrumentFontColor); xml.colorTag(level, "midiInstrumentFontActiveColor", MusEGlobal::config.midiInstrumentFontActiveColor); xml.colorTag(level, "midiInstrumentBorderColor", MusEGlobal::config.midiInstrumentBorderColor); xml.colorTag(level, "transportHandleColor", MusEGlobal::config.transportHandleColor); xml.colorTag(level, "bigtimeForegroundcolor", MusEGlobal::config.bigTimeForegroundColor); xml.colorTag(level, "bigtimeBackgroundcolor", MusEGlobal::config.bigTimeBackgroundColor); xml.colorTag(level, "waveEditBackgroundColor", MusEGlobal::config.waveEditBackgroundColor); xml.colorTag(level, "rulerBackgroundColor", MusEGlobal::config.rulerBg); xml.colorTag(level, "rulerForegroundColor", MusEGlobal::config.rulerFg); xml.colorTag(level, "rulerCurrentColor", MusEGlobal::config.rulerCurrent); xml.colorTag(level, "waveNonselectedPart", MusEGlobal::config.waveNonselectedPart); xml.colorTag(level, "wavePeakColor", MusEGlobal::config.wavePeakColor); xml.colorTag(level, "waveRmsColor", MusEGlobal::config.waveRmsColor); xml.colorTag(level, "wavePeakColorSelected", MusEGlobal::config.wavePeakColorSelected); xml.colorTag(level, "waveRmsColorSelected", MusEGlobal::config.waveRmsColorSelected); xml.colorTag(level, "partWaveColorPeak", MusEGlobal::config.partWaveColorPeak); xml.colorTag(level, "partWaveColorRms", MusEGlobal::config.partWaveColorRms); xml.colorTag(level, "partMidiDarkEventColor", MusEGlobal::config.partMidiDarkEventColor); xml.colorTag(level, "partMidiLightEventColor", MusEGlobal::config.partMidiLightEventColor); xml.colorTag(level, "midiCanvasBackgroundColor", MusEGlobal::config.midiCanvasBg); xml.colorTag(level, "midiCanvasFineColor", MusEGlobal::config.midiCanvasFineColor); xml.colorTag(level, "midiCanvasBeatColor", MusEGlobal::config.midiCanvasBeatColor); xml.colorTag(level, "midiCanvasBarColor", MusEGlobal::config.midiCanvasBarColor); xml.colorTag(level, "midiDividerColor", MusEGlobal::config.midiDividerColor); xml.colorTag(level, "midiItemColor", MusEGlobal::config.midiItemColor); xml.colorTag(level, "midiItemSelectedColor", MusEGlobal::config.midiItemSelectedColor); xml.colorTag(level, "midiControllerViewBackgroundColor", MusEGlobal::config.midiControllerViewBg); xml.colorTag(level, "drumListBackgroundColor", MusEGlobal::config.drumListBg); xml.colorTag(level, "drumListFont", MusEGlobal::config.drumListFont); xml.colorTag(level, "drumListSel", MusEGlobal::config.drumListSel); xml.colorTag(level, "drumListSelFont", MusEGlobal::config.drumListSelFont); xml.colorTag(level, "pianoCurrentKey", MusEGlobal::config.pianoCurrentKey); xml.colorTag(level, "pianoPressedKey", MusEGlobal::config.pianoPressedKey); xml.colorTag(level, "pianoSelectedKey", MusEGlobal::config.pianoSelectedKey); xml.colorTag(level, "markerColor", MusEGlobal::config.markerColor); xml.colorTag(level, "rangeMarkerColor", MusEGlobal::config.rangeMarkerColor); xml.colorTag(level, "positionMarkerColor", MusEGlobal::config.positionMarkerColor); xml.colorTag(level, "currentPositionColor", MusEGlobal::config.currentPositionColor); } } // namespace MusECore namespace MusEGui { //--------------------------------------------------------- // writeGlobalConfiguration //--------------------------------------------------------- void MusE::writeGlobalConfiguration() const { FILE* f = fopen(MusEGlobal::configName.toLatin1().constData(), "w"); if (f == nullptr) { fprintf(stderr, "save configuration to <%s> failed: %s\n", MusEGlobal::configName.toLatin1().constData(), strerror(errno)); return; } MusECore::Xml xml(f); xml.header(); xml.nput(0, "\n", xml.latestMajorVersion(), xml.latestMinorVersion()); writeGlobalConfiguration(1, xml); xml.tag(1, "/muse"); fclose(f); } bool MusE::loadConfigurationColors(QWidget* parent) { if(!parent) parent = this; //QString file = QFileDialog::getOpenFileName(parent, tr("Load configuration colors"), QString(), tr("MusE color configuration files *.cfc (*.cfc)")); QString file = MusEGui::getOpenFileName(QString("themes"), MusEGlobal::colors_config_file_pattern, this, tr("Load configuration colors"), nullptr, MusEGui::MFileDialog::GLOBAL_VIEW); if(file.isEmpty()) return false; if(QMessageBox::question(parent, QString("MusE"), tr("Color settings will immediately be replaced with any found in the file.\nAre you sure you want to proceed?"), tr("&Ok"), tr("&Cancel"), QString(), 0, 1 ) == 1) return false; // Read, and return if error. if(MusECore::readConfiguration(file.toLatin1().constData())) // True if error. { fprintf(stderr, "MusE::loadConfigurationColors failed\n"); return false; } // Notify app, and write into configuration file. // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. changeConfig(false); return true; } bool MusE::saveConfigurationColors(QWidget* parent) { if(!parent) parent = this; QString file = MusEGui::getSaveFileName(QString("themes"), MusEGlobal::colors_config_file_pattern, this, tr("Save configuration colors"), nullptr, MusEGui::MFileDialog::USER_VIEW); if(file.isEmpty()) return false; // redundant, this is already done by the file dialog itself (kybos) // if(QFile::exists(file)) // { // if(QMessageBox::question(parent, QString("MusE"), // tr("File exists.\nDo you want to overwrite it?"), tr("&Ok"), tr("&Cancel"), // QString(), 0, 1 ) == 1) // return false; // } FILE* f = fopen(file.toLatin1().constData(), "w"); if (f == nullptr) { fprintf(stderr, "save configuration colors to <%s> failed: %s\n", file.toLatin1().constData(), strerror(errno)); return false; } MusECore::Xml xml(f); xml.header(); xml.nput(0, "\n", xml.latestMajorVersion(), xml.latestMinorVersion()); xml.tag(1, "configuration"); MusECore::writeConfigurationColors(2, xml, false); // Don't save part colour names. xml.etag(1, "configuration"); xml.tag(0, "/muse"); fclose(f); return true; } void MusE::writeGlobalConfiguration(int level, MusECore::Xml& xml) const { xml.tag(level++, "configuration"); xml.strTag(level, "pluginLadspaPathList", MusEGlobal::config.pluginLadspaPathList.join(":")); xml.strTag(level, "pluginDssiPathList", MusEGlobal::config.pluginDssiPathList.join(":")); xml.strTag(level, "pluginVstsPathList", MusEGlobal::config.pluginVstPathList.join(":")); xml.strTag(level, "pluginLinuxVstsPathList", MusEGlobal::config.pluginLinuxVstPathList.join(":")); xml.strTag(level, "pluginLv2PathList", MusEGlobal::config.pluginLv2PathList.join(":")); if(MusEGlobal::defaultAudioConverterSettings) MusEGlobal::defaultAudioConverterSettings->write(level, xml, &MusEGlobal::audioConverterPluginList); xml.intTag(level, "pluginCacheTriggerRescan", MusEGlobal::config.pluginCacheTriggerRescan); xml.intTag(level, "enableAlsaMidiDriver", MusEGlobal::config.enableAlsaMidiDriver); xml.intTag(level, "division", MusEGlobal::config.division); xml.intTag(level, "rtcTicks", MusEGlobal::config.rtcTicks); xml.intTag(level, "curMidiSyncInPort", MusEGlobal::config.curMidiSyncInPort); xml.intTag(level, "midiSendInit", MusEGlobal::config.midiSendInit); xml.intTag(level, "warnInitPending", MusEGlobal::config.warnInitPending); xml.intTag(level, "midiSendCtlDefaults", MusEGlobal::config.midiSendCtlDefaults); xml.intTag(level, "midiSendNullParameters", MusEGlobal::config.midiSendNullParameters); xml.intTag(level, "midiOptimizeControllers", MusEGlobal::config.midiOptimizeControllers); xml.intTag(level, "warnIfBadTiming", MusEGlobal::config.warnIfBadTiming); xml.intTag(level, "warnOnFileVersions", MusEGlobal::config.warnOnFileVersions); xml.intTag(level, "minMeter", MusEGlobal::config.minMeter); xml.doubleTag(level, "minSlider", MusEGlobal::config.minSlider); xml.intTag(level, "freewheelMode", MusEGlobal::config.freewheelMode); xml.intTag(level, "denormalProtection", MusEGlobal::config.useDenormalBias); xml.intTag(level, "didYouKnow", MusEGlobal::config.showDidYouKnow); xml.intTag(level, "outputLimiter", MusEGlobal::config.useOutputLimiter); xml.intTag(level, "vstInPlace", MusEGlobal::config.vstInPlace); xml.intTag(level, "deviceAudioBufSize", MusEGlobal::config.deviceAudioBufSize); xml.intTag(level, "deviceAudioSampleRate", MusEGlobal::config.deviceAudioSampleRate); xml.intTag(level, "deviceAudioBackend", MusEGlobal::config.deviceAudioBackend); xml.intTag(level, "enableLatencyCorrection", MusEGlobal::config.enableLatencyCorrection); xml.intTag(level, "correctUnterminatedInBranchLatency", MusEGlobal::config.correctUnterminatedInBranchLatency); xml.intTag(level, "correctUnterminatedOutBranchLatency", MusEGlobal::config.correctUnterminatedOutBranchLatency); xml.intTag(level, "monitoringAffectsLatency", MusEGlobal::config.monitoringAffectsLatency); xml.intTag(level, "commonProjectLatency", MusEGlobal::config.commonProjectLatency); xml.uintTag(level, "minControlProcessPeriod", MusEGlobal::config.minControlProcessPeriod); xml.intTag(level, "guiRefresh", MusEGlobal::config.guiRefresh); xml.intTag(level, "extendedMidi", MusEGlobal::config.extendedMidi); xml.intTag(level, "midiExportDivision", MusEGlobal::config.midiDivision); xml.strTag(level, "copyright", MusEGlobal::config.copyright); xml.intTag(level, "smfFormat", MusEGlobal::config.smfFormat); xml.intTag(level, "expRunningStatus", MusEGlobal::config.expRunningStatus); xml.intTag(level, "exp2ByteTimeSigs", MusEGlobal::config.exp2ByteTimeSigs); xml.intTag(level, "expOptimNoteOffs", MusEGlobal::config.expOptimNoteOffs); xml.intTag(level, "importMidiSplitParts", MusEGlobal::config.importMidiSplitParts); xml.intTag(level, "importDevNameMetas", MusEGlobal::config.importDevNameMetas); xml.intTag(level, "useLastEditedEvent", MusEGlobal::config.useLastEditedEvent); xml.intTag(level, "importInstrNameMetas", MusEGlobal::config.importInstrNameMetas); xml.intTag(level, "exportPortsDevices", MusEGlobal::config.exportPortsDevices); xml.intTag(level, "exportPortDeviceSMF0", MusEGlobal::config.exportPortDeviceSMF0); xml.intTag(level, "exportDrumMapOverrides", MusEGlobal::config.exportDrumMapOverrides); xml.intTag(level, "exportChannelOverridesToNewTrack", MusEGlobal::config.exportChannelOverridesToNewTrack); xml.intTag(level, "exportModeInstr", MusEGlobal::config.exportModeInstr); xml.strTag(level, "importMidiDefaultInstr", MusEGlobal::config.importMidiDefaultInstr); xml.intTag(level, "startMode", MusEGlobal::config.startMode); xml.strTag(level, "startSong", MusEGlobal::config.startSong); xml.intTag(level, "startSongLoadConfig", MusEGlobal::config.startSongLoadConfig); xml.intTag(level, "newDrumRecordCondition", MusEGlobal::config.newDrumRecordCondition); xml.strTag(level, "projectBaseFolder", MusEGlobal::config.projectBaseFolder); xml.intTag(level, "projectStoreInFolder", MusEGlobal::config.projectStoreInFolder); xml.intTag(level, "useProjectSaveDialog", MusEGlobal::config.useProjectSaveDialog); xml.intTag(level, "midiInputDevice", MusEGlobal::midiInputPorts); xml.intTag(level, "midiInputChannel", MusEGlobal::midiInputChannel); xml.intTag(level, "midiRecordType", MusEGlobal::midiRecordType); xml.intTag(level, "midiThruType", MusEGlobal::midiThruType); xml.intTag(level, "midiFilterCtrl1", MusEGlobal::midiFilterCtrl1); xml.intTag(level, "midiFilterCtrl2", MusEGlobal::midiFilterCtrl2); xml.intTag(level, "midiFilterCtrl3", MusEGlobal::midiFilterCtrl3); xml.intTag(level, "midiFilterCtrl4", MusEGlobal::midiFilterCtrl4); xml.intTag(level, "preferredRouteNameOrAlias", static_cast(MusEGlobal::config.preferredRouteNameOrAlias)); xml.intTag(level, "routerExpandVertically", MusEGlobal::config.routerExpandVertically); xml.intTag(level, "routerGroupingChannels", MusEGlobal::config.routerGroupingChannels); // xml.strTag(level, "qtStyle", MusEGlobal::config.style); xml.intTag(level, "autoSave", MusEGlobal::config.autoSave); xml.strTag(level, "museTheme", MusEGlobal::config.theme); xml.strTag(level, "externalWavEditor", MusEGlobal::config.externalWavEditor); xml.intTag(level, "useOldStyleStopShortCut", MusEGlobal::config.useOldStyleStopShortCut); xml.intTag(level, "useRewindOnStop", MusEGlobal::config.useRewindOnStop); xml.intTag(level, "moveArmedCheckBox", MusEGlobal::config.moveArmedCheckBox); xml.intTag(level, "popupsDefaultStayOpen", MusEGlobal::config.popupsDefaultStayOpen); xml.intTag(level, "leftMouseButtonCanDecrease", MusEGlobal::config.leftMouseButtonCanDecrease); // xml.intTag(level, "rangeMarkerWithoutMMB", MusEGlobal::config.rangeMarkerWithoutMMB); xml.intTag(level, "smartFocus", MusEGlobal::config.smartFocus); xml.intTag(level, "borderlessMouse", MusEGlobal::config.borderlessMouse); xml.intTag(level, "velocityPerNote", MusEGlobal::config.velocityPerNote); xml.intTag(level, "unhideTracks", MusEGlobal::config.unhideTracks); xml.intTag(level, "addHiddenTracks", MusEGlobal::config.addHiddenTracks); // Obsolete. There is only 'New' drum tracks now. // drumTrackPreference is fixed until it is removed some day... //xml.intTag(level, "drumTrackPreference", MusEGlobal::config.drumTrackPreference); #ifdef _USE_INSTRUMENT_OVERRIDES_ MusECore::midiInstruments.writeDrummapOverrides(level, xml); #endif xml.intTag(level, "waveTracksVisible", MusECore::WaveTrack::visible()); xml.intTag(level, "auxTracksVisible", MusECore::AudioAux::visible()); xml.intTag(level, "groupTracksVisible", MusECore::AudioGroup::visible()); xml.intTag(level, "midiTracksVisible", MusECore::MidiTrack::visible()); xml.intTag(level, "inputTracksVisible", MusECore::AudioInput::visible()); xml.intTag(level, "outputTracksVisible", MusECore::AudioOutput::visible()); xml.intTag(level, "synthTracksVisible", MusECore::SynthI::visible()); xml.intTag(level, "trackHeight", MusEGlobal::config.trackHeight); xml.intTag(level, "scrollableSubMenus", MusEGlobal::config.scrollableSubMenus); xml.intTag(level, "liveWaveUpdate", MusEGlobal::config.liveWaveUpdate); xml.intTag(level, "audioEffectsRackVisibleItems", MusEGlobal::config.audioEffectsRackVisibleItems); xml.intTag(level, "preferKnobsVsSliders", MusEGlobal::config.preferKnobsVsSliders); xml.intTag(level, "showControlValues", MusEGlobal::config.showControlValues); xml.intTag(level, "monitorOnRecord", MusEGlobal::config.monitorOnRecord); xml.intTag(level, "lineEditStyleHack", MusEGlobal::config.lineEditStyleHack); xml.intTag(level, "preferMidiVolumeDb", MusEGlobal::config.preferMidiVolumeDb); xml.intTag(level, "midiCtrlGraphMergeErase", MusEGlobal::config.midiCtrlGraphMergeErase); xml.intTag(level, "midiCtrlGraphMergeEraseInclusive", MusEGlobal::config.midiCtrlGraphMergeEraseInclusive); xml.intTag(level, "midiCtrlGraphMergeEraseWysiwyg", MusEGlobal::config.midiCtrlGraphMergeEraseWysiwyg); xml.intTag(level, "lv2UiBehavior", static_cast(MusEGlobal::config.lv2UiBehavior)); xml.strTag(level, "mixdownPath", MusEGlobal::config.mixdownPath); xml.intTag(level, "showNoteNamesInPianoRoll", MusEGlobal::config.showNoteNamesInPianoRoll); xml.intTag(level, "showNoteTooltips", MusEGlobal::config.showNoteTooltips); xml.intTag(level, "showTimeScaleBeatNumbers", MusEGlobal::config.showTimeScaleBeatNumbers); xml.intTag(level, "noPluginScaling", MusEGlobal::config.noPluginScaling); // xml.intTag(level, "openMDIWinMaximized", MusEGlobal::config.openMDIWinMaximized); xml.intTag(level, "keepTransportWindowOnTop", MusEGlobal::config.keepTransportWindowOnTop); xml.intTag(level, "showStatusBar", MusEGlobal::config.showStatusBar); for (int i = 1; i < NUM_FONTS; ++i) { // for (int i = 0; i < NUM_FONTS; ++i) { xml.strTag(level, QString("font") + QString::number(i), MusEGlobal::config.fonts[i].toString()); } xml.intTag(level, "autoAdjustFontSize", MusEGlobal::config.autoAdjustFontSize); xml.intTag(level, "globalAlphaBlend", MusEGlobal::config.globalAlphaBlend); // MusECore::writeConfigurationColors(level, xml); xml.intTag(level, "mtctype", MusEGlobal::mtcType); xml.nput(level, "%02d:%02d:%02d:%02d:%02d\n", MusEGlobal::mtcOffset.h(), MusEGlobal::mtcOffset.m(), MusEGlobal::mtcOffset.s(), MusEGlobal::mtcOffset.f(), MusEGlobal::mtcOffset.sf()); xml.intTag(level, "extSync", MusEGlobal::extSyncFlag); xml.intTag(level, "useJackTransport", MusEGlobal::config.useJackTransport); xml.intTag(level, "timebaseMaster", MusEGlobal::config.timebaseMaster); xml.qrectTag(level, "geometryMain", MusEGlobal::config.geometryMain); xml.qrectTag(level, "geometryTransport", MusEGlobal::config.geometryTransport); xml.qrectTag(level, "geometryBigTime", MusEGlobal::config.geometryBigTime); xml.intTag(level, "bigtimeVisible", MusEGlobal::config.bigTimeVisible); xml.intTag(level, "transportVisible", MusEGlobal::config.transportVisible); xml.intTag(level, "mixer1Visible", MusEGlobal::config.mixer1Visible); xml.intTag(level, "mixer2Visible", MusEGlobal::config.mixer2Visible); // True = Write global config. MusEGlobal::config.mixer1.write(level, xml, true); MusEGlobal::config.mixer2.write(level, xml, true); xml.intTag(level, "showSplashScreen", MusEGlobal::config.showSplashScreen); xml.intTag(level, "canvasShowPartType", MusEGlobal::config.canvasShowPartType); xml.intTag(level, "canvasShowPartEvent", MusEGlobal::config.canvasShowPartEvent); xml.intTag(level, "canvasShowGrid", MusEGlobal::config.canvasShowGrid); xml.intTag(level, "canvasShowGridHorizontalAlways", MusEGlobal::config.canvasShowGridHorizontalAlways); xml.intTag(level, "canvasShowGridBeatsAlways", MusEGlobal::config.canvasShowGridBeatsAlways); xml.intTag(level, "useTrackColorForParts", MusEGlobal::config.useTrackColorForParts); xml.strTag(level, "canvasBgPixmap", MusEGlobal::config.canvasBgPixmap); xml.strTag(level, "canvasCustomBgList", MusEGlobal::config.canvasCustomBgList.join(";")); xml.intTag(level, "maxAliasedPointSize", MusEGlobal::config.maxAliasedPointSize); xml.intTag(level, "iconSize", MusEGlobal::config.iconSize); xml.intTag(level, "cursorSize", MusEGlobal::config.cursorSize); xml.intTag(level, "trackGradientStrength", MusEGlobal::config.trackGradientStrength); xml.intTag(level, "partGradientStrength", MusEGlobal::config.partGradientStrength); xml.intTag(level, "cascadeStylesheets", MusEGlobal::config.cascadeStylesheets); xml.intTag(level, "showIconsInMenus", MusEGlobal::config.showIconsInMenus); xml.intTag(level, "useNativeStandardDialogs", MusEGlobal::config.useNativeStandardDialogs); MusEGlobal::writePluginGroupConfiguration(level, xml); MusEGui::SynthDialog::writeFavConfiguration(level, xml); writeSeqConfiguration(level, xml, false); MusEGui::DrumEdit::writeConfiguration(level, xml); MusEGui::PianoRoll::writeConfiguration(level, xml); MusEGui::ScoreEdit::write_configuration(level, xml); MusEGui::MasterEdit::writeConfiguration(level, xml); MusEGui::WaveEdit::writeConfiguration(level, xml); // MusEGui::ListEdit::writeConfiguration(level, xml); // MusEGui::LMaster::writeConfiguration(level, xml); arrangerView->writeConfiguration(level, xml); MusEGui::write_function_dialog_config(level, xml); MusEGui::writeShortCuts(level, xml); xml.etag(level, "configuration"); } //--------------------------------------------------------- // writeConfiguration // write song specific configuration //--------------------------------------------------------- void MusE::writeConfiguration(int level, MusECore::Xml& xml) const { xml.tag(level++, "configuration"); xml.intTag(level, "midiInputDevice", MusEGlobal::midiInputPorts); xml.intTag(level, "midiInputChannel", MusEGlobal::midiInputChannel); xml.intTag(level, "midiRecordType", MusEGlobal::midiRecordType); xml.intTag(level, "midiThruType", MusEGlobal::midiThruType); xml.intTag(level, "midiFilterCtrl1", MusEGlobal::midiFilterCtrl1); xml.intTag(level, "midiFilterCtrl2", MusEGlobal::midiFilterCtrl2); xml.intTag(level, "midiFilterCtrl3", MusEGlobal::midiFilterCtrl3); xml.intTag(level, "midiFilterCtrl4", MusEGlobal::midiFilterCtrl4); xml.intTag(level, "mtctype", MusEGlobal::mtcType); xml.nput(level, "%02d:%02d:%02d:%02d:%02d\n", MusEGlobal::mtcOffset.h(), MusEGlobal::mtcOffset.m(), MusEGlobal::mtcOffset.s(), MusEGlobal::mtcOffset.f(), MusEGlobal::mtcOffset.sf()); xml.uintTag(level, "sendClockDelay", MusEGlobal::syncSendFirstClockDelay); xml.intTag(level, "useJackTransport", MusEGlobal::config.useJackTransport); xml.intTag(level, "timebaseMaster", MusEGlobal::config.timebaseMaster); xml.intTag(level, "syncRecFilterPreset", MusEGlobal::syncRecFilterPreset); xml.doubleTag(level, "syncRecTempoValQuant", MusEGlobal::syncRecTempoValQuant); xml.intTag(level, "extSync", MusEGlobal::extSyncFlag); xml.intTag(level, "bigtimeVisible", viewBigtimeAction->isChecked()); xml.intTag(level, "transportVisible", viewTransportAction->isChecked()); xml.geometryTag(level, "geometryMain", this); // FINDME: maybe remove this? do we want // the main win to jump around when loading? if (transport) xml.geometryTag(level, "geometryTransport", transport); if (bigtime) xml.geometryTag(level, "geometryBigTime", bigtime); // i thought this was obsolete, but it seems to be necessary (flo) xml.intTag(level, "markerVisible", viewMarkerAction->isChecked()); // but storing the geometry IS obsolete. this is really // done by TopLevel::writeConfiguration xml.intTag(level, "mixer1Visible", viewMixerAAction->isChecked()); xml.intTag(level, "mixer2Visible", viewMixerBAction->isChecked()); // False = Write song-specific config. MusEGlobal::config.mixer1.write(level, xml, false); MusEGlobal::config.mixer2.write(level, xml, false); writeSeqConfiguration(level, xml, true); MusEGui::write_function_dialog_config(level, xml); writeMidiTransforms(level, xml); writeMidiInputTransforms(level, xml); xml.etag(level, "configuration"); } //--------------------------------------------------------- // configMidiSync //--------------------------------------------------------- void MusE::configMidiSync() { if (!midiSyncConfig) // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). midiSyncConfig = new MusEGui::MidiSyncConfig; if (midiSyncConfig->isVisible()) { midiSyncConfig->raise(); midiSyncConfig->activateWindow(); } else midiSyncConfig->show(); } //--------------------------------------------------------- // configMidiFile //--------------------------------------------------------- void MusE::configMidiFile() { if (!midiFileConfig) // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). midiFileConfig = new MusEGui::MidiFileConfig(); midiFileConfig->updateValues(); if (midiFileConfig->isVisible()) { midiFileConfig->raise(); midiFileConfig->activateWindow(); } else midiFileConfig->show(); } //--------------------------------------------------------- // configGlobalSettings //--------------------------------------------------------- void MusE::configGlobalSettings() { if (!globalSettingsConfig) // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). globalSettingsConfig = new MusEGui::GlobalSettingsConfig(); if (globalSettingsConfig->isVisible()) { globalSettingsConfig->raise(); globalSettingsConfig->activateWindow(); } else globalSettingsConfig->show(); } //--------------------------------------------------------- // MidiFileConfig // config properties of exported midi files //--------------------------------------------------------- MidiFileConfig::MidiFileConfig(QWidget* parent) : QDialog(parent), ConfigMidiFileBase() { setupUi(this); connect(buttonOk, SIGNAL(clicked()), SLOT(okClicked())); connect(buttonCancel, SIGNAL(clicked()), SLOT(cancelClicked())); } //--------------------------------------------------------- // updateValues //--------------------------------------------------------- void MidiFileConfig::updateValues() { importDefaultInstr->clear(); for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) if(!(*i)->isSynti()) // Sorry, no synths for now. importDefaultInstr->addItem((*i)->iname()); int idx = importDefaultInstr->findText(MusEGlobal::config.importMidiDefaultInstr); if(idx != -1) importDefaultInstr->setCurrentIndex(idx); QString defInstr = importDefaultInstr->currentText(); if(!defInstr.isEmpty()) MusEGlobal::config.importMidiDefaultInstr = defInstr; int divisionIdx = 2; switch(MusEGlobal::config.midiDivision) { case 96: divisionIdx = 0; break; case 192: divisionIdx = 1; break; case 384: divisionIdx = 2; break; } divisionCombo->setCurrentIndex(divisionIdx); formatCombo->setCurrentIndex(MusEGlobal::config.smfFormat); extendedFormat->setChecked(MusEGlobal::config.extendedMidi); copyrightEdit->setText(MusEGlobal::config.copyright); runningStatus->setChecked(MusEGlobal::config.expRunningStatus); optNoteOffs->setChecked(MusEGlobal::config.expOptimNoteOffs); twoByteTimeSigs->setChecked(MusEGlobal::config.exp2ByteTimeSigs); splitPartsCheckBox->setChecked(MusEGlobal::config.importMidiSplitParts); // Obsolete. There is only 'New' drum tracks now. // newDrumsCheckbox->setChecked(MusEGlobal::config.importMidiNewStyleDrum); // oldDrumsCheckbox->setChecked(!MusEGlobal::config.importMidiNewStyleDrum); importDevNameMetas->setChecked(MusEGlobal::config.importDevNameMetas); importInstrNameMetas->setChecked(MusEGlobal::config.importInstrNameMetas); exportPortDeviceSMF0->setChecked(MusEGlobal::config.exportPortDeviceSMF0); drumMapOverrides->setChecked(MusEGlobal::config.exportDrumMapOverrides); channelOverridesToNewTrack->setChecked(MusEGlobal::config.exportChannelOverridesToNewTrack); exportPortMetas->setChecked(MusEGlobal::config.exportPortsDevices & MusEGlobal::PORT_NUM_META); exportDeviceNameMetas->setChecked(MusEGlobal::config.exportPortsDevices & MusEGlobal::DEVICE_NAME_META); exportModeSysexes->setChecked(MusEGlobal::config.exportModeInstr & MusEGlobal::MODE_SYSEX); exportInstrumentNames->setChecked(MusEGlobal::config.exportModeInstr & MusEGlobal::INSTRUMENT_NAME_META); } //--------------------------------------------------------- // okClicked //--------------------------------------------------------- void MidiFileConfig::okClicked() { QString defInstr = importDefaultInstr->currentText(); if(!defInstr.isEmpty()) MusEGlobal::config.importMidiDefaultInstr = defInstr; int divisionIdx = divisionCombo->currentIndex(); int divisions[3] = { 96, 192, 384 }; if (divisionIdx >= 0 && divisionIdx < 3) MusEGlobal::config.midiDivision = divisions[divisionIdx]; MusEGlobal::config.extendedMidi = extendedFormat->isChecked(); MusEGlobal::config.smfFormat = formatCombo->currentIndex(); MusEGlobal::config.copyright = copyrightEdit->text(); MusEGlobal::config.expRunningStatus = runningStatus->isChecked(); MusEGlobal::config.expOptimNoteOffs = optNoteOffs->isChecked(); MusEGlobal::config.exp2ByteTimeSigs = twoByteTimeSigs->isChecked(); MusEGlobal::config.importMidiSplitParts = splitPartsCheckBox->isChecked(); // Obsolete. There is only 'New' drum tracks now. // MusEGlobal::config.importMidiNewStyleDrum = newDrumsCheckbox->isChecked(); MusEGlobal::config.importDevNameMetas = importDevNameMetas->isChecked(); MusEGlobal::config.importInstrNameMetas = importInstrNameMetas->isChecked(); MusEGlobal::config.exportPortDeviceSMF0 = exportPortDeviceSMF0->isChecked(); MusEGlobal::config.exportDrumMapOverrides = drumMapOverrides->isChecked(); MusEGlobal::config.exportChannelOverridesToNewTrack = channelOverridesToNewTrack->isChecked(); MusEGlobal::config.exportPortsDevices = 0; if(exportPortMetas->isChecked()) MusEGlobal::config.exportPortsDevices |= MusEGlobal::PORT_NUM_META; if(exportDeviceNameMetas->isChecked()) MusEGlobal::config.exportPortsDevices |= MusEGlobal::DEVICE_NAME_META; MusEGlobal::config.exportModeInstr = 0; if(exportModeSysexes->isChecked()) MusEGlobal::config.exportModeInstr |= MusEGlobal::MODE_SYSEX; if(exportInstrumentNames->isChecked()) MusEGlobal::config.exportModeInstr |= MusEGlobal::INSTRUMENT_NAME_META; // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); // write config file close(); } //--------------------------------------------------------- // cancelClicked //--------------------------------------------------------- void MidiFileConfig::cancelClicked() { close(); } } // namespace MusEGui namespace MusEGlobal { //--------------------------------------------------------- // write //--------------------------------------------------------- void StripConfig::write(int level, MusECore::Xml& xml) const { if(_serial < 0) return; // Do NOT save if there is no corresponding track. const MusECore::TrackList* tl = song->tracks(); const int idx = tl->indexOfSerial(_serial); if(idx < 0) return; xml.nput(level, "= 0) xml.nput(level, " width=\"%d\"", _width); xml.put(" />"); //xml.put(">"); //level++; // TODO: Anything else to add? ... //xml.etag(level, "StripConfig"); } //--------------------------------------------------------- // read //--------------------------------------------------------- void StripConfig::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: xml.unknown("StripConfig"); break; case MusECore::Xml::Attribut: if (tag == "trackIdx") { _tmpFileIdx = xml.s2().toInt(); } else if (tag == "visible") { _visible = xml.s2().toInt(); } else if (tag == "width") { _width = xml.s2().toInt(); } break; case MusECore::Xml::TagEnd: if (tag == "StripConfig") return; default: break; } } } //--------------------------------------------------------- // write //--------------------------------------------------------- void MixerConfig::write(int level, MusECore::Xml& xml, bool global) const { xml.tag(level++, "Mixer"); xml.strTag(level, "name", name); xml.qrectTag(level, "geometry", geometry); xml.intTag(level, "showMidiTracks", showMidiTracks); xml.intTag(level, "showDrumTracks", showDrumTracks); xml.intTag(level, "showNewDrumTracks", showNewDrumTracks); xml.intTag(level, "showInputTracks", showInputTracks); xml.intTag(level, "showOutputTracks", showOutputTracks); xml.intTag(level, "showWaveTracks", showWaveTracks); xml.intTag(level, "showGroupTracks", showGroupTracks); xml.intTag(level, "showAuxTracks", showAuxTracks); xml.intTag(level, "showSyntiTracks", showSyntiTracks); xml.intTag(level, "displayOrder", displayOrder); // Specific to song file. if(!global) { if(!stripConfigList.empty()) { const int sz = stripConfigList.size(); for(int i = 0; i < sz; ++i) stripConfigList.at(i).write(level, xml); } } xml.etag(level, "Mixer"); } //--------------------------------------------------------- // read //--------------------------------------------------------- void MixerConfig::read(MusECore::Xml& xml) { bool ignore_next_visible = false; for (;;) { MusECore::Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "name") name = xml.parse1(); else if (tag == "geometry") geometry = readGeometry(xml, tag); else if (tag == "showMidiTracks") showMidiTracks = xml.parseInt(); else if (tag == "showDrumTracks") showDrumTracks = xml.parseInt(); else if (tag == "showNewDrumTracks") showNewDrumTracks = xml.parseInt(); else if (tag == "showInputTracks") showInputTracks = xml.parseInt(); else if (tag == "showOutputTracks") showOutputTracks = xml.parseInt(); else if (tag == "showWaveTracks") showWaveTracks = xml.parseInt(); else if (tag == "showGroupTracks") showGroupTracks = xml.parseInt(); else if (tag == "showAuxTracks") showAuxTracks = xml.parseInt(); else if (tag == "showSyntiTracks") showSyntiTracks = xml.parseInt(); else if (tag == "displayOrder") displayOrder = (DisplayOrder)xml.parseInt(); // Obsolete. Support old songs. else if (tag == "StripName") { const QString s = xml.parse1(); // Protection from duplicates in song file, observed (old flaws?). if(stripOrder.contains(s)) ignore_next_visible = true; else stripOrder.append(s); } // Obsolete. Support old songs. else if (tag == "StripVisible") { // Protection from duplicates in song file, observed (old flaws?). if(ignore_next_visible) { xml.parseInt(); ignore_next_visible = false; } else { stripVisibility.append(xml.parseInt() == 0 ? false : true ); } } else if (tag == "StripConfig") { StripConfig sc; sc.read(xml); if(sc._tmpFileIdx >= 0) stripConfigList.append(sc); } else xml.unknown("Mixer"); break; case MusECore::Xml::TagEnd: if (tag == "Mixer") return; default: break; } } } } // namespace MusEGlobal muse-4.0.0/src/muse/conf.h000066400000000000000000000042161404105311000153120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: conf.h,v 1.4.2.1 2006/09/28 19:22:25 spamatica Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CONF_H__ #define __CONF_H__ #include "components/ui_configmidifilebase.h" #include #include "xml.h" // REMOVE Tim. newdrums. Added. // Adds the ability to override at instrument level. // But it just makes things too complex for the user. // And in a way is unnecessary and overkill, since we // already allow modifying an instrument. //#define _USE_INSTRUMENT_OVERRIDES_ namespace MusEGui { //--------------------------------------------------------- // MidiFileConfig // config properties of exported midi files //--------------------------------------------------------- class MidiFileConfig : public QDialog, public Ui::ConfigMidiFileBase { Q_OBJECT private slots: void okClicked(); void cancelClicked(); public: MidiFileConfig(QWidget* parent=0); void updateValues(); }; } // namespace MusEGui namespace MusECore { extern bool readConfiguration(); extern bool readConfiguration(const char *configFile); extern void readConfiguration(Xml& xml, bool doReadMidiPortConfig, bool doReadGlobalConfig); extern void writeConfigurationColors(int level, MusECore::Xml& xml, bool partColorNames = true); } #endif muse-4.0.0/src/muse/controlfifo.cpp000066400000000000000000000044151404105311000172450ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "controlfifo.h" namespace MusECore { //--------------------------------------------------------- // ControlFifo // put // return true on fifo overflow //--------------------------------------------------------- bool ControlFifo::put(const ControlEvent& event) { if (size < CONTROL_FIFO_SIZE) { fifo[wIndex] = event; wIndex = (wIndex + 1) % CONTROL_FIFO_SIZE; ++size; return false; } return true; } //--------------------------------------------------------- // get //--------------------------------------------------------- ControlEvent ControlFifo::get() { ControlEvent event(fifo[rIndex]); rIndex = (rIndex + 1) % CONTROL_FIFO_SIZE; --size; return event; } //--------------------------------------------------------- // peek //--------------------------------------------------------- const ControlEvent& ControlFifo::peek(int n) { int idx = (rIndex + n) % CONTROL_FIFO_SIZE; return fifo[idx]; } //--------------------------------------------------------- // remove //--------------------------------------------------------- void ControlFifo::remove() { rIndex = (rIndex + 1) % CONTROL_FIFO_SIZE; --size; } } // namespace MusECore muse-4.0.0/src/muse/controlfifo.h000066400000000000000000000044471404105311000167170ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __CONTROLFIFO_H__ #define __CONTROLFIFO_H__ #define CONTROL_FIFO_SIZE 8192 namespace MusECore { //--------------------------------------------------------- // ControlEvent // Item struct for ControlFifo. //--------------------------------------------------------- struct ControlEvent { // Unique: Whether the item must not be skipped, even if it has the same // (possibly rounded) frame and index as the previous item. This is mainly for // dssi-vst guis, they require acknowledgment of every message. bool unique; // Whether or not the event is from a synth or effect's own GUI. bool fromGui; unsigned long idx; double value; unsigned long frame; }; //--------------------------------------------------------- // ControlFifo //--------------------------------------------------------- class ControlFifo { ControlEvent fifo[CONTROL_FIFO_SIZE]; volatile int size; int wIndex; int rIndex; public: ControlFifo() { clear(); } bool put(const ControlEvent& event); // returns true on fifo overflow ControlEvent get(); const ControlEvent& peek(int n = 0); void remove(); bool isEmpty() const { return size == 0; } void clear() { size = 0, wIndex = 0, rIndex = 0; } int getSize() const { return size; } }; } // namespace MusECore #endif muse-4.0.0/src/muse/ctrl.cpp000066400000000000000000000772331404105311000156750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ctrl.cpp,v 1.1.2.4 2009/06/10 00:34:59 terminator356 Exp $ // // controller handling for mixer automation // // (C) Copyright 2003 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2013 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // Turn on debugging messages //#define _CTRL_DEBUG_ #include #include #include "muse_math.h" #include "gconfig.h" #include "fastlog.h" #include "globals.h" #include "ctrl.h" #include "midictrl.h" // Forwards from header: #include "xml.h" namespace MusECore { void CtrlList::initColor(int i) { QColor collist[] = { Qt::red, Qt::yellow, Qt::blue , Qt::black, Qt::white, Qt::green }; if (i < 6) _displayColor = collist[i%6]; else _displayColor = Qt::green; _visible = false; } //--------------------------------------------------------- // midi2AudioCtrlValue // Apply mapper if it is non-null //--------------------------------------------------------- double midi2AudioCtrlValue(const CtrlList* audio_ctrl_list, const MidiAudioCtrlStruct* /*mapper*/, int midi_ctlnum, int midi_val) { double fmin, fmax; audio_ctrl_list->range(&fmin, &fmax); double frng = fmax - fmin; // The audio control range. MidiController::ControllerType t = midiControllerType(midi_ctlnum); CtrlValueType aud_t = audio_ctrl_list->valueType(); #ifdef _CTRL_DEBUG_ printf("midi2AudioCtrlValue: midi_ctlnum:%d val:%d fmin:%f fmax:%f\n", midi_ctlnum, midi_val, fmin, fmax); #endif int ctlmn = 0; int ctlmx = 127; int bval = midi_val; switch(t) { case MidiController::RPN: case MidiController::NRPN: case MidiController::Controller7: ctlmn = 0; ctlmx = 127; break; case MidiController::Controller14: case MidiController::RPN14: case MidiController::NRPN14: ctlmn = 0; ctlmx = 16383; break; case MidiController::Program: ctlmn = 0; ctlmx = 0xffffff; break; case MidiController::Pitch: ctlmn = -8192; ctlmx = 8191; bval += 8192; break; case MidiController::Velo: // cannot happen default: break; } double fictlrng = double(ctlmx - ctlmn); // Float version of the integer midi range. double normval = double(bval) / fictlrng; // Float version of the normalized midi value. // ---------- TODO: Do stuff with the mapper, if supplied. if(aud_t == VAL_LOG) { // FIXME: Although this should be correct, some sliders show "---" at top end, some don't. // Possibly because of use of fast_log10 in value(), and in sliders and automation IIRC. fmin = 20.0*log10(fmin); fmax = 20.0*log10(fmax); frng = fmax - fmin; double ret = exp10((normval * frng + fmin) / 20.0); #ifdef _CTRL_DEBUG_ printf("midi2AudioCtrlValue: is VAL_LOG normval:%f frng:%f returning:%f\n", normval, frng, ret); #endif return ret; } if(aud_t == VAL_LINEAR) { double ret = normval * frng + fmin; #ifdef _CTRL_DEBUG_ printf("midi2AudioCtrlValue: is VAL_LINEAR normval:%f frng:%f returning:%f\n", normval, frng, ret); #endif return ret; } if(aud_t == VAL_INT) { double ret = int(normval * frng + fmin); #ifdef _CTRL_DEBUG_ printf("midi2AudioCtrlValue: is VAL_INT returning:%f\n", ret); #endif return ret; } if(aud_t == VAL_BOOL) { #ifdef _CTRL_DEBUG_ printf("midi2AudioCtrlValue: is VAL_BOOL\n"); #endif //if(midi_val > ((ctlmx - ctlmn)/2 + ctlmn)) if((normval * frng + fmin) > (frng/2.0 + fmin)) return fmax; else return fmin; } printf("midi2AudioCtrlValue: unknown audio controller type:%d\n", aud_t); return 0.0; } //--------------------------------------------------------- // Midi to audio controller stuff //--------------------------------------------------------- MidiAudioCtrlStruct::MidiAudioCtrlStruct() { _audio_ctrl_id = 0; }; MidiAudioCtrlStruct::MidiAudioCtrlStruct(int audio_ctrl_id) : _audio_ctrl_id(audio_ctrl_id) { }; MidiAudioCtrlMap_idx_t MidiAudioCtrlMap::index_hash(int midi_port, int midi_chan, int midi_ctrl_num) const { return ((MidiAudioCtrlMap_idx_t(midi_port) & 0xff) << 24) | ((MidiAudioCtrlMap_idx_t(midi_chan) & 0xf) << 20) | (MidiAudioCtrlMap_idx_t(midi_ctrl_num) & 0xfffff); } void MidiAudioCtrlMap::hash_values(MidiAudioCtrlMap_idx_t hash, int* midi_port, int* midi_chan, int* midi_ctrl_num) const { if(midi_ctrl_num) *midi_ctrl_num = hash & 0xfffff; if(midi_chan) *midi_chan = (hash >> 20) & 0xf; if(midi_port) *midi_port = (hash >> 24) & 0xff; } iMidiAudioCtrlMap MidiAudioCtrlMap::add_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, const MidiAudioCtrlStruct& macs) { MidiAudioCtrlMap_idx_t h = index_hash(midi_port, midi_chan, midi_ctrl_num); std::pair range = equal_range(h); for(iMidiAudioCtrlMap imacp = range.first; imacp != range.second; ++imacp) if(imacp->second.audioCtrlId() == macs.audioCtrlId()) return imacp; return insert(std::pair(h, macs)); } void MidiAudioCtrlMap::erase_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, int audio_ctrl_id) { MidiAudioCtrlMap_idx_t h = index_hash(midi_port, midi_chan, midi_ctrl_num); std::pair range = equal_range(h); MidiAudioCtrlMap macm; macm.insert(range.first, range.second); for(iMidiAudioCtrlMap imacm = macm.begin(); imacm != macm.end(); ++imacm) if(imacm->second.audioCtrlId() == audio_ctrl_id) erase(imacm); } void MidiAudioCtrlMap::find_audio_ctrl_structs(int audio_ctrl_id, AudioMidiCtrlStructMap* amcs) //const { for(iMidiAudioCtrlMap imacm = begin(); imacm != end(); ++imacm) if(imacm->second.audioCtrlId() == audio_ctrl_id) amcs->push_back(imacm); } void MidiAudioCtrlMap::write(int level, Xml& xml) const { for(ciMidiAudioCtrlMap imacm = begin(); imacm != end(); ++imacm) { int port, chan, mctrl; hash_values(imacm->first, &port, &chan, &mctrl); int actrl = imacm->second.audioCtrlId(); QString s= QString("midiMapper port=\"%1\" ch=\"%2\" mctrl=\"%3\" actrl=\"%4\"") .arg(port) .arg(chan) .arg(mctrl) .arg(actrl); xml.tag(level++, s.toLatin1().constData()); // TODO //const MidiAudioCtrlStruct& macs = imacs->second; //xml.intTag(level, "macs ???", macs.); xml.etag(level--, "midiMapper"); } } //--------------------------------------------------------- // read //--------------------------------------------------------- void MidiAudioCtrlMap::read(Xml& xml) { int port = -1, chan = -1, midi_ctrl = -1; MidiAudioCtrlStruct macs(-1); QLocale loc = QLocale::c(); bool ok; int errcount = 0; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::Attribut: if (tag == "port") { port = loc.toInt(xml.s2(), &ok); if(!ok) { ++errcount; printf("MidiAudioCtrlPortMap::read failed reading port string: %s\n", xml.s2().toLatin1().constData()); } } else if (tag == "ch") { chan = loc.toInt(xml.s2(), &ok); if(!ok) { ++errcount; printf("MidiAudioCtrlPortMap::read failed reading ch string: %s\n", xml.s2().toLatin1().constData()); } } else if (tag == "mctrl") { midi_ctrl = loc.toInt(xml.s2(), &ok); if(!ok) { ++errcount; printf("MidiAudioCtrlPortMap::read failed reading mctrl string: %s\n", xml.s2().toLatin1().constData()); } } else if (tag == "actrl") { macs.setAudioCtrlId(loc.toInt(xml.s2(), &ok)); if(!ok) { ++errcount; printf("MidiAudioCtrlPortMap::read failed reading actrl string: %s\n", xml.s2().toLatin1().constData()); } } else printf("unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagStart: // TODO //if (tag == "???") { // } //else xml.unknown("midiMapper"); break; case Xml::TagEnd: if (xml.s1() == "midiMapper") { if(errcount == 0 && port != -1 && chan != -1 && midi_ctrl != -1 && macs.audioCtrlId() != -1) add_ctrl_struct(port, chan, midi_ctrl, macs); return; } default: break; } } } //--------------------------------------------------------- // CtrlList //--------------------------------------------------------- CtrlList::CtrlList(bool dontShow) { _id = 0; _default = 0.0; _curVal = 0.0; _mode = INTERPOLATE; _dontShow = dontShow; _visible = false; _guiUpdatePending = false; initColor(0); } CtrlList::CtrlList(int id, bool dontShow) { _id = id; _default = 0.0; _curVal = 0.0; _mode = INTERPOLATE; _dontShow = dontShow; _visible = false; _guiUpdatePending = false; initColor(id); } CtrlList::CtrlList(int id, QString name, double min, double max, CtrlValueType v, bool dontShow) { _id = id; _default = 0.0; _curVal = 0.0; _mode = INTERPOLATE; _name = name; _min = min; _max = max; _valueType = v; _dontShow = dontShow; _visible = false; _guiUpdatePending = false; initColor(id); } CtrlList::CtrlList(const CtrlList& l, int flags) { _id = l._id; _valueType = l._valueType; assign(l, flags | ASSIGN_PROPERTIES); } //--------------------------------------------------------- // assign //--------------------------------------------------------- void CtrlList::assign(const CtrlList& l, int flags) { if(flags & ASSIGN_PROPERTIES) { _default = l._default; _curVal = l._curVal; _mode = l._mode; _name = l._name; _min = l._min; _max = l._max; _dontShow = l._dontShow; _displayColor = l._displayColor; _visible = l._visible; } if(flags & ASSIGN_VALUES) { CtrlList_t::operator=(l); // Let map copy the items. _guiUpdatePending = true; } } //--------------------------------------------------------- // getInterpolation // Fills CtrlInterpolate struct for given frame. // cur_val_only means read the current 'manual' value, not from the list even if it is not empty. // CtrlInterpolate member eFrameValid can be false meaning no next value (wide-open, endless). //--------------------------------------------------------- void CtrlList::getInterpolation(unsigned int frame, bool cur_val_only, CtrlInterpolate* interp) { interp->eStop = false; // During processing, control FIFO ring buffers will set this true. if(cur_val_only || empty()) { interp->sFrame = 0; interp->eFrame = 0; interp->eFrameValid = false; interp->sVal = _curVal; interp->eVal = _curVal; interp->doInterp = false; return; } ciCtrl i = upper_bound(frame); // get the index after current frame if (i == end()) // if we are past all items just return the last value { --i; interp->sFrame = 0; interp->eFrame = 0; interp->eFrameValid = false; interp->sVal = i->second.val; interp->eVal = i->second.val; interp->doInterp = false; return; } else if(_mode == DISCRETE) { if(i == begin()) { interp->sFrame = 0; interp->eFrame = i->second.frame; interp->eFrameValid = true; interp->sVal = i->second.val; interp->eVal = i->second.val; interp->doInterp = false; } else { interp->eFrame = i->second.frame; interp->eFrameValid = true; interp->eVal = i->second.val; --i; interp->sFrame = i->second.frame; interp->sVal = i->second.val; interp->doInterp = false; } } else // INTERPOLATE { if(i == begin()) { interp->sFrame = 0; interp->eFrame = i->second.frame; interp->eFrameValid = true; interp->sVal = i->second.val; interp->eVal = i->second.val; interp->doInterp = false; } else { interp->eFrame = i->second.frame; interp->eFrameValid = true; interp->eVal = i->second.val; --i; interp->sFrame = i->second.frame; interp->sVal = i->second.val; interp->doInterp = (interp->eVal != interp->sVal && interp->eFrame > interp->sFrame); } } } //--------------------------------------------------------- // interpolate // Returns interpolated value at given frame, from a CtrlInterpolate struct. // For speed, no checking is done for frame = frame2, val1 = val2 or even CtrlInterpolate::doInterp. // Those are to be taken care of before calling this routine. See getInterpolation(). //--------------------------------------------------------- double CtrlList::interpolate(unsigned int frame, const CtrlInterpolate& interp) { const unsigned int frame1 = interp.sFrame; const unsigned int frame2 = interp.eFrame; double val1 = interp.sVal; double val2 = interp.eVal; if(!interp.eFrameValid || frame >= frame2) { if(_valueType == VAL_LOG) { const double min = exp10(MusEGlobal::config.minSlider / 20.0); // TODO Try fastexp10 if(val2 < min) val2 = min; } return val2; } if(frame <= frame1) { if(_valueType == VAL_LOG) { const double min = exp10(MusEGlobal::config.minSlider / 20.0); // TODO Try fastexp10 if(val1 < min) val1 = min; } return val1; } if(_valueType == VAL_LOG) { val1 = 20.0*fast_log10(val1); if (val1 < MusEGlobal::config.minSlider) val1=MusEGlobal::config.minSlider; val2 = 20.0*fast_log10(val2); if (val2 < MusEGlobal::config.minSlider) val2=MusEGlobal::config.minSlider; } val2 -= val1; val1 += (double(frame - frame1) * val2) / double(frame2 - frame1); if (_valueType == VAL_LOG) val1 = exp10(val1/20.0); return val1; } //--------------------------------------------------------- // value // Returns value at frame. // cur_val_only means read the current 'manual' value, not from the list even if it is not empty. // If passed a nextFrame, sets nextFrame to the next event frame, or 0 and eFrameValid false if no next frame (wide-open), or, // since CtrlList is a map, ZERO if should be replaced with some other frame by the caller (interpolation). //--------------------------------------------------------- double CtrlList::value(unsigned int frame, bool cur_val_only, unsigned int* nextFrame, bool* nextFrameValid) const { if(cur_val_only || empty()) { if(nextFrameValid) *nextFrameValid = false; if(nextFrame) *nextFrame = 0; return _curVal; } double rv; unsigned int nframe; ciCtrl i = upper_bound(frame); // get the index after current frame if (i == end()) { // if we are past all items just return the last value --i; if(nextFrameValid) *nextFrameValid = false; if(nextFrame) *nextFrame = 0; return i->second.val; } else if(_mode == DISCRETE) { if(i == begin()) { nframe = i->second.frame; rv = i->second.val; } else { nframe = i->second.frame; --i; rv = i->second.val; } } else { // INTERPOLATE if (i == begin()) { nframe = i->second.frame; rv = i->second.val; } else { const unsigned int frame2 = i->second.frame; double val2 = i->second.val; --i; const unsigned int frame1 = i->second.frame; double val1 = i->second.val; if(val2 != val1) nframe = 0; // Zero signifies the next frame should be determined by caller. else nframe = frame2; if (_valueType == VAL_LOG) { val1 = 20.0*fast_log10(val1); if (val1 < MusEGlobal::config.minSlider) val1=MusEGlobal::config.minSlider; val2 = 20.0*fast_log10(val2); if (val2 < MusEGlobal::config.minSlider) val2=MusEGlobal::config.minSlider; } val2 -= val1; val1 += (double(frame - frame1) * val2)/double(frame2 - frame1); if (_valueType == VAL_LOG) { val1 = exp10(val1/20.0); } rv = val1; } } if(nextFrame) *nextFrame = nframe; if(nextFrameValid) *nextFrameValid = true; return rv; } //--------------------------------------------------------- // curVal // returns the static 'manual' value //--------------------------------------------------------- double CtrlList::curVal() const { return _curVal; } //--------------------------------------------------------- // setCurVal // Sets the static 'manual' value //--------------------------------------------------------- void CtrlList::setCurVal(double val) { #ifdef _CTRL_DEBUG_ printf("CtrlList::setCurVal val:%f\n", val); #endif bool upd = (val != _curVal); _curVal = val; // If empty, any controller graphs etc. will be displaying this value. // Otherwise they'll be displaying the list, so update is not required. if(empty() && upd) _guiUpdatePending = true; } //--------------------------------------------------------- // // Catch all insert, erase, clear etc. // //--------------------------------------------------------- CtrlList& CtrlList::operator=(const CtrlList& cl) { #ifdef _CTRL_DEBUG_ printf("CtrlList::operator= id:%d\n", cl.id()); #endif _id = cl._id; _default = cl._default; _curVal = cl._curVal; _mode = cl._mode; _name = cl._name; _min = cl._min; _max = cl._max; _valueType = cl._valueType; _dontShow = cl._dontShow; _displayColor = cl._displayColor; _visible = cl._visible; // Let map copy the items. CtrlList_t::operator=(cl); _guiUpdatePending = true; return *this; } void CtrlList::swap(CtrlList& cl) { #ifdef _CTRL_DEBUG_ printf("CtrlList::swap id:%d\n", cl.id()); #endif CtrlList_t::swap(cl); cl.setGuiUpdatePending(true); _guiUpdatePending = true; } std::pair CtrlList::insert(const CtrlListInsertPair_t& p) { #ifdef _CTRL_DEBUG_ printf("CtrlList::insert frame:%u val:%f\n", p.first, p.second.val); #endif std::pair res = CtrlList_t::insert(p); _guiUpdatePending = true; return res; } iCtrl CtrlList::insert(iCtrl ic, const CtrlListInsertPair_t& p) { #ifdef _CTRL_DEBUG_ printf("CtrlList::insert2 frame:%u val:%f\n", p.first, p.second.val); #endif iCtrl res = CtrlList_t::insert(ic, p); _guiUpdatePending = true; return res; } void CtrlList::insert(iCtrl first, iCtrl last) { #ifdef _CTRL_DEBUG_ printf("CtrlList::insert3 first frame:%u last frame:%d\n", first->first, last->first); #endif CtrlList_t::insert(first, last); _guiUpdatePending = true; } void CtrlList::erase(iCtrl ictl) { #ifdef _CTRL_DEBUG_ printf("CtrlList::erase iCtrl frame:%u val:%f\n", ictl->second.frame, ictl->second.val); #endif CtrlList_t::erase(ictl); _guiUpdatePending = true; } CtrlList_t::size_type CtrlList::erase(unsigned int frame) { #ifdef _CTRL_DEBUG_ printf("CtrlList::erase frame:%u\n", frame); #endif size_type res = CtrlList_t::erase(frame); _guiUpdatePending = true; return res; } void CtrlList::erase(iCtrl first, iCtrl last) { #ifdef _CTRL_DEBUG_ printf("CtrlList::erase range first frame:%u val:%f second frame:%u val:%f\n", first->second.frame, first->second.val, last->second.frame, last->second.val); #endif CtrlList_t::erase(first, last); _guiUpdatePending = true; } void CtrlList::clear() { #ifdef _CTRL_DEBUG_ printf("CtrlList::clear\n"); #endif CtrlList_t::clear(); _guiUpdatePending = true; } //--------------------------------------------------------- // add // Add, or replace, an event at time frame having value val. //--------------------------------------------------------- void CtrlList::add(unsigned int frame, double val) { iCtrl e = find(frame); if (e != end()) { bool upd = (val != e->second.val); e->second.val = val; #ifdef _CTRL_DEBUG_ printf("CtrlList::add frame:%u val:%f\n", frame, val); #endif if(upd) _guiUpdatePending = true; } else insert(CtrlListInsertPair_t(frame, CtrlVal(frame, val))); } //--------------------------------------------------------- // del //--------------------------------------------------------- void CtrlList::del(unsigned int frame) { iCtrl e = find(frame); if (e == end()) return; erase(e); } //--------------------------------------------------------- // updateCurValues // Set the current static 'manual' value (non-automation value) // from the automation value at the given time. //--------------------------------------------------------- void CtrlList::updateCurValue(unsigned int frame) { double v = value(frame); bool upd = (v != _curVal); _curVal = v; // If empty, any controller graphs etc. will be displaying this value. // Otherwise they'll be displaying the list, so update is not required. if(empty() && upd) _guiUpdatePending = true; } //--------------------------------------------------------- // read //--------------------------------------------------------- void CtrlList::read(Xml& xml) { QLocale loc = QLocale::c(); bool ok; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::Attribut: if (tag == "id") { _id = loc.toInt(xml.s2(), &ok); if(!ok) printf("CtrlList::read failed reading _id string: %s\n", xml.s2().toLatin1().constData()); } else if (tag == "cur") { _curVal = loc.toDouble(xml.s2(), &ok); if(!ok) printf("CtrlList::read failed reading _curVal string: %s\n", xml.s2().toLatin1().constData()); } else if (tag == "visible") { _visible = loc.toInt(xml.s2(), &ok); if(!ok) printf("CtrlList::read failed reading _visible string: %s\n", xml.s2().toLatin1().constData()); } else if (tag == "color") { ok = _displayColor.isValidColor(xml.s2()); if (!ok) { printf("CtrlList::read failed reading color string: %s\n", xml.s2().toLatin1().constData()); break; } _displayColor.setNamedColor(xml.s2()); } else printf("unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::Text: { int len = tag.length(); unsigned int frame; double val; int i = 0; for(;;) { while(i < len && (tag[i] == ',' || tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; QString fs; while(i < len && tag[i] != ' ') { fs.append(tag[i]); ++i; } if(i == len) break; frame = loc.toUInt(fs, &ok); if(!ok) { printf("CtrlList::read failed reading frame string: %s\n", fs.toLatin1().constData()); break; } while(i < len && (tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; QString vs; while(i < len && tag[i] != ' ' && tag[i] != ',') { vs.append(tag[i]); ++i; } val = loc.toDouble(vs, &ok); if(!ok) { printf("CtrlList::read failed reading value string: %s\n", vs.toLatin1().constData()); break; } // For now, the conversion only has a TEMPORARY effect during song loading. // See comments in Song::read at the "samplerate" tag. add(MusEGlobal::convertFrame4ProjectSampleRate(frame), val); if(i == len) break; } } break; case Xml::TagEnd: if (xml.s1() == "controller") return; default: break; } } } //--------------------------------------------------------- // add //--------------------------------------------------------- void CtrlListList::add(CtrlList* vl) { insert(std::pair(vl->id(), vl)); } //--------------------------------------------------------- // value // Returns value at frame for controller with id ctrlId. // cur_val_only means read the current 'manual' value, not from the list even if it is not empty. // If passed a nextFrame, sets nextFrame to the next event frame, or 0 and eFrameValid false if no next frame (wide-open), or, // since CtrlList is a map, ZERO if should be replaced with some other frame by the caller (interpolation). //--------------------------------------------------------- double CtrlListList::value(int ctrlId, unsigned int frame, bool cur_val_only, unsigned int* nextFrame, bool* nextFrameValid) const { ciCtrlList cl = find(ctrlId); if (cl == end()) { if(nextFrameValid) *nextFrameValid = false; if(nextFrame) *nextFrame = 0; return 0.0; } return cl->second->value(frame, cur_val_only, nextFrame, nextFrameValid); } //--------------------------------------------------------- // updateCurValues // Set the current 'manual' values (non-automation values) // from the automation values at the given time. // This is typically called right after a track's automation type changes // to OFF, so that the manual value becomes the last automation value. // There are some interesting advantages to having completely independent // 'manual' and automation values, but the jumping around when switching to OFF // becomes disconcerting. //--------------------------------------------------------- void CtrlListList::updateCurValues(unsigned int frame) { for(ciCtrlList cl = begin(); cl != end(); ++cl) cl->second->updateCurValue(frame); } //--------------------------------------------------------- // value //--------------------------------------------------------- void CtrlListList::write(int level, Xml& xml) const { for (ciCtrlList icl = begin(); icl != end(); ++icl) { const CtrlList* cl = icl->second; QString s= QString("controller id=\"%1\" cur=\"%2\"").arg(cl->id()).arg(cl->curVal()); s += QString(" color=\"%1\" visible=\"%2\"").arg(cl->color().name()).arg(cl->isVisible()); xml.tag(level++, s.toLatin1().constData()); int i = 0; for (ciCtrl ic = cl->begin(); ic != cl->end(); ++ic) { QString s("%1 %2, "); xml.nput(level, s.arg(ic->second.frame).arg(ic->second.val).toLatin1().constData()); ++i; if (i >= 4) { xml.put(level, ""); i = 0; } } if (i) xml.put(level, ""); xml.etag(level--, "controller"); } _midi_controls.write(level, xml); } } // namespace MusECore muse-4.0.0/src/muse/ctrl.h000066400000000000000000000272571404105311000153430ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ctrl.h,v 1.4.2.2 2006/10/29 07:54:51 terminator356 Exp $ // // controller for mixer automation // // (C) Copyright 2003-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2013 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CTRL_H__ #define __CTRL_H__ #include #include #include #include #include #ifdef OSC_SUPPORT #include #else #include #endif #define AC_PLUGIN_CTL_BASE 0x1000 #define AC_PLUGIN_CTL_BASE_POW 12 #define AC_PLUGIN_CTL_ID_MASK 0xFFF namespace MusECore { // Forward declarations: class Xml; const int AC_VOLUME = 0; const int AC_PAN = 1; const int AC_MUTE = 2; inline unsigned long genACnum(unsigned long plugin, unsigned long ctrl) { return (plugin + 1) * AC_PLUGIN_CTL_BASE + ctrl; } enum CtrlValueType { VAL_LOG, VAL_LINEAR, VAL_INT, VAL_BOOL, VAL_ENUM }; enum CtrlRecValueType { ARVT_VAL, ARVT_START, ARVT_STOP }; typedef std::map CtrlEnumValues; //--------------------------------------------------------- // CtrlInterpolate // Controller interpolation values. // For speed: Can be filled once by CtrlList::getInterpolation(), // then passed repeatedly to CtrlList::interpolate(). //--------------------------------------------------------- struct CtrlInterpolate { unsigned int sFrame; // Starting frame. Always valid. Can be less than any first CtrlList item's frame, or zero ! double sVal; // Value at starting frame. unsigned int eFrame; // Ending frame if eFrameValid is true. bool eFrameValid; // True if eFrame is valid. False if endless, eFrame is invalid. double eVal; // Value at ending frame, or sVal if eFrameValid is false. bool eStop; // Whether to stop refreshing this struct from CtrlList upon eFrame. Control FIFO ring buffers // set this true and replace eFrame and eVal. Upon the next run slice, if eStop is set, eval // should be copied to sVal, eFrame to sFrame, doInterp cleared, and eFrame set to some frame or eFrameValid false. bool doInterp; // Whether to actually interpolate whenever this struct is passed to CtrlList::interpolate(). CtrlInterpolate(unsigned int sframe = 0, unsigned int eframe = 0, bool eframevalid = false, double sval = 0.0, double eval = 0.0, bool end_stop = false, bool do_interpolate = false) { sFrame = sframe; sVal = sval; eFrame = eframe; eFrameValid = eframevalid; eVal = eval; eStop = end_stop; doInterp = do_interpolate; } }; //--------------------------------------------------------- // CtrlVal // controller "event" //--------------------------------------------------------- struct CtrlVal { unsigned int frame; double val; CtrlVal(unsigned int f, double v) { frame = f; val = v; } }; //--------------------------------------------------------- // CtrlRecVal // recorded controller event, mixer automation //--------------------------------------------------------- struct CtrlRecVal : public CtrlVal { int id; CtrlRecValueType type; // 0 - ctrlVal, 1 - start, 2 - end CtrlRecVal(unsigned int f, int n, double v) : CtrlVal(f, v), id(n), type(ARVT_VAL) {} CtrlRecVal(unsigned int f, int n, double v, CtrlRecValueType t) : CtrlVal(f, v), id(n), type(t) {} }; //--------------------------------------------------------- // CtrlRecList //--------------------------------------------------------- class CtrlRecList : public std::list { public: }; typedef CtrlRecList::iterator iCtrlRec; typedef CtrlRecList::const_iterator ciCtrlRec; //--------------------------------------------------------- // MidiAudioCtrlMap // Describes midi control of audio controllers //--------------------------------------------------------- class MidiAudioCtrlStruct { int _audio_ctrl_id; public: MidiAudioCtrlStruct(); MidiAudioCtrlStruct(int audio_ctrl_id); int audioCtrlId() const { return _audio_ctrl_id; } void setAudioCtrlId(int actrl) { _audio_ctrl_id = actrl; } }; typedef uint32_t MidiAudioCtrlMap_idx_t; typedef std::multimap >::iterator iMidiAudioCtrlMap; typedef std::multimap >::const_iterator ciMidiAudioCtrlMap; // Reverse lookup based on audio control. typedef std::vector::iterator iAudioMidiCtrlStructMap; typedef std::vector::const_iterator ciAudioMidiCtrlStructMap; class AudioMidiCtrlStructMap : public std::vector { public: }; // Midi to audio controller map. // The index is a hash of port, chan, and midi control number. class MidiAudioCtrlMap : public std::multimap > { public: MidiAudioCtrlMap_idx_t index_hash(int midi_port, int midi_chan, int midi_ctrl_num) const; void hash_values(MidiAudioCtrlMap_idx_t hash, int* midi_port, int* midi_chan, int* midi_ctrl_num) const; iMidiAudioCtrlMap add_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, const MidiAudioCtrlStruct& amcs); void find_audio_ctrl_structs(int audio_ctrl_id, AudioMidiCtrlStructMap* amcs); // const; void erase_ctrl_struct(int midi_port, int midi_chan, int midi_ctrl_num, int audio_ctrl_id); void write(int level, Xml& xml) const; void read(Xml& xml); }; //--------------------------------------------------------- // CtrlList // arrange controller events of a specific type in a // list for easy retrieval //--------------------------------------------------------- typedef std::map > CtrlList_t; typedef std::pair CtrlListInsertPair_t; class CtrlList : public CtrlList_t { public: enum Mode { INTERPOLATE, DISCRETE}; enum AssignFlags { ASSIGN_PROPERTIES=1, ASSIGN_VALUES=2 }; // Can be or'd together. private: Mode _mode; int _id; double _default; double _curVal; void del(CtrlVal); QString _name; double _min, _max; CtrlValueType _valueType; QColor _displayColor; bool _visible; bool _dontShow; // when this is true the control exists but is not compatible with viewing in the arranger volatile bool _guiUpdatePending; // Gui heartbeat routines read this. Checked and cleared in Song::beat(). void initColor(int i); public: CtrlList(bool dontShow=false); CtrlList(int id, bool dontShow=false); CtrlList(int id, QString name, double min, double max, CtrlValueType v, bool dontShow=false); CtrlList(const CtrlList& l, int flags); void assign(const CtrlList& l, int flags); void swap(CtrlList&); std::pair insert(const CtrlListInsertPair_t& p); iterator insert(iterator ic, const CtrlListInsertPair_t& p); void insert(iterator first, iterator last); void erase(iterator ictl); size_type erase(unsigned int frame); void erase(iterator first, iterator last); void clear(); CtrlList& operator=(const CtrlList&); Mode mode() const { return _mode; } void setMode(Mode m) { _mode = m; } double getDefault() const { return _default; } void setDefault(double val) { _default = val; } double curVal() const; void updateCurValue(unsigned int frame); void setCurVal(double val); int id() const { return _id; } QString name() const { return _name; } void setName(const QString& s) { _name = s; } double minVal() const { return _min; } double maxVal() const { return _max; } void setRange(double min, double max) { _min = min; _max = max; } void range(double* min, double* max) const { *min = _min; *max = _max; } CtrlValueType valueType() const { return _valueType; } void setValueType(CtrlValueType t) { _valueType = t; } void getInterpolation(unsigned int frame, bool cur_val_only, CtrlInterpolate* interp); double interpolate(unsigned int frame, const CtrlInterpolate& interp); double value(unsigned int frame, bool cur_val_only = false, unsigned int* nextFrame = NULL, bool* nextFrameValid = NULL) const; void add(unsigned int frame, double value); void del(unsigned int frame); void read(Xml& xml); void setColor( QColor c ) { _displayColor = c;} QColor color() const { return _displayColor; } void setVisible(bool v) { _visible = v; } bool isVisible() const { return _visible; } bool dontShow() const { return _dontShow; } bool guiUpdatePending() const { return _guiUpdatePending; } void setGuiUpdatePending(bool v) { _guiUpdatePending = v; } }; typedef CtrlList::iterator iCtrl; typedef CtrlList::const_iterator ciCtrl; //--------------------------------------------------------- // CtrlListList // List of controller value lists. // This list represents the controller state of a // mixer strip //--------------------------------------------------------- typedef std::map >::iterator iCtrlList; typedef std::map >::const_iterator ciCtrlList; class CtrlListList : public std::map > { private: MidiAudioCtrlMap _midi_controls; // For midi control of audio controllers. public: void add(CtrlList* vl); void clearDelete() { for(iCtrlList i = begin(); i != end(); ++i) delete i->second; clear(); } iCtrlList find(int id) { return std::map >::find(id); } ciCtrlList find(int id) const { return std::map >::find(id); } MidiAudioCtrlMap* midiControls() { return &_midi_controls; } double value(int ctrlId, unsigned int frame, bool cur_val_only = false, unsigned int* nextFrame = NULL, bool* nextFrameValid = NULL) const; void updateCurValues(unsigned int frame); void clearAllAutomation() { for(iCtrlList i = begin(); i != end(); ++i) i->second->clear(); } void write(int level, Xml& xml) const; }; extern double midi2AudioCtrlValue(const CtrlList* audio_ctrl_list, const MidiAudioCtrlStruct* mapper, int midi_ctlnum, int midi_val); } // namespace MusECore #endif muse-4.0.0/src/muse/ctrl/000077500000000000000000000000001404105311000151555ustar00rootroot00000000000000muse-4.0.0/src/muse/ctrl/CMakeLists.txt000066400000000000000000000040531404105311000177170ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP (ctrl_mocs # configmidictrl.h # ctrldialog.h # definemidictrl.h ctrlcanvas.h ctrledit.h ctrlpanel.h ) ## ## List of source files to compile ## file (GLOB ctrl_source_files ctrlcanvas.cpp ctrledit.cpp ctrlpanel.cpp ) ## ## Define target ## add_library ( ctrl ${MODULES_BUILD} # configmidictrl.cpp # definemidictrl.cpp # ctrldialog.cpp # ctrleditor.cpp ${ctrl_source_files} ${ctrl_mocs} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${ctrl_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( ctrl PROPERTIES OUTPUT_NAME muse_ctrl ) ## ## Linkage ## target_link_libraries ( ctrl ${QT_LIBRARIES} widgets ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS ctrl DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/ctrl/ctrlcanvas.cpp000066400000000000000000003513671404105311000200400ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ctrlcanvas.cpp,v 1.15.2.10 2009/11/14 03:37:48 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include "ctrlcanvas.h" #include "app.h" #include "globals.h" #include "ctrledit.h" #include "midieditor.h" #include "icons.h" #include "midiport.h" #include "song.h" #include "midictrl.h" #include "audio.h" #include "gconfig.h" #include "ctrlpanel.h" #include "midiedit/drummap.h" #include "drumedit.h" #include "drummap.h" #include "functions.h" #include "popupmenu.h" #include "menutitleitem.h" #define ABS(x) ((x) < 0) ? -(x) : (x) static MusECore::MidiCtrlValList veloList(MusECore::CTRL_VELOCITY); // dummy namespace MusEGui { // static const int CtrlCanvas::overlayTextOffsetFromOrg = 2; //--------------------------------------------------------- // computeVal //--------------------------------------------------------- static int computeVal(MusECore::MidiController* mc, int y, int height) { int min; int max; if(mc->num() == MusECore::CTRL_PROGRAM) { min = 1; max = 128; } else { min = mc->minVal(); max = mc->maxVal(); } int val = max - (y * (max-min) / height); if (val < min) val = min; if (val > max) val = max; if(mc->num() != MusECore::CTRL_PROGRAM) val += mc->bias(); return val; } //--------------------------------------------------------- // computeY //--------------------------------------------------------- static int computeY(const MusECore::MidiController* mc, int val, int height) { int min; int max; if(mc->num() == MusECore::CTRL_PROGRAM) { min = 1; max = 128; } else { min = mc->minVal(); max = mc->maxVal(); } //printf("computeY #1 min:%d max:%d val:%d bias:%d height:%d\n", min, max, val, mc->bias(), height); if(mc->num() != MusECore::CTRL_PROGRAM) val -= mc->bias(); if (val < min) val = min; if (val > max) val = max; int y = min == max ? 0 : (max-val)*height/(max-min); //printf("computeY #2 y:%d min:%d max:%d val:%d bias:%d height:%d\n", y, min, max, val, mc->bias(), height); return y; } //--------------------------------------------------------- // CEvent //--------------------------------------------------------- CEvent::CEvent() : CItem() { _part = NULL; _val = 0; ex = 0; } CEvent::CEvent(const MusECore::Event& e, MusECore::Part* pt, int v) : CItem() { _part = pt; _event = e; _val = v; ex = !e.empty() ? e.tick() : 0; } bool CEvent::isObjectInRange(const MusECore::Pos& p0, const MusECore::Pos& p1) const { MusECore::Pos pos = _event.pos(); if(_part) pos += (*_part); return pos >= p0 && pos < p1; } //--------------------------------------------------------- // intersects //--------------------------------------------------------- bool CEvent::intersectsController(const MusECore::MidiController* mc, const QRect& r, const int tickstep, const int wh) const { if(_event.empty()) return false; int y1 = computeY(mc, _val, wh); int tick1 = _event.tick() + _part->tick(); if(ex == -1) return tick1 < (r.x() + r.width()) && y1 < (r.y() + r.height()); int tick2 = ex + _part->tick(); // Velocities don't use EX (set equal to event tick, giving zero width here), // and velocities are drawn three pixels wide, so adjust the width now. // Remember, each drawn pixel represents one tickstep which varies with zoom. // So that's 3 x tickstep for each velocity line. // Hmm, actually, for better pin-point accuracy just use one tickstep for now. if(MusECore::midiControllerType(mc->num()) == MusECore::MidiController::Velo) tick2 += tickstep; QRect er(tick1, y1, tick2 - tick1, wh - y1); return r.intersects(er); } bool CEvent::containsPoint(const MusECore::MidiController* mc, const QPoint& p, const int tickstep, const int wh) const { if(_event.empty()) return false; const int y1 = computeY(mc, _val, wh); const int tick1 = _event.tick() + _part->tick(); if(ex == -1) return tick1 <= p.x() && y1 <= p.y(); int tick2 = ex + _part->tick(); // Velocities don't use EX (set equal to event tick, giving zero width here), // and velocities are drawn three pixels wide, so adjust the width now. // Remember, each drawn pixel represents one tickstep which varies with zoom. // So that's 3 x tickstep for each velocity line. // Hmm, actually, for better pin-point accuracy just use one tickstep for now. if(MusECore::midiControllerType(mc->num()) == MusECore::MidiController::Velo) tick2 += tickstep; return p.x() >= tick1 && p.x() < tick2 && y1 <= p.y(); } //--------------------------------------------------------- // contains //--------------------------------------------------------- bool CEvent::containsXRange(int x1, int x2) const { int tick1 = !_event.empty() ? _event.tick() + _part->tick() : 0; if(ex == -1) return (tick1 < x2); int tick2 = ex + _part->tick(); return ((tick1 >= x1 && tick1 < x2) //|| (tick2 >= x1 && tick2 < x2) DELETETHIS? || (tick2 > x1 && tick2 < x2) || (tick1 < x1 && tick2 >= x2)); } //--------------------------------------------------------- // eventWithLength // See header comments about this small Event hack... //--------------------------------------------------------- MusECore::Event CEvent::eventWithLength() const { // Grab a clone of the event. MusECore::Event new_e = _event.clone(); // Synthesize a length value. // This is HACK for copy/paste and the value must be reset // to zero after usage. const unsigned int pos_val = new_e.posValue(); unsigned int len = 0; if(ex >= 0) { if((unsigned int)ex > pos_val) len = ex - pos_val; else // It's an error, but give it a minimum length of 1. len = 1; } new_e.setLenValue(len); return new_e; } //--------------------------------------------------------- // CtrlCanvas //--------------------------------------------------------- CtrlCanvas::CtrlCanvas(MidiEditor* e, QWidget* parent, int xmag, const char* name, CtrlPanel* pnl) : View(parent, xmag, 1, name) { // Reset these since our parent will typically turn them on for speed. // Definitely not static contents. Full repaint desired upon resize // because the contents need to scale. Without this, contents were not // updating on Mint Cinnamon when adjusting the vertical splitters. setAttribute(Qt::WA_StaticContents, false); setStatusTip(tr("Control canvas: Use Pencil tool to edit events and Draw tool to adjust them gradually. Hold Ctrl to affect only existing events.")); if (MusEGlobal::config.canvasBgPixmap.isEmpty()) { setBg(MusEGlobal::config.midiControllerViewBg); setBg(QPixmap()); } else { setBg(QPixmap(MusEGlobal::config.canvasBgPixmap)); } setFocusPolicy(Qt::StrongFocus); _cursorOverrideCount = 0; setFont(MusEGlobal::config.fonts[3]); //_cursorShape = Qt::ArrowCursor; _mouseGrabbed = false; curItem = nullptr; _movingItemUnderCursor = nullptr; editor = e; _panel = pnl; drag = DRAG_OFF; _dragType = MOVE_MOVE; tool = MusEGui::PointerTool; //button = Qt::NoButton; _dragFirstXPos = 0; line1x = line1y = line2x = line2y = 0; drawLineMode = false; _bgAlpha = static_cast(MusEGlobal::config.globalAlphaBlend / 2); // Initialize the position markers. pos[0] = MusEGlobal::song->cPos().tick(); pos[1] = MusEGlobal::song->lPos().tick(); pos[2] = MusEGlobal::song->rPos().tick(); noEvents=false; _perNoteVeloMode = MusEGlobal::config.velocityPerNote; if(_panel) _panel->setVeloPerNoteMode(_perNoteVeloMode); filterTrack=false; ctrl = &veloList; _controller = &MusECore::veloCtrl; _cnum = MusECore::CTRL_VELOCITY; _dnum = MusECore::CTRL_VELOCITY; _didx = MusECore::CTRL_VELOCITY; connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(setPos(int, unsigned, bool))); setMouseTracking(true); curPart = nullptr; curTrack = nullptr; if (!editor->parts()->empty()) setCurTrackAndPart(); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); setCurDrumPitch(editor->curDrumInstrument()); //printf("CtrlCanvas::CtrlCanvas curDrumPitch:%d\n", curDrumPitch); connect(editor, SIGNAL(curDrumInstrumentChanged(int)), SLOT(setCurDrumPitch(int))); updateItems(); } CtrlCanvas::~CtrlCanvas() { // Just in case the ref count is not 0. This is our last chance to clear // our contribution to QApplication::setOverrideCursor references. showCursor(); items.clearDelete(); } //--------------------------------------------------------- // setPanel //--------------------------------------------------------- void CtrlCanvas::setPanel(CtrlPanel* pnl) { _panel = pnl; if(_panel) _panel->setVeloPerNoteMode(_perNoteVeloMode); } //--------------------------------------------------------- // setPos // set one of three markers // idx - 0-cpos 1-lpos 2-rpos // flag - emit followEvent() //--------------------------------------------------------- void CtrlCanvas::setPos(int idx, unsigned val, bool adjustScrollbar) { if (pos[idx] == val) return; int opos = mapx(pos[idx]); int npos = mapx(val); if (adjustScrollbar && idx == 0) { switch (MusEGlobal::song->follow()) { case MusECore::Song::NO: break; case MusECore::Song::JUMP: if (npos >= width()) { int ppos = val - rmapxDev(width()/4); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } else if (npos < 0) { int ppos = val - rmapxDev(width()*3/4); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } break; case MusECore::Song::CONTINUOUS: if (npos > (width()*5)/8) { int ppos = pos[idx] - rmapxDev(width()*5/8); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } else if (npos < (width()*3)/8) { int ppos = pos[idx] - rmapxDev(width()*3/8); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } break; } } int x; int w = 1; if (opos > npos) { w += opos - npos; x = npos; } else { w += npos - opos; x = opos; } pos[idx] = val; redraw(QRect(x, 0, w, height())); } //--------------------------------------------------------- // setMidiController //--------------------------------------------------------- void CtrlCanvas::setMidiController(int num) { _cnum = num; partControllers(curPart, _cnum, &_dnum, &_didx, &_controller, &ctrl, &_ctrlInfo); if(_panel) { if(_cnum == MusECore::CTRL_VELOCITY) _panel->setHWController(curTrack, &MusECore::veloCtrl); else _panel->setHWController(curTrack, _controller); } } //--------------------------------------------------------- // keyPressEvent //--------------------------------------------------------- void CtrlCanvas::keyPressEvent(QKeyEvent *event) { switch(event->key()) { case Qt::Key_Control: _dragType = MOVE_COPY; setCursor(); event->accept(); return; break; case Qt::Key_Escape: if(!moving.empty()) { cancelMouseOps(); setCursor(); event->accept(); return; } break; default: break; } event->ignore(); View::keyPressEvent(event); } //--------------------------------------------------------- // keyReleaseEvent //--------------------------------------------------------- void CtrlCanvas::keyReleaseEvent(QKeyEvent *event) { switch(event->key()) { case Qt::Key_Control: _dragType = MOVE_MOVE; setCursor(); event->accept(); return; break; default: break; } event->ignore(); View::keyReleaseEvent(event); } //--------------------------------------------------------- // enterEvent //--------------------------------------------------------- void CtrlCanvas::enterEvent(QEvent*) { setCursor(); } //--------------------------------------------------------- // leaveEvent //--------------------------------------------------------- void CtrlCanvas::leaveEvent(QEvent*) { emit xposChanged(INT_MAX); emit yposChanged(-1); } //--------------------------------------------------------- // raster //--------------------------------------------------------- QPoint CtrlCanvas::raster(const QPoint& p) const { if(!editor) return p; int x = p.x(); if (x < 0) x = 0; x = editor->rasterVal(x); return QPoint(x, p.y()); } //--------------------------------------------------------- // deselectAll //--------------------------------------------------------- void CtrlCanvas::deselectAll() { // To save time searching the potentially large 'items' list, a selection list is used. for(iCItemList i = selection.begin(); i != selection.end(); ++i) (*i)->setSelected(false); // Removed. Let itemSelectionsChanged() handle it later. //selection.clear(); } //--------------------------------------------------------- // selectItem //--------------------------------------------------------- void CtrlCanvas::selectItem(CEvent* e) { e->setSelected(true); for (iCItemList i = selection.begin(); i != selection.end(); ++i) { if (*i == e) { // It was found in the list. Just return. // It will be selected by now, from setSelected() above. return; } } // It's not in the selection list. Add it now. selection.push_back(e); } //--------------------------------------------------------- // deselectItem //--------------------------------------------------------- void CtrlCanvas::deselectItem(CEvent* e) { e->setSelected(false); // The item cannot be removed yet from the selection list. // Only itemSelectionsChanged() does that. } //--------------------------------------------------------- // deselectSelection //--------------------------------------------------------- void CtrlCanvas::removeSelection(CEvent* e) { for (iCItemList i = selection.begin(); i != selection.end(); ++i) { if (*i == e) { selection.erase(i); break; } } } //--------------------------------------------------------- // applyYOffset //--------------------------------------------------------- void CtrlCanvas::applyYOffset(MusECore::Event& e, int yoffset) const { if(!curPart) return; //fprintf(stderr, "min: %d max: %d bias:%d yoffset: %d cur val: %d \n", _ctrlInfo.min, _ctrlInfo.max, _ctrlInfo.bias, yoffset, e.dataB()); // Y offset is top to bottom. Reverse it. int new_v = e.dataB() - yoffset; const int min = _ctrlInfo.min + _ctrlInfo.bias; const int max = _ctrlInfo.max + _ctrlInfo.bias; if(new_v < min) new_v = min; else if(new_v > max) new_v = max; e.setB(new_v); } //--------------------------------------------------------- // tagItems //--------------------------------------------------------- void CtrlCanvas::tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const { if(!curPart) return; const bool tagSelected = options._flags & MusECore::TagSelected; const bool tagMoving = options._flags & MusECore::TagMoving; const bool tagAllItems = options._flags & MusECore::TagAllItems; const bool tagAllParts = options._flags & MusECore::TagAllParts; const bool range = options._flags & MusECore::TagRange; const MusECore::Pos& p0 = options._p0; const MusECore::Pos& p1 = options._p1; // Protect against divide by zero. const int offset_y = rmapyDev(height() == 0 ? 0 : _curDragOffset.y() * (_ctrlInfo.max - _ctrlInfo.min) / height()); MusECore::Event new_e; CEvent* item; MusECore::Part* part; if(range) { if(tagAllItems || tagAllParts) { for(ciCItemList i = items.cbegin(); i != items.cend(); ++i) { item = static_cast(*i); part = item->part(); if(!tagAllParts && (part != curPart || (part && part->track() != curTrack))) continue; if(!(tagAllItems || (tagSelected && item->isSelected()) || (tagMoving && item->isMoving()))) continue; if(item->isObjectInRange(p0, p1)) { // Grab a clone of the event with a synthesized length // representing the visual length of the 'bar' on the graph. // This is a HACK of the Event length for this temporary purpose. // Normally the length is always zero for controllers. // The caller (xml copy/paste routines etc.) resets this back to zero // upon reception of the event when it is done with the information. new_e = item->eventWithLength(); if(tagMoving && item->isMoving()) applyYOffset(new_e, offset_y); tag_list->add(part, new_e); } } } else { if(tagSelected) { for(ciCItemList i = selection.cbegin(); i != selection.cend(); ++i) { item = static_cast(*i); part = item->part(); if(part != curPart || (part && part->track() != curTrack)) continue; if(item->isObjectInRange(p0, p1)) { new_e = item->eventWithLength(); tag_list->add(part, new_e); } } } if(tagMoving) { for(ciCItemList i = moving.cbegin(); i != moving.cend(); ++i) { item = static_cast(*i); part = item->part(); if(part != curPart || (part && part->track() != curTrack)) continue; if(item->isObjectInRange(p0, p1)) { // Avoid duplicates found in moving list. if(tagSelected && selection.cfind(item) != selection.cend()) continue; new_e = item->eventWithLength(); applyYOffset(new_e, offset_y); tag_list->add(part, new_e); } } } } } else { if(tagAllItems || tagAllParts) { for(ciCItemList i = items.cbegin(); i != items.cend(); ++i) { item = static_cast(*i); part = item->part(); if(!tagAllParts && (part != curPart || (part && part->track() != curTrack))) continue; if(!(tagAllItems || (tagSelected && item->isSelected()) || (tagMoving && item->isMoving()))) continue; new_e = item->eventWithLength(); if(tagMoving && item->isMoving()) applyYOffset(new_e, offset_y); tag_list->add(part, new_e); } } else { if(tagSelected) { for(ciCItemList i = selection.cbegin(); i != selection.cend(); ++i) { item = static_cast(*i); part = item->part(); if(part != curPart || (part && part->track() != curTrack)) continue; new_e = item->eventWithLength(); tag_list->add(part, new_e); } } if(tagMoving) { for(ciCItemList i = moving.cbegin(); i != moving.cend(); ++i) { item = static_cast(*i); // Avoid duplicates found in selection list. if(tagSelected && selection.cfind(item) != selection.cend()) continue; part = item->part(); if(part != curPart || (part && part->track() != curTrack)) continue; new_e = item->eventWithLength(); applyYOffset(new_e, offset_y); tag_list->add(part, new_e); } } } } } //--------------------------------------------------------- // setController //--------------------------------------------------------- void CtrlCanvas::setController(int num) { setMidiController(num); updateItems(); } //--------------------------------------------------------- // setCurTrackAndPart //--------------------------------------------------------- bool CtrlCanvas::setCurTrackAndPart() { bool changed = false; MusECore::MidiPart* part = 0; MusECore::MidiTrack* track = 0; if(!editor->parts()->empty()) { MusECore::Part* pt = editor->curCanvasPart(); if(pt && pt->track()) { if(pt->track()->isMidiTrack()) { part = (MusECore::MidiPart*)pt; track = part->track(); } } } if(part != curPart) { curPart = part; changed = true; } if(track != curTrack) { curTrack = track; changed = true; } return changed; } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void CtrlCanvas::configChanged() { if (MusEGlobal::config.canvasBgPixmap.isEmpty()) { setBg(MusEGlobal::config.midiControllerViewBg); setBg(QPixmap()); } else { setBg(QPixmap(MusEGlobal::config.canvasBgPixmap)); } songChanged(SC_CONFIG); } //--------------------------------------------------------- // songChanged // all marked parts are added to the internal event list //--------------------------------------------------------- void CtrlCanvas::songChanged(MusECore::SongChangedStruct_t type) { if(editor->deleting()) // Ignore while while deleting to prevent crash. return; if(type & SC_CONFIG) { setBg(MusEGlobal::config.midiControllerViewBg); setFont(MusEGlobal::config.fonts[3]); } bool changed = false; if(type & (SC_CONFIG | SC_PART_MODIFIED | SC_SELECTION)) changed = setCurTrackAndPart(); // Although changing the instrument/device in the // config window generates a type of -1, we can eliminate // some other useless calls using SC_CONFIG, which was not used // anywhere else in muse before now, except song header. if((type & (SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUM_SELECTION | SC_PIANO_SELECTION | SC_DRUMMAP)) || ((type & (SC_PART_MODIFIED | SC_SELECTION)) && changed)) setMidiController(_cnum); if(!curPart) return; if(type & (SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUM_SELECTION | SC_PIANO_SELECTION | SC_DRUMMAP | SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED)) updateItems(); else if(type & SC_SELECTION) { // Prevent race condition: Ignore if the change was ultimately sent by the canvas itself. if(type._sender != this) updateItemSelections(); } } //--------------------------------------------------------- // partControllers // num is the controller number, in 'MidiController terms' (lo-byte = 0xff for per-note controllers). //--------------------------------------------------------- void CtrlCanvas::partControllers(const MusECore::MidiPart* part, int num, int* dnum, int* didx, MusECore::MidiController** mc, MusECore::MidiCtrlValList** mcvl, CtrlCanvasInfoStruct* ctrlInfo) { if(num == MusECore::CTRL_VELOCITY) // special case { if(mcvl) *mcvl = &veloList; if(mc) *mc = &MusECore::veloCtrl; if(dnum) *dnum = num; if(didx) *didx = num; if(ctrlInfo) *ctrlInfo = CtrlCanvasInfoStruct(); } else { if(!part) { if(mcvl) *mcvl = 0; if(mc) *mc = 0; if(dnum) *dnum = 0; if(didx) *didx = 0; if(ctrlInfo) *ctrlInfo = CtrlCanvasInfoStruct(); return; } MusECore::MidiTrack* mt = part->track(); MusECore::MidiPort* mp = NULL; MusECore::MidiController* mp_mc = NULL; int chan; int di = 0; int n = 0; bool is_newdrum_ctl = false; if((curDrumPitch >= 0) && ((num & 0xff) == 0xff)) { di = (num & ~0xff) | curDrumPitch; if(mt->type() == MusECore::Track::DRUM) { is_newdrum_ctl = true; n = (num & ~0xff) | mt->drummap()[curDrumPitch].anote; // Default to track port if -1 and track channel if -1. int mport = mt->drummap()[curDrumPitch].port; if(mport == -1) mport = mt->outPort(); mp = &MusEGlobal::midiPorts[mport]; chan = mt->drummap()[curDrumPitch].channel; if(chan == -1) chan = mt->outChannel(); } else if(mt->type() == MusECore::Track::MIDI) { n = di; // Simple one-to-one correspondence. There is no 'mapping' for piano roll midi - yet. mp = &MusEGlobal::midiPorts[mt->outPort()]; chan = mt->outChannel(); } } else { di = num; n = num; mp = &MusEGlobal::midiPorts[mt->outPort()]; chan = mt->outChannel(); } if(mp) mp_mc = mp->midiController(n, chan); if(dnum) *dnum = n; if(didx) *didx = di; if(mc) *mc = mp_mc; if(ctrlInfo) { int min = 0; int max = 127; int bias = 0; if(n == MusECore::CTRL_PROGRAM) { min = 1; max = 128; bias = 0; } else if(mp_mc) { min = mp_mc->minVal(); max = mp_mc->maxVal(); bias = mp_mc->bias(); } ctrlInfo->fin_ctrl_num = n; ctrlInfo->is_newdrum_ctl = is_newdrum_ctl; ctrlInfo->min = min; ctrlInfo->max = max; ctrlInfo->bias = bias; } if(mcvl) { MusECore::MidiCtrlValList* tmcvl = 0; MusECore::MidiCtrlValListList* cvll = mp->controller(); for(MusECore::iMidiCtrlValList i = cvll->begin(); i != cvll->end(); ++i) { if(i->second->num() == n) { tmcvl = i->second; break; } } *mcvl = tmcvl; } } } //--------------------------------------------------------- // itemSelectionsChanged //--------------------------------------------------------- bool CtrlCanvas::itemSelectionsChanged(MusECore::Undo* operations, bool deselectAll) { MusECore::Undo ops; MusECore::Undo* opsp = operations ? operations : &ops; bool item_selected; bool obj_selected; bool changed=false; // If we are deselecting all, globally deselect all events, // and don't bother individually deselecting objects, below. if(deselectAll) { opsp->push_back(MusECore::UndoOp(MusECore::UndoOp::GlobalSelectAllEvents, false, 0, 0)); changed = true; } // To save time searching the potentially large 'items' list, a selection list is used. for(ciCItemList i = selection.begin(); i != selection.end() ; ) { CItem* item = *i; item_selected = item->isSelected(); obj_selected = item->objectIsSelected(); // Don't bother deselecting objects if we have already deselected all, above. if((item_selected || !deselectAll) && ((item_selected != obj_selected) || // Need to force this because after the 'deselect all events' command executes, // if the item is selected another select needs to be executed even though it // appears nothing changed here. (item_selected && deselectAll))) // Here we have a choice of whether to allow undoing of selections. // Disabled for now, it's too tedious in use. Possibly make the choice user settable. opsp->push_back(MusECore::UndoOp(MusECore::UndoOp::SelectEvent, //item->event(), item->part(), item_selected, obj_selected, false)); item->event(), item->part(), item_selected, obj_selected)); changed=true; // Now it is OK to remove the item from the // selection list if it is unselected. if(!item_selected) i = selection.erase(i); else ++i; } if (!operations && changed) { // Set the 'sender' to this so that we can ignore self-generated songChanged signals. // Here we have a choice of whether to allow undoing of selections. if(MusEGlobal::config.selectionsUndoable) MusEGlobal::song->applyOperationGroup(ops, MusECore::Song::OperationUndoMode, this); else MusEGlobal::song->applyOperationGroup(ops, MusECore::Song::OperationExecuteUpdate, this); // For testing... // fprintf(stderr, "CtrlCanvas::updateSelection: Applied SelectPart operations, redrawing\n"); } return changed; } //--------------------------------------------------------- // updateItems //--------------------------------------------------------- void CtrlCanvas::updateItems() { selection.clear(); items.clearDelete(); moving.clear(); cancelMouseOps(); if(!editor->parts()->empty()) { CEvent *newev = 0; for (MusECore::iPart p = editor->parts()->begin(); p != editor->parts()->end(); ++p) { MusECore::Event last; CEvent* lastce = 0; MusECore::MidiPart* part = (MusECore::MidiPart*)(p->second); if (filterTrack && part->track() != curTrack) continue; MusECore::MidiCtrlValList* mcvl; partControllers(part, _cnum, 0, 0, 0, &mcvl, 0); unsigned len = part->lenTick(); for (MusECore::ciEvent i = part->events().begin(); i != part->events().end(); ++i) { const MusECore::Event& e = i->second; // Do not add events which are past the end of the part. #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)e.tick() /*+ (int)e.lenTick()*/ < 0) continue; if((int)e.tick() >= (int)len) break; #else if(e.tick() > len) break; #endif if(_cnum == MusECore::CTRL_VELOCITY && e.type() == MusECore::Note) { newev = 0; // Zero note on vel is not allowed now. int vel = e.velo(); if(vel == 0) { fprintf(stderr, "CtrlCanvas::updateItems: Warning: Event has zero note on velocity!\n"); vel = 1; } if (curDrumPitch == -1 || !_perNoteVeloMode) // and NOT >=0 items.add(newev = new CEvent(e, part, vel)); else if (e.dataA() == curDrumPitch) //same note. if curDrumPitch==-2, this never is true items.add(newev = new CEvent(e, part, vel)); if(newev && e.selected()) { newev->setSelected(true); selection.push_back(newev); } } else if (e.type() == MusECore::Controller) { int ctl = e.dataA(); if(part->track() && part->track()->type() == MusECore::Track::DRUM && (_cnum & 0xff) == 0xff) { if(curDrumPitch < 0) continue; // Default to track port if -1 and track channel if -1. int port = part->track()->drummap()[ctl & 0x7f].port; if(port == -1) port = part->track()->outPort(); int chan = part->track()->drummap()[ctl & 0x7f].channel; if(chan == -1) chan = part->track()->outChannel(); int cur_port = part->track()->drummap()[curDrumPitch].port; if(cur_port == -1) cur_port = part->track()->outPort(); int cur_chan = part->track()->drummap()[curDrumPitch].channel; if(cur_chan == -1) cur_chan = part->track()->outChannel(); if((port != cur_port) || (chan != cur_chan)) continue; ctl = (ctl & ~0xff) | part->track()->drummap()[ctl & 0x7f].anote; } if(ctl == _dnum) { if(mcvl && last.empty()) { lastce = new CEvent(MusECore::Event(), part, mcvl->value(part->tick())); items.add(lastce); } if (lastce) lastce->setEX(e.tick()); lastce = new CEvent(e, part, e.dataB()); lastce->setEX(-1); items.add(lastce); if(e.selected()) { lastce->setSelected(true); selection.push_back(lastce); } last = e; } } } } } redraw(); } //--------------------------------------------------------- // updateItemSelections //--------------------------------------------------------- void CtrlCanvas::updateItemSelections() { selection.clear(); cancelMouseOps(); bool obj_selected; for(ciCItemList i = items.begin(); i != items.end(); ++i) { // To save time searching the potentially large 'items' list, a selection list is used. CItem* item = *i; obj_selected = item->objectIsSelected(); item->setSelected(obj_selected); if(obj_selected) selection.push_back(item); } redraw(); } //--------------------------------------------------------- // startMoving // copy selection-List to moving-List //--------------------------------------------------------- void CtrlCanvas::startMoving(const QPoint& pos, int dir, bool rasterize) { CItem* first_item = NULL; for (iCItemList i = items.begin(); i != items.end(); ++i) { CItem* item = *i; if (item->isSelected() && item->part() == curPart) { if(!item->isMoving()) { item->setMoving(true); moving.add(item); } // Find the item with the lowest event time (the 'first' item by time). if(!first_item || item->event().tick() < first_item->event().tick()) first_item = item; } } _dragFirstXPos = 0; if(first_item) { const MusECore::Part* part = first_item->part(); if(part) { const MusECore::Event& ev = first_item->event(); _dragFirstXPos = !ev.empty() ? ev.tick() + part->tick() : 0; } } moveItems(pos, dir, rasterize); } //--------------------------------------------------------- // moveItems // dir = 0 move in all directions // 1 move only horizontal // 2 move only vertical //--------------------------------------------------------- void CtrlCanvas::moveItems(const QPoint& pos, int dir, bool rasterize) { if(!curPart) return; int dist_x = pos.x() - start.x(); int dist_y = pos.y() - start.y(); if (dir == 1) dist_y = 0; else if (dir == 2) dist_x = 0; int dx = _mouseDist.x() + dist_x; int dy = _mouseDist.y() + dist_y; if(dir != 2) { int tick = _dragFirstXPos + dx; if(tick < 0) tick = 0; if(rasterize) tick = editor->rasterVal(tick); dx = tick - _dragFirstXPos; } unsigned int first_pos_offset; if(_dragFirstXPos > curPart->posValue()) first_pos_offset = _dragFirstXPos - curPart->posValue(); else first_pos_offset = curPart->posValue(); // Limit the left movement. if(dx < 0 && (unsigned int)-dx > first_pos_offset) { dx = -first_pos_offset; // Reset the accumulating mouse distance. _mouseDist.setX(-first_pos_offset); } // Limit the up/down movement. if(start.y() + dy < 0) { dy = -start.y(); _mouseDist.setY(-start.y()); } else if(/*start.y() +*/ dy >= height()) { dy = height() /*- start.y()*/ - 1; _mouseDist.setY(height() /*- start.y()*/ - 1); } _curDragOffset = QPoint(dx, dy); // For testing... // fprintf(stderr, "_mouseDist x:%d y:%d dist_x:%d dist_y:%d _curDragOffset x:%d y:%d" // " _dragFirstXPos:%u first_pos_offset:%d\n", // _mouseDist.x(), _mouseDist.y(), dist_x, dist_y, // _curDragOffset.x(), _curDragOffset.y(), _dragFirstXPos, // first_pos_offset); redraw(); } void CtrlCanvas::endMoveItems() { if(!curPart) return; // Limit the paste position at zero. unsigned int pos = 0; if(_curDragOffset.x() > 0 || (_dragFirstXPos > ((unsigned int) -_curDragOffset.x()))) pos = _dragFirstXPos + _curDragOffset.x(); // Tag only moving items, regardless of selection (avoid the redundant // search in the selection list). MusECore::TagEventList tag_list; tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagMoving)); // HACK tagItems() returns a list of cloned events with the lengths set to the visual lengths. // We only use it for temporary things like copy/paste. // The lengths will be reset to zero after when done, in the paste_items_at() function. // Normally an event's length is ALWAYS zero for all controller events. // Whee ! Now paste the items directly from the list. No Xml conversion required. MusECore::paste_items_at( // Part list to search for given parts. //part_to_set(curPart), std::set(), // List of events to paste. &tag_list, // The paste position. MusECore::Pos(pos, true), // true = ticks. // Max distance before new part created. 3072, // Pasting options including whether to cut, how to erase existing target events, // and whether to create a new part. MusECore::FunctionOptionsStruct( (_dragType == MOVE_MOVE ? MusECore::FunctionCutItems : MusECore::FunctionNoOptions) | (MusEGlobal::config.midiCtrlGraphMergeErase ? MusECore::FunctionEraseItems : MusECore::FunctionNoOptions) | (MusEGlobal::config.midiCtrlGraphMergeEraseWysiwyg ? MusECore::FunctionEraseItemsWysiwyg : MusECore::FunctionNoOptions) | (MusEGlobal::config.midiCtrlGraphMergeEraseInclusive ? MusECore::FunctionEraseItemsInclusive : MusECore::FunctionNoOptions) | (MusECore::FunctionPasteNeverNewPart) ), // Paste into this part instead of the original part(s). //NULL, curPart, // Number of copies to paste. 1, // Separation between copies. 3072, // Choose which events to paste. MusECore::ControllersRelevant, // If pasting controllers, paste into this controller number if not -1. // If the source has multiple controllers, user will be asked which one to paste. _cnum ); // Be sure to clear the items' moving flag and the moving list! if(!moving.empty()) { for(iCItemList i = moving.begin(); i != moving.end(); ++i) (*i)->setMoving(false); moving.clear(); } if(drag != DRAG_OFF) drag = DRAG_OFF; _curDragOffset = QPoint(0, 0); _mouseDist = QPoint(0, 0); redraw(); } void CtrlCanvas::setCursor() { showCursor(); switch (drag) { case DRAGX_MOVE: case DRAGX_COPY: View::setCursor(QCursor(Qt::SizeHorCursor)); break; case DRAGY_MOVE: case DRAGY_COPY: View::setCursor(QCursor(Qt::SizeVerCursor)); break; case DRAG_MOVE: case DRAG_COPY: // Bug in KDE cursor theme? On some distros this cursor is actually another version of a closed hand! From 'net: // "It might be a problem in the distribution as Qt uses the cursor that is provided by X.org/xcursor extension with name "size_all". // We fixed this issue by setting the KDE cursor theme to "System theme" " View::setCursor(QCursor(Qt::SizeAllCursor)); break; case DRAG_RESIZE: View::setCursor(QCursor(Qt::SizeHorCursor)); break; case DRAG_PAN: if(MusEGlobal::config.borderlessMouse) showCursor(false); // CAUTION else View::setCursor(QCursor(Qt::ClosedHandCursor)); break; case DRAG_ZOOM: if(MusEGlobal::config.borderlessMouse) showCursor(false); // CAUTION break; case DRAG_DELETE: case DRAG_COPY_START: case DRAG_MOVE_START: case DRAG_NEW: case DRAG_LASSO_START: case DRAG_LASSO: case DRAG_OFF: switch(tool) { case PencilTool: QWidget::setCursor(*pencilCursor); // QWidget::setCursor(*magnetCursor); break; case RubberTool: QWidget::setCursor(*deleteCursor); break; case GlueTool: QWidget::setCursor(QCursor(Qt::ForbiddenCursor)); break; case CutTool: QWidget::setCursor(QCursor(Qt::ForbiddenCursor)); break; case MuteTool: QWidget::setCursor(QCursor(Qt::ForbiddenCursor)); break; case DrawTool: QWidget::setCursor(*drawCursor); break; case PanTool: QWidget::setCursor(QCursor(Qt::ForbiddenCursor)); break; case ZoomTool: QWidget::setCursor(QCursor(Qt::ForbiddenCursor)); break; default: if(moving.empty()) { View::setCursor(QCursor(Qt::ArrowCursor)); } else { if(_movingItemUnderCursor) { View::setCursor(QCursor(Qt::SizeAllCursor)); } else { if(_dragType == MOVE_MOVE) View::setCursor(*editpasteSCursor); else View::setCursor(*editpasteCloneSCursor); } } break; } break; } } //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- void CtrlCanvas::viewMousePressEvent(QMouseEvent* event) { // For testing... // fprintf(stderr, "CtrlCanvas::viewMousePressEvent\n"); if(!_controller || curDrumPitch==-2) { cancelMouseOps(); return; } Qt::MouseButton button = event->button(); // Ignore event if (another) button is already active: if (event->buttons() ^ button) { return; } setMouseGrab(false); start = event->pos(); MusEGui::Tool activeTool = tool; bool ctrlKey = event->modifiers() & Qt::ControlModifier; int xpos = start.x(); if(xpos < 0) xpos = 0; int ypos = start.y(); MusECore::MidiController::ControllerType type = MusECore::midiControllerType(_controller->num()); const bool velo = type == MusECore::MidiController::Velo; _operations.clear(); const int tickstep = rmapxDev(1); curItem = findCurrentItem(start, tickstep, height()); if (button == Qt::RightButton) { // No stay-open for now. // TODO How to write a click handler to support stay-open for // the 'options' items but not the 'actions' items ? PopupMenu* itemPopupMenu = new PopupMenu(this, false); populateMergeOptions(itemPopupMenu); itemPopupMenu->setToolTipsVisible(true); QAction *act = itemPopupMenu->exec(event->globalPos()); int idx = -1; bool is_checked = false; if(act && act->data().isValid()) { idx = act->data().toInt(); is_checked = act->isChecked(); } delete itemPopupMenu; switch(idx) { case ContextIdCancelDrag: cancelMouseOps(); break; case ContextIdMerge: if(!moving.empty()) { // Force the drag type before merge. _dragType = MOVE_MOVE; // Merge the moving items... endMoveItems(); setCursor(); // Return. The user must click again to select anything. return; } break; case ContextIdMergeCopy: if(!moving.empty()) { // Force the drag type before merge. _dragType = MOVE_COPY; // Merge the moving items... endMoveItems(); setCursor(); // Return. The user must click again to select anything. return; } break; case ContextIdErase: MusEGlobal::config.midiCtrlGraphMergeErase = is_checked; break; case ContextIdEraseWysiwyg: MusEGlobal::config.midiCtrlGraphMergeEraseWysiwyg = is_checked; break; case ContextIdEraseInclusive: MusEGlobal::config.midiCtrlGraphMergeEraseInclusive = is_checked; break; default: break; } } else if (button == Qt::LeftButton) { // If selecting controller empty space or a new item, // we must first merge whatever items may have been // moved before anything further is done. if(!velo && (!curItem || !curItem->isSelected() || !curItem->isMoving())) { if(!moving.empty()) { // Merge the moving items... endMoveItems(); setCursor(); // Return. The user must click again to select anything. return; } } switch (activeTool) { case MusEGui::PointerTool: if(curPart) { if (!velo && curItem && (curItem->isMoving() || curItem->isSelected())) { // If the current item is not already moving, then this // is the grand start of a move. Here we must reset // these 'accumulating' variables! if(!curItem->isMoving()) { clearMoving(); } // Alt alone is usually reserved for moving a window in X11. Ignore shift + alt. if (ctrlKey) drag = DRAG_COPY_START; else drag = DRAG_MOVE_START; } else drag = DRAG_LASSO_START; setCursor(); setMouseGrab(true); // CAUTION } break; case MusEGui::PencilTool: if (!ctrlKey && !velo) { drag = DRAG_NEW; newVal(xpos, ypos); } else { drag = DRAG_RESIZE; changeVal(xpos, xpos, ypos); } // setCursor(); break; case MusEGui::RubberTool: if (!velo) { drag = DRAG_DELETE; deleteVal(xpos, xpos, ypos); } setCursor(); break; case MusEGui::DrawTool: if (drawLineMode) { line2x = xpos; line2y = ypos; if (!ctrlKey && !velo) newValRamp(line1x, line1y, line2x, line2y); else changeValRamp(line1x, line1y, line2x, line2y); drawLineMode = false; } else { line2x = line1x = xpos; line2y = line1y = ypos; drawLineMode = true; } redraw(); break; default: break; } } } //--------------------------------------------------------- // viewMouseMoveEvent //--------------------------------------------------------- void CtrlCanvas::viewMouseMoveEvent(QMouseEvent* event) { _movingItemUnderCursor = NULL; if(!_controller || curDrumPitch==-2) { // Be sure to cancel any relative stuff. Otherwise it's left in a bad state. cancelMouseOps(); return; } // Drag not off and left mouse button not pressed? It's an error. // Meaning likely mouseRelease was not called (which CAN happen). if(drag != DRAG_OFF && !(event->buttons() & Qt::LeftButton)) { // For testing... // fprintf(stderr, "CtrlCanvas::viewMouseMoveEvent: calling cancelMouseOps()\n"); // Be sure to cancel any relative stuff. Otherwise it's left in a bad state. cancelMouseOps(); } Qt::KeyboardModifiers modifiers = event->modifiers(); bool shift = modifiers & Qt::ShiftModifier; QPoint pos = event->pos(); const QPoint dist = pos - start; const int ax = ABS(dist.x()); const int ay = ABS(dist.y()); const bool is_moving = (ax >= 2) || (ay > 2); switch (drag) { case DRAG_LASSO_START: if (!is_moving) break; drag = DRAG_LASSO; setCursor(); // fallthrough case DRAG_LASSO: lasso.setRect(start.x(), start.y(), dist.x(), dist.y()); redraw(); break; case DRAG_RESIZE: changeVal(start.x(), pos.x(), pos.y()); start = pos; break; case DRAG_MOVE_START: case DRAG_COPY_START: { if (!is_moving) break; int dir = 0; if (shift) { if (ax > ay) { if (drag == DRAG_MOVE_START) drag = DRAGX_MOVE; else drag = DRAGX_COPY; dir = 1; } else { if (drag == DRAG_MOVE_START) drag = DRAGY_MOVE; else drag = DRAGY_COPY; dir = 2; } } else { if (drag == DRAG_MOVE_START) drag = DRAG_MOVE; else drag = DRAG_COPY; } setCursor(); if (curItem && !curItem->isSelected()) { if (drag == DRAG_MOVE) deselectAll(); selectItem(curItem); itemSelectionsChanged(NULL, drag == DRAG_MOVE); redraw(); } startMoving(pos, dir, !shift); break; } case DRAG_MOVE: case DRAG_COPY: // TODO // if(!scrollTimer) moveItems(pos, 0, !shift); break; case DRAGX_MOVE: case DRAGX_COPY: // TODO // if(!scrollTimer) // moveItems(pos, 1, false); moveItems(pos, 1, !shift); break; case DRAGY_MOVE: case DRAGY_COPY: // TODO // if(!scrollTimer) // moveItems(pos, 2, false); moveItems(pos, 2, !shift); break; case DRAG_NEW: newVal(start.x(), start.y(), pos.x(), pos.y()); start = pos; break; case DRAG_DELETE: deleteVal(start.x(), pos.x(), pos.y()); start = pos; break; case DRAG_OFF: { _movingItemUnderCursor = NULL; const int tickstep = rmapxDev(1); for(iCItemList i = moving.begin(); i != moving.end(); ++i) { CEvent* item = static_cast(*i); if(item->part() != curPart) continue; // Be sure to subtract the drag offset from the given point. if(item->containsPoint(_controller, pos - _curDragOffset, tickstep, height())) { _movingItemUnderCursor = item; break; } } // Set the cursor but only if items are moving. if(!moving.empty()) setCursor(); } break; default: break; } if(pos.x() < 0) pos.setX(0); if (tool == MusEGui::DrawTool && drawLineMode) { line2x = pos.x(); line2y = pos.y(); redraw(); } emit xposChanged(editor->rasterVal(pos.x())); int val = computeVal(_controller, pos.y(), height()); emit yposChanged(val); //_mouseDist = event->pos(); if (MusEGlobal::config.showNoteTooltips) QToolTip::showText(QPoint(event->globalX(), event->globalY() + 20), tr("Value: ") + QString::number(val)); } //--------------------------------------------------------- // viewMouseReleaseEvent //--------------------------------------------------------- void CtrlCanvas::viewMouseReleaseEvent(QMouseEvent* event) { // For testing... // fprintf(stderr, "CtrlCanvas::viewMouseReleaseEvent\n"); // We want only the left mouse release events. Ignore anything else. // Do nothing, even if the mouse is grabbed or we have a moving list. if(event->button() != Qt::LeftButton) return; // Immediately cancel any mouse grabbing. // Because for example there are a few message boxes that may appear // in the subsequent code, and the mouse will not work in them if it // is still grabbed. setMouseGrab(false); const QPoint pos = event->pos(); Qt::KeyboardModifiers modifiers = event->modifiers(); bool ctrlKey = modifiers & Qt::ControlModifier; const int xpos = start.x(); const int ypos = start.y(); const int tickstep = rmapxDev(1); // Now that the mouse is up we must contribute the // distance moved to the 'accumulating' variable. _mouseDist += (pos - start); switch (drag) { case DRAG_MOVE_START: case DRAG_COPY_START: // Don't change anything if the current item is moving. if(!curItem || !curItem->isMoving()) { if (!ctrlKey) deselectAll(); if(curItem) { if (ctrlKey && curItem->isSelected()) deselectItem(curItem); else selectItem(curItem); } itemSelectionsChanged(NULL, !ctrlKey); redraw(); } break; case DRAG_COPY: case DRAGX_COPY: case DRAGY_COPY: case DRAG_MOVE: case DRAGX_MOVE: case DRAGY_MOVE: break; case DRAG_LASSO_START: { lasso = QRect(xpos, ypos, tickstep, rmapyDev(1)); } //fallthrough case DRAG_LASSO: { // Don't change anything if the current item is moving. if(!curItem || !curItem->isMoving()) { if (!ctrlKey) { deselectAll(); } if(_controller) { lasso = lasso.normalized(); int h = height(); CEvent* item; for (iCItemList i = items.begin(); i != items.end(); ++i) { item = static_cast(*i); if(item->part() != curPart) continue; if (item->intersectsController(_controller, lasso, tickstep, h)) { // If the lasso is empty treat it as a single click. if (ctrlKey && item->isSelected()) deselectItem(item); else selectItem(item); } } drag = DRAG_OFF; itemSelectionsChanged(NULL, !ctrlKey); } redraw(); } } break; case DRAG_RESIZE: case DRAG_NEW: case DRAG_DELETE: // Also in case ramp was drawn, check if any operations are required. default: // Set the 'sender' to this so that we can ignore self-generated songChanged signals. MusEGlobal::song->applyOperationGroup(_operations, MusECore::Song::OperationUndoMode, this); break; } // Done with any operations. Clear the list. _operations.clear(); drag = DRAG_OFF; } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void CtrlCanvas::wheelEvent(QWheelEvent* ev) { emit redirectWheelEvent(ev); } //--------------------------------------------------------- // newValRamp //--------------------------------------------------------- void CtrlCanvas::newValRamp(int x1, int y1, int x2, int y2) { if(!curPart || !_controller) return; if(x1 < 0) x1 = 0; if(x2 < 0) x2 = 0; if(x2 - x1 < 0) { int a = x1; x1 = x2; x2 = a; a = y1; y1 = y2; y2 = a; } int xx1 = editor->rasterVal1(x1); int xx2 = editor->rasterVal2(x2); // If x1 and x2 happen to lie directly on the same raster, xx1 will equal xx2, // which is not good - there should always be a spread. Nudge by +1 and recompute. if(xx1 == xx2) xx2 = editor->rasterVal2(x2 + 1); int type = _controller->num(); bool useRaster = false; int raster = editor->raster(); if (raster == 1) // set reasonable raster { raster = MusEGlobal::config.division/16; // Let's use 64th notes, for a bit finer resolution. Tim. useRaster = true; } // delete existing events unsigned curPartTick = curPart->tick(); int lastpv = MusECore::CTRL_VAL_UNKNOWN; for (ciCItemList i = items.begin(); i != items.end(); ++i) { CItem* ev = *i; if (ev->part() != curPart) continue; MusECore::Event event = ev->event(); if (event.empty()) continue; int x = event.tick() + curPartTick; if (x < xx1) continue; if (x >= xx2) break; // Do port controller values and clone parts. _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, event, curPart, true, true)); } if (ctrl) lastpv = ctrl->hwVal(); unsigned curPartLen = curPart->lenTick(); // insert new events for (int x = xx1, step; x < xx2 ; x += step ) { step = useRaster ? raster : editor->rasterVal2(x + 1) - x; int y = x + step >= xx2 || x2 == x1 ? y2 : (x == xx1 ? y1 : (((y2 - y1) * (x + step/2 - x1)) / (x2 - x1)) + y1); int nval = computeVal(_controller, y, height()); unsigned tick = x - curPartTick; // Do not add events which are past the end of the part. if (tick >= curPartLen) break; MusECore::Event event(MusECore::Controller); event.setTick(tick); event.setA(_didx); if (type == MusECore::CTRL_PROGRAM) { if (lastpv == MusECore::CTRL_VAL_UNKNOWN) event.setB(nval - 1); else event.setB((lastpv & 0xffff00) | (nval - 1)); } else event.setB(nval); // Do port controller values and clone parts. _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, event, curPart, true, true)); } } //--------------------------------------------------------- // changeValRamp //--------------------------------------------------------- void CtrlCanvas::changeValRamp(int x1, int y1, int x2, int y2) { if(!curPart || !_controller) return; int h = height(); int type = _controller->num(); for (ciCItemList i = items.begin(); i != items.end(); ++i) { CEvent* ev = static_cast(*i); if (ev->containsXRange(x1, x2)) { if (ev->part() != curPart) continue; MusECore::Event event = ev->event(); if (event.empty()) continue; int x = event.tick() + curPart->tick(); int y = (x2==x1) ? y1 : (((y2-y1)*(x-x1))/(x2-x1))+y1; int nval = computeVal(_controller, y, h); if (type == MusECore::CTRL_PROGRAM) { if (event.dataB() == MusECore::CTRL_VAL_UNKNOWN) --nval; else nval = (event.dataB() & 0xffff00) | (nval - 1); } ev->setVal(nval); if (type == MusECore::CTRL_VELOCITY) { if(nval > 127) nval = 127; else if(nval <= 0) nval = 1; // Zero note on vel is not allowed. if ((event.velo() != nval)) { MusECore::Event newEvent = event.clone(); newEvent.setVelo(nval); // Do not do port controller values and clone parts. _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, curPart, false, false)); } } else { if (!event.empty()) { if ((event.dataB() != nval)) { MusECore::Event newEvent = event.clone(); newEvent.setB(nval); // Do port controller values and clone parts. _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, curPart, true, true)); } } } } } } //--------------------------------------------------------- // changeVal //--------------------------------------------------------- void CtrlCanvas::changeVal(int x1, int x2, int y) { if(!curPart || !_controller) return; bool changed = false; int newval = computeVal(_controller, y, height()); int type = _controller->num(); for (ciCItemList i = items.begin(); i != items.end(); ++i) { CEvent* ev = static_cast(*i); if (!ev->containsXRange(x1, x2)) continue; if(ev->part() != curPart) continue; MusECore::Event event = ev->event(); if (type == MusECore::CTRL_VELOCITY) { if(newval > 127) newval = 127; else if(newval <= 0) newval = 1; // Zero note on vel is not allowed. if ((event.velo() != newval)) { ev->setVal(newval); MusECore::Event newEvent = event.clone(); newEvent.setVelo(newval); // Indicate do not do port controller values and clone parts. _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, curPart, false, false)); changed = true; } } else { if (!event.empty()) { int nval = newval; if(type == MusECore::CTRL_PROGRAM) { if(event.dataB() == MusECore::CTRL_VAL_UNKNOWN) --nval; else nval = (event.dataB() & 0xffff00) | (nval - 1); } ev->setVal(nval); if ((event.dataB() != nval)) { MusECore::Event newEvent = event.clone(); newEvent.setB(nval); // Indicate do port controller values and clone parts. _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, curPart, true, true)); changed = true; } } } } if (changed) redraw(); } //--------------------------------------------------------- // newVal //--------------------------------------------------------- void CtrlCanvas::newVal(int x1, int y) { if(!curPart || !_controller) return; if(x1 < 0) x1 = 0; int xx1 = editor->rasterVal1(x1); int xx2 = editor->rasterVal2(x1); // If x1 happens to lie directly on a raster, xx1 will equal xx2, // which is not good - there should always be a spread. Nudge by +1 and recompute. if(xx1 == xx2) xx2 = editor->rasterVal2(x1 + 1); int newval = computeVal(_controller, y, height()); int type = _controller->num(); bool found = false; bool do_redraw = false; iCItemList ice_tmp; iCItemList prev_ev = items.end(); // End is OK with multi-part, this is just an end 'invalid' marker at first. iCItemList insertPoint = items.end(); // Similar case here. bool curPartFound = false; int lastpv = MusECore::CTRL_VAL_UNKNOWN; if(ctrl) lastpv = ctrl->hwVal(); int partTick = curPart->tick(); for (iCItemList i = items.begin(); i != items.end() ; ) { CEvent* ev = static_cast(*i); if(ev->part() != curPart) { if(curPartFound) { // Make sure insertPoint doesn't lie beyond next part's first event. if(insertPoint == items.end()) insertPoint = i; // Speed-up: If the current part was already processed, we are done. break; } ++i; continue; } curPartFound = true; MusECore::Event event = ev->event(); if (event.empty()) { prev_ev = i; ++i; continue; } int ax = event.tick() + partTick; if (ax < xx1) { prev_ev = i; ++i; continue; } if (ax >= xx2) { insertPoint = i; break; } int nval = newval; if(type == MusECore::CTRL_PROGRAM) { if(event.dataB() == MusECore::CTRL_VAL_UNKNOWN) { if(lastpv == MusECore::CTRL_VAL_UNKNOWN) --nval; else nval = (lastpv & 0xffff00) | (nval - 1); } else nval = (event.dataB() & 0xffff00) | (nval - 1); } if (ax == xx1) { // change event found = true; ev->setVal(nval); if ((event.dataB() != nval)) { MusECore::Event newEvent = event.clone(); newEvent.setB(nval); // For testing... // fprintf(stderr, "CtrlCanvas::newVal: Found. Pushing ModifyEvent\n"); // Indicate do port controller values and clone parts. _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, curPart, true, true)); // Now that the modify command has been sent, 'event' will stay alive // (its reference count stays alive) in the operation/undo lists etc, // so it is now safe to overwrite the canvas item's event. // This is important so that successive modify commands use newEvent as the 'old event', // otherwise the undo/operation system will complain about 'double deletes' etc. // The undo/operation system then optimizes the entire sequence down to just one modify command. ev->setEvent(newEvent); do_redraw = true; } prev_ev = i; ++i; } else if (ax < xx2) { // We must remove the item from the selected list. removeSelection(ev); // Indicate do port controller values and clone parts. // For testing... // fprintf(stderr, "CtrlCanvas::newVal: Found. Pushing DeleteEvent\n"); _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, event, curPart, true, true)); // Now that the delete command has been sent, 'event' will stay alive // (its reference count stays alive) in the operation/undo lists etc, // so it is now safe to delete the canvas item and its event. delete ev; // Erase the item from the item list and get the next item. i = items.erase(i); ev = static_cast(*i); // Is there a previous item? if(prev_ev != items.end()) { // Have we reached the end of the list, or the end of the current part's items within the item list? // Then it's current part's last drawn item. EX is 'infinity'. (As far as the part knows. Another part may overlay later.) // Else EX is current item's tick. (By now, ((*i)->event() should be valid - it must be not empty to call tick()). static_cast(*prev_ev)->setEX(i == items.end() || ev->part() != curPart ? -1 : ev->event().tick()); } do_redraw = true; // Let songChanged handle the redraw upon SC_SELECTION. prev_ev = i; } } if (!found) { // new event int tick = xx1 - curPart->tick(); // Do not add events which are past the end of the part. if((unsigned)tick < curPart->lenTick()) { MusECore::Event event(MusECore::Controller); event.setTick(tick); event.setA(_didx); if(type == MusECore::CTRL_PROGRAM) { if(lastpv == MusECore::CTRL_VAL_UNKNOWN) event.setB(newval - 1); else event.setB((lastpv & 0xffff00) | (newval - 1)); } else event.setB(newval); // Indicate do port controller values and clone parts. // For testing... // fprintf(stderr, "CtrlCanvas::newVal: Not found. Pushing AddEvent\n"); _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, event, curPart, true, true)); CEvent* newev = new CEvent(event, curPart, event.dataB()); insertPoint = items.insert(insertPoint, newev); //selectItem(newev); // TODO: There are advantages, but do we really want new items to be selected? if(insertPoint != items.begin()) { ice_tmp = insertPoint; --ice_tmp; static_cast(*ice_tmp)->setEX(tick); } ice_tmp = insertPoint; ++ice_tmp; static_cast(*insertPoint)->setEX(ice_tmp == items.end() || (*ice_tmp)->part() != curPart ? -1 : (*ice_tmp)->event().tick()); do_redraw = true; } } if(do_redraw) // Let songChanged handle the redraw upon SC_SELECTION. redraw(); } //--------------------------------------------------------- // newVal //--------------------------------------------------------- void CtrlCanvas::newVal(int x1, int y1, int x2, int y2) { if(!curPart || !_controller) return; if(x1 < 0) x1 = 0; if(x2 < 0) x2 = 0; if(x2 - x1 < 0) { int a = x1; x1 = x2; x2 = a; a = y1; y1 = y2; y2 = a; } // I wanted to avoid redundantly drawing the first x position since it is already done via mouseDown. // But with the raster 'off' there were some problems not catching some items, and jagged drawing. // Possibly there are slight differences between rasterVal1() and rasterVal2() - not reciprocals of each other? // Also the loops below were getting complicated to ignore that first position. // So I draw from the first x. (TODO The idea may work now since I wrote this - more work was done.) Tim. int xx1 = editor->rasterVal1(x1); int xx2 = editor->rasterVal2(x2); // If x1 and x2 happen to lie directly on the same raster, xx1 will equal xx2, // which is not good - there should always be a spread. Nudge by +1 and recompute. if(xx1 == xx2) xx2 = editor->rasterVal2(x2 + 1); int type = _controller->num(); bool useRaster = false; int raster = editor->raster(); if (raster == 1) // set reasonable raster { raster = MusEGlobal::config.division/16; // Let's use 64th notes, for a bit finer resolution. Tim. useRaster = true; } bool do_redraw = false; // delete existing events iCItemList prev_ev = items.end(); // End is OK with multi-part, this is just an end 'invalid' marker at first. iCItemList insertPoint = items.end(); // Similar case here. iCItemList ice_tmp; bool curPartFound = false; int lastpv = MusECore::CTRL_VAL_UNKNOWN; int partTick = curPart->tick(); for (iCItemList i = items.begin(); i != items.end() ; ) { CEvent* ev = static_cast(*i); if(ev->part() != curPart) { if(curPartFound) { // Make sure insertPoint doesn't lie beyond next part's first event. if(insertPoint == items.end()) insertPoint = i; // Speed-up: If the current part was already processed, we are done. break; } ++i; continue; } curPartFound = true; MusECore::Event event = ev->event(); if (event.empty()) { prev_ev = i; ++i; continue; } int x = event.tick() + partTick; if (x < xx1) { prev_ev = i; ++i; continue; } if (x >= xx2) { insertPoint = i; break; } // We must remove the item from the selected list. removeSelection(ev); // Indicate do port controller values and clone parts. // For testing... // fprintf(stderr, "CtrlCanvas::newVal 2: Pushing DeleteEvent\n"); _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, event, curPart, true, true)); delete (ev); i = items.erase(i); ev = static_cast(*i); // Is there a previous item? if(prev_ev != items.end()) { // Have we reached the end of the list, or the end of the current part's items within the item list? // Then it's current part's last drawn item. EX is 'infinity'. (As far as the part knows. Another part may overlay later.) // Else EX is current item's tick. (By now, ((*i)->event() should be valid - it must be not empty to call tick()). static_cast(*prev_ev)->setEX(i == items.end() || ev->part() != curPart ? -1 : ev->event().tick()); } do_redraw = true; // Let songChanged handle the redraw upon SC_SELECTION. prev_ev = i; } if(ctrl) lastpv = ctrl->hwVal(); // insert new events for (int x = xx1, step; x < xx2 ; x += step ) { step = useRaster ? raster : editor->rasterVal2(x + 1) - x; int y = x + step >= xx2 || x2 == x1 ? y2 : (x == xx1 ? y1 : (((y2 - y1) * (x + step/2 - x1)) / (x2 - x1)) + y1); int nval = computeVal(_controller, y, height()); int tick = x - partTick; // Do not add events which are past the end of the part. if((unsigned)tick >= curPart->lenTick()) break; MusECore::Event event(MusECore::Controller); event.setTick(tick); event.setA(_didx); if(type == MusECore::CTRL_PROGRAM) { if(lastpv == MusECore::CTRL_VAL_UNKNOWN) event.setB(nval - 1); else event.setB((lastpv & 0xffff00) | (nval - 1)); } else event.setB(nval); // Indicate do port controller values and clone parts. // For testing... // fprintf(stderr, "CtrlCanvas::newVal 2: Pushing AddEvent\n"); _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, event, curPart, true, true)); CEvent* newev = new CEvent(event, curPart, event.dataB()); insertPoint = items.insert(insertPoint, newev); //selectItem(newev); // TODO: There are advantages, but do we really want new items to be selected? if(insertPoint != items.begin()) { ice_tmp = insertPoint; --ice_tmp; static_cast(*ice_tmp)->setEX(tick); } ice_tmp = insertPoint; ++ice_tmp; static_cast(*insertPoint)->setEX(ice_tmp == items.end() || (*ice_tmp)->part() != curPart ? -1 : (*ice_tmp)->event().tick()); ++insertPoint; do_redraw = true; } if(do_redraw) // redraw(); } //--------------------------------------------------------- // deleteVal //--------------------------------------------------------- void CtrlCanvas::deleteVal(int x1, int x2, int) { if(!curPart) return; if(x1 < 0) x1 = 0; if(x2 < 0) x2 = 0; if(x2 - x1 < 0) { int a = x1; x1 = x2; x2 = a; } int xx1 = editor->rasterVal1(x1); int xx2 = editor->rasterVal2(x2); // If x1 and x2 happen to lie directly on the same raster, xx1 will equal xx2, // which is not good - there should always be a spread. Nudge by +1 and recompute. if(xx1 == xx2) xx2 = editor->rasterVal2(x2 + 1); int partTick = curPart->tick(); xx1 -= partTick; xx2 -= partTick; iCItemList prev_ev = items.end(); bool curPartFound = false; bool do_redraw = false; for (iCItemList i = items.begin(); i != items.end() ;) { CEvent* ev = static_cast(*i); if(ev->part() != curPart) { // Speed-up: If the current part was already processed, we are done. if(curPartFound) break; ++i; continue; } curPartFound = true; MusECore::Event event = ev->event(); if (event.empty()) { prev_ev = i; ++i; continue; } int x = event.tick(); if (x < xx1) { prev_ev = i; ++i; continue; } if (x >= xx2) break; // We must remove the item from the selected list. removeSelection(ev); // Indicate do port controller values and clone parts. _operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, event, curPart, true, true)); delete (ev); i = items.erase(i); ev = static_cast(*i); // Is there a previous item? if(prev_ev != items.end()) { // Have we reached the end of the list, or the end of the current part's items within the item list? // Then it's current part's last drawn item. EX is 'infinity'. (As far as the part knows. Another part may overlay later.) // Else EX is current item's tick. (By now, ((*i)->event() should be valid - it must be not empty to call tick()). static_cast(*prev_ev)->setEX(i == items.end() || ev->part() != curPart ? -1 : ev->event().tick()); } do_redraw = true; prev_ev = i; } if(do_redraw) redraw(); // Let songChanged handle the redraw upon SC_SELECTION. } //--------------------------------------------------------- // setTool //--------------------------------------------------------- void CtrlCanvas::setTool(int t) { if (tool == MusEGui::Tool(t)) return; tool = MusEGui::Tool(t); switch(tool) { case MusEGui::DrawTool: drawLineMode = false; break; default: break; } cancelMouseOps(); setCursor(); } //--------------------------------------------------------- // pFillBackgrounds //--------------------------------------------------------- void CtrlCanvas::pFillBackgrounds(QPainter& p, const QRect& rect, const MusECore::MidiPart* part) { if(!part) return; int x = rect.x() - 1; // compensate for 3 pixel line width int w = rect.width() + 2; int wh = height(); QColor dark_gray_color = Qt::darkGray; dark_gray_color.setAlpha(_bgAlpha); MusECore::MidiTrack* mt = part->track(); MusECore::MidiPort* mp; int chan; int cnum = _cnum; bool is_newdrum_ctl = (mt->type() == MusECore::Track::DRUM) && (curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff); if(is_newdrum_ctl) { // Default to track port if -1 and track channel if -1. int mport = mt->drummap()[curDrumPitch].port; if(mport == -1) mport = mt->outPort(); mp = &MusEGlobal::midiPorts[mport]; cnum = (_cnum & ~0xff) | mt->drummap()[curDrumPitch].anote; chan = mt->drummap()[curDrumPitch].channel; if(chan == -1) chan = mt->outChannel(); } else { mp = &MusEGlobal::midiPorts[mt->outPort()]; chan = mt->outChannel(); } MusECore::MidiController* mc = mp->midiController(cnum, chan); int min; int max; int bias; if(cnum == MusECore::CTRL_PROGRAM) { min = 1; max = 128; bias = 0; } else { min = mc->minVal(); max = mc->maxVal(); bias = mc->bias(); } int x1 = rect.x(); int lval = MusECore::CTRL_VAL_UNKNOWN; for (iCItemList i = items.begin(); i != items.end(); ++i) { CEvent* e = static_cast(*i); // Draw unselected part controller events (lines) on top of selected part events (bars). if(e->part() != part) continue; MusECore::Event ev = e->event(); // Draw drum controllers from another drum on top of ones from this drum. if(is_newdrum_ctl && ev.type() == MusECore::Controller && ev.dataA() != _didx) continue; int tick = mapx(!ev.empty() ? ev.tick() + e->part()->tick() : 0); int val = e->val(); int pval = val; if(cnum == MusECore::CTRL_PROGRAM) { if((val & 0xff) == 0xff) // What to do here? prog = 0xff should not be allowed, but may still be encountered. pval = 1; else pval = (val & 0x7f) + 1; } if (tick <= x) { if (val == MusECore::CTRL_VAL_UNKNOWN) lval = MusECore::CTRL_VAL_UNKNOWN; else { if(cnum == MusECore::CTRL_PROGRAM) lval = wh - ((pval - min - bias) * wh / (max - min)); else lval = wh - ((val - min - bias) * wh / (max - min)); } continue; } if (tick > x+w) break; if (lval == MusECore::CTRL_VAL_UNKNOWN) p.fillRect(x1, 0, tick - x1, wh, dark_gray_color); x1 = tick; if (val == MusECore::CTRL_VAL_UNKNOWN) lval = MusECore::CTRL_VAL_UNKNOWN; else { if(cnum == MusECore::CTRL_PROGRAM) lval = wh - ((pval - min - bias) * wh / (max - min)); else lval = wh - ((val - min - bias) * wh / (max - min)); } } if (lval == MusECore::CTRL_VAL_UNKNOWN) p.fillRect(x1, 0, (x+w) - x1, wh, dark_gray_color); } //--------------------------------------------------------- // drawMoving // draws moving items //--------------------------------------------------------- void CtrlCanvas::drawMoving(QPainter& p, const QRect& rect, const QRegion& /*region*/, const MusECore::MidiPart* part) { int x = rect.x(); // compensate for 3 pixel line width int w = rect.width(); int wh = height(); // const QColor selection_color(0, 160, 255, MusEGlobal::config.globalAlphaBlend); QColor selection_color(MusEGlobal::config.ctrlGraphSel); selection_color.setAlpha(MusEGlobal::config.globalAlphaBlend); QColor graph_fg_color = MusEGlobal::config.ctrlGraphFg; graph_fg_color.setAlpha(MusEGlobal::config.globalAlphaBlend); QColor gray_color = Qt::gray; gray_color.setAlpha(MusEGlobal::config.globalAlphaBlend); if(!part) return; QPen pen; pen.setCosmetic(true); CtrlCanvasInfoStruct info_out; partControllers(curPart, _cnum, 0, 0, 0, 0, &info_out); const int cnum = info_out.fin_ctrl_num; const bool is_newdrum_ctl = info_out.is_newdrum_ctl; const int min = info_out.min; const int max = info_out.max; const int bias = info_out.bias; int lval = MusECore::CTRL_VAL_UNKNOWN; int item_x, item_endx; int tick; unsigned int part_tick; QColor color; for (iCItemList i = moving.begin(); i != moving.end(); ++i) { CEvent* e = static_cast(*i); // Draw unselected part controller events (lines) on top of selected part events (bars). if(e->part() != part) continue; MusECore::Event ev = e->event(); if(ev.empty()) continue; // Draw drum controllers from another drum on top of ones from this drum. if(is_newdrum_ctl && ev.type() == MusECore::Controller && ev.dataA() != _didx) continue; part_tick = e->part()->tick(); tick = ev.tick() + part_tick + _curDragOffset.x(); if(tick < 0) tick = 0; item_x = mapx(tick); item_endx = x + w; if(e->EX() >= 0) { int end_tick = e->EX() + part_tick + _curDragOffset.x(); if(end_tick < 0) end_tick = 0; item_endx = mapx(end_tick); } if(item_x >= (x + w) || item_endx <= x) continue; const int val = e->val(); int pval = val; if(cnum == MusECore::CTRL_PROGRAM) { if((val & 0xff) == 0xff) // What to do here? prog = 0xff should not be allowed, but may still be encountered. pval = 1; else pval = (val & 0x7f) + 1; } color = selection_color; if(pval == MusECore::CTRL_VAL_UNKNOWN) { pval = max; color = gray_color; } else { pval -= bias; if(pval < min) pval = min; if(pval > max) pval = max; } lval = wh - ((pval - min) * wh / (max - min)); lval += mapy(_curDragOffset.y()); if(lval < 0) lval = 0; if(lval >= wh) lval = wh - 1; if(item_x < x) item_x = x; if(item_endx > (x + w)) item_endx = x + w; p.fillRect(item_x, lval, item_endx - item_x, wh - lval, color); } } //--------------------------------------------------------- // pdrawItems //--------------------------------------------------------- void CtrlCanvas::pdrawItems(QPainter& p, const QRect& rect, const MusECore::MidiPart* part, bool velo, bool fg) { int x = rect.x() - 1; // compensate for 3 pixel line width int w = rect.width() + 2; int wh = height(); // const QColor selection_color(0, 160, 255, MusEGlobal::config.globalAlphaBlend); QColor selection_color(MusEGlobal::config.ctrlGraphSel); selection_color.setAlpha(MusEGlobal::config.globalAlphaBlend); QColor graph_fg_color = MusEGlobal::config.ctrlGraphFg; graph_fg_color.setAlpha(MusEGlobal::config.globalAlphaBlend); QColor gray_color = Qt::gray; gray_color.setAlpha(MusEGlobal::config.globalAlphaBlend); QColor light_gray_color = Qt::lightGray; light_gray_color.setAlpha(MusEGlobal::config.globalAlphaBlend); QColor fill_color; noEvents=true; if(velo) { noEvents=false; for(iCItemList i = items.begin(); i != items.end(); ++i) { CEvent* e = static_cast(*i); // Draw selected part velocity events on top of unselected part events. if(e->part() != part) continue; MusECore::Event event = e->event(); int tick = mapx(event.tick() + e->part()->tick()); if (tick <= x) continue; if (tick > x+w) break; int y1 = wh - (e->val() * wh / 128); // fg means 'draw selected parts'. if(fg) { if(e->isSelected()) //p.setPen(QPen(Qt::blue, 3)); p.setPen(QPen(selection_color, 3)); else p.setPen(QPen(graph_fg_color, 3)); } else p.setPen(QPen(Qt::darkGray, 3)); p.drawLine(tick, wh, tick, y1); } } else { if(!part) return; QPen pen; pen.setCosmetic(true); MusECore::MidiTrack* mt = part->track(); MusECore::MidiPort* mp; int chan; int cnum = _cnum; bool is_newdrum_ctl = (mt->type() == MusECore::Track::DRUM) && (curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff); if(is_newdrum_ctl) { // Default to track port if -1 and track channel if -1. int mport = mt->drummap()[curDrumPitch].port; if(mport == -1) mport = mt->outPort(); mp = &MusEGlobal::midiPorts[mport]; cnum = (_cnum & ~0xff) | mt->drummap()[curDrumPitch].anote; chan = mt->drummap()[curDrumPitch].channel; if(chan == -1) chan = mt->outChannel(); } else { mp = &MusEGlobal::midiPorts[mt->outPort()]; chan = mt->outChannel(); } MusECore::MidiController* mc = mp->midiController(cnum, chan); int min; int max; int bias; if(cnum == MusECore::CTRL_PROGRAM) { min = 1; max = 128; bias = 0; } else { min = mc->minVal(); max = mc->maxVal(); bias = mc->bias(); } int x1 = rect.x(); int lval = MusECore::CTRL_VAL_UNKNOWN; bool selected = false; bool is_moving = false; for (iCItemList i = items.begin(); i != items.end(); ++i) { noEvents=false; CEvent* e = static_cast(*i); // Draw unselected part controller events (lines) on top of selected part events (bars). if(e->part() != part) continue; MusECore::Event ev = e->event(); // Draw drum controllers from another drum on top of ones from this drum. if(is_newdrum_ctl && ev.type() == MusECore::Controller && ev.dataA() != _didx) continue; int tick = mapx(!ev.empty() ? ev.tick() + e->part()->tick() : 0); int val = e->val(); int pval = val; if(cnum == MusECore::CTRL_PROGRAM) { if((val & 0xff) == 0xff) // What to do here? prog = 0xff should not be allowed, but may still be encountered. pval = 1; else pval = (val & 0x7f) + 1; } if (tick <= x) { if (val == MusECore::CTRL_VAL_UNKNOWN) lval = MusECore::CTRL_VAL_UNKNOWN; else { if(cnum == MusECore::CTRL_PROGRAM) lval = wh - ((pval - min - bias) * wh / (max - min)); else lval = wh - ((val - min - bias) * wh / (max - min)); } selected = e->isSelected(); is_moving = e->isMoving(); continue; } if (tick > x+w) break; if (lval != MusECore::CTRL_VAL_UNKNOWN) { if(fg) { pen.setColor(gray_color); p.setPen(pen); p.drawLine(x1, lval, tick, lval); } else { fill_color = is_moving ? light_gray_color : (selected ? selection_color : graph_fg_color); p.fillRect(x1, lval, tick - x1, wh - lval, fill_color); } } x1 = tick; if (val == MusECore::CTRL_VAL_UNKNOWN) lval = MusECore::CTRL_VAL_UNKNOWN; else { if(cnum == MusECore::CTRL_PROGRAM) lval = wh - ((pval - min - bias) * wh / (max - min)); else lval = wh - ((val - min - bias) * wh / (max - min)); } selected = e->isSelected(); is_moving = e->isMoving(); } if (lval != MusECore::CTRL_VAL_UNKNOWN) { if(fg) { pen.setColor(gray_color); p.setPen(pen); p.drawLine(x1, lval, x + w, lval); } else { fill_color = is_moving ? light_gray_color : (selected ? selection_color : graph_fg_color); p.fillRect(x1, lval, (x+w) - x1, wh - lval, fill_color); } } } } //--------------------------------------------------------- // pdrawExtraDrumCtrlItems //--------------------------------------------------------- void CtrlCanvas::pdrawExtraDrumCtrlItems(QPainter& p, const QRect& rect, const MusECore::MidiPart* part, int drum_ctl) { int x = rect.x() - 1; // compensate for 3 pixel line width int w = rect.width() + 2; int wh = height(); QPen pen; pen.setCosmetic(true); noEvents=true; { if(!part) return; MusECore::MidiTrack* mt = part->track(); MusECore::MidiPort* mp; int chan; int cnum = _cnum; bool is_newdrum_ctl = (mt->type() == MusECore::Track::DRUM) && (curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff); if(is_newdrum_ctl) { // Default to track port if -1 and track channel if -1. int mport = mt->drummap()[curDrumPitch].port; if(mport == -1) mport = mt->outPort(); mp = &MusEGlobal::midiPorts[mport]; cnum = (_cnum & ~0xff) | mt->drummap()[curDrumPitch].anote; chan = mt->drummap()[curDrumPitch].channel; if(chan == -1) chan = mt->outChannel(); } else { mp = &MusEGlobal::midiPorts[mt->outPort()]; chan = mt->outChannel(); } MusECore::MidiController* mc = mp->midiController(cnum, chan); int min; int max; int bias; if(cnum == MusECore::CTRL_PROGRAM) { min = 1; max = 128; bias = 0; } else { min = mc->minVal(); max = mc->maxVal(); bias = mc->bias(); } int x1 = rect.x(); int lval = MusECore::CTRL_VAL_UNKNOWN; for (iCItemList i = items.begin(); i != items.end(); ++i) { noEvents=false; CEvent* e = static_cast(*i); // Draw unselected part controller events (lines) on top of selected part events (bars). if(e->part() != part) continue; MusECore::Event ev = e->event(); // Draw drum controllers from another drum on top of ones from this drum. // FIXME TODO Finish this off, not correct yet. if(drum_ctl == -1 && is_newdrum_ctl && ev.type() == MusECore::Controller && ev.dataA() != _didx) continue; if(drum_ctl != -1 && (!is_newdrum_ctl || (ev.type() == MusECore::Controller && ev.dataA() == _didx))) continue; int tick = mapx(!ev.empty() ? ev.tick() + e->part()->tick() : 0); int val = e->val(); int pval = val; if(cnum == MusECore::CTRL_PROGRAM) { if((val & 0xff) == 0xff) // What to do here? prog = 0xff should not be allowed, but may still be encountered. pval = 1; else pval = (val & 0x7f) + 1; } if (tick <= x) { if (val == MusECore::CTRL_VAL_UNKNOWN) lval = MusECore::CTRL_VAL_UNKNOWN; else { if(cnum == MusECore::CTRL_PROGRAM) lval = wh - ((pval - min - bias) * wh / (max - min)); else lval = wh - ((val - min - bias) * wh / (max - min)); } continue; } if (tick > x+w) break; if (lval != MusECore::CTRL_VAL_UNKNOWN) { pen.setColor(Qt::gray); p.setPen(pen); p.drawLine(x1, lval, tick, lval); } x1 = tick; if (val == MusECore::CTRL_VAL_UNKNOWN) lval = MusECore::CTRL_VAL_UNKNOWN; else { if(cnum == MusECore::CTRL_PROGRAM) lval = wh - ((pval - min - bias) * wh / (max - min)); else lval = wh - ((val - min - bias) * wh / (max - min)); } } if (lval != MusECore::CTRL_VAL_UNKNOWN) { pen.setColor(Qt::gray); p.setPen(pen); p.drawLine(x1, lval, x + w, lval); } } } //--------------------------------------------------------- // pdraw //--------------------------------------------------------- void CtrlCanvas::pdraw(QPainter& p, const QRect& rect, const QRegion& region) { if(!_controller) return; QPen pen; pen.setCosmetic(true); int x = rect.x() - 1; // compensate for 3 pixel line width int y = rect.y(); int w = rect.width() + 2; int h = rect.height(); //--------------------------------------------------- // draw Canvas Items //--------------------------------------------------- bool velo = (MusECore::midiControllerType(_controller->num()) == MusECore::MidiController::Velo); if(!velo) // Fill backgrounds of 'unknown' value areas. pFillBackgrounds(p, rect, curPart); //--------------------------------------------------- // draw the grid and markers now - before velocity items //--------------------------------------------------- p.save(); View::pdraw(p, rect); p.restore(); pen.setColor(MusEGlobal::config.rangeMarkerColor); p.setPen(pen); int xp = mapx(pos[1]); if (xp >= x && xp < x+w) { p.drawLine(xp, y, xp, y+h); } xp = mapx(pos[2]); if (xp >= x && xp < x+w) { p.drawLine(xp, y, xp, y+h); } // Draw the red main position cursor last, on top of the others. xp = mapx(pos[0]); if (xp >= x && xp < x+w) { pen.setColor(MusEGlobal::config.positionMarkerColor); p.setPen(pen); p.drawLine(xp, y, xp, y+h); } if(!velo) // Draw non-fg non-velocity items for the current part pdrawItems(p, rect, curPart, false, false); for(MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { MusECore::MidiPart* part = (MusECore::MidiPart*)(ip->second); if(part == curPart || (filterTrack && part->track() != curTrack)) continue; // Draw items for all parts - other than current part pdrawItems(p, rect, part, velo, !velo); } // Special: Draw fg drum controller items for non-current selected drum, for the current part // FIXME TODO Finish this off, not correct yet. if(curPart && curPart->track() && curPart->track()->type() == MusECore::Track::DRUM && curDrumPitch >= 0 && ((_cnum & 0xff) == 0xff)) { // Default to track port if -1 and track channel if -1. int port = curPart->track()->drummap()[curDrumPitch].port; if(port == -1) port = curPart->track()->outPort(); int anote = curPart->track()->drummap()[curDrumPitch].anote; for(int i = 0; i < DRUM_MAPSIZE; ++i) { int iport = curPart->track()->drummap()[i].port; if(iport == -1) iport = curPart->track()->outPort(); if(i != curDrumPitch && iport == port && curPart->track()->drummap()[i].anote == anote) pdrawExtraDrumCtrlItems(p, rect, curPart, anote); } } if(velo) { // Draw fg velocity items for the current part pdrawItems(p, rect, curPart, true, true); } //--------------------------------------------------- // draw outlines of potential drop places of moving items //--------------------------------------------------- if(!velo) drawMoving(p, rect, region, curPart); //--------------------------------------------------- // draw lasso //--------------------------------------------------- if (drag == DRAG_LASSO) { setPainter(p); pen.setColor(Qt::blue); p.setPen(pen); p.setBrush(Qt::NoBrush); p.drawRect(lasso); } } //--------------------------------------------------------- // drawOverlay //--------------------------------------------------------- void CtrlCanvas::drawOverlay(QPainter& p, const QRect&, const QRegion&) { const QString s = _controller ? _controller->name() : QString(""); p.setFont(font()); p.setPen(palette().color(QPalette::WindowText)); int y = fontMetrics().lineSpacing() + 2; const int txt_x = -xorg + overlayTextOffsetFromOrg; p.drawText(txt_x, y, s); } //--------------------------------------------------------- // overlayRect // returns geometry of overlay rectangle //--------------------------------------------------------- QRect CtrlCanvas::overlayRect() const { const QFontMetrics fm = fontMetrics(); QRect r = fm.boundingRect(_controller ? _controller->name() : QString("")); int y = fm.lineSpacing() + 2; const int txt_x = -xorg + overlayTextOffsetFromOrg; r.translate(txt_x, y); return r; } //--------------------------------------------------------- // draw //--------------------------------------------------------- void CtrlCanvas::draw(QPainter& p, const QRect& rect, const QRegion& rg) { if (MusEGlobal::config.canvasShowGrid) { drawTickRaster(p, rect, rg, editor->raster(), false, false, false, MusEGlobal::config.midiCanvasBeatColor, MusEGlobal::config.midiCanvasBeatColor, MusEGlobal::config.midiCanvasFineColor, MusEGlobal::config.midiCanvasBarColor ); } //--------------------------------------------------- // draw line tool //--------------------------------------------------- if ((tool == MusEGui::DrawTool) && drawLineMode) { QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); p.drawLine(line1x, line1y, line2x, line2y); } } //--------------------------------------------------------- // drumPitchChanged //--------------------------------------------------------- bool CtrlCanvas::drumPitchChanged() { // Is it a drum controller? if((curDrumPitch >= 0) && ((_cnum & 0xff) == 0xff)) { // Recompose the canvas according to the new selected pitch. setMidiController(_cnum); updateItems(); return true; } return false; } //--------------------------------------------------------- // findCurrentItem //--------------------------------------------------------- CEvent* CtrlCanvas::findCurrentItem(const QPoint& p, const int tickstep, const int h) { // Look for items that are moving first, since they are displayed // on top of existing items until merged at some later time. for (iCItemList i = moving.begin(); i != moving.end(); ++i) { CEvent* item = static_cast(*i); if(item->part() != curPart) continue; // Just to be thorough, the item should be marked as moving. if(!item->isMoving()) continue; // Be sure to subtract the drag offset from the given point. if(item->containsPoint(_controller, p - _curDragOffset, tickstep, h)) { return item; } } for (iCItemList i = items.begin(); i != items.end(); ++i) { CEvent* item = static_cast(*i); if(item->part() != curPart) continue; // Disregard items that are moving. if(item->isMoving()) continue; if(item->containsPoint(_controller, p, tickstep, h)) { return item; } } return NULL; } void CtrlCanvas::showCursor(bool show) { if(_cursorOverrideCount > 1) fprintf(stderr, "MusE Warning: _cursorOverrideCount > 1 in CtrlCanvas::showCursor(%d)\n", show); if(show) { while(_cursorOverrideCount > 0) { QApplication::restoreOverrideCursor(); _cursorOverrideCount--; } } else { _cursorOverrideCount++; QApplication::setOverrideCursor(Qt::BlankCursor); // CAUTION } } //--------------------------------------------------------- // setMouseGrab //--------------------------------------------------------- void CtrlCanvas::setMouseGrab(bool grabbed) { if(grabbed && !_mouseGrabbed) { _mouseGrabbed = true; grabMouse(); // CAUTION } else if(!grabbed && _mouseGrabbed) { releaseMouse(); _mouseGrabbed = false; } } //--------------------------------------------------------- // clearMoving //--------------------------------------------------------- bool CtrlCanvas::clearMoving() { bool changed = false; // Be sure to clear the moving list and especially the item moving flags! if(!moving.empty()) { for(iCItemList i = moving.begin(); i != moving.end(); ++i) (*i)->setMoving(false); moving.clear(); changed = true; } _curDragOffset = QPoint(0, 0); _mouseDist = QPoint(0, 0); _dragType = MOVE_MOVE; return changed; } //--------------------------------------------------------- // cancelMouseOps //--------------------------------------------------------- bool CtrlCanvas::cancelMouseOps() { bool changed = false; // Make sure this is done. See mousePressEvent. showCursor(); setMouseGrab(false); // Be sure to clear the moving list and especially the item moving flags! if(clearMoving()) changed = true; // Be sure to clear the operations list! if(!_operations.empty()) { _operations.clear(); changed = true; } if(drag != DRAG_OFF) { drag = DRAG_OFF; changed = true; } if(_dragType != MOVE_MOVE) { _dragType = MOVE_MOVE; changed = true; } redraw(); return changed; } //--------------------------------------------------------- // setCurDrumPitch //--------------------------------------------------------- bool CtrlCanvas::setCurDrumPitch(int instrument) { DrumEdit* drumedit = dynamic_cast(editor); if (drumedit == nullptr) curDrumPitch = instrument; else // new style drummap mode { // Crash protection by Tim. // FIXME: Still, drum list is blank, editor can't edit. Other values of instrument or curDrumPitch just crash too. // Seems only with drum tracks that were created by importing a midi file (then changed to use fluidsynth device?). if(instrument == -1) curDrumPitch = -1; else if (drumedit->get_instrument_map()[instrument].tracks.contains(curTrack)) curDrumPitch = drumedit->get_instrument_map()[instrument].pitch; else curDrumPitch = -2; // this means "invalid", but not "unused" } return drumPitchChanged(); } void CtrlCanvas::curPartHasChanged(MusECore::Part*) { // If the current part or track changed, setup the midi controller variables. if(setCurTrackAndPart()) setMidiController(_cnum); // Rebuild if setCurDrumPitch() doesn't already do it. if(!setCurDrumPitch(editor->curDrumInstrument())) updateItems(); // songChanged(SC_EVENT_MODIFIED); } void CtrlCanvas::setPerNoteVeloMode(bool v) { if(v == _perNoteVeloMode) return; _perNoteVeloMode = v; if(_cnum == MusECore::CTRL_VELOCITY) updateItems(); } //--------------------------------------------------- // populateMergeOptions //--------------------------------------------------- void CtrlCanvas::populateMergeOptions(PopupMenu* menu) { menu->addAction(new MenuTitleItem(tr("Merge Options"), menu)); QAction* act = menu->addAction(QIcon(*midiCtrlMergeEraseIcon), tr("Erase Target")); act->setData(ContextIdErase); act->setCheckable(true); act->setChecked(MusEGlobal::config.midiCtrlGraphMergeErase); act->setToolTip(tr("Erase target events between source events")); act = menu->addAction(QIcon(*midiCtrlMergeEraseWysiwygIcon), tr("Erase Target WYSIWYG")); act->setData(ContextIdEraseWysiwyg); act->setCheckable(true); act->setChecked(MusEGlobal::config.midiCtrlGraphMergeEraseWysiwyg); act->setToolTip(tr("Include last source item width when erasing")); act = menu->addAction(QIcon(*midiCtrlMergeEraseInclusiveIcon), tr("Erase Target Inclusive")); act->setData(ContextIdEraseInclusive); act->setCheckable(true); act->setChecked(MusEGlobal::config.midiCtrlGraphMergeEraseInclusive); act->setToolTip(tr("Include entire source range when erasing")); menu->addAction(new MenuTitleItem(tr("Merge Actions"), menu)); const bool is_mv = !moving.empty(); act = menu->addAction(*pasteSVGIcon, tr("Merge Dragged Items")); act->setData(ContextIdMerge); act->setCheckable(false); // act->setToolTip(tr("Merge the dragged items")); act->setEnabled(is_mv); act = menu->addAction(*copySVGIcon, tr("Merge Copy of Dragged Items")); act->setData(ContextIdMergeCopy); act->setCheckable(false); // act->setToolTip(tr("Merge a copy of the dragged items")); act->setEnabled(is_mv); act = menu->addAction(*clearSVGIcon, tr("Cancel Drag")); act->setData(ContextIdCancelDrag); act->setCheckable(false); // act->setToolTip(tr("Cancel dragging the items")); act->setEnabled(is_mv); } //--------------------------------------------------- // mergeDraggedItems // Merges any dragged items. Merges copies of items if 'copy' is true. Otherwise moves the items. //--------------------------------------------------- bool CtrlCanvas::mergeDraggedItems(bool /*copy*/) { return true; } } // namespace MusEGui muse-4.0.0/src/muse/ctrl/ctrlcanvas.h000066400000000000000000000276761404105311000175100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ctrlcanvas.h,v 1.7.2.4 2009/06/01 20:15:53 spamatica Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CTRLCANVAS_H__ #define __CTRLCANVAS_H__ #include #include "type_defs.h" #include "view.h" #include "tools.h" #include "midictrl.h" #include "event.h" #include "citem.h" #include "undo.h" #include "event_tag_list.h" class QWheelEvent; class QMouseEvent; class QEvent; class QWidget; class QLabel; namespace MusECore { class Event; class MidiPart; class MidiTrack; class PartList; } namespace MusEGui { class CtrlPanel; class MidiEditor; class PopupMenu; //--------------------------------------------------------- // CEvent // ''visual'' Controller Event //--------------------------------------------------------- class CEvent : public CItem { private: MusECore::Event _event; int _val; MusECore::Part* _part; int ex; public: CEvent(const MusECore::Event&, MusECore::Part*, int v); CEvent(); bool isObjectInRange(const MusECore::Pos&, const MusECore::Pos&) const; bool objectIsSelected() const { return _event.selected(); } int val() const { return _val; } void setVal(int v) { _val = v; } void setEX(int v) { ex = v; } bool containsPoint(const MusECore::MidiController* mc, const QPoint& p, const int tickstep, const int wh) const; bool containsXRange(int x1, int x2) const; bool intersectsController(const MusECore::MidiController*, const QRect&, const int tickstep, const int windowHeight) const; int EX() { return ex; } MusECore::Event event() const { return _event; } // HACK This returns a clone of the event with the length set to the visual length. // It should only be used for temporary things like copy/paste and the length // value should be reset to zero after usage. // Normally an event's length is ALWAYS zero for all controller events. MusECore::Event eventWithLength() const; void setEvent(const MusECore::Event& e) { _event = e; } MusECore::Part* part() const { return _part; } void setPart(MusECore::Part* p) { _part = p; } }; //--------------------------------------------------------- // CtrlCanvas //--------------------------------------------------------- class CtrlCanvas : public MusEGui::View { Q_OBJECT Q_PROPERTY( quint8 bgAlpha READ bgAlpha WRITE setBgAlpha ) public: //--------------------------------------------------------- // CtrlCanvasInfoStruct // Structure for returning info from CtrlCanvas::getCtrlInfo() //--------------------------------------------------------- struct CtrlCanvasInfoStruct { int fin_ctrl_num; bool is_newdrum_ctl; int min; int max; int bias; CtrlCanvasInfoStruct() : fin_ctrl_num(0), is_newdrum_ctl(false), min(0), max(127), bias(0) {} }; private: MidiEditor* editor; MusECore::MidiTrack* curTrack; MusECore::MidiPart* curPart; MusECore::MidiCtrlValList* ctrl; MusECore::MidiController* _controller; CtrlPanel* _panel; int _cnum; int _dnum; // Current real drum controller number (anote). int _didx; // Current real drum controller index. // For current part. CtrlCanvasInfoStruct _ctrlInfo; int line1x; int line1y; int line2x; int line2y; bool drawLineMode; bool noEvents; bool filterTrack; // Whether we have grabbed the mouse. bool _mouseGrabbed; // The number of times we have called QApplication::setOverrideCursor(). // This should always be one or zero, anything else is an error, but unforeseen // events might cause us to miss a decrement with QApplication::restoreOverrideCursor(). int _cursorOverrideCount; quint8 _bgAlpha; QPoint _curDragOffset; unsigned int _dragFirstXPos; //Qt::CursorShape _cursorShape; static const int overlayTextOffsetFromOrg; void applyYOffset(MusECore::Event& e, int yoffset) const; void viewMousePressEvent(QMouseEvent* event); void viewMouseMoveEvent(QMouseEvent*); void viewMouseReleaseEvent(QMouseEvent*); virtual void wheelEvent(QWheelEvent*); virtual void draw(QPainter&, const QRect& rect, const QRegion& = QRegion()); virtual void pdraw(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void drawOverlay(QPainter&, const QRect&, const QRegion& = QRegion()); virtual QRect overlayRect() const; void changeValRamp(int x1, int x2, int y1, int y2); void newValRamp(int x1, int y1, int x2, int y2); void changeVal(int x1, int x2, int y); void newVal(int x1, int y); void newVal(int x1, int y1, int x2, int y2); void deleteVal(int x1, int x2, int y); bool setCurTrackAndPart(); void drawMoving(QPainter& p, const QRect& rect, const QRegion& region, const MusECore::MidiPart* part); void pdrawItems(QPainter& p, const QRect& rect, const MusECore::MidiPart* part, bool velo, bool fg); void pFillBackgrounds(QPainter& p, const QRect& rect, const MusECore::MidiPart* part); void pdrawExtraDrumCtrlItems(QPainter& p, const QRect& rect, const MusECore::MidiPart* part, int drum_ctl); void partControllers( const MusECore::MidiPart* part, int num, int* dnum, int* didx, MusECore::MidiController** mc, MusECore::MidiCtrlValList** mcvl, CtrlCanvasInfoStruct* ctrlInfo); // Checks if the current drum pitch requires setting the midi controller and rebuilding the items. // Returns whether setMidiController() and updateItems() were in fact called. bool drumPitchChanged(); CEvent* findCurrentItem(const QPoint& p, const int tickstep, const int h); // If show is true, calls QApplication::restoreOverrideCursor() until _cursorOverrideCount-- is <= 0. // If show is false, calls QApplication::setOverrideCursor with a blank cursor. void showCursor(bool show = true); // Sets or resets the _mouseGrabbed flag and grabs or releases the mouse. void setMouseGrab(bool grabbed = false); protected: enum DragMode { DRAG_OFF, DRAG_NEW, DRAG_MOVE_START, DRAG_MOVE, DRAG_DELETE, DRAG_COPY_START, DRAG_COPY, DRAGX_MOVE, DRAGY_MOVE, DRAGX_COPY, DRAGY_COPY, DRAG_RESIZE, DRAG_LASSO_START, DRAG_LASSO, DRAG_PAN, DRAG_ZOOM }; enum DragType { MOVE_MOVE, MOVE_COPY }; enum ContextIds { ContextIdCancelDrag = 0x01, ContextIdMerge = 0x02, ContextIdMergeCopy = 0x04, ContextIdErase = 0x08, ContextIdEraseWysiwyg = 0x10, ContextIdEraseInclusive = 0x20 }; CItemList items; // To avoid working directly with a potentially huge number of items // in the item list, these 'indexing' lists are used instead. CItemList selection; CItemList moving; CEvent* curItem; CEvent* _movingItemUnderCursor; DragMode drag; DragType _dragType; QRect lasso; QPoint start; QPoint _mouseDist; MusEGui::Tool tool; unsigned pos[3]; int curDrumPitch; //Used by the drum-editor to view velocity of only one key (one drum) bool _perNoteVeloMode; // Accumulated operations during drawing etc. MusECore::Undo _operations; void setCursor(); void keyPressEvent(QKeyEvent *event); void keyReleaseEvent(QKeyEvent *event); void enterEvent(QEvent*e); void leaveEvent(QEvent*e); QPoint raster(const QPoint&) const; // selection bool isSingleSelection() { return selection.size() == 1; } void deselectAll(); void selectItem(CEvent* e); void deselectItem(CEvent* e); void removeSelection(CEvent* e); void setMidiController(int); void updateItems(); // Inform the app if local items have changed and their corresponding // objects need to be updated synchronously in the audio thread. // Returns true if anything changed (or will change). // Uses an internal undo operations list or optionally with a supplied list // so operations can be chained. bool itemSelectionsChanged(MusECore::Undo* operations = 0, bool deselectAll = false); void updateItemSelections(); // moving void startMoving(const QPoint&, int dir, bool rasterize = true); void moveItems(const QPoint&, int dir = 0, bool rasterize = true); void endMoveItems(); MusECore::Undo moveCanvasItems(CItemList&, int, int, DragType, bool rasterize = true); bool moveItem(MusECore::Undo&, CItem*, const QPoint&, DragType, bool rasterize = true); // Resets the moving flag of all items in moving list, then clears the list. // Returns true if anything was changed. bool clearMoving(); // Resets all mouse operations if detecting missed mouseRelease event (which DOES happen). // Returns true if reset was actually done. bool cancelMouseOps(); // Populates a popup menu with items related to drag/drop merging. void populateMergeOptions(PopupMenu* menu); // Merges any dragged items. Merges copies of items if 'copy' is true. Otherwise moves the items. // Returns true if items were merged, and it was successful. False if no items were moving, or error. bool mergeDraggedItems(bool copy); private slots: void songChanged(MusECore::SongChangedStruct_t type); void configChanged(); // Returns whether setMidiController() and updateItems() were in fact called, via drumPitchChanged(). bool setCurDrumPitch(int); public slots: void setTool(int t); void setPos(int, unsigned, bool adjustScrollbar); void setController(int ctrl); void curPartHasChanged(MusECore::Part*); signals: void followEvent(int); void xposChanged(unsigned); void yposChanged(int); void redirectWheelEvent(QWheelEvent*); public: CtrlCanvas(MidiEditor*, QWidget* parent, int, const char* name = 0, CtrlPanel* pnl = 0); ~CtrlCanvas(); void setPanel(CtrlPanel* pnl); MusECore::MidiCtrlValList* ctrlValList() { return ctrl; } MusECore::MidiController* controller() { return _controller; } MusECore::MidiTrack* track() const { return curTrack; } int getCurDrumPitch() const { return curDrumPitch; } bool perNoteVeloMode() const { return _perNoteVeloMode; } void setPerNoteVeloMode(bool); bool itemsAreSelected() const { return !selection.empty(); } // Appends given tag list with item objects according to options. Avoids duplicate events or clone events. // Special: We 'abuse' a controller event's length, normally 0, to indicate visual item length. void tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const; quint8 bgAlpha() const { return _bgAlpha; } void setBgAlpha(const quint8 a) { _bgAlpha = a; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/ctrl/ctrledit.cpp000066400000000000000000000153741404105311000175050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ctrledit.cpp,v 1.4.2.2 2009/02/02 21:38:00 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "ctrledit.h" #include "ctrlcanvas.h" #include "midieditor.h" #include "part.h" #include "xml.h" #include "vscale.h" #include "ctrlpanel.h" #include "globals.h" #include "gconfig.h" #include namespace MusEGui { //--------------------------------------------------------- // setTool //--------------------------------------------------------- void CtrlEdit::setTool(int t) { canvas->setTool(t); } void CtrlEdit::setXPos(int val) { canvas->setXPos(val); } void CtrlEdit::setXMag(int val) { canvas->setXMag(val); } //--------------------------------------------------------- // CtrlEdit //--------------------------------------------------------- CtrlEdit::CtrlEdit(QWidget* parent, MidiEditor* e, int xmag, int xOrigin, int yOrigin, bool expand, const char* name) : QWidget(parent) { setObjectName(name); setAttribute(Qt::WA_DeleteOnClose); QHBoxLayout* hbox = new QHBoxLayout; canvas = new CtrlCanvas(e, this, xmag, "ctrlcanvas"); panel = new CtrlPanel(this, e, canvas, "panel"); canvas->setPanel(panel); QWidget* vscale = new MusEGui::VScale(this); hbox->setContentsMargins(0, 0, 0, 0); hbox->setSpacing (0); canvas->setOrigin(xOrigin, yOrigin); canvas->setMinimumHeight(50); panel->setFixedWidth(CTRL_PANEL_FIXED_WIDTH); hbox->addWidget(panel, expand ? 100 : 0, Qt::AlignRight); hbox->addWidget(canvas, 100); hbox->addWidget(vscale, 0); setLayout(hbox); connect(panel, SIGNAL(destroyPanel()), SLOT(destroy())); connect(panel, SIGNAL(controllerChanged(int)), canvas, SLOT(setController(int))); connect(canvas, SIGNAL(xposChanged(unsigned)), SIGNAL(timeChanged(unsigned))); connect(canvas, SIGNAL(yposChanged(int)), SIGNAL(yposChanged(int))); connect(canvas, SIGNAL(redirectWheelEvent(QWheelEvent*)), SIGNAL(redirectWheelEvent(QWheelEvent*))); } //--------------------------------------------------------- // ctrlNum //--------------------------------------------------------- int CtrlEdit::ctrlNum() const { if(canvas) return canvas->controller()->num(); return 0; } //--------------------------------------------------------- // perNoteVel //--------------------------------------------------------- bool CtrlEdit::perNoteVel() const { if(canvas) return canvas->perNoteVeloMode(); return false; } //--------------------------------------------------------- // setPerNoteVel //--------------------------------------------------------- void CtrlEdit::setPerNoteVel(bool v) { if(canvas) canvas->setPerNoteVeloMode(v); if(panel) panel->setVeloPerNoteMode(v); } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void CtrlEdit::writeStatus(int level, MusECore::Xml& xml) { if (canvas && canvas->controller()) { xml.tag(level++, "ctrledit"); xml.intTag(level, "ctrlnum", canvas->controller()->num()); xml.intTag(level, "perNoteVeloMode", canvas->perNoteVeloMode()); xml.tag(level, "/ctrledit"); } } //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void CtrlEdit::readStatus(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "ctrl") xml.parse1(); // Obsolete. else if (tag == "ctrlnum") { int num = xml.parseInt(); if(canvas) canvas->setController(num); } else if (tag == "perNoteVeloMode") { bool v = xml.parseInt(); if(canvas) canvas->setPerNoteVeloMode(v); panel->setVeloPerNoteMode(v); } else xml.unknown("CtrlEdit"); break; case MusECore::Xml::TagEnd: if (tag == "ctrledit") return; default: break; } } } //--------------------------------------------------------- // destroy //--------------------------------------------------------- void CtrlEdit::destroy() { emit destroyedCtrl(this); close(); // close and destroy widget } //--------------------------------------------------------- // setCanvasWidth //--------------------------------------------------------- void CtrlEdit::setCanvasWidth(int w) { if(canvas) canvas->setFixedWidth(w); } void CtrlEdit::setController(int n) { if(canvas) canvas->setController(n); } void CtrlEdit::curPartHasChanged(MusECore::Part* p) { if(canvas) canvas->curPartHasChanged(p); } void CtrlEdit::setPanelWidth(int w) { panel->setFixedWidth(w); } bool CtrlEdit::itemsAreSelected() const { if(!canvas) return false; return canvas->itemsAreSelected(); } void CtrlEdit::tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const { if(canvas) canvas->tagItems(tag_list, options); } void CtrlEdit::redrawCanvas() { if(canvas) canvas->redraw();} void CtrlEdit::setCanvasOrigin(int xo, int yo) { if(canvas) canvas->setOrigin(xo, yo); } } // namespace MusEGui muse-4.0.0/src/muse/ctrl/ctrledit.h000066400000000000000000000056131404105311000171450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ctrledit.h,v 1.4.2.1 2008/05/21 00:28:53 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CTRL_EDIT_H__ #define __CTRL_EDIT_H__ #include #include #include "event_tag_list.h" namespace MusECore { class Xml; class Part; } #define CTRL_PANEL_FIXED_WIDTH 40 namespace MusEGui { class MidiEditor; class CtrlView; class CtrlPanel; class CtrlCanvas; //--------------------------------------------------------- // CtrlEdit //--------------------------------------------------------- class CtrlEdit : public QWidget { Q_OBJECT CtrlCanvas* canvas; CtrlPanel* panel; private slots: void destroy(); public slots: void setTool(int tool); void setXPos(int val); void setXMag(int val); void setCanvasWidth(int w); void setController(int n); void curPartHasChanged(MusECore::Part*); signals: void timeChanged(unsigned); void destroyedCtrl(CtrlEdit*); void enterCanvas(); void yposChanged(int); void redirectWheelEvent(QWheelEvent*); public: CtrlEdit(QWidget*, MidiEditor* e, int xmag, int xOrigin = 0, int yOrigin = 0, bool expand = false, const char* name = 0); int ctrlNum() const; bool perNoteVel() const; void setPerNoteVel(bool); void readStatus(MusECore::Xml&); void writeStatus(int, MusECore::Xml&); bool itemsAreSelected() const; // Appends given tag list with item objects according to options. Avoids duplicate events or clone events. // Special: We 'abuse' a controller event's length, normally 0, to indicate visual item length. void tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const; void redrawCanvas(); void setCanvasOrigin(int xo, int yo); void setPanelWidth(int w); }; typedef std::list CtrlEditList; typedef CtrlEditList::iterator iCtrlEdit; typedef CtrlEditList::const_iterator ciCtrlEdit; } // namespace MusEGui #endif muse-4.0.0/src/muse/ctrl/ctrlpanel.cpp000066400000000000000000000761701404105311000176600ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ctrlpanel.cpp,v 1.10.2.9 2009/06/14 05:24:45 terminator356 Exp $ // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2012, 2017 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "ctrlpanel.h" #include "ctrlcanvas.h" #include #include #include #include #include "muse_math.h" #include "globaldefs.h" #include "app.h" #include "globals.h" #include "midictrl.h" #include "instruments/minstrument.h" #include "mididev.h" #include "icons.h" #include "event.h" #include "part.h" #include "midiedit/drummap.h" #include "gconfig.h" #include "song.h" #include "utils.h" #include "audio.h" #include "midi_consts.h" #include "menutitleitem.h" #include "popupmenu.h" #include "helper.h" // Forwards from header: #include #include #include #include #include "midieditor.h" #include "midi_controller.h" #include "midiport.h" #include "track.h" #include "ctrlcanvas.h" #include "compact_knob.h" #include "compact_slider.h" #include "lcd_widgets.h" namespace MusEGui { //--------------------------------------------------------- // CtrlPanel //--------------------------------------------------------- CtrlPanel::CtrlPanel(QWidget* parent, MidiEditor* e, CtrlCanvas* c, const char* name) : QWidget(parent) { setObjectName(name); inHeartBeat = true; //setFocusPolicy(Qt::NoFocus); _knob = nullptr; _slider = nullptr; _patchEdit = nullptr; _veloPerNoteButton = nullptr; _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; _showval = MusEGlobal::config.showControlValues; editor = e; ctrlcanvas = c; setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); vbox = new QVBoxLayout; QHBoxLayout* bbox = new QHBoxLayout; bbox->setSpacing (0); vbox->addLayout(bbox); vbox->addStretch(); kbox = new QHBoxLayout; vbox->addLayout(kbox); vbox->addStretch(); vbox->setContentsMargins(0, 0, 0, 0); bbox->setContentsMargins(0, 0, 0, 0); kbox->setContentsMargins(0, 0, 0, 0); vbox->setSpacing (0); kbox->setSpacing(0); lspacer = nullptr; rspacer = nullptr; //: Select controller selCtrl = new CompactToolButton(this); selCtrl->setIcon(*midiControllerSelectSVGIcon); selCtrl->setIconSize(QSize(14, 14)); selCtrl->setHasFixedIconSize(true); selCtrl->setContentsMargins(4, 4, 4, 4); selCtrl->setFocusPolicy(Qt::NoFocus); selCtrl->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum)); selCtrl->setToolTip(tr("Select controller")); //: Remove panel (destroy button) CompactToolButton* destroy = new CompactToolButton(this); destroy->setIcon(*midiControllerRemoveSVGIcon); destroy->setIconSize(QSize(14, 14)); destroy->setHasFixedIconSize(true); destroy->setContentsMargins(4, 4, 4, 4); destroy->setFocusPolicy(Qt::NoFocus); destroy->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum)); destroy->setToolTip(tr("Remove panel")); connect(selCtrl, SIGNAL(clicked()), SLOT(ctrlPopup())); connect(destroy, SIGNAL(clicked()), SIGNAL(destroyPanel())); _track = nullptr; _ctrl = nullptr; _dnum = -1; bbox->addStretch(); bbox->addWidget(selCtrl); bbox->addWidget(destroy); bbox->addStretch(); configChanged(); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); inHeartBeat = false; setLayout(vbox); } void CtrlPanel::buildPanel() { if(!_track || !_ctrl) { if(_veloPerNoteButton) { kbox->removeWidget(_veloPerNoteButton); _veloPerNoteButton->deleteLater(); _veloPerNoteButton = nullptr; } if(_slider) { kbox->removeWidget(_slider); _slider->deleteLater(); _slider = nullptr; } if(_knob) { kbox->removeWidget(_knob); _knob->deleteLater(); _knob = nullptr; } if(_patchEdit) { kbox->removeWidget(_patchEdit); _patchEdit->deleteLater(); _patchEdit = nullptr; } if(lspacer) { kbox->removeItem(lspacer); delete lspacer; lspacer = nullptr; } if(rspacer) { kbox->removeItem(rspacer); delete rspacer; rspacer = nullptr; } return; } if(_dnum == MusECore::CTRL_VELOCITY) { if(_slider) { kbox->removeWidget(_slider); _slider->deleteLater(); _slider = nullptr; } if(_knob) { kbox->removeWidget(_knob); _knob->deleteLater(); _knob = nullptr; } if(_patchEdit) { kbox->removeWidget(_patchEdit); _patchEdit->deleteLater(); _patchEdit = nullptr; } if(!lspacer) { lspacer = new QSpacerItem(0, 0); kbox->addSpacerItem(lspacer); } if(!_veloPerNoteButton) { _veloPerNoteButton = new CompactToolButton(this); _veloPerNoteButton->setIcon(*velocityPerNoteSVGIcon); _veloPerNoteButton->setIconSize(QSize(19, 19)); _veloPerNoteButton->setHasFixedIconSize(true); _veloPerNoteButton->setContentsMargins(2, 2, 2, 2); _veloPerNoteButton->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum)); _veloPerNoteButton->setFocusPolicy(Qt::NoFocus); _veloPerNoteButton->setCheckable(true); _veloPerNoteButton->setToolTip(tr("All/Per-note velocity mode")); if(ctrlcanvas) _veloPerNoteButton->setChecked(ctrlcanvas->perNoteVeloMode()); connect(_veloPerNoteButton, SIGNAL(clicked()), SLOT(velPerNoteClicked())); kbox->addWidget(_veloPerNoteButton); } if(!rspacer) { rspacer = new QSpacerItem(0, 0); kbox->addSpacerItem(rspacer); } } else { if(lspacer) { kbox->removeItem(lspacer); delete lspacer; lspacer = nullptr; } if(rspacer) { kbox->removeItem(rspacer); delete rspacer; rspacer = nullptr; } if(_dnum == MusECore::CTRL_PROGRAM) { if(_veloPerNoteButton) { kbox->removeWidget(_veloPerNoteButton); _veloPerNoteButton->deleteLater(); _veloPerNoteButton = nullptr; } if(_slider) { kbox->removeWidget(_slider); _slider->deleteLater(); _slider = nullptr; } if(_knob) { kbox->removeWidget(_knob); _knob->deleteLater(); _knob = nullptr; } if(!_patchEdit) { _patchEdit = new LCDPatchEdit(this); _patchEdit->setReadoutOrientation(LCDPatchEdit::PatchVertical); _patchEdit->setValue(MusECore::CTRL_VAL_UNKNOWN); _patchEdit->setFocusPolicy(Qt::NoFocus); // Don't allow anything here, it interferes with the CompactPatchEdit which sets it's own controls' tooltips. //control->setToolTip(d->_toolTipText); _patchEdit->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); _patchEdit->setContentsMargins(0, 0, 0, 0); _patchEdit->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); if(_patchEdit->font() != MusEGlobal::config.fonts[1]) { _patchEdit->setFont(MusEGlobal::config.fonts[1]); _patchEdit->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1])); } connect(_patchEdit, SIGNAL(valueChanged(int,int)), SLOT(patchCtrlChanged(int))); connect(_patchEdit, SIGNAL(rightClicked(const QPoint&, int)), SLOT(ctrlRightClicked(const QPoint&, int))); kbox->addWidget(_patchEdit); } } else { if(_preferKnobs) { if(_veloPerNoteButton) { kbox->removeWidget(_veloPerNoteButton); _veloPerNoteButton->deleteLater(); _veloPerNoteButton = nullptr; } if(_slider) { kbox->removeWidget(_slider); _slider->deleteLater(); _slider = nullptr; } if(_patchEdit) { kbox->removeWidget(_patchEdit); _patchEdit->deleteLater(); _patchEdit = nullptr; } if(!_knob) { _knob = new CompactKnob(this, "CtrlPanelKnob", CompactKnob::Bottom); _knob->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); _knob->setToolTip(tr("Manual adjust (Ctrl-double-click on/off)")); //_knob->setFocusPolicy(Qt::NoFocus); _knob->setRange(0.0, 127.0, 1.0); _knob->setValue(0.0); _knob->setHasOffMode(true); _knob->setOff(true); _knob->setValueDecimals(0); _knob->setFaceColor(MusEGlobal::config.midiControllerSliderColor); _knob->setStep(1.0); _knob->setShowLabel(false); _knob->setShowValue(true); _knob->setEnableValueToolTips(false); // FIXME: Tooltip just gets in the way! _knob->setShowValueToolTipsOnHover(false); // if(_knob->font() != MusEGlobal::config.fonts[1]) { _knob->setFont(MusEGlobal::config.fonts[1]); _knob->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1])); } connect(_knob, SIGNAL(valueStateChanged(double,bool,int,int)), SLOT(ctrlChanged(double,bool,int,int))); connect(_knob, SIGNAL(sliderRightClicked(const QPoint&, int)), SLOT(ctrlRightClicked(const QPoint&, int))); kbox->addWidget(_knob); } } else { if(_veloPerNoteButton) { kbox->removeWidget(_veloPerNoteButton); _veloPerNoteButton->deleteLater(); _veloPerNoteButton = nullptr; } if(_knob) { kbox->removeWidget(_knob); _knob->deleteLater(); _knob = nullptr; } if(_patchEdit) { kbox->removeWidget(_patchEdit); _patchEdit->deleteLater(); _patchEdit = nullptr; } if(!_slider) { _slider = new CompactSlider(this, "CtrlPanelSlider"); _slider->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); _slider->setToolTip(tr("Manual adjust (Ctrl-double-click on/off)")); //_slider->setFocusPolicy(Qt::NoFocus); _slider->setRange(0.0, 127.0, 1.0); _slider->setValue(0.0); _slider->setHasOffMode(true); _slider->setOff(true); _slider->setValueDecimals(0); _slider->setBarColor(MusEGlobal::config.sliderBackgroundColor); _slider->setStep(1.0); _slider->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); _slider->setEnableValueToolTips(false); // FIXME: Tooltip just gets in the way! _slider->setShowValueToolTipsOnHover(false); // if(_slider->font() != MusEGlobal::config.fonts[1]) { _slider->setFont(MusEGlobal::config.fonts[1]); _slider->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1])); } connect(_slider, SIGNAL(valueStateChanged(double,bool,int,int)), SLOT(ctrlChanged(double,bool,int,int))); connect(_slider, SIGNAL(sliderRightClicked(const QPoint&, int)), SLOT(ctrlRightClicked(const QPoint&, int))); kbox->addWidget(_slider); } } } } } //--------------------------------------------------------- // heartBeat //--------------------------------------------------------- void CtrlPanel::heartBeat() { if(editor && editor->deleting()) // Ignore while while deleting to prevent crash. return; inHeartBeat = true; if(_track && _ctrl && _dnum != -1) { if(_dnum != MusECore::CTRL_VELOCITY) { int outport = _track->outPort(); int chan = _track->outChannel(); int cdp = ctrlcanvas->getCurDrumPitch(); if(_ctrl->isPerNoteController() && cdp >= 0) { if(_track->type() == MusECore::Track::DRUM) { // Default to track port if -1 and track channel if -1. outport = _track->drummap()[cdp].port; if(outport == -1) outport = _track->outPort(); chan = _track->drummap()[cdp].channel; if(chan == -1) chan = _track->outChannel(); } } MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outport]; MusECore::MidiCtrlValListList* mcvll = mp->controller(); MusECore::ciMidiCtrlValList imcvl = mcvll->find(chan, _dnum); const bool enable = imcvl != mcvll->end() && !_track->off(); // REMOVE Tim. midi. Added. // Although we do this for the midi strip control racks (and probably shouldn't), // we need the control to be enabled - user would expect to be able to adjust it // even if controller was not found - our code will create the controller. // if(_knob) // { // if(_knob->isEnabled() != enable) // _knob->setEnabled(enable); // } // else if(_slider) // { // if(_slider->isEnabled() != enable) // _slider->setEnabled(enable); // } if(enable) { MusECore::MidiCtrlValList* mcvl = imcvl->second; int hwVal = mcvl->hwVal(); if(_patchEdit && _dnum == MusECore::CTRL_PROGRAM) { // Special for new LCD patch edit control: Need to give both current and last values. // Keeping a local last value with the control won't work. _patchEdit->blockSignals(true); _patchEdit->setLastValidPatch(mcvl->lastValidHWVal()); _patchEdit->setLastValidBytes(mcvl->lastValidByte2(), mcvl->lastValidByte1(), mcvl->lastValidByte0()); _patchEdit->setValue(hwVal); _patchEdit->blockSignals(false); } else { int min = 0; int max = 127; int bias = 0; int initval = 0; MusECore::MidiController* mc = mp->midiController(_dnum, chan); if(mc) { bias = mc->bias(); min = mc->minVal(); max = mc->maxVal(); initval = mc->initVal(); if(initval == MusECore::CTRL_VAL_UNKNOWN) initval = 0; } const double dmin = (double)min; const double dmax = (double)max; if(_knob) { const double c_dmin = _knob->minValue(); const double c_dmax = _knob->maxValue(); if(c_dmin != min && c_dmax != max) { _knob->blockSignals(true); _knob->setRange(dmin, dmax, 1.0); _knob->blockSignals(false); } else if(c_dmin != min) { _knob->blockSignals(true); _knob->setMinValue(min); _knob->blockSignals(false); } else if(c_dmax != max) { _knob->blockSignals(true); _knob->setMaxValue(max); _knob->blockSignals(false); } if(hwVal == MusECore::CTRL_VAL_UNKNOWN) { hwVal = mcvl->lastValidHWVal(); if(hwVal == MusECore::CTRL_VAL_UNKNOWN) { hwVal = initval; if(!_knob->isOff() || hwVal != _knob->value()) { _knob->blockSignals(true); _knob->setValueState(hwVal, true); _knob->blockSignals(false); } } else { hwVal -= bias; if(!_knob->isOff() || hwVal != _knob->value()) { _knob->blockSignals(true); _knob->setValueState(hwVal, true); _knob->blockSignals(false); } } } else { hwVal -= bias; if(_knob->isOff() || hwVal != _knob->value()) { _knob->blockSignals(true); _knob->setValueState(hwVal, false); _knob->blockSignals(false); } } } else if(_slider) { const double c_dmin = _slider->minValue(); const double c_dmax = _slider->maxValue(); if(c_dmin != min && c_dmax != max) { _slider->blockSignals(true); _slider->setRange(dmin, dmax, 1.0); _slider->blockSignals(false); } else if(c_dmin != min) { _slider->blockSignals(true); _slider->setMinValue(min); _slider->blockSignals(false); } else if(c_dmax != max) { _slider->blockSignals(true); _slider->setMaxValue(max); _slider->blockSignals(false); } if(hwVal == MusECore::CTRL_VAL_UNKNOWN) { hwVal = mcvl->lastValidHWVal(); if(hwVal == MusECore::CTRL_VAL_UNKNOWN) { hwVal = initval; if(!_slider->isOff() || hwVal != _slider->value()) { _slider->blockSignals(true); _slider->setValueState(hwVal, true); _slider->blockSignals(false); } } else { hwVal -= bias; if(!_slider->isOff() || hwVal != _slider->value()) { _slider->blockSignals(true); _slider->setValueState(hwVal, true); _slider->blockSignals(false); } } } else { hwVal -= bias; if(_slider->isOff() || hwVal != _slider->value()) { _slider->blockSignals(true); _slider->setValueState(hwVal, false); _slider->blockSignals(false); } } } } } } } inHeartBeat = false; } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void CtrlPanel::configChanged() { songChanged(SC_CONFIG); // Detect when knobs are preferred and rebuild. if(_preferKnobs != MusEGlobal::config.preferKnobsVsSliders) { _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; setController(); } if(_patchEdit) { if(_patchEdit->font() != MusEGlobal::config.fonts[1]) { _patchEdit->setFont(MusEGlobal::config.fonts[1]); _patchEdit->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1])); } _patchEdit->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); } if(_knob) { if(_knob->font() != MusEGlobal::config.fonts[1]) { _knob->setFont(MusEGlobal::config.fonts[1]); _knob->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1])); } // Whether to show values along with labels for certain controls. //_knob->setShowValue(MusEGlobal::config.showControlValues); } if(_slider) { if(_slider->font() != MusEGlobal::config.fonts[1]) { _slider->setFont(MusEGlobal::config.fonts[1]); _slider->setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1])); } _slider->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); // Whether to show values along with labels for certain controls. //_slider->setShowValue(MusEGlobal::config.showControlValues); } setControlColor(); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void CtrlPanel::songChanged(MusECore::SongChangedStruct_t /*flags*/) { if(editor && editor->deleting()) // Ignore while while deleting to prevent crash. return; } void CtrlPanel::patchCtrlChanged(int val) { ctrlChanged(double(val), false, _dnum, 0); } //--------------------------------------------------------- // ctrlChanged //--------------------------------------------------------- void CtrlPanel::ctrlChanged(double val, bool off, int /*id*/, int /*scrollMode*/) { if (inHeartBeat) return; if(!_track || !_ctrl || _dnum == -1) return; int ival = lrint(val); int outport = _track->outPort(); int chan = _track->outChannel(); if(chan < 0 || chan >= MusECore::MUSE_MIDI_CHANNELS || outport < 0 || outport >= MusECore::MIDI_PORTS) return; int cdp = ctrlcanvas->getCurDrumPitch(); if(_ctrl->isPerNoteController() && cdp >= 0) { if(_track->type() == MusECore::Track::DRUM) { // Default to track port if -1 and track channel if -1. outport = _track->drummap()[cdp].port; if(outport == -1) outport = _track->outPort(); chan = _track->drummap()[cdp].channel; if(chan == -1) chan = _track->outChannel(); } } MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outport]; // Shouldn't happen, but... if(off || ival < _ctrl->minVal() || ival > _ctrl->maxVal()) ival = MusECore::CTRL_VAL_UNKNOWN; if(ival != MusECore::CTRL_VAL_UNKNOWN) // Auto bias... ival += _ctrl->bias(); MusECore::MidiPlayEvent ev(MusEGlobal::audio->curFrame(), outport, chan, MusECore::ME_CONTROLLER, _dnum, ival); mp->putEvent(ev); } //--------------------------------------------------------- // setController //--------------------------------------------------------- void CtrlPanel::setController() { if(!_track || !_ctrl) { buildPanel(); inHeartBeat = false; return; } MusECore::MidiPort* mp = &MusEGlobal::midiPorts[_track->outPort()]; int ch = _track->outChannel(); int cdp = ctrlcanvas->getCurDrumPitch(); _dnum = _ctrl->num(); if(_ctrl->isPerNoteController() && cdp >= 0) { if(_track->type() == MusECore::Track::DRUM) { _dnum = (_dnum & ~0xff) | _track->drummap()[cdp].anote; int mport = _track->drummap()[cdp].port; // Default to track port if -1 and track channel if -1. if(mport == -1) mport = _track->outPort(); mp = &MusEGlobal::midiPorts[mport]; ch = _track->drummap()[cdp].channel; if(ch == -1) ch = _track->outChannel(); } else if(_track->type() == MusECore::Track::MIDI) { _dnum = (_dnum & ~0xff) | cdp; //FINDMICHJETZT does that work? } } buildPanel(); if(_dnum == MusECore::CTRL_VELOCITY) { } else { MusECore::MidiCtrlValListList* mcvll = mp->controller(); int mn; int mx; int v; if(_dnum == MusECore::CTRL_PROGRAM) { if(_patchEdit) { MusECore::ciMidiCtrlValList imcvl = mcvll->find(ch, _dnum); if(imcvl != mcvll->end()) { MusECore::MidiCtrlValList* mcvl = imcvl->second; int hwVal = mcvl->hwVal(); // Special for new LCD patch edit control: Need to give both current and last values. // Keeping a local last value with the control won't work. _patchEdit->blockSignals(true); _patchEdit->setLastValidPatch(mcvl->lastValidHWVal()); _patchEdit->setLastValidBytes(mcvl->lastValidByte2(), mcvl->lastValidByte1(), mcvl->lastValidByte0()); _patchEdit->setValue(hwVal); _patchEdit->blockSignals(false); } } else { mn = 1; mx = 128; v = mp->hwCtrlState(ch, _dnum); if(_knob) _knob->setRange(double(mn), double(mx), 1.0); else if(_slider) _slider->setRange(double(mn), double(mx), 1.0); if(v == MusECore::CTRL_VAL_UNKNOWN || ((v & 0xffffff) == 0xffffff)) { int lastv = mp->lastValidHWCtrlState(ch, _dnum); if(lastv == MusECore::CTRL_VAL_UNKNOWN || ((lastv & 0xffffff) == 0xffffff)) { int initv = _ctrl->initVal(); if(initv == MusECore::CTRL_VAL_UNKNOWN || ((initv & 0xffffff) == 0xffffff)) v = 1; else v = (initv + 1) & 0xff; } else v = (lastv + 1) & 0xff; if(v > 128) v = 128; } else { v = (v + 1) & 0xff; if(v > 128) v = 128; } if(_knob) { _knob->setValue(double(v)); } else if(_slider) { _slider->setValue(double(v)); } } } else { mn = _ctrl->minVal(); mx = _ctrl->maxVal(); v = mp->hwCtrlState(ch, _dnum); if(_knob) _knob->setRange(double(mn), double(mx), 1.0); else if(_slider) _slider->setRange(double(mn), double(mx), 1.0); if(v == MusECore::CTRL_VAL_UNKNOWN) { int lastv = mp->lastValidHWCtrlState(ch, _dnum); if(lastv == MusECore::CTRL_VAL_UNKNOWN) { if(_ctrl->initVal() == MusECore::CTRL_VAL_UNKNOWN) v = 0; else v = _ctrl->initVal(); } else v = lastv - _ctrl->bias(); } else { // Auto bias... v -= _ctrl->bias(); } if(_knob) { _knob->setValue(double(v)); } else if(_slider) { _slider->setValue(double(v)); } } } setControlColor(); } void CtrlPanel::setControlColor() { if(_dnum == -1) return; QColor color = MusEGlobal::config.sliderBackgroundColor; switch(_dnum) { case MusECore::CTRL_PANPOT: color = MusEGlobal::config.panSliderColor; break; case MusECore::CTRL_PROGRAM: color = MusEGlobal::config.midiPatchReadoutColor; break; default: color = MusEGlobal::config.midiControllerSliderColor; break; } if(_patchEdit) { _patchEdit->setReadoutColor(color); // Colours in these sections were not updating with stylesheet colours, // because normally that's only done at creation, and we create these // things once and keep them around waiting to be shown. // Anyway, this trick recommended by help. Tested OK. // Good demonstration of how to apply a stylesheet after a property has been changed. style()->unpolish(_patchEdit); style()->polish(_patchEdit); } if(_knob) { _knob->setFaceColor(color); style()->unpolish(_knob); style()->polish(_knob); } if(_slider) { _slider->setBorderColor(color); _slider->setBarColor(MusEGlobal::config.sliderBarColor); style()->unpolish(_slider); style()->polish(_slider); } } //--------------------------------------------------------- // setHWController //--------------------------------------------------------- void CtrlPanel::setHWController(MusECore::MidiTrack* t, MusECore::MidiController* ctrl) { inHeartBeat = true; _track = t; _ctrl = ctrl; setController(); inHeartBeat = false; } //--------------------------------------------------------- // setHeight //--------------------------------------------------------- void CtrlPanel::setHeight(int h) { setFixedHeight(h); } //--------------------------------------------------- // ctrlPopup //--------------------------------------------------- void CtrlPanel::ctrlPopup() { MusECore::PartList* parts = editor->parts(); MusECore::Part* part = editor->curCanvasPart(); int curDrumPitch = ctrlcanvas->getCurDrumPitch(); PopupMenu* pup = new PopupMenu(true); // true = enable stay open. Don't bother with parent. int est_width = populateMidiCtrlMenu(pup, parts, part, curDrumPitch); QPoint ep = mapToGlobal(QPoint(0,0)); //int newx = ep.x() - ctrlMainPop->width(); // Too much! Width says 640. Maybe because it hasn't been shown yet . int newx = ep.x() - est_width; if(newx < 0) newx = 0; ep.setX(newx); connect(pup, SIGNAL(triggered(QAction*)), SLOT(ctrlPopupTriggered(QAction*))); pup->exec(ep); delete pup; selCtrl->setDown(false); } //--------------------------------------------------------- // ctrlPopupTriggered //--------------------------------------------------------- void CtrlPanel::ctrlPopupTriggered(QAction* act) { if(!act || (act->data().toInt() == -1)) return; MusECore::Part* part = editor->curCanvasPart(); MusECore::MidiTrack* track = (MusECore::MidiTrack*)(part->track()); int channel = track->outChannel(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; MusECore::MidiCtrlValListList* cll = port->controller(); const int min = channel << 24; const int max = min + 0x1000000; const int edit_ins = max + 3; const int velo = max + 0x101; int rv = act->data().toInt(); if (rv == velo) { // special case velocity emit controllerChanged(MusECore::CTRL_VELOCITY); } else if (rv == edit_ins) { // edit instrument MusECore::MidiInstrument* instr = port->instrument(); MusEGlobal::muse->startEditInstrument(instr ? instr->iname() : QString(), EditInstrumentControllers); } else { // Select a control MusECore::iMidiCtrlValList i = cll->find(channel, rv); if(i == cll->end()) { MusECore::MidiCtrlValList* vl = new MusECore::MidiCtrlValList(rv); cll->add(channel, vl); } int num = rv; if(port->drumController(rv)) num |= 0xff; emit controllerChanged(num); } } //--------------------------------------------------------- // ctrlRightClicked //--------------------------------------------------------- void CtrlPanel::ctrlRightClicked(const QPoint& p, int /*id*/) { if(!editor->curCanvasPart() || !_ctrl) return; int cdp = ctrlcanvas->getCurDrumPitch(); int ctlnum = _ctrl->num(); if(_track->isDrumTrack() && _ctrl->isPerNoteController() && cdp >= 0) //ctlnum = (ctlnum & ~0xff) | MusEGlobal::drumMap[cdp].enote; DELETETHIS or which of them is correct? ctlnum = (ctlnum & ~0xff) | cdp; MusECore::MidiPart* part = dynamic_cast(editor->curCanvasPart()); MusEGlobal::song->execMidiAutomationCtlPopup(0, part, p, ctlnum); } //--------------------------------------------------------- // velPerNoteClicked //--------------------------------------------------------- void CtrlPanel::velPerNoteClicked() { if(ctrlcanvas && _veloPerNoteButton && _veloPerNoteButton->isChecked() != ctrlcanvas->perNoteVeloMode()) ctrlcanvas->setPerNoteVeloMode(_veloPerNoteButton->isChecked()); } //--------------------------------------------------------- // setVeloPerNoteMode //--------------------------------------------------------- void CtrlPanel::setVeloPerNoteMode(bool v) { if(_veloPerNoteButton && v != _veloPerNoteButton->isChecked()) _veloPerNoteButton->setChecked(v); } } // namespace MusEGui muse-4.0.0/src/muse/ctrl/ctrlpanel.h000066400000000000000000000062571404105311000173240ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ctrlpanel.h,v 1.2.2.5 2009/06/10 00:34:59 terminator356 Exp $ // (C) Copyright 1999-2001 Werner Schweer (ws@seh.de) // (C) Copyright 2012, 2017 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CTRL_PANEL_H__ #define __CTRL_PANEL_H__ #include #include "ttoolbutton.h" #include "type_defs.h" // Forward declarations: class QHBoxLayout; class QVBoxLayout; class QSpacerItem; class QAction; namespace MusECore { class MidiController; class MidiPort; class MidiTrack; } namespace MusEGui { class MidiEditor; class CtrlCanvas; class CompactKnob; class CompactSlider; class LCDPatchEdit; //--------------------------------------------------------- // CtrlPanel //--------------------------------------------------------- class CtrlPanel: public QWidget { Q_OBJECT CompactToolButton* selCtrl; MidiEditor* editor; CtrlCanvas* ctrlcanvas; MusECore::MidiTrack* _track; MusECore::MidiController* _ctrl; int _dnum; bool inHeartBeat; QVBoxLayout* vbox; QHBoxLayout* kbox; QSpacerItem* lspacer; QSpacerItem* rspacer; CompactKnob* _knob; CompactSlider* _slider; LCDPatchEdit* _patchEdit; // Current local state of knobs versus sliders preference global setting. bool _preferKnobs; // Current local state of show values preference global setting. bool _showval; CompactToolButton* _veloPerNoteButton; void buildPanel(); void setController(); void setControlColor(); signals: void destroyPanel(); void controllerChanged(int); private slots: void patchCtrlChanged(int val); void ctrlChanged(double val, bool off, int id, int scrollMode); void ctrlRightClicked(const QPoint& p, int id); void ctrlPopupTriggered(QAction* act); void velPerNoteClicked(); void songChanged(MusECore::SongChangedStruct_t type); void configChanged(); protected slots: virtual void heartBeat(); public slots: void setHeight(int); void ctrlPopup(); void setVeloPerNoteMode(bool); public: CtrlPanel(QWidget*, MidiEditor*, CtrlCanvas*, const char* name = 0); void setHWController(MusECore::MidiTrack* t, MusECore::MidiController* ctrl); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/debug.h000066400000000000000000000041341404105311000154520ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: debug.h,v 1.1.1.1 2003/10/27 18:51:20 wschweer Exp $ // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MUSE_DEBUG_H__ #define __MUSE_DEBUG_H__ #include #include "globals.h" #ifdef DEBUG_1 #define M_REPORT(string) printf("%s:%d:%s: " string "\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); #define M_REPORTARG(format, args...) printf("%s:%d:%s: " format "\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, ##args); #define M_DEBUG(string) if (debugMsg) fprintf(stderr, "%s:%d:%s: " string "\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); #define M_DEBUGARG(format, args...) if (debugMsg) fprintf(stderr, "%s:%d:%s: " format "\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, ##args); #define M_ERROR(string) fprintf(stderr, "%s:%d:%s: " string "\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); #define M_ERRORARG(format, args...) fprintf(stderr, "%s:%d:%s: " format "\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, ##args); #else #define M_REPORT(string) #define M_REPORTARG(format, args...) #define M_DEBUG(string) #define M_DEBUGARG(format, args...) #define M_ERROR(string) #define M_ERRORARG(format, args...) #endif #define DEBUG_ARGS __FILE__ << ":" << __LINE__ << ":" << __PRETTY_FUNCTION__ << ": " #endif /* __MUSE_DEBUG_H__ */ muse-4.0.0/src/muse/default_click.h000066400000000000000000002271121404105311000171600ustar00rootroot00000000000000/* Copyright (C) 20002 Paul Davis 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; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. $Id: default_click.h,v 1.1.2.1 2004/11/15 23:25:51 spamatica Exp $ */ static const float defaultClick[] = { -0.0019836426, 0.0097045898, 0.019165039, 0.028594971, 0.036102295, 0.040679932, 0.040649414, 0.036560059, 0.029693604, 0.016967773, 0.0057067871, -0.008026123, -0.021453857, -0.026153564, -0.031921387, -0.036834717, -0.04397583, -0.046844482, -0.045349121, -0.041748047, -0.032623291, -0.026611328, -0.026702881, -0.025482178, -0.013092041, 0.0055847168, 0.023040771, 0.027435303, 0.043426514, 0.07925415, 0.1086731, 0.09463501, 0.039154053, -0.027832031, -0.081542969, -0.099090576, -0.089385986, -0.051055908, -0.0023803711, 0.031951904, 0.044128418, 0.029998779, 0.0086669922, 0.0022888184, 0.0082092285, 0.0085144043, -0.0057983398, -0.027496338, -0.036834717, -0.021759033, -0.020721436, -0.024078369, -0.025360107, -0.090698242, -0.16491699, -0.16052246, -0.090454102, 0.0031433105, 0.013031006, 0.034790039, 0.30599976, 0.58151245, 0.5078125, 0.32858276, 0.21975708, -0.050018311, -0.34790039, -0.46588135, -0.26513672, 0.12893677, 0.19259644, 0.11685181, 0.095825195, -0.039154053, -0.041473389, 0.0069274902, -0.14685059, -0.32232666, -0.21795654, 0.051086426, 0.18112183, 0.30905151, 0.47305298, 0.60980225, 0.59518433, 0.46920776, 0.51705933, 0.47116089, 0.31832886, 0.20901489, 0.059539795, -0.10055542, -0.23834229, -0.26708984, -0.27468872, -0.29516602, -0.22879028, -0.12854004, -0.064300537, -0.0068664551, 0.060211182, 0.10339355, 0.12594604, 0.12954712, 0.063568115, -0.041046143, -0.1184082, -0.19332886, -0.27337646, -0.34906006, -0.40966797, -0.46533203, -0.45361328, -0.39672852, -0.32958984, -0.15246582, 0.086853027, 0.23678589, 0.24838257, 0.13034058, -0.15222168, -0.5199585, -0.80224609, -0.9397583, -0.89654541, -0.71923828, -0.47998047, -0.14666748, 0.26187134, 0.54995728, 0.69186401, 0.84616089, 0.79928589, 0.53170776, 0.39968872, 0.32455444, 0.2137146, 0.19454956, 0.24069214, 0.22750854, 0.14596558, 0.047058105, -0.04876709, -0.13095093, -0.19775391, -0.16293335, -0.059265137, 0.027526855, 0.13961792, 0.26803589, 0.35470581, 0.37600708, 0.34085083, 0.2572937, 0.13265991, -0.008026123, -0.12841797, -0.22216797, -0.30899048, -0.37841797, -0.4251709, -0.45947266, -0.48376465, -0.50341797, -0.52294922, -0.54516602, -0.58447266, -0.62121582, -0.63989258, -0.64990234, -0.63037109, -0.568573, -0.46630859, -0.36468506, -0.27618408, -0.18487549, -0.11065674, -0.070404053, -0.066101074, -0.049835205, -0.026611328, -0.013061523, 0.011444092, 0.034576416, 0.046112061, 0.052215576, 0.045501709, 0.030059814, 0.011657715, 0.026489258, 0.056121826, 0.10122681, 0.20016479, 0.33053589, 0.45944214, 0.54830933, 0.57751465, 0.5625, 0.55612183, 0.55319214, 0.5697937, 0.64987183, 0.73553467, 0.78170776, 0.84420776, 0.89987183, 0.90475464, 0.85861206, 0.75338745, 0.57077026, 0.3354187, 0.067779541, -0.1741333, -0.36557007, -0.47802734, -0.51281738, -0.47766113, -0.36865234, -0.22381592, -0.065765381, 0.066680908, 0.12365723, 0.098114014, 0.020965576, -0.10775757, -0.27294922, -0.41845703, -0.50146484, -0.52563477, -0.48291016, -0.38186646, -0.27264404, -0.16925049, -0.07043457, 0.003326416, 0.025085449, -0.028320312, -0.11358643, -0.18164062, -0.21984863, -0.21286011, -0.16635132, -0.12728882, -0.088104248, 0.016693115, 0.18893433, 0.40280151, 0.59127808, 0.67233276, 0.64682007, 0.54733276, 0.4213562, 0.30703735, 0.19845581, 0.12661743, 0.095611572, 0.10531616, 0.17950439, 0.29904175, 0.41726685, 0.47506714, 0.48678589, 0.48873901, 0.46694946, 0.42233276, 0.38088989, 0.34689331, 0.30709839, 0.26022339, 0.18258667, 0.064544678, -0.09085083, -0.25634766, -0.41540527, -0.54693604, -0.62731934, -0.6809082, -0.70849609, -0.71044922, -0.68469238, -0.64886475, -0.62115479, -0.6104126, -0.62451172, -0.65185547, -0.67504883, -0.68969727, -0.70257568, -0.71337891, -0.68994141, -0.61865234, -0.49755859, -0.32177734, -0.10437012, 0.11209106, 0.30691528, 0.46789551, 0.51602173, 0.44674683, 0.32055664, 0.1769104, 0.06072998, 0.0075378418, -0.013641357, -0.0038146973, 0.082244873, 0.22116089, 0.35787964, 0.47689819, 0.60266113, 0.71801758, 0.81747437, 0.91799927, 0.97079468, 0.97677612, 0.93850708, 0.83322144, 0.65866089, 0.42324829, 0.14834595, -0.10574341, -0.31484985, -0.46813965, -0.54833984, -0.55517578, -0.51220703, -0.43115234, -0.34613037, -0.29052734, -0.2699585, -0.29443359, -0.33349609, -0.38720703, -0.45751953, -0.51318359, -0.54800415, -0.55706787, -0.5402832, -0.48666382, -0.41162109, -0.32958984, -0.24560547, -0.19873047, -0.18457031, -0.19171143, -0.21240234, -0.23876953, -0.25823975, -0.26184082, -0.24902344, -0.21612549, -0.16552734, -0.11083984, -0.058349609, -0.0034179688, 0.053283691, 0.12722778, 0.23052979, 0.35769653, 0.51147461, 0.68243408, 0.85015869, 0.96902466, 0.99908447, 0.99899292, 0.99453735, 0.87936401, 0.72964478, 0.59124756, 0.45162964, 0.29949951, 0.18435669, 0.087219238, 0.0085754395, -0.031677246, -0.057434082, -0.06338501, -0.050262451, -0.021179199, 0.010986328, 0.035095215, 0.041900635, 0.026275635, -0.008972168, -0.067260742, -0.14938354, -0.24298096, -0.33901978, -0.44342041, -0.53179932, -0.59924316, -0.64294434, -0.66220093, -0.65362549, -0.61437988, -0.56277466, -0.48764038, -0.41287231, -0.3420105, -0.25909424, -0.17782593, -0.10604858, -0.033355713, 0.037872314, 0.097198486, 0.15203857, 0.19644165, 0.23324585, 0.2668457, 0.29403687, 0.31442261, 0.33148193, 0.33639526, 0.32794189, 0.31109619, 0.30651855, 0.30456543, 0.31005859, 0.32266235, 0.34017944, 0.36062622, 0.37988281, 0.39459229, 0.40344238, 0.41464233, 0.41500854, 0.41091919, 0.40512085, 0.39819336, 0.3843689, 0.36633301, 0.33621216, 0.28076172, 0.19436646, 0.083892822, -0.056732178, -0.21832275, -0.38809204, -0.55502319, -0.70330811, -0.82672119, -0.909729, -0.93505859, -0.9085083, -0.84347534, -0.74786377, -0.63342285, -0.52468872, -0.42883301, -0.35321045, -0.29504395, -0.26339722, -0.23782349, -0.21252441, -0.19006348, -0.16159058, -0.12472534, -0.075958252, -0.012115479, 0.056091309, 0.11953735, 0.17373657, 0.22116089, 0.25097656, 0.25982666, 0.2539978, 0.24578857, 0.24325562, 0.25292969, 0.28744507, 0.34677124, 0.42510986, 0.51596069, 0.60205078, 0.67550659, 0.71774292, 0.728302, 0.70874023, 0.6612854, 0.59188843, 0.49536133, 0.37322998, 0.23904419, 0.10256958, -0.030303955, -0.15093994, -0.24526978, -0.31253052, -0.3414917, -0.33178711, -0.29302979, -0.23806763, -0.17816162, -0.13470459, -0.12387085, -0.15142822, -0.20706177, -0.28427124, -0.37112427, -0.44927979, -0.50619507, -0.53219604, -0.53118896, -0.49990845, -0.45379639, -0.39599609, -0.31643677, -0.22329712, -0.15414429, -0.1050415, -0.075592041, -0.0753479, -0.088775635, -0.099090576, -0.086639404, -0.035797119, 0.052154541, 0.1661377, 0.28082275, 0.39169312, 0.50491333, 0.59762573, 0.67288208, 0.71621704, 0.73251343, 0.71405029, 0.66171265, 0.60040283, 0.53762817, 0.46520996, 0.3727417, 0.26971436, 0.17245483, 0.092346191, 0.034851074, 0.0072937012, 0.007232666, 0.017547607, 0.01852417, 0.020172119, 0.023345947, 0.014434814, -0.013946533, -0.054168701, -0.094726562, -0.14425659, -0.20623779, -0.27316284, -0.34069824, -0.40481567, -0.4647522, -0.51278687, -0.55215454, -0.57962036, -0.58953857, -0.59140015, -0.57885742, -0.55456543, -0.51055908, -0.45095825, -0.37948608, -0.29974365, -0.22027588, -0.13543701, -0.047271729, 0.033721924, 0.1005249, 0.14877319, 0.18429565, 0.20465088, 0.21621704, 0.2272644, 0.23416138, 0.24511719, 0.26138306, 0.2805481, 0.3006897, 0.32943726, 0.36660767, 0.40802002, 0.44424438, 0.47698975, 0.50256348, 0.52005005, 0.5267334, 0.52258301, 0.50723267, 0.47485352, 0.42205811, 0.34805298, 0.26202393, 0.16522217, 0.067749023, -0.024169922, -0.1060791, -0.18280029, -0.25424194, -0.31921387, -0.37774658, -0.43612671, -0.50094604, -0.56121826, -0.61495972, -0.66091919, -0.69436646, -0.70004272, -0.67868042, -0.63104248, -0.56182861, -0.48074341, -0.39840698, -0.31900024, -0.24609375, -0.18695068, -0.1461792, -0.11993408, -0.11032104, -0.10159302, -0.091918945, -0.076904297, -0.069580078, -0.058898926, -0.038146973, -0.012542725, 0.014831543, 0.043304443, 0.082824707, 0.13180542, 0.1895752, 0.25256348, 0.32229614, 0.38531494, 0.43493652, 0.4697876, 0.48736572, 0.48733521, 0.46533203, 0.42901611, 0.38223267, 0.33636475, 0.2819519, 0.22467041, 0.16455078, 0.10604858, 0.047485352, -0.010040283, -0.057098389, -0.093933105, -0.12252808, -0.14291382, -0.15545654, -0.16064453, -0.16348267, -0.16918945, -0.17095947, -0.17773438, -0.19436646, -0.21868896, -0.25158691, -0.2890625, -0.33297729, -0.37677002, -0.41171265, -0.43563843, -0.44619751, -0.44320679, -0.42520142, -0.39151001, -0.34609985, -0.29324341, -0.23846436, -0.18026733, -0.12319946, -0.068023682, -0.017944336, 0.033691406, 0.08416748, 0.13388062, 0.18310547, 0.23513794, 0.28579712, 0.3314209, 0.37133789, 0.40841675, 0.44143677, 0.46252441, 0.47073364, 0.46435547, 0.44342041, 0.40725708, 0.35971069, 0.30911255, 0.26272583, 0.22372437, 0.19876099, 0.18948364, 0.18948364, 0.19799805, 0.21658325, 0.23733521, 0.25439453, 0.26513672, 0.26702881, 0.25582886, 0.2338562, 0.19677734, 0.15100098, 0.097106934, 0.036804199, -0.02456665, -0.087005615, -0.14819336, -0.20632935, -0.26040649, -0.30117798, -0.33422852, -0.35610962, -0.36178589, -0.3522644, -0.3237915, -0.28857422, -0.24789429, -0.20108032, -0.14822388, -0.097442627, -0.055877686, -0.019500732, 0.0064086914, 0.024627686, 0.033721924, 0.042114258, 0.052032471, 0.069763184, 0.094207764, 0.12030029, 0.14993286, 0.18215942, 0.22003174, 0.26171875, 0.30029297, 0.33355713, 0.35992432, 0.3833313, 0.40148926, 0.41094971, 0.40899658, 0.39849854, 0.37387085, 0.34091187, 0.30215454, 0.26016235, 0.2170105, 0.17681885, 0.13604736, 0.093658447, 0.049316406, 0.0074462891, -0.037322998, -0.086120605, -0.13262939, -0.18286133, -0.22872925, -0.26879883, -0.3026123, -0.33206177, -0.35943604, -0.37924194, -0.39431763, -0.40466309, -0.40936279, -0.40270996, -0.38815308, -0.36676025, -0.3421936, -0.31149292, -0.27597046, -0.24679565, -0.22177124, -0.20294189, -0.19259644, -0.18527222, -0.18157959, -0.17645264, -0.16998291, -0.1524353, -0.12554932, -0.086669922, -0.03894043, 0.016204834, 0.075744629, 0.13665771, 0.18896484, 0.23040771, 0.26107788, 0.28408813, 0.29443359, 0.29440308, 0.28460693, 0.27456665, 0.26107788, 0.24020386, 0.21322632, 0.18551636, 0.15698242, 0.12554932, 0.095855713, 0.069152832, 0.045501709, 0.020141602, -0.0030517578, -0.026702881, -0.0496521, -0.071960449, -0.098327637, -0.12268066, -0.14907837, -0.17523193, -0.20068359, -0.22494507, -0.2482605, -0.27352905, -0.29666138, -0.31420898, -0.32751465, -0.33670044, -0.33874512, -0.32943726, -0.31417847, -0.29504395, -0.26898193, -0.23904419, -0.20245361, -0.16390991, -0.1210022, -0.078308105, -0.039337158, -0.0037231445, 0.029174805, 0.05847168, 0.084106445, 0.1078186, 0.13174438, 0.15719604, 0.17941284, 0.20126343, 0.22348022, 0.2432251, 0.2578125, 0.2640686, 0.26593018, 0.25933838, 0.24685669, 0.2288208, 0.21047974, 0.19494629, 0.18075562, 0.17199707, 0.17199707, 0.17175293, 0.1729126, 0.17126465, 0.16873169, 0.16256714, 0.15036011, 0.1355896, 0.11254883, 0.083465576, 0.050079346, 0.01159668, -0.030029297, -0.074707031, -0.11532593, -0.15432739, -0.18942261, -0.21673584, -0.23681641, -0.24554443, -0.24703979, -0.24472046, -0.23815918, -0.22784424, -0.21243286, -0.19384766, -0.17596436, -0.15844727, -0.14089966, -0.12030029, -0.099395752, -0.076538086, -0.058746338, -0.034484863, -0.0097351074, 0.015625, 0.037445068, 0.063079834, 0.087738037, 0.11401367, 0.13973999, 0.16848755, 0.19680786, 0.2244873, 0.25567627, 0.28305054, 0.30465698, 0.31854248, 0.32287598, 0.31808472, 0.30102539, 0.27633667, 0.24591064, 0.20962524, 0.17144775, 0.13021851, 0.09487915, 0.057861328, 0.023651123, -0.014129639, -0.051239014, -0.086639404, -0.12316895, -0.15304565, -0.18545532, -0.21154785, -0.23434448, -0.25100708, -0.26113892, -0.26712036, -0.26678467, -0.26687622, -0.262146, -0.25656128, -0.24734497, -0.23501587, -0.22433472, -0.20773315, -0.19207764, -0.17459106, -0.15997314, -0.14196777, -0.12670898, -0.1166687, -0.10726929, -0.099243164, -0.090179443, -0.078521729, -0.062225342, -0.038360596, -0.0068054199, 0.031402588, 0.072143555, 0.11181641, 0.15362549, 0.19085693, 0.22195435, 0.24661255, 0.26379395, 0.27166748, 0.27072144, 0.26507568, 0.25247192, 0.2348938, 0.21447754, 0.18756104, 0.15771484, 0.13241577, 0.10873413, 0.087036133, 0.065460205, 0.046875, 0.029754639, 0.013153076, -0.0065917969, -0.024993896, -0.046051025, -0.067810059, -0.092285156, -0.11779785, -0.14529419, -0.17376709, -0.19503784, -0.21487427, -0.23205566, -0.24533081, -0.25128174, -0.2522583, -0.24685669, -0.23486328, -0.21954346, -0.19946289, -0.1786499, -0.15280151, -0.12744141, -0.098022461, -0.069091797, -0.038146973, -0.0079040527, 0.024719238, 0.054351807, 0.082275391, 0.11019897, 0.13510132, 0.15545654, 0.17276001, 0.1854248, 0.19042969, 0.19281006, 0.1930542, 0.19232178, 0.18795776, 0.17938232, 0.17156982, 0.16223145, 0.15603638, 0.15182495, 0.15118408, 0.14651489, 0.14263916, 0.13916016, 0.1343689, 0.12649536, 0.11480713, 0.10400391, 0.091766357, 0.079589844, 0.063781738, 0.04776001, 0.029510498, 0.013061523, -0.0087890625, -0.034454346, -0.06362915, -0.097381592, -0.12896729, -0.15893555, -0.18429565, -0.20910645, -0.22930908, -0.2411499, -0.25009155, -0.2505188, -0.24719238, -0.23956299, -0.22445679, -0.20892334, -0.18774414, -0.16699219, -0.14205933, -0.12145996, -0.10317993, -0.085449219, -0.066101074, -0.050048828, -0.03326416, -0.014404297, 0.0099182129, 0.041809082, 0.077087402, 0.11599731, 0.14718628, 0.18060303, 0.20654297, 0.23117065, 0.25247192, 0.27026367, 0.28042603, 0.27871704, 0.27175903, 0.25527954, 0.2315979, 0.20584106, 0.17654419, 0.1446228, 0.1086731, 0.073699951, 0.040588379, 0.011627197, -0.016143799, -0.044189453, -0.071838379, -0.098449707, -0.12316895, -0.14602661, -0.16595459, -0.17871094, -0.18606567, -0.19488525, -0.20278931, -0.209198, -0.21121216, -0.21276855, -0.21395874, -0.21328735, -0.21072388, -0.20718384, -0.20404053, -0.19812012, -0.19073486, -0.17901611, -0.16781616, -0.15267944, -0.13824463, -0.12042236, -0.098999023, -0.077453613, -0.053497314, -0.029022217, -0.001373291, 0.022583008, 0.050720215, 0.077026367, 0.10748291, 0.13626099, 0.16424561, 0.18908691, 0.21191406, 0.2300415, 0.2428894, 0.25292969, 0.25643921, 0.25924683, 0.25518799, 0.24127197, 0.22161865, 0.19989014, 0.17800903, 0.14935303, 0.12268066, 0.094787598, 0.068878174, 0.041046143, 0.018585205, 0.0020141602, -0.0094604492, -0.01651001, -0.02243042, -0.031860352, -0.049102783, -0.072113037, -0.098083496, -0.12472534, -0.14935303, -0.17062378, -0.18566895, -0.19500732, -0.19784546, -0.19299316, -0.18365479, -0.17153931, -0.1555481, -0.13491821, -0.11233521, -0.090209961, -0.063781738, -0.038879395, -0.017089844, 0.0027770996, 0.021697998, 0.041046143, 0.059844971, 0.078674316, 0.093536377, 0.10656738, 0.11816406, 0.12698364, 0.13323975, 0.13742065, 0.14559937, 0.1522522, 0.15563965, 0.15548706, 0.1583252, 0.15756226, 0.15576172, 0.15216064, 0.1479187, 0.14260864, 0.13577271, 0.1272583, 0.11904907, 0.10968018, 0.10412598, 0.096618652, 0.087615967, 0.07522583, 0.059265137, 0.039978027, 0.017852783, -0.0061950684, -0.029510498, -0.05065918, -0.075500488, -0.099395752, -0.12225342, -0.1418457, -0.16101074, -0.17797852, -0.1932373, -0.20556641, -0.20901489, -0.21026611, -0.20684814, -0.20016479, -0.18768311, -0.17312622, -0.15994263, -0.14834595, -0.13482666, -0.11865234, -0.10229492, -0.083404541, -0.063079834, -0.039276123, -0.014129639, 0.011199951, 0.035583496, 0.059417725, 0.079925537, 0.098571777, 0.11416626, 0.1270752, 0.14013672, 0.15286255, 0.16619873, 0.17810059, 0.18783569, 0.19454956, 0.19598389, 0.19143677, 0.18264771, 0.16867065, 0.15258789, 0.13143921, 0.10482788, 0.075073242, 0.045928955, 0.018371582, -0.0075683594, -0.032928467, -0.054992676, -0.072662354, -0.089813232, -0.10479736, -0.12127686, -0.13748169, -0.15161133, -0.16473389, -0.17370605, -0.18240356, -0.18569946, -0.18682861, -0.18756104, -0.18310547, -0.18099976, -0.17584229, -0.17102051, -0.16162109, -0.15145874, -0.1421814, -0.12973022, -0.11398315, -0.096557617, -0.078460693, -0.061035156, -0.042541504, -0.023590088, -0.0029296875, 0.020996094, 0.044403076, 0.068237305, 0.092926025, 0.11682129, 0.13772583, 0.15460205, 0.17050171, 0.18438721, 0.19360352, 0.19952393, 0.20303345, 0.2048645, 0.20132446, 0.1930542, 0.18280029, 0.16989136, 0.1546936, 0.13711548, 0.12115479, 0.10336304, 0.087890625, 0.068023682, 0.049804688, 0.028320312, 0.0081787109, -0.012664795, -0.030334473, -0.046783447, -0.061584473, -0.073883057, -0.084655762, -0.093719482, -0.10314941, -0.11251831, -0.12094116, -0.1300354, -0.13421631, -0.13647461, -0.13491821, -0.13186646, -0.12530518, -0.11520386, -0.10275269, -0.088775635, -0.074310303, -0.061462402, -0.046875, -0.032409668, -0.014892578, 0.00057983398, 0.016967773, 0.031738281, 0.0440979, 0.054656982, 0.063659668, 0.073791504, 0.081176758, 0.088562012, 0.095916748, 0.10043335, 0.10443115, 0.10519409, 0.1076355, 0.1071167, 0.10748291, 0.10601807, 0.1026001, 0.10031128, 0.095123291, 0.08972168, 0.082763672, 0.075164795, 0.06729126, 0.057922363, 0.049865723, 0.042144775, 0.035125732, 0.026367188, 0.016571045, 0.0040893555, -0.0084228516, -0.025054932, -0.042053223, -0.060974121, -0.077178955, -0.093902588, -0.10845947, -0.1229248, -0.13531494, -0.14489746, -0.15274048, -0.15701294, -0.15966797, -0.15670776, -0.15130615, -0.140625, -0.12561035, -0.10900879, -0.091644287, -0.077362061, -0.063690186, -0.051879883, -0.04083252, -0.028869629, -0.017059326, -0.0053710938, 0.0051574707, 0.020172119, 0.034484863, 0.051361084, 0.067810059, 0.085540771, 0.10223389, 0.11761475, 0.13146973, 0.14044189, 0.1468811, 0.14923096, 0.14846802, 0.14407349, 0.13641357, 0.12796021, 0.11773682, 0.1076355, 0.09463501, 0.081207275, 0.065124512, 0.050323486, 0.035552979, 0.020965576, 0.0063476562, -0.0094299316, -0.024810791, -0.040008545, -0.054534912, -0.0703125, -0.084228516, -0.096862793, -0.10809326, -0.1171875, -0.12515259, -0.13067627, -0.13568115, -0.13632202, -0.13449097, -0.131073, -0.12503052, -0.11868286, -0.11099243, -0.10317993, -0.095306396, -0.087158203, -0.077667236, -0.068115234, -0.059082031, -0.047241211, -0.033966064, -0.019805908, -0.0041503906, 0.012573242, 0.030029297, 0.047637939, 0.063751221, 0.079162598, 0.092987061, 0.1055603, 0.11737061, 0.12628174, 0.13504028, 0.14266968, 0.14840698, 0.1519165, 0.15158081, 0.14685059, 0.14041138, 0.13082886, 0.11962891, 0.10525513, 0.089599609, 0.072784424, 0.057556152, 0.044464111, 0.032470703, 0.02053833, 0.0086364746, -0.0019226074, -0.013336182, -0.026184082, -0.039398193, -0.052368164, -0.064605713, -0.076293945, -0.086303711, -0.09475708, -0.10128784, -0.10568237, -0.10748291, -0.10894775, -0.10787964, -0.10577393, -0.10229492, -0.097198486, -0.090087891, -0.081695557, -0.07244873, -0.062927246, -0.051940918, -0.041748047, -0.028839111, -0.015960693, -0.0022277832, 0.011779785, 0.024932861, 0.038482666, 0.048614502, 0.059326172, 0.066925049, 0.072021484, 0.077026367, 0.079681396, 0.082733154, 0.082855225, 0.081481934, 0.08001709, 0.079833984, 0.079345703, 0.079162598, 0.078582764, 0.078521729, 0.078582764, 0.077575684, 0.073059082, 0.065612793, 0.055480957, 0.044189453, 0.031768799, 0.019744873, 0.008972168, -0.00094604492, -0.0081176758, -0.013885498, -0.018463135, -0.025634766, -0.032623291, -0.041442871, -0.05279541, -0.0652771, -0.078399658, -0.088256836, -0.098297119, -0.10681152, -0.112854, -0.11355591, -0.11132812, -0.10617065, -0.098449707, -0.087738037, -0.077606201, -0.068054199, -0.059051514, -0.051330566, -0.041503906, -0.031066895, -0.019470215, -0.0077514648, 0.0030517578, 0.014892578, 0.026062012, 0.038879395, 0.050079346, 0.060028076, 0.069793701, 0.076568604, 0.082611084, 0.088989258, 0.094360352, 0.098754883, 0.10293579, 0.10635376, 0.10720825, 0.10586548, 0.10314941, 0.0987854, 0.093475342, 0.087432861, 0.079406738, 0.070159912, 0.059204102, 0.048095703, 0.036529541, 0.023071289, 0.010406494, -0.0024719238, -0.01361084, -0.026184082, -0.036682129, -0.047180176, -0.056213379, -0.065765381, -0.07510376, -0.082702637, -0.091064453, -0.09677124, -0.10232544, -0.10546875, -0.1072998, -0.10693359, -0.10385132, -0.10055542, -0.093078613, -0.085235596, -0.075439453, -0.065643311, -0.054138184, -0.042449951, -0.029785156, -0.017822266, -0.0068359375, 0.0038452148, 0.01373291, 0.023773193, 0.033081055, 0.042053223, 0.049560547, 0.057952881, 0.066223145, 0.075775146, 0.083190918, 0.08984375, 0.096191406, 0.10202026, 0.10595703, 0.10742188, 0.10778809, 0.10528564, 0.1020813, 0.096069336, 0.08883667, 0.07925415, 0.069458008, 0.057952881, 0.047454834, 0.036804199, 0.026397705, 0.015777588, 0.004119873, -0.0077819824, -0.019927979, -0.030822754, -0.040405273, -0.048980713, -0.056610107, -0.062805176, -0.069366455, -0.074035645, -0.077026367, -0.079406738, -0.079986572, -0.080322266, -0.078338623, -0.07598877, -0.072479248, -0.067230225, -0.061218262, -0.055847168, -0.050048828, -0.043823242, -0.037536621, -0.030792236, -0.02230835, -0.012634277, -0.0030822754, 0.0069580078, 0.016204834, 0.023040771, 0.03024292, 0.036865234, 0.043518066, 0.051025391, 0.056396484, 0.061645508, 0.063720703, 0.065185547, 0.065032959, 0.0652771, 0.06439209, 0.06451416, 0.063598633, 0.062011719, 0.058654785, 0.055297852, 0.053131104, 0.051208496, 0.048950195, 0.046905518, 0.042175293, 0.035675049, 0.028411865, 0.019622803, 0.0091247559, -0.0016174316, -0.012207031, -0.022521973, -0.031616211, -0.040618896, -0.047302246, -0.052734375, -0.057098389, -0.060455322, -0.064849854, -0.068664551, -0.07321167, -0.074829102, -0.07623291, -0.075378418, -0.072814941, -0.069152832, -0.064819336, -0.059417725, -0.052368164, -0.044769287, -0.036712646, -0.027679443, -0.018005371, -0.0093383789, 0.0010375977, 0.011474609, 0.022399902, 0.03125, 0.03793335, 0.042449951, 0.045379639, 0.048065186, 0.050567627, 0.054260254, 0.058166504, 0.062316895, 0.066436768, 0.068786621, 0.072113037, 0.074707031, 0.077453613, 0.076507568, 0.074523926, 0.071838379, 0.06729126, 0.061126709, 0.053771973, 0.045410156, 0.037078857, 0.027862549, 0.019134521, 0.0096435547, 0.00079345703, -0.0076293945, -0.016296387, -0.024810791, -0.034454346, -0.043334961, -0.052703857, -0.06048584, -0.066101074, -0.070739746, -0.07220459, -0.073394775, -0.072967529, -0.072509766, -0.072296143, -0.070404053, -0.06842041, -0.064819336, -0.062072754, -0.058624268, -0.05380249, -0.048400879, -0.039764404, -0.031280518, -0.021881104, -0.012786865, -0.0053100586, 0.0022888184, 0.0081176758, 0.014038086, 0.020568848, 0.026885986, 0.032867432, 0.037628174, 0.042999268, 0.046722412, 0.052276611, 0.056884766, 0.061584473, 0.065124512, 0.066955566, 0.068054199, 0.067565918, 0.066589355, 0.06362915, 0.058410645, 0.052764893, 0.045349121, 0.036987305, 0.029144287, 0.021606445, 0.015197754, 0.0087280273, 0.0020446777, -0.004699707, -0.012115479, -0.018920898, -0.02633667, -0.032897949, -0.037841797, -0.042205811, -0.046783447, -0.050567627, -0.055114746, -0.05847168, -0.061004639, -0.061401367, -0.059173584, -0.056915283, -0.053955078, -0.049255371, -0.043121338, -0.037384033, -0.032501221, -0.027709961, -0.023468018, -0.018951416, -0.01385498, -0.0096740723, -0.005279541, -0.0010986328, 0.0033874512, 0.0088806152, 0.01348877, 0.01953125, 0.025024414, 0.031097412, 0.036895752, 0.0418396, 0.045837402, 0.048492432, 0.050231934, 0.051208496, 0.052490234, 0.052856445, 0.053344727, 0.052581787, 0.051452637, 0.049224854, 0.046356201, 0.042938232, 0.038543701, 0.034362793, 0.029602051, 0.025726318, 0.021606445, 0.017272949, 0.013641357, 0.009185791, 0.0037841797, -0.0022888184, -0.0105896, -0.019439697, -0.027709961, -0.034454346, -0.039642334, -0.044464111, -0.046569824, -0.048400879, -0.047546387, -0.046142578, -0.043395996, -0.040405273, -0.037994385, -0.035217285, -0.03414917, -0.031890869, -0.029205322, -0.026306152, -0.021820068, -0.018035889, -0.01361084, -0.0084228516, -0.001373291, 0.0057067871, 0.011962891, 0.018157959, 0.022766113, 0.02722168, 0.030090332, 0.032928467, 0.03503418, 0.036895752, 0.039001465, 0.04107666, 0.042816162, 0.04397583, 0.045074463, 0.045562744, 0.045654297, 0.045532227, 0.045135498, 0.043518066, 0.040618896, 0.036437988, 0.032012939, 0.027099609, 0.021911621, 0.016296387, 0.010955811, 0.006072998, 0.00048828125, -0.0053100586, -0.011535645, -0.017303467, -0.023529053, -0.028717041, -0.03427124, -0.038604736, -0.042572021, -0.045074463, -0.046661377, -0.047851562, -0.04776001, -0.047668457, -0.046691895, -0.045379639, -0.04385376, -0.042297363, -0.040435791, -0.037322998, -0.033691406, -0.029205322, -0.024749756, -0.019775391, -0.014556885, -0.0090942383, -0.0034790039, 0.0011901855, 0.0063171387, 0.0095214844, 0.013519287, 0.016174316, 0.019622803, 0.022094727, 0.024627686, 0.027435303, 0.029876709, 0.032958984, 0.035858154, 0.038208008, 0.039398193, 0.040374756, 0.040222168, 0.039978027, 0.039154053, 0.037811279, 0.034820557, 0.030822754, 0.025695801, 0.020721436, 0.015991211, 0.010864258, 0.006439209, 0.0018005371, -0.002166748, -0.0062561035, -0.0096740723, -0.013244629, -0.01663208, -0.019927979, -0.023742676, -0.026611328, -0.029907227, -0.032684326, -0.035064697, -0.037261963, -0.037811279, -0.037902832, -0.036437988, -0.033233643, -0.029876709, -0.026031494, -0.021759033, -0.017456055, -0.014251709, -0.011566162, -0.0094909668, -0.0071105957, -0.0048217773, -0.0027770996, 0.00036621094, 0.0036010742, 0.0076904297, 0.01159668, 0.015350342, 0.019226074, 0.021697998, 0.024749756, 0.026947021, 0.029266357, 0.030853271, 0.032104492, 0.032470703, 0.032165527, 0.03225708, 0.031860352, 0.03213501, 0.032104492, 0.03213501, 0.031860352, 0.031005859, 0.029205322, 0.026550293, 0.023406982, 0.01940918, 0.015258789, 0.010437012, 0.005859375, 0.0013122559, -0.003112793, -0.0067443848, -0.010040283, -0.01361084, -0.016662598, -0.020324707, -0.023193359, -0.026062012, -0.028503418, -0.029663086, -0.030487061, -0.030059814, -0.029418945, -0.027374268, -0.025024414, -0.022857666, -0.0206604, -0.018585205, -0.016113281, -0.013397217, -0.010192871, -0.0072021484, -0.0043029785, -0.002166748, 0, 0.0024414062, 0.005065918, 0.0079345703, 0.01071167, 0.012878418, 0.015380859, 0.017669678, 0.019958496, 0.021575928, 0.023284912, 0.024078369, 0.024719238, 0.025024414, 0.02557373, 0.02633667, 0.026275635, 0.02520752, 0.024017334, 0.022369385, 0.021057129, 0.019622803, 0.017578125, 0.015258789, 0.012054443, 0.0086975098, 0.0051269531, 0.0020141602, -0.00082397461, -0.00390625, -0.0068054199, -0.010406494, -0.014129639, -0.017425537, -0.020385742, -0.02230835, -0.025054932, -0.026916504, -0.028961182, -0.029724121, -0.029937744, -0.029907227, -0.02911377, -0.027770996, -0.025238037, -0.023010254, -0.019714355, -0.016540527, -0.013641357, -0.011077881, -0.008605957, -0.0062866211, -0.0043945312, -0.0021972656, -0.00067138672, 0.0012512207, 0.0020751953, 0.0036315918, 0.0053405762, 0.0081481934, 0.011352539, 0.014709473, 0.017944336, 0.020202637, 0.022003174, 0.023742676, 0.02520752, 0.026031494, 0.026062012, 0.025421143, 0.024261475, 0.022766113, 0.020904541, 0.019287109, 0.017852783, 0.017333984, 0.017181396, 0.016357422, 0.014770508, 0.012298584, 0.0095825195, 0.0057373047, 0.0017089844, -0.0032043457, -0.0075073242, -0.011260986, -0.014068604, -0.016021729, -0.017608643, -0.0184021, -0.019104004, -0.019775391, -0.020507812, -0.020721436, -0.020355225, -0.019348145, -0.017974854, -0.016235352, -0.014648438, -0.013183594, -0.011657715, -0.010498047, -0.0097961426, -0.0091552734, -0.0082702637, -0.0070800781, -0.0055236816, -0.003112793, -0.00054931641, 0.0024719238, 0.0049743652, 0.0075073242, 0.0095825195, 0.011352539, 0.012512207, 0.013580322, 0.014251709, 0.015045166, 0.016113281, 0.017089844, 0.018096924, 0.018890381, 0.019897461, 0.020629883, 0.020965576, 0.020782471, 0.019744873, 0.018371582, 0.016113281, 0.014068604, 0.011260986, 0.0094604492, 0.0072631836, 0.0054016113, 0.0033874512, 0.0014343262, -0.0007019043, -0.0032958984, -0.0055847168, -0.0082397461, -0.010650635, -0.013275146, -0.015167236, -0.016662598, -0.017852783, -0.018218994, -0.018157959, -0.017486572, -0.017181396, -0.01675415, -0.01651001, -0.015808105, -0.015014648, -0.01385498, -0.012390137, -0.01083374, -0.0090332031, -0.0072937012, -0.0054626465, -0.0038757324, -0.0022888184, -0.00051879883, 0.0011901855, 0.0027770996, 0.0043334961, 0.0057067871, 0.0071716309, 0.0084533691, 0.010101318, 0.011047363, 0.011993408, 0.012969971, 0.013092041, 0.013427734, 0.013641357, 0.013946533, 0.014221191, 0.014099121, 0.01385498, 0.012756348, 0.012054443, 0.011322021, 0.010162354, 0.0087280273, 0.0073547363, 0.0061950684, 0.0046386719, 0.0029602051, 0.0014953613, -0.00033569336, -0.0021362305, -0.0043640137, -0.0064086914, -0.0086975098, -0.010498047, -0.012023926, -0.013092041, -0.013366699, -0.01348877, -0.013793945, -0.013763428, -0.013977051, -0.013824463, -0.013519287, -0.013305664, -0.012878418, -0.012390137, -0.011352539, -0.010131836, -0.0085144043, -0.0065307617, -0.0048217773, -0.0028076172, -0.00094604492, 0.00082397461, 0.0019836426, 0.0028991699, 0.0037841797, 0.0042114258, 0.0050048828, 0.0057067871, 0.0067443848, 0.0078735352, 0.0088195801, 0.0099182129, 0.010986328, 0.011657715, 0.012145996, 0.012573242, 0.012969971, 0.013000488, 0.0128479, 0.012481689, 0.011993408, 0.011352539, 0.010498047, 0.0093688965, 0.0081176758, 0.0067749023, 0.0054931641, 0.0044250488, 0.0032958984, 0.001953125, 0.00054931641, -0.00085449219, -0.0026855469, -0.0042114258, -0.0060119629, -0.0071716309, -0.0086364746, -0.0095825195, -0.010284424, -0.010528564, -0.010498047, -0.010284424, -0.0096740723, -0.0094299316, -0.0088195801, -0.0082092285, -0.0076599121, -0.007232666, -0.0068359375, -0.0063171387, -0.0056152344, -0.0047607422, -0.0038146973, -0.0027770996, -0.0015563965, -0.00061035156, 0.00057983398, 0.0016174316, 0.002532959, 0.0032348633, 0.0037536621, 0.0041503906, 0.0043945312, 0.0048217773, 0.0053405762, 0.0060119629, 0.0067443848, 0.0075073242, 0.0080566406, 0.0084228516, 0.008392334, 0.0083618164, 0.0080566406, 0.0076293945, 0.0071411133, 0.0067749023, 0.0062255859, 0.0054626465, 0.0046081543, 0.0036315918, 0.0026550293, 0.0014648438, 0.0002746582, -0.00076293945, -0.0018615723, -0.0027160645, -0.0035400391, -0.0041809082, -0.0048522949, -0.0054626465, -0.0061035156, -0.0067138672, -0.0072021484, -0.0072937012, -0.0072937012, -0.0069885254, -0.0065002441, -0.0059204102, -0.0051269531, -0.0045471191, -0.0039672852, -0.0036010742, -0.0031738281, -0.0027770996, -0.0023803711, -0.0018310547, -0.0012817383, -0.00064086914, -3.0517578e-05, 0.00064086914, 0.0011901855, 0.0017700195, 0.0021972656, 0.002532959, 0.0028381348, 0.003112793, 0.0034484863, 0.0038146973, 0.0040893555, 0.0043334961, 0.0044250488, 0.0044555664, 0.0045776367, 0.0046691895, 0.0047302246, 0.0046691895, 0.0045471191, 0.0042724609, 0.00390625, 0.0035095215, 0.0029296875, 0.0024414062, 0.001953125, 0.0014343262, 0.00085449219, 0.00021362305, -0.00018310547, -0.00073242188, -0.0012207031, -0.001739502, -0.002166748, 0 }; static const unsigned defaultClickLength = sizeof(defaultClick) / sizeof(*defaultClick); static const float defaultClickEmphasis[] = { 0.002166748, 0.0023193359, 0.001739502, 0.002166748, 0.0021362305, 0.0018615723, 0.0020446777, 0.002166748, 0.0019836426, 0.0022277832, 0.0018310547, 0.0023803711, 0.0022277832, 0.001953125, 0.0022888184, 0.0018005371, 0.0025024414, 0.0018310547, 0.0019836426, 0.001953125, 0.0020446777, 0.0017700195, 0.0021057129, 0.0020751953, 0.0019836426, 0.0014343262, 0.0024414062, 0.0015869141, 0.0022888184, 0.0016784668, 0.0025024414, 0.0017089844, 0.0024719238, 0.0020141602, 0.0026855469, 0.0013427734, 0.0028381348, 0.0015563965, 0.0020446777, 0.0018615723, 0.002532959, 0.0015258789, 0.0021972656, 0.0016784668, 0.002166748, 0.0016479492, 0.0021362305, 0.0018920898, 0.0021362305, 0.0017700195, 0.0020141602, 0.0022277832, 0.001739502, 0.0022583008, 0.001953125, 0.0021362305, 0.0018920898, 0.0020446777, 0.001953125, 0.0026550293, 0.0014953613, 0.0023803711, 0.001953125, 0.0021972656, 0.0015869141, 0.0017700195, 0.0020141602, 0.0022277832, 0.0015258789, 0.0015869141, -0.0045471191, -0.011230469, -0.009185791, -0.0038757324, -0.020690918, -0.030731201, -0.0088806152, 0.0032958984, -0.0068664551, -0.0024719238, 0.024688721, 0.035400391, 0.014892578, 0.0047607422, 0.011779785, 0.0035095215, -0.012573242, -0.0059814453, 0.0065612793, 0.0039672852, 0.0014343262, 0.010406494, 0.027374268, 0.021942139, 0.0065612793, 0.010681152, 0.024688721, 0.024291992, 0.012451172, 0.023590088, 0.033203125, 0.024139404, 0.010375977, 0.016662598, 0.026885986, 0.039215088, 0.038330078, 0.039703369, 0.048309326, 0.046447754, 0.029937744, 0.031311035, 0.066497803, 0.052856445, -0.0074768066, -0.016998291, 0.016784668, 0.039428711, 0.021759033, 0.0040893555, -0.0016174316, 0.012451172, 0.052215576, 0.047485352, 0.017150879, 0.015075684, 0.013397217, 0.024475098, 0.035247803, 0.039978027, 0.06137085, 0.080108643, 0.078521729, 0.054779053, 0.0625, 0.1076355, 0.10934448, 0.079803467, 0.056945801, 0.056182861, 0.047851562, 0.028503418, 0.022979736, 0.010803223, -0.015686035, -0.050811768, -0.072509766, -0.057891846, -0.032073975, -0.051300049, -0.078887939, -0.069366455, -0.038146973, -0.0099182129, 0.010528564, 0.048492432, 0.082305908, 0.084838867, 0.059753418, 0.0043029785, -0.087158203, -0.18395996, -0.2911377, -0.37133789, -0.33422852, -0.28649902, -0.31976318, -0.33630371, -0.22824097, 0.020385742, 0.24456787, 0.31582642, 0.37380981, 0.40820312, 0.40539551, 0.41415405, 0.34210205, 0.20877075, 0.010498047, -0.17623901, -0.22705078, -0.15548706, -0.039855957, 0.0067443848, -0.0022277832, -0.050048828, -0.10186768, -0.11407471, -0.1050415, -0.067352295, 0.0018615723, 0.079742432, 0.11517334, 0.12005615, 0.14709473, 0.19543457, 0.2015686, 0.150177, 0.052703857, -0.029602051, -0.084838867, -0.14590454, -0.18951416, -0.18814087, -0.15014648, -0.11505127, -0.094360352, -0.076721191, -0.037872314, 0.011749268, 0.034851074, 0.060791016, 0.11419678, 0.14096069, 0.13140869, 0.1277771, 0.12313843, 0.10934448, 0.055541992, -0.026397705, -0.07144165, -0.050262451, -0.0097961426, -0.02822876, -0.15008545, -0.28927612, -0.32809448, -0.2772522, -0.15994263, 0.016448975, 0.13085938, 0.12866211, 0.1072998, 0.094177246, 0.036346436, -0.026245117, -0.040496826, -0.01348877, 0.0085144043, 0.0070800781, -0.0086669922, -0.0038452148, 0.040252686, 0.077026367, 0.071960449, 0.032470703, 0.002166748, 0.0065612793, 0.0053100586, -0.025115967, -0.058532715, -0.060699463, -0.015563965, 0.048278809, 0.086273193, 0.078582764, 0.031524658, -0.026580811, -0.077911377, -0.11288452, -0.13244629, -0.15325928, -0.14471436, -0.095672607, -0.048858643, -0.012115479, 0.023132324, 0.051544189, 0.046691895, 0.01373291, -0.0097961426, -0.016143799, -0.022460938, -0.025726318, 0.0075378418, 0.091522217, 0.19741821, 0.23269653, 0.16726685, 0.12173462, 0.12973022, 0.064300537, -0.12158203, -0.29794312, -0.33813477, -0.27990723, -0.19500732, -0.063903809, 0.10449219, 0.23712158, 0.34381104, 0.41766357, 0.37658691, 0.22747803, 0.056365967, -0.088317871, -0.19509888, -0.21295166, -0.15499878, -0.1114502, -0.093597412, -0.071289062, -0.034606934, 0.009552002, 0.068084717, 0.12075806, 0.13098145, 0.11383057, 0.08291626, 0.037322998, -0.017211914, -0.05456543, -0.074829102, -0.087768555, -0.076202393, -0.022735596, 0.032653809, 0.045623779, 0.045806885, 0.040496826, 0.016845703, -0.023712158, -0.057769775, -0.07232666, -0.055145264, -0.00091552734, 0.063201904, 0.11480713, 0.13497925, 0.11572266, 0.030731201, -0.11346436, -0.23406982, -0.27478027, -0.23690796, -0.13735962, -0.016479492, 0.076904297, 0.09487915, 0.070220947, 0.058074951, 0.05355835, 0.017181396, -0.096893311, -0.21951294, -0.26168823, -0.20983887, -0.093109131, 0.051879883, 0.15631104, 0.18319702, 0.15551758, 0.078826904, -0.012084961, -0.083007812, -0.10766602, -0.085479736, -0.059906006, -0.040039062, -0.031646729, -0.046142578, -0.065551758, -0.03414917, 0.025482178, 0.046325684, 0.054473877, 0.073303223, 0.073791504, 0.068786621, 0.082366943, 0.080596924, 0.052215576, 0.015594482, -0.0053405762, 0.0040588379, 0.032501221, 0.065551758, 0.093322754, 0.1199646, 0.13677979, 0.14523315, 0.1401062, 0.12902832, 0.11553955, 0.09664917, 0.091278076, 0.13305664, 0.24594116, 0.38180542, 0.45907593, 0.45343018, 0.4213562, 0.36801147, 0.31027222, 0.30541992, 0.33691406, 0.35479736, 0.3321228, 0.27334595, 0.25701904, 0.23641968, 0.18139648, 0.16793823, 0.17538452, 0.17803955, 0.19009399, 0.25280762, 0.38833618, 0.54940796, 0.70336914, 0.74789429, 0.64334106, 0.49359131, 0.30703735, 0.071624756, -0.075897217, -0.096191406, -0.076568604, 0.023406982, 0.15078735, 0.21612549, 0.30493164, 0.39697266, 0.41009521, 0.32348633, 0.24707031, 0.20645142, 0.11102295, 0.0075073242, -0.046325684, -0.038085938, 0.014251709, 0.096618652, 0.1842041, 0.21417236, 0.15905762, 0.0016174316, -0.25387573, -0.57302856, -0.85641479, -0.99038696, -0.99038696, -0.99038696, -0.99038696, -0.99038696, -0.90664673, -0.64364624, -0.42892456, -0.32131958, -0.23550415, -0.25338745, -0.32595825, -0.44995117, -0.57858276, -0.68716431, -0.80804443, -0.90670776, -0.9654541, -0.97409058, -0.95211792, -0.90264893, -0.85357666, -0.80429077, -0.7482605, -0.69125366, -0.60876465, -0.46228027, -0.23776245, -0.049041748, 0.019989014, 0.048950195, 0.033172607, -0.02142334, -0.084259033, -0.15020752, -0.16220093, -0.14608765, -0.14526367, -0.16497803, -0.1781311, -0.19717407, -0.24871826, -0.26089478, -0.21688843, -0.19818115, -0.23718262, -0.2824707, -0.32797241, -0.36758423, -0.3258667, -0.18960571, -0.082885742, -0.056365967, -0.054534912, -0.10766602, -0.26501465, -0.44006348, -0.58209229, -0.71032715, -0.77719116, -0.76467896, -0.68914795, -0.51403809, -0.27841187, -0.092163086, 0.056030273, 0.16799927, 0.19827271, 0.18478394, 0.17562866, 0.15100098, 0.066619873, -0.040313721, -0.15875244, -0.27044678, -0.35754395, -0.39682007, -0.34078979, -0.22293091, -0.13269043, -0.048034668, 0.0730896, 0.19732666, 0.30789185, 0.39804077, 0.44244385, 0.41928101, 0.35534668, 0.29177856, 0.22891235, 0.18084717, 0.15231323, 0.13360596, 0.13049316, 0.13156128, 0.1293335, 0.11477661, 0.12258911, 0.1824646, 0.26675415, 0.35171509, 0.43341064, 0.49163818, 0.51086426, 0.50985718, 0.49337769, 0.45962524, 0.43182373, 0.42025757, 0.4161377, 0.40631104, 0.40435791, 0.42172241, 0.44003296, 0.45986938, 0.47451782, 0.47540283, 0.48825073, 0.51907349, 0.5612793, 0.62319946, 0.7109375, 0.79650879, 0.86904907, 0.92111206, 0.96124268, 0.96994019, 0.92593384, 0.83483887, 0.70803833, 0.57583618, 0.45834351, 0.3828125, 0.35479736, 0.35110474, 0.38894653, 0.45022583, 0.49264526, 0.48919678, 0.45336914, 0.40823364, 0.35009766, 0.28097534, 0.22503662, 0.18313599, 0.14697266, 0.11010742, 0.07019043, 0.023956299, -0.024658203, -0.087219238, -0.14846802, -0.19796753, -0.23556519, -0.25576782, -0.25366211, -0.2333374, -0.19955444, -0.13546753, -0.052581787, 0.018371582, 0.079620361, 0.14343262, 0.20013428, 0.2388916, 0.25930786, 0.26889038, 0.26208496, 0.24691772, 0.22842407, 0.19110107, 0.14624023, 0.093017578, 0.034301758, -0.044189453, -0.1378479, -0.22967529, -0.29983521, -0.32650757, -0.31271362, -0.26196289, -0.19345093, -0.11380005, -0.032806396, 0.038330078, 0.087677002, 0.10690308, 0.092254639, 0.035766602, -0.057952881, -0.17190552, -0.28945923, -0.40023804, -0.49761963, -0.5635376, -0.59222412, -0.60348511, -0.61352539, -0.60958862, -0.57702637, -0.53674316, -0.49453735, -0.45465088, -0.42507935, -0.40863037, -0.4095459, -0.42730713, -0.45852661, -0.49362183, -0.53048706, -0.55499268, -0.5526123, -0.52798462, -0.49053955, -0.44320679, -0.38500977, -0.30548096, -0.2182312, -0.14306641, -0.073547363, -0.0094299316, 0.045196533, 0.083312988, 0.11129761, 0.14489746, 0.19921875, 0.24578857, 0.25418091, 0.23397827, 0.20111084, 0.14822388, 0.078582764, 0.020812988, -0.012390137, -0.02520752, -0.010559082, 0.026947021, 0.081542969, 0.13687134, 0.18441772, 0.21426392, 0.22711182, 0.21377563, 0.17559814, 0.12246704, 0.060882568, -0.0017089844, -0.067504883, -0.12475586, -0.16159058, -0.16809082, -0.15145874, -0.12081909, -0.077362061, -0.03012085, 0.011444092, 0.044189453, 0.075256348, 0.1050415, 0.13238525, 0.15667725, 0.17337036, 0.17050171, 0.14389038, 0.10055542, 0.057220459, 0.016204834, -0.012664795, -0.025024414, -0.014221191, 0.012939453, 0.063781738, 0.12664795, 0.19824219, 0.26501465, 0.32858276, 0.3782959, 0.40698242, 0.42160034, 0.42788696, 0.43948364, 0.46075439, 0.48413086, 0.49819946, 0.50167847, 0.49963379, 0.48529053, 0.45324707, 0.40890503, 0.36273193, 0.31619263, 0.27200317, 0.23919678, 0.21868896, 0.2046814, 0.18276978, 0.15286255, 0.11727905, 0.071807861, 0.015777588, -0.054199219, -0.12478638, -0.19580078, -0.25622559, -0.30892944, -0.34820557, -0.37606812, -0.39535522, -0.40704346, -0.42147827, -0.43313599, -0.4447937, -0.44998169, -0.44503784, -0.431427, -0.40750122, -0.37838745, -0.34109497, -0.29644775, -0.24822998, -0.1991272, -0.15881348, -0.12728882, -0.10827637, -0.09979248, -0.094055176, -0.091125488, -0.089508057, -0.087646484, -0.0809021, -0.072387695, -0.055328369, -0.035583496, -0.0079956055, 0.018188477, 0.04309082, 0.060577393, 0.073272705, 0.080413818, 0.084503174, 0.083343506, 0.07522583, 0.071136475, 0.076141357, 0.088348389, 0.10449219, 0.12374878, 0.14376831, 0.1534729, 0.14807129, 0.1255188, 0.092559814, 0.047485352, -0.0028076172, -0.062286377, -0.12301636, -0.17855835, -0.22631836, -0.26260376, -0.28588867, -0.29559326, -0.29629517, -0.287323, -0.27877808, -0.26751709, -0.25668335, -0.24749756, -0.24139404, -0.24041748, -0.23944092, -0.23544312, -0.22592163, -0.20898438, -0.184021, -0.15014648, -0.11633301, -0.082702637, -0.0496521, -0.017944336, 0.017486572, 0.052581787, 0.092041016, 0.13427734, 0.18481445, 0.24057007, 0.29467773, 0.33944702, 0.37042236, 0.38433838, 0.38241577, 0.36376953, 0.33966064, 0.31497192, 0.29443359, 0.27792358, 0.2600708, 0.24700928, 0.23727417, 0.23452759, 0.23358154, 0.23257446, 0.23406982, 0.23239136, 0.23312378, 0.22958374, 0.22579956, 0.21710205, 0.20043945, 0.17419434, 0.13870239, 0.096466064, 0.051971436, 0.010437012, -0.024963379, -0.052490234, -0.066467285, -0.068969727, -0.058563232, -0.044616699, -0.030029297, -0.017486572, -0.0077209473, -0.0032043457, -0.0048217773, -0.012481689, -0.027801514, -0.042724609, -0.060211182, -0.072937012, -0.081756592, -0.082977295, -0.07800293, -0.064605713, -0.043792725, -0.013031006, 0.028625488, 0.077667236, 0.13046265, 0.18130493, 0.22851562, 0.26617432, 0.29119873, 0.3039856, 0.30383301, 0.2925415, 0.27261353, 0.24078369, 0.20632935, 0.16989136, 0.13552856, 0.1038208, 0.074462891, 0.045898438, 0.013061523, -0.021087646, -0.06072998, -0.099700928, -0.14016724, -0.17874146, -0.21130371, -0.23596191, -0.25204468, -0.26132202, -0.26803589, -0.27981567, -0.29934692, -0.32730103, -0.36425781, -0.40698242, -0.45092773, -0.48971558, -0.51776123, -0.53198242, -0.5333252, -0.52218628, -0.50076294, -0.47000122, -0.43017578, -0.38583374, -0.33612061, -0.28347778, -0.23306274, -0.18682861, -0.14859009, -0.11853027, -0.099853516, -0.09072876, -0.088562012, -0.091369629, -0.098022461, -0.10787964, -0.11395264, -0.11013794, -0.093841553, -0.069274902, -0.035369873, 0.0026245117, 0.046783447, 0.090698242, 0.1315918, 0.16296387, 0.18664551, 0.2038269, 0.2154541, 0.22036743, 0.21444702, 0.20489502, 0.18753052, 0.16650391, 0.13882446, 0.10861206, 0.078735352, 0.049346924, 0.024078369, 0.0013427734, -0.017974854, -0.032165527, -0.043243408, -0.050262451, -0.05670166, -0.061157227, -0.068695068, -0.078918457, -0.085601807, -0.090698242, -0.093658447, -0.095581055, -0.092529297, -0.08404541, -0.068939209, -0.053649902, -0.037017822, -0.016448975, 0.0096740723, 0.041687012, 0.074798584, 0.10549927, 0.13494873, 0.16445923, 0.1937561, 0.2208252, 0.24539185, 0.26721191, 0.28359985, 0.29733276, 0.30651855, 0.31622314, 0.32266235, 0.31842041, 0.31008911, 0.29681396, 0.2819519, 0.26678467, 0.25457764, 0.24642944, 0.24273682, 0.23709106, 0.22891235, 0.2197876, 0.21051025, 0.20257568, 0.19024658, 0.17764282, 0.1607666, 0.14309692, 0.12210083, 0.096893311, 0.073150635, 0.049530029, 0.028045654, 0.0048522949, -0.017669678, -0.036224365, -0.050506592, -0.059112549, -0.066345215, -0.068939209, -0.070495605, -0.073059082, -0.073028564, -0.076812744, -0.080963135, -0.092010498, -0.10275269, -0.11016846, -0.11907959, -0.12677002, -0.13555908, -0.13842773, -0.13674927, -0.13088989, -0.11846924, -0.099334717, -0.072784424, -0.042541504, -0.0082702637, 0.031524658, 0.072387695, 0.10968018, 0.13861084, 0.15847778, 0.16812134, 0.16650391, 0.15774536, 0.14169312, 0.12005615, 0.092895508, 0.061737061, 0.02923584, -0.0022277832, -0.032836914, -0.05670166, -0.075439453, -0.093322754, -0.1121521, -0.13204956, -0.15255737, -0.17541504, -0.19900513, -0.21817017, -0.23519897, -0.25091553, -0.27148438, -0.29296875, -0.31069946, -0.3243103, -0.33599854, -0.34689331, -0.35461426, -0.3598938, -0.36212158, -0.36105347, -0.35699463, -0.34725952, -0.33319092, -0.31484985, -0.29327393, -0.26959229, -0.24423218, -0.21859741, -0.18777466, -0.15194702, -0.11468506, -0.079498291, -0.0496521, -0.023529053, 0.001159668, 0.025848389, 0.044494629, 0.054168701, 0.058135986, 0.059844971, 0.066894531, 0.075195312, 0.087585449, 0.10009766, 0.11755371, 0.13864136, 0.16278076, 0.18545532, 0.20370483, 0.22076416, 0.23773193, 0.25531006, 0.26928711, 0.27508545, 0.27371216, 0.26660156, 0.25100708, 0.2260437, 0.1942749, 0.16079712, 0.12878418, 0.10110474, 0.07244873, 0.046051025, 0.024993896, 0.010284424, -0.0010070801, -0.0082397461, -0.011383057, -0.0072937012, -0.0034179688, 0.0040893555, 0.013519287, 0.022583008, 0.031219482, 0.032714844, 0.034851074, 0.033508301, 0.033935547, 0.030975342, 0.031982422, 0.039794922, 0.05569458, 0.077392578, 0.10113525, 0.1277771, 0.15429688, 0.18026733, 0.20275879, 0.21591187, 0.22341919, 0.224823, 0.22775269, 0.22766113, 0.22491455, 0.2170105, 0.20431519, 0.19171143, 0.17459106, 0.15859985, 0.14315796, 0.12554932, 0.10998535, 0.09463501, 0.083435059, 0.070465088, 0.057983398, 0.048278809, 0.041351318, 0.036224365, 0.030639648, 0.021850586, 0.012176514, -0.0034484863, -0.022735596, -0.045166016, -0.068908691, -0.094421387, -0.11810303, -0.13882446, -0.15774536, -0.17269897, -0.18533325, -0.19125366, -0.19116211, -0.18655396, -0.18423462, -0.18267822, -0.17877197, -0.17248535, -0.16625977, -0.16271973, -0.16104126, -0.16125488, -0.16192627, -0.16333008, -0.16461182, -0.16616821, -0.16235352, -0.15325928, -0.13644409, -0.11523438, -0.090240479, -0.058013916, -0.024383545, 0.0089111328, 0.039093018, 0.067749023, 0.096221924, 0.11837769, 0.13458252, 0.13946533, 0.13269043, 0.12045288, 0.099212646, 0.073608398, 0.042266846, 0.011627197, -0.019042969, -0.04876709, -0.07409668, -0.096038818, -0.11062622, -0.1239624, -0.1340332, -0.14199829, -0.14428711, -0.14074707, -0.13641357, -0.13253784, -0.13400269, -0.13830566, -0.1505127, -0.16162109, -0.17391968, -0.1847229, -0.19418335, -0.2019043, -0.20770264, -0.21234131, -0.21121216, -0.20510864, -0.19058228, -0.16986084, -0.14498901, -0.11407471, -0.082061768, -0.048492432, -0.015533447, 0.018341064, 0.052185059, 0.078826904, 0.10336304, 0.12106323, 0.1315918, 0.13513184, 0.13653564, 0.13986206, 0.14501953, 0.14926147, 0.15634155, 0.16189575, 0.16915894, 0.17584229, 0.18264771, 0.19329834, 0.20681763, 0.22433472, 0.24029541, 0.25708008, 0.27435303, 0.28552246, 0.28768921, 0.2796936, 0.26013184, 0.23287964, 0.20193481, 0.17483521, 0.14996338, 0.12689209, 0.10336304, 0.081939697, 0.063842773, 0.047302246, 0.031677246, 0.01953125, 0.010559082, 0.0013427734, -0.0082397461, -0.016357422, -0.019348145, -0.023223877, -0.023406982, -0.023895264, -0.022155762, -0.020477295, -0.017089844, -0.013031006, -0.0098571777, -0.0046691895, 6.1035156e-05, 0.0072937012, 0.0128479, 0.025878906, 0.040618896, 0.055908203, 0.066558838, 0.072021484, 0.076965332, 0.080383301, 0.081237793, 0.077575684, 0.072113037, 0.067443848, 0.064880371, 0.063720703, 0.058959961, 0.054534912, 0.047576904, 0.039276123, 0.025390625, 0.010986328, -0.0061645508, -0.018859863, -0.028900146, -0.036376953, -0.041473389, -0.047241211, -0.054260254, -0.063049316, -0.0730896, -0.081085205, -0.090118408, -0.10089111, -0.11264038, -0.12442017, -0.1350708, -0.14535522, -0.15383911, -0.16052246, -0.16329956, -0.1668396, -0.17166138, -0.17819214, -0.18157959, -0.18133545, -0.17935181, -0.1758728, -0.17181396, -0.16687012, -0.16067505, -0.15344238, -0.14562988, -0.13626099, -0.12780762, -0.11743164, -0.10961914, -0.098510742, -0.085601807, -0.071716309, -0.05758667, -0.041412354, -0.024078369, -0.0043640137, 0.015960693, 0.037567139, 0.060455322, 0.082855225, 0.10211182, 0.11419678, 0.12088013, 0.12197876, 0.12145996, 0.11743164, 0.11026001, 0.096984863, 0.077789307, 0.054504395, 0.031890869, 0.010986328, -0.0061340332, -0.020507812, -0.032836914, -0.042541504, -0.050476074, -0.056762695, -0.06137085, -0.065032959, -0.064727783, -0.064147949, -0.060119629, -0.05770874, -0.056549072, -0.057098389, -0.059692383, -0.063140869, -0.067321777, -0.072021484, -0.075439453, -0.078033447, -0.077575684, -0.073059082, -0.063995361, -0.049560547, -0.030212402, -0.008605957, 0.015930176, 0.039794922, 0.065917969, 0.090820312, 0.11099243, 0.12619019, 0.13739014, 0.1472168, 0.15374756, 0.15893555, 0.16152954, 0.16671753, 0.17425537, 0.17889404, 0.18060303, 0.17886353, 0.17861938, 0.17984009, 0.18179321, 0.18499756, 0.18548584, 0.18609619, 0.18521118, 0.18234253, 0.17956543, 0.17547607, 0.1690979, 0.16101074, 0.15505981, 0.14837646, 0.13739014, 0.12255859, 0.10522461, 0.082397461, 0.05770874, 0.030609131, 0.0046691895, -0.018676758, -0.037872314, -0.054779053, -0.069458008, -0.078826904, -0.084136963, -0.085479736, -0.083404541, -0.076324463, -0.066314697, -0.056060791, -0.049926758, -0.043182373, -0.040252686, -0.037811279, -0.038543701, -0.039276123, -0.040771484, -0.041687012, -0.042053223, -0.043548584, -0.040161133, -0.034637451, -0.024841309, -0.015197754, -0.0059509277, 0.001159668, 0.0042114258, 0.0062866211, 0.0049438477, 0.0047302246, 0.0028686523, -0.00082397461, -0.0034790039, -0.0077514648, -0.01260376, -0.019500732, -0.02645874, -0.032409668, -0.041351318, -0.049163818, -0.058532715, -0.068603516, -0.078948975, -0.088897705, -0.094543457, -0.097167969, -0.095581055, -0.094726562, -0.095001221, -0.095336914, -0.094726562, -0.096160889, -0.09866333, -0.10427856, -0.1104126, -0.11727905, -0.12301636, -0.13006592, -0.13479614, -0.13574219, -0.13119507, -0.12451172, -0.11630249, -0.10476685, -0.094543457, -0.08416748, -0.074066162, -0.061767578, -0.050415039, -0.040557861, -0.031402588, -0.023834229, -0.01361084, -0.0036010742, 0.0055236816, 0.013122559, 0.02142334, 0.029418945, 0.041717529, 0.056518555, 0.075714111, 0.095062256, 0.11523438, 0.13363647, 0.146698, 0.15646362, 0.16061401, 0.16043091, 0.15634155, 0.14889526, 0.13769531, 0.12127686, 0.10272217, 0.085418701, 0.070739746, 0.055541992, 0.040405273, 0.024200439, 0.010803223, -0.00048828125, -0.0087890625, -0.012481689, -0.014404297, -0.014068604, -0.011230469, -0.0081787109, -0.0049438477, -0.0041809082, -0.0060119629, -0.012237549, -0.020355225, -0.027832031, -0.034759521, -0.042053223, -0.04675293, -0.045349121, -0.042297363, -0.03604126, -0.027862549, -0.015777588, -0.0011291504, 0.013397217, 0.030212402, 0.04598999, 0.061401367, 0.074615479, 0.086242676, 0.094238281, 0.09979248, 0.10458374, 0.10662842, 0.10894775, 0.11077881, 0.11056519, 0.11022949, 0.10903931, 0.1072998, 0.10266113, 0.098999023, 0.095977783, 0.095916748, 0.095611572, 0.094909668, 0.093963623, 0.092895508, 0.092559814, 0.090423584, 0.084014893, 0.073852539, 0.059020996, 0.044830322, 0.02822876, 0.012023926, -0.0049743652, -0.020050049, -0.036193848, -0.052398682, -0.066253662, -0.078796387, -0.089019775, -0.098083496, -0.10592651, -0.11224365, -0.11618042, -0.11715698, -0.11807251, -0.11676025, -0.11459351, -0.11108398, -0.10748291, -0.10400391, -0.10064697, -0.096923828, -0.092163086, -0.086639404, -0.08114624, -0.075531006, -0.071838379, -0.06854248, -0.064971924, -0.061035156, -0.054382324, -0.047393799, -0.043518066, -0.04095459, -0.041351318, -0.041259766, -0.041931152, -0.04095459, -0.037841797, -0.034210205, -0.029541016, -0.026550293, -0.02444458, -0.024139404, -0.026397705, -0.03036499, -0.035675049, -0.041046143, -0.04675293, -0.054168701, -0.061218262, -0.065704346, -0.066650391, -0.066650391, -0.063537598, -0.06048584, -0.055664062, -0.053375244, -0.052642822, -0.052764893, -0.054718018, -0.053955078, -0.05355835, -0.052764893, -0.053405762, -0.056213379, -0.06036377, -0.062744141, -0.063110352, -0.06072998, -0.054504395, -0.044647217, -0.034057617, -0.022949219, -0.011810303, 0.002532959, 0.015594482, 0.024383545, 0.032165527, 0.03894043, 0.047302246, 0.055236816, 0.064605713, 0.072387695, 0.081115723, 0.088348389, 0.094940186, 0.10223389, 0.11135864, 0.12249756, 0.13082886, 0.13803101, 0.14520264, 0.14993286, 0.15118408, 0.14987183, 0.14846802, 0.14581299, 0.13851929, 0.12878418, 0.11639404, 0.10375977, 0.09173584, 0.078796387, 0.063842773, 0.048980713, 0.034576416, 0.022338867, 0.011932373, 0.0039672852, 0.0011291504, -0.00094604492, -0.0015563965, -0.0034484863, -0.0050354004, -0.0083312988, -0.012390137, -0.01651001, -0.019683838, -0.022003174, -0.024414062, -0.027557373, -0.03137207, -0.03314209, -0.030700684, -0.028045654, -0.025482178, -0.022247314, -0.018737793, -0.013275146, -0.0074157715, 0.0010070801, 0.0093078613, 0.01763916, 0.027404785, 0.035583496, 0.043151855, 0.047485352, 0.051239014, 0.053649902, 0.054626465, 0.054382324, 0.053039551, 0.050323486, 0.046691895, 0.044158936, 0.041534424, 0.039794922, 0.035797119, 0.031494141, 0.026580811, 0.021575928, 0.017944336, 0.014038086, 0.0093688965, 0.0051574707, -0.00067138672, -0.0070495605, -0.014984131, -0.021728516, -0.030151367, -0.037719727, -0.049804688, -0.062255859, -0.073791504, -0.08416748, -0.089599609, -0.096496582, -0.10055542, -0.10531616, -0.10870361, -0.11083984, -0.11102295, -0.10925293, -0.10610962, -0.10125732, -0.094848633, -0.086700439, -0.07925415, -0.074005127, -0.070770264, -0.068511963, -0.065643311, -0.060577393, -0.054595947, -0.049407959, -0.043914795, -0.039794922, -0.033813477, -0.027923584, -0.022674561, -0.017791748, -0.01260376, -0.008972168, -0.0076293945, -0.0053100586, -0.0030517578, -0.0024719238, -0.0032043457, -0.0035705566, -0.002746582, -0.0013427734, -0.0011901855, -0.0012817383, -0.0016479492, -0.0030212402, -0.0040588379, -0.0071411133, -0.0085449219, -0.010375977, -0.01171875, -0.011230469, -0.012512207, -0.013305664, -0.014190674, -0.014099121, -0.015625, -0.01763916, -0.020202637, -0.020172119, -0.017730713, -0.014068604, -0.0097351074, -0.0065917969, -0.0020751953, 0.0018310547, 0.0045471191, 0.0046691895, 0.003326416, 0.0018615723, -0.00021362305, 0.0010986328, 0.0056152344, 0.011962891, 0.019866943, 0.029846191, 0.040130615, 0.050811768, 0.058746338, 0.068267822, 0.076721191, 0.083862305, 0.091491699, 0.096008301, 0.10150146, 0.1036377, 0.10562134, 0.10449219, 0.1026001, 0.10076904, 0.099822998, 0.098297119, 0.095245361, 0.094207764, 0.091949463, 0.092773438, 0.093048096, 0.093658447, 0.090881348, 0.084747314, 0.078186035, 0.068328857, 0.058197021, 0.044403076, 0.031768799, 0.019866943, 0.010345459, 0.0029602051, -0.0044555664, -0.011932373, -0.018920898, -0.024993896, -0.029205322, -0.031768799, -0.034790039, -0.036773682, -0.037841797, -0.04019165, -0.042785645, -0.045928955, -0.045776367, -0.044006348, -0.040649414, -0.037902832, -0.036834717, -0.035308838, -0.033569336, -0.030090332, -0.025543213, -0.020263672, -0.014678955, -0.0094604492, -0.0042114258, -0.00067138672, 0.0016784668, 0.0021362305, 0.0038452148, 0.007598877, 0.0099487305, 0.013793945, 0.015838623, 0.01852417, 0.019958496, 0.021087646, 0.021270752, 0.021911621, 0.021789551, 0.020965576, 0.01776123, 0.013275146, 0.0070495605, 0.00076293945, -0.004119873, -0.007232666, -0.009552002, -0.012695312, -0.016052246, -0.019226074, -0.02331543, -0.028625488, -0.032348633, -0.035736084, -0.038909912, -0.044372559, -0.050354004, -0.056610107, -0.061309814, -0.065704346, -0.065948486, -0.067230225, -0.06652832, -0.0652771, -0.061981201, -0.057128906, -0.052185059, -0.046112061, -0.041595459, -0.035461426, -0.031097412, -0.025970459, -0.022979736, -0.019836426, -0.015899658, -0.01260376, -0.0087585449, -0.0061950684, -0.0028991699, -0.0014953613, 0.0018615723, 0.0055847168, 0.008605957, 0.011871338, 0.015594482, 0.020629883, 0.025970459, 0.030212402, 0.034973145, 0.037322998, 0.037597656, 0.036254883, 0.034698486, 0.032440186, 0.029937744, 0.026062012, 0.021972656, 0.018432617, 0.013397217, 0.010528564, 0.0077514648, 0.0076904297, 0.0078430176, 0.0095825195, 0.012512207, 0.01550293, 0.017486572, 0.017150879, 0.015563965, 0.013031006, 0.010070801, 0.0068969727, 0.0039367676, 0.0021057129, 0.0018920898, 0.0032653809, 0.0064697266, 0.0092163086, 0.010437012, 0.0097961426, 0.0078125, 0.0053405762, 0.0048828125, 0.0055236816, 0.0086669922, 0.013061523, 0.019897461, 0.027252197, 0.035675049, 0.043518066, 0.050018311, 0.056427002, 0.06072998, 0.062011719, 0.061859131, 0.060546875, 0.060577393, 0.059173584, 0.056945801, 0.054138184, 0.053283691, 0.053131104, 0.052764893, 0.050811768, 0.04864502, 0.046295166, 0.044921875, 0.043182373, 0.040161133, 0.036529541, 0.032287598, 0.028411865, 0.022247314, 0.015960693, 0.0095825195, 0.0032958984, -0.0028991699, -0.01159668, -0.018249512, -0.02532959, -0.03137207, -0.035919189, -0.040710449, -0.044006348, -0.049163818, -0.052246094, -0.055969238, -0.056396484, -0.055511475, -0.052825928, -0.049194336, -0.046844482, -0.04598999, -0.045227051, -0.044586182, -0.042388916, -0.039611816, -0.037719727, -0.035766602, -0.032806396, -0.030700684, -0.027313232, -0.02444458, -0.020019531, -0.015533447, -0.012145996, -0.0091552734, -0.0064697266, -0.0040893555, -0.00048828125, 0.001953125, 0.0048522949, 0.0061950684, 0.007019043, 0.0083007812, 0.0091247559, 0.0094909668, 0.0098876953, 0.010894775, 0.011566162, 0.0105896, 0.0083007812, 0.0047607422, 0.0010986328, -0.0036621094, -0.0073242188, -0.010223389, -0.011505127, -0.011993408, -0.012786865, -0.015350342, -0.017669678, -0.019500732, -0.022613525, -0.025115967, -0.027862549, -0.027832031, -0.028320312, -0.029602051, -0.030761719, -0.033111572, -0.033630371, -0.032592773, -0.030914307, -0.027008057, -0.02355957, -0.020507812, -0.017211914, -0.014709473, -0.0097351074, -0.0033874512, 0.0032958984, 0.010284424, 0.014007568, 0.017272949, 0.017791748, 0.018371582, 0.018096924, 0.017272949, 0.016326904, 0.016021729, 0.017028809, 0.020507812, 0.025238037, 0.031280518, 0.036834717, 0.040283203, 0.04284668, 0.045410156, 0.048339844, 0.050109863, 0.04800415, 0.044311523, 0.040557861, 0.036010742, 0.031982422, 0.026977539, 0.022766113, 0.018829346, 0.015411377, 0.012359619, 0.010925293, 0.0095214844, 0.010498047, 0.012054443, 0.014221191, 0.016326904, 0.017150879, 0.016784668, 0.01473999, 0.012481689, 0.0090026855, 0.0057678223, 0.002746582, -0.00024414062, -6.1035156e-05, -0.00042724609, -0.00039672852, -0.0024414062, -0.0037841797, -0.0043334961, -0.0034790039, -0.0015563965, 0.0011901855, 0.0047302246, 0.0079345703, 0.011108398, 0.013885498, 0.017150879, 0.020874023, 0.024139404, 0.028411865, 0.031860352, 0.036010742, 0.038330078, 0.038574219, 0.03805542, 0.035980225, 0.033905029, 0.03112793, 0.027496338, 0.024414062, 0.020690918, 0.017791748, 0.013549805, 0.0098876953, 0.0071105957, 0.0046081543, 0.0029296875, 0.0013122559, 0.0002746582, -0.0014343262, -0.0029907227, -0.0065307617, -0.009979248, -0.014831543, -0.02041626, -0.027008057, -0.036132812, -0.043670654, -0.049591064, -0.053894043, -0.057312012, -0.059570312, -0.059051514, -0.057617188, -0.05581665, -0.054962158, -0.053283691, -0.051300049, -0.049591064, -0.049407959, -0.049560547, -0.0496521, -0.04788208, -0.044830322, -0.040771484, -0.03616333, -0.031066895, -0.024902344, -0.019226074, -0.014312744, -0.010925293, -0.0085754395, -0.0065612793, -0.0048217773, -0.0020141602, -0.00039672852, 0.0014648438, 0.003326416, 0.0067749023, 0.009979248, 0.014068604, 0.016052246, 0.017486572, 0.017700195, 0.018127441, 0.018066406, 0.017608643, 0.016845703, 0.015838623, 0.014404297, 0.013214111, 0.011901855, 0.010650635, 0.0085754395, 0.0069885254, 0.0051269531, 0.0030212402, 0.00015258789, -0.0031738281, -0.0065307617, -0.0090637207, -0.010498047, -0.011505127, -0.012023926, -0.01260376, -0.012115479, -0.010406494, -0.0086975098, -0.0063781738, -0.005859375, -0.0042114258, -0.0034179688, -0.0012207031, 0.00057983398, 0.0030822754, 0.0048828125, 0.0070800781, 0.010040283, 0.013793945, 0.01763916, 0.020935059, 0.024047852, 0.026245117, 0.02822876, 0.028381348, 0.028015137, 0.027618408, 0.027496338, 0.0284729, 0.029174805, 0.029815674, 0.030670166, 0.033081055, 0.035797119, 0.037445068, 0.039154053, 0.039825439, 0.040283203, 0.03918457, 0.036987305, 0.033874512, 0.02935791, 0.025177002, 0.020507812, 0.016479492, 0.013031006, 0.010070801, 0.0077209473, 0.0056152344, 0.003326416, 0.00048828125, -0.0017089844, -0.0030517578, -0.0029602051, -0.003112793, -0.0025024414, -0.0024414062, -0.0021362305, -0.0023498535, -0.0029907227, -0.0043029785, -0.0059814453, -0.0078735352, -0.01083374, -0.013397217, -0.016448975, -0.018890381, -0.020568848, -0.020080566, -0.01852417, -0.016845703, -0.014007568, -0.0105896, -0.0066833496, -0.0030517578, 0.0014953613, 0.0065002441, 0.0098571777, 0.013122559, 0.014404297, 0.015472412, 0.016571045, 0.018035889, 0.018585205, 0.018371582, 0.016540527, 0.014099121, 0.011413574, 0.009185791, 0.0065917969, 0.0032653809, 0.00054931641, -0.0018005371, -0.0024719238, -0.0046081543, -0.0062255859, -0.0078430176, -0.009979248, -0.011627197, -0.015289307, -0.017456055, -0.019897461, -0.022918701, -0.025543213, -0.028747559, -0.031524658, -0.036865234, -0.041534424, -0.045410156, -0.046875, -0.048126221, -0.049499512, -0.049163818, -0.048828125, -0.047790527, -0.04574585, -0.043579102, -0.040924072, -0.040222168, -0.038360596, -0.036895752, -0.034210205, -0.031616211, -0.028289795, -0.024200439, -0.020294189, -0.015838623, -0.011993408, -0.007598877, -0.0036010742, 0.00054931641, 0.004486084, 0.0074157715, 0.0098571777, 0.012054443, 0.01473999, 0.017700195, 0.019744873, 0.022399902, 0.02432251, 0.025817871, 0.027130127, 0.027709961, 0.0284729, 0.027893066, 0.027496338, 0.026580811, 0.025634766, 0.023681641, 0.021972656, 0.019775391, 0.017730713, 0.016052246, 0.014556885, 0.014434814, 0.013763428, 0.012268066, 0.0105896, 0.0091247559, 0.0077819824, 0.0054626465, 0.0030822754, 0.00067138672, -0.0012207031, -0.0028686523, -0.0048217773, -0.0047912598, -0.004486084, -0.002746582, -0.00067138672, 0.0012512207, 0.0037231445, 0.0054321289, 0.0074157715, 0.010192871, 0.013214111, 0.016448975, 0.018005371, 0.019134521, 0.018463135, 0.017944336, 0.016723633, 0.016448975, 0.01651001, 0.01651001, 0.017669678, 0.019622803, 0.022064209, 0.024017334, 0.025909424, 0.026977539, 0.027008057, 0.026733398, 0.025634766, 0.024261475, 0.022247314, 0.020202637, 0.017730713, 0.01663208, 0.015319824, 0.014404297, 0.013092041, 0.0098266602, 0.0065307617, 0.0032653809, 0.00061035156, -0.0028076172, -0.0061340332, -0.0094909668, -0.012115479, -0.013244629, -0.014251709, -0.015075684, -0.015808105, -0.015991211, -0.016418457, -0.016601562, -0.01663208, -0.01675415, -0.017333984, -0.018615723, -0.019775391, -0.02053833, -0.020721436, -0.021820068, -0.022918701, -0.023651123, -0.023895264, -0.023590088, -0.02331543, -0.022338867, -0.020141602, -0.017669678, -0.014068604, -0.010528564, -0.0055236816, 0, 0.0051574707, 0.008972168, 0.011291504, 0.012207031, 0.012420654, 0.01159668, 0.0105896, 0.009185791, 0.0078735352, 0.0067749023, 0.0052490234, 0.0040588379, 0.0025024414, 0.0013427734, 0.0014038086, 0.0009765625, 0.00076293945, -0.00079345703, -0.0029907227, -0.0054626465, -0.0073547363, -0.0095825195, -0.011383057, -0.012237549, -0.013000488, -0.014373779, -0.016448975, -0.018341064, -0.020446777, -0.02331543, -0.025909424, -0.02835083, -0.028869629, -0.028839111, -0.028686523, -0.028106689, -0.027740479, -0.027435303, -0.027313232, -0.026672363, -0.025604248, -0.02355957, -0.020263672, -0.016204834, -0.011383057, -0.0068359375, -0.0024719238, 0.00021362305, 0.0022583008, 0.0036621094, 0.0053710938, 0.0079040527, 0.010162354, 0.013214111, 0.016052246, 0.019042969, 0.022064209, 0.024871826, 0.027618408, 0.029205322, 0.030761719, 0.031921387, 0.032012939, 0.032562256, 0.032714844, 0.032958984, 0.032318115, 0.031860352, 0.030822754, 0.03024292, 0.029510498, 0.028167725, 0.027099609, 0.024810791, 0.022674561, 0.019958496, 0.018218994, 0.016143799, 0.014373779, 0.012145996, 0.010101318, 0.0079345703, 0.0063476562, 0.0045471191, 0.003112793, 0.00085449219, -0.00088500977, -0.002532959, -0.0035095215, -0.0039672852, -0.0041503906, -0.0031433105, -0.0017700195, -3.0517578e-05, 0.0010681152, 0.0033874512, 0.0053405762, 0.0074768066, 0.0082397461, 0.0075683594, 0.007019043, 0.005279541, 0.004486084, 0.0035400391, 0.0035400391, 0.0047607422, 0.0061645508, 0.0079345703, 0.0090332031, 0.010314941, 0.010681152, 0.010467529, 0.010467529, 0.009979248, 0.009765625, 0.0090637207, 0.0083007812, 0.0075683594, 0.0065917969, 0.0066223145, 0.0054626465, 0.0040893555, 0.0021362305, -3.0517578e-05, -0.0024719238, -0.0052490234, -0.008026123, -0.010375977, -0.012573242, -0.01461792, -0.01651001, -0.017822266, -0.018981934, -0.019622803, -0.019592285, -0.020172119, -0.020751953, -0.021728516, -0.022186279, -0.02230835, -0.02230835, -0.022155762, -0.022583008, -0.022338867, -0.023193359, -0.023284912, -0.023406982, -0.022766113, -0.022216797, -0.022094727, -0.021484375, -0.021026611, -0.019836426, -0.018005371, -0.015533447, -0.012512207, -0.0092773438, -0.0061340332, -0.0029907227, 0.00076293945, 0.00390625, 0.0065002441, 0.0084228516, 0.010070801, 0.011566162, 0.012054443, 0.012573242, 0.011779785, 0.011077881, 0.010131836, 0.0088500977, 0.0075073242, 0.0054321289, 0.0044555664, 0.003692627, 0.0032348633, 0.0028991699, 0.0022583008, 0.0026245117, 0.0021972656, 0.001953125, 0.00079345703, -0.001159668, -0.0028381348, -0.0046386719, -0.0059814453, -0.0075378418, -0.0087890625, -0.010192871, -0.01083374, -0.011993408, -0.012695312, -0.012756348, -0.012908936, -0.012695312, -0.013061523, -0.012756348, -0.012145996, -0.011352539, -0.010253906, -0.0094909668, -0.0081787109, -0.0067138672, -0.0047912598, -0.0016784668, 0.00085449219, 0.0041809082, 0.0070495605, 0.0098571777, 0.013061523, 0.016052246, 0.019012451, 0.020965576, 0.022613525, 0.023590088, 0.024169922, 0.02432251, 0.024078369, 0.024261475, 0.023681641, 0.024291992, 0.024475098, 0.025390625, 0.026306152, 0.026641846, 0.027008057, 0.026672363, 0.026641846, 0.026153564, 0.025482178, 0.024108887, 0.022155762, 0.020050049, 0.017578125, 0.01461792, 0.011932373, 0.0090332031, 0.0069580078, 0.0048828125, 0.0032653809, 0.001953125, 0.0007019043, -0.00036621094, -0.0019226074, -0.0037536621, -0.0056152344, -0.0076904297, -0.0087280273, -0.0090942383, -0.0088195801, -0.0081481934, -0.0072937012, -0.006439209, -0.005279541, -0.0043640137, -0.0034179688, -0.003112793, -0.0025024414, -0.0022888184, -0.0019226074, -0.0015869141, -0.0015869141, -0.0015258789, -0.0016784668, -0.0017700195, -0.0016784668, -0.0013427734, -0.0011291504, -0.00061035156, 0, 0.00079345703, 0.0012207031, 0.0015869141, 0.0016479492, 0.0015258789, 0.0012817383, 0.00094604492, 0.00045776367, -0.0002746582, -0.00088500977, -0.0016479492, -0.0024108887, -0.0036621094, -0.0050354004, -0.0063476562, -0.0079956055, -0.0096435547, -0.011535645, -0.013458252, -0.014953613, -0.016296387, -0.017364502, -0.01776123, -0.018280029, -0.018371582, -0.018035889, -0.017456055, -0.016479492, -0.015838623, -0.015167236, -0.015228271, -0.015380859, -0.015625, -0.015716553, -0.015411377, -0.015197754, -0.015106201, -0.01473999, -0.014221191, -0.013092041, -0.012054443, -0.010742188, -0.008605957, -0.006439209, -0.0038757324, -0.0016479492, 0.00079345703, 0.0026550293, 0.0045471191, 0.0065612793, 0.009185791, 0.011566162, 0.013427734, 0.014404297, 0.015075684, 0.015411377, 0.015319824, 0.014801025, 0.013977051, 0.013305664, 0.012634277, 0.011932373, 0.010864258, 0.0099182129, 0.0098571777, 0.0093994141, 0.0091247559, 0.0080566406, 0.0068359375, 0.0056762695, 0.0043640137, 0.0037841797, 0.0027770996, 0.0023803711, 0.0017700195, 0.0010375977, 0, -0.0011901855, -0.0020446777, -0.0028991699, -0.0037841797, -0.0050964355, -0.0057983398, -0.0063476562, -0.0065917969, -0.0063781738, -0.0061950684, -0.0056152344, -0.0050354004, -0.0045471191, -0.0035705566, -0.0025939941, -0.00091552734, 0.00082397461, 0.0028076172, 0.0045471191, 0.0061950684, 0.0078430176, 0.0096435547, 0.010742188, 0.011474609, 0.012176514, 0.01260376, 0.013153076, 0.013458252, 0.014129639, 0.014801025, 0.015228271, 0.015991211, 0.016204834, 0.01663208, 0.016601562, 0.016357422, 0.015808105, 0.014770508, 0.014007568, 0.012939453, 0.012268066, 0.011444092, 0.01071167, 0.010070801, 0.0086975098, 0.0075683594, 0.0058288574, 0.0040588379, 0.0018615723, 6.1035156e-05, -0.0015258789, -0.0029602051, -0.0040283203, -0.0055236816, -0.0065307617, -0.0078735352, -0.0090942383, -0.010131836, -0.010925293, -0.011260986, -0.011322021, -0.011199951, -0.010955811, -0.010925293, -0.0105896, -0.010375977, -0.0097351074, -0.0092163086, -0.0085754395, -0.0077819824, -0.007232666, -0.0067443848, -0.0063171387, -0.0060119629, -0.0053405762, -0.0050048828, -0.0045166016, -0.0040588379, -0.0035400391, -0.0030517578, -0.0029907227, -0.0027160645, -0.0028076172, -0.0025024414, -0.0021972656, -0.001953125, -0.0013122559, -0.00091552734, -0.00033569336, -0.00012207031, -0.00024414062, -0.00054931641, -0.0010375977, -0.0018615723, -0.0028686523, -0.0036010742, -0.0042724609, -0.0046691895, -0.0052490234, -0.0056152344, -0.0061035156, -0.0066833496, -0.0074768066, -0.0083007812, -0.0089111328, -0.0093688965, -0.0096435547, -0.0096740723, -0.0093994141, -0.0086669922, -0.0079040527, -0.0073242188, -0.007019043, -0.0067749023, -0.0067138672, -0.0064697266, -0.0060424805, -0.0054931641, -0.0048828125, -0.0044250488, -0.0039367676, -0.0031738281, -0.0024414062, -0.0014953613, -0.00039672852, 0.0007019043, 0.001739502, 0.0028991699, 0.0042724609, 0.005645752, 0.0072021484, 0.0087585449, 0.010192871, 0.011627197, 0.012542725, 0.013244629, 0.01361084, 0.013671875, 0.013580322, 0.013122559, 0.012512207, 0.012145996, 0.011993408, 0.011932373, 0.011779785, 0.011352539, 0.010742188, 0.0099487305, 0.0090942383, 0.0079956055, 0.0068054199, 0.0057373047, 0.0047912598, 0.004119873, 0.003326416, 0.0027770996, 0.0021057129, 0.0015869141, 0.0010375977, 0.00024414062, -0.00030517578, -0.00085449219, -0.0012512207, -0.0015869141, -0.002166748, -0.0025939941, -0.0032043457, -0.0038757324, -0.0043640137, -0.0044250488, -0.004486084, -0.004119873, -0.0036621094, -0.0028381348, -0.0020751953, -0.0012817383, -0.00064086914, -0.00012207031, 0.00051879883, 0.0012512207, 0.002166748, 0.0032653809, 0.0042419434, 0.0049743652, 0.0056152344, 0.0059509277, 0.0061645508, 0.0062866211, 0.0061035156, 0.0059204102, 0.005645752, 0.005645752, 0.0054016113, 0.0051879883, 0.0050354004, 0.0049133301, 0.0049133301, 0.004699707, 0.0045776367, 0.0043640137, 0.0040283203, 0.0033569336, 0.0025024414, 0.0013122559, 6.1035156e-05, -0.0009765625, -0.0021972656, -0.0028076172, -0.0036010742, -0.0038452148, -0.0042419434, -0.004699707, -0.0052185059, -0.0058898926, -0.0064697266, -0.0073242188, -0.0082397461, -0.0091552734, -0.0096740723, -0.0099182129, -0.0096435547, -0.0091552734, -0.0085449219, -0.0079040527, -0.0074768066, -0.0068969727, -0.0066223145, -0.0062561035, -0.005859375, -0.0055236816, -0.0049438477, -0.0045471191, -0.0039672852, -0.0035095215, -0.0030822754, -0.002746582, -0.0026245117, -0.0025939941, -0.002532959, -0.0024414062, -0.0020141602, -0.0015869141, -0.001159668, -0.00067138672, -0.0002746582, 0.00021362305, 0.00033569336, 0.00045776367, 0.00051879883, 0.00064086914, 0.00079345703, 0.00088500977, 0.00094604492, 0.00082397461, 0.00064086914, 0.00030517578, 0.00015258789, -0.00024414062, -0.00061035156, -0.0011901855, -0.0016174316, -0.0020446777, -0.0022888184, -0.0023803711, -0.0023803711, -0.0023803711, -0.0023498535, -0.0022888184, -0.0020446777, -0.0018005371, -0.0014343262, -0.0011901855, -0.00088500977, -0.00064086914, -0.00045776367, -0.00039672852, -0.00033569336, -0.00015258789, 0, 0.00030517578, 0.00054931641, 0.0010375977, 0.001373291, 0.0016784668, 0.001953125, 0.0023193359, 0.0028381348, 0.0035095215, 0.0043334961, 0.0049133301, 0.0056152344, 0.006072998, 0.0065917969, 0.0068664551, 0.0072021484, 0.0073547363, 0.0074768066, 0.0075378418, 0.0073547363, 0.0070800781, 0.0066223145, 0.0062866211, 0.005859375, 0.0054321289, 0.0050048828, 0.0045776367, 0.0043334961, 0.0039978027, 0.003692627, 0.0032958984, 0.0029602051, 0.0024108887, 0.0019226074, 0.0014343262, 0.00088500977, 0.00054931641, 0.00012207031, -0.00015258789, -0.00045776367, -0.00067138672, -0.0009765625, -0.0012512207, -0.0015869141, -0.001953125, -0.0022888184, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -6.1035156e-05, -3.0517578e-05, -6.1035156e-05, -3.0517578e-05, -6.1035156e-05, -6.1035156e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, -3.0517578e-05, 0, }; static const unsigned defaultClickEmphasisLength = sizeof(defaultClickEmphasis) / sizeof(*defaultClickEmphasis); muse-4.0.0/src/muse/device.h000066400000000000000000000036051404105311000156250ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: device.h,v 1.1.1.1 2003/10/27 18:51:58 wschweer Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DEVICE_H__ #define __DEVICE_H__ class QString; //--------------------------------------------------------- // Device //--------------------------------------------------------- class Device { public: enum DeviceType { MidiDevice, WaveDevice }; protected: QString _name; DeviceType _type; int _port; public: Device() {} virtual ~Device() {} Device(const QString& name, DeviceType t = MidiDevice) : _name(name), _type(t) {} virtual QString open(int) = 0; virtual void close() = 0; const QString& name() const { return _name; } void setName(const QString& s) { _name = s; } DeviceType type() const { return _type; } void setDeviceType(DeviceType t) { _type = t; } int port() const { return _port; } void setPort(int p) { _port = p; } }; #endif muse-4.0.0/src/muse/dialogs.cpp000066400000000000000000000141031404105311000163360ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: functions.cpp,v 1.20.2.19 2011/05/05 20:10 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "dialogs.h" #include // Forwards from header: #include "function_dialogs/gatetime.h" #include "function_dialogs/velocity.h" #include "function_dialogs/quantize.h" #include "function_dialogs/remove.h" #include "function_dialogs/deloverlaps.h" #include "function_dialogs/setlen.h" #include "function_dialogs/move.h" #include "function_dialogs/transpose.h" #include "function_dialogs/crescendo.h" #include "function_dialogs/legato.h" #include "components/pastedialog.h" #include "components/pasteeventsdialog.h" #include "xml.h" using namespace std; namespace MusEGui { GateTime* gatetime_dialog=NULL; Velocity* velocity_dialog=NULL; Quantize* quantize_dialog=NULL; Remove* erase_dialog=NULL; DelOverlaps* del_overlaps_dialog=NULL; Setlen* set_notelen_dialog=NULL; Move* move_notes_dialog=NULL; Transpose* transpose_dialog=NULL; Crescendo* crescendo_dialog=NULL; Legato* legato_dialog=NULL; PasteDialog* paste_dialog=NULL; PasteEventsDialog* paste_events_dialog=NULL; void init_function_dialogs() { // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). gatetime_dialog = new GateTime(); velocity_dialog = new Velocity(); quantize_dialog = new Quantize(); erase_dialog = new Remove(); del_overlaps_dialog = new DelOverlaps(); set_notelen_dialog = new Setlen(); move_notes_dialog = new Move(); transpose_dialog = new Transpose(); crescendo_dialog = new Crescendo(); legato_dialog = new Legato(); paste_dialog = new PasteDialog(); paste_events_dialog = new PasteEventsDialog(); } // // NOTE: This is called by MusE::deleteParentlessDialogs() // void destroy_function_dialogs() { if(gatetime_dialog) delete gatetime_dialog; if(velocity_dialog) delete velocity_dialog; if(quantize_dialog) delete quantize_dialog; if(erase_dialog) delete erase_dialog; if(del_overlaps_dialog) delete del_overlaps_dialog; if(set_notelen_dialog) delete set_notelen_dialog; if(move_notes_dialog) delete move_notes_dialog; if(transpose_dialog) delete transpose_dialog; if(crescendo_dialog) delete crescendo_dialog; if(legato_dialog) delete legato_dialog; if(paste_dialog) delete paste_dialog; if(paste_events_dialog) delete paste_events_dialog; } void retranslate_function_dialogs() { gatetime_dialog->retranslateUi(gatetime_dialog); velocity_dialog->retranslateUi(velocity_dialog); quantize_dialog->retranslateUi(quantize_dialog); erase_dialog->retranslateUi(erase_dialog); del_overlaps_dialog->retranslateUi(del_overlaps_dialog); set_notelen_dialog->retranslateUi(set_notelen_dialog); move_notes_dialog->retranslateUi(move_notes_dialog); transpose_dialog->retranslateUi(transpose_dialog); crescendo_dialog->retranslateUi(crescendo_dialog); legato_dialog->retranslateUi(legato_dialog); paste_dialog->retranslateUi(paste_dialog); paste_events_dialog->retranslateUi(paste_events_dialog); } void read_function_dialog_config(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "mod_len") gatetime_dialog->read_configuration(xml); else if (tag == "mod_velo") velocity_dialog->read_configuration(xml); else if (tag == "quantize") quantize_dialog->read_configuration(xml); else if (tag == "erase") erase_dialog->read_configuration(xml); else if (tag == "del_overlaps") del_overlaps_dialog->read_configuration(xml); else if (tag == "setlen") set_notelen_dialog->read_configuration(xml); else if (tag == "move") move_notes_dialog->read_configuration(xml); else if (tag == "transpose") transpose_dialog->read_configuration(xml); else if (tag == "crescendo") crescendo_dialog->read_configuration(xml); else if (tag == "legato") legato_dialog->read_configuration(xml); else if (tag == "pastedialog") paste_dialog->read_configuration(xml); else if (tag == "pasteeventsdialog") paste_events_dialog->read_configuration(xml); else xml.unknown("dialogs"); break; case MusECore::Xml::TagEnd: if (tag == "dialogs") return; default: break; } } } void write_function_dialog_config(int level, MusECore::Xml& xml) { xml.tag(level++, "dialogs"); gatetime_dialog->write_configuration(level, xml); velocity_dialog->write_configuration(level, xml); quantize_dialog->write_configuration(level, xml); erase_dialog->write_configuration(level, xml); del_overlaps_dialog->write_configuration(level, xml); set_notelen_dialog->write_configuration(level, xml); move_notes_dialog->write_configuration(level, xml); transpose_dialog->write_configuration(level, xml); crescendo_dialog->write_configuration(level, xml); legato_dialog->write_configuration(level, xml); paste_dialog->write_configuration(level, xml); paste_events_dialog->write_configuration(level, xml); xml.tag(level, "/dialogs"); } } muse-4.0.0/src/muse/dialogs.h000066400000000000000000000037151404105311000160120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dialogs.h,v 1.20.2.19 2011/05/05 20:10 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DIALOGS_H__ #define __DIALOGS_H__ // Forward declarations: namespace MusECore { class Xml; } namespace MusEGui { class GateTime; class Velocity; class Quantize; class Remove; class DelOverlaps; class Setlen; class Move; class Transpose; class Crescendo; class Legato; class PasteDialog; class PasteEventsDialog; extern GateTime* gatetime_dialog; extern Velocity* velocity_dialog; extern Quantize* quantize_dialog; extern Remove* erase_dialog; extern DelOverlaps* del_overlaps_dialog; extern Setlen* set_notelen_dialog; extern Move* move_notes_dialog; extern Transpose* transpose_dialog; extern Crescendo* crescendo_dialog; extern Legato* legato_dialog; extern PasteDialog* paste_dialog; extern PasteEventsDialog* paste_events_dialog; void init_function_dialogs(); void destroy_function_dialogs(); void retranslate_function_dialogs(); void read_function_dialog_config(MusECore::Xml& xml); void write_function_dialog_config(int level, MusECore::Xml& xml); } #endif muse-4.0.0/src/muse/driver/000077500000000000000000000000001404105311000155045ustar00rootroot00000000000000muse-4.0.0/src/muse/driver/CMakeLists.txt000066400000000000000000000040631404105311000202470ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB driver_source_files audiodev.cpp alsamidi.cpp alsatimer.cpp dummyaudio.cpp jack.cpp jackmidi.cpp posixtimer.cpp rtctimer.cpp ) if (HAVE_RTAUDIO) file (GLOB driver_source_files ${driver_source_files} rtaudio.cpp ) endif (HAVE_RTAUDIO) ## ## Define target ## add_library ( driver ${MODULES_BUILD} ${driver_source_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${driver_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( driver PROPERTIES OUTPUT_NAME muse_driver ) ## ## Linkage ## target_link_libraries ( driver ${ALSA_LIBRARIES} ${JACK_LIBRARIES} ${QT_LIBRARIES} ${RTAUDIO_LIBRARIES} mplugins ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS driver DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/driver/alsamidi.cpp000066400000000000000000002571661404105311000200140ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: alsamidi.cpp,v 1.8.2.7 2009/11/19 04:20:33 terminator356 Exp $ // (C) Copyright 2000-2001 Werner Schweer (ws@seh.de) // (C) Copyright 2011, 2015 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "alsamidi.h" #ifdef ALSA_SUPPORT #include #include "globals.h" #include "midi_consts.h" #include "../midiport.h" #include "../midiseq.h" #include "../midictrl.h" #include "../audio.h" #include "minstrument.h" #include "utils.h" #include "helper.h" #include "audiodev.h" #include "xml.h" #include "part.h" #include "gconfig.h" #include "track.h" #include "song.h" #include "muse_atomic.h" #include "lock_free_buffer.h" #include "evdata.h" #include // Enable debugging: //#define ALSA_DEBUG 1 // For debugging output: Uncomment the fprintf section. #define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); namespace MusECore { muse_atomic_t atomicAlsaMidiScanPending; static int alsaSeqFdi = -1; static int alsaSeqFdo = -1; snd_seq_t* alsaSeq = 0; static snd_seq_addr_t musePort; static snd_seq_addr_t announce_adr; //--------------------------------------------------------- // createAlsaMidiDevice // If name parameter is blank, creates a new (locally) unique one. //--------------------------------------------------------- MidiDevice* MidiAlsaDevice::createAlsaMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable 3: Writable + Readable { int ni = 0; if(name.isEmpty()) { for( ; ni < 65536; ++ni) { name = QString("alsa-midi-") + QString::number(ni); if(!MusEGlobal::midiDevices.find(name)) break; } } if(ni >= 65536) { fprintf(stderr, "MusE: createAlsaMidiDevice failed! Can't find an unused midi device name 'alsa-midi-[0-65535]'.\n"); return 0; } snd_seq_addr_t a; // From seq.h: "Special client (port) ids SND_SEQ_ADDRESS_UNKNOWN 253 = unknown source" // Hopefully we can use that as a 'valid' marker here. We can't use zero. a.client = SND_SEQ_ADDRESS_UNKNOWN; a.port = SND_SEQ_ADDRESS_UNKNOWN; MidiAlsaDevice* dev = new MidiAlsaDevice(a, name); dev->setrwFlags(rwflags); MusEGlobal::midiDevices.add(dev); return dev; } //--------------------------------------------------------- // MidiAlsaDevice //--------------------------------------------------------- MidiAlsaDevice::MidiAlsaDevice(const snd_seq_addr_t& a, const QString& n) : MidiDevice(n) { // _playEventFifo = new LockFreeBuffer(8192); adr = a; } MidiAlsaDevice::~MidiAlsaDevice() { // if(_playEventFifo) // delete _playEventFifo; } //--------------------------------------------------------- // selectWfd //--------------------------------------------------------- int MidiAlsaDevice::selectWfd() { return alsaSeqFdo; } //--------------------------------------------------------- // open //--------------------------------------------------------- QString MidiAlsaDevice::open() { _openFlags &= _rwFlags; // restrict to available bits if(!alsaSeq) { _state = QString("Unavailable"); return _state; } snd_seq_port_info_t *pinfo = NULL; snd_seq_port_subscribe_t* subs = NULL; DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Getting port info: address: %d:%d\n", adr.client, adr.port); if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN) { snd_seq_port_info_alloca(&pinfo); int rv = snd_seq_get_any_port_info(alsaSeq, adr.client, adr.port, pinfo); if(rv < 0) { fprintf(stderr, "MidiAlsaDevice::open Error getting port info: address: %d:%d: %s\n", adr.client, adr.port, snd_strerror(rv)); _state = QString(snd_strerror(rv)); return _state; } DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open: address: %d:%d\n", adr.client, adr.port); // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. snd_seq_port_subscribe_alloca(&subs); } QString estr; int wer = 0; int rer = 0; if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN) { int cap = snd_seq_port_info_get_capability(pinfo); #ifdef ALSA_DEBUG fprintf(stderr, "MidiAlsaDevice::open cap:%d\n", cap); #endif // subscribe for writing if (_openFlags & 1) { if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE) { snd_seq_port_subscribe_set_sender(subs, &musePort); snd_seq_port_subscribe_set_dest(subs, &adr); DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Checking write subscription: address: %d:%d\n", adr.client, adr.port); // Not already subscribed (or error)? Then try subscribing. if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) { //int error = snd_seq_subscribe_port(alsaSeq, subs); wer = snd_seq_subscribe_port(alsaSeq, subs); //if (error < 0) if(wer < 0) //return QString("Play: ")+QString(snd_strerror(error)); estr += (QString("Play: ") + QString(snd_strerror(wer)) + QString(" ")); } } if(!wer && (cap & SND_SEQ_PORT_CAP_WRITE)) _writeEnable = true; } // subscribe for reading if (_openFlags & 2) { if(cap & SND_SEQ_PORT_CAP_SUBS_READ) { snd_seq_port_subscribe_set_dest(subs, &musePort); snd_seq_port_subscribe_set_sender(subs, &adr); DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Checking read subscription: address: %d:%d\n", adr.client, adr.port); // Not already subscribed (or error)? Then try subscribing. if(snd_seq_get_port_subscription(alsaSeq, subs) < 0) { //int error = snd_seq_subscribe_port(alsaSeq, subs); rer = snd_seq_subscribe_port(alsaSeq, subs); //if (error < 0) if(rer < 0) //return QString("Rec: ") + QString(snd_strerror(error)); estr += (QString("Rec: ") + QString(snd_strerror(rer))); } } if(!rer && (cap & SND_SEQ_PORT_CAP_READ)) _readEnable = true; } } else { _state = QString("Unavailable"); return _state; } if(wer < 0 || rer < 0) { _state = estr; return _state; } _state = QString("OK"); return _state; } //--------------------------------------------------------- // close //--------------------------------------------------------- void MidiAlsaDevice::close() { if(!alsaSeq) { _state = QString("Unavailable"); return; } snd_seq_port_info_t *pinfo; snd_seq_port_subscribe_t* subs; if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN) { snd_seq_port_info_alloca(&pinfo); int rv = snd_seq_get_any_port_info(alsaSeq, adr.client, adr.port, pinfo); if(rv < 0) { fprintf(stderr, "MidiAlsaDevice::close Error getting port info: adr: %d:%d: %s\n", adr.client, adr.port, snd_strerror(rv)); _state = QString("Error on close"); return; } // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. snd_seq_port_subscribe_alloca(&subs); } if(adr.client == SND_SEQ_ADDRESS_UNKNOWN || adr.port == SND_SEQ_ADDRESS_UNKNOWN) { _readEnable = false; _writeEnable = false; _state = QString("Unavailable"); } else { int wer = 0; int rer = 0; int cap = snd_seq_port_info_get_capability(pinfo); #ifdef ALSA_DEBUG fprintf(stderr, "MidiAlsaDevice::close cap:%d\n", cap); #endif // This function appears to be called only by MidiPort::setMidiDevice(), // which closes then opens the device. // Because the open flags are set BEFORE setMidiDevice() is called, we must ignore the flags. // // NOTE: Tested: The read unsubscribe works ok but not the write. // As viewed in say, qjackctl, the connection is clearly lost, // but strangely the events are still accepted, ie, playback notes // are still heard etc. Tried an alsa midi device AND external fluidsynth inst. // // Also, jack running and with jack midi disabled, we get messages like // MidiAlsaDevice::0x84512c0 putEvent(): midi write error: No such device // dst 16:0 // only sometimes (not when playing notes), but with jack midi turned on, // we don't get the messages. With jack stopped we get the messages // no matter if jack midi is turned on or not. Tim. //if (_openFlags & 1) { //if (!(_openFlags & 1)) { if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE) { snd_seq_port_subscribe_set_sender(subs, &musePort); snd_seq_port_subscribe_set_dest(subs, &adr); // Already subscribed? Then unsubscribe. if(!snd_seq_get_port_subscription(alsaSeq, subs)) { wer = snd_seq_unsubscribe_port(alsaSeq, subs); //if(!wer) // _writeEnable = false; //else if(wer < 0) fprintf(stderr, "MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for writing: %s\n", adr.client, adr.port, snd_strerror(wer)); } //else //_writeEnable = false; } _writeEnable = false; } //if (_openFlags & 2) { //if (!(_openFlags & 2)) { if(cap & SND_SEQ_PORT_CAP_SUBS_READ) { snd_seq_port_subscribe_set_dest(subs, &musePort); snd_seq_port_subscribe_set_sender(subs, &adr); // Already subscribed? Then unsubscribe. if(!snd_seq_get_port_subscription(alsaSeq, subs)) { rer = snd_seq_unsubscribe_port(alsaSeq, subs); //if(!rer) // _readEnable = false; //else if(rer < 0) fprintf(stderr, "MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for reading: %s\n", adr.client, adr.port, snd_strerror(rer)); } //else // _readEnable = false; } _readEnable = false; } _state = QString("Closed"); } } //--------------------------------------------------------- // writeRouting //--------------------------------------------------------- void MidiAlsaDevice::writeRouting(int level, Xml& xml) const { // If this device is not actually in use by the song, do not write any routes. // This prevents bogus routes from being saved and propagated in the med file. Tim. if(midiPort() == -1) return; QString s; for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) { if((r->type == Route::TRACK_ROUTE && r->track) || (r->type != Route::TRACK_ROUTE && !r->name().isEmpty())) { s = "Route"; if(r->channel != -1) s += QString(" channel=\"%1\"").arg(r->channel); xml.tag(level++, s.toLatin1().constData()); xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, Xml::xmlString(name()).toLatin1().constData()); s = "dest"; if(r->type == Route::MIDI_DEVICE_ROUTE) s += QString(" devtype=\"%1\" name=\"%2\"/").arg(r->device->deviceType()).arg(Xml::xmlString(r->name())); else if(r->type == Route::TRACK_ROUTE) s += QString(" track=\"%1\"").arg(MusEGlobal::song->tracks()->index(r->track)); else s += QString(" type=\"%1\" name=\"%2\"/").arg(r->type).arg(Xml::xmlString(r->name())); xml.tag(level, s.toLatin1().constData()); xml.etag(level--, "Route"); } } } //--------------------------------------------------------- // putAlsaEvent // return false if event is delivered //--------------------------------------------------------- bool MidiAlsaDevice::putAlsaEvent(snd_seq_event_t* event) { if (MusEGlobal::midiOutputTrace) { fprintf(stderr, "ALSA MidiOut driver: <%s>: ", name().toLatin1().constData()); dump(event); } if(!_writeEnable || !alsaSeq || adr.client == SND_SEQ_ADDRESS_UNKNOWN || adr.port == SND_SEQ_ADDRESS_UNKNOWN) return true; int error; #ifdef ALSA_DEBUG fprintf(stderr, "MidiAlsaDevice::putAlsaEvent\n"); #endif do { error = snd_seq_event_output_direct(alsaSeq, event); int len = snd_seq_event_length(event); if (error == len) { // printf(".");fflush(stdout); return false; } if (error < 0) { if (error == -12) { return true; } else { fprintf(stderr, "MidiAlsaDevice::%p putAlsaEvent(): midi write error: %s\n", this, snd_strerror(error)); fprintf(stderr, " dst %d:%d\n", adr.client, adr.port); //exit(-1); } } else fprintf(stderr, "MidiAlsaDevice::putAlsaEvent(): midi write returns %d, expected %d: %s\n", error, len, snd_strerror(error)); } while (error == -12); return true; } //--------------------------------------------------------- // processEvent // return false if event is delivered //--------------------------------------------------------- bool MidiAlsaDevice::processEvent(const MidiPlayEvent& ev) { if (MusEGlobal::midiOutputTrace) { fprintf(stderr, "ALSA MidiOut pre-driver: <%s>: ", name().toLatin1().constData()); dumpMPEvent(&ev); } int chn = ev.channel(); int a = ev.dataA(); int b = ev.dataB(); snd_seq_event_t event; snd_seq_ev_clear(&event); event.queue = SND_SEQ_QUEUE_DIRECT; event.source = musePort; event.dest = adr; MidiInstrument::NoteOffMode nom = MidiInstrument::NoteOffAll; // Default to NoteOffAll in case of no port. const int mport = midiPort(); if(mport != -1) { if(MidiInstrument* mi = MusEGlobal::midiPorts[mport].instrument()) nom = mi->noteOffMode(); } switch(ev.type()) { case ME_NOTEON: if(b == 0) { // Handle zero-velocity note ons. Technically this is an error because internal midi paths // are now all 'note-off' without zero-vel note ons - they're converted to note offs. // Nothing should be setting a Note type Event's on velocity to zero. // But just in case... If we get this warning, it means there is still code to change. fprintf(stderr, "MidiAlsaDevice::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", ev.time(), ev.type(), chn, a, b); switch(nom) { // Instrument uses note offs. Convert to zero-vel note off. case MidiInstrument::NoteOffAll: if(MusEGlobal::midiOutputTrace) fprintf(stderr, "MidiOut: Alsa: Following event will be converted to zero-velocity note off:\n"); snd_seq_ev_set_noteoff(&event, chn, a, 0); break; // Instrument uses no note offs at all. Send as-is. case MidiInstrument::NoteOffNone: // Instrument converts all note offs to zero-vel note ons. Send as-is. case MidiInstrument::NoteOffConvertToZVNoteOn: snd_seq_ev_set_noteon(&event, chn, a, b); break; } } else snd_seq_ev_set_noteon(&event, chn, a, b); break; case ME_NOTEOFF: switch(nom) { // Instrument uses note offs. Send as-is. case MidiInstrument::NoteOffAll: snd_seq_ev_set_noteoff(&event, chn, a, b); break; // Instrument uses no note offs at all. Send nothing. Eat up the event - return false. case MidiInstrument::NoteOffNone: return false; // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. case MidiInstrument::NoteOffConvertToZVNoteOn: if(MusEGlobal::midiOutputTrace) fprintf(stderr, "MidiOut: Alsa: Following event will be converted to zero-velocity note on:\n"); snd_seq_ev_set_noteon(&event, chn, a, 0); break; } break; case ME_PROGRAM: { _curOutParamNums[chn].resetParamNums(); // Probably best to reset. _curOutParamNums[chn].setPROG(a); snd_seq_ev_set_pgmchange(&event, chn, a); } break; case ME_PITCHBEND: snd_seq_ev_set_pitchbend(&event, chn, a); break; case ME_POLYAFTER: snd_seq_ev_set_keypress(&event, chn, a, b); break; case ME_AFTERTOUCH: snd_seq_ev_set_chanpress(&event, chn, a); break; case ME_SYSEX: { // Probably best to reset all. resetCurOutParamNums(); // Stage the event data - is it OK to proceed? const size_t len = sysExOutProcessor()->stageEvData(ev.eventData(), ev.time()); if(len > 0) { unsigned char buf[len]; if(sysExOutProcessor()->getCurChunk(buf, MusEGlobal::sampleRate)) { snd_seq_ev_set_sysex(&event, len, buf); // NOTE: Don't move this out, 'buf' would go out of scope. return putAlsaEvent(&event); } } return true; } break; case ME_SONGPOS: event.data.control.value = a; event.type = SND_SEQ_EVENT_SONGPOS; break; case ME_CLOCK: event.type = SND_SEQ_EVENT_CLOCK; break; case ME_START: event.type = SND_SEQ_EVENT_START; break; case ME_CONTINUE: event.type = SND_SEQ_EVENT_CONTINUE; break; case ME_STOP: event.type = SND_SEQ_EVENT_STOP; break; case ME_CONTROLLER: { int a = ev.dataA(); int b = ev.dataB(); int chn = ev.channel(); if(a == CTRL_PITCH) snd_seq_ev_set_pitchbend(&event, chn, b); else if((a | 0xff) == CTRL_POLYAFTER) snd_seq_ev_set_keypress(&event, chn, a & 0x7f, b & 0x7f); else if(a == CTRL_AFTERTOUCH) snd_seq_ev_set_chanpress(&event, chn, b); else if(a == CTRL_PROGRAM) { _curOutParamNums[chn].resetParamNums(); // Probably best to reset. int hb = (b >> 16) & 0xff; int lb = (b >> 8) & 0xff; int pr = b & 0xff; _curOutParamNums[chn].setCurrentProg(pr, lb, hb); if(hb != 0xff) { snd_seq_ev_set_controller(&event, chn, CTRL_HBANK, hb); if(putAlsaEvent(&event)) return true; } if(lb != 0xff) { snd_seq_ev_set_controller(&event, chn, CTRL_LBANK, lb); if(putAlsaEvent(&event)) return true; } if(pr != 0xff) { snd_seq_ev_set_pgmchange(&event, chn, pr); if(putAlsaEvent(&event)) return true; } return false; } // Set this to 1 if ALSA cannot handle RPN NRPN etc. // NOTE: Although ideally it should be 0, there are problems with // letting ALSA do the 'composition' of the messages in putMidiEvent() - // chiefly that ALSA does not handle 7-bit (N)RPN controllers. // This define is kept because it is important to understand, try, and see // the difference between the two techniques, and possibly make it work... // Also see the corresponding define in MidiAlsaDevice::putMidiEvent(). #if 0 snd_seq_ev_set_controller(&event, chn, a, b); #else else if (a < CTRL_14_OFFSET) { // 7 Bit Controller if(a == CTRL_HRPN) _curOutParamNums[chn].setRPNH(b); else if(a == CTRL_LRPN) _curOutParamNums[chn].setRPNL(b); else if(a == CTRL_HNRPN) _curOutParamNums[chn].setNRPNH(b); else if(a == CTRL_LNRPN) _curOutParamNums[chn].setNRPNL(b); else if(a == CTRL_HBANK) { _curOutParamNums[chn].setBANKH(b); _curOutParamNums[chn].resetParamNums(); // Probably best to reset. } else if(a == CTRL_LBANK) { _curOutParamNums[chn].setBANKL(b); _curOutParamNums[chn].resetParamNums(); // Probably best to reset. } else if(a == CTRL_RESET_ALL_CTRL) _curOutParamNums[chn].resetParamNums(); // Probably best to reset. snd_seq_ev_set_controller(&event, chn, a, b); } else if (a < CTRL_RPN_OFFSET) { // 14 bit high resolution controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; #if 0 int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; snd_seq_ev_set_controller(&event, chn, ctrlH, dataH); if(putAlsaEvent(&event)) return true; snd_seq_ev_set_controller(&event, chn, ctrlL, dataL); return putAlsaEvent(&event); #else snd_seq_event_t ev; snd_seq_ev_clear(&ev); ev.queue = SND_SEQ_QUEUE_DIRECT; ev.source = musePort; ev.dest = adr; int n = (ctrlH << 7) + ctrlL; snd_seq_ev_set_controller(&ev, chn, n, b); ev.type = SND_SEQ_EVENT_CONTROL14; return putAlsaEvent(&ev); #endif } else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int data = b & 0x7f; if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setRPNL(ctrlL); snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, ctrlL); if(putAlsaEvent(&event)) return true; } if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setRPNH(ctrlH); snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, ctrlH); if(putAlsaEvent(&event)) return true; } if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAH(data); snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, data); if(putAlsaEvent(&event)) return true; } // Select null parameters so that subsequent data controller // events do not upset the last *RPN controller. Tim. if(MusEGlobal::config.midiSendNullParameters) { _curOutParamNums[chn].setRPNH(0x7f); snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, 0x7f); if(putAlsaEvent(&event)) return true; _curOutParamNums[chn].setRPNL(0x7f); snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, 0x7f); if(putAlsaEvent(&event)) return true; } return false; } else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int data = b & 0x7f; if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setNRPNL(ctrlL); snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, ctrlL); if(putAlsaEvent(&event)) return true; } if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setNRPNH(ctrlH); snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, ctrlH); if(putAlsaEvent(&event)) return true; } if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAH(data); snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, data); if(putAlsaEvent(&event)) return true; } if(MusEGlobal::config.midiSendNullParameters) { _curOutParamNums[chn].setNRPNH(0x7f); snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, 0x7f); if(putAlsaEvent(&event)) return true; _curOutParamNums[chn].setNRPNL(0x7f); snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, 0x7f); if(putAlsaEvent(&event)) return true; } return false; } else if (a < CTRL_RPN14_OFFSET) // Unaccounted for internal controller return true; else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setRPNL(ctrlL); snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, ctrlL); if(putAlsaEvent(&event)) return true; } if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setRPNH(ctrlH); snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, ctrlH); if(putAlsaEvent(&event)) return true; } if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAH(dataH); snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, dataH); if(putAlsaEvent(&event)) return true; } if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAL(dataL); snd_seq_ev_set_controller(&event, chn, CTRL_LDATA, dataL); if(putAlsaEvent(&event)) return true; } if(MusEGlobal::config.midiSendNullParameters) { _curOutParamNums[chn].setRPNH(0x7f); snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, 0x7f); if(putAlsaEvent(&event)) return true; _curOutParamNums[chn].setRPNL(0x7f); snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, 0x7f); if(putAlsaEvent(&event)) return true; } return false; } else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; #if 0 int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setNRPNL(ctrlL); snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, ctrlL); if(putAlsaEvent(&event)) return true; } if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setNRPNH(ctrlH); snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, ctrlH); if(putAlsaEvent(&event)) return true; } if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAH(dataH); snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, dataH); if(putAlsaEvent(&event)) return true; } if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAL(dataL); snd_seq_ev_set_controller(&event, chn, CTRL_LDATA, dataL); if(putAlsaEvent(&event)) return true; } #else int n = (ctrlH << 7) + ctrlL; snd_seq_ev_set_controller(&event, chn, n, b); event.type = SND_SEQ_EVENT_NONREGPARAM; if(putAlsaEvent(&event)) return true; #endif if(MusEGlobal::config.midiSendNullParameters) { _curOutParamNums[chn].setNRPNH(0x7f); snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, 0x7f); if(putAlsaEvent(&event)) return true; _curOutParamNums[chn].setNRPNL(0x7f); snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, 0x7f); if(putAlsaEvent(&event)) return true; } return false; } else { fprintf(stderr, "MidiAlsaDevice::processEvent: unknown controller type 0x%x\n", a); return true; } #endif } break; // ME_CONTROLLER default: if(MusEGlobal::debugMsg) fprintf(stderr, "MidiAlsaDevice::processEvent(): event type %d not implemented\n", ev.type()); return true; } return putAlsaEvent(&event); } //--------------------------------------------------------- // processMidi // Called from ALSA midi sequencer thread only. //--------------------------------------------------------- void MidiAlsaDevice::processMidi(unsigned int curFrame) { // Get the state of the stop flag. const bool do_stop = stopFlag(); //-------------------------------------------------------------------------------- // For now we stop ALL ring buffer processing until any sysex transmission is finished. // TODO FIXME Some realtime events ARE allowed while sending a sysex. Let that happen below... //-------------------------------------------------------------------------------- SysExOutputProcessor* sop = sysExOutProcessor(); switch(sop->state()) { case SysExOutputProcessor::Clear: // Proceed with normal ring buffer processing, below... break; case SysExOutputProcessor::Sending: { // Current chunk is meant for a future cycle? if(sop->curChunkFrame() > curFrame) break; const size_t len = sop->curChunkSize(); if(len > 0) { unsigned char buf[len]; if(sop->getCurChunk(buf, MusEGlobal::sampleRate)) { snd_seq_event_t event; snd_seq_ev_clear(&event); event.queue = SND_SEQ_QUEUE_DIRECT; event.source = musePort; event.dest = adr; snd_seq_ev_set_sysex(&event, len, buf); putAlsaEvent(&event); } } else { fprintf(stderr, "Error: MidiAlsaDevice::processMidi(): curChunkSize is zero while state is Sending\n"); // Should not happen. Protection against accidental zero chunk size while sending. // Let's just clear the thing. sop->clear(); } } break; case SysExOutputProcessor::Finished: { // Wait for the last chunk to transmit. if(sop->curChunkFrame() > curFrame) break; // Now we are truly done. Clear or reset the processor, which // sets the state to Clear. Prefer reset for speed but clear is OK, // the EvData reference will already have been released. //sop->reset(); sop->clear(); } break; } MidiPlayEvent buf_ev; // Transfer the user lock-free buffer events to the user sorted multi-set. // False = don't use the size snapshot, but update it. const unsigned int usr_buf_sz = eventBuffers(UserBuffer)->getSize(false); for(unsigned int i = 0; i < usr_buf_sz; ++i) { if(eventBuffers(UserBuffer)->get(buf_ev)) _outUserEvents.insert(buf_ev); } // Transfer the playback lock-free buffer events to the playback sorted multi-set. const unsigned int pb_buf_sz = eventBuffers(PlaybackBuffer)->getSize(false); for(unsigned int i = 0; i < pb_buf_sz; ++i) { // Are we stopping? Just remove the item. if(do_stop) eventBuffers(PlaybackBuffer)->remove(); // Otherwise get the item. else if(eventBuffers(PlaybackBuffer)->get(buf_ev)) _outPlaybackEvents.insert(buf_ev); } // Are we stopping? if(do_stop) { // Transport has stopped, purge ALL further scheduled playback events now. _outPlaybackEvents.clear(); // Reset the flag. setStopFlag(false); } iMPEvent impe_pb = _outPlaybackEvents.begin(); iMPEvent impe_us = _outUserEvents.begin(); bool using_pb; while(1) { if(impe_pb != _outPlaybackEvents.end() && impe_us != _outUserEvents.end()) using_pb = *impe_pb < *impe_us; else if(impe_pb != _outPlaybackEvents.end()) using_pb = true; else if(impe_us != _outUserEvents.end()) using_pb = false; else break; const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us; #ifdef ALSA_DEBUG fprintf(stderr, "INFO: MidiAlsaDevice::processMidi() evTime:%u curFrame:%u\n", e.time(), curFrame); #endif // Event is meant for next cycle? if(e.time() > curFrame) { #ifdef ALSA_DEBUG fprintf(stderr, " alsa play event is for future:%lu, breaking loop now\n", e.time()); #endif break; } // Is the sysex processor not in a Clear state? // Only realtime events are allowed to be sent while a sysex is in progress. // The delayed events list is pre-allocated with reserve(). // According to http://en.cppreference.com/w/cpp/container/vector/clear, // clear() shall not change the capacity. That statement references here: // https://stackoverflow.com/questions/18467624/what-does-the-standard-say- // about-how-calling-clear-on-a-vector-changes-the-capac/18467916#18467916 // But http://www.cplusplus.com/reference/vector/vector/clear // has not been updated to clarify this situation. if(sop->state() != SysExOutputProcessor::Clear) { // Is it a realtime message? if(e.type() >= 0xf8 && e.type() <= 0xff) // Process it now. processEvent(e); else // Store it for later. _sysExOutDelayedEvents->push_back(e); } else { // Process any delayed events. const unsigned int sz = _sysExOutDelayedEvents->size(); for(unsigned int i = 0; i < sz; ++i) processEvent(_sysExOutDelayedEvents->at(i)); // Let's check that capacity out of curiosity... const unsigned int cap = _sysExOutDelayedEvents->capacity(); // Done with the delayed event list. Clear it. _sysExOutDelayedEvents->clear(); // Throw up a developer warning if things are fishy. if(_sysExOutDelayedEvents->capacity() != cap) fprintf(stderr, "WARNING: MidiAlsaDevice::processMidi() delayed events vector " "capacity:%u is not the same as before clear:%u\n", (unsigned int)_sysExOutDelayedEvents->capacity(), cap); // If processEvent fails, although we would like to not miss events by keeping them // until next cycle and trying again, that can lead to a large backup of events // over a long time. So we'll just... miss them. processEvent(e); } // Successfully processed event. Remove it from FIFO. // C++11. if(using_pb) impe_pb = _outPlaybackEvents.erase(impe_pb); else impe_us = _outUserEvents.erase(impe_us); } } //--------------------------------------------------------- // initMidiAlsa // return true on error //--------------------------------------------------------- bool initMidiAlsa() { // The sequencer is required for ALSA midi. Initialize it if not already done. // But do not start the sequencer thread yet... the caller may want to do that a bit later. MusECore::initMidiSequencer(); if(alsaSeq) { DEBUG_PRST_ROUTES(stderr, "initMidiAlsa: alsaSeq already initialized, ignoring\n"); return false; } muse_atomic_init(&atomicAlsaMidiScanPending); muse_atomic_set(&atomicAlsaMidiScanPending, 0); if (MusEGlobal::debugMsg) fprintf(stderr, "initMidiAlsa\n"); int error = snd_seq_open(&alsaSeq, "hw", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); if (error < 0) { fprintf(stderr, "Could not open ALSA sequencer: %s\n", snd_strerror(error)); return true; } const int inCap = SND_SEQ_PORT_CAP_SUBS_READ; const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE; snd_seq_client_info_t *cinfo; snd_seq_client_info_alloca(&cinfo); snd_seq_client_info_set_client(cinfo, -1); while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { const char* cname = snd_seq_client_info_get_name(cinfo); //fprintf(stderr, "ALSA client name: %s\n", cname); // Put Midi Through and user clients after others. Insert other unwanted clients here: // p4.0.41 if(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT || strcmp("Midi Through", cname) == 0) continue; snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca(&pinfo); snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); snd_seq_port_info_set_port(pinfo, -1); while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) { unsigned int capability = snd_seq_port_info_get_capability(pinfo); if (capability & SND_SEQ_PORT_CAP_NO_EXPORT) // Ignore ports like "qjackctl" or "port". p4.0.41 continue; if ((capability & outCap) == 0) { const char *name = snd_seq_port_info_get_name(pinfo); if (strcmp("Timer", name) == 0 || strcmp("Announce", name) == 0 || strcmp("Receiver", name) == 0) continue; } snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo); const QString dev_name(snd_seq_port_info_get_name(pinfo)); MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI); const bool dev_found = dev; if(dev_found) { DEBUG_PRST_ROUTES(stderr, "initMidiAlsa device found:%p %s\n", dev, snd_seq_port_info_get_name(pinfo)); // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device? dev->setAddressClient(adr.client); dev->setAddressPort(adr.port); // The state should be 'Unavailable', change it to 'Closed' (or 'Open' below). //if(dev->midiPort() == -1) dev->setState("Closed"); } else dev = new MidiAlsaDevice(adr, QString(dev_name)); //MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo))); int flags = 0; if (capability & outCap) flags |= 1; if (capability & inCap) flags |= 2; dev->setrwFlags(flags); if (MusEGlobal::debugMsg) fprintf(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", snd_seq_port_info_get_name(pinfo), adr.client, adr.port, flags, capability); DEBUG_PRST_ROUTES(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", snd_seq_port_info_get_name(pinfo), adr.client, adr.port, flags, capability); if(dev_found) { // // The device should be closed right now. Open it if necessary, // // which will also change the state to 'Open'. // if(dev->midiPort() != -1) // dev->open(); } else MusEGlobal::midiDevices.add(dev); } } snd_seq_client_info_set_client(cinfo, -1); // Reset while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { const char* cname = snd_seq_client_info_get_name(cinfo); //fprintf(stderr, "ALSA client name: %s\n", cname); bool is_thru = (strcmp("Midi Through", cname) == 0); // Put Midi Through and user clients after others. Insert other unwanted clients here: // p4.0.41 if( !(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT || is_thru) ) continue; snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca(&pinfo); snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); snd_seq_port_info_set_port(pinfo, -1); while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) { unsigned int capability = snd_seq_port_info_get_capability(pinfo); if (capability & SND_SEQ_PORT_CAP_NO_EXPORT) // Ignore ports like "qjackctl" or "port". p4.0.41 continue; if ((capability & outCap) == 0) { const char *name = snd_seq_port_info_get_name(pinfo); if (strcmp("Timer", name) == 0 || strcmp("Announce", name) == 0 || strcmp("Receiver", name) == 0) continue; } snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo); const QString dev_name(snd_seq_port_info_get_name(pinfo)); MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI); const bool dev_found = dev; if(dev_found) { DEBUG_PRST_ROUTES(stderr, "initMidiAlsa device found:%p %s\n", dev, snd_seq_port_info_get_name(pinfo)); // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device? dev->setAddressClient(adr.client); dev->setAddressPort(adr.port); // The state should be 'Unavailable', change it to 'Closed' (or 'Open' below). //if(dev->midiPort() == -1) dev->setState("Closed"); } else dev = new MidiAlsaDevice(adr, dev_name); //MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo))); int flags = 0; if (capability & outCap) flags |= 1; if (capability & inCap) flags |= 2; dev->setrwFlags(flags); if(is_thru) // Don't auto-open Midi Through. dev->setOpenFlags(0); if (MusEGlobal::debugMsg) fprintf(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", snd_seq_port_info_get_name(pinfo), adr.client, adr.port, flags, capability); DEBUG_PRST_ROUTES(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n", snd_seq_port_info_get_name(pinfo), adr.client, adr.port, flags, capability); if(dev_found) { // // The device should be closed right now. Open it if necessary, // // which will also change the state to 'Open'. // if(dev->midiPort() != -1) // dev->open(); } else MusEGlobal::midiDevices.add(dev); } } //snd_seq_set_client_name(alsaSeq, "MusE Sequencer"); error = snd_seq_set_client_name(alsaSeq, MusEGlobal::audioDevice->clientName()); if (error < 0) { fprintf(stderr, "Alsa: Set client name failed: %s", snd_strerror(error)); return true; } int ci = snd_seq_poll_descriptors_count(alsaSeq, POLLIN); int co = snd_seq_poll_descriptors_count(alsaSeq, POLLOUT); if (ci > 1 || co > 1) { fprintf(stderr, "ALSA midi: cannot handle more than one poll fd\n"); abort(); } struct pollfd pfdi[ci]; struct pollfd pfdo[co]; snd_seq_poll_descriptors(alsaSeq, pfdi, ci, POLLIN); snd_seq_poll_descriptors(alsaSeq, pfdo, co, POLLOUT); alsaSeqFdo = pfdo[0].fd; alsaSeqFdi = pfdi[0].fd; int port = snd_seq_create_simple_port(alsaSeq, "MusE Port 0", inCap | outCap | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE, SND_SEQ_PORT_TYPE_APPLICATION); if (port < 0) { perror("create port"); exit(1); } musePort.port = port; musePort.client = snd_seq_client_id(alsaSeq); //----------------------------------------- // subscribe to "Announce" // this enables callbacks for any // alsa port changes //----------------------------------------- //snd_seq_addr_t aadr; announce_adr.client = SND_SEQ_CLIENT_SYSTEM; announce_adr.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE; snd_seq_port_subscribe_t* subs; snd_seq_port_subscribe_alloca(&subs); snd_seq_port_subscribe_set_dest(subs, &musePort); snd_seq_port_subscribe_set_sender(subs, &announce_adr); error = snd_seq_subscribe_port(alsaSeq, subs); if (error < 0) { fprintf(stderr, "Alsa: Subscribe System failed: %s", snd_strerror(error)); return true; } // The ALSA devices should be closed right now. Open them if necessary, // which will also change their states to 'Open'. for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { MidiDevice* d = *i; switch(d->deviceType()) { case MidiDevice::ALSA_MIDI: if(d->midiPort() != -1) d->open(); break; case MidiDevice::JACK_MIDI: case MidiDevice::SYNTH_MIDI: break; } } return false; } //--------------------------------------------------------- // exitMidiAlsa //--------------------------------------------------------- void exitMidiAlsa() { if(alsaSeq) { int error = 0; snd_seq_port_subscribe_t* subs; // Allocated on stack, no need to call snd_seq_port_subscribe_free() later. snd_seq_port_subscribe_alloca(&subs); //snd_seq_port_info_t *pinfo; //snd_seq_port_info_alloca(&pinfo); //snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); //snd_seq_port_info_set_addr(pinfo, &announce_adr); snd_seq_port_subscribe_set_dest(subs, &musePort); snd_seq_port_subscribe_set_sender(subs, &announce_adr); // Already subscribed? Then unsubscribe. if(!snd_seq_get_port_subscription(alsaSeq, subs)) { error = snd_seq_unsubscribe_port(alsaSeq, subs); if(error < 0) fprintf(stderr, "MusE: exitMidiAlsa: Error unsubscribing alsa midi Announce port %d:%d for reading: %s\n", announce_adr.client, announce_adr.port, snd_strerror(error)); } error = snd_seq_delete_simple_port(alsaSeq, musePort.port); if(error < 0) fprintf(stderr, "MusE: Could not delete ALSA simple port: %s\n", snd_strerror(error)); error = snd_seq_close(alsaSeq); if(error < 0) fprintf(stderr, "MusE: Could not close ALSA sequencer: %s\n", snd_strerror(error)); muse_atomic_destroy(&atomicAlsaMidiScanPending); } else fprintf(stderr, "initMidiAlsa: alsaSeq already exited, ignoring\n"); alsaSeq = 0; // Be sure to call MusEGlobal::midiSeq->msgUpdatePollFd() or midiSeq->updatePollFd() // for this to take effect. alsaSeqFdi = -1; alsaSeqFdo = -1; } //--------------------------------------------------------- // setAlsaClientName //--------------------------------------------------------- void setAlsaClientName(const char* name) { #ifdef ALSA_DEBUG fprintf(stderr, "setAlsaClientName: %s seq:%p\n", name, alsaSeq); #endif if(!alsaSeq) return; int error = snd_seq_set_client_name(alsaSeq, name); if (error < 0) fprintf(stderr, "setAlsaClientName: failed: %s", snd_strerror(error)); } struct AlsaPort { snd_seq_addr_t adr; char* name; int flags; AlsaPort(snd_seq_addr_t a, const char* s, int f) { adr = a; name = strdup(s); flags = f; } //~AlsaPort() { if(name) free(name); } }; static std::list portList; //--------------------------------------------------------- // alsaScanMidiPorts //--------------------------------------------------------- void alsaScanMidiPorts() { #ifdef ALSA_DEBUG fprintf(stderr, "alsa scan midi ports\n"); #endif DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts\n"); bool idling = false; portList.clear(); if(!alsaSeq) { // Reset this now. muse_atomic_set(&atomicAlsaMidiScanPending, 0); // Check for devices to disable for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { MidiAlsaDevice* d = dynamic_cast(*i); if(d == 0) continue; DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts stopped: disabling device:%p %s\n", d, d->name().toLatin1().constData()); if(!idling) { // Not much choice but to idle both the audio and midi threads since // midi does not idle while audio messages are being processed. MusEGlobal::audio->msgIdle(true); idling = true; } //operations.add(PendingOperationItem(d, SND_SEQ_ADDRESS_UNKNOWN, SND_SEQ_ADDRESS_UNKNOWN, PendingOperationItem::ModifyMidiDeviceAddress)); d->adr.client = SND_SEQ_ADDRESS_UNKNOWN; d->adr.port = SND_SEQ_ADDRESS_UNKNOWN; // Close to reset some device members. d->close(); // Update the port's state d->setState("Unavailable"); if(d->midiPort() != -1) MusEGlobal::midiPorts[d->midiPort()].setState(d->state()); } if(idling) { MusEGlobal::audio->msgIdle(false); // Update the GUI. MusEGlobal::song->update(SC_CONFIG); } return; } QString state; const int inCap = SND_SEQ_PORT_CAP_SUBS_READ; const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE; snd_seq_client_info_t* cinfo; snd_seq_client_info_alloca(&cinfo); snd_seq_client_info_set_client(cinfo, 0); while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) { snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca(&pinfo); snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); snd_seq_port_info_set_port(pinfo, -1); while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) { unsigned int capability = snd_seq_port_info_get_capability(pinfo); if (capability & SND_SEQ_PORT_CAP_NO_EXPORT) // Ignore ports like "qjackctl" or "port". p4.0.41 continue; if (((capability & outCap) == 0) && ((capability & inCap) == 0)) continue; snd_seq_addr_t adr; const char* name; adr = *snd_seq_port_info_get_addr(pinfo); name = snd_seq_port_info_get_name(pinfo); if (adr.client == musePort.client && adr.port == musePort.port) continue; int flags = 0; if (capability & outCap) flags |= 1; if (capability & inCap) flags |= 2; // fprintf(stderr, "ALSA port add: <%s>, flags %d\n", name, flags); portList.push_back(AlsaPort(adr, name, flags)); } } // Reset this now. muse_atomic_set(&atomicAlsaMidiScanPending, 0); // // check for devices to delete // for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { MidiAlsaDevice* d = dynamic_cast(*i); if (d == 0) continue; std::list::iterator k = portList.begin(); for (; k != portList.end(); ++k) { if (k->adr.client == d->adr.client && k->adr.port == d->adr.port) { break; } // Search by name if either of the client or port are 0. if(strcmp(k->name, d->name().toLatin1().constData()) == 0 && ((d->adr.client == SND_SEQ_ADDRESS_UNKNOWN && d->adr.port == SND_SEQ_ADDRESS_UNKNOWN) || (d->adr.client == SND_SEQ_ADDRESS_UNKNOWN && d->adr.port == k->adr.port) || (d->adr.port == SND_SEQ_ADDRESS_UNKNOWN && d->adr.client == k->adr.client))) break; } if (k == portList.end()) { DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts nulling adr op:ModifyMidiDeviceAddress device:%p %s\n", d, d->name().toLatin1().constData()); if(!idling) { // Not much choice but to idle both the audio and midi threads since // midi does not idle while audio messages are being processed. MusEGlobal::audio->msgIdle(true); idling = true; } //operations.add(PendingOperationItem(d, SND_SEQ_ADDRESS_UNKNOWN, SND_SEQ_ADDRESS_UNKNOWN, PendingOperationItem::ModifyMidiDeviceAddress)); d->adr.client = SND_SEQ_ADDRESS_UNKNOWN; d->adr.port = SND_SEQ_ADDRESS_UNKNOWN; // Close to reset some device members. d->close(); // Update the port's state d->setState("Unavailable"); if(d->midiPort() != -1) MusEGlobal::midiPorts[d->midiPort()].setState(d->state()); } } // // check for devices to add // // TODO: Possibly auto-add them to available midi ports. // for (std::list::iterator k = portList.begin(); k != portList.end(); ++k) { iMidiDevice i = MusEGlobal::midiDevices.begin(); for (;i != MusEGlobal::midiDevices.end(); ++i) { MidiAlsaDevice* d = dynamic_cast(*i); if (d == 0) continue; DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts add: checking port:%s client:%d port:%d device:%p %s client:%d port:%d\n", k->name, k->adr.client, k->adr.port, d, d->name().toLatin1().constData(), d->adr.client, d->adr.port); if (k->adr.client == d->adr.client && k->adr.port == d->adr.port) break; if((d->adr.client == SND_SEQ_ADDRESS_UNKNOWN || d->adr.port == SND_SEQ_ADDRESS_UNKNOWN) && strcmp(k->name, d->name().toLatin1().constData()) == 0) { if(d->adr.client != SND_SEQ_ADDRESS_UNKNOWN && d->adr.client != k->adr.client) { DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts: k->name:%s d->adr.client:%u != k->adr.client:%u", k->name, d->adr.client, k->adr.client); //continue; } if(d->adr.port != SND_SEQ_ADDRESS_UNKNOWN && d->adr.port != k->adr.port) { DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts: k->name:%s d->adr.port:%u != k->adr.port:%u", k->name, d->adr.port, k->adr.port); //continue; } //if(d->adr.client == SND_SEQ_ADDRESS_UNKNOWN) // d->adr.client = k->adr.client; //if(d->adr.port == SND_SEQ_ADDRESS_UNKNOWN) // d->adr.port = k->adr.port; DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts modifying adr op:ModifyMidiDeviceAddress device:%p %s client:%d port:%d\n", d, d->name().toLatin1().constData(), k->adr.client, k->adr.port); if(!idling) { MusEGlobal::audio->msgIdle(true); idling = true; } //operations.add(PendingOperationItem(d, k->adr.client, k->adr.port, PendingOperationItem::ModifyMidiDeviceAddress)); // FIXME: Re-subscribe to any ports for now, need a time delay to implement unsubscribe-exit events //operations.add(PendingOperationItem(d, k->flags, k->flags, PendingOperationItem::ModifyMidiDeviceFlags)); d->setAddressClient(k->adr.client); d->setAddressPort(k->adr.port); d->setrwFlags(k->flags); // FIXME: Re-subscribe to any ports for now, need a time delay to implement unsubscribe-exit events d->setOpenFlags(k->flags); if(d->midiPort() < 0) // Keep the device closed and update the state. d->setState("Closed"); else // Re-subscribe, open and update the port's state. MusEGlobal::midiPorts[d->midiPort()].setState(d->open()); break; } } if (i == MusEGlobal::midiDevices.end()) { if(!idling) { MusEGlobal::audio->msgIdle(true); idling = true; } // add device const QString dev_name(k->name); MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI); const bool dev_found = dev; if(dev_found) { // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device? dev->setAddressClient(k->adr.client); dev->setAddressPort(k->adr.port); } else dev = new MidiAlsaDevice(k->adr, dev_name); //MidiAlsaDevice* dev = new MidiAlsaDevice(k->adr, QString(k->name)); dev->setrwFlags(k->flags); DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts op:AddMidiDevice adding:%p %s adr.client:%d adr.port:%d\n", dev, dev->name().toLatin1().constData(), k->adr.client, k->adr.port); //operations.add(PendingOperationItem(MusEGlobal::midiDevices, dev, PendingOperationItem::AddMidiDevice)); //MusEGlobal::midiDevices.addOperation(dev, operations); if(!dev_found) MusEGlobal::midiDevices.add(dev); // Subscribe //dev->open(); } } //if(!operations.empty()) //{ // if(!idling) // MusEGlobal::audio->msgIdle(true); // //MusEGlobal::audio->msgExecutePendingOperations(operations, true); // // Execute directly both stages // operations.executeNonRTStage(); // operations.executeRTStage(); if(idling) { MusEGlobal::audio->msgIdle(false); // Update the GUI. MusEGlobal::song->update(SC_CONFIG); } //} } //--------------------------------------------------------- // alsaSelectRfd //--------------------------------------------------------- int alsaSelectRfd() { return alsaSeqFdi; } //--------------------------------------------------------- // alsaSelectWfd //--------------------------------------------------------- int alsaSelectWfd() { return alsaSeqFdo; } //--------------------------------------------------------- // processInput //--------------------------------------------------------- void alsaProcessMidiInput() { unsigned frame_ts = MusEGlobal::audio->curFrame(); DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput()\n"); if(!alsaSeq) return; MidiRecordEvent event; snd_seq_event_t* ev; for (;;) { int rv = snd_seq_event_input(alsaSeq, &ev); // fprintf(stderr, "AlsaInput %d\n", rv); if (rv < 0) { // fprintf(stderr, "AlsaMidi: read error %s\n", snd_strerror(rv)); return; } if (MusEGlobal::midiInputTrace) { switch(ev->type) { // Ignore some flooding events like clock. case SND_SEQ_EVENT_CLOCK: break; default: fprintf(stderr, "ALSA MidiIn driver: "); MidiAlsaDevice::dump(ev); break; } } switch(ev->type) { case SND_SEQ_EVENT_PORT_SUBSCRIBED: DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_SUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n", ev->data.connect.sender.client, ev->data.connect.sender.port, ev->data.connect.dest.client, ev->data.connect.dest.port); // MusEGlobal::audio->midiPortsChanged(); // signal gui // snd_seq_free_event(ev); // return; if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) { muse_atomic_set(&atomicAlsaMidiScanPending, 1); MusEGlobal::audio->sendMsgToGui('P'); } snd_seq_free_event(ev); if(rv == 0) return; continue; case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_UNSUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n", ev->data.connect.sender.client, ev->data.connect.sender.port, ev->data.connect.dest.client, ev->data.connect.dest.port); // MusEGlobal::audio->midiPortsChanged(); // signal gui // snd_seq_free_event(ev); // return; if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) { muse_atomic_set(&atomicAlsaMidiScanPending, 1); MusEGlobal::audio->sendMsgToGui('P'); } snd_seq_free_event(ev); if(rv == 0) return; continue; case SND_SEQ_EVENT_CLIENT_START: DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_CLIENT_START adr: %d:%d\n", ev->data.addr.client, ev->data.addr.port); //alsaScanMidiPorts(); // MusEGlobal::audio->midiPortsChanged(); // signal gui // snd_seq_free_event(ev); // return; if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) { muse_atomic_set(&atomicAlsaMidiScanPending, 1); MusEGlobal::audio->sendMsgToGui('P'); } snd_seq_free_event(ev); if(rv == 0) return; continue; case SND_SEQ_EVENT_CLIENT_EXIT: DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_CLIENT_EXIT adr: %d:%d\n", ev->data.addr.client, ev->data.addr.port); //snd_seq_free_event(ev); // return; // on first start of a software synthesizer we only // get CLIENT_START event and no PORT_START, why? //alsaScanMidiPorts(); // MusEGlobal::audio->midiPortsChanged(); // signal gui // snd_seq_free_event(ev); // return; if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) { muse_atomic_set(&atomicAlsaMidiScanPending, 1); MusEGlobal::audio->sendMsgToGui('P'); } snd_seq_free_event(ev); if(rv == 0) return; continue; case SND_SEQ_EVENT_PORT_START: DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_START adr: %d:%d\n", ev->data.addr.client, ev->data.addr.port); //alsaScanMidiPorts(); // MusEGlobal::audio->midiPortsChanged(); // signal gui // snd_seq_free_event(ev); // return; if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) { muse_atomic_set(&atomicAlsaMidiScanPending, 1); MusEGlobal::audio->sendMsgToGui('P'); } snd_seq_free_event(ev); if(rv == 0) return; continue; case SND_SEQ_EVENT_PORT_EXIT: DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_EXIT adr: %d:%d\n", ev->data.addr.client, ev->data.addr.port); //alsaScanMidiPorts(); // MusEGlobal::audio->midiPortsChanged(); // signal gui // snd_seq_free_event(ev); // return; if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) { muse_atomic_set(&atomicAlsaMidiScanPending, 1); MusEGlobal::audio->sendMsgToGui('P'); } snd_seq_free_event(ev); if(rv == 0) return; continue; // case SND_SEQ_EVENT_PORT_SUBSCRIBED: // case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // case SND_SEQ_EVENT_CLIENT_START: // case SND_SEQ_EVENT_CLIENT_EXIT: // case SND_SEQ_EVENT_PORT_START: // case SND_SEQ_EVENT_PORT_EXIT: // if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0) // { // muse_atomic_set(&atomicAlsaMidiScanPending, 1); // MusEGlobal::audio->sendMsgToGui('P'); // } // snd_seq_free_event(ev); // if(rv == 0) // return; // continue; } int curPort = -1; MidiAlsaDevice* mdev = 0; // // find real source device // for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { if((*i)->deviceType() != MidiDevice::ALSA_MIDI) continue; MidiAlsaDevice* d = static_cast(*i); if(d->adr.client == ev->source.client && d->adr.port == ev->source.port) { curPort = d->midiPort(); mdev = d; } } if (mdev == 0 || curPort == -1) { if (MusEGlobal::debugMsg) { fprintf(stderr, "no port %d:%d found for received alsa event\n", ev->source.client, ev->source.port); } snd_seq_free_event(ev); //return; if(rv == 0) return; continue; } event.setType(0); // mark as unused event.setPort(curPort); event.setB(0); //MidiInstrument* instr = MusEGlobal::midiPorts[curPort].inputInstrument(); switch(ev->type) { case SND_SEQ_EVENT_NOTEON: if(ev->data.note.velocity == 0) { // Convert zero-velocity note ons to note offs as per midi spec. event.setChannel(ev->data.note.channel); event.setType(ME_NOTEOFF); event.setA(ev->data.note.note); event.setB(ev->data.note.velocity); } else { event.setChannel(ev->data.note.channel); event.setType(ME_NOTEON); event.setA(ev->data.note.note); event.setB(ev->data.note.velocity); } break; case SND_SEQ_EVENT_NOTEOFF: event.setChannel(ev->data.note.channel); event.setType(ME_NOTEOFF); event.setA(ev->data.note.note); event.setB(ev->data.note.velocity); break; case SND_SEQ_EVENT_KEYPRESS: event.setChannel(ev->data.note.channel); event.setType(ME_POLYAFTER); event.setA(ev->data.note.note); event.setB(ev->data.note.velocity); break; case SND_SEQ_EVENT_CHANPRESS: event.setChannel(ev->data.control.channel); event.setType(ME_AFTERTOUCH); event.setA(ev->data.control.value); break; case SND_SEQ_EVENT_PGMCHANGE: event.setChannel(ev->data.control.channel); event.setType(ME_PROGRAM); event.setA(ev->data.control.value); break; case SND_SEQ_EVENT_PITCHBEND: event.setChannel(ev->data.control.channel); event.setType(ME_PITCHBEND); event.setA(ev->data.control.value); break; case SND_SEQ_EVENT_CONTROLLER: event.setChannel(ev->data.control.channel); event.setType(ME_CONTROLLER); event.setA(ev->data.control.param); event.setB(ev->data.control.value); break; case SND_SEQ_EVENT_CLOCK: if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) mdev->midiClockInput(frame_ts); break; case SND_SEQ_EVENT_START: #ifdef ALSA_DEBUG if(MusEGlobal::midiInputTrace) fprintf(stderr, "alsaProcessMidiInput: start port:%d curFrame:%u\n", curPort, frame_ts); #endif MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_START); break; case SND_SEQ_EVENT_CONTINUE: MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_CONTINUE); break; case SND_SEQ_EVENT_STOP: MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_STOP); break; case SND_SEQ_EVENT_TICK: MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_TICK); break; case SND_SEQ_EVENT_SYSEX: { EvData ed; const unsigned char* p = (unsigned char*)ev->data.ext.ptr; // Process the input. Create the event data only if finished. if(mdev->sysExInProcessor()->processInput( &ed, p, ev->data.ext.len, frame_ts) != SysExInputProcessor::Finished) break; #ifdef ALSA_DEBUG fprintf(stderr, "alsaProcessMidiInput: SysEx: frame_ts:%u startFrame:%u\n", frame_ts, (unsigned int)mdev->sysExInProcessor()->startFrame()); #endif // Finished composing the sysex data. // Mark the frame timestamp as the frame at which the sysex started. frame_ts = mdev->sysExInProcessor()->startFrame(); event.setType(ME_SYSEX); event.setData(ed); } break; case SND_SEQ_EVENT_PORT_SUBSCRIBED: case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // write port is released break; case SND_SEQ_EVENT_SONGPOS: MusEGlobal::midiSyncContainer.setSongPosition(curPort, ev->data.control.value); break; case SND_SEQ_EVENT_SENSING: break; case SND_SEQ_EVENT_QFRAME: MusEGlobal::midiSyncContainer.mtcInputQuarter(curPort, ev->data.control.value); break; // case SND_SEQ_EVENT_CLIENT_START: // case SND_SEQ_EVENT_CLIENT_EXIT: // case SND_SEQ_EVENT_CLIENT_CHANGE: // case SND_SEQ_EVENT_PORT_CHANGE: // case SND_SEQ_EVENT_SONGSEL: // case SND_SEQ_EVENT_TIMESIGN: // case SND_SEQ_EVENT_KEYSIGN: // case SND_SEQ_EVENT_SETPOS_TICK: // case SND_SEQ_EVENT_SETPOS_TIME: // case SND_SEQ_EVENT_TEMPO: // case SND_SEQ_EVENT_TUNE_REQUEST: // case SND_SEQ_EVENT_RESET: // case SND_SEQ_EVENT_NOTE: // case SND_SEQ_EVENT_CONTROL14: // case SND_SEQ_EVENT_NONREGPARAM: // fprintf(stderr, "ALSA Midi input: NONREGPARAM ch:%u param:%u value:%d\n", // ev->data.control.channel, // ev->data.control.param, // ev->data.control.value); // event.setChannel(ev->data.control.channel); // event.setType(ME_CONTROLLER); // event.setA(ev->data.control.param); // event.setB(ev->data.control.value); // break; // case SND_SEQ_EVENT_REGPARAM: default: fprintf(stderr, "ALSA Midi input: type %d not handled\n", ev->type); break; } if(event.type()) { // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. event.setTime(frame_ts); event.setTick(MusEGlobal::lastExtMidiSyncTick); mdev->recordEvent(event); } snd_seq_free_event(ev); if (rv == 0) break; } } //--------------------------------------------------------- // dump // static //--------------------------------------------------------- void MidiAlsaDevice::dump(const snd_seq_event_t* ev) { switch(ev->type) { case SND_SEQ_EVENT_PORT_SUBSCRIBED: fprintf(stderr, "SND_SEQ_EVENT_PORT_SUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n", ev->data.connect.sender.client, ev->data.connect.sender.port, ev->data.connect.dest.client, ev->data.connect.dest.port); break; case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: fprintf(stderr, "SND_SEQ_EVENT_PORT_UNSUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n", ev->data.connect.sender.client, ev->data.connect.sender.port, ev->data.connect.dest.client, ev->data.connect.dest.port); break; case SND_SEQ_EVENT_CLIENT_START: fprintf(stderr, "SND_SEQ_EVENT_CLIENT_START adr: %d:%d\n", ev->data.addr.client, ev->data.addr.port); break; case SND_SEQ_EVENT_CLIENT_EXIT: fprintf(stderr, "SND_SEQ_EVENT_CLIENT_EXIT adr: %d:%d\n", ev->data.addr.client, ev->data.addr.port); break; case SND_SEQ_EVENT_PORT_START: fprintf(stderr, "SND_SEQ_EVENT_PORT_START adr: %d:%d\n", ev->data.addr.client, ev->data.addr.port); break; case SND_SEQ_EVENT_PORT_EXIT: fprintf(stderr, "SND_SEQ_EVENT_PORT_EXIT adr: %d:%d\n", ev->data.addr.client, ev->data.addr.port); break; case SND_SEQ_EVENT_CONTROLLER: fprintf(stderr, "SND_SEQ_EVENT_CONTROLLER chan:%u param:%u value:%d\n", ev->data.control.channel, ev->data.control.param, ev->data.control.value); break; case SND_SEQ_EVENT_NOTE: fprintf(stderr, "SND_SEQ_EVENT_NOTE chan:%u note:%u velocity:%u off_velocity:%u duration:%u\n", ev->data.note.channel, ev->data.note.note, ev->data.note.velocity, ev->data.note.off_velocity, ev->data.note.duration); break; case SND_SEQ_EVENT_NOTEON: fprintf(stderr, "SND_SEQ_EVENT_NOTEON chan:%u note:%u velocity:%u\n", ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); break; case SND_SEQ_EVENT_NOTEOFF: fprintf(stderr, "SND_SEQ_EVENT_NOTEOFF chan:%u note:%u velocity:%u\n", ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); break; case SND_SEQ_EVENT_KEYPRESS: fprintf(stderr, "SND_SEQ_EVENT_KEYPRESS chan:%u note:%u velocity:%u\n", ev->data.note.channel, ev->data.note.note, ev->data.note.velocity); break; case SND_SEQ_EVENT_CHANPRESS: fprintf(stderr, "SND_SEQ_EVENT_CHANPRESS chan:%u value:%d\n", ev->data.control.channel, ev->data.control.value); break; case SND_SEQ_EVENT_PGMCHANGE: fprintf(stderr, "SND_SEQ_EVENT_PGMCHANGE chan:%u value:%d\n", ev->data.control.channel, ev->data.control.value); break; case SND_SEQ_EVENT_PITCHBEND: fprintf(stderr, "SND_SEQ_EVENT_PITCHBEND chan:%u value:%d\n", ev->data.control.channel, ev->data.control.value); break; case SND_SEQ_EVENT_CLOCK: fprintf(stderr, "SND_SEQ_EVENT_CLOCK\n"); break; case SND_SEQ_EVENT_START: fprintf(stderr, "SND_SEQ_EVENT_START\n"); break; case SND_SEQ_EVENT_CONTINUE: fprintf(stderr, "SND_SEQ_EVENT_CONTINUE\n"); break; case SND_SEQ_EVENT_STOP: fprintf(stderr, "SND_SEQ_EVENT_STOP\n"); break; case SND_SEQ_EVENT_TICK: fprintf(stderr, "SND_SEQ_EVENT_TICK\n"); break; case SND_SEQ_EVENT_SYSEX: fprintf(stderr, "SND_SEQ_EVENT_SYSEX len:%u data: ", ev->data.ext.len); for(unsigned int i = 0; i < ev->data.ext.len && i < 16; ++i) fprintf(stderr, "%0x ", ((unsigned char*)ev->data.ext.ptr)[i]); if(ev->data.ext.len >= 16) fprintf(stderr, "..."); fprintf(stderr, "\n"); break; case SND_SEQ_EVENT_SONGPOS: fprintf(stderr, "SND_SEQ_EVENT_SONGPOS value:%d\n", ev->data.control.value); break; case SND_SEQ_EVENT_SENSING: fprintf(stderr, "SND_SEQ_EVENT_SENSING\n"); break; case SND_SEQ_EVENT_QFRAME: fprintf(stderr, "SND_SEQ_EVENT_QFRAME value:%d\n", ev->data.control.value); break; case SND_SEQ_EVENT_CONTROL14: fprintf(stderr, "SND_SEQ_EVENT_CONTROL14 ch:%u param:%u value:%d\n", ev->data.control.channel, ev->data.control.param, ev->data.control.value); break; case SND_SEQ_EVENT_NONREGPARAM: fprintf(stderr, "SND_SEQ_EVENT_NONREGPARAM ch:%u param:%u value:%d\n", ev->data.control.channel, ev->data.control.param, ev->data.control.value); break; case SND_SEQ_EVENT_REGPARAM: fprintf(stderr, "SND_SEQ_EVENT_REGPARAM ch:%u param:%u value:%d\n", ev->data.control.channel, ev->data.control.param, ev->data.control.value); break; // case SND_SEQ_EVENT_CLIENT_CHANGE: // case SND_SEQ_EVENT_PORT_CHANGE: // case SND_SEQ_EVENT_SONGSEL: // case SND_SEQ_EVENT_TIMESIGN: // case SND_SEQ_EVENT_KEYSIGN: // case SND_SEQ_EVENT_SETPOS_TICK: // case SND_SEQ_EVENT_SETPOS_TIME: // case SND_SEQ_EVENT_TEMPO: // case SND_SEQ_EVENT_TUNE_REQUEST: // case SND_SEQ_EVENT_RESET: default: fprintf(stderr, "ALSA dump event: unknown type:%u\n", ev->type); break; } } } // namespace MusECore #else // ALSA_SUPPORT namespace MusECore { bool initMidiAlsa() { return false; } void exitMidiAlsa() {} int alsaSelectRfd() { return -1; } int alsaSelectWfd() { return -1; } void alsaProcessMidiInput() { } void alsaScanMidiPorts() { } void setAlsaClientName(const char*) { } } #endif // ALSA_SUPPORT muse-4.0.0/src/muse/driver/alsamidi.h000066400000000000000000000075271404105311000174530ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: alsamidi.h,v 1.2 2004/01/14 09:06:43 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // (C) Copyright 2011, 2015 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ALSAMIDI_H__ #define __ALSAMIDI_H__ #include "config.h" #ifdef ALSA_SUPPORT #include #include "mpevent.h" #include "mididev.h" #endif // ALSA_SUPPORT namespace MusECore { #ifdef ALSA_SUPPORT class Xml; //--------------------------------------------------------- // MidiAlsaDevice //--------------------------------------------------------- class MidiAlsaDevice : public MidiDevice { public: snd_seq_addr_t adr; private: // Fifo for midi playback events sent from audio thread: // The audio thread will gather the events in _playEvents for the // convenience of its sorting, then dump them to this FIFO so that // a driver or device may read it, possibly from another thread (ALSA driver). SeqMPEventList _outPlaybackEvents; SeqMPEventList _outUserEvents; // Return false if event is delivered. bool processEvent(const MidiPlayEvent& ev); inline virtual void processInput() {} inline virtual int selectRfd() { return -1; } virtual int selectWfd(); bool putAlsaEvent(snd_seq_event_t*); public: MidiAlsaDevice(const snd_seq_addr_t&, const QString& name); virtual ~MidiAlsaDevice(); static MidiDevice* createAlsaMidiDevice(QString name = "", int rwflags = 3); // 1:Writable 2: Readable 3: Writable + Readable static void dump(const snd_seq_event_t* ev); virtual QString open(); virtual void close(); // The meaning of the returned pointer depends on the driver. // For Jack it returns the address of a Jack port, for ALSA it return the address of a snd_seq_addr_t. // For ALSA midi, in/out client ports are the same. That is, ALSA midi client ports can be both r/w. inline virtual void* inClientPort() { return (void*)&adr; } inline virtual void* outClientPort() { return (void*)&adr; } virtual void writeRouting(int, Xml&) const; virtual inline MidiDeviceType deviceType() const { return ALSA_MIDI; } // Play all events up to current frame. virtual void processMidi(unsigned int curFrame = 0); virtual void setAddressClient(int client) { adr.client = client; } virtual void setAddressPort(int port) { adr.port = port; } // We (ab)use the ALSA value SND_SEQ_ADDRESS_UNKNOWN to mean 'unavailable' - if BOTH client and port equal it. virtual bool isAddressUnknown() const { return adr.client == SND_SEQ_ADDRESS_UNKNOWN || adr.port == SND_SEQ_ADDRESS_UNKNOWN; } }; #endif // ALSA_SUPPORT extern bool initMidiAlsa(); extern void exitMidiAlsa(); extern int alsaSelectRfd(); extern int alsaSelectWfd(); extern void alsaProcessMidiInput(); extern void alsaScanMidiPorts(); extern void setAlsaClientName(const char*); } // namespace MusECore #endif muse-4.0.0/src/muse/driver/alsatimer.cpp000066400000000000000000000233221404105311000201730ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: alsatimer.cpp,v 1.1.2.9 2009/03/28 01:46:10 terminator356 Exp $ // // Plenty of code borrowed from timer.c example in // alsalib 1.0.7 // // (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) // (C) Copyright 2016-2017 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "alsatimer.h" #ifdef ALSA_SUPPORT #include #include #include "globals.h" #define ALSA_TIMER_DEBUG 0 namespace MusECore { AlsaTimer::AlsaTimer() { if(TIMER_DEBUG || ALSA_TIMER_DEBUG) fprintf(stderr,"AlsaTimer::AlsaTimer(this=%p) called\n",this); handle = NULL; id = NULL; info = NULL; params = NULL; fds = NULL; } AlsaTimer::~AlsaTimer() { if(TIMER_DEBUG || ALSA_TIMER_DEBUG) fprintf(stderr,"AlsaTimer::~AlsaTimer(this=%p) called\n",this); if (handle) snd_timer_close(handle); if (id) snd_timer_id_free(id); if (info) snd_timer_info_free(info); if (params) snd_timer_params_free(params); if(fds) free(fds); } signed int AlsaTimer::initTimer(unsigned long desiredFrequency) { if(TIMER_DEBUG || ALSA_TIMER_DEBUG) fprintf(stderr, "AlsaTimer::initTimer(this=%p)\n",this); if(fds || id || info || params) { fprintf(stderr, "AlsaTimer::initTimer(): called on initialised timer!\n"); if(fds) return fds->fd; return 0; } snd_timer_id_malloc(&id); snd_timer_id_set_class(id, SND_TIMER_CLASS_NONE); snd_timer_info_malloc(&info); snd_timer_params_malloc(¶ms); int best_dev = SND_TIMER_GLOBAL_SYSTEM; int best_devclass = SND_TIMER_CLASS_GLOBAL; int best_sclass = SND_TIMER_CLASS_NONE; int best_card = 0; int best_subdevice = 0; unsigned long best_freq = 0; int err; snd_timer_query_t *timer_query = NULL; if(snd_timer_query_open(&timer_query, "hw", 0) >= 0) { int device = SND_TIMER_GLOBAL_SYSTEM; int devclass = SND_TIMER_CLASS_GLOBAL; int sclass = SND_TIMER_CLASS_NONE; int card = 0; int subdevice = 0; while(snd_timer_query_next_device(timer_query, id) >= 0) { devclass = snd_timer_id_get_class(id); if(devclass < 0) break; sclass = snd_timer_id_get_sclass(id); if(sclass < 0) sclass = 0; card = snd_timer_id_get_card(id); if(card < 0) card = 0; device = snd_timer_id_get_device(id); if(device < 0) device = 0; subdevice = snd_timer_id_get_subdevice(id); if(subdevice < 0) subdevice = 0; snprintf(timername, sizeof(timername) - 1, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i", devclass, sclass, card, device, subdevice); if(snd_timer_open(&handle, timername, SND_TIMER_OPEN_NONBLOCK) >= 0) { if(snd_timer_info(handle, info) >= 0) { // Select a non slave timer with the lowest resolution value if(snd_timer_info_is_slave(info) == 0) { unsigned long freq = setTimerFreq(desiredFrequency); if(MusEGlobal::debugMsg) fprintf(stderr, "AlsaTimer::initTimer(): Checked timer:%s got frequency:%lu Hz\n", snd_timer_info_get_name(info), freq); if(freq > best_freq) { best_freq = freq; best_dev = device; best_devclass = devclass; best_sclass = sclass; best_card = card; best_subdevice = subdevice; } } } snd_timer_close(handle); } } snd_timer_query_close(timer_query); } sprintf(timername, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i", best_devclass, best_sclass, best_card, best_dev, best_subdevice); if ((err = snd_timer_open(&handle, timername, SND_TIMER_OPEN_NONBLOCK))<0) { fprintf(stderr, "AlsaTimer::initTimer(): timer open %i (%s)\n", err, snd_strerror(err)); return -1; } if ((err = snd_timer_info(handle, info)) < 0) { fprintf(stderr, "AlsaTimer::initTimer(): timer info %i (%s)\n", err, snd_strerror(err)); return -1; } //if(debugMsg) fprintf(stderr, "AlsaTimer::initTimer(): best available ALSA timer: %s\n", snd_timer_info_get_name(info)); count = snd_timer_poll_descriptors_count(handle); fds = (pollfd *)calloc(count, sizeof(pollfd)); if (fds == NULL) { fprintf(stderr, "AlsaTimer::initTimer(): malloc error\n"); return -1; } if ((err = snd_timer_poll_descriptors(handle, fds, count)) < 0) { fprintf(stderr, "AlsaTimer::initTimer(): snd_timer_poll_descriptors error: %s\n", snd_strerror(err)); return -1; } return fds->fd; } unsigned long AlsaTimer::setTimerResolution(unsigned long resolution) { if(TIMER_DEBUG || ALSA_TIMER_DEBUG) fprintf(stderr, "AlsaTimer::setTimerResolution(%lu)\n",resolution); /* Resolution of an AlsaTimer is fixed - it cannot be set */ return 0; } unsigned long AlsaTimer::setTimerFreq(unsigned long freq) { if(freq == 0) return 0; signed int err; if(TIMER_DEBUG || ALSA_TIMER_DEBUG) fprintf(stderr, "AlsaTimer::setTimerFreq(this=%p)\n",this); const long int res = snd_timer_info_get_resolution(info); const long int adj_res = 1000000000L / res; long int setTick = adj_res / freq; if(setTick <= 0) setTick = 1; snd_timer_params_set_auto_start(params, 1); if(!snd_timer_info_is_slave(info)) { snd_timer_params_set_ticks(params, setTick); if(snd_timer_params_get_ticks(params) < 1) snd_timer_params_set_ticks(params, 1); } else snd_timer_params_set_ticks(params, 1); if((err = snd_timer_params(handle, params)) < 0) { const int num_freqs = 10; const unsigned int freqs[num_freqs] = {32768, 16384, 8192, 4096, 2048, 1024, 1000, 500, 250, 100}; int found_idx = -1; if(!snd_timer_info_is_slave(info)) { for(int i = 0; i < num_freqs; ++i) { const unsigned int f = freqs[i]; if(f >= freq) continue; long int t = adj_res / f; if(t <= 0) t = 1; snd_timer_params_set_ticks(params, t); if(snd_timer_params_get_ticks(params) < 1) snd_timer_params_set_ticks(params, 1); if((err = snd_timer_params(handle, params)) == 0) { found_idx = i; break; } } if(found_idx == -1) { if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Cannot find a suitable ALSA timer frequency. Your system may need adjustment.\n"); snd_timer_params_set_ticks(params, 1); return 0; } } if(found_idx >= 0) { if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Cannot set requested ALSA timer frequency (%luHz). Your system may need adjustment.\n" " Timer frequency set to best value: %liHz\n", freq, 1000000000L / snd_timer_info_get_resolution(info) / snd_timer_params_get_ticks(params)); } } const long int ticks = snd_timer_params_get_ticks(params); const long int cur_freq = adj_res / ticks; if(setTick <= 0) { // Return, print error if freq is below 500 (timing will suffer). if(cur_freq < 500) { if(MusEGlobal::debugMsg) { fprintf(stderr,"AlsaTimer::setTimerFreq(): requested freq %lu Hz too high for timer (max is %ld)\n", freq, adj_res); fprintf(stderr," freq stays at %ld Hz\n", cur_freq); } } } if(MusEGlobal::debugMsg) fprintf(stderr, "AlsaTimer::setTimerFreq(%lu): Using %li tick(s)\n", freq, ticks); return (unsigned long)cur_freq; } unsigned long AlsaTimer::getTimerResolution() { return snd_timer_info_get_resolution(info); } unsigned long AlsaTimer::getTimerFreq() { return (1000000000L / snd_timer_info_get_resolution(info)) / snd_timer_params_get_ticks(params); } bool AlsaTimer::startTimer() { if(TIMER_DEBUG || ALSA_TIMER_DEBUG) fprintf(stderr, "AlsaTimer::startTimer(this=%p): handle=%p\n",this,handle); int err; if ((err = snd_timer_start(handle)) < 0) { fprintf(stderr, "AlsaTimer::startTimer(): timer start %i (%s)\n", err, snd_strerror(err)); return false; } return true; } bool AlsaTimer::stopTimer() { int err; if(TIMER_DEBUG || ALSA_TIMER_DEBUG) fprintf(stderr, "AlsaTimer::stopTimer(this=%p): handle=%p\n",this,handle); if ((err = snd_timer_stop(handle)) < 0) { fprintf(stderr, "AlsaTimer::stopTimer(): timer stop %i (%s)\n", err, snd_strerror(err)); return false; } return true; } unsigned long AlsaTimer::getTimerTicks(bool printTicks) { snd_timer_read_t tr; tr.ticks = 0; while (snd_timer_read(handle, &tr, sizeof(tr)) == sizeof(tr)) { if (printTicks) { fprintf(stderr, "TIMER: resolution = %uns, ticks = %u\n", tr.resolution, tr.ticks); } } return tr.ticks; } } // namespace MusECore #endif // ALSA_SUPPORT muse-4.0.0/src/muse/driver/alsatimer.h000066400000000000000000000043101404105311000176340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: alsatimer.h,v 1.1.2.4 2009/03/09 02:05:18 terminator356 Exp $ // // Plenty of code borrowed from timer.c example in // alsalib 1.0.7 // // (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ALSATIMER_H__ #define __ALSATIMER_H__ #include "config.h" #ifdef ALSA_SUPPORT #include "alsa/asoundlib.h" #include "timerdev.h" namespace MusECore { //--------------------------------------------------------- // AlsaTimer //--------------------------------------------------------- class AlsaTimer : public Timer{ snd_timer_t *handle; snd_timer_id_t *id; snd_timer_info_t *info; snd_timer_params_t *params; struct pollfd *fds; char timername[256]; signed int count; public: AlsaTimer(); virtual ~AlsaTimer(); virtual const char* getTimerName() { return "AlsaTimer"; } virtual signed int initTimer(unsigned long desiredFrequency); virtual unsigned long setTimerResolution(unsigned long resolution); virtual unsigned long getTimerResolution(); virtual unsigned long setTimerFreq(unsigned long freq); virtual unsigned long getTimerFreq(); virtual bool startTimer(); virtual bool stopTimer(); virtual unsigned long getTimerTicks(bool printTicks=false); }; } // namespace MusECore #endif // ALSA_SUPPORT #endif //__ALSATIMER_H__ muse-4.0.0/src/muse/driver/audiodev.cpp000066400000000000000000000126341404105311000200160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "audio.h" #include "audiodev.h" #include "globals.h" #include "large_int.h" #ifdef _WIN32 #include #endif // Forwards from header: #include "pos.h" namespace MusECore { AudioDevice::AudioDevice() { _dummyState = Audio::STOP; _dummyPos = 0; // Default 10 seconds, like Jack2. Jack1 is 2 seconds. _syncTimeout = 10.0; _syncTimeoutCounter = 0.0; _dummyStatePending = 0; _dummyPosPending = 0; } //-------------------------------------- // Timing functions: //-------------------------------------- uint64_t AudioDevice::systemTimeUS() const { struct timeval t; gettimeofday(&t, 0); //fprintf(stderr, "%lu %lu\n", t.tv_sec, t.tv_usec); // Note I observed values coming out of order! Causing some problems. return ((uint64_t)t.tv_sec * 1000000UL) + (uint64_t)t.tv_usec; } //-------------------------------------- // Transport functions: //-------------------------------------- bool AudioDevice::processTransport(unsigned int frames) { const int state_pending = _dummyStatePending; // Snapshots. const int pos_pending = _dummyPosPending; // _dummyStatePending = -1; // Reset. _dummyPosPending = -1; // if(!MusEGlobal::audio->isRunning()) { if(MusEGlobal::debugMsg) puts("Dummy sync: Called when audio is not running!\n"); return false; } // STOP -> STOP, STOP -> START_PLAY, PLAY -> START_PLAY all count as 'syncing'. if(((_dummyState == Audio::STOP || _dummyState == Audio::PLAY) && state_pending == Audio::START_PLAY) || (_dummyState == Audio::STOP && state_pending == Audio::STOP) ) { _syncTimeoutCounter = (float)frames / (float)MusEGlobal::sampleRate; // (Re)start the timeout counter... if(pos_pending != -1) _dummyPos = pos_pending; // Set the new dummy position. if((_dummyState == Audio::STOP || _dummyState == Audio::PLAY) && state_pending == Audio::START_PLAY) _dummyState = Audio::START_PLAY; } else // All other states such as START_PLAY -> STOP, PLAY -> STOP. if(state_pending != -1 && state_pending != _dummyState) { _syncTimeoutCounter = 0.0; // Reset. _dummyState = state_pending; } // Is the sync timeout counter running? if(_syncTimeoutCounter > 0.0) { //printf("Jack processAudio dummy sync: state:%d pending:%d\n", jackAudio->dummyState, state_pending); // Is MusE audio ready to roll? if(MusEGlobal::audio->sync(_dummyState, _dummyPos)) { _syncTimeoutCounter = 0.0; // Reset. // We're ready. Switch to PLAY state. if(_dummyState == Audio::START_PLAY) _dummyState = Audio::PLAY; } else { _syncTimeoutCounter += (float)frames / (float)MusEGlobal::sampleRate; // Has the counter surpassed the timeout limit? if(_syncTimeoutCounter > _syncTimeout) { if (MusEGlobal::debugMsg) puts("Dummy sync timeout! Starting anyway...\n"); _syncTimeoutCounter = 0.0; // Reset. // We're not ready, but no time left - gotta roll anyway. Switch to PLAY state, similar to how Jack is supposed to work. if(_dummyState == Audio::START_PLAY) { _dummyState = Audio::PLAY; // Docs say sync will be called with Rolling state when timeout expires. MusEGlobal::audio->sync(_dummyState, _dummyPos); } } } } // Now call the audio process. // Don't process while we're syncing. ToDO: May need to deliver silence in process! //if(jackAudio->getState() != Audio::START_PLAY) MusEGlobal::audio->process(frames); // Is the transport playing? Advance the transport position. if(_dummyState == Audio::PLAY) _dummyPos += frames; return true; } unsigned AudioDevice::transportSyncToPlayDelay() const { return 0; } unsigned AudioDevice::transportRelocateOrPlayDelay() const { return 0; } void AudioDevice::startTransport() { _dummyStatePending = Audio::START_PLAY; } void AudioDevice::stopTransport() { _dummyStatePending = Audio::STOP; } void AudioDevice::seekTransport(unsigned frame) { _dummyPosPending = frame; // STOP -> STOP means seek in stop mode. PLAY -> START_PLAY means seek in play mode. _dummyStatePending = (_dummyState == Audio::STOP ? Audio::STOP : Audio::START_PLAY); } void AudioDevice::seekTransport(const Pos &p) { _dummyPosPending = p.frame(); // STOP -> STOP means seek in stop mode. PLAY -> START_PLAY means seek in play mode. _dummyStatePending = (_dummyState == Audio::STOP ? Audio::STOP : Audio::START_PLAY); } } // namespace MusECore muse-4.0.0/src/muse/driver/audiodev.h000066400000000000000000000172641404105311000174670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: audiodev.h,v 1.5.2.2 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __AUDIODEV_H__ #define __AUDIODEV_H__ #include #include #ifdef _WIN32 #include #else #include #include #endif #include namespace MusECore { // Forward declarations: class Pos; //--------------------------------------------------------- // AudioDevice //--------------------------------------------------------- class AudioDevice { private: // // The following are for the built-in transport: // // The amount of time to wait before sync times out, in seconds. float _syncTimeout; float _syncTimeoutCounter; // In seconds. int _dummyState; unsigned int _dummyPos; volatile int _dummyStatePending; volatile unsigned int _dummyPosPending; public: enum { DUMMY_AUDIO=0, JACK_AUDIO=1, RTAUDIO_AUDIO=2 }; enum PortType { UnknownType=0, AudioPort=1, MidiPort=2 }; enum PortDirection { UnknownDirection=0, InputPort=1, OutputPort=2 }; AudioDevice(); virtual ~AudioDevice() {} virtual int deviceType() const = 0; virtual const char* driverName() const = 0; virtual bool isRealtime() = 0; virtual int realtimePriority() const = 0; // return zero if not realtime // Returns true on success. virtual bool start(int priority) = 0; virtual void stop () = 0; // Estimated current frame. // This is meant to be called from threads other than the process thread. virtual unsigned framePos() const = 0; // A constantly increasing counter, incremented by segment size at cycle start. virtual unsigned frameTime() const = 0; virtual uint64_t systemTimeUS() const; virtual unsigned curTransportFrame() const { return 0; } // These are meant to be called from inside process thread only. virtual unsigned framesAtCycleStart() const = 0; virtual unsigned framesSinceCycleStart() const = 0; virtual float* getBuffer(void* port, unsigned long nframes) = 0; virtual std::list outputPorts(bool midi = false, int aliases = -1) = 0; virtual std::list inputPorts(bool midi = false, int aliases = -1) = 0; virtual void registerClient() = 0; virtual const char* clientName() = 0; virtual void* registerOutPort(const char* /*name*/, bool /*midi*/) = 0; virtual void* registerInPort(const char* /*name*/, bool /*midi*/) = 0; virtual float getDSP_Load() { return 0.0f; } virtual PortType portType(void*) const = 0; virtual PortDirection portDirection(void*) const = 0; virtual void unregisterPort(void*) = 0; virtual bool connect(void* src, void* dst) = 0; virtual bool connect(const char* src, const char* dst) = 0; virtual bool disconnect(void* src, void* dst) = 0; virtual bool disconnect(const char* src, const char* dst) = 0; virtual int connections(void* /*clientPort*/) = 0; virtual bool portConnectedTo(void* our_port, const char* port) = 0; // Returns true if the ports are connected. virtual bool portsCanDisconnect(void* src, void* dst) const = 0; // Returns true if the ports are found and they are connected. virtual bool portsCanDisconnect(const char* src, const char* dst) const = 0; // Returns true if the ports are not connected and CAN be connected. virtual bool portsCanConnect(void* src, void* dst) const = 0; // Returns true if the ports are found and they are not connected and CAN be connected. virtual bool portsCanConnect(const char* src, const char* dst) const = 0; // Returns true if the ports CAN be connected. virtual bool portsCompatible(void* src, void* dst) const = 0; // Returns true if the ports are found and they CAN be connected. virtual bool portsCompatible(const char* src, const char* dst) const = 0; virtual void setPortName(void* p, const char* n) = 0; virtual void* findPort(const char* name) = 0; // preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. virtual char* portName(void* port, char* str, int str_size, int preferred_name_or_alias = -1) = 0; virtual const char* canonicalPortName(void*) = 0; virtual unsigned int portLatency(void* port, bool capture) const = 0; virtual void setFreewheel(bool f) = 0; virtual void graphChanged() {} virtual void registrationChanged() {} virtual void connectionsChanged() {} // Whether the device has its own transport (Jack transport etc.), beyond the one built into this class. virtual bool hasOwnTransport() const { return false; }; // Whether the device supports timebase master capabilities. virtual bool hasTimebaseMaster() const { return false; }; // Sets or resets timebase master. Returns 0 on success. Otherwise, may return an error code. virtual int setMaster(bool f, bool unconditional = false) = 0; //---------------------------------------------- // Functions for built-in transport. // Audio devices are free to ignore, defer to, // override etc. the built-in transport. // For example with the Jack driver, we can use // either Jack's transport or this built-in one. //---------------------------------------------- // Sets the amount of time to wait before sync times out, in microseconds. // Note that at least with the Jack driver, this function seems not realtime friendly. virtual void setSyncTimeout(unsigned usec) { _syncTimeout = (float)usec / 1000000.0; } // The number of frames that the driver waits to switch to PLAY // mode after the audio sync function says it is ready to roll. // For example Jack Transport waits one cycle while our own transport does not. virtual unsigned transportSyncToPlayDelay() const; // The number of frames that the driver waits to relocate or switch to PLAY // mode after the a call to relocation or seek. // For example Jack Transport waits two cycles while our own transport does not. virtual unsigned transportRelocateOrPlayDelay() const; virtual int getState() { return _dummyState; } virtual unsigned getCurFrame() const { return _dummyPos; } virtual void startTransport(); virtual void stopTransport(); virtual void seekTransport(unsigned frame); virtual void seekTransport(const Pos &p); // This includes the necessary calls to Audio::sync() and ultimately // Audio::process(). There's no need to call them from the device code. // Returns true on success. virtual bool processTransport(unsigned int frames); }; } // namespace MusECore #endif muse-4.0.0/src/muse/driver/dummyaudio.cpp000066400000000000000000000333011404105311000203650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dummyaudio.cpp,v 1.3.2.16 2009/12/20 05:00:35 terminator356 Exp $ // (C) Copyright 2002-2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "muse_math.h" #include #include #include #ifndef _WIN32 #include #endif #include #include #include "config.h" #include "audio.h" #include "audiodev.h" #include "globals.h" #include "song.h" // #include "driver/alsatimer.h" #include "pos.h" #include "gconfig.h" #include "large_int.h" #include "al/al.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_DUMMY(dev, format, args...) // fprintf(dev, format, ##args); namespace MusECore { //--------------------------------------------------------- // DummyAudioDevice //--------------------------------------------------------- class DummyAudioDevice : public AudioDevice { private: pthread_t dummyThread; float* buffer; int _realTimePriority; bool _freewheelMode; // Critical variables that need to all update at once. // We employ a 'flipping' technique. unsigned _framesAtCycleStart[2]; uint64_t _timeUSAtCycleStart[2]; unsigned _frameCounter[2]; unsigned _criticalVariablesIdx; public: // Time in microseconds at which the driver was created. uint64_t _start_timeUS; virtual const char* driverName() const { return "DummyAudioDevice"; } // For callback usage only. void setCriticalVariables(unsigned segmentSize) { static bool _firstTime = true; const unsigned idx = (_criticalVariablesIdx + 1) % 2; _timeUSAtCycleStart[idx] = systemTimeUS(); // Let these start at zero and only increment on subsequent callbacks. if(!_firstTime) { _framesAtCycleStart[idx] = _framesAtCycleStart[_criticalVariablesIdx] + segmentSize; _frameCounter[idx] = _frameCounter[_criticalVariablesIdx] + segmentSize; } _firstTime = false; // Now 'flip' the variables all at once. _criticalVariablesIdx = idx; } DummyAudioDevice(); virtual ~DummyAudioDevice() { free(buffer); } virtual inline int deviceType() const { return DUMMY_AUDIO; } // Returns true on success. virtual bool start(int); virtual void stop (); virtual unsigned framePos() const { // Not much choice but to do this: const unsigned int facs = framesAtCycleStart(); const unsigned int fscs = framesSinceCycleStart(); DEBUG_DUMMY(stderr, "DummyAudioDevice::framePos framesAtCycleStart:%u framesSinceCycleStart:%u\n", facs, fscs); return facs + fscs; } // These are meant to be called from inside process thread only. virtual unsigned framesAtCycleStart() const { return _framesAtCycleStart[_criticalVariablesIdx]; } virtual unsigned framesSinceCycleStart() const { const uint64_t ct = systemTimeUS(); DEBUG_DUMMY(stderr, "DummyAudioDevice::framesSinceCycleStart systemTimeUS:%lu timeUSAtCycleStart:%lu\n", ct, _timeUSAtCycleStart[_criticalVariablesIdx]); // Do not round up here since time resolution is higher than (audio) frame resolution. unsigned f = muse_multiply_64_div_64_to_64(ct - _timeUSAtCycleStart[_criticalVariablesIdx], MusEGlobal::sampleRate, 1000000UL); // Safety due to inaccuracies. It cannot be after the segment, right? if(f >= MusEGlobal::segmentSize) f = MusEGlobal::segmentSize - 1; return f; } virtual float* getBuffer(void* /*port*/, unsigned long nframes) { if (nframes > MusEGlobal::segmentSize) { fprintf(stderr, "DummyAudioDevice::getBuffer nframes > segment size\n"); exit(-1); } return buffer; } virtual std::list outputPorts(bool midi = false, int aliases = -1); virtual std::list inputPorts(bool midi = false, int aliases = -1); virtual void registerClient() {} virtual const char* clientName() { return "MusE"; } virtual void* registerOutPort(const char*, bool) { return (void*)1; } virtual void* registerInPort(const char*, bool) { return (void*)2; } virtual AudioDevice::PortType portType(void*) const { return UnknownType; } virtual AudioDevice::PortDirection portDirection(void*) const { return UnknownDirection; } virtual void unregisterPort(void*) {} virtual bool connect(void* /*src*/, void* /*dst*/) { return false; } virtual bool connect(const char* /*src*/, const char* /*dst*/) { return false; } virtual bool disconnect(void* /*src*/, void* /*dst*/) { return false; } virtual bool disconnect(const char* /*src*/, const char* /*dst*/) { return false; } virtual int connections(void* /*clientPort*/) { return 0; } virtual bool portConnectedTo(void*, const char*) { return false; } virtual bool portsCanDisconnect(void* /*src*/, void* /*dst*/) const { return false; }; virtual bool portsCanDisconnect(const char* /*src*/, const char* /*dst*/) const { return false; } virtual bool portsCanConnect(void* /*src*/, void* /*dst*/) const { return false; } virtual bool portsCanConnect(const char* /*src*/, const char* /*dst*/) const { return false; } virtual bool portsCompatible(void* /*src*/, void* /*dst*/) const { return false; } virtual bool portsCompatible(const char* /*src*/, const char* /*dst*/) const { return false; } virtual void setPortName(void*, const char*) {} virtual void* findPort(const char*) { return 0;} // preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. virtual char* portName(void*, char* str, int str_size, int /*preferred_name_or_alias*/ = -1) { if(str_size == 0) return 0; str[0] = '\0'; return str; } virtual const char* canonicalPortName(void*) { return 0; } virtual unsigned int portLatency(void* /*port*/, bool /*capture*/) const { return 0; } virtual unsigned frameTime() const { return _frameCounter[_criticalVariablesIdx]; } virtual bool isRealtime() { return MusEGlobal::realTimeScheduling; } //virtual int realtimePriority() const { return 40; } virtual int realtimePriority() const { return _realTimePriority; } bool freewheelMode() const { return _freewheelMode; } virtual void setFreewheel(bool v) { _freewheelMode = v; } virtual int setMaster(bool, bool /*unconditional*/ = false) { return 1; } }; DummyAudioDevice* dummyAudio = 0; DummyAudioDevice::DummyAudioDevice() : AudioDevice() { _freewheelMode = false; // MusEGlobal::sampleRate = MusEGlobal::config.dummyAudioSampleRate; // MusEGlobal::segmentSize = MusEGlobal::config.dummyAudioBufSize; MusEGlobal::sampleRate = MusEGlobal::config.deviceAudioSampleRate; // Make sure the AL namespace variables mirror our variables. AL::sampleRate = MusEGlobal::sampleRate; MusEGlobal::segmentSize = MusEGlobal::config.deviceAudioBufSize; MusEGlobal::projectSampleRate = MusEGlobal::sampleRate; #ifdef _WIN32 buffer = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(buffer == NULL) { fprintf(stderr, "ERROR: DummyAudioDevice ctor: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)&buffer, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DummyAudioDevice ctor: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) buffer[q] = MusEGlobal::denormalBias; } else memset(buffer, 0, sizeof(float) * MusEGlobal::segmentSize); dummyThread = 0; _start_timeUS = systemTimeUS(); _criticalVariablesIdx = 0; for(unsigned x = 0; x < 2; ++x) { _timeUSAtCycleStart[x] = 0; _framesAtCycleStart[x] = 0; _frameCounter[x] = 0; } } //--------------------------------------------------------- // exitDummyAudio //--------------------------------------------------------- void exitDummyAudio() { if(dummyAudio) delete dummyAudio; dummyAudio = NULL; MusEGlobal::audioDevice = NULL; } //--------------------------------------------------------- // initDummyAudio //--------------------------------------------------------- bool initDummyAudio() { dummyAudio = new DummyAudioDevice(); MusEGlobal::audioDevice = dummyAudio; return false; } //--------------------------------------------------------- // outputPorts //--------------------------------------------------------- std::list DummyAudioDevice::outputPorts(bool midi, int /*aliases*/) { std::list clientList; if(!midi) { clientList.push_back(QString("output1")); clientList.push_back(QString("output2")); } return clientList; } //--------------------------------------------------------- // inputPorts //--------------------------------------------------------- std::list DummyAudioDevice::inputPorts(bool midi, int /*aliases*/) { std::list clientList; if(!midi) { clientList.push_back(QString("input1")); clientList.push_back(QString("input2")); } return clientList; } //--------------------------------------------------------- // dummyLoop //--------------------------------------------------------- static void* dummyLoop(void* ptr) { DummyAudioDevice *drvPtr = (DummyAudioDevice *)ptr; bool freewheel = false; for(;;) { drvPtr->setCriticalVariables(MusEGlobal::segmentSize); if(MusEGlobal::audio->isRunning()) { // Detect if freewheel was requested. const bool fm = drvPtr->freewheelMode(); if(fm != freewheel) { freewheel = fm; MusEGlobal::audio->setFreewheel(fm); } // Use our built-in transport, which INCLUDES the necessary // calls to Audio::sync() and ultimately Audio::process(), // and increments the built-in play position. drvPtr->processTransport(MusEGlobal::segmentSize); } if(!freewheel) usleep(MusEGlobal::segmentSize*1000000/MusEGlobal::sampleRate); } pthread_exit(0); } //--------------------------------------------------------- // start // Returns true on success. //--------------------------------------------------------- bool DummyAudioDevice::start(int priority) { _realTimePriority = priority; pthread_attr_t* attributes = 0; if (MusEGlobal::realTimeScheduling && _realTimePriority > 0) { attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); pthread_attr_init(attributes); if (pthread_attr_setschedpolicy(attributes, SCHED_FIFO)) { fprintf(stderr, "cannot set FIFO scheduling class for dummy RT thread\n"); } if (pthread_attr_setscope (attributes, PTHREAD_SCOPE_SYSTEM)) { fprintf(stderr, "Cannot set scheduling scope for dummy RT thread\n"); } // p4.0.16 Dummy was not running FIFO because this is needed. if (pthread_attr_setinheritsched(attributes, PTHREAD_EXPLICIT_SCHED)) { fprintf(stderr, "Cannot set setinheritsched for dummy RT thread\n"); } struct sched_param rt_param; memset(&rt_param, 0, sizeof(rt_param)); rt_param.sched_priority = priority; if (pthread_attr_setschedparam (attributes, &rt_param)) { fprintf(stderr, "Cannot set scheduling priority %d for dummy RT thread (%s)\n", priority, strerror(errno)); } } int rv = pthread_create(&dummyThread, attributes, dummyLoop, this); if(rv) { if (MusEGlobal::realTimeScheduling && _realTimePriority > 0) rv = pthread_create(&dummyThread, NULL, dummyLoop, this); } if(rv) fprintf(stderr, "creating dummy audio thread failed: %s\n", strerror(rv)); if (attributes) { pthread_attr_destroy(attributes); free(attributes); } return true; } void DummyAudioDevice::stop () { pthread_cancel(dummyThread); pthread_join(dummyThread, 0); dummyThread = 0; } } // namespace MusECore muse-4.0.0/src/muse/driver/jack.cpp000066400000000000000000002463011404105311000171260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: jack.cpp,v 1.30.2.17 2009/12/20 05:00:35 terminator356 Exp $ // (C) Copyright 2002 Werner Schweer (ws@seh.de) // (C) Copyright 2012-2015 Tim E. Real (terminator356 on sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "strntcpy.h" #include "audio.h" #include "globals.h" #include "song.h" #include "jackaudio.h" #include "track.h" #include "pos.h" #include "tempo.h" #include "sync.h" #include "utils.h" #include "gconfig.h" #include "route.h" #include "midi_consts.h" #include "mididev.h" #include "mpevent.h" #include "jackmidi.h" #include "muse_atomic.h" #include "al/al.h" #define JACK_DEBUG 0 #define JACK_CALLBACK_FIFO_SIZE 512 // For debugging output: Uncomment the fprintf section. #define DEBUG_JACK(dev, format, args...) // fprintf(dev, format, ##args); // For debugging some of the 'persistent routes' code: Uncomment the fprintf section. #define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); #ifdef VST_SUPPORT #include #endif namespace MusEGlobal { #ifndef RTCAP extern void doSetuid(); extern void undoSetuid(); #endif //--------------------------------------------------------- // checkAudioDevice - make sure audioDevice exists //--------------------------------------------------------- bool checkAudioDevice() { if (audioDevice == NULL) { if(debugMsg) fprintf(stderr, "Muse:checkAudioDevice: no audioDevice\n"); return false; } return true; } } // namespace MusEGlobal namespace MusECore { JackAudioDevice* jackAudio; int jack_ver_maj = 0, jack_ver_min = 0, jack_ver_micro = 0, jack_ver_proto = 0; muse_atomic_t atomicGraphChangedPending; bool jack1_port_by_name_workaround = false; // Function pointers obtained with dlsym: jack_get_version_type jack_get_version_fp = NULL; jack_port_set_name_type jack_port_set_name_fp = NULL; jack_port_rename_type jack_port_rename_fp = NULL; // REMOVE Tim. latency. Added. TESTING. // Jack BUG ? : // Before latency compensation was added, we would deactivate and reactivate the Jack server // when loading a song. But tests revealed Jack does not like that, and ALL reported // port latencies are ZERO after reactivation. So we have no choice but to leave the // Jack server running all the time until program close. bool jackStarted = false; // Flag for detecting if sync callback is being called. bool jack_sync_detect_flag = false; JackAudioDevice::JackSyncPhases jackSyncPhase = JackAudioDevice::SyncCheck; // If we are the timebase master. bool jack_timebase_cur_master_state = false; JackAudioDevice::JackTimebaseMasterPhases jackTimebaseMasterPhase = JackAudioDevice::MasterCheck; //--------------------------------------------------------- // JackCallbackFifo //--------------------------------------------------------- class JackCallbackFifo { JackCallbackEvent fifo[JACK_CALLBACK_FIFO_SIZE]; volatile int size; int wIndex; int rIndex; public: JackCallbackFifo() { clear(); } bool put(const JackCallbackEvent& event); // returns true on fifo overflow const JackCallbackEvent& get(); const JackCallbackEvent& peek(int n = 0); void remove(); bool isEmpty() const { return size == 0; } void clear() { size = 0, wIndex = 0, rIndex = 0; } int getSize() const { return size; } }; //--------------------------------------------------------- // JackCallbackFifo // put // return true on fifo overflow //--------------------------------------------------------- bool JackCallbackFifo::put(const JackCallbackEvent& event) { if (size < JACK_CALLBACK_FIFO_SIZE) { fifo[wIndex] = event; wIndex = (wIndex + 1) % JACK_CALLBACK_FIFO_SIZE; ++size; return false; } return true; } //--------------------------------------------------------- // get //--------------------------------------------------------- const JackCallbackEvent& JackCallbackFifo::get() { const JackCallbackEvent& event = fifo[rIndex]; rIndex = (rIndex + 1) % JACK_CALLBACK_FIFO_SIZE; --size; return event; } //--------------------------------------------------------- // peek //--------------------------------------------------------- const JackCallbackEvent& JackCallbackFifo::peek(int n) { int idx = (rIndex + n) % JACK_CALLBACK_FIFO_SIZE; return fifo[idx]; } //--------------------------------------------------------- // remove //--------------------------------------------------------- void JackCallbackFifo::remove() { rIndex = (rIndex + 1) % JACK_CALLBACK_FIFO_SIZE; --size; } JackCallbackFifo jackCallbackFifo; //--------------------------------------------------------- // checkJackClient - make sure client is valid //--------------------------------------------------------- inline bool checkJackClient(jack_client_t* _client) { if (_client == NULL) { fprintf(stderr, "Panic! no _client!\n"); return false; } return true; } //--------------------------------------------------------- // jack_thread_init //--------------------------------------------------------- static void jack_thread_init (void* ) { DEBUG_JACK(stderr, "jack_thread_init()\n"); MusEGlobal::doSetuid(); #ifdef VST_SUPPORT if (loadVST) fst_adopt_thread(); #endif MusEGlobal::undoSetuid(); } int JackAudioDevice::processAudio(jack_nframes_t frames, void* arg) { jackAudio->_frameCounter += frames; MusEGlobal::segmentSize = frames; if (MusEGlobal::audio->isRunning()) { // Are we using Jack transport? if(MusEGlobal::config.useJackTransport) { //-------------------------------------------------- // Jack timebase master acquire/loss detection //-------------------------------------------------- if(arg) { JackAudioDevice* jad = (JackAudioDevice*)arg; jack_client_t* client = jad->jackClient(); if(client) { jack_transport_state_t state = jack_transport_query(client, nullptr); // The timebase callback is called after the process callback. // Check if it got called, meaning we are timebase master. if(state == JackTransportStopped || state == JackTransportRolling) { if(jackSyncPhase == SyncStarted) { // Reset and wait for the next cycle to see if we became master. jackTimebaseMasterPhase = IsNotMaster; } else if(jackTimebaseMasterPhase == IsNotMaster) { // The timebase callback was not called - phase was not set to IsMaster. // Therefore we are not timebase master. if(jack_timebase_cur_master_state) { jack_timebase_cur_master_state = false; MusEGlobal::audio->sendMsgToGui('t'); } } else // Bring in flag here in case sync timed out in stop mode. // Jack may stop calling our sync, even though on the last sync // we still requested more time by returning FALSE. // There would be no other way to detect that condition. if(jackSyncPhase == Synced || !jack_sync_detect_flag) { // Done with phase. Reset for future sync calls. jackSyncPhase = SyncCheck; } } if(state == JackTransportRolling) { // Reset and wait for the next cycle to see if we became master. jackTimebaseMasterPhase = IsNotMaster; } } } // Just call the audio process normally. Jack transport will take care of itself. // Don't process while we're syncing. ToDO: May need to deliver silence in process! //if(jackAudio->getState() != Audio::START_PLAY) MusEGlobal::audio->process((unsigned long)frames); } else { // Not using Jack transport. Use our built-in transport, which INCLUDES // the necessary calls to Audio::sync() and ultimately Audio::process(), // and increments the built-in play position. jackAudio->processTransport((unsigned long)frames); } } else { if (MusEGlobal::debugMsg) puts("jack calling when audio is disconnected!\n"); } // Reset for next cycle. jack_sync_detect_flag = false; return 0; } //--------------------------------------------------------- // processSync // return TRUE (non-zero) when ready to roll. //--------------------------------------------------------- static int processSync(jack_transport_state_t state, jack_position_t* pos, void*) { #if JACK_DEBUG fprintf(stderr, "processSync frame:%u state:%d\n", pos->frame, state); if(pos->valid & JackPositionBBT) { fprintf(stderr, "processSync BBT:\n bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", pos->bar, pos->beat, pos->tick, pos->bar_start_tick, pos->beats_per_bar, pos->beat_type, pos->ticks_per_beat, pos->beats_per_minute); if(pos->valid & JackBBTFrameOffset) fprintf(stderr, "processSync BBTFrameOffset: %u\n", pos->bbt_offset); } #endif if(!MusEGlobal::config.useJackTransport) return 1; int audioState = Audio::STOP; switch (int(state)) { case JackTransportStopped: audioState = Audio::STOP; break; case JackTransportLooping: case JackTransportRolling: audioState = Audio::PLAY; break; case JackTransportStarting: //fprintf(stderr, "processSync JackTransportStarting\n"); audioState = Audio::START_PLAY; break; //case JackTransportNetStarting: -- only available in Jack-2! // FIXME: Quick and dirty hack to support both Jack-1 and Jack-2 // Really need a config check of version... case 4: //fprintf(stderr, "processSync JackTransportNetStarting\n"); audioState = Audio::START_PLAY; break; } unsigned frame = pos->frame; //return MusEGlobal::audio->sync(audioState, frame); int rv = MusEGlobal::audio->sync(audioState, frame); //fprintf(stderr, "Jack processSync() after MusEGlobal::audio->sync frame:%d\n", frame); jack_sync_detect_flag = true; if(rv) { jackSyncPhase = JackAudioDevice::Synced; } else { if(jackSyncPhase == JackAudioDevice::SyncStarted) jackSyncPhase = JackAudioDevice::Syncing; else jackSyncPhase = JackAudioDevice::SyncStarted; } return rv; } //--------------------------------------------------------- // timebase_callback //--------------------------------------------------------- #if JACK_DEBUG static void timebase_callback(jack_transport_state_t state, jack_nframes_t nframes, jack_position_t* pos, int new_pos, void*) #else static void timebase_callback(jack_transport_state_t, jack_nframes_t, jack_position_t* pos, int, void*) #endif { // I think, therefore I am... timebase master. jackTimebaseMasterPhase = JackAudioDevice::IsMaster; if(!jack_timebase_cur_master_state) { jack_timebase_cur_master_state = true; MusEGlobal::audio->sendMsgToGui('T'); } #if JACK_DEBUG fprintf(stderr, "timebase_callback() state:%d\n", state); if(pos->valid & JackPositionBBT) fprintf(stderr, "timebase_callback BBT:\n bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", pos->bar, pos->beat, pos->tick, pos->bar_start_tick, pos->beats_per_bar, pos->beat_type, pos->ticks_per_beat, pos->beats_per_minute); if(pos->valid & JackBBTFrameOffset) fprintf(stderr, "timebase_callback BBTFrameOffset: %u\n", pos->bbt_offset); if(pos->valid & JackPositionTimecode) fprintf(stderr, "timebase_callback JackPositionTimecode: frame_time:%f next_time:%f\n", pos->frame_time, pos->next_time); if(pos->valid & JackAudioVideoRatio) fprintf(stderr, "timebase_callback JackAudioVideoRatio: %f\n", pos->audio_frames_per_video_frame); if(pos->valid & JackVideoFrameOffset) fprintf(stderr, "timebase_callback JackVideoFrameOffset: %u\n", pos->video_offset); #endif //Pos p(pos->frame, false); Pos p(MusEGlobal::extSyncFlag ? MusEGlobal::audio->tickPos() : pos->frame, MusEGlobal::extSyncFlag ? true : false); // Can't use song pos - it is only updated every (slow) GUI heartbeat ! //Pos p(MusEGlobal::extSyncFlag ? MusEGlobal::song->cpos() : pos->frame, MusEGlobal::extSyncFlag ? true : false); pos->valid = JackPositionBBT; int bar, beat, tick; p.mbt(&bar, &beat, &tick); pos->bar = bar; pos->beat = beat; pos->tick = tick; pos->bar_start_tick = Pos(pos->bar, 0, 0).tick(); pos->bar++; pos->beat++; int z, n; MusEGlobal::sigmap.timesig(p.tick(), z, n); pos->beats_per_bar = z; pos->beat_type = n; pos->ticks_per_beat = MusEGlobal::config.division; //pos->ticks_per_beat = 24; double tempo = MusEGlobal::tempomap.tempo(p.tick()); pos->beats_per_minute = ((double)MusEGlobal::tempomap.globalTempo() * 600000.0) / tempo; #if JACK_DEBUG fprintf(stderr, "timebase_callback is new_pos:%d nframes:%u frame:%u tickPos:%d cpos:%d\n", new_pos, nframes, pos->frame, MusEGlobal::audio->tickPos(), MusEGlobal::song->cpos()); fprintf(stderr, " new: bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", pos->bar, pos->beat, pos->tick, pos->bar_start_tick, pos->beats_per_bar, pos->beat_type, pos->ticks_per_beat, pos->beats_per_minute); #endif } //--------------------------------------------------------- // processShutdown //--------------------------------------------------------- static void processShutdown(void*) { DEBUG_JACK(stderr, "processShutdown()\n"); //fprintf(stderr, "processShutdown\n"); jackAudio->nullify_client(); MusEGlobal::audio->shutdown(); int c=0; while(MusEGlobal::midiSeqRunning == true) { if(c++ >10) { fprintf(stderr, "sequencer still running, something is very wrong.\n"); break; } sleep(1); } delete jackAudio; jackAudio=0; MusEGlobal::audioDevice=0; } //--------------------------------------------------------- // jackError //--------------------------------------------------------- static void jackError(const char *s) { fprintf(stderr,"JACK ERROR: %s\n", s); } //--------------------------------------------------------- // noJackError //--------------------------------------------------------- static void noJackError(const char* /* s */) { //fprintf(stderr, "noJackError()\n"); } //--------------------------------------------------------- // jackInfo //--------------------------------------------------------- static void jackInfo(const char* s) { fprintf(stderr, "JACK INFO: %s\n", s); } //--------------------------------------------------------- // noJackInfo //--------------------------------------------------------- static void noJackInfo(const char* /*s*/) { //fprintf(stderr, "noJackInfo()\n"); } //--------------------------------------------------------- // JackAudioDevice //--------------------------------------------------------- JackAudioDevice::JackAudioDevice(jack_client_t* cl, char* name) : AudioDevice() { _frameCounter = 0; strcpy(jackRegisteredName, name); _client = cl; } //--------------------------------------------------------- // ~JackAudioDevice //--------------------------------------------------------- JackAudioDevice::~JackAudioDevice() { DEBUG_JACK(stderr, "~JackAudioDevice()\n"); if (_client) { // REMOVE Tim. latency. Added. TESTING. if (jack_deactivate(_client)) { fprintf (stderr, "cannot deactivate client\n"); } if (jack_client_close(_client)) { fprintf(stderr,"jack_client_close() failed: %s\n", strerror(errno)); } } // REMOVE Tim. latency. Added. TESTING. jackStarted = false; DEBUG_JACK(stderr, "~JackAudioDevice() after jack_client_close()\n"); } //--------------------------------------------------------- // realtimePriority // return zero if not running realtime // can only be called if JACK client thread is already // running //--------------------------------------------------------- int JackAudioDevice::realtimePriority() const { if(!_client) return 0; pthread_t t = (pthread_t) jack_client_thread_id(_client); if(t == 0) return jack_client_real_time_priority(_client); int policy; struct sched_param param; memset(¶m, 0, sizeof(param)); int rv = pthread_getschedparam(t, &policy, ¶m); if (rv) { perror("MusE: JackAudioDevice::realtimePriority: Error: Get jack schedule parameter"); return 0; } if (policy != SCHED_FIFO) { fprintf(stderr, "MusE: JackAudioDevice::realtimePriority: JACK is not running realtime\n"); return 0; } return param.sched_priority; } //--------------------------------------------------------- // initJackAudio // return true if JACK not found //--------------------------------------------------------- bool initJackAudio() { DEBUG_JACK(stderr, "initJackAudio()\n"); muse_atomic_init(&atomicGraphChangedPending); muse_atomic_set(&atomicGraphChangedPending, 0); jack_get_version_fp = reinterpret_cast(dlsym(RTLD_DEFAULT, "jack_get_version")); DEBUG_PRST_ROUTES(stderr, "initJackAudio jack_get_version() address:%p \n", jack_get_version_fp); if(jack_get_version_fp) // ATM Only in Jack-2. Dlsym'd. Check for existence first. { jack_get_version_fp(&jack_ver_maj, &jack_ver_min, &jack_ver_micro, &jack_ver_proto); DEBUG_PRST_ROUTES(stderr, "initJackAudio: jack_ver_maj:%d jack_ver_min:%d jack_ver_micro:%d jack_ver_proto:%d\n", jack_ver_maj, jack_ver_min, jack_ver_micro, jack_ver_proto); // FIXME: ATM Jack-2 jack_get_version() returns all zeros. When it is fixed, do something with the values. if(jack_ver_maj == 0 && jack_ver_min == 0 && jack_ver_micro == 0 && jack_ver_proto == 0) { fprintf(stderr, "MusE:initJackAudio: jack_get_version() returned zeros. Setting version major to 1.\n"); jack_ver_maj = 1; } } jack_port_set_name_fp = reinterpret_cast(dlsym(RTLD_DEFAULT, "jack_port_set_name")); DEBUG_PRST_ROUTES(stderr, "initJackAudio jack_port_set_name() address:%p \n", jack_port_set_name_fp); if(jack_port_set_name_fp) { } jack_port_rename_fp = reinterpret_cast(dlsym(RTLD_DEFAULT, "jack_port_rename")); DEBUG_PRST_ROUTES(stderr, "initJackAudio jack_port_rename() address:%p \n", jack_port_rename_fp); if(jack_port_rename_fp) { } if (MusEGlobal::debugMsg) { fprintf(stderr, "initJackAudio(): registering error and info callbacks...\n"); jack_set_error_function(jackError); jack_set_info_function(jackInfo); } else { jack_set_error_function(noJackError); jack_set_info_function(noJackInfo); } MusEGlobal::doSetuid(); int opts = JackNullOption; if(MusEGlobal::noAutoStartJack) opts |= JackNoStartServer; jack_status_t status; jack_client_t* client = jack_client_open("MusE", (jack_options_t)opts, &status); if (!client) { if (status & JackServerStarted) fprintf(stderr, "jack server started...\n"); if (status & JackServerFailed) fprintf(stderr, "cannot connect to jack server\n"); if (status & JackServerError) fprintf(stderr, "communication with jack server failed\n"); if (status & JackShmFailure) fprintf(stderr, "jack cannot access shared memory\n"); if (status & JackVersionError) fprintf(stderr, "jack server has wrong version\n"); fprintf(stderr, "cannot create jack client\n"); MusEGlobal::undoSetuid(); return true; } if (MusEGlobal::debugMsg) fprintf(stderr, "initJackAudio(): client %s opened.\n", jack_get_client_name(client)); // Check if Jack-1 jack_port_by_name() workaround is required: if(jack_ver_maj == 0) { sleep(1); jack_port_t* p = jack_port_register(client, "jack1_test_port", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); if(p) { sleep(1); int sz = jack_port_name_size(); char s[sz]; strcpy(s, jack_get_client_name(client)); strcat(s, ":jack1_test_port"); jack_port_t* sp = jack_port_by_name(client, s); if(sp) { if(p != sp) { fprintf(stderr, "initJackAudio(): Enabling Jack-1 jack_port_by_name() workaround\n"); jack1_port_by_name_workaround = true; } } else fprintf(stderr, "initJackAudio(): Jack-1 jack_port_by_name() workaround: Error on jack_port_by_name(): port not found\n"); if(jack_port_unregister(client, p)) fprintf(stderr, "initJackAudio(): Jack-1 jack_port_by_name() workaround: Error on jack_port_unregister()\n"); else sleep(1); } else fprintf(stderr, "initJackAudio(): Jack-1 jack_port_by_name() workaround: Error on jack_port_register()\n"); } jackAudio = new JackAudioDevice(client, jack_get_client_name(client)); if (MusEGlobal::debugMsg) fprintf(stderr, "initJackAudio(): registering client...\n"); MusEGlobal::undoSetuid(); MusEGlobal::audioDevice = jackAudio; // WARNING Don't do this here. Do it after any MusE ALSA client is registered, otherwise random crashes can occur. //jackAudio->registerClient(); MusEGlobal::sampleRate = jack_get_sample_rate(client); MusEGlobal::projectSampleRate = MusEGlobal::sampleRate; // Make sure the AL namespace variables mirror our variables. AL::sampleRate = MusEGlobal::sampleRate; MusEGlobal::segmentSize = jack_get_buffer_size(client); return false; } static int bufsize_callback(jack_nframes_t n, void*) { fprintf(stderr, "JACK: buffersize changed %d\n", n); return 0; } //--------------------------------------------------------- // freewheel_callback //--------------------------------------------------------- static void freewheel_callback(int starting, void*) { if (MusEGlobal::debugMsg || JACK_DEBUG) fprintf(stderr, "JACK: freewheel_callback: starting%d\n", starting); MusEGlobal::audio->setFreewheel(starting); } static int srate_callback(jack_nframes_t n, void*) { if (MusEGlobal::debugMsg || JACK_DEBUG) fprintf(stderr, "JACK: sample rate changed: %d\n", n); return 0; } //--------------------------------------------------------- // registration_callback //--------------------------------------------------------- static void registration_callback(jack_port_id_t port_id, int is_register, void*) { if(MusEGlobal::debugMsg || JACK_DEBUG) fprintf(stderr, "JACK: registration_callback\n"); DEBUG_PRST_ROUTES(stderr, "JACK: registration_callback: port_id:%d is_register:%d\n", port_id, is_register); // With Jack-1 do not use functions like jack_port_by_name and jack_port_by_id here. // With registration the port has not been added yet, so they allocate a new // 'external' port which is NOT the same as the port returned by jack_port_register ! // Thereafter each call to those functions returns THAT allocated port NOT the jack_port_register one. // [ This was a bug in Jack1 due to a missing section. A fix by Tim was submitted late 2014 and was pending. ] JackCallbackEvent ev; ev.type = is_register ? PortRegister : PortUnregister; ev.port_id_A = port_id; jackCallbackFifo.put(ev); // NOTE: Jack-1 does not issue a graph order callback after a registration call. // Jack-1 callbacks: [ port connect -> graph order -> registration ] {...} // Jack-2 callbacks: [ port connect {...} -> registration {...} -> graph order ] {...} if(jack_ver_maj != 1) { // Add a GraphChanged event. JackCallbackEvent ev; ev.type = GraphChanged; jackCallbackFifo.put(ev); // we cannot call JackAudioDevice::graphChanged() from this // context, so we send a message to the gui thread which in turn // calls graphChanged() if(muse_atomic_read(&atomicGraphChangedPending) == 0) { muse_atomic_set(&atomicGraphChangedPending, 1); MusEGlobal::audio->sendMsgToGui('C'); } } } //--------------------------------------------------------- // client_registration_callback //--------------------------------------------------------- static void client_registration_callback(const char *name, int isRegister, void*) { if (MusEGlobal::debugMsg || JACK_DEBUG) fprintf(stderr, "JACK: client registration changed:%s register:%d\n", name, isRegister); DEBUG_PRST_ROUTES(stderr, "JACK: client registration changed:%s register:%d\n", name, isRegister); } //--------------------------------------------------------- // port_connect_callback //--------------------------------------------------------- static void port_connect_callback(jack_port_id_t a, jack_port_id_t b, int isConnect, void* arg) { if (MusEGlobal::debugMsg || JACK_DEBUG) fprintf(stderr, "JACK: port connections changed: A:%d B:%d isConnect:%d\n", a, b, isConnect); DEBUG_PRST_ROUTES(stderr, "JACK: port_connect_callback id a:%d id b:%d isConnect:%d\n", a, b, isConnect); JackCallbackEvent ev; ev.type = isConnect ? PortConnect : PortDisconnect; ev.port_id_A = a; ev.port_id_B = b; JackAudioDevice* jad = (JackAudioDevice*)arg; jack_client_t* client = jad->jackClient(); if(client) { ev.port_A = jack_port_by_id(client, a); ev.port_B = jack_port_by_id(client, b); } else { ev.port_A = NULL; ev.port_B = NULL; } jackCallbackFifo.put(ev); } //--------------------------------------------------------- // graph_callback // this is called from jack when the connections // changed //--------------------------------------------------------- static int graph_callback(void*) { if (MusEGlobal::debugMsg || JACK_DEBUG) fprintf(stderr, "graph_callback()\n"); DEBUG_PRST_ROUTES(stderr, "JACK: graph_callback\n"); // Add a GraphChanged event. JackCallbackEvent ev; ev.type = GraphChanged; jackCallbackFifo.put(ev); // we cannot call JackAudioDevice::graphChanged() from this // context, so we send a message to the gui thread which in turn // calls graphChanged() if(muse_atomic_read(&atomicGraphChangedPending) == 0) { muse_atomic_set(&atomicGraphChangedPending, 1); MusEGlobal::audio->sendMsgToGui('C'); } return 0; } // static void latency_callback(jack_latency_callback_mode_t /*mode*/, void* /*arg*/) // { // //JackAudioDevice* jad = (JackAudioDevice*)arg; // // // TODO: Do all of our Input Track and Output Track ports. // // // jack_latency_range_t range; // // if (mode == JackCaptureLatency) { // // jack_port_get_latency_range (input_port, mode, &range); // // range.min += latency; // // range.max += latency; // // jack_port_set_latency_range (output_port, mode, &range); // // } else { // // jack_port_get_latency_range (output_port, mode, &range); // // range.min += latency; // // range.max += latency; // // jack_port_set_latency_range (input_port, mode, &range); // // } // } void JackAudioDevice::processJackCallbackEvents(const Route& our_node, jack_port_t* our_port, RouteList* route_list, bool is_input) { jack_client_t* client = jackClient(); if(!client) return; jack_port_t* our_ext_port = our_port; const char* our_port_name = our_port ? jack_port_name(our_port) : 0; if(our_port && our_port_name && jack1_port_by_name_workaround) { jack_port_t* jp = jack_port_by_name(client, our_port_name); if(jp && jp != our_port) { DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::processJackCallbackEvents: changing audio input port!: channel:%d our_port:%p new port:%p\n", our_node.channel, our_port, jp); our_ext_port = jp; } } for(iRoute ir = route_list->begin(); ir != route_list->end(); ++ir) { // Check correct route type, and channel if required. if((ir->type != Route::JACK_ROUTE) || (our_node.channel != -1 && ir->channel != our_node.channel)) continue; const char* route_jpname = ir->persistentJackPortName; // FIXME: TODO: Maybe switch to get_ports jack_port_t* jp = jack_port_by_name(client, route_jpname); if(jp) { // TODO: For Jack-2 maybe alter this? Before calling jack_port_connected_to(), maybe first check if the IDs // (hence jack ports) passed in the connect callback match here, to avoid calling jack_port_connected_to() ? if(our_port && jack_port_connected_to(our_port, route_jpname)) { // The ports are connected. Keep the route node but update its jack port pointer if necessary. const char* s = NULL; if(jp != ir->jackPort) { DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports connected. Modifying route: our_port:%p old_route_jp:%p new_route_jp:%p route_persistent_name:%s\n", our_port, ir->jackPort, jp, route_jpname); s = route_jpname; } // Find a more appropriate name if necessary. char fin_name[ROUTE_PERSISTENT_NAME_SIZE]; portName(jp, fin_name, ROUTE_PERSISTENT_NAME_SIZE); if(strcmp(ir->persistentJackPortName, fin_name) != 0) { DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports connected. Modifying route name: route_persistent_name:%s new name:%s\n", route_jpname, fin_name); s = fin_name; } if(s) operations.add(PendingOperationItem(Route(Route::JACK_ROUTE, 0, jp, ir->channel, 0, 0, s), &(*ir), PendingOperationItem::ModifyRouteNode)); } else { if(ir->jackPort) { // Check whether the disconnect happened BEFORE this graphChanged() was called, // or just now during it, or was followed by an unregister. // Support our port == null (midi device not assigned to a midi port or I/O disabled etc.): // If our port is null, treat this as an unregister... const int ret = our_ext_port ? checkDisconnectCallback(our_ext_port, jp) : 1; if(ret == 2) { DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports not connected, ret=DeleteRouteNode. Deleting route: our_port:%p route_jp:%p found_jp:%p route_persistent_name:%s\n", our_port, ir->jackPort, jp, route_jpname); // The port exists but is not connected to our port. Remove the route node. operations.add(PendingOperationItem(route_list, ir, PendingOperationItem::DeleteRouteNode)); } else if(ret == 1) { DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports not connected, ret=ModifyRouteNode. Modifying route: our_port:%p route_jp:%p found_jp:%p route_persistent_name:%s\n", our_port, ir->jackPort, jp, route_jpname); operations.add(PendingOperationItem(Route(Route::JACK_ROUTE, 0, NULL, ir->channel, 0, 0, ir->persistentJackPortName), &(*ir), PendingOperationItem::ModifyRouteNode)); } } else if(MusEGlobal::audio && MusEGlobal::audio->isRunning()) // Don't try to connect if not running. { // Check whether a port registration happened BEFORE this graphChanged() was called, // or just now during it, or was followed by an unregister. int ret = checkPortRegisterCallback(jp); if(ret == 1) { // No failed attempts at connection to the Jack port now or in the previous call to graphChanged(). if(our_port) { // The port exists but is not connected to our port. Reconnect the route. // NOTE: Jack2: graph changed callback will be called again regardless if jack_connect succeeds or fails... DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports not connected. Reconnecting route: our_port:%p route_jp:%p found_jp:%p our_port_name:%s route_persistent_name:%s\n", our_port, ir->jackPort, jp, our_port_name, route_jpname); if(our_port_name) { int err; if(is_input) err = jack_connect(client, route_jpname, our_port_name); else err = jack_connect(client, our_port_name, route_jpname); if(err) { DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports not connected. Reconnecting route: ERROR:%d our_port:%p route_jp:%p found_jp:%p our_port_name:%s route_persistent_name:%s\n", err, our_port, ir->jackPort, jp, our_port_name, route_jpname); } else { // We have our jack port, on a supposedly active client. Update the route node's jack port pointer. // Find a more appropriate name if necessary. const char* s = ir->persistentJackPortName; char fin_name[ROUTE_PERSISTENT_NAME_SIZE]; portName(jp, fin_name, ROUTE_PERSISTENT_NAME_SIZE); if(strcmp(ir->persistentJackPortName, fin_name) != 0) { DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Ports connected. Modifying route name: route_persistent_name:%s new name:%s\n", route_jpname, fin_name); s = fin_name; } operations.add(PendingOperationItem(Route(Route::JACK_ROUTE, 0, jp, ir->channel, 0, 0, s), &(*ir), PendingOperationItem::ModifyRouteNode)); } } } } // State unchanged. } } } else { // Port does not exist. Keep the route node but clear its jack port pointer if necessary. if(ir->jackPort) { DEBUG_PRST_ROUTES(stderr, "processJackCallbackEvents: Port non-existent. Modifying route: our_port:%p route_jp:%p route_persistent_name:%s\n", our_port, ir->jackPort, route_jpname); operations.add(PendingOperationItem(Route(Route::JACK_ROUTE, 0, NULL, ir->channel, 0, 0, ir->persistentJackPortName), &(*ir), PendingOperationItem::ModifyRouteNode)); } } } if(our_port) checkNewRouteConnections(our_port, our_node.channel, route_list); } //--------------------------------------------------------- // JackAudioDevice::graphChanged // this is called from song in gui context triggered // by graph_callback() //--------------------------------------------------------- void JackAudioDevice::graphChanged() { DEBUG_JACK(stderr, "JackAudioDevice::graphChanged()\n"); if(!checkJackClient(_client)) { jackCallbackFifo.clear(); // Want this? // Reset this now. muse_atomic_set(&atomicGraphChangedPending, 0); return; } // For Jack-1 only: See if we need to wait, for example for a port unregister event. // Jack "2" does not require waiting, Jack "1" does, assume any other version requires the wait (no harm). if(MusEGlobal::audio && jack_ver_maj != 1) { // TODO: It may be desirable to always wait so that bunches of commands can be processed easier. //bool do_audio_wait = false; // This is safe because the writer only increases the size. int cb_fifo_sz = jackCallbackFifo.getSize(); for(int i = 0; i < cb_fifo_sz; ++i) { const JackCallbackEvent& jcb = jackCallbackFifo.peek(i); if(jcb.type == PortDisconnect && (jack_port_is_mine(_client, jcb.port_A) || jack_port_is_mine(_client, jcb.port_B))) { DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::graphChanged: *** calling msgAudioWait()\n"); MusEGlobal::audio->msgAudioWait(); // Wait until upcoming process call has finished... break; } } } // Reset this now. muse_atomic_set(&atomicGraphChangedPending, 0); jackCallbackEvents.clear(); // Find the last GraphChanged event, if any. // This is safe because the writer only increases the size. int cb_fifo_sz = jackCallbackFifo.getSize(); if(cb_fifo_sz) { int last_gc_idx = cb_fifo_sz - 1; if(jack_ver_maj == 1) for(int i = 0; i < cb_fifo_sz; ++i) if(jackCallbackFifo.peek(i).type == GraphChanged) last_gc_idx = i; // Move the events into a list for processing, including the final GraphChanged event. // Leave any 'still in progress' ending events (without closing GraphChanged event) in the ring buffer. //jackCallbackEvents.clear(); for(int i = 0; i <= last_gc_idx; ++i) jackCallbackEvents.push_back(jackCallbackFifo.get()); } processGraphChanges(); if(!operations.empty()) { MusEGlobal::audio->msgExecutePendingOperations(operations, true); operations.clear(); } } void JackAudioDevice::processGraphChanges() { DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::processGraphChanges()\n"); //--------------------------------------- // Audio inputs: //--------------------------------------- InputList* il = MusEGlobal::song->inputs(); for(iAudioInput ii = il->begin(); ii != il->end(); ++ii) { AudioInput* it = *ii; int channels = it->channels(); for(int channel = 0; channel < channels; ++channel) { jack_port_t* port = (jack_port_t*)(it->jackPort(channel)); // Support even if port == null. processJackCallbackEvents(Route(it, channel), port, it->inRoutes(), true); } } //--------------------------------------- // Audio outputs: //--------------------------------------- OutputList* ol = MusEGlobal::song->outputs(); for(iAudioOutput ii = ol->begin(); ii != ol->end(); ++ii) { AudioOutput* it = *ii; int channels = it->channels(); for(int channel = 0; channel < channels; ++channel) { jack_port_t* port = (jack_port_t*)(it->jackPort(channel)); // Support even if port == null. processJackCallbackEvents(Route(it, channel), port, it->outRoutes(), false); } } //--------------------------------------- // Midi devices: //--------------------------------------- for(iMidiDevice ii = MusEGlobal::midiDevices.begin(); ii != MusEGlobal::midiDevices.end(); ++ii) { MidiDevice* md = *ii; if(md->deviceType() != MidiDevice::JACK_MIDI) continue; //--------------------------------------- // Midi outputs: //--------------------------------------- if(md->rwFlags() & 1) // Writable { jack_port_t* port = (jack_port_t*)md->outClientPort(); // Support even if port == null. processJackCallbackEvents(Route(md, -1), port, md->outRoutes(), false); } //------------------------ // Midi inputs: //------------------------ if(md->rwFlags() & 2) // Readable { jack_port_t* port = (jack_port_t*)md->inClientPort(); // Support even if port == null. processJackCallbackEvents(Route(md, -1), port, md->inRoutes(), true); } } } void JackAudioDevice::checkNewRouteConnections(jack_port_t* our_port, int channel, RouteList* route_list) { DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::checkNewRouteConnections(): client:%p our_port:%p channel:%d route_list:%p\n", _client, our_port, channel, route_list); // Check for new connections... const char** ports = jack_port_get_all_connections(_client, our_port); if(ports) { const char** pn = ports; while(*pn) { // Should be safe and quick search here, we know that the port name is valid. jack_port_t* jp = jack_port_by_name(_client, *pn); if(jp) { bool found = false; for(ciRoute ir = route_list->begin(); ir != route_list->end(); ++ir) { if(ir->type != Route::JACK_ROUTE || (channel != -1 && ir->channel != channel)) continue; // See if any changes are pending for the route node and take them into account. jack_port_t* op_jp = (jack_port_t*)ir->jackPort; const char* op_ppname = ir->persistentJackPortName; iPendingOperation ipo = operations.end(); while(ipo != operations.begin()) { --ipo; switch(ipo->_type) { case PendingOperationItem::DeleteRouteNode: if(ipo->_route_list == route_list && &(*ipo->_iRoute) == &(*ir)) { found = true; ipo = operations.begin(); // Breakout } break; case PendingOperationItem::ModifyRouteNode: if(ipo->_dst_route_pointer == &(*ir)) { op_jp = (jack_port_t*)ipo->_src_route.jackPort; op_ppname = ipo->_src_route.persistentJackPortName; ipo = operations.begin(); // Breakout } break; default: break; } } if(found) { found = false; continue; // Ignore the route node - it has been scheduled for deletion. } // TODO: Maybe switch to get_ports if(op_jp == jp || jack_port_by_name(_client, op_ppname) == jp) { found = true; break; } } if(!found) { Route r(Route::JACK_ROUTE, 0, jp, channel, 0, 0, NULL); // Find a better name. portName(jp, r.persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); DEBUG_PRST_ROUTES(stderr, " adding route: route_jp:%p portname:%s route_persistent_name:%s\n", jp, *pn, r.persistentJackPortName); operations.add(PendingOperationItem(route_list, r, PendingOperationItem::AddRouteNode)); } } ++pn; } jack_free(ports); } } int JackAudioDevice::checkDisconnectCallback(const jack_port_t* our_port, const jack_port_t* port) { DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::checkDisconnectCallback(): our_port:%p port:%p\n", our_port, port); iJackCallbackEvent ijce = jackCallbackEvents.end(); while(ijce != jackCallbackEvents.begin()) { --ijce; if(ijce->type == PortConnect && ((ijce->port_A == our_port && ijce->port_B == port) || (ijce->port_B == our_port && ijce->port_A == port))) return 0; if(ijce->type == PortDisconnect) { jack_port_id_t id; if(ijce->port_A == our_port && ijce->port_B == port) id = ijce->port_id_B; else if(ijce->port_B == our_port && ijce->port_A == port) id = ijce->port_id_A; else continue; for( ++ijce ; ijce != jackCallbackEvents.end(); ++ijce) if(ijce->type == PortUnregister && ijce->port_id_A == id) return 1; return 2; } } return 0; } int JackAudioDevice::checkPortRegisterCallback(const jack_port_t* port) { DEBUG_PRST_ROUTES(stderr, "JackAudioDevice::checkPortRegisterCallback(): port:%p\n", port); iJackCallbackEvent ijce = jackCallbackEvents.end(); while(ijce != jackCallbackEvents.begin()) { --ijce; if(ijce->type == PortRegister) { jack_port_id_t id = ijce->port_id_A; if(jack_port_by_id(_client, id) == port) { for( ++ijce ; ijce != jackCallbackEvents.end(); ++ijce) if(ijce->type == PortUnregister && ijce->port_id_A == id) return 0; return 1; } } } return 0; } int JackAudioDevice::static_JackXRunCallback(void *) { MusEGlobal::audio->incXruns(); return 0; } //static int xrun_callback(void*) // { // fprintf(stderr, "JACK: xrun\n"); // return 0; // } //--------------------------------------------------------- // register //--------------------------------------------------------- void JackAudioDevice::registerClient() { DEBUG_JACK(stderr, "registerClient()\n"); // if (MusEGlobal::debugMsg) { // fprintf(stderr, "JackAudioDevice::registerClient(): registering error and info callbacks...\n"); // jack_set_error_function(jackError); // jack_set_info_function(jackInfo); // } // else { // fprintf(stderr, "JackAudioDevice::registerClient(): registering no error and no info callbacks...\n"); // jack_set_error_function(noJackError); // jack_set_info_function(noJackInfo); // } if(!checkJackClient(_client)) return; jack_set_thread_init_callback(_client, (JackThreadInitCallback) jack_thread_init, 0); //jack_set_timebase_callback(client, 0, (JackTimebaseCallback) timebase_callback, 0); jack_set_process_callback(_client, processAudio, this); // NOTE: We set the driver's sync timeout in the gui thread. See Song::seqSignal(). jack_set_sync_callback(_client, processSync, 0); jack_on_shutdown(_client, processShutdown, 0); jack_set_buffer_size_callback(_client, bufsize_callback, 0); jack_set_sample_rate_callback(_client, srate_callback, 0); jack_set_port_registration_callback(_client, registration_callback, this); jack_set_client_registration_callback(_client, client_registration_callback, 0); jack_set_port_connect_callback(_client, port_connect_callback, this); // Can't use this! Incompatible in jack 1/2 and unimplemented in jack1. //jack_set_port_rename_callback(_client, port_rename_callback, 0); jack_set_graph_order_callback(_client, graph_callback, this); // jack_set_xrun_callback(client, xrun_callback, 0); jack_set_freewheel_callback (_client, freewheel_callback, 0); // Tell the JACK server to call `latency()' whenever the latency needs to be recalculated. // if(jack_set_latency_callback) // jack_set_latency_callback(_client, latency_callback, this); jack_set_xrun_callback(_client, static_JackXRunCallback, this); } //--------------------------------------------------------- // registerInPort //--------------------------------------------------------- void* JackAudioDevice::registerInPort(const char* name, bool midi) { DEBUG_JACK(stderr, "registerInPort()\n"); if(!checkJackClient(_client) || !name || name[0] == '\0') return NULL; const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; void* p = jack_port_register(_client, name, type, JackPortIsInput, 0); DEBUG_PRST_ROUTES(stderr, "JACK: registerInPort: <%s> %p\n", name, p); return p; } //--------------------------------------------------------- // registerOutPort //--------------------------------------------------------- void* JackAudioDevice::registerOutPort(const char* name, bool midi) { DEBUG_JACK(stderr, "registerOutPort()\n"); if(!checkJackClient(_client) || !name || name[0] == '\0') return NULL; const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; void* p = jack_port_register(_client, name, type, JackPortIsOutput, 0); DEBUG_PRST_ROUTES(stderr, "JACK: registerOutPort: <%s> %p\n", name, p); return p; } //--------------------------------------------------------- // connect //--------------------------------------------------------- bool JackAudioDevice::connect(void* src, void* dst) { DEBUG_JACK(stderr, "JackAudioDevice::connect()\n"); if(!checkJackClient(_client)) return false; const char* sn = jack_port_name((jack_port_t*) src); const char* dn = jack_port_name((jack_port_t*) dst); if (sn == 0 || dn == 0) { fprintf(stderr, "JackAudio::connect: unknown jack ports\n"); return false; } int err = jack_connect(_client, sn, dn); //if (jack_connect(_client, sn, dn)) { if (err) { fprintf(stderr, "jack connect <%s>%p - <%s>%p failed with err:%d\n", sn, src, dn, dst, err); return false; } else { DEBUG_JACK(stderr, "jack connect <%s>%p - <%s>%p succeeded\n", sn, src, dn, dst); } return true; } bool JackAudioDevice::connect(const char* src, const char* dst) { DEBUG_JACK(stderr, "JackAudioDevice::connect()\n"); if(!checkJackClient(_client) || !src || !dst || src[0] == '\0' || dst[0] == '\0') return false; int err = jack_connect(_client, src, dst); if(err) { fprintf(stderr, "jack connect <%s> - <%s> failed with err:%d\n", src, dst, err); return false; } return true; } //--------------------------------------------------------- // disconnect //--------------------------------------------------------- bool JackAudioDevice::disconnect(void* src, void* dst) { DEBUG_JACK(stderr, "JackAudioDevice::disconnect()\n"); if(!checkJackClient(_client)) return false; if(!src || !dst) return false; const char* sn = jack_port_name((jack_port_t*) src); const char* dn = jack_port_name((jack_port_t*) dst); if (sn == nullptr || dn == nullptr) { fprintf(stderr, "JackAudio::disconnect: unknown jack ports\n"); return false; } int err = jack_disconnect(_client, sn, dn); //if (jack_disconnect(_client, sn, dn)) { if (err) { fprintf(stderr, "jack disconnect <%s> - <%s> failed with err:%d\n", sn, dn, err); return false; } else { DEBUG_JACK(stderr, "jack disconnect <%s> - <%s> succeeded\n", sn, dn); } return true; } bool JackAudioDevice::disconnect(const char* src, const char* dst) { DEBUG_JACK(stderr, "JackAudioDevice::disconnect()\n"); if(!checkJackClient(_client) || !src || !dst || src[0] == '\0' || dst[0] == '\0') return false; int err = jack_disconnect(_client, src, dst); if(err) { fprintf(stderr, "jack disconnect <%s> - <%s> failed with err:%d\n", src, dst, err); return false; } return true; } //--------------------------------------------------------- // portsCanDisconnect //--------------------------------------------------------- bool JackAudioDevice::portsCanDisconnect(void* src, void* dst) const { if(!_client) return false; if(!src || !dst) return false; const char** ports = jack_port_get_all_connections(_client, (jack_port_t*)src); if(!ports) return false; bool rv = false; for(const char** p = ports; p && *p; ++p) { jack_port_t* jp = jack_port_by_name(_client, *p); if(jp == dst) { rv = true; break; } } jack_free(ports); return rv; } bool JackAudioDevice::portsCanDisconnect(const char* src, const char* dst) const { if(!_client) return false; return portsCanDisconnect(jack_port_by_name(_client, src), jack_port_by_name(_client, dst)); } //--------------------------------------------------------- // portsCanConnect //--------------------------------------------------------- bool JackAudioDevice::portsCanConnect(void* src, void* dst) const { if(!_client) return false; if(!src || !dst) return false; const char* src_type = jack_port_type((jack_port_t*)src); const char* dst_type = jack_port_type((jack_port_t*)dst); if(!src_type || !dst_type || (strcmp(src_type, dst_type) != 0)) return false; if(!(jack_port_flags((jack_port_t*)src) & JackPortIsOutput) || !(jack_port_flags((jack_port_t*)dst) & JackPortIsInput)) return false; const char** ports = jack_port_get_all_connections(_client, (jack_port_t*)src); if(!ports) return true; bool rv = true; for(const char** p = ports; p && *p; ++p) { jack_port_t* jp = jack_port_by_name(_client, *p); if(jp == dst) { rv = false; break; } } jack_free(ports); return rv; } bool JackAudioDevice::portsCanConnect(const char* src, const char* dst) const { if(!_client) return false; return portsCanConnect(jack_port_by_name(_client, src), jack_port_by_name(_client, dst)); } //--------------------------------------------------------- // portsCompatible //--------------------------------------------------------- bool JackAudioDevice::portsCompatible(void* src, void* dst) const { if(!src || !dst) return false; const char* src_type = jack_port_type((jack_port_t*)src); const char* dst_type = jack_port_type((jack_port_t*)dst); if(!src_type || !dst_type || (strcmp(src_type, dst_type) != 0)) return false; if(!(jack_port_flags((jack_port_t*)src) & JackPortIsOutput) || !(jack_port_flags((jack_port_t*)dst) & JackPortIsInput)) return false; return true; } bool JackAudioDevice::portsCompatible(const char* src, const char* dst) const { if(!_client) return false; return portsCompatible(jack_port_by_name(_client, src), jack_port_by_name(_client, dst)); } //--------------------------------------------------------- // start // Return true on success. //--------------------------------------------------------- bool JackAudioDevice::start(int /*priority*/) { DEBUG_JACK(stderr, "JackAudioDevice::start()\n"); if(!checkJackClient(_client)) return false; MusEGlobal::doSetuid(); DEBUG_PRST_ROUTES (stderr, "JackAudioDevice::start(): calling jack_activate()\n"); // REMOVE Tim. latency. Changed. TESTING. // if (jack_activate(_client)) { // MusEGlobal::undoSetuid(); // fprintf (stderr, "JACK: cannot activate client\n"); // exit(-1); // } if(!jackStarted) { if (jack_activate(_client)) { MusEGlobal::undoSetuid(); fprintf (stderr, "JACK: cannot activate client\n"); exit(-1); } } jackStarted = true; MusEGlobal::undoSetuid(); /* connect the ports. Note: you can't do this before the client is activated, because we can't allow connections to be made to clients that aren't running. */ MusEGlobal::song->connectAllPorts(); fflush(stdin); return true; } //--------------------------------------------------------- // stop //--------------------------------------------------------- void JackAudioDevice::stop() { DEBUG_JACK(stderr, "JackAudioDevice::stop()\n"); if(!checkJackClient(_client)) return; DEBUG_PRST_ROUTES (stderr, "JackAudioDevice::stop(): calling jack_deactivate()\n"); // REMOVE Tim. latency. Changed. TESTING. // if (jack_deactivate(_client)) { // fprintf (stderr, "cannot deactivate client\n"); // } // //if(jackStarted) // { // // if (jack_deactivate(_client)) { // fprintf (stderr, "cannot deactivate client\n"); // } // // } // jackStarted = false; } //--------------------------------------------------------- // transportQuery //--------------------------------------------------------- jack_transport_state_t JackAudioDevice::transportQuery(jack_position_t* pos) { DEBUG_JACK(stderr, "JackAudioDevice::transportQuery pos:%d\n", (unsigned int)pos->frame); // TODO: Compose and return a state if MusE is disengaged from Jack transport. return jack_transport_query(_client, pos); } //--------------------------------------------------------- // timebaseQuery // Given the number of frames in this period, get the bar, beat, tick, // and current absolute tick, and number of ticks in this period. // Return false if information could not be obtained. //--------------------------------------------------------- bool JackAudioDevice::timebaseQuery(unsigned frames, unsigned* bar, unsigned* beat, unsigned* tick, unsigned* curr_abs_tick, unsigned* next_ticks) { jack_position_t jp; jack_transport_query(_client, &jp); DEBUG_JACK(stderr, "timebaseQuery frame:%u\n", jp.frame); if(jp.valid & JackPositionBBT) { #if JACK_DEBUG fprintf(stderr, "timebaseQuery BBT:\n bar:%d beat:%d tick:%d\n bar_start_tick:%f beats_per_bar:%f beat_type:%f ticks_per_beat:%f beats_per_minute:%f\n", jp.bar, jp.beat, jp.tick, jp.bar_start_tick, jp.beats_per_bar, jp.beat_type, jp.ticks_per_beat, jp.beats_per_minute); if(jp.valid & JackBBTFrameOffset) fprintf(stderr, "timebaseQuery BBTFrameOffset: %u\n", jp.bbt_offset); #endif if(jp.ticks_per_beat > 0.0) { unsigned muse_tick = unsigned((double(jp.tick) / jp.ticks_per_beat) * double(MusEGlobal::config.division)); unsigned curr_tick = ((jp.bar - 1) * jp.beats_per_bar + (jp.beat - 1)) * double(MusEGlobal::config.division) + muse_tick; // Prefer the reported frame rate over the app's rate if possible. double f_rate = jp.frame_rate != 0 ? jp.frame_rate : MusEGlobal::sampleRate; // beats_per_minute is "supposed" to be quantized to period size - that is, computed // so that mid-period changes are averaged out to produce a single tempo which // produces the same tick in the end. If we can rely on that, we should be good accuracy. unsigned ticks = double(MusEGlobal::config.division) * (jp.beats_per_minute / 60.0) * double(frames) / f_rate; DEBUG_JACK(stderr, "timebaseQuery curr_tick:%u f_rate:%f ticks:%u\n", curr_tick, f_rate, ticks); if(bar) *bar = jp.bar; if(beat) *beat = jp.beat; if(tick) *tick = muse_tick; if(curr_abs_tick) *curr_abs_tick = curr_tick; if(next_ticks) *next_ticks = ticks; return true; } } #if JACK_DEBUG if(jp.valid & JackPositionTimecode) fprintf(stderr, "timebaseQuery JackPositionTimecode: frame_time:%f next_time:%f\n", jp.frame_time, jp.next_time); if(jp.valid & JackAudioVideoRatio) fprintf(stderr, "timebaseQuery JackAudioVideoRatio: %f\n", jp.audio_frames_per_video_frame); if(jp.valid & JackVideoFrameOffset) fprintf(stderr, "timebaseQuery JackVideoFrameOffset: %u\n", jp.video_offset); #endif return false; } //--------------------------------------------------------- // systemTimeUS // Return system time in microseconds as a 64-bit integer. // Depends on selected clock source. // With Jack, may be based upon wallclock time, the // processor cycle counter or the HPET clock etc. //--------------------------------------------------------- uint64_t JackAudioDevice::systemTimeUS() const { // Client valid? According to sletz: For jack_get_time "There are some timing related // initialization that are done once when a first client is created." if(!checkJackClient(_client)) return AudioDevice::systemTimeUS(); return jack_get_time(); } //--------------------------------------------------------- // getCurFrame //--------------------------------------------------------- unsigned int JackAudioDevice::getCurFrame() const { DEBUG_JACK(stderr, "JackAudioDevice::getCurFrame pos.frame:%d\n", pos.frame); if(!MusEGlobal::config.useJackTransport) return AudioDevice::getCurFrame(); return pos.frame; } //--------------------------------------------------------- // framePos //--------------------------------------------------------- unsigned JackAudioDevice::framePos() const { //if(!MusEGlobal::config.useJackTransport) //{ // DEBUG_JACK(stderr, "JackAudioDevice::framePos dummyPos:%d\n", dummyPos); // return dummyPos; //} if(!checkJackClient(_client)) return 0; jack_nframes_t n = jack_frame_time(_client); //DEBUG_JACK(stderr, "JackAudioDevice::framePos jack frame:%d\n", (int)n); return n; } //--------------------------------------------------------- // framesAtCycleStart // Frame count at the start of current cycle. // This is meant to be called from inside process thread only. //--------------------------------------------------------- unsigned JackAudioDevice::framesAtCycleStart() const { if(!checkJackClient(_client)) return 0; jack_nframes_t n = jack_last_frame_time(_client); //DEBUG_JACK(stderr, "JackAudioDevice::framesAtCycleStart jack frame:%d\n", (unsigned)n); return (unsigned)n; } //--------------------------------------------------------- // framesSinceCycleStart // Estimated frames since the last process cycle began // This is meant to be called from inside process thread only. //--------------------------------------------------------- unsigned JackAudioDevice::framesSinceCycleStart() const { if(!checkJackClient(_client)) return 0; jack_nframes_t n = jack_frames_since_cycle_start(_client); //DEBUG_JACK(stderr, "JackAudioDevice::framesSinceCycleStart jack frame:%d\n", (unsigned)n); // Safety due to inaccuracies. It cannot be after the segment, right? if(n >= MusEGlobal::segmentSize) n = MusEGlobal::segmentSize - 1; return (unsigned)n; } #if 0 //--------------------------------------------------------- // framesDelay // TODO //--------------------------------------------------------- int JackAudioDevice::frameDelay() const { jack_nframes_t n = (MusEGlobal::segmentSize * (segmentCount-1)) - jack_frames_since_cycle_start(client); return (int)n; } #endif unsigned JackAudioDevice::curTransportFrame() const { if(!checkJackClient(_client)) return 0; return jack_get_current_transport_frame(_client); } //--------------------------------------------------------- // getJackPorts //--------------------------------------------------------- void JackAudioDevice::getJackPorts(const char** ports, std::list& name_list, bool midi, bool physical, int aliases) { DEBUG_JACK(stderr, "JackAudioDevice::getJackPorts()\n"); QString qname; QString cname(jack_get_client_name(_client)); for (const char** p = ports; p && *p; ++p) { // Should be safe and quick search here, we know that the port name is valid. jack_port_t* port = jack_port_by_name(_client, *p); int port_flags = jack_port_flags(port); // Ignore our own client ports. if(jack_port_is_mine(_client, port)) { if(MusEGlobal::debugMsg) fprintf(stderr, "JackAudioDevice::getJackPorts ignoring own port: %s\n", *p); continue; } int nsz = jack_port_name_size(); char buffer[nsz]; bool mthrough = false; if(midi) { strncpy(buffer, *p, nsz); char a2[nsz]; char* al[2]; al[0] = buffer; al[1] = a2; int na = jack_port_get_aliases(port, al); if(na >= 1) { qname = QString(al[0]); //fprintf(stderr, "Checking port name for: %s\n", (QString("alsa_pcm:") + cname + QString("/")).toLatin1().constData()); // Ignore our own ALSA client! if(qname.startsWith(QString("alsa_pcm:") + cname + QString("/"))) continue; // Put Midi Through after all others. mthrough = qname.startsWith(QString("alsa_pcm:Midi-Through/")); //if((physical && mthrough) || (!physical && !mthrough)) //if(physical && mthrough) // continue; } } // Put physical/terminal ports before others. bool is_phys = (port_flags & (JackPortIsTerminal | JackPortIsPhysical)) && !mthrough; if((physical && !is_phys) || (!physical && is_phys)) continue; strncpy(buffer, *p, nsz); if((aliases == 0) || (aliases == 1)) { char a2[nsz]; char* al[2]; al[0] = buffer; al[1] = a2; int na = jack_port_get_aliases(port, al); int a = aliases; if(a >= na) { a = na; if(a > 0) a--; } qname = QString(al[a]); } else qname = QString(buffer); name_list.push_back(qname); } } //--------------------------------------------------------- // outputPorts //--------------------------------------------------------- std::list JackAudioDevice::outputPorts(bool midi, int aliases) { DEBUG_JACK(stderr, "JackAudioDevice::outputPorts()\n"); std::list clientList; if(!checkJackClient(_client)) return clientList; const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; const char** ports = jack_get_ports(_client, 0, type, JackPortIsOutput); if(ports) { getJackPorts(ports, clientList, midi, true, aliases); // Get physical ports first. getJackPorts(ports, clientList, midi, false, aliases); // Get non-physical ports last. jack_free(ports); } return clientList; } //--------------------------------------------------------- // inputPorts //--------------------------------------------------------- std::list JackAudioDevice::inputPorts(bool midi, int aliases) { DEBUG_JACK(stderr, "JackAudioDevice::inputPorts()\n"); std::list clientList; if(!checkJackClient(_client)) return clientList; const char* type = midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE; const char** ports = jack_get_ports(_client, 0, type, JackPortIsInput); if(ports) { getJackPorts(ports, clientList, midi, true, aliases); // Get physical ports first. getJackPorts(ports, clientList, midi, false, aliases); // Get non-physical ports last. jack_free(ports); } return clientList; } //--------------------------------------------------------- // setPortName //--------------------------------------------------------- void JackAudioDevice::setPortName(void* p, const char* n) { // NOTE: jack_port_set_name() is deprecated as of Jack2 = 1.9.11, and Jack1 > 0.124.1 if(jack_port_rename_fp) { if(!checkJackClient(_client)) return; jack_port_rename_fp(_client, (jack_port_t*)p, n); } else if(jack_port_set_name_fp) jack_port_set_name_fp((jack_port_t*)p, n); } //--------------------------------------------------------- // portName // Returns name of port and sets success. // This method consults a blacklist of client names, // such as "system:", whether to pick the name or // one of the aliases, whichever does NOT contain // the blacklist names. // preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. //--------------------------------------------------------- char* JackAudioDevice::portName(void* port, char* str, int str_size, int preferred_name_or_alias) { bool A = false, B = false, C = false; const char* p_name = jack_port_name((jack_port_t*)port); if(p_name && p_name[0] != '\0') { // TODO: Make this a user editable blacklist of client names! if((strncmp(p_name, "system:", 7) != 0 && preferred_name_or_alias == -1) || preferred_name_or_alias == 0) return MusELib::strntcpy(str, p_name, str_size); A = true; } int nsz = jack_port_name_size(); char a1[nsz]; char a2[nsz]; char* al[2]; al[0] = &a1[0]; al[1] = &a2[0]; int na = jack_port_get_aliases((jack_port_t*)port, al); if(na >= 1 && *al[0] != '\0') { if((strncmp(al[0], "system:", 7) != 0 && preferred_name_or_alias == -1) || preferred_name_or_alias == 1) return MusELib::strntcpy(str, al[0], str_size); B = true; } if(na >= 2 && *al[1] != '\0') { if((strncmp(al[1], "system:", 7) != 0 && preferred_name_or_alias == -1) || preferred_name_or_alias == 2) return MusELib::strntcpy(str, al[1], str_size); C = true; } if(A) return MusELib::strntcpy(str, p_name, str_size); if(B) return MusELib::strntcpy(str, al[0], str_size); if(C) return MusELib::strntcpy(str, al[1], str_size); return MusELib::strntcpy(str, p_name, str_size); // strntcpy accepts NULL source } //--------------------------------------------------------- // portLatency // If capture is true get the capture latency, // otherwise get the playback latency. //--------------------------------------------------------- unsigned int JackAudioDevice::portLatency(void* port, bool capture) const { if(!checkJackClient(_client) || !port) return 0; //QString s(jack_port_name((jack_port_t*)port)); //fprintf(stderr, "Jack::portName %p %s\n", port, s.toLatin1().constData()); // NOTICE: For at least the ALSA driver (tested), the input latency is // always 1 period while the output latency is always n periods // (or n-1 periods for Jack1 or Jack2 Sync mode). // (Also there is the user latency from command line or QJackCtl.) // In other words, the Jack command line -p (number of periods) ONLY applies to audio output ports. jack_latency_range_t p_range; jack_port_get_latency_range((jack_port_t*)port, JackPlaybackLatency, &p_range); jack_latency_range_t c_range; jack_port_get_latency_range((jack_port_t*)port, JackCaptureLatency, &c_range); //if(MusEGlobal::audio->isPlaying()) // fprintf(stderr, "JackAudioDevice::portLatency port:%p capture:%d c_range.min:%d c_range.max:%d p_range.min:%d p_range.max:%d\n", // port, capture, c_range.min, c_range.max, p_range.min, p_range.max); if(capture) return c_range.max; return p_range.max; // Hm... for speed, maybe cache the values? } //--------------------------------------------------------- // unregisterPort //--------------------------------------------------------- void JackAudioDevice::unregisterPort(void* p) { DEBUG_JACK(stderr, "JackAudioDevice::unregisterPort(%p)\n", p); if(!checkJackClient(_client) || !p) return; // fprintf(stderr, "JACK: unregister Port\n"); jack_port_unregister(_client, (jack_port_t*)p); } float JackAudioDevice::getDSP_Load() { return jack_cpu_load(_client); } AudioDevice::PortType JackAudioDevice::portType(void* p) const { if(!p) return UnknownType; if(const char* type = jack_port_type((jack_port_t*)p)) { if(strcmp(type, JACK_DEFAULT_AUDIO_TYPE) == 0) return AudioPort; if(strcmp(type, JACK_DEFAULT_MIDI_TYPE) == 0) return MidiPort; } return UnknownType; } AudioDevice::PortDirection JackAudioDevice::portDirection(void* p) const { if(!p) return UnknownDirection; const int flags = jack_port_flags((jack_port_t*)p); if(flags & JackPortIsInput) return InputPort; if(flags & JackPortIsOutput) return OutputPort; return UnknownDirection; } //--------------------------------------------------------- // setSyncTimeout // Sets the amount of time to wait before sync times out, in microseconds. // Note that at least with the Jack driver, this function seems not realtime friendly. //--------------------------------------------------------- void JackAudioDevice::setSyncTimeout(unsigned usec) { // Make sure our built-in transport sync timeout is set as well. AudioDevice::setSyncTimeout(usec); if(!checkJackClient(_client)) return; // Note that docs say default is 2 sec, but that's wrong, // Jack1 does set 2 sec, but Jack2 sets a default of 10 secs ! jack_set_sync_timeout(_client, usec); } //--------------------------------------------------------- // transportSyncToPlayDelay // The number of frames which the driver waits to switch to PLAY // mode after the audio sync function says it is ready to roll. // For example Jack Transport waits one cycle while our own transport does not. //--------------------------------------------------------- unsigned JackAudioDevice::transportSyncToPlayDelay() const { // If Jack transport is being used, it delays by one cycle. if(MusEGlobal::config.useJackTransport) return MusEGlobal::segmentSize; // Otherwise return our internal transport's value. return AudioDevice::transportSyncToPlayDelay(); } unsigned JackAudioDevice::transportRelocateOrPlayDelay() const { // If Jack transport is being used, it delays by two cycles. if(MusEGlobal::config.useJackTransport) return 2 * MusEGlobal::segmentSize; // Otherwise return our internal transport's value. return AudioDevice::transportRelocateOrPlayDelay(); } //--------------------------------------------------------- // getState //--------------------------------------------------------- int JackAudioDevice::getState() { // If we're not using Jack's transport, just return current state. if(!MusEGlobal::config.useJackTransport) { //pos.valid = jack_position_bits_t(0); //pos.frame = MusEGlobal::audio->pos().frame(); //return MusEGlobal::audio->getState(); //DEBUG_JACK(stderr, "JackAudioDevice::getState dummyState:%d\n", dummyState); return AudioDevice::getState(); } //DEBUG_JACK(stderr, "JackAudioDevice::getState ()\n"); if(!checkJackClient(_client)) return 0; transportState = jack_transport_query(_client, &pos); //DEBUG_JACK(stderr, "JackAudioDevice::getState transportState:%d\n", transportState); switch (int(transportState)) { case JackTransportStopped: return Audio::STOP; case JackTransportLooping: case JackTransportRolling: return Audio::PLAY; case JackTransportStarting: //fprintf(stderr, "JackAudioDevice::getState JackTransportStarting\n"); return Audio::START_PLAY; //case JackTransportNetStarting: -- only available in Jack-2! // FIXME: Quick and dirty hack to support both Jack-1 and Jack-2 // Really need a config check of version... case 4: //fprintf(stderr, "JackAudioDevice::getState JackTransportNetStarting\n"); return Audio::START_PLAY; break; default: return Audio::STOP; } } //--------------------------------------------------------- // setFreewheel //--------------------------------------------------------- void JackAudioDevice::setFreewheel(bool f) { DEBUG_JACK(stderr, "JackAudioDevice::setFreewheel(\n"); if(!checkJackClient(_client)) return; jack_set_freewheel(_client, f); } //--------------------------------------------------------- // startTransport //--------------------------------------------------------- void JackAudioDevice::startTransport() { DEBUG_JACK(stderr, "JackAudioDevice::startTransport()\n"); // If we're not using Jack's transport, just pass PLAY and current frame along // as if processSync was called. if(!MusEGlobal::config.useJackTransport) { AudioDevice::startTransport(); return; } if(!checkJackClient(_client)) return; // fprintf(stderr, "JACK: startTransport\n"); jack_transport_start(_client); } //--------------------------------------------------------- // stopTransport //--------------------------------------------------------- void JackAudioDevice::stopTransport() { DEBUG_JACK(stderr, "JackAudioDevice::stopTransport()\n"); if(!MusEGlobal::config.useJackTransport) { AudioDevice::stopTransport(); return; } if(!checkJackClient(_client)) return; if (transportState != JackTransportStopped) { // fprintf(stderr, "JACK: stopTransport\n"); jack_transport_stop(_client); transportState=JackTransportStopped; } } //--------------------------------------------------------- // seekTransport //--------------------------------------------------------- void JackAudioDevice::seekTransport(unsigned frame) { DEBUG_JACK(stderr, "JackAudioDevice::seekTransport() frame:%d\n", frame); if(!MusEGlobal::config.useJackTransport) { // STOP -> STOP means seek in stop mode. PLAY -> START_PLAY means seek in play mode. AudioDevice::seekTransport(frame); return; } if(!checkJackClient(_client)) return; // fprintf(stderr, "JACK: seekTransport %d\n", frame); jack_transport_locate(_client, frame); } //--------------------------------------------------------- // seekTransport //--------------------------------------------------------- void JackAudioDevice::seekTransport(const Pos &p) { DEBUG_JACK(stderr, "JackAudioDevice::seekTransport(Pos) frame:%d\n", p.frame()); if(!MusEGlobal::config.useJackTransport) { // STOP -> STOP means seek in stop mode. PLAY -> START_PLAY means seek in play mode. AudioDevice::seekTransport(p); return; } if(!checkJackClient(_client)) return; // TODO: Be friendly to other apps... Sadly not many of us use jack_transport_reposition. // This is actually required IF we want the extra position info to show up // in the sync callback, otherwise we get just the frame only. // This information is shared on the server, it is directly passed around. // jack_transport_locate blanks the info from sync until the timebase callback reads // it again right after, from some timebase master. See process in audio.cpp // jack_position_t jp; // jp.frame = p.frame(); // // jp.valid = JackPositionBBT; // p.mbt(&jp.bar, &jp.beat, &jp.tick); // jp.bar_start_tick = Pos(jp.bar, 0, 0).tick(); // jp.bar++; // jp.beat++; // jp.beats_per_bar = 5; // TODO Make this correct ! // jp.beat_type = 8; // // jp.ticks_per_beat = MusEGlobal::config.division; // int tempo = MusEGlobal::tempomap.tempo(p.tick()); // jp.beats_per_minute = (60000000.0 / tempo) * MusEGlobal::tempomap.globalTempo()/100.0; // jack_transport_reposition(_client, &jp); jack_transport_locate(_client, p.frame()); } //--------------------------------------------------------- // findPort //--------------------------------------------------------- void* JackAudioDevice::findPort(const char* name) { DEBUG_JACK(stderr, "JackAudioDevice::findPort(%s)\n", name); if(!checkJackClient(_client) || !name || name[0] == '\0') return NULL; void* p = jack_port_by_name(_client, name); return p; } //--------------------------------------------------------- // setMaster //--------------------------------------------------------- int JackAudioDevice::setMaster(bool f, bool unconditional) { // Check this one-time hack flag so that it forces master. // MusEGlobal::audioDevice may be NULL, esp. at startup when loading a song file, // so this flag is necessary for the next valid call to setMaster. if(MusEGlobal::timebaseMasterForceFlag) { unconditional = true; MusEGlobal::timebaseMasterForceFlag = false; } DEBUG_JACK(stderr, "JackAudioDevice::setMaster val:%d unconditional:%d\n", f, unconditional); if(!checkJackClient(_client)) return 0; int r = 0; if(f) { if(MusEGlobal::config.useJackTransport) { // Make Muse the Jack timebase master. Possibly do it unconditionally (second param = 0). // Note that if we are already the timebase master, this will succeed and it will still // call the timebase callback once afterwards. r = jack_set_timebase_callback(_client, !unconditional, (JackTimebaseCallback) timebase_callback, 0); if(MusEGlobal::debugMsg || JACK_DEBUG) { if(r && !MusEGlobal::timebaseMasterState && unconditional) fprintf(stderr, "JackAudioDevice::setMaster jack_set_timebase_callback failed: result:%d\n", r); } if(!(bool)r != MusEGlobal::timebaseMasterState) { MusEGlobal::timebaseMasterState = !(bool)r; MusEGlobal::song->update(SC_TIMEBASE_MASTER); } } else { r = 1; fprintf(stderr, "JackAudioDevice::setMaster cannot set master because useJackTransport is false\n"); if(MusEGlobal::timebaseMasterState) { MusEGlobal::timebaseMasterState = false; MusEGlobal::song->update(SC_TIMEBASE_MASTER); } } } else { r = jack_release_timebase(_client); if(MusEGlobal::debugMsg || JACK_DEBUG) { if(r && MusEGlobal::timebaseMasterState) fprintf(stderr, "JackAudioDevice::setMaster jack_release_timebase failed: result:%d\n", r); } if(!r && MusEGlobal::timebaseMasterState) { MusEGlobal::timebaseMasterState = false; MusEGlobal::song->update(SC_TIMEBASE_MASTER); } } return r; } //--------------------------------------------------------- // exitJackAudio //--------------------------------------------------------- void exitJackAudio() { DEBUG_JACK(stderr, "exitJackAudio()\n"); if (jackAudio) delete jackAudio; DEBUG_JACK(stderr, "exitJackAudio() after delete jackAudio\n"); MusEGlobal::audioDevice = NULL; muse_atomic_destroy(&atomicGraphChangedPending); } } // namespace MusECore muse-4.0.0/src/muse/driver/jackaudio.h000066400000000000000000000222141404105311000176100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: jackaudio.h,v 1.20.2.4 2009/12/20 05:00:35 terminator356 Exp $ // (C) Copyright 2002 Werner Schweer (ws@seh.de) // (C) Copyright 2015 Tim E. Real (terminator356 on sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __JACKAUDIO_H__ #define __JACKAUDIO_H__ #include #include #include "audiodev.h" #include "operations.h" class QString; namespace MusEGlobal { bool checkAudioDevice(); } namespace MusECore { enum JackCallbackEventType {PortRegister, PortUnregister, PortConnect, PortDisconnect, GraphChanged}; struct JackCallbackEvent { JackCallbackEventType type; jack_port_id_t port_id_A; jack_port_id_t port_id_B; jack_port_t* port_A; jack_port_t* port_B; }; typedef std::list JackCallbackEventList; typedef std::list::iterator iJackCallbackEvent; //--------------------------------------------------------- // JackAudioDevice //--------------------------------------------------------- class JackAudioDevice : public AudioDevice { public: enum JackSyncPhases { SyncCheck = 0, SyncStarted, Syncing, Synced }; enum JackTimebaseMasterPhases { MasterCheck = 0, IsNotMaster, IsMaster }; private: jack_client_t* _client; jack_transport_state_t transportState; jack_position_t pos; char jackRegisteredName[16]; // Free-running frame counter incremented always in process. jack_nframes_t _frameCounter; PendingOperationList operations; // Temporary, for processing callback event FIFO. JackCallbackEventList jackCallbackEvents; void getJackPorts(const char** ports, std::list& name_list, bool midi, bool physical, int aliases); static int processAudio(jack_nframes_t frames, void*); void processGraphChanges(); void processJackCallbackEvents(const Route& our_node, jack_port_t* our_port, RouteList* route_list, bool is_input); void checkNewRouteConnections(jack_port_t* our_port, int channel, RouteList* route_list); // Return 0: Neither disconnect nor unregister found // 1: Disconnect found followed later by unregister // 2: Disconnect found (with no unregister later) int checkDisconnectCallback(const jack_port_t* our_port, const jack_port_t* port); // Return 0: No port register found (or it was cancelled by a later unregister) // 1: Port register was found. int checkPortRegisterCallback(const jack_port_t* port); static int static_JackXRunCallback(void *); public: JackAudioDevice(jack_client_t* cl, char * jack_id_string); virtual ~JackAudioDevice(); virtual inline int deviceType() const { return JACK_AUDIO; } virtual const char* driverName() const { return "JackAudioDevice"; } virtual bool start(int); virtual void stop (); // These are meant to be called from inside process thread only. virtual unsigned framesAtCycleStart() const; virtual unsigned framesSinceCycleStart() const; jack_client_t* jackClient() const { return _client; } virtual void registerClient(); virtual const char* clientName() { return jackRegisteredName; } virtual void nullify_client() { _client = 0; } float getDSP_Load(); virtual std::list outputPorts(bool midi = false, int aliases = -1); virtual std::list inputPorts(bool midi = false, int aliases = -1); virtual void* registerOutPort(const char* /*name*/, bool /*midi*/); virtual void* registerInPort(const char* /*name*/, bool /*midi*/); virtual void unregisterPort(void*); virtual AudioDevice::PortType portType(void*) const; virtual AudioDevice::PortDirection portDirection(void*) const; virtual bool connect(void* src, void* dst); virtual bool connect(const char* src, const char* dst); virtual bool disconnect(void* src, void* dst); virtual bool disconnect(const char* src, const char* dst); virtual int connections(void* clientPort) { return jack_port_connected((jack_port_t*)clientPort); } virtual bool portConnectedTo(void* our_port, const char* port) { return jack_port_connected_to((jack_port_t*)our_port, port); } // Returns true if the ports are connected. virtual bool portsCanDisconnect(void* src, void* dst) const; // Returns true if the ports are found and they are connected. virtual bool portsCanDisconnect(const char* src, const char* dst) const; // Returns true if the ports are not connected and CAN be connected. virtual bool portsCanConnect(void* src, void* dst) const; // Returns true if the ports are found and they are not connected and CAN be connected. virtual bool portsCanConnect(const char* src, const char* dst) const; // Returns true if the ports CAN be connected. virtual bool portsCompatible(void* src, void* dst) const; // Returns true if the ports are found and they CAN be connected. virtual bool portsCompatible(const char* src, const char* dst) const; virtual void setPortName(void* p, const char* n); // preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. virtual char* portName(void* port, char* str, int str_size, int preferred_name_or_alias = -1); virtual const char* canonicalPortName(void* port) { if(!port) return NULL; return jack_port_name((jack_port_t*)port); } virtual void* findPort(const char* name); virtual unsigned int portLatency(void* port, bool capture) const; virtual float* getBuffer(void* port, unsigned long nframes) { return (float*)jack_port_get_buffer((jack_port_t*)port, nframes); } // Sets the amount of time to wait before sync times out, in microseconds. // Note that at least with the Jack driver, this function seems not realtime friendly. virtual void setSyncTimeout(unsigned usec); // The number of frames that the driver waits to switch to PLAY // mode after the audio sync function says it is ready to roll. // For example Jack Transport waits one cycle while our own tranport does not. virtual unsigned transportSyncToPlayDelay() const; // The number of frames that the driver waits to relocate or switch to PLAY // mode after the a call to relocation or seek. // For example Jack Transport waits two cycles while our own transport does not. virtual unsigned transportRelocateOrPlayDelay() const; virtual int getState(); virtual unsigned int getCurFrame() const; virtual unsigned framePos() const; virtual unsigned frameTime() const { return _frameCounter; } virtual uint64_t systemTimeUS() const; virtual bool isRealtime() { return jack_is_realtime(_client); } // Jack does this for us. virtual bool setRealTime() { return false; } virtual int realtimePriority() const; virtual unsigned curTransportFrame() const; virtual void startTransport(); virtual void stopTransport(); virtual void seekTransport(unsigned frame); virtual void seekTransport(const Pos &p); virtual void setFreewheel(bool f); // Whether the device has its own transport (Jack transport etc.), beyond the one built into this class. virtual bool hasOwnTransport() const { return true; }; // Whether the device supports timebase master capabilities. virtual bool hasTimebaseMaster() const { return true; }; // Sets or resets timebase master. Returns 0 on success. Otherwise, may return an error code. virtual int setMaster(bool f, bool unconditional = false); jack_transport_state_t transportQuery(jack_position_t* pos); bool timebaseQuery(unsigned frames, unsigned* bar, unsigned* beat, unsigned* tick, unsigned* curr_abs_tick, unsigned* next_ticks); void graphChanged(); }; // Our own wrappers for functions we need to look up with dlsym: typedef void(*jack_get_version_type)(int*, int*, int*, int*); extern jack_get_version_type jack_get_version_fp; typedef int(*jack_port_set_name_type)(jack_port_t*, const char*); extern jack_port_set_name_type jack_port_set_name_fp; typedef int(*jack_port_rename_type)(jack_client_t*, jack_port_t*, const char*); extern jack_port_rename_type jack_port_rename_fp; } // namespace MusECore #endif muse-4.0.0/src/muse/driver/jackmidi.cpp000066400000000000000000001556601404105311000200000ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: jackmidi.cpp,v 1.1.1.1 2010/01/27 09:06:43 terminator356 Exp $ // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "jackmidi.h" #include "jackaudio.h" #include "song.h" #include "globals.h" #include "midi_consts.h" #include "mididev.h" #include "../midiport.h" #include "../midiseq.h" #include "../midictrl.h" #include "../audio.h" #include "minstrument.h" #include "mpevent.h" #include "sync.h" #include "audiodev.h" #include "../mplugins/midiitransform.h" #include "../mplugins/mitplugin.h" #include "gconfig.h" #include "track.h" #include "route.h" #include "helper.h" // Forwards from header: #include "xml.h" // Turn on debug messages. //#define JACK_MIDI_DEBUG // For debugging output: Uncomment the fprintf section. #define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args); namespace MusECore { //--------------------------------------------------------- // MidiJackDevice // in_jack_port or out_jack_port can be null //--------------------------------------------------------- MidiJackDevice::MidiJackDevice(const QString& n) : MidiDevice(n) { _in_client_jackport = NULL; _out_client_jackport = NULL; } MidiJackDevice::~MidiJackDevice() { #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::~MidiJackDevice()\n"); #endif if(MusEGlobal::audioDevice) { if(_in_client_jackport) MusEGlobal::audioDevice->unregisterPort(_in_client_jackport); if(_out_client_jackport) MusEGlobal::audioDevice->unregisterPort(_out_client_jackport); } //close(); } //--------------------------------------------------------- // createJackMidiDevice // If name parameter is blank, creates a new (locally) unique one. //--------------------------------------------------------- MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable 3: Writable + Readable { int ni = 0; if(name.isEmpty()) { for( ; ni < 65536; ++ni) { name = QString("jack-midi-") + QString::number(ni); if(!MusEGlobal::midiDevices.find(name, JACK_MIDI)) break; } } if(ni >= 65536) { fprintf(stderr, "MusE: createJackMidiDevice failed! Can't find an unused midi device name 'jack-midi-[0-65535]'.\n"); return 0; } MidiJackDevice* dev = new MidiJackDevice(name); dev->setrwFlags(rwflags); MusEGlobal::midiDevices.add(dev); return dev; } //--------------------------------------------------------- // setName //--------------------------------------------------------- void MidiJackDevice::setName(const QString& s) { #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::setName %s new name:%s\n", name().toLatin1().constData(), s.toLatin1().constData()); #endif _name = s; if(inClientPort()) MusEGlobal::audioDevice->setPortName(inClientPort(), (s + QString(JACK_MIDI_IN_PORT_SUFFIX)).toLatin1().constData()); if(outClientPort()) MusEGlobal::audioDevice->setPortName(outClientPort(), (s + QString(JACK_MIDI_OUT_PORT_SUFFIX)).toLatin1().constData()); } //--------------------------------------------------------- // open //--------------------------------------------------------- QString MidiJackDevice::open() { _openFlags &= _rwFlags; // restrict to available bits #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::open %s\n", name().toLatin1().constData()); #endif // Start by disabling for now. _writeEnable = _readEnable = false; if(!MusEGlobal::checkAudioDevice()) { fprintf(stderr, "MusE: MidiJackDevice::open failed: No audio device\n"); _state = QString("Not ready"); return _state; } QString s; bool out_fail = false, in_fail = false; if(_openFlags & 1) { if(!_out_client_jackport) { if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO) { s = name() + QString(JACK_MIDI_OUT_PORT_SUFFIX); QByteArray ba = s.toLatin1(); const char* cs = ba.constData(); DEBUG_PRST_ROUTES(stderr, "MusE: MidiJackDevice::open creating output port name %s\n", cs); _out_client_jackport = (jack_port_t*)MusEGlobal::audioDevice->registerOutPort(cs, true); if(!_out_client_jackport) { fprintf(stderr, "MusE: MidiJackDevice::open failed creating output port name %s\n", cs); _writeEnable = false; out_fail = true; } else { _writeEnable = true; const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(_out_client_jackport); if(our_port_name) { // (We just registered the port. At this point, any existing persistent routes' jackPort SHOULD be 0.) for(iRoute ir = _outRoutes.begin(); ir != _outRoutes.end(); ++ir) { if(ir->type != Route::JACK_ROUTE) continue; const char* route_name = ir->persistentJackPortName; if(!ir->jackPort) ir->jackPort = MusEGlobal::audioDevice->findPort(route_name); //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) if(ir->jackPort) MusEGlobal::audioDevice->connect(our_port_name, route_name); } } } } } } else { _writeEnable = false; if(_out_client_jackport) { DEBUG_PRST_ROUTES(stderr, "MusE: MidiJackDevice::open unregistering output port\n"); // We want to unregister the port (which will also disconnect it), AND remove Routes, and then NULL-ify _out_client_jackport. // We could let our graph change callback (the gui thread one) remove the Routes (which it would anyway). // But that happens later (gui thread) and it needs a valid _out_client_jackport, // so use of a registration callback would be required to finally NULL-ify _out_client_jackport, // and that would require some MidiDevice setter or re-scanner function. // So instead, manually remove the Routes (in the audio thread), then unregister the port, then immediately NULL-ify _out_client_jackport. // Our graph change callback (the gui thread one) will see a NULL _out_client_jackport // so it cannot possibly remove the Routes, but that won't matter - we are removing them manually. // This is the same technique that is used for audio elsewhere in the code, like Audio::msgSetChannels() // (but not Song::connectJackRoutes() which keeps the Routes for when undoing deletion of a track). // // NOTE: TESTED: Possibly a bug in QJackCtl, with Jack-1 (not Jack-2 !): // After toggling the input/output green lights in the midi ports list (which gets us here), intermittently // qjackctl refuses to draw connections. It allows them to be made (MusE responds) but blanks them out immediately // and does not show 'disconnect', as if it is not properly aware of the connections. // But ALL else is OK - the connection is fine in MusE, verbose Jack messages show all went OK. // Yes, there's no doubt the connections are being made. // When I toggle the lights again (which kills, then recreates the ports here), the problem can disappear or come back again. // Also once observed a weird double connection from the port to two different Jack ports but one of // the connections should not have been there and kept toggling along with the other (like a 'ghost' connection). for(iRoute ir = _outRoutes.begin(); ir != _outRoutes.end(); ++ir) { if(ir->type != Route::JACK_ROUTE) continue; if(ir->jackPort) { // Before we nullify the jackPort, grab the latest valid name of the port. MusEGlobal::audioDevice->portName(ir->jackPort, ir->persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); ir->jackPort = 0; } } MusEGlobal::audioDevice->unregisterPort(_out_client_jackport); _out_client_jackport = NULL; } } if(_openFlags & 2) { if(!_in_client_jackport) { if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO) { s = name() + QString(JACK_MIDI_IN_PORT_SUFFIX); QByteArray ba = s.toLatin1(); const char* cs = ba.constData(); DEBUG_PRST_ROUTES(stderr, "MusE: MidiJackDevice::open creating input port name %s\n", cs); _in_client_jackport = (jack_port_t*)MusEGlobal::audioDevice->registerInPort(cs, true); if(!_in_client_jackport) { fprintf(stderr, "MusE: MidiJackDevice::open failed creating input port name %s\n", cs); _readEnable = false; in_fail = true; } else { _readEnable = true; const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(_in_client_jackport); if(our_port_name) { // (We just registered the port. At this point, any existing persistent routes' jackPort SHOULD be 0.) for(iRoute ir = _inRoutes.begin(); ir != _inRoutes.end(); ++ir) { if(ir->type != Route::JACK_ROUTE) continue; const char* route_name = ir->persistentJackPortName; if(!ir->jackPort) ir->jackPort = MusEGlobal::audioDevice->findPort(route_name); //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) if(ir->jackPort) MusEGlobal::audioDevice->connect(route_name, our_port_name); } } } } } } else { _readEnable = false; if(_in_client_jackport) { DEBUG_PRST_ROUTES(stderr, "MusE: MidiJackDevice::open unregistering input port\n"); for(iRoute ir = _inRoutes.begin(); ir != _inRoutes.end(); ++ir) { if(ir->type != Route::JACK_ROUTE) continue; if(ir->jackPort) { // Before we nullify the jackPort, grab the latest valid name of the port. MusEGlobal::audioDevice->portName(ir->jackPort, ir->persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); ir->jackPort = 0; } } MusEGlobal::audioDevice->unregisterPort(_in_client_jackport); _in_client_jackport = NULL; } } if(out_fail && in_fail) _state = QString("R+W Open fail"); else if(out_fail) _state = QString("Write open fail"); else if(in_fail) _state = QString("Read open fail"); else _state = QString("OK"); return _state; } //--------------------------------------------------------- // close //--------------------------------------------------------- void MidiJackDevice::close() { #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::close %s\n", name().toLatin1().constData()); #endif DEBUG_PRST_ROUTES(stderr, "MidiJackDevice::close %s\n", name().toLatin1().constData()); // Disable immediately. _writeEnable = _readEnable = false; jack_port_t* i_jp = _in_client_jackport; jack_port_t* o_jp = _out_client_jackport; _in_client_jackport = 0; _out_client_jackport = 0; DEBUG_PRST_ROUTES(stderr, "MidiJackDevice::close nullifying route jackPorts...\n"); for(iRoute ir = _outRoutes.begin(); ir != _outRoutes.end(); ++ir) { if(ir->type != Route::JACK_ROUTE) continue; if(ir->jackPort) { // Before we nullify the jackPort, grab the latest valid name of the port. if(MusEGlobal::checkAudioDevice()) MusEGlobal::audioDevice->portName(ir->jackPort, ir->persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); ir->jackPort = 0; } } for(iRoute ir = _inRoutes.begin(); ir != _inRoutes.end(); ++ir) { if(ir->type != Route::JACK_ROUTE) continue; if(ir->jackPort) { // Before we nullify the jackPort, grab the latest valid name of the port. if(MusEGlobal::checkAudioDevice()) MusEGlobal::audioDevice->portName(ir->jackPort, ir->persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); ir->jackPort = 0; } } // if(_in_client_jackport) // { // if(MusEGlobal::checkAudioDevice()) // MusEGlobal::audioDevice->unregisterPort(_in_client_jackport); // _in_client_jackport = 0; // } // if(_out_client_jackport) // { // if(MusEGlobal::checkAudioDevice()) // MusEGlobal::audioDevice->unregisterPort(_out_client_jackport); // _out_client_jackport = 0; // } DEBUG_PRST_ROUTES(stderr, "MidiJackDevice::close unregistering our ports...\n"); if(i_jp) { if(MusEGlobal::checkAudioDevice()) MusEGlobal::audioDevice->unregisterPort(i_jp); } if(o_jp) { if(MusEGlobal::checkAudioDevice()) MusEGlobal::audioDevice->unregisterPort(o_jp); } _state = QString("Closed"); } //--------------------------------------------------------- // writeRouting //--------------------------------------------------------- void MidiJackDevice::writeRouting(int level, Xml& xml) const { // If this device is not actually in use by the song, do not write any routes. // This prevents bogus routes from being saved and propagated in the med file. // Removed. Need to let routes be saved. //if(midiPort() == -1) // return; QString s; if(rwFlags() & 2) // Readable { for (ciRoute r = _inRoutes.begin(); r != _inRoutes.end(); ++r) { if((r->type == Route::TRACK_ROUTE && r->track) || (r->type != Route::TRACK_ROUTE && !r->name().isEmpty())) { xml.tag(level++, "Route"); s = "source"; if(r->type == Route::TRACK_ROUTE) s += QString(" track=\"%1\"/").arg(MusEGlobal::song->tracks()->index(r->track)); else s += QString(" type=\"%1\" name=\"%2\"/").arg(r->type).arg(Xml::xmlString(r->name())); xml.tag(level, s.toLatin1().constData()); xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, Xml::xmlString(name()).toLatin1().constData()); xml.etag(level--, "Route"); } } } for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) { if((r->type == Route::TRACK_ROUTE && r->track) || (r->type != Route::TRACK_ROUTE && !r->name().isEmpty())) { s = "Route"; if(r->channel != -1) s += QString(" channel=\"%1\"").arg(r->channel); xml.tag(level++, s.toLatin1().constData()); xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, Xml::xmlString(name()).toLatin1().constData()); s = "dest"; if(r->type == Route::MIDI_DEVICE_ROUTE) s += QString(" devtype=\"%1\" name=\"%2\"/").arg(r->device->deviceType()).arg(Xml::xmlString(r->name())); else if(r->type == Route::TRACK_ROUTE) s += QString(" track=\"%1\"/").arg(MusEGlobal::song->tracks()->index(r->track)); else s += QString(" type=\"%1\" name=\"%2\"/").arg(r->type).arg(Xml::xmlString(r->name())); xml.tag(level, s.toLatin1().constData()); xml.etag(level--, "Route"); } } } //--------------------------------------------------------- // pbForwardShiftFrames //--------------------------------------------------------- unsigned int MidiJackDevice::pbForwardShiftFrames() const { return MusEGlobal::segmentSize; } //--------------------------------------------------------- // recordEvent //--------------------------------------------------------- void MidiJackDevice::recordEvent(MidiRecordEvent& event) { // Set the loop number which the event came in at. //if(MusEGlobal::audio->isRecording()) if(MusEGlobal::audio->isPlaying()) event.setLoopNum(MusEGlobal::audio->loopCount()); if (MusEGlobal::midiInputTrace) { fprintf(stderr, "MidiIn Jack: <%s>: ", name().toLatin1().constData()); dumpMPEvent(&event); } int typ = event.type(); if(_port != -1) { int idin = MusEGlobal::midiPorts[_port].syncInfo().idIn(); //--------------------------------------------------- // filter some SYSEX events //--------------------------------------------------- if (typ == ME_SYSEX) { const unsigned char* p = event.data(); int n = event.len(); if (n >= 4) { if ((p[0] == 0x7f) //&& ((p[1] == 0x7f) || (p[1] == rxDeviceId))) { && ((p[1] == 0x7f) || (idin == 0x7f) || (p[1] == idin))) { if (p[2] == 0x06) { //mmcInput(p, n); MusEGlobal::midiSyncContainer.mmcInput(_port, p, n); return; } if (p[2] == 0x01) { //mtcInputFull(p, n); MusEGlobal::midiSyncContainer.mtcInputFull(_port, p, n); return; } } else if (p[0] == 0x7e) { //nonRealtimeSystemSysex(p, n); MusEGlobal::midiSyncContainer.nonRealtimeSystemSysex(_port, p, n); return; } } } else // Trigger general activity indicator detector. Sysex has no channel, don't trigger. MusEGlobal::midiPorts[_port].syncInfo().trigActDetect(event.channel()); } // // process midi event input filtering and // transformation // processMidiInputTransformPlugins(event); if (filterEvent(event, MusEGlobal::midiRecordType, false)) return; if (!applyMidiInputTransformation(event)) { if (MusEGlobal::midiInputTrace) printf(" midi input transformation: event filtered\n"); return; } // // transfer noteOn and Off events to gui for step recording and keyboard // remote control (changed by flo93: added noteOff-events) // if (typ == ME_NOTEON) { int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff); MusEGlobal::song->putEvent(pv); } else if (typ == ME_NOTEOFF) { int pv = ((event.dataA() & 0xff)<<8) + (0x00); //send an event with velo=0 MusEGlobal::song->putEvent(pv); } else if (MusEGlobal::rcEnableCC && typ == ME_CONTROLLER) { char cc = static_cast(event.dataA() & 0xff); printf("*** Input CC: %d\n", cc); MusEGlobal::song->putEventCC(cc); } //if(_recordFifo.put(MidiPlayEvent(event))) // printf("MidiJackDevice::recordEvent: fifo overflow\n"); // Do not bother recording if it is NOT actually being used by a port. // Because from this point on, process handles things, by selected port. p3.3.38 if(_port == -1) return; // Split the events up into channel fifos. Special 'channel' number 17 for sysex events. unsigned int ch = (typ == ME_SYSEX)? MusECore::MUSE_MIDI_CHANNELS : event.channel(); if(_recordFifo[ch].put(event)) printf("MidiJackDevice::recordEvent: fifo channel %d overflow\n", ch); } //--------------------------------------------------------- // eventReceived //--------------------------------------------------------- void MidiJackDevice::eventReceived(jack_midi_event_t* ev) { if(ev->size == 0) return; MidiRecordEvent event; event.setB(0); event.setPort(_port); jack_nframes_t abs_ft = 0; // NOTE: From muse_qt4_evolution. Not done here in Muse-2 (yet). // move all events 2*MusEGlobal::segmentSize into the future to get // jitterfree playback // // cycle n-1 n n+1 // -+----------+----------+----------+- // ^ ^ ^ // catch process play // // These Jack events arrived in the previous period, and it may not have been at the audio position before this one (after a seek). // This is how our ALSA driver works, events there are timestamped asynchronous of any process, referenced to the CURRENT audio // position, so that by the time of the NEXT process, THOSE events have also occurred in the previous period. // So, technically this is correct. What MATTERS is how we adjust the times for storage, and/or simultaneous playback in THIS period, // and TEST: we'll need to make sure any non-contiguous previous period is handled correctly by process - will it work OK as is? // If ALSA works OK than this should too... // The events arrived in the previous cycle, not this one. Adjust. abs_ft = MusEGlobal::audio->curSyncFrame() + ev->time; if(abs_ft >= MusEGlobal::segmentSize) abs_ft -= MusEGlobal::segmentSize; event.setTime(abs_ft); event.setTick(MusEGlobal::lastExtMidiSyncTick); event.setChannel(*(ev->buffer) & 0xf); const int type = *(ev->buffer) & 0xf0; event.setType(type); switch(type) { case ME_NOTEON: { if(ev->size < 3) return; // Convert zero-velocity note ons to note offs as per midi spec. if(*(ev->buffer + 2) == 0) event.setType(ME_NOTEOFF); } // Fall through. case ME_NOTEOFF: case ME_CONTROLLER: case ME_POLYAFTER: if(ev->size < 3) return; event.setA(*(ev->buffer + 1) & 0x7f); event.setB(*(ev->buffer + 2) & 0x7f); break; case ME_PROGRAM: case ME_AFTERTOUCH: if(ev->size < 2) return; event.setA(*(ev->buffer + 1) & 0x7f); break; case ME_PITCHBEND: if(ev->size < 3) return; event.setA(( ((*(ev->buffer + 2) & 0x7f) << 7) + (*(ev->buffer + 1) & 0x7f) ) - 8192); break; case ME_SYSEX: { const int type = *(ev->buffer) & 0xff; switch(type) { case ME_SYSEX: #ifdef JACK_MIDI_DEBUG // ---Diagnostics---: fprintf(stderr, "MidiJackDevice::eventReceived SYSEX len:%u data: ", (unsigned int)ev->size); for(unsigned int i = 0; i < ev->size && i < 16; ++i) fprintf(stderr, "%0x ", ((unsigned char*)ev->buffer)[i]); if(ev->size >= 16) fprintf(stderr, "..."); fprintf(stderr, "\n"); #endif // TODO: Deal with large sysex, which are broken up into chunks! // For now, do not accept if the last byte is not EOX, meaning it's a chunk with more chunks to follow. if(*(((unsigned char*)ev->buffer) + ev->size - 1) != ME_SYSEX_END) { fprintf(stderr, "MidiJackDevice::eventReceived sysex chunks not supported!\n"); return; } //event.setTime(0); // mark as used event.setType(ME_SYSEX); event.setData((unsigned char*)(ev->buffer + 1), ev->size - 2); break; case ME_MTC_QUARTER: if(_port != -1) { MusEGlobal::midiSyncContainer.mtcInputQuarter(_port, *(ev->buffer + 1)); } return; case ME_SONGPOS: if(_port != -1) { MusEGlobal::midiSyncContainer.setSongPosition(_port, *(ev->buffer + 1) | (*(ev->buffer + 2) << 7 )); // LSB then MSB } return; //case ME_SONGSEL: //case ME_TUNE_REQ: // We don't use sensing. But suppress warning about this one since it is repetitive. case ME_SENSE: return; case ME_CLOCK: { midiClockInput(abs_ft); return; } case ME_START: { #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MidiJackDevice::eventReceived: START port:%d time:%u\n", _port, abs_ft); #endif } // FALLTHROUGH case ME_TICK: case ME_CONTINUE: case ME_STOP: { MusEGlobal::midiSyncContainer.realtimeSystemInput(_port, type); return; } //case ME_SYSEX_END: //break; // return; default: if(MusEGlobal::debugMsg) printf("MidiJackDevice::eventReceived unsupported system event 0x%02x\n", type); return; } } break; default: if(MusEGlobal::debugMsg) printf("MidiJackDevice::eventReceived unknown event 0x%02x\n", type); return; } #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::eventReceived time:%d type:%d ch:%d A:%d B:%d\n", event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); #endif // Let recordEvent handle it from here, with timestamps, filtering, gui triggering etc. recordEvent(event); } //--------------------------------------------------------- // collectMidiEvents //--------------------------------------------------------- void MidiJackDevice::collectMidiEvents() { if(!_readEnable) return; if(!_in_client_jackport) return; void* port_buf = jack_port_get_buffer(_in_client_jackport, MusEGlobal::segmentSize); jack_midi_event_t event; jack_nframes_t eventCount = jack_midi_get_event_count(port_buf); for (jack_nframes_t i = 0; i < eventCount; ++i) { jack_midi_event_get(&event, port_buf, i); #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::collectMidiEvents number:%d time:%d\n", i, event.time); #endif eventReceived(&event); } } //--------------------------------------------------------- // queueEvent // return true if successful //--------------------------------------------------------- bool MidiJackDevice::queueEvent(const MidiPlayEvent& e, void* evBuffer) { // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). // No big deal if not. Not used for now. //int port = e.port(); if(!_writeEnable || !evBuffer) return false; const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); if(e.time() != 0 && e.time() < syncFrame) fprintf(stderr, "MidiJackDevice::queueEvent() evTime:%u < syncFrame:%u!!\n", e.time(), syncFrame); unsigned int ft = (e.time() < syncFrame) ? 0 : e.time() - syncFrame; if (ft >= MusEGlobal::segmentSize) { fprintf(stderr, "MidiJackDevice::queueEvent: Event time:%d out of range. syncFrame:%d ft:%d (seg=%d)\n", e.time(), syncFrame, ft, MusEGlobal::segmentSize); ft = MusEGlobal::segmentSize - 1; } #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MidiJackDevice::queueEvent pos:%d syncFrame:%d ft:%d time:%d type:%d ch:%d A:%d B:%d\n", pos, syncFrame, ft, e.time(), e.type(), e.channel(), e.dataA(), e.dataB()); #endif if (MusEGlobal::midiOutputTrace) { fprintf(stderr, "MidiOut: Jack: <%s>: ", name().toLatin1().constData()); dumpMPEvent(&e); } switch(e.type()) { case ME_CONTROLLER: case ME_NOTEON: case ME_NOTEOFF: case ME_POLYAFTER: case ME_PITCHBEND: { #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::queueEvent note on/off polyafter controller or pitch\n"); #endif unsigned char* p = jack_midi_event_reserve(evBuffer, ft, 3); if (p == 0) { #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MidiJackDevice::queueEvent NOTE CTL PAT or PB: buffer overflow, stopping until next cycle\n"); #endif return false; } p[0] = e.type() | e.channel(); p[1] = e.dataA(); p[2] = e.dataB(); } break; case ME_PROGRAM: case ME_AFTERTOUCH: { #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::queueEvent program or aftertouch\n"); #endif unsigned char* p = jack_midi_event_reserve(evBuffer, ft, 2); if (p == 0) { #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MidiJackDevice::queueEvent PROG or AT: buffer overflow, stopping until next cycle\n"); #endif return false; } p[0] = e.type() | e.channel(); p[1] = e.dataA(); } break; case ME_SYSEX: { #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::queueEvent sysex\n"); #endif const unsigned char* data = e.constData(); int len = e.len(); unsigned char* p = jack_midi_event_reserve(evBuffer, ft, len+2); if (p == 0) { fprintf(stderr, "MidiJackDevice::queueEvent ME_SYSEX: buffer overflow, sysex too big, event lost\n"); //return false; // Changed to true. Absorb the sysex if it is too big, to avoid attempting // to resend repeatedly. If the sysex is too big, it would just stay in the // list and never be processed, because Jack could never reserve enough space. // Other types of events should be OK since they are small and can be resent // next cycle. p4.0.15 Tim. // FIXME: We really need to chunk sysex events properly. It's tough. Investigating... return true; } p[0] = 0xf0; memcpy(p+1, data, len); p[len+1] = 0xf7; } break; case ME_SONGPOS: { #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::queueEvent songpos %d\n", e.dataA()); #endif unsigned char* p = jack_midi_event_reserve(evBuffer, ft, 3); if (p == 0) { #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MidiJackDevice::queueEvent songpos: buffer overflow, stopping until next cycle\n"); #endif return false; } int pos = e.dataA(); p[0] = e.type(); p[1] = pos & 0x7f; // LSB p[2] = (pos >> 7) & 0x7f; // MSB } break; case ME_CLOCK: case ME_START: case ME_CONTINUE: case ME_STOP: { #ifdef JACK_MIDI_DEBUG printf("MidiJackDevice::queueEvent realtime %x\n", e.type()); #endif unsigned char* p = jack_midi_event_reserve(evBuffer, ft, 1); if (p == 0) { #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MidiJackDevice::queueEvent realtime: buffer overflow, stopping until next cycle\n"); #endif return false; } p[0] = e.type(); } break; default: if(MusEGlobal::debugMsg) printf("MidiJackDevice::queueEvent: event type %x not supported\n", e.type()); return true; // Absorb the event. Don't want it hanging around in the list. } return true; } //--------------------------------------------------------- // processEvent // return true if successful //--------------------------------------------------------- bool MidiJackDevice::processEvent(const MidiPlayEvent& event, void* evBuffer) { int chn = event.channel(); unsigned t = event.time(); int a = event.dataA(); int b = event.dataB(); // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). // No big deal if not. Not used for now. int port = event.port(); #ifdef JACK_MIDI_DEBUG //printf("MidiJackDevice::processEvent time:%d type:%d ch:%d A:%d B:%d\n", t, event.type(), chn, a, b); #endif MidiInstrument::NoteOffMode nom = MidiInstrument::NoteOffAll; // Default to NoteOffAll in case of no port. const int mport = midiPort(); if(mport != -1) { if(MidiInstrument* mi = MusEGlobal::midiPorts[mport].instrument()) nom = mi->noteOffMode(); } // REMOVE Tim. Noteoff. Added. if(event.type() == ME_NOTEON) { if(b == 0) { // Handle zero-velocity note ons. Technically this is an error because internal midi paths // are now all 'note-off' without zero-vel note ons - they're converted to note offs. // Nothing should be setting a Note type Event's on velocity to zero. // But just in case... If we get this warning, it means there is still code to change. fprintf(stderr, "MidiJackDevice::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", t, event.type(), chn, a, b); switch(nom) { // Instrument uses note offs. Convert to zero-vel note off. case MidiInstrument::NoteOffAll: return queueEvent(MidiPlayEvent(t, port, chn, ME_NOTEOFF, a, 0), evBuffer); break; // Instrument uses no note offs at all. Send as-is. case MidiInstrument::NoteOffNone: // Instrument converts all note offs to zero-vel note ons. Send as-is. case MidiInstrument::NoteOffConvertToZVNoteOn: return queueEvent(event, evBuffer); break; } } return queueEvent(event, evBuffer); } else if(event.type() == ME_NOTEOFF) { switch(nom) { // Instrument uses note offs. Send as-is. case MidiInstrument::NoteOffAll: return queueEvent(event, evBuffer); break; // Instrument uses no note offs at all. Send nothing. Eat up the event - return true. case MidiInstrument::NoteOffNone: return true; // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. case MidiInstrument::NoteOffConvertToZVNoteOn: return queueEvent(MidiPlayEvent(t, port, chn, ME_NOTEON, a, 0), evBuffer); break; } return queueEvent(event, evBuffer); } else if(event.type() == ME_PROGRAM) { //_curOutParamNums[chn].resetParamNums(); // Probably best to reset. // don't output program changes for GM drum channel //if (!(MusEGlobal::song->mtype() == MT_GM && chn == 9)) { _curOutParamNums[chn].resetParamNums(); // Probably best to reset. _curOutParamNums[chn].setPROG(a); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PROGRAM, a, 0), evBuffer)) return false; // } } else if(event.type() == ME_PITCHBEND) { int v = a + 8192; //printf("MidiJackDevice::processEvent ME_PITCHBEND v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f), evBuffer)) return false; } else if(event.type() == ME_SYSEX) { resetCurOutParamNums(); // Probably best to reset all. if(!queueEvent(event, evBuffer)) return false; } else if(event.type() == ME_CONTROLLER) { // Perhaps we can find use for this value later, together with the Jack midi MusE port(s). // No big deal if not. Not used for now. //int port = event.port(); if((a | 0xff) == CTRL_POLYAFTER) { //printf("MidiJackDevice::processEvent CTRL_POLYAFTER v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_POLYAFTER, a & 0x7f, b & 0x7f), evBuffer)) return false; } else if(a == CTRL_AFTERTOUCH) { //printf("MidiJackDevice::processEvent CTRL_AFTERTOUCH v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_AFTERTOUCH, b & 0x7f, 0), evBuffer)) return false; } else if(a == CTRL_PITCH) { int v = b + 8192; //printf("MidiJackDevice::processEvent CTRL_PITCH v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f), evBuffer)) return false; } else if (a == CTRL_PROGRAM) { _curOutParamNums[chn].resetParamNums(); // Probably best to reset. // don't output program changes for GM drum channel //if (!(MusEGlobal::song->mtype() == MT_GM && chn == 9)) { int hb = (b >> 16) & 0xff; int lb = (b >> 8) & 0xff; int pr = b & 0xff; //printf("MidiJackDevice::processEvent CTRL_PROGRAM time:%d type:%d ch:%d A:%d B:%d hb:%d lb:%d pr:%d\n", // event.time(), event.type(), event.channel(), event.dataA(), event.dataB(), hb, lb, pr); _curOutParamNums[chn].setCurrentProg(pr, lb, hb); if (hb != 0xff) { if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HBANK, hb), evBuffer)) return false; } if (lb != 0xff) { if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LBANK, lb), evBuffer)) return false; } if (pr != 0xff) { if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PROGRAM, pr, 0), evBuffer)) return false; } // } } else if (a == CTRL_MASTER_VOLUME) { unsigned char sysex[] = { 0x7f, 0x7f, 0x04, 0x01, 0x00, 0x00 }; //sysex[1] = deviceId(); TODO FIXME p4.0.15 Grab the ID from midi port sync info. sysex[4] = b & 0x7f; sysex[5] = (b >> 7) & 0x7f; if(!queueEvent(MidiPlayEvent(t, port, ME_SYSEX, sysex, 6), evBuffer)) return false; } else if (a < CTRL_14_OFFSET) { // 7 Bit Controller if(a == CTRL_HRPN) _curOutParamNums[chn].setRPNH(b); else if(a == CTRL_LRPN) _curOutParamNums[chn].setRPNL(b); else if(a == CTRL_HNRPN) _curOutParamNums[chn].setNRPNH(b); else if(a == CTRL_LNRPN) _curOutParamNums[chn].setNRPNL(b); else if(a == CTRL_HBANK) { _curOutParamNums[chn].setBANKH(b); _curOutParamNums[chn].resetParamNums(); // Probably best to reset. } else if(a == CTRL_LBANK) { _curOutParamNums[chn].setBANKL(b); _curOutParamNums[chn].resetParamNums(); // Probably best to reset. } else if(a == CTRL_RESET_ALL_CTRL) _curOutParamNums[chn].resetParamNums(); // Probably best to reset. //queueEvent(museport, MidiPlayEvent(t, port, chn, event)); if(!queueEvent(event, evBuffer)) return false; } else if (a < CTRL_RPN_OFFSET) { // 14 bit high resolution controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlH, dataH), evBuffer)) return false; if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, ctrlL, dataL), evBuffer)) return false; } else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int data = b & 0x7f; if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setRPNH(ctrlH); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH), evBuffer)) return false; } if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setRPNL(ctrlL); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL), evBuffer)) return false; } if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAH(data); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, data), evBuffer)) return false; } // Select null parameters so that subsequent data controller events do not upset the last *RPN controller. if(MusEGlobal::config.midiSendNullParameters) { _curOutParamNums[chn].setRPNH(0x7f); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, 0x7f), evBuffer)) return false; _curOutParamNums[chn].setRPNL(0x7f); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, 0x7f), evBuffer)) return false; } } else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int data = b & 0x7f; if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setNRPNH(ctrlH); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH), evBuffer)) return false; } if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setNRPNL(ctrlL); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL), evBuffer)) return false; } if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAH(data); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, data), evBuffer)) return false; } if(MusEGlobal::config.midiSendNullParameters) { _curOutParamNums[chn].setNRPNH(0x7f); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, 0x7f), evBuffer)) return false; _curOutParamNums[chn].setNRPNL(0x7f); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, 0x7f), evBuffer)) return false; } } else if (a < CTRL_RPN14_OFFSET) // Unaccounted for internal controller return false; else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setRPNH(ctrlH); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, ctrlH), evBuffer)) return false; } if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setRPNL(ctrlL); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, ctrlL), evBuffer)) return false; } if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAH(dataH); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH), evBuffer)) return false; } if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAL(dataL); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL), evBuffer)) return false; } if(MusEGlobal::config.midiSendNullParameters) { _curOutParamNums[chn].setRPNH(0x7f); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HRPN, 0x7f), evBuffer)) return false; _curOutParamNums[chn].setRPNL(0x7f); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LRPN, 0x7f), evBuffer)) return false; } } else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setNRPNH(ctrlH); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, ctrlH), evBuffer)) return false; } if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setNRPNL(ctrlL); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, ctrlL), evBuffer)) return false; } if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAH(dataH); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HDATA, dataH), evBuffer)) return false; } if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers) { _curOutParamNums[chn].setDATAL(dataL); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LDATA, dataL), evBuffer)) return false; } if(MusEGlobal::config.midiSendNullParameters) { _curOutParamNums[chn].setNRPNH(0x7f); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_HNRPN, 0x7f), evBuffer)) return false; _curOutParamNums[chn].setNRPNL(0x7f); if(!queueEvent(MidiPlayEvent(t, port, chn, ME_CONTROLLER, CTRL_LNRPN, 0x7f), evBuffer)) return false; } } else { if(MusEGlobal::debugMsg) printf("MidiJackDevice::processEvent: unknown controller type 0x%x\n", a); //return false; // Just ignore it. } } else { //queueEvent(MidiPlayEvent(t, port, chn, event)); if(!queueEvent(event, evBuffer)) return false; } return true; } //--------------------------------------------------------- // processMidi // Called from audio thread only. //--------------------------------------------------------- void MidiJackDevice::processMidi(unsigned int curFrame) { void* port_buf = 0; if(_out_client_jackport && _writeEnable) { port_buf = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize); jack_midi_clear_buffer(port_buf); } // Get the state of the stop flag. const bool do_stop = stopFlag(); MidiPlayEvent buf_ev; // Transfer the user lock-free buffer events to the user sorted multi-set. // False = don't use the size snapshot, but update it. const unsigned int usr_buf_sz = eventBuffers(UserBuffer)->getSize(false); for(unsigned int i = 0; i < usr_buf_sz; ++i) { if(eventBuffers(UserBuffer)->get(buf_ev)) _outUserEvents.insert(buf_ev); } // Transfer the playback lock-free buffer events to the playback sorted multi-set. const unsigned int pb_buf_sz = eventBuffers(PlaybackBuffer)->getSize(false); for(unsigned int i = 0; i < pb_buf_sz; ++i) { // Are we stopping? Just remove the item. if(do_stop) eventBuffers(PlaybackBuffer)->remove(); // Otherwise get the item. else if(eventBuffers(PlaybackBuffer)->get(buf_ev)) _outPlaybackEvents.insert(buf_ev); } // Are we stopping? if(do_stop) { // Transport has stopped, purge ALL further scheduled playback events now. _outPlaybackEvents.clear(); // Reset the flag. setStopFlag(false); } iMPEvent impe_pb = _outPlaybackEvents.begin(); iMPEvent impe_us = _outUserEvents.begin(); bool using_pb; while(1) { if(impe_pb != _outPlaybackEvents.end() && impe_us != _outUserEvents.end()) using_pb = *impe_pb < *impe_us; else if(impe_pb != _outPlaybackEvents.end()) using_pb = true; else if(impe_us != _outUserEvents.end()) using_pb = false; else break; const MidiPlayEvent& ev = using_pb ? *impe_pb : *impe_us; if(ev.time() >= (curFrame + MusEGlobal::segmentSize)) { #ifdef JACK_MIDI_DEBUG fprintf(stderr, "MusE: Jack midi: putted event is for future:%lu, breaking loop now\n", ev.time() - curFrame); #endif break; } // If processEvent fails, although we would like to not miss events by keeping them // until next cycle and trying again, that can lead to a large backup of events // over a long time. So we'll just... miss them. processEvent(ev, port_buf); // Successfully processed event. Remove it from FIFO. // C++11. if(using_pb) impe_pb = _outPlaybackEvents.erase(impe_pb); else impe_us = _outUserEvents.erase(impe_us); } } //--------------------------------------------------------- // portLatency // If capture is true get the capture latency, // otherwise get the playback latency. //--------------------------------------------------------- unsigned int MidiJackDevice::portLatency(void* /*port*/, bool capture) const { // jack_latency_range_t c_range; // jack_port_get_latency_range((jack_port_t*)port, JackCaptureLatency, &c_range); // jack_latency_range_t p_range; // jack_port_get_latency_range((jack_port_t*)port, JackPlaybackLatency, &p_range); // TODO FIXME: Tests on both Jack-1 Midi and Jack-2 Midi show the returned values are always zero. // Spent a few days trying to diagnose, it appears Jack does not initialize any ALSA // midi port latency values as it does with the audio ports. Thus right from the start, // right from the backend physical port, the values passed throughout the system and // to the app are always zero! // NOTICE: For at least the ALSA seq driver (tested), the input latency is // always 1 period while the output latency is always 2 periods // regardless of Jack command line -p (period size). // (Also there is the user latency from command line or QJackCtl.) // In other words, the Jack command line -p (number of periods) ONLY applies to audio output ports. //fprintf(stderr, "MidiJackDevice::portLatency port:%p capture:%d c_range.min:%d c_range.max:%d p_range.min:%d p_range.max:%d\n", // port, capture, c_range.min, c_range.max, p_range.min, p_range.max); if(capture) { // jack_latency_range_t c_range; // jack_port_get_latency_range((jack_port_t*)port, JackCaptureLatency, &c_range); // REMOVE Tim. latency. TESTING Reinstate. For simulating non-functional jack midi latency. This seems to work well. // return c_range.max; return MusEGlobal::segmentSize; } else { // jack_latency_range_t p_range; // jack_port_get_latency_range((jack_port_t*)port, JackPlaybackLatency, &p_range); // REMOVE Tim. latency. TESTING Reinstate. For simulating non-functional jack midi latency. This seems to work well. // return p_range.max; return MusEGlobal::segmentSize * 2; } } //--------------------------------------------------------- // selfLatencyMidi //--------------------------------------------------------- float MidiJackDevice::selfLatencyMidi(int channel, bool capture) const { float l = MidiDevice::selfLatencyMidi(channel, capture); //if(!MusEGlobal::checkAudioDevice()) // return l; if(capture) { if(_in_client_jackport) l += portLatency(_in_client_jackport, capture); } else { if(_out_client_jackport) l += portLatency(_out_client_jackport, capture); } return l; } //--------------------------------------------------------- // initMidiJack // return true on error //--------------------------------------------------------- bool initMidiJack() { return false; } } // namespace MusECore muse-4.0.0/src/muse/driver/jackmidi.h000066400000000000000000000075261404105311000174420ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: jackmidi.h,v 1.1.1.1 2010/01/27 09:06:43 terminator356 Exp $ // (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __JACKMIDI_H__ #define __JACKMIDI_H__ #include #include #include #include #include "mididev.h" #include "route.h" #include "mpevent.h" namespace MusECore { // Forward declarations: class Xml; // It appears one client port per remote port will be necessary. // Jack doesn't seem to like manipulation of non-local ports buffers. //#define JACK_MIDI_USE_MULTIPLE_CLIENT_PORTS //--------------------------------------------------------- // MidiJackDevice //--------------------------------------------------------- class MidiJackDevice : public MidiDevice { private: jack_port_t* _in_client_jackport; jack_port_t* _out_client_jackport; MPEventList _outPlaybackEvents; MPEventList _outUserEvents; virtual QString open(); virtual void close(); //bool putEvent(int*); // Return true if successful // evBuffer is the Jack buffer. bool processEvent(const MidiPlayEvent&, void* evBuffer); // Port is not midi port, it is the port(s) created for MusE. // evBuffer is the Jack buffer. bool queueEvent(const MidiPlayEvent&, void* evBuffer); void eventReceived(jack_midi_event_t*); protected: // Returns the number of frames to shift forward output event scheduling times when putting events // into the eventFifos. virtual unsigned int pbForwardShiftFrames() const; public: MidiJackDevice(const QString& name); virtual ~MidiJackDevice(); static MidiDevice* createJackMidiDevice(QString name = "", int rwflags = 3); // 1:Writable 2: Readable 3: Writable + Readable virtual inline MidiDeviceType deviceType() const { return JACK_MIDI; } virtual void setName(const QString&); virtual void processMidi(unsigned int curFrame = 0); virtual void recordEvent(MidiRecordEvent&); virtual void collectMidiEvents(); // The meaning of the returned pointer depends on the driver. // For Jack it returns the address of a Jack port, for ALSA it return the address of a snd_seq_addr_t. inline virtual void* inClientPort() { return (void*) _in_client_jackport; } inline virtual void* outClientPort() { return (void*) _out_client_jackport; } virtual void writeRouting(int, Xml&) const; virtual unsigned int portLatency(void* port, bool capture) const; // The contribution to latency by the device's own members (midi effect rack, Jack ports etc). // A midi device can contain both an input and an output. The 'capture' parameter determines which one. virtual float selfLatencyMidi(int /*channel*/, bool /*capture*/) const; }; extern bool initMidiJack(); } // namespace MusECore #endif muse-4.0.0/src/muse/driver/posixtimer.cpp000066400000000000000000000147361404105311000204260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // posixtimer.cpp // (C) Copyright 2019 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "posixtimer.h" #ifdef POSIX_TIMER_SUPPORT #include "globals.h" #include #include #include #include #include //#include #define POSIX_TIMER_DEBUG 0 namespace MusECore { PosixTimer::PosixTimer() { if(TIMER_DEBUG || POSIX_TIMER_DEBUG) fprintf(stderr,"PosixTimer::PosixTimer(this=%p) called\n",this); timerFd = -1; } PosixTimer::~PosixTimer() { if(TIMER_DEBUG || POSIX_TIMER_DEBUG) fprintf(stderr,"PosixTimer::~PosixTimer(this=%p) called\n",this); if(timerFd != -1) { if(close(timerFd) != 0) fprintf(stderr, "PosixTimer::PosixTimer(): timer close(%d) failed!\n", timerFd); } } signed int PosixTimer::initTimer(unsigned long desiredFrequency) { if(TIMER_DEBUG || POSIX_TIMER_DEBUG) fprintf(stderr, "PosixTimer::initTimer(this=%p)\n",this); if(timerFd != -1) { fprintf(stderr, "PosixTimer::initTimer(): called on initialised timer!\n"); return timerFd; } timerFd = timerfd_create(CLOCK_REALTIME, 0); if(timerFd == -1) { fprintf(stderr, "PosixTimer::initTimer(): timerfd_create failed\n"); return -1; } // if(fcntl(timerFd, F_SETFL, O_NONBLOCK) == -1) // { // fprintf(stderr, "PosixTimer::initTimer(): fcntl(O_NONBLOCK) failed\n"); // if(close(timerFd) == -1) // fprintf(stderr, "PosixTimer::PosixTimer(): timer close(%d) failed!\n", timerFd); // else // timerFd = -1; // return -1; // } if(!setTimerFreq(desiredFrequency)) { // unable to set timer frequency if(close(timerFd) == -1) fprintf(stderr, "PosixTimer::PosixTimer(): timer close(%d) failed!\n", timerFd); else timerFd = -1; return -1; } return timerFd; } unsigned long PosixTimer::setTimerResolution(unsigned long resolution) { if(TIMER_DEBUG || POSIX_TIMER_DEBUG) fprintf(stderr, "PosixTimer::setTimerResolution(%lu)\n",resolution); /* Resolution of a PosixTimer is fixed - it cannot be set */ return 0; } unsigned long PosixTimer::setTimerFreq(unsigned long freq) { if(timerFd == -1 || freq == 0) return 0; if(TIMER_DEBUG || POSIX_TIMER_DEBUG) fprintf(stderr, "PosixTimer::setTimerFreq(this=%p, freq=%ld)\n", this, freq); struct itimerspec new_value; const long int bil = 1000L * 1000000L; long int sec = 1 / freq; long int nsec = (bil / freq) % bil; // Don't start the timer yet. new_value.it_value.tv_sec = 0; new_value.it_value.tv_nsec = 0; new_value.it_interval.tv_sec = sec; new_value.it_interval.tv_nsec = nsec; //if(timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1) if(timerfd_settime(timerFd, 0 /* Relative */, &new_value, NULL) == -1) { fprintf(stderr, "PosixTimer::setTimerFreq(): timerfd_settime failed. sec:%ld nsec:%ld\n", sec, nsec); return 0; } // Return actual frequency. return getTimerFreq(); } unsigned long PosixTimer::getTimerResolution() { /* The PosixTimer doesn't really work with a set resolution as such. * Not sure how this fits into things yet. */ return 0; } unsigned long PosixTimer::getTimerFreq() { if(timerFd == -1) return 0; struct itimerspec curr_value; if(timerfd_gettime(timerFd, &curr_value) == -1) { fprintf(stderr, "PosixTimer::getTimerFreq(): timerfd_gettime(fd:%d) failed\n", timerFd); return 0; } // Avoid divide by zero. if(curr_value.it_interval.tv_sec == 0 && curr_value.it_interval.tv_nsec == 0) { fprintf(stderr, "PosixTimer::getTimerFreq(): timerfd_gettime(fd:%d): zero sec and nsec!\n", timerFd); return 0; } const long int bil = 1000L * 1000000L; const long int nsec = bil * curr_value.it_interval.tv_sec + curr_value.it_interval.tv_nsec; const unsigned long int freq = bil / nsec; return freq; } bool PosixTimer::startTimer() { if(timerFd == -1) return false; struct itimerspec curr_value; if(timerfd_gettime(timerFd, &curr_value) == -1) { fprintf(stderr, "PosixTimer::startTimer(): timerfd_gettime(fd:%d) failed\n", timerFd); return false; } // Start the timer. curr_value.it_value.tv_sec = curr_value.it_interval.tv_sec; curr_value.it_value.tv_nsec = curr_value.it_interval.tv_nsec; if(timerfd_settime(timerFd, 0 /* Relative */, &curr_value, NULL) == -1) { fprintf(stderr, "PosixTimer::startTimer(): timerfd_settime failed. cur interval sec:%ld nsec:%ld\n", curr_value.it_interval.tv_sec, curr_value.it_interval.tv_nsec); return false; } return true; } bool PosixTimer::stopTimer() { if(timerFd == -1) return false; struct itimerspec curr_value; if(timerfd_gettime(timerFd, &curr_value) == -1) { fprintf(stderr, "PosixTimer::stopTimer(): timerfd_gettime(fd:%d) failed\n", timerFd); return false; } // Stop the timer. curr_value.it_value.tv_sec = 0; curr_value.it_value.tv_nsec = 0; if(timerfd_settime(timerFd, 0 /* Relative */, &curr_value, NULL) == -1) { fprintf(stderr, "PosixTimer::stopTimer(): timerfd_settime failed. cur interval sec:%ld nsec:%ld\n", curr_value.it_interval.tv_sec, curr_value.it_interval.tv_nsec); return false; } return true; } unsigned long PosixTimer::getTimerTicks(bool printTicks) { if(timerFd == -1) return 0; uint64_t nn; if (read(timerFd, &nn, sizeof(nn)) != sizeof(nn)) { fprintf(stderr,"PosixTimer::getTimerTicks(): error reading POSIX timer\n"); return 0; } if (printTicks) { fprintf(stderr, "TIMER: ticks = %ld\n", (unsigned long)nn); } return nn; } } // namespace MusECore #endif // POSIX_TIMER_SUPPORT muse-4.0.0/src/muse/driver/posixtimer.h000066400000000000000000000036471404105311000200720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // posixtimer.h // (C) Copyright 2019 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __POSIXTIMER_H__ #define __POSIXTIMER_H__ #include "config.h" #ifdef POSIX_TIMER_SUPPORT #include "timerdev.h" namespace MusECore { //--------------------------------------------------------- // PosixTimer //--------------------------------------------------------- class PosixTimer : public Timer{ int timerFd; public: PosixTimer(); virtual ~PosixTimer(); virtual const char * getTimerName() { return "PosixTimer"; } virtual signed int initTimer(unsigned long desiredFrequency); virtual unsigned long setTimerResolution(unsigned long resolution); virtual unsigned long getTimerResolution(); virtual unsigned long setTimerFreq(unsigned long freq); virtual unsigned long getTimerFreq(); virtual bool startTimer(); virtual bool stopTimer(); virtual unsigned long getTimerTicks(bool printTicks=false); }; } // namespace MusECore #endif // POSIX_TIMER_SUPPORT #endif //__POSIXTIMER_H__ muse-4.0.0/src/muse/driver/qttimer.cpp000066400000000000000000000100311404105311000176700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // (C) Copyright 2015 Robert Jonsson (spamatica@gmail.com) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "qttimer.h" #ifndef TIMER_DEBUG #define TIMER_DEBUG 1 #endif #ifdef _WIN32 #define pipe(fds) _pipe(fds, 4096, _O_BINARY) #endif namespace MusECore { QtTimer::QtTimer() { if(TIMER_DEBUG) fprintf(stderr,"QtTimer::QtTimer(this=%p) called\n",this); innerTimer = NULL; timeoutms = 10; readPipe=-1; writePipe=-1; } QtTimer::~QtTimer() { if(TIMER_DEBUG) fprintf(stderr,"QtTimer::~QtTimer(this=%p) called\n",this); exit(); // thread exit } signed int QtTimer::initTimer(unsigned long) { if(TIMER_DEBUG) printf("QtTimer::initTimer(this=%p)\n",this); int filedes[2]; // 0 - reading 1 - writing if (pipe(filedes) == -1) { perror("QtTimer - creating pipe failed"); exit(-1); } #ifndef _WIN32 int rv = fcntl(filedes[1], F_SETFL, O_NONBLOCK); if (rv == -1) perror("set pipe O_NONBLOCK"); #endif if (pipe(filedes) == -1) { perror("QtTimer - creating pipe1"); exit(-1); } writePipe = filedes[1]; readPipe = filedes[0]; return filedes[0]; } long unsigned int QtTimer::setTimerResolution(unsigned long) { return 0; } long unsigned int QtTimer::setTimerFreq(unsigned long freq) { if (freq > 1000) freq = 1000; if (freq < 100) freq = 100; timeoutms = 1000/freq; return 1000/timeoutms; } long unsigned int QtTimer::getTimerResolution() { return 20; } long unsigned int QtTimer::getTimerFreq() { return 1000/timeoutms; } bool QtTimer::startTimer() { QThread::start(); return true; } bool QtTimer::stopTimer() { QThread::quit(); return true; } unsigned long int QtTimer::getTimerTicks(bool /*printTicks*/) { if(TIMER_DEBUG) printf("getTimerTicks()\n"); unsigned long int nn; if (readPipe==-1) { fprintf(stderr,"QtTimer::getTimerTicks(): no pipe open to read!\n"); return 0; } if (read(readPipe, &nn, sizeof(char)) != sizeof(char)) { fprintf(stderr,"QtTimer::getTimerTicks(): error reading pipe\n"); return 0; } //return nn; return innerTimer != 0 ? innerTimer->getTick() : 0; } void QtTimer::run() { //bool keepRunning = true; innerTimer = new InnerTimer(); innerTimer->setupTimer(writePipe, timeoutms); // make sure it is running in the right thread exec(); } void InnerTimer::setupTimer(int fd, int timeoutms) { tickCount=0; writePipe = fd; timer.start(timeoutms, this); printf("InnerTimer::setupTimer() started\n"); } InnerTimer::~InnerTimer() { timer.stop(); } void InnerTimer::timerEvent(QTimerEvent *event) { //if (tickCount%1000) //printf("InnerTimer::timerEvent %ld ++++++++++++++++++++++\n",tickCount); if (event->timerId() == timer.timerId()) { tickCount++; write(writePipe,"t",1); } } long int InnerTimer::getTick() { return tickCount; } } // namespace MusECore muse-4.0.0/src/muse/driver/qttimer.h000066400000000000000000000045731404105311000173530ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // (C) Copyright 2015 Robert Jonsson (spamatica@gmail.com) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __QTTIMER_H__ #define __QTTIMER_H__ #include #include #include #include #include "timerdev.h" namespace MusECore { //--------------------------------------------------------- // QtTimer //--------------------------------------------------------- class InnerTimer : public QObject { Q_OBJECT int writePipe; long int tickCount; QBasicTimer timer; public: void setupTimer(int fd, int timeoutms); ~InnerTimer(); long int getTick(); bool isRunning() { return timer.isActive(); } protected: void timerEvent(QTimerEvent *event); }; class QtTimer : public Timer, public QThread { int writePipe; int readPipe; bool keepRunning; InnerTimer *innerTimer; int timeoutms; public: QtTimer(); virtual ~QtTimer(); virtual const char * getTimerName() { return "QtTimer"; } virtual signed int initTimer(unsigned long init); virtual long unsigned int setTimerResolution(unsigned long resolution); virtual long unsigned int getTimerResolution(); virtual long unsigned int setTimerFreq(unsigned long freq); virtual long unsigned int getTimerFreq(); virtual bool startTimer(); virtual bool stopTimer(); virtual long unsigned int getTimerTicks(bool printTicks=false); void setFindBestTimer(bool ) { } private: virtual void run(); }; } // namespace MusECore #endif //__QTTIMER_H__ muse-4.0.0/src/muse/driver/rtaudio.cpp000066400000000000000000000250411404105311000176610ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // 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; 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; 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 #include #include #include #include "muse_math.h" #include "config.h" #include "audio.h" #include "audiodev.h" #include "globals.h" #include "song.h" // #include "driver/alsatimer.h" #include "pos.h" #include "gconfig.h" //#include "utils.h" #include "al/al.h" #include "rtaudio.h" #define MASTER_LEFT (void*)1 #define MASTER_RIGHT (void*)2 namespace MusECore { RtAudioDevice* rtAudioDevice = 0; RtAudioDevice::RtAudioDevice(bool forceDefault) : AudioDevice() { fprintf(stderr, "Init RtAudioDevice\n"); MusEGlobal::sampleRate = MusEGlobal::config.deviceAudioSampleRate; // Make sure the AL namespace variables mirror our variables. AL::sampleRate = MusEGlobal::sampleRate; MusEGlobal::segmentSize = MusEGlobal::config.deviceAudioBufSize; _start_timeUS = systemTimeUS(); _criticalVariablesIdx = 0; for(unsigned x = 0; x < 2; ++x) { _timeUSAtCycleStart[x] = 0; _framesAtCycleStart[x] = 0; _frameCounter[x] = 0; } RtAudio::Api api = RtAudio::UNSPECIFIED; switch (MusEGlobal::config.deviceAudioBackend) { case MusEGlobal::RtAudioChoice: api = RtAudio::UNSPECIFIED; break; case MusEGlobal::RtAudioAlsa: api = RtAudio::LINUX_ALSA; break; case MusEGlobal::RtAudioPulse: api = RtAudio::LINUX_PULSE; break; case MusEGlobal::RtAudioOss: api = RtAudio::LINUX_OSS; break; //case MusEGlobal::RtAudioJack: // api = RtAudio::UNIX_JACK; //break; default: fprintf(stderr, "Error: RtAudio device selection illegal, setting up dummy audio backend!\n"); api = RtAudio::RTAUDIO_DUMMY; } if (forceDefault) { api = RtAudio::LINUX_PULSE; } dac = new RtAudio(api); if ( dac->getDeviceCount() < 1 ) { fprintf (stderr, "\nNo audio devices found!\n"); QMessageBox::warning(NULL,"No sound device.","RtAudio did not find any audio device - run muse in midi-only mode if there is audio capable device.", QMessageBox::Ok); } } //--------------------------------------------------------- // exitRtAudio //--------------------------------------------------------- void exitRtAudio() { if(rtAudioDevice) delete rtAudioDevice; rtAudioDevice = NULL; MusEGlobal::audioDevice = NULL; } //--------------------------------------------------------- // initRtAudio //--------------------------------------------------------- bool initRtAudio(bool forceDefault = false) { rtAudioDevice = new RtAudioDevice(forceDefault); MusEGlobal::audioDevice = rtAudioDevice; return false; } //--------------------------------------------------------- // processAudio //--------------------------------------------------------- int processAudio( void * outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double /*streamTime*/, RtAudioStreamStatus /* status */, void * /* userData */ ) { rtAudioDevice->setCriticalVariables(nBufferFrames); if(MusEGlobal::audio->isRunning()) { // Use our built-in transport, which INCLUDES the necessary // calls to Audio::sync() and ultimately Audio::process(), // and increments the built-in play position. rtAudioDevice->processTransport(nBufferFrames); } float *floatOutputBuffer = (float*)outputBuffer; float *floatInputBuffer = (float*)inputBuffer; if (rtAudioDevice->outputPortsList.size() >= 2) { MuseRtAudioPort *left = rtAudioDevice->outputPortsList.at(0); MuseRtAudioPort *right= rtAudioDevice->outputPortsList.at(1); // copy buffers into output for (unsigned int i = 0; i < nBufferFrames; i++ ) { floatOutputBuffer[i*2] = left->buffer[i]; floatOutputBuffer[i*2+1] = right->buffer[i]; } } else { //fprintf(stderr, "Too few ports in list, won't copy any data\n"); } if (rtAudioDevice->inputPortsList.size() >= 1) { MuseRtAudioPort *left = rtAudioDevice->inputPortsList.at(0); MuseRtAudioPort *right = NULL; if (rtAudioDevice->inputPortsList.size() >= 2) { right= rtAudioDevice->inputPortsList.at(1); } // if (left->buffer[0] > 0.001) { // fprintf(stderr, "Got non zero buffer value %f\n", left->buffer[0]); // } // copy buffers into input for (unsigned int i = 0; i < nBufferFrames; i++ ) { left->buffer[i] = floatInputBuffer[i*2]; if (right != NULL) { right->buffer[i] = floatInputBuffer[i*2+1]; } } } else { //fprintf(stderr, "Too few ports in list, won't copy any data\n"); } return 0; } //--------------------------------------------------------- // start //--------------------------------------------------------- bool RtAudioDevice::start(int priority) { if (dac->isStreamRunning()) { stop(); } RtAudio::StreamOptions options; options.flags = RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_SCHEDULE_REALTIME; options.numberOfBuffers = 2; options.priority = priority; options.streamName = "MusE"; DEBUG_RTAUDIO(stderr, "RtAudioDevice:start desired options: flags:%d numberOfBuffers:%d priority:%d streamName:%s\n", options.flags, options.numberOfBuffers, options.priority, options.streamName.c_str()); RtAudio::StreamParameters outParameters; outParameters.deviceId = dac->getDefaultOutputDevice(); outParameters.nChannels = 2; outParameters.firstChannel = 0; RtAudio::StreamParameters inParameters; inParameters.deviceId = dac->getDefaultInputDevice(); inParameters.nChannels = 2; inParameters.firstChannel = 0; unsigned int fin_sr = MusEGlobal::sampleRate; RtAudio::DeviceInfo in_di = dac->getDeviceInfo(inParameters.deviceId); RtAudio::DeviceInfo out_di = dac->getDeviceInfo(outParameters.deviceId); if(!in_di.probed || !out_di.probed) { fprintf(stderr, "Error: RtAudioDevice: Could not probe device info.\n"); } else { typedef std::vector::iterator t_isr; std::set sr_set; typedef std::set::iterator t_isr_set; if(in_di.sampleRates.empty()) { for(t_isr isr_o = out_di.sampleRates.begin(); isr_o != out_di.sampleRates.end(); ++isr_o) sr_set.insert(*isr_o); } else { if(out_di.sampleRates.empty()) { for(t_isr isr_i = in_di.sampleRates.begin(); isr_i != in_di.sampleRates.end(); ++isr_i) sr_set.insert(*isr_i); } else { std::vector out_sr_tmp = out_di.sampleRates; for(t_isr isr_i = in_di.sampleRates.begin(); isr_i != in_di.sampleRates.end(); ++isr_i) { for(t_isr isr_o = out_sr_tmp.begin(); isr_o != out_sr_tmp.end(); ++isr_o) { // Since we currently just use one openStream for both input and output, // and openStream takes only one samplerate value, then we can only openStream // if the samplerate is supported for both input and output... I guess? if(*isr_o == *isr_i) { sr_set.insert(*isr_i); // Done with this output samplerate. Remove it to speed up as we go. out_sr_tmp.erase(isr_o); break; } } } } } if(sr_set.find(fin_sr) == sr_set.end()) { unsigned int near_low = 0; unsigned int near_high = 0; unsigned int sr; for(t_isr_set isr = sr_set.begin(); isr != sr_set.end(); ++isr) { sr = *isr; // Include the desired samplerate. if(sr > fin_sr) continue; if(sr > near_low) near_low = sr; } for(t_isr_set isr = sr_set.begin(); isr != sr_set.end(); ++isr) { sr = *isr; // Include the desired samplerate. if(sr < fin_sr) continue; if(near_high == 0 || sr < near_high) near_high = sr; } // Prefer the closest lower rate rather than highest to be safe, I suppose. if(near_low == 0 && near_high == 0) { fprintf(stderr, "Error: RtAudioDevice: Unsupported samplerate for both in/out:%d. No other samplerates found! Trying 44100 anyway...\n", MusEGlobal::sampleRate); fin_sr = 44100; } else { if(near_low == 0) fin_sr = near_high; else fin_sr = near_low; fprintf(stderr, "Warning: RtAudioDevice: Unsupported samplerate for both in/out:%d. Using closest:%d\n", MusEGlobal::sampleRate, fin_sr); } } } MusEGlobal::sampleRate = fin_sr; // Make sure the AL namespace variables mirror our variables. AL::sampleRate = MusEGlobal::sampleRate; double data[2]; try { dac->openStream( &outParameters, &inParameters, RTAUDIO_FLOAT32, MusEGlobal::sampleRate, &MusEGlobal::segmentSize, &processAudio, (void *)&data, &options ); dac->startStream(); } catch ( RtAudioError& e ) { e.printMessage(); fprintf(stderr, "Error: RtAudioDevice: Cannot open device for streaming!.\n"); return false; } DEBUG_RTAUDIO(stderr, "RtAudioDevice:start actual options: flags:%d numberOfBuffers:%d priority:%d streamName:%s\n", options.flags, options.numberOfBuffers, options.priority, options.streamName.c_str()); return true; } void RtAudioDevice::stop () { try { if (dac->isStreamRunning()) { dac->stopStream(); } } catch (RtAudioError& e) { e.printMessage(); } if ( dac->isStreamOpen() ) { dac->closeStream(); } } } // namespace MusECore muse-4.0.0/src/muse/driver/rtaudio.h000066400000000000000000000257311404105311000173340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "audiodev.h" #include "large_int.h" #include // For debugging output: Uncomment the fprintf section. #define DEBUG_RTAUDIO(dev, format, args...) // fprintf(dev, format, ##args); namespace MusECore { //--------------------------------------------------------- // RtAudioDevice //--------------------------------------------------------- struct MuseRtAudioPort { QString name; float* buffer; }; class RtAudioDevice : public AudioDevice { private: RtAudio *dac; // Critical variables that need to all update at once. // We employ a 'flipping' technique. unsigned _framesAtCycleStart[2]; uint64_t _timeUSAtCycleStart[2]; unsigned _frameCounter[2]; unsigned _criticalVariablesIdx; public: // Time in microseconds at which the driver was created. uint64_t _start_timeUS; QList outputPortsList; QList inputPortsList; virtual const char* driverName() const { return "RtAudioDevice"; } virtual QString driverBackendName() { auto api = dac->getCurrentApi(); switch (api) { case RtAudio::RTAUDIO_DUMMY: return "RTAUDIO_DUMMY"; break; case RtAudio::UNSPECIFIED: return "UNSPECIFIED"; break; case RtAudio::LINUX_ALSA: return "LINUX_ALSA"; break; case RtAudio::LINUX_PULSE: return "LINUX_PULSE"; break; case RtAudio::LINUX_OSS: return "LINUX_OSS"; break; case RtAudio::UNIX_JACK: return "UNIX_JACK"; break; case RtAudio::MACOSX_CORE: return "MACOSX_CORE"; break; case RtAudio::WINDOWS_WASAPI: return "WINDOWS_WASAPI"; break; case RtAudio::WINDOWS_ASIO: return "WINDOWS_ASIO"; break; case RtAudio::WINDOWS_DS: return "WINDOWS_DS"; break; default: return "UNKNOWN"; } } // For callback usage only. void setCriticalVariables(unsigned segmentSize) { static bool _firstTime = true; const unsigned idx = (_criticalVariablesIdx + 1) % 2; _timeUSAtCycleStart[idx] = systemTimeUS(); // Let these start at zero and only increment on subsequent callbacks. if(!_firstTime) { _framesAtCycleStart[idx] = _framesAtCycleStart[_criticalVariablesIdx] + segmentSize; _frameCounter[idx] = _frameCounter[_criticalVariablesIdx] + segmentSize; } _firstTime = false; // Now 'flip' the variables all at once. _criticalVariablesIdx = idx; } RtAudioDevice(bool forceDefault); virtual ~RtAudioDevice() { while (outputPortsList.size() > 0) { MuseRtAudioPort *port = outputPortsList.takeFirst(); free (port->buffer); free (port); } while (inputPortsList.size() > 0) { MuseRtAudioPort *port = inputPortsList.takeFirst(); free (port->buffer); free (port); } } virtual inline int deviceType() const { return RTAUDIO_AUDIO; } virtual bool start(int); virtual void stop (); virtual unsigned framePos() const { // Not much choice but to do this: const unsigned int facs = framesAtCycleStart(); const unsigned int fscs = framesSinceCycleStart(); DEBUG_RTAUDIO(stderr, "RtAudioDevice::framePos framesAtCycleStart:%u framesSinceCycleStart:%u\n", facs, fscs); return facs + fscs; } // These are meant to be called from inside process thread only. virtual unsigned framesAtCycleStart() const { return _framesAtCycleStart[_criticalVariablesIdx]; } virtual unsigned framesSinceCycleStart() const { const uint64_t ct = systemTimeUS(); DEBUG_RTAUDIO(stderr, "RtAudioDevice::framesSinceCycleStart systemTimeUS:%lu timeUSAtCycleStart:%lu\n", ct, _timeUSAtCycleStart[_criticalVariablesIdx]); // Do not round up here since time resolution is higher than (audio) frame resolution. unsigned f = muse_multiply_64_div_64_to_64(ct - _timeUSAtCycleStart[_criticalVariablesIdx], MusEGlobal::sampleRate, 1000000UL); // Safety due to inaccuracies. It cannot be after the segment, right? if(f >= MusEGlobal::segmentSize) f = MusEGlobal::segmentSize - 1; return f; } virtual float* getBuffer(void* port, unsigned long nframes) { if (nframes > MusEGlobal::segmentSize) { fprintf(stderr, "RtAudioDevice::getBuffer nframes > segment size\n"); exit(-1); } return ((MuseRtAudioPort*)port)->buffer; } virtual std::list outputPorts(bool, int) { std::list outlist; foreach (MuseRtAudioPort *port, outputPortsList) { outlist.push_back(port->name); } return outlist; } virtual std::list inputPorts(bool, int) { std::list inlist; foreach (MuseRtAudioPort *port, inputPortsList) { inlist.push_back(port->name); } return inlist; } virtual void registerClient() {} virtual const char* clientName() { return "RtAudio"; } virtual void* registerOutPort(const char* name, bool) { fprintf(stderr, "register output port [%s] length %d char %c\n", name, int(strlen(name)), name[strlen(name)-1] ); foreach (MuseRtAudioPort *port, outputPortsList) { if (port->name == name) { fprintf(stderr, "RtAudioDevice::registerOutPort - port [%s] already exists, return existing.", name); return port; } } MuseRtAudioPort *port = new MuseRtAudioPort(); port->name = name; port->buffer = new float[MusEGlobal::segmentSize]; memset(port->buffer, 0, MusEGlobal::segmentSize * sizeof(float)); outputPortsList.push_back(port); return port; } virtual void* registerInPort(const char* name, bool) { fprintf(stderr, "register input port [%s] length %d char %c\n", name, int(strlen(name)), name[strlen(name)-1] ); foreach (MuseRtAudioPort *port, inputPortsList) { if (port->name == name) { fprintf(stderr, "RtAudioDevice::registerInPort - port [%s] already exists, return existing.", name); return port; } } MuseRtAudioPort *port = new MuseRtAudioPort(); port->name = name; port->buffer = new float[MusEGlobal::segmentSize]; memset(port->buffer, 0, MusEGlobal::segmentSize * sizeof(float)); inputPortsList.push_back(port); return port; } virtual AudioDevice::PortType portType(void*) const { return AudioPort; } virtual AudioDevice::PortDirection portDirection(void*) const { return OutputPort; } virtual void unregisterPort(void*) {} virtual bool connect(void* /*src*/, void* /*dst*/) { DEBUG_RTAUDIO(stderr, "RtAudio::connect\n"); return false; } virtual bool connect(const char* /*src*/, const char* /*dst*/) { DEBUG_RTAUDIO(stderr, "RtAudio::connect2\n"); return false; } virtual bool disconnect(void* /*src*/, void* /*dst*/) { return false; } virtual bool disconnect(const char* /*src*/, const char* /*dst*/) { return false; } virtual int connections(void* /* clientPort */) { DEBUG_RTAUDIO(stderr, "RtAudio::connections\n"); return 1; // always return nonzero, for now } virtual bool portConnectedTo(void*, const char*) { DEBUG_RTAUDIO(stderr, "RtAudio::portConnectedTo\n"); return false; } virtual bool portsCanDisconnect(void* /*src*/, void* /*dst*/) const { DEBUG_RTAUDIO(stderr, "RtAudio::portCanDisconnect\n"); return false; } virtual bool portsCanDisconnect(const char* /*src*/, const char* /*dst*/) const { DEBUG_RTAUDIO(stderr, "RtAudio::portCanDisconnect2\n"); return false; } virtual bool portsCanConnect(void* /*src*/, void* /*dst*/) const { DEBUG_RTAUDIO(stderr, "RtAudio::portCanConnect\n"); return false; } virtual bool portsCanConnect(const char* /*src*/, const char* /*dst*/) const { DEBUG_RTAUDIO(stderr, "RtAudio::portCanConnect\n"); return false; } virtual bool portsCompatible(void* /*src*/, void* /*dst*/) const { DEBUG_RTAUDIO(stderr, "RtAudio::portCompatible\n"); return false; } virtual bool portsCompatible(const char* /*src*/, const char* /*dst*/) const { DEBUG_RTAUDIO(stderr, "RtAudio::portCompatible2\n"); return false; } virtual void setPortName(void*, const char*) { DEBUG_RTAUDIO(stderr, "RtAudio::setPortName\n"); } virtual void* findPort(const char*) { DEBUG_RTAUDIO(stderr, "RtAudio::findPort\n"); return 0; } // preferred_name_or_alias: -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. virtual char* portName(void*, char* str, int str_size, int /*preferred_name_or_alias*/ = -1) { DEBUG_RTAUDIO(stderr, "RtAudio::portName %s\n", str); if(str_size == 0) { return 0; } str[0] = '\0'; return str; } virtual const char* canonicalPortName(void*) { DEBUG_RTAUDIO(stderr, "RtAudio::canonicalPortName\n"); return 0; } virtual unsigned int portLatency(void* /*port*/, bool /*capture*/) const { DEBUG_RTAUDIO(stderr, "RtAudio::portLatency\n"); return 0; } virtual unsigned frameTime() const { return _frameCounter[_criticalVariablesIdx]; } virtual bool isRealtime() { return MusEGlobal::realTimeScheduling; } virtual int realtimePriority() const { return 40; } virtual void setFreewheel(bool) {} virtual int setMaster(bool, bool /*unconditional*/ = false) { return 1; } }; } // namespace MusECore muse-4.0.0/src/muse/driver/rtctimer.cpp000066400000000000000000000124121404105311000200410ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: rtctimer.cpp,v 1.1.2.11 2009/03/09 02:05:18 terminator356 Exp $ // // Most code moved from midiseq.cpp by Werner Schweer. // // (C) Copyright -2004 Werner Schweer (werner@seh.de) // (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "rtctimer.h" #ifdef ALSA_SUPPORT #include #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) #include #include #else #include #include #endif #include #include #include #include #include "globals.h" namespace MusECore { RtcTimer::RtcTimer() { timerFd = -1; } RtcTimer::~RtcTimer() { if (timerFd != -1) close(timerFd); } signed int RtcTimer::initTimer(unsigned long desiredFrequency) { if(TIMER_DEBUG) printf("RtcTimer::initTimer()\n"); if (timerFd != -1) { fprintf(stderr,"RtcTimer::initTimer(): called on initialised timer!\n"); return -1; } MusEGlobal::doSetuid(); #ifdef _WIN32 QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(showGPS())); timer->start(15000); //time specified in ms //https://stackoverflow.com/questions/19287550/how-to-call-a-function-after-every-15-seconds-using-qt //http://doc.qt.io/qt-5/qtimer.html#details //http://doc.qt.io/qt-5/qt.html#TimerType-enum #else timerFd = ::open("/dev/rtc", O_RDONLY); if (timerFd == -1) { fprintf(stderr, "fatal error: open /dev/rtc failed: %s\n", strerror(errno)); MusEGlobal::undoSetuid(); return timerFd; } if (!setTimerFreq(desiredFrequency)) { // unable to set timer frequency return -1; } // check if timer really works, start and stop it once. if (!startTimer()) { return -1; } if (!stopTimer()) { return -1; } return timerFd; #endif } unsigned long RtcTimer::setTimerResolution(unsigned long resolution) { if(TIMER_DEBUG) printf("RtcTimer::setTimerResolution(%lu)\n",resolution); /* The RTC can take power-of-two frequencies from 2 to 8196 Hz. * It doesn't really have a resolution as such. */ return 0; } unsigned long RtcTimer::setTimerFreq(unsigned long freq) { #ifdef _WIN32 // int rc = _getch(timerFd, ); #else int rc = ioctl(timerFd, RTC_IRQP_SET, freq); //RTC_IRQP_SET = set periodic interrupt frequency #endif if (rc == -1) { fprintf(stderr, "RtcTimer::setTimerFreq(): cannot set freq %lu on /dev/rtc: %s\n", freq, strerror(errno)); fprintf(stderr, " precise timer not available, check file permissions and allowed RTC freq (/sys/class/rtc/rtc0/max_user_freq)\n"); return 0; } return freq; } unsigned long RtcTimer::getTimerResolution() { /* The RTC doesn't really work with a set resolution as such. * Not sure how this fits into things yet. */ return 0; } unsigned long RtcTimer::getTimerFreq() { unsigned long freq; int rv = ioctl(timerFd, RTC_IRQP_READ, &freq); if (rv < 0) return 0; return freq; } bool RtcTimer::startTimer() { if(TIMER_DEBUG) printf("RtcTimer::startTimer()\n"); if (timerFd == -1) { fprintf(stderr, "RtcTimer::startTimer(): no timer open to start!\n"); return false; } if (ioctl(timerFd, RTC_PIE_ON, 0) == -1) { perror("MidiThread: start: RTC_PIE_ON failed"); MusEGlobal::undoSetuid(); return false; } return true; } bool RtcTimer::stopTimer() { if(TIMER_DEBUG) printf("RtcTimer::stopTimer\n"); if (timerFd != -1) { ioctl(timerFd, RTC_PIE_OFF, 0); } else { fprintf(stderr,"RtcTimer::stopTimer(): no RTC to stop!\n"); return false; } return true; } unsigned long RtcTimer::getTimerTicks(bool /*printTicks*/)// prevent compiler warning: unused parameter { if(TIMER_DEBUG) printf("getTimerTicks()\n"); unsigned long int nn; if (timerFd==-1) { fprintf(stderr,"RtcTimer::getTimerTicks(): no RTC open to read!\n"); return 0; } if (read(timerFd, &nn, sizeof(unsigned long)) != sizeof(unsigned long)) { fprintf(stderr,"RtcTimer::getTimerTicks(): error reading RTC\n"); return 0; } return nn; } } // namespace MusECore #endif // ALSA_SUPPORT muse-4.0.0/src/muse/driver/rtctimer.h000066400000000000000000000040531404105311000175100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: rtctimer.h,v 1.1.2.3 2005/08/21 18:11:28 spamatica Exp $ // // Most code moved from midiseq.cpp // // (C) Copyright -2004 Werner Schweer (werner@seh.de) // (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __RTCTIMER_H__ #define __RTCTIMER_H__ #include "config.h" #ifdef ALSA_SUPPORT #include "timerdev.h" namespace MusECore { //--------------------------------------------------------- // AlsaTimer //--------------------------------------------------------- class RtcTimer : public Timer{ public: RtcTimer(); virtual ~RtcTimer(); virtual const char * getTimerName() { return "RtcTimer"; } virtual signed int initTimer(unsigned long desiredFrequency); virtual unsigned long setTimerResolution(unsigned long resolution); virtual unsigned long getTimerResolution(); virtual unsigned long setTimerFreq(unsigned long tick); virtual unsigned long getTimerFreq(); virtual bool startTimer(); virtual bool stopTimer(); virtual unsigned long getTimerTicks(bool printTicks=false); private: int timerFd; }; } // namespace MusECore #endif // ALSA_SUPPORT #endif //__RTCTIMER_H__ muse-4.0.0/src/muse/driver/simpletimer.cpp000066400000000000000000000047041404105311000205470ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // (C) Copyright 2015 Robert Jonsson (spamatica@gmail.com) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "simpletimer.h" #ifndef TIMER_DEBUG #define TIMER_DEBUG 1 #endif namespace MusECore { SimpleTimer::SimpleTimer() { if(TIMER_DEBUG) fprintf(stderr,"SimpleTimer::SimpleTimer(this=%p) called\n",this); } SimpleTimer::~SimpleTimer() { if(TIMER_DEBUG) fprintf(stderr,"SimpleTimer::~SimpleTimer(this=%p) called\n",this); } signed int SimpleTimer::initTimer(unsigned long) { if(TIMER_DEBUG) printf("SimpleTimer::initTimer(this=%p)\n",this); return 12345; // we return a dummy file descriptor for simplicity } // current usage is only dummy timer which does not use the file descriptor unsigned long SimpleTimer::setTimerResolution(unsigned long) { return 0; } unsigned long SimpleTimer::setTimerFreq(unsigned long) { return 1000; } unsigned long SimpleTimer::getTimerResolution() { return 20; } unsigned long SimpleTimer::getTimerFreq() { return 1000; } bool SimpleTimer::startTimer() { QThread::start(); return true; } bool SimpleTimer::stopTimer() { QThread::quit(); return true; } unsigned long SimpleTimer::getTimerTicks(bool /*printTicks*/) { return tickCount; } void SimpleTimer::run() { bool keepRunning = true; while(keepRunning) { if (TIMER_DEBUG) printf("timer tick %ld\n",tickCount); usleep(1000); tickCount++; } } } // namespace MusECore muse-4.0.0/src/muse/driver/simpletimer.h000066400000000000000000000037331404105311000202150ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // (C) Copyright 2015 Robert Jonsson (spamatica@gmail.com) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SIMPLETIMER_H__ #define __SIMPLETIMER_H__ #include "timerdev.h" #include namespace MusECore { //--------------------------------------------------------- // AlsaTimer //--------------------------------------------------------- class SimpleTimer : public Timer, public QThread { unsigned long tickCount; bool keepRunning; public: SimpleTimer(); virtual ~SimpleTimer(); virtual const char * getTimerName() { return "SimpleTimer"; } virtual signed int initTimer(unsigned long init); virtual unsigned long setTimerResolution(unsigned long resolution); virtual unsigned long getTimerResolution(); virtual unsigned long setTimerFreq(unsigned long freq); virtual unsigned long getTimerFreq(); virtual bool startTimer(); virtual bool stopTimer(); virtual unsigned long getTimerTicks(bool printTicks=false); void setFindBestTimer(bool ) { } private: virtual void run(); }; } // namespace MusECore #endif //__SIMPLETIMER_H__ muse-4.0.0/src/muse/driver/timerdev.h000066400000000000000000000036571404105311000175070ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: timerdev.h,v 1.1.2.3 2005/08/21 18:11:28 spamatica Exp $ // // Plenty of code borrowed from timer.c example in // alsalib 1.0.7 // // (C) Copyright 2004 Robert Jonsson (rj@spamatica.se) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TIMERDEV_H__ #define __TIMERDEV_H__ #define TIMER_DEBUG 0 namespace MusECore { //--------------------------------------------------------- // AlsaTimer //--------------------------------------------------------- class Timer { public: Timer() {}; virtual ~Timer() {}; virtual const char * getTimerName() = 0; virtual signed int initTimer(unsigned long desiredFrequency) = 0; virtual unsigned long setTimerResolution(unsigned long resolution) = 0; virtual unsigned long getTimerResolution() = 0; virtual unsigned long setTimerFreq(unsigned long freq) = 0; virtual unsigned long getTimerFreq() = 0; virtual bool startTimer() = 0; virtual bool stopTimer() = 0; virtual unsigned long getTimerTicks(bool printTicks = false) = 0; }; } // namespace MusECore #endif //__TIMERDEV_H__ muse-4.0.0/src/muse/dssihost.cpp000066400000000000000000002563541404105311000165740ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // $Id: dssihost.cpp,v 1.15.2.16 2009/12/15 03:39:58 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "config.h" #ifdef DSSI_SUPPORT // Turn on debugging messages //#define DSSI_DEBUG // Turn on constant flow of process debugging messages //#define DSSI_DEBUG_PROCESS // Support vst state saving/loading with vst chunks. //#define DSSI_VST_CHUNK_SUPPORT #include #include #include #include #include #include #include #include #include #include "dssihost.h" #include "synth.h" #include "audio.h" #include "jackaudio.h" #include "midi_consts.h" #include "midiport.h" #include "minstrument.h" #include "stringparam.h" #include "plugin.h" #include "controlfifo.h" #include "xml.h" #include "song.h" #include "ctrl.h" #include "app.h" #include "globals.h" #include "gconfig.h" #include "popupmenu.h" #include "lock_free_buffer.h" #include "pluglist.h" namespace MusECore { //--------------------------------------------------------- // initDSSI //--------------------------------------------------------- void initDSSI() { const MusEPlugin::PluginScanList& scan_list = MusEPlugin::pluginList; for(MusEPlugin::ciPluginScanList isl = scan_list.begin(); isl != scan_list.end(); ++isl) { const MusEPlugin::PluginScanInfoRef inforef = *isl; const MusEPlugin::PluginScanInfoStruct& info = inforef->info(); switch(info._type) { case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI: case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST: { #ifdef DSSI_SUPPORT if(MusEGlobal::loadDSSI) { // * Done in plugin.cpp already. * //if(info._class & PluginScanInfo::PluginClassEffect) //{ // // Make sure it doesn't already exist. // if(const Plugin* pl = MusEGlobal::plugins.find(info._fi.completeBaseName(), info._label)) // { // fprintf(stderr, "Ignoring DSSI effect label:%s path:%s duplicate of path:%s\n", // info._label.toLatin1().constData(), // info._fi.filePath().toLatin1().constData(), // pl->filePath().toLatin1().constData()); // // } // else // { // if(MusEGlobal::debugMsg) // info.dump(message); // MusEGlobal::plugins.add(info); // } //} // For now we allow effects as a synth track. Until we allow programs (and midi) in the effect rack. if(info._class & MusEPlugin::PluginScanInfoStruct::PluginClassEffect || info._class & MusEPlugin::PluginScanInfoStruct::PluginClassInstrument) { // Make sure it doesn't already exist. if(const Synth* sy = MusEGlobal::synthis.find( PLUGIN_GET_QSTRING(info._completeBaseName), PLUGIN_GET_QSTRING(info._uri), PLUGIN_GET_QSTRING(info._label))) { fprintf(stderr, "Ignoring DSSI synth label:%s uri:%s path:%s duplicate of path:%s\n", PLUGIN_GET_CSTRING(info._label), PLUGIN_GET_CSTRING(info._uri), PLUGIN_GET_CSTRING(info.filePath()), sy->filePath().toLatin1().constData()); } else { DssiSynth* s = new DssiSynth(info); MusEGlobal::synthis.push_back(s); } } } #endif } break; case MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA: case MusEPlugin::PluginScanInfoStruct::PluginTypeVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeLV2: case MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeMESS: case MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown: case MusEPlugin::PluginScanInfoStruct::PluginTypeNone: case MusEPlugin::PluginScanInfoStruct::PluginTypeAll: break; } } } //--------------------------------------------------------- // DssiSynth // Synth.name = plug.Label (In LADSPA and DSSI this is the more important unique string) // Synth.descr = plug.Name (In LADSPA and DSSI this is the less important name string) // Synth.maker = plug.maker // Synth.version = nil (no such field in ladspa, maybe try copyright instead) //--------------------------------------------------------- DssiSynth::DssiSynth(QFileInfo& fi, const QString& uri, const DSSI_Descriptor* d, bool isDssiVst, PluginFeatures_t reqFeatures) : // ddskrjo removed const from QFileInfo Synth(fi, uri, QString(d->LADSPA_Plugin->Label), QString(d->LADSPA_Plugin->Name), QString(d->LADSPA_Plugin->Maker), QString(), reqFeatures) { df = 0; handle = 0; dssi = 0; _isDssiVst = isDssiVst; _hasGui = false; const LADSPA_Descriptor* descr = d->LADSPA_Plugin; _portCount = descr->PortCount; _inports = 0; _outports = 0; _controlInPorts = 0; _controlOutPorts = 0; for(unsigned long k = 0; k < _portCount; ++k) { LADSPA_PortDescriptor pd = descr->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) { if(pd & LADSPA_PORT_INPUT) ++_inports; else if(pd & LADSPA_PORT_OUTPUT) ++_outports; } else if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) ++_controlInPorts; else if(pd & LADSPA_PORT_OUTPUT) ++_controlOutPorts; } } // Hack: Blacklist vst plugins in-place, configurable for now. if ((_inports != _outports) || (_isDssiVst && !MusEGlobal::config.vstInPlace)) _requiredFeatures |= PluginNoInPlaceProcessing; } //--------------------------------------------------------- // DssiSynth // Synth.name = plug.Label (In LADSPA and DSSI this is the more important unique string) // Synth.descr = plug.Name (In LADSPA and DSSI this is the less important name string) // Synth.maker = plug.maker // Synth.version = nil (no such field in ladspa, maybe try copyright instead) //--------------------------------------------------------- DssiSynth::DssiSynth(const MusEPlugin::PluginScanInfoStruct& info) : Synth(PLUGIN_GET_QSTRING(info.filePath()), PLUGIN_GET_QSTRING(info._uri), PLUGIN_GET_QSTRING(info._label), PLUGIN_GET_QSTRING(info._name), PLUGIN_GET_QSTRING(info._maker), QString(), info._requiredFeatures) { df = 0; handle = 0; dssi = 0; _isDssiVst = info._type == MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST; // _hasGui = false; _hasGui = info._pluginFlags & MusEPlugin::PluginScanInfoStruct::HasGui; _portCount = info._portCount; _inports = info._inports; _outports = info._outports; _controlInPorts = info._controlInPorts; _controlOutPorts = info._controlOutPorts; // Hack: Blacklist vst plugins in-place, configurable for now. if(_isDssiVst && !MusEGlobal::config.vstInPlace) _requiredFeatures |= PluginNoInPlaceProcessing; } DssiSynth::~DssiSynth() { if(dssi) // delete dssi; printf("DssiSynth::~DssiSynth Error: dssi descriptor is not NULL\n"); } //--------------------------------------------------------- // createSIF //--------------------------------------------------------- SynthIF* DssiSynth::createSIF(SynthI* synti) { if (_instances == 0) { handle = dlopen(info.filePath().toLatin1().constData(), RTLD_NOW); if (handle == 0) { fprintf(stderr, "DssiSynth::createSIF dlopen(%s) failed: %s\n", info.filePath().toLatin1().constData(), dlerror()); return 0; } df = (DSSI_Descriptor_Function)dlsym(handle, "dssi_descriptor"); if (!df) { const char *txt = dlerror(); fprintf(stderr, "Unable to find dssi_descriptor() function in plugin " "library file \"%s\": %s.\n" "Are you sure this is a DSSI plugin file?\n", info.filePath().toLatin1().constData(), txt ? txt : "?"); dlclose(handle); handle = 0; return 0; } for (int i = 0;; ++i) { dssi = df(i); if (dssi == 0) break; QString label(dssi->LADSPA_Plugin->Label); if (label == _name) break; } if(dssi != 0) { _inports = 0; _outports = 0; _controlInPorts = 0; _controlOutPorts = 0; iIdx.clear(); oIdx.clear(); rpIdx.clear(); midiCtl2PortMap.clear(); port2MidiCtlMap.clear(); const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; _portCount = descr->PortCount; for (unsigned long k = 0; k < _portCount; ++k) { LADSPA_PortDescriptor pd = descr->PortDescriptors[k]; #ifdef DSSI_DEBUG printf("DssiSynth::createSIF ladspa plugin Port:%lu Name:%s descriptor:%x\n", k, descr->PortNames[k], pd); #endif if (LADSPA_IS_PORT_AUDIO(pd)) { if (LADSPA_IS_PORT_INPUT(pd)) { ++_inports; iIdx.push_back(k); } else if (LADSPA_IS_PORT_OUTPUT(pd)) { ++_outports; oIdx.push_back(k); } rpIdx.push_back((unsigned long)-1); } else if (LADSPA_IS_PORT_CONTROL(pd)) { if (LADSPA_IS_PORT_INPUT(pd)) { rpIdx.push_back(_controlInPorts); ++_controlInPorts; } else if (LADSPA_IS_PORT_OUTPUT(pd)) { rpIdx.push_back((unsigned long)-1); ++_controlOutPorts; } } } // Hack: Blacklist vst plugins in-place, configurable for now. if((_inports != _outports) || (_isDssiVst && !MusEGlobal::config.vstInPlace)) _requiredFeatures |= PluginNoInPlaceProcessing; } } if (dssi == 0) { fprintf(stderr, "cannot find DSSI synti %s\n", _name.toLatin1().constData()); dlclose(handle); handle = 0; df = 0; return 0; } DssiSynthIF* sif = new DssiSynthIF(synti); ++_instances; sif->init(this); return sif; } //--------------------------------------------------------- // guiVisible //--------------------------------------------------------- bool DssiSynthIF::nativeGuiVisible() const { #ifdef OSC_SUPPORT return _oscif.oscGuiVisible(); #endif return false; } //--------------------------------------------------------- // showNativeGui //--------------------------------------------------------- void DssiSynthIF::showNativeGui(bool #if defined(OSC_SUPPORT) v #endif ) { #ifdef OSC_SUPPORT #ifdef DSSI_DEBUG printf("DssiSynthIF::showNativeGui(): v:%d visible:%d\n", v, guiVisible()); #endif _oscif.oscShowGui(v); #endif // OSC_SUPPORT } //--------------------------------------------------------- // receiveEvent //--------------------------------------------------------- MidiPlayEvent DssiSynthIF::receiveEvent() { return MidiPlayEvent(); } //--------------------------------------------------------- // init //--------------------------------------------------------- bool DssiSynthIF::init(DssiSynth* s) { #ifdef DSSI_DEBUG printf("DssiSynthIF::init\n"); #endif _synth = s; const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; _handle = ld->instantiate(ld, MusEGlobal::sampleRate); #ifdef OSC_SUPPORT _oscif.oscSetSynthIF(this); #endif queryPrograms(); int inports = _synth->_inports; if(inports != 0) { int rv = posix_memalign((void**)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DssiSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) _audioInSilenceBuf[q] = MusEGlobal::denormalBias; } else memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); _audioInBuffers = new float*[inports]; for(int k = 0; k < inports; ++k) { int rv = posix_memalign((void**)&_audioInBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DssiSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) _audioInBuffers[k][q] = MusEGlobal::denormalBias; } else memset(_audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); ld->connect_port(_handle, _synth->iIdx[k], _audioInBuffers[k]); } } int outports = _synth->_outports; if(outports != 0) { _audioOutBuffers = new float*[outports]; for(int k = 0; k < outports; ++k) { int rv = posix_memalign((void**)&_audioOutBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: DssiSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) _audioOutBuffers[k][q] = MusEGlobal::denormalBias; } else memset(_audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); ld->connect_port(_handle, _synth->oIdx[k], _audioOutBuffers[k]); } } int controlPorts = _synth->_controlInPorts; int controlOutPorts = _synth->_controlOutPorts; if(controlPorts != 0) _controls = new Port[controlPorts]; else _controls = 0; if(controlOutPorts != 0) _controlsOut = new Port[controlOutPorts]; else _controlsOut = 0; _synth->midiCtl2PortMap.clear(); _synth->port2MidiCtlMap.clear(); int cip = 0; int cop = 0; for (unsigned long k = 0; k < _synth->_portCount; ++k) { LADSPA_PortDescriptor pd = ld->PortDescriptors[k]; #ifdef DSSI_DEBUG printf("DssiSynth::init ladspa plugin Port:%lu Name:%s descriptor:%x\n", k, ld->PortNames[k], pd); #endif if (LADSPA_IS_PORT_CONTROL(pd)) { if (LADSPA_IS_PORT_INPUT(pd)) { _controls[cip].idx = k; float val; ladspaDefaultValue(ld, k, &val); _controls[cip].val = val; _controls[cip].tmpVal = val; _controls[cip].enCtrl = true; #ifdef DSSI_DEBUG printf("DssiSynthIF::init control port:%d port idx:%lu name:%s\n", cip, k, ld->PortNames[k]); #endif // This code is duplicated in ::getControllerInfo() int ctlnum = DSSI_NONE; if(dssi->get_midi_controller_for_port) ctlnum = dssi->get_midi_controller_for_port(_handle, k); // No controller number? Try to give it a unique one... if(ctlnum == DSSI_NONE) { // FIXME: Be more careful. Must make sure to pick numbers not already chosen or which WILL BE chosen. // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. // TODO: Update: Actually we want to try to use CC Controller7 controllers if possible (or a choice) because what if // the user's controller hardware doesn't support RPN? // If CC Controller7 is chosen we must make sure to use only non-common numbers. An already limited range // of 127 now becomes narrower. See the cool document midi-controllers.txt in the DSSI source for a // nice roundup of numbers and how to choose them and how they relate to synths and DSSI synths etc. ! ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + cip; } else { int c = ctlnum; // Can be both CC and NRPN! Prefer CC over NRPN. if(DSSI_IS_CC(ctlnum)) { #ifdef DSSI_DEBUG printf("DssiSynthIF::init is CC control\n"); #endif ctlnum = DSSI_CC_NUMBER(c); #ifdef DSSI_DEBUG if(DSSI_IS_NRPN(ctlnum)) printf("DssiSynthIF::init is also NRPN control. Using CC.\n"); #endif } else if(DSSI_IS_NRPN(ctlnum)) { #ifdef DSSI_DEBUG printf("DssiSynthIF::init is NRPN control\n"); #endif ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; } } #ifdef DSSI_DEBUG printf("DssiSynthIF::init inserting to midiCtl2PortMap: ctlnum:%d k:%d\n", ctlnum, cip); #endif // We have a controller number! Insert it and the DSSI port number into both maps. _synth->midiCtl2PortMap.insert(std::pair(ctlnum, cip)); _synth->port2MidiCtlMap.insert(std::pair(cip, ctlnum)); // Support a special block for dssi synth ladspa controllers. // Put the ID at a special block after plugins (far after). int id = genACnum(MusECore::MAX_PLUGINS, cip); const char* name = ld->PortNames[k]; float min, max; ladspaControlRange(ld, k, &min, &max); CtrlList* cl; CtrlListList* cll = track()->controller(); iCtrlList icl = cll->find(id); if (icl == cll->end()) { cl = new CtrlList(id); cll->add(cl); cl->setCurVal(_controls[cip].val); } else { cl = icl->second; _controls[cip].val = cl->curVal(); } cl->setRange(min, max); cl->setName(QString(name)); cl->setValueType(ladspaCtrlValueType(ld, k)); cl->setMode(ladspaCtrlMode(ld, k)); ld->connect_port(_handle, k, &_controls[cip].val); ++cip; } else if (LADSPA_IS_PORT_OUTPUT(pd)) { const char* pname = ld->PortNames[k]; if(pname == QString("latency") || pname == QString("_latency")) { _hasLatencyOutPort = true; _latencyOutPort = cop; } _controlsOut[cop].idx = k; _controlsOut[cop].val = 0.0; _controlsOut[cop].tmpVal = 0.0; _controlsOut[cop].enCtrl = false; #ifdef DSSI_DEBUG printf("DssiSynthIF::init control output port:%d port idx:%lu name:%s\n", cop, k, ld->PortNames[k]); #endif // Control outs are not handled but still must be connected to something. ld->connect_port(_handle, k, &_controlsOut[cop].val); ++cop; } } } activate(); // Set current configuration values. if(dssi->configure) { char *rv = dssi->configure(_handle, DSSI_PROJECT_DIRECTORY_KEY, MusEGlobal::museProject.toLatin1().constData()); //MusEGlobal::song->projectPath() if(rv) { fprintf(stderr, "MusE: Warning: plugin doesn't like project directory: \"%s\"\n", rv); free(rv); } for(ciStringParamMap r = synti->_initConfig._stringParamMap.begin(); r != synti->_initConfig._stringParamMap.end(); ++r) { rv = 0; rv = dssi->configure(_handle, r->first.c_str(), r->second.c_str()); if(rv) { fprintf(stderr, "MusE: Warning: plugin config key: %s value: %s \"%s\"\n", r->first.c_str(), r->second.c_str(), rv); free(rv); } } } // // For stored initial control values, let SynthI::initInstance() take care of that via ::setParameter(). // return true; } //--------------------------------------------------------- // DssiSynthIF //--------------------------------------------------------- DssiSynthIF::DssiSynthIF(SynthI* s) : SynthIF(s) { #ifdef DSSI_DEBUG printf("DssiSynthIF::DssiSynthIF\n"); #endif _synth = 0; _handle = NULL; _controls = 0; _controlsOut = 0; _hasLatencyOutPort = false; _latencyOutPort = 0; _audioInBuffers = 0; _audioInSilenceBuf = 0; _audioOutBuffers = 0; } //--------------------------------------------------------- // ~DssiSynthIF //--------------------------------------------------------- DssiSynthIF::~DssiSynthIF() { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIF\n"); #endif #ifdef OSC_SUPPORT _oscif.oscSetSynthIF(NULL); #endif if(_synth) { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIF synth:%p\n", synth); #endif if(_synth->dssi) { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIF synth->dssi:%p\n", synth->dssi); #endif if(_synth->dssi->LADSPA_Plugin) { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIFsynth->dssi->LADSPA_Plugin:%p\n", synth->dssi->LADSPA_Plugin); #endif } } } if(_synth && _synth->dssi && _synth->dssi->LADSPA_Plugin) { const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIF checking cleanup function exists\n"); #endif if(descr->cleanup) { #ifdef DSSI_DEBUG printf("DssiSynthIF::~DssiSynthIF calling cleanup function\n"); #endif descr->cleanup(_handle); } } if(_audioInBuffers) { for(unsigned long i = 0; i < _synth->_inports; ++i) { if(_audioInBuffers[i]) free(_audioInBuffers[i]); } delete[] _audioInBuffers; } if(_audioInSilenceBuf) free(_audioInSilenceBuf); if(_audioOutBuffers) { for(unsigned long i = 0; i < _synth->_outports; ++i) { if(_audioOutBuffers[i]) free(_audioOutBuffers[i]); } delete[] _audioOutBuffers; } if(_controls) delete[] _controls; if(_controlsOut) delete[] _controlsOut; } int DssiSynthIF::oldMidiStateHeader(const unsigned char** data) const { static unsigned char const d[2] = {MUSE_SYNTH_SYSEX_MFG_ID, DSSI_SYNTH_UNIQUE_ID}; *data = &d[0]; return 2; } bool DssiSynthIF::hasLatencyOutPort() const { return _hasLatencyOutPort; } unsigned long DssiSynthIF::latencyOutPortIndex() const { return _latencyOutPort; } //--------------------------------------------------------- // latency //--------------------------------------------------------- float DssiSynthIF::latency() const { // Do not report any latency if the plugin is not on. if(!on()) return 0.0; if(cquirks()._overrideReportedLatency) return cquirks()._latencyOverrideValue; if(!hasLatencyOutPort()) return 0.0; return _controlsOut[latencyOutPortIndex()].val; } //--------------------------------------------------------- // getParameter //--------------------------------------------------------- double DssiSynthIF::getParameter(unsigned long n) const { if(n >= _synth->_controlInPorts) { printf("DssiSynthIF::getParameter param number %lu out of range of ports:%lu\n", n, _synth->_controlInPorts); return 0.0; } if(!_controls) return 0.0; return _controls[n].val; } //--------------------------------------------------------- // getParameter //--------------------------------------------------------- double DssiSynthIF::getParameterOut(unsigned long n) const { if(n >= _synth->_controlOutPorts) { printf("DssiSynthIF::getParameterOut param number %lu out of range of ports:%lu\n", n, _synth->_controlOutPorts); return 0.0; } if(!_controlsOut) return 0.0; return _controlsOut[n].val; } //--------------------------------------------------------- // setParameter //--------------------------------------------------------- void DssiSynthIF::setParameter(unsigned long n, double v) { addScheduledControlEvent(n, v, MusEGlobal::audio->curFrame()); } //--------------------------------------------------------- // write //--------------------------------------------------------- void DssiSynthIF::write(int level, Xml& xml) const { #ifdef DSSI_VST_CHUNK_SUPPORT if(_synth->dssi->getCustomData) { //--------------------------------------------- // dump current state of synth //--------------------------------------------- printf("dumping DSSI custom data! %p\n", _synth->dssi->getCustomData); // this is only needed and supported if // we are talking to a VST plugin at the other end. std::string name = _synth->dssi->LADSPA_Plugin->Name; if ((name.length()> 4) && name.substr(name.length() - 4) == " VST") { printf("is vst plugin, commencing data dump, apiversion=%d!\n", _synth->dssi->DSSI_API_Version); unsigned long len = 0; void* p = 0; _synth->dssi->getCustomData(_handle,&p, &len); if (len) { xml.tag(level++, " version=\"%d\"", SYNTH_MIDI_STATE_SAVE_VERSION); xml.nput(level++, "\n", len+9 /* 9 = 2 bytes header + "VSTSAVE"*/); xml.nput(level, ""); xml.nput("%02x %02x ", (char)MUSE_SYNTH_SYSEX_MFG_ID, (char)DSSI_SYNTH_UNIQUE_ID); // Wrap in a proper header xml.nput("56 53 54 53 41 56 45 "); // embed a save marker "string 'VSTSAVE' for (long unsigned int i = 0; i < len; ++i) { if (i && (((i+9) % 16) == 0)) { xml.nput("\n"); xml.nput(level, ""); } xml.nput("%02x ", ((char*)(p))[i] & 0xff); } xml.nput("\n"); xml.tag(level--, "/event"); xml.etag(level--, "midistate"); } } } #else printf("support for vst chunks not compiled in!\n"); #endif // DELETETHIS 97 ??? /* // p3.3.39 Store the state of current program and bank and all input control values, but only if VSTSAVE above didn't do it already! // TODO: Not quite good enough, we would want to store all controls for EACH program, not just the current one. // Need to modify controls array to be inside a program array and act as a cache when the user changes a control on a particular program. if(!vstsaved) { if(synth->_controlInPorts) { // TODO: Hmm, what if these sizes change (platform etc.)? Hard code? Not good - need to store complete value. const int fs = sizeof(float); const int uls = sizeof(unsigned long); // Data length: Version major and minor bytes, bank + program, and controllers. const unsigned long len = 2 + 2 * uls + synth->_controlInPorts * fs; unsigned long prog = _curBank; unsigned long bnk = _curProgram; xml.tag(level++, "midistate"); xml.nput(level++, "\n", len+9); // "PARAMSAVE" length + data length. xml.nput(level, ""); xml.nput("50 41 52 41 4d 53 41 56 45 "); // Embed a save marker string "PARAMSAVE". unsigned long i = 9; // Store PARAMSAVE version major... char uc = DSSI_PARAMSAVE_VERSION_MAJOR; if(i && ((i % 16) == 0)) { xml.nput("\n"); xml.nput(level, ""); } xml.nput("%02x ", uc & 0xff); ++i; // Store PARAMSAVE version minor... uc = DSSI_PARAMSAVE_VERSION_MINOR; if(i && ((i % 16) == 0)) { xml.nput("\n"); xml.nput(level, ""); } xml.nput("%02x ", uc & 0xff); ++i; // Store bank... void* p = &bnk; for(int j = 0; j < uls; ++j) { if(i && ((i % 16) == 0)) { xml.nput("\n"); xml.nput(level, ""); } xml.nput("%02x ", ((char*)(p))[j] & 0xff); ++i; } // Store program... p = &prog; for(int j = 0; j < uls; ++j) { if(i && ((i % 16) == 0)) { xml.nput("\n"); xml.nput(level, ""); } xml.nput("%02x ", ((char*)(p))[j] & 0xff); ++i; } // Store controls... for(unsigned long c = 0; c < synth->_controlInPorts; ++c) { float v = controls[c].val; p = &v; for(int j = 0; j < fs; ++j) { if(i && ((i % 16) == 0)) { xml.nput("\n"); xml.nput(level, ""); } xml.nput("%02x ", ((char*)(p))[j] & 0xff); ++i; } } xml.nput("\n"); xml.tag(level--, "/event"); xml.etag(level--, "midistate"); } } */ // Store controls as parameters... for(unsigned long c = 0; c < _synth->_controlInPorts; ++c) xml.doubleTag(level, "param", _controls[c].val); } //--------------------------------------------------------- // processEvent // Return true if event pointer filled. //-------------------------------------------------------- bool DssiSynthIF::processEvent(const MidiPlayEvent& e, snd_seq_event_t* event) { const DSSI_Descriptor* dssi = _synth->dssi; int chn = e.channel(); int a = e.dataA(); int b = e.dataB(); #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event type:%d chn:%d a:%d b:%d\n", e.type(), chn, a, b); #endif const MidiInstrument::NoteOffMode nom = synti->noteOffMode(); switch(e.type()) { case ME_NOTEON: #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_NOTEON\n"); #endif snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; if(b == 0) { // Handle zero-velocity note ons. Technically this is an error because internal midi paths // are now all 'note-off' without zero-vel note ons - they're converted to note offs. // Nothing should be setting a Note type Event's on velocity to zero. // But just in case... If we get this warning, it means there is still code to change. fprintf(stderr, "DssiSynthIF::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", e.time(), e.type(), chn, a, b); switch(nom) { // Instrument uses note offs. Convert to zero-vel note off. case MidiInstrument::NoteOffAll: //if(MusEGlobal::midiOutputTrace) // fprintf(stderr, "MidiOut: DSSI: Following event will be converted to zero-velocity note off:\n"); snd_seq_ev_set_noteoff(event, chn, a, 0); break; // Instrument uses no note offs at all. Send as-is. case MidiInstrument::NoteOffNone: // Instrument converts all note offs to zero-vel note ons. Send as-is. case MidiInstrument::NoteOffConvertToZVNoteOn: snd_seq_ev_set_noteon(event, chn, a, b); break; } } else snd_seq_ev_set_noteon(event, chn, a, b); break; case ME_NOTEOFF: #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_NOTEOFF\n"); #endif snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; switch(nom) { // Instrument uses note offs. Send as-is. case MidiInstrument::NoteOffAll: snd_seq_ev_set_noteoff(event, chn, a, b); break; // Instrument uses no note offs at all. Send nothing. Eat up the event - return false. case MidiInstrument::NoteOffNone: return false; // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. case MidiInstrument::NoteOffConvertToZVNoteOn: //if(MusEGlobal::midiOutputTrace) // fprintf(stderr, "MidiOut: DSSI: Following event will be converted to zero-velocity note on:\n"); snd_seq_ev_set_noteon(event, chn, a, 0); break; } break; // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore. case ME_PROGRAM: { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_PROGRAM\n"); #endif int hb, lb; synti->currentProg(chn, NULL, &lb, &hb); synti->setCurrentProg(chn, a & 0xff, lb, hb); doSelectProgram(_handle, hb, lb, a); // Event pointer not filled. Return false. return false; } break; case ME_CONTROLLER: { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER\n"); #endif // Our internal hwCtrl controllers support the 'unknown' value. // Don't send 'unknown' values to the driver. Ignore and return no error. if(b == CTRL_VAL_UNKNOWN) return false; if(a == CTRL_PROGRAM) { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PROGRAM\n"); #endif int hb = (b >> 16) & 0xff; int lb = (b >> 8) & 0xff; int pr = b & 0xff; synti->setCurrentProg(chn, pr, lb, hb); doSelectProgram(_handle, hb, lb, pr); // Event pointer not filled. Return false. return false; } if(a == CTRL_HBANK) { int lb, pr; synti->currentProg(chn, &pr, &lb, NULL); synti->setCurrentProg(chn, pr, lb, b & 0xff); doSelectProgram(_handle, b, lb, pr); // Event pointer not filled. Return false. return false; } if(a == CTRL_LBANK) { int hb, pr; synti->currentProg(chn, &pr, NULL, &hb); synti->setCurrentProg(chn, pr, b & 0xff, hb); doSelectProgram(_handle, hb, b, pr); // Event pointer not filled. Return false. return false; } if(a == CTRL_PITCH) { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PITCH\n"); #endif snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_pitchbend(event, chn, b); // Event pointer filled. Return true. return true; } if(a == CTRL_AFTERTOUCH) { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_AFTERTOUCH\n"); #endif snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_chanpress(event, chn, b); // Event pointer filled. Return true. return true; } if((a | 0xff) == CTRL_POLYAFTER) { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_POLYAFTER\n"); #endif snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_keypress(event, chn, a & 0x7f, b & 0x7f); // Event pointer filled. Return true. return true; } const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; ciMidiCtl2LadspaPort ip = _synth->midiCtl2PortMap.find(a); // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)? // NOTE: There's no way to tell which of these controllers is supported by the plugin. // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port. if(ip == _synth->midiCtl2PortMap.end()) { int ctlnum = a; if(midiControllerType(a) != MidiController::Controller7) return false; // Event pointer not filled. Return false. else { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent non-ladspa midi event is Controller7. Current dataA:%d\n", a); #endif a &= 0x7f; ctlnum = DSSI_CC_NUMBER(ctlnum); } // Fill the event. #ifdef DSSI_DEBUG printf("DssiSynthIF::processEvent non-ladspa filling midi event chn:%d dataA:%d dataB:%d\n", chn, a, b); #endif snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_controller(event, chn, a, b); return true; } unsigned long k = ip->second; unsigned long i = _controls[k].idx; int ctlnum = DSSI_NONE; if(dssi->get_midi_controller_for_port) ctlnum = dssi->get_midi_controller_for_port(_handle, i); // No midi controller for the ladspa port? Send to ladspa control. if(ctlnum == DSSI_NONE) { // Sanity check. if(k > _synth->_controlInPorts) return false; // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. ctlnum = k + (CTRL_NRPN14_OFFSET + 0x2000); } else { #ifdef DSSI_DEBUG printf("DssiSynthIF::processEvent plugin requests DSSI-style ctlnum:%x(h) %d(d) be mapped to control port:%lu...\n", ctlnum, ctlnum, i); #endif int c = ctlnum; // Can be both CC and NRPN! Prefer CC over NRPN. if(DSSI_IS_CC(ctlnum)) { ctlnum = DSSI_CC_NUMBER(c); #ifdef DSSI_DEBUG printf("DssiSynthIF::processEvent is CC ctlnum:%d\n", ctlnum); #endif #ifdef DSSI_DEBUG if(DSSI_IS_NRPN(ctlnum)) printf("DssiSynthIF::processEvent is also NRPN control. Using CC.\n"); #endif } else if(DSSI_IS_NRPN(ctlnum)) { ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; #ifdef DSSI_DEBUG printf("DssiSynthIF::processEvent is NRPN ctlnum:%x(h) %d(d)\n", ctlnum, ctlnum); #endif } } float val = midi2LadspaValue(ld, i, ctlnum, b); #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent control port:%lu port:%lu dataA:%d Converting val from:%d to ladspa:%f\n", i, k, a, b, val); #endif // Set the ladspa port value. _controls[k].val = val; // Need to update the automation value, otherwise it overwrites later with the last automation value. if(id() != -1) // We're in the audio thread context: no need to send a message, just modify directly. synti->setPluginCtrlVal(genACnum(id(), k), val); // Since we absorbed the message as a ladspa control change, return false - the event is not filled. return false; } break; case ME_PITCHBEND: snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_pitchbend(event, chn, a); break; case ME_AFTERTOUCH: snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_chanpress(event, chn, a); break; case ME_POLYAFTER: snd_seq_ev_clear(event); event->queue = SND_SEQ_QUEUE_DIRECT; snd_seq_ev_set_keypress(event, chn, a & 0x7f, b & 0x7f); break; case ME_SYSEX: { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX\n"); #endif const unsigned char* data = e.constData(); if(e.len() >= 2) { if(data[0] == MUSE_SYNTH_SYSEX_MFG_ID) { if(data[1] == DSSI_SYNTH_UNIQUE_ID) { if(e.len() >= 9) { if (QString((const char*)(data + 2)).startsWith("VSTSAVE")) { #ifdef DSSI_VST_CHUNK_SUPPORT if(dssi->setCustomData) { printf("loading chunk from sysex %s!\n", data+9); usleep(300000); dssi->setCustomData(_handle, (unsigned char*)(data+9) /* len of str*/,e.len()-9); usleep(300000); } #else printf("support for vst chunks not compiled in!\n"); #endif // Event not filled. return false; } } } } } // DELETETHIS, 50 clean it up or fix it? /* // p3.3.39 Read the state of current bank and program and all input control values. // TODO: Needs to be better. See write(). //else if (QString((const char*)e.data()).startsWith("PARAMSAVE")) { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::processEvent midi event is ME_SYSEX PARAMSAVE\n"); #endif unsigned long dlen = e.len() - 9; // Minus "PARAMSAVE" if(dlen > 0) { //if(dlen < 2 * sizeof(unsigned long)) if(dlen < (2 + 2 * sizeof(unsigned long))) // Version major and minor bytes, bank and program. printf("DssiSynthIF::processEvent Error: PARAMSAVE data length does not include at least version major and minor, bank and program!\n"); else { // Not required, yet. //char vmaj = *((char*)(e.data() + 9)); // After "PARAMSAVE" //char vmin = *((char*)(e.data() + 10)); unsigned long* const ulp = (unsigned long*)(e.data() + 11); // After "PARAMSAVE" + version major and minor. // TODO: TODO: Set plugin bank and program. _curBank = ulp[0]; _curProgram = ulp[1]; dlen -= (2 + 2 * sizeof(unsigned long)); // After the version major and minor, bank and program. if(dlen > 0) { if((dlen % sizeof(float)) != 0) printf("DssiSynthIF::processEvent Error: PARAMSAVE float data length not integral multiple of float size!\n"); else { const unsigned long n = dlen / sizeof(float); if(n != synth->_controlInPorts) printf("DssiSynthIF::processEvent Warning: PARAMSAVE number of floats:%lu != number of controls:%lu\n", n, synth->_controlInPorts); // Point to location after "PARAMSAVE", version major and minor, bank and progam. float* const fp = (float*)(e.data() + 9 + 2 + 2 * sizeof(unsigned long)); for(unsigned long i = 0; i < synth->_controlInPorts && i < n; ++i) { const float v = fp[i]; controls[i].val = v; } } } } } // Event not filled. return false; } */ //else { // NOTE: There is a limit on the size of a sysex. Got this: // "DssiSynthIF::processEvent midi event is ME_SYSEX" // "WARNING: MIDI event of type ? decoded to 367 bytes, discarding" // That might be ALSA doing that. const int len = e.len(); char buf[len + 2]; buf[0] = 0xF0; memcpy(buf + 1, e.constData(), len); buf[len + 1] = 0xF7; snd_seq_ev_clear(event); snd_seq_ev_set_sysex(event, len + 2, buf); event->queue = SND_SEQ_QUEUE_DIRECT; // NOTE: Don't move this out, 'buf' would go out of scope. // Event was filled. Return true. return true; } } break; default: if(MusEGlobal::debugMsg) fprintf(stderr, "DssiSynthIF::processEvent midi event unknown type:%d\n", e.type()); // Event not filled. return false; break; } return true; } //--------------------------------------------------------- // getData // If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- bool DssiSynthIF::getData(MidiPort* /*mp*/, unsigned pos, int ports, unsigned nframes, float** buffer) { const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu\n", pos, ports, nframes, syncFrame); #endif // All ports must be connected to something! const unsigned long in_ports = _synth->inPorts(); const unsigned long out_ports = _synth->outPorts(); const unsigned long nop = ((unsigned long) ports) > out_ports ? out_ports : ((unsigned long) ports); const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* descr = dssi->LADSPA_Plugin; unsigned long sample = 0; // NOTE Tested: Variable run-lengths worked superbly for LADSPA and DSSI synths. But DSSI-VST definitely // does NOT like changing sample run length. It crashes the plugin and Wine (but MusE keeps running!). // Furthermore, it resizes the shared memory (mmap, remap) upon each run length DIFFERENT from the last. // And all of this done through client-server communications. It doesn't seem designed for this technique. // // So we could support an alternate technique: A fixed control processing rate, in number of samples. // // Allow user to choose either a fixed rate or these 'packets' for LADSPA and DSSI plugins/synths, // but make fixed-rate MANDATORY for DSSI-VST plugins and synths. // // Or K.I.S.S - Just use fixed rates only, but allow it to be changed. I'm worried about libraries and // plugins other than DSSI-VST. What if they need the fixed-rate, too? // How to tell, and manage it all...? // But this 'packet' method sure seems to work nicely so far, so we'll throw it in... // // Must make this detectable for dssi vst synths, just like the plugins' in-place blacklist. // FIXME Better support for PluginPowerOf2BlockSize, by quantizing the control period times. // For now we treat it like fixed size. const bool usefixedrate = (requiredFeatures() & (PluginFixedBlockSize | PluginPowerOf2BlockSize | PluginCoarseBlockSize)); // Note for dssi-vst this MUST equal MusEGlobal::audio period. It doesn't like broken-up runs (it stutters), // even with fixed sizes. Could be a Wine + Jack thing, wanting a full Jack buffer's length. // For now, the fixed size is clamped to the MusEGlobal::audio buffer size. // TODO: We could later add slower processing over several cycles - // so that users can select a small MusEGlobal::audio period but a larger control period. const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; const unsigned long min_per_mask = min_per-1; // min_per must be power of 2 AudioTrack* atrack = track(); const AutomationType at = atrack->automationType(); const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; const unsigned long in_ctrls = _synth->inControls(); CtrlListList* cll = atrack->controller(); ciCtrlList icl_first; const int plug_id = id(); if(plug_id != -1 && ports != 0) // Don't bother if not 'running'. icl_first = cll->lower_bound(genACnum(plug_id, 0)); #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData: Handling inputs...\n"); #endif bool used_in_chan_array[in_ports]; // Don't bother initializing if not 'running'. // Don't bother if not 'running'. if(ports != 0) { // Initialize the array. for(unsigned long i = 0; i < in_ports; ++i) used_in_chan_array[i] = false; if(!atrack->noInRoute()) { RouteList *irl = atrack->inRoutes(); for(ciRoute i = irl->begin(); i != irl->end(); ++i) { if(i->track->isMidiTrack()) continue; // Only this synth knows how many destination channels there are, // while only the track knows how many source channels there are. // So take care of the destination channels here, and let the track handle the source channels. const int dst_ch = i->channel <= -1 ? 0 : i->channel; if((unsigned long)dst_ch >= in_ports) continue; const int dst_chs = i->channels <= -1 ? in_ports : i->channels; //const int total_ins = atrack->totalRoutableInputs(Route::TRACK_ROUTE); const int src_ch = i->remoteChannel <= -1 ? 0 : i->remoteChannel; const int src_chs = i->channels; int fin_dst_chs = dst_chs; if((unsigned long)(dst_ch + fin_dst_chs) > in_ports) fin_dst_chs = in_ports - dst_ch; static_cast(i->track)->copyData(pos, dst_ch, dst_chs, fin_dst_chs, src_ch, src_chs, nframes, &_audioInBuffers[0], false, used_in_chan_array); const int nxt_ch = dst_ch + fin_dst_chs; for(int ch = dst_ch; ch < nxt_ch; ++ch) used_in_chan_array[ch] = true; } } } #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData: Processing automation control values...\n"); #endif int cur_slice = 0; while(sample < nframes) { unsigned long nsamp = nframes - sample; const unsigned long slice_frame = pos + sample; // // Process automation control values, while also determining the maximum acceptable // size of this run. Further processing, from FIFOs for example, can lower the size // from there, but this section determines where the next highest maximum frame // absolutely needs to be for smooth playback of the controller value stream... // if(ports != 0) // Don't bother if not 'running'. { ciCtrlList icl = icl_first; for(unsigned long k = 0; k < in_ctrls; ++k) { CtrlList* cl = (cll && plug_id != -1 && icl != cll->end()) ? icl->second : NULL; CtrlInterpolate& ci = _controls[k].interp; // Always refresh the interpolate struct at first, since things may have changed. // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && (slice_frame < (unsigned long)ci.sFrame || (ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame)) ) ) { if(cl && plug_id != -1 && (unsigned long)cl->id() == genACnum(plug_id, k)) { cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); if(icl != cll->end()) ++icl; } else { // No matching controller, or end. Just copy the current value into the interpolator. // Keep the current icl iterator, because since they are sorted by frames, // if the IDs didn't match it means we can just let k catch up with icl. ci.sFrame = 0; ci.eFrame = 0; ci.eFrameValid = false; ci.sVal = _controls[k].val; ci.eVal = ci.sVal; ci.doInterp = false; ci.eStop = false; } } else { if(ci.eStop && ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. { // Clear the stop condition and set up the interp struct appropriately as an endless value. ci.sFrame = 0; //ci->eFrame; ci.eFrame = 0; ci.eFrameValid = false; ci.sVal = ci.eVal; ci.doInterp = false; ci.eStop = false; } if(cl && cll && icl != cll->end()) ++icl; } if(!usefixedrate && MusEGlobal::audio->isPlaying()) { unsigned long samps = nsamp; if(ci.eFrameValid) samps = (unsigned long)ci.eFrame - slice_frame; if(!ci.doInterp && samps > min_per) { samps &= ~min_per_mask; if((samps & min_per_mask) != 0) samps += min_per; } else samps = min_per; if(samps < nsamp) nsamp = samps; } if(ci.doInterp && cl) _controls[k].val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); else _controls[k].val = ci.sVal; #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData k:%lu sample:%lu frame:%lu ci.eFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); #endif } } #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp); #endif bool found = false; unsigned long frame = 0; unsigned long index = 0; unsigned long evframe; // Get all control ring buffer items valid for this time period... while(!_controlFifo.isEmpty()) { const ControlEvent& v = _controlFifo.peek(); // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", found, evframe, frame, v.frame, v.idx, v.value, v.unique); #endif // Protection. Observed this condition. Why? Supposed to be linear timestamps. if(found && evframe < frame) { fprintf(stderr, "DssiSynthIF::getData *** Error: Event out of order: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d syncFrame:%lu nframes:%u v.frame:%lu\n", evframe, frame, v.idx, v.value, v.unique, syncFrame, nframes, v.frame); // No choice but to ignore it. _controlFifo.remove(); // Done with the ring buffer's item. Remove it. continue; } if(evframe >= nframes // Next events are for a later period. || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all. break; if(v.idx >= in_ctrls) // Sanity check. { _controlFifo.remove(); // Done with the ring buffer's item. Remove it. break; } found = true; frame = evframe; index = v.idx; if(ports == 0) // Don't bother if not 'running'. _controls[v.idx].val = v.value; // Might as well at least update these. else { CtrlInterpolate* ci = &_controls[v.idx].interp; // Tell it to stop the current ramp at this frame, when it does stop, set this value: ci->eFrame = frame; ci->eFrameValid = true; ci->eVal = v.value; ci->eStop = true; } // Need to update the automation value, otherwise it overwrites later with the last automation value. if(plug_id != -1) synti->setPluginCtrlVal(genACnum(plug_id, v.idx), v.value); _controlFifo.remove(); // Done with the ring buffer's item. Remove it. } if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. nsamp = frame - sample; if(sample + nsamp > nframes) // Safety check. nsamp = nframes - sample; // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. // Note this means it is still possible to get stuck in the top loop (at least for a while). if(nsamp != 0) { unsigned long nevents = 0; // Get the state of the stop flag. const bool do_stop = synti->stopFlag(); MidiPlayEvent buf_ev; // Transfer the user lock-free buffer events to the user sorted multi-set. // False = don't use the size snapshot, but update it. const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); for(unsigned int i = 0; i < usr_buf_sz; ++i) { if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) synti->_outUserEvents.insert(buf_ev); } // Transfer the playback lock-free buffer events to the playback sorted multi-set. const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); for(unsigned int i = 0; i < pb_buf_sz; ++i) { // Are we stopping? Just remove the item. if(do_stop) synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); // Otherwise get the item. else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) synti->_outPlaybackEvents.insert(buf_ev); } // Are we stopping? if(do_stop) { // Transport has stopped, purge ALL further scheduled playback events now. synti->_outPlaybackEvents.clear(); // Reset the flag. synti->setStopFlag(false); } // Count how many events we need. for(ciMPEvent impe = synti->_outPlaybackEvents.begin(); impe != synti->_outPlaybackEvents.end(); ++impe) { const MidiPlayEvent& e = *impe; if(e.time() >= (syncFrame + sample + nsamp)) break; ++nevents; } for(ciMPEvent impe = synti->_outUserEvents.begin(); impe != synti->_outUserEvents.end(); ++impe) { const MidiPlayEvent& e = *impe; if(e.time() >= (syncFrame + sample + nsamp)) break; ++nevents; } snd_seq_event_t events[nevents]; iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); iMPEvent impe_us = synti->_outUserEvents.begin(); bool using_pb; unsigned long event_counter = 0; while(1) { if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) using_pb = *impe_pb < *impe_us; else if(impe_pb != synti->_outPlaybackEvents.end()) using_pb = true; else if(impe_us != synti->_outUserEvents.end()) using_pb = false; else break; const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us; #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::getData eventFifos event time:%d\n", e.time()); #endif if(e.time() >= (syncFrame + sample + nsamp)) break; if(ports != 0) // Don't bother if not 'running'. { // Returns false if the event was not filled. It was handled, but some other way. if(processEvent(e, &events[event_counter])) { // Time-stamp the event. unsigned int ft = (e.time() < syncFrame) ? 0 : e.time() - syncFrame; ft = (ft < sample) ? 0 : ft - sample; if (ft >= nsamp) { fprintf(stderr, "DssiSynthIF::getData: eventFifos event time:%d out of range. pos:%d syncFrame:%lu ft:%u sample:%lu nsamp:%lu\n", e.time(), pos, syncFrame, ft, sample, nsamp); ft = nsamp - 1; } // "Each event is timestamped relative to the start of the block, (mis)using the ALSA "tick time" field as a frame count. // The host is responsible for ensuring that events with differing timestamps are already ordered by time." - From dssi.h events[event_counter].time.tick = ft; ++event_counter; } } // Done with ring buffer's event. Remove it. // C++11. if(using_pb) impe_pb = synti->_outPlaybackEvents.erase(impe_pb); else impe_us = synti->_outUserEvents.erase(impe_us); } if(event_counter < nevents) nevents = event_counter; #ifdef DSSI_DEBUG_PROCESS fprintf(stderr, "DssiSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents); #endif if(ports != 0) // Don't bother if not 'running'. { // Connect the given buffers directly to the ports, up to a max of synth ports. for(unsigned long k = 0; k < nop; ++k) descr->connect_port(_handle, _synth->oIdx[k], buffer[k] + sample); // Connect the remaining ports to some local buffers (not used yet). for(unsigned long k = nop; k < out_ports; ++k) descr->connect_port(_handle, _synth->oIdx[k], _audioOutBuffers[k] + sample); // Connect all inputs either to some local buffers, or a silence buffer. for(unsigned long k = 0; k < in_ports; ++k) { if(used_in_chan_array[k]) descr->connect_port(_handle, _synth->iIdx[k], _audioInBuffers[k] + sample); else descr->connect_port(_handle, _synth->iIdx[k], _audioInSilenceBuf + sample); } // Run the synth for a period of time. This processes events and gets/fills our local buffers... if(_synth->dssi->run_synth) { _synth->dssi->run_synth(_handle, nsamp, events, nevents); } else if (_synth->dssi->run_multiple_synths) { snd_seq_event_t* ev = events; _synth->dssi->run_multiple_synths(1, &_handle, nsamp, &ev, &nevents); } // TIP: Until we add programs to plugins, uncomment these four checks to load dssi effects as synths, in order to have programs. //else //if(synth->dssi->LADSPA_Plugin->run) //{ // synth->dssi->LADSPA_Plugin->run(handle, nsamp); //} } sample += nsamp; } ++cur_slice; // Slice is done. Moving on to any next slice now... } return true; } //--------------------------------------------------------- // incInstances //--------------------------------------------------------- void DssiSynth::incInstances(int val) { _instances += val; if (_instances == 0) { if (handle) { #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynth::incInstances no more instances, closing library\n"); #endif dlclose(handle); } handle = 0; dssi = NULL; df = NULL; iIdx.clear(); oIdx.clear(); rpIdx.clear(); midiCtl2PortMap.clear(); port2MidiCtlMap.clear(); } } //--------------------------------------------------------- // guiHeartBeat //--------------------------------------------------------- void DssiSynthIF::guiHeartBeat() { #ifdef OSC_SUPPORT int chn = 0; // TODO: Channel? int hb, lb, pr; synti->currentProg(chn, &pr, &lb, &hb); if(hb > 127) // Map "dont care" to 0 hb = 0; if(lb > 127) lb = 0; if(pr > 127) pr = 0; // Update the gui's program if needed. _oscif.oscSendProgram(pr, (hb << 8) + lb); // Update the gui's controls if needed. unsigned long ports = _synth->_controlInPorts; for(unsigned long i = 0; i < ports; ++i) _oscif.oscSendControl(_controls[i].idx, _controls[i].val); #endif } #ifdef OSC_SUPPORT //--------------------------------------------------------- // oscUpdate //--------------------------------------------------------- int DssiSynthIF::oscUpdate() { // Send project directory. _oscif.oscSendConfigure(DSSI_PROJECT_DIRECTORY_KEY, MusEGlobal::museProject.toLatin1().constData()); // MusEGlobal::song->projectPath() // Send current string configuration parameters. int i = 0; for(ciStringParamMap r = synti->_initConfig._stringParamMap.begin(); r != synti->_initConfig._stringParamMap.end(); ++r) { _oscif.oscSendConfigure(r->first.c_str(), r->second.c_str()); // Avoid overloading the GUI if there are lots and lots of params. if((i+1) % 50 == 0) usleep(300000); ++i; } // Send current bank and program. int chn = 0; // TODO: Channel? int hb, lb, pr; synti->currentProg(chn, &pr, &lb, &hb); if(hb > 127) // Map "dont care" to 0 hb = 0; if(lb > 127) lb = 0; if(pr > 127) pr = 0; _oscif.oscSendProgram(pr, (hb << 8) + lb, true /*force*/); // Send current control values. unsigned long ports = _synth->_controlInPorts; for(unsigned long i = 0; i < ports; ++i) { _oscif.oscSendControl(_controls[i].idx, _controls[i].val, true /*force*/); // Avoid overloading the GUI if there are lots and lots of ports. if((i+1) % 50 == 0) usleep(300000); } return 0; } //--------------------------------------------------------- // oscProgram //--------------------------------------------------------- int DssiSynthIF::oscProgram(unsigned long program, unsigned long bank) { int ch = 0; // TODO: ?? int port = synti->midiPort(); // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. int hb = bank >> 8; int lb = bank & 0xff; if(hb > 127 || lb > 127 || program > 127) return 0; hb &= 0x7f; lb &= 0x7f; synti->setCurrentProg(ch, program, lb, hb); if(port != -1) { // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore. const MidiPlayEvent event(0, port, ch, ME_CONTROLLER, CTRL_PROGRAM, (hb << 16) | (lb << 8) | program); #ifdef DSSI_DEBUG fprintf(stderr, "DssiSynthIF::oscProgram midi event chn:%d a:%d b:%d\n", event.channel(), event.dataA(), event.dataB()); #endif MusEGlobal::midiPorts[port].putEvent(event); } return 0; } //--------------------------------------------------------- // oscControl //--------------------------------------------------------- int DssiSynthIF::oscControl(unsigned long port, float value) { #ifdef DSSI_DEBUG printf("DssiSynthIF::oscControl received oscControl port:%lu val:%f\n", port, value); #endif if(port >= _synth->rpIdx.size()) { fprintf(stderr, "DssiSynthIF::oscControl: port number:%lu is out of range of index list size:%zd\n", port, _synth->rpIdx.size()); return 0; } // Convert from DSSI port number to control input port index. unsigned long cport = _synth->rpIdx[port]; if((int)cport == -1) { fprintf(stderr, "DssiSynthIF::oscControl: port number:%lu is not a control input\n", port); return 0; } // Record automation: // Take care of this immediately, because we don't want the silly delay associated with // processing the fifo one-at-a-time in the apply(). // NOTE: With some vsts we don't receive control events until the user RELEASES a control. // So the events all arrive at once when the user releases a control. // That makes this pretty useless... But what the heck... if(id() != -1) { unsigned long pid = genACnum(id(), cport); synti->recordAutomation(pid, value); } // DELETETHIS????: is the below still correct? of so, then keep it of course! // p3.3.39 Set the DSSI control input port's value. // Observations: With a native DSSI synth like LessTrivialSynth, the native GUI's controls do not change the sound at all // ie. they don't update the DSSI control port values themselves. // Hence in response to the call to this oscControl, sent by the native GUI, it is required to that here. /// controls[cport].val = value; DELETETHIS // DSSI-VST synths however, unlike DSSI synths, DO change their OWN sound in response to their gui controls. // AND this function is called. // Despite the descrepency we are STILL required to update the DSSI control port values here // because dssi-vst is WAITING FOR A RESPONSE. (A CHANGE in the control port value). // It will output something like "...4 events expected..." and count that number down as 4 actual control port value CHANGES // are done here in response. Normally it says "...0 events expected..." when MusE is the one doing the DSSI control changes. // // NOTE: NOTE: This line in RemoteVSTServer::setParameter(int p, float v) in dssi-vst-server.cpp : // // " if (tv.tv_sec > m_lastGuiComms.tv_sec + 10) " // // explains an observation that after ten seconds, the server automatically clears the expected number to 0. // You can't send any 'new' values until either you a): send all the expected events or b): wait ten seconds. // (Because the server simply ignores the 'expected' messages.) // // Well, at least here are the fifos. Try this ... // Schedules a timed control change: ControlEvent ce; ce.unique = _synth->isDssiVst(); // Special for messages from vst gui to host - requires processing every message. ce.fromGui = true; // It came from the plugin's own GUI. ce.idx = cport; ce.value = value; // Don't use timestamp(), because it's circular, which is making it impossible to deal // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. ce.frame = MusEGlobal::audio->curFrame(); if(_controlFifo.put(ce)) fprintf(stderr, "DssiSynthIF::oscControl: fifo overflow: in control number:%lu\n", cport); enableController(cport, false); //TODO maybe re-enable the ctrl soon? return 0; } //--------------------------------------------------------- // oscMidi //--------------------------------------------------------- int DssiSynthIF::oscMidi(int a, int b, int c) { // From the DSSI RFC document: // /midi // "Send an arbitrary MIDI event to the plugin. Takes a four-byte MIDI string. // This is expected to be used for note data generated from a test panel on the UI, // for example. It should not be used for program or controller changes, sysex data, etc. // A host should feel free to drop any values it doesn't wish to pass on. // No guarantees are provided about timing accuracy, etc, of the MIDI communication." // From dssi.h: // " A host must not attempt to switch notes off by sending // zero-velocity NOTE_ON events. It should always send true // NOTE_OFFs. It is the host's responsibility to remap events in // cases where an external MIDI source has sent it zero-velocity // NOTE_ONs." int type = a & 0xf0; if (type == ME_NOTEON && c == 0) { type = ME_NOTEOFF; c = 64; } const int channel = a & 0x0f; const int port = synti->midiPort(); if(port != -1) { // Time-stamp the event. MidiPlayEvent event(MusEGlobal::audio->curFrame(), port, channel, type, b, c); #ifdef DSSI_DEBUG printf("DssiSynthIF::oscMidi midi event port:%d type:%d chn:%d a:%d b:%d\n", event.port(), event.type(), event.channel(), event.dataA(), event.dataB()); #endif // Just in case someone decides to send controllers, sysex, or stuff // OTHER than "test notes", contrary to the rules... // Since this is a thread other than audio or gui, it may not be safe to // even ask whether a controller exists, so MidiPort::putEvent or putHwCtrlEvent // would not be safe here. Ask the gui to do it for us. // Is it a controller message? Send the message to the gui. //if(event.translateCtrlNum() >= 0) MusEGlobal::song->putIpcInEvent(event); // Send the message to the device. if(MidiDevice* md = MusEGlobal::midiPorts[port].device()) md->putEvent(event, MidiDevice::Late); } return 0; } //--------------------------------------------------------- // oscConfigure //--------------------------------------------------------- int DssiSynthIF::oscConfigure(const char *key, const char *value) { // "The host has the option to remember the set of (key,value) // pairs associated with a particular instance, so that if it // wants to restore the "same" instance on another occasion it can // just call configure() on it for each of those pairs and so // restore state without any input from a GUI." #ifdef DSSI_DEBUG printf("DssiSynthIF::oscConfigure synth name:%s key:%s value:%s\n", synti->name().toLatin1().constData(), key, value); #endif // Add or modify the configuration map item. synti->_initConfig._stringParamMap.set(key, value); if (!strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, strlen(DSSI_RESERVED_CONFIGURE_PREFIX))) { fprintf(stderr, "MusE: OSC: UI for plugin '%s' attempted to use reserved configure key \"%s\", ignoring\n", synti->name().toLatin1().constData(), key); return 0; } if (!_synth->dssi->configure) return 0; char* message = _synth->dssi->configure(_handle, key, value); if (message) { printf("MusE: on configure '%s' '%s', plugin '%s' returned error '%s'\n", key, value, synti->name().toLatin1().constData(), message); free(message); } // DELETETHIS 6 ??? // "also call back on UIs for plugins other than the one // that requested this:" // if (n != instance->number && instances[n].uiTarget) { // lo_send(instances[n].uiTarget, // instances[n].ui_osc_configure_path, "ss", key, value); // } // configure invalidates bank and program information, so // we should do this again now: queryPrograms(); return 0; } #endif // OSC_SUPPORT //--------------------------------------------------------- // queryPrograms //--------------------------------------------------------- void DssiSynthIF::queryPrograms() { for (std::vector::const_iterator i = programs.begin(); i != programs.end(); ++i) { free((void*)(i->Name)); } programs.clear(); if (!_synth->dssi->get_program) return; for (int i = 0;; ++i) { const DSSI_Program_Descriptor* pd = _synth->dssi->get_program(_handle, i); if (pd == 0) break; // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. if((pd->Bank >> 8) > 127 || (pd->Bank & 0xff) > 127 || pd->Program > 127) continue; DSSI_Program_Descriptor d; d.Name = strdup(pd->Name); d.Program = pd->Program; d.Bank = pd->Bank; programs.push_back(d); } } void DssiSynthIF::doSelectProgram(LADSPA_Handle handle, int bankH, int bankL, int prog) { if(bankH > 127) // Map "dont care" to 0 bankH = 0; if(bankL > 127) bankL = 0; if(prog > 127) prog = 0; const int bank = (bankH << 8) | bankL; const DSSI_Descriptor* dssi = _synth->dssi; dssi->select_program(handle, bank, prog); // Need to update the automation value, otherwise it overwrites later with the last automation value. // "A plugin is permitted to re-write the values of its input control ports when select_program is called. // The host should re-read the input control port values and update its own records appropriately. // (This is the only circumstance in which a DSSI plugin is allowed to modify its own input ports.)" From dssi.h if(id() != -1) { for(unsigned long k = 0; k < _synth->_controlInPorts; ++k) { // We're in the audio thread context: no need to send a message, just modify directly. synti->setPluginCtrlVal(genACnum(id(), k), _controls[k].val); } } } //--------------------------------------------------------- // getPatchName //--------------------------------------------------------- QString DssiSynthIF::getPatchName(int /*chan*/, int prog, bool /*drum*/) const { unsigned program = prog & 0xff; unsigned lbank = (prog >> 8) & 0xff; unsigned hbank = (prog >> 16) & 0xff; if (program > 127) // Map "dont care" to 0 program = 0; if (lbank > 127) lbank = 0; if (hbank > 127) hbank = 0; const unsigned bank = (hbank << 8) + lbank; for (std::vector::const_iterator i = programs.begin(); i != programs.end(); ++i) { if (i->Bank == bank && i->Program ==program) return i->Name; } return "?"; } //--------------------------------------------------------- // populatePatchPopup //--------------------------------------------------------- void DssiSynthIF::populatePatchPopup(MusEGui::PopupMenu* menu, int /*ch*/, bool /*drum*/) { // The plugin can change the programs, patches etc. // So make sure we're up to date by calling queryPrograms. queryPrograms(); menu->clear(); for (std::vector::const_iterator i = programs.begin(); i != programs.end(); ++i) { // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. int hb = i->Bank >> 8; int lb = i->Bank & 0xff; if(hb > 127 || lb > 127 || i->Program > 127) continue; hb &= 0x7f; lb &= 0x7f; QString astr; astr += QString::number(hb + 1) + QString(":"); astr += QString::number(lb + 1) + QString(":"); astr += QString::number(i->Program + 1); astr += QString(" "); astr += QString(i->Name); QAction *act = menu->addAction(astr); act->setData((hb << 16) | (lb << 8) | (int)i->Program); } } int DssiSynthIF::getControllerInfo(int id, QString* name, int* ctrl, int* min, int* max, int* initval) { int controlPorts = _synth->_controlInPorts; if(id == controlPorts || id == controlPorts + 1) { // // It is unknown at this point whether or not a synth recognizes aftertouch and poly aftertouch // (channel and key pressure) midi messages, so add support for them now (as controllers). // if(id == controlPorts) *ctrl = CTRL_POLYAFTER; else if(id == controlPorts + 1) *ctrl = CTRL_AFTERTOUCH; *min = 0; *max = 127; *initval = CTRL_VAL_UNKNOWN; *name = midiCtrlName(*ctrl); return ++id; } else if(id >= controlPorts + 2) return 0; const DSSI_Descriptor* dssi = _synth->dssi; const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; unsigned long i = _controls[id].idx; #ifdef DSSI_DEBUG printf("DssiSynthIF::getControllerInfo control port:%d port idx:%lu name:%s\n", id, i, ld->PortNames[i]); #endif int ctlnum = DSSI_NONE; if(dssi->get_midi_controller_for_port) ctlnum = dssi->get_midi_controller_for_port(_handle, i); // No controller number? Give it one. if(ctlnum == DSSI_NONE) { // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + id; } else { #ifdef DSSI_DEBUG printf("DssiSynthIF::getControllerInfo ctlnum:%d\n", ctlnum); #endif int c = ctlnum; // Can be both CC and NRPN! Prefer CC over NRPN. if(DSSI_IS_CC(ctlnum)) { #ifdef DSSI_DEBUG printf("DssiSynthIF::getControllerInfo is CC control\n"); #endif ctlnum = DSSI_CC_NUMBER(c); #ifdef DSSI_DEBUG if(DSSI_IS_NRPN(ctlnum)) printf("DssiSynthIF::getControllerInfo is also NRPN control. Using CC.\n"); #endif } else if(DSSI_IS_NRPN(ctlnum)) { #ifdef DSSI_DEBUG printf("DssiSynthIF::getControllerInfo is NRPN control\n"); #endif ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; } } int def = CTRL_VAL_UNKNOWN; if(ladspa2MidiControlValues(ld, i, ctlnum, min, max, &def)) *initval = def; else *initval = CTRL_VAL_UNKNOWN; #ifdef DSSI_DEBUG printf("DssiSynthIF::getControllerInfo passed ctlnum:%d min:%d max:%d initval:%d\n", ctlnum, *min, *max, *initval); #endif *ctrl = ctlnum; *name = QString(ld->PortNames[i]); return ++id; } int DssiSynthIF::channels() const { return ((int)_synth->_outports) > MusECore::MAX_CHANNELS ? MusECore::MAX_CHANNELS : ((int)_synth->_outports) ; } int DssiSynthIF::totalOutChannels() const { return _synth->_outports; } int DssiSynthIF::totalInChannels() const { return _synth->_inports; } void DssiSynthIF::deactivate3() { deactivate(); } //-------------------------------- // Methods for PluginIBase: //-------------------------------- unsigned long DssiSynthIF::pluginID() { return (_synth && _synth->dssi) ? _synth->dssi->LADSPA_Plugin->UniqueID : 0; } int DssiSynthIF::id() { return MusECore::MAX_PLUGINS; } // Set for special block reserved for dssi synth. p4.0.20 QString DssiSynthIF::pluginLabel() const { return (_synth && _synth->dssi) ? QString(_synth->dssi->LADSPA_Plugin->Label) : QString(); } QString DssiSynthIF::lib() const { return _synth ? _synth->completeBaseName() : QString(); } QString DssiSynthIF::uri() const { return _synth ? _synth->uri() : QString(); } QString DssiSynthIF::dirPath() const { return _synth ? _synth->absolutePath() : QString(); } QString DssiSynthIF::fileName() const { return _synth ? _synth->fileName() : QString(); } void DssiSynthIF::enableController(unsigned long i, bool v) { _controls[i].enCtrl = v; } bool DssiSynthIF::controllerEnabled(unsigned long i) const { return _controls[i].enCtrl; } void DssiSynthIF::enableAllControllers(bool v) { if(!_synth) return; for(unsigned long i = 0; i < _synth->_controlInPorts; ++i) _controls[i].enCtrl = v; } void DssiSynthIF::updateControllers() { } void DssiSynthIF::activate() { if(_synth && _synth->dssi && _synth->dssi->LADSPA_Plugin && _synth->dssi->LADSPA_Plugin->activate) //for (int i = 0; i < instances; ++i) // _plugin->activate(handle[i]); _synth->dssi->LADSPA_Plugin->activate(_handle); // REMOVE Tim. Or keep? From PluginI::activate(). // if (initControlValues) { // for (unsigned long i = 0; i < controlPorts; ++i) { // controls[i].val = controls[i].tmpVal; // } // } // else { // // get initial control values from plugin // for (unsigned long i = 0; i < controlPorts; ++i) { // controls[i].tmpVal = controls[i].val; // } // } } void DssiSynthIF::deactivate() { if(!_synth || !_synth->dssi || !_synth->dssi->LADSPA_Plugin ||!_synth->dssi->LADSPA_Plugin->deactivate) return; //for (int i = 0; i < instances; ++i) // synth->dssi->LADSPA_Plugin->deactivate(handle[i]); _synth->dssi->LADSPA_Plugin->deactivate(_handle); } unsigned long DssiSynthIF::parameters() const { return _synth ? _synth->_controlInPorts : 0; } unsigned long DssiSynthIF::parametersOut() const { return _synth ? _synth->_controlOutPorts : 0; } void DssiSynthIF::setParam(unsigned long i, double val) { setParameter(i, val); } double DssiSynthIF::param(unsigned long i) const { return getParameter(i); } double DssiSynthIF::paramOut(unsigned long i) const { return getParameterOut(i); } const char* DssiSynthIF::paramName(unsigned long i) { return (_synth && _synth->dssi) ? _synth->dssi->LADSPA_Plugin->PortNames[_controls[i].idx] : 0; } const char* DssiSynthIF::paramOutName(unsigned long i) { return (_synth && _synth->dssi) ? _synth->dssi->LADSPA_Plugin->PortNames[_controlsOut[i].idx] : 0; } LADSPA_PortRangeHint DssiSynthIF::range(unsigned long i) { return _synth->dssi->LADSPA_Plugin->PortRangeHints[_controls[i].idx]; } LADSPA_PortRangeHint DssiSynthIF::rangeOut(unsigned long i) { return _synth->dssi->LADSPA_Plugin->PortRangeHints[_controlsOut[i].idx]; } CtrlValueType DssiSynthIF::ctrlValueType(unsigned long i) const { return ladspaCtrlValueType(_synth->dssi->LADSPA_Plugin, _controls[i].idx); } CtrlList::Mode DssiSynthIF::ctrlMode(unsigned long i) const { return ladspaCtrlMode(_synth->dssi->LADSPA_Plugin, _controls[i].idx); }; } // namespace MusECore #else //DSSI_SUPPORT namespace MusECore { void initDSSI() {} } #endif muse-4.0.0/src/muse/dssihost.h000066400000000000000000000200711404105311000162220ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // $Id: dssihost.h,v 1.10.2.7 2009/12/06 10:05:00 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __DSSIHOST_H__ #define __DSSIHOST_H__ #include "config.h" // Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. // 127 is reserved for special MusE system messages. #define DSSI_SYNTH_UNIQUE_ID 8 // Midistate sysex initialization command. #define DSSI_INIT_DATA_CMD 1 #define DSSI_PARAMSAVE_VERSION_MAJOR 0 #define DSSI_PARAMSAVE_VERSION_MINOR 1 #ifdef DSSI_SUPPORT #include #include #include #ifdef OSC_SUPPORT #include #include "osc.h" #endif #include #include #include #include "globaldefs.h" #include "midictrl.h" #include "synth.h" #include "stringparam.h" #include "plugin.h" #include "plugin_list.h" //#include #include "popupmenu.h" #endif // DSSI_SUPPORT namespace MusECore { #ifdef DSSI_SUPPORT struct _DSSI; class DssiPluginIF; struct Port; //--------------------------------------------------------- // DssiSynth //--------------------------------------------------------- class DssiSynth : public Synth { protected: void* handle; const DSSI_Descriptor* dssi; DSSI_Descriptor_Function df; unsigned long _portCount, _inports, _outports, _controlInPorts, _controlOutPorts; std::vector iIdx; // Audio input index to port number. std::vector oIdx; // Audio output index to port number. std::vector rpIdx; // Port number to control input index. Item is -1 if it's not a control input. MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to DSSI port numbers. MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps DSSI port numbers to midi controller numbers. bool _hasGui; // Hack: Special flag required. bool _isDssiVst; public: DssiSynth(QFileInfo&, const QString& uri, const DSSI_Descriptor*, bool isDssiVst = false, PluginFeatures_t reqFeatures = PluginNoFeatures); // removed const for QFileInfo DssiSynth(const MusEPlugin::PluginScanInfoStruct& info); virtual ~DssiSynth(); virtual Type synthType() const { return DSSI_SYNTH; } virtual void incInstances(int); virtual SynthIF* createSIF(SynthI*); friend class DssiSynthIF; unsigned long inPorts() const { return _inports; } unsigned long outPorts() const { return _outports; } unsigned long inControls() const { return _controlInPorts; } unsigned long outControls() const { return _controlOutPorts; } bool isDssiVst() const { return _isDssiVst; } const std::vector* getRpIdx() { return &rpIdx; } }; //--------------------------------------------------------- // DssiSynthIF // VSTi synthesizer instance //--------------------------------------------------------- class DssiSynthIF : public SynthIF { DssiSynth* _synth; LADSPA_Handle _handle; Port* _controls; Port* _controlsOut; bool _hasLatencyOutPort; unsigned long _latencyOutPort; #ifdef OSC_SUPPORT OscDssiIF _oscif; #endif std::vector programs; void queryPrograms(); void doSelectProgram(LADSPA_Handle handle, int bankH, int bankL, int prog); bool processEvent(const MidiPlayEvent&, snd_seq_event_t*); float** _audioInBuffers; float** _audioOutBuffers; float* _audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. public: DssiSynthIF(SynthI* s); // This is only a kludge required to support old songs' midistates. Do not use in any new synth. virtual int oldMidiStateHeader(const unsigned char** data) const; virtual ~DssiSynthIF(); bool init(DssiSynth* s); virtual DssiSynth* dssiSynth() { return _synth; } virtual SynthI* dssiSynthI() { return synti; } virtual void guiHeartBeat(); virtual bool hasGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool); virtual bool hasNativeGui() const { return !dssi_ui_filename().isEmpty(); } virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } virtual void setNativeGeometry(int, int, int, int) {} virtual bool getData(MidiPort*, unsigned pos, int ports, unsigned n, float** buffer); virtual MidiPlayEvent receiveEvent(); virtual int eventsPending() const { return 0; } virtual int channels() const; virtual int totalOutChannels() const; virtual int totalInChannels() const; virtual void deactivate3(); virtual QString getPatchName(int, int, bool) const; virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool); virtual void write(int level, Xml& xml) const; virtual double getParameter(unsigned long /*idx*/) const; virtual double getParameterOut(unsigned long n) const; virtual void setParameter(unsigned long /*idx*/, double /*value*/); virtual int getControllerInfo(int, QString*, int*, int*, int*, int*); #ifdef OSC_SUPPORT OscDssiIF& oscIF() { return _oscif; } int oscProgram(unsigned long prog, unsigned long bank); int oscControl(unsigned long dssiPort, float val); int oscMidi(int a, int b, int c); int oscConfigure(const char *key, const char *val); int oscUpdate(); #endif //------------------------- // Methods for PluginIBase: //------------------------- unsigned long pluginID(); int id(); QString pluginLabel() const; QString lib() const; QString uri() const; QString dirPath() const; QString fileName() const; void enableController(unsigned long i, bool v = true); bool controllerEnabled(unsigned long i) const; void enableAllControllers(bool v = true); void updateControllers(); void activate(); void deactivate(); unsigned long parameters() const; unsigned long parametersOut() const; void setParam(unsigned long i, double val); double param(unsigned long i) const; double paramOut(unsigned long i) const; const char* paramName(unsigned long i); const char* paramOutName(unsigned long i); LADSPA_PortRangeHint range(unsigned long i); LADSPA_PortRangeHint rangeOut(unsigned long i); bool hasLatencyOutPort() const; unsigned long latencyOutPortIndex() const; float latency() const; CtrlValueType ctrlValueType(unsigned long i) const; CtrlList::Mode ctrlMode(unsigned long i) const; friend class DssiSynth; }; #endif // DSSI_SUPPORT extern void initDSSI(); } // namespace MusECore #endif muse-4.0.0/src/muse/event.cpp000066400000000000000000000322411404105311000160400ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: event.cpp,v 1.8.2.5 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "event.h" #include "waveevent.h" #include "midievent.h" #include "midi_consts.h" #include "part.h" // Forwards from header: #include "eventbase.h" #include "part.h" #include "xml.h" #include "audio_fifo.h" namespace MusECore { EventID_t EventBase::idGen=0; //--------------------------------------------------------- // Event //--------------------------------------------------------- EventBase::EventBase(EventType t) { _type = t; Pos::setType(_type == Wave ? FRAMES : TICKS); refCount = 0; _selected = false; _uniqueId = newId(); _id = _uniqueId; } EventBase::EventBase(const EventBase& ev, bool duplicate_not_clone) : PosLen(ev) { refCount = 0; _selected = ev._selected; _type = ev._type; _uniqueId = newId(); _id = duplicate_not_clone ? _uniqueId : ev._id; } void EventBase::assign(const EventBase& ev) { if(this == &ev) // Is it a shared clone? return; if(ev.type() != _type) return; (PosLen&)(*this) = (const PosLen&)ev; setSelected(ev.selected()); } //--------------------------------------------------------- // move //--------------------------------------------------------- void EventBase::move(int tickOffset) { setTick(tick() + tickOffset); } bool EventBase::isSimilarType(const EventBase& e, bool compareTime, bool compareA, bool compareB, bool compareC, bool compareWavePath, bool compareWavePos, bool compareWaveStartPos) const { // Types must be the same. if((e.type() != type()) || // Compares base Pos positions. (compareTime && *this != e)) return false; switch(type()) { case Note: // Are the note, and on and off velocities the same? return (!compareA || e.dataA() == dataA()) && (!compareB || e.dataB() == dataB()) && (!compareC || e.dataC() == dataC()); break; case Controller: // Are the controller numbers and values the same? return (!compareA || e.dataA() == dataA()) && (!compareB || e.dataB() == dataB()); break; case Sysex: case Meta: // Are the sysex or meta length and data the same? return dataLen() == e.dataLen() && (dataLen() == 0 || (memcmp( constData(), e.constData(), dataLen()) == 0)); break; case Wave: // Are the sound file path, wave starting position and event position and length the same? return (!compareWavePos || this->PosLen::operator==(e)) && (!compareWaveStartPos || spos() == e.spos()) && (!compareWavePath || sndFile().dirPath() == e.sndFile().dirPath()); break; } return false; } //--------------------------------------------------------- // dump //--------------------------------------------------------- void EventBase::dump(int n) const { for (int i = 0; i < n; ++i) putchar(' '); printf("Event %p refs:%d ", this, refCount); PosLen::dump(n+2); } //--------------------------------------------------------- // duplicate //--------------------------------------------------------- Event Event::duplicate() const { return ev ? Event(ev->duplicate()) : Event(); } //--------------------------------------------------------- // clone //--------------------------------------------------------- Event Event::clone() const { return ev ? Event(ev->clone()) : Event(); } //--------------------------------------------------------- // deClone //--------------------------------------------------------- void Event::deClone() { if(ev) ev->deClone(); } Event::Event() { ev = 0; } Event::Event(EventType t) { if (t == Wave) ev = new MusECore::WaveEventBase(t); else ev = new MusECore::MidiEventBase(t); ++(ev->refCount); } Event::Event(const Event& e) { ev = e.ev; if(ev) ++(ev->refCount); } Event::Event(EventBase* eb) { ev = eb; if(ev) ++(ev->refCount); } Event::~Event() { if (ev && --(ev->refCount) == 0) { delete ev; ev=0; } } MidiPlayEvent Event::asMidiPlayEvent(unsigned time, int port, int channel) const { MidiPlayEvent mpe; mpe.setChannel(channel); mpe.setTime(time); mpe.setPort(port); mpe.setLoopNum(0); switch(type()) { case Note: mpe.setType(ME_NOTEON); mpe.setA(dataA()); mpe.setB(dataB()); break; case Controller: mpe.setType(ME_CONTROLLER); mpe.setA(dataA()); // controller number mpe.setB(dataB()); // controller value break; case Sysex: mpe.setType(ME_SYSEX); mpe.setData(eventData()); break; default: fprintf(stderr, "Event::asMidiPlayEvent: event type %d not implemented\n", type()); break; } return mpe; } bool Event::empty() const { return ev == 0; } EventType Event::type() const { return ev ? ev->type() : Note; } EventID_t Event::id() const { return ev ? ev->id() : MUSE_INVALID_EVENT_ID; } void Event::shareId(const Event& e) { if(ev && e.ev) ev->shareId(e.ev); } void Event::setType(EventType t) { if (ev && --(ev->refCount) == 0) { delete ev; ev = 0; } if (t == Wave) ev = new MusECore::WaveEventBase(t); else ev = new MusECore::MidiEventBase(t); ++(ev->refCount); } Event& Event::operator=(const Event& e) { if (ev != e.ev) { if (ev && --(ev->refCount) == 0) { delete ev; ev = 0; } ev = e.ev; if (ev) ++(ev->refCount); } return *this; } void Event::assign(const Event& e) { if(!ev || !e.ev || ev == e.ev) return; ev->assign(*(e.ev)); } bool Event::operator==(const Event& e) const { return ev == e.ev; } bool Event::isSimilarTo(const Event& other) const { return ev ? ev->isSimilarTo(*other.ev) : (other.ev ? false : true); } bool Event::isSimilarType(const Event& e, bool compareTime, bool compareA, bool compareB, bool compareC, bool compareWavePath, bool compareWavePos, bool compareWaveStartPos) const { return ev ? ev->isSimilarType(*e.ev, compareTime, compareA, compareB, compareC, compareWavePath, compareWavePos, compareWaveStartPos) : (e.ev ? false : true); } int Event::getRefCount() const { return ev ? ev->getRefCount() : 0; } bool Event::selected() const { return ev ? ev->_selected : false; } void Event::setSelected(bool val) { if(ev) ev->_selected = val; } void Event::move(int offset) { if(ev) ev->move(offset); } void Event::read(Xml& xml) { if(ev) ev->read(xml); } void Event::write(int a, Xml& xml, const Pos& o, bool forceWavePaths) const { if(ev) ev->write(a, xml, o, forceWavePaths); } void Event::dump(int n) const { if(ev) ev->dump(n); } Event Event::mid(unsigned a, unsigned b) const { return ev ? Event(ev->mid(a, b)) : Event(); } bool Event::isNote() const { return ev ? ev->isNote() : false; } bool Event::isNoteOff() const { return ev ? ev->isNoteOff() : false; } bool Event::isNoteOff(const Event& e) const { return ev ? (e.isNoteOff() && (e.pitch() == dataA())) : false; } int Event::dataA() const { return ev ? ev->dataA() : 0; } int Event::pitch() const { return ev ? ev->dataA() : 0; } void Event::setA(int val) { if(ev) ev->setA(val); } void Event::setPitch(int val) { if(ev) ev->setA(val); } int Event::dataB() const { return ev ? ev->dataB() : 0; } int Event::velo() const { return ev ? ev->dataB() : 0; } void Event::setB(int val) { if(ev) ev->setB(val); } void Event::setVelo(int val) { if(ev) ev->setB(val); } int Event::dataC() const { return ev ? ev->dataC() : 0; } int Event::veloOff() const { return ev ? ev->dataC() : 0; } void Event::setC(int val) { if(ev) ev->setC(val); } void Event::setVeloOff(int val) { if(ev) ev->setC(val); } const unsigned char* Event::data() const { return ev ? ev->constData() : 0; } int Event::dataLen() const { return ev ? ev->dataLen() : 0; } void Event::setData(const unsigned char* data, int len) { if(ev) ev->setData(data, len); } const EvData Event::eventData() const { return ev ? ev->eventData() : EvData(); } const QString Event::name() const { return ev ? ev->name() : QString(); } void Event::setName(const QString& s) { if(ev) ev->setName(s); } int Event::spos() const { return ev ? ev->spos() : 0; } void Event::setSpos(int s) { if(ev) ev->setSpos(s); } MusECore::SndFileR Event::sndFile() const { return ev ? ev->sndFile() : MusECore::SndFileR(); } void Event::setSndFile(MusECore::SndFileR& sf) { if(ev) ev->setSndFile(sf); } void Event::readAudio(unsigned offset, float** bpp, int channels, int nn, bool doSeek, bool overwrite) { if(ev) ev->readAudio(offset, bpp, channels, nn, doSeek, overwrite); } void Event::seekAudio(sf_count_t offset) { if(ev) ev->seekAudio(offset); } Fifo* Event::audioPrefetchFifo() { return ev ? ev->audioPrefetchFifo() : 0; } void Event::prefetchAudio(Part* part, sf_count_t frames) { if(ev) ev->prefetchAudio(part, frames); } //-------------------------------------------------------- // 'Agnostic' position methods - can be TICKS and FRAMES. //-------------------------------------------------------- Pos Event::pos() const { return ev ? *ev : Pos(); } void Event::setPos(const Pos& p) { if(ev) ev->setPos(p); } Pos Event::end() const { return ev ? ev->end() : Pos(); } PosLen Event::posLen() const { return ev ? *ev : PosLen(); } unsigned Event::posValue() const { return ev ? ev->posValue() : 0; } unsigned Event::posValue(Pos::TType time_type) const { return ev ? ev->posValue(time_type) : 0; } void Event::setPosValue(unsigned val) { if(ev) ev->setPosValue(val); } void Event::setPosValue(unsigned val, Pos::TType time_type) { if(ev) ev->setPosValue(val, time_type); } unsigned Event::lenValue() const { return ev ? ev->lenValue() : 0; } unsigned Event::lenValue(Pos::TType time_type) const { return ev ? ev->lenValue(time_type) : 0; } void Event::setLenValue(unsigned val) { if(ev) ev->setLenValue(val); } void Event::setLenValue(unsigned val, Pos::TType time_type) { if(ev) ev->setLenValue(val, time_type); } unsigned Event::endPosValue() const { return ev ? ev->endValue() : 0; } //-------------------------------------------------------- // 'Resolving' position methods - must be TICKS or FRAMES. //-------------------------------------------------------- unsigned Event::tick() const { return ev ? ev->tick() : 0; } void Event::setTick(unsigned val) { if(ev) ev->setTick(val); } unsigned Event::frame() const { return ev ? ev->frame() : 0; } void Event::setFrame(unsigned val) { if(ev) ev->setFrame(val); } unsigned Event::lenTick() const { return ev ? ev->lenTick() : 0; } void Event::setLenTick(unsigned val) { if(ev) ev->setLenTick(val); } unsigned Event::lenFrame() const { return ev ? ev->lenFrame() : 0; } void Event::setLenFrame(unsigned val) { if(ev) ev->setLenFrame(val); } unsigned Event::endTick() const { return ev ? ev->end().tick() : 0; } unsigned Event::endFrame() const { return ev ? ev->end().frame() : 0; } } // namespace MusECore muse-4.0.0/src/muse/event.h000066400000000000000000000254141404105311000155110ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: event.h,v 1.7.2.4 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __EVENT_H__ #define __EVENT_H__ #include #include #include #include #include #include "type_defs.h" #include "pos.h" #include "mpevent.h" #include "wave.h" #include "config.h" namespace MusECore { // Forward declarations: class EventBase; class Part; class Xml; class Fifo; //--------------------------------------------------------- // Event //--------------------------------------------------------- class Event { EventBase* ev; public: Event(); Event(EventType t); Event(const Event& e); // Creates a true shared clone of the event. They share the same event base pointer. Event(EventBase* eb); // Wraps an event base in an event and increases the event base's ref count. virtual ~Event(); MidiPlayEvent asMidiPlayEvent(unsigned time, int port, int channel) const; bool empty() const; EventType type() const; // Shared and non-shared clone events have the same id. An empty event returns MUSE_INVALID_EVENT_ID. EventID_t id() const; void shareId(const Event& e); // Makes id same as given event's. Effectively makes the events non-shared clones. void setType(EventType t); Event& operator=(const Event& e); // Makes the two events true shared clones. They share the same event base pointer. virtual void assign(const Event&); // Assigns to this event, excluding the id. Including its clones. bool operator==(const Event& e) const; bool isSimilarTo(const Event& other) const; bool isSimilarType(const Event&, bool compareTime = false, bool compareA = false, bool compareB = false, bool compareC = false, bool compareWavePath = false, bool compareWavePos = false, bool compareWaveStartPos = false) const; int getRefCount() const; bool selected() const; void setSelected(bool val); void move(int offset); void read(Xml& xml); void write(int a, Xml& xml, const Pos& offset, bool ForceWavePaths = false) const; void dump(int n = 0) const; // Creates a non-shared clone of the event base, having the same 'group' id. // NOTE: Certain pointer members may still be SHARED. Such as the sysex MidiEventBase::edata. // Be aware when iterating or modifying clones. Event clone() const; // Restores id to original uniqueId, removing the event from any clone 'group'. void deClone(); // Creates a copy of the event base, excluding the 'group' _id. Event duplicate() const; Event mid(unsigned a, unsigned b) const; bool isNote() const; bool isNoteOff() const; bool isNoteOff(const Event& e) const; int dataA() const; int pitch() const; void setA(int val); void setPitch(int val); int dataB() const; int velo() const; void setB(int val); void setVelo(int val); int dataC() const; int veloOff() const; void setC(int val); void setVeloOff(int val); const unsigned char* data() const; int dataLen() const; void setData(const unsigned char* data, int len); const EvData eventData() const; const QString name() const; void setName(const QString& s); int spos() const; void setSpos(int s); MusECore::SndFileR sndFile() const; virtual void setSndFile(MusECore::SndFileR& sf); virtual void readAudio(unsigned offset, float** bpp, int channels, int nn, bool doSeek, bool overwrite); virtual void seekAudio(sf_count_t offset); virtual Fifo* audioPrefetchFifo(); virtual void prefetchAudio(Part* part, sf_count_t frames); //-------------------------------------------------------- // 'Agnostic' position methods - can be TICKS and FRAMES. //-------------------------------------------------------- Pos pos() const; void setPos(const Pos& p); Pos end() const; PosLen posLen() const; unsigned posValue() const; unsigned posValue(Pos::TType time_type) const; void setPosValue(unsigned val); void setPosValue(unsigned val, Pos::TType time_type); unsigned lenValue() const; unsigned lenValue(Pos::TType time_type) const; void setLenValue(unsigned val); void setLenValue(unsigned val, Pos::TType time_type); unsigned endPosValue() const; //-------------------------------------------------------- // 'Resolving' position methods - must be TICKS or FRAMES. //-------------------------------------------------------- unsigned tick() const; void setTick(unsigned val); unsigned frame() const; void setFrame(unsigned val); unsigned lenTick() const; void setLenTick(unsigned val); unsigned lenFrame() const; void setLenFrame(unsigned val); unsigned endTick() const; unsigned endFrame() const; }; typedef std::pair FindMidiCtlsPair_t; typedef std::map > FindMidiCtlsList_t; typedef FindMidiCtlsList_t::iterator iFindMidiCtlsList; typedef FindMidiCtlsList_t::const_iterator ciFindMidiCtlsList; typedef std::pair FindMidiCtlsListInsResPair_t; #ifdef ALLOW_LEFT_HIDDEN_EVENTS // NOTE: Signed integer comparison (at least) to support sorting negative event times. // Really need to change the whole thing to signed. typedef std::multimap > EL; #else typedef std::multimap > EL; #endif typedef EL::iterator iEvent; typedef EL::const_iterator ciEvent; typedef std::pair cEventRange; typedef std::pair EventRange; //--------------------------------------------------------- // EventList // tick sorted list of events //--------------------------------------------------------- class EventList : public EL { public: // Looks for specific event (EventBase pointer). ciEvent find(const Event&) const; iEvent find(const Event&); // Looks for events with identical values, but not necessarily EventBase pointers. ciEvent findSimilar(const Event&) const; iEvent findSimilar(const Event&); // Looks for events with identical type, and identical note or controller number, or // sysex or meta length and identical data, or wave path and start postion and event position. // Adds found items to the given list. Does not clear the list first. Returns the number of items added. int findSimilarType(const Event&, EventList&, bool compareTime = false, bool compareA = false, bool compareB = false, bool compareC = false, bool compareWavePath = false, bool compareWavePos = false, bool compareWaveStartPos = false) const; ciEvent findId(const Event&) const; // Fast, index t is known. iEvent findId(const Event&); // Fast, index t is known. ciEvent findId(unsigned t, EventID_t id) const; // Fast, index t is known. iEvent findId(unsigned t, EventID_t id); // Fast, index t is known. ciEvent findId(EventID_t id) const; // Slow, index t is not known iEvent findId(EventID_t id); // Slow, index t is not known ciEvent findWithId(const Event&) const; // Finds event base or event id. Fast, index t is known. iEvent findWithId(const Event&); // Finds event base or event id. Fast, index t is known. // Returns an iterator that points to the inserted event. // Returns end() if an error occurred. // REMOVE Tim. Ctrl. Added comment. // NOTE: It is FORBIDDEN to have multiple controller events at the same time with the same controller number. // To preserve speed, we RELY on catching that at higher levels like the add event dialog or during song loading. // See detailed comments in add(). iEvent add(Event event); void move(Event& event, unsigned tick); void dump() const; void read(Xml& xml, const char* name, bool midi); // Returns the exents of the contents of the list, as a PosLen. // If wave is true it only looks at wave events, otherwise it // only looks at midi events. It also looks only for relevant. // If ctrlNum is not -1, it looks only for that controller number. // The returned PosLen is in units of frames or ticks respective of wave. // numEvents indicates the number of events found and whether PosLen is valid. PosLen evrange(bool wave, RelevantSelectedEvents_t relevant, int* numEvents, int ctrlNum = -1) const; // Fills set with the different controller numbers found in the event list. // Looks for midi controller events, or wave controller events if wave is true (does nothing ATM). // If findCtl is given it finds that specific controller. // Otherwise if findCtl -1 it finds all controllers. void findControllers(bool wave, FindMidiCtlsList_t* outList, int findCtl = -1) const; // REMOVE Tim. ctrl. Added. // If event is a controller, returns an iterator to the event if found, otherwise returns end. // Note: It is FORBIDDEN to have multiple controller events at the same time with the same controller number. ciEvent findControllerAt(const Event& event) const; iEvent findControllerAt(const Event& event); // REMOVE Tim. ctrl. Added. // Returns true if the event is a controller event and its controller number // already exists at the given time, regardless of value. bool controllerValueExists(const Event& event) const; }; } // namespace MusECore #endif muse-4.0.0/src/muse/event_tag_list.cpp000066400000000000000000000143621404105311000177320ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // event_tag_list.cpp // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "event_tag_list.h" namespace MusECore { //---------------------------------- // TagEventStatsStruct //---------------------------------- void TagEventStatsStruct::add(const Event& e) { //if(e.empty()) // return; switch(e.type()) { case Note: if(_notes == 0 || (e.pos() < _noteRange)) _noteRange.setPos(e.pos()); if(_notes == 0 || (e.end() > _noteRange.end())) _noteRange.setEnd(e.end()); ++_notes; break; case Wave: if(_waves == 0 || (e.pos() < _waveRange)) _waveRange.setPos(e.pos()); if(_waves == 0 || (e.end() > _waveRange.end())) _waveRange.setEnd(e.end()); ++_waves; break; case Controller: if(_midiCtrls == 0 || (e.pos() < _midiCtrlRange)) _midiCtrlRange.setPos(e.pos()); // For these events, minimum 1 unit time, to qualify as a valid 'range'. if(_midiCtrls == 0 || (e.posValue() + 1 > _midiCtrlRange.endValue())) _midiCtrlRange.setEndValue(e.posValue() + 1); ++_midiCtrls; break; case Sysex: if(_sysexes == 0 || (e.pos() < _sysexRange)) _sysexRange.setPos(e.pos()); // For these events, minimum 1 unit time, to qualify as a valid 'range'. if(_sysexes == 0 || (e.posValue() + 1 > _sysexRange.endValue())) _sysexRange.setEndValue(e.posValue() + 1); ++_sysexes; break; case Meta: if(_metas == 0 || (e.pos() < _metaRange)) _metaRange.setPos(e.pos()); // For these events, minimum 1 unit time, to qualify as a valid 'range'. if(_metas == 0 || (e.posValue() + 1 > _metaRange.endValue())) _metaRange.setEndValue(e.posValue() + 1); ++_metas; break; } } PosLen TagEventStatsStruct::evrange(const RelevantSelectedEvents_t& types) const { // If wave events are included the result is in FRAMES. // Otherwise the result is in TICKS. PosLen pl(_waves == 0 || !(types & WaveRelevant)); bool first = true; if(types & NotesRelevant) { if(_notes > 0) { if(first || pl > _noteRange) pl.setPos(_noteRange); if(first || pl.end() < _noteRange.end()) pl.setEnd(_noteRange.end()); first = false; } } if(types & ControllersRelevant) { if(_midiCtrls > 0) { if(first || pl > _midiCtrlRange) pl.setPos(_midiCtrlRange); if(first || pl.end() < _midiCtrlRange.end()) pl.setEnd(_midiCtrlRange.end()); first = false; } } if(types & SysexRelevant) { if(_sysexes > 0) { if(first || pl > _sysexRange) pl.setPos(_sysexRange); if(first || pl.end() < _sysexRange.end()) pl.setEnd(_sysexRange.end()); first = false; } } if(types & MetaRelevant) { if(_metas > 0) { if(first || pl > _metaRange) pl.setPos(_metaRange); if(first || pl.end() < _metaRange.end()) pl.setEnd(_metaRange.end()); first = false; } } if(types & WaveRelevant) { if(_waves > 0) { if(first || pl > _waveRange) pl.setPos(_waveRange); if(first || pl.end() < _waveRange.end()) pl.setEnd(_waveRange.end()); first = false; } } return pl; } //---------------------------------- // TagEventListStruct //---------------------------------- bool TagEventListStruct::add(const Event& e) { //if(e.empty()) // return _evlist.end(); // REMOVE Tim. Ctrl. Changed. // _stats.add(e); // return _evlist.add(e) != _evlist.end(); const bool res = _evlist.add(e) != _evlist.end(); if(res) _stats.add(e); return res; } //---------------------------------- // TagEventList //---------------------------------- bool TagEventList::add(const Part* part, const Event& event) { // If the event is given, do not allow clone events to be added. // We allow clone parts to be in the list here just in case by // some mistake an event is included in one part but not another. // But normally we should ignore simply if any clone part is found, // because they're all supposed to be IDENTICAL, but just to be safe // we'll check the event lists so we don't miss anything ... if(!event.empty()) { TagEventListStruct* found_part_el = NULL; iTagEventList itl = begin(); for( ; itl != end(); ++itl) { const Part* p = itl->first; // Is the event or a clone of the event already listed in this part? const EventList& el = itl->second.evlist(); ciEvent ie = el.findWithId(event); if(ie != el.cend()) return false; if(p == part) found_part_el = &itl->second; } if(!found_part_el) { TagEventListInsertResultPair_t ires = insert(TagEventListPair_t(part, TagEventListStruct())); if(!ires.second) return false; found_part_el = &ires.first->second; } if(!found_part_el->add(event)) return false; _globalStats.add(event); return true; } else { TagEventListInsertResultPair_t ires = insert(TagEventListPair_t(part, TagEventListStruct())); return ires.second; } return false; } void TagEventList::globalCtlStats(FindMidiCtlsList_t* tclist, int findCtl) const { for(ciTagEventList itl = cbegin(); itl != cend(); ++itl) { const TagEventListStruct& tel = itl->second; tel.evlist().findControllers(false, tclist, findCtl); } } } // namespace MusECore muse-4.0.0/src/muse/event_tag_list.h000066400000000000000000000137251404105311000174010ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // event_tag_list.h // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __EVENT_TAG_LIST_H__ #define __EVENT_TAG_LIST_H__ #include #include #include "part.h" #include "event.h" #include "pos.h" #include "type_defs.h" namespace MusECore { class TagEventStatsStruct { private: unsigned int _notes; unsigned int _midiCtrls; unsigned int _sysexes; unsigned int _metas; unsigned int _waves; PosLen _noteRange; PosLen _midiCtrlRange; PosLen _sysexRange; PosLen _metaRange; PosLen _waveRange; public: TagEventStatsStruct() : _notes(0), _midiCtrls(0), _sysexes(0), _metas(0), _waves(0), _waveRange(false /* use Pos::FRAMES */) {} // Be sure to use this instead of EventList::add(), to use the statistics feature. void add(const Event& e); //-------------- // Statistics: //-------------- unsigned int notes() const { return _notes; } unsigned int mctrls() const { return _midiCtrls; } unsigned int sysexes() const { return _sysexes; } unsigned int metas() const { return _metas; } unsigned int waves() const { return _waves; } PosLen noteRange() const { return _noteRange; } PosLen midiCtrlRange() const { return _midiCtrlRange; } PosLen sysexRange() const { return _sysexRange; } PosLen metaRange() const { return _metaRange; } PosLen waveRange() const { return _waveRange; } PosLen evrange(const RelevantSelectedEvents_t& types = AllEventsRelevant) const; }; //------------------------------------------- // TagEventListStruct // Basically meant to be filled once then discarded. // Removing items would not be of much use since the // statistics would have to recomputed each time. //------------------------------------------- class TagEventListStruct { private: EventList _evlist; TagEventStatsStruct _stats; public: TagEventListStruct() {} EventList& evlist() { return _evlist; } const EventList& evlist() const { return _evlist; } // Be sure to use this instead of EventList::add(), to use the statistics feature. // Returns true if successfully added. bool add(const Event& e); //-------------- // Statistics: //-------------- const TagEventStatsStruct& stats() const { return _stats; } }; typedef std::pair TagEventListPair_t; typedef std::map > TagEventList_t; class TagEventList : public TagEventList_t { private: TagEventStatsStruct _globalStats; public: TagEventList() { } // Adds a part and optionally an event. // Returns true if successfully added. bool add(const Part*, const Event& = Event()); //-------------- // Statistics: //-------------- const TagEventStatsStruct& globalStats() const { return _globalStats; } // Fills tclist with info about found controllers. // If findCtl is given it finds that specific controller. // Otherwise if findCtl -1 it finds all controllers. void globalCtlStats(FindMidiCtlsList_t* tclist, int findCtl = -1) const; }; typedef TagEventList::const_iterator ciTagEventList; typedef TagEventList::iterator iTagEventList; typedef std::pair TagEventListInsertResultPair_t; //-------------------------------------------------------------- // Event tagging flags and structure for the tagging and // copying/pasting system. //-------------------------------------------------------------- enum EventTagOptions { TagNoOptions = 0x00, // Tag selected items. TagSelected = 0x01, // Tag moving items. TagMoving = 0x02, TagSelectedAndMoving = TagSelected | TagMoving, // Whether to tag all items regardless of selection or moving. TagAllItems = 0x04, // Whether to tag all parts regardless of selection or moving. TagAllParts = 0x08, // Whether the range parameters are valid. TagRange = 0x10, TagDefaults = TagSelected | TagAllParts, EventTagAllOptions = TagSelected | TagMoving | TagAllItems | TagAllParts | TagRange }; typedef int EventTagOptions_t; struct EventTagOptionsStruct { EventTagOptions_t _flags; Pos _p0; Pos _p1; EventTagOptionsStruct() : _flags(TagDefaults) { } EventTagOptionsStruct(const EventTagOptions_t& flags, Pos p0 = Pos(), Pos p1 = Pos()) : _flags(flags), _p0(p0), _p1(p1) { } static EventTagOptionsStruct fromOptions(bool tagAllItems, bool tagAllParts, bool tagRange, Pos p0 = Pos(), Pos p1 = Pos(), bool tagSelected = true, bool tagMoving = false) { return EventTagOptionsStruct( (tagAllItems ? TagAllItems : TagNoOptions) | (tagAllParts ? TagAllParts : TagNoOptions) | (tagRange ? TagRange : TagNoOptions) | (tagSelected ? TagSelected : TagNoOptions) | (tagMoving ? TagMoving : TagNoOptions), p0, p1); } void clear() { _flags = TagNoOptions; _p0 = Pos(); _p1 = Pos(); } void appendFlags(const EventTagOptions_t& flags) { _flags |= flags; } void removeFlags(const EventTagOptions_t& flags) { _flags &= ~flags; } }; } // namespace MusECore #endif muse-4.0.0/src/muse/eventbase.h000066400000000000000000000136621404105311000163460ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: eventbase.h,v 1.3.2.3 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __EVENTBASE_H__ #define __EVENTBASE_H__ #include #include #include "type_defs.h" #include "pos.h" #include "evdata.h" #include "wave.h" // for SndFileR //#include "part.h" //#include "audio_fifo.h" namespace MusECore { // Forward declarations: class Part; class Fifo; //--------------------------------------------------------- // EventBase //--------------------------------------------------------- class EventBase : public PosLen { EventType _type; static EventID_t idGen; // An always unique id. EventID_t _uniqueId; // Can be either _uniqueId or the same _uniqueId as other clone 'group' events. De-cloning restores it to _uniqueId. EventID_t _id; protected: int refCount; bool _selected; public: EventBase(EventType t); // Creates a non-shared clone with same id, or duplicate with unique id, and 0 ref count and invalid Pos sn. EventBase(const EventBase& ev, bool duplicate_not_clone = false); virtual ~EventBase() { } int getRefCount() const { return refCount; } EventID_t id() const { return _id; } EventID_t newId() { return idGen++; } void shareId(const EventBase* ev) { _id = ev->_id; } // Makes id same as given event's. Effectively makes the events non-shared clones. virtual void assign(const EventBase& ev); // Assigns to this event, excluding the _id. EventType type() const { return _type; } void setType(EventType t) { _type = t; } bool selected() const { return _selected; } void setSelected(bool val) { _selected = val; } void move(int offset); virtual bool isSimilarTo(const EventBase& other) const = 0; virtual bool isSimilarType(const EventBase& other, bool compareTime = false, bool compareA = false, bool compareB = false, bool compareC = false, bool compareWavePath = false, bool compareWavePos = false, bool compareWaveStartPos = false) const; virtual void read(Xml&) = 0; virtual void write(int, Xml&, const Pos& offset, bool forcePath = false) const = 0; virtual void dump(int n = 0) const; virtual EventBase* mid(unsigned, unsigned) const = 0; friend class Event; virtual bool isNote() const { return false; } virtual bool isNoteOff() const { return false; } virtual int pitch() const { return 0; } virtual int program() const { return 0; } virtual int cntrl() const { return 0; } virtual int dataA() const { return 0; } virtual void setA(int) { } virtual void setPitch(int) { } virtual int cntrlVal() const { return 0; } virtual int dataB() const { return 0; } virtual int velo() const { return 0; } virtual void setB(int) { } virtual void setVelo(int) { } virtual int veloOff() const { return 0; } virtual int dataC() const { return 0; } virtual void setC(int) { } virtual void setVeloOff(int) { } virtual const unsigned char* constData() const { return 0; } virtual int dataLen() const { return 0; } virtual void setData(const unsigned char*, int) { } virtual const EvData eventData() const { return EvData(); } virtual const QString name() const { return QString("?"); } virtual void setName(const QString&) { } virtual int spos() const { return 0; } virtual void setSpos(int) { } virtual SndFileR sndFile() const { return 0; } virtual void setSndFile(SndFileR&) { } // Creates a non-shared clone, having the same 'group' _id. // NOTE: Certain pointer members may still be SHARED. Such as the sysex MidiEventBase::edata. // Be aware when iterating or modifying clones. virtual EventBase* clone() const = 0; // Restores _id to _uniqueId, removing the event from any clone 'group'. virtual void deClone() { _id = _uniqueId; } // Creates a copy of the event base, excluding the 'group' _id. virtual EventBase* duplicate() const = 0; virtual void readAudio(unsigned /*frame*/, float** /*bpp*/, int /*channels*/, int /*nn*/, bool /*doSeek*/, bool /*overwrite*/) { } virtual void seekAudio(sf_count_t /*frame*/) { } virtual Fifo* audioPrefetchFifo() { return 0; } virtual void prefetchAudio(Part* /*part*/, sf_count_t /*frames*/) { } }; } // namespace MusECore #endif muse-4.0.0/src/muse/eventlist.cpp000066400000000000000000000412151404105311000167350ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: eventlist.cpp,v 1.7.2.3 2009/11/05 03:14:35 terminator356 Exp $ // // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "tempo.h" #include "event.h" #include "xml.h" namespace MusECore { //--------------------------------------------------------- // readEventList //--------------------------------------------------------- void EventList::read(Xml& xml, const char* name, bool midi) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "event") { Event e(midi ? Note : Wave); e.read(xml); add(e); } else xml.unknown("readEventList"); break; case Xml::TagEnd: if (tag == name) return; default: break; } } } //--------------------------------------------------------- // add //--------------------------------------------------------- iEvent EventList::add(Event event) { // Changed by Tim. An event list containing wave events should be sorted by // frames. WaveTrack::fetchData() relies on the sorting order, and // there was a bug that waveparts were sometimes muted because of // incorrect sorting order (by ticks). // Also, when the tempo map is changed, every wave event would have to be // re-added to the event list so that the proper sorting order (by ticks) // could be achieved. // Note that in a med file, the tempo list is loaded AFTER all the tracks. // There was a bug that all the wave events' tick values were not correct, // since they were computed BEFORE the tempo map was loaded. // From cppreference.com about the insert hint parameter (we now use C++11): // hint - // iterator, used as a suggestion as to where to start the search (until C++11) // iterator to the position before which the new element will be inserted (since C++11) // ------ if(event.type() == Wave) return insert(std::pair (event.frame(), event)); unsigned key = event.tick(); if(event.type() == Note) // Place notes after controllers. { iEvent i = upper_bound(key); return insert(i, std::pair (key, event)); } else { // REMOVE Tim. Ctrl. Changed comment. // // // REMOVE Tim. citem. ctl. Added. It seems we must allow multiple controller events // // if they are to be moved, dragged, and dropped. // NOTE: Due to the companion controller cache, it is FORBIDDEN to have multiple controller events at the same time // with the same controller number. The cache has no way of linking specific entries with those multiple events. // If the cache has multiple events, it can crash the operations system when deleting cache items. // It is tempting to catch that at a low level like the event list. // But it would only be there to catch rare mistakes, and may slow other legitimate additions, and be redundant // in many cases where a search is already done. // Also, if add() is called as part of an (undo) operation, we have no mechanism to return a flag indicating success - // not when add() is called in the realtime thread. Not even in non-realtime stage 1 when Song::applyOperation() is called. // So to preserve speed, we RELY on catching that at higher levels such as the add event dialog or during song loading. // if(event.type() == Controller) // { // EventRange er = equal_range(key); // iEvent i = er.second; // iEvent loc = i; // const int data_a = event.dataA(); // while(i != er.first) // { // --i; // // Special: There must be only ONE value per controller per position. // // If there is already a controller value for this controller number // // at this position, just replace it and return. // // // // This is meant as a last line of defense against accidental multiple // // controller values at a given time. The rule of thumb when executing // // add event commands is you must check beforehand whether an event // // exists and tell the command system to delete it so that the undo // // system can remember what was replaced. // // In some cases the command/undo system may do that for you. // // But simply relying on this low-level catch-all is not good, the undo // // system won't remember what was deleted. // if(i->second.type() == Controller && i->second.dataA() == data_a) // { // i->second.setB(event.dataB()); // return i; // } // if(i->second.type() == Note) // loc = i; // } // return insert(loc, std::pair (key, event)); // } // else // { iEvent i = lower_bound(key); while(i != end() && i->first == key && i->second.type() != Note) ++i; return insert(i, std::pair (key, event)); // } } return end(); } //--------------------------------------------------------- // move //--------------------------------------------------------- void EventList::move(Event& event, unsigned tick) { iEvent i = find(event); if(i != end()) erase(i); if(event.type() == Wave) { insert(std::pair (MusEGlobal::tempomap.tick2frame(tick), event)); return; } if(event.type() == Note) // Place notes after controllers. { iEvent i = upper_bound(tick); insert(i, std::pair (tick, event)); return; } else { iEvent i = lower_bound(tick); while(i != end() && i->first == tick && i->second.type() != Note) ++i; insert(i, std::pair (tick, event)); return; } } //--------------------------------------------------------- // find //--------------------------------------------------------- iEvent EventList::find(const Event& event) { EventRange range = equal_range(event.posValue()); for (iEvent i = range.first; i != range.second; ++i) { if (i->second == event) return i; } return end(); } ciEvent EventList::find(const Event& event) const { cEventRange range = equal_range(event.posValue()); for (ciEvent i = range.first; i != range.second; ++i) { if (i->second == event) return i; } return end(); } iEvent EventList::findSimilar(const Event& event) { EventRange range = equal_range(event.posValue()); for (iEvent i = range.first; i != range.second; ++i) { if (i->second.isSimilarTo(event)) return i; } return end(); } ciEvent EventList::findSimilar(const Event& event) const { cEventRange range = equal_range(event.posValue()); for (ciEvent i = range.first; i != range.second; ++i) { if (i->second.isSimilarTo(event)) return i; } return end(); } int EventList::findSimilarType(const Event& event, EventList& list, bool compareTime, bool compareA, bool compareB, bool compareC, bool compareWavePath, bool compareWavePos, bool compareWaveStartPos) const { int cnt = 0; cEventRange range = compareTime ? equal_range(event.posValue()) : cEventRange(begin(), end()); for(ciEvent i = range.first; i != range.second; ++i) { const Event& e = i->second; if(e.isSimilarType(event, false, // Do not compare time, that's handled above. compareA, compareB, compareC, compareWavePath, compareWavePos, compareWaveStartPos)) { // REMOVE Tim. ctrl. Changed. // ++cnt; // list.add(e); if(list.add(e) != list.end()) ++cnt; } } return cnt; } iEvent EventList::findId(const Event& event) { EventRange range = equal_range(event.posValue()); for (iEvent i = range.first; i != range.second; ++i) { if (i->second.id() == event.id()) return i; } return end(); } ciEvent EventList::findId(const Event& event) const { cEventRange range = equal_range(event.posValue()); for (ciEvent i = range.first; i != range.second; ++i) { if (i->second.id() == event.id()) return i; } return end(); } iEvent EventList::findId(unsigned t, EventID_t id) { EventRange range = equal_range(t); for (iEvent i = range.first; i != range.second; ++i) { if (i->second.id() == id) return i; } return end(); } ciEvent EventList::findId(unsigned t, EventID_t id) const { cEventRange range = equal_range(t); for (ciEvent i = range.first; i != range.second; ++i) { if (i->second.id() == id) return i; } return end(); } iEvent EventList::findId(EventID_t id) { for (iEvent i = begin(); i != end(); ++i) { if (i->second.id() == id) return i; } return end(); } ciEvent EventList::findId(EventID_t id) const { for (ciEvent i = begin(); i != end(); ++i) { if (i->second.id() == id) return i; } return end(); } iEvent EventList::findWithId(const Event& event) { EventRange range = equal_range(event.posValue()); for (iEvent i = range.first; i != range.second; ++i) { if (i->second == event || i->second.id() == event.id()) return i; } return end(); } ciEvent EventList::findWithId(const Event& event) const { cEventRange range = equal_range(event.posValue()); for (ciEvent i = range.first; i != range.second; ++i) { if (i->second == event || i->second.id() == event.id()) return i; } return end(); } //--------------------------------------------------------- // dump //--------------------------------------------------------- void EventList::dump() const { for (ciEvent i = begin(); i != end(); ++i) i->second.dump(); } PosLen EventList::evrange(bool wave, RelevantSelectedEvents_t relevant, int* numEvents, int ctrlNum) const { PosLen res; res.setType(wave ? Pos::FRAMES : Pos::TICKS); int e_found = 0; bool first_found = false; unsigned start_time = 0; unsigned end_time = 0; for(ciEvent ie = begin(); ie != end(); ++ie) { const Event& e = ie->second; // Only events of the given type are considered. const EventType et = e.type(); switch(et) { case Note: if(wave || (relevant & NotesRelevant) == NoEventsRelevant) continue; // Don't add Note event types if they have no length. // Hm, it is possible for zero-length events to be // accidentally present in the list. So the user should // at least be allowed to cut and paste them. The EventList // class will probably be correcting this condition anyway // when adding the event to the list. //if(e.lenValue() == 0) // continue; if(!first_found) { start_time = e.posValue(); first_found = true; } if(e.endPosValue() > end_time) end_time = e.endPosValue(); ++e_found; break; case Wave: if(!wave || (relevant & WaveRelevant) == NoEventsRelevant) continue; // Don't add Wave event types if they have no length. //if(e.lenValue() == 0) // continue; if(!first_found) { start_time = e.posValue(); first_found = true; } if(e.endPosValue() > end_time) end_time = e.endPosValue(); ++e_found; break; case Controller: case Meta: case Sysex: if(wave) continue; switch(et) { case Controller: if((relevant & ControllersRelevant) == NoEventsRelevant) continue; if(ctrlNum >= 0 && e.dataA() != ctrlNum) continue; break; case Meta: if((relevant & MetaRelevant) == NoEventsRelevant) continue; break; case Sysex: if((relevant & SysexRelevant) == NoEventsRelevant) continue; break; default: continue; break; } // For these events, even if duplicates are already found at this position, // the range is still the same, which simplifies this code - go ahead and count it... if(!first_found) { start_time = e.posValue(); first_found = true; } // For these events, minimum 1 unit time, to qualify as a valid 'range'. if((e.posValue() + 1) > end_time) end_time = e.posValue() + 1; ++e_found; break; } } res.setPosValue(start_time); res.setLenValue(end_time - start_time); *numEvents = e_found; return res; } void EventList::findControllers(bool wave, FindMidiCtlsList_t* outList, int findCtl) const { for(ciEvent ie = cbegin(); ie != cend(); ++ie) { const Event& e = ie->second; const EventType et = e.type(); switch(et) { case Note: case Meta: case Sysex: continue; break; case Wave: if(!wave) continue; // TODO Audio controllers. //list->insert( ? ); break; case Controller: if(wave) continue; if(findCtl < 0 || findCtl == e.dataA()) { const PosLen epl = e.posLen(); FindMidiCtlsListInsResPair_t pres = outList->insert(FindMidiCtlsPair_t(e.dataA(), epl)); if(!pres.second) { iFindMidiCtlsList ifml = pres.first; PosLen& fml = ifml->second; if(fml > epl) fml = epl; } } break; } } } // REMOVE Tim. ctrl. Added. ciEvent EventList::findControllerAt(const Event& event) const { cEventRange range = equal_range(event.posValue()); const int ctlnum = event.dataA(); for (ciEvent i = range.first; i != range.second; ++i) { if (i->second.type() == Controller && i->second.dataA() == ctlnum) return i; } return cend(); } // REMOVE Tim. ctrl. Added. iEvent EventList::findControllerAt(const Event& event) { EventRange range = equal_range(event.posValue()); const int ctlnum = event.dataA(); for (iEvent i = range.first; i != range.second; ++i) { if (i->second.type() == Controller && i->second.dataA() == ctlnum) return i; } return end(); } // REMOVE Tim. ctrl. Added. bool EventList::controllerValueExists(const Event& event) const { cEventRange range = equal_range(event.posValue()); const int ctlnum = event.dataA(); for (ciEvent i = range.first; i != range.second; ++i) { if (i->second.type() == Controller && i->second.dataA() == ctlnum) return true; } return false; } } // namespace MusECore muse-4.0.0/src/muse/exportmidi.cpp000066400000000000000000001151701404105311000171060ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: exportmidi.cpp,v 1.9.2.1 2009/04/01 01:37:10 terminator356 Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // (C) Copyright 2012, 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "sig.h" // Tim. #include "keyevent.h" #include "app.h" #include "midifile.h" #include "midi_consts.h" #include "midiport.h" #include "midictrl.h" #include "globals.h" #include "filedialog.h" #include "track.h" #include "song.h" #include "mpevent.h" #include "event.h" #include "marker/marker.h" #include "drummap.h" #include "gconfig.h" namespace MusECore { //--------------------------------------------------------- // addMarkerList //--------------------------------------------------------- static void addMarkerList(MPEventList* l, int port = 0) { MusECore::MarkerList* ml = MusEGlobal::song->marker(); for (MusECore::ciMarker m = ml->begin(); m != ml->end(); ++m) { QByteArray ba = m->second.name().toLatin1(); const char* name = ba.constData(); int len = ba.length(); MusECore::MidiPlayEvent ev(m->first, port, MusECore::ME_META, (const unsigned char*)name, len); ev.setA(MusECore::ME_META_TEXT_6_MARKER); l->add(ev); } }; //--------------------------------------------------------- // addCopyright //--------------------------------------------------------- static void addCopyright(MPEventList* l, int port = 0) { QByteArray ba = MusEGlobal::config.copyright.toLatin1(); const char* copyright = ba.constData(); if (copyright && *copyright) { int len = ba.length(); MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)copyright, len); ev.setA(MusECore::ME_META_TEXT_2_COPYRIGHT); l->add(ev); } } //--------------------------------------------------------- // addComment //--------------------------------------------------------- static void addComment(MPEventList* l, const Track* track, int port = 0) { if (!track->comment().isEmpty()) { QByteArray ba = track->comment().toLatin1(); const char* comment = ba.constData(); int len = ba.length(); MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)comment, len); ev.setA(MusECore::ME_META_TEXT_1_COMMENT); l->add(ev); } } //--------------------------------------------------------- // addTempomap //--------------------------------------------------------- static void addTempomap(MPEventList* l, int port = 0) { MusECore::TempoList* tl = &MusEGlobal::tempomap; for (MusECore::ciTEvent e = tl->begin(); e != tl->end(); ++e) { MusECore::TEvent* event = e->second; unsigned char data[3]; int tempo = event->tempo; data[2] = tempo & 0xff; data[1] = (tempo >> 8) & 0xff; data[0] = (tempo >> 16) & 0xff; MusECore::MidiPlayEvent ev(event->tick, port, MusECore::ME_META, data, 3); ev.setA(MusECore::ME_META_SET_TEMPO); l->add(ev); } } //--------------------------------------------------------- // addTimeSignatures //--------------------------------------------------------- static void addTimeSignatures(MPEventList* l, int port = 0) { const MusECore::SigList* sl = &MusEGlobal::sigmap; for (MusECore::ciSigEvent e = sl->begin(); e != sl->end(); ++e) { MusECore::SigEvent* event = e->second; int sz = (MusEGlobal::config.exp2ByteTimeSigs ? 2 : 4); // export 2 byte timesigs instead of 4 ? unsigned char data[sz]; data[0] = event->sig.z; switch(event->sig.n) { case 1: data[1] = 0; break; case 2: data[1] = 1; break; case 4: data[1] = 2; break; case 8: data[1] = 3; break; case 16: data[1] = 4; break; case 32: data[1] = 5; break; case 64: data[1] = 6; break; default: fprintf(stderr, "wrong Signature; denominator is %d\n", event->sig.n); break; } // By T356. In muse the metronome pulse is fixed at 24 (once per quarter-note). // The number of 32nd notes per 24 MIDI clock signals (per quarter-note) is 8. if(!MusEGlobal::config.exp2ByteTimeSigs) { data[2] = 24; data[3] = 8; } MusECore::MidiPlayEvent ev(event->tick, port, MusECore::ME_META, data, sz); ev.setA(MusECore::ME_META_TIME_SIGNATURE); l->add(ev); } } //--------------------------------------------------------- // addKeySignatures //--------------------------------------------------------- static void addKeySignatures(MPEventList* l, int port = 0) { MusECore::KeyList* kl = &MusEGlobal::keymap; for (MusECore::ciKeyEvent e = kl->cbegin(); e != kl->cend(); ++e) { const MusECore::KeyEvent& event = e->second; char kc = 0; switch(event.key) { case KEY_SHARP_BEGIN: case KEY_SHARP_END: case KEY_B_BEGIN: case KEY_B_END: continue; break; case KEY_C: // C or am: uses # for "black keys" kc = 0; break; case KEY_G: kc = 1; break; case KEY_D: kc = 2; break; case KEY_A: kc = 3; break; case KEY_E: kc = 4; break; case KEY_B: // or H in german. kc = 5; break; case KEY_FIS: //replaces F with E# kc = 5; break; case KEY_C_B: // the same as C: but uses b for "black keys" kc = 0; break; case KEY_F: kc = -1; break; case KEY_BES: // or B in german kc = -2; break; case KEY_ES: kc = -3; break; case KEY_AS: kc = -4; break; case KEY_DES: kc = -5; break; case KEY_GES: //sounds like FIS: but uses b instead of # kc = -5; break; }; unsigned char data[2]; data[1] = event.minor; data[0] = kc; MusECore::MidiPlayEvent ev(event.tick, port, MusECore::ME_META, data, 2); ev.setA(MusECore::ME_META_KEY_SIGNATURE); l->add(ev); } } //--------------------------------------------------------- // addController //--------------------------------------------------------- static void addController(MPEventList* l, int tick, int port, int channel, int a, int b) { if (a >= CTRL_7_OFFSET && a < (CTRL_7_OFFSET + 0x10000)) { // 7 Bit Controller l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, a, b)); } else if (a >= CTRL_14_OFFSET && a < (CTRL_14_OFFSET + 0x10000)) { // 14 Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, ctrlH, dataH)); l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, ctrlL, dataL)); } else if (a >= CTRL_RPN_OFFSET && a < (CTRL_RPN_OFFSET + 0x10000)) { // RPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HRPN, ctrlH)); l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LRPN, ctrlL)); l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, b)); } else if (a >= CTRL_NRPN_OFFSET && a < (CTRL_NRPN_OFFSET + 0x10000)) { // NRPN 7-Bit Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, b)); } else if (a == CTRL_PITCH) { int r_a = b + 8192; int r_b = r_a >> 7; l->add(MidiPlayEvent(tick, port, channel, ME_PITCHBEND, r_a & 0x7f, r_b & 0x7f)); } else if (a == CTRL_PROGRAM) { int hb = (b >> 16) & 0xff; int lb = (b >> 8) & 0xff; int pr = b & 0x7f; int tickoffset = 0; // REMOVE Tim. Song type removal. Hm, TEST is this OK here? //switch(MusEGlobal::song->mtype()) { // case MT_GM: // no HBANK/LBANK // break; // case MT_GS: // case MT_XG: // case MT_UNKNOWN: if (hb != 0xff) { l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HBANK, hb)); ++tickoffset; } if (lb != 0xff) { l->add(MidiPlayEvent(tick+tickoffset, port, channel, ME_CONTROLLER, CTRL_LBANK, lb)); ++tickoffset; } // break; // } l->add(MidiPlayEvent(tick+tickoffset, port, channel, ME_PROGRAM, pr, 0)); } else if(a == CTRL_AFTERTOUCH) { l->add(MidiPlayEvent(tick, port, channel, ME_AFTERTOUCH, b & 0x7f, 0)); } else if((a | 0xff) == CTRL_POLYAFTER) { l->add(MidiPlayEvent(tick, port, channel, ME_POLYAFTER, a & 0x7f, b & 0x7f)); } else if (a >= CTRL_INTERNAL_OFFSET && a < CTRL_RPN14_OFFSET) // Unaccounted for internal controller return; else if (a >= CTRL_RPN14_OFFSET && a < (CTRL_RPN14_OFFSET + 0x10000)) { // RPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HRPN, ctrlH)); l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LRPN, ctrlL)); l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, dataH)); l->add(MidiPlayEvent(tick+3, port, channel, ME_CONTROLLER, CTRL_LDATA, dataL)); } else if (a >= CTRL_NRPN14_OFFSET && a < (CTRL_NRPN14_OFFSET + 0x10000)) { // NRPN14 Controller int ctrlH = (a >> 8) & 0x7f; int ctrlL = a & 0x7f; int dataH = (b >> 7) & 0x7f; int dataL = b & 0x7f; l->add(MidiPlayEvent(tick, port, channel, ME_CONTROLLER, CTRL_HNRPN, ctrlH)); l->add(MidiPlayEvent(tick+1, port, channel, ME_CONTROLLER, CTRL_LNRPN, ctrlL)); l->add(MidiPlayEvent(tick+2, port, channel, ME_CONTROLLER, CTRL_HDATA, dataH)); l->add(MidiPlayEvent(tick+3, port, channel, ME_CONTROLLER, CTRL_LDATA, dataL)); } } //--------------------------------------------------------- // addEventList // part can be NULL meaning no part used. // track can be NULL meaning no concept of drum notes is allowed in init sequences. //--------------------------------------------------------- static void addEventList(const MusECore::EventList& evlist, MusECore::MPEventList* mpevlist, const MusECore::MidiTrack* track, const MusECore::Part* part, int port, int channel) { DrumMap dm; for (MusECore::ciEvent i = evlist.cbegin(); i != evlist.cend(); ++i) { const MusECore::Event& ev = i->second; int tick = ev.tick(); if(part) tick += part->tick(); switch (ev.type()) { case MusECore::Note: { if (ev.velo() == 0) { printf("Warning: midi note has velocity 0, (ignored)\n"); continue; } int pitch = ev.pitch(); int fin_pitch = pitch; int fin_port = port; int fin_chan = channel; if(MusEGlobal::config.exportDrumMapOverrides) { if (track && track->type() == MusECore::Track::DRUM) { // Map drum-notes to the drum-map values // We must look at what the drum map WOULD say at the note's tick, // not what it says now at the current cursor. track->getMapItemAt(tick, pitch, dm, WorkingDrumMapEntry::AllOverrides); fin_pitch = dm.anote; // Default to track port if -1 and track channel if -1. // Port is only allowed to change in format 1. if(dm.port != -1 && MusEGlobal::config.smfFormat != 0) fin_port = dm.port; if(dm.channel != -1) fin_chan = dm.channel; } } // If the port or channel is overridden by a drum map, DO NOT add the event here // because it requires its own track. That is taken care of in exportMidi(). if(fin_port != port || // Channel is allowed to be different but can only cause a new track in format 1. (fin_chan != channel && MusEGlobal::config.exportChannelOverridesToNewTrack && MusEGlobal::config.smfFormat != 0)) continue; int velo = ev.velo(); int veloOff = ev.veloOff(); int len = ev.lenTick(); //--------------------------------------- // apply trackinfo values //--------------------------------------- if (track && ((!track->isDrumTrack() && track->transposition) || track->velocity || track->compression != 100 || track->len != 100)) { // Transpose only midi not drum tracks. if(!track->isDrumTrack()) fin_pitch += track->transposition; if (fin_pitch > 127) fin_pitch = 127; if (fin_pitch < 0) fin_pitch = 0; velo += track->velocity; velo = (velo * track->compression) / 100; if (velo > 127) velo = 127; if (velo < 1) // no off event velo = 1; // REMOVE Tim. Noteoff. Added. Zero means zero. Should mean no note at all? // Although we might not actually play such an event in the midi.cpp engine, // I don't like the idea of discarding the event during export. // Keeping the notes is more important than the velocities, I'd say. //break; len = (len * track->len) / 100; } if (len <= 0) len = 1; mpevlist->add(MusECore::MidiPlayEvent(tick, fin_port, fin_chan, MusECore::ME_NOTEON, fin_pitch, velo)); if(MusEGlobal::config.expOptimNoteOffs) // Save space by replacing note offs with note on velocity 0 mpevlist->add(MusECore::MidiPlayEvent(tick+len, fin_port, fin_chan, MusECore::ME_NOTEON, fin_pitch, 0)); else mpevlist->add(MusECore::MidiPlayEvent(tick+len, fin_port, fin_chan, MusECore::ME_NOTEOFF, fin_pitch, veloOff)); } break; case MusECore::Controller: { int ctlnum = ev.dataA(); int fin_ctlnum = ctlnum; int fin_port = port; int fin_chan = channel; if(MusEGlobal::config.exportDrumMapOverrides) { // Is it a drum controller event, according to the track port's instrument? if(MusEGlobal::midiPorts[port].drumController(ctlnum)) { if (track && track->type() == MusECore::Track::DRUM) { int v_idx = ctlnum & 0x7f; track->getMapItemAt(tick, v_idx, dm, WorkingDrumMapEntry::AllOverrides); fin_ctlnum = (ctlnum & ~0xff) | dm.anote; // Default to track port if -1 and track channel if -1. // Port is only allowed to change in format 1. if(dm.port != -1 && MusEGlobal::config.smfFormat != 0) fin_port = dm.port; if(dm.channel != -1) fin_chan = dm.channel; } } } // If the port or channel is overridden by a drum map, DO NOT add the event here // because it requires its own track. That is taken care of in exportMidi(). if(fin_port != port || // Channel is allowed to be different but can only cause a new track in format 1. (fin_chan != channel && MusEGlobal::config.exportChannelOverridesToNewTrack && MusEGlobal::config.smfFormat != 0)) continue; addController(mpevlist, tick, fin_port, fin_chan, fin_ctlnum, ev.dataB()); } break; case MusECore::Sysex: { mpevlist->add(MusECore::MidiPlayEvent(tick, port, MusECore::ME_SYSEX, ev.eventData())); //MusECore::MidiPlayEvent ev(tick, port, MusECore::ME_SYSEX, ev.eventData()); //ev.setChannel(channel); // Sysex are channelless, but this is required for sorting! //mpevlist->add(ev); } break; case MusECore::Meta: { MusECore::MidiPlayEvent mpev(tick, port, MusECore::ME_META, ev.eventData()); mpev.setA(ev.dataA()); //mpev.setChannel(channel); // Metas are channelless, but this is required for sorting! mpevlist->add(mpev); } break; case MusECore::Wave: break; } } } //--------------------------------------------------------- // addTrackEvents //--------------------------------------------------------- static void addTrackEvents(MPEventList* l, int port, int channel, const MidiTrack* track) { //--------------------------------------------------- // Write all track events. //--------------------------------------------------- const MusECore::PartList* parts = track->cparts(); for (MusECore::ciPart p = parts->cbegin(); p != parts->cend(); ++p) { const MusECore::MidiPart* part = (MusECore::MidiPart*) (p->second); MusECore::addEventList(part->events(), l, track, part, port, channel); } } static void writeDeviceOrPortMeta(int port, MPEventList* mpel) { if(port >= 0 && port < MusECore::MIDI_PORTS) { if(MusEGlobal::config.exportPortsDevices & MusEGlobal::PORT_NUM_META) { unsigned char port_char = (unsigned char)port; MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, &port_char, 1); ev.setA(MusECore::ME_META_PORT_CHANGE); // Meta port change //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! mpel->add(ev); } if(MusEGlobal::config.exportPortsDevices & MusEGlobal::DEVICE_NAME_META) { MusECore::MidiDevice* dev = MusEGlobal::midiPorts[port].device(); const char* str; int len; QByteArray ba; if(dev && !dev->name().isEmpty()) ba = dev->name().toLatin1(); else ba = QString::number(port).toLatin1(); str = ba.constData(); len = ba.length(); MusECore::MidiPlayEvent ev(0, port, MusECore::ME_META, (const unsigned char*)str, len); ev.setA(MusECore::ME_META_TEXT_9_DEVICE_NAME); // Meta Device Name //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! mpel->add(ev); } } } static void writeInitSeqOrInstrNameMeta(int port, int channel, MPEventList* mpel) { if(MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument()) { //-------------------------- // Port midi init sequence //-------------------------- if(MusEGlobal::config.exportModeInstr & MusEGlobal::MODE_SYSEX) { EventList* el = instr->midiInit(); if(!el->empty()) addEventList(*el, mpel, NULL, NULL, port, channel); // No track or part passed for init sequences } //-------------------------- // Instrument Name meta //-------------------------- if(!instr->iname().isEmpty() && (MusEGlobal::config.exportModeInstr & MusEGlobal::INSTRUMENT_NAME_META)) { const char* str; int len; QByteArray ba = instr->iname().toLatin1(); str = ba.constData(); len = ba.length(); MidiPlayEvent ev(0, port, ME_META, (const unsigned char*)str, len); ev.setA(ME_META_TEXT_4_INSTRUMENT_NAME); // Meta Instrument Name //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! mpel->add(ev); } } } static void writeTrackNameMeta(int port, Track* track, MPEventList* mpel) { if (!track->name().isEmpty()) { QByteArray ba = track->name().toLatin1(); const char* name = ba.constData(); int len = ba.length(); MidiPlayEvent ev(0, port, ME_META, (const unsigned char*)name, len); ev.setA(ME_META_TEXT_3_TRACK_NAME); // Meta Sequence/Track Name //ev.setChannel(channel); // Metas are channelless, but this is required for sorting! mpel->add(ev); } } } // namespace MusECore namespace MusEGui { //--------------------------------------------------------- // exportMidi //--------------------------------------------------------- void MusE::exportMidi() { if(MusEGlobal::config.smfFormat == 0) // Want single track? Warn if multiple ports in song... { MusECore::MidiTrackList* mtl = MusEGlobal::song->midis(); int prev_port = -1; for(MusECore::ciMidiTrack im = mtl->begin(); im != mtl->end(); ++im) { int port = (*im)->outPort(); if(prev_port == -1) { prev_port = port; continue; } if(port != prev_port) { if(QMessageBox::warning(this, tr("MusE: Warning"), tr("The song uses multiple ports but export format 0 (single track) is set.\n" "The first track's port will be used. Playback will likely be wrong\n" " unless the channels used in one port are different from all other ports.\n" "Canceling and setting a different export format would be better.\nContinue?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) return; break; } } } MusEGui::MFile file(QString("midis"), QString(".mid")); FILE* fp = file.open("w", MusEGlobal::midi_file_save_pattern, this, false, true, tr("MusE: Export Midi")); if (fp == 0) return; MusECore::MidiFile mf(fp); MusECore::TrackList* tl = MusEGlobal::song->tracks(); // Changed to full track list so user can rearrange tracks. MusECore::MidiFileTrackList* mtl = new MusECore::MidiFileTrackList; MusECore::DrumMap dm; std::set used_ports; // There will always be at least one track, regardless of format. int track_count = 1; MusECore::MidiFileTrack* mft = new MusECore::MidiFileTrack; mtl->push_back(mft); MusECore::MPEventList* l = &(mft->events); // Write track marker addMarkerList(l); // Write copyright addCopyright(l); // Write comment // TODO Remove? //addComment(l, track); // Write tempomap addTempomap(l); // Write time signatures addTimeSignatures(l); // Write key signatures addKeySignatures(l); int tr_cnt = 0; for(MusECore::ciTrack im = tl->cbegin(); im != tl->cend(); ++im) { if(!(*im)->isMidiTrack()) continue; if(MusEGlobal::config.smfFormat != 0) { mft = new MusECore::MidiFileTrack; mtl->push_back(mft); l = &(mft->events); ++track_count; } MusECore::MidiTrack* track = (MusECore::MidiTrack*)(*im); const int port = track->outPort(); const int channel = track->outChannel(); if(tr_cnt == 0 || MusEGlobal::config.smfFormat != 0) { // Write comment addComment(l, track, port); // Write track name writeTrackNameMeta(port, track, l); // Write device name or port change meta if(MusEGlobal::config.exportPortDeviceSMF0) writeDeviceOrPortMeta(port, l); //writeInitSeqOrInstrNameMeta(port, channel, l); } // Write midi port init sequence: GM/GS/XG etc. // and Instrument Name meta. std::set::iterator iup = used_ports.find(port); if(iup == used_ports.end()) { if(port >= 0 && port < MusECore::MIDI_PORTS) { if(tr_cnt == 0 || MusEGlobal::config.smfFormat != 0) MusECore::writeInitSeqOrInstrNameMeta(port, channel, l); used_ports.insert(port); } } // Write all track events. addTrackEvents(l, port, channel, track); ++tr_cnt; } // For drum tracks with drum map port overrides, we may need to add extra tracks. // But we can can only do that if multi-track format is chosen. if(MusEGlobal::config.exportDrumMapOverrides && MusEGlobal::config.smfFormat != 0) { MusECore::MidiFileTrackList aux_mtl; for (MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) { if(!(*it)->isDrumTrack()) continue; MusECore::MidiTrack* track = static_cast(*it); int port = track->outPort(); int channel = track->outChannel(); MusECore::PartList* parts = track->parts(); for (MusECore::iPart ip = parts->begin(); ip != parts->end(); ++ip) { MusECore::MidiPart* part = (MusECore::MidiPart*) (ip->second); const MusECore::EventList& evlist = part->events(); for (MusECore::ciEvent iev = evlist.begin(); iev != evlist.end(); ++iev) { const MusECore::Event& ev = iev->second; int tick = ev.tick() + part->tick(); switch (ev.type()) { case MusECore::Note: { if (ev.velo() == 0) { fprintf(stderr, "Warning: midi note has velocity 0, (ignored)\n"); continue; } int pitch; int fin_port = port; int fin_chan = channel; if (track->type() == MusECore::Track::DRUM) { // Map drum-notes to the drum-map values // We must look at what the drum map WOULD say at the note's tick, // not what it says now at the current cursor. int instr = ev.pitch(); track->getMapItemAt(tick, instr, dm, MusECore::WorkingDrumMapEntry::AllOverrides); pitch = dm.anote; // Default to track port if -1 and track channel if -1. if(dm.port != -1) fin_port = dm.port; if(dm.channel != -1) fin_chan = dm.channel; } // We are only looking for port or channel overrides. They require a separate track. if(fin_port == port && (fin_chan == channel || !MusEGlobal::config.exportChannelOverridesToNewTrack)) continue; // Is there already a MidiFileTrack for the port? MusECore::MidiFileTrack* aux_mft = 0; for(MusECore::iMidiFileTrack imft = aux_mtl.begin(); imft != aux_mtl.end(); ++imft) { MusECore::MidiFileTrack* t = *imft; // Take the first event's port (in this case they are all the same). const MusECore::MidiPlayEvent& mpe = *t->events.begin(); if(mpe.port() == fin_port) { aux_mft = t; break; } } // If not, create a new MidiFileTrack. if(!aux_mft) { aux_mft = new MusECore::MidiFileTrack; aux_mft->_isDrumTrack = true; //----------------------------------- // track name //----------------------------------- // TODO: Maybe append some text here? MusECore::writeTrackNameMeta(fin_port, track, &aux_mft->events); //----------------------------------------- // Write device name or port change meta //----------------------------------------- MusECore::writeDeviceOrPortMeta(fin_port, &aux_mft->events); //--------------------------------------------------- // Write midi port init sequence: GM/GS/XG etc. // and Instrument Name meta. //--------------------------------------------------- std::set::iterator iup = used_ports.find(fin_port); if(iup == used_ports.end()) { MusECore::writeInitSeqOrInstrNameMeta(fin_port, fin_chan, &aux_mft->events); used_ports.insert(fin_port); } aux_mtl.push_back(aux_mft); mtl->push_back(aux_mft); // Increment the track count. ++track_count; } int velo = ev.velo(); int veloOff = ev.veloOff(); int len = ev.lenTick(); //--------------------------------------- // apply trackinfo values //--------------------------------------- if (track && (track->velocity || track->compression != 100 || track->len != 100)) { if (pitch > 127) pitch = 127; if (pitch < 0) pitch = 0; velo += track->velocity; velo = (velo * track->compression) / 100; if (velo > 127) velo = 127; if (velo < 1) // no off event velo = 1; // REMOVE Tim. Noteoff. Added. Zero means zero. Should mean no note at all? // Although we might not actually play such an event in the midi.cpp engine, // I don't like the idea of discarding the event during export. // Keeping the notes is more important than the velocities, I'd say. //break; len = (len * track->len) / 100; } if (len <= 0) len = 1; aux_mft->events.add(MusECore::MidiPlayEvent(tick, fin_port, fin_chan, MusECore::ME_NOTEON, pitch, velo)); if(MusEGlobal::config.expOptimNoteOffs) // Save space by replacing note offs with note on velocity 0 aux_mft->events.add(MusECore::MidiPlayEvent(tick+len, fin_port, fin_chan, MusECore::ME_NOTEON, pitch, 0)); else aux_mft->events.add(MusECore::MidiPlayEvent(tick+len, fin_port, fin_chan, MusECore::ME_NOTEOFF, pitch, veloOff)); } break; case MusECore::Controller: { int fin_port = port; int fin_chan = channel; int ctlnum = ev.dataA(); int fin_ctlnum = ctlnum; // Is it a drum controller event, according to the track port's instrument? if(MusEGlobal::midiPorts[port].drumController(ctlnum)) { if (track->type() == MusECore::Track::DRUM) { int v_idx = ctlnum & 0x7f; track->getMapItemAt(tick, v_idx, dm, MusECore::WorkingDrumMapEntry::AllOverrides); fin_ctlnum = (ctlnum & ~0xff) | dm.anote; // Default to track port if -1 and track channel if -1. if(dm.port != -1) fin_port = dm.port; if(dm.channel != -1) fin_chan = dm.channel; } } // We are only looking for port or channel overrides. They require a separate track. if(fin_port == port && (fin_chan == channel || !MusEGlobal::config.exportChannelOverridesToNewTrack)) continue; // Is there already a MidiFileTrack for the port? MusECore::MidiFileTrack* aux_mft = 0; for(MusECore::iMidiFileTrack imft = aux_mtl.begin(); imft != aux_mtl.end(); ++imft) { MusECore::MidiFileTrack* t = *imft; // Take the first event's port (in this case they are all the same). const MusECore::MidiPlayEvent& mpe = *t->events.begin(); if(mpe.port() == fin_port) { aux_mft = t; break; } } // If not, create a new MidiFileTrack. if(!aux_mft) { aux_mft = new MusECore::MidiFileTrack; aux_mft->_isDrumTrack = true; //----------------------------------- // track name //----------------------------------- // TODO: Maybe append some text here? MusECore::writeTrackNameMeta(fin_port, track, &aux_mft->events); //----------------------------------------- // Write device name or port change meta //----------------------------------------- MusECore::writeDeviceOrPortMeta(fin_port, &aux_mft->events); //--------------------------------------------------- // Write midi port init sequence: GM/GS/XG etc. // and Instrument Name meta. //--------------------------------------------------- std::set::iterator iup = used_ports.find(fin_port); if(iup == used_ports.end()) { MusECore::writeInitSeqOrInstrNameMeta(fin_port, fin_chan, &aux_mft->events); used_ports.insert(fin_port); } aux_mtl.push_back(aux_mft); mtl->push_back(aux_mft); // Increment the track count. ++track_count; } MusECore::addController(&aux_mft->events, tick, fin_port, fin_chan, fin_ctlnum, ev.dataB()); } break; case MusECore::Sysex: case MusECore::Meta: case MusECore::Wave: break; } } } } } mf.setDivision(MusEGlobal::config.midiDivision); // Takes ownership of mtl and its contents. mf.setTrackList(mtl, track_count); mf.write(); } } // namespace MusEGui muse-4.0.0/src/muse/fastlog.h000066400000000000000000000036611404105311000160270ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/fastlog.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= /* Copyright unknown. Code by Laurent de Soras . */ #ifndef __pbd_fastlog_h__ #define __pbd_fastlog_h__ #include "muse_math.h" /* Get math.h for HUGE_VAL */ namespace MusECore { static inline float fast_log2 (float val) { /* don't use reinterpret_cast<> because that prevents this from being used by pure C code (for example, GnomeCanvasItems) */ union {float f; int i;} t; t.f = val; int* const exp_ptr = &t.i; int x = *exp_ptr; const int log_2 = ((x >> 23) & 255) - 128; x &= ~(255 << 23); x += 127 << 23; *exp_ptr = x; val = ((-1.0f/3) * t.f + 2) * t.f - 2.0f/3; return (val + log_2); } static inline float fast_log (const float val) { return (fast_log2 (val) * 0.69314718f); } static inline float fast_log10 (const float val) { return fast_log2(val) / 3.312500f; } static inline float minus_infinity() { return -HUGE_VAL; } } // namespace MusECore #endif /* __pbd_fastlog_h__ */ muse-4.0.0/src/muse/function_dialog_consts.h000066400000000000000000000055001404105311000211170ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // function_dialog_base.h // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __FUNCTION_DIALOG_CONSTS_H__ #define __FUNCTION_DIALOG_CONSTS_H__ namespace MusEGui { //-------------------------------------------------------- // FunctionDialogElements: Types of common elements which // can be displayed in all function dialogs: //-------------------------------------------------------- enum FunctionDialogElements { FunctionDialogNoElements = 0x0000, //------------------------------ // Buttons: //------------------------------ FunctionAllEventsButton = 0x100, FunctionSelectedEventsButton = 0x200, FunctionLoopedButton = 0x400, FunctionSelectedLoopedButton = 0x800, FunctionAllPartsButton = 0x1000, FunctionSelectedPartsButton = 0x2000, FunctionDialogAllElements = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton | FunctionAllPartsButton | FunctionSelectedPartsButton }; // Combination of FunctionDialogElements flags. typedef int FunctionDialogElements_t; //------------------------------------------------ // FunctionDialogReturnFlags: Types of convenience // return flags common to all function dialogs // (individual dialog members can also be accessed): //------------------------------------------------ enum FunctionDialogReturnFlags { FunctionReturnNoFlags = 0x0, FunctionReturnAllEvents = 0x01, FunctionReturnLooped = 0x02, FunctionReturnAllParts = 0x04, FunctionReturnAllFlags = FunctionReturnAllEvents | FunctionReturnLooped | FunctionReturnAllParts }; // Combination of FunctionDialogReturnFlags flags. typedef int FunctionReturnDialogFlags_t; const int functionQuantizeRasterVals[] = { 1, // Whole note divisor 2, // Half note divisor 4, // 4th note divisor 6, // 4thT divisor 8, // 8th divisor 12,//8thT divisor 16,// ... 24, 32, 48, 64 }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/000077500000000000000000000000001404105311000175405ustar00rootroot00000000000000muse-4.0.0/src/muse/function_dialogs/CMakeLists.txt000066400000000000000000000057361404105311000223130ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP (widgets_functiondialogs_mocs crescendo.h deloverlaps.h function_dialog_base.h gatetime.h move.h quantize.h remove.h setlen.h transpose.h legato.h velocity.h ) ## ## UI files ## file (GLOB widgets_functiondialogs_ui_files crescendobase.ui deloverlapsbase.ui gatetimebase.ui movebase.ui quantbase.ui removebase.ui setlenbase.ui transposebase.ui legatobase.ui velocitybase.ui ) QT5_WRAP_UI (widgets_functiondialogs_ui_headers ${widgets_functiondialogs_ui_files}) ## ## List of source files to compile ## file (GLOB widgets_functiondialogs_source_files crescendo.cpp deloverlaps.cpp function_dialog_base.cpp gatetime.cpp move.cpp quantize.cpp remove.cpp setlen.cpp transpose.cpp legato.cpp velocity.cpp ) ## ## Define target ## add_library ( widgets_functiondialogs ${MODULES_BUILD} ${widgets_functiondialogs_ui_headers} ${widgets_functiondialogs_mocs} ${widgets_functiondialogs_source_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${widgets_functiondialogs_source_files} ${widgets_functiondialogs_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( widgets_functiondialogs # PROPERTIES COMPILE_FLAGS "${MUSECXXFLAGS} -I../ -I${PROJECT_SOURCE_DIR}/synti " PROPERTIES COMPILE_FLAGS "${MUSECXXFLAGS} -I../ -I${PROJECT_SOURCE_DIR}/synti" OUTPUT_NAME muse_widgets_functiondialogs ) ## ## Linkage ## target_link_libraries ( widgets_functiondialogs ${QT_LIBRARIES} xml_module # icons ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS widgets_functiondialogs DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/function_dialogs/crescendo.cpp000066400000000000000000000124631404105311000222170ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: crescendo.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "crescendo.h" // Forwards from header: #include "xml.h" namespace MusEGui { FunctionReturnDialogFlags_t Crescendo::_ret_flags = FunctionReturnNoFlags; FunctionDialogElements_t Crescendo::_elements = FunctionDialogNoElements; int Crescendo::_range = FunctionLoopedButton; int Crescendo::_parts = FunctionSelectedPartsButton; int Crescendo::start_val = 80; int Crescendo::end_val = 130; bool Crescendo::absolute = 0; Crescendo::Crescendo(QWidget* parent) : FunctionDialogBase(parent) { setupUi(this); //-------------------------------------------- // Set range and parts containers if available. //-------------------------------------------- _range_container = rangeBox; _parts_container = partsBox; //-------------------------------------------- // Add element widgets to range and parts groups. //-------------------------------------------- _range_group->addButton(looped_events_button, FunctionLoopedButton); _range_group->addButton(selected_looped_button, FunctionSelectedLoopedButton); _parts_group->addButton(not_all_parts_button, FunctionSelectedPartsButton); _parts_group->addButton(all_parts_button, FunctionAllPartsButton); connect(absolute_button, SIGNAL(toggled(bool)), SLOT(absolute_changed(bool))); } void Crescendo::pull_values() { //-------------------------------------------- // Grab IDs or values from common base object // (range and parts groups etc.) //-------------------------------------------- FunctionDialogBase::pull_values(); //-------------------------------------------- // Grab this dialog's specific IDs or values. //-------------------------------------------- start_val = start_spinbox->value(); end_val = end_spinbox->value(); absolute = absolute_button->isChecked(); } void Crescendo::setupDialog() { //------------------------------------ // Setup common base object items. //------------------------------------ FunctionDialogBase::setupDialog(); //------------------------------------ // Setup this dialog's specific items. //------------------------------------ start_spinbox->setValue(start_val); end_spinbox->setValue(end_val); absolute_button->setChecked(absolute); } void Crescendo::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: //----------------------------------------- // Handle any common base settings. //----------------------------------------- if(!FunctionDialogBase::read_configuration(tag, xml)) { //----------------------------------------- // Handle this dialog's specific settings. //----------------------------------------- if (tag == "range") _range = xml.parseInt(); else if (tag == "parts") _parts = xml.parseInt(); else if (tag == "start") start_val=xml.parseInt(); else if (tag == "end") end_val=xml.parseInt(); else if (tag == "absolute") absolute=xml.parseInt(); else xml.unknown("Crescendo"); } break; case MusECore::Xml::TagEnd: if (tag == "crescendo") return; default: break; } } } void Crescendo::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "crescendo"); //----------------------------------------- // Write any common base settings. //----------------------------------------- FunctionDialogBase::write_configuration(level, xml); //----------------------------------------- // Write this dialog's specific settings. //----------------------------------------- xml.intTag(level, "range", _range); xml.intTag(level, "parts", _parts); xml.intTag(level, "start", start_val); xml.intTag(level, "end", end_val); xml.intTag(level, "absolute", absolute); xml.tag(level, "/crescendo"); } void Crescendo::absolute_changed(bool val) { if (val) { start_spinbox->setMaximum(127); start_spinbox->setSuffix(""); end_spinbox->setMaximum(127); end_spinbox->setSuffix(""); } else { start_spinbox->setMaximum(12700); start_spinbox->setSuffix(" %"); end_spinbox->setMaximum(12700); end_spinbox->setSuffix(" %"); } } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/crescendo.h000066400000000000000000000043741404105311000216660ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: crescendo.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CRESCENDO_H__ #define __CRESCENDO_H__ #include "ui_crescendobase.h" #include "function_dialog_base.h" // Forward declarations: namespace MusECore { class Xml; } namespace MusEGui { class Crescendo : public FunctionDialogBase, public Ui::CrescendoBase { Q_OBJECT protected slots: void pull_values(); public: Crescendo(QWidget* parent = 0); static int _range; static int _parts; static int start_val; static int end_val; static bool absolute; static FunctionReturnDialogFlags_t _ret_flags; static FunctionDialogElements_t _elements; static void setElements(FunctionDialogElements_t elements = FunctionLoopedButton | FunctionSelectedLoopedButton) { _elements = elements; } static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); void setupDialog(); int curRange() const { return _range; } void setCurRange(int range) { _range = range; } int curParts() const { return _parts; } void setCurParts(int parts) { _parts = parts; } void setReturnFlags(FunctionReturnDialogFlags_t f) { _ret_flags = f; } FunctionDialogElements_t elements() const { return _elements; } private slots: void absolute_changed(bool); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/crescendobase.ui000066400000000000000000000175351404105311000227120ustar00rootroot00000000000000 CrescendoBase 0 0 275 454 MusE: Crescendo/Decrescendo 6 11 11 11 11 Range 6 11 11 11 11 &Looped Events Selected Looped true 0 0 Parts 4 6 4 6 4 Selected parts or c&urrent part true All parts &in editor Values false false 11 11 11 11 6 Start velocity false % 0 12700 80 End velocity &Absolute false Rela&tive true % 12700 130 Qt::Vertical 20 40 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel okButton clicked() CrescendoBase accept() 20 20 20 20 cancelButton clicked() CrescendoBase reject() 20 20 20 20 muse-4.0.0/src/muse/function_dialogs/deloverlaps.cpp000066400000000000000000000073761404105311000226010ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: deloverlaps.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "deloverlaps.h" // Forwards from header: #include "xml.h" namespace MusEGui { FunctionReturnDialogFlags_t DelOverlaps::_ret_flags = FunctionReturnNoFlags; FunctionDialogElements_t DelOverlaps::_elements = FunctionDialogNoElements; int DelOverlaps::_range = FunctionSelectedEventsButton; int DelOverlaps::_parts = FunctionSelectedPartsButton; DelOverlaps::DelOverlaps(QWidget* parent) : FunctionDialogBase(parent) { setupUi(this); //-------------------------------------------- // Set range and parts containers if available. //-------------------------------------------- _range_container = rangeBox; _parts_container = partsBox; //-------------------------------------------- // Add element widgets to range and parts groups. //-------------------------------------------- _range_group->addButton(all_events_button, FunctionAllEventsButton); _range_group->addButton(selected_events_button,FunctionSelectedEventsButton); _range_group->addButton(looped_events_button, FunctionLoopedButton); _range_group->addButton(selected_looped_button, FunctionSelectedLoopedButton); _parts_group->addButton(not_all_parts_button, FunctionSelectedPartsButton); _parts_group->addButton(all_parts_button, FunctionAllPartsButton); } void DelOverlaps::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: //----------------------------------------- // Handle any common base settings. //----------------------------------------- if(!FunctionDialogBase::read_configuration(tag, xml)) { //----------------------------------------- // Handle this dialog's specific settings. //----------------------------------------- if (tag == "range") _range = xml.parseInt(); else if (tag == "parts") _parts = xml.parseInt(); else xml.unknown("DelOverlaps"); } break; case MusECore::Xml::TagEnd: if (tag == "del_overlaps") return; default: break; } } } void DelOverlaps::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "del_overlaps"); //----------------------------------------- // Write any common base settings. //----------------------------------------- FunctionDialogBase::write_configuration(level, xml); //----------------------------------------- // Write this dialog's specific settings. //----------------------------------------- xml.intTag(level, "range", _range); xml.intTag(level, "parts", _parts); xml.tag(level, "/del_overlaps"); } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/deloverlaps.h000066400000000000000000000042021404105311000222270ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: deloverlaps.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DELOVERLAPS_H__ #define __DELOVERLAPS_H__ #include "ui_deloverlapsbase.h" #include "function_dialog_base.h" // Forward declarations: namespace MusECore { class Xml; } namespace MusEGui { class DelOverlaps : public FunctionDialogBase, public Ui::DelOverlapsBase { Q_OBJECT public: DelOverlaps(QWidget* parent = 0); static int _range; static int _parts; static FunctionReturnDialogFlags_t _ret_flags; static FunctionDialogElements_t _elements; static void setElements(FunctionDialogElements_t elements = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton) { _elements = elements; } static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); int curRange() const { return _range; } void setCurRange(int range) { _range = range; } int curParts() const { return _parts; } void setCurParts(int parts) { _parts = parts; } void setReturnFlags(FunctionReturnDialogFlags_t f) { _ret_flags = f; } FunctionDialogElements_t elements() const { return _elements; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/deloverlapsbase.ui000066400000000000000000000133051404105311000232540ustar00rootroot00000000000000 DelOverlapsBase true 0 0 275 332 MusE: Delete Overlaps 6 11 11 11 11 Range 6 11 11 11 11 All Events Selected Events true Looped Events Selected Looped Qt::Vertical 20 40 0 0 Parts 4 6 4 6 4 Selected parts or c&urrent part true All parts &in editor 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel okButton clicked() DelOverlapsBase accept() 20 20 20 20 cancelButton clicked() DelOverlapsBase reject() 20 20 20 20 muse-4.0.0/src/muse/function_dialogs/function_dialog_base.cpp000066400000000000000000000134501404105311000244050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // function_dialog_base.cpp // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "function_dialog_base.h" // Forwards from header: #include #include #include "xml.h" namespace MusEGui { FunctionDialogBase::FunctionDialogBase(QWidget* parent) : QDialog(parent) { _range_container = NULL; _parts_container = NULL; _range_group = new QButtonGroup; _parts_group = new QButtonGroup; _range_group->setExclusive(true); _parts_group->setExclusive(true); } FunctionDialogBase::~FunctionDialogBase() { delete _parts_group; delete _range_group; } void FunctionDialogBase::pull_values() { //-------------------------------------------- // Grab current IDs from range and parts groups. //-------------------------------------------- setCurRange(_range_group->checkedId()); setCurParts(_parts_group->checkedId()); //-------------------------------------------- // Set convenience return flags. //-------------------------------------------- setReturnFlags(calc_return_flags()); } void FunctionDialogBase::accept() { pull_values(); QDialog::accept(); } void FunctionDialogBase::setupButton(QButtonGroup* group, int buttonID, bool show) { QAbstractButton* bt = group->button(buttonID); if(!bt) return; bt->setEnabled(show); bt->setVisible(show); } void FunctionDialogBase::setupDialog() { const FunctionDialogElements_t elem = elements(); //-------------------------------------------- // Show or hide the range and parts containers. // The containers need to be enabled first before // their controls can be enabled in setupButton(). //-------------------------------------------- if(_range_container) { const bool show = elem & (FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton); _range_container->setEnabled(show); _range_container->setVisible(show); } if(_parts_container) { const bool show = elem & (FunctionAllPartsButton | FunctionSelectedPartsButton); _parts_container->setEnabled(show); _parts_container->setVisible(show); } //-------------------------------------------- // Now show or hide the individual controls in // the range and parts containers. //-------------------------------------------- setupButton(_range_group, FunctionAllEventsButton, elem & FunctionAllEventsButton); setupButton(_range_group, FunctionSelectedEventsButton, elem & FunctionSelectedEventsButton); setupButton(_range_group, FunctionLoopedButton, elem & FunctionLoopedButton); setupButton(_range_group, FunctionSelectedLoopedButton, elem & FunctionSelectedLoopedButton); setupButton(_parts_group, FunctionAllPartsButton, elem & FunctionAllPartsButton); setupButton(_parts_group, FunctionSelectedPartsButton, elem & FunctionSelectedPartsButton); //--------------------------------------- // If the current range is invalid or the // button is not visible, choose another. //--------------------------------------- QAbstractButton* bt = _range_group->button(curRange()); if(!bt || !bt->isEnabled()) { QList bl = _range_group->buttons(); const int range_group_sz = bl.size(); for(int i = 0; i < range_group_sz; ++i) { bt = bl.at(i); if(bt->isEnabled()) { setCurRange(_range_group->id(bt)); break; } } } bt =_range_group->button(curRange()); if(bt) bt->setChecked(true); //--------------------------------------- // If the current parts is invalid or the // button is not visible, choose another. //--------------------------------------- bt = _parts_group->button(curParts()); if(!bt || !bt->isEnabled()) { QList bl = _parts_group->buttons(); const int parts_group_sz = bl.size(); for(int i = 0; i < parts_group_sz; ++i) { bt = bl.at(i); if(bt->isEnabled()) { setCurParts(_parts_group->id(bt)); break; } } } bt = _parts_group->button(curParts()); if(bt) bt->setChecked(true); } int FunctionDialogBase::exec() { //----------------------------------- // Set up the dialog according to the // various settings. //----------------------------------- setupDialog(); //----------------------------------- // Now execute the dialog. //----------------------------------- return QDialog::exec(); } bool FunctionDialogBase::read_configuration(const QString& /*tag*/, MusECore::Xml& /*xml*/) { // if (tag == "range") // { // setCurRange(xml.parseInt()); // return true; // } // else if (tag == "parts") // { // setCurParts(xml.parseInt()); // return true; // } return false; } void FunctionDialogBase::write_configuration(int /*level*/, MusECore::Xml& /*xml*/) { // xml.intTag(level, "range", curRange()); // xml.intTag(level, "parts", curParts()); } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/function_dialog_base.h000066400000000000000000000056611404105311000240570ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // function_dialog_base.h // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __FUNCTION_DIALOG_BASE_H__ #define __FUNCTION_DIALOG_BASE_H__ #include "function_dialog_consts.h" #include // Forward declarations: class QButtonGroup; class QWidget; namespace MusECore { class Xml; } namespace MusEGui { class FunctionDialogBase : public QDialog { Q_OBJECT protected: QButtonGroup* _range_group; QButtonGroup* _parts_group; QWidget* _range_container; QWidget* _parts_container; void setupButton(QButtonGroup* group, int buttonID, bool show); virtual FunctionReturnDialogFlags_t calc_return_flags() { const int cr = curRange(); const int cp = curParts(); FunctionReturnDialogFlags_t ret_flags = FunctionReturnNoFlags; if(cr == FunctionAllEventsButton || cr == FunctionLoopedButton) ret_flags |= FunctionReturnAllEvents; if(cp == FunctionAllPartsButton) ret_flags |= FunctionReturnAllParts; if(cr == FunctionLoopedButton || cr == FunctionSelectedLoopedButton) ret_flags |= FunctionReturnLooped; return ret_flags; } protected slots: virtual void accept(); virtual void pull_values(); public: FunctionDialogBase(QWidget* parent = 0); virtual ~FunctionDialogBase(); // Returns true if the tag was handled by this base class, otherwsise false. static bool read_configuration(const QString& tag, MusECore::Xml& xml); // Writes a small portion, just part of the overall method in the sub classes. virtual void write_configuration(int level, MusECore::Xml& xml); virtual void setupDialog(); virtual int curRange() const { return -1; } virtual void setCurRange(int) { } virtual int curParts() const { return -1; } virtual void setCurParts(int) { } virtual FunctionDialogElements_t elements() const { return FunctionDialogNoElements; } virtual void setReturnFlags(FunctionReturnDialogFlags_t) { } public slots: virtual int exec(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/gatetime.cpp000066400000000000000000000121771404105311000220530ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: gatetime.cpp,v 1.1.1.1 2003/10/27 18:54:37 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "gatetime.h" #include "xml.h" namespace MusEGui { FunctionReturnDialogFlags_t GateTime::_ret_flags = FunctionReturnNoFlags; FunctionDialogElements_t GateTime::_elements = FunctionDialogNoElements; int GateTime::_range = FunctionSelectedEventsButton; int GateTime::_parts = FunctionSelectedPartsButton; int GateTime::rateVal = 100; int GateTime::offsetVal = 0; //--------------------------------------------------------- // GateTime //--------------------------------------------------------- GateTime::GateTime(QWidget* parent) : FunctionDialogBase(parent) { setupUi(this); //-------------------------------------------- // Set range and parts containers if available. //-------------------------------------------- _range_container = rangeBox; _parts_container = partsBox; //-------------------------------------------- // Add element widgets to range and parts groups. //-------------------------------------------- _range_group->addButton(allButton, FunctionAllEventsButton); _range_group->addButton(selButton,FunctionSelectedEventsButton); _range_group->addButton(loopButton, FunctionLoopedButton); _range_group->addButton(sloopButton, FunctionSelectedLoopedButton); _parts_group->addButton(not_all_parts_button, FunctionSelectedPartsButton); _parts_group->addButton(all_parts_button, FunctionAllPartsButton); } //--------------------------------------------------------- // pullValues //--------------------------------------------------------- void GateTime::pull_values() { //-------------------------------------------- // Grab IDs or values from common base object // (range and parts groups etc.) //-------------------------------------------- FunctionDialogBase::pull_values(); //-------------------------------------------- // Grab this dialog's specific IDs or values. //-------------------------------------------- rateVal = rate->value(); offsetVal = offset->value(); } void GateTime::setupDialog() { //------------------------------------ // Setup common base object items. //------------------------------------ FunctionDialogBase::setupDialog(); //------------------------------------ // Setup this dialog's specific items. //------------------------------------ rate->setValue(rateVal); offset->setValue(offsetVal); } void GateTime::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: //----------------------------------------- // Handle any common base settings. //----------------------------------------- if(!FunctionDialogBase::read_configuration(tag, xml)) { //----------------------------------------- // Handle this dialog's specific settings. //----------------------------------------- if (tag == "range") _range = xml.parseInt(); else if (tag == "parts") _parts = xml.parseInt(); else if (tag == "rate") rateVal=xml.parseInt(); else if (tag == "offset") offsetVal=xml.parseInt(); else xml.unknown("ModLen"); } break; case MusECore::Xml::TagEnd: if (tag == "mod_len") return; default: break; } } } void GateTime::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "mod_len"); //----------------------------------------- // Write any common base settings. //----------------------------------------- FunctionDialogBase::write_configuration(level, xml); //----------------------------------------- // Write this dialog's specific settings. //----------------------------------------- xml.intTag(level, "range", _range); xml.intTag(level, "parts", _parts); xml.intTag(level, "offset", offsetVal); xml.intTag(level, "rate", rateVal); xml.tag(level, "/mod_len"); } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/gatetime.h000066400000000000000000000046471404105311000215230ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: gatetime.h,v 1.1.1.1.2.1 2008/01/19 13:33:47 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __GATETIME_H__ #define __GATETIME_H__ #include "ui_gatetimebase.h" #include "function_dialog_base.h" namespace MusECore { class Xml; } namespace MusEGui { //--------------------------------------------------------- // GateTime //--------------------------------------------------------- class GateTime : public FunctionDialogBase, public Ui::GateTimeBase { Q_OBJECT protected slots: void pull_values(); public: GateTime(QWidget* parent=0); static int _range; static int _parts; static int rateVal; static int offsetVal; static FunctionReturnDialogFlags_t _ret_flags; static FunctionDialogElements_t _elements; static void setElements(FunctionDialogElements_t elements = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton) { _elements = elements; } static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); void setupDialog(); int curRange() const { return _range; } void setCurRange(int range) { _range = range; } int curParts() const { return _parts; } void setCurParts(int parts) { _parts = parts; } void setReturnFlags(FunctionReturnDialogFlags_t f) { _ret_flags = f; } FunctionDialogElements_t elements() const { return _elements; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/gatetimebase.ui000066400000000000000000000175251404105311000225430ustar00rootroot00000000000000 GateTimeBase 0 0 275 485 MusE: Modify Event Length 6 11 11 11 11 Range 6 11 11 11 11 All Events Selected Events true Looped Events Selected Looped 0 0 Parts 4 6 4 6 4 Selected parts or c&urrent part true All parts &in editor Values 11 11 11 11 6 Rate: false Offset: false % 1000 100 -999 999 1 lenNew = (lenOld * rate) + offset Qt::Vertical 20 40 6 0 0 0 0 Qt::Horizontal QSizePolicy::Expanding 0 0 OK false true Cancel okButton clicked() GateTimeBase accept() 20 20 20 20 cancelButton clicked() GateTimeBase reject() 20 20 20 20 muse-4.0.0/src/muse/function_dialogs/legato.cpp000066400000000000000000000115201404105311000215160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: legato.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "legato.h" #include "xml.h" namespace MusEGui { FunctionReturnDialogFlags_t Legato::_ret_flags = FunctionReturnNoFlags; FunctionDialogElements_t Legato::_elements = FunctionDialogNoElements; int Legato::_range = FunctionSelectedEventsButton; int Legato::_parts = FunctionSelectedPartsButton; int Legato::min_len = 0; bool Legato::allow_shortening = 0; Legato::Legato(QWidget* parent) : FunctionDialogBase(parent) { setupUi(this); //-------------------------------------------- // Set range and parts containers if available. //-------------------------------------------- _range_container = rangeBox; _parts_container = partsBox; //-------------------------------------------- // Add element widgets to range and parts groups. //-------------------------------------------- _range_group->addButton(all_events_button, FunctionAllEventsButton); _range_group->addButton(selected_events_button,FunctionSelectedEventsButton); _range_group->addButton(looped_events_button, FunctionLoopedButton); _range_group->addButton(selected_looped_button, FunctionSelectedLoopedButton); _parts_group->addButton(not_all_parts_button, FunctionSelectedPartsButton); _parts_group->addButton(all_parts_button, FunctionAllPartsButton); } void Legato::pull_values() { //-------------------------------------------- // Grab IDs or values from common base object // (range and parts groups etc.) //-------------------------------------------- FunctionDialogBase::pull_values(); //-------------------------------------------- // Grab this dialog's specific IDs or values. //-------------------------------------------- min_len = len_spinbox->value(); allow_shortening = allow_shorten_checkbox->isChecked(); } void Legato::setupDialog() { //------------------------------------ // Setup common base object items. //------------------------------------ FunctionDialogBase::setupDialog(); //------------------------------------ // Setup this dialog's specific items. //------------------------------------ len_spinbox->setValue(min_len); allow_shorten_checkbox->setChecked(allow_shortening); } void Legato::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: //----------------------------------------- // Handle any common base settings. //----------------------------------------- if(!FunctionDialogBase::read_configuration(tag, xml)) { //----------------------------------------- // Handle this dialog's specific settings. //----------------------------------------- if (tag == "range") _range = xml.parseInt(); else if (tag == "parts") _parts = xml.parseInt(); else if (tag == "min_len") min_len=xml.parseInt(); else if (tag == "allow_shortening") allow_shortening=xml.parseInt(); else xml.unknown("Legato"); } break; case MusECore::Xml::TagEnd: if (tag == "legato") return; default: break; } } } void Legato::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "legato"); //----------------------------------------- // Write any common base settings. //----------------------------------------- FunctionDialogBase::write_configuration(level, xml); //----------------------------------------- // Write this dialog's specific settings. //----------------------------------------- xml.intTag(level, "range", _range); xml.intTag(level, "parts", _parts); xml.intTag(level, "min_len", min_len); xml.intTag(level, "allow_shortening", allow_shortening); xml.tag(level, "/legato"); } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/legato.h000066400000000000000000000043031404105311000211640ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: legato.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LEGATO_H__ #define __LEGATO_H__ #include "ui_legatobase.h" #include "function_dialog_base.h" namespace MusECore { class Xml; } namespace MusEGui { class Legato : public FunctionDialogBase, public Ui::LegatoBase { Q_OBJECT protected slots: void pull_values(); public: Legato(QWidget* parent = 0); static int _range; static int _parts; static int min_len; static bool allow_shortening; static FunctionReturnDialogFlags_t _ret_flags; static FunctionDialogElements_t _elements; static void setElements(FunctionDialogElements_t elements = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton) { _elements = elements; } static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); void setupDialog(); int curRange() const { return _range; } void setCurRange(int range) { _range = range; } int curParts() const { return _parts; } void setCurParts(int parts) { _parts = parts; } void setReturnFlags(FunctionReturnDialogFlags_t f) { _ret_flags = f; } FunctionDialogElements_t elements() const { return _elements; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/legatobase.ui000066400000000000000000000202261404105311000222070ustar00rootroot00000000000000 LegatoBase true 0 0 275 455 MusE: Legato 6 11 11 11 11 Range 6 11 11 11 11 All Events Selected Events true Looped Events Selected Looped 0 0 Parts 4 6 4 6 4 Selected parts or c&urrent part true All parts &in editor Settings false false 11 11 11 11 6 true true ticks 0 10000 1 0 Minimum Length 0 0 Allow shortening notes Qt::RightToLeft Qt::Vertical 20 40 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel okButton clicked() LegatoBase accept() 20 20 20 20 cancelButton clicked() LegatoBase reject() 20 20 20 20 muse-4.0.0/src/muse/function_dialogs/move.cpp000066400000000000000000000110171404105311000212120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: move.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "move.h" #include "xml.h" namespace MusEGui { FunctionReturnDialogFlags_t Move::_ret_flags = FunctionReturnNoFlags; FunctionDialogElements_t Move::_elements = FunctionDialogNoElements; int Move::_range = FunctionSelectedEventsButton; int Move::_parts = FunctionSelectedPartsButton; int Move::amount = 0; Move::Move(QWidget* parent) : FunctionDialogBase(parent) { setupUi(this); //-------------------------------------------- // Set range and parts containers if available. //-------------------------------------------- _range_container = rangeBox; _parts_container = partsBox; //-------------------------------------------- // Add element widgets to range and parts groups. //-------------------------------------------- _range_group->addButton(all_events_button, FunctionAllEventsButton); _range_group->addButton(selected_events_button,FunctionSelectedEventsButton); _range_group->addButton(looped_events_button, FunctionLoopedButton); _range_group->addButton(selected_looped_button, FunctionSelectedLoopedButton); _parts_group->addButton(not_all_parts_button, FunctionSelectedPartsButton); _parts_group->addButton(all_parts_button, FunctionAllPartsButton); } void Move::pull_values() { //-------------------------------------------- // Grab IDs or values from common base object // (range and parts groups etc.) //-------------------------------------------- FunctionDialogBase::pull_values(); //-------------------------------------------- // Grab this dialog's specific IDs or values. //-------------------------------------------- amount = amount_spinbox->value(); } void Move::setupDialog() { //------------------------------------ // Setup common base object items. //------------------------------------ FunctionDialogBase::setupDialog(); //------------------------------------ // Setup this dialog's specific items. //------------------------------------ amount_spinbox->setValue(amount); } void Move::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: //----------------------------------------- // Handle any common base settings. //----------------------------------------- if(!FunctionDialogBase::read_configuration(tag, xml)) { //----------------------------------------- // Handle this dialog's specific settings. //----------------------------------------- if (tag == "range") _range = xml.parseInt(); else if (tag == "parts") _parts = xml.parseInt(); else if (tag == "amount") amount=xml.parseInt(); else xml.unknown("Move"); } break; case MusECore::Xml::TagEnd: if (tag == "move") return; default: break; } } } void Move::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "move"); //----------------------------------------- // Write any common base settings. //----------------------------------------- FunctionDialogBase::write_configuration(level, xml); //----------------------------------------- // Write this dialog's specific settings. //----------------------------------------- xml.intTag(level, "range", _range); xml.intTag(level, "parts", _parts); xml.intTag(level, "amount", amount); xml.tag(level, "/move"); } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/move.h000066400000000000000000000042261404105311000206630ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: move.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MOVE_H__ #define __MOVE_H__ #include "ui_movebase.h" #include "function_dialog_base.h" namespace MusECore { class Xml; } namespace MusEGui { class Move : public FunctionDialogBase, public Ui::MoveBase { Q_OBJECT protected slots: void pull_values(); public: Move(QWidget* parent = 0); static int _range; static int _parts; static int amount; static FunctionReturnDialogFlags_t _ret_flags; static FunctionDialogElements_t _elements; static void setElements(FunctionDialogElements_t elements = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton) { _elements = elements; } static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); void setupDialog(); int curRange() const { return _range; } void setCurRange(int range) { _range = range; } int curParts() const { return _parts; } void setCurParts(int parts) { _parts = parts; } void setReturnFlags(FunctionReturnDialogFlags_t f) { _ret_flags = f; } FunctionDialogElements_t elements() const { return _elements; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/movebase.ui000066400000000000000000000163701404105311000217070ustar00rootroot00000000000000 MoveBase 0 0 279 423 MusE: Move Events 6 11 11 11 11 Range 6 11 11 11 11 All Events Selected Events true Looped Events Selected Looped 0 0 Parts 4 6 4 6 4 Selected parts or c&urrent part true All parts &in editor Value false false 11 11 11 11 6 Move by false true ticks -9999999 9999999 386 0 Qt::Vertical 20 40 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel okButton clicked() MoveBase accept() 20 20 20 20 cancelButton clicked() MoveBase reject() 20 20 20 20 muse-4.0.0/src/muse/function_dialogs/quantbase.ui000066400000000000000000000254361404105311000220740ustar00rootroot00000000000000 QuantBase true 0 0 439 593 MusE: Quantize 6 11 11 11 11 Range 6 11 11 11 11 All Events Selected Events true Looped Events Selected Looped 0 0 Parts 4 6 4 6 4 Selected parts or c&urrent part true All parts &in editor Values 11 11 11 11 6 Strength: false % 100 80 Threshold (ticks): false 0 10000 1 Quantize Len true Raster false 0 true Whole Half 4th 4th Triplet 8th 8th Triplet 16th 16th Triplet 32th 32th Triplet Swing: -99 0 0 If the proposed change in tick or length is smaller than threshold, nothing is done. If swing=0, this is normal If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. true Qt::Vertical 20 40 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel ok_button clicked() QuantBase accept() 20 20 20 20 cancel_button clicked() QuantBase reject() 20 20 20 20 muse-4.0.0/src/muse/function_dialogs/quantize.cpp000066400000000000000000000127101404105311000221050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: quantize.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "quantize.h" #include "xml.h" namespace MusEGui { FunctionReturnDialogFlags_t Quantize::_ret_flags = FunctionReturnNoFlags; FunctionDialogElements_t Quantize::_elements = FunctionDialogNoElements; int Quantize::_range = FunctionSelectedEventsButton; int Quantize::_parts = FunctionSelectedPartsButton; int Quantize::strength = 90; int Quantize::threshold = 0; int Quantize::raster_index = 3; int Quantize::swing = 0; bool Quantize::quant_len = 1; Quantize::Quantize(QWidget* parent) : FunctionDialogBase(parent) { setupUi(this); //-------------------------------------------- // Set range and parts containers if available. //-------------------------------------------- _range_container = rangeBox; _parts_container = partsBox; //-------------------------------------------- // Add element widgets to range and parts groups. //-------------------------------------------- _range_group->addButton(all_events_button, FunctionAllEventsButton); _range_group->addButton(selected_events_button,FunctionSelectedEventsButton); _range_group->addButton(looped_events_button, FunctionLoopedButton); _range_group->addButton(selected_looped_button, FunctionSelectedLoopedButton); _parts_group->addButton(not_all_parts_button, FunctionSelectedPartsButton); _parts_group->addButton(all_parts_button, FunctionAllPartsButton); } void Quantize::pull_values() { //-------------------------------------------- // Grab IDs or values from common base object // (range and parts groups etc.) //-------------------------------------------- FunctionDialogBase::pull_values(); //-------------------------------------------- // Grab this dialog's specific IDs or values. //-------------------------------------------- strength = strength_spinbox->value(); threshold = threshold_spinbox->value(); raster_index = raster_combobox->currentIndex(); quant_len = len_checkbox->isChecked(); swing = swing_spinbox->value(); } void Quantize::setupDialog() { //------------------------------------ // Setup common base object items. //------------------------------------ FunctionDialogBase::setupDialog(); //------------------------------------ // Setup this dialog's specific items. //------------------------------------ strength_spinbox->setValue(strength); threshold_spinbox->setValue(threshold); raster_combobox->setCurrentIndex(raster_index); len_checkbox->setChecked(quant_len); swing_spinbox->setValue(swing); } void Quantize::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: //----------------------------------------- // Handle any common base settings. //----------------------------------------- if(!FunctionDialogBase::read_configuration(tag, xml)) { //----------------------------------------- // Handle this dialog's specific settings. //----------------------------------------- if (tag == "range") _range = xml.parseInt(); else if (tag == "parts") _parts = xml.parseInt(); else if (tag == "strength") strength=xml.parseInt(); else if (tag == "threshold") threshold=xml.parseInt(); else if (tag == "raster") raster_index=xml.parseInt(); else if (tag == "swing") swing=xml.parseInt(); else if (tag == "quant_len") quant_len=xml.parseInt(); else xml.unknown("Quantize"); } break; case MusECore::Xml::TagEnd: if (tag == "quantize") return; default: break; } } } void Quantize::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "quantize"); //----------------------------------------- // Write any common base settings. //----------------------------------------- FunctionDialogBase::write_configuration(level, xml); //----------------------------------------- // Write this dialog's specific settings. //----------------------------------------- xml.intTag(level, "range", _range); xml.intTag(level, "parts", _parts); xml.intTag(level, "strength", strength); xml.intTag(level, "threshold", threshold); xml.intTag(level, "raster", raster_index); xml.intTag(level, "swing", swing); xml.intTag(level, "quant_len", quant_len); xml.tag(level, "/quantize"); } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/quantize.h000066400000000000000000000044161404105311000215560ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: quantize.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __QUANTIZE_H__ #define __QUANTIZE_H__ #include "ui_quantbase.h" #include "function_dialog_base.h" namespace MusECore { class Xml; } namespace MusEGui { class Quantize : public FunctionDialogBase, public Ui::QuantBase { Q_OBJECT protected slots: void pull_values(); public: Quantize(QWidget* parent = 0); static int _range; static int _parts; static int strength; static int threshold; static int raster_index; static int swing; static bool quant_len; static FunctionReturnDialogFlags_t _ret_flags; static FunctionDialogElements_t _elements; static void setElements(FunctionDialogElements_t elements = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton) { _elements = elements; } static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); void setupDialog(); int curRange() const { return _range; } void setCurRange(int range) { _range = range; } int curParts() const { return _parts; } void setCurParts(int parts) { _parts = parts; } void setReturnFlags(FunctionReturnDialogFlags_t f) { _ret_flags = f; } FunctionDialogElements_t elements() const { return _elements; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/remove.cpp000066400000000000000000000125311404105311000215430ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: remove.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "remove.h" #include "xml.h" namespace MusEGui { FunctionReturnDialogFlags_t Remove::_ret_flags = FunctionReturnNoFlags; FunctionDialogElements_t Remove::_elements = FunctionDialogNoElements; int Remove::_range = FunctionSelectedEventsButton; int Remove::_parts = FunctionSelectedPartsButton; int Remove::velo_threshold = 16; bool Remove::velo_thres_used = false; int Remove::len_threshold = 12; bool Remove::len_thres_used = false; Remove::Remove(QWidget* parent) : FunctionDialogBase(parent) { setupUi(this); //-------------------------------------------- // Set range and parts containers if available. //-------------------------------------------- _range_container = rangeBox; _parts_container = partsBox; //-------------------------------------------- // Add element widgets to range and parts groups. //-------------------------------------------- _range_group->addButton(all_events_button, FunctionAllEventsButton); _range_group->addButton(selected_events_button,FunctionSelectedEventsButton); _range_group->addButton(looped_events_button, FunctionLoopedButton); _range_group->addButton(selected_looped_button, FunctionSelectedLoopedButton); _parts_group->addButton(not_all_parts_button, FunctionSelectedPartsButton); _parts_group->addButton(all_parts_button, FunctionAllPartsButton); } void Remove::pull_values() { //-------------------------------------------- // Grab IDs or values from common base object // (range and parts groups etc.) //-------------------------------------------- FunctionDialogBase::pull_values(); //-------------------------------------------- // Grab this dialog's specific IDs or values. //-------------------------------------------- len_thres_used=len_checkbox->isChecked(); len_threshold=len_spinbox->value(); velo_thres_used=velo_checkbox->isChecked(); velo_threshold=velo_spinbox->value(); } void Remove::setupDialog() { //------------------------------------ // Setup common base object items. //------------------------------------ FunctionDialogBase::setupDialog(); //------------------------------------ // Setup this dialog's specific items. //------------------------------------ len_checkbox->setChecked(len_thres_used); len_spinbox->setValue(len_threshold); velo_checkbox->setChecked(velo_thres_used); velo_spinbox->setValue(velo_threshold); } void Remove::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: //----------------------------------------- // Handle any common base settings. //----------------------------------------- if(!FunctionDialogBase::read_configuration(tag, xml)) { //----------------------------------------- // Handle this dialog's specific settings. //----------------------------------------- if (tag == "range") _range = xml.parseInt(); else if (tag == "parts") _parts = xml.parseInt(); else if (tag == "velo_threshold") velo_threshold=xml.parseInt(); else if (tag == "velo_thres_used") velo_thres_used=xml.parseInt(); else if (tag == "len_threshold") len_threshold=xml.parseInt(); else if (tag == "len_thres_used") len_thres_used=xml.parseInt(); else xml.unknown("Erase"); } break; case MusECore::Xml::TagEnd: if (tag == "erase") return; default: break; } } } void Remove::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "erase"); //----------------------------------------- // Write any common base settings. //----------------------------------------- FunctionDialogBase::write_configuration(level, xml); //----------------------------------------- // Write this dialog's specific settings. //----------------------------------------- xml.intTag(level, "range", _range); xml.intTag(level, "parts", _parts); xml.intTag(level, "velo_threshold", velo_threshold); xml.intTag(level, "velo_thres_used", velo_thres_used); xml.intTag(level, "len_threshold", len_threshold); xml.intTag(level, "len_thres_used", len_thres_used); xml.tag(level, "/erase"); } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/remove.h000066400000000000000000000044751404105311000212200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: remove.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __REMOVE_H__ #define __REMOVE_H__ #include "ui_removebase.h" #include "function_dialog_base.h" namespace MusECore { class Xml; } namespace MusEGui { class Remove : public FunctionDialogBase, public Ui::RemoveBase { Q_OBJECT // private: protected slots: void pull_values(); public: Remove(QWidget* parent = 0); static int _range; static int _parts; static int velo_threshold; static bool velo_thres_used; static int len_threshold; static bool len_thres_used; static FunctionReturnDialogFlags_t _ret_flags; static FunctionDialogElements_t _elements; static void setElements(FunctionDialogElements_t elements = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton) { _elements = elements; } static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); void setupDialog(); int curRange() const { return _range; } void setCurRange(int range) { _range = range; } int curParts() const { return _parts; } void setCurParts(int parts) { _parts = parts; } void setReturnFlags(FunctionReturnDialogFlags_t f) { _ret_flags = f; } FunctionDialogElements_t elements() const { return _elements; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/removebase.ui000066400000000000000000000256511404105311000222400ustar00rootroot00000000000000 RemoveBase true 0 0 273 515 0 0 MusE: Erase Events 2 6 4 6 4 0 0 Range 4 6 4 6 4 All Eve&nts Selec&ted Events true &Looped Events Selected L&ooped 0 0 Parts 4 6 4 6 4 Selected parts or c&urrent part true All parts &in editor 0 0 Thresholds false false 6 4 6 4 6 false true 0 127 1 16 false ticks 10000 12 Velocity Length 0 0 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> Qt::AutoText true Qt::Vertical QSizePolicy::Expanding 20 10 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel okButton clicked() RemoveBase accept() 20 20 20 20 cancelButton clicked() RemoveBase reject() 20 20 20 20 velo_checkbox toggled(bool) velo_spinbox setEnabled(bool) 83 192 198 193 len_checkbox toggled(bool) len_spinbox setEnabled(bool) 83 221 198 222 muse-4.0.0/src/muse/function_dialogs/setlen.cpp000066400000000000000000000110301404105311000215310ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: setlen.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "setlen.h" #include "xml.h" namespace MusEGui { FunctionReturnDialogFlags_t Setlen::_ret_flags = FunctionReturnNoFlags; FunctionDialogElements_t Setlen::_elements = FunctionDialogNoElements; int Setlen::_range = FunctionSelectedEventsButton; int Setlen::_parts = FunctionSelectedPartsButton; int Setlen::len = 384; Setlen::Setlen(QWidget* parent) : FunctionDialogBase(parent) { setupUi(this); //-------------------------------------------- // Set range and parts containers if available. //-------------------------------------------- _range_container = rangeBox; _parts_container = partsBox; //-------------------------------------------- // Add element widgets to range and parts groups. //-------------------------------------------- _range_group->addButton(all_events_button, FunctionAllEventsButton); _range_group->addButton(selected_events_button,FunctionSelectedEventsButton); _range_group->addButton(looped_events_button, FunctionLoopedButton); _range_group->addButton(selected_looped_button, FunctionSelectedLoopedButton); _parts_group->addButton(not_all_parts_button, FunctionSelectedPartsButton); _parts_group->addButton(all_parts_button, FunctionAllPartsButton); } void Setlen::pull_values() { //-------------------------------------------- // Grab IDs or values from common base object // (range and parts groups etc.) //-------------------------------------------- FunctionDialogBase::pull_values(); //-------------------------------------------- // Grab this dialog's specific IDs or values. //-------------------------------------------- len = len_spinbox->value(); } void Setlen::setupDialog() { //------------------------------------ // Setup common base object items. //------------------------------------ FunctionDialogBase::setupDialog(); //------------------------------------ // Setup this dialog's specific items. //------------------------------------ len_spinbox->setValue(len); } void Setlen::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: //----------------------------------------- // Handle any common base settings. //----------------------------------------- if(!FunctionDialogBase::read_configuration(tag, xml)) { //----------------------------------------- // Handle this dialog's specific settings. //----------------------------------------- if (tag == "range") _range = xml.parseInt(); else if (tag == "parts") _parts = xml.parseInt(); else if (tag == "len") len=xml.parseInt(); else xml.unknown("SetLen"); } break; case MusECore::Xml::TagEnd: if (tag == "setlen") return; default: break; } } } void Setlen::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "setlen"); //----------------------------------------- // Write any common base settings. //----------------------------------------- FunctionDialogBase::write_configuration(level, xml); //----------------------------------------- // Write this dialog's specific settings. //----------------------------------------- xml.intTag(level, "range", _range); xml.intTag(level, "parts", _parts); xml.intTag(level, "len", len); xml.tag(level, "/setlen"); } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/setlen.h000066400000000000000000000042371404105311000212110ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: setlen.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SETLEN_H__ #define __SETLEN_H__ #include "ui_setlenbase.h" #include "function_dialog_base.h" namespace MusECore { class Xml; } namespace MusEGui { class Setlen : public FunctionDialogBase, public Ui::SetlenBase { Q_OBJECT protected slots: void pull_values(); public: Setlen(QWidget* parent = 0); static int _range; static int _parts; static int len; static FunctionReturnDialogFlags_t _ret_flags; static FunctionDialogElements_t _elements; static void setElements(FunctionDialogElements_t elements = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton) { _elements = elements; } static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); void setupDialog(); int curRange() const { return _range; } void setCurRange(int range) { _range = range; } int curParts() const { return _parts; } void setCurParts(int parts) { _parts = parts; } void setReturnFlags(FunctionReturnDialogFlags_t f) { _ret_flags = f; } FunctionDialogElements_t elements() const { return _elements; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/setlenbase.ui000066400000000000000000000161201404105311000222240ustar00rootroot00000000000000 SetlenBase 0 0 275 424 MusE: Set Event Length 6 11 11 11 11 Range 6 11 11 11 11 All Events Selected Events true Looped Events Selected Looped 0 0 Parts 4 6 4 6 4 Selected parts or c&urrent part true All parts &in editor Value false false 11 11 11 11 6 New length false ticks 1 10000 1 Qt::Vertical 20 40 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel okButton clicked() SetlenBase accept() 20 20 20 20 cancelButton clicked() SetlenBase reject() 20 20 20 20 muse-4.0.0/src/muse/function_dialogs/transpose.cpp000066400000000000000000000111421404105311000222610ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: transpose.cpp,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "transpose.h" #include "xml.h" namespace MusEGui { FunctionReturnDialogFlags_t Transpose::_ret_flags = FunctionReturnNoFlags; FunctionDialogElements_t Transpose::_elements = FunctionDialogNoElements; int Transpose::_range = FunctionSelectedEventsButton; int Transpose::_parts = FunctionSelectedPartsButton; int Transpose::amount = 0; Transpose::Transpose(QWidget* parent) : FunctionDialogBase(parent) { setupUi(this); //-------------------------------------------- // Set range and parts containers if available. //-------------------------------------------- _range_container = rangeBox; _parts_container = partsBox; //-------------------------------------------- // Add element widgets to range and parts groups. //-------------------------------------------- _range_group->addButton(all_events_button, FunctionAllEventsButton); _range_group->addButton(selected_events_button,FunctionSelectedEventsButton); _range_group->addButton(looped_events_button, FunctionLoopedButton); _range_group->addButton(selected_looped_button, FunctionSelectedLoopedButton); _parts_group->addButton(not_all_parts_button, FunctionSelectedPartsButton); _parts_group->addButton(all_parts_button, FunctionAllPartsButton); } void Transpose::pull_values() { //-------------------------------------------- // Grab IDs or values from common base object // (range and parts groups etc.) //-------------------------------------------- FunctionDialogBase::pull_values(); //-------------------------------------------- // Grab this dialog's specific IDs or values. //-------------------------------------------- amount = amount_spinbox->value(); } void Transpose::setupDialog() { //------------------------------------ // Setup common base object items. //------------------------------------ FunctionDialogBase::setupDialog(); //------------------------------------ // Setup this dialog's specific items. //------------------------------------ amount_spinbox->setValue(amount); } void Transpose::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: //----------------------------------------- // Handle any common base settings. //----------------------------------------- if(!FunctionDialogBase::read_configuration(tag, xml)) { //----------------------------------------- // Handle this dialog's specific settings. //----------------------------------------- if (tag == "range") _range = xml.parseInt(); else if (tag == "parts") _parts = xml.parseInt(); else if (tag == "amount") amount=xml.parseInt(); else xml.unknown("Transpose"); } break; case MusECore::Xml::TagEnd: if (tag == "transpose") return; default: break; } } } void Transpose::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "transpose"); //----------------------------------------- // Write any common base settings. //----------------------------------------- FunctionDialogBase::write_configuration(level, xml); //----------------------------------------- // Write this dialog's specific settings. //----------------------------------------- xml.intTag(level, "range", _range); xml.intTag(level, "parts", _parts); xml.intTag(level, "amount", amount); xml.tag(level, "/transpose"); } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/transpose.h000066400000000000000000000042671404105311000217400ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: transpose.h,v 1.1.1.1 2011/05/05 18:51:04 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TRANSPOSE_H__ #define __TRANSPOSE_H__ #include "ui_transposebase.h" #include "function_dialog_base.h" namespace MusECore { class Xml; } namespace MusEGui { class Transpose : public FunctionDialogBase, public Ui::TransposeBase { Q_OBJECT protected slots: void pull_values(); public: Transpose(QWidget* parent = 0); static int _range; static int _parts; static int amount; static FunctionReturnDialogFlags_t _ret_flags; static FunctionDialogElements_t _elements; static void setElements(FunctionDialogElements_t elements = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton) { _elements = elements; } static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); void setupDialog(); int curRange() const { return _range; } void setCurRange(int range) { _range = range; } int curParts() const { return _parts; } void setCurParts(int parts) { _parts = parts; } void setReturnFlags(FunctionReturnDialogFlags_t f) { _ret_flags = f; } FunctionDialogElements_t elements() const { return _elements; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/transposebase.ui000066400000000000000000000163711404105311000227600ustar00rootroot00000000000000 TransposeBase 0 0 275 434 MusE: Transpose 6 11 11 11 11 Range 6 11 11 11 11 All Events Selected Events true Looped Events Selected Looped 0 0 Parts 4 6 4 6 4 Selected parts or c&urrent part true All parts &in editor Value false false 11 11 11 11 6 Halftone-steps false true -127 127 1 0 Qt::Vertical 20 40 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel okButton clicked() TransposeBase accept() 20 20 20 20 cancelButton clicked() TransposeBase reject() 20 20 20 20 muse-4.0.0/src/muse/function_dialogs/velocity.cpp000066400000000000000000000122721404105311000221060ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: velocity.cpp,v 1.1.1.1 2003/10/27 18:55:04 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "velocity.h" #include "xml.h" namespace MusEGui { FunctionReturnDialogFlags_t Velocity::_ret_flags = FunctionReturnNoFlags; FunctionDialogElements_t Velocity::_elements = FunctionDialogNoElements; int Velocity::_range = FunctionSelectedEventsButton; int Velocity::_parts = FunctionSelectedPartsButton; int Velocity::rateVal = 100; int Velocity::offsetVal = 0; //--------------------------------------------------------- // Velocity //--------------------------------------------------------- Velocity::Velocity(QWidget* parent) : FunctionDialogBase(parent) { setupUi(this); //-------------------------------------------- // Set range and parts containers if available. //-------------------------------------------- _range_container = rangeBox; _parts_container = partsBox; //-------------------------------------------- // Add element widgets to range and parts groups. //-------------------------------------------- _range_group->addButton(allEvents, FunctionAllEventsButton); _range_group->addButton(selectedEvents,FunctionSelectedEventsButton); _range_group->addButton(loopedEvents, FunctionLoopedButton); _range_group->addButton(selectedLooped, FunctionSelectedLoopedButton); _parts_group->addButton(not_all_parts_button, FunctionSelectedPartsButton); _parts_group->addButton(all_parts_button, FunctionAllPartsButton); } //--------------------------------------------------------- // pullValues //--------------------------------------------------------- void Velocity::pull_values() { //-------------------------------------------- // Grab IDs or values from common base object // (range and parts groups etc.) //-------------------------------------------- FunctionDialogBase::pull_values(); //-------------------------------------------- // Grab this dialog's specific IDs or values. //-------------------------------------------- rateVal = rate->value(); offsetVal = offset->value(); } void Velocity::setupDialog() { //------------------------------------ // Setup common base object items. //------------------------------------ FunctionDialogBase::setupDialog(); //------------------------------------ // Setup this dialog's specific items. //------------------------------------ rate->setValue(rateVal); offset->setValue(offsetVal); } void Velocity::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: //----------------------------------------- // Handle any common base settings. //----------------------------------------- if(!FunctionDialogBase::read_configuration(tag, xml)) { //----------------------------------------- // Handle this dialog's specific settings. //----------------------------------------- if (tag == "range") _range = xml.parseInt(); else if (tag == "parts") _parts = xml.parseInt(); else if (tag == "rate") rateVal=xml.parseInt(); else if (tag == "offset") offsetVal=xml.parseInt(); else xml.unknown("ModVelo"); } break; case MusECore::Xml::TagEnd: if (tag == "mod_velo") return; default: break; } } } void Velocity::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "mod_velo"); //----------------------------------------- // Write any common base settings. //----------------------------------------- FunctionDialogBase::write_configuration(level, xml); //----------------------------------------- // Write this dialog's specific settings. //----------------------------------------- xml.intTag(level, "range", _range); xml.intTag(level, "parts", _parts); xml.intTag(level, "offset", offsetVal); xml.intTag(level, "rate", rateVal); xml.tag(level, "/mod_velo"); } } // namespace MusEGui muse-4.0.0/src/muse/function_dialogs/velocity.h000066400000000000000000000046471404105311000215620ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: velocity.h,v 1.1.1.1 2003/10/27 18:54:51 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __VELOCITY_H__ #define __VELOCITY_H__ #include "ui_velocitybase.h" #include "function_dialog_base.h" namespace MusECore { class Xml; } namespace MusEGui { //--------------------------------------------------------- // Velocity //--------------------------------------------------------- class Velocity : public FunctionDialogBase, public Ui::VelocityBase { Q_OBJECT protected slots: void pull_values(); public: Velocity(QWidget* parent = 0); static int _range; static int _parts; static int rateVal; static int offsetVal; static FunctionReturnDialogFlags_t _ret_flags; static FunctionDialogElements_t _elements; static void setElements(FunctionDialogElements_t elements = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton) { _elements = elements; } static void read_configuration(MusECore::Xml& xml); void write_configuration(int level, MusECore::Xml& xml); void setupDialog(); int curRange() const { return _range; } void setCurRange(int range) { _range = range; } int curParts() const { return _parts; } void setCurParts(int parts) { _parts = parts; } void setReturnFlags(FunctionReturnDialogFlags_t f) { _ret_flags = f; } FunctionDialogElements_t elements() const { return _elements; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/function_dialogs/velocitybase.ui000066400000000000000000000174001404105311000225720ustar00rootroot00000000000000 VelocityBase 0 0 275 488 MusE: Modify Velocity 6 11 11 11 11 Range 6 11 11 11 11 All Events Selected Events true Looped Events Selected Looped 0 0 Parts 4 6 4 6 4 Selected parts or c&urrent part true All parts &in editor Values 11 11 11 11 6 Rate: false % 200 100 Offset: false -127 127 1 0 veloNew = (veloOld * rate) + offset Qt::Vertical 20 40 6 Qt::Horizontal QSizePolicy::Expanding 20 20 OK false true Cancel okButton clicked() VelocityBase accept() 20 20 20 20 cancelButton clicked() VelocityBase reject() 20 20 20 20 muse-4.0.0/src/muse/functions.cpp000066400000000000000000003416231404105311000167360ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: functions.cpp,v 1.20.2.19 2011/05/05 20:10 flo93 Exp $ // (C) Copyright 2011,2013 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "functions.h" #include "song.h" #include "helper.h" #include "event.h" #include "audio.h" #include "gconfig.h" #include "sig.h" #include "function_dialogs/velocity.h" #include "function_dialogs/quantize.h" #include "function_dialogs/crescendo.h" #include "function_dialogs/gatetime.h" #include "function_dialogs/remove.h" #include "function_dialogs/transpose.h" #include "function_dialogs/setlen.h" #include "function_dialogs/move.h" #include "function_dialogs/deloverlaps.h" #include "function_dialogs/legato.h" #include "components/pasteeventsdialog.h" #include #include #include #include #include #include #ifdef _WIN32 #include "mman.h" #include "mman.c" #else #include #endif #include "muse_math.h" #include #include #include #include #include #include // Forwards from header: #include #include "track.h" #include "part.h" using namespace std; using MusEGlobal::config; namespace MusEGui { FunctionDialogReturnErase erase_items_dialog(const FunctionDialogMode& mode) { erase_dialog->setElements(mode._buttons); if(!erase_dialog->exec()) return FunctionDialogReturnErase(); const int flags = erase_dialog->_ret_flags; return FunctionDialogReturnErase(flags & FunctionReturnAllEvents, flags & FunctionReturnAllParts, flags & FunctionReturnLooped, MusEGlobal::song->lPos(), MusEGlobal::song->rPos(), erase_dialog->velo_thres_used, erase_dialog->velo_threshold, erase_dialog->len_thres_used, erase_dialog->len_threshold); } FunctionDialogReturnCrescendo crescendo_items_dialog(const FunctionDialogMode& mode) { if (MusEGlobal::song->rPos() <= MusEGlobal::song->lPos()) { QMessageBox::warning(NULL, QObject::tr("Error"), QObject::tr("Please first select the range for crescendo with the loop markers.")); return FunctionDialogReturnCrescendo(); } crescendo_dialog->setElements(mode._buttons); if(!crescendo_dialog->exec()) return FunctionDialogReturnCrescendo(); const int flags = crescendo_dialog->_ret_flags; return FunctionDialogReturnCrescendo(flags & FunctionReturnAllEvents, flags & FunctionReturnAllParts, flags & FunctionReturnLooped, MusEGlobal::song->lPos(), MusEGlobal::song->rPos(), crescendo_dialog->start_val, crescendo_dialog->end_val, crescendo_dialog->absolute); } FunctionDialogReturnDelOverlaps deloverlaps_items_dialog(const FunctionDialogMode& mode) { del_overlaps_dialog->setElements(mode._buttons); if(!del_overlaps_dialog->exec()) return FunctionDialogReturnDelOverlaps(); const int flags = del_overlaps_dialog->_ret_flags; return FunctionDialogReturnDelOverlaps(flags & FunctionReturnAllEvents, flags & FunctionReturnAllParts, flags & FunctionReturnLooped, MusEGlobal::song->lPos(), MusEGlobal::song->rPos()); } FunctionDialogReturnGateTime gatetime_items_dialog(const FunctionDialogMode& mode) { gatetime_dialog->setElements(mode._buttons); if(!gatetime_dialog->exec()) return FunctionDialogReturnGateTime(); const int flags = gatetime_dialog->_ret_flags; return FunctionDialogReturnGateTime(flags & FunctionReturnAllEvents, flags & FunctionReturnAllParts, flags & FunctionReturnLooped, MusEGlobal::song->lPos(), MusEGlobal::song->rPos(), gatetime_dialog->rateVal, gatetime_dialog->offsetVal); } FunctionDialogReturnLegato legato_items_dialog(const FunctionDialogMode& mode) { legato_dialog->setElements(mode._buttons); if(!legato_dialog->exec()) return FunctionDialogReturnLegato(); const int flags = legato_dialog->_ret_flags; return FunctionDialogReturnLegato(flags & FunctionReturnAllEvents, flags & FunctionReturnAllParts, flags & FunctionReturnLooped, MusEGlobal::song->lPos(), MusEGlobal::song->rPos(), legato_dialog->min_len, legato_dialog->allow_shortening); } FunctionDialogReturnMove move_items_dialog(const FunctionDialogMode& mode) { move_notes_dialog->setElements(mode._buttons); if(!move_notes_dialog->exec()) return FunctionDialogReturnMove(); const int flags = move_notes_dialog->_ret_flags; return FunctionDialogReturnMove(flags & FunctionReturnAllEvents, flags & FunctionReturnAllParts, flags & FunctionReturnLooped, MusEGlobal::song->lPos(), MusEGlobal::song->rPos(), move_notes_dialog->amount); } FunctionDialogReturnQuantize quantize_items_dialog(const FunctionDialogMode& mode) { quantize_dialog->setElements(mode._buttons); if(!quantize_dialog->exec()) return FunctionDialogReturnQuantize(); const int flags = quantize_dialog->_ret_flags; return FunctionDialogReturnQuantize(flags & FunctionReturnAllEvents, flags & FunctionReturnAllParts, flags & FunctionReturnLooped, MusEGlobal::song->lPos(), MusEGlobal::song->rPos(), quantize_dialog->strength, quantize_dialog->threshold, quantize_dialog->raster_index, quantize_dialog->swing, quantize_dialog->quant_len); } FunctionDialogReturnSetLen setlen_items_dialog(const FunctionDialogMode& mode) { set_notelen_dialog->setElements(mode._buttons); if(!set_notelen_dialog->exec()) return FunctionDialogReturnSetLen(); const int flags = set_notelen_dialog->_ret_flags; return FunctionDialogReturnSetLen(flags & FunctionReturnAllEvents, flags & FunctionReturnAllParts, flags & FunctionReturnLooped, MusEGlobal::song->lPos(), MusEGlobal::song->rPos(), set_notelen_dialog->len); } FunctionDialogReturnTranspose transpose_items_dialog(const FunctionDialogMode& mode) { transpose_dialog->setElements(mode._buttons); if(!transpose_dialog->exec()) return FunctionDialogReturnTranspose(); const int flags = transpose_dialog->_ret_flags; return FunctionDialogReturnTranspose(flags & FunctionReturnAllEvents, flags & FunctionReturnAllParts, flags & FunctionReturnLooped, MusEGlobal::song->lPos(), MusEGlobal::song->rPos(), transpose_dialog->amount); } FunctionDialogReturnVelocity velocity_items_dialog(const FunctionDialogMode& mode) { velocity_dialog->setElements(mode._buttons); if(!velocity_dialog->exec()) return FunctionDialogReturnVelocity(); const int flags = velocity_dialog->_ret_flags; return FunctionDialogReturnVelocity(flags & FunctionReturnAllEvents, flags & FunctionReturnAllParts, flags & FunctionReturnLooped, MusEGlobal::song->lPos(), MusEGlobal::song->rPos(), velocity_dialog->rateVal, velocity_dialog->offsetVal); } } // namespace MusEGui namespace MusECore { // unit private functions: bool read_eventlist_and_part(Xml& xml, EventList* el, int* part_id); // ----------------------- typedef map expand_map_t; typedef map > new_part_map_t; set partlist_to_set(PartList* pl) { set result; for (PartList::iterator it=pl->begin(); it!=pl->end(); it++) result.insert(it->second); return result; } set part_to_set(const Part* p) { set result; result.insert(p); return result; } set get_all_parts() { set result; TrackList* tracks=MusEGlobal::song->tracks(); for (TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++) { const PartList* parts=(*t_it)->cparts(); for (ciPart p_it=parts->begin(); p_it!=parts->end(); p_it++) result.insert(p_it->second); } return result; } set get_all_selected_parts() { set result; TrackList* tracks=MusEGlobal::song->tracks(); for (TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++) { const PartList* parts=(*t_it)->cparts(); for (ciPart p_it=parts->begin(); p_it!=parts->end(); p_it++) if (p_it->second->selected()) result.insert(p_it->second); } return result; } bool is_relevant(const Event& event, const Part* part, int range, RelevantSelectedEvents_t relevant) { unsigned tick; switch(event.type()) { case Note: if(!(relevant & NotesRelevant)) return false; break; case Controller: if(!(relevant & ControllersRelevant)) return false; break; case Sysex: if(!(relevant & SysexRelevant)) return false; break; case Meta: if(!(relevant & MetaRelevant)) return false; break; case Wave: if(!(relevant & WaveRelevant)) return false; break; } switch (range) { case 0: return true; case 1: return event.selected(); case 2: tick=event.tick()+part->tick(); return (tick >= MusEGlobal::song->lpos()) && (tick < MusEGlobal::song->rpos()); case 3: return is_relevant(event,part,1, relevant) && is_relevant(event,part,2, relevant); default: cout << "ERROR: ILLEGAL FUNCTION CALL in is_relevant: range is illegal: "< get_events(const set& parts, int range, RelevantSelectedEvents_t relevant) { map events; for (set::iterator part=parts.begin(); part!=parts.end(); part++) for (ciEvent event=(*part)->events().begin(); event!=(*part)->events().end(); event++) if (is_relevant(event->second, *part, range, relevant)) events.insert(pair(&event->second, *part)); return events; } bool modify_velocity(const set& parts, int range, int rate, int offset) { map events = get_events(parts, range); Undo operations; if ( (!events.empty()) && ((rate!=100) || (offset!=0)) ) { for (map::iterator it=events.begin(); it!=events.end(); it++) { const Event& event=*(it->first); // This operation can only apply to notes. if(event.type() != Note) continue; const Part* part=it->second; int velo = event.velo(); velo = (velo * rate) / 100; velo += offset; if (velo <= 0) velo = 1; else if (velo > 127) velo = 127; if (event.velo() != velo) { Event newEvent = event.clone(); newEvent.setVelo(velo); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); } } return MusEGlobal::song->applyOperationGroup(operations); } else return false; } bool modify_off_velocity(const set& parts, int range, int rate, int offset) { map events = get_events(parts, range); Undo operations; if ( (!events.empty()) && ((rate!=100) || (offset!=0)) ) { for (map::iterator it=events.begin(); it!=events.end(); it++) { const Event& event=*(it->first); // This operation can only apply to notes. if(event.type() != Note) continue; const Part* part=it->second; int velo = event.veloOff(); velo = (velo * rate) / 100; velo += offset; if (velo <= 0) velo = 1; else if (velo > 127) velo = 127; if (event.veloOff() != velo) { Event newEvent = event.clone(); newEvent.setVeloOff(velo); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); } } return MusEGlobal::song->applyOperationGroup(operations); } else return false; } bool modify_notelen(const set& parts, int range, int rate, int offset) { map events = get_events(parts, range); Undo operations; map partlen; if ( (!events.empty()) && ((rate!=100) || (offset!=0)) ) { for (map::iterator it=events.begin(); it!=events.end(); it++) { const Event& event=*(it->first); // This operation can only apply to notes. if(event.type() != Note) continue; const Part* part=it->second; unsigned int len = event.lenTick(); //prevent compiler warning: comparison signed/unsigned len = (len * rate) / 100; len += offset; if (len <= 0) len = 1; if ((event.tick()+len > part->lenTick()) && (!(part->hasHiddenEvents() & Part::RightEventsHidden))) partlen[part]=event.tick()+len; // schedule auto-expanding if (event.lenTick() != len) { Event newEvent = event.clone(); newEvent.setLenTick(len); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); } } for (map::iterator it=partlen.begin(); it!=partlen.end(); it++) schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); return MusEGlobal::song->applyOperationGroup(operations); } else return false; } bool set_notelen(const set& parts, int range, int len) { return modify_notelen(parts, range, 0, len); } unsigned quantize_tick(unsigned tick, unsigned raster, int swing) { //find out the nearest tick and the distance to it: //this is so complicated because this function supports //swing: if swing is 50, the resulting rhythm is not //"daa daa daa daa" but "daaaa da daaaa da"... int tick_dest1 = MusEGlobal::sigmap.raster1(tick, raster*2); //round down int tick_dest2 = tick_dest1 + raster + raster*swing/100; int tick_dest3 = tick_dest1 + raster*2; int tick_diff1 = abs(tick_dest1 - (int)tick); int tick_diff2 = abs(tick_dest2 - (int)tick); int tick_diff3 = abs(tick_dest3 - (int)tick); if ((tick_diff3 <= tick_diff1) && (tick_diff3 <= tick_diff2)) //tick_dest3 is the nearest tick return tick_dest3; else if ((tick_diff2 <= tick_diff1) && (tick_diff2 <= tick_diff3)) //tick_dest2 is the nearest tick return tick_dest2; else return tick_dest1; } bool quantize_notes(const set& parts, int range, int raster, bool quant_len, int strength, int swing, int threshold) { map events = get_events(parts, range); Undo operations; if (!events.empty()) { for (map::iterator it=events.begin(); it!=events.end(); it++) { const Event& event=*(it->first); // This operation can only apply to notes. if(event.type() != Note) continue; const Part* part=it->second; unsigned begin_tick = event.tick() + part->tick(); int begin_diff = quantize_tick(begin_tick, raster, swing) - begin_tick; if (abs(begin_diff) > threshold) begin_tick = begin_tick + begin_diff*strength/100; unsigned len=event.lenTick(); unsigned end_tick = begin_tick + len; int len_diff = quantize_tick(end_tick, raster, swing) - end_tick; if ((abs(len_diff) > threshold) && quant_len) len = len + len_diff*strength/100; if (len <= 0) len = 1; if ( (event.lenTick() != len) || (event.tick() + part->tick() != begin_tick) ) { Event newEvent = event.clone(); newEvent.setTick(begin_tick - part->tick()); newEvent.setLenTick(len); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); } } return MusEGlobal::song->applyOperationGroup(operations); } else return false; } bool erase_notes(const set& parts, int range, int velo_threshold, bool velo_thres_used, int len_threshold, bool len_thres_used) { map events = get_events(parts, range); Undo operations; if (!events.empty()) { for (map::iterator it=events.begin(); it!=events.end(); it++) { // This operation can apply to any event... const Event& event=*(it->first); const Part* part=it->second; if ( (!velo_thres_used && !len_thres_used) || (velo_thres_used && event.velo() < velo_threshold) || (len_thres_used && int(event.lenTick()) < len_threshold) ) operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false)); } return MusEGlobal::song->applyOperationGroup(operations); } else return false; } bool transpose_notes(const set& parts, int range, signed int halftonesteps) { map events = get_events(parts, range); Undo operations; if ( (!events.empty()) && (halftonesteps!=0) ) { for (map::iterator it=events.begin(); it!=events.end(); it++) { const Event& event=*(it->first); // This operation can only apply to notes. if(event.type() != Note) continue; const Part* part=it->second; Event newEvent = event.clone(); int pitch = event.pitch()+halftonesteps; if (pitch > 127) pitch=127; if (pitch < 0) pitch=0; newEvent.setPitch(pitch); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); } return MusEGlobal::song->applyOperationGroup(operations); } else return false; } bool crescendo(const set& parts, int range, int start_val, int end_val, bool absolute) { map events = get_events(parts, range); Undo operations; int from=MusEGlobal::song->lpos(); int to=MusEGlobal::song->rpos(); if ( (!events.empty()) && (to>from) ) { for (map::iterator it=events.begin(); it!=events.end(); it++) { const Event& event=*(it->first); // This operation can only apply to notes. if(event.type() != Note) continue; const Part* part=it->second; unsigned tick = event.tick() + part->tick(); float curr_val= (float)start_val + (float)(end_val-start_val) * (tick-from) / (to-from); Event newEvent = event.clone(); int velo = event.velo(); if (absolute) velo=curr_val; else velo=curr_val*velo/100; if (velo > 127) velo=127; if (velo <= 0) velo=1; newEvent.setVelo(velo); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); } return MusEGlobal::song->applyOperationGroup(operations); } else return false; } bool move_notes(const set& parts, int range, signed int ticks) { map events = get_events(parts, range); Undo operations; map partlen; if ( (!events.empty()) && (ticks!=0) ) { for (map::iterator it=events.begin(); it!=events.end(); it++) { const Event& event=*(it->first); // This operation can only apply to notes. if(event.type() != Note) continue; const Part* part=it->second; bool del=false; Event newEvent = event.clone(); if ((signed)event.tick()+ticks < 0) //don't allow moving before the part's begin newEvent.setTick(0); else newEvent.setTick(event.tick()+ticks); if (newEvent.endTick() > part->lenTick()) //if exceeding the part's end: { if (part->hasHiddenEvents() & Part::RightEventsHidden) // auto-expanding is forbidden, clip { if (part->lenTick() > newEvent.tick()) newEvent.setLenTick(part->lenTick() - newEvent.tick()); else del=true; //if the new length would be <= 0, erase the note } else partlen[part]=newEvent.endTick(); // schedule auto-expanding } if (del==false) operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false)); else operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false)); } for (map::iterator it=partlen.begin(); it!=partlen.end(); it++) schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); return MusEGlobal::song->applyOperationGroup(operations); } else return false; } bool delete_overlaps(const set& parts, int range) { map events = get_events(parts, range); Undo operations; set deleted_events; if (!events.empty()) { for (map::iterator it1=events.begin(); it1!=events.end(); it1++) { const Event& event1=*(it1->first); // This operation can only apply to notes. if(event1.type() != Note) continue; const Part* part1=it1->second; // we may NOT optimize by letting it2 start at (it1 +1); this optimisation // is only allowed when events was sorted by time. it is, however, sorted // randomly by pointer. for (map::iterator it2=events.begin(); it2!=events.end(); it2++) { const Event& event2=*(it2->first); // This operation can only apply to notes. if(event2.type() != Note) continue; const Part* part2=it2->second; if ( (part1->isCloneOf(part2)) && // part1 and part2 are the same or are duplicates (&event1 != &event2) && // and event1 and event2 aren't the same (deleted_events.find(&event2) == deleted_events.end()) ) //and event2 hasn't been deleted before { if ( (event1.pitch() == event2.pitch()) && (event1.tick() <= event2.tick()) && (event1.endTick() > event2.tick()) ) //they overlap { int new_len = event2.tick() - event1.tick(); if (new_len==0) { operations.push_back(UndoOp(UndoOp::DeleteEvent, event2, part2, false, false)); deleted_events.insert(&event2); } else { Event new_event1 = event1.clone(); new_event1.setLenTick(new_len); operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event1, event1, part1, false, false)); } } } } } return MusEGlobal::song->applyOperationGroup(operations); } else return false; } bool legato(const set& parts, int range, int min_len, bool dont_shorten) { map events = get_events(parts, range); Undo operations; if (min_len<=0) min_len=1; if (!events.empty()) { for (map::iterator it1=events.begin(); it1!=events.end(); it1++) { const Event& event1=*(it1->first); // This operation can only apply to notes. if(event1.type() != Note) continue; const Part* part1=it1->second; unsigned len=INT_MAX; // we may NOT optimize by letting it2 start at (it1 +1); this optimisation // is only allowed when events was sorted by time. it is, however, sorted // randomly by pointer. for (map::iterator it2=events.begin(); it2!=events.end(); it2++) { const Event& event2=*(it2->first); // This operation can only apply to notes. if(event2.type() != Note) continue; const Part* part2=it2->second; bool relevant = (event2.tick() >= event1.tick() + min_len); if (dont_shorten) relevant = relevant && (event2.tick() >= event1.endTick()); if ( (part1->isCloneOf(part2)) && // part1 and part2 are the same or are duplicates relevant && // they're not too near (respect min_len and dont_shorten) (event2.tick()-event1.tick() < len ) ) // that's the nearest relevant following note len=event2.tick()-event1.tick(); } if (len==INT_MAX) len=event1.lenTick(); // if no following note was found, keep the length if (event1.lenTick() != len) { Event new_event1 = event1.clone(); new_event1.setLenTick(len); operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event1, event1, part1, false, false)); } } return MusEGlobal::song->applyOperationGroup(operations); } else return false; } // if nothing is selected/relevant, this function returns NULL QMimeData* selected_events_to_mime(const set& parts, int range) { unsigned start_tick = INT_MAX; //will be the tick of the first event or INT_MAX if no events are there for (set::iterator part=parts.begin(); part!=parts.end(); part++) for (ciEvent ev=(*part)->events().begin(); ev!=(*part)->events().end(); ev++) if (is_relevant(ev->second, *part, range, AllEventsRelevant)) if (ev->second.tick() < start_tick) start_tick=ev->second.tick(); if (start_tick == INT_MAX) return NULL; //--------------------------------------------------- // write events as XML into tmp file //--------------------------------------------------- FILE* tmp = tmpfile(); if (tmp == 0) { fprintf(stderr, "EventCanvas::getTextDrag() fopen failed: %s\n", strerror(errno)); return 0; } Xml xml(tmp); int level = 0; for (set::iterator part=parts.begin(); part!=parts.end(); part++) { xml.tag(level++, "eventlist part_id=\"%d\"", (*part)->sn()); for (ciEvent ev=(*part)->events().begin(); ev!=(*part)->events().end(); ev++) if (is_relevant(ev->second, *part, range, AllEventsRelevant)) ev->second.write(level, xml, -start_tick); xml.etag(--level, "eventlist"); } QMimeData *mimeData = file_to_mimedata(tmp, "text/x-muse-groupedeventlists" ); fclose(tmp); return mimeData; } void copy_notes(const set& parts, int range) { QMimeData* drag = selected_events_to_mime(parts,range); if (drag) QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard); } unsigned get_groupedevents_len(const QString& pt) { unsigned maxlen=0; QByteArray pt_= pt.toLatin1(); Xml xml(pt_.constData()); for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return maxlen; case Xml::TagStart: if (tag == "eventlist") { EventList el; int part_id; if (read_eventlist_and_part(xml, &el, &part_id)) { unsigned len = el.rbegin()->first; if (len > maxlen) maxlen=len; } } else xml.unknown("get_clipboard_len"); break; case Xml::Attribut: case Xml::TagEnd: default: break; } } return maxlen; // see also the return statement above! } unsigned get_clipboard_len() { QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :( QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); return get_groupedevents_len(s); } bool paste_notes(const Part* paste_into_part) { unsigned temp_begin = MusEGlobal::sigmap.raster1(MusEGlobal::song->cpos(),0); unsigned temp_end = MusEGlobal::sigmap.raster2(temp_begin + get_clipboard_len(), 0); MusEGui::paste_events_dialog->raster = temp_end - temp_begin; MusEGui::paste_events_dialog->into_single_part_allowed = (paste_into_part!=NULL); if (!MusEGui::paste_events_dialog->exec()) return false; paste_notes(MusEGui::paste_events_dialog->max_distance, MusEGui::paste_events_dialog->always_new_part, MusEGui::paste_events_dialog->never_new_part, MusEGui::paste_events_dialog->into_single_part ? paste_into_part : NULL, MusEGui::paste_events_dialog->number, MusEGui::paste_events_dialog->raster); return true; } void paste_notes(int max_distance, bool always_new_part, bool never_new_part, const Part* paste_into_part, int amount, int raster) { QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :( QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); paste_at(s, MusEGlobal::song->cpos(), max_distance, always_new_part, never_new_part, paste_into_part, amount, raster); } // if nothing is selected/relevant, this function returns NULL QMimeData* parts_to_mime(const set& parts) { //--------------------------------------------------- // write events as XML into tmp file //--------------------------------------------------- FILE* tmp = tmpfile(); if (tmp == 0) { fprintf(stderr, "EventCanvas::getTextDrag() fopen failed: %s\n", strerror(errno)); return 0; } Xml xml(tmp); int level = 0; bool midi=false; bool wave=false; for (set::iterator part=parts.begin(); part!=parts.end(); part++) { if ((*part)->track()->type() == MusECore::Track::MIDI) midi=true; else wave=true; (*part)->write(level, xml, true, true); } QString mimeString = "text/x-muse-mixedpartlist"; if (!midi) mimeString = "text/x-muse-wavepartlist"; else if (!wave) mimeString = "text/x-muse-midipartlist"; QMimeData *mimeData = file_to_mimedata(tmp, mimeString ); fclose(tmp); return mimeData; } //--------------------------------------------------- // read datafile into mime Object //--------------------------------------------------- QMimeData* file_to_mimedata(FILE *datafile, QString mimeType) { fflush(datafile); struct stat f_stat; if (fstat(fileno(datafile), &f_stat) == -1) { fprintf(stderr, "copy_notes() fstat failed:<%s>\n", strerror(errno)); fclose(datafile); return 0; } int n = f_stat.st_size; char* fbuf = (char*)mmap(0, n+1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(datafile), 0); fbuf[n] = 0; QByteArray data(fbuf); QMimeData* md = new QMimeData(); md->setData(mimeType, data); munmap(fbuf, n); return md; } bool read_eventlist_and_part(Xml& xml, EventList* el, int* part_id) // true on success, false on failure { *part_id = -1; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return false; case Xml::Attribut: if (tag == "part_id") *part_id = xml.s2().toInt(); else printf("unknown attribute '%s' in read_eventlist_and_part(), ignoring it...\n", tag.toLatin1().data()); break; case Xml::TagStart: if (tag == "event") { Event e(Note); e.read(xml); el->add(e); } else xml.unknown("read_eventlist_and_part"); break; case Xml::TagEnd: if (tag == "eventlist") return true; default: break; } } } void paste_at(const QString& pt, int pos, int max_distance, bool always_new_part, bool never_new_part, const Part* paste_into_part, int amount, int raster) { Undo operations; map expand_map; map > new_part_map; QByteArray pt_= pt.toLatin1(); Xml xml(pt_.constData()); for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto out_of_paste_at_for; case Xml::TagStart: if (tag == "eventlist") { EventList el; int part_id; if (read_eventlist_and_part(xml, &el, &part_id)) { const Part* dest_part; Track* dest_track; const Part* old_dest_part; if (paste_into_part == NULL) dest_part = partFromSerialNumber(part_id); else dest_part=paste_into_part; if (dest_part == NULL) { printf("ERROR: destination part wasn't found. ignoring these events\n"); } else { dest_track=dest_part->track(); old_dest_part=dest_part; unsigned first_paste_tick = el.begin()->first + pos; bool create_new_part = ( (dest_part->tick() > first_paste_tick) || // dest_part begins too late ( ( (dest_part->endTick() + max_distance < first_paste_tick) || // dest_part is too far away always_new_part ) && !never_new_part ) ); // respect function arguments for (int i=0;ifirst + curr_pos; if (create_new_part) { dest_part = NULL; Part* newpart = dest_track->newPart(); if(newpart) { newpart->setTick(MusEGlobal::sigmap.raster1(first_paste_tick, config.division)); dest_part = newpart; new_part_map[old_dest_part].insert(dest_part); operations.push_back(UndoOp(UndoOp::AddPart, dest_part)); } } if(dest_part) { for (iEvent i = el.begin(); i != el.end(); ++i) { // If the destination part is a midi part, any midi event is allowed. // If the destination part is a wave part, any wave event is allowed. switch(i->second.type()) { case Note: case Controller: case Sysex: case Meta: if(dest_part->type() == Pos::FRAMES) continue; break; case Wave: // FIXME TODO: To support pasting wave events, some code below must be made agnostic. // For now just ignore wave events. //if(dest_part->type() == Pos::TICKS) continue; break; } // FIXME TODO: To support pasting wave events, this code block and some code below it MUST be made position-agnostic. Event e = i->second.clone(); int tick = e.tick() + curr_pos - dest_part->tick(); if (tick<0) { printf("ERROR: trying to add event before current part! ignoring this event\n"); continue; } e.setTick(tick); e.setSelected(true); // No need to select clones, AddEvent operation below will take care of that. if (e.endTick() > dest_part->lenTick()) // event exceeds part? { if (dest_part->hasHiddenEvents() & Part::RightEventsHidden) // auto-expanding is forbidden? { if (e.tick() < dest_part->lenTick()) e.setLenTick(dest_part->lenTick() - e.tick()); // clip else e.setLenTick(0); // don't insert that note at all } else { if (e.endTick() > expand_map[dest_part]) expand_map[dest_part]=e.endTick(); } } switch(e.type()) { case Note: // Don't add Note event types if they have no length. // Notes are allowed to overlap. There is no DeleteEvent or ModifyEvent first. if(e.lenTick() != 0) operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false)); break; case Wave: // Don't add Wave event types if they have no length. if(e.lenFrame() != 0) { EventList el; // Compare time, and wave position, path, and start position. dest_part->events().findSimilarType(e, el, true, false, false, false, true, true, true); // Do NOT add the new wave event if it already exists at the position. // Don't event bother replacing it using DeletEvent or ModifyEvent. if(el.empty()) { operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false)); } else { // Delete all but one of them. There shouldn't be more than one wave event // at a time for a given wave event anyway. iEvent nie; for(iEvent ie = el.begin(); ie != el.end(); ++ie) { // Break on the second-last one, to leave one item intact. nie = ie; ++nie; if(nie == el.end()) { break; } operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false)); } } } break; case Controller: { EventList el; // Compare time and controller number (data A) only. dest_part->events().findSimilarType(e, el, true, true); // Delete them all. There shouldn't be more than one controller event // at a time for a given controller number anyway. for(iEvent ie = el.begin(); ie != el.end(); ++ie) { operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, true, true)); } // Do port controller values and clone parts. operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, true, true)); } break; case Sysex: { EventList el; // Compare time and sysex data only. dest_part->events().findSimilarType(e, el, true); // Do NOT add the new sysex if it already exists at the position. // Don't even bother replacing it using DeletEvent or ModifyEvent. if(el.empty()) { operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false)); } else { // Delete all but one of them. There shouldn't be more than one sysex event // at a time for a given sysex anyway. iEvent nie; for(iEvent ie = el.begin(); ie != el.end(); ++ie) { // Break on the second-last one, to leave one item intact. nie = ie; ++nie; if(nie == el.end()) { break; } operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false)); } } } break; case Meta: { EventList el; // Compare time and meta data only. dest_part->events().findSimilarType(e, el, true); // Do NOT add the new meta if it already exists at the position. // Don't even bother replacing it using DeletEvent or ModifyEvent. if(el.empty()) { operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false)); } else { // Delete all but one of them. There shouldn't be more than one meta event // at a time for a given meta anyway. iEvent nie; for(iEvent ie = el.begin(); ie != el.end(); ++ie) { // Break on the second-last one, to leave one item intact. nie = ie; ++nie; if(nie == el.end()) { break; } operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false)); } } } break; } } } } } } else { printf("ERROR: reading eventlist from clipboard failed. ignoring this one...\n"); } } else xml.unknown("paste_at"); break; case Xml::Attribut: case Xml::TagEnd: default: break; } } out_of_paste_at_for: for (map::iterator it = expand_map.begin(); it!=expand_map.end(); it++) if (it->second != it->first->lenTick()) schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); MusEGlobal::song->informAboutNewParts(new_part_map); // must be called before apply. otherwise // pointer changes (by resize) screw it up MusEGlobal::song->applyOperationGroup(operations); MusEGlobal::song->update(SC_SELECTION | SC_PART_SELECTION); } void select_all(const set& parts) { Undo operations; operations.combobreaker=true; for (set::iterator part=parts.begin(); part!=parts.end(); part++) for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++) { const Event& event=ev_it->second; operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, true, event.selected())); } MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate); } void select_none(const set& parts) { Undo operations; operations.combobreaker=true; for (set::iterator part=parts.begin(); part!=parts.end(); part++) for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++) { const Event& event=ev_it->second; operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, false, event.selected())); } MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate); } void select_invert(const set& parts) { Undo operations; operations.combobreaker=true; for (set::iterator part=parts.begin(); part!=parts.end(); part++) for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++) { const Event& event=ev_it->second; operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, !event.selected(), event.selected())); } MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate); } void select_in_loop(const set& parts) { select_none(parts); Undo operations; operations.combobreaker=true; for (set::iterator part=parts.begin(); part!=parts.end(); part++) for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++) { const Event& event=ev_it->second; operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, (event.tick()>=MusEGlobal::song->lpos() && event.endTick()<=MusEGlobal::song->rpos()), event.selected())); } MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate); } void select_not_in_loop(const set& parts) { select_none(parts); Undo operations; operations.combobreaker=true; for (set::iterator part=parts.begin(); part!=parts.end(); part++) for (ciEvent ev_it=(*part)->events().begin(); ev_it!=(*part)->events().end(); ev_it++) { const Event& event=ev_it->second; operations.push_back(UndoOp(UndoOp::SelectEvent,event, *part, !(event.tick()>=MusEGlobal::song->lpos() && event.endTick()<=MusEGlobal::song->rpos()), event.selected())); } MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate); } bool tracks_are_selected() { const TrackList* tl = MusEGlobal::song->tracks(); for(ciTrack it = tl->begin(); it != tl->end(); ++it) if((*it)->selected()) return true; return false; } bool parts_are_selected() { const TrackList* tl = MusEGlobal::song->tracks(); for(ciTrack it = tl->begin(); it != tl->end(); ++it) { const PartList* pl = (*it)->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) if(ip->second->selected()) return true; } return false; } void shrink_parts(int raster) { Undo operations; unsigned min_len; if (raster<0) raster=MusEGlobal::config.division; if (raster>=0) min_len=raster; else min_len=MusEGlobal::config.division; TrackList* tracks = MusEGlobal::song->tracks(); for (iTrack track = tracks->begin(); track != tracks->end(); track++) for (iPart part = (*track)->parts()->begin(); part != (*track)->parts()->end(); part++) if (part->second->selected()) { unsigned len=0; for (ciEvent ev=part->second->events().begin(); ev!=part->second->events().end(); ev++) if (ev->second.endTick() > len) len=ev->second.endTick(); if (raster) len=ceil((float)len/raster)*raster; if (lensecond->lenTick()) operations.push_back(UndoOp(UndoOp::ModifyPartLength, part->second, part->second->lenValue(), len, 0, Pos::TICKS)); } MusEGlobal::song->applyOperationGroup(operations); } void schedule_resize_all_same_len_clone_parts(const Part* part, unsigned new_len, Undo& operations) { QSet already_done; for (Undo::iterator op_it=operations.begin(); op_it!=operations.end();op_it++) if (op_it->type==UndoOp::DeletePart) already_done.insert(op_it->part); unsigned old_len = part->lenValue(); if (old_len!=new_len) { const Part* part_it=part; do { if (part_it->lenValue()==old_len && !already_done.contains(part_it)) operations.push_back(UndoOp(UndoOp::ModifyPartLength, part_it, old_len, new_len, 0, part->type())); part_it=part_it->nextClone(); } while (part_it!=part); } } void expand_parts(int raster) { Undo operations; unsigned min_len; if (raster<0) raster=MusEGlobal::config.division; if (raster>=0) min_len=raster; else min_len=MusEGlobal::config.division; TrackList* tracks = MusEGlobal::song->tracks(); for (iTrack track = tracks->begin(); track != tracks->end(); track++) for (iPart part = (*track)->parts()->begin(); part != (*track)->parts()->end(); part++) if (part->second->selected()) { unsigned len=part->second->lenTick(); for (ciEvent ev=part->second->events().begin(); ev!=part->second->events().end(); ev++) if (ev->second.endTick() > len) len=ev->second.endTick(); if (raster) len=ceil((float)len/raster)*raster; if (len part->second->lenTick()) operations.push_back(UndoOp(UndoOp::ModifyPartLength, part->second, part->second->lenValue(), len, 0, Pos::TICKS)); } MusEGlobal::song->applyOperationGroup(operations); } void clean_parts() { Undo operations; set already_processed; TrackList* tracks = MusEGlobal::song->tracks(); for (iTrack track = tracks->begin(); track != tracks->end(); track++) for (iPart part = (*track)->parts()->begin(); part != (*track)->parts()->end(); part++) if ((part->second->selected()) && (already_processed.find(part->second)==already_processed.end())) { // find out the length of the longest clone of this part; // avoid processing eventlist multiple times (because of // multiple clones) unsigned len=0; const Part* part_it=part->second; do { if (part_it->lenTick() > len) len=part_it->lenTick(); already_processed.insert(part_it); part_it=part_it->nextClone(); } while ((part_it!=part->second) && (part_it!=NULL)); // erase all events exceeding the longest clone of this part // (i.e., erase all hidden events) or shorten them for (ciEvent ev=part->second->events().begin(); ev!=part->second->events().end(); ev++) if (ev->second.tick() >= len) operations.push_back(UndoOp(UndoOp::DeleteEvent, ev->second, part->second, true, true)); else if (ev->second.endTick() > len) { Event new_event = ev->second.clone(); new_event.setLenTick(len - ev->second.tick()); operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event, ev->second, part->second, true, true)); } } MusEGlobal::song->applyOperationGroup(operations); } bool merge_with_next_part(const Part* oPart) { const Track* track = oPart->track(); if(track->type() != Track::WAVE && !track->isMidiTrack()) return false; const PartList* pl = track->cparts(); const Part* nextPart = 0; for (ciPart ip = pl->begin(); ip != pl->end(); ++ip) { if (ip->second == oPart) { ++ip; if (ip == pl->end()) return false; nextPart = ip->second; break; } } if (nextPart) { set parts; parts.insert(oPart); parts.insert(nextPart); return merge_parts(parts); } else return false; } bool merge_selected_parts() { set temp = get_all_selected_parts(); return merge_parts(temp); } bool merge_parts(const set& parts) { set tracks; for (set::iterator it=parts.begin(); it!=parts.end(); it++) tracks.insert( (*it)->track() ); Undo operations; // process tracks separately for (set::iterator t_it=tracks.begin(); t_it!=tracks.end(); t_it++) { const Track* track=*t_it; unsigned begin=INT_MAX, end=0; const Part* first_part=NULL; // find begin of the first and end of the last part for (set::iterator it=parts.begin(); it!=parts.end(); it++) if ((*it)->track()==track) { const Part* p=*it; if (p->tick() < begin) { begin=p->tick(); first_part=p; } if (p->endTick() > end) end=p->endTick(); } if (begin==INT_MAX || end==0) { printf("THIS SHOULD NEVER HAPPEN: begin==INT_MAX || end==0 in merge_parts()\n"); continue; // skip the actual work, as we cannot work under errornous conditions. } // create and prepare the new part Part* new_part = first_part->duplicateEmpty(); new_part->setTick(begin); new_part->setLenTick(end-begin); // copy all events from the source parts into the new part for (set::iterator p_it=parts.begin(); p_it!=parts.end(); p_it++) if ((*p_it)->track()==track) { const EventList& old_el= (*p_it)->events(); for (ciEvent ev_it=old_el.begin(); ev_it!=old_el.end(); ev_it++) { Event new_event=ev_it->second.clone(); new_event.setTick( new_event.tick() + (*p_it)->tick() - new_part->tick() ); new_part->addEvent(new_event); } } // delete all the source parts for (set::iterator it=parts.begin(); it!=parts.end(); it++) if ((*it)->track()==track) operations.push_back( UndoOp(UndoOp::DeletePart, *it) ); // and add the new one operations.push_back( UndoOp(UndoOp::AddPart, new_part) ); } return MusEGlobal::song->applyOperationGroup(operations); } bool split_part(const Part* part, int tick) { int l1 = tick - part->tick(); int l2 = part->lenTick() - l1; if (l1 <= 0 || l2 <= 0) return false; Part* p1; Part* p2; part->splitPart(tick, p1, p2); MusEGlobal::song->informAboutNewParts(part, p1); MusEGlobal::song->informAboutNewParts(part, p2); Undo operations; operations.push_back(UndoOp(UndoOp::DeletePart, part)); operations.push_back(UndoOp(UndoOp::AddPart, p1)); operations.push_back(UndoOp(UndoOp::AddPart, p2)); return MusEGlobal::song->applyOperationGroup(operations); } bool delete_selected_parts() { Undo operations; bool partSelected = false; TrackList* tl = MusEGlobal::song->tracks(); for (iTrack it = tl->begin(); it != tl->end(); ++it) { PartList* pl = (*it)->parts(); for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { if (ip->second->selected()) { operations.push_back(UndoOp(UndoOp::DeletePart,ip->second)); partSelected = true; } } } MusEGlobal::song->applyOperationGroup(operations); return partSelected; } //============================================================================= // BEGIN item-based functions //============================================================================= // For erasing existing target controller events before pasting source controller events. typedef std::pair PasteEraseMapInsertPair_t; typedef std::map PasteEraseMap_t; typedef PasteEraseMap_t::iterator iPasteEraseMap; typedef PasteEraseMap_t::const_iterator ciPasteEraseMap; typedef std::pair PasteEraseCtlMapPair_t; typedef std::map PasteEraseCtlMap_t; typedef PasteEraseCtlMap_t::iterator iPasteEraseCtlMap; typedef PasteEraseCtlMap_t::const_iterator ciPasteEraseCtlMap; class PasteEraseCtlMap : public PasteEraseCtlMap_t { private: bool _erase_controllers_wysiwyg; bool _erase_controllers_inclusive; public: PasteEraseCtlMap(bool erase_controllers_wysiwyg, bool erase_controllers_inclusive) : _erase_controllers_wysiwyg(erase_controllers_wysiwyg), _erase_controllers_inclusive(erase_controllers_inclusive) { } // Add an item. All ctl_time must be sorted beforehand. // Be sure to call tidy() after all items have been added. void add(int ctl_num, unsigned int ctl_time, unsigned int len_val); // Tidy up the very last items in the list. void tidy(); }; void PasteEraseCtlMap::add(int ctl_num, unsigned int ctl_time, unsigned int len_val) { unsigned long ctl_end_time; if(len_val > 0) ctl_end_time = ctl_time + len_val; else ctl_end_time = ctl_time + 1; iPasteEraseCtlMap icm = find(ctl_num); if(icm == end()) { PasteEraseMap_t new_tmap; new_tmap.insert(PasteEraseMapInsertPair_t(ctl_time, ctl_end_time)); insert(PasteEraseCtlMapPair_t(ctl_num, new_tmap)); } else { PasteEraseMap_t& tmap = icm->second; // The event times must be sorted already. So this would always return end(). //iPasteEraseMap itm = tmap.upper_bound(ctl_time); iPasteEraseMap itm = tmap.end(); if(itm != tmap.begin()) { --itm; iPasteEraseMap prev_itm_2 = tmap.end(); if(itm != tmap.begin()) { prev_itm_2 = itm; --prev_itm_2; } if((itm->second >= ctl_time) || _erase_controllers_inclusive) { if(_erase_controllers_inclusive) itm->second = ctl_time; if(prev_itm_2 != tmap.end()) { if((prev_itm_2->second >= itm->first) || _erase_controllers_inclusive) { prev_itm_2->second = itm->second; tmap.erase(itm); } } tmap.insert(PasteEraseMapInsertPair_t(ctl_time, ctl_end_time)); } else { // If we want wysiwyg pasting, we erase existing events up to // the end-time of the last tmap item which ended a contiguous // 'cluster' of items. Otherwise we ONLY erase UP TO AND INCLUDING // the start-time of that last tmap item. So we 'truncate' that // last item in the cluster by setting the end-time to the start-time, // so that the gathering routine below knows to erase that last // single-time position. if(!_erase_controllers_wysiwyg) itm->second = itm->first + 1; if(prev_itm_2 != tmap.end()) { if(prev_itm_2->second >= itm->first) { prev_itm_2->second = itm->second; tmap.erase(itm); } } tmap.insert(PasteEraseMapInsertPair_t(ctl_time, ctl_end_time)); } } } } void PasteEraseCtlMap::tidy() { // Tidy up the very last items in the list. for(iPasteEraseCtlMap icm = begin(); icm != end(); ++icm) { PasteEraseMap_t& tmap = icm->second; iPasteEraseMap itm = tmap.end(); if(itm != tmap.begin()) { --itm; if(!_erase_controllers_wysiwyg) itm->second = itm->first + 1; if(itm != tmap.begin()) { iPasteEraseMap itm_2 = itm; --itm_2; if((itm_2->second >= itm->second) || _erase_controllers_inclusive) { itm_2->second = itm->second; tmap.erase(itm); } } } } } bool erase_items(TagEventList* tag_list, int velo_threshold, bool velo_thres_used, int len_threshold, bool len_thres_used) { Undo operations; const Part* part; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { // This operation can apply to any event. const Event& e = ie->second; // FIXME TODO Likely need agnostic Pos or frames rather than ticks if WaveCanvas is to use this. if ( e.type() != Note || (!velo_thres_used && !len_thres_used) || (velo_thres_used && e.velo() < velo_threshold) || (len_thres_used && int(e.lenTick()) < len_threshold) ) { operations.push_back(UndoOp(UndoOp::DeleteEvent, e, part, true, true)); } } } return MusEGlobal::song->applyOperationGroup(operations); } bool crescendo_items(TagEventList* tag_list, int start_val, int end_val, bool absolute) { const Pos& from = MusEGlobal::song->lPos(); const Pos& to = MusEGlobal::song->rPos(); if(to <= from) return false; Undo operations; Pos pos; float curr_val; unsigned int pos_val = (to - from).posValue(); const Part* part; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { const Event& e = ie->second; // This operation can only apply to notes. if(e.type() != Note) continue; pos = e.pos() + *part; curr_val = (float)start_val + (float)(end_val - start_val) * (pos - from).posValue() / pos_val; Event newEvent = e.clone(); int velo = e.velo(); if (absolute) velo=curr_val; else velo=curr_val*velo/100; if (velo > 127) velo=127; if (velo <= 0) velo=1; newEvent.setVelo(velo); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false)); } } return MusEGlobal::song->applyOperationGroup(operations); } bool delete_overlaps_items(TagEventList* tag_list) { Undo operations; set deleted_events; int new_len; Event new_event1; const Part* part; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { const Event& e = ie->second; // This operation can only apply to notes. if(e.type() != Note) continue; // Has this event already been scheduled for deletion? Ignore it. if(deleted_events.find(&e) != deleted_events.end()) continue; ciEvent ie2 = ie; ++ie2; for( ; ie2 != el.end(); ++ie2) { const Event& e2 = ie2->second; // This operation can only apply to notes. if(e2.type() != Note) continue; // Do e2 and e point to the same event? Or has e2 already been scheduled for deletion? Ignore it. if(e == e2 || deleted_events.find(&e2) != deleted_events.end()) continue; if ( (e.pitch() == e2.pitch()) && (e.tick() <= e2.tick()) && (e.endTick() > e2.tick()) ) //they overlap { new_len = e2.tick() - e.tick(); if(new_len==0) { operations.push_back(UndoOp(UndoOp::DeleteEvent, e2, part, false, false)); deleted_events.insert(&e2); } else { new_event1 = e.clone(); new_event1.setLenTick(new_len); operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event1, e, part, false, false)); // After resizing the event, it should not be necessary to continue with any further // events in this loop since any more sorted events will come at or AFTER e2's position // which we have just resized the end of e to. break; } } } } } return MusEGlobal::song->applyOperationGroup(operations); } bool modify_notelen_items(TagEventList* tag_list, int rate, int offset) { if(rate == 100 && offset == 0) return false; Undo operations; unsigned int len; map partlen; Event newEvent; const Part* part; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { const Event& e = ie->second; // This operation can only apply to notes. if(e.type() != Note) continue; len = e.lenTick(); //prevent compiler warning: comparison signed/unsigned len = (len * rate) / 100; len += offset; if (len <= 0) len = 1; if ((e.tick()+len > part->lenTick()) && (!(part->hasHiddenEvents() & Part::RightEventsHidden))) partlen[part] = e.tick() + len; // schedule auto-expanding if (e.lenTick() != len) { newEvent = e.clone(); newEvent.setLenTick(len); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false)); } } for (map::iterator it=partlen.begin(); it!=partlen.end(); it++) schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); } return MusEGlobal::song->applyOperationGroup(operations); } bool legato_items(TagEventList* tag_list, int min_len, bool dont_shorten) { Undo operations; if (min_len<=0) min_len=1; unsigned len = INT_MAX; bool relevant; Event new_event1; const Part* part; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { const Event& e = ie->second; // This operation can only apply to notes. if(e.type() != Note) continue; ciEvent ie2 = ie; ++ie2; for( ; ie2 != el.end(); ++ie2) { const Event& e2 = ie2->second; // This operation can only apply to notes. if(e2.type() != Note) continue; relevant = (e2.tick() >= e.tick() + min_len); if (dont_shorten) relevant = relevant && (e2.tick() >= e.endTick()); if ( relevant && // they're not too near (respect min_len and dont_shorten) (e2.tick() - e.tick() < len ) ) // that's the nearest relevant following note len = e2.tick() - e.tick(); } if (len==INT_MAX) len = e.lenTick(); // if no following note was found, keep the length if (e.lenTick() != len) { new_event1 = e.clone(); new_event1.setLenTick(len); operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event1, e, part, false, false)); } } } return MusEGlobal::song->applyOperationGroup(operations); } bool move_items(TagEventList* tag_list, signed int ticks) { if(ticks == 0) return false; Undo operations; map partlen; bool del; Event newEvent; const Part* part; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { const Event& e = ie->second; del = false; // This operation can only apply to notes. if(e.type() != Note) continue; newEvent = e.clone(); if ((signed)e.tick() + ticks < 0) //don't allow moving before the part's begin newEvent.setTick(0); else newEvent.setTick(e.tick() + ticks); if (newEvent.endTick() > part->lenTick()) //if exceeding the part's end: { if (part->hasHiddenEvents() & Part::RightEventsHidden) // auto-expanding is forbidden, clip { if (part->lenTick() > newEvent.tick()) newEvent.setLenTick(part->lenTick() - newEvent.tick()); else del = true; //if the new length would be <= 0, erase the note } else partlen[part] = newEvent.endTick(); // schedule auto-expanding } if (del == false) operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false)); else operations.push_back(UndoOp(UndoOp::DeleteEvent, e, part, false, false)); } for (map::iterator it=partlen.begin(); it!=partlen.end(); it++) schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); } return MusEGlobal::song->applyOperationGroup(operations); } bool quantize_items(TagEventList* tag_list, int raster_idx, bool quant_len, int strength, int swing, int threshold) { const int rv = MusEGui::functionQuantizeRasterVals[raster_idx]; if(rv <= 0) return false; const int raster = (MusEGlobal::config.division*4) / rv; Undo operations; unsigned begin_tick; int begin_diff; unsigned len; unsigned end_tick; int len_diff; Event newEvent; const Part* part; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { const Event& e = ie->second; // This operation can only apply to notes. if(e.type() != Note) continue; begin_tick = e.tick() + part->tick(); begin_diff = quantize_tick(begin_tick, raster, swing) - begin_tick; if (abs(begin_diff) > threshold) begin_tick = begin_tick + begin_diff*strength/100; len = e.lenTick(); end_tick = begin_tick + len; len_diff = quantize_tick(end_tick, raster, swing) - end_tick; if ((abs(len_diff) > threshold) && quant_len) len = len + len_diff*strength/100; if (len <= 0) len = 1; if ( (e.lenTick() != len) || (e.tick() + part->tick() != begin_tick) ) { newEvent = e.clone(); newEvent.setTick(begin_tick - part->tick()); newEvent.setLenTick(len); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false)); } } } return MusEGlobal::song->applyOperationGroup(operations); } bool set_notelen_items(TagEventList* tag_list, int len) { return modify_notelen_items(tag_list, 0, len); } bool transpose_items(TagEventList* tag_list, signed int halftonesteps) { if(halftonesteps == 0) return false; Undo operations; Event newEvent; int pitch; const Part* part; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { const Event& e = ie->second; // This operation can only apply to notes. if(e.type() != Note) continue; newEvent = e.clone(); pitch = e.pitch() + halftonesteps; if (pitch > 127) pitch = 127; if (pitch < 0) pitch = 0; newEvent.setPitch(pitch); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false)); } } return MusEGlobal::song->applyOperationGroup(operations); } bool modify_velocity_items(TagEventList* tag_list, int rate, int offset) { if(rate == 100 && offset == 0) return false; Undo operations; int velo; Event newEvent; const Part* part; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { const Event& e = ie->second; // This operation can only apply to notes. if(e.type() != Note) continue; velo = e.velo(); velo = (velo * rate) / 100; velo += offset; if (velo <= 0) velo = 1; else if (velo > 127) velo = 127; if (e.velo() != velo) { newEvent = e.clone(); newEvent.setVelo(velo); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false)); } } } return MusEGlobal::song->applyOperationGroup(operations); } bool modify_off_velocity_items(TagEventList* tag_list, int rate, int offset) { if(rate == 100 && offset == 0) return false; Undo operations; int velo; Event newEvent; const Part* part; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { const Event& e = ie->second; // This operation can only apply to notes. if(e.type() != Note) continue; velo = e.veloOff(); velo = (velo * rate) / 100; velo += offset; if (velo <= 0) velo = 1; else if (velo > 127) velo = 127; if (e.veloOff() != velo) { newEvent = e.clone(); newEvent.setVeloOff(velo); operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, e, part, false, false)); } } } return MusEGlobal::song->applyOperationGroup(operations); } void copy_items(TagEventList* tag_list) { QMimeData* drag = cut_or_copy_tagged_items_to_mime(tag_list); if (drag) QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard); } bool cut_items(TagEventList* tag_list) { QMimeData* drag = cut_or_copy_tagged_items_to_mime(tag_list, true); if(drag) { QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard); return true; } return false; } // if nothing is selected/relevant, this function returns NULL QMimeData* cut_or_copy_tagged_items_to_mime(TagEventList* tag_list, bool cut_mode) { if(tag_list->empty()) return NULL; QTemporaryFile tmp; if(!tmp.open()) { fprintf(stderr, "cut_or_copy_tagged_items_to_mime(): ERROR: Failed to open temporary file\n"); return NULL; } const Pos start_pos = tag_list->globalStats().evrange(); Undo operations; bool changed = false; const Part* part; //--------------------------------------------------- // write events as XML into tmp file //--------------------------------------------------- Xml xml(&tmp); int level = 0; for(ciTagEventList itl = tag_list->begin(); itl != tag_list->end(); ++itl) { part = itl->first; const EventList& el = itl->second.evlist(); if(el.empty()) continue; xml.tag(level++, "eventlist part_id=\"%d\"", part->sn()); for(ciEvent ie = el.begin(); ie != el.end(); ie++) { const Event& e = ie->second; Event ne = e.clone(); ne.setPos(ne.pos() - start_pos); ne.write(level, xml, Pos(0, e.pos().type() == Pos::TICKS)); if(cut_mode) { changed = true; operations.push_back(UndoOp(UndoOp::DeleteEvent, e, part, true, true)); } } xml.etag(--level, "eventlist"); } tmp.flush(); tmp.seek(0); const QByteArray data = tmp.readAll(); QMimeData* mimeData = new QMimeData(); mimeData->setData("text/x-muse-groupedeventlists", data); if(changed) MusEGlobal::song->applyOperationGroup(operations); return mimeData; } bool paste_items(const std::set& parts, const Part* paste_into_part) { unsigned temp_begin = MusEGlobal::sigmap.raster1(MusEGlobal::song->cpos(),0); unsigned temp_end = MusEGlobal::sigmap.raster2(temp_begin + get_clipboard_len(), 0); MusEGui::paste_events_dialog->raster = temp_end - temp_begin; MusEGui::paste_events_dialog->into_single_part_allowed = (paste_into_part!=NULL); if (!MusEGui::paste_events_dialog->exec()) return false; paste_items(parts, MusEGui::paste_events_dialog->max_distance, FunctionOptionsStruct( (MusEGui::paste_events_dialog->ctrl_erase ? FunctionEraseItems : FunctionNoOptions) | (MusEGui::paste_events_dialog->ctrl_erase_wysiwyg ? FunctionEraseItemsWysiwyg : FunctionNoOptions) | (MusEGui::paste_events_dialog->ctrl_erase_inclusive ? FunctionEraseItemsInclusive : FunctionNoOptions) | (MusEGui::paste_events_dialog->always_new_part ? FunctionPasteAlwaysNewPart : FunctionNoOptions) | (MusEGui::paste_events_dialog->never_new_part ? FunctionPasteNeverNewPart : FunctionNoOptions)), MusEGui::paste_events_dialog->into_single_part ? paste_into_part : NULL, MusEGui::paste_events_dialog->number, MusEGui::paste_events_dialog->raster, AllEventsRelevant, -1 /*paste to ctrl num*/ ); return true; } void paste_items(const set& parts, int max_distance, const FunctionOptionsStruct& options, const Part* paste_into_part, int amount, int raster, RelevantSelectedEvents_t relevant, int paste_to_ctrl_num ) { QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :( QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); paste_items_at(parts, s, MusEGlobal::song->cPos(), max_distance, options, paste_into_part, amount, raster, relevant, paste_to_ctrl_num ); } void pasteEventList( const EventList& el, const Pos& pos, Part* dest_part, Undo& operations, Undo& add_operations, expand_map_t& expand_map, new_part_map_t& new_part_map, // The source part where the event list came from, in case // the erase_source argument is true. const Part* source_part = nullptr, // Whether to erase ('cut') the source events after pasting. bool erase_source = false, const Pos& start_pos = Pos(), int max_distance=3072, // Options. Default is erase target existing controllers first + erase wysiwyg. const FunctionOptionsStruct& options = FunctionOptionsStruct(), // Number of copies to paste. int amount=1, // Separation between copies. int raster=3072, // Choose which events to paste. const RelevantSelectedEvents_t relevant = AllEventsRelevant, // If pasting controllers, paste into this controller number if not -1. // If the source has multiple controllers, user will be asked which one to paste. int paste_to_ctrl_num = -1 ) { const bool wave_mode = dest_part->partType() == Part::WavePartType; int num_events; PosLen el_range = el.evrange(wave_mode, relevant, &num_events, paste_to_ctrl_num); if(num_events <= 0) return; const bool always_new_part = options._flags & FunctionPasteAlwaysNewPart; const bool never_new_part = options._flags & FunctionPasteNeverNewPart; const bool erase_controllers = options._flags & FunctionEraseItems; const bool erase_controllers_wysiwyg = options._flags & FunctionEraseItemsWysiwyg; const bool erase_controllers_inclusive = options._flags & FunctionEraseItemsInclusive; const Pos::TType time_type = wave_mode ? Pos::FRAMES : Pos::TICKS; Track* dest_track = NULL; const Part* old_dest_part = NULL; // Be sure to subtract the position of the very first event of interest. // This is exactly what the copy/cut functions do before they write the results // to an output file. But here the events in the directly-passed source list // cannot be time-modified beforehand. So here we subtract this start position: el_range -= start_pos; const unsigned pos_value = pos.posValue(time_type); unsigned dest_part_pos_value = dest_part->posValue(time_type); unsigned dest_part_end_value = dest_part->end().posValue(time_type); dest_track=dest_part->track(); old_dest_part=dest_part; unsigned first_paste_pos_value = el_range.posValue() + pos_value; bool create_new_part = ( (first_paste_pos_value < dest_part_pos_value) || // dest_part begins too late ( ( (dest_part_end_value + max_distance < first_paste_pos_value) || // dest_part is too far away always_new_part ) && !never_new_part ) ); // respect function arguments for (int i=0;inewPart(); if(newpart) { // TODO: Shouldn't we snap to frames for wave parts? But snap to what exactly? const unsigned pos_tick = Pos(first_paste_pos_value, !wave_mode).tick(); const unsigned rast_pos_tick = MusEGlobal::sigmap.raster1(pos_tick, config.division); newpart->setTick(rast_pos_tick); const unsigned len_rast_off_value = pos_tick >= rast_pos_tick ? pos_tick - rast_pos_tick : 0; newpart->setLenValue(el_range.lenValue() + len_rast_off_value, time_type); dest_part = newpart; dest_part_pos_value = dest_part->posValue(time_type); dest_part_end_value = dest_part->end().posValue(time_type); new_part_map[old_dest_part].insert(dest_part); add_operations.push_back(UndoOp(UndoOp::AddPart, dest_part)); } } if(!dest_part) continue; // This will be filled as we go. PasteEraseCtlMap ctl_map(erase_controllers_wysiwyg, erase_controllers_inclusive); const unsigned dest_part_len_value = dest_part->lenValue(time_type); for (ciEvent i = el.cbegin(); i != el.cend(); ++i) { const Event& old_e = i->second; // If the destination part is a midi part, any midi event is allowed. // If the destination part is a wave part, any wave event is allowed. switch(old_e.type()) { case Note: if(!(relevant & NotesRelevant) || dest_part->type() == Pos::FRAMES) continue; break; case Controller: if(!(relevant & ControllersRelevant) || dest_part->type() == Pos::FRAMES || (paste_to_ctrl_num >= 0 && paste_to_ctrl_num != old_e.dataA())) continue; break; case Sysex: if(!(relevant & SysexRelevant) || dest_part->type() == Pos::FRAMES) continue; break; case Meta: if(!(relevant & MetaRelevant) || dest_part->type() == Pos::FRAMES) continue; break; case Wave: if(!(relevant & WaveRelevant) || dest_part->type() == Pos::TICKS) continue; break; } Event e = old_e.clone(); unsigned tick = e.posValue(time_type) + curr_pos; // Be sure to subtract the position of the very first event of interest. const unsigned sp_val = start_pos.posValue(time_type); if(tick >= sp_val) tick -= sp_val; else { printf("WARNING: paste_items_at(): Should not happen: event pos value: %u less than start pos value: %u\n", tick, sp_val); tick = 0; } if (tick < dest_part_pos_value) //if (tick.posValue() < 0) { printf("ERROR: paste_items_at(): trying to add event before current part! ignoring this event\n"); continue; } tick -= dest_part_pos_value; e.setPosValue(tick, time_type); e.setSelected(true); // No need to select clones, AddEvent operation below will take care of that. // Don't bother with expansion if these are new parts. if (!create_new_part && e.endPosValue() > dest_part_len_value) // event exceeds part? { if (dest_part->hasHiddenEvents() & Part::RightEventsHidden) // auto-expanding is forbidden? { if (e.posValue(time_type) < dest_part_len_value) e.setLenValue(dest_part_len_value - e.posValue(time_type), time_type); // clip else e.setLenValue(0, time_type); // don't insert that note at all } else { if (e.endPosValue() > expand_map[dest_part]) expand_map[dest_part]=e.endPosValue(); } } switch(e.type()) { case Note: // Don't add Note event types if they have no length. // Notes are allowed to overlap. There is no DeleteEvent or ModifyEvent first. //if(e.lenTick() != 0) //{ // If this is a fresh new part, to avoid double operation warnings when undoing // just add the event directly to the part instead of an operation. if(create_new_part) ((Part*)dest_part)->addEvent(e); else add_operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false)); //} break; case Wave: // Don't add Wave event types if they have no length. //if(e.lenFrame() != 0) //{ // If this is a fresh new part, to avoid double operation warnings when undoing // just add the event directly to the part instead of an operation. if(create_new_part) { ((Part*)dest_part)->addEvent(e); } else { EventList s_el; // Compare time, and wave position, path, and start position. dest_part->events().findSimilarType(e, s_el, true, false, false, false, true, true, true); // Do NOT add the new wave event if it already exists at the position. // Don't event bother replacing it using DeletEvent or ModifyEvent. if(s_el.empty()) { add_operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false)); } else { // Delete all but one of them. There shouldn't be more than one wave event // at a time for a given wave event anyway. ciEvent nie; for(ciEvent ie = s_el.cbegin(); ie != s_el.cend(); ++ie) { // Break on the second-last one, to leave one item intact. nie = ie; ++nie; if(nie == s_el.end()) { break; } // If we are 'cutting' the source events, and the source and destination parts // are the same, and the cut and erase events are the same, don't push the // deletes here. Let the cutting section at the end of the routine take do it. if(!erase_source || source_part != dest_part || el.findId(ie->second) == el.end()) operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false)); } } } //} break; case Controller: { // HACK Grab the event length since we use it for indicating // the visual width when tagging controller items. const unsigned int len_val = e.lenValue(); // Be sure to reset this always since we use it for the above hack. e.setLenValue(0); // If this is a fresh new part, to avoid double DeleteMidiCtrlVal warnings when undoing // just add the event directly to the part instead of an operation. if(create_new_part) { ((Part*)dest_part)->addEvent(e); } else { // If we are erasing existing controller values first, this block will // take care of all of the erasures. But even if we are NOT specifically // erasing first, we still MUST erase any existing controller values found // at that exact time value. So that is done in the next block. if(erase_controllers) { ctl_map.add(e.dataA(), e.posValue(), len_val); } else // Here we are not specifically erasing first. But we still MUST erase any // existing controller values found at that exact time value. { EventList s_el; // Compare time and controller number (data A) only. dest_part->events().findSimilarType(e, s_el, true, true); // Delete them all. There shouldn't be more than one controller event // at a time for a given controller number anyway. for(ciEvent ie = s_el.cbegin(); ie != s_el.cend(); ++ie) { // If we are 'cutting' the source events, and the source and destination parts // are the same, and the cut and erase events are the same, don't push the // deletes here. Let the cutting section at the end of the routine take do it. if(!erase_source || source_part != dest_part || el.findId(ie->second) == el.end()) // Do port controller values and clone parts. operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, true, true)); } } // Do port controller values and clone parts. add_operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, true, true)); } } break; case Sysex: { // If this is a fresh new part, to avoid double operation warnings when undoing // just add the event directly to the part instead of an operation. if(create_new_part) { ((Part*)dest_part)->addEvent(e); } else { EventList s_el; // Compare time and sysex data only. dest_part->events().findSimilarType(e, s_el, true); // Do NOT add the new sysex if it already exists at the position. // Don't event bother replacing it using DeletEvent or ModifyEvent. if(s_el.empty()) { add_operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false)); } else { // Delete all but one of them. There shouldn't be more than one sysex event // at a time for a given sysex anyway. ciEvent nie; for(ciEvent ie = s_el.cbegin(); ie != s_el.cend(); ++ie) { // Break on the second-last one, to leave one item intact. nie = ie; ++nie; if(nie == s_el.end()) { break; } // If we are 'cutting' the source events, and the source and destination parts // are the same, and the cut and erase events are the same, don't push the // deletes here. Let the cutting section at the end of the routine take do it. if(!erase_source || source_part != dest_part || el.findId(ie->second) == el.end()) operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false)); } } } } break; case Meta: { // If this is a fresh new part, to avoid double operation warnings when undoing // just add the event directly to the part instead of an operation. if(create_new_part) { ((Part*)dest_part)->addEvent(e); } else { EventList s_el; // Compare time and meta data only. dest_part->events().findSimilarType(e, s_el, true); // Do NOT add the new meta if it already exists at the position. // Don't event bother replacing it using DeletEvent or ModifyEvent. if(s_el.empty()) { add_operations.push_back(UndoOp(UndoOp::AddEvent, e, dest_part, false, false)); } else { // Delete all but one of them. There shouldn't be more than one meta event // at a time for a given meta anyway. ciEvent nie; for(ciEvent ie = s_el.cbegin(); ie != s_el.cend(); ++ie) { // Break on the second-last one, to leave one item intact. nie = ie; ++nie; if(nie == s_el.end()) { break; } // If we are 'cutting' the source events, and the source and destination parts // are the same, and the cut and erase events are the same, don't push the // deletes here. Let the cutting section at the end of the routine take do it. if(!erase_source || source_part != dest_part || el.findId(ie->second) == el.end()) operations.push_back(UndoOp(UndoOp::DeleteEvent, ie->second, dest_part, false, false)); } } } } break; } } // If this is not a fresh new part, gather the operations to // erase existing controller events in the destination part. if(erase_controllers && !create_new_part && dest_part && !ctl_map.empty()) { // Tidy up the very last items in the list. ctl_map.tidy(); unsigned e_pos; const EventList& er_el = dest_part->events(); for(ciEvent ie = er_el.cbegin(); ie != er_el.cend(); ++ie) { const Event& er_e = ie->second; if(er_e.type() != Controller) continue; ciPasteEraseCtlMap icm = ctl_map.find(er_e.dataA()); if(icm == ctl_map.end()) continue; const PasteEraseMap_t& tmap = icm->second; e_pos = er_e.posValue(); ciPasteEraseMap itm = tmap.upper_bound(e_pos); if(itm == tmap.begin()) continue; --itm; if(e_pos >= itm->first && e_pos < itm->second) { // If we are 'cutting' the source events, and the source and destination parts // are the same, and the cut and erase events are the same, don't push the // deletes here. Let the cutting section at the end of the routine take do it. if(!erase_source || source_part != dest_part || el.findId(er_e) == el.end()) operations.push_back(UndoOp(UndoOp::DeleteEvent, er_e, dest_part, true, true)); } } } } // Do we want to cut the items as well? if(erase_source && source_part) { for(ciEvent i = el.cbegin(); i != el.cend(); ++i) { const Event& old_e = i->second; operations.push_back(UndoOp(UndoOp::DeleteEvent, old_e, source_part, true, true)); } } } void paste_items_at(const std::set& parts, const QString& pt, const Pos& pos, int max_distance, const FunctionOptionsStruct& options, const Part* paste_into_part, int amount, int raster, RelevantSelectedEvents_t relevant, int paste_to_ctrl_num) { // To maximize speed and minimize memory use, the processing below // can only find any delete operations AFTER it has gathered // add operations. So we keep two separate operations lists and // combine them later so that all the deletes come BEFORE all the adds. Undo add_operations, operations; map expand_map; map > new_part_map; QByteArray pt_= pt.toLatin1(); Xml xml(pt_.constData()); for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto out_of_paste_at_for; case Xml::TagStart: if (tag == "eventlist") { EventList el; int part_id; if (!read_eventlist_and_part(xml, &el, &part_id)) { printf("ERROR: reading eventlist from clipboard failed. ignoring this one...\n"); break; } const Part* dest_part; if (paste_into_part == NULL) dest_part = partFromSerialNumber(part_id); else dest_part=paste_into_part; if (dest_part == NULL) { printf("ERROR: destination part wasn't found. ignoring these events\n"); break; } // Paste into the destination part ONLY if it is included in the given set of parts, // typically the parts used by an editor window instance's canvas. (WYSIWYG). // Override if paste_into_part is given, to allow 'Paste to current part' to work. if(!paste_into_part && parts.find(dest_part) == parts.end()) break; const bool wave_mode = dest_part->partType() == Part::WavePartType; FindMidiCtlsList_t ctrlList; el.findControllers(wave_mode, &ctrlList); int ctrlsFound = 0; if(!ctrlList.empty()) ctrlsFound = ctrlList.size(); if(paste_to_ctrl_num >= 0 && ctrlsFound > 0) { // TODO Dialog for choosing which controller to paste. } pasteEventList( el, pos, ((Part*)dest_part), operations, add_operations, expand_map, new_part_map, nullptr, false, Pos(), max_distance, options, amount, raster, relevant, paste_to_ctrl_num); } else xml.unknown("paste_items_at"); break; case Xml::Attribut: case Xml::TagEnd: default: break; } } out_of_paste_at_for: // Push any part resizing operations onto the operations list now, before merging // the add operations. for (map::iterator it = expand_map.begin(); it!=expand_map.end(); it++) if (it->second != it->first->lenValue()) schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); // Now merge the add operations into the operations so that all of the 'deletes' come first. //add_operations.splice(add_operations.begin(), delete_operations); for(ciUndoOp iuo = add_operations.begin(); iuo != add_operations.end(); ++iuo) operations.push_back(*iuo); MusEGlobal::song->informAboutNewParts(new_part_map); // must be called before apply. otherwise // pointer changes (by resize) screw it up MusEGlobal::song->applyOperationGroup(operations); MusEGlobal::song->update(SC_SELECTION | SC_PART_SELECTION); } void paste_items_at( const std::set& parts, const TagEventList* tag_list, const Pos& pos, int max_distance, const FunctionOptionsStruct& options, const Part* paste_into_part, int amount, int raster, RelevantSelectedEvents_t relevant, int paste_to_ctrl_num ) { const bool cut_mode = options._flags & FunctionCutItems; // To maximize speed and minimize memory use, the processing below // can only find any delete operations AFTER it has gathered // add operations. So we keep two separate operations lists and // combine them later so that all the deletes come BEFORE all the adds. Undo add_operations, operations; expand_map_t expand_map; new_part_map_t new_part_map; // Find the lowest position of all the events - the 'start' position. const Pos start_pos = tag_list->globalStats().evrange(relevant); // At this point the tag list's event list will still have any controller // visual lengths HACK applied. // Those lengths will be reset below. But for now we could use them... FindMidiCtlsList_t globalCtrlList; int globalCtrlsFound = 0; if(!globalCtrlList.empty()) globalCtrlsFound = globalCtrlList.size(); if(paste_to_ctrl_num >= 0) { tag_list->globalCtlStats(&globalCtrlList, paste_to_ctrl_num); if(!globalCtrlList.empty()) globalCtrlsFound = globalCtrlList.size(); if(globalCtrlsFound > 0) { // Prompt user to choose controller... } } for(ciTagEventList itl = tag_list->cbegin(); itl != tag_list->cend(); ++itl) { const Part* dest_part = NULL; const Part* src_part = itl->first; if (paste_into_part == NULL) // Paste to original source part. dest_part = src_part; else // Paste to specific part. dest_part=paste_into_part; if (dest_part == NULL) { printf("paste_items_at(): ERROR: destination part wasn't found. ignoring these events\n"); continue; } // Paste into the destination part ONLY if it is included in the given set of parts, // typically the parts used by an editor window instance's canvas. (WYSIWYG). // Override if paste_into_part is given, to allow 'Paste to current part' to work. if(!paste_into_part && parts.find(dest_part) == parts.end()) continue; // Grab the event list and find the number of relevant events. const EventList& el = itl->second.evlist(); pasteEventList( el, pos, ((Part*)dest_part), operations, add_operations, expand_map, new_part_map, src_part, cut_mode, start_pos, max_distance, options, amount, raster, relevant, paste_to_ctrl_num); } // Push any part resizing operations onto the operations list now, before merging // the add operations. for (map::iterator it = expand_map.begin(); it!=expand_map.end(); it++) if (it->second != it->first->lenValue()) schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); // Now merge the add operations into the operations so that all of the 'deletes' come first. //add_operations.splice(add_operations.begin(), delete_operations); for(ciUndoOp iuo = add_operations.begin(); iuo != add_operations.end(); ++iuo) operations.push_back(*iuo); MusEGlobal::song->informAboutNewParts(new_part_map); // must be called before apply. otherwise // pointer changes (by resize) screw it up MusEGlobal::song->applyOperationGroup(operations); MusEGlobal::song->update(SC_SELECTION | SC_PART_SELECTION); } //--------------------------------------------------------- // getSelectedParts //--------------------------------------------------------- PartList* getSelectedParts() { PartList* parts1; PartList* parts2; parts1 = getSelectedMidiParts(); parts2 = getSelectedWaveParts(); for (ciPart p = parts2->begin(); p != parts2->end(); ++p) { parts1->add(p->second); } return parts1; } PartList* getSelectedMidiParts() { PartList* parts = new PartList(); /* If a part is selected, edit that. If a track is selected, edit the first part of the track, the rest are 'ghost parts' When multiple parts are selected, then edit the first, the rest are 'ghost parts' */ // collect marked parts for (ciMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t) { PartList* pl = (*t)->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { if (p->second->selected()) { parts->add(p->second); } } } // if no part is selected, then search for selected track // and collect all parts of this track if (parts->empty()) { for (ciMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t) { if ((*t)->selected()) { PartList* pl = (*t)->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) parts->add(p->second); break; } } } return parts; } PartList* getSelectedWaveParts() { PartList* parts = new PartList(); /* If a part is selected, edit that. If a track is selected, edit the first part of the track, the rest are 'ghost parts' When multiple parts are selected, then edit the first, the rest are 'ghost parts' */ // collect selected parts for (ciWaveTrack t = MusEGlobal::song->waves()->begin(); t != MusEGlobal::song->waves()->end(); ++t) { PartList* pl = (*t)->parts(); for (ciPart p = pl->begin(); p != pl->end(); ++p) { if (p->second->selected()) { parts->add(p->second); } } } // if no parts are selected, then search the selected track // and collect all parts in this track if (parts->empty()) { for (ciWaveTrack t = MusEGlobal::song->waves()->begin(); t != MusEGlobal::song->waves()->end(); ++t) { if ((*t)->selected()) { PartList* pl = (*t)->parts(); for (ciPart p = pl->begin(); p != pl->end(); ++p) parts->add(p->second); break; } } } return parts; } //--------------------------------------------------------- // resize_part //--------------------------------------------------------- void resize_part( Track* track, Part* originalPart, unsigned int newTickPosOrLen, MusECore::ResizeDirection resizeDirection, bool doClones, bool dragEvents) { // Are the events to be offset? const bool use_events_offset = (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT && dragEvents) || (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT && !dragEvents); // Under this condition we MUST do all clones. The RULE is that event times, which are relative to the part start, // must be the same in all clones. if(use_events_offset) doClones = true; switch(track->type()) { case Track::WAVE: case Track::MIDI: case Track::DRUM: { Undo operations; #ifdef ALLOW_LEFT_HIDDEN_EVENTS const Pos::TType newPosOrLenType = Pos::TType::TICKS; const unsigned int origPosValue = originalPart->posValue(); const unsigned int newOrigPosValue = Pos::convert(newTickPosOrLen, newPosOrLenType, originalPart->type()); // The amount to shift a part. The int64_t cast ensures we preserve the unsigned range. const int64_t origPosValueDiff = (int64_t)newOrigPosValue - (int64_t)origPosValue; const unsigned int origPosValueConverted = originalPart->posValue(newPosOrLenType); const unsigned int newOrigEndPosValue = Pos::convert(origPosValueConverted + newTickPosOrLen, newPosOrLenType, originalPart->type()); const unsigned int newOrigLenValue = newOrigEndPosValue - origPosValue; const unsigned int origLenValue = originalPart->lenValue(); const int64_t origLenValueDiff = (int64_t)newOrigLenValue - (int64_t)origLenValue; int64_t events_offset = 0L; if(use_events_offset) { switch(resizeDirection) { case MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT: events_offset = origLenValueDiff; break; case MusECore::ResizeDirection::RESIZE_TO_THE_LEFT: events_offset = -origPosValueDiff; break; } } auto currentPart = originalPart; do { if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT) { const unsigned int pos_val = currentPart->posValue(originalPart->type()); const unsigned int new_end_pos_val = Pos::convert(pos_val + newOrigLenValue, originalPart->type(), currentPart->type()); const unsigned int new_len_val = new_end_pos_val - pos_val; operations.push_back( UndoOp(UndoOp::ModifyPartLength, currentPart, currentPart->lenValue(), new_len_val, // The amount to shift all events in the part. events_offset, // The position type of the amount to shift all events in the part. originalPart->type())); } else if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT) { const unsigned int pos_val = currentPart->posValue(originalPart->type()); const unsigned int end_pos_val = currentPart->endValue(originalPart->type()); unsigned int new_pos_val, new_len_val; if((int64_t)pos_val + origPosValueDiff < 0L) { new_pos_val = 0; new_len_val = Pos::convert((int64_t)end_pos_val - ((int64_t)pos_val + origPosValueDiff), originalPart->type(), currentPart->type()) - new_pos_val; } else { new_pos_val = Pos::convert((int64_t)pos_val + origPosValueDiff, originalPart->type(), currentPart->type()); new_len_val = currentPart->endValue() - new_pos_val; } operations.push_back( UndoOp(UndoOp::ModifyPartStart, currentPart, currentPart->posValue(), new_pos_val, currentPart->lenValue(), new_len_val, // The amount to shift all events in the part. events_offset, // The position type of the amount to shift all events in the part. originalPart->type())); } currentPart = currentPart->nextClone(); } while (doClones && (currentPart != originalPart)); #else const Pos::TType newPosOrLenType = Pos::TType::TICKS; const unsigned int origPosValue = originalPart->posValue(); const unsigned int newOrigPosValue = Pos::convert(newTickPosOrLen, newPosOrLenType, originalPart->type()); // The amount to shift a part. The int64_t cast ensures we preserve the unsigned range. const int64_t origPosValueDiff = (int64_t)newOrigPosValue - (int64_t)origPosValue; const Pos::TType events_offset_time_type = originalPart->type(); const unsigned int origPosValueConverted = originalPart->posValue(newPosOrLenType); const unsigned int newOrigEndPosValue = Pos::convert(origPosValueConverted + newTickPosOrLen, newPosOrLenType, originalPart->type()); const unsigned int newOrigLenValue = newOrigEndPosValue - origPosValue; const unsigned int origLenValue = originalPart->lenValue(); const int64_t origLenValueDiff = (int64_t)newOrigLenValue - (int64_t)origLenValue; int64_t events_offset = 0L; if(use_events_offset) { switch(resizeDirection) { case MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT: events_offset = origLenValueDiff; break; case MusECore::ResizeDirection::RESIZE_TO_THE_LEFT: events_offset = -origPosValueDiff; break; } } // Check to see if the events offset would move events before time zero, and limit if so. if(use_events_offset) { const EventList& el = originalPart->events(); const ciEvent iev = el.cbegin(); if(iev != el.cend()) { const Event first_ev = iev->second; // In case the event and part pos types differ, the event dominates. unsigned int new_part_pos_val; switch(resizeDirection) { // If resizing to the right, just use the original part position, converted. case MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT: new_part_pos_val = originalPart->posValue(first_ev.pos().type()); break; // If resizing to the left, use the new part position, converted. case MusECore::ResizeDirection::RESIZE_TO_THE_LEFT: new_part_pos_val = Pos::convert(newTickPosOrLen, newPosOrLenType, first_ev.pos().type()); break; } const unsigned int old_abs_ev_pos_val = Pos::convert(first_ev.posValue() + new_part_pos_val, first_ev.pos().type(), events_offset_time_type); if((int64_t)old_abs_ev_pos_val + events_offset < 0L) events_offset = -(int64_t)old_abs_ev_pos_val; const unsigned int new_abs_ev_pos_val = Pos::convert((int64_t)old_abs_ev_pos_val + events_offset, events_offset_time_type, first_ev.pos().type()); if(new_abs_ev_pos_val < new_part_pos_val) events_offset = -(int64_t)first_ev.pos().posValue(); } } auto currentPart = originalPart; do { if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_RIGHT) { const unsigned int pos_val = currentPart->posValue(originalPart->type()); const unsigned int new_end_pos_val = Pos::convert(pos_val + newOrigLenValue, originalPart->type(), currentPart->type()); const unsigned int new_len_val = new_end_pos_val - pos_val; operations.push_back( UndoOp(UndoOp::ModifyPartLength, currentPart, currentPart->lenValue(), new_len_val, // The amount to shift all events in the part. events_offset, // The position type of the amount to shift all events in the part. originalPart->type())); } else if(resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT) { const unsigned int pos_val = currentPart->posValue(originalPart->type()); const unsigned int end_pos_val = currentPart->endValue(originalPart->type()); unsigned int new_pos_val, new_len_val; if((int64_t)pos_val + origPosValueDiff < 0L) { new_pos_val = 0; new_len_val = Pos::convert((int64_t)end_pos_val - ((int64_t)pos_val + origPosValueDiff), originalPart->type(), currentPart->type()) - new_pos_val; } else { new_pos_val = Pos::convert((int64_t)pos_val + origPosValueDiff, originalPart->type(), currentPart->type()); new_len_val = currentPart->endValue() - new_pos_val; } operations.push_back( UndoOp(UndoOp::ModifyPartStart, currentPart, currentPart->posValue(), new_pos_val, currentPart->lenValue(), new_len_val, // The amount to shift all events in the part. events_offset, // The position type of the amount to shift all events in the part. originalPart->type())); } currentPart = currentPart->nextClone(); } while (doClones && (currentPart != originalPart)); #endif MusEGlobal::song->applyOperationGroup(operations); break; } default: break; } } } // namespace MusECore muse-4.0.0/src/muse/functions.h000066400000000000000000000440211404105311000163730ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: functions.h,v 1.20.2.19 2011/05/05 20:10 flo93 Exp $ // (C) Copyright 2011 Florian Jung (flo93@sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __FUNCTIONS_H__ #define __FUNCTIONS_H__ #include #include #include "dialogs.h" #include "type_defs.h" #include "event_tag_list.h" #include "pos.h" #include "function_dialog_consts.h" #include "undo.h" #include class QMimeData; #define FUNCTION_RANGE_ONLY_SELECTED 1 #define FUNCTION_RANGE_ONLY_BETWEEN_MARKERS 2 #define FUNCTION_ALL_PARTS 1 // Forward declarations: namespace MusECore { class Track; class Part; class PartList; } namespace MusEGui { struct FunctionDialogMode{ FunctionDialogElements_t _buttons; MusECore::Pos _pos0; MusECore::Pos _pos1; FunctionDialogMode() : _buttons(FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton) { } FunctionDialogMode(MusEGui::FunctionDialogElements_t buttons = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos()) : _buttons(buttons), _pos0(pos0), _pos1(pos1) { } }; class FunctionDialogReturnBase { public: bool _valid; bool _allEvents; bool _allParts; bool _range; MusECore::Pos _pos0; MusECore::Pos _pos1; FunctionDialogReturnBase() :_valid(false), _allEvents(false), _allParts(false), _range(false) { } FunctionDialogReturnBase(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos()) : _valid(true), _allEvents(allEvents), _allParts(allParts), _range(useRange), _pos0(pos0), _pos1(pos1) { } }; class FunctionDialogReturnErase : public FunctionDialogReturnBase { public: bool _veloThresUsed; int _veloThreshold; bool _lenThresUsed; int _lenThreshold; FunctionDialogReturnErase() : FunctionDialogReturnBase(), _veloThresUsed(false), _veloThreshold(0), _lenThresUsed(false), _lenThreshold(0) { } FunctionDialogReturnErase(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos(), bool veloThresUsed = false, int veloThreshold = 0, bool lenThresUsed = false, int lenThreshold = 0) : FunctionDialogReturnBase(allEvents, allParts, useRange, pos0, pos1), _veloThresUsed(veloThresUsed), _veloThreshold(veloThreshold), _lenThresUsed(lenThresUsed), _lenThreshold(lenThreshold) { } }; class FunctionDialogReturnCrescendo : public FunctionDialogReturnBase { public: int _start_val; int _end_val; bool _absolute; FunctionDialogReturnCrescendo() : FunctionDialogReturnBase(), _start_val(0), _end_val(0), _absolute(false) { } FunctionDialogReturnCrescendo(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos(), int startVal = 0, int endVal = 0, bool absolute = false) : FunctionDialogReturnBase(allEvents, allParts, useRange, pos0, pos1), _start_val(startVal), _end_val(endVal), _absolute(absolute) { } }; class FunctionDialogReturnDelOverlaps : public FunctionDialogReturnBase { public: FunctionDialogReturnDelOverlaps() : FunctionDialogReturnBase() { } FunctionDialogReturnDelOverlaps(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos()) : FunctionDialogReturnBase(allEvents, allParts, useRange, pos0, pos1) { } }; class FunctionDialogReturnGateTime : public FunctionDialogReturnBase { public: int _rateVal; int _offsetVal; FunctionDialogReturnGateTime() : FunctionDialogReturnBase(), _rateVal(0), _offsetVal(0) { } FunctionDialogReturnGateTime(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos(), int rateVal = 0, int offsetVal = 0) : FunctionDialogReturnBase(allEvents, allParts, useRange, pos0, pos1), _rateVal(rateVal), _offsetVal(offsetVal) { } }; class FunctionDialogReturnLegato : public FunctionDialogReturnBase { public: int _min_len; bool _allow_shortening; FunctionDialogReturnLegato() : FunctionDialogReturnBase(), _min_len(0), _allow_shortening(false) { } FunctionDialogReturnLegato(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos(), int minLen = 0, bool allowShortening = false) : FunctionDialogReturnBase(allEvents, allParts, useRange, pos0, pos1), _min_len(minLen), _allow_shortening(allowShortening) { } }; class FunctionDialogReturnMove : public FunctionDialogReturnBase { public: int _amount; FunctionDialogReturnMove() : FunctionDialogReturnBase(), _amount(0) { } FunctionDialogReturnMove(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos(), int amount = 0) : FunctionDialogReturnBase(allEvents, allParts, useRange, pos0, pos1), _amount(amount) { } }; class FunctionDialogReturnQuantize : public FunctionDialogReturnBase { public: int _strength; int _threshold; int _raster_index; int _swing; bool _quant_len; FunctionDialogReturnQuantize() : FunctionDialogReturnBase(), _strength(0), _threshold(0), _raster_index(0), _swing(0), _quant_len(false) { } FunctionDialogReturnQuantize(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos(), int strength = 0, int threshold = 0, int rasterIndex = 0, int swing = 0, bool quantLen = false) : FunctionDialogReturnBase(allEvents, allParts, useRange, pos0, pos1), _strength(strength), _threshold(threshold), _raster_index(rasterIndex), _swing(swing), _quant_len(quantLen) { } }; class FunctionDialogReturnSetLen : public FunctionDialogReturnBase { public: int _len; FunctionDialogReturnSetLen() : FunctionDialogReturnBase(), _len(0) { } FunctionDialogReturnSetLen(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos(), int len = 0) : FunctionDialogReturnBase(allEvents, allParts, useRange, pos0, pos1), _len(len) { } }; class FunctionDialogReturnTranspose : public FunctionDialogReturnBase { public: int _amount; FunctionDialogReturnTranspose() : FunctionDialogReturnBase(), _amount(0) { } FunctionDialogReturnTranspose(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos(), int amount = 0) : FunctionDialogReturnBase(allEvents, allParts, useRange, pos0, pos1), _amount(amount) { } }; class FunctionDialogReturnVelocity : public FunctionDialogReturnBase { public: int _rateVal; int _offsetVal; FunctionDialogReturnVelocity() : FunctionDialogReturnBase(), _rateVal(0), _offsetVal(0) { } FunctionDialogReturnVelocity(bool allEvents, bool allParts, bool useRange = false, const MusECore::Pos& pos0 = MusECore::Pos(), const MusECore::Pos& pos1 = MusECore::Pos(), int rateVal = 0, int offsetVal = 0) : FunctionDialogReturnBase(allEvents, allParts, useRange, pos0, pos1), _rateVal(rateVal), _offsetVal(offsetVal) { } }; //the below functions automatically open the dialog //they return true if you click "ok" and false if "abort" FunctionDialogReturnErase erase_items_dialog (const FunctionDialogMode&); FunctionDialogReturnCrescendo crescendo_items_dialog (const FunctionDialogMode&); FunctionDialogReturnDelOverlaps deloverlaps_items_dialog(const FunctionDialogMode&); FunctionDialogReturnGateTime gatetime_items_dialog (const FunctionDialogMode&); FunctionDialogReturnLegato legato_items_dialog (const FunctionDialogMode&); FunctionDialogReturnMove move_items_dialog (const FunctionDialogMode&); FunctionDialogReturnQuantize quantize_items_dialog (const FunctionDialogMode&); FunctionDialogReturnSetLen setlen_items_dialog (const FunctionDialogMode&); FunctionDialogReturnTranspose transpose_items_dialog (const FunctionDialogMode&); FunctionDialogReturnVelocity velocity_items_dialog (const FunctionDialogMode&); } // namespace MusEGui namespace MusECore { std::set partlist_to_set(PartList* pl); std::set part_to_set(const Part* p); std::map get_events(const std::set& parts, int range, MusECore::RelevantSelectedEvents_t relevant = MusECore::NotesRelevant); //these functions simply do their job, non-interactively bool modify_velocity(const std::set& parts, int range, int rate, int offset=0); bool modify_off_velocity(const std::set& parts, int range, int rate, int offset=0); bool modify_notelen(const std::set& parts, int range, int rate, int offset=0); bool quantize_notes(const std::set& parts, int range, int raster, bool len=false, int strength=100, int swing=0, int threshold=0); bool erase_notes(const std::set& parts, int range, int velo_threshold=0, bool velo_thres_used=false, int len_threshold=0, bool len_thres_used=false); bool delete_overlaps(const std::set& parts, int range); bool set_notelen(const std::set& parts, int range, int len); bool move_notes(const std::set& parts, int range, signed int ticks); bool transpose_notes(const std::set& parts, int range, signed int halftonesteps); bool crescendo(const std::set& parts, int range, int start_val, int end_val, bool absolute); bool legato(const std::set& parts, int range, int min_len=1, bool dont_shorten=false); bool modify_velocity_items(TagEventList* tag_list, int rate, int offset=0); bool modify_off_velocity_items(TagEventList* tag_list, int rate, int offset=0); bool modify_notelen_items(TagEventList* tag_list, int rate, int offset=0); bool quantize_items(TagEventList* tag_list, int raster, bool quant_len=false, int strength=100, int swing=0, int threshold=0); bool erase_items(TagEventList* tag_list, int velo_threshold=0, bool velo_thres_used=false, int len_threshold=0, bool len_thres_used=false); bool delete_overlaps_items(TagEventList* tag_list); bool set_notelen_items(TagEventList* tag_list, int len); bool move_items(TagEventList* tag_list, signed int ticks); bool transpose_items(TagEventList* tag_list, signed int halftonesteps); bool crescendo_items(TagEventList* tag_list, int start_val, int end_val, bool absolute); bool legato_items(TagEventList* tag_list, int min_len=1, bool dont_shorten=false); //------------------------------------------------------------------------ // NOTE: The copy/cut/paste functions are for event lists such as found in // the various editors like pianoroll and drum editor. // A different set of copy and paste functions are used for the // part canvas (in the arranger). Use them for copying and pasting parts. // TODO TODO Unify those part copy/cut/paste routines into these routines ! //------------------------------------------------------------------------ // void copy_items(); void copy_items(TagEventList* tag_list); // bool cut_items(); bool cut_items(TagEventList* tag_list); QMimeData* cut_or_copy_tagged_items_to_mime(TagEventList* tag_list, bool cut_mode = false); bool paste_items(const std::set& parts, const Part* paste_into_part=NULL); // shows a dialog void paste_items(const std::set& parts, int max_distance=3072, // Options. Default is erase target existing controllers first + erase wysiwyg. const FunctionOptionsStruct& options = FunctionOptionsStruct(), // Paste into this part instead of the original part(s). const Part* paste_into_part=NULL, // Number of copies to paste. int amount=1, // Separation between copies. int raster=3072, // Choose which events to paste. RelevantSelectedEvents_t relevant = AllEventsRelevant, // If pasting controllers, paste into this controller number if not -1. // If the source has multiple controllers, user will be asked which one to paste. int paste_to_ctrl_num = -1 ); void paste_items_at( // List of parts from which to look for original part(s). const std::set& parts, // Text Xml list of parts and events to paste. const QString& pt, // Position to paste at. const Pos& pos, // Distance at which destination part is too far away // (too soon, to the left) so a new part will be created. int max_distance=3072, // Erase target existing controllers first + erase wysiwyg. const FunctionOptionsStruct& options = FunctionOptionsStruct(), // Paste into this part instead of the original part(s). const Part* paste_into_part=NULL, // Number of copies to paste. int amount=1, // Separation between copies. int raster=3072, // Choose which events to paste. RelevantSelectedEvents_t relevant = AllEventsRelevant, // If pasting controllers, paste into this controller number if not -1. // If the source has multiple controllers, user will be asked which one to paste. int paste_to_ctrl_num = -1 ); void paste_items_at( // List of parts from which to look for original part(s). const std::set& parts, // List of parts and events to paste. const TagEventList* tag_list, // Position to paste at. const Pos& pos, // Distance at which destination part is too far away // (too soon, to the left) so a new part will be created. int max_distance=3072, // Erase target existing controllers first + erase wysiwyg. const FunctionOptionsStruct& options = FunctionOptionsStruct(), // Paste into this part instead of the original part(s). const Part* paste_into_part=NULL, // Number of copies to paste. int amount=1, // Separation between copies. int raster=3072, // Choose which events to paste. RelevantSelectedEvents_t relevant = AllEventsRelevant, // If pasting controllers, paste into this controller number if not -1. // If the source has multiple controllers, user will be asked which one to paste. int paste_to_ctrl_num = -1 ); //functions for copy'n'paste void copy_notes(const std::set& parts, int range); bool paste_notes(const Part* paste_into_part=NULL); // shows a dialog void paste_notes(int max_distance=3072, bool always_new_part=false, bool never_new_part=false, const Part* paste_into_part=NULL, int amount=1, int raster=3072); QMimeData* selected_events_to_mime(const std::set& parts, int range); QMimeData* parts_to_mime(const std::set& parts); void paste_at(const QString& pt, int pos, int max_distance=3072, bool always_new_part=false, bool never_new_part=false, const Part* paste_into_part=NULL, int amount=1, int raster=3072); //functions for selections void select_all(const std::set& parts); void select_none(const std::set& parts); void select_invert(const std::set& parts); void select_in_loop(const std::set& parts); void select_not_in_loop(const std::set& parts); bool tracks_are_selected(); bool parts_are_selected(); //functions for parts void shrink_parts(int raster=-1); //negative values mean "config.division" void expand_parts(int raster=-1); void schedule_resize_all_same_len_clone_parts(const Part* part, unsigned new_len, Undo& operations); void clean_parts(); bool merge_with_next_part(const Part* part); bool merge_selected_parts(); bool merge_parts(const std::set& parts); bool split_part(const Part* part, int tick); bool delete_selected_parts(); PartList* getSelectedMidiParts(); PartList* getSelectedWaveParts(); PartList* getSelectedParts(); // called from GUI thread, calls applyOperationGroup. void resize_part(Track* t, Part* p, unsigned int newTickPosOrLen, MusECore::ResizeDirection resizeDirection, bool doClones = false, bool dragEvents = false); // internal QMimeData* file_to_mimedata(FILE *datafile, QString mimeType); } // namespace MusECore #endif muse-4.0.0/src/muse/gconfig.cpp000066400000000000000000000514411404105311000163360ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: gconfig.cpp,v 1.15.2.13 2009/12/01 03:52:40 terminator356 Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "gconfig.h" #include "xml.h" namespace MusEGlobal { /* --- PLEASE READ BEFORE EDITING --- * * The values below are default init parameters for most MusE * configuration parameters. * They are however NOT certain to have this value! * * This is for two reasons: * 1) MusE has a config file which overrides these values * 2) When no configuration file exists a default TEMPLATE * is loaded from the share dir which overwrites most * of these values. * In a perfect world it would overwrite all values and * these would be removed. * * SE 3 2019: In a perfect world ALL configuration values are stored here. * And most are. But some are not, they are scattered throughout the app. * But they are not THAT difficult to find by reverse looking up an xml tag * in an existing config file to find out what uses it. * We should strive to keep ALL config values here, where each item is * guaranteed to have a value. Some devs (me included) are neglecting * to put new values in the template. So it becomes ineffective. * * Therefore item 2) is no longer true. It has been disabled in main.cpp. * Tim. */ StripConfig::StripConfig() { _serial = -1; _tmpFileIdx = -1; _visible = true; _width = -1; _deleted = false; } StripConfig::StripConfig(int trackSerial, bool visible, int width) { _serial = trackSerial; _tmpFileIdx = -1; _visible = visible; _width = width; _deleted = false; } bool StripConfig::isNull() const { return _serial < 0; } GlobalConfigValues config = { QStringList(), // pluginLadspaPathList QStringList(), // pluginDssiPathList QStringList(), // pluginVstPathList QStringList(), // pluginLinuxVstPathList QStringList(), // pluginLv2PathList false, // pluginCacheTriggerRescan Whether to trigger a plugin cache rescan. 170, // globalAlphaBlend { QColor(0xff, 0xff, 0xff), // palette QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff) }, { QColor(255, 232, 140), // part colors QColor(0xff, 0x00, 0x00), QColor(0x00, 0xff, 0x00), QColor(0x00, 0x00, 0xff), QColor(0xff, 0xff, 0x00), QColor(0x00, 0xff, 0xff), QColor(0xff, 0x00, 0xff), QColor(0x9f, 0xc7, 0xef), QColor(0x00, 0xff, 0x7f), QColor(0x7f, 0x00, 0x00), QColor(0x00, 0x7f, 0x00), QColor(0x00, 0x00, 0x7f), QColor(0x7f, 0x7f, 0x3f), QColor(0x00, 0x7f, 0x7f), QColor(0x7f, 0x00, 0x7f), QColor(0x00, 0x7f, 0xff), QColor(0x00, 0x3f, 0x3f), QColor(170, 85, 0) }, { QString("Default"), // Default part color names QString("Refrain"), QString("Bridge"), QString("Intro"), QString("Coda"), QString("Chorus"), QString("Solo"), QString("Brass"), QString("Percussion"), QString("Drums"), QString("Guitar"), QString("Bass"), QString("Flute"), QString("Strings"), QString("Keyboard"), QString("Piano"), QString("Saxophone"), QString("Organ") }, QColor(51, 114, 178), // transportHandleColor; QColor(219, 65, 65), // bigTimeForegroundColor; QColor(0, 0, 0), // bigTimeBackgroundColor; QColor(200, 192, 171), // waveEditBackgroundColor; { QFont(), // filled with system font dynamically QFont(QString(), 7, QFont::Normal), // Mixer strips and midi track info panel QFont(QString(), 8, QFont::Normal), // Part name overlay QFont(QString(), 9, QFont::Normal), QFont(QString(), 8, QFont::Normal), // Small numbers: Timescale and markers QFont(QString(), 8, QFont::Bold), // Small bold numbers such as marker text QFont(QString(), 8, QFont::Bold, true) // Mixer strip labels. Looks and fits better with bold + italic than bold alone, // at the price of only few more pixels than Normal mode. }, true, // autoAdjustFontSize; QColor(84, 97, 114), // trackBg; QColor(109, 174, 178), // selected track Bg; QColor(0x00, 0x00, 0x00), // selected track Fg; QColor(90, 154, 158), // current selected track Bg; Qt::gray, // trackSectionDividerColor; QColor(74, 150, 194), // midiTrackLabelBg; // Med blue QColor(150, 177, 189), // drumTrackLabelBg; // Pale pastel blue QColor(116, 232, 242), // newDrumTrackLabelBg; // Light blue QColor(213, 128, 202), // waveTrackLabelBg; // magenta QColor(84, 185, 58), // outputTrackLabelBg; // green QColor(199, 75, 64), // inputTrackLabelBg; // red QColor(236, 214, 90), // groupTrackLabelBg; // yellow QColor(142, 157, 6), // auxTrackLabelBg; // Med olive QColor(229, 157, 101), // synthTrackLabelBg; // Med orange QColor(215, 220, 230), // midiTrackBg; QColor(215, 220, 230), // drumTrackBg; QColor(215, 220, 230), // newDrumTrackBg; QColor(220, 209, 217), // waveTrackBg; QColor(197, 220, 206), // outputTrackBg; QColor(220, 214, 206), // inputTrackBg; QColor(220, 216, 202), // groupTrackBg; QColor(208, 215, 220), // auxTrackBg; QColor(220, 211, 202), // synthTrackBg; QColor(98, 124, 168), // part canvas bg QColor(71, 71, 71), // partCanvasCoarseRasterColor; QColor(90, 90, 90), // partCanvasBeatRasterColor; QColor(130, 136, 168), // partCanvasFineRasterColor; QColor(255, 170, 0), // ctrlGraphFg; Medium orange Qt::blue, // ctrlGraphSel; // QColor(0, 0, 0), // mixerBg; QColor(0xe0, 0xe0, 0xe0), // Ruler background QColor(0, 0, 0), // Ruler text QColor(255, 255, 255), // Midi editor canvas QColor(255, 255, 255), // midiControllerViewBg QColor(255, 255, 255), // drumListBg Qt::black, // drumListFont Qt::yellow, // drumListSel Qt::black, // drumListSelFont QColor(255, 255, 255), // rulerCurrent QColor(210, 210, 210), // midiCanvasFineColor QColor(130, 130, 130), // midiCanvasBeatColor Qt::black, // midiCanvasBarColor Qt::blue, // midiItemColor QColor(255, 170, 0), // midiItemSelectedColor Qt::lightGray, // dummyPartColor Qt::gray, // midiDividerColor QColor(0, 191, 255), // pianoCurrentKey; QColor(255, 69, 0), // pianoPressedKey; QColor(255, 218, 185), // pianoSelectedKey Qt::lightGray, // waveNonselectedPart Qt::darkGray, // wavePeakColor Qt::black, // waveRmsColor Qt::lightGray, // wavePeakColorSelected Qt::white, // waveRmsColorSelected Qt::darkGray, // partWaveColorPeak QColor(20,20,20), // partWaveColorRms QColor(54,54,54), // partMidiDarkEventColor QColor(200,200,200), // partMidiLightEventColor QColor(0,181,241 ), // sliderBarDefaultColor Qt::gray, // sliderDefaultColor // QColor(228,203,36 ), // sliderDefaultColor QColor(78,172,35 ), // panSliderColor QColor(209,86,86 ), // gainSliderColor QColor(190,190,39 ), // auxSliderColor QColor(154,135,124), // audioVolumeSliderColor QColor(153,156,124), // midiVolumeSliderColor QColor(154,135,124), // audioVolumeHandleColor QColor(153,156,124), // midiVolumeHandleColor QColor(37,121,255 ), // audioControllerSliderDefaultColor QColor(220,77,255 ), // audioPropertySliderDefaultColor QColor(37,121,255 ), // midiControllerSliderDefaultColor QColor(220,77,255 ), // midiPropertySliderDefaultColor QColor(100,255,255), // midiPatchReadoutColor Qt::gray, // knobFontColor QColor(0,221,255 ), // audioMeterPrimaryColor QColor(0,221,255 ), // midiMeterPrimaryColor Qt::black, // meterBackgroundColor Qt::gray, // rackItemBackgroundColor QColor(0, 120, 200), // rackItemBgActiveColor Qt::black, // rackItemFontColor Qt::white, // rackItemFontActiveColor Qt::darkGray, // rackItemBorderColor Qt::yellow, // rackItemBorderHoverColor Qt::gray, // midiInstrumentBackgroundColor; QColor(0, 120, 200), // midiInstrumentBgActiveColor; Qt::black, // midiInstrumentFontColor; Qt::white, // midiInstrumentFontActiveColor; Qt::darkGray, // midiInstrumentBorderColor; Qt::green, // markerColor; Qt::blue, // rangeMarkerColor; Qt::red, // positionMarkerColor; Qt::black, // currentPositionColor; MusEGlobal::WaveOutLine, // waveDrawing // maxAliasedPointSize At what point size to switch from aliased text to non-aliased text. // Zero means always use anti-aliasing. For certain widgets that use it. May be more later. 8, 18, // iconSize 18, // cursorSize (for custom cursors) true, // cascadeStylesheets true, // showIconsInMenus true, // useNativeStandardDialogs 16, // trackGradientStrength 64, // partGradientStrength false, // enableAlsaMidiDriver Whether to enable the ALSA midi driver 384, // division; 1024, // rtcTicks 0, // curMidiSyncInPort The currently selected midi sync input port. true, // midiSendInit Send instrument initialization sequences true, // warnInitPending Warn instrument initialization sequences pending false, // midiSendCtlDefaults Send instrument controller defaults at position 0 if none in song false, // midiSendNullParameters Send null parameters after each (N)RPN event false, // midiOptimizeControllers Don't send redundant H/L parameters or H/L values true, // warnIfBadTiming Warn if timer res not good false, // velocityPerNote Whether to show per-note or all velocities -60, // int minMeter; -60.0, // double minSlider; true, // use Jack freewheel 20, // int guiRefresh; QString(""), // userInstrumentsDir // Obsolete. Must keep for compatibility. //QString(""), // helpBrowser; // Obsolete true, // extendedMidi 384, // division for smf export QString(""), // copyright string for smf export 1, // smf export file format false, // midi export file 2 byte timesigs instead of 4 true, // optimize midi export file note offs true, // expRunningStatus; Save space by using running status true, // Split imported tracks into multiple parts. false, // useLastEditedEvent // Obsolete. There is only 'New' drum tracks now. // true, // importMidiNewStyleDrum true, // importDevNameMetas Import Prefer Device Name metas over port number metas if both exist. true, // importInstrNameMetas Import Prefer Instrument Name metas over Mode sysexes if both exist. MusEGlobal::PORT_NUM_META | MusEGlobal::DEVICE_NAME_META, // exportPortsDevices. Or'd ExportPortsDevices_t flags. Export port number metas and/or device name metas. true, // exportPortDeviceSMF0 Export a port and/or device meta even for SMF0. MusEGlobal::MODE_SYSEX | MusEGlobal::INSTRUMENT_NAME_META, // exportModeInstr. Or'd ExportModeInstr_t flags. Export mode sysexes and/or instrument name metas. QString("GM"), // importMidiDefaultInstr Default to this instrument not Generic, if no match found true, // exportDrumMapOverrides Apply drum map overrides to export true, // exportChannelOverridesToNewTrack Drum map Channel overrides go to a separate track 1, // startMode QString(""), // start song path false, // startSongLoadConfig QRect(0, 0, 0, 0), // GeometryMain; QRect(0, 0, 200, 100), // GeometryTransport; QRect(0, 0, 600, 200), // GeometryBigTime; { QString("Mixer A"), QStringList(), QRect(0, 0, 300, 500), // Mixer1 true, true, true, true, true, true, true, true, true, MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW, QList(), QList() }, { QString("Mixer B"), QStringList(), QRect(200, 200, 300, 500), // Mixer2 true, true, true, true, true, true, true, true, true, MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW, QList(), QList() }, false, // TransportVisible; false, // BigTimeVisible; false, // mixer1Visible; false, // mixer2Visible; false, // markerVisible; // true, // arrangerVisible; true, // showSplashScreen 1, // canvasShowPartType 1 - names, 2 events 5, // canvasShowPartEvent true, // canvasShowGrid; false, // canvasShowGridHorizontalAlways false, // canvasShowGridBeatsAlways true, // useTrackColorForParts QString(""), // canvasBgPixmap; QStringList(), // canvasCustomBgList // QString(""), // default styleSheetFile // QString("Fusion"), // style QString("Dark Flat"), // theme QString("sweep"), // externalWavEditor false, // useOldStyleStopShortCut true, // useRewindOnStop false, // moveArmedCheckBox true, // useDenormalBias false, // useOutputLimiter true, // showDidYouKnow false, // vstInPlace Enable VST in-place processing 44100, // Device audio preferred sample rate 512, // Device audio buffer size 0, // Device RtAudio selected backend true, // useJackTransport true, // timebaseMaster QString("./"), // projectBaseFolder true, // projectStoreInFolder true, // useProjectSaveDialog 256, // minControlProcessPeriod false, // popupsDefaultStayOpen false, // leftMouseButtonCanDecrease // false, // rangeMarkerWithoutMMB MusECore::DONT_REC_MUTED_OR_HIDDEN, true, // addHiddenTracks true, // unhideTracks true, // smartFocus 24, // trackHeight true, // borderlessMouse false, // autoSave false, // scrollableSubMenus true, // liveWaveUpdate true, // warnOnFileVersions Warn if file version different than current MusEGlobal::CONF_LV2_UI_USE_FIRST, //lv2UiBehavior 4, // audioEffectsRackVisibleItems true, // preferKnobsVsSliders Whether to prefer the use of knobs over sliders, esp in mixer. true, // showControlValues Whether to show the value along with label in small controls, esp in mixer. true, // monitorOnRecord Whether to automatically monitor on record arm. true, // lineEditStyleHack Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. false, // preferMidiVolumeDb Prefer midi volume as decibels instead of 0-127. true, // midiCtrlGraphMergeErase Whether to erase underlying erase target items when dragging/dropping source items. false, // midiCtrlGraphMergeEraseInclusive Whether to erase target items in-between source item groups. true, // midiCtrlGraphMergeEraseWysiwyg Whether to erase past the last item in a group to include its original source width. MusEGlobal::RoutePreferSecondAlias, // preferredRouteNameOrAlias true, // routerExpandVertically 2, // routerGroupingChannels false, // enableLatencyCorrection. false, // correctUnterminatedOutBranchLatency false, // correctUnterminatedInBranchLatency false, // monitoringAffectsLatency false, // commonProjectLatency "", // mixdownPath true, // showNoteNamesInPianoRoll true, // showNoteTooltips true, // showTimeScaleBeatNumbers false, // selectionsUndoable Whether selecting parts or events is undoable. true, // noPluginScaling true, // keepTransportWindowOnTop true, // showStatusBar 6 // recentListLength - number of files in recent menu }; } // namespace MusEGlobal muse-4.0.0/src/muse/gconfig.h000066400000000000000000000375501404105311000160100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: gconfig.h,v 1.12.2.10 2009/12/01 03:52:40 terminator356 Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CONFIG_H__ #define __CONFIG_H__ #define NUM_PARTCOLORS 18 #define NUM_FONTS 7 #include #include #include #include #include #include namespace MusECore { class Xml; enum newDrumRecordCondition_t { REC_ALL = 0, DONT_REC_HIDDEN = 1, DONT_REC_MUTED = 2, DONT_REC_MUTED_OR_HIDDEN = 3 }; } namespace MusEGlobal { // font scaling factors struct FntFac { static constexpr double F1 = 0.7; static constexpr double F2 = 0.8; static constexpr double F3 = 0.9; static constexpr double F4 = 0.8; static constexpr double F5 = 0.8; static constexpr double F6 = 0.8; }; // Or'd together enum ExportPortsDevices_t { PORT_NUM_META = 0x01, DEVICE_NAME_META = 0x02, }; // Or'd together enum ExportModeInstr_t { MODE_SYSEX = 0x01, INSTRUMENT_NAME_META = 0x02, }; enum RouteNameAliasPreference { RoutePreferCanonicalName = 0, RoutePreferFirstAlias = 1, RoutePreferSecondAlias = 2 }; enum WaveDrawing { WaveRmsPeak=1, WaveOutLine=2 }; struct StripConfig { // The corresponding track's serial number. Can be -1. int _serial; // The corresponding track's index in the song file. Can be -1. // Temporary during loading to avoid using globally or locally // 'unique' identifiers in the song file, such as the serial, // to resolve references. int _tmpFileIdx; bool _visible; int _width; bool _deleted; StripConfig(); StripConfig(int trackSerial, bool visible, int width); bool isNull() const; void write(int level, MusECore::Xml& xml) const; void read(MusECore::Xml& xml); }; typedef QList StripConfigList_t; typedef StripConfigList_t::iterator iStripConfigList; typedef StripConfigList_t::const_iterator ciStripConfigList; //--------------------------------------------------------- // MixerConfig //--------------------------------------------------------- struct MixerConfig { enum DisplayOrder { STRIPS_TRADITIONAL_VIEW = -1004, STRIPS_EDITED_VIEW = -1003, STRIPS_ARRANGER_VIEW = -1002, }; QString name; // Obsolete. Keep for old song support. QStringList stripOrder; QRect geometry; bool showMidiTracks; bool showDrumTracks; bool showNewDrumTracks; bool showInputTracks; bool showOutputTracks; bool showWaveTracks; bool showGroupTracks; bool showAuxTracks; bool showSyntiTracks; DisplayOrder displayOrder; // Obsolete. Keep for old song support. QList stripVisibility; // This replaces stripOrder and stripVisibility. // NOTE: To avoid having to put this information within a track, // we keep it conveniently here. But this means it does not // participate in the UNDO/REDO system. If a track is 'deleted' // the information here MUST be allowed to exist so that undoing // the track 'delete' finds the info. // Thus it acts sort of 'in parallel' to the undo system and is similar // to the undo list (it never dies). The redo list can die, and we // could safely remove these corresponding items. FIXME TODO: DO THAT! // Therefore the list must generally be protected from haphazard item // removal for the duration of the song file session. // When writing to file BE SURE to ignore items with no corresponding track, // to filter out all the undesired 'deleted' ones. StripConfigList_t stripConfigList; void write(int level, MusECore::Xml& xml, bool global) const; void read(MusECore::Xml& xml); }; //--------------------------------------------------------- // GlobalConfigValues //--------------------------------------------------------- enum CONF_LV2_UI_BEHAVIOR { CONF_LV2_UI_USE_FIRST = 0, CONF_LV2_UI_ASK_ONCE, CONF_LV2_UI_ASK_ALWAYS }; struct GlobalConfigValues { QStringList pluginLadspaPathList; QStringList pluginDssiPathList; QStringList pluginVstPathList; QStringList pluginLinuxVstPathList; QStringList pluginLv2PathList; bool pluginCacheTriggerRescan; // Whether to trigger a plugin cache rescan. int globalAlphaBlend; QColor palette[16]; QColor partColors[NUM_PARTCOLORS]; QString partColorNames[NUM_PARTCOLORS]; QColor transportHandleColor; QColor bigTimeForegroundColor; QColor bigTimeBackgroundColor; QColor waveEditBackgroundColor; QFont fonts[NUM_FONTS]; bool autoAdjustFontSize; QColor trackBg; QColor selectTrackBg; QColor selectTrackFg; QColor selectTrackCurBg; QColor trackSectionDividerColor; QColor midiTrackLabelBg; QColor drumTrackLabelBg; QColor newDrumTrackLabelBg; QColor waveTrackLabelBg; QColor outputTrackLabelBg; QColor inputTrackLabelBg; QColor groupTrackLabelBg; QColor auxTrackLabelBg; QColor synthTrackLabelBg; QColor midiTrackBg; QColor drumTrackBg; QColor newDrumTrackBg; QColor waveTrackBg; QColor outputTrackBg; QColor inputTrackBg; QColor groupTrackBg; QColor auxTrackBg; QColor synthTrackBg; QColor partCanvasBg; QColor partCanvasCoarseRasterColor; QColor partCanvasBeatRasterColor; QColor partCanvasFineRasterColor; QColor ctrlGraphFg; QColor ctrlGraphSel; // QColor mixerBg; QColor rulerBg; QColor rulerFg; QColor midiCanvasBg; QColor midiControllerViewBg; QColor drumListBg; QColor drumListFont; QColor drumListSel; QColor drumListSelFont; QColor rulerCurrent; QColor midiCanvasFineColor; QColor midiCanvasBeatColor; QColor midiCanvasBarColor; QColor midiItemColor; QColor midiItemSelectedColor; QColor dummyPartColor; QColor midiDividerColor; QColor pianoCurrentKey; QColor pianoPressedKey; QColor pianoSelectedKey; QColor waveNonselectedPart; QColor wavePeakColor; QColor waveRmsColor; QColor wavePeakColorSelected; QColor waveRmsColorSelected; QColor partWaveColorPeak; QColor partWaveColorRms; QColor partMidiDarkEventColor; QColor partMidiLightEventColor; QColor sliderBarColor; QColor sliderBackgroundColor; QColor panSliderColor; QColor gainSliderColor; QColor auxSliderColor; QColor audioVolumeSliderColor; QColor midiVolumeSliderColor; QColor audioVolumeHandleColor; QColor midiVolumeHandleColor; QColor audioControllerSliderColor; QColor audioPropertySliderColor; QColor midiControllerSliderColor; QColor midiPropertySliderColor; QColor midiPatchReadoutColor; QColor knobFontColor; QColor audioMeterPrimaryColor; QColor midiMeterPrimaryColor; QColor meterBackgroundColor; QColor rackItemBackgroundColor; QColor rackItemBgActiveColor; QColor rackItemFontColor; QColor rackItemFontActiveColor; QColor rackItemBorderColor; QColor rackItemFontColorHover; QColor midiInstrumentBackgroundColor; QColor midiInstrumentBgActiveColor; QColor midiInstrumentFontColor; QColor midiInstrumentFontActiveColor; QColor midiInstrumentBorderColor; QColor markerColor; QColor rangeMarkerColor; QColor positionMarkerColor; QColor currentPositionColor; WaveDrawing waveDrawing; // At what point size to switch from aliased text to non-aliased text. Zero means always use anti-aliasing. // For certain widgets that use it. May be more later. int maxAliasedPointSize; int iconSize; int cursorSize; bool cascadeStylesheets; bool showIconsInMenus; bool useNativeStandardDialogs; int trackGradientStrength; int partGradientStrength; bool enableAlsaMidiDriver; // Whether to enable the ALSA midi driver int division; int rtcTicks; int curMidiSyncInPort; // The currently selected midi sync input port. bool midiSendInit; // Send instrument initialization sequences bool warnInitPending; // Warn instrument initialization sequences pending bool midiSendCtlDefaults; // Send instrument controller defaults at position 0 if none in song bool midiSendNullParameters; // Send null parameters after each (N)RPN event bool midiOptimizeControllers; // Don't send redundant H/L parameters or H/L values bool warnIfBadTiming; // Warn if timer res not good bool velocityPerNote; // Whether to show per-note or all velocities int minMeter; double minSlider; bool freewheelMode; int guiRefresh; QString userInstrumentsDir; // Obsolete. Must keep for compatibility. bool extendedMidi; // extended smf format int midiDivision; // division for smf export QString copyright; // copyright string for smf export int smfFormat; // smf export file type bool exp2ByteTimeSigs; // Export 2 byte time sigs instead of 4 bytes bool expOptimNoteOffs; // Save space by replacing note offs with note on velocity 0 bool expRunningStatus; // Save space by using running status bool importMidiSplitParts; // Split imported tracks into multiple parts. bool useLastEditedEvent; // option to set event properties from last edited when adding events in pianoroll // Obsolete. There is only 'New' drum tracks now. // bool importMidiNewStyleDrum; // Use new style drum tracks bool importDevNameMetas; // Import Prefer Device Name metas over port number metas if both exist. bool importInstrNameMetas; // Import Prefer Instrument Name metas over Mode sysexes if both exist. int exportPortsDevices; // Or'd ExportPortsDevices_t flags. Export port number metas and/or device name metas. bool exportPortDeviceSMF0; // Export a port and/or device meta even for SMF0. int exportModeInstr; // Or'd ExportModeInstr_t flags. Export mode sysexes and/or instrument name metas. QString importMidiDefaultInstr; // Default to this instrument not Generic, if no match found bool exportDrumMapOverrides; // Apply Port, Channel, and ANote drum map overrides to export bool exportChannelOverridesToNewTrack; // Drum map Channel overrides go to a separate track int startMode; // 0 - start with last song // 1 - start with default template // 2 - start with song QString startSong; // path for start song bool startSongLoadConfig; // Whether to load configuration with the start template or song QRect geometryMain; QRect geometryTransport; QRect geometryBigTime; MixerConfig mixer1; MixerConfig mixer2; bool transportVisible; bool bigTimeVisible; bool mixer1Visible; bool mixer2Visible; bool markerVisible; // bool arrangerVisible; bool showSplashScreen; int canvasShowPartType; // 1 - names, 2 events int canvasShowPartEvent; // bool canvasShowGrid; bool canvasShowGridHorizontalAlways; bool canvasShowGridBeatsAlways; bool useTrackColorForParts; QString canvasBgPixmap; QStringList canvasCustomBgList; // QString styleSheetFile; // QString style; QString theme; QString externalWavEditor; bool useOldStyleStopShortCut; bool useRewindOnStop; bool moveArmedCheckBox; bool useDenormalBias; bool useOutputLimiter; bool showDidYouKnow; bool vstInPlace; // Enable VST in-place processing int deviceAudioSampleRate; int deviceAudioBufSize; int deviceAudioBackend; bool useJackTransport; bool timebaseMaster; QString projectBaseFolder; bool projectStoreInFolder; bool useProjectSaveDialog; unsigned long minControlProcessPeriod; bool popupsDefaultStayOpen; bool leftMouseButtonCanDecrease; // bool rangeMarkerWithoutMMB; MusECore::newDrumRecordCondition_t newDrumRecordCondition; bool addHiddenTracks; bool unhideTracks; // Obsolete. There is only 'New' drum tracks now. // drumTrackPreference_t drumTrackPreference; bool smartFocus; int trackHeight; bool borderlessMouse; bool autoSave; bool scrollableSubMenus; bool liveWaveUpdate; //live update wave tracks while recording bool warnOnFileVersions; // Warn if file version different than current CONF_LV2_UI_BEHAVIOR lv2UiBehavior; int audioEffectsRackVisibleItems; // Number of rack slots visible at one time in the rack. bool preferKnobsVsSliders; // Whether to prefer the use of knobs over sliders, esp in mixer. bool showControlValues; // Whether to show the value along with label in small controls, esp in mixer. bool monitorOnRecord; // Whether to automatically monitor on record arm. bool lineEditStyleHack; // Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. bool preferMidiVolumeDb; // Prefer midi volume as decibels instead of 0-127. // NOTE: The following are similar to the paste dialog function options, stored separately. bool midiCtrlGraphMergeErase; // Whether to erase underlying erase target items when dragging/dropping source items. bool midiCtrlGraphMergeEraseInclusive; // Whether to erase target items in-between source item groups. bool midiCtrlGraphMergeEraseWysiwyg; // Whether to erase past the last item in a group to include its original source width. RouteNameAliasPreference preferredRouteNameOrAlias; bool routerExpandVertically; // Whether to expand the router items vertically. (Good use of space but slow!) // How to group the router channels together for easier multi-channel manipulation. int routerGroupingChannels; // Whether to enable latency correction/compensation. bool enableLatencyCorrection; // Whether to include unterminated output branches in latency correction calculations. bool correctUnterminatedOutBranchLatency; // Whether to include unterminated input branches in latency correction calculations. bool correctUnterminatedInBranchLatency; // Whether a track's monitoring feature affects latency. bool monitoringAffectsLatency; // Whether completely independent branches share a common latency. bool commonProjectLatency; QString mixdownPath; bool showNoteNamesInPianoRoll; bool showNoteTooltips; bool showTimeScaleBeatNumbers; // Whether selecting parts or events is undoable. // If set, it can be somewhat tedious for the user to step through all the undo/redo items. bool selectionsUndoable; // Revert native GUI scaling on HiDPI bool noPluginScaling; bool keepTransportWindowOnTop; bool showStatusBar; int recentListLength; }; extern GlobalConfigValues config; } // namespace MusEGlobal #endif muse-4.0.0/src/muse/givertcap.c000066400000000000000000000045521404105311000163470ustar00rootroot00000000000000/* * COPYRIGHT * * This file is part of Mustajuuri GPL modules. You may distribute it * with or without other Mustajuuri components. * * Author: Tommi Ilmonen, 2001. * Tommi.Ilmonen@hut.fi * * http://www.tml.hut.fi/~tilmonen/mustajuuri/ * This app also has its own home page at (installation instruction * etc.): http://www.tml.hut.fi/~tilmonen/givertcap/ * This file is licensed under the GNU Public License (GPL) version * 2. The GPL can also be found from the givertcap home page. Any * application may call civertcap (regardless of the license of the * calling application). * If you want a parallel license (for commercial reasons for example), * you should negotiate the matter with the author(s). * */ #include #include #include #undef _POSIX_SOURCE #include /* If the compilation fails on the preceding line, then you probably do not have the libcap installed. */ static void usage(const char *programName) { fprintf(stderr, "usage: %s \n\n" " This program gives real-time application capabilities to the" " parent process\n\n" "[Copyright (c) 2001 Tommi Ilmonen ]\n" "Home page: http://www.tml.hut.fi/~tilmonen/givertcap/\n", programName); } int main(int argc, char **argv) { if(argc > 1) { usage(argv[0]); return 1; } pid_t parentPid = getppid(); if(!parentPid) return 1; cap_t caps = cap_init(); #define nofCaps 3 /* We need these capabilities: CAP_SYS_NICE -> Real-time priority CAP_SYS_RESOURCE -> RTC above 64 Hz CAP_IPC_LOCK -> mlockall */ cap_value_t capList[nofCaps] = { CAP_SYS_NICE, CAP_SYS_RESOURCE, CAP_IPC_LOCK} ; cap_clear(caps); cap_set_flag(caps, CAP_EFFECTIVE, nofCaps, capList , CAP_SET); cap_set_flag(caps, CAP_INHERITABLE, nofCaps, capList , CAP_SET); cap_set_flag(caps, CAP_PERMITTED, nofCaps, capList , CAP_SET); /* If your COMPILATION FAILS here then you probably are not running Linux. the function "capsetp" is not part of the POSIX capability standard, but a Linux-specific extension. */ if (capsetp(parentPid, caps)) { perror("mjsucaps: capsetp"); return 1; } ssize_t x; // printf("The process %d was give capabilities %s\n", // (int) parentPid, cap_to_text(caps, &x)); fflush(0); // Don't bother to free the memory... return 0; } muse-4.0.0/src/muse/globaldefs.h000066400000000000000000000063501404105311000164700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: globaldefs.h,v 1.3.2.1 2009/05/03 04:14:00 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __GLOBALDEFS_H__ #define __GLOBALDEFS_H__ namespace MusECore { // Midi Type // MT_GM - General Midi // MT_GS - Roland GS // MT_XG - Yamaha XG // MT_GM2 - General Midi Level 2 enum MType { MT_UNKNOWN=0, MT_GM, MT_GS, MT_XG, MT_GM2 }; enum AutomationType { AUTO_OFF, AUTO_READ, AUTO_TOUCH, AUTO_WRITE }; // Can be Or'd together. enum PluginFeature { PluginNoFeatures = 0x00, PluginFixedBlockSize = 0x01, PluginPowerOf2BlockSize = 0x02, PluginNoInPlaceProcessing = 0x04, PluginCoarseBlockSize = 0x08 }; typedef int PluginFeatures_t; enum VstPluginFlags { vstPluginNoFlags = 0, canSendVstEvents = 1 << 0, canSendVstMidiEvents = 1 << 1, canSendVstTimeInfo = 1 << 2, canReceiveVstEvents = 1 << 3, canReceiveVstMidiEvents = 1 << 4, canReceiveVstTimeInfo = 1 << 5, canProcessVstOffline = 1 << 6, canUseVstAsInsert = 1 << 7, canUseVstAsSend = 1 << 8, canMixVstDryWet = 1 << 9, canVstMidiProgramNames = 1 << 10 }; typedef int VstPluginFlags_t; // Absolute max number of plugins in mixer rack (if we ever want to increase PipelineDepth). // Used to determine the index where special blocks (dssi ladspa controls) appear in the list of controllers. // The special block(s) must appear AFTER any rack plugins, so we need this variable to help us // leave some room in case we ever want to increase the number of rack plugins. const int MAX_PLUGINS = 8; // plugins in mixer rack, max up to MAX_PLUGINS const int PipelineDepth = 8; // max audio channels const int MAX_CHANNELS = 2; // max Number of Midi Ports const int MIDI_PORTS = 200; // Midi channels per Port const int MUSE_MIDI_CHANNELS = 16; const double MIN_TEMPO_VAL = 20.0; const double MAX_TEMPO_VAL = 400.0; } // namespace MusECore namespace MusEGui { enum EditInstrumentTabType { EditInstrumentPatches=0, EditInstrumentDrumMaps=1, EditInstrumentControllers=2, EditInstrumentSysex=3, EditInstrumentInitSeq=4 }; enum class MidiEventColorMode { blueEvents, pitchColorEvents, velocityColorEvents, lastInList }; // The default amount of space before bar # 1 (or the start of a part). const int DefaultCanvasXOrigin = -16; } // namespace MusEGui #endif muse-4.0.0/src/muse/globals.cpp000066400000000000000000000367521404105311000163550ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: globals.cpp,v 1.15.2.11 2009/11/25 09:09:43 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include "globals.h" #include "config.h" namespace MusEGlobal { int sampleRate = 44100; unsigned segmentSize = 1024U; // segmentSize in frames (set by JACK) unsigned fifoLength = 128; // 131072/segmentSize // 131072 - magic number that gives a sufficient buffer size int segmentCount = 2; // NOTE: For now, this is TEMPORARILY set to the project sample rate during song loading, // then at the END of song loading is immediately set to the real current rate. // See comments in Song::read() at the "samplerate" tag section. int projectSampleRate = sampleRate; const int numAudioSampleRates = 8; const int selectableAudioSampleRates[] = { 22050, 32000, 44100, 48000, 64000, 88200, 96000, 192000 }; MusECore::SndFileList sndFiles; MusECore::AudioConverterPluginList audioConverterPluginList; // This global variable is a pointer so that we can replace it quickly with a new one in RT operations. MusECore::AudioConverterSettingsGroup* defaultAudioConverterSettings = nullptr; // denormal bias value used to eliminate the manifestation of denormals by // lifting the zero level slightly above zero // denormal problems occur when values get extremely close to zero const float denormalBias=1e-18; bool overrideAudioOutput = false; bool overrideAudioInput = false; const QString selectableAudioBackendDevices[] = { "Jack Audio (default)", "Midi only", "RtAudio Pulse Audio", "RtAudio ALSA", "RtAudio OSS - Open Sound System", "RtAudio best guess" }; const int numRtAudioDevices = 6; MusEGui::Rasterizer *globalRasterizer = nullptr; QTimer* heartBeatTimer; bool blinkTimerPhase = false; bool hIsB = true; // call note h "b" const signed char sharpTab[14][7] = { { 0, 3, -1, 2, 5, 1, 4 }, { 0, 3, -1, 2, 5, 1, 4 }, { 0, 3, -1, 2, 5, 1, 4 }, { 0, 3, -1, 2, 5, 1, 4 }, { 2, 5, 1, 4, 7, 3, 6 }, { 2, 5, 1, 4, 7, 3, 6 }, { 2, 5, 1, 4, 7, 3, 6 }, { 4, 0, 3, -1, 2, 5, 1 }, { 7, 3, 6, 2, 5, 1, 4 }, { 5, 8, 4, 7, 3, 6, 2 }, { 3, 6, 2, 5, 1, 4, 7 }, { 1, 4, 0, 3, 6, 2, 5 }, { 6, 2, 5, 1, 4, 0, 3 }, { 0, 3, -1, 2, 5, 1, 4 }, }; const signed char flatTab[14][7] = { { 4, 1, 5, 2, 6, 3, 7 }, { 4, 1, 5, 2, 6, 3, 7 }, { 4, 1, 5, 2, 6, 3, 7 }, { 4, 1, 5, 2, 6, 3, 7 }, { 6, 3, 7, 4, 8, 5, 9 }, { 6, 3, 7, 4, 8, 5, 9 }, { 6, 3, 7, 4, 8, 5, 9 }, { 1, 5, 2, 6, 3, 7, 4 }, { 4, 1, 5, 2, 6, 3, 7 }, { 2, 6, 3, 7, 4, 8, 5 }, { 7, 4, 1, 5, 2, 6, 3 }, { 5, 2, 6, 3, 7, 4, 8 }, { 3, 0, 4, 1, 5, 2, 6 }, { 4, 1, 5, 2, 6, 3, 7 }, }; QString museGlobalLib; QString museGlobalShare; QString museUser; QString museProject; QString museProjectInitPath("./"); QString configName; QString configPath; QString cachePath; QString museInstruments; QString museUserInstruments; QString lastWavePath("."); QString lastMidiPath("."); bool debugMode = false; bool unityWorkaround = false; bool debugMsg = false; bool heavyDebugMsg = false; bool midiInputTrace = false; bool midiOutputTrace = false; bool realTimeScheduling = false; int realTimePriority = 40; // 80 int midiRTPrioOverride = -1; bool loadPlugins = true; bool loadMESS = true; bool loadVST = true; bool loadNativeVST = true; bool loadDSSI = true; bool loadLV2 = true; bool usePythonBridge = false; QString pythonBridgePyroNSHostname; QString pythonBridgePyroNSPort; QString pythonBridgePyroDaemonHostname; QString pythonBridgePyroDaemonPort; float pythonBridgePyroCommTimeout = 5.0; bool useLASH = true; bool useAlsaWithJack = false; bool noAutoStartJack = false; bool populateMidiPortsOnStart = true; const char* midi_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "Midi (*.mid *.MID *.mid.gz *.mid.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; //FIXME: By T356 01/19/2010 // If saving as a compressed file (gz or bz2), // the file is a pipe, and pipes can't seek ! // This results in a corrupted midi file from MidiFile::writeTrack(). // So exporting compressed midi has simply been disabled here for now... // For re-enabling, add .mid.gz and .mid.bz2 and same for .kar again const char* midi_file_save_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Midi (*.mid)"), QT_TRANSLATE_NOOP("file_patterns", "Karaoke (*.kar)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* med_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar)"), QT_TRANSLATE_NOOP("file_patterns", "med Files (*.med *.med.gz *.med.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "Uncompressed med Files (*.med)"), QT_TRANSLATE_NOOP("file_patterns", "gzip compressed med Files (*.med.gz)"), QT_TRANSLATE_NOOP("file_patterns", "bzip2 compressed med Files (*.med.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* med_file_save_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Uncompressed med Files (*.med)"), QT_TRANSLATE_NOOP("file_patterns", "gzip compressed med Files (*.med.gz)"), QT_TRANSLATE_NOOP("file_patterns", "bzip2 compressed med Files (*.med.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* project_create_file_save_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Uncompressed med Files (*.med)"), QT_TRANSLATE_NOOP("file_patterns", "gzip compressed med Files (*.med.gz)"), QT_TRANSLATE_NOOP("file_patterns", "bzip2 compressed med Files (*.med.bz2)"), 0 }; const char* image_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "(*.jpg *.gif *.png)"), QT_TRANSLATE_NOOP("file_patterns", "(*.jpg)"), QT_TRANSLATE_NOOP("file_patterns", "(*.gif)"), QT_TRANSLATE_NOOP("file_patterns", "(*.png)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* part_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "part Files (*.mpt *.mpt.gz *.mpt.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* part_file_save_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "part Files (*.mpt)"), QT_TRANSLATE_NOOP("file_patterns", "gzip compressed part Files (*.mpt.gz)"), QT_TRANSLATE_NOOP("file_patterns", "bzip2 compressed part Files (*.mpt.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* preset_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Presets (*.pre *.pre.gz *.pre.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* preset_file_save_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Presets (*.pre)"), QT_TRANSLATE_NOOP("file_patterns", "gzip compressed presets (*.pre.gz)"), QT_TRANSLATE_NOOP("file_patterns", "bzip2 compressed presets (*.pre.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* drum_map_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Presets (*.map *.map.gz *.map.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* drum_map_file_save_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Presets (*.map)"), QT_TRANSLATE_NOOP("file_patterns", "gzip compressed presets (*.map.gz)"), QT_TRANSLATE_NOOP("file_patterns", "bzip2 compressed presets (*.map.bz2)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* audio_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Wave/Binary (*.wav *.ogg *.flac *.bin)"), QT_TRANSLATE_NOOP("file_patterns", "Wave (*.wav *.ogg *.flac)"), QT_TRANSLATE_NOOP("file_patterns", "Binary (*.bin)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* colors_config_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Color configuration files (*.cfc)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; const char* stylesheet_file_pattern[] = { QT_TRANSLATE_NOOP("file_patterns", "Qt style sheets (*.qss)"), QT_TRANSLATE_NOOP("file_patterns", "All Files (*)"), 0 }; Qt::KeyboardModifiers globalKeyState; // Midi Filter Parameter int midiInputPorts = 0; // receive from all devices int midiInputChannel = 0; // receive all channel int midiRecordType = 0; // receive all events int midiThruType = 0; // transmit all events int midiFilterCtrl1 = 0; int midiFilterCtrl2 = 0; int midiFilterCtrl3 = 0; int midiFilterCtrl4 = 0; QActionGroup* undoRedo; QAction* undoAction; QAction* redoAction; QActionGroup* transportAction; QAction* playAction; QAction* startAction; QAction* stopAction; QAction* rewindAction; QAction* forwardAction; QAction* loopAction; QAction* punchinAction; QAction* punchoutAction; QAction* recordAction; QAction* panicAction; QAction* metronomeAction; QAction* cpuLoadAction; MusEGui::MusE* muse = nullptr; MusECore::MetroAccentsPresetsMap metroAccentPresets; MusECore::MetronomeSettings metroGlobalSettings; MusECore::MetronomeSettings metroSongSettings; // Whether to use the global or song metronome settings. bool metroUseSongSettings = false; bool rcEnable = false; unsigned char rcStopNote = 28; unsigned char rcRecordNote = 31; unsigned char rcGotoLeftMarkNote = 33; unsigned char rcPlayNote = 29; unsigned char rcSteprecNote = 36; unsigned char rcForwardNote = 26; unsigned char rcBackwardNote = 24; bool rcEnableCC = false; unsigned char rcGotoLeftMarkCC = 111; unsigned char rcBackwardCC = 112; unsigned char rcForwardCC = 113; unsigned char rcStopCC = 114; unsigned char rcPlayCC = 115; //unsigned char rcInsertPauseCC = 116; unsigned char rcRecordCC = 117; // REMOVE Tim. automation. Remove this. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. bool automation = true; // Midi learn params. These will be initialized to -1 by any midi learn function, // and then filled by the midi engine in response to the drivers. int midiLearnPort = -1; int midiLearnChan = -1; int midiLearnCtrl = -1; const QString inputRoutingToolTipBase = QObject::tr("Input routing"); const QString noInputRoutingToolTipWarn = inputRoutingToolTipBase + QString("\n") + QObject::tr("Warning: No input routes! Click to connect..."); const QString outputRoutingToolTipBase = QObject::tr("Output routing"); const QString noOutputRoutingToolTipWarn = outputRoutingToolTipBase + QString("\n") + QObject::tr("Warning: No output routes! Click to connect..."); // REMOVE Tim. setuid. Removed. //uid_t euid, ruid; // effective user id, real user id bool midiSeqRunning = false; QString defaultStyle = "Fusion"; //--------------------------------------------------------- // convertFrame4ProjectSampleRate //--------------------------------------------------------- unsigned convertFrame4ProjectSampleRate(unsigned frame) { return double(frame) * double(MusEGlobal::sampleRate) / double(MusEGlobal::projectSampleRate); } //--------------------------------------------------------- // doSetuid // Restore the effective UID to its original value. //--------------------------------------------------------- void doSetuid() { // BUG REMOVE Tim. setuid. Removed. Ancient stuff no longer required? // Tests OK under several scenarios: Jack started externally, // Jack started by MusE, Jack started as root, dummy driver etc. // Was causing crash with new DrumGizmo 0.9.11 // #ifndef RTCAP // int status; // #ifdef _POSIX_SAVED_IDS // status = seteuid (euid); // #else // status = setreuid (ruid, euid); // #endif // if (status < 0) { // perror("doSetuid: Couldn't set uid"); // } // #endif } //--------------------------------------------------------- // undoSetuid // Set the effective UID to the real UID. //--------------------------------------------------------- void undoSetuid() { // BUG REMOVE Tim. setuid. Removed. Ancient stuff no longer required? // Tests OK under several scenarios: Jack started externally, // Jack started by MusE, Jack started as root, dummy driver etc. // Was causing crash with new DrumGizmo 0.9.11 // #ifndef RTCAP // int status; // // #ifdef _POSIX_SAVED_IDS // status = seteuid (ruid); // #else // status = setreuid (euid, ruid); // #endif // if (status < 0) { // fprintf(stderr, "undoSetuid: Couldn't set uid (eff:%d,real:%d): %s\n", // euid, ruid, strerror(errno)); // exit (status); // } // #endif } //--------------------------------------------------------- // getUniqueTmpfileName //--------------------------------------------------------- bool getUniqueTmpfileName(QString subDir, QString ext, QString& newFilename) { // Check if tmp-directory exists under project path QString tmpInDir = museProject + "/" + subDir; QDir absDir(tmpInDir); tmpInDir = absDir.cleanPath(absDir.absolutePath()); QFileInfo tmpdirfi(tmpInDir); if (!tmpdirfi.isDir()) { // Try to create a tmpdir QDir projdir(museProject); if (!projdir.mkdir(subDir)) { printf("Could not create tmp dir %s!\n", tmpInDir.toLatin1().data() ); return false; } } tmpdirfi.setFile(tmpInDir); if (!tmpdirfi.isWritable()) { printf("Temp directory is not writable - aborting\n"); return false; } QDir tmpdir = tmpdirfi.dir(); // Find a new filename for (int i=0; i<10000; i++) { QString filename = "muse_tmp"; filename.append(QString::number(i)); if (!ext.startsWith(".")) filename.append("."); filename.append(ext); if (!tmpdir.exists(tmpInDir +"/" + filename)) { newFilename = tmpInDir + "/" + filename; if (debugMsg) printf("returning temporary filename %s\n", newFilename.toLatin1().data()); return true; } } printf("Could not find a suitable tmpfilename (more than 10000 tmpfiles in tmpdir - clean up!\n"); return false; } } // namespace MusEGlobal muse-4.0.0/src/muse/globals.h000066400000000000000000000161241404105311000160110ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: globals.h,v 1.10.2.11 2009/11/25 09:09:43 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef GLOBALS_H #define GLOBALS_H #include #include #include #include #include #include #include "value.h" #include "mtc.h" #include "metronome_class.h" #include "wave.h" #include "audio_convert/audio_converter_plugin.h" #include "audio_convert/audio_converter_settings_group.h" #include "rasterizer.h" #include namespace MusEGui { class MusE; } namespace MusEGlobal { extern const float denormalBias; extern int sampleRate; extern unsigned segmentSize; extern unsigned fifoLength; // inversely proportional to segmentSize extern int segmentCount; extern int projectSampleRate; extern const int numAudioSampleRates; extern const int selectableAudioSampleRates[]; extern MusECore::SndFileList sndFiles; extern MusECore::AudioConverterPluginList audioConverterPluginList; // This global variable is a pointer so that we can replace it quickly with a new one in RT operations. extern MusECore::AudioConverterSettingsGroup* defaultAudioConverterSettings; extern bool overrideAudioOutput; extern bool overrideAudioInput; extern const QString selectableAudioBackendDevices[]; extern const int numRtAudioDevices; enum SelectableAudioBackendDevices { JackAudio = 0, DummyAudio = 1, RtAudioPulse = 2, RtAudioAlsa = 3, RtAudioOss = 4, RtAudioChoice = 5 //RtAudioJack, -- it's just stupid to keep this option }; extern QTimer* heartBeatTimer; extern MusEGui::Rasterizer *globalRasterizer; extern bool blinkTimerPhase; extern bool hIsB; extern const signed char sharpTab[14][7]; extern const signed char flatTab[14][7]; extern QString museGlobalLib; extern QString museGlobalShare; extern QString museUser; extern QString museProject; extern QString museProjectInitPath; extern QString configName; extern QString configPath; extern QString museInstruments; extern QString museUserInstruments; extern QString cachePath; extern QString lastWavePath; extern QString lastMidiPath; extern bool debugMode; extern bool midiInputTrace; extern bool midiOutputTrace; extern bool unityWorkaround; extern bool debugMsg; extern bool heavyDebugMsg; extern bool debugSync; extern bool loadPlugins; extern bool loadMESS; extern bool loadVST; extern bool loadNativeVST; extern bool loadDSSI; extern bool usePythonBridge; extern QString pythonBridgePyroNSHostname; extern QString pythonBridgePyroNSPort; extern QString pythonBridgePyroDaemonHostname; extern QString pythonBridgePyroDaemonPort; extern float pythonBridgePyroCommTimeout; extern bool useLASH; extern bool loadLV2; extern bool useAlsaWithJack; extern bool noAutoStartJack; extern bool populateMidiPortsOnStart; extern bool realTimeScheduling; extern int realTimePriority; extern int midiRTPrioOverride; extern const char* midi_file_pattern[]; extern const char* midi_file_save_pattern[]; extern const char* med_file_pattern[]; extern const char* med_file_save_pattern[]; extern const char* project_create_file_save_pattern[]; extern const char* image_file_pattern[]; extern const char* part_file_pattern[]; extern const char* part_file_save_pattern[]; extern const char* preset_file_pattern[]; extern const char* preset_file_save_pattern[]; extern const char* drum_map_file_pattern[]; extern const char* drum_map_file_save_pattern[]; extern const char* audio_file_pattern[]; extern const char* colors_config_file_pattern[]; //extern const char* stylesheet_file_pattern[]; extern Qt::KeyboardModifiers globalKeyState; extern int midiInputPorts; //!< receive from all devices extern int midiInputChannel; //!< receive all channel extern int midiRecordType; //!< receive all events #define MIDI_FILTER_NOTEON 1 #define MIDI_FILTER_POLYP 2 #define MIDI_FILTER_CTRL 4 #define MIDI_FILTER_PROGRAM 8 #define MIDI_FILTER_AT 16 #define MIDI_FILTER_PITCH 32 #define MIDI_FILTER_SYSEX 64 extern int midiThruType; // transmit all events extern int midiFilterCtrl1; extern int midiFilterCtrl2; extern int midiFilterCtrl3; extern int midiFilterCtrl4; #define CMD_RANGE_ALL 0 #define CMD_RANGE_SELECTED 1 #define CMD_RANGE_LOOP 2 extern QActionGroup* undoRedo; extern QAction* undoAction; extern QAction* redoAction; extern QActionGroup* transportAction; extern QAction* playAction; extern QAction* startAction; extern QAction* stopAction; extern QAction* rewindAction; extern QAction* forwardAction; extern QAction* loopAction; extern QAction* punchinAction; extern QAction* punchoutAction; extern QAction* recordAction; extern QAction* panicAction; extern QAction* metronomeAction; extern QAction* cpuLoadAction; extern MusEGui::MusE* muse; extern MusECore::MetroAccentsPresetsMap metroAccentPresets; extern MusECore::MetronomeSettings metroGlobalSettings; extern MusECore::MetronomeSettings metroSongSettings; // Whether to use the global or song metronome settings. extern bool metroUseSongSettings; extern bool rcEnable; extern unsigned char rcStopNote; extern unsigned char rcRecordNote; extern unsigned char rcGotoLeftMarkNote; extern unsigned char rcPlayNote; extern unsigned char rcSteprecNote; extern unsigned char rcForwardNote; extern unsigned char rcBackwardNote; extern bool rcEnableCC; extern unsigned char rcGotoLeftMarkCC; extern unsigned char rcBackwardCC; extern unsigned char rcForwardCC; extern unsigned char rcStopCC; extern unsigned char rcPlayCC; //extern unsigned char rcInsertPauseCC; extern unsigned char rcRecordCC; extern int midiLearnPort; extern int midiLearnChan; extern int midiLearnCtrl; extern bool midiSeqRunning; extern bool automation; extern const QString inputRoutingToolTipBase; extern const QString outputRoutingToolTipBase; extern const QString noInputRoutingToolTipWarn; extern const QString noOutputRoutingToolTipWarn; #define JACK_MIDI_OUT_PORT_SUFFIX "_out" #define JACK_MIDI_IN_PORT_SUFFIX "_in" // REMOVE Tim. setuid. Removed. // extern uid_t euid, ruid; extern void doSetuid(); extern void undoSetuid(); extern bool checkAudioDevice(); extern bool getUniqueTmpfileName(QString subDir, QString ext, QString& newFilename); extern unsigned convertFrame4ProjectSampleRate(unsigned frame); extern QString defaultStyle; } // namespace MusEGlobal #endif muse-4.0.0/src/muse/gui.h000066400000000000000000000022671404105311000151550ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // $Id: ./muse/gui.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //============================================================================= #ifndef __GUI_H__ #define __GUI_H__ // // GUI constants // central point of tweaking the gui // //static const int ICON_WIDTH = 18; //static const QSize ICON_SIZE(ICON_WIDTH, ICON_WIDTH); #endif muse-4.0.0/src/muse/help.cpp000066400000000000000000000127401404105311000156510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: help.cpp,v 1.7.2.4 2009/07/05 23:06:21 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; 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 //#include #include "app.h" #include "globals.h" #include "gconfig.h" #include "icons.h" #include "aboutbox_impl.h" // Whether to open the pdf or the html //#define MUSE_USE_PDF_HELP_FILE namespace MusEGui { //--------------------------------------------------------- // startHelpBrowser //--------------------------------------------------------- void MusE::startHelpBrowser() { QWidget* w = QApplication::widgetAt (QCursor::pos()); // qDebug() << "Debug F1 help: Widget at mouse position:" << w << w->objectName(); // setting object name for action toolbutton doesn't work, use a workaround QToolButton* tb = nullptr; if (w && strcmp(w->metaObject()->className(), "QToolButton") == 0) { tb = dynamic_cast(w); // qDebug() << "Debug F1 help: Tool button action:" << tb << tb->defaultAction()->objectName(); } QString museManual; if (w && w->objectName() == "PartCanvas") museManual = QString("https://github.com/muse-sequencer/muse/wiki/usage-of-the-editors"); else if (w && w->objectName() == "Pianoroll") museManual = QString("https://github.com/muse-sequencer/muse/wiki/usage-of-the-editors"); else if (w && w->objectName() == "DrumCanvas") museManual = QString("https://github.com/muse-sequencer/muse/wiki/usage-of-the-editors"); else if (w && w->objectName() == "WaveCanvas") museManual = QString("https://github.com/muse-sequencer/muse/wiki/usage-of-the-editors"); else if (w && w->objectName() == "TrackList") museManual = QString("https://github.com/muse-sequencer/muse/wiki/Documentation#tracks-and-parts"); else if (w && w->objectName() == "EffectRack") museManual = QString("https://github.com/muse-sequencer/muse/wiki/understanding-the-effects-rack"); else if (w && w->objectName() == "SoloButton") museManual = QString("https://github.com/muse-sequencer/muse/wiki/Documentation#track-soloing"); else if (w && (w->objectName() == "InputRouteButton" || w->objectName() == "OutputRouteButton")) museManual = QString("https://github.com/muse-sequencer/muse/wiki/Documentation#routes"); else if (w && (w->objectName() == "AudioAutoType")) museManual = QString("https://github.com/muse-sequencer/muse/wiki/Documentation#audio-automation"); else if (w && tb && tb->defaultAction()->objectName() == "PanicButton") museManual = QString("https://github.com/muse-sequencer/muse/wiki/Documentation#panic-local-off-reset-instrument-and-init-instrument"); else if (w && tb && tb->defaultAction()->objectName() == "MetronomeButton") museManual = QString("https://github.com/muse-sequencer/muse/wiki/metronome"); else museManual = QString("https://github.com/muse-sequencer/muse/wiki/Documentation"); launchBrowser(museManual); } //--------------------------------------------------------- // startHelpBrowser //--------------------------------------------------------- void MusE::startHomepageBrowser() { QString museHome = QString("https://muse-sequencer.github.io"); launchBrowser(museHome); } //--------------------------------------------------------- // startBugBrowser //--------------------------------------------------------- void MusE::startBugBrowser() { QString museBugPage("https://github.com/muse-sequencer/muse/issues"); launchBrowser(museBugPage); } //--------------------------------------------------------- // about //--------------------------------------------------------- void MusE::about() { MusEGui::AboutBoxImpl ab; ab.show(); ab.exec(); } //--------------------------------------------------------- // aboutQt //--------------------------------------------------------- void MusE::aboutQt() { QMessageBox::aboutQt(this, QString("MusE")); } //--------------------------------------------------------- // launchBrowser //--------------------------------------------------------- void MusE::launchBrowser(QString &whereTo) { if (! QDesktopServices::openUrl(QUrl(whereTo))) { QMessageBox::information(this, tr("Unable to launch browser"), tr("Error launching default browser"), QMessageBox::Ok); printf("Unable to launch browser\n"); } } } // namespace MusEGui muse-4.0.0/src/muse/helper.cpp000066400000000000000000002201241404105311000161750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: helper.cpp,v 1.1.1.1 2003/10/27 18:51:27 wschweer Exp $ // (C) Copyright 2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "helper.h" #include "song.h" #include "app.h" #include "icons.h" #include "synth.h" #include "functions.h" #include "operations.h" #include "gconfig.h" #include "driver/jackmidi.h" #include "route.h" #include "mididev.h" #include "midiport.h" #include "globaldefs.h" #include "globals.h" #include "audio.h" #include "audiodev.h" #include "midi_consts.h" #include "midiseq.h" #include "midictrl.h" #include "menutitleitem.h" #include "dssihost.h" #include "lv2host.h" #include "vst_native.h" #include "appearance.h" #include "event.h" #include "popupmenu.h" #include "mpevent.h" #include "track.h" #include "part.h" #include "drummap.h" #include "xml.h" #include "conf.h" #include "synthdialog.h" #include "shortcuts.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include using std::set; namespace MusEGlobal { extern bool hIsB; } namespace MusECore { static const char* vall[] = { "c","c#","d","d#","e","f","f#","g","g#","a","a#","h" }; static const char* valu[] = { "C","C#","D","D#","E","F","F#","G","G#","A","A#","H" }; //--------------------------------------------------------- // pitch2string //--------------------------------------------------------- QString pitch2string(int v) { if (v < 0 || v > 127) return QString("----"); int octave = (v / 12) - 2; QString o = QString::number(octave); int i = v % 12; QString s(octave < 0 ? valu[i] : vall[i]); if (MusEGlobal::hIsB) { if (s == "h") s = "b"; else if (s == "H") s = "B"; } return s + o; } //--------------------------------------------------------- // string2pitch //--------------------------------------------------------- int string2pitch(const QString &s) { if (validatePitch(s) != QValidator::Acceptable) return 0; QString p; int oct = 0; if (s.length() == 4) { p = s.left(2); oct = s.mid(2, 2).toInt(); } else if (s.length() == 3) { if (s.at(1) == '#') { p = s.left(2); oct = s.mid(2, 1).toInt(); } else { p = s.left(1); oct = s.mid(1, 2).toInt(); } } else { p = s.left(1); oct = s.mid(1, 1).toInt(); } int cnt = 0; for (const auto& it : vall) { if (QString::compare(QString(it), p, Qt::CaseInsensitive) == 0) break; cnt++; } return (oct + 2) * 12 + cnt; } QValidator::State validatePitch(const QString &s) { static const QRegularExpression regExp("\\A[A-H]#?-[12]|[a-h]#?[0-8]\\z"); Q_ASSERT(regExp.isValid()); const QRegularExpressionMatch match = regExp.match(s, 0, QRegularExpression::PartialPreferCompleteMatch); if (match.hasMatch()) return QValidator::Acceptable; else if (match.hasPartialMatch()) return QValidator::Intermediate; else return QValidator::Invalid; } //--------------------------------------------------------- // dumpMPEvent //--------------------------------------------------------- void dumpMPEvent(const MEvent* ev) { fprintf(stderr, "time:%d port:%d chan:%d ", ev->time(), ev->port(), ev->channel()+1); if (ev->type() == ME_NOTEON) { QString s = pitch2string(ev->dataA()); fprintf(stderr, "NoteOn %s(0x%x) %d\n", s.toLatin1().constData(), ev->dataA(), ev->dataB()); } else if (ev->type() == ME_NOTEOFF) { QString s = pitch2string(ev->dataA()); fprintf(stderr, "NoteOff %s(0x%x) %d\n", s.toLatin1().constData(), ev->dataA(), ev->dataB()); } else if (ev->type() == ME_SYSEX) { fprintf(stderr, "SysEx len %d 0x%0x ...\n", ev->len(), ev->constData()[0]); } else fprintf(stderr, "type:0x%02x a=%d b=%d\n", ev->type(), ev->dataA(), ev->dataB()); } #if 0 // ------------------------------------------------------------------------------------------------------- // enumerateJackMidiDevices() // This version creates separate devices for Jack midi input and outputs. // It does not attempt to pair them together. // ------------------------------------------------------------------------------------------------------- void enumerateJackMidiDevices() { if(!MusEGlobal::checkAudioDevice()) return; MidiDevice* dev = 0; PendingOperationList operations; // If Jack is running. if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO) { char good_name[ROUTE_PERSISTENT_NAME_SIZE]; std::list sl; // sl = MusEGlobal::audioDevice->inputPorts(true, 1); // Ask for second aliases. sl = MusEGlobal::audioDevice->inputPorts(true); for(std::list::iterator i = sl.begin(); i != sl.end(); ++i) { QByteArray ba = (*i).toLatin1(); const char* port_name = ba.constData(); void* const port = MusEGlobal::audioDevice->findPort(port_name); if(port) { //dev = MidiJackDevice::createJackMidiDevice(*i, 1); dev = MidiJackDevice::createJackMidiDevice(QString(), 1); // Let it pick the name if(dev) { // Get a good routing name. MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE); const Route dstRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, good_name); // Persistent route. // If audio is running, this calls jack_connect() and waits for the audio thread to execute addRoute(). // If audio is not running, this directly executes addRoute(), bypassing the audio messaging system, // and jack_connect() is not called. //MusEGlobal::audio->msgAddRoute(srcRoute, dstRoute); // // We only want to add the route, not call jack_connect - jack may not have been activated yet. // If it has been, we should be calling our graph changed handler soon, it will handle actual connections. // If audio is not running yet, this directly executes addRoute(), bypassing the audio messaging system, if(!dev->outRoutes()->contains(dstRoute)) operations.add(MusECore::PendingOperationItem(dev->outRoutes(), dstRoute, MusECore::PendingOperationItem::AddRouteNode)); } } } //sl = MusEGlobal::audioDevice->outputPorts(true, 1); // Ask for second aliases. sl = MusEGlobal::audioDevice->outputPorts(true); for(std::list::iterator i = sl.begin(); i != sl.end(); ++i) { QByteArray ba = (*i).toLatin1(); const char* port_name = ba.constData(); void* const port = MusEGlobal::audioDevice->findPort(port_name); if(port) { dev = MidiJackDevice::createJackMidiDevice(QString(), 2); // Let it pick the name if(dev) { // Get a good routing name. MusEGlobal::audioDevice->portName(port, good_name, ROUTE_PERSISTENT_NAME_SIZE); const Route srcRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, good_name); // Persistent route. if(!dev->inRoutes()->contains(srcRoute)) operations.add(MusECore::PendingOperationItem(dev->inRoutes(), srcRoute, MusECore::PendingOperationItem::AddRouteNode)); } } } } if(!operations.empty()) { //operations.add(MusECore::PendingOperationItem((TrackList*)NULL, PendingOperationItem::UpdateSoloStates)); MusEGlobal::audio->msgExecutePendingOperations(operations); // Don't update here. //MusEGlobal::song->update(SC_ROUTE); } } #else // ------------------------------------------------------------------------------------------------------- // enumerateJackMidiDevices() // This version attempts to pair together Jack midi input and outputs into single MidiDevices, // similar to how ALSA presents pairs of inputs and outputs. // ------------------------------------------------------------------------------------------------------- void enumerateJackMidiDevices() { if(!MusEGlobal::checkAudioDevice()) return; PendingOperationList operations; // If Jack is running. if(MusEGlobal::audioDevice->deviceType() == AudioDevice::JACK_AUDIO) { MidiDevice* dev = 0; char w_good_name[ROUTE_PERSISTENT_NAME_SIZE]; char r_good_name[ROUTE_PERSISTENT_NAME_SIZE]; std::list wsl; std::list rsl; wsl = MusEGlobal::audioDevice->inputPorts(true); rsl = MusEGlobal::audioDevice->outputPorts(true); for(std::list::iterator wi = wsl.begin(); wi != wsl.end(); ++wi) { QByteArray w_ba = (*wi).toLatin1(); const char* w_port_name = w_ba.constData(); bool match_found = false; void* const w_port = MusEGlobal::audioDevice->findPort(w_port_name); if(w_port) { // Get a good routing name. MusEGlobal::audioDevice->portName(w_port, w_good_name, ROUTE_PERSISTENT_NAME_SIZE); for(std::list::iterator ri = rsl.begin(); ri != rsl.end(); ++ri) { QByteArray r_ba = (*ri).toLatin1(); const char* r_port_name = r_ba.constData(); void* const r_port = MusEGlobal::audioDevice->findPort(r_port_name); if(r_port) { // Get a good routing name. MusEGlobal::audioDevice->portName(r_port, r_good_name, ROUTE_PERSISTENT_NAME_SIZE); const size_t w_sz = strlen(w_good_name); const size_t r_sz = strlen(r_good_name); size_t start_c = 0; size_t w_end_c = w_sz; size_t r_end_c = r_sz; while(start_c < w_sz && start_c < r_sz && w_good_name[start_c] == r_good_name[start_c]) ++start_c; while(w_end_c > 0 && r_end_c > 0) { if(w_good_name[w_end_c - 1] != r_good_name[r_end_c - 1]) break; --w_end_c; --r_end_c; } if(w_end_c > start_c && r_end_c > start_c) { const char* w_str = w_good_name + start_c; const char* r_str = r_good_name + start_c; const size_t w_len = w_end_c - start_c; const size_t r_len = r_end_c - start_c; // Do we have a matching pair? if((w_len == 7 && r_len == 8 && strncasecmp(w_str, "capture", w_len) == 0 && strncasecmp(r_str, "playback", r_len) == 0) || (w_len == 8 && r_len == 7 && strncasecmp(w_str, "playback", w_len) == 0 && strncasecmp(r_str, "capture", r_len) == 0) || (w_len == 5 && r_len == 6 && strncasecmp(w_str, "input", w_len) == 0 && strncasecmp(r_str, "output", r_len) == 0) || (w_len == 6 && r_len == 5 && strncasecmp(w_str, "output", w_len) == 0 && strncasecmp(r_str, "input", r_len) == 0) || (w_len == 2 && r_len == 3 && strncasecmp(w_str, "in", w_len) == 0 && strncasecmp(r_str, "out", r_len) == 0) || (w_len == 3 && r_len == 2 && strncasecmp(w_str, "out", w_len) == 0 && strncasecmp(r_str, "in", r_len) == 0) || (w_len == 1 && r_len == 1 && strncasecmp(w_str, "p", w_len) == 0 && strncasecmp(r_str, "c", r_len) == 0) || (w_len == 1 && r_len == 1 && strncasecmp(w_str, "c", w_len) == 0 && strncasecmp(r_str, "p", r_len) == 0)) { dev = MidiJackDevice::createJackMidiDevice(QString(), 3); // Let it pick the name if(dev) { const Route srcRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, r_good_name); // Persistent route. const Route dstRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, w_good_name); // Persistent route. // We only want to add the route, not call jack_connect - jack may not have been activated yet. // If it has been, we should be calling our graph changed handler soon, it will handle actual connections. // If audio is not running yet, this directly executes addRoute(), bypassing the audio messaging system, if(!dev->inRoutes()->contains(srcRoute)) operations.add(MusECore::PendingOperationItem(dev->inRoutes(), srcRoute, MusECore::PendingOperationItem::AddRouteNode)); if(!dev->outRoutes()->contains(dstRoute)) operations.add(MusECore::PendingOperationItem(dev->outRoutes(), dstRoute, MusECore::PendingOperationItem::AddRouteNode)); } rsl.erase(ri); // Done with this read port. Remove. match_found = true; break; } } } } } if(!match_found) { // No match was found. Create a single writeable device. dev = MidiJackDevice::createJackMidiDevice(QString(), 1); // Let it pick the name if(dev) { const Route dstRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, w_good_name); // Persistent route. // We only want to add the route, not call jack_connect - jack may not have been activated yet. // If it has been, we should be calling our graph changed handler soon, it will handle actual connections. // If audio is not running yet, this directly executes addRoute(), bypassing the audio messaging system, if(!dev->outRoutes()->contains(dstRoute)) operations.add(MusECore::PendingOperationItem(dev->outRoutes(), dstRoute, MusECore::PendingOperationItem::AddRouteNode)); } } } // Create the remaining readable ports as single readable devices. for(std::list::iterator ri = rsl.begin(); ri != rsl.end(); ++ri) { dev = MidiJackDevice::createJackMidiDevice(QString(), 2); // Let it pick the name if(dev) { QByteArray r_ba = (*ri).toLatin1(); const char* r_port_name = r_ba.constData(); void* const r_port = MusEGlobal::audioDevice->findPort(r_port_name); if(r_port) { // Get a good routing name. MusEGlobal::audioDevice->portName(r_port, r_good_name, ROUTE_PERSISTENT_NAME_SIZE); const Route srcRoute(Route::JACK_ROUTE, -1, NULL, -1, -1, -1, r_good_name); // Persistent route. if(!dev->inRoutes()->contains(srcRoute)) operations.add(MusECore::PendingOperationItem(dev->inRoutes(), srcRoute, MusECore::PendingOperationItem::AddRouteNode)); } } } } if(!operations.empty()) { //operations.add(MusECore::PendingOperationItem((TrackList*)NULL, PendingOperationItem::UpdateSoloStates)); MusEGlobal::audio->msgExecutePendingOperations(operations); // Don't update here. //MusEGlobal::song->update(SC_ROUTE); } } #endif // enumerateJackMidiDevices // ------------------------------------------------------------------------------------------------------- // populateMidiPorts() // Attempts to auto-populate midi ports with found devices. // ------------------------------------------------------------------------------------------------------- void populateMidiPorts() { if(!MusEGlobal::checkAudioDevice()) return; MusECore::MidiDevice* dev = 0; int port_num = 0; int jack_midis_found = 0; bool def_in_found = false; // bool def_out_found = false; // If Jack is running, prefer Jack midi devices over ALSA. if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::JACK_AUDIO) { for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { dev = *i; if(dev) { ++jack_midis_found; MidiPort* mp = &MusEGlobal::midiPorts[port_num]; MusEGlobal::audio->msgSetMidiDevice(mp, dev); // robert: removing the default init on several places to allow for the case // where you rather want the midi track to default to the last created port // this can only happen if there is _no_ default set // // Global function initMidiPorts() already sets defs to port #1, but this will override. // if(!def_out_found && dev->rwFlags() & 0x1) // { // mp->setDefaultOutChannels(1); // def_out_found = true; // } // else mp->setDefaultOutChannels(0); if(!def_in_found && dev->rwFlags() & 0x2) { mp->setDefaultInChannels(1); def_in_found = true; } else mp->setDefaultInChannels(0); if(++port_num == MusECore::MIDI_PORTS) return; } } } //else // If Jack is not running, use ALSA devices. // Try to do the user a favour: If we still have no Jack devices, even if Jack is running, fill with ALSA. // It is possible user has Jack running on ALSA back-end but without midi support. // IE. They use Jack for audio but use ALSA for midi! // If unwanted, remove "|| jack_midis_found == 0". if(MusEGlobal::audioDevice->deviceType() == MusECore::AudioDevice::DUMMY_AUDIO || jack_midis_found == 0) { for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { if((*i)->deviceType() != MusECore::MidiDevice::ALSA_MIDI) continue; dev = *i; MidiPort* mp = &MusEGlobal::midiPorts[port_num]; MusEGlobal::audio->msgSetMidiDevice(mp, dev); // robert: removing the default init on several places to allow for the case // where you rather want the midi track to default to the last created port // this can only happen if there is _no_ default set // // Global function initMidiPorts() already sets defs to port #1, but this will override. // if(!def_out_found && dev->rwFlags() & 0x1) // { // mp->setDefaultOutChannels(1); // def_out_found = true; // } // else mp->setDefaultOutChannels(0); if(!def_in_found && dev->rwFlags() & 0x2) { mp->setDefaultInChannels(1); def_in_found = true; } else mp->setDefaultInChannels(0); if(++port_num == MusECore::MIDI_PORTS) return; } } } Part* partFromSerialNumber(int serial) { TrackList* tl = MusEGlobal::song->tracks(); for (iTrack it = tl->begin(); it != tl->end(); ++it) { PartList* pl = (*it)->parts(); iPart ip; for (ip = pl->begin(); ip != pl->end(); ++ip) if (ip->second->sn() == serial) return ip->second; } printf("ERROR: partFromSerialNumber(%i) wasn't able to find an appropriate part!\n",serial); return NULL; } bool any_event_selected(const set& parts, bool in_range, RelevantSelectedEvents_t relevant) { return !get_events(parts, in_range ? 3 : 1, relevant).empty(); } bool drummaps_almost_equal(const DrumMap* one, const DrumMap* two, int len) { for (int i=0; i parts_at_tick(unsigned tick) { using MusEGlobal::song; QSet tmp; for (iTrack it=song->tracks()->begin(); it!=song->tracks()->end(); it++) tmp.insert(*it); return parts_at_tick(tick, tmp); } QSet parts_at_tick(unsigned tick, Track* track) { QSet tmp; tmp.insert(track); return parts_at_tick(tick, tmp); } QSet parts_at_tick(unsigned tick, const QSet& tracks) { QSet result; for (QSet::const_iterator it=tracks.begin(); it!=tracks.end(); it++) { Track* track=*it; for (iPart p_it=track->parts()->begin(); p_it!=track->parts()->end(); p_it++) if (tick >= p_it->second->tick() && tick <= p_it->second->endTick()) result.insert(p_it->second); } return result; } bool parse_range(const QString& str, int* from, int* to) { int idx = str.indexOf("-"); if (idx<0) // no "-" in str { bool ok; int i = str.toInt(&ok); if (!ok) { *from=-1; *to=-1; return false; } else { *from=i; *to=i; return true; } } else // there is a "-" in str { QString str1=str.mid(0,idx); QString str2=str.mid(idx+1); bool ok; int i = str1.toInt(&ok); if (!ok) { *from=-1; *to=-1; return false; } else { *from=i; i = str2.toInt(&ok); if (!ok) { *from=-1; *to=-1; return false; } else { *to=i; return true; } } } } void write_new_style_drummap(int level, Xml& xml, const char* tagname, DrumMap* drummap, bool full) { xml.tag(level++, tagname); for (int i=0;i<128;i++) { DrumMap* dm = &drummap[i]; const DrumMap* idm = &iNewDrumMap[i]; if ( (dm->name != idm->name) || (dm->vol != idm->vol) || (dm->quant != idm->quant) || (dm->len != idm->len) || (dm->lv1 != idm->lv1) || (dm->lv2 != idm->lv2) || (dm->lv3 != idm->lv3) || (dm->lv4 != idm->lv4) || (dm->enote != idm->enote) || (dm->mute != idm->mute) || (dm->port != idm->port) || (dm->channel != idm->channel) || (dm->anote != idm->anote) || (dm->hide != idm->hide) || full) { xml.tag(level++, "entry pitch=\"%d\"", i); // when any of these "if"s changes, also update the large "if" // above (this scope's parent) if (full || dm->name != idm->name) xml.strTag(level, "name", dm->name); if (full || dm->vol != idm->vol) xml.intTag(level, "vol", dm->vol); if (full || dm->quant != idm->quant) xml.intTag(level, "quant", dm->quant); if (full || dm->len != idm->len) xml.intTag(level, "len", dm->len); if (full || dm->channel != idm->channel) xml.intTag(level, "channel", dm->channel); if (full || dm->port != idm->port) xml.intTag(level, "port", dm->port); if (full || dm->lv1 != idm->lv1) xml.intTag(level, "lv1", dm->lv1); if (full || dm->lv2 != idm->lv2) xml.intTag(level, "lv2", dm->lv2); if (full || dm->lv3 != idm->lv3) xml.intTag(level, "lv3", dm->lv3); if (full || dm->lv4 != idm->lv4) xml.intTag(level, "lv4", dm->lv4); if (full || dm->enote != idm->enote) xml.intTag(level, "enote", dm->enote); if (full || dm->anote != idm->anote) xml.intTag(level, "anote", dm->anote); if (full || dm->mute != idm->mute) xml.intTag(level, "mute", dm->mute); if (full || dm->hide != idm->hide) xml.intTag(level, "hide", dm->hide); xml.tag(--level, "/entry"); } } xml.etag(level, tagname); } void read_new_style_drummap(Xml& xml, const char* tagname, DrumMap* drummap, bool compatibility) { for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; const QString& tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "entry") // then read that entry with a nested loop { DrumMap* dm=NULL; DrumMap temporaryMap; for (;;) // nested loop { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto end_of_nested_for; case Xml::Attribut: if (tag == "pitch") { int pitch = xml.s2().toInt() & 0x7f; if (pitch < 0 || pitch > 127) printf("ERROR: THIS SHOULD NEVER HAPPEN: invalid pitch in read_new_style_drummap()!\n"); else { dm = &drummap[pitch]; } } break; case Xml::TagStart: if (dm==NULL && compatibility == false) printf("ERROR: THIS SHOULD NEVER HAPPEN: no valid 'pitch' attribute in tag, but sub-tags follow in read_new_style_drummap()!\n"); else if (dm ==NULL && compatibility == true) { dm = &temporaryMap; } if (tag == "name") dm->name = xml.parse(QString("name")); else if (tag == "vol") dm->vol = (unsigned char)xml.parseInt(); else if (tag == "quant") dm->quant = xml.parseInt(); else if (tag == "len") dm->len = xml.parseInt(); else if (tag == "channel") dm->channel = xml.parseInt(); else if (tag == "port") dm->port = xml.parseInt(); else if (tag == "lv1") dm->lv1 = xml.parseInt(); else if (tag == "lv2") dm->lv2 = xml.parseInt(); else if (tag == "lv3") dm->lv3 = xml.parseInt(); else if (tag == "lv4") dm->lv4 = xml.parseInt(); else if (tag == "enote") { dm->enote = xml.parseInt(); if (compatibility) { int pitch = temporaryMap.enote; drummap[pitch] = temporaryMap; dm = &drummap[pitch]; dm->anote = pitch; } } else if (tag == "anote") dm->anote = xml.parseInt(); else if (tag == "mute") dm->mute = xml.parseInt(); else if (tag == "hide") dm->hide = xml.parseInt(); else xml.unknown("read_new_style_drummap"); break; case Xml::TagEnd: if (tag == "entry") goto end_of_nested_for; default: break; } } // end of nested loop end_of_nested_for: ; } // end of 'if (tag == "entry")' else xml.unknown("read_new_style_drummap"); break; case Xml::TagEnd: if (tag == tagname) return; default: break; } } } int readDrummapsEntryPatchCollection(Xml& xml) { int hbank = (CTRL_PROGRAM_VAL_DONT_CARE >> 16) & 0xff; int lbank = (CTRL_PROGRAM_VAL_DONT_CARE >> 8) & 0xff; int prog = CTRL_PROGRAM_VAL_DONT_CARE & 0xff; int last_prog, last_hbank, last_lbank; // OBSOLETE. Not used. for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto read_end; case Xml::TagStart: xml.unknown("readDrummapsEntryPatchCollection"); break; case Xml::Attribut: // last_prog, last_hbank, last_lbank are OBSOLETE. Not used. if (tag == "prog") parse_range(xml.s2(), &prog, &last_prog); else if (tag == "lbank") parse_range(xml.s2(), &lbank, &last_lbank); else if (tag == "hbank") parse_range(xml.s2(), &hbank, &last_hbank); break; case Xml::TagEnd: if (tag == "patch_collection") return ((hbank & 0xff) << 16) | ((lbank & 0xff) << 8) | (prog & 0xff); default: break; } } read_end: fprintf(stderr, "ERROR: End or Error in readDrummapsEntryPatchCollection()!\n"); return CTRL_VAL_UNKNOWN; // an invalid collection } void record_controller_change_and_maybe_send(unsigned tick, int ctrl_num, int val, MidiTrack* mt) { MusECore::Event a(MusECore::Controller); a.setTick(tick); a.setA(ctrl_num); a.setB(val); MusEGlobal::song->recordEvent(mt, a); if (MusEGlobal::song->cpos() < mt->getControllerValueLifetime(tick, ctrl_num)) { // this CC has an immediate effect? so send it out to the device. MusECore::MidiPlayEvent ev(0, mt->outPort(), mt->outChannel(), MusECore::ME_CONTROLLER, ctrl_num, val); MusEGlobal::audio->msgPlayMidiEvent(&ev); } } } // namespace MusECore namespace MusEGui { //--------------------------------------------------------- // midiPortsPopup //--------------------------------------------------------- QMenu* midiPortsPopup(QWidget* parent, int checkPort, bool includeDefaultEntry) { QMenu* p = new QMenu(parent); QMenu* subp = nullptr; QAction *act = nullptr; QString name; const int openConfigId = MusECore::MIDI_PORTS; const int defaultId = MusECore::MIDI_PORTS + 1; // Warn if no devices available. Add an item to open midi config. int pi = 0; for( ; pi < MusECore::MIDI_PORTS; ++pi) { MusECore::MidiDevice* md = MusEGlobal::midiPorts[pi].device(); if(md && (md->rwFlags() & 1)) break; } if(pi == MusECore::MIDI_PORTS) { act = p->addAction(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Warning: No output devices!"))); act->setCheckable(false); act->setData(-1); p->addSeparator(); } act = p->addAction(*MusEGui::ankerSVGIcon, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "MIDI Ports/Soft Synths..."))); act->setCheckable(false); act->setData(openConfigId); p->addSeparator(); p->addAction(new MusEGui::MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Output Port/Device")), p)); p->addSeparator(); if(includeDefaultEntry) { act = p->addAction(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "default"))); act->setCheckable(false); act->setData(defaultId); } QVector alsa_list; QVector jack_list; QVector synth_list; QVector *cur_list = nullptr; QVector unused_list; for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; MusECore::MidiDevice* md = port->device(); if(!md) { unused_list.push_back(i); continue; } // Make deleted audio softsynths not show in select dialog if(md->isSynti()) { MusECore::AudioTrack *_track = static_cast(static_cast(md)); MusECore::TrackList* tl = MusEGlobal::song->tracks(); if(tl->find(_track) == tl->end()) continue; } // Only writeable ports, or current one. if(!(md->rwFlags() & 1) && (i != checkPort)) continue; switch(md->deviceType()) { case MusECore::MidiDevice::ALSA_MIDI: alsa_list.push_back(i); break; case MusECore::MidiDevice::JACK_MIDI: jack_list.push_back(i); break; case MusECore::MidiDevice::SYNTH_MIDI: synth_list.push_back(i); break; } } // Order the entire listing by device type. for(int dtype = 0; dtype <= MusECore::MidiDevice::SYNTH_MIDI; ++dtype) { switch(dtype) { case MusECore::MidiDevice::ALSA_MIDI: if(!alsa_list.isEmpty()) p->addAction(new MusEGui::MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "ALSA")), p)); cur_list = &alsa_list; break; case MusECore::MidiDevice::JACK_MIDI: if(!jack_list.isEmpty()) p->addAction(new MusEGui::MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "JACK")), p)); cur_list = &jack_list; break; case MusECore::MidiDevice::SYNTH_MIDI: if(!synth_list.isEmpty()) p->addAction(new MusEGui::MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Synth")), p)); cur_list = &synth_list; break; } if(cur_list->isEmpty()) continue; int row = 0; int sz = cur_list->size(); for (int i = 0; i < sz; ++i) { const int port = cur_list->at(i); if(port < 0 || port >= MusECore::MIDI_PORTS) continue; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; name = QString("%1:%2") .arg(port + 1) .arg(mp->portname()); act = p->addAction(name); act->setData(port); act->setCheckable(true); act->setChecked(port == checkPort); ++row; } } int sz = unused_list.size(); if(sz > 0) { p->addSeparator(); for (int i = 0; i < sz; ++i) { const int port = unused_list.at(i); // No submenu yet? Create it now. if(!subp) { subp = new QMenu(p); subp->setTitle(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Empty Ports"))); } act = subp->addAction(QString().setNum(port + 1)); act->setData(port); act->setCheckable(true); act->setChecked(port == checkPort); } } if(subp) p->addMenu(subp); return p; } //--------------------------------------------------------- // midiPortsPopupMenu //--------------------------------------------------------- void midiPortsPopupMenu(MusECore::Track* t, int x, int y, bool allClassPorts, const QWidget* widget, bool includeDefaultEntry) { switch(t->type()) { case MusECore::Track::MIDI: case MusECore::Track::DRUM: case MusECore::Track::AUDIO_SOFTSYNTH: { MusECore::MidiTrack* track = nullptr; MusECore::SynthI* synthi = nullptr; int potential_new_port_no=-1; int port = -1; if(t->isSynthTrack()) { // Cast as SynthI which inherits MidiDevice. synthi = static_cast(t); if(!synthi) return; port = synthi->midiPort(); } else { track = static_cast(t); port = track->outPort(); } // NOTE: If parent is given, causes accelerators to be returned in QAction::text() ! QMenu* p = MusEGui::midiPortsPopup(nullptr, port, includeDefaultEntry); // find first free port number // do not permit numbers already used in other tracks! // except if it's only used in this track. int no; for (no=0;nomidis()->begin(); it!=MusEGlobal::song->midis()->end(); ++it) { MusECore::MidiTrack* mt=*it; if (mt!=t && mt->outPort()==no) break; } if (it == MusEGlobal::song->midis()->end()) break; // TODO Ports which are used by synths ?? } if (no==MusECore::MIDI_PORTS) { delete p; printf("THIS IS VERY UNLIKELY TO HAPPEN: no free midi ports! you have used all %i!\n",MusECore::MIDI_PORTS); break; } potential_new_port_no=no; // Do not include unused devices if the track is a synth track. if(!synthi) { typedef std::map asmap; typedef std::map::iterator imap; asmap mapALSA; asmap mapJACK; asmap mapSYNTH; int aix = 0x10000000; int jix = 0x20000000; int six = 0x30000000; for(MusECore::iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { // don't add devices which are used somewhere if((*i)->midiPort() >= 0 && (*i)->midiPort() < MusECore::MIDI_PORTS) continue; switch((*i)->deviceType()) { case MusECore::MidiDevice::ALSA_MIDI: mapALSA.insert(std::pair ((*i)->name().toStdString(), aix)); ++aix; break; case MusECore::MidiDevice::JACK_MIDI: mapJACK.insert(std::pair ((*i)->name().toStdString(), jix)); ++jix; break; case MusECore::MidiDevice::SYNTH_MIDI: mapSYNTH.insert(std::pair ((*i)->name().toStdString(), six)); ++six; break; } } if (!mapALSA.empty() || !mapJACK.empty() || !mapSYNTH.empty()) { QMenu* pup = p->addMenu(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Unused Devices"))); QAction* act; if (!mapALSA.empty()) { pup->addAction(new MusEGui::MenuTitleItem("ALSA", pup)); for(imap i = mapALSA.begin(); i != mapALSA.end(); ++i) { int idx = i->second; QString s(i->first.c_str()); MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::ALSA_MIDI); if(md) { if(md->deviceType() != MusECore::MidiDevice::ALSA_MIDI) continue; act = pup->addAction(md->name()); act->setData(idx); } } } if (!mapJACK.empty()) { pup->addAction(new MusEGui::MenuTitleItem("JACK", pup)); for(imap i = mapJACK.begin(); i != mapJACK.end(); ++i) { int idx = i->second; QString s(i->first.c_str()); MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::JACK_MIDI); if(md) { if(md->deviceType() != MusECore::MidiDevice::JACK_MIDI) continue; act = pup->addAction(md->name()); act->setData(idx); } } } if (!mapSYNTH.empty()) { pup->addAction(new MusEGui::MenuTitleItem("Synth", pup)); for(imap i = mapSYNTH.begin(); i != mapSYNTH.end(); ++i) { int idx = i->second; QString s(i->first.c_str()); MusECore::MidiDevice* md = MusEGlobal::midiDevices.find(s, MusECore::MidiDevice::SYNTH_MIDI); if(md) { if(md->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) continue; act = pup->addAction(md->name()); act->setData(idx); } } } } } QAction* act = widget ? p->exec(widget->mapToGlobal(QPoint(x, y)), nullptr) : p->exec(); if(!act) { delete p; break; } QString acttext=act->text(); int n = act->data().toInt(); delete p; if(n < 0) // Invalid item. break; if(n == MusECore::MIDI_PORTS) // Show port config dialog. { MusEGlobal::muse->configMidiPorts(); break; } else if (n >= 0x10000000) { // Error, should not happen. if(synthi) break; int typ; if (n < 0x20000000) typ = MusECore::MidiDevice::ALSA_MIDI; else if (n < 0x30000000) typ = MusECore::MidiDevice::JACK_MIDI; else typ = MusECore::MidiDevice::SYNTH_MIDI; MusECore::MidiDevice* sdev = MusEGlobal::midiDevices.find(acttext, typ); MusEGlobal::audio->msgSetMidiDevice(&MusEGlobal::midiPorts[potential_new_port_no], sdev); n=potential_new_port_no; MusEGlobal::song->update(); } MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; MusEGlobal::audio->msgIdle(true); // In the case of synths, multiple synths cannot be assigned to the same port. if(synthi || (!allClassPorts && !t->selected())) { if(synthi) { MusEGlobal::audio->msgSetMidiDevice(&MusEGlobal::midiPorts[n], synthi); MusEGlobal::song->update(); changed |= MusECore::MidiTrack::PortChanged; } else if(track) { if(n != track->outPort()) changed |= track->setOutPortAndUpdate(n, false); } } else { if(track) { for(const auto& mt : *MusEGlobal::song->midis()) { if (allClassPorts && (mt->type() != track->type())) continue; if(n != mt->outPort() && (allClassPorts || mt->selected())) changed |= mt->setOutPortAndUpdate(n, false); } } } MusEGlobal::audio->msgIdle(false); MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); // Prompt and send init sequences. MusEGlobal::audio->msgInitMidiDevices(false); } break; case MusECore::Track::WAVE: case MusECore::Track::AUDIO_OUTPUT: case MusECore::Track::AUDIO_INPUT: case MusECore::Track::AUDIO_GROUP: case MusECore::Track::AUDIO_AUX: //TODO break; } } //--------------------------------------------------------- // populateAddSynth //--------------------------------------------------------- QMenu* populateAddSynth(QWidget* parent) { QMenu* synp = new PopupMenu(parent); typedef std::multimap asmap; typedef std::multimap::iterator imap; const int ntypes = MusECore::Synth::SYNTH_TYPE_END; asmap smaps[ntypes]; PopupMenu* mmaps[ntypes]; for(int itype = 0; itype < ntypes; ++itype) mmaps[itype] = nullptr; MusECore::Synth* synth; MusECore::Synth::Type type; // QVector favActions; QMap favActions; int ii = 0; for(std::vector::iterator i = MusEGlobal::synthis.begin(); i != MusEGlobal::synthis.end(); ++i) { synth = *i; type = synth->synthType(); // dssi-vst is dead, really no point in keeping this case around //#ifdef DSSI_SUPPORT // if (type == MusECore::Synth::DSSI_SYNTH && ((MusECore::DssiSynth*)synth)->isDssiVst() ) // Place Wine VSTs in a separate sub menu // type = MusECore::Synth::VST_SYNTH; //#endif if(type >= ntypes) continue; smaps[type].insert( std::pair (synth->description().toLower().toStdString(), ii) ); ++ii; } int sz = MusEGlobal::synthis.size(); for(int itype = 0; itype < ntypes; ++itype) { for(imap i = smaps[itype].begin(); i != smaps[itype].end(); ++i) { int idx = i->second; if(idx > sz) // Sanity check continue; synth = MusEGlobal::synthis[idx]; if(synth) { // No sub-menu yet? Create it now. if(!mmaps[itype]) { mmaps[itype] = new PopupMenu(parent); mmaps[itype]->setToolTipsVisible(true); mmaps[itype]->setIcon(*synthSVGIcon); mmaps[itype]->setTitle(MusECore::synthType2String((MusECore::Synth::Type)itype)); synp->addMenu(mmaps[itype]); } //QAction* act = mmaps[itype]->addAction(synth->description() + " <" + synth->name() + ">"); QAction* act = mmaps[itype]->addAction(synth->description()); act->setData( MENU_ADD_SYNTH_ID_BASE * (itype + 1) + idx ); if(!synth->uri().isEmpty()) act->setToolTip(synth->uri()); if (SynthDialog::isFav(synth)) favActions.insert(synth->description().toLower(), act); } } } if (!favActions.isEmpty()) { QAction *fa = synp->actions().at(0); synp->insertAction(fa, new MusEGui::MenuTitleItem("Favorites", synp)); for (const auto& it : favActions) synp->insertAction(fa, it); synp->insertAction(fa, new MusEGui::MenuTitleItem("All", synp)); } return synp; } //--------------------------------------------------------- // populateAddTrack // this is also used in "mixer" //--------------------------------------------------------- QActionGroup* populateAddTrack(QMenu* addTrack, bool populateAll, bool insert, bool addHeader) { QActionGroup* grp = new QActionGroup(addTrack); if (MusEGlobal::config.addHiddenTracks) populateAll=true; if (addHeader) addTrack->addAction(new MusEGui::MenuTitleItem(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Add Track")), addTrack)); if (populateAll || MusECore::MidiTrack::visible()) { QAction* midi = addTrack->addAction(*pianorollSVGIcon, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Midi Track"))); midi->setData(MusECore::Track::MIDI); midi->setShortcut(shortcuts[insert ? SHRT_INSERT_MIDI_TRACK : SHRT_ADD_MIDI_TRACK].key); grp->addAction(midi); QAction* drum = addTrack->addAction(*drumeditSVGIcon, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Drum Track"))); drum->setData(MusECore::Track::DRUM); drum->setShortcut(shortcuts[insert ? SHRT_INSERT_DRUM_TRACK : SHRT_ADD_DRUM_TRACK].key); grp->addAction(drum); } if (populateAll || MusECore::WaveTrack::visible()) { QAction* wave = addTrack->addAction(*waveeditorSVGIcon, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Wave Track"))); wave->setData(MusECore::Track::WAVE); wave->setShortcut(shortcuts[insert ? SHRT_INSERT_WAVE_TRACK : SHRT_ADD_WAVE_TRACK].key); grp->addAction(wave); } if (populateAll || MusECore::AudioOutput::visible()) { QAction* aoutput = addTrack->addAction(*trackOutputSVGIcon, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Audio Output"))); aoutput->setData(MusECore::Track::AUDIO_OUTPUT); aoutput->setShortcut(shortcuts[insert ? SHRT_INSERT_AUDIO_OUTPUT : SHRT_ADD_AUDIO_OUTPUT].key); grp->addAction(aoutput); } if (populateAll || MusECore::AudioGroup::visible()) { QAction* agroup = addTrack->addAction(*trackGroupVGIcon, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Audio Group"))); agroup->setData(MusECore::Track::AUDIO_GROUP); agroup->setShortcut(shortcuts[insert ? SHRT_INSERT_AUDIO_GROUP : SHRT_ADD_AUDIO_GROUP].key); grp->addAction(agroup); } if (populateAll || MusECore::AudioInput::visible()) { QAction* ainput = addTrack->addAction(*trackInputSVGIcon, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Audio Input"))); ainput->setData(MusECore::Track::AUDIO_INPUT); ainput->setShortcut(shortcuts[insert ? SHRT_INSERT_AUDIO_INPUT : SHRT_ADD_AUDIO_INPUT].key); grp->addAction(ainput); } if (populateAll || MusECore::AudioAux::visible()) { QAction* aaux = addTrack->addAction(*trackAuxSVGIcon, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Aux Send"))); aaux->setData(MusECore::Track::AUDIO_AUX); aaux->setShortcut(shortcuts[insert ? SHRT_INSERT_AUDIO_AUX : SHRT_ADD_AUDIO_AUX].key); grp->addAction(aaux); } if (populateAll || MusECore::SynthI::visible()) { addTrack->addSeparator(); QAction *asynthd = addTrack->addAction(*synthSVGIcon, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Synth (Show Dialog)..."))); asynthd->setData(MusECore::Track::AUDIO_SOFTSYNTH); asynthd->setShortcut(shortcuts[insert ? SHRT_INSERT_SYNTH_TRACK : SHRT_ADD_SYNTH_TRACK].key); grp->addAction(asynthd); // Create a sub-menu and fill it with found synth types. Make addTrack the owner. QMenu* synp = populateAddSynth(addTrack); synp->setTitle(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Synth"))); // Add the sub-menu to the given menu. addTrack->addMenu(synp); } return grp; } //--------------------------------------------------------- // getFilterExtension //--------------------------------------------------------- QString getFilterExtension(const QString &filter) { // Return the first extension found. Must contain at least one * character. int pos = filter.indexOf('*'); if(pos == -1) return QString(); QString filt; int len = filter.length(); ++pos; for( ; pos < len; ++pos) { QChar c = filter[pos]; if((c == ')') || (c == ';') || (c == ',') || (c == ' ')) break; filt += filter[pos]; } return filt; } QStringList localizedStringListFromCharArray(const char** array, const char* context) { QStringList temp; for (int i=0;array[i];i++) temp << qApp->translate(context, array[i]); return temp; } QString browseProjectFolder(QWidget* parent) { QString path; if(!MusEGlobal::config.projectBaseFolder.isEmpty()) { QDir d(MusEGlobal::config.projectBaseFolder); path = d.absolutePath(); } QString dir = QFileDialog::getExistingDirectory(parent, qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Select project directory")), path); if(dir.isEmpty()) dir = MusEGlobal::config.projectBaseFolder; return dir; } QString projectTitleFromFilename(QString filename) { int idx; idx = filename.lastIndexOf(".med.bz2", -1, Qt::CaseInsensitive); if(idx == -1) idx = filename.lastIndexOf(".med.gz", -1, Qt::CaseInsensitive); if(idx == -1) idx = filename.lastIndexOf(".med", -1, Qt::CaseInsensitive); if(idx != -1) filename.truncate(idx); QFileInfo fi(filename); return fi.fileName(); } QString projectPathFromFilename(QString filename) { QFileInfo fi(filename); return QDir::cleanPath(fi.absolutePath()); } QString projectExtensionFromFilename(QString filename) { int idx; idx = filename.lastIndexOf(".med.bz2", -1, Qt::CaseInsensitive); if(idx == -1) idx = filename.lastIndexOf(".med.gz", -1, Qt::CaseInsensitive); if(idx == -1) idx = filename.lastIndexOf(".med", -1, Qt::CaseInsensitive); if(idx == -1) idx = filename.lastIndexOf(".bz2", -1, Qt::CaseInsensitive); if(idx == -1) idx = filename.lastIndexOf(".gz", -1, Qt::CaseInsensitive); return (idx == -1) ? QString() : filename.right(filename.size() - idx); } QString getUniqueUntitledName() { QString filename("untitled"); QString fbase(MusEGlobal::config.projectBaseFolder); QString nfb = fbase; if(MusEGlobal::config.projectStoreInFolder) nfb += "/" + filename; QFileInfo fi(nfb + "/" + filename + ".med"); // TODO p4.0.40 Check other extensions. if(!fi.exists()) return fi.filePath(); // Find a new filename QString nfn = filename; int idx; for (idx=2; idx<10000; idx++) { QString num = QString::number(idx); nfn = filename + "_" + num; nfb = fbase; if(MusEGlobal::config.projectStoreInFolder) nfb += "/" + nfn; QFileInfo fi(nfb + "/" + nfn + ".med"); if(!fi.exists()) return fi.filePath(); } printf("MusE error: Could not make untitled project name (10000 or more untitled projects in project dir - clean up!\n"); nfb = fbase; if(MusEGlobal::config.projectStoreInFolder) nfb += "/" + filename; return nfb + "/" + filename + ".med"; } struct CI { int num; QString s; bool used; bool off; bool instrument; CI(int n, const QString& ss, bool u, bool o, bool i) : num(n), s(ss), used(u), off(o), instrument(i) {} }; //--------------------------------------------------- // populateMidiCtrlMenu // Returns estimated width of the completed menu. //--------------------------------------------------- int populateMidiCtrlMenu(PopupMenu* menu, MusECore::PartList* part_list, MusECore::Part* cur_part, int curDrumPitch) { //--------------------------------------------------- // build list of midi controllers for current // MusECore::MidiPort/channel //--------------------------------------------------- MusECore::MidiTrack* track = (MusECore::MidiTrack*)(cur_part->track()); int channel = track->outChannel(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; bool isNewDrum = track->type() == MusECore::Track::DRUM; bool isMidi = track->type() == MusECore::Track::MIDI; MusECore::MidiInstrument* instr = port->instrument(); MusECore::MidiCtrlValListList* cll = port->controller(); const int min = channel << 24; const int max = min + 0x1000000; const int edit_ins = max + 3; const int velo = max + 0x101; int est_width = 0; const int patch = port->hwCtrlState(channel, MusECore::CTRL_PROGRAM); std::list sList; typedef std::list::iterator isList; std::set already_added_nums; for (MusECore::iMidiCtrlValList it = cll->lower_bound(min); it != cll->lower_bound(max); ++it) { MusECore::MidiCtrlValList* cl = it->second; MusECore::MidiController* c = port->midiController(cl->num(), channel); bool isDrumCtrl = (c->isPerNoteController()); int show = c->showInTracks(); int cnum = c->num(); int num = cl->num(); if (isDrumCtrl) { // Only show controller for current pitch: if (isNewDrum) { if ((curDrumPitch < 0) || ((num & 0xff) != track->drummap()[curDrumPitch].anote)) continue; } else if (isMidi) { if ((curDrumPitch < 0) || ((num & 0xff) != curDrumPitch)) // FINDMICH does this work? continue; } else continue; } isList i = sList.begin(); for (; i != sList.end(); ++i) { if (i->num == num) break; } if (i == sList.end()) { bool used = false; for (MusECore::iPart ip = part_list->begin(); ip != part_list->end(); ++ip) { const MusECore::EventList& el = ip->second->events(); for (MusECore::ciEvent ie = el.begin(); ie != el.end(); ++ie) { const MusECore::Event& e = ie->second; if(e.type() != MusECore::Controller) continue; int ctl_num = e.dataA(); // Is it a drum controller event, according to the track port's instrument? MusECore::MidiController *mc = port->drumController(ctl_num); if(mc) { if((ctl_num & 0xff) != curDrumPitch) continue; if(isNewDrum) ctl_num = (ctl_num & ~0xff) | track->drummap()[ctl_num & 0x7f].anote; } if(ctl_num == num) { used = true; break; } } if (used) break; } bool off = cl->hwVal() == MusECore::CTRL_VAL_UNKNOWN; // Does it have a value or is it 'off'? // Filter if not used and off. But if there's something there, we must show it. if(!used && off && (((isDrumCtrl || isNewDrum) && !(show & MusECore::MidiController::ShowInDrum)) || (isMidi && !(show & MusECore::MidiController::ShowInMidi)))) continue; const bool isinstr = instr->findController(cnum, channel, patch) != nullptr; // Need to distinguish between global default controllers and // instrument defined controllers. Instrument takes priority over global // ie they 'overtake' definition of a global controller such that the // global def is no longer available. sList.push_back(CI(num, isinstr ? MusECore::midiCtrlNumString(cnum, true) + c->name() : MusECore::midiCtrlName(cnum, true), used, off, isinstr)); already_added_nums.insert(num); } } QString stext = QWidget::tr("Instrument-defined"); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int fmw = menu->fontMetrics().horizontalAdvance(stext); #else int fmw = menu->fontMetrics().width(stext); #endif if(fmw > est_width) est_width = fmw; menu->addAction(new MenuTitleItem(stext, menu)); // Don't allow editing instrument if it's a synth if(!port->device() || port->device()->deviceType() != MusECore::MidiDevice::SYNTH_MIDI) { stext = QWidget::tr("Edit Instrument..."); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 fmw = menu->fontMetrics().horizontalAdvance(stext); #else fmw = menu->fontMetrics().width(stext); #endif if(fmw > est_width) est_width = fmw; menu->addAction(*editInstrumentSVGIcon, QWidget::tr("Edit Instrument..."))->setData(edit_ins); menu->addSeparator(); } // // populate popup with all controllers available for // current instrument // stext = QWidget::tr("Add"); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 fmw = menu->fontMetrics().horizontalAdvance(stext); #else fmw = menu->fontMetrics().width(stext); #endif if(fmw > est_width) est_width = fmw; PopupMenu * ctrlSubPop = new PopupMenu(stext, menu, true); // true = enable stay open MusECore::MidiControllerList* mcl = new MusECore::MidiControllerList(); instr->getControllers(mcl, channel, patch); for (MusECore::iMidiController ci = mcl->begin(); ci != mcl->end(); ++ci) { int show = ci->second->showInTracks(); if((isNewDrum && !(show & MusECore::MidiController::ShowInDrum)) || (isMidi && !(show & MusECore::MidiController::ShowInMidi))) continue; int cnum = ci->second->num(); int num = cnum; if(ci->second->isPerNoteController()) { if (isNewDrum && curDrumPitch >= 0) num = (cnum & ~0xff) | track->drummap()[curDrumPitch].anote; else if (isMidi && curDrumPitch >= 0) num = (cnum & ~0xff) | curDrumPitch; //FINDMICH does this work? else continue; } // If it's not already in the parent menu... if(cll->find(channel, num) == cll->end()) { ctrlSubPop->addAction(MusECore::midiCtrlNumString(cnum, true) + ci->second->name())->setData(num); already_added_nums.insert(num); //cnum); } } delete mcl; menu->addMenu(ctrlSubPop); menu->addSeparator(); // Add instrument-defined controllers: for (isList i = sList.begin(); i != sList.end(); ++i) { if(!i->instrument) continue; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 fmw = menu->fontMetrics().horizontalAdvance(i->s); #else fmw = menu->fontMetrics().width(i->s); #endif if(fmw > est_width) est_width = fmw; if (i->used && !i->off) menu->addAction(*ledYellowSVGIcon, i->s)->setData(i->num); else if (i->used) menu->addAction(*ledGreenSVGIcon, i->s)->setData(i->num); else if(!i->off) menu->addAction(*ledBlueSVGIcon, i->s)->setData(i->num); else menu->addAction(i->s)->setData(i->num); } stext = QWidget::tr("Others"); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 fmw = menu->fontMetrics().horizontalAdvance(stext); #else fmw = menu->fontMetrics().width(stext); #endif if(fmw > est_width) est_width = fmw; menu->addAction(new MenuTitleItem(stext, menu)); // Add a.k.a. Common Controls not found in instrument: stext = QWidget::tr("Common Controls"); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 fmw = menu->fontMetrics().horizontalAdvance(stext); #else fmw = menu->fontMetrics().width(stext); #endif if(fmw > est_width) est_width = fmw; PopupMenu* ccSubPop = new PopupMenu(stext, menu, true); // true = enable stay open for(int num = 0; num < 128; ++num) // If it's not already in the parent menu... if(already_added_nums.find(num) == already_added_nums.end()) ccSubPop->addAction(MusECore::midiCtrlName(num, true))->setData(num); menu->addMenu(ccSubPop); menu->addSeparator(); // Add the special case velocity: stext = QWidget::tr("Velocity"); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 fmw = menu->fontMetrics().horizontalAdvance(stext); #else fmw = menu->fontMetrics().width(stext); #endif if(fmw > est_width) est_width = fmw; menu->addAction(stext)->setData(velo); // Add global default controllers (all controllers not found in instrument). for (isList i = sList.begin(); i != sList.end(); ++i) { if(i->instrument) continue; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 fmw = menu->fontMetrics().horizontalAdvance(i->s); #else fmw = menu->fontMetrics().width(i->s); #endif if(fmw > est_width) est_width = fmw; if (i->used && !i->off) menu->addAction(*ledYellowSVGIcon, i->s)->setData(i->num); else if (i->used) menu->addAction(*ledGreenSVGIcon, i->s)->setData(i->num); else if(!i->off) menu->addAction(*ledBlueSVGIcon, i->s)->setData(i->num); else menu->addAction(i->s)->setData(i->num); } est_width += 60; // Add about 60 for the coloured lights on the left. return est_width; } //--------------------------------------------------- // openSynthGui //--------------------------------------------------- void openSynthGui(MusECore::Track* t) { //auto curTrack = MusEGlobal::muse->arranger()->curTrack(); MusECore::SynthI* synth = nullptr; if (t->isMidiTrack()) { int oPort = ((MusECore::MidiTrack*)t)->outPort(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[oPort]; if (port->device() && port->device()->isSynti()) synth = static_cast(port->device()); } else if (t->isSynthTrack()) { synth = static_cast(t); } else { return; } if (!synth || !synth->synth()) return; if (synth->hasNativeGui()) { synth->showNativeGui(!synth->nativeGuiVisible()); } else if (synth->hasGui()) { synth->showGui(!synth->guiVisible()); } } //--------------------------------------------------- // clipQLine //--------------------------------------------------- QLine clipQLine(int x1, int y1, int x2, int y2, const QRect& rect) { const int rect_x = rect.x(); const int rect_y = rect.y(); const int rect_right = rect_x + rect.width(); const int rect_bot = rect_y + rect.height(); if(x1 < rect_x) { if(x2 < rect_x) return QLine(); x1 = rect_x; } else if(x1 > rect_right) { if(x2 > rect_right) return QLine(); x1 = rect_right; } if(x2 < rect_x) x2 = rect_x; else if(x2 > rect_right) x2 = rect_right; if(y1 < rect_y) { if(y2 < rect_y) return QLine(); y1 = rect_y; } else if(y1 > rect_bot) { if(y2 > rect_bot) return QLine(); y1 = rect_bot; } if(y2 < rect_y) y2 = rect_y; if(y2 > rect_bot) y2 = rect_bot; return QLine(x1, y1, x2, y2); } QRect normalizeQRect(const QRect& rect) { int x = rect.x(); int y = rect.y(); int w = rect.width(); int h = rect.height(); if(w < 0) { x += w; w = -w; } if(h < 0) { y += h; h = -h; } return QRect(x, y, w, h); } //--------------------------------------------------------- // loadQtStyle //--------------------------------------------------------- //void loadQtStyle(const QString& style) //{ // // Style sheets take priority over styles, and actually // // reset the style object name to empty when set. // const QString curStyle(qApp->style()->objectName()); // QStringList styleList = QStyleFactory::keys(); // if (styleList.indexOf(style) == -1) { // if (MusEGlobal::debugMsg) // printf("Application Qt style does not exist, setting default.\n"); // // To find the name of the current style, use objectName(). // if (curStyle.compare(Appearance::getSetDefaultStyle(), Qt::CaseInsensitive) != 0) // { // qApp->setStyle(Appearance::getSetDefaultStyle()); // if (MusEGlobal::debugMsg) // { // fprintf(stderr, "loadQtStyle: Setting app style to default: %s\n", Appearance::getSetDefaultStyle().toLatin1().constData()); // fprintf(stderr, " App style is now: %s\n", qApp->style()->objectName().toLatin1().constData()); // } // // No style object name? It will happen when a stylesheet is active. // // Give it a name. NOTE: The object names always seem to be lower case while // // the style factory key names are not. // if (qApp->style()->objectName().isEmpty()) // { // qApp->style()->setObjectName(Appearance::getSetDefaultStyle().toLower()); // if (MusEGlobal::debugMsg) // fprintf(stderr, " Setting empty style object name. App style is now: %s\n", qApp->style()->objectName().toLatin1().constData()); // } // } // } // else if (curStyle.compare(style, Qt::CaseInsensitive) != 0) // { // qApp->setStyle(style); // // Do the style again to fix a bug where the arranger is non-responsive. // if(MusEGlobal::debugMsg) // { // fprintf(stderr, "loadTheme setting app style to: %s\n", style.toLatin1().constData()); // fprintf(stderr, " app style is now: %s\n", qApp->style()->objectName().toLatin1().constData()); // } // // No style object name? It will happen when a stylesheet is active. // // Give it a name. NOTE: The object names always seem to be lower case while // // the style factory key names are not. // if(qApp->style()->objectName().isEmpty()) // { // qApp->style()->setObjectName(style.toLower()); // if(MusEGlobal::debugMsg) // fprintf(stderr, " Setting empty style object name. App style is now: %s\n", qApp->style()->objectName().toLatin1().constData()); // } // } //} //--------------------------------------------------------- // loadTheme //--------------------------------------------------------- void loadTheme(const QString& theme) { if (theme.isEmpty()) return; if(MusEGlobal::debugMsg) fprintf(stderr, "loadTheme: %s\n", theme.toLatin1().constData()); QString stylePathUser = MusEGlobal::configPath + "/themes/" + theme + ".qss"; QString stylePathDef = MusEGlobal::museGlobalShare + "/themes/" + theme + ".qss"; QFile fdef(stylePathDef); if (!fdef.open(QIODevice::ReadOnly)) { printf("loading style sheet <%s> failed\n", qPrintable(theme)); return; } QByteArray sdef = fdef.readAll(); fdef.close(); QByteArray suser; if (QFile::exists(stylePathUser)) { QFile fuser(stylePathUser); if (fuser.open(QIODevice::ReadOnly)) { suser = fuser.readAll(); } else { printf("loading style sheet <%s> failed\n", qPrintable(theme)); } fuser.close(); } QString sheet; if (suser.isEmpty()) { sheet = QString::fromUtf8(sdef.data()); } else { if (MusEGlobal::config.cascadeStylesheets) sheet = QString::fromUtf8(sdef.data()) + '\n' + QString::fromUtf8(suser.data()); else sheet = QString::fromUtf8(suser.data()); } // QTBUG-78238, QTBUG-80506 etc. #if QT_VERSION < QT_VERSION_CHECK(5, 12, 6) if (theme == "Dark Flat" || theme == "Deep Ocean") sheet += "QMenu::item { padding-left: 26px; }"; #elif QT_VERSION < QT_VERSION_CHECK(5, 14, 1) // if (theme == "Dark Flat" || theme == "Deep Ocean") sheet += "QMenu#CheckmarkOnly::item { padding-left: 26px; }"; #endif qApp->setStyleSheet(sheet); loadThemeColors(theme); } //--------------------------------------------------------- // loadThemeColors //--------------------------------------------------------- void loadThemeColors(const QString& theme) { if (MusEGlobal::debugMsg) fprintf(stderr, "loadThemeColors: %s\n", theme.toLatin1().constData()); QString configColorPath = MusEGlobal::configPath + "/themes/" + theme + ".cfc"; if (!QFile::exists(configColorPath)) { configColorPath = MusEGlobal::museGlobalShare + "/themes/" + theme + ".cfc"; } MusECore::readConfiguration(qPrintable(configColorPath)); } } // namespace MusEGui muse-4.0.0/src/muse/helper.h000066400000000000000000000103451404105311000156440ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: helper.h,v 1.1.1.1 2003/10/27 18:52:11 wschweer Exp $ // (C) Copyright 2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __HELPER_H__ #define __HELPER_H__ #include #include #include #include #include #include #include #include "type_defs.h" class QActionGroup; class QMenu; class QWidget; namespace MusEGui { class PopupMenu; } namespace MusECore { class MEvent; class Track; class MidiTrack; class Part; class PartList; struct DrumMap; class Xml; void enumerateJackMidiDevices(); void populateMidiPorts(); QString pitch2string(int v); int string2pitch(const QString &s); QValidator::State validatePitch(const QString &s); void dumpMPEvent(const MEvent* ev); Part* partFromSerialNumber(int serial); bool any_event_selected(const std::set&, bool in_range=false, RelevantSelectedEvents_t relevant = NotesRelevant); bool drummaps_almost_equal(const DrumMap* one, const DrumMap* two, int drummap_size=128); // drummap_hidden may be NULL. void write_new_style_drummap(int level, Xml& xml, const char* tagname, DrumMap* drummap, bool full=false); void read_new_style_drummap(Xml& xml, const char* tagname, DrumMap* drummap, bool compatibility=false); int readDrummapsEntryPatchCollection(Xml& xml); QSet parts_at_tick(unsigned tick); QSet parts_at_tick(unsigned tick, const Track* track); QSet parts_at_tick(unsigned tick, const QSet& tracks); bool parse_range(const QString& str, int* from, int* to); // returns true if successful, false on error void record_controller_change_and_maybe_send(unsigned tick, int ctrl_num, int val, MidiTrack* mt); } namespace MusEGui { // Lists all used midi ports + devices, plus all empty ports. QMenu* midiPortsPopup(QWidget* parent = nullptr, int checkPort = -1, bool includeDefaultEntry = false); // Includes all listed in midiPortsPopup, plus unused devices. void midiPortsPopupMenu(MusECore::Track* t, int x, int y, bool allClassPorts, const QWidget* widget = nullptr, bool includeDefaultEntry = false); QMenu* populateAddSynth(QWidget* parent); QActionGroup* populateAddTrack(QMenu* addTrack, bool populateAll=false, bool insert=false, bool addHeader=false); void openSynthGui(MusECore::Track* t); QStringList localizedStringListFromCharArray(const char** array, const char* context); QString getFilterExtension(const QString &filter); QString browseProjectFolder(QWidget* parent = nullptr); QString projectTitleFromFilename(QString filename); QString projectPathFromFilename(QString filename); QString projectExtensionFromFilename(QString filename); QString getUniqueUntitledName(); int populateMidiCtrlMenu(PopupMenu* menu, MusECore::PartList* part_list, MusECore::Part* cur_part, int curDrumPitch); QLine clipQLine(int x1, int y1, int x2, int y2, const QRect& rect); // We need normalizeQRect() because Qt's own QRect normalize makes the rectangle larger, // that is NOT what we want! For example Qt normalizes (50, 50, -40, -40) to (9, 9, 42, 42), // but we want (10, 10, 40, 40). QRect normalizeQRect(const QRect& rect); //void loadQtStyle(const QString&); void loadTheme(const QString&); void loadThemeColors(const QString&); // Call when the theme or stylesheet part of the configuration has changed, to actually switch them. //void updateThemeAndStyle(); } #endif muse-4.0.0/src/muse/icons.cpp000066400000000000000000000775511404105311000160470ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: icons.cpp,v 1.13.2.8 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; 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 "xpm/midi_ctrl_graph_merge_erase.xpm" #include "xpm/midi_ctrl_graph_merge_erase_inclusive.xpm" #include "xpm/midi_ctrl_graph_merge_erase_wysiwyg.xpm" #include "xpm/muse_icon.xpm" #include "xpm/about_muse.xpm" #include "xpm/global.xpm" #include "xpm/project.xpm" #include "xpm/user.xpm" #include "xpm/pianoNew.xpm" #include "xpm/presetsNew.xpm" #include "icons.h" namespace MusEGui { QPixmap* midiCtrlMergeEraseIcon; QPixmap* midiCtrlMergeEraseInclusiveIcon; QPixmap* midiCtrlMergeEraseWysiwygIcon; QPixmap* museIcon; QPixmap* aboutMuseImage; QIcon* globalIcon; QIcon* projectIcon; QIcon* userIcon; QIcon* pianoNewIcon; QIcon* presetsNewIcon; //---------------------------------- // SVG... //---------------------------------- QIcon* dropDownTriangleSVGIcon; QIcon* expandLeftRightSVGIcon; QIcon* routingInputSVGIcon; QIcon* routingOutputSVGIcon; QIcon* routingInputUnconnectedSVGIcon; QIcon* routingOutputUnconnectedSVGIcon; QIcon* headphonesOffSVGIcon; QIcon* headphonesOnSVGIcon; QIcon* muteOffSVGIcon; QIcon* muteOnSVGIcon; QIcon* muteOnXSVGIcon; QIcon* muteProxyOnSVGIcon; QIcon* muteAndProxyOnSVGIcon; QIcon* muteStateSVGIcon; QIcon* soloOffSVGIcon; QIcon* soloOnSVGIcon; QIcon* soloOnAloneSVGIcon; QIcon* soloProxyOnSVGIcon; QIcon* soloProxyOnAloneSVGIcon; QIcon* soloAndProxyOnSVGIcon; QIcon* soloStateSVGIcon; QIcon* trackOffSVGIcon; QIcon* trackOnSVGIcon; QIcon* stereoOffSVGIcon; QIcon* stereoOnSVGIcon; QIcon* preFaderOffSVGIcon; QIcon* preFaderOnSVGIcon; QIcon* recArmOffSVGIcon; QIcon* recArmOnSVGIcon; QIcon* recArmStateSVGIcon; QIcon* monitorOffSVGIcon; QIcon* monitorOnSVGIcon; QIcon* monitorStateSVGIcon; QIcon* velocityPerNoteSVGIcon; QIcon* midiControllerNewSVGIcon; QIcon* midiControllerSelectSVGIcon; QIcon* midiControllerRemoveSVGIcon; //QIcon* soloSVGIcon; //QIcon* soloProxySVGIcon; QIcon* muteSVGIcon; //QIcon* trackEnableSVGIcon; //QIcon* recArmSVGIcon; QIcon* recMasterSVGIcon; QIcon* stopSVGIcon; QIcon* playSVGIcon; QIcon* fastForwardSVGIcon; QIcon* rewindSVGIcon; QIcon* rewindToStartSVGIcon; QIcon* externSyncOffSVGIcon; QIcon* externSyncOnSVGIcon; QIcon* masterTrackOffSVGIcon; QIcon* masterTrackOnSVGIcon; QIcon* jackTransportOffSVGIcon; QIcon* jackTransportOnSVGIcon; QIcon* timebaseMasterOffSVGIcon; QIcon* timebaseMasterOnSVGIcon; QIcon* metronomeOffSVGIcon; QIcon* metronomeOnSVGIcon; QIcon* fixedSpeedSVGIcon; QIcon* transportAffectsLatencySVGIcon; QIcon* overrideLatencySVGIcon; QIcon* panicSVGIcon; QIcon* loopSVGIcon; QIcon* punchinSVGIcon; QIcon* punchoutSVGIcon; QIcon* undoSVGIcon; QIcon* redoSVGIcon; QIcon* midiInSVGIcon; QIcon* steprecSVGIcon; QIcon* speakerSVGIcon; QIcon* speakerSingleNoteSVGIcon; QIcon* speakerChordsSVGIcon; QIcon* filenewSVGIcon; QIcon* filetemplateSVGIcon; QIcon* fileopenSVGIcon; QIcon* filesaveSVGIcon; QIcon* filesaveasSVGIcon; QIcon* filesaveProjectSVGIcon; QIcon* filesaveTemplateSVGIcon; QIcon* filesaveRevisionSVGIcon; QIcon* filecloseSVGIcon; QIcon* appexitSVGIcon; QIcon* whatsthisSVGIcon; QIcon* infoSVGIcon; QIcon* showFieldsSVGIcon; QIcon* exitSVGIcon; QIcon* noteSVGIcon; QIcon* metaSVGIcon; QIcon* ctrlSVGIcon; QIcon* sysexSVGIcon; QIcon* tracktypeSVGIcon; QIcon* pianorollSVGIcon; QIcon* arrangerSVGIcon; QIcon* waveeditorSVGIcon; QIcon* scoreeditSVGIcon; QIcon* mastereditSVGIcon; QIcon* drumeditSVGIcon; QIcon* listeditSVGIcon; QIcon* synthSVGIcon; QIcon* trackInputSVGIcon; QIcon* trackOutputSVGIcon; QIcon* trackAuxSVGIcon; QIcon* trackGroupVGIcon; // tool icons QIcon* pencilIconSVG; QIcon* glueIconSVG; QIcon* cutterIconSVG; QIcon* zoomIconSVG; QIcon* zoomAtIconSVG; QIcon* deleteIconSVG; QIcon* drawIconSVG; QIcon* pointerIconSVG; QIcon* mutePartsIconSVG; QIcon* handIconSVG; QIcon* closedHandIconSVG; QIcon* cursorIconSVG; //QIcon* magnetIconSVG; //QIcon* customMoveIconSVG; QIcon* pencilMove4WayIconSVG; QIcon* pencilMoveHorizIconSVG; QIcon* pencilMoveVertIconSVG; QIcon* audioStretchIconSVG; QIcon* audioResampleIconSVG; QIcon* synthGuiSVGIcon; QIcon* ankerSVGIcon; QIcon* settingsSVGIcon; QIcon* transportSVGIcon; QIcon* bigtimeSVGIcon; QIcon* mixerSVGIcon; QIcon* plusSVGIcon; QIcon* minusSVGIcon; QIcon* keySVGIcon; QIcon* delSelTracksSVGIcon; QIcon* duplSelTrackSVGIcon; QIcon* duplSelTracksSVGIcon; QIcon* routeAutoAdjustSVGIcon; QIcon* routeSelSourceSVGIcon; QIcon* routeSelDestSVGIcon; QIcon* routeSourceSVGIcon; QIcon* routeDestSVGIcon; QIcon* gridOnSVGIcon; QIcon* rangeToSelectionSVGIcon; QIcon* quantizeSVGIcon; QIcon* clearSVGIcon; QIcon* downmixOffSVGIcon; QIcon* downmixOnSVGIcon; QIcon* downmixTrackSVGIcon; QIcon* downmixStateSVGIcon; QIcon* restartSVGIcon; QIcon* snapshotSVGIcon; QIcon* emptyBarSVGIcon; QIcon* lockSVGIcon; QIcon* midiResetSVGIcon; QIcon* midiInitSVGIcon; QIcon* midiLocalOffSVGIcon; QIcon* midiTransformSVGIcon; QIcon* midiInputTransformSVGIcon; QIcon* midiInputTransposeSVGIcon; QIcon* midiInputFilterSVGIcon; QIcon* midiInputRemoteSVGIcon; QIcon* midiSyncSVGIcon; QIcon* midiExportImportSVGIcon; QIcon* cutSVGIcon; QIcon* copySVGIcon; QIcon* pasteSVGIcon; QIcon* pasteDialogSVGIcon; QIcon* pasteCloneSVGIcon; QIcon* copyRangeSVGIcon; QIcon* deleteSVGIcon; QIcon* pasteSelectedTrackSVGIcon; QIcon* pasteCloneSelectedTrackSVGIcon; QIcon* eyeSVGIcon; QIcon* eyeCrossedSVGIcon; QIcon* eyeGreySVGIcon; QIcon* nextPartSVGIcon; QIcon* lastPartSVGIcon; QIcon* selectAllSVGIcon; QIcon* selectAllTrackSVGIcon; QIcon* selectInsideLoopSVGIcon; QIcon* selectOutsideLoopSVGIcon; QIcon* selectInvertSVGIcon; QIcon* deselectAllSVGIcon; QIcon* ledGreenSVGIcon; QIcon* ledGreenDarkSVGIcon; QIcon* ledRedSVGIcon; QIcon* ledBlueSVGIcon; QIcon* ledYellowSVGIcon; QIcon* ledOffSVGIcon; QIcon* routeInSVGIcon; QIcon* routeOutSVGIcon; QIcon* routeInMidiSVGIcon; QIcon* routeOutMidiSVGIcon; QIcon* midiPortSVGIcon; QIcon* routerSVGIcon; QIcon* appearanceSVGIcon; QIcon* editInstrumentSVGIcon; QIcon* deltaSVGIcon; QIcon* dummySVGIcon; //---------------------------------- // Cursors //---------------------------------- QCursor* editpasteSCursor; QCursor* editpasteCloneSCursor; QCursor* pencilCursor; QCursor* glueCursor; QCursor* cutterCursor; QCursor* zoomCursor; QCursor* zoomAtCursor; QCursor* deleteCursor; QCursor* drawCursor; QCursor* mutePartsCursor; QCursor* handCursor; QCursor* closedHandCursor; //QCursor* magnetCursor; //QCursor* customMoveCursor; QCursor* pencilMove4WayCursor; QCursor* pencilMoveHorizCursor; QCursor* pencilMoveVertCursor; //--------------------------------------------------------- // class Icons //--------------------------------------------------------- class Icons { QStringList _global, _user; QString _path_global, _path_user; bool _global_on, _user_on; public: Icons(const QString & path_global, const QString & path_user) : _path_global(path_global), _path_user(path_user) { QDir dir(path_global, "*.svg"); _global = dir.entryList(QDir::Files); _global_on = !_global.isEmpty(); dir.setPath(path_user); _user = dir.entryList(QDir::Files); _user_on = !_user.isEmpty(); } QIcon* getSVG(const QString & filename) { if (_user_on && _user.contains(filename)) return new QIcon(_path_user + "/" + filename); if (_global_on && _global.contains(filename)) return new QIcon(_path_global + "/" + filename); return new QIcon(":/svg/" + filename); } void addSVG(QIcon *icon, const QString & filename, QIcon::Mode mode = QIcon::Normal, QIcon::State state = QIcon::On) { if (!icon) return; if (_user_on && _user.contains(filename)) { icon->addFile(_path_user + "/" + filename, QSize(), mode, state); return; } if (_global_on && _global.contains(filename)) { icon->addFile(_path_global + "/" + filename, QSize(), mode, state); return; } icon->addFile(":/svg/" + filename, QSize(), mode, state); } }; //--------------------------------------------------------- // initIcons //--------------------------------------------------------- void initIcons(int cursorSize, const QString& gpath, const QString& upath) { const qreal dpr = qApp->devicePixelRatio(); Icons icons(gpath, upath); midiCtrlMergeEraseIcon = new QPixmap(midi_ctrl_graph_merge_erase_xpm); midiCtrlMergeEraseInclusiveIcon = new QPixmap(midi_ctrl_graph_merge_erase_inclusive_xpm); midiCtrlMergeEraseWysiwygIcon = new QPixmap(midi_ctrl_graph_merge_erase_wysiwyg_xpm); museIcon = new QPixmap(muse_icon_xpm); aboutMuseImage = new QPixmap(about_muse_xpm); globalIcon = new QIcon(QPixmap(global_xpm)); userIcon = new QIcon(QPixmap(user_xpm)); projectIcon = new QIcon(QPixmap(project_xpm)); pianoNewIcon = new QIcon(QPixmap(pianoNew_xpm)); presetsNewIcon = new QIcon(QPixmap(presetsNew_xpm)); //---------------------------------- // SVG... //---------------------------------- dropDownTriangleSVGIcon = icons.getSVG("drop_down_triangle.svg"); expandLeftRightSVGIcon = icons.getSVG("expand_left_right.svg"); routingInputSVGIcon = icons.getSVG("routing_input.svg"); routingOutputSVGIcon = icons.getSVG("routing_output.svg"); routingInputUnconnectedSVGIcon = icons.getSVG("routing_input_unconnected.svg"); routingOutputUnconnectedSVGIcon = icons.getSVG("routing_output_unconnected.svg"); headphonesOffSVGIcon = icons.getSVG("headphones_off.svg"); headphonesOnSVGIcon = icons.getSVG("headphones_on.svg"); muteOffSVGIcon = icons.getSVG("mute_off.svg"); muteOnSVGIcon = icons.getSVG("mute_on.svg"); muteOnXSVGIcon = icons.getSVG("mute_on_X.svg"); muteProxyOnSVGIcon = icons.getSVG("mute_proxy_on.svg"); muteAndProxyOnSVGIcon = icons.getSVG("mute_and_proxy_on.svg"); muteStateSVGIcon = icons.getSVG("mute_off.svg"); icons.addSVG(muteStateSVGIcon, "mute_on.svg"); soloOffSVGIcon = icons.getSVG("solo_spotlight_off.svg"); soloOnSVGIcon = icons.getSVG("solo_spotlight_on.svg"); soloOnAloneSVGIcon = icons.getSVG("solo_spotlight_on_alone.svg"); soloProxyOnSVGIcon = icons.getSVG("solo_proxy_spotlight_on.svg"); soloProxyOnAloneSVGIcon = icons.getSVG("solo_proxy_spotlight_on_alone.svg"); soloAndProxyOnSVGIcon = icons.getSVG("solo_and_proxy_spotlight_on.svg"); soloStateSVGIcon = icons.getSVG("solo_spotlight_off.svg"); icons.addSVG(soloStateSVGIcon, "solo_spotlight_on_alone.svg"); trackOffSVGIcon = icons.getSVG("track_off.svg"); trackOnSVGIcon = icons.getSVG("track_on.svg"); icons.addSVG(trackOnSVGIcon, "track_off.svg"); stereoOffSVGIcon = icons.getSVG("stereo_off.svg"); stereoOnSVGIcon = icons.getSVG("stereo_on.svg"); preFaderOffSVGIcon = icons.getSVG("pre_fader_off.svg"); preFaderOnSVGIcon = icons.getSVG("pre_fader_on.svg"); recArmOffSVGIcon = icons.getSVG("rec_arm_off_default_col.svg"); recArmOnSVGIcon = icons.getSVG("rec_arm_on.svg"); recArmStateSVGIcon = icons.getSVG("rec_arm_off_default_col.svg"); icons.addSVG(recArmStateSVGIcon, "rec_arm_on.svg"); monitorOffSVGIcon = icons.getSVG("monitor_off_default_col.svg"); monitorOnSVGIcon = icons.getSVG("monitor_on.svg"); monitorStateSVGIcon = icons.getSVG("monitor_off_default_col.svg"); icons.addSVG(monitorStateSVGIcon, "monitor_on.svg"); velocityPerNoteSVGIcon = icons.getSVG("velocity_all_notes.svg"); velocityPerNoteSVGIcon->addFile(":/svg/velocity_per_note.svg", QSize(), QIcon::Normal, QIcon::On); midiControllerNewSVGIcon = icons.getSVG("midi_controller_new.svg"); midiControllerSelectSVGIcon = icons.getSVG("midi_controller_select.svg"); midiControllerRemoveSVGIcon = icons.getSVG("midi_controller_remove.svg"); // soloSVGIcon = icons.getSVG("headphones_off.svg"); // soloSVGIcon->addFile(":/svg/headphones_on.svg", QSize(), QIcon::Normal, QIcon::On); // TODO // soloProxySVGIcon = icons.getSVG("headphones_off.svg"); // soloProxySVGIcon->addFile(":/svg/headphones_on.svg", QSize(), QIcon::Normal, QIcon::On); muteSVGIcon = icons.getSVG("mute_off.svg"); muteSVGIcon->addFile(":/svg/mute_on.svg", QSize(), QIcon::Normal, QIcon::On); // trackEnableSVGIcon = icons.getSVG("track_on.svg"); // trackEnableSVGIcon->addFile(":/svg/track_off.svg", QSize(), QIcon::Normal, QIcon::On); //recArmSVGIcon = icons.getSVG("rec_arm_off_default_col.svg"); // recArmSVGIcon = icons.getSVG("rec_arm_off.svg"); // recArmSVGIcon->addFile(":/svg/rec_arm_on.svg", QSize(), QIcon::Normal, QIcon::On); //recMasterSVGIcon = icons.getSVG("rec_arm_off_default_col.svg"); recMasterSVGIcon = icons.getSVG("rec_arm_off.svg"); recMasterSVGIcon->addFile(":/svg/rec_arm_on.svg", QSize(), QIcon::Normal, QIcon::On); stopSVGIcon = icons.getSVG("stop.svg"); playSVGIcon = icons.getSVG("play_off.svg"); playSVGIcon->addFile(":/svg/play_on.svg", QSize(), QIcon::Normal, QIcon::On); fastForwardSVGIcon = icons.getSVG("fast_forward.svg"); rewindSVGIcon = icons.getSVG("rewind.svg"); rewindToStartSVGIcon = icons.getSVG("rewind_to_start.svg"); externSyncOffSVGIcon = icons.getSVG("extern_sync_off.svg"); externSyncOnSVGIcon = icons.getSVG("extern_sync_on.svg"); masterTrackOffSVGIcon = icons.getSVG("master_track_off.svg"); masterTrackOnSVGIcon = icons.getSVG("master_track_on.svg"); jackTransportOffSVGIcon = icons.getSVG("jack_transport_off.svg"); jackTransportOnSVGIcon = icons.getSVG("jack_transport_on.svg"); timebaseMasterOffSVGIcon = icons.getSVG("timebase_master_off.svg"); timebaseMasterOnSVGIcon = icons.getSVG("timebase_master_on.svg"); metronomeOffSVGIcon = icons.getSVG("metronome_off.svg"); metronomeOnSVGIcon = icons.getSVG("metronome_on.svg"); fixedSpeedSVGIcon = icons.getSVG("speed_off.svg"); fixedSpeedSVGIcon->addFile(":/svg/speed_on.svg", QSize(), QIcon::Normal, QIcon::On); transportAffectsLatencySVGIcon = icons.getSVG("transport_affects_latency_off.svg"); transportAffectsLatencySVGIcon->addFile(":/svg/transport_affects_latency_on.svg", QSize(), QIcon::Normal, QIcon::On); overrideLatencySVGIcon = icons.getSVG("override_latency_off.svg"); overrideLatencySVGIcon->addFile(":/svg/override_latency_on.svg", QSize(), QIcon::Normal, QIcon::On); panicSVGIcon = icons.getSVG("panic.svg"); loopSVGIcon = icons.getSVG("loop.svg"); punchinSVGIcon = icons.getSVG("punchin.svg"); punchoutSVGIcon = icons.getSVG("punchout.svg"); undoSVGIcon = icons.getSVG("undo.svg"); redoSVGIcon = icons.getSVG("redo.svg"); midiInSVGIcon = icons.getSVG("midiin.svg"); steprecSVGIcon = icons.getSVG("steprec.svg"); speakerSVGIcon = icons.getSVG("speaker.svg"); speakerSingleNoteSVGIcon = icons.getSVG("speaker_single_note.svg"); speakerChordsSVGIcon = icons.getSVG("speaker_chords.svg"); whatsthisSVGIcon = icons.getSVG("whatsthis.svg"); infoSVGIcon = icons.getSVG("info.svg"); showFieldsSVGIcon = icons.getSVG("show_fields.svg"); exitSVGIcon = icons.getSVG("exit.svg"); noteSVGIcon = icons.getSVG("note.svg"); metaSVGIcon = icons.getSVG("meta.svg"); ctrlSVGIcon = icons.getSVG("ctrl.svg"); sysexSVGIcon = icons.getSVG("sysex.svg"); tracktypeSVGIcon = icons.getSVG("tracktype.svg"); filenewSVGIcon = icons.getSVG("filenew.svg"); filetemplateSVGIcon = icons.getSVG("filefromtemplate.svg"); fileopenSVGIcon = icons.getSVG("fileopen.svg"); filesaveSVGIcon = icons.getSVG("filesave.svg"); filesaveasSVGIcon = icons.getSVG("filesaveas.svg"); filesaveRevisionSVGIcon = icons.getSVG("filesave_revision.svg"); filesaveTemplateSVGIcon = icons.getSVG("filesave_template.svg"); filesaveProjectSVGIcon = icons.getSVG("filesave_project.svg"); filecloseSVGIcon = icons.getSVG("fileclose.svg"); appexitSVGIcon = icons.getSVG("appexit.svg"); pianorollSVGIcon = icons.getSVG("pianoroll.svg"); arrangerSVGIcon = icons.getSVG("arranger.svg"); waveeditorSVGIcon = icons.getSVG("waveeditor.svg"); scoreeditSVGIcon = icons.getSVG("scoreedit.svg"); mastereditSVGIcon = icons.getSVG("masteredit.svg"); drumeditSVGIcon = icons.getSVG("drumedit.svg"); listeditSVGIcon = icons.getSVG("listedit.svg"); synthSVGIcon = icons.getSVG("synth.svg"); trackInputSVGIcon = icons.getSVG("track_input.svg"); trackOutputSVGIcon = icons.getSVG("track_output.svg"); trackAuxSVGIcon = icons.getSVG("track_aux.svg"); trackGroupVGIcon = icons.getSVG("track_group.svg"); synthGuiSVGIcon = icons.getSVG("synth_gui.svg"); ankerSVGIcon = icons.getSVG("anker.svg"); settingsSVGIcon = icons.getSVG("settings.svg"); transportSVGIcon = icons.getSVG("transport.svg"); bigtimeSVGIcon = icons.getSVG("bigtime.svg"); mixerSVGIcon = icons.getSVG("mixer.svg"); plusSVGIcon = icons.getSVG("plus.svg"); minusSVGIcon = icons.getSVG("minus.svg"); keySVGIcon = icons.getSVG("key.svg"); delSelTracksSVGIcon = icons.getSVG("delete_sel_tracks.svg"); duplSelTrackSVGIcon = icons.getSVG("duplicate_sel_track.svg"); duplSelTracksSVGIcon = icons.getSVG("duplicate_sel_tracks.svg"); // tool icons pencilIconSVG = icons.getSVG("pencil.svg"); glueIconSVG = icons.getSVG("glue.svg"); cutterIconSVG = icons.getSVG("cutter.svg"); zoomIconSVG = icons.getSVG("zoom.svg"); zoomAtIconSVG = icons.getSVG("zoomAt.svg"); deleteIconSVG = icons.getSVG("eraser.svg"); drawIconSVG = icons.getSVG("draw.svg"); pointerIconSVG = icons.getSVG("pointer.svg"); mutePartsIconSVG = icons.getSVG("mute_parts.svg"); handIconSVG = icons.getSVG("hand.svg"); closedHandIconSVG = icons.getSVG("closed_hand.svg"); cursorIconSVG = icons.getSVG("cursor.svg"); //magnetIconSVG = icons.getSVG("magnet.svg"); //customMoveIconSVG = icons.getSVG("cursor_move.svg"); pencilMove4WayIconSVG = icons.getSVG("pencil_move_4_way.svg"); pencilMoveHorizIconSVG = icons.getSVG("pencil_move_horiz.svg"); pencilMoveVertIconSVG = icons.getSVG("pencil_move_vert.svg"); audioStretchIconSVG = icons.getSVG("audio_stretch.svg"); audioResampleIconSVG = icons.getSVG("audio_resample.svg"); routeAutoAdjustSVGIcon = icons.getSVG("route_auto_adjust.svg"); routeSelSourceSVGIcon = icons.getSVG("route_sel_source.svg"); routeSelDestSVGIcon = icons.getSVG("route_sel_dest.svg"); routeSourceSVGIcon = icons.getSVG("route_sources.svg"); routeDestSVGIcon = icons.getSVG("route_destinations.svg"); gridOnSVGIcon = icons.getSVG("grid_on.svg"); rangeToSelectionSVGIcon = icons.getSVG("range_to_selection.svg"); quantizeSVGIcon = icons.getSVG("quantize.svg"); clearSVGIcon = icons.getSVG("clear.svg"); downmixOffSVGIcon = icons.getSVG("downmix_off.svg"); downmixOnSVGIcon = icons.getSVG("downmix_on.svg"); downmixTrackSVGIcon = icons.getSVG("downmix_track.svg"); downmixStateSVGIcon = icons.getSVG("downmix_off.svg"); icons.addSVG(downmixStateSVGIcon, "downmix_on.svg"); restartSVGIcon = icons.getSVG("restart.svg"); snapshotSVGIcon = icons.getSVG("snapshot.svg"); emptyBarSVGIcon = icons.getSVG("empty_bar.svg"); lockSVGIcon = icons.getSVG("lock.svg"); midiResetSVGIcon = icons.getSVG("midi_reset.svg"); midiInitSVGIcon = icons.getSVG("midi_init.svg"); midiLocalOffSVGIcon = icons.getSVG("midi_local_off.svg"); midiTransformSVGIcon = icons.getSVG("midi_transform.svg"); midiInputTransformSVGIcon = icons.getSVG("midi_input_transform.svg"); midiInputTransposeSVGIcon = icons.getSVG("midi_input_transpose.svg"); midiInputFilterSVGIcon = icons.getSVG("midi_input_filter.svg"); midiInputRemoteSVGIcon = icons.getSVG("midi_input_remote.svg"); midiSyncSVGIcon = icons.getSVG("midi_sync.svg"); midiExportImportSVGIcon = icons.getSVG("midi_import_export.svg"); cutSVGIcon = icons.getSVG("cut.svg"); copySVGIcon = icons.getSVG("copy.svg"); pasteSVGIcon = icons.getSVG("paste.svg"); pasteDialogSVGIcon = icons.getSVG("paste_dialog.svg"); pasteCloneSVGIcon = icons.getSVG("paste_clone.svg"); copyRangeSVGIcon = icons.getSVG("copy_range.svg"); deleteSVGIcon = icons.getSVG("delete.svg"); pasteSelectedTrackSVGIcon = icons.getSVG("paste_selected_track.svg"); pasteCloneSelectedTrackSVGIcon = icons.getSVG("paste_clone_selected_track.svg"); eyeSVGIcon = icons.getSVG("eye.svg"); eyeCrossedSVGIcon = icons.getSVG("eye_crossed.svg"); eyeGreySVGIcon = icons.getSVG("eye_grey.svg"); nextPartSVGIcon = icons.getSVG("next_part.svg"); lastPartSVGIcon = icons.getSVG("last_part.svg"); selectAllSVGIcon = icons.getSVG("select_all.svg"); selectAllTrackSVGIcon = icons.getSVG("select_all_track.svg"); selectInsideLoopSVGIcon = icons.getSVG("select_inside_loop.svg"); selectOutsideLoopSVGIcon = icons.getSVG("select_outside_loop.svg"); selectInvertSVGIcon = icons.getSVG("select_invert.svg"); deselectAllSVGIcon = icons.getSVG("deselect_all.svg"); ledGreenSVGIcon = icons.getSVG("led_green.svg"); ledGreenDarkSVGIcon = icons.getSVG("led_green_dark.svg"); ledRedSVGIcon = icons.getSVG("led_red.svg"); ledBlueSVGIcon = icons.getSVG("led_blue.svg"); ledYellowSVGIcon = icons.getSVG("led_yellow.svg"); ledOffSVGIcon = icons.getSVG("led_off.svg"); routeInSVGIcon = icons.getSVG("route_in.svg"); routeOutSVGIcon = icons.getSVG("route_out.svg"); routeInMidiSVGIcon = icons.getSVG("route_in_midi.svg"); routeOutMidiSVGIcon = icons.getSVG("route_out_midi.svg"); midiPortSVGIcon = icons.getSVG("midi_port.svg"); routerSVGIcon = icons.getSVG("router.svg"); appearanceSVGIcon = icons.getSVG("appearance.svg"); editInstrumentSVGIcon = icons.getSVG("edit_instrument.svg"); deltaSVGIcon = icons.getSVG("delta.svg"); QPixmap px(10,10); px.fill(Qt::transparent); dummySVGIcon = new QIcon(px); //---------------------------------- // Cursors //---------------------------------- editpasteSCursor = new QCursor(pasteSVGIcon->pixmap(QSize(cursorSize, cursorSize))); editpasteCloneSCursor = new QCursor(copySVGIcon->pixmap(QSize(cursorSize, cursorSize))); // tool cursors pencilCursor = new QCursor(pencilIconSVG->pixmap(QSize(cursorSize, cursorSize)), 0, qRound(dpr * (cursorSize - 1))); glueCursor = new QCursor(glueIconSVG->pixmap(QSize(cursorSize, cursorSize)), 0, qRound(dpr * (cursorSize - 1))); cutterCursor = new QCursor(cutterIconSVG->pixmap(QSize(cursorSize, cursorSize)), 0, qRound(dpr * (cursorSize - 1))); zoomCursor = new QCursor(zoomIconSVG->pixmap(QSize(cursorSize, cursorSize))); zoomAtCursor = new QCursor(zoomAtIconSVG->pixmap(QSize(cursorSize, cursorSize))); deleteCursor = new QCursor(deleteIconSVG->pixmap(QSize(cursorSize, cursorSize)), qRound(dpr * (cursorSize / 6)), qRound(dpr * (cursorSize - 1))); drawCursor = new QCursor(drawIconSVG->pixmap(QSize(cursorSize, cursorSize)), 0, qRound(dpr * (cursorSize - 1))); mutePartsCursor = new QCursor(mutePartsIconSVG->pixmap(QSize(cursorSize, cursorSize))); handCursor = new QCursor(handIconSVG->pixmap(QSize(cursorSize, cursorSize))); closedHandCursor = new QCursor(closedHandIconSVG->pixmap(QSize(cursorSize, cursorSize))); //magnetCursor = new QCursor(magnetIconSVG->pixmap(QSize(cursorSize, cursorSize)), -1, qRound(dpr * 15)); // This one needs to be bigger to contain drum notes. //customMoveCursor = new QCursor(customMoveIconSVG->pixmap(QSize(2 * cursorSize, 2 * cursorSize))); pencilMove4WayCursor = new QCursor(pencilMove4WayIconSVG->pixmap(QSize(cursorSize, cursorSize)), 0, qRound(dpr * (cursorSize - 1))); pencilMoveHorizCursor = new QCursor(pencilMoveHorizIconSVG->pixmap(QSize(cursorSize, cursorSize)), 0, qRound(dpr * (cursorSize - 1))); pencilMoveVertCursor = new QCursor(pencilMoveVertIconSVG->pixmap(QSize(cursorSize, cursorSize)), 0, qRound(dpr * (cursorSize - 1))); } //--------------------------------------------------------- // deleteIcons //--------------------------------------------------------- void deleteIcons() { delete midiCtrlMergeEraseIcon; delete midiCtrlMergeEraseInclusiveIcon; delete midiCtrlMergeEraseWysiwygIcon; delete museIcon; delete aboutMuseImage; delete globalIcon; delete userIcon; delete projectIcon; //---------------------------------- // SVG... //---------------------------------- delete dropDownTriangleSVGIcon; delete expandLeftRightSVGIcon; delete routingInputSVGIcon; delete routingOutputSVGIcon; delete routingInputUnconnectedSVGIcon; delete routingOutputUnconnectedSVGIcon; delete headphonesOffSVGIcon; delete headphonesOnSVGIcon; delete muteOffSVGIcon; delete muteOnSVGIcon; delete muteOnXSVGIcon; delete muteProxyOnSVGIcon; delete muteAndProxyOnSVGIcon; delete muteStateSVGIcon; delete soloOffSVGIcon; delete soloOnSVGIcon; delete soloOnAloneSVGIcon; delete soloProxyOnSVGIcon; delete soloProxyOnAloneSVGIcon; delete soloAndProxyOnSVGIcon; delete soloStateSVGIcon; delete trackOffSVGIcon; delete trackOnSVGIcon; delete stereoOffSVGIcon; delete stereoOnSVGIcon; delete preFaderOffSVGIcon; delete preFaderOnSVGIcon; delete recArmOffSVGIcon; delete recArmOnSVGIcon; delete recArmStateSVGIcon; delete monitorOffSVGIcon; delete monitorOnSVGIcon; delete monitorStateSVGIcon; delete velocityPerNoteSVGIcon; delete midiControllerNewSVGIcon; delete midiControllerSelectSVGIcon; delete midiControllerRemoveSVGIcon; // delete soloSVGIcon; // delete soloProxySVGIcon; delete muteSVGIcon; // delete trackEnableSVGIcon; // delete recArmSVGIcon; delete recMasterSVGIcon; delete stopSVGIcon; delete playSVGIcon; delete fastForwardSVGIcon; delete rewindSVGIcon; delete rewindToStartSVGIcon; delete externSyncOffSVGIcon; delete externSyncOnSVGIcon; delete masterTrackOffSVGIcon; delete masterTrackOnSVGIcon; delete jackTransportOffSVGIcon; delete jackTransportOnSVGIcon; delete timebaseMasterOffSVGIcon; delete timebaseMasterOnSVGIcon; delete metronomeOffSVGIcon; delete metronomeOnSVGIcon; delete fixedSpeedSVGIcon; delete transportAffectsLatencySVGIcon; delete overrideLatencySVGIcon; delete panicSVGIcon; delete loopSVGIcon; delete punchinSVGIcon; delete punchoutSVGIcon; delete undoSVGIcon; delete redoSVGIcon; delete midiInSVGIcon; delete steprecSVGIcon; delete speakerSVGIcon; delete speakerSingleNoteSVGIcon; delete speakerChordsSVGIcon; delete filenewSVGIcon; delete filetemplateSVGIcon; delete fileopenSVGIcon; delete filesaveSVGIcon; delete filesaveasSVGIcon; delete filesaveProjectSVGIcon; delete filesaveTemplateSVGIcon; delete filesaveRevisionSVGIcon; delete filecloseSVGIcon; delete appexitSVGIcon; delete whatsthisSVGIcon; delete infoSVGIcon; delete showFieldsSVGIcon; delete exitSVGIcon; delete noteSVGIcon; delete metaSVGIcon; delete ctrlSVGIcon; delete sysexSVGIcon; delete tracktypeSVGIcon; delete pencilIconSVG; delete glueIconSVG; delete cutterIconSVG; delete zoomIconSVG; delete zoomAtIconSVG; delete deleteIconSVG; delete drawIconSVG; delete pointerIconSVG; delete mutePartsIconSVG; delete handIconSVG; delete closedHandIconSVG; delete cursorIconSVG; //delete magnetIconSVG; //delete customMoveIconSVG; delete pencilMove4WayIconSVG; delete pencilMoveHorizIconSVG; delete pencilMoveVertIconSVG; delete audioStretchIconSVG; delete audioResampleIconSVG; delete synthGuiSVGIcon; delete ankerSVGIcon; delete settingsSVGIcon; delete transportSVGIcon; delete bigtimeSVGIcon; delete mixerSVGIcon; delete plusSVGIcon; delete minusSVGIcon; delete keySVGIcon; delete delSelTracksSVGIcon; delete duplSelTrackSVGIcon; delete duplSelTracksSVGIcon; delete pianorollSVGIcon; delete arrangerSVGIcon; delete waveeditorSVGIcon; delete scoreeditSVGIcon; delete mastereditSVGIcon; delete drumeditSVGIcon; delete listeditSVGIcon; delete synthSVGIcon; delete trackInputSVGIcon; delete trackOutputSVGIcon; delete trackAuxSVGIcon; delete trackGroupVGIcon; delete routeAutoAdjustSVGIcon; delete routeSelSourceSVGIcon; delete routeSelDestSVGIcon; delete routeSourceSVGIcon; delete routeDestSVGIcon; delete gridOnSVGIcon; delete rangeToSelectionSVGIcon; delete quantizeSVGIcon; delete clearSVGIcon; delete downmixOffSVGIcon; delete downmixOnSVGIcon; delete downmixTrackSVGIcon; delete downmixStateSVGIcon; delete restartSVGIcon; delete snapshotSVGIcon; delete emptyBarSVGIcon; delete lockSVGIcon; delete midiResetSVGIcon; delete midiInitSVGIcon; delete midiLocalOffSVGIcon; delete midiTransformSVGIcon; delete midiInputTransformSVGIcon; delete midiInputTransposeSVGIcon; delete midiInputFilterSVGIcon; delete midiInputRemoteSVGIcon; delete midiSyncSVGIcon; delete midiExportImportSVGIcon; delete cutSVGIcon; delete copySVGIcon; delete pasteSVGIcon; delete pasteDialogSVGIcon; delete pasteCloneSVGIcon; delete copyRangeSVGIcon; delete deleteSVGIcon; delete pasteSelectedTrackSVGIcon; delete pasteCloneSelectedTrackSVGIcon; delete eyeSVGIcon; delete eyeCrossedSVGIcon; delete eyeGreySVGIcon; delete nextPartSVGIcon; delete lastPartSVGIcon; delete selectAllSVGIcon; delete selectAllTrackSVGIcon; delete selectInsideLoopSVGIcon; delete selectOutsideLoopSVGIcon; delete selectInvertSVGIcon; delete deselectAllSVGIcon; delete ledGreenSVGIcon; delete ledGreenDarkSVGIcon; delete ledRedSVGIcon; delete ledBlueSVGIcon; delete ledYellowSVGIcon; delete ledOffSVGIcon; delete routeInSVGIcon; delete routeOutSVGIcon; delete routeInMidiSVGIcon; delete routeOutMidiSVGIcon; delete midiPortSVGIcon; delete routerSVGIcon; delete appearanceSVGIcon; delete editInstrumentSVGIcon; delete deltaSVGIcon; delete dummySVGIcon; //---------------------------------- // Cursors //---------------------------------- delete editpasteSCursor; delete editpasteCloneSCursor; delete pencilCursor; delete glueCursor; delete cutterCursor; delete zoomCursor; delete zoomAtCursor; delete deleteCursor; delete drawCursor; delete mutePartsCursor; delete handCursor; delete closedHandCursor; //delete magnetCursor; //delete customMoveCursor; delete pencilMove4WayCursor; delete pencilMoveHorizCursor; delete pencilMoveVertCursor; } } // namespace MusEGui muse-4.0.0/src/muse/icons.h000066400000000000000000000214631404105311000155030ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: icons.h,v 1.11.2.8 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef ICONS_H #define ICONS_H class QPixmap; class QIcon; class QCursor; // NOTICE: Although it is tempting to use multi-icons (addPixmap, addFile etc.), // certain styles do not support it, such as QtCurve. // Therefore the separate icons must be manually set upon each state. // No longer relevant, arbitrary styles not supported any more (MusE 4.0) namespace MusEGui { extern QPixmap* midiCtrlMergeEraseIcon; extern QPixmap* midiCtrlMergeEraseInclusiveIcon; extern QPixmap* midiCtrlMergeEraseWysiwygIcon; extern QPixmap* museIcon; extern QPixmap* aboutMuseImage; extern QIcon* globalIcon; extern QIcon* projectIcon; extern QIcon* userIcon; extern QIcon* pianoNewIcon; extern QIcon* presetsNewIcon; //---------------------------------- // SVG... //---------------------------------- extern QIcon* dropDownTriangleSVGIcon; extern QIcon* expandLeftRightSVGIcon; extern QIcon* routingInputSVGIcon; extern QIcon* routingOutputSVGIcon; extern QIcon* routingInputUnconnectedSVGIcon; extern QIcon* routingOutputUnconnectedSVGIcon; extern QIcon* headphonesOffSVGIcon; extern QIcon* headphonesOnSVGIcon; extern QIcon* muteOffSVGIcon; extern QIcon* muteOnSVGIcon; extern QIcon* muteOnXSVGIcon; extern QIcon* muteProxyOnSVGIcon; extern QIcon* muteAndProxyOnSVGIcon; extern QIcon* muteStateSVGIcon; extern QIcon* soloOffSVGIcon; extern QIcon* soloOnSVGIcon; extern QIcon* soloOnAloneSVGIcon; extern QIcon* soloProxyOnSVGIcon; extern QIcon* soloProxyOnAloneSVGIcon; extern QIcon* soloAndProxyOnSVGIcon; extern QIcon* soloStateSVGIcon; extern QIcon* trackOffSVGIcon; extern QIcon* trackOnSVGIcon; extern QIcon* stereoOffSVGIcon; extern QIcon* stereoOnSVGIcon; extern QIcon* preFaderOffSVGIcon; extern QIcon* preFaderOnSVGIcon; extern QIcon* recArmOffSVGIcon; extern QIcon* recArmOnSVGIcon; extern QIcon* recArmStateSVGIcon; extern QIcon* monitorOffSVGIcon; extern QIcon* monitorOnSVGIcon; extern QIcon* monitorStateSVGIcon; extern QIcon* velocityPerNoteSVGIcon; extern QIcon* midiControllerNewSVGIcon; extern QIcon* midiControllerSelectSVGIcon; extern QIcon* midiControllerRemoveSVGIcon; //extern QIcon* soloSVGIcon; //extern QIcon* soloProxySVGIcon; extern QIcon* muteSVGIcon; //extern QIcon* trackEnableSVGIcon; //extern QIcon* recArmSVGIcon; extern QIcon* recMasterSVGIcon; extern QIcon* stopSVGIcon; extern QIcon* playSVGIcon; extern QIcon* fastForwardSVGIcon; extern QIcon* rewindSVGIcon; extern QIcon* rewindToStartSVGIcon; extern QIcon* externSyncOffSVGIcon; extern QIcon* externSyncOnSVGIcon; extern QIcon* masterTrackOffSVGIcon; extern QIcon* masterTrackOnSVGIcon; extern QIcon* jackTransportOffSVGIcon; extern QIcon* jackTransportOnSVGIcon; extern QIcon* timebaseMasterOffSVGIcon; extern QIcon* timebaseMasterOnSVGIcon; extern QIcon* metronomeOffSVGIcon; extern QIcon* metronomeOnSVGIcon; extern QIcon* fixedSpeedSVGIcon; extern QIcon* transportAffectsLatencySVGIcon; extern QIcon* overrideLatencySVGIcon; extern QIcon* panicSVGIcon; extern QIcon* loopSVGIcon; extern QIcon* punchinSVGIcon; extern QIcon* punchoutSVGIcon; extern QIcon* undoSVGIcon; extern QIcon* redoSVGIcon; extern QIcon* midiInSVGIcon; extern QIcon* steprecSVGIcon; extern QIcon* speakerSVGIcon; extern QIcon* speakerSingleNoteSVGIcon; extern QIcon* speakerChordsSVGIcon; extern QIcon* filenewSVGIcon; extern QIcon* filetemplateSVGIcon; extern QIcon* fileopenSVGIcon; extern QIcon* filesaveSVGIcon; extern QIcon* filesaveasSVGIcon; extern QIcon* filesaveProjectSVGIcon; extern QIcon* filesaveTemplateSVGIcon; extern QIcon* filesaveRevisionSVGIcon; extern QIcon* filecloseSVGIcon; extern QIcon* appexitSVGIcon; extern QIcon* whatsthisSVGIcon; extern QIcon* infoSVGIcon; extern QIcon* showFieldsSVGIcon; extern QIcon* exitSVGIcon; extern QIcon* noteSVGIcon; extern QIcon* metaSVGIcon; extern QIcon* ctrlSVGIcon; extern QIcon* sysexSVGIcon; extern QIcon* tracktypeSVGIcon; extern QIcon* pencilIconSVG; extern QIcon* glueIconSVG; extern QIcon* cutterIconSVG; extern QIcon* zoomIconSVG; extern QIcon* zoomAtIconSVG; extern QIcon* deleteIconSVG; extern QIcon* drawIconSVG; extern QIcon* pointerIconSVG; extern QIcon* mutePartsIconSVG; extern QIcon* handIconSVG; extern QIcon* closedHandIconSVG; extern QIcon* cursorIconSVG; //extern QIcon* magnetIconSVG; //extern QIcon* customMoveIconSVG; extern QIcon* pencilMove4WayIconSVG; extern QIcon* pencilMoveHorizIconSVG; extern QIcon* pencilMoveVertIconSVG; extern QIcon* audioStretchIconSVG; extern QIcon* audioResampleIconSVG; extern QIcon* pianorollSVGIcon; extern QIcon* arrangerSVGIcon; extern QIcon* waveeditorSVGIcon; extern QIcon* scoreeditSVGIcon; extern QIcon* mastereditSVGIcon; extern QIcon* drumeditSVGIcon; extern QIcon* listeditSVGIcon; extern QIcon* synthSVGIcon; extern QIcon* trackInputSVGIcon; extern QIcon* trackOutputSVGIcon; extern QIcon* trackAuxSVGIcon; extern QIcon* trackGroupVGIcon; extern QIcon* synthGuiSVGIcon; extern QIcon* ankerSVGIcon; extern QIcon* settingsSVGIcon; extern QIcon* transportSVGIcon; extern QIcon* bigtimeSVGIcon; extern QIcon* mixerSVGIcon; extern QIcon* plusSVGIcon; extern QIcon* minusSVGIcon; extern QIcon* keySVGIcon; extern QIcon* delSelTracksSVGIcon; extern QIcon* duplSelTrackSVGIcon; extern QIcon* duplSelTracksSVGIcon; extern QIcon* routeAutoAdjustSVGIcon; extern QIcon* routeSelSourceSVGIcon; extern QIcon* routeSelDestSVGIcon; extern QIcon* routeSourceSVGIcon; extern QIcon* routeDestSVGIcon; extern QIcon* gridOnSVGIcon; extern QIcon* rangeToSelectionSVGIcon; extern QIcon* quantizeSVGIcon; extern QIcon* clearSVGIcon; extern QIcon* downmixOffSVGIcon; extern QIcon* downmixOnSVGIcon; extern QIcon* downmixTrackSVGIcon; extern QIcon* downmixStateSVGIcon; extern QIcon* restartSVGIcon; extern QIcon* snapshotSVGIcon; extern QIcon* emptyBarSVGIcon; extern QIcon* lockSVGIcon; extern QIcon* midiResetSVGIcon; extern QIcon* midiInitSVGIcon; extern QIcon* midiLocalOffSVGIcon; extern QIcon* midiTransformSVGIcon; extern QIcon* midiInputTransformSVGIcon; extern QIcon* midiInputTransposeSVGIcon; extern QIcon* midiInputFilterSVGIcon; extern QIcon* midiInputRemoteSVGIcon; extern QIcon* midiSyncSVGIcon; extern QIcon* midiExportImportSVGIcon; extern QIcon* cutSVGIcon; extern QIcon* copySVGIcon; extern QIcon* pasteSVGIcon; extern QIcon* pasteDialogSVGIcon; extern QIcon* pasteCloneSVGIcon; extern QIcon* copyRangeSVGIcon; extern QIcon* deleteSVGIcon; extern QIcon* pasteSelectedTrackSVGIcon; extern QIcon* pasteCloneSelectedTrackSVGIcon; extern QIcon* eyeSVGIcon; extern QIcon* eyeCrossedSVGIcon; extern QIcon* eyeGreySVGIcon; extern QIcon* nextPartSVGIcon; extern QIcon* lastPartSVGIcon; extern QIcon* selectAllSVGIcon; extern QIcon* selectAllTrackSVGIcon; extern QIcon* selectInsideLoopSVGIcon; extern QIcon* selectOutsideLoopSVGIcon; extern QIcon* selectInvertSVGIcon; extern QIcon* deselectAllSVGIcon; extern QIcon* ledGreenSVGIcon; extern QIcon* ledGreenDarkSVGIcon; extern QIcon* ledRedSVGIcon; extern QIcon* ledBlueSVGIcon; extern QIcon* ledYellowSVGIcon; extern QIcon* ledOffSVGIcon; extern QIcon* routeInSVGIcon; extern QIcon* routeOutSVGIcon; extern QIcon* routeInMidiSVGIcon; extern QIcon* routeOutMidiSVGIcon; extern QIcon* midiPortSVGIcon; extern QIcon* routerSVGIcon; extern QIcon* appearanceSVGIcon; extern QIcon* editInstrumentSVGIcon; extern QIcon* deltaSVGIcon; extern QIcon* dummySVGIcon; //---------------------------------- // Cursors //---------------------------------- extern QCursor* editpasteSCursor; extern QCursor* editpasteCloneSCursor; extern QCursor* pencilCursor; extern QCursor* glueCursor; extern QCursor* cutterCursor; extern QCursor* zoomCursor; extern QCursor* zoomAtCursor; extern QCursor* deleteCursor; extern QCursor* drawCursor; extern QCursor* mutePartsCursor; extern QCursor* handCursor; extern QCursor* closedHandCursor; //extern QCursor* magnetCursor; //extern QCursor* customMoveCursor; extern QCursor* pencilMove4WayCursor; extern QCursor* pencilMoveHorizCursor; extern QCursor* pencilMoveVertCursor; } // namespace MusEGui #endif muse-4.0.0/src/muse/importmidi.cpp000066400000000000000000000712231404105311000170770ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: importmidi.cpp,v 1.26.2.10 2009/11/05 03:14:35 terminator356 Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 "app.h" #include "song.h" #include "globals.h" #include "filedialog.h" #include "midi.h" #include "midifile.h" #include "midiport.h" #include "midiseq.h" #include "transport.h" #include "arranger.h" #include "mpevent.h" #include "event.h" #include "midictrl.h" #include "instruments/minstrument.h" #include "drummap.h" #include "xml.h" #include "audio.h" #include "gconfig.h" #include "undo.h" using std::set; using std::pair; namespace MusEGui { //--------------------------------------------------------- // importMidi //--------------------------------------------------------- void MusE::importMidi() { QString empty(""); importMidi(empty); } void MusE::importMidi(const QString &file) { QString fn; if (file.isEmpty()) { fn = MusEGui::getOpenFileName(MusEGlobal::lastMidiPath, MusEGlobal::midi_file_pattern, this, tr("MusE: Import Midi"), 0); if (fn.isEmpty()) return; MusEGlobal::lastMidiPath = fn; } else fn = file; int n = QMessageBox::question(this, appName, tr("Add midi file to current project?\n"), tr("&Add to Project"), tr("&Replace"), tr("&Abort"), 0, 2); switch (n) { case 0: // FIXME TODO REMOVE Tim. clip. Added. Comment. // Hm, importMidi() doesn't even stop the audio or idle or anything! Unsafe? // Do like in loadProjectFile() ... Hm, don't think we can stop the audio // because it needs to process commands below. // // Try this as a minimum... stopHeartBeat(); MusEGlobal::audio->msgIdle(true); importMidi(fn, true); MusEGlobal::audio->msgIdle(false); setHeartBeat(); MusEGlobal::song->update(); break; case 1: loadProjectFile(fn, false, false); // replace break; default: return; } } //--------------------------------------------------------- // importMidi // return true on error //--------------------------------------------------------- bool MusE::importMidi(const QString name, bool merge) { bool popenFlag; FILE* fp = MusEGui::fileOpen(this, name, QString(".mid"), "r", popenFlag); if (fp == 0) return true; MusECore::MidiFile mf(fp); bool rv = mf.read(); popenFlag ? pclose(fp) : fclose(fp); if (rv) { QString s(tr("Reading midifile\n ")); s += name; s += tr("\nfailed: "); s += mf.error(); QMessageBox::critical(this, QString("MusE"), s); return rv; } MusECore::MidiFileTrackList* etl = mf.trackList(); int division = mf.division(); // If the division is linear time, pass zero division to buildMidiEventList() to signify // that the event times are to be taken verbosely - we have already done the entire conversion // from time to ticks, don't let buildMidiEventList() convert it further. if(mf.divisionIsLinearTime()) division = 0; // Find the default instrument, we may need it later... MusECore::MidiInstrument* def_instr = 0; if(!MusEGlobal::config.importMidiDefaultInstr.isEmpty()) { for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { if((*i)->iname() == MusEGlobal::config.importMidiDefaultInstr) { def_instr = *i; break; } } } // // Need to set up ports and instruments first // MusECore::MidiFilePortMap* usedPortMap = mf.usedPortMap(); bool dev_changed = false; for(MusECore::iMidiFilePort imp = usedPortMap->begin(); imp != usedPortMap->end(); ++imp) { MusECore::MType midi_type = imp->second._midiType; QString instr_name = MusEGlobal::config.importInstrNameMetas ? imp->second._instrName : QString(); MusECore::MidiInstrument* typed_instr = 0; MusECore::MidiInstrument* named_instr = 0; // Find a typed instrument and a named instrument, if requested for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { MusECore::MidiInstrument* mi = *i; if(midi_type != MusECore::MT_UNKNOWN && midi_type == mi->midiType()) typed_instr = mi; if(!instr_name.isEmpty() && instr_name == mi->iname()) named_instr = mi; if((typed_instr && named_instr) || ((typed_instr && instr_name.isEmpty()) || (named_instr && midi_type == MusECore::MT_UNKNOWN))) break; // Done searching } int port = imp->first; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiDevice* md = mp->device(); // Take care of assigning devices to empty ports here rather than in midifile. //if(MusEGlobal::config.importDevNameMetas) // TODO { if(!md) { QString dev_name = imp->second._subst4DevName; md = MusEGlobal::midiDevices.find(dev_name); // Find any type of midi device - HW, synth etc. if(md) { // TEST: Hopefully shouldn't require any routing saves/restorations as in midi port config set device name... MusEGlobal::audio->msgSetMidiDevice(mp, md); // TEST: Hopefully can get away with this ouside the loop below... // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. //MusEGlobal::muse->changeConfig(true); // save configuration file //MusEGlobal::audio->msgUpdateSoloStates(); //MusEGlobal::song->update(); dev_changed = true; } else printf("importMidi error: assign to empty port: device not found: %s\n", dev_name.toLatin1().constData()); } } MusECore::MidiInstrument* instr = 0; // Priority is exact named instrument over a typed instrument. // This allows a named instrument plus a typed sysex, and the name will take priority. // But it is possible that named mismatches may occur. So this named/typed order is user-selectable. if(named_instr && (!typed_instr || MusEGlobal::config.importInstrNameMetas)) { instr = named_instr; if(MusEGlobal::debugMsg) printf("port:%d named instrument found:%s\n", port, instr->iname().toLatin1().constData()); } else if(typed_instr) { instr = typed_instr; if(MusEGlobal::debugMsg) printf("port:%d typed instrument found:%s\n", port, instr->iname().toLatin1().constData()); } else if(def_instr) { instr = def_instr; if(MusEGlobal::debugMsg) printf("port:%d no named or typed instrument found. Using default:%s\n", port, instr->iname().toLatin1().constData()); } else { instr = MusECore::genericMidiInstrument; if(MusEGlobal::debugMsg) printf("port:%d no named, typed, or default instrument found! Using:%s\n", port, instr->iname().toLatin1().constData()); } // If the instrument is one of the three standard GM, GS, or XG, mark the usedPort as "ch 10 is drums". // Otherwise it's anybody's guess what channel(s) drums are on. // Code is a bit HACKISH just to accomplish passing this bool value to the next stage, where tracks are created. if(instr->midiType() != MusECore::MT_UNKNOWN) imp->second._isStandardDrums = true; // Set the device's instrument - ONLY for non-synths because they provide their own. if(!md || (md->deviceType() != MusECore::MidiDevice::SYNTH_MIDI)) { // this overwrites any instrument set for this port: if(mp->instrument() != instr) mp->changeInstrument(instr); } } if(dev_changed) { // TEST: Hopefully can get away with this here instead of inside the loop above... // TEST: Are these really necessary as in midi port config set device name? // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); // save configuration file MusEGlobal::audio->msgUpdateSoloStates(); // MusEGlobal::song->update(); } // // create MidiTrack and copy events to ->events() // - combine note on/off events // - calculate tick value for internal resolution // for (MusECore::ciMidiFileTrack t = etl->cbegin(); t != etl->cend(); ++t) { MusECore::MPEventList& el = ((*t)->events); if (el.empty()) continue; // // if we split the track, SYSEX and META events go into // the first target track bool first = true; // vastly changed by flo: replaced that silly loop // with that already_processed-set-check. // this makes stuff really fast :) MusECore::iMPEvent ev; set< pair > already_processed; for (ev = el.begin(); ev != el.end(); ++ev) { if (ev->type() != MusECore::ME_SYSEX && ev->type() != MusECore::ME_META) { int channel=ev->channel(); int port=ev->port(); if (already_processed.find(pair(channel, port)) == already_processed.end()) { MusECore::MidiTrack* track = new MusECore::MidiTrack(); if ((*t)->_isDrumTrack) { // Obsolete. There is only 'New' drum tracks now. // if (MusEGlobal::config.importMidiNewStyleDrum) track->setType(MusECore::Track::DRUM); // else // track->setType(MusECore::Track::DRUM); } track->setOutChannel(channel); track->setOutPort(port); // MusECore::MidiPort* mport = &MusEGlobal::midiPorts[port]; buildMidiEventList(&track->events, el, track, division, first, false); // Don't do loops. // The first track of a format 1 file is special by convention. // It is SUPPOSED to contain only timing (tempo/sig/marker etc.) events, no notes. // Those are handled and stripped out by the builder. // Don't create a track if there's nothing else of value to be found in the event list // after the build - even if the track has a name and comment. // Do the same even for a format 0 track. // For other tracks after the first one, it is OK if they are blank // because the creator wanted it that way. Sometimes they are used as separators. if(t == etl->begin() && track->events.empty()/* && track->name().simplified().isEmpty() && track->comment().simplified().isEmpty()*/) { delete track; } else { already_processed.insert(pair(channel, port)); first = false; // Comment Added by T356. // Hmm. buildMidiEventList already takes care of this. // But it seems to work. How? Must test. //if (channel == 9 && instr->midiType() != MT_UNKNOWN) { MusECore::ciMidiFilePort imp = usedPortMap->find(port); if(imp != usedPortMap->end() && imp->second._isStandardDrums && channel == 9) { // A bit HACKISH, see above // Obsolete. There is only 'New' drum tracks now. // if (MusEGlobal::config.importMidiNewStyleDrum) track->setType(MusECore::Track::DRUM); // else // { // track->setType(MusECore::Track::DRUM); // // remap drum pitch with drumOutmap (was: Inmap. flo93 thought this was wrong) // for (MusECore::iEvent i = track->events.begin(); i != track->events.end(); ++i) { // MusECore::Event ev = i->second; // if (ev.isNote()) { // int pitch = MusEGlobal::drumOutmap[ev.pitch()]; // ev.setPitch(pitch); // } // else // if(ev.type() == MusECore::Controller) // { // int ctl = ev.dataA(); // MusECore::MidiController *mc = mport->drumController(ctl); // if(mc) // ev.setA((ctl & ~0xff) | MusEGlobal::drumOutmap[ctl & 0x7f]); // } // } // } } processTrack(track); MusEGlobal::song->insertTrack0(track, -1); MusECore::addPortCtrlEvents(track); } } } } if (first) { // // track does only contain non-channel messages // (SYSEX or META) // MusECore::MidiTrack* track = new MusECore::MidiTrack(); track->setOutChannel(0); track->setOutPort(0); buildMidiEventList(&track->events, el, track, division, true, false); // Do SysexMeta. Don't do loops. // The first track of a format 1 file is special by convention. // It is SUPPOSED to contain only timing (tempo/sig/marker etc.) events, no notes. // Those are handled and stripped out by the builder. // Don't create a track if there's nothing else of value to be found in the event list // after the build - even if the track has a name and comment. // Do the same even for a format 0 track. // For other tracks after the first one, it is OK if they are blank // because the creator wanted it that way. Sometimes they are used as separators. if(t == etl->begin() && track->events.empty()/* && track->name().simplified().isEmpty() && track->comment().simplified().isEmpty()*/) { delete track; } else { processTrack(track); MusEGlobal::song->insertTrack0(track, -1); MusECore::addPortCtrlEvents(track); } } } if (!merge) { MusECore::TrackList* tl = MusEGlobal::song->tracks(); if (!tl->empty()) { MusECore::Track* track = tl->front(); track->setSelected(true); } MusEGlobal::song->initLen(); int z, n; MusEGlobal::sigmap.timesig(0, z, n); int tempo = MusEGlobal::tempomap.tempo(0); transport->setTimesig(z, n); transport->setTempo(tempo); bool masterF = !MusEGlobal::tempomap.empty(); MusEGlobal::song->setMasterFlag(masterF); MusEGlobal::song->updatePos(); _arranger->reset(); } else { MusEGlobal::song->initLen(); } return false; } //--------------------------------------------------------- // processTrack // divide events into parts //--------------------------------------------------------- void MusE::processTrack(MusECore::MidiTrack* track) { MusECore::EventList& tevents = track->events; if (tevents.empty()) return; //--------------------------------------------------- // Parts ermitteln // die Midi-Spuren werden in Parts aufgebrochen; // ein neuer Part wird bei einer L�cke von einem // Takt gebildet; die L�nge wird jeweils auf // Takte aufgerundet und aligned //--------------------------------------------------- MusECore::PartList* pl = track->parts(); int lastTick = 0; for (MusECore::ciEvent i = tevents.begin(); i != tevents.end(); ++i) { const MusECore::Event& event = i->second; int epos = event.tick() + event.lenTick(); if (epos > lastTick) lastTick = epos; } QString partname = track->name(); int len = MusEGlobal::song->roundUpBar(lastTick+1); // p3.3.27 if(MusEGlobal::config.importMidiSplitParts) { int bar2, beat; unsigned tick; MusEGlobal::sigmap.tickValues(len, &bar2, &beat, &tick); int lastOff = 0; int st = -1; // start tick current part int x1 = 0; // start tick current measure int x2 = 0; // end tick current measure for (int bar = 0; bar < bar2; ++bar, x1 = x2) { x2 = MusEGlobal::sigmap.bar2tick(bar+1, 0, 0); if (lastOff > x2) { continue; } MusECore::iEvent i1 = tevents.lower_bound(x1); MusECore::iEvent i2 = tevents.lower_bound(x2); if (i1 == i2) { // empty? if (st != -1) { MusECore::MidiPart* part = new MusECore::MidiPart(track); part->setTick(st); part->setLenTick((lastOff > x1 ? x2 : x1) - st); part->setName(partname); pl->add(part); st = -1; } } else { if (st == -1) st = x1; // begin new part //HACK: //lastOff: for (MusECore::ciEvent i = i1; i != i2; ++i) { const MusECore::Event& event = i->second; if (event.type() == MusECore::Note) { int off = event.tick() + event.lenTick(); if (off > lastOff) lastOff = off; } } } } if (st != -1) { MusECore::MidiPart* part = new MusECore::MidiPart(track); part->setTick(st); part->setLenTick(x2-st); part->setName(partname); pl->add(part); } } else { // Just one long part... MusECore::MidiPart* part = new MusECore::MidiPart(track); part->setTick(0); part->setLenTick(len); part->setName(partname); pl->add(part); } //------------------------------------------------------------- // assign events to parts //------------------------------------------------------------- for (MusECore::iPart p = pl->begin(); p != pl->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart*)(p->second); int stick = part->tick(); int etick = part->tick() + part->lenTick(); MusECore::iEvent r1 = tevents.lower_bound(stick); MusECore::iEvent r2 = tevents.lower_bound(etick); int startTick = part->tick(); for (MusECore::iEvent i = r1; i != r2; ++i) { MusECore::Event& ev = i->second; int ntick = ev.tick() - startTick; ev.setTick(ntick); part->addEvent(ev); } tevents.erase(r1, r2); } if (tevents.size()) printf("-----------events left: %zd\n", tevents.size()); for (MusECore::ciEvent i = tevents.begin(); i != tevents.end(); ++i) { printf("%d===\n", i->first); i->second.dump(); } // all events should be processed: if (!tevents.empty()) printf("THIS SHOULD NEVER HAPPEN: not all events processed at the end of MusE::processTrack()!\n"); } //--------------------------------------------------------- // importController //--------------------------------------------------------- void MusE::importController(int channel, MusECore::MidiPort* mport, int n) { MusECore::MidiInstrument* instr = mport->instrument(); MusECore::MidiCtrlValListList* vll = mport->controller(); MusECore::iMidiCtrlValList i = vll->find(channel, n); if (i != vll->end()) return; // controller does already exist MusECore::MidiController* ctrl = 0; // Search the instrument's controller lists (including midnam controllers). const int patch = mport->hwCtrlState(channel, MusECore::CTRL_PROGRAM); if (instr) { ctrl = instr->findController(n, channel, patch); } if (ctrl == 0) { printf("controller 0x%x not defined for instrument %s, channel %d, patch:%d\n", n, instr->iname().toLatin1().constData(), channel, patch); } MusECore::MidiCtrlValList* newValList = new MusECore::MidiCtrlValList(n); vll->add(channel, newValList); } //--------------------------------------------------------- // importPart //--------------------------------------------------------- void MusE::importPart() { unsigned curPos = MusEGlobal::song->cpos(); MusECore::TrackList* tracks = MusEGlobal::song->tracks(); MusECore::Track* track = 0; // Get first selected track: for (MusECore::iTrack i = tracks->begin(); i != tracks->end(); i++) { MusECore::Track* t = *i; if (t->selected()) { // Changed by T356. Support mixed .mpt files. if (t->isMidiTrack() || t->type() == MusECore::Track::WAVE) { track = t; break; } else { QMessageBox::warning(this, QString("MusE"), tr("Import part is only valid for midi and wave tracks!")); return; } } } if (track) { bool loadAll; QString filename = MusEGui::getOpenFileName(QString(""), MusEGlobal::part_file_pattern, this, tr("MusE: load part"), &loadAll); if (!filename.isEmpty()){ // Make a backup of the current clone list, to retain any 'copy' items, // so that pasting works properly after. MusECore::CloneList copyCloneList = MusEGlobal::cloneList; // Clear the clone list to prevent any dangerous associations with // current non-original parts. MusEGlobal::cloneList.clear(); importPartToTrack(filename, curPos, track); // Restore backup of the clone list, to retain any 'copy' items, // so that pasting works properly after. MusEGlobal::cloneList.clear(); MusEGlobal::cloneList = copyCloneList; } } else { QMessageBox::warning(this, QString("MusE"), tr("No track selected for import")); } } //--------------------------------------------------------- // importPartToTrack //--------------------------------------------------------- void MusE::importPartToTrack(QString& filename, unsigned tick, MusECore::Track* track) { bool popenFlag = false; FILE* fp = MusEGui::fileOpen(this, filename, ".mpt", "r", popenFlag, false, false); if(fp) { MusECore::Xml xml = MusECore::Xml(fp); bool firstPart = true; int posOffset = 0; int notDone = 0; int done = 0; bool end = false; MusEGlobal::song->startUndo(); for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: end = true; break; case MusECore::Xml::TagStart: if (tag == "part") { // Read the part. MusECore::Part* p = 0; p = MusECore::Part::readFromXml(xml, track); // If it could not be created... if(!p) { // Increment the number of parts not done and break. ++notDone; break; } // Increment the number of parts done. ++done; if(firstPart) { firstPart=false; posOffset = tick - p->tick(); } p->setTick(p->tick() + posOffset); // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddPart, p), MusECore::Song::OperationUndoable); } else xml.unknown("MusE::importPartToTrack"); break; case MusECore::Xml::TagEnd: break; default: end = true; break; } if(end) break; } fclose(fp); MusEGlobal::song->endUndo(SC_PART_INSERTED); if(notDone) { int tot = notDone + done; QMessageBox::critical(this, QString("MusE"), (tot > 1 ? tr("%n part(s) out of %1 could not be imported.\nLikely the selected track is the wrong type.","",notDone).arg(tot) : tr("%n part(s) could not be imported.\nLikely the selected track is the wrong type.","",notDone))); } return; } } } // namespace MuseGui muse-4.0.0/src/muse/instruments/000077500000000000000000000000001404105311000166045ustar00rootroot00000000000000muse-4.0.0/src/muse/instruments/CMakeLists.txt000066400000000000000000000034221404105311000213450ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB instruments_source_files minstrument.cpp ) ## ## Define target ## add_library ( instruments ${MODULES_BUILD} ${instruments_source_files} ${instruments_mocs} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${instruments_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( instruments PROPERTIES OUTPUT_NAME muse_instruments ) ## ## Linkage ## target_link_libraries ( instruments ${QT_LIBRARIES} icons widgets ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS instruments DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/instruments/ccontrolbase.ui000066400000000000000000000512551404105311000216310ustar00rootroot00000000000000 MidiControllerEditDialogBase MidiControllerEditDialogBase 0 0 698 457 MusE: Define Midi Controller true unnamed 11 6 Layout2 unnamed 0 6 buttonNew &Add Alt+A false create new entry pressing the New button you create a new entry in the MusE list of defined controllers buttonDelete &Delete false delete selected entry Spacer1 Horizontal Expanding 20 0 buttonOk &OK false buttonApply A&pply false buttonCancel &Cancel false textLabel1 Predefined Controller: Name true true Type true true H-Ctrl true false L-Ctrl true false Min Val true false Max Val true false viewController StyledPanel Sunken true list of defined controllers This is the MusE list of defined controllers. layout2 unnamed textLabel2 Managed Controller for Port midiPortsList textLabel3 Channel midiChannel 16 1 listController This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. GroupBox1 Properties unnamed 11 6 TextLabel1 Name layout3 unnamed TextLabel1_2 Min Value 10 spinboxMin 16384 -16385 TextLabel2_2 Max Value 10 spinboxMax 16384 -16385 127 TextLabel2 Type entryName layout5 unnamed Control7 Control14 RPN NRPN RPN14 NRPN14 Pitch Program comboType TextLabel3 H-Ctrl 10 spinboxHCtrlNo 127 0 1 Midi Controller Number High Byte TextLabel2_3 L-Ctrl 10 spinboxLCtrlNo 127 0 1 Midi Controller Number Low Byte textLabel4 Range buttonCancel clicked() MidiControllerEditDialogBase reject() buttonOk clicked() MidiControllerEditDialogBase accept() viewController entryName comboType spinboxHCtrlNo spinboxLCtrlNo spinboxMin spinboxMax buttonNew buttonDelete buttonOk buttonApply buttonCancel muse-4.0.0/src/muse/instruments/midictrledit.cpp000066400000000000000000000604421404105311000217730ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midictrledit.cpp,v 1.1.1.1.2.2 2008/08/18 00:15:24 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "app.h" #include "midiport.h" #include "mididev.h" #include "midictrl.h" #include "midictrledit.h" #include "minstrument.h" #include "song.h" #include "xml.h" #include "filedialog.h" #include "globals.h" namespace MusEGui { MidiControllerEditDialog* midiControllerEditDialog; static MidiController predefinedMidiController[] = { MidiController(QString("Pitch"), 0x40000, -8192, +8191, 0), }; enum { COL_NAME = 0, COL_TYPE, COL_HNUM, COL_LNUM, COL_MIN, COL_MAX }; //--------------------------------------------------------- // addControllerToView //--------------------------------------------------------- void MidiControllerEditDialog::addControllerToView(MidiController* mctrl) { QString hnum; QString lnum; QString min; QString max; int n = mctrl->num(); int h = (n >> 8) & 0x7f; int l = n & 0x7f; MidiController::ControllerType t = midiControllerType(n); switch(t) { case MidiController::Controller7: hnum = "---"; lnum.setNum(l); min.setNum(mctrl->minVal()); max.setNum(mctrl->maxVal()); break; case MidiController::RPN: case MidiController::NRPN: case MidiController::RPN14: case MidiController::NRPN14: case MidiController::Controller14: hnum.setNum(h); lnum.setNum(l); min.setNum(mctrl->minVal()); max.setNum(mctrl->maxVal()); break; case MidiController::Pitch: hnum = "---"; lnum = "---"; min.setNum(mctrl->minVal()); max.setNum(mctrl->maxVal()); break; default: hnum = "---"; lnum = "---"; min.setNum(0); max.setNum(0); break; } new Q3ListViewItem(viewController, mctrl->name(), int2ctrlType(t), hnum, lnum, min, max ); } //--------------------------------------------------------- // MidiControllerEditDialog //--------------------------------------------------------- MidiControllerEditDialog::MidiControllerEditDialog(QWidget* parent, const char* name, bool modal, Qt::WindowFlags fl) : MidiControllerEditDialogBase(parent, name, modal, fl) { _lastPort = midiPortsList->currentItem(); viewController->setColumnAlignment(COL_HNUM, Qt::AlignCenter); viewController->setColumnAlignment(COL_LNUM, Qt::AlignCenter); viewController->setColumnAlignment(COL_MIN, Qt::AlignCenter); viewController->setColumnAlignment(COL_MAX, Qt::AlignCenter); viewController->setColumnWidthMode(COL_NAME, Q3ListView::Maximum); // populate list of predefined controller updatePredefinedList(); // populate ports pulldown updateMidiPortsList(); connect(buttonNew, SIGNAL(clicked()), SLOT(ctrlAdd())); connect(buttonDelete, SIGNAL(clicked()), SLOT(ctrlDelete())); connect(entryName, SIGNAL(textChanged(const QString&)), SLOT(nameChanged(const QString&))); connect(comboType, SIGNAL(activated(const QString&)), SLOT(typeChanged(const QString&))); connect(spinboxHCtrlNo, SIGNAL(valueChanged(int)), SLOT(valueHChanged(int))); connect(spinboxLCtrlNo, SIGNAL(valueChanged(int)), SLOT(valueLChanged(int))); connect(spinboxMin, SIGNAL(valueChanged(int)), SLOT(minChanged(int))); connect(spinboxMax, SIGNAL(valueChanged(int)), SLOT(maxChanged(int))); connect(viewController, SIGNAL(selectionChanged()), SLOT(controllerChanged())); connect(buttonApply, SIGNAL(clicked()), SLOT(apply())); connect(midiPortsList, SIGNAL(activated(int)), SLOT(portChanged(int))); updateViewController(); _modified = false; buttonApply->setEnabled(false); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MidiControllerEditDialog::songChanged(MusECore::SongChangedStruct_t flags) { // Is it simply a midi controller value adjustment? Forget it. if(flags == SC_MIDI_CONTROLLER) return; if(flags & (SC_CONFIG | SC_MIDI_CONTROLLER)) { midiPortsList->blockSignals(true); updatePredefinedList(); updateMidiPortsList(); updateViewController(); midiPortsList->blockSignals(false); } } //--------------------------------------------------------- // updatePredefinedList //--------------------------------------------------------- void MidiControllerEditDialog::updatePredefinedList() { listController->clear(); int size = sizeof(predefinedMidiController) / sizeof(*predefinedMidiController); for (int i = 0; i < size; ++i) listController->insertItem(predefinedMidiController[i].name()); listController->setSelected(0, true); } //--------------------------------------------------------- // updateMidiPortsList //--------------------------------------------------------- void MidiControllerEditDialog::updateMidiPortsList() { midiPortsList->clear(); for (int i = 0; i < MIDI_PORTS; ++i) { MidiPort* port = &midiPorts[i]; MidiDevice* dev = port->device(); QString name = QString("%1(%2)").arg(port->portno() + 1).arg(dev ? dev->name() : QString("none")); midiPortsList->insertItem(name, i); } _lastPort = midiPortsList->currentItem(); } //--------------------------------------------------------- // updateViewController //--------------------------------------------------------- void MidiControllerEditDialog::updateViewController() { int mpidx = midiPortsList->currentItem(); viewController->clear(); MidiInstrument* mi = midiPorts[mpidx].instrument(); MidiControllerList* mcl = mi->controller(); for (iMidiController i = mcl->begin(); i != mcl->end(); ++i) addControllerToView(*i); viewController->blockSignals(true); viewController->setCurrentItem(viewController->firstChild()); controllerChanged(viewController->currentItem()); viewController->blockSignals(false); setModified(false); } //--------------------------------------------------------- // reject //--------------------------------------------------------- void MidiControllerEditDialog::setModified(bool v) { if(v == _modified) return; _modified = v; if(v) { buttonApply->setEnabled(true); } else { buttonApply->setEnabled(false); } } //--------------------------------------------------------- // reject //--------------------------------------------------------- void MidiControllerEditDialog::reject() { // Restore the list before closing this dialog. updateViewController(); //setModified(false); MidiControllerEditDialogBase::reject(); } //--------------------------------------------------------- // apply //--------------------------------------------------------- void MidiControllerEditDialog::apply() { int mpidx = midiPortsList->currentItem(); MidiInstrument* mi = midiPorts[mpidx].instrument(); MidiControllerList* mcl = mi->controller(); mcl->clear(); Q3ListViewItem* item = viewController->firstChild(); int hval; int lval; while (item) { hval = item->text(COL_HNUM).toInt(); lval = item->text(COL_LNUM).toInt(); MidiController* c = new MidiController(); c->setName(item->text(COL_NAME)); MidiController::ControllerType type = ctrlType2Int(item->text(COL_TYPE)); switch(type) { case MidiController::Controller7: c->setNum(lval); break; case MidiController::Controller14: c->setNum((hval << 8 | lval) | CTRL_14_OFFSET); break; case MidiController::RPN: c->setNum((hval << 8 | lval) | CTRL_RPN_OFFSET); break; case MidiController::NRPN: c->setNum((hval << 8 | lval) | CTRL_NRPN_OFFSET); break; case MidiController::RPN14: c->setNum((hval << 8 | lval) | CTRL_RPN14_OFFSET); break; case MidiController::NRPN14: c->setNum((hval << 8 | lval) | CTRL_NRPN14_OFFSET); break; case MidiController::Program: c->setNum(CTRL_PROGRAM); break; case MidiController::Pitch: c->setNum(CTRL_PITCH); break; default: break; } if(type == MidiController::Program) { c->setMinVal(0); c->setMaxVal(0xffffff); } else { c->setMinVal(item->text(COL_MIN).toInt()); c->setMaxVal(item->text(COL_MAX).toInt()); } mcl->push_back(c); item = item->nextSibling(); } MusEGlobal::song->update(SC_CONFIG | SC_MIDI_CONTROLLER); } //--------------------------------------------------------- // ctrlAdd //--------------------------------------------------------- void MidiControllerEditDialog::ctrlAdd() { Q3ListBoxItem* item = listController->selectedItem(); if (item == 0) return; QString name = item->text(); int size = sizeof(predefinedMidiController) / sizeof(*predefinedMidiController); for (int i = 0; i < size; ++i) { MidiController* c = &predefinedMidiController[i]; if (c->name() != name) continue; MidiController::ControllerType t = midiControllerType(c->num()); QString type = int2ctrlType(t); QString min, max; QString hno, lno; int h = (c->num() >> 8) & 0x7f; int l = c->num() & 0x7f; switch(t) { case MidiController::Controller7: min.setNum(c->minVal()); max.setNum(c->maxVal()); hno = "---"; lno.setNum(l); break; case MidiController::RPN: case MidiController::NRPN: case MidiController::RPN14: case MidiController::NRPN14: case MidiController::Controller14: min.setNum(c->minVal()); max.setNum(c->maxVal()); hno.setNum(h); lno.setNum(l); break; case MidiController::Pitch: min.setNum(c->minVal()); max.setNum(c->maxVal()); hno = "---"; lno = "---"; break; default: hno = "---"; lno = "---"; min.setNum(0); max.setNum(0); break; } Q3ListViewItem* item = new Q3ListViewItem(viewController, name, type, hno, lno, min, max); viewController->blockSignals(true); viewController->setCurrentItem(item); controllerChanged(item); viewController->blockSignals(false); setModified(true); break; } } //--------------------------------------------------------- // ctrlDelete //--------------------------------------------------------- void MidiControllerEditDialog::ctrlDelete() { Q3ListViewItem* item = viewController->currentItem(); if (item == 0) return; delete item; setModified(true); } //--------------------------------------------------------- // accept //--------------------------------------------------------- void MidiControllerEditDialog::accept() { apply(); MidiControllerEditDialogBase::accept(); } //--------------------------------------------------------- // portChanged //--------------------------------------------------------- void MidiControllerEditDialog::portChanged(int n) { if(n == _lastPort) return; _lastPort = n; //listController->blockSignals(true); DELETETHIS //midiPortsList->blockSignals(true); //viewController->blockSignals(true); //updatePredefinedList(); //updateMidiPortsList(); //reject(); // populate list updateViewController(); //viewController->setCurrentItem(viewController->firstChild()); DELETETHIS //controllerChanged(viewController->currentItem()); //listController->blockSignals(false); //midiPortsList->blockSignals(false); //viewController->blockSignals(false); } //--------------------------------------------------------- // nameChanged //--------------------------------------------------------- void MidiControllerEditDialog::nameChanged(const QString& s) { Q3ListViewItem* item = viewController->currentItem(); if (item == 0) return; item->setText(COL_NAME, s); setModified(true); } //--------------------------------------------------------- // typeChanged //--------------------------------------------------------- void MidiControllerEditDialog::typeChanged(const QString& s) { Q3ListViewItem* item = viewController->currentItem(); if (item == 0) return; item->setText(COL_TYPE, s); switch(ctrlType2Int(s)) { case MidiController::Controller14: case MidiController::RPN14: case MidiController::NRPN14: item->setText(COL_LNUM, QString("0")); item->setText(COL_HNUM, QString("0")); item->setText(COL_MIN, QString("0")); item->setText(COL_MAX, QString("16383")); break; case MidiController::Controller7: item->setText(COL_MIN, QString("0")); item->setText(COL_MAX, QString("127")); item->setText(COL_LNUM, QString("0")); item->setText(COL_HNUM, QString("---")); break; case MidiController::RPN: case MidiController::NRPN: item->setText(COL_MIN, QString("0")); item->setText(COL_MAX, QString("127")); item->setText(COL_LNUM, QString("0")); item->setText(COL_HNUM, QString("0")); break; case MidiController::Program: item->setText(COL_MIN, QString("---")); item->setText(COL_MAX, QString("---")); item->setText(COL_LNUM, QString("---")); item->setText(COL_HNUM, QString("---")); break; case MidiController::Pitch: item->setText(COL_MIN, QString("-8192")); item->setText(COL_MAX, QString("8191")); item->setText(COL_LNUM, QString("---")); item->setText(COL_HNUM, QString("---")); break; default: break; } setModified(true); controllerChanged(item); } //--------------------------------------------------------- // valueHChanged //--------------------------------------------------------- void MidiControllerEditDialog::valueHChanged(int val) { Q3ListViewItem* item = viewController->currentItem(); if (item == 0) return; QString s; s.setNum(val); item->setText(COL_HNUM, s); setModified(true); } //--------------------------------------------------------- // valueLChanged //--------------------------------------------------------- void MidiControllerEditDialog::valueLChanged(int val) { Q3ListViewItem* item = viewController->currentItem(); if (item == 0) return; QString s; s.setNum(val); item->setText(COL_LNUM, s); setModified(true); } //--------------------------------------------------------- // controllerChanged //--------------------------------------------------------- void MidiControllerEditDialog::controllerChanged() { Q3ListViewItem* item = viewController->selectedItem(); controllerChanged(item); } void MidiControllerEditDialog::controllerChanged(Q3ListViewItem* item) { if (item == 0) { entryName->setEnabled(false); comboType->setEnabled(false); spinboxHCtrlNo->setEnabled(false); spinboxLCtrlNo->setEnabled(false); spinboxMin->setEnabled(false); spinboxMax->setEnabled(false); return; } entryName->blockSignals(true); comboType->blockSignals(true); spinboxHCtrlNo->blockSignals(true); spinboxLCtrlNo->blockSignals(true); spinboxMin->blockSignals(true); spinboxMax->blockSignals(true); entryName->setEnabled(true); entryName->setText(item->text(COL_NAME)); comboType->setCurrentItem(int(ctrlType2Int(item->text(COL_TYPE)))); switch (ctrlType2Int(item->text(COL_TYPE))) { case MidiController::Controller7: comboType->setEnabled(true); spinboxHCtrlNo->setEnabled(false); spinboxLCtrlNo->setEnabled(true); spinboxMin->setEnabled(true); spinboxMax->setEnabled(true); spinboxHCtrlNo->setValue(0); spinboxLCtrlNo->setValue(item->text(COL_LNUM).toInt()); spinboxMin->setRange(0, 127); spinboxMax->setRange(0, 127); spinboxMin->setValue(item->text(COL_MIN).toInt()); spinboxMax->setValue(item->text(COL_MAX).toInt()); break; case MidiController::RPN: case MidiController::NRPN: comboType->setEnabled(true); spinboxHCtrlNo->setEnabled(true); spinboxLCtrlNo->setEnabled(true); spinboxMin->setEnabled(true); spinboxMax->setEnabled(true); spinboxHCtrlNo->setValue(item->text(COL_HNUM).toInt()); spinboxLCtrlNo->setValue(item->text(COL_LNUM).toInt()); spinboxMin->setRange(0, 127); spinboxMax->setRange(0, 127); spinboxMin->setValue(item->text(COL_MIN).toInt()); spinboxMax->setValue(item->text(COL_MAX).toInt()); break; case MidiController::Controller14: case MidiController::RPN14: case MidiController::NRPN14: comboType->setEnabled(true); spinboxHCtrlNo->setEnabled(true); spinboxLCtrlNo->setEnabled(true); spinboxMin->setEnabled(true); spinboxMax->setEnabled(true); spinboxHCtrlNo->setValue(item->text(COL_HNUM).toInt()); spinboxLCtrlNo->setValue(item->text(COL_LNUM).toInt()); spinboxMin->setRange(0, 16383); spinboxMax->setRange(0, 16383); spinboxMin->setValue(item->text(COL_MIN).toInt()); spinboxMax->setValue(item->text(COL_MAX).toInt()); break; case MidiController::Pitch: comboType->setEnabled(true); spinboxHCtrlNo->setEnabled(false); spinboxLCtrlNo->setEnabled(false); spinboxMin->setEnabled(true); spinboxMax->setEnabled(true); spinboxHCtrlNo->setValue(0); spinboxLCtrlNo->setValue(0); spinboxMin->setRange(-8192, 8191); spinboxMax->setRange(-8192, 8191); spinboxMin->setValue(item->text(COL_MIN).toInt()); spinboxMax->setValue(item->text(COL_MAX).toInt()); break; case MidiController::Program: comboType->setEnabled(true); spinboxHCtrlNo->setEnabled(false); spinboxLCtrlNo->setEnabled(false); spinboxMin->setEnabled(false); spinboxMax->setEnabled(false); spinboxHCtrlNo->setValue(0); spinboxLCtrlNo->setValue(0); spinboxMin->setRange(0, 0); spinboxMax->setRange(0, 0); spinboxMin->setValue(0); spinboxMax->setValue(0); break; default: break; } entryName->blockSignals(false); comboType->blockSignals(false); spinboxHCtrlNo->blockSignals(false); spinboxLCtrlNo->blockSignals(false); spinboxMin->blockSignals(false); spinboxMax->blockSignals(false); } //--------------------------------------------------------- // minChanged //--------------------------------------------------------- void MidiControllerEditDialog::minChanged(int val) { Q3ListViewItem* item = viewController->currentItem(); if (item == 0) return; QString s; s.setNum(val); item->setText(COL_MIN, s); if(val > item->text(COL_MAX).toInt()) { spinboxMax->blockSignals(true); spinboxMax->setValue(val); item->setText(COL_MAX, s); spinboxMax->blockSignals(false); } setModified(true); } //--------------------------------------------------------- // maxChanged //--------------------------------------------------------- void MidiControllerEditDialog::maxChanged(int val) { Q3ListViewItem* item = viewController->currentItem(); if (item == 0) return; QString s; s.setNum(val); item->setText(COL_MAX, s); if(val < item->text(COL_MIN).toInt()) { spinboxMin->blockSignals(true); spinboxMin->setValue(val); item->setText(COL_MIN, s); spinboxMin->blockSignals(false); } setModified(true); } //--------------------------------------------------------- // configMidiController //--------------------------------------------------------- void configMidiController() { if (midiControllerEditDialog == 0) { midiControllerEditDialog = new MidiControllerEditDialog(); midiControllerEditDialog->show(); } else { if(midiControllerEditDialog->isShown()) midiControllerEditDialog->hide(); else midiControllerEditDialog->show(); } } } // namespace MusEGui muse-4.0.0/src/muse/instruments/midictrledit.h000066400000000000000000000045521404105311000214400ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midictrledit.h,v 1.1.1.1.2.1 2008/08/18 00:15:25 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDICTRLEDIT_H__ #define __MIDICTRLEDIT_H__ #include "type_defs.h" #include "ccontrolbase.h" #include "midictrl.h" //--------------------------------------------------------- // MidiControllerEditDialog //--------------------------------------------------------- class MidiControllerEditDialog : public MidiControllerEditDialogBase { Q_OBJECT int _lastPort; bool _modified; void addControllerToView(MidiController* mctrl); void mergeReplace(bool replace); void updatePredefinedList(); void updateMidiPortsList(); void updateViewController(); void setModified(bool); private slots: void ctrlAdd(); void ctrlDelete(); virtual void accept(); virtual void reject(); void apply(); void nameChanged(const QString&); void typeChanged(const QString&); void valueHChanged(int); void valueLChanged(int); void controllerChanged(Q3ListViewItem*); void controllerChanged(); void minChanged(int); void maxChanged(int); void portChanged(int); void songChanged(MusECore::SongChangedStruct_t); public: MidiControllerEditDialog(QWidget* parent = 0, const char* name = 0, bool modal = false, Qt::WindowFlags fl = 0); }; extern MidiControllerEditDialog* midiControllerEditDialog; extern void configMidiController(); #endif muse-4.0.0/src/muse/instruments/minstrument.cpp000066400000000000000000002673131404105311000217110ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: minstrument.cpp,v 1.10.2.5 2009/03/28 01:46:10 terminator356 Exp $ // // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include #include "minstrument.h" #include "mididev.h" #include "midiport.h" #include "audio.h" #include "midi_consts.h" #include "globals.h" #include "midictrl.h" #include "gconfig.h" #include "helper.h" #include "menutitleitem.h" #include "synth.h" #include "icons.h" // Forwards from header: #include "event.h" #include "popupmenu.h" #include "midi_controller.h" #include "mpevent.h" #include "xml.h" #ifdef _USE_INSTRUMENT_OVERRIDES_ namespace MusEGlobal { // This list holds instrument drum map overrides read from config. // Whenever an instrument has been loaded it will adopt any corresponding item in this list. // (Instruments are loaded long after config is loaded. So we need this 'holding' list.) MusECore::WorkingDrumMapInstrumentList workingDrumMapInstrumentList; } #endif namespace MusECore { MidiInstrumentList midiInstruments; MidiInstrument* genericMidiInstrument; //--------------------------------------------------------- // string2sysex // Return -1 if cannot be converted. //--------------------------------------------------------- int string2sysex(const QString& s, unsigned char** data) { QByteArray ba = s.toLatin1(); const char* src = ba.constData(); char buffer[2048]; char* dst = buffer; if(src) { while (*src) { while (*src == ' ' || *src == '\n') { ++src; } if(!(*src)) break; char* ep; long val = strtol(src, &ep, 16); if (ep == src) { printf("string2sysex: Cannot convert string to sysex %s\n", src); return -1; } src = ep; // Strip all f0 and f7 (whether accidental or on purpose enclosing etc). if(val == MusECore::ME_SYSEX || val == MusECore::ME_SYSEX_END) continue; *dst++ = val; if (dst - buffer >= 2048) { printf("string2sysex: Hex String too long (2048 bytes limit)\n"); return -1; } } } int len = dst - buffer; if(len > 0) { unsigned char* b = new unsigned char[len]; memcpy(b, buffer, len); *data = b; } else *data = 0; return len; } //--------------------------------------------------------- // sysex2string //--------------------------------------------------------- QString sysex2string(int len, unsigned char* data) { QString d; for (int i = 0; i < len; ++i) { if ((i > 0) && ((i % 8)==0)) { d += QString("\n"); } else if (i) d += QString(" "); // Strip all f0 and f7 (whether accidental or on purpose enclosing etc). if(data[i] == MusECore::ME_SYSEX || data[i] == MusECore::ME_SYSEX_END) continue; d += QString("%1").arg(data[i], 2, 16, QLatin1Char('0')); } return d; } //--------------------------------------------------------- // readEventList //--------------------------------------------------------- static void readEventList(Xml& xml, EventList* el, const char* name) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "event") { Event e(Note); e.read(xml); el->add(e); } else xml.unknown("readEventList"); break; case Xml::TagEnd: if (tag == name) return; default: break; } } } //--------------------------------------------------------- // read //--------------------------------------------------------- //--------------------------------------------------------- // loadIDF //--------------------------------------------------------- static void loadIDF(QFileInfo* fi) { FILE* f = fopen(fi->filePath().toLatin1().constData(), "r"); if (f == 0) return; if (MusEGlobal::debugMsg) printf("READ IDF %s\n", fi->filePath().toLatin1().constData()); Xml xml(f); bool skipmode = true; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto loadIDF_end; case Xml::TagStart: if (skipmode && tag == "muse") skipmode = false; else if (skipmode) break; else if (tag == "MidiInstrument") { MidiInstrument* i = new MidiInstrument(); i->setFilePath(fi->filePath()); i->read(xml); // Ignore duplicate named instruments. iMidiInstrument ii = midiInstruments.begin(); for(; ii != midiInstruments.end(); ++ii) { if((*ii)->iname() == i->iname()) break; } if(ii == midiInstruments.end()) { #ifdef _USE_INSTRUMENT_OVERRIDES_ // Add in the drum map overrides that were found in config. // They can only be added now that the instrument has been loaded. ciWorkingDrumMapInstrumentList_t iwdmil = MusEGlobal::workingDrumMapInstrumentList.find(i->iname().toStdString()); if(iwdmil != MusEGlobal::workingDrumMapInstrumentList.end()) { const WorkingDrumMapPatchList& wdmil = iwdmil->second; patch_drummap_mapping_list_t* pdml = i->get_patch_drummap_mapping(); int patch; for(ciWorkingDrumMapPatchList_t iwdmpl = wdmil.begin(); iwdmpl != wdmil.end(); ++iwdmpl) { patch = iwdmpl->first; iPatchDrummapMapping_t ipdm = pdml->find(patch, false); // No default. if(ipdm != pdml->end()) { patch_drummap_mapping_t& pdm = *ipdm; const WorkingDrumMapList& wdml = iwdmpl->second; pdm._workingDrumMapList = wdml; } } // TODO: Done with the config override, so erase it? Hm, maybe we might need it later... //MusEGlobal::workingDrumMapInstrumentList.erase(iwdmil); } #endif midiInstruments.push_back(i); } else delete i; } else xml.unknown("muse"); break; case Xml::Attribut: break; case Xml::TagEnd: if (!skipmode && tag == "muse") { goto loadIDF_end; } default: break; } } loadIDF_end: fclose(f); } //--------------------------------------------------------- // initMidiInstruments //--------------------------------------------------------- void initMidiInstruments() { genericMidiInstrument = new MidiInstrument(QWidget::tr("Generic midi")); midiInstruments.push_back(genericMidiInstrument); // Initialize with a default drum map on default channel. Patch is default 0xffffff. GM-1 does not specify a drum patch number. ChannelDrumMappingList* cdml = genericMidiInstrument->getChannelDrumMapping(); cdml->add(-1, patch_drummap_mapping_list_t()); #ifdef _USE_INSTRUMENT_OVERRIDES_ // Add in the drum map overrides that were found in config. // They can only be added now that the instrument has been created. ciWorkingDrumMapInstrumentList_t iwdmil = MusEGlobal::workingDrumMapInstrumentList.find(genericMidiInstrument->iname().toStdString()); if(iwdmil != MusEGlobal::workingDrumMapInstrumentList.end()) { const WorkingDrumMapPatchList& wdmil = iwdmil->second; int patch; for(ciWorkingDrumMapPatchList_t iwdmpl = wdmil.begin(); iwdmpl != wdmil.end(); ++iwdmpl) { patch = iwdmpl->first; iPatchDrummapMapping_t ipdm = pdml->find(patch, false); // No default. if(ipdm != pdml->end()) { patch_drummap_mapping_t& pdm = *ipdm; const WorkingDrumMapList& wdml = iwdmpl->second; pdm._workingDrumMapList = wdml; } } // TODO: Done with the config override, so erase it? Hm, maybe we might need it later... //MusEGlobal::workingDrumMapInstrumentList.erase(iwdmil); } #endif if (MusEGlobal::debugMsg) printf("load user instrument definitions from <%s>\n", MusEGlobal::museUserInstruments.toLatin1().constData()); QDir usrInstrumentsDir(MusEGlobal::museUserInstruments, QString("*.idf")); if (usrInstrumentsDir.exists()) { QFileInfoList list = usrInstrumentsDir.entryInfoList(); QFileInfoList::iterator it=list.begin(); // ddskrjo while(it != list.end()) { // ddskrjo loadIDF(&*it); ++it; } } if (MusEGlobal::debugMsg) printf("load instrument definitions from <%s>\n", MusEGlobal::museInstruments.toLatin1().constData()); QDir instrumentsDir(MusEGlobal::museInstruments, QString("*.idf")); if (instrumentsDir.exists()) { QFileInfoList list = instrumentsDir.entryInfoList(); QFileInfoList::iterator it=list.begin(); // ddskrjo while(it!=list.end()) { loadIDF(&*it); ++it; } } else printf("Instrument directory not found: %s\n", MusEGlobal::museInstruments.toLatin1().constData()); } //--------------------------------------------------------- // registerMidiInstrument //--------------------------------------------------------- MidiInstrument* registerMidiInstrument(const QString& name) { for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { if ((*i)->iname() == name) return *i; } return genericMidiInstrument; } //--------------------------------------------------------- // removeMidiInstrument //--------------------------------------------------------- void removeMidiInstrument(const QString& name) { for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { if ((*i)->iname() == name) { midiInstruments.erase(i); return; } } } void removeMidiInstrument(const MidiInstrument* instr) { for (iMidiInstrument i = midiInstruments.begin(); i != midiInstruments.end(); ++i) { if (*i == instr) { midiInstruments.erase(i); return; } } } //--------------------------------------------------------- // findMidiInstrument //--------------------------------------------------------- iMidiInstrument MidiInstrumentList::find(const MidiInstrument* instr) { for (iMidiInstrument i = begin(); i != end(); ++i) { if (*i == instr) { return i; } } return end(); } #ifdef _USE_INSTRUMENT_OVERRIDES_ void MidiInstrumentList::writeDrummapOverrides(int level, Xml& xml) const { MidiInstrument* mi; for(ciMidiInstrument imi = begin(); imi != end(); ++imi) { mi = *imi; mi->writeDrummapOverrides(level, xml); } } #endif //--------------------------------------------------------- // MidiInstrument //--------------------------------------------------------- void MidiInstrument::init() { _noteOffMode = NoteOffAll; // By default, use note offs. _tmpMidiStateVersion = 1; // Assume old version. readMidiState will overwrite anyway. _initScript = 0; _waitForLSB = true; _midiInit = new EventList(); _midiReset = new EventList(); _midiState = new EventList(); _controller = new MidiControllerList; // add some default controller to controller list // this controllers are always available for all instruments // MidiController* prog = new MidiController("Program", CTRL_PROGRAM, 0, 0xffffff, 0, 0); _controller->add(prog); _dirty = false; } MidiInstrument::MidiInstrument() { init(); } //--------------------------------------------------------- // MidiInstrument //--------------------------------------------------------- MidiInstrument::MidiInstrument(const QString& txt) { _name = txt; init(); } //--------------------------------------------------------- // MidiInstrument //--------------------------------------------------------- MidiInstrument::~MidiInstrument() { for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g) { PatchGroup* pgp = *g; const PatchList& pl = pgp->patches; for (ciPatch p = pl.begin(); p != pl.end(); ++p) { delete *p; } delete pgp; } delete _midiInit; delete _midiReset; delete _midiState; for(iMidiController i = _controller->begin(); i != _controller->end(); ++i) delete i->second; delete _controller; if (_initScript) delete _initScript; if(!_sysex.isEmpty()) { int j = _sysex.size(); for(int i = 0; i < j; ++i) delete _sysex.at(i); } _channelDrumMapping.clear(); } //--------------------------------------------------------- // assign //--------------------------------------------------------- MidiInstrument& MidiInstrument::assign(const MidiInstrument& ins) { //--------------------------------------------------------- // TODO: Copy the _initScript (if and when it is ever used) //--------------------------------------------------------- for(iMidiController i = _controller->begin(); i != _controller->end(); ++i) delete i->second; _controller->clr(); _waitForLSB = ins._waitForLSB; _noteOffMode = ins._noteOffMode; // Assignment for(ciMidiController i = ins._controller->begin(); i != ins._controller->end(); ++i) { MidiController* mc = i->second; _controller->add(new MidiController(*mc)); } if(!_sysex.isEmpty()) { int j = _sysex.size(); for(int i = 0; i < j; ++i) delete _sysex.at(i); _sysex.clear(); } if(!ins.sysex().isEmpty()) { int j = ins.sysex().size(); for(int i = 0; i < j; ++i) _sysex.append(new MusECore::SysEx(*(ins.sysex().at(i)))); } *(_midiInit) = *(ins._midiInit); *(_midiReset) = *(ins._midiReset); *(_midiState) = *(ins._midiState); for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g) { PatchGroup* pgp = *g; const PatchList& pl = pgp->patches; for (ciPatch p = pl.begin(); p != pl.end(); ++p) { delete *p; } delete pgp; } pg.clear(); // Assignment for(ciPatchGroup g = ins.pg.begin(); g != ins.pg.end(); ++g) { PatchGroup* pgp = *g; const PatchList& pl = pgp->patches; PatchGroup* npg = new PatchGroup; npg->name = pgp->name; pg.push_back(npg); for (ciPatch p = pl.begin(); p != pl.end(); ++p) { Patch* pp = *p; Patch* np = new Patch; //np->typ = pp->typ; np->hbank = pp->hbank; np->lbank = pp->lbank; np->program = pp->program; np->name = pp->name; np->drum = pp->drum; npg->patches.push_back(np); } } _name = ins._name; _filePath = ins._filePath; _channelDrumMapping = ins._channelDrumMapping; // Hmm, dirty, yes? But init sets it to false... DELETETHIS //_dirty = ins._dirty; //_dirty = false; //_dirty = true; return *this; } //--------------------------------------------------------- // midiType //--------------------------------------------------------- MType MidiInstrument::midiType() const { if(_name == "GM") return MT_GM; if(_name == "GM2") return MT_GM2; if(_name == "GS") return MT_GS; if(_name == "XG") return MT_XG; return MT_UNKNOWN; } //--------------------------------------------------------- // reset // send note off to all channels // To be called by audio thread only. //--------------------------------------------------------- void MidiInstrument::reset(int portNo) { MusECore::MidiPort* port = &MusEGlobal::midiPorts[portNo]; if(port->device() == 0) return; MusECore::MidiPlayEvent ev; ev.setType(ME_NOTEOFF); ev.setPort(portNo); ev.setTime(0); // Immediate processing. TODO: Use curFrame? ev.setB(64); for (int chan = 0; chan < MusECore::MUSE_MIDI_CHANNELS; ++chan) { ev.setChannel(chan); for (int pitch = 0; pitch < 128; ++pitch) { ev.setA(pitch); port->device()->putEvent(ev, MidiDevice::NotLate); } } } //--------------------------------------------------------- // readPatchGroup //--------------------------------------------------------- void PatchGroup::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "Patch") { Patch* patch = new Patch; patch->read(xml); patches.push_back(patch); } else xml.unknown("PatchGroup"); break; case Xml::Attribut: if (tag == "name") name = xml.s2(); break; case Xml::TagEnd: if (tag == "PatchGroup") return; default: break; } } } //--------------------------------------------------------- // read //--------------------------------------------------------- void Patch::read(Xml& xml) { hbank = -1; lbank = -1; program = -1; drum = false; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: xml.unknown("Patch"); break; case Xml::Attribut: if (tag == "name") name = xml.s2(); else if (tag == "mode") // Obsolete { xml.s2().toInt(); } else if (tag == "hbank") hbank = xml.s2().toInt(); else if (tag == "lbank") lbank = xml.s2().toInt(); else if (tag == "prog") program = xml.s2().toInt(); else if (tag == "drum") drum = xml.s2().toInt(); break; case Xml::TagEnd: if (tag == "Patch") return; default: break; } } } //--------------------------------------------------------- // write //--------------------------------------------------------- void Patch::write(int level, Xml& xml) { xml.nput(level, ""); } iPatch PatchList::find(int patch, bool drum, bool includeDefault) { int pnum; Patch* p; iPatch ip_default = end(); for(iPatch ip = begin(); ip != end(); ++ip) { p = *ip; pnum = p->patch(); // Look for an exact match above all else. The given patch must be valid. if(patch != CTRL_VAL_UNKNOWN && pnum == patch && p->drum == drum) return ip; // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). if(includeDefault && p->dontCare() && p->drum == drum && ip_default == end()) ip_default = ip; } return ip_default; } ciPatch PatchList::find(int patch, bool drum, bool includeDefault) const { int pnum; const Patch* p; ciPatch ip_default = end(); for(ciPatch ip = begin(); ip != end(); ++ip) { p = *ip; pnum = p->patch(); // Look for an exact match above all else. The given patch must be valid. if(patch != CTRL_VAL_UNKNOWN && pnum == patch && p->drum == drum) return ip; // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). if(includeDefault && p->dontCare() && p->drum == drum && ip_default == end()) ip_default = ip; } return ip_default; } Patch* PatchGroupList::findPatch(int patch, bool drum, bool includeDefault) { for(iPatchGroup ipg = begin(); ipg != end(); ++ipg) { PatchGroup* pg = *ipg; iPatch ip = pg->patches.find(patch, drum, includeDefault); if(ip != pg->patches.end()) return *ip; } return 0; } Patch* PatchGroupList::findPatch(int patch, bool drum, bool includeDefault) const { for(ciPatchGroup ipg = begin(); ipg != end(); ++ipg) { const PatchGroup* pg = *ipg; ciPatch ip = pg->patches.find(patch, drum, includeDefault); if(ip != pg->patches.end()) return *ip; } return 0; } //--------------------------------------------------------- // SysEx //--------------------------------------------------------- SysEx::SysEx() { dataLen = 0; data = 0; } SysEx::SysEx(const SysEx& src) { name = src.name; comment = src.comment; dataLen = src.dataLen; data = 0; if(dataLen != 0 && src.data) { data = new unsigned char[dataLen]; memcpy(data, src.data, dataLen); } } SysEx::~SysEx() { if(dataLen != 0 && data) delete[] data; } bool SysEx::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return false; case Xml::TagStart: if (tag == "comment") comment = xml.parse1(); else if (tag == "data") { unsigned char*d; int len = string2sysex(xml.parse1(), &d); // Was the conversion successful, even if empty? if(len != -1) { // Delete existing. if(dataLen != 0 && data) delete[] data; dataLen = len; data = d; } } else xml.unknown("SysEx"); break; case Xml::Attribut: if (tag == "name") name = xml.s2(); break; case Xml::TagEnd: if (tag == "SysEx") { return !name.isEmpty(); } default: break; } } return false; } void SysEx::write(int level, Xml& xml) { xml.nput(level, "\n", Xml::xmlString(name).toLatin1().constData()); level++; if(!comment.isEmpty()) xml.strTag(level, "comment", comment.toLatin1().constData()); if(dataLen > 0 && data) xml.strTag(level, "data", sysex2string(dataLen, data)); xml.etag(level, "SysEx"); } //--------------------------------------------------------- // readMidiState //--------------------------------------------------------- void MidiInstrument::readMidiState(Xml& xml) { // A kludge to support old midistates by wrapping them in the proper header. _tmpMidiStateVersion = 1; // Assume old (unmarked) first version 1. for (;;) { Xml::Token token = xml.parse(); const QString tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "event") { Event e(Note); e.read(xml); _midiState->add(e); } else xml.unknown("midistate"); break; case Xml::Attribut: if(tag == "version") _tmpMidiStateVersion = xml.s2().toInt(); else xml.unknown("MidiInstrument"); break; case Xml::TagEnd: if(tag == "midistate") return; default: break; } } } void MidiInstrument::readDrummaps(Xml& xml) { //_channelDrumMapping.clear(); // ??? const QString start_tag = xml.s1(); for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "drumMapChannel") _channelDrumMapping.read(xml); else if (tag == "entry") { patch_drummap_mapping_list_t pdml; pdml.read(xml); if(!pdml.empty()) _channelDrumMapping.add(-1, pdml); // Add to the default channel. } else xml.unknown("MidiInstrument::readDrummaps"); break; case Xml::TagEnd: if (tag == start_tag) return; default: break; } } } void MidiInstrument::writeDrummaps(int level, Xml& xml) const { xml.tag(level++, "Drummaps"); _channelDrumMapping.write(level, xml); xml.etag(--level, "Drummaps"); } //--------------------------------------------------------- // read //--------------------------------------------------------- void MidiInstrument::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "Patch") { Patch* patch = new Patch; patch->read(xml); if (pg.empty()) { PatchGroup* p = new PatchGroup; p->patches.push_back(patch); pg.push_back(p); } else pg[0]->patches.push_back(patch); } else if (tag == "PatchGroup") { PatchGroup* p = new PatchGroup; p->read(xml); pg.push_back(p); } else if (tag == "Controller") { MidiController* mc = new MidiController(); mc->read(xml); // // HACK: make predefined "Program" controller overloadable // if (mc->name() == "Program") { for (iMidiController i = _controller->begin(); i != _controller->end(); ++i) { if (i->second->name() == mc->name()) { delete i->second; _controller->del(i); break; } } } _controller->add(mc); } else if (tag == "Drummaps") { readDrummaps(xml); } else if (tag == "Init") readEventList(xml, _midiInit, "Init"); else if (tag == "Reset") readEventList(xml, _midiReset, "Reset"); else if (tag == "State") readEventList(xml, _midiState, "State"); else if (tag == "InitScript") { if (_initScript) delete _initScript; QByteArray ba = xml.parse1().toLatin1(); const char* istr = ba.constData(); int len = ba.length() +1; if (len > 1) { _initScript = new char[len]; memcpy(_initScript, istr, len); } } else if (tag == "SysEx") { SysEx* se = new SysEx; if(!se->read(xml)) { delete se; printf("MidiInstrument::read():SysEx: reading sysex failed\n"); } else _sysex.append(se); } else xml.unknown("MidiInstrument"); break; case Xml::Attribut: if (tag == "name") setIName(xml.s2()); else if(tag == "nullparam") { } // Obsolete. else if(tag == "NoteOffMode") _noteOffMode = (NoteOffMode)xml.s2().toInt(); // Default is NoteOffAll. break; case Xml::TagEnd: if (tag == "MidiInstrument") return; default: break; } } } //--------------------------------------------------------- // write //--------------------------------------------------------- void MidiInstrument::write(int level, Xml& xml) { xml.header(); xml.tag(level, "muse version=\"1.0\""); level++; xml.nput(level, ""); level++; for (ciPatchGroup g = pg.begin(); g != pg.end(); ++g) { PatchGroup* pgp = *g; const PatchList& pl = pgp->patches; xml.tag(level, "PatchGroup name=\"%s\"", Xml::xmlString(pgp->name).toLatin1().constData()); level++; for (ciPatch p = pl.begin(); p != pl.end(); ++p) (*p)->write(level, xml); level--; xml.etag(level, "PatchGroup"); } for (iMidiController ic = _controller->begin(); ic != _controller->end(); ++ic) ic->second->write(level, xml); if(!_sysex.isEmpty()) { int j = _sysex.size(); for(int i = 0; i < j; ++i) _sysex.at(i)->write(level, xml); } xml.tag(level++, "Init"); for(ciEvent ev=_midiInit->begin(); ev != _midiInit->end(); ++ev) ev->second.write(level, xml, MusECore::Pos(0, true)); xml.etag(--level, "Init"); // ------------- // TODO: What about _midiReset, _midiState, and _initScript ? // ------------- writeDrummaps(level, xml); level--; xml.etag(level, "MidiInstrument"); level--; xml.etag(level, "muse"); } #ifdef _USE_INSTRUMENT_OVERRIDES_ void MidiInstrument::writeDrummapOverrides(int level, Xml& xml) const { for(ciPatchDrummapMapping_t ipdm = patch_drummap_mapping.begin(); ipdm != patch_drummap_mapping.end(); ++ipdm) { if(!(*ipdm)._workingDrumMapList.empty()) { xml.tag(level++, "drummapOverrides instrument=\"%s\"", Xml::xmlString(iname()).toLatin1().constData()); patch_drummap_mapping.writeDrummapOverrides(level, xml); xml.etag(--level, "drummapOverrides"); break; } } } #endif //--------------------------------------------------------- // readMidnamDocument //--------------------------------------------------------- #ifdef MIDNAM_SUPPORT bool MidiInstrument::readMidnamDocument(Xml& xml) { return _midnamDocument.read(xml); } #endif patch_drummap_mapping_list_t* MidiInstrument::get_patch_drummap_mapping(int channel, bool includeDefault) { patch_drummap_mapping_list_t* pdml = _channelDrumMapping.find(channel, includeDefault); if(!pdml) // Not found? Search the global mapping list. return genericMidiInstrument->getChannelDrumMapping()->find(channel, includeDefault); return pdml; } //--------------------------------------------------------- // populateInstrPopup (static) //--------------------------------------------------------- void MidiInstrument::populateInstrPopup(MusEGui::PopupMenu* menu, int port, bool show_synths) { menu->clear(); if(port < 0 || port >= MIDI_PORTS) return; const MidiPort* mp = &MusEGlobal::midiPorts[port]; const MidiDevice* md = mp->device(); const MidiInstrument* dev_curr_instr = mp->instrument(); const SynthI* dev_synth = nullptr; const MidiInstrument* dev_synth_instr = nullptr; QAction* act; act = menu->addAction(*MusEGui::editInstrumentSVGIcon, QWidget::tr("Edit Instrument...")); act->setData(100); menu->addSeparator(); menu->addAction(new MusEGui::MenuTitleItem(QObject::tr("Instruments"), menu)); menu->addSeparator(); if(md && md->isSynti()) { dev_synth = static_cast(md); dev_synth_instr = static_cast(dev_synth); } if(dev_synth_instr) { menu->addAction(new MusEGui::MenuTitleItem(QObject::tr("Current device"), menu)); act = menu->addAction(dev_synth_instr->iname()); act->setCheckable(true); if(dev_synth_instr == dev_curr_instr) act->setChecked(true); } if(!MusECore::midiInstruments.empty()) { bool has_synths = false; for (MusECore::ciMidiInstrument i = MusECore::midiInstruments.cbegin(); i != MusECore::midiInstruments.cend(); ++i) { if(show_synths && (*i)->isSynti() && (*i) != dev_synth_instr) { has_synths = true; break; } } if(has_synths) { if(dev_synth_instr) menu->addAction(new MusEGui::MenuTitleItem(QObject::tr("Others"), menu)); MusEGui::PopupMenu* instr_menu = new MusEGui::PopupMenu(menu, false); instr_menu->setTitle(QObject::tr("Files")); for (MusECore::ciMidiInstrument i = MusECore::midiInstruments.cbegin(); i != MusECore::midiInstruments.cend(); ++i) { if(!(*i)->isSynti()) { act = instr_menu->addAction((*i)->iname()); act->setCheckable(true); if((*i) == dev_curr_instr) act->setChecked(true); } } menu->addMenu(instr_menu); MusEGui::PopupMenu* synth_submenu = new MusEGui::PopupMenu(menu, false); synth_submenu->setTitle(QObject::tr("Synthesizers")); for (MusECore::ciMidiInstrument i = MusECore::midiInstruments.cbegin(); i != MusECore::midiInstruments.cend(); ++i) { if((*i)->isSynti() && (*i) != dev_synth_instr) { act = synth_submenu->addAction((*i)->iname()); act->setCheckable(true); if((*i) == dev_curr_instr) act->setChecked(true); } } menu->addMenu(synth_submenu); } else { menu->addAction(new MusEGui::MenuTitleItem(QObject::tr("Files"), menu)); for (MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { if(!(*i)->isSynti()) { act = menu->addAction((*i)->iname()); act->setCheckable(true); if((*i) == dev_curr_instr) act->setChecked(true); } } } } } //--------------------------------------------------------- // populatePatchPopup //--------------------------------------------------------- void MidiInstrument::populatePatchPopup(MusEGui::PopupMenu* menu, int /*chan*/, bool drum) { menu->clear(); //int mask = 7; if (pg.size() > 1) { for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) { PatchGroup* pgp = *i; MusEGui::PopupMenu* pm = 0; const PatchList& pl = pgp->patches; for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const Patch* mp = *ipl; if (//(mp->typ & mask) && (mp->drum == drum)) { if(!pm) { pm = new MusEGui::PopupMenu(pgp->name, menu, menu->stayOpen()); // Use the parent stayOpen here. menu->addMenu(pm); pm->setFont(qApp->font()); } const int hb = mp->hbank & 0xff; const int lb = mp->lbank & 0xff; const int pr = mp->program & 0xff; const int id = (hb << 16) | (lb << 8) | pr; const bool vhb = hb != 0xff; const bool vlb = lb != 0xff; const bool vpr = pr != 0xff; QString astr; if(vhb || vlb || vpr) { if(vhb) astr += QString::number(hb + 1) + QString(":"); if(vlb) astr += QString::number(lb + 1) + QString(":"); else if(vhb) astr += QString("--:"); if(vpr) astr += QString::number(pr + 1); else if(vhb && vlb) astr += QString("--"); astr += QString(" "); } astr += mp->name; QAction* act = pm->addAction(astr); act->setData(id); } } } } else if (pg.size() == 1 ){ // no groups const PatchList& pl = pg.front()->patches; for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const Patch* mp = *ipl; //if (mp->typ & mask) { const int hb = mp->hbank & 0xff; const int lb = mp->lbank & 0xff; const int pr = mp->program & 0xff; const int id = (hb << 16) | (lb << 8) | pr; const bool vhb = hb != 0xff; const bool vlb = lb != 0xff; const bool vpr = pr != 0xff; QString astr; if(vhb || vlb || vpr) { if(vhb) astr += QString::number(hb + 1) + QString(":"); if(vlb) astr += QString::number(lb + 1) + QString(":"); else if(vhb) astr += QString("--:"); if(vpr) astr += QString::number(pr + 1); else if(vhb && vlb) astr += QString("--"); astr += QString(" "); } astr += mp->name; QAction* act = menu->addAction(astr); act->setData(id); //} } } } void MidiInstrument::getMapItem(int channel, int patch, int index, DrumMap& dest_map, int #ifdef _USE_INSTRUMENT_OVERRIDES_ overrideType #endif ) const { const patch_drummap_mapping_list_t* pdml = _channelDrumMapping.find(channel, true); // Include default. if(!pdml) { fprintf(stderr, "MidiInstrument::getMapItem Error: No channel:%d mapping or default found. Using iNewDrumMap.\n", channel); dest_map = iNewDrumMap[index]; return; } // Always search this instrument's mapping first. ciPatchDrummapMapping_t ipdm = pdml->find(patch, false); // Don't include defaults here. if(ipdm == pdml->end()) { // Not found? Is there a default patch mapping? #ifdef _USE_INSTRUMENT_OVERRIDES_ if(overrideType & WorkingDrumMapEntry::InstrumentDefaultOverride) #endif ipdm = pdml->find(CTRL_PROGRAM_VAL_DONT_CARE, false); // Don't include defaults here. if(ipdm == pdml->end()) { // Not found? Search the global mapping list. patch_drummap_mapping_list_t* def_pdml = genericMidiInstrument->get_patch_drummap_mapping(channel, false); if(!def_pdml) { //fprintf(stderr, "MidiInstrument::getMapItem Error: No default patch mapping found in genericMidiInstrument. Using iNewDrumMap.\n"); dest_map = iNewDrumMap[index]; return; } ipdm = def_pdml->find(patch, false); // Don't include defaults here. if(ipdm == def_pdml->end()) { // Not found? Is there a default patch mapping? #ifdef _USE_INSTRUMENT_OVERRIDES_ if(overrideType & WorkingDrumMapEntry::InstrumentDefaultOverride) #endif ipdm = def_pdml->find(CTRL_PROGRAM_VAL_DONT_CARE, false); // Don't include defaults here. if(ipdm == def_pdml->end()) { // Not found? Use the global drum map. // Update: This shouldn't really happen now, since we have added a default patch drum map to the genericMidiInstrument. fprintf(stderr, "MidiInstrument::getMapItem Error: No default patch mapping found in genericMidiInstrument. Using iNewDrumMap.\n"); dest_map = iNewDrumMap[index]; return; } } } } const patch_drummap_mapping_t& pdm = (*ipdm); dest_map = pdm.drummap[index]; #ifdef _USE_INSTRUMENT_OVERRIDES_ // Did we request to include any instrument overrides? if(!(overrideType & WorkingDrumMapEntry::InstrumentOverride)) return; // Get any instrument overrides. ciWorkingDrumMapPatch_t iwdp = pdm._workingDrumMapList.find(index); if(iwdp == pdm._workingDrumMapList.end()) return; const WorkingDrumMapEntry& wdm = iwdp->second; if(wdm._fields & WorkingDrumMapEntry::NameField) dest_map.name = wdm._mapItem.name; if(wdm._fields & WorkingDrumMapEntry::VolField) dest_map.vol = wdm._mapItem.vol; if(wdm._fields & WorkingDrumMapEntry::QuantField) dest_map.quant = wdm._mapItem.quant; if(wdm._fields & WorkingDrumMapEntry::LenField) dest_map.len = wdm._mapItem.len; if(wdm._fields & WorkingDrumMapEntry::ChanField) dest_map.channel = wdm._mapItem.channel; if(wdm._fields & WorkingDrumMapEntry::PortField) dest_map.port = wdm._mapItem.port; if(wdm._fields & WorkingDrumMapEntry::Lv1Field) dest_map.lv1 = wdm._mapItem.lv1; if(wdm._fields & WorkingDrumMapEntry::Lv2Field) dest_map.lv2 = wdm._mapItem.lv2; if(wdm._fields & WorkingDrumMapEntry::Lv3Field) dest_map.lv3 = wdm._mapItem.lv3; if(wdm._fields & WorkingDrumMapEntry::Lv4Field) dest_map.lv4 = wdm._mapItem.lv4; if(wdm._fields & WorkingDrumMapEntry::ENoteField) dest_map.enote = wdm._mapItem.enote; if(wdm._fields & WorkingDrumMapEntry::ANoteField) dest_map.anote = wdm._mapItem.anote; if(wdm._fields & WorkingDrumMapEntry::MuteField) dest_map.mute = wdm._mapItem.mute; if(wdm._fields & WorkingDrumMapEntry::HideField) dest_map.hide = wdm._mapItem.hide; #endif } MidiController* MidiInstrument::findController(int num, int channel, int patch) const { #ifdef MIDNAM_SUPPORT // Is there a midnam controller list for the given channel and patch? const MidiControllerList* mcl = _midnamDocument.getControllers(channel, patch); if(mcl) { // Is there a controller for the given num? MidiController* mc = mcl->findController(num); if(mc) // Return that controller. return mc; } #endif // No midnam controller was found. Does the instrument have the controller? return controller()->findController(num); } void MidiInstrument::getControllers(MidiControllerList* dest, int channel, int patch) const { ciMidiController imc; #ifdef MIDNAM_SUPPORT // Is there a midnam controller list for the given channel and patch? const MidiControllerList* mcl = _midnamDocument.getControllers(channel, patch); if(mcl) { // Copy the midnam controller pointers directly to the destination. // Defer the RPN update until after done. for(imc = mcl->begin(); imc != mcl->end(); ++imc) dest->add(imc->second, false); } #endif mcl = controller(); // Copy the instrument controller pointers directly to the destination. // Defer the RPN update until after done. for(imc = mcl->begin(); imc != mcl->end(); ++imc) dest->add(imc->second, false); // Be sure to call this since we deferred it above. dest->update_RPN_Ctrls_Reserved(); } #ifdef _USE_INSTRUMENT_OVERRIDES_ int MidiInstrument::isWorkingMapItem(int patch, int index, int fields) const { int ret = WorkingDrumMapEntry::NoOverride; // Is there a default patch override for this drum map item? bool def_ipdm_valid = true; ciPatchDrummapMapping_t def_ipdm = patch_drummap_mapping.find(CTRL_PROGRAM_VAL_DONT_CARE, false); // Don't include defaults here. if(def_ipdm == patch_drummap_mapping.end()) { // Not found? Search the global mapping list. def_ipdm = genericMidiInstrument->get_patch_drummap_mapping()->find(CTRL_PROGRAM_VAL_DONT_CARE, false); if(def_ipdm == genericMidiInstrument->get_patch_drummap_mapping()->end()) def_ipdm_valid = false; } if(def_ipdm_valid) { const patch_drummap_mapping_t& pdm = (*def_ipdm); ciWorkingDrumMapPatch_t iwdp = pdm._workingDrumMapList.find(index); if(iwdp != pdm._workingDrumMapList.end()) { const WorkingDrumMapEntry& wdm = iwdp->second; if(wdm._fields & fields) ret |= WorkingDrumMapEntry::InstrumentDefaultOverride; } } // Is there a patch override for this drum map item? // Always search this instrument's mapping first. bool ipdm_valid = true; ciPatchDrummapMapping_t ipdm = patch_drummap_mapping.find(patch, false); if(ipdm == patch_drummap_mapping.end()) { // Not found? Search the global mapping list. ipdm = MusECore::genericMidiInstrument->get_patch_drummap_mapping()->find(patch, false); if(ipdm == MusECore::genericMidiInstrument->get_patch_drummap_mapping()->end()) ipdm_valid = false; } if(ipdm_valid) { const patch_drummap_mapping_t& pdm = (*ipdm); ciWorkingDrumMapPatch_t iwdp = pdm._workingDrumMapList.find(index); if(iwdp != pdm._workingDrumMapList.end()) { const WorkingDrumMapEntry& wdm = iwdp->second; if(wdm._fields & fields) ret |= WorkingDrumMapEntry::InstrumentOverride; } } return ret; } void MidiInstrument::clearDrumMapOverrides() { for(iPatchDrummapMapping_t ipdm = patch_drummap_mapping.begin(); ipdm != patch_drummap_mapping.end(); ++ipdm) { patch_drummap_mapping_t& pdm = *ipdm; pdm._workingDrumMapList.clear(); } } bool MidiInstrument::setWorkingDrumMapItem(int patch, int index, const WorkingDrumMapEntry& item, bool isReset) { // Special value. Save it from searching. // if(patch == CTRL_VAL_UNKNOWN) // return false; // iPatchDrummapMapping_t patch_ipm; // patch_ipm = patch_drummap_mapping.find(patch); // // You can't edit a drum map item in a collection that doesn't exist. // if(patch_ipm == patch_drummap_mapping.end()) // return false; // Always search this instrument's mapping first. iPatchDrummapMapping_t ipdm = patch_drummap_mapping.find(patch, false); // Don't include defaults here. if(ipdm == patch_drummap_mapping.end()) { // Not found? Search the global mapping list. ipdm = MusECore::genericMidiInstrument->get_patch_drummap_mapping()->find(patch, false); // Don't include defaults here. // Not found? You can't edit a drum map item in a collection that doesn't exist. if(ipdm == MusECore::genericMidiInstrument->get_patch_drummap_mapping()->end()) return false; } patch_drummap_mapping_t& pdm = *ipdm; const int fields = item._fields; DrumMap cur_dm; getMapItem(patch, index, cur_dm, WorkingDrumMapEntry::InstrumentOverride | WorkingDrumMapEntry::InstrumentDefaultOverride); const int cur_enote = cur_dm.enote; if(isReset) pdm.removeWorkingDrumMapEntry(index, fields); else pdm.addWorkingDrumMapEntry(index, item); DrumMap new_dm; getMapItem(patch, index, new_dm, WorkingDrumMapEntry::InstrumentOverride | WorkingDrumMapEntry::InstrumentDefaultOverride); if(fields & WorkingDrumMapEntry::ENoteField) { int new_enote = new_dm.enote; int other_index = pdm.drum_in_map[new_enote]; { DrumMap other_dm; if(isReset) { // Here we need to see the map item value just /before/ any override, so that we can tell // whether this other_index brute-force 'reset' value is still technically an // override, and either remove or add (modify) the list appropriately. getMapItem(patch, other_index, other_dm, WorkingDrumMapEntry::InstrumentDefaultOverride); if(other_dm.enote == cur_enote) { // The values are equal. This is technically no longer an override and we may remove it. //_workingDrumMapPatchList->remove(patch, other_index, WorkingDrumMapEntry::ENoteField); pdm.removeWorkingDrumMapEntry(other_index, WorkingDrumMapEntry::ENoteField); } else { // The values are not equal. This is technically still an override, so add (modify) it. other_dm.enote = cur_enote; WorkingDrumMapEntry other_wdme(other_dm, WorkingDrumMapEntry::ENoteField); //_workingDrumMapPatchList->add(patch, other_index, other_wdme); pdm.addWorkingDrumMapEntry(other_index, other_wdme); } } else { other_dm.enote = cur_enote; WorkingDrumMapEntry other_wdme(other_dm, WorkingDrumMapEntry::ENoteField); //_workingDrumMapPatchList->add(patch, other_index, other_wdme); pdm.addWorkingDrumMapEntry(other_index, other_wdme); } pdm.drum_in_map[cur_enote] = other_index; pdm.drum_in_map[new_enote] = index; } } return true; } #endif //--------------------------------------------------------- // getPatchName //--------------------------------------------------------- QString MidiInstrument::getPatchName(int /*channel*/, int prog, bool drum, bool includeDefault) const { if(MusECore::Patch* p = pg.findPatch(prog, drum, includeDefault)) return p->name; return ""; } unsigned MidiInstrument::getNextPatch(int channel, unsigned patch, bool drum) { QList haystack=getPatches(channel,drum); if (haystack.empty()) return MusECore::CTRL_VAL_UNKNOWN; int prog=patch&0xFF; int lbank=(patch>>8)&0xFF; int hbank=(patch>>16)&0xFF; dumb_patchlist_entry_t needle=dumb_patchlist_entry_t(prog, (lbank!=0xFF)?lbank:-1, (hbank!=0xFF)?hbank:-1); QList::iterator it; for (it=haystack.begin(); it!=haystack.end(); it++) if ((*it) == needle) break; if (it==haystack.end()) //not found? use first entry it=haystack.begin(); else { for (;it!=haystack.end(); it++) if ((*it)!=needle) break; if (it==haystack.end()) it=haystack.begin(); //wrap-over } return (it->prog&0xFF) | ((((it->lbank==-1)?0xFF:it->lbank)<<8)&0xFF00) | ((((it->hbank==-1)?0xFF:it->hbank)<<16)&0xFF0000); } unsigned MidiInstrument::getPrevPatch(int channel, unsigned patch, bool drum) { QList haystack=getPatches(channel,drum); if (haystack.empty()) return MusECore::CTRL_VAL_UNKNOWN; int prog=patch&0xFF; int lbank=(patch>>8)&0xFF; int hbank=(patch>>16)&0xFF; dumb_patchlist_entry_t needle=dumb_patchlist_entry_t(prog, (lbank!=0xFF)?lbank:-1, (hbank!=0xFF)?hbank:-1); QList::iterator it; for (it=haystack.begin(); it!=haystack.end(); it++) if ((*it) == needle) break; if (it==haystack.end()) //not found? use first entry it=haystack.begin(); else { if (it==haystack.begin()) it=haystack.end(); //wrap-over it--; } return (it->prog&0xFF) | ((((it->lbank==-1)?0xFF:it->lbank)<<8)&0xFF00) | ((((it->hbank==-1)?0xFF:it->hbank)<<16)&0xFF0000); } QList MidiInstrument::getPatches(int /*channel*/, bool drum) { //int tmask = 1; QList tmp; for (ciPatchGroup i = pg.begin(); i != pg.end(); ++i) { const PatchList& pl = (*i)->patches; for (ciPatch ipl = pl.begin(); ipl != pl.end(); ++ipl) { const Patch* mp = *ipl; if (//(mp->typ & tmask) && (mp->drum == drum)) { int prog = mp->program; int lbank = mp->lbank; int hbank = mp->hbank; tmp.push_back(dumb_patchlist_entry_t(prog,lbank,hbank)); } } } return tmp; } //--------------------------------------------------------- // patch_drummap_mapping_t //--------------------------------------------------------- patch_drummap_mapping_t::patch_drummap_mapping_t() { _patch = CTRL_PROGRAM_VAL_DONT_CARE; drummap=new DrumMap[128]; for (int i=0;i<128;i++) drummap[i]=iNewDrumMap[i]; update_drum_in_map(); } patch_drummap_mapping_t::patch_drummap_mapping_t(const patch_drummap_mapping_t& that) { drummap=NULL; if(that.drummap) { drummap=new DrumMap[128]; for (int i=0;i<128;i++) drummap[i]=that.drummap[i]; } _patch = that._patch; update_drum_in_map(); } patch_drummap_mapping_t& patch_drummap_mapping_t::operator=(const patch_drummap_mapping_t& that) { if (drummap) delete [] drummap; drummap=NULL; if(that.drummap) { drummap=new DrumMap[128]; for (int i=0;i<128;i++) drummap[i]=that.drummap[i]; } _patch = that._patch; update_drum_in_map(); return *this; } bool patch_drummap_mapping_t::isValid() const { return _patch != CTRL_VAL_UNKNOWN && drummap != NULL; } patch_drummap_mapping_t::~patch_drummap_mapping_t() { if(drummap) delete [] drummap; } #ifdef _USE_INSTRUMENT_OVERRIDES_ void patch_drummap_mapping_t::addWorkingDrumMapEntry(int index,const WorkingDrumMapEntry& item) { _workingDrumMapList.add(index, item); } void patch_drummap_mapping_t::removeWorkingDrumMapEntry(int index, const WorkingDrumMapEntry& item) { _workingDrumMapList.remove(index, item); } void patch_drummap_mapping_t::removeWorkingDrumMapEntry(int index, int fields) { _workingDrumMapList.remove(index, fields); } #endif void patch_drummap_mapping_t::update_drum_in_map() { if(drummap) { for(int i = 0; i < 128; ++i) drum_in_map[(int)drummap[i].enote] = i; } else { for(int i = 0; i < 128; ++i) drum_in_map[i] = i; } #ifdef _USE_INSTRUMENT_OVERRIDES_ int index; int enote; for(ciWorkingDrumMapPatch_t iwdmp = _workingDrumMapList.begin(); iwdmp != _workingDrumMapList.end(); ++iwdmp) { const WorkingDrumMapEntry& wde = iwdmp->second; if(wde._fields & WorkingDrumMapEntry::ENoteField) { index = iwdmp->first; const DrumMap& dm = wde._mapItem; enote = (int)dm.enote; drum_in_map[enote] = index; } } #endif } bool patch_drummap_mapping_t::isPatchInRange(int patch, bool includeDefault) const { // No exceptions: If all three prg, hb, and lb are don't care, then patch is always in range. if(dontCare()) return includeDefault; // Special value. Unknown cannot be part of a collection (unless don't care). if(!isValid() || patch == CTRL_VAL_UNKNOWN) return false; const int hb = (patch >> 16) & 0xff; const int lb = (patch >> 8) & 0xff; const int pr = patch & 0xff; const bool hboff = hb >= 128; const bool lboff = lb >= 128; const bool prgoff = pr >= 128; // Shouldn't happen. return (programDontCare() || (!prgoff && pr == prog())) && (hbankDontCare() || (!hboff && hb == hbank())) && (lbankDontCare() || (!lboff && lb == lbank())); } QString patch_drummap_mapping_t::to_string() { QString tmp; if (dontCare()) tmp="default"; else { if(hbankDontCare()) tmp += "---"; else tmp += QString::number(hbank() + 1); tmp+=" / "; if(lbankDontCare()) tmp += "---"; else tmp += QString::number(lbank() + 1); tmp+=" / "; if(programDontCare()) tmp += "---"; else tmp += QString::number(prog() + 1); } return tmp; } //--------------------------------------------------------- // patch_drummap_mapping_t //--------------------------------------------------------- void patch_drummap_mapping_list_t::add(const patch_drummap_mapping_list_t& other) { for(ciPatchDrummapMapping_t ipdm = other.begin(); ipdm != other.end(); ++ipdm) { const patch_drummap_mapping_t& pdm = *ipdm; add(pdm); } } void patch_drummap_mapping_list_t::add(const patch_drummap_mapping_t& pdm) { // No duplicates: If a mapping item by that patch already exists, replace it. iPatchDrummapMapping_t ipdm = find(pdm._patch, false); // No default. if(ipdm == end()) push_back(pdm); else *ipdm = pdm; } iPatchDrummapMapping_t patch_drummap_mapping_list_t::find(int patch, bool includeDefault) { iPatchDrummapMapping_t ipdm_default = end(); for(iPatchDrummapMapping_t ipdm = begin(); ipdm != end(); ++ipdm) { // Look for an exact match above all else. The given patch must be valid. if(patch != CTRL_VAL_UNKNOWN && ipdm->_patch == patch) return ipdm; // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). if(includeDefault && ipdm->dontCare() && ipdm_default == end()) ipdm_default = ipdm; } return ipdm_default; } ciPatchDrummapMapping_t patch_drummap_mapping_list_t::find(int patch, bool includeDefault) const { ciPatchDrummapMapping_t ipdm_default = end(); for(ciPatchDrummapMapping_t ipdm = begin(); ipdm != end(); ++ipdm) { // Look for an exact match above all else. The given patch must be valid. if(patch != CTRL_VAL_UNKNOWN && ipdm->_patch == patch) return ipdm; // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). if(includeDefault && ipdm->dontCare() && ipdm_default == end()) ipdm_default = ipdm; } return ipdm_default; } void patch_drummap_mapping_list_t::read(Xml& xml) { int patch = CTRL_PROGRAM_VAL_DONT_CARE; DrumMap* drummap=new DrumMap[128]; for (int i=0;i<128;i++) drummap[i]=iNewDrumMap[i]; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto pdml_read_end; case Xml::TagStart: if (tag == "patch_collection") patch = readDrummapsEntryPatchCollection(xml); else if (tag == "drummap") read_new_style_drummap(xml, "drummap", drummap); else xml.unknown("patch_drummap_mapping_list_t::read"); break; case Xml::TagEnd: if (tag == "entry") { push_back(patch_drummap_mapping_t(drummap, patch)); return; } default: break; } } pdml_read_end: fprintf(stderr, "End or Error in patch_drummap_mapping_list_t::read()!\n"); delete [] drummap; } void patch_drummap_mapping_list_t::write(int level, Xml& xml) const { for (ciPatchDrummapMapping_t it = begin(); it != end(); it++) { xml.tag(level++, "entry"); const patch_drummap_mapping_t& pdm = *it; if(!pdm.dontCare()) { QString tmp="drummap); //write_new_style_drummap(level, xml, "drummap", it->drummap, true); // true = Need to save all entries. xml.etag(--level, "entry"); } } #ifdef _USE_INSTRUMENT_OVERRIDES_ void patch_drummap_mapping_list_t::writeDrummapOverrides(int level, Xml& xml) const { for(ciPatchDrummapMapping_t ipdm = begin(); ipdm != end(); ++ipdm) { const patch_drummap_mapping_t& pdm = *ipdm; if(pdm._workingDrumMapList.empty()) continue; xml.tag(level++, "drumMapPatch patch=\"%d\"", pdm._patch); pdm._workingDrumMapList.write(level, xml); xml.etag(--level, "drumMapPatch"); } } #endif //--------------------------------------------------------- // WorkingDrumMapEntry //--------------------------------------------------------- WorkingDrumMapEntry::WorkingDrumMapEntry() { _fields = NoField; } WorkingDrumMapEntry::WorkingDrumMapEntry(const DrumMap& dm, fields_t fields) { _fields = fields; _mapItem = dm; } WorkingDrumMapEntry::WorkingDrumMapEntry(const WorkingDrumMapEntry& other) { _fields = other._fields; _mapItem = other._mapItem; } WorkingDrumMapEntry& WorkingDrumMapEntry::operator=(const WorkingDrumMapEntry& other) { _fields = other._fields; _mapItem = other._mapItem; return *this; } //--------------------------------------------------------- // WorkingDrumMapList //--------------------------------------------------------- void WorkingDrumMapList::add(int index, const WorkingDrumMapEntry& item) { WorkingDrumMapPatchInsertResult_t res = insert(WorkingDrumMapPatchInsertPair_t(index, item)); if(res.second == false) { iWorkingDrumMapPatch_t& iwp = res.first; WorkingDrumMapEntry& wde = iwp->second; if(item._fields & WorkingDrumMapEntry::NameField) wde._mapItem.name = item._mapItem.name; if(item._fields & WorkingDrumMapEntry::VolField) wde._mapItem.vol = item._mapItem.vol; if(item._fields & WorkingDrumMapEntry::QuantField) wde._mapItem.quant = item._mapItem.quant; if(item._fields & WorkingDrumMapEntry::LenField) wde._mapItem.len = item._mapItem.len; if(item._fields & WorkingDrumMapEntry::ChanField) wde._mapItem.channel = item._mapItem.channel; if(item._fields & WorkingDrumMapEntry::PortField) wde._mapItem.port = item._mapItem.port; if(item._fields & WorkingDrumMapEntry::Lv1Field) wde._mapItem.lv1 = item._mapItem.lv1; if(item._fields & WorkingDrumMapEntry::Lv2Field) wde._mapItem.lv2 = item._mapItem.lv2; if(item._fields & WorkingDrumMapEntry::Lv3Field) wde._mapItem.lv3 = item._mapItem.lv3; if(item._fields & WorkingDrumMapEntry::Lv4Field) wde._mapItem.lv4 = item._mapItem.lv4; if(item._fields & WorkingDrumMapEntry::ENoteField) wde._mapItem.enote = item._mapItem.enote; if(item._fields & WorkingDrumMapEntry::ANoteField) wde._mapItem.anote = item._mapItem.anote; if(item._fields & WorkingDrumMapEntry::MuteField) wde._mapItem.mute = item._mapItem.mute; if(item._fields & WorkingDrumMapEntry::HideField) wde._mapItem.hide = item._mapItem.hide; wde._fields |= item._fields; } } int WorkingDrumMapList::remove(int index, const WorkingDrumMapEntry& item) { return remove(index, item._fields); } int WorkingDrumMapList::remove(int index, int fields) { iWorkingDrumMapPatch_t iwp = find(index); if(iwp == end()) return fields; WorkingDrumMapEntry& wde = iwp->second; int ret = wde._fields ^ fields; wde._fields &= ~fields; ret ^= wde._fields; if(wde._fields == WorkingDrumMapEntry::NoField) erase(iwp); return ret; } void WorkingDrumMapList::read(Xml& xml, bool fillUnused, int defaultIndex) { const QString start_tag = xml.s1(); int index = defaultIndex; int index_read; bool enote_read = false; bool anote_read = false; bool ok; WorkingDrumMapEntry wdme; if(fillUnused) { // Must initialize the map item in case some fields aren't given. wdme._mapItem.init(); // Technically we are overriding all fields even if some are not given. wdme._fields = WorkingDrumMapEntry::AllFields; } for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "name") { wdme._mapItem.name = xml.parse1(); wdme._fields |= WorkingDrumMapEntry::NameField; } else if (tag == "vol") { wdme._mapItem.vol = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::VolField; } else if (tag == "quant") { wdme._mapItem.quant = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::QuantField; } else if (tag == "len") { wdme._mapItem.len = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::LenField; } else if (tag == "channel") { wdme._mapItem.channel = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::ChanField; } else if (tag == "port") { wdme._mapItem.port = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::PortField; } else if (tag == "lv1") { wdme._mapItem.lv1 = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::Lv1Field; } else if (tag == "lv2") { wdme._mapItem.lv2 = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::Lv2Field; } else if (tag == "lv3") { wdme._mapItem.lv3 = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::Lv3Field; } else if (tag == "lv4") { wdme._mapItem.lv4 = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::Lv4Field; } else if (tag == "enote") { wdme._mapItem.enote = xml.parseInt(); enote_read = true; wdme._fields |= WorkingDrumMapEntry::ENoteField; } else if (tag == "anote") { wdme._mapItem.anote = xml.parseInt(); anote_read = true; wdme._fields |= WorkingDrumMapEntry::ANoteField; } else if (tag == "mute") { wdme._mapItem.mute = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::MuteField; } else if (tag == "hide") { wdme._mapItem.hide = xml.parseInt(); wdme._fields |= WorkingDrumMapEntry::HideField; } else xml.unknown(start_tag.toLatin1().constData()); break; case Xml::Attribut: if (tag == "idx" || tag == "pitch") { index_read = xml.s2().toInt(&ok); if(ok) index = index_read; } break; case Xml::TagEnd: if (tag == start_tag) { if(index >= 0 && index < 128) { // If no enote was given, set it to the index. if(fillUnused) { if(!enote_read) wdme._mapItem.enote = index; // If no anote was given, set it to the enote. if(!anote_read) wdme._mapItem.anote = wdme._mapItem.enote; } insert(WorkingDrumMapPatchInsertPair_t(index, wdme)); } return; } default: break; } } } void WorkingDrumMapList::write(int level, Xml& xml) const { int index; for(ciWorkingDrumMapPatch_t iwdp = begin(); iwdp != end(); ++iwdp) { index = iwdp->first; xml.tag(level++, "entry idx=\"%d\"", index); const WorkingDrumMapEntry& wde = iwdp->second; if(wde._fields & WorkingDrumMapEntry::NameField) xml.strTag(level, "name", wde._mapItem.name); if(wde._fields & WorkingDrumMapEntry::VolField) xml.intTag(level, "vol", wde._mapItem.vol); if(wde._fields & WorkingDrumMapEntry::QuantField) xml.intTag(level, "quant", wde._mapItem.quant); if(wde._fields & WorkingDrumMapEntry::LenField) xml.intTag(level, "len", wde._mapItem.len); if(wde._fields & WorkingDrumMapEntry::ChanField) xml.intTag(level, "channel", wde._mapItem.channel); if(wde._fields & WorkingDrumMapEntry::PortField) xml.intTag(level, "port", wde._mapItem.port); if(wde._fields & WorkingDrumMapEntry::Lv1Field) xml.intTag(level, "lv1", wde._mapItem.lv1); if(wde._fields & WorkingDrumMapEntry::Lv2Field) xml.intTag(level, "lv2", wde._mapItem.lv2); if(wde._fields & WorkingDrumMapEntry::Lv3Field) xml.intTag(level, "lv3", wde._mapItem.lv3); if(wde._fields & WorkingDrumMapEntry::Lv4Field) xml.intTag(level, "lv4", wde._mapItem.lv4); if(wde._fields & WorkingDrumMapEntry::ENoteField) xml.intTag(level, "enote", wde._mapItem.enote); if(wde._fields & WorkingDrumMapEntry::ANoteField) xml.intTag(level, "anote", wde._mapItem.anote); if(wde._fields & WorkingDrumMapEntry::MuteField) xml.intTag(level, "mute", wde._mapItem.mute); if(wde._fields & WorkingDrumMapEntry::HideField) xml.intTag(level, "hide", wde._mapItem.hide); xml.tag(--level, "/entry"); } } //--------------------------------------------------------- // WorkingDrumMapInstrumentList //--------------------------------------------------------- void WorkingDrumMapInstrumentList::read(Xml& xml) { const QString start_tag = xml.s1(); QString instr_name; WorkingDrumMapPatchList wdmpl; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "drumMapPatch") // false = Do not fill in unused items. wdmpl.read(xml, false); else xml.unknown(start_tag.toLatin1().constData()); break; case Xml::Attribut: if (tag == "instrument") { instr_name = xml.s2(); } break; case Xml::TagEnd: if (tag == start_tag) { if(!instr_name.isEmpty() && !wdmpl.empty()) insert(WorkingDrumMapInstrumentListInsertPair_t(instr_name.toStdString(), wdmpl)); return; } default: break; } } } //--------------------------------------------------------- // WorkingDrumMapPatchList //--------------------------------------------------------- void WorkingDrumMapPatchList::add(const WorkingDrumMapPatchList& other) { int patch; int index; for(ciWorkingDrumMapPatchList_t iwdmpl = other.begin(); iwdmpl != other.end(); ++iwdmpl) { patch = iwdmpl->first; const WorkingDrumMapList& wdml = iwdmpl->second; WorkingDrumMapPatchListInsertResult_t res = insert(WorkingDrumMapPatchListInsertPair_t(patch, wdml)); iWorkingDrumMapPatchList_t res_iwdmpl = res.first; if(res_iwdmpl == end()) // Error. continue; WorkingDrumMapList& res_wdml = res_iwdmpl->second; for(iWorkingDrumMapPatch_t res_iwdp = res_wdml.begin(); res_iwdp != res_wdml.end(); ++res_iwdp) { index = res_iwdp->first; WorkingDrumMapEntry& wdme = res_iwdp->second; res_wdml.add(index, wdme); } } } void WorkingDrumMapPatchList::add(int patch, const WorkingDrumMapList& list) { insert(WorkingDrumMapPatchListInsertPair_t(patch, list)); } void WorkingDrumMapPatchList::add(int patch, int index, const WorkingDrumMapEntry& item) { WorkingDrumMapPatchListInsertResult_t res = insert(WorkingDrumMapPatchListInsertPair_t(patch, WorkingDrumMapList())); iWorkingDrumMapPatchList_t iwdmpl = res.first; if(iwdmpl == end()) // Error, should exist. return; WorkingDrumMapList& wdml = iwdmpl->second; wdml.add(index, item); } void WorkingDrumMapPatchList::remove(int patch, int index, const WorkingDrumMapEntry& item, bool includeDefault) { remove(patch, index, item._fields, includeDefault); } void WorkingDrumMapPatchList::remove(int patch, int index, int fields, bool includeDefault) { // Remove requested fields from the exact patch number first. iWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch); if(iwdmpl != end()) { WorkingDrumMapList& wdml = iwdmpl->second; // Consider defaults and real patch overrides as part of the same deal, // remove all requested fields from BOTH. //fields = wdml.remove(index, fields); wdml.remove(index, fields); // No more items in the list? Remove this container list. if(wdml.empty()) erase(iwdmpl); } // Consider defaults and real patch overrides as part of the same deal, // remove all requested fields from BOTH. //if(includeDefault && fields != WorkingDrumMapEntry::NoField) if(includeDefault) { iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE); if(iwdmpl != end()) { WorkingDrumMapList& wdml = iwdmpl->second; wdml.remove(index, fields); // No more items in the list? Remove this container list. if(wdml.empty()) erase(iwdmpl); } } } void WorkingDrumMapPatchList::remove(int patch, bool includeDefault) { // Remove requested fields from the exact patch number first. iWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch); if(iwdmpl != end()) erase(iwdmpl); // Patch map not found? Look for a default patch number (all three pr, hb, lb = don't care). else if(includeDefault) { iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE); if(iwdmpl != end()) erase(iwdmpl); } } WorkingDrumMapList* WorkingDrumMapPatchList::find(int patch, bool includeDefault) { // Look for an exact match above all else. The given patch must be valid. iWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch); // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). if(iwdmpl == end()) { if(!includeDefault) return NULL; iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE); if(iwdmpl == end()) return NULL; } return &iwdmpl->second; } const WorkingDrumMapList* WorkingDrumMapPatchList::find(int patch, bool includeDefault) const { // Look for an exact match above all else. The given patch must be valid. ciWorkingDrumMapPatchList_t iwdmpl = WorkingDrumMapPatchList_t::find(patch); // If no exact match is found we'll take a default if found (all three pr, hb, lb = don't care). if(iwdmpl == end()) { if(!includeDefault) return NULL; iwdmpl = WorkingDrumMapPatchList_t::find(CTRL_PROGRAM_VAL_DONT_CARE); if(iwdmpl == end()) return NULL; } return &iwdmpl->second; } WorkingDrumMapEntry* WorkingDrumMapPatchList::find(int patch, int index, bool includeDefault) { WorkingDrumMapList* wdmpl = find(patch, includeDefault); if(!wdmpl) return NULL; iWorkingDrumMapPatch_t iwdmp = wdmpl->find(index); if(iwdmp == wdmpl->end()) return NULL; return &iwdmp->second; } const WorkingDrumMapEntry* WorkingDrumMapPatchList::find(int patch, int index, bool includeDefault) const { const WorkingDrumMapList* wdmpl = find(patch, includeDefault); if(!wdmpl) return NULL; ciWorkingDrumMapPatch_t iwdmp = wdmpl->find(index); if(iwdmp == wdmpl->end()) return NULL; return &iwdmp->second; } void WorkingDrumMapPatchList::read(Xml& xml, bool fillUnused) { const QString start_tag = xml.s1(); // Default "don't care" patch number, in case no patch number found. int patch = 0xffffff; int patch_read; bool ok; int index = 0; WorkingDrumMapList wdml; // TODO Need to move this stuff up into the caller, because a default patch map may not have been loaded yet! // For now, we rely on the loaded map being trustworthy with no duplicate enotes. Still the situation IS compensated // for at the lowest level in MidiTrack::normalizeDrumMap(), so it IS tolerant of mistakes in the loaded map. // // REMOVE Tim. newdrums. Removed. // WorkingDrumMapList* def_wdml = 0; // //if(patch != CTRL_PROGRAM_VAL_DONT_CARE) // def_wdml = find(CTRL_PROGRAM_VAL_DONT_CARE, false); // WorkingDrumMapEntry new_wdme; // // We can init these outside of the loop. // new_wdme._fields = WorkingDrumMapEntry::AllFields; // new_wdme._mapItem.init(); // // bool used_index[128]; // int used_enotes[128]; // for(int i = 0; i < 128; ++i) // { // used_index[i] = false; // used_enotes[i] = 0; // } // char unused_enotes[128]; // int unused_enotes_sz = 0; // char unused_index[128]; // int unused_index_sz = 0; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "entry") { // In case there are no index attributes in this drum map, // we use a running index. wdml.read(xml, fillUnused, index); ++index; } else if (tag == "comment") xml.parse(); else xml.unknown(start_tag.toLatin1().constData()); break; case Xml::Attribut: if (tag == "patch") { patch_read = xml.s2().toInt(&ok); if(ok) patch = patch_read; } break; case Xml::TagEnd: if (tag == start_tag) { if(!wdml.empty()) { // // // We can only deal with duplicate enotes here if requesting to // // // fill up all items, because in the context of further overrides // // // masking any unused ones here, we cannot fully know which enotes are used. // //if(fillUnused) // //{ // // Find all the used enotes and indexes. // for(iWorkingDrumMapPatch_t iwdml = wdml.begin(); iwdml != wdml.end(); ++iwdml) // { // used_index[iwdml->first] = true; // ++used_enotes[(unsigned char)iwdml->second._mapItem.enote]; // } // // // Find all the unused enotes and indexes. // for(int i = 0; i < 128; ++i) // { // if(!used_index[i]) // unused_index[unused_index_sz++] = i; // if(used_enotes[i] == 0) // unused_enotes[unused_enotes_sz++] = i; // } // // // Ensure there are NO duplicate enotes in the existing map items so far. // int unused_enotes_cnt = 0; // for(iWorkingDrumMapPatch_t iwdml = wdml.begin(); iwdml != wdml.end(); ++iwdml) // { // // More than 1 (this) usage? // if(used_enotes[(unsigned char)iwdml->second._mapItem.enote] > 1) // { // if(unused_enotes_cnt >= unused_enotes_sz) // { // fprintf(stderr, "WorkingDrumMapPatchList::read: Error: unused_enotes_cnt >= unused_enotes_sz:%d\n", // unused_enotes_sz); // break; // } // --used_enotes[(unsigned char)iwdml->second._mapItem.enote]; // iwdml->second._mapItem.enote = unused_enotes[unused_enotes_cnt++]; // } // } // // // Technically we are overriding the entire map, even if some map items weren't given. // // In case of loading a partial or incomplete map, ensure that all 128 map items are filled. // for(int i = 0; i < unused_index_sz; ++i) // { // if(unused_enotes_cnt >= unused_enotes_sz) // { // fprintf(stderr, "WorkingDrumMapPatchList::read: Error filling unused items: unused_enotes_cnt >= unused_enotes_sz:%d\n", // unused_enotes_sz); // break; // } // // Set the enote. // new_wdme._mapItem.enote = unused_enotes[unused_enotes_cnt++]; // // Might as well set the anote to enote. // new_wdme._mapItem.anote = new_wdme._mapItem.enote; // // Insert the item at the unused index. // wdml.insert(WorkingDrumMapPatchInsertPair_t(unused_index[i], new_wdme)); // } // //} // Insert the working drum map list at the patch. insert(WorkingDrumMapPatchListInsertPair_t(patch, wdml)); } return; } default: break; } } } void WorkingDrumMapPatchList::write(int level, Xml& xml) const { int patch; for(ciWorkingDrumMapPatchList_t iwdpl = begin(); iwdpl != end(); ++iwdpl) { const WorkingDrumMapList& wdml = iwdpl->second; if(wdml.empty()) continue; patch = iwdpl->first; xml.tag(level++, "drumMapPatch patch=\"%d\"", patch); wdml.write(level, xml); xml.etag(--level, "drumMapPatch"); } } //--------------------------------------------------------- // ChannelDrumMappingList //--------------------------------------------------------- ChannelDrumMappingList::ChannelDrumMappingList() { // Ensure there is always a default channel. // Initialize with a default drum map on default channel. Patch is default 0xffffff. GM-1 does not specify a drum patch number. add(-1, patch_drummap_mapping_list_t()); } void ChannelDrumMappingList::add(const ChannelDrumMappingList& other) { int channel; for(ciChannelDrumMappingList_t icdml = other.begin(); icdml != other.end(); ++icdml) { channel = icdml->first; const patch_drummap_mapping_list_t& pdml = icdml->second; add(channel, pdml); } } void ChannelDrumMappingList::add(int channel, const patch_drummap_mapping_list_t& list) { ChannelDrumMappingListInsertResult_t res = insert(ChannelDrumMappingListInsertPair_t(channel, list)); if(res.second == false) { iChannelDrumMappingList_t res_icdml = res.first; patch_drummap_mapping_list_t& res_pdml = res_icdml->second; res_pdml.add(list); } } patch_drummap_mapping_list_t* ChannelDrumMappingList::find(int channel, bool includeDefault) { // Look for an exact match above all else. The given channel must be valid. iChannelDrumMappingList_t icdml = ChannelDrumMappingList_t::find(channel); // If no exact match is found we'll take a default if found. if(icdml == end()) { if(!includeDefault) return NULL; icdml = ChannelDrumMappingList_t::find(-1); if(icdml == end()) return NULL; } return &icdml->second; } const patch_drummap_mapping_list_t* ChannelDrumMappingList::find(int channel, bool includeDefault) const { // Look for an exact match above all else. The given channel must be valid. ciChannelDrumMappingList_t icdml = ChannelDrumMappingList_t::find(channel); // If no exact match is found we'll take a default if found. if(icdml == end()) { if(!includeDefault) return NULL; icdml = ChannelDrumMappingList_t::find(-1); if(icdml == end()) return NULL; } return &icdml->second; } void ChannelDrumMappingList::read(Xml& xml) { const QString start_tag = xml.s1(); // Default "don't care" channel number, in case no channel number found. int channel = -1; // Default. int channel_read; bool ok; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "entry") { patch_drummap_mapping_list_t pdml; pdml.read(xml); if(!pdml.empty()) add(channel, pdml); } else if (tag == "comment") xml.parse(); else xml.unknown(start_tag.toLatin1().constData()); break; case Xml::Attribut: if (tag == "channel") { channel_read = xml.s2().toInt(&ok); if(ok) channel = channel_read; } break; case Xml::TagEnd: if (tag == start_tag) return; default: break; } } } void ChannelDrumMappingList::write(int level, Xml& xml) const { int channel; // Count the items used. int sz = 0; for(ciChannelDrumMappingList_t icdml = begin(); icdml != end(); ++icdml) { const patch_drummap_mapping_list_t& pdml = icdml->second; if(pdml.empty()) continue; ++sz; } for(ciChannelDrumMappingList_t icdml = begin(); icdml != end(); ++icdml) { const patch_drummap_mapping_list_t& pdml = icdml->second; if(pdml.empty()) continue; channel = icdml->first; // Don't bother with the drumMapChannel tag if not required. if(sz >= 2 || channel != -1) // -1 is default. xml.tag(level++, "drumMapChannel channel=\"%d\"", channel); pdml.write(level, xml); if(sz >= 2 || channel != -1) // -1 is default. xml.etag(--level, "drumMapChannel"); } } } // namespace MusECore muse-4.0.0/src/muse/instruments/minstrument.h000066400000000000000000000506201404105311000213450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: minstrument.h,v 1.3.2.3 2009/03/09 02:05:18 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MINSTRUMENT_H__ #define __MINSTRUMENT_H__ #include "globaldefs.h" #include #include #include #include #include #include #include "midiedit/drummap.h" #include "config.h" #ifdef MIDNAM_SUPPORT #include "midnam.h" #endif #include "midictrl_consts.h" // REMOVE Tim. newdrums. Added. // Adds the ability to override at instrument level. // But it just makes things too complex for the user. // And in a way is unnecessary and overkill, since we // already allow modifying an instrument. //#define _USE_INSTRUMENT_OVERRIDES_ // Forward declarations: namespace MusEGui { class PopupMenu; } namespace MusECore { class EventList; class MidiControllerList; class MidiPlayEvent; class Xml; //--------------------------------------------------------- // Patch //--------------------------------------------------------- struct Patch { signed char hbank, lbank, program; QString name; bool drum; inline int patch() const { return (((unsigned int)hbank & 0xff) << 16) | (((unsigned int)lbank & 0xff) << 8) | ((unsigned int)program & 0xff); } inline bool dontCare() const { return hbankDontCare() && lbankDontCare() && programDontCare(); } inline bool hbankDontCare() const { return hbank < 0; } inline bool lbankDontCare() const { return lbank < 0; } inline bool programDontCare() const { return program < 0; } void read(Xml&); void write(int level, Xml&); }; class PatchList : public std::list { public: iterator find(int patch, bool drum, bool includeDefault); const_iterator find(int patch, bool drum, bool includeDefault) const; }; typedef PatchList::iterator iPatch; typedef PatchList::const_iterator ciPatch; //--------------------------------------------------------- // PatchGroup //--------------------------------------------------------- struct PatchGroup { QString name; PatchList patches; void read(Xml&); }; class PatchGroupList : public std::vector { public: Patch* findPatch(int patch, bool drum, bool includeDefault); Patch* findPatch(int patch, bool drum, bool includeDefault) const; }; typedef PatchGroupList::iterator iPatchGroup; typedef PatchGroupList::const_iterator ciPatchGroup; struct SysEx { QString name; QString comment; int dataLen; unsigned char* data; bool read(Xml&); void write(int level, Xml&); SysEx(); SysEx(const SysEx& src); ~SysEx(); }; struct dumb_patchlist_entry_t { int prog; int lbank; int hbank; // "-1" means "unused" dumb_patchlist_entry_t(int p, int l, int h) { prog=p; lbank=l; hbank=h; } bool operator<(const dumb_patchlist_entry_t& other) const { if (hbank < other.hbank) return true; if (hbank > other.hbank) return false; if (lbank < other.lbank) return true; if (lbank > other.lbank) return false; return (prog < other.prog); } bool operator>(const dumb_patchlist_entry_t& other) const { return other < *this; } bool operator==(const dumb_patchlist_entry_t& other) const { return (prog==other.prog && lbank==other.lbank && hbank==other.hbank); } bool operator!=(const dumb_patchlist_entry_t& other) const { return (!(*this==other)); } }; struct WorkingDrumMapEntry { #ifdef _USE_INSTRUMENT_OVERRIDES_ enum OverrideType { NoOverride = 0x0, TrackDefaultOverride = 0x1, TrackOverride = 0x2, InstrumentDefaultOverride = 0x4, InstrumentOverride = 0x8, AllOverrides = InstrumentDefaultOverride | InstrumentOverride | TrackDefaultOverride | TrackOverride }; #else enum OverrideType { NoOverride = 0x0, TrackDefaultOverride = 0x1, TrackOverride = 0x2, AllOverrides = TrackDefaultOverride | TrackOverride }; #endif enum Field { NoField = 0x0, NameField = 0x1, VolField = 0x2, QuantField = 0x4, LenField = 0x8, ChanField = 0x10, PortField = 0x20, Lv1Field = 0x40, Lv2Field = 0x80, Lv3Field = 0x100, Lv4Field = 0x200, ENoteField = 0x400, ANoteField = 0x800, MuteField = 0x1000, HideField = 0x2000, AllFields = NameField | VolField | QuantField | LenField | ChanField | PortField | Lv1Field | Lv2Field | Lv3Field | Lv4Field | ENoteField | ANoteField | MuteField | HideField}; // OR'd Fields. typedef int override_t; typedef int fields_t; DrumMap _mapItem; fields_t _fields; // Starts with null _mapItem. WorkingDrumMapEntry(); // Allocates _mapItem and copies dm to it. WorkingDrumMapEntry(const DrumMap& dm, fields_t fields); // Copy ctor. WorkingDrumMapEntry(const WorkingDrumMapEntry&); WorkingDrumMapEntry& operator=(const WorkingDrumMapEntry&); }; typedef std::map < int /*index*/, WorkingDrumMapEntry, std::less > WorkingDrumMapList_t; typedef WorkingDrumMapList_t::iterator iWorkingDrumMapPatch_t; typedef WorkingDrumMapList_t::const_iterator ciWorkingDrumMapPatch_t; typedef std::pair WorkingDrumMapPatchInsertResult_t; typedef std::pair WorkingDrumMapPatchInsertPair_t; typedef std::pair WorkingDrumMapPatchRangePair_t; class WorkingDrumMapList : public WorkingDrumMapList_t { public: void add(int index,const WorkingDrumMapEntry& item); // Returns the requested fields that were NOT removed. int remove(int index, const WorkingDrumMapEntry& item); // Returns the requested fields that were NOT removed. int remove(int index, int fields); // If fillUnused is true it will fill any unused fields. void read(Xml&, bool fillUnused, int defaultIndex = -1); void write(int level, Xml&) const; }; typedef std::map < int /*patch*/, WorkingDrumMapList, std::less > WorkingDrumMapPatchList_t; typedef WorkingDrumMapPatchList_t::iterator iWorkingDrumMapPatchList_t; typedef WorkingDrumMapPatchList_t::const_iterator ciWorkingDrumMapPatchList_t; typedef std::pair WorkingDrumMapPatchListInsertResult_t; typedef std::pair WorkingDrumMapPatchListInsertPair_t; class WorkingDrumMapPatchList : public WorkingDrumMapPatchList_t { public: void add(const WorkingDrumMapPatchList& other); void add(int patch, const WorkingDrumMapList& list); void add(int patch, int index, const WorkingDrumMapEntry& item); void remove(int patch, int index, const WorkingDrumMapEntry& item, bool includeDefault); void remove(int patch, int index, int fields, bool includeDefault); void remove(int patch, bool includeDefault); WorkingDrumMapList* find(int patch, bool includeDefault); const WorkingDrumMapList* find(int patch, bool includeDefault) const; WorkingDrumMapEntry* find(int patch, int index, bool includeDefault); const WorkingDrumMapEntry* find(int patch, int index, bool includeDefault) const; // If fillUnused is true it will fill any unused items to ensure that all 128 items are filled. void read(Xml&, bool fillUnused); void write(int level, Xml&) const; }; typedef std::map < std::string /*instrument name*/, WorkingDrumMapPatchList > WorkingDrumMapInstrumentList_t; typedef WorkingDrumMapInstrumentList_t::iterator iWorkingDrumMapInstrumentList_t; typedef WorkingDrumMapInstrumentList_t::const_iterator ciWorkingDrumMapInstrumentList_t; typedef std::pair WorkingDrumMapInstrumentListInsertResult_t; typedef std::pair WorkingDrumMapInstrumentListInsertPair_t; class WorkingDrumMapInstrumentList : public WorkingDrumMapInstrumentList_t { public: void read(Xml&); }; struct patch_drummap_mapping_t { int _patch; DrumMap* drummap; int drum_in_map[128]; #ifdef _USE_INSTRUMENT_OVERRIDES_ // A list of user-altered drum map items. WorkingDrumMapList _workingDrumMapList; #endif // A NULL drummap is allowed, it means invalid (ie. invalid() returns true). patch_drummap_mapping_t(DrumMap* d, int patch = 0xffffff) { drummap = d; _patch = patch; update_drum_in_map(); } patch_drummap_mapping_t(const patch_drummap_mapping_t& that); patch_drummap_mapping_t(); ~patch_drummap_mapping_t(); patch_drummap_mapping_t& operator=(const patch_drummap_mapping_t& that); #ifdef _USE_INSTRUMENT_OVERRIDES_ void addWorkingDrumMapEntry(int index,const WorkingDrumMapEntry& item); void removeWorkingDrumMapEntry(int index, const WorkingDrumMapEntry& item); void removeWorkingDrumMapEntry(int index, int fields); #endif int map_drum_in(int enote) { return drum_in_map[enote]; } void update_drum_in_map(); inline int hbank() const { return (_patch >> 16) & 0xff; } inline int lbank() const { return (_patch >> 8) & 0xff; } inline int prog() const { return _patch & 0xff; } inline void setHBank(int v) { _patch = (_patch & 0x00ffff) | ((v & 0xff) << 16); } inline void setLBank(int v) { _patch = (_patch & 0xff00ff) | ((v & 0xff) << 8); } inline void setProg(int v) { _patch = (_patch & 0xffff00) | (v & 0xff); } inline bool dontCare() const { return hbankDontCare() && lbankDontCare() && programDontCare(); } inline bool hbankDontCare() const { const int hb = hbank(); return hb < 0 || hb > 127; } inline bool lbankDontCare() const { const int lb = lbank(); return lb < 0 || lb > 127; } inline bool programDontCare() const { const int pr = prog(); return pr < 0 || pr > 127; } bool isPatchInRange(int patch, bool includeDefault) const; bool isValid() const; QString to_string(); }; class patch_drummap_mapping_list_t : public std::list { public: void add(const patch_drummap_mapping_list_t& other); void add(const patch_drummap_mapping_t& pdm); iterator find(int patch, bool includeDefault); const_iterator find(int patch, bool includeDefault) const; void read(Xml& xml); void write(int level, Xml&) const; #ifdef _USE_INSTRUMENT_OVERRIDES_ void writeDrummapOverrides(int level, Xml& xml) const; #endif }; typedef patch_drummap_mapping_list_t::iterator iPatchDrummapMapping_t; typedef patch_drummap_mapping_list_t::const_iterator ciPatchDrummapMapping_t; // NOTE: Channel == -1 (default) is legal. typedef std::map < int /*channel*/, patch_drummap_mapping_list_t, std::less > ChannelDrumMappingList_t; typedef ChannelDrumMappingList_t::iterator iChannelDrumMappingList_t; typedef ChannelDrumMappingList_t::const_iterator ciChannelDrumMappingList_t; typedef std::pair ChannelDrumMappingListInsertResult_t; typedef std::pair ChannelDrumMappingListInsertPair_t; class ChannelDrumMappingList : public ChannelDrumMappingList_t { public: ChannelDrumMappingList(); void add(const ChannelDrumMappingList& other); void add(int channel, const patch_drummap_mapping_list_t& list); //void add(int patch, int index, const WorkingDrumMapEntry& item); //void remove(int patch, int index, const WorkingDrumMapEntry& item, bool includeDefault); //void remove(int patch, int index, int fields, bool includeDefault); //void remove(int patch, bool includeDefault); patch_drummap_mapping_list_t* find(int channel, bool includeDefault); const patch_drummap_mapping_list_t* find(int channel, bool includeDefault) const; //WorkingDrumMapEntry* find(int patch, int index, bool includeDefault); //const WorkingDrumMapEntry* find(int patch, int index, bool includeDefault) const; // If fillUnused is true it will fill any unused items to ensure that all 128 items are filled. void read(Xml&); void write(int level, Xml&) const; }; //--------------------------------------------------------- // MidiInstrument //--------------------------------------------------------- class MidiInstrument { public: // NoteOffAll = Use all note offs. // NoteOffNone = Do not use any note offs. // NoteOffConvertToZVNoteOn = Convert all note offs to zero-velocity note ons. enum NoteOffMode { NoteOffAll=0, NoteOffNone, NoteOffConvertToZVNoteOn }; private: PatchGroupList pg; MidiControllerList* _controller; QList _sysex; ChannelDrumMappingList _channelDrumMapping; bool _dirty; bool _waitForLSB; // Whether 14-bit controllers wait for LSB, or MSB and LSB are separate. NoteOffMode _noteOffMode; void init(); protected: EventList* _midiInit; EventList* _midiReset; EventList* _midiState; // Set when loading midi state in SynthI::read, to indicate version // to SynthI::initInstance, which is called later. int _tmpMidiStateVersion; char* _initScript; QString _name; QString _filePath; #ifdef MIDNAM_SUPPORT MidNamMIDIName _midnamDocument; #endif void writeDrummaps(int level, Xml& xml) const; void readDrummaps(Xml& xml); public: MidiInstrument(); virtual ~MidiInstrument(); MidiInstrument(const QString& txt); const QString& iname() const { return _name; } void setIName(const QString& txt) { _name = txt; } MType midiType() const; virtual bool isSynti() const { return false; } // Assign will 'delete' all existing patches and groups from the instrument. MidiInstrument& assign(const MidiInstrument&); QString filePath() const { return _filePath; } void setFilePath(const QString& s) { _filePath = s; } bool dirty() const { return _dirty; } void setDirty(bool v) { _dirty = v; } const QList& sysex() const { return _sysex; } void removeSysex(SysEx* sysex) { _sysex.removeAll(sysex); } void addSysex(SysEx* sysex) { _sysex.append(sysex); } QList getPatches(int channel, bool drum); unsigned getNextPatch(int channel, unsigned patch, bool drum); unsigned getPrevPatch(int channel, unsigned patch, bool drum); #ifdef _USE_INSTRUMENT_OVERRIDES_ bool setWorkingDrumMapItem(int patch, int index, const WorkingDrumMapEntry&, bool isReset); // Returns OR'd WorkingDrumMapEntry::OverrideType flags indicating whether a map item's members, // given by 'fields' (OR'd WorkingDrumMapEntry::Fields), are either the original or working map item. // Here in MidiInstrument the flags can be NoOverride and InstrumentOverride. See corresponding function // in MidiTrack for additional TrackOverride flag use. int isWorkingMapItem(int patch, int index, int fields) const; void clearDrumMapOverrides(); #endif // Returns a map item with members filled from either the original or working map item, // depending on which Field flags are set. The returned map includes any requested // WorkingDrumMapEntry::OverrideType instrument overrides. Channel can be -1 meaning default. virtual void getMapItem(int channel, int patch, int index, DrumMap& dest_map, int overrideType = WorkingDrumMapEntry::AllOverrides) const; EventList* midiInit() const { return _midiInit; } EventList* midiReset() const { return _midiReset; } EventList* midiState() const { return _midiState; } const char* initScript() const { return _initScript; } MidiControllerList* controller() const { return _controller; } bool waitForLSB() { return _waitForLSB; } void setWaitForLSB(bool v) { _waitForLSB = v; } // Finds a controller. By default it looks in this instrument's MidiControllerList map // just like calling the map's find(). But if channel or patch are given (not default), // it looks in BOTH the midnam's MidiControllerList and the instrument's MidiControllerList. // In other words, the instrument's list acts as a default for all channels and patches, // and has no 'channel' or 'patch number'. The midnam's list takes priority if used. // Like find() which finds a verbose ctl number, but this version also finds a per-note // controller if there is one for the given ctl number, if no verbose one was found. The ctl // number can be the 'real' controller number, ie the low byte can be the actual note number // and does not have to be 0xff. // Returns null if no such specific controller OR suitable default is found. MidiController* findController(int num, int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; // Fills a given MidiControllerList with all available controllers for a given channel and patch. // By default it gathers from this instrument's MidiControllerList map just like calling controller(). // But if channel or patch are given (not default), it gathers from BOTH the midnam's MidiControllerList // and the instrument's MidiControllerList. // This would not be suitable for realtime code since it may allocate. And it copies all the items to dest. void getControllers(MidiControllerList* dest, int channel = -1, int patch = CTRL_PROGRAM_VAL_DONT_CARE) const; // Virtual so that inheriters (synths etc) can return whatever they want. virtual NoteOffMode noteOffMode() const { return _noteOffMode; } // For non-synths, users can set this value. void setNoteOffMode(NoteOffMode mode) { _noteOffMode = mode; } void readMidiState(Xml& xml); virtual void reset(int); virtual QString getPatchName(int channel, int prog, bool drum, bool includeDefault) const; virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool); static void populateInstrPopup(MusEGui::PopupMenu*, int port, bool show_synths = false); // Static void read(Xml&); void write(int level, Xml&); #ifdef _USE_INSTRUMENT_OVERRIDES_ void writeDrummapOverrides(int level, Xml&) const; #endif #ifdef MIDNAM_SUPPORT MidNamMIDIName& midnamDocument() { return _midnamDocument; } const MidNamMIDIName& midnamDocument() const { return _midnamDocument; } // Possible plan: Use this INSTEAD of a direct call to MidNamMIDIName::read() // so that we can catch it and update some stuff here afterwards (controllers). bool readMidnamDocument(Xml&); #endif PatchGroupList* groups() { return &pg; } patch_drummap_mapping_list_t* get_patch_drummap_mapping(int channel, bool includeDefault); ChannelDrumMappingList* getChannelDrumMapping() { return &_channelDrumMapping; } }; //--------------------------------------------------------- // MidiInstrumentList //--------------------------------------------------------- class MidiInstrumentList : public std::list { public: MidiInstrumentList() {} iterator find(const MidiInstrument* instr); #ifdef _USE_INSTRUMENT_OVERRIDES_ void writeDrummapOverrides(int level, Xml&) const; #endif }; typedef MidiInstrumentList::iterator iMidiInstrument; typedef MidiInstrumentList::const_iterator ciMidiInstrument; extern MidiInstrumentList midiInstruments; extern MidiInstrument* genericMidiInstrument; extern void initMidiInstruments(); extern MidiInstrument* registerMidiInstrument(const QString&); extern void removeMidiInstrument(const QString& name); extern void removeMidiInstrument(const MidiInstrument* instr); } // namespace MusECore #ifdef _USE_INSTRUMENT_OVERRIDES_ namespace MusEGlobal { extern MusECore::WorkingDrumMapInstrumentList workingDrumMapInstrumentList; } #endif #endif muse-4.0.0/src/muse/key.cpp000066400000000000000000000030661404105311000155120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: key.cpp,v 1.1.1.1 2003/10/27 18:51:22 wschweer Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "key.h" #include "globals.h" #include "xml.h" namespace MusECore { int NKey::offsets[14] = { 0, 7, 14, -7, -(12), -19, -26, -10, -14, -2, -4, -6, -8, 0 }; int NKey::width() const { return 25; } //--------------------------------------------------------- // Scale::width //--------------------------------------------------------- int Scale::width() const { int i = val; if (i < 0) i = -i; return i * 7; } } // namespace MusECore muse-4.0.0/src/muse/key.h000066400000000000000000000043451404105311000151600ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: key.h,v 1.1.1.1 2003/10/27 18:51:25 wschweer Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __KEY_H__ #define __KEY_H__ #include class QPainter; class QPoint; namespace MusECore { class Xml; //--------------------------------------------------------- // NKey //--------------------------------------------------------- class NKey { static int offsets[14]; int val; public: NKey() { val = 7; } NKey(int k) { val = k; } void draw(QPainter& p, const QPoint& pt) const; int idx() const { return val; } int offset() const { return offsets[val]; } void read(Xml&); void write(int, Xml&) const; void set(int n) { val = n; } int width() const; }; //--------------------------------------------------------- // Scale //--------------------------------------------------------- class Scale { int val; // 1 = 1 sharp, -1 1 flat bool minor; public: Scale() { val = 0; minor = false; } Scale(int s, bool m = false) { val = s; minor = m; } int idx() const { return val; } void read(Xml&); void write(int, Xml&) const; void set(int n) { val = n; } void setMajorMinor(bool f) { minor = f; } // true == minor int width() const; }; } // namespace MusECore #endif muse-4.0.0/src/muse/keyevent.cpp000066400000000000000000000276401404105311000165600ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: key.cpp,v 1.7.2.7 2008/05/21 00:28:52 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "muse_math.h" #include "key.h" #include "globals.h" #include "gconfig.h" #include "xml.h" #include "keyevent.h" namespace MusEGlobal { MusECore::KeyList keymap; } namespace MusECore { #define DEFAULT_KEY KEY_C //--------------------------------------------------------- // KeyList //--------------------------------------------------------- KeyList::KeyList() { insert(std::pair (MAX_TICK+1, KeyEvent(DEFAULT_KEY, 0, false))); } //--------------------------------------------------------- // copy //--------------------------------------------------------- void KeyList::copy(const KeyList& src) { // Clear the existing destination list. KEYLIST::clear(); for (ciKeyEvent i = src.cbegin(); i != src.cend(); ++i) { KeyEvent new_e = KeyEvent(i->second); std::pair res = insert(std::pair (i->first, new_e)); if(!res.second) { fprintf(stderr, "KeyList::copy insert failed: keylist:%p key:%d tick:%d minor:%d\n", this, new_e.key, new_e.tick, new_e.minor); } } } //--------------------------------------------------------- // add //--------------------------------------------------------- void KeyList::add(unsigned tick, key_enum key, bool isMinor) { if (tick > MAX_TICK) tick = MAX_TICK; iKeyEvent e = upper_bound(tick); if (tick == e->second.tick) { e->second.key = key; e->second.minor = isMinor; } else { KeyEvent& ne = e->second; KeyEvent ev = KeyEvent(ne.key, ne.tick, ne.minor); ne.key = key; ne.tick = tick; ne.minor = isMinor; insert(std::pair (tick, ev)); } } void KeyList::add(KeyEvent e) { int tick = e.tick; key_enum k = e.key; bool is_minor = e.minor; std::pair res = insert(std::pair (tick, e)); if(!res.second) { fprintf(stderr, "KeyList::add insert failed: keylist:%p key:%d tick:%d minor:%d\n", this, e.key, e.tick, e.minor); } else { iKeyEvent ike = res.first; ++ike; // There is always a 'next' key event - there is always one at index MAX_TICK. KeyEvent& ne = ike->second; // Swap the values. (This is how the key list works.) e.key = ne.key; e.tick = ne.tick; e.minor = ne.minor; ne.key = k; ne.tick = tick; ne.minor = is_minor; } } //--------------------------------------------------------- // KeyList::dump //--------------------------------------------------------- void KeyList::dump() const { printf("\nKeyList:\n"); for (ciKeyEvent i = begin(); i != end(); ++i) { printf("%6d %06d key %6d minor:%d\n", i->first, i->second.tick, i->second.key, i->second.minor); } } //--------------------------------------------------------- // clear //--------------------------------------------------------- void KeyList::clear() { KEYLIST::clear(); insert(std::pair (MAX_TICK+1, KeyEvent(DEFAULT_KEY, 0, false))); } //--------------------------------------------------------- // keyAtTick //--------------------------------------------------------- KeyEvent KeyList::keyAtTick(unsigned tick) const { ciKeyEvent i = upper_bound(tick); if (i == end()) { printf("no key at tick %d,0x%x\n", tick, tick); return KeyEvent(); } return i->second; } //--------------------------------------------------------- // del //--------------------------------------------------------- void KeyList::del(unsigned tick) { iKeyEvent e = find(tick); if (e == end()) { printf("KeyList::del(%d): not found\n", tick); return; } del(e); } void KeyList::del(iKeyEvent e) { iKeyEvent ne = e; ++ne; if (ne == end()) { printf("KeyList::del() HALLO\n"); return; } ne->second.key = e->second.key; ne->second.tick = e->second.tick; ne->second.minor = e->second.minor; erase(e); } //--------------------------------------------------------- // addKey //--------------------------------------------------------- void KeyList::addKey(unsigned t, key_enum key, bool isMinor) { add(t, key, isMinor); } //--------------------------------------------------------- // delKey //--------------------------------------------------------- void KeyList::delKey(unsigned tick) { del(tick); } //--------------------------------------------------------- // KeyList::write //--------------------------------------------------------- void KeyList::write(int level, Xml& xml) const { xml.tag(level, "keylist"); for (ciKeyEvent i = begin(); i != end(); ++i) i->second.write(level, xml, i->first); xml.tag(level, "/keylist"); } //--------------------------------------------------------- // KeyList::read //--------------------------------------------------------- void KeyList::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "key") { KeyEvent t; unsigned tick = t.read(xml); iKeyEvent pos = find(tick); if (pos != end()) erase(pos); insert(std::pair (tick, t)); } else xml.unknown("keyList"); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "keylist") { return; } default: break; } } } KeyEvent::KeyEvent() { key = DEFAULT_KEY; tick = 0; minor = false; } KeyEvent::KeyEvent(key_enum k, unsigned tk, bool isMinor) { key = k; tick = tk; minor = isMinor; } //--------------------------------------------------------- // KeyEvent::write //--------------------------------------------------------- void KeyEvent::write(int level, Xml& xml, int at) const { xml.tag(level++, "key at=\"%d\"", at); xml.intTag(level, "tick", tick); xml.intTag(level, "val", key); xml.intTag(level, "minor", minor); xml.tag(level, "/key"); } //--------------------------------------------------------- // KeyEvent::read //--------------------------------------------------------- int KeyEvent::read(Xml& xml) { int at = 0; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return 0; case Xml::TagStart: if (tag == "tick") tick = xml.parseInt(); else if (tag == "val") key = key_enum(xml.parseInt()); else if (tag == "minor") minor = xml.parseInt(); else xml.unknown("KeyEvent"); break; case Xml::Attribut: if (tag == "at") at = xml.s2().toInt(); break; case Xml::TagEnd: if (tag == "key") { return at; } default: break; } } return 0; } //don't remove or insert new elements in keyStrs. //only renaming (keeping the semantic sense) is allowed! (flo) // Static const QStringList KeyEvent::keyStrs = QStringList() << "C (sharps)" << "G" << "D" << "A"<< "E" << "B" << "F#" << "C (flats)" << "F"<< "Bb" << "Eb"<< "Ab"<< "Db"<< "Gb" << "Am (sharps)" << "Em" << "Bm" << "F#m" << "C#m" << "G#m" << "D#m" << "Am (flats)" << "Dm" << "Gm" << "Cm" << "Fm" << "Bbm" << "Ebm"; //don't change this function (except when renaming stuff) // Static KeyEvent KeyEvent::stringToKey(QString input) //flo { int index = keyStrs.indexOf(input); KeyEvent map[]={ {KEY_C, 0, false}, {KEY_G, 0, false}, {KEY_D, 0, false}, {KEY_A, 0, false}, {KEY_E, 0, false}, {KEY_B, 0, false}, {KEY_FIS, 0, false}, {KEY_C_B, 0, false}, {KEY_F, 0, false}, {KEY_BES, 0, false}, {KEY_ES, 0, false}, {KEY_AS, 0, false}, {KEY_DES, 0, false}, {KEY_GES, 0, false}, {KEY_C, 0, true}, {KEY_G, 0, true}, {KEY_D, 0, true}, {KEY_A, 0, true}, {KEY_E, 0, true}, {KEY_B, 0, true}, {KEY_FIS, 0, true}, {KEY_C_B, 0, true}, {KEY_F, 0, true}, {KEY_BES, 0, true}, {KEY_ES, 0, true}, {KEY_AS, 0, true}, {KEY_DES, 0, true}, {KEY_GES, 0, true} }; return map[index]; } //don't change the below two functions (except when renaming stuff) // Static int KeyEvent::keyToIndex(key_enum key, bool isMinor) { int index=0; switch(key) { case KEY_C: index= isMinor ? 14 : 0; break; case KEY_G: index= isMinor ? 15 : 1; break; case KEY_D: index= isMinor ? 16 : 2; break; case KEY_A: index= isMinor ? 17 : 3; break; case KEY_E: index= isMinor ? 18 : 4; break; case KEY_B: index= isMinor ? 19 : 5; break; case KEY_FIS: index= isMinor ? 20 : 6; break; case KEY_C_B: index= isMinor ? 21 : 7; break; case KEY_F: index= isMinor ? 22 : 8; break; case KEY_BES: index= isMinor ? 23 : 9; break; case KEY_ES: index= isMinor ? 24 : 10; break; case KEY_AS: index= isMinor ? 25 : 11; break; case KEY_DES: index= isMinor ? 26 : 12; break; case KEY_GES: index= isMinor ? 27 : 13; break; case KEY_SHARP_BEGIN: case KEY_SHARP_END: case KEY_B_BEGIN: case KEY_B_END: printf("ILLEGAL FUNCTION CALL: keyToIndex called with key_sharp_begin etc.\n"); return 0; break; default: printf("ILLEGAL FUNCTION CALL: keyToIndex called with illegal key value (not in enum)\n"); return 0; } return index; } // Static QString KeyEvent::keyToString(key_enum key, bool isMinor) { return keyStrs[keyToIndex(key, isMinor)]; } int KeyEvent::keyIndex() const { return keyToIndex(key, minor); } QString KeyEvent::keyString() const { return keyToString(key, minor); } } // namespace MusECore muse-4.0.0/src/muse/keyevent.h000066400000000000000000000071221404105311000162160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tempo.h,v 1.2.2.1 2006/09/19 19:07:09 spamatica Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef KEYEVENT_H #define KEYEVENT_H #include #include #include #ifndef MAX_TICK #define MAX_TICK (0x7fffffff/100) #endif namespace MusECore { class Xml; //don't change this enum! changing the numeric values will affect //all files using key_enum, and even worse: //PREVIOUSLY SAVED FILES WILL BE CORRUPT because the keys are //stored as integers. when the integer -> key mapping changes //(by inserting or removing elements, for example), this will //break stuff! (flo) enum key_enum { KEY_SHARP_BEGIN, KEY_C, // C or am, uses # for "black keys" KEY_G, KEY_D, KEY_A, KEY_E, KEY_B, // or H in german. KEY_FIS, //replaces F with E# KEY_SHARP_END, KEY_B_BEGIN, KEY_C_B, // the same as C, but uses b for "black keys" KEY_F, KEY_BES, // or B in german KEY_ES, KEY_AS, KEY_DES, KEY_GES, //sounds like FIS, but uses b instead of # KEY_B_END }; //--------------------------------------------------------- // Key Event //--------------------------------------------------------- struct KeyEvent { key_enum key; unsigned tick; bool minor; int read(Xml&); void write(int, Xml&, int) const; KeyEvent(); KeyEvent(key_enum k, unsigned tk, bool isMinor); static const QStringList keyStrs; static KeyEvent stringToKey(QString input); static int keyToIndex(key_enum key, bool isMinor); static QString keyToString(key_enum key, bool isMinor); int keyIndex() const; QString keyString() const; }; //--------------------------------------------------------- // KeyList //--------------------------------------------------------- typedef std::map > KEYLIST; typedef KEYLIST::iterator iKeyEvent; typedef KEYLIST::const_iterator ciKeyEvent; typedef KEYLIST::reverse_iterator riKeyEvent; typedef KEYLIST::const_reverse_iterator criKeyEvent; class KeyList : public KEYLIST { void add(unsigned tick, key_enum tempo, bool isMinor); void add(KeyEvent e); void del(iKeyEvent); void del(unsigned tick); public: KeyList(); // Makes a copy of the source list including all items. // This clears existing items in the destination list. void copy(const KeyList& src); void clear(); void read(Xml&); void write(int, Xml&) const; void dump() const; KeyEvent keyAtTick(unsigned tick) const; void addKey(unsigned t, key_enum newKey, bool isMinor); void delKey(unsigned tick); }; } // namespace MusECore namespace MusEGlobal { extern MusECore::KeyList keymap; } #endif // KEYEVENT_H muse-4.0.0/src/muse/klick1.h000066400000000000000000006036761404105311000155620ustar00rootroot00000000000000#ifndef __KLICK_1_H__ #define __KLICK_1_H__ static const float defaultKlick1[] = { 0.00067139 ,-0.00415039 ,-0.00408936 ,-0.00286865 ,0.00863647 , 0.01782227 ,0.05905151 ,0.15887451 ,0.28036499 ,0.26165771 , 0.02786255 ,-0.27114868 ,-0.44372559 ,-0.42837524 ,-0.28781128 , -0.08627319 ,0.10696411 ,0.20272827 ,0.13861084 ,-0.02041626 , -0.13891602 ,-0.15573120 ,-0.08966064 ,-0.00949097 ,0.01962280 , -0.03268433 ,-0.10122681 ,-0.14855957 ,-0.22067261 ,-0.30673218 , -0.29174805 ,-0.18646240 ,-0.05935669 ,0.13848877 ,0.40219116 , 0.44461060 ,0.13690186 ,-0.29843140 ,-0.66677856 ,-0.89129639 , -0.83499146 ,-0.43106079 ,0.14215088 ,0.61065674 ,0.78820801 , 0.63299561 ,0.25811768 ,-0.13897705 ,-0.35241699 ,-0.28207397 , -0.03448486 ,0.18515015 ,0.25369263 ,0.15612793 ,-0.09545898 , -0.37042236 ,-0.46871948 ,-0.33932495 ,-0.11596680 ,0.05850220 , 0.10668945 ,0.02297974 ,-0.12881470 ,-0.28692627 ,-0.35638428 , -0.24215698 ,-0.01245117 ,0.17938232 ,0.30993652 ,0.36386108 , 0.26022339 ,-0.00012207 ,-0.28527832 ,-0.51000977 ,-0.63125610 , -0.54873657 ,-0.22717285 ,0.18817139 ,0.52899170 ,0.71572876 , 0.70507812 ,0.50531006 ,0.22119141 ,0.00982666 ,-0.07824707 , -0.12280273 ,-0.18280029 ,-0.24484253 ,-0.28869629 ,-0.31152344 , -0.26815796 ,-0.11645508 ,0.07919312 ,0.23443604 ,0.31814575 , 0.33361816 ,0.26199341 ,0.13818359 ,0.04016113 ,-0.01284790 , -0.10125732 ,-0.25442505 ,-0.39102173 ,-0.42358398 ,-0.29345703 , 0.00057983 ,0.36178589 ,0.60623169 ,0.63580322 ,0.49734497 , 0.29718018 ,0.11221313 ,0.01959229 ,0.04418945 ,0.16580200 , 0.29318237 ,0.35208130 ,0.30670166 ,0.20062256 ,0.10601807 , 0.07980347 ,0.10827637 ,0.13766479 ,0.15100098 ,0.12353516 , 0.06448364 ,0.00985718 ,-0.00588989 ,0.01348877 ,0.06256104 , 0.13027954 ,0.21148682 ,0.26330566 ,0.24447632 ,0.12713623 , -0.04101562 ,-0.14550781 ,-0.12585449 ,0.01748657 ,0.22409058 , 0.42816162 ,0.54513550 ,0.55810547 ,0.51489258 ,0.45834351 , 0.38800049 ,0.28402710 ,0.17056274 ,0.08819580 ,0.04672241 , 0.05694580 ,0.11401367 ,0.20089722 ,0.28359985 ,0.33218384 , 0.35559082 ,0.35644531 ,0.34011841 ,0.32135010 ,0.28741455 , 0.23086548 ,0.16317749 ,0.11972046 ,0.11090088 ,0.13775635 , 0.18905640 ,0.24710083 ,0.28704834 ,0.28430176 ,0.23754883 , 0.16830444 ,0.11413574 ,0.10995483 ,0.16061401 ,0.24484253 , 0.32171631 ,0.37139893 ,0.37170410 ,0.31045532 ,0.20739746 , 0.11151123 ,0.07345581 ,0.08645630 ,0.11654663 ,0.14569092 , 0.15304565 ,0.14303589 ,0.12142944 ,0.09997559 ,0.09027100 , 0.10433960 ,0.14431763 ,0.20886230 ,0.26199341 ,0.29626465 , 0.29141235 ,0.26641846 ,0.23342896 ,0.22335815 ,0.25897217 , 0.31021118 ,0.33151245 ,0.27111816 ,0.15579224 ,0.02392578 , -0.05239868 ,-0.06872559 ,-0.02999878 ,0.03445435 ,0.10217285 , 0.14608765 ,0.15521240 ,0.13342285 ,0.08230591 ,0.02883911 , -0.01907349 ,-0.04147339 ,-0.02694702 ,0.01956177 ,0.08239746 , 0.12365723 ,0.13946533 ,0.15136719 ,0.16708374 ,0.19705200 , 0.21850586 ,0.21411133 ,0.17730713 ,0.10510254 ,0.03024292 , -0.03451538 ,-0.05899048 ,-0.06124878 ,-0.04760742 ,-0.04281616 , -0.04138184 ,-0.05908203 ,-0.09548950 ,-0.14816284 ,-0.20223999 , -0.23330688 ,-0.23037720 ,-0.18103027 ,-0.10171509 ,-0.01858521 , 0.04196167 ,0.06216431 ,0.04113770 ,-0.01464844 ,-0.08108521 , -0.13882446 ,-0.17160034 ,-0.16958618 ,-0.14120483 ,-0.10031128 , -0.05899048 ,-0.04370117 ,-0.03442383 ,-0.04275513 ,-0.04721069 , -0.06423950 ,-0.09600830 ,-0.13977051 ,-0.19564819 ,-0.24343872 , -0.28524780 ,-0.30358887 ,-0.29934692 ,-0.27761841 ,-0.24707031 , -0.22851562 ,-0.22897339 ,-0.26635742 ,-0.30978394 ,-0.35540771 , -0.37399292 ,-0.36019897 ,-0.31646729 ,-0.25750732 ,-0.21142578 , -0.18814087 ,-0.18899536 ,-0.20178223 ,-0.22329712 ,-0.23947144 , -0.26641846 ,-0.28417969 ,-0.30596924 ,-0.31851196 ,-0.32650757 , -0.32382202 ,-0.30102539 ,-0.27072144 ,-0.23336792 ,-0.20001221 , -0.17892456 ,-0.18414307 ,-0.22467041 ,-0.28250122 ,-0.34146118 , -0.37786865 ,-0.38702393 ,-0.37002563 ,-0.34091187 ,-0.30108643 , -0.26583862 ,-0.24389648 ,-0.24087524 ,-0.25750732 ,-0.27731323 , -0.29443359 ,-0.29486084 ,-0.27902222 ,-0.25888062 ,-0.23953247 , -0.24169922 ,-0.25448608 ,-0.27947998 ,-0.30230713 ,-0.31161499 , -0.31103516 ,-0.30282593 ,-0.29702759 ,-0.27993774 ,-0.26062012 , -0.22982788 ,-0.19610596 ,-0.16659546 ,-0.15084839 ,-0.15164185 , -0.16448975 ,-0.18194580 ,-0.21014404 ,-0.23425293 ,-0.25881958 , -0.28088379 ,-0.29739380 ,-0.31033325 ,-0.30795288 ,-0.30065918 , -0.27981567 ,-0.25326538 ,-0.22274780 ,-0.20269775 ,-0.19119263 , -0.18591309 ,-0.18380737 ,-0.17538452 ,-0.17016602 ,-0.15734863 , -0.15792847 ,-0.15069580 ,-0.14962769 ,-0.13006592 ,-0.11618042 , -0.09588623 ,-0.08584595 ,-0.08447266 ,-0.08981323 ,-0.10244751 , -0.11706543 ,-0.12481689 ,-0.12805176 ,-0.12619019 ,-0.13195801 , -0.14993286 ,-0.17538452 ,-0.20791626 ,-0.22723389 ,-0.23208618 , -0.21929932 ,-0.19210815 ,-0.15609741 ,-0.11959839 ,-0.08718872 , -0.05856323 ,-0.04013062 ,-0.03051758 ,-0.03616333 ,-0.05026245 , -0.06561279 ,-0.07263184 ,-0.06433105 ,-0.04623413 ,-0.02307129 , -0.00411987 ,0.01303101 ,0.01815796 ,0.01623535 ,0.00054932 , -0.02600098 ,-0.05206299 ,-0.07174683 ,-0.07482910 ,-0.06384277 , -0.04019165 ,-0.01464844 ,0.00817871 ,0.01119995 ,0.00030518 , -0.02392578 ,-0.05026245 ,-0.06600952 ,-0.07223511 ,-0.05682373 , -0.03091431 ,0.00946045 ,0.04595947 ,0.08541870 ,0.10833740 , 0.12847900 ,0.13507080 ,0.13641357 ,0.12191772 ,0.10913086 , 0.09344482 ,0.09194946 ,0.09780884 ,0.11486816 ,0.12890625 , 0.13952637 ,0.13854980 ,0.12588501 ,0.11224365 ,0.09948730 , 0.09777832 ,0.10430908 ,0.11007690 ,0.12060547 ,0.11349487 , 0.10574341 ,0.09371948 ,0.09210205 ,0.09817505 ,0.11401367 , 0.12844849 ,0.13385010 ,0.12533569 ,0.10702515 ,0.09732056 , 0.09594727 ,0.10882568 ,0.11386108 ,0.11944580 ,0.11779785 , 0.12158203 ,0.13284302 ,0.14953613 ,0.16281128 ,0.16888428 , 0.16668701 ,0.16802979 ,0.16098022 ,0.16098022 ,0.15753174 , 0.16094971 ,0.16845703 ,0.17330933 ,0.17984009 ,0.17773438 , 0.16796875 ,0.15359497 ,0.13851929 ,0.12460327 ,0.11614990 , 0.11050415 ,0.12194824 ,0.13986206 ,0.15310669 ,0.15487671 , 0.14294434 ,0.13024902 ,0.12240601 ,0.12048340 ,0.12994385 , 0.13934326 ,0.15451050 ,0.16531372 ,0.17819214 ,0.18374634 , 0.18563843 ,0.18603516 ,0.17718506 ,0.17294312 ,0.16748047 , 0.16983032 ,0.17022705 ,0.17190552 ,0.16149902 ,0.14382935 , 0.12368774 ,0.10958862 ,0.10391235 ,0.10562134 ,0.10925293 , 0.11831665 ,0.12283325 ,0.12014771 ,0.10736084 ,0.09567261 , 0.08575439 ,0.07812500 ,0.08007812 ,0.08596802 ,0.09899902 , 0.09823608 ,0.08502197 ,0.06524658 ,0.04986572 ,0.05911255 , 0.07531738 ,0.10836792 ,0.13107300 ,0.14520264 ,0.13858032 , 0.11752319 ,0.09378052 ,0.07409668 ,0.06124878 ,0.04815674 , 0.04064941 ,0.03231812 ,0.02572632 ,0.01434326 ,0.00415039 , -0.00277710 ,-0.00500488 ,-0.00592041 ,-0.00234985 ,-0.00637817 , -0.00717163 ,-0.01907349 ,-0.02212524 ,-0.01733398 ,-0.00939941 , 0.00335693 ,0.01077271 ,0.02532959 ,0.03094482 ,0.03280640 , 0.01605225 ,-0.00061035 ,-0.02700806 ,-0.04449463 ,-0.05334473 , -0.04629517 ,-0.02694702 ,-0.01119995 ,0.00247192 ,0.00073242 , -0.01034546 ,-0.03176880 ,-0.05068970 ,-0.06433105 ,-0.07742310 , -0.08187866 ,-0.08496094 ,-0.07879639 ,-0.07174683 ,-0.05773926 , -0.04888916 ,-0.04019165 ,-0.04229736 ,-0.04412842 ,-0.05328369 , -0.06964111 ,-0.08663940 ,-0.10208130 ,-0.10910034 ,-0.11563110 , -0.11840820 ,-0.12219238 ,-0.12493896 ,-0.11874390 ,-0.11172485 , -0.09771729 ,-0.09585571 ,-0.09378052 ,-0.09893799 ,-0.10342407 , -0.10427856 ,-0.10379028 ,-0.09860229 ,-0.09277344 ,-0.08810425 , -0.08837891 ,-0.10299683 ,-0.12533569 ,-0.15032959 ,-0.16976929 , -0.17404175 ,-0.16885376 ,-0.15914917 ,-0.14904785 ,-0.14727783 , -0.14517212 ,-0.15118408 ,-0.15789795 ,-0.16748047 ,-0.17230225 , -0.17721558 ,-0.17581177 ,-0.17544556 ,-0.17230225 ,-0.17105103 , -0.16613770 ,-0.17282104 ,-0.18020630 ,-0.18508911 ,-0.17803955 , -0.16226196 ,-0.14770508 ,-0.13986206 ,-0.13760376 ,-0.14120483 , -0.15341187 ,-0.16796875 ,-0.17889404 ,-0.18377686 ,-0.18640137 , -0.19161987 ,-0.19430542 ,-0.19866943 ,-0.20397949 ,-0.21008301 , -0.21408081 ,-0.21752930 ,-0.21014404 ,-0.20040894 ,-0.18988037 , -0.18206787 ,-0.18322754 ,-0.18515015 ,-0.18499756 ,-0.18161011 , -0.17535400 ,-0.17239380 ,-0.17095947 ,-0.17459106 ,-0.17138672 , -0.16885376 ,-0.15917969 ,-0.15780640 ,-0.15612793 ,-0.15869141 , -0.16003418 ,-0.15832520 ,-0.14987183 ,-0.14562988 ,-0.14675903 , -0.15487671 ,-0.16830444 ,-0.18026733 ,-0.18771362 ,-0.18856812 , -0.18463135 ,-0.17147827 ,-0.15560913 ,-0.14556885 ,-0.14028931 , -0.13714600 ,-0.12982178 ,-0.11822510 ,-0.11004639 ,-0.09982300 , -0.09548950 ,-0.09417725 ,-0.10424805 ,-0.11041260 ,-0.11437988 , -0.10861206 ,-0.10693359 ,-0.09854126 ,-0.10293579 ,-0.10342407 , -0.10330200 ,-0.09777832 ,-0.08834839 ,-0.08108521 ,-0.07434082 , -0.07397461 ,-0.08013916 ,-0.09365845 ,-0.10961914 ,-0.12130737 , -0.12408447 ,-0.12048340 ,-0.11572266 ,-0.11169434 ,-0.10513306 , -0.09375000 ,-0.08093262 ,-0.07363892 ,-0.06680298 ,-0.05783081 , -0.04840088 ,-0.04104614 ,-0.03710938 ,-0.03833008 ,-0.03622437 , -0.03753662 ,-0.03875732 ,-0.04464722 ,-0.04177856 ,-0.03234863 , -0.01940918 ,-0.00860596 ,-0.00320435 ,0.00064087 ,0.00341797 , 0.00454712 ,0.00607300 ,0.00851440 ,0.00601196 ,-0.00064087 , -0.00936890 ,-0.00991821 ,-0.01501465 ,-0.01916504 ,-0.02075195 , -0.02438354 ,-0.02081299 ,-0.01553345 ,0.00070190 ,0.01690674 , 0.03149414 ,0.04452515 ,0.04995728 ,0.04681396 ,0.03271484 , 0.02050781 ,0.00854492 ,0.01391602 ,0.02658081 ,0.04684448 , 0.06652832 ,0.08065796 ,0.08413696 ,0.06875610 ,0.05252075 , 0.04199219 ,0.04986572 ,0.05957031 ,0.07794189 ,0.08773804 , 0.08843994 ,0.08114624 ,0.07397461 ,0.06616211 ,0.05865479 , 0.05560303 ,0.05822754 ,0.06262207 ,0.06262207 ,0.06698608 , 0.07580566 ,0.09469604 ,0.11267090 ,0.13024902 ,0.13641357 , 0.13375854 ,0.11926270 ,0.11007690 ,0.09991455 ,0.09692383 , 0.09768677 ,0.10000610 ,0.10955811 ,0.12634277 ,0.14208984 , 0.15527344 ,0.15838623 ,0.15832520 ,0.14828491 ,0.14251709 , 0.13378906 ,0.12982178 ,0.12539673 ,0.12503052 ,0.12799072 , 0.13119507 ,0.13677979 ,0.14477539 ,0.15353394 ,0.16229248 , 0.16497803 ,0.16799927 ,0.16931152 ,0.16983032 ,0.16973877 , 0.16406250 ,0.15930176 ,0.15386963 ,0.14788818 ,0.14779663 , 0.14868164 ,0.15487671 ,0.16711426 ,0.17767334 ,0.18078613 , 0.17984009 ,0.17507935 ,0.17239380 ,0.16540527 ,0.16448975 , 0.16275024 ,0.16275024 ,0.16143799 ,0.16757202 ,0.17376709 , 0.17938232 ,0.17727661 ,0.17898560 ,0.17022705 ,0.15835571 , 0.14166260 ,0.13287354 ,0.12896729 ,0.13549805 ,0.15179443 , 0.16622925 ,0.17593384 ,0.17901611 ,0.17901611 ,0.17587280 , 0.16409302 ,0.15530396 ,0.15127563 ,0.14968872 ,0.15249634 , 0.16241455 ,0.17370605 ,0.18600464 ,0.19000244 ,0.18688965 , 0.18380737 ,0.17907715 ,0.17758179 ,0.17468262 ,0.17279053 , 0.17160034 ,0.16577148 ,0.15927124 ,0.15744019 ,0.16540527 , 0.17858887 ,0.17587280 ,0.17639160 ,0.16055298 ,0.14910889 , 0.13119507 ,0.12271118 ,0.11758423 ,0.11965942 ,0.12631226 , 0.13595581 ,0.14562988 ,0.14782715 ,0.14517212 ,0.14297485 , 0.13687134 ,0.13107300 ,0.12680054 ,0.13018799 ,0.13296509 , 0.13195801 ,0.12805176 ,0.13116455 ,0.13369751 ,0.14132690 , 0.13763428 ,0.13296509 ,0.12136841 ,0.11059570 ,0.10687256 , 0.10049438 ,0.10162354 ,0.09649658 ,0.09375000 ,0.09823608 , 0.09729004 ,0.09292603 ,0.08416748 ,0.07308960 ,0.07412720 , 0.07305908 ,0.08245850 ,0.08236694 ,0.08233643 ,0.07586670 , 0.06866455 ,0.06097412 ,0.05899048 ,0.05398560 ,0.05407715 , 0.04791260 ,0.04309082 ,0.03686523 ,0.03176880 ,0.03982544 , 0.04769897 ,0.05865479 ,0.06790161 ,0.07492065 ,0.07400513 , 0.06704712 ,0.06085205 ,0.04681396 ,0.03848267 ,0.03491211 , 0.03497314 ,0.04022217 ,0.03845215 ,0.03408813 ,0.02661133 , 0.02523804 ,0.02624512 ,0.02783203 ,0.02713013 ,0.02609253 , 0.01702881 ,0.00939941 ,-0.00009155 ,-0.00552368 ,-0.00979614 , -0.01605225 ,-0.01062012 ,-0.01214600 ,-0.00976562 ,-0.02044678 , -0.02822876 ,-0.02923584 ,-0.02203369 ,-0.00558472 ,0.00387573 , 0.00723267 ,0.00509644 ,0.00161743 ,0.00018311 ,-0.01770020 , -0.02963257 ,-0.03921509 ,-0.04241943 ,-0.04223633 ,-0.03851318 , -0.03567505 ,-0.03448486 ,-0.03659058 ,-0.03707886 ,-0.04235840 , -0.04055786 ,-0.04370117 ,-0.03790283 ,-0.03887939 ,-0.03964233 , -0.04855347 ,-0.05895996 ,-0.06610107 ,-0.06561279 ,-0.05551147 , -0.03881836 ,-0.02435303 ,-0.01904297 ,-0.01821899 ,-0.02120972 , -0.01910400 ,-0.01992798 ,-0.01953125 ,-0.01507568 ,-0.01559448 , -0.01593018 ,-0.01867676 ,-0.02517700 ,-0.02920532 ,-0.04010010 , -0.04150391 ,-0.03488159 ,-0.02603149 ,-0.01342773 ,-0.01635742 , -0.02087402 ,-0.04367065 ,-0.05984497 ,-0.07272339 ,-0.07781982 , -0.07272339 ,-0.06646729 ,-0.05508423 ,-0.04803467 ,-0.04235840 , -0.03573608 ,-0.03182983 ,-0.03396606 ,-0.03402710 ,-0.03265381 , -0.03140259 ,-0.03305054 ,-0.03363037 ,-0.03085327 ,-0.02966309 , -0.03573608 ,-0.04406738 ,-0.03884888 ,-0.02258301 ,-0.00720215 , -0.00280762 ,-0.01028442 ,-0.01950073 ,-0.03137207 ,-0.03707886 , -0.03616333 ,-0.03622437 ,-0.03311157 ,-0.02914429 ,-0.02526855 , -0.01898193 ,-0.01477051 ,-0.00448608 ,-0.00375366 ,-0.00100708 , -0.00857544 ,-0.00805664 ,-0.01028442 ,-0.01333618 ,-0.01473999 , -0.01586914 ,-0.01000977 ,-0.00393677 ,0.00448608 ,0.01043701 , 0.01824951 ,0.01559448 ,0.00473022 ,-0.00509644 ,-0.00979614 , -0.00851440 ,-0.00244141 ,0.00869751 ,0.02124023 ,0.02798462 , 0.02923584 ,0.02267456 ,0.01251221 ,0.00512695 ,-0.00109863 , -0.00277710 ,0.00286865 ,0.01034546 ,0.01873779 ,0.03448486 , 0.04244995 ,0.04852295 ,0.04159546 ,0.03665161 ,0.03149414 , 0.03491211 ,0.04025269 ,0.04589844 ,0.04730225 ,0.04980469 , 0.04644775 ,0.04763794 ,0.04507446 ,0.04986572 ,0.05517578 , 0.06521606 ,0.07321167 ,0.07882690 ,0.07846069 ,0.07843018 , 0.07797241 ,0.07144165 ,0.06695557 ,0.05960083 ,0.06082153 , 0.06442261 ,0.07269287 ,0.07754517 ,0.08593750 ,0.08715820 , 0.08682251 ,0.08761597 ,0.08944702 ,0.09286499 ,0.09652710 , 0.09411621 ,0.09042358 ,0.08227539 ,0.07760620 ,0.07620239 , 0.08767700 ,0.10302734 ,0.12408447 ,0.13418579 ,0.13998413 , 0.13543701 ,0.13339233 ,0.12231445 ,0.12493896 ,0.12069702 , 0.12664795 ,0.12854004 ,0.13284302 ,0.12634277 ,0.12054443 , 0.11450195 ,0.11743164 ,0.11541748 ,0.11083984 ,0.10311890 , 0.10424805 ,0.10879517 ,0.11791992 ,0.12634277 ,0.12979126 , 0.13595581 ,0.13378906 ,0.13064575 ,0.12152100 ,0.12097168 , 0.12329102 ,0.12490845 ,0.12902832 ,0.13104248 ,0.13555908 , 0.13772583 ,0.14163208 ,0.13867188 ,0.13717651 ,0.13299561 , 0.13497925 ,0.13955688 ,0.13986206 ,0.14123535 ,0.14038086 , 0.14031982 ,0.13861084 ,0.13775635 ,0.14160156 ,0.15051270 , 0.15350342 ,0.15084839 ,0.13916016 ,0.12924194 ,0.12112427 , 0.11834717 ,0.11706543 ,0.11978149 ,0.12619019 ,0.13037109 , 0.13146973 ,0.13165283 ,0.13150024 ,0.12591553 ,0.11880493 , 0.10916138 ,0.10702515 ,0.11041260 ,0.11453247 ,0.12008667 , 0.11798096 ,0.11795044 ,0.11532593 ,0.11132812 ,0.10748291 , 0.10162354 ,0.10662842 ,0.11346436 ,0.12332153 ,0.12930298 , 0.12677002 ,0.12451172 ,0.12414551 ,0.12054443 ,0.11755371 , 0.11355591 ,0.11224365 ,0.10873413 ,0.10433960 ,0.09906006 , 0.09866333 ,0.09771729 ,0.09295654 ,0.09552002 ,0.09823608 , 0.10122681 ,0.10437012 ,0.10037231 ,0.10214233 ,0.09866333 , 0.10296631 ,0.10440063 ,0.10076904 ,0.09692383 ,0.08807373 , 0.08898926 ,0.08938599 ,0.09420776 ,0.09429932 ,0.09332275 , 0.08551025 ,0.07617188 ,0.07098389 ,0.06921387 ,0.07315063 , 0.07583618 ,0.07925415 ,0.08502197 ,0.08724976 ,0.08541870 , 0.07012939 ,0.06170654 ,0.04724121 ,0.05123901 ,0.05029297 , 0.05563354 ,0.05773926 ,0.06130981 ,0.06219482 ,0.06173706 , 0.05648804 ,0.05206299 ,0.04986572 ,0.04553223 ,0.04327393 , 0.04025269 ,0.03622437 ,0.03189087 ,0.03234863 ,0.03140259 , 0.03323364 ,0.03051758 ,0.03146362 ,0.02926636 ,0.03097534 , 0.02966309 ,0.02444458 ,0.01425171 ,0.00433350 ,-0.00515747 , -0.00885010 ,-0.01345825 ,-0.01019287 ,-0.01123047 ,-0.01196289 , -0.01873779 ,-0.01718140 ,-0.01959229 ,-0.01593018 ,-0.01385498 , -0.01156616 ,-0.01864624 ,-0.02474976 ,-0.04101562 ,-0.04983521 , -0.05859375 ,-0.05682373 ,-0.05682373 ,-0.05422974 ,-0.05020142 , -0.04370117 ,-0.03878784 ,-0.04235840 ,-0.04983521 ,-0.06207275 , -0.06735229 ,-0.06915283 ,-0.07046509 ,-0.07482910 ,-0.07751465 , -0.08090210 ,-0.08282471 ,-0.08883667 ,-0.09292603 ,-0.09802246 , -0.10260010 ,-0.10769653 ,-0.11090088 ,-0.11260986 ,-0.11605835 , -0.11883545 ,-0.12924194 ,-0.13327026 ,-0.13412476 ,-0.13632202 , -0.13763428 ,-0.14117432 ,-0.14376831 ,-0.14599609 ,-0.14773560 , -0.14553833 ,-0.14514160 ,-0.14154053 ,-0.14208984 ,-0.14108276 , -0.14605713 ,-0.14639282 ,-0.15036011 ,-0.15081787 ,-0.15829468 , -0.16839600 ,-0.17626953 ,-0.17715454 ,-0.17849731 ,-0.17495728 , -0.17102051 ,-0.17105103 ,-0.17092896 ,-0.17376709 ,-0.17398071 , -0.17373657 ,-0.17272949 ,-0.17144775 ,-0.17456055 ,-0.17276001 , -0.17495728 ,-0.17721558 ,-0.18408203 ,-0.18563843 ,-0.18893433 , -0.18511963 ,-0.17846680 ,-0.16748047 ,-0.16183472 ,-0.16439819 , -0.16534424 ,-0.16918945 ,-0.17022705 ,-0.17227173 ,-0.17147827 , -0.17102051 ,-0.16748047 ,-0.16531372 ,-0.16183472 ,-0.15603638 , -0.15216064 ,-0.15914917 ,-0.16268921 ,-0.16879272 ,-0.16271973 , -0.15515137 ,-0.14776611 ,-0.14163208 ,-0.14282227 ,-0.15045166 , -0.16262817 ,-0.16796875 ,-0.16879272 ,-0.15835571 ,-0.14761353 , -0.13470459 ,-0.12875366 ,-0.12936401 ,-0.12973022 ,-0.12881470 , -0.12979126 ,-0.13537598 ,-0.14035034 ,-0.13760376 ,-0.13327026 , -0.12573242 ,-0.12274170 ,-0.12399292 ,-0.12500000 ,-0.12609863 , -0.12368774 ,-0.11776733 ,-0.10656738 ,-0.10113525 ,-0.10119629 , -0.10427856 ,-0.10906982 ,-0.11175537 ,-0.11520386 ,-0.11093140 , -0.10940552 ,-0.09826660 ,-0.09707642 ,-0.09561157 ,-0.09979248 , -0.10168457 ,-0.10287476 ,-0.10037231 ,-0.09808350 ,-0.09857178 , -0.09811401 ,-0.09460449 ,-0.09106445 ,-0.08718872 ,-0.08355713 , -0.08236694 ,-0.08181763 ,-0.08016968 ,-0.07391357 ,-0.06518555 , -0.05819702 ,-0.05102539 ,-0.05212402 ,-0.05368042 ,-0.05688477 , -0.05987549 ,-0.06167603 ,-0.06295776 ,-0.06085205 ,-0.05764771 , -0.06127930 ,-0.05987549 ,-0.06253052 ,-0.05953979 ,-0.05892944 , -0.06002808 ,-0.05319214 ,-0.05603027 ,-0.05462646 ,-0.05813599 , -0.05862427 ,-0.06118774 ,-0.05728149 ,-0.05465698 ,-0.04846191 , -0.04806519 ,-0.05114746 ,-0.05062866 ,-0.05117798 ,-0.04492188 , -0.03933716 ,-0.03482056 ,-0.03225708 ,-0.03356934 ,-0.03616333 , -0.03268433 ,-0.03625488 ,-0.03079224 ,-0.03329468 ,-0.03210449 , -0.03317261 ,-0.03048706 ,-0.02780151 ,-0.02395630 ,-0.02651978 , -0.02606201 ,-0.02523804 ,-0.02078247 ,-0.01296997 ,-0.00717163 , -0.00283813 ,-0.00631714 ,-0.01113892 ,-0.01779175 ,-0.01550293 , -0.01385498 ,-0.00891113 ,-0.00634766 ,-0.00854492 ,-0.00936890 , -0.01644897 ,-0.01293945 ,-0.01416016 ,-0.00863647 ,-0.00842285 , -0.00509644 ,-0.00186157 ,-0.00070190 ,-0.00271606 ,-0.00073242 , -0.00097656 ,0.00152588 ,-0.00103760 ,0.00509644 ,0.00772095 , 0.01473999 ,0.01696777 ,0.01821899 ,0.01217651 ,0.00765991 , 0.00207520 ,0.00549316 ,0.00729370 ,0.01211548 ,0.01211548 , 0.01562500 ,0.02090454 ,0.02310181 ,0.02264404 ,0.01562500 , 0.00906372 ,0.00726318 ,0.01123047 ,0.02050781 ,0.02917480 , 0.02975464 ,0.03094482 ,0.01916504 ,0.01693726 ,0.01168823 , 0.01559448 ,0.01614380 ,0.01773071 ,0.01528931 ,0.01858521 , 0.02056885 ,0.02346802 ,0.02008057 ,0.01556396 ,0.01696777 , 0.01742554 ,0.02215576 ,0.02493286 ,0.02474976 ,0.02667236 , 0.02395630 ,0.01431274 ,0.00903320 ,0.00247192 ,0.00503540 , 0.00866699 ,0.01776123 ,0.02008057 ,0.02172852 ,0.01434326 , 0.01559448 ,0.01214600 ,0.01254272 ,0.01388550 ,0.01559448 , 0.01916504 ,0.01736450 ,0.01391602 ,0.00765991 ,0.00338745 , 0.00369263 ,0.00781250 ,0.01336670 ,0.01611328 ,0.01559448 , 0.00726318 ,-0.00494385 ,-0.01739502 ,-0.02114868 ,-0.01742554 , -0.00622559 ,0.00192261 ,0.00866699 ,0.00503540 ,0.00207520 , -0.00595093 ,-0.00891113 ,-0.01382446 ,-0.01248169 ,-0.01116943 , -0.00762939 ,-0.00192261 ,-0.00152588 ,-0.00411987 ,-0.01428223 , -0.01812744 ,-0.02700806 ,-0.02294922 ,-0.02355957 ,-0.01766968 , -0.01385498 ,-0.01553345 ,-0.02163696 ,-0.03057861 ,-0.03436279 , -0.03320312 ,-0.02731323 ,-0.02264404 ,-0.01904297 ,-0.02172852 , -0.02740479 ,-0.03439331 ,-0.04238892 ,-0.04580688 ,-0.04592896 , -0.03875732 ,-0.03143311 ,-0.02514648 ,-0.02612305 ,-0.03659058 , -0.04458618 ,-0.05285645 ,-0.05465698 ,-0.05242920 ,-0.04937744 , -0.04318237 ,-0.04418945 ,-0.04394531 ,-0.05001831 ,-0.05310059 , -0.05743408 ,-0.05850220 ,-0.05599976 ,-0.05111694 ,-0.04672241 , -0.04846191 ,-0.05511475 ,-0.05947876 ,-0.06381226 ,-0.06436157 , -0.06512451 ,-0.05947876 ,-0.06082153 ,-0.05590820 ,-0.06170654 , -0.06384277 ,-0.06826782 ,-0.06997681 ,-0.07489014 ,-0.07916260 , -0.08282471 ,-0.08401489 ,-0.08319092 ,-0.08230591 ,-0.08184814 , -0.08410645 ,-0.08401489 ,-0.08279419 ,-0.08267212 ,-0.08325195 , -0.08227539 ,-0.08273315 ,-0.08499146 ,-0.08926392 ,-0.09420776 , -0.09020996 ,-0.08358765 ,-0.07269287 ,-0.07348633 ,-0.07485962 , -0.07968140 ,-0.08575439 ,-0.08810425 ,-0.09323120 ,-0.09155273 , -0.09371948 ,-0.09545898 ,-0.09857178 ,-0.10293579 ,-0.10739136 , -0.10989380 ,-0.11221313 ,-0.10995483 ,-0.10644531 ,-0.10388184 , -0.09762573 ,-0.09548950 ,-0.09371948 ,-0.09594727 ,-0.10116577 , -0.10601807 ,-0.10690308 ,-0.10559082 ,-0.09988403 ,-0.10028076 , -0.09463501 ,-0.09985352 ,-0.09680176 ,-0.10253906 ,-0.10244751 , -0.09991455 ,-0.09722900 ,-0.09722900 ,-0.09991455 ,-0.10772705 , -0.11218262 ,-0.11526489 ,-0.10992432 ,-0.10522461 ,-0.09756470 , -0.09558105 ,-0.09454346 ,-0.09597778 ,-0.09808350 ,-0.10028076 , -0.09991455 ,-0.09939575 ,-0.09729004 ,-0.09411621 ,-0.09506226 , -0.09329224 ,-0.09985352 ,-0.09768677 ,-0.09460449 ,-0.08316040 , -0.07705688 ,-0.07791138 ,-0.08361816 ,-0.08758545 ,-0.08801270 , -0.08712769 ,-0.08145142 ,-0.08181763 ,-0.08190918 ,-0.08273315 , -0.09020996 ,-0.09109497 ,-0.09371948 ,-0.08889771 ,-0.08843994 , -0.08319092 ,-0.08364868 ,-0.08047485 ,-0.08288574 ,-0.08212280 , -0.08117676 ,-0.07949829 ,-0.07629395 ,-0.06991577 ,-0.06613159 , -0.06292725 ,-0.06210327 ,-0.06390381 ,-0.06338501 ,-0.06961060 , -0.07302856 ,-0.07354736 ,-0.06826782 ,-0.05722046 ,-0.04989624 , -0.04443359 ,-0.03936768 ,-0.03918457 ,-0.03930664 ,-0.04406738 , -0.04589844 ,-0.04931641 ,-0.04681396 ,-0.04443359 ,-0.04244995 , -0.03564453 ,-0.03628540 ,-0.03350830 ,-0.03930664 ,-0.04141235 , -0.04678345 ,-0.04711914 ,-0.04327393 ,-0.03704834 ,-0.03091431 , -0.02786255 ,-0.03045654 ,-0.03140259 ,-0.03350830 ,-0.02880859 , -0.02288818 ,-0.01220703 ,-0.00878906 ,-0.00604248 ,-0.01412964 , -0.01821899 ,-0.02392578 ,-0.02297974 ,-0.01907349 ,-0.01248169 , -0.00543213 ,-0.00067139 ,0.00076294 ,0.00109863 ,-0.00369263 , -0.00543213 ,-0.00857544 ,-0.00363159 ,-0.00021362 ,0.00729370 , 0.00991821 ,0.00988770 ,0.01126099 ,0.00775146 ,0.01074219 , 0.00735474 ,0.00982666 ,0.00869751 ,0.01159668 ,0.00955200 , 0.01336670 ,0.01348877 ,0.01779175 ,0.01739502 ,0.01867676 , 0.02316284 ,0.02651978 ,0.02493286 ,0.02435303 ,0.02133179 , 0.02755737 ,0.02914429 ,0.03155518 ,0.03396606 ,0.03018188 , 0.03097534 ,0.02749634 ,0.03186035 ,0.03408813 ,0.03713989 , 0.03717041 ,0.03930664 ,0.03848267 ,0.04107666 ,0.04333496 , 0.04458618 ,0.04418945 ,0.04461670 ,0.04461670 ,0.04855347 , 0.04727173 ,0.05203247 ,0.05258179 ,0.05203247 ,0.05123901 , 0.04415894 ,0.04284668 ,0.04244995 ,0.04721069 ,0.05035400 , 0.05822754 ,0.05993652 ,0.06271362 ,0.05813599 ,0.05740356 , 0.05383301 ,0.05557251 ,0.05386353 ,0.06082153 ,0.06527710 , 0.07226562 ,0.07717896 ,0.07833862 ,0.07586670 ,0.07180786 , 0.05908203 ,0.05786133 ,0.05285645 ,0.06231689 ,0.06686401 , 0.07373047 ,0.07656860 ,0.07672119 ,0.07186890 ,0.06692505 , 0.06625366 ,0.07086182 ,0.07717896 ,0.07882690 ,0.07797241 , 0.07455444 ,0.07003784 ,0.06698608 ,0.06927490 ,0.07440186 , 0.07714844 ,0.08239746 ,0.08410645 ,0.08459473 ,0.08151245 , 0.07708740 ,0.07580566 ,0.07977295 ,0.09204102 ,0.10165405 , 0.10528564 ,0.10031128 ,0.08993530 ,0.07705688 ,0.06832886 , 0.06658936 ,0.06567383 ,0.07009888 ,0.07052612 ,0.07620239 , 0.07455444 ,0.07876587 ,0.07546997 ,0.07217407 ,0.07061768 , 0.06652832 ,0.07049561 ,0.07058716 ,0.07659912 ,0.07891846 , 0.08062744 ,0.07751465 ,0.07406616 ,0.07226562 ,0.07360840 , 0.07580566 ,0.08233643 ,0.08462524 ,0.08499146 ,0.08242798 , 0.07574463 ,0.07318115 ,0.06744385 ,0.06835938 ,0.06655884 , 0.06829834 ,0.06881714 ,0.06872559 ,0.06878662 ,0.06750488 , 0.06604004 ,0.06533813 ,0.06564331 ,0.06878662 ,0.06967163 , 0.07135010 ,0.06704712 ,0.06478882 ,0.06179810 ,0.05947876 , 0.06002808 ,0.05688477 ,0.05603027 ,0.05123901 ,0.04852295 , 0.05123901 ,0.05554199 ,0.06091309 ,0.06478882 ,0.06655884 , 0.06750488 ,0.06472778 ,0.05877686 ,0.05374146 ,0.04772949 , 0.04943848 ,0.05078125 ,0.05642700 ,0.05786133 ,0.05636597 , 0.05175781 ,0.04364014 ,0.04074097 ,0.03927612 ,0.04464722 , 0.04901123 ,0.05203247 ,0.05215454 ,0.05377197 ,0.05078125 , 0.04946899 ,0.04629517 ,0.04913330 ,0.04843140 ,0.05044556 , 0.04541016 ,0.04241943 ,0.03808594 ,0.03488159 ,0.02886963 , 0.03051758 ,0.02880859 ,0.03671265 ,0.03936768 ,0.04412842 , 0.04595947 ,0.04022217 ,0.03756714 ,0.02841187 ,0.02960205 , 0.02886963 ,0.03140259 ,0.03411865 ,0.03710938 ,0.03890991 , 0.03802490 ,0.03146362 ,0.02612305 ,0.02227783 ,0.02389526 , 0.03060913 ,0.03405762 ,0.03366089 ,0.03051758 ,0.02487183 , 0.02395630 ,0.02050781 ,0.02346802 ,0.02142334 ,0.02343750 , 0.02185059 ,0.02923584 ,0.03182983 ,0.03765869 ,0.03665161 , 0.03454590 ,0.03274536 ,0.02835083 ,0.02618408 ,0.02438354 , 0.02047729 ,0.02001953 ,0.01824951 ,0.01962280 ,0.01818848 , 0.01483154 ,0.00985718 ,0.00820923 ,0.00857544 ,0.01519775 , 0.02001953 ,0.02615356 ,0.02578735 ,0.02258301 ,0.01260376 , 0.01077271 ,0.00946045 ,0.01568604 ,0.01864624 ,0.02050781 , 0.01733398 ,0.00997925 ,0.00765991 ,0.00646973 ,0.01119995 , 0.01473999 ,0.01876831 ,0.02255249 ,0.02450562 ,0.02432251 , 0.02188110 ,0.01904297 ,0.01263428 ,0.01071167 ,0.00778198 , 0.01562500 ,0.02041626 ,0.02709961 ,0.02746582 ,0.02484131 , 0.01742554 ,0.01293945 ,0.01171875 ,0.01431274 ,0.02041626 , 0.02401733 ,0.02349854 ,0.02096558 ,0.01861572 ,0.01525879 , 0.01950073 ,0.01788330 ,0.02441406 ,0.02264404 ,0.02178955 , 0.01736450 ,0.01431274 ,0.01388550 ,0.01693726 ,0.02224731 , 0.02655029 ,0.02975464 ,0.03002930 ,0.02978516 ,0.02825928 , 0.02536011 ,0.02264404 ,0.02261353 ,0.02450562 ,0.03262329 , 0.03552246 ,0.04321289 ,0.04113770 ,0.04022217 ,0.03140259 , 0.02883911 ,0.02661133 ,0.03582764 ,0.03799438 ,0.04376221 , 0.04064941 ,0.04067993 ,0.03540039 ,0.03182983 ,0.03060913 , 0.03048706 ,0.03726196 ,0.04229736 ,0.04693604 ,0.04580688 , 0.04077148 ,0.03573608 ,0.03369141 ,0.03887939 ,0.04553223 , 0.04983521 ,0.05169678 ,0.04629517 ,0.04290771 ,0.03759766 , 0.04104614 ,0.04687500 ,0.05596924 ,0.05871582 ,0.05908203 , 0.05648804 ,0.05291748 ,0.05392456 ,0.04846191 ,0.05041504 , 0.04937744 ,0.04772949 ,0.04986572 ,0.04989624 ,0.05383301 , 0.05514526 ,0.05999756 ,0.05911255 ,0.06433105 ,0.06094360 , 0.06518555 ,0.06130981 ,0.06048584 ,0.05725098 ,0.05789185 , 0.05725098 ,0.05874634 ,0.05905151 ,0.05737305 ,0.05603027 , 0.05426025 ,0.05386353 ,0.05691528 ,0.05816650 ,0.05874634 , 0.05770874 ,0.05346680 ,0.05426025 ,0.05380249 ,0.05786133 , 0.05899048 ,0.06140137 ,0.06124878 ,0.06307983 ,0.06353760 , 0.06912231 ,0.06927490 ,0.07312012 ,0.07495117 ,0.07272339 , 0.07180786 ,0.06219482 ,0.06088257 ,0.05688477 ,0.06091309 , 0.06607056 ,0.07147217 ,0.07092285 ,0.06567383 ,0.06222534 , 0.05557251 ,0.05252075 ,0.05209351 ,0.05334473 ,0.06182861 , 0.06823730 ,0.07412720 ,0.07479858 ,0.07022095 ,0.06030273 , 0.05526733 ,0.04934692 ,0.05477905 ,0.05993652 ,0.06838989 , 0.07571411 ,0.07589722 ,0.07260132 ,0.06405640 ,0.05682373 , 0.05389404 ,0.05291748 ,0.05606079 ,0.05905151 ,0.06533813 , 0.06692505 ,0.07064819 ,0.07080078 ,0.07025146 ,0.06817627 , 0.06582642 ,0.06604004 ,0.06750488 ,0.06832886 ,0.06787109 , 0.06481934 ,0.06088257 ,0.05819702 ,0.05825806 ,0.05996704 , 0.06262207 ,0.06616211 ,0.06826782 ,0.06750488 ,0.06652832 , 0.06002808 ,0.05908203 ,0.05468750 ,0.05429077 ,0.05474854 , 0.05642700 ,0.06265259 ,0.06170654 ,0.06747437 ,0.06042480 , 0.05688477 ,0.04949951 ,0.04721069 ,0.04592896 ,0.04861450 , 0.04541016 ,0.04910278 ,0.04498291 ,0.05081177 ,0.04544067 , 0.04815674 ,0.04504395 ,0.04681396 ,0.04638672 ,0.04986572 , 0.04721069 ,0.04776001 ,0.04412842 ,0.04507446 ,0.04241943 , 0.04281616 ,0.04116821 ,0.04061890 ,0.04067993 ,0.03890991 , 0.04107666 ,0.04110718 ,0.04330444 ,0.03930664 ,0.03942871 , 0.03442383 ,0.03543091 ,0.03494263 ,0.03274536 ,0.03366089 , 0.02877808 ,0.02926636 ,0.02569580 ,0.02578735 ,0.02389526 , 0.02404785 ,0.02478027 ,0.02667236 ,0.02786255 ,0.02755737 , 0.02435303 ,0.02136230 ,0.01693726 ,0.01605225 ,0.01477051 , 0.01477051 ,0.01382446 ,0.01174927 ,0.00982666 ,0.00778198 , 0.00375366 ,0.00509644 ,0.00335693 ,0.00811768 ,0.01083374 , 0.01296997 ,0.01168823 ,0.00683594 ,0.00247192 ,-0.00198364 , -0.00448608 ,-0.00686646 ,-0.00756836 ,-0.00814819 ,-0.00497437 , -0.00152588 ,0.00158691 ,0.00686646 ,0.00286865 ,0.00421143 , -0.00189209 ,-0.00250244 ,-0.00350952 ,-0.00125122 ,0.00131226 , 0.00057983 ,-0.00320435 ,-0.00942993 ,-0.01336670 ,-0.01330566 , -0.01211548 ,-0.00799561 ,-0.00424194 ,-0.00268555 ,-0.00473022 , -0.00750732 ,-0.01257324 ,-0.01287842 ,-0.01638794 ,-0.01565552 , -0.02026367 ,-0.01742554 ,-0.02029419 ,-0.01867676 ,-0.02035522 , -0.01821899 ,-0.01593018 ,-0.01654053 ,-0.01409912 ,-0.01525879 , -0.01626587 ,-0.02142334 ,-0.02374268 ,-0.02493286 ,-0.02200317 , -0.01519775 ,-0.01507568 ,-0.01464844 ,-0.02090454 ,-0.02337646 , -0.02789307 ,-0.02828979 ,-0.02780151 ,-0.02749634 ,-0.02597046 , -0.02886963 ,-0.03121948 ,-0.03060913 ,-0.03082275 ,-0.02835083 , -0.02648926 ,-0.02438354 ,-0.02603149 ,-0.02661133 ,-0.03390503 , -0.03674316 ,-0.04266357 ,-0.04067993 ,-0.03921509 ,-0.03399658 , -0.03100586 ,-0.03079224 ,-0.03057861 ,-0.03308105 ,-0.03179932 , -0.02966309 ,-0.02731323 ,-0.02615356 ,-0.02389526 ,-0.02560425 , -0.02664185 ,-0.03164673 ,-0.03588867 ,-0.03961182 ,-0.04022217 , -0.03878784 ,-0.03839111 ,-0.03573608 ,-0.03402710 ,-0.03176880 , -0.03100586 ,-0.03121948 ,-0.03240967 ,-0.03381348 ,-0.03677368 , -0.03656006 ,-0.03753662 ,-0.03533936 ,-0.03570557 ,-0.03710938 , -0.03833008 ,-0.03936768 ,-0.04180908 ,-0.04199219 ,-0.04180908 , -0.04107666 ,-0.03396606 ,-0.03228760 ,-0.02694702 ,-0.02609253 , -0.02740479 ,-0.02478027 ,-0.02999878 ,-0.03234863 ,-0.03607178 , -0.03805542 ,-0.03482056 ,-0.03265381 ,-0.02792358 ,-0.02645874 , -0.02569580 ,-0.02874756 ,-0.02951050 ,-0.02883911 ,-0.02908325 , -0.02658081 ,-0.02737427 ,-0.02609253 ,-0.02743530 ,-0.02822876 , -0.02926636 ,-0.02905273 ,-0.02706909 ,-0.02642822 ,-0.02401733 , -0.02420044 ,-0.02359009 ,-0.02380371 ,-0.02566528 ,-0.02310181 , -0.02551270 ,-0.02401733 ,-0.02294922 ,-0.01907349 ,-0.01602173 , -0.01461792 ,-0.01516724 ,-0.01684570 ,-0.02038574 ,-0.01950073 , -0.01776123 ,-0.01376343 ,-0.01077271 ,-0.01019287 ,-0.01303101 , -0.01373291 ,-0.01641846 ,-0.01779175 ,-0.01724243 ,-0.01870728 , -0.01547241 ,-0.01565552 ,-0.01541138 ,-0.01394653 ,-0.01281738 , -0.00985718 ,-0.00860596 ,-0.00619507 ,-0.00296021 ,-0.00137329 , 0.00149536 ,-0.00057983 ,-0.00021362 ,-0.00274658 ,-0.00424194 , -0.00488281 ,-0.00558472 ,-0.00442505 ,-0.00384521 ,-0.00445557 , -0.00640869 ,-0.01110840 ,-0.01425171 ,-0.01687622 ,-0.01687622 , -0.01113892 ,-0.00988770 ,-0.00448608 ,-0.00335693 ,-0.00137329 , 0.00051880 ,0.00131226 ,0.00186157 ,0.00302124 ,0.00238037 , 0.00244141 ,0.00030518 ,-0.00155640 ,-0.00320435 ,-0.00375366 , -0.00451660 ,-0.00372314 ,-0.00149536 ,0.00158691 ,0.00329590 , 0.00466919 ,0.00643921 ,0.00592041 ,0.00338745 ,0.00018311 , -0.00540161 ,-0.00326538 ,-0.00195312 ,-0.00012207 ,0.00192261 , 0.00167847 ,0.00189209 ,-0.00180054 ,-0.00469971 ,-0.00750732 , -0.00820923 ,-0.00582886 ,-0.00195312 ,0.00024414 ,0.00424194 , 0.00595093 ,0.00775146 ,0.00854492 ,0.00997925 ,0.01162720 , 0.01388550 ,0.01434326 ,0.01205444 ,0.00823975 ,0.00457764 , 0.00164795 ,-0.00070190 ,0.00292969 ,0.00201416 ,0.00640869 , 0.00637817 ,0.00561523 ,0.00759888 ,0.00564575 ,0.00408936 , 0.00299072 ,0.00152588 ,0.00466919 ,0.00424194 ,0.00631714 , 0.00781250 ,0.00546265 ,0.00643921 ,0.00286865 ,0.00512695 , 0.00549316 ,0.00820923 ,0.01074219 ,0.00863647 ,0.00772095 , 0.00375366 ,-0.00103760 ,-0.00149536 ,-0.00457764 ,-0.00061035 , -0.00329590 ,0.00250244 ,-0.00067139 ,0.00161743 ,0.00024414 , -0.00064087 ,0.00030518 ,-0.00027466 ,0.00170898 ,0.00540161 , 0.00650024 ,0.00588989 ,0.00030518 ,-0.00280762 ,-0.00723267 , -0.00497437 ,-0.00460815 ,-0.00457764 ,-0.00451660 ,-0.00772095 , -0.00671387 ,-0.00939941 ,-0.00726318 ,-0.00933838 ,-0.00506592 , -0.00454712 ,-0.00457764 ,-0.00326538 ,-0.00631714 ,-0.00720215 , -0.01248169 ,-0.01776123 ,-0.01861572 ,-0.02258301 ,-0.02081299 , -0.02038574 ,-0.01425171 ,-0.01113892 ,-0.00765991 ,-0.01071167 , -0.01116943 ,-0.01641846 ,-0.02084351 ,-0.02474976 ,-0.02746582 , -0.02206421 ,-0.02438354 ,-0.01815796 ,-0.02258301 ,-0.02130127 , -0.02383423 ,-0.02615356 ,-0.02734375 ,-0.02923584 ,-0.03085327 , -0.03143311 ,-0.02999878 ,-0.03137207 ,-0.03094482 ,-0.03573608 , -0.03533936 ,-0.03485107 ,-0.03179932 ,-0.02697754 ,-0.02914429 , -0.02880859 ,-0.03781128 ,-0.03982544 ,-0.04577637 ,-0.04458618 , -0.04409790 ,-0.03836060 ,-0.03704834 ,-0.03274536 ,-0.03305054 , -0.03451538 ,-0.03741455 ,-0.04110718 ,-0.04449463 ,-0.04586792 , -0.04586792 ,-0.04446411 ,-0.04464722 ,-0.04531860 ,-0.04550171 , -0.04754639 ,-0.04675293 ,-0.04586792 ,-0.04318237 ,-0.04196167 , -0.04138184 ,-0.04507446 ,-0.04794312 ,-0.04992676 ,-0.05450439 , -0.05081177 ,-0.05322266 ,-0.04721069 ,-0.04763794 ,-0.04360962 , -0.04415894 ,-0.04492188 ,-0.04980469 ,-0.05337524 ,-0.05587769 , -0.05471802 ,-0.05151367 ,-0.04809570 ,-0.04492188 ,-0.04458618 , -0.04138184 ,-0.04421997 ,-0.04223633 ,-0.04724121 ,-0.04931641 , -0.05380249 ,-0.05502319 ,-0.05471802 ,-0.05368042 ,-0.04809570 , -0.04803467 ,-0.04626465 ,-0.04678345 ,-0.05020142 ,-0.04986572 , -0.05105591 ,-0.04986572 ,-0.04797363 ,-0.04727173 ,-0.04925537 , -0.05123901 ,-0.05453491 ,-0.05514526 ,-0.05459595 ,-0.05203247 , -0.05065918 ,-0.04849243 ,-0.04763794 ,-0.04669189 ,-0.04632568 , -0.04714966 ,-0.04983521 ,-0.05502319 ,-0.05691528 ,-0.05853271 , -0.05770874 ,-0.05642700 ,-0.05065918 ,-0.04895020 ,-0.04455566 , -0.04754639 ,-0.04766846 ,-0.04846191 ,-0.04675293 ,-0.04718018 , -0.04580688 ,-0.04504395 ,-0.04315186 ,-0.04589844 ,-0.04583740 , -0.04937744 ,-0.04760742 ,-0.04980469 ,-0.04760742 ,-0.04891968 , -0.04763794 ,-0.04849243 ,-0.04800415 ,-0.05072021 ,-0.05065918 , -0.05203247 ,-0.04980469 ,-0.04760742 ,-0.04541016 ,-0.04321289 , -0.04235840 ,-0.04275513 ,-0.04370117 ,-0.04492188 ,-0.04763794 , -0.04541016 ,-0.04498291 ,-0.03836060 ,-0.03799438 ,-0.03436279 , -0.03671265 ,-0.03744507 ,-0.04104614 ,-0.04190063 ,-0.04629517 , -0.04193115 ,-0.04141235 ,-0.03536987 ,-0.03259277 ,-0.03192139 , -0.03430176 ,-0.03720093 ,-0.04135132 ,-0.04110718 ,-0.03967285 , -0.03881836 ,-0.03225708 ,-0.03530884 ,-0.02825928 ,-0.03143311 , -0.02777100 ,-0.02923584 ,-0.02999878 ,-0.02963257 ,-0.03002930 , -0.02871704 ,-0.03048706 ,-0.02874756 ,-0.02828979 ,-0.02697754 , -0.02560425 ,-0.02569580 ,-0.02429199 ,-0.02307129 ,-0.02166748 , -0.01776123 ,-0.01684570 ,-0.01516724 ,-0.01766968 ,-0.01913452 , -0.02340698 ,-0.02478027 ,-0.02569580 ,-0.02209473 ,-0.01776123 , -0.01379395 ,-0.00894165 ,-0.01119995 ,-0.01159668 ,-0.01901245 , -0.01782227 ,-0.01721191 ,-0.01348877 ,-0.01013184 ,-0.00955200 , -0.01101685 ,-0.00820923 ,-0.01065063 ,-0.00769043 ,-0.01110840 , -0.00683594 ,-0.01150513 ,-0.00732422 ,-0.01062012 ,-0.00683594 , -0.00717163 ,-0.00503540 ,-0.00543213 ,-0.00372314 ,-0.00497437 , -0.00106812 ,-0.00195312 ,0.00335693 ,0.00067139 ,0.00247192 , -0.00146484 ,-0.00155640 ,-0.00494385 ,-0.00421143 ,-0.00537109 , -0.00289917 ,-0.00140381 ,0.00369263 ,0.00469971 ,0.00857544 , 0.00549316 ,0.00341797 ,0.00234985 ,0.00039673 ,0.00231934 , 0.00256348 ,0.00366211 ,0.00476074 ,0.00543213 ,0.00256348 , 0.00411987 ,-0.00100708 ,0.00109863 ,-0.00018311 ,0.00292969 , 0.00683594 ,0.00814819 ,0.01126099 ,0.00677490 ,0.00561523 , -0.00024414 ,0.00122070 ,0.00149536 ,0.00512695 ,0.00946045 , 0.00994873 ,0.01119995 ,0.00686646 ,0.00598145 ,0.00372314 , 0.00778198 ,0.00854492 ,0.01171875 ,0.01080322 ,0.01074219 , 0.00778198 ,0.00595093 ,0.00375366 ,0.00515747 ,0.00543213 , 0.01000977 ,0.01071167 ,0.01174927 ,0.01074219 ,0.00775146 , 0.00732422 ,0.00369263 ,0.00646973 ,0.00503540 ,0.01129150 , 0.01074219 ,0.01525879 ,0.01205444 ,0.01654053 ,0.01300049 , 0.01470947 ,0.01348877 ,0.01290894 ,0.01132202 ,0.01074219 , 0.01217651 ,0.01205444 ,0.01611328 ,0.01428223 ,0.01562500 , 0.01214600 ,0.01165771 ,0.00903320 ,0.00955200 ,0.00979614 , 0.00961304 ,0.01284790 ,0.01400757 ,0.01550293 ,0.01531982 , 0.01376343 ,0.01306152 ,0.01339722 ,0.01211548 ,0.01611328 , 0.01510620 ,0.01794434 ,0.01550293 ,0.01705933 ,0.01467896 , 0.01565552 ,0.01477051 ,0.01516724 ,0.01608276 ,0.01428223 , 0.01480103 ,0.01382446 ,0.00820923 ,0.00854492 ,0.00381470 , 0.00769043 ,0.00994873 ,0.01077271 ,0.01565552 ,0.01293945 , 0.01309204 ,0.01025391 ,0.00558472 ,0.00552368 ,0.00241089 , 0.00430298 ,0.00546265 ,0.00552368 ,0.00863647 ,0.00723267 , 0.01040649 ,0.00991821 ,0.00683594 ,0.00769043 ,0.00207520 , 0.00585938 ,0.00262451 ,0.00534058 ,0.00479126 ,0.00411987 , 0.00204468 ,0.00073242 ,-0.00109863 ,0.00070190 ,0.00332642 , 0.00598145 ,0.00903320 ,0.00686646 ,0.00595093 ,0.00460815 , 0.00469971 ,0.00549316 ,0.00469971 ,0.00677490 ,0.00473022 , 0.00280762 ,0.00076294 ,-0.00372314 ,-0.00588989 ,-0.00674438 , -0.00595093 ,-0.00140381 ,0.00234985 ,0.00521851 ,0.00186157 , -0.00006104 ,-0.00683594 ,-0.00848389 ,-0.00720215 ,-0.00726318 , 0.00207520 ,-0.00064087 ,0.00244141 ,-0.00189209 ,-0.00640869 , -0.00756836 ,-0.01165771 ,-0.00891113 ,-0.00549316 ,-0.00323486 , 0.00024414 ,-0.00369263 ,-0.00323486 ,-0.00811768 ,-0.00677490 , -0.00674438 ,-0.00546265 ,-0.00238037 ,-0.00369263 ,-0.00192261 , -0.00460815 ,-0.00234985 ,-0.00280762 ,-0.00067139 ,-0.00054932 , -0.00109863 ,-0.00326538 ,-0.00320435 ,-0.00637817 ,-0.00280762 , -0.00717163 ,-0.00244141 ,-0.00668335 ,-0.00244141 ,-0.00759888 , -0.00592041 ,-0.00939941 ,-0.00720215 ,-0.00720215 ,-0.00677490 , -0.00674438 ,-0.00814819 ,-0.00671387 ,-0.00765991 ,-0.00503540 , -0.00408936 ,-0.00509644 ,-0.00488281 ,-0.00863647 ,-0.01107788 , -0.01083374 ,-0.00927734 ,-0.00640869 ,-0.00189209 ,-0.00329590 , 0.00070190 ,-0.00323486 ,-0.00457764 ,-0.00634766 ,-0.00762939 , -0.00723267 ,-0.00717163 ,-0.00988770 ,-0.00894165 ,-0.01071167 , -0.00897217 ,-0.00939941 ,-0.00720215 ,-0.00769043 ,-0.00759888 , -0.00897217 ,-0.01028442 ,-0.01116943 ,-0.00851440 ,-0.01119995 , -0.00930786 ,-0.01129150 ,-0.00885010 ,-0.00817871 ,-0.00274658 , -0.00326538 ,0.00109863 ,-0.00189209 ,-0.00329590 ,-0.00540161 , -0.00814819 ,-0.00845337 ,-0.00949097 ,-0.00622559 ,-0.00598145 , -0.00320435 ,-0.00021362 ,-0.00015259 ,0.00198364 ,-0.00277710 , -0.00286865 ,-0.00628662 ,-0.00811768 ,-0.00979614 ,-0.01300049 , -0.01324463 ,-0.01214600 ,-0.01196289 ,-0.00680542 ,-0.00720215 , -0.00238037 ,-0.00543213 ,-0.00503540 ,-0.00674438 ,-0.00857544 , -0.00845337 ,-0.00860596 ,-0.00625610 ,-0.00549316 ,-0.00106812 , -0.00012207 ,-0.00161743 ,-0.00091553 ,-0.00646973 ,-0.00488281 , -0.00820923 ,-0.00537109 ,-0.00723267 ,-0.00192261 ,-0.00326538 , 0.00109863 ,-0.00100708 ,-0.00021362 ,-0.00234985 ,-0.00021362 , -0.00146484 ,0.00109863 ,0.00158691 ,0.00247192 ,0.00329590 , 0.00161743 ,0.00241089 ,0.00250244 ,0.00106812 ,0.00167847 , 0.00015259 ,0.00296021 ,0.00506592 ,0.00726318 ,0.00735474 , 0.00631714 ,0.00607300 ,0.00497437 ,0.00692749 ,0.00549316 , 0.01034546 ,0.00646973 ,0.00894165 ,0.00607300 ,0.00588989 , 0.00466919 ,0.00598145 ,0.00549316 ,0.00866699 ,0.00851440 , 0.00958252 ,0.01025391 ,0.00958252 ,0.00982666 ,0.00518799 , 0.00717163 ,0.00607300 ,0.00897217 ,0.01080322 ,0.01083374 , 0.00942993 ,0.00643921 ,0.00555420 ,0.00457764 ,0.00997925 , 0.01162720 ,0.01742554 ,0.01651001 ,0.01736450 ,0.01345825 , 0.00988770 ,0.00952148 ,0.00769043 ,0.01080322 ,0.01168823 , 0.01342773 ,0.01211548 ,0.01080322 ,0.00903320 ,0.00946045 , 0.01126099 ,0.01211548 ,0.01296997 ,0.01260376 ,0.01202393 , 0.01354980 ,0.01202393 ,0.01348877 ,0.01296997 ,0.01123047 , 0.01083374 ,0.00726318 ,0.00683594 ,0.00817871 ,0.00857544 , 0.01171875 ,0.01205444 ,0.01394653 ,0.01293945 ,0.01431274 , 0.01303101 ,0.01510620 ,0.01351929 ,0.01336670 ,0.01306152 , 0.01159668 ,0.01174927 ,0.00985718 ,0.00994873 ,0.01165771 , 0.01257324 ,0.01168823 ,0.01254272 ,0.01165771 ,0.01123047 , 0.01126099 ,0.01080322 ,0.01254272 ,0.01300049 ,0.01428223 , 0.01257324 ,0.01211548 ,0.01168823 ,0.01211548 ,0.01385498 , 0.01168823 ,0.01165771 ,0.00906372 ,0.00683594 ,0.00772095 , 0.00555420 ,0.00942993 ,0.00997925 ,0.01031494 ,0.01126099 , 0.00946045 ,0.00909424 ,0.00854492 ,0.00820923 ,0.00942993 , 0.00997925 ,0.01074219 ,0.01040649 ,0.01031494 ,0.01037598 , 0.00949097 ,0.00942993 ,0.00912476 ,0.00982666 ,0.01174927 , 0.01205444 ,0.01129150 ,0.01031494 ,0.00646973 ,0.00808716 , 0.00820923 ,0.00903320 ,0.00860596 ,0.00860596 ,0.00637817 , 0.00601196 ,0.00634766 ,0.00473022 ,0.00805664 ,0.00827026 , 0.01113892 ,0.01220703 ,0.01336670 ,0.01391602 ,0.01293945 , 0.01394653 ,0.01290894 ,0.01354980 ,0.01068115 ,0.01174927 , 0.00637817 ,0.00814819 ,0.00427246 ,0.00415039 ,0.00509644 , 0.00424194 ,0.00592041 ,0.00778198 ,0.00680542 ,0.00991821 , 0.00906372 ,0.00988770 ,0.00906372 ,0.00903320 ,0.00466919 , 0.00769043 ,0.00292969 ,0.00634766 ,0.00338745 ,0.00418091 , 0.00421143 ,0.00424194 ,0.00592041 ,0.00378418 ,0.00686646 , 0.00372314 ,0.00778198 ,0.00595093 ,0.00637817 ,0.00778198 , 0.00497437 ,0.00521851 ,0.00192261 ,-0.00054932 ,-0.00024414 , -0.00363159 ,-0.00201416 ,-0.00143433 ,0.00152588 ,0.00338745 , 0.00634766 ,0.00427246 ,0.00369263 ,-0.00012207 ,-0.00018311 , 0.00021362 ,0.00161743 ,0.00155640 ,0.00070190 ,-0.00152588 , -0.00103760 ,-0.00192261 ,0.00195312 ,0.00256348 ,0.00579834 , 0.00173950 ,0.00234985 ,-0.00274658 ,-0.00067139 ,-0.00146484 , 0.00241089 ,0.00119019 ,0.00418091 ,-0.00192261 ,-0.00280762 , -0.00985718 ,-0.00762939 ,-0.00769043 ,-0.00363159 ,-0.00109863 , 0.00027466 ,-0.00106812 ,-0.00061035 ,-0.00592041 ,-0.00320435 , -0.00549316 ,-0.00015259 ,0.00024414 ,0.00112915 ,0.00070190 , -0.00326538 ,-0.00231934 ,-0.00509644 ,-0.00402832 ,-0.00427246 , -0.00265503 ,-0.00125122 ,-0.00085449 ,-0.00253296 ,-0.00183105 , -0.00244141 ,-0.00323486 ,-0.00106812 ,-0.00192261 ,0.00244141 , 0.00289917 ,0.00158691 ,-0.00018311 ,-0.00503540 ,-0.00717163 , -0.00769043 ,-0.00497437 ,-0.00018311 ,0.00506592 ,0.00817871 , 0.00860596 ,0.00772095 ,0.00418091 ,0.00161743 ,-0.00064087 , 0.00027466 ,0.00112915 ,0.00286865 ,0.00466919 ,0.00289917 , 0.00418091 ,0.00073242 ,0.00158691 ,-0.00112915 ,0.00036621 , -0.00384521 ,-0.00265503 ,-0.00643921 ,-0.00228882 ,-0.00424194 , -0.00140381 ,-0.00506592 ,-0.00366211 ,-0.00500488 ,-0.00372314 , 0.00112915 ,0.00161743 ,0.00854492 ,0.00512695 ,0.00729370 , 0.00415039 ,0.00207520 ,0.00286865 ,0.00332642 ,0.00381470 , 0.00408936 ,0.00302124 ,-0.00027466 ,0.00210571 ,0.00021362 , 0.00418091 ,0.00469971 ,0.00631714 ,0.00607300 ,0.00326538 , 0.00030518 ,-0.00024414 ,0.00033569 ,0.00411987 ,0.00473022 , 0.00503540 ,0.00512695 ,0.00372314 ,0.00210571 ,0.00189209 , -0.00183105 ,0.00503540 ,0.00201416 ,0.00909424 ,0.00500488 , 0.00997925 ,0.00769043 ,0.00909424 ,0.00897217 ,0.00735474 , 0.00677490 ,0.00646973 ,0.00460815 ,0.00463867 ,0.00427246 , 0.00326538 ,0.00381470 ,0.00286865 ,0.00463867 ,0.00598145 , 0.00817871 ,0.00900269 ,0.00820923 ,0.00634766 ,0.00469971 , 0.00503540 ,0.00381470 ,0.00595093 ,0.00552368 ,0.00860596 , 0.00772095 ,0.00903320 ,0.00726318 ,0.00866699 ,0.00588989 , 0.01000977 ,0.00720215 ,0.01171875 ,0.00946045 ,0.01477051 , 0.01208496 ,0.01565552 ,0.00857544 ,0.01040649 ,0.00546265 , 0.01040649 ,0.00903320 ,0.01470947 ,0.01174927 ,0.01467896 , 0.01129150 ,0.01251221 ,0.01040649 ,0.01116943 ,0.01263428 , 0.01467896 ,0.01480103 ,0.01516724 ,0.01342773 ,0.01257324 , 0.01165771 ,0.01257324 ,0.01254272 ,0.01605225 ,0.01608276 , 0.01565552 ,0.01690674 ,0.01565552 ,0.01736450 ,0.01739502 , 0.01870728 ,0.01693726 ,0.01739502 ,0.01696777 ,0.01602173 , 0.01568604 ,0.01336670 ,0.01394653 ,0.01248169 ,0.01480103 , 0.01647949 ,0.02001953 ,0.02136230 ,0.02261353 ,0.02008057 , 0.01815796 ,0.01708984 ,0.01370239 ,0.01754761 ,0.01333618 , 0.01959229 ,0.01611328 ,0.02169800 ,0.01925659 ,0.02297974 , 0.02008057 ,0.02175903 ,0.01782227 ,0.01785278 ,0.01824951 , 0.01733398 ,0.02053833 ,0.02081299 ,0.02053833 ,0.02084351 , 0.01611328 ,0.01470947 ,0.00994873 ,0.01034546 ,0.00991821 , 0.01388550 ,0.01605225 ,0.01916504 ,0.01821899 ,0.01657104 , 0.01684570 ,0.01705933 ,0.01861572 ,0.02008057 ,0.02127075 , 0.02227783 ,0.02261353 ,0.02182007 ,0.02087402 ,0.02133179 , 0.01959229 ,0.02136230 ,0.02130127 ,0.02224731 ,0.02172852 , 0.02053833 ,0.01858521 ,0.01708984 ,0.01550293 ,0.01837158 , 0.01596069 ,0.02011108 ,0.01907349 ,0.01919556 ,0.01913452 , 0.01779175 ,0.01525879 ,0.01815796 ,0.01443481 ,0.02035522 , 0.02142334 ,0.02130127 ,0.02352905 ,0.01867676 ,0.01968384 , 0.01724243 ,0.01620483 ,0.01773071 ,0.01654053 ,0.01913452 , 0.01696777 ,0.01907349 ,0.01879883 ,0.01687622 ,0.01785278 , 0.01477051 ,0.01776123 ,0.01660156 ,0.01815796 ,0.01614380 , 0.01736450 ,0.01473999 ,0.01608276 ,0.01431274 ,0.01605225 , 0.01519775 ,0.01605225 ,0.01696777 ,0.01693726 ,0.01828003 , 0.01605225 ,0.01736450 ,0.01437378 ,0.01684570 ,0.01531982 , 0.01678467 ,0.01712036 ,0.01504517 ,0.01528931 ,0.01336670 , 0.01696777 ,0.01608276 ,0.02130127 ,0.01916504 ,0.01959229 , 0.01913452 ,0.01473999 ,0.01522827 ,0.01333618 ,0.01135254 , 0.01336670 ,0.01257324 ,0.01565552 ,0.01776123 ,0.01788330 , 0.01690674 ,0.01477051 ,0.01300049 ,0.01034546 ,0.01168823 , 0.01034546 ,0.01257324 ,0.01296997 ,0.01300049 ,0.01342773 , 0.01257324 ,0.01342773 ,0.01428223 ,0.01391602 ,0.01556396 , 0.01263428 ,0.01467896 ,0.01126099 ,0.01126099 ,0.00946045 , 0.00949097 ,0.00991821 ,0.00946045 ,0.01083374 ,0.00814819 , 0.01077271 ,0.00778198 ,0.00897217 ,0.00781250 ,0.00720215 , 0.00643921 ,0.00772095 ,0.00640869 ,0.00860596 ,0.00595093 , 0.00949097 ,0.00772095 ,0.01168823 ,0.01077271 ,0.01257324 , 0.00991821 ,0.01165771 ,0.00643921 ,0.01077271 ,0.00640869 , 0.00863647 ,0.00634766 ,0.00646973 ,0.00460815 ,0.00643921 , 0.00595093 ,0.00860596 ,0.00903320 ,0.00860596 ,0.00683594 , 0.00598145 ,0.00335693 ,0.00457764 ,0.00430298 ,0.00674438 , 0.00781250 ,0.00985718 ,0.00906372 ,0.00817871 ,0.00552368 , 0.00375366 ,0.00204468 ,0.00152588 ,0.00338745 ,0.00283813 , 0.00732422 ,0.00460815 ,0.00689697 ,0.00286865 ,0.00552368 , 0.00421143 ,0.00683594 ,0.00817871 ,0.00637817 ,0.00909424 , 0.00326538 ,0.00558472 ,0.00198364 ,0.00552368 ,0.00466919 , 0.00772095 ,0.00509644 ,0.00814819 ,0.00598145 ,0.00637817 , 0.00643921 ,0.00509644 ,0.00680542 ,0.00558472 ,0.00762939 , 0.00784302 ,0.00851440 ,0.00817871 ,0.00732422 ,0.00723267 , 0.00686646 ,0.00729370 ,0.00637817 ,0.00778198 ,0.00677490 , 0.00732422 ,0.00372314 ,0.00427246 ,0.00198364 ,0.00244141 , 0.00381470 ,0.00546265 ,0.00735474 ,0.01116943 ,0.00863647 , 0.01083374 ,0.00808716 ,0.00869751 ,0.00543213 ,0.00558472 , 0.00378418 ,0.00503540 ,0.00561523 ,0.00759888 ,0.00653076 , 0.00805664 ,0.00695801 ,0.00631714 ,0.00469971 ,0.00549316 , 0.00335693 ,0.00595093 ,0.00378418 ,0.00552368 ,0.00332642 , 0.00552368 ,0.00378418 ,0.00418091 ,0.00601196 ,0.00241089 , 0.00552368 ,0.00115967 ,0.00244141 ,0.00247192 ,0.00198364 , 0.00424194 ,0.00372314 ,0.00555420 ,0.00601196 ,0.00631714 , 0.00738525 ,0.00677490 ,0.00686646 ,0.00552368 ,0.00555420 , 0.00637817 ,0.00643921 ,0.00857544 ,0.00860596 ,0.00863647 , 0.01028442 ,0.00915527 ,0.00845337 ,0.00875854 ,0.00628662 , 0.00823975 ,0.00680542 ,0.00814819 ,0.00863647 ,0.00726318 , 0.00863647 ,0.00680542 ,0.00686646 ,0.00814819 ,0.00640869 , 0.00863647 ,0.01031494 ,0.00820923 ,0.01119995 ,0.00686646 , 0.00772095 ,0.00421143 ,0.00683594 ,0.00509644 ,0.00994873 , 0.00985718 ,0.01394653 ,0.01071167 ,0.01132202 ,0.00939941 , 0.00912476 ,0.01028442 ,0.01260376 ,0.01165771 ,0.01473999 , 0.01171875 ,0.01119995 ,0.00991821 ,0.00820923 ,0.00854492 , 0.00863647 ,0.00903320 ,0.01031494 ,0.01092529 ,0.01065063 , 0.01309204 ,0.01028442 ,0.01260376 ,0.01119995 ,0.01220703 , 0.01330566 ,0.01351929 ,0.01385498 ,0.01205444 ,0.01049805 , 0.00933838 ,0.00738525 ,0.01074219 ,0.00732422 ,0.01205444 , 0.00912476 ,0.01205444 ,0.00952148 ,0.01165771 ,0.00991821 , 0.01123047 ,0.00994873 ,0.01119995 ,0.01126099 ,0.01168823 , 0.01205444 ,0.01043701 ,0.00985718 ,0.01214600 ,0.01077271 , 0.01568604 ,0.01376343 ,0.01577759 ,0.01370239 ,0.01446533 , 0.01202393 ,0.01083374 ,0.00991821 ,0.00903320 ,0.00946045 , 0.00775146 ,0.00772095 ,0.01077271 ,0.00906372 ,0.01385498 , 0.01080322 ,0.01254272 ,0.00949097 ,0.00814819 ,0.00466919 , 0.00509644 ,0.00198364 ,0.00466919 ,0.00332642 ,0.00466919 , 0.00286865 ,0.00424194 ,0.00592041 ,0.00689697 ,0.00811768 , 0.01083374 ,0.00942993 ,0.01086426 ,0.00985718 ,0.00643921 , 0.00903320 ,0.00595093 ,0.00775146 ,0.00769043 ,0.00643921 , 0.00683594 ,0.00506592 ,0.00338745 ,0.00149536 ,0.00210571 , 0.00061035 ,0.00558472 ,0.00509644 ,0.00946045 ,0.00640869 , 0.00598145 ,0.00286865 ,0.00292969 ,0.00244141 ,0.00421143 , 0.00637817 ,0.00601196 ,0.00723267 ,0.00338745 ,0.00418091 , 0.00070190 ,0.00463867 ,0.00509644 ,0.00772095 ,0.00772095 , 0.00732422 ,0.00592041 ,0.00512695 ,0.00106812 ,0.00033569 , -0.00152588 ,-0.00152588 ,0.00204468 ,0.00155640 ,0.00552368 , 0.00555420 ,0.00460815 ,0.00469971 ,0.00152588 ,0.00030518 , 0.00064087 ,-0.00146484 ,-0.00061035 ,-0.00149536 ,-0.00152588 , -0.00146484 ,-0.00112915 ,0.00036621 ,0.00146484 ,0.00341797 , 0.00372314 ,0.00155640 ,0.00167847 ,-0.00207520 ,-0.00140381 , -0.00375366 ,-0.00323486 ,-0.00280762 ,-0.00234985 ,-0.00286865 , -0.00277710 ,-0.00375366 ,-0.00405884 ,-0.00418091 ,-0.00234985 , -0.00372314 ,-0.00018311 ,-0.00234985 ,-0.00021362 ,0.00073242 , -0.00241089 ,-0.00061035 ,-0.00457764 ,-0.00323486 ,-0.00372314 , -0.00323486 ,-0.00323486 ,-0.00552368 ,-0.00619507 ,-0.00823975 , -0.00927734 ,-0.00903320 ,-0.01068115 ,-0.00811768 ,-0.00939941 , -0.00543213 ,-0.00723267 ,-0.00543213 ,-0.00765991 ,-0.00897217 , -0.01199341 ,-0.01296997 ,-0.01333618 ,-0.01293945 ,-0.01022339 , -0.01165771 ,-0.00933838 ,-0.01211548 ,-0.01110840 ,-0.01205444 , -0.01202393 ,-0.00942993 ,-0.01156616 ,-0.00946045 ,-0.01196289 , -0.01211548 ,-0.01196289 ,-0.01211548 ,-0.01150513 ,-0.01080322 , -0.01068115 ,-0.01208496 ,-0.01242065 ,-0.01556396 ,-0.01510620 , -0.01776123 ,-0.01602173 ,-0.01593018 ,-0.01473999 ,-0.01416016 , -0.01431274 ,-0.01327515 ,-0.01565552 ,-0.01498413 ,-0.01788330 , -0.01544189 ,-0.01739502 ,-0.01458740 ,-0.01300049 ,-0.01327515 , -0.01254272 ,-0.01464844 ,-0.01422119 ,-0.01559448 ,-0.01458740 , -0.01388550 ,-0.01593018 ,-0.01516724 ,-0.01901245 ,-0.01779175 , -0.01943970 ,-0.01916504 ,-0.01678467 ,-0.01779175 ,-0.01510620 , -0.01638794 ,-0.01824951 ,-0.01687622 ,-0.01898193 ,-0.01696777 , -0.01678467 ,-0.01864624 ,-0.01693726 ,-0.01898193 ,-0.01821899 , -0.01779175 ,-0.01852417 ,-0.01696777 ,-0.01770020 ,-0.01641846 , -0.01559448 ,-0.01687622 ,-0.01507568 ,-0.01821899 ,-0.01550293 , -0.01824951 ,-0.01596069 ,-0.01910400 ,-0.01635742 ,-0.01913452 , -0.01638794 ,-0.01910400 ,-0.01815796 ,-0.01904297 ,-0.01824951 , -0.01770020 ,-0.01690674 ,-0.01727295 ,-0.01733398 ,-0.01861572 , -0.01907349 ,-0.01861572 ,-0.01730347 ,-0.01690674 ,-0.01638794 , -0.01776123 ,-0.01641846 ,-0.01818848 ,-0.01647949 ,-0.01724243 , -0.01736450 ,-0.01593018 ,-0.01647949 ,-0.01730347 ,-0.01727295 , -0.02087402 ,-0.02120972 ,-0.02349854 ,-0.02474976 ,-0.02258301 , -0.02215576 ,-0.01812744 ,-0.01739502 ,-0.01504517 ,-0.01556396 , -0.01910400 ,-0.01855469 ,-0.02221680 ,-0.01947021 ,-0.02035522 , -0.01824951 ,-0.01809692 ,-0.01651001 ,-0.01947021 ,-0.01776123 , -0.02127075 ,-0.01770020 ,-0.01998901 ,-0.01812744 ,-0.01959229 , -0.01809692 ,-0.02001953 ,-0.01943970 ,-0.02084351 ,-0.02081299 , -0.01953125 ,-0.02124023 ,-0.02081299 ,-0.02261353 ,-0.02160645 , -0.02359009 ,-0.02029419 ,-0.02349854 ,-0.01904297 ,-0.02301025 , -0.01995850 ,-0.02349854 ,-0.02294922 ,-0.02175903 ,-0.02209473 , -0.01861572 ,-0.01913452 ,-0.01986694 ,-0.01956177 ,-0.02124023 , -0.02081299 ,-0.02212524 ,-0.02218628 ,-0.02294922 ,-0.02175903 , -0.02124023 ,-0.01992798 ,-0.01995850 ,-0.01818848 ,-0.02035522 , -0.01998901 ,-0.02252197 ,-0.02352905 ,-0.02294922 ,-0.02261353 , -0.02130127 ,-0.01763916 ,-0.01788330 ,-0.01455688 ,-0.01782227 , -0.01599121 ,-0.01858521 ,-0.01818848 ,-0.01864624 ,-0.01861572 , -0.01910400 ,-0.01855469 ,-0.01870728 ,-0.01983643 ,-0.01876831 , -0.02111816 ,-0.02050781 ,-0.02029419 ,-0.02130127 ,-0.01992798 , -0.02258301 ,-0.02172852 ,-0.02386475 ,-0.02084351 ,-0.02209473 , -0.01910400 ,-0.02212524 ,-0.02081299 ,-0.02438354 ,-0.02252197 , -0.02304077 ,-0.02124023 ,-0.01995850 ,-0.01995850 ,-0.01818848 , -0.02035522 ,-0.01995850 ,-0.02389526 ,-0.02301025 ,-0.02392578 , -0.01989746 ,-0.01867676 ,-0.01773071 ,-0.01815796 ,-0.01910400 , -0.02124023 ,-0.02212524 ,-0.02175903 ,-0.02337646 ,-0.02133179 , -0.02429199 ,-0.02124023 ,-0.02349854 ,-0.02124023 ,-0.02304077 , -0.02212524 ,-0.02209473 ,-0.02047729 ,-0.02160645 ,-0.02136230 , -0.02377319 ,-0.02532959 ,-0.02511597 ,-0.02706909 ,-0.02334595 , -0.02575684 ,-0.01983643 ,-0.02224731 ,-0.01986694 ,-0.02258301 , -0.02484131 ,-0.02511597 ,-0.02880859 ,-0.02648926 ,-0.02694702 , -0.02441406 ,-0.02337646 ,-0.02264404 ,-0.02297974 ,-0.02301025 , -0.02526855 ,-0.02557373 ,-0.02703857 ,-0.02606201 ,-0.02743530 , -0.02297974 ,-0.02566528 ,-0.02169800 ,-0.02478027 ,-0.02478027 , -0.02474976 ,-0.02786255 ,-0.02432251 ,-0.02658081 ,-0.02557373 , -0.02487183 ,-0.02731323 ,-0.02615356 ,-0.02828979 ,-0.02734375 , -0.02575684 ,-0.02600098 ,-0.02352905 ,-0.02737427 ,-0.02432251 , -0.02877808 ,-0.02603149 ,-0.02838135 ,-0.02642822 ,-0.02749634 , -0.02468872 ,-0.02484131 ,-0.02340698 ,-0.02264404 ,-0.02340698 , -0.02523804 ,-0.02691650 ,-0.02923584 ,-0.02911377 ,-0.03009033 , -0.02783203 ,-0.02780151 ,-0.02529907 ,-0.02600098 ,-0.02441406 , -0.02822876 ,-0.02700806 ,-0.02957153 ,-0.02700806 ,-0.02694702 , -0.02432251 ,-0.02438354 ,-0.02294922 ,-0.02575684 ,-0.02508545 , -0.02883911 ,-0.02685547 ,-0.02709961 ,-0.02423096 ,-0.02307129 , -0.02209473 ,-0.02218628 ,-0.02432251 ,-0.02609253 ,-0.02871704 , -0.02868652 ,-0.02926636 ,-0.02688599 ,-0.02615356 ,-0.02606201 , -0.02606201 ,-0.02746582 ,-0.02648926 ,-0.02612305 ,-0.02651978 , -0.02346802 ,-0.02563477 ,-0.02346802 ,-0.02517700 ,-0.02658081 , -0.02474976 ,-0.02651978 ,-0.02481079 ,-0.02426147 ,-0.02310181 , -0.02252197 ,-0.02435303 ,-0.02478027 ,-0.02694702 ,-0.02789307 , -0.02822876 ,-0.02877808 ,-0.02474976 ,-0.02478027 ,-0.02215576 , -0.02166748 ,-0.02346802 ,-0.02304077 ,-0.02563477 ,-0.02700806 , -0.02557373 ,-0.02618408 ,-0.02160645 ,-0.02224731 ,-0.02029419 , -0.02133179 ,-0.02249146 ,-0.02398682 ,-0.02203369 ,-0.02447510 , -0.02154541 ,-0.02316284 ,-0.02377319 ,-0.02224731 ,-0.02426147 , -0.02175903 ,-0.02294922 ,-0.02221680 ,-0.02120972 ,-0.02127075 , -0.01910400 ,-0.02117920 ,-0.02047729 ,-0.02294922 ,-0.02435303 , -0.02523804 ,-0.02340698 ,-0.02175903 ,-0.01812744 ,-0.01736450 , -0.01461792 ,-0.01779175 ,-0.01684570 ,-0.01907349 ,-0.02041626 , -0.01809692 ,-0.02047729 ,-0.01809692 ,-0.02136230 ,-0.01855469 , -0.02172852 ,-0.02035522 ,-0.01910400 ,-0.02078247 ,-0.01690674 , -0.01858521 ,-0.01821899 ,-0.01641846 ,-0.01776123 ,-0.01727295 , -0.01733398 ,-0.01818848 ,-0.01641846 ,-0.01602173 ,-0.01419067 , -0.01516724 ,-0.01239014 ,-0.01437378 ,-0.01144409 ,-0.01217651 , -0.01062012 ,-0.01254272 ,-0.01113892 ,-0.01290894 ,-0.01205444 , -0.01156616 ,-0.01165771 ,-0.01068115 ,-0.01159668 ,-0.01293945 , -0.01199341 ,-0.01428223 ,-0.00939941 ,-0.01333618 ,-0.00808716 , -0.01248169 ,-0.00851440 ,-0.01116943 ,-0.00942993 ,-0.00976562 , -0.00863647 ,-0.00885010 ,-0.00860596 ,-0.01025391 ,-0.01113892 , -0.01119995 ,-0.01156616 ,-0.00942993 ,-0.00897217 ,-0.00588989 , -0.00494385 ,-0.00643921 ,-0.00448608 ,-0.00772095 ,-0.00628662 , -0.00497437 ,-0.00732422 ,-0.00268555 ,-0.00689697 ,-0.00576782 , -0.00466919 ,-0.00759888 ,-0.00241089 ,-0.00540161 ,-0.00109863 , -0.00280762 ,-0.00061035 ,-0.00283813 ,-0.00192261 ,-0.00277710 , -0.00289917 ,-0.00231934 ,-0.00241089 ,-0.00231934 ,-0.00201416 , -0.00271606 ,-0.00338745 ,-0.00137329 ,-0.00289917 ,-0.00015259 , -0.00146484 ,-0.00286865 ,-0.00189209 ,-0.00244141 ,-0.00143433 , -0.00021362 ,-0.00064087 ,0.00164795 ,-0.00027466 ,0.00122070 , 0.00021362 ,-0.00152588 ,0.00119019 ,-0.00241089 ,-0.00015259 , -0.00195312 ,0.00024414 ,-0.00018311 ,0.00204468 ,0.00286865 , 0.00289917 ,0.00332642 ,0.00286865 ,0.00210571 ,0.00366211 , 0.00207520 ,0.00418091 ,0.00158691 ,0.00250244 ,0.00015259 , 0.00210571 ,0.00021362 ,0.00378418 ,0.00158691 ,0.00506592 , 0.00332642 ,0.00558472 ,0.00454712 ,0.00518799 ,0.00634766 , 0.00427246 ,0.00588989 ,0.00381470 ,0.00503540 ,0.00253296 , 0.00592041 ,0.00158691 ,0.00860596 ,0.00460815 ,0.00912476 , 0.00717163 ,0.00827026 ,0.00680542 ,0.00418091 ,0.00561523 , 0.00231934 ,0.00610352 ,0.00588989 ,0.00775146 ,0.00946045 , 0.01037598 ,0.00857544 ,0.00906372 ,0.00772095 ,0.00595093 , 0.00683594 ,0.00643921 ,0.00680542 ,0.00775146 ,0.00814819 , 0.00860596 ,0.00863647 ,0.00811768 ,0.00775146 ,0.00723267 , 0.00781250 ,0.00634766 ,0.00686646 ,0.00775146 ,0.00720215 , 0.00646973 ,0.00595093 ,0.00463867 ,0.00643921 ,0.00772095 , 0.00942993 ,0.00997925 ,0.01119995 ,0.00729370 ,0.00906372 , 0.00418091 ,0.00686646 ,0.00598145 ,0.00720215 ,0.00830078 , 0.00799561 ,0.00878906 ,0.00885010 ,0.00833130 ,0.00756836 , 0.00787354 ,0.00805664 ,0.00909424 ,0.00946045 ,0.01080322 , 0.00860596 ,0.01034546 ,0.00686646 ,0.00680542 ,0.00558472 , 0.00592041 ,0.00729370 ,0.00817871 ,0.00988770 ,0.01126099 , 0.01080322 ,0.00991821 ,0.00723267 ,0.00607300 ,0.00405884 , 0.00350952 ,0.00537109 ,0.00695801 ,0.00939941 ,0.01129150 , 0.00988770 ,0.01037598 ,0.00814819 ,0.00555420 ,0.00506592 , 0.00466919 ,0.00506592 ,0.00686646 ,0.00814819 ,0.00906372 , 0.01080322 ,0.00942993 ,0.00955200 ,0.00765991 ,0.00732422 , 0.00686646 ,0.00762939 ,0.00695801 ,0.00894165 ,0.00823975 , 0.00985718 ,0.00778198 ,0.00985718 ,0.00952148 ,0.00946045 , 0.01300049 ,0.00949097 ,0.01254272 ,0.00991821 ,0.00817871 , 0.00814819 ,0.00732422 ,0.00723267 ,0.00732422 ,0.00903320 , 0.00640869 ,0.01080322 ,0.00769043 ,0.00994873 ,0.00860596 , 0.00903320 ,0.00863647 ,0.00939941 ,0.00955200 ,0.01031494 , 0.01126099 ,0.01080322 ,0.01119995 ,0.00952148 ,0.00946045 , 0.00860596 ,0.00860596 ,0.00946045 ,0.00903320 ,0.00863647 , 0.00903320 ,0.00729370 ,0.00769043 ,0.00598145 ,0.00817871 , 0.00683594 ,0.00994873 ,0.00939941 ,0.01089478 ,0.01116943 , 0.00909424 ,0.01034546 ,0.00683594 ,0.00857544 ,0.00820923 , 0.00942993 ,0.01132202 ,0.01159668 ,0.01388550 ,0.01257324 , 0.01559448 ,0.01217651 ,0.01422119 ,0.01086426 ,0.01165771 , 0.00946045 ,0.00909424 ,0.00854492 ,0.00906372 ,0.00949097 , 0.01123047 ,0.01208496 ,0.01394653 ,0.01333618 ,0.01351929 , 0.01205444 ,0.01171875 ,0.01165771 ,0.01123047 ,0.01214600 , 0.01162720 ,0.01306152 ,0.00985718 ,0.01040649 ,0.01077271 , 0.01037598 ,0.01342773 ,0.01080322 ,0.01470947 ,0.01217651 , 0.01336670 ,0.01263428 ,0.01159668 ,0.01217651 ,0.01205444 , 0.01171875 ,0.01296997 ,0.01080322 ,0.01254272 ,0.01168823 , 0.01296997 ,0.01394653 ,0.01376343 ,0.01525879 ,0.01473999 , 0.01470947 ,0.01483154 ,0.01556396 ,0.01565552 ,0.01870728 , 0.01690674 ,0.01919556 ,0.01470947 ,0.01699829 ,0.01162720 , 0.01388550 ,0.01034546 ,0.01303101 ,0.01296997 ,0.01519775 , 0.01562500 ,0.01693726 ,0.01605225 ,0.01742554 ,0.01559448 , 0.01828003 ,0.01608276 ,0.01864624 ,0.01742554 ,0.01693726 , 0.01739502 ,0.01736450 ,0.01873779 ,0.01995850 ,0.01919556 , 0.02087402 ,0.02001953 ,0.02182007 ,0.02124023 ,0.02014160 , 0.01901245 ,0.01794434 ,0.01864624 ,0.01824951 ,0.02050781 , 0.02041626 ,0.02136230 ,0.02001953 ,0.01913452 ,0.01824951 , 0.01788330 ,0.01861572 ,0.02008057 ,0.02175903 ,0.02307129 , 0.02316284 ,0.02215576 ,0.02178955 ,0.01962280 ,0.01992798 , 0.02056885 ,0.02124023 ,0.02362061 ,0.02478027 ,0.02359009 , 0.02612305 ,0.02050781 ,0.02389526 ,0.02011108 ,0.02343750 , 0.02322388 ,0.02471924 ,0.02453613 ,0.02557373 ,0.02239990 , 0.02426147 ,0.02053833 ,0.02175903 ,0.02218628 ,0.02185059 , 0.02389526 ,0.02313232 ,0.02441406 ,0.02438354 ,0.02359009 , 0.02435303 ,0.02313232 ,0.02529907 ,0.02523804 ,0.02664185 , 0.02572632 ,0.02618408 ,0.02349854 ,0.02313232 ,0.02346802 , 0.02227783 ,0.02395630 ,0.02438354 ,0.02398682 ,0.02398682 , 0.02484131 ,0.02221680 ,0.02529907 ,0.02127075 ,0.02407837 , 0.02212524 ,0.02227783 ,0.02395630 ,0.02349854 ,0.02624512 , 0.02517700 ,0.02627563 ,0.02435303 ,0.02664185 ,0.02398682 , 0.02697754 ,0.02493286 ,0.02566528 ,0.02667236 ,0.02523804 , 0.02621460 ,0.02569580 ,0.02444458 ,0.02349854 ,0.02444458 , 0.02218628 ,0.02621460 ,0.02395630 ,0.02703857 ,0.02441406 , 0.02484131 ,0.02441406 ,0.02313232 ,0.02523804 ,0.02536011 , 0.02606201 ,0.02581787 ,0.02566528 ,0.02270508 ,0.02484131 , 0.02438354 ,0.02355957 ,0.02658081 ,0.02487183 ,0.02838135 , 0.02874756 ,0.02752686 ,0.02923584 ,0.02526855 ,0.02664185 , 0.02346802 ,0.02404785 ,0.02392578 ,0.02532959 ,0.02697754 , 0.02755737 ,0.02828979 ,0.02673340 ,0.02560425 ,0.02536011 , 0.02392578 ,0.02578735 ,0.02569580 ,0.02706909 ,0.02655029 , 0.02801514 ,0.02386475 ,0.02542114 ,0.02209473 ,0.02447510 , 0.02438354 ,0.02575684 ,0.02658081 ,0.02706909 ,0.02658081 , 0.02618408 ,0.02441406 ,0.02352905 ,0.02398682 ,0.02261353 , 0.02447510 ,0.02301025 ,0.02450562 ,0.02346802 ,0.02401733 , 0.02307129 ,0.02310181 ,0.02349854 ,0.02316284 ,0.02478027 , 0.02447510 ,0.02569580 ,0.02529907 ,0.02484131 ,0.02444458 , 0.02523804 ,0.02270508 ,0.02481079 ,0.02093506 ,0.02352905 , 0.02172852 ,0.02270508 ,0.02261353 ,0.02227783 ,0.02258301 , 0.02359009 ,0.02215576 ,0.02450562 ,0.02124023 ,0.02316284 , 0.01998901 ,0.02005005 ,0.01910400 ,0.01788330 ,0.01907349 , 0.02005005 ,0.02005005 ,0.02258301 ,0.02096558 ,0.02130127 , 0.02044678 ,0.01785278 ,0.01651001 ,0.01602173 ,0.01568604 , 0.01602173 ,0.01654053 ,0.01736450 ,0.01608276 ,0.01821899 , 0.01525879 ,0.01733398 ,0.01565552 ,0.01647949 ,0.01608276 , 0.01519775 ,0.01473999 ,0.01522827 ,0.01470947 ,0.01608276 , 0.01519775 ,0.01516724 ,0.01608276 ,0.01477051 ,0.01647949 , 0.01651001 ,0.01434326 ,0.01733398 ,0.01257324 ,0.01562500 , 0.01214600 ,0.01425171 ,0.01089478 ,0.01287842 ,0.01086426 , 0.01168823 ,0.01296997 ,0.01211548 ,0.01431274 ,0.01300049 , 0.01339722 ,0.01223755 ,0.01416016 ,0.01177979 ,0.01602173 , 0.01080322 ,0.01345825 ,0.01034546 ,0.00988770 ,0.00912476 , 0.00939941 ,0.00909424 ,0.01031494 ,0.01126099 ,0.00991821 , 0.01077271 ,0.01040649 ,0.00985718 ,0.01083374 ,0.01080322 , 0.00988770 ,0.01040649 ,0.00811768 ,0.00817871 ,0.00683594 , 0.00466919 ,0.00772095 ,0.00421143 ,0.00857544 ,0.00820923 , 0.00897217 ,0.01000977 ,0.00939941 ,0.00863647 ,0.00860596 , 0.00683594 ,0.00726318 ,0.00646973 ,0.00851440 ,0.00735474 , 0.00900269 ,0.00598145 ,0.00817871 ,0.00418091 ,0.00686646 , 0.00421143 ,0.00683594 ,0.00555420 ,0.00723267 ,0.00646973 , 0.00677490 ,0.00650024 ,0.00674438 ,0.00735474 ,0.00808716 , 0.00784302 ,0.00671387 ,0.00476074 ,0.00369263 ,0.00119019 , 0.00372314 ,0.00122070 ,0.00585938 ,0.00518799 ,0.00546265 , 0.00643921 ,0.00332642 ,0.00375366 ,0.00073242 ,0.00241089 , 0.00030518 ,0.00283813 ,0.00204468 ,0.00244141 ,0.00201416 , 0.00289917 ,0.00158691 ,0.00415039 ,0.00384521 ,0.00195312 , 0.00381470 ,-0.00109863 ,0.00073242 ,0.00064087 ,-0.00009155 , 0.00238037 ,0.00247192 ,0.00158691 ,0.00286865 ,-0.00192261 , 0.00027466 ,-0.00372314 ,0.00073242 ,-0.00109863 ,0.00204468 , 0.00155640 ,0.00158691 ,0.00070190 ,-0.00106812 ,-0.00064087 , -0.00186157 ,-0.00119019 ,-0.00134277 ,-0.00253296 ,-0.00009155 , -0.00152588 ,0.00070190 ,0.00070190 ,-0.00061035 ,0.00064087 , -0.00143433 ,0.00021362 ,-0.00189209 ,-0.00024414 ,-0.00274658 , -0.00024414 ,-0.00234985 ,-0.00103760 ,-0.00198364 ,-0.00192261 , -0.00277710 ,-0.00292969 ,-0.00439453 ,-0.00302124 ,-0.00396729 , -0.00381470 ,-0.00228882 ,-0.00375366 ,-0.00057983 ,-0.00286865 , 0.00033569 ,-0.00247192 ,-0.00097656 ,-0.00286865 ,-0.00320435 , -0.00241089 ,-0.00280762 ,-0.00064087 ,-0.00146484 ,-0.00018311 , -0.00106812 ,-0.00238037 ,-0.00106812 ,-0.00411987 ,-0.00323486 , -0.00418091 ,-0.00454712 ,-0.00323486 ,-0.00286865 ,-0.00274658 , -0.00067139 ,-0.00415039 ,-0.00097656 ,-0.00552368 ,-0.00146484 , -0.00323486 ,-0.00027466 ,-0.00268555 ,-0.00030518 ,-0.00271606 , -0.00244141 ,-0.00540161 ,-0.00418091 ,-0.00497437 ,-0.00241089 , -0.00366211 ,-0.00064087 ,-0.00280762 ,-0.00106812 ,-0.00280762 , -0.00323486 ,-0.00372314 ,-0.00192261 ,-0.00372314 ,-0.00366211 , -0.00415039 ,-0.00585938 ,-0.00549316 ,-0.00585938 ,-0.00503540 , -0.00408936 ,-0.00201416 ,-0.00317383 ,-0.00289917 ,-0.00448608 , -0.00506592 ,-0.00674438 ,-0.00592041 ,-0.00585938 ,-0.00418091 , -0.00186157 ,-0.00466919 ,-0.00271606 ,-0.00814819 ,-0.00543213 , -0.00897217 ,-0.00546265 ,-0.00631714 ,-0.00366211 ,-0.00332642 , -0.00448608 ,-0.00335693 ,-0.00491333 ,-0.00329590 ,-0.00503540 , -0.00540161 ,-0.00552368 ,-0.00668335 ,-0.00592041 ,-0.00811768 , -0.00671387 ,-0.00814819 ,-0.00759888 ,-0.00503540 ,-0.00762939 , -0.00283813 ,-0.00500488 ,-0.00411987 ,-0.00503540 ,-0.00805664 , -0.00765991 ,-0.00942993 ,-0.00894165 ,-0.00765991 ,-0.00720215 , -0.00628662 ,-0.00595093 ,-0.00759888 ,-0.00640869 ,-0.00888062 , -0.00811768 ,-0.00897217 ,-0.00717163 ,-0.00683594 ,-0.00582886 , -0.00592041 ,-0.00765991 ,-0.00674438 ,-0.01028442 ,-0.00808716 , -0.01159668 ,-0.00808716 ,-0.00942993 ,-0.00845337 ,-0.00772095 , -0.00979614 ,-0.00808716 ,-0.01031494 ,-0.00936890 ,-0.00851440 , -0.00857544 ,-0.00756836 ,-0.00903320 ,-0.01025391 ,-0.00982666 , -0.01116943 ,-0.01028442 ,-0.01113892 ,-0.00946045 ,-0.01065063 , -0.00814819 ,-0.00976562 ,-0.00906372 ,-0.00930786 ,-0.01168823 , -0.01065063 ,-0.01342773 ,-0.01153564 ,-0.01208496 ,-0.01287842 , -0.01159668 ,-0.01428223 ,-0.01333618 ,-0.01379395 ,-0.01467896 , -0.01242065 ,-0.01562500 ,-0.01287842 ,-0.01513672 ,-0.01293945 , -0.01504517 ,-0.01428223 ,-0.01422119 ,-0.01467896 ,-0.01245117 , -0.01516724 ,-0.01284790 ,-0.01516724 ,-0.01376343 ,-0.01513672 , -0.01419067 ,-0.01559448 ,-0.01553345 ,-0.01513672 ,-0.01684570 , -0.01559448 ,-0.01678467 ,-0.01651001 ,-0.01596069 ,-0.01733398 , -0.01684570 ,-0.01644897 ,-0.01684570 ,-0.01599121 ,-0.01605225 , -0.01721191 ,-0.01654053 ,-0.01940918 ,-0.01913452 ,-0.01901245 , -0.01910400 ,-0.01727295 ,-0.01867676 ,-0.01724243 ,-0.01910400 , -0.01730347 ,-0.01904297 ,-0.01605225 ,-0.01898193 ,-0.01519775 , -0.01986694 ,-0.01605225 ,-0.02166748 ,-0.01864624 ,-0.02212524 , -0.01953125 ,-0.02035522 ,-0.01907349 ,-0.01950073 ,-0.01995850 , -0.01950073 ,-0.01992798 ,-0.01995850 ,-0.01950073 ,-0.02169800 , -0.02041626 ,-0.02206421 ,-0.02136230 ,-0.02029419 ,-0.02218628 , -0.01861572 ,-0.02255249 ,-0.02133179 ,-0.02337646 ,-0.02307129 , -0.02386475 ,-0.02127075 ,-0.02215576 ,-0.01861572 ,-0.02081299 , -0.01776123 ,-0.02081299 ,-0.02038574 ,-0.02169800 ,-0.02258301 , -0.02301025 ,-0.02169800 ,-0.02304077 ,-0.02166748 ,-0.02261353 , -0.02255249 ,-0.02127075 ,-0.01995850 ,-0.02078247 ,-0.01779175 , -0.01950073 ,-0.01815796 ,-0.01910400 ,-0.01989746 ,-0.01995850 , -0.02130127 ,-0.02166748 ,-0.02212524 ,-0.02172852 ,-0.02252197 , -0.02090454 ,-0.02163696 ,-0.02087402 ,-0.02078247 ,-0.02038574 , -0.02127075 ,-0.02127075 ,-0.02166748 ,-0.02218628 ,-0.02212524 , -0.02169800 ,-0.02304077 ,-0.02166748 ,-0.02258301 ,-0.02218628 , -0.02120972 ,-0.02172852 ,-0.01995850 ,-0.02120972 ,-0.02001953 , -0.01986694 ,-0.02044678 ,-0.01858521 ,-0.02084351 ,-0.01861572 , -0.02038574 ,-0.01953125 ,-0.02032471 ,-0.02044678 ,-0.02032471 , -0.02218628 ,-0.01947021 ,-0.02261353 ,-0.01989746 ,-0.02172852 , -0.02078247 ,-0.02136230 ,-0.01980591 ,-0.02142334 ,-0.01843262 , -0.02099609 ,-0.01852417 ,-0.01998901 ,-0.02169800 ,-0.01992798 , -0.02258301 ,-0.02038574 ,-0.02081299 ,-0.01998901 ,-0.01770020 , -0.01910400 ,-0.01681519 ,-0.02044678 ,-0.01858521 ,-0.02041626 , -0.01947021 ,-0.01998901 ,-0.01943970 ,-0.01870728 ,-0.01986694 , -0.01824951 ,-0.02124023 ,-0.01861572 ,-0.02084351 ,-0.01950073 , -0.01992798 ,-0.01821899 ,-0.01815796 ,-0.01730347 ,-0.01736450 , -0.01809692 ,-0.01782227 ,-0.01724243 ,-0.01693726 ,-0.01593018 , -0.01690674 ,-0.01507568 ,-0.01733398 ,-0.01556396 ,-0.01773071 , -0.01687622 ,-0.01684570 ,-0.01602173 ,-0.01684570 ,-0.01513672 , -0.01776123 ,-0.01504517 ,-0.01739502 ,-0.01501465 ,-0.01654053 , -0.01412964 ,-0.01608276 ,-0.01370239 ,-0.01562500 ,-0.01507568 , -0.01556396 ,-0.01467896 ,-0.01510620 ,-0.01467896 ,-0.01464844 , -0.01470947 ,-0.01510620 ,-0.01422119 ,-0.01559448 ,-0.01150513 , -0.01434326 ,-0.00933838 ,-0.01251221 ,-0.01028442 ,-0.01107788 , -0.01260376 ,-0.01062012 ,-0.01257324 ,-0.01019287 ,-0.01165771 , -0.01025391 ,-0.01159668 ,-0.00988770 ,-0.01199341 ,-0.00942993 , -0.01251221 ,-0.01062012 ,-0.01303101 ,-0.01235962 ,-0.01434326 , -0.01284790 ,-0.01385498 ,-0.01153564 ,-0.01205444 ,-0.00985718 , -0.01071167 ,-0.00985718 ,-0.00982666 ,-0.01071167 ,-0.00854492 , -0.01202393 ,-0.00988770 ,-0.01196289 ,-0.01080322 ,-0.00973511 , -0.01129150 ,-0.00930786 ,-0.01123047 ,-0.00891113 ,-0.01031494 , -0.00762939 ,-0.01028442 ,-0.00723267 ,-0.01068115 ,-0.00991821 , -0.01019287 ,-0.01208496 ,-0.00985718 ,-0.01065063 ,-0.00952148 , -0.00927734 ,-0.00906372 ,-0.00891113 ,-0.00939941 ,-0.00946045 , -0.00888062 ,-0.00949097 ,-0.00888062 ,-0.00946045 ,-0.01028442 , -0.00979614 ,-0.01080322 ,-0.01058960 ,-0.00997925 ,-0.01019287 , -0.00946045 ,-0.00979614 ,-0.00854492 ,-0.00939941 ,-0.00854492 , -0.00933838 ,-0.00909424 ,-0.00967407 ,-0.00823975 ,-0.01062012 , -0.00857544 ,-0.01028442 ,-0.01071167 ,-0.00979614 ,-0.01168823 , -0.01239014 ,-0.01165771 ,-0.01333618 ,-0.00985718 ,-0.01156616 , -0.00769043 ,-0.00933838 ,-0.00772095 ,-0.00848389 ,-0.01074219 , -0.00805664 ,-0.01208496 ,-0.00845337 ,-0.01168823 ,-0.00799561 , -0.00988770 ,-0.00811768 ,-0.00973511 ,-0.00909424 ,-0.00888062 , -0.00811768 ,-0.00854492 ,-0.00717163 ,-0.00897217 ,-0.00592041 , -0.00982666 ,-0.00677490 ,-0.01119995 ,-0.00842285 ,-0.01083374 , -0.00888062 ,-0.01031494 ,-0.00808716 ,-0.00982666 ,-0.00939941 , -0.00942993 ,-0.00979614 ,-0.00985718 ,-0.00988770 ,-0.01065063 , -0.01034546 ,-0.00979614 ,-0.00897217 ,-0.00946045 ,-0.00973511 , -0.01040649 ,-0.01104736 ,-0.00949097 ,-0.01025391 ,-0.00894165 , -0.00854492 ,-0.00805664 ,-0.00857544 ,-0.00848389 ,-0.00946045 , -0.00845337 ,-0.00946045 ,-0.00759888 ,-0.00726318 ,-0.00759888 , -0.00595093 ,-0.01019287 ,-0.00732422 ,-0.01058960 ,-0.00866699 , -0.01016235 ,-0.00988770 ,-0.00942993 ,-0.00933838 ,-0.00906372 , -0.00885010 ,-0.00949097 ,-0.00848389 ,-0.00762939 ,-0.00946045 , -0.00759888 ,-0.00988770 ,-0.01025391 ,-0.01071167 ,-0.01071167 , -0.01123047 ,-0.00845337 ,-0.01165771 ,-0.00802612 ,-0.01296997 , -0.00802612 ,-0.01211548 ,-0.00845337 ,-0.01077271 ,-0.01068115 , -0.00942993 ,-0.01113892 ,-0.00854492 ,-0.01071167 ,-0.00851440 , -0.00985718 ,-0.00939941 ,-0.01074219 ,-0.01110840 ,-0.01165771 , -0.01065063 ,-0.01080322 ,-0.00891113 ,-0.01031494 ,-0.00894165 , -0.01116943 ,-0.01028442 ,-0.01202393 ,-0.00942993 ,-0.01113892 , -0.01074219 ,-0.01071167 ,-0.01113892 ,-0.00942993 ,-0.00979614 , -0.00860596 ,-0.01019287 ,-0.00860596 ,-0.01065063 ,-0.00811768 , -0.00982666 ,-0.00765991 ,-0.00897217 ,-0.00808716 ,-0.00936890 , -0.00903320 ,-0.00842285 ,-0.00952148 ,-0.00665283 ,-0.00863647 , -0.00756836 ,-0.00814819 ,-0.00976562 ,-0.00991821 ,-0.01022339 , -0.01119995 ,-0.00894165 ,-0.00942993 ,-0.00762939 ,-0.00851440 , -0.00677490 ,-0.00851440 ,-0.00723267 ,-0.00939941 ,-0.00805664 , -0.00900269 ,-0.00933838 ,-0.00994873 ,-0.00970459 ,-0.00955200 , -0.00970459 ,-0.00906372 ,-0.00976562 ,-0.00772095 ,-0.00756836 , -0.00683594 ,-0.00674438 ,-0.00674438 ,-0.00723267 ,-0.00720215 , -0.00808716 ,-0.00723267 ,-0.00805664 ,-0.00631714 ,-0.00680542 , -0.00631714 ,-0.00674438 ,-0.00637817 ,-0.00714111 ,-0.00683594 , -0.00717163 ,-0.00677490 ,-0.00588989 ,-0.00503540 ,-0.00540161 , -0.00463867 ,-0.00582886 ,-0.00637817 ,-0.00631714 ,-0.00671387 , -0.00598145 ,-0.00405884 ,-0.00460815 ,-0.00195312 ,-0.00494385 , -0.00421143 ,-0.00582886 ,-0.00506592 ,-0.00411987 ,-0.00411987 , -0.00326538 ,-0.00323486 ,-0.00286865 ,-0.00320435 ,-0.00286865 , -0.00363159 ,-0.00463867 ,-0.00317383 ,-0.00469971 ,-0.00265503 , -0.00384521 ,-0.00268555 ,-0.00335693 ,-0.00231934 ,-0.00241089 , -0.00366211 ,-0.00195312 ,-0.00503540 ,-0.00320435 ,-0.00506592 , -0.00454712 ,-0.00457764 ,-0.00460815 ,-0.00497437 ,-0.00323486 , -0.00332642 ,-0.00274658 ,-0.00070190 ,-0.00405884 ,-0.00021362 , -0.00500488 ,-0.00061035 ,-0.00329590 ,0.00073242 ,-0.00061035 , 0.00198364 ,0.00073242 ,0.00109863 ,-0.00018311 ,0.00076294 , -0.00067139 ,-0.00018311 ,-0.00015259 ,0.00018311 ,0.00079346 , 0.00198364 ,0.00067139 ,0.00289917 ,0.00158691 ,0.00244141 , 0.00247192 ,0.00112915 ,0.00155640 ,0.00158691 ,0.00112915 , 0.00244141 ,0.00335693 ,0.00244141 ,0.00378418 ,0.00283813 , 0.00207520 ,0.00198364 ,0.00204468 ,0.00198364 ,0.00378418 , 0.00332642 ,0.00552368 ,0.00335693 ,0.00726318 ,0.00244141 , 0.00686646 ,0.00241089 ,0.00515747 ,0.00503540 ,0.00466919 , 0.00509644 ,0.00460815 ,0.00469971 ,0.00595093 ,0.00595093 , 0.00555420 ,0.00769043 ,0.00598145 ,0.00772095 ,0.00726318 , 0.00689697 ,0.00723267 ,0.00686646 ,0.00683594 ,0.00772095 , 0.00598145 ,0.00814819 ,0.00466919 ,0.00814819 ,0.00640869 , 0.00683594 ,0.00817871 ,0.00592041 ,0.00958252 ,0.00848389 , 0.00958252 ,0.01068115 ,0.00784302 ,0.01113892 ,0.00607300 , 0.00982666 ,0.00686646 ,0.00860596 ,0.00860596 ,0.00643921 , 0.00897217 ,0.00781250 ,0.00891113 ,0.00915527 ,0.00942993 , 0.00903320 ,0.00952148 ,0.00857544 ,0.00726318 ,0.00863647 , 0.00726318 ,0.00729370 ,0.00949097 ,0.00640869 ,0.00903320 , 0.00640869 ,0.00814819 ,0.00775146 ,0.00903320 ,0.01034546 , 0.01080322 ,0.01037598 ,0.01119995 ,0.01043701 ,0.00933838 , 0.01007080 ,0.00805664 ,0.00997925 ,0.00857544 ,0.01037598 , 0.00857544 ,0.01040649 ,0.00854492 ,0.00952148 ,0.00903320 , 0.00857544 ,0.00820923 ,0.00900269 ,0.00689697 ,0.01071167 , 0.00735474 ,0.01296997 ,0.00860596 ,0.01257324 ,0.00857544 , 0.01123047 ,0.00863647 ,0.00988770 ,0.00775146 ,0.00988770 , 0.00778198 ,0.01071167 ,0.00827026 ,0.01071167 ,0.00994873 , 0.01171875 ,0.01071167 ,0.01132202 ,0.00942993 ,0.01126099 , 0.00991821 ,0.00946045 ,0.00994873 ,0.00857544 ,0.00949097 , 0.00772095 ,0.00903320 ,0.00817871 ,0.00946045 ,0.01037598 , 0.01119995 ,0.01171875 ,0.01119995 ,0.01080322 ,0.00949097 , 0.01037598 ,0.00985718 ,0.01043701 ,0.00985718 ,0.00997925 , 0.00942993 ,0.00994873 ,0.00900269 ,0.00997925 ,0.00897217 , 0.01177979 ,0.00979614 ,0.01223755 ,0.00979614 ,0.01177979 , 0.01028442 ,0.01220703 ,0.01156616 ,0.01220703 ,0.01159668 , 0.01000977 ,0.01074219 ,0.00860596 ,0.00949097 ,0.00900269 , 0.01043701 ,0.01119995 ,0.01123047 ,0.01168823 ,0.00988770 , 0.01217651 ,0.00854492 ,0.01260376 ,0.00857544 ,0.01119995 , 0.00912476 ,0.00982666 ,0.00915527 ,0.00936890 ,0.00866699 , 0.00942993 ,0.00823975 ,0.00982666 ,0.00915527 ,0.01025391 , 0.01040649 ,0.01126099 ,0.01071167 ,0.01046753 ,0.01028442 , 0.00906372 ,0.00863647 ,0.00897217 ,0.00735474 ,0.01074219 , 0.00820923 ,0.01077271 ,0.00863647 ,0.00985718 ,0.00820923 , 0.00903320 ,0.00814819 ,0.00863647 ,0.00900269 ,0.00906372 , 0.00814819 ,0.00952148 ,0.00765991 ,0.00866699 ,0.00723267 , 0.00778198 ,0.00634766 ,0.00909424 ,0.00634766 ,0.01000977 , 0.00674438 ,0.00955200 ,0.00677490 ,0.00955200 ,0.00723267 , 0.00909424 ,0.00723267 ,0.00772095 ,0.00778198 ,0.00674438 , 0.00784302 ,0.00631714 ,0.00866699 ,0.00680542 ,0.00814819 , 0.00689697 ,0.00811768 ,0.00643921 ,0.00900269 ,0.00555420 , 0.00814819 ,0.00643921 ,0.00634766 ,0.00732422 ,0.00595093 , 0.00726318 ,0.00692749 ,0.00671387 ,0.00784302 ,0.00588989 , 0.00732422 ,0.00552368 ,0.00640869 ,0.00769043 ,0.00692749 , 0.00894165 ,0.00827026 ,0.00805664 ,0.00866699 ,0.00595093 , 0.00772095 ,0.00643921 ,0.00723267 ,0.00775146 ,0.00680542 , 0.00689697 ,0.00592041 ,0.00512695 ,0.00726318 ,0.00552368 , 0.00817871 ,0.00772095 ,0.00772095 ,0.00860596 ,0.00595093 , 0.00909424 ,0.00500488 ,0.00872803 ,0.00537109 ,0.00830078 , 0.00808716 ,0.00732422 ,0.00946045 ,0.00595093 ,0.00952148 , 0.00637817 ,0.00863647 ,0.00769043 ,0.00817871 ,0.00772095 , 0.00772095 ,0.00775146 ,0.00769043 ,0.00817871 ,0.00814819 , 0.00729370 ,0.00906372 ,0.00677490 ,0.00869751 ,0.00805664 , 0.00738525 ,0.00894165 ,0.00604248 ,0.00811768 ,0.00732422 , 0.00857544 ,0.00949097 ,0.00946045 ,0.00952148 ,0.00942993 , 0.00866699 ,0.00765991 ,0.00820923 ,0.00637817 ,0.00909424 , 0.00585938 ,0.00872803 ,0.00628662 ,0.00738525 ,0.00680542 , 0.00769043 ,0.00820923 ,0.00857544 ,0.00903320 ,0.00775146 , 0.00857544 ,0.00643921 ,0.00723267 ,0.00689697 ,0.00634766 , 0.00732422 ,0.00817871 ,0.00726318 ,0.00991821 ,0.00686646 , 0.00854492 ,0.00778198 ,0.00683594 ,0.00814819 ,0.00598145 , 0.00900269 ,0.00689697 ,0.00988770 ,0.00775146 ,0.00857544 , 0.00906372 ,0.00680542 ,0.00778198 ,0.00723267 ,0.00643921 , 0.00949097 ,0.00634766 ,0.00955200 ,0.00634766 ,0.00820923 , 0.00811768 ,0.00775146 ,0.00903320 ,0.00903320 ,0.00863647 , 0.00900269 ,0.00860596 ,0.00817871 ,0.00772095 ,0.00769043 , 0.00643921 ,0.00814819 ,0.00726318 ,0.00866699 ,0.00985718 , 0.00903320 ,0.01129150 ,0.00897217 ,0.00997925 ,0.00857544 , 0.00860596 ,0.00903320 ,0.00906372 ,0.00900269 ,0.00909424 , 0.00854492 ,0.00955200 ,0.00805664 ,0.00918579 ,0.00845337 , 0.00915527 ,0.00939941 ,0.00866699 ,0.00942993 ,0.00820923 , 0.01028442 ,0.00781250 ,0.01116943 ,0.00866699 ,0.01028442 , 0.00909424 ,0.01031494 ,0.00994873 ,0.00949097 ,0.01077271 , 0.00860596 ,0.01168823 ,0.00946045 ,0.01083374 ,0.01074219 , 0.00909424 ,0.00985718 ,0.00958252 ,0.00936890 ,0.01132202 , 0.00897217 ,0.01043701 ,0.00811768 ,0.00726318 ,0.00820923 , 0.00765991 ,0.00866699 ,0.00991821 ,0.00811768 ,0.01083374 , 0.00814819 ,0.01034546 ,0.00906372 ,0.00988770 ,0.00909424 , 0.00942993 ,0.01037598 ,0.00949097 ,0.01162720 ,0.00866699 , 0.01162720 ,0.00949097 ,0.01126099 ,0.01031494 ,0.00997925 , 0.00982666 ,0.00961304 ,0.00891113 ,0.01004028 ,0.00805664 , 0.00997925 ,0.00903320 ,0.00991821 ,0.01037598 ,0.00946045 , 0.01034546 ,0.00860596 ,0.00994873 ,0.00723267 ,0.00869751 , 0.00762939 ,0.00952148 ,0.00991821 ,0.01119995 ,0.01174927 , 0.01074219 ,0.01126099 ,0.00949097 ,0.00857544 ,0.00906372 , 0.00857544 ,0.01037598 ,0.00946045 ,0.01040649 ,0.01074219 , 0.00820923 ,0.01077271 ,0.00595093 ,0.01040649 ,0.00680542 , 0.00952148 ,0.00857544 ,0.00946045 ,0.00952148 ,0.00900269 , 0.00866699 ,0.00720215 ,0.00823975 ,0.00631714 ,0.00958252 , 0.00674438 ,0.01043701 ,0.00854492 ,0.00909424 ,0.00985718 , 0.00863647 ,0.00949097 ,0.00988770 ,0.00952148 ,0.01031494 , 0.01080322 ,0.01080322 ,0.01037598 ,0.01034546 ,0.00860596 , 0.00991821 ,0.00726318 ,0.00860596 ,0.00952148 ,0.00765991 , 0.01043701 ,0.00851440 ,0.00778198 ,0.00946045 ,0.00729370 , 0.00949097 ,0.00942993 ,0.00912476 ,0.00939941 ,0.00820923 , 0.00769043 ,0.00863647 ,0.00592041 ,0.00955200 ,0.00585938 , 0.00869751 ,0.00897217 ,0.00820923 ,0.00988770 ,0.00772095 , 0.00775146 ,0.00726318 ,0.00772095 ,0.00643921 ,0.00851440 , 0.00784302 ,0.00936890 ,0.00738525 ,0.00677490 ,0.00775146 , 0.00463867 ,0.00814819 ,0.00381470 ,0.00769043 ,0.00598145 , 0.00772095 ,0.00683594 ,0.00775146 ,0.00592041 ,0.00689697 , 0.00503540 ,0.00558472 ,0.00503540 ,0.00558472 ,0.00457764 , 0.00561523 ,0.00369263 ,0.00598145 ,0.00338745 ,0.00546265 , 0.00381470 ,0.00549316 ,0.00424194 ,0.00552368 ,0.00418091 , 0.00643921 ,0.00506592 ,0.00643921 ,0.00549316 ,0.00512695 , 0.00415039 ,0.00341797 ,0.00280762 ,0.00338745 ,0.00329590 , 0.00424194 ,0.00418091 ,0.00555420 ,0.00375366 ,0.00552368 , 0.00332642 ,0.00512695 ,0.00329590 ,0.00466919 ,0.00286865 , 0.00289917 ,0.00292969 ,0.00286865 ,0.00286865 ,0.00335693 , 0.00329590 ,0.00381470 ,0.00375366 ,0.00378418 ,0.00244141 , 0.00418091 ,0.00207520 ,0.00411987 ,0.00259399 ,0.00320435 , 0.00296021 ,0.00329590 ,0.00289917 ,0.00378418 ,0.00286865 , 0.00250244 ,0.00238037 ,0.00338745 ,0.00241089 ,0.00338745 , 0.00283813 ,0.00292969 ,0.00329590 ,0.00250244 ,0.00329590 , 0.00250244 ,0.00366211 ,0.00347900 ,0.00274658 ,0.00390625 , 0.00192261 ,0.00381470 ,0.00155640 ,0.00332642 ,0.00161743 , 0.00238037 ,0.00167847 ,0.00146484 ,0.00253296 ,0.00286865 , 0.00158691 ,0.00329590 ,0.00207520 ,0.00192261 ,0.00430298 , 0.00064087 ,0.00424194 ,0.00198364 ,0.00338745 ,0.00195312 , 0.00335693 ,0.00067139 ,0.00338745 ,0.00064087 ,0.00204468 , 0.00289917 ,0.00152588 ,0.00384521 ,0.00149536 ,0.00292969 , 0.00201416 ,0.00158691 ,0.00109863 ,0.00161743 ,0.00106812 , 0.00122070 ,0.00195312 ,0.00204468 ,0.00155640 ,0.00378418 , 0.00070190 ,0.00286865 ,0.00161743 ,0.00195312 ,0.00341797 , 0.00323486 ,0.00387573 ,0.00323486 ,0.00299072 ,0.00146484 , 0.00079346 ,-0.00024414 ,0.00119019 ,0.00021362 ,0.00115967 , 0.00109863 ,0.00119019 ,0.00155640 ,0.00067139 ,0.00119019 , 0.00103760 ,0.00256348 ,0.00061035 ,0.00250244 ,0.00112915 , 0.00067139 ,0.00247192 ,-0.00103760 ,0.00106812 ,-0.00054932 , -0.00064087 ,0.00067139 ,-0.00057983 ,0.00112915 ,0.00064087 , 0.00122070 ,0.00152588 ,0.00024414 ,0.00115967 ,0.00027466 , 0.00106812 ,0.00033569 ,0.00064087 ,-0.00018311 ,-0.00057983 , 0.00021362 ,-0.00015259 ,0.00024414 ,0.00158691 ,0.00109863 , 0.00119019 ,0.00283813 ,0.00033569 ,0.00323486 ,0.00036621 , 0.00100708 ,0.00039673 ,0.00012207 ,-0.00003052 ,0.00140381 , -0.00045776 ,0.00054932 ,0.00039673 ,-0.00030518 ,-0.00009155 , 0.00109863 ,-0.00106812 ,0.00204468 ,0.00021362 ,0.00073242 , 0.00115967 ,0.00018311 ,0.00033569 ,0.00061035 ,0.00079346 , 0.00061035 ,0.00122070 ,-0.00027466 ,0.00164795 ,-0.00152588 , 0.00158691 ,-0.00152588 ,0.00073242 ,-0.00021362 ,0.00161743 , 0.00152588 ,0.00161743 ,0.00198364 ,-0.00015259 ,0.00112915 , -0.00109863 ,0.00076294 ,-0.00021362 ,0.00067139 ,0.00030518 , 0.00064087 ,0.00030518 ,0.00112915 ,-0.00021362 ,0.00119019 , -0.00024414 ,0.00161743 ,-0.00018311 ,0.00152588 ,-0.00009155 , 0.00106812 ,0.00158691 ,0.00115967 ,0.00155640 ,0.00155640 , 0.00207520 ,0.00064087 ,0.00201416 ,-0.00100708 ,0.00195312 , -0.00189209 ,0.00067139 ,0.00027466 ,0.00021362 ,0.00122070 , 0.00149536 ,-0.00061035 ,0.00253296 ,-0.00253296 ,0.00265503 , -0.00213623 ,0.00216675 ,-0.00119019 ,0.00039673 ,-0.00076294 , -0.00222778 ,0.00012207 ,-0.00402832 ,0.00106812 ,-0.00320435 , 0.00109863 ,-0.00189209 ,0.00021362 ,-0.00057983 ,-0.00109863 , -0.00100708 ,-0.00286865 ,-0.00146484 ,-0.00238037 ,-0.00064087 , -0.00057983 ,-0.00109863 ,0.00027466 ,-0.00106812 ,-0.00106812 , -0.00057983 ,-0.00152588 ,-0.00152588 ,-0.00012207 ,-0.00247192 , -0.00006104 ,-0.00247192 ,-0.00143433 ,-0.00198364 ,-0.00323486 , -0.00149536 ,-0.00372314 ,-0.00146484 ,-0.00326538 ,-0.00280762 , -0.00372314 ,-0.00323486 ,-0.00238037 ,-0.00326538 ,-0.00103760 , -0.00326538 ,-0.00103760 ,-0.00372314 ,-0.00146484 ,-0.00375366 , -0.00363159 ,-0.00329590 ,-0.00497437 ,-0.00372314 ,-0.00411987 , -0.00418091 ,-0.00277710 ,-0.00283813 ,-0.00320435 ,-0.00155640 , -0.00497437 ,-0.00241089 ,-0.00497437 ,-0.00503540 ,-0.00411987 , -0.00457764 ,-0.00500488 ,-0.00326538 ,-0.00631714 ,-0.00457764 , -0.00546265 ,-0.00543213 ,-0.00460815 ,-0.00497437 ,-0.00500488 , -0.00418091 ,-0.00494385 ,-0.00509644 ,-0.00494385 ,-0.00503540 , -0.00369263 ,-0.00454712 ,-0.00286865 ,-0.00494385 ,-0.00289917 , -0.00537109 ,-0.00418091 ,-0.00588989 ,-0.00585938 ,-0.00683594 , -0.00537109 ,-0.00637817 ,-0.00500488 ,-0.00585938 ,-0.00552368 , -0.00537109 ,-0.00683594 ,-0.00540161 ,-0.00765991 ,-0.00588989 , -0.00808716 ,-0.00677490 ,-0.00808716 ,-0.00631714 ,-0.00677490 , -0.00631714 ,-0.00549316 ,-0.00714111 ,-0.00421143 ,-0.00759888 , -0.00500488 ,-0.00723267 ,-0.00631714 ,-0.00677490 ,-0.00592041 , -0.00756836 ,-0.00460815 ,-0.00854492 ,-0.00408936 ,-0.00857544 , -0.00540161 ,-0.00857544 ,-0.00714111 ,-0.00903320 ,-0.00711060 , -0.00952148 ,-0.00714111 ,-0.00765991 ,-0.00762939 ,-0.00723267 , -0.00805664 ,-0.00811768 ,-0.00677490 ,-0.00802612 ,-0.00643921 , -0.00750732 ,-0.00732422 ,-0.00711060 ,-0.00906372 ,-0.00714111 , -0.00942993 ,-0.00762939 ,-0.00851440 ,-0.00769043 ,-0.00848389 , -0.00726318 ,-0.00845337 ,-0.00814819 ,-0.00848389 ,-0.00942993 , -0.00851440 ,-0.01031494 ,-0.00933838 ,-0.01034546 ,-0.00848389 , -0.00982666 ,-0.00729370 ,-0.00885010 ,-0.00732422 ,-0.00796509 , -0.00863647 ,-0.00756836 ,-0.00860596 ,-0.00799561 ,-0.00994873 , -0.00711060 ,-0.01034546 ,-0.00674438 ,-0.00942993 ,-0.00717163 , -0.00946045 ,-0.00668335 ,-0.00949097 ,-0.00845337 ,-0.00903320 , -0.00891113 ,-0.00946045 ,-0.00802612 ,-0.00942993 ,-0.00720215 , -0.00939941 ,-0.00637817 ,-0.01065063 ,-0.00726318 ,-0.01068115 , -0.00897217 ,-0.00942993 ,-0.00894165 ,-0.00762939 ,-0.00857544 , -0.00671387 ,-0.00854492 ,-0.00765991 ,-0.00851440 ,-0.00808716 , -0.00854492 ,-0.00802612 ,-0.00903320 ,-0.00762939 ,-0.01028442 , -0.00765991 ,-0.01025391 ,-0.00897217 ,-0.00946045 ,-0.00933838 , -0.00903320 ,-0.00802612 ,-0.00894165 ,-0.00863647 ,-0.00796509 , -0.00863647 ,-0.00799561 ,-0.00857544 ,-0.00805664 ,-0.00897217 , -0.00765991 ,-0.00851440 ,-0.00854492 ,-0.00762939 ,-0.00851440 , -0.00765991 ,-0.00854492 ,-0.00674438 ,-0.00851440 ,-0.00680542 , -0.00891113 ,-0.00726318 ,-0.00894165 ,-0.00759888 ,-0.00729370 , -0.00885010 ,-0.00646973 ,-0.00839233 ,-0.00820923 ,-0.00753784 , -0.00814819 ,-0.00808716 ,-0.00717163 ,-0.00946045 ,-0.00759888 , -0.00857544 ,-0.00717163 ,-0.00811768 ,-0.00805664 ,-0.00634766 , -0.00985718 ,-0.00543213 ,-0.01077271 ,-0.00625610 ,-0.00942993 , -0.00765991 ,-0.00808716 ,-0.00851440 ,-0.00765991 ,-0.00805664 , -0.00808716 ,-0.00814819 ,-0.00708008 ,-0.00781250 ,-0.00573730 , -0.00686646 ,-0.00585938 ,-0.00762939 ,-0.00726318 ,-0.00759888 , -0.00769043 ,-0.00717163 ,-0.00765991 ,-0.00634766 ,-0.00894165 , -0.00546265 ,-0.00942993 ,-0.00625610 ,-0.00817871 ,-0.00799561 , -0.00729370 ,-0.00714111 ,-0.00726318 ,-0.00537109 ,-0.00820923 , -0.00531006 ,-0.00866699 ,-0.00576782 ,-0.00857544 ,-0.00637817 , -0.00796509 ,-0.00689697 ,-0.00799561 ,-0.00637817 ,-0.00765991 , -0.00714111 ,-0.00640869 ,-0.00714111 ,-0.00549316 ,-0.00720215 , -0.00500488 ,-0.00723267 ,-0.00631714 ,-0.00720215 ,-0.00762939 , -0.00637817 ,-0.00759888 ,-0.00506592 ,-0.00714111 ,-0.00595093 , -0.00582886 ,-0.00814819 ,-0.00582886 ,-0.00900269 ,-0.00628662 , -0.00811768 ,-0.00546265 ,-0.00671387 ,-0.00509644 ,-0.00708008 , -0.00555420 ,-0.00805664 ,-0.00588989 ,-0.00851440 ,-0.00588989 , -0.00762939 ,-0.00592041 ,-0.00674438 ,-0.00503540 ,-0.00674438 , -0.00588989 ,-0.00592041 ,-0.00674438 ,-0.00457764 ,-0.00677490 , -0.00588989 ,-0.00677490 ,-0.00588989 ,-0.00677490 ,-0.00543213 , -0.00723267 ,-0.00585938 ,-0.00723267 ,-0.00585938 ,-0.00726318 , -0.00585938 ,-0.00677490 ,-0.00634766 ,-0.00540161 ,-0.00640869 , -0.00537109 ,-0.00552368 ,-0.00717163 ,-0.00546265 ,-0.00854492 , -0.00625610 ,-0.00686646 ,-0.00665283 ,-0.00558472 ,-0.00708008 , -0.00469971 ,-0.00753784 ,-0.00375366 ,-0.00805664 ,-0.00546265 , -0.00723267 ,-0.00588989 ,-0.00631714 ,-0.00543213 ,-0.00680542 , -0.00540161 ,-0.00595093 ,-0.00717163 ,-0.00503540 ,-0.00848389 , -0.00506592 ,-0.00759888 ,-0.00549316 ,-0.00714111 ,-0.00506592 , -0.00677490 ,-0.00585938 ,-0.00726318 ,-0.00622559 ,-0.00686646 , -0.00625610 ,-0.00686646 ,-0.00665283 ,-0.00732422 ,-0.00622559 , -0.00726318 ,-0.00588989 ,-0.00762939 ,-0.00546265 ,-0.00808716 , -0.00588989 ,-0.00808716 ,-0.00674438 ,-0.00680542 ,-0.00717163 , -0.00680542 ,-0.00631714 ,-0.00720215 ,-0.00588989 ,-0.00631714 , -0.00723267 ,-0.00631714 ,-0.00680542 ,-0.00714111 ,-0.00640869 , -0.00799561 ,-0.00775146 ,-0.00753784 ,-0.00686646 ,-0.00714111 , -0.00506592 ,-0.00717163 ,-0.00503540 ,-0.00674438 ,-0.00588989 , -0.00765991 ,-0.00677490 ,-0.00762939 ,-0.00637817 ,-0.00622559 , -0.00601196 ,-0.00753784 ,-0.00640869 ,-0.00848389 ,-0.00765991 , -0.00762939 ,-0.00723267 ,-0.00762939 ,-0.00543213 ,-0.00729370 , -0.00576782 ,-0.00646973 ,-0.00753784 ,-0.00549316 ,-0.00765991 , -0.00454712 ,-0.00723267 ,-0.00460815 ,-0.00625610 ,-0.00595093 , -0.00628662 ,-0.00723267 ,-0.00720215 ,-0.00677490 ,-0.00765991 , -0.00674438 ,-0.00723267 ,-0.00762939 ,-0.00720215 ,-0.00769043 , -0.00802612 ,-0.00726318 ,-0.00845337 ,-0.00640869 ,-0.00759888 , -0.00680542 ,-0.00628662 ,-0.00634766 ,-0.00674438 ,-0.00592041 , -0.00897217 ,-0.00500488 ,-0.00765991 ,-0.00494385 ,-0.00598145 , -0.00537109 ,-0.00640869 ,-0.00582886 ,-0.00680542 ,-0.00631714 , -0.00634766 ,-0.00585938 ,-0.00592041 ,-0.00543213 ,-0.00592041 , -0.00671387 ,-0.00552368 ,-0.00802612 ,-0.00546265 ,-0.00769043 , -0.00579834 ,-0.00643921 ,-0.00579834 ,-0.00683594 ,-0.00674438 , -0.00674438 ,-0.00683594 ,-0.00668335 ,-0.00598145 ,-0.00714111 , -0.00418091 ,-0.00674438 ,-0.00415039 ,-0.00677490 ,-0.00454712 , -0.00637817 ,-0.00537109 ,-0.00683594 ,-0.00585938 ,-0.00592041 , -0.00543213 ,-0.00503540 ,-0.00585938 ,-0.00543213 ,-0.00509644 , -0.00665283 ,-0.00604248 ,-0.00576782 ,-0.00726318 ,-0.00454712 , -0.00631714 ,-0.00549316 ,-0.00497437 ,-0.00549316 ,-0.00540161 , -0.00326538 ,-0.00723267 ,-0.00189209 ,-0.00637817 ,-0.00326538 , -0.00491333 ,-0.00561523 ,-0.00396729 ,-0.00601196 ,-0.00363159 , -0.00457764 ,-0.00375366 ,-0.00360107 ,-0.00289917 ,-0.00451660 , -0.00241089 ,-0.00415039 ,-0.00274658 ,-0.00378418 ,-0.00405884 , -0.00289917 ,-0.00445557 ,-0.00253296 ,-0.00396729 ,-0.00253296 , -0.00448608 ,-0.00238037 ,-0.00418091 ,-0.00274658 ,-0.00289917 , -0.00274658 ,-0.00329590 ,-0.00323486 ,-0.00369263 ,-0.00369263 , -0.00286865 ,-0.00363159 ,-0.00241089 ,-0.00323486 ,-0.00192261 , -0.00286865 ,-0.00192261 ,-0.00280762 ,-0.00238037 ,-0.00189209 , -0.00289917 ,-0.00228882 ,-0.00247192 ,-0.00140381 ,-0.00247192 , -0.00051880 ,-0.00204468 ,-0.00180054 ,-0.00210571 ,-0.00311279 , -0.00207520 ,-0.00225830 ,-0.00201416 ,-0.00143433 ,-0.00155640 , -0.00103760 ,-0.00064087 ,-0.00192261 ,-0.00061035 ,-0.00195312 , -0.00015259 ,-0.00241089 ,0.00027466 ,-0.00283813 ,-0.00061035 , -0.00061035 ,-0.00195312 ,0.00070190 ,-0.00238037 ,0.00024414 , -0.00103760 ,-0.00064087 ,-0.00152588 ,-0.00143433 ,-0.00152588 , -0.00061035 ,-0.00109863 ,0.00033569 ,-0.00115967 ,-0.00009155 , -0.00198364 ,0.00024414 ,-0.00228882 ,0.00012207 ,-0.00094604 , -0.00067139 ,0.00024414 ,-0.00100708 ,0.00061035 ,-0.00097656 , 0.00155640 ,-0.00064087 ,0.00112915 ,-0.00057983 ,0.00021362 , -0.00012207 ,0.00018311 ,-0.00012207 ,0.00024414 ,0.00067139 , -0.00057983 ,0.00064087 ,-0.00097656 ,0.00018311 ,0.00158691 , 0.00027466 ,0.00286865 ,-0.00012207 ,0.00238037 ,-0.00015259 , 0.00158691 ,0.00112915 ,0.00112915 ,0.00244141 ,0.00073242 , 0.00375366 ,0.00070190 ,0.00289917 ,0.00198364 ,0.00207520 , 0.00283813 ,0.00161743 ,0.00286865 ,0.00244141 ,0.00250244 , 0.00372314 ,0.00161743 ,0.00329590 ,0.00247192 ,0.00241089 , 0.00341797 ,0.00149536 ,0.00384521 ,0.00234985 ,0.00213623 , 0.00366211 ,0.00167847 ,0.00323486 ,0.00341797 ,0.00241089 , 0.00378418 ,0.00289917 ,0.00375366 ,0.00332642 ,0.00424194 , 0.00286865 ,0.00421143 ,0.00296021 ,0.00451660 ,0.00433350 , 0.00369263 ,0.00469971 ,0.00375366 ,0.00378418 ,0.00503540 , 0.00473022 ,0.00588989 ,0.00515747 ,0.00549316 ,0.00506592 , 0.00427246 ,0.00546265 ,0.00384521 ,0.00546265 ,0.00469971 , 0.00503540 ,0.00515747 ,0.00457764 ,0.00518799 ,0.00497437 , 0.00476074 ,0.00543213 ,0.00601196 ,0.00421143 ,0.00680542 , 0.00338745 ,0.00680542 ,0.00378418 ,0.00729370 ,0.00329590 , 0.00732422 ,0.00418091 ,0.00643921 ,0.00506592 ,0.00643921 , 0.00500488 ,0.00695801 ,0.00540161 ,0.00653076 ,0.00543213 , 0.00646973 ,0.00546265 ,0.00473022 ,0.00720215 ,0.00341797 , 0.00674438 ,0.00518799 ,0.00634766 ,0.00643921 ,0.00595093 , 0.00640869 ,0.00595093 ,0.00686646 ,0.00637817 ,0.00601196 , 0.00680542 ,0.00601196 ,0.00723267 ,0.00598145 ,0.00689697 , 0.00717163 ,0.00613403 ,0.00711060 ,0.00610352 ,0.00631714 , 0.00689697 ,0.00595093 ,0.00598145 ,0.00680542 ,0.00598145 , 0.00732422 ,0.00592041 ,0.00686646 ,0.00686646 ,0.00677490 , 0.00735474 ,0.00683594 ,0.00680542 ,0.00735474 ,0.00588989 , 0.00732422 ,0.00640869 ,0.00772095 ,0.00729370 ,0.00814819 , 0.00729370 ,0.00811768 ,0.00735474 ,0.00723267 ,0.00820923 , 0.00723267 ,0.00775146 ,0.00903320 ,0.00637817 ,0.00955200 , 0.00543213 ,0.00823975 ,0.00592041 ,0.00772095 ,0.00823975 , 0.00762939 ,0.01000977 ,0.00674438 ,0.00955200 ,0.00640869 , 0.00946045 ,0.00686646 ,0.00946045 ,0.00686646 ,0.00946045 , 0.00686646 ,0.00946045 ,0.00732422 ,0.00988770 ,0.00772095 , 0.00994873 ,0.00811768 ,0.00866699 ,0.00857544 ,0.00903320 , 0.00860596 ,0.00949097 ,0.00857544 ,0.00909424 ,0.00854492 , 0.00909424 ,0.00811768 ,0.00863647 ,0.00814819 ,0.00903320 , 0.00772095 ,0.00906372 ,0.00726318 ,0.00817871 ,0.00817871 , 0.00769043 ,0.00860596 ,0.00949097 ,0.00860596 ,0.01077271 , 0.00823975 ,0.01022339 ,0.00915527 ,0.00854492 ,0.00949097 , 0.00860596 ,0.00903320 ,0.01034546 ,0.00729370 ,0.01037598 , 0.00900269 ,0.00866699 ,0.01028442 ,0.00909424 ,0.00988770 , 0.00952148 ,0.00988770 ,0.00817871 ,0.01165771 ,0.00729370 , 0.01211548 ,0.00732422 ,0.01071167 ,0.00869751 ,0.00894165 , 0.00958252 ,0.00851440 ,0.00955200 ,0.00939941 ,0.01000977 , 0.00982666 ,0.01000977 ,0.00894165 ,0.01046753 ,0.00851440 , 0.01040649 ,0.00811768 ,0.01040649 ,0.00811768 ,0.01000977 , 0.00805664 ,0.00997925 ,0.00860596 ,0.01028442 ,0.00833130 , 0.01058960 ,0.00833130 ,0.01110840 ,0.00823975 ,0.01165771 , 0.00775146 ,0.01159668 ,0.00869751 ,0.01113892 ,0.00915527 , 0.00979614 ,0.01004028 ,0.00979614 ,0.01135254 ,0.00805664 , 0.01309204 ,0.00765991 ,0.01300049 ,0.00906372 ,0.01119995 , 0.00994873 ,0.01080322 ,0.00900269 ,0.01129150 ,0.00900269 , 0.01037598 ,0.00946045 ,0.01037598 ,0.00949097 ,0.01031494 , 0.01043701 ,0.01025391 ,0.01046753 ,0.01116943 ,0.00991821 , 0.01260376 ,0.01028442 ,0.01223755 ,0.01110840 ,0.01089478 , 0.01202393 ,0.00997925 ,0.01083374 ,0.01071167 ,0.01043701 , 0.01074219 ,0.01126099 ,0.01037598 ,0.01165771 ,0.01123047 , 0.01126099 ,0.01119995 ,0.01083374 ,0.01077271 ,0.01083374 , 0.01074219 ,0.01174927 ,0.01071167 ,0.01217651 ,0.01119995 , 0.01126099 ,0.01123047 ,0.01168823 ,0.01031494 ,0.01303101 , 0.00988770 ,0.01303101 ,0.01119995 ,0.01171875 ,0.01119995 , 0.01037598 ,0.01165771 ,0.01037598 ,0.01211548 ,0.01083374 , 0.01116943 ,0.01306152 ,0.00982666 ,0.01394653 ,0.00946045 , 0.01257324 ,0.01119995 ,0.01171875 ,0.01162720 ,0.01129150 , 0.01205444 ,0.01086426 ,0.01248169 ,0.01043701 ,0.01205444 , 0.01126099 ,0.01077271 ,0.01214600 ,0.01034546 ,0.01211548 , 0.01037598 ,0.01248169 ,0.01046753 ,0.01156616 ,0.01223755 , 0.00982666 ,0.01260376 ,0.01119995 ,0.01083374 ,0.01296997 , 0.01083374 ,0.01208496 ,0.01211548 ,0.01126099 ,0.01296997 , 0.01037598 ,0.01296997 ,0.01083374 ,0.01165771 ,0.01168823 , 0.01031494 ,0.01129150 ,0.01077271 ,0.01126099 ,0.01119995 , 0.01037598 ,0.01211548 ,0.00949097 ,0.01211548 ,0.00988770 , 0.00994873 ,0.01077271 ,0.00909424 ,0.01159668 ,0.00912476 , 0.01248169 ,0.00952148 ,0.01254272 ,0.00991821 ,0.01080322 , 0.01080322 ,0.00991821 ,0.01123047 ,0.01034546 ,0.01260376 , 0.00985718 ,0.01174927 ,0.01028442 ,0.01083374 ,0.01080322 , 0.01119995 ,0.00997925 ,0.01205444 ,0.00952148 ,0.01162720 , 0.00997925 ,0.01071167 ,0.01004028 ,0.01025391 ,0.00952148 , 0.01080322 ,0.00988770 ,0.01083374 ,0.00946045 ,0.01168823 , 0.00903320 ,0.01211548 ,0.00860596 ,0.01077271 ,0.00952148 , 0.00946045 ,0.00991821 ,0.00991821 ,0.00949097 ,0.01077271 , 0.00955200 ,0.01113892 ,0.00958252 ,0.01113892 ,0.00955200 , 0.01165771 ,0.00903320 ,0.01126099 ,0.00900269 ,0.00994873 , 0.00903320 ,0.00900269 ,0.01000977 ,0.00802612 ,0.01141357 , 0.00839233 ,0.01101685 ,0.00885010 ,0.00918579 ,0.00985718 , 0.00903320 ,0.00952148 ,0.00900269 ,0.00994873 ,0.00860596 , 0.01034546 ,0.00772095 ,0.01037598 ,0.00637817 ,0.01037598 , 0.00729370 ,0.01031494 ,0.00778198 ,0.00946045 ,0.00726318 , 0.00949097 ,0.00860596 ,0.00857544 ,0.00909424 ,0.00765991 , 0.00952148 ,0.00814819 ,0.00860596 ,0.00814819 ,0.00775146 , 0.00857544 ,0.00817871 ,0.00814819 ,0.00906372 ,0.00726318 , 0.00952148 ,0.00677490 ,0.00866699 ,0.00723267 ,0.00820923 , 0.00680542 ,0.00817871 ,0.00683594 ,0.00860596 ,0.00683594 , 0.00817871 ,0.00640869 ,0.00857544 ,0.00686646 ,0.00903320 , 0.00729370 ,0.00775146 ,0.00723267 ,0.00820923 ,0.00680542 , 0.00686646 ,0.00643921 ,0.00588989 ,0.00781250 ,0.00546265 , 0.00729370 ,0.00598145 ,0.00640869 ,0.00592041 ,0.00561523 , 0.00585938 ,0.00515747 ,0.00598145 ,0.00631714 ,0.00521851 , 0.00799561 ,0.00527954 ,0.00668335 ,0.00653076 ,0.00543213 , 0.00646973 ,0.00637817 ,0.00466919 ,0.00683594 ,0.00506592 , 0.00643921 ,0.00595093 ,0.00598145 ,0.00506592 ,0.00640869 , 0.00421143 ,0.00640869 ,0.00598145 ,0.00549316 ,0.00643921 , 0.00503540 ,0.00561523 ,0.00500488 ,0.00558472 ,0.00506592 , 0.00463867 ,0.00555420 ,0.00552368 ,0.00552368 ,0.00595093 , 0.00381470 ,0.00549316 ,0.00378418 ,0.00512695 ,0.00497437 , 0.00433350 ,0.00457764 ,0.00466919 ,0.00381470 ,0.00543213 , 0.00341797 ,0.00592041 ,0.00332642 ,0.00604248 ,0.00405884 , 0.00482178 ,0.00494385 ,0.00390625 ,0.00497437 ,0.00427246 , 0.00415039 ,0.00473022 ,0.00326538 ,0.00512695 ,0.00332642 , 0.00375366 ,0.00421143 ,0.00335693 ,0.00415039 ,0.00430298 , 0.00323486 ,0.00387573 ,0.00323486 ,0.00381470 ,0.00332642 , 0.00418091 ,0.00381470 ,0.00378418 ,0.00415039 ,0.00250244 , 0.00503540 ,0.00253296 ,0.00411987 ,0.00299072 ,0.00411987 , 0.00164795 ,0.00460815 ,0.00158691 ,0.00375366 ,0.00335693 , 0.00332642 ,0.00421143 ,0.00332642 ,0.00244141 ,0.00338745 , 0.00238037 ,0.00296021 ,0.00283813 ,0.00247192 ,0.00250244 , 0.00280762 ,0.00167847 ,0.00231934 ,0.00256348 ,0.00195312 , 0.00292969 ,0.00109863 ,0.00292969 ,0.00109863 ,0.00292969 , 0.00198364 ,0.00292969 ,0.00198364 ,0.00381470 ,0.00152588 , 0.00427246 ,0.00149536 ,0.00430298 ,0.00149536 ,0.00292969 , 0.00289917 ,0.00152588 ,0.00469971 ,0.00112915 ,0.00375366 , 0.00204468 ,0.00198364 ,0.00201416 ,0.00204468 ,0.00112915 , 0.00244141 ,0.00115967 ,0.00241089 ,0.00119019 ,0.00198364 , 0.00112915 ,0.00244141 ,0.00030518 ,0.00283813 ,0.00033569 , 0.00280762 ,0.00076294 ,0.00195312 ,0.00161743 ,0.00155640 , 0.00112915 ,0.00073242 ,0.00109863 ,0.00158691 ,0.00070190 , 0.00244141 ,0.00067139 ,0.00296021 ,0.00195312 ,0.00119019 , 0.00283813 ,-0.00103760 ,0.00332642 ,-0.00146484 ,0.00244141 , -0.00018311 ,0.00155640 ,0.00112915 ,0.00030518 ,0.00198364 , -0.00103760 ,0.00155640 ,-0.00064087 ,0.00076294 ,0.00061035 , 0.00033569 ,0.00064087 ,0.00073242 ,0.00024414 ,0.00112915 , 0.00027466 ,0.00152588 ,-0.00009155 ,0.00189209 ,-0.00048828 , 0.00192261 ,-0.00100708 ,0.00064087 ,-0.00054932 ,-0.00027466 , -0.00006104 ,-0.00030518 ,0.00036621 ,-0.00115967 ,0.00076294 , -0.00198364 ,0.00076294 ,-0.00112915 ,-0.00100708 ,-0.00024414 , -0.00146484 ,0.00027466 ,-0.00195312 ,-0.00018311 ,-0.00323486 , 0.00021362 ,-0.00234985 ,0.00024414 ,-0.00103760 ,-0.00064087 , 0.00027466 ,-0.00152588 ,-0.00103760 ,-0.00192261 ,-0.00238037 , -0.00109863 ,-0.00189209 ,-0.00152588 ,-0.00057983 ,-0.00241089 , -0.00103760 ,-0.00155640 ,-0.00228882 ,-0.00070190 ,-0.00274658 , -0.00109863 ,-0.00280762 ,-0.00106812 ,-0.00277710 ,-0.00067139 , -0.00231934 ,-0.00109863 ,-0.00192261 ,-0.00234985 ,-0.00198364 , -0.00277710 ,-0.00241089 ,-0.00189209 ,-0.00286865 ,-0.00189209 , -0.00198364 ,-0.00323486 ,-0.00238037 ,-0.00323486 ,-0.00241089 , -0.00366211 ,-0.00244141 ,-0.00360107 ,-0.00292969 ,-0.00268555 , -0.00381470 ,-0.00274658 ,-0.00369263 ,-0.00329590 ,-0.00320435 , -0.00418091 ,-0.00277710 ,-0.00372314 ,-0.00366211 ,-0.00326538 , -0.00369263 ,-0.00372314 ,-0.00231934 ,-0.00421143 ,-0.00274658 , -0.00460815 ,-0.00369263 ,-0.00457764 ,-0.00411987 ,-0.00549316 , -0.00405884 ,-0.00552368 ,-0.00408936 ,-0.00418091 ,-0.00540161 , -0.00329590 ,-0.00540161 ,-0.00375366 ,-0.00582886 ,-0.00332642 , -0.00668335 ,-0.00286865 ,-0.00588989 ,-0.00366211 ,-0.00506592 , -0.00451660 ,-0.00543213 ,-0.00509644 ,-0.00491333 ,-0.00598145 , -0.00537109 ,-0.00640869 ,-0.00540161 ,-0.00546265 ,-0.00585938 , -0.00549316 ,-0.00585938 ,-0.00637817 ,-0.00540161 ,-0.00723267 , -0.00631714 ,-0.00674438 ,-0.00637817 ,-0.00674438 ,-0.00588989 , -0.00769043 ,-0.00579834 ,-0.00729370 ,-0.00671387 ,-0.00723267 , -0.00634766 ,-0.00759888 ,-0.00640869 ,-0.00711060 ,-0.00772095 , -0.00582886 ,-0.00857544 ,-0.00631714 ,-0.00854492 ,-0.00671387 , -0.00857544 ,-0.00714111 ,-0.00906372 ,-0.00668335 ,-0.00900269 , -0.00674438 ,-0.00854492 ,-0.00762939 ,-0.00765991 ,-0.00762939 , -0.00854492 ,-0.00854492 ,-0.00848389 ,-0.00854492 ,-0.00674438 , -0.00900269 ,-0.00546265 ,-0.01022339 ,-0.00640869 ,-0.00888062 , -0.00900269 ,-0.00808716 ,-0.00939941 ,-0.00897217 ,-0.00894165 , -0.00769043 ,-0.00933838 ,-0.00686646 ,-0.00842285 ,-0.00857544 , -0.00851440 ,-0.00900269 ,-0.00894165 ,-0.00939941 ,-0.00851440 , -0.00985718 ,-0.00851440 ,-0.00900269 ,-0.00891113 ,-0.00726318 , -0.00979614 ,-0.00680542 ,-0.01110840 ,-0.00686646 ,-0.01147461 , -0.00732422 ,-0.01150513 ,-0.00900269 ,-0.01074219 ,-0.00894165 , -0.00982666 ,-0.00854492 ,-0.01028442 ,-0.00848389 ,-0.00991821 , -0.00930786 ,-0.01037598 ,-0.00888062 ,-0.01126099 ,-0.00753784 , -0.01168823 ,-0.00756836 ,-0.00949097 ,-0.00891113 ,-0.00854492 , -0.00985718 ,-0.00891113 ,-0.01037598 ,-0.00930786 ,-0.01077271 , -0.00939941 ,-0.01025391 ,-0.00946045 ,-0.01022339 ,-0.00988770 , -0.00979614 ,-0.00988770 ,-0.00933838 ,-0.00994873 ,-0.00973511 , -0.00949097 ,-0.01065063 ,-0.00946045 ,-0.01110840 ,-0.00946045 , -0.01025391 ,-0.00936890 ,-0.00946045 ,-0.00939941 ,-0.00936890 , -0.00903320 ,-0.00930786 ,-0.00946045 ,-0.00985718 ,-0.00939941 , -0.00979614 ,-0.01037598 ,-0.00973511 ,-0.01126099 ,-0.01019287 , -0.00991821 ,-0.01199341 ,-0.00765991 ,-0.01293945 ,-0.00799561 , -0.01129150 ,-0.01062012 ,-0.00988770 ,-0.01159668 ,-0.01025391 , -0.00942993 ,-0.00985718 ,-0.00891113 ,-0.00946045 ,-0.00979614 , -0.00946045 ,-0.01065063 ,-0.01037598 ,-0.01016235 ,-0.01080322 , -0.00936890 ,-0.01028442 ,-0.00942993 ,-0.00936890 ,-0.00985718 , -0.01025391 ,-0.00942993 ,-0.01028442 ,-0.00894165 ,-0.00988770 , -0.00936890 ,-0.00936890 ,-0.01037598 ,-0.00888062 ,-0.01077271 , -0.00985718 ,-0.01065063 ,-0.00991821 ,-0.00894165 ,-0.01025391 , -0.00854492 ,-0.01074219 ,-0.00936890 ,-0.00946045 ,-0.01110840 , -0.00808716 ,-0.01116943 ,-0.00851440 ,-0.00988770 ,-0.00936890 , -0.00942993 ,-0.00894165 ,-0.00982666 ,-0.00854492 ,-0.01031494 , -0.00802612 ,-0.00991821 ,-0.00756836 ,-0.00946045 ,-0.00762939 , -0.00939941 ,-0.00808716 ,-0.00854492 ,-0.00936890 ,-0.00814819 , -0.00933838 ,-0.00854492 ,-0.00942993 ,-0.00759888 ,-0.00991821 , -0.00756836 ,-0.00900269 ,-0.00851440 ,-0.00808716 ,-0.00851440 , -0.00854492 ,-0.00808716 ,-0.00894165 ,-0.00811768 ,-0.00891113 , -0.00772095 ,-0.00933838 ,-0.00814819 ,-0.00891113 ,-0.00811768 , -0.00851440 ,-0.00808716 ,-0.00717163 ,-0.00817871 ,-0.00753784 , -0.00729370 ,-0.00845337 ,-0.00634766 ,-0.00857544 ,-0.00714111 , -0.00723267 ,-0.00808716 ,-0.00674438 ,-0.00946045 ,-0.00671387 , -0.00897217 ,-0.00769043 ,-0.00668335 ,-0.00863647 ,-0.00619507 , -0.00820923 ,-0.00625610 ,-0.00726318 ,-0.00671387 ,-0.00680542 , -0.00717163 ,-0.00680542 ,-0.00677490 ,-0.00759888 ,-0.00683594 , -0.00668335 ,-0.00729370 ,-0.00628662 ,-0.00677490 ,-0.00677490 , -0.00588989 ,-0.00759888 ,-0.00555420 ,-0.00842285 ,-0.00552368 , -0.00717163 ,-0.00588989 ,-0.00637817 ,-0.00668335 ,-0.00729370 , -0.00537109 ,-0.00860596 ,-0.00497437 ,-0.00851440 ,-0.00546265 , -0.00717163 ,-0.00726318 ,-0.00540161 ,-0.00769043 ,-0.00451660 , -0.00726318 ,-0.00497437 ,-0.00680542 ,-0.00405884 ,-0.00860596 , -0.00366211 ,-0.00851440 ,-0.00506592 ,-0.00668335 ,-0.00640869 , -0.00537109 ,-0.00683594 ,-0.00540161 ,-0.00592041 ,-0.00634766 , -0.00540161 ,-0.00723267 ,-0.00546265 ,-0.00674438 ,-0.00549316 , -0.00628662 ,-0.00503540 ,-0.00588989 ,-0.00454712 ,-0.00680542 , -0.00454712 ,-0.00674438 ,-0.00552368 ,-0.00668335 ,-0.00509644 , -0.00625610 ,-0.00372314 ,-0.00631714 ,-0.00375366 ,-0.00579834 , -0.00466919 ,-0.00537109 ,-0.00549316 ,-0.00460815 ,-0.00622559 , -0.00424194 ,-0.00537109 ,-0.00463867 ,-0.00585938 ,-0.00503540 , -0.00628662 ,-0.00503540 ,-0.00500488 ,-0.00546265 ,-0.00369263 , -0.00546265 ,-0.00411987 ,-0.00543213 ,-0.00506592 ,-0.00494385 , -0.00549316 ,-0.00546265 ,-0.00408936 ,-0.00506592 ,-0.00451660 , -0.00372314 ,-0.00677490 ,-0.00280762 ,-0.00720215 ,-0.00326538 , -0.00585938 ,-0.00329590 ,-0.00415039 ,-0.00405884 ,-0.00424194 , -0.00488281 ,-0.00381470 ,-0.00494385 ,-0.00415039 ,-0.00503540 , -0.00497437 ,-0.00460815 ,-0.00497437 ,-0.00506592 ,-0.00405884 , -0.00640869 ,-0.00274658 ,-0.00637817 ,-0.00189209 ,-0.00549316 , -0.00323486 ,-0.00454712 ,-0.00375366 ,-0.00494385 ,-0.00506592 , -0.00454712 ,-0.00543213 ,-0.00329590 ,-0.00585938 ,-0.00286865 , -0.00494385 ,-0.00418091 ,-0.00366211 ,-0.00503540 ,-0.00283813 , -0.00540161 ,-0.00241089 ,-0.00631714 ,-0.00238037 ,-0.00546265 , -0.00366211 ,-0.00503540 ,-0.00500488 ,-0.00411987 ,-0.00595093 , -0.00317383 ,-0.00506592 ,-0.00366211 ,-0.00415039 ,-0.00457764 , -0.00372314 ,-0.00402832 ,-0.00427246 ,-0.00357056 ,-0.00424194 , -0.00317383 ,-0.00418091 ,-0.00320435 ,-0.00418091 ,-0.00323486 , -0.00503540 ,-0.00280762 ,-0.00454712 ,-0.00369263 ,-0.00241089 , -0.00497437 ,-0.00155640 ,-0.00448608 ,-0.00289917 ,-0.00451660 , -0.00326538 ,-0.00460815 ,-0.00189209 ,-0.00503540 ,-0.00238037 , -0.00411987 ,-0.00283813 ,-0.00326538 ,-0.00408936 ,-0.00283813 , -0.00415039 ,-0.00234985 ,-0.00460815 ,-0.00192261 ,-0.00454712 , -0.00286865 ,-0.00363159 ,-0.00332642 ,-0.00186157 ,-0.00329590 , -0.00238037 ,-0.00234985 ,-0.00332642 ,-0.00228882 ,-0.00418091 , -0.00320435 ,-0.00289917 ,-0.00363159 ,-0.00241089 ,-0.00280762 , -0.00366211 ,-0.00244141 ,-0.00366211 ,-0.00238037 ,-0.00326538 , -0.00323486 ,-0.00283813 ,-0.00326538 ,-0.00192261 ,-0.00283813 , -0.00189209 ,-0.00241089 ,-0.00152588 ,-0.00186157 ,-0.00161743 , -0.00222778 ,-0.00161743 ,-0.00186157 ,-0.00152588 ,-0.00152588 , -0.00186157 ,-0.00115967 ,-0.00228882 ,-0.00109863 ,-0.00192261 , -0.00152588 ,-0.00100708 ,-0.00158691 ,-0.00140381 ,-0.00112915 , -0.00146484 ,-0.00106812 ,-0.00061035 ,-0.00155640 ,-0.00009155 , -0.00115967 ,-0.00051880 ,-0.00027466 ,-0.00186157 ,0.00018311 , -0.00274658 ,0.00021362 ,-0.00106812 ,-0.00186157 ,0.00061035 , -0.00320435 ,0.00158691 ,-0.00198364 ,-0.00015259 ,-0.00057983 , -0.00030518 ,-0.00045776 ,0.00057983 ,-0.00057983 ,0.00070190 , 0.00024414 ,-0.00061035 ,0.00067139 ,-0.00100708 ,0.00149536 , -0.00094604 ,0.00192261 ,-0.00146484 ,0.00158691 ,-0.00064087 , 0.00027466 ,0.00024414 ,-0.00057983 ,0.00061035 ,-0.00006104 , 0.00100708 ,-0.00009155 ,0.00155640 ,-0.00067139 ,0.00164795 , -0.00024414 ,0.00161743 ,0.00024414 ,0.00198364 ,0.00030518 , 0.00198364 ,-0.00057983 ,0.00241089 ,-0.00057983 ,0.00238037 , 0.00036621 ,0.00149536 ,0.00115967 ,0.00204468 ,0.00106812 , 0.00161743 ,0.00158691 ,0.00155640 ,0.00247192 ,0.00155640 , 0.00289917 ,0.00112915 ,0.00378418 ,0.00027466 ,0.00418091 , 0.00115967 ,0.00332642 ,0.00198364 ,0.00207520 ,0.00286865 , 0.00201416 ,0.00329590 ,0.00164795 ,0.00326538 ,0.00207520 , 0.00329590 ,0.00198364 ,0.00384521 ,0.00195312 ,0.00338745 , 0.00326538 ,0.00161743 ,0.00332642 ,0.00244141 ,0.00204468 , 0.00372314 ,0.00292969 ,0.00244141 ,0.00509644 ,0.00198364 , 0.00424194 ,0.00286865 ,0.00338745 ,0.00457764 ,0.00292969 , 0.00460815 ,0.00381470 ,0.00378418 ,0.00503540 ,0.00338745 , 0.00460815 ,0.00378418 ,0.00466919 ,0.00418091 ,0.00424194 , 0.00463867 ,0.00332642 ,0.00509644 ,0.00289917 ,0.00552368 , 0.00244141 ,0.00555420 ,0.00241089 ,0.00558472 ,0.00415039 , 0.00515747 ,0.00411987 ,0.00430298 ,0.00411987 ,0.00384521 , 0.00549316 ,0.00421143 ,0.00598145 ,0.00463867 ,0.00552368 , 0.00424194 ,0.00592041 ,0.00381470 ,0.00637817 ,0.00463867 , 0.00512695 ,0.00595093 ,0.00463867 ,0.00509644 ,0.00506592 , 0.00378418 ,0.00555420 ,0.00463867 ,0.00552368 ,0.00421143 , 0.00726318 ,0.00289917 ,0.00732422 ,0.00283813 ,0.00692749 , 0.00408936 ,0.00653076 ,0.00497437 ,0.00561523 ,0.00592041 , 0.00466919 ,0.00640869 ,0.00378418 ,0.00683594 ,0.00375366 , 0.00732422 ,0.00372314 ,0.00781250 ,0.00454712 ,0.00692749 , 0.00592041 ,0.00466919 ,0.00683594 ,0.00463867 ,0.00643921 , 0.00549316 ,0.00555420 ,0.00640869 ,0.00506592 ,0.00640869 , 0.00466919 ,0.00637817 ,0.00427246 ,0.00765991 ,0.00335693 , 0.00860596 ,0.00460815 ,0.00735474 ,0.00549316 ,0.00552368 , 0.00643921 ,0.00500488 ,0.00781250 ,0.00503540 ,0.00775146 , 0.00683594 ,0.00598145 ,0.00769043 ,0.00469971 ,0.00720215 , 0.00518799 ,0.00677490 ,0.00643921 ,0.00595093 ,0.00729370 , 0.00595093 ,0.00775146 ,0.00506592 ,0.00775146 ,0.00460815 , 0.00775146 ,0.00637817 ,0.00601196 ,0.00637817 ,0.00643921 , 0.00634766 ,0.00643921 ,0.00595093 ,0.00729370 ,0.00598145 , 0.00772095 ,0.00506592 ,0.00775146 ,0.00460815 ,0.00820923 , 0.00418091 ,0.00817871 ,0.00552368 ,0.00726318 ,0.00601196 , 0.00765991 ,0.00515747 ,0.00854492 ,0.00424194 ,0.00814819 , 0.00552368 ,0.00729370 ,0.00729370 ,0.00640869 ,0.00769043 , 0.00555420 ,0.00860596 ,0.00463867 ,0.00903320 ,0.00512695 , 0.00854492 ,0.00515747 ,0.00720215 ,0.00601196 ,0.00683594 , 0.00555420 ,0.00769043 ,0.00509644 ,0.00860596 ,0.00595093 , 0.00775146 ,0.00595093 ,0.00686646 ,0.00549316 ,0.00732422 , 0.00503540 ,0.00820923 ,0.00375366 ,0.00860596 ,0.00463867 , 0.00729370 ,0.00640869 ,0.00549316 ,0.00781250 ,0.00540161 , 0.00827026 ,0.00592041 ,0.00683594 ,0.00643921 ,0.00549316 , 0.00683594 ,0.00601196 ,0.00637817 ,0.00686646 ,0.00634766 , 0.00689697 ,0.00723267 ,0.00607300 ,0.00671387 ,0.00607300 , 0.00585938 ,0.00650024 ,0.00680542 ,0.00595093 ,0.00778198 , 0.00677490 ,0.00689697 ,0.00769043 ,0.00598145 ,0.00726318 , 0.00646973 ,0.00677490 ,0.00689697 ,0.00637817 ,0.00726318 , 0.00646973 ,0.00677490 ,0.00689697 ,0.00637817 ,0.00686646 , 0.00680542 ,0.00601196 ,0.00808716 ,0.00610352 ,0.00671387 , 0.00646973 ,0.00552368 ,0.00769043 ,0.00604248 ,0.00631714 , 0.00735474 ,0.00637817 ,0.00772095 ,0.00686646 ,0.00769043 , 0.00558472 ,0.00808716 ,0.00558472 ,0.00769043 ,0.00686646 , 0.00729370 ,0.00769043 ,0.00686646 ,0.00772095 ,0.00640869 , 0.00726318 ,0.00689697 ,0.00631714 ,0.00695801 ,0.00671387 , 0.00653076 ,0.00720215 ,0.00598145 ,0.00732422 ,0.00677490 , 0.00692749 ,0.00720215 ,0.00692749 ,0.00674438 ,0.00784302 , 0.00585938 ,0.00735474 ,0.00549316 ,0.00814819 ,0.00555420 , 0.00903320 ,0.00595093 ,0.00820923 ,0.00720215 ,0.00695801 , 0.00805664 ,0.00650024 ,0.00762939 ,0.00604248 ,0.00769043 , 0.00601196 ,0.00811768 ,0.00555420 ,0.00811768 ,0.00515747 , 0.00854492 ,0.00515747 ,0.00851440 ,0.00604248 ,0.00808716 , 0.00735474 ,0.00723267 ,0.00778198 ,0.00677490 ,0.00778198 , 0.00592041 ,0.00820923 ,0.00637817 ,0.00775146 ,0.00637817 , 0.00686646 ,0.00683594 ,0.00640869 ,0.00683594 ,0.00643921 , 0.00634766 ,0.00778198 ,0.00680542 ,0.00775146 ,0.00637817 , 0.00686646 ,0.00640869 ,0.00637817 ,0.00692749 ,0.00585938 , 0.00692749 ,0.00634766 ,0.00732422 ,0.00640869 ,0.00772095 , 0.00595093 ,0.00729370 ,0.00729370 ,0.00637817 ,0.00775146 , 0.00595093 ,0.00643921 ,0.00726318 ,0.00552368 ,0.00772095 , 0.00552368 ,0.00686646 ,0.00683594 ,0.00598145 ,0.00726318 , 0.00598145 ,0.00683594 ,0.00729370 ,0.00506592 ,0.00906372 , 0.00418091 ,0.00955200 ,0.00543213 ,0.00781250 ,0.00674438 , 0.00695801 ,0.00677490 ,0.00686646 ,0.00640869 ,0.00683594 , 0.00555420 ,0.00726318 ,0.00598145 ,0.00683594 ,0.00683594 , 0.00601196 ,0.00631714 ,0.00650024 ,0.00631714 ,0.00561523 , 0.00637817 ,0.00506592 ,0.00686646 ,0.00549316 ,0.00643921 , 0.00643921 ,0.00680542 ,0.00686646 ,0.00637817 ,0.00686646 , 0.00598145 ,0.00723267 ,0.00604248 ,0.00674438 ,0.00607300 , 0.00680542 ,0.00592041 ,0.00781250 ,0.00497437 ,0.00738525 , 0.00592041 ,0.00640869 ,0.00643921 ,0.00592041 ,0.00601196 , 0.00677490 ,0.00607300 ,0.00543213 ,0.00692749 ,0.00500488 , 0.00692749 ,0.00460815 ,0.00643921 ,0.00546265 ,0.00558472 , 0.00683594 ,0.00463867 ,0.00732422 ,0.00503540 ,0.00732422 , 0.00460815 ,0.00732422 ,0.00463867 ,0.00683594 ,0.00512695 , 0.00588989 ,0.00515747 ,0.00595093 ,0.00595093 ,0.00509644 , 0.00598145 ,0.00463867 ,0.00598145 ,0.00463867 ,0.00509644 , 0.00552368 ,0.00595093 ,0.00469971 ,0.00674438 ,0.00479126 , 0.00625610 ,0.00433350 ,0.00585938 ,0.00384521 ,0.00592041 , 0.00424194 ,0.00463867 ,0.00549316 ,0.00384521 ,0.00543213 , 0.00430298 ,0.00415039 ,0.00555420 ,0.00421143 ,0.00506592 , 0.00466919 ,0.00463867 ,0.00378418 ,0.00552368 ,0.00289917 , 0.00552368 ,0.00375366 ,0.00421143 ,0.00512695 ,0.00329590 , 0.00509644 ,0.00466919 ,0.00415039 ,0.00476074 ,0.00451660 , 0.00390625 ,0.00451660 ,0.00390625 ,0.00408936 ,0.00473022 , 0.00375366 ,0.00463867 ,0.00378418 ,0.00418091 ,0.00427246 , 0.00323486 ,0.00390625 ,0.00274658 ,0.00476074 ,0.00238037 , 0.00558472 ,0.00241089 ,0.00555420 ,0.00244141 ,0.00552368 , 0.00201416 ,0.00552368 ,0.00204468 ,0.00460815 ,0.00292969 , 0.00460815 ,0.00158691 ,0.00424194 ,0.00152588 ,0.00427246 , 0.00195312 ,0.00384521 ,0.00192261 ,0.00430298 ,0.00192261 , 0.00384521 ,0.00198364 ,0.00335693 ,0.00241089 ,0.00335693 , 0.00244141 ,0.00247192 ,0.00244141 ,0.00247192 ,0.00241089 , 0.00207520 ,0.00195312 ,0.00292969 ,0.00204468 ,0.00372314 , 0.00161743 ,0.00280762 ,0.00170898 ,0.00274658 ,0.00216675 , 0.00143433 ,0.00341797 ,0.00067139 ,0.00375366 ,0.00115967 , 0.00244141 ,0.00201416 ,0.00115967 ,0.00195312 ,0.00164795 , 0.00106812 ,0.00207520 ,0.00109863 ,0.00158691 ,0.00155640 , 0.00119019 ,0.00061035 ,0.00213623 ,-0.00033569 ,0.00259399 , 0.00018311 ,0.00204468 ,0.00115967 ,0.00018311 ,0.00167847 , 0.00015259 ,0.00076294 ,0.00155640 ,-0.00018311 ,0.00161743 , -0.00024414 ,0.00119019 ,-0.00070190 ,0.00125122 ,-0.00030518 , -0.00009155 ,0.00155640 ,-0.00067139 ,0.00167847 ,0.00015259 , -0.00012207 ,0.00109863 ,-0.00015259 ,0.00155640 ,-0.00018311 , 0.00073242 ,-0.00024414 ,0.00122070 ,0.00015259 ,0.00036621 , -0.00027466 ,0.00030518 ,-0.00061035 ,0.00109863 ,-0.00146484 , 0.00112915 ,-0.00106812 ,0.00027466 ,-0.00064087 ,0.00027466 , -0.00064087 ,0.00030518 ,-0.00067139 ,-0.00057983 ,-0.00064087 , -0.00061035 ,-0.00018311 ,-0.00061035 ,-0.00061035 ,-0.00064087 , -0.00103760 ,-0.00021362 ,-0.00100708 ,-0.00064087 ,-0.00061035 , -0.00109863 ,-0.00061035 ,-0.00103760 ,-0.00061035 ,-0.00106812 , -0.00195312 ,-0.00018311 ,-0.00195312 ,-0.00012207 ,-0.00158691 , -0.00140381 ,-0.00112915 ,-0.00149536 ,-0.00146484 ,-0.00112915 , -0.00271606 ,-0.00027466 ,-0.00274658 ,0.00018311 ,-0.00186157 , 0.00018311 ,-0.00186157 ,-0.00024414 ,-0.00234985 ,-0.00061035 , -0.00241089 ,-0.00057983 ,-0.00241089 ,-0.00061035 ,-0.00192261 , -0.00109863 ,-0.00097656 ,-0.00161743 ,-0.00137329 ,-0.00207520 , -0.00180054 ,-0.00161743 ,-0.00183105 ,-0.00204468 ,-0.00274658 , -0.00106812 ,-0.00283813 ,-0.00146484 ,-0.00152588 ,-0.00238037 , -0.00149536 ,-0.00238037 ,-0.00146484 ,-0.00195312 ,-0.00238037 , -0.00152588 ,-0.00280762 ,-0.00103760 ,-0.00195312 ,-0.00234985 , -0.00067139 ,-0.00366211 ,-0.00064087 ,-0.00366211 ,-0.00109863 , -0.00366211 ,-0.00152588 ,-0.00366211 ,-0.00241089 ,-0.00234985 , -0.00329590 ,-0.00143433 ,-0.00375366 ,-0.00100708 ,-0.00372314 , -0.00103760 ,-0.00375366 ,-0.00097656 ,-0.00421143 ,-0.00100708 , -0.00411987 ,-0.00201416 ,-0.00314331 ,-0.00381470 ,-0.00186157 , -0.00372314 ,-0.00192261 ,-0.00326538 ,-0.00280762 ,-0.00283813 , -0.00323486 ,-0.00283813 ,-0.00369263 ,-0.00280762 ,-0.00326538 , -0.00326538 ,-0.00234985 ,-0.00286865 ,-0.00231934 ,-0.00375366 , -0.00231934 ,-0.00375366 ,-0.00277710 ,-0.00372314 ,-0.00277710 , -0.00372314 ,-0.00323486 ,-0.00286865 ,-0.00360107 ,-0.00207520 , -0.00268555 ,-0.00421143 ,-0.00189209 ,-0.00418091 ,-0.00323486 , -0.00283813 ,-0.00451660 ,-0.00155640 ,-0.00454712 ,-0.00241089 , -0.00320435 ,-0.00418091 ,-0.00189209 ,-0.00506592 ,-0.00192261 , -0.00411987 ,-0.00195312 ,-0.00411987 ,-0.00238037 ,-0.00415039 , -0.00283813 ,-0.00274658 ,-0.00378418 ,-0.00271606 ,-0.00378418 , -0.00317383 ,-0.00289917 ,-0.00451660 ,-0.00238037 ,-0.00460815 , -0.00186157 ,-0.00421143 ,-0.00320435 ,-0.00283813 ,-0.00369263 , -0.00323486 ,-0.00329590 ,-0.00451660 ,-0.00286865 ,-0.00454712 , -0.00369263 ,-0.00418091 ,-0.00405884 ,-0.00289917 ,-0.00448608 , -0.00335693 ,-0.00448608 ,-0.00332642 ,-0.00451660 ,-0.00286865 , -0.00497437 ,-0.00283813 ,-0.00415039 ,-0.00405884 ,-0.00381470 , -0.00531006 ,-0.00338745 ,-0.00491333 ,-0.00421143 ,-0.00320435 , -0.00500488 ,-0.00286865 ,-0.00448608 ,-0.00424194 ,-0.00317383 , -0.00592041 ,-0.00323486 ,-0.00503540 ,-0.00369263 ,-0.00454712 , -0.00329590 ,-0.00451660 ,-0.00375366 ,-0.00411987 ,-0.00540161 , -0.00332642 ,-0.00540161 ,-0.00418091 ,-0.00497437 ,-0.00415039 , -0.00500488 ,-0.00415039 ,-0.00500488 ,-0.00415039 ,-0.00451660 , -0.00421143 ,-0.00494385 ,-0.00421143 ,-0.00579834 ,-0.00378418 , -0.00579834 ,-0.00421143 ,-0.00543213 ,-0.00408936 ,-0.00509644 , -0.00363159 ,-0.00503540 ,-0.00457764 ,-0.00451660 ,-0.00552368 , -0.00366211 ,-0.00634766 ,-0.00366211 ,-0.00680542 ,-0.00320435 , -0.00729370 ,-0.00317383 ,-0.00637817 ,-0.00411987 ,-0.00500488 , -0.00503540 ,-0.00543213 ,-0.00503540 ,-0.00585938 ,-0.00418091 , -0.00540161 ,-0.00503540 ,-0.00543213 ,-0.00503540 ,-0.00500488 , -0.00546265 ,-0.00543213 ,-0.00543213 ,-0.00506592 ,-0.00448608 , -0.00555420 ,-0.00360107 ,-0.00640869 ,-0.00320435 ,-0.00680542 , -0.00366211 ,-0.00634766 ,-0.00415039 ,-0.00497437 ,-0.00549316 , -0.00411987 ,-0.00631714 ,-0.00460815 ,-0.00585938 ,-0.00546265 , -0.00546265 ,-0.00540161 ,-0.00466919 ,-0.00576782 ,-0.00427246 , -0.00619507 ,-0.00463867 ,-0.00546265 ,-0.00582886 ,-0.00509644 , -0.00625610 ,-0.00503540 ,-0.00634766 ,-0.00408936 ,-0.00726318 , -0.00366211 ,-0.00677490 ,-0.00457764 ,-0.00588989 ,-0.00588989 , -0.00503540 ,-0.00631714 ,-0.00500488 ,-0.00634766 ,-0.00500488 , -0.00585938 ,-0.00466919 ,-0.00576782 ,-0.00469971 ,-0.00576782 , -0.00509644 ,-0.00540161 ,-0.00634766 ,-0.00503540 ,-0.00674438 , -0.00503540 ,-0.00585938 ,-0.00546265 ,-0.00588989 ,-0.00457764 , -0.00634766 ,-0.00540161 ,-0.00595093 ,-0.00628662 ,-0.00546265 , -0.00546265 ,-0.00674438 ,-0.00415039 ,-0.00762939 ,-0.00549316 , -0.00625610 ,-0.00598145 ,-0.00622559 ,-0.00598145 ,-0.00582886 , -0.00592041 ,-0.00543213 ,-0.00634766 ,-0.00588989 ,-0.00588989 , -0.00543213 ,-0.00634766 ,-0.00546265 ,-0.00585938 ,-0.00680542 , -0.00497437 ,-0.00765991 ,-0.00503540 ,-0.00762939 ,-0.00585938 , -0.00640869 ,-0.00576782 ,-0.00689697 ,-0.00579834 ,-0.00592041 , -0.00720215 ,-0.00457764 ,-0.00808716 ,-0.00500488 ,-0.00677490 , -0.00674438 ,-0.00592041 ,-0.00677490 ,-0.00585938 ,-0.00680542 , -0.00543213 ,-0.00631714 ,-0.00595093 ,-0.00668335 ,-0.00595093 , -0.00805664 ,-0.00503540 ,-0.00808716 ,-0.00543213 ,-0.00808716 , -0.00546265 ,-0.00805664 ,-0.00552368 ,-0.00756836 ,-0.00549316 , -0.00762939 ,-0.00634766 ,-0.00762939 ,-0.00637817 ,-0.00711060 , -0.00601196 ,-0.00750732 ,-0.00646973 ,-0.00668335 ,-0.00634766 , -0.00637817 ,-0.00668335 ,-0.00595093 ,-0.00720215 ,-0.00628662 , -0.00640869 ,-0.00668335 ,-0.00683594 ,-0.00585938 ,-0.00808716 , -0.00503540 ,-0.00717163 ,-0.00637817 ,-0.00585938 ,-0.00677490 , -0.00588989 ,-0.00634766 ,-0.00759888 ,-0.00595093 ,-0.00762939 , -0.00631714 ,-0.00765991 ,-0.00588989 ,-0.00762939 ,-0.00592041 , -0.00720215 ,-0.00674438 ,-0.00634766 ,-0.00720215 ,-0.00585938 , -0.00683594 ,-0.00622559 ,-0.00646973 ,-0.00659180 ,-0.00692749 , -0.00622559 ,-0.00723267 ,-0.00592041 ,-0.00717163 ,-0.00634766 , -0.00677490 ,-0.00674438 ,-0.00720215 ,-0.00726318 ,-0.00579834 , -0.00817871 ,-0.00540161 ,-0.00851440 ,-0.00552368 ,-0.00753784 , -0.00689697 ,-0.00619507 ,-0.00820923 ,-0.00582886 ,-0.00851440 , -0.00640869 ,-0.00753784 ,-0.00598145 ,-0.00717163 ,-0.00585938 , -0.00683594 ,-0.00582886 ,-0.00683594 ,-0.00625610 ,-0.00726318 , -0.00625610 ,-0.00640869 ,-0.00671387 ,-0.00637817 ,-0.00671387 , -0.00640869 ,-0.00534058 ,-0.00732422 ,-0.00491333 ,-0.00772095 , -0.00585938 ,-0.00634766 ,-0.00674438 ,-0.00637817 ,-0.00582886 , -0.00726318 ,-0.00537109 ,-0.00686646 ,-0.00668335 ,-0.00595093 , -0.00631714 ,-0.00671387 ,-0.00598145 ,-0.00668335 ,-0.00592041 , -0.00637817 ,-0.00625610 ,-0.00683594 ,-0.00628662 ,-0.00634766 , -0.00720215 ,-0.00500488 ,-0.00723267 ,-0.00588989 ,-0.00631714 , -0.00720215 ,-0.00588989 ,-0.00723267 ,-0.00585938 ,-0.00726318 , -0.00491333 ,-0.00732422 ,-0.00448608 ,-0.00726318 ,-0.00454712 , -0.00720215 ,-0.00457764 ,-0.00677490 ,-0.00500488 ,-0.00677490 , -0.00543213 ,-0.00680542 ,-0.00540161 ,-0.00595093 ,-0.00625610 , -0.00506592 ,-0.00720215 ,-0.00454712 ,-0.00680542 ,-0.00500488 , -0.00585938 ,-0.00592041 ,-0.00457764 ,-0.00714111 ,-0.00381470 , -0.00708008 ,-0.00421143 ,-0.00674438 ,-0.00369263 ,-0.00720215 , -0.00415039 ,-0.00628662 ,-0.00595093 ,-0.00408936 ,-0.00634766 , -0.00457764 ,-0.00497437 ,-0.00598145 ,-0.00399780 ,-0.00646973 , -0.00491333 ,-0.00595093 ,-0.00543213 ,-0.00497437 ,-0.00595093 , -0.00497437 ,-0.00546265 ,-0.00503540 ,-0.00540161 ,-0.00503540 , -0.00549316 ,-0.00448608 ,-0.00555420 ,-0.00448608 ,-0.00460815 , -0.00588989 ,-0.00369263 ,-0.00588989 ,-0.00411987 ,-0.00546265 , -0.00457764 ,-0.00540161 ,-0.00378418 ,-0.00625610 ,-0.00329590 , -0.00497437 ,-0.00503540 ,-0.00411987 ,-0.00506592 ,-0.00491333 , -0.00378418 ,-0.00628662 ,-0.00326538 ,-0.00631714 ,-0.00372314 , -0.00540161 ,-0.00378418 ,-0.00534058 ,-0.00335693 ,-0.00582886 , -0.00283813 ,-0.00588989 ,-0.00326538 ,-0.00543213 ,-0.00375366 , -0.00491333 ,-0.00466919 ,-0.00405884 ,-0.00463867 ,-0.00366211 , -0.00457764 ,-0.00369263 ,-0.00500488 ,-0.00415039 ,-0.00411987 , -0.00546265 ,-0.00280762 ,-0.00503540 ,-0.00320435 ,-0.00418091 , -0.00454712 ,-0.00283813 ,-0.00543213 ,-0.00241089 ,-0.00540161 , -0.00241089 ,-0.00546265 ,-0.00274658 ,-0.00469971 ,-0.00268555 , -0.00466919 ,-0.00320435 ,-0.00457764 ,-0.00369263 ,-0.00415039 , -0.00411987 ,-0.00369263 ,-0.00369263 ,-0.00411987 ,-0.00372314 , -0.00411987 ,-0.00415039 ,-0.00277710 ,-0.00552368 ,-0.00228882 , -0.00509644 ,-0.00228882 ,-0.00506592 ,-0.00238037 ,-0.00454712 , -0.00195312 ,-0.00411987 ,-0.00323486 ,-0.00332642 ,-0.00314331 , -0.00378418 ,-0.00320435 ,-0.00418091 ,-0.00277710 ,-0.00460815 , -0.00274658 ,-0.00463867 ,-0.00234985 ,-0.00415039 ,-0.00238037 , -0.00494385 ,-0.00204468 ,-0.00402832 ,-0.00247192 ,-0.00366211 , -0.00320435 ,-0.00335693 ,-0.00314331 ,-0.00292969 ,-0.00402832 , -0.00158691 ,-0.00405884 ,-0.00244141 ,-0.00277710 ,-0.00372314 , -0.00146484 ,-0.00457764 ,-0.00195312 ,-0.00369263 ,-0.00326538 , -0.00192261 ,-0.00457764 ,-0.00103760 ,-0.00421143 ,-0.00225830 , -0.00292969 ,-0.00317383 ,-0.00329590 ,-0.00195312 ,-0.00363159 , -0.00244141 ,-0.00231934 ,-0.00375366 ,-0.00189209 ,-0.00329590 , -0.00320435 ,-0.00198364 ,-0.00366211 ,-0.00238037 ,-0.00329590 , -0.00231934 ,-0.00286865 ,-0.00363159 ,-0.00155640 ,-0.00408936 , -0.00155640 ,-0.00363159 ,-0.00241089 ,-0.00280762 ,-0.00192261 , -0.00241089 ,-0.00234985 ,-0.00195312 ,-0.00280762 ,-0.00146484 , -0.00418091 ,-0.00100708 ,-0.00378418 ,-0.00094604 ,-0.00289917 , -0.00186157 ,-0.00244141 ,-0.00149536 ,-0.00234985 ,-0.00149536 , -0.00283813 ,-0.00146484 ,-0.00244141 ,-0.00143433 ,-0.00198364 , -0.00189209 ,-0.00241089 ,-0.00149536 ,-0.00192261 ,-0.00106812 , -0.00234985 ,-0.00109863 ,-0.00280762 ,0.00027466 ,-0.00415039 , 0.00073242 ,-0.00375366 ,-0.00103760 ,-0.00146484 ,-0.00201416 , -0.00097656 ,-0.00201416 ,-0.00186157 ,-0.00109863 ,-0.00283813 , -0.00103760 ,-0.00195312 ,-0.00100708 ,-0.00198364 ,-0.00103760 , -0.00198364 ,-0.00012207 ,-0.00201416 ,-0.00006104 ,-0.00204468 , -0.00012207 ,-0.00198364 ,-0.00061035 ,-0.00149536 ,-0.00057983 , -0.00112915 ,-0.00054932 ,-0.00112915 ,-0.00057983 ,-0.00195312 , 0.00070190 ,-0.00195312 ,0.00030518 ,-0.00155640 ,-0.00100708 , -0.00067139 ,-0.00015259 ,-0.00149536 ,0.00027466 ,-0.00152588 , 0.00027466 ,-0.00018311 ,-0.00106812 ,0.00073242 ,-0.00155640 , -0.00009155 ,-0.00027466 ,-0.00054932 ,-0.00067139 ,0.00027466 , -0.00106812 ,-0.00057983 ,0.00021362 ,-0.00103760 ,-0.00018311 , -0.00021362 ,-0.00057983 ,-0.00064087 ,-0.00018311 ,-0.00015259 , -0.00112915 ,0.00033569 ,-0.00070190 ,-0.00009155 ,0.00018311 , -0.00100708 ,0.00064087 ,-0.00015259 ,-0.00015259 ,0.00064087 , -0.00097656 ,0.00103760 ,-0.00054932 ,0.00067139 ,-0.00061035 , -0.00018311 ,0.00024414 ,-0.00015259 ,-0.00067139 ,0.00030518 , -0.00064087 ,0.00070190 ,-0.00018311 ,0.00027466 ,-0.00067139 , 0.00164795 ,-0.00198364 ,0.00244141 ,-0.00143433 ,0.00195312 , -0.00057983 ,0.00070190 ,0.00021362 ,0.00030518 ,0.00109863 , -0.00015259 ,0.00109863 ,0.00030518 ,0.00109863 ,0.00027466 , 0.00027466 ,0.00064087 ,-0.00012207 ,0.00155640 ,-0.00064087 , 0.00161743 ,-0.00067139 ,0.00115967 ,0.00030518 ,0.00106812 , -0.00015259 ,0.00112915 ,0.00024414 ,0.00161743 ,-0.00021362 , 0.00158691 ,-0.00106812 ,0.00201416 ,-0.00106812 ,0.00204468 , -0.00106812 ,0.00286865 ,-0.00103760 ,0.00241089 ,-0.00057983 , 0.00247192 ,-0.00024414 ,0.00207520 ,-0.00024414 ,0.00119019 , 0.00067139 ,0.00115967 ,0.00064087 ,0.00119019 ,0.00109863 , 0.00119019 ,0.00238037 ,-0.00054932 ,0.00326538 ,-0.00015259 , 0.00201416 ,0.00286865 ,-0.00057983 ,0.00418091 ,-0.00061035 , 0.00289917 ,0.00067139 ,0.00204468 ,0.00067139 ,0.00247192 , 0.00027466 ,0.00198364 ,0.00158691 ,0.00067139 ,0.00250244 , 0.00064087 ,0.00164795 ,0.00149536 ,0.00119019 ,0.00244141 , 0.00064087 ,0.00256348 ,0.00057983 ,0.00210571 ,0.00106812 , 0.00207520 ,0.00106812 ,0.00210571 ,0.00146484 ,0.00125122 , 0.00192261 ,0.00164795 ,0.00198364 ,0.00201416 ,0.00161743 , 0.00195312 ,0.00164795 ,0.00241089 ,0.00112915 ,0.00253296 , 0.00146484 ,0.00167847 ,0.00195312 ,0.00112915 ,0.00253296 , 0.00061035 ,0.00296021 ,0.00021362 ,0.00332642 ,0.00115967 , 0.00158691 ,0.00286865 ,0.00070190 ,0.00289917 ,0.00155640 , 0.00164795 ,0.00234985 ,0.00164795 ,0.00244141 ,0.00155640 , 0.00250244 ,0.00195312 ,0.00204468 ,0.00247192 ,0.00198364 , 0.00247192 ,0.00198364 ,0.00292969 ,0.00155640 ,0.00332642 , 0.00161743 ,0.00238037 ,0.00207520 ,0.00155640 ,0.00244141 , 0.00204468 ,0.00201416 ,0.00195312 ,0.00256348 ,0.00103760 , 0.00381470 ,0.00027466 ,0.00372314 ,0.00119019 ,0.00241089 , 0.00289917 ,0.00070190 ,0.00466919 ,0.00024414 ,0.00375366 , 0.00158691 ,0.00244141 ,0.00292969 ,0.00198364 ,0.00289917 , 0.00201416 ,0.00244141 ,0.00292969 ,0.00195312 ,0.00338745 , 0.00198364 ,0.00247192 ,0.00289917 ,0.00195312 ,0.00299072 , 0.00238037 ,0.00292969 ,0.00201416 ,0.00329590 ,0.00204468 , 0.00289917 ,0.00244141 ,0.00289917 ,0.00289917 ,0.00286865 , 0.00332642 ,0.00207520 ,0.00369263 ,0.00164795 ,0.00326538 , 0.00292969 ,0.00289917 ,0.00283813 ,0.00299072 ,0.00189209 , 0.00433350 ,0.00189209 ,0.00302124 ,0.00320435 ,0.00213623 , 0.00323486 ,0.00250244 ,0.00289917 ,0.00283813 ,0.00384521 , 0.00195312 ,0.00427246 ,0.00238037 ,0.00381470 ,0.00289917 , 0.00286865 ,0.00338745 ,0.00283813 ,0.00335693 ,0.00286865 , 0.00381470 ,0.00195312 ,0.00473022 ,0.00195312 ,0.00466919 , 0.00244141 ,0.00421143 ,0.00247192 ,0.00418091 ,0.00161743 , 0.00418091 ,0.00201416 ,0.00335693 ,0.00283813 ,0.00296021 , 0.00241089 ,0.00335693 ,0.00198364 ,0.00335693 ,0.00332642 , 0.00244141 ,0.00466919 ,0.00195312 ,0.00518799 ,0.00238037 , 0.00381470 ,0.00372314 ,0.00207520 ,0.00454712 ,0.00216675 , 0.00317383 ,0.00347900 ,0.00320435 ,0.00344849 ,0.00366211 , 0.00302124 ,0.00405884 ,0.00213623 ,0.00457764 ,0.00207520 , 0.00415039 ,0.00296021 ,0.00369263 ,0.00338745 ,0.00378418 , 0.00280762 ,0.00433350 ,0.00280762 ,0.00424194 ,0.00335693 , 0.00415039 ,0.00384521 ,0.00415039 ,0.00338745 ,0.00460815 , 0.00247192 ,0.00506592 ,0.00250244 ,0.00546265 ,0.00161743 , 0.00595093 ,0.00201416 ,0.00463867 ,0.00289917 ,0.00375366 , 0.00381470 ,0.00283813 ,0.00381470 ,0.00372314 ,0.00384521 , 0.00415039 ,0.00247192 ,0.00375366 ,0.00335693 ,0.00332642 , 0.00289917 ,0.00332642 ,0.00289917 ,0.00415039 ,0.00341797 , 0.00326538 ,0.00469971 ,0.00198364 ,0.00552368 ,0.00247192 , 0.00463867 ,0.00332642 ,0.00335693 ,0.00418091 ,0.00292969 , 0.00418091 ,0.00201416 ,0.00512695 ,0.00283813 ,0.00515747 , 0.00238037 ,0.00466919 ,0.00378418 ,0.00289917 ,0.00549316 , 0.00164795 ,0.00540161 ,0.00347900 ,0.00317383 ,0.00479126 , 0.00277710 ,0.00430298 ,0.00372314 ,0.00378418 ,0.00421143 , 0.00372314 ,0.00469971 ,0.00375366 ,0.00378418 ,0.00332642 , 0.00375366 ,0.00335693 ,0.00372314 ,0.00338745 ,0.00372314 , 0.00381470 ,0.00421143 ,0.00283813 ,0.00427246 ,0.00326538 , 0.00296021 ,0.00503540 ,0.00164795 ,0.00500488 ,0.00207520 , 0.00460815 ,0.00204468 ,0.00463867 ,0.00201416 ,0.00421143 , 0.00332642 ,0.00289917 ,0.00418091 ,0.00338745 ,0.00369263 , 0.00299072 ,0.00411987 ,0.00292969 ,0.00466919 ,0.00241089 , 0.00473022 ,0.00234985 ,0.00430298 ,0.00369263 ,0.00296021 , 0.00460815 ,0.00204468 ,0.00375366 ,0.00421143 ,0.00244141 , 0.00512695 ,0.00238037 ,0.00518799 ,0.00277710 ,0.00518799 , 0.00283813 ,0.00466919 ,0.00289917 ,0.00375366 ,0.00375366 , 0.00296021 ,0.00411987 ,0.00253296 ,0.00375366 ,0.00326538 , 0.00299072 ,0.00411987 ,0.00207520 ,0.00460815 ,0.00247192 , 0.00332642 ,0.00335693 ,0.00283813 ,0.00341797 ,0.00363159 , 0.00262451 ,0.00408936 ,0.00253296 ,0.00329590 ,0.00332642 , 0.00332642 ,0.00335693 ,0.00329590 ,0.00296021 ,0.00326538 , 0.00296021 ,0.00323486 ,0.00344849 ,0.00231934 ,0.00347900 , 0.00320435 ,0.00256348 ,0.00415039 ,0.00244141 ,0.00296021 , 0.00366211 ,0.00170898 ,0.00408936 ,0.00256348 ,0.00280762 , 0.00338745 ,0.00286865 ,0.00201416 ,0.00466919 ,0.00024414 , 0.00463867 ,0.00207520 ,0.00280762 ,0.00296021 ,0.00241089 , 0.00292969 ,0.00244141 ,0.00289917 ,0.00198364 ,0.00296021 , 0.00195312 ,0.00338745 ,0.00152588 ,0.00332642 ,0.00207520 , 0.00323486 ,0.00128174 ,0.00360107 ,0.00128174 ,0.00366211 , 0.00079346 ,0.00369263 ,0.00030518 ,0.00460815 ,0.00027466 , 0.00424194 ,0.00109863 ,0.00292969 ,0.00149536 ,0.00296021 , 0.00198364 ,0.00292969 ,0.00195312 ,0.00161743 ,0.00329590 , 0.00073242 ,0.00421143 ,0.00109863 ,0.00335693 ,0.00201416 , 0.00198364 ,0.00210571 ,0.00234985 ,0.00207520 ,0.00201416 , 0.00198364 ,0.00158691 ,0.00247192 ,0.00198364 ,0.00204468 , 0.00201416 ,0.00155640 ,0.00201416 ,0.00158691 ,0.00201416 , 0.00201416 ,0.00115967 ,0.00241089 ,0.00030518 ,0.00241089 , 0.00119019 ,0.00106812 ,0.00161743 ,0.00158691 ,0.00152588 , 0.00253296 ,0.00057983 ,0.00299072 ,-0.00064087 ,0.00329590 , -0.00012207 ,0.00283813 ,0.00027466 ,0.00250244 ,-0.00027466 , 0.00299072 ,0.00061035 ,0.00207520 ,0.00112915 ,0.00155640 , 0.00158691 ,0.00112915 ,0.00201416 ,0.00027466 ,0.00244141 , 0.00112915 ,0.00115967 ,0.00155640 ,0.00070190 ,0.00155640 , 0.00115967 ,0.00109863 ,0.00210571 ,-0.00030518 ,0.00256348 , -0.00027466 ,0.00119019 ,0.00158691 ,-0.00021362 ,0.00204468 , 0.00021362 ,0.00164795 ,0.00018311 ,0.00207520 ,0.00021362 , 0.00115967 ,0.00073242 ,0.00064087 ,0.00119019 ,0.00106812 , 0.00033569 ,0.00106812 ,0.00119019 ,0.00067139 ,0.00070190 , 0.00070190 ,0.00024414 ,0.00073242 ,0.00021362 ,0.00030518 , 0.00064087 ,0.00030518 ,0.00067139 ,-0.00015259 ,0.00067139 , 0.00067139 ,0.00033569 ,0.00103760 ,-0.00009155 ,0.00061035 , 0.00033569 ,0.00061035 ,0.00079346 ,-0.00024414 ,0.00112915 , -0.00054932 ,0.00057983 ,-0.00006104 ,0.00061035 ,0.00030518 , -0.00021362 ,0.00073242 ,-0.00064087 ,0.00027466 ,0.00024414 , -0.00106812 ,0.00115967 ,-0.00103760 ,0.00021362 ,-0.00012207 , -0.00070190 ,0.00079346 ,-0.00112915 ,0.00073242 ,-0.00106812 , -0.00018311 ,0.00024414 ,-0.00189209 ,0.00149536 ,-0.00228882 , 0.00106812 ,-0.00103760 ,-0.00018311 ,0.00070190 ,-0.00106812 , 0.00070190 ,-0.00146484 ,0.00018311 ,-0.00051880 ,-0.00076294 , -0.00088501 ,-0.00033569 ,-0.00137329 ,0.00057983 ,-0.00140381 , -0.00070190 ,0.00030518 ,-0.00195312 ,0.00067139 ,-0.00186157 , 0.00015259 ,-0.00140381 ,-0.00027466 ,-0.00140381 ,-0.00070190 , -0.00100708 ,-0.00106812 ,-0.00061035 ,-0.00061035 ,-0.00109863 , -0.00015259 ,-0.00152588 ,-0.00012207 ,-0.00109863 ,-0.00057983 , -0.00109863 ,-0.00061035 ,-0.00146484 ,-0.00024414 ,-0.00143433 , -0.00021362 ,-0.00152588 ,-0.00012207 ,-0.00198364 ,0.00030518 , -0.00286865 ,0.00027466 ,-0.00238037 ,-0.00064087 ,-0.00054932 , -0.00244141 ,0.00115967 ,-0.00326538 ,0.00024414 ,-0.00234985 , -0.00021362 ,-0.00274658 ,-0.00030518 ,-0.00265503 ,-0.00076294 , -0.00228882 ,-0.00112915 ,-0.00228882 ,-0.00161743 ,-0.00091553 , -0.00207520 ,-0.00140381 ,-0.00109863 ,-0.00241089 ,-0.00097656 , -0.00204468 ,-0.00180054 ,-0.00207520 ,-0.00143433 ,-0.00277710 , -0.00030518 ,-0.00265503 ,-0.00076294 ,-0.00228882 ,-0.00155640 , -0.00146484 ,-0.00238037 ,-0.00061035 ,-0.00372314 ,-0.00061035 , -0.00195312 ,-0.00277710 ,-0.00106812 ,-0.00283813 ,-0.00238037 , -0.00103760 ,-0.00283813 ,-0.00146484 ,-0.00241089 ,-0.00238037 , -0.00146484 ,-0.00286865 ,-0.00143433 ,-0.00283813 ,-0.00155640 , -0.00271606 ,-0.00292969 ,-0.00140381 ,-0.00372314 ,-0.00152588 , -0.00320435 ,-0.00201416 ,-0.00314331 ,-0.00161743 ,-0.00402832 , -0.00155640 ,-0.00326538 ,-0.00234985 ,-0.00241089 ,-0.00320435 , -0.00195312 ,-0.00372314 ,-0.00103760 ,-0.00372314 ,-0.00189209 , -0.00375366 ,-0.00231934 ,-0.00286865 ,-0.00317383 ,-0.00247192 , -0.00317383 ,-0.00247192 ,-0.00314331 ,-0.00292969 ,-0.00317383 , -0.00283813 ,-0.00457764 ,-0.00149536 ,-0.00415039 ,-0.00280762 , -0.00234985 ,-0.00546265 ,-0.00067139 ,-0.00534058 ,-0.00207520 , -0.00357056 ,-0.00378418 ,-0.00231934 ,-0.00418091 ,-0.00320435 , -0.00332642 ,-0.00363159 ,-0.00238037 ,-0.00418091 ,-0.00234985 , -0.00369263 ,-0.00326538 ,-0.00277710 ,-0.00460815 ,-0.00238037 , -0.00454712 ,-0.00286865 ,-0.00405884 ,-0.00372314 ,-0.00415039 , -0.00280762 ,-0.00411987 ,-0.00241089 ,-0.00451660 ,-0.00241089 , -0.00418091 ,-0.00268555 ,-0.00473022 ,-0.00265503 ,-0.00515747 , -0.00228882 ,-0.00457764 ,-0.00283813 ,-0.00500488 ,-0.00238037 , -0.00457764 ,-0.00280762 ,-0.00366211 ,-0.00375366 ,-0.00320435 , -0.00375366 ,-0.00363159 ,-0.00421143 ,-0.00402832 ,-0.00335693 , -0.00451660 ,-0.00329590 ,-0.00497437 ,-0.00326538 ,-0.00369263 , -0.00415039 ,-0.00369263 ,-0.00411987 ,-0.00411987 ,-0.00457764 , -0.00415039 ,-0.00411987 ,-0.00415039 ,-0.00411987 ,-0.00411987 , -0.00415039 ,-0.00411987 ,-0.00503540 ,-0.00369263 ,-0.00546265 , -0.00363159 ,-0.00466919 ,-0.00491333 ,-0.00378418 ,-0.00540161 , -0.00372314 ,-0.00500488 ,-0.00411987 ,-0.00457764 ,-0.00457764 , -0.00369263 ,-0.00503540 ,-0.00408936 ,-0.00460815 ,-0.00451660 , -0.00421143 ,-0.00540161 ,-0.00372314 ,-0.00585938 ,-0.00238037 , -0.00677490 ,-0.00238037 ,-0.00680542 ,-0.00274658 ,-0.00598145 , -0.00314331 ,-0.00598145 ,-0.00363159 ,-0.00506592 ,-0.00497437 , -0.00329590 ,-0.00625610 ,-0.00378418 ,-0.00537109 ,-0.00506592 , -0.00369263 ,-0.00628662 ,-0.00421143 ,-0.00579834 ,-0.00378418 , -0.00579834 ,-0.00424194 ,-0.00622559 ,-0.00463867 ,-0.00497437 , -0.00543213 ,-0.00552368 ,-0.00491333 ,-0.00509644 ,-0.00582886 , -0.00415039 ,-0.00680542 ,-0.00408936 ,-0.00592041 ,-0.00497437 , -0.00592041 ,-0.00500488 ,-0.00588989 ,-0.00457764 ,-0.00631714 , -0.00457764 ,-0.00592041 ,-0.00537109 ,-0.00598145 ,-0.00537109 , -0.00509644 ,-0.00537109 ,-0.00463867 ,-0.00628662 ,-0.00415039 , -0.00631714 ,-0.00415039 ,-0.00631714 ,-0.00415039 ,-0.00631714 , -0.00454712 ,-0.00595093 ,-0.00491333 ,-0.00643921 ,-0.00491333 , -0.00555420 ,-0.00534058 ,-0.00512695 ,-0.00531006 ,-0.00558472 , -0.00448608 ,-0.00680542 ,-0.00457764 ,-0.00674438 ,-0.00415039 , -0.00677490 ,-0.00454712 ,-0.00634766 ,-0.00543213 ,-0.00460815 , -0.00717163 ,-0.00460815 ,-0.00671387 ,-0.00463867 ,-0.00674438 , -0.00454712 ,-0.00723267 ,-0.00500488 ,-0.00543213 ,-0.00595093 , -0.00494385 ,-0.00634766 ,-0.00549316 ,-0.00537109 ,-0.00552368 , -0.00582886 ,-0.00595093 ,-0.00540161 ,-0.00634766 ,-0.00546265 , -0.00585938 ,-0.00592041 ,-0.00546265 ,-0.00671387 ,-0.00463867 , -0.00756836 ,-0.00466919 ,-0.00671387 ,-0.00546265 ,-0.00546265 , -0.00582886 ,-0.00509644 ,-0.00628662 ,-0.00546265 ,-0.00585938 , -0.00549316 ,-0.00540161 ,-0.00640869 ,-0.00582886 ,-0.00546265 , -0.00720215 ,-0.00411987 ,-0.00772095 ,-0.00408936 ,-0.00720215 , -0.00503540 ,-0.00582886 ,-0.00595093 ,-0.00588989 ,-0.00585938 , -0.00637817 ,-0.00582886 ,-0.00592041 ,-0.00634766 ,-0.00500488 , -0.00762939 ,-0.00457764 ,-0.00765991 ,-0.00500488 ,-0.00680542 , -0.00540161 ,-0.00720215 ,-0.00506592 ,-0.00668335 ,-0.00558472 , -0.00573730 ,-0.00689697 ,-0.00534058 ,-0.00686646 ,-0.00494385 , -0.00769043 ,-0.00411987 ,-0.00808716 ,-0.00369263 ,-0.00811768 , -0.00451660 ,-0.00726318 ,-0.00543213 ,-0.00588989 ,-0.00592041 , -0.00628662 ,-0.00549316 ,-0.00671387 ,-0.00552368 ,-0.00625610 , -0.00595093 ,-0.00628662 ,-0.00634766 ,-0.00592041 ,-0.00625610 , -0.00552368 ,-0.00671387 ,-0.00592041 ,-0.00588989 ,-0.00588989 , -0.00631714 ,-0.00549316 ,-0.00668335 ,-0.00555420 ,-0.00494385 , -0.00769043 ,-0.00451660 ,-0.00637817 ,-0.00585938 ,-0.00549316 , -0.00671387 ,-0.00595093 ,-0.00579834 ,-0.00555420 ,-0.00625610 , -0.00549316 ,-0.00631714 ,-0.00543213 ,-0.00592041 ,-0.00543213 , -0.00634766 ,-0.00540161 ,-0.00598145 ,-0.00619507 ,-0.00561523 , -0.00573730 ,-0.00686646 ,-0.00454712 ,-0.00759888 ,-0.00378418 , -0.00668335 ,-0.00552368 ,-0.00585938 ,-0.00543213 ,-0.00506592 , -0.00582886 ,-0.00595093 ,-0.00543213 ,-0.00585938 ,-0.00552368 , -0.00534058 ,-0.00689697 ,-0.00488281 ,-0.00643921 ,-0.00491333 , -0.00637817 ,-0.00503540 ,-0.00717163 ,-0.00460815 ,-0.00671387 , -0.00460815 ,-0.00674438 ,-0.00509644 ,-0.00576782 ,-0.00598145 , -0.00540161 ,-0.00549316 ,-0.00631714 ,-0.00454712 ,-0.00631714 , -0.00506592 ,-0.00582886 ,-0.00512695 ,-0.00619507 ,-0.00466919 , -0.00625610 ,-0.00506592 ,-0.00497437 ,-0.00637817 ,-0.00411987 , -0.00631714 ,-0.00500488 ,-0.00546265 ,-0.00500488 ,-0.00634766 , -0.00369263 ,-0.00674438 ,-0.00418091 ,-0.00497437 ,-0.00588989 , -0.00372314 ,-0.00625610 ,-0.00378418 ,-0.00585938 ,-0.00369263 , -0.00631714 ,-0.00372314 ,-0.00537109 ,-0.00469971 ,-0.00445557 , -0.00552368 ,-0.00411987 ,-0.00454712 ,-0.00549316 ,-0.00363159 , -0.00595093 ,-0.00411987 ,-0.00500488 ,-0.00457764 ,-0.00500488 , -0.00457764 ,-0.00546265 ,-0.00411987 ,-0.00500488 ,-0.00460815 , -0.00408936 ,-0.00546265 ,-0.00460815 ,-0.00402832 ,-0.00604248 , -0.00357056 ,-0.00595093 ,-0.00323486 ,-0.00543213 ,-0.00460815 , -0.00454712 ,-0.00460815 ,-0.00408936 ,-0.00375366 ,-0.00582886 , -0.00241089 ,-0.00677490 ,-0.00234985 ,-0.00549316 ,-0.00366211 , -0.00411987 ,-0.00460815 ,-0.00454712 ,-0.00460815 ,-0.00451660 , -0.00421143 ,-0.00448608 ,-0.00421143 ,-0.00451660 ,-0.00372314 , -0.00503540 ,-0.00320435 ,-0.00460815 ,-0.00500488 ,-0.00238037 , -0.00631714 ,-0.00195312 ,-0.00628662 ,-0.00244141 ,-0.00585938 , -0.00326538 ,-0.00454712 ,-0.00415039 ,-0.00415039 ,-0.00323486 , -0.00503540 ,-0.00234985 ,-0.00546265 ,-0.00283813 ,-0.00454712 , -0.00460815 ,-0.00320435 ,-0.00595093 ,-0.00231934 ,-0.00677490 , -0.00201416 ,-0.00579834 ,-0.00332642 ,-0.00408936 ,-0.00500488 , -0.00286865 ,-0.00494385 ,-0.00289917 ,-0.00448608 ,-0.00335693 , -0.00357056 ,-0.00469971 ,-0.00271606 ,-0.00418091 ,-0.00369263 , -0.00408936 ,-0.00286865 ,-0.00546265 ,-0.00143433 ,-0.00640869 , -0.00186157 ,-0.00503540 ,-0.00241089 ,-0.00494385 ,-0.00244141 , -0.00497437 ,-0.00326538 ,-0.00326538 ,-0.00497437 ,-0.00155640 , -0.00579834 ,-0.00201416 ,-0.00408936 ,-0.00326538 ,-0.00286865 , -0.00405884 ,-0.00283813 ,-0.00415039 ,-0.00277710 ,-0.00418091 , -0.00234985 ,-0.00411987 ,-0.00244141 ,-0.00405884 ,-0.00332642 , -0.00271606 ,-0.00335693 ,-0.00317383 ,-0.00378418 ,-0.00317383 , -0.00329590 ,-0.00323486 ,-0.00283813 ,-0.00323486 ,-0.00326538 , -0.00280762 ,-0.00329590 ,-0.00274658 ,-0.00375366 ,-0.00231934 , -0.00375366 ,-0.00189209 ,-0.00415039 ,-0.00238037 ,-0.00323486 , -0.00241089 ,-0.00320435 ,-0.00241089 ,-0.00326538 ,-0.00238037 , -0.00277710 ,-0.00286865 ,-0.00231934 ,-0.00283813 ,-0.00241089 , -0.00274658 ,-0.00289917 ,-0.00231934 ,-0.00238037 ,-0.00238037 , -0.00280762 ,-0.00149536 ,-0.00372314 ,-0.00149536 ,-0.00234985 , -0.00283813 ,-0.00146484 ,-0.00332642 ,-0.00189209 ,-0.00192261 , -0.00198364 ,-0.00231934 ,-0.00158691 ,-0.00274658 ,-0.00152588 , -0.00280762 ,-0.00106812 ,-0.00192261 ,-0.00241089 ,-0.00143433 , -0.00247192 ,-0.00140381 ,-0.00204468 ,-0.00137329 ,-0.00204468 , -0.00186157 ,-0.00155640 ,-0.00189209 ,-0.00152588 ,-0.00238037 , -0.00146484 ,-0.00201416 ,-0.00094604 ,-0.00161743 ,-0.00137329 , -0.00204468 ,-0.00057983 ,-0.00238037 ,-0.00018311 ,-0.00146484 , -0.00201416 ,-0.00012207 ,-0.00195312 ,-0.00103760 ,-0.00109863 , -0.00146484 ,-0.00067139 ,-0.00146484 ,-0.00057983 ,-0.00201416 , -0.00012207 ,-0.00198364 ,0.00027466 ,-0.00277710 ,0.00106812 , -0.00274658 ,0.00018311 ,-0.00097656 ,-0.00109863 ,-0.00064087 , -0.00057983 ,-0.00109863 ,-0.00057983 ,-0.00061035 ,-0.00155640 , 0.00033569 ,-0.00158691 ,-0.00009155 ,-0.00112915 ,-0.00012207 , -0.00109863 ,-0.00061035 ,-0.00018311 ,-0.00106812 ,-0.00015259 , -0.00152588 ,0.00073242 ,-0.00201416 ,0.00122070 ,-0.00109863 , -0.00018311 ,0.00030518 ,-0.00070190 ,-0.00054932 ,0.00021362 , -0.00012207 ,0.00064087 ,-0.00100708 ,0.00106812 ,-0.00143433 , 0.00109863 ,-0.00015259 ,-0.00067139 ,0.00164795 ,-0.00112915 , 0.00210571 ,-0.00119019 ,0.00170898 ,-0.00119019 ,0.00170898 , -0.00070190 ,0.00115967 ,-0.00061035 ,0.00152588 ,-0.00100708 , 0.00155640 ,-0.00061035 ,0.00070190 ,0.00024414 ,0.00067139 , 0.00030518 ,0.00024414 ,0.00070190 ,0.00027466 ,0.00149536 , -0.00048828 ,0.00143433 ,-0.00006104 ,0.00061035 ,0.00073242 , 0.00112915 ,-0.00018311 ,0.00158691 ,0.00021362 ,0.00033569 , 0.00146484 ,-0.00097656 ,0.00201416 ,-0.00024414 ,0.00210571 , -0.00027466 ,0.00207520 ,0.00024414 ,0.00201416 ,-0.00018311 , 0.00201416 ,-0.00018311 ,0.00244141 ,-0.00015259 ,0.00198364 , 0.00070190 ,0.00204468 ,0.00061035 ,0.00259399 ,0.00012207 , 0.00253296 ,0.00112915 ,0.00152588 ,0.00164795 ,0.00109863 , 0.00204468 ,0.00109863 ,0.00161743 ,0.00198364 ,0.00070190 , 0.00161743 ,0.00195312 ,0.00076294 ,0.00326538 ,0.00033569 , 0.00234985 ,0.00125122 ,0.00146484 ,0.00167847 ,0.00192261 , 0.00119019 ,0.00286865 ,-0.00018311 ,0.00378418 ,-0.00021362 , 0.00381470 ,0.00067139 ,0.00289917 ,0.00109863 ,0.00250244 , 0.00109863 ,0.00296021 ,0.00149536 ,0.00296021 ,0.00106812 , 0.00338745 ,0.00109863 ,0.00338745 ,0.00064087 ,0.00338745 , 0.00149536 ,0.00253296 ,0.00238037 ,0.00253296 ,0.00238037 , 0.00296021 ,0.00152588 ,0.00289917 ,0.00247192 ,0.00244141 , 0.00292969 ,0.00198364 ,0.00378418 ,0.00158691 ,0.00372314 , 0.00253296 ,0.00195312 ,0.00381470 ,0.00158691 ,0.00369263 , 0.00256348 ,0.00277710 ,0.00253296 ,0.00286865 ,0.00244141 , 0.00292969 ,0.00198364 ,0.00329590 ,0.00207520 ,0.00241089 , 0.00335693 ,0.00201416 ,0.00329590 ,0.00292969 ,0.00201416 , 0.00329590 ,0.00296021 ,0.00280762 ,0.00384521 ,0.00198364 , 0.00375366 ,0.00247192 ,0.00289917 ,0.00286865 ,0.00247192 , 0.00378418 ,0.00283813 ,0.00296021 ,0.00326538 ,0.00296021 , 0.00326538 ,0.00292969 ,0.00375366 ,0.00158691 ,0.00509644 , 0.00155640 ,0.00421143 ,0.00289917 ,0.00335693 ,0.00283813 , 0.00335693 ,0.00335693 ,0.00283813 ,0.00427246 ,0.00283813 , 0.00381470 ,0.00286865 ,0.00378418 ,0.00286865 ,0.00424194 , 0.00244141 ,0.00421143 ,0.00289917 ,0.00335693 ,0.00372314 , 0.00247192 ,0.00378418 ,0.00372314 ,0.00253296 ,0.00411987 , 0.00256348 ,0.00408936 ,0.00302124 ,0.00408936 ,0.00299072 , 0.00326538 ,0.00381470 ,0.00244141 ,0.00466919 ,0.00241089 , 0.00469971 ,0.00241089 ,0.00466919 ,0.00244141 ,0.00466919 , 0.00283813 ,0.00430298 ,0.00323486 ,0.00384521 ,0.00329590 , 0.00418091 ,0.00296021 ,0.00411987 ,0.00302124 ,0.00408936 , 0.00299072 ,0.00411987 ,0.00338745 ,0.00418091 ,0.00338745 , 0.00411987 ,0.00302124 ,0.00451660 ,0.00299072 ,0.00460815 , 0.00286865 ,0.00427246 ,0.00372314 ,0.00424194 ,0.00329590 , 0.00469971 ,0.00280762 ,0.00473022 ,0.00329590 ,0.00378418 , 0.00424194 ,0.00280762 ,0.00341797 ,0.00369263 ,0.00299072 , 0.00366211 ,0.00430298 ,0.00195312 ,0.00555420 ,0.00158691 , 0.00503540 ,0.00296021 ,0.00326538 ,0.00381470 ,0.00375366 , 0.00332642 ,0.00424194 ,0.00286865 ,0.00421143 ,0.00332642 , 0.00378418 ,0.00418091 ,0.00338745 ,0.00457764 ,0.00250244 , 0.00418091 ,0.00421143 ,0.00335693 ,0.00415039 ,0.00338745 , 0.00375366 ,0.00329590 ,0.00515747 ,0.00192261 ,0.00561523 , 0.00241089 ,0.00421143 ,0.00466919 ,0.00244141 ,0.00555420 , 0.00238037 ,0.00518799 ,0.00280762 ,0.00427246 ,0.00375366 , 0.00286865 ,0.00512695 ,0.00241089 ,0.00469971 ,0.00283813 , 0.00424194 ,0.00329590 ,0.00427246 ,0.00283813 ,0.00427246 , 0.00326538 ,0.00335693 ,0.00463867 ,0.00119019 ,0.00634766 , 0.00119019 ,0.00503540 ,0.00292969 ,0.00332642 ,0.00375366 , 0.00378418 ,0.00289917 ,0.00460815 ,0.00210571 ,0.00454712 , 0.00296021 ,0.00375366 ,0.00375366 ,0.00378418 ,0.00289917 , 0.00463867 ,0.00289917 ,0.00463867 ,0.00289917 ,0.00375366 , 0.00378418 ,0.00378418 ,0.00375366 ,0.00332642 ,0.00335693 , 0.00457764 ,0.00299072 ,0.00415039 ,0.00292969 ,0.00372314 , 0.00381470 ,0.00286865 ,0.00421143 ,0.00250244 ,0.00454712 , 0.00299072 ,0.00415039 ,0.00292969 ,0.00421143 ,0.00198364 , 0.00466919 ,0.00244141 ,0.00381470 ,0.00372314 ,0.00250244 , 0.00372314 ,0.00335693 ,0.00332642 ,0.00335693 ,0.00286865 , 0.00335693 ,0.00332642 ,0.00372314 ,0.00296021 ,0.00329590 , 0.00332642 ,0.00296021 ,0.00411987 ,0.00207520 ,0.00418091 , 0.00244141 ,0.00250244 ,0.00418091 ,0.00201416 ,0.00378418 , 0.00329590 ,0.00292969 ,0.00332642 ,0.00335693 ,0.00280762 , 0.00387573 ,0.00280762 ,0.00338745 ,0.00335693 ,0.00277710 , 0.00387573 ,0.00283813 ,0.00292969 ,0.00421143 ,0.00198364 , 0.00463867 ,0.00204468 ,0.00375366 ,0.00381470 ,0.00241089 , 0.00421143 ,0.00204468 ,0.00415039 ,0.00256348 ,0.00408936 , 0.00164795 ,0.00418091 ,0.00204468 ,0.00372314 ,0.00250244 , 0.00326538 ,0.00253296 ,0.00283813 ,0.00292969 ,0.00244141 , 0.00375366 ,0.00204468 ,0.00375366 ,0.00204468 ,0.00332642 , 0.00329590 ,0.00161743 ,0.00457764 ,0.00213623 ,0.00366211 , 0.00253296 ,0.00238037 ,0.00381470 ,0.00241089 ,0.00381470 , 0.00201416 ,0.00332642 ,0.00289917 ,0.00286865 ,0.00289917 , 0.00335693 ,0.00289917 ,0.00244141 ,0.00332642 ,0.00289917 , 0.00244141 ,0.00378418 ,0.00112915 ,0.00378418 ,0.00241089 , 0.00250244 ,0.00283813 ,0.00292969 ,0.00204468 ,0.00415039 , 0.00070190 ,0.00427246 ,0.00103760 ,0.00344849 ,0.00192261 , 0.00381470 ,0.00112915 ,0.00421143 ,0.00067139 ,0.00381470 , 0.00195312 ,0.00250244 ,0.00286865 ,0.00244141 ,0.00204468 , 0.00329590 ,0.00204468 ,0.00241089 ,0.00338745 ,0.00109863 , 0.00381470 ,0.00152588 ,0.00250244 ,0.00238037 ,0.00213623 , 0.00277710 ,0.00167847 ,0.00323486 ,0.00119019 ,0.00375366 , 0.00115967 ,0.00244141 ,0.00289917 ,0.00155640 ,0.00332642 , 0.00115967 ,0.00332642 ,0.00161743 ,0.00280762 ,0.00164795 , 0.00283813 ,0.00161743 ,0.00244141 ,0.00158691 ,0.00198364 , 0.00250244 ,0.00195312 ,0.00204468 ,0.00158691 ,0.00244141 , 0.00070190 ,0.00332642 ,0.00112915 ,0.00244141 ,0.00119019 , 0.00283813 ,0.00030518 ,0.00372314 ,-0.00015259 ,0.00289917 , 0.00067139 ,0.00250244 ,0.00064087 ,0.00250244 ,0.00021362 , 0.00335693 ,-0.00021362 ,0.00292969 ,0.00070190 ,0.00152588 , 0.00250244 ,-0.00024414 ,0.00338745 ,0.00024414 ,0.00201416 , 0.00155640 ,0.00070190 ,0.00244141 ,0.00073242 ,0.00198364 , 0.00070190 ,0.00289917 ,-0.00018311 ,0.00286865 ,0.00073242 , 0.00112915 ,0.00201416 ,0.00155640 ,0.00073242 ,0.00241089 , -0.00015259 ,0.00247192 ,0.00018311 ,0.00210571 ,0.00106812 , 0.00161743 ,0.00024414 ,0.00201416 ,-0.00018311 ,0.00201416 , 0.00070190 ,0.00070190 ,0.00112915 ,0.00067139 ,0.00119019 , 0.00064087 ,0.00115967 ,0.00070190 ,0.00067139 ,0.00115967 , 0.00024414 ,0.00158691 ,-0.00021362 ,0.00164795 ,0.00015259 , 0.00082397 ,0.00100708 ,-0.00006104 ,0.00149536 ,-0.00012207 , 0.00067139 ,0.00067139 ,0.00030518 ,0.00109863 ,-0.00012207 , 0.00064087 ,0.00073242 ,-0.00021362 ,0.00201416 ,-0.00100708 , 0.00149536 ,-0.00051880 ,0.00146484 ,-0.00009155 ,0.00064087 , 0.00027466 ,0.00027466 ,0.00155640 ,-0.00103760 ,0.00198364 , -0.00061035 ,0.00115967 ,0.00024414 ,0.00024414 ,0.00027466 , -0.00018311 ,0.00112915 ,-0.00103760 ,0.00155640 ,-0.00064087 , 0.00033569 ,0.00057983 ,-0.00048828 ,0.00146484 ,-0.00097656 , 0.00064087 ,-0.00018311 ,-0.00054932 ,0.00106812 ,-0.00061035 , -0.00015259 ,0.00021362 ,-0.00057983 ,0.00024414 ,-0.00018311 , -0.00021362 ,-0.00009155 ,-0.00073242 ,0.00033569 ,-0.00021362 , -0.00061035 ,0.00024414 ,-0.00100708 ,0.00103760 ,-0.00094604 , -0.00030518 ,-0.00006104 ,-0.00027466 ,-0.00054932 ,-0.00021362 , -0.00021362 ,-0.00012207 ,0.00021362 ,-0.00103760 ,-0.00018311 , -0.00018311 ,-0.00018311 ,-0.00018311 ,-0.00061035 ,0.00024414 , -0.00149536 ,0.00027466 ,-0.00109863 ,-0.00057983 ,-0.00018311 , -0.00155640 ,0.00030518 ,-0.00149536 ,-0.00021362 ,-0.00100708 , -0.00024414 ,-0.00103760 ,-0.00061035 ,-0.00106812 ,-0.00057983 , -0.00112915 ,-0.00009155 ,-0.00250244 ,0.00085449 ,-0.00296021 , 0.00125122 ,-0.00332642 ,0.00030518 ,-0.00201416 ,-0.00094604 , -0.00073242 ,-0.00140381 ,-0.00152588 ,-0.00070190 ,-0.00225830 , -0.00115967 ,-0.00051880 ,-0.00289917 ,0.00073242 ,-0.00283813 , -0.00018311 ,-0.00189209 ,-0.00112915 ,-0.00143433 ,-0.00155640 , -0.00100708 ,-0.00155640 ,-0.00100708 ,-0.00155640 ,-0.00100708 , -0.00152588 ,-0.00103760 ,-0.00152588 ,-0.00103760 ,-0.00152588 , -0.00106812 ,-0.00186157 ,-0.00115967 ,-0.00140381 ,-0.00158691 , -0.00143433 ,-0.00064087 ,-0.00283813 ,0.00030518 ,-0.00332642 , -0.00009155 ,-0.00244141 ,-0.00189209 ,-0.00067139 ,-0.00320435 , 0.00021362 ,-0.00366211 ,-0.00064087 ,-0.00189209 ,-0.00247192 , -0.00137329 ,-0.00204468 ,-0.00234985 ,-0.00097656 ,-0.00250244 , -0.00225830 ,-0.00070190 ,-0.00277710 ,-0.00192261 ,-0.00109863 , -0.00317383 ,-0.00076294 ,-0.00305176 ,-0.00128174 ,-0.00173950 , -0.00207520 ,-0.00143433 ,-0.00238037 ,-0.00195312 ,-0.00149536 , -0.00234985 ,-0.00198364 ,-0.00189209 ,-0.00241089 ,-0.00192261 , -0.00195312 ,-0.00189209 ,-0.00201416 ,-0.00228882 ,-0.00158691 , -0.00277710 ,-0.00146484 ,-0.00286865 ,-0.00143433 ,-0.00289917 , -0.00100708 ,-0.00369263 ,-0.00021362 ,-0.00451660 ,-0.00024414 , -0.00323486 ,-0.00234985 ,-0.00112915 ,-0.00448608 ,-0.00070190 , -0.00363159 ,-0.00152588 ,-0.00195312 ,-0.00320435 ,-0.00155640 , -0.00280762 ,-0.00231934 ,-0.00292969 ,-0.00177002 ,-0.00384521 , -0.00094604 ,-0.00335693 ,-0.00228882 ,-0.00198364 ,-0.00323486 , -0.00192261 ,-0.00375366 ,-0.00186157 ,-0.00286865 ,-0.00277710 , -0.00241089 ,-0.00320435 ,-0.00247192 ,-0.00271606 ,-0.00244141 , -0.00320435 ,-0.00152588 ,-0.00415039 ,-0.00103760 ,-0.00457764 , -0.00018311 ,-0.00500488 ,-0.00106812 ,-0.00372314 ,-0.00231934 , -0.00244141 ,-0.00320435 ,-0.00241089 ,-0.00323486 ,-0.00238037 , -0.00280762 ,-0.00283813 ,-0.00277710 ,-0.00286865 ,-0.00320435 , -0.00195312 ,-0.00460815 ,-0.00100708 ,-0.00549316 ,-0.00103760 , -0.00454712 ,-0.00198364 ,-0.00366211 ,-0.00198364 ,-0.00363159 , -0.00198364 ,-0.00408936 ,-0.00201416 ,-0.00360107 ,-0.00289917 , -0.00317383 ,-0.00332642 ,-0.00323486 ,-0.00238037 ,-0.00326538 , -0.00277710 ,-0.00329590 ,-0.00366211 ,-0.00198364 ,-0.00408936 , -0.00241089 ,-0.00323486 ,-0.00326538 ,-0.00280762 ,-0.00369263 , -0.00280762 ,-0.00283813 ,-0.00369263 ,-0.00231934 ,-0.00338745 , -0.00305176 ,-0.00302124 ,-0.00357056 ,-0.00198364 ,-0.00415039 , -0.00189209 ,-0.00457764 ,-0.00244141 ,-0.00314331 ,-0.00381470 , -0.00271606 ,-0.00286865 ,-0.00415039 ,-0.00231934 ,-0.00375366 , -0.00277710 ,-0.00326538 ,-0.00283813 ,-0.00369263 ,-0.00234985 , -0.00418091 ,-0.00189209 ,-0.00460815 ,-0.00195312 ,-0.00451660 , -0.00201416 ,-0.00448608 ,-0.00201416 ,-0.00454712 ,-0.00234985 , -0.00329590 ,-0.00277710 ,-0.00283813 ,-0.00411987 ,-0.00195312 , -0.00454712 ,-0.00152588 ,-0.00411987 ,-0.00234985 ,-0.00378418 , -0.00225830 ,-0.00466919 ,-0.00100708 ,-0.00503540 ,-0.00149536 , -0.00369263 ,-0.00234985 ,-0.00332642 ,-0.00274658 ,-0.00326538 , -0.00283813 ,-0.00234985 ,-0.00375366 ,-0.00274658 ,-0.00329590 , -0.00277710 ,-0.00201416 ,-0.00360107 ,-0.00247192 ,-0.00317383 , -0.00244141 ,-0.00366211 ,-0.00149536 ,-0.00457764 ,-0.00195312 , -0.00323486 ,-0.00369263 ,-0.00241089 ,-0.00317383 ,-0.00335693 , -0.00143433 ,-0.00415039 ,-0.00241089 ,-0.00317383 ,-0.00332642 , -0.00234985 ,-0.00326538 ,-0.00280762 ,-0.00369263 ,-0.00234985 , -0.00418091 ,-0.00146484 ,-0.00418091 ,-0.00186157 ,-0.00375366 , -0.00277710 ,-0.00286865 ,-0.00363159 ,-0.00247192 ,-0.00357056 , -0.00247192 ,-0.00366211 ,-0.00149536 ,-0.00415039 ,-0.00192261 , -0.00369263 ,-0.00238037 ,-0.00329590 ,-0.00231934 ,-0.00332642 , -0.00228882 ,-0.00289917 ,-0.00231934 ,-0.00415039 ,-0.00067139 , -0.00537109 ,-0.00024414 ,-0.00540161 ,-0.00064087 ,-0.00457764 , -0.00146484 ,-0.00418091 ,-0.00277710 ,-0.00286865 ,-0.00363159 , -0.00241089 ,-0.00326538 ,-0.00277710 ,-0.00329590 ,-0.00192261 , -0.00454712 ,-0.00201416 ,-0.00360107 ,-0.00375366 ,-0.00149536 , -0.00497437 ,-0.00201416 ,-0.00405884 ,-0.00198364 ,-0.00454712 , -0.00152588 ,-0.00457764 ,-0.00146484 ,-0.00372314 ,-0.00277710 , -0.00332642 ,-0.00271606 ,-0.00332642 ,-0.00277710 ,-0.00329590 , -0.00231934 ,-0.00375366 ,-0.00228882 ,-0.00427246 ,-0.00180054 , -0.00335693 ,-0.00317383 ,-0.00244141 ,-0.00408936 ,-0.00198364 , -0.00317383 ,-0.00378418 ,-0.00140381 ,-0.00421143 ,-0.00103760 , -0.00411987 ,-0.00149536 ,-0.00411987 ,-0.00155640 ,-0.00405884 , -0.00158691 ,-0.00360107 ,-0.00244141 ,-0.00323486 ,-0.00234985 , -0.00244141 ,-0.00274658 ,-0.00332642 ,-0.00231934 ,-0.00372314 , -0.00192261 ,-0.00326538 ,-0.00238037 ,-0.00283813 ,-0.00320435 , -0.00241089 ,-0.00366211 ,-0.00241089 ,-0.00280762 ,-0.00283813 , -0.00231934 ,-0.00332642 ,-0.00320435 ,-0.00195312 ,-0.00372314 , -0.00143433 ,-0.00378418 ,-0.00186157 ,-0.00283813 ,-0.00283813 , -0.00274658 ,-0.00158691 ,-0.00317383 ,-0.00155640 ,-0.00366211 , -0.00149536 ,-0.00329590 ,-0.00143433 ,-0.00332642 ,-0.00186157 , -0.00241089 ,-0.00329590 ,-0.00143433 ,-0.00286865 ,-0.00146484 , -0.00323486 ,-0.00070190 ,-0.00402832 ,-0.00070190 ,-0.00320435 , -0.00152588 ,-0.00238037 ,-0.00234985 ,-0.00241089 ,-0.00189209 , -0.00286865 ,-0.00100708 ,-0.00372314 ,-0.00106812 ,-0.00323486 , -0.00149536 ,-0.00329590 ,-0.00189209 ,-0.00241089 ,-0.00234985 , -0.00195312 ,-0.00238037 ,-0.00280762 ,-0.00106812 ,-0.00323486 , -0.00109863 ,-0.00277710 ,-0.00106812 ,-0.00326538 ,-0.00061035 , -0.00372314 ,-0.00100708 ,-0.00244141 ,-0.00143433 ,-0.00241089 , -0.00241089 ,-0.00183105 ,-0.00250244 ,-0.00137329 ,-0.00250244 , -0.00186157 ,-0.00195312 ,-0.00152588 ,-0.00271606 ,-0.00164795 , -0.00134277 ,-0.00253296 ,-0.00137329 ,-0.00155640 ,-0.00366211 , 0.00109863 ,-0.00454712 ,-0.00021362 ,-0.00277710 ,-0.00198364 , -0.00103760 ,-0.00238037 ,-0.00106812 ,-0.00280762 ,-0.00106812 , -0.00280762 ,-0.00064087 ,-0.00280762 ,-0.00146484 ,-0.00152588 , -0.00234985 ,-0.00109863 ,-0.00280762 ,-0.00146484 ,-0.00198364 , -0.00146484 ,-0.00195312 ,-0.00234985 ,-0.00152588 ,-0.00195312 , -0.00149536 ,-0.00189209 ,-0.00198364 ,-0.00146484 ,-0.00109863 , -0.00149536 ,-0.00143433 ,-0.00158691 ,-0.00143433 ,-0.00067139 , -0.00231934 ,-0.00067139 ,-0.00234985 ,-0.00106812 ,-0.00064087 , -0.00234985 ,-0.00064087 ,-0.00189209 ,-0.00158691 ,-0.00097656 , -0.00155640 ,-0.00057983 ,-0.00195312 ,-0.00106812 ,-0.00192261 , -0.00064087 ,-0.00192261 ,-0.00106812 ,-0.00149536 ,-0.00149536 , -0.00106812 ,-0.00149536 ,-0.00103760 ,-0.00112915 ,-0.00183105 , -0.00073242 ,-0.00228882 ,-0.00109863 ,-0.00106812 ,-0.00234985 , 0.00024414 ,-0.00280762 ,-0.00064087 ,-0.00100708 ,-0.00201416 , 0.00033569 ,-0.00286865 ,0.00027466 ,-0.00238037 ,-0.00057983 , -0.00155640 ,-0.00054932 ,-0.00158691 ,-0.00054932 ,-0.00198364 , -0.00057983 ,-0.00061035 ,-0.00244141 ,0.00125122 ,-0.00430298 , 0.00216675 ,-0.00332642 ,0.00070190 ,-0.00192261 ,-0.00064087 , -0.00149536 ,-0.00057983 ,-0.00070190 ,-0.00143433 ,0.00024414 , -0.00152588 ,-0.00054932 ,0.00015259 ,-0.00183105 ,0.00064087 , -0.00106812 ,-0.00057983 ,-0.00024414 ,-0.00140381 ,-0.00027466 , -0.00097656 ,-0.00024414 ,-0.00103760 ,-0.00018311 ,-0.00106812 , -0.00015259 ,-0.00106812 ,-0.00064087 ,-0.00015259 ,-0.00109863 , -0.00057983 ,0.00024414 ,-0.00152588 ,0.00030518 ,-0.00109863 , -0.00061035 ,-0.00015259 ,-0.00112915 ,-0.00054932 ,-0.00021362 , -0.00064087 ,-0.00057983 ,-0.00021362 ,-0.00061035 ,-0.00015259 , -0.00112915 ,0.00033569 ,-0.00067139 ,-0.00061035 ,0.00073242 , -0.00244141 ,0.00164795 ,-0.00155640 ,0.00027466 ,-0.00018311 , -0.00057983 ,0.00064087 ,-0.00100708 ,0.00064087 ,-0.00057983 , 0.00024414 ,-0.00018311 ,-0.00057983 ,0.00018311 ,-0.00012207 , -0.00064087 ,0.00109863 ,-0.00140381 ,0.00100708 ,-0.00091553 , 0.00012207 ,0.00036621 ,-0.00027466 ,-0.00054932 ,0.00021362 , -0.00057983 ,0.00021362 ,-0.00057983 ,0.00021362 ,-0.00057983 , 0.00064087 ,-0.00097656 ,0.00064087 ,-0.00057983 ,0.00109863 , -0.00064087 ,0.00033569 ,-0.00027466 ,0.00036621 ,-0.00027466 , 0.00073242 ,-0.00103760 ,0.00152588 ,-0.00057983 ,0.00109863 , -0.00100708 ,0.00106812 ,-0.00009155 ,0.00015259 ,-0.00006104 , 0.00057983 ,0.00033569 ,0.00024414 ,0.00067139 ,-0.00054932 , 0.00100708 ,0.00039673 ,0.00057983 ,0.00036621 ,0.00061035 , -0.00012207 ,0.00152588 ,-0.00103760 ,0.00204468 ,-0.00155640 , 0.00296021 ,-0.00112915 ,0.00164795 ,-0.00021362 ,-0.00018311 , 0.00158691 ,-0.00018311 ,0.00070190 ,0.00112915 ,-0.00015259 , 0.00064087 ,0.00076294 ,0.00024414 ,0.00106812 ,0.00082397 , 0.00009155 ,0.00131226 ,0.00100708 ,-0.00012207 ,0.00198364 , -0.00057983 ,0.00241089 ,-0.00012207 ,0.00103760 ,0.00167847 , -0.00067139 ,0.00332642 ,-0.00146484 ,0.00415039 ,-0.00186157 , 0.00329590 ,0.00027466 ,0.00109863 ,0.00158691 ,0.00027466 , 0.00158691 ,0.00021362 ,0.00250244 ,-0.00070190 ,0.00341797 , -0.00109863 ,0.00286865 ,-0.00012207 ,0.00238037 ,0.00073242 , 0.00161743 ,0.00061035 ,0.00210571 ,0.00018311 ,0.00161743 , 0.00155640 ,0.00027466 ,0.00286865 ,-0.00015259 ,0.00198364 , 0.00158691 ,0.00112915 ,0.00158691 ,0.00155640 ,0.00070190 , 0.00250244 ,0.00018311 ,0.00253296 ,0.00061035 ,0.00164795 , 0.00198364 ,0.00112915 ,0.00204468 ,0.00064087 ,0.00207520 , 0.00112915 ,0.00152588 ,0.00119019 ,0.00241089 ,0.00073242 , 0.00198364 ,0.00161743 ,0.00152588 ,0.00204468 ,0.00155640 , 0.00158691 ,0.00201416 ,0.00158691 ,0.00109863 ,0.00247192 , 0.00155640 ,0.00164795 ,0.00189209 ,0.00128174 ,0.00189209 , 0.00161743 ,0.00247192 ,0.00112915 ,0.00286865 ,0.00073242 , 0.00198364 ,0.00198364 ,0.00167847 ,0.00234985 ,0.00119019 , 0.00286865 ,0.00070190 ,0.00378418 ,0.00070190 ,0.00198364 , 0.00247192 ,0.00073242 ,0.00280762 ,0.00170898 ,0.00100708 , 0.00296021 ,0.00024414 ,0.00332642 ,0.00067139 ,0.00210571 , 0.00143433 ,0.00170898 ,0.00192261 ,0.00204468 ,0.00070190 , 0.00332642 ,0.00024414 ,0.00338745 ,0.00103760 ,0.00122070 , 0.00329590 ,-0.00018311 ,0.00424194 ,-0.00067139 ,0.00427246 , -0.00067139 ,0.00424194 ,0.00067139 ,0.00289917 ,0.00115967 , 0.00286865 ,0.00161743 ,0.00280762 ,0.00167847 ,0.00234985 , 0.00210571 ,0.00241089 ,0.00158691 ,0.00244141 ,0.00158691 , 0.00198364 ,0.00250244 ,0.00155640 ,0.00244141 ,0.00204468 , 0.00195312 ,0.00253296 ,0.00238037 ,0.00207520 ,0.00241089 , 0.00204468 ,0.00155640 ,0.00335693 ,0.00021362 ,0.00427246 , 0.00018311 ,0.00341797 ,0.00152588 ,0.00247192 ,0.00241089 , 0.00164795 ,0.00238037 ,0.00164795 ,0.00286865 ,0.00106812 , 0.00299072 ,0.00106812 ,0.00381470 ,0.00024414 ,0.00421143 , -0.00018311 ,0.00463867 ,0.00027466 ,0.00375366 ,0.00115967 , 0.00289917 ,0.00241089 ,0.00204468 ,0.00286865 ,0.00204468 , 0.00241089 ,0.00292969 ,0.00198364 ,0.00292969 ,0.00198364 , 0.00335693 ,0.00155640 ,0.00375366 ,0.00161743 ,0.00329590 , 0.00204468 ,0.00332642 ,0.00155640 ,0.00424194 ,0.00106812 , 0.00387573 ,0.00189209 ,0.00347900 ,0.00189209 ,0.00338745 , 0.00201416 ,0.00283813 ,0.00299072 ,0.00238037 ,0.00292969 , 0.00198364 ,0.00289917 ,0.00247192 ,0.00244141 ,0.00335693 , 0.00152588 ,0.00381470 ,0.00244141 ,0.00201416 ,0.00332642 , 0.00204468 ,0.00286865 ,0.00247192 ,0.00286865 ,0.00289917 , 0.00292969 ,0.00283813 ,0.00250244 ,0.00283813 ,0.00292969 , 0.00244141 ,0.00332642 ,0.00247192 ,0.00329590 ,0.00292969 , 0.00329590 ,0.00250244 ,0.00415039 ,0.00076294 ,0.00588989 , 0.00033569 ,0.00457764 ,0.00253296 ,0.00277710 ,0.00344849 , 0.00323486 ,0.00256348 ,0.00366211 ,0.00256348 ,0.00277710 , 0.00344849 ,0.00280762 ,0.00296021 ,0.00326538 ,0.00256348 , 0.00320435 ,0.00302124 ,0.00277710 ,0.00341797 ,0.00283813 , 0.00292969 ,0.00332642 ,0.00244141 ,0.00335693 ,0.00286865 , 0.00332642 ,0.00250244 ,0.00326538 ,0.00207520 ,0.00415039 , 0.00161743 ,0.00506592 ,0.00161743 ,0.00415039 ,0.00207520 , 0.00326538 ,0.00296021 ,0.00372314 ,0.00247192 ,0.00378418 , 0.00198364 ,0.00469971 ,0.00195312 ,0.00427246 ,0.00238037 , 0.00427246 ,0.00198364 ,0.00555420 ,0.00067139 ,0.00552368 , 0.00158691 ,0.00418091 ,0.00296021 ,0.00329590 ,0.00286865 , 0.00424194 ,0.00241089 ,0.00381470 ,0.00201416 ,0.00418091 , 0.00204468 ,0.00421143 ,0.00286865 ,0.00335693 ,0.00286865 , 0.00378418 ,0.00244141 ,0.00381470 ,0.00326538 ,0.00250244 , 0.00375366 ,0.00283813 ,0.00302124 ,0.00360107 ,0.00350952 , 0.00186157 ,0.00518799 ,0.00109863 ,0.00552368 ,0.00158691 , 0.00463867 ,0.00201416 ,0.00466919 ,0.00244141 ,0.00375366 , 0.00335693 ,0.00286865 ,0.00292969 ,0.00460815 ,0.00161743 , 0.00506592 ,0.00241089 ,0.00341797 ,0.00415039 ,0.00292969 , 0.00329590 ,0.00421143 ,0.00250244 ,0.00506592 ,0.00198364 , 0.00427246 ,0.00280762 ,0.00390625 ,0.00320435 ,0.00384521 , 0.00286865 ,0.00375366 ,0.00384521 ,0.00277710 ,0.00430298 , 0.00329590 ,0.00286865 ,0.00515747 ,0.00192261 ,0.00473022 , 0.00283813 ,0.00335693 ,0.00421143 ,0.00247192 ,0.00463867 , 0.00201416 ,0.00463867 ,0.00292969 ,0.00329590 ,0.00427246 , 0.00195312 ,0.00466919 ,0.00292969 ,0.00411987 ,0.00256348 , 0.00411987 ,0.00296021 ,0.00418091 ,0.00286865 ,0.00381470 , 0.00286865 ,0.00378418 ,0.00332642 ,0.00332642 ,0.00332642 , 0.00378418 ,0.00332642 ,0.00286865 ,0.00427246 ,0.00234985 , 0.00476074 ,0.00234985 ,0.00430298 ,0.00238037 ,0.00469971 , 0.00241089 ,0.00469971 ,0.00283813 ,0.00430298 ,0.00274658 , 0.00436401 ,0.00320435 ,0.00341797 ,0.00463867 ,0.00198364 , 0.00512695 ,0.00198364 ,0.00509644 ,0.00155640 ,0.00558472 , 0.00106812 ,0.00561523 ,0.00192261 ,0.00424194 ,0.00335693 , 0.00241089 ,0.00469971 ,0.00241089 ,0.00469971 ,0.00238037 , 0.00384521 ,0.00326538 ,0.00296021 ,0.00418091 ,0.00244141 , 0.00424194 ,0.00283813 ,0.00341797 ,0.00326538 ,0.00381470 , 0.00244141 ,0.00463867 ,0.00244141 ,0.00381470 ,0.00332642 , 0.00286865 ,0.00378418 ,0.00375366 ,0.00335693 ,0.00332642 , 0.00375366 ,0.00335693 ,0.00418091 ,0.00247192 ,0.00421143 , 0.00241089 ,0.00473022 ,0.00234985 ,0.00344849 ,0.00363159 , 0.00347900 ,0.00320435 ,0.00387573 ,0.00326538 ,0.00289917 , 0.00335693 ,0.00332642 ,0.00286865 ,0.00427246 ,0.00234985 , 0.00344849 ,0.00323486 ,0.00387573 ,0.00189209 ,0.00479126 , 0.00097656 ,0.00521851 ,0.00152588 ,0.00421143 ,0.00289917 , 0.00332642 ,0.00332642 ,0.00335693 ,0.00201416 ,0.00418091 , 0.00289917 ,0.00247192 ,0.00460815 ,0.00122070 ,0.00497437 , 0.00210571 ,0.00329590 ,0.00332642 ,0.00292969 ,0.00326538 , 0.00341797 ,0.00280762 ,0.00341797 ,0.00323486 ,0.00256348 , 0.00457764 ,0.00204468 ,0.00418091 ,0.00292969 ,0.00375366 , 0.00289917 ,0.00421143 ,0.00198364 ,0.00424194 ,0.00292969 , 0.00369263 ,0.00253296 ,0.00369263 ,0.00250244 ,0.00421143 , 0.00286865 ,0.00338745 ,0.00326538 ,0.00292969 ,0.00332642 , 0.00289917 ,0.00375366 ,0.00204468 ,0.00372314 ,0.00296021 , 0.00280762 ,0.00341797 ,0.00283813 ,0.00289917 ,0.00292969 , 0.00372314 ,0.00204468 ,0.00378418 ,0.00195312 ,0.00384521 , 0.00280762 ,0.00341797 ,0.00241089 ,0.00378418 ,0.00244141 , 0.00375366 ,0.00247192 ,0.00332642 ,0.00289917 ,0.00378418 , 0.00155640 ,0.00509644 ,0.00112915 ,0.00552368 ,0.00115967 , 0.00421143 ,0.00244141 ,0.00335693 ,0.00372314 ,0.00207520 , 0.00326538 ,0.00296021 ,0.00283813 ,0.00381470 ,0.00195312 , 0.00384521 ,0.00192261 ,0.00387573 ,0.00189209 ,0.00390625 , 0.00274658 ,0.00170898 ,0.00500488 ,0.00027466 ,0.00512695 , 0.00106812 ,0.00381470 ,0.00204468 ,0.00369263 ,0.00210571 , 0.00280762 ,0.00296021 ,0.00241089 ,0.00292969 ,0.00198364 , 0.00289917 ,0.00247192 ,0.00286865 ,0.00247192 ,0.00289917 , 0.00195312 ,0.00299072 ,0.00192261 ,0.00296021 ,0.00241089 , 0.00247192 ,0.00201416 ,0.00289917 ,0.00244141 ,0.00247192 , 0.00201416 ,0.00289917 ,0.00155640 ,0.00424194 ,0.00064087 , 0.00384521 ,0.00238037 ,0.00164795 ,0.00326538 ,0.00207520 , 0.00238037 ,0.00250244 ,0.00198364 ,0.00247192 ,0.00158691 , 0.00378418 ,0.00021362 ,0.00424194 ,0.00067139 ,0.00292969 , 0.00198364 ,0.00207520 ,0.00238037 ,0.00161743 ,0.00286865 , 0.00112915 ,0.00289917 ,0.00119019 ,0.00323486 ,0.00122070 , 0.00238037 ,0.00204468 ,0.00204468 ,0.00198364 ,0.00247192 , 0.00158691 ,0.00238037 ,0.00125122 ,0.00234985 ,0.00122070 , 0.00329590 ,0.00067139 ,0.00296021 ,0.00061035 ,0.00341797 , -0.00027466 ,0.00387573 ,-0.00027466 ,0.00427246 ,-0.00064087 , 0.00375366 ,0.00119019 ,0.00195312 ,0.00207520 ,0.00152588 , 0.00204468 ,0.00247192 ,0.00152588 ,0.00250244 ,0.00064087 , 0.00384521 ,-0.00024414 ,0.00427246 ,0.00015259 ,0.00344849 , 0.00061035 ,0.00250244 ,0.00155640 ,0.00201416 ,0.00158691 , 0.00201416 ,0.00067139 ,0.00247192 ,0.00112915 ,0.00115967 , 0.00241089 ,0.00030518 ,0.00241089 ,0.00115967 ,0.00158691 , 0.00152588 ,0.00164795 ,0.00061035 ,0.00253296 ,0.00021362 , 0.00247192 ,0.00109863 ,0.00115967 ,0.00204468 ,0.00064087 , 0.00207520 ,0.00106812 ,0.00115967 ,0.00204468 ,0.00067139 , 0.00158691 ,0.00158691 ,0.00021362 ,0.00250244 ,0.00067139 , 0.00112915 ,0.00204468 ,0.00067139 ,0.00201416 ,0.00115967 , 0.00109863 ,0.00158691 ,0.00161743 ,0.00106812 ,0.00207520 , -0.00021362 ,0.00247192 ,0.00024414 ,0.00201416 ,0.00070190 , 0.00070190 ,0.00115967 ,0.00152588 ,0.00073242 ,0.00198364 , -0.00057983 ,0.00286865 ,-0.00061035 ,0.00244141 ,-0.00061035 , 0.00247192 ,-0.00109863 ,0.00338745 ,-0.00155640 ,0.00292969 , -0.00064087 ,0.00201416 ,0.00024414 ,0.00119019 ,0.00018311 , 0.00161743 ,0.00024414 ,0.00067139 ,0.00119019 ,0.00021362 , 0.00115967 ,0.00070190 ,-0.00021362 ,0.00204468 ,-0.00064087 , 0.00207520 ,-0.00027466 ,0.00122070 ,0.00061035 ,0.00076294 , 0.00024414 ,0.00112915 ,0.00024414 ,0.00070190 ,0.00115967 , -0.00024414 ,0.00213623 ,-0.00122070 ,0.00259399 ,-0.00115967 , 0.00073242 ,0.00161743 ,-0.00073242 ,0.00170898 ,-0.00073242 , 0.00122070 ,0.00018311 ,0.00076294 ,-0.00024414 ,0.00122070 , -0.00070190 ,0.00119019 ,-0.00021362 ,0.00070190 ,-0.00057983 , 0.00106812 ,-0.00097656 ,0.00149536 ,-0.00054932 ,0.00064087 , -0.00057983 ,0.00112915 ,-0.00064087 ,0.00030518 ,0.00064087 , -0.00097656 ,0.00146484 ,-0.00094604 ,0.00103760 ,-0.00100708 , 0.00115967 ,-0.00024414 ,0.00033569 ,-0.00070190 ,0.00076294 , -0.00067139 ,0.00119019 ,-0.00067139 ,-0.00015259 ,0.00027466 , 0.00021362 ,0.00030518 ,0.00021362 ,-0.00012207 ,0.00018311 , 0.00033569 ,0.00018311 ,-0.00012207 ,-0.00021362 ,0.00027466 , -0.00061035 ,0.00067139 ,-0.00057983 ,0.00018311 ,0.00033569 , -0.00067139 ,0.00073242 ,-0.00106812 ,0.00024414 ,-0.00018311 , -0.00018311 ,-0.00018311 ,-0.00015259 ,-0.00067139 ,-0.00012207 , -0.00024414 ,-0.00057983 ,-0.00021362 ,-0.00012207 ,-0.00070190 , -0.00054932 ,-0.00024414 ,-0.00100708 ,0.00021362 ,-0.00100708 , 0.00018311 ,-0.00140381 ,0.00061035 ,-0.00186157 ,0.00018311 , -0.00054932 ,-0.00067139 ,-0.00054932 ,-0.00070190 ,-0.00097656 , 0.00015259 ,-0.00094604 ,-0.00070190 ,-0.00100708 ,0.00024414 , -0.00155640 ,0.00122070 ,-0.00244141 ,0.00030518 ,-0.00112915 , -0.00054932 ,0.00021362 ,-0.00192261 ,0.00115967 ,-0.00289917 , 0.00122070 ,-0.00198364 ,0.00030518 ,-0.00112915 ,-0.00097656 , -0.00073242 ,-0.00048828 ,-0.00115967 ,-0.00057983 ,-0.00149536 , -0.00067139 ,-0.00097656 ,-0.00024414 ,-0.00192261 ,-0.00015259 , -0.00198364 ,-0.00012207 ,-0.00112915 ,-0.00143433 ,-0.00024414 , -0.00228882 ,-0.00067139 ,-0.00106812 ,-0.00146484 ,-0.00064087 , -0.00106812 ,-0.00189209 ,0.00018311 ,-0.00228882 ,-0.00024414 , -0.00189209 ,-0.00067139 ,-0.00143433 ,-0.00112915 ,-0.00054932 , -0.00155640 ,-0.00100708 ,-0.00152588 ,-0.00106812 ,-0.00149536 , -0.00015259 ,-0.00241089 ,-0.00015259 ,-0.00152588 ,-0.00146484 , -0.00067139 ,-0.00189209 ,-0.00021362 ,-0.00280762 ,0.00073242 , -0.00247192 ,0.00036621 ,-0.00247192 ,-0.00051880 ,-0.00161743 , -0.00094604 ,-0.00158691 ,-0.00146484 ,-0.00100708 ,-0.00201416 , -0.00057983 ,-0.00195312 ,-0.00106812 ,-0.00146484 ,-0.00106812 , -0.00198364 ,-0.00097656 ,-0.00204468 ,-0.00183105 ,-0.00027466 , -0.00320435 ,-0.00021362 ,-0.00366211 ,0.00021362 ,-0.00320435 , -0.00070190 ,-0.00228882 ,-0.00158691 ,-0.00137329 ,-0.00207520 , -0.00137329 ,-0.00204468 ,-0.00189209 ,-0.00100708 ,-0.00201416 , -0.00143433 ,-0.00155640 ,-0.00277710 ,0.00024414 ,-0.00326538 , -0.00103760 ,-0.00152588 ,-0.00277710 ,-0.00064087 ,-0.00238037 , -0.00152588 ,-0.00146484 ,-0.00192261 ,-0.00198364 ,-0.00100708 , -0.00289917 ,-0.00054932 ,-0.00238037 ,-0.00198364 ,-0.00189209 , -0.00198364 ,-0.00192261 ,-0.00189209 ,-0.00244141 ,-0.00100708 , -0.00329590 ,-0.00057983 ,-0.00283813 ,-0.00152588 ,-0.00189209 , -0.00241089 ,-0.00231934 ,-0.00155640 ,-0.00280762 ,-0.00149536 , -0.00280762 ,-0.00192261 ,-0.00195312 ,-0.00241089 ,-0.00189209 , -0.00238037 ,-0.00241089 ,-0.00189209 ,-0.00241089 ,-0.00195312 , -0.00274658 ,-0.00158691 ,-0.00231934 ,-0.00241089 ,-0.00189209 , -0.00241089 ,-0.00238037 ,-0.00106812 ,-0.00411987 ,-0.00018311 , -0.00369263 ,-0.00106812 ,-0.00241089 ,-0.00274658 ,-0.00158691 , -0.00274658 ,-0.00152588 ,-0.00238037 ,-0.00192261 ,-0.00195312 , -0.00326538 ,-0.00057983 ,-0.00375366 ,-0.00100708 ,-0.00283813 , -0.00192261 ,-0.00195312 ,-0.00238037 ,-0.00234985 ,-0.00152588 , -0.00323486 ,-0.00149536 ,-0.00326538 ,-0.00149536 ,-0.00326538 , -0.00106812 ,-0.00363159 ,-0.00115967 ,-0.00271606 ,-0.00201416 , -0.00280762 ,-0.00186157 ,-0.00338745 ,-0.00180054 ,-0.00204468 , -0.00360107 ,-0.00158691 ,-0.00320435 ,-0.00198364 ,-0.00189209 , -0.00283813 ,-0.00192261 ,-0.00241089 ,-0.00280762 ,-0.00146484 , -0.00375366 ,-0.00100708 ,-0.00283813 ,-0.00238037 ,-0.00149536 , -0.00323486 ,-0.00155640 ,-0.00274658 ,-0.00152588 ,-0.00283813 , -0.00143433 ,-0.00292969 ,-0.00140381 ,-0.00241089 ,-0.00195312 , -0.00228882 ,-0.00253296 ,-0.00134277 ,-0.00296021 ,-0.00183105 , -0.00241089 ,-0.00241089 ,-0.00231934 ,-0.00201416 ,-0.00231934 , -0.00238037 ,-0.00244141 ,-0.00274658 ,-0.00198364 ,-0.00234985 , -0.00280762 ,-0.00198364 ,-0.00234985 ,-0.00326538 ,-0.00018311 , -0.00500488 ,-0.00018311 ,-0.00415039 ,-0.00146484 ,-0.00198364 , -0.00323486 ,-0.00149536 ,-0.00326538 ,-0.00192261 ,-0.00241089 , -0.00189209 ,-0.00286865 ,-0.00189209 ,-0.00241089 ,-0.00280762 , -0.00061035 ,-0.00372314 ,-0.00192261 ,-0.00192261 ,-0.00241089 , -0.00189209 ,-0.00244141 ,-0.00274658 ,-0.00155640 ,-0.00274658 , -0.00112915 ,-0.00363159 ,-0.00067139 ,-0.00408936 ,-0.00064087 , -0.00323486 ,-0.00198364 ,-0.00186157 ,-0.00244141 ,-0.00192261 , -0.00277710 ,-0.00158691 ,-0.00314331 ,-0.00115967 ,-0.00274658 , -0.00201416 ,-0.00228882 ,-0.00201416 ,-0.00274658 ,-0.00112915 , -0.00323486 ,-0.00100708 ,-0.00335693 ,-0.00094604 ,-0.00292969 , -0.00140381 ,-0.00286865 ,-0.00149536 ,-0.00234985 ,-0.00198364 , -0.00231934 ,-0.00198364 ,-0.00234985 ,-0.00195312 ,-0.00238037 , -0.00103760 ,-0.00286865 ,-0.00186157 ,-0.00155640 ,-0.00280762 , -0.00103760 ,-0.00283813 ,-0.00146484 ,-0.00286865 ,-0.00054932 , -0.00332642 ,-0.00054932 ,-0.00286865 ,-0.00103760 ,-0.00238037 , -0.00195312 ,-0.00103760 ,-0.00283813 ,-0.00103760 ,-0.00195312 , -0.00195312 ,-0.00192261 ,-0.00152588 ,-0.00231934 ,-0.00155640 , -0.00146484 ,-0.00329590 ,0.00030518 ,-0.00418091 ,0.00030518 , -0.00372314 ,-0.00064087 ,-0.00274658 ,-0.00158691 ,-0.00186157 , -0.00241089 ,-0.00106812 ,-0.00234985 ,-0.00152588 ,-0.00192261 , -0.00149536 ,-0.00238037 ,-0.00106812 ,-0.00234985 ,-0.00195312 , -0.00106812 ,-0.00189209 ,-0.00158691 ,-0.00183105 ,-0.00158691 , -0.00189209 ,-0.00103760 ,-0.00201416 ,-0.00140381 ,-0.00115967 , -0.00274658 ,-0.00021362 ,-0.00234985 ,-0.00152588 ,-0.00061035 , -0.00238037 ,-0.00061035 ,-0.00149536 ,-0.00195312 ,-0.00061035 , -0.00238037 ,-0.00018311 ,-0.00234985 ,-0.00067139 ,-0.00143433 , -0.00155640 ,-0.00103760 ,-0.00103760 ,-0.00198364 ,-0.00012207 , -0.00241089 ,-0.00021362 ,-0.00274658 ,0.00018311 ,-0.00274658 , -0.00021362 ,-0.00195312 ,-0.00146484 ,-0.00109863 ,-0.00192261 , -0.00100708 ,-0.00158691 ,-0.00137329 ,-0.00119019 ,-0.00143433 , -0.00149536 ,-0.00106812 ,-0.00192261 ,-0.00064087 ,-0.00195312 , -0.00100708 ,-0.00155640 ,-0.00100708 ,-0.00112915 ,-0.00143433 , -0.00067139 ,-0.00189209 ,-0.00067139 ,-0.00192261 ,-0.00015259 , -0.00152588 ,-0.00061035 ,-0.00152588 ,-0.00100708 ,-0.00112915 , -0.00054932 ,-0.00152588 ,-0.00109863 ,-0.00051880 ,-0.00119019 , -0.00006104 ,-0.00161743 ,-0.00048828 ,-0.00207520 ,0.00039673 , -0.00161743 ,-0.00097656 ,-0.00027466 ,-0.00268555 ,0.00097656 , -0.00177002 ,-0.00030518 ,-0.00103760 ,-0.00100708 ,-0.00070190 , -0.00097656 ,-0.00070190 ,-0.00097656 ,-0.00070190 ,-0.00143433 , 0.00021362 ,-0.00234985 ,0.00027466 ,-0.00198364 ,-0.00012207 , -0.00155640 ,-0.00015259 ,-0.00146484 ,-0.00070190 ,-0.00051880 , -0.00112915 ,-0.00061035 ,-0.00103760 ,-0.00106812 ,-0.00021362 , -0.00100708 ,0.00018311 ,-0.00097656 ,-0.00112915 ,0.00076294 , -0.00289917 ,0.00207520 ,-0.00329590 ,0.00207520 ,-0.00244141 , 0.00073242 ,-0.00064087 ,-0.00103760 ,0.00070190 ,-0.00106812 , 0.00024414 ,-0.00061035 ,-0.00018311 ,-0.00018311 ,-0.00015259 , -0.00024414 ,-0.00009155 ,-0.00030518 ,-0.00048828 ,0.00057983 , -0.00097656 ,0.00109863 ,-0.00103760 ,0.00024414 ,-0.00018311 , -0.00018311 ,0.00027466 ,-0.00018311 ,-0.00015259 ,0.00064087 , -0.00100708 ,0.00198364 ,-0.00238037 ,0.00161743 ,-0.00112915 , 0.00079346 ,-0.00027466 ,-0.00057983 ,0.00115967 ,-0.00158691 , 0.00213623 ,-0.00204468 ,0.00207520 ,-0.00106812 ,0.00067139 , 0.00027466 ,-0.00018311 ,0.00115967 ,-0.00109863 ,0.00073242 , 0.00064087 ,-0.00097656 ,0.00238037 ,-0.00189209 ,0.00244141 , -0.00109863 ,0.00079346 ,0.00100708 ,-0.00094604 ,0.00238037 , -0.00146484 ,0.00158691 ,0.00064087 ,-0.00097656 ,0.00280762 , -0.00183105 ,0.00234985 ,-0.00054932 ,0.00109863 ,0.00070190 , 0.00030518 ,0.00018311 ,0.00122070 ,-0.00027466 ,0.00207520 , -0.00106812 ,0.00198364 ,-0.00057983 ,0.00241089 ,-0.00146484 , 0.00241089 ,-0.00097656 ,0.00143433 ,0.00042725 ,0.00012207 , 0.00122070 ,0.00018311 ,0.00122070 ,0.00061035 ,0.00079346 , 0.00061035 ,0.00119019 ,0.00067139 ,0.00158691 ,-0.00021362 , 0.00164795 ,0.00061035 ,0.00122070 ,0.00064087 ,0.00112915 , 0.00119019 ,0.00064087 ,0.00161743 ,0.00027466 ,0.00149536 , 0.00122070 ,0.00021362 ,0.00244141 ,-0.00012207 ,0.00192261 , 0.00033569 ,0.00198364 ,0.00070190 ,0.00244141 ,-0.00015259 , 0.00241089 ,0.00073242 ,0.00112915 ,0.00289917 ,-0.00106812 , 0.00421143 ,-0.00103760 ,0.00283813 ,0.00122070 ,0.00103760 , 0.00210571 ,0.00018311 ,0.00250244 ,0.00070190 ,0.00195312 , 0.00122070 ,0.00103760 ,0.00122070 ,0.00195312 ,0.00119019 , 0.00152588 ,0.00204468 ,0.00024414 ,0.00286865 ,0.00119019 , 0.00106812 ,0.00207520 ,0.00112915 ,0.00109863 ,0.00204468 , 0.00112915 ,0.00155640 ,0.00161743 ,0.00152588 ,0.00161743 , 0.00109863 ,0.00250244 ,0.00061035 ,0.00299072 ,0.00061035 , 0.00250244 ,0.00155640 ,0.00112915 ,0.00247192 ,0.00115967 , 0.00283813 ,0.00115967 ,0.00204468 ,0.00238037 ,0.00125122 , 0.00320435 ,0.00039673 ,0.00320435 ,0.00079346 ,0.00280762 , 0.00119019 ,0.00247192 ,0.00198364 ,0.00201416 ,0.00204468 , 0.00106812 ,0.00341797 ,0.00064087 ,0.00381470 ,0.00024414 , 0.00286865 ,0.00115967 ,0.00244141 ,0.00161743 ,0.00241089 , 0.00158691 ,0.00201416 ,0.00244141 ,0.00250244 ,0.00064087 , 0.00381470 ,0.00021362 ,0.00384521 ,0.00103760 ,0.00256348 , 0.00189209 ,0.00256348 ,0.00238037 ,0.00204468 ,0.00244141 , 0.00201416 ,0.00289917 ,0.00201416 ,0.00244141 ,0.00155640 , 0.00292969 ,0.00198364 ,0.00207520 ,0.00369263 ,0.00033569 , 0.00454712 ,0.00082397 ,0.00366211 ,0.00125122 ,0.00366211 , 0.00122070 ,0.00326538 ,0.00247192 ,0.00119019 ,0.00415039 , 0.00115967 ,0.00332642 ,0.00198364 ,0.00250244 ,0.00244141 , 0.00283813 ,0.00253296 ,0.00195312 ,0.00338745 ,0.00106812 , 0.00384521 ,0.00149536 ,0.00296021 ,0.00286865 ,0.00244141 , 0.00247192 ,0.00332642 ,0.00112915 ,0.00378418 ,0.00201416 , 0.00286865 ,0.00250244 ,0.00283813 ,0.00161743 ,0.00460815 , 0.00030518 ,0.00463867 ,0.00155640 ,0.00332642 ,0.00289917 , 0.00201416 ,0.00378418 ,0.00112915 ,0.00509644 ,0.00064087 , 0.00473022 ,0.00149536 ,0.00384521 ,0.00195312 ,0.00427246 , 0.00149536 ,0.00384521 ,0.00238037 ,0.00299072 ,0.00277710 , 0.00299072 ,0.00238037 ,0.00338745 ,0.00283813 ,0.00250244 , 0.00372314 ,0.00161743 ,0.00375366 ,0.00247192 ,0.00244141 , 0.00375366 ,0.00204468 ,0.00329590 ,0.00338745 ,0.00241089 , 0.00292969 ,0.00283813 ,0.00341797 ,0.00277710 ,0.00387573 , 0.00195312 ,0.00381470 ,0.00244141 ,0.00329590 ,0.00338745 , 0.00283813 ,0.00384521 ,0.00238037 ,0.00381470 ,0.00329590 , 0.00296021 ,0.00369263 ,0.00253296 ,0.00411987 ,0.00207520 , 0.00460815 ,0.00164795 ,0.00454712 ,0.00210571 ,0.00415039 , 0.00292969 ,0.00332642 ,0.00329590 ,0.00384521 ,0.00280762 , 0.00430298 ,0.00283813 ,0.00332642 ,0.00381470 ,0.00283813 , 0.00381470 ,0.00286865 ,0.00381470 ,0.00234985 ,0.00436401 , 0.00228882 ,0.00393677 ,0.00317383 ,0.00299072 ,0.00418091 , 0.00244141 ,0.00427246 ,0.00238037 ,0.00466919 ,0.00204468 , 0.00460815 ,0.00250244 ,0.00418091 ,0.00244141 ,0.00424194 , 0.00241089 ,0.00469971 ,0.00195312 ,0.00469971 ,0.00198364 , 0.00466919 ,0.00244141 ,0.00375366 ,0.00247192 ,0.00463867 , 0.00247192 ,0.00375366 ,0.00378418 ,0.00198364 ,0.00555420 , 0.00198364 ,0.00381470 ,0.00372314 ,0.00292969 ,0.00329590 , 0.00424194 ,0.00198364 ,0.00512695 ,0.00241089 ,0.00424194 , 0.00244141 ,0.00424194 ,0.00286865 ,0.00463867 ,0.00204468 , 0.00460815 ,0.00207520 ,0.00460815 ,0.00286865 ,0.00341797 , 0.00323486 ,0.00341797 ,0.00369263 ,0.00204468 ,0.00512695 , 0.00109863 ,0.00555420 ,0.00109863 ,0.00555420 ,0.00201416 , 0.00335693 ,0.00372314 ,0.00292969 ,0.00332642 ,0.00375366 , 0.00250244 ,0.00415039 ,0.00292969 ,0.00332642 ,0.00375366 , 0.00204468 ,0.00463867 ,0.00244141 ,0.00378418 ,0.00286865 , 0.00378418 ,0.00247192 ,0.00372314 ,0.00338745 ,0.00283813 , 0.00427246 ,0.00238037 ,0.00384521 ,0.00323486 ,0.00341797 , 0.00329590 ,0.00332642 ,0.00335693 ,0.00375366 ,0.00286865 , 0.00384521 ,0.00234985 ,0.00476074 ,0.00149536 ,0.00515747 , 0.00195312 ,0.00469971 ,0.00241089 ,0.00381470 ,0.00286865 , 0.00332642 ,0.00378418 ,0.00244141 ,0.00421143 ,0.00158691 , 0.00506592 ,0.00161743 ,0.00460815 ,0.00158691 ,0.00466919 , 0.00198364 ,0.00381470 ,0.00329590 ,0.00247192 ,0.00421143 , 0.00244141 ,0.00335693 ,0.00286865 ,0.00335693 ,0.00286865 , 0.00332642 ,0.00289917 ,0.00292969 ,0.00326538 ,0.00296021 , 0.00372314 ,0.00244141 ,0.00381470 ,0.00241089 ,0.00292969 , 0.00421143 ,0.00152588 ,0.00427246 ,0.00238037 ,0.00299072 , 0.00369263 ,0.00250244 ,0.00375366 ,0.00244141 ,0.00332642 , 0.00292969 ,0.00286865 ,0.00335693 ,0.00244141 ,0.00329590 , 0.00292969 ,0.00247192 ,0.00372314 ,0.00204468 ,0.00375366 , 0.00158691 ,0.00424194 ,0.00149536 ,0.00384521 ,0.00286865 , 0.00155640 ,0.00427246 ,0.00061035 ,0.00473022 ,0.00155640 , 0.00283813 ,0.00299072 ,0.00280762 ,0.00253296 ,0.00326538 , 0.00204468 ,0.00286865 ,0.00292969 ,0.00244141 ,0.00244141 , 0.00335693 ,0.00109863 ,0.00427246 ,0.00106812 ,0.00381470 , 0.00198364 ,0.00292969 ,0.00244141 ,0.00286865 ,0.00204468 , 0.00289917 ,0.00241089 ,0.00296021 ,0.00192261 ,0.00341797 , 0.00198364 ,0.00286865 ,0.00247192 ,0.00201416 ,0.00335693 , 0.00195312 ,0.00296021 ,0.00238037 ,0.00210571 ,0.00326538 , 0.00158691 ,0.00335693 ,0.00152588 ,0.00384521 ,0.00152588 , 0.00335693 ,0.00198364 ,0.00161743 ,0.00372314 ,0.00161743 , 0.00289917 ,0.00198364 ,0.00204468 ,0.00286865 ,0.00158691 , 0.00335693 ,0.00112915 ,0.00244141 ,0.00247192 ,0.00198364 , 0.00204468 ,0.00244141 ,0.00115967 ,0.00329590 ,0.00161743 , 0.00195312 ,0.00253296 ,0.00149536 ,0.00250244 ,0.00244141 , 0.00067139 ,0.00384521 ,0.00061035 ,0.00296021 ,0.00195312 , 0.00119019 ,0.00329590 ,0.00070190 ,0.00289917 ,0.00112915 , 0.00289917 ,0.00112915 ,0.00198364 ,0.00207520 ,0.00152588 , 0.00247192 ,0.00204468 ,0.00106812 ,0.00296021 ,0.00067139 , 0.00286865 ,0.00119019 ,0.00241089 ,0.00115967 ,0.00155640 , 0.00247192 ,0.00067139 ,0.00289917 ,0.00115967 ,0.00155640 , 0.00201416 ,0.00115967 ,0.00241089 ,0.00115967 ,0.00158691 , 0.00152588 ,0.00164795 ,0.00152588 ,0.00158691 ,0.00115967 , 0.00195312 ,0.00122070 ,0.00192261 ,0.00079346 ,0.00195312 , 0.00115967 ,0.00201416 ,0.00155640 ,0.00115967 ,0.00155640 , 0.00161743 ,0.00106812 ,0.00207520 ,0.00067139 ,0.00198364 , 0.00207520 ,-0.00021362 ,0.00286865 ,0.00033569 ,0.00231934 , 0.00082397 ,0.00149536 ,0.00076294 ,0.00280762 ,-0.00097656 , 0.00415039 ,-0.00143433 ,0.00372314 ,-0.00061035 ,0.00244141 , 0.00073242 ,0.00155640 ,0.00073242 ,0.00106812 ,0.00119019 , 0.00112915 ,0.00112915 ,0.00073242 ,0.00106812 ,0.00115967 , 0.00073242 ,0.00152588 ,0.00033569 ,0.00238037 ,-0.00061035 , 0.00250244 ,-0.00070190 ,0.00213623 ,0.00012207 ,0.00125122 , 0.00103760 ,-0.00009155 ,0.00195312 ,-0.00015259 ,0.00155640 , 0.00024414 ,0.00158691 ,0.00070190 ,0.00067139 ,0.00076294 , 0.00103760 ,0.00033569 ,0.00106812 ,0.00073242 ,0.00073242 , 0.00149536 ,-0.00054932 ,0.00241089 ,-0.00106812 ,0.00207520 , 0.00018311 ,0.00030518 ,0.00067139 ,0.00112915 ,-0.00012207 , 0.00149536 ,-0.00012207 ,0.00152588 ,-0.00057983 ,0.00198364 , -0.00057983 ,0.00109863 ,0.00027466 ,0.00024414 ,0.00070190 , 0.00024414 ,0.00027466 ,-0.00015259 ,0.00103760 ,-0.00051880 , 0.00149536 ,-0.00100708 ,0.00112915 ,-0.00064087 ,0.00112915 , -0.00103760 ,0.00112915 ,-0.00064087 ,0.00073242 ,-0.00064087 , 0.00112915 ,-0.00146484 ,0.00149536 ,-0.00051880 ,-0.00024414 , 0.00073242 ,-0.00061035 ,0.00018311 ,-0.00006104 ,0.00012207 , -0.00048828 ,0.00103760 ,-0.00100708 ,0.00198364 ,-0.00234985 , 0.00198364 ,-0.00143433 ,0.00061035 ,-0.00012207 ,-0.00024414 , -0.00009155 ,-0.00024414 ,0.00030518 ,-0.00067139 ,0.00027466 , -0.00018311 ,-0.00057983 ,0.00018311 ,-0.00054932 ,0.00018311 , -0.00054932 ,-0.00024414 ,-0.00012207 ,-0.00067139 ,0.00030518 , -0.00064087 ,0.00024414 ,-0.00103760 ,0.00024414 ,-0.00061035 , -0.00015259 ,-0.00067139 ,-0.00012207 ,-0.00115967 ,-0.00006104 , -0.00115967 ,-0.00054932 ,-0.00021362 ,-0.00109863 ,-0.00051880 , -0.00027466 ,-0.00189209 ,0.00067139 ,-0.00189209 ,0.00018311 , -0.00051880 ,-0.00161743 ,0.00033569 ,-0.00238037 ,0.00106812 , -0.00183105 ,0.00015259 ,-0.00146484 ,-0.00061035 ,-0.00018311 , -0.00109863 ,-0.00009155 ,-0.00161743 ,-0.00006104 ,-0.00115967 , -0.00012207 ,-0.00155640 ,-0.00012207 ,-0.00109863 ,-0.00057983 , -0.00067139 ,-0.00189209 ,0.00064087 ,-0.00189209 ,-0.00109863 , 0.00073242 ,-0.00326538 ,0.00112915 ,-0.00192261 ,-0.00109863 , 0.00030518 ,-0.00241089 ,0.00030518 ,-0.00244141 ,-0.00012207 , -0.00155640 ,-0.00054932 ,-0.00158691 ,-0.00051880 ,-0.00204468 , -0.00006104 ,-0.00204468 ,-0.00009155 ,-0.00204468 ,-0.00006104 , -0.00247192 ,0.00033569 ,-0.00244141 ,-0.00012207 ,-0.00155640 , -0.00143433 ,-0.00070190 ,-0.00140381 ,-0.00115967 ,-0.00051880 , -0.00201416 ,-0.00012207 ,-0.00241089 ,0.00024414 ,-0.00277710 , 0.00021362 ,-0.00231934 ,-0.00024414 ,-0.00231934 ,0.00064087 , -0.00317383 ,0.00103760 ,-0.00314331 ,-0.00027466 ,-0.00146484 , -0.00146484 ,-0.00112915 ,-0.00143433 ,-0.00109863 ,-0.00149536 , -0.00061035 ,-0.00234985 ,-0.00070190 ,-0.00183105 ,-0.00073242 , -0.00183105 ,-0.00198364 ,-0.00064087 ,-0.00231934 ,-0.00070190 , -0.00231934 ,-0.00106812 ,-0.00149536 ,-0.00149536 ,-0.00152588 , -0.00149536 ,-0.00192261 ,-0.00106812 ,-0.00189209 ,-0.00070190 , -0.00274658 ,0.00021362 ,-0.00408936 ,0.00152588 ,-0.00497437 , 0.00112915 ,-0.00329590 ,-0.00051880 ,-0.00164795 ,-0.00222778 , -0.00119019 ,-0.00183105 ,-0.00155640 ,-0.00103760 ,-0.00283813 , -0.00057983 ,-0.00244141 ,-0.00100708 ,-0.00195312 ,-0.00152588 , -0.00186157 ,-0.00112915 ,-0.00152588 ,-0.00186157 ,-0.00201416 , -0.00143433 ,-0.00149536 ,-0.00201416 ,-0.00140381 ,-0.00158691 , -0.00186157 ,-0.00201416 ,-0.00143433 ,-0.00195312 ,-0.00109863 , -0.00228882 ,-0.00164795 ,-0.00268555 ,-0.00067139 ,-0.00326538 , -0.00015259 ,-0.00329590 ,-0.00103760 ,-0.00195312 ,-0.00234985 , -0.00106812 ,-0.00241089 ,-0.00143433 ,-0.00158691 ,-0.00228882 , -0.00155640 ,-0.00192261 ,-0.00146484 ,-0.00244141 ,-0.00054932 , -0.00289917 ,-0.00057983 ,-0.00238037 ,-0.00192261 ,-0.00149536 , -0.00195312 ,-0.00195312 ,-0.00103760 ,-0.00241089 ,-0.00097656 , -0.00247192 ,-0.00140381 ,-0.00204468 ,-0.00140381 ,-0.00158691 , -0.00140381 ,-0.00201416 ,-0.00146484 ,-0.00195312 ,-0.00146484 , -0.00244141 ,-0.00143433 ,-0.00198364 ,-0.00189209 ,-0.00109863 , -0.00280762 ,-0.00106812 ,-0.00280762 ,-0.00057983 ,-0.00289917 , -0.00097656 ,-0.00201416 ,-0.00228882 ,-0.00112915 ,-0.00280762 , -0.00103760 ,-0.00195312 ,-0.00192261 ,-0.00238037 ,-0.00106812 , -0.00326538 ,-0.00103760 ,-0.00198364 ,-0.00277710 ,-0.00061035 , -0.00241089 ,-0.00146484 ,-0.00198364 ,-0.00192261 ,-0.00280762 , -0.00061035 ,-0.00323486 ,-0.00070190 ,-0.00317383 ,-0.00155640 , -0.00192261 ,-0.00231934 ,-0.00204468 ,-0.00140381 ,-0.00244141 , -0.00100708 ,-0.00241089 ,-0.00146484 ,-0.00241089 ,-0.00061035 , -0.00326538 ,-0.00061035 ,-0.00234985 ,-0.00241089 ,-0.00061035 , -0.00283813 ,-0.00149536 ,-0.00146484 ,-0.00241089 ,-0.00103760 , -0.00329590 ,-0.00100708 ,-0.00244141 ,-0.00140381 ,-0.00158691 , -0.00231934 ,-0.00198364 ,-0.00146484 ,-0.00241089 ,-0.00100708 , -0.00244141 ,-0.00186157 ,-0.00158691 ,-0.00274658 ,-0.00112915 , -0.00231934 ,-0.00195312 ,-0.00195312 ,-0.00192261 ,-0.00149536 , -0.00195312 ,-0.00149536 ,-0.00280762 ,-0.00061035 ,-0.00286865 , -0.00054932 ,-0.00286865 ,-0.00100708 ,-0.00286865 ,-0.00103760 , -0.00238037 ,-0.00106812 ,-0.00192261 ,-0.00195312 ,-0.00103760 , -0.00241089 ,-0.00149536 ,-0.00146484 ,-0.00241089 ,-0.00061035 , -0.00238037 ,-0.00195312 ,-0.00103760 ,-0.00280762 ,-0.00021362 , -0.00277710 ,-0.00109863 ,-0.00192261 ,-0.00149536 ,-0.00149536 , -0.00192261 ,-0.00112915 ,-0.00183105 ,-0.00161743 ,-0.00225830 , -0.00112915 ,-0.00238037 ,-0.00103760 ,-0.00195312 ,-0.00192261 , -0.00106812 ,-0.00195312 ,-0.00192261 ,-0.00149536 ,-0.00198364 , -0.00097656 ,-0.00247192 ,-0.00097656 ,-0.00195312 ,-0.00158691 , -0.00091553 ,-0.00250244 ,-0.00140381 ,-0.00106812 ,-0.00292969 , 0.00000000 ,-0.00253296 ,-0.00186157 ,-0.00064087 ,-0.00326538 , -0.00057983 ,-0.00198364 ,-0.00146484 ,-0.00155640 ,-0.00143433 , -0.00241089 ,-0.00064087 ,-0.00231934 ,-0.00112915 ,-0.00186157 , -0.00112915 ,-0.00192261 ,-0.00103760 ,-0.00195312 ,-0.00149536 , -0.00106812 ,-0.00149536 ,-0.00195312 ,-0.00012207 ,-0.00335693 , -0.00051880 ,-0.00158691 ,-0.00143433 ,-0.00198364 ,-0.00146484 , -0.00238037 ,-0.00018311 ,-0.00241089 ,-0.00097656 ,-0.00247192 , -0.00054932 ,-0.00283813 ,0.00024414 ,-0.00323486 ,-0.00061035 , -0.00198364 ,-0.00100708 ,-0.00198364 ,-0.00146484 ,-0.00106812 , -0.00195312 ,-0.00103760 ,-0.00152588 ,-0.00192261 ,-0.00018311 , -0.00326538 ,0.00027466 ,-0.00283813 ,-0.00103760 ,-0.00109863 , -0.00146484 ,-0.00195312 ,-0.00061035 ,-0.00238037 ,-0.00015259 , -0.00244141 ,-0.00012207 ,-0.00238037 ,-0.00024414 ,-0.00180054 , -0.00079346 ,-0.00134277 ,-0.00115967 ,-0.00143433 ,-0.00067139 , -0.00231934 ,0.00064087 ,-0.00277710 ,-0.00064087 ,-0.00103760 , -0.00198364 ,-0.00054932 ,-0.00158691 ,-0.00140381 ,-0.00073242 , -0.00183105 ,-0.00112915 ,-0.00100708 ,-0.00155640 ,-0.00100708 , -0.00109863 ,-0.00146484 ,-0.00106812 ,-0.00106812 ,-0.00149536 , -0.00146484 ,-0.00067139 ,-0.00192261 ,-0.00103760 ,-0.00067139 , -0.00140381 ,-0.00115967 ,-0.00054932 ,-0.00286865 ,0.00027466 , -0.00146484 ,-0.00155640 ,-0.00054932 ,-0.00155640 ,-0.00103760 , -0.00149536 ,-0.00021362 ,-0.00143433 ,-0.00067139 ,-0.00103760 , -0.00195312 ,0.00027466 ,-0.00149536 ,-0.00064087 ,-0.00146484 , -0.00024414 ,-0.00097656 ,-0.00112915 ,-0.00012207 ,-0.00244141 , 0.00073242 ,-0.00149536 ,-0.00106812 ,-0.00018311 ,-0.00195312 , 0.00027466 ,-0.00195312 ,-0.00015259 ,-0.00152588 ,-0.00018311 , -0.00149536 ,-0.00061035 ,-0.00109863 ,-0.00097656 ,-0.00073242 , -0.00140381 ,-0.00112915 ,-0.00057983 ,-0.00195312 ,-0.00018311 , -0.00149536 ,-0.00103760 ,-0.00021362 ,-0.00192261 ,0.00024414 , -0.00192261 ,-0.00018311 ,-0.00152588 ,-0.00097656 ,-0.00073242 , -0.00094604 ,-0.00119019 ,-0.00006104 ,-0.00158691 ,-0.00054932 , -0.00024414 ,-0.00143433 ,-0.00024414 ,-0.00100708 ,-0.00067139 , -0.00103760 ,-0.00015259 ,-0.00155640 ,0.00033569 ,-0.00201416 , 0.00033569 ,-0.00161743 ,-0.00003052 ,-0.00073242 ,-0.00143433 , 0.00024414 ,-0.00152588 ,-0.00015259 ,-0.00064087 ,-0.00057983 , -0.00067139 ,-0.00100708 ,-0.00024414 ,-0.00146484 ,-0.00015259 , -0.00064087 ,-0.00152588 ,0.00033569 ,-0.00164795 ,0.00045776 , -0.00210571 ,0.00082397 ,-0.00289917 ,0.00158691 ,-0.00234985 , 0.00067139 ,-0.00189209 ,0.00064087 ,-0.00234985 ,0.00070190 , -0.00195312 ,-0.00015259 ,-0.00021362 ,-0.00238037 ,0.00073242 , -0.00152588 ,-0.00018311 ,-0.00103760 ,-0.00067139 ,-0.00012207 , -0.00024414 ,-0.00097656 ,0.00015259 ,-0.00183105 ,0.00015259 , -0.00009155 ,-0.00112915 ,-0.00009155 ,-0.00119019 ,-0.00048828 , -0.00027466 ,-0.00012207 ,-0.00106812 ,0.00024414 ,-0.00149536 , 0.00024414 ,-0.00057983 ,-0.00064087 ,-0.00103760 ,-0.00021362 , -0.00061035 ,-0.00015259 ,-0.00109863 ,-0.00012207 ,-0.00070190 , -0.00012207 ,-0.00106812 ,0.00021362 ,-0.00143433 ,0.00064087 , -0.00192261 ,0.00030518 ,-0.00073242 ,-0.00094604 ,0.00018311 , -0.00149536 ,-0.00009155 ,-0.00030518 ,-0.00097656 ,0.00024414 , -0.00109863 ,-0.00054932 ,-0.00070190 ,-0.00054932 ,-0.00067139 , 0.00030518 ,-0.00109863 ,0.00027466 ,-0.00106812 ,0.00024414 , -0.00100708 ,0.00061035 ,-0.00097656 ,-0.00024414 ,-0.00057983 , -0.00021362 ,0.00030518 ,-0.00070190 ,-0.00009155 ,-0.00024414 , -0.00015259 ,0.00024414 ,-0.00061035 ,-0.00067139 ,0.00033569 , -0.00112915 ,0.00076294 ,-0.00112915 ,0.00073242 ,-0.00152588 , 0.00073242 ,-0.00106812 ,0.00024414 ,-0.00061035 ,0.00027466 , -0.00109863 ,0.00073242 ,-0.00152588 ,-0.00015259 ,0.00024414 , -0.00106812 ,0.00073242 ,-0.00112915 ,-0.00009155 ,0.00018311 , -0.00146484 ,0.00158691 ,-0.00195312 ,0.00112915 ,-0.00192261 , 0.00115967 ,-0.00198364 ,0.00164795 ,-0.00158691 ,0.00030518 , -0.00018311 ,-0.00061035 ,0.00067139 ,-0.00057983 ,0.00018311 , -0.00054932 ,0.00018311 ,-0.00054932 ,0.00021362 ,-0.00018311 , -0.00057983 ,0.00106812 ,-0.00231934 ,0.00241089 ,-0.00234985 , 0.00155640 ,-0.00149536 ,0.00070190 ,-0.00061035 ,0.00070190 , -0.00067139 ,-0.00012207 ,-0.00024414 ,0.00076294 ,-0.00106812 , 0.00109863 ,-0.00189209 ,0.00152588 ,-0.00143433 ,0.00192261 , -0.00271606 ,0.00238037 ,-0.00234985 ,0.00161743 ,-0.00073242 , -0.00006104 ,-0.00070190 ,0.00073242 ,-0.00106812 ,0.00067139 , -0.00057983 ,-0.00018311 ,0.00021362 ,-0.00054932 ,0.00018311 , -0.00057983 ,0.00027466 ,-0.00021362 ,-0.00018311 ,0.00030518 , -0.00067139 ,0.00073242 ,-0.00106812 ,0.00112915 ,-0.00149536 , 0.00115967 ,-0.00109863 ,0.00073242 ,-0.00018311 ,-0.00018311 , 0.00024414 ,-0.00015259 ,-0.00021362 ,0.00073242 ,-0.00106812 , 0.00155640 ,-0.00146484 ,0.00152588 ,-0.00143433 ,0.00061035 , 0.00036621 ,-0.00119019 ,0.00170898 ,-0.00204468 ,0.00122070 , -0.00070190 ,0.00030518 ,0.00024414 ,-0.00061035 ,0.00070190 , -0.00109863 ,0.00073242 ,-0.00018311 ,-0.00064087 ,0.00073242 , -0.00021362 ,-0.00015259 ,0.00024414 ,-0.00021362 ,-0.00012207 , -0.00021362 ,-0.00018311 ,0.00030518 ,-0.00073242 ,0.00039673 , -0.00030518 ,0.00036621 ,-0.00024414 ,0.00070190 ,-0.00146484 , 0.00106812 ,-0.00054932 ,-0.00021362 ,0.00027466 ,-0.00061035 , 0.00067139 ,-0.00064087 ,0.00076294 ,-0.00112915 ,0.00122070 , -0.00115967 ,0.00164795 ,-0.00109863 ,0.00115967 ,-0.00061035 , 0.00064087 ,-0.00012207 ,0.00064087 ,-0.00057983 ,0.00112915 , -0.00103760 ,0.00064087 ,-0.00015259 ,0.00024414 ,-0.00057983 , 0.00021362 ,-0.00012207 ,0.00015259 ,-0.00006104 ,-0.00027466 , 0.00033569 ,-0.00070190 ,0.00079346 ,-0.00115967 ,0.00167847 , -0.00204468 ,0.00210571 ,-0.00244141 ,0.00207520 ,-0.00109863 , 0.00024414 ,-0.00012207 ,-0.00027466 ,0.00036621 ,-0.00024414 , 0.00024414 ,0.00030518 ,-0.00021362 ,0.00027466 ,-0.00106812 , 0.00112915 ,-0.00103760 ,0.00021362 ,-0.00009155 ,-0.00030518 , 0.00125122 ,-0.00115967 ,0.00119019 ,-0.00064087 ,0.00073242 , -0.00024414 ,0.00033569 ,-0.00024414 ,-0.00018311 ,0.00076294 , -0.00030518 ,0.00039673 ,-0.00027466 ,0.00030518 ,-0.00018311 , 0.00109863 ,-0.00100708 ,0.00018311 ,0.00033569 ,-0.00067139 , 0.00119019 ,-0.00152588 ,0.00067139 ,-0.00057983 ,0.00067139 , -0.00018311 ,-0.00057983 ,0.00061035 ,-0.00094604 ,0.00061035 , -0.00015259 ,-0.00018311 ,0.00067139 ,-0.00100708 ,0.00155640 , -0.00195312 ,0.00201416 ,-0.00195312 ,0.00115967 ,-0.00057983 , 0.00057983 ,-0.00091553 ,0.00143433 ,-0.00137329 ,0.00106812 , -0.00015259 ,-0.00021362 ,0.00033569 ,0.00018311 ,-0.00057983 , 0.00070190 ,-0.00021362 ,-0.00012207 ,0.00109863 ,-0.00064087 , 0.00076294 ,-0.00027466 ,-0.00009155 ,0.00021362 ,0.00024414 , -0.00100708 ,0.00149536 ,-0.00140381 ,0.00149536 ,-0.00143433 , 0.00152588 ,-0.00149536 ,0.00158691 ,-0.00103760 ,0.00021362 , -0.00012207 ,-0.00073242 ,0.00125122 ,-0.00112915 ,0.00115967 , -0.00103760 ,0.00064087 ,-0.00057983 ,0.00027466 ,0.00018311 , -0.00051880 ,0.00015259 ,-0.00051880 ,0.00018311 ,0.00030518 , -0.00067139 ,0.00027466 ,-0.00015259 ,0.00021362 ,-0.00012207 , -0.00070190 ,0.00076294 ,-0.00109863 ,0.00161743 ,-0.00198364 , 0.00161743 ,-0.00109863 ,0.00073242 ,-0.00018311 ,0.00024414 , -0.00061035 ,0.00112915 ,-0.00146484 ,0.00149536 ,-0.00094604 , 0.00057983 ,-0.00051880 ,0.00064087 ,-0.00149536 ,0.00204468 , -0.00198364 ,0.00161743 ,-0.00109863 ,0.00073242 ,-0.00018311 , 0.00067139 ,-0.00146484 ,0.00155640 ,-0.00195312 ,0.00204468 , -0.00195312 ,0.00070190 ,-0.00061035 ,0.00024414 ,-0.00064087 , 0.00073242 ,-0.00152588 ,0.00115967 ,-0.00106812 ,-0.00021362 , 0.00076294 ,-0.00112915 ,0.00161743 ,-0.00195312 ,0.00155640 , -0.00189209 ,0.00152588 ,-0.00103760 ,0.00070190 ,-0.00061035 , -0.00021362 ,0.00030518 ,-0.00115967 ,0.00128174 ,-0.00115967 , 0.00073242 ,-0.00103760 ,0.00018311 ,0.00033569 ,-0.00064087 , 0.00067139 ,-0.00100708 ,0.00106812 ,-0.00100708 ,0.00070190 , -0.00064087 ,0.00027466 ,0.00021362 ,-0.00097656 ,0.00149536 , -0.00143433 ,0.00064087 ,-0.00015259 ,-0.00061035 ,0.00067139 , -0.00103760 ,0.00067139 ,-0.00103760 ,-0.00018311 ,0.00024414 , -0.00061035 ,0.00027466 ,-0.00024414 ,-0.00048828 ,0.00009155 , -0.00045776 ,-0.00033569 ,0.00039673 ,-0.00161743 ,0.00210571 , -0.00198364 ,0.00027466 ,-0.00021362 ,-0.00057983 ,-0.00021362 , 0.00027466 ,-0.00061035 ,-0.00067139 ,-0.00012207 ,-0.00024414 , -0.00009155 ,-0.00027466 ,-0.00054932 ,-0.00024414 ,0.00030518 , -0.00109863 ,0.00076294 ,-0.00201416 ,0.00210571 ,-0.00247192 , 0.00164795 ,-0.00152588 ,0.00024414 ,-0.00015259 ,-0.00067139 , 0.00033569 ,-0.00070190 ,0.00033569 ,-0.00073242 ,-0.00006104 , -0.00024414 ,-0.00018311 ,-0.00057983 ,0.00064087 ,-0.00100708 , 0.00021362 ,-0.00061035 ,-0.00018311 ,0.00030518 ,-0.00067139 , -0.00018311 ,-0.00061035 ,0.00024414 ,-0.00061035 ,0.00070190 , -0.00198364 ,0.00076294 ,-0.00067139 ,-0.00061035 ,0.00073242 , -0.00158691 ,-0.00006104 ,0.00012207 ,-0.00137329 ,0.00149536 , -0.00277710 ,0.00155640 ,-0.00146484 ,0.00018311 ,-0.00048828 , -0.00122070 ,0.00042725 ,-0.00076294 ,-0.00012207 ,-0.00106812 , 0.00024414 ,-0.00057983 ,-0.00021362 ,-0.00018311 ,-0.00106812 , 0.00027466 ,-0.00061035 ,0.00024414 ,-0.00061035 ,-0.00064087 , 0.00027466 ,-0.00064087 ,0.00030518 ,-0.00067139 ,-0.00054932 , 0.00015259 ,-0.00051880 ,-0.00027466 ,-0.00006104 ,-0.00073242 , -0.00009155 ,0.00018311 ,-0.00143433 ,0.00064087 ,-0.00100708 , 0.00021362 ,-0.00015259 ,-0.00018311 ,-0.00109863 ,-0.00012207 , -0.00067139 ,-0.00015259 ,-0.00018311 ,-0.00064087 ,-0.00061035 , -0.00018311 ,-0.00103760 ,0.00067139 ,-0.00189209 ,0.00149536 , -0.00228882 ,0.00103760 ,-0.00137329 ,0.00012207 ,-0.00048828 , -0.00073242 ,0.00033569 ,-0.00155640 ,0.00119019 ,-0.00244141 , 0.00122070 ,-0.00115967 ,0.00033569 ,-0.00064087 ,-0.00018311 , -0.00064087 ,-0.00015259 ,-0.00064087 ,-0.00015259 ,-0.00064087 , 0.00024414 ,-0.00146484 ,0.00109863 ,-0.00097656 ,-0.00030518 , 0.00036621 ,-0.00112915 ,-0.00015259 ,0.00024414 ,-0.00103760 , 0.00024414 ,-0.00106812 ,0.00030518 ,-0.00073242 ,-0.00003052 , -0.00073242 ,-0.00057983 ,0.00027466 ,-0.00070190 ,-0.00094604 , 0.00018311 ,-0.00149536 ,0.00027466 ,-0.00109863 ,-0.00012207 , -0.00112915 ,0.00033569 ,-0.00115967 ,0.00036621 ,-0.00158691 , 0.00030518 ,-0.00149536 ,-0.00021362 ,-0.00057983 ,-0.00064087 , -0.00018311 ,-0.00061035 ,-0.00064087 ,-0.00057983 ,0.00021362 , -0.00189209 ,0.00109863 ,-0.00192261 ,0.00070190 ,-0.00109863 , -0.00054932 ,0.00018311 ,-0.00140381 ,0.00012207 ,-0.00003052 , -0.00122070 ,0.00039673 ,-0.00070190 ,-0.00146484 ,0.00112915 , -0.00192261 ,0.00021362 ,-0.00054932 ,-0.00070190 ,0.00033569 , -0.00155640 ,0.00115967 ,-0.00192261 ,0.00106812 ,-0.00183105 , 0.00061035 ,-0.00146484 ,0.00027466 ,-0.00112915 ,-0.00051880 , -0.00027466 ,-0.00143433 ,0.00067139 ,-0.00192261 ,0.00067139 , -0.00149536 ,0.00027466 ,-0.00109863 ,-0.00054932 ,-0.00027466 , -0.00140381 ,0.00061035 ,-0.00189209 ,0.00024414 ,-0.00106812 , -0.00057983 ,-0.00024414 ,-0.00103760 ,0.00073242 ,-0.00244141 , 0.00122070 ,-0.00158691 ,-0.00012207 ,-0.00021362 ,-0.00146484 , 0.00067139 ,-0.00192261 ,0.00112915 ,-0.00238037 ,0.00112915 , -0.00189209 ,0.00021362 ,-0.00103760 ,-0.00061035 ,-0.00024414 , -0.00094604 ,-0.00027466 ,-0.00146484 ,-0.00018311 ,-0.00018311 , -0.00149536 ,0.00073242 ,-0.00155640 ,-0.00061035 ,0.00027466 , -0.00198364 ,0.00079346 ,-0.00158691 ,-0.00054932 ,-0.00115967 , -0.00051880 ,-0.00115967 ,-0.00006104 ,-0.00115967 ,-0.00054932 , -0.00070190 ,-0.00051880 ,-0.00158691 ,0.00033569 ,-0.00201416 , 0.00030518 ,-0.00109863 ,-0.00186157 ,0.00106812 ,-0.00323486 , 0.00112915 ,-0.00192261 ,-0.00064087 ,-0.00061035 ,-0.00103760 , -0.00109863 ,-0.00057983 ,-0.00067139 ,-0.00100708 ,-0.00021362 , -0.00146484 ,0.00021362 ,-0.00103760 ,-0.00061035 ,-0.00109863 , -0.00100708 ,0.00021362 ,-0.00143433 ,0.00018311 ,-0.00231934 , 0.00064087 ,-0.00277710 ,0.00112915 ,-0.00241089 ,0.00033569 , -0.00073242 ,-0.00137329 ,0.00015259 ,-0.00189209 ,-0.00018311 , -0.00064087 ,-0.00146484 ,0.00021362 ,-0.00186157 ,-0.00027466 , -0.00140381 ,0.00015259 ,-0.00140381 ,-0.00027466 ,-0.00225830 , 0.00057983 ,-0.00225830 ,0.00015259 ,-0.00231934 ,-0.00021362 , -0.00057983 ,-0.00152588 ,-0.00057983 ,-0.00109863 ,-0.00146484 , 0.00021362 ,-0.00186157 ,-0.00070190 ,-0.00054932 ,-0.00198364 , 0.00073242 ,-0.00238037 ,0.00024414 ,-0.00152588 ,-0.00100708 , 0.00021362 ,-0.00189209 ,-0.00021362 ,-0.00106812 ,-0.00103760 , -0.00106812 ,0.00024414 ,-0.00277710 ,0.00152588 ,-0.00320435 , 0.00021362 ,-0.00103760 ,-0.00106812 ,-0.00061035 ,-0.00152588 , 0.00030518 ,-0.00241089 ,0.00073242 ,-0.00329590 ,0.00115967 , -0.00283813 ,0.00073242 ,-0.00195312 ,-0.00106812 ,-0.00015259 }; static const unsigned defaultKlick1Length = sizeof(defaultKlick1) / sizeof(*defaultKlick1); #endif muse-4.0.0/src/muse/klick2.h000066400000000000000000003544731404105311000155610ustar00rootroot00000000000000#ifndef __KLICK_2_H__ #define __KLICK_2_H__ static const float defaultKlick2[] = { 0.00335693 ,0.00280762 ,0.00061035 ,-0.00332642 ,-0.00582886 , -0.00125122 ,0.00863647 ,0.02160645 ,0.06506348 ,0.17977905 , 0.31439209 ,0.29367065 ,0.03240967 ,-0.30532837 ,-0.49655151 , -0.48089600 ,-0.32238770 ,-0.09667969 ,0.12036133 ,0.22760010 , 0.15548706 ,-0.02197266 ,-0.15612793 ,-0.17373657 ,-0.10150146 , -0.00933838 ,0.02145386 ,-0.03582764 ,-0.11325073 ,-0.16659546 , -0.24795532 ,-0.34259033 ,-0.32888794 ,-0.20693970 ,-0.06787109 , 0.15618896 ,0.45187378 ,0.49865723 ,0.15447998 ,-0.33529663 , -0.74749756 ,-1.00000000 ,-0.93637085 ,-0.48342896 ,0.15921021 , 0.68582153 ,0.88446045 ,0.71105957 ,0.28991699 ,-0.15585327 , -0.39526367 ,-0.31649780 ,-0.03778076 ,0.20761108 ,0.28518677 , 0.17535400 ,-0.10687256 ,-0.41546631 ,-0.52548218 ,-0.38049316 , -0.13024902 ,0.06692505 ,0.11901855 ,0.02633667 ,-0.14367676 , -0.32247925 ,-0.39944458 ,-0.27078247 ,-0.01477051 ,0.20272827 , 0.34716797 ,0.40884399 ,0.29232788 ,0.00036621 ,-0.32006836 , -0.57183838 ,-0.70855713 ,-0.61483765 ,-0.25482178 ,0.21148682 , 0.59417725 ,0.80264282 ,0.79196167 ,0.56710815 ,0.24877930 , 0.01135254 ,-0.08837891 ,-0.13662720 ,-0.20538330 ,-0.27420044 , -0.32345581 ,-0.35009766 ,-0.29962158 ,-0.13128662 ,0.08996582 , 0.26254272 ,0.35791016 ,0.37429810 ,0.29473877 ,0.15496826 , 0.04550171 ,-0.01416016 ,-0.11349487 ,-0.28491211 ,-0.43899536 , -0.47390747 ,-0.33035278 ,0.00146484 ,0.40637207 ,0.68029785 , 0.71371460 ,0.55899048 ,0.33197021 ,0.12799072 ,0.02105713 , 0.05090332 ,0.18551636 ,0.32980347 ,0.39401245 ,0.34603882 , 0.22470093 ,0.11938477 ,0.09051514 ,0.11984253 ,0.15646362 , 0.16885376 ,0.13925171 ,0.07278442 ,0.01129150 ,-0.00714111 , 0.01678467 ,0.06909180 ,0.14749146 ,0.23715210 ,0.29595947 , 0.27432251 ,0.14285278 ,-0.04486084 ,-0.16458130 ,-0.13943481 , 0.01849365 ,0.25289917 ,0.48001099 ,0.61224365 ,0.62631226 , 0.57766724 ,0.51562500 ,0.43490601 ,0.31896973 ,0.19226074 , 0.09835815 ,0.05374146 ,0.06362915 ,0.12777710 ,0.22683716 , 0.31756592 ,0.37387085 ,0.39880371 ,0.39987183 ,0.38305664 , 0.35998535 ,0.32321167 ,0.25909424 ,0.18386841 ,0.13406372 , 0.12487793 ,0.15475464 ,0.21270752 ,0.27752686 ,0.32202148 , 0.31970215 ,0.26669312 ,0.18890381 ,0.12893677 ,0.12304688 , 0.18136597 ,0.27404785 ,0.36184692 ,0.41662598 ,0.41769409 , 0.34896851 ,0.23184204 ,0.12628174 ,0.08245850 ,0.09667969 , 0.13232422 ,0.16226196 ,0.17312622 ,0.16003418 ,0.13644409 , 0.11282349 ,0.10183716 ,0.11639404 ,0.16372681 ,0.23233032 , 0.29666138 ,0.33114624 ,0.32843018 ,0.29864502 ,0.26202393 , 0.25137329 ,0.28997803 ,0.34994507 ,0.37036133 ,0.30670166 , 0.17257690 ,0.02944946 ,-0.06033325 ,-0.07543945 ,-0.03421021 , 0.03918457 ,0.11489868 ,0.16421509 ,0.17507935 ,0.14877319 , 0.09426880 ,0.03112793 ,-0.02047729 ,-0.04571533 ,-0.03149414 , 0.02395630 ,0.09194946 ,0.13867188 ,0.15777588 ,0.16854858 , 0.18939209 ,0.22058105 ,0.24539185 ,0.24124146 ,0.19769287 , 0.12036133 ,0.03216553 ,-0.03665161 ,-0.06771851 ,-0.06686401 , -0.05444336 ,-0.04626465 ,-0.04776001 ,-0.06518555 ,-0.10665894 , -0.16662598 ,-0.22595215 ,-0.26248169 ,-0.25723267 ,-0.20318604 , -0.11404419 ,-0.02047729 ,0.04711914 ,0.07052612 ,0.04595947 , -0.01565552 ,-0.09091187 ,-0.15591431 ,-0.19149780 ,-0.19076538 , -0.15832520 ,-0.11068726 ,-0.06845093 ,-0.04580688 ,-0.04132080 , -0.04544067 ,-0.05374146 ,-0.07192993 ,-0.10668945 ,-0.15780640 , -0.21755981 ,-0.27450562 ,-0.31857300 ,-0.34094238 ,-0.33602905 , -0.31033325 ,-0.27810669 ,-0.25448608 ,-0.25869751 ,-0.29617310 , -0.34994507 ,-0.39688110 ,-0.41964722 ,-0.40460205 ,-0.35375977 , -0.28927612 ,-0.23709106 ,-0.21078491 ,-0.21124268 ,-0.22732544 , -0.24829102 ,-0.27084351 ,-0.29635620 ,-0.32083130 ,-0.34164429 , -0.35717773 ,-0.36669922 ,-0.36215210 ,-0.33880615 ,-0.30245972 , -0.26214600 ,-0.22393799 ,-0.20007324 ,-0.20733643 ,-0.25100708 , -0.31735229 ,-0.38223267 ,-0.42416382 ,-0.43365479 ,-0.41586304 , -0.38134766 ,-0.33807373 ,-0.29797363 ,-0.27307129 ,-0.27056885 , -0.28787231 ,-0.31195068 ,-0.32952881 ,-0.33041382 ,-0.31375122 , -0.28851318 ,-0.27053833 ,-0.26943970 ,-0.28601074 ,-0.31332397 , -0.33859253 ,-0.34997559 ,-0.34777832 ,-0.34075928 ,-0.33148193 , -0.31558228 ,-0.29071045 ,-0.25827026 ,-0.21981812 ,-0.18621826 , -0.16943359 ,-0.16989136 ,-0.18356323 ,-0.20541382 ,-0.23416138 , -0.26339722 ,-0.28985596 ,-0.31433105 ,-0.33496094 ,-0.34634399 , -0.34649658 ,-0.33615112 ,-0.31457520 ,-0.28308105 ,-0.25024414 , -0.22671509 ,-0.21435547 ,-0.20886230 ,-0.20477295 ,-0.19842529 , -0.18826294 ,-0.17880249 ,-0.17422485 ,-0.17187500 ,-0.16448975 , -0.14865112 ,-0.12789917 ,-0.10833740 ,-0.09609985 ,-0.09393311 , -0.10079956 ,-0.11483765 ,-0.13055420 ,-0.14093018 ,-0.14221191 , -0.14175415 ,-0.14813232 ,-0.16677856 ,-0.19808960 ,-0.23202515 , -0.25512695 ,-0.25988770 ,-0.24603271 ,-0.21511841 ,-0.17489624 , -0.13397217 ,-0.09716797 ,-0.06649780 ,-0.04373169 ,-0.03448486 , -0.04006958 ,-0.05627441 ,-0.07360840 ,-0.08071899 ,-0.07202148 , -0.05154419 ,-0.02621460 ,-0.00332642 ,0.01388550 ,0.02160645 , 0.01809692 ,0.00073242 ,-0.02819824 ,-0.05895996 ,-0.07980347 , -0.08395386 ,-0.07095337 ,-0.04544067 ,-0.01531982 ,0.00790405 , 0.01440430 ,-0.00009155 ,-0.02685547 ,-0.05541992 ,-0.07489014 , -0.07940674 ,-0.06478882 ,-0.03298950 ,0.00894165 ,0.05419922 , 0.09411621 ,0.12338257 ,0.14288330 ,0.15338135 ,0.15206909 , 0.13870239 ,0.12084961 ,0.10653687 ,0.10241699 ,0.11093140 , 0.12844849 ,0.14562988 ,0.15655518 ,0.15527344 ,0.14224243 , 0.12521362 ,0.11294556 ,0.11022949 ,0.11566162 ,0.12597656 , 0.13317871 ,0.12979126 ,0.11785889 ,0.10601807 ,0.10293579 , 0.11096191 ,0.12762451 ,0.14495850 ,0.15048218 ,0.14013672 , 0.12161255 ,0.10803223 ,0.10961914 ,0.12042236 ,0.12963867 , 0.13333130 ,0.13327026 ,0.13629150 ,0.14913940 ,0.16821289 , 0.18325806 ,0.18875122 ,0.18908691 ,0.18649292 ,0.18350220 , 0.17892456 ,0.17761230 ,0.18121338 ,0.18853760 ,0.19610596 , 0.20111084 ,0.19979858 ,0.18911743 ,0.17248535 ,0.15551758 , 0.14083862 ,0.12921143 ,0.12570190 ,0.13647461 ,0.15701294 , 0.17269897 ,0.17337036 ,0.16116333 ,0.14660645 ,0.13681030 , 0.13647461 ,0.14492798 ,0.15792847 ,0.17230225 ,0.18713379 , 0.19931030 ,0.20651245 ,0.20928955 ,0.20742798 ,0.20098877 , 0.19268799 ,0.18939209 ,0.18991089 ,0.19226074 ,0.19207764 , 0.18215942 ,0.16156006 ,0.13876343 ,0.12335205 ,0.11724854 , 0.11798096 ,0.12365723 ,0.13265991 ,0.13845825 ,0.13439941 , 0.12115479 ,0.10733032 ,0.09652710 ,0.08871460 ,0.08868408 , 0.09829712 ,0.11001587 ,0.11138916 ,0.09576416 ,0.07211304 , 0.05865479 ,0.06411743 ,0.08721924 ,0.11972046 ,0.14865112 , 0.16256714 ,0.15585327 ,0.13253784 ,0.10504150 ,0.08364868 , 0.06860352 ,0.05484009 ,0.04498291 ,0.03753662 ,0.02874756 , 0.01647949 ,0.00506592 ,-0.00320435 ,-0.00585938 ,-0.00491333 , -0.00381470 ,-0.00497437 ,-0.01013184 ,-0.01956177 ,-0.02493286 , -0.01992798 ,-0.00885010 ,0.00244141 ,0.01406860 ,0.02722168 , 0.03634644 ,0.03497314 ,0.02087402 ,-0.00234985 ,-0.02868652 , -0.05053711 ,-0.05950928 ,-0.05102539 ,-0.03088379 ,-0.01110840 , 0.00192261 ,0.00198364 ,-0.01220703 ,-0.03475952 ,-0.05624390 , -0.07269287 ,-0.08557129 ,-0.09304810 ,-0.09368896 ,-0.08901978 , -0.07931519 ,-0.06591797 ,-0.05285645 ,-0.04656982 ,-0.04580688 , -0.04980469 ,-0.05996704 ,-0.07720947 ,-0.09765625 ,-0.11322021 , -0.12329102 ,-0.12878418 ,-0.13314819 ,-0.13674927 ,-0.13894653 , -0.13482666 ,-0.12298584 ,-0.11148071 ,-0.10552979 ,-0.10586548 , -0.11044312 ,-0.11544800 ,-0.11740112 ,-0.11572266 ,-0.11059570 , -0.10354614 ,-0.09817505 ,-0.09994507 ,-0.11450195 ,-0.14059448 , -0.16885376 ,-0.18917847 ,-0.19583130 ,-0.18911743 ,-0.17755127 , -0.16830444 ,-0.16351318 ,-0.16357422 ,-0.16885376 ,-0.17758179 , -0.18673706 ,-0.19400024 ,-0.19787598 ,-0.19750977 ,-0.19592285 , -0.19384766 ,-0.19052124 ,-0.18774414 ,-0.19241333 ,-0.20239258 , -0.20733643 ,-0.19918823 ,-0.18191528 ,-0.16577148 ,-0.15664673 , -0.15399170 ,-0.15847778 ,-0.17117310 ,-0.18881226 ,-0.20059204 , -0.20553589 ,-0.20944214 ,-0.21395874 ,-0.21817017 ,-0.22293091 , -0.22860718 ,-0.23480225 ,-0.24093628 ,-0.24267578 ,-0.23645020 , -0.22442627 ,-0.21224976 ,-0.20495605 ,-0.20458984 ,-0.20745850 , -0.20761108 ,-0.20336914 ,-0.19686890 ,-0.19219971 ,-0.19241333 , -0.19470215 ,-0.19354248 ,-0.18750000 ,-0.17965698 ,-0.17590332 , -0.17559814 ,-0.17736816 ,-0.17971802 ,-0.17681885 ,-0.16860962 , -0.16259766 ,-0.16470337 ,-0.17373657 ,-0.18826294 ,-0.20196533 , -0.21011353 ,-0.21197510 ,-0.20651245 ,-0.19219971 ,-0.17462158 , -0.16235352 ,-0.15765381 ,-0.15365601 ,-0.14495850 ,-0.13308716 , -0.12258911 ,-0.11267090 ,-0.10571289 ,-0.10638428 ,-0.11590576 , -0.12457275 ,-0.12683105 ,-0.12347412 ,-0.11749268 ,-0.11254883 , -0.11343384 ,-0.11660767 ,-0.11553955 ,-0.10888672 ,-0.09979248 , -0.09020996 ,-0.08316040 ,-0.08255005 ,-0.08978271 ,-0.10452271 , -0.12329102 ,-0.13565063 ,-0.13891602 ,-0.13482666 ,-0.12951660 , -0.12530518 ,-0.11779785 ,-0.10437012 ,-0.09088135 ,-0.08251953 , -0.07440186 ,-0.06503296 ,-0.05386353 ,-0.04553223 ,-0.04208374 , -0.04162598 ,-0.04147339 ,-0.04098511 ,-0.04428101 ,-0.04864502 , -0.04711914 ,-0.03622437 ,-0.02120972 ,-0.00942993 ,-0.00314331 , 0.00094604 ,0.00405884 ,0.00543213 ,0.00732422 ,0.00961304 , 0.00717163 ,-0.00115967 ,-0.00888062 ,-0.01226807 ,-0.01574707 , -0.02090454 ,-0.02444458 ,-0.02532959 ,-0.02465820 ,-0.01602173 , 0.00057983 ,0.01922607 ,0.03585815 ,0.05014038 ,0.05661011 , 0.05209351 ,0.03808594 ,0.02188110 ,0.01144409 ,0.01455688 , 0.03063965 ,0.05297852 ,0.07455444 ,0.09143066 ,0.09375000 , 0.07849121 ,0.05783081 ,0.04925537 ,0.05429077 ,0.06875610 , 0.08666992 ,0.09927368 ,0.09933472 ,0.09146118 ,0.08306885 , 0.07476807 ,0.06585693 ,0.06277466 ,0.06576538 ,0.07003784 , 0.07131958 ,0.07440186 ,0.08627319 ,0.10592651 ,0.12731934 , 0.14556885 ,0.15447998 ,0.14892578 ,0.13552856 ,0.12249756 , 0.11328125 ,0.10910034 ,0.10940552 ,0.11257935 ,0.12329102 , 0.14175415 ,0.16061401 ,0.17333984 ,0.17935181 ,0.17651367 , 0.16812134 ,0.15863037 ,0.15155029 ,0.14544678 ,0.14138794 , 0.14071655 ,0.14321899 ,0.14801025 ,0.15377808 ,0.16259766 , 0.17297363 ,0.18179321 ,0.18591309 ,0.18841553 ,0.19042969 , 0.19137573 ,0.18957520 ,0.18515015 ,0.17889404 ,0.17245483 , 0.16714478 ,0.16546631 ,0.16708374 ,0.17437744 ,0.18750000 , 0.19995117 ,0.20336914 ,0.20114136 ,0.19772339 ,0.19293213 , 0.18695068 ,0.18377686 ,0.18368530 ,0.18222046 ,0.18234253 , 0.18746948 ,0.19580078 ,0.20065308 ,0.20092773 ,0.19931030 , 0.19256592 ,0.17697144 ,0.15994263 ,0.14889526 ,0.14526367 , 0.15258789 ,0.17004395 ,0.18734741 ,0.19717407 ,0.20123291 , 0.20190430 ,0.19674683 ,0.18481445 ,0.17471313 ,0.16958618 , 0.16854858 ,0.17150879 ,0.18188477 ,0.19607544 ,0.20840454 , 0.21334839 ,0.21072388 ,0.20562744 ,0.20193481 ,0.19924927 , 0.19598389 ,0.19445801 ,0.19253540 ,0.18701172 ,0.17861938 , 0.17660522 ,0.18682861 ,0.19873047 ,0.20068359 ,0.19497681 , 0.18295288 ,0.16589355 ,0.14883423 ,0.13723755 ,0.13226318 , 0.13452148 ,0.14212036 ,0.15313721 ,0.16317749 ,0.16632080 , 0.16354370 ,0.16012573 ,0.15451050 ,0.14675903 ,0.14288330 , 0.14645386 ,0.14944458 ,0.14791870 ,0.14492798 ,0.14617920 , 0.15191650 ,0.15710449 ,0.15634155 ,0.14840698 ,0.13647461 , 0.12573242 ,0.11813354 ,0.11505127 ,0.11267090 ,0.10900879 , 0.10656738 ,0.10900879 ,0.11044312 ,0.10433960 ,0.09371948 , 0.08465576 ,0.08071899 ,0.08444214 ,0.09115601 ,0.09387207 , 0.09207153 ,0.08569336 ,0.07672119 ,0.06942749 ,0.06542969 , 0.06259155 ,0.05914307 ,0.05496216 ,0.04861450 ,0.04067993 , 0.03741455 ,0.04354858 ,0.05471802 ,0.06552124 ,0.07693481 , 0.08407593 ,0.08282471 ,0.07656860 ,0.06713867 ,0.05407715 , 0.04281616 ,0.03906250 ,0.04061890 ,0.04431152 ,0.04421997 , 0.03784180 ,0.03057861 ,0.02853394 ,0.03009033 ,0.03063965 , 0.03152466 ,0.02883911 ,0.02038574 ,0.00985718 ,0.00079346 , -0.00588989 ,-0.01184082 ,-0.01562500 ,-0.01428223 ,-0.01092529 , -0.01235962 ,-0.02154541 ,-0.03195190 ,-0.03289795 ,-0.02337646 , -0.00717163 ,0.00564575 ,0.00808716 ,0.00488281 ,0.00408936 , -0.00149536 ,-0.01794434 ,-0.03402710 ,-0.04339600 ,-0.04763794 , -0.04678345 ,-0.04302979 ,-0.03964233 ,-0.03897095 ,-0.03982544 , -0.04260254 ,-0.04544067 ,-0.04742432 ,-0.04690552 ,-0.04385376 , -0.04220581 ,-0.04458618 ,-0.05389404 ,-0.06622314 ,-0.07397461 , -0.07324219 ,-0.06207275 ,-0.04330444 ,-0.02706909 ,-0.02072144 , -0.02108765 ,-0.02218628 ,-0.02233887 ,-0.02191162 ,-0.02044678 , -0.01837158 ,-0.01611328 ,-0.01745605 ,-0.02172852 ,-0.02627563 , -0.03430176 ,-0.04373169 ,-0.04635620 ,-0.03933716 ,-0.02761841 , -0.01718140 ,-0.01507568 ,-0.02612305 ,-0.04650879 ,-0.06799316 , -0.08142090 ,-0.08605957 ,-0.08248901 ,-0.07351685 ,-0.06219482 , -0.05371094 ,-0.04666138 ,-0.03985596 ,-0.03607178 ,-0.03710938 , -0.03854370 ,-0.03601074 ,-0.03488159 ,-0.03710938 ,-0.03726196 , -0.03427124 ,-0.03323364 ,-0.03976440 ,-0.04882812 ,-0.04364014 , -0.02511597 ,-0.00750732 ,-0.00341797 ,-0.01019287 ,-0.02267456 , -0.03442383 ,-0.04116821 ,-0.04113770 ,-0.03933716 ,-0.03720093 , -0.03280640 ,-0.02731323 ,-0.02233887 ,-0.01483154 ,-0.00622559 , -0.00198364 ,-0.00323486 ,-0.00726318 ,-0.00985718 ,-0.01141357 , -0.01370239 ,-0.01705933 ,-0.01727295 ,-0.01132202 ,-0.00326538 , 0.00421143 ,0.01309204 ,0.01998901 ,0.01785278 ,0.00585938 , -0.00552368 ,-0.01034546 ,-0.00958252 ,-0.00253296 ,0.01022339 , 0.02392578 ,0.03213501 ,0.03289795 ,0.02554321 ,0.01470947 , 0.00564575 ,-0.00109863 ,-0.00219727 ,0.00302124 ,0.01153564 , 0.02215576 ,0.03735352 ,0.04992676 ,0.05322266 ,0.04776001 , 0.04071045 ,0.03616333 ,0.03909302 ,0.04577637 ,0.05126953 , 0.05426025 ,0.05505371 ,0.05380249 ,0.05230713 ,0.05212402 , 0.05538940 ,0.06256104 ,0.07312012 ,0.08316040 ,0.08807373 , 0.08880615 ,0.08834839 ,0.08703613 ,0.08175659 ,0.07409668 , 0.06820679 ,0.06781006 ,0.07321167 ,0.08096313 ,0.08868408 , 0.09545898 ,0.09869385 ,0.09783936 ,0.09796143 ,0.10086060 , 0.10504150 ,0.10763550 ,0.10675049 ,0.10101318 ,0.09295654 , 0.08700562 ,0.08691406 ,0.09729004 ,0.11682129 ,0.13845825 , 0.15194702 ,0.15637207 ,0.15414429 ,0.14706421 ,0.14053345 , 0.13760376 ,0.13803101 ,0.14071655 ,0.14566040 ,0.14819336 , 0.14334106 ,0.13446045 ,0.12973022 ,0.13125610 ,0.13027954 , 0.12393188 ,0.11706543 ,0.11660767 ,0.12268066 ,0.13256836 , 0.14129639 ,0.14724731 ,0.15136719 ,0.15167236 ,0.14566040 , 0.13772583 ,0.13546753 ,0.13821411 ,0.14126587 ,0.14434814 , 0.14801025 ,0.15179443 ,0.15542603 ,0.15832520 ,0.15725708 , 0.15283203 ,0.15017700 ,0.15209961 ,0.15603638 ,0.15823364 , 0.15814209 ,0.15786743 ,0.15780640 ,0.15591431 ,0.15460205 , 0.15969849 ,0.16845703 ,0.17315674 ,0.16891479 ,0.15722656 , 0.14465332 ,0.13635254 ,0.13290405 ,0.13177490 ,0.13507080 , 0.14160156 ,0.14651489 ,0.14767456 ,0.14810181 ,0.14782715 , 0.14239502 ,0.13250732 ,0.12350464 ,0.12020874 ,0.12362671 , 0.13006592 ,0.13375854 ,0.13381958 ,0.13159180 ,0.12985229 , 0.12622070 ,0.11950684 ,0.11560059 ,0.11877441 ,0.12786865 , 0.13919067 ,0.14459229 ,0.14309692 ,0.13995361 ,0.13888550 , 0.13656616 ,0.13146973 ,0.12808228 ,0.12609863 ,0.12243652 , 0.11688232 ,0.11203003 ,0.11102295 ,0.10922241 ,0.10577393 , 0.10617065 ,0.11077881 ,0.11474609 ,0.11572266 ,0.11495972 , 0.11276245 ,0.11257935 ,0.11514282 ,0.11697388 ,0.11453247 , 0.10729980 ,0.10076904 ,0.09881592 ,0.10177612 ,0.10516357 , 0.10675049 ,0.10440063 ,0.09619141 ,0.08633423 ,0.07931519 , 0.07836914 ,0.08218384 ,0.08557129 ,0.08917236 ,0.09518433 , 0.09896851 ,0.09454346 ,0.08157349 ,0.06658936 ,0.05633545 , 0.05514526 ,0.05828857 ,0.06207275 ,0.06536865 ,0.06863403 , 0.07083130 ,0.06890869 ,0.06375122 ,0.05905151 ,0.05548096 , 0.05194092 ,0.04876709 ,0.04528809 ,0.04067993 ,0.03668213 , 0.03582764 ,0.03637695 ,0.03640747 ,0.03567505 ,0.03448486 , 0.03408813 ,0.03460693 ,0.03369141 ,0.02777100 ,0.01620483 , 0.00457764 ,-0.00451660 ,-0.01071167 ,-0.01348877 ,-0.01235962 , -0.01113892 ,-0.01473999 ,-0.01919556 ,-0.02041626 ,-0.02041626 , -0.01849365 ,-0.01461792 ,-0.01385498 ,-0.01895142 ,-0.02951050 , -0.04370117 ,-0.05718994 ,-0.06417847 ,-0.06460571 ,-0.06304932 , -0.06085205 ,-0.05572510 ,-0.04843140 ,-0.04437256 ,-0.04635620 , -0.05633545 ,-0.06854248 ,-0.07571411 ,-0.07736206 ,-0.07907104 , -0.08312988 ,-0.08728027 ,-0.08975220 ,-0.09338379 ,-0.09884644 , -0.10415649 ,-0.10958862 ,-0.11535645 ,-0.12008667 ,-0.12414551 , -0.12655640 ,-0.12878418 ,-0.13470459 ,-0.14373779 ,-0.14947510 , -0.15039062 ,-0.15213013 ,-0.15502930 ,-0.15774536 ,-0.16091919 , -0.16397095 ,-0.16482544 ,-0.16387939 ,-0.16198730 ,-0.15905762 , -0.15817261 ,-0.15945435 ,-0.16220093 ,-0.16522217 ,-0.16757202 , -0.16943359 ,-0.17706299 ,-0.18884277 ,-0.19677734 ,-0.19964600 , -0.19927979 ,-0.19619751 ,-0.19192505 ,-0.19076538 ,-0.19262695 , -0.19393921 ,-0.19515991 ,-0.19491577 ,-0.19281006 ,-0.19302368 , -0.19494629 ,-0.19448853 ,-0.19488525 ,-0.19955444 ,-0.20526123 , -0.20925903 ,-0.21096802 ,-0.20800781 ,-0.19934082 ,-0.18771362 , -0.18191528 ,-0.18328857 ,-0.18634033 ,-0.18887329 ,-0.19094849 , -0.19259644 ,-0.19262695 ,-0.19119263 ,-0.18786621 ,-0.18548584 , -0.18106079 ,-0.17459106 ,-0.17126465 ,-0.17654419 ,-0.18435669 , -0.18768311 ,-0.18280029 ,-0.17416382 ,-0.16485596 ,-0.15921021 , -0.15963745 ,-0.16870117 ,-0.18167114 ,-0.18933105 ,-0.18777466 , -0.17855835 ,-0.16442871 ,-0.15121460 ,-0.14443970 ,-0.14471436 , -0.14535522 ,-0.14395142 ,-0.14559937 ,-0.15185547 ,-0.15655518 , -0.15536499 ,-0.14828491 ,-0.14114380 ,-0.13735962 ,-0.13861084 , -0.14053345 ,-0.14102173 ,-0.13879395 ,-0.13110352 ,-0.12005615 , -0.11294556 ,-0.11297607 ,-0.11727905 ,-0.12173462 ,-0.12573242 , -0.12777710 ,-0.12612915 ,-0.12002563 ,-0.11224365 ,-0.10751343 , -0.10739136 ,-0.11132812 ,-0.11416626 ,-0.11495972 ,-0.11254883 , -0.10974121 ,-0.11004639 ,-0.10998535 ,-0.10610962 ,-0.10144043 , -0.09759521 ,-0.09368896 ,-0.09191895 ,-0.09185791 ,-0.08929443 , -0.08285522 ,-0.07290649 ,-0.06414795 ,-0.05828857 ,-0.05746460 , -0.06005859 ,-0.06365967 ,-0.06652832 ,-0.06945801 ,-0.07025146 , -0.06735229 ,-0.06555176 ,-0.06704712 ,-0.06900024 ,-0.06814575 , -0.06707764 ,-0.06661987 ,-0.06512451 ,-0.06225586 ,-0.06039429 , -0.06216431 ,-0.06417847 ,-0.06625366 ,-0.06756592 ,-0.06546021 , -0.05944824 ,-0.05471802 ,-0.05432129 ,-0.05596924 ,-0.05783081 , -0.05599976 ,-0.05050659 ,-0.04418945 ,-0.03829956 ,-0.03634644 , -0.03759766 ,-0.03918457 ,-0.03854370 ,-0.03793335 ,-0.03646851 , -0.03564453 ,-0.03656006 ,-0.03659058 ,-0.03439331 ,-0.02984619 , -0.02798462 ,-0.02838135 ,-0.02966309 ,-0.02770996 ,-0.02282715 , -0.01483154 ,-0.00717163 ,-0.00344849 ,-0.00625610 ,-0.01290894 , -0.01876831 ,-0.01821899 ,-0.01431274 ,-0.00970459 ,-0.00738525 , -0.00836182 ,-0.01202393 ,-0.01589966 ,-0.01654053 ,-0.01345825 , -0.01080322 ,-0.00878906 ,-0.00546265 ,-0.00152588 ,-0.00079346 , -0.00219727 ,-0.00149536 ,0.00009155 ,0.00073242 ,0.00122070 , 0.00424194 ,0.01019287 ,0.01556396 ,0.02017212 ,0.02005005 , 0.01477051 ,0.00793457 ,0.00393677 ,0.00512695 ,0.00939941 , 0.01293945 ,0.01455688 ,0.01794434 ,0.02316284 ,0.02685547 , 0.02493286 ,0.01815796 ,0.01049805 ,0.00817871 ,0.01336670 , 0.02310181 ,0.03204346 ,0.03558350 ,0.03247070 ,0.02450562 , 0.01724243 ,0.01480103 ,0.01696777 ,0.01895142 ,0.01922607 , 0.01852417 ,0.02026367 ,0.02438354 ,0.02578735 ,0.02255249 , 0.01879883 ,0.01809692 ,0.02081299 ,0.02478027 ,0.02770996 , 0.02890015 ,0.02978516 ,0.02679443 ,0.01776123 ,0.00860596 , 0.00439453 ,0.00515747 ,0.01062012 ,0.01919556 ,0.02444458 , 0.02264404 ,0.01849365 ,0.01574707 ,0.01513672 ,0.01419067 , 0.01525879 ,0.01834106 ,0.02093506 ,0.02056885 ,0.01580811 , 0.00869751 ,0.00390625 ,0.00418091 ,0.00942993 ,0.01504517 , 0.01876831 ,0.01742554 ,0.00885010 ,-0.00582886 ,-0.01898193 , -0.02362061 ,-0.01840210 ,-0.00765991 ,0.00341797 ,0.00881958 , 0.00692749 ,0.00164795 ,-0.00515747 ,-0.01092529 ,-0.01425171 , -0.01437378 ,-0.01248169 ,-0.00775146 ,-0.00238037 ,-0.00061035 , -0.00552368 ,-0.01400757 ,-0.02255249 ,-0.02728271 ,-0.02786255 , -0.02462769 ,-0.01989746 ,-0.01571655 ,-0.01672363 ,-0.02429199 , -0.03366089 ,-0.03881836 ,-0.03652954 ,-0.03085327 ,-0.02450562 , -0.02120972 ,-0.02429199 ,-0.03051758 ,-0.03848267 ,-0.04684448 , -0.05163574 ,-0.05047607 ,-0.04388428 ,-0.03482056 ,-0.02786255 , -0.02966309 ,-0.03973389 ,-0.05072021 ,-0.05816650 ,-0.06115723 , -0.05932617 ,-0.05416870 ,-0.04919434 ,-0.04797363 ,-0.05017090 , -0.05496216 ,-0.05990601 ,-0.06393433 ,-0.06536865 ,-0.06286621 , -0.05682373 ,-0.05215454 ,-0.05441284 ,-0.06127930 ,-0.06677246 , -0.07086182 ,-0.07281494 ,-0.07135010 ,-0.06863403 ,-0.06549072 , -0.06472778 ,-0.06738281 ,-0.07223511 ,-0.07595825 ,-0.07843018 , -0.08343506 ,-0.08898926 ,-0.09237671 ,-0.09402466 ,-0.09350586 , -0.09158325 ,-0.09185791 ,-0.09396362 ,-0.09393311 ,-0.09298706 , -0.09234619 ,-0.09271240 ,-0.09237671 ,-0.09262085 ,-0.09487915 , -0.10012817 ,-0.10476685 ,-0.10226440 ,-0.09194946 ,-0.08261108 , -0.08093262 ,-0.08456421 ,-0.08944702 ,-0.09494019 ,-0.09982300 , -0.10272217 ,-0.10360718 ,-0.10452271 ,-0.10675049 ,-0.11062622 , -0.11508179 ,-0.11984253 ,-0.12371826 ,-0.12496948 ,-0.12319946 , -0.11975098 ,-0.11532593 ,-0.11010742 ,-0.10641479 ,-0.10479736 , -0.10760498 ,-0.11325073 ,-0.11846924 ,-0.12014771 ,-0.11734009 , -0.11318970 ,-0.11007690 ,-0.10882568 ,-0.10861206 ,-0.11068726 , -0.11395264 ,-0.11468506 ,-0.11178589 ,-0.10876465 ,-0.10858154 , -0.11267090 ,-0.11987305 ,-0.12612915 ,-0.12829590 ,-0.12408447 , -0.11682129 ,-0.11001587 ,-0.10650635 ,-0.10565186 ,-0.10763550 , -0.11010742 ,-0.11184692 ,-0.11221313 ,-0.11083984 ,-0.10858154 , -0.10638428 ,-0.10507202 ,-0.10617065 ,-0.10961914 ,-0.11111450 , -0.10485840 ,-0.09329224 ,-0.08612061 ,-0.08737183 ,-0.09332275 , -0.09796143 ,-0.09902954 ,-0.09616089 ,-0.09252930 ,-0.09115601 , -0.09103394 ,-0.09384155 ,-0.09912109 ,-0.10354614 ,-0.10363770 , -0.10104370 ,-0.09722900 ,-0.09442139 ,-0.09197998 ,-0.09161377 , -0.09201050 ,-0.09191895 ,-0.09054565 ,-0.08892822 ,-0.08529663 , -0.07888794 ,-0.07299805 ,-0.07052612 ,-0.06988525 ,-0.07037354 , -0.07235718 ,-0.07678223 ,-0.08184814 ,-0.08276367 ,-0.07550049 , -0.06478882 ,-0.05557251 ,-0.04904175 ,-0.04455566 ,-0.04306030 , -0.04440308 ,-0.04846191 ,-0.05218506 ,-0.05395508 ,-0.05288696 , -0.05004883 ,-0.04595947 ,-0.04171753 ,-0.03836060 ,-0.03900146 , -0.04260254 ,-0.04745483 ,-0.05157471 ,-0.05255127 ,-0.04840088 , -0.04116821 ,-0.03448486 ,-0.03176880 ,-0.03259277 ,-0.03567505 , -0.03674316 ,-0.03305054 ,-0.02453613 ,-0.01446533 ,-0.00765991 , -0.00830078 ,-0.01443481 ,-0.02130127 ,-0.02569580 ,-0.02578735 , -0.02124023 ,-0.01339722 ,-0.00625610 ,-0.00033569 ,0.00167847 , 0.00054932 ,-0.00280762 ,-0.00689697 ,-0.00830078 ,-0.00479126 , 0.00103760 ,0.00775146 ,0.01147461 ,0.01229858 ,0.01110840 , 0.01092529 ,0.01062012 ,0.01010132 ,0.00997925 ,0.01110840 , 0.01214600 ,0.01263428 ,0.01370239 ,0.01641846 ,0.01956177 , 0.02011108 ,0.02157593 ,0.02603149 ,0.02984619 ,0.02905273 , 0.02612305 ,0.02597046 ,0.02972412 ,0.03372192 ,0.03616333 , 0.03710938 ,0.03573608 ,0.03329468 ,0.03262329 ,0.03536987 , 0.03884888 ,0.04132080 ,0.04272461 ,0.04385376 ,0.04397583 , 0.04580688 ,0.04925537 ,0.05047607 ,0.04989624 ,0.04962158 , 0.05148315 ,0.05328369 ,0.05520630 ,0.05755615 ,0.05899048 , 0.05960083 ,0.05661011 ,0.05099487 ,0.04733276 ,0.04861450 , 0.05261230 ,0.05773926 ,0.06417847 ,0.06896973 ,0.06936646 , 0.06674194 ,0.06338501 ,0.06204224 ,0.06118774 ,0.06250000 , 0.06710815 ,0.07406616 ,0.08114624 ,0.08685303 ,0.08843994 , 0.08602905 ,0.07904053 ,0.06900024 ,0.06243896 ,0.06256104 , 0.06814575 ,0.07620239 ,0.08264160 ,0.08633423 ,0.08599854 , 0.08142090 ,0.07525635 ,0.07446289 ,0.07992554 ,0.08660889 , 0.08880615 ,0.08782959 ,0.08407593 ,0.07858276 ,0.07571411 , 0.07815552 ,0.08312988 ,0.08743286 ,0.09219360 ,0.09533691 , 0.09472656 ,0.09194946 ,0.08700562 ,0.08459473 ,0.09039307 , 0.10314941 ,0.11495972 ,0.11804199 ,0.11306763 ,0.10073853 , 0.08697510 ,0.07733154 ,0.07427979 ,0.07507324 ,0.07742310 , 0.08108521 ,0.08392334 ,0.08587646 ,0.08718872 ,0.08538818 , 0.08200073 ,0.07791138 ,0.07672119 ,0.07785034 ,0.08071899 , 0.08535767 ,0.08959961 ,0.09027100 ,0.08724976 ,0.08343506 , 0.08181763 ,0.08248901 ,0.08609009 ,0.09140015 ,0.09582520 , 0.09603882 ,0.09219360 ,0.08636475 ,0.08078003 ,0.07723999 , 0.07583618 ,0.07595825 ,0.07650757 ,0.07736206 ,0.07751465 , 0.07736206 ,0.07595825 ,0.07440186 ,0.07318115 ,0.07437134 , 0.07699585 ,0.07916260 ,0.07925415 ,0.07659912 ,0.07247925 , 0.06933594 ,0.06784058 ,0.06668091 ,0.06509399 ,0.06240845 , 0.05780029 ,0.05532837 ,0.05737305 ,0.06286621 ,0.06842041 , 0.07266235 ,0.07568359 ,0.07586670 ,0.07250977 ,0.06686401 , 0.05966187 ,0.05517578 ,0.05432129 ,0.05804443 ,0.06295776 , 0.06567383 ,0.06387329 ,0.05758667 ,0.05001831 ,0.04498291 , 0.04504395 ,0.05010986 ,0.05551147 ,0.05828857 ,0.05957031 , 0.05969238 ,0.05783081 ,0.05499268 ,0.05316162 ,0.05416870 , 0.05651855 ,0.05505371 ,0.05187988 ,0.04782104 ,0.04339600 , 0.03869629 ,0.03411865 ,0.03247070 ,0.03469849 ,0.03967285 , 0.04541016 ,0.05017090 ,0.05065918 ,0.04711914 ,0.04000854 , 0.03424072 ,0.03228760 ,0.03320312 ,0.03546143 ,0.03820801 , 0.04190063 ,0.04428101 ,0.04223633 ,0.03643799 ,0.02896118 , 0.02526855 ,0.02755737 ,0.03436279 ,0.03863525 ,0.03826904 , 0.03372192 ,0.02917480 ,0.02621460 ,0.02484131 ,0.02514648 , 0.02542114 ,0.02520752 ,0.02661133 ,0.03137207 ,0.03771973 , 0.04129028 ,0.04162598 ,0.03967285 ,0.03634644 ,0.03250122 , 0.02960205 ,0.02716064 ,0.02435303 ,0.02166748 ,0.02133179 , 0.02191162 ,0.02081299 ,0.01684570 ,0.01174927 ,0.00897217 , 0.01055908 ,0.01663208 ,0.02328491 ,0.02877808 ,0.03039551 , 0.02444458 ,0.01574707 ,0.01092529 ,0.01214600 ,0.01663208 , 0.02215576 ,0.02301025 ,0.01898193 ,0.01251221 ,0.00814819 , 0.00817871 ,0.01229858 ,0.01687622 ,0.02148438 ,0.02557373 , 0.02780151 ,0.02746582 ,0.02520752 ,0.02096558 ,0.01559448 , 0.01055908 ,0.01077271 ,0.01660156 ,0.02404785 ,0.02990723 , 0.03164673 ,0.02764893 ,0.02035522 ,0.01449585 ,0.01318359 , 0.01663208 ,0.02304077 ,0.02725220 ,0.02694702 ,0.02355957 , 0.02053833 ,0.01895142 ,0.02008057 ,0.02270508 ,0.02587891 , 0.02661133 ,0.02438354 ,0.01974487 ,0.01635742 ,0.01611328 , 0.01925659 ,0.02496338 ,0.03024292 ,0.03329468 ,0.03427124 , 0.03372192 ,0.03192139 ,0.02896118 ,0.02557373 ,0.02508545 , 0.02883911 ,0.03558350 ,0.04205322 ,0.04678345 ,0.04809570 , 0.04400635 ,0.03689575 ,0.03106689 ,0.03213501 ,0.03839111 , 0.04467773 ,0.04754639 ,0.04760742 ,0.04495239 ,0.04040527 , 0.03588867 ,0.03381348 ,0.03585815 ,0.04122925 ,0.04827881 , 0.05242920 ,0.05163574 ,0.04647827 ,0.03994751 ,0.03860474 , 0.04354858 ,0.05120850 ,0.05670166 ,0.05755615 ,0.05319214 , 0.04708862 ,0.04342651 ,0.04595947 ,0.05343628 ,0.06234741 , 0.06677246 ,0.06600952 ,0.06350708 ,0.06106567 ,0.05886841 , 0.05639648 ,0.05599976 ,0.05538940 ,0.05480957 ,0.05496216 , 0.05722046 ,0.05999756 ,0.06320190 ,0.06600952 ,0.06878662 , 0.06997681 ,0.07098389 ,0.07156372 ,0.07034302 ,0.06753540 , 0.06503296 ,0.06457520 ,0.06518555 ,0.06607056 ,0.06622314 , 0.06500244 ,0.06289673 ,0.06094360 ,0.06152344 ,0.06341553 , 0.06591797 ,0.06640625 ,0.06417847 ,0.06127930 ,0.06008911 , 0.06201172 ,0.06420898 ,0.06674194 ,0.06881714 ,0.06979370 , 0.07034302 ,0.07260132 ,0.07611084 ,0.07922363 ,0.08224487 , 0.08389282 ,0.08325195 ,0.07852173 ,0.07220459 ,0.06719971 , 0.06494141 ,0.06805420 ,0.07498169 ,0.08013916 ,0.07965088 , 0.07470703 ,0.06893921 ,0.06372070 ,0.05923462 ,0.05776978 , 0.06118774 ,0.06872559 ,0.07742310 ,0.08334351 ,0.08422852 , 0.07858276 ,0.06896973 ,0.06066895 ,0.05761719 ,0.06024170 , 0.06793213 ,0.07720947 ,0.08477783 ,0.08639526 ,0.08099365 , 0.07199097 ,0.06445312 ,0.06033325 ,0.06005859 ,0.06274414 , 0.06713867 ,0.07250977 ,0.07638550 ,0.07852173 ,0.08029175 , 0.07928467 ,0.07626343 ,0.07434082 ,0.07424927 ,0.07604980 , 0.07730103 ,0.07617188 ,0.07293701 ,0.06857300 ,0.06594849 , 0.06561279 ,0.06735229 ,0.07049561 ,0.07452393 ,0.07711792 , 0.07653809 ,0.07366943 ,0.06875610 ,0.06549072 ,0.06262207 , 0.06088257 ,0.06115723 ,0.06460571 ,0.06884766 ,0.07211304 , 0.07339478 ,0.06988525 ,0.06289673 ,0.05654907 ,0.05294800 , 0.05264282 ,0.05328369 ,0.05319214 ,0.05288696 ,0.05383301 , 0.05450439 ,0.05364990 ,0.05242920 ,0.05206299 ,0.05184937 , 0.05358887 ,0.05487061 ,0.05459595 ,0.05258179 ,0.05068970 , 0.04998779 ,0.04901123 ,0.04757690 ,0.04656982 ,0.04605103 , 0.04522705 ,0.04492188 ,0.04550171 ,0.04721069 ,0.04791260 , 0.04574585 ,0.04281616 ,0.04031372 ,0.03945923 ,0.03915405 , 0.03823853 ,0.03619385 ,0.03436279 ,0.03192139 ,0.02990723 , 0.02865601 ,0.02749634 ,0.02691650 ,0.02807617 ,0.03018188 , 0.03164673 ,0.03097534 ,0.02816772 ,0.02346802 ,0.01980591 , 0.01788330 ,0.01724243 ,0.01660156 ,0.01565552 ,0.01382446 , 0.01132202 ,0.00851440 ,0.00543213 ,0.00442505 ,0.00527954 , 0.00909424 ,0.01272583 ,0.01452637 ,0.01290894 ,0.00854492 , 0.00289917 ,-0.00164795 ,-0.00509644 ,-0.00717163 ,-0.00866699 , -0.00814819 ,-0.00598145 ,-0.00143433 ,0.00329590 ,0.00582886 , 0.00543213 ,0.00326538 ,-0.00021362 ,-0.00338745 ,-0.00347900 , -0.00134277 ,0.00177002 ,0.00122070 ,-0.00372314 ,-0.01025391 , -0.01422119 ,-0.01528931 ,-0.01296997 ,-0.00881958 ,-0.00436401 , -0.00308228 ,-0.00463867 ,-0.00857544 ,-0.01296997 ,-0.01538086 , -0.01696777 ,-0.01867676 ,-0.02050781 ,-0.02117920 ,-0.02130127 , -0.02178955 ,-0.02160645 ,-0.02014160 ,-0.01852417 ,-0.01751709 , -0.01596069 ,-0.01623535 ,-0.01870728 ,-0.02328491 ,-0.02694702 , -0.02725220 ,-0.02383423 ,-0.01812744 ,-0.01516724 ,-0.01745605 , -0.02224731 ,-0.02679443 ,-0.03012085 ,-0.03192139 ,-0.03128052 , -0.02981567 ,-0.02957153 ,-0.03207397 ,-0.03424072 ,-0.03472900 , -0.03375244 ,-0.03167725 ,-0.02947998 ,-0.02746582 ,-0.02774048 , -0.03125000 ,-0.03613281 ,-0.04223633 ,-0.04632568 ,-0.04641724 , -0.04312134 ,-0.03817749 ,-0.03463745 ,-0.03378296 ,-0.03469849 , -0.03628540 ,-0.03573608 ,-0.03329468 ,-0.03027344 ,-0.02847290 , -0.02716064 ,-0.02789307 ,-0.03018188 ,-0.03503418 ,-0.04025269 , -0.04373169 ,-0.04461670 ,-0.04382324 ,-0.04241943 ,-0.04055786 , -0.03735352 ,-0.03543091 ,-0.03442383 ,-0.03494263 ,-0.03598022 , -0.03811646 ,-0.04043579 ,-0.04129028 ,-0.04104614 ,-0.03973389 , -0.03964233 ,-0.04147339 ,-0.04284668 ,-0.04418945 ,-0.04577637 , -0.04724121 ,-0.04705811 ,-0.04458618 ,-0.03964233 ,-0.03433228 , -0.03057861 ,-0.02951050 ,-0.02899170 ,-0.02947998 ,-0.03176880 , -0.03656006 ,-0.04046631 ,-0.04174805 ,-0.03967285 ,-0.03573608 , -0.03146362 ,-0.02896118 ,-0.02917480 ,-0.03149414 ,-0.03277588 , -0.03253174 ,-0.03173828 ,-0.03036499 ,-0.02966309 ,-0.02954102 , -0.02999878 ,-0.03164673 ,-0.03274536 ,-0.03207397 ,-0.03045654 , -0.02877808 ,-0.02746582 ,-0.02624512 ,-0.02621460 ,-0.02716064 , -0.02755737 ,-0.02682495 ,-0.02697754 ,-0.02755737 ,-0.02532959 , -0.02114868 ,-0.01757812 ,-0.01605225 ,-0.01678467 ,-0.01910400 , -0.02169800 ,-0.02206421 ,-0.01962280 ,-0.01516724 ,-0.01177979 , -0.01150513 ,-0.01348877 ,-0.01599121 ,-0.01788330 ,-0.01916504 , -0.02008057 ,-0.01953125 ,-0.01797485 ,-0.01693726 ,-0.01681519 , -0.01574707 ,-0.01364136 ,-0.01107788 ,-0.00921631 ,-0.00653076 , -0.00360107 ,-0.00042725 ,0.00094604 ,0.00057983 ,-0.00073242 , -0.00241089 ,-0.00442505 ,-0.00546265 ,-0.00561523 ,-0.00491333 , -0.00405884 ,-0.00460815 ,-0.00729370 ,-0.01147461 ,-0.01602173 , -0.01916504 ,-0.01766968 ,-0.01348877 ,-0.00949097 ,-0.00570679 , -0.00323486 ,-0.00094604 ,0.00061035 ,0.00161743 ,0.00265503 , 0.00329590 ,0.00326538 ,0.00286865 ,0.00079346 ,-0.00152588 , -0.00320435 ,-0.00415039 ,-0.00500488 ,-0.00360107 ,-0.00128174 , 0.00173950 ,0.00418091 ,0.00570679 ,0.00704956 ,0.00723267 , 0.00427246 ,-0.00079346 ,-0.00433350 ,-0.00408936 ,-0.00164795 , 0.00021362 ,0.00183105 ,0.00280762 ,0.00183105 ,-0.00122070 , -0.00521851 ,-0.00833130 ,-0.00863647 ,-0.00613403 ,-0.00234985 , 0.00100708 ,0.00469971 ,0.00714111 ,0.00897217 ,0.00979614 , 0.01132202 ,0.01345825 ,0.01589966 ,0.01638794 ,0.01379395 , 0.00958252 ,0.00512695 ,0.00177002 ,0.00076294 ,0.00204468 , 0.00421143 ,0.00625610 ,0.00744629 ,0.00741577 ,0.00793457 , 0.00698853 ,0.00491333 ,0.00311279 ,0.00289917 ,0.00442505 , 0.00582886 ,0.00738525 ,0.00827026 ,0.00769043 ,0.00601196 , 0.00476074 ,0.00518799 ,0.00695801 ,0.00973511 ,0.01129150 , 0.01113892 ,0.00839233 ,0.00427246 ,-0.00021362 ,-0.00262451 , -0.00305176 ,-0.00241089 ,-0.00106812 ,0.00036621 ,0.00161743 , 0.00122070 ,0.00051880 ,0.00015259 ,-0.00009155 ,0.00024414 , 0.00244141 ,0.00601196 ,0.00793457 ,0.00637817 ,0.00143433 , -0.00396729 ,-0.00653076 ,-0.00622559 ,-0.00463867 ,-0.00454712 , -0.00549316 ,-0.00747681 ,-0.00839233 ,-0.00888062 ,-0.00942993 , -0.00875854 ,-0.00646973 ,-0.00466919 ,-0.00405884 ,-0.00460815 , -0.00543213 ,-0.00881958 ,-0.01379395 ,-0.01873779 ,-0.02191162 , -0.02371216 ,-0.02429199 ,-0.02163696 ,-0.01651001 ,-0.01147461 , -0.00946045 ,-0.01043701 ,-0.01324463 ,-0.01770020 ,-0.02334595 , -0.02789307 ,-0.02880859 ,-0.02706909 ,-0.02444458 ,-0.02273560 , -0.02282715 ,-0.02435303 ,-0.02682495 ,-0.02883911 ,-0.03057861 , -0.03234863 ,-0.03424072 ,-0.03497314 ,-0.03408813 ,-0.03372192 , -0.03598022 ,-0.03875732 ,-0.03991699 ,-0.03857422 ,-0.03491211 , -0.03137207 ,-0.03088379 ,-0.03390503 ,-0.04025269 ,-0.04592896 , -0.04992676 ,-0.05062866 ,-0.04846191 ,-0.04412842 ,-0.04022217 , -0.03704834 ,-0.03656006 ,-0.03848267 ,-0.04193115 ,-0.04559326 , -0.04968262 ,-0.05145264 ,-0.05099487 ,-0.04968262 ,-0.04986572 , -0.05014038 ,-0.05142212 ,-0.05242920 ,-0.05267334 ,-0.05105591 , -0.04833984 ,-0.04629517 ,-0.04675293 ,-0.04980469 ,-0.05371094 , -0.05670166 ,-0.05892944 ,-0.05902100 ,-0.05722046 ,-0.05471802 , -0.05181885 ,-0.04949951 ,-0.04870605 ,-0.05087280 ,-0.05508423 , -0.05987549 ,-0.06198120 ,-0.06124878 ,-0.05792236 ,-0.05340576 , -0.05062866 ,-0.04861450 ,-0.04769897 ,-0.04772949 ,-0.04861450 , -0.05145264 ,-0.05584717 ,-0.05981445 ,-0.06173706 ,-0.06115723 , -0.05905151 ,-0.05508423 ,-0.05249023 ,-0.05203247 ,-0.05288696 , -0.05477905 ,-0.05664062 ,-0.05676270 ,-0.05557251 ,-0.05380249 , -0.05297852 ,-0.05456543 ,-0.05743408 ,-0.06079102 ,-0.06210327 , -0.06039429 ,-0.05847168 ,-0.05636597 ,-0.05441284 ,-0.05310059 , -0.05212402 ,-0.05148315 ,-0.05267334 ,-0.05618286 ,-0.06088257 , -0.06414795 ,-0.06506348 ,-0.06460571 ,-0.06234741 ,-0.05789185 , -0.05337524 ,-0.05102539 ,-0.05197144 ,-0.05355835 ,-0.05371094 , -0.05285645 ,-0.05230713 ,-0.05145264 ,-0.04959106 ,-0.04861450 , -0.05044556 ,-0.05258179 ,-0.05386353 ,-0.05422974 ,-0.05435181 , -0.05426025 ,-0.05407715 ,-0.05371094 ,-0.05346680 ,-0.05401611 , -0.05593872 ,-0.05761719 ,-0.05746460 ,-0.05578613 ,-0.05297852 , -0.05032349 ,-0.04846191 ,-0.04760742 ,-0.04760742 ,-0.04830933 , -0.05056763 ,-0.05258179 ,-0.05203247 ,-0.04858398 ,-0.04400635 , -0.04101562 ,-0.03982544 ,-0.03994751 ,-0.04226685 ,-0.04492188 , -0.04800415 ,-0.04977417 ,-0.04870605 ,-0.04486084 ,-0.04003906 , -0.03625488 ,-0.03515625 ,-0.03814697 ,-0.04229736 ,-0.04534912 , -0.04608154 ,-0.04479980 ,-0.04180908 ,-0.03854370 ,-0.03607178 , -0.03414917 ,-0.03317261 ,-0.03219604 ,-0.03213501 ,-0.03283691 , -0.03347778 ,-0.03280640 ,-0.03298950 ,-0.03305054 ,-0.03265381 , -0.03112793 ,-0.02978516 ,-0.02886963 ,-0.02801514 ,-0.02743530 , -0.02603149 ,-0.02307129 ,-0.02038574 ,-0.01794434 ,-0.01751709 , -0.01882935 ,-0.02185059 ,-0.02520752 ,-0.02816772 ,-0.02828979 , -0.02481079 ,-0.01965332 ,-0.01461792 ,-0.01068115 ,-0.01080322 , -0.01473999 ,-0.01937866 ,-0.02087402 ,-0.01831055 ,-0.01461792 , -0.01147461 ,-0.01089478 ,-0.01092529 ,-0.01049805 ,-0.00979614 , -0.01031494 ,-0.01010132 ,-0.00982666 ,-0.01000977 ,-0.01031494 , -0.00985718 ,-0.00869751 ,-0.00717163 ,-0.00579834 ,-0.00512695 , -0.00491333 ,-0.00415039 ,-0.00244141 ,0.00006104 ,0.00195312 , 0.00289917 ,0.00152588 ,-0.00030518 ,-0.00250244 ,-0.00405884 , -0.00524902 ,-0.00503540 ,-0.00369263 ,-0.00073242 ,0.00323486 , 0.00717163 ,0.00875854 ,0.00701904 ,0.00415039 ,0.00192261 , 0.00180054 ,0.00241089 ,0.00329590 ,0.00427246 ,0.00570679 , 0.00582886 ,0.00451660 ,0.00292969 ,0.00112915 ,0.00015259 , 0.00091553 ,0.00341797 ,0.00723267 ,0.01031494 ,0.01177979 , 0.00924683 ,0.00509644 ,0.00158691 ,0.00030518 ,0.00228882 , 0.00628662 ,0.01019287 ,0.01248169 ,0.01184082 ,0.00900269 , 0.00610352 ,0.00555420 ,0.00778198 ,0.01095581 ,0.01272583 , 0.01306152 ,0.01184082 ,0.00927734 ,0.00668335 ,0.00491333 , 0.00537109 ,0.00747681 ,0.01049805 ,0.01278687 ,0.01324463 , 0.01220703 ,0.00961304 ,0.00753784 ,0.00592041 ,0.00546265 , 0.00775146 ,0.01123047 ,0.01425171 ,0.01541138 ,0.01605225 , 0.01641846 ,0.01641846 ,0.01614380 ,0.01577759 ,0.01455688 , 0.01300049 ,0.01242065 ,0.01345825 ,0.01501465 ,0.01693726 , 0.01754761 ,0.01681519 ,0.01480103 ,0.01239014 ,0.01113892 , 0.01095581 ,0.01083374 ,0.01162720 ,0.01376343 ,0.01672363 , 0.01776123 ,0.01696777 ,0.01586914 ,0.01501465 ,0.01480103 , 0.01507568 ,0.01702881 ,0.01834106 ,0.01910400 ,0.01885986 , 0.01834106 ,0.01776123 ,0.01693726 ,0.01733398 ,0.01751709 , 0.01760864 ,0.01712036 ,0.01660156 ,0.01492310 ,0.01126099 , 0.00769043 ,0.00665283 ,0.00799561 ,0.01095581 ,0.01379395 , 0.01605225 ,0.01623535 ,0.01443481 ,0.01138306 ,0.00784302 , 0.00497437 ,0.00415039 ,0.00466919 ,0.00607300 ,0.00723267 , 0.00894165 ,0.00973511 ,0.01110840 ,0.01107788 ,0.00906372 , 0.00708008 ,0.00515747 ,0.00448608 ,0.00482178 ,0.00552368 , 0.00579834 ,0.00491333 ,0.00286865 ,0.00070190 ,-0.00033569 , 0.00064087 ,0.00384521 ,0.00769043 ,0.00952148 ,0.00854492 , 0.00677490 ,0.00564575 ,0.00552368 ,0.00561523 ,0.00680542 , 0.00695801 ,0.00592041 ,0.00357056 ,0.00036621 ,-0.00302124 , -0.00628662 ,-0.00778198 ,-0.00607300 ,-0.00170898 ,0.00341797 , 0.00564575 ,0.00350952 ,-0.00131226 ,-0.00637817 ,-0.00939941 , -0.00894165 ,-0.00534058 ,-0.00051880 ,0.00198364 ,0.00164795 , -0.00170898 ,-0.00592041 ,-0.00973511 ,-0.01156616 ,-0.01016235 , -0.00631714 ,-0.00241089 ,-0.00082397 ,-0.00216675 ,-0.00503540 , -0.00741577 ,-0.00830078 ,-0.00729370 ,-0.00509644 ,-0.00341797 , -0.00283813 ,-0.00311279 ,-0.00354004 ,-0.00338745 ,-0.00207520 , -0.00088501 ,-0.00027466 ,-0.00109863 ,-0.00274658 ,-0.00448608 , -0.00503540 ,-0.00537109 ,-0.00509644 ,-0.00509644 ,-0.00485229 , -0.00485229 ,-0.00616455 ,-0.00814819 ,-0.00897217 ,-0.00845337 , -0.00765991 ,-0.00729370 ,-0.00769043 ,-0.00820923 ,-0.00802612 , -0.00732422 ,-0.00573730 ,-0.00476074 ,-0.00457764 ,-0.00613403 , -0.00885010 ,-0.01181030 ,-0.01242065 ,-0.01004028 ,-0.00640869 , -0.00326538 ,-0.00125122 ,-0.00073242 ,-0.00253296 ,-0.00488281 , -0.00695801 ,-0.00799561 ,-0.00805664 ,-0.00833130 ,-0.00955200 , -0.01077271 ,-0.01098633 ,-0.01068115 ,-0.00949097 ,-0.00851440 , -0.00790405 ,-0.00839233 ,-0.00970459 ,-0.01147461 ,-0.01159668 , -0.01058960 ,-0.01089478 ,-0.01138306 ,-0.01138306 ,-0.01025391 , -0.00799561 ,-0.00457764 ,-0.00128174 ,-0.00015259 ,-0.00131226 , -0.00317383 ,-0.00598145 ,-0.00845337 ,-0.01010132 ,-0.00952148 , -0.00769043 ,-0.00552368 ,-0.00332642 ,-0.00079346 ,0.00140381 , 0.00076294 ,-0.00137329 ,-0.00381470 ,-0.00628662 ,-0.00881958 , -0.01150513 ,-0.01358032 ,-0.01467896 ,-0.01400757 ,-0.01199341 , -0.00888062 ,-0.00582886 ,-0.00436401 ,-0.00473022 ,-0.00585938 , -0.00714111 ,-0.00894165 ,-0.00958252 ,-0.00912476 ,-0.00744629 , -0.00485229 ,-0.00152588 ,-0.00006104 ,-0.00039673 ,-0.00238037 , -0.00512695 ,-0.00692749 ,-0.00750732 ,-0.00711060 ,-0.00616455 , -0.00375366 ,-0.00149536 ,0.00021362 ,-0.00003052 ,-0.00076294 , -0.00143433 ,-0.00097656 ,-0.00015259 ,0.00064087 ,0.00225830 , 0.00341797 ,0.00317383 ,0.00286865 ,0.00268555 ,0.00274658 , 0.00213623 ,0.00140381 ,0.00131226 ,0.00302124 ,0.00610352 , 0.00830078 ,0.00851440 ,0.00762939 ,0.00686646 ,0.00628662 , 0.00692749 ,0.00827026 ,0.00964355 ,0.00973511 ,0.00866699 , 0.00781250 ,0.00659180 ,0.00613403 ,0.00619507 ,0.00741577 , 0.00900269 ,0.01031494 ,0.01089478 ,0.01159668 ,0.01184082 , 0.01004028 ,0.00765991 ,0.00680542 ,0.00793457 ,0.01031494 , 0.01208496 ,0.01266479 ,0.01058960 ,0.00799561 ,0.00588989 , 0.00653076 ,0.00985718 ,0.01477051 ,0.01849365 ,0.02001953 , 0.01882935 ,0.01553345 ,0.01208496 ,0.01007080 ,0.00970459 , 0.01147461 ,0.01382446 ,0.01531982 ,0.01428223 ,0.01156616 , 0.01095581 ,0.01095581 ,0.01278687 ,0.01385498 ,0.01437378 , 0.01464844 ,0.01458740 ,0.01431274 ,0.01470947 ,0.01486206 , 0.01473999 ,0.01367188 ,0.01138306 ,0.00912476 ,0.00796509 , 0.00875854 ,0.01065063 ,0.01287842 ,0.01455688 ,0.01519775 , 0.01528931 ,0.01559448 ,0.01611328 ,0.01641846 ,0.01593018 , 0.01498413 ,0.01461792 ,0.01412964 ,0.01278687 ,0.01144409 , 0.01174927 ,0.01318359 ,0.01391602 ,0.01397705 ,0.01376343 , 0.01367188 ,0.01300049 ,0.01226807 ,0.01290894 ,0.01391602 , 0.01547241 ,0.01562500 ,0.01483154 ,0.01336670 ,0.01345825 , 0.01443481 ,0.01522827 ,0.01425171 ,0.01245117 ,0.01058960 , 0.00866699 ,0.00790405 ,0.00790405 ,0.00976562 ,0.01171875 , 0.01245117 ,0.01229858 ,0.01107788 ,0.01043701 ,0.00973511 , 0.00991821 ,0.01055908 ,0.01150513 ,0.01232910 ,0.01217651 , 0.01193237 ,0.01144409 ,0.01107788 ,0.01077271 ,0.01080322 , 0.01144409 ,0.01278687 ,0.01391602 ,0.01336670 ,0.01083374 , 0.00866699 ,0.00860596 ,0.00979614 ,0.01031494 ,0.00997925 , 0.00936890 ,0.00808716 ,0.00711060 ,0.00643921 ,0.00692749 , 0.00827026 ,0.01016235 ,0.01226807 ,0.01412964 ,0.01519775 , 0.01596069 ,0.01531982 ,0.01504517 ,0.01525879 ,0.01483154 , 0.01339722 ,0.01168823 ,0.00949097 ,0.00756836 ,0.00613403 , 0.00512695 ,0.00482178 ,0.00604248 ,0.00674438 ,0.00827026 , 0.00918579 ,0.01031494 ,0.01101685 ,0.01104736 ,0.01086426 , 0.00933838 ,0.00762939 ,0.00637817 ,0.00595093 ,0.00552368 , 0.00512695 ,0.00479126 ,0.00488281 ,0.00540161 ,0.00585938 , 0.00592041 ,0.00640869 ,0.00662231 ,0.00711060 ,0.00750732 , 0.00765991 ,0.00796509 ,0.00744629 ,0.00531006 ,0.00225830 , 0.00033569 ,-0.00122070 ,-0.00238037 ,-0.00271606 ,-0.00106812 , 0.00164795 ,0.00488281 ,0.00662231 ,0.00595093 ,0.00347900 , 0.00088501 ,-0.00012207 ,0.00042725 ,0.00207520 ,0.00216675 , 0.00115967 ,-0.00109863 ,-0.00180054 ,-0.00100708 ,0.00149536 , 0.00448608 ,0.00546265 ,0.00396729 ,0.00085449 ,-0.00131226 , -0.00164795 ,0.00000000 ,0.00155640 ,0.00314331 ,0.00317383 , 0.00012207 ,-0.00482178 ,-0.00912476 ,-0.00961304 ,-0.00756836 , -0.00378418 ,-0.00100708 ,0.00009155 ,-0.00012207 ,-0.00189209 , -0.00430298 ,-0.00527954 ,-0.00408936 ,-0.00137329 ,0.00131226 , 0.00152588 ,0.00033569 ,-0.00213623 ,-0.00354004 ,-0.00445557 , -0.00491333 ,-0.00418091 ,-0.00280762 ,-0.00125122 ,-0.00106812 , -0.00183105 ,-0.00225830 ,-0.00250244 ,-0.00286865 ,-0.00219727 , -0.00039673 ,0.00219727 ,0.00350952 ,0.00256348 ,-0.00039673 , -0.00473022 ,-0.00817871 ,-0.00860596 ,-0.00512695 ,0.00039673 , 0.00592041 ,0.00942993 ,0.00994873 ,0.00848389 ,0.00543213 , 0.00189209 ,0.00000000 ,0.00000000 ,0.00183105 ,0.00375366 , 0.00491333 ,0.00451660 ,0.00357056 ,0.00238037 ,0.00112915 , 0.00030518 ,-0.00094604 ,-0.00253296 ,-0.00436401 ,-0.00494385 , -0.00399780 ,-0.00302124 ,-0.00289917 ,-0.00399780 ,-0.00482178 , -0.00488281 ,-0.00341797 ,0.00021362 ,0.00399780 ,0.00738525 , 0.00845337 ,0.00704956 ,0.00512695 ,0.00311279 ,0.00314331 , 0.00396729 ,0.00479126 ,0.00469971 ,0.00308228 ,0.00134277 , 0.00115967 ,0.00195312 ,0.00390625 ,0.00582886 ,0.00732422 , 0.00698853 ,0.00399780 ,0.00085449 ,-0.00051880 ,0.00137329 , 0.00393677 ,0.00592041 ,0.00619507 ,0.00579834 ,0.00463867 , 0.00289917 ,0.00091553 ,0.00070190 ,0.00305176 ,0.00540161 , 0.00772095 ,0.00857544 ,0.00924683 ,0.00994873 ,0.01034546 , 0.01000977 ,0.00894165 ,0.00787354 ,0.00701904 ,0.00607300 , 0.00524902 ,0.00473022 ,0.00466919 ,0.00375366 ,0.00415039 , 0.00485229 ,0.00741577 ,0.00952148 ,0.01025391 ,0.00921631 , 0.00741577 ,0.00607300 ,0.00546265 ,0.00497437 ,0.00592041 , 0.00772095 ,0.00906372 ,0.00979614 ,0.00970459 ,0.00915527 , 0.00842285 ,0.00906372 ,0.00949097 ,0.01052856 ,0.01110840 , 0.01281738 ,0.01492310 ,0.01617432 ,0.01531982 ,0.01223755 , 0.00979614 ,0.00851440 ,0.00970459 ,0.01287842 ,0.01467896 , 0.01528931 ,0.01504517 ,0.01425171 ,0.01361084 ,0.01239014 , 0.01275635 ,0.01422119 ,0.01657104 ,0.01721191 ,0.01699829 , 0.01544189 ,0.01446533 ,0.01330566 ,0.01373291 ,0.01538086 , 0.01745605 ,0.01846313 ,0.01852417 ,0.01849365 ,0.01831055 , 0.01943970 ,0.02035522 ,0.02044678 ,0.02011108 ,0.01956177 , 0.01928711 ,0.01846313 ,0.01724243 ,0.01608276 ,0.01489258 , 0.01531982 ,0.01632690 ,0.01904297 ,0.02246094 ,0.02471924 , 0.02502441 ,0.02322388 ,0.02090454 ,0.01852417 ,0.01733398 , 0.01763916 ,0.01797485 ,0.01947021 ,0.02072144 ,0.02258301 , 0.02365112 ,0.02447510 ,0.02416992 ,0.02334595 ,0.02117920 , 0.02038574 ,0.01998901 ,0.02072144 ,0.02246094 ,0.02368164 , 0.02420044 ,0.02243042 ,0.01965332 ,0.01589966 ,0.01220703 , 0.01092529 ,0.01217651 ,0.01553345 ,0.01873779 ,0.02120972 , 0.02075195 ,0.01943970 ,0.01876831 ,0.01943970 ,0.02099609 , 0.02294922 ,0.02416992 ,0.02536011 ,0.02539062 ,0.02468872 , 0.02416992 ,0.02328491 ,0.02328491 ,0.02359009 ,0.02459717 , 0.02478027 ,0.02484131 ,0.02340698 ,0.02108765 ,0.01907349 , 0.01870728 ,0.01931763 ,0.01980591 ,0.02127075 ,0.02236938 , 0.02233887 ,0.02120972 ,0.01980591 ,0.01895142 ,0.01821899 , 0.01892090 ,0.02163696 ,0.02441406 ,0.02551270 ,0.02478027 , 0.02294922 ,0.02133179 ,0.01965332 ,0.01913452 ,0.01913452 , 0.02001953 ,0.02062988 ,0.02050781 ,0.02093506 ,0.02111816 , 0.02023315 ,0.01898193 ,0.01849365 ,0.01892090 ,0.01974487 , 0.01968384 ,0.01940918 ,0.01864624 ,0.01803589 ,0.01724243 , 0.01718140 ,0.01733398 ,0.01791382 ,0.01837158 ,0.01913452 , 0.01962280 ,0.01971436 ,0.01968384 ,0.01864624 ,0.01779175 , 0.01757812 ,0.01821899 ,0.01922607 ,0.01901245 ,0.01779175 , 0.01651001 ,0.01638794 ,0.01803589 ,0.02017212 ,0.02218628 , 0.02276611 ,0.02249146 ,0.02059937 ,0.01858521 ,0.01614380 , 0.01492310 ,0.01422119 ,0.01397705 ,0.01528931 ,0.01751709 , 0.02001953 ,0.02041626 ,0.01934814 ,0.01687622 ,0.01446533 , 0.01269531 ,0.01242065 ,0.01260376 ,0.01406860 ,0.01489258 , 0.01510620 ,0.01519775 ,0.01458740 ,0.01507568 ,0.01617432 , 0.01690674 ,0.01629639 ,0.01614380 ,0.01507568 ,0.01391602 , 0.01229858 ,0.01126099 ,0.01104736 ,0.01113892 ,0.01119995 , 0.01129150 ,0.01107788 ,0.01083374 ,0.01025391 ,0.00964355 , 0.00903320 ,0.00833130 ,0.00808716 ,0.00805664 ,0.00857544 , 0.00848389 ,0.00848389 ,0.00930786 ,0.01074219 ,0.01235962 , 0.01296997 ,0.01336670 ,0.01278687 ,0.01138306 ,0.01062012 , 0.00949097 ,0.00924683 ,0.00863647 ,0.00827026 ,0.00714111 , 0.00619507 ,0.00622559 ,0.00790405 ,0.00952148 ,0.01046753 , 0.00988770 ,0.00836182 ,0.00628662 ,0.00497437 ,0.00442505 , 0.00588989 ,0.00747681 ,0.00955200 ,0.01065063 ,0.01071167 , 0.00918579 ,0.00704956 ,0.00448608 ,0.00228882 ,0.00222778 , 0.00314331 ,0.00512695 ,0.00680542 ,0.00723267 ,0.00595093 , 0.00531006 ,0.00506592 ,0.00610352 ,0.00753784 ,0.00869751 , 0.00909424 ,0.00799561 ,0.00671387 ,0.00442505 ,0.00415039 , 0.00506592 ,0.00659180 ,0.00756836 ,0.00787354 ,0.00793457 , 0.00762939 ,0.00720215 ,0.00711060 ,0.00695801 ,0.00698853 , 0.00729370 ,0.00823975 ,0.00912476 ,0.01004028 ,0.00961304 , 0.00854492 ,0.00817871 ,0.00808716 ,0.00811768 ,0.00817871 , 0.00833130 ,0.00851440 ,0.00750732 ,0.00582886 ,0.00411987 , 0.00277710 ,0.00311279 ,0.00415039 ,0.00665283 ,0.00912476 , 0.01141357 ,0.01141357 ,0.01113892 ,0.01040649 ,0.00909424 , 0.00744629 ,0.00558472 ,0.00491333 ,0.00579834 ,0.00711060 , 0.00793457 ,0.00854492 ,0.00863647 ,0.00833130 ,0.00698853 , 0.00625610 ,0.00546265 ,0.00537109 ,0.00537109 ,0.00582886 , 0.00552368 ,0.00518799 ,0.00534058 ,0.00491333 ,0.00558472 , 0.00573730 ,0.00527954 ,0.00405884 ,0.00299072 ,0.00250244 , 0.00286865 ,0.00332642 ,0.00430298 ,0.00479126 ,0.00604248 , 0.00723267 ,0.00775146 ,0.00814819 ,0.00808716 ,0.00744629 , 0.00695801 ,0.00659180 ,0.00729370 ,0.00790405 ,0.00915527 , 0.00994873 ,0.01062012 ,0.01119995 ,0.01098633 ,0.00982666 , 0.00906372 ,0.00845337 ,0.00854492 ,0.00878906 ,0.00897217 , 0.00958252 ,0.00924683 ,0.00885010 ,0.00869751 ,0.00839233 , 0.00817871 ,0.00866699 ,0.00961304 ,0.01077271 ,0.01129150 , 0.01095581 ,0.00982666 ,0.00762939 ,0.00631714 ,0.00607300 , 0.00744629 ,0.01028442 ,0.01287842 ,0.01428223 ,0.01367188 , 0.01211548 ,0.01092529 ,0.01077271 ,0.01184082 ,0.01373291 , 0.01489258 ,0.01510620 ,0.01428223 ,0.01269531 ,0.01107788 , 0.01004028 ,0.00958252 ,0.00997925 ,0.01031494 ,0.01165771 , 0.01251221 ,0.01303101 ,0.01351929 ,0.01327515 ,0.01342773 , 0.01330566 ,0.01394653 ,0.01486206 ,0.01593018 ,0.01538086 , 0.01403809 ,0.01202393 ,0.01040649 ,0.00997925 ,0.01004028 , 0.01062012 ,0.01174927 ,0.01248169 ,0.01235962 ,0.01235962 , 0.01187134 ,0.01232910 ,0.01199341 ,0.01232910 ,0.01232910 , 0.01315308 ,0.01361084 ,0.01306152 ,0.01229858 ,0.01174927 , 0.01269531 ,0.01419067 ,0.01589966 ,0.01727295 ,0.01721191 , 0.01638794 ,0.01544189 ,0.01409912 ,0.01263428 ,0.01138306 , 0.01089478 ,0.01004028 ,0.00921631 ,0.00955200 ,0.01095581 , 0.01260376 ,0.01358032 ,0.01382446 ,0.01348877 ,0.01193237 , 0.00872803 ,0.00625610 ,0.00448608 ,0.00411987 ,0.00445557 , 0.00500488 ,0.00448608 ,0.00415039 ,0.00497437 ,0.00662231 , 0.00808716 ,0.00967407 ,0.01153564 ,0.01202393 ,0.01190186 , 0.01065063 ,0.00936890 ,0.00823975 ,0.00842285 ,0.00848389 , 0.00860596 ,0.00817871 ,0.00762939 ,0.00582886 ,0.00418091 , 0.00219727 ,0.00173950 ,0.00253296 ,0.00466919 ,0.00772095 , 0.00924683 ,0.00854492 ,0.00653076 ,0.00408936 ,0.00262451 , 0.00326538 ,0.00518799 ,0.00723267 ,0.00775146 ,0.00714111 , 0.00524902 ,0.00384521 ,0.00262451 ,0.00390625 ,0.00689697 , 0.00869751 ,0.00924683 ,0.00833130 ,0.00711060 ,0.00524902 , 0.00250244 ,-0.00003052 ,-0.00134277 ,-0.00064087 ,0.00112915 , 0.00338745 ,0.00552368 ,0.00653076 ,0.00631714 ,0.00460815 , 0.00222778 ,0.00106812 ,0.00000000 ,-0.00024414 ,-0.00125122 , -0.00122070 ,-0.00146484 ,-0.00143433 ,-0.00064087 ,0.00051880 , 0.00219727 ,0.00375366 ,0.00402832 ,0.00302124 ,0.00106812 , -0.00079346 ,-0.00228882 ,-0.00347900 ,-0.00347900 ,-0.00299072 , -0.00238037 ,-0.00253296 ,-0.00320435 ,-0.00381470 ,-0.00430298 , -0.00387573 ,-0.00326538 ,-0.00259399 ,-0.00189209 ,-0.00073242 , 0.00006104 ,0.00015259 ,-0.00100708 ,-0.00216675 ,-0.00347900 , -0.00384521 ,-0.00375366 ,-0.00326538 ,-0.00378418 ,-0.00521851 , -0.00732422 ,-0.00885010 ,-0.00952148 ,-0.01071167 ,-0.01068115 , -0.01016235 ,-0.00869751 ,-0.00735474 ,-0.00640869 ,-0.00680542 , -0.00775146 ,-0.01013184 ,-0.01284790 ,-0.01455688 ,-0.01489258 , -0.01354980 ,-0.01205444 ,-0.01156616 ,-0.01159668 ,-0.01232910 , -0.01281738 ,-0.01303101 ,-0.01248169 ,-0.01168823 ,-0.01132202 , -0.01156616 ,-0.01263428 ,-0.01306152 ,-0.01342773 ,-0.01333618 , -0.01251221 ,-0.01184082 ,-0.01181030 ,-0.01272583 ,-0.01455688 , -0.01626587 ,-0.01776123 ,-0.01846313 ,-0.01828003 ,-0.01742554 , -0.01660156 ,-0.01568604 ,-0.01522827 ,-0.01513672 ,-0.01644897 , -0.01782227 ,-0.01855469 ,-0.01861572 ,-0.01782227 ,-0.01641846 , -0.01458740 ,-0.01434326 ,-0.01449585 ,-0.01528931 ,-0.01638794 , -0.01663208 ,-0.01626587 ,-0.01583862 ,-0.01666260 ,-0.01791382 , -0.01968384 ,-0.02081299 ,-0.02130127 ,-0.02078247 ,-0.01959229 , -0.01815796 ,-0.01779175 ,-0.01818848 ,-0.01922607 ,-0.02001953 , -0.01986694 ,-0.01919556 ,-0.01907349 ,-0.01928711 ,-0.01995850 , -0.02050781 ,-0.02044678 ,-0.01977539 ,-0.01965332 ,-0.01934814 , -0.01940918 ,-0.01821899 ,-0.01766968 ,-0.01748657 ,-0.01812744 , -0.01861572 ,-0.01855469 ,-0.01873779 ,-0.01919556 ,-0.01956177 , -0.01968384 ,-0.01943970 ,-0.01959229 ,-0.02011108 ,-0.02084351 , -0.02090454 ,-0.02020264 ,-0.01931763 ,-0.01892090 ,-0.01889038 , -0.01950073 ,-0.02066040 ,-0.02105713 ,-0.02056885 ,-0.01904297 , -0.01861572 ,-0.01876831 ,-0.01889038 ,-0.01895142 ,-0.01910400 , -0.01925659 ,-0.01882935 ,-0.01882935 ,-0.01809692 ,-0.01794434 , -0.01873779 ,-0.02017212 ,-0.02203369 ,-0.02420044 ,-0.02606201 , -0.02700806 ,-0.02606201 ,-0.02359009 ,-0.02084351 ,-0.01855469 , -0.01702881 ,-0.01766968 ,-0.01977539 ,-0.02209473 ,-0.02313232 , -0.02282715 ,-0.02166748 ,-0.02066040 ,-0.01943970 ,-0.01928711 , -0.02023315 ,-0.02130127 ,-0.02157593 ,-0.02148438 ,-0.02105713 , -0.02087402 ,-0.02066040 ,-0.02093506 ,-0.02148438 ,-0.02224731 , -0.02279663 ,-0.02246094 ,-0.02258301 ,-0.02291870 ,-0.02362061 , -0.02447510 ,-0.02481079 ,-0.02481079 ,-0.02416992 ,-0.02407837 , -0.02349854 ,-0.02328491 ,-0.02386475 ,-0.02502441 ,-0.02557373 , -0.02490234 ,-0.02334595 ,-0.02169800 ,-0.02087402 ,-0.02160645 , -0.02215576 ,-0.02301025 ,-0.02346802 ,-0.02444458 ,-0.02478027 , -0.02508545 ,-0.02426147 ,-0.02325439 ,-0.02258301 ,-0.02151489 , -0.02114868 ,-0.02130127 ,-0.02288818 ,-0.02478027 ,-0.02600098 , -0.02578735 ,-0.02523804 ,-0.02294922 ,-0.02059937 ,-0.01840210 , -0.01773071 ,-0.01809692 ,-0.01898193 ,-0.01986694 ,-0.02053833 , -0.02050781 ,-0.02062988 ,-0.02099609 ,-0.02053833 ,-0.02111816 , -0.02136230 ,-0.02178955 ,-0.02252197 ,-0.02297974 ,-0.02285767 , -0.02267456 ,-0.02307129 ,-0.02413940 ,-0.02529907 ,-0.02539062 , -0.02407837 ,-0.02313232 ,-0.02264404 ,-0.02331543 ,-0.02462769 , -0.02536011 ,-0.02581787 ,-0.02511597 ,-0.02413940 ,-0.02227783 , -0.02124023 ,-0.02062988 ,-0.02175903 ,-0.02359009 ,-0.02545166 , -0.02624512 ,-0.02542114 ,-0.02288818 ,-0.02075195 ,-0.01953125 , -0.01980591 ,-0.02139282 ,-0.02325439 ,-0.02456665 ,-0.02484131 , -0.02474976 ,-0.02511597 ,-0.02523804 ,-0.02532959 ,-0.02462769 , -0.02481079 ,-0.02493286 ,-0.02471924 ,-0.02410889 ,-0.02346802 , -0.02331543 ,-0.02432251 ,-0.02615356 ,-0.02777100 ,-0.02883911 , -0.02880859 ,-0.02780151 ,-0.02615356 ,-0.02435303 ,-0.02288818 , -0.02316284 ,-0.02478027 ,-0.02697754 ,-0.02896118 ,-0.03054810 , -0.03082275 ,-0.02923584 ,-0.02709961 ,-0.02597046 ,-0.02523804 , -0.02539062 ,-0.02615356 ,-0.02743530 ,-0.02862549 ,-0.02960205 , -0.03009033 ,-0.02923584 ,-0.02743530 ,-0.02618408 ,-0.02584839 , -0.02664185 ,-0.02767944 ,-0.02856445 ,-0.02899170 ,-0.02890015 , -0.02862549 ,-0.02847290 ,-0.02832031 ,-0.02917480 ,-0.03018188 , -0.03094482 ,-0.03033447 ,-0.02893066 ,-0.02798462 ,-0.02780151 , -0.02835083 ,-0.02902222 ,-0.03015137 ,-0.03027344 ,-0.03045654 , -0.03054810 ,-0.02941895 ,-0.02844238 ,-0.02700806 ,-0.02590942 , -0.02529907 ,-0.02606201 ,-0.02795410 ,-0.03021240 ,-0.03201294 , -0.03305054 ,-0.03286743 ,-0.03170776 ,-0.03027344 ,-0.02871704 , -0.02801514 ,-0.02847290 ,-0.03021240 ,-0.03118896 ,-0.03173828 , -0.03085327 ,-0.02947998 ,-0.02761841 ,-0.02627563 ,-0.02630615 , -0.02740479 ,-0.02951050 ,-0.03073120 ,-0.03079224 ,-0.02944946 , -0.02719116 ,-0.02542114 ,-0.02441406 ,-0.02478027 ,-0.02685547 , -0.02932739 ,-0.03158569 ,-0.03234863 ,-0.03195190 ,-0.03036499 , -0.02899170 ,-0.02874756 ,-0.02947998 ,-0.02999878 ,-0.02990723 , -0.02908325 ,-0.02838135 ,-0.02758789 ,-0.02706909 ,-0.02713013 , -0.02795410 ,-0.02850342 ,-0.02874756 ,-0.02859497 ,-0.02789307 , -0.02703857 ,-0.02551270 ,-0.02548218 ,-0.02633667 ,-0.02804565 , -0.02957153 ,-0.03134155 ,-0.03170776 ,-0.03091431 ,-0.02886963 , -0.02661133 ,-0.02505493 ,-0.02426147 ,-0.02493286 ,-0.02648926 , -0.02841187 ,-0.02960205 ,-0.02926636 ,-0.02767944 ,-0.02523804 , -0.02377319 ,-0.02307129 ,-0.02365112 ,-0.02481079 ,-0.02587891 , -0.02581787 ,-0.02569580 ,-0.02529907 ,-0.02539062 ,-0.02557373 , -0.02581787 ,-0.02572632 ,-0.02514648 ,-0.02496338 ,-0.02499390 , -0.02365112 ,-0.02276611 ,-0.02212524 ,-0.02252197 ,-0.02340698 , -0.02520752 ,-0.02706909 ,-0.02764893 ,-0.02642822 ,-0.02374268 , -0.02059937 ,-0.01824951 ,-0.01751709 ,-0.01824951 ,-0.01947021 , -0.02108765 ,-0.02175903 ,-0.02139282 ,-0.02142334 ,-0.02142334 , -0.02154541 ,-0.02270508 ,-0.02282715 ,-0.02294922 ,-0.02182007 , -0.02117920 ,-0.02044678 ,-0.02014160 ,-0.01971436 ,-0.01895142 , -0.01895142 ,-0.01913452 ,-0.01983643 ,-0.01928711 ,-0.01864624 , -0.01736450 ,-0.01632690 ,-0.01565552 ,-0.01510620 ,-0.01437378 , -0.01370239 ,-0.01254272 ,-0.01232910 ,-0.01293945 ,-0.01330566 , -0.01342773 ,-0.01339722 ,-0.01278687 ,-0.01245117 ,-0.01196289 , -0.01290894 ,-0.01348877 ,-0.01437378 ,-0.01397705 ,-0.01254272 , -0.01217651 ,-0.01135254 ,-0.01129150 ,-0.01126099 ,-0.01113892 , -0.01086426 ,-0.01031494 ,-0.00973511 ,-0.00936890 ,-0.00985718 , -0.01089478 ,-0.01208496 ,-0.01269531 ,-0.01226807 ,-0.01089478 , -0.00891113 ,-0.00680542 ,-0.00552368 ,-0.00582886 ,-0.00634766 , -0.00695801 ,-0.00698853 ,-0.00656128 ,-0.00567627 ,-0.00512695 , -0.00582886 ,-0.00625610 ,-0.00650024 ,-0.00573730 ,-0.00491333 , -0.00384521 ,-0.00234985 ,-0.00161743 ,-0.00137329 ,-0.00210571 , -0.00271606 ,-0.00256348 ,-0.00268555 ,-0.00259399 ,-0.00228882 , -0.00222778 ,-0.00234985 ,-0.00280762 ,-0.00280762 ,-0.00268555 , -0.00134277 ,-0.00076294 ,-0.00134277 ,-0.00231934 ,-0.00280762 , -0.00198364 ,-0.00125122 ,-0.00030518 ,0.00048828 ,0.00103760 , 0.00094604 ,0.00100708 ,0.00009155 ,0.00018311 ,-0.00039673 , -0.00067139 ,-0.00115967 ,-0.00100708 ,-0.00039673 ,0.00097656 , 0.00210571 ,0.00344849 ,0.00369263 ,0.00372314 ,0.00347900 , 0.00335693 ,0.00329590 ,0.00369263 ,0.00381470 ,0.00302124 , 0.00216675 ,0.00164795 ,0.00146484 ,0.00183105 ,0.00292969 , 0.00354004 ,0.00473022 ,0.00531006 ,0.00552368 ,0.00582886 , 0.00628662 ,0.00640869 ,0.00619507 ,0.00579834 ,0.00555420 , 0.00506592 ,0.00433350 ,0.00451660 ,0.00503540 ,0.00680542 , 0.00796509 ,0.00909424 ,0.00918579 ,0.00894165 ,0.00772095 , 0.00601196 ,0.00479126 ,0.00488281 ,0.00592041 ,0.00732422 , 0.00927734 ,0.01055908 ,0.01150513 ,0.01074219 ,0.01022339 , 0.00863647 ,0.00756836 ,0.00720215 ,0.00759888 ,0.00827026 , 0.00881958 ,0.00942993 ,0.00964355 ,0.00997925 ,0.00961304 , 0.00903320 ,0.00863647 ,0.00799561 ,0.00814819 ,0.00820923 , 0.00866699 ,0.00836182 ,0.00765991 ,0.00643921 ,0.00631714 , 0.00692749 ,0.00912476 ,0.01080322 ,0.01205444 ,0.01162720 , 0.01028442 ,0.00830078 ,0.00698853 ,0.00677490 ,0.00741577 , 0.00839233 ,0.00933838 ,0.00970459 ,0.01004028 ,0.00979614 , 0.00946045 ,0.00930786 ,0.00912476 ,0.00933838 ,0.01019287 , 0.01101685 ,0.01171875 ,0.01123047 ,0.01083374 ,0.00875854 , 0.00741577 ,0.00653076 ,0.00701904 ,0.00820923 ,0.00985718 , 0.01129150 ,0.01251221 ,0.01260376 ,0.01123047 ,0.00891113 , 0.00656128 ,0.00491333 ,0.00445557 ,0.00604248 ,0.00845337 , 0.01065063 ,0.01239014 ,0.01239014 ,0.01135254 ,0.00906372 , 0.00708008 ,0.00582886 ,0.00558472 ,0.00610352 ,0.00759888 , 0.00936890 ,0.01107788 ,0.01181030 ,0.01144409 ,0.01040649 , 0.00930786 ,0.00836182 ,0.00811768 ,0.00839233 ,0.00888062 , 0.00946045 ,0.01031494 ,0.01031494 ,0.01031494 ,0.01040649 , 0.01119995 ,0.01165771 ,0.01281738 ,0.01315308 ,0.01300049 , 0.01159668 ,0.01004028 ,0.00881958 ,0.00842285 ,0.00869751 , 0.00866699 ,0.00903320 ,0.00970459 ,0.01016235 ,0.01052856 , 0.01077271 ,0.01007080 ,0.01010132 ,0.01028442 ,0.01065063 , 0.01116943 ,0.01217651 ,0.01248169 ,0.01281738 ,0.01229858 , 0.01141357 ,0.01077271 ,0.01000977 ,0.01013184 ,0.01028442 , 0.01083374 ,0.01025391 ,0.00982666 ,0.00888062 ,0.00830078 , 0.00793457 ,0.00842285 ,0.00918579 ,0.01022339 ,0.01144409 , 0.01251221 ,0.01232910 ,0.01171875 ,0.01019287 ,0.00918579 , 0.00909424 ,0.00979614 ,0.01101685 ,0.01245117 ,0.01376343 , 0.01516724 ,0.01565552 ,0.01611328 ,0.01550293 ,0.01477051 , 0.01373291 ,0.01245117 ,0.01147461 ,0.01037598 ,0.00970459 , 0.01019287 ,0.01113892 ,0.01272583 ,0.01428223 ,0.01553345 , 0.01559448 ,0.01513672 ,0.01394653 ,0.01339722 ,0.01315308 , 0.01330566 ,0.01370239 ,0.01388550 ,0.01348877 ,0.01251221 , 0.01174927 ,0.01208496 ,0.01275635 ,0.01348877 ,0.01449585 , 0.01522827 ,0.01504517 ,0.01470947 ,0.01416016 ,0.01397705 , 0.01342773 ,0.01388550 ,0.01373291 ,0.01397705 ,0.01336670 , 0.01373291 ,0.01388550 ,0.01470947 ,0.01528931 ,0.01666260 , 0.01687622 ,0.01705933 ,0.01669312 ,0.01684570 ,0.01751709 , 0.01870728 ,0.01995850 ,0.02056885 ,0.02017212 ,0.01885986 , 0.01699829 ,0.01544189 ,0.01388550 ,0.01345825 ,0.01394653 , 0.01535034 ,0.01684570 ,0.01824951 ,0.01870728 ,0.01892090 , 0.01892090 ,0.01913452 ,0.01950073 ,0.01968384 ,0.01995850 , 0.02001953 ,0.01959229 ,0.01980591 ,0.02011108 ,0.02127075 , 0.02191162 ,0.02270508 ,0.02288818 ,0.02368164 ,0.02426147 , 0.02413940 ,0.02301025 ,0.02142334 ,0.02062988 ,0.02084351 , 0.02124023 ,0.02285767 ,0.02359009 ,0.02383423 ,0.02301025 , 0.02154541 ,0.02081299 ,0.02035522 ,0.02127075 ,0.02276611 , 0.02487183 ,0.02597046 ,0.02612305 ,0.02551270 ,0.02407837 , 0.02313232 ,0.02236938 ,0.02285767 ,0.02465820 ,0.02651978 , 0.02774048 ,0.02807617 ,0.02722168 ,0.02563477 ,0.02502441 , 0.02465820 ,0.02560425 ,0.02670288 ,0.02758789 ,0.02832031 , 0.02798462 ,0.02703857 ,0.02578735 ,0.02453613 ,0.02432251 , 0.02453613 ,0.02560425 ,0.02624512 ,0.02703857 ,0.02755737 , 0.02725220 ,0.02722168 ,0.02682495 ,0.02734375 ,0.02789307 , 0.02908325 ,0.02981567 ,0.02987671 ,0.02871704 ,0.02743530 , 0.02606201 ,0.02587891 ,0.02621460 ,0.02679443 ,0.02752686 , 0.02734375 ,0.02728271 ,0.02719116 ,0.02706909 ,0.02661133 , 0.02606201 ,0.02569580 ,0.02569580 ,0.02587891 ,0.02630615 , 0.02777100 ,0.02871704 ,0.02914429 ,0.02896118 ,0.02893066 , 0.02871704 ,0.02902222 ,0.02877808 ,0.02911377 ,0.02905273 , 0.02963257 ,0.02929688 ,0.02944946 ,0.02880859 ,0.02786255 , 0.02713013 ,0.02642822 ,0.02700806 ,0.02770996 ,0.02877808 , 0.02917480 ,0.02880859 ,0.02795410 ,0.02691650 ,0.02719116 , 0.02792358 ,0.02902222 ,0.02957153 ,0.02935791 ,0.02813721 , 0.02743530 ,0.02706909 ,0.02752686 ,0.02764893 ,0.02853394 , 0.02999878 ,0.03128052 ,0.03201294 ,0.03237915 ,0.03140259 , 0.03054810 ,0.02868652 ,0.02734375 ,0.02700806 ,0.02728271 , 0.02859497 ,0.03057861 ,0.03134155 ,0.03158569 ,0.03070068 , 0.02929688 ,0.02810669 ,0.02777100 ,0.02853394 ,0.02951050 , 0.03051758 ,0.03088379 ,0.03018188 ,0.02868652 ,0.02716064 , 0.02658081 ,0.02700806 ,0.02770996 ,0.02914429 ,0.03012085 , 0.03060913 ,0.03033447 ,0.02914429 ,0.02795410 ,0.02700806 , 0.02648926 ,0.02627563 ,0.02679443 ,0.02697754 ,0.02716064 , 0.02697754 ,0.02688599 ,0.02624512 ,0.02639771 ,0.02630615 , 0.02676392 ,0.02740479 ,0.02832031 ,0.02880859 ,0.02862549 , 0.02847290 ,0.02786255 ,0.02783203 ,0.02700806 ,0.02630615 , 0.02563477 ,0.02526855 ,0.02542114 ,0.02554321 ,0.02557373 , 0.02551270 ,0.02560425 ,0.02590942 ,0.02648926 ,0.02621460 , 0.02587891 ,0.02468872 ,0.02359009 ,0.02249146 ,0.02145386 , 0.02090454 ,0.02130127 ,0.02239990 ,0.02371216 ,0.02444458 , 0.02481079 ,0.02401733 ,0.02258301 ,0.02072144 ,0.01904297 , 0.01779175 ,0.01806641 ,0.01824951 ,0.01907349 ,0.01931763 , 0.01916504 ,0.01910400 ,0.01904297 ,0.01873779 ,0.01858521 , 0.01843262 ,0.01812744 ,0.01779175 ,0.01681519 ,0.01702881 , 0.01721191 ,0.01776123 ,0.01770020 ,0.01760864 ,0.01760864 , 0.01766968 ,0.01828003 ,0.01812744 ,0.01800537 ,0.01751709 , 0.01669312 ,0.01599121 ,0.01535034 ,0.01467896 ,0.01400757 , 0.01364136 ,0.01306152 ,0.01345825 ,0.01406860 ,0.01519775 , 0.01516724 ,0.01553345 ,0.01477051 ,0.01470947 ,0.01504517 , 0.01538086 ,0.01562500 ,0.01477051 ,0.01391602 ,0.01248169 , 0.01129150 ,0.01052856 ,0.01043701 ,0.01074219 ,0.01199341 , 0.01229858 ,0.01245117 ,0.01181030 ,0.01168823 ,0.01162720 , 0.01226807 ,0.01248169 ,0.01202393 ,0.01098633 ,0.01007080 , 0.00894165 ,0.00772095 ,0.00698853 ,0.00683594 ,0.00714111 , 0.00833130 ,0.00961304 ,0.01089478 ,0.01126099 ,0.01092529 , 0.01025391 ,0.00906372 ,0.00854492 ,0.00802612 ,0.00823975 , 0.00875854 ,0.00952148 ,0.00918579 ,0.00866699 ,0.00744629 , 0.00674438 ,0.00619507 ,0.00656128 ,0.00692749 ,0.00726318 , 0.00784302 ,0.00781250 ,0.00784302 ,0.00759888 ,0.00793457 , 0.00854492 ,0.00912476 ,0.00915527 ,0.00793457 ,0.00579834 , 0.00381470 ,0.00271606 ,0.00271606 ,0.00360107 ,0.00555420 , 0.00631714 ,0.00704956 ,0.00637817 ,0.00537109 ,0.00320435 , 0.00222778 ,0.00173950 ,0.00186157 ,0.00265503 ,0.00286865 , 0.00292969 ,0.00262451 ,0.00286865 ,0.00329590 ,0.00393677 , 0.00457764 ,0.00363159 ,0.00256348 ,0.00103760 ,0.00009155 , 0.00054932 ,0.00122070 ,0.00231934 ,0.00305176 ,0.00305176 , 0.00164795 ,0.00009155 ,-0.00143433 ,-0.00183105 ,-0.00094604 , 0.00061035 ,0.00192261 ,0.00250244 ,0.00158691 ,0.00070190 , -0.00033569 ,-0.00070190 ,-0.00128174 ,-0.00149536 ,-0.00180054 , -0.00149536 ,-0.00103760 ,-0.00030518 ,0.00048828 ,0.00067139 , 0.00051880 ,-0.00015259 ,-0.00012207 ,-0.00042725 ,-0.00100708 , -0.00103760 ,-0.00152588 ,-0.00122070 ,-0.00122070 ,-0.00149536 , -0.00161743 ,-0.00213623 ,-0.00268555 ,-0.00338745 ,-0.00375366 , -0.00387573 ,-0.00402832 ,-0.00369263 ,-0.00320435 ,-0.00238037 , -0.00219727 ,-0.00119019 ,-0.00103760 ,-0.00106812 ,-0.00180054 , -0.00259399 ,-0.00311279 ,-0.00280762 ,-0.00216675 ,-0.00143433 , -0.00061035 ,-0.00051880 ,-0.00067139 ,-0.00164795 ,-0.00228882 , -0.00329590 ,-0.00399780 ,-0.00442505 ,-0.00439453 ,-0.00375366 , -0.00286865 ,-0.00219727 ,-0.00207520 ,-0.00250244 ,-0.00299072 , -0.00354004 ,-0.00308228 ,-0.00228882 ,-0.00140381 ,-0.00122070 , -0.00115967 ,-0.00195312 ,-0.00335693 ,-0.00469971 ,-0.00527954 , -0.00454712 ,-0.00347900 ,-0.00250244 ,-0.00177002 ,-0.00152588 , -0.00201416 ,-0.00253296 ,-0.00326538 ,-0.00341797 ,-0.00302124 , -0.00314331 ,-0.00387573 ,-0.00448608 ,-0.00595093 ,-0.00640869 , -0.00610352 ,-0.00515747 ,-0.00396729 ,-0.00299072 ,-0.00259399 , -0.00329590 ,-0.00445557 ,-0.00564575 ,-0.00698853 ,-0.00695801 , -0.00610352 ,-0.00408936 ,-0.00314331 ,-0.00299072 ,-0.00476074 , -0.00659180 ,-0.00811768 ,-0.00823975 ,-0.00695801 ,-0.00573730 , -0.00439453 ,-0.00372314 ,-0.00387573 ,-0.00430298 ,-0.00424194 , -0.00442505 ,-0.00503540 ,-0.00567627 ,-0.00625610 ,-0.00665283 , -0.00708008 ,-0.00781250 ,-0.00845337 ,-0.00830078 ,-0.00787354 , -0.00689697 ,-0.00601196 ,-0.00494385 ,-0.00418091 ,-0.00460815 , -0.00607300 ,-0.00762939 ,-0.00915527 ,-0.00994873 ,-0.00964355 , -0.00866699 ,-0.00744629 ,-0.00686646 ,-0.00689697 ,-0.00704956 , -0.00820923 ,-0.00878906 ,-0.00927734 ,-0.00915527 ,-0.00836182 , -0.00729370 ,-0.00625610 ,-0.00650024 ,-0.00726318 ,-0.00872803 , -0.01004028 ,-0.01040649 ,-0.01092529 ,-0.01019287 ,-0.00958252 , -0.00921631 ,-0.00906372 ,-0.00973511 ,-0.00988770 ,-0.01065063 , -0.01022339 ,-0.00973511 ,-0.00872803 ,-0.00888062 ,-0.00967407 , -0.01083374 ,-0.01138306 ,-0.01162720 ,-0.01171875 ,-0.01150513 , -0.01144409 ,-0.01046753 ,-0.00988770 ,-0.00985718 ,-0.00988770 , -0.01086426 ,-0.01196289 ,-0.01293945 ,-0.01333618 ,-0.01361084 , -0.01330566 ,-0.01339722 ,-0.01376343 ,-0.01467896 ,-0.01538086 , -0.01528931 ,-0.01522827 ,-0.01513672 ,-0.01547241 ,-0.01556396 , -0.01559448 ,-0.01544189 ,-0.01574707 ,-0.01596069 ,-0.01583862 , -0.01556396 ,-0.01501465 ,-0.01522827 ,-0.01553345 ,-0.01568604 , -0.01580811 ,-0.01617432 ,-0.01638794 ,-0.01678467 ,-0.01696777 , -0.01733398 ,-0.01773071 ,-0.01797485 ,-0.01824951 ,-0.01797485 , -0.01843262 ,-0.01834106 ,-0.01873779 ,-0.01864624 ,-0.01818848 , -0.01800537 ,-0.01785278 ,-0.01824951 ,-0.01931763 ,-0.02087402 , -0.02142334 ,-0.02111816 ,-0.02072144 ,-0.01989746 ,-0.01992798 , -0.01986694 ,-0.01995850 ,-0.02038574 ,-0.01989746 ,-0.01956177 , -0.01889038 ,-0.01879883 ,-0.01950073 ,-0.02062988 ,-0.02163696 , -0.02276611 ,-0.02276611 ,-0.02264404 ,-0.02200317 ,-0.02166748 , -0.02163696 ,-0.02166748 ,-0.02185059 ,-0.02185059 ,-0.02194214 , -0.02246094 ,-0.02307129 ,-0.02346802 ,-0.02371216 ,-0.02380371 , -0.02319336 ,-0.02301025 ,-0.02264404 ,-0.02337646 ,-0.02438354 , -0.02569580 ,-0.02615356 ,-0.02572632 ,-0.02478027 ,-0.02288818 , -0.02230835 ,-0.02124023 ,-0.02154541 ,-0.02203369 ,-0.02291870 , -0.02395630 ,-0.02474976 ,-0.02526855 ,-0.02514648 ,-0.02462769 , -0.02450562 ,-0.02484131 ,-0.02471924 ,-0.02380371 ,-0.02273560 , -0.02178955 ,-0.02102661 ,-0.02050781 ,-0.02050781 ,-0.02114868 , -0.02178955 ,-0.02267456 ,-0.02322388 ,-0.02407837 ,-0.02435303 , -0.02465820 ,-0.02426147 ,-0.02398682 ,-0.02337646 ,-0.02322388 , -0.02304077 ,-0.02307129 ,-0.02304077 ,-0.02383423 ,-0.02389526 , -0.02459717 ,-0.02453613 ,-0.02456665 ,-0.02459717 ,-0.02487183 , -0.02474976 ,-0.02447510 ,-0.02404785 ,-0.02343750 ,-0.02291870 , -0.02276611 ,-0.02261353 ,-0.02221680 ,-0.02203369 ,-0.02172852 , -0.02169800 ,-0.02178955 ,-0.02197266 ,-0.02200317 ,-0.02212524 , -0.02267456 ,-0.02322388 ,-0.02328491 ,-0.02355957 ,-0.02301025 , -0.02368164 ,-0.02331543 ,-0.02340698 ,-0.02322388 ,-0.02279663 , -0.02255249 ,-0.02206421 ,-0.02157593 ,-0.02148438 ,-0.02224731 , -0.02288818 ,-0.02359009 ,-0.02383423 ,-0.02359009 ,-0.02267456 , -0.02145386 ,-0.02066040 ,-0.02014160 ,-0.02041626 ,-0.02084351 , -0.02154541 ,-0.02188110 ,-0.02215576 ,-0.02182007 ,-0.02154541 , -0.02124023 ,-0.02105713 ,-0.02145386 ,-0.02169800 ,-0.02206421 , -0.02243042 ,-0.02212524 ,-0.02160645 ,-0.02059937 ,-0.01953125 , -0.01937866 ,-0.01953125 ,-0.01959229 ,-0.01983643 ,-0.01907349 , -0.01873779 ,-0.01809692 ,-0.01766968 ,-0.01763916 ,-0.01803589 , -0.01846313 ,-0.01889038 ,-0.01879883 ,-0.01867676 ,-0.01806641 , -0.01782227 ,-0.01785278 ,-0.01831055 ,-0.01803589 ,-0.01797485 , -0.01763916 ,-0.01730347 ,-0.01669312 ,-0.01635742 ,-0.01617432 , -0.01669312 ,-0.01705933 ,-0.01699829 ,-0.01644897 ,-0.01629639 , -0.01626587 ,-0.01617432 ,-0.01647949 ,-0.01641846 ,-0.01657104 , -0.01547241 ,-0.01483154 ,-0.01324463 ,-0.01254272 ,-0.01226807 , -0.01199341 ,-0.01239014 ,-0.01251221 ,-0.01293945 ,-0.01281738 , -0.01232910 ,-0.01199341 ,-0.01171875 ,-0.01184082 ,-0.01190186 , -0.01184082 ,-0.01202393 ,-0.01217651 ,-0.01278687 ,-0.01354980 , -0.01437378 ,-0.01473999 ,-0.01525879 ,-0.01428223 ,-0.01351929 , -0.01242065 ,-0.01156616 ,-0.01132202 ,-0.01095581 ,-0.01074219 , -0.01083374 ,-0.01104736 ,-0.01165771 ,-0.01199341 ,-0.01220703 , -0.01177979 ,-0.01162720 ,-0.01126099 ,-0.01132202 ,-0.01110840 , -0.01068115 ,-0.01013184 ,-0.00976562 ,-0.00958252 ,-0.00964355 , -0.01052856 ,-0.01107788 ,-0.01187134 ,-0.01202393 ,-0.01184082 , -0.01141357 ,-0.01043701 ,-0.01004028 ,-0.01004028 ,-0.00991821 , -0.01016235 ,-0.00988770 ,-0.01034546 ,-0.00994873 ,-0.01000977 , -0.01025391 ,-0.01089478 ,-0.01110840 ,-0.01162720 ,-0.01162720 , -0.01104736 ,-0.01089478 ,-0.01049805 ,-0.01028442 ,-0.00997925 , -0.00967407 ,-0.00964355 ,-0.01007080 ,-0.00994873 ,-0.00994873 , -0.01019287 ,-0.01028442 ,-0.01055908 ,-0.01068115 ,-0.01119995 , -0.01165771 ,-0.01275635 ,-0.01303101 ,-0.01361084 ,-0.01333618 , -0.01245117 ,-0.01119995 ,-0.00982666 ,-0.00918579 ,-0.00872803 , -0.00961304 ,-0.01028442 ,-0.01101685 ,-0.01116943 ,-0.01110840 , -0.01086426 ,-0.01025391 ,-0.00997925 ,-0.00967407 ,-0.00994873 , -0.00973511 ,-0.00976562 ,-0.00946045 ,-0.00891113 ,-0.00851440 , -0.00827026 ,-0.00817871 ,-0.00885010 ,-0.00952148 ,-0.01016235 , -0.01077271 ,-0.01086426 ,-0.01052856 ,-0.01034546 ,-0.00991821 , -0.01013184 ,-0.01034546 ,-0.01025391 ,-0.01046753 ,-0.01113892 , -0.01107788 ,-0.01141357 ,-0.01116943 ,-0.01068115 ,-0.01004028 , -0.01022339 ,-0.01062012 ,-0.01147461 ,-0.01162720 ,-0.01116943 , -0.01080322 ,-0.00979614 ,-0.00924683 ,-0.00897217 ,-0.00915527 , -0.00964355 ,-0.00970459 ,-0.01019287 ,-0.00930786 ,-0.00885010 , -0.00787354 ,-0.00744629 ,-0.00799561 ,-0.00915527 ,-0.00964355 , -0.01028442 ,-0.01052856 ,-0.01058960 ,-0.01065063 ,-0.01052856 , -0.01010132 ,-0.01000977 ,-0.00994873 ,-0.00964355 ,-0.00936890 , -0.00903320 ,-0.00909424 ,-0.00952148 ,-0.01013184 ,-0.01113892 , -0.01199341 ,-0.01181030 ,-0.01153564 ,-0.01083374 ,-0.01086426 , -0.01098633 ,-0.01129150 ,-0.01138306 ,-0.01113892 ,-0.01107788 , -0.01107788 ,-0.01101685 ,-0.01116943 ,-0.01107788 ,-0.01071167 , -0.01046753 ,-0.00994873 ,-0.01031494 ,-0.01071167 ,-0.01162720 , -0.01226807 ,-0.01266479 ,-0.01202393 ,-0.01126099 ,-0.01065063 , -0.01046753 ,-0.01071167 ,-0.01138306 ,-0.01220703 ,-0.01193237 , -0.01174927 ,-0.01141357 ,-0.01190186 ,-0.01171875 ,-0.01162720 , -0.01113892 ,-0.01037598 ,-0.00994873 ,-0.01004028 ,-0.01034546 , -0.01049805 ,-0.01034546 ,-0.00958252 ,-0.00918579 ,-0.00894165 , -0.00961304 ,-0.00982666 ,-0.00991821 ,-0.00955200 ,-0.00946045 , -0.00836182 ,-0.00848389 ,-0.00845337 ,-0.00924683 ,-0.01010132 , -0.01101685 ,-0.01147461 ,-0.01147461 ,-0.01065063 ,-0.00973511 , -0.00897217 ,-0.00845337 ,-0.00827026 ,-0.00833130 ,-0.00894165 , -0.00909424 ,-0.00970459 ,-0.00955200 ,-0.01037598 ,-0.01040649 , -0.01074219 ,-0.01049805 ,-0.01062012 ,-0.01031494 ,-0.00976562 , -0.00897217 ,-0.00827026 ,-0.00735474 ,-0.00732422 ,-0.00723267 , -0.00778198 ,-0.00817871 ,-0.00845337 ,-0.00827026 ,-0.00790405 , -0.00747681 ,-0.00720215 ,-0.00704956 ,-0.00680542 ,-0.00720215 , -0.00741577 ,-0.00769043 ,-0.00778198 ,-0.00711060 ,-0.00643921 , -0.00555420 ,-0.00537109 ,-0.00534058 ,-0.00604248 ,-0.00659180 , -0.00729370 ,-0.00711060 ,-0.00616455 ,-0.00479126 ,-0.00369263 , -0.00354004 ,-0.00424194 ,-0.00512695 ,-0.00552368 ,-0.00546265 , -0.00488281 ,-0.00384521 ,-0.00357056 ,-0.00323486 ,-0.00283813 , -0.00311279 ,-0.00344849 ,-0.00384521 ,-0.00405884 ,-0.00430298 , -0.00393677 ,-0.00357056 ,-0.00338745 ,-0.00338745 ,-0.00299072 , -0.00250244 ,-0.00271606 ,-0.00302124 ,-0.00320435 ,-0.00390625 , -0.00439453 ,-0.00485229 ,-0.00500488 ,-0.00503540 ,-0.00494385 , -0.00463867 ,-0.00405884 ,-0.00305176 ,-0.00234985 ,-0.00219727 , -0.00207520 ,-0.00238037 ,-0.00265503 ,-0.00256348 ,-0.00183105 , -0.00045776 ,0.00073242 ,0.00167847 ,0.00177002 ,0.00115967 , 0.00076294 ,0.00030518 ,-0.00015259 ,-0.00021362 ,0.00033569 , 0.00067139 ,0.00134277 ,0.00158691 ,0.00213623 ,0.00234985 , 0.00296021 ,0.00271606 ,0.00262451 ,0.00195312 ,0.00195312 , 0.00167847 ,0.00219727 ,0.00271606 ,0.00360107 ,0.00387573 , 0.00387573 ,0.00350952 ,0.00283813 ,0.00256348 ,0.00231934 , 0.00305176 ,0.00378418 ,0.00476074 ,0.00558472 ,0.00570679 , 0.00592041 ,0.00570679 ,0.00534058 ,0.00509644 ,0.00540161 , 0.00570679 ,0.00585938 ,0.00543213 ,0.00585938 ,0.00576782 , 0.00653076 ,0.00689697 ,0.00741577 ,0.00769043 ,0.00817871 , 0.00808716 ,0.00839233 ,0.00848389 ,0.00808716 ,0.00817871 , 0.00823975 ,0.00817871 ,0.00799561 ,0.00778198 ,0.00765991 , 0.00784302 ,0.00793457 ,0.00839233 ,0.00842285 ,0.00851440 , 0.00967407 ,0.01031494 ,0.01123047 ,0.01150513 ,0.01083374 , 0.01019287 ,0.00964355 ,0.00930786 ,0.00942993 ,0.00949097 , 0.00906372 ,0.00903320 ,0.00912476 ,0.00961304 ,0.01019287 , 0.01049805 ,0.01104736 ,0.01062012 ,0.01058960 ,0.00961304 , 0.00942993 ,0.00878906 ,0.00918579 ,0.00900269 ,0.00946045 , 0.00900269 ,0.00875854 ,0.00863647 ,0.00888062 ,0.00939941 , 0.01043701 ,0.01153564 ,0.01214600 ,0.01248169 ,0.01254272 , 0.01196289 ,0.01135254 ,0.01058960 ,0.01040649 ,0.01049805 , 0.01071167 ,0.01104736 ,0.01095581 ,0.01089478 ,0.01074219 , 0.01043701 ,0.01055908 ,0.00991821 ,0.00952148 ,0.00967407 , 0.00979614 ,0.01013184 ,0.01095581 ,0.01202393 ,0.01251221 , 0.01220703 ,0.01211548 ,0.01129150 ,0.01077271 ,0.01028442 , 0.01028442 ,0.01031494 ,0.01022339 ,0.01062012 ,0.01098633 , 0.01156616 ,0.01220703 ,0.01269531 ,0.01266479 ,0.01242065 , 0.01202393 ,0.01196289 ,0.01150513 ,0.01141357 ,0.01068115 , 0.01086426 ,0.01004028 ,0.00979614 ,0.00955200 ,0.00994873 , 0.01092529 ,0.01208496 ,0.01272583 ,0.01321411 ,0.01293945 , 0.01232910 ,0.01165771 ,0.01119995 ,0.01156616 ,0.01168823 , 0.01162720 ,0.01150513 ,0.01110840 ,0.01092529 ,0.01062012 , 0.01110840 ,0.01144409 ,0.01226807 ,0.01245117 ,0.01263428 , 0.01251221 ,0.01263428 ,0.01263428 ,0.01321411 ,0.01354980 , 0.01388550 ,0.01309204 ,0.01239014 ,0.01104736 ,0.01071167 , 0.01037598 ,0.01089478 ,0.01177979 ,0.01269531 ,0.01312256 , 0.01272583 ,0.01266479 ,0.01211548 ,0.01205444 ,0.01199341 , 0.01181030 ,0.01156616 ,0.01132202 ,0.01095581 ,0.01068115 , 0.01065063 ,0.01034546 ,0.01016235 ,0.01019287 ,0.01068115 , 0.01132202 ,0.01150513 ,0.01211548 ,0.01245117 ,0.01260376 , 0.01229858 ,0.01147461 ,0.01055908 ,0.01007080 ,0.00976562 , 0.00991821 ,0.01068115 ,0.01092529 ,0.01123047 ,0.01086426 , 0.01049805 ,0.01034546 ,0.00970459 ,0.00985718 ,0.00994873 , 0.01016235 ,0.01040649 ,0.01000977 ,0.01013184 ,0.00970459 , 0.00933838 ,0.00878906 ,0.00845337 ,0.00878906 ,0.00891113 , 0.00906372 ,0.00955200 ,0.00952148 ,0.00970459 ,0.00958252 , 0.00930786 ,0.00952148 ,0.00942993 ,0.00921631 ,0.00872803 , 0.00860596 ,0.00836182 ,0.00845337 ,0.00836182 ,0.00888062 , 0.00860596 ,0.00897217 ,0.00854492 ,0.00866699 ,0.00866699 , 0.00857544 ,0.00833130 ,0.00820923 ,0.00802612 ,0.00762939 , 0.00784302 ,0.00747681 ,0.00805664 ,0.00802612 ,0.00839233 , 0.00814819 ,0.00796509 ,0.00723267 ,0.00726318 ,0.00735474 , 0.00833130 ,0.00906372 ,0.00955200 ,0.00988770 ,0.00961304 , 0.00897217 ,0.00808716 ,0.00805664 ,0.00823975 ,0.00836182 , 0.00860596 ,0.00814819 ,0.00759888 ,0.00714111 ,0.00665283 , 0.00701904 ,0.00778198 ,0.00869751 ,0.00921631 ,0.00915527 , 0.00881958 ,0.00875854 ,0.00845337 ,0.00784302 ,0.00790405 , 0.00827026 ,0.00866699 ,0.00927734 ,0.00924683 ,0.00924683 , 0.00906372 ,0.00900269 ,0.00897217 ,0.00912476 ,0.00927734 , 0.00924683 ,0.00909424 ,0.00891113 ,0.00888062 ,0.00900269 , 0.00942993 ,0.00915527 ,0.00936890 ,0.00903320 ,0.00915527 , 0.00924683 ,0.00921631 ,0.00942993 ,0.00885010 ,0.00860596 , 0.00839233 ,0.00894165 ,0.00976562 ,0.01052856 ,0.01110840 , 0.01113892 ,0.01089478 ,0.00997925 ,0.00897217 ,0.00878906 , 0.00866699 ,0.00872803 ,0.00888062 ,0.00839233 ,0.00845337 , 0.00793457 ,0.00817871 ,0.00881958 ,0.00955200 ,0.00985718 , 0.00997925 ,0.00955200 ,0.00933838 ,0.00823975 ,0.00796509 , 0.00775146 ,0.00781250 ,0.00839233 ,0.00885010 ,0.00979614 , 0.00973511 ,0.00964355 ,0.00921631 ,0.00857544 ,0.00860596 , 0.00839233 ,0.00845337 ,0.00894165 ,0.00942993 ,0.00982666 , 0.01004028 ,0.00982666 ,0.00952148 ,0.00888062 ,0.00842285 , 0.00839233 ,0.00851440 ,0.00921631 ,0.00924683 ,0.00900269 , 0.00891113 ,0.00894165 ,0.00906372 ,0.00970459 ,0.01000977 , 0.01010132 ,0.01034546 ,0.01031494 ,0.01000977 ,0.00964355 , 0.00885010 ,0.00857544 ,0.00839233 ,0.00857544 ,0.00897217 , 0.00991821 ,0.01086426 ,0.01153564 ,0.01177979 ,0.01113892 , 0.01083374 ,0.01028442 ,0.01019287 ,0.01016235 ,0.01046753 , 0.01028442 ,0.01055908 ,0.01031494 ,0.01019287 ,0.01013184 , 0.01007080 ,0.01028442 ,0.01043701 ,0.01031494 ,0.01055908 , 0.01019287 ,0.01071167 ,0.01037598 ,0.01071167 ,0.01104736 , 0.01126099 ,0.01123047 ,0.01098633 ,0.01153564 ,0.01135254 , 0.01141357 ,0.01132202 ,0.01165771 ,0.01181030 ,0.01199341 , 0.01214600 ,0.01165771 ,0.01123047 ,0.01089478 ,0.01116943 , 0.01144409 ,0.01177979 ,0.01156616 ,0.01080322 ,0.00976562 , 0.00900269 ,0.00888062 ,0.00927734 ,0.01004028 ,0.01046753 , 0.01086426 ,0.01095581 ,0.01080322 ,0.01101685 ,0.01089478 , 0.01089478 ,0.01071167 ,0.01104736 ,0.01135254 ,0.01193237 , 0.01162720 ,0.01190186 ,0.01177979 ,0.01199341 ,0.01214600 , 0.01196289 ,0.01193237 ,0.01113892 ,0.01113892 ,0.01037598 , 0.01058960 ,0.01058960 ,0.01074219 ,0.01110840 ,0.01101685 , 0.01153564 ,0.01141357 ,0.01132202 ,0.01089478 ,0.01010132 , 0.00946045 ,0.00930786 ,0.00942993 ,0.01040649 ,0.01156616 , 0.01287842 ,0.01321411 ,0.01303101 ,0.01211548 ,0.01113892 , 0.01025391 ,0.01016235 ,0.01043701 ,0.01110840 ,0.01159668 , 0.01187134 ,0.01177979 ,0.01086426 ,0.01004028 ,0.00961304 , 0.00964355 ,0.00964355 ,0.00988770 ,0.01016235 ,0.01083374 , 0.01110840 ,0.01034546 ,0.00970459 ,0.00897217 ,0.00863647 , 0.00878906 ,0.00915527 ,0.00994873 ,0.01025391 ,0.01062012 , 0.01062012 ,0.01068115 ,0.01071167 ,0.01083374 ,0.01095581 , 0.01135254 ,0.01171875 ,0.01226807 ,0.01226807 ,0.01226807 , 0.01153564 ,0.01083374 ,0.01004028 ,0.00939941 ,0.00973511 , 0.01010132 ,0.01052856 ,0.01049805 ,0.01034546 ,0.00979614 , 0.00949097 ,0.00964355 ,0.01031494 ,0.01068115 ,0.01098633 , 0.01031494 ,0.00982666 ,0.00927734 ,0.00891113 ,0.00854492 , 0.00863647 ,0.00894165 ,0.00936890 ,0.01013184 ,0.01034546 , 0.01031494 ,0.00961304 ,0.00888062 ,0.00848389 ,0.00854492 , 0.00836182 ,0.00909424 ,0.00973511 ,0.00979614 ,0.00915527 , 0.00839233 ,0.00775146 ,0.00735474 ,0.00698853 ,0.00674438 , 0.00750732 ,0.00784302 ,0.00830078 ,0.00860596 ,0.00817871 , 0.00781250 ,0.00717163 ,0.00671387 ,0.00631714 ,0.00595093 , 0.00625610 ,0.00598145 ,0.00582886 ,0.00570679 ,0.00558472 , 0.00549316 ,0.00524902 ,0.00546265 ,0.00549316 ,0.00585938 , 0.00564575 ,0.00625610 ,0.00637817 ,0.00671387 ,0.00677490 , 0.00662231 ,0.00613403 ,0.00497437 ,0.00411987 ,0.00363159 , 0.00341797 ,0.00442505 ,0.00479126 ,0.00552368 ,0.00552368 , 0.00555420 ,0.00534058 ,0.00524902 ,0.00506592 ,0.00460815 , 0.00469971 ,0.00402832 ,0.00357056 ,0.00332642 ,0.00350952 , 0.00354004 ,0.00396729 ,0.00424194 ,0.00424194 ,0.00454712 , 0.00399780 ,0.00408936 ,0.00384521 ,0.00390625 ,0.00369263 , 0.00369263 ,0.00354004 ,0.00387573 ,0.00399780 ,0.00393677 , 0.00378418 ,0.00338745 ,0.00341797 ,0.00335693 ,0.00338745 , 0.00354004 ,0.00347900 ,0.00381470 ,0.00375366 ,0.00369263 , 0.00341797 ,0.00320435 ,0.00381470 ,0.00405884 ,0.00415039 , 0.00405884 ,0.00350952 ,0.00357056 ,0.00335693 ,0.00320435 , 0.00311279 ,0.00262451 ,0.00256348 ,0.00219727 ,0.00253296 , 0.00265503 ,0.00296021 ,0.00308228 ,0.00299072 ,0.00317383 , 0.00311279 ,0.00323486 ,0.00323486 ,0.00341797 ,0.00320435 , 0.00354004 ,0.00311279 ,0.00308228 ,0.00234985 ,0.00247192 , 0.00210571 ,0.00231934 ,0.00299072 ,0.00311279 ,0.00314331 , 0.00329590 ,0.00286865 ,0.00262451 ,0.00216675 ,0.00189209 , 0.00158691 ,0.00140381 ,0.00189209 ,0.00222778 ,0.00265503 , 0.00280762 ,0.00289917 ,0.00253296 ,0.00250244 ,0.00238037 , 0.00292969 ,0.00341797 ,0.00433350 ,0.00439453 ,0.00415039 , 0.00320435 ,0.00207520 ,0.00103760 ,0.00076294 ,0.00054932 , 0.00109863 ,0.00131226 ,0.00177002 ,0.00155640 ,0.00173950 , 0.00137329 ,0.00152588 ,0.00183105 ,0.00201416 ,0.00210571 , 0.00222778 ,0.00198364 ,0.00170898 ,0.00137329 ,0.00073242 , 0.00051880 ,-0.00006104 ,0.00000000 ,0.00018311 ,0.00030518 , 0.00112915 ,0.00131226 ,0.00173950 ,0.00140381 ,0.00137329 , 0.00100708 ,0.00094604 ,0.00100708 ,0.00109863 ,0.00067139 , 0.00024414 ,-0.00021362 ,0.00012207 ,0.00021362 ,0.00097656 , 0.00131226 ,0.00180054 ,0.00210571 ,0.00225830 ,0.00250244 , 0.00216675 ,0.00173950 ,0.00106812 ,0.00064087 ,0.00057983 , 0.00067139 ,0.00067139 ,0.00076294 ,0.00042725 ,0.00061035 , 0.00030518 ,0.00033569 ,0.00033569 ,0.00067139 ,0.00115967 , 0.00125122 ,0.00128174 ,0.00100708 ,0.00079346 ,0.00082397 , 0.00079346 ,0.00106812 ,0.00112915 ,0.00115967 ,0.00109863 , 0.00057983 ,0.00033569 ,0.00042725 ,-0.00012207 ,0.00030518 , 0.00067139 ,0.00183105 ,0.00216675 ,0.00216675 ,0.00186157 , 0.00112915 ,0.00048828 ,0.00021362 ,0.00000000 ,0.00067139 , 0.00057983 ,0.00103760 ,0.00070190 ,0.00109863 ,0.00073242 , 0.00091553 ,0.00079346 ,0.00115967 ,0.00091553 ,0.00112915 , 0.00094604 ,0.00097656 ,0.00143433 ,0.00161743 ,0.00183105 , 0.00201416 ,0.00225830 ,0.00204468 ,0.00180054 ,0.00125122 , 0.00106812 ,0.00033569 ,0.00006104 ,-0.00006104 ,0.00045776 , 0.00112915 ,0.00140381 ,0.00161743 ,0.00100708 ,0.00067139 , 0.00030518 ,0.00024414 ,0.00054932 ,0.00064087 ,0.00033569 , 0.00000000 ,-0.00064087 ,-0.00125122 ,-0.00149536 ,-0.00161743 , -0.00119019 ,-0.00100708 ,-0.00048828 ,-0.00024414 ,-0.00015259 , -0.00024414 ,-0.00082397 ,-0.00149536 ,-0.00210571 ,-0.00213623 , -0.00177002 ,-0.00082397 ,-0.00067139 ,-0.00018311 ,-0.00021362 , -0.00057983 ,-0.00070190 ,-0.00094604 ,-0.00115967 ,-0.00103760 , -0.00088501 ,-0.00109863 ,-0.00115967 ,-0.00146484 ,-0.00189209 , -0.00222778 ,-0.00231934 ,-0.00268555 ,-0.00238037 ,-0.00277710 , -0.00277710 ,-0.00335693 ,-0.00332642 ,-0.00338745 ,-0.00277710 , -0.00262451 ,-0.00216675 ,-0.00192261 ,-0.00225830 ,-0.00234985 , -0.00283813 ,-0.00314331 ,-0.00381470 ,-0.00396729 ,-0.00451660 , -0.00436401 ,-0.00457764 ,-0.00378418 ,-0.00350952 ,-0.00274658 , -0.00265503 ,-0.00289917 ,-0.00338745 ,-0.00405884 ,-0.00463867 , -0.00500488 ,-0.00491333 ,-0.00457764 ,-0.00473022 ,-0.00482178 , -0.00540161 ,-0.00546265 ,-0.00564575 ,-0.00567627 ,-0.00537109 , -0.00509644 ,-0.00491333 ,-0.00479126 ,-0.00521851 ,-0.00543213 , -0.00524902 ,-0.00506592 ,-0.00451660 ,-0.00418091 ,-0.00381470 , -0.00378418 ,-0.00439453 ,-0.00476074 ,-0.00531006 ,-0.00595093 , -0.00650024 ,-0.00671387 ,-0.00668335 ,-0.00619507 ,-0.00579834 , -0.00595093 ,-0.00592041 ,-0.00637817 ,-0.00640869 ,-0.00680542 , -0.00717163 ,-0.00759888 ,-0.00778198 ,-0.00805664 ,-0.00811768 , -0.00756836 ,-0.00704956 ,-0.00665283 ,-0.00650024 ,-0.00634766 , -0.00653076 ,-0.00650024 ,-0.00680542 ,-0.00683594 ,-0.00717163 , -0.00704956 ,-0.00723267 ,-0.00689697 ,-0.00668335 ,-0.00689697 , -0.00698853 ,-0.00704956 ,-0.00747681 ,-0.00805664 ,-0.00857544 , -0.00900269 ,-0.00918579 ,-0.00897217 ,-0.00848389 ,-0.00808716 , -0.00817871 ,-0.00845337 ,-0.00830078 ,-0.00857544 ,-0.00814819 , -0.00799561 ,-0.00769043 ,-0.00744629 ,-0.00790405 ,-0.00854492 , -0.00869751 ,-0.00918579 ,-0.00894165 ,-0.00918579 ,-0.00872803 , -0.00869751 ,-0.00860596 ,-0.00881958 ,-0.00866699 ,-0.00888062 , -0.00927734 ,-0.00970459 ,-0.01034546 ,-0.01049805 ,-0.01083374 , -0.01049805 ,-0.01019287 ,-0.00958252 ,-0.00918579 ,-0.00863647 , -0.00875854 ,-0.00845337 ,-0.00878906 ,-0.00878906 ,-0.00924683 , -0.00915527 ,-0.00961304 ,-0.00933838 ,-0.00936890 ,-0.00918579 , -0.00903320 ,-0.00875854 ,-0.00894165 ,-0.00863647 ,-0.00942993 , -0.00967407 ,-0.00976562 ,-0.00997925 ,-0.00976562 ,-0.00939941 , -0.00939941 ,-0.00885010 ,-0.00903320 ,-0.00900269 ,-0.00918579 , -0.00976562 ,-0.01034546 ,-0.01040649 ,-0.01031494 ,-0.00949097 , -0.00888062 ,-0.00842285 ,-0.00827026 ,-0.00833130 ,-0.00894165 , -0.00897217 ,-0.00909424 ,-0.00909424 ,-0.00909424 ,-0.00918579 , -0.00936890 ,-0.00964355 ,-0.00988770 ,-0.01022339 ,-0.01031494 , -0.01019287 ,-0.01010132 ,-0.00970459 ,-0.00933838 ,-0.00939941 , -0.00909424 ,-0.00921631 ,-0.00888062 ,-0.00912476 ,-0.00912476 , -0.00909424 ,-0.00918579 ,-0.00881958 ,-0.00900269 ,-0.00909424 , -0.00900269 ,-0.00878906 ,-0.00872803 ,-0.00857544 ,-0.00842285 , -0.00811768 ,-0.00845337 ,-0.00863647 ,-0.00888062 ,-0.00903320 , -0.00839233 ,-0.00854492 ,-0.00814819 ,-0.00830078 ,-0.00872803 , -0.00863647 ,-0.00869751 ,-0.00857544 ,-0.00851440 ,-0.00875854 , -0.00930786 ,-0.00888062 ,-0.00888062 ,-0.00842285 ,-0.00842285 , -0.00827026 ,-0.00823975 ,-0.00842285 ,-0.00854492 ,-0.00900269 , -0.00909424 ,-0.00900269 ,-0.00897217 ,-0.00857544 ,-0.00888062 , -0.00863647 ,-0.00872803 ,-0.00924683 ,-0.00839233 ,-0.00820923 , -0.00750732 ,-0.00692749 ,-0.00689697 ,-0.00720215 ,-0.00753784 , -0.00814819 ,-0.00814819 ,-0.00830078 ,-0.00802612 ,-0.00799561 , -0.00772095 ,-0.00793457 ,-0.00808716 ,-0.00811768 ,-0.00836182 , -0.00839233 ,-0.00823975 ,-0.00851440 ,-0.00756836 ,-0.00729370 , -0.00717163 ,-0.00723267 ,-0.00753784 ,-0.00756836 ,-0.00790405 , -0.00805664 ,-0.00784302 ,-0.00799561 ,-0.00796509 ,-0.00787354 , -0.00784302 ,-0.00778198 ,-0.00769043 ,-0.00729370 ,-0.00717163 , -0.00677490 ,-0.00659180 ,-0.00656128 ,-0.00689697 ,-0.00732422 , -0.00778198 ,-0.00784302 ,-0.00750732 ,-0.00735474 ,-0.00668335 , -0.00671387 ,-0.00659180 ,-0.00680542 ,-0.00762939 ,-0.00808716 , -0.00811768 ,-0.00814819 ,-0.00750732 ,-0.00689697 ,-0.00650024 , -0.00643921 ,-0.00659180 ,-0.00708008 ,-0.00735474 ,-0.00781250 , -0.00790405 ,-0.00759888 ,-0.00735474 ,-0.00689697 ,-0.00656128 , -0.00634766 ,-0.00650024 ,-0.00650024 ,-0.00653076 ,-0.00640869 , -0.00634766 ,-0.00625610 ,-0.00668335 ,-0.00680542 ,-0.00683594 , -0.00683594 ,-0.00680542 ,-0.00665283 ,-0.00692749 ,-0.00711060 , -0.00720215 ,-0.00704956 ,-0.00708008 ,-0.00665283 ,-0.00671387 , -0.00637817 ,-0.00616455 ,-0.00628662 ,-0.00619507 ,-0.00668335 , -0.00744629 ,-0.00759888 ,-0.00775146 ,-0.00720215 ,-0.00708008 , -0.00659180 ,-0.00665283 ,-0.00616455 ,-0.00622559 ,-0.00646973 , -0.00683594 ,-0.00701904 ,-0.00698853 ,-0.00671387 ,-0.00665283 , -0.00637817 ,-0.00637817 ,-0.00643921 ,-0.00662231 ,-0.00686646 , -0.00708008 ,-0.00714111 ,-0.00720215 ,-0.00704956 ,-0.00686646 , -0.00677490 ,-0.00643921 ,-0.00665283 ,-0.00692749 ,-0.00704956 , -0.00723267 ,-0.00714111 ,-0.00708008 ,-0.00735474 ,-0.00732422 , -0.00741577 ,-0.00714111 ,-0.00732422 ,-0.00711060 ,-0.00726318 , -0.00711060 ,-0.00738525 ,-0.00759888 ,-0.00756836 ,-0.00787354 , -0.00753784 ,-0.00769043 ,-0.00692749 ,-0.00738525 ,-0.00698853 , -0.00723267 ,-0.00686646 ,-0.00704956 ,-0.00704956 ,-0.00729370 , -0.00759888 ,-0.00762939 ,-0.00802612 ,-0.00842285 ,-0.00814819 , -0.00784302 ,-0.00711060 ,-0.00637817 ,-0.00631714 ,-0.00637817 , -0.00677490 ,-0.00714111 ,-0.00769043 ,-0.00784302 ,-0.00741577 , -0.00723267 ,-0.00668335 ,-0.00692749 ,-0.00744629 ,-0.00784302 , -0.00827026 ,-0.00863647 ,-0.00854492 ,-0.00790405 ,-0.00747681 , -0.00677490 ,-0.00686646 ,-0.00692749 ,-0.00711060 ,-0.00729370 , -0.00698853 ,-0.00680542 ,-0.00656128 ,-0.00616455 ,-0.00607300 , -0.00619507 ,-0.00634766 ,-0.00717163 ,-0.00753784 ,-0.00769043 , -0.00796509 ,-0.00778198 ,-0.00762939 ,-0.00775146 ,-0.00796509 , -0.00811768 ,-0.00857544 ,-0.00836182 ,-0.00863647 ,-0.00799561 , -0.00799561 ,-0.00747681 ,-0.00750732 ,-0.00689697 ,-0.00698853 , -0.00686646 ,-0.00756836 ,-0.00762939 ,-0.00720215 ,-0.00695801 , -0.00628662 ,-0.00604248 ,-0.00604248 ,-0.00640869 ,-0.00683594 , -0.00695801 ,-0.00711060 ,-0.00659180 ,-0.00665283 ,-0.00619507 , -0.00607300 ,-0.00640869 ,-0.00650024 ,-0.00708008 ,-0.00735474 , -0.00726318 ,-0.00726318 ,-0.00674438 ,-0.00659180 ,-0.00665283 , -0.00695801 ,-0.00729370 ,-0.00750732 ,-0.00735474 ,-0.00717163 , -0.00689697 ,-0.00640869 ,-0.00598145 ,-0.00598145 ,-0.00576782 , -0.00579834 ,-0.00592041 ,-0.00619507 ,-0.00653076 ,-0.00668335 , -0.00643921 ,-0.00637817 ,-0.00579834 ,-0.00592041 ,-0.00564575 , -0.00595093 ,-0.00595093 ,-0.00646973 ,-0.00671387 ,-0.00683594 , -0.00659180 ,-0.00631714 ,-0.00598145 ,-0.00579834 ,-0.00570679 , -0.00546265 ,-0.00546265 ,-0.00509644 ,-0.00509644 ,-0.00463867 , -0.00466919 ,-0.00491333 ,-0.00476074 ,-0.00537109 ,-0.00543213 , -0.00521851 ,-0.00488281 ,-0.00436401 ,-0.00393677 ,-0.00363159 , -0.00366211 ,-0.00372314 ,-0.00344849 ,-0.00350952 ,-0.00347900 , -0.00372314 ,-0.00381470 ,-0.00378418 ,-0.00381470 ,-0.00350952 , -0.00326538 ,-0.00344849 ,-0.00363159 ,-0.00372314 ,-0.00344849 , -0.00317383 ,-0.00308228 ,-0.00271606 ,-0.00329590 ,-0.00357056 , -0.00372314 ,-0.00369263 ,-0.00357056 ,-0.00296021 ,-0.00302124 , -0.00262451 ,-0.00265503 ,-0.00244141 ,-0.00241089 ,-0.00228882 , -0.00241089 ,-0.00238037 ,-0.00259399 ,-0.00253296 ,-0.00213623 , -0.00195312 ,-0.00140381 ,-0.00149536 ,-0.00140381 ,-0.00180054 , -0.00231934 ,-0.00268555 ,-0.00259399 ,-0.00213623 ,-0.00183105 , -0.00149536 ,-0.00122070 ,-0.00097656 ,-0.00088501 ,-0.00112915 , -0.00112915 ,-0.00097656 ,-0.00106812 ,-0.00112915 ,-0.00112915 , -0.00122070 ,-0.00094604 ,-0.00094604 ,-0.00064087 ,-0.00082397 , -0.00045776 ,-0.00054932 ,-0.00039673 ,-0.00085449 ,-0.00109863 , -0.00137329 ,-0.00119019 ,-0.00094604 ,-0.00051880 ,0.00003052 , -0.00030518 ,-0.00048828 ,-0.00106812 ,-0.00085449 ,-0.00094604 , -0.00048828 ,-0.00030518 ,-0.00045776 ,0.00003052 ,-0.00006104 , 0.00015259 ,0.00033569 ,0.00067139 ,0.00061035 ,0.00070190 , 0.00036621 ,0.00018311 ,0.00009155 ,0.00030518 ,0.00048828 , 0.00057983 ,0.00045776 ,0.00036621 ,-0.00009155 ,0.00015259 , 0.00051880 ,0.00134277 ,0.00158691 ,0.00177002 ,0.00195312 , 0.00137329 ,0.00143433 ,0.00125122 ,0.00158691 ,0.00186157 , 0.00216675 ,0.00256348 ,0.00271606 ,0.00265503 ,0.00265503 , 0.00268555 ,0.00283813 ,0.00280762 ,0.00289917 ,0.00326538 , 0.00302124 ,0.00341797 ,0.00320435 ,0.00360107 ,0.00317383 , 0.00338745 ,0.00323486 ,0.00329590 ,0.00320435 ,0.00344849 , 0.00335693 ,0.00326538 ,0.00329590 ,0.00323486 ,0.00350952 , 0.00350952 ,0.00375366 ,0.00399780 ,0.00402832 ,0.00427246 , 0.00415039 ,0.00433350 ,0.00411987 ,0.00439453 ,0.00460815 , 0.00479126 ,0.00482178 ,0.00506592 ,0.00479126 ,0.00485229 , 0.00491333 ,0.00555420 ,0.00576782 ,0.00640869 ,0.00646973 , 0.00640869 ,0.00585938 ,0.00543213 ,0.00540161 ,0.00555420 , 0.00585938 ,0.00579834 ,0.00604248 ,0.00576782 ,0.00558472 , 0.00588989 ,0.00567627 ,0.00628662 ,0.00619507 ,0.00628662 , 0.00628662 ,0.00604248 ,0.00628662 ,0.00595093 ,0.00637817 , 0.00622559 ,0.00640869 ,0.00643921 ,0.00646973 ,0.00646973 , 0.00671387 ,0.00692749 ,0.00686646 ,0.00698853 ,0.00708008 , 0.00720215 ,0.00704956 ,0.00674438 ,0.00665283 ,0.00631714 , 0.00650024 ,0.00628662 ,0.00634766 ,0.00680542 ,0.00729370 , 0.00717163 ,0.00723267 ,0.00738525 ,0.00729370 ,0.00756836 , 0.00741577 ,0.00756836 ,0.00738525 ,0.00747681 ,0.00759888 , 0.00769043 ,0.00808716 ,0.00765991 ,0.00793457 ,0.00750732 , 0.00781250 ,0.00729370 ,0.00753784 ,0.00711060 ,0.00744629 , 0.00735474 ,0.00762939 ,0.00769043 ,0.00775146 ,0.00759888 , 0.00787354 ,0.00814819 ,0.00811768 ,0.00830078 ,0.00814819 , 0.00784302 ,0.00756836 ,0.00781250 ,0.00808716 ,0.00848389 , 0.00863647 ,0.00891113 ,0.00906372 ,0.00885010 ,0.00881958 , 0.00854492 ,0.00894165 ,0.00866699 ,0.00927734 ,0.00930786 , 0.00924683 ,0.00897217 ,0.00827026 ,0.00796509 ,0.00805664 , 0.00869751 ,0.00912476 ,0.00979614 ,0.00970459 ,0.00979614 , 0.00949097 ,0.00927734 ,0.00915527 ,0.00939941 ,0.00918579 , 0.00964355 ,0.00936890 ,0.00946045 ,0.00964355 ,0.00979614 , 0.00985718 ,0.01013184 ,0.01043701 ,0.00988770 ,0.01013184 , 0.00964355 ,0.01022339 ,0.01019287 ,0.01052856 ,0.01028442 , 0.01028442 ,0.01016235 ,0.01013184 ,0.00976562 ,0.00949097 , 0.00991821 ,0.00970459 ,0.00994873 ,0.00936890 ,0.00933838 , 0.00918579 ,0.00900269 ,0.00933838 ,0.00982666 ,0.01028442 , 0.01092529 ,0.01132202 ,0.01092529 ,0.01080322 ,0.01022339 , 0.01062012 ,0.01016235 ,0.01071167 ,0.01043701 ,0.01052856 , 0.01049805 ,0.01049805 ,0.01074219 ,0.01058960 ,0.01086426 , 0.01104736 ,0.01119995 ,0.01104736 ,0.01089478 ,0.01104736 , 0.01095581 ,0.01110840 ,0.01104736 ,0.01101685 ,0.01077271 , 0.01065063 ,0.01046753 ,0.01034546 ,0.01068115 ,0.01049805 , 0.01104736 ,0.01126099 ,0.01138306 ,0.01123047 ,0.01104736 , 0.01101685 ,0.01083374 ,0.01095581 ,0.01049805 ,0.01058960 , 0.01058960 ,0.01049805 ,0.01034546 ,0.01068115 ,0.01058960 , 0.01077271 ,0.01104736 ,0.01086426 ,0.01116943 ,0.01095581 , 0.01138306 ,0.01116943 ,0.01123047 ,0.01165771 ,0.01135254 , 0.01162720 ,0.01113892 ,0.01129150 ,0.01129150 ,0.01184082 , 0.01153564 ,0.01181030 ,0.01187134 ,0.01208496 ,0.01214600 , 0.01214600 ,0.01205444 ,0.01181030 ,0.01177979 ,0.01156616 , 0.01147461 ,0.01150513 ,0.01126099 ,0.01132202 ,0.01132202 , 0.01165771 ,0.01153564 ,0.01196289 ,0.01184082 ,0.01214600 , 0.01232910 ,0.01254272 ,0.01284790 ,0.01312256 ,0.01333618 , 0.01312256 ,0.01281738 ,0.01263428 ,0.01239014 ,0.01226807 , 0.01217651 ,0.01226807 ,0.01217651 ,0.01263428 ,0.01263428 , 0.01290894 ,0.01290894 ,0.01303101 ,0.01281738 ,0.01248169 , 0.01242065 ,0.01239014 ,0.01257324 ,0.01278687 ,0.01290894 , 0.01339722 ,0.01315308 ,0.01303101 ,0.01284790 ,0.01275635 , 0.01290894 ,0.01330566 ,0.01324463 ,0.01354980 ,0.01354980 , 0.01309204 ,0.01275635 ,0.01251221 ,0.01245117 ,0.01293945 , 0.01290894 ,0.01303101 ,0.01330566 ,0.01342773 ,0.01321411 , 0.01342773 ,0.01324463 ,0.01318359 ,0.01318359 ,0.01324463 , 0.01324463 ,0.01318359 ,0.01327515 ,0.01318359 ,0.01330566 , 0.01327515 ,0.01312256 ,0.01275635 ,0.01300049 ,0.01275635 , 0.01312256 ,0.01287842 ,0.01306152 ,0.01290894 ,0.01296997 , 0.01318359 ,0.01287842 ,0.01318359 ,0.01296997 ,0.01309204 , 0.01330566 ,0.01342773 ,0.01342773 ,0.01370239 ,0.01354980 , 0.01379395 ,0.01321411 ,0.01351929 ,0.01345825 ,0.01336670 , 0.01318359 ,0.01266479 ,0.01260376 ,0.01251221 ,0.01245117 , 0.01272583 ,0.01272583 ,0.01266479 ,0.01260376 ,0.01245117 , 0.01226807 ,0.01232910 ,0.01156616 ,0.01153564 ,0.01159668 , 0.01193237 ,0.01214600 ,0.01257324 ,0.01266479 ,0.01272583 , 0.01235962 ,0.01229858 ,0.01187134 ,0.01220703 ,0.01226807 , 0.01269531 ,0.01278687 ,0.01281738 ,0.01260376 ,0.01251221 , 0.01220703 ,0.01220703 ,0.01245117 ,0.01245117 ,0.01266479 , 0.01223755 ,0.01242065 ,0.01190186 ,0.01196289 ,0.01174927 , 0.01156616 ,0.01144409 ,0.01168823 ,0.01184082 ,0.01196289 , 0.01187134 ,0.01199341 ,0.01199341 ,0.01187134 ,0.01177979 , 0.01132202 ,0.01132202 ,0.01095581 ,0.01129150 ,0.01119995 , 0.01144409 ,0.01150513 ,0.01196289 ,0.01190186 ,0.01199341 , 0.01174927 ,0.01208496 ,0.01193237 ,0.01193237 ,0.01159668 , 0.01141357 ,0.01074219 ,0.01062012 ,0.01058960 ,0.01068115 , 0.01095581 ,0.01113892 ,0.01123047 ,0.01138306 ,0.01083374 , 0.01083374 ,0.01071167 ,0.01083374 ,0.01068115 ,0.01074219 , 0.01086426 ,0.01083374 ,0.01077271 ,0.01034546 ,0.01013184 , 0.00961304 ,0.01007080 ,0.00994873 ,0.01028442 ,0.01022339 , 0.00994873 ,0.00970459 ,0.00994873 ,0.01000977 ,0.00994873 , 0.01013184 ,0.01004028 ,0.00997925 ,0.00991821 ,0.00958252 , 0.00955200 ,0.00930786 ,0.00946045 ,0.00958252 ,0.00961304 , 0.00970459 ,0.00958252 ,0.00949097 ,0.00927734 ,0.00909424 , 0.00885010 ,0.00900269 ,0.00869751 ,0.00872803 ,0.00897217 , 0.00875854 ,0.00881958 ,0.00863647 ,0.00872803 ,0.00894165 , 0.00888062 ,0.00903320 ,0.00924683 ,0.00891113 ,0.00891113 , 0.00875854 ,0.00811768 ,0.00814819 ,0.00738525 ,0.00753784 , 0.00750732 ,0.00784302 ,0.00762939 ,0.00762939 ,0.00708008 , 0.00695801 ,0.00662231 ,0.00646973 ,0.00646973 ,0.00692749 , 0.00698853 ,0.00741577 ,0.00744629 ,0.00723267 ,0.00765991 , 0.00720215 ,0.00708008 ,0.00708008 ,0.00683594 ,0.00680542 , 0.00665283 ,0.00708008 ,0.00686646 ,0.00701904 ,0.00680542 , 0.00662231 ,0.00646973 ,0.00640869 ,0.00662231 ,0.00674438 , 0.00695801 ,0.00683594 ,0.00671387 ,0.00625610 ,0.00607300 , 0.00592041 ,0.00604248 ,0.00598145 ,0.00643921 ,0.00637817 , 0.00656128 ,0.00616455 ,0.00576782 ,0.00558472 ,0.00546265 , 0.00534058 ,0.00561523 ,0.00549316 ,0.00546265 ,0.00531006 , 0.00527954 ,0.00512695 ,0.00537109 ,0.00561523 ,0.00546265 , 0.00570679 ,0.00570679 ,0.00546265 ,0.00537109 ,0.00540161 , 0.00518799 ,0.00534058 ,0.00500488 ,0.00527954 ,0.00476074 , 0.00494385 ,0.00460815 ,0.00473022 ,0.00448608 ,0.00469971 , 0.00457764 ,0.00473022 ,0.00445557 ,0.00436401 ,0.00427246 , 0.00402832 ,0.00448608 ,0.00448608 ,0.00469971 ,0.00469971 , 0.00439453 ,0.00436401 ,0.00430298 ,0.00430298 ,0.00418091 , 0.00424194 ,0.00402832 ,0.00357056 ,0.00360107 ,0.00350952 , 0.00381470 ,0.00405884 ,0.00451660 ,0.00448608 ,0.00396729 , 0.00357056 ,0.00344849 ,0.00338745 ,0.00344849 ,0.00344849 , 0.00335693 ,0.00289917 ,0.00305176 ,0.00262451 ,0.00286865 , 0.00283813 ,0.00299072 ,0.00271606 ,0.00247192 ,0.00256348 , 0.00234985 ,0.00289917 ,0.00289917 ,0.00329590 ,0.00302124 , 0.00344849 ,0.00354004 ,0.00335693 ,0.00372314 ,0.00332642 , 0.00329590 ,0.00314331 ,0.00317383 ,0.00305176 ,0.00350952 , 0.00344849 ,0.00363159 ,0.00271606 ,0.00274658 ,0.00228882 , 0.00228882 ,0.00216675 ,0.00228882 ,0.00238037 ,0.00234985 , 0.00219727 ,0.00201416 ,0.00204468 ,0.00210571 ,0.00192261 , 0.00225830 ,0.00183105 ,0.00225830 ,0.00198364 ,0.00222778 , 0.00207520 ,0.00177002 ,0.00158691 ,0.00146484 ,0.00140381 , 0.00152588 ,0.00180054 ,0.00198364 ,0.00231934 ,0.00253296 , 0.00274658 ,0.00228882 ,0.00210571 ,0.00122070 ,0.00128174 , 0.00115967 ,0.00119019 ,0.00134277 ,0.00155640 ,0.00137329 , 0.00134277 ,0.00109863 ,0.00070190 ,0.00054932 ,0.00064087 , 0.00091553 ,0.00054932 ,0.00091553 ,0.00094604 ,0.00091553 , 0.00088501 ,0.00109863 ,0.00115967 ,0.00131226 ,0.00115967 , 0.00112915 ,0.00106812 ,0.00109863 ,0.00039673 ,0.00024414 , -0.00009155 ,0.00012207 ,-0.00012207 ,0.00021362 ,0.00033569 , -0.00024414 ,0.00000000 ,-0.00030518 ,-0.00042725 ,-0.00036621 , -0.00064087 ,-0.00036621 ,-0.00051880 ,-0.00051880 ,-0.00088501 , -0.00137329 ,-0.00131226 ,-0.00134277 ,-0.00091553 ,-0.00082397 , -0.00006104 ,-0.00036621 ,-0.00021362 ,-0.00067139 ,-0.00146484 , -0.00164795 ,-0.00186157 ,-0.00177002 ,-0.00152588 ,-0.00112915 , -0.00125122 ,-0.00128174 ,-0.00143433 ,-0.00155640 ,-0.00164795 , -0.00183105 ,-0.00155640 ,-0.00198364 ,-0.00173950 ,-0.00204468 , -0.00173950 ,-0.00155640 ,-0.00149536 ,-0.00152588 ,-0.00167847 , -0.00207520 ,-0.00244141 ,-0.00256348 ,-0.00238037 ,-0.00234985 , -0.00216675 ,-0.00219727 ,-0.00247192 ,-0.00247192 ,-0.00286865 , -0.00299072 ,-0.00311279 ,-0.00308228 ,-0.00308228 ,-0.00314331 , -0.00335693 ,-0.00305176 ,-0.00341797 ,-0.00335693 ,-0.00341797 , -0.00350952 ,-0.00375366 ,-0.00347900 ,-0.00366211 ,-0.00363159 , -0.00378418 ,-0.00357056 ,-0.00363159 ,-0.00332642 ,-0.00341797 , -0.00347900 ,-0.00354004 ,-0.00424194 ,-0.00430298 ,-0.00454712 , -0.00476074 ,-0.00512695 ,-0.00521851 ,-0.00515747 ,-0.00473022 , -0.00479126 ,-0.00466919 ,-0.00469971 ,-0.00473022 ,-0.00485229 , -0.00503540 ,-0.00521851 ,-0.00509644 ,-0.00485229 ,-0.00479126 , -0.00476074 ,-0.00503540 ,-0.00515747 ,-0.00531006 ,-0.00534058 , -0.00576782 ,-0.00619507 ,-0.00601196 ,-0.00643921 ,-0.00607300 , -0.00613403 ,-0.00582886 ,-0.00607300 ,-0.00640869 ,-0.00646973 , -0.00683594 ,-0.00689697 ,-0.00704956 ,-0.00729370 ,-0.00695801 , -0.00717163 ,-0.00695801 ,-0.00711060 ,-0.00729370 ,-0.00726318 , -0.00765991 ,-0.00729370 ,-0.00759888 ,-0.00729370 ,-0.00765991 , -0.00765991 ,-0.00750732 ,-0.00787354 ,-0.00778198 ,-0.00814819 , -0.00817871 ,-0.00827026 ,-0.00860596 ,-0.00851440 ,-0.00872803 , -0.00869751 ,-0.00848389 ,-0.00851440 ,-0.00839233 ,-0.00827026 , -0.00839233 ,-0.00885010 ,-0.00903320 ,-0.00921631 ,-0.00912476 , -0.00903320 ,-0.00830078 ,-0.00836182 ,-0.00823975 ,-0.00839233 , -0.00875854 ,-0.00912476 ,-0.00942993 ,-0.00961304 ,-0.00994873 , -0.00967407 ,-0.00949097 ,-0.00915527 ,-0.00909424 ,-0.00860596 , -0.00875854 ,-0.00915527 ,-0.00942993 ,-0.00973511 ,-0.00994873 , -0.00997925 ,-0.00997925 ,-0.00994873 ,-0.00997925 ,-0.00939941 , -0.00936890 ,-0.00900269 ,-0.00927734 ,-0.00946045 ,-0.00964355 , -0.00988770 ,-0.01034546 ,-0.01034546 ,-0.01071167 ,-0.01095581 , -0.01083374 ,-0.01065063 ,-0.01022339 ,-0.01016235 ,-0.00994873 , -0.01025391 ,-0.01019287 ,-0.01049805 ,-0.01095581 ,-0.01058960 , -0.01071167 ,-0.01049805 ,-0.01034546 ,-0.00994873 ,-0.00973511 , -0.00949097 ,-0.00997925 ,-0.01010132 ,-0.01040649 ,-0.01058960 , -0.01098633 ,-0.01107788 ,-0.01083374 ,-0.01080322 ,-0.01068115 , -0.01083374 ,-0.01089478 ,-0.01086426 ,-0.01055908 ,-0.01049805 , -0.01049805 ,-0.01068115 ,-0.01077271 ,-0.01107788 ,-0.01116943 , -0.01119995 ,-0.01126099 ,-0.01071167 ,-0.01043701 ,-0.01028442 , -0.01013184 ,-0.01031494 ,-0.00982666 ,-0.01040649 ,-0.01028442 , -0.01052856 ,-0.01052856 ,-0.01077271 ,-0.01098633 ,-0.01135254 , -0.01150513 ,-0.01171875 ,-0.01144409 ,-0.01138306 ,-0.01098633 , -0.01104736 ,-0.01129150 ,-0.01132202 ,-0.01150513 ,-0.01177979 , -0.01165771 ,-0.01144409 ,-0.01071167 ,-0.01043701 ,-0.01004028 , -0.01019287 ,-0.01046753 ,-0.01071167 ,-0.01129150 ,-0.01153564 , -0.01165771 ,-0.01098633 ,-0.01092529 ,-0.01065063 ,-0.01055908 , -0.01062012 ,-0.01068115 ,-0.01071167 ,-0.01092529 ,-0.01068115 , -0.01055908 ,-0.01028442 ,-0.01028442 ,-0.01058960 ,-0.01068115 , -0.01089478 ,-0.01086426 ,-0.01141357 ,-0.01101685 ,-0.01092529 , -0.01062012 ,-0.01037598 ,-0.01037598 ,-0.01058960 ,-0.01052856 , -0.01092529 ,-0.01098633 ,-0.01058960 ,-0.01058960 ,-0.01046753 , -0.01016235 ,-0.01037598 ,-0.01016235 ,-0.01010132 ,-0.01034546 , -0.01010132 ,-0.01019287 ,-0.00979614 ,-0.00982666 ,-0.00930786 , -0.00939941 ,-0.00912476 ,-0.00955200 ,-0.00927734 ,-0.00958252 , -0.00946045 ,-0.00973511 ,-0.00973511 ,-0.00952148 ,-0.00967407 , -0.00933838 ,-0.00946045 ,-0.00946045 ,-0.00921631 ,-0.00915527 , -0.00912476 ,-0.00921631 ,-0.00915527 ,-0.00909424 ,-0.00930786 , -0.00936890 ,-0.00924683 ,-0.00927734 ,-0.00933838 ,-0.00924683 , -0.00946045 ,-0.00912476 ,-0.00915527 ,-0.00854492 ,-0.00851440 , -0.00814819 ,-0.00827026 ,-0.00830078 ,-0.00823975 ,-0.00820923 , -0.00811768 ,-0.00814819 ,-0.00805664 ,-0.00820923 ,-0.00851440 , -0.00863647 ,-0.00875854 ,-0.00866699 ,-0.00857544 ,-0.00814819 , -0.00814819 ,-0.00790405 ,-0.00765991 ,-0.00759888 ,-0.00747681 , -0.00753784 ,-0.00735474 ,-0.00753784 ,-0.00738525 ,-0.00765991 , -0.00765991 ,-0.00775146 ,-0.00759888 ,-0.00735474 ,-0.00714111 , -0.00711060 ,-0.00698853 ,-0.00729370 ,-0.00714111 ,-0.00756836 , -0.00741577 ,-0.00729370 ,-0.00662231 ,-0.00695801 ,-0.00698853 , -0.00717163 ,-0.00711060 ,-0.00747681 ,-0.00729370 ,-0.00747681 , -0.00729370 ,-0.00720215 ,-0.00747681 ,-0.00720215 ,-0.00708008 , -0.00659180 ,-0.00677490 ,-0.00634766 ,-0.00625610 ,-0.00610352 , -0.00646973 ,-0.00637817 ,-0.00674438 ,-0.00704956 ,-0.00683594 , -0.00689697 ,-0.00643921 ,-0.00659180 ,-0.00640869 ,-0.00622559 , -0.00640869 ,-0.00656128 ,-0.00671387 ,-0.00668335 ,-0.00671387 , -0.00671387 ,-0.00637817 ,-0.00616455 ,-0.00610352 ,-0.00564575 , -0.00598145 ,-0.00582886 ,-0.00607300 ,-0.00640869 ,-0.00662231 , -0.00650024 ,-0.00619507 ,-0.00558472 ,-0.00552368 ,-0.00506592 , -0.00515747 ,-0.00527954 ,-0.00564575 ,-0.00546265 ,-0.00567627 , -0.00555420 ,-0.00546265 ,-0.00561523 ,-0.00518799 ,-0.00549316 , -0.00573730 ,-0.00582886 ,-0.00616455 ,-0.00582886 ,-0.00561523 , -0.00518799 ,-0.00485229 ,-0.00491333 ,-0.00473022 ,-0.00546265 , -0.00555420 ,-0.00579834 ,-0.00558472 ,-0.00534058 ,-0.00497437 , -0.00500488 ,-0.00463867 ,-0.00506592 ,-0.00534058 ,-0.00537109 , -0.00543213 ,-0.00515747 ,-0.00473022 ,-0.00454712 ,-0.00405884 , -0.00427246 ,-0.00457764 ,-0.00469971 ,-0.00476074 ,-0.00469971 , -0.00485229 ,-0.00512695 ,-0.00506592 ,-0.00543213 ,-0.00521851 , -0.00509644 ,-0.00531006 ,-0.00500488 ,-0.00494385 ,-0.00451660 , -0.00439453 ,-0.00408936 ,-0.00418091 ,-0.00418091 ,-0.00463867 , -0.00500488 ,-0.00521851 ,-0.00509644 ,-0.00515747 ,-0.00482178 , -0.00473022 ,-0.00430298 ,-0.00457764 ,-0.00430298 ,-0.00439453 , -0.00430298 ,-0.00439453 ,-0.00421143 ,-0.00448608 ,-0.00415039 , -0.00454712 ,-0.00433350 ,-0.00497437 ,-0.00488281 ,-0.00512695 , -0.00503540 ,-0.00497437 ,-0.00485229 ,-0.00445557 ,-0.00436401 , -0.00405884 ,-0.00451660 ,-0.00427246 ,-0.00454712 ,-0.00411987 , -0.00408936 ,-0.00390625 ,-0.00408936 ,-0.00381470 ,-0.00384521 , -0.00387573 ,-0.00424194 ,-0.00427246 ,-0.00402832 ,-0.00424194 , -0.00354004 ,-0.00360107 ,-0.00338745 ,-0.00326538 ,-0.00384521 , -0.00363159 ,-0.00402832 ,-0.00393677 ,-0.00393677 ,-0.00381470 , -0.00350952 ,-0.00354004 ,-0.00350952 ,-0.00341797 ,-0.00372314 , -0.00357056 ,-0.00360107 ,-0.00357056 ,-0.00335693 ,-0.00363159 , -0.00338745 ,-0.00366211 ,-0.00369263 ,-0.00344849 ,-0.00314331 , -0.00268555 ,-0.00262451 ,-0.00250244 ,-0.00268555 ,-0.00292969 , -0.00338745 ,-0.00326538 ,-0.00360107 ,-0.00320435 ,-0.00332642 , -0.00296021 ,-0.00286865 ,-0.00314331 ,-0.00317383 ,-0.00299072 , -0.00314331 ,-0.00326538 ,-0.00320435 ,-0.00296021 ,-0.00283813 , -0.00268555 ,-0.00228882 ,-0.00234985 ,-0.00189209 ,-0.00183105 , -0.00161743 ,-0.00186157 ,-0.00158691 ,-0.00186157 ,-0.00170898 , -0.00149536 ,-0.00152588 ,-0.00152588 ,-0.00155640 ,-0.00158691 , -0.00155640 ,-0.00140381 ,-0.00146484 ,-0.00131226 ,-0.00119019 , -0.00112915 ,-0.00125122 ,-0.00109863 ,-0.00091553 ,-0.00085449 , -0.00067139 ,-0.00051880 ,-0.00054932 ,-0.00033569 ,-0.00064087 , -0.00082397 ,-0.00082397 ,-0.00115967 ,-0.00076294 ,-0.00082397 , -0.00097656 ,-0.00073242 ,-0.00070190 ,-0.00057983 ,-0.00042725 , -0.00054932 ,-0.00024414 ,-0.00009155 ,0.00012207 ,0.00027466 , 0.00042725 ,0.00051880 ,0.00018311 ,0.00042725 ,0.00030518 , 0.00024414 ,0.00051880 ,0.00057983 ,0.00076294 ,0.00051880 , 0.00073242 ,0.00030518 ,0.00024414 ,0.00015259 ,0.00027466 , 0.00054932 ,0.00064087 ,0.00070190 ,0.00094604 ,0.00091553 , 0.00094604 ,0.00088501 ,0.00103760 ,0.00112915 ,0.00143433 , 0.00170898 ,0.00137329 ,0.00167847 ,0.00106812 ,0.00134277 , 0.00119019 ,0.00149536 ,0.00158691 ,0.00167847 ,0.00198364 , 0.00183105 ,0.00195312 ,0.00177002 ,0.00231934 ,0.00213623 , 0.00262451 ,0.00250244 ,0.00262451 ,0.00292969 ,0.00274658 , 0.00299072 ,0.00277710 ,0.00308228 ,0.00283813 ,0.00292969 , 0.00299072 ,0.00308228 ,0.00292969 ,0.00311279 ,0.00308228 , 0.00308228 ,0.00317383 ,0.00335693 ,0.00357056 ,0.00341797 , 0.00354004 ,0.00338745 ,0.00354004 ,0.00317383 ,0.00332642 , 0.00311279 ,0.00308228 ,0.00354004 ,0.00375366 ,0.00405884 , 0.00424194 ,0.00411987 ,0.00393677 ,0.00408936 ,0.00430298 , 0.00436401 ,0.00466919 ,0.00473022 ,0.00473022 ,0.00500488 , 0.00491333 ,0.00500488 ,0.00488281 ,0.00506592 ,0.00491333 , 0.00527954 ,0.00503540 ,0.00494385 ,0.00509644 ,0.00479126 , 0.00488281 ,0.00473022 ,0.00473022 ,0.00469971 ,0.00491333 , 0.00527954 ,0.00537109 ,0.00546265 ,0.00540161 ,0.00491333 , 0.00500488 ,0.00494385 ,0.00564575 ,0.00592041 ,0.00616455 , 0.00604248 ,0.00595093 ,0.00598145 ,0.00585938 ,0.00588989 , 0.00604248 ,0.00601196 ,0.00659180 ,0.00613403 ,0.00610352 , 0.00582886 ,0.00540161 ,0.00552368 ,0.00555420 ,0.00585938 , 0.00613403 ,0.00619507 ,0.00616455 ,0.00610352 ,0.00595093 , 0.00598145 ,0.00598145 ,0.00631714 ,0.00646973 ,0.00646973 , 0.00671387 ,0.00628662 ,0.00665283 ,0.00607300 ,0.00613403 , 0.00613403 ,0.00637817 ,0.00643921 ,0.00677490 ,0.00683594 , 0.00704956 ,0.00717163 ,0.00683594 ,0.00650024 ,0.00683594 , 0.00650024 ,0.00668335 ,0.00665283 ,0.00689697 ,0.00695801 , 0.00671387 ,0.00671387 ,0.00625610 ,0.00646973 ,0.00634766 , 0.00689697 ,0.00686646 ,0.00726318 ,0.00729370 ,0.00726318 , 0.00695801 ,0.00689697 ,0.00665283 ,0.00695801 ,0.00735474 , 0.00762939 ,0.00808716 ,0.00802612 ,0.00769043 ,0.00750732 , 0.00704956 ,0.00720215 ,0.00711060 ,0.00726318 ,0.00738525 , 0.00765991 ,0.00781250 ,0.00790405 ,0.00750732 ,0.00756836 , 0.00735474 ,0.00741577 ,0.00753784 ,0.00772095 ,0.00723267 , 0.00741577 ,0.00732422 ,0.00741577 ,0.00741577 ,0.00753784 , 0.00790405 ,0.00759888 ,0.00787354 ,0.00732422 ,0.00747681 , 0.00732422 ,0.00708008 ,0.00741577 ,0.00744629 ,0.00762939 , 0.00765991 ,0.00784302 ,0.00781250 ,0.00750732 ,0.00762939 , 0.00750732 ,0.00762939 ,0.00762939 ,0.00805664 ,0.00808716 , 0.00814819 ,0.00790405 ,0.00802612 ,0.00781250 ,0.00793457 , 0.00805664 ,0.00796509 ,0.00802612 ,0.00772095 ,0.00750732 , 0.00732422 ,0.00738525 ,0.00744629 ,0.00756836 ,0.00796509 , 0.00799561 ,0.00830078 ,0.00802612 ,0.00772095 ,0.00741577 , 0.00723267 ,0.00732422 ,0.00747681 ,0.00729370 ,0.00732422 , 0.00747681 ,0.00741577 ,0.00726318 ,0.00744629 ,0.00762939 , 0.00753784 ,0.00820923 ,0.00769043 ,0.00805664 ,0.00747681 , 0.00747681 ,0.00701904 ,0.00732422 ,0.00720215 ,0.00759888 , 0.00759888 ,0.00781250 ,0.00796509 ,0.00796509 ,0.00769043 , 0.00738525 ,0.00720215 ,0.00714111 ,0.00723267 ,0.00759888 , 0.00790405 ,0.00802612 ,0.00836182 ,0.00823975 ,0.00808716 , 0.00790405 ,0.00775146 ,0.00775146 ,0.00805664 ,0.00769043 , 0.00793457 ,0.00781250 ,0.00814819 ,0.00769043 ,0.00775146 , 0.00775146 ,0.00781250 ,0.00796509 ,0.00775146 ,0.00802612 , 0.00775146 ,0.00775146 ,0.00741577 ,0.00738525 ,0.00762939 , 0.00735474 ,0.00787354 ,0.00799561 ,0.00820923 ,0.00836182 , 0.00823975 ,0.00811768 ,0.00793457 ,0.00787354 ,0.00781250 , 0.00811768 ,0.00830078 ,0.00848389 ,0.00857544 ,0.00845337 , 0.00836182 ,0.00827026 ,0.00811768 ,0.00759888 ,0.00796509 , 0.00781250 ,0.00778198 ,0.00799561 ,0.00781250 ,0.00769043 , 0.00784302 ,0.00796509 ,0.00836182 ,0.00814819 ,0.00814819 , 0.00811768 ,0.00814819 ,0.00775146 ,0.00775146 ,0.00772095 , 0.00799561 ,0.00808716 ,0.00833130 ,0.00851440 ,0.00878906 , 0.00866699 ,0.00848389 ,0.00848389 ,0.00823975 ,0.00808716 , 0.00799561 ,0.00796509 ,0.00796509 ,0.00817871 ,0.00802612 , 0.00790405 ,0.00759888 ,0.00793457 ,0.00793457 ,0.00836182 , 0.00827026 ,0.00857544 ,0.00878906 ,0.00866699 ,0.00857544 , 0.00823975 ,0.00830078 ,0.00811768 ,0.00830078 ,0.00833130 , 0.00817871 ,0.00796509 ,0.00796509 ,0.00762939 ,0.00775146 , 0.00750732 ,0.00769043 ,0.00802612 ,0.00817871 ,0.00845337 , 0.00827026 ,0.00805664 ,0.00781250 ,0.00750732 ,0.00750732 , 0.00735474 ,0.00756836 ,0.00765991 ,0.00765991 ,0.00817871 , 0.00781250 ,0.00802612 ,0.00796509 ,0.00820923 ,0.00814819 , 0.00805664 ,0.00808716 ,0.00759888 ,0.00781250 ,0.00759888 , 0.00741577 ,0.00775146 ,0.00753784 ,0.00756836 ,0.00750732 , 0.00759888 ,0.00759888 ,0.00759888 ,0.00784302 ,0.00756836 , 0.00787354 ,0.00759888 ,0.00823975 ,0.00799561 ,0.00833130 , 0.00817871 ,0.00799561 ,0.00820923 ,0.00796509 ,0.00781250 , 0.00756836 ,0.00750732 ,0.00744629 ,0.00738525 ,0.00781250 , 0.00744629 ,0.00765991 ,0.00753784 ,0.00750732 ,0.00729370 , 0.00701904 ,0.00704956 ,0.00683594 ,0.00698853 ,0.00692749 , 0.00701904 ,0.00732422 ,0.00765991 ,0.00778198 ,0.00772095 , 0.00772095 ,0.00784302 ,0.00759888 ,0.00753784 ,0.00750732 , 0.00744629 ,0.00756836 ,0.00759888 ,0.00753784 ,0.00762939 , 0.00759888 ,0.00726318 ,0.00759888 ,0.00714111 ,0.00756836 , 0.00704956 ,0.00720215 ,0.00750732 ,0.00698853 ,0.00717163 , 0.00692749 ,0.00714111 ,0.00686646 ,0.00674438 ,0.00656128 , 0.00680542 ,0.00674438 ,0.00698853 ,0.00717163 ,0.00695801 , 0.00723267 ,0.00717163 ,0.00704956 ,0.00689697 ,0.00683594 , 0.00680542 ,0.00668335 ,0.00650024 ,0.00662231 ,0.00659180 , 0.00674438 ,0.00643921 ,0.00646973 ,0.00622559 ,0.00622559 , 0.00576782 ,0.00634766 ,0.00619507 ,0.00677490 ,0.00643921 , 0.00677490 ,0.00646973 ,0.00643921 ,0.00619507 ,0.00570679 , 0.00588989 ,0.00567627 ,0.00567627 ,0.00582886 ,0.00552368 , 0.00555420 ,0.00543213 ,0.00552368 ,0.00537109 ,0.00564575 , 0.00570679 ,0.00555420 ,0.00564575 ,0.00527954 ,0.00524902 , 0.00527954 ,0.00515747 ,0.00512695 ,0.00518799 ,0.00521851 , 0.00521851 ,0.00515747 ,0.00534058 ,0.00546265 ,0.00518799 , 0.00537109 ,0.00531006 ,0.00515747 ,0.00488281 ,0.00488281 , 0.00491333 ,0.00512695 ,0.00494385 ,0.00500488 ,0.00509644 , 0.00466919 ,0.00476074 ,0.00421143 ,0.00430298 ,0.00408936 , 0.00439453 ,0.00457764 ,0.00466919 ,0.00485229 ,0.00479126 , 0.00485229 ,0.00448608 ,0.00476074 ,0.00433350 ,0.00433350 , 0.00424194 ,0.00442505 ,0.00408936 ,0.00378418 ,0.00350952 , 0.00347900 ,0.00360107 ,0.00350952 ,0.00375366 ,0.00347900 , 0.00378418 ,0.00363159 ,0.00363159 ,0.00332642 ,0.00350952 , 0.00341797 ,0.00350952 ,0.00335693 ,0.00326538 ,0.00296021 , 0.00296021 ,0.00280762 ,0.00271606 ,0.00299072 ,0.00299072 , 0.00323486 ,0.00323486 ,0.00329590 ,0.00280762 ,0.00286865 , 0.00262451 ,0.00268555 ,0.00292969 ,0.00265503 ,0.00289917 , 0.00256348 ,0.00265503 ,0.00277710 ,0.00241089 ,0.00210571 , 0.00207520 ,0.00195312 ,0.00204468 ,0.00201416 ,0.00210571 , 0.00186157 ,0.00195312 ,0.00167847 ,0.00131226 ,0.00152588 , 0.00161743 ,0.00177002 ,0.00173950 ,0.00177002 ,0.00137329 , 0.00137329 ,0.00109863 ,0.00125122 ,0.00100708 ,0.00128174 , 0.00115967 ,0.00097656 ,0.00091553 ,0.00079346 ,0.00048828 , 0.00061035 ,0.00061035 ,0.00061035 ,0.00073242 ,0.00091553 , 0.00097656 ,0.00076294 ,0.00082397 ,0.00076294 ,0.00082397 , 0.00085449 ,0.00079346 ,0.00079346 ,0.00073242 ,0.00073242 , 0.00070190 ,0.00045776 ,0.00036621 ,0.00036621 ,0.00012207 , 0.00033569 ,0.00000000 ,0.00018311 ,-0.00003052 ,0.00012207 , 0.00021362 ,0.00000000 ,-0.00015259 ,0.00009155 ,-0.00027466 , -0.00024414 ,-0.00039673 ,-0.00021362 ,-0.00045776 ,-0.00024414 , -0.00039673 ,-0.00033569 ,-0.00051880 ,-0.00045776 ,-0.00057983 , -0.00067139 ,-0.00051880 ,-0.00036621 ,-0.00082397 ,-0.00054932 , -0.00094604 ,-0.00091553 ,-0.00094604 ,-0.00103760 ,-0.00088501 , -0.00091553 ,-0.00088501 ,-0.00112915 ,-0.00128174 ,-0.00122070 , -0.00131226 ,-0.00164795 ,-0.00152588 ,-0.00152588 ,-0.00125122 , -0.00088501 ,-0.00070190 ,-0.00067139 ,-0.00070190 ,-0.00100708 , -0.00131226 ,-0.00134277 ,-0.00155640 ,-0.00143433 ,-0.00137329 , -0.00131226 ,-0.00112915 ,-0.00125122 ,-0.00112915 ,-0.00112915 , -0.00155640 ,-0.00164795 ,-0.00164795 ,-0.00198364 ,-0.00177002 , -0.00198364 ,-0.00204468 ,-0.00198364 ,-0.00201416 ,-0.00177002 , -0.00189209 ,-0.00164795 ,-0.00195312 ,-0.00189209 ,-0.00170898 , -0.00201416 ,-0.00210571 ,-0.00198364 ,-0.00180054 ,-0.00183105 , -0.00146484 ,-0.00201416 ,-0.00183105 ,-0.00201416 ,-0.00207520 , -0.00241089 ,-0.00234985 ,-0.00250244 ,-0.00277710 ,-0.00259399 , -0.00283813 ,-0.00283813 ,-0.00268555 ,-0.00244141 ,-0.00238037 , -0.00247192 ,-0.00244141 ,-0.00247192 ,-0.00256348 ,-0.00222778 , -0.00265503 ,-0.00283813 ,-0.00289917 ,-0.00292969 ,-0.00311279 , -0.00311279 ,-0.00305176 ,-0.00280762 ,-0.00283813 ,-0.00280762 , -0.00305176 ,-0.00280762 ,-0.00332642 ,-0.00357056 ,-0.00323486 , -0.00314331 ,-0.00329590 ,-0.00302124 ,-0.00302124 ,-0.00262451 , -0.00296021 ,-0.00280762 ,-0.00311279 ,-0.00323486 ,-0.00350952 , -0.00335693 ,-0.00350952 ,-0.00350952 ,-0.00323486 ,-0.00347900 , -0.00271606 ,-0.00299072 ,-0.00292969 ,-0.00323486 ,-0.00326538 , -0.00360107 ,-0.00338745 ,-0.00363159 ,-0.00329590 ,-0.00350952 , -0.00320435 ,-0.00317383 ,-0.00344849 ,-0.00357056 ,-0.00338745 , -0.00341797 ,-0.00338745 ,-0.00323486 ,-0.00320435 ,-0.00317383 , -0.00332642 ,-0.00335693 ,-0.00323486 ,-0.00326538 ,-0.00341797 , -0.00317383 ,-0.00344849 ,-0.00335693 ,-0.00372314 ,-0.00338745 , -0.00381470 ,-0.00347900 ,-0.00350952 ,-0.00335693 ,-0.00341797 , -0.00326538 ,-0.00363159 ,-0.00338745 ,-0.00375366 ,-0.00384521 , -0.00415039 ,-0.00405884 ,-0.00415039 ,-0.00408936 ,-0.00393677 , -0.00402832 ,-0.00384521 ,-0.00418091 ,-0.00390625 ,-0.00421143 , -0.00411987 ,-0.00408936 ,-0.00415039 ,-0.00369263 ,-0.00402832 , -0.00424194 ,-0.00445557 ,-0.00473022 ,-0.00457764 ,-0.00451660 , -0.00445557 ,-0.00418091 ,-0.00396729 ,-0.00408936 ,-0.00411987 , -0.00424194 ,-0.00445557 ,-0.00479126 ,-0.00469971 ,-0.00457764 , -0.00436401 ,-0.00415039 ,-0.00411987 ,-0.00408936 ,-0.00451660 , -0.00451660 ,-0.00469971 ,-0.00469971 ,-0.00488281 ,-0.00485229 , -0.00503540 ,-0.00479126 ,-0.00491333 ,-0.00485229 ,-0.00479126 , -0.00463867 ,-0.00482178 ,-0.00479126 ,-0.00485229 ,-0.00491333 , -0.00524902 ,-0.00531006 ,-0.00531006 ,-0.00515747 ,-0.00509644 , -0.00491333 ,-0.00488281 ,-0.00460815 ,-0.00494385 ,-0.00494385 , -0.00488281 ,-0.00521851 ,-0.00521851 ,-0.00546265 ,-0.00543213 , -0.00540161 ,-0.00540161 ,-0.00549316 ,-0.00561523 ,-0.00527954 , -0.00518799 ,-0.00524902 ,-0.00524902 ,-0.00564575 ,-0.00564575 , -0.00561523 ,-0.00552368 ,-0.00543213 ,-0.00549316 ,-0.00512695 , -0.00552368 ,-0.00579834 ,-0.00576782 ,-0.00576782 ,-0.00570679 , -0.00534058 ,-0.00524902 ,-0.00509644 ,-0.00509644 ,-0.00521851 , -0.00534058 ,-0.00558472 ,-0.00540161 ,-0.00543213 ,-0.00527954 , -0.00527954 ,-0.00534058 ,-0.00537109 ,-0.00555420 ,-0.00573730 , -0.00585938 ,-0.00604248 ,-0.00582886 ,-0.00555420 ,-0.00549316 , -0.00540161 ,-0.00543213 ,-0.00564575 ,-0.00564575 ,-0.00582886 , -0.00601196 ,-0.00598145 ,-0.00613403 ,-0.00604248 ,-0.00595093 , -0.00579834 ,-0.00576782 ,-0.00595093 ,-0.00576782 ,-0.00592041 , -0.00595093 ,-0.00598145 ,-0.00588989 ,-0.00610352 ,-0.00607300 , -0.00616455 ,-0.00588989 ,-0.00573730 ,-0.00558472 ,-0.00561523 , -0.00564575 ,-0.00540161 ,-0.00585938 ,-0.00601196 ,-0.00619507 , -0.00619507 ,-0.00646973 ,-0.00598145 ,-0.00613403 ,-0.00592041 , -0.00576782 ,-0.00582886 ,-0.00604248 ,-0.00643921 ,-0.00622559 , -0.00640869 ,-0.00637817 ,-0.00604248 ,-0.00619507 ,-0.00628662 , -0.00646973 ,-0.00650024 ,-0.00686646 ,-0.00646973 ,-0.00659180 , -0.00625610 ,-0.00637817 ,-0.00637817 ,-0.00613403 ,-0.00631714 , -0.00646973 ,-0.00634766 ,-0.00631714 ,-0.00625610 ,-0.00619507 , -0.00631714 ,-0.00656128 ,-0.00674438 ,-0.00674438 ,-0.00695801 , -0.00711060 ,-0.00680542 ,-0.00668335 ,-0.00674438 ,-0.00668335 , -0.00662231 ,-0.00671387 ,-0.00659180 ,-0.00677490 ,-0.00683594 , -0.00671387 ,-0.00683594 ,-0.00701904 ,-0.00692749 ,-0.00683594 , -0.00671387 ,-0.00668335 ,-0.00659180 ,-0.00646973 ,-0.00668335 , -0.00677490 ,-0.00717163 ,-0.00726318 ,-0.00738525 ,-0.00704956 , -0.00750732 ,-0.00723267 ,-0.00735474 ,-0.00723267 ,-0.00723267 , -0.00720215 ,-0.00717163 ,-0.00732422 ,-0.00744629 ,-0.00744629 , -0.00750732 ,-0.00744629 ,-0.00732422 ,-0.00723267 ,-0.00723267 , -0.00717163 ,-0.00680542 ,-0.00720215 ,-0.00683594 ,-0.00704956 , -0.00695801 ,-0.00704956 ,-0.00708008 ,-0.00701904 ,-0.00726318 , -0.00711060 ,-0.00723267 ,-0.00686646 ,-0.00686646 ,-0.00683594 , -0.00680542 ,-0.00665283 ,-0.00689697 ,-0.00704956 ,-0.00735474 , -0.00735474 ,-0.00732422 ,-0.00759888 ,-0.00765991 ,-0.00726318 , -0.00726318 ,-0.00726318 ,-0.00723267 ,-0.00726318 ,-0.00726318 , -0.00720215 ,-0.00704956 ,-0.00689697 ,-0.00692749 ,-0.00704956 , -0.00714111 ,-0.00717163 ,-0.00726318 ,-0.00717163 ,-0.00723267 , -0.00695801 ,-0.00732422 ,-0.00711060 ,-0.00753784 ,-0.00756836 , -0.00741577 ,-0.00750732 ,-0.00738525 ,-0.00756836 ,-0.00735474 , -0.00738525 ,-0.00741577 ,-0.00753784 ,-0.00732422 ,-0.00784302 , -0.00784302 ,-0.00793457 ,-0.00765991 ,-0.00750732 ,-0.00726318 , -0.00704956 ,-0.00692749 ,-0.00686646 ,-0.00680542 ,-0.00695801 , -0.00717163 ,-0.00711060 ,-0.00708008 ,-0.00723267 ,-0.00729370 , -0.00698853 ,-0.00689697 ,-0.00665283 ,-0.00674438 ,-0.00662231 , -0.00683594 ,-0.00698853 ,-0.00686646 ,-0.00701904 ,-0.00698853 , -0.00683594 ,-0.00680542 ,-0.00677490 ,-0.00698853 ,-0.00671387 , -0.00723267 ,-0.00680542 ,-0.00689697 ,-0.00674438 ,-0.00698853 , -0.00653076 ,-0.00680542 ,-0.00674438 ,-0.00708008 ,-0.00698853 , -0.00686646 ,-0.00708008 ,-0.00668335 ,-0.00677490 ,-0.00680542 , -0.00689697 ,-0.00692749 ,-0.00708008 ,-0.00708008 ,-0.00726318 , -0.00698853 ,-0.00680542 ,-0.00659180 ,-0.00640869 ,-0.00653076 , -0.00604248 ,-0.00640869 ,-0.00628662 ,-0.00631714 ,-0.00604248 , -0.00637817 ,-0.00650024 ,-0.00656128 ,-0.00640869 ,-0.00650024 , -0.00625610 ,-0.00634766 ,-0.00656128 ,-0.00607300 ,-0.00646973 , -0.00598145 ,-0.00607300 ,-0.00622559 ,-0.00588989 ,-0.00610352 , -0.00598145 ,-0.00576782 ,-0.00604248 ,-0.00570679 ,-0.00588989 , -0.00573730 ,-0.00588989 ,-0.00579834 ,-0.00610352 ,-0.00579834 , -0.00564575 ,-0.00573730 ,-0.00531006 ,-0.00552368 ,-0.00552368 , -0.00543213 ,-0.00588989 ,-0.00610352 ,-0.00588989 ,-0.00579834 , -0.00598145 ,-0.00576782 ,-0.00573730 ,-0.00564575 ,-0.00531006 , -0.00579834 ,-0.00549316 ,-0.00564575 ,-0.00534058 ,-0.00521851 , -0.00509644 ,-0.00515747 ,-0.00537109 ,-0.00521851 ,-0.00515747 , -0.00527954 ,-0.00524902 ,-0.00537109 ,-0.00500488 ,-0.00515747 , -0.00515747 ,-0.00503540 ,-0.00473022 ,-0.00500488 ,-0.00494385 , -0.00479126 ,-0.00518799 ,-0.00515747 ,-0.00518799 ,-0.00509644 , }; static const unsigned defaultKlick2Length = sizeof(defaultKlick2) / sizeof(*defaultKlick2); #endif muse-4.0.0/src/muse/klick3.h000066400000000000000000005463571404105311000155660ustar00rootroot00000000000000#ifndef __KLICK_3_H__ #define __KLICK_3_H__ static const float defaultKlick3[] = { -0.00576782 ,-0.00253296 ,0.00070190 ,0.00479126 ,0.03662109 , 0.12130737 ,0.18511963 ,0.14517212 ,0.05474854 ,-0.01477051 , -0.03149414 ,0.00616455 ,-0.01736450 ,-0.13937378 ,-0.28067017 , -0.32983398 ,-0.16326904 ,0.17321777 ,0.39169312 ,0.26425171 , -0.17303467 ,-0.58129883 ,-0.62698364 ,-0.35766602 ,-0.01974487 , 0.23742676 ,0.29763794 ,0.21353149 ,0.06167603 ,-0.10430908 , -0.13775635 ,0.02258301 ,0.32723999 ,0.62341309 ,0.73046875 , 0.51336670 ,-0.03289795 ,-0.57104492 ,-0.64666748 ,-0.23629761 , 0.26895142 ,0.50543213 ,0.40316772 ,0.11804199 ,-0.11508179 , -0.12762451 ,0.04702759 ,0.20465088 ,0.20910645 ,0.14929199 , 0.14620972 ,0.16366577 ,0.07189941 ,-0.18432617 ,-0.47741699 , -0.59082031 ,-0.44006348 ,-0.12905884 ,0.09539795 ,0.07043457 , -0.17565918 ,-0.47735596 ,-0.61236572 ,-0.50805664 ,-0.28613281 , -0.06024170 ,0.14007568 ,0.24316406 ,0.13763428 ,-0.21520996 , -0.67718506 ,-0.99517822 ,-0.97607422 ,-0.57101440 ,0.08593750 , 0.70269775 ,0.99505615 ,0.87454224 ,0.57327271 ,0.42352295 , 0.47882080 ,0.55068970 ,0.48092651 ,0.29006958 ,0.16162109 , 0.19647217 ,0.23846436 ,0.13574219 ,-0.01123047 ,-0.05157471 , 0.02056885 ,0.07241821 ,-0.00192261 ,-0.09625244 ,-0.01806641 , 0.24786377 ,0.51577759 ,0.57196045 ,0.37432861 ,0.08529663 , -0.15914917 ,-0.35537720 ,-0.54257202 ,-0.71960449 ,-0.85760498 , -0.92013550 ,-0.84704590 ,-0.65600586 ,-0.44549561 ,-0.30752563 , -0.23324585 ,-0.18533325 ,-0.11166382 ,0.01202393 ,0.12670898 , 0.17221069 ,0.15213013 ,0.08102417 ,0.02981567 ,0.08609009 , 0.19680786 ,0.22619629 ,0.16271973 ,0.09548950 ,0.01770020 , -0.08886719 ,-0.13079834 ,-0.07650757 ,0.02627563 ,0.17105103 , 0.37115479 ,0.57495117 ,0.69400024 ,0.64129639 ,0.43179321 , 0.16064453 ,-0.09378052 ,-0.29943848 ,-0.42608643 ,-0.43521118 , -0.38681030 ,-0.43591309 ,-0.54556274 ,-0.51354980 ,-0.31240845 , -0.02380371 ,0.26147461 ,0.40591431 ,0.34704590 ,0.15728760 , -0.11856079 ,-0.37493896 ,-0.44595337 ,-0.30764771 ,-0.06115723 , 0.20733643 ,0.44467163 ,0.56256104 ,0.48736572 ,0.29159546 , 0.11981201 ,0.04812622 ,0.05624390 ,0.08215332 ,0.07534790 , 0.02502441 ,-0.10766602 ,-0.26843262 ,-0.25015259 ,-0.00805664 , 0.24926758 ,0.33322144 ,0.24188232 ,0.07379150 ,-0.09283447 , -0.18170166 ,-0.15890503 ,-0.06283569 ,0.03750610 ,0.08615112 , 0.06515503 ,0.03213501 ,0.03881836 ,0.05334473 ,0.04348755 , 0.05871582 ,0.13250732 ,0.24343872 ,0.28869629 ,0.18377686 , -0.00665283 ,-0.13619995 ,-0.11935425 ,0.01852417 ,0.18325806 , 0.30517578 ,0.35809326 ,0.33306885 ,0.26617432 ,0.17007446 , 0.06301880 ,-0.01046753 ,-0.04766846 ,-0.07199097 ,-0.10333252 , -0.16024780 ,-0.23983765 ,-0.30484009 ,-0.32217407 ,-0.28466797 , -0.23577881 ,-0.22607422 ,-0.26379395 ,-0.30352783 ,-0.27282715 , -0.15731812 ,-0.00628662 ,0.11709595 ,0.14160156 ,0.06881714 , -0.02188110 ,-0.07604980 ,-0.07266235 ,-0.03271484 ,0.01113892 , 0.02087402 ,-0.00051880 ,-0.01623535 ,0.02474976 ,0.12478638 , 0.23129272 ,0.27383423 ,0.23123169 ,0.15298462 ,0.07318115 , -0.00390625 ,-0.06759644 ,-0.12347412 ,-0.20639038 ,-0.30148315 , -0.34100342 ,-0.28720093 ,-0.16580200 ,-0.03894043 ,0.06027222 , 0.12936401 ,0.17507935 ,0.20568848 ,0.20584106 ,0.14218140 , 0.01351929 ,-0.12966919 ,-0.18283081 ,-0.11938477 ,-0.00558472 , 0.09732056 ,0.16079712 ,0.18719482 ,0.20495605 ,0.21997070 , 0.19528198 ,0.13629150 ,0.07952881 ,0.04174805 ,0.04492188 , 0.08557129 ,0.11685181 ,0.10494995 ,0.07147217 ,0.05432129 , 0.05041504 ,0.01989746 ,-0.06967163 ,-0.19790649 ,-0.30645752 , -0.34893799 ,-0.33462524 ,-0.30908203 ,-0.29193115 ,-0.27001953 , -0.24432373 ,-0.20550537 ,-0.13986206 ,-0.06320190 ,-0.00759888 , 0.02401733 ,0.04000854 ,0.04144287 ,0.03610229 ,0.03335571 , 0.03854370 ,0.04782104 ,0.06008911 ,0.08105469 ,0.12890625 , 0.18634033 ,0.24005127 ,0.28524780 ,0.31460571 ,0.32482910 , 0.31747437 ,0.27197266 ,0.19766235 ,0.11465454 ,0.04281616 , -0.01574707 ,-0.05178833 ,-0.04602051 ,0.02066040 ,0.11871338 , 0.19476318 ,0.22161865 ,0.20315552 ,0.14776611 ,0.06884766 , -0.01766968 ,-0.07995605 ,-0.09851074 ,-0.08807373 ,-0.08218384 , -0.09973145 ,-0.14065552 ,-0.18466187 ,-0.21090698 ,-0.20834351 , -0.18347168 ,-0.14544678 ,-0.12280273 ,-0.12750244 ,-0.14181519 , -0.12884521 ,-0.07559204 ,-0.00909424 ,0.04067993 ,0.06567383 , 0.06646729 ,0.04306030 ,0.00952148 ,-0.02816772 ,-0.07205200 , -0.11380005 ,-0.14294434 ,-0.14288330 ,-0.10424805 ,-0.05004883 , 0.00054932 ,0.04287720 ,0.07821655 ,0.10723877 ,0.13180542 , 0.15209961 ,0.15823364 ,0.15005493 ,0.13400269 ,0.11129761 , 0.07678223 ,0.02847290 ,-0.02249146 ,-0.05514526 ,-0.06149292 , -0.05212402 ,-0.04928589 ,-0.07479858 ,-0.12371826 ,-0.15185547 , -0.12396240 ,-0.03753662 ,0.07223511 ,0.16482544 ,0.20416260 , 0.17382812 ,0.09735107 ,0.01336670 ,-0.04696655 ,-0.07760620 , -0.08779907 ,-0.07955933 ,-0.05584717 ,-0.02029419 ,0.01907349 , 0.04949951 ,0.06311035 ,0.03970337 ,-0.01947021 ,-0.07962036 , -0.12164307 ,-0.15466309 ,-0.17617798 ,-0.17483521 ,-0.14532471 , -0.08590698 ,-0.00045776 ,0.08599854 ,0.14630127 ,0.15634155 , 0.12524414 ,0.08444214 ,0.04214478 ,-0.00210571 ,-0.01538086 , 0.03860474 ,0.15441895 ,0.28210449 ,0.37393188 ,0.40417480 , 0.34835815 ,0.22229004 ,0.09750366 ,0.02136230 ,-0.00555420 , -0.01724243 ,-0.05261230 ,-0.09594727 ,-0.10006714 ,-0.06887817 , -0.04299927 ,-0.05163574 ,-0.08697510 ,-0.13085938 ,-0.17266846 , -0.21408081 ,-0.23074341 ,-0.19668579 ,-0.12030029 ,-0.04699707 , -0.01724243 ,-0.05856323 ,-0.13562012 ,-0.16430664 ,-0.10824585 , -0.02337646 ,0.02395630 ,0.01498413 ,-0.02410889 ,-0.03747559 , 0.00027466 ,0.05657959 ,0.08724976 ,0.07003784 ,0.02389526 , 0.00167847 ,0.03222656 ,0.10342407 ,0.16354370 ,0.15722656 , 0.06558228 ,-0.04006958 ,-0.09082031 ,-0.07379150 ,0.00241089 , 0.11508179 ,0.18966675 ,0.16610718 ,0.04949951 ,-0.12158203 , -0.28262329 ,-0.33401489 ,-0.26165771 ,-0.15451050 ,-0.10021973 , -0.10861206 ,-0.15621948 ,-0.17037964 ,-0.10629272 ,0.01284790 , 0.10781860 ,0.12567139 ,0.06613159 ,-0.01446533 ,-0.06259155 , -0.05566406 ,0.00418091 ,0.09323120 ,0.17282104 ,0.21939087 , 0.22793579 ,0.18783569 ,0.12725830 ,0.07821655 ,0.06130981 , 0.06161499 ,0.05294800 ,0.02285767 ,0.00582886 ,0.03390503 , 0.10470581 ,0.16525269 ,0.16156006 ,0.08514404 ,-0.01620483 , -0.06781006 ,-0.03909302 ,0.01910400 ,0.02767944 ,-0.03890991 , -0.13278198 ,-0.15948486 ,-0.07333374 ,0.05584717 ,0.13433838 , 0.11624146 ,0.04037476 ,-0.01489258 ,-0.01522827 ,0.02355957 , 0.06256104 ,0.05685425 ,-0.00317383 ,-0.07992554 ,-0.14395142 , -0.18646240 ,-0.18945312 ,-0.15515137 ,-0.10848999 ,-0.06384277 , -0.02108765 ,0.00915527 ,0.02783203 ,0.03405762 ,0.01989746 , -0.01617432 ,-0.06835938 ,-0.12167358 ,-0.16430664 ,-0.17391968 , -0.15521240 ,-0.12698364 ,-0.09555054 ,-0.05731201 ,-0.02081299 , 0.00817871 ,0.02984619 ,0.04223633 ,0.03091431 ,-0.00207520 , -0.03494263 ,-0.05126953 ,-0.05285645 ,-0.04150391 ,-0.02239990 , 0.00323486 ,0.02804565 ,0.04302979 ,0.04235840 ,0.04940796 , 0.08334351 ,0.13327026 ,0.16708374 ,0.17199707 ,0.15332031 , 0.12145996 ,0.08874512 ,0.05850220 ,0.03311157 ,0.01660156 , -0.01205444 ,-0.07546997 ,-0.16406250 ,-0.22573853 ,-0.21881104 , -0.15753174 ,-0.09478760 ,-0.08340454 ,-0.12551880 ,-0.18688965 , -0.23767090 ,-0.26351929 ,-0.23916626 ,-0.16415405 ,-0.06707764 , 0.00973511 ,0.03167725 ,0.00170898 ,-0.04254150 ,-0.05047607 , -0.02230835 ,-0.00103760 ,-0.00482178 ,-0.00762939 ,0.01257324 , 0.04306030 ,0.06939697 ,0.07531738 ,0.05212402 ,0.01980591 , 0.00454712 ,0.01205444 ,0.02935791 ,0.02157593 ,-0.03607178 , -0.10424805 ,-0.15194702 ,-0.16513062 ,-0.14883423 ,-0.11694336 , -0.09207153 ,-0.08786011 ,-0.10174561 ,-0.10531616 ,-0.08880615 , -0.04721069 ,0.01800537 ,0.10305786 ,0.17630005 ,0.20550537 , 0.18881226 ,0.16323853 ,0.15103149 ,0.14614868 ,0.12683105 , 0.07720947 ,0.01538086 ,-0.01690674 ,0.00042725 ,0.05032349 , 0.10098267 ,0.13626099 ,0.17382812 ,0.21759033 ,0.23736572 , 0.20391846 ,0.13214111 ,0.06051636 ,0.00738525 ,-0.02746582 , -0.05596924 ,-0.09329224 ,-0.13327026 ,-0.14486694 ,-0.12612915 , -0.09710693 ,-0.08569336 ,-0.10379028 ,-0.14770508 ,-0.18731689 , -0.20101929 ,-0.19116211 ,-0.15777588 ,-0.11230469 ,-0.06582642 , -0.01635742 ,0.01235962 ,-0.00051880 ,-0.04864502 ,-0.09600830 , -0.10650635 ,-0.07870483 ,-0.04644775 ,-0.03350830 ,-0.02435303 , -0.00976562 ,0.01840210 ,0.06311035 ,0.12338257 ,0.17428589 , 0.18951416 ,0.16055298 ,0.09649658 ,0.03466797 ,0.00271606 , 0.00729370 ,0.04196167 ,0.08023071 ,0.08810425 ,0.07226562 , 0.06008911 ,0.05929565 ,0.05728149 ,0.05239868 ,0.05148315 , 0.05636597 ,0.05563354 ,0.03515625 ,0.01037598 ,-0.00765991 , -0.02795410 ,-0.06036377 ,-0.09542847 ,-0.11370850 ,-0.12670898 , -0.15435791 ,-0.18603516 ,-0.19946289 ,-0.18353271 ,-0.15795898 , -0.14602661 ,-0.12979126 ,-0.07827759 ,-0.00048828 ,0.05664062 , 0.05233765 ,-0.01138306 ,-0.10083008 ,-0.17196655 ,-0.18972778 , -0.15496826 ,-0.10464478 ,-0.06817627 ,-0.03030396 ,0.02502441 , 0.08749390 ,0.14733887 ,0.17901611 ,0.16836548 ,0.14096069 , 0.10531616 ,0.04171753 ,-0.01397705 ,-0.00808716 ,0.04049683 , 0.07925415 ,0.09127808 ,0.06936646 ,0.02395630 ,0.01034546 , 0.06484985 ,0.16555786 ,0.24002075 ,0.25561523 ,0.23056030 , 0.17285156 ,0.09088135 ,0.03240967 ,0.03329468 ,0.04602051 , 0.00915527 ,-0.07699585 ,-0.15554810 ,-0.18164062 ,-0.13943481 , -0.06521606 ,-0.01370239 ,-0.00717163 ,-0.01913452 ,-0.04223633 , -0.08908081 ,-0.14785767 ,-0.19894409 ,-0.23107910 ,-0.19207764 , -0.07485962 ,0.03579712 ,0.09408569 ,0.10348511 ,0.05917358 , -0.03320312 ,-0.11001587 ,-0.13168335 ,-0.13275146 ,-0.14248657 , -0.10882568 ,-0.00262451 ,0.11373901 ,0.15469360 ,0.12487793 , 0.06738281 ,0.01498413 ,0.00622559 ,0.02651978 ,0.04092407 , 0.05545044 ,0.08474731 ,0.09237671 ,0.05664062 ,0.01824951 , 0.01901245 ,0.04513550 ,0.08853149 ,0.13598633 ,0.13003540 , 0.06811523 ,0.03570557 ,0.06762695 ,0.12170410 ,0.13287354 , 0.08483887 ,0.02182007 ,-0.01925659 ,-0.03604126 ,-0.03164673 , -0.01058960 ,-0.00064087 ,-0.01422119 ,-0.02740479 ,-0.02096558 , 0.00857544 ,0.05133057 ,0.08230591 ,0.09002686 ,0.09527588 , 0.11380005 ,0.11505127 ,0.08184814 ,0.03079224 ,-0.03329468 , -0.10330200 ,-0.13546753 ,-0.11059570 ,-0.05725098 ,-0.02078247 , -0.00717163 ,0.00070190 ,0.01104736 ,0.01779175 ,0.01019287 , -0.02041626 ,-0.05285645 ,-0.05984497 ,-0.03237915 ,0.00875854 , 0.03189087 ,0.02264404 ,-0.00918579 ,-0.05474854 ,-0.06964111 , -0.02325439 ,0.04861450 ,0.11193848 ,0.17019653 ,0.22305298 , 0.25067139 ,0.24093628 ,0.18972778 ,0.11471558 ,0.04751587 , -0.00521851 ,-0.06384277 ,-0.11093140 ,-0.10241699 ,-0.04687500 , -0.00463867 ,0.00296021 ,-0.00872803 ,-0.04302979 ,-0.08294678 , -0.11178589 ,-0.14465332 ,-0.18347168 ,-0.21197510 ,-0.20010376 , -0.12783813 ,-0.00775146 ,0.11843872 ,0.19110107 ,0.17718506 , 0.10852051 ,0.04449463 ,0.00903320 ,-0.01129150 ,-0.02276611 , -0.02575684 ,-0.03668213 ,-0.04013062 ,-0.00244141 ,0.06118774 , 0.10699463 ,0.14233398 ,0.17303467 ,0.16094971 ,0.10324097 , 0.02893066 ,-0.05111694 ,-0.13717651 ,-0.20916748 ,-0.24639893 , -0.24316406 ,-0.18612671 ,-0.11026001 ,-0.06530762 ,-0.03744507 , 0.00982666 ,0.05166626 ,0.06570435 ,0.04049683 ,-0.02407837 , -0.09387207 ,-0.10217285 ,-0.03225708 ,0.05398560 ,0.08969116 , 0.06146240 ,0.01718140 ,0.00411987 ,0.02825928 ,0.04742432 , 0.02960205 ,-0.01190186 ,-0.05249023 ,-0.05136108 ,0.01507568 , 0.12564087 ,0.21206665 ,0.21261597 ,0.12188721 ,0.00491333 , -0.05093384 ,-0.03179932 ,-0.00842285 ,-0.03042603 ,-0.08197021 , -0.12530518 ,-0.15112305 ,-0.15484619 ,-0.12335205 ,-0.06362915 , -0.00057983 ,0.04162598 ,0.05310059 ,0.03869629 ,0.01431274 , -0.01309204 ,-0.02807617 ,-0.00430298 ,0.03622437 ,0.03872681 , 0.00167847 ,-0.01797485 ,-0.00558472 ,0.01696777 ,0.05032349 , 0.08023071 ,0.09609985 ,0.09893799 ,0.10180664 ,0.10635376 , 0.08279419 ,-0.01025391 ,-0.14572144 ,-0.22030640 ,-0.18002319 , -0.08181763 ,-0.00955200 ,-0.00402832 ,-0.06011963 ,-0.12881470 , -0.14202881 ,-0.09155273 ,-0.02886963 ,-0.00485229 ,-0.03707886 , -0.09323120 ,-0.15301514 ,-0.21051025 ,-0.23294067 ,-0.17745972 , -0.05453491 ,0.09182739 ,0.20501709 ,0.23016357 ,0.16201782 , 0.05850220 ,-0.02554321 ,-0.04074097 ,0.02331543 ,0.11975098 , 0.20190430 ,0.24200439 ,0.23770142 ,0.20254517 ,0.16241455 , 0.13250732 ,0.11679077 ,0.10287476 ,0.07598877 ,0.02648926 , -0.01626587 ,-0.02096558 ,0.00177002 ,0.03521729 ,0.05957031 , 0.05422974 ,0.01470947 ,-0.04214478 ,-0.07791138 ,-0.07824707 , -0.07067871 ,-0.10620117 ,-0.16986084 ,-0.20730591 ,-0.19039917 , -0.15036011 ,-0.11523438 ,-0.09689331 ,-0.08383179 ,-0.05404663 , -0.00366211 ,0.03817749 ,0.04598999 ,0.01406860 ,-0.05599976 , -0.13220215 ,-0.18078613 ,-0.17147827 ,-0.09945679 ,-0.01333618 , 0.06265259 ,0.12603760 ,0.17355347 ,0.18847656 ,0.16552734 , 0.10708618 ,0.03320312 ,-0.03057861 ,-0.06863403 ,-0.07168579 , -0.02261353 ,0.06463623 ,0.15139771 ,0.20233154 ,0.20071411 , 0.14730835 ,0.07135010 ,0.00836182 ,-0.02261353 ,-0.01016235 , 0.02810669 ,0.05551147 ,0.05169678 ,0.04095459 ,0.06027222 , 0.09301758 ,0.11352539 ,0.11456299 ,0.09533691 ,0.05816650 , 0.01757812 ,-0.01144409 ,-0.02154541 ,-0.02044678 ,-0.04049683 , -0.09292603 ,-0.15151978 ,-0.19500732 ,-0.19219971 ,-0.13549805 , -0.06710815 ,-0.03533936 ,-0.04220581 ,-0.05697632 ,-0.06826782 , -0.08389282 ,-0.11700439 ,-0.15103149 ,-0.17602539 ,-0.18133545 , -0.14874268 ,-0.07147217 ,0.03732300 ,0.13601685 ,0.17904663 , 0.16549683 ,0.13394165 ,0.12396240 ,0.14526367 ,0.18304443 , 0.21398926 ,0.20864868 ,0.16955566 ,0.12976074 ,0.11251831 , 0.10824585 ,0.09646606 ,0.07339478 ,0.05517578 ,0.05560303 , 0.06903076 ,0.08483887 ,0.09722900 ,0.10122681 ,0.08715820 , 0.04141235 ,-0.02413940 ,-0.06594849 ,-0.05889893 ,-0.01382446 , 0.04077148 ,0.06362915 ,0.02554321 ,-0.06243896 ,-0.14718628 , -0.17602539 ,-0.13720703 ,-0.08038330 ,-0.04956055 ,-0.06387329 , -0.11581421 ,-0.18249512 ,-0.21807861 ,-0.18524170 ,-0.10913086 , -0.05114746 ,-0.04647827 ,-0.07489014 ,-0.10559082 ,-0.11840820 , -0.11157227 ,-0.08505249 ,-0.03814697 ,0.01541138 ,0.05990601 , 0.07800293 ,0.06845093 ,0.05548096 ,0.06637573 ,0.10049438 , 0.13818359 ,0.15301514 ,0.12829590 ,0.06896973 ,0.00662231 , -0.02142334 ,-0.02755737 ,-0.03833008 ,-0.05050659 ,-0.04248047 , -0.00540161 ,0.06903076 ,0.15139771 ,0.20172119 ,0.19024658 , 0.11074829 ,0.00714111 ,-0.06240845 ,-0.07821655 ,-0.06130981 , -0.02468872 ,0.02658081 ,0.06759644 ,0.07376099 ,0.05270386 , 0.03674316 ,0.03741455 ,0.06692505 ,0.10668945 ,0.08248901 , -0.01177979 ,-0.12078857 ,-0.18179321 ,-0.15356445 ,-0.06594849 , -0.00567627 ,-0.00753784 ,-0.03747559 ,-0.04696655 ,-0.02029419 , 0.01947021 ,0.04049683 ,0.02072144 ,-0.04177856 ,-0.10284424 , -0.10787964 ,-0.05230713 ,0.01568604 ,0.04394531 ,0.02911377 , -0.00137329 ,-0.02932739 ,-0.05310059 ,-0.05953979 ,-0.05041504 , -0.04443359 ,-0.06414795 ,-0.10617065 ,-0.12249756 ,-0.07305908 , 0.01473999 ,0.08969116 ,0.13098145 ,0.13934326 ,0.11480713 , 0.07723999 ,0.07025146 ,0.10797119 ,0.14981079 ,0.14645386 , 0.09725952 ,0.04547119 ,0.01928711 ,0.02185059 ,0.03875732 , 0.04748535 ,0.02731323 ,-0.00729370 ,-0.03189087 ,-0.02075195 , 0.01284790 ,0.02673340 ,-0.00894165 ,-0.08203125 ,-0.15753174 , -0.20227051 ,-0.19592285 ,-0.13882446 ,-0.05352783 ,0.01602173 , 0.04321289 ,0.04241943 ,0.03344727 ,0.03115845 ,0.02490234 , 0.01412964 ,0.01089478 ,0.02218628 ,0.04766846 ,0.07485962 , 0.09597778 ,0.10470581 ,0.09494019 ,0.05429077 ,0.00646973 , -0.00173950 ,0.02835083 ,0.05419922 ,0.05679321 ,0.04199219 , 0.01727295 ,0.00042725 ,-0.00134277 ,0.00137329 ,0.00064087 , 0.00039673 ,0.00476074 ,0.00918579 ,0.00518799 ,-0.02523804 , -0.07192993 ,-0.10089111 ,-0.09707642 ,-0.07415771 ,-0.06201172 , -0.05725098 ,-0.05081177 ,-0.03994751 ,-0.02618408 ,-0.00769043 , 0.01251221 ,0.03454590 ,0.06634521 ,0.10720825 ,0.14569092 , 0.16320801 ,0.13653564 ,0.08206177 ,0.03579712 ,0.01528931 , 0.00003052 ,-0.03268433 ,-0.07791138 ,-0.10916138 ,-0.10906982 , -0.08407593 ,-0.04699707 ,-0.01882935 ,-0.01156616 ,-0.00662231 , 0.01083374 ,0.04452515 ,0.06088257 ,0.02951050 ,-0.02239990 , -0.02621460 ,0.02514648 ,0.07986450 ,0.09143066 ,0.05752563 , 0.00671387 ,-0.04248047 ,-0.06152344 ,-0.03509521 ,0.00308228 , 0.02355957 ,0.04022217 ,0.06423950 ,0.09011841 ,0.10745239 , 0.08319092 ,0.03430176 ,-0.00125122 ,-0.00921631 ,0.00698853 , 0.03344727 ,0.03915405 ,0.01467896 ,-0.04754639 ,-0.10955811 , -0.13552856 ,-0.11929321 ,-0.06973267 ,0.00421143 ,0.05899048 , 0.04846191 ,-0.02224731 ,-0.10101318 ,-0.13760376 ,-0.11749268 , -0.06625366 ,-0.03344727 ,-0.04269409 ,-0.08575439 ,-0.11962891 , -0.11688232 ,-0.06954956 ,0.00329590 ,0.05804443 ,0.06463623 , 0.03500366 ,0.00204468 ,-0.01812744 ,-0.01889038 ,0.00299072 , 0.03485107 ,0.05630493 ,0.06161499 ,0.04095459 ,0.00399780 , -0.02700806 ,-0.04324341 ,-0.04919434 ,-0.03665161 ,-0.02020264 , -0.01171875 ,-0.00137329 ,0.02584839 ,0.07009888 ,0.10430908 , 0.08215332 ,0.00625610 ,-0.07565308 ,-0.11566162 ,-0.10043335 , -0.05603027 ,-0.01809692 ,-0.00219727 ,-0.00234985 ,-0.00854492 , -0.01351929 ,-0.02722168 ,-0.05590820 ,-0.09017944 ,-0.10058594 , -0.06582642 ,-0.00439453 ,0.03857422 ,0.05242920 ,0.03240967 , -0.01760864 ,-0.08340454 ,-0.13269043 ,-0.13061523 ,-0.07785034 , 0.00115967 ,0.07427979 ,0.12609863 ,0.14782715 ,0.13928223 , 0.10070801 ,0.05560303 ,0.02728271 ,0.00415039 ,-0.02563477 , -0.06259155 ,-0.09197998 ,-0.10232544 ,-0.08334351 ,-0.04205322 , -0.00189209 ,0.01196289 ,0.00292969 ,-0.00299072 ,-0.00271606 , 0.01812744 ,0.04736328 ,0.05056763 ,0.03616333 ,0.02496338 , 0.02142334 ,0.00451660 ,-0.03646851 ,-0.08676147 ,-0.10638428 , -0.10183716 ,-0.10656738 ,-0.11544800 ,-0.10552979 ,-0.07940674 , -0.05017090 ,-0.02529907 ,-0.01617432 ,-0.02145386 ,-0.02151489 , -0.00616455 ,0.01290894 ,0.03564453 ,0.04028320 ,0.02178955 , 0.00231934 ,-0.00192261 ,0.00650024 ,0.03445435 ,0.07617188 , 0.11248779 ,0.11761475 ,0.08520508 ,0.03460693 ,-0.01150513 , -0.04754639 ,-0.05911255 ,-0.04446411 ,-0.00915527 ,0.01849365 , 0.00558472 ,-0.03356934 ,-0.05661011 ,-0.04827881 ,-0.02111816 , 0.00534058 ,0.03268433 ,0.05584717 ,0.06842041 ,0.07180786 , 0.07556152 ,0.07687378 ,0.08108521 ,0.08642578 ,0.07070923 , 0.03515625 ,-0.00921631 ,-0.04840088 ,-0.06445312 ,-0.04818726 , -0.00344849 ,0.04791260 ,0.07546997 ,0.06033325 ,0.02035522 , -0.01144409 ,-0.02212524 ,-0.03216553 ,-0.04956055 ,-0.07064819 , -0.07858276 ,-0.05856323 ,-0.01113892 ,0.03936768 ,0.07302856 , 0.07589722 ,0.05572510 ,0.02954102 ,0.00466919 ,-0.01806641 , -0.05654907 ,-0.10876465 ,-0.13455200 ,-0.10293579 ,-0.03744507 , 0.02786255 ,0.07391357 ,0.08581543 ,0.06610107 ,0.01669312 , -0.05517578 ,-0.12518311 ,-0.15744019 ,-0.12881470 ,-0.05313110 , 0.03182983 ,0.08609009 ,0.07754517 ,0.01217651 ,-0.06292725 , -0.08419800 ,-0.03829956 ,0.02264404 ,0.05578613 ,0.06326294 , 0.05407715 ,0.03009033 ,0.00332642 ,-0.00671387 ,0.01397705 , 0.05184937 ,0.06106567 ,0.01699829 ,-0.05545044 ,-0.11669922 , -0.13003540 ,-0.07919312 ,0.01171875 ,0.08776855 ,0.10494995 , 0.05484009 ,-0.02691650 ,-0.10226440 ,-0.15072632 ,-0.16299438 , -0.13452148 ,-0.07464600 ,-0.01913452 ,0.01010132 ,0.02005005 , 0.02844238 ,0.03814697 ,0.05148315 ,0.06179810 ,0.07104492 , 0.09057617 ,0.09542847 ,0.05603027 ,-0.00537109 ,-0.03884888 , -0.02236938 ,0.01333618 ,0.03448486 ,0.01980591 ,-0.02108765 , -0.07369995 ,-0.10440063 ,-0.10110474 ,-0.07095337 ,-0.02914429 , 0.01519775 ,0.04962158 ,0.05926514 ,0.03039551 ,-0.00335693 , -0.00747681 ,0.00985718 ,0.02606201 ,0.02096558 ,-0.00125122 , -0.01849365 ,-0.01458740 ,-0.00103760 ,0.01373291 ,0.02691650 , 0.02444458 ,-0.00231934 ,-0.03421021 ,-0.04397583 ,-0.02532959 , 0.00805664 ,0.02624512 ,0.02352905 ,0.01840210 ,0.02987671 , 0.04876709 ,0.04000854 ,-0.00390625 ,-0.06451416 ,-0.10232544 , -0.10238647 ,-0.07290649 ,-0.04193115 ,-0.01446533 ,0.01626587 , 0.04620361 ,0.05523682 ,0.04028320 ,0.00582886 ,-0.01965332 , -0.01284790 ,0.01446533 ,0.03579712 ,0.02932739 ,-0.00338745 , -0.03820801 ,-0.04946899 ,-0.02374268 ,0.02029419 ,0.04541016 , 0.03973389 ,0.01342773 ,-0.01010132 ,-0.00265503 ,0.02398682 , 0.04980469 ,0.05761719 ,0.04174805 ,0.00616455 ,-0.02499390 , -0.03305054 ,-0.01788330 ,-0.00079346 ,0.00241089 ,-0.00564575 , -0.01690674 ,-0.02612305 ,-0.02813721 ,-0.01757812 ,0.00015259 , 0.00781250 ,0.00292969 ,0.00222778 ,0.00820923 ,0.00576782 , -0.00402832 ,-0.00717163 ,-0.00619507 ,-0.02297974 ,-0.04620361 , -0.04757690 ,-0.01342773 ,0.02804565 ,0.05545044 ,0.06106567 , 0.05154419 ,0.03112793 ,0.00506592 ,-0.01089478 ,-0.00418091 , 0.00961304 ,0.00524902 ,-0.02947998 ,-0.07830811 ,-0.11108398 , -0.10696411 ,-0.06756592 ,-0.01306152 ,0.03353882 ,0.05773926 , 0.06484985 ,0.06243896 ,0.06442261 ,0.06765747 ,0.06152344 , 0.03164673 ,-0.01458740 ,-0.05566406 ,-0.07763672 ,-0.08187866 , -0.06365967 ,-0.03131104 ,0.00195312 ,0.02774048 ,0.03945923 , 0.03170776 ,0.00891113 ,-0.01959229 ,-0.02981567 ,-0.01095581 , 0.02459717 ,0.05343628 ,0.05639648 ,0.02911377 ,0.00418091 , -0.00265503 ,-0.01306152 ,-0.04891968 ,-0.09536743 ,-0.10888672 , -0.07241821 ,-0.01757812 ,0.02148438 ,0.02416992 ,-0.00616455 , -0.04095459 ,-0.05035400 ,-0.03506470 ,-0.01147461 ,0.00436401 , 0.00335693 ,-0.01400757 ,-0.03857422 ,-0.05960083 ,-0.07803345 , -0.08905029 ,-0.07876587 ,-0.04400635 ,-0.00512695 ,0.01919556 , 0.02459717 ,0.02377319 ,0.02429199 ,0.02627563 ,0.01296997 , -0.01214600 ,-0.02917480 ,-0.02941895 ,-0.02673340 ,-0.03442383 , -0.04382324 ,-0.03869629 ,-0.01693726 ,0.00903320 ,0.02230835 , 0.02349854 ,0.02148438 ,0.01034546 ,-0.00747681 ,-0.01007080 , 0.01782227 ,0.06240845 ,0.10101318 ,0.11401367 ,0.09271240 , 0.04769897 ,0.00698853 ,-0.00436401 ,0.00875854 ,0.02682495 , 0.03771973 ,0.04217529 ,0.05615234 ,0.06176758 ,0.03448486 , -0.00604248 ,-0.01986694 ,0.00750732 ,0.05953979 ,0.08822632 , 0.07077026 ,0.02648926 ,-0.01541138 ,-0.04714966 ,-0.07122803 , -0.07321167 ,-0.05288696 ,-0.02841187 ,-0.00506592 ,0.01132202 , 0.00885010 ,-0.00991821 ,-0.03741455 ,-0.06033325 ,-0.07092285 , -0.06460571 ,-0.04916382 ,-0.03442383 ,-0.02661133 ,-0.03430176 , -0.04486084 ,-0.04727173 ,-0.04257202 ,-0.02993774 ,-0.01071167 , 0.00527954 ,0.01547241 ,0.01104736 ,0.00286865 ,-0.00009155 , 0.00985718 ,0.03216553 ,0.05490112 ,0.06723022 ,0.06423950 , 0.05398560 ,0.05477905 ,0.05249023 ,0.03094482 ,-0.00195312 , -0.02236938 ,-0.02301025 ,-0.00946045 ,0.01080322 ,0.02108765 , 0.01632690 ,0.00152588 ,-0.00854492 ,-0.00222778 ,0.02362061 , 0.06213379 ,0.08938599 ,0.09130859 ,0.05917358 ,0.00436401 , -0.04232788 ,-0.05392456 ,-0.03021240 ,0.01736450 ,0.05780029 , 0.07910156 ,0.08303833 ,0.06665039 ,0.03680420 ,0.00421143 , -0.01666260 ,-0.02136230 ,-0.02218628 ,-0.03146362 ,-0.04751587 , -0.05947876 ,-0.05712891 ,-0.04202271 ,-0.02908325 ,-0.03170776 , -0.04074097 ,-0.03564453 ,-0.01324463 ,0.00973511 ,0.01931763 , 0.01544189 ,0.01239014 ,0.03115845 ,0.05181885 ,0.04815674 , 0.02597046 ,0.00665283 ,-0.01812744 ,-0.05093384 ,-0.06472778 , -0.04553223 ,-0.00024414 ,0.04782104 ,0.07012939 ,0.06353760 , 0.05288696 ,0.05194092 ,0.04565430 ,0.03445435 ,0.02157593 , 0.00759888 ,-0.00445557 ,-0.01138306 ,-0.00750732 ,0.00051880 , 0.00863647 ,0.01092529 ,0.00714111 ,-0.00531006 ,-0.02896118 , -0.05535889 ,-0.07098389 ,-0.07257080 ,-0.06118774 ,-0.03866577 , -0.01339722 ,-0.00054932 ,0.00357056 ,0.00863647 ,0.01425171 , 0.01672363 ,0.00274658 ,-0.02749634 ,-0.04589844 ,-0.03161621 , 0.00997925 ,0.04943848 ,0.07632446 ,0.09228516 ,0.08541870 , 0.04864502 ,0.00216675 ,-0.03546143 ,-0.05056763 ,-0.03686523 , -0.01281738 ,0.00479126 ,0.00775146 ,0.01666260 ,0.04562378 , 0.09100342 ,0.11947632 ,0.10379028 ,0.04785156 ,-0.01123047 , -0.04025269 ,-0.04382324 ,-0.04013062 ,-0.03479004 ,-0.02630615 , -0.01275635 ,0.00347900 ,0.01599121 ,0.01684570 ,0.00228882 , -0.01730347 ,-0.01953125 ,-0.00793457 ,-0.00097656 ,-0.01644897 , -0.04495239 ,-0.06954956 ,-0.06707764 ,-0.03173828 ,0.01602173 , 0.04293823 ,0.03967285 ,0.02087402 ,0.00134277 ,-0.01220703 , -0.02270508 ,-0.03704834 ,-0.05096436 ,-0.04501343 ,-0.01547241 , 0.01428223 ,0.03833008 ,0.05987549 ,0.08819580 ,0.11795044 , 0.13003540 ,0.10717773 ,0.05758667 ,0.00448608 ,-0.02969360 , -0.03036499 ,-0.00494385 ,0.01828003 ,0.02816772 ,0.02474976 , 0.01547241 ,-0.00201416 ,-0.01379395 ,-0.01593018 ,-0.00460815 , 0.00772095 ,0.03213501 ,0.07073975 ,0.10632324 ,0.11444092 , 0.09451294 ,0.06274414 ,0.03591919 ,0.02371216 ,0.02401733 , 0.03363037 ,0.03540039 ,0.03213501 ,0.03479004 ,0.04299927 , 0.04953003 ,0.03762817 ,0.00311279 ,-0.03158569 ,-0.03982544 , -0.03143311 ,-0.02239990 ,-0.03250122 ,-0.05715942 ,-0.07205200 , -0.06805420 ,-0.05813599 ,-0.06610107 ,-0.08862305 ,-0.11465454 , -0.11886597 ,-0.09622192 ,-0.05819702 ,-0.03021240 ,-0.01940918 , -0.01797485 ,-0.01263428 ,0.00384521 ,0.01907349 ,0.01416016 , -0.00558472 ,-0.02584839 ,-0.03100586 ,-0.01538086 ,0.01144409 , 0.03280640 ,0.04345703 ,0.04629517 ,0.04376221 ,0.03039551 , 0.01596069 ,0.01416016 ,0.02035522 ,0.02236938 ,0.00534058 , -0.02627563 ,-0.05178833 ,-0.06140137 ,-0.04321289 ,0.00259399 , 0.04824829 ,0.06127930 ,0.04373169 ,0.01947021 ,0.01254272 , 0.01773071 ,0.02230835 ,0.02679443 ,0.02661133 ,0.02221680 , 0.01998901 ,0.02114868 ,0.02642822 ,0.03173828 ,0.03033447 , 0.01315308 ,-0.01858521 ,-0.05773926 ,-0.08557129 ,-0.08682251 , -0.06549072 ,-0.03674316 ,-0.02200317 ,-0.02536011 ,-0.02978516 , -0.02078247 ,-0.00512695 ,0.00219727 ,-0.00628662 ,-0.02340698 , -0.04907227 ,-0.06738281 ,-0.06744385 ,-0.05062866 ,-0.02764893 , -0.01797485 ,-0.01535034 ,0.00390625 ,0.03964233 ,0.06436157 , 0.05490112 ,0.02224731 ,-0.01095581 ,-0.02548218 ,-0.02221680 , -0.00732422 ,0.02294922 ,0.05172729 ,0.06335449 ,0.04922485 , 0.01968384 ,-0.00476074 ,-0.01239014 ,0.00985718 ,0.04980469 , 0.08584595 ,0.09222412 ,0.06777954 ,0.02456665 ,-0.01669312 , -0.04669189 ,-0.05349731 ,-0.03646851 ,-0.00891113 ,0.01199341 , 0.01593018 ,0.01022339 ,0.00250244 ,-0.00396729 ,-0.01800537 , -0.03970337 ,-0.05813599 ,-0.05044556 ,-0.02023315 ,0.01803589 , 0.05026245 ,0.06546021 ,0.06582642 ,0.05175781 ,0.02841187 , 0.00222778 ,-0.01000977 ,-0.00131226 ,0.02191162 ,0.04345703 , 0.05093384 ,0.04476929 ,0.03732300 ,0.04440308 ,0.06219482 , 0.08001709 ,0.08605957 ,0.08245850 ,0.07467651 ,0.06564331 , 0.05523682 ,0.04220581 ,0.03411865 ,0.03762817 ,0.04681396 , 0.05279541 ,0.04296875 ,0.01516724 ,-0.01623535 ,-0.03369141 , -0.03149414 ,-0.01696777 ,-0.00625610 ,0.00247192 ,0.00848389 , 0.00552368 ,-0.00210571 ,-0.01968384 ,-0.04470825 ,-0.06118774 , -0.05017090 ,-0.02258301 ,-0.00732422 ,-0.00842285 ,-0.01754761 , -0.02746582 ,-0.03427124 ,-0.02975464 ,-0.00619507 ,0.01571655 , 0.02938843 ,0.02841187 ,0.01940918 ,0.01187134 ,0.01147461 , 0.02243042 ,0.04534912 ,0.06613159 ,0.05941772 ,0.02532959 , -0.01129150 ,-0.03701782 ,-0.04614258 ,-0.03637695 ,-0.01486206 , 0.00698853 ,0.01031494 ,0.00103760 ,0.00299072 ,0.02743530 , 0.04901123 ,0.04193115 ,0.00726318 ,-0.02731323 ,-0.03308105 , -0.01214600 ,0.00994873 ,0.02133179 ,0.02206421 ,0.01974487 , 0.01809692 ,0.01657104 ,0.01724243 ,0.01211548 ,0.00173950 , -0.01171875 ,-0.02490234 ,-0.04388428 ,-0.05343628 ,-0.04727173 , -0.03121948 ,-0.02398682 ,-0.03161621 ,-0.05047607 ,-0.05709839 , -0.03939819 ,-0.01733398 ,-0.02066040 ,-0.05102539 ,-0.09460449 , -0.11407471 ,-0.08480835 ,-0.01824951 ,0.04013062 ,0.05535889 , 0.03814697 ,0.01608276 ,0.01037598 ,0.02438354 ,0.04690552 , 0.06353760 ,0.06771851 ,0.05776978 ,0.03771973 ,0.02111816 , 0.01757812 ,0.02227783 ,0.03573608 ,0.05105591 ,0.05337524 , 0.02941895 ,-0.01431274 ,-0.04168701 ,-0.03051758 ,-0.00314331 , -0.00091553 ,-0.03393555 ,-0.07626343 ,-0.09500122 ,-0.08020020 , -0.04003906 ,-0.00076294 ,0.01141357 ,-0.00588989 ,-0.03521729 , -0.05108643 ,-0.03210449 ,0.00714111 ,0.04287720 ,0.05136108 , 0.04174805 ,0.03448486 ,0.03375244 ,0.02618408 ,0.00775146 , -0.00607300 ,-0.00125122 ,0.00747681 ,-0.00241089 ,-0.02920532 , -0.05029297 ,-0.06100464 ,-0.05532837 ,-0.03277588 ,0.00000000 , 0.03979492 ,0.07516479 ,0.08346558 ,0.05639648 ,0.01321411 , -0.02505493 ,-0.04199219 ,-0.03268433 ,-0.00698853 ,0.01895142 , 0.03045654 ,0.02709961 ,0.01705933 ,0.01007080 ,0.01785278 , 0.02841187 ,0.03344727 ,0.03417969 ,0.02554321 ,0.00622559 , -0.00747681 ,-0.01275635 ,-0.02322388 ,-0.04257202 ,-0.06365967 , -0.07623291 ,-0.06674194 ,-0.03237915 ,0.00408936 ,0.03222656 , 0.04519653 ,0.03378296 ,0.00152588 ,-0.03497314 ,-0.05523682 , -0.04498291 ,-0.00650024 ,0.03176880 ,0.04931641 ,0.03768921 , 0.01446533 ,0.00274658 ,0.01522827 ,0.03970337 ,0.05209351 , 0.04638672 ,0.02777100 ,0.00894165 ,-0.00219727 ,-0.00494385 , 0.00460815 ,0.02682495 ,0.04315186 ,0.03845215 ,0.00683594 , -0.02551270 ,-0.04177856 ,-0.03701782 ,-0.01025391 ,0.02215576 , 0.04815674 ,0.05740356 ,0.04870605 ,0.02648926 ,0.00784302 , 0.00549316 ,0.01574707 ,0.02505493 ,0.02713013 ,0.02294922 , 0.01629639 ,0.01193237 ,-0.00036621 ,-0.01568604 ,-0.02938843 , -0.03088379 ,-0.02444458 ,-0.01257324 ,-0.01147461 ,-0.01306152 , -0.01095581 ,0.00360107 ,0.01815796 ,0.02639771 ,0.02694702 , 0.01635742 ,0.00616455 ,0.00369263 ,0.00405884 ,0.00256348 , 0.00930786 ,0.02597046 ,0.05401611 ,0.07623291 ,0.07540894 , 0.04241943 ,0.00094604 ,-0.01974487 ,-0.00790405 ,0.02484131 , 0.05014038 ,0.05313110 ,0.03552246 ,0.01031494 ,-0.01019287 , -0.01715088 ,-0.00872803 ,0.00988770 ,0.02938843 ,0.03903198 , 0.03689575 ,0.01940918 ,-0.01391602 ,-0.04974365 ,-0.06170654 , -0.03378296 ,0.01565552 ,0.05471802 ,0.05599976 ,0.01470947 , -0.04315186 ,-0.08718872 ,-0.09475708 ,-0.07257080 ,-0.04211426 , -0.01385498 ,0.00570679 ,0.01412964 ,0.00900269 ,-0.01467896 , -0.04949951 ,-0.08337402 ,-0.10195923 ,-0.09158325 ,-0.05136108 , -0.00003052 ,0.03771973 ,0.04583740 ,0.02682495 ,-0.00619507 , -0.04113770 ,-0.06448364 ,-0.06503296 ,-0.04556274 ,-0.01461792 , 0.01986694 ,0.04324341 ,0.05236816 ,0.05187988 ,0.05517578 , 0.06430054 ,0.07260132 ,0.05712891 ,0.01556396 ,-0.03057861 , -0.05355835 ,-0.05230713 ,-0.02886963 ,0.00897217 ,0.04803467 , 0.07116699 ,0.06539917 ,0.03820801 ,0.00079346 ,-0.02572632 , -0.03460693 ,-0.02700806 ,-0.01202393 ,0.00222778 ,0.00946045 , 0.01599121 ,0.03375244 ,0.04696655 ,0.03967285 ,0.01510620 , -0.00656128 ,-0.01855469 ,-0.02096558 ,-0.01458740 ,-0.00854492 , -0.01428223 ,-0.02984619 ,-0.04074097 ,-0.03125000 ,0.00354004 , 0.04547119 ,0.06970215 ,0.06564331 ,0.04879761 ,0.04656982 , 0.05969238 ,0.06875610 ,0.05834961 ,0.03591919 ,0.00494385 , -0.02038574 ,-0.03344727 ,-0.03573608 ,-0.02520752 ,-0.00225830 , 0.02810669 ,0.05328369 ,0.05545044 ,0.03466797 ,-0.00085449 , -0.02963257 ,-0.04144287 ,-0.03082275 ,-0.01727295 ,-0.00921631 , -0.00198364 ,0.00183105 ,-0.00018311 ,-0.00714111 ,-0.01849365 , -0.01953125 ,-0.00634766 ,-0.00027466 ,-0.01034546 ,-0.02639771 , -0.03536987 ,-0.03289795 ,-0.03808594 ,-0.04943848 ,-0.05471802 , -0.05044556 ,-0.04010010 ,-0.03314209 ,-0.03274536 ,-0.04458618 , -0.06463623 ,-0.07836914 ,-0.06887817 ,-0.04122925 ,-0.01559448 , -0.00418091 ,-0.01318359 ,-0.02365112 ,-0.02767944 ,-0.02618408 , -0.01596069 ,0.00845337 ,0.02947998 ,0.03332520 ,0.02261353 , 0.00314331 ,-0.02005005 ,-0.03967285 ,-0.04592896 ,-0.03842163 , -0.02069092 ,0.00094604 ,0.02398682 ,0.04199219 ,0.04962158 , 0.04388428 ,0.02755737 ,0.00781250 ,-0.00216675 ,0.01159668 , 0.03662109 ,0.04791260 ,0.03503418 ,0.00784302 ,-0.01391602 , -0.01599121 ,-0.01306152 ,-0.01187134 ,-0.00775146 ,0.00582886 , 0.01577759 ,0.01062012 ,-0.00500488 ,-0.01293945 ,-0.00271606 , 0.01736450 ,0.02828979 ,0.02172852 ,0.00698853 ,-0.00781250 , -0.01824951 ,-0.02297974 ,-0.02087402 ,-0.00808716 ,0.01019287 , 0.01983643 ,0.01507568 ,0.00961304 ,0.01370239 ,0.03521729 , 0.05285645 ,0.04629517 ,0.01541138 ,-0.01693726 ,-0.02877808 , -0.02713013 ,-0.02035522 ,-0.01605225 ,-0.01342773 ,-0.01513672 , -0.02114868 ,-0.02944946 ,-0.03222656 ,-0.03118896 ,-0.03204346 , -0.04382324 ,-0.06341553 ,-0.07333374 ,-0.06024170 ,-0.02166748 , 0.01986694 ,0.04571533 ,0.04580688 ,0.03045654 ,0.01614380 , 0.01187134 ,0.00939941 ,-0.00207520 ,-0.02047729 ,-0.02603149 , 0.00192261 ,0.04837036 ,0.07873535 ,0.07290649 ,0.04342651 , 0.01300049 ,-0.00112915 ,0.00335693 ,0.00720215 ,-0.00341797 , -0.02194214 ,-0.03012085 ,-0.01718140 ,0.00909424 ,0.03137207 , 0.03875732 ,0.03598022 ,0.02426147 ,0.00778198 ,-0.01242065 , -0.02133179 ,-0.02069092 ,-0.01229858 ,-0.00585938 ,-0.00106812 , -0.00064087 ,-0.00933838 ,-0.01889038 ,-0.01980591 ,-0.00646973 , 0.01425171 ,0.02838135 ,0.03179932 ,0.02697754 ,0.02505493 , 0.02239990 ,0.02014160 ,0.02062988 ,0.01846313 ,0.01422119 , 0.01046753 ,0.00765991 ,0.01507568 ,0.03030396 ,0.05004883 , 0.06457520 ,0.06747437 ,0.06280518 ,0.05569458 ,0.04409790 , 0.01745605 ,-0.01727295 ,-0.04333496 ,-0.05041504 ,-0.03887939 , -0.01544189 ,0.00335693 ,0.00820923 ,0.00637817 ,0.00717163 , 0.00906372 ,0.01119995 ,0.00909424 ,0.00152588 ,-0.00885010 , -0.01974487 ,-0.02484131 ,-0.02236938 ,-0.01165771 ,0.00549316 , 0.02914429 ,0.04467773 ,0.03884888 ,0.01736450 ,-0.01226807 , -0.03567505 ,-0.04089355 ,-0.03228760 ,-0.01556396 ,0.00289917 , 0.01177979 ,0.00863647 ,0.00796509 ,0.02084351 ,0.03314209 , 0.02569580 ,0.00277710 ,-0.01644897 ,-0.01913452 ,-0.01437378 , -0.00982666 ,-0.01473999 ,-0.02661133 ,-0.03970337 ,-0.03820801 , -0.02133179 ,0.00765991 ,0.03619385 ,0.04583740 ,0.03302002 , 0.01013184 ,-0.00064087 ,0.00088501 ,0.00836182 ,0.01290894 , 0.00839233 ,-0.00125122 ,-0.00845337 ,-0.01693726 ,-0.02066040 , -0.01467896 ,0.00402832 ,0.02914429 ,0.05148315 ,0.05395508 , 0.03942871 ,0.01763916 ,0.00442505 ,-0.00143433 ,-0.00936890 , -0.02087402 ,-0.03118896 ,-0.03002930 ,-0.02276611 ,-0.01293945 , -0.01119995 ,-0.02700806 ,-0.05477905 ,-0.07656860 ,-0.06976318 , -0.04266357 ,-0.01989746 ,-0.01409912 ,-0.02166748 ,-0.02865601 , -0.02569580 ,-0.01641846 ,-0.00057983 ,0.01968384 ,0.03079224 , 0.02828979 ,0.01449585 ,0.00317383 ,-0.00228882 ,0.00366211 , 0.02151489 ,0.04089355 ,0.04757690 ,0.03479004 ,0.01156616 , -0.00634766 ,-0.01806641 ,-0.02627563 ,-0.02755737 ,-0.02774048 , -0.02468872 ,-0.01571655 ,-0.00155640 ,0.01095581 ,0.01425171 , 0.00726318 ,-0.00894165 ,-0.02615356 ,-0.03097534 ,-0.01580811 , -0.00158691 ,-0.00219727 ,-0.01574707 ,-0.03079224 ,-0.03790283 , -0.04418945 ,-0.04702759 ,-0.04113770 ,-0.03039551 ,-0.03067017 , -0.04479980 ,-0.06448364 ,-0.06652832 ,-0.04202271 ,0.00045776 , 0.03677368 ,0.04806519 ,0.02905273 ,0.00018311 ,-0.01547241 , -0.00820923 ,0.01168823 ,0.02084351 ,0.01629639 ,0.00537109 , -0.00402832 ,-0.01184082 ,-0.00744629 ,0.01266479 ,0.03097534 , 0.03347778 ,0.02154541 ,0.00677490 ,0.00268555 ,0.01220703 , 0.03179932 ,0.04687500 ,0.04516602 ,0.02462769 ,0.00448608 , -0.00558472 ,-0.00296021 ,0.00280762 ,0.00686646 ,0.01412964 , 0.02151489 ,0.02944946 ,0.03326416 ,0.03158569 ,0.01855469 , -0.00704956 ,-0.02822876 ,-0.03460693 ,-0.02777100 ,-0.02322388 , -0.02844238 ,-0.03884888 ,-0.04455566 ,-0.04202271 ,-0.03161621 , -0.01907349 ,-0.00335693 ,0.01565552 ,0.02890015 ,0.03576660 , 0.03237915 ,0.02777100 ,0.03308105 ,0.04473877 ,0.05319214 , 0.05255127 ,0.04296875 ,0.03128052 ,0.02505493 ,0.03521729 , 0.05325317 ,0.06741333 ,0.07434082 ,0.06921387 ,0.05191040 , 0.03707886 ,0.03314209 ,0.04760742 ,0.07324219 ,0.09188843 , 0.08703613 ,0.05920410 ,0.01651001 ,-0.02056885 ,-0.04504395 , -0.05798340 ,-0.05886841 ,-0.04367065 ,-0.02227783 ,-0.00787354 , -0.00320435 ,-0.00424194 ,-0.00448608 ,0.00122070 ,0.00408936 , 0.00378418 ,-0.00155640 ,-0.00656128 ,-0.02087402 ,-0.04367065 , -0.06201172 ,-0.06045532 ,-0.04492188 ,-0.03237915 ,-0.02569580 , -0.01571655 ,-0.00091553 ,0.01666260 ,0.02825928 ,0.03292847 , 0.02819824 ,0.02078247 ,0.01437378 ,0.00543213 ,-0.00354004 , -0.00869751 ,-0.01077271 ,-0.01367188 ,-0.01989746 ,-0.03549194 , -0.05368042 ,-0.05975342 ,-0.04302979 ,-0.00738525 ,0.02990723 , 0.05740356 ,0.06668091 ,0.05450439 ,0.02975464 ,0.00329590 , -0.01470947 ,-0.01870728 ,-0.00466919 ,0.01556396 ,0.02343750 , 0.01925659 ,0.01547241 ,0.01962280 ,0.02758789 ,0.03314209 , 0.03430176 ,0.02975464 ,0.02471924 ,0.02798462 ,0.04492188 , 0.07052612 ,0.08901978 ,0.09219360 ,0.07684326 ,0.05587769 , 0.03677368 ,0.01611328 ,-0.00634766 ,-0.02380371 ,-0.03323364 , -0.03179932 ,-0.01342773 ,0.02252197 ,0.06280518 ,0.08563232 , 0.07855225 ,0.05398560 ,0.02764893 ,0.00521851 ,-0.01141357 , -0.02243042 ,-0.02728271 ,-0.03231812 ,-0.04162598 ,-0.04867554 , -0.04147339 ,-0.02111816 ,-0.00369263 ,-0.00466919 ,-0.02078247 , -0.03546143 ,-0.04293823 ,-0.04812622 ,-0.05358887 ,-0.05093384 , -0.03601074 ,-0.00878906 ,0.01672363 ,0.03073120 ,0.02618408 , 0.01065063 ,-0.00921631 ,-0.03125000 ,-0.05633545 ,-0.07513428 , -0.07669067 ,-0.04867554 ,-0.00369263 ,0.04141235 ,0.06527710 , 0.05978394 ,0.03567505 ,0.00814819 ,-0.00970459 ,-0.01980591 , -0.02529907 ,-0.02905273 ,-0.03814697 ,-0.05087280 ,-0.05566406 , -0.05288696 ,-0.05126953 ,-0.05740356 ,-0.06552124 ,-0.06170654 , -0.04284668 ,-0.02252197 ,-0.00930786 ,-0.00534058 ,-0.00173950 , 0.00930786 ,0.02648926 ,0.03625488 ,0.03228760 ,0.02597046 , 0.03576660 ,0.06015015 ,0.08120728 ,0.08581543 ,0.07257080 , 0.05899048 ,0.05767822 ,0.06738281 ,0.07180786 ,0.06768799 , 0.05438232 ,0.03811646 ,0.02474976 ,0.01785278 ,0.00708008 , -0.01278687 ,-0.03781128 ,-0.06204224 ,-0.07049561 ,-0.05593872 , -0.03463745 ,-0.02230835 ,-0.01681519 ,-0.00451660 ,0.01925659 , 0.03802490 ,0.03720093 ,0.01696777 ,-0.00775146 ,-0.02435303 , -0.03253174 ,-0.03283691 ,-0.03088379 ,-0.02917480 ,-0.02383423 , -0.00973511 ,0.00946045 ,0.01947021 ,0.01501465 ,-0.00140381 , -0.01486206 ,-0.01278687 ,-0.00195312 ,0.00228882 ,-0.00729370 , -0.01815796 ,-0.02346802 ,-0.01284790 ,0.00128174 ,0.00653076 , 0.00582886 ,0.00436401 ,0.00302124 ,-0.00271606 ,-0.00564575 , -0.00173950 ,0.01220703 ,0.02819824 ,0.03662109 ,0.03640747 , 0.04125977 ,0.05651855 ,0.07250977 ,0.07055664 ,0.05050659 , 0.01568604 ,-0.02859497 ,-0.06475830 ,-0.07916260 ,-0.06814575 , -0.03756714 ,-0.00158691 ,0.02478027 ,0.03414917 ,0.02801514 , 0.01583862 ,-0.00064087 ,-0.02044678 ,-0.03634644 ,-0.04129028 , -0.03250122 ,-0.01855469 ,-0.01266479 ,-0.01251221 ,-0.01080322 , -0.00140381 ,0.01437378 ,0.02285767 ,0.02261353 ,0.01391602 , 0.00393677 ,0.00289917 ,0.01062012 ,0.01931763 ,0.02288818 , 0.02148438 ,0.00973511 ,-0.00503540 ,-0.02236938 ,-0.03732300 , -0.05130005 ,-0.04623413 ,-0.01361084 ,0.03033447 ,0.06042480 , 0.06710815 ,0.06512451 ,0.06976318 ,0.08035278 ,0.08230591 , 0.06542969 ,0.03280640 ,0.00491333 ,0.00213623 ,0.02334595 , 0.04016113 ,0.02700806 ,-0.01135254 ,-0.04727173 ,-0.05630493 , -0.04132080 ,-0.01602173 ,0.00939941 ,0.02853394 ,0.03521729 , 0.02862549 ,0.02404785 ,0.02459717 ,0.02832031 ,0.03137207 , 0.03350830 ,0.03311157 ,0.01919556 ,-0.00839233 ,-0.03640747 , -0.04693604 ,-0.03320312 ,-0.00875854 ,0.01098633 ,0.01635742 , 0.01654053 ,0.01419067 ,0.01040649 ,-0.00015259 ,-0.01617432 , -0.02853394 ,-0.03433228 ,-0.03723145 ,-0.03463745 ,-0.02877808 , -0.02572632 ,-0.02600098 ,-0.01950073 ,-0.00695801 ,0.01068115 , 0.02468872 ,0.02542114 ,0.01287842 ,-0.00009155 ,-0.00738525 , -0.00982666 ,-0.01412964 ,-0.02154541 ,-0.02435303 ,-0.02294922 , -0.01742554 ,-0.01531982 ,-0.01553345 ,-0.01351929 ,-0.00827026 , 0.00048828 ,0.00991821 ,0.01376343 ,0.01062012 ,-0.00100708 , -0.01135254 ,-0.01437378 ,-0.00781250 ,-0.00875854 ,-0.01730347 , -0.02288818 ,-0.01687622 ,0.00158691 ,0.01425171 ,0.00930786 , -0.00482178 ,-0.01351929 ,-0.01312256 ,-0.00808716 ,-0.00695801 , -0.00592041 ,-0.00039673 ,0.00988770 ,0.02392578 ,0.03475952 , 0.03906250 ,0.03860474 ,0.03784180 ,0.03643799 ,0.03753662 , 0.03494263 ,0.02502441 ,0.00866699 ,-0.00451660 ,-0.01141357 , -0.00357056 ,0.00778198 ,0.01867676 ,0.02792358 ,0.04040527 , 0.05419922 ,0.06176758 ,0.05569458 ,0.03570557 ,0.00619507 , -0.01910400 ,-0.02761841 ,-0.02005005 ,-0.01248169 ,-0.00857544 , -0.00085449 ,0.01000977 ,0.01699829 ,0.00894165 ,-0.00384521 , -0.00805664 ,-0.00054932 ,0.01235962 ,0.01776123 ,0.01324463 , -0.00173950 ,-0.01312256 ,-0.01483154 ,-0.00296021 ,0.02157593 , 0.04104614 ,0.04025269 ,0.01950073 ,-0.00256348 ,-0.00906372 , -0.00244141 ,0.01013184 ,0.01367188 ,0.00772095 ,0.00073242 , 0.00064087 ,0.00955200 ,0.01928711 ,0.02154541 ,0.00683594 , -0.01733398 ,-0.03710938 ,-0.04067993 ,-0.03320312 ,-0.02664185 , -0.02041626 ,-0.01007080 ,0.00018311 ,0.00955200 ,0.01303101 , 0.01657104 ,0.01403809 ,0.00915527 ,-0.00140381 ,-0.01724243 , -0.03488159 ,-0.04397583 ,-0.04376221 ,-0.03341675 ,-0.02230835 , -0.00683594 ,0.01040649 ,0.02627563 ,0.03158569 ,0.02481079 , 0.01712036 ,0.01458740 ,0.01574707 ,0.01159668 ,0.00466919 , 0.00479126 ,0.00793457 ,0.01730347 ,0.02429199 ,0.03106689 , 0.02438354 ,0.00540161 ,-0.01501465 ,-0.02636719 ,-0.02511597 , -0.01773071 ,-0.00900269 ,-0.00491333 ,-0.00601196 ,-0.01046753 , -0.01351929 ,-0.01315308 ,-0.01980591 ,-0.02346802 ,-0.01419067 , 0.00753784 ,0.02835083 ,0.03701782 ,0.03976440 ,0.04275513 , 0.04913330 ,0.05899048 ,0.06707764 ,0.07025146 ,0.06365967 , 0.05590820 ,0.04580688 ,0.03353882 ,0.01461792 ,-0.00924683 , -0.02066040 ,-0.01641846 ,0.00195312 ,0.01626587 ,0.01589966 , 0.00213623 ,-0.01391602 ,-0.01971436 ,-0.01641846 ,-0.00839233 , -0.00448608 ,-0.00799561 ,-0.01263428 ,-0.01544189 ,-0.01913452 , -0.02536011 ,-0.03332520 ,-0.03717041 ,-0.02838135 ,-0.01095581 , 0.00482178 ,0.01141357 ,0.01138306 ,0.01263428 ,0.01327515 , 0.00735474 ,-0.00012207 ,-0.01290894 ,-0.02554321 ,-0.03283691 , -0.02575684 ,-0.00854492 ,0.01388550 ,0.03314209 ,0.04974365 , 0.05252075 ,0.04010010 ,0.01843262 ,0.00076294 ,-0.00778198 , -0.00848389 ,-0.00064087 ,0.01019287 ,0.01690674 ,0.01763916 , 0.01364136 ,0.00573730 ,-0.00119019 ,-0.00985718 ,-0.01947021 , -0.02938843 ,-0.02871704 ,-0.01748657 ,-0.00280762 ,0.00942993 , 0.01135254 ,0.00381470 ,-0.00494385 ,-0.01605225 ,-0.03027344 , -0.04476929 ,-0.05160522 ,-0.04580688 ,-0.03079224 ,-0.01333618 , 0.00201416 ,0.00845337 ,0.00756836 ,-0.00070190 ,-0.00347900 , -0.00399780 ,-0.00765991 ,-0.02038574 ,-0.03317261 ,-0.03942871 , -0.03262329 ,-0.01922607 ,-0.00375366 ,0.00518799 ,0.01028442 , 0.00912476 ,0.00555420 ,0.00616455 ,0.01821899 ,0.03286743 , 0.04724121 ,0.05587769 ,0.05715942 ,0.05731201 ,0.05395508 , 0.04812622 ,0.04382324 ,0.04779053 ,0.05310059 ,0.04989624 , 0.03930664 ,0.02410889 ,0.01330566 ,0.01098633 ,0.01397705 , 0.01034546 ,0.00643921 ,0.00543213 ,0.00714111 ,0.01367188 , 0.01809692 ,0.01275635 ,-0.00210571 ,-0.01379395 ,-0.01620483 , -0.00708008 ,0.00680542 ,0.01599121 ,0.01339722 ,-0.00039673 , -0.02053833 ,-0.04309082 ,-0.05984497 ,-0.06079102 ,-0.04498291 , -0.02041626 ,-0.00372314 ,0.00241089 ,0.00674438 ,0.01129150 , 0.01342773 ,0.00747681 ,-0.00564575 ,-0.02438354 ,-0.04190063 , -0.04675293 ,-0.03622437 ,-0.01770020 ,0.00393677 ,0.01992798 , 0.02618408 ,0.02947998 ,0.03649902 ,0.03561401 ,0.02877808 , 0.02108765 ,0.01556396 ,0.01403809 ,0.01123047 ,0.00192261 , -0.00634766 ,-0.00643921 ,0.00167847 ,0.00619507 ,0.00164795 , -0.00537109 ,-0.01296997 ,-0.01400757 ,-0.00534058 ,0.00997925 , 0.02066040 ,0.01644897 ,0.00473022 ,-0.00146484 ,0.00405884 , 0.01721191 ,0.02285767 ,0.01455688 ,-0.00650024 ,-0.01916504 , -0.02026367 ,-0.01156616 ,-0.00012207 ,0.00668335 ,0.01034546 , 0.01324463 ,0.01818848 ,0.02151489 ,0.02261353 ,0.02560425 , 0.03067017 ,0.03088379 ,0.02243042 ,0.01104736 ,0.00485229 , 0.00662231 ,0.01354980 ,0.01956177 ,0.01968384 ,0.01409912 , 0.01116943 ,0.00985718 ,0.00964355 ,0.01593018 ,0.02719116 , 0.03335571 ,0.02926636 ,0.02093506 ,0.01528931 ,0.01284790 , 0.01480103 ,0.01681519 ,0.02188110 ,0.02459717 ,0.02624512 , 0.03094482 ,0.03384399 ,0.03256226 ,0.02175903 ,0.00134277 , -0.02566528 ,-0.04537964 ,-0.04492188 ,-0.02819824 ,-0.00955200 , 0.00106812 ,0.00262451 ,-0.00405884 ,-0.01083374 ,-0.01635742 , -0.02365112 ,-0.03085327 ,-0.04000854 ,-0.04742432 ,-0.05081177 , -0.04299927 ,-0.02359009 ,0.00204468 ,0.01495361 ,0.01733398 , 0.01342773 ,0.01403809 ,0.02005005 ,0.02609253 ,0.03060913 , 0.02355957 ,0.01138306 ,-0.00177002 ,-0.00769043 ,-0.00466919 , 0.01129150 ,0.03308105 ,0.05242920 ,0.06625366 ,0.06549072 , 0.04907227 ,0.02389526 ,0.00296021 ,-0.01040649 ,-0.01333618 , -0.00613403 ,0.00509644 ,0.01989746 ,0.03158569 ,0.03552246 , 0.03359985 ,0.02670288 ,0.01785278 ,0.00674438 ,-0.00466919 , -0.01605225 ,-0.02465820 ,-0.02581787 ,-0.01879883 ,-0.00732422 , 0.00408936 ,0.00878906 ,0.00399780 ,-0.01419067 ,-0.04025269 , -0.06164551 ,-0.07165527 ,-0.06353760 ,-0.04171753 ,-0.01589966 , 0.00576782 ,0.02175903 ,0.03262329 ,0.03924561 ,0.03674316 , 0.03018188 ,0.01687622 ,0.00717163 ,0.00354004 ,0.00537109 , 0.00952148 ,0.01333618 ,0.01739502 ,0.01992798 ,0.01403809 , -0.00253296 ,-0.02023315 ,-0.02786255 ,-0.02087402 ,-0.00057983 , 0.02334595 ,0.04064941 ,0.04180908 ,0.03427124 ,0.02151489 , 0.00634766 ,0.00192261 ,0.01062012 ,0.03036499 ,0.04336548 , 0.03771973 ,0.02224731 ,0.01074219 ,0.01083374 ,0.02178955 , 0.03143311 ,0.03216553 ,0.02163696 ,0.00607300 ,-0.00491333 , -0.01052856 ,-0.01300049 ,-0.02056885 ,-0.03256226 ,-0.04010010 , -0.03417969 ,-0.01864624 ,-0.00445557 ,0.00399780 ,0.00070190 , -0.00967407 ,-0.01995850 ,-0.02224731 ,-0.02322388 ,-0.02450562 , -0.03134155 ,-0.03665161 ,-0.03048706 ,-0.01477051 ,-0.00177002 , 0.00012207 ,-0.00802612 ,-0.00988770 ,-0.00216675 ,0.00534058 , 0.00793457 ,0.00497437 ,0.00427246 ,0.00604248 ,0.00854492 , 0.01208496 ,0.01367188 ,0.00598145 ,-0.00775146 ,-0.02108765 , -0.02896118 ,-0.03128052 ,-0.02969360 ,-0.02346802 ,-0.01190186 , 0.00338745 ,0.01901245 ,0.02972412 ,0.03424072 ,0.02874756 , 0.01504517 ,0.00009155 ,-0.00796509 ,0.00119019 ,0.02679443 , 0.05078125 ,0.06118774 ,0.05358887 ,0.04348755 ,0.03619385 , 0.03231812 ,0.02978516 ,0.03005981 ,0.03207397 ,0.03210449 , 0.02655029 ,0.01889038 ,0.01654053 ,0.02001953 ,0.02365112 , 0.02813721 ,0.03491211 ,0.03878784 ,0.03219604 ,0.01245117 , -0.00939941 ,-0.02105713 ,-0.02282715 ,-0.02322388 ,-0.01867676 , -0.00927734 ,-0.00222778 ,-0.00250244 ,-0.01257324 ,-0.02136230 , -0.02636719 ,-0.02020264 ,-0.00900269 ,0.00103760 ,0.00311279 , -0.00064087 ,-0.00686646 ,-0.00552368 ,-0.00234985 ,-0.00555420 , -0.00985718 ,-0.00833130 ,0.00839233 ,0.03237915 ,0.04699707 , 0.04568481 ,0.03305054 ,0.02047729 ,0.01583862 ,0.01867676 , 0.02117920 ,0.01763916 ,0.00671387 ,-0.00857544 ,-0.01797485 , -0.01626587 ,-0.00582886 ,0.00192261 ,0.00573730 ,0.01104736 , 0.02230835 ,0.03121948 ,0.02362061 ,0.00198364 ,-0.02517700 , -0.04412842 ,-0.04519653 ,-0.03662109 ,-0.02142334 ,-0.00823975 , 0.00476074 ,0.01464844 ,0.02154541 ,0.02056885 ,0.01501465 , 0.00714111 ,0.00354004 ,0.00665283 ,0.01638794 ,0.02670288 , 0.03344727 ,0.03799438 ,0.04107666 ,0.03787231 ,0.03576660 , 0.03347778 ,0.02966309 ,0.02609253 ,0.01550293 ,0.00820923 , 0.00894165 ,0.02127075 ,0.03741455 ,0.04364014 ,0.03570557 , 0.01681519 ,-0.00717163 ,-0.02407837 ,-0.02713013 ,-0.02258301 , -0.02044678 ,-0.02554321 ,-0.03561401 ,-0.04434204 ,-0.05029297 , -0.04901123 ,-0.04571533 ,-0.04275513 ,-0.04089355 ,-0.03363037 , -0.02493286 ,-0.01382446 ,-0.00576782 ,-0.00161743 ,0.00061035 , 0.00350952 ,0.00991821 ,0.01580811 ,0.01754761 ,0.01983643 , 0.02319336 ,0.02557373 ,0.02325439 ,0.01968384 ,0.01721191 , 0.01620483 ,0.01745605 ,0.02471924 ,0.02743530 ,0.02383423 , 0.01281738 ,0.00476074 ,0.00494385 ,0.01101685 ,0.01507568 , 0.01147461 ,-0.00375366 ,-0.02310181 ,-0.04415894 ,-0.05761719 , -0.05908203 ,-0.04937744 ,-0.03103638 ,-0.00924683 ,0.01312256 , 0.02587891 ,0.02377319 ,0.01013184 ,-0.00201416 ,-0.00448608 , 0.00595093 ,0.01544189 ,0.01800537 ,0.01522827 ,0.01095581 , 0.00570679 ,-0.00418091 ,-0.01754761 ,-0.02600098 ,-0.02743530 , -0.02026367 ,-0.01400757 ,-0.01300049 ,-0.01919556 ,-0.02703857 , -0.02987671 ,-0.02191162 ,-0.00552368 ,0.01174927 ,0.01190186 , -0.00616455 ,-0.03045654 ,-0.04473877 ,-0.04452515 ,-0.03619385 , -0.03051758 ,-0.02505493 ,-0.01586914 ,-0.00723267 ,-0.00131226 , -0.00448608 ,-0.00952148 ,-0.01284790 ,-0.01150513 ,-0.00558472 , -0.00299072 ,-0.00314331 ,-0.00250244 ,0.00350952 ,0.00894165 , 0.00729370 ,-0.00259399 ,-0.01733398 ,-0.02331543 ,-0.02050781 , -0.01330566 ,-0.00488281 ,0.00143433 ,0.00540161 ,0.00680542 , 0.00921631 ,0.01425171 ,0.02136230 ,0.02545166 ,0.02285767 , 0.01550293 ,0.01080322 ,0.01382446 ,0.02575684 ,0.02807617 , 0.02230835 ,0.00869751 ,0.00296021 ,0.01016235 ,0.02313232 , 0.03448486 ,0.03906250 ,0.04107666 ,0.03997803 ,0.04006958 , 0.03787231 ,0.03283691 ,0.02209473 ,0.01278687 ,0.00677490 , 0.00485229 ,0.00262451 ,0.00088501 ,0.00289917 ,0.01251221 , 0.02505493 ,0.02960205 ,0.02136230 ,0.00387573 ,-0.01477051 , -0.02554321 ,-0.02868652 ,-0.02716064 ,-0.02105713 ,-0.01342773 , -0.00765991 ,-0.00872803 ,-0.01242065 ,-0.02069092 ,-0.03002930 , -0.04306030 ,-0.04739380 ,-0.04129028 ,-0.02911377 ,-0.01809692 , -0.00936890 ,-0.00109863 ,0.01065063 ,0.02175903 ,0.02896118 , 0.02780151 ,0.02807617 ,0.02664185 ,0.02743530 ,0.03097534 , 0.03375244 ,0.03292847 ,0.02911377 ,0.02902222 ,0.03164673 , 0.03897095 ,0.04428101 ,0.04269409 ,0.04373169 ,0.04364014 , 0.04287720 ,0.03298950 ,0.02182007 ,0.01159668 ,0.00424194 , 0.00042725 ,-0.00219727 ,-0.00207520 ,0.00210571 ,0.01037598 , 0.00961304 ,0.00173950 ,-0.00457764 ,-0.00408936 ,0.00332642 , 0.00915527 ,0.00561523 ,0.00000000 ,-0.00219727 ,-0.00592041 , -0.01007080 ,-0.02017212 ,-0.02795410 ,-0.02407837 ,-0.01330566 , -0.00088501 ,0.00781250 ,0.01385498 ,0.01882935 ,0.01907349 , 0.01641846 ,0.00967407 ,0.00170898 ,-0.00668335 ,-0.00875854 , -0.00381470 ,0.01245117 ,0.02819824 ,0.03649902 ,0.03140259 , 0.01910400 ,0.01040649 ,0.01220703 ,0.02468872 ,0.03329468 , 0.02740479 ,0.01477051 ,0.01113892 ,0.02081299 ,0.03320312 , 0.03964233 ,0.03393555 ,0.02319336 ,0.01086426 ,0.00286865 , -0.00640869 ,-0.02035522 ,-0.03256226 ,-0.03823853 ,-0.03359985 , -0.01931763 ,0.00009155 ,0.01739502 ,0.02420044 ,0.02120972 , 0.00262451 ,-0.01849365 ,-0.03952026 ,-0.05563354 ,-0.06210327 , -0.05889893 ,-0.04608154 ,-0.03131104 ,-0.01910400 ,-0.01513672 , -0.01101685 ,-0.00460815 ,0.00439453 ,0.00567627 ,-0.00204468 , -0.01797485 ,-0.03497314 ,-0.04205322 ,-0.03598022 ,-0.02218628 , -0.00540161 ,0.00814819 ,0.01379395 ,0.01159668 ,0.00387573 , -0.00723267 ,-0.01715088 ,-0.02020264 ,-0.01409912 ,-0.00494385 , 0.00100708 ,0.00576782 ,0.01498413 ,0.02850342 ,0.03890991 , 0.04406738 ,0.04449463 ,0.04302979 ,0.04229736 ,0.04110718 , 0.03335571 ,0.02749634 ,0.02536011 ,0.02902222 ,0.03820801 , 0.04385376 ,0.04309082 ,0.03363037 ,0.01956177 ,0.00802612 , 0.00247192 ,0.00289917 ,0.00689697 ,0.00622559 ,-0.00378418 , -0.01507568 ,-0.02276611 ,-0.02459717 ,-0.02838135 ,-0.03561401 , -0.04342651 ,-0.04324341 ,-0.02923584 ,-0.00952148 ,0.00451660 , 0.01083374 ,0.01452637 ,0.01638794 ,0.01834106 ,0.01681519 , 0.00918579 ,-0.00579834 ,-0.02191162 ,-0.03326416 ,-0.03628540 , -0.03408813 ,-0.02468872 ,-0.01339722 ,0.00112915 ,0.02056885 , 0.03704834 ,0.04598999 ,0.03985596 ,0.01925659 ,0.00189209 , -0.00720215 ,-0.00619507 ,0.00115967 ,0.00765991 ,0.01461792 , 0.01547241 ,0.01495361 ,0.01229858 ,0.00198364 ,-0.01260376 , -0.02606201 ,-0.02972412 ,-0.02386475 ,-0.01440430 ,-0.00933838 , -0.01165771 ,-0.01794434 ,-0.02560425 ,-0.03002930 ,-0.03463745 , -0.03988647 ,-0.04537964 ,-0.04757690 ,-0.04504395 ,-0.03622437 , -0.02258301 ,-0.00726318 ,0.00216675 ,0.00964355 ,0.01885986 , 0.02539062 ,0.02767944 ,0.02462769 ,0.01828003 ,0.00933838 , 0.00454712 ,0.00744629 ,0.01443481 ,0.02075195 ,0.02517700 , 0.02606201 ,0.02551270 ,0.02868652 ,0.02981567 ,0.02502441 , 0.01644897 ,0.00778198 ,0.00204468 ,0.00387573 ,0.01135254 , 0.02014160 ,0.02630615 ,0.02551270 ,0.01733398 ,0.00222778 , -0.01065063 ,-0.01303101 ,-0.00445557 ,0.00546265 ,0.01150513 , 0.01229858 ,0.00723267 ,0.00442505 ,0.00604248 ,0.00433350 , 0.00393677 ,0.00405884 ,0.00839233 ,0.01132202 ,0.01232910 , 0.00613403 ,0.00241089 ,0.00250244 ,0.00354004 ,0.00308228 , 0.00280762 ,0.00170898 ,0.00500488 ,0.00833130 ,0.00872803 , 0.00756836 ,0.00543213 ,0.00115967 ,-0.00320435 ,-0.00765991 , -0.01141357 ,-0.01403809 ,-0.01739502 ,-0.02172852 ,-0.02444458 , -0.02365112 ,-0.01739502 ,-0.00875854 ,-0.00387573 ,-0.00354004 , -0.00772095 ,-0.00973511 ,-0.01037598 ,-0.01034546 ,-0.00955200 , -0.00869751 ,-0.00927734 ,-0.01119995 ,-0.01098633 ,-0.00701904 , -0.00396729 ,-0.00183105 ,0.00070190 ,0.00225830 ,0.00500488 , 0.00320435 ,-0.00570679 ,-0.01660156 ,-0.02520752 ,-0.02627563 , -0.02276611 ,-0.01831055 ,-0.01150513 ,-0.00650024 ,-0.00234985 , 0.00317383 ,0.00433350 ,-0.00103760 ,-0.01568604 ,-0.03082275 , -0.03604126 ,-0.02832031 ,-0.01113892 ,0.00485229 ,0.01129150 , 0.00991821 ,0.00100708 ,-0.00585938 ,-0.00537109 ,0.00482178 , 0.01831055 ,0.02752686 ,0.03253174 ,0.03704834 ,0.04132080 , 0.04782104 ,0.05230713 ,0.05834961 ,0.06558228 ,0.06744385 , 0.06121826 ,0.05004883 ,0.04199219 ,0.04058838 ,0.03933716 , 0.03619385 ,0.02947998 ,0.01623535 ,-0.00097656 ,-0.01480103 , -0.01940918 ,-0.01498413 ,-0.00994873 ,-0.00540161 ,-0.00854492 , -0.02163696 ,-0.03753662 ,-0.04403687 ,-0.03601074 ,-0.01788330 , -0.00576782 ,-0.00509644 ,-0.01242065 ,-0.01693726 ,-0.01632690 , -0.01092529 ,-0.00341797 ,0.00320435 ,0.00653076 ,0.00708008 , 0.00439453 ,-0.00439453 ,-0.01647949 ,-0.02526855 ,-0.02658081 , -0.01968384 ,-0.00936890 ,0.00299072 ,0.00885010 ,0.01239014 , 0.01541138 ,0.01834106 ,0.01986694 ,0.01852417 ,0.01837158 , 0.02056885 ,0.02496338 ,0.02175903 ,0.00714111 ,-0.00769043 , -0.01892090 ,-0.01431274 ,0.00173950 ,0.01876831 ,0.02694702 , 0.02273560 ,0.00811768 ,-0.00930786 ,-0.02084351 ,-0.01889038 , -0.00817871 ,0.00805664 ,0.01669312 ,0.01803589 ,0.00817871 , -0.00341797 ,-0.00906372 ,-0.00820923 ,-0.00686646 ,-0.00735474 , -0.01278687 ,-0.01928711 ,-0.02212524 ,-0.01840210 ,-0.01260376 , -0.00827026 ,-0.00433350 ,-0.00283813 ,0.00051880 ,0.00848389 , 0.01611328 ,0.01754761 ,0.00723267 ,-0.00863647 ,-0.02096558 , -0.02343750 ,-0.01715088 ,-0.01062012 ,-0.00836182 ,-0.01226807 , -0.01687622 ,-0.01980591 ,-0.02301025 ,-0.02548218 ,-0.02224731 , -0.01364136 ,-0.00524902 ,-0.00149536 ,0.00051880 ,0.00030518 , -0.00234985 ,-0.00460815 ,0.00115967 ,0.01403809 ,0.02914429 , 0.03811646 ,0.03787231 ,0.03747559 ,0.04498291 ,0.05673218 , 0.06558228 ,0.06820679 ,0.06118774 ,0.04699707 ,0.03414917 , 0.02349854 ,0.01300049 ,0.00271606 ,-0.00534058 ,-0.01086426 , -0.00735474 ,0.00280762 ,0.01138306 ,0.01718140 ,0.02178955 , 0.02099609 ,0.01754761 ,0.00485229 ,-0.00958252 ,-0.01562500 , -0.00842285 ,0.00643921 ,0.01821899 ,0.01815796 ,0.00878906 , -0.00906372 ,-0.01916504 ,-0.02035522 ,-0.01724243 ,-0.01580811 , -0.01419067 ,-0.01376343 ,-0.01229858 ,-0.00930786 ,-0.00610352 , -0.00756836 ,-0.01318359 ,-0.01776123 ,-0.02398682 ,-0.02951050 , -0.03308105 ,-0.03564453 ,-0.03433228 ,-0.02847290 ,-0.01806641 , -0.00689697 ,0.00479126 ,0.01309204 ,0.02062988 ,0.02658081 , 0.03115845 ,0.03488159 ,0.03463745 ,0.03134155 ,0.02410889 , 0.01751709 ,0.01205444 ,0.00711060 ,-0.00015259 ,-0.00067139 , 0.00271606 ,0.00625610 ,0.00622559 ,0.00454712 ,0.00311279 , 0.00302124 ,0.00610352 ,0.01635742 ,0.02944946 ,0.03717041 , 0.03588867 ,0.01861572 ,-0.00341797 ,-0.02194214 ,-0.02859497 , -0.02655029 ,-0.02096558 ,-0.01699829 ,-0.01162720 ,-0.00598145 , 0.00201416 ,0.00631714 ,0.01144409 ,0.01171875 ,0.00811768 , 0.00115967 ,-0.00741577 ,-0.00985718 ,-0.00921631 ,-0.00381470 , 0.00421143 ,0.01300049 ,0.02331543 ,0.03240967 ,0.04022217 , 0.04302979 ,0.04092407 ,0.03530884 ,0.03253174 ,0.03176880 , 0.03445435 ,0.03753662 ,0.03857422 ,0.03552246 ,0.02847290 , 0.01861572 ,0.01019287 ,0.00262451 ,-0.00289917 ,-0.00082397 , 0.00476074 ,0.00762939 ,0.00515747 ,0.00366211 ,0.00918579 , 0.01907349 ,0.02487183 ,0.01928711 ,0.00650024 ,-0.00674438 , -0.01058960 ,-0.00878906 ,-0.00289917 ,-0.00128174 ,-0.00909424 , -0.02044678 ,-0.03121948 ,-0.03482056 ,-0.03146362 ,-0.01974487 , -0.00344849 ,0.01068115 ,0.01864624 ,0.02221680 ,0.02429199 , 0.02532959 ,0.02105713 ,0.01281738 ,0.00262451 ,-0.00274658 , -0.00442505 ,-0.00573730 ,-0.00830078 ,-0.01251221 ,-0.01174927 , -0.00613403 ,0.00292969 ,0.01324463 ,0.02160645 ,0.02853394 , 0.02615356 ,0.01580811 ,0.00134277 ,-0.01165771 ,-0.01596069 , -0.01321411 ,-0.01168823 ,-0.01492310 ,-0.01852417 ,-0.01794434 , -0.01525879 ,-0.01065063 ,-0.00509644 ,0.00463867 ,0.01858521 , 0.02600098 ,0.02593994 ,0.01947021 ,0.01080322 ,0.00448608 , 0.00329590 ,0.00613403 ,0.00936890 ,0.00668335 ,0.00439453 , 0.00378418 ,0.00909424 ,0.01446533 ,0.01629639 ,0.01971436 , 0.02322388 ,0.03030396 ,0.03347778 ,0.03219604 ,0.02767944 , 0.02047729 ,0.00946045 ,0.00335693 ,0.00451660 ,0.01016235 , 0.01705933 ,0.02038574 ,0.02062988 ,0.01663208 ,0.01055908 , 0.00811768 ,0.01058960 ,0.01464844 ,0.01446533 ,0.01013184 , -0.00158691 ,-0.01211548 ,-0.01406860 ,-0.00964355 ,-0.00308228 , -0.00305176 ,-0.01101685 ,-0.01898193 ,-0.02502441 ,-0.02847290 , -0.03176880 ,-0.03277588 ,-0.03359985 ,-0.03021240 ,-0.02694702 , -0.02282715 ,-0.02151489 ,-0.01647949 ,-0.01165771 ,-0.00286865 , 0.00378418 ,0.00765991 ,0.00625610 ,0.00158691 ,-0.00491333 , -0.00927734 ,-0.01144409 ,-0.01104736 ,-0.01147461 ,-0.01348877 , -0.01391602 ,-0.00711060 ,0.00497437 ,0.01132202 ,0.01089478 , 0.00399780 ,0.00094604 ,0.00430298 ,0.01068115 ,0.01611328 , 0.01715088 ,0.01480103 ,0.00973511 ,0.00714111 ,0.00244141 , 0.00018311 ,0.00183105 ,0.00561523 ,0.01046753 ,0.01501465 , 0.01724243 ,0.01318359 ,0.00418091 ,-0.00711060 ,-0.01602173 , -0.01809692 ,-0.01818848 ,-0.01785278 ,-0.02160645 ,-0.02404785 , -0.02398682 ,-0.01733398 ,-0.00985718 ,-0.00384521 ,0.00460815 , 0.01467896 ,0.02352905 ,0.02758789 ,0.02456665 ,0.01226807 , 0.00399780 ,-0.00027466 ,0.00183105 ,0.00717163 ,0.01110840 , 0.01000977 ,0.00662231 ,0.00189209 ,0.00418091 ,0.01278687 , 0.02474976 ,0.03100586 ,0.02966309 ,0.02239990 ,0.01434326 , 0.00942993 ,0.00738525 ,0.00537109 ,0.00378418 ,0.00234985 , 0.00079346 ,0.00225830 ,0.00585938 ,0.01184082 ,0.01531982 , 0.02017212 ,0.02655029 ,0.03387451 ,0.03778076 ,0.03720093 , 0.03030396 ,0.01922607 ,0.00930786 ,0.00561523 ,0.00750732 , 0.00994873 ,0.00808716 ,0.00192261 ,-0.00512695 ,-0.00732422 , -0.00546265 ,-0.00680542 ,-0.01245117 ,-0.01828003 ,-0.02041626 , -0.01852417 ,-0.01770020 ,-0.01651001 ,-0.01696777 ,-0.01696777 , -0.01391602 ,-0.01025391 ,-0.00860596 ,-0.00848389 ,-0.01098633 , -0.01608276 ,-0.02169800 ,-0.02539062 ,-0.02386475 ,-0.02044678 , -0.01547241 ,-0.00906372 ,-0.00149536 ,0.00607300 ,0.01275635 , 0.01544189 ,0.01315308 ,0.00909424 ,0.00894165 ,0.01101685 , 0.01214600 ,0.00970459 ,0.00512695 ,0.00161743 ,0.00250244 , 0.00933838 ,0.02075195 ,0.03033447 ,0.03634644 ,0.03607178 , 0.03430176 ,0.03289795 ,0.03231812 ,0.02951050 ,0.02832031 , 0.03005981 ,0.02987671 ,0.02847290 ,0.02490234 ,0.01751709 , 0.01403809 ,0.01245117 ,0.01522827 ,0.02056885 ,0.02233887 , 0.02011108 ,0.01406860 ,0.00973511 ,0.00619507 ,0.00378418 , 0.00247192 ,0.00225830 ,0.00103760 ,0.00070190 ,0.00137329 , 0.00091553 ,-0.00689697 ,-0.01763916 ,-0.02435303 ,-0.02267456 , -0.01373291 ,-0.00146484 ,0.00518799 ,0.00851440 ,0.01345825 , 0.02310181 ,0.03500366 ,0.04132080 ,0.03973389 ,0.03298950 , 0.02810669 ,0.02505493 ,0.02899170 ,0.03244019 ,0.03164673 , 0.02429199 ,0.01647949 ,0.01281738 ,0.01586914 ,0.02331543 , 0.02801514 ,0.02456665 ,0.01666260 ,0.00958252 ,0.00292969 , -0.00463867 ,-0.00967407 ,-0.00997925 ,-0.00701904 ,-0.00762939 , -0.01309204 ,-0.02255249 ,-0.03067017 ,-0.03369141 ,-0.03140259 , -0.02645874 ,-0.02035522 ,-0.01467896 ,-0.00979614 ,-0.00567627 , -0.00866699 ,-0.01428223 ,-0.01895142 ,-0.01947021 ,-0.01544189 , -0.01187134 ,-0.01113892 ,-0.01220703 ,-0.01470947 ,-0.01187134 , -0.00845337 ,-0.00274658 ,0.00048828 ,0.00744629 ,0.01464844 , 0.01895142 ,0.02044678 ,0.01956177 ,0.01965332 ,0.01998901 , 0.02249146 ,0.02474976 ,0.02880859 ,0.02951050 ,0.02420044 , 0.01446533 ,0.00775146 ,0.00625610 ,0.01287842 ,0.02246094 , 0.02844238 ,0.02484131 ,0.01654053 ,0.01019287 ,0.00708008 , 0.00607300 ,0.00247192 ,-0.00048828 ,-0.00045776 ,0.00131226 , 0.00106812 ,0.00000000 ,-0.00192261 ,0.00036621 ,-0.00051880 , -0.00308228 ,-0.00439453 ,-0.00088501 ,0.00573730 ,0.01754761 , 0.02349854 ,0.02362061 ,0.02182007 ,0.01803589 ,0.01870728 , 0.01632690 ,0.01330566 ,0.00503540 ,-0.00091553 ,-0.00448608 , -0.00399780 ,-0.00393677 ,0.00103760 ,0.01049805 ,0.02078247 , 0.02917480 ,0.03179932 ,0.03002930 ,0.02142334 ,0.00637817 , -0.00762939 ,-0.01507568 ,-0.01162720 ,-0.00515747 ,0.00009155 , 0.00030518 ,-0.00433350 ,-0.00924683 ,-0.01669312 ,-0.02462769 , -0.02789307 ,-0.02764893 ,-0.02410889 ,-0.02050781 ,-0.01541138 , -0.01452637 ,-0.01440430 ,-0.01760864 ,-0.01904297 ,-0.01586914 , -0.00918579 ,-0.00747681 ,-0.00997925 ,-0.01824951 ,-0.02395630 , -0.02569580 ,-0.01770020 ,-0.00564575 ,0.00228882 ,0.00543213 , 0.00292969 ,0.00119019 ,0.00357056 ,0.00357056 ,0.00357056 , -0.00024414 ,-0.00308228 ,0.00106812 ,0.00753784 ,0.01251221 , 0.00857544 ,-0.00448608 ,-0.01919556 ,-0.02728271 ,-0.02349854 , -0.01345825 ,-0.00149536 ,0.00805664 ,0.01232910 ,0.01126099 , 0.00680542 ,0.00054932 ,-0.00552368 ,-0.01080322 ,-0.01348877 , -0.01290894 ,-0.01113892 ,-0.00662231 ,-0.00799561 ,-0.00854492 , -0.00802612 ,-0.00207520 ,0.00360107 ,0.00833130 ,0.00756836 , 0.00704956 ,0.00622559 ,0.01165771 ,0.01681519 ,0.02456665 , 0.02850342 ,0.02801514 ,0.02490234 ,0.02514648 ,0.02606201 , 0.02545166 ,0.02154541 ,0.01184082 ,0.00375366 ,0.00100708 , 0.00177002 ,0.00900269 ,0.01617432 ,0.01837158 ,0.01284790 , -0.00317383 ,-0.01626587 ,-0.02233887 ,-0.02114868 ,-0.01733398 , -0.01547241 ,-0.01602173 ,-0.01611328 ,-0.01583862 ,-0.01531982 , -0.01461792 ,-0.01452637 ,-0.01287842 ,-0.01052856 ,-0.00695801 , -0.00070190 ,0.00543213 ,0.01281738 ,0.01473999 ,0.01458740 , 0.01754761 ,0.02285767 ,0.02774048 ,0.02386475 ,0.01232910 , -0.00350952 ,-0.01251221 ,-0.01452637 ,-0.00564575 ,0.00451660 , 0.01040649 ,0.00933838 ,0.00582886 ,-0.00067139 ,-0.00863647 , -0.01553345 ,-0.02258301 ,-0.02554321 ,-0.02603149 ,-0.02249146 , -0.01559448 ,-0.01226807 ,-0.01245117 ,-0.01510620 ,-0.01937866 , -0.02182007 ,-0.01910400 ,-0.01187134 ,-0.00585938 ,0.00155640 , 0.00769043 ,0.01443481 ,0.02160645 ,0.02673340 ,0.02920532 , 0.02713013 ,0.01779175 ,0.00119019 ,-0.01495361 ,-0.02371216 , -0.02166748 ,-0.00894165 ,0.00500488 ,0.01596069 ,0.02059937 , 0.02133179 ,0.01858521 ,0.01504517 ,0.00991821 ,0.00393677 , -0.00030518 ,-0.00341797 ,-0.00491333 ,-0.00317383 ,0.00274658 , 0.01104736 ,0.01757812 ,0.02423096 ,0.03015137 ,0.03121948 , 0.02883911 ,0.02301025 ,0.01330566 ,0.00698853 ,0.00634766 , 0.01583862 ,0.02743530 ,0.02938843 ,0.02301025 ,0.01281738 , 0.00399780 ,-0.00131226 ,-0.00732422 ,-0.01306152 ,-0.01882935 , -0.01507568 ,-0.00122070 ,0.01779175 ,0.02993774 ,0.02868652 , 0.01986694 ,0.01077271 ,0.00662231 ,0.01071167 ,0.01489258 , 0.01104736 ,0.00253296 ,-0.00427246 ,-0.00567627 ,-0.00051880 , 0.00592041 ,0.01150513 ,0.01077271 ,0.00823975 ,0.00259399 , -0.00350952 ,-0.00854492 ,-0.01092529 ,-0.00778198 ,-0.00283813 , 0.00222778 ,0.00100708 ,-0.00552368 ,-0.01361084 ,-0.01785278 , -0.01757812 ,-0.01245117 ,-0.00918579 ,-0.00476074 ,-0.00158691 , 0.00155640 ,0.00524902 ,0.00671387 ,0.00656128 ,0.00338745 , 0.00018311 ,-0.00259399 ,-0.00729370 ,-0.01049805 ,-0.01232910 , -0.00964355 ,-0.00915527 ,-0.00805664 ,-0.00662231 ,-0.00445557 , -0.00247192 ,-0.00268555 ,-0.00350952 ,-0.00628662 ,-0.00833130 , -0.00781250 ,-0.00585938 ,-0.00711060 ,-0.00915527 ,-0.01156616 , -0.01321411 ,-0.01226807 ,-0.00845337 ,-0.00302124 ,0.00347900 , 0.00881958 ,0.01248169 ,0.01257324 ,0.01318359 ,0.01538086 , 0.01824951 ,0.02075195 ,0.01879883 ,0.01641846 ,0.01843262 , 0.02206421 ,0.02230835 ,0.01773071 ,0.01190186 ,0.01187134 , 0.01751709 ,0.02160645 ,0.01864624 ,0.01034546 ,0.00085449 , -0.00128174 ,0.00195312 ,0.00885010 ,0.01324463 ,0.01086426 , 0.00762939 ,0.00537109 ,0.00405884 ,0.00231934 ,-0.00152588 , -0.00329590 ,-0.00482178 ,-0.00384521 ,-0.00186157 ,0.00146484 , 0.00189209 ,0.00030518 ,-0.00082397 ,0.00036621 ,-0.00173950 , -0.00732422 ,-0.01095581 ,-0.00958252 ,-0.00341797 ,0.00204468 , 0.00451660 ,0.00076294 ,-0.00476074 ,-0.00814819 ,-0.00787354 , -0.00851440 ,-0.00476074 ,-0.00390625 ,-0.00442505 ,-0.00341797 , 0.00057983 ,0.00744629 ,0.01165771 ,0.01113892 ,0.00744629 , 0.00427246 ,-0.00082397 ,-0.00521851 ,-0.00619507 ,-0.00198364 , 0.00769043 ,0.01681519 ,0.01943970 ,0.01541138 ,0.00500488 , -0.00228882 ,-0.00723267 ,-0.00973511 ,-0.00842285 ,-0.00704956 , -0.00460815 ,-0.00234985 ,0.00213623 ,0.00552368 ,0.00628662 , 0.00454712 ,0.00097656 ,0.00250244 ,0.00817871 ,0.01403809 , 0.01724243 ,0.01605225 ,0.01165771 ,0.00732422 ,0.00604248 , 0.00650024 ,0.00912476 ,0.00985718 ,0.00775146 ,0.00604248 , 0.00640869 ,0.00701904 ,0.00784302 ,0.01016235 ,0.01004028 , 0.00631714 ,0.00338745 ,0.00192261 ,0.00314331 ,0.00921631 , 0.01348877 ,0.01190186 ,0.00408936 ,-0.00463867 ,-0.01208496 , -0.01373291 ,-0.00814819 ,-0.00079346 ,0.00756836 ,0.01409912 , 0.01522827 ,0.01126099 ,0.00650024 ,0.00378418 ,0.00482178 , 0.00921631 ,0.01062012 ,0.01010132 ,0.00756836 ,0.00213623 , -0.00302124 ,-0.00323486 ,0.00268555 ,0.01031494 ,0.01119995 , 0.00479126 ,-0.00314331 ,-0.00607300 ,-0.00405884 ,-0.00018311 , -0.00152588 ,-0.00381470 ,-0.00317383 ,-0.00195312 ,0.00234985 , 0.00473022 ,-0.00051880 ,-0.01074219 ,-0.02328491 ,-0.03060913 , -0.03283691 ,-0.02774048 ,-0.02151489 ,-0.01821899 ,-0.01617432 , -0.01229858 ,-0.00909424 ,-0.00595093 ,-0.00506592 ,-0.00515747 , -0.00268555 ,-0.00402832 ,-0.00564575 ,-0.01077271 ,-0.01632690 , -0.01889038 ,-0.01739502 ,-0.01385498 ,-0.00891113 ,-0.00753784 , -0.00656128 ,-0.00579834 ,-0.00869751 ,-0.01132202 ,-0.01266479 , -0.01095581 ,-0.00839233 ,-0.00326538 ,0.00064087 ,0.00283813 , 0.00390625 ,0.00451660 ,0.00622559 ,0.00543213 ,0.00378418 , 0.00167847 ,-0.00201416 ,-0.00268555 ,-0.00442505 ,-0.00476074 , -0.00076294 ,0.00317383 ,0.00497437 ,0.00762939 ,0.00869751 , 0.01013184 ,0.00857544 ,0.00277710 ,-0.00354004 ,-0.00579834 , 0.00207520 ,0.01199341 ,0.01818848 ,0.01522827 ,0.01147461 , 0.00750732 ,0.00762939 ,0.00857544 ,0.01046753 ,0.01385498 , 0.01821899 ,0.02145386 ,0.01782227 ,0.01290894 ,0.00625610 , 0.00079346 ,-0.00241089 ,-0.00372314 ,-0.00366211 ,-0.00219727 , 0.00061035 ,0.00537109 ,0.00897217 ,0.00885010 ,0.00357056 , -0.00369263 ,-0.00802612 ,-0.00592041 ,0.00122070 ,0.00772095 , 0.00900269 ,0.00570679 ,-0.00094604 ,-0.00445557 ,-0.00149536 , 0.00454712 ,0.01016235 ,0.01199341 ,0.00741577 ,-0.00241089 , -0.00970459 ,-0.00994873 ,-0.00579834 ,-0.00341797 ,-0.00375366 , -0.00946045 ,-0.01293945 ,-0.00878906 ,0.00137329 ,0.01293945 , 0.01797485 ,0.01925659 ,0.01812744 ,0.01840210 ,0.02191162 , 0.02700806 ,0.02716064 ,0.02719116 ,0.02331543 ,0.01821899 , 0.01345825 ,0.00906372 ,0.00781250 ,0.00921631 ,0.01184082 , 0.01165771 ,0.01205444 ,0.01126099 ,0.01107788 ,0.01007080 , 0.00640869 ,0.00427246 ,-0.00033569 ,-0.00695801 ,-0.01119995 , -0.01461792 ,-0.01312256 ,-0.00848389 ,-0.00384521 ,-0.00042725 , 0.00140381 ,0.00155640 ,0.00106812 ,0.00411987 ,0.00619507 , 0.00814819 ,0.00958252 ,0.01077271 ,0.01553345 ,0.02261353 , 0.03192139 ,0.03686523 ,0.03890991 ,0.03167725 ,0.02203369 , 0.01263428 ,0.00921631 ,0.01043701 ,0.01257324 ,0.01547241 , 0.01834106 ,0.02105713 ,0.02130127 ,0.02252197 ,0.02175903 , 0.01791382 ,0.01324463 ,0.01013184 ,0.00885010 ,0.00750732 , 0.00650024 ,0.00961304 ,0.00964355 ,0.01019287 ,0.00939941 , 0.01016235 ,0.01214600 ,0.01464844 ,0.01687622 ,0.01544189 , 0.00994873 ,0.00225830 ,-0.00286865 ,-0.00845337 ,-0.00814819 , -0.00561523 ,-0.00070190 ,0.00094604 ,-0.00381470 ,-0.01202393 , -0.02014160 ,-0.02328491 ,-0.01892090 ,-0.01278687 ,-0.00762939 , -0.00915527 ,-0.01098633 ,-0.01184082 ,-0.00704956 ,-0.00784302 , -0.01214600 ,-0.02365112 ,-0.03280640 ,-0.03567505 ,-0.03152466 , -0.02551270 ,-0.02163696 ,-0.01962280 ,-0.01831055 ,-0.01486206 , -0.00811768 ,-0.00332642 ,0.00079346 ,0.00210571 ,0.00292969 , 0.00064087 ,-0.00479126 ,-0.00851440 ,-0.00857544 ,-0.00314331 , 0.00643921 ,0.01129150 ,0.00863647 ,0.00119019 ,-0.00997925 , -0.01379395 ,-0.01049805 ,-0.00268555 ,0.00442505 ,0.00723267 , 0.00476074 ,0.00708008 ,0.00958252 ,0.00781250 ,0.00192261 , -0.00561523 ,-0.01113892 ,-0.01110840 ,-0.00866699 ,-0.00540161 , -0.00308228 ,-0.00320435 ,-0.00473022 ,-0.00393677 ,-0.00262451 , -0.00057983 ,0.00253296 ,0.00839233 ,0.01318359 ,0.01565552 , 0.01617432 ,0.01370239 ,0.00942993 ,0.00588989 ,0.00283813 , 0.00323486 ,0.00244141 ,0.00247192 ,0.00622559 ,0.00891113 , 0.01406860 ,0.01882935 ,0.02096558 ,0.02282715 ,0.02230835 , 0.02426147 ,0.02551270 ,0.02374268 ,0.01794434 ,0.01013184 , 0.00515747 ,0.00503540 ,0.00967407 ,0.01412964 ,0.01638794 , 0.01306152 ,0.01248169 ,0.00973511 ,0.00759888 ,0.00643921 , 0.00915527 ,0.01461792 ,0.01736450 ,0.01647949 ,0.00793457 , 0.00167847 ,0.00039673 ,0.00518799 ,0.00924683 ,0.01043701 , 0.00955200 ,0.00546265 ,-0.00067139 ,-0.00509644 ,-0.00976562 , -0.01431274 ,-0.01705933 ,-0.01638794 ,-0.01327515 ,-0.00772095 , -0.00277710 ,0.00448608 ,0.00650024 ,0.00250244 ,0.00115967 , 0.00073242 ,0.00192261 ,0.00067139 ,-0.00314331 ,-0.00665283 , -0.01077271 ,-0.01177979 ,-0.01296997 ,-0.01104736 ,-0.00469971 , 0.00115967 ,0.00692749 ,0.01141357 ,0.01254272 ,0.01226807 , 0.01141357 ,0.00845337 ,0.00613403 ,0.00323486 ,0.00289917 , 0.00027466 ,0.00042725 ,0.00152588 ,0.00354004 ,0.00915527 , 0.01321411 ,0.01486206 ,0.01516724 ,0.01452637 ,0.01272583 , 0.01171875 ,0.00686646 ,0.00357056 ,0.00366211 ,0.00579834 , 0.00900269 ,0.01464844 ,0.01828003 ,0.02249146 ,0.02319336 , 0.02005005 ,0.01342773 ,0.00601196 ,0.00201416 ,0.00531006 , 0.01147461 ,0.01858521 ,0.02136230 ,0.02084351 ,0.01824951 , 0.01480103 ,0.01129150 ,0.00814819 ,0.00396729 ,-0.00015259 , -0.00338745 ,-0.00555420 ,-0.00473022 ,-0.00360107 ,-0.00280762 , -0.00201416 ,-0.00057983 ,0.00155640 ,0.00280762 ,-0.00064087 , -0.00839233 ,-0.01928711 ,-0.02749634 ,-0.02822876 ,-0.02108765 , -0.00888062 ,0.00442505 ,0.01254272 ,0.01382446 ,0.01193237 , 0.01119995 ,0.00729370 ,0.00192261 ,-0.00512695 ,-0.01428223 , -0.01800537 ,-0.01721191 ,-0.01336670 ,-0.01138306 ,-0.00930786 , -0.01007080 ,-0.01019287 ,-0.01251221 ,-0.01745605 ,-0.02178955 , -0.02581787 ,-0.02401733 ,-0.02124023 ,-0.01651001 ,-0.01495361 , -0.01724243 ,-0.01916504 ,-0.01837158 ,-0.01583862 ,-0.01452637 , -0.01513672 ,-0.01544189 ,-0.01593018 ,-0.01446533 ,-0.01358032 , -0.00903320 ,-0.00357056 ,0.00296021 ,0.00799561 ,0.00985718 , 0.01315308 ,0.01147461 ,0.00827026 ,0.00585938 ,0.00598145 , 0.00814819 ,0.00930786 ,0.00741577 ,0.00085449 ,-0.00823975 , -0.01528931 ,-0.01712036 ,-0.01577759 ,-0.01162720 ,-0.00930786 , -0.00598145 ,-0.00378418 ,-0.00177002 ,0.00320435 ,0.00833130 , 0.01113892 ,0.01321411 ,0.01119995 ,0.00973511 ,0.00775146 , 0.00473022 ,0.00354004 ,0.00476074 ,0.00753784 ,0.01242065 , 0.01879883 ,0.02590942 ,0.02752686 ,0.02355957 ,0.01681519 , 0.00991821 ,0.00790405 ,0.00817871 ,0.01385498 ,0.01620483 , 0.01675415 ,0.01245117 ,0.00692749 ,0.00308228 ,-0.00033569 , -0.00012207 ,0.00091553 ,0.00201416 ,0.00274658 ,0.00320435 , 0.00054932 ,-0.00143433 ,-0.00408936 ,-0.00704956 ,-0.00628662 , -0.00302124 ,0.00067139 ,0.00262451 ,0.00335693 ,0.00170898 , 0.00344849 ,0.00820923 ,0.01205444 ,0.01373291 ,0.01354980 , 0.01171875 ,0.01416016 ,0.01287842 ,0.01165771 ,0.00762939 , 0.00582886 ,0.00689697 ,0.01223755 ,0.01950073 ,0.02322388 , 0.02529907 ,0.02078247 ,0.01254272 ,0.00387573 ,-0.00466919 , -0.01022339 ,-0.00985718 ,-0.00299072 ,0.00595093 ,0.01037598 , 0.00756836 ,0.00109863 ,-0.00677490 ,-0.00946045 ,-0.00378418 , 0.00604248 ,0.01205444 ,0.00921631 ,-0.00033569 ,-0.00833130 , -0.00878906 ,-0.00497437 ,0.00106812 ,0.00350952 ,0.00387573 , 0.00247192 ,-0.00051880 ,-0.00338745 ,-0.00613403 ,-0.00833130 , -0.00567627 ,-0.00082397 ,0.00497437 ,0.00860596 ,0.00949097 , 0.00573730 ,0.00228882 ,0.00000000 ,-0.00109863 ,-0.00122070 , -0.00396729 ,-0.00625610 ,-0.00601196 ,0.00103760 ,0.00823975 , 0.01388550 ,0.01562500 ,0.01422119 ,0.01306152 ,0.01263428 , 0.01043701 ,0.00250244 ,-0.00628662 ,-0.01574707 ,-0.02066040 , -0.01873779 ,-0.01077271 ,-0.00152588 ,0.00686646 ,0.01000977 , 0.01498413 ,0.01611328 ,0.01791382 ,0.02069092 ,0.01959229 , 0.01828003 ,0.01800537 ,0.01696777 ,0.01437378 ,0.01220703 , 0.01071167 ,0.00787354 ,0.00384521 ,0.00021362 ,-0.00082397 , -0.00198364 ,-0.00079346 ,-0.00354004 ,-0.00357056 ,-0.00390625 , -0.00439453 ,-0.00286865 ,-0.00091553 ,0.00036621 ,0.00039673 , 0.00097656 ,-0.00323486 ,-0.00878906 ,-0.01138306 ,-0.01254272 , -0.00881958 ,-0.00408936 ,-0.00140381 ,-0.00115967 ,-0.00256348 , -0.00622559 ,-0.00692749 ,-0.00466919 ,-0.00152588 ,-0.00170898 , -0.00521851 ,-0.01223755 ,-0.01733398 ,-0.02215576 ,-0.02600098 , -0.02609253 ,-0.02288818 ,-0.02279663 ,-0.02041626 ,-0.01763916 , -0.01257324 ,-0.00738525 ,-0.00366211 ,-0.00219727 ,-0.00219727 , -0.00073242 ,0.00018311 ,0.00283813 ,0.00479126 ,0.00781250 , 0.00909424 ,0.01239014 ,0.01663208 ,0.02148438 ,0.02230835 , 0.02252197 ,0.02413940 ,0.02709961 ,0.03222656 ,0.03604126 , 0.03741455 ,0.03579712 ,0.03033447 ,0.02621460 ,0.02612305 , 0.02734375 ,0.02899170 ,0.02841187 ,0.02221680 ,0.01580811 , 0.00823975 ,0.00292969 ,0.00177002 ,0.00219727 ,0.00552368 , 0.00656128 ,0.00674438 ,0.00585938 ,-0.00024414 ,-0.01132202 , -0.01940918 ,-0.02291870 ,-0.02072144 ,-0.01397705 ,-0.00909424 , -0.00872803 ,-0.00967407 ,-0.00833130 ,-0.00445557 ,0.00119019 , 0.00726318 ,0.00961304 ,0.01296997 ,0.01480103 ,0.01565552 , 0.01660156 ,0.01586914 ,0.01416016 ,0.01321411 ,0.01275635 , 0.01367188 ,0.01757812 ,0.02032471 ,0.02056885 ,0.01986694 , 0.01855469 ,0.01455688 ,0.01086426 ,0.00631714 ,0.00128174 , -0.00225830 ,-0.00585938 ,-0.00738525 ,-0.00747681 ,-0.00857544 , -0.01007080 ,-0.01419067 ,-0.01904297 ,-0.02462769 ,-0.02319336 , -0.01800537 ,-0.01022339 ,-0.00555420 ,-0.00451660 ,-0.00628662 , -0.00827026 ,-0.01040649 ,-0.01181030 ,-0.00811768 ,-0.00350952 , 0.00509644 ,0.00885010 ,0.00936890 ,0.00253296 ,-0.00622559 , -0.01373291 ,-0.01599121 ,-0.01428223 ,-0.01422119 ,-0.01443481 , -0.01403809 ,-0.01354980 ,-0.00836182 ,-0.00210571 ,0.00570679 , 0.01461792 ,0.01751709 ,0.01705933 ,0.01190186 ,0.00930786 , 0.00946045 ,0.01220703 ,0.01452637 ,0.01654053 ,0.01748657 , 0.01736450 ,0.01541138 ,0.01599121 ,0.01556396 ,0.01751709 , 0.02029419 ,0.02056885 ,0.01965332 ,0.01672363 ,0.01458740 , 0.01361084 ,0.00769043 ,0.00164795 ,-0.00512695 ,-0.01211548 , -0.01507568 ,-0.01284790 ,-0.00839233 ,-0.00335693 ,-0.00094604 , -0.00173950 ,-0.00357056 ,-0.00338745 ,-0.00253296 ,0.00216675 , 0.00476074 ,0.00665283 ,0.00796509 ,0.00741577 ,0.00521851 , -0.00128174 ,-0.00765991 ,-0.01306152 ,-0.01647949 ,-0.01803589 , -0.01586914 ,-0.01315308 ,-0.01196289 ,-0.01174927 ,-0.00991821 , -0.00540161 ,0.00228882 ,0.00543213 ,0.00347900 ,-0.00003052 , -0.00201416 ,-0.00277710 ,-0.00500488 ,-0.00677490 ,-0.00851440 , -0.00900269 ,-0.00863647 ,-0.00555420 ,-0.00308228 ,-0.00167847 , -0.00210571 ,-0.00067139 ,-0.00033569 ,-0.00024414 ,-0.00509644 , -0.01281738 ,-0.02032471 ,-0.02337646 ,-0.02169800 ,-0.01437378 , -0.00582886 ,0.00122070 ,0.00399780 ,0.00286865 ,-0.00033569 , -0.00234985 ,-0.00277710 ,-0.00360107 ,-0.00424194 ,-0.00485229 , -0.00323486 ,-0.00280762 ,0.00006104 ,0.00506592 ,0.00857544 , 0.00942993 ,0.00921631 ,0.00775146 ,0.00317383 ,0.00415039 , 0.00622559 ,0.00988770 ,0.01214600 ,0.01455688 ,0.01309204 , 0.01159668 ,0.01046753 ,0.01144409 ,0.00973511 ,0.00842285 , 0.00296021 ,0.00143433 ,0.00085449 ,0.00433350 ,0.00735474 , 0.00747681 ,0.00686646 ,0.00619507 ,0.00677490 ,0.00964355 , 0.01248169 ,0.01586914 ,0.01782227 ,0.01947021 ,0.02069092 , 0.02008057 ,0.01803589 ,0.01583862 ,0.01501465 ,0.01202393 , 0.01113892 ,0.00970459 ,0.00848389 ,0.00961304 ,0.01104736 , 0.01303101 ,0.01583862 ,0.01675415 ,0.01483154 ,0.01223755 , 0.01278687 ,0.01303101 ,0.01312256 ,0.01126099 ,0.00445557 , -0.00418091 ,-0.01190186 ,-0.01647949 ,-0.01721191 ,-0.01712036 , -0.01724243 ,-0.01464844 ,-0.00827026 ,-0.00292969 ,0.00128174 , 0.00335693 ,0.00357056 ,0.00521851 ,0.00778198 ,0.01181030 , 0.01589966 ,0.01599121 ,0.01330566 ,0.01132202 ,0.01071167 , 0.00979614 ,0.00967407 ,0.01101685 ,0.01300049 ,0.01638794 , 0.02044678 ,0.02081299 ,0.01766968 ,0.01293945 ,0.00982666 , 0.00735474 ,0.00680542 ,0.00741577 ,0.00671387 ,0.00695801 , 0.00357056 ,-0.00158691 ,-0.00878906 ,-0.01379395 ,-0.01473999 , -0.01406860 ,-0.01339722 ,-0.01184082 ,-0.01281738 ,-0.01394653 , -0.01248169 ,-0.00863647 ,-0.00827026 ,-0.00918579 ,-0.01281738 , -0.01315308 ,-0.01217651 ,-0.01000977 ,-0.01007080 ,-0.00915527 , -0.00885010 ,-0.00875854 ,-0.00506592 ,-0.00161743 ,0.00210571 , 0.00454712 ,0.00512695 ,0.00494385 ,0.00534058 ,0.00335693 , 0.00201416 ,0.00170898 ,0.00503540 ,0.00726318 ,0.00778198 , 0.00125122 ,-0.00265503 ,-0.00317383 ,0.00375366 ,0.01251221 , 0.02117920 ,0.02453613 ,0.02221680 ,0.02209473 ,0.02160645 , 0.02273560 ,0.02243042 ,0.02102661 ,0.02127075 ,0.02197266 , 0.01959229 ,0.01776123 ,0.01324463 ,0.00634766 ,0.00469971 , 0.00354004 ,0.00585938 ,0.00543213 ,0.00357056 ,-0.00454712 , -0.01211548 ,-0.01742554 ,-0.01693726 ,-0.01416016 ,-0.01040649 , -0.00842285 ,-0.01150513 ,-0.01470947 ,-0.01843262 ,-0.02282715 , -0.02261353 ,-0.02084351 ,-0.01480103 ,-0.01187134 ,-0.00872803 , -0.00726318 ,-0.00854492 ,-0.00894165 ,-0.00772095 ,-0.01022339 , -0.01342773 ,-0.01510620 ,-0.01489258 ,-0.01223755 ,-0.01049805 , -0.01031494 ,-0.00927734 ,-0.00790405 ,-0.00643921 ,-0.00399780 , -0.00143433 ,0.00164795 ,0.00357056 ,0.00054932 ,-0.00146484 , -0.00091553 ,-0.00106812 ,0.00076294 ,0.00100708 ,0.00100708 , -0.00018311 ,0.00103760 ,0.00213623 ,0.00524902 ,0.00527954 , 0.00625610 ,0.00695801 ,0.00836182 ,0.00765991 ,0.00592041 , 0.00326538 ,0.00015259 ,0.00064087 ,-0.00125122 ,-0.00308228 , -0.00469971 ,-0.00329590 ,0.00088501 ,0.00521851 ,0.00848389 , 0.00726318 ,0.00531006 ,0.00244141 ,0.00103760 ,0.00213623 , 0.00354004 ,0.00897217 ,0.01412964 ,0.01870728 ,0.02105713 , 0.01794434 ,0.01510620 ,0.01425171 ,0.01422119 ,0.01391602 , 0.01431274 ,0.01568604 ,0.01708984 ,0.01812744 ,0.01586914 , 0.01535034 ,0.01498413 ,0.01437378 ,0.01409912 ,0.01242065 , 0.00631714 ,0.00402832 ,0.00283813 ,0.00579834 ,0.00711060 , 0.00607300 ,0.00436401 ,0.00314331 ,0.00540161 ,0.01022339 , 0.01049805 ,0.00689697 ,0.00222778 ,-0.00247192 ,-0.00665283 , -0.00576782 ,-0.00256348 ,0.00170898 ,0.00643921 ,0.00717163 , 0.00244141 ,-0.00631714 ,-0.01171875 ,-0.01504517 ,-0.01367188 , -0.01327515 ,-0.00942993 ,-0.00497437 ,-0.00155640 ,-0.00091553 , -0.00482178 ,-0.00711060 ,-0.00585938 ,-0.00469971 ,0.00021362 , -0.00045776 ,-0.00347900 ,-0.00762939 ,-0.01049805 ,-0.01156616 , -0.01239014 ,-0.01031494 ,-0.01062012 ,-0.00967407 ,-0.00869751 , -0.00994873 ,-0.01132202 ,-0.01165771 ,-0.01367188 ,-0.01443481 , -0.01388550 ,-0.01391602 ,-0.01144409 ,-0.00921631 ,-0.00256348 , 0.00045776 ,0.00277710 ,0.00189209 ,0.00323486 ,0.00308228 , 0.00476074 ,0.00582886 ,0.00775146 ,0.00787354 ,0.00894165 , 0.00982666 ,0.01019287 ,0.01171875 ,0.01257324 ,0.01388550 , 0.01239014 ,0.01016235 ,0.00756836 ,0.00643921 ,0.00527954 , 0.00698853 ,0.00741577 ,0.00518799 ,0.00491333 ,0.00524902 , 0.00686646 ,0.00906372 ,0.01028442 ,0.00976562 ,0.01043701 , 0.00683594 ,0.00268555 ,0.00125122 ,0.00204468 ,0.00369263 , 0.00491333 ,0.00393677 ,0.00296021 ,0.00070190 ,0.00448608 , 0.00769043 ,0.01089478 ,0.01184082 ,0.00921631 ,0.00482178 , -0.00006104 ,-0.00506592 ,-0.00750732 ,-0.00756836 ,-0.00524902 , -0.00375366 ,-0.00225830 ,-0.00561523 ,-0.00796509 ,-0.00732422 , -0.00476074 ,-0.00222778 ,-0.00323486 ,-0.00668335 ,-0.00894165 , -0.01052856 ,-0.01052856 ,-0.00854492 ,-0.00723267 ,-0.00512695 , -0.00039673 ,0.00280762 ,0.00372314 ,0.00567627 ,0.00369263 , 0.00216675 ,0.00027466 ,-0.00317383 ,-0.00155640 ,-0.00009155 , 0.00485229 ,0.00784302 ,0.01205444 ,0.01416016 ,0.01351929 , 0.01226807 ,0.00976562 ,0.00897217 ,0.00939941 ,0.01083374 , 0.01278687 ,0.01364136 ,0.01568604 ,0.01306152 ,0.01196289 , 0.01058960 ,0.00939941 ,0.00903320 ,0.00558472 ,0.00344849 , -0.00045776 ,-0.00167847 ,-0.00009155 ,0.00357056 ,0.00527954 , 0.00524902 ,0.00329590 ,-0.00119019 ,-0.00357056 ,-0.00518799 , -0.00253296 ,-0.00158691 ,0.00125122 ,0.00018311 ,-0.00015259 , 0.00131226 ,0.00436401 ,0.00585938 ,0.00848389 ,0.01052856 , 0.01144409 ,0.01196289 ,0.01068115 ,0.01037598 ,0.01092529 , 0.01315308 ,0.01635742 ,0.01571655 ,0.01412964 ,0.01046753 , 0.00711060 ,0.00491333 ,0.00625610 ,0.00778198 ,0.00769043 , 0.00708008 ,0.00750732 ,0.00723267 ,0.00833130 ,0.00891113 , 0.00772095 ,0.00839233 ,0.01123047 ,0.01290894 ,0.01449585 , 0.01248169 ,0.00863647 ,0.00527954 ,0.00363159 ,0.00372314 , 0.00503540 ,0.00634766 ,0.00357056 ,-0.00134277 ,-0.00527954 , -0.01040649 ,-0.01409912 ,-0.01147461 ,-0.00677490 ,-0.00100708 , 0.00024414 ,-0.00216675 ,-0.00802612 ,-0.01113892 ,-0.01358032 , -0.01464844 ,-0.01553345 ,-0.01593018 ,-0.01528931 ,-0.01132202 , -0.00744629 ,-0.00415039 ,-0.00296021 ,-0.00170898 ,-0.00521851 , -0.01013184 ,-0.01605225 ,-0.01834106 ,-0.01821899 ,-0.01867676 , -0.01748657 ,-0.01885986 ,-0.01931763 ,-0.02081299 ,-0.02072144 , -0.01931763 ,-0.01489258 ,-0.01162720 ,-0.00759888 ,-0.00820923 , -0.00933838 ,-0.01034546 ,-0.00909424 ,-0.00695801 ,-0.00369263 , -0.00433350 ,-0.00436401 ,-0.00424194 ,-0.00320435 ,-0.00152588 , 0.00094604 ,0.00103760 ,0.00094604 ,-0.00009155 ,0.00006104 , -0.00192261 ,-0.00314331 ,-0.00259399 ,-0.00045776 ,0.00262451 , 0.00457764 ,0.00317383 ,0.00164795 ,0.00109863 ,0.00158691 , 0.00274658 ,0.00299072 ,0.00186157 ,-0.00128174 ,-0.00012207 , 0.00140381 ,0.00610352 ,0.00787354 ,0.00857544 ,0.00427246 , -0.00323486 ,-0.00714111 ,-0.00994873 ,-0.00784302 ,-0.00720215 , -0.00393677 ,-0.00286865 ,-0.00244141 ,-0.00500488 ,-0.00640869 , -0.01199341 ,-0.01528931 ,-0.01852417 ,-0.01708984 ,-0.01519775 , -0.01507568 ,-0.01705933 ,-0.01611328 ,-0.01211548 ,-0.00735474 , -0.00149536 ,-0.00012207 ,-0.00204468 ,-0.00451660 ,-0.00558472 , -0.00674438 ,-0.00549316 ,-0.00555420 ,-0.00268555 ,-0.00003052 , -0.00033569 ,0.00244141 ,0.00469971 ,0.00613403 ,0.00418091 , 0.00192261 ,0.00067139 ,-0.00073242 ,-0.00085449 ,-0.00131226 , -0.00006104 ,0.00149536 ,0.00573730 ,0.00997925 ,0.01339722 , 0.01617432 ,0.01510620 ,0.01089478 ,0.00912476 ,0.00616455 , 0.00347900 ,0.00234985 ,0.00225830 ,0.00537109 ,0.00802612 , 0.00558472 ,0.00451660 ,-0.00003052 ,-0.00057983 ,0.00079346 , 0.00180054 ,0.00326538 ,0.00436401 ,0.00775146 ,0.00994873 , 0.01458740 ,0.01663208 ,0.01898193 ,0.01928711 ,0.01766968 , 0.01461792 ,0.00891113 ,0.00827026 ,0.01110840 ,0.01556396 , 0.02084351 ,0.02169800 ,0.02227783 ,0.02096558 ,0.01913452 , 0.01803589 ,0.01446533 ,0.00750732 ,0.00384521 ,0.00057983 , 0.00082397 ,0.00256348 ,0.00665283 ,0.00741577 ,0.00588989 , 0.00228882 ,-0.00256348 ,-0.00509644 ,-0.00650024 ,-0.00103760 , 0.00506592 ,0.01019287 ,0.01058960 ,0.01031494 ,0.01040649 , 0.01126099 ,0.01406860 ,0.01580811 ,0.01638794 ,0.01654053 , 0.01568604 ,0.01199341 ,0.00723267 ,0.00173950 ,-0.00280762 , -0.00341797 ,-0.00448608 ,-0.00125122 ,0.00070190 ,0.00222778 , 0.00216675 ,-0.00146484 ,-0.00469971 ,-0.01062012 ,-0.01306152 , -0.01306152 ,-0.01235962 ,-0.01257324 ,-0.01406860 ,-0.01672363 , -0.01910400 ,-0.01925659 ,-0.01864624 ,-0.01754761 ,-0.01675415 , -0.01525879 ,-0.01409912 ,-0.01327515 ,-0.01110840 ,-0.00869751 , -0.00457764 ,-0.00244141 ,-0.00408936 ,-0.00729370 ,-0.01126099 , -0.01535034 ,-0.01733398 ,-0.01791382 ,-0.01580811 ,-0.01278687 , -0.00857544 ,-0.00515747 ,-0.00259399 ,0.00079346 ,-0.00015259 , -0.00106812 ,-0.00518799 ,-0.00866699 ,-0.00833130 ,-0.00878906 , -0.00735474 ,-0.00729370 ,-0.00561523 ,-0.00421143 ,-0.00042725 , 0.00006104 ,0.00186157 ,-0.00009155 ,-0.00119019 ,0.00097656 , 0.00289917 ,0.00408936 ,0.00442505 ,0.00491333 ,0.00466919 , 0.00677490 ,0.00875854 ,0.00747681 ,0.00570679 ,0.00442505 , 0.00363159 ,0.00686646 ,0.00949097 ,0.01217651 ,0.01272583 , 0.01174927 ,0.00930786 ,0.00680542 ,0.00402832 ,0.00207520 , -0.00100708 ,-0.00308228 ,-0.00479126 ,-0.00588989 ,-0.00598145 , -0.00546265 ,-0.00192261 ,0.00073242 ,-0.00036621 ,-0.00173950 , -0.00366211 ,-0.00631714 ,-0.00366211 ,-0.00021362 ,0.00277710 , 0.00555420 ,0.00674438 ,0.00842285 ,0.00851440 ,0.00903320 , 0.00827026 ,0.00402832 ,0.00039673 ,-0.00152588 ,-0.00253296 , -0.00134277 ,0.00103760 ,0.00335693 ,0.00790405 ,0.00857544 , 0.00891113 ,0.00820923 ,0.00897217 ,0.01071167 ,0.01181030 , 0.00991821 ,0.00778198 ,0.00537109 ,0.00305176 ,0.00198364 , -0.00048828 ,0.00006104 ,0.00164795 ,0.00161743 ,0.00039673 , -0.00405884 ,-0.00820923 ,-0.01104736 ,-0.00842285 ,-0.00402832 , 0.00085449 ,0.00222778 ,0.00308228 ,0.00131226 ,-0.00128174 , -0.00289917 ,-0.00488281 ,-0.00442505 ,-0.00506592 ,-0.00201416 , 0.00183105 ,0.00704956 ,0.01104736 ,0.01171875 ,0.01025391 , 0.00869751 ,0.00805664 ,0.00854492 ,0.01092529 ,0.01150513 , 0.01330566 ,0.01248169 ,0.01016235 ,0.00628662 ,0.00311279 , 0.00247192 ,0.00366211 ,0.00668335 ,0.00952148 ,0.01193237 , 0.01245117 ,0.01257324 ,0.01306152 ,0.01544189 ,0.01806641 , 0.01974487 ,0.02099609 ,0.01934814 ,0.01531982 ,0.01098633 , 0.00390625 ,-0.00238037 ,-0.00402832 ,-0.00338745 ,-0.00320435 , -0.00073242 ,-0.00152588 ,-0.00097656 ,0.00051880 ,0.00225830 , 0.00210571 ,-0.00366211 ,-0.00790405 ,-0.01229858 ,-0.01327515 , -0.01034546 ,-0.00656128 ,-0.00210571 ,-0.00115967 ,-0.00213623 , -0.00369263 ,-0.00662231 ,-0.00802612 ,-0.01150513 ,-0.01211548 , -0.01083374 ,-0.01080322 ,-0.00994873 ,-0.01065063 ,-0.01214600 , -0.01382446 ,-0.01412964 ,-0.01412964 ,-0.01187134 ,-0.01022339 , -0.00747681 ,-0.00875854 ,-0.00860596 ,-0.00686646 ,-0.00503540 , -0.00253296 ,-0.00045776 ,0.00051880 ,-0.00021362 ,-0.00115967 , -0.00360107 ,-0.00723267 ,-0.00781250 ,-0.00924683 ,-0.00942993 , -0.00769043 ,-0.00839233 ,-0.01049805 ,-0.01065063 ,-0.01373291 , -0.01409912 ,-0.01281738 ,-0.01284790 ,-0.01287842 ,-0.01419067 , -0.01718140 ,-0.01696777 ,-0.01773071 ,-0.01654053 ,-0.01620483 , -0.01455688 ,-0.01281738 ,-0.01144409 ,-0.01217651 ,-0.01416016 , -0.01528931 ,-0.01647949 ,-0.01605225 ,-0.01434326 ,-0.01211548 , -0.00909424 ,-0.00973511 ,-0.00891113 ,-0.00866699 ,-0.01141357 , -0.01040649 ,-0.00909424 ,-0.00555420 ,-0.00326538 ,-0.00152588 , -0.00204468 ,0.00051880 ,-0.00006104 ,-0.00018311 ,-0.00061035 , -0.00283813 ,-0.00207520 ,-0.00088501 ,0.00347900 ,0.00689697 , 0.01278687 ,0.01544189 ,0.01733398 ,0.02148438 ,0.02346802 , 0.02456665 ,0.02127075 ,0.01687622 ,0.01242065 ,0.01174927 , 0.01361084 ,0.01696777 ,0.01910400 ,0.01852417 ,0.01770020 , 0.01620483 ,0.01367188 ,0.01290894 ,0.01229858 ,0.01037598 , 0.00973511 ,0.00894165 ,0.00616455 ,0.00476074 ,0.00503540 , 0.00726318 ,0.00857544 ,0.00955200 ,0.00671387 ,0.00360107 , 0.00195312 ,0.00036621 ,0.00109863 ,0.00350952 ,0.00613403 , 0.00616455 ,0.00747681 ,0.00653076 ,0.00610352 ,0.00604248 , 0.00399780 ,0.00326538 ,0.00500488 ,0.00567627 ,0.00839233 , 0.00939941 ,0.01150513 ,0.01196289 ,0.01278687 ,0.01495361 , 0.01403809 ,0.01480103 ,0.01376343 ,0.01330566 ,0.01272583 , 0.01040649 ,0.01193237 ,0.01611328 ,0.01962280 ,0.02203369 , 0.02316284 ,0.02142334 ,0.01812744 ,0.01504517 ,0.01495361 , 0.01574707 ,0.01690674 ,0.01629639 ,0.01547241 ,0.01315308 , 0.01013184 ,0.00787354 ,0.00775146 ,0.00796509 ,0.00790405 , 0.00640869 ,0.00482178 ,0.00582886 ,0.00659180 ,0.00756836 , 0.00860596 ,0.00845337 ,0.00701904 ,0.00637817 ,0.00546265 , 0.00506592 ,0.00418091 ,0.00338745 ,0.00332642 ,0.00366211 , 0.00537109 ,0.00762939 ,0.00726318 ,0.00735474 ,0.00552368 , 0.00448608 ,0.00259399 ,0.00195312 ,0.00161743 ,-0.00061035 , -0.00436401 ,-0.00759888 ,-0.01089478 ,-0.01055908 ,-0.00964355 , -0.00698853 ,-0.00741577 ,-0.00631714 ,-0.00793457 ,-0.00668335 , -0.00387573 ,-0.00424194 ,-0.00363159 ,-0.00411987 ,-0.00656128 , -0.00668335 ,-0.00677490 ,-0.00787354 ,-0.00881958 ,-0.01013184 , -0.01135254 ,-0.01083374 ,-0.01217651 ,-0.01281738 ,-0.01440430 , -0.01571655 ,-0.01705933 ,-0.01901245 ,-0.02072144 ,-0.02096558 , -0.02288818 ,-0.02246094 ,-0.02319336 ,-0.02172852 ,-0.01968384 , -0.01904297 ,-0.01925659 ,-0.02163696 ,-0.02270508 ,-0.02087402 , -0.01837158 ,-0.01852417 ,-0.01879883 ,-0.01947021 ,-0.01788330 , -0.01574707 ,-0.01239014 ,-0.00848389 ,-0.00671387 ,-0.00592041 , -0.00653076 ,-0.01065063 ,-0.01263428 ,-0.01446533 ,-0.01300049 , -0.01037598 ,-0.00665283 ,-0.00439453 ,-0.00155640 ,-0.00024414 , -0.00051880 ,-0.00119019 ,-0.00143433 ,-0.00146484 ,0.00210571 , 0.00421143 ,0.00323486 ,0.00036621 ,-0.00302124 ,-0.00216675 , 0.00006104 ,0.00274658 ,0.00259399 ,0.00064087 ,-0.00167847 , -0.00457764 ,-0.00436401 ,-0.00509644 ,-0.00564575 ,-0.00527954 , -0.00689697 ,-0.00967407 ,-0.00857544 ,-0.00823975 ,-0.00894165 , -0.00595093 ,-0.00271606 ,-0.00180054 ,-0.00363159 ,-0.00503540 , -0.00405884 ,-0.00207520 ,0.00341797 ,0.00872803 ,0.01083374 , 0.01202393 ,0.01113892 ,0.00997925 ,0.00778198 ,0.00558472 , 0.00518799 ,0.00515747 ,0.00552368 ,0.00714111 ,0.00866699 , 0.01071167 ,0.00949097 ,0.00585938 ,0.00155640 ,-0.00216675 , -0.00286865 ,-0.00244141 ,-0.00158691 ,0.00131226 ,0.00607300 , 0.00970459 ,0.01492310 ,0.01641846 ,0.01617432 ,0.01599121 , 0.01562500 ,0.01385498 ,0.01388550 ,0.01492310 ,0.01568604 , 0.01535034 ,0.01531982 ,0.01785278 ,0.01846313 ,0.01913452 , 0.01660156 ,0.01361084 ,0.01016235 ,0.00860596 ,0.00988770 , 0.01034546 ,0.01080322 ,0.01043701 ,0.00717163 ,0.00473022 , 0.00125122 ,-0.00042725 ,-0.00387573 ,-0.00564575 ,-0.00814819 , -0.00924683 ,-0.01052856 ,-0.00979614 ,-0.00711060 ,-0.00503540 , -0.00112915 ,0.00247192 ,0.00695801 ,0.00674438 ,0.00469971 , 0.00299072 ,-0.00051880 ,-0.00094604 ,0.00100708 ,0.00384521 , 0.00610352 ,0.00830078 ,0.00958252 ,0.00769043 ,0.00878906 , 0.00796509 ,0.00717163 ,0.00561523 ,0.00442505 ,0.00399780 , 0.00448608 ,0.00576782 ,0.00781250 ,0.00885010 ,0.00851440 , 0.00738525 ,0.00692749 ,0.00518799 ,0.00479126 ,0.00350952 , 0.00231934 ,0.00256348 ,0.00375366 ,0.00588989 ,0.00732422 , 0.00741577 ,0.00573730 ,0.00561523 ,0.00451660 ,0.00253296 , 0.00109863 ,0.00064087 ,0.00024414 ,0.00219727 ,0.00222778 , 0.00329590 ,0.00097656 ,0.00048828 ,-0.00180054 ,-0.00332642 , -0.00509644 ,-0.00628662 ,-0.00561523 ,-0.00576782 ,-0.00381470 , -0.00222778 ,0.00128174 ,0.00283813 ,0.00198364 ,-0.00131226 , -0.00210571 ,-0.00222778 ,-0.00158691 ,-0.00015259 ,-0.00082397 , -0.00259399 ,-0.00506592 ,-0.00790405 ,-0.00650024 ,-0.00634766 , -0.00485229 ,-0.00399780 ,-0.00378418 ,-0.00418091 ,-0.00341797 , -0.00332642 ,-0.00073242 ,-0.00170898 ,-0.00103760 ,-0.00430298 , -0.00653076 ,-0.00775146 ,-0.00769043 ,-0.00613403 ,-0.00366211 , 0.00024414 ,0.00155640 ,0.00402832 ,0.00070190 ,-0.00326538 , -0.00643921 ,-0.00549316 ,-0.00286865 ,-0.00109863 ,-0.00192261 , -0.00643921 ,-0.00878906 ,-0.00952148 ,-0.01040649 ,-0.00857544 , -0.00802612 ,-0.01171875 ,-0.01275635 ,-0.01217651 ,-0.01022339 , -0.00711060 ,-0.00357056 ,0.00137329 ,0.00695801 ,0.01107788 , 0.01348877 ,0.01522827 ,0.01763916 ,0.01913452 ,0.02062988 , 0.02368164 ,0.02352905 ,0.02584839 ,0.02560425 ,0.02386475 , 0.02236938 ,0.01892090 ,0.01446533 ,0.01336670 ,0.01260376 , 0.01480103 ,0.01446533 ,0.01269531 ,0.00854492 ,0.00756836 , 0.00695801 ,0.01004028 ,0.01257324 ,0.01260376 ,0.01205444 , 0.01019287 ,0.00674438 ,0.00241089 ,0.00140381 ,0.00354004 , 0.00686646 ,0.01031494 ,0.01217651 ,0.01272583 ,0.01296997 , 0.01126099 ,0.01116943 ,0.01016235 ,0.00854492 ,0.00802612 , 0.00668335 ,0.00543213 ,0.00616455 ,0.00506592 ,0.00408936 , 0.00378418 ,0.00479126 ,0.00576782 ,0.00653076 ,0.00488281 , 0.00320435 ,0.00247192 ,0.00393677 ,0.00604248 ,0.00820923 , 0.00866699 ,0.00711060 ,0.00534058 ,0.00430298 ,0.00244141 , 0.00082397 ,0.00085449 ,0.00335693 ,0.00683594 ,0.01019287 , 0.01168823 ,0.00924683 ,0.00421143 ,-0.00045776 ,-0.00521851 , -0.00811768 ,-0.00607300 ,-0.00366211 ,-0.00177002 ,-0.00311279 , -0.00347900 ,-0.00659180 ,-0.00750732 ,-0.00964355 ,-0.01123047 , -0.01162720 ,-0.01226807 ,-0.01370239 ,-0.01397705 ,-0.01043701 , -0.00732422 ,-0.00564575 ,-0.00640869 ,-0.00869751 ,-0.00961304 , -0.00790405 ,-0.00610352 ,-0.00592041 ,-0.00701904 ,-0.00332642 , 0.00000000 ,0.00424194 ,0.00540161 ,0.00320435 ,-0.00024414 , -0.00424194 ,-0.00552368 ,-0.00463867 ,-0.00210571 ,0.00076294 , 0.00128174 ,-0.00033569 ,0.00122070 ,0.00082397 ,-0.00009155 , -0.00042725 ,0.00036621 ,0.00122070 ,0.00012207 ,-0.00003052 , -0.00064087 ,0.00088501 ,0.00189209 ,0.00277710 ,0.00134277 , -0.00018311 ,-0.00219727 ,-0.00457764 ,-0.00634766 ,-0.00744629 , -0.00924683 ,-0.01052856 ,-0.01419067 ,-0.01574707 ,-0.01721191 , -0.02032471 ,-0.02026367 ,-0.01657104 ,-0.01156616 ,-0.00588989 , -0.00402832 ,-0.00332642 ,-0.00604248 ,-0.00827026 ,-0.01068115 , -0.01132202 ,-0.00915527 ,-0.00588989 ,-0.00106812 ,0.00033569 , -0.00006104 ,-0.00042725 ,-0.00137329 ,-0.00354004 ,-0.00381470 , -0.00363159 ,-0.00302124 ,-0.00225830 ,-0.00213623 ,-0.00341797 , -0.00463867 ,-0.00473022 ,-0.00360107 ,-0.00277710 ,-0.00131226 , -0.00131226 ,-0.00079346 ,-0.00268555 ,-0.00390625 ,-0.00219727 , 0.00067139 ,0.00341797 ,0.00445557 ,0.00616455 ,0.00738525 , 0.00866699 ,0.00881958 ,0.00860596 ,0.00918579 ,0.00567627 , 0.00408936 ,0.00283813 ,0.00430298 ,0.00723267 ,0.00891113 , 0.00991821 ,0.00711060 ,0.00543213 ,0.00280762 ,0.00009155 , -0.00070190 ,0.00057983 ,0.00115967 ,0.00183105 ,0.00042725 , -0.00070190 ,-0.00454712 ,-0.00357056 ,-0.00296021 ,0.00033569 , 0.00445557 ,0.00744629 ,0.00817871 ,0.01058960 ,0.01303101 , 0.01501465 ,0.01559448 ,0.01593018 ,0.01501465 ,0.01443481 , 0.01486206 ,0.01400757 ,0.01419067 ,0.01281738 ,0.01263428 , 0.01358032 ,0.01403809 ,0.01693726 ,0.02035522 ,0.02197266 , 0.02008057 ,0.01757812 ,0.01419067 ,0.01205444 ,0.01367188 , 0.01208496 ,0.01220703 ,0.01318359 ,0.01126099 ,0.00942993 , 0.00631714 ,0.00509644 ,0.00274658 ,0.00286865 ,0.00436401 , 0.00375366 ,0.00415039 ,0.00399780 ,0.00259399 ,0.00155640 , 0.00189209 ,0.00097656 ,0.00128174 ,0.00103760 ,0.00173950 , 0.00073242 ,-0.00100708 ,-0.00192261 ,-0.00140381 ,-0.00082397 , 0.00103760 ,0.00326538 ,0.00448608 ,0.00360107 ,0.00247192 , 0.00024414 ,-0.00164795 ,-0.00183105 ,0.00045776 ,0.00289917 , 0.00531006 ,0.00592041 ,0.00402832 ,0.00204468 ,0.00204468 , 0.00051880 ,0.00009155 ,-0.00128174 ,-0.00384521 ,-0.00796509 , -0.00936890 ,-0.00814819 ,-0.00830078 ,-0.00967407 ,-0.01144409 , -0.01278687 ,-0.01303101 ,-0.01156616 ,-0.01263428 ,-0.01336670 , -0.01239014 ,-0.01348877 ,-0.01287842 ,-0.01257324 ,-0.01278687 , -0.01281738 ,-0.01382446 ,-0.01147461 ,-0.01162720 ,-0.01126099 , -0.01095581 ,-0.00976562 ,-0.00909424 ,-0.00863647 ,-0.00610352 , -0.00393677 ,-0.00402832 ,-0.00451660 ,-0.00515747 ,-0.00616455 , -0.00497437 ,-0.00323486 ,-0.00146484 ,0.00054932 ,0.00134277 , 0.00137329 ,0.00076294 ,-0.00115967 ,-0.00207520 ,-0.00473022 , -0.00634766 ,-0.00735474 ,-0.00387573 ,-0.00329590 ,-0.00305176 , -0.00436401 ,-0.00424194 ,-0.00338745 ,-0.00317383 ,-0.00128174 , -0.00112915 ,-0.00003052 ,-0.00100708 ,-0.00286865 ,-0.00201416 , -0.00204468 ,-0.00039673 ,0.00036621 ,0.00161743 ,0.00241089 , 0.00210571 ,0.00173950 ,-0.00109863 ,-0.00253296 ,-0.00668335 , -0.00869751 ,-0.01058960 ,-0.01226807 ,-0.01251221 ,-0.01181030 , -0.01062012 ,-0.00970459 ,-0.00805664 ,-0.00570679 ,-0.00866699 , -0.00811768 ,-0.00894165 ,-0.00875854 ,-0.00479126 ,-0.00094604 , -0.00164795 ,-0.00146484 ,-0.00280762 ,-0.00509644 ,-0.00558472 , -0.00457764 ,-0.00277710 ,-0.00433350 ,-0.00509644 ,-0.00741577 , -0.00451660 ,-0.00207520 ,0.00265503 ,0.00457764 ,0.00607300 , 0.00500488 ,0.00299072 ,0.00085449 ,-0.00082397 ,-0.00027466 , -0.00259399 ,-0.00115967 ,-0.00039673 ,0.00088501 ,0.00177002 , 0.00299072 ,0.00332642 ,0.00152588 ,-0.00051880 ,0.00167847 , 0.00482178 ,0.00592041 ,0.00723267 ,0.00689697 ,0.00442505 , 0.00375366 ,0.00442505 ,0.00637817 ,0.00930786 ,0.01373291 , 0.01651001 ,0.01834106 ,0.01828003 ,0.01782227 ,0.01712036 , 0.01599121 ,0.01361084 ,0.01541138 ,0.01696777 ,0.01879883 , 0.01855469 ,0.01736450 ,0.01617432 ,0.01583862 ,0.01632690 , 0.01629639 ,0.01562500 ,0.01394653 ,0.01345825 ,0.01364136 , 0.01446533 ,0.01464844 ,0.01254272 ,0.00967407 ,0.00668335 , 0.00506592 ,0.00436401 ,0.00497437 ,0.00582886 ,0.00659180 , 0.00772095 ,0.00558472 ,0.00405884 ,0.00283813 ,0.00045776 , 0.00070190 ,0.00045776 ,-0.00091553 ,-0.00024414 ,0.00067139 , 0.00054932 ,0.00039673 ,-0.00036621 ,-0.00286865 ,-0.00680542 , -0.00930786 ,-0.01248169 ,-0.01504517 ,-0.01614380 ,-0.01614380 , -0.01559448 ,-0.01397705 ,-0.01312256 ,-0.01181030 ,-0.00848389 , -0.00982666 ,-0.01004028 ,-0.01147461 ,-0.01330566 ,-0.01242065 , -0.01220703 ,-0.01525879 ,-0.01385498 ,-0.01507568 ,-0.01245117 , -0.01013184 ,-0.00817871 ,-0.00799561 ,-0.00811768 ,-0.00997925 , -0.00891113 ,-0.00823975 ,-0.00765991 ,-0.00796509 ,-0.00894165 , -0.00836182 ,-0.00622559 ,-0.00213623 ,-0.00027466 ,-0.00073242 , -0.00259399 ,-0.00601196 ,-0.00933838 ,-0.01058960 ,-0.01040649 , -0.01086426 ,-0.00918579 ,-0.00765991 ,-0.00646973 ,-0.00579834 , -0.00506592 ,-0.00585938 ,-0.00698853 ,-0.00860596 ,-0.01040649 , -0.01229858 ,-0.01187134 ,-0.00878906 ,-0.00704956 ,-0.00698853 , -0.00683594 ,-0.00424194 ,-0.00234985 ,0.00045776 ,0.00170898 , 0.00119019 ,0.00039673 ,0.00097656 ,0.00003052 ,0.00296021 , 0.00360107 ,0.00369263 ,0.00460815 ,0.00683594 ,0.01000977 , 0.00775146 ,0.00708008 ,0.00527954 ,0.00488281 ,0.00488281 , 0.00427246 ,0.00289917 ,0.00317383 ,0.00088501 ,0.00103760 , 0.00180054 ,0.00186157 ,0.00393677 ,0.00372314 ,0.00192261 , 0.00134277 ,0.00106812 ,0.00302124 ,0.00408936 ,0.00320435 , 0.00051880 ,-0.00289917 ,-0.00485229 ,-0.00564575 ,-0.00500488 , -0.00518799 ,-0.00451660 ,-0.00427246 ,-0.00338745 ,-0.00354004 , -0.00442505 ,-0.00424194 ,-0.00442505 ,-0.00378418 ,-0.00396729 , -0.00436401 ,-0.00576782 ,-0.00506592 ,-0.00628662 ,-0.00598145 , -0.00341797 ,-0.00216675 ,-0.00073242 ,0.00088501 ,0.00009155 , -0.00100708 ,-0.00180054 ,-0.00311279 ,-0.00103760 ,0.00131226 , 0.00466919 ,0.00765991 ,0.01174927 ,0.01498413 ,0.01528931 , 0.01580811 ,0.01742554 ,0.01312256 ,0.01254272 ,0.01156616 , 0.01132202 ,0.01147461 ,0.01058960 ,0.01126099 ,0.01156616 , 0.01211548 ,0.01199341 ,0.01110840 ,0.00753784 ,0.00399780 , 0.00360107 ,0.00228882 ,0.00228882 ,0.00253296 ,0.00305176 , 0.00256348 ,0.00256348 ,0.00207520 ,0.00308228 ,0.00228882 , 0.00125122 ,0.00097656 ,0.00140381 ,0.00131226 ,0.00213623 , 0.00030518 ,-0.00241089 ,-0.00207520 ,-0.00024414 ,-0.00006104 , 0.00177002 ,0.00552368 ,0.00695801 ,0.00820923 ,0.00817871 , 0.01126099 ,0.01345825 ,0.01528931 ,0.01412964 ,0.01208496 , 0.00939941 ,0.00863647 ,0.00595093 ,0.00308228 ,0.00051880 , 0.00070190 ,0.00195312 ,0.00399780 ,0.00656128 ,0.00802612 , 0.00796509 ,0.00741577 ,0.00634766 ,0.00411987 ,0.00460815 , 0.00213623 ,0.00140381 ,0.00201416 ,0.00204468 ,0.00234985 , 0.00088501 ,-0.00106812 ,-0.00225830 ,-0.00054932 ,-0.00073242 , -0.00262451 ,-0.00579834 ,-0.00888062 ,-0.01052856 ,-0.01199341 , -0.01223755 ,-0.01181030 ,-0.01199341 ,-0.01367188 ,-0.01641846 , -0.01852417 ,-0.01980591 ,-0.02017212 ,-0.02072144 ,-0.02084351 , -0.02062988 ,-0.02151489 ,-0.01983643 ,-0.01846313 ,-0.01925659 , -0.01763916 ,-0.01934814 ,-0.02072144 ,-0.02377319 ,-0.02264404 , -0.02175903 ,-0.02258301 ,-0.02285767 ,-0.02423096 ,-0.02410889 , -0.02236938 ,-0.01803589 ,-0.01446533 ,-0.01260376 ,-0.01193237 , -0.01068115 ,-0.01052856 ,-0.00936890 ,-0.00717163 ,-0.00665283 , -0.00601196 ,-0.00543213 ,-0.00491333 ,-0.00329590 ,-0.00048828 , 0.00244141 ,0.00582886 ,0.00692749 ,0.00744629 ,0.00827026 , 0.00714111 ,0.00765991 ,0.00909424 ,0.00949097 ,0.00875854 , 0.00869751 ,0.00860596 ,0.00650024 ,0.00439453 ,0.00057983 , -0.00064087 ,-0.00067139 ,0.00018311 ,0.00039673 ,0.00173950 , 0.00225830 ,0.00415039 ,0.00515747 ,0.00662231 ,0.00479126 , 0.00228882 ,-0.00088501 ,-0.00039673 ,-0.00027466 ,-0.00003052 , -0.00125122 ,-0.00033569 ,-0.00134277 ,-0.00311279 ,-0.00473022 , -0.00595093 ,-0.00650024 ,-0.00704956 ,-0.00509644 ,-0.00347900 , -0.00125122 ,-0.00094604 ,0.00039673 ,-0.00051880 ,-0.00140381 , -0.00112915 ,-0.00109863 ,0.00045776 ,-0.00015259 ,-0.00006104 , 0.00100708 ,0.00146484 ,0.00128174 ,0.00125122 ,0.00183105 , 0.00369263 ,0.00628662 ,0.00646973 ,0.00762939 ,0.00775146 , 0.00906372 ,0.00875854 ,0.00732422 ,0.00515747 ,0.00564575 , 0.00714111 ,0.00527954 ,0.00195312 ,-0.00070190 ,-0.00158691 , -0.00210571 ,-0.00280762 ,-0.00122070 ,-0.00106812 ,0.00177002 , 0.00430298 ,0.00500488 ,0.00521851 ,0.00445557 ,0.00222778 , 0.00106812 ,-0.00027466 ,0.00192261 ,0.00582886 ,0.00823975 , 0.00961304 ,0.01123047 ,0.01290894 ,0.01458740 ,0.01480103 , 0.01434326 ,0.01480103 ,0.01455688 ,0.01452637 ,0.01702881 , 0.01876831 ,0.01834106 ,0.01770020 ,0.01550293 ,0.01550293 , 0.01696777 ,0.01443481 ,0.01394653 ,0.01266479 ,0.01150513 , 0.01184082 ,0.01300049 ,0.01263428 ,0.00939941 ,0.00741577 , 0.00601196 ,0.00585938 ,0.00555420 ,0.00289917 ,0.00247192 , -0.00088501 ,-0.00085449 ,-0.00271606 ,-0.00115967 ,-0.00170898 , -0.00091553 ,0.00100708 ,0.00186157 ,0.00244141 ,-0.00070190 , -0.00299072 ,-0.00375366 ,-0.00222778 ,-0.00518799 ,-0.00375366 , -0.00396729 ,-0.00296021 ,-0.00216675 ,-0.00234985 ,-0.00329590 , -0.00314331 ,-0.00329590 ,-0.00372314 ,-0.00381470 ,-0.00436401 , -0.00393677 ,-0.00125122 ,-0.00009155 ,0.00314331 ,0.00241089 , 0.00216675 ,0.00006104 ,-0.00204468 ,-0.00167847 ,-0.00119019 , -0.00137329 ,-0.00247192 ,-0.00671387 ,-0.00698853 ,-0.00851440 , -0.00848389 ,-0.00601196 ,-0.00454712 ,-0.00262451 ,-0.00201416 , -0.00161743 ,-0.00103760 ,-0.00079346 ,-0.00189209 ,-0.00350952 , -0.00360107 ,-0.00564575 ,-0.00592041 ,-0.00701904 ,-0.00817871 , -0.00790405 ,-0.00714111 ,-0.00289917 ,0.00143433 ,0.00323486 , 0.00393677 ,0.00122070 ,-0.00018311 ,-0.00161743 ,-0.00329590 , -0.00131226 ,0.00042725 ,0.00039673 ,0.00015259 ,-0.00180054 , -0.00567627 ,-0.00570679 ,-0.00463867 ,-0.00241089 ,0.00106812 , 0.00549316 ,0.00799561 ,0.00805664 ,0.00564575 ,0.00393677 , 0.00369263 ,0.00274658 ,0.00341797 ,0.00180054 ,0.00341797 , 0.00436401 ,0.00573730 ,0.00805664 ,0.00808716 ,0.00808716 , 0.00817871 ,0.00717163 ,0.00903320 ,0.00964355 ,0.01062012 , 0.00927734 ,0.00976562 ,0.00839233 ,0.00729370 ,0.00555420 , 0.00302124 ,-0.00045776 ,-0.00344849 ,-0.00415039 ,-0.00393677 , -0.00286865 ,-0.00201416 ,-0.00076294 ,0.00085449 ,0.00390625 , 0.00537109 ,0.00631714 ,0.00292969 ,0.00109863 ,-0.00082397 , -0.00149536 ,-0.00076294 ,-0.00021362 ,-0.00027466 ,0.00122070 , 0.00115967 ,0.00094604 ,0.00112915 ,0.00152588 ,-0.00079346 , -0.00329590 ,-0.00399780 ,-0.00473022 ,-0.00283813 ,-0.00305176 , -0.00259399 ,-0.00268555 ,-0.00253296 ,-0.00216675 ,0.00076294 , 0.00131226 ,0.00195312 ,0.00018311 ,-0.00286865 ,-0.00378418 , -0.00399780 ,-0.00320435 ,0.00036621 ,0.00357056 ,0.00308228 , 0.00152588 ,0.00146484 ,-0.00057983 ,-0.00009155 ,-0.00155640 , -0.00262451 ,-0.00500488 ,-0.00662231 ,-0.00711060 ,-0.00659180 , -0.00509644 ,-0.00405884 ,-0.00283813 ,-0.00204468 ,-0.00210571 , -0.00054932 ,0.00082397 ,0.00283813 ,0.00314331 ,0.00659180 , 0.00509644 ,0.00500488 ,0.00521851 ,0.00579834 ,0.00527954 , 0.00451660 ,0.00244141 ,0.00177002 ,0.00228882 ,0.00375366 , 0.00439453 ,0.00668335 ,0.00802612 ,0.00695801 ,0.00424194 , 0.00070190 ,-0.00366211 ,-0.00454712 ,-0.00482178 ,-0.00366211 , -0.00512695 ,-0.00521851 ,-0.00424194 ,-0.00570679 ,-0.00509644 , -0.00292969 ,-0.00378418 ,-0.00518799 ,-0.00683594 ,-0.00802612 , -0.00701904 ,-0.00616455 ,-0.00616455 ,-0.00512695 ,-0.00650024 , -0.00720215 ,-0.00863647 ,-0.00802612 ,-0.00753784 ,-0.00817871 , -0.00885010 ,-0.00769043 ,-0.00668335 ,-0.00552368 ,-0.00494385 , -0.00311279 ,-0.00167847 ,0.00268555 ,0.00430298 ,0.00424194 , 0.00079346 ,-0.00122070 ,-0.00402832 ,-0.00323486 ,-0.00354004 , -0.00143433 ,-0.00173950 ,-0.00006104 ,0.00097656 ,0.00259399 , 0.00241089 ,-0.00079346 ,-0.00186157 ,-0.00262451 ,-0.00302124 , -0.00256348 ,-0.00177002 ,-0.00286865 ,-0.00222778 ,-0.00482178 , -0.00588989 ,-0.00369263 ,-0.00415039 ,-0.00268555 ,-0.00308228 , -0.00518799 ,-0.00354004 ,-0.00466919 ,-0.00463867 ,-0.00482178 , -0.00305176 ,-0.00103760 ,-0.00296021 ,-0.00106812 ,-0.00241089 , -0.00146484 ,-0.00131226 ,-0.00167847 ,-0.00106812 ,-0.00051880 , 0.00009155 ,0.00000000 ,0.00195312 ,0.00210571 ,0.00317383 , 0.00573730 ,0.00659180 ,0.00671387 ,0.00463867 ,0.00491333 , 0.00305176 ,0.00296021 ,0.00454712 ,0.00698853 ,0.00921631 , 0.00848389 ,0.01141357 ,0.01141357 ,0.01162720 ,0.01126099 , 0.01187134 ,0.01263428 ,0.01226807 ,0.01229858 ,0.01205444 , 0.00985718 ,0.01196289 ,0.01132202 ,0.01055908 ,0.01104736 , 0.01007080 ,0.00793457 ,0.00656128 ,0.00442505 ,0.00317383 , 0.00463867 ,0.00653076 ,0.00952148 ,0.01080322 ,0.01110840 , 0.01116943 ,0.01043701 ,0.01205444 ,0.01104736 ,0.01150513 , 0.01248169 ,0.01312256 ,0.01214600 ,0.01147461 ,0.00921631 , 0.00708008 ,0.00585938 ,0.00436401 ,0.00491333 ,0.00628662 , 0.00601196 ,0.00500488 ,0.00311279 ,0.00088501 ,-0.00149536 , -0.00122070 ,0.00012207 ,0.00076294 ,0.00030518 ,-0.00021362 , -0.00048828 ,-0.00131226 ,-0.00067139 ,-0.00445557 ,-0.00473022 , -0.00244141 ,-0.00213623 ,0.00134277 ,0.00332642 ,0.00219727 , 0.00112915 ,0.00244141 ,0.00271606 ,0.00158691 ,-0.00027466 , -0.00192261 ,-0.00488281 ,-0.00616455 ,-0.00625610 ,-0.00567627 , -0.00415039 ,-0.00244141 ,-0.00164795 ,-0.00103760 ,-0.00140381 , -0.00149536 ,0.00106812 ,0.00094604 ,0.00247192 ,0.00323486 , 0.00396729 ,0.00540161 ,0.00329590 ,0.00305176 ,0.00253296 , 0.00259399 ,0.00296021 ,0.00451660 ,0.00515747 ,0.00695801 , 0.00592041 ,0.00534058 ,0.00320435 ,0.00097656 ,-0.00213623 , -0.00326538 ,-0.00375366 ,-0.00396729 ,-0.00277710 ,-0.00311279 , -0.00283813 ,-0.00250244 ,-0.00424194 ,-0.00634766 ,-0.00793457 , -0.00897217 ,-0.01126099 ,-0.01062012 ,-0.01000977 ,-0.00894165 , -0.00524902 ,-0.00399780 ,-0.00186157 ,-0.00097656 ,-0.00460815 , -0.00503540 ,-0.00668335 ,-0.00469971 ,-0.00503540 ,-0.00317383 , -0.00564575 ,-0.00753784 ,-0.00955200 ,-0.01065063 ,-0.01153564 , -0.01205444 ,-0.01153564 ,-0.01135254 ,-0.00924683 ,-0.00866699 , -0.00775146 ,-0.01049805 ,-0.01168823 ,-0.01333618 ,-0.01345825 , -0.01425171 ,-0.01354980 ,-0.01248169 ,-0.01071167 ,-0.00820923 , -0.00860596 ,-0.00808716 ,-0.00717163 ,-0.00659180 ,-0.00521851 , -0.00720215 ,-0.00692749 ,-0.00622559 ,-0.00613403 ,-0.00457764 , -0.00335693 ,-0.00427246 ,-0.00408936 ,-0.00515747 ,-0.00607300 , -0.00576782 ,-0.00473022 ,-0.00119019 ,0.00051880 ,-0.00067139 , -0.00228882 ,-0.00311279 ,-0.00375366 ,-0.00289917 ,-0.00164795 , -0.00103760 ,-0.00015259 ,0.00131226 ,0.00262451 ,0.00390625 , 0.00393677 ,0.00454712 ,0.00329590 ,0.00271606 ,0.00201416 , 0.00225830 ,0.00369263 ,0.00308228 ,0.00433350 ,0.00488281 , 0.00595093 ,0.00473022 ,0.00457764 ,0.00341797 ,0.00323486 , 0.00515747 ,0.00643921 ,0.00698853 ,0.00689697 ,0.00604248 , 0.00421143 ,0.00863647 ,0.00961304 ,0.00946045 ,0.00817871 , 0.00836182 ,0.00863647 ,0.00909424 ,0.00979614 ,0.00976562 , 0.00878906 ,0.00830078 ,0.00869751 ,0.00860596 ,0.00631714 , 0.00561523 ,0.00552368 ,0.00521851 ,0.00720215 ,0.00894165 , 0.01095581 ,0.01174927 ,0.01113892 ,0.01028442 ,0.00885010 , 0.00750732 ,0.00543213 ,0.00610352 ,0.00531006 ,0.00692749 , 0.00726318 ,0.00811768 ,0.00698853 ,0.00473022 ,0.00241089 , 0.00289917 ,0.00265503 ,0.00222778 ,0.00238037 ,0.00158691 , 0.00204468 ,0.00158691 ,0.00207520 ,0.00296021 ,0.00241089 , 0.00161743 ,0.00030518 ,-0.00170898 ,-0.00405884 ,-0.00604248 , -0.00671387 ,-0.00656128 ,-0.00680542 ,-0.00476074 ,-0.00451660 , -0.00549316 ,-0.00473022 ,-0.00604248 ,-0.00720215 ,-0.00686646 , -0.00662231 ,-0.00674438 ,-0.00708008 ,-0.00686646 ,-0.00448608 , -0.00579834 ,-0.00509644 ,-0.00637817 ,-0.00845337 ,-0.00854492 , -0.01058960 ,-0.01217651 ,-0.01275635 ,-0.01351929 ,-0.00878906 , -0.00640869 ,-0.00335693 ,-0.00360107 ,-0.00466919 ,-0.00604248 , -0.00656128 ,-0.00604248 ,-0.00463867 ,-0.00320435 ,-0.00369263 , -0.00405884 ,-0.00332642 ,-0.00378418 ,-0.00213623 ,-0.00076294 , -0.00106812 ,-0.00228882 ,-0.00509644 ,-0.00823975 ,-0.00869751 , -0.01052856 ,-0.00762939 ,-0.00387573 ,-0.00170898 ,-0.00137329 , -0.00137329 ,-0.00433350 ,-0.00582886 ,-0.00616455 ,-0.00817871 , -0.00695801 ,-0.00775146 ,-0.00564575 ,-0.00445557 ,-0.00195312 , -0.00140381 ,-0.00259399 ,-0.00396729 ,-0.00378418 ,-0.00274658 , -0.00051880 ,0.00033569 ,0.00192261 ,0.00277710 ,0.00109863 , -0.00042725 ,-0.00155640 ,-0.00128174 ,-0.00158691 ,-0.00143433 , -0.00122070 ,-0.00308228 ,-0.00390625 ,-0.00628662 ,-0.00759888 , -0.00622559 ,-0.00613403 ,-0.00393677 ,-0.00311279 ,-0.00344849 , -0.00619507 ,-0.00561523 ,-0.00671387 ,-0.00714111 ,-0.00494385 , -0.00524902 ,-0.00430298 ,-0.00350952 ,-0.00186157 ,-0.00262451 , -0.00302124 ,-0.00381470 ,-0.00427246 ,-0.00262451 ,-0.00210571 , -0.00088501 ,-0.00061035 ,0.00305176 ,0.00192261 ,0.00320435 , 0.00231934 ,0.00131226 ,0.00225830 ,0.00369263 ,0.00387573 , 0.00375366 ,0.00268555 ,0.00201416 ,0.00167847 ,0.00280762 , 0.00543213 ,0.00900269 ,0.00982666 ,0.00863647 ,0.00753784 , 0.00598145 ,0.00460815 ,0.00656128 ,0.00735474 ,0.00808716 , 0.00851440 ,0.00814819 ,0.00845337 ,0.00912476 ,0.00839233 , 0.00851440 ,0.01089478 ,0.01358032 ,0.01495361 ,0.01446533 , 0.01559448 ,0.01431274 ,0.01376343 ,0.01287842 ,0.00991821 , 0.00875854 ,0.00872803 ,0.00866699 ,0.01028442 ,0.00741577 , 0.00677490 ,0.00674438 ,0.00573730 ,0.00466919 ,0.00552368 , 0.00781250 ,0.00866699 ,0.01022339 ,0.00848389 ,0.00683594 , 0.00537109 ,0.00311279 ,0.00201416 ,0.00146484 ,0.00143433 , 0.00326538 ,0.00421143 ,0.00381470 ,0.00625610 ,0.00607300 , 0.00640869 ,0.00735474 ,0.00561523 ,0.00540161 ,0.00405884 , 0.00427246 ,0.00671387 ,0.00765991 ,0.00891113 ,0.00732422 , 0.00765991 ,0.00747681 ,0.00659180 ,0.00436401 ,0.00302124 , 0.00271606 ,0.00329590 ,0.00433350 ,0.00604248 ,0.00891113 , 0.00732422 ,0.00708008 ,0.00503540 ,0.00238037 ,-0.00119019 , -0.00234985 ,-0.00466919 ,-0.00485229 ,-0.00183105 ,-0.00244141 , -0.00366211 ,-0.00570679 ,-0.00720215 ,-0.00888062 ,-0.00991821 , -0.01181030 ,-0.01254272 ,-0.01333618 ,-0.01452637 ,-0.01449585 , -0.01284790 ,-0.01220703 ,-0.01184082 ,-0.01202393 ,-0.01345825 , -0.01367188 ,-0.01184082 ,-0.01046753 ,-0.00854492 ,-0.00839233 , -0.00778198 ,-0.00640869 ,-0.00640869 ,-0.00515747 ,-0.00357056 , -0.00384521 ,-0.00500488 ,-0.00460815 ,-0.00448608 ,-0.00546265 , -0.00357056 ,-0.00292969 ,-0.00378418 ,-0.00369263 ,-0.00387573 , -0.00384521 ,-0.00418091 ,-0.00494385 ,-0.00558472 ,-0.00457764 , -0.00512695 ,-0.00561523 ,-0.00619507 ,-0.00521851 ,-0.00659180 , -0.00646973 ,-0.00640869 ,-0.00643921 ,-0.00781250 ,-0.00622559 , -0.00592041 ,-0.00537109 ,-0.00598145 ,-0.00628662 ,-0.00866699 , -0.00772095 ,-0.00701904 ,-0.00714111 ,-0.00610352 ,-0.00747681 , -0.00912476 ,-0.01116943 ,-0.01251221 ,-0.01358032 ,-0.01446533 , -0.01263428 ,-0.01019287 ,-0.00820923 ,-0.00686646 ,-0.00503540 , -0.00424194 ,-0.00289917 ,-0.00473022 ,-0.00555420 ,-0.00720215 , -0.00820923 ,-0.00787354 ,-0.00836182 ,-0.00762939 ,-0.00543213 , -0.00369263 ,-0.00146484 ,0.00030518 ,0.00109863 ,-0.00122070 , -0.00149536 ,0.00003052 ,-0.00082397 ,-0.00003052 ,0.00042725 , 0.00204468 ,0.00317383 ,0.00292969 ,0.00497437 ,0.00610352 , 0.00625610 ,0.00659180 ,0.00680542 ,0.00619507 ,0.00543213 , 0.00415039 ,0.00036621 ,-0.00082397 ,-0.00149536 ,-0.00027466 , -0.00003052 ,0.00024414 ,0.00106812 ,0.00164795 ,0.00198364 , 0.00167847 ,0.00088501 ,0.00134277 ,0.00225830 ,0.00219727 , 0.00274658 ,0.00256348 ,0.00317383 ,0.00460815 ,0.00430298 , 0.00607300 ,0.00778198 ,0.00970459 ,0.00952148 ,0.01031494 , 0.00991821 ,0.00997925 ,0.01025391 ,0.01190186 ,0.01358032 , 0.01467896 ,0.01519775 ,0.01394653 ,0.01232910 ,0.01242065 , 0.01156616 ,0.01174927 ,0.01205444 ,0.01220703 ,0.01336670 , 0.01376343 ,0.01327515 ,0.01336670 ,0.01196289 ,0.01040649 , 0.00964355 ,0.00854492 ,0.00897217 ,0.00918579 ,0.00903320 , 0.00711060 ,0.00631714 ,0.00549316 ,0.00595093 ,0.00451660 , 0.00524902 ,0.00643921 ,0.00680542 ,0.00534058 ,0.00521851 , 0.00222778 ,0.00219727 ,0.00131226 ,0.00131226 ,0.00134277 , 0.00033569 ,0.00018311 ,-0.00070190 ,0.00000000 ,-0.00070190 , 0.00030518 ,0.00189209 ,0.00070190 ,0.00018311 ,-0.00051880 , -0.00131226 ,-0.00292969 ,-0.00241089 ,-0.00213623 ,-0.00274658 , -0.00244141 ,-0.00195312 ,-0.00204468 ,-0.00125122 ,-0.00024414 , -0.00024414 ,-0.00167847 ,-0.00241089 ,-0.00134277 ,-0.00213623 , -0.00143433 ,-0.00378418 ,-0.00537109 ,-0.00704956 ,-0.00784302 , -0.00775146 ,-0.00662231 ,-0.00588989 ,-0.00579834 ,-0.00598145 , -0.00613403 ,-0.00692749 ,-0.00708008 ,-0.00686646 ,-0.00625610 , -0.00765991 ,-0.00665283 ,-0.00765991 ,-0.00564575 ,-0.00643921 , -0.00640869 ,-0.00506592 ,-0.00427246 ,-0.00396729 ,-0.00381470 , -0.00482178 ,-0.00527954 ,-0.00488281 ,-0.00604248 ,-0.00527954 , -0.00515747 ,-0.00335693 ,-0.00317383 ,-0.00149536 ,-0.00067139 , -0.00036621 ,-0.00024414 ,-0.00152588 ,-0.00170898 ,-0.00219727 , -0.00161743 ,0.00018311 ,0.00085449 ,0.00244141 ,0.00268555 , 0.00128174 ,0.00231934 ,0.00274658 ,0.00158691 ,0.00143433 , 0.00057983 ,0.00045776 ,0.00112915 ,0.00219727 ,0.00454712 , 0.00378418 ,0.00445557 ,0.00427246 ,0.00488281 ,0.00415039 , 0.00402832 ,0.00509644 ,0.00442505 ,0.00588989 ,0.00537109 , 0.00411987 ,0.00357056 ,0.00354004 ,0.00280762 ,0.00427246 , 0.00402832 ,0.00366211 ,0.00280762 ,0.00299072 ,0.00170898 , 0.00122070 ,-0.00054932 ,-0.00140381 ,-0.00366211 ,-0.00415039 , -0.00454712 ,-0.00390625 ,-0.00274658 ,-0.00216675 ,-0.00222778 , -0.00241089 ,-0.00231934 ,-0.00207520 ,-0.00204468 ,-0.00204468 , -0.00378418 ,-0.00360107 ,-0.00631714 ,-0.00588989 ,-0.00698853 , -0.00610352 ,-0.00433350 ,-0.00225830 ,-0.00216675 ,-0.00158691 , 0.00036621 ,0.00088501 ,-0.00021362 ,-0.00134277 ,-0.00070190 , -0.00051880 ,0.00131226 ,0.00018311 ,-0.00064087 ,-0.00033569 , -0.00042725 ,0.00012207 ,0.00027466 ,0.00091553 ,0.00143433 , -0.00057983 ,-0.00143433 ,-0.00125122 ,-0.00054932 ,0.00006104 , 0.00259399 ,0.00546265 ,0.00622559 ,0.00643921 ,0.00643921 , 0.00527954 ,0.00488281 ,0.00680542 ,0.00784302 ,0.00827026 , 0.00805664 ,0.00851440 ,0.00823975 ,0.00952148 ,0.01071167 , 0.01110840 ,0.01010132 ,0.00793457 ,0.00634766 ,0.00341797 , 0.00262451 ,0.00405884 ,0.00381470 ,0.00390625 ,0.00430298 , 0.00396729 ,0.00253296 ,0.00149536 ,0.00177002 ,0.00094604 , 0.00036621 ,-0.00009155 ,0.00070190 ,-0.00036621 ,-0.00051880 , -0.00070190 ,-0.00134277 ,-0.00051880 ,-0.00173950 ,-0.00204468 , -0.00183105 ,-0.00167847 ,-0.00250244 ,-0.00317383 ,-0.00415039 , -0.00518799 ,-0.00308228 ,-0.00231934 ,-0.00271606 ,-0.00109863 , -0.00219727 ,-0.00238037 ,-0.00277710 ,-0.00424194 ,-0.00448608 , -0.00610352 ,-0.00784302 ,-0.00915527 ,-0.01037598 ,-0.00906372 , -0.00790405 ,-0.00833130 ,-0.00885010 ,-0.00924683 ,-0.00994873 , -0.01013184 ,-0.01074219 ,-0.01037598 ,-0.01138306 ,-0.01190186 , -0.00952148 ,-0.00756836 ,-0.00534058 ,-0.00463867 ,-0.00595093 , -0.00704956 ,-0.00775146 ,-0.00656128 ,-0.00534058 ,-0.00595093 , -0.00527954 ,-0.00436401 ,-0.00439453 ,-0.00341797 ,-0.00213623 , -0.00207520 ,-0.00225830 ,-0.00155640 ,-0.00180054 ,-0.00164795 , -0.00207520 ,-0.00268555 ,-0.00411987 ,-0.00469971 ,-0.00512695 , -0.00354004 ,-0.00271606 ,-0.00244141 ,-0.00183105 ,-0.00308228 , -0.00347900 ,-0.00219727 ,-0.00119019 ,-0.00106812 ,-0.00054932 , -0.00149536 ,-0.00109863 ,-0.00207520 ,-0.00140381 ,-0.00268555 , -0.00216675 ,0.00003052 ,0.00231934 ,0.00402832 ,0.00509644 , 0.00442505 ,0.00399780 ,0.00436401 ,0.00494385 ,0.00601196 , 0.00683594 ,0.00671387 ,0.00714111 ,0.00756836 ,0.00701904 , 0.00424194 ,0.00308228 ,0.00366211 ,0.00463867 ,0.00616455 , 0.00747681 ,0.00827026 ,0.00708008 ,0.00601196 ,0.00457764 , 0.00360107 ,0.00268555 ,0.00238037 ,0.00250244 ,0.00189209 , 0.00289917 ,0.00247192 ,0.00222778 ,0.00106812 ,0.00216675 , 0.00338745 ,0.00552368 ,0.00561523 ,0.00570679 ,0.00454712 , 0.00378418 ,0.00570679 ,0.00469971 ,0.00759888 ,0.00665283 , 0.00628662 ,0.00387573 ,0.00085449 ,0.00000000 ,-0.00134277 , -0.00030518 ,0.00073242 ,0.00189209 ,0.00341797 ,0.00396729 , 0.00421143 ,0.00164795 ,0.00057983 ,-0.00036621 ,-0.00054932 , -0.00198364 ,-0.00039673 ,0.00030518 ,-0.00027466 ,0.00012207 , 0.00143433 ,0.00109863 ,0.00274658 ,0.00247192 ,0.00253296 , 0.00314331 ,0.00424194 ,0.00335693 ,0.00573730 ,0.00601196 , 0.00610352 ,0.00729370 ,0.00689697 ,0.00595093 ,0.00668335 , 0.00601196 ,0.00604248 ,0.00765991 ,0.00698853 ,0.00775146 , 0.00839233 ,0.00842285 ,0.00994873 ,0.01159668 ,0.01138306 , 0.00949097 ,0.00903320 ,0.00790405 ,0.00573730 ,0.00479126 , 0.00445557 ,0.00238037 ,0.00170898 ,0.00140381 ,0.00143433 , 0.00036621 ,0.00024414 ,0.00033569 ,-0.00003052 ,0.00033569 , -0.00112915 ,-0.00183105 ,-0.00531006 ,-0.00585938 ,-0.00506592 , -0.00488281 ,-0.00363159 ,-0.00280762 ,-0.00491333 ,-0.00457764 , -0.00393677 ,-0.00436401 ,-0.00469971 ,-0.00326538 ,-0.00167847 , -0.00170898 ,-0.00186157 ,-0.00125122 ,-0.00137329 ,-0.00241089 , -0.00372314 ,-0.00445557 ,-0.00668335 ,-0.00704956 ,-0.00885010 , -0.01000977 ,-0.00946045 ,-0.00836182 ,-0.00677490 ,-0.00631714 , -0.00607300 ,-0.00491333 ,-0.00369263 ,-0.00482178 ,-0.00567627 , -0.00720215 ,-0.00973511 ,-0.00946045 ,-0.01098633 ,-0.01010132 , -0.01031494 ,-0.01116943 ,-0.01089478 ,-0.01065063 ,-0.01147461 , -0.01055908 ,-0.01007080 ,-0.00946045 ,-0.00891113 ,-0.00802612 , -0.00683594 ,-0.00546265 ,-0.00485229 ,-0.00393677 ,-0.00274658 , -0.00360107 ,-0.00207520 ,-0.00271606 ,-0.00457764 ,-0.00430298 , -0.00415039 ,-0.00527954 ,-0.00573730 ,-0.00527954 ,-0.00207520 , -0.00155640 ,-0.00067139 ,0.00024414 ,0.00030518 ,0.00082397 , 0.00036621 ,-0.00115967 ,-0.00054932 ,-0.00161743 ,-0.00198364 , -0.00018311 ,-0.00018311 ,0.00048828 ,-0.00149536 ,-0.00195312 , -0.00128174 ,-0.00228882 ,-0.00167847 ,-0.00146484 ,0.00082397 , 0.00167847 ,0.00134277 ,0.00173950 ,0.00296021 ,0.00457764 , 0.00463867 ,0.00418091 ,0.00378418 ,0.00393677 ,0.00457764 , 0.00711060 ,0.00750732 ,0.00695801 ,0.00564575 ,0.00769043 , 0.00817871 ,0.00674438 ,0.00634766 ,0.00463867 ,0.00433350 , 0.00415039 ,0.00369263 ,0.00357056 ,0.00155640 ,0.00097656 , 0.00018311 ,-0.00146484 ,-0.00213623 ,-0.00366211 ,-0.00292969 , -0.00341797 ,-0.00137329 ,-0.00329590 ,-0.00167847 ,-0.00265503 , -0.00119019 ,-0.00015259 ,-0.00027466 ,-0.00027466 ,-0.00207520 , -0.00405884 ,-0.00399780 ,-0.00494385 ,-0.00427246 ,-0.00311279 , -0.00366211 ,-0.00323486 ,-0.00143433 ,-0.00012207 ,0.00152588 , 0.00311279 ,0.00207520 ,0.00109863 ,0.00082397 ,0.00051880 , 0.00131226 ,0.00103760 ,0.00283813 ,0.00354004 ,0.00460815 , 0.00482178 ,0.00518799 ,0.00408936 ,0.00292969 ,0.00369263 , 0.00421143 ,0.00488281 ,0.00595093 ,0.00482178 ,0.00460815 , 0.00476074 ,0.00442505 ,0.00561523 ,0.00579834 ,0.00387573 , 0.00292969 ,0.00274658 ,0.00384521 ,0.00479126 ,0.00650024 , 0.00585938 ,0.00604248 ,0.00601196 ,0.00701904 ,0.00671387 , 0.00689697 ,0.00573730 ,0.00521851 ,0.00473022 ,0.00399780 , 0.00393677 ,0.00430298 ,0.00253296 ,0.00216675 ,0.00094604 , -0.00097656 ,-0.00195312 ,-0.00143433 ,-0.00158691 ,-0.00076294 , -0.00030518 ,0.00091553 ,-0.00051880 ,-0.00146484 ,-0.00311279 , -0.00424194 ,-0.00320435 ,-0.00335693 ,-0.00375366 ,-0.00390625 , -0.00299072 ,-0.00210571 ,-0.00433350 ,-0.00415039 ,-0.00158691 , -0.00244141 ,-0.00177002 ,-0.00155640 ,-0.00317383 ,-0.00268555 , -0.00338745 ,-0.00381470 ,-0.00369263 ,-0.00384521 ,-0.00326538 , -0.00234985 ,-0.00216675 ,-0.00173950 ,-0.00238037 ,-0.00421143 , -0.00415039 ,-0.00619507 ,-0.00634766 ,-0.00582886 ,-0.00558472 , -0.00595093 ,-0.00549316 ,-0.00390625 ,-0.00314331 ,-0.00311279 , -0.00100708 ,-0.00213623 ,-0.00332642 ,-0.00396729 ,-0.00445557 , -0.00326538 ,-0.00292969 ,-0.00326538 ,-0.00155640 ,-0.00372314 , -0.00393677 ,-0.00317383 ,-0.00451660 ,-0.00393677 ,-0.00643921 , -0.00534058 ,-0.00473022 ,-0.00482178 ,-0.00396729 ,-0.00332642 , -0.00231934 ,-0.00286865 ,-0.00360107 ,-0.00460815 ,-0.00497437 , -0.00363159 ,-0.00305176 ,-0.00329590 ,-0.00234985 ,-0.00106812 , -0.00152588 ,0.00048828 ,0.00222778 ,0.00155640 ,0.00354004 , 0.00296021 ,0.00485229 ,0.00460815 ,0.00491333 ,0.00552368 , 0.00564575 ,0.00701904 ,0.00750732 ,0.00820923 ,0.00869751 , 0.00698853 ,0.00787354 ,0.00814819 ,0.00827026 ,0.00817871 , 0.00784302 ,0.00692749 ,0.00515747 ,0.00531006 ,0.00500488 , 0.00466919 ,0.00390625 ,0.00360107 ,0.00369263 ,0.00347900 , 0.00460815 ,0.00433350 ,0.00332642 ,0.00259399 ,0.00128174 , -0.00009155 ,-0.00082397 ,-0.00039673 ,-0.00048828 ,0.00143433 , 0.00027466 ,0.00051880 ,0.00003052 ,-0.00015259 ,-0.00024414 , -0.00021362 ,-0.00091553 ,0.00033569 ,0.00033569 ,0.00155640 , 0.00079346 ,0.00241089 ,0.00244141 ,0.00195312 ,0.00210571 , 0.00137329 ,0.00122070 ,0.00131226 ,0.00140381 ,0.00061035 , 0.00076294 ,0.00210571 ,0.00140381 ,0.00097656 ,0.00061035 , 0.00061035 ,-0.00042725 ,0.00006104 ,0.00073242 ,-0.00051880 , -0.00100708 ,-0.00076294 ,-0.00286865 ,-0.00320435 ,-0.00411987 , -0.00473022 ,-0.00482178 ,-0.00619507 ,-0.00552368 ,-0.00482178 , -0.00497437 ,-0.00515747 ,-0.00427246 ,-0.00524902 ,-0.00582886 , -0.00466919 ,-0.00454712 ,-0.00415039 ,-0.00241089 ,-0.00119019 , -0.00073242 ,-0.00064087 ,0.00015259 ,0.00000000 ,-0.00009155 , -0.00039673 ,-0.00030518 ,0.00076294 ,0.00100708 ,0.00238037 , 0.00262451 ,0.00311279 ,0.00320435 ,0.00430298 ,0.00579834 , 0.00497437 ,0.00338745 ,0.00292969 ,0.00109863 ,0.00137329 , 0.00354004 ,0.00305176 ,0.00515747 ,0.00473022 ,0.00433350 , 0.00296021 ,0.00418091 ,0.00292969 ,0.00469971 ,0.00448608 , 0.00601196 ,0.00363159 ,0.00469971 ,0.00305176 ,0.00177002 , 0.00149536 ,0.00045776 ,-0.00057983 ,-0.00073242 ,-0.00030518 , -0.00161743 ,-0.00207520 ,-0.00265503 ,-0.00256348 ,-0.00250244 , -0.00152588 ,-0.00112915 ,-0.00259399 ,-0.00207520 ,-0.00280762 , -0.00369263 ,-0.00491333 ,-0.00704956 ,-0.00601196 ,-0.00631714 , -0.00418091 ,-0.00418091 ,-0.00405884 ,-0.00415039 ,-0.00500488 , -0.00500488 ,-0.00555420 ,-0.00503540 ,-0.00405884 ,-0.00347900 , -0.00369263 ,-0.00314331 ,-0.00354004 ,-0.00540161 ,-0.00494385 , -0.00396729 ,-0.00357056 ,-0.00323486 ,-0.00265503 ,-0.00311279 , -0.00186157 ,-0.00308228 ,-0.00112915 ,-0.00308228 ,-0.00274658 , -0.00262451 ,-0.00131226 ,-0.00030518 ,0.00122070 ,0.00030518 , 0.00076294 ,0.00042725 ,-0.00097656 ,-0.00170898 ,-0.00119019 , -0.00048828 ,-0.00085449 ,-0.00045776 ,0.00045776 ,0.00036621 , 0.00140381 ,0.00173950 ,0.00173950 ,0.00296021 ,0.00372314 , 0.00384521 ,0.00317383 ,0.00299072 ,0.00314331 ,0.00436401 , 0.00430298 ,0.00515747 ,0.00369263 ,0.00497437 ,0.00588989 , 0.00582886 ,0.00628662 ,0.00540161 ,0.00607300 ,0.00537109 , 0.00588989 ,0.00518799 ,0.00534058 ,0.00552368 ,0.00534058 , 0.00720215 ,0.00784302 ,0.00524902 ,0.00704956 ,0.00665283 , 0.00665283 ,0.00582886 ,0.00659180 ,0.00668335 ,0.00552368 , 0.00408936 ,0.00427246 ,0.00354004 ,0.00344849 ,0.00357056 , 0.00344849 ,0.00430298 ,0.00357056 ,0.00369263 ,0.00418091 , 0.00375366 ,0.00219727 ,0.00265503 ,0.00268555 ,0.00305176 , 0.00354004 ,0.00292969 ,0.00430298 ,0.00424194 ,0.00305176 , 0.00170898 ,0.00082397 ,-0.00024414 ,-0.00054932 ,-0.00173950 , -0.00222778 ,-0.00347900 ,-0.00341797 ,-0.00216675 ,-0.00250244 , -0.00222778 ,-0.00372314 ,-0.00308228 ,-0.00311279 ,-0.00335693 , -0.00296021 ,-0.00170898 ,-0.00271606 ,-0.00274658 ,-0.00341797 , -0.00418091 ,-0.00402832 ,-0.00485229 ,-0.00500488 ,-0.00463867 , -0.00518799 ,-0.00350952 ,-0.00283813 ,-0.00079346 ,-0.00195312 , -0.00152588 ,-0.00146484 ,-0.00177002 ,0.00039673 ,-0.00036621 , -0.00054932 ,-0.00137329 ,-0.00186157 ,-0.00112915 ,-0.00054932 , -0.00125122 ,0.00061035 ,-0.00076294 ,0.00119019 ,0.00201416 , 0.00177002 ,0.00143433 ,-0.00033569 ,-0.00125122 ,-0.00180054 , -0.00061035 ,-0.00021362 ,-0.00018311 ,-0.00015259 ,-0.00183105 , -0.00167847 ,-0.00045776 ,-0.00222778 ,-0.00219727 ,-0.00344849 , -0.00320435 ,-0.00323486 ,-0.00216675 ,-0.00271606 ,-0.00271606 , -0.00241089 ,-0.00280762 ,-0.00283813 ,-0.00219727 ,-0.00238037 , -0.00326538 ,-0.00320435 ,-0.00494385 ,-0.00662231 ,-0.00711060 , -0.00711060 ,-0.00631714 ,-0.00454712 ,-0.00323486 ,-0.00103760 , -0.00173950 ,-0.00219727 ,-0.00299072 ,-0.00430298 ,-0.00323486 , -0.00354004 ,-0.00213623 ,-0.00347900 ,-0.00247192 ,-0.00366211 , -0.00338745 ,-0.00265503 ,-0.00131226 ,-0.00189209 ,-0.00097656 , -0.00112915 ,-0.00265503 ,-0.00354004 ,-0.00363159 ,-0.00402832 , -0.00436401 ,-0.00457764 ,-0.00384521 ,-0.00360107 ,-0.00329590 , -0.00335693 ,-0.00302124 ,-0.00305176 ,-0.00283813 ,-0.00372314 , -0.00274658 ,-0.00305176 ,-0.00216675 ,-0.00326538 ,-0.00442505 , -0.00537109 ,-0.00506592 ,-0.00573730 ,-0.00521851 ,-0.00537109 , -0.00534058 ,-0.00625610 ,-0.00527954 ,-0.00564575 ,-0.00579834 , -0.00427246 ,-0.00274658 ,-0.00280762 ,-0.00234985 ,-0.00158691 , -0.00149536 ,-0.00033569 ,-0.00076294 ,-0.00048828 ,-0.00103760 , 0.00021362 ,-0.00094604 ,-0.00085449 ,-0.00119019 ,-0.00009155 , -0.00003052 ,0.00161743 ,0.00061035 ,0.00390625 ,0.00442505 , 0.00341797 ,0.00283813 ,0.00253296 ,0.00390625 ,0.00418091 , 0.00357056 ,0.00384521 ,0.00497437 ,0.00418091 ,0.00527954 , 0.00546265 ,0.00735474 ,0.00787354 ,0.00784302 ,0.00671387 , 0.00457764 ,0.00473022 ,0.00381470 ,0.00384521 ,0.00415039 , 0.00537109 ,0.00518799 ,0.00485229 ,0.00451660 ,0.00393677 , 0.00274658 ,0.00152588 ,0.00018311 ,-0.00094604 ,-0.00119019 , -0.00155640 ,0.00109863 ,0.00198364 ,0.00296021 ,0.00326538 , 0.00234985 ,0.00225830 ,0.00216675 ,0.00140381 ,0.00204468 , 0.00335693 ,0.00463867 ,0.00585938 ,0.00683594 ,0.00573730 , 0.00628662 ,0.00384521 ,0.00415039 ,0.00317383 ,0.00274658 , 0.00039673 ,0.00036621 ,-0.00119019 ,-0.00018311 ,-0.00024414 , 0.00109863 ,0.00195312 ,0.00103760 ,-0.00021362 ,-0.00143433 , -0.00247192 ,-0.00204468 ,-0.00152588 ,-0.00244141 ,-0.00152588 , -0.00308228 ,-0.00155640 ,-0.00183105 ,-0.00155640 ,-0.00167847 , -0.00161743 ,0.00006104 ,-0.00033569 ,-0.00088501 ,-0.00210571 , -0.00204468 ,-0.00323486 ,-0.00149536 ,-0.00140381 ,-0.00061035 , -0.00067139 ,0.00003052 ,-0.00100708 ,-0.00134277 ,-0.00085449 , 0.00039673 ,-0.00006104 ,-0.00244141 ,-0.00308228 ,-0.00317383 , -0.00329590 ,-0.00283813 ,-0.00216675 ,-0.00299072 ,-0.00302124 , -0.00189209 ,-0.00247192 ,-0.00231934 ,-0.00262451 ,-0.00167847 , 0.00036621 ,0.00070190 ,0.00234985 ,0.00326538 ,0.00347900 , 0.00299072 ,0.00296021 ,0.00326538 ,0.00286865 ,0.00152588 , 0.00103760 ,0.00064087 ,0.00057983 ,0.00054932 ,0.00143433 , 0.00140381 ,0.00143433 ,0.00021362 ,0.00057983 ,0.00115967 , 0.00167847 ,0.00143433 ,0.00283813 ,0.00134277 ,0.00140381 , 0.00079346 ,-0.00048828 ,-0.00057983 ,-0.00186157 ,-0.00180054 , -0.00286865 ,-0.00424194 ,-0.00430298 ,-0.00524902 ,-0.00390625 , -0.00396729 ,-0.00421143 ,-0.00427246 ,-0.00262451 ,-0.00399780 , -0.00411987 ,-0.00244141 ,-0.00234985 ,-0.00210571 ,-0.00256348 , -0.00149536 ,-0.00213623 ,-0.00180054 ,-0.00265503 ,-0.00143433 , -0.00033569 ,-0.00015259 ,-0.00054932 ,0.00036621 ,-0.00051880 , 0.00076294 ,0.00112915 ,0.00109863 ,0.00143433 ,0.00183105 , 0.00234985 ,0.00274658 ,0.00418091 ,0.00363159 ,0.00277710 , 0.00256348 ,0.00363159 ,0.00311279 ,0.00320435 ,0.00201416 , 0.00378418 ,0.00280762 ,0.00418091 ,0.00421143 ,0.00451660 , 0.00595093 ,0.00454712 ,0.00509644 ,0.00561523 ,0.00585938 , 0.00714111 ,0.00723267 ,0.00534058 ,0.00500488 ,0.00457764 , 0.00402832 ,0.00469971 ,0.00442505 ,0.00381470 ,0.00296021 , 0.00326538 ,0.00500488 ,0.00280762 ,0.00439453 ,0.00326538 , 0.00305176 ,0.00314331 ,0.00277710 ,0.00375366 ,0.00222778 , 0.00283813 ,0.00210571 ,0.00253296 ,0.00375366 ,0.00323486 , 0.00305176 ,0.00335693 ,0.00259399 ,0.00335693 ,0.00311279 , 0.00357056 ,0.00360107 ,0.00521851 ,0.00527954 ,0.00650024 , 0.00491333 ,0.00415039 ,0.00427246 ,0.00399780 ,0.00527954 , 0.00479126 ,0.00354004 ,0.00238037 ,0.00109863 ,0.00048828 , -0.00112915 ,0.00039673 ,-0.00003052 ,0.00000000 ,-0.00027466 , -0.00039673 ,-0.00134277 ,-0.00091553 ,0.00054932 ,0.00018311 , 0.00045776 ,-0.00091553 ,-0.00204468 ,-0.00177002 ,-0.00183105 , -0.00183105 ,-0.00262451 ,-0.00231934 ,-0.00128174 ,-0.00131226 , -0.00152588 ,-0.00125122 ,-0.00146484 ,-0.00198364 ,-0.00216675 , -0.00280762 ,-0.00268555 ,-0.00241089 ,-0.00286865 ,-0.00250244 , -0.00247192 ,-0.00213623 ,-0.00289917 ,-0.00201416 ,-0.00238037 , -0.00231934 ,-0.00225830 ,-0.00286865 ,-0.00335693 ,-0.00335693 , -0.00430298 ,-0.00524902 ,-0.00552368 ,-0.00515747 ,-0.00466919 , -0.00439453 ,-0.00448608 ,-0.00399780 ,-0.00488281 ,-0.00494385 , -0.00573730 ,-0.00640869 ,-0.00451660 ,-0.00570679 ,-0.00479126 , -0.00497437 ,-0.00543213 ,-0.00466919 ,-0.00320435 ,-0.00244141 , -0.00283813 ,-0.00350952 ,-0.00366211 ,-0.00344849 ,-0.00552368 , -0.00433350 ,-0.00396729 ,-0.00262451 ,-0.00247192 ,-0.00189209 , -0.00256348 ,-0.00250244 ,-0.00323486 ,-0.00302124 ,-0.00457764 , -0.00427246 ,-0.00512695 ,-0.00466919 ,-0.00561523 ,-0.00650024 , -0.00512695 ,-0.00463867 ,-0.00393677 ,-0.00323486 ,-0.00485229 , -0.00390625 ,-0.00363159 ,-0.00219727 ,-0.00222778 ,-0.00225830 , -0.00222778 ,-0.00091553 ,-0.00164795 ,-0.00329590 ,-0.00335693 , -0.00482178 ,-0.00543213 ,-0.00350952 ,-0.00302124 ,-0.00268555 , -0.00299072 ,-0.00268555 ,-0.00393677 ,-0.00527954 ,-0.00479126 , -0.00567627 ,-0.00601196 ,-0.00549316 ,-0.00537109 ,-0.00457764 , -0.00299072 ,-0.00314331 ,-0.00225830 ,-0.00320435 ,-0.00256348 , -0.00244141 ,-0.00286865 ,-0.00314331 ,-0.00466919 ,-0.00329590 , -0.00280762 ,-0.00204468 ,-0.00274658 ,-0.00378418 ,-0.00195312 , -0.00277710 ,-0.00045776 ,-0.00100708 ,-0.00213623 ,-0.00137329 , -0.00189209 ,-0.00173950 ,-0.00228882 ,-0.00073242 ,-0.00106812 , -0.00048828 ,-0.00088501 ,0.00100708 ,0.00128174 ,0.00112915 , 0.00082397 ,0.00115967 ,0.00122070 ,0.00146484 ,0.00259399 , 0.00308228 ,0.00476074 ,0.00515747 ,0.00497437 ,0.00448608 , 0.00302124 ,0.00198364 ,0.00329590 ,0.00378418 ,0.00384521 , 0.00485229 ,0.00592041 ,0.00540161 ,0.00534058 ,0.00595093 , 0.00662231 ,0.00723267 ,0.00729370 ,0.00778198 ,0.00735474 , 0.00671387 ,0.00582886 ,0.00573730 ,0.00570679 ,0.00424194 , 0.00460815 ,0.00567627 ,0.00643921 ,0.00738525 ,0.00738525 , 0.00732422 ,0.00683594 ,0.00723267 ,0.00729370 ,0.00665283 , 0.00616455 ,0.00640869 ,0.00579834 ,0.00570679 ,0.00473022 , 0.00500488 ,0.00302124 ,0.00500488 ,0.00476074 ,0.00558472 , 0.00372314 ,0.00173950 ,0.00076294 ,-0.00036621 ,-0.00045776 , 0.00173950 ,0.00283813 ,0.00283813 ,0.00350952 ,0.00131226 , 0.00079346 ,0.00027466 ,-0.00128174 ,-0.00198364 ,-0.00271606 , -0.00204468 ,-0.00350952 ,-0.00445557 ,-0.00518799 ,-0.00415039 , -0.00552368 ,-0.00579834 ,-0.00643921 ,-0.00585938 ,-0.00619507 , -0.00555420 ,-0.00634766 ,-0.00546265 ,-0.00653076 ,-0.00588989 , -0.00634766 ,-0.00738525 ,-0.00585938 ,-0.00592041 ,-0.00494385 , -0.00366211 ,-0.00363159 ,-0.00360107 ,-0.00305176 ,-0.00265503 , -0.00067139 ,-0.00122070 ,0.00027466 ,0.00082397 ,-0.00039673 , -0.00048828 ,-0.00106812 ,-0.00036621 ,0.00070190 ,-0.00170898 , -0.00173950 ,-0.00076294 ,-0.00326538 ,-0.00299072 ,-0.00186157 , -0.00204468 ,-0.00173950 ,-0.00225830 ,-0.00250244 ,-0.00244141 , -0.00128174 ,-0.00088501 ,0.00048828 ,0.00057983 ,0.00006104 , 0.00125122 ,-0.00064087 ,0.00082397 ,0.00155640 ,0.00262451 , 0.00280762 ,0.00317383 ,0.00396729 ,0.00408936 ,0.00347900 , 0.00369263 ,0.00268555 ,0.00250244 ,0.00265503 ,0.00280762 , 0.00296021 ,0.00149536 ,0.00170898 ,0.00119019 ,0.00201416 , 0.00109863 ,0.00204468 ,0.00250244 ,0.00350952 ,0.00231934 , 0.00039673 ,0.00003052 ,-0.00024414 ,-0.00009155 ,0.00115967 , 0.00234985 ,0.00146484 ,0.00085449 ,0.00189209 ,0.00079346 , 0.00262451 ,0.00241089 ,0.00073242 ,0.00296021 ,0.00183105 , 0.00054932 ,0.00146484 ,-0.00045776 ,-0.00085449 ,-0.00115967 , -0.00247192 ,-0.00228882 ,-0.00247192 ,-0.00320435 ,-0.00314331 , -0.00265503 ,-0.00183105 ,-0.00256348 ,-0.00177002 ,0.00015259 , -0.00067139 ,-0.00128174 ,-0.00231934 ,-0.00256348 ,-0.00378418 , -0.00418091 ,-0.00393677 ,-0.00256348 ,-0.00265503 ,-0.00186157 , -0.00164795 ,-0.00100708 ,-0.00219727 ,-0.00314331 ,-0.00256348 , -0.00289917 ,-0.00109863 ,-0.00057983 ,0.00009155 ,0.00057983 , -0.00082397 ,-0.00094604 ,-0.00152588 ,-0.00024414 ,0.00100708 , 0.00140381 ,0.00192261 ,0.00289917 ,0.00238037 ,0.00256348 , 0.00469971 ,0.00332642 ,0.00439453 ,0.00421143 ,0.00357056 , 0.00466919 ,0.00491333 ,0.00387573 ,0.00430298 ,0.00491333 , 0.00332642 ,0.00372314 ,0.00405884 ,0.00335693 ,0.00180054 , 0.00271606 ,0.00286865 ,0.00250244 ,0.00250244 ,0.00183105 , 0.00158691 ,0.00106812 ,0.00177002 ,0.00045776 ,0.00033569 , -0.00140381 ,-0.00250244 ,-0.00064087 ,0.00015259 ,0.00000000 , 0.00097656 ,-0.00140381 ,-0.00155640 ,-0.00253296 ,-0.00262451 , -0.00259399 ,-0.00338745 ,-0.00314331 ,-0.00271606 ,-0.00265503 , -0.00207520 ,-0.00119019 ,-0.00244141 ,-0.00424194 ,-0.00540161 , -0.00549316 ,-0.00585938 ,-0.00659180 ,-0.00576782 ,-0.00650024 , -0.00576782 ,-0.00640869 ,-0.00521851 ,-0.00595093 ,-0.00524902 , -0.00640869 ,-0.00439453 ,-0.00543213 ,-0.00640869 ,-0.00607300 , -0.00595093 ,-0.00509644 ,-0.00442505 ,-0.00524902 ,-0.00372314 , -0.00274658 ,-0.00311279 ,-0.00067139 ,-0.00048828 ,-0.00070190 , -0.00143433 ,-0.00076294 ,-0.00073242 ,-0.00079346 ,-0.00088501 , -0.00006104 ,0.00161743 ,0.00125122 ,0.00137329 ,0.00183105 , 0.00112915 ,0.00122070 ,0.00082397 ,0.00253296 ,0.00198364 , 0.00277710 ,0.00198364 ,0.00079346 ,0.00106812 ,0.00195312 , 0.00082397 ,0.00137329 ,0.00128174 ,0.00134277 ,0.00070190 , 0.00170898 ,0.00097656 ,0.00122070 ,0.00155640 ,0.00183105 , 0.00115967 ,0.00158691 ,0.00189209 ,0.00027466 ,-0.00125122 , -0.00155640 ,-0.00164795 ,-0.00186157 ,0.00009155 ,-0.00076294 , 0.00042725 ,-0.00051880 ,-0.00167847 ,-0.00125122 ,-0.00192261 , -0.00082397 ,0.00000000 ,-0.00177002 ,-0.00079346 ,-0.00006104 , -0.00048828 ,0.00021362 ,0.00140381 ,0.00088501 ,0.00192261 , 0.00280762 ,0.00274658 ,0.00354004 ,0.00424194 ,0.00491333 , 0.00549316 ,0.00485229 ,0.00521851 ,0.00698853 ,0.00631714 , 0.00656128 ,0.00714111 ,0.00588989 ,0.00585938 ,0.00561523 , 0.00534058 ,0.00659180 ,0.00738525 ,0.00720215 ,0.00604248 , 0.00610352 ,0.00613403 ,0.00436401 ,0.00567627 ,0.00439453 , 0.00540161 ,0.00378418 ,0.00289917 ,0.00274658 ,0.00106812 , 0.00051880 ,0.00128174 ,0.00015259 ,-0.00051880 ,-0.00064087 , -0.00198364 ,-0.00268555 ,-0.00335693 ,-0.00253296 ,-0.00283813 , -0.00180054 ,-0.00027466 ,-0.00051880 ,-0.00091553 ,-0.00155640 , -0.00146484 ,-0.00125122 ,-0.00125122 ,-0.00134277 ,-0.00036621 , 0.00064087 ,-0.00024414 ,0.00061035 ,0.00082397 ,0.00128174 , 0.00347900 ,0.00335693 ,0.00320435 ,0.00344849 ,0.00387573 , 0.00503540 ,0.00378418 ,0.00366211 ,0.00405884 ,0.00283813 , 0.00543213 ,0.00555420 ,0.00466919 ,0.00454712 ,0.00466919 , 0.00448608 ,0.00454712 ,0.00662231 ,0.00543213 ,0.00628662 , 0.00549316 ,0.00567627 ,0.00479126 ,0.00570679 ,0.00329590 , 0.00347900 ,0.00234985 ,0.00253296 ,0.00228882 ,-0.00018311 , 0.00030518 ,-0.00079346 ,-0.00088501 ,0.00021362 ,-0.00100708 , -0.00164795 ,-0.00189209 ,-0.00238037 ,-0.00195312 ,-0.00253296 , -0.00433350 ,-0.00314331 ,-0.00299072 ,-0.00390625 ,-0.00363159 , -0.00360107 ,-0.00418091 ,-0.00476074 ,-0.00537109 ,-0.00451660 , -0.00527954 ,-0.00375366 ,-0.00375366 ,-0.00350952 ,-0.00305176 , -0.00418091 ,-0.00439453 ,-0.00656128 ,-0.00668335 ,-0.00775146 , -0.00698853 ,-0.00747681 ,-0.00778198 ,-0.00924683 ,-0.00744629 , -0.00866699 ,-0.00692749 ,-0.00701904 ,-0.00759888 ,-0.00747681 , -0.00747681 ,-0.00878906 ,-0.00735474 ,-0.00891113 ,-0.00857544 , -0.00866699 ,-0.00817871 ,-0.00827026 ,-0.00976562 ,-0.00839233 , -0.00808716 ,-0.00790405 ,-0.00799561 ,-0.00894165 ,-0.00863647 , -0.00881958 ,-0.01046753 ,-0.00903320 ,-0.00894165 ,-0.00787354 , -0.00723267 ,-0.00793457 ,-0.00662231 ,-0.00784302 ,-0.00634766 , -0.00692749 ,-0.00643921 ,-0.00527954 ,-0.00399780 ,-0.00283813 , -0.00274658 ,-0.00228882 ,-0.00128174 ,-0.00161743 ,-0.00094604 , -0.00027466 ,-0.00027466 ,-0.00012207 ,0.00027466 ,0.00033569 , -0.00003052 ,0.00115967 ,0.00238037 ,0.00207520 ,0.00247192 , 0.00192261 ,0.00354004 ,0.00360107 ,0.00436401 ,0.00476074 , 0.00466919 ,0.00411987 ,0.00311279 ,0.00363159 ,0.00375366 , 0.00384521 ,0.00140381 ,0.00256348 ,0.00198364 ,0.00140381 , 0.00146484 ,0.00192261 ,0.00305176 ,0.00088501 ,0.00143433 , 0.00070190 ,0.00033569 ,0.00125122 ,0.00094604 ,-0.00009155 , -0.00082397 ,-0.00207520 ,-0.00149536 ,-0.00164795 ,-0.00109863 , -0.00119019 ,-0.00119019 ,-0.00085449 ,-0.00167847 ,0.00027466 , 0.00115967 ,0.00009155 ,-0.00024414 ,-0.00170898 ,-0.00198364 , -0.00244141 ,-0.00299072 ,-0.00091553 ,-0.00091553 ,0.00036621 , -0.00106812 ,-0.00061035 ,-0.00170898 ,-0.00106812 ,-0.00085449 , -0.00061035 ,-0.00054932 ,-0.00054932 ,0.00106812 ,0.00103760 , 0.00186157 ,0.00234985 ,0.00363159 ,0.00299072 ,0.00387573 , 0.00411987 ,0.00537109 ,0.00582886 ,0.00494385 ,0.00454712 , 0.00497437 ,0.00521851 ,0.00463867 ,0.00558472 ,0.00537109 , 0.00512695 ,0.00323486 ,0.00411987 ,0.00430298 ,0.00445557 , 0.00500488 ,0.00546265 ,0.00592041 ,0.00534058 ,0.00567627 , 0.00613403 ,0.00527954 ,0.00604248 ,0.00671387 ,0.00588989 , 0.00695801 ,0.00860596 ,0.00857544 ,0.00817871 ,0.00836182 , 0.00741577 ,0.00656128 ,0.00839233 ,0.00799561 ,0.00735474 , 0.00717163 ,0.00735474 ,0.00674438 ,0.00735474 ,0.00775146 , 0.00845337 ,0.01007080 ,0.01007080 ,0.00921631 ,0.00817871 , 0.00802612 ,0.00668335 ,0.00714111 ,0.00668335 ,0.00668335 , 0.00839233 ,0.00738525 ,0.00610352 ,0.00497437 ,0.00344849 , 0.00335693 ,0.00177002 ,0.00131226 ,0.00134277 ,0.00164795 , 0.00149536 ,0.00326538 ,0.00241089 ,0.00177002 ,0.00051880 , 0.00012207 ,0.00061035 ,0.00024414 ,0.00134277 ,0.00048828 , -0.00015259 ,-0.00039673 ,0.00015259 ,0.00112915 ,-0.00009155 , 0.00033569 ,0.00094604 ,-0.00039673 ,-0.00042725 ,-0.00155640 , -0.00085449 ,-0.00180054 ,-0.00094604 ,-0.00204468 ,-0.00238037 , -0.00213623 ,-0.00207520 ,-0.00381470 ,-0.00347900 ,-0.00317383 , -0.00308228 ,-0.00296021 ,-0.00338745 ,-0.00448608 ,-0.00320435 , -0.00354004 ,-0.00244141 ,-0.00280762 ,-0.00201416 ,-0.00213623 , -0.00167847 ,-0.00204468 ,-0.00430298 ,-0.00393677 ,-0.00442505 , -0.00436401 ,-0.00222778 ,-0.00247192 ,-0.00177002 ,-0.00198364 , -0.00161743 ,-0.00149536 ,-0.00067139 ,-0.00146484 ,-0.00106812 , -0.00106812 ,-0.00091553 ,-0.00128174 ,-0.00079346 ,0.00048828 , 0.00180054 ,0.00155640 ,0.00073242 ,0.00146484 ,0.00183105 , 0.00189209 ,0.00192261 ,0.00280762 ,0.00247192 ,0.00302124 , 0.00241089 ,0.00262451 ,0.00152588 ,0.00045776 ,-0.00119019 , 0.00009155 ,-0.00073242 ,0.00009155 ,0.00109863 ,0.00335693 , 0.00366211 ,0.00280762 ,0.00250244 ,0.00186157 ,0.00100708 , -0.00012207 ,-0.00106812 ,-0.00094604 ,-0.00094604 ,0.00045776 , 0.00109863 ,0.00192261 ,0.00183105 ,0.00082397 ,0.00106812 , 0.00128174 ,0.00021362 ,-0.00018311 ,0.00018311 ,0.00115967 , 0.00045776 ,0.00015259 ,-0.00045776 ,-0.00137329 ,-0.00128174 , -0.00061035 ,-0.00134277 ,-0.00018311 ,-0.00131226 ,-0.00051880 , -0.00207520 ,-0.00204468 ,-0.00064087 ,-0.00195312 ,-0.00244141 , -0.00225830 ,-0.00204468 ,-0.00225830 ,-0.00256348 ,-0.00231934 , -0.00350952 ,-0.00097656 ,-0.00241089 ,-0.00140381 ,-0.00146484 , -0.00094604 ,-0.00170898 ,-0.00219727 ,-0.00296021 ,-0.00262451 , -0.00228882 ,-0.00213623 ,-0.00238037 ,-0.00323486 ,-0.00161743 , -0.00103760 ,-0.00161743 ,-0.00228882 ,-0.00238037 ,-0.00415039 , -0.00326538 ,-0.00360107 ,-0.00259399 ,-0.00396729 ,-0.00314331 , -0.00207520 ,-0.00186157 ,-0.00021362 ,-0.00183105 ,-0.00225830 , -0.00195312 ,-0.00164795 ,-0.00271606 ,-0.00149536 ,-0.00109863 , -0.00338745 ,-0.00137329 ,0.00015259 ,-0.00015259 ,0.00076294 , 0.00061035 ,-0.00027466 ,0.00180054 ,0.00024414 ,0.00134277 , -0.00021362 ,0.00061035 ,0.00100708 ,0.00082397 ,0.00140381 , 0.00119019 ,0.00192261 ,0.00225830 ,0.00091553 ,0.00045776 , -0.00009155 ,0.00122070 ,-0.00015259 ,-0.00024414 ,0.00103760 , 0.00170898 ,0.00048828 ,-0.00085449 ,-0.00051880 ,-0.00164795 , -0.00161743 ,-0.00115967 ,-0.00247192 ,-0.00354004 ,-0.00344849 , -0.00381470 ,-0.00424194 ,-0.00479126 ,-0.00424194 ,-0.00305176 , -0.00460815 ,-0.00500488 ,-0.00338745 ,-0.00451660 ,-0.00411987 , -0.00494385 ,-0.00534058 ,-0.00527954 ,-0.00579834 ,-0.00659180 , -0.00622559 ,-0.00527954 ,-0.00521851 ,-0.00582886 ,-0.00671387 , -0.00720215 ,-0.00723267 ,-0.00778198 ,-0.00814819 ,-0.00802612 , -0.00839233 ,-0.00799561 ,-0.00708008 ,-0.00726318 ,-0.00634766 , -0.00601196 ,-0.00601196 ,-0.00573730 ,-0.00595093 ,-0.00646973 , -0.00463867 ,-0.00457764 ,-0.00430298 ,-0.00189209 ,-0.00146484 , -0.00177002 ,-0.00097656 ,-0.00033569 ,-0.00079346 ,-0.00085449 , -0.00167847 ,-0.00146484 ,-0.00024414 ,0.00027466 ,-0.00030518 , 0.00012207 ,0.00173950 ,0.00140381 ,0.00225830 ,0.00201416 , 0.00222778 ,0.00329590 ,0.00320435 ,0.00390625 ,0.00469971 , 0.00442505 ,0.00460815 ,0.00598145 ,0.00451660 ,0.00650024 , 0.00653076 ,0.00637817 ,0.00680542 ,0.00671387 ,0.00817871 , 0.00653076 ,0.00665283 ,0.00750732 ,0.00854492 ,0.00711060 , 0.00759888 ,0.00827026 ,0.00735474 ,0.00799561 ,0.00769043 , 0.00662231 ,0.00793457 ,0.00646973 ,0.00616455 ,0.00561523 , 0.00579834 ,0.00631714 ,0.00650024 ,0.00512695 ,0.00460815 , 0.00399780 ,0.00402832 ,0.00415039 ,0.00509644 ,0.00515747 , 0.00521851 ,0.00405884 ,0.00399780 ,0.00302124 ,0.00228882 , 0.00277710 ,0.00274658 ,0.00180054 ,0.00219727 ,0.00137329 , 0.00155640 ,0.00231934 ,0.00149536 ,0.00103760 ,0.00106812 , -0.00012207 ,-0.00018311 ,0.00051880 ,0.00033569 ,0.00061035 , 0.00030518 ,-0.00006104 ,0.00015259 ,0.00018311 ,-0.00115967 , -0.00125122 ,-0.00195312 ,-0.00119019 ,-0.00067139 ,-0.00036621 , -0.00204468 ,-0.00164795 ,-0.00036621 ,-0.00018311 ,0.00009155 , 0.00115967 ,0.00164795 ,0.00161743 ,0.00311279 ,0.00289917 , 0.00320435 ,0.00286865 ,0.00244141 ,0.00268555 ,0.00366211 , 0.00454712 ,0.00509644 ,0.00534058 ,0.00537109 ,0.00500488 , 0.00460815 ,0.00445557 ,0.00314331 ,0.00195312 ,0.00265503 , 0.00311279 ,0.00256348 ,0.00204468 ,0.00366211 ,0.00375366 , 0.00402832 ,0.00314331 ,0.00317383 ,0.00247192 ,0.00158691 , 0.00042725 ,0.00051880 ,0.00106812 ,0.00122070 ,0.00122070 , 0.00122070 ,0.00183105 ,0.00238037 ,0.00289917 ,0.00198364 , 0.00195312 ,0.00155640 ,0.00152588 ,0.00112915 ,0.00128174 , 0.00238037 ,0.00186157 ,0.00021362 ,0.00018311 ,0.00042725 , -0.00094604 ,-0.00018311 ,0.00042725 ,-0.00201416 ,-0.00015259 , -0.00048828 ,-0.00222778 ,-0.00253296 ,-0.00149536 ,-0.00134277 , -0.00042725 ,-0.00152588 ,-0.00054932 ,-0.00103760 ,-0.00271606 , -0.00231934 ,-0.00357056 ,-0.00357056 ,-0.00289917 ,-0.00286865 , -0.00283813 ,-0.00299072 ,-0.00323486 ,-0.00332642 ,-0.00326538 , -0.00192261 ,-0.00393677 ,-0.00292969 ,-0.00512695 ,-0.00418091 , -0.00531006 ,-0.00466919 ,-0.00534058 ,-0.00497437 ,-0.00491333 , -0.00393677 ,-0.00436401 ,-0.00344849 ,-0.00238037 ,-0.00299072 , -0.00354004 ,-0.00314331 ,-0.00292969 ,-0.00427246 ,-0.00405884 , -0.00631714 ,-0.00625610 ,-0.00579834 ,-0.00521851 ,-0.00549316 , -0.00457764 ,-0.00366211 ,-0.00396729 ,-0.00408936 ,-0.00357056 , -0.00347900 ,-0.00219727 ,-0.00305176 ,-0.00241089 ,-0.00262451 , -0.00335693 ,-0.00341797 ,-0.00244141 ,-0.00219727 ,-0.00201416 , -0.00109863 ,-0.00064087 ,-0.00106812 ,-0.00103760 ,0.00030518 , -0.00045776 ,-0.00012207 ,0.00015259 ,0.00213623 ,0.00097656 , 0.00274658 ,0.00335693 ,0.00241089 ,0.00326538 ,0.00311279 , 0.00262451 ,0.00177002 ,0.00177002 ,0.00180054 ,0.00149536 , 0.00204468 ,0.00207520 ,0.00259399 ,0.00219727 ,0.00296021 , 0.00268555 ,0.00247192 ,0.00115967 ,0.00198364 ,0.00210571 , 0.00250244 ,0.00186157 ,0.00250244 ,0.00155640 ,0.00149536 , 0.00140381 ,0.00112915 ,0.00021362 ,0.00152588 ,0.00097656 , 0.00122070 ,0.00088501 ,-0.00015259 ,-0.00009155 ,-0.00073242 , -0.00219727 ,-0.00097656 ,-0.00137329 ,-0.00015259 ,-0.00097656 , -0.00091553 ,-0.00076294 ,-0.00234985 ,-0.00216675 ,-0.00314331 , -0.00177002 ,-0.00375366 ,-0.00265503 ,-0.00161743 ,-0.00100708 , -0.00033569 ,-0.00140381 ,-0.00054932 ,-0.00164795 ,-0.00250244 , -0.00347900 ,-0.00280762 ,-0.00357056 ,-0.00286865 ,-0.00280762 , -0.00262451 ,-0.00195312 ,-0.00180054 ,-0.00183105 ,-0.00173950 , -0.00131226 ,-0.00085449 ,-0.00036621 ,0.00054932 ,-0.00051880 , -0.00030518 ,0.00027466 ,-0.00036621 ,-0.00100708 ,0.00097656 , 0.00042725 ,0.00073242 ,0.00006104 ,0.00015259 ,0.00149536 , 0.00247192 ,0.00231934 ,0.00326538 ,0.00482178 ,0.00375366 , 0.00326538 ,0.00375366 ,0.00305176 ,0.00399780 ,0.00460815 , 0.00546265 ,0.00579834 ,0.00592041 ,0.00570679 ,0.00521851 , 0.00476074 ,0.00448608 ,0.00445557 ,0.00372314 ,0.00405884 , 0.00311279 ,0.00405884 ,0.00424194 ,0.00512695 ,0.00411987 , 0.00396729 ,0.00457764 ,0.00366211 ,0.00424194 ,0.00350952 , 0.00149536 ,0.00241089 ,0.00106812 ,0.00082397 ,0.00076294 , 0.00109863 ,0.00204468 ,0.00210571 ,0.00177002 ,0.00082397 , 0.00103760 ,-0.00045776 ,-0.00100708 ,-0.00109863 ,-0.00170898 , -0.00106812 ,-0.00256348 ,-0.00180054 ,-0.00128174 ,-0.00112915 , -0.00152588 ,-0.00125122 ,-0.00268555 ,-0.00173950 ,-0.00207520 , -0.00106812 ,-0.00189209 ,-0.00039673 ,-0.00091553 ,-0.00054932 , -0.00061035 ,-0.00253296 ,-0.00164795 ,-0.00067139 ,0.00021362 , -0.00036621 ,-0.00027466 ,-0.00033569 ,-0.00045776 ,0.00012207 , -0.00012207 ,0.00045776 ,0.00015259 ,0.00033569 ,-0.00131226 , -0.00100708 ,-0.00192261 ,-0.00128174 ,-0.00122070 ,-0.00073242 , -0.00045776 ,0.00064087 ,0.00030518 ,0.00039673 ,0.00210571 , 0.00201416 ,0.00201416 ,0.00158691 ,0.00073242 ,0.00152588 , 0.00241089 ,0.00234985 ,0.00344849 ,0.00354004 ,0.00277710 , 0.00308228 ,0.00277710 ,0.00292969 ,0.00341797 ,0.00344849 , 0.00454712 ,0.00466919 ,0.00515747 ,0.00335693 ,0.00222778 , 0.00433350 ,0.00390625 ,0.00430298 ,0.00393677 ,0.00350952 , 0.00393677 ,0.00433350 ,0.00283813 ,0.00149536 ,0.00097656 , 0.00189209 ,0.00112915 ,0.00024414 ,0.00073242 ,0.00146484 , 0.00079346 ,0.00085449 ,0.00125122 ,0.00076294 ,0.00122070 , 0.00228882 ,0.00137329 ,0.00088501 ,-0.00009155 ,-0.00082397 , 0.00000000 ,-0.00030518 ,-0.00018311 ,0.00006104 ,-0.00085449 , -0.00018311 ,-0.00109863 ,-0.00164795 ,-0.00189209 ,-0.00286865 , -0.00195312 ,-0.00296021 ,-0.00164795 ,-0.00170898 ,-0.00274658 , -0.00183105 ,-0.00143433 ,-0.00146484 ,-0.00180054 ,-0.00042725 , 0.00015259 ,-0.00045776 ,-0.00140381 ,-0.00106812 ,-0.00155640 , -0.00189209 ,-0.00112915 ,-0.00177002 ,-0.00106812 ,-0.00167847 , -0.00201416 ,-0.00054932 ,0.00000000 ,-0.00137329 ,-0.00152588 , -0.00094604 ,-0.00079346 ,-0.00277710 ,-0.00155640 ,-0.00186157 , 0.00021362 ,-0.00045776 ,-0.00039673 ,-0.00088501 ,-0.00079346 , -0.00167847 ,-0.00115967 ,-0.00164795 ,-0.00094604 ,0.00012207 , -0.00164795 ,-0.00097656 ,0.00009155 ,0.00033569 ,-0.00015259 , 0.00009155 ,-0.00106812 ,-0.00097656 ,-0.00064087 ,-0.00125122 , -0.00137329 ,-0.00146484 ,-0.00024414 ,-0.00027466 ,-0.00085449 , -0.00091553 ,-0.00073242 ,-0.00204468 ,-0.00225830 ,-0.00210571 , -0.00180054 ,-0.00140381 ,-0.00140381 ,-0.00051880 ,-0.00097656 , 0.00042725 ,0.00057983 ,0.00064087 ,0.00048828 ,0.00106812 , 0.00134277 ,0.00128174 ,0.00152588 ,0.00119019 ,0.00210571 , 0.00119019 ,0.00186157 ,0.00189209 ,0.00302124 ,0.00222778 , 0.00323486 ,0.00183105 ,0.00155640 ,0.00146484 ,0.00256348 , 0.00277710 ,0.00219727 ,0.00146484 ,0.00216675 ,0.00161743 , 0.00082397 ,0.00128174 ,0.00018311 ,0.00183105 ,-0.00018311 , 0.00036621 ,0.00018311 ,-0.00073242 ,-0.00125122 ,-0.00183105 , -0.00125122 ,-0.00216675 ,-0.00320435 ,-0.00296021 ,-0.00320435 , -0.00308228 ,-0.00253296 ,-0.00244141 ,-0.00195312 ,-0.00283813 , -0.00274658 ,-0.00299072 ,-0.00149536 ,-0.00271606 ,-0.00213623 , -0.00241089 ,-0.00198364 ,-0.00158691 ,-0.00070190 ,-0.00122070 , -0.00106812 ,-0.00036621 ,-0.00152588 ,-0.00119019 ,-0.00097656 , -0.00094604 ,0.00000000 ,0.00018311 ,-0.00051880 ,0.00103760 , 0.00115967 ,0.00045776 ,0.00112915 ,0.00161743 ,0.00115967 , -0.00003052 ,0.00042725 ,0.00134277 ,0.00119019 ,0.00173950 , 0.00259399 ,0.00329590 ,0.00357056 ,0.00427246 ,0.00326538 , 0.00387573 ,0.00430298 ,0.00509644 ,0.00549316 ,0.00579834 , 0.00473022 ,0.00506592 ,0.00601196 ,0.00503540 ,0.00430298 , 0.00457764 ,0.00393677 ,0.00485229 ,0.00573730 ,0.00524902 , 0.00430298 ,0.00280762 ,0.00430298 ,0.00344849 ,0.00259399 , 0.00271606 ,0.00296021 ,0.00192261 ,0.00177002 ,0.00033569 , 0.00173950 ,0.00213623 ,0.00198364 ,0.00219727 ,0.00134277 , 0.00091553 ,0.00164795 ,0.00238037 ,0.00155640 ,0.00155640 , 0.00173950 ,0.00210571 ,0.00173950 ,0.00198364 ,0.00222778 , 0.00268555 ,0.00213623 ,0.00180054 ,0.00173950 ,0.00158691 , 0.00189209 ,0.00250244 ,0.00167847 ,0.00231934 ,0.00125122 , 0.00183105 ,0.00082397 ,0.00131226 ,0.00140381 ,0.00094604 , 0.00064087 ,-0.00015259 ,0.00024414 ,0.00015259 ,-0.00140381 , -0.00067139 ,-0.00161743 ,-0.00134277 ,-0.00128174 ,-0.00057983 , -0.00051880 ,-0.00146484 ,-0.00054932 ,-0.00125122 ,0.00033569 , -0.00070190 ,-0.00067139 ,-0.00051880 ,-0.00021362 ,-0.00039673 , -0.00079346 ,-0.00192261 ,-0.00109863 ,-0.00021362 ,-0.00018311 , 0.00076294 ,-0.00064087 ,-0.00009155 ,0.00085449 ,0.00097656 , -0.00024414 ,-0.00030518 ,0.00003052 ,-0.00036621 ,0.00234985 , 0.00177002 ,0.00143433 ,0.00070190 ,0.00183105 ,0.00167847 , 0.00061035 ,0.00027466 ,0.00061035 ,0.00033569 ,0.00155640 , 0.00009155 ,0.00100708 ,0.00045776 ,0.00100708 ,-0.00018311 , 0.00012207 ,0.00051880 ,0.00039673 ,0.00030518 ,-0.00036621 , -0.00039673 ,-0.00003052 ,-0.00115967 ,-0.00140381 ,-0.00079346 , -0.00146484 ,-0.00280762 ,-0.00250244 ,-0.00326538 ,-0.00314331 , -0.00326538 ,-0.00366211 ,-0.00332642 ,-0.00317383 ,-0.00302124 , -0.00427246 ,-0.00274658 ,-0.00518799 ,-0.00384521 ,-0.00387573 , -0.00509644 ,-0.00296021 ,-0.00305176 ,-0.00311279 ,-0.00216675 , -0.00323486 ,-0.00109863 ,-0.00198364 ,-0.00280762 ,-0.00317383 , -0.00164795 ,-0.00244141 ,-0.00207520 ,-0.00216675 ,-0.00140381 , -0.00161743 ,-0.00146484 ,-0.00088501 ,-0.00106812 ,-0.00164795 , -0.00186157 ,-0.00173950 ,-0.00152588 ,-0.00332642 ,-0.00286865 , -0.00256348 ,-0.00170898 ,-0.00198364 ,-0.00173950 ,-0.00241089 , -0.00183105 ,-0.00314331 ,-0.00186157 ,-0.00198364 ,-0.00207520 , -0.00283813 ,-0.00354004 ,-0.00283813 ,-0.00183105 ,-0.00125122 , -0.00161743 ,-0.00109863 ,-0.00183105 ,-0.00332642 ,-0.00323486 , -0.00363159 ,-0.00277710 ,-0.00238037 ,-0.00143433 ,-0.00234985 , -0.00204468 ,-0.00296021 ,0.00012207 ,0.00036621 ,-0.00088501 , -0.00048828 ,-0.00082397 ,-0.00125122 ,-0.00073242 ,0.00045776 , -0.00045776 ,0.00082397 ,0.00006104 ,0.00027466 ,0.00155640 , 0.00195312 ,0.00213623 ,0.00271606 ,0.00213623 ,0.00360107 , 0.00357056 ,0.00387573 ,0.00341797 ,0.00399780 ,0.00393677 , 0.00372314 ,0.00494385 ,0.00415039 ,0.00448608 ,0.00384521 , 0.00418091 ,0.00460815 ,0.00488281 ,0.00555420 ,0.00576782 , 0.00448608 ,0.00491333 ,0.00518799 ,0.00430298 ,0.00292969 , 0.00311279 ,0.00256348 ,0.00204468 ,0.00186157 ,-0.00057983 , -0.00061035 ,-0.00085449 ,-0.00006104 ,-0.00067139 ,-0.00109863 , -0.00152588 ,-0.00155640 ,-0.00286865 ,-0.00231934 ,-0.00283813 , -0.00250244 ,-0.00283813 ,-0.00360107 ,-0.00283813 ,-0.00186157 , -0.00259399 ,-0.00308228 ,-0.00335693 ,-0.00225830 ,-0.00350952 , -0.00317383 ,-0.00241089 ,-0.00234985 ,-0.00207520 ,-0.00109863 , -0.00173950 ,-0.00091553 ,-0.00146484 ,-0.00164795 ,-0.00238037 , -0.00131226 ,-0.00244141 ,-0.00170898 ,-0.00155640 ,-0.00137329 , -0.00045776 ,0.00033569 ,0.00207520 ,0.00079346 ,0.00112915 , 0.00085449 ,0.00125122 ,0.00097656 ,0.00064087 ,0.00198364 , 0.00076294 ,0.00122070 ,0.00186157 ,0.00100708 ,0.00167847 , 0.00149536 ,0.00180054 ,0.00213623 ,0.00131226 ,0.00155640 , 0.00170898 ,0.00115967 ,0.00082397 ,0.00006104 ,0.00128174 , 0.00027466 ,0.00070190 ,0.00061035 ,-0.00003052 ,0.00015259 , 0.00097656 ,0.00146484 ,0.00064087 ,0.00061035 ,0.00189209 , 0.00195312 ,0.00222778 ,0.00149536 ,0.00271606 ,0.00317383 , 0.00378418 ,0.00332642 ,0.00341797 ,0.00387573 ,0.00360107 , 0.00277710 ,0.00454712 ,0.00506592 ,0.00491333 ,0.00494385 , 0.00363159 ,0.00341797 ,0.00399780 ,0.00482178 ,0.00320435 , 0.00283813 ,0.00311279 ,0.00460815 ,0.00289917 ,0.00283813 , 0.00244141 ,0.00201416 ,0.00119019 ,0.00265503 ,0.00042725 , 0.00018311 ,-0.00094604 ,-0.00057983 ,-0.00091553 ,-0.00115967 , -0.00064087 ,-0.00204468 ,-0.00198364 ,-0.00216675 ,-0.00231934 , -0.00173950 ,-0.00296021 ,-0.00402832 ,-0.00369263 ,-0.00146484 , -0.00271606 ,-0.00335693 ,-0.00302124 ,-0.00238037 ,-0.00134277 , -0.00192261 ,-0.00161743 ,-0.00103760 ,-0.00228882 ,-0.00277710 , -0.00238037 ,-0.00170898 ,-0.00152588 ,-0.00173950 ,-0.00311279 , -0.00155640 ,-0.00106812 ,-0.00167847 ,-0.00131226 ,-0.00195312 , -0.00119019 ,-0.00125122 ,-0.00064087 ,0.00064087 ,-0.00030518 , 0.00006104 ,0.00064087 ,0.00030518 ,-0.00045776 ,-0.00030518 , 0.00131226 ,0.00143433 ,0.00070190 ,0.00149536 ,0.00155640 , 0.00106812 ,0.00161743 ,0.00305176 ,0.00280762 ,0.00302124 , 0.00241089 ,0.00225830 ,0.00177002 ,0.00253296 ,0.00228882 , 0.00250244 ,0.00384521 ,0.00393677 ,0.00418091 ,0.00427246 , 0.00479126 ,0.00442505 ,0.00515747 ,0.00488281 ,0.00466919 , 0.00622559 ,0.00610352 ,0.00543213 ,0.00564575 ,0.00573730 , 0.00521851 ,0.00335693 ,0.00354004 ,0.00390625 ,0.00408936 , 0.00439453 ,0.00430298 ,0.00326538 ,0.00253296 ,0.00213623 , 0.00250244 ,0.00326538 ,0.00210571 ,0.00192261 ,0.00149536 , 0.00268555 ,0.00250244 ,0.00213623 ,0.00173950 ,0.00131226 , 0.00009155 ,0.00024414 ,0.00015259 ,0.00009155 ,-0.00079346 , -0.00021362 ,-0.00073242 ,-0.00216675 ,-0.00100708 ,-0.00158691 , -0.00177002 ,-0.00250244 ,-0.00357056 ,-0.00378418 ,-0.00357056 , -0.00500488 ,-0.00411987 ,-0.00454712 ,-0.00485229 ,-0.00363159 , -0.00390625 ,-0.00408936 ,-0.00469971 ,-0.00460815 ,-0.00497437 , -0.00549316 ,-0.00488281 ,-0.00463867 ,-0.00479126 ,-0.00332642 , -0.00390625 ,-0.00210571 ,-0.00244141 ,-0.00234985 ,-0.00225830 , -0.00192261 ,-0.00256348 ,-0.00323486 ,-0.00250244 ,-0.00357056 , -0.00344849 ,-0.00320435 ,-0.00430298 ,-0.00320435 ,-0.00238037 , -0.00311279 ,-0.00238037 ,-0.00201416 ,-0.00259399 ,-0.00296021 , -0.00344849 ,-0.00323486 ,-0.00360107 ,-0.00445557 ,-0.00390625 , -0.00314331 ,-0.00262451 ,-0.00238037 ,-0.00259399 ,-0.00225830 , -0.00143433 ,-0.00186157 ,-0.00286865 ,-0.00170898 ,-0.00146484 , -0.00161743 ,-0.00204468 ,-0.00213623 ,-0.00106812 ,-0.00231934 , -0.00186157 ,-0.00170898 ,-0.00253296 ,-0.00192261 ,-0.00170898 , -0.00155640 ,-0.00088501 ,-0.00170898 ,-0.00204468 ,-0.00268555 , -0.00231934 ,-0.00277710 ,-0.00274658 ,-0.00393677 ,-0.00338745 , -0.00357056 ,-0.00320435 ,-0.00335693 ,-0.00201416 ,-0.00216675 , -0.00305176 ,-0.00238037 ,-0.00274658 ,-0.00152588 ,-0.00231934 , -0.00119019 ,-0.00143433 ,-0.00216675 ,-0.00195312 ,-0.00180054 , -0.00024414 ,-0.00003052 ,-0.00003052 ,0.00054932 ,0.00039673 , 0.00119019 ,0.00054932 ,-0.00021362 ,0.00088501 ,0.00149536 , 0.00122070 ,0.00216675 ,0.00033569 ,0.00057983 ,0.00048828 , 0.00100708 ,0.00030518 ,0.00042725 ,-0.00030518 ,0.00000000 , 0.00067139 ,0.00146484 ,0.00079346 ,0.00067139 ,-0.00036621 , 0.00122070 ,0.00106812 ,0.00100708 ,0.00109863 ,0.00186157 , 0.00079346 ,0.00183105 ,0.00167847 ,0.00225830 ,0.00149536 , 0.00216675 ,0.00347900 ,0.00286865 ,0.00354004 ,0.00399780 , 0.00280762 ,0.00363159 ,0.00427246 ,0.00408936 ,0.00454712 , 0.00448608 ,0.00598145 ,0.00506592 ,0.00576782 ,0.00662231 , 0.00668335 ,0.00653076 ,0.00787354 ,0.00756836 ,0.00683594 , 0.00793457 ,0.00823975 ,0.00738525 ,0.00680542 ,0.00689697 , 0.00579834 ,0.00674438 ,0.00576782 ,0.00555420 ,0.00683594 , 0.00677490 ,0.00665283 ,0.00680542 ,0.00668335 ,0.00708008 , 0.00738525 ,0.00616455 ,0.00756836 ,0.00509644 ,0.00643921 , 0.00637817 ,0.00634766 ,0.00527954 ,0.00488281 ,0.00497437 , 0.00485229 ,0.00344849 ,0.00390625 ,0.00381470 ,0.00326538 , 0.00283813 ,0.00415039 ,0.00259399 ,0.00210571 ,0.00308228 , 0.00164795 ,0.00314331 ,0.00326538 ,0.00360107 ,0.00430298 , 0.00442505 ,0.00369263 ,0.00244141 ,0.00289917 ,0.00253296 , 0.00180054 ,0.00189209 ,0.00244141 ,0.00094604 ,0.00192261 , 0.00265503 ,0.00317383 ,0.00186157 ,0.00207520 ,0.00238037 , 0.00213623 ,0.00195312 ,0.00253296 ,0.00286865 ,0.00225830 , 0.00323486 ,0.00192261 ,0.00131226 ,0.00122070 ,0.00164795 , 0.00149536 ,0.00219727 ,0.00238037 ,0.00137329 ,0.00262451 , 0.00012207 ,0.00064087 ,0.00228882 ,0.00082397 ,-0.00003052 , 0.00088501 ,0.00051880 ,0.00015259 ,0.00094604 ,-0.00079346 , -0.00003052 ,0.00042725 ,0.00003052 ,0.00054932 ,-0.00109863 , 0.00018311 ,0.00054932 ,-0.00158691 ,-0.00070190 ,-0.00030518 , -0.00067139 ,-0.00094604 ,-0.00119019 ,0.00021362 ,-0.00051880 , -0.00119019 ,-0.00097656 ,-0.00109863 ,0.00091553 ,0.00039673 , -0.00036621 ,0.00250244 ,0.00018311 ,0.00122070 ,0.00057983 , 0.00057983 ,-0.00070190 ,0.00024414 ,-0.00039673 ,0.00112915 , 0.00003052 ,0.00021362 ,0.00076294 ,0.00021362 ,0.00106812 , 0.00000000 ,0.00024414 ,0.00012207 ,0.00006104 ,-0.00106812 , -0.00192261 ,-0.00094604 ,-0.00283813 ,-0.00164795 ,-0.00207520 , -0.00192261 ,-0.00289917 ,-0.00231934 ,-0.00146484 ,-0.00283813 , -0.00170898 ,-0.00308228 ,-0.00283813 ,-0.00231934 ,-0.00164795 , -0.00329590 ,-0.00231934 ,-0.00311279 ,-0.00433350 ,-0.00439453 , -0.00473022 ,-0.00494385 ,-0.00375366 ,-0.00357056 ,-0.00369263 , -0.00256348 ,-0.00372314 ,-0.00265503 ,-0.00225830 ,-0.00274658 , -0.00274658 ,-0.00262451 ,-0.00192261 ,-0.00210571 ,-0.00335693 , -0.00210571 ,-0.00250244 ,-0.00192261 ,-0.00119019 ,-0.00128174 , -0.00128174 ,-0.00167847 ,-0.00216675 ,-0.00268555 ,-0.00329590 , -0.00180054 ,-0.00170898 ,-0.00015259 ,-0.00100708 ,-0.00067139 , -0.00018311 ,-0.00033569 ,-0.00097656 ,-0.00067139 ,0.00079346 , -0.00021362 ,0.00015259 ,0.00079346 ,-0.00018311 ,0.00064087 , 0.00137329 ,0.00057983 ,0.00152588 ,0.00064087 ,0.00125122 , 0.00140381 ,0.00088501 ,0.00076294 ,0.00106812 ,0.00036621 , 0.00122070 ,0.00125122 ,0.00146484 ,0.00244141 ,0.00241089 , 0.00198364 ,0.00305176 ,0.00366211 ,0.00296021 ,0.00360107 , 0.00256348 ,0.00256348 ,0.00241089 ,0.00296021 ,0.00280762 , 0.00344849 ,0.00296021 ,0.00253296 ,0.00256348 ,0.00234985 , 0.00216675 ,0.00213623 ,0.00112915 ,0.00091553 ,0.00109863 , 0.00085449 ,0.00088501 ,0.00012207 ,-0.00051880 ,-0.00003052 , -0.00012207 ,-0.00140381 ,-0.00125122 ,-0.00207520 ,-0.00216675 , -0.00216675 ,-0.00192261 ,-0.00262451 ,-0.00302124 ,-0.00259399 , -0.00122070 ,-0.00149536 ,-0.00155640 ,-0.00152588 ,-0.00122070 , -0.00268555 ,-0.00292969 ,-0.00347900 ,-0.00210571 ,-0.00384521 , -0.00363159 ,-0.00335693 ,-0.00384521 ,-0.00534058 ,-0.00552368 , -0.00540161 ,-0.00476074 ,-0.00448608 ,-0.00457764 ,-0.00332642 , -0.00402832 ,-0.00332642 ,-0.00280762 ,-0.00393677 ,-0.00274658 , -0.00369263 ,-0.00329590 ,-0.00302124 ,-0.00369263 ,-0.00238037 , -0.00286865 ,-0.00216675 ,-0.00244141 ,-0.00247192 ,-0.00131226 , -0.00070190 ,-0.00137329 ,-0.00091553 ,-0.00164795 ,-0.00073242 , -0.00125122 ,-0.00149536 ,-0.00158691 ,-0.00082397 ,-0.00106812 , -0.00170898 ,-0.00061035 ,-0.00137329 ,0.00033569 ,-0.00042725 , 0.00079346 ,0.00003052 ,0.00006104 ,0.00012207 ,0.00180054 , 0.00143433 ,0.00115967 ,0.00137329 ,0.00189209 ,0.00259399 , 0.00231934 ,0.00250244 ,0.00219727 ,0.00335693 ,0.00195312 , 0.00265503 ,0.00222778 ,0.00262451 ,0.00265503 ,0.00335693 , 0.00387573 ,0.00277710 ,0.00338745 ,0.00341797 ,0.00238037 , 0.00204468 ,0.00195312 ,0.00186157 ,0.00131226 ,0.00186157 , 0.00189209 ,0.00311279 ,0.00177002 ,0.00149536 ,0.00143433 , 0.00164795 ,0.00140381 ,0.00115967 ,0.00180054 ,0.00106812 , 0.00225830 ,0.00003052 ,-0.00018311 ,-0.00015259 ,0.00030518 , 0.00082397 ,0.00091553 ,0.00289917 ,0.00222778 ,0.00299072 , 0.00183105 ,0.00238037 ,0.00140381 ,0.00198364 ,0.00189209 , 0.00091553 ,0.00128174 ,0.00216675 ,0.00283813 ,0.00057983 , 0.00146484 ,0.00228882 ,0.00161743 ,0.00064087 ,-0.00018311 , 0.00057983 ,0.00027466 ,0.00064087 ,0.00085449 ,0.00076294 , 0.00021362 ,-0.00021362 ,0.00051880 ,0.00222778 ,0.00158691 , 0.00082397 ,0.00103760 ,0.00085449 ,0.00033569 ,0.00115967 , 0.00128174 ,0.00018311 ,0.00183105 ,0.00192261 ,0.00207520 , 0.00198364 ,0.00198364 ,0.00317383 ,0.00338745 ,0.00366211 , 0.00427246 ,0.00332642 ,0.00296021 ,0.00302124 ,0.00415039 , 0.00451660 ,0.00454712 ,0.00259399 ,0.00454712 ,0.00460815 , 0.00460815 ,0.00451660 ,0.00518799 ,0.00411987 ,0.00500488 , 0.00564575 ,0.00506592 ,0.00598145 ,0.00473022 ,0.00430298 , 0.00457764 ,0.00436401 ,0.00408936 ,0.00473022 ,0.00415039 , 0.00503540 ,0.00408936 ,0.00381470 ,0.00384521 ,0.00411987 , 0.00354004 ,0.00283813 ,0.00286865 ,0.00369263 ,0.00225830 , 0.00222778 ,0.00180054 ,0.00189209 ,0.00131226 ,0.00137329 , 0.00079346 ,-0.00048828 ,0.00012207 ,-0.00067139 ,-0.00177002 , -0.00189209 ,-0.00177002 ,-0.00177002 ,-0.00170898 ,-0.00268555 , -0.00271606 ,-0.00280762 ,-0.00274658 ,-0.00381470 ,-0.00473022 , -0.00360107 ,-0.00363159 ,-0.00451660 ,-0.00387573 ,-0.00384521 , -0.00387573 ,-0.00482178 ,-0.00466919 ,-0.00424194 ,-0.00543213 , -0.00445557 ,-0.00494385 ,-0.00448608 ,-0.00518799 ,-0.00558472 , -0.00610352 ,-0.00497437 ,-0.00558472 ,-0.00524902 ,-0.00573730 , -0.00418091 ,-0.00393677 ,-0.00497437 ,-0.00415039 ,-0.00375366 , -0.00350952 ,-0.00344849 ,-0.00454712 ,-0.00338745 ,-0.00311279 , -0.00323486 ,-0.00399780 ,-0.00503540 ,-0.00372314 ,-0.00323486 , -0.00411987 ,-0.00384521 ,-0.00335693 ,-0.00238037 ,-0.00384521 , -0.00155640 ,-0.00222778 ,-0.00213623 ,-0.00186157 ,-0.00204468 , -0.00067139 ,-0.00106812 ,-0.00039673 ,-0.00152588 ,-0.00067139 , -0.00051880 ,0.00033569 ,-0.00024414 ,0.00024414 ,-0.00045776 , -0.00012207 ,0.00100708 ,0.00073242 ,0.00198364 ,0.00158691 , 0.00036621 ,0.00122070 ,0.00137329 ,0.00140381 ,0.00131226 , 0.00213623 ,0.00164795 ,0.00167847 ,0.00091553 ,0.00039673 , 0.00128174 ,0.00216675 ,0.00225830 ,0.00134277 ,0.00119019 , 0.00213623 ,0.00140381 ,0.00173950 ,0.00207520 ,0.00076294 , 0.00051880 ,0.00015259 ,-0.00048828 ,-0.00070190 ,-0.00064087 , -0.00158691 ,-0.00137329 ,-0.00134277 ,-0.00274658 ,-0.00238037 , -0.00149536 ,-0.00207520 ,-0.00180054 ,-0.00241089 ,-0.00253296 , -0.00329590 ,-0.00375366 ,-0.00320435 ,-0.00140381 ,-0.00317383 , -0.00302124 ,-0.00253296 ,-0.00262451 ,-0.00100708 ,-0.00146484 , -0.00115967 ,0.00012207 ,-0.00140381 ,0.00018311 ,-0.00061035 , -0.00027466 ,-0.00079346 ,0.00067139 ,0.00045776 ,0.00143433 , 0.00122070 ,0.00241089 ,0.00280762 ,0.00222778 ,0.00296021 , 0.00424194 ,0.00469971 ,0.00469971 ,0.00424194 ,0.00494385 , 0.00488281 ,0.00534058 ,0.00537109 ,0.00650024 ,0.00601196 , 0.00631714 ,0.00570679 ,0.00790405 ,0.00616455 ,0.00683594 , 0.00659180 ,0.00537109 ,0.00555420 ,0.00558472 ,0.00592041 , 0.00497437 ,0.00598145 ,0.00418091 ,0.00454712 ,0.00518799 , 0.00442505 ,0.00491333 ,0.00518799 ,0.00570679 ,0.00549316 , 0.00515747 ,0.00604248 ,0.00598145 ,0.00585938 ,0.00616455 , 0.00521851 ,0.00506592 ,0.00555420 ,0.00427246 ,0.00552368 , 0.00466919 ,0.00576782 ,0.00506592 ,0.00494385 ,0.00534058 , 0.00308228 ,0.00277710 ,0.00238037 ,0.00305176 ,0.00228882 , 0.00387573 ,0.00238037 ,0.00192261 ,0.00015259 ,0.00115967 , 0.00134277 ,0.00006104 ,0.00039673 ,-0.00061035 ,-0.00024414 , -0.00170898 ,-0.00234985 ,-0.00039673 ,-0.00186157 ,-0.00173950 , -0.00183105 ,-0.00198364 ,-0.00164795 ,-0.00244141 ,-0.00326538 , -0.00375366 ,-0.00341797 ,-0.00329590 ,-0.00268555 ,-0.00323486 , -0.00277710 ,-0.00225830 ,-0.00326538 ,-0.00329590 ,-0.00241089 , -0.00268555 ,-0.00329590 ,-0.00219727 ,-0.00268555 ,-0.00228882 , -0.00219727 ,-0.00210571 ,-0.00244141 ,-0.00134277 ,-0.00216675 , -0.00094604 ,-0.00125122 ,-0.00231934 ,-0.00131226 ,-0.00253296 , -0.00173950 ,-0.00219727 ,-0.00143433 ,-0.00131226 ,-0.00143433 , -0.00070190 ,0.00003052 ,0.00091553 ,0.00036621 ,-0.00009155 , 0.00128174 ,-0.00097656 ,0.00024414 ,-0.00088501 ,0.00033569 , 0.00018311 ,0.00109863 ,0.00012207 ,0.00170898 ,0.00225830 , 0.00161743 ,0.00082397 ,0.00180054 ,0.00131226 ,0.00039673 , -0.00012207 ,0.00009155 ,-0.00067139 ,-0.00119019 ,-0.00027466 , -0.00149536 ,-0.00009155 ,-0.00070190 ,-0.00003052 ,-0.00082397 , -0.00103760 ,-0.00067139 ,-0.00106812 ,-0.00027466 ,-0.00103760 , -0.00100708 ,-0.00003052 ,-0.00009155 ,0.00003052 ,-0.00012207 , -0.00012207 ,-0.00076294 ,-0.00021362 ,-0.00030518 ,-0.00042725 , -0.00015259 ,-0.00012207 ,-0.00057983 ,-0.00131226 ,-0.00076294 , -0.00119019 ,0.00003052 ,-0.00112915 ,-0.00070190 ,-0.00012207 , -0.00146484 ,-0.00100708 ,-0.00061035 ,-0.00039673 ,-0.00158691 , -0.00115967 ,-0.00085449 ,-0.00106812 ,-0.00177002 ,-0.00106812 , -0.00061035 ,-0.00009155 ,0.00091553 ,-0.00122070 ,-0.00021362 , 0.00003052 ,-0.00024414 ,-0.00042725 ,-0.00140381 ,0.00048828 , 0.00003052 ,0.00009155 ,-0.00076294 ,0.00064087 ,0.00012207 , 0.00085449 ,-0.00024414 ,0.00036621 ,0.00073242 ,0.00079346 , 0.00057983 ,0.00039673 ,0.00094604 ,0.00167847 ,0.00201416 , 0.00329590 ,0.00256348 ,0.00228882 ,0.00216675 ,0.00308228 , 0.00344849 ,0.00308228 ,0.00418091 ,0.00482178 ,0.00372314 , 0.00415039 ,0.00454712 ,0.00546265 ,0.00512695 ,0.00619507 , 0.00463867 ,0.00708008 ,0.00637817 ,0.00634766 ,0.00561523 , 0.00482178 ,0.00534058 ,0.00473022 ,0.00588989 ,0.00457764 , 0.00500488 ,0.00488281 ,0.00424194 ,0.00363159 ,0.00488281 , 0.00451660 ,0.00424194 ,0.00341797 ,0.00344849 ,0.00366211 , 0.00372314 ,0.00283813 ,0.00225830 ,0.00271606 ,0.00244141 , 0.00305176 ,0.00122070 ,0.00177002 ,0.00167847 ,0.00115967 , 0.00122070 ,0.00088501 ,0.00027466 ,0.00064087 ,0.00045776 , -0.00027466 ,-0.00097656 ,-0.00219727 ,-0.00109863 ,-0.00195312 , -0.00228882 ,-0.00259399 ,-0.00222778 ,-0.00198364 ,-0.00137329 , -0.00134277 ,-0.00241089 ,-0.00308228 ,-0.00186157 ,-0.00231934 , -0.00332642 ,-0.00347900 ,-0.00320435 ,-0.00247192 ,-0.00418091 , -0.00350952 ,-0.00244141 ,-0.00405884 ,-0.00387573 ,-0.00363159 , -0.00338745 ,-0.00344849 ,-0.00399780 ,-0.00277710 ,-0.00292969 , -0.00241089 ,-0.00415039 ,-0.00305176 ,-0.00247192 ,-0.00384521 , -0.00302124 ,-0.00335693 ,-0.00122070 ,-0.00338745 ,-0.00302124 , -0.00384521 ,-0.00277710 ,-0.00274658 ,-0.00045776 ,-0.00134277 , -0.00082397 ,-0.00115967 ,-0.00137329 ,-0.00152588 ,-0.00024414 , -0.00073242 ,-0.00128174 ,-0.00027466 ,-0.00006104 ,0.00000000 , 0.00106812 ,0.00201416 ,0.00103760 ,0.00042725 ,0.00134277 , 0.00305176 ,0.00271606 ,0.00344849 ,0.00375366 ,0.00311279 , 0.00445557 ,0.00393677 ,0.00482178 ,0.00430298 ,0.00381470 , 0.00372314 ,0.00430298 ,0.00454712 ,0.00445557 ,0.00512695 , 0.00540161 ,0.00479126 ,0.00421143 ,0.00488281 ,0.00393677 , 0.00512695 ,0.00457764 ,0.00527954 ,0.00466919 ,0.00537109 , 0.00466919 ,0.00405884 ,0.00354004 ,0.00311279 ,0.00363159 , 0.00317383 ,0.00390625 ,0.00283813 ,0.00259399 ,0.00256348 , 0.00283813 ,0.00259399 ,0.00125122 ,0.00192261 ,0.00225830 , 0.00262451 ,0.00231934 ,0.00207520 ,0.00112915 ,0.00088501 , 0.00152588 ,0.00152588 ,0.00103760 ,0.00189209 ,0.00213623 , 0.00100708 ,0.00195312 ,0.00280762 ,0.00308228 ,0.00265503 , 0.00271606 ,0.00451660 ,0.00317383 ,0.00280762 ,0.00231934 , 0.00302124 ,0.00231934 ,0.00292969 ,0.00354004 ,0.00332642 , 0.00387573 ,0.00375366 ,0.00366211 ,0.00381470 ,0.00363159 , 0.00350952 ,0.00335693 ,0.00463867 ,0.00421143 ,0.00552368 , 0.00521851 ,0.00378418 ,0.00485229 ,0.00427246 ,0.00427246 , 0.00482178 ,0.00558472 ,0.00656128 ,0.00631714 ,0.00598145 , 0.00601196 ,0.00564575 ,0.00650024 ,0.00564575 ,0.00543213 , 0.00717163 ,0.00671387 ,0.00585938 ,0.00439453 ,0.00573730 , 0.00430298 ,0.00454712 ,0.00347900 ,0.00317383 ,0.00402832 , 0.00317383 ,0.00390625 ,0.00326538 ,0.00323486 ,0.00344849 , 0.00375366 ,0.00277710 ,0.00427246 ,0.00231934 ,0.00262451 , 0.00259399 ,0.00338745 ,0.00189209 ,0.00216675 ,0.00158691 , 0.00064087 ,0.00128174 ,0.00054932 ,0.00073242 ,-0.00003052 , 0.00082397 ,0.00183105 ,0.00122070 ,0.00067139 ,-0.00006104 , -0.00030518 ,-0.00137329 ,-0.00091553 ,-0.00042725 ,-0.00183105 , -0.00125122 ,-0.00149536 ,-0.00201416 ,-0.00158691 ,-0.00231934 , -0.00183105 ,-0.00344849 ,-0.00259399 ,-0.00262451 ,-0.00305176 , -0.00314331 ,-0.00274658 ,-0.00253296 ,-0.00299072 ,-0.00244141 , -0.00320435 ,-0.00424194 ,-0.00305176 ,-0.00259399 ,-0.00234985 , -0.00311279 ,-0.00231934 ,-0.00241089 ,-0.00213623 ,-0.00198364 , -0.00189209 ,-0.00167847 ,-0.00195312 ,-0.00091553 ,-0.00183105 , -0.00146484 ,-0.00122070 ,-0.00167847 ,-0.00234985 ,-0.00292969 , -0.00204468 ,-0.00366211 ,-0.00280762 ,-0.00399780 ,-0.00381470 , -0.00225830 ,-0.00280762 ,-0.00231934 ,-0.00311279 ,-0.00317383 , -0.00231934 ,-0.00192261 ,-0.00143433 ,-0.00274658 ,-0.00204468 , -0.00192261 ,-0.00268555 ,-0.00177002 ,-0.00149536 ,-0.00067139 , -0.00076294 ,-0.00186157 ,-0.00244141 ,-0.00140381 ,-0.00186157 , -0.00173950 ,-0.00161743 ,-0.00097656 ,-0.00106812 ,-0.00012207 , -0.00003052 ,0.00057983 ,-0.00009155 ,0.00015259 ,0.00119019 , 0.00048828 ,0.00125122 ,0.00070190 ,0.00128174 ,0.00137329 , 0.00106812 ,0.00164795 ,0.00189209 ,0.00198364 ,0.00222778 , 0.00219727 ,0.00207520 ,0.00265503 ,0.00341797 ,0.00265503 , 0.00259399 ,0.00256348 ,0.00228882 ,0.00323486 ,0.00289917 , 0.00366211 ,0.00305176 ,0.00192261 ,0.00250244 ,0.00234985 , 0.00238037 ,0.00134277 ,0.00091553 ,0.00054932 ,0.00100708 , 0.00042725 ,-0.00024414 ,-0.00039673 ,-0.00033569 ,0.00018311 , 0.00115967 ,0.00085449 ,0.00036621 ,0.00051880 ,-0.00027466 , -0.00042725 ,-0.00024414 ,-0.00036621 ,-0.00073242 ,-0.00051880 , 0.00051880 ,0.00061035 ,-0.00015259 ,-0.00112915 ,-0.00079346 , 0.00021362 ,0.00045776 ,-0.00125122 ,-0.00030518 ,-0.00173950 , -0.00021362 ,-0.00033569 ,-0.00070190 ,0.00079346 ,-0.00036621 , -0.00006104 ,-0.00042725 ,-0.00051880 ,0.00000000 ,-0.00030518 , 0.00137329 ,0.00073242 ,0.00137329 ,0.00097656 ,0.00143433 , 0.00250244 ,0.00210571 ,0.00308228 ,0.00296021 ,0.00253296 , 0.00262451 ,0.00271606 ,0.00122070 ,0.00296021 ,0.00259399 , 0.00268555 ,0.00289917 ,0.00283813 ,0.00161743 ,0.00210571 , 0.00311279 ,0.00341797 ,0.00350952 ,0.00250244 ,0.00238037 , 0.00268555 ,0.00170898 ,0.00213623 ,0.00119019 ,0.00048828 , 0.00036621 ,0.00112915 ,0.00003052 ,0.00012207 ,0.00006104 , 0.00109863 ,0.00119019 ,0.00048828 ,0.00018311 ,-0.00012207 , -0.00021362 ,-0.00061035 ,-0.00158691 ,-0.00131226 ,-0.00164795 , -0.00106812 ,-0.00006104 ,-0.00225830 ,-0.00094604 ,-0.00170898 , -0.00039673 ,-0.00088501 ,-0.00201416 ,-0.00170898 ,-0.00100708 , -0.00103760 ,-0.00054932 ,-0.00183105 ,-0.00076294 ,-0.00210571 , -0.00234985 ,-0.00265503 ,-0.00167847 ,-0.00216675 ,-0.00253296 , -0.00299072 ,-0.00354004 ,-0.00305176 ,-0.00189209 ,-0.00207520 , -0.00244141 ,-0.00225830 ,-0.00363159 ,-0.00238037 ,-0.00271606 , -0.00357056 ,-0.00280762 ,-0.00280762 ,-0.00415039 ,-0.00308228 , -0.00271606 ,-0.00259399 ,-0.00265503 ,-0.00234985 ,-0.00375366 , -0.00271606 ,-0.00320435 ,-0.00338745 ,-0.00350952 ,-0.00250244 , -0.00286865 ,-0.00198364 ,-0.00305176 ,-0.00198364 ,-0.00146484 , -0.00265503 ,-0.00070190 ,-0.00241089 ,-0.00155640 ,-0.00021362 , -0.00067139 ,-0.00155640 ,-0.00082397 ,-0.00045776 ,-0.00018311 , 0.00018311 ,-0.00009155 ,-0.00027466 ,0.00073242 ,-0.00027466 , 0.00036621 ,0.00067139 ,0.00033569 ,0.00073242 ,0.00140381 , 0.00088501 ,0.00088501 ,0.00180054 ,0.00186157 ,0.00088501 , 0.00180054 ,0.00076294 ,0.00180054 ,0.00244141 ,0.00271606 , 0.00277710 ,0.00366211 ,0.00360107 ,0.00314331 ,0.00326538 , 0.00302124 ,0.00289917 ,0.00360107 ,0.00424194 ,0.00512695 , 0.00512695 ,0.00527954 ,0.00570679 ,0.00585938 ,0.00567627 , 0.00476074 ,0.00537109 ,0.00509644 ,0.00485229 ,0.00357056 , 0.00344849 ,0.00338745 ,0.00421143 ,0.00375366 ,0.00381470 , 0.00366211 ,0.00363159 ,0.00274658 ,0.00192261 ,0.00259399 , 0.00219727 ,0.00225830 ,0.00152588 ,0.00222778 ,0.00161743 , 0.00076294 ,0.00183105 ,0.00018311 ,0.00125122 ,0.00097656 , 0.00161743 ,0.00219727 ,0.00134277 ,-0.00015259 ,0.00076294 , 0.00094604 ,0.00003052 ,0.00091553 ,-0.00042725 ,-0.00009155 , -0.00024414 ,0.00088501 ,0.00109863 ,0.00048828 ,0.00036621 , 0.00186157 ,0.00000000 ,0.00112915 ,0.00204468 ,0.00045776 , 0.00131226 ,0.00088501 ,0.00173950 ,0.00277710 ,0.00064087 , 0.00183105 ,0.00283813 ,0.00207520 ,0.00201416 ,0.00241089 , 0.00164795 ,0.00283813 ,0.00161743 ,-0.00054932 ,0.00164795 , 0.00186157 ,0.00103760 ,0.00207520 ,0.00103760 ,0.00122070 , 0.00042725 ,0.00030518 ,0.00143433 ,0.00006104 ,0.00009155 , -0.00073242 ,-0.00030518 ,-0.00216675 ,-0.00189209 ,-0.00027466 , -0.00073242 ,-0.00091553 ,-0.00186157 ,-0.00146484 ,-0.00115967 , -0.00146484 ,-0.00149536 ,-0.00109863 ,-0.00241089 ,-0.00131226 , -0.00268555 ,-0.00222778 ,-0.00131226 ,-0.00204468 ,-0.00131226 , -0.00152588 ,-0.00103760 ,-0.00222778 ,-0.00134277 ,-0.00158691 , -0.00247192 ,-0.00244141 ,-0.00195312 ,-0.00286865 ,-0.00259399 , -0.00289917 ,-0.00207520 ,-0.00250244 ,-0.00357056 ,-0.00439453 , -0.00271606 ,-0.00396729 ,-0.00396729 ,-0.00445557 ,-0.00451660 , -0.00436401 ,-0.00439453 ,-0.00445557 ,-0.00524902 ,-0.00537109 , -0.00451660 ,-0.00558472 ,-0.00537109 ,-0.00628662 ,-0.00531006 , -0.00607300 ,-0.00646973 ,-0.00646973 ,-0.00527954 ,-0.00598145 , -0.00582886 ,-0.00427246 ,-0.00515747 ,-0.00424194 ,-0.00430298 , -0.00415039 ,-0.00408936 ,-0.00433350 ,-0.00354004 ,-0.00244141 , -0.00283813 ,-0.00268555 ,-0.00283813 ,-0.00253296 ,-0.00158691 , -0.00241089 ,-0.00225830 ,-0.00354004 ,-0.00204468 ,-0.00250244 , -0.00143433 ,-0.00146484 ,-0.00250244 ,-0.00143433 ,-0.00189209 , -0.00198364 ,-0.00292969 ,-0.00268555 ,-0.00286865 ,-0.00167847 , -0.00140381 ,-0.00033569 ,-0.00061035 ,-0.00021362 ,0.00021362 , 0.00051880 ,0.00073242 ,0.00030518 ,0.00134277 ,0.00183105 , 0.00213623 ,0.00204468 ,0.00146484 ,0.00357056 ,0.00256348 , 0.00329590 ,0.00289917 ,0.00411987 ,0.00436401 ,0.00393677 , 0.00344849 ,0.00332642 ,0.00296021 ,0.00378418 ,0.00347900 , 0.00442505 ,0.00430298 ,0.00344849 ,0.00357056 ,0.00411987 , 0.00473022 ,0.00387573 ,0.00439453 ,0.00527954 ,0.00372314 , 0.00405884 ,0.00341797 ,0.00350952 ,0.00326538 ,0.00366211 , 0.00344849 ,0.00296021 ,0.00268555 ,0.00289917 ,0.00317383 , 0.00289917 ,0.00360107 ,0.00204468 ,0.00292969 ,0.00302124 , 0.00256348 ,0.00277710 ,0.00259399 ,0.00280762 ,0.00378418 , 0.00244141 ,0.00244141 ,0.00247192 ,0.00259399 ,0.00207520 , 0.00158691 ,0.00247192 ,0.00268555 ,0.00189209 ,0.00308228 , 0.00222778 ,0.00177002 ,0.00167847 ,0.00109863 ,0.00186157 , 0.00097656 ,0.00140381 ,0.00051880 ,0.00173950 ,0.00061035 , 0.00094604 ,0.00167847 ,0.00155640 ,0.00158691 ,0.00079346 , 0.00119019 ,0.00149536 ,0.00149536 ,0.00109863 ,0.00210571 , 0.00231934 ,0.00201416 ,0.00222778 ,0.00271606 ,0.00161743 , 0.00158691 ,0.00167847 ,0.00183105 ,0.00152588 ,0.00231934 , 0.00268555 ,0.00247192 ,0.00219727 ,0.00296021 ,0.00341797 , 0.00149536 ,0.00286865 ,0.00302124 ,0.00283813 ,0.00317383 , 0.00183105 ,0.00238037 ,0.00256348 ,0.00222778 ,0.00204468 , 0.00149536 ,0.00143433 ,0.00283813 ,0.00216675 ,0.00231934 , 0.00183105 ,0.00149536 ,0.00201416 ,0.00106812 ,0.00030518 , -0.00015259 ,0.00033569 ,-0.00009155 ,-0.00103760 ,-0.00067139 , -0.00045776 ,-0.00158691 ,-0.00109863 ,-0.00158691 ,-0.00198364 , -0.00231934 ,-0.00201416 ,-0.00271606 ,-0.00250244 ,-0.00256348 , -0.00283813 ,-0.00256348 ,-0.00213623 ,-0.00329590 ,-0.00231934 , -0.00445557 ,-0.00366211 ,-0.00509644 ,-0.00448608 ,-0.00415039 , -0.00314331 ,-0.00390625 ,-0.00396729 ,-0.00430298 ,-0.00500488 , -0.00436401 ,-0.00393677 ,-0.00393677 ,-0.00570679 ,-0.00552368 , -0.00427246 ,-0.00430298 ,-0.00311279 ,-0.00436401 ,-0.00390625 , -0.00326538 ,-0.00384521 ,-0.00448608 ,-0.00341797 ,-0.00350952 , -0.00350952 ,-0.00323486 ,-0.00207520 ,-0.00317383 ,-0.00164795 , -0.00378418 ,-0.00250244 ,-0.00247192 ,-0.00253296 ,-0.00244141 , -0.00195312 ,-0.00271606 ,-0.00274658 ,-0.00320435 ,-0.00207520 , -0.00155640 ,-0.00192261 ,-0.00274658 ,-0.00167847 ,-0.00247192 , -0.00183105 ,-0.00097656 ,-0.00097656 ,-0.00143433 ,-0.00128174 , -0.00070190 ,-0.00201416 ,-0.00125122 ,-0.00134277 ,-0.00076294 , -0.00103760 ,-0.00048828 ,0.00009155 ,-0.00079346 ,0.00006104 , 0.00051880 ,0.00177002 ,0.00079346 ,0.00018311 ,0.00091553 , 0.00064087 ,0.00033569 ,0.00061035 ,0.00021362 ,0.00045776 , 0.00103760 ,0.00088501 ,0.00173950 ,0.00234985 ,0.00244141 , 0.00308228 ,0.00305176 ,0.00366211 ,0.00384521 ,0.00323486 , 0.00381470 ,0.00308228 ,0.00323486 ,0.00341797 ,0.00341797 , 0.00289917 ,0.00405884 ,0.00277710 ,0.00259399 ,0.00244141 , 0.00292969 ,0.00442505 ,0.00344849 ,0.00393677 ,0.00320435 , 0.00277710 ,0.00323486 ,0.00271606 ,0.00173950 ,0.00311279 , 0.00256348 ,0.00219727 ,0.00177002 ,0.00195312 ,0.00164795 , 0.00137329 ,0.00152588 ,0.00088501 ,0.00146484 ,0.00137329 , 0.00094604 ,0.00100708 ,0.00003052 ,0.00045776 ,-0.00125122 , -0.00103760 ,-0.00100708 ,-0.00009155 ,0.00018311 ,0.00042725 , -0.00106812 ,-0.00042725 ,-0.00082397 ,-0.00173950 ,-0.00079346 , -0.00045776 ,-0.00152588 ,-0.00085449 ,-0.00131226 ,-0.00140381 , -0.00189209 ,-0.00079346 ,-0.00164795 ,0.00000000 ,-0.00088501 , -0.00167847 ,-0.00106812 ,-0.00137329 ,-0.00042725 ,-0.00106812 , -0.00137329 ,0.00003052 ,-0.00167847 ,0.00003052 ,-0.00030518 , -0.00006104 ,-0.00122070 ,-0.00152588 ,-0.00094604 ,-0.00238037 , -0.00170898 ,-0.00213623 ,-0.00009155 ,0.00015259 ,0.00030518 , -0.00073242 ,0.00009155 ,-0.00045776 ,-0.00039673 ,-0.00009155 , -0.00048828 ,-0.00067139 ,0.00076294 ,0.00054932 ,-0.00057983 , -0.00036621 ,-0.00061035 ,0.00006104 ,-0.00064087 ,0.00201416 , 0.00079346 ,0.00015259 ,0.00039673 ,0.00024414 ,0.00100708 , 0.00024414 ,0.00253296 ,0.00115967 ,0.00112915 ,0.00238037 , 0.00195312 ,-0.00057983 ,0.00094604 ,-0.00015259 ,0.00079346 , 0.00094604 ,0.00036621 ,-0.00039673 ,0.00009155 ,0.00061035 , -0.00009155 ,-0.00024414 ,-0.00033569 ,-0.00045776 ,0.00015259 , -0.00061035 ,-0.00085449 ,0.00012207 ,-0.00009155 ,-0.00082397 , -0.00054932 ,0.00009155 ,-0.00067139 ,-0.00134277 ,0.00061035 , 0.00021362 ,0.00042725 ,0.00064087 ,0.00198364 ,0.00131226 , 0.00204468 ,0.00177002 ,0.00061035 ,0.00030518 ,0.00085449 , -0.00042725 ,-0.00122070 ,0.00170898 ,0.00064087 ,0.00137329 , 0.00244141 ,0.00167847 ,0.00198364 ,0.00250244 ,0.00167847 , 0.00338745 ,0.00189209 ,0.00381470 ,0.00375366 ,0.00256348 , 0.00338745 ,0.00222778 ,0.00314331 ,0.00296021 ,0.00299072 , 0.00433350 ,0.00579834 ,0.00390625 ,0.00421143 ,0.00393677 , 0.00372314 ,0.00503540 ,0.00515747 ,0.00421143 ,0.00393677 , 0.00482178 ,0.00439453 ,0.00509644 ,0.00457764 ,0.00451660 , 0.00488281 ,0.00527954 ,0.00460815 ,0.00457764 ,0.00433350 , 0.00421143 ,0.00592041 ,0.00402832 ,0.00476074 ,0.00375366 , 0.00393677 ,0.00424194 ,0.00442505 ,0.00402832 ,0.00546265 , 0.00524902 ,0.00494385 ,0.00384521 ,0.00335693 ,0.00381470 , 0.00338745 ,0.00433350 ,0.00381470 ,0.00335693 ,0.00366211 , 0.00311279 ,0.00210571 ,0.00305176 ,0.00274658 ,0.00180054 , 0.00274658 ,0.00173950 ,0.00238037 ,0.00112915 ,0.00112915 , 0.00125122 ,-0.00030518 ,-0.00042725 ,-0.00048828 ,0.00048828 , 0.00143433 ,0.00085449 ,0.00009155 ,-0.00109863 ,-0.00061035 , -0.00125122 ,-0.00241089 ,-0.00271606 ,-0.00256348 ,-0.00180054 , -0.00363159 ,-0.00305176 ,-0.00369263 ,-0.00439453 ,-0.00366211 , -0.00411987 ,-0.00372314 ,-0.00424194 ,-0.00357056 ,-0.00494385 , -0.00399780 ,-0.00460815 ,-0.00384521 ,-0.00466919 ,-0.00411987 , -0.00408936 ,-0.00372314 ,-0.00402832 ,-0.00384521 ,-0.00347900 , -0.00238037 ,-0.00256348 ,-0.00247192 ,-0.00186157 ,-0.00189209 , -0.00308228 ,-0.00155640 ,-0.00155640 ,-0.00036621 ,-0.00146484 , -0.00253296 ,-0.00137329 ,-0.00155640 ,-0.00103760 ,-0.00177002 , -0.00024414 ,-0.00045776 ,-0.00082397 ,-0.00061035 ,-0.00048828 , 0.00000000 ,-0.00024414 ,0.00088501 ,-0.00067139 ,0.00006104 , 0.00057983 ,0.00009155 ,0.00054932 ,-0.00045776 ,0.00103760 , 0.00103760 ,0.00122070 ,0.00091553 ,-0.00024414 ,0.00064087 , 0.00000000 ,-0.00042725 ,-0.00036621 ,-0.00103760 ,-0.00067139 , -0.00085449 ,-0.00082397 ,-0.00085449 ,-0.00091553 ,-0.00085449 , -0.00140381 ,-0.00103760 ,-0.00112915 ,-0.00045776 ,-0.00036621 , -0.00067139 ,-0.00018311 ,-0.00103760 ,0.00018311 ,-0.00057983 , -0.00003052 ,0.00015259 ,-0.00140381 ,-0.00115967 ,0.00021362 , -0.00042725 ,-0.00106812 ,0.00024414 ,-0.00012207 ,-0.00054932 , -0.00061035 ,-0.00054932 ,-0.00097656 ,0.00024414 ,-0.00051880 , -0.00030518 ,-0.00045776 ,-0.00030518 ,-0.00070190 ,0.00000000 , -0.00064087 ,0.00006104 ,0.00009155 ,0.00100708 ,0.00064087 , 0.00061035 ,0.00131226 ,0.00094604 ,0.00094604 ,0.00180054 , 0.00085449 ,0.00100708 ,0.00143433 ,0.00207520 ,0.00311279 , 0.00216675 ,0.00201416 ,0.00277710 ,0.00241089 ,0.00360107 , 0.00158691 ,0.00350952 ,0.00277710 ,0.00442505 ,0.00396729 , 0.00311279 ,0.00332642 ,0.00277710 ,0.00411987 ,0.00454712 , 0.00604248 ,0.00515747 ,0.00518799 ,0.00601196 ,0.00573730 , 0.00598145 ,0.00598145 ,0.00585938 ,0.00701904 ,0.00704956 , 0.00689697 ,0.00741577 ,0.00711060 ,0.00790405 ,0.00769043 , 0.00756836 ,0.00769043 ,0.00848389 ,0.00820923 ,0.00808716 , 0.00796509 ,0.00823975 ,0.00732422 ,0.00659180 ,0.00799561 , 0.00723267 ,0.00546265 ,0.00607300 ,0.00549316 ,0.00558472 , 0.00735474 ,0.00537109 ,0.00479126 ,0.00366211 ,0.00463867 , 0.00415039 ,0.00344849 ,0.00241089 ,0.00112915 ,0.00125122 , 0.00143433 ,0.00082397 ,0.00091553 ,0.00073242 ,0.00000000 , 0.00018311 ,-0.00045776 ,-0.00054932 ,-0.00106812 ,-0.00186157 , -0.00213623 ,-0.00079346 ,-0.00234985 ,-0.00112915 ,-0.00216675 , -0.00189209 ,-0.00238037 ,-0.00262451 ,-0.00228882 ,-0.00177002 , -0.00198364 ,-0.00134277 ,-0.00259399 ,-0.00164795 ,-0.00106812 , -0.00280762 ,-0.00305176 ,-0.00277710 ,-0.00253296 ,-0.00314331 , -0.00308228 ,-0.00347900 ,-0.00390625 ,-0.00332642 ,-0.00347900 , -0.00476074 ,-0.00369263 ,-0.00274658 ,-0.00372314 ,-0.00411987 , -0.00320435 ,-0.00457764 ,-0.00445557 ,-0.00363159 ,-0.00289917 , -0.00448608 ,-0.00430298 ,-0.00393677 ,-0.00448608 ,-0.00320435 , -0.00485229 ,-0.00387573 ,-0.00347900 ,-0.00341797 ,-0.00363159 , -0.00286865 ,-0.00231934 ,-0.00363159 ,-0.00308228 ,-0.00375366 , -0.00268555 ,-0.00381470 ,-0.00247192 ,-0.00222778 ,-0.00372314 , -0.00323486 ,-0.00360107 ,-0.00326538 ,-0.00238037 ,-0.00262451 , -0.00265503 ,-0.00317383 ,-0.00207520 ,-0.00219727 ,-0.00180054 , -0.00296021 ,-0.00222778 ,-0.00161743 ,-0.00173950 ,-0.00140381 , -0.00259399 ,-0.00119019 ,-0.00073242 ,-0.00143433 ,-0.00094604 , -0.00152588 ,-0.00039673 ,-0.00067139 ,0.00006104 ,0.00015259 , 0.00018311 ,0.00112915 ,0.00009155 ,0.00024414 ,0.00064087 , 0.00054932 ,0.00024414 ,0.00079346 ,0.00027466 ,-0.00033569 , 0.00051880 ,0.00158691 ,0.00158691 ,-0.00003052 ,-0.00015259 , 0.00048828 ,0.00067139 ,-0.00149536 ,-0.00027466 ,-0.00027466 , -0.00073242 ,0.00039673 ,-0.00158691 ,-0.00097656 ,-0.00003052 , -0.00085449 ,-0.00006104 ,0.00088501 ,0.00057983 ,0.00039673 , -0.00021362 ,0.00018311 ,0.00134277 ,0.00012207 ,0.00042725 , -0.00048828 ,0.00183105 ,0.00228882 ,0.00302124 ,0.00122070 , 0.00173950 ,0.00201416 ,0.00250244 ,0.00131226 ,0.00238037 , 0.00271606 ,0.00180054 ,0.00274658 ,0.00271606 ,0.00317383 , 0.00329590 ,0.00241089 ,0.00234985 ,0.00305176 ,0.00326538 , 0.00271606 ,0.00305176 ,0.00399780 ,0.00415039 ,0.00402832 , 0.00363159 ,0.00308228 ,0.00411987 ,0.00433350 ,0.00427246 , 0.00299072 ,0.00378418 ,0.00436401 ,0.00360107 ,0.00439453 , 0.00500488 ,0.00451660 ,0.00476074 ,0.00454712 ,0.00506592 , 0.00497437 ,0.00512695 ,0.00415039 ,0.00463867 ,0.00405884 , 0.00561523 ,0.00466919 ,0.00482178 ,0.00387573 ,0.00247192 , 0.00347900 ,0.00335693 ,0.00390625 ,0.00299072 ,0.00192261 , 0.00280762 ,0.00238037 ,0.00115967 ,0.00170898 ,0.00131226 , 0.00015259 ,0.00003052 ,-0.00057983 ,0.00048828 ,-0.00119019 , -0.00131226 ,-0.00167847 ,-0.00216675 ,-0.00189209 ,-0.00115967 , -0.00253296 ,-0.00299072 ,-0.00268555 ,-0.00402832 ,-0.00292969 , -0.00341797 ,-0.00170898 ,-0.00320435 ,-0.00384521 ,-0.00411987 , -0.00244141 ,-0.00338745 ,-0.00305176 ,-0.00500488 ,-0.00442505 , -0.00405884 ,-0.00332642 ,-0.00405884 ,-0.00387573 ,-0.00335693 , -0.00332642 ,-0.00302124 ,-0.00521851 ,-0.00543213 ,-0.00354004 , -0.00405884 ,-0.00427246 ,-0.00329590 ,-0.00399780 ,-0.00326538 , -0.00274658 ,-0.00296021 ,-0.00311279 ,-0.00305176 ,-0.00329590 , -0.00299072 ,-0.00253296 ,-0.00308228 ,-0.00292969 ,-0.00131226 , -0.00210571 ,-0.00131226 ,-0.00189209 ,-0.00286865 ,-0.00170898 , -0.00134277 ,-0.00268555 ,-0.00210571 ,-0.00149536 ,-0.00222778 , -0.00234985 ,-0.00039673 ,-0.00085449 ,-0.00228882 ,-0.00131226 , -0.00119019 ,-0.00070190 ,-0.00079346 ,-0.00045776 ,-0.00076294 , 0.00003052 ,-0.00122070 ,-0.00039673 ,0.00033569 ,-0.00085449 , -0.00024414 ,-0.00067139 ,0.00076294 ,0.00000000 ,0.00112915 , -0.00039673 ,0.00036621 ,0.00073242 ,0.00018311 ,0.00225830 , 0.00103760 ,0.00115967 ,0.00030518 ,0.00167847 ,0.00006104 , 0.00112915 ,0.00064087 ,0.00189209 ,0.00000000 }; static const unsigned defaultKlick3Length = sizeof(defaultKlick3) / sizeof(*defaultKlick3); #endif muse-4.0.0/src/muse/klick4.h000066400000000000000000006653541404105311000155660ustar00rootroot00000000000000#ifndef __KLICK_4_H__ #define __KLICK_4_H__ static const float defaultKlick4[] = { 0.00912476 ,0.00164795 ,-0.01119995 ,-0.01855469 ,-0.04171753 , -0.14804077 ,-0.28125000 ,-0.19204712 ,0.20870972 ,0.62155151 , 0.73324585 ,0.54299927 ,0.19351196 ,-0.21319580 ,-0.51977539 , -0.55010986 ,-0.31689453 ,-0.03207397 ,0.13427734 ,0.21081543 , 0.28277588 ,0.30221558 ,0.14392090 ,-0.16372681 ,-0.41052246 , -0.47103882 ,-0.39730835 ,-0.22357178 ,0.05566406 ,0.34497070 , 0.50061035 ,0.46951294 ,0.30712891 ,0.11816406 ,-0.04379272 , -0.17355347 ,-0.23724365 ,-0.17480469 ,-0.01004028 ,0.12899780 , 0.15078735 ,0.10336304 ,0.07400513 ,0.06689453 ,0.02728271 , -0.04061890 ,-0.05932617 ,-0.01928711 ,-0.03903198 ,-0.18161011 , -0.33514404 ,-0.36199951 ,-0.22003174 ,0.03826904 ,0.28646851 , 0.40490723 ,0.37561035 ,0.23580933 ,0.01037598 ,-0.22323608 , -0.31121826 ,-0.18612671 ,0.03341675 ,0.18991089 ,0.25338745 , 0.26718140 ,0.23867798 ,0.12026978 ,-0.08776855 ,-0.27050781 , -0.31317139 ,-0.26171875 ,-0.23675537 ,-0.23104858 ,-0.12933350 , 0.10006714 ,0.31152344 ,0.34783936 ,0.23828125 ,0.12869263 , 0.04437256 ,-0.09442139 ,-0.24407959 ,-0.24008179 ,-0.07260132 , 0.07620239 ,0.10494995 ,0.11679077 ,0.18649292 ,0.20324707 , 0.05841064 ,-0.15411377 ,-0.25332642 ,-0.20611572 ,-0.11782837 , -0.05358887 ,0.02178955 ,0.12011719 ,0.16802979 ,0.11953735 , 0.02734375 ,-0.04513550 ,-0.09378052 ,-0.13519287 ,-0.16827393 , -0.16165161 ,-0.07922363 ,0.05081177 ,0.13552856 ,0.12716675 , 0.08956909 ,0.08734131 ,0.08822632 ,0.04837036 ,-0.00286865 , -0.02325439 ,-0.03320312 ,-0.07092285 ,-0.12741089 ,-0.14450073 , -0.07150269 ,0.05343628 ,0.12326050 ,0.10186768 ,0.04797363 , 0.00015259 ,-0.06192017 ,-0.12423706 ,-0.12969971 ,-0.06732178 , 0.01324463 ,0.06481934 ,0.08969116 ,0.12115479 ,0.14416504 , 0.09197998 ,-0.03445435 ,-0.13168335 ,-0.13336182 ,-0.08798218 , -0.04727173 ,0.00048828 ,0.05368042 ,0.07891846 ,0.07113647 , 0.05456543 ,0.03848267 ,0.02029419 ,0.00061035 ,-0.03051758 , -0.07791138 ,-0.12139893 ,-0.12261963 ,-0.07385254 ,-0.01013184 , 0.03359985 ,0.05200195 ,0.06176758 ,0.07125854 ,0.06127930 , 0.02484131 ,-0.00802612 ,-0.01528931 ,-0.01651001 ,-0.01547241 , 0.00561523 ,0.04678345 ,0.07247925 ,0.05984497 ,0.01046753 , -0.04556274 ,-0.07327271 ,-0.07211304 ,-0.07104492 ,-0.07165527 , -0.04956055 ,-0.00991821 ,0.01678467 ,0.02966309 ,0.04479980 , 0.05435181 ,0.04327393 ,0.01510620 ,-0.02236938 ,-0.05895996 , -0.07351685 ,-0.06115723 ,-0.03430176 ,-0.00311279 ,0.02767944 , 0.05096436 ,0.06555176 ,0.06591797 ,0.04519653 ,0.01651001 , 0.00167847 ,-0.01351929 ,-0.04104614 ,-0.05725098 ,-0.03802490 , -0.01184082 ,-0.00671387 ,-0.01083374 ,-0.00729370 ,-0.00479126 , -0.02053833 ,-0.04934692 ,-0.06524658 ,-0.04971313 ,-0.01519775 , 0.01013184 ,0.02285767 ,0.04516602 ,0.07192993 ,0.07260132 , 0.03765869 ,-0.00393677 ,-0.02951050 ,-0.04498291 ,-0.05822754 , -0.06167603 ,-0.04153442 ,-0.00515747 ,0.02145386 ,0.02392578 , 0.01831055 ,0.01834106 ,0.01519775 ,0.00088501 ,-0.01364136 , -0.02178955 ,-0.02789307 ,-0.03030396 ,-0.02471924 ,-0.01831055 , -0.00759888 ,0.01068115 ,0.02117920 ,0.00567627 ,-0.01504517 , -0.01333618 ,0.00192261 ,0.00479126 ,0.00085449 ,0.01101685 , 0.03436279 ,0.04940796 ,0.04220581 ,0.02108765 ,0.00503540 , -0.00955200 ,-0.04064941 ,-0.07482910 ,-0.07443237 ,-0.03979492 , -0.01092529 ,-0.00558472 ,-0.00360107 ,0.00894165 ,0.02105713 , 0.01965332 ,0.00997925 ,0.01083374 ,0.02624512 ,0.03091431 , 0.01025391 ,-0.01211548 ,-0.01187134 ,0.00085449 ,0.00323486 , -0.00305176 ,-0.00341797 ,0.00546265 ,0.01562500 ,0.01330566 , -0.00357056 ,-0.01556396 ,-0.00857544 ,0.00378418 ,0.00521851 , 0.00173950 ,0.00680542 ,0.01321411 ,0.00369263 ,-0.02105713 , -0.03857422 ,-0.02938843 ,-0.00741577 ,-0.00109863 ,-0.01263428 , -0.01089478 ,0.01257324 ,0.02850342 ,0.02038574 ,0.01336670 , 0.02587891 ,0.03707886 ,0.02111816 ,-0.01022339 ,-0.02618408 , -0.01733398 ,-0.00619507 ,-0.01269531 ,-0.02401733 ,-0.01507568 , 0.00347900 ,0.00424194 ,-0.01077271 ,-0.01547241 ,-0.00518799 , 0.00164795 ,-0.00189209 ,-0.00509644 ,0.00396729 ,0.02304077 , 0.03164673 ,0.01345825 ,-0.01376343 ,-0.02191162 ,-0.01211548 , -0.00796509 ,-0.01092529 ,-0.00415039 ,0.01419067 ,0.02587891 , 0.01541138 ,-0.00433350 ,-0.00659180 ,0.01177979 ,0.02172852 , 0.00619507 ,-0.01141357 ,-0.00869751 ,-0.00326538 ,-0.01751709 , -0.03964233 ,-0.03906250 ,-0.01385498 ,0.00753784 ,0.00543213 , -0.00326538 ,0.00643921 ,0.02349854 ,0.01788330 ,-0.00628662 , -0.01150513 ,0.00997925 ,0.02685547 ,0.01919556 ,0.00198364 , -0.00302124 ,0.00289917 ,0.00082397 ,-0.01254272 ,-0.01950073 , -0.00607300 ,0.00936890 ,0.00268555 ,-0.01510620 ,-0.01522827 , 0.00292969 ,0.01559448 ,0.00781250 ,-0.00558472 ,-0.00555420 , 0.00372314 ,0.00097656 ,-0.01818848 ,-0.02697754 ,-0.01226807 , 0.00573730 ,0.00259399 ,-0.00912476 ,-0.00222778 ,0.01776123 , 0.02575684 ,0.01138306 ,-0.00408936 ,0.00018311 ,0.01342773 , 0.01004028 ,-0.01040649 ,-0.01660156 ,-0.00253296 ,0.00439453 , -0.00933838 ,-0.02053833 ,-0.00970459 ,0.00997925 ,0.01296997 , -0.00231934 ,-0.01208496 ,-0.00164795 ,0.01303101 ,0.01040649 , -0.00082397 ,0.00094604 ,0.01406860 ,0.01818848 ,0.00564575 , -0.00784302 ,-0.00772095 ,0.00006104 ,-0.00067139 ,-0.01107788 , -0.01187134 ,0.00711060 ,0.02526855 ,0.02243042 ,0.00793457 , 0.00332642 ,0.00842285 ,0.00610352 ,-0.00695801 ,-0.01309204 , -0.00170898 ,0.01217651 ,0.00759888 ,-0.01083374 ,-0.01834106 , -0.00750732 ,0.00231934 ,-0.00244141 ,-0.00772095 ,0.00244141 , 0.01605225 ,0.01684570 ,0.00588989 ,0.00103760 ,0.00958252 , 0.01715088 ,0.00894165 ,-0.00592041 ,-0.00848389 ,0.00128174 , 0.00207520 ,-0.01174927 ,-0.02236938 ,-0.01535034 ,0.00082397 , 0.01043701 ,0.01025391 ,0.01062012 ,0.01330566 ,0.01034546 , -0.00619507 ,-0.02023315 ,-0.01422119 ,0.00607300 ,0.01522827 , 0.00561523 ,-0.00778198 ,-0.00985718 ,-0.00271606 ,0.00057983 , -0.00241089 ,0.00125122 ,0.01220703 ,0.01681519 ,0.00521851 , -0.00787354 ,-0.00485229 ,0.00384521 ,0.00201416 ,-0.00906372 , -0.01345825 ,-0.00637817 ,0.00134277 ,-0.00238037 ,-0.01174927 , -0.01382446 ,-0.00805664 ,-0.00723267 ,-0.01110840 ,-0.00781250 , 0.00930786 ,0.02407837 ,0.02404785 ,0.01235962 ,0.00421143 , 0.00155640 ,-0.00350952 ,-0.01477051 ,-0.02127075 ,-0.01351929 , 0.00003052 ,0.00311279 ,-0.00405884 ,-0.00955200 ,-0.00701904 , -0.00283813 ,-0.00115967 ,-0.00003052 ,0.00402832 ,0.00854492 , 0.00518799 ,-0.00628662 ,-0.01379395 ,-0.00836182 ,0.00234985 , 0.00708008 ,0.00292969 ,-0.00225830 ,-0.00372314 ,-0.00534058 , -0.01071167 ,-0.01464844 ,-0.00628662 ,0.00915527 ,0.01562500 , 0.00942993 ,0.00421143 ,0.00567627 ,0.00509644 ,-0.00149536 , -0.00726318 ,-0.00433350 ,0.00216675 ,0.00302124 ,-0.00723267 , -0.01852417 ,-0.02066040 ,-0.01461792 ,-0.00872803 ,-0.00497437 , 0.00521851 ,0.01821899 ,0.02206421 ,0.01113892 ,-0.00228882 , -0.00506592 ,0.00097656 ,0.00418091 ,0.00018311 ,0.00054932 , 0.00848389 ,0.01208496 ,0.00241089 ,-0.01089478 ,-0.01464844 , -0.00854492 ,-0.00473022 ,-0.00524902 ,-0.00320435 ,0.00506592 , 0.00894165 ,0.00390625 ,-0.00076294 ,0.00387573 ,0.01239014 , 0.01077271 ,-0.00140381 ,-0.00891113 ,-0.00338745 ,0.00527954 , 0.00387573 ,-0.00381470 ,-0.00399780 ,0.00488281 ,0.01119995 , 0.00677490 ,-0.00201416 ,-0.00628662 ,-0.00765991 ,-0.01306152 , -0.02169800 ,-0.01718140 ,0.00262451 ,0.02188110 ,0.02139282 , 0.00552368 ,-0.00961304 ,-0.01455688 ,-0.01626587 ,-0.01861572 , -0.01239014 ,0.00689697 ,0.02636719 ,0.02758789 ,0.01672363 , 0.00912476 ,0.00711060 ,-0.00076294 ,-0.01467896 ,-0.02059937 , -0.00946045 ,0.00805664 ,0.01232910 ,0.00128174 ,-0.00912476 , -0.00878906 ,-0.00778198 ,-0.01269531 ,-0.01281738 ,-0.00097656 , 0.01119995 ,0.00958252 ,-0.00219727 ,-0.00524902 ,0.00698853 , 0.02139282 ,0.01943970 ,0.00354004 ,-0.00851440 ,-0.01211548 , -0.01565552 ,-0.01998901 ,-0.01495361 ,0.00082397 ,0.01440430 , 0.01272583 ,0.00009155 ,-0.00463867 ,0.00445557 ,0.01296997 , 0.00845337 ,-0.00228882 ,-0.00439453 ,0.00332642 ,0.00729370 , 0.00332642 ,0.00238037 ,0.00814819 ,0.00930786 ,-0.00213623 , -0.01623535 ,-0.01199341 ,0.00619507 ,0.01464844 ,0.00207520 , -0.01354980 ,-0.01208496 ,0.00247192 ,0.01190186 ,0.01495361 , 0.01971436 ,0.02383423 ,0.01608276 ,-0.00579834 ,-0.02285767 , -0.02053833 ,-0.00308228 ,0.01187134 ,0.01751709 ,0.02084351 , 0.02713013 ,0.02667236 ,0.01156616 ,-0.00976562 ,-0.01733398 , -0.00817871 ,0.00378418 ,0.00598145 ,0.00607300 ,0.01510620 , 0.02365112 ,0.01412964 ,-0.00845337 ,-0.01885986 ,-0.00894165 , 0.00588989 ,0.00772095 ,-0.00027466 ,-0.00299072 ,0.00570679 , 0.01098633 ,0.00311279 ,-0.00625610 ,-0.00051880 ,0.01144409 , 0.01098633 ,-0.00372314 ,-0.01232910 ,-0.00738525 ,-0.00341797 , -0.01098633 ,-0.01858521 ,-0.00888062 ,0.01083374 ,0.02023315 , 0.01232910 ,0.00332642 ,0.00207520 ,-0.00042725 ,-0.01296997 , -0.02331543 ,-0.01483154 ,0.00759888 ,0.02218628 ,0.01654053 , 0.00079346 ,-0.00628662 ,-0.00674438 ,-0.01443481 ,-0.02551270 , -0.02236938 ,-0.00369263 ,0.01150513 ,0.01037598 ,0.00430298 , 0.00598145 ,0.01062012 ,0.00421143 ,-0.01159668 ,-0.02026367 , -0.01354980 ,-0.00231934 ,-0.00250244 ,-0.00738525 ,-0.00460815 , 0.00631714 ,0.01123047 ,0.00384521 ,-0.00598145 ,-0.00625610 , -0.00424194 ,-0.01159668 ,-0.02117920 ,-0.01733398 ,-0.00283813 , 0.00494385 ,-0.00158691 ,-0.00933838 ,-0.00637817 ,-0.00024414 , -0.00326538 ,-0.01083374 ,-0.01107788 ,-0.00106812 ,0.00598145 , 0.00100708 ,-0.00726318 ,-0.00799561 ,-0.00228882 ,-0.00039673 , -0.00454712 ,-0.00341797 ,0.00628662 ,0.01098633 ,0.00149536 , -0.01318359 ,-0.02023315 ,-0.01940918 ,-0.01556396 ,-0.01040649 , -0.00155640 ,0.00878906 ,0.01159668 ,0.00091553 ,-0.01385498 , -0.01904297 ,-0.01400757 ,-0.01126099 ,-0.01260376 ,-0.01135254 , -0.00268555 ,0.00573730 ,0.00598145 ,-0.00048828 ,-0.00286865 , -0.00027466 ,-0.00051880 ,-0.00695801 ,-0.01031494 ,-0.00576782 , -0.00009155 ,-0.00042725 ,-0.00292969 ,-0.00299072 ,0.00079346 , 0.00177002 ,-0.00167847 ,-0.00497437 ,-0.00640869 ,-0.00643921 , -0.00915527 ,-0.01089478 ,-0.00482178 ,0.00497437 ,0.00851440 , 0.00308228 ,-0.00299072 ,-0.00439453 ,-0.00277710 ,-0.00433350 , -0.00836182 ,-0.01077271 ,-0.01000977 ,-0.00695801 ,-0.00448608 , -0.00119019 ,0.00402832 ,0.00808716 ,0.00787354 ,0.00320435 , -0.00158691 ,-0.00366211 ,-0.00402832 ,-0.00430298 ,-0.00558472 , -0.00405884 ,0.00195312 ,0.00692749 ,0.00799561 ,0.00860596 , 0.00955200 ,0.00726318 ,-0.00003052 ,-0.00576782 ,-0.00564575 , -0.00180054 ,0.00238037 ,0.00170898 ,-0.00164795 ,-0.00134277 , 0.00466919 ,0.00936890 ,0.00765991 ,0.00305176 ,-0.00027466 , -0.00183105 ,-0.00338745 ,-0.00299072 ,0.00140381 ,0.00781250 , 0.01098633 ,0.00759888 ,0.00076294 ,-0.00091553 ,0.00463867 , 0.01031494 ,0.01046753 ,0.00653076 ,0.00299072 ,0.00137329 , -0.00003052 ,-0.00122070 ,0.00247192 ,0.01431274 ,0.02273560 , 0.01483154 ,0.00012207 ,-0.00482178 ,0.00167847 ,0.00680542 , 0.00613403 ,0.00463867 ,0.00787354 ,0.01119995 ,0.00845337 , 0.00204468 ,-0.00146484 ,0.00006104 ,-0.00033569 ,-0.00433350 , -0.00524902 ,0.00543213 ,0.02084351 ,0.02737427 ,0.01919556 , 0.00781250 ,0.00234985 ,0.00018311 ,-0.00469971 ,-0.00616455 , 0.00341797 ,0.01702881 ,0.02136230 ,0.01232910 ,-0.00082397 , -0.00689697 ,-0.00457764 ,0.00030518 ,0.00329590 ,0.00537109 , 0.01165771 ,0.01678467 ,0.01150513 ,-0.00161743 ,-0.00726318 , -0.00045776 ,0.00833130 ,0.00973511 ,0.01025391 ,0.01373291 , 0.01394653 ,0.00253296 ,-0.01306152 ,-0.01745605 ,-0.00601196 , 0.00793457 ,0.01303101 ,0.01348877 ,0.01715088 ,0.01907349 , 0.00976562 ,-0.00833130 ,-0.01971436 ,-0.01571655 ,-0.00323486 , 0.00350952 ,0.00405884 ,0.00790405 ,0.01547241 ,0.01428223 , 0.00195312 ,-0.00552368 ,-0.00164795 ,0.00378418 ,0.00152588 , -0.00189209 ,0.00012207 ,0.00732422 ,0.00952148 ,0.00271606 , -0.00350952 ,0.00030518 ,0.00744629 ,0.00646973 ,-0.00119019 , -0.00439453 ,-0.00036621 ,0.00390625 ,0.00125122 ,-0.00366211 , -0.00231934 ,0.00384521 ,0.00515747 ,-0.00125122 ,-0.00579834 , -0.00161743 ,0.00399780 ,0.00271606 ,-0.00134277 ,-0.00021362 , 0.00537109 ,0.00677490 ,-0.00067139 ,-0.00711060 ,-0.00207520 , 0.01055908 ,0.01501465 ,0.00610352 ,-0.00292969 ,-0.00326538 , -0.00225830 ,-0.00622559 ,-0.00762939 ,0.00012207 ,0.00900269 , 0.00708008 ,-0.00424194 ,-0.01361084 ,-0.01312256 ,-0.00704956 , -0.00210571 ,0.00140381 ,0.00704956 ,0.01199341 ,0.01202393 , 0.00473022 ,-0.00158691 ,-0.00244141 ,0.00070190 ,-0.00100708 , -0.00781250 ,-0.01074219 ,-0.00497437 ,0.00347900 ,0.00405884 , 0.00073242 ,0.00222778 ,0.00698853 ,0.00747681 ,0.00357056 , -0.00103760 ,-0.00485229 ,-0.00842285 ,-0.01025391 ,-0.00955200 , -0.00384521 ,0.00473022 ,0.01046753 ,0.00854492 ,0.00021362 , -0.00756836 ,-0.00967407 ,-0.00775146 ,-0.00573730 ,-0.00308228 , 0.00415039 ,0.01303101 ,0.01428223 ,0.00598145 ,-0.00283813 , -0.00613403 ,-0.00747681 ,-0.00900269 ,-0.00817871 ,-0.00173950 , 0.00634766 ,0.00994873 ,0.00622559 ,-0.00048828 ,-0.00366211 , -0.00122070 ,0.00238037 ,0.00543213 ,0.00833130 ,0.01016235 , 0.00723267 ,-0.00097656 ,-0.00964355 ,-0.01156616 ,-0.00592041 , -0.00048828 ,0.00085449 ,0.00018311 ,0.00201416 ,0.00274658 , -0.00128174 ,-0.00540161 ,-0.00430298 ,0.00082397 ,0.00439453 , 0.00402832 ,0.00183105 ,0.00027466 ,-0.00070190 ,-0.00317383 , -0.00445557 ,-0.00186157 ,0.00268555 ,0.00308228 ,-0.00262451 , -0.00903320 ,-0.01019287 ,-0.00692749 ,-0.00393677 ,-0.00070190 , 0.00460815 ,0.01153564 ,0.01199341 ,0.00720215 ,0.00134277 , -0.00204468 ,-0.00497437 ,-0.00836182 ,-0.00991821 ,-0.00781250 , -0.00311279 ,0.00045776 ,-0.00015259 ,-0.00155640 ,-0.00140381 , -0.00085449 ,-0.00341797 ,-0.00662231 ,-0.00439453 ,0.00320435 , 0.00872803 ,0.00576782 ,-0.00247192 ,-0.00720215 ,-0.00613403 , -0.00460815 ,-0.00540161 ,-0.00534058 ,-0.00296021 ,-0.00384521 , -0.00888062 ,-0.01226807 ,-0.00762939 ,0.00198364 ,0.00814819 , 0.00827026 ,0.00369263 ,-0.00103760 ,-0.00729370 ,-0.01654053 , -0.02221680 ,-0.01580811 ,-0.00164795 ,0.00720215 ,0.00360107 , -0.00347900 ,-0.00622559 ,-0.00704956 ,-0.00973511 ,-0.01296997 , -0.00946045 ,-0.00036621 ,0.00616455 ,0.00378418 ,-0.00314331 , -0.00708008 ,-0.00775146 ,-0.00918579 ,-0.01089478 ,-0.00863647 , -0.00360107 ,-0.00170898 ,-0.00604248 ,-0.01266479 ,-0.01419067 , -0.00839233 ,-0.00192261 ,0.00045776 ,0.00210571 ,0.00744629 , 0.01303101 ,0.00912476 ,-0.00289917 ,-0.01440430 ,-0.01885986 , -0.01611328 ,-0.01089478 ,-0.00454712 ,0.00445557 ,0.01226807 , 0.01159668 ,0.00250244 ,-0.00543213 ,-0.00573730 ,-0.00210571 , -0.00228882 ,-0.00433350 ,-0.00329590 ,0.00231934 ,0.00485229 , 0.00268555 ,0.00006104 ,-0.00018311 ,-0.00082397 ,-0.00610352 , -0.01263428 ,-0.01318359 ,-0.00570679 ,0.00372314 ,0.00701904 , 0.00436401 ,0.00283813 ,0.00628662 ,0.00985718 ,0.00872803 , 0.00411987 ,0.00158691 ,-0.00146484 ,-0.00735474 ,-0.01348877 , -0.01229858 ,-0.00167847 ,0.00851440 ,0.00997925 ,0.00500488 , 0.00192261 ,0.00201416 ,0.00000000 ,-0.00534058 ,-0.00714111 , -0.00195312 ,0.00399780 ,0.00350952 ,-0.00238037 ,-0.00418091 , -0.00125122 ,0.00207520 ,0.00054932 ,-0.00314331 ,-0.00302124 , 0.00146484 ,0.00445557 ,0.00231934 ,-0.00009155 ,0.00277710 , 0.00662231 ,0.00631714 ,0.00439453 ,0.00503540 ,0.00634766 , 0.00131226 ,-0.00912476 ,-0.01541138 ,-0.01123047 ,0.00003052 , 0.00759888 ,0.00796509 ,0.00448608 ,0.00244141 ,0.00119019 , -0.00112915 ,-0.00277710 ,0.00103760 ,0.00936890 ,0.01351929 , 0.00875854 ,0.00064087 ,-0.00173950 ,-0.00009155 ,-0.00204468 , -0.00799561 ,-0.00811768 ,0.00000000 ,0.00894165 ,0.00994873 , 0.00659180 ,0.00540161 ,0.00570679 ,0.00381470 ,-0.00067139 , 0.00039673 ,0.00811768 ,0.01394653 ,0.00885010 ,-0.00189209 , -0.00961304 ,-0.00994873 ,-0.00683594 ,-0.00286865 ,0.00393677 , 0.01303101 ,0.01702881 ,0.01159668 ,0.00109863 ,-0.00521851 , -0.00274658 ,0.00228882 ,0.00552368 ,0.00680542 ,0.00878906 , 0.00909424 ,0.00451660 ,-0.00292969 ,-0.00753784 ,-0.00552368 , -0.00064087 ,0.00216675 ,0.00350952 ,0.00753784 ,0.01239014 , 0.01168823 ,0.00531006 ,0.00021362 ,0.00189209 ,0.00723267 , 0.01132202 ,0.01058960 ,0.00555420 ,0.00000000 ,-0.00518799 , -0.00814819 ,-0.00799561 ,-0.00207520 ,0.00662231 ,0.01165771 , 0.01089478 ,0.00735474 ,0.00561523 ,0.00424194 ,0.00149536 , -0.00085449 ,0.00051880 ,0.00476074 ,0.00784302 ,0.00622559 , 0.00201416 ,-0.00125122 ,-0.00402832 ,-0.00607300 ,-0.00436401 , 0.00143433 ,0.00848389 ,0.01196289 ,0.00967407 ,0.00354004 , -0.00115967 ,-0.00073242 ,0.00140381 ,0.00341797 ,0.00378418 , 0.00457764 ,0.00332642 ,-0.00009155 ,-0.00372314 ,-0.00689697 , -0.00735474 ,-0.00622559 ,-0.00173950 ,0.00241089 ,0.00659180 , 0.00939941 ,0.00909424 ,0.00497437 ,-0.00024414 ,-0.00289917 , -0.00256348 ,-0.00024414 ,0.00122070 ,0.00189209 ,0.00164795 , 0.00143433 ,-0.00027466 ,-0.00277710 ,-0.00372314 ,-0.00317383 , -0.00250244 ,-0.00137329 ,0.00103760 ,0.00369263 ,0.00509644 , 0.00482178 ,0.00375366 ,0.00192261 ,0.00100708 ,0.00091553 , 0.00076294 ,-0.00067139 ,-0.00164795 ,-0.00180054 ,-0.00244141 , -0.00219727 ,0.00112915 ,0.00579834 ,0.00970459 ,0.01089478 , 0.00982666 ,0.00726318 ,0.00329590 ,-0.00094604 ,-0.00286865 , -0.00064087 ,0.00436401 ,0.00662231 ,0.00503540 ,0.00234985 , 0.00088501 ,-0.00112915 ,-0.00415039 ,-0.00436401 ,-0.00054932 , 0.00485229 ,0.00729370 ,0.00732422 ,0.00765991 ,0.00881958 , 0.00750732 ,0.00503540 ,0.00347900 ,0.00674438 ,0.01019287 , 0.00982666 ,0.00616455 ,0.00271606 ,-0.00024414 ,-0.00128174 , -0.00079346 ,0.00180054 ,0.00680542 ,0.01116943 ,0.01080322 , 0.00405884 ,-0.00354004 ,-0.00585938 ,-0.00375366 ,-0.00079346 , 0.00247192 ,0.00717163 ,0.01013184 ,0.00823975 ,0.00323486 , -0.00048828 ,-0.00015259 ,-0.00079346 ,-0.00439453 ,-0.00769043 , -0.00460815 ,0.00488281 ,0.01409912 ,0.01773071 ,0.01580811 , 0.01068115 ,0.00534058 ,0.00036621 ,-0.00473022 ,-0.00991821 , -0.01068115 ,-0.00665283 ,0.00009155 ,0.00704956 ,0.01245117 , 0.01263428 ,0.00433350 ,-0.00848389 ,-0.01672363 ,-0.01599121 , -0.00851440 ,0.00033569 ,0.00805664 ,0.01382446 ,0.01699829 , 0.01400757 ,0.00509644 ,-0.00457764 ,-0.01052856 ,-0.01007080 , -0.00494385 ,0.00146484 ,0.00579834 ,0.00814819 ,0.00839233 , 0.00445557 ,-0.00164795 ,-0.00445557 ,-0.00231934 ,0.00250244 , 0.00512695 ,0.00485229 ,0.00225830 ,-0.00103760 ,-0.00375366 , -0.00570679 ,-0.00537109 ,-0.00137329 ,0.00436401 ,0.00726318 , 0.00698853 ,0.00555420 ,0.00537109 ,0.00421143 ,0.00103760 , -0.00170898 ,-0.00067139 ,0.00552368 ,0.01074219 ,0.00918579 , 0.00283813 ,-0.00234985 ,-0.00323486 ,-0.00518799 ,-0.00805664 , -0.01000977 ,-0.00814819 ,-0.00500488 ,-0.00363159 ,-0.00268555 , 0.00256348 ,0.01071167 ,0.01458740 ,0.01055908 ,0.00540161 , 0.00332642 ,0.00268555 ,-0.00253296 ,-0.00830078 ,-0.00762939 , -0.00125122 ,0.00485229 ,0.00643921 ,0.00332642 ,-0.00048828 , -0.00347900 ,-0.00674438 ,-0.01013184 ,-0.00903320 ,-0.00231934 , 0.00500488 ,0.00601196 ,0.00268555 ,0.00012207 ,-0.00061035 , -0.00192261 ,-0.00595093 ,-0.00744629 ,-0.00540161 ,-0.00180054 , -0.00177002 ,-0.00216675 ,0.00073242 ,0.00643921 ,0.00967407 , 0.00820923 ,0.00411987 ,0.00079346 ,-0.00201416 ,-0.00524902 , -0.00967407 ,-0.01181030 ,-0.00781250 ,-0.00100708 ,0.00424194 , 0.00469971 ,0.00186157 ,-0.00186157 ,-0.00598145 ,-0.00866699 , -0.00997925 ,-0.00601196 ,-0.00094604 ,0.00289917 ,0.00357056 , 0.00363159 ,0.00375366 ,0.00128174 ,-0.00411987 ,-0.00814819 , -0.00988770 ,-0.00955200 ,-0.00833130 ,-0.00610352 ,-0.00317383 , -0.00003052 ,0.00125122 ,0.00094604 ,-0.00064087 ,-0.00222778 , -0.00317383 ,-0.00344849 ,-0.00314331 ,-0.00390625 ,-0.00561523 , -0.00692749 ,-0.00650024 ,-0.00573730 ,-0.00558472 ,-0.00469971 , -0.00183105 ,0.00048828 ,0.00085449 ,-0.00073242 ,-0.00128174 , -0.00125122 ,-0.00094604 ,-0.00048828 ,-0.00079346 ,-0.00079346 , -0.00161743 ,-0.00228882 ,-0.00155640 ,-0.00027466 ,-0.00027466 , -0.00173950 ,-0.00369263 ,-0.00393677 ,-0.00366211 ,-0.00247192 , -0.00067139 ,0.00167847 ,0.00222778 ,0.00183105 ,0.00073242 , -0.00082397 ,-0.00186157 ,-0.00125122 ,-0.00030518 ,0.00064087 , 0.00054932 ,0.00012207 ,-0.00073242 ,-0.00103760 ,-0.00161743 , -0.00015259 ,0.00201416 ,0.00381470 ,0.00451660 ,0.00369263 , 0.00173950 ,-0.00143433 ,-0.00375366 ,-0.00415039 ,-0.00192261 , -0.00085449 ,-0.00067139 ,-0.00030518 ,0.00024414 ,0.00088501 , 0.00161743 ,0.00244141 ,0.00317383 ,0.00244141 ,0.00271606 , 0.00152588 ,-0.00134277 ,-0.00436401 ,-0.00390625 ,-0.00158691 , -0.00024414 ,-0.00057983 ,-0.00183105 ,-0.00161743 ,-0.00152588 , -0.00247192 ,-0.00262451 ,-0.00024414 ,0.00207520 ,0.00180054 , 0.00152588 ,0.00357056 ,0.00650024 ,0.00662231 ,0.00253296 , -0.00170898 ,-0.00396729 ,-0.00378418 ,-0.00259399 ,-0.00073242 , 0.00277710 ,0.00671387 ,0.00781250 ,0.00473022 ,0.00097656 , 0.00006104 ,0.00119019 ,0.00207520 ,0.00213623 ,0.00314331 , 0.00500488 ,0.00561523 ,0.00375366 ,0.00207520 ,0.00097656 , 0.00039673 ,-0.00091553 ,-0.00155640 ,-0.00006104 ,0.00225830 , 0.00317383 ,0.00283813 ,0.00198364 ,0.00274658 ,0.00524902 , 0.00720215 ,0.00643921 ,0.00360107 ,-0.00006104 ,-0.00314331 , -0.00625610 ,-0.00796509 ,-0.00595093 ,-0.00009155 ,0.00662231 , 0.01010132 ,0.00927734 ,0.00579834 ,0.00216675 ,-0.00253296 , -0.00656128 ,-0.00756836 ,-0.00427246 ,0.00039673 ,0.00442505 , 0.00451660 ,0.00436401 ,0.00473022 ,0.00366211 ,0.00015259 , -0.00341797 ,-0.00421143 ,-0.00341797 ,-0.00100708 ,0.00051880 , 0.00146484 ,0.00140381 ,0.00125122 ,0.00131226 ,0.00054932 , -0.00094604 ,-0.00177002 ,-0.00271606 ,-0.00543213 ,-0.00888062 , -0.00964355 ,-0.00689697 ,-0.00344849 ,-0.00106812 ,0.00048828 , 0.00296021 ,0.00445557 ,0.00259399 ,-0.00164795 ,-0.00482178 , -0.00503540 ,-0.00466919 ,-0.00411987 ,-0.00268555 ,0.00082397 , 0.00485229 ,0.00531006 ,0.00146484 ,-0.00384521 ,-0.00628662 , -0.00619507 ,-0.00473022 ,-0.00460815 ,-0.00396729 ,-0.00204468 , -0.00079346 ,-0.00234985 ,-0.00534058 ,-0.00534058 ,-0.00268555 , -0.00247192 ,-0.00506592 ,-0.00823975 ,-0.00640869 ,-0.00225830 , -0.00021362 ,-0.00030518 ,-0.00064087 ,0.00186157 ,0.00286865 , 0.00128174 ,-0.00332642 ,-0.00598145 ,-0.00534058 ,-0.00592041 , -0.00756836 ,-0.00674438 ,-0.00207520 ,0.00241089 ,0.00149536 , -0.00225830 ,-0.00372314 ,-0.00289917 ,-0.00262451 ,-0.00369263 , -0.00439453 ,-0.00244141 ,0.00045776 ,0.00024414 ,-0.00177002 , -0.00189209 ,0.00018311 ,0.00042725 ,-0.00241089 ,-0.00659180 , -0.00836182 ,-0.00747681 ,-0.00549316 ,-0.00503540 ,-0.00408936 , -0.00155640 ,0.00161743 ,0.00250244 ,0.00137329 ,0.00064087 , -0.00048828 ,-0.00296021 ,-0.00640869 ,-0.00790405 ,-0.00604248 , -0.00268555 ,-0.00094604 ,-0.00103760 ,-0.00067139 ,0.00112915 , 0.00192261 ,-0.00024414 ,-0.00436401 ,-0.00708008 ,-0.00750732 , -0.00564575 ,-0.00323486 ,-0.00079346 ,0.00234985 ,0.00296021 , -0.00048828 ,-0.00509644 ,-0.00854492 ,-0.00885010 ,-0.00558472 , -0.00167847 ,0.00167847 ,0.00250244 ,0.00134277 ,-0.00183105 , -0.00418091 ,-0.00408936 ,-0.00085449 ,0.00119019 ,0.00125122 , -0.00183105 ,-0.00555420 ,-0.00793457 ,-0.00683594 ,-0.00238037 , 0.00109863 ,0.00253296 ,0.00024414 ,-0.00314331 ,-0.00585938 , -0.00659180 ,-0.00564575 ,-0.00308228 ,0.00009155 ,0.00320435 , 0.00552368 ,0.00662231 ,0.00497437 ,0.00112915 ,-0.00338745 , -0.00683594 ,-0.00790405 ,-0.00625610 ,-0.00149536 ,0.00335693 , 0.00607300 ,0.00482178 ,0.00125122 ,-0.00234985 ,-0.00415039 , -0.00463867 ,-0.00265503 ,-0.00006104 ,0.00100708 ,0.00103760 , -0.00067139 ,-0.00167847 ,-0.00094604 ,0.00067139 ,0.00073242 , 0.00000000 ,0.00061035 ,0.00296021 ,0.00344849 ,0.00146484 , -0.00177002 ,-0.00402832 ,-0.00299072 ,0.00155640 ,0.00729370 , 0.01116943 ,0.01046753 ,0.00433350 ,-0.00341797 ,-0.00857544 , -0.00924683 ,-0.00613403 ,-0.00134277 ,0.00283813 ,0.00451660 , 0.00616455 ,0.00677490 ,0.00537109 ,0.00332642 ,0.00292969 , 0.00344849 ,0.00354004 ,0.00238037 ,0.00277710 ,0.00552368 , 0.00918579 ,0.00976562 ,0.00631714 ,0.00274658 ,-0.00064087 , -0.00326538 ,-0.00558472 ,-0.00634766 ,-0.00424194 ,-0.00073242 , 0.00274658 ,0.00567627 ,0.00714111 ,0.00860596 ,0.00830078 , 0.00479126 ,0.00051880 ,-0.00097656 ,0.00103760 ,0.00347900 , 0.00421143 ,0.00457764 ,0.00433350 ,0.00289917 ,-0.00070190 , -0.00418091 ,-0.00534058 ,-0.00350952 ,-0.00097656 ,-0.00039673 , -0.00085449 ,-0.00048828 ,0.00210571 ,0.00329590 ,0.00073242 , -0.00302124 ,-0.00329590 ,-0.00170898 ,-0.00079346 ,-0.00045776 , 0.00164795 ,0.00418091 ,0.00424194 ,0.00146484 ,-0.00021362 , 0.00115967 ,0.00448608 ,0.00585938 ,0.00488281 ,0.00268555 , 0.00064087 ,-0.00122070 ,-0.00286865 ,-0.00460815 ,-0.00518799 , -0.00494385 ,-0.00534058 ,-0.00637817 ,-0.00485229 ,-0.00177002 , 0.00128174 ,0.00253296 ,0.00222778 ,0.00286865 ,0.00271606 , 0.00238037 ,0.00128174 ,0.00079346 ,0.00128174 ,0.00216675 , 0.00357056 ,0.00469971 ,0.00299072 ,-0.00180054 ,-0.00585938 , -0.00643921 ,-0.00463867 ,-0.00210571 ,0.00347900 ,0.00823975 , 0.00857544 ,0.00411987 ,-0.00036621 ,-0.00347900 ,-0.00219727 , 0.00073242 ,0.00259399 ,0.00225830 ,0.00003052 ,-0.00183105 , -0.00265503 ,-0.00369263 ,-0.00466919 ,-0.00436401 ,-0.00164795 , 0.00167847 ,0.00482178 ,0.00604248 ,0.00570679 ,0.00296021 , -0.00152588 ,-0.00457764 ,-0.00433350 ,-0.00057983 ,0.00268555 , 0.00488281 ,0.00402832 ,0.00222778 ,-0.00146484 ,-0.00631714 , -0.00967407 ,-0.00793457 ,0.00000000 ,0.00802612 ,0.01055908 , 0.00665283 ,0.00042725 ,-0.00527954 ,-0.00784302 ,-0.00625610 , 0.00024414 ,0.00704956 ,0.00973511 ,0.00637817 ,-0.00006104 , -0.00503540 ,-0.00662231 ,-0.00643921 ,-0.00396729 ,0.00039673 , 0.00457764 ,0.00588989 ,0.00289917 ,-0.00158691 ,-0.00518799 , -0.00567627 ,-0.00296021 ,0.00079346 ,0.00439453 ,0.00588989 , 0.00222778 ,-0.00497437 ,-0.01000977 ,-0.00915527 ,-0.00302124 , 0.00323486 ,0.00720215 ,0.00839233 ,0.00735474 ,0.00387573 , -0.00070190 ,-0.00375366 ,-0.00241089 ,0.00100708 ,0.00314331 , 0.00292969 ,0.00189209 ,0.00057983 ,-0.00201416 ,-0.00509644 , -0.00643921 ,-0.00555420 ,-0.00195312 ,0.00256348 ,0.00479126 , 0.00338745 ,0.00012207 ,-0.00463867 ,-0.00726318 ,-0.00729370 , -0.00183105 ,0.00582886 ,0.01000977 ,0.00805664 ,0.00396729 , -0.00027466 ,-0.00228882 ,-0.00347900 ,-0.00277710 ,0.00057983 , 0.00543213 ,0.00793457 ,0.00515747 ,-0.00018311 ,-0.00564575 , -0.00851440 ,-0.00955200 ,-0.00607300 ,0.00057983 ,0.00765991 , 0.01068115 ,0.00708008 ,-0.00100708 ,-0.00845337 ,-0.01217651 , -0.01016235 ,-0.00354004 ,0.00460815 ,0.01199341 ,0.01373291 , 0.00827026 ,-0.00057983 ,-0.00744629 ,-0.00921631 ,-0.00695801 , -0.00286865 ,0.00094604 ,0.00369263 ,0.00378418 ,0.00103760 , -0.00177002 ,-0.00286865 ,-0.00222778 ,-0.00128174 ,-0.00061035 , -0.00106812 ,-0.00219727 ,-0.00357056 ,-0.00463867 ,-0.00405884 , -0.00201416 ,0.00048828 ,0.00231934 ,0.00372314 ,0.00408936 , 0.00167847 ,-0.00170898 ,-0.00418091 ,-0.00448608 ,-0.00204468 , 0.00146484 ,0.00399780 ,0.00463867 ,0.00289917 ,-0.00082397 , -0.00588989 ,-0.00949097 ,-0.01055908 ,-0.00765991 ,-0.00299072 , 0.00061035 ,0.00119019 ,0.00216675 ,0.00286865 ,0.00317383 , 0.00274658 ,0.00277710 ,0.00320435 ,0.00195312 ,-0.00149536 , -0.00558472 ,-0.00701904 ,-0.00717163 ,-0.00759888 ,-0.00659180 , -0.00283813 ,0.00283813 ,0.00537109 ,0.00415039 ,-0.00042725 , -0.00451660 ,-0.00674438 ,-0.00561523 ,-0.00180054 ,0.00283813 , 0.00723267 ,0.00778198 ,0.00195312 ,-0.00692749 ,-0.01309204 , -0.01342773 ,-0.00885010 ,-0.00234985 ,0.00396729 ,0.00762939 , 0.00701904 ,0.00186157 ,-0.00378418 ,-0.00650024 ,-0.00552368 , -0.00302124 ,0.00018311 ,0.00195312 ,0.00210571 ,0.00109863 , -0.00198364 ,-0.00506592 ,-0.00665283 ,-0.00540161 ,-0.00149536 , 0.00207520 ,0.00442505 ,0.00418091 ,0.00259399 ,0.00000000 , -0.00213623 ,-0.00347900 ,-0.00170898 ,0.00094604 ,0.00250244 , 0.00082397 ,-0.00268555 ,-0.00686646 ,-0.00888062 ,-0.00842285 , -0.00653076 ,-0.00250244 ,0.00274658 ,0.00723267 ,0.00765991 , 0.00524902 ,0.00204468 ,-0.00070190 ,-0.00225830 ,-0.00149536 , 0.00006104 ,0.00271606 ,0.00442505 ,0.00384521 ,-0.00128174 , -0.00671387 ,-0.01150513 ,-0.01272583 ,-0.01141357 ,-0.00662231 , -0.00091553 ,0.00418091 ,0.00570679 ,0.00396729 ,0.00018311 , -0.00277710 ,-0.00354004 ,-0.00311279 ,-0.00296021 ,-0.00286865 , -0.00253296 ,-0.00091553 ,-0.00048828 ,-0.00122070 ,-0.00225830 , -0.00238037 ,-0.00207520 ,-0.00286865 ,-0.00335693 ,-0.00225830 , 0.00021362 ,0.00131226 ,0.00115967 ,0.00091553 ,0.00115967 , 0.00042725 ,-0.00143433 ,-0.00485229 ,-0.00674438 ,-0.00753784 , -0.00714111 ,-0.00604248 ,-0.00296021 ,0.00213623 ,0.00692749 , 0.00891113 ,0.00784302 ,0.00393677 ,-0.00039673 ,-0.00250244 , -0.00332642 ,-0.00375366 ,-0.00198364 ,0.00180054 ,0.00469971 , 0.00469971 ,0.00268555 ,-0.00064087 ,-0.00372314 ,-0.00521851 , -0.00454712 ,-0.00137329 ,0.00170898 ,0.00366211 ,0.00427246 , 0.00369263 ,0.00271606 ,0.00219727 ,0.00210571 ,0.00302124 , 0.00277710 ,0.00061035 ,-0.00231934 ,-0.00457764 ,-0.00625610 , -0.00540161 ,-0.00177002 ,0.00433350 ,0.00949097 ,0.01092529 , 0.00872803 ,0.00430298 ,-0.00009155 ,-0.00277710 ,-0.00344849 , -0.00256348 ,-0.00024414 ,0.00250244 ,0.00418091 ,0.00479126 , 0.00408936 ,0.00289917 ,-0.00009155 ,-0.00259399 ,-0.00494385 , -0.00387573 ,-0.00128174 ,0.00311279 ,0.00619507 ,0.00808716 , 0.00982666 ,0.01004028 ,0.00775146 ,0.00338745 ,-0.00051880 , -0.00198364 ,-0.00097656 ,0.00033569 ,0.00103760 ,0.00292969 , 0.00579834 ,0.00704956 ,0.00613403 ,0.00497437 ,0.00439453 , 0.00320435 ,0.00131226 ,0.00021362 ,0.00070190 ,0.00411987 , 0.00656128 ,0.00796509 ,0.00646973 ,0.00488281 ,0.00259399 , 0.00106812 ,0.00122070 ,0.00259399 ,0.00518799 ,0.00686646 , 0.00601196 ,0.00317383 ,0.00134277 ,0.00320435 ,0.00659180 , 0.00885010 ,0.00921631 ,0.00759888 ,0.00494385 ,0.00131226 , -0.00167847 ,-0.00137329 ,0.00091553 ,0.00363159 ,0.00646973 , 0.00820923 ,0.00863647 ,0.00695801 ,0.00415039 ,0.00231934 , 0.00198364 ,0.00411987 ,0.00683594 ,0.00942993 ,0.00991821 , 0.00701904 ,0.00311279 ,-0.00057983 ,-0.00213623 ,-0.00076294 , 0.00277710 ,0.00698853 ,0.00930786 ,0.00918579 ,0.00683594 , 0.00384521 ,0.00137329 ,0.00033569 ,0.00173950 ,0.00390625 , 0.00494385 ,0.00659180 ,0.00689697 ,0.00698853 ,0.00613403 , 0.00488281 ,0.00308228 ,0.00137329 ,0.00048828 ,0.00131226 , 0.00390625 ,0.00680542 ,0.00793457 ,0.00616455 ,0.00439453 , 0.00387573 ,0.00448608 ,0.00451660 ,0.00396729 ,0.00299072 , 0.00164795 ,0.00109863 ,0.00207520 ,0.00381470 ,0.00592041 , 0.00637817 ,0.00418091 ,0.00152588 ,-0.00073242 ,-0.00109863 , 0.00051880 ,0.00271606 ,0.00579834 ,0.00833130 ,0.00991821 , 0.00845337 ,0.00402832 ,-0.00158691 ,-0.00460815 ,-0.00509644 , -0.00213623 ,0.00106812 ,0.00521851 ,0.00878906 ,0.00906372 , 0.00543213 ,-0.00076294 ,-0.00524902 ,-0.00567627 ,-0.00280762 , 0.00216675 ,0.00628662 ,0.00732422 ,0.00564575 ,0.00152588 , -0.00198364 ,-0.00231934 ,-0.00033569 ,0.00253296 ,0.00271606 , 0.00256348 ,0.00170898 ,0.00097656 ,-0.00042725 ,-0.00170898 , -0.00198364 ,-0.00006104 ,0.00350952 ,0.00692749 ,0.00845337 , 0.00778198 ,0.00515747 ,0.00045776 ,-0.00442505 ,-0.00659180 , -0.00503540 ,-0.00067139 ,0.00341797 ,0.00552368 ,0.00436401 , 0.00289917 ,0.00183105 ,0.00103760 ,0.00103760 ,0.00085449 , 0.00262451 ,0.00515747 ,0.00634766 ,0.00640869 ,0.00537109 , 0.00485229 ,0.00363159 ,0.00210571 ,-0.00030518 ,-0.00051880 , 0.00085449 ,0.00195312 ,0.00158691 ,-0.00015259 ,-0.00048828 , 0.00048828 ,0.00225830 ,0.00445557 ,0.00686646 ,0.00720215 , 0.00555420 ,0.00158691 ,-0.00128174 ,-0.00097656 ,0.00112915 , 0.00369263 ,0.00537109 ,0.00616455 ,0.00656128 ,0.00579834 , 0.00363159 ,0.00094604 ,0.00003052 ,0.00085449 ,0.00265503 , 0.00363159 ,0.00393677 ,0.00350952 ,0.00335693 ,0.00259399 , 0.00067139 ,-0.00125122 ,-0.00177002 ,-0.00057983 ,0.00115967 , 0.00274658 ,0.00305176 ,0.00320435 ,0.00238037 ,0.00213623 , 0.00216675 ,0.00344849 ,0.00509644 ,0.00506592 ,0.00360107 , 0.00201416 ,0.00119019 ,0.00122070 ,0.00085449 ,0.00088501 , 0.00115967 ,0.00244141 ,0.00338745 ,0.00244141 ,0.00045776 , -0.00003052 ,0.00027466 ,0.00146484 ,0.00268555 ,0.00387573 , 0.00601196 ,0.00634766 ,0.00405884 ,0.00054932 ,-0.00161743 , -0.00180054 ,-0.00140381 ,-0.00112915 ,-0.00073242 ,0.00082397 , 0.00134277 ,0.00164795 ,0.00061035 ,0.00006104 ,0.00082397 , 0.00286865 ,0.00457764 ,0.00524902 ,0.00500488 ,0.00378418 , 0.00134277 ,-0.00198364 ,-0.00375366 ,-0.00216675 ,0.00091553 , 0.00329590 ,0.00137329 ,-0.00106812 ,-0.00396729 ,-0.00592041 , -0.00662231 ,-0.00503540 ,-0.00119019 ,0.00308228 ,0.00549316 , 0.00473022 ,0.00231934 ,-0.00042725 ,-0.00280762 ,-0.00521851 , -0.00613403 ,-0.00390625 ,0.00039673 ,0.00387573 ,0.00399780 , 0.00302124 ,0.00106812 ,-0.00131226 ,-0.00393677 ,-0.00506592 , -0.00387573 ,-0.00192261 ,-0.00064087 ,-0.00146484 ,-0.00198364 , -0.00326538 ,-0.00372314 ,-0.00402832 ,-0.00375366 ,-0.00329590 , -0.00210571 ,-0.00064087 ,0.00006104 ,0.00070190 ,0.00073242 , 0.00051880 ,-0.00061035 ,-0.00268555 ,-0.00219727 ,0.00009155 , 0.00177002 ,0.00119019 ,-0.00006104 ,-0.00164795 ,-0.00317383 , -0.00436401 ,-0.00515747 ,-0.00451660 ,-0.00286865 ,-0.00039673 , 0.00201416 ,0.00247192 ,0.00244141 ,0.00091553 ,-0.00012207 , -0.00238037 ,-0.00424194 ,-0.00476074 ,-0.00296021 ,-0.00131226 , 0.00042725 ,0.00018311 ,-0.00042725 ,-0.00262451 ,-0.00527954 , -0.00643921 ,-0.00457764 ,-0.00085449 ,0.00225830 ,0.00393677 , 0.00390625 ,0.00177002 ,-0.00106812 ,-0.00378418 ,-0.00537109 , -0.00506592 ,-0.00384521 ,-0.00204468 ,-0.00061035 ,0.00112915 , 0.00262451 ,0.00292969 ,0.00143433 ,-0.00091553 ,-0.00302124 , -0.00427246 ,-0.00387573 ,-0.00213623 ,-0.00073242 ,0.00091553 , 0.00119019 ,0.00115967 ,-0.00054932 ,-0.00152588 ,-0.00332642 , -0.00408936 ,-0.00469971 ,-0.00366211 ,-0.00155640 ,0.00094604 , 0.00198364 ,0.00192261 ,0.00106812 ,0.00015259 ,-0.00012207 , -0.00003052 ,0.00173950 ,0.00225830 ,0.00183105 ,0.00057983 , -0.00115967 ,-0.00225830 ,-0.00305176 ,-0.00198364 ,-0.00033569 , 0.00170898 ,0.00247192 ,0.00286865 ,0.00259399 ,0.00201416 , 0.00085449 ,-0.00015259 ,-0.00100708 ,-0.00073242 ,-0.00061035 , 0.00030518 ,0.00067139 ,0.00015259 ,-0.00152588 ,-0.00326538 , -0.00405884 ,-0.00393677 ,-0.00274658 ,-0.00137329 ,-0.00024414 , 0.00085449 ,0.00146484 ,0.00067139 ,-0.00030518 ,-0.00173950 , -0.00204468 ,-0.00186157 ,-0.00140381 ,-0.00097656 ,-0.00027466 , 0.00012207 ,-0.00045776 ,-0.00186157 ,-0.00323486 ,-0.00354004 , -0.00323486 ,-0.00247192 ,-0.00131226 ,-0.00167847 ,-0.00250244 , -0.00314331 ,-0.00268555 ,-0.00268555 ,-0.00308228 ,-0.00399780 , -0.00427246 ,-0.00451660 ,-0.00396729 ,-0.00411987 ,-0.00320435 , -0.00259399 ,-0.00183105 ,-0.00210571 ,-0.00198364 ,-0.00134277 , -0.00024414 ,0.00030518 ,0.00134277 ,0.00097656 ,-0.00018311 , -0.00259399 ,-0.00503540 ,-0.00595093 ,-0.00515747 ,-0.00442505 , -0.00363159 ,-0.00308228 ,-0.00396729 ,-0.00476074 ,-0.00592041 , -0.00573730 ,-0.00509644 ,-0.00326538 ,-0.00262451 ,-0.00180054 , -0.00076294 ,0.00131226 ,0.00106812 ,0.00018311 ,-0.00222778 , -0.00320435 ,-0.00384521 ,-0.00393677 ,-0.00375366 ,-0.00390625 , -0.00442505 ,-0.00515747 ,-0.00646973 ,-0.00604248 ,-0.00442505 , -0.00198364 ,-0.00061035 ,-0.00061035 ,-0.00186157 ,-0.00344849 , -0.00494385 ,-0.00695801 ,-0.00738525 ,-0.00628662 ,-0.00408936 , -0.00231934 ,-0.00143433 ,-0.00140381 ,-0.00149536 ,-0.00195312 , -0.00265503 ,-0.00329590 ,-0.00363159 ,-0.00344849 ,-0.00247192 , -0.00180054 ,-0.00115967 ,-0.00143433 ,-0.00125122 ,-0.00250244 , -0.00476074 ,-0.00637817 ,-0.00555420 ,-0.00451660 ,-0.00320435 , -0.00210571 ,-0.00048828 ,0.00128174 ,0.00183105 ,0.00042725 , -0.00082397 ,-0.00149536 ,-0.00125122 ,-0.00198364 ,-0.00381470 , -0.00469971 ,-0.00378418 ,-0.00149536 ,-0.00006104 ,-0.00015259 , -0.00119019 ,-0.00183105 ,-0.00256348 ,-0.00320435 ,-0.00253296 , -0.00051880 ,0.00173950 ,0.00247192 ,0.00189209 ,0.00152588 , 0.00170898 ,0.00064087 ,-0.00122070 ,-0.00335693 ,-0.00421143 , -0.00332642 ,-0.00186157 ,-0.00057983 ,0.00164795 ,0.00408936 , 0.00442505 ,0.00219727 ,-0.00134277 ,-0.00244141 ,-0.00146484 , 0.00015259 ,0.00048828 ,0.00012207 ,0.00030518 ,0.00057983 , 0.00003052 ,-0.00119019 ,-0.00143433 ,-0.00088501 ,0.00024414 , 0.00054932 ,0.00057983 ,0.00106812 ,0.00207520 ,0.00259399 , 0.00228882 ,0.00082397 ,0.00076294 ,0.00204468 ,0.00271606 , 0.00170898 ,-0.00036621 ,-0.00128174 ,-0.00189209 ,-0.00268555 , -0.00161743 ,0.00140381 ,0.00418091 ,0.00537109 ,0.00357056 , 0.00195312 ,0.00146484 ,0.00164795 ,0.00109863 ,0.00036621 , 0.00061035 ,0.00164795 ,0.00158691 ,0.00128174 ,0.00051880 , 0.00115967 ,0.00201416 ,0.00173950 ,0.00088501 ,0.00100708 , 0.00256348 ,0.00405884 ,0.00302124 ,0.00222778 ,0.00094604 , 0.00042725 ,0.00088501 ,0.00228882 ,0.00314331 ,0.00296021 , 0.00140381 ,-0.00094604 ,-0.00228882 ,-0.00079346 ,0.00302124 , 0.00683594 ,0.00793457 ,0.00686646 ,0.00430298 ,0.00073242 , -0.00247192 ,-0.00393677 ,-0.00320435 ,-0.00094604 ,0.00119019 , 0.00231934 ,0.00134277 ,-0.00009155 ,-0.00250244 ,-0.00350952 , -0.00308228 ,-0.00064087 ,0.00234985 ,0.00491333 ,0.00592041 , 0.00534058 ,0.00363159 ,0.00189209 ,0.00115967 ,0.00009155 , 0.00079346 ,0.00125122 ,0.00054932 ,-0.00070190 ,-0.00164795 , -0.00149536 ,-0.00100708 ,0.00064087 ,0.00241089 ,0.00402832 , 0.00436401 ,0.00418091 ,0.00305176 ,0.00216675 ,0.00061035 , -0.00088501 ,-0.00189209 ,-0.00201416 ,-0.00106812 ,-0.00024414 , 0.00015259 ,0.00115967 ,0.00155640 ,0.00198364 ,0.00112915 , 0.00079346 ,0.00152588 ,0.00393677 ,0.00546265 ,0.00582886 , 0.00445557 ,0.00250244 ,0.00042725 ,-0.00106812 ,-0.00161743 , -0.00103760 ,0.00051880 ,0.00109863 ,0.00149536 ,0.00143433 , 0.00125122 ,0.00241089 ,0.00289917 ,0.00283813 ,0.00286865 , 0.00454712 ,0.00628662 ,0.00662231 ,0.00408936 ,0.00073242 , -0.00170898 ,-0.00274658 ,-0.00320435 ,-0.00091553 ,0.00280762 , 0.00555420 ,0.00521851 ,0.00305176 ,0.00106812 ,-0.00018311 , -0.00009155 ,0.00091553 ,0.00253296 ,0.00466919 ,0.00579834 , 0.00521851 ,0.00399780 ,0.00140381 ,-0.00033569 ,-0.00109863 , -0.00158691 ,-0.00067139 ,0.00128174 ,0.00421143 ,0.00573730 , 0.00506592 ,0.00314331 ,0.00180054 ,0.00140381 ,0.00204468 , 0.00320435 ,0.00445557 ,0.00448608 ,0.00302124 ,0.00039673 , -0.00155640 ,-0.00204468 ,-0.00155640 ,0.00009155 ,0.00201416 , 0.00320435 ,0.00381470 ,0.00335693 ,0.00250244 ,0.00073242 , -0.00036621 ,-0.00039673 ,0.00045776 ,0.00277710 ,0.00527954 , 0.00598145 ,0.00466919 ,0.00119019 ,-0.00180054 ,-0.00363159 , -0.00375366 ,-0.00180054 ,0.00036621 ,0.00262451 ,0.00357056 , 0.00378418 ,0.00283813 ,0.00238037 ,0.00198364 ,0.00234985 , 0.00280762 ,0.00271606 ,0.00183105 ,0.00039673 ,-0.00073242 , -0.00155640 ,-0.00143433 ,-0.00064087 ,0.00051880 ,0.00225830 , 0.00317383 ,0.00314331 ,0.00170898 ,-0.00042725 ,-0.00204468 , -0.00296021 ,-0.00192261 ,-0.00036621 ,0.00216675 ,0.00354004 , 0.00350952 ,0.00131226 ,-0.00170898 ,-0.00479126 ,-0.00595093 , -0.00506592 ,-0.00149536 ,0.00192261 ,0.00308228 ,0.00256348 , 0.00024414 ,-0.00195312 ,-0.00268555 ,-0.00140381 ,-0.00033569 , 0.00061035 ,0.00122070 ,0.00009155 ,-0.00167847 ,-0.00332642 , -0.00390625 ,-0.00265503 ,-0.00054932 ,0.00119019 ,0.00222778 , 0.00198364 ,0.00024414 ,-0.00329590 ,-0.00561523 ,-0.00598145 , -0.00369263 ,0.00003052 ,0.00372314 ,0.00579834 ,0.00463867 , 0.00039673 ,-0.00421143 ,-0.00701904 ,-0.00665283 ,-0.00463867 , -0.00134277 ,0.00137329 ,0.00338745 ,0.00296021 ,0.00152588 , -0.00061035 ,-0.00100708 ,-0.00027466 ,0.00097656 ,0.00180054 , 0.00192261 ,0.00146484 ,-0.00009155 ,-0.00204468 ,-0.00347900 , -0.00247192 ,-0.00024414 ,0.00198364 ,0.00238037 ,0.00238037 , 0.00119019 ,0.00006104 ,-0.00177002 ,-0.00195312 ,-0.00097656 , 0.00155640 ,0.00369263 ,0.00387573 ,0.00244141 ,0.00067139 , -0.00064087 ,-0.00210571 ,-0.00311279 ,-0.00225830 ,0.00003052 , 0.00250244 ,0.00299072 ,0.00244141 ,0.00207520 ,0.00219727 , 0.00198364 ,0.00180054 ,0.00207520 ,0.00274658 ,0.00228882 , 0.00024414 ,-0.00170898 ,-0.00183105 ,0.00003052 ,0.00140381 , 0.00213623 ,0.00247192 ,0.00268555 ,0.00253296 ,0.00198364 , 0.00082397 ,0.00082397 ,0.00192261 ,0.00271606 ,0.00299072 , 0.00250244 ,0.00347900 ,0.00387573 ,0.00323486 ,0.00088501 , -0.00109863 ,-0.00244141 ,-0.00262451 ,-0.00259399 ,-0.00177002 , 0.00000000 ,0.00286865 ,0.00460815 ,0.00540161 ,0.00595093 , 0.00588989 ,0.00476074 ,0.00244141 ,0.00021362 ,-0.00180054 , -0.00170898 ,-0.00054932 ,0.00152588 ,0.00241089 ,0.00308228 , 0.00317383 ,0.00326538 ,0.00225830 ,0.00228882 ,0.00161743 , 0.00094604 ,0.00024414 ,-0.00027466 ,0.00012207 ,0.00088501 , 0.00137329 ,0.00161743 ,0.00149536 ,0.00189209 ,0.00076294 , -0.00045776 ,-0.00204468 ,-0.00302124 ,-0.00305176 ,-0.00192261 , 0.00000000 ,0.00259399 ,0.00506592 ,0.00677490 ,0.00799561 , 0.00701904 ,0.00427246 ,0.00003052 ,-0.00360107 ,-0.00579834 , -0.00503540 ,-0.00222778 ,0.00070190 ,0.00277710 ,0.00299072 , 0.00210571 ,0.00122070 ,-0.00021362 ,-0.00115967 ,-0.00201416 , -0.00238037 ,-0.00195312 ,-0.00167847 ,-0.00042725 ,0.00128174 , 0.00247192 ,0.00253296 ,0.00186157 ,0.00036621 ,-0.00112915 , -0.00204468 ,-0.00241089 ,-0.00253296 ,-0.00189209 ,-0.00094604 , -0.00048828 ,0.00076294 ,0.00329590 ,0.00540161 ,0.00552368 , 0.00326538 ,-0.00064087 ,-0.00408936 ,-0.00616455 ,-0.00582886 , -0.00375366 ,-0.00048828 ,0.00289917 ,0.00405884 ,0.00314331 , 0.00091553 ,-0.00097656 ,-0.00213623 ,-0.00238037 ,-0.00189209 , -0.00067139 ,-0.00039673 ,0.00091553 ,0.00161743 ,0.00192261 , 0.00183105 ,0.00177002 ,0.00137329 ,-0.00018311 ,-0.00201416 , -0.00231934 ,-0.00244141 ,-0.00228882 ,-0.00250244 ,-0.00189209 , -0.00036621 ,0.00201416 ,0.00408936 ,0.00463867 ,0.00442505 , 0.00286865 ,0.00015259 ,-0.00296021 ,-0.00399780 ,-0.00274658 , 0.00009155 ,0.00244141 ,0.00268555 ,0.00262451 ,0.00253296 , 0.00161743 ,-0.00036621 ,-0.00180054 ,-0.00177002 ,-0.00021362 , 0.00180054 ,0.00228882 ,0.00167847 ,0.00073242 ,-0.00085449 , -0.00131226 ,-0.00100708 ,0.00061035 ,0.00231934 ,0.00354004 , 0.00268555 ,0.00079346 ,-0.00100708 ,-0.00213623 ,-0.00189209 , -0.00018311 ,0.00271606 ,0.00579834 ,0.00732422 ,0.00570679 , 0.00167847 ,-0.00231934 ,-0.00411987 ,-0.00418091 ,-0.00274658 , -0.00036621 ,0.00244141 ,0.00424194 ,0.00491333 ,0.00274658 , 0.00073242 ,-0.00076294 ,-0.00137329 ,-0.00088501 ,0.00024414 , 0.00256348 ,0.00482178 ,0.00512695 ,0.00350952 ,0.00161743 , 0.00045776 ,0.00033569 ,0.00076294 ,0.00088501 ,0.00161743 , 0.00195312 ,0.00134277 ,0.00030518 ,0.00015259 ,0.00125122 , 0.00314331 ,0.00430298 ,0.00418091 ,0.00292969 ,0.00170898 , 0.00067139 ,-0.00088501 ,-0.00106812 ,0.00018311 ,0.00299072 , 0.00418091 ,0.00418091 ,0.00314331 ,0.00225830 ,0.00079346 , -0.00012207 ,-0.00030518 ,0.00109863 ,0.00268555 ,0.00372314 , 0.00326538 ,0.00247192 ,0.00207520 ,0.00180054 ,0.00216675 , 0.00222778 ,0.00302124 ,0.00390625 ,0.00384521 ,0.00210571 , -0.00067139 ,-0.00241089 ,-0.00234985 ,-0.00100708 ,0.00128174 , 0.00375366 ,0.00524902 ,0.00396729 ,0.00024414 ,-0.00198364 , -0.00286865 ,-0.00143433 ,0.00042725 ,0.00183105 ,0.00320435 , 0.00399780 ,0.00372314 ,0.00195312 ,-0.00012207 ,-0.00054932 , 0.00042725 ,0.00204468 ,0.00399780 ,0.00540161 ,0.00576782 , 0.00418091 ,0.00054932 ,-0.00244141 ,-0.00259399 ,-0.00061035 , 0.00292969 ,0.00427246 ,0.00427246 ,0.00283813 ,0.00061035 , -0.00183105 ,-0.00341797 ,-0.00161743 ,0.00134277 ,0.00418091 , 0.00503540 ,0.00421143 ,0.00335693 ,0.00177002 ,0.00057983 , -0.00018311 ,0.00057983 ,0.00103760 ,0.00137329 ,0.00061035 , 0.00051880 ,0.00109863 ,0.00170898 ,0.00180054 ,0.00213623 , 0.00347900 ,0.00415039 ,0.00280762 ,0.00054932 ,-0.00076294 , -0.00115967 ,0.00015259 ,0.00125122 ,0.00213623 ,0.00274658 , 0.00234985 ,0.00201416 ,0.00061035 ,-0.00094604 ,-0.00137329 , -0.00006104 ,0.00201416 ,0.00372314 ,0.00473022 ,0.00473022 , 0.00350952 ,0.00091553 ,-0.00073242 ,-0.00051880 ,0.00109863 , 0.00296021 ,0.00338745 ,0.00289917 ,0.00115967 ,-0.00073242 , -0.00216675 ,-0.00216675 ,-0.00027466 ,0.00265503 ,0.00433350 , 0.00369263 ,0.00238037 ,0.00073242 ,0.00039673 ,-0.00036621 , -0.00064087 ,0.00085449 ,0.00296021 ,0.00469971 ,0.00448608 , 0.00317383 ,0.00158691 ,-0.00009155 ,-0.00061035 ,-0.00036621 , 0.00189209 ,0.00378418 ,0.00457764 ,0.00338745 ,0.00183105 , 0.00073242 ,0.00103760 ,0.00167847 ,0.00225830 ,0.00177002 , 0.00100708 ,-0.00048828 ,-0.00146484 ,-0.00189209 ,0.00003052 , 0.00320435 ,0.00564575 ,0.00582886 ,0.00390625 ,0.00204468 , 0.00030518 ,-0.00057983 ,-0.00094604 ,-0.00085449 ,0.00051880 , 0.00231934 ,0.00326538 ,0.00378418 ,0.00329590 ,0.00189209 , 0.00006104 ,-0.00125122 ,-0.00140381 ,-0.00024414 ,0.00192261 , 0.00311279 ,0.00302124 ,0.00198364 ,0.00137329 ,0.00067139 , 0.00021362 ,0.00054932 ,0.00128174 ,0.00177002 ,0.00027466 , -0.00143433 ,-0.00274658 ,-0.00216675 ,-0.00122070 ,0.00036621 , 0.00225830 ,0.00396729 ,0.00332642 ,0.00149536 ,-0.00070190 , -0.00189209 ,-0.00204468 ,-0.00149536 ,-0.00076294 ,0.00021362 , 0.00109863 ,0.00122070 ,0.00097656 ,0.00073242 ,-0.00027466 , -0.00100708 ,-0.00225830 ,-0.00231934 ,-0.00137329 ,-0.00012207 , 0.00036621 ,0.00000000 ,-0.00085449 ,-0.00061035 ,-0.00036621 , -0.00003052 ,-0.00085449 ,-0.00195312 ,-0.00292969 ,-0.00317383 , -0.00347900 ,-0.00283813 ,-0.00152588 ,-0.00039673 ,0.00045776 , 0.00131226 ,0.00119019 ,0.00067139 ,-0.00094604 ,-0.00259399 , -0.00369263 ,-0.00375366 ,-0.00360107 ,-0.00292969 ,-0.00103760 , 0.00033569 ,0.00082397 ,0.00018311 ,-0.00158691 ,-0.00219727 , -0.00247192 ,-0.00201416 ,-0.00173950 ,-0.00119019 ,-0.00042725 , -0.00054932 ,-0.00125122 ,-0.00247192 ,-0.00274658 ,-0.00265503 , -0.00155640 ,-0.00115967 ,-0.00042725 ,-0.00054932 ,-0.00094604 , -0.00219727 ,-0.00311279 ,-0.00241089 ,-0.00061035 ,0.00103760 , 0.00198364 ,0.00170898 ,0.00039673 ,-0.00143433 ,-0.00296021 , -0.00381470 ,-0.00289917 ,-0.00119019 ,0.00021362 ,0.00042725 , 0.00000000 ,-0.00064087 ,-0.00177002 ,-0.00268555 ,-0.00323486 , -0.00228882 ,-0.00006104 ,0.00180054 ,0.00286865 ,0.00177002 , 0.00033569 ,-0.00137329 ,-0.00244141 ,-0.00259399 ,-0.00192261 , -0.00033569 ,0.00088501 ,0.00183105 ,0.00152588 ,0.00021362 , -0.00073242 ,-0.00152588 ,-0.00173950 ,-0.00106812 ,-0.00042725 , 0.00054932 ,0.00054932 ,0.00021362 ,0.00003052 ,-0.00027466 , -0.00027466 ,-0.00033569 ,-0.00048828 ,-0.00048828 ,-0.00067139 , -0.00082397 ,-0.00112915 ,-0.00115967 ,-0.00082397 ,-0.00021362 , 0.00070190 ,0.00106812 ,0.00042725 ,-0.00036621 ,-0.00131226 , -0.00192261 ,-0.00204468 ,-0.00152588 ,-0.00051880 ,0.00097656 , 0.00161743 ,0.00076294 ,-0.00073242 ,-0.00222778 ,-0.00247192 , -0.00134277 ,0.00003052 ,0.00094604 ,0.00131226 ,0.00039673 , -0.00064087 ,-0.00164795 ,-0.00247192 ,-0.00299072 ,-0.00277710 , -0.00241089 ,-0.00170898 ,-0.00125122 ,-0.00076294 ,-0.00143433 , -0.00262451 ,-0.00347900 ,-0.00250244 ,-0.00103760 ,0.00054932 , 0.00109863 ,0.00073242 ,-0.00027466 ,-0.00247192 ,-0.00436401 , -0.00473022 ,-0.00320435 ,-0.00112915 ,0.00018311 ,0.00067139 , 0.00057983 ,0.00000000 ,-0.00119019 ,-0.00228882 ,-0.00326538 , -0.00308228 ,-0.00219727 ,-0.00140381 ,-0.00054932 ,0.00021362 , -0.00006104 ,-0.00085449 ,-0.00253296 ,-0.00335693 ,-0.00292969 , -0.00277710 ,-0.00204468 ,-0.00213623 ,-0.00225830 ,-0.00195312 , -0.00189209 ,-0.00164795 ,-0.00122070 ,-0.00051880 ,-0.00003052 , -0.00030518 ,-0.00186157 ,-0.00314331 ,-0.00366211 ,-0.00329590 , -0.00262451 ,-0.00115967 ,0.00036621 ,0.00231934 ,0.00262451 , 0.00091553 ,-0.00143433 ,-0.00296021 ,-0.00433350 ,-0.00402832 , -0.00393677 ,-0.00265503 ,-0.00149536 ,-0.00115967 ,-0.00112915 , -0.00198364 ,-0.00308228 ,-0.00369263 ,-0.00381470 ,-0.00335693 , -0.00204468 ,-0.00051880 ,0.00024414 ,-0.00076294 ,-0.00100708 , -0.00112915 ,-0.00039673 ,0.00015259 ,0.00018311 ,-0.00045776 , -0.00189209 ,-0.00302124 ,-0.00436401 ,-0.00436401 ,-0.00317383 , -0.00100708 ,0.00061035 ,0.00137329 ,0.00155640 ,0.00061035 , -0.00186157 ,-0.00375366 ,-0.00460815 ,-0.00408936 ,-0.00228882 , -0.00042725 ,0.00122070 ,0.00152588 ,0.00085449 ,-0.00045776 , -0.00177002 ,-0.00216675 ,-0.00146484 ,-0.00057983 ,-0.00006104 , -0.00070190 ,-0.00115967 ,-0.00137329 ,-0.00140381 ,-0.00125122 , -0.00115967 ,-0.00097656 ,-0.00054932 ,-0.00088501 ,-0.00183105 , -0.00268555 ,-0.00268555 ,-0.00128174 ,0.00045776 ,0.00238037 , 0.00311279 ,0.00314331 ,0.00201416 ,0.00027466 ,-0.00103760 , -0.00173950 ,-0.00131226 ,-0.00067139 ,-0.00076294 ,-0.00137329 , -0.00201416 ,-0.00256348 ,-0.00271606 ,-0.00183105 ,-0.00009155 , 0.00140381 ,0.00228882 ,0.00213623 ,0.00115967 ,-0.00015259 , -0.00143433 ,-0.00222778 ,-0.00265503 ,-0.00173950 ,-0.00009155 , 0.00219727 ,0.00296021 ,0.00173950 ,-0.00115967 ,-0.00350952 , -0.00448608 ,-0.00347900 ,-0.00119019 ,0.00219727 ,0.00476074 , 0.00454712 ,0.00283813 ,-0.00033569 ,-0.00250244 ,-0.00274658 , -0.00210571 ,-0.00076294 ,0.00079346 ,0.00195312 ,0.00146484 , 0.00009155 ,-0.00219727 ,-0.00296021 ,-0.00234985 ,-0.00067139 , 0.00131226 ,0.00292969 ,0.00347900 ,0.00213623 ,-0.00051880 , -0.00274658 ,-0.00323486 ,-0.00268555 ,-0.00115967 ,0.00015259 , 0.00134277 ,0.00192261 ,0.00109863 ,-0.00018311 ,-0.00146484 , -0.00076294 ,0.00030518 ,0.00119019 ,0.00231934 ,0.00277710 , 0.00256348 ,0.00131226 ,-0.00067139 ,-0.00192261 ,-0.00250244 , -0.00274658 ,-0.00262451 ,-0.00296021 ,-0.00216675 ,-0.00137329 , -0.00051880 ,0.00030518 ,0.00125122 ,0.00207520 ,0.00250244 , 0.00238037 ,0.00088501 ,0.00006104 ,-0.00018311 ,-0.00054932 , -0.00094604 ,-0.00061035 ,0.00097656 ,0.00210571 ,0.00265503 , 0.00125122 ,-0.00048828 ,-0.00183105 ,-0.00222778 ,-0.00195312 , -0.00054932 ,0.00122070 ,0.00231934 ,0.00259399 ,0.00161743 , 0.00079346 ,0.00009155 ,0.00012207 ,-0.00039673 ,-0.00100708 , -0.00134277 ,-0.00064087 ,0.00018311 ,0.00073242 ,0.00131226 , 0.00167847 ,0.00286865 ,0.00360107 ,0.00341797 ,0.00274658 , 0.00219727 ,0.00054932 ,-0.00152588 ,-0.00320435 ,-0.00207520 , 0.00000000 ,0.00146484 ,0.00231934 ,0.00201416 ,0.00131226 , -0.00021362 ,-0.00143433 ,-0.00146484 ,-0.00033569 ,0.00158691 , 0.00283813 ,0.00408936 ,0.00415039 ,0.00308228 ,0.00119019 , -0.00076294 ,-0.00180054 ,-0.00155640 ,-0.00057983 ,0.00100708 , 0.00170898 ,0.00177002 ,0.00158691 ,0.00091553 ,0.00115967 , 0.00082397 ,0.00112915 ,0.00094604 ,0.00140381 ,0.00143433 , 0.00097656 ,0.00061035 ,0.00003052 ,0.00027466 ,0.00073242 , 0.00146484 ,0.00234985 ,0.00225830 ,0.00207520 ,0.00115967 , 0.00018311 ,0.00076294 ,0.00204468 ,0.00378418 ,0.00537109 , 0.00497437 ,0.00378418 ,0.00128174 ,-0.00076294 ,-0.00219727 , -0.00183105 ,0.00039673 ,0.00219727 ,0.00372314 ,0.00338745 , 0.00311279 ,0.00274658 ,0.00170898 ,0.00085449 ,0.00064087 , 0.00180054 ,0.00305176 ,0.00399780 ,0.00357056 ,0.00262451 , 0.00097656 ,-0.00033569 ,-0.00033569 ,0.00051880 ,0.00274658 , 0.00396729 ,0.00454712 ,0.00411987 ,0.00366211 ,0.00323486 , 0.00329590 ,0.00280762 ,0.00216675 ,0.00201416 ,0.00231934 , 0.00259399 ,0.00167847 ,0.00015259 ,-0.00131226 ,-0.00192261 , -0.00137329 ,-0.00015259 ,0.00216675 ,0.00439453 ,0.00604248 , 0.00616455 ,0.00476074 ,0.00317383 ,0.00250244 ,0.00173950 , 0.00122070 ,0.00067139 ,0.00140381 ,0.00228882 ,0.00280762 , 0.00180054 ,0.00085449 ,-0.00054932 ,-0.00106812 ,-0.00094604 , 0.00003052 ,0.00219727 ,0.00421143 ,0.00527954 ,0.00509644 , 0.00323486 ,0.00161743 ,0.00042725 ,-0.00051880 ,-0.00039673 , -0.00051880 ,0.00006104 ,0.00039673 ,0.00143433 ,0.00234985 , 0.00277710 ,0.00299072 ,0.00228882 ,0.00238037 ,0.00216675 , 0.00140381 ,0.00051880 ,-0.00106812 ,-0.00149536 ,-0.00146484 , -0.00088501 ,0.00036621 ,0.00122070 ,0.00177002 ,0.00128174 , 0.00054932 ,-0.00030518 ,-0.00106812 ,-0.00128174 ,-0.00009155 , 0.00189209 ,0.00372314 ,0.00463867 ,0.00433350 ,0.00219727 , -0.00027466 ,-0.00183105 ,-0.00213623 ,-0.00085449 ,0.00015259 , 0.00079346 ,0.00140381 ,0.00189209 ,0.00241089 ,0.00204468 , 0.00161743 ,0.00125122 ,0.00134277 ,0.00122070 ,0.00155640 , 0.00231934 ,0.00201416 ,0.00149536 ,0.00048828 ,0.00042725 , 0.00070190 ,0.00195312 ,0.00296021 ,0.00323486 ,0.00216675 , 0.00100708 ,-0.00045776 ,-0.00076294 ,0.00003052 ,0.00189209 , 0.00360107 ,0.00497437 ,0.00439453 ,0.00341797 ,0.00201416 , 0.00067139 ,-0.00006104 ,0.00033569 ,0.00164795 ,0.00341797 , 0.00457764 ,0.00402832 ,0.00286865 ,0.00073242 ,0.00045776 , 0.00088501 ,0.00225830 ,0.00341797 ,0.00411987 ,0.00357056 , 0.00207520 ,0.00094604 ,0.00051880 ,0.00091553 ,0.00158691 , 0.00286865 ,0.00366211 ,0.00323486 ,0.00271606 ,0.00238037 , 0.00183105 ,0.00122070 ,0.00134277 ,0.00149536 ,0.00161743 , 0.00115967 ,0.00085449 ,0.00085449 ,0.00082397 ,0.00076294 , 0.00137329 ,0.00262451 ,0.00384521 ,0.00466919 ,0.00436401 , 0.00381470 ,0.00280762 ,0.00161743 ,0.00134277 ,0.00109863 , 0.00137329 ,0.00177002 ,0.00219727 ,0.00180054 ,0.00100708 , 0.00051880 ,0.00057983 ,0.00155640 ,0.00161743 ,0.00192261 , 0.00204468 ,0.00250244 ,0.00207520 ,0.00170898 ,0.00219727 , 0.00262451 ,0.00265503 ,0.00222778 ,0.00106812 ,0.00054932 , -0.00009155 ,-0.00112915 ,-0.00158691 ,-0.00106812 ,0.00036621 , 0.00210571 ,0.00244141 ,0.00274658 ,0.00207520 ,0.00189209 , 0.00155640 ,0.00195312 ,0.00244141 ,0.00357056 ,0.00439453 , 0.00302124 ,0.00048828 ,-0.00177002 ,-0.00296021 ,-0.00289917 , -0.00231934 ,0.00024414 ,0.00250244 ,0.00491333 ,0.00521851 , 0.00378418 ,0.00112915 ,-0.00030518 ,-0.00177002 ,-0.00198364 , -0.00039673 ,0.00164795 ,0.00354004 ,0.00323486 ,0.00183105 , 0.00009155 ,-0.00024414 ,0.00024414 ,0.00119019 ,0.00210571 , 0.00231934 ,0.00256348 ,0.00192261 ,0.00131226 ,0.00082397 , 0.00158691 ,0.00253296 ,0.00302124 ,0.00195312 ,0.00094604 , -0.00039673 ,-0.00177002 ,-0.00314331 ,-0.00305176 ,-0.00149536 , 0.00112915 ,0.00326538 ,0.00463867 ,0.00497437 ,0.00454712 , 0.00253296 ,0.00082397 ,-0.00048828 ,-0.00057983 ,0.00042725 , 0.00112915 ,0.00122070 ,0.00045776 ,-0.00030518 ,-0.00100708 , -0.00106812 ,-0.00076294 ,0.00045776 ,0.00119019 ,0.00192261 , 0.00140381 ,0.00091553 ,0.00027466 ,0.00109863 ,0.00216675 , 0.00289917 ,0.00277710 ,0.00241089 ,0.00146484 ,0.00076294 , -0.00039673 ,-0.00112915 ,-0.00097656 ,-0.00030518 ,0.00128174 , 0.00247192 ,0.00256348 ,0.00225830 ,0.00167847 ,0.00076294 , 0.00115967 ,0.00149536 ,0.00244141 ,0.00198364 ,0.00088501 , 0.00015259 ,-0.00067139 ,-0.00039673 ,0.00036621 ,0.00131226 , 0.00207520 ,0.00305176 ,0.00329590 ,0.00213623 ,0.00027466 , -0.00057983 ,-0.00076294 ,0.00009155 ,0.00045776 ,0.00103760 , 0.00198364 ,0.00234985 ,0.00164795 ,0.00122070 ,0.00061035 , -0.00039673 ,-0.00070190 ,0.00000000 ,0.00128174 ,0.00314331 , 0.00347900 ,0.00259399 ,0.00128174 ,0.00088501 ,0.00070190 , 0.00094604 ,0.00143433 ,0.00170898 ,0.00225830 ,0.00149536 , 0.00033569 ,-0.00109863 ,-0.00112915 ,-0.00036621 ,0.00094604 , 0.00134277 ,0.00213623 ,0.00262451 ,0.00228882 ,0.00146484 , 0.00051880 ,0.00012207 ,-0.00027466 ,0.00042725 ,0.00119019 , 0.00222778 ,0.00292969 ,0.00299072 ,0.00198364 ,0.00033569 , 0.00033569 ,0.00000000 ,0.00131226 ,0.00152588 ,0.00177002 , 0.00170898 ,0.00143433 ,0.00140381 ,0.00134277 ,0.00170898 , 0.00213623 ,0.00155640 ,0.00064087 ,-0.00006104 ,-0.00057983 , -0.00061035 ,-0.00030518 ,0.00064087 ,0.00189209 ,0.00277710 , 0.00332642 ,0.00317383 ,0.00259399 ,0.00170898 ,0.00070190 , -0.00039673 ,-0.00115967 ,-0.00091553 ,-0.00003052 ,0.00103760 , 0.00204468 ,0.00186157 ,0.00186157 ,0.00131226 ,0.00085449 , 0.00119019 ,0.00238037 ,0.00283813 ,0.00292969 ,0.00164795 , 0.00125122 ,0.00085449 ,0.00082397 ,0.00164795 ,0.00173950 , 0.00195312 ,0.00131226 ,0.00079346 ,0.00018311 ,-0.00006104 , -0.00048828 ,-0.00027466 ,0.00012207 ,0.00073242 ,0.00207520 , 0.00296021 ,0.00347900 ,0.00274658 ,0.00128174 ,0.00018311 , 0.00021362 ,0.00039673 ,0.00097656 ,0.00186157 ,0.00274658 , 0.00323486 ,0.00262451 ,0.00256348 ,0.00189209 ,0.00164795 , 0.00109863 ,0.00097656 ,0.00018311 ,-0.00045776 ,-0.00021362 , 0.00006104 ,0.00073242 ,0.00131226 ,0.00219727 ,0.00274658 , 0.00247192 ,0.00122070 ,0.00009155 ,-0.00085449 ,-0.00036621 , 0.00073242 ,0.00192261 ,0.00289917 ,0.00347900 ,0.00369263 , 0.00341797 ,0.00222778 ,0.00125122 ,0.00042725 ,0.00027466 , 0.00009155 ,0.00073242 ,0.00170898 ,0.00323486 ,0.00296021 , 0.00219727 ,0.00137329 ,0.00137329 ,0.00091553 ,0.00009155 , -0.00021362 ,0.00030518 ,0.00137329 ,0.00222778 ,0.00280762 , 0.00338745 ,0.00369263 ,0.00384521 ,0.00277710 ,0.00241089 , 0.00189209 ,0.00183105 ,0.00106812 ,0.00079346 ,0.00076294 , 0.00180054 ,0.00268555 ,0.00296021 ,0.00204468 ,0.00134277 , 0.00082397 ,0.00079346 ,0.00085449 ,0.00109863 ,0.00128174 , 0.00149536 ,0.00131226 ,0.00128174 ,0.00152588 ,0.00250244 , 0.00375366 ,0.00384521 ,0.00262451 ,0.00195312 ,0.00125122 , 0.00057983 ,-0.00006104 ,0.00051880 ,0.00143433 ,0.00302124 , 0.00445557 ,0.00473022 ,0.00415039 ,0.00335693 ,0.00131226 , 0.00006104 ,-0.00119019 ,-0.00076294 ,0.00012207 ,0.00149536 , 0.00155640 ,0.00207520 ,0.00219727 ,0.00265503 ,0.00262451 , 0.00274658 ,0.00314331 ,0.00308228 ,0.00317383 ,0.00231934 , 0.00201416 ,0.00204468 ,0.00247192 ,0.00296021 ,0.00350952 , 0.00366211 ,0.00292969 ,0.00228882 ,0.00100708 ,0.00009155 , -0.00100708 ,-0.00088501 ,0.00051880 ,0.00201416 ,0.00375366 , 0.00473022 ,0.00454712 ,0.00396729 ,0.00219727 ,0.00097656 , 0.00021362 ,-0.00024414 ,-0.00057983 ,0.00061035 ,0.00167847 , 0.00271606 ,0.00305176 ,0.00320435 ,0.00299072 ,0.00335693 , 0.00247192 ,0.00152588 ,0.00112915 ,0.00103760 ,0.00131226 , 0.00112915 ,0.00109863 ,0.00155640 ,0.00216675 ,0.00192261 , 0.00115967 ,0.00039673 ,-0.00085449 ,-0.00149536 ,-0.00173950 , -0.00143433 ,0.00021362 ,0.00256348 ,0.00366211 ,0.00418091 , 0.00317383 ,0.00177002 ,0.00021362 ,-0.00070190 ,-0.00143433 , -0.00070190 ,0.00067139 ,0.00158691 ,0.00177002 ,0.00100708 , 0.00039673 ,-0.00061035 ,-0.00109863 ,-0.00112915 ,-0.00021362 , 0.00094604 ,0.00134277 ,0.00070190 ,-0.00036621 ,-0.00076294 , -0.00027466 ,-0.00012207 ,0.00021362 ,0.00076294 ,0.00149536 , 0.00161743 ,0.00115967 ,-0.00039673 ,-0.00155640 ,-0.00189209 , -0.00128174 ,0.00000000 ,0.00158691 ,0.00305176 ,0.00289917 , 0.00146484 ,-0.00106812 ,-0.00222778 ,-0.00238037 ,-0.00198364 , -0.00076294 ,-0.00051880 ,-0.00009155 ,-0.00036621 ,-0.00097656 , -0.00164795 ,-0.00134277 ,-0.00061035 ,0.00045776 ,0.00128174 , 0.00189209 ,0.00234985 ,0.00216675 ,0.00158691 ,0.00012207 , -0.00115967 ,-0.00128174 ,-0.00149536 ,-0.00088501 ,-0.00119019 , -0.00152588 ,-0.00225830 ,-0.00323486 ,-0.00314331 ,-0.00228882 , -0.00097656 ,0.00027466 ,0.00070190 ,0.00021362 ,-0.00039673 , -0.00064087 ,-0.00064087 ,-0.00091553 ,-0.00073242 ,-0.00057983 , -0.00057983 ,-0.00097656 ,-0.00152588 ,-0.00183105 ,-0.00137329 , -0.00076294 ,0.00027466 ,0.00131226 ,0.00158691 ,0.00137329 , -0.00009155 ,-0.00149536 ,-0.00256348 ,-0.00228882 ,-0.00183105 , -0.00161743 ,-0.00115967 ,-0.00067139 ,-0.00149536 ,-0.00241089 , -0.00363159 ,-0.00369263 ,-0.00271606 ,-0.00167847 ,-0.00064087 , -0.00018311 ,0.00021362 ,0.00027466 ,-0.00033569 ,-0.00088501 , -0.00106812 ,-0.00131226 ,-0.00128174 ,-0.00158691 ,-0.00247192 , -0.00259399 ,-0.00256348 ,-0.00222778 ,-0.00222778 ,-0.00195312 , -0.00128174 ,-0.00076294 ,-0.00076294 ,-0.00146484 ,-0.00216675 , -0.00238037 ,-0.00253296 ,-0.00289917 ,-0.00259399 ,-0.00201416 , -0.00183105 ,-0.00268555 ,-0.00427246 ,-0.00476074 ,-0.00473022 , -0.00329590 ,-0.00299072 ,-0.00189209 ,-0.00119019 ,0.00006104 , 0.00024414 ,-0.00030518 ,-0.00131226 ,-0.00143433 ,-0.00143433 , -0.00216675 ,-0.00271606 ,-0.00329590 ,-0.00369263 ,-0.00421143 , -0.00479126 ,-0.00405884 ,-0.00314331 ,-0.00198364 ,-0.00164795 , -0.00122070 ,-0.00112915 ,-0.00167847 ,-0.00173950 ,-0.00216675 , -0.00143433 ,-0.00076294 ,-0.00021362 ,-0.00106812 ,-0.00198364 , -0.00302124 ,-0.00317383 ,-0.00357056 ,-0.00292969 ,-0.00177002 , -0.00054932 ,-0.00030518 ,-0.00057983 ,-0.00109863 ,-0.00128174 , -0.00146484 ,-0.00198364 ,-0.00164795 ,-0.00158691 ,-0.00137329 , -0.00149536 ,-0.00238037 ,-0.00265503 ,-0.00268555 ,-0.00268555 , -0.00286865 ,-0.00198364 ,-0.00106812 ,0.00024414 ,0.00073242 , 0.00018311 ,-0.00057983 ,-0.00103760 ,-0.00094604 ,-0.00042725 , -0.00006104 ,-0.00027466 ,-0.00082397 ,-0.00234985 ,-0.00396729 , -0.00469971 ,-0.00427246 ,-0.00253296 ,-0.00119019 ,-0.00021362 , 0.00027466 ,0.00070190 ,0.00064087 ,-0.00012207 ,-0.00091553 , -0.00115967 ,-0.00109863 ,-0.00088501 ,-0.00070190 ,-0.00057983 , -0.00082397 ,-0.00183105 ,-0.00311279 ,-0.00451660 ,-0.00430298 , -0.00360107 ,-0.00183105 ,-0.00039673 ,0.00106812 ,0.00146484 , 0.00088501 ,-0.00051880 ,-0.00128174 ,-0.00177002 ,-0.00186157 , -0.00180054 ,-0.00228882 ,-0.00244141 ,-0.00283813 ,-0.00308228 , -0.00238037 ,-0.00161743 ,-0.00131226 ,-0.00100708 ,-0.00140381 , -0.00109863 ,-0.00112915 ,-0.00122070 ,-0.00097656 ,-0.00091553 , -0.00051880 ,-0.00045776 ,-0.00003052 ,-0.00070190 ,-0.00186157 , -0.00308228 ,-0.00381470 ,-0.00402832 ,-0.00326538 ,-0.00241089 , -0.00039673 ,0.00054932 ,0.00064087 ,-0.00076294 ,-0.00170898 , -0.00280762 ,-0.00332642 ,-0.00289917 ,-0.00140381 ,0.00033569 , 0.00048828 ,-0.00012207 ,-0.00149536 ,-0.00244141 ,-0.00305176 , -0.00247192 ,-0.00137329 ,-0.00033569 ,0.00054932 ,0.00088501 , 0.00030518 ,-0.00079346 ,-0.00262451 ,-0.00323486 ,-0.00341797 , -0.00234985 ,-0.00134277 ,-0.00012207 ,0.00003052 ,-0.00097656 , -0.00268555 ,-0.00405884 ,-0.00445557 ,-0.00329590 ,-0.00131226 , 0.00021362 ,0.00125122 ,0.00100708 ,-0.00003052 ,-0.00164795 , -0.00292969 ,-0.00292969 ,-0.00228882 ,-0.00088501 ,0.00015259 , 0.00039673 ,-0.00042725 ,-0.00054932 ,-0.00219727 ,-0.00296021 , -0.00338745 ,-0.00286865 ,-0.00177002 ,-0.00064087 ,0.00009155 , -0.00039673 ,-0.00082397 ,-0.00198364 ,-0.00280762 ,-0.00283813 , -0.00207520 ,-0.00109863 ,-0.00039673 ,-0.00024414 ,-0.00073242 , -0.00112915 ,-0.00137329 ,-0.00140381 ,-0.00097656 ,-0.00030518 , -0.00033569 ,-0.00042725 ,-0.00122070 ,-0.00195312 ,-0.00198364 , -0.00186157 ,-0.00140381 ,-0.00061035 ,-0.00021362 ,0.00003052 , 0.00000000 ,-0.00067139 ,-0.00137329 ,-0.00213623 ,-0.00173950 , -0.00106812 ,-0.00051880 ,0.00033569 ,0.00042725 ,-0.00018311 , -0.00140381 ,-0.00161743 ,-0.00115967 ,-0.00088501 ,-0.00015259 , -0.00048828 ,-0.00027466 ,-0.00076294 ,-0.00094604 ,-0.00097656 , -0.00039673 ,0.00000000 ,0.00045776 ,0.00082397 ,0.00027466 , -0.00033569 ,-0.00112915 ,-0.00146484 ,-0.00167847 ,-0.00140381 , -0.00027466 ,0.00112915 ,0.00183105 ,0.00161743 ,0.00091553 , 0.00003052 ,-0.00061035 ,-0.00140381 ,-0.00115967 ,-0.00015259 , 0.00085449 ,0.00106812 ,0.00106812 ,0.00064087 ,0.00073242 , -0.00009155 ,-0.00082397 ,-0.00134277 ,-0.00073242 ,0.00021362 , 0.00122070 ,0.00170898 ,0.00231934 ,0.00289917 ,0.00216675 , 0.00161743 ,0.00042725 ,0.00009155 ,0.00024414 ,0.00024414 , 0.00042725 ,0.00064087 ,0.00143433 ,0.00155640 ,0.00216675 , 0.00183105 ,0.00170898 ,0.00210571 ,0.00210571 ,0.00186157 , 0.00186157 ,0.00213623 ,0.00207520 ,0.00186157 ,0.00164795 , 0.00180054 ,0.00225830 ,0.00228882 ,0.00173950 ,0.00122070 , 0.00057983 ,0.00070190 ,0.00061035 ,0.00149536 ,0.00225830 , 0.00350952 ,0.00393677 ,0.00402832 ,0.00320435 ,0.00271606 , 0.00213623 ,0.00204468 ,0.00183105 ,0.00201416 ,0.00238037 , 0.00213623 ,0.00207520 ,0.00238037 ,0.00268555 ,0.00305176 , 0.00299072 ,0.00289917 ,0.00286865 ,0.00289917 ,0.00323486 , 0.00241089 ,0.00234985 ,0.00277710 ,0.00253296 ,0.00311279 , 0.00283813 ,0.00173950 ,0.00140381 ,0.00082397 ,0.00036621 , 0.00061035 ,0.00109863 ,0.00289917 ,0.00427246 ,0.00506592 , 0.00476074 ,0.00363159 ,0.00213623 ,0.00137329 ,0.00137329 , 0.00192261 ,0.00253296 ,0.00280762 ,0.00256348 ,0.00186157 , 0.00173950 ,0.00149536 ,0.00173950 ,0.00137329 ,0.00158691 , 0.00186157 ,0.00241089 ,0.00271606 ,0.00283813 ,0.00296021 , 0.00311279 ,0.00247192 ,0.00213623 ,0.00219727 ,0.00274658 , 0.00259399 ,0.00311279 ,0.00173950 ,0.00149536 ,0.00070190 , 0.00128174 ,0.00167847 ,0.00238037 ,0.00311279 ,0.00329590 , 0.00247192 ,0.00195312 ,0.00164795 ,0.00128174 ,0.00149536 , 0.00192261 ,0.00170898 ,0.00216675 ,0.00198364 ,0.00207520 , 0.00167847 ,0.00158691 ,0.00128174 ,0.00131226 ,0.00195312 , 0.00277710 ,0.00326538 ,0.00344849 ,0.00341797 ,0.00296021 , 0.00213623 ,0.00167847 ,0.00167847 ,0.00131226 ,0.00122070 , 0.00079346 ,0.00064087 ,0.00076294 ,0.00054932 ,0.00103760 , 0.00122070 ,0.00198364 ,0.00234985 ,0.00262451 ,0.00241089 , 0.00210571 ,0.00234985 ,0.00234985 ,0.00259399 ,0.00268555 , 0.00231934 ,0.00189209 ,0.00192261 ,0.00216675 ,0.00228882 , 0.00238037 ,0.00213623 ,0.00164795 ,0.00125122 ,0.00082397 , 0.00140381 ,0.00210571 ,0.00296021 ,0.00393677 ,0.00393677 , 0.00369263 ,0.00271606 ,0.00204468 ,0.00103760 ,0.00109863 , 0.00112915 ,0.00109863 ,0.00158691 ,0.00195312 ,0.00268555 , 0.00323486 ,0.00305176 ,0.00320435 ,0.00268555 ,0.00225830 , 0.00152588 ,0.00112915 ,0.00128174 ,0.00210571 ,0.00256348 , 0.00320435 ,0.00323486 ,0.00299072 ,0.00265503 ,0.00177002 , 0.00073242 ,0.00030518 ,0.00122070 ,0.00256348 ,0.00369263 , 0.00381470 ,0.00378418 ,0.00317383 ,0.00308228 ,0.00173950 , 0.00177002 ,0.00161743 ,0.00158691 ,0.00152588 ,0.00115967 , 0.00106812 ,0.00158691 ,0.00161743 ,0.00180054 ,0.00149536 , 0.00149536 ,0.00137329 ,0.00170898 ,0.00192261 ,0.00228882 , 0.00296021 ,0.00250244 ,0.00177002 ,0.00088501 ,0.00106812 , 0.00109863 ,0.00112915 ,0.00061035 ,0.00051880 ,0.00027466 , 0.00036621 ,0.00061035 ,0.00094604 ,0.00137329 ,0.00170898 , 0.00155640 ,0.00134277 ,0.00128174 ,0.00149536 ,0.00125122 , 0.00094604 ,0.00030518 ,-0.00009155 ,-0.00018311 ,-0.00045776 , -0.00048828 ,-0.00009155 ,0.00006104 ,-0.00009155 ,-0.00027466 , -0.00015259 ,0.00048828 ,0.00106812 ,0.00149536 ,0.00149536 , 0.00109863 ,0.00122070 ,0.00109863 ,0.00027466 ,-0.00006104 , -0.00030518 ,-0.00073242 ,-0.00085449 ,-0.00103760 ,-0.00036621 , -0.00009155 ,-0.00009155 ,-0.00006104 ,0.00012207 ,0.00015259 , 0.00021362 ,-0.00012207 ,-0.00030518 ,-0.00061035 ,-0.00070190 , -0.00057983 ,-0.00070190 ,-0.00085449 ,-0.00082397 ,-0.00054932 , -0.00033569 ,-0.00082397 ,-0.00070190 ,-0.00054932 ,-0.00048828 , -0.00018311 ,-0.00061035 ,-0.00003052 ,0.00006104 ,0.00000000 , -0.00051880 ,-0.00070190 ,-0.00112915 ,-0.00100708 ,-0.00158691 , -0.00177002 ,-0.00143433 ,-0.00134277 ,-0.00097656 ,-0.00094604 , -0.00048828 ,-0.00009155 ,0.00027466 ,0.00009155 ,-0.00006104 , 0.00015259 ,-0.00027466 ,-0.00042725 ,-0.00030518 ,-0.00064087 , -0.00094604 ,-0.00158691 ,-0.00219727 ,-0.00259399 ,-0.00198364 , -0.00180054 ,-0.00091553 ,-0.00064087 ,0.00003052 ,0.00033569 , 0.00094604 ,-0.00009155 ,-0.00003052 ,-0.00073242 ,-0.00079346 , -0.00045776 ,-0.00091553 ,-0.00115967 ,-0.00079346 ,-0.00085449 , -0.00115967 ,-0.00076294 ,-0.00106812 ,-0.00064087 ,-0.00073242 , -0.00079346 ,-0.00091553 ,-0.00045776 ,-0.00076294 ,-0.00061035 , -0.00097656 ,-0.00091553 ,-0.00082397 ,-0.00079346 ,-0.00131226 , -0.00112915 ,-0.00152588 ,-0.00161743 ,-0.00094604 ,-0.00051880 , 0.00003052 ,0.00048828 ,0.00027466 ,0.00036621 ,0.00003052 , 0.00006104 ,0.00003052 ,0.00006104 ,-0.00073242 ,-0.00128174 , -0.00170898 ,-0.00198364 ,-0.00219727 ,-0.00244141 ,-0.00241089 , -0.00195312 ,-0.00094604 ,-0.00027466 ,0.00033569 ,0.00012207 , 0.00027466 ,0.00036621 ,-0.00048828 ,-0.00051880 ,-0.00045776 , 0.00000000 ,0.00018311 ,0.00015259 ,-0.00039673 ,-0.00122070 , -0.00189209 ,-0.00256348 ,-0.00259399 ,-0.00189209 ,-0.00079346 , -0.00024414 ,0.00027466 ,0.00009155 ,0.00015259 ,0.00000000 , -0.00073242 ,-0.00073242 ,-0.00091553 ,-0.00061035 ,-0.00027466 , -0.00079346 ,-0.00085449 ,-0.00134277 ,-0.00158691 ,-0.00152588 , -0.00131226 ,-0.00082397 ,0.00000000 ,0.00054932 ,0.00128174 , 0.00103760 ,0.00100708 ,0.00003052 ,-0.00051880 ,-0.00128174 , -0.00109863 ,-0.00097656 ,-0.00033569 ,-0.00079346 ,-0.00119019 , -0.00213623 ,-0.00195312 ,-0.00213623 ,-0.00183105 ,-0.00085449 , 0.00073242 ,0.00192261 ,0.00244141 ,0.00228882 ,0.00097656 , 0.00021362 ,-0.00067139 ,-0.00140381 ,-0.00103760 ,-0.00036621 , 0.00054932 ,0.00061035 ,0.00027466 ,-0.00015259 ,-0.00054932 , -0.00079346 ,-0.00079346 ,-0.00036621 ,0.00051880 ,0.00128174 , 0.00119019 ,0.00094604 ,0.00064087 ,-0.00033569 ,-0.00103760 , -0.00134277 ,-0.00164795 ,-0.00137329 ,-0.00076294 ,-0.00057983 , -0.00061035 ,-0.00048828 ,0.00018311 ,0.00042725 ,0.00030518 , 0.00067139 ,0.00064087 ,0.00064087 ,0.00021362 ,0.00006104 , -0.00033569 ,-0.00048828 ,0.00015259 ,-0.00006104 ,-0.00057983 , -0.00094604 ,-0.00137329 ,-0.00161743 ,-0.00146484 ,-0.00067139 , 0.00051880 ,0.00170898 ,0.00216675 ,0.00158691 ,0.00030518 , -0.00033569 ,-0.00070190 ,-0.00085449 ,-0.00024414 ,0.00045776 , 0.00070190 ,0.00061035 ,-0.00036621 ,-0.00109863 ,-0.00146484 , -0.00115967 ,-0.00076294 ,-0.00021362 ,0.00045776 ,0.00085449 , 0.00149536 ,0.00146484 ,0.00115967 ,0.00094604 ,0.00021362 , -0.00003052 ,-0.00057983 ,-0.00024414 ,0.00018311 ,0.00064087 , 0.00057983 ,0.00064087 ,0.00088501 ,0.00115967 ,0.00146484 , 0.00112915 ,0.00134277 ,0.00137329 ,0.00195312 ,0.00134277 , 0.00100708 ,0.00103760 ,0.00115967 ,0.00122070 ,0.00106812 , 0.00088501 ,0.00039673 ,0.00070190 ,0.00073242 ,0.00137329 , 0.00201416 ,0.00262451 ,0.00241089 ,0.00213623 ,0.00173950 , 0.00134277 ,0.00125122 ,0.00094604 ,0.00097656 ,0.00161743 , 0.00158691 ,0.00161743 ,0.00103760 ,0.00042725 ,0.00033569 , 0.00009155 ,0.00085449 ,0.00170898 ,0.00256348 ,0.00329590 , 0.00296021 ,0.00231934 ,0.00122070 ,0.00067139 ,0.00027466 , 0.00088501 ,0.00192261 ,0.00305176 ,0.00308228 ,0.00253296 , 0.00192261 ,0.00076294 ,0.00064087 ,0.00112915 ,0.00149536 , 0.00238037 ,0.00256348 ,0.00265503 ,0.00231934 ,0.00161743 , 0.00091553 ,0.00076294 ,0.00021362 ,0.00076294 ,0.00131226 , 0.00216675 ,0.00216675 ,0.00201416 ,0.00170898 ,0.00122070 , 0.00103760 ,0.00115967 ,0.00158691 ,0.00231934 ,0.00308228 , 0.00228882 ,0.00149536 ,0.00033569 ,0.00000000 ,0.00033569 , 0.00177002 ,0.00247192 ,0.00335693 ,0.00277710 ,0.00189209 , 0.00094604 ,0.00006104 ,0.00051880 ,0.00152588 ,0.00198364 , 0.00225830 ,0.00195312 ,0.00155640 ,0.00085449 ,0.00073242 , 0.00073242 ,0.00137329 ,0.00155640 ,0.00173950 ,0.00128174 , 0.00073242 ,0.00027466 ,0.00000000 ,0.00009155 ,0.00061035 , 0.00143433 ,0.00231934 ,0.00335693 ,0.00292969 ,0.00250244 , 0.00164795 ,0.00097656 ,0.00051880 ,0.00039673 ,0.00103760 , 0.00173950 ,0.00210571 ,0.00173950 ,0.00161743 ,0.00122070 , 0.00100708 ,0.00070190 ,0.00070190 ,0.00070190 ,0.00091553 , 0.00131226 ,0.00152588 ,0.00198364 ,0.00228882 ,0.00204468 , 0.00186157 ,0.00167847 ,0.00143433 ,0.00115967 ,0.00122070 , 0.00097656 ,0.00112915 ,0.00088501 ,0.00146484 ,0.00167847 , 0.00256348 ,0.00317383 ,0.00283813 ,0.00234985 ,0.00149536 , 0.00091553 ,0.00076294 ,0.00134277 ,0.00173950 ,0.00213623 , 0.00207520 ,0.00170898 ,0.00161743 ,0.00115967 ,0.00161743 , 0.00173950 ,0.00183105 ,0.00198364 ,0.00189209 ,0.00189209 , 0.00161743 ,0.00140381 ,0.00146484 ,0.00137329 ,0.00170898 , 0.00183105 ,0.00189209 ,0.00204468 ,0.00164795 ,0.00155640 , 0.00082397 ,0.00100708 ,0.00106812 ,0.00155640 ,0.00244141 , 0.00265503 ,0.00234985 ,0.00183105 ,0.00134277 ,0.00119019 , 0.00119019 ,0.00161743 ,0.00164795 ,0.00201416 ,0.00204468 , 0.00158691 ,0.00122070 ,0.00091553 ,0.00094604 ,0.00070190 , 0.00091553 ,0.00112915 ,0.00125122 ,0.00146484 ,0.00164795 , 0.00143433 ,0.00149536 ,0.00146484 ,0.00125122 ,0.00088501 , 0.00051880 ,0.00024414 ,0.00030518 ,0.00030518 ,0.00033569 , 0.00045776 ,0.00128174 ,0.00180054 ,0.00186157 ,0.00112915 , 0.00061035 ,0.00018311 ,0.00003052 ,-0.00012207 ,0.00009155 , 0.00115967 ,0.00177002 ,0.00158691 ,0.00100708 ,0.00021362 , 0.00009155 ,-0.00018311 ,-0.00024414 ,0.00000000 ,0.00015259 , 0.00061035 ,0.00119019 ,0.00091553 ,0.00054932 ,0.00057983 , 0.00021362 ,0.00006104 ,-0.00006104 ,0.00030518 ,0.00021362 , -0.00006104 ,-0.00021362 ,-0.00048828 ,-0.00045776 ,-0.00012207 , 0.00009155 ,0.00018311 ,0.00039673 ,0.00070190 ,0.00039673 , -0.00018311 ,-0.00021362 ,-0.00033569 ,-0.00051880 ,-0.00103760 , -0.00094604 ,-0.00073242 ,-0.00009155 ,0.00018311 ,-0.00033569 , -0.00033569 ,-0.00088501 ,-0.00097656 ,-0.00128174 ,-0.00115967 , -0.00030518 ,0.00012207 ,0.00054932 ,0.00021362 ,-0.00030518 , -0.00073242 ,-0.00134277 ,-0.00149536 ,-0.00131226 ,-0.00097656 , -0.00064087 ,-0.00064087 ,-0.00128174 ,-0.00170898 ,-0.00167847 , -0.00158691 ,-0.00112915 ,-0.00045776 ,-0.00015259 ,0.00015259 , -0.00012207 ,-0.00091553 ,-0.00207520 ,-0.00192261 ,-0.00152588 , -0.00122070 ,-0.00027466 ,-0.00042725 ,-0.00048828 ,-0.00097656 , -0.00140381 ,-0.00152588 ,-0.00152588 ,-0.00137329 ,-0.00112915 , -0.00088501 ,-0.00100708 ,-0.00051880 ,-0.00048828 ,-0.00103760 , -0.00149536 ,-0.00152588 ,-0.00155640 ,-0.00082397 ,-0.00064087 , -0.00057983 ,-0.00064087 ,-0.00051880 ,-0.00085449 ,-0.00128174 , -0.00164795 ,-0.00137329 ,-0.00122070 ,-0.00115967 ,-0.00076294 , -0.00030518 ,-0.00030518 ,-0.00045776 ,-0.00076294 ,-0.00164795 , -0.00195312 ,-0.00247192 ,-0.00164795 ,-0.00158691 ,-0.00122070 , -0.00103760 ,-0.00064087 ,-0.00070190 ,-0.00115967 ,-0.00125122 , -0.00082397 ,-0.00134277 ,-0.00100708 ,-0.00131226 ,-0.00103760 , -0.00119019 ,-0.00106812 ,-0.00131226 ,-0.00112915 ,-0.00140381 , -0.00167847 ,-0.00189209 ,-0.00195312 ,-0.00158691 ,-0.00143433 , -0.00155640 ,-0.00173950 ,-0.00164795 ,-0.00152588 ,-0.00088501 , -0.00027466 ,-0.00021362 ,-0.00073242 ,-0.00161743 ,-0.00204468 , -0.00241089 ,-0.00155640 ,-0.00109863 ,0.00000000 ,0.00045776 , 0.00082397 ,-0.00021362 ,-0.00076294 ,-0.00177002 ,-0.00231934 , -0.00253296 ,-0.00204468 ,-0.00170898 ,-0.00100708 ,-0.00088501 , -0.00125122 ,-0.00131226 ,-0.00210571 ,-0.00241089 ,-0.00189209 , -0.00149536 ,-0.00103760 ,-0.00045776 ,-0.00021362 ,-0.00054932 , -0.00097656 ,-0.00064087 ,-0.00003052 ,0.00036621 ,0.00015259 , -0.00042725 ,-0.00161743 ,-0.00225830 ,-0.00277710 ,-0.00231934 , -0.00173950 ,-0.00042725 ,-0.00018311 ,-0.00018311 ,-0.00030518 , -0.00100708 ,-0.00177002 ,-0.00256348 ,-0.00161743 ,-0.00161743 , -0.00082397 ,-0.00076294 ,-0.00064087 ,-0.00061035 ,-0.00097656 , -0.00143433 ,-0.00149536 ,-0.00119019 ,-0.00091553 ,-0.00021362 , -0.00033569 ,-0.00015259 ,0.00003052 ,-0.00067139 ,-0.00085449 , -0.00070190 ,-0.00119019 ,-0.00085449 ,-0.00064087 ,-0.00122070 , -0.00173950 ,-0.00164795 ,-0.00183105 ,-0.00103760 ,-0.00048828 , 0.00015259 ,0.00061035 ,0.00048828 ,0.00042725 ,0.00000000 , -0.00021362 ,-0.00003052 ,0.00000000 ,-0.00036621 ,-0.00076294 , -0.00088501 ,-0.00097656 ,-0.00112915 ,-0.00103760 ,-0.00079346 , -0.00061035 ,-0.00003052 ,0.00039673 ,0.00024414 ,0.00030518 , 0.00027466 ,0.00051880 ,0.00000000 ,0.00036621 ,0.00021362 , 0.00024414 ,-0.00030518 ,-0.00103760 ,-0.00088501 ,-0.00088501 , -0.00024414 ,-0.00045776 ,0.00033569 ,0.00070190 ,0.00091553 , 0.00100708 ,0.00097656 ,0.00070190 ,0.00054932 ,0.00006104 , 0.00021362 ,-0.00006104 ,-0.00042725 ,-0.00091553 ,-0.00088501 , -0.00122070 ,-0.00073242 ,-0.00064087 ,0.00003052 ,0.00064087 , 0.00079346 ,0.00042725 ,0.00033569 ,0.00021362 ,0.00070190 , 0.00054932 ,0.00122070 ,0.00119019 ,0.00119019 ,0.00036621 , -0.00054932 ,-0.00088501 ,-0.00088501 ,-0.00088501 ,-0.00039673 , 0.00000000 ,0.00067139 ,0.00103760 ,0.00119019 ,0.00137329 , 0.00106812 ,0.00100708 ,0.00070190 ,0.00051880 ,0.00003052 , 0.00033569 ,0.00042725 ,-0.00003052 ,0.00012207 ,0.00021362 , 0.00021362 ,0.00006104 ,0.00015259 ,0.00079346 ,0.00128174 , 0.00173950 ,0.00155640 ,0.00140381 ,0.00109863 ,0.00094604 , 0.00088501 ,0.00079346 ,0.00085449 ,0.00036621 ,0.00021362 , 0.00000000 ,0.00006104 ,0.00036621 ,0.00070190 ,0.00122070 , 0.00164795 ,0.00155640 ,0.00186157 ,0.00180054 ,0.00213623 , 0.00161743 ,0.00167847 ,0.00119019 ,0.00143433 ,0.00100708 , 0.00042725 ,0.00018311 ,0.00015259 ,0.00045776 ,0.00073242 , 0.00134277 ,0.00192261 ,0.00241089 ,0.00253296 ,0.00177002 , 0.00100708 ,0.00054932 ,0.00119019 ,0.00164795 ,0.00213623 , 0.00253296 ,0.00228882 ,0.00164795 ,0.00021362 ,0.00030518 , 0.00003052 ,0.00073242 ,0.00140381 ,0.00210571 ,0.00222778 , 0.00256348 ,0.00244141 ,0.00186157 ,0.00134277 ,0.00115967 , 0.00125122 ,0.00155640 ,0.00152588 ,0.00158691 ,0.00106812 , 0.00103760 ,0.00073242 ,0.00091553 ,0.00128174 ,0.00183105 , 0.00225830 ,0.00219727 ,0.00244141 ,0.00225830 ,0.00222778 , 0.00155640 ,0.00155640 ,0.00128174 ,0.00128174 ,0.00177002 , 0.00140381 ,0.00125122 ,0.00094604 ,0.00088501 ,0.00064087 , 0.00064087 ,0.00137329 ,0.00210571 ,0.00283813 ,0.00262451 , 0.00259399 ,0.00225830 ,0.00204468 ,0.00177002 ,0.00155640 , 0.00134277 ,0.00170898 ,0.00183105 ,0.00161743 ,0.00122070 , 0.00094604 ,0.00131226 ,0.00183105 ,0.00216675 ,0.00317383 , 0.00311279 ,0.00314331 ,0.00280762 ,0.00225830 ,0.00222778 , 0.00234985 ,0.00231934 ,0.00216675 ,0.00189209 ,0.00170898 , 0.00177002 ,0.00128174 ,0.00152588 ,0.00164795 ,0.00213623 , 0.00274658 ,0.00274658 ,0.00277710 ,0.00311279 ,0.00253296 , 0.00271606 ,0.00250244 ,0.00238037 ,0.00274658 ,0.00265503 , 0.00256348 ,0.00189209 ,0.00149536 ,0.00140381 ,0.00167847 , 0.00256348 ,0.00305176 ,0.00314331 ,0.00317383 ,0.00283813 , 0.00265503 ,0.00225830 ,0.00253296 ,0.00238037 ,0.00283813 , 0.00231934 ,0.00219727 ,0.00201416 ,0.00177002 ,0.00161743 , 0.00158691 ,0.00173950 ,0.00183105 ,0.00222778 ,0.00277710 , 0.00326538 ,0.00363159 ,0.00317383 ,0.00283813 ,0.00213623 , 0.00201416 ,0.00158691 ,0.00167847 ,0.00164795 ,0.00210571 , 0.00241089 ,0.00280762 ,0.00265503 ,0.00231934 ,0.00234985 , 0.00259399 ,0.00256348 ,0.00308228 ,0.00283813 ,0.00283813 , 0.00280762 ,0.00253296 ,0.00186157 ,0.00204468 ,0.00195312 , 0.00180054 ,0.00222778 ,0.00204468 ,0.00170898 ,0.00164795 , 0.00131226 ,0.00164795 ,0.00225830 ,0.00253296 ,0.00317383 , 0.00262451 ,0.00247192 ,0.00164795 ,0.00186157 ,0.00180054 , 0.00167847 ,0.00164795 ,0.00158691 ,0.00100708 ,0.00054932 , 0.00045776 ,0.00097656 ,0.00192261 ,0.00244141 ,0.00201416 , 0.00170898 ,0.00161743 ,0.00183105 ,0.00143433 ,0.00106812 , 0.00106812 ,0.00158691 ,0.00146484 ,0.00088501 ,0.00076294 , 0.00091553 ,0.00112915 ,0.00128174 ,0.00067139 ,0.00061035 , 0.00051880 ,0.00119019 ,0.00070190 ,0.00119019 ,0.00143433 , 0.00097656 ,0.00128174 ,0.00085449 ,0.00048828 ,0.00021362 , 0.00048828 ,0.00067139 ,0.00036621 ,0.00051880 ,0.00091553 , 0.00137329 ,0.00137329 ,0.00152588 ,0.00128174 ,0.00137329 , 0.00122070 ,0.00048828 ,0.00012207 ,0.00030518 ,0.00024414 , 0.00012207 ,-0.00039673 ,-0.00036621 ,0.00000000 ,0.00009155 , 0.00012207 ,0.00033569 ,0.00070190 ,0.00076294 ,0.00057983 , 0.00024414 ,0.00070190 ,0.00091553 ,0.00134277 ,0.00119019 , 0.00134277 ,0.00115967 ,0.00082397 ,-0.00006104 ,-0.00051880 , -0.00054932 ,-0.00064087 ,-0.00033569 ,-0.00006104 ,0.00079346 , 0.00119019 ,0.00134277 ,0.00082397 ,-0.00006104 ,0.00018311 , 0.00036621 ,0.00122070 ,0.00137329 ,0.00112915 ,0.00067139 , 0.00057983 ,0.00009155 ,-0.00003052 ,0.00006104 ,0.00024414 , 0.00045776 ,0.00015259 ,0.00018311 ,0.00045776 ,0.00036621 , 0.00054932 ,0.00036621 ,0.00021362 ,0.00030518 ,0.00045776 , -0.00015259 ,-0.00021362 ,-0.00018311 ,-0.00018311 ,-0.00024414 , -0.00009155 ,0.00054932 ,0.00088501 ,0.00100708 ,0.00112915 , 0.00036621 ,0.00015259 ,0.00006104 ,0.00027466 ,0.00091553 , 0.00061035 ,0.00094604 ,0.00061035 ,0.00036621 ,-0.00030518 , -0.00112915 ,-0.00106812 ,-0.00091553 ,-0.00039673 ,0.00015259 , 0.00067139 ,0.00076294 ,0.00064087 ,0.00061035 ,0.00027466 , 0.00051880 ,0.00036621 ,0.00088501 ,0.00054932 ,0.00045776 , 0.00042725 ,-0.00003052 ,-0.00021362 ,-0.00033569 ,-0.00033569 , 0.00012207 ,0.00030518 ,0.00024414 ,-0.00030518 ,-0.00036621 , -0.00070190 ,-0.00021362 ,-0.00027466 ,0.00042725 ,0.00070190 , 0.00079346 ,-0.00012207 ,-0.00045776 ,-0.00128174 ,-0.00115967 , -0.00067139 ,-0.00048828 ,-0.00012207 ,-0.00042725 ,-0.00036621 , -0.00030518 ,-0.00027466 ,-0.00027466 ,-0.00018311 ,0.00003052 , -0.00009155 ,0.00000000 ,-0.00012207 ,-0.00018311 ,-0.00042725 , -0.00067139 ,-0.00097656 ,-0.00122070 ,-0.00103760 ,-0.00097656 , -0.00097656 ,-0.00076294 ,-0.00112915 ,-0.00125122 ,-0.00112915 , -0.00088501 ,-0.00051880 ,-0.00012207 ,-0.00021362 ,-0.00045776 , -0.00033569 ,-0.00033569 ,-0.00048828 ,-0.00085449 ,-0.00106812 , -0.00100708 ,-0.00094604 ,-0.00067139 ,-0.00082397 ,-0.00061035 , -0.00112915 ,-0.00119019 ,-0.00161743 ,-0.00109863 ,-0.00048828 , -0.00006104 ,-0.00009155 ,-0.00088501 ,-0.00125122 ,-0.00155640 , -0.00100708 ,-0.00103760 ,-0.00115967 ,-0.00079346 ,-0.00073242 , -0.00082397 ,-0.00131226 ,-0.00143433 ,-0.00112915 ,-0.00091553 , -0.00036621 ,-0.00064087 ,-0.00042725 ,-0.00067139 ,-0.00076294 , -0.00112915 ,-0.00149536 ,-0.00170898 ,-0.00140381 ,-0.00073242 , -0.00054932 ,-0.00012207 ,-0.00048828 ,-0.00064087 ,-0.00100708 , -0.00125122 ,-0.00112915 ,-0.00067139 ,-0.00018311 ,-0.00015259 , -0.00039673 ,-0.00061035 ,-0.00033569 ,-0.00024414 ,-0.00067139 , -0.00143433 ,-0.00167847 ,-0.00201416 ,-0.00201416 ,-0.00183105 , -0.00149536 ,-0.00027466 ,-0.00009155 ,0.00006104 ,-0.00018311 , -0.00045776 ,-0.00045776 ,-0.00051880 ,-0.00036621 ,-0.00051880 , -0.00015259 ,-0.00015259 ,-0.00045776 ,-0.00033569 ,-0.00061035 , -0.00067139 ,-0.00115967 ,-0.00140381 ,-0.00122070 ,-0.00097656 , -0.00051880 ,-0.00051880 ,-0.00039673 ,-0.00051880 ,-0.00042725 , -0.00018311 ,-0.00018311 ,-0.00006104 ,0.00000000 ,-0.00012207 , -0.00033569 ,-0.00036621 ,-0.00067139 ,-0.00027466 ,-0.00042725 , -0.00009155 ,-0.00021362 ,-0.00006104 ,-0.00057983 ,-0.00051880 , -0.00057983 ,-0.00024414 ,0.00006104 ,0.00012207 ,0.00006104 , 0.00009155 ,-0.00030518 ,-0.00042725 ,-0.00061035 ,-0.00079346 , -0.00067139 ,0.00000000 ,0.00048828 ,0.00021362 ,-0.00015259 , -0.00039673 ,-0.00033569 ,-0.00006104 ,0.00079346 ,0.00152588 , 0.00183105 ,0.00204468 ,0.00091553 ,-0.00009155 ,-0.00088501 , -0.00103760 ,-0.00091553 ,-0.00006104 ,0.00057983 ,0.00103760 , 0.00164795 ,0.00115967 ,0.00048828 ,-0.00033569 ,-0.00067139 , -0.00006104 ,0.00073242 ,0.00122070 ,0.00137329 ,0.00128174 , 0.00094604 ,0.00042725 ,-0.00012207 ,-0.00042725 ,-0.00021362 , 0.00006104 ,0.00036621 ,0.00045776 ,0.00082397 ,0.00061035 , 0.00021362 ,0.00018311 ,0.00039673 ,0.00070190 ,0.00082397 , 0.00143433 ,0.00155640 ,0.00128174 ,0.00067139 ,0.00024414 , -0.00009155 ,0.00003052 ,0.00036621 ,0.00100708 ,0.00094604 , 0.00076294 ,0.00048828 ,0.00051880 ,0.00064087 ,0.00082397 , 0.00082397 ,0.00146484 ,0.00128174 ,0.00115967 ,0.00064087 , 0.00012207 ,-0.00003052 ,-0.00006104 ,0.00012207 ,0.00067139 , 0.00079346 ,0.00091553 ,0.00079346 ,0.00045776 ,0.00027466 , 0.00064087 ,0.00103760 ,0.00155640 ,0.00131226 ,0.00079346 , 0.00119019 ,0.00051880 ,0.00027466 ,0.00051880 ,0.00039673 , 0.00061035 ,0.00076294 ,0.00085449 ,0.00064087 ,0.00070190 , 0.00003052 ,-0.00015259 ,-0.00030518 ,0.00018311 ,0.00079346 , 0.00134277 ,0.00122070 ,0.00112915 ,0.00039673 ,-0.00006104 , -0.00073242 ,-0.00085449 ,-0.00051880 ,0.00045776 ,0.00103760 , 0.00094604 ,0.00149536 ,0.00112915 ,0.00103760 ,0.00067139 , 0.00064087 ,0.00085449 ,0.00051880 ,0.00091553 ,0.00051880 , 0.00039673 ,0.00027466 ,0.00021362 ,0.00015259 ,0.00021362 , 0.00103760 ,0.00122070 ,0.00161743 ,0.00134277 ,0.00070190 , 0.00051880 ,0.00070190 ,0.00051880 ,0.00091553 ,0.00119019 , 0.00158691 ,0.00155640 ,0.00158691 ,0.00088501 ,0.00070190 , 0.00067139 ,0.00061035 ,0.00073242 ,0.00076294 ,0.00140381 , 0.00158691 ,0.00164795 ,0.00164795 ,0.00146484 ,0.00097656 , 0.00152588 ,0.00122070 ,0.00106812 ,0.00106812 ,0.00109863 , 0.00112915 ,0.00094604 ,0.00122070 ,0.00106812 ,0.00137329 , 0.00134277 ,0.00109863 ,0.00146484 ,0.00125122 ,0.00143433 , 0.00134277 ,0.00140381 ,0.00167847 ,0.00192261 ,0.00119019 , 0.00109863 ,0.00033569 ,0.00048828 ,0.00076294 ,0.00112915 , 0.00109863 ,0.00137329 ,0.00164795 ,0.00192261 ,0.00177002 , 0.00167847 ,0.00149536 ,0.00158691 ,0.00183105 ,0.00115967 , 0.00088501 ,0.00076294 ,0.00109863 ,0.00088501 ,0.00088501 , 0.00128174 ,0.00115967 ,0.00064087 ,0.00064087 ,0.00067139 , 0.00076294 ,0.00103760 ,0.00106812 ,0.00115967 ,0.00128174 , 0.00149536 ,0.00140381 ,0.00082397 ,0.00088501 ,0.00024414 , 0.00067139 ,0.00082397 ,0.00115967 ,0.00112915 ,0.00137329 , 0.00149536 ,0.00152588 ,0.00106812 ,0.00094604 ,0.00122070 , 0.00125122 ,0.00137329 ,0.00076294 ,0.00100708 ,0.00048828 , 0.00030518 ,0.00018311 ,0.00030518 ,0.00033569 ,0.00106812 , 0.00125122 ,0.00109863 ,0.00079346 ,0.00091553 ,0.00042725 , 0.00030518 ,0.00000000 ,0.00073242 ,0.00054932 ,0.00091553 , 0.00073242 ,0.00051880 ,0.00048828 ,-0.00030518 ,-0.00006104 , -0.00033569 ,-0.00033569 ,0.00015259 ,0.00042725 ,0.00073242 , 0.00100708 ,0.00070190 ,0.00064087 ,0.00036621 ,0.00015259 , -0.00018311 ,-0.00024414 ,-0.00036621 ,-0.00021362 ,-0.00021362 , 0.00018311 ,0.00064087 ,0.00051880 ,0.00048828 ,0.00033569 , -0.00018311 ,0.00006104 ,-0.00033569 ,0.00003052 ,-0.00006104 , 0.00027466 ,0.00021362 ,0.00012207 ,-0.00039673 ,-0.00057983 , -0.00076294 ,-0.00112915 ,-0.00091553 ,-0.00021362 ,0.00006104 , 0.00091553 ,0.00097656 ,0.00079346 ,0.00018311 ,-0.00036621 , -0.00030518 ,-0.00036621 ,-0.00018311 ,0.00000000 ,0.00024414 , -0.00051880 ,-0.00073242 ,-0.00109863 ,-0.00073242 ,-0.00042725 , -0.00003052 ,0.00057983 ,0.00036621 ,0.00051880 ,0.00024414 , -0.00030518 ,-0.00033569 ,-0.00061035 ,-0.00036621 ,-0.00021362 , 0.00003052 ,0.00012207 ,-0.00051880 ,-0.00085449 ,-0.00112915 , -0.00082397 ,-0.00100708 ,-0.00082397 ,-0.00021362 ,0.00033569 , 0.00039673 ,0.00064087 ,-0.00018311 ,-0.00076294 ,-0.00094604 , -0.00097656 ,-0.00103760 ,-0.00103760 ,-0.00057983 ,-0.00045776 , -0.00051880 ,-0.00048828 ,-0.00085449 ,-0.00076294 ,-0.00076294 , -0.00027466 ,-0.00054932 ,-0.00009155 ,-0.00051880 ,-0.00021362 , -0.00061035 ,-0.00054932 ,-0.00088501 ,-0.00085449 ,-0.00094604 , -0.00119019 ,-0.00134277 ,-0.00094604 ,-0.00097656 ,-0.00076294 , -0.00091553 ,-0.00073242 ,-0.00073242 ,-0.00048828 ,-0.00082397 , -0.00064087 ,-0.00045776 ,-0.00067139 ,-0.00061035 ,-0.00094604 , -0.00134277 ,-0.00152588 ,-0.00170898 ,-0.00146484 ,-0.00143433 , -0.00070190 ,-0.00061035 ,-0.00067139 ,-0.00094604 ,-0.00076294 , -0.00061035 ,-0.00100708 ,-0.00079346 ,-0.00064087 ,-0.00070190 , -0.00051880 ,-0.00097656 ,-0.00088501 ,-0.00128174 ,-0.00100708 , -0.00094604 ,-0.00106812 ,-0.00054932 ,-0.00030518 ,-0.00082397 , -0.00106812 ,-0.00155640 ,-0.00115967 ,-0.00088501 ,-0.00070190 , -0.00064087 ,-0.00067139 ,-0.00082397 ,-0.00158691 ,-0.00122070 , -0.00137329 ,-0.00125122 ,-0.00091553 ,-0.00061035 ,-0.00045776 , -0.00039673 ,-0.00036621 ,-0.00061035 ,-0.00051880 ,-0.00021362 , 0.00036621 ,0.00009155 ,-0.00009155 ,-0.00039673 ,-0.00054932 , -0.00122070 ,-0.00183105 ,-0.00146484 ,-0.00106812 ,-0.00057983 , 0.00006104 ,-0.00045776 ,-0.00051880 ,-0.00057983 ,-0.00106812 , -0.00112915 ,-0.00085449 ,-0.00042725 ,0.00027466 ,0.00015259 , 0.00003052 ,-0.00088501 ,-0.00088501 ,-0.00146484 ,-0.00183105 , -0.00167847 ,-0.00100708 ,-0.00048828 ,0.00003052 ,-0.00015259 , -0.00003052 ,-0.00039673 ,-0.00076294 ,-0.00067139 ,-0.00085449 , -0.00042725 ,-0.00048828 ,-0.00021362 ,-0.00051880 ,-0.00106812 , -0.00103760 ,-0.00109863 ,-0.00061035 ,-0.00061035 ,0.00021362 , -0.00030518 ,-0.00030518 ,-0.00079346 ,-0.00122070 ,-0.00076294 , -0.00036621 ,-0.00006104 ,-0.00003052 ,-0.00030518 ,-0.00045776 , -0.00085449 ,-0.00122070 ,-0.00189209 ,-0.00146484 ,-0.00164795 , -0.00091553 ,-0.00042725 ,-0.00036621 ,-0.00057983 ,-0.00070190 , -0.00076294 ,-0.00097656 ,-0.00088501 ,-0.00039673 ,-0.00048828 , -0.00045776 ,-0.00079346 ,-0.00103760 ,-0.00112915 ,-0.00131226 , -0.00100708 ,-0.00064087 ,-0.00018311 ,0.00003052 ,-0.00042725 , -0.00042725 ,-0.00085449 ,-0.00067139 ,-0.00088501 ,-0.00079346 , -0.00018311 ,-0.00030518 ,-0.00006104 ,-0.00036621 ,-0.00048828 , -0.00094604 ,-0.00128174 ,-0.00167847 ,-0.00158691 ,-0.00125122 , -0.00051880 ,-0.00036621 ,0.00042725 ,0.00039673 ,0.00027466 , -0.00015259 ,-0.00033569 ,-0.00054932 ,-0.00006104 ,-0.00027466 , -0.00024414 ,-0.00042725 ,-0.00061035 ,-0.00054932 ,-0.00064087 , -0.00045776 ,-0.00070190 ,-0.00073242 ,-0.00070190 ,-0.00085449 , -0.00054932 ,-0.00027466 ,-0.00003052 ,0.00006104 ,0.00015259 , 0.00009155 ,0.00036621 ,-0.00012207 ,-0.00039673 ,-0.00054932 , -0.00088501 ,-0.00054932 ,-0.00027466 ,0.00006104 ,0.00015259 , 0.00036621 ,0.00048828 ,0.00045776 ,0.00064087 ,0.00030518 , 0.00045776 ,0.00033569 ,0.00036621 ,-0.00015259 ,-0.00015259 , -0.00003052 ,0.00012207 ,0.00045776 ,0.00091553 ,0.00094604 , 0.00064087 ,0.00030518 ,0.00000000 ,-0.00036621 ,-0.00027466 , 0.00030518 ,0.00100708 ,0.00140381 ,0.00155640 ,0.00146484 , 0.00082397 ,0.00073242 ,-0.00009155 ,0.00027466 ,-0.00003052 , 0.00018311 ,0.00064087 ,0.00091553 ,0.00091553 ,0.00122070 , 0.00131226 ,0.00088501 ,0.00036621 ,0.00000000 ,0.00000000 , -0.00006104 ,0.00051880 ,0.00064087 ,0.00079346 ,0.00100708 , 0.00073242 ,0.00073242 ,0.00073242 ,0.00042725 ,0.00039673 , 0.00061035 ,0.00045776 ,0.00039673 ,0.00088501 ,0.00112915 , 0.00088501 ,0.00079346 ,0.00064087 ,0.00048828 ,0.00054932 , 0.00079346 ,0.00064087 ,0.00076294 ,0.00109863 ,0.00082397 , 0.00085449 ,0.00073242 ,0.00064087 ,0.00091553 ,0.00088501 , 0.00149536 ,0.00106812 ,0.00164795 ,0.00122070 ,0.00119019 , 0.00100708 ,0.00061035 ,0.00091553 ,0.00067139 ,0.00125122 , 0.00119019 ,0.00161743 ,0.00152588 ,0.00112915 ,0.00109863 , 0.00079346 ,0.00054932 ,0.00061035 ,0.00125122 ,0.00140381 , 0.00204468 ,0.00158691 ,0.00183105 ,0.00155640 ,0.00173950 , 0.00140381 ,0.00164795 ,0.00155640 ,0.00183105 ,0.00149536 , 0.00173950 ,0.00189209 ,0.00180054 ,0.00173950 ,0.00152588 , 0.00115967 ,0.00137329 ,0.00097656 ,0.00134277 ,0.00137329 , 0.00158691 ,0.00173950 ,0.00177002 ,0.00195312 ,0.00213623 , 0.00198364 ,0.00189209 ,0.00207520 ,0.00195312 ,0.00158691 , 0.00177002 ,0.00161743 ,0.00167847 ,0.00167847 ,0.00170898 , 0.00189209 ,0.00198364 ,0.00238037 ,0.00234985 ,0.00231934 , 0.00170898 ,0.00177002 ,0.00146484 ,0.00170898 ,0.00189209 , 0.00225830 ,0.00250244 ,0.00247192 ,0.00216675 ,0.00183105 , 0.00170898 ,0.00180054 ,0.00146484 ,0.00143433 ,0.00155640 , 0.00177002 ,0.00219727 ,0.00277710 ,0.00299072 ,0.00268555 , 0.00195312 ,0.00152588 ,0.00143433 ,0.00115967 ,0.00143433 , 0.00164795 ,0.00183105 ,0.00186157 ,0.00213623 ,0.00210571 , 0.00177002 ,0.00213623 ,0.00180054 ,0.00177002 ,0.00155640 , 0.00183105 ,0.00167847 ,0.00222778 ,0.00228882 ,0.00241089 , 0.00222778 ,0.00155640 ,0.00170898 ,0.00115967 ,0.00152588 , 0.00131226 ,0.00152588 ,0.00152588 ,0.00155640 ,0.00189209 , 0.00222778 ,0.00268555 ,0.00265503 ,0.00265503 ,0.00201416 , 0.00155640 ,0.00109863 ,0.00119019 ,0.00167847 ,0.00186157 , 0.00234985 ,0.00259399 ,0.00207520 ,0.00198364 ,0.00140381 , 0.00164795 ,0.00170898 ,0.00198364 ,0.00238037 ,0.00241089 , 0.00231934 ,0.00225830 ,0.00152588 ,0.00158691 ,0.00167847 , 0.00170898 ,0.00195312 ,0.00183105 ,0.00195312 ,0.00183105 , 0.00183105 ,0.00152588 ,0.00155640 ,0.00173950 ,0.00234985 , 0.00244141 ,0.00234985 ,0.00222778 ,0.00198364 ,0.00180054 , 0.00158691 ,0.00167847 ,0.00158691 ,0.00186157 ,0.00192261 , 0.00195312 ,0.00192261 ,0.00183105 ,0.00189209 ,0.00201416 , 0.00186157 ,0.00195312 ,0.00201416 ,0.00234985 ,0.00231934 , 0.00198364 ,0.00155640 ,0.00146484 ,0.00137329 ,0.00186157 , 0.00177002 ,0.00228882 ,0.00207520 ,0.00180054 ,0.00186157 , 0.00161743 ,0.00192261 ,0.00189209 ,0.00210571 ,0.00225830 , 0.00228882 ,0.00219727 ,0.00189209 ,0.00155640 ,0.00119019 , 0.00146484 ,0.00146484 ,0.00143433 ,0.00155640 ,0.00173950 , 0.00158691 ,0.00149536 ,0.00106812 ,0.00103760 ,0.00106812 , 0.00137329 ,0.00189209 ,0.00198364 ,0.00225830 ,0.00177002 , 0.00158691 ,0.00109863 ,0.00097656 ,0.00079346 ,0.00094604 , 0.00137329 ,0.00137329 ,0.00137329 ,0.00106812 ,0.00076294 , 0.00073242 ,0.00067139 ,0.00064087 ,0.00091553 ,0.00131226 , 0.00170898 ,0.00177002 ,0.00152588 ,0.00082397 ,0.00057983 , -0.00012207 ,0.00000000 ,0.00003052 ,0.00024414 ,0.00073242 , 0.00073242 ,0.00030518 ,0.00036621 ,0.00042725 ,0.00039673 , 0.00036621 ,0.00061035 ,0.00045776 ,0.00051880 ,0.00030518 , 0.00018311 ,-0.00018311 ,-0.00006104 ,0.00015259 ,0.00009155 , 0.00012207 ,0.00024414 ,0.00039673 ,0.00036621 ,0.00021362 , 0.00021362 ,0.00015259 ,0.00045776 ,0.00042725 ,0.00048828 , 0.00085449 ,0.00036621 ,0.00000000 ,-0.00021362 ,-0.00073242 , -0.00015259 ,-0.00030518 ,-0.00018311 ,0.00006104 ,0.00021362 , -0.00030518 ,-0.00006104 ,-0.00021362 ,-0.00006104 ,0.00009155 , 0.00036621 ,0.00045776 ,0.00021362 ,0.00000000 ,0.00006104 , -0.00064087 ,-0.00036621 ,-0.00033569 ,-0.00036621 ,-0.00003052 , 0.00027466 ,0.00015259 ,0.00057983 ,0.00027466 ,0.00021362 , -0.00018311 ,0.00009155 ,-0.00015259 ,0.00036621 ,0.00051880 , 0.00073242 ,0.00048828 ,0.00018311 ,0.00000000 ,-0.00033569 , -0.00018311 ,-0.00015259 ,-0.00006104 ,0.00033569 ,0.00021362 , 0.00009155 ,0.00039673 ,0.00036621 ,0.00042725 ,0.00021362 , 0.00018311 ,0.00027466 ,-0.00006104 ,0.00006104 ,-0.00012207 , -0.00018311 ,0.00003052 ,-0.00039673 ,-0.00024414 ,-0.00003052 , 0.00033569 ,0.00048828 ,0.00082397 ,0.00045776 ,0.00051880 , 0.00045776 ,0.00015259 ,0.00030518 ,0.00012207 ,0.00039673 , 0.00061035 ,0.00021362 ,0.00064087 ,0.00036621 ,0.00030518 , -0.00012207 ,-0.00012207 ,-0.00030518 ,0.00003052 ,-0.00009155 , 0.00003052 ,0.00036621 ,0.00042725 ,0.00033569 ,0.00039673 , -0.00018311 ,0.00012207 ,-0.00042725 ,0.00009155 ,-0.00033569 , -0.00018311 ,-0.00024414 ,-0.00009155 ,0.00021362 ,0.00009155 , -0.00006104 ,-0.00015259 ,-0.00003052 ,0.00012207 ,-0.00042725 , -0.00021362 ,-0.00015259 ,-0.00036621 ,-0.00033569 ,-0.00030518 , -0.00018311 ,-0.00030518 ,0.00000000 ,-0.00015259 ,-0.00039673 , -0.00033569 ,-0.00045776 ,-0.00051880 ,-0.00082397 ,-0.00057983 , -0.00021362 ,0.00021362 ,0.00039673 ,-0.00006104 ,-0.00003052 , -0.00036621 ,-0.00064087 ,-0.00067139 ,-0.00091553 ,-0.00061035 , -0.00051880 ,-0.00036621 ,-0.00070190 ,-0.00061035 ,-0.00100708 , -0.00088501 ,-0.00079346 ,-0.00073242 ,-0.00012207 ,-0.00042725 , -0.00030518 ,-0.00036621 ,-0.00085449 ,-0.00079346 ,-0.00079346 , -0.00039673 ,-0.00024414 ,0.00009155 ,0.00009155 ,-0.00015259 , -0.00051880 ,-0.00039673 ,-0.00070190 ,-0.00033569 ,-0.00067139 , -0.00036621 ,-0.00027466 ,-0.00024414 ,-0.00051880 ,-0.00036621 , -0.00067139 ,-0.00073242 ,-0.00051880 ,-0.00051880 ,-0.00085449 , -0.00048828 ,-0.00079346 ,-0.00045776 ,-0.00021362 ,-0.00015259 , 0.00012207 ,0.00027466 ,-0.00006104 ,-0.00054932 ,-0.00061035 , -0.00079346 ,-0.00064087 ,-0.00045776 ,-0.00036621 ,-0.00030518 , -0.00015259 ,-0.00015259 ,-0.00067139 ,-0.00076294 ,-0.00070190 , -0.00061035 ,-0.00054932 ,-0.00045776 ,-0.00057983 ,-0.00018311 , -0.00015259 ,-0.00054932 ,-0.00070190 ,-0.00085449 ,-0.00054932 , -0.00036621 ,-0.00024414 ,-0.00036621 ,-0.00006104 ,-0.00042725 , -0.00048828 ,-0.00085449 ,-0.00088501 ,-0.00057983 ,-0.00015259 , -0.00051880 ,-0.00042725 ,-0.00057983 ,-0.00057983 ,-0.00103760 , -0.00079346 ,-0.00067139 ,-0.00073242 ,-0.00054932 ,-0.00048828 , -0.00036621 ,-0.00015259 ,-0.00024414 ,-0.00027466 ,-0.00064087 , -0.00015259 ,-0.00018311 ,0.00006104 ,0.00006104 ,-0.00042725 , -0.00079346 ,-0.00082397 ,-0.00064087 ,-0.00091553 ,-0.00039673 , -0.00015259 ,0.00009155 ,-0.00024414 ,-0.00048828 ,-0.00064087 , -0.00030518 ,-0.00033569 ,0.00003052 ,0.00021362 ,0.00003052 , -0.00018311 ,-0.00018311 ,-0.00054932 ,-0.00048828 ,-0.00079346 , -0.00070190 ,-0.00039673 ,-0.00012207 ,0.00015259 ,0.00045776 , 0.00039673 ,0.00015259 ,-0.00003052 ,0.00006104 ,0.00009155 , 0.00021362 ,0.00009155 ,0.00000000 ,-0.00021362 ,-0.00036621 , -0.00042725 ,-0.00042725 ,-0.00027466 ,-0.00012207 ,0.00027466 , -0.00003052 ,-0.00021362 ,-0.00015259 ,-0.00027466 ,-0.00009155 , -0.00033569 ,0.00033569 ,0.00061035 ,0.00091553 ,0.00051880 , 0.00030518 ,-0.00021362 ,-0.00030518 ,0.00012207 ,0.00021362 , 0.00067139 ,0.00103760 ,0.00079346 ,0.00073242 ,0.00015259 , 0.00036621 ,0.00015259 ,0.00021362 ,0.00051880 ,0.00051880 , 0.00094604 ,0.00085449 ,0.00045776 ,0.00033569 ,-0.00024414 , 0.00000000 ,0.00024414 ,0.00079346 ,0.00088501 ,0.00119019 , 0.00134277 ,0.00122070 ,0.00042725 ,0.00030518 ,0.00027466 , 0.00039673 ,0.00091553 ,0.00076294 ,0.00076294 ,0.00039673 , 0.00033569 ,0.00015259 ,-0.00024414 ,0.00015259 ,0.00033569 , 0.00073242 ,0.00088501 ,0.00100708 ,0.00128174 ,0.00076294 , 0.00073242 ,0.00042725 ,0.00070190 ,0.00061035 ,0.00045776 , 0.00061035 ,0.00012207 ,0.00045776 ,-0.00015259 ,0.00006104 , -0.00012207 ,0.00006104 ,0.00067139 ,0.00042725 ,0.00088501 , 0.00027466 ,0.00039673 ,0.00015259 ,0.00076294 ,0.00039673 , 0.00057983 ,0.00100708 ,0.00045776 ,0.00006104 ,0.00006104 , -0.00024414 ,0.00024414 ,0.00045776 ,0.00067139 ,0.00048828 , 0.00048828 ,0.00054932 ,0.00045776 ,0.00027466 ,0.00003052 , 0.00036621 ,0.00033569 ,0.00070190 ,0.00067139 ,0.00070190 , 0.00021362 ,-0.00009155 ,-0.00012207 ,-0.00018311 ,0.00018311 , 0.00091553 ,0.00103760 ,0.00134277 ,0.00088501 ,0.00094604 , 0.00048828 ,0.00012207 ,0.00000000 ,0.00048828 ,0.00082397 , 0.00082397 ,0.00076294 ,0.00054932 ,0.00033569 ,-0.00024414 , -0.00024414 ,0.00018311 ,0.00085449 ,0.00085449 ,0.00119019 , 0.00112915 ,0.00067139 ,0.00061035 ,0.00036621 ,0.00027466 , 0.00085449 ,0.00082397 ,0.00112915 ,0.00119019 ,0.00073242 , 0.00082397 ,0.00048828 ,0.00057983 ,0.00039673 ,0.00085449 , 0.00097656 ,0.00106812 ,0.00128174 ,0.00085449 ,0.00082397 , 0.00051880 ,0.00085449 ,0.00061035 ,0.00128174 ,0.00097656 , 0.00094604 ,0.00070190 ,0.00067139 ,0.00076294 ,0.00064087 , 0.00079346 ,0.00119019 ,0.00103760 ,0.00149536 ,0.00115967 , 0.00152588 ,0.00103760 ,0.00122070 ,0.00042725 ,0.00082397 , 0.00076294 ,0.00109863 ,0.00085449 ,0.00100708 ,0.00051880 , 0.00079346 ,0.00039673 ,0.00079346 ,0.00106812 ,0.00115967 , 0.00094604 ,0.00070190 ,0.00094604 ,0.00048828 ,0.00073242 , 0.00076294 ,0.00064087 ,0.00061035 ,0.00085449 ,0.00082397 , 0.00054932 ,0.00057983 ,0.00033569 ,0.00064087 ,0.00082397 , 0.00088501 ,0.00134277 ,0.00140381 ,0.00091553 ,0.00079346 , 0.00042725 ,0.00048828 ,0.00039673 ,0.00048828 ,0.00057983 , 0.00057983 ,0.00088501 ,0.00042725 ,0.00030518 ,0.00009155 , 0.00006104 ,0.00003052 ,0.00054932 ,0.00054932 ,0.00109863 , 0.00061035 ,0.00070190 ,0.00042725 ,-0.00006104 ,0.00006104 , -0.00030518 ,-0.00015259 ,-0.00009155 ,0.00033569 ,0.00018311 , 0.00015259 ,-0.00009155 ,0.00006104 ,-0.00024414 ,-0.00012207 , 0.00015259 ,0.00009155 ,0.00012207 ,0.00018311 ,0.00006104 , -0.00009155 ,-0.00012207 ,-0.00030518 ,-0.00021362 ,-0.00036621 , -0.00057983 ,-0.00012207 ,-0.00006104 ,-0.00009155 ,-0.00006104 , -0.00036621 ,-0.00021362 ,-0.00045776 ,-0.00009155 ,-0.00027466 , -0.00015259 ,-0.00054932 ,-0.00021362 ,-0.00079346 ,-0.00042725 , -0.00067139 ,-0.00064087 ,-0.00057983 ,-0.00057983 ,-0.00039673 , -0.00012207 ,0.00000000 ,-0.00021362 ,-0.00006104 ,-0.00009155 , -0.00021362 ,-0.00030518 ,-0.00030518 ,0.00006104 ,-0.00012207 , 0.00015259 ,-0.00039673 ,-0.00042725 ,-0.00079346 ,-0.00033569 , -0.00051880 ,-0.00042725 ,-0.00064087 ,-0.00018311 ,-0.00027466 , 0.00003052 ,-0.00021362 ,-0.00036621 ,-0.00033569 ,-0.00012207 , -0.00036621 ,-0.00033569 ,-0.00067139 ,-0.00045776 ,-0.00070190 , -0.00036621 ,-0.00054932 ,-0.00048828 ,-0.00042725 ,-0.00064087 , -0.00054932 ,-0.00018311 ,-0.00027466 ,0.00009155 ,-0.00039673 , -0.00009155 ,-0.00036621 ,-0.00018311 ,-0.00051880 ,-0.00091553 , -0.00082397 ,-0.00094604 ,-0.00051880 ,-0.00076294 ,-0.00079346 , -0.00079346 ,-0.00079346 ,-0.00079346 ,-0.00112915 ,-0.00085449 , -0.00061035 ,-0.00067139 ,-0.00042725 ,-0.00061035 ,-0.00030518 , -0.00061035 ,-0.00027466 ,-0.00045776 ,-0.00054932 ,-0.00103760 , -0.00085449 ,-0.00088501 ,-0.00036621 ,-0.00057983 ,-0.00033569 , -0.00054932 ,-0.00061035 ,-0.00054932 ,-0.00094604 ,-0.00112915 , -0.00097656 ,-0.00119019 ,-0.00070190 ,-0.00079346 ,-0.00070190 , -0.00067139 ,-0.00091553 ,-0.00079346 ,-0.00094604 ,-0.00085449 , -0.00042725 ,-0.00048828 ,-0.00030518 ,-0.00042725 ,-0.00079346 , -0.00109863 ,-0.00106812 ,-0.00091553 ,-0.00106812 ,-0.00076294 , -0.00085449 ,-0.00064087 ,-0.00112915 ,-0.00097656 ,-0.00140381 , -0.00115967 ,-0.00091553 ,-0.00100708 ,-0.00070190 ,-0.00076294 , -0.00057983 ,-0.00042725 ,-0.00073242 ,-0.00064087 ,-0.00100708 , -0.00097656 ,-0.00079346 ,-0.00033569 ,-0.00070190 ,-0.00042725 , -0.00091553 ,-0.00100708 ,-0.00152588 ,-0.00128174 ,-0.00115967 , -0.00073242 ,-0.00042725 ,-0.00018311 ,-0.00076294 ,-0.00122070 , -0.00137329 ,-0.00161743 ,-0.00125122 ,-0.00112915 ,-0.00048828 , -0.00048828 ,-0.00061035 ,-0.00054932 ,-0.00091553 ,-0.00073242 , -0.00091553 ,-0.00051880 ,-0.00036621 ,-0.00051880 ,-0.00030518 , -0.00018311 ,-0.00039673 ,-0.00079346 ,-0.00064087 ,-0.00033569 , -0.00009155 ,-0.00073242 ,-0.00036621 ,-0.00091553 ,-0.00094604 , -0.00079346 ,-0.00082397 ,-0.00076294 ,-0.00030518 ,-0.00006104 , -0.00003052 ,-0.00021362 ,-0.00054932 ,-0.00100708 ,-0.00119019 , -0.00115967 ,-0.00103760 ,-0.00039673 ,0.00012207 ,0.00000000 , 0.00024414 ,-0.00057983 ,-0.00070190 ,-0.00106812 ,-0.00109863 , -0.00088501 ,-0.00061035 ,-0.00018311 ,-0.00027466 ,-0.00036621 , -0.00027466 ,-0.00091553 ,-0.00097656 ,-0.00085449 ,-0.00061035 , -0.00012207 ,-0.00027466 ,-0.00039673 ,-0.00036621 ,-0.00094604 , -0.00073242 ,-0.00085449 ,-0.00054932 ,0.00021362 ,-0.00027466 , 0.00033569 ,-0.00015259 ,-0.00061035 ,-0.00088501 ,-0.00085449 , -0.00088501 ,-0.00036621 ,0.00000000 ,0.00030518 ,0.00024414 , -0.00015259 ,-0.00042725 ,-0.00048828 ,-0.00064087 ,-0.00061035 , -0.00076294 ,-0.00045776 ,-0.00021362 ,-0.00009155 ,0.00015259 , 0.00000000 ,-0.00036621 ,-0.00027466 ,-0.00048828 ,-0.00054932 , -0.00064087 ,-0.00033569 ,-0.00024414 ,-0.00027466 ,0.00009155 , 0.00015259 ,0.00006104 ,0.00027466 ,-0.00003052 ,-0.00006104 , -0.00024414 ,0.00009155 ,0.00009155 ,0.00003052 ,-0.00009155 , 0.00006104 ,0.00054932 ,-0.00003052 ,0.00021362 ,-0.00030518 , -0.00018311 ,-0.00024414 ,-0.00036621 ,-0.00024414 ,-0.00009155 , 0.00015259 ,0.00018311 ,0.00012207 ,0.00009155 ,0.00054932 , 0.00051880 ,0.00051880 ,0.00006104 ,0.00024414 ,0.00018311 , 0.00036621 ,-0.00012207 ,0.00000000 ,0.00003052 ,0.00039673 , 0.00006104 ,0.00015259 ,0.00006104 ,0.00000000 ,0.00030518 , 0.00021362 ,0.00039673 ,0.00009155 ,0.00061035 ,0.00030518 , 0.00039673 ,0.00036621 ,0.00039673 ,0.00064087 ,0.00061035 , 0.00076294 ,0.00048828 ,0.00015259 ,-0.00006104 ,0.00000000 , -0.00003052 ,0.00027466 ,0.00073242 ,0.00079346 ,0.00085449 , 0.00051880 ,0.00033569 ,0.00009155 ,0.00003052 ,-0.00012207 , 0.00033569 ,0.00039673 ,0.00036621 ,0.00061035 ,0.00024414 , 0.00036621 ,0.00033569 ,0.00036621 ,0.00057983 ,0.00073242 , 0.00070190 ,0.00097656 ,0.00036621 ,0.00015259 ,0.00024414 , 0.00009155 ,0.00015259 ,0.00045776 ,0.00036621 ,0.00064087 , 0.00042725 ,0.00051880 ,-0.00003052 ,0.00018311 ,0.00000000 , 0.00057983 ,0.00057983 ,0.00088501 ,0.00079346 ,0.00106812 , 0.00067139 ,0.00048828 ,-0.00009155 ,0.00036621 ,-0.00015259 , -0.00006104 ,-0.00006104 ,-0.00018311 ,0.00009155 ,0.00009155 , 0.00021362 ,0.00054932 ,0.00042725 ,0.00064087 ,0.00061035 , 0.00091553 ,0.00054932 ,0.00018311 ,0.00018311 ,0.00024414 , 0.00015259 ,0.00051880 ,0.00036621 ,0.00048828 ,0.00039673 , 0.00018311 ,0.00033569 ,0.00030518 ,0.00042725 ,0.00054932 , 0.00094604 ,0.00076294 ,0.00085449 ,0.00079346 ,0.00073242 , 0.00054932 ,0.00048828 ,0.00012207 ,0.00009155 ,0.00027466 , 0.00033569 ,0.00036621 ,0.00076294 ,0.00070190 ,0.00091553 , 0.00103760 ,0.00103760 ,0.00125122 ,0.00106812 ,0.00125122 , 0.00082397 ,0.00054932 ,0.00036621 ,0.00033569 ,0.00061035 , 0.00054932 ,0.00061035 ,0.00076294 ,0.00061035 ,0.00067139 , 0.00045776 ,0.00085449 ,0.00097656 ,0.00112915 ,0.00155640 , 0.00137329 ,0.00106812 ,0.00143433 ,0.00131226 ,0.00064087 , 0.00054932 ,0.00030518 ,0.00021362 ,0.00033569 ,0.00039673 , 0.00076294 ,0.00106812 ,0.00143433 ,0.00134277 ,0.00128174 , 0.00085449 ,0.00091553 ,0.00103760 ,0.00122070 ,0.00109863 , 0.00076294 ,0.00082397 ,0.00094604 ,0.00067139 ,0.00064087 , 0.00051880 ,0.00079346 ,0.00061035 ,0.00085449 ,0.00094604 , 0.00146484 ,0.00152588 ,0.00137329 ,0.00128174 ,0.00137329 , 0.00125122 ,0.00149536 ,0.00103760 ,0.00067139 ,0.00045776 , 0.00018311 ,0.00048828 ,0.00042725 ,0.00064087 ,0.00119019 , 0.00119019 ,0.00094604 ,0.00112915 ,0.00115967 ,0.00103760 , 0.00106812 ,0.00091553 ,0.00079346 ,0.00109863 ,0.00054932 , 0.00045776 ,0.00070190 ,0.00073242 ,0.00085449 ,0.00073242 , 0.00061035 ,0.00076294 ,0.00097656 ,0.00091553 ,0.00100708 , 0.00097656 ,0.00091553 ,0.00134277 ,0.00134277 ,0.00094604 , 0.00073242 ,0.00057983 ,0.00015259 ,0.00009155 ,-0.00015259 , 0.00027466 ,0.00012207 ,0.00051880 ,0.00067139 ,0.00106812 , 0.00106812 ,0.00091553 ,0.00109863 ,0.00079346 ,0.00070190 , 0.00061035 ,0.00057983 ,0.00036621 ,0.00045776 ,0.00042725 , 0.00027466 ,0.00018311 ,0.00012207 ,0.00024414 ,0.00018311 , 0.00024414 ,0.00051880 ,0.00048828 ,0.00076294 ,0.00119019 , 0.00106812 ,0.00100708 ,0.00097656 ,0.00085449 ,0.00067139 , 0.00054932 ,0.00015259 ,0.00003052 ,0.00018311 ,-0.00006104 , 0.00012207 ,0.00051880 ,0.00067139 ,0.00073242 ,0.00064087 , 0.00079346 ,0.00045776 ,0.00051880 ,0.00057983 ,0.00064087 , 0.00076294 ,0.00079346 ,0.00057983 ,0.00070190 ,0.00051880 , 0.00048828 ,0.00021362 ,0.00012207 ,0.00039673 ,0.00057983 , 0.00054932 ,0.00088501 ,0.00085449 ,0.00112915 ,0.00079346 , 0.00064087 ,0.00064087 ,0.00064087 ,0.00021362 ,0.00048828 , 0.00057983 ,0.00039673 ,0.00030518 ,0.00018311 ,0.00015259 , 0.00033569 ,0.00033569 ,0.00082397 ,0.00076294 ,0.00119019 , 0.00088501 ,0.00076294 ,0.00048828 ,0.00064087 ,0.00036621 , 0.00024414 ,0.00021362 ,0.00051880 ,0.00030518 ,0.00033569 , 0.00030518 ,0.00024414 ,0.00042725 ,0.00024414 ,0.00027466 , 0.00033569 ,0.00036621 ,0.00067139 ,0.00057983 ,0.00042725 , 0.00064087 ,0.00036621 ,0.00045776 ,0.00027466 ,0.00015259 , -0.00006104 ,0.00015259 ,0.00033569 ,0.00018311 ,0.00027466 , 0.00015259 ,0.00045776 ,0.00021362 ,0.00076294 ,0.00033569 , 0.00045776 ,0.00039673 ,0.00036621 ,0.00036621 ,0.00030518 , 0.00006104 ,0.00006104 ,-0.00012207 ,-0.00018311 ,-0.00036621 , -0.00030518 ,-0.00009155 ,0.00018311 ,0.00030518 ,-0.00033569 , 0.00030518 ,-0.00021362 ,-0.00015259 ,-0.00021362 ,0.00000000 , 0.00015259 ,-0.00009155 ,0.00009155 ,-0.00027466 ,-0.00057983 , -0.00045776 ,-0.00067139 ,-0.00015259 ,-0.00015259 ,-0.00012207 , -0.00024414 ,0.00006104 ,-0.00003052 ,-0.00024414 ,-0.00009155 , -0.00057983 ,-0.00054932 ,-0.00030518 ,-0.00036621 ,-0.00054932 , -0.00030518 ,-0.00030518 ,-0.00015259 ,-0.00024414 ,-0.00009155 , -0.00039673 ,-0.00061035 ,-0.00051880 ,-0.00073242 ,-0.00039673 , -0.00057983 ,-0.00033569 ,-0.00021362 ,-0.00033569 ,-0.00048828 , -0.00051880 ,-0.00045776 ,-0.00073242 ,-0.00073242 ,-0.00061035 , -0.00067139 ,-0.00048828 ,-0.00057983 ,-0.00024414 ,-0.00051880 , 0.00000000 ,-0.00030518 ,-0.00006104 ,-0.00012207 ,-0.00027466 , -0.00030518 ,-0.00042725 ,-0.00079346 ,-0.00085449 ,-0.00061035 , -0.00021362 ,-0.00042725 ,-0.00021362 ,-0.00042725 ,-0.00015259 , -0.00012207 ,-0.00057983 ,-0.00039673 ,-0.00051880 ,-0.00036621 , -0.00015259 ,-0.00009155 ,-0.00012207 ,-0.00057983 ,-0.00018311 , -0.00051880 ,-0.00051880 ,-0.00042725 ,-0.00057983 ,-0.00042725 , -0.00067139 ,-0.00048828 ,-0.00054932 ,-0.00067139 ,-0.00027466 , -0.00033569 ,-0.00006104 ,-0.00006104 ,-0.00003052 ,-0.00003052 , -0.00036621 ,-0.00048828 ,-0.00064087 ,-0.00057983 ,-0.00061035 , -0.00045776 ,-0.00039673 ,-0.00048828 ,-0.00036621 ,-0.00054932 , -0.00051880 ,-0.00045776 ,-0.00076294 ,-0.00061035 ,-0.00030518 , -0.00042725 ,-0.00039673 ,-0.00057983 ,-0.00051880 ,-0.00088501 , -0.00039673 ,-0.00045776 ,-0.00015259 ,-0.00006104 ,-0.00027466 , -0.00067139 ,-0.00064087 ,-0.00009155 ,-0.00036621 ,-0.00009155 , -0.00033569 ,-0.00012207 ,-0.00048828 ,-0.00061035 ,-0.00088501 , -0.00067139 ,-0.00067139 ,-0.00033569 ,-0.00045776 ,-0.00021362 , -0.00027466 ,-0.00057983 ,-0.00039673 ,-0.00039673 ,-0.00048828 , -0.00009155 ,0.00000000 ,0.00021362 ,0.00027466 ,0.00009155 , -0.00030518 ,-0.00027466 ,-0.00051880 ,-0.00024414 ,-0.00030518 , -0.00033569 ,-0.00054932 ,-0.00006104 ,-0.00021362 ,-0.00003052 , -0.00030518 ,-0.00027466 ,-0.00045776 ,-0.00036621 ,-0.00027466 , -0.00039673 ,-0.00018311 ,0.00012207 ,0.00000000 ,-0.00012207 , -0.00027466 ,-0.00006104 ,-0.00018311 ,-0.00027466 ,-0.00015259 , -0.00024414 ,-0.00051880 ,-0.00036621 ,-0.00042725 ,-0.00027466 , -0.00030518 ,0.00006104 ,-0.00003052 ,-0.00021362 ,-0.00048828 , -0.00033569 ,-0.00018311 ,-0.00027466 ,-0.00039673 ,-0.00036621 , -0.00021362 ,-0.00061035 ,-0.00051880 ,-0.00057983 ,-0.00057983 , -0.00039673 ,-0.00024414 ,-0.00048828 ,-0.00039673 ,-0.00027466 , -0.00027466 ,-0.00051880 ,-0.00018311 ,-0.00054932 ,-0.00051880 , -0.00021362 ,-0.00051880 ,-0.00045776 ,-0.00054932 ,-0.00039673 , -0.00036621 ,-0.00039673 ,-0.00021362 ,0.00000000 ,0.00009155 , 0.00003052 ,-0.00015259 ,-0.00018311 ,-0.00042725 ,-0.00045776 , -0.00076294 ,-0.00042725 ,-0.00027466 ,-0.00033569 ,0.00003052 , -0.00021362 ,-0.00030518 ,-0.00030518 ,-0.00051880 ,-0.00045776 , -0.00057983 ,0.00000000 ,-0.00009155 ,0.00027466 ,-0.00003052 , 0.00042725 ,0.00021362 ,0.00006104 ,-0.00006104 ,-0.00033569 , -0.00012207 ,-0.00057983 ,0.00003052 ,-0.00006104 ,-0.00003052 , 0.00012207 ,-0.00027466 ,-0.00045776 ,-0.00036621 ,-0.00021362 , -0.00024414 ,-0.00030518 ,-0.00006104 ,-0.00018311 ,-0.00003052 , 0.00027466 ,0.00039673 ,0.00009155 ,0.00024414 ,-0.00003052 , -0.00018311 ,-0.00015259 ,-0.00012207 ,-0.00006104 ,-0.00012207 , -0.00006104 ,0.00015259 ,-0.00009155 ,0.00042725 ,0.00006104 , 0.00030518 ,-0.00024414 ,-0.00006104 ,-0.00015259 ,0.00003052 , -0.00003052 ,0.00012207 ,0.00024414 ,0.00039673 ,-0.00003052 , 0.00027466 ,-0.00006104 ,0.00018311 ,0.00003052 ,0.00030518 , 0.00009155 ,0.00045776 ,-0.00009155 ,-0.00003052 ,0.00009155 , 0.00003052 ,0.00030518 ,0.00027466 ,0.00039673 ,-0.00018311 , 0.00033569 ,0.00003052 ,0.00039673 ,0.00036621 ,0.00091553 , 0.00076294 ,0.00064087 ,0.00061035 ,0.00051880 ,0.00021362 , 0.00033569 ,0.00045776 ,0.00054932 ,0.00073242 ,0.00079346 , 0.00036621 ,0.00054932 ,0.00024414 ,0.00030518 ,0.00027466 , 0.00021362 ,0.00018311 ,0.00018311 ,0.00064087 ,0.00073242 , 0.00051880 ,0.00057983 ,0.00057983 ,0.00051880 ,0.00030518 , 0.00012207 ,0.00030518 ,0.00003052 ,0.00057983 ,0.00021362 , 0.00027466 ,0.00006104 ,0.00051880 ,0.00003052 ,0.00033569 , 0.00042725 ,0.00042725 ,0.00042725 ,0.00039673 ,0.00042725 , 0.00042725 ,0.00045776 ,0.00033569 ,0.00045776 ,0.00045776 , 0.00045776 ,0.00027466 ,0.00018311 ,-0.00018311 ,-0.00027466 , -0.00003052 ,0.00015259 ,0.00039673 ,0.00045776 ,0.00039673 , 0.00024414 ,0.00036621 ,0.00009155 ,-0.00009155 ,-0.00036621 , 0.00009155 ,-0.00009155 ,0.00018311 ,0.00006104 ,0.00042725 , 0.00024414 ,0.00033569 ,0.00018311 ,0.00006104 ,-0.00003052 , -0.00018311 ,-0.00018311 ,-0.00027466 ,-0.00012207 ,0.00000000 , 0.00021362 ,0.00006104 ,-0.00012207 ,-0.00006104 ,0.00003052 , -0.00006104 ,-0.00009155 ,-0.00003052 ,0.00045776 ,0.00018311 , 0.00009155 ,0.00042725 ,0.00036621 ,0.00003052 ,0.00000000 , -0.00030518 ,-0.00048828 ,-0.00018311 ,-0.00015259 ,-0.00009155 , -0.00012207 ,-0.00027466 ,-0.00012207 ,-0.00027466 ,-0.00021362 , 0.00000000 ,0.00003052 ,0.00024414 ,-0.00009155 ,0.00015259 , -0.00003052 ,0.00000000 ,0.00000000 ,0.00021362 ,-0.00012207 , 0.00015259 ,-0.00009155 ,-0.00006104 ,0.00009155 ,-0.00018311 , -0.00021362 ,-0.00009155 ,0.00015259 ,0.00000000 ,0.00000000 , 0.00012207 ,0.00024414 ,0.00015259 ,0.00006104 ,-0.00015259 , 0.00006104 ,0.00015259 ,0.00027466 ,0.00012207 ,0.00009155 , 0.00006104 ,-0.00012207 ,-0.00003052 ,0.00012207 ,0.00024414 , 0.00018311 ,0.00033569 ,0.00027466 ,0.00003052 ,0.00015259 , 0.00024414 ,0.00003052 ,0.00009155 ,0.00009155 ,0.00024414 , 0.00018311 ,0.00024414 ,0.00018311 ,0.00045776 ,0.00061035 , 0.00030518 ,0.00061035 ,0.00048828 ,0.00030518 ,0.00021362 , 0.00033569 ,0.00036621 ,0.00036621 ,0.00021362 ,0.00003052 , 0.00021362 ,0.00024414 ,0.00024414 ,0.00015259 ,-0.00003052 , 0.00000000 ,-0.00003052 ,0.00018311 ,0.00024414 ,0.00048828 , 0.00039673 ,0.00054932 ,0.00009155 ,0.00015259 ,-0.00012207 , -0.00012207 ,0.00015259 ,-0.00012207 ,0.00024414 ,0.00003052 , 0.00030518 ,0.00030518 ,0.00042725 ,0.00033569 ,0.00054932 , -0.00012207 ,-0.00003052 ,-0.00027466 ,-0.00024414 ,0.00024414 , -0.00006104 ,-0.00021362 ,-0.00003052 ,-0.00003052 ,0.00006104 , -0.00036621 ,-0.00042725 ,-0.00006104 ,-0.00024414 ,-0.00036621 , 0.00003052 ,-0.00027466 ,0.00000000 ,-0.00024414 ,-0.00030518 , -0.00018311 ,-0.00021362 ,-0.00033569 ,-0.00006104 ,-0.00027466 , -0.00033569 ,-0.00021362 ,-0.00006104 ,-0.00042725 ,-0.00042725 , -0.00039673 ,-0.00018311 ,-0.00030518 ,-0.00030518 ,-0.00015259 , -0.00036621 ,-0.00009155 ,-0.00015259 ,-0.00036621 ,-0.00024414 , -0.00061035 ,-0.00015259 ,-0.00024414 ,-0.00018311 ,-0.00009155 , -0.00012207 ,-0.00036621 ,-0.00003052 ,-0.00021362 ,-0.00024414 , -0.00027466 ,-0.00015259 ,-0.00015259 ,-0.00024414 ,-0.00036621 , -0.00024414 ,-0.00061035 ,-0.00009155 ,-0.00048828 ,-0.00045776 , -0.00030518 ,-0.00045776 ,-0.00033569 ,-0.00054932 ,-0.00006104 , -0.00061035 ,-0.00021362 ,-0.00039673 ,-0.00042725 ,-0.00057983 , -0.00070190 ,-0.00018311 ,-0.00039673 ,-0.00018311 ,-0.00030518 , -0.00033569 ,-0.00021362 ,-0.00036621 ,-0.00076294 ,-0.00064087 , -0.00088501 ,-0.00036621 ,-0.00048828 ,-0.00070190 ,-0.00024414 , -0.00048828 ,-0.00012207 ,-0.00051880 ,-0.00051880 ,-0.00073242 , -0.00057983 ,-0.00054932 ,-0.00036621 ,-0.00048828 ,-0.00024414 , -0.00024414 ,-0.00018311 ,-0.00033569 ,-0.00042725 ,-0.00036621 , -0.00051880 ,-0.00045776 ,-0.00039673 ,-0.00030518 ,-0.00036621 , -0.00009155 ,-0.00039673 ,-0.00048828 ,-0.00045776 ,-0.00088501 , -0.00067139 ,-0.00070190 ,-0.00045776 ,-0.00009155 ,0.00000000 , -0.00033569 ,-0.00015259 ,-0.00024414 ,-0.00067139 ,-0.00033569 , -0.00033569 ,-0.00036621 ,-0.00045776 ,-0.00039673 ,-0.00045776 , -0.00051880 ,-0.00067139 ,-0.00085449 ,-0.00039673 ,-0.00079346 , -0.00085449 ,-0.00088501 ,-0.00100708 ,-0.00073242 ,-0.00048828 , -0.00061035 ,-0.00030518 ,-0.00039673 ,-0.00045776 ,-0.00045776 , -0.00061035 ,-0.00067139 ,-0.00061035 ,-0.00064087 ,-0.00085449 , -0.00106812 ,-0.00079346 ,-0.00082397 ,-0.00064087 ,-0.00088501 , -0.00085449 ,-0.00073242 ,-0.00076294 ,-0.00085449 ,-0.00082397 , -0.00057983 ,-0.00091553 ,-0.00051880 ,-0.00051880 ,-0.00045776 , -0.00051880 ,-0.00064087 ,-0.00076294 ,-0.00079346 ,-0.00085449 , -0.00082397 ,-0.00054932 ,-0.00051880 ,-0.00079346 ,-0.00036621 , -0.00057983 ,-0.00042725 ,-0.00012207 ,-0.00027466 ,-0.00033569 , -0.00036621 ,-0.00045776 ,-0.00079346 ,-0.00073242 ,-0.00082397 , -0.00079346 ,-0.00051880 ,-0.00061035 ,-0.00042725 ,-0.00045776 , -0.00030518 ,-0.00015259 ,-0.00051880 ,-0.00036621 ,-0.00051880 , -0.00036621 ,-0.00039673 ,-0.00033569 ,-0.00021362 ,-0.00045776 , -0.00064087 ,-0.00033569 ,-0.00036621 ,-0.00033569 ,-0.00021362 , -0.00015259 ,0.00000000 ,-0.00018311 ,-0.00018311 ,-0.00012207 , -0.00036621 ,0.00009155 ,-0.00036621 ,-0.00030518 ,-0.00036621 , -0.00039673 ,-0.00033569 ,-0.00048828 ,-0.00061035 ,-0.00036621 , -0.00064087 ,-0.00024414 ,-0.00064087 ,-0.00033569 ,-0.00030518 , 0.00006104 ,-0.00009155 ,-0.00012207 ,-0.00039673 ,-0.00015259 , -0.00027466 ,-0.00027466 ,-0.00051880 ,-0.00012207 ,-0.00054932 , -0.00030518 ,-0.00048828 ,-0.00039673 ,-0.00042725 ,-0.00027466 , -0.00021362 ,-0.00009155 ,-0.00021362 ,-0.00027466 ,-0.00048828 , -0.00003052 ,0.00000000 ,-0.00012207 ,0.00024414 ,0.00012207 , 0.00009155 ,-0.00006104 ,-0.00024414 ,-0.00057983 ,-0.00015259 , -0.00027466 ,0.00009155 ,0.00024414 ,0.00003052 ,-0.00006104 , -0.00030518 ,-0.00009155 ,-0.00036621 ,-0.00006104 ,-0.00039673 , -0.00033569 ,-0.00030518 ,-0.00018311 ,-0.00015259 ,-0.00021362 , -0.00003052 ,0.00000000 ,0.00030518 ,0.00000000 ,0.00006104 , -0.00030518 ,0.00000000 ,0.00000000 ,-0.00012207 ,0.00039673 , 0.00012207 ,-0.00009155 ,-0.00012207 ,-0.00015259 ,-0.00003052 , -0.00030518 ,-0.00009155 ,-0.00030518 ,-0.00024414 ,-0.00003052 , -0.00015259 ,0.00030518 ,0.00027466 ,0.00045776 ,0.00012207 , -0.00003052 ,0.00006104 ,0.00024414 ,-0.00021362 ,0.00015259 , -0.00006104 ,0.00027466 ,0.00012207 ,0.00051880 ,0.00045776 , 0.00067139 ,0.00067139 ,0.00039673 ,0.00042725 ,0.00036621 , 0.00009155 ,0.00012207 ,0.00018311 ,0.00018311 ,0.00024414 , -0.00009155 ,0.00042725 ,0.00033569 ,0.00036621 ,0.00003052 , 0.00018311 ,-0.00003052 ,0.00000000 ,0.00015259 ,0.00039673 , 0.00039673 ,0.00061035 ,0.00036621 ,0.00061035 ,0.00042725 , 0.00012207 ,0.00015259 ,0.00009155 ,-0.00003052 ,0.00003052 , 0.00015259 ,0.00000000 ,0.00027466 ,0.00067139 ,0.00036621 , 0.00064087 ,0.00027466 ,0.00024414 ,0.00018311 ,0.00042725 , -0.00012207 ,0.00036621 ,0.00039673 ,0.00009155 ,0.00039673 , 0.00012207 ,0.00045776 ,0.00012207 ,0.00012207 ,0.00042725 , 0.00012207 ,0.00018311 ,0.00045776 ,0.00039673 ,0.00082397 , 0.00024414 ,0.00067139 ,0.00009155 ,0.00036621 ,0.00000000 , -0.00012207 ,0.00012207 ,0.00012207 ,-0.00003052 ,0.00012207 , 0.00042725 ,0.00061035 ,0.00064087 ,0.00024414 ,0.00045776 , 0.00048828 ,0.00067139 ,0.00057983 ,0.00073242 ,0.00039673 , 0.00042725 ,0.00015259 ,0.00012207 ,0.00039673 ,0.00021362 , 0.00018311 ,0.00030518 ,0.00012207 ,0.00003052 ,0.00012207 , 0.00018311 ,0.00036621 ,0.00054932 ,0.00033569 ,0.00039673 , 0.00012207 ,0.00015259 ,0.00006104 ,0.00012207 ,0.00015259 , 0.00006104 ,-0.00015259 ,0.00006104 ,0.00006104 ,0.00021362 , 0.00021362 ,0.00042725 ,0.00018311 ,0.00042725 ,0.00061035 , 0.00067139 ,0.00045776 ,0.00039673 ,0.00030518 ,0.00015259 , -0.00012207 ,0.00018311 ,0.00030518 ,0.00067139 ,0.00051880 , 0.00054932 ,0.00024414 ,0.00024414 ,0.00036621 ,0.00030518 , 0.00045776 ,0.00039673 ,0.00076294 ,0.00073242 ,0.00051880 , 0.00067139 ,0.00070190 ,0.00045776 ,0.00030518 ,0.00021362 , 0.00030518 ,0.00064087 ,0.00042725 ,0.00064087 ,0.00057983 , 0.00036621 ,0.00054932 ,0.00030518 ,0.00048828 ,0.00057983 , 0.00073242 ,0.00070190 ,0.00042725 ,0.00061035 ,0.00042725 , 0.00000000 ,0.00015259 ,0.00033569 ,0.00030518 ,0.00042725 , 0.00048828 ,0.00057983 ,0.00036621 ,-0.00003052 ,0.00045776 , 0.00027466 ,0.00067139 ,0.00054932 ,0.00045776 ,0.00051880 , 0.00061035 ,0.00067139 ,0.00015259 ,0.00018311 ,0.00030518 , 0.00039673 ,0.00015259 ,0.00067139 ,0.00042725 ,0.00048828 , 0.00042725 ,0.00021362 ,0.00021362 ,0.00033569 ,0.00027466 , 0.00030518 ,0.00027466 ,0.00027466 ,0.00024414 ,0.00030518 , 0.00024414 ,0.00027466 ,0.00030518 ,0.00030518 ,0.00018311 , 0.00033569 ,0.00018311 ,0.00030518 ,-0.00003052 ,0.00021362 , 0.00006104 ,0.00021362 ,0.00039673 ,0.00018311 ,0.00012207 , -0.00015259 ,-0.00024414 ,-0.00036621 ,-0.00045776 ,-0.00024414 , -0.00009155 ,0.00021362 ,0.00012207 ,0.00012207 ,-0.00012207 , 0.00006104 ,-0.00030518 ,-0.00015259 ,-0.00018311 ,0.00000000 , -0.00018311 ,-0.00021362 ,-0.00033569 ,-0.00015259 ,-0.00061035 , -0.00048828 ,-0.00030518 ,-0.00018311 ,0.00009155 ,-0.00003052 , -0.00015259 ,-0.00033569 ,-0.00024414 ,-0.00030518 ,-0.00018311 , 0.00009155 ,0.00012207 ,0.00000000 ,0.00006104 ,-0.00054932 , -0.00027466 ,-0.00057983 ,-0.00024414 ,-0.00009155 ,-0.00030518 , -0.00021362 ,0.00012207 ,-0.00018311 ,-0.00073242 ,-0.00024414 , -0.00036621 ,-0.00042725 ,-0.00015259 ,-0.00030518 ,-0.00024414 , -0.00012207 ,-0.00018311 ,-0.00021362 ,-0.00042725 ,-0.00042725 , -0.00024414 ,-0.00015259 ,-0.00033569 ,-0.00042725 ,-0.00036621 , -0.00036621 ,-0.00036621 ,-0.00021362 ,-0.00024414 ,-0.00018311 , -0.00018311 ,-0.00033569 ,-0.00009155 ,-0.00030518 ,-0.00003052 , -0.00024414 ,-0.00027466 ,-0.00021362 ,-0.00018311 ,-0.00045776 , -0.00012207 ,-0.00030518 ,0.00009155 ,-0.00027466 ,-0.00027466 , -0.00024414 ,-0.00015259 ,-0.00027466 ,-0.00015259 ,-0.00057983 , -0.00039673 ,-0.00033569 ,-0.00021362 ,-0.00042725 ,0.00006104 , -0.00009155 ,-0.00006104 ,-0.00003052 ,-0.00048828 ,-0.00057983 , -0.00024414 ,-0.00061035 ,-0.00021362 ,-0.00042725 ,0.00006104 , -0.00012207 ,0.00003052 ,-0.00021362 ,-0.00033569 ,-0.00039673 , -0.00067139 ,-0.00039673 ,-0.00054932 ,-0.00039673 ,-0.00079346 , -0.00045776 ,-0.00036621 ,-0.00033569 ,-0.00018311 ,-0.00045776 , -0.00045776 ,-0.00064087 ,-0.00048828 ,-0.00048828 ,-0.00045776 , -0.00036621 ,-0.00018311 ,-0.00021362 ,-0.00039673 ,-0.00012207 , -0.00048828 ,-0.00030518 ,-0.00039673 ,-0.00054932 ,-0.00030518 , -0.00045776 ,-0.00048828 ,-0.00051880 ,-0.00030518 ,-0.00033569 , -0.00030518 ,-0.00042725 ,-0.00030518 ,-0.00036621 ,-0.00036621 , -0.00042725 ,-0.00036621 ,-0.00045776 ,-0.00036621 ,-0.00039673 , -0.00042725 ,0.00000000 ,-0.00024414 ,-0.00009155 ,-0.00027466 , -0.00015259 ,0.00006104 ,-0.00021362 ,-0.00036621 ,-0.00012207 , -0.00082397 ,-0.00045776 ,-0.00057983 ,-0.00042725 ,-0.00030518 , -0.00045776 ,-0.00021362 ,-0.00054932 ,-0.00064087 ,-0.00076294 , -0.00054932 ,-0.00018311 ,-0.00021362 ,0.00003052 ,-0.00012207 , -0.00009155 ,-0.00039673 ,0.00009155 ,-0.00033569 ,-0.00021362 , -0.00045776 ,-0.00042725 ,-0.00033569 ,-0.00030518 ,-0.00018311 , -0.00042725 ,-0.00057983 ,-0.00033569 ,-0.00073242 ,-0.00048828 , -0.00021362 ,-0.00033569 ,-0.00051880 ,-0.00070190 ,-0.00018311 , -0.00064087 ,-0.00076294 ,-0.00030518 ,-0.00054932 ,-0.00009155 , -0.00064087 ,-0.00030518 ,-0.00091553 ,-0.00076294 ,-0.00051880 , -0.00051880 ,-0.00042725 ,-0.00027466 ,-0.00039673 ,-0.00030518 , -0.00051880 ,-0.00073242 ,-0.00057983 ,-0.00054932 ,-0.00079346 , -0.00070190 ,-0.00085449 ,-0.00054932 ,-0.00054932 ,-0.00073242 , -0.00067139 ,-0.00094604 ,-0.00076294 ,-0.00079346 ,-0.00057983 , -0.00064087 ,-0.00088501 ,-0.00067139 ,-0.00085449 ,-0.00079346 , -0.00054932 ,-0.00064087 ,-0.00045776 ,-0.00057983 ,-0.00073242 , -0.00094604 ,-0.00079346 ,-0.00094604 ,-0.00076294 ,-0.00076294 , -0.00076294 ,-0.00054932 ,-0.00021362 ,-0.00064087 ,-0.00061035 , -0.00097656 ,-0.00091553 ,-0.00085449 ,-0.00100708 ,-0.00067139 , -0.00057983 ,-0.00036621 ,-0.00048828 ,-0.00076294 ,-0.00048828 , -0.00128174 ,-0.00079346 ,-0.00076294 ,-0.00067139 ,-0.00033569 , -0.00061035 ,-0.00064087 ,-0.00051880 ,-0.00057983 ,-0.00064087 , -0.00048828 ,-0.00054932 ,-0.00048828 ,-0.00036621 ,-0.00091553 , -0.00057983 ,-0.00079346 ,-0.00103760 ,-0.00021362 ,-0.00067139 , -0.00036621 ,-0.00061035 ,-0.00018311 ,-0.00054932 ,-0.00064087 , -0.00067139 ,-0.00033569 ,-0.00054932 ,-0.00015259 ,-0.00048828 , -0.00012207 ,-0.00024414 ,-0.00051880 ,-0.00064087 ,-0.00048828 , -0.00018311 ,-0.00036621 ,-0.00042725 ,-0.00039673 ,-0.00018311 , -0.00024414 ,-0.00018311 ,-0.00079346 ,-0.00006104 ,-0.00024414 , -0.00003052 ,-0.00061035 ,-0.00024414 ,-0.00030518 ,0.00024414 , -0.00033569 ,-0.00033569 ,-0.00006104 ,-0.00018311 ,-0.00018311 , -0.00003052 ,0.00012207 ,-0.00006104 ,-0.00033569 ,-0.00045776 , -0.00045776 ,-0.00009155 ,-0.00033569 ,-0.00009155 ,-0.00021362 , -0.00030518 ,-0.00039673 ,-0.00039673 ,-0.00036621 ,-0.00027466 , -0.00009155 ,-0.00021362 ,-0.00003052 ,-0.00012207 ,-0.00021362 , -0.00021362 ,-0.00015259 ,-0.00042725 ,-0.00051880 ,-0.00054932 , -0.00006104 ,-0.00003052 ,-0.00015259 ,-0.00003052 ,-0.00039673 , -0.00045776 ,-0.00039673 ,-0.00030518 ,-0.00018311 ,-0.00027466 , -0.00018311 ,-0.00021362 ,-0.00012207 ,-0.00067139 ,-0.00048828 , -0.00039673 ,-0.00048828 ,-0.00045776 ,-0.00061035 ,-0.00036621 , -0.00021362 ,-0.00033569 ,-0.00024414 ,-0.00051880 ,-0.00027466 , -0.00057983 ,-0.00033569 ,-0.00006104 ,-0.00033569 ,-0.00024414 , -0.00039673 ,-0.00018311 ,-0.00036621 ,-0.00018311 ,-0.00030518 , -0.00039673 ,-0.00051880 ,-0.00045776 ,-0.00048828 ,-0.00061035 , -0.00048828 ,-0.00057983 ,-0.00057983 ,-0.00042725 ,-0.00045776 , -0.00027466 ,-0.00048828 ,-0.00048828 ,-0.00027466 ,-0.00027466 , -0.00042725 ,-0.00039673 ,-0.00054932 ,-0.00039673 ,-0.00036621 , -0.00064087 ,-0.00076294 ,-0.00027466 ,-0.00064087 ,-0.00048828 , -0.00048828 ,-0.00051880 ,-0.00033569 ,-0.00045776 ,-0.00033569 , -0.00048828 ,-0.00045776 ,-0.00030518 ,-0.00006104 ,-0.00024414 , -0.00033569 ,-0.00024414 ,-0.00024414 ,-0.00027466 ,-0.00018311 , -0.00030518 ,-0.00015259 ,0.00000000 ,-0.00003052 ,0.00000000 , -0.00012207 ,-0.00027466 ,-0.00039673 ,-0.00033569 ,-0.00036621 , -0.00033569 ,-0.00042725 ,-0.00012207 ,-0.00018311 ,-0.00009155 , -0.00054932 ,-0.00030518 ,-0.00048828 ,-0.00033569 ,-0.00033569 , -0.00021362 ,-0.00024414 ,0.00000000 ,0.00000000 ,-0.00006104 , -0.00003052 ,0.00006104 ,0.00000000 ,-0.00030518 ,0.00000000 , -0.00021362 ,-0.00006104 ,-0.00009155 ,-0.00018311 ,-0.00024414 , -0.00003052 ,-0.00015259 ,-0.00024414 ,-0.00012207 ,-0.00006104 , -0.00027466 ,0.00018311 ,0.00027466 ,0.00000000 ,-0.00021362 , 0.00009155 ,0.00000000 ,0.00033569 ,0.00030518 ,0.00039673 , 0.00009155 ,0.00006104 ,0.00009155 ,0.00012207 ,-0.00006104 , 0.00021362 ,0.00021362 ,0.00009155 ,0.00036621 ,0.00009155 , 0.00033569 ,-0.00003052 ,0.00030518 ,0.00024414 ,0.00039673 , 0.00012207 ,0.00033569 ,-0.00012207 ,0.00018311 ,0.00009155 , -0.00018311 ,0.00003052 ,0.00057983 ,0.00012207 ,0.00021362 , 0.00015259 ,0.00042725 ,0.00000000 ,0.00018311 ,0.00027466 , 0.00009155 ,0.00012207 ,0.00039673 ,0.00003052 ,0.00012207 , 0.00006104 ,0.00000000 ,0.00006104 ,-0.00003052 ,0.00006104 , 0.00000000 ,0.00018311 ,-0.00003052 ,0.00027466 ,0.00018311 , -0.00012207 ,-0.00003052 ,-0.00024414 ,0.00015259 ,0.00021362 , 0.00003052 ,0.00003052 ,0.00000000 ,0.00012207 ,-0.00027466 , 0.00018311 ,0.00009155 ,0.00033569 ,0.00024414 ,0.00006104 , -0.00012207 ,0.00033569 ,0.00006104 ,0.00003052 ,-0.00021362 , 0.00000000 ,-0.00009155 ,0.00015259 ,0.00009155 ,-0.00012207 , -0.00015259 ,-0.00036621 ,-0.00012207 ,-0.00009155 ,-0.00003052 , 0.00003052 ,0.00000000 ,0.00012207 ,-0.00009155 ,-0.00009155 , -0.00015259 ,0.00003052 ,0.00009155 ,0.00021362 ,-0.00009155 , -0.00024414 ,-0.00045776 ,-0.00015259 ,-0.00036621 ,-0.00012207 , -0.00015259 ,-0.00003052 ,0.00018311 ,-0.00039673 ,0.00009155 , -0.00003052 ,-0.00033569 ,-0.00033569 ,-0.00015259 ,0.00021362 , -0.00003052 ,0.00015259 ,-0.00015259 ,0.00021362 ,-0.00042725 , -0.00015259 ,-0.00033569 ,-0.00018311 ,-0.00027466 ,-0.00024414 , 0.00012207 ,-0.00030518 ,-0.00045776 ,-0.00061035 ,-0.00051880 , -0.00030518 ,-0.00045776 ,-0.00030518 ,-0.00015259 ,-0.00006104 , -0.00027466 ,0.00000000 ,-0.00067139 ,-0.00036621 ,-0.00054932 , -0.00024414 ,-0.00003052 ,0.00021362 ,0.00018311 ,0.00000000 , -0.00051880 ,-0.00039673 ,-0.00033569 ,-0.00045776 ,-0.00033569 , -0.00030518 ,-0.00030518 ,-0.00033569 ,-0.00033569 ,-0.00048828 , -0.00051880 ,-0.00042725 ,-0.00027466 ,-0.00036621 ,-0.00021362 , -0.00021362 ,0.00000000 ,-0.00045776 ,-0.00036621 ,-0.00012207 , -0.00039673 ,-0.00009155 ,-0.00057983 ,0.00003052 ,-0.00039673 , -0.00027466 ,-0.00030518 ,-0.00021362 ,-0.00027466 ,-0.00015259 , -0.00030518 ,-0.00003052 ,-0.00021362 ,0.00003052 ,-0.00015259 , -0.00003052 ,-0.00033569 ,-0.00030518 ,-0.00009155 ,-0.00027466 , 0.00009155 ,-0.00030518 ,-0.00048828 ,-0.00070190 ,-0.00009155 , -0.00021362 ,-0.00021362 ,-0.00012207 ,-0.00021362 ,-0.00027466 , -0.00024414 ,-0.00045776 ,-0.00033569 ,-0.00033569 ,-0.00012207 , -0.00048828 ,0.00006104 ,-0.00042725 ,0.00003052 ,-0.00015259 , -0.00012207 ,-0.00030518 ,-0.00009155 ,-0.00021362 ,-0.00012207 , -0.00009155 ,0.00000000 ,-0.00006104 ,-0.00045776 ,-0.00030518 , -0.00021362 ,-0.00036621 ,-0.00012207 ,-0.00015259 ,-0.00003052 , -0.00003052 ,-0.00036621 ,-0.00039673 ,-0.00033569 ,-0.00039673 , -0.00024414 ,-0.00033569 ,0.00021362 ,-0.00030518 ,0.00003052 , -0.00012207 ,-0.00012207 ,-0.00036621 ,-0.00003052 ,-0.00012207 , 0.00000000 ,-0.00015259 ,-0.00012207 ,-0.00021362 ,-0.00003052 , -0.00018311 ,-0.00033569 ,-0.00024414 ,0.00003052 ,0.00003052 , -0.00033569 ,-0.00021362 ,-0.00018311 ,-0.00009155 ,-0.00033569 , -0.00027466 ,-0.00006104 ,-0.00021362 ,-0.00009155 ,-0.00021362 , -0.00012207 ,-0.00015259 ,-0.00018311 ,-0.00012207 ,-0.00024414 , 0.00024414 ,-0.00018311 ,0.00009155 ,-0.00003052 ,-0.00030518 , -0.00036621 ,-0.00009155 ,-0.00009155 ,-0.00030518 ,-0.00030518 , -0.00015259 ,-0.00024414 ,-0.00024414 ,-0.00039673 ,-0.00015259 , -0.00024414 ,-0.00033569 ,-0.00027466 ,-0.00024414 ,-0.00018311 , -0.00030518 ,-0.00021362 ,0.00003052 ,-0.00027466 ,-0.00021362 , -0.00012207 ,-0.00024414 ,-0.00027466 ,-0.00036621 ,-0.00027466 , -0.00036621 ,-0.00054932 ,-0.00039673 ,-0.00042725 ,-0.00024414 , -0.00057983 ,-0.00024414 ,-0.00039673 ,-0.00030518 ,-0.00048828 , -0.00045776 ,-0.00057983 ,-0.00045776 ,-0.00054932 ,-0.00036621 , -0.00021362 ,-0.00033569 ,0.00009155 ,-0.00054932 ,-0.00012207 , -0.00057983 ,-0.00048828 ,-0.00045776 ,0.00000000 ,-0.00045776 , -0.00012207 ,-0.00012207 ,-0.00054932 ,-0.00057983 ,-0.00024414 , -0.00057983 ,-0.00061035 ,-0.00015259 ,-0.00042725 ,-0.00024414 , -0.00051880 ,-0.00024414 ,-0.00021362 ,-0.00018311 ,-0.00015259 , -0.00030518 ,-0.00012207 ,-0.00015259 ,-0.00003052 ,-0.00027466 , -0.00021362 ,-0.00045776 ,-0.00048828 ,-0.00033569 ,-0.00036621 , 0.00009155 ,0.00012207 ,-0.00003052 ,-0.00015259 ,-0.00018311 , -0.00030518 ,-0.00054932 ,-0.00033569 ,-0.00039673 ,-0.00015259 , -0.00061035 ,-0.00021362 ,-0.00064087 ,-0.00015259 ,-0.00033569 , -0.00033569 ,-0.00033569 ,-0.00027466 ,-0.00036621 ,-0.00015259 , -0.00003052 ,-0.00027466 ,-0.00051880 ,-0.00061035 ,-0.00057983 , -0.00018311 ,-0.00033569 ,-0.00027466 ,-0.00030518 ,-0.00070190 , -0.00054932 ,-0.00048828 ,-0.00048828 ,-0.00018311 ,-0.00009155 , -0.00012207 ,-0.00003052 ,-0.00048828 ,-0.00042725 ,-0.00051880 , -0.00033569 ,-0.00051880 ,-0.00021362 ,-0.00039673 ,-0.00021362 , -0.00021362 ,-0.00073242 ,-0.00036621 ,-0.00036621 ,-0.00036621 , -0.00039673 ,-0.00030518 ,-0.00009155 ,-0.00012207 ,-0.00030518 , -0.00045776 ,-0.00054932 ,-0.00042725 ,-0.00064087 ,-0.00054932 , -0.00042725 ,-0.00033569 ,-0.00042725 ,-0.00064087 ,-0.00079346 , -0.00045776 ,-0.00027466 ,-0.00030518 ,-0.00042725 ,-0.00003052 , -0.00018311 ,-0.00021362 ,-0.00006104 ,-0.00057983 ,-0.00045776 , -0.00030518 ,-0.00045776 ,-0.00067139 ,-0.00067139 ,-0.00039673 , -0.00045776 ,-0.00045776 ,-0.00039673 ,-0.00039673 ,-0.00036621 , -0.00009155 ,-0.00018311 ,-0.00036621 ,-0.00061035 ,-0.00051880 , -0.00045776 ,-0.00006104 ,-0.00057983 ,-0.00064087 ,-0.00048828 , -0.00036621 ,-0.00061035 ,-0.00042725 ,-0.00036621 ,-0.00015259 , -0.00027466 ,0.00000000 ,0.00012207 ,-0.00006104 ,-0.00036621 , -0.00042725 ,-0.00054932 ,-0.00045776 ,-0.00045776 ,-0.00033569 , -0.00039673 ,-0.00009155 ,-0.00085449 ,-0.00042725 ,-0.00061035 , -0.00033569 ,-0.00030518 ,-0.00006104 ,-0.00012207 ,0.00012207 , -0.00024414 ,-0.00015259 ,-0.00036621 ,-0.00042725 ,0.00000000 , -0.00003052 ,0.00009155 ,-0.00018311 ,-0.00027466 ,-0.00024414 , -0.00027466 ,0.00030518 ,0.00000000 ,0.00000000 ,-0.00003052 , -0.00024414 ,-0.00012207 ,-0.00012207 ,0.00012207 ,-0.00012207 , 0.00000000 ,0.00012207 ,0.00036621 ,0.00039673 ,-0.00003052 , 0.00003052 ,-0.00027466 ,-0.00030518 ,-0.00018311 ,0.00024414 , 0.00048828 ,0.00021362 ,0.00036621 ,0.00030518 ,0.00018311 , -0.00015259 ,0.00006104 ,0.00027466 ,0.00003052 ,0.00012207 , 0.00027466 ,0.00024414 ,-0.00024414 ,-0.00006104 ,0.00018311 , 0.00024414 ,0.00009155 ,-0.00009155 ,0.00009155 ,0.00024414 , 0.00045776 ,0.00009155 ,0.00009155 ,-0.00012207 ,0.00003052 , 0.00024414 ,0.00018311 ,0.00018311 ,0.00000000 ,-0.00015259 , 0.00003052 ,-0.00009155 ,0.00027466 ,0.00042725 ,0.00024414 , 0.00048828 ,-0.00015259 ,0.00018311 ,0.00006104 ,0.00000000 , 0.00012207 ,0.00012207 ,-0.00012207 ,-0.00015259 ,0.00000000 , -0.00015259 ,-0.00027466 ,-0.00036621 ,-0.00033569 ,0.00000000 , 0.00006104 ,0.00021362 ,0.00030518 ,0.00006104 ,0.00024414 , 0.00000000 ,0.00000000 ,-0.00021362 ,0.00036621 ,0.00009155 , 0.00006104 ,0.00009155 ,0.00012207 ,0.00003052 ,-0.00021362 , -0.00006104 ,0.00006104 ,0.00009155 ,0.00024414 ,0.00030518 , 0.00018311 ,0.00006104 ,0.00006104 ,-0.00006104 ,0.00000000 , -0.00012207 ,-0.00012207 ,-0.00009155 ,0.00030518 ,0.00015259 , 0.00015259 ,0.00009155 ,0.00006104 ,-0.00009155 ,-0.00009155 , 0.00030518 ,0.00021362 ,0.00036621 ,0.00000000 ,0.00003052 , -0.00012207 ,-0.00006104 ,-0.00018311 ,0.00018311 ,0.00006104 , 0.00033569 ,0.00036621 ,0.00003052 ,-0.00006104 ,-0.00015259 , -0.00024414 ,-0.00033569 ,0.00012207 ,0.00015259 ,0.00021362 , 0.00057983 ,0.00003052 ,-0.00003052 ,-0.00042725 ,-0.00033569 , -0.00021362 ,0.00030518 ,0.00021362 ,0.00051880 ,-0.00006104 , -0.00006104 ,-0.00012207 ,-0.00009155 ,0.00048828 ,0.00021362 , 0.00042725 ,0.00027466 ,0.00027466 ,-0.00033569 ,0.00015259 , -0.00030518 ,-0.00018311 ,-0.00003052 ,0.00057983 ,0.00051880 , 0.00064087 ,0.00024414 ,-0.00030518 ,-0.00057983 ,-0.00024414 , -0.00009155 ,0.00012207 ,0.00036621 ,0.00030518 ,0.00012207 , 0.00009155 ,-0.00018311 ,-0.00030518 ,-0.00012207 ,0.00003052 , 0.00009155 ,0.00033569 ,0.00021362 ,-0.00027466 ,-0.00003052 , -0.00021362 ,-0.00006104 ,0.00000000 ,-0.00003052 ,0.00021362 , -0.00012207 ,-0.00003052 ,0.00000000 ,-0.00027466 ,-0.00027466 , 0.00015259 ,0.00006104 ,0.00006104 ,0.00009155 ,0.00000000 , -0.00012207 ,-0.00006104 ,-0.00021362 ,0.00003052 ,-0.00018311 , 0.00018311 ,0.00015259 ,0.00000000 ,0.00015259 ,-0.00036621 , -0.00042725 ,-0.00051880 ,-0.00048828 ,-0.00021362 ,0.00003052 , -0.00024414 ,-0.00006104 ,-0.00012207 ,-0.00030518 ,-0.00027466 , -0.00073242 ,-0.00048828 ,-0.00054932 ,-0.00051880 ,-0.00042725 , -0.00024414 ,-0.00061035 ,-0.00024414 ,-0.00030518 ,-0.00012207 , -0.00030518 ,-0.00015259 ,-0.00027466 ,-0.00003052 ,-0.00039673 , -0.00030518 ,-0.00051880 ,-0.00027466 ,-0.00070190 ,-0.00051880 , -0.00079346 ,-0.00030518 ,-0.00018311 ,-0.00015259 ,-0.00009155 , -0.00009155 ,-0.00012207 ,-0.00030518 ,-0.00021362 ,-0.00033569 , -0.00021362 ,-0.00012207 ,-0.00051880 ,-0.00012207 ,-0.00027466 , -0.00009155 ,-0.00030518 ,-0.00021362 ,-0.00054932 ,-0.00027466 , -0.00018311 ,-0.00033569 ,-0.00057983 ,0.00000000 ,-0.00045776 , -0.00030518 ,-0.00006104 ,-0.00036621 ,-0.00051880 ,-0.00027466 , -0.00033569 ,-0.00048828 ,0.00000000 ,0.00000000 ,-0.00015259 , -0.00009155 ,-0.00003052 ,-0.00015259 ,-0.00027466 ,-0.00042725 , -0.00057983 ,-0.00030518 ,-0.00012207 ,-0.00006104 ,0.00012207 , -0.00036621 ,-0.00012207 ,-0.00030518 ,-0.00027466 ,0.00006104 , -0.00030518 ,-0.00021362 ,0.00003052 ,-0.00057983 ,-0.00018311 , -0.00045776 ,-0.00039673 ,-0.00027466 ,0.00000000 ,-0.00009155 , 0.00000000 ,-0.00006104 ,-0.00009155 ,-0.00006104 ,-0.00009155 , -0.00024414 ,-0.00015259 ,-0.00018311 ,-0.00015259 ,-0.00006104 , -0.00012207 ,-0.00009155 ,-0.00012207 ,-0.00018311 ,-0.00006104 , -0.00024414 ,-0.00012207 ,0.00006104 ,-0.00012207 ,-0.00021362 , -0.00045776 ,-0.00033569 ,-0.00039673 ,-0.00009155 ,-0.00048828 , -0.00021362 ,-0.00033569 ,-0.00009155 ,0.00003052 ,-0.00024414 , -0.00015259 ,-0.00018311 ,-0.00012207 ,-0.00009155 ,-0.00054932 , -0.00018311 ,-0.00027466 ,-0.00021362 ,-0.00021362 ,-0.00009155 , -0.00012207 ,-0.00003052 ,-0.00027466 ,-0.00021362 ,-0.00030518 , -0.00030518 ,-0.00021362 ,-0.00012207 ,0.00000000 ,-0.00009155 , -0.00006104 ,-0.00027466 ,-0.00021362 ,-0.00042725 ,-0.00051880 , -0.00030518 ,-0.00036621 ,-0.00015259 ,-0.00027466 ,-0.00009155 , -0.00018311 ,-0.00024414 ,-0.00027466 ,-0.00039673 ,-0.00030518 , -0.00027466 ,-0.00006104 ,-0.00009155 ,-0.00033569 ,-0.00015259 , -0.00018311 ,-0.00018311 ,-0.00039673 ,-0.00018311 ,-0.00042725 , -0.00015259 ,-0.00024414 ,-0.00027466 ,-0.00015259 ,-0.00024414 , 0.00000000 ,-0.00024414 ,-0.00033569 ,-0.00006104 ,-0.00006104 , 0.00000000 ,-0.00015259 ,-0.00036621 ,-0.00021362 ,-0.00039673 , -0.00024414 ,-0.00042725 ,-0.00027466 ,-0.00045776 ,-0.00018311 , -0.00045776 ,-0.00027466 ,-0.00067139 ,-0.00024414 ,-0.00054932 , -0.00045776 ,-0.00033569 ,-0.00027466 ,-0.00039673 ,-0.00030518 , -0.00067139 ,-0.00064087 ,-0.00048828 ,-0.00045776 ,-0.00070190 , -0.00054932 ,-0.00051880 ,-0.00039673 ,-0.00051880 ,-0.00021362 , -0.00057983 ,-0.00064087 ,-0.00051880 ,-0.00070190 ,-0.00036621 , -0.00054932 ,-0.00051880 ,-0.00051880 ,-0.00070190 ,-0.00061035 , -0.00061035 ,-0.00061035 ,-0.00051880 ,-0.00067139 ,-0.00027466 , -0.00036621 ,-0.00073242 ,-0.00036621 ,-0.00048828 ,-0.00021362 , -0.00051880 ,-0.00042725 ,-0.00042725 ,-0.00036621 ,-0.00033569 , -0.00061035 ,-0.00042725 ,-0.00057983 ,-0.00027466 ,-0.00064087 , -0.00021362 ,-0.00070190 ,-0.00070190 ,-0.00082397 ,-0.00054932 , -0.00088501 ,-0.00027466 ,-0.00070190 ,-0.00021362 ,-0.00064087 , -0.00030518 ,-0.00039673 ,-0.00036621 ,-0.00061035 ,-0.00045776 , -0.00061035 ,-0.00073242 ,-0.00039673 ,-0.00018311 ,-0.00048828 , -0.00009155 ,-0.00057983 ,-0.00045776 ,-0.00067139 ,-0.00051880 , -0.00057983 ,-0.00030518 ,-0.00054932 ,-0.00030518 ,-0.00057983 , -0.00045776 ,-0.00051880 ,-0.00024414 ,-0.00033569 ,-0.00042725 , -0.00027466 ,-0.00033569 ,-0.00033569 ,-0.00042725 ,-0.00024414 , -0.00024414 ,-0.00021362 ,-0.00039673 ,-0.00027466 ,-0.00030518 , -0.00027466 ,-0.00033569 ,-0.00021362 ,-0.00054932 ,-0.00015259 , -0.00033569 ,-0.00039673 ,-0.00006104 ,-0.00033569 ,-0.00015259 , 0.00009155 ,0.00006104 ,0.00024414 ,0.00000000 ,-0.00045776 , -0.00003052 ,-0.00021362 ,-0.00015259 ,-0.00021362 ,-0.00030518 , 0.00012207 ,-0.00024414 ,0.00012207 ,-0.00006104 ,0.00012207 , 0.00003052 ,0.00012207 ,-0.00015259 ,0.00003052 ,-0.00003052 , -0.00021362 ,0.00033569 ,0.00030518 ,0.00000000 ,0.00024414 , -0.00009155 ,0.00006104 ,-0.00018311 ,0.00012207 ,0.00003052 , 0.00021362 ,0.00006104 ,0.00012207 ,-0.00009155 ,0.00024414 , 0.00003052 ,-0.00003052 ,0.00015259 ,-0.00003052 ,-0.00021362 , 0.00024414 ,0.00012207 ,0.00021362 ,-0.00030518 ,-0.00003052 , -0.00024414 ,-0.00006104 ,0.00003052 ,-0.00003052 ,0.00003052 , 0.00027466 ,0.00033569 ,-0.00048828 ,0.00018311 ,-0.00039673 , -0.00012207 ,0.00003052 ,0.00006104 ,-0.00015259 ,0.00006104 , -0.00024414 ,-0.00006104 ,-0.00012207 ,0.00009155 ,-0.00021362 , -0.00006104 ,0.00006104 ,-0.00006104 ,-0.00009155 ,-0.00018311 , 0.00006104 ,0.00021362 ,0.00003052 ,-0.00003052 ,-0.00015259 , -0.00021362 ,-0.00048828 ,-0.00030518 ,0.00003052 ,-0.00018311 , -0.00009155 ,-0.00012207 ,-0.00018311 ,-0.00024414 ,-0.00039673 , -0.00021362 ,-0.00027466 ,0.00006104 ,-0.00024414 ,-0.00012207 , 0.00012207 ,-0.00012207 ,-0.00018311 ,-0.00012207 ,-0.00051880 , -0.00036621 ,-0.00027466 ,-0.00015259 ,-0.00015259 ,-0.00003052 , 0.00009155 ,0.00012207 ,-0.00006104 ,0.00012207 ,-0.00024414 , -0.00027466 ,-0.00006104 ,0.00003052 ,0.00018311 ,0.00015259 , 0.00018311 ,-0.00009155 ,0.00021362 ,-0.00048828 ,0.00030518 , -0.00012207 ,0.00024414 ,0.00015259 ,0.00027466 ,0.00021362 , 0.00006104 ,0.00024414 ,0.00039673 ,0.00009155 ,0.00027466 , -0.00009155 ,-0.00009155 ,0.00015259 ,-0.00024414 ,0.00024414 , 0.00042725 ,0.00015259 ,0.00030518 ,0.00030518 ,0.00003052 , 0.00054932 ,0.00006104 ,0.00042725 ,0.00009155 ,0.00048828 , 0.00042725 ,0.00042725 ,0.00030518 ,-0.00003052 ,0.00024414 , 0.00051880 ,0.00018311 ,0.00033569 ,0.00045776 ,0.00018311 , 0.00067139 ,0.00015259 ,0.00036621 ,0.00048828 ,0.00070190 , 0.00061035 ,0.00033569 ,0.00033569 ,0.00088501 ,0.00012207 , 0.00048828 ,0.00015259 ,0.00003052 ,0.00009155 ,0.00042725 , 0.00033569 ,0.00042725 ,0.00009155 ,0.00048828 ,0.00033569 , 0.00064087 ,0.00042725 ,0.00045776 ,0.00057983 ,0.00021362 , 0.00045776 ,0.00006104 ,0.00036621 ,0.00000000 ,0.00015259 , 0.00006104 ,0.00061035 ,0.00033569 ,0.00018311 ,0.00033569 , 0.00006104 ,0.00015259 ,0.00018311 ,0.00027466 ,0.00024414 , 0.00030518 ,0.00030518 ,0.00036621 ,0.00015259 ,0.00006104 , 0.00033569 ,0.00000000 ,0.00012207 ,0.00003052 ,0.00018311 , 0.00024414 ,0.00048828 ,0.00045776 ,0.00033569 ,-0.00006104 , 0.00027466 ,0.00009155 ,0.00027466 ,0.00036621 ,-0.00003052 , 0.00021362 ,-0.00030518 ,0.00018311 ,-0.00003052 ,0.00003052 , 0.00033569 ,-0.00006104 ,0.00021362 ,0.00009155 ,0.00036621 , -0.00006104 ,0.00027466 ,0.00009155 ,0.00021362 ,-0.00018311 , -0.00006104 ,-0.00006104 ,-0.00027466 ,-0.00003052 ,0.00012207 , 0.00021362 ,0.00009155 ,0.00018311 ,0.00006104 ,0.00006104 , 0.00018311 ,0.00021362 ,0.00009155 ,0.00009155 ,0.00015259 , 0.00012207 ,0.00003052 ,0.00003052 ,0.00006104 ,0.00012207 , 0.00009155 ,-0.00012207 ,0.00036621 ,0.00003052 ,0.00003052 , 0.00009155 ,0.00012207 ,-0.00006104 ,-0.00003052 ,-0.00027466 , -0.00015259 ,-0.00003052 ,0.00012207 ,0.00021362 ,0.00015259 , 0.00021362 ,-0.00006104 ,-0.00018311 ,-0.00012207 ,-0.00012207 , 0.00021362 ,0.00015259 ,0.00027466 ,0.00015259 ,0.00036621 , -0.00009155 ,0.00012207 ,-0.00015259 ,0.00003052 ,0.00009155 , -0.00009155 ,0.00024414 ,0.00000000 ,0.00009155 ,-0.00021362 , 0.00000000 ,0.00009155 ,-0.00015259 ,0.00003052 ,-0.00006104 , 0.00006104 ,-0.00018311 ,-0.00021362 ,-0.00024414 ,-0.00039673 , -0.00009155 ,0.00006104 ,0.00000000 ,0.00015259 ,-0.00024414 , -0.00027466 ,-0.00021362 ,-0.00015259 ,-0.00030518 ,0.00003052 , -0.00015259 ,0.00006104 ,0.00009155 ,0.00006104 ,-0.00006104 , -0.00027466 ,0.00015259 ,-0.00042725 ,-0.00024414 ,-0.00030518 , -0.00015259 ,-0.00003052 ,0.00006104 ,0.00012207 ,-0.00009155 , -0.00006104 ,-0.00012207 ,0.00006104 ,0.00015259 ,0.00033569 , 0.00009155 ,-0.00018311 ,-0.00015259 ,-0.00030518 ,-0.00018311 , -0.00030518 ,-0.00015259 ,-0.00006104 ,0.00015259 ,0.00018311 , 0.00015259 ,-0.00015259 ,-0.00003052 ,-0.00024414 ,0.00006104 , 0.00009155 ,0.00033569 ,0.00027466 ,0.00018311 ,0.00027466 , 0.00003052 ,0.00000000 ,-0.00009155 ,0.00000000 ,-0.00003052 , -0.00006104 ,0.00045776 ,0.00045776 ,0.00030518 ,0.00015259 , 0.00036621 ,0.00003052 ,0.00012207 ,0.00030518 ,0.00012207 , 0.00024414 ,0.00021362 ,0.00033569 ,0.00015259 ,0.00018311 , 0.00030518 ,0.00015259 ,0.00051880 ,0.00015259 ,0.00015259 , 0.00015259 ,-0.00021362 ,0.00027466 ,0.00024414 ,0.00039673 , 0.00003052 ,0.00030518 ,0.00027466 ,0.00006104 ,0.00033569 , 0.00021362 ,0.00003052 ,0.00003052 ,0.00021362 ,0.00051880 , 0.00048828 ,0.00033569 ,0.00033569 ,0.00051880 ,0.00048828 , 0.00027466 ,0.00036621 ,-0.00003052 ,0.00061035 ,0.00027466 , 0.00048828 ,0.00042725 ,0.00006104 ,0.00045776 ,0.00012207 , 0.00021362 ,0.00021362 ,0.00048828 ,0.00027466 ,0.00018311 , 0.00042725 ,0.00039673 ,0.00021362 ,0.00048828 ,0.00012207 , 0.00036621 ,0.00033569 ,0.00006104 ,0.00018311 ,-0.00018311 , 0.00006104 ,-0.00006104 ,0.00021362 ,0.00024414 ,-0.00018311 , 0.00027466 ,-0.00006104 ,0.00027466 ,0.00006104 ,-0.00030518 , 0.00012207 ,-0.00012207 ,-0.00018311 ,-0.00003052 ,0.00003052 , -0.00012207 ,-0.00006104 ,-0.00009155 ,0.00051880 ,0.00003052 , 0.00018311 ,0.00030518 ,0.00009155 ,0.00036621 ,0.00030518 , -0.00033569 ,0.00000000 ,0.00000000 ,0.00003052 ,-0.00006104 , 0.00000000 ,-0.00003052 ,0.00000000 ,-0.00021362 ,-0.00048828 , 0.00003052 ,-0.00009155 ,0.00009155 ,0.00000000 ,-0.00003052 , -0.00003052 ,-0.00018311 ,-0.00015259 ,0.00009155 ,-0.00018311 , -0.00021362 ,0.00027466 ,-0.00012207 ,0.00021362 ,-0.00018311 , 0.00012207 ,0.00000000 ,-0.00018311 ,0.00000000 ,0.00000000 , 0.00000000 ,0.00003052 ,-0.00015259 ,-0.00006104 ,-0.00021362 , -0.00006104 ,-0.00009155 ,0.00000000 ,-0.00018311 ,-0.00012207 , -0.00009155 ,-0.00015259 ,0.00027466 ,0.00009155 ,0.00039673 , 0.00000000 ,-0.00012207 ,-0.00012207 ,0.00021362 ,-0.00003052 , 0.00045776 ,-0.00006104 ,0.00015259 ,0.00006104 ,-0.00015259 , -0.00012207 ,0.00015259 ,0.00015259 ,0.00006104 ,-0.00012207 , 0.00024414 ,-0.00018311 ,0.00009155 ,0.00003052 ,0.00024414 , 0.00024414 ,0.00006104 ,0.00039673 ,-0.00015259 ,0.00018311 , 0.00006104 ,0.00000000 ,-0.00030518 ,-0.00042725 ,-0.00015259 , 0.00015259 ,0.00009155 ,0.00024414 ,0.00000000 ,0.00045776 , 0.00012207 ,0.00024414 ,0.00036621 ,0.00024414 ,0.00027466 , -0.00009155 ,0.00030518 ,-0.00003052 ,0.00012207 ,0.00024414 , 0.00015259 ,0.00021362 ,0.00015259 ,0.00012207 ,0.00027466 , 0.00024414 ,0.00012207 ,0.00015259 ,0.00012207 ,0.00018311 , 0.00033569 ,0.00021362 ,0.00036621 ,0.00033569 ,-0.00012207 , 0.00006104 ,0.00015259 ,-0.00021362 ,0.00021362 ,0.00018311 , 0.00021362 ,0.00009155 ,0.00015259 ,-0.00003052 ,0.00012207 , 0.00024414 ,0.00033569 ,-0.00012207 ,0.00021362 ,-0.00015259 , 0.00021362 ,0.00024414 ,-0.00018311 ,0.00003052 ,0.00003052 , 0.00039673 ,0.00036621 ,0.00000000 ,0.00021362 ,0.00000000 , 0.00042725 ,0.00006104 ,0.00015259 ,-0.00018311 ,0.00006104 , 0.00012207 ,0.00000000 ,-0.00015259 ,0.00018311 ,-0.00009155 , 0.00015259 ,-0.00015259 ,0.00006104 ,0.00006104 ,-0.00015259 , 0.00012207 ,-0.00006104 ,0.00018311 ,0.00015259 ,0.00033569 , 0.00012207 ,0.00012207 ,-0.00006104 ,0.00012207 ,-0.00024414 , -0.00027466 ,-0.00036621 ,-0.00015259 ,-0.00033569 ,0.00033569 , 0.00018311 ,0.00042725 ,0.00012207 ,0.00027466 ,0.00018311 , 0.00006104 ,-0.00012207 ,0.00021362 ,-0.00015259 ,0.00015259 , 0.00006104 ,-0.00009155 ,0.00018311 ,0.00012207 ,0.00018311 , 0.00021362 ,0.00033569 ,0.00006104 ,0.00027466 ,0.00012207 , 0.00006104 ,-0.00033569 ,-0.00006104 ,-0.00042725 ,0.00003052 , 0.00009155 ,0.00039673 ,0.00024414 ,0.00009155 ,0.00030518 , 0.00003052 ,-0.00006104 ,-0.00012207 ,0.00006104 ,-0.00006104 , 0.00024414 ,0.00009155 ,0.00015259 ,0.00006104 ,-0.00015259 , -0.00024414 ,-0.00027466 ,-0.00033569 ,-0.00012207 ,0.00000000 , 0.00003052 ,0.00015259 ,-0.00018311 ,-0.00021362 ,-0.00021362 , 0.00012207 ,-0.00009155 ,0.00000000 ,-0.00009155 ,0.00003052 , 0.00003052 ,-0.00015259 ,-0.00018311 ,-0.00009155 ,0.00006104 , -0.00018311 ,-0.00015259 ,-0.00015259 ,-0.00039673 ,-0.00033569 , -0.00039673 ,-0.00027466 ,-0.00009155 ,-0.00006104 ,0.00003052 , 0.00018311 ,-0.00042725 ,-0.00015259 ,-0.00024414 ,-0.00036621 , -0.00033569 ,-0.00030518 ,-0.00003052 ,-0.00006104 ,0.00009155 , -0.00003052 ,-0.00003052 ,-0.00012207 ,0.00006104 ,-0.00003052 , -0.00012207 ,0.00003052 ,-0.00030518 ,0.00009155 ,-0.00027466 , -0.00018311 ,-0.00012207 ,-0.00018311 ,-0.00009155 ,-0.00024414 , -0.00003052 ,0.00009155 ,-0.00012207 ,-0.00018311 ,-0.00012207 , -0.00006104 ,-0.00015259 ,-0.00018311 ,-0.00018311 ,-0.00021362 , -0.00021362 ,-0.00006104 ,-0.00018311 ,-0.00012207 ,-0.00033569 , -0.00015259 ,0.00003052 ,-0.00015259 ,0.00009155 ,-0.00009155 , -0.00012207 ,0.00006104 ,-0.00012207 ,-0.00009155 ,0.00006104 , 0.00012207 ,0.00009155 ,-0.00003052 ,-0.00006104 ,-0.00009155 , 0.00003052 ,0.00012207 ,0.00009155 ,0.00012207 ,-0.00009155 , 0.00009155 ,-0.00033569 ,0.00012207 ,0.00012207 ,-0.00012207 , 0.00027466 ,0.00027466 ,0.00021362 ,0.00003052 ,0.00009155 , 0.00018311 ,-0.00015259 ,0.00030518 ,0.00021362 ,0.00012207 , 0.00003052 ,-0.00012207 ,0.00003052 ,-0.00003052 ,0.00027466 , 0.00045776 ,0.00006104 ,-0.00012207 ,0.00012207 ,0.00030518 , 0.00021362 ,0.00003052 ,0.00000000 ,0.00012207 ,-0.00030518 , 0.00000000 ,0.00012207 ,0.00000000 ,0.00012207 ,0.00027466 , 0.00009155 ,0.00006104 ,0.00024414 ,-0.00012207 ,0.00030518 , -0.00012207 ,0.00024414 ,-0.00003052 ,0.00015259 ,0.00009155 , -0.00003052 ,0.00003052 ,0.00021362 ,0.00030518 ,0.00018311 , 0.00024414 ,0.00012207 ,0.00030518 ,0.00009155 ,0.00012207 , -0.00012207 ,0.00003052 ,0.00000000 ,-0.00012207 ,-0.00009155 , 0.00000000 ,0.00000000 ,0.00009155 ,-0.00018311 ,-0.00012207 , -0.00012207 ,0.00000000 ,-0.00012207 ,-0.00030518 ,0.00009155 , 0.00003052 ,0.00018311 ,-0.00036621 ,-0.00009155 ,-0.00036621 , -0.00009155 ,-0.00036621 ,-0.00003052 ,-0.00006104 ,0.00003052 , -0.00009155 ,-0.00030518 ,-0.00006104 ,-0.00027466 ,0.00003052 , -0.00006104 ,0.00018311 ,0.00018311 ,-0.00006104 ,-0.00006104 , -0.00003052 ,-0.00027466 ,-0.00006104 ,-0.00030518 ,0.00006104 , -0.00009155 ,-0.00018311 ,-0.00027466 ,0.00000000 ,-0.00018311 , 0.00006104 ,-0.00021362 ,0.00024414 ,-0.00006104 ,0.00006104 , -0.00030518 ,-0.00009155 ,-0.00030518 ,-0.00024414 ,-0.00021362 , -0.00039673 ,-0.00006104 ,-0.00021362 ,-0.00021362 ,-0.00003052 , -0.00030518 ,-0.00036621 ,-0.00015259 ,-0.00024414 ,-0.00027466 , -0.00003052 ,0.00003052 ,0.00003052 ,-0.00018311 ,-0.00009155 , -0.00033569 ,0.00012207 ,-0.00009155 ,-0.00021362 ,0.00000000 , -0.00030518 ,-0.00015259 ,-0.00006104 ,-0.00015259 ,0.00003052 , 0.00003052 ,-0.00003052 ,-0.00024414 ,0.00021362 ,-0.00027466 , -0.00009155 ,-0.00048828 ,0.00015259 ,-0.00015259 ,-0.00018311 , 0.00003052 ,-0.00012207 ,-0.00012207 ,-0.00042725 ,-0.00012207 , -0.00042725 ,0.00000000 ,-0.00006104 ,-0.00009155 ,-0.00012207 , 0.00015259 ,-0.00033569 ,0.00021362 ,-0.00003052 ,-0.00036621 , -0.00027466 ,-0.00015259 ,-0.00009155 ,0.00003052 ,0.00006104 , -0.00006104 ,-0.00006104 ,0.00000000 ,-0.00009155 ,0.00009155 , -0.00012207 ,-0.00039673 ,-0.00021362 ,-0.00033569 ,0.00000000 , -0.00009155 ,0.00009155 ,-0.00030518 ,0.00000000 ,-0.00024414 , -0.00030518 ,0.00003052 ,0.00006104 ,0.00003052 ,-0.00012207 , 0.00006104 ,-0.00012207 ,-0.00009155 ,-0.00009155 ,-0.00012207 , -0.00012207 ,-0.00027466 ,-0.00003052 ,-0.00015259 ,-0.00027466 , -0.00039673 ,-0.00009155 ,0.00018311 ,0.00003052 ,-0.00042725 , 0.00000000 ,-0.00006104 ,-0.00045776 ,-0.00012207 ,-0.00051880 , -0.00033569 ,-0.00018311 ,-0.00027466 ,-0.00012207 ,-0.00027466 , -0.00030518 ,-0.00042725 ,-0.00006104 ,-0.00030518 ,0.00006104 , 0.00006104 ,0.00009155 ,0.00000000 ,0.00012207 ,-0.00015259 , -0.00033569 ,-0.00009155 ,0.00012207 ,-0.00030518 ,0.00015259 , -0.00009155 ,-0.00009155 ,-0.00009155 ,-0.00021362 ,-0.00012207 , -0.00015259 ,0.00003052 ,-0.00030518 ,0.00027466 ,-0.00021362 , -0.00021362 ,-0.00021362 ,-0.00018311 ,0.00009155 ,-0.00003052 , 0.00006104 ,0.00018311 ,0.00003052 ,-0.00012207 ,0.00003052 , 0.00000000 ,0.00015259 ,-0.00012207 ,-0.00003052 ,0.00000000 , 0.00003052 ,-0.00033569 ,-0.00024414 ,-0.00012207 ,-0.00033569 , -0.00015259 ,-0.00015259 ,0.00015259 ,0.00006104 ,-0.00006104 , 0.00009155 ,-0.00012207 ,-0.00021362 ,-0.00003052 ,-0.00048828 , 0.00000000 ,-0.00006104 ,0.00006104 ,0.00012207 ,-0.00030518 , 0.00003052 ,0.00003052 ,-0.00009155 ,0.00009155 ,-0.00036621 , -0.00021362 ,-0.00021362 ,-0.00015259 ,-0.00024414 ,-0.00024414 , -0.00006104 ,-0.00003052 ,0.00012207 ,-0.00018311 ,-0.00030518 , -0.00006104 ,-0.00030518 ,0.00009155 ,-0.00024414 ,0.00012207 , -0.00015259 ,0.00000000 ,-0.00024414 ,-0.00033569 ,-0.00045776 , -0.00027466 ,-0.00006104 ,-0.00009155 ,-0.00039673 ,-0.00027466 , -0.00027466 ,-0.00027466 ,-0.00045776 ,-0.00039673 ,-0.00051880 , 0.00000000 ,-0.00006104 ,-0.00012207 ,-0.00012207 ,-0.00021362 , -0.00054932 ,-0.00018311 ,-0.00012207 ,-0.00018311 ,-0.00030518 , -0.00048828 ,-0.00030518 ,-0.00061035 ,-0.00024414 ,0.00000000 , -0.00021362 ,-0.00018311 ,-0.00048828 ,0.00006104 ,-0.00033569 , -0.00024414 ,-0.00033569 ,-0.00021362 ,-0.00003052 ,-0.00024414 , -0.00048828 ,-0.00042725 ,-0.00030518 ,-0.00048828 ,0.00012207 , 0.00012207 ,0.00003052 ,-0.00018311 ,0.00000000 ,-0.00045776 , -0.00009155 ,-0.00051880 ,-0.00045776 ,-0.00006104 ,-0.00012207 , 0.00006104 ,0.00009155 ,0.00021362 ,-0.00003052 ,-0.00027466 , 0.00006104 ,-0.00036621 ,-0.00003052 ,-0.00006104 ,0.00009155 , 0.00012207 ,0.00000000 ,-0.00006104 ,-0.00006104 ,0.00018311 , 0.00009155 ,0.00027466 ,0.00027466 ,0.00027466 ,0.00006104 , -0.00012207 ,0.00006104 ,0.00012207 ,0.00015259 ,0.00018311 , 0.00009155 ,0.00006104 ,-0.00009155 ,0.00024414 ,-0.00009155 , 0.00012207 ,0.00003052 ,0.00027466 ,-0.00012207 ,0.00018311 , 0.00009155 ,0.00018311 ,-0.00012207 ,-0.00018311 ,0.00018311 , 0.00000000 ,0.00024414 ,0.00045776 ,0.00039673 ,0.00024414 , 0.00018311 ,0.00015259 ,0.00012207 ,0.00012207 ,-0.00015259 , 0.00015259 ,0.00030518 ,0.00018311 ,0.00030518 ,0.00012207 , 0.00021362 ,0.00018311 ,0.00018311 ,0.00039673 ,0.00061035 , 0.00042725 ,0.00048828 ,0.00048828 ,0.00054932 ,0.00009155 , 0.00030518 ,0.00015259 ,0.00012207 ,0.00024414 ,0.00000000 , 0.00024414 ,0.00009155 ,0.00051880 ,0.00054932 ,0.00036621 , 0.00018311 ,0.00030518 ,0.00021362 ,0.00027466 ,0.00061035 , 0.00045776 ,0.00045776 ,0.00030518 ,0.00030518 ,0.00036621 , 0.00039673 ,0.00039673 ,0.00000000 ,0.00003052 ,0.00027466 , -0.00009155 ,-0.00024414 ,0.00006104 ,0.00003052 ,-0.00009155 , -0.00012207 ,0.00021362 ,-0.00009155 ,-0.00006104 ,-0.00018311 , 0.00021362 ,0.00000000 ,0.00000000 ,0.00000000 ,-0.00015259 , 0.00018311 ,-0.00018311 ,0.00009155 ,-0.00009155 ,0.00009155 , -0.00003052 ,0.00009155 ,0.00003052 ,0.00024414 ,-0.00024414 , 0.00027466 ,0.00015259 ,-0.00015259 ,-0.00012207 ,-0.00024414 , -0.00015259 ,0.00015259 ,0.00018311 ,-0.00012207 ,0.00024414 , 0.00000000 ,-0.00015259 ,-0.00009155 ,-0.00036621 ,-0.00015259 , 0.00012207 ,-0.00018311 ,0.00003052 ,-0.00018311 ,-0.00021362 , -0.00021362 ,-0.00006104 ,-0.00018311 ,-0.00009155 ,-0.00012207 , 0.00006104 ,0.00000000 ,-0.00015259 ,0.00021362 ,0.00039673 , 0.00009155 ,-0.00027466 ,0.00030518 ,-0.00015259 ,0.00003052 , -0.00033569 ,-0.00003052 ,0.00000000 ,-0.00009155 ,-0.00012207 , 0.00006104 ,-0.00021362 ,-0.00006104 ,-0.00015259 ,0.00024414 , -0.00009155 ,0.00003052 ,-0.00006104 ,0.00015259 ,-0.00006104 , 0.00003052 ,0.00045776 ,-0.00006104 ,0.00027466 ,0.00021362 , 0.00000000 ,-0.00003052 ,0.00003052 ,0.00018311 ,0.00012207 , -0.00030518 ,0.00027466 ,0.00000000 ,0.00027466 ,0.00024414 , 0.00021362 ,0.00021362 ,0.00045776 ,0.00021362 ,0.00030518 , 0.00000000 ,0.00051880 ,0.00036621 ,0.00015259 ,0.00051880 , 0.00018311 ,0.00039673 ,-0.00006104 ,0.00024414 ,0.00030518 , 0.00042725 ,0.00009155 ,0.00021362 ,0.00030518 ,0.00018311 , 0.00030518 ,0.00030518 ,0.00045776 ,0.00033569 ,0.00033569 , 0.00024414 ,0.00042725 ,0.00039673 ,0.00051880 ,0.00045776 , 0.00045776 ,0.00030518 ,0.00054932 ,0.00024414 ,0.00036621 , 0.00054932 ,0.00045776 ,0.00042725 ,0.00048828 ,0.00067139 , 0.00042725 ,0.00057983 ,0.00051880 ,0.00048828 ,0.00070190 , 0.00061035 ,0.00054932 ,0.00070190 ,0.00067139 ,0.00076294 , 0.00088501 ,0.00057983 ,0.00073242 ,0.00051880 ,0.00051880 , 0.00100708 ,0.00061035 ,0.00100708 ,0.00048828 ,0.00079346 , 0.00064087 ,0.00064087 ,0.00067139 ,0.00054932 ,0.00088501 , 0.00091553 ,0.00076294 ,0.00057983 ,0.00091553 ,0.00051880 , 0.00070190 ,0.00061035 ,0.00079346 ,0.00048828 ,0.00091553 , 0.00057983 ,0.00067139 ,0.00042725 ,0.00061035 ,0.00024414 , 0.00048828 ,0.00057983 ,0.00076294 ,0.00061035 ,0.00057983 , 0.00030518 ,0.00064087 ,0.00054932 ,0.00045776 ,0.00039673 , 0.00076294 ,0.00042725 ,0.00054932 ,0.00064087 ,0.00067139 , 0.00030518 ,0.00054932 ,0.00051880 ,0.00045776 ,0.00088501 , 0.00076294 ,0.00054932 ,0.00045776 ,0.00042725 ,0.00036621 , 0.00048828 ,0.00051880 ,0.00054932 ,0.00054932 ,0.00045776 , 0.00051880 ,0.00027466 ,0.00039673 ,0.00030518 ,0.00042725 , 0.00048828 ,0.00061035 ,0.00042725 ,0.00045776 ,0.00033569 , 0.00036621 ,0.00006104 ,0.00048828 ,0.00051880 ,0.00021362 , 0.00045776 ,0.00021362 ,0.00070190 ,0.00021362 ,0.00042725 , 0.00003052 ,0.00048828 ,0.00042725 ,0.00057983 ,0.00051880 , 0.00054932 ,0.00030518 ,0.00027466 ,0.00042725 ,0.00036621 , 0.00036621 ,0.00024414 ,0.00003052 ,0.00000000 ,0.00015259 , 0.00024414 ,-0.00024414 ,0.00024414 ,0.00015259 ,0.00024414 , 0.00030518 ,0.00009155 ,0.00003052 ,0.00006104 ,0.00009155 , 0.00000000 ,-0.00021362 ,0.00006104 ,0.00003052 ,-0.00015259 , -0.00006104 ,0.00000000 ,-0.00009155 ,-0.00003052 ,0.00012207 , 0.00024414 ,0.00018311 ,0.00027466 ,-0.00006104 ,-0.00027466 , 0.00027466 ,0.00036621 ,0.00027466 ,0.00015259 ,0.00042725 , 0.00039673 ,0.00033569 ,0.00024414 ,0.00000000 ,0.00051880 , 0.00003052 ,0.00003052 ,0.00015259 ,0.00018311 ,0.00000000 , 0.00006104 ,0.00027466 ,0.00012207 ,0.00006104 ,0.00015259 , 0.00012207 ,0.00033569 ,-0.00003052 ,-0.00006104 ,-0.00015259 , 0.00012207 ,0.00012207 ,0.00015259 ,0.00054932 ,0.00012207 , 0.00015259 ,0.00018311 ,0.00006104 ,0.00024414 ,0.00030518 , 0.00033569 ,0.00003052 ,0.00048828 ,0.00033569 ,0.00018311 , 0.00027466 ,0.00024414 ,-0.00003052 ,0.00042725 ,0.00015259 , 0.00006104 ,0.00018311 ,0.00012207 ,0.00030518 ,0.00042725 , 0.00039673 ,0.00036621 ,0.00021362 ,0.00036621 ,0.00039673 , 0.00009155 ,-0.00012207 ,0.00009155 ,0.00015259 ,0.00009155 , 0.00018311 ,0.00030518 ,0.00030518 ,0.00021362 ,0.00000000 , 0.00015259 ,0.00027466 ,0.00030518 ,0.00018311 ,0.00018311 , 0.00018311 ,0.00064087 ,0.00042725 ,0.00021362 ,0.00021362 , 0.00009155 ,0.00021362 ,0.00045776 ,0.00033569 ,0.00033569 , 0.00030518 ,0.00021362 ,0.00051880 ,0.00033569 ,0.00033569 , 0.00030518 ,0.00036621 ,0.00012207 ,0.00018311 ,0.00027466 , -0.00006104 ,0.00012207 ,0.00006104 ,-0.00006104 ,-0.00003052 , 0.00009155 ,-0.00012207 ,0.00009155 ,-0.00006104 ,0.00012207 , 0.00000000 ,-0.00003052 ,0.00021362 ,-0.00006104 ,0.00000000 , -0.00021362 ,0.00000000 ,0.00009155 ,0.00003052 ,0.00003052 , 0.00009155 ,0.00030518 ,0.00024414 ,0.00012207 ,0.00021362 , 0.00003052 ,0.00030518 ,0.00003052 ,0.00012207 ,0.00027466 , -0.00009155 ,-0.00006104 ,-0.00006104 ,-0.00012207 ,0.00006104 , -0.00024414 ,-0.00009155 ,-0.00030518 ,-0.00009155 ,-0.00018311 , -0.00027466 ,-0.00018311 ,-0.00006104 ,0.00003052 ,-0.00009155 , -0.00021362 ,-0.00024414 ,-0.00021362 ,-0.00006104 ,-0.00039673 , -0.00036621 ,-0.00006104 ,-0.00045776 ,-0.00018311 ,-0.00015259 , -0.00009155 ,-0.00009155 ,-0.00006104 ,-0.00027466 ,0.00015259 , -0.00003052 ,-0.00009155 ,-0.00027466 ,-0.00003052 ,-0.00012207 , -0.00006104 ,-0.00012207 ,0.00000000 ,-0.00033569 ,-0.00024414 , -0.00009155 ,0.00003052 ,0.00000000 ,-0.00018311 ,-0.00006104 , -0.00015259 ,-0.00003052 ,0.00006104 ,-0.00012207 ,0.00009155 , -0.00012207 ,0.00012207 ,-0.00012207 ,-0.00030518 ,-0.00012207 , 0.00000000 ,-0.00021362 ,-0.00003052 ,0.00024414 ,0.00000000 , 0.00021362 ,-0.00015259 ,0.00000000 ,0.00003052 ,0.00027466 , -0.00006104 ,0.00021362 ,-0.00015259 ,-0.00009155 ,0.00015259 , 0.00018311 ,0.00006104 ,0.00024414 ,0.00006104 ,0.00012207 , 0.00036621 ,0.00000000 ,0.00033569 ,0.00048828 ,0.00042725 , 0.00015259 ,0.00030518 ,0.00006104 ,0.00024414 ,0.00024414 , 0.00027466 ,0.00021362 ,0.00036621 ,0.00048828 ,-0.00009155 , 0.00012207 ,0.00015259 ,0.00015259 ,0.00006104 ,0.00009155 , 0.00033569 ,0.00027466 ,-0.00003052 ,0.00015259 ,0.00009155 , 0.00018311 ,-0.00006104 ,0.00012207 ,0.00021362 ,0.00000000 , 0.00015259 ,-0.00027466 ,0.00024414 ,-0.00015259 ,0.00000000 , -0.00009155 ,-0.00006104 ,0.00006104 ,0.00018311 ,0.00015259 , 0.00009155 ,-0.00012207 ,0.00003052 ,0.00015259 ,0.00021362 , 0.00006104 ,0.00018311 ,-0.00024414 ,0.00021362 ,-0.00021362 , 0.00006104 ,0.00006104 ,-0.00012207 ,-0.00006104 ,0.00015259 , 0.00024414 ,0.00030518 ,0.00006104 ,0.00006104 ,0.00000000 , 0.00012207 ,0.00009155 ,0.00006104 ,-0.00006104 ,-0.00012207 , -0.00012207 ,0.00018311 ,-0.00021362 ,-0.00003052 ,0.00015259 , -0.00012207 ,0.00003052 ,-0.00021362 ,0.00000000 ,-0.00012207 , -0.00012207 ,-0.00015259 ,-0.00003052 ,-0.00045776 ,-0.00018311 , -0.00024414 ,0.00006104 ,0.00003052 ,-0.00009155 ,-0.00012207 , -0.00009155 ,-0.00030518 ,-0.00027466 ,0.00033569 ,0.00003052 , 0.00027466 ,0.00000000 ,-0.00009155 ,-0.00015259 ,-0.00006104 , -0.00012207 ,0.00012207 ,0.00000000 ,-0.00015259 ,0.00003052 , 0.00003052 ,-0.00003052 ,-0.00015259 ,-0.00027466 ,-0.00021362 , -0.00003052 ,-0.00045776 ,0.00003052 ,-0.00036621 ,-0.00021362 , -0.00006104 ,-0.00012207 ,-0.00021362 ,0.00006104 ,-0.00033569 , -0.00024414 ,-0.00036621 ,-0.00018311 ,-0.00018311 ,-0.00006104 , -0.00027466 ,-0.00006104 ,-0.00012207 ,-0.00012207 ,-0.00051880 , -0.00024414 ,-0.00036621 ,-0.00003052 ,-0.00027466 ,0.00024414 , 0.00003052 ,-0.00018311 ,-0.00015259 ,-0.00018311 ,-0.00027466 , 0.00018311 ,-0.00024414 ,0.00000000 ,0.00006104 ,-0.00027466 , -0.00003052 ,-0.00009155 ,-0.00024414 ,-0.00018311 ,0.00000000 , -0.00033569 ,-0.00015259 ,-0.00024414 ,-0.00018311 ,-0.00012207 , -0.00036621 ,0.00009155 ,-0.00012207 ,-0.00021362 ,0.00000000 , -0.00009155 ,-0.00027466 ,0.00015259 ,-0.00024414 ,0.00009155 , 0.00012207 ,-0.00009155 ,-0.00018311 ,-0.00006104 ,-0.00003052 , -0.00036621 ,-0.00006104 ,-0.00045776 ,-0.00015259 ,-0.00021362 , -0.00015259 ,-0.00030518 ,-0.00021362 ,-0.00024414 ,-0.00021362 , -0.00027466 ,-0.00027466 ,0.00003052 ,-0.00018311 ,-0.00009155 , -0.00033569 ,-0.00018311 ,-0.00009155 ,-0.00027466 ,-0.00015259 , -0.00015259 ,-0.00003052 ,-0.00021362 ,0.00003052 ,0.00006104 , -0.00036621 ,-0.00012207 ,-0.00021362 ,-0.00018311 ,-0.00009155 , -0.00018311 ,-0.00012207 ,-0.00021362 ,-0.00009155 ,-0.00030518 , -0.00015259 ,-0.00033569 ,-0.00006104 ,-0.00009155 ,-0.00021362 , 0.00006104 ,0.00003052 ,0.00024414 ,0.00018311 ,-0.00003052 , -0.00021362 ,0.00000000 ,-0.00021362 ,0.00000000 ,-0.00003052 , -0.00009155 ,0.00006104 ,-0.00006104 ,-0.00018311 ,-0.00006104 , 0.00009155 ,0.00003052 ,0.00015259 ,0.00009155 ,-0.00009155 , 0.00024414 ,-0.00003052 ,0.00006104 ,0.00000000 ,-0.00009155 , 0.00012207 ,-0.00006104 ,0.00009155 ,0.00015259 ,-0.00009155 , -0.00009155 ,0.00024414 ,-0.00006104 ,0.00012207 ,0.00033569 , 0.00021362 ,-0.00003052 ,0.00018311 ,0.00000000 ,0.00036621 , -0.00003052 ,0.00018311 ,0.00027466 ,0.00012207 ,-0.00021362 , 0.00009155 ,-0.00018311 ,0.00048828 ,0.00012207 ,0.00027466 , 0.00009155 ,0.00021362 ,0.00048828 ,0.00033569 ,0.00030518 , 0.00006104 ,0.00000000 ,0.00018311 ,0.00012207 ,-0.00021362 , 0.00021362 ,0.00000000 ,0.00009155 ,-0.00009155 ,0.00024414 , 0.00039673 ,-0.00018311 ,0.00018311 ,0.00009155 ,0.00006104 , 0.00003052 ,0.00006104 ,-0.00027466 ,0.00003052 ,-0.00003052 , -0.00012207 ,-0.00021362 ,-0.00006104 ,0.00012207 ,-0.00003052 , -0.00006104 ,-0.00012207 ,-0.00018311 ,0.00006104 ,0.00000000 , 0.00006104 ,-0.00009155 ,0.00006104 ,-0.00009155 ,-0.00009155 , -0.00015259 ,-0.00018311 ,-0.00018311 ,-0.00012207 ,-0.00006104 , 0.00000000 ,0.00024414 ,0.00003052 ,0.00003052 ,-0.00012207 , 0.00009155 ,-0.00006104 ,-0.00024414 ,-0.00018311 ,0.00009155 , -0.00003052 ,-0.00036621 ,-0.00015259 ,-0.00036621 ,-0.00024414 , -0.00024414 ,-0.00024414 ,-0.00036621 ,-0.00036621 ,-0.00021362 , -0.00027466 ,-0.00030518 ,-0.00018311 ,-0.00048828 ,-0.00015259 , -0.00030518 ,-0.00030518 ,-0.00027466 ,-0.00021362 ,-0.00021362 , -0.00021362 ,-0.00036621 ,-0.00015259 ,-0.00042725 ,0.00000000 , -0.00024414 ,-0.00027466 ,-0.00021362 ,-0.00012207 ,0.00009155 , -0.00012207 ,-0.00045776 ,-0.00036621 ,-0.00042725 ,-0.00039673 , -0.00015259 ,-0.00033569 ,-0.00018311 ,-0.00021362 ,-0.00021362 , -0.00039673 ,-0.00036621 ,-0.00018311 ,-0.00021362 ,-0.00015259 , -0.00009155 ,-0.00021362 ,-0.00018311 ,-0.00030518 ,0.00003052 , -0.00015259 ,-0.00012207 ,-0.00006104 ,0.00021362 ,-0.00018311 , -0.00003052 ,-0.00012207 ,-0.00003052 ,-0.00048828 ,-0.00018311 , -0.00021362 ,0.00003052 ,-0.00018311 ,0.00003052 ,-0.00006104 , -0.00009155 ,-0.00006104 ,-0.00042725 ,-0.00003052 ,-0.00009155 , -0.00006104 ,0.00003052 ,0.00018311 ,-0.00003052 ,-0.00015259 , -0.00018311 ,-0.00024414 ,-0.00009155 ,0.00000000 ,-0.00024414 , -0.00012207 ,-0.00030518 ,0.00012207 ,-0.00036621 ,-0.00009155 , -0.00021362 ,-0.00021362 ,-0.00015259 ,-0.00003052 ,-0.00012207 , -0.00003052 ,0.00012207 ,0.00000000 ,-0.00018311 ,0.00009155 , 0.00030518 ,-0.00012207 ,0.00015259 ,-0.00009155 ,-0.00018311 , -0.00006104 ,-0.00015259 ,0.00000000 ,-0.00009155 ,0.00000000 , -0.00006104 ,0.00003052 ,-0.00006104 ,-0.00018311 ,-0.00015259 , -0.00012207 ,-0.00015259 ,-0.00003052 ,-0.00027466 ,0.00015259 , -0.00012207 ,-0.00021362 ,-0.00012207 ,-0.00021362 ,-0.00009155 , -0.00030518 ,-0.00006104 ,-0.00039673 ,-0.00006104 ,-0.00030518 , -0.00036621 ,-0.00009155 ,-0.00024414 ,-0.00009155 ,0.00009155 , 0.00000000 ,-0.00003052 ,-0.00021362 ,0.00003052 ,-0.00009155 , -0.00003052 ,-0.00003052 ,0.00000000 ,0.00012207 ,-0.00012207 , 0.00006104 ,-0.00003052 ,-0.00009155 ,-0.00003052 ,0.00021362 , -0.00021362 ,-0.00024414 ,-0.00003052 ,-0.00027466 ,0.00015259 , -0.00009155 ,0.00000000 ,-0.00009155 ,0.00009155 ,-0.00036621 , 0.00003052 ,-0.00024414 ,-0.00003052 ,-0.00018311 ,0.00018311 , -0.00027466 ,-0.00024414 ,-0.00009155 ,0.00003052 ,-0.00009155 , 0.00006104 ,-0.00024414 ,-0.00018311 ,0.00006104 ,0.00009155 , 0.00021362 ,-0.00006104 ,0.00006104 ,-0.00018311 ,0.00024414 , -0.00012207 ,0.00009155 ,-0.00012207 ,0.00003052 ,0.00030518 , 0.00003052 ,0.00003052 ,0.00006104 ,0.00000000 ,0.00000000 , 0.00015259 ,0.00021362 ,0.00012207 ,0.00015259 ,0.00015259 , -0.00018311 ,0.00015259 ,-0.00012207 ,-0.00009155 ,-0.00006104 , -0.00015259 ,-0.00018311 ,-0.00006104 ,-0.00012207 ,-0.00006104 , -0.00012207 ,0.00018311 ,-0.00009155 ,0.00018311 ,-0.00033569 , -0.00021362 ,-0.00024414 ,-0.00024414 ,-0.00012207 ,-0.00003052 , -0.00015259 ,-0.00012207 ,-0.00024414 ,-0.00027466 ,0.00012207 , -0.00042725 ,-0.00003052 ,-0.00036621 ,-0.00012207 ,-0.00018311 , -0.00027466 ,-0.00021362 ,-0.00018311 ,-0.00033569 ,0.00003052 , -0.00018311 ,-0.00009155 ,-0.00030518 ,-0.00006104 ,-0.00033569 , -0.00015259 ,-0.00024414 ,-0.00033569 ,-0.00021362 ,-0.00033569 , -0.00045776 ,-0.00036621 ,-0.00039673 ,-0.00048828 ,-0.00012207 , -0.00033569 ,-0.00024414 ,-0.00006104 ,-0.00027466 ,0.00006104 , -0.00012207 ,-0.00009155 ,-0.00015259 ,-0.00024414 ,-0.00030518 , -0.00009155 ,-0.00018311 ,-0.00051880 ,0.00006104 ,-0.00030518 , -0.00033569 ,-0.00045776 ,-0.00030518 ,-0.00009155 ,-0.00024414 , -0.00027466 ,-0.00009155 ,-0.00030518 ,-0.00021362 ,-0.00027466 , -0.00015259 ,-0.00003052 ,-0.00048828 ,-0.00012207 ,-0.00042725 , -0.00015259 ,0.00003052 ,-0.00021362 ,0.00009155 ,-0.00006104 , 0.00003052 ,-0.00027466 ,0.00003052 ,-0.00015259 ,-0.00009155 , -0.00003052 ,-0.00018311 ,0.00000000 ,-0.00009155 ,-0.00003052 , -0.00006104 ,0.00003052 ,0.00006104 ,-0.00024414 ,0.00012207 , -0.00012207 ,0.00003052 ,-0.00024414 ,0.00012207 ,0.00027466 , 0.00006104 ,0.00024414 ,0.00012207 ,-0.00003052 ,-0.00006104 , 0.00006104 ,0.00009155 ,0.00006104 ,0.00021362 ,0.00006104 , 0.00012207 ,0.00012207 ,0.00009155 ,0.00015259 ,0.00027466 , -0.00018311 ,0.00009155 ,-0.00003052 ,0.00015259 ,0.00000000 , 0.00003052 ,-0.00003052 ,0.00018311 ,-0.00036621 ,-0.00009155 , -0.00018311 ,-0.00003052 ,-0.00018311 ,-0.00006104 ,-0.00033569 , 0.00006104 ,-0.00021362 ,-0.00009155 ,-0.00033569 ,-0.00009155 , -0.00012207 ,-0.00009155 ,-0.00030518 ,-0.00009155 ,-0.00003052 , -0.00006104 ,-0.00012207 ,-0.00003052 ,-0.00003052 ,-0.00021362 , -0.00033569 ,-0.00030518 ,-0.00003052 ,-0.00015259 ,-0.00024414 , -0.00021362 ,-0.00018311 ,-0.00012207 ,-0.00051880 ,-0.00018311 , -0.00033569 ,-0.00012207 ,-0.00027466 ,-0.00018311 ,-0.00009155 , -0.00015259 ,-0.00006104 ,-0.00003052 ,-0.00006104 ,-0.00051880 , -0.00051880 ,-0.00027466 ,-0.00018311 ,-0.00042725 ,-0.00015259 , -0.00036621 ,0.00000000 ,-0.00015259 ,-0.00021362 ,-0.00033569 , -0.00027466 ,0.00006104 ,-0.00036621 ,-0.00003052 ,-0.00024414 , -0.00012207 ,-0.00030518 ,-0.00012207 ,-0.00030518 ,-0.00039673 , -0.00033569 ,-0.00048828 ,-0.00024414 ,-0.00045776 ,-0.00027466 , -0.00021362 ,-0.00027466 ,-0.00018311 ,-0.00024414 ,-0.00024414 , -0.00006104 ,-0.00033569 ,-0.00036621 ,-0.00033569 ,-0.00030518 , -0.00018311 ,-0.00006104 ,-0.00021362 ,-0.00048828 ,-0.00003052 , -0.00018311 ,0.00015259 ,-0.00030518 ,-0.00009155 ,-0.00015259 , -0.00015259 ,-0.00012207 ,-0.00027466 ,-0.00027466 ,-0.00054932 , 0.00006104 ,-0.00024414 ,-0.00006104 ,-0.00015259 ,-0.00003052 , 0.00006104 ,0.00003052 ,0.00015259 ,0.00012207 ,-0.00003052 , 0.00006104 ,-0.00009155 ,0.00012207 ,-0.00009155 ,-0.00021362 , -0.00012207 ,0.00003052 ,0.00003052 ,-0.00015259 ,-0.00012207 , -0.00015259 ,-0.00030518 ,-0.00006104 ,-0.00027466 ,0.00000000 , -0.00024414 ,-0.00006104 ,0.00006104 ,-0.00015259 ,-0.00003052 , 0.00006104 ,-0.00018311 ,-0.00003052 ,-0.00003052 ,0.00006104 , -0.00033569 ,-0.00009155 ,-0.00015259 ,0.00009155 ,-0.00012207 , 0.00012207 ,-0.00012207 ,0.00018311 ,0.00036621 ,-0.00003052 , 0.00000000 ,0.00000000 ,0.00006104 ,0.00009155 ,0.00009155 , 0.00006104 ,-0.00006104 ,0.00015259 ,0.00027466 ,0.00003052 , 0.00012207 ,0.00018311 ,-0.00003052 ,0.00009155 ,0.00018311 , 0.00009155 ,0.00024414 ,0.00006104 ,0.00003052 ,0.00027466 , 0.00003052 ,-0.00003052 ,-0.00024414 ,-0.00009155 ,-0.00036621 , -0.00012207 ,-0.00015259 ,0.00033569 ,0.00006104 ,0.00018311 , -0.00006104 ,-0.00006104 ,0.00015259 ,0.00012207 ,0.00018311 , -0.00015259 ,-0.00003052 ,0.00000000 ,-0.00006104 ,0.00006104 , 0.00006104 ,0.00015259 ,-0.00003052 ,0.00009155 ,0.00006104 , -0.00012207 ,0.00027466 ,-0.00006104 ,0.00000000 ,-0.00006104 , 0.00006104 ,0.00024414 ,0.00021362 ,0.00006104 ,-0.00006104 , -0.00006104 ,-0.00015259 ,0.00018311 ,0.00000000 ,0.00021362 , -0.00009155 ,-0.00003052 ,0.00027466 ,-0.00015259 ,0.00009155 , 0.00003052 ,-0.00006104 ,0.00036621 ,0.00003052 ,0.00024414 , -0.00009155 ,0.00009155 ,-0.00003052 ,-0.00012207 ,0.00021362 , -0.00018311 ,0.00003052 ,0.00009155 ,-0.00012207 ,-0.00003052 , 0.00030518 ,-0.00030518 ,-0.00003052 ,-0.00018311 ,0.00006104 , -0.00003052 ,-0.00006104 ,0.00009155 ,-0.00015259 ,-0.00009155 , -0.00009155 ,0.00012207 ,0.00015259 ,-0.00015259 ,0.00006104 , -0.00033569 ,-0.00021362 ,-0.00015259 ,-0.00009155 ,-0.00015259 , -0.00015259 ,-0.00012207 ,-0.00018311 ,-0.00036621 ,-0.00024414 , 0.00000000 ,-0.00033569 ,-0.00030518 ,-0.00024414 ,-0.00021362 , -0.00024414 ,-0.00006104 ,-0.00012207 ,-0.00033569 ,-0.00024414 , -0.00015259 ,-0.00018311 ,-0.00018311 ,-0.00054932 ,-0.00006104 , -0.00036621 ,-0.00009155 ,-0.00027466 ,-0.00012207 ,-0.00051880 , -0.00042725 ,-0.00051880 ,-0.00024414 ,-0.00054932 ,-0.00021362 , -0.00006104 ,-0.00012207 ,-0.00021362 ,-0.00042725 ,-0.00021362 , 0.00000000 ,-0.00030518 ,-0.00018311 ,-0.00027466 ,-0.00024414 , -0.00048828 ,-0.00027466 ,-0.00024414 ,-0.00024414 ,-0.00018311 , -0.00024414 ,-0.00036621 ,-0.00048828 ,-0.00036621 ,-0.00033569 , -0.00030518 ,0.00006104 ,-0.00024414 ,-0.00021362 ,-0.00030518 , 0.00003052 ,-0.00033569 ,-0.00012207 ,-0.00036621 ,-0.00012207 , -0.00051880 ,-0.00033569 ,-0.00039673 ,0.00009155 ,0.00000000 , -0.00027466 ,0.00015259 ,-0.00024414 ,-0.00006104 ,-0.00024414 , -0.00021362 ,-0.00030518 ,-0.00015259 ,-0.00006104 ,-0.00036621 , 0.00009155 ,-0.00030518 ,-0.00003052 ,-0.00006104 ,-0.00033569 , 0.00009155 ,-0.00015259 ,-0.00012207 ,-0.00030518 ,-0.00012207 , 0.00006104 ,0.00024414 ,-0.00006104 ,0.00003052 ,-0.00012207 , 0.00012207 ,0.00009155 ,0.00006104 ,0.00012207 ,-0.00003052 , 0.00024414 ,0.00003052 ,0.00021362 ,-0.00027466 ,0.00027466 , -0.00012207 ,-0.00024414 ,0.00030518 ,0.00027466 ,0.00033569 , 0.00006104 ,0.00012207 ,0.00036621 ,0.00009155 ,0.00021362 , 0.00021362 ,0.00003052 ,0.00036621 ,0.00006104 ,0.00018311 , -0.00012207 ,0.00009155 ,-0.00009155 ,-0.00012207 ,-0.00003052 , 0.00018311 ,-0.00027466 ,0.00015259 ,0.00009155 ,-0.00003052 , 0.00003052 ,-0.00006104 ,-0.00024414 ,0.00015259 ,-0.00012207 , -0.00009155 ,0.00012207 ,0.00015259 ,0.00012207 ,0.00000000 , 0.00009155 ,-0.00021362 ,-0.00024414 ,-0.00015259 ,-0.00042725 , -0.00021362 ,-0.00018311 ,-0.00024414 ,-0.00048828 ,-0.00033569 , -0.00051880 ,-0.00054932 ,-0.00039673 ,-0.00024414 ,-0.00030518 , -0.00045776 ,-0.00036621 ,-0.00036621 ,-0.00033569 ,-0.00042725 , -0.00030518 ,-0.00054932 ,-0.00070190 ,-0.00030518 ,-0.00054932 , -0.00039673 ,-0.00027466 ,-0.00064087 ,-0.00042725 ,-0.00073242 , -0.00048828 ,-0.00045776 ,-0.00064087 ,-0.00064087 ,-0.00030518 , -0.00051880 ,-0.00048828 ,-0.00039673 ,-0.00057983 ,-0.00094604 , -0.00048828 ,-0.00064087 ,-0.00088501 ,-0.00051880 ,-0.00067139 , -0.00057983 ,-0.00042725 ,-0.00042725 ,-0.00042725 ,-0.00051880 , -0.00036621 ,-0.00073242 ,-0.00042725 ,-0.00057983 ,-0.00027466 , -0.00073242 ,-0.00033569 ,-0.00054932 ,-0.00039673 ,-0.00054932 , -0.00051880 ,-0.00051880 ,-0.00061035 ,-0.00073242 ,-0.00048828 , -0.00036621 ,-0.00051880 ,-0.00030518 ,-0.00070190 ,-0.00018311 , -0.00024414 ,-0.00033569 ,-0.00039673 ,-0.00045776 ,-0.00051880 , -0.00057983 ,-0.00061035 ,-0.00021362 ,-0.00027466 ,-0.00051880 , -0.00039673 ,-0.00045776 ,-0.00030518 ,-0.00057983 ,-0.00042725 , -0.00070190 ,-0.00048828 ,-0.00061035 ,-0.00042725 ,-0.00054932 , -0.00042725 ,-0.00061035 ,-0.00030518 ,-0.00061035 ,-0.00064087 , -0.00036621 ,-0.00012207 ,-0.00024414 ,-0.00042725 ,-0.00048828 , -0.00033569 ,-0.00024414 ,-0.00027466 ,-0.00030518 ,-0.00039673 , -0.00045776 ,-0.00030518 ,-0.00015259 ,-0.00070190 ,-0.00039673 , -0.00030518 ,-0.00036621 ,-0.00067139 ,-0.00054932 ,-0.00064087 , -0.00009155 ,-0.00054932 ,-0.00027466 ,-0.00030518 ,-0.00012207 , -0.00036621 ,0.00018311 ,-0.00036621 ,-0.00021362 ,-0.00027466 , -0.00018311 ,-0.00015259 ,-0.00027466 ,-0.00045776 ,-0.00042725 , -0.00027466 ,-0.00048828 ,-0.00054932 ,-0.00057983 ,-0.00042725 , -0.00036621 ,-0.00015259 ,-0.00033569 ,-0.00054932 ,-0.00082397 , -0.00042725 ,-0.00061035 ,-0.00030518 ,-0.00057983 ,-0.00024414 , -0.00045776 ,-0.00042725 ,-0.00045776 ,-0.00045776 ,-0.00048828 , -0.00036621 ,-0.00018311 ,-0.00033569 ,-0.00015259 ,-0.00042725 , -0.00027466 ,-0.00018311 ,-0.00061035 ,-0.00033569 ,-0.00012207 , -0.00030518 ,-0.00039673 ,-0.00045776 ,-0.00085449 ,-0.00045776 , -0.00045776 ,-0.00073242 ,-0.00054932 ,-0.00036621 ,-0.00045776 , -0.00033569 ,-0.00036621 ,-0.00039673 ,-0.00027466 ,-0.00061035 , -0.00036621 ,-0.00033569 ,-0.00045776 ,-0.00033569 ,-0.00024414 , -0.00033569 ,-0.00045776 ,-0.00015259 ,-0.00054932 ,-0.00012207 , -0.00033569 ,-0.00027466 ,-0.00057983 ,-0.00042725 ,-0.00033569 , -0.00027466 ,-0.00048828 ,-0.00021362 ,-0.00033569 ,-0.00027466 , -0.00057983 ,-0.00018311 ,-0.00006104 ,-0.00024414 ,-0.00067139 , -0.00009155 ,-0.00024414 ,0.00006104 ,-0.00039673 ,-0.00027466 , -0.00024414 ,-0.00012207 ,-0.00033569 ,-0.00012207 ,-0.00027466 , -0.00033569 ,-0.00039673 ,-0.00045776 ,-0.00021362 ,-0.00042725 , -0.00027466 ,-0.00024414 ,-0.00015259 ,-0.00021362 ,-0.00018311 , -0.00024414 ,-0.00015259 ,-0.00036621 ,-0.00009155 ,0.00012207 , -0.00036621 ,-0.00018311 ,0.00000000 ,-0.00003052 ,-0.00015259 , -0.00024414 ,-0.00036621 ,-0.00012207 ,-0.00006104 ,-0.00015259 , -0.00021362 ,-0.00015259 ,-0.00006104 ,-0.00039673 ,-0.00042725 , -0.00027466 ,-0.00018311 ,-0.00024414 ,-0.00036621 ,-0.00024414 , -0.00015259 ,-0.00027466 ,-0.00030518 ,-0.00012207 ,-0.00024414 , -0.00024414 ,-0.00024414 ,-0.00030518 ,-0.00003052 ,-0.00015259 , -0.00042725 ,-0.00033569 ,-0.00006104 ,-0.00051880 ,-0.00018311 , 0.00006104 ,-0.00030518 ,-0.00048828 ,-0.00030518 ,-0.00021362 , -0.00039673 ,-0.00039673 ,-0.00015259 ,-0.00009155 ,-0.00042725 , -0.00003052 ,-0.00057983 ,-0.00027466 ,-0.00036621 ,-0.00006104 , -0.00015259 ,-0.00009155 ,-0.00015259 ,-0.00030518 ,-0.00036621 , 0.00006104 ,-0.00018311 ,-0.00015259 ,-0.00021362 ,-0.00024414 , -0.00018311 ,-0.00015259 ,-0.00009155 ,-0.00006104 ,-0.00015259 , -0.00012207 ,-0.00021362 ,-0.00027466 ,-0.00036621 ,-0.00006104 , -0.00015259 ,-0.00012207 ,-0.00009155 ,-0.00027466 ,0.00024414 , -0.00009155 ,0.00000000 ,-0.00021362 ,-0.00042725 ,0.00000000 , -0.00036621 ,0.00003052 ,-0.00003052 ,-0.00057983 ,-0.00018311 , -0.00036621 ,-0.00015259 ,-0.00021362 ,-0.00021362 ,-0.00012207 , -0.00012207 ,-0.00015259 ,-0.00021362 ,-0.00045776 ,-0.00012207 , -0.00027466 ,0.00000000 ,-0.00009155 ,-0.00003052 ,-0.00003052 , 0.00012207 ,0.00009155 ,-0.00012207 ,-0.00003052 ,-0.00015259 , -0.00012207 ,-0.00012207 ,-0.00027466 ,-0.00003052 ,-0.00015259 , -0.00006104 ,-0.00036621 ,0.00018311 ,0.00024414 ,-0.00030518 , -0.00003052 ,0.00006104 ,-0.00012207 ,-0.00009155 ,-0.00054932 , -0.00036621 ,0.00000000 ,0.00003052 ,-0.00030518 ,0.00015259 , -0.00012207 ,-0.00012207 ,-0.00015259 ,-0.00024414 ,-0.00024414 , -0.00003052 ,-0.00024414 ,0.00009155 ,-0.00015259 ,0.00021362 , -0.00012207 ,0.00030518 ,-0.00006104 ,0.00003052 ,-0.00012207 , -0.00018311 ,0.00024414 ,-0.00012207 ,0.00006104 ,-0.00030518 , -0.00012207 ,-0.00006104 ,-0.00021362 ,0.00012207 ,-0.00030518 , -0.00006104 ,-0.00009155 ,-0.00009155 ,-0.00027466 ,-0.00021362 , -0.00009155 ,-0.00006104 ,-0.00006104 ,-0.00042725 ,-0.00009155 , -0.00033569 ,-0.00033569 ,-0.00024414 ,-0.00024414 ,-0.00048828 , -0.00039673 ,-0.00018311 ,-0.00015259 ,-0.00027466 ,-0.00036621 , -0.00024414 ,-0.00021362 ,-0.00027466 ,-0.00015259 ,-0.00027466 , -0.00027466 ,-0.00024414 ,-0.00015259 ,-0.00024414 ,0.00012207 , -0.00039673 ,-0.00003052 ,-0.00030518 ,-0.00024414 ,0.00012207 , -0.00048828 ,-0.00030518 ,-0.00027466 ,-0.00033569 ,-0.00051880 , -0.00027466 ,-0.00036621 ,-0.00021362 ,-0.00006104 ,0.00015259 , -0.00027466 ,-0.00015259 ,-0.00012207 ,-0.00024414 ,-0.00036621 , -0.00024414 ,-0.00030518 ,-0.00012207 ,-0.00024414 ,0.00000000 , -0.00021362 ,-0.00018311 ,-0.00030518 ,-0.00021362 ,-0.00033569 , -0.00015259 ,-0.00018311 ,0.00009155 ,-0.00003052 ,-0.00018311 , -0.00051880 ,-0.00015259 ,-0.00009155 ,-0.00009155 ,-0.00012207 , -0.00006104 ,-0.00021362 ,-0.00027466 ,0.00003052 ,-0.00003052 , -0.00009155 ,-0.00012207 ,0.00012207 ,0.00021362 ,0.00012207 , 0.00006104 ,-0.00006104 ,-0.00018311 ,0.00006104 ,-0.00015259 , 0.00021362 ,0.00003052 ,0.00015259 ,-0.00012207 ,0.00006104 , -0.00006104 ,0.00006104 ,0.00000000 ,0.00000000 ,-0.00006104 , 0.00024414 ,0.00012207 ,-0.00003052 ,0.00009155 ,0.00015259 , -0.00006104 ,0.00006104 ,0.00000000 ,0.00000000 ,-0.00006104 , -0.00018311 ,0.00024414 ,0.00012207 ,0.00003052 ,-0.00003052 , -0.00006104 ,0.00018311 ,-0.00006104 ,0.00006104 ,-0.00021362 , -0.00009155 ,-0.00018311 ,-0.00018311 ,-0.00015259 ,-0.00003052 , -0.00027466 ,-0.00018311 ,-0.00018311 ,-0.00003052 ,-0.00003052 , -0.00009155 ,-0.00003052 ,-0.00018311 ,0.00003052 ,-0.00012207 , -0.00027466 ,0.00009155 ,-0.00027466 ,0.00003052 ,-0.00030518 , -0.00018311 ,-0.00042725 ,-0.00042725 ,-0.00009155 ,-0.00006104 , -0.00018311 ,-0.00039673 ,0.00000000 ,-0.00021362 ,-0.00018311 , -0.00024414 ,-0.00009155 ,-0.00024414 ,0.00003052 ,-0.00030518 , 0.00000000 ,-0.00039673 ,0.00000000 ,0.00000000 ,0.00000000 , -0.00018311 ,-0.00030518 ,0.00000000 ,-0.00012207 ,-0.00018311 , -0.00009155 ,-0.00015259 ,-0.00027466 ,-0.00036621 ,-0.00012207 , -0.00024414 ,-0.00012207 ,-0.00015259 ,0.00006104 ,-0.00009155 , 0.00003052 ,-0.00012207 ,-0.00006104 ,-0.00048828 ,-0.00015259 , -0.00036621 ,0.00006104 ,0.00003052 ,0.00000000 ,-0.00021362 , 0.00003052 ,-0.00039673 ,-0.00021362 ,0.00000000 ,-0.00030518 , -0.00009155 ,-0.00021362 ,-0.00030518 ,-0.00018311 ,-0.00015259 , -0.00012207 ,-0.00030518 ,-0.00012207 ,-0.00018311 ,-0.00009155 , -0.00039673 ,-0.00006104 ,-0.00006104 ,-0.00003052 ,-0.00012207 , -0.00009155 ,-0.00006104 ,0.00000000 ,-0.00036621 ,-0.00024414 , -0.00009155 ,-0.00006104 ,-0.00021362 ,-0.00015259 ,0.00012207 , -0.00018311 ,0.00003052 ,0.00000000 ,-0.00009155 ,-0.00018311 , -0.00003052 ,-0.00018311 ,-0.00018311 ,-0.00003052 ,-0.00021362 , 0.00009155 ,0.00006104 ,-0.00018311 ,-0.00003052 ,-0.00030518 , -0.00021362 ,-0.00030518 ,-0.00015259 ,-0.00015259 ,0.00015259 , -0.00021362 ,0.00012207 ,0.00015259 ,-0.00027466 ,-0.00021362 , -0.00024414 ,-0.00033569 ,-0.00045776 ,-0.00039673 ,-0.00024414 , -0.00036621 ,-0.00027466 ,-0.00018311 ,-0.00021362 ,-0.00024414 , -0.00054932 ,-0.00012207 ,-0.00021362 ,-0.00009155 ,-0.00042725 , -0.00012207 ,-0.00039673 ,-0.00036621 ,-0.00024414 ,-0.00027466 , -0.00030518 ,-0.00021362 ,-0.00021362 ,-0.00036621 ,-0.00045776 , -0.00036621 ,-0.00021362 ,-0.00033569 ,-0.00036621 ,-0.00024414 , -0.00018311 ,-0.00033569 ,-0.00024414 ,-0.00015259 ,-0.00027466 , -0.00036621 ,-0.00024414 ,-0.00039673 ,-0.00015259 ,-0.00036621 , -0.00009155 ,-0.00027466 ,-0.00045776 ,-0.00027466 ,-0.00057983 , -0.00024414 ,-0.00012207 ,-0.00018311 ,-0.00021362 ,-0.00054932 , -0.00045776 ,-0.00036621 ,-0.00061035 ,-0.00033569 ,-0.00033569 , -0.00021362 ,-0.00039673 ,-0.00033569 ,-0.00051880 ,-0.00015259 , -0.00039673 ,-0.00024414 ,-0.00039673 ,-0.00048828 ,-0.00057983 , -0.00036621 ,-0.00045776 ,-0.00048828 ,-0.00027466 ,-0.00027466 , -0.00048828 ,0.00012207 ,-0.00015259 ,-0.00024414 ,-0.00033569 , -0.00012207 ,-0.00033569 ,-0.00015259 ,-0.00036621 ,-0.00018311 , -0.00039673 ,-0.00012207 ,-0.00024414 ,-0.00027466 ,-0.00027466 , -0.00018311 ,-0.00003052 ,-0.00009155 ,-0.00033569 ,-0.00039673 , -0.00006104 ,-0.00036621 ,-0.00009155 ,-0.00018311 ,-0.00009155 , -0.00009155 ,-0.00003052 ,-0.00027466 ,-0.00030518 ,-0.00006104 , -0.00009155 ,-0.00003052 ,-0.00024414 ,0.00000000 ,-0.00024414 , 0.00000000 ,-0.00036621 ,0.00000000 ,-0.00030518 ,-0.00024414 , -0.00003052 ,-0.00012207 ,0.00003052 ,-0.00015259 ,-0.00024414 , -0.00009155 ,-0.00027466 ,-0.00045776 ,-0.00027466 ,-0.00030518 , -0.00003052 ,0.00009155 ,-0.00015259 ,-0.00006104 ,-0.00006104 , -0.00024414 ,-0.00024414 ,0.00003052 ,-0.00006104 ,-0.00021362 , 0.00012207 ,-0.00006104 ,0.00000000 ,-0.00015259 ,-0.00024414 , 0.00009155 ,-0.00015259 ,0.00015259 ,-0.00012207 ,-0.00006104 , 0.00012207 ,-0.00018311 ,0.00009155 ,0.00009155 ,-0.00006104 , 0.00006104 ,-0.00024414 ,-0.00012207 ,0.00009155 ,-0.00009155 , 0.00021362 ,-0.00027466 ,-0.00006104 ,-0.00021362 ,-0.00018311 , -0.00027466 ,0.00003052 ,-0.00021362 ,-0.00006104 ,0.00003052 , -0.00021362 ,-0.00018311 ,-0.00048828 ,0.00003052 ,-0.00012207 , -0.00006104 ,-0.00039673 ,0.00003052 ,-0.00051880 ,-0.00009155 , -0.00009155 ,-0.00048828 ,-0.00012207 ,-0.00030518 ,-0.00024414 , -0.00045776 ,-0.00003052 ,-0.00003052 ,-0.00033569 ,-0.00030518 , -0.00015259 ,0.00006104 ,-0.00045776 ,-0.00021362 ,-0.00027466 , -0.00018311 ,-0.00030518 ,-0.00021362 ,-0.00033569 ,-0.00024414 , -0.00015259 ,-0.00018311 ,-0.00021362 ,-0.00054932 ,-0.00036621 , -0.00036621 ,-0.00033569 ,-0.00051880 ,-0.00030518 ,-0.00061035 , -0.00024414 ,-0.00024414 ,-0.00006104 ,-0.00012207 ,-0.00039673 , -0.00042725 ,-0.00045776 ,-0.00042725 ,-0.00042725 ,-0.00027466 , -0.00051880 ,-0.00036621 ,-0.00057983 ,-0.00036621 ,-0.00021362 , 0.00006104 ,-0.00030518 ,-0.00018311 ,-0.00067139 ,-0.00039673 , -0.00051880 ,-0.00024414 ,0.00000000 ,-0.00006104 ,-0.00042725 , -0.00021362 ,-0.00018311 ,-0.00042725 ,-0.00027466 ,-0.00015259 , -0.00021362 ,-0.00012207 ,-0.00030518 ,-0.00018311 ,-0.00030518 , -0.00018311 ,-0.00027466 ,0.00000000 ,-0.00012207 ,-0.00036621 , -0.00030518 ,-0.00024414 ,-0.00012207 ,-0.00009155 ,-0.00012207 , -0.00024414 ,-0.00076294 ,-0.00036621 ,-0.00042725 ,-0.00051880 , -0.00033569 ,-0.00018311 ,-0.00039673 ,-0.00015259 ,-0.00042725 , -0.00051880 ,-0.00039673 ,-0.00030518 ,-0.00039673 ,-0.00039673 , -0.00027466 ,-0.00048828 ,-0.00045776 ,-0.00036621 ,-0.00048828 , -0.00033569 ,-0.00051880 ,-0.00033569 ,-0.00009155 ,-0.00009155 , -0.00057983 ,-0.00042725 ,-0.00064087 ,-0.00036621 ,-0.00067139 , -0.00030518 ,-0.00033569 ,-0.00027466 ,-0.00042725 ,-0.00039673 , -0.00027466 ,-0.00024414 ,-0.00036621 ,-0.00015259 ,-0.00021362 , -0.00015259 ,-0.00021362 ,-0.00033569 ,-0.00021362 ,-0.00027466 , 0.00003052 ,-0.00006104 ,-0.00057983 ,-0.00042725 ,-0.00036621 , -0.00030518 ,-0.00021362 ,-0.00033569 ,-0.00042725 ,-0.00036621 , -0.00048828 ,-0.00018311 ,-0.00018311 ,-0.00015259 ,-0.00018311 , -0.00036621 ,-0.00045776 ,-0.00039673 ,-0.00030518 ,-0.00039673 , -0.00061035 ,-0.00027466 ,-0.00018311 ,-0.00051880 ,-0.00015259 , -0.00009155 ,-0.00048828 ,-0.00015259 ,-0.00045776 ,-0.00033569 , -0.00012207 ,-0.00057983 ,-0.00039673 ,-0.00009155 ,-0.00054932 , 0.00003052 ,-0.00036621 ,-0.00027466 ,-0.00012207 ,-0.00051880 , -0.00030518 ,-0.00027466 ,-0.00024414 ,-0.00045776 ,0.00000000 , -0.00030518 ,-0.00027466 ,-0.00024414 ,-0.00027466 ,-0.00033569 , -0.00024414 ,-0.00006104 ,-0.00015259 ,-0.00021362 ,-0.00018311 , -0.00021362 ,-0.00018311 ,-0.00042725 ,-0.00021362 ,-0.00027466 , -0.00036621 ,-0.00021362 ,-0.00003052 ,-0.00021362 ,-0.00003052 , -0.00033569 ,0.00003052 ,-0.00018311 ,-0.00021362 ,-0.00039673 , -0.00012207 ,-0.00021362 ,0.00000000 ,-0.00024414 ,-0.00024414 , -0.00024414 ,-0.00033569 ,-0.00024414 ,-0.00006104 ,-0.00015259 , -0.00024414 ,-0.00024414 ,-0.00024414 ,-0.00021362 ,-0.00042725 , -0.00051880 ,-0.00045776 ,-0.00006104 ,-0.00039673 ,-0.00039673 , -0.00015259 ,-0.00015259 ,-0.00051880 ,-0.00018311 ,-0.00012207 , -0.00027466 ,-0.00024414 ,-0.00021362 ,-0.00015259 ,-0.00061035 , -0.00030518 ,-0.00039673 ,-0.00009155 ,-0.00036621 ,-0.00042725 , -0.00045776 ,-0.00045776 ,-0.00036621 ,-0.00045776 ,-0.00018311 , -0.00045776 ,-0.00021362 ,-0.00027466 ,-0.00054932 ,-0.00048828 , -0.00036621 ,-0.00051880 ,-0.00042725 ,-0.00057983 ,-0.00030518 , -0.00045776 ,-0.00018311 ,-0.00054932 ,-0.00027466 ,-0.00039673 , -0.00048828 ,-0.00057983 ,-0.00027466 ,-0.00009155 ,-0.00051880 , -0.00045776 ,-0.00027466 ,-0.00045776 ,-0.00027466 ,-0.00039673 , -0.00027466 ,-0.00045776 ,-0.00021362 ,-0.00042725 ,-0.00036621 , -0.00024414 ,-0.00051880 ,-0.00033569 ,-0.00036621 ,-0.00018311 , -0.00057983 ,-0.00021362 ,-0.00051880 ,-0.00039673 ,-0.00036621 , -0.00039673 ,-0.00027466 ,-0.00033569 ,-0.00006104 ,-0.00033569 , 0.00009155 ,-0.00045776 ,-0.00027466 ,-0.00045776 ,-0.00018311 , -0.00024414 ,-0.00048828 ,-0.00012207 ,-0.00033569 ,-0.00027466 , -0.00021362 ,-0.00045776 ,-0.00024414 ,-0.00027466 ,-0.00018311 , -0.00036621 ,-0.00042725 ,-0.00042725 ,-0.00051880 ,-0.00036621 , -0.00048828 ,-0.00015259 ,-0.00045776 ,-0.00024414 ,-0.00042725 , -0.00030518 ,-0.00051880 ,-0.00027466 ,-0.00033569 ,-0.00039673 , -0.00033569 ,-0.00030518 ,-0.00030518 ,-0.00030518 ,-0.00057983 , -0.00012207 ,-0.00054932 ,-0.00030518 ,-0.00057983 ,-0.00006104 , -0.00070190 ,-0.00027466 ,-0.00018311 ,-0.00024414 ,-0.00036621 , -0.00039673 ,-0.00030518 ,-0.00024414 ,-0.00027466 ,-0.00045776 , -0.00033569 ,-0.00064087 ,-0.00039673 ,-0.00064087 ,-0.00027466 , -0.00057983 ,-0.00030518 ,-0.00027466 ,-0.00030518 ,-0.00064087 , -0.00027466 ,-0.00045776 ,-0.00036621 ,-0.00048828 ,-0.00042725 , -0.00057983 ,-0.00045776 ,-0.00042725 ,-0.00061035 ,-0.00027466 , -0.00067139 ,-0.00051880 ,-0.00079346 ,-0.00018311 ,-0.00039673 , -0.00082397 ,-0.00057983 ,-0.00042725 ,-0.00048828 ,-0.00073242 , -0.00064087 ,-0.00051880 ,-0.00094604 ,-0.00079346 ,-0.00057983 , -0.00057983 ,-0.00051880 ,-0.00061035 ,-0.00057983 ,-0.00082397 , -0.00045776 ,-0.00067139 ,-0.00048828 ,-0.00082397 ,-0.00048828 , -0.00057983 ,-0.00067139 ,-0.00054932 ,-0.00057983 ,-0.00042725 , -0.00076294 ,-0.00036621 ,-0.00070190 ,-0.00045776 ,-0.00051880 , -0.00061035 ,-0.00054932 ,-0.00085449 ,-0.00042725 ,-0.00085449 , -0.00033569 ,-0.00067139 ,-0.00061035 ,-0.00073242 ,-0.00067139 , -0.00057983 ,-0.00067139 ,-0.00051880 ,-0.00061035 ,-0.00042725 , -0.00082397 ,-0.00064087 ,-0.00070190 ,-0.00054932 ,-0.00076294 , -0.00088501 ,-0.00057983 ,-0.00061035 ,-0.00051880 ,-0.00070190 , -0.00091553 ,-0.00054932 ,-0.00030518 ,-0.00079346 ,-0.00064087 , -0.00073242 ,-0.00070190 ,-0.00067139 ,-0.00070190 ,-0.00048828 , -0.00057983 ,-0.00042725 ,-0.00070190 ,-0.00064087 ,-0.00036621 , -0.00067139 ,-0.00064087 ,-0.00027466 ,-0.00048828 ,-0.00009155 , -0.00048828 ,-0.00045776 ,-0.00033569 ,-0.00064087 ,-0.00042725 , -0.00054932 ,-0.00039673 ,-0.00045776 ,-0.00042725 ,-0.00036621 , -0.00045776 ,-0.00033569 ,-0.00073242 ,-0.00045776 ,-0.00064087 , -0.00027466 ,-0.00070190 ,-0.00045776 ,-0.00070190 ,-0.00048828 , -0.00042725 ,-0.00042725 ,-0.00045776 ,-0.00057983 ,-0.00076294 , -0.00064087 ,-0.00054932 ,-0.00067139 ,-0.00036621 ,-0.00061035 , -0.00045776 ,-0.00054932 ,-0.00061035 ,-0.00048828 ,-0.00073242 , -0.00033569 ,-0.00045776 ,-0.00054932 ,-0.00039673 ,-0.00057983 , -0.00048828 ,-0.00064087 ,-0.00030518 ,-0.00051880 ,-0.00048828 , -0.00039673 ,-0.00039673 ,-0.00030518 ,-0.00070190 ,-0.00051880 , -0.00039673 ,-0.00073242 ,-0.00018311 ,-0.00027466 ,-0.00033569 , -0.00042725 ,-0.00039673 ,-0.00051880 ,-0.00027466 ,-0.00045776 , -0.00033569 ,-0.00070190 ,-0.00042725 ,-0.00054932 ,-0.00030518 , -0.00030518 ,-0.00027466 ,-0.00051880 ,-0.00033569 ,-0.00054932 , -0.00012207 ,-0.00048828 ,-0.00024414 ,-0.00051880 ,-0.00042725 , -0.00033569 ,-0.00030518 ,-0.00030518 ,-0.00015259 ,-0.00045776 , -0.00048828 ,-0.00051880 ,-0.00048828 ,-0.00009155 ,-0.00045776 , -0.00039673 ,-0.00033569 ,-0.00018311 ,-0.00054932 ,-0.00054932 , -0.00039673 ,-0.00051880 ,-0.00048828 ,-0.00057983 ,-0.00039673 , -0.00048828 ,-0.00018311 ,-0.00039673 ,-0.00021362 ,-0.00030518 , -0.00027466 ,-0.00012207 ,-0.00051880 ,-0.00039673 ,-0.00030518 , -0.00045776 ,-0.00051880 ,-0.00042725 ,-0.00030518 ,-0.00033569 , -0.00024414 ,-0.00039673 ,-0.00036621 ,-0.00064087 ,-0.00015259 , -0.00027466 ,-0.00042725 ,-0.00042725 ,-0.00018311 ,-0.00024414 , -0.00033569 ,-0.00033569 ,-0.00018311 ,-0.00033569 ,-0.00027466 , -0.00027466 ,-0.00039673 ,-0.00030518 ,-0.00030518 ,-0.00015259 , -0.00006104 ,-0.00015259 ,-0.00030518 ,-0.00015259 ,-0.00033569 , -0.00033569 ,-0.00033569 ,-0.00036621 ,-0.00054932 ,-0.00039673 , -0.00003052 ,-0.00036621 ,-0.00018311 ,-0.00012207 ,-0.00030518 , -0.00027466 ,-0.00036621 ,-0.00036621 ,-0.00042725 ,-0.00042725 , -0.00006104 ,-0.00039673 ,-0.00036621 ,-0.00027466 ,-0.00048828 , -0.00012207 ,-0.00045776 ,-0.00027466 ,-0.00048828 ,-0.00045776 , -0.00067139 ,-0.00048828 ,-0.00039673 ,-0.00036621 ,-0.00018311 , -0.00033569 ,-0.00015259 ,-0.00027466 ,-0.00045776 ,-0.00061035 , -0.00051880 ,-0.00027466 ,-0.00039673 ,-0.00042725 ,-0.00039673 , -0.00021362 ,-0.00030518 ,-0.00009155 ,-0.00039673 ,-0.00021362 , -0.00018311 ,-0.00042725 ,-0.00021362 ,-0.00057983 ,-0.00033569 , -0.00045776 ,-0.00024414 ,-0.00036621 ,-0.00042725 ,-0.00039673 , -0.00009155 ,-0.00036621 ,-0.00015259 ,-0.00033569 ,-0.00027466 , -0.00030518 ,-0.00027466 ,-0.00021362 ,-0.00033569 ,-0.00036621 , -0.00015259 ,-0.00015259 ,-0.00027466 ,-0.00030518 ,-0.00024414 , -0.00030518 ,-0.00039673 ,-0.00024414 ,0.00003052 ,-0.00042725 , -0.00051880 ,-0.00015259 ,-0.00030518 ,0.00003052 ,-0.00045776 , -0.00027466 ,-0.00030518 ,-0.00015259 ,-0.00042725 ,-0.00018311 , -0.00030518 ,-0.00039673 ,-0.00018311 ,-0.00015259 ,-0.00015259 , -0.00021362 ,-0.00006104 ,-0.00006104 ,-0.00027466 ,-0.00027466 , -0.00015259 ,-0.00021362 ,0.00009155 ,-0.00036621 ,-0.00021362 , -0.00018311 ,-0.00015259 ,-0.00024414 ,-0.00024414 ,0.00012207 , -0.00021362 ,-0.00009155 ,-0.00009155 ,-0.00006104 ,-0.00009155 , 0.00003052 ,0.00003052 ,-0.00039673 ,-0.00006104 ,-0.00030518 , 0.00006104 ,-0.00030518 ,-0.00012207 ,-0.00009155 ,-0.00021362 , 0.00009155 ,-0.00015259 ,-0.00021362 ,0.00003052 ,-0.00048828 , 0.00009155 ,-0.00015259 ,-0.00024414 ,-0.00027466 ,0.00021362 , 0.00003052 ,-0.00015259 ,-0.00003052 ,0.00006104 ,-0.00009155 , -0.00045776 ,-0.00021362 ,-0.00033569 ,-0.00006104 ,-0.00024414 , -0.00012207 ,-0.00027466 ,-0.00033569 ,-0.00006104 ,-0.00027466 , 0.00006104 ,0.00000000 ,-0.00012207 ,-0.00030518 ,-0.00015259 , -0.00009155 ,-0.00003052 ,-0.00009155 ,-0.00012207 ,-0.00027466 , -0.00042725 ,-0.00033569 ,-0.00027466 ,-0.00015259 ,-0.00033569 , -0.00027466 ,-0.00012207 ,-0.00024414 ,-0.00033569 ,-0.00027466 , -0.00027466 ,-0.00039673 ,-0.00030518 ,0.00009155 ,-0.00021362 , 0.00003052 ,-0.00030518 ,-0.00024414 ,-0.00039673 ,-0.00018311 , -0.00033569 ,-0.00018311 ,-0.00015259 ,-0.00006104 ,-0.00012207 , -0.00012207 ,-0.00003052 ,-0.00012207 ,0.00000000 ,-0.00015259 , 0.00015259 ,0.00003052 ,0.00003052 ,0.00006104 ,0.00006104 , -0.00012207 ,0.00006104 ,-0.00015259 ,0.00003052 ,0.00000000 , -0.00012207 ,-0.00027466 ,0.00006104 ,-0.00051880 ,-0.00021362 , -0.00018311 ,-0.00027466 ,-0.00024414 ,-0.00036621 ,-0.00018311 , -0.00009155 ,-0.00006104 ,-0.00015259 ,0.00021362 ,-0.00030518 , -0.00021362 ,0.00009155 ,-0.00027466 ,0.00006104 ,-0.00006104 , -0.00012207 ,-0.00015259 ,0.00006104 ,-0.00021362 ,0.00009155 , 0.00003052 ,-0.00009155 ,-0.00003052 ,-0.00021362 ,0.00003052 , -0.00024414 ,0.00006104 ,-0.00012207 ,-0.00018311 ,0.00000000 , 0.00012207 ,0.00006104 ,0.00006104 ,-0.00021362 ,-0.00021362 , -0.00021362 ,0.00000000 ,-0.00012207 ,-0.00018311 ,-0.00009155 , -0.00006104 ,0.00021362 ,-0.00012207 ,0.00024414 ,-0.00021362 , 0.00003052 ,0.00012207 ,0.00024414 ,-0.00003052 ,-0.00015259 , -0.00015259 ,0.00027466 ,0.00003052 ,0.00000000 ,0.00003052 , -0.00018311 ,-0.00012207 ,0.00000000 ,-0.00051880 ,-0.00006104 , -0.00018311 ,-0.00024414 ,-0.00045776 ,-0.00009155 ,-0.00012207 , -0.00024414 ,-0.00009155 ,-0.00039673 ,-0.00018311 ,-0.00021362 , -0.00024414 ,-0.00033569 ,-0.00030518 ,-0.00036621 ,-0.00051880 , -0.00027466 ,-0.00027466 ,-0.00045776 ,-0.00027466 ,-0.00042725 , -0.00024414 ,-0.00036621 ,-0.00018311 ,-0.00015259 ,-0.00018311 , -0.00024414 ,-0.00018311 ,-0.00024414 ,-0.00021362 ,-0.00039673 , -0.00027466 ,-0.00021362 ,-0.00039673 ,-0.00033569 ,-0.00042725 , -0.00006104 ,-0.00064087 ,-0.00030518 ,-0.00024414 ,-0.00048828 , -0.00024414 ,-0.00033569 ,-0.00009155 ,-0.00030518 ,-0.00057983 , -0.00039673 ,-0.00039673 ,-0.00015259 ,-0.00015259 ,-0.00039673 , -0.00018311 ,-0.00054932 ,-0.00039673 ,-0.00039673 ,-0.00033569 , -0.00039673 ,-0.00045776 ,0.00000000 ,-0.00018311 ,-0.00027466 , -0.00024414 ,-0.00027466 ,-0.00036621 ,-0.00036621 ,-0.00036621 , -0.00027466 ,-0.00033569 ,-0.00024414 ,-0.00048828 ,-0.00030518 , -0.00012207 ,-0.00048828 ,-0.00015259 ,-0.00021362 ,-0.00021362 , 0.00000000 ,-0.00012207 ,-0.00030518 ,0.00006104 ,-0.00018311 , 0.00000000 ,-0.00024414 ,-0.00045776 ,-0.00045776 ,-0.00033569 , -0.00024414 ,-0.00057983 ,-0.00027466 ,0.00006104 ,-0.00051880 , -0.00042725 ,-0.00006104 ,-0.00030518 ,-0.00024414 ,-0.00024414 , -0.00009155 ,-0.00036621 ,-0.00015259 ,-0.00021362 ,-0.00015259 , -0.00027466 ,-0.00024414 ,-0.00027466 ,-0.00006104 ,-0.00027466 , -0.00036621 ,-0.00012207 ,0.00003052 ,-0.00021362 ,-0.00024414 , -0.00030518 ,-0.00027466 ,-0.00036621 ,-0.00024414 ,-0.00018311 , -0.00036621 ,-0.00027466 ,-0.00027466 ,-0.00021362 ,-0.00021362 , -0.00021362 ,-0.00036621 ,-0.00039673 ,-0.00027466 ,-0.00036621 , -0.00033569 ,-0.00039673 ,-0.00036621 ,-0.00015259 ,-0.00033569 , -0.00012207 ,-0.00036621 ,-0.00033569 ,-0.00015259 ,-0.00021362 , -0.00076294 ,-0.00048828 ,-0.00039673 ,-0.00021362 ,-0.00018311 , -0.00030518 ,-0.00027466 ,-0.00045776 ,-0.00039673 ,-0.00024414 , -0.00015259 ,-0.00039673 ,-0.00033569 ,-0.00003052 ,-0.00054932 , -0.00042725 ,-0.00042725 ,-0.00042725 ,-0.00042725 ,-0.00057983 , -0.00042725 ,-0.00027466 ,-0.00054932 ,-0.00039673 ,-0.00048828 , -0.00057983 ,-0.00018311 ,-0.00057983 ,-0.00057983 ,-0.00048828 , -0.00048828 ,-0.00061035 ,-0.00045776 ,-0.00054932 ,-0.00021362 , -0.00054932 ,-0.00042725 ,-0.00070190 ,-0.00039673 ,-0.00045776 , -0.00048828 ,-0.00036621 ,-0.00042725 ,-0.00057983 ,-0.00030518 , -0.00018311 ,-0.00061035 ,-0.00039673 ,-0.00054932 ,-0.00036621 , -0.00036621 ,-0.00051880 ,-0.00024414 ,-0.00021362 ,-0.00054932 , -0.00018311 ,-0.00036621 ,-0.00061035 ,-0.00045776 ,-0.00021362 , -0.00048828 ,-0.00045776 ,-0.00030518 ,-0.00054932 ,-0.00045776 , -0.00036621 ,-0.00054932 ,-0.00057983 ,-0.00057983 ,-0.00045776 , -0.00045776 ,-0.00030518 ,-0.00042725 ,-0.00018311 ,-0.00021362 , -0.00042725 ,-0.00003052 ,-0.00033569 ,-0.00015259 ,-0.00024414 , -0.00015259 ,-0.00018311 ,-0.00015259 ,-0.00024414 ,-0.00027466 , -0.00018311 ,-0.00036621 ,0.00006104 ,-0.00021362 ,-0.00015259 , -0.00033569 ,-0.00042725 ,-0.00036621 ,-0.00030518 ,-0.00018311 , -0.00042725 ,-0.00045776 ,-0.00024414 ,-0.00018311 ,-0.00061035 , -0.00021362 ,-0.00024414 ,-0.00015259 ,-0.00021362 ,-0.00006104 , -0.00039673 ,-0.00015259 ,-0.00015259 ,-0.00006104 ,-0.00009155 , -0.00012207 ,-0.00018311 ,-0.00009155 ,-0.00027466 ,-0.00012207 , -0.00015259 ,-0.00045776 ,-0.00006104 ,-0.00009155 ,-0.00015259 , -0.00015259 ,-0.00009155 ,-0.00024414 ,-0.00015259 ,-0.00003052 , -0.00024414 ,-0.00027466 ,-0.00039673 ,-0.00036621 ,0.00006104 , -0.00027466 ,-0.00015259 ,-0.00027466 ,-0.00030518 ,-0.00027466 , -0.00024414 ,-0.00030518 ,-0.00030518 ,-0.00015259 ,-0.00024414 , -0.00006104 ,-0.00027466 ,-0.00009155 ,-0.00030518 ,-0.00024414 , -0.00024414 ,-0.00027466 ,-0.00018311 ,-0.00018311 ,-0.00012207 , -0.00012207 ,-0.00021362 ,-0.00024414 ,-0.00009155 ,-0.00003052 , -0.00036621 ,0.00018311 ,-0.00030518 ,-0.00009155 ,-0.00033569 , -0.00021362 ,-0.00003052 ,-0.00006104 ,0.00000000 ,-0.00003052 , 0.00006104 ,-0.00030518 ,-0.00042725 ,-0.00024414 ,-0.00015259 , -0.00003052 ,-0.00009155 ,-0.00021362 ,0.00003052 ,-0.00018311 , 0.00006104 ,-0.00006104 ,-0.00021362 ,0.00000000 ,0.00006104 , -0.00018311 ,-0.00021362 ,0.00000000 ,-0.00003052 ,0.00003052 , -0.00009155 ,-0.00015259 ,0.00003052 ,-0.00024414 ,0.00024414 , -0.00003052 ,0.00015259 ,-0.00015259 ,-0.00027466 ,-0.00012207 , -0.00003052 ,-0.00021362 ,0.00009155 ,-0.00006104 ,0.00015259 , -0.00012207 ,-0.00012207 ,-0.00006104 ,-0.00015259 ,-0.00012207 , -0.00018311 ,-0.00003052 ,-0.00015259 ,-0.00021362 ,0.00012207 , -0.00009155 ,-0.00006104 ,-0.00027466 ,-0.00045776 ,-0.00054932 , -0.00006104 ,-0.00012207 ,-0.00039673 ,-0.00027466 ,-0.00009155 , -0.00024414 ,-0.00030518 ,-0.00015259 ,-0.00015259 ,-0.00036621 , -0.00033569 ,-0.00042725 ,-0.00009155 ,-0.00027466 ,0.00000000 , -0.00018311 ,-0.00015259 ,-0.00021362 ,-0.00039673 ,-0.00036621 , -0.00042725 ,-0.00042725 ,-0.00036621 ,-0.00042725 ,-0.00015259 , -0.00030518 ,-0.00036621 ,-0.00030518 ,-0.00030518 ,-0.00024414 , -0.00036621 ,-0.00054932 ,-0.00033569 ,-0.00039673 ,-0.00054932 , -0.00042725 ,-0.00054932 ,-0.00036621 ,-0.00042725 ,-0.00018311 , -0.00054932 ,-0.00045776 ,-0.00033569 ,-0.00036621 ,-0.00048828 , -0.00039673 ,-0.00036621 ,-0.00027466 ,-0.00015259 ,-0.00042725 , -0.00024414 ,-0.00045776 ,-0.00042725 ,-0.00027466 ,-0.00030518 , -0.00033569 ,-0.00057983 ,-0.00021362 ,-0.00012207 ,-0.00030518 , -0.00033569 ,-0.00033569 ,-0.00015259 ,-0.00045776 ,-0.00039673 , -0.00021362 ,-0.00033569 ,-0.00018311 ,-0.00012207 ,-0.00018311 , -0.00006104 ,-0.00009155 ,-0.00012207 ,-0.00018311 ,-0.00024414 , -0.00036621 ,-0.00033569 ,-0.00027466 ,-0.00006104 ,-0.00009155 , -0.00021362 ,-0.00015259 ,-0.00009155 ,-0.00009155 ,-0.00012207 , -0.00033569 ,0.00018311 ,-0.00045776 ,0.00018311 ,-0.00009155 , 0.00009155 ,0.00003052 ,0.00000000 ,0.00006104 ,-0.00009155 , -0.00003052 ,0.00003052 ,-0.00015259 ,0.00006104 ,-0.00018311 , 0.00003052 ,-0.00003052 ,0.00006104 ,0.00000000 ,0.00015259 , 0.00003052 ,0.00012207 ,-0.00012207 ,0.00000000 ,-0.00018311 , 0.00009155 ,-0.00012207 ,-0.00012207 ,0.00000000 ,-0.00006104 , -0.00021362 ,0.00003052 ,-0.00018311 ,0.00003052 ,-0.00045776 , 0.00015259 ,-0.00018311 ,-0.00003052 ,-0.00006104 ,-0.00042725 , 0.00009155 ,0.00024414 ,-0.00003052 ,-0.00021362 ,-0.00030518 , -0.00039673 ,-0.00036621 ,-0.00018311 ,-0.00009155 ,-0.00015259 , -0.00015259 ,-0.00021362 ,-0.00018311 ,-0.00018311 ,-0.00015259 , 0.00003052 ,-0.00021362 ,-0.00003052 ,-0.00018311 ,0.00000000 , -0.00009155 ,-0.00009155 ,0.00000000 ,-0.00006104 ,-0.00033569 , -0.00024414 ,-0.00033569 ,-0.00015259 ,-0.00006104 ,-0.00030518 , -0.00015259 ,-0.00027466 ,-0.00021362 ,-0.00021362 ,0.00000000 , -0.00021362 ,-0.00027466 ,-0.00030518 ,-0.00018311 ,-0.00039673 , -0.00009155 ,-0.00039673 ,-0.00036621 ,-0.00027466 ,-0.00039673 , -0.00024414 ,-0.00021362 ,-0.00006104 ,0.00000000 ,-0.00042725 , 0.00006104 ,-0.00030518 ,-0.00009155 ,-0.00042725 ,-0.00033569 , -0.00054932 ,-0.00012207 ,-0.00030518 ,-0.00036621 ,-0.00021362 , -0.00033569 ,-0.00033569 ,-0.00015259 ,-0.00012207 ,-0.00021362 , -0.00039673 ,-0.00015259 ,-0.00048828 ,-0.00018311 ,-0.00027466 , -0.00006104 ,-0.00018311 ,-0.00030518 ,-0.00015259 ,-0.00048828 , -0.00009155 ,-0.00036621 ,-0.00018311 ,-0.00033569 ,-0.00045776 , -0.00024414 ,-0.00009155 ,-0.00030518 ,-0.00027466 ,-0.00030518 , -0.00033569 ,-0.00048828 ,-0.00015259 ,-0.00064087 ,-0.00018311 , -0.00045776 ,-0.00015259 ,-0.00045776 ,-0.00048828 ,-0.00039673 , -0.00039673 ,-0.00033569 ,-0.00024414 ,-0.00021362 ,-0.00064087 , -0.00061035 ,-0.00030518 ,-0.00018311 ,-0.00009155 ,-0.00027466 , -0.00030518 ,-0.00036621 ,-0.00021362 ,-0.00036621 ,-0.00009155 , -0.00033569 ,-0.00030518 ,-0.00021362 ,-0.00039673 ,0.00009155 , -0.00039673 ,-0.00027466 ,0.00009155 ,-0.00003052 ,-0.00033569 , -0.00024414 ,-0.00009155 ,-0.00021362 ,-0.00033569 ,-0.00003052 , -0.00021362 ,-0.00018311 ,-0.00024414 ,-0.00030518 ,-0.00042725 , -0.00015259 ,-0.00033569 ,0.00000000 ,-0.00030518 ,-0.00027466 , -0.00027466 ,-0.00015259 ,-0.00042725 ,-0.00006104 ,-0.00015259 , -0.00006104 ,0.00000000 ,-0.00033569 ,-0.00018311 ,-0.00027466 , 0.00000000 ,-0.00048828 ,-0.00018311 ,-0.00015259 ,-0.00027466 , -0.00015259 ,-0.00036621 ,-0.00027466 ,-0.00024414 ,-0.00015259 , -0.00015259 ,-0.00006104 ,-0.00027466 ,0.00009155 ,-0.00015259 , 0.00012207 ,-0.00042725 ,-0.00003052 ,-0.00003052 ,-0.00021362 , -0.00027466 ,-0.00036621 ,-0.00015259 ,-0.00024414 ,-0.00003052 , -0.00045776 ,-0.00021362 ,-0.00033569 ,-0.00024414 ,-0.00012207 , -0.00033569 ,-0.00036621 ,-0.00048828 ,-0.00015259 ,-0.00024414 , -0.00003052 ,-0.00024414 ,-0.00006104 ,-0.00024414 ,-0.00036621 , -0.00036621 ,-0.00039673 ,-0.00042725 ,-0.00015259 ,-0.00064087 , -0.00036621 ,-0.00015259 ,-0.00042725 ,-0.00021362 ,-0.00061035 , -0.00030518 ,-0.00045776 ,-0.00030518 ,-0.00042725 ,-0.00024414 , -0.00039673 ,0.00000000 ,-0.00054932 ,-0.00030518 ,-0.00042725 , -0.00042725 ,-0.00009155 ,-0.00036621 ,-0.00030518 ,-0.00030518 , -0.00042725 ,-0.00018311 ,-0.00039673 ,-0.00012207 ,-0.00036621 , -0.00003052 ,-0.00030518 ,-0.00018311 ,-0.00021362 ,-0.00057983 , -0.00021362 ,-0.00039673 ,-0.00030518 ,-0.00033569 ,-0.00027466 , -0.00036621 ,-0.00051880 ,-0.00018311 ,-0.00030518 ,-0.00024414 , -0.00054932 ,0.00000000 ,-0.00048828 ,-0.00024414 ,-0.00057983 , -0.00018311 ,-0.00067139 ,-0.00048828 ,-0.00027466 ,-0.00054932 , -0.00030518 ,-0.00048828 ,-0.00048828 ,-0.00048828 ,-0.00073242 , -0.00033569 ,-0.00036621 ,-0.00006104 ,-0.00067139 ,-0.00033569 , -0.00048828 ,-0.00027466 ,-0.00064087 ,-0.00042725 ,-0.00051880 , -0.00057983 ,-0.00057983 ,-0.00042725 ,-0.00045776 ,-0.00045776 , -0.00039673 ,-0.00054932 ,-0.00039673 ,-0.00048828 ,-0.00042725 , -0.00030518 ,-0.00045776 ,-0.00030518 ,-0.00042725 ,-0.00042725 , -0.00021362 ,-0.00033569 ,-0.00048828 ,-0.00042725 ,-0.00036621 , -0.00033569 ,-0.00030518 ,-0.00033569 ,-0.00003052 ,-0.00024414 , -0.00036621 ,-0.00045776 ,-0.00036621 ,-0.00057983 ,-0.00030518 , -0.00051880 ,-0.00039673 ,-0.00064087 ,-0.00033569 ,-0.00015259 , -0.00021362 ,-0.00057983 ,-0.00061035 ,-0.00070190 ,-0.00030518 , -0.00027466 ,-0.00042725 ,-0.00042725 ,-0.00024414 ,-0.00036621 , -0.00054932 ,-0.00015259 ,-0.00042725 ,-0.00036621 ,-0.00021362 , -0.00045776 ,-0.00039673 ,-0.00048828 ,-0.00021362 ,-0.00006104 , -0.00045776 ,-0.00030518 ,-0.00045776 ,-0.00039673 ,0.00006104 , -0.00021362 ,-0.00018311 ,-0.00042725 ,0.00000000 ,-0.00003052 , -0.00045776 ,-0.00036621 ,-0.00012207 ,-0.00012207 ,-0.00012207 , -0.00024414 ,-0.00003052 ,-0.00036621 ,-0.00030518 ,-0.00012207 , -0.00030518 ,-0.00015259 ,-0.00021362 ,-0.00009155 ,-0.00033569 , -0.00021362 ,-0.00009155 ,-0.00015259 ,-0.00006104 ,-0.00018311 , -0.00021362 ,-0.00006104 ,-0.00009155 ,-0.00012207 ,-0.00018311 , 0.00009155 ,-0.00012207 ,-0.00006104 ,0.00024414 ,-0.00012207 , -0.00030518 ,-0.00021362 ,-0.00021362 ,-0.00018311 ,-0.00018311 , -0.00021362 ,-0.00015259 ,0.00006104 ,-0.00003052 ,-0.00006104 , 0.00003052 ,-0.00012207 ,-0.00024414 ,-0.00021362 ,-0.00006104 , -0.00042725 ,-0.00015259 ,-0.00033569 ,-0.00012207 ,-0.00006104 , -0.00030518 ,-0.00018311 ,0.00006104 ,-0.00009155 ,-0.00003052 , -0.00009155 ,0.00012207 ,-0.00021362 ,-0.00012207 ,-0.00006104 , -0.00003052 ,-0.00021362 ,-0.00012207 ,-0.00018311 ,0.00012207 , -0.00009155 ,-0.00024414 ,-0.00009155 ,-0.00024414 ,-0.00009155 , -0.00015259 ,-0.00009155 ,-0.00003052 ,-0.00042725 ,-0.00009155 , -0.00030518 ,-0.00018311 ,-0.00021362 ,-0.00009155 ,-0.00021362 , -0.00051880 ,-0.00024414 ,-0.00030518 ,-0.00030518 ,-0.00036621 , -0.00021362 ,-0.00042725 ,0.00000000 ,-0.00033569 ,-0.00015259 , -0.00015259 ,-0.00024414 ,0.00003052 ,-0.00018311 ,-0.00009155 , -0.00030518 ,-0.00006104 ,-0.00030518 ,-0.00033569 ,-0.00021362 , -0.00006104 ,-0.00009155 ,-0.00015259 ,-0.00003052 ,-0.00018311 , 0.00003052 ,-0.00033569 ,0.00003052 ,-0.00015259 ,-0.00009155 , -0.00021362 ,-0.00009155 ,0.00000000 ,0.00000000 ,0.00003052 , -0.00006104 ,-0.00018311 ,-0.00015259 ,-0.00006104 ,-0.00012207 , -0.00009155 ,-0.00018311 ,0.00000000 ,0.00015259 ,0.00000000 , -0.00009155 ,-0.00003052 ,-0.00009155 ,0.00018311 ,-0.00003052 , -0.00009155 ,-0.00006104 ,0.00006104 ,0.00000000 ,-0.00009155 , -0.00003052 ,0.00009155 ,-0.00009155 ,-0.00009155 ,-0.00024414 , 0.00012207 ,-0.00003052 ,-0.00003052 ,-0.00003052 ,-0.00006104 , -0.00009155 ,-0.00006104 ,-0.00003052 ,-0.00009155 ,0.00015259 , -0.00006104 ,0.00030518 ,-0.00003052 ,-0.00006104 ,-0.00018311 , 0.00003052 ,-0.00009155 ,0.00003052 ,0.00021362 ,0.00018311 , -0.00003052 ,0.00024414 ,-0.00015259 ,-0.00012207 ,0.00018311 , -0.00003052 ,0.00003052 ,-0.00015259 ,-0.00006104 ,-0.00009155 , -0.00012207 ,0.00015259 ,0.00015259 ,-0.00012207 ,0.00003052 , 0.00000000 ,-0.00024414 ,-0.00009155 ,-0.00015259 ,-0.00006104 , 0.00027466 ,-0.00018311 ,0.00015259 ,-0.00024414 ,-0.00018311 , -0.00009155 ,0.00006104 ,-0.00015259 ,-0.00003052 ,-0.00003052 , 0.00009155 ,-0.00006104 ,-0.00018311 ,0.00006104 ,0.00012207 , -0.00009155 ,-0.00009155 ,-0.00018311 ,-0.00009155 ,-0.00012207 , -0.00012207 ,0.00009155 ,-0.00018311 ,-0.00009155 ,-0.00018311 , 0.00003052 ,-0.00009155 ,0.00009155 ,-0.00006104 ,0.00003052 , -0.00009155 ,0.00006104 ,0.00009155 ,0.00003052 ,0.00000000 , -0.00006104 ,-0.00030518 ,0.00000000 ,-0.00015259 ,0.00003052 , 0.00024414 ,-0.00015259 ,0.00006104 ,0.00018311 ,0.00009155 , -0.00003052 ,0.00006104 ,0.00015259 ,0.00000000 ,0.00015259 , 0.00003052 ,0.00009155 ,0.00006104 ,0.00027466 ,-0.00003052 , 0.00006104 ,0.00006104 ,0.00030518 ,-0.00006104 ,-0.00009155 , -0.00024414 ,-0.00009155 ,0.00018311 ,-0.00012207 ,0.00003052 , 0.00006104 ,0.00006104 ,-0.00042725 ,-0.00009155 ,0.00009155 , -0.00015259 ,0.00006104 ,-0.00003052 ,0.00006104 ,0.00012207 , -0.00018311 ,0.00003052 ,-0.00027466 ,-0.00012207 ,-0.00012207 , -0.00012207 ,0.00024414 ,-0.00012207 ,-0.00003052 ,0.00012207 , -0.00015259 ,-0.00006104 ,-0.00009155 ,0.00015259 ,-0.00015259 , 0.00003052 ,0.00021362 ,-0.00003052 ,-0.00027466 ,-0.00012207 , -0.00024414 ,0.00000000 ,-0.00024414 ,-0.00009155 ,-0.00024414 , -0.00015259 ,-0.00006104 ,-0.00006104 ,-0.00027466 ,-0.00003052 , 0.00006104 ,-0.00018311 ,-0.00015259 ,-0.00003052 ,-0.00012207 , -0.00027466 ,0.00000000 ,0.00000000 ,-0.00030518 ,-0.00012207 , -0.00015259 ,-0.00009155 ,-0.00003052 ,-0.00021362 ,0.00012207 , -0.00036621 ,-0.00024414 ,-0.00024414 ,0.00009155 ,-0.00030518 , 0.00006104 ,-0.00024414 ,-0.00021362 ,-0.00012207 ,-0.00018311 , -0.00012207 ,-0.00024414 ,-0.00012207 ,-0.00009155 ,-0.00009155 , -0.00012207 ,0.00009155 ,-0.00036621 ,-0.00009155 ,-0.00030518 , -0.00015259 ,-0.00027466 ,-0.00039673 ,-0.00006104 ,-0.00024414 , -0.00015259 ,-0.00003052 ,-0.00012207 ,-0.00003052 ,-0.00057983 , -0.00009155 ,-0.00048828 ,0.00000000 ,-0.00042725 ,-0.00033569 , -0.00030518 ,-0.00027466 ,-0.00036621 ,-0.00033569 ,-0.00027466 , -0.00033569 ,-0.00045776 ,0.00003052 ,-0.00036621 ,-0.00027466 , -0.00018311 ,-0.00042725 ,-0.00003052 ,-0.00021362 ,-0.00006104 , -0.00024414 ,-0.00036621 ,0.00000000 ,-0.00039673 ,-0.00003052 , -0.00027466 ,-0.00018311 ,-0.00042725 ,-0.00012207 ,-0.00039673 , -0.00018311 ,-0.00027466 ,-0.00006104 ,-0.00021362 ,-0.00012207 , -0.00012207 ,-0.00030518 ,-0.00006104 ,-0.00012207 ,0.00003052 , -0.00021362 ,-0.00045776 ,-0.00003052 ,-0.00018311 ,-0.00033569 , -0.00024414 ,-0.00018311 ,-0.00006104 ,-0.00009155 ,-0.00021362 , -0.00030518 ,-0.00006104 ,-0.00045776 ,-0.00015259 ,-0.00006104 , -0.00021362 ,-0.00018311 ,-0.00030518 ,-0.00006104 ,-0.00030518 , 0.00000000 ,-0.00027466 ,-0.00012207 ,-0.00027466 ,-0.00024414 , -0.00018311 ,-0.00024414 ,-0.00006104 ,-0.00018311 ,-0.00027466 , -0.00021362 ,-0.00012207 ,-0.00012207 ,-0.00003052 ,-0.00030518 , -0.00015259 ,-0.00042725 ,-0.00021362 ,-0.00003052 ,-0.00027466 , -0.00033569 ,-0.00012207 ,0.00003052 ,-0.00006104 ,-0.00045776 , -0.00012207 ,-0.00009155 ,-0.00027466 ,-0.00018311 ,-0.00006104 , 0.00000000 ,-0.00024414 ,-0.00009155 ,-0.00036621 ,-0.00024414 , -0.00033569 ,-0.00009155 ,-0.00012207 ,0.00012207 ,-0.00045776 , -0.00018311 ,-0.00039673 ,-0.00015259 ,-0.00018311 ,-0.00048828 , -0.00042725 ,-0.00027466 ,-0.00033569 ,-0.00030518 ,-0.00039673 , -0.00024414 ,-0.00015259 ,-0.00009155 ,-0.00003052 ,-0.00051880 , -0.00039673 ,-0.00021362 ,-0.00051880 ,-0.00006104 ,-0.00039673 , -0.00030518 ,-0.00018311 ,-0.00045776 ,-0.00027466 ,-0.00027466 , -0.00048828 ,-0.00015259 ,-0.00061035 ,-0.00045776 ,-0.00039673 , -0.00039673 ,-0.00036621 ,-0.00045776 ,-0.00057983 ,-0.00036621 , -0.00039673 ,-0.00048828 ,-0.00021362 ,-0.00048828 ,-0.00018311 , -0.00061035 ,-0.00015259 ,-0.00045776 ,-0.00057983 ,-0.00054932 , -0.00036621 ,-0.00048828 ,-0.00027466 ,-0.00045776 ,-0.00030518 , -0.00033569 ,-0.00027466 ,-0.00039673 ,-0.00030518 ,-0.00057983 , -0.00018311 ,-0.00042725 ,-0.00048828 ,-0.00027466 ,-0.00045776 , -0.00045776 ,-0.00051880 ,-0.00051880 ,-0.00027466 ,-0.00057983 , -0.00051880 ,-0.00064087 ,-0.00042725 ,-0.00033569 ,-0.00039673 , -0.00067139 ,-0.00057983 ,-0.00021362 ,-0.00061035 ,-0.00030518 , -0.00030518 ,-0.00039673 ,-0.00064087 ,-0.00045776 ,-0.00054932 , -0.00061035 ,-0.00033569 ,-0.00033569 ,-0.00042725 ,-0.00048828 , -0.00033569 ,-0.00076294 ,-0.00073242 ,-0.00024414 ,-0.00027466 , -0.00048828 ,-0.00042725 ,-0.00045776 ,-0.00048828 ,-0.00061035 , -0.00030518 ,-0.00057983 ,-0.00042725 ,-0.00036621 ,-0.00030518 , -0.00024414 ,-0.00045776 ,-0.00030518 ,-0.00033569 ,-0.00021362 , -0.00048828 ,-0.00021362 ,-0.00033569 ,-0.00042725 ,-0.00061035 , -0.00033569 ,-0.00021362 ,-0.00006104 ,-0.00042725 ,-0.00051880 , -0.00009155 ,-0.00048828 ,-0.00045776 ,-0.00030518 ,-0.00021362 , -0.00030518 ,-0.00015259 ,-0.00021362 ,-0.00061035 ,-0.00036621 , -0.00042725 ,-0.00018311 ,-0.00039673 ,-0.00027466 ,-0.00039673 , -0.00036621 ,-0.00042725 ,-0.00033569 ,-0.00054932 ,-0.00006104 , -0.00027466 ,-0.00027466 ,-0.00042725 ,-0.00006104 ,-0.00039673 , -0.00012207 ,-0.00018311 ,-0.00012207 ,-0.00030518 ,-0.00012207 , -0.00015259 ,-0.00036621 ,-0.00015259 ,-0.00030518 ,-0.00012207 , -0.00033569 ,-0.00006104 ,-0.00036621 ,-0.00018311 ,-0.00027466 , -0.00024414 ,-0.00006104 ,-0.00024414 ,-0.00012207 ,0.00000000 , -0.00012207 ,-0.00021362 ,-0.00045776 ,-0.00024414 ,-0.00009155 , -0.00006104 ,-0.00030518 ,-0.00021362 ,-0.00021362 ,-0.00003052 , -0.00039673 ,-0.00012207 ,-0.00018311 ,-0.00006104 ,-0.00006104 , -0.00018311 ,-0.00021362 ,-0.00009155 ,-0.00015259 ,-0.00012207 , 0.00000000 ,-0.00018311 ,-0.00018311 ,0.00003052 ,-0.00012207 , -0.00012207 ,-0.00012207 ,-0.00009155 ,-0.00024414 ,-0.00012207 , -0.00012207 ,-0.00006104 ,-0.00039673 ,0.00006104 ,-0.00021362 , -0.00015259 ,-0.00024414 ,-0.00009155 ,0.00003052 ,0.00000000 , 0.00003052 ,-0.00039673 ,-0.00018311 ,-0.00009155 ,-0.00036621 , -0.00009155 ,-0.00042725 ,0.00000000 ,-0.00021362 ,0.00006104 , -0.00054932 ,0.00009155 ,-0.00024414 ,0.00000000 ,-0.00021362 , -0.00018311 ,-0.00018311 ,0.00006104 ,-0.00015259 ,-0.00036621 , -0.00061035 ,-0.00009155 ,-0.00015259 ,-0.00018311 ,-0.00024414 , -0.00036621 ,-0.00027466 ,-0.00039673 ,-0.00003052 ,-0.00003052 , -0.00051880 ,-0.00027466 ,-0.00012207 ,-0.00033569 ,-0.00033569 , -0.00018311 ,-0.00006104 ,-0.00015259 ,-0.00024414 ,-0.00012207 , -0.00015259 ,-0.00009155 ,-0.00042725 ,-0.00027466 ,-0.00051880 , -0.00036621 ,-0.00030518 ,-0.00018311 ,-0.00018311 ,-0.00018311 , -0.00036621 ,-0.00018311 ,-0.00033569 ,0.00000000 ,-0.00018311 , -0.00015259 ,-0.00027466 ,-0.00027466 ,-0.00030518 ,-0.00051880 , -0.00027466 ,-0.00030518 ,-0.00018311 ,-0.00027466 ,-0.00027466 , -0.00024414 ,-0.00042725 ,0.00000000 ,-0.00036621 ,0.00000000 , -0.00021362 ,-0.00015259 ,-0.00027466 ,0.00009155 ,-0.00006104 , -0.00027466 ,-0.00006104 ,0.00003052 ,-0.00012207 ,-0.00015259 , 0.00009155 ,-0.00006104 ,-0.00015259 ,-0.00021362 ,0.00000000 , -0.00003052 ,-0.00003052 ,-0.00018311 ,0.00000000 ,-0.00006104 , 0.00018311 ,0.00012207 ,-0.00006104 ,-0.00024414 ,-0.00030518 , 0.00003052 ,-0.00033569 ,0.00009155 ,-0.00012207 ,-0.00003052 , 0.00015259 ,0.00012207 ,0.00003052 ,-0.00009155 ,0.00018311 , -0.00021362 ,0.00024414 ,-0.00012207 ,-0.00015259 ,-0.00003052 , 0.00024414 ,0.00003052 ,0.00012207 ,0.00015259 ,0.00024414 , 0.00021362 ,0.00009155 ,0.00006104 ,0.00021362 ,0.00000000 , 0.00015259 ,0.00030518 ,0.00024414 ,0.00033569 ,0.00000000 , 0.00027466 ,0.00021362 ,0.00006104 ,0.00018311 ,0.00009155 , 0.00012207 ,0.00015259 ,0.00006104 ,0.00024414 ,0.00021362 , 0.00015259 ,-0.00018311 ,0.00051880 ,0.00003052 ,0.00030518 , 0.00012207 ,0.00009155 ,0.00033569 ,0.00003052 ,0.00012207 , 0.00012207 ,0.00015259 ,0.00033569 ,0.00009155 ,0.00018311 , -0.00036621 ,0.00027466 ,0.00024414 ,-0.00009155 ,0.00018311 , 0.00006104 ,0.00015259 ,-0.00012207 ,0.00036621 ,0.00003052 , 0.00024414 ,0.00012207 ,-0.00021362 ,0.00009155 ,0.00003052 , 0.00015259 ,-0.00012207 ,0.00042725 ,-0.00009155 ,0.00015259 , -0.00015259 ,0.00027466 ,0.00015259 ,0.00030518 ,-0.00021362 , 0.00009155 ,0.00015259 ,0.00021362 ,0.00030518 ,-0.00003052 , -0.00006104 ,-0.00033569 ,-0.00018311 ,-0.00003052 ,0.00003052 , -0.00006104 ,-0.00021362 ,-0.00021362 ,0.00012207 ,-0.00009155 , -0.00027466 ,0.00021362 ,-0.00018311 ,-0.00003052 ,-0.00039673 , -0.00009155 ,-0.00030518 ,-0.00003052 ,-0.00057983 ,-0.00036621 , -0.00030518 ,-0.00042725 ,-0.00045776 ,-0.00042725 ,-0.00039673 , -0.00003052 ,-0.00027466 ,-0.00039673 ,-0.00015259 ,-0.00027466 , -0.00021362 ,-0.00021362 ,-0.00015259 ,-0.00015259 ,-0.00042725 , -0.00018311 ,-0.00061035 ,0.00000000 ,-0.00030518 ,-0.00015259 , -0.00048828 ,0.00000000 ,-0.00021362 ,-0.00024414 ,-0.00018311 , -0.00006104 ,-0.00036621 ,-0.00018311 ,-0.00048828 ,-0.00012207 , -0.00042725 ,-0.00015259 ,-0.00012207 ,-0.00018311 ,-0.00024414 , -0.00015259 ,-0.00051880 ,-0.00027466 ,-0.00033569 ,-0.00027466 , -0.00033569 ,-0.00027466 ,-0.00039673 ,0.00006104 ,-0.00039673 , -0.00024414 ,-0.00009155 ,-0.00003052 ,-0.00024414 ,-0.00030518 , -0.00015259 ,-0.00021362 ,-0.00024414 ,-0.00027466 ,-0.00015259 , -0.00009155 ,-0.00018311 ,-0.00006104 ,-0.00015259 ,0.00003052 , -0.00048828 ,-0.00024414 ,-0.00009155 ,-0.00030518 ,-0.00012207 , -0.00021362 ,-0.00033569 ,-0.00045776 ,-0.00030518 ,-0.00018311 , -0.00009155 ,-0.00018311 ,-0.00027466 ,-0.00030518 ,-0.00024414 , -0.00027466 ,-0.00006104 ,-0.00033569 ,-0.00021362 ,-0.00033569 , -0.00021362 ,-0.00018311 ,-0.00015259 ,-0.00045776 ,-0.00015259 , -0.00003052 ,0.00006104 ,0.00018311 ,-0.00009155 ,-0.00003052 , -0.00012207 ,-0.00021362 ,-0.00036621 ,-0.00033569 ,0.00006104 , 0.00009155 ,-0.00012207 ,-0.00024414 ,-0.00006104 ,0.00000000 , -0.00012207 ,-0.00027466 ,-0.00018311 ,-0.00024414 ,0.00000000 , -0.00012207 ,0.00003052 ,-0.00006104 ,-0.00027466 ,0.00015259 , -0.00009155 ,0.00006104 ,-0.00027466 ,-0.00018311 ,-0.00027466 , -0.00030518 ,-0.00021362 ,-0.00024414 ,-0.00009155 ,-0.00030518 , -0.00021362 ,-0.00027466 ,-0.00036621 ,0.00015259 ,-0.00030518 , -0.00018311 ,-0.00003052 ,-0.00045776 ,-0.00018311 ,-0.00039673 , -0.00021362 ,-0.00024414 ,-0.00015259 ,-0.00042725 ,-0.00039673 , -0.00018311 ,-0.00024414 ,-0.00015259 ,-0.00015259 ,-0.00033569 , -0.00042725 ,-0.00033569 ,-0.00039673 ,-0.00015259 ,-0.00042725 , -0.00021362 ,-0.00027466 ,-0.00012207 ,-0.00039673 ,-0.00024414 , -0.00042725 ,-0.00012207 ,-0.00027466 ,-0.00042725 ,-0.00018311 , -0.00036621 ,-0.00021362 ,-0.00030518 ,-0.00045776 ,-0.00033569 , -0.00027466 ,0.00003052 ,-0.00024414 ,-0.00045776 ,0.00015259 , -0.00030518 ,-0.00012207 ,-0.00024414 ,-0.00009155 ,-0.00015259 , -0.00021362 ,-0.00018311 ,-0.00036621 ,0.00012207 ,-0.00051880 , -0.00015259 ,-0.00033569 ,-0.00021362 ,-0.00030518 ,-0.00009155 , 0.00000000 ,-0.00009155 ,-0.00009155 ,-0.00045776 ,-0.00021362 , -0.00042725 ,-0.00030518 ,-0.00018311 ,-0.00021362 ,-0.00036621 , -0.00021362 ,-0.00036621 ,0.00000000 ,-0.00018311 ,-0.00036621 , 0.00006104 ,-0.00033569 ,-0.00018311 ,-0.00048828 ,-0.00015259 , -0.00012207 ,-0.00048828 ,-0.00033569 ,-0.00054932 ,-0.00027466 , -0.00030518 ,-0.00030518 ,-0.00048828 ,-0.00003052 ,-0.00045776 , 0.00024414 ,-0.00039673 ,-0.00009155 ,-0.00033569 ,-0.00015259 , -0.00048828 ,-0.00045776 ,-0.00042725 ,-0.00039673 ,-0.00021362 , -0.00051880 ,-0.00018311 ,-0.00018311 ,-0.00033569 ,-0.00067139 , -0.00073242 ,-0.00030518 ,-0.00021362 ,-0.00051880 ,-0.00027466 , -0.00045776 ,-0.00021362 ,-0.00018311 ,-0.00033569 ,-0.00015259 , -0.00018311 ,-0.00039673 ,-0.00030518 ,-0.00036621 ,-0.00024414 , -0.00054932 ,-0.00039673 ,-0.00045776 ,-0.00027466 ,-0.00039673 , -0.00057983 ,-0.00027466 ,-0.00054932 ,-0.00039673 ,-0.00009155 , -0.00033569 ,-0.00033569 ,-0.00039673 ,-0.00012207 ,-0.00030518 , -0.00048828 ,-0.00024414 ,-0.00061035 ,-0.00018311 ,-0.00021362 , -0.00027466 ,-0.00018311 ,-0.00033569 ,-0.00024414 ,0.00000000 , -0.00027466 ,-0.00018311 ,-0.00030518 ,0.00003052 ,-0.00003052 , -0.00024414 ,-0.00012207 ,-0.00024414 ,-0.00024414 ,-0.00012207 , -0.00039673 ,-0.00024414 ,-0.00009155 ,0.00003052 ,-0.00012207 , -0.00009155 ,-0.00039673 ,-0.00012207 ,0.00000000 ,-0.00009155 , -0.00009155 ,-0.00009155 ,0.00021362 ,-0.00006104 ,-0.00024414 , -0.00024414 ,-0.00009155 ,-0.00021362 ,-0.00015259 ,0.00003052 , -0.00006104 ,-0.00003052 ,-0.00006104 ,-0.00003052 ,0.00012207 , 0.00000000 ,-0.00006104 ,0.00009155 ,0.00006104 ,0.00003052 , -0.00009155 ,0.00012207 ,-0.00015259 ,0.00006104 ,-0.00003052 , -0.00009155 ,0.00012207 ,0.00009155 ,-0.00003052 ,-0.00006104 , 0.00009155 ,0.00015259 ,0.00024414 ,-0.00012207 ,0.00027466 , 0.00012207 ,0.00021362 ,-0.00009155 ,0.00006104 ,-0.00012207 , 0.00033569 ,0.00018311 ,-0.00006104 ,0.00012207 ,-0.00006104 , 0.00015259 ,0.00021362 ,-0.00006104 ,0.00000000 ,0.00021362 , -0.00012207 ,0.00006104 ,0.00006104 ,0.00036621 ,0.00015259 , -0.00009155 ,0.00027466 ,0.00000000 ,-0.00003052 ,-0.00024414 , -0.00012207 ,-0.00027466 ,-0.00009155 ,-0.00030518 ,0.00006104 , -0.00018311 ,0.00009155 ,0.00012207 ,0.00006104 ,0.00006104 , 0.00003052 ,0.00012207 ,-0.00003052 ,0.00021362 ,-0.00009155 , 0.00021362 ,0.00012207 ,0.00030518 ,-0.00018311 ,-0.00015259 , 0.00003052 ,0.00009155 ,0.00009155 ,0.00003052 ,0.00000000 , -0.00015259 ,0.00009155 ,-0.00036621 ,0.00009155 ,-0.00018311 , -0.00012207 ,-0.00006104 ,-0.00012207 ,-0.00009155 ,0.00000000 , -0.00015259 ,-0.00012207 ,0.00021362 ,-0.00006104 ,0.00012207 , -0.00003052 ,-0.00021362 ,-0.00006104 ,-0.00015259 ,-0.00003052 , -0.00039673 ,0.00012207 ,-0.00003052 ,0.00006104 ,0.00000000 , -0.00003052 ,-0.00012207 ,0.00003052 ,-0.00009155 ,-0.00003052 , 0.00030518 ,-0.00009155 ,-0.00012207 ,0.00003052 ,0.00006104 , 0.00015259 ,0.00000000 ,0.00003052 ,-0.00015259 ,-0.00006104 , 0.00009155 ,0.00000000 ,-0.00015259 ,0.00003052 ,-0.00003052 , 0.00000000 ,0.00000000 ,0.00006104 ,-0.00021362 ,0.00018311 , -0.00003052 ,0.00003052 ,-0.00006104 ,0.00009155 ,-0.00006104 , 0.00003052 ,0.00027466 ,0.00018311 ,0.00003052 ,-0.00003052 , -0.00024414 ,0.00027466 ,0.00033569 ,0.00012207 ,0.00003052 , -0.00003052 ,0.00021362 ,0.00021362 ,0.00009155 ,0.00021362 , 0.00012207 ,0.00012207 ,-0.00006104 ,0.00006104 ,0.00009155 , 0.00000000 ,0.00003052 ,-0.00003052 ,0.00033569 ,0.00015259 , -0.00003052 ,0.00024414 ,0.00018311 ,0.00009155 ,0.00021362 , -0.00015259 ,0.00027466 ,-0.00003052 ,0.00024414 ,0.00006104 , 0.00024414 ,-0.00009155 ,0.00021362 ,0.00000000 ,0.00015259 , 0.00021362 ,0.00030518 ,-0.00009155 ,0.00009155 ,0.00000000 , 0.00018311 ,-0.00003052 ,-0.00003052 ,-0.00006104 ,0.00015259 , 0.00024414 ,-0.00018311 ,-0.00024414 ,-0.00003052 ,-0.00003052 , -0.00006104 ,-0.00024414 ,0.00018311 ,0.00012207 ,0.00012207 , -0.00015259 ,-0.00012207 ,0.00003052 ,-0.00021362 ,-0.00006104 , -0.00006104 ,0.00000000 ,-0.00015259 ,0.00012207 ,-0.00027466 , -0.00009155 ,0.00009155 ,-0.00030518 ,-0.00003052 ,-0.00009155 , 0.00021362 ,0.00000000 ,0.00018311 ,-0.00024414 ,-0.00018311 , -0.00003052 ,-0.00003052 ,-0.00003052 ,-0.00003052 ,-0.00009155 , -0.00012207 ,-0.00009155 ,0.00006104 ,0.00018311 ,-0.00003052 , -0.00012207 ,0.00003052 ,-0.00006104 ,0.00015259 ,0.00012207 , -0.00009155 ,0.00021362 ,-0.00024414 ,0.00012207 ,-0.00003052 , 0.00000000 ,0.00003052 ,-0.00033569 ,0.00015259 ,-0.00015259 , 0.00003052 ,0.00018311 ,0.00006104 ,0.00003052 ,-0.00018311 , -0.00012207 ,0.00006104 ,0.00003052 ,0.00003052 ,0.00018311 , 0.00015259 ,0.00039673 ,-0.00003052 ,0.00012207 ,-0.00009155 , 0.00009155 ,0.00018311 ,-0.00021362 ,0.00015259 ,0.00009155 , 0.00003052 ,0.00027466 ,-0.00006104 ,0.00003052 ,0.00021362 , 0.00003052 ,-0.00006104 ,0.00009155 ,0.00000000 ,-0.00006104 , 0.00015259 ,-0.00012207 ,0.00018311 ,-0.00003052 ,-0.00021362 , 0.00003052 ,-0.00015259 ,-0.00009155 ,-0.00027466 ,0.00024414 , -0.00006104 ,-0.00003052 ,0.00006104 ,-0.00003052 ,0.00003052 , -0.00021362 ,-0.00006104 ,-0.00030518 ,-0.00021362 ,-0.00021362 , -0.00018311 ,-0.00009155 ,-0.00003052 ,-0.00051880 ,-0.00021362 , -0.00012207 ,0.00000000 ,-0.00033569 ,0.00006104 ,-0.00033569 , 0.00018311 ,-0.00003052 ,-0.00009155 ,-0.00009155 ,-0.00039673 , -0.00015259 ,-0.00012207 ,-0.00015259 ,0.00012207 ,-0.00003052 , -0.00021362 ,-0.00012207 ,-0.00027466 ,-0.00018311 ,-0.00009155 , -0.00012207 ,-0.00003052 ,-0.00018311 ,0.00003052 ,-0.00018311 , -0.00030518 ,-0.00033569 ,-0.00024414 ,-0.00018311 ,-0.00027466 , -0.00015259 ,-0.00021362 ,-0.00006104 ,-0.00018311 ,-0.00030518 , -0.00003052 ,-0.00015259 ,-0.00018311 ,0.00000000 ,-0.00039673 , -0.00045776 ,-0.00009155 ,-0.00015259 ,-0.00009155 ,-0.00009155 , -0.00024414 ,-0.00048828 ,-0.00021362 ,-0.00009155 ,-0.00006104 , -0.00003052 ,-0.00012207 ,-0.00027466 ,-0.00024414 ,-0.00027466 , -0.00024414 ,-0.00039673 ,-0.00003052 ,-0.00024414 ,-0.00003052 , -0.00006104 ,-0.00036621 ,-0.00003052 ,-0.00018311 ,0.00015259 , -0.00012207 ,-0.00006104 ,-0.00027466 ,-0.00006104 ,0.00003052 , 0.00000000 ,0.00000000 ,-0.00030518 ,0.00006104 ,0.00000000 , -0.00015259 ,0.00000000 ,0.00000000 ,0.00027466 ,0.00006104 , 0.00018311 ,0.00006104 ,0.00006104 ,-0.00009155 ,-0.00006104 , -0.00012207 ,-0.00012207 ,0.00006104 ,0.00030518 ,0.00015259 , 0.00018311 ,0.00000000 ,-0.00021362 ,0.00039673 ,0.00024414 , 0.00009155 ,0.00021362 ,-0.00009155 ,0.00021362 ,-0.00006104 , -0.00030518 ,0.00009155 ,0.00003052 ,0.00015259 ,0.00006104 , 0.00006104 ,-0.00003052 ,-0.00006104 ,-0.00009155 ,0.00024414 , -0.00021362 ,0.00018311 ,-0.00021362 ,0.00021362 ,-0.00018311 , -0.00012207 ,-0.00018311 ,0.00024414 ,-0.00015259 ,-0.00009155 , -0.00018311 ,-0.00012207 ,0.00009155 ,0.00000000 ,-0.00012207 , -0.00018311 ,0.00000000 ,0.00006104 ,-0.00006104 ,-0.00027466 , -0.00009155 ,-0.00015259 ,0.00006104 ,-0.00018311 ,-0.00012207 , -0.00021362 ,0.00009155 ,-0.00009155 ,-0.00018311 ,-0.00024414 , -0.00027466 ,-0.00009155 ,-0.00006104 ,-0.00030518 ,-0.00006104 , 0.00003052 ,-0.00036621 ,0.00012207 ,-0.00009155 ,-0.00021362 , -0.00003052 ,-0.00006104 ,-0.00006104 ,-0.00003052 ,-0.00015259 , 0.00015259 ,-0.00018311 ,0.00000000 ,-0.00024414 ,-0.00003052 , -0.00027466 ,-0.00006104 ,-0.00003052 ,0.00009155 ,0.00000000 , -0.00006104 ,0.00021362 ,-0.00027466 ,0.00003052 ,-0.00006104 , 0.00015259 ,0.00018311 ,0.00006104 ,-0.00015259 ,-0.00018311 , 0.00006104 ,-0.00021362 ,-0.00015259 ,-0.00021362 ,-0.00012207 , -0.00015259 ,-0.00021362 ,-0.00009155 ,0.00000000 ,0.00015259 , -0.00006104 ,-0.00021362 ,-0.00015259 ,-0.00015259 ,0.00006104 , 0.00006104 ,-0.00006104 ,-0.00039673 ,-0.00021362 ,0.00003052 , -0.00006104 ,-0.00012207 ,-0.00012207 ,-0.00009155 ,0.00024414 , -0.00018311 ,-0.00006104 ,-0.00021362 ,-0.00015259 ,-0.00006104 , -0.00006104 ,-0.00015259 ,-0.00027466 ,0.00000000 ,-0.00021362 , -0.00018311 ,0.00003052 ,0.00030518 ,0.00009155 ,-0.00048828 , 0.00006104 ,-0.00003052 ,0.00033569 ,-0.00018311 ,-0.00003052 , 0.00006104 ,0.00006104 ,0.00009155 ,-0.00021362 ,-0.00033569 , -0.00018311 ,0.00003052 ,-0.00003052 ,-0.00003052 ,0.00009155 , 0.00021362 ,-0.00009155 ,0.00000000 ,0.00024414 ,0.00012207 , 0.00012207 ,-0.00012207 ,0.00021362 ,-0.00018311 ,-0.00006104 , 0.00003052 ,-0.00012207 ,0.00003052 ,0.00000000 ,0.00018311 , -0.00003052 ,-0.00012207 ,0.00012207 ,0.00012207 ,0.00003052 , 0.00033569 ,0.00015259 ,0.00009155 ,0.00021362 ,0.00006104 , 0.00009155 ,0.00012207 ,-0.00006104 ,0.00006104 ,0.00009155 , -0.00012207 ,0.00021362 ,0.00015259 ,0.00012207 ,-0.00015259 , 0.00021362 ,0.00012207 ,-0.00006104 ,0.00021362 ,0.00030518 , 0.00006104 ,0.00018311 ,0.00003052 ,0.00000000 ,0.00012207 , -0.00018311 ,0.00027466 ,-0.00006104 ,0.00006104 ,0.00018311 , -0.00003052 ,0.00006104 ,0.00003052 ,0.00024414 ,0.00030518 , 0.00021362 ,0.00039673 ,0.00024414 ,0.00009155 ,0.00009155 , 0.00024414 ,0.00033569 ,0.00033569 ,0.00030518 ,0.00033569 , 0.00039673 ,0.00015259 ,0.00015259 ,0.00030518 ,0.00039673 , 0.00012207 ,0.00045776 ,0.00051880 ,0.00018311 ,0.00027466 , 0.00006104 ,0.00018311 ,0.00030518 ,0.00024414 ,0.00021362 , 0.00027466 ,0.00036621 ,0.00030518 ,0.00003052 ,0.00033569 , 0.00033569 ,0.00015259 ,0.00009155 ,0.00021362 ,0.00033569 , 0.00009155 ,0.00042725 ,0.00030518 ,0.00024414 ,0.00021362 , 0.00048828 ,0.00048828 ,0.00030518 ,0.00051880 ,0.00021362 , 0.00048828 ,0.00033569 ,0.00030518 ,0.00030518 ,0.00027466 , 0.00015259 ,0.00057983 ,0.00006104 ,0.00039673 ,0.00000000 , 0.00036621 ,0.00012207 ,0.00015259 ,0.00003052 ,0.00030518 , 0.00030518 ,0.00012207 ,0.00027466 ,0.00003052 ,0.00048828 , 0.00015259 ,-0.00009155 ,0.00003052 ,-0.00006104 ,0.00009155 , 0.00036621 ,0.00027466 ,0.00021362 ,0.00015259 ,0.00030518 , -0.00003052 ,0.00015259 ,0.00027466 ,-0.00015259 ,0.00006104 , 0.00024414 ,0.00051880 ,-0.00009155 ,0.00024414 ,0.00027466 , 0.00030518 ,-0.00024414 ,0.00045776 ,0.00030518 ,0.00036621 , -0.00018311 ,0.00045776 ,0.00009155 ,0.00030518 ,0.00024414 , 0.00039673 ,0.00012207 ,0.00006104 ,-0.00006104 ,0.00024414 , -0.00015259 ,0.00030518 ,0.00012207 ,0.00027466 ,0.00030518 , 0.00000000 ,0.00006104 ,0.00018311 ,0.00018311 ,0.00009155 , 0.00024414 ,0.00039673 ,0.00015259 ,0.00021362 ,0.00015259 , -0.00003052 ,0.00003052 ,-0.00009155 ,0.00027466 ,0.00030518 , 0.00021362 ,0.00012207 ,0.00030518 ,0.00006104 ,0.00036621 , 0.00048828 ,0.00015259 ,0.00033569 ,0.00027466 ,0.00042725 , -0.00009155 ,0.00048828 ,0.00051880 ,0.00021362 ,0.00024414 , 0.00027466 ,0.00027466 ,0.00021362 ,0.00054932 ,0.00048828 , 0.00018311 ,0.00033569 ,0.00036621 ,0.00039673 ,0.00027466 , 0.00030518 ,0.00042725 ,0.00036621 ,0.00051880 ,0.00042725 , 0.00021362 ,-0.00003052 ,0.00048828 ,0.00064087 ,0.00048828 , 0.00042725 ,0.00036621 ,0.00015259 ,0.00033569 ,0.00042725 , 0.00045776 ,0.00045776 ,0.00024414 ,0.00024414 ,0.00061035 , 0.00033569 ,0.00045776 ,0.00061035 ,0.00051880 ,0.00000000 , }; static const unsigned defaultKlick4Length = sizeof(defaultKlick4) / sizeof(*defaultKlick4); #endif muse-4.0.0/src/muse/large_int.h000066400000000000000000000135201404105311000163270ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // large_int.h // (C) Copyright 2018 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LARGE_INT_H__ #define __LARGE_INT_H__ #include "libdivide.h" #if defined(_MSC_VER) #define LARGE_INT_VC #endif #ifdef __cplusplus #include #include #else #include #include #endif #include #if defined(LARGE_INT_VC) #include #endif #if defined(__SIZEOF_INT128__) #define LARGE_INT_HAS_INT128_T #endif #if defined(__x86_64__) || defined(_WIN64) || defined(_M_X64) #define LARGE_INT_IS_X86_64 #endif namespace MusECore { enum LargeIntRoundMode { // Round down and remainder zero. LargeIntRoundDown, // Round up and remainder zero. LargeIntRoundUp, // Round to nearest and remainder zero. LargeIntRoundNearest, // No rounding and remainder is valid. LargeIntRoundNone }; static inline uint64_t muse_multiply_64_div_64_to_64( uint64_t a, uint64_t b, uint64_t c, LargeIntRoundMode round_mode = LargeIntRoundDown, uint64_t* remainder = nullptr) { #if defined(LARGE_INT_VC) && defined(LARGE_INT_IS_X86_64) uint64_t t_hi; const uint64_t t_lo = _umul128(a, b, &t_hi); uint64_t q, r; q = libdivide::libdivide_128_div_64_to_64(t_hi, t_lo, c, &r); switch(round_mode) { case LargeIntRoundDown: if(remainder) *remainder = 0; break; case LargeIntRoundUp: if(r) q++; if(remainder) *remainder = 0; break; case LargeIntRoundNearest: if(r >= (c / 2)) q++; if(remainder) *remainder = 0; break; case LargeIntRoundNone: if(remainder) *remainder = r; break; } return q; #elif defined(LARGE_INT_HAS_INT128_T) switch(round_mode) { case LargeIntRoundDown: break; case LargeIntRoundUp: { const __uint128_t dividend = (__uint128_t)a * (__uint128_t)b; const __uint128_t cc = (__uint128_t)c; if(remainder) *remainder = 0; // FIXME Too bad this is slowed by divide and mod. No 128-bit support for div() in stdlib. Go asm ? return dividend / cc + (dividend % cc ? 1 : 0); } break; case LargeIntRoundNearest: { const __uint128_t dividend = (__uint128_t)a * (__uint128_t)b; const __uint128_t cc = (__uint128_t)c; if(remainder) *remainder = 0; // FIXME Too bad this is slowed by divide and mod. No 128-bit support for div() in stdlib. Go asm ? return dividend / cc + ((dividend % cc >= (cc / 2)) ? 1 : 0); } break; case LargeIntRoundNone: const __uint128_t dividend = (__uint128_t)a * (__uint128_t)b; const __uint128_t cc = (__uint128_t)c; const __uint128_t q = dividend / cc; const __uint128_t r = dividend % cc; if(remainder) *remainder = r; return q; break; } if(remainder) *remainder = 0; return ((__uint128_t)a * (__uint128_t)b / (__uint128_t)c); #else uint64_t u1 = (a & 0xffffffff); uint64_t v1 = (b & 0xffffffff); uint64_t t = (u1 * v1); uint64_t w3 = (t & 0xffffffff); uint64_t k = (t >> 32); a >>= 32; t = (a * v1) + k; k = (t & 0xffffffff); uint64_t w1 = (t >> 32); b >>= 32; t = (u1 * b) + k; k = (t >> 32); uint64_t q, r; q = libdivide::libdivide_128_div_64_to_64( (a * b) + w1 + k, (t << 32) + w3, c, &r); switch(round_mode) { case LargeIntRoundDown: if(remainder) *remainder = 0; break; case LargeIntRoundUp: if(r) q++; if(remainder) *remainder = 0; break; case LargeIntRoundNearest: if(r >= (c / 2)) q++; if(remainder) *remainder = 0; break; case LargeIntRoundNone: if(remainder) *remainder = r; break; } return q; #endif } // class Fixed_U64 // { // public: // uint64_t _dividend; // uint64_t _divisor; // //uint64_t _quotient; // uint64_t _remainder; // // //Fixed_U64() : _dividend(0), _divisor(1), _quotient(0), _remainder(0) { } // Fixed_U64() : _dividend(0), _divisor(1), _remainder(0) { } // //Fixed_U64(uint64_t dividend, uint64_t divisor = 1, uint64_t quotient = 0, uint64_t remainder = 0) : // // _dividend(dividend), _divisor(divisor), _quotient(quotient), _remainder(remainder) { } // Fixed_U64(uint64_t dividend, uint64_t divisor = 1, uint64_t remainder = 0) : // _dividend(dividend), _divisor(divisor), _remainder(remainder) { } // // Fixed_U64 operator+(const Fixed_U64& val) // { // uint64_t dd = _dividend + val._dividend; // uint64_t dv; // uint64_t r; // if(val._divisor != _divisor) // { // dv = _dividend * val._dividend; // r = _remainder * val._dividend; // } // // //uint64_t q = _dividend + val._dividend; // //uint64_t r = _dividend + val._dividend; // //return Fixed_U64(dd, dv, q, r); // return Fixed_U64(dd, dv, r); // } // }; } // namespace MusECore #endif muse-4.0.0/src/muse/latency_compensator.cpp000066400000000000000000000205141404105311000207700ustar00rootroot00000000000000//=================================================================== // MusE // Linux Music Editor // // latency_compensator.cpp // (C) Copyright 2016, 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //=================================================================== #include #include "latency_compensator.h" namespace MusECore { LatencyCompensator::LatencyCompensator(int channels, unsigned long bufferSize) : _channels(channels), _bufferSize(bufferSize), _bufferSizeMask(bufferSize - 1) { _buffer = nullptr; _readPointers = nullptr; _peekedChannels = nullptr; if(_channels > 0) { _buffer = new float*[_channels]; _readPointers = new unsigned long[_channels]; _peekedChannels = new bool[_channels]; for(int i = 0; i < _channels; ++i) { _buffer[i] = new float[_bufferSize]; std::memset(_buffer[i], 0, sizeof(float) * _bufferSize); _readPointers[i] = 0; _peekedChannels[i] = false; } } } LatencyCompensator::~LatencyCompensator() { if(_buffer) { for(int i = 0; i < _channels; ++i) delete [] _buffer[i]; delete [] _buffer; } if(_readPointers) delete [] _readPointers; if(_peekedChannels) delete [] _peekedChannels; } void LatencyCompensator::clear() { for(int i = 0; i < _channels; ++i) std::memset(_buffer[i], 0, sizeof(float) * _bufferSize); } void LatencyCompensator::setBufferSize(unsigned long size) { _bufferSize = size; if(_buffer) { for(int i = 0; i < _channels; ++i) { delete [] _buffer[i]; _buffer[i] = new float[_bufferSize]; std::memset(_buffer[i], 0, sizeof(float) * _bufferSize); _readPointers[i] = 0; _peekedChannels[i] = false; } } _bufferSizeMask = _bufferSize - 1; } void LatencyCompensator::setChannels(int channels) { if(_buffer) { for(int i = 0; i < _channels; ++i) delete [] _buffer[i]; delete [] _buffer; _buffer = nullptr; } if(_readPointers) { delete [] _readPointers; _readPointers = nullptr; } if(_peekedChannels) { delete [] _peekedChannels; _peekedChannels = nullptr; } _bufferSizeMask = 0; _channels = channels; if(_channels > 0) { _buffer = new float*[_channels]; _readPointers = new unsigned long[_channels]; _peekedChannels = new bool[_channels]; for(int i = 0; i < _channels; ++i) { _buffer[i] = new float[_bufferSize]; std::memset(_buffer[i], 0, sizeof(float) * _bufferSize); _readPointers[i] = 0; _peekedChannels[i] = false; } _bufferSizeMask = _bufferSize - 1; } } void LatencyCompensator::read(unsigned long sampleCount, float** data) { unsigned long read_position, i, idx; float *output, *buf; for(int ch = 0; ch < _channels; ++ch) { output = data[ch]; buf = _buffer[ch]; read_position = _readPointers[ch]; for(i = 0; i < sampleCount; i++) { idx = (i + read_position) & _bufferSizeMask; *(output++) = buf[idx]; // Clear the data so that the next time around, simple addition of data can be used. // Like the erase head of a (imaginary) multi-record-head latency-correction tape loop // mechanism, where in this simulation the erase head is just after the read head. buf[idx] = 0.0f; } _readPointers[ch] = (_readPointers[ch] + sampleCount) & _bufferSizeMask; // The channel's read pointer has been advanced. Reset the peeked flag. _peekedChannels[ch] = false; } } void LatencyCompensator::read(int channel, unsigned long sampleCount, float* data) { if(channel >= _channels) return; unsigned long read_position, i, idx; float *output, *buf; output = data; buf = _buffer[channel]; read_position = _readPointers[channel]; for(i = 0; i < sampleCount; i++) { idx = (i + read_position) & _bufferSizeMask; *(output++) = buf[idx]; // Clear the data so that the next time around, simple addition of data can be used. // Like the erase head of a (imaginary) multi-record-head latency-correction tape loop // mechanism, where in this simulation the erase head is just after the read head. buf[idx] = 0.0f; } _readPointers[channel] = (_readPointers[channel] + sampleCount) & _bufferSizeMask; // The channel's read pointer has been advanced. Reset the peeked flag. _peekedChannels[channel] = false; } void LatencyCompensator::peek(unsigned long sampleCount, float** data) { unsigned long read_position, i, idx; float *output, *buf; for(int ch = 0; ch < _channels; ++ch) { output = data[ch]; buf = _buffer[ch]; read_position = _readPointers[ch]; for(i = 0; i < sampleCount; i++) { idx = (i + read_position) & _bufferSizeMask; *(output++) = buf[idx]; } // The channel's read pointer has NOT been advanced yet. // Set the peeked flag, to warn that it must be advanced later. _peekedChannels[ch] = true; } } void LatencyCompensator::peek(int channel, unsigned long sampleCount, float* data) { if(channel >= _channels) return; unsigned long read_position, i, idx; float *output, *buf; output = data; buf = _buffer[channel]; read_position = _readPointers[channel]; for(i = 0; i < sampleCount; i++) { idx = (i + read_position) & _bufferSizeMask; *(output++) = buf[idx]; } // The channel's read pointer has NOT been advanced yet. // Set the peeked flag, to warn that it must be advanced later. _peekedChannels[channel] = true; } void LatencyCompensator::advance(unsigned long sampleCount) { for(int ch = 0; ch < _channels; ++ch) advance(ch, sampleCount); } void LatencyCompensator::advance(int channel, unsigned long sampleCount) { // If the channel was not peeked there's no reason to advance. if(channel >= _channels || !_peekedChannels[channel]) return; unsigned long i, rp, sz2, sz1; float *buf1, *buf2; buf1 = _buffer[channel]; rp = _readPointers[channel]; sz2 = sampleCount; i = rp + sz2; if(i > _bufferSize) { sz2 = _bufferSize - rp; sz1 = i - _bufferSize; std::memset(buf1, 0, sizeof(float) * sz1); } buf2 = &buf1[rp]; std::memset(buf2, 0, sizeof(float) * sz2); _readPointers[channel] = (_readPointers[channel] + sampleCount) & _bufferSizeMask; // The channel's read pointer has been advanced. Reset the peeked flag. _peekedChannels[channel] = false; } void LatencyCompensator::write(unsigned long sampleCount, const unsigned long* const writeOffsets, const float* const* data) { unsigned long write_position, i; const float *input; float *buf; for(int ch = 0; ch < _channels; ++ch) { input = data[ch]; buf = _buffer[ch]; write_position = _readPointers[ch] + writeOffsets[ch]; for(i = 0; i < sampleCount; i++) buf[((i + write_position) & _bufferSizeMask)] += *(input++); } } void LatencyCompensator::write(int channel, unsigned long sampleCount, unsigned long writeOffset, const float* const data) { if(channel >= _channels) return; unsigned long write_position, i; const float *input; float *buf; input = data; buf = _buffer[channel]; write_position = _readPointers[channel] + writeOffset; for(i = 0; i < sampleCount; i++) buf[((i + write_position) & _bufferSizeMask)] += *(input++); } void LatencyCompensator::write(unsigned long sampleCount, unsigned long writeOffset, const float* const* data) { unsigned long write_position, i; const float *input; float *buf; for(int ch = 0; ch < _channels; ++ch) { input = data[ch]; buf = _buffer[ch]; write_position = _readPointers[ch] + writeOffset; for(i = 0; i < sampleCount; i++) buf[((i + write_position) & _bufferSizeMask)] += *(input++); } } } // namespace MusECore muse-4.0.0/src/muse/latency_compensator.h000066400000000000000000000067311404105311000204420ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // latency_compensator.h // (C) Copyright 2016, 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LATENCY_COMPENSATOR_H__ #define __LATENCY_COMPENSATOR_H__ namespace MusECore { class LatencyCompensator { private: int _channels; unsigned long _bufferSize; // Must be power of two. unsigned long _bufferSizeMask; unsigned long* _readPointers; float** _buffer; // Channels that have been peeked and require an advance. bool* _peekedChannels; public: LatencyCompensator(unsigned long bufferSize = 16384) : _channels(0), _bufferSize(bufferSize), _bufferSizeMask(bufferSize - 1), _readPointers(0), _buffer(0), _peekedChannels(0) { } LatencyCompensator(int channels, unsigned long bufferSize = 16384); virtual ~LatencyCompensator(); void clear(); void setBufferSize(unsigned long size); void setChannels(int channels); // Write a block of data on each channel at the given write offsets (from the read position). // All writes are additive. Read will clear the blocks. void write(unsigned long sampleCount, const unsigned long* const writeOffsets, const float* const* data); // Convenient single channel version of write. void write(int channel, unsigned long sampleCount, unsigned long writeOffset, const float* const data); // Convenient version of write with common write offset for all channels. void write(unsigned long sampleCount, unsigned long writeOffset, const float* const* data); // Read a block of data on each channel. // The internal read pointer is advanced to the next position. // The block is cleared after a read. void read(unsigned long sampleCount, float** data); // Convenient single channel version of read. void read(int channel, unsigned long sampleCount, float* data); // Read (peek) a block of data on each channel. // The internal read pointer is NOT advanced to the next position. // The block is NOT cleared after a peek. // This allows further data to be written after a peek, before a full read. void peek(unsigned long sampleCount, float** data); // Convenient single channel version of peek. void peek(int channel, unsigned long sampleCount, float* data); // Manually advances all channels' read pointers, but only if they were peeked. // This also clears the blocks. void advance(unsigned long sampleCount); // Manually advances a channel's read pointer, but only if it was peeked. // This also clears the block. void advance(int channel, unsigned long sampleCount); }; } // namespace MusECore #endif muse-4.0.0/src/muse/latency_info.cpp000066400000000000000000000041611404105311000173710ustar00rootroot00000000000000//=================================================================== // MusE // Linux Music Editor // // latency_info.cpp // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //=================================================================== #include "latency_info.h" namespace MusECore { void TrackLatencyInfo::initialize() { _dominanceProcessed = false; _dominanceInputProcessed = false; _canDominateProcessed = false; _canDominateInputProcessed = false; _correctionProcessed = false; _correctionInputProcessed = false; _worstPluginLatencyProcessed = false; _worstPluginLatency = 0.0f; _worstPortLatencyProcessed = false; _worstPortLatency = 0.0f; _processed = false; _inputProcessed = false; _worstSelfLatency = 0.0f; _worstSelfLatencyMidi = 0.0f; _worstSelfLatencyProcessed = false; _worstSelfLatencyMidiProcessed = false; _outputLatency = 0.0f; _inputLatency = 0.0f; _isLatencyInputTerminal = false; _isLatencyOutputTerminal = false; _isLatencyInputTerminalProcessed = false; _isLatencyOutputTerminalProcessed = false; _canDominateOutputLatency = false; _canDominateInputLatency = false; _canCorrectOutputLatency = false; _sourceCorrectionValue = 0.0f; _compensatorWriteOffset = 0; // Special for Midi Tracks: _latencyOutMidiTrack = 0.0f; // Special for Metronome: _latencyOutMetronome = 0.0f; } } // namespace MusECore muse-4.0.0/src/muse/latency_info.h000066400000000000000000000133211404105311000170340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // latency_info.h // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LATENCY_INFO_H__ #define __LATENCY_INFO_H__ namespace MusECore { // During latency computations each process cycle, // this holds cached computed latency values. struct TrackLatencyInfo { // Whether the dominance information is valid (has already been // gathered in the in the dominance latency scan). // This is reset near the beginning of the process handler. bool _dominanceProcessed; bool _dominanceInputProcessed; bool _canDominateProcessed; bool _canDominateInputProcessed; // Whether the correction information is valid (has already been gathered // in the correction latency scan). // This is reset near the beginning of the process handler. bool _correctionProcessed; bool _correctionInputProcessed; // Whether the _worstEffRackLatency has been processed. bool _worstPluginLatencyProcessed; // The worst contribution to latency by the channels in the track's effect rack. float _worstPluginLatency; // Whether the _worstPortLatency has been processed. bool _worstPortLatencyProcessed; // The worst contribution to latency by any ports (for ex. audio input/output tracks). float _worstPortLatency; // Whether the final latency information is valid (has already been gathered // in the in the final latency scan). // This is reset near the beginning of the process handler. bool _processed; bool _inputProcessed; bool _worstSelfLatencyProcessed; bool _worstSelfLatencyMidiProcessed; // Contributions to latency from rack plugins and/or Jack ports etc. // This value is the worst-case latency of all the channels in a track. // See AudioTrack::trackLatency(). float _worstSelfLatency; float _worstSelfLatencyMidi; // The absolute latency of all signals leaving a track, relative to audio driver frame (transport, etc). // This value is the cumulative value of all series routes connected to this track, plus some // adjustment for the track's own members' latency. // The goal is to have equal latency output on all channels. // Thus the value will be the WORST-CASE latency of any channel. All other channels are delayed to match it. // For example, a Wave Track can use this total value to appropriately shift recordings of the signals // arriving at its inputs. float _outputLatency; float _inputLatency; // Whether any of the connected output routes are effectively connected. // That means track is not off, track is monitored where applicable, etc, // ie. signal can actually flow. bool _isLatencyInputTerminal; bool _isLatencyOutputTerminal; bool _isLatencyInputTerminalProcessed; bool _isLatencyOutputTerminalProcessed; // Whether this track (and the branch it is in) can force other parallel branches to // increase their latency compensation to match this one. // If false, this branch will NOT disturb other parallel branches' compensation, // intead only allowing compensation UP TO the worst case in other branches. bool _canDominateOutputLatency; bool _canDominateInputLatency; // Whether this track and its branch can correct for latency, not just compensate. bool _canCorrectOutputLatency; // For tracks which can correct for latency, this is the value that the track // must shift (ahead) to correct. It is essentially the programmed latency value // of the track's ultimate source (wave file etc.). Therefore it will be NEGATIVE // when requiring correction, but never POSITIVE (that would be unnecessary, // a subsequent compensator delay can do that). float _sourceCorrectionValue; // Balances end points (Audio Outputs or open branches) of parallel branches. unsigned long int _compensatorWriteOffset; //-------------------------------------------------------- // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet // because we don't have multiple Midi Track outputs yet, only a single output port. // So we must store this information here just for Midi Tracks. // // All other tracks store this information in the route itself. // // Temporary variables used during latency calculations: // Holds the output latency of this node, so that it can be compared with others. float _latencyOutMidiTrack; //-------------------------------------------------------- //-------------------------------------------------------- // Special for Metronome: We don't have Metronome routes. // So we must store this information here just for the metronome. // Temporary variables used during latency calculations: // Holds the output latency of this node, so that it can be compared with others. float _latencyOutMetronome; //-------------------------------------------------------- // Initializes (resets) the structure to prepare for (re)computation. void initialize(); }; } // namespace MusECore #endif muse-4.0.0/src/muse/libdivide.h000066400000000000000000002372311404105311000163250ustar00rootroot00000000000000// libdivide.h // Copyright 2010 - 2018 ridiculous_fish // // libdivide is dual-licensed under the Boost or zlib licenses. // You may use libdivide under the terms of either of these. // See LICENSE.txt for more details. #ifndef LIBDIVIDE_H #define LIBDIVIDE_H #if defined(_MSC_VER) // disable warning C4146: unary minus operator applied to // unsigned type, result still unsigned #pragma warning(disable: 4146) #define LIBDIVIDE_VC #endif #ifdef __cplusplus #include #include #else #include #include #endif #include #if defined(LIBDIVIDE_USE_SSE2) #include #endif #if defined(LIBDIVIDE_VC) #include #endif #ifndef __has_builtin #define __has_builtin(x) 0 // Compatibility with non-clang compilers. #endif #if defined(__SIZEOF_INT128__) #define HAS_INT128_T #endif #if defined(__x86_64__) || defined(_WIN64) || defined(_M_X64) #define LIBDIVIDE_IS_X86_64 #endif #if defined(__i386__) #define LIBDIVIDE_IS_i386 #endif #if defined(__GNUC__) || defined(__clang__) #define LIBDIVIDE_GCC_STYLE_ASM #endif #if defined(__cplusplus) || defined(LIBDIVIDE_VC) #define LIBDIVIDE_FUNCTION __FUNCTION__ #else #define LIBDIVIDE_FUNCTION __func__ #endif #define LIBDIVIDE_ERROR(msg) \ do { \ fprintf(stderr, "libdivide.h:%d: %s(): Error: %s\n", \ __LINE__, LIBDIVIDE_FUNCTION, msg); \ exit(-1); \ } while (0) #if defined(LIBDIVIDE_ASSERTIONS_ON) #define LIBDIVIDE_ASSERT(x) \ do { \ if (!(x)) { \ fprintf(stderr, "libdivide.h:%d: %s(): Assertion failed: %s\n", \ __LINE__, LIBDIVIDE_FUNCTION, #x); \ exit(-1); \ } \ } while (0) #else #define LIBDIVIDE_ASSERT(x) #endif // libdivide may use the pmuldq (vector signed 32x32->64 mult instruction) // which is in SSE 4.1. However, signed multiplication can be emulated // efficiently with unsigned multiplication, and SSE 4.1 is currently rare, so // it is OK to not turn this on. #ifdef LIBDIVIDE_USE_SSE4_1 #include #endif #ifdef __cplusplus // We place libdivide within the libdivide namespace, and that goes in an // anonymous namespace so that the functions are only visible to files that // #include this header and don't get external linkage. At least that's the // theory. namespace { namespace libdivide { #endif // Explanation of "more" field: bit 6 is whether to use shift path. If we are // using the shift path, bit 7 is whether the divisor is negative in the signed // case; in the unsigned case it is 0. Bits 0-4 is shift value (for shift // path or mult path). In 32 bit case, bit 5 is always 0. We use bit 7 as the // "negative divisor indicator" so that we can use sign extension to // efficiently go to a full-width -1. // // u32: [0-4] shift value // [5] ignored // [6] add indicator // [7] shift path // // s32: [0-4] shift value // [5] shift path // [6] add indicator // [7] indicates negative divisor // // u64: [0-5] shift value // [6] add indicator // [7] shift path // // s64: [0-5] shift value // [6] add indicator // [7] indicates negative divisor // magic number of 0 indicates shift path (we ran out of bits!) // // In s32 and s64 branchfree modes, the magic number is negated according to // whether the divisor is negated. In branchfree strategy, it is not negated. enum { LIBDIVIDE_32_SHIFT_MASK = 0x1F, LIBDIVIDE_64_SHIFT_MASK = 0x3F, LIBDIVIDE_ADD_MARKER = 0x40, LIBDIVIDE_U32_SHIFT_PATH = 0x80, LIBDIVIDE_U64_SHIFT_PATH = 0x80, LIBDIVIDE_S32_SHIFT_PATH = 0x20, LIBDIVIDE_NEGATIVE_DIVISOR = 0x80 }; // pack divider structs to prevent compilers from padding. // This reduces memory usage by up to 43% when using a large // array of libdivide dividers and improves performance // by up to 10% because of reduced memory bandwidth. #pragma pack(push, 1) struct libdivide_u32_t { uint32_t magic; uint8_t more; }; struct libdivide_s32_t { int32_t magic; uint8_t more; }; struct libdivide_u64_t { uint64_t magic; uint8_t more; }; struct libdivide_s64_t { int64_t magic; uint8_t more; }; struct libdivide_u32_branchfree_t { uint32_t magic; uint8_t more; }; struct libdivide_s32_branchfree_t { int32_t magic; uint8_t more; }; struct libdivide_u64_branchfree_t { uint64_t magic; uint8_t more; }; struct libdivide_s64_branchfree_t { int64_t magic; uint8_t more; }; #pragma pack(pop) #ifndef LIBDIVIDE_API #ifdef __cplusplus // In C++, we don't want our public functions to be static, because // they are arguments to templates and static functions can't do that. // They get internal linkage through virtue of the anonymous namespace. // In C, they should be static. #define LIBDIVIDE_API #else #define LIBDIVIDE_API static inline #endif #endif LIBDIVIDE_API struct libdivide_s32_t libdivide_s32_gen(int32_t y); LIBDIVIDE_API struct libdivide_u32_t libdivide_u32_gen(uint32_t y); LIBDIVIDE_API struct libdivide_s64_t libdivide_s64_gen(int64_t y); LIBDIVIDE_API struct libdivide_u64_t libdivide_u64_gen(uint64_t y); LIBDIVIDE_API struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t y); LIBDIVIDE_API struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t y); LIBDIVIDE_API struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t y); LIBDIVIDE_API struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t y); LIBDIVIDE_API int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom); LIBDIVIDE_API uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom); LIBDIVIDE_API int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom); LIBDIVIDE_API uint64_t libdivide_u64_do(uint64_t y, const struct libdivide_u64_t *denom); LIBDIVIDE_API int32_t libdivide_s32_branchfree_do(int32_t numer, const struct libdivide_s32_branchfree_t *denom); LIBDIVIDE_API uint32_t libdivide_u32_branchfree_do(uint32_t numer, const struct libdivide_u32_branchfree_t *denom); LIBDIVIDE_API int64_t libdivide_s64_branchfree_do(int64_t numer, const struct libdivide_s64_branchfree_t *denom); LIBDIVIDE_API uint64_t libdivide_u64_branchfree_do(uint64_t y, const struct libdivide_u64_branchfree_t *denom); LIBDIVIDE_API int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom); LIBDIVIDE_API uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom); LIBDIVIDE_API int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom); LIBDIVIDE_API uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom); LIBDIVIDE_API int32_t libdivide_s32_branchfree_recover(const struct libdivide_s32_branchfree_t *denom); LIBDIVIDE_API uint32_t libdivide_u32_branchfree_recover(const struct libdivide_u32_branchfree_t *denom); LIBDIVIDE_API int64_t libdivide_s64_branchfree_recover(const struct libdivide_s64_branchfree_t *denom); LIBDIVIDE_API uint64_t libdivide_u64_branchfree_recover(const struct libdivide_u64_branchfree_t *denom); LIBDIVIDE_API int libdivide_u32_get_algorithm(const struct libdivide_u32_t *denom); LIBDIVIDE_API uint32_t libdivide_u32_do_alg0(uint32_t numer, const struct libdivide_u32_t *denom); LIBDIVIDE_API uint32_t libdivide_u32_do_alg1(uint32_t numer, const struct libdivide_u32_t *denom); LIBDIVIDE_API uint32_t libdivide_u32_do_alg2(uint32_t numer, const struct libdivide_u32_t *denom); LIBDIVIDE_API int libdivide_u64_get_algorithm(const struct libdivide_u64_t *denom); LIBDIVIDE_API uint64_t libdivide_u64_do_alg0(uint64_t numer, const struct libdivide_u64_t *denom); LIBDIVIDE_API uint64_t libdivide_u64_do_alg1(uint64_t numer, const struct libdivide_u64_t *denom); LIBDIVIDE_API uint64_t libdivide_u64_do_alg2(uint64_t numer, const struct libdivide_u64_t *denom); LIBDIVIDE_API int libdivide_s32_get_algorithm(const struct libdivide_s32_t *denom); LIBDIVIDE_API int32_t libdivide_s32_do_alg0(int32_t numer, const struct libdivide_s32_t *denom); LIBDIVIDE_API int32_t libdivide_s32_do_alg1(int32_t numer, const struct libdivide_s32_t *denom); LIBDIVIDE_API int32_t libdivide_s32_do_alg2(int32_t numer, const struct libdivide_s32_t *denom); LIBDIVIDE_API int32_t libdivide_s32_do_alg3(int32_t numer, const struct libdivide_s32_t *denom); LIBDIVIDE_API int32_t libdivide_s32_do_alg4(int32_t numer, const struct libdivide_s32_t *denom); LIBDIVIDE_API int libdivide_s64_get_algorithm(const struct libdivide_s64_t *denom); LIBDIVIDE_API int64_t libdivide_s64_do_alg0(int64_t numer, const struct libdivide_s64_t *denom); LIBDIVIDE_API int64_t libdivide_s64_do_alg1(int64_t numer, const struct libdivide_s64_t *denom); LIBDIVIDE_API int64_t libdivide_s64_do_alg2(int64_t numer, const struct libdivide_s64_t *denom); LIBDIVIDE_API int64_t libdivide_s64_do_alg3(int64_t numer, const struct libdivide_s64_t *denom); LIBDIVIDE_API int64_t libdivide_s64_do_alg4(int64_t numer, const struct libdivide_s64_t *denom); #if defined(LIBDIVIDE_USE_SSE2) LIBDIVIDE_API __m128i libdivide_u32_do_vector(__m128i numers, const struct libdivide_u32_t *denom); LIBDIVIDE_API __m128i libdivide_s32_do_vector(__m128i numers, const struct libdivide_s32_t *denom); LIBDIVIDE_API __m128i libdivide_u64_do_vector(__m128i numers, const struct libdivide_u64_t *denom); LIBDIVIDE_API __m128i libdivide_s64_do_vector(__m128i numers, const struct libdivide_s64_t *denom); LIBDIVIDE_API __m128i libdivide_u32_do_vector_alg0(__m128i numers, const struct libdivide_u32_t *denom); LIBDIVIDE_API __m128i libdivide_u32_do_vector_alg1(__m128i numers, const struct libdivide_u32_t *denom); LIBDIVIDE_API __m128i libdivide_u32_do_vector_alg2(__m128i numers, const struct libdivide_u32_t *denom); LIBDIVIDE_API __m128i libdivide_s32_do_vector_alg0(__m128i numers, const struct libdivide_s32_t *denom); LIBDIVIDE_API __m128i libdivide_s32_do_vector_alg1(__m128i numers, const struct libdivide_s32_t *denom); LIBDIVIDE_API __m128i libdivide_s32_do_vector_alg2(__m128i numers, const struct libdivide_s32_t *denom); LIBDIVIDE_API __m128i libdivide_s32_do_vector_alg3(__m128i numers, const struct libdivide_s32_t *denom); LIBDIVIDE_API __m128i libdivide_s32_do_vector_alg4(__m128i numers, const struct libdivide_s32_t *denom); LIBDIVIDE_API __m128i libdivide_u64_do_vector_alg0(__m128i numers, const struct libdivide_u64_t *denom); LIBDIVIDE_API __m128i libdivide_u64_do_vector_alg1(__m128i numers, const struct libdivide_u64_t *denom); LIBDIVIDE_API __m128i libdivide_u64_do_vector_alg2(__m128i numers, const struct libdivide_u64_t *denom); LIBDIVIDE_API __m128i libdivide_s64_do_vector_alg0(__m128i numers, const struct libdivide_s64_t *denom); LIBDIVIDE_API __m128i libdivide_s64_do_vector_alg1(__m128i numers, const struct libdivide_s64_t *denom); LIBDIVIDE_API __m128i libdivide_s64_do_vector_alg2(__m128i numers, const struct libdivide_s64_t *denom); LIBDIVIDE_API __m128i libdivide_s64_do_vector_alg3(__m128i numers, const struct libdivide_s64_t *denom); LIBDIVIDE_API __m128i libdivide_s64_do_vector_alg4(__m128i numers, const struct libdivide_s64_t *denom); LIBDIVIDE_API __m128i libdivide_u32_branchfree_do_vector(__m128i numers, const struct libdivide_u32_branchfree_t *denom); LIBDIVIDE_API __m128i libdivide_s32_branchfree_do_vector(__m128i numers, const struct libdivide_s32_branchfree_t *denom); LIBDIVIDE_API __m128i libdivide_u64_branchfree_do_vector(__m128i numers, const struct libdivide_u64_branchfree_t *denom); LIBDIVIDE_API __m128i libdivide_s64_branchfree_do_vector(__m128i numers, const struct libdivide_s64_branchfree_t *denom); #endif //////// Internal Utility Functions static inline uint32_t libdivide__mullhi_u32(uint32_t x, uint32_t y) { uint64_t xl = x, yl = y; uint64_t rl = xl * yl; return (uint32_t)(rl >> 32); } static uint64_t libdivide__mullhi_u64(uint64_t x, uint64_t y) { #if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_IS_X86_64) return __umulh(x, y); #elif defined(HAS_INT128_T) __uint128_t xl = x, yl = y; __uint128_t rl = xl * yl; return (uint64_t)(rl >> 64); #else // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64) uint32_t mask = 0xFFFFFFFF; uint32_t x0 = (uint32_t)(x & mask); uint32_t x1 = (uint32_t)(x >> 32); uint32_t y0 = (uint32_t)(y & mask); uint32_t y1 = (uint32_t)(y >> 32); uint32_t x0y0_hi = libdivide__mullhi_u32(x0, y0); uint64_t x0y1 = x0 * (uint64_t)y1; uint64_t x1y0 = x1 * (uint64_t)y0; uint64_t x1y1 = x1 * (uint64_t)y1; uint64_t temp = x1y0 + x0y0_hi; uint64_t temp_lo = temp & mask; uint64_t temp_hi = temp >> 32; return x1y1 + temp_hi + ((temp_lo + x0y1) >> 32); #endif } static inline int64_t libdivide__mullhi_s64(int64_t x, int64_t y) { #if defined(LIBDIVIDE_VC) && defined(LIBDIVIDE_IS_X86_64) return __mulh(x, y); #elif defined(HAS_INT128_T) __int128_t xl = x, yl = y; __int128_t rl = xl * yl; return (int64_t)(rl >> 64); #else // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64) uint32_t mask = 0xFFFFFFFF; uint32_t x0 = (uint32_t)(x & mask); uint32_t y0 = (uint32_t)(y & mask); int32_t x1 = (int32_t)(x >> 32); int32_t y1 = (int32_t)(y >> 32); uint32_t x0y0_hi = libdivide__mullhi_u32(x0, y0); int64_t t = x1 * (int64_t)y0 + x0y0_hi; int64_t w1 = x0 * (int64_t)y1 + (t & mask); return x1 * (int64_t)y1 + (t >> 32) + (w1 >> 32); #endif } #if defined(LIBDIVIDE_USE_SSE2) static inline __m128i libdivide__u64_to_m128(uint64_t x) { #if defined(LIBDIVIDE_VC) && !defined(_WIN64) // 64 bit windows doesn't seem to have an implementation of any of these // load intrinsics, and 32 bit Visual C++ crashes _declspec(align(16)) uint64_t temp[2] = {x, x}; return _mm_load_si128((const __m128i*)temp); #else // everyone else gets it right return _mm_set1_epi64x(x); #endif } static inline __m128i libdivide_get_FFFFFFFF00000000(void) { // returns the same as _mm_set1_epi64(0xFFFFFFFF00000000ULL) // without touching memory. // optimizes to pcmpeqd on OS X __m128i result = _mm_set1_epi8(-1); return _mm_slli_epi64(result, 32); } static inline __m128i libdivide_get_00000000FFFFFFFF(void) { // returns the same as _mm_set1_epi64(0x00000000FFFFFFFFULL) // without touching memory. // optimizes to pcmpeqd on OS X __m128i result = _mm_set1_epi8(-1); result = _mm_srli_epi64(result, 32); return result; } static inline __m128i libdivide_s64_signbits(__m128i v) { // we want to compute v >> 63, that is, _mm_srai_epi64(v, 63). But there // is no 64 bit shift right arithmetic instruction in SSE2. So we have to // fake it by first duplicating the high 32 bit values, and then using a 32 // bit shift. Another option would be to use _mm_srli_epi64(v, 63) and // then subtract that from 0, but that approach appears to be substantially // slower for unknown reasons __m128i hiBitsDuped = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 3, 1, 1)); __m128i signBits = _mm_srai_epi32(hiBitsDuped, 31); return signBits; } // Returns an __m128i whose low 32 bits are equal to amt and has zero elsewhere. static inline __m128i libdivide_u32_to_m128i(uint32_t amt) { return _mm_set_epi32(0, 0, 0, amt); } static inline __m128i libdivide_s64_shift_right_vector(__m128i v, int amt) { // implementation of _mm_sra_epi64. Here we have two 64 bit values which // are shifted right to logically become (64 - amt) values, and are then // sign extended from a (64 - amt) bit number. const int b = 64 - amt; __m128i m = libdivide__u64_to_m128(1ULL << (b - 1)); __m128i x = _mm_srl_epi64(v, libdivide_u32_to_m128i(amt)); __m128i result = _mm_sub_epi64(_mm_xor_si128(x, m), m); // result = x^m - m return result; } // Here, b is assumed to contain one 32 bit value repeated four times. // If it did not, the function would not work. static inline __m128i libdivide__mullhi_u32_flat_vector(__m128i a, __m128i b) { __m128i hi_product_0Z2Z = _mm_srli_epi64(_mm_mul_epu32(a, b), 32); __m128i a1X3X = _mm_srli_epi64(a, 32); __m128i mask = libdivide_get_FFFFFFFF00000000(); __m128i hi_product_Z1Z3 = _mm_and_si128(_mm_mul_epu32(a1X3X, b), mask); return _mm_or_si128(hi_product_0Z2Z, hi_product_Z1Z3); // = hi_product_0123 } // Here, y is assumed to contain one 64 bit value repeated twice. static inline __m128i libdivide_mullhi_u64_flat_vector(__m128i x, __m128i y) { // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64) __m128i mask = libdivide_get_00000000FFFFFFFF(); // x0 is low half of 2 64 bit values, x1 is high half in low slots __m128i x0 = _mm_and_si128(x, mask); __m128i x1 = _mm_srli_epi64(x, 32); __m128i y0 = _mm_and_si128(y, mask); __m128i y1 = _mm_srli_epi64(y, 32); // x0 happens to have the low half of the two 64 bit values in 32 bit slots // 0 and 2, so _mm_mul_epu32 computes their full product, and then we shift // right by 32 to get just the high values __m128i x0y0_hi = _mm_srli_epi64(_mm_mul_epu32(x0, y0), 32); __m128i x0y1 = _mm_mul_epu32(x0, y1); __m128i x1y0 = _mm_mul_epu32(x1, y0); __m128i x1y1 = _mm_mul_epu32(x1, y1); __m128i temp = _mm_add_epi64(x1y0, x0y0_hi); __m128i temp_lo = _mm_and_si128(temp, mask); __m128i temp_hi = _mm_srli_epi64(temp, 32); temp_lo = _mm_srli_epi64(_mm_add_epi64(temp_lo, x0y1), 32); temp_hi = _mm_add_epi64(x1y1, temp_hi); return _mm_add_epi64(temp_lo, temp_hi); } // y is one 64 bit value repeated twice static inline __m128i libdivide_mullhi_s64_flat_vector(__m128i x, __m128i y) { __m128i p = libdivide_mullhi_u64_flat_vector(x, y); __m128i t1 = _mm_and_si128(libdivide_s64_signbits(x), y); p = _mm_sub_epi64(p, t1); __m128i t2 = _mm_and_si128(libdivide_s64_signbits(y), x); p = _mm_sub_epi64(p, t2); return p; } #ifdef LIBDIVIDE_USE_SSE4_1 // b is one 32 bit value repeated four times. static inline __m128i libdivide_mullhi_s32_flat_vector(__m128i a, __m128i b) { __m128i hi_product_0Z2Z = _mm_srli_epi64(_mm_mul_epi32(a, b), 32); __m128i a1X3X = _mm_srli_epi64(a, 32); __m128i mask = libdivide_get_FFFFFFFF00000000(); __m128i hi_product_Z1Z3 = _mm_and_si128(_mm_mul_epi32(a1X3X, b), mask); return _mm_or_si128(hi_product_0Z2Z, hi_product_Z1Z3); // = hi_product_0123 } #else // SSE2 does not have a signed multiplication instruction, but we can convert // unsigned to signed pretty efficiently. Again, b is just a 32 bit value // repeated four times. static inline __m128i libdivide_mullhi_s32_flat_vector(__m128i a, __m128i b) { __m128i p = libdivide__mullhi_u32_flat_vector(a, b); __m128i t1 = _mm_and_si128(_mm_srai_epi32(a, 31), b); // t1 = (a >> 31) & y, arithmetic shift __m128i t2 = _mm_and_si128(_mm_srai_epi32(b, 31), a); p = _mm_sub_epi32(p, t1); p = _mm_sub_epi32(p, t2); return p; } #endif // LIBDIVIDE_USE_SSE4_1 #endif // LIBDIVIDE_USE_SSE2 static inline int32_t libdivide__count_leading_zeros32(uint32_t val) { #if defined(__GNUC__) || __has_builtin(__builtin_clz) // Fast way to count leading zeros return __builtin_clz(val); #elif defined(LIBDIVIDE_VC) unsigned long result; if (_BitScanReverse(&result, val)) { return 31 - result; } return 0; #else int32_t result = 0; uint32_t hi = 1U << 31; while (~val & hi) { hi >>= 1; result++; } return result; #endif } static inline int32_t libdivide__count_leading_zeros64(uint64_t val) { #if defined(__GNUC__) || __has_builtin(__builtin_clzll) // Fast way to count leading zeros return __builtin_clzll(val); #elif defined(LIBDIVIDE_VC) && defined(_WIN64) unsigned long result; if (_BitScanReverse64(&result, val)) { return 63 - result; } return 0; #else uint32_t hi = val >> 32; uint32_t lo = val & 0xFFFFFFFF; if (hi != 0) return libdivide__count_leading_zeros32(hi); return 32 + libdivide__count_leading_zeros32(lo); #endif } #if (defined(LIBDIVIDE_IS_i386) || defined(LIBDIVIDE_IS_X86_64)) && \ defined(LIBDIVIDE_GCC_STYLE_ASM) // libdivide_64_div_32_to_32: divides a 64 bit uint {u1, u0} by a 32 bit // uint {v}. The result must fit in 32 bits. // Returns the quotient directly and the remainder in *r static uint32_t libdivide_64_div_32_to_32(uint32_t u1, uint32_t u0, uint32_t v, uint32_t *r) { uint32_t result; __asm__("divl %[v]" : "=a"(result), "=d"(*r) : [v] "r"(v), "a"(u0), "d"(u1) ); return result; } #else static uint32_t libdivide_64_div_32_to_32(uint32_t u1, uint32_t u0, uint32_t v, uint32_t *r) { uint64_t n = (((uint64_t)u1) << 32) | u0; uint32_t result = (uint32_t)(n / v); *r = (uint32_t)(n - result * (uint64_t)v); return result; } #endif #if defined(LIBDIVIDE_IS_X86_64) && \ defined(LIBDIVIDE_GCC_STYLE_ASM) static uint64_t libdivide_128_div_64_to_64(uint64_t u1, uint64_t u0, uint64_t v, uint64_t *r) { // u0 -> rax // u1 -> rdx // divq uint64_t result; __asm__("divq %[v]" : "=a"(result), "=d"(*r) : [v] "r"(v), "a"(u0), "d"(u1) ); return result; } #else // Code taken from Hacker's Delight: // http://www.hackersdelight.org/HDcode/divlu.c. // License permits inclusion here per: // http://www.hackersdelight.org/permissions.htm static uint64_t libdivide_128_div_64_to_64(uint64_t u1, uint64_t u0, uint64_t v, uint64_t *r) { const uint64_t b = (1ULL << 32); // Number base (16 bits) uint64_t un1, un0; // Norm. dividend LSD's uint64_t vn1, vn0; // Norm. divisor digits uint64_t q1, q0; // Quotient digits uint64_t un64, un21, un10; // Dividend digit pairs uint64_t rhat; // A remainder int s; // Shift amount for norm // If overflow, set rem. to an impossible value, // and return the largest possible quotient if (u1 >= v) { if (r != NULL) *r = (uint64_t) -1; return (uint64_t) -1; } // count leading zeros s = libdivide__count_leading_zeros64(v); if (s > 0) { // Normalize divisor v = v << s; un64 = (u1 << s) | ((u0 >> (64 - s)) & (-s >> 31)); un10 = u0 << s; // Shift dividend left } else { // Avoid undefined behavior un64 = u1 | u0; un10 = u0; } // Break divisor up into two 32-bit digits vn1 = v >> 32; vn0 = v & 0xFFFFFFFF; // Break right half of dividend into two digits un1 = un10 >> 32; un0 = un10 & 0xFFFFFFFF; // Compute the first quotient digit, q1 q1 = un64 / vn1; rhat = un64 - q1 * vn1; while (q1 >= b || q1 * vn0 > b * rhat + un1) { q1 = q1 - 1; rhat = rhat + vn1; if (rhat >= b) break; } // Multiply and subtract un21 = un64 * b + un1 - q1 * v; // Compute the second quotient digit q0 = un21 / vn1; rhat = un21 - q0 * vn1; while (q0 >= b || q0 * vn0 > b * rhat + un0) { q0 = q0 - 1; rhat = rhat + vn1; if (rhat >= b) break; } // If remainder is wanted, return it if (r != NULL) *r = (un21 * b + un0 - q0 * v) >> s; return q1 * b + q0; } #endif // Bitshift a u128 in place, left (signed_shift > 0) or right (signed_shift < 0) static inline void libdivide_u128_shift(uint64_t *u1, uint64_t *u0, int32_t signed_shift) { if (signed_shift > 0) { uint32_t shift = signed_shift; *u1 <<= shift; *u1 |= *u0 >> (64 - shift); *u0 <<= shift; } else { uint32_t shift = -signed_shift; *u0 >>= shift; *u0 |= *u1 << (64 - shift); *u1 >>= shift; } } // Computes a 128 / 128 -> 64 bit division, with a 128 bit remainder. static uint64_t libdivide_128_div_128_to_64(uint64_t u_hi, uint64_t u_lo, uint64_t v_hi, uint64_t v_lo, uint64_t *r_hi, uint64_t *r_lo) { #if defined(HAS_INT128_T) __uint128_t ufull = u_hi; __uint128_t vfull = v_hi; ufull = (ufull << 64) | u_lo; vfull = (vfull << 64) | v_lo; uint64_t res = (uint64_t)(ufull / vfull); __uint128_t remainder = ufull - (vfull * res); *r_lo = (uint64_t)remainder; *r_hi = (uint64_t)(remainder >> 64); return res; #else // Adapted from "Unsigned Doubleword Division" in Hacker's Delight // We want to compute u / v typedef struct { uint64_t hi; uint64_t lo; } u128_t; u128_t u = {u_hi, u_lo}; u128_t v = {v_hi, v_lo}; if (v.hi == 0) { // divisor v is a 64 bit value, so we just need one 128/64 division // Note that we are simpler than Hacker's Delight here, because we know // the quotient fits in 64 bits whereas Hacker's Delight demands a full // 128 bit quotient *r_hi = 0; return libdivide_128_div_64_to_64(u.hi, u.lo, v.lo, r_lo); } // Here v >= 2**64 // We know that v.hi != 0, so count leading zeros is OK // We have 0 <= n <= 63 uint32_t n = libdivide__count_leading_zeros64(v.hi); // Normalize the divisor so its MSB is 1 u128_t v1t = v; libdivide_u128_shift(&v1t.hi, &v1t.lo, n); uint64_t v1 = v1t.hi; // i.e. v1 = v1t >> 64 // To ensure no overflow u128_t u1 = u; libdivide_u128_shift(&u1.hi, &u1.lo, -1); // Get quotient from divide unsigned insn. uint64_t rem_ignored; uint64_t q1 = libdivide_128_div_64_to_64(u1.hi, u1.lo, v1, &rem_ignored); // Undo normalization and division of u by 2. u128_t q0 = {0, q1}; libdivide_u128_shift(&q0.hi, &q0.lo, n); libdivide_u128_shift(&q0.hi, &q0.lo, -63); // Make q0 correct or too small by 1 // Equivalent to `if (q0 != 0) q0 = q0 - 1;` if (q0.hi != 0 || q0.lo != 0) { q0.hi -= (q0.lo == 0); // borrow q0.lo -= 1; } // Now q0 is correct. // Compute q0 * v as q0v // = (q0.hi << 64 + q0.lo) * (v.hi << 64 + v.lo) // = (q0.hi * v.hi << 128) + (q0.hi * v.lo << 64) + // (q0.lo * v.hi << 64) + q0.lo * v.lo) // Each term is 128 bit // High half of full product (upper 128 bits!) are dropped u128_t q0v = {0, 0}; q0v.hi = q0.hi*v.lo + q0.lo*v.hi + libdivide__mullhi_u64(q0.lo, v.lo); q0v.lo = q0.lo*v.lo; // Compute u - q0v as u_q0v // This is the remainder u128_t u_q0v = u; u_q0v.hi -= q0v.hi + (u.lo < q0v.lo); // second term is borrow u_q0v.lo -= q0v.lo; // Check if u_q0v >= v // This checks if our remainder is larger than the divisor if ((u_q0v.hi > v.hi) || (u_q0v.hi == v.hi && u_q0v.lo >= v.lo)) { // Increment q0 q0.lo += 1; q0.hi += (q0.lo == 0); // carry // Subtract v from remainder u_q0v.hi -= v.hi + (u_q0v.lo < v.lo); u_q0v.lo -= v.lo; } *r_hi = u_q0v.hi; *r_lo = u_q0v.lo; LIBDIVIDE_ASSERT(q0.hi == 0); return q0.lo; #endif } ////////// UINT32 static inline struct libdivide_u32_t libdivide_internal_u32_gen(uint32_t d, int branchfree) { if (d == 0) { LIBDIVIDE_ERROR("divider must be != 0"); } struct libdivide_u32_t result; uint32_t floor_log_2_d = 31 - libdivide__count_leading_zeros32(d); if ((d & (d - 1)) == 0) { // Power of 2 if (! branchfree) { result.magic = 0; result.more = floor_log_2_d | LIBDIVIDE_U32_SHIFT_PATH; } else { // We want a magic number of 2**32 and a shift of floor_log_2_d // but one of the shifts is taken up by LIBDIVIDE_ADD_MARKER, // so we subtract 1 from the shift result.magic = 0; result.more = (floor_log_2_d-1) | LIBDIVIDE_ADD_MARKER; } } else { uint8_t more; uint32_t rem, proposed_m; proposed_m = libdivide_64_div_32_to_32(1U << floor_log_2_d, 0, d, &rem); LIBDIVIDE_ASSERT(rem > 0 && rem < d); const uint32_t e = d - rem; // This power works if e < 2**floor_log_2_d. if (!branchfree && (e < (1U << floor_log_2_d))) { // This power works more = floor_log_2_d; } else { // We have to use the general 33-bit algorithm. We need to compute // (2**power) / d. However, we already have (2**(power-1))/d and // its remainder. By doubling both, and then correcting the // remainder, we can compute the larger division. // don't care about overflow here - in fact, we expect it proposed_m += proposed_m; const uint32_t twice_rem = rem + rem; if (twice_rem >= d || twice_rem < rem) proposed_m += 1; more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; } result.magic = 1 + proposed_m; result.more = more; // result.more's shift should in general be ceil_log_2_d. But if we // used the smaller power, we subtract one from the shift because we're // using the smaller power. If we're using the larger power, we // subtract one from the shift because it's taken care of by the add // indicator. So floor_log_2_d happens to be correct in both cases. } return result; } struct libdivide_u32_t libdivide_u32_gen(uint32_t d) { return libdivide_internal_u32_gen(d, 0); } struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d) { if (d == 1) { LIBDIVIDE_ERROR("branchfree divider must be != 1"); } struct libdivide_u32_t tmp = libdivide_internal_u32_gen(d, 1); struct libdivide_u32_branchfree_t ret = {tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_32_SHIFT_MASK)}; return ret; } uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) { uint8_t more = denom->more; if (more & LIBDIVIDE_U32_SHIFT_PATH) { return numer >> (more & LIBDIVIDE_32_SHIFT_MASK); } else { uint32_t q = libdivide__mullhi_u32(denom->magic, numer); if (more & LIBDIVIDE_ADD_MARKER) { uint32_t t = ((numer - q) >> 1) + q; return t >> (more & LIBDIVIDE_32_SHIFT_MASK); } else { // all upper bits are 0 - don't need to mask them off return q >> more; } } } uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom) { uint8_t more = denom->more; uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; if (more & LIBDIVIDE_U32_SHIFT_PATH) { return 1U << shift; } else if (!(more & LIBDIVIDE_ADD_MARKER)) { // We compute q = n/d = n*m / 2^(32 + shift) // Therefore we have d = 2^(32 + shift) / m // We need to ceil it. // We know d is not a power of 2, so m is not a power of 2, // so we can just add 1 to the floor uint32_t hi_dividend = 1U << shift; uint32_t rem_ignored; return 1 + libdivide_64_div_32_to_32(hi_dividend, 0, denom->magic, &rem_ignored); } else { // Here we wish to compute d = 2^(32+shift+1)/(m+2^32). // Notice (m + 2^32) is a 33 bit number. Use 64 bit division for now // Also note that shift may be as high as 31, so shift + 1 will // overflow. So we have to compute it as 2^(32+shift)/(m+2^32), and // then double the quotient and remainder. uint64_t half_n = 1ULL << (32 + shift); uint64_t d = (1ULL << 32) | denom->magic; // Note that the quotient is guaranteed <= 32 bits, but the remainder // may need 33! uint32_t half_q = (uint32_t)(half_n / d); uint64_t rem = half_n % d; // We computed 2^(32+shift)/(m+2^32) // Need to double it, and then add 1 to the quotient if doubling th // remainder would increase the quotient. // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits uint32_t full_q = half_q + half_q + ((rem<<1) >= d); // We rounded down in gen unless we're a power of 2 (i.e. in branchfree case) // We can detect that by looking at m. If m zero, we're a power of 2 return full_q + (denom->magic != 0); } } uint32_t libdivide_u32_branchfree_recover(const struct libdivide_u32_branchfree_t *denom) { struct libdivide_u32_t denom_u32 = {denom->magic, (uint8_t)(denom->more | LIBDIVIDE_ADD_MARKER)}; return libdivide_u32_recover(&denom_u32); } int libdivide_u32_get_algorithm(const struct libdivide_u32_t *denom) { uint8_t more = denom->more; if (more & LIBDIVIDE_U32_SHIFT_PATH) return 0; else if (!(more & LIBDIVIDE_ADD_MARKER)) return 1; else return 2; } uint32_t libdivide_u32_do_alg0(uint32_t numer, const struct libdivide_u32_t *denom) { return numer >> (denom->more & LIBDIVIDE_32_SHIFT_MASK); } uint32_t libdivide_u32_do_alg1(uint32_t numer, const struct libdivide_u32_t *denom) { uint32_t q = libdivide__mullhi_u32(denom->magic, numer); return q >> denom->more; } uint32_t libdivide_u32_do_alg2(uint32_t numer, const struct libdivide_u32_t *denom) { // denom->add != 0 uint32_t q = libdivide__mullhi_u32(denom->magic, numer); uint32_t t = ((numer - q) >> 1) + q; // Note that this mask is typically free. Only the low bits are meaningful // to a shift, so compilers can optimize out this AND. return t >> (denom->more & LIBDIVIDE_32_SHIFT_MASK); } // same as algo 2 uint32_t libdivide_u32_branchfree_do(uint32_t numer, const struct libdivide_u32_branchfree_t *denom) { uint32_t q = libdivide__mullhi_u32(denom->magic, numer); uint32_t t = ((numer - q) >> 1) + q; return t >> denom->more; } #if defined(LIBDIVIDE_USE_SSE2) __m128i libdivide_u32_do_vector(__m128i numers, const struct libdivide_u32_t *denom) { uint8_t more = denom->more; if (more & LIBDIVIDE_U32_SHIFT_PATH) { return _mm_srl_epi32(numers, libdivide_u32_to_m128i(more & LIBDIVIDE_32_SHIFT_MASK)); } else { __m128i q = libdivide__mullhi_u32_flat_vector(numers, _mm_set1_epi32(denom->magic)); if (more & LIBDIVIDE_ADD_MARKER) { // uint32_t t = ((numer - q) >> 1) + q; // return t >> denom->shift; __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q); return _mm_srl_epi32(t, libdivide_u32_to_m128i(more & LIBDIVIDE_32_SHIFT_MASK)); } else { // q >> denom->shift return _mm_srl_epi32(q, libdivide_u32_to_m128i(more)); } } } __m128i libdivide_u32_do_vector_alg0(__m128i numers, const struct libdivide_u32_t *denom) { return _mm_srl_epi32(numers, libdivide_u32_to_m128i(denom->more & LIBDIVIDE_32_SHIFT_MASK)); } __m128i libdivide_u32_do_vector_alg1(__m128i numers, const struct libdivide_u32_t *denom) { __m128i q = libdivide__mullhi_u32_flat_vector(numers, _mm_set1_epi32(denom->magic)); return _mm_srl_epi32(q, libdivide_u32_to_m128i(denom->more)); } __m128i libdivide_u32_do_vector_alg2(__m128i numers, const struct libdivide_u32_t *denom) { __m128i q = libdivide__mullhi_u32_flat_vector(numers, _mm_set1_epi32(denom->magic)); __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q); return _mm_srl_epi32(t, libdivide_u32_to_m128i(denom->more & LIBDIVIDE_32_SHIFT_MASK)); } // same as algo 2 LIBDIVIDE_API __m128i libdivide_u32_branchfree_do_vector(__m128i numers, const struct libdivide_u32_branchfree_t *denom) { __m128i q = libdivide__mullhi_u32_flat_vector(numers, _mm_set1_epi32(denom->magic)); __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q); return _mm_srl_epi32(t, libdivide_u32_to_m128i(denom->more)); } #endif /////////// UINT64 static inline struct libdivide_u64_t libdivide_internal_u64_gen(uint64_t d, int branchfree) { if (d == 0) { LIBDIVIDE_ERROR("divider must be != 0"); } struct libdivide_u64_t result; uint32_t floor_log_2_d = 63 - libdivide__count_leading_zeros64(d); if ((d & (d - 1)) == 0) { // Power of 2 if (! branchfree) { result.magic = 0; result.more = floor_log_2_d | LIBDIVIDE_U64_SHIFT_PATH; } else { // We want a magic number of 2**64 and a shift of floor_log_2_d // but one of the shifts is taken up by LIBDIVIDE_ADD_MARKER, // so we subtract 1 from the shift result.magic = 0; result.more = (floor_log_2_d-1) | LIBDIVIDE_ADD_MARKER; } } else { uint64_t proposed_m, rem; uint8_t more; // (1 << (64 + floor_log_2_d)) / d proposed_m = libdivide_128_div_64_to_64(1ULL << floor_log_2_d, 0, d, &rem); LIBDIVIDE_ASSERT(rem > 0 && rem < d); const uint64_t e = d - rem; // This power works if e < 2**floor_log_2_d. if (!branchfree && e < (1ULL << floor_log_2_d)) { // This power works more = floor_log_2_d; } else { // We have to use the general 65-bit algorithm. We need to compute // (2**power) / d. However, we already have (2**(power-1))/d and // its remainder. By doubling both, and then correcting the // remainder, we can compute the larger division. // don't care about overflow here - in fact, we expect it proposed_m += proposed_m; const uint64_t twice_rem = rem + rem; if (twice_rem >= d || twice_rem < rem) proposed_m += 1; more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; } result.magic = 1 + proposed_m; result.more = more; // result.more's shift should in general be ceil_log_2_d. But if we // used the smaller power, we subtract one from the shift because we're // using the smaller power. If we're using the larger power, we // subtract one from the shift because it's taken care of by the add // indicator. So floor_log_2_d happens to be correct in both cases, // which is why we do it outside of the if statement. } return result; } struct libdivide_u64_t libdivide_u64_gen(uint64_t d) { return libdivide_internal_u64_gen(d, 0); } struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d) { if (d == 1) { LIBDIVIDE_ERROR("branchfree divider must be != 1"); } struct libdivide_u64_t tmp = libdivide_internal_u64_gen(d, 1); struct libdivide_u64_branchfree_t ret = {tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_64_SHIFT_MASK)}; return ret; } uint64_t libdivide_u64_do(uint64_t numer, const struct libdivide_u64_t *denom) { uint8_t more = denom->more; if (more & LIBDIVIDE_U64_SHIFT_PATH) { return numer >> (more & LIBDIVIDE_64_SHIFT_MASK); } else { uint64_t q = libdivide__mullhi_u64(denom->magic, numer); if (more & LIBDIVIDE_ADD_MARKER) { uint64_t t = ((numer - q) >> 1) + q; return t >> (more & LIBDIVIDE_64_SHIFT_MASK); } else { // all upper bits are 0 - don't need to mask them off return q >> more; } } } uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom) { uint8_t more = denom->more; uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; if (more & LIBDIVIDE_U64_SHIFT_PATH) { return 1ULL << shift; } else if (!(more & LIBDIVIDE_ADD_MARKER)) { // We compute q = n/d = n*m / 2^(64 + shift) // Therefore we have d = 2^(64 + shift) / m // We need to ceil it. // We know d is not a power of 2, so m is not a power of 2, // so we can just add 1 to the floor uint64_t hi_dividend = 1ULL << shift; uint64_t rem_ignored; return 1 + libdivide_128_div_64_to_64(hi_dividend, 0, denom->magic, &rem_ignored); } else { // Here we wish to compute d = 2^(64+shift+1)/(m+2^64). // Notice (m + 2^64) is a 65 bit number. This gets hairy. See // libdivide_u32_recover for more on what we do here. // TODO: do something better than 128 bit math // Hack: if d is not a power of 2, this is a 128/128->64 divide // If d is a power of 2, this may be a bigger divide // However we can optimize that easily if (denom->magic == 0) { // 2^(64 + shift + 1) / (2^64) == 2^(shift + 1) return 1ULL << (shift + 1); } // Full n is a (potentially) 129 bit value // half_n is a 128 bit value // Compute the hi half of half_n. Low half is 0. uint64_t half_n_hi = 1ULL << shift, half_n_lo = 0; // d is a 65 bit value. The high bit is always set to 1. const uint64_t d_hi = 1, d_lo = denom->magic; // Note that the quotient is guaranteed <= 64 bits, // but the remainder may need 65! uint64_t r_hi, r_lo; uint64_t half_q = libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo); // We computed 2^(64+shift)/(m+2^64) // Double the remainder ('dr') and check if that is larger than d // Note that d is a 65 bit value, so r1 is small and so r1 + r1 cannot // overflow uint64_t dr_lo = r_lo + r_lo; uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo); // last term is carry int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo); uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0); return full_q + 1; } } uint64_t libdivide_u64_branchfree_recover(const struct libdivide_u64_branchfree_t *denom) { struct libdivide_u64_t denom_u64 = {denom->magic, (uint8_t)(denom->more | LIBDIVIDE_ADD_MARKER)}; return libdivide_u64_recover(&denom_u64); } int libdivide_u64_get_algorithm(const struct libdivide_u64_t *denom) { uint8_t more = denom->more; if (more & LIBDIVIDE_U64_SHIFT_PATH) return 0; else if (!(more & LIBDIVIDE_ADD_MARKER)) return 1; else return 2; } uint64_t libdivide_u64_do_alg0(uint64_t numer, const struct libdivide_u64_t *denom) { return numer >> (denom->more & LIBDIVIDE_64_SHIFT_MASK); } uint64_t libdivide_u64_do_alg1(uint64_t numer, const struct libdivide_u64_t *denom) { uint64_t q = libdivide__mullhi_u64(denom->magic, numer); return q >> denom->more; } uint64_t libdivide_u64_do_alg2(uint64_t numer, const struct libdivide_u64_t *denom) { uint64_t q = libdivide__mullhi_u64(denom->magic, numer); uint64_t t = ((numer - q) >> 1) + q; return t >> (denom->more & LIBDIVIDE_64_SHIFT_MASK); } // same as alg 2 uint64_t libdivide_u64_branchfree_do(uint64_t numer, const struct libdivide_u64_branchfree_t *denom) { uint64_t q = libdivide__mullhi_u64(denom->magic, numer); uint64_t t = ((numer - q) >> 1) + q; return t >> denom->more; } #if defined(LIBDIVIDE_USE_SSE2) __m128i libdivide_u64_do_vector(__m128i numers, const struct libdivide_u64_t *denom) { uint8_t more = denom->more; if (more & LIBDIVIDE_U64_SHIFT_PATH) { return _mm_srl_epi64(numers, libdivide_u32_to_m128i(more & LIBDIVIDE_64_SHIFT_MASK)); } else { __m128i q = libdivide_mullhi_u64_flat_vector(numers, libdivide__u64_to_m128(denom->magic)); if (more & LIBDIVIDE_ADD_MARKER) { // uint32_t t = ((numer - q) >> 1) + q; // return t >> denom->shift; __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q); return _mm_srl_epi64(t, libdivide_u32_to_m128i(more & LIBDIVIDE_64_SHIFT_MASK)); } else { // q >> denom->shift return _mm_srl_epi64(q, libdivide_u32_to_m128i(more)); } } } __m128i libdivide_u64_do_vector_alg0(__m128i numers, const struct libdivide_u64_t *denom) { return _mm_srl_epi64(numers, libdivide_u32_to_m128i(denom->more & LIBDIVIDE_64_SHIFT_MASK)); } __m128i libdivide_u64_do_vector_alg1(__m128i numers, const struct libdivide_u64_t *denom) { __m128i q = libdivide_mullhi_u64_flat_vector(numers, libdivide__u64_to_m128(denom->magic)); return _mm_srl_epi64(q, libdivide_u32_to_m128i(denom->more)); } __m128i libdivide_u64_do_vector_alg2(__m128i numers, const struct libdivide_u64_t *denom) { __m128i q = libdivide_mullhi_u64_flat_vector(numers, libdivide__u64_to_m128(denom->magic)); __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q); return _mm_srl_epi64(t, libdivide_u32_to_m128i(denom->more & LIBDIVIDE_64_SHIFT_MASK)); } __m128i libdivide_u64_branchfree_do_vector(__m128i numers, const struct libdivide_u64_branchfree_t *denom) { __m128i q = libdivide_mullhi_u64_flat_vector(numers, libdivide__u64_to_m128(denom->magic)); __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q); return _mm_srl_epi64(t, libdivide_u32_to_m128i(denom->more)); } #endif /////////// SINT32 static inline int32_t libdivide__mullhi_s32(int32_t x, int32_t y) { int64_t xl = x, yl = y; int64_t rl = xl * yl; // needs to be arithmetic shift return (int32_t)(rl >> 32); } static inline struct libdivide_s32_t libdivide_internal_s32_gen(int32_t d, int branchfree) { if (d == 0) { LIBDIVIDE_ERROR("divider must be != 0"); } struct libdivide_s32_t result; // If d is a power of 2, or negative a power of 2, we have to use a shift. // This is especially important because the magic algorithm fails for -1. // To check if d is a power of 2 or its inverse, it suffices to check // whether its absolute value has exactly one bit set. This works even for // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set // and is a power of 2. uint32_t ud = (uint32_t)d; uint32_t absD = (d < 0) ? -ud : ud; uint32_t floor_log_2_d = 31 - libdivide__count_leading_zeros32(absD); // check if exactly one bit is set, // don't care if absD is 0 since that's divide by zero if ((absD & (absD - 1)) == 0) { // Branchfree and normal paths are exactly the same result.magic = 0; result.more = floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0) | LIBDIVIDE_S32_SHIFT_PATH; } else { LIBDIVIDE_ASSERT(floor_log_2_d >= 1); uint8_t more; // the dividend here is 2**(floor_log_2_d + 31), so the low 32 bit word // is 0 and the high word is floor_log_2_d - 1 uint32_t rem, proposed_m; proposed_m = libdivide_64_div_32_to_32(1U << (floor_log_2_d - 1), 0, absD, &rem); const uint32_t e = absD - rem; // We are going to start with a power of floor_log_2_d - 1. // This works if works if e < 2**floor_log_2_d. if (!branchfree && e < (1U << floor_log_2_d)) { // This power works more = floor_log_2_d - 1; } else { // We need to go one higher. This should not make proposed_m // overflow, but it will make it negative when interpreted as an // int32_t. proposed_m += proposed_m; const uint32_t twice_rem = rem + rem; if (twice_rem >= absD || twice_rem < rem) proposed_m += 1; more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; } proposed_m += 1; int32_t magic = (int32_t)proposed_m; // Mark if we are negative. Note we only negate the magic number in the // branchfull case. if (d < 0) { more |= LIBDIVIDE_NEGATIVE_DIVISOR; if (!branchfree) { magic = -magic; } } result.more = more; result.magic = magic; } return result; } LIBDIVIDE_API struct libdivide_s32_t libdivide_s32_gen(int32_t d) { return libdivide_internal_s32_gen(d, 0); } LIBDIVIDE_API struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d) { if (d == 1) { LIBDIVIDE_ERROR("branchfree divider must be != 1"); } if (d == -1) { LIBDIVIDE_ERROR("branchfree divider must be != -1"); } struct libdivide_s32_t tmp = libdivide_internal_s32_gen(d, 1); struct libdivide_s32_branchfree_t result = {tmp.magic, tmp.more}; return result; } int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom) { uint8_t more = denom->more; uint32_t sign = (int8_t)more >> 7; if (more & LIBDIVIDE_S32_SHIFT_PATH) { uint8_t shifter = more & LIBDIVIDE_32_SHIFT_MASK; uint32_t uq = (uint32_t)(numer + ((numer >> 31) & ((1U << shifter) - 1))); int32_t q = (int32_t)uq; q = q >> shifter; q = (q ^ sign) - sign; return q; } else { uint32_t uq = (uint32_t)libdivide__mullhi_s32(denom->magic, numer); if (more & LIBDIVIDE_ADD_MARKER) { // must be arithmetic shift and then sign extend int32_t sign = (int8_t)more >> 7; // q += (more < 0 ? -numer : numer), casts to avoid UB uq += ((uint32_t)numer ^ sign) - sign; } int32_t q = (int32_t)uq; q >>= more & LIBDIVIDE_32_SHIFT_MASK; q += (q < 0); return q; } } int32_t libdivide_s32_branchfree_do(int32_t numer, const struct libdivide_s32_branchfree_t *denom) { uint8_t more = denom->more; uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; // must be arithmetic shift and then sign extend int32_t sign = (int8_t)more >> 7; int32_t magic = denom->magic; int32_t q = libdivide__mullhi_s32(magic, numer); q += numer; // If q is non-negative, we have nothing to do // If q is negative, we want to add either (2**shift)-1 if d is a power of // 2, or (2**shift) if it is not a power of 2 uint32_t is_power_of_2 = !!(more & LIBDIVIDE_S32_SHIFT_PATH); uint32_t q_sign = (uint32_t)(q >> 31); q += q_sign & ((1 << shift) - is_power_of_2); // Now arithmetic right shift q >>= shift; // Negate if needed q = (q ^ sign) - sign; return q; } int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom) { uint8_t more = denom->more; uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; if (more & LIBDIVIDE_S32_SHIFT_PATH) { uint32_t absD = 1U << shift; if (more & LIBDIVIDE_NEGATIVE_DIVISOR) { absD = -absD; } return (int32_t)absD; } else { // Unsigned math is much easier // We negate the magic number only in the branchfull case, and we don't // know which case we're in. However we have enough information to // determine the correct sign of the magic number. The divisor was // negative if LIBDIVIDE_NEGATIVE_DIVISOR is set. If ADD_MARKER is set, // the magic number's sign is opposite that of the divisor. // We want to compute the positive magic number. int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR); int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) ? denom->magic > 0 : denom->magic < 0; // Handle the power of 2 case (including branchfree) if (denom->magic == 0) { int32_t result = 1 << shift; return negative_divisor ? -result : result; } uint32_t d = (uint32_t)(magic_was_negated ? -denom->magic : denom->magic); uint64_t n = 1ULL << (32 + shift); // this shift cannot exceed 30 uint32_t q = (uint32_t)(n / d); int32_t result = (int32_t)q; result += 1; return negative_divisor ? -result : result; } } int32_t libdivide_s32_branchfree_recover(const struct libdivide_s32_branchfree_t *denom) { return libdivide_s32_recover((const struct libdivide_s32_t *)denom); } int libdivide_s32_get_algorithm(const struct libdivide_s32_t *denom) { uint8_t more = denom->more; int positiveDivisor = !(more & LIBDIVIDE_NEGATIVE_DIVISOR); if (more & LIBDIVIDE_S32_SHIFT_PATH) return (positiveDivisor ? 0 : 1); else if (more & LIBDIVIDE_ADD_MARKER) return (positiveDivisor ? 2 : 3); else return 4; } int32_t libdivide_s32_do_alg0(int32_t numer, const struct libdivide_s32_t *denom) { uint8_t shifter = denom->more & LIBDIVIDE_32_SHIFT_MASK; int32_t q = numer + ((numer >> 31) & ((1U << shifter) - 1)); return q >> shifter; } int32_t libdivide_s32_do_alg1(int32_t numer, const struct libdivide_s32_t *denom) { uint8_t shifter = denom->more & LIBDIVIDE_32_SHIFT_MASK; int32_t q = numer + ((numer >> 31) & ((1U << shifter) - 1)); return - (q >> shifter); } int32_t libdivide_s32_do_alg2(int32_t numer, const struct libdivide_s32_t *denom) { int32_t q = libdivide__mullhi_s32(denom->magic, numer); q += numer; q >>= denom->more & LIBDIVIDE_32_SHIFT_MASK; q += (q < 0); return q; } int32_t libdivide_s32_do_alg3(int32_t numer, const struct libdivide_s32_t *denom) { int32_t q = libdivide__mullhi_s32(denom->magic, numer); q -= numer; q >>= denom->more & LIBDIVIDE_32_SHIFT_MASK; q += (q < 0); return q; } int32_t libdivide_s32_do_alg4(int32_t numer, const struct libdivide_s32_t *denom) { int32_t q = libdivide__mullhi_s32(denom->magic, numer); q >>= denom->more & LIBDIVIDE_32_SHIFT_MASK; q += (q < 0); return q; } #if defined(LIBDIVIDE_USE_SSE2) __m128i libdivide_s32_do_vector(__m128i numers, const struct libdivide_s32_t *denom) { uint8_t more = denom->more; if (more & LIBDIVIDE_S32_SHIFT_PATH) { uint32_t shifter = more & LIBDIVIDE_32_SHIFT_MASK; __m128i roundToZeroTweak = _mm_set1_epi32((1U << shifter) - 1); // could use _mm_srli_epi32 with an all -1 register __m128i q = _mm_add_epi32(numers, _mm_and_si128(_mm_srai_epi32(numers, 31), roundToZeroTweak)); //q = numer + ((numer >> 31) & roundToZeroTweak); q = _mm_sra_epi32(q, libdivide_u32_to_m128i(shifter)); // q = q >> shifter __m128i shiftMask = _mm_set1_epi32((int32_t)((int8_t)more >> 7)); // set all bits of shift mask = to the sign bit of more q = _mm_sub_epi32(_mm_xor_si128(q, shiftMask), shiftMask); // q = (q ^ shiftMask) - shiftMask; return q; } else { __m128i q = libdivide_mullhi_s32_flat_vector(numers, _mm_set1_epi32(denom->magic)); if (more & LIBDIVIDE_ADD_MARKER) { __m128i sign = _mm_set1_epi32((int32_t)(int8_t)more >> 7); // must be arithmetic shift q = _mm_add_epi32(q, _mm_sub_epi32(_mm_xor_si128(numers, sign), sign)); // q += ((numer ^ sign) - sign); } q = _mm_sra_epi32(q, libdivide_u32_to_m128i(more & LIBDIVIDE_32_SHIFT_MASK)); // q >>= shift q = _mm_add_epi32(q, _mm_srli_epi32(q, 31)); // q += (q < 0) return q; } } __m128i libdivide_s32_do_vector_alg0(__m128i numers, const struct libdivide_s32_t *denom) { uint8_t shifter = denom->more & LIBDIVIDE_32_SHIFT_MASK; __m128i roundToZeroTweak = _mm_set1_epi32((1U << shifter) - 1); __m128i q = _mm_add_epi32(numers, _mm_and_si128(_mm_srai_epi32(numers, 31), roundToZeroTweak)); return _mm_sra_epi32(q, libdivide_u32_to_m128i(shifter)); } __m128i libdivide_s32_do_vector_alg1(__m128i numers, const struct libdivide_s32_t *denom) { uint8_t shifter = denom->more & LIBDIVIDE_32_SHIFT_MASK; __m128i roundToZeroTweak = _mm_set1_epi32((1U << shifter) - 1); __m128i q = _mm_add_epi32(numers, _mm_and_si128(_mm_srai_epi32(numers, 31), roundToZeroTweak)); return _mm_sub_epi32(_mm_setzero_si128(), _mm_sra_epi32(q, libdivide_u32_to_m128i(shifter))); } __m128i libdivide_s32_do_vector_alg2(__m128i numers, const struct libdivide_s32_t *denom) { __m128i q = libdivide_mullhi_s32_flat_vector(numers, _mm_set1_epi32(denom->magic)); q = _mm_add_epi32(q, numers); q = _mm_sra_epi32(q, libdivide_u32_to_m128i(denom->more & LIBDIVIDE_32_SHIFT_MASK)); q = _mm_add_epi32(q, _mm_srli_epi32(q, 31)); return q; } __m128i libdivide_s32_do_vector_alg3(__m128i numers, const struct libdivide_s32_t *denom) { __m128i q = libdivide_mullhi_s32_flat_vector(numers, _mm_set1_epi32(denom->magic)); q = _mm_sub_epi32(q, numers); q = _mm_sra_epi32(q, libdivide_u32_to_m128i(denom->more & LIBDIVIDE_32_SHIFT_MASK)); q = _mm_add_epi32(q, _mm_srli_epi32(q, 31)); return q; } __m128i libdivide_s32_do_vector_alg4(__m128i numers, const struct libdivide_s32_t *denom) { uint8_t more = denom->more; __m128i q = libdivide_mullhi_s32_flat_vector(numers, _mm_set1_epi32(denom->magic)); q = _mm_sra_epi32(q, libdivide_u32_to_m128i(more & LIBDIVIDE_32_SHIFT_MASK)); //q >>= shift q = _mm_add_epi32(q, _mm_srli_epi32(q, 31)); // q += (q < 0) return q; } __m128i libdivide_s32_branchfree_do_vector(__m128i numers, const struct libdivide_s32_branchfree_t *denom) { int32_t magic = denom->magic; uint8_t more = denom->more; uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; // must be arithmetic shift __m128i sign = _mm_set1_epi32((int32_t)(int8_t)more >> 7); // libdivide__mullhi_s32(numers, magic); __m128i q = libdivide_mullhi_s32_flat_vector(numers, _mm_set1_epi32(magic)); q = _mm_add_epi32(q, numers); // q += numers // If q is non-negative, we have nothing to do // If q is negative, we want to add either (2**shift)-1 if d is a power of // 2, or (2**shift) if it is not a power of 2 uint32_t is_power_of_2 = (magic == 0); __m128i q_sign = _mm_srai_epi32(q, 31); // q_sign = q >> 31 __m128i mask = _mm_set1_epi32((1 << shift) - is_power_of_2); q = _mm_add_epi32(q, _mm_and_si128(q_sign, mask)); // q = q + (q_sign & mask) q = _mm_srai_epi32(q, shift); //q >>= shift q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign); // q = (q ^ sign) - sign return q; } #endif ///////////// SINT64 static inline struct libdivide_s64_t libdivide_internal_s64_gen(int64_t d, int branchfree) { if (d == 0) { LIBDIVIDE_ERROR("divider must be != 0"); } struct libdivide_s64_t result; // If d is a power of 2, or negative a power of 2, we have to use a shift. // This is especially important because the magic algorithm fails for -1. // To check if d is a power of 2 or its inverse, it suffices to check // whether its absolute value has exactly one bit set. This works even for // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set // and is a power of 2. uint64_t ud = (uint64_t)d; uint64_t absD = (d < 0) ? -ud : ud; uint32_t floor_log_2_d = 63 - libdivide__count_leading_zeros64(absD); // check if exactly one bit is set, // don't care if absD is 0 since that's divide by zero if ((absD & (absD - 1)) == 0) { // Branchfree and non-branchfree cases are the same result.magic = 0; result.more = floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0); } else { // the dividend here is 2**(floor_log_2_d + 63), so the low 64 bit word // is 0 and the high word is floor_log_2_d - 1 uint8_t more; uint64_t rem, proposed_m; proposed_m = libdivide_128_div_64_to_64(1ULL << (floor_log_2_d - 1), 0, absD, &rem); const uint64_t e = absD - rem; // We are going to start with a power of floor_log_2_d - 1. // This works if works if e < 2**floor_log_2_d. if (!branchfree && e < (1ULL << floor_log_2_d)) { // This power works more = floor_log_2_d - 1; } else { // We need to go one higher. This should not make proposed_m // overflow, but it will make it negative when interpreted as an // int32_t. proposed_m += proposed_m; const uint64_t twice_rem = rem + rem; if (twice_rem >= absD || twice_rem < rem) proposed_m += 1; // note that we only set the LIBDIVIDE_NEGATIVE_DIVISOR bit if we // also set ADD_MARKER this is an annoying optimization that // enables algorithm #4 to avoid the mask. However we always set it // in the branchfree case more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; } proposed_m += 1; int64_t magic = (int64_t)proposed_m; // Mark if we are negative if (d < 0) { more |= LIBDIVIDE_NEGATIVE_DIVISOR; if (!branchfree) { magic = -magic; } } result.more = more; result.magic = magic; } return result; } struct libdivide_s64_t libdivide_s64_gen(int64_t d) { return libdivide_internal_s64_gen(d, 0); } struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d) { if (d == 1) { LIBDIVIDE_ERROR("branchfree divider must be != 1"); } if (d == -1) { LIBDIVIDE_ERROR("branchfree divider must be != -1"); } struct libdivide_s64_t tmp = libdivide_internal_s64_gen(d, 1); struct libdivide_s64_branchfree_t ret = {tmp.magic, tmp.more}; return ret; } int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom) { uint8_t more = denom->more; int64_t magic = denom->magic; if (magic == 0) { //shift path uint32_t shifter = more & LIBDIVIDE_64_SHIFT_MASK; uint64_t uq = (uint64_t)numer + ((numer >> 63) & ((1ULL << shifter) - 1)); int64_t q = (int64_t)uq; q = q >> shifter; // must be arithmetic shift and then sign-extend int64_t shiftMask = (int8_t)more >> 7; q = (q ^ shiftMask) - shiftMask; return q; } else { uint64_t uq = (uint64_t)libdivide__mullhi_s64(magic, numer); if (more & LIBDIVIDE_ADD_MARKER) { // must be arithmetic shift and then sign extend int64_t sign = (int8_t)more >> 7; uq += ((uint64_t)numer ^ sign) - sign; } int64_t q = (int64_t)uq; q >>= more & LIBDIVIDE_64_SHIFT_MASK; q += (q < 0); return q; } } int64_t libdivide_s64_branchfree_do(int64_t numer, const struct libdivide_s64_branchfree_t *denom) { uint8_t more = denom->more; uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; // must be arithmetic shift and then sign extend int64_t sign = (int8_t)more >> 7; int64_t magic = denom->magic; int64_t q = libdivide__mullhi_s64(magic, numer); q += numer; // If q is non-negative, we have nothing to do. // If q is negative, we want to add either (2**shift)-1 if d is a power of // 2, or (2**shift) if it is not a power of 2. uint32_t is_power_of_2 = (magic == 0); uint64_t q_sign = (uint64_t)(q >> 63); q += q_sign & ((1ULL << shift) - is_power_of_2); // Arithmetic right shift q >>= shift; // Negate if needed q = (q ^ sign) - sign; return q; } int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom) { uint8_t more = denom->more; uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; if (denom->magic == 0) { // shift path uint64_t absD = 1ULL << shift; if (more & LIBDIVIDE_NEGATIVE_DIVISOR) { absD = -absD; } return (int64_t)absD; } else { // Unsigned math is much easier int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR); int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) ? denom->magic > 0 : denom->magic < 0; uint64_t d = (uint64_t)(magic_was_negated ? -denom->magic : denom->magic); uint64_t n_hi = 1ULL << shift, n_lo = 0; uint64_t rem_ignored; uint64_t q = libdivide_128_div_64_to_64(n_hi, n_lo, d, &rem_ignored); int64_t result = (int64_t)(q + 1); if (negative_divisor) { result = -result; } return result; } } int64_t libdivide_s64_branchfree_recover(const struct libdivide_s64_branchfree_t *denom) { return libdivide_s64_recover((const struct libdivide_s64_t *)denom); } int libdivide_s64_get_algorithm(const struct libdivide_s64_t *denom) { uint8_t more = denom->more; int positiveDivisor = !(more & LIBDIVIDE_NEGATIVE_DIVISOR); if (denom->magic == 0) return (positiveDivisor ? 0 : 1); // shift path else if (more & LIBDIVIDE_ADD_MARKER) return (positiveDivisor ? 2 : 3); else return 4; } int64_t libdivide_s64_do_alg0(int64_t numer, const struct libdivide_s64_t *denom) { uint32_t shifter = denom->more & LIBDIVIDE_64_SHIFT_MASK; int64_t q = numer + ((numer >> 63) & ((1ULL << shifter) - 1)); return q >> shifter; } int64_t libdivide_s64_do_alg1(int64_t numer, const struct libdivide_s64_t *denom) { // denom->shifter != -1 && demo->shiftMask != 0 uint32_t shifter = denom->more & LIBDIVIDE_64_SHIFT_MASK; int64_t q = numer + ((numer >> 63) & ((1ULL << shifter) - 1)); return - (q >> shifter); } int64_t libdivide_s64_do_alg2(int64_t numer, const struct libdivide_s64_t *denom) { int64_t q = libdivide__mullhi_s64(denom->magic, numer); q += numer; q >>= denom->more & LIBDIVIDE_64_SHIFT_MASK; q += (q < 0); return q; } int64_t libdivide_s64_do_alg3(int64_t numer, const struct libdivide_s64_t *denom) { int64_t q = libdivide__mullhi_s64(denom->magic, numer); q -= numer; q >>= denom->more & LIBDIVIDE_64_SHIFT_MASK; q += (q < 0); return q; } int64_t libdivide_s64_do_alg4(int64_t numer, const struct libdivide_s64_t *denom) { int64_t q = libdivide__mullhi_s64(denom->magic, numer); q >>= denom->more & LIBDIVIDE_64_SHIFT_MASK; q += (q < 0); return q; } #if defined(LIBDIVIDE_USE_SSE2) __m128i libdivide_s64_do_vector(__m128i numers, const struct libdivide_s64_t *denom) { uint8_t more = denom->more; int64_t magic = denom->magic; if (magic == 0) { // shift path uint32_t shifter = more & LIBDIVIDE_64_SHIFT_MASK; __m128i roundToZeroTweak = libdivide__u64_to_m128((1ULL << shifter) - 1); __m128i q = _mm_add_epi64(numers, _mm_and_si128(libdivide_s64_signbits(numers), roundToZeroTweak)); // q = numer + ((numer >> 63) & roundToZeroTweak); q = libdivide_s64_shift_right_vector(q, shifter); // q = q >> shifter __m128i shiftMask = _mm_set1_epi32((int32_t)((int8_t)more >> 7)); q = _mm_sub_epi64(_mm_xor_si128(q, shiftMask), shiftMask); // q = (q ^ shiftMask) - shiftMask; return q; } else { __m128i q = libdivide_mullhi_s64_flat_vector(numers, libdivide__u64_to_m128(magic)); if (more & LIBDIVIDE_ADD_MARKER) { __m128i sign = _mm_set1_epi32((int32_t)((int8_t)more >> 7)); // must be arithmetic shift q = _mm_add_epi64(q, _mm_sub_epi64(_mm_xor_si128(numers, sign), sign)); // q += ((numer ^ sign) - sign); } // q >>= denom->mult_path.shift q = libdivide_s64_shift_right_vector(q, more & LIBDIVIDE_64_SHIFT_MASK); q = _mm_add_epi64(q, _mm_srli_epi64(q, 63)); // q += (q < 0) return q; } } __m128i libdivide_s64_do_vector_alg0(__m128i numers, const struct libdivide_s64_t *denom) { uint32_t shifter = denom->more & LIBDIVIDE_64_SHIFT_MASK; __m128i roundToZeroTweak = libdivide__u64_to_m128((1ULL << shifter) - 1); __m128i q = _mm_add_epi64(numers, _mm_and_si128(libdivide_s64_signbits(numers), roundToZeroTweak)); q = libdivide_s64_shift_right_vector(q, shifter); return q; } __m128i libdivide_s64_do_vector_alg1(__m128i numers, const struct libdivide_s64_t *denom) { uint32_t shifter = denom->more & LIBDIVIDE_64_SHIFT_MASK; __m128i roundToZeroTweak = libdivide__u64_to_m128((1ULL << shifter) - 1); __m128i q = _mm_add_epi64(numers, _mm_and_si128(libdivide_s64_signbits(numers), roundToZeroTweak)); q = libdivide_s64_shift_right_vector(q, shifter); return _mm_sub_epi64(_mm_setzero_si128(), q); } __m128i libdivide_s64_do_vector_alg2(__m128i numers, const struct libdivide_s64_t *denom) { __m128i q = libdivide_mullhi_s64_flat_vector(numers, libdivide__u64_to_m128(denom->magic)); q = _mm_add_epi64(q, numers); q = libdivide_s64_shift_right_vector(q, denom->more & LIBDIVIDE_64_SHIFT_MASK); q = _mm_add_epi64(q, _mm_srli_epi64(q, 63)); // q += (q < 0) return q; } __m128i libdivide_s64_do_vector_alg3(__m128i numers, const struct libdivide_s64_t *denom) { __m128i q = libdivide_mullhi_s64_flat_vector(numers, libdivide__u64_to_m128(denom->magic)); q = _mm_sub_epi64(q, numers); q = libdivide_s64_shift_right_vector(q, denom->more & LIBDIVIDE_64_SHIFT_MASK); q = _mm_add_epi64(q, _mm_srli_epi64(q, 63)); // q += (q < 0) return q; } __m128i libdivide_s64_do_vector_alg4(__m128i numers, const struct libdivide_s64_t *denom) { __m128i q = libdivide_mullhi_s64_flat_vector(numers, libdivide__u64_to_m128(denom->magic)); q = libdivide_s64_shift_right_vector(q, denom->more & LIBDIVIDE_64_SHIFT_MASK); q = _mm_add_epi64(q, _mm_srli_epi64(q, 63)); return q; } __m128i libdivide_s64_branchfree_do_vector(__m128i numers, const struct libdivide_s64_branchfree_t *denom) { int64_t magic = denom->magic; uint8_t more = denom->more; uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; // must be arithmetic shift __m128i sign = _mm_set1_epi32((int32_t)(int8_t)more >> 7); // libdivide__mullhi_s64(numers, magic); __m128i q = libdivide_mullhi_s64_flat_vector(numers, libdivide__u64_to_m128(magic)); q = _mm_add_epi64(q, numers); // q += numers // If q is non-negative, we have nothing to do. // If q is negative, we want to add either (2**shift)-1 if d is a power of // 2, or (2**shift) if it is not a power of 2. uint32_t is_power_of_2 = (magic == 0); __m128i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63 __m128i mask = libdivide__u64_to_m128((1ULL << shift) - is_power_of_2); q = _mm_add_epi64(q, _mm_and_si128(q_sign, mask)); // q = q + (q_sign & mask) q = libdivide_s64_shift_right_vector(q, shift); // q >>= shift q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign); // q = (q ^ sign) - sign return q; } #endif /////////// C++ stuff #ifdef __cplusplus // Our divider struct is templated on both a type (like uint64_t) and an // algorithm index. BRANCHFULL is the default algorithm, BRANCHFREE is the // branchfree variant, and the indexed variants are for unswitching. enum { BRANCHFULL = -1, BRANCHFREE = -2, ALGORITHM0 = 0, ALGORITHM1 = 1, ALGORITHM2 = 2, ALGORITHM3 = 3, ALGORITHM4 = 4 }; namespace libdivide_internal { #if defined(LIBDIVIDE_USE_SSE2) #define MAYBE_VECTOR(X) X #define MAYBE_VECTOR_PARAM(X) __m128i vector_func(__m128i, const X *) #else #define MAYBE_VECTOR(X) 0 #define MAYBE_VECTOR_PARAM(X) int unused #endif // The following convenience macros are used to build a type of the base // divider class and give it as template arguments the C functions // related to the macro name and the macro type paramaters. #define BRANCHFULL_DIVIDER(INT, TYPE) \ typedef base #define BRANCHFREE_DIVIDER(INT, TYPE) \ typedef base #define ALGORITHM_DIVIDER(INT, TYPE, ALGO) \ typedef base #define CRASH_DIVIDER(INT, TYPE) \ typedef base // Base divider, provides storage for the actual divider. // @IntType: e.g. uint32_t // @DenomType: e.g. libdivide_u32_t // @gen_func(): e.g. libdivide_u32_gen // @do_func(): e.g. libdivide_u32_do // @MAYBE_VECTOR_PARAM: e.g. libdivide_u32_do_vector template struct base { // Storage for the actual divider DenomType denom; // Constructor that takes a divisor value, and applies the gen function base(IntType d) : denom(gen_func(d)) { } // Default constructor to allow uninitialized uses in e.g. arrays base() {} // Needed for unswitch base(const DenomType& d) : denom(d) { } IntType perform_divide(IntType val) const { return do_func(val, &denom); } #if defined(LIBDIVIDE_USE_SSE2) __m128i perform_divide_vector(__m128i val) const { return vector_func(val, &denom); } #endif }; // Functions that will never be called but are required to be able // to use unswitch in C++ template code. Unsigned has fewer algorithms // than signed i.e. alg3 and alg4 are not defined for unsigned. In // order to make templates compile we need to define unsigned alg3 and // alg4 as crash functions. uint32_t libdivide_u32_crash(uint32_t, const libdivide_u32_t *) { exit(-1); } uint64_t libdivide_u64_crash(uint64_t, const libdivide_u64_t *) { exit(-1); } #if defined(LIBDIVIDE_USE_SSE2) __m128i libdivide_u32_crash_vector(__m128i, const libdivide_u32_t *) { exit(-1); } __m128i libdivide_u64_crash_vector(__m128i, const libdivide_u64_t *) { exit(-1); } #endif template struct dispatcher { }; // Templated dispatch using partial specialization template<> struct dispatcher { BRANCHFULL_DIVIDER(int32_t, s32) divider; }; template<> struct dispatcher { BRANCHFREE_DIVIDER(int32_t, s32) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(int32_t, s32, alg0) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(int32_t, s32, alg1) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(int32_t, s32, alg2) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(int32_t, s32, alg3) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(int32_t, s32, alg4) divider; }; template<> struct dispatcher { BRANCHFULL_DIVIDER(uint32_t, u32) divider; }; template<> struct dispatcher { BRANCHFREE_DIVIDER(uint32_t, u32) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(uint32_t, u32, alg0) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(uint32_t, u32, alg1) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(uint32_t, u32, alg2) divider; }; template<> struct dispatcher { CRASH_DIVIDER(uint32_t, u32) divider; }; template<> struct dispatcher { CRASH_DIVIDER(uint32_t, u32) divider; }; template<> struct dispatcher { BRANCHFULL_DIVIDER(int64_t, s64) divider; }; template<> struct dispatcher { BRANCHFREE_DIVIDER(int64_t, s64) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER (int64_t, s64, alg0) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER (int64_t, s64, alg1) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER (int64_t, s64, alg2) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER (int64_t, s64, alg3) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER (int64_t, s64, alg4) divider; }; template<> struct dispatcher { BRANCHFULL_DIVIDER(uint64_t, u64) divider; }; template<> struct dispatcher { BRANCHFREE_DIVIDER(uint64_t, u64) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(uint64_t, u64, alg0) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(uint64_t, u64, alg1) divider; }; template<> struct dispatcher { ALGORITHM_DIVIDER(uint64_t, u64, alg2) divider; }; template<> struct dispatcher { CRASH_DIVIDER(uint64_t, u64) divider; }; template<> struct dispatcher { CRASH_DIVIDER(uint64_t, u64) divider; }; // Overloads that don't depend on the algorithm inline int32_t recover(const libdivide_s32_t *s) { return libdivide_s32_recover(s); } inline uint32_t recover(const libdivide_u32_t *s) { return libdivide_u32_recover(s); } inline int64_t recover(const libdivide_s64_t *s) { return libdivide_s64_recover(s); } inline uint64_t recover(const libdivide_u64_t *s) { return libdivide_u64_recover(s); } inline int32_t recover(const libdivide_s32_branchfree_t *s) { return libdivide_s32_branchfree_recover(s); } inline uint32_t recover(const libdivide_u32_branchfree_t *s) { return libdivide_u32_branchfree_recover(s); } inline int64_t recover(const libdivide_s64_branchfree_t *s) { return libdivide_s64_branchfree_recover(s); } inline uint64_t recover(const libdivide_u64_branchfree_t *s) { return libdivide_u64_branchfree_recover(s); } inline int get_algorithm(const libdivide_s32_t *s) { return libdivide_s32_get_algorithm(s); } inline int get_algorithm(const libdivide_u32_t *s) { return libdivide_u32_get_algorithm(s); } inline int get_algorithm(const libdivide_s64_t *s) { return libdivide_s64_get_algorithm(s); } inline int get_algorithm(const libdivide_u64_t *s) { return libdivide_u64_get_algorithm(s); } // Fallback for branchfree variants, which do not support unswitching template int get_algorithm(const T *) { return -1; } } // This is the main divider class for use by the user (C++ API). // The divider itself is stored in the div variable who's // type is chosen by the dispatcher based on the template paramaters. template class divider { private: // Here's the actual divider typedef typename libdivide_internal::dispatcher::divider div_t; div_t div; // unswitch() friend declaration template friend divider unswitch(const divider & d); // Constructor used by the unswitch friend divider(const div_t& denom) : div(denom) { } public: // Ordinary constructor that takes the divisor as a parameter divider(T n) : div(n) { } // Default constructor. We leave this deliberately undefined so that // creating an array of divider and then initializing them // doesn't slow us down. divider() { } // Divides the parameter by the divisor, returning the quotient T perform_divide(T val) const { return div.perform_divide(val); } // Recovers the divisor that was used to initialize the divider T recover_divisor() const { return libdivide_internal::recover(&div.denom); } #if defined(LIBDIVIDE_USE_SSE2) // Treats the vector as either two or four packed values (depending on the // size), and divides each of them by the divisor, // returning the packed quotients. __m128i perform_divide_vector(__m128i val) const { return div.perform_divide_vector(val); } #endif // Returns the index of algorithm, for use in the unswitch function. Does // not apply to branchfree variant. // Returns the algorithm for unswitching. int get_algorithm() const { return libdivide_internal::get_algorithm(&div.denom); } bool operator==(const divider& him) const { return div.denom.magic == him.div.denom.magic && div.denom.more == him.div.denom.more; } bool operator!=(const divider& him) const { return !(*this == him); } }; #if __cplusplus >= 201103L || \ (defined(_MSC_VER) && _MSC_VER >= 1800) // libdivdie::branchfree_divider template using branchfree_divider = divider; #endif // Returns a divider specialized for the given algorithm template divider unswitch(const divider& d) { return divider(d.div.denom); } // Overload of the / operator for scalar division template int_type operator/(int_type numer, const divider& denom) { return denom.perform_divide(numer); } // Overload of the /= operator for scalar division template int_type operator/=(int_type& numer, const divider& denom) { numer = denom.perform_divide(numer); return numer; } #if defined(LIBDIVIDE_USE_SSE2) // Overload of the / operator for vector division template __m128i operator/(__m128i numer, const divider& denom) { return denom.perform_divide_vector(numer); } // Overload of the /= operator for vector division template __m128i operator/=(__m128i& numer, const divider& denom) { numer = denom.perform_divide_vector(numer); return numer; } #endif } // namespace libdivide } // anonymous namespace #endif // __cplusplus #endif // LIBDIVIDE_H muse-4.0.0/src/muse/liste/000077500000000000000000000000001404105311000153315ustar00rootroot00000000000000muse-4.0.0/src/muse/liste/CMakeLists.txt000066400000000000000000000035371404105311000201010ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( liste_mocs listedit.h ) ## ## List of source files to compile ## file (GLOB liste_source_files listedit.cpp ) ## ## Define target ## add_library ( liste ${MODULES_BUILD} ${liste_source_files} ${liste_mocs} ) add_dependencies(liste components) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${liste_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( liste PROPERTIES OUTPUT_NAME muse_liste ) ## ## Linkage ## target_link_libraries ( liste ${QT_LIBRARIES} widgets ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS liste DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/liste/listedit.cpp000066400000000000000000001022561404105311000176640ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: listedit.cpp,v 1.11.2.11 2009/05/24 21:43:44 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include "listedit.h" #include "mtscale.h" #include "globals.h" #include "helper.h" #include "icons.h" #include "editevent.h" #include "xml.h" #include "pitchedit.h" #include "song.h" #include "audio.h" #include "shortcuts.h" #include "midi.h" #include "event.h" #include "midiport.h" #include "midictrl.h" #include "minstrument.h" #include "app.h" #include "gconfig.h" #include "undo.h" namespace MusEGui { //--------------------------------------------------------- // EventListItem //--------------------------------------------------------- class EventListItem : public QTreeWidgetItem { public: MusECore::Event event; MusECore::MidiPart* part; EventListItem(QTreeWidget* parent, MusECore::Event ev, MusECore::MidiPart* p) : QTreeWidgetItem(parent) { event = ev; part = p; } virtual QString text(int col) const; virtual bool operator< ( const QTreeWidgetItem & other ) const { int col = other.treeWidget()->sortColumn(); EventListItem* eli = (EventListItem*) &other; switch(col) { case 0: return event.tick() < eli->event.tick(); break; case 1: return part->tick() + event.tick() < (eli->part->tick() + eli->event.tick()); break; case 2: return text(col).localeAwareCompare(other.text(col)) < 0; break; case 3: return part->track()->outChannel() < eli->part->track()->outChannel(); break; case 4: return event.dataA() < eli->event.dataA(); break; case 5: return event.dataB() < eli->event.dataB(); break; case 6: return event.dataC() < eli->event.dataC(); break; case 7: return event.lenTick() < eli->event.lenTick(); break; case 8: return text(col).localeAwareCompare(other.text(col)) < 0; break; default: break; } return 0; } }; /*--------------------------------------------------------- * midi_meta_name *---------------------------------------------------------*/ static QString midiMetaComment(const MusECore::Event& ev) { int meta = ev.dataA(); QString s = MusECore::midiMetaName(meta); switch (meta) { case 0: case 0x2f: case 0x51: case 0x54: case 0x58: case 0x59: case 0x74: case 0x7f: return s; case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: { s += QString(": "); const char* txt = (char*)(ev.data()); int len = ev.dataLen(); char buffer[len+1]; memcpy(buffer, txt, len); buffer[len] = 0; for (int i = 0; i < len; ++i) { if (buffer[i] == '\n' || buffer[i] == '\r') buffer[i] = ' '; } return s + QString(buffer); } case 0x20: case 0x21: default: { s += QString(": "); int i; int len = ev.lenTick(); int n = len > 10 ? 10 : len; for (i = 0; i < n; ++i) { if (i >= ev.dataLen()) break; s += QString(" 0x"); QString k; k.setNum(ev.data()[i] & 0xff, 16); s += k; } if (i == 10) s += QString("..."); return s; } } } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void ListEdit::closeEvent(QCloseEvent* e) { _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. // emit isDeleting(static_cast(this)); e->accept(); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void ListEdit::songChanged(MusECore::SongChangedStruct_t type) { if(_isDeleting) // Ignore while deleting to prevent crash. return; if (!type) return; if (type & (// SC_MIDI_TRACK_PROP FIXME Needed, but might make it slow! SC_PART_REMOVED | SC_PART_MODIFIED | SC_PART_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED | SC_SIG // Required so that bar/beat/tick of listed items are shown correctly. | SC_EVENT_INSERTED | SC_SELECTION)) { if (type & (SC_PART_REMOVED | SC_PART_INSERTED | SC_PART_MODIFIED | SC_SIG)) genPartlist(); // close window if editor has no parts anymore if (_pl->empty()) { parentWidget()->close(); return; } liste->setSortingEnabled(false); if (type == SC_SELECTION) { // BUGFIX: I found the keyboard modifier states affect how QTreeWidget::setCurrentItem() operates. // So for example (not) holding shift while lassoo-ing notes in piano roll affected // whether multiple items were selected in this event list editor! // Also blockSignals() definitely required - was messing up selections. p4.0.18 Tim. bool ci_done = false; liste->blockSignals(true); // Go backwards to avoid QTreeWidget::setCurrentItem() dependency on KB modifiers! for (int row = liste->topLevelItemCount() -1; row >= 0 ; --row) { QTreeWidgetItem* i = liste->topLevelItem(row); bool sel = ((EventListItem*)i)->event.selected(); if (i->isSelected() ^ sel) { // Do setCurrentItem() before setSelected(). if(sel && !ci_done) { liste->setCurrentItem(i); ci_done = true; } i->setSelected(sel); } } liste->blockSignals(false); } else { curPart = 0; curTrack = 0; liste->blockSignals(true); liste->clear(); for (MusECore::iPart p = _pl->begin(); p != _pl->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart*) (p->second); if (part->sn() == curPartId) curPart = part; for (MusECore::ciEvent i = part->events().begin(); i != part->events().end(); ++i) { EventListItem* item = new EventListItem(liste, i->second, part); for (int col = 0; col < liste->columnCount(); ++col) item->setText(col, item->text(col)); item->setSelected(i->second.selected()); if (item->event.tick() == (unsigned) selectedTick) { //prevent compiler warning: comparison of signed/unsigned) liste->setCurrentItem(item); item->setSelected(true); liste->scrollToItem(item, QAbstractItemView::EnsureVisible); } } } liste->blockSignals(false); } if(!curPart) { if(!_pl->empty()) { curPart = (MusECore::MidiPart*)(_pl->begin()->second); if(curPart) curTrack = curPart->track(); else curPart = 0; } } } liste->setSortingEnabled(true); } void ListEdit::genPartlist() { _pl->clear(); for (const auto& i : _pidSet) { MusECore::TrackList* tl = MusEGlobal::song->tracks(); for (const auto& it : *tl) { MusECore::PartList* pl = it->parts(); MusECore::iPart ip; for (ip = pl->begin(); ip != pl->end(); ++ip) { if (ip->second->sn() == i) { _pl->add(ip->second); break; } } if (ip != pl->end()) break; } } } //--------------------------------------------------------- // text //--------------------------------------------------------- QString EventListItem::text(int col) const { QString s; switch(col) { case 0: s.setNum(event.tick()); break; case 1: { int t = event.tick() + part->tick(); int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(t, &bar, &beat, &tick); s = QString("%1.%2.%3") .arg(bar + 1, 4, 10, QLatin1Char('0')) .arg(beat + 1, 2, 10, QLatin1Char('0')) .arg(tick, 3, 10, QLatin1Char('0')); } break; case 2: switch(event.type()) { case MusECore::Note: s = QString("Note"); break; case MusECore::Controller: { const char* cs; switch (MusECore::midiControllerType(event.dataA())) { case MusECore::MidiController::Controller7: cs = "Ctrl7"; break; case MusECore::MidiController::Controller14: cs = "Ctrl14"; break; case MusECore::MidiController::RPN: cs = "RPN"; break; case MusECore::MidiController::NRPN: cs = "NRPN"; break; case MusECore::MidiController::Pitch: cs = "Pitch"; break; case MusECore::MidiController::Program: cs = "Program"; break; case MusECore::MidiController::PolyAftertouch: cs = "PolyAftertouch"; break; case MusECore::MidiController::Aftertouch: cs = "Aftertouch"; break; case MusECore::MidiController::RPN14: cs = "RPN14"; break; case MusECore::MidiController::NRPN14: cs = "NRPN14"; break; default: cs = "Ctrl?"; break; } s = QString(cs); } break; case MusECore::Sysex: s = QString("SysEx"); break; case MusECore::Meta: s = QString("Meta"); break; case MusECore::Wave: break; default: printf("unknown event type %d\n", event.type()); } break; case 3: switch(event.type()) { case MusECore::Sysex: case MusECore::Meta: break; default: s.setNum(part->track()->outChannel() + 1); } break; case 4: if (event.isNote()) s = MusECore::pitch2string(event.dataA()); else if (event.type() == MusECore::Controller) s.setNum(event.dataA() & 0xffff); // mask off type bits else { switch(event.type()) { case MusECore::Sysex: case MusECore::Meta: break; default: s.setNum(event.dataA()); } } break; case 5: if(event.type() == MusECore::Controller && MusECore::midiControllerType(event.dataA()) == MusECore::MidiController::Program) { int val = event.dataB(); int hb = ((val >> 16) & 0xff) + 1; if (hb == 0x100) hb = 0; int lb = ((val >> 8) & 0xff) + 1; if (lb == 0x100) lb = 0; int pr = (val & 0xff) + 1; if (pr == 0x100) pr = 0; s = QString("%1-%2-%3").arg(hb).arg(lb).arg(pr); } else { switch(event.type()) { case MusECore::Sysex: case MusECore::Meta: break; default: s.setNum(event.dataB()); } } break; case 6: switch(event.type()) { case MusECore::Sysex: case MusECore::Meta: break; default: s.setNum(event.dataC()); } break; case 7: switch(event.type()) { case MusECore::Sysex: case MusECore::Meta: s.setNum(event.dataLen()); break; case MusECore::Controller: break; default: s.setNum(event.lenTick()); break; } break; case 8: switch(event.type()) { case MusECore::Controller: { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[part->track()->outPort()]; const int chan = part->track()->outChannel(); MusECore::MidiController* mc = mp->midiController(event.dataA(), chan); s = mc->name(); } break; case MusECore::Sysex: case MusECore::Meta: { if(event.type() == MusECore::Sysex) s = MusECore::nameSysex(event.dataLen(), event.data(), MusEGlobal::midiPorts[part->track()->outPort()].instrument()) + QString(" "); else if(event.type() == MusECore::Meta) s = midiMetaComment(event) + QString(" "); int i; for (i = 0; i < 10; ++i) { if (i >= event.dataLen()) break; s += QString(" 0x"); QString k; k.setNum(event.data()[i] & 0xff, 16); s += k; } if (i == 10) s += QString("..."); } break; default: break; } break; } return s; } //--------------------------------------------------------- // ListEdit //--------------------------------------------------------- ListEdit::ListEdit(MusECore::PartList* pl, QWidget* parent) : QWidget(parent) { setObjectName("ListEdit"); _isDeleting = false; _pl = pl; for (const auto& i : *_pl) _pidSet.insert(i.second->sn()); selectedTick=0; noteAction = new QAction(tr("Note")); sysexAction = new QAction(tr("SysEx")); ctrlAction = new QAction(tr("Ctrl")); metaAction = new QAction(tr("Meta")); //// insertNote = new QAction(*noteSVGIcon, tr("Insert Note"), insertItems); //// insertSysEx = new QAction(*sysexSVGIcon, tr("Insert SysEx"), insertItems); //// insertCtrl = new QAction(*ctrlSVGIcon, tr("Insert Ctrl"), insertItems); //// insertMeta = new QAction(*metaSVGIcon, tr("Insert Meta"), insertItems); addAction(noteAction); addAction(sysexAction); addAction(ctrlAction); addAction(metaAction); connect(noteAction, SIGNAL(triggered()), SLOT(editInsertNote())); connect(sysexAction, SIGNAL(triggered()), SLOT(editInsertSysEx())); connect(ctrlAction, SIGNAL(triggered()), SLOT(editInsertCtrl())); connect(metaAction, SIGNAL(triggered()), SLOT(editInsertMeta())); #if 0 // DELETETHIS or implement? QAction *cutAction = menuEdit->addAction(QIcon(*editcutIconSet), tr("Cut")); connect(cutAction, SIGNAL(triggered()), editSignalMapper, SLOT(map())); editSignalMapper->setMapping(cutAction, EList::CMD_CUT); cutAction->setShortcut(Qt::CTRL+Qt::Key_X); QAction *copyAction = menuEdit->addAction(QIcon(*editcopyIconSet), tr("Copy")); connect(copyAction, SIGNAL(triggered()), editSignalMapper, SLOT(map())); editSignalMapper->setMapping(cutAction, EList::CMD_COPY); copyAction->setShortcut(Qt::CTRL+Qt::Key_C); QAction *pasteAction = menuEdit->addAction(QIcon(*editpasteIconSet), tr("Paste")); connect(pasteAction, SIGNAL(triggered()), editSignalMapper, SLOT(map())); editSignalMapper->setMapping(cutAction, EList::CMD_PASTE); pasteAction->setShortcut(Qt::CTRL+Qt::Key_V); menuEdit->insertSeparator(); #endif incAction = new QAction(tr("Tick+")); incAction->setShortcut(Qt::Key_Plus); decAction = new QAction(tr("Tick-")); decAction->setShortcut(Qt::Key_Minus); deleteAction = new QAction(tr("Delete")); addAction(incAction); addAction(decAction); addAction(deleteAction); connect(deleteAction, &QAction::triggered, [this]() { cmd(CMD_DELETE); } ); connect(incAction, &QAction::triggered, [this]() { cmd(CMD_INC); } ); connect(decAction, &QAction::triggered, [this]() { cmd(CMD_DEC); } ); QToolBar* tb = new QToolBar(tr("Insert tools")); // insertTools->setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize)); tb->addAction(noteAction); tb->addAction(sysexAction); tb->addAction(ctrlAction); tb->addAction(metaAction); tb->addAction(incAction); tb->addAction(decAction); tb->addAction(deleteAction); noteAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); sysexAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); ctrlAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); metaAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); incAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); decAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); deleteAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); // //--------------------------------------------------- // liste //--------------------------------------------------- // liste = new QTreeWidget(this); QFontMetrics fm(liste->font()); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int n = fm.horizontalAdvance('9'); #else int n = fm.width('9'); #endif int b = 24; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int c = fm.horizontalAdvance(QString("Val B")); #else int c = fm.width(QString("Val B")); #endif int sortIndW = n * 3; liste->setAllColumnsShowFocus(true); liste->sortByColumn(0, Qt::AscendingOrder); liste->setSelectionMode(QAbstractItemView::ExtendedSelection); liste->setIndentation(2); QStringList columnnames; columnnames << tr("Tick") << tr("Bar") << tr("Type") << tr("Ch") << tr("Val A") << tr("Val B") << tr("Val C") << tr("Len") << tr("Comment"); liste->setHeaderLabels(columnnames); liste->setColumnWidth(0, n * 6 + b); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 liste->setColumnWidth(1, fm.horizontalAdvance(QString("9999.99.999")) + b); liste->setColumnWidth(2, fm.horizontalAdvance(QString("Program")) + b); #else liste->setColumnWidth(1, fm.width(QString("9999.99.999")) + b); liste->setColumnWidth(2, fm.width(QString("Program")) + b); #endif liste->setColumnWidth(3, n * 2 + b + sortIndW); liste->setColumnWidth(4, c + b + sortIndW); liste->setColumnWidth(5, c + b + sortIndW); liste->setColumnWidth(6, c + b + sortIndW); liste->setColumnWidth(7, n * 4 + b + sortIndW); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 liste->setColumnWidth(8, fm.horizontalAdvance(QString("MainVolume")) + 70); #else liste->setColumnWidth(8, fm.width(QString("MainVolume")) + 70); #endif connect(liste, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); connect(liste, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), SLOT(doubleClicked(QTreeWidgetItem*))); //--------------------------------------------------- // Rest //--------------------------------------------------- QGridLayout* mainGrid = new QGridLayout(this); mainGrid->setRowStretch(1, 100); mainGrid->setColumnStretch(0, 100); mainGrid->addWidget(tb, 0, 0); mainGrid->addWidget(liste, 1, 0, 2, 1); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); if(pl->empty()) { curPart = nullptr; curPartId = -1; } else { curPart = (MusECore::MidiPart*)pl->begin()->second; if(curPart) curPartId = curPart->sn(); else { curPart = nullptr; curPartId = -1; } } songChanged(-1); initShortcuts(); noteAction->setToolTip(tr("Insert note event") + " (" + noteAction->shortcut().toString() + ")"); sysexAction->setToolTip(tr("Insert system exclusive event") + " (" + sysexAction->shortcut().toString() + ")"); ctrlAction->setToolTip(tr("Insert controller event") + " (" + ctrlAction->shortcut().toString() + ")"); metaAction->setToolTip(tr("Insert meta event") + " (" + metaAction->shortcut().toString() + ")"); incAction->setToolTip(tr("Increase tick value") + " (" + incAction->shortcut().toString() + ")"); decAction->setToolTip(tr("Decrease tick value") + " (" + decAction->shortcut().toString() + ")"); deleteAction->setToolTip(tr("Delete event") + " (" + deleteAction->shortcut().toString() + ")"); qApp->installEventFilter(this); } //--------------------------------------------------------- // ~ListEdit //--------------------------------------------------------- ListEdit::~ListEdit() { } //--------------------------------------------------------- // editInsertNote //--------------------------------------------------------- void ListEdit::editInsertNote() { if(!curPart) return; MusECore::Event event = EditNoteDialog::getEvent(curPart->tick(), MusECore::Event(), this); if (!event.empty()) { //No events before beginning of part + take Part offset into consideration unsigned tick = event.tick(); if (tick < curPart->tick()) tick = 0; else tick-= curPart->tick(); event.setTick(tick); // Indicate do undo, and do not handle port controller values. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddEvent, event, curPart, false, false)); } } //--------------------------------------------------------- // editInsertSysEx //--------------------------------------------------------- void ListEdit::editInsertSysEx() { if(!curPart) return; MusECore::MidiInstrument* minstr = NULL; if(curPart->track()) minstr = MusEGlobal::midiPorts[curPart->track()->outPort()].instrument(); MusECore::Event event = EditSysexDialog::getEvent(curPart->tick(), MusECore::Event(), this, minstr); if (!event.empty()) { //No events before beginning of part + take Part offset into consideration unsigned tick = event.tick(); if (tick < curPart->tick()) tick = 0; else tick-= curPart->tick(); event.setTick(tick); // Indicate do undo, and do not handle port controller values. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddEvent, event, curPart, false, false)); } } //--------------------------------------------------------- // editInsertCtrl //--------------------------------------------------------- void ListEdit::editInsertCtrl() { if(!curPart) return; MusECore::Event event = EditCtrlDialog::getEvent(curPart->tick(), MusECore::Event(), curPart, this); if (!event.empty()) { //No events before beginning of part + take Part offset into consideration unsigned tick = event.tick(); if (tick < curPart->tick()) tick = 0; else tick-= curPart->tick(); event.setTick(tick); // REMOVE Tim. ctrl. Added. // It is FORBIDDEN to have multiple controller events at the same time with the same controller number. MusECore::ciEvent iev = curPart->events().findControllerAt(event); if(iev != curPart->events().end()) { // Indicate do undo, and do port controller values and clone parts. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, event, iev->second, curPart, true, true)); } else // Indicate do undo, and do port controller values and clone parts. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddEvent, event, curPart, true, true)); } } //--------------------------------------------------------- // editInsertMeta //--------------------------------------------------------- void ListEdit::editInsertMeta() { if(!curPart) return; MusECore::Event event = EditMetaDialog::getEvent(curPart->tick(), MusECore::Event(), this); if (!event.empty()) { //No events before beginning of part + take Part offset into consideration unsigned tick = event.tick(); if (tick < curPart->tick()) tick = 0; else tick-= curPart->tick(); event.setTick(tick); // Indicate do undo, and do not handle port controller values. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddEvent, event, curPart, false, false)); } } //--------------------------------------------------------- // editEvent //--------------------------------------------------------- void ListEdit::editEvent(MusECore::Event& event, MusECore::MidiPart* part) { int tick = event.tick() + part->tick(); MusECore::Event nevent; switch(event.type()) { case MusECore::Note: nevent = EditNoteDialog::getEvent(tick, event, this); break; case MusECore::Controller: nevent = EditCtrlDialog::getEvent(tick, event, part, this); break; case MusECore::Sysex: { MusECore::MidiInstrument* minstr = NULL; if(part->track()) minstr = MusEGlobal::midiPorts[part->track()->outPort()].instrument(); nevent = EditSysexDialog::getEvent(tick, event, this, minstr); } break; case MusECore::Meta: nevent = EditMetaDialog::getEvent(tick, event, this); break; default: return; } if (!nevent.empty()) { // TODO: check for event != nevent int tick = nevent.tick() - part->tick(); nevent.setTick(tick); if (tick < 0) printf("event not in part %d - %d - %d, not changed\n", part->tick(), nevent.tick(), part->tick() + part->lenTick()); else { if(event.type() == MusECore::Controller) // Indicate do undo, and do port controller values and clone parts. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, nevent, event, part, true, true)); else // Indicate do undo, and do not do port controller values and clone parts. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, nevent, event, part, false, false)); } } } //--------------------------------------------------------- // selectionChanged //--------------------------------------------------------- void ListEdit::selectionChanged() { bool update = false; EventListItem* eli; for (int row = 0; row < liste->topLevelItemCount(); ++row) { QTreeWidgetItem* i = liste->topLevelItem(row); eli = (EventListItem*)i; if (i->isSelected() ^ eli->event.selected()) { MusEGlobal::song->selectEvent(eli->event, eli->part, i->isSelected()); update = true; } } if (update) MusEGlobal::song->update(SC_SELECTION); } //--------------------------------------------------------- // doubleClicked //--------------------------------------------------------- void ListEdit::doubleClicked(QTreeWidgetItem* item) { EventListItem* ev = (EventListItem*) item; selectedTick = ev->event.tick(); editEvent(ev->event, ev->part); } //--------------------------------------------------------- // cmd //--------------------------------------------------------- void ListEdit::cmd(int cmd) { bool found = false; for (int row = 0; row < liste->topLevelItemCount(); ++row) { QTreeWidgetItem* i = liste->topLevelItem(row); EventListItem *item = (EventListItem *) i; if (i->isSelected() || item->event.selected()) { found = true; break; } } switch(cmd) { case CMD_DELETE: { if(!found) break; MusECore::Undo operations; EventListItem *deletedEvent=NULL; for (int row = 0; row < liste->topLevelItemCount(); ++row) { QTreeWidgetItem* i = liste->topLevelItem(row); EventListItem *item = (EventListItem *) i; if (i->isSelected() || item->event.selected()) { deletedEvent=item; // Port controller values and clone parts. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent,item->event, item->part, true, true)); } } unsigned int nextTick=0; // find biggest tick for (int row = 0; row < liste->topLevelItemCount(); ++row) { QTreeWidgetItem* i = liste->topLevelItem(row); EventListItem *item = (EventListItem *) i; if (item->event.tick() > nextTick && item != deletedEvent) nextTick=item->event.tick(); } // check if there's a tick that is "just" bigger than the deleted for (int row = 0; row < liste->topLevelItemCount(); ++row) { QTreeWidgetItem* i = liste->topLevelItem(row); EventListItem *item = (EventListItem *) i; if (item->event.tick() >= deletedEvent->event.tick() && item->event.tick() < nextTick && item != deletedEvent ) { nextTick=item->event.tick(); } } selectedTick=nextTick; MusEGlobal::song->applyOperationGroup(operations); } break; case CMD_INC: case CMD_DEC: { if(!found) break; MusECore::Undo operations; for (int row = 0; row < liste->topLevelItemCount(); ++row) { QTreeWidgetItem* i = liste->topLevelItem(row); EventListItem *item = (EventListItem *) i; if (i->isSelected() || item->event.selected()) { MusECore::Event new_event=item->event.clone(); new_event.setTick( new_event.tick() + ( cmd==CMD_INC?1:-1) ); operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent,new_event, item->event, item->part, false, false)); selectedTick=new_event.tick(); break; } } MusEGlobal::song->applyOperationGroup(operations); } } } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void ListEdit::configChanged() { initShortcuts(); } //--------------------------------------------------------- // initShortcuts //--------------------------------------------------------- void ListEdit::initShortcuts() { noteAction->setShortcut(shortcuts[SHRT_LE_INS_NOTES].key); sysexAction->setShortcut(shortcuts[SHRT_LE_INS_SYSEX].key); ctrlAction->setShortcut(shortcuts[SHRT_LE_INS_CTRL].key); metaAction->setShortcut(shortcuts[SHRT_LE_INS_META].key); deleteAction->setShortcut(shortcuts[SHRT_DELETE].key); } //--------------------------------------------------------- // focusCanvas //--------------------------------------------------------- void ListEdit::focusCanvas() { if(MusEGlobal::config.smartFocus) { liste->setFocus(); liste->activateWindow(); } } QSize ListEdit::minimumSizeHint() const { return QSize(380, 200); } bool ListEdit::eventFilter(QObject*, QEvent *e) { if (!liste->hasFocus()) return false; if (e->type() == QEvent::Shortcut) { QShortcutEvent* sev = static_cast(e); if (sev->isAmbiguous()) { for (const auto& action : actions()) { if (action->shortcut() == sev->key()) { action->trigger(); return true; } } } } return false; } } // namespace MusEGui muse-4.0.0/src/muse/liste/listedit.h000066400000000000000000000056031404105311000173270ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: listedit.h,v 1.3.2.3 2006/09/19 22:03:33 spamatica Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LIST_EDIT_H__ #define __LIST_EDIT_H__ #include "type_defs.h" #include "midieditor.h" #include "noteinfo.h" #include "cobject.h" class QAction; class QActionGroup; class QCloseEvent; class QKeyEvent; class QTreeWidget; class QTreeWidgetItem; namespace MusECore { class Event; class MidiPart; class MidiTrack; class PartList; class Xml; } namespace MusEGui { //--------------------------------------------------------- // ListEdit //--------------------------------------------------------- class ListEdit : public QWidget { Q_OBJECT QTreeWidget* liste; MusECore::MidiTrack* curTrack; MusECore::MidiPart* curPart; int selectedTick; int curPartId; bool _isDeleting; std::set _pidSet; MusECore::PartList* _pl; enum { CMD_DELETE, CMD_INC, CMD_DEC }; void closeEvent(QCloseEvent*) override; // virtual void keyPressEvent(QKeyEvent*); // virtual QSize sizeHint() const override; QSize minimumSizeHint() const override; bool eventFilter(QObject *, QEvent *event) override; void initShortcuts(); void genPartlist(); QAction *noteAction, *sysexAction, *ctrlAction, *metaAction; QAction *incAction, *decAction, *deleteAction; private slots: void editInsertNote(); void editInsertSysEx(); void editInsertCtrl(); void editInsertMeta(); void editEvent(MusECore::Event&, MusECore::MidiPart*); void selectionChanged(); void doubleClicked(QTreeWidgetItem*); void cmd(int cmd); void configChanged(); public slots: void songChanged(MusECore::SongChangedStruct_t); void focusCanvas(); public: ListEdit(MusECore::PartList*, QWidget* parent = 0); ~ListEdit(); MusECore::PartList* parts() { return _pl; } const MusECore::PartList* parts() const { return _pl; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/lock_free_buffer.h000066400000000000000000000575131404105311000176570ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // lock_free_buffer.h // (C) Copyright 1999-2002 Werner Schweer (ws@seh.de) // (C) Copyright 2012, 2017 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LOCK_FREE_BUFFER_H__ #define __LOCK_FREE_BUFFER_H__ //#include #include namespace MusECore { //--------------------------------------------------------- // LockFreeBuffer //--------------------------------------------------------- template class LockFreeBuffer { int _capacity; int _id; // Optional ID value. T *_fifo; volatile int _size; int _wIndex; int _rIndex; int _sizeSnapshot; T _dummyRetValue; public: // Start simple with just 2, like a flipping buffer for example. LockFreeBuffer(int capacity = 2, int id = 0) : _capacity(capacity), _id(id) { _dummyRetValue = T(); _fifo = new T[_capacity]; clear(); } ~LockFreeBuffer() { if(_fifo) delete[] _fifo; } int id() const { return _id; } void setCapacity(int capacity = 2) { if(_fifo) delete _fifo; _fifo = 0; _capacity = capacity; _fifo = new T[_capacity]; } // This is only for the writer. // Returns true on fifo overflow bool put(const T& item) { if (_size < _capacity) { _fifo[_wIndex] = item; _wIndex = (_wIndex + 1) % _capacity; // q_atomic_increment(&_size); ++_size; return false; } return true; } // This is only for the reader. T get() { if(_size <= 0) return _dummyRetValue; T item(_fifo[_rIndex]); _rIndex = (_rIndex + 1) % _capacity; --_size; return item; } // This is only for the reader. const T& peek(int n = 0) { const int idx = (_rIndex + n) % _capacity; return _fifo[idx]; } // // This is only for the reader. // // A non-constant version of peek so that we can modify the items in-place. // T& peekNonConst(int n = 0) // { // const int idx = (_rIndex + n) % _capacity; // return _fifo[idx]; // } // This is only for the reader. // Returns true if error (nothing to remove). bool remove() { if(_size <= 0) return true; _rIndex = (_rIndex + 1) % _capacity; --_size; return false; } // This is only for the reader. // Returns the number of items in the buffer. // If NOT requesting the size snapshot, this conveniently stores a snapshot (cached) version // of the size for consistent behaviour later. If requesting the size snapshot, it does not // update the snapshot itself. int getSize(bool useSizeSnapshot/* = false*/) { const int sz = useSizeSnapshot ? _sizeSnapshot : _size; if(!useSizeSnapshot) _sizeSnapshot = sz; return sz; } // This is only for the reader. bool isEmpty(bool useSizeSnapshot/* = false*/) const { return useSizeSnapshot ? _sizeSnapshot == 0 : _size == 0; } // This is not thread safe, call it only when it is safe to do so. void clear() { _size = 0; _sizeSnapshot = 0; _wIndex = 0; _rIndex = 0; } // Clear the 'read' side of the ring buffer, which also clears the size. // NOTE: A corresponding clearWrite() is not provided because // it is dangerous to reset the size from the sender side - // the receiver might cache the size, briefly. The sender should // only grow the size while the receiver should only shrink it. void clearRead() { _size = 0; _sizeSnapshot = 0; _rIndex = _wIndex; } }; // // template // // class LockFreeMultiBuffer // // { // // int _listCapacity; // // LockFreeBuffer *_list; // // //volatile int _size; // // //int _wIndex; // // //int _rIndex; // // // // public: // // // Start simple with just 2, like a flipping buffer for example. // // LockFreeMultiBuffer(int listCapacity = 1) // // { // // _listCapacity = listCapacity; // // _list = new LockFreeBuffer[_listCapacity]; // // //clear(); // // } // // ~LockFreeMultiBuffer() // // { // // if(_list) // // delete[] _list; // // } // // // // void setListCapacity(int listCapacity = 1) // // { // // if(_list) // // delete _list; // // _list = 0; // // _listCapacity = listCapacity; // // _list = new LockFreeBuffer[_listCapacity]; // // } // // // // }; // template // class LockFreeMultiBuffer : public std::map*, std::less > // { // public: // typedef typename std::map*, std::less > vlist; // typedef typename vlist::iterator iLockFreeMultiBuffer; // typedef typename vlist::const_iterator ciLockFreeMultiBuffer; // // private: // // int _curId; // T _dummyRetValue; // // public: // //LockFreeMultiBuffer() : _curId(0) { } // LockFreeMultiBuffer() { _dummyRetValue = T(); } // ~LockFreeMultiBuffer() // { // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) // { // if(i->second) // delete i->second; // } // } // // // Returns new buffer or zero if duplicate id or other error. // // Start simple with just 2, like a flipping buffer for example. // LockFreeBuffer* createBuffer(int id, int capacity = 2) // { // LockFreeBuffer* buf = new LockFreeBuffer(capacity, id); // std::pair < iLockFreeMultiBuffer, bool > res = // vlist::insert(std::pair < const int, LockFreeBuffer* >(buf->id(), buf)); // // if(res.second) // // { // // const int c_id = _curId; // // ++_curId; // // return c_id; // // } // // return -1; // // if(res.second) // return buf; // // delete buf; // return 0; // } // // // Returns true on error. // bool deleteBuffer(int id) // { // //if(id < 0) // // return true; // iLockFreeMultiBuffer i = vlist::find(id); // if(i == vlist::end()) // return true; // if(i->second) // delete i->second; // vlist::erase(i); // return false; // } // // LockFreeBuffer* findBuffer(int id) // { // //if(id < 0) // // return 0; // iLockFreeMultiBuffer i = vlist::find(id); // if(i == vlist::end()) // return 0; // return i->second; // } // // // Returns true on invalid id. // bool setCapacity(int id, int capacity = 2) // { // //if(id < 0) // // return true; // iLockFreeMultiBuffer i = vlist::find(id); // if(i == vlist::end()) // return true; // i->second->setCapacity(capacity); // return false; // } // // // This is only for the writer. // // Returns true on invalid id, or on fifo overflow of that id's buffer. // bool put(int id, const T& item) // { // //if(id < 0) // // return true; // iLockFreeMultiBuffer i = vlist::find(id); // if(i == vlist::end()) // return true; // return i->second->put(item); // } // // // This is only for the reader. // T get(bool useSizeSnapshot/* = false*/) // { // iLockFreeMultiBuffer least_i = vlist::end(); // bool is_first = true; // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) // { // LockFreeBuffer* buf = i->second; // if(!buf || buf->isEmpty(useSizeSnapshot)) // continue; // const T& temp_val = buf->peek(); // if(is_first) // { // is_first = false; // least_i = i; // //least_t = temp_val; // continue; // } // else if(temp_val < least_i->second->peek()) // least_i = i; // } // // if(least_i != vlist::end()) // return least_i->second->get(); // // return _dummyRetValue; // } // // // This is only for the reader. // const T& peek(bool useSizeSnapshot/* = false*/, int n = 0) // const // { // iLockFreeMultiBuffer least_i = vlist::end(); // bool is_first = true; // int buf_sz; // for(int idx = 0; idx <= n; ++idx) // Yes, that's <= // { // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) // { // LockFreeBuffer* buf = i->second; // if(!buf) // continue; // buf_sz = buf->getSize(useSizeSnapshot); // if(buf_sz == 0 || n >= buf_sz) // continue; // const T& temp_val = buf->peek(); // if(is_first) // { // is_first = false; // least_i = i; // // //if(idx == n) // // break; // //++idx; // continue; // } // else if(temp_val < least_i->second->peek()) // { // least_i = i; // // //if(idx == n) // // break; // //++idx; // } // } // if(idx == n) // break; // ++idx; // } // // if(least_i != vlist::end()) // return least_i->second->peek(); // // return _dummyRetValue; // } // // // // This is only for the reader. // // // A non-constant version of peek so that we can modify the items in-place. // // T& peekNonConst(bool useSizeSnapshot/* = false*/, int n = 0) // const // // { // // iLockFreeMultiBuffer least_i = vlist::end(); // // bool is_first = true; // // int buf_sz; // // for(int idx = 0; idx <= n; ++idx) // Yes, that's <= // // { // // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) // // { // // LockFreeBuffer* buf = i->second; // // if(!buf) // // continue; // // buf_sz = buf->getSize(useSizeSnapshot); // // if(buf_sz == 0 || n >= buf_sz) // // continue; // // T& temp_val = buf->peekNonConst(); // // if(is_first) // // { // // is_first = false; // // least_i = i; // // // // //if(idx == n) // // // break; // // //++idx; // // continue; // // } // // else if(temp_val < least_i->second->peekNonConst()) // // { // // least_i = i; // // // // //if(idx == n) // // // break; // // //++idx; // // } // // } // // if(idx == n) // // break; // // ++idx; // // } // // // // if(least_i != vlist::end()) // // return least_i->second->peekNonConst(); // // // // return _dummyRetValue; // // } // // // This is only for the reader. // // Returns true if error (nothing to remove). // bool remove(bool useSizeSnapshot/* = false*/) // { // iLockFreeMultiBuffer least_i = vlist::end(); // bool is_first = true; // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) // { // LockFreeBuffer* buf = i->second; // if(!buf || buf->isEmpty(useSizeSnapshot)) // continue; // const T& temp_val = buf->peek(); // if(is_first) // { // is_first = false; // least_i = i; // continue; // } // else if(temp_val < least_i->second->peek()) // least_i = i; // } // // if(least_i != vlist::end()) // return least_i->second->remove(); // // return true; // } // // // This is only for the reader. // // Returns the total number of items in the buffers. // // Also conveniently stores a cached version of the size for consistent behaviour later. // int getSize(bool useSizeSnapshot/* = false*/) const // { // int sz = 0; // // Hm, maybe not so accurate, sizes may be susceptable to // // asynchronous change as we iterate here... // for(ciLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) // { // if(LockFreeBuffer* buf = i->second) // sz += buf->getSize(useSizeSnapshot); // } // return sz; // } // // // This is only for the reader. // bool isEmpty(bool useSizeSnapshot/* = false*/) const // { // // Hm, maybe not so accurate, sizes may be susceptable to // // asynchronous change as we iterate here... // for(ciLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) // { // if(const LockFreeBuffer* buf = i->second) // { // if(!buf->isEmpty(useSizeSnapshot)) // return false; // } // } // return true; // } // // // This is not thread safe, call it only when it is safe to do so. // void clear() // { // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) // { // if(LockFreeBuffer* buf = i->second) // buf->clear(); // } // } // // // Clear the 'read' side of the ring buffer, which also clears the size. // // NOTE: A corresponding clearWrite() is not provided because // // it is dangerous to reset the size from the sender side - // // the receiver might cache the size, briefly. The sender should // // only grow the size while the receiver should only shrink it. // void clearRead() // { // for(iLockFreeMultiBuffer i = vlist::begin(); i != vlist::end(); ++i) // { // if(LockFreeBuffer* buf = i->second) // buf->clearRead(); // } // } // }; //--------------------------------------------------------- // LockFreeMPSCBuffer // A lock-free Multi-Producer Single-Consumer buffer. // Similar to a FIFO or Ring Buffer, but uses a fixed number of 'bins'. // There are no position counters or modulo operations. // There is no size or peek method. // It is intended to be fully consumed at reading time. //--------------------------------------------------------- template class LockFreeMPSCBuffer { T _array[capacity]; std::atomic _inUse[capacity]; std::atomic _hasData[capacity]; public: LockFreeMPSCBuffer() { clear(); } // Returns the buffer capacity. unsigned int bufferCapacity() const { return capacity; } // This is only for the writer. // Returns true on success, false if buffer overflow. bool put(const T& item) { bool expected; for(unsigned int i = 0; i < capacity; ++i) { // Expecting a not-in-use bin. Must reset expected each time. expected = false; // Safely check and set the bin's inUse flag. if(_inUse[i].compare_exchange_strong(expected, true)) { // Bin was not in use, now safely marked as in use. Now set the item. _array[i] = item; // Safely set the hasData flag for the reader to examine. _hasData[i].store(true); // Success. return true; } } // Sorry, all bins were full. A buffer overflow condition. return false; } // This is only for the reader. // Returns true on success, false if there was no data // available at the bin index or other error. bool get(T& dst, unsigned int index) { if(index >= capacity) return false; // Expecting hasData true. bool expected = true; // Safely check if there is data in the bin, and reset the // bin's hasData and inUse flags. Clear the hasData flag first !!! if(_hasData[index].compare_exchange_strong(expected, false)) { // It is safe to store the value in the destination. dst = _array[index]; // Now clear the inUse flag !!! _inUse[index].store(false); // Success. return true; } // Sorry, there was no data available in that bin. return false; } // This is only for the reader. // Returns true on success. bool remove(unsigned int index) { if(index >= capacity) return false; // Expecting hasData true. bool expected = true; // Safely check and reset the bin's hasData and inUse flags. if(_hasData[index].compare_exchange_strong(expected, false)) { _inUse[index].store(false); // Success. return true; } // Sorry, there was no data available in that bin. return false; } // Not thread safe. Only call when safe to do so, // like constructor etc. void clear() { for(unsigned int i = 0; i < capacity; ++i) { // Clear the hasData flag first !!! _hasData[i].store(false); // Now clear the inUse flag !!! _inUse[i].store(false); } } // This is only for the reader. void clearRead() { bool expected; for(unsigned int i = 0; i < capacity; ++i) { // Expecting hasData true. Must reset expected each time. expected = true; // Safely check and reset the bin's hasData and inUse flags. if(_hasData[i].compare_exchange_strong(expected, false)) _inUse[i].store(false); } } }; //--------------------------------------------------------- // LockFreeMPSCRingBuffer //--------------------------------------------------------- template class LockFreeMPSCRingBuffer { unsigned int _capacity; T *_fifo; std::atomic _size; std::atomic _wIndex; std::atomic _rIndex; unsigned int _capacityMask; unsigned int _sizeSnapshot; // Rounds to the nearest or equal power of 2. // For 0, 1, and 2, always returns 2. unsigned int roundCapacity(unsigned int reqCap) const { unsigned int i; for(i = 1; (1U << i) < reqCap; i++); return 1U << i; } public: // Start simple with just 2, like a flipping buffer for example. LockFreeMPSCRingBuffer(unsigned int capacity = 2) { _capacity = roundCapacity(capacity); _capacityMask = _capacity - 1; _fifo = new T[_capacity]; clear(); } ~LockFreeMPSCRingBuffer() { if(_fifo) delete[] _fifo; } void setCapacity(unsigned int capacity = 2) { if(_fifo) delete[] _fifo; _fifo = 0; _capacity = roundCapacity(capacity); _capacityMask = _capacity - 1; _fifo = new T[_capacity]; } // This is only for the writer. // Returns true on success, false on fifo overflow or other error. bool put(const T& item) { // Buffer full? Overflow condition. if(_size.load() >= _capacity) return false; // Safely read, then increment, the current write position. //std::atomic pos = _wIndex++; unsigned int pos = _wIndex++; // Mask the position for a circular effect. pos &= _capacityMask; // Store the item in that position. _fifo[pos] = item; // Now safely increment the size. _size++; // Success. return true; } // This is only for the reader. // Returns true on success, false if nothing to read or other error. // NOTE: This is not multi-reader safe. Yet. bool get(T& dst) { // Nothing to read? if(_size.load() == 0) return false; // Safely read, then increment, the current read position. //std::atomic pos = _rIndex++; unsigned int pos = _rIndex++; // Mask the position for a circular effect. pos &= _capacityMask; // Store the item in that position into the destination. dst = _fifo[pos]; // Now safely decrement the size. _size--; // Success. return true; } // This is only for the reader. // NOTE: This is not multi-reader safe. Yet. const T& peek(unsigned int n = 0) { // Safely read the current read position. //std::atomic pos = _rIndex.load(); unsigned int pos = _rIndex.load(); // Add the desired position. pos += n; // Mask the position for a circular effect. pos &= _capacityMask; return _fifo[pos]; } // // This is only for the reader. // // A non-constant version of peek so that we can modify the items in-place. // T& peekNonConst(int n = 0) // { // const int idx = (_rIndex + n) % _capacity; // return _fifo[idx]; // } // This is only for the reader. // Returns true on success or false if nothing to remove or other error. bool remove() { // Nothing to read? if(_size.load() == 0) return false; // Safely increment the current read position. _rIndex++; // Now safely decrement the size. _size--; // Success. return true; } // This is only for the reader. // Returns the number of items in the buffer. // If NOT requesting the size snapshot, this conveniently stores a snapshot (cached) version // of the size for consistent behaviour later. If requesting the size snapshot, it does not // update the snapshot itself. unsigned int getSize(bool useSizeSnapshot/* = false*/) { const unsigned int sz = useSizeSnapshot ? _sizeSnapshot : _size.load(); if(!useSizeSnapshot) _sizeSnapshot = sz; return sz; } // This is only for the reader. bool isEmpty(bool useSizeSnapshot/* = false*/) const { return useSizeSnapshot ? _sizeSnapshot == 0 : _size.load() == 0; } // This is not thread safe, call it only when it is safe to do so. void clear() { _size.store(0); _sizeSnapshot = 0; _wIndex.store(0); _rIndex.store(0); } // This is only for the reader. // Clear the 'read' side of the ring buffer, which also clears the size. // NOTE: A corresponding clearWrite() is not provided because it is dangerous to reset // the size from the sender side - the receiver might cache the size, briefly. // The sender should only grow the size while the receiver should only shrink it. //void clearRead() { _size = 0; _sizeSnapshot = 0; _rIndex = _wIndex; } void clearRead() { _size.store(0); _sizeSnapshot = 0; _rIndex.store(_wIndex); } }; } // namespace MusECore #endif muse-4.0.0/src/muse/lock_free_data_buffer.h000066400000000000000000000424441404105311000206450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // lock_free_data_buffer.h // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LOCK_FREE_DATA_BUFFER_H__ #define __LOCK_FREE_DATA_BUFFER_H__ #include #include #include namespace MusECore { //--------------------------------------------------------- // LockFreeDataRingBuffer // Variable sized data items. // No 'split' buffer or extra copying required when reading, // read() and peek() return linear addresses. // 64KBytes maximum capacity. //--------------------------------------------------------- class LockFreeDataRingBuffer { uint16_t _capacity; uint8_t *_fifo; std::atomic _size; std::atomic _wPos; std::atomic _rPos; uint16_t _sizeSnapshot; public: LockFreeDataRingBuffer(uint16_t capacity) { _capacity = capacity; _fifo = new uint8_t[_capacity]; clear(); } ~LockFreeDataRingBuffer() { if(_fifo) delete[] _fifo; } void setCapacity(uint16_t capacity) { if(_fifo) delete _fifo; _fifo = 0; _capacity = capacity; _fifo = new uint8_t[_capacity]; } // This is only for the writer. // Returns true on success, false on fifo overflow or other error. bool put(const void* data, size_t data_size) { // Nothing to save? if(data_size == 0) return false; // The size of the data size value. const uint16_t count_sz = sizeof(uint16_t); // Data too big? if(data_size >= 65536 - count_sz) return false; // The total size required. const uint16_t total_sz = data_size + count_sz; // The current read position. // TODO: Hm, watch out for this. No choice but to read it here in put(). const uint16_t cur_rpos = _rPos.load(); // The current write position. uint16_t cur_wpos = _wPos.load(); // If the read pointer is after the write pointer, make sure // the write pointer doesn't get ahead of the read pointer. if(cur_rpos > cur_wpos) { if(cur_wpos + total_sz >= cur_rpos) return false; } // Is there space at the end of the buffer? else if(cur_wpos + total_sz >= _capacity) { // No space at end of buffer. Is there space at the start? if(cur_rpos < total_sz) return false; // There is space at the start of the buffer. // If there is room, store zero as the current data size value, // meaning 'end of buffer' reader should jump back to the start. if(_capacity - cur_wpos >= count_sz) *((uint16_t*)&_fifo[cur_wpos]) = 0; // Reset the write pointer. cur_wpos = 0; } // Store the data size value. *((uint16_t*)&_fifo[cur_wpos]) = data_size; // Increment to the start of the data position. cur_wpos += count_sz; // Store the data. ::memcpy(&_fifo[cur_wpos], data, data_size); // Increment to the start of the next item position. // Don't bother checking and resetting write pos if it passes capacity. // That needs to be done safely in the next call to put(). cur_wpos += data_size; // Now safely store the new write position. _wPos.store(cur_wpos); // Now safely increment the size. _size++; //fprintf(stderr, "put SIZE:%d\n", _size.load()); // Success. return true; } // This is only for the reader. // Returns true on success, false if nothing to read or other error. // NOTE: This is not multi-reader safe. Yet. bool get(void* data, size_t* data_size) { // Nothing to read? if(_size.load() == 0) return false; // The size of the data size value. const uint16_t count_sz = sizeof(uint16_t); // Safely read the current read position. uint16_t cur_rpos = _rPos.load(); // If there is no room for the data size value, we've reached the end of the buffer, // reader should jump back to the start. // Or if the data size value is zero meaning 'end of buffer', // reader should jump back to the start. if((_capacity - cur_rpos < count_sz) || (*((uint16_t*)&_fifo[cur_rpos]) == 0)) cur_rpos = 0; // Get the data size value. const uint16_t dat_sz = *((uint16_t*)&_fifo[cur_rpos]); // Increment to the start of the data position. cur_rpos += count_sz; // Return the data size. *data_size = dat_sz; // Is there data? Return it. if(dat_sz > 0) ::memcpy(data, &_fifo[cur_rpos], dat_sz); // Increment to the start of the next item position. cur_rpos += dat_sz; // Now safely store the new read position. _rPos.store(cur_rpos); // Now safely decrement the size. _size--; // Success. return true; } // This is only for the reader. // Returns true on success, false if nothing to read or other error. // NOTE: This is not multi-reader safe. Yet. bool peek(void** data, size_t* data_size) { // Nothing to read? if(_size.load() == 0) return false; // The size of the data size value. const uint16_t count_sz = sizeof(uint16_t); // Safely read the current read position. uint16_t cur_rpos = _rPos.load(); // If there is no room for the data size value, we've reached the end of the buffer, // reader should jump back to the start. // Or if the data size value is zero meaning 'end of buffer', // reader should jump back to the start. if((_capacity - cur_rpos < count_sz) || (*((uint16_t*)&_fifo[cur_rpos]) == 0)) cur_rpos = 0; // Get the data size value. const uint16_t dat_sz = *((uint16_t*)&_fifo[cur_rpos]); // Increment to the start of the data position. cur_rpos += count_sz; // Return the data size. *data_size = dat_sz; // Is there data? Return it. if(dat_sz > 0) *data = &_fifo[cur_rpos]; // Success. return true; } // This is only for the reader. // Returns true on success or false if nothing to remove or other error. bool remove() { // Nothing to read? if(_size.load() == 0) return false; // The size of the data size value. const uint16_t count_sz = sizeof(uint16_t); // Safely read the current read position. uint16_t cur_rpos = _rPos.load(); // If there is no room for the data size value, we've reached the end of the buffer, // reader should jump back to the start. // Or if the data size value is zero meaning 'end of buffer', // reader should jump back to the start. if((_capacity - cur_rpos < count_sz) || (*((uint16_t*)&_fifo[cur_rpos]) == 0)) cur_rpos = 0; // Get the data size value. const uint16_t dat_sz = *((uint16_t*)&_fifo[cur_rpos]); // Increment to the start of the next item position. cur_rpos += count_sz + dat_sz; // Now safely store the new read position. _rPos.store(cur_rpos); // Now safely decrement the size. _size--; //fprintf(stderr, "remove SIZE:%d\n", _size.load()); // Success. return true; } // This is only for the reader. // Returns the number of items in the buffer. // If NOT requesting the size snapshot, this conveniently stores a snapshot (cached) version // of the size for consistent behaviour later. If requesting the size snapshot, it does not // update the snapshot itself. unsigned int getSize(bool useSizeSnapshot/* = false*/) { const unsigned int sz = useSizeSnapshot ? _sizeSnapshot : _size.load(); if(!useSizeSnapshot) _sizeSnapshot = sz; return sz; } // This is only for the reader. bool isEmpty(bool useSizeSnapshot/* = false*/) const { return useSizeSnapshot ? _sizeSnapshot == 0 : _size.load() == 0; } // This is not thread safe, call it only when it is safe to do so. void clear() { _size.store(0); _sizeSnapshot = 0; _wPos.store(0); _rPos.store(0); } // This is only for the reader. // Clear the 'read' side of the ring buffer, which also clears the size. // NOTE: A corresponding clearWrite() is not provided because it is dangerous to reset // the size from the sender side - the receiver might cache the size, briefly. // The sender should only grow the size while the receiver should only shrink it. //void clearRead() { _size = 0; _sizeSnapshot = 0; _rIndex = _wIndex; } void clearRead() { _size.store(0); _sizeSnapshot = 0; _rPos.store(_wPos); } }; //--------------------------------------------------------- // LockFreeMPSCDataRingBuffer //--------------------------------------------------------- class LockFreeMPSCDataRingBuffer { unsigned int _capacity; size_t _itemSize; uint8_t* *_fifo; std::atomic _size; std::atomic _wIndex; std::atomic _rIndex; unsigned int _capacityMask; unsigned int _sizeSnapshot; // Rounds to the nearest or equal power of 2. // For 0, 1, and 2, always returns 2. unsigned int roundCapacity(unsigned int reqCap) const { unsigned int i; for(i = 1; (1U << i) < reqCap; i++); return 1U << i; } public: // Start simple with just 2, like a flipping buffer for example. LockFreeMPSCDataRingBuffer(size_t data_size, unsigned int capacity = 2) { // The size of the data size value. const int count_sz = sizeof(_itemSize); _itemSize = count_sz + data_size; _capacity = roundCapacity(capacity); _capacityMask = _capacity - 1; _fifo = new uint8_t*[_capacity]; for(unsigned int i = 0; i < _capacity; ++i) _fifo[i] = new uint8_t[_itemSize]; clear(); } ~LockFreeMPSCDataRingBuffer() { if(_fifo) { for(unsigned int i = 0; i < _capacity; ++i) delete[] _fifo[i]; delete[] _fifo; } } void setCapacity(size_t data_size, unsigned int capacity = 2) { if(_fifo) { for(unsigned int i = 0; i < _capacity; ++i) delete[] _fifo[i]; delete[] _fifo; } _fifo = 0; // The size of the data size value. const int count_sz = sizeof(_itemSize); _itemSize = count_sz + data_size; _capacity = roundCapacity(capacity); _capacityMask = _capacity - 1; _fifo = new uint8_t*[_capacity]; for(unsigned int i = 0; i < _capacity; ++i) _fifo[i] = new uint8_t[_itemSize]; } // This is only for the writer. // Returns true on success, false on fifo overflow or other error. bool put(const void* data, size_t data_size) { // Buffer full? Overflow condition. if(_size.load() >= _capacity) return false; // The size of the data size value. const int count_sz = sizeof(_itemSize); // Data too big? if(data_size >= _itemSize - count_sz) return false; // Safely read, then increment, the current write position. //std::atomic pos = _wIndex++; unsigned int pos = _wIndex++; // Mask the position for a circular effect. pos &= _capacityMask; // Pointer to the data item. uint8_t* p_data = _fifo[pos]; // Store the data size value. *((size_t*)p_data) = data_size; // Increment to the start of the data position. p_data += count_sz; // Store the data. std::memcpy(p_data, data, data_size); // Now safely increment the size. _size++; //fprintf(stderr, "put SIZE:%d\n", _size.load()); // Success. return true; } // This is only for the reader. // Returns true on success, false if nothing to read or other error. // NOTE: This is not multi-reader safe. Yet. bool get(void* data, size_t* data_size) { // Nothing to read? if(_size.load() == 0) return false; // The size of the data size value. const int count_sz = sizeof(_itemSize); // Safely read, then increment, the current read position. //std::atomic pos = _rIndex++; unsigned int pos = _rIndex++; // Mask the position for a circular effect. pos &= _capacityMask; // Pointer to the data item. uint8_t* p_data = _fifo[pos]; // Get the data size value. const size_t sz = *((size_t*)p_data); // Return the data size. *data_size = sz; if(sz > 0) { // Increment to the start of the data position. p_data += count_sz; // Return the data. std::memcpy(data, p_data, sz); } // Now safely decrement the size. _size--; // Success. return true; } // This is only for the reader. // Returns true on success, false if nothing to read or other error. // NOTE: This is not multi-reader safe. Yet. bool peek(void** data, size_t* data_size, unsigned int n = 0) { // The size of the data size value. const int count_sz = sizeof(_itemSize); // Safely read the current read position. //std::atomic pos = _rIndex.load(); unsigned int pos = _rIndex.load(); // Add the desired position. pos += n; // Mask the position for a circular effect. pos &= _capacityMask; // Pointer to the data item. uint8_t* p_data = _fifo[pos]; // Get the data size value. const size_t sz = *((size_t*)p_data); // Return the data size. *data_size = sz; // Increment to the start of the data position. p_data += count_sz; // Return the data pointer. *data = p_data; // Success. return true; } // // This is only for the reader. // // A non-constant version of peek so that we can modify the items in-place. // T& peekNonConst(int n = 0) // { // const int idx = (_rIndex + n) % _capacity; // return _fifo[idx]; // } // This is only for the reader. // Returns true on success or false if nothing to remove or other error. bool remove() { // Nothing to read? if(_size.load() == 0) return false; // Safely increment the current read position. _rIndex++; // Now safely decrement the size. _size--; //fprintf(stderr, "remove SIZE:%d\n", _size.load()); // Success. return true; } // This is only for the reader. // Returns the number of items in the buffer. // If NOT requesting the size snapshot, this conveniently stores a snapshot (cached) version // of the size for consistent behaviour later. If requesting the size snapshot, it does not // update the snapshot itself. unsigned int getSize(bool useSizeSnapshot/* = false*/) { const unsigned int sz = useSizeSnapshot ? _sizeSnapshot : _size.load(); if(!useSizeSnapshot) _sizeSnapshot = sz; return sz; } // This is only for the reader. bool isEmpty(bool useSizeSnapshot/* = false*/) const { return useSizeSnapshot ? _sizeSnapshot == 0 : _size.load() == 0; } // This is not thread safe, call it only when it is safe to do so. void clear() { _size.store(0); _sizeSnapshot = 0; _wIndex.store(0); _rIndex.store(0); } // This is only for the reader. // Clear the 'read' side of the ring buffer, which also clears the size. // NOTE: A corresponding clearWrite() is not provided because it is dangerous to reset // the size from the sender side - the receiver might cache the size, briefly. // The sender should only grow the size while the receiver should only shrink it. //void clearRead() { _size = 0; _sizeSnapshot = 0; _rIndex = _wIndex; } void clearRead() { _size.store(0); _sizeSnapshot = 0; _rIndex.store(_wIndex); } }; } // namespace MusECore #endif muse-4.0.0/src/muse/lv2Gtk2Support/000077500000000000000000000000001404105311000170415ustar00rootroot00000000000000muse-4.0.0/src/muse/lv2Gtk2Support/CMakeLists.txt000066400000000000000000000033651404105311000216100ustar00rootroot00000000000000 include_directories(${GTK2_INCLUDE_DIRS}) ## ## List of source files to compile ## file (GLOB lv2_gtk2_support_source_files lv2Gtk2Support.cpp ) ## ## Define target ## add_library ( lv2_gtk2_support ${MODULES_BUILD} ${lv2_gtk2_support_source_files} ) ## ## Append to the list of translations ## # set (FILES_TO_TRANSLATE # ${FILES_TO_TRANSLATE} # ${lv2_gtk2_support_source_files} # CACHE INTERNAL "" # ) # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -shared -pedantic -Wall -Wextra -Werror -std=c++11") # set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g -D_DEBUG") # set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Os -g0 -DNDEBUG") # # set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -Wall -Wextra -fPIC ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" CACHE STRING "Release w/deb info CXX flags" FORCE) # # set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-Os -g0 -D_DEBUG ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}" CACHE STRING "Release w/deb info CXX flags" FORCE) # set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -Os -g0 -D_DEBUG") ## ## Compilation flags and target name ## set_target_properties( lv2_gtk2_support # Avoid treating warnings as errors here, unlike the rest of the app. PROPERTIES COMPILE_FLAGS "-std=c++11 -Wno-error" # COMPILE_FLAGS "-std=c++11" OUTPUT_NAME muse_lv2_gtk2_support ) ## ## Linkage ## target_link_libraries ( lv2_gtk2_support ${GTK2_LIBRARIES} ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS lv2_gtk2_support DESTINATION ${MusE_MODULES_DIR} ) else ( ${MODULES_BUILD} STREQUAL SHARED ) target_link_libraries (lv2_gtk2_support core) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/lv2Gtk2Support/lv2Gtk2Support.cpp000066400000000000000000000061451404105311000224030ustar00rootroot00000000000000 #include "lv2Gtk2Support.h" #include #include #include #include namespace MusEGui { Gtk::Main *gtkmm2Main = NULL; typedef void(*sz_cb_fn)(int, int, void *); static void plug_on_size_request(GtkWidget* widget, GtkRequisition* requisition, gpointer user_data) { sz_cb_fn fn = reinterpret_cast(user_data); int width = requisition->width; int height = requisition->height; void *arg = static_cast(g_object_get_data(G_OBJECT(widget), "lv2Gtk2Helper_arg")); fn(width, height, arg); } static void plug_on_size_allocate(GtkWidget* widget, GdkRectangle* allocation, gpointer user_data) { sz_cb_fn fn = reinterpret_cast(user_data); int width = allocation->width; int height = allocation->height; void *arg = static_cast(g_object_get_data(G_OBJECT(widget), "lv2Gtk2Helper_arg")); gtk_widget_set_size_request( widget, width, height ); fn(width, height, arg); } bool lv2Gtk2Helper_init() { gtk_init(NULL, NULL); //create gtkmm2 main class // Not required? gtkmm2Main = new Gtk::Main(NULL, NULL); return true; } void *lv2Gtk2Helper_gtk_plug_new(unsigned long winId, void *arg) { GtkWidget *gtkPlug = gtk_plug_new(winId); g_object_set_data(G_OBJECT(gtkPlug), "lv2Gtk2Helper_arg", arg); return static_cast(gtkPlug); } void *lv2Gtk2Helper_gtk_window_new(void *arg) { GtkWidget *gtkWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_object_set_data(G_OBJECT(gtkWindow), "lv2Gtk2Helper_arg", arg); return static_cast(gtkWindow); } void lv2Gtk2Helper_gtk_widget_destroy(void *plug) { gtk_widget_destroy(static_cast(plug)); } void lv2Gtk2Helper_gtk_container_add(void *plug, void *w) { gtk_container_add(GTK_CONTAINER(plug), static_cast(w)); } void lv2Gtk2Helper_gtk_widget_show_all(void *plug) { //gtk_widget_realize(static_cast(plug)); gtk_widget_show_all(static_cast(plug)); } void lv2Gtk2Helper_gtk_widget_get_allocation(void *plug, int *width, int *height) { GtkAllocation allocSize; gtk_widget_get_allocation(static_cast(plug), &allocSize); *width = allocSize.width; *height = allocSize.height; } void lv2Gtk2Helper_register_allocate_cb(void *plug, sz_cb_fn fn) { g_signal_connect(G_OBJECT(plug), "size-allocate", G_CALLBACK(plug_on_size_allocate), reinterpret_cast(fn)); } void lv2Gtk2Helper_register_resize_cb(void *plug, sz_cb_fn fn) { g_signal_connect(G_OBJECT(plug), "size-request", G_CALLBACK(plug_on_size_request), reinterpret_cast(fn)); } unsigned long lv2Gtk2Helper_gdk_x11_drawable_get_xid(void *plug) { //GdkWindow *w =gtk_widget_get_window(static_cast(widget)); //return gdk_x11_drawable_get_xid(w); return gtk_plug_get_id(static_cast(plug)); } unsigned long lv2Gtk2Helper_gtk_window_get_xid(void *window) { return GDK_WINDOW_XID(gtk_widget_get_window(static_cast(window))); } void lv2Gtk2Helper_deinit() { if(gtkmm2Main != NULL) { delete gtkmm2Main; gtkmm2Main = NULL; } } } // namespace MusEGui muse-4.0.0/src/muse/lv2Gtk2Support/lv2Gtk2Support.h000066400000000000000000000015351404105311000220460ustar00rootroot00000000000000#ifndef __LV2_GTK2_SUPPORT_H__ #define __LV2_GTK2_SUPPORT_H__ namespace MusEGui { typedef void(*sz_cb_fn)(int, int, void *); bool lv2Gtk2Helper_init(); void *lv2Gtk2Helper_gtk_plug_new(unsigned long winId, void *arg); void *lv2Gtk2Helper_gtk_window_new(void *arg); void lv2Gtk2Helper_gtk_widget_destroy(void *plug); void lv2Gtk2Helper_gtk_container_add(void *plug, void *w); void lv2Gtk2Helper_gtk_widget_show_all(void *plug); void lv2Gtk2Helper_gtk_widget_get_allocation(void *plug, int *width, int *height); void lv2Gtk2Helper_register_allocate_cb(void *plug, sz_cb_fn fn); void lv2Gtk2Helper_register_resize_cb(void *plug, sz_cb_fn fn); unsigned long lv2Gtk2Helper_gdk_x11_drawable_get_xid(void *plug); unsigned long lv2Gtk2Helper_gtk_window_get_xid(void *window); void lv2Gtk2Helper_deinit(); } // namespace MusEGui #endif muse-4.0.0/src/muse/lv2Support/000077500000000000000000000000001404105311000163115ustar00rootroot00000000000000muse-4.0.0/src/muse/lv2Support/lv2extprg.h000066400000000000000000000155021404105311000204220ustar00rootroot00000000000000/* LV2 Programs Extension Copyright 2012 Filipe Coelho 2014 Deryabin Andrew Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file lv2extprg.h C header for the LV2 programs extension . */ #ifndef LV2_PROGRAMS_H #define LV2_PROGRAMS_H #define LV2_PROGRAMS_URI "http://kxstudio.sf.net/ns/lv2ext/programs" #define LV2_PROGRAMS_PREFIX LV2_PROGRAMS_URI "#" #define LV2_PROGRAMS__Host LV2_PROGRAMS_PREFIX "Host" #define LV2_PROGRAMS__Interface LV2_PROGRAMS_PREFIX "Interface" #define LV2_PROGRAMS__UIInterface LV2_PROGRAMS_PREFIX "UIInterface" #define LV2_PROGRAMSNEW_URI "http://yoshimi.sf.net/ns/lv2ext/programs" #define LV2_PROGRAMSNEW_PREFIX LV2_PROGRAMSNEW_URI "#" #define LV2_PROGRAMSNEW__Interface LV2_PROGRAMSNEW_PREFIX "Interface" #define LV2_PROGRAMSNEW__UIInterface LV2_PROGRAMSNEW_PREFIX "UIInterface" #ifdef __cplusplus extern "C" { #endif typedef void* LV2_Programs_Handle; typedef struct _LV2_Program_Descriptor { /** Bank number for this program. Note that this extension does not support MIDI-style separation of bank LSB and MSB values. There is no restriction on the set of available banks: the numbers do not need to be contiguous, there does not need to be a bank 0, etc. */ uint32_t bank; /** Program number (unique within its bank) for this program. There is no restriction on the set of available programs: the numbers do not need to be contiguous, there does not need to be a program 0, etc. */ uint32_t program; /** Name of the program. */ const char * name; } LV2_Program_Descriptor; /** Programs extension, plugin data. When the plugin's extension_data is called with argument LV2_PROGRAMS__Interface, the plugin MUST return an LV2_Programs_Instance structure, which remains valid for the lifetime of the plugin. */ typedef struct _LV2_Programs_Interface { /** * get_program() * * This member is a function pointer that provides a description * of a program (named preset sound) available on this plugin. * * The index argument is an index into the plugin's list of * programs, not a program number as represented by the Program * field of the LV2_Program_Descriptor. (This distinction is * needed to support plugins that use non-contiguous program or * bank numbers.) * * This function returns a LV2_Program_Descriptor pointer that is * guaranteed to be valid only until the next call to get_program * or deactivate, on the same plugin instance. This function must * return NULL if passed an index argument out of range, so that * the host can use it to query the number of programs as well as * their properties. */ const LV2_Program_Descriptor *(*get_program)(LV2_Handle handle, uint32_t index); /** * select_program() * * This member is a function pointer that selects a new program * for this plugin. The program change should take effect * immediately at the start of the next run() call. (This * means that a host providing the capability of changing programs * between any two notes on a track must vary the block size so as * to place the program change at the right place. A host that * wanted to avoid this would probably just instantiate a plugin * for each program.) * * Plugins should ignore a select_program() call with an invalid * bank or program. * * A plugin is not required to select any particular default * program on activate(): it's the host's duty to set a program * explicitly. * * A plugin is permitted to re-write the values of its input * control ports when select_program is called. The host should * re-read the input control port values and update its own * records appropriately. (This is the only circumstance in which * a LV2 plugin is allowed to modify its own control-input ports.) */ void (*select_program)(LV2_Handle handle, uint32_t bank, uint32_t program); void (*select_program_for_channel)(LV2_Handle handle, unsigned char channel, uint32_t bank, uint32_t program); } LV2_Programs_Interface; /** Programs extension, UI data. When the UI's extension_data is called with argument LV2_PROGRAMS__UIInterface, the UI MUST return an LV2_Programs_UI_Interface structure, which remains valid for the lifetime of the UI. */ typedef struct _LV2_Programs_UI_Interface { /** * select_program() * * This is exactly the same as select_program in LV2_Programs_Instance, * but this struct relates to the UI instead of the plugin. * * When called, UIs should update their state to match the selected program. */ void (*select_program)(LV2UI_Handle handle, uint32_t bank, uint32_t program); void (*select_program_for_channel)(LV2_Handle handle, unsigned char channel, uint32_t bank, uint32_t program); } LV2_Programs_UI_Interface; /** Feature data for LV2_PROGRAMS__Host. */ typedef struct _LV2_Programs_Host { /** * Opaque host data. */ LV2_Programs_Handle handle; /** * program_changed() * * Tell the host to reload a plugin's program. * Parameter handle MUST be the 'handle' member of this struct. * Parameter index is program index to change. * When index is -1, host should reload all the programs. * * The plugin MUST NEVER call this function on a RT context or during run(). * * NOTE: This call is to inform the host about a program's bank, program or name change. * It DOES NOT change the current selected program. */ void (*program_changed)(LV2_Programs_Handle handle, int32_t index); } LV2_Programs_Host; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LV2_PROGRAMS_H */ muse-4.0.0/src/muse/lv2Support/lv2extui.h000066400000000000000000000067201404105311000202510ustar00rootroot00000000000000/* LV2 External UI extension This work is in public domain. This file 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. If you have questions, contact Filipe Coelho (aka falkTX) or ask in #lad channel, FreeNode IRC network. */ /** @file lv2_external_ui.h C header for the LV2 External UI extension . */ #ifndef LV2_EXTERNAL_UI_H #define LV2_EXTERNAL_UI_H #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #define LV2_EXTERNAL_UI_URI "http://kxstudio.sf.net/ns/lv2ext/external-ui" #define LV2_EXTERNAL_UI_PREFIX LV2_EXTERNAL_UI_URI "#" #define LV2_EXTERNAL_UI__Host LV2_EXTERNAL_UI_PREFIX "Host" #define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget" /** This extension used to be defined by a lv2plug.in URI */ #define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external" #ifdef __cplusplus extern "C" { #endif /** * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget. * UI is created in invisible state. */ typedef struct _LV2_External_UI_Widget { /** * Host calls this function regulary. UI library implementing the * callback may do IPC or redraw the UI. * * @param _this_ the UI context */ void (*run)(struct _LV2_External_UI_Widget * _this_); /** * Host calls this function to make the plugin UI visible. * * @param _this_ the UI context */ void (*show)(struct _LV2_External_UI_Widget * _this_); /** * Host calls this function to make the plugin UI invisible again. * * @param _this_ the UI context */ void (*hide)(struct _LV2_External_UI_Widget * _this_); } LV2_External_UI_Widget; #define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr) #define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr) #define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr) /** * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature. * LV2_Feature::data must be pointer to LV2_External_UI_Host. */ typedef struct _LV2_External_UI_Host { /** * Callback that plugin UI will call when UI (GUI window) is closed by user. * This callback will be called during execution of LV2_External_UI_Widget::run() * (i.e. not from background thread). * * After this callback is called, UI is defunct. Host must call LV2UI_Descriptor::cleanup(). * If host wants to make the UI visible again, the UI must be reinstantiated. * * @note When using the depreated URI LV2_EXTERNAL_UI_DEPRECATED_URI, * some hosts will not call LV2UI_Descriptor::cleanup() as they should, * and may call show() again without re-initialization. * * @param controller Host context associated with plugin UI, as * supplied to LV2UI_Descriptor::instantiate(). */ void (*ui_closed)(LV2UI_Controller controller); /** * Optional (may be NULL) "user friendly" identifier which the UI * may display to allow a user to easily associate this particular * UI instance with the correct plugin instance as it is represented * by the host (e.g. "track 1" or "channel 4"). * * If supplied by host, the string will be referenced only during * LV2UI_Descriptor::instantiate() */ const char * plugin_human_id; } LV2_External_UI_Host; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LV2_EXTERNAL_UI_H */ muse-4.0.0/src/muse/lv2Support/midnam_lv2.h000066400000000000000000000020721404105311000205130ustar00rootroot00000000000000#define LV2_MIDNAM_URI "http://ardour.org/lv2/midnam" #define LV2_MIDNAM_PREFIX LV2_MIDNAM_URI "#" #define LV2_MIDNAM__interface LV2_MIDNAM_PREFIX "interface" #define LV2_MIDNAM__update LV2_MIDNAM_PREFIX "update" typedef void* LV2_Midnam_Handle; /** a LV2 Feature provided by the Host to the plugin */ typedef struct { /** Opaque host data */ LV2_Midnam_Handle handle; /** Request from run() that the host should re-read the midnam */ void (*update)(LV2_Midnam_Handle handle); } LV2_Midnam; typedef struct { /** Query midnam document. The plugin * is expected to return a null-terminated XML * text which is a valid midnam desciption * (or NULL in case of error). * * The midnam must be unique and * specific for the given plugin-instance. */ char* (*midnam)(LV2_Handle instance); /** The unique model id used ith the midnam, * (or NULL). */ char* (*model)(LV2_Handle instance); /** free allocated strings. The host * calls this for every value returned by * \ref midnam and \ref model. */ void (*free)(char*); } LV2_Midnam_Interface; muse-4.0.0/src/muse/lv2host.cpp000066400000000000000000006563001404105311000163300ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // // lv2host.cpp // Copyright (C) 2014 by Deryabin Andrew // 2017 - Implement LV2_STATE__StateChanged #565 (danvd) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "config.h" #ifdef LV2_SUPPORT #define LV2_HOST_CPP #include #include //#include #include #include #include //#include #include //#include //#include #include #include #include #include #include #include //#include #include //#include #include #include #include "pluglist.h" #include "lv2host.h" #include "synth.h" #include "audio.h" #include "jackaudio.h" #include "midi_consts.h" #include "midiport.h" #include "stringparam.h" #include "plugin.h" #include "controlfifo.h" #include "xml.h" #include "song.h" #include "ctrl.h" #include "minstrument.h" #include "operations.h" #include "app.h" #include "globals.h" #include "gconfig.h" #include "components/popupmenu.h" #include "widgets/menutitleitem.h" #include "icons.h" #include #include "muse_math.h" #include #include #include #include #include #include // Uncomment to print audio process info. //#define LV2_DEBUG_PROCESS // Uncomment to print general info. // (There is also the CMake option LV2_DEBUG for more output.) //#define LV2_DEBUG // For process/DSP debugging output: Uncomment the fprintf section. #define DEBUG_LV2_PROCESS(dev, format, args...) // fprintf(dev, format, ##args); // For general debugging output: Uncomment the fprintf section. #define DEBUG_LV2_GENERAL(dev, format, args...) // fprintf(dev, format, ##args); #ifdef HAVE_GTK2 #include "lv2Gtk2Support/lv2Gtk2Support.h" #endif // Define to use GtkPlug instead of GtkWindow for a Gtk plugin gui container. // This works better than GtkWindow for some plugins. // For example with GtkWindow, AMSynth fails to embed into the container window // resulting in two separate windows. #define LV2_GUI_USE_GTKPLUG ; // This is the maximum supported worker message size. // Many plugins use a small worker message size, say 16. The messages simply point back to tasks // that the plugin has queued up to be worked upon. // Some other plugins directly send over 2000 bytes in one long message. // Some plugins send out lots of (small message) work requests in one run even before the worker can run them. // Using our custom non-splitting data FIFO, we might typically set a size higher than what a true 'wrap around' // data FIFO would use. But that's still far better than using a very wasteful fixed-size item based FIFO. // Actually, to accomodate say, the LSP 48 channel samplers, we MUST set this fairly high - even if we were to // use true 'wrap around' FIFOs because LSP plugins try to schedule MANY work requests in one run. // (That's up to 8 wave files per channel x 48 channels = 384 requests x 16 bytes per message = 6,144 bytes!). #define LV2_WRK_FIFO_SIZE 8192 namespace MusECore { #define NS_EXT "http://lv2plug.in/ns/ext/" #define NS_LV2CORE "http://lv2plug.in/ns/lv2core" #define LV2_INSTRUMENT_CLASS NS_LV2CORE "#InstrumentPlugin" #define LV2_F_BOUNDED_BLOCK_LENGTH LV2_BUF_SIZE__boundedBlockLength #define LV2_F_FIXED_BLOCK_LENGTH LV2_BUF_SIZE__fixedBlockLength #define LV2_F_POWER_OF_2_BLOCK_LENGTH LV2_BUF_SIZE__powerOf2BlockLength // BUG FIXME: 'coarseBlockLength' is NOT in the lv2 buf-size.h header file! // #define LV2_F_COARSE_BLOCK_LENGTH LV2_BUF_SIZE__coarseBlockLength #define LV2_F_COARSE_BLOCK_LENGTH LV2_BUF_SIZE_PREFIX "coarseBlockLength" #define LV2_P_SEQ_SIZE LV2_BUF_SIZE__sequenceSize #define LV2_P_MAX_BLKLEN LV2_BUF_SIZE__maxBlockLength #define LV2_P_MIN_BLKLEN LV2_BUF_SIZE__minBlockLength #define LV2_P_NOM_BLKLEN LV2_BUF_SIZE__nominalBlockLength #define LV2_P_SAMPLE_RATE LV2_PARAMETERS__sampleRate #define LV2_F_OPTIONS LV2_OPTIONS__options #define LV2_F_URID_MAP LV2_URID__map #define LV2_F_URID_UNMAP LV2_URID__unmap #ifdef LV2_URI_MAP_SUPPORT #define LV2_F_URI_MAP LV2_URI_MAP_URI #endif #define LV2_F_UI_PARENT LV2_UI__parent #define LV2_F_INSTANCE_ACCESS NS_EXT "instance-access" #define LV2_F_DATA_ACCESS LV2_DATA_ACCESS_URI #define LV2_F_UI_EXTERNAL_HOST LV2_EXTERNAL_UI__Host #define LV2_F_WORKER_SCHEDULE LV2_WORKER__schedule #define LV2_F_WORKER_INTERFACE LV2_WORKER__interface #define LV2_F_UI_IDLE LV2_UI__idleInterface #define LV2_F_UI_Qt5_UI LV2_UI_PREFIX "Qt5UI" #define LV2_UI_HOST_URI LV2_F_UI_Qt5_UI #define LV2_UI_EXTERNAL LV2_EXTERNAL_UI__Widget #define LV2_UI_EXTERNAL_DEPRECATED LV2_EXTERNAL_UI_DEPRECATED_URI #define LV2_F_DEFAULT_STATE LV2_STATE_PREFIX "loadDefaultState" #define LV2_F_STATE_CHANGED LV2_STATE_PREFIX "StateChanged" #define LV2_UI_SCALE_FACTOR LV2_UI_PREFIX "scaleFactor" #ifdef MIDNAM_SUPPORT #define LV2_F_MIDNAM_INTERFACE LV2_MIDNAM__interface #define LV2_F_MIDNAM_UPDATE LV2_MIDNAM__update #endif static LilvWorld *lilvWorld = nullptr; // LV2 does not use unique id numbers and frowns upon using anything but the uri. // static int uniqueID = 1; //uri cache structure. typedef struct { LilvNode *atom_AtomPort; #ifdef LV2_EVENT_BUFFER_SUPPORT LilvNode *ev_EventPort; #endif LilvNode *lv2_AudioPort; LilvNode *lv2_ControlPort; LilvNode *lv2_InputPort; LilvNode *lv2_OutputPort; LilvNode *lv2_connectionOptional; LilvNode *host_uiType; LilvNode *ext_uiType; LilvNode *ext_d_uiType; LilvNode *lv2_OptionalFeature; LilvNode *uiFixedSize; LilvNode *uiNoUserResize; LilvNode *lv2_portMinimumSize; LilvNode *lv2_portDiscrete; LilvNode *lv2_portContinuous; LilvNode *lv2_portLogarithmic; LilvNode *lv2_portInteger; LilvNode *lv2_portTrigger; LilvNode *lv2_portToggled; LilvNode *lv2_TimePosition; LilvNode *lv2_FreeWheelPort; LilvNode *lv2_SampleRate; LilvNode *lv2_CVPort; LilvNode *lv2_psetPreset; LilvNode *lv2_rdfsLabel; LilvNode *lv2_actionSavePreset; LilvNode *lv2_actionUpdatePresets; LilvNode *lv2_portEnumeration; LilvNode *lv2_rdfsRange; LilvNode *pg_group; LilvNode *lv2_name; LilvNode *lv2_minimum; LilvNode *lv2_maximum; LilvNode *lv2_default; LilvNode *pp_notOnGui; LilvNode *end; ///< nullptr terminator for easy freeing of entire structure } CacheNodes; LV2_URID Synth_Urid_Map(LV2_URID_Unmap_Handle _host_data, const char *uri) { LV2Synth *_synth = reinterpret_cast(_host_data); if(_synth == nullptr) //broken plugin { return 0; } return _synth->mapUrid(uri); } const char *Synth_Urid_Unmap(LV2_URID_Unmap_Handle _host_data, LV2_URID id) { LV2Synth *_synth = reinterpret_cast(_host_data); if(_synth == nullptr) //broken plugin { return nullptr; } return _synth->unmapUrid(id); } #ifdef LV2_URI_MAP_SUPPORT LV2_URID Synth_Uri_Map(LV2_URI_Map_Callback_Data _host_data, const char *, const char *uri) { LV2Synth *_synth = reinterpret_cast(_host_data); if(_synth == nullptr) //broken plugin { return 0; } return _synth->mapUrid(uri); } #endif static CacheNodes lv2CacheNodes; LV2_Feature lv2Features [] = { {LV2_F_URID_MAP, nullptr}, {LV2_F_URID_UNMAP, nullptr}, #ifdef LV2_URI_MAP_SUPPORT {LV2_F_URI_MAP, nullptr}, #endif {LV2_F_BOUNDED_BLOCK_LENGTH, nullptr}, {LV2_F_FIXED_BLOCK_LENGTH, nullptr}, {LV2_F_POWER_OF_2_BLOCK_LENGTH, nullptr}, {LV2_F_COARSE_BLOCK_LENGTH, nullptr}, {LV2_F_UI_PARENT, nullptr}, {LV2_F_INSTANCE_ACCESS, nullptr}, {LV2_F_UI_EXTERNAL_HOST, nullptr}, {LV2_UI_EXTERNAL_DEPRECATED, nullptr}, {LV2_F_WORKER_SCHEDULE, nullptr}, {LV2_F_UI_IDLE, nullptr}, {LV2_F_OPTIONS, nullptr}, {LV2_UI__resize, nullptr}, {LV2_PROGRAMS__Host, nullptr}, #ifdef MIDNAM_SUPPORT {LV2_MIDNAM__update, nullptr}, #endif {LV2_LOG__log, nullptr}, #ifdef LV2_MAKE_PATH_SUPPORT {LV2_STATE__makePath, nullptr}, #endif {LV2_STATE__mapPath, nullptr}, {LV2_F_STATE_CHANGED, nullptr}, {LV2_F_DATA_ACCESS, nullptr} //must be the last always! }; std::vector synthsToFree; #define SIZEOF_ARRAY(x) sizeof(x)/sizeof(x[0]) // static // Pointer to this required for LV2_P_MIN_BLKLEN option when // composing LV2_Options_Option array in LV2Synth::LV2Synth(). const unsigned LV2Synth::minBlockSize = 0U; void initLV2() { #ifdef HAVE_GTK2 //----------------- // Initialize Gtk //----------------- MusEGui::lv2Gtk2Helper_init(); #endif std::set supportedFeatures; uint32_t i = 0; if(MusEGlobal::debugMsg) std::cerr << "LV2: MusE supports these features:" << std::endl; for(i = 0; i < SIZEOF_ARRAY(lv2Features); i++) { supportedFeatures.insert(lv2Features [i].URI); if(MusEGlobal::debugMsg) std::cerr << "\t" << lv2Features [i].URI << std::endl; } lilvWorld = lilv_world_new(); lv2CacheNodes.atom_AtomPort = lilv_new_uri(lilvWorld, LV2_ATOM__AtomPort); #ifdef LV2_EVENT_BUFFER_SUPPORT lv2CacheNodes.ev_EventPort = lilv_new_uri(lilvWorld, LV2_EVENT__EventPort); #endif lv2CacheNodes.lv2_AudioPort = lilv_new_uri(lilvWorld, LV2_CORE__AudioPort); lv2CacheNodes.lv2_ControlPort = lilv_new_uri(lilvWorld, LV2_CORE__ControlPort); lv2CacheNodes.lv2_InputPort = lilv_new_uri(lilvWorld, LV2_CORE__InputPort); lv2CacheNodes.lv2_OutputPort = lilv_new_uri(lilvWorld, LV2_CORE__OutputPort); lv2CacheNodes.lv2_connectionOptional = lilv_new_uri(lilvWorld, LV2_CORE__connectionOptional); lv2CacheNodes.host_uiType = lilv_new_uri(lilvWorld, LV2_UI_HOST_URI); lv2CacheNodes.ext_uiType = lilv_new_uri(lilvWorld, LV2_UI_EXTERNAL); lv2CacheNodes.ext_d_uiType = lilv_new_uri(lilvWorld, LV2_UI_EXTERNAL_DEPRECATED); lv2CacheNodes.lv2_OptionalFeature = lilv_new_uri(lilvWorld, LV2_CORE__optionalFeature); lv2CacheNodes.uiFixedSize = lilv_new_uri(lilvWorld, LV2_UI__fixedSize); lv2CacheNodes.uiNoUserResize = lilv_new_uri(lilvWorld, LV2_UI__noUserResize); lv2CacheNodes.lv2_portMinimumSize = lilv_new_uri(lilvWorld, LV2_RESIZE_PORT__minimumSize); lv2CacheNodes.lv2_portContinuous = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__continuousCV); lv2CacheNodes.lv2_portDiscrete = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__discreteCV); lv2CacheNodes.lv2_portLogarithmic = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__logarithmic); lv2CacheNodes.lv2_portInteger = lilv_new_uri(lilvWorld, LV2_CORE__integer); lv2CacheNodes.lv2_portTrigger = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__trigger); lv2CacheNodes.lv2_portToggled = lilv_new_uri(lilvWorld, LV2_CORE__toggled); lv2CacheNodes.lv2_TimePosition = lilv_new_uri(lilvWorld, LV2_TIME__Position); lv2CacheNodes.lv2_FreeWheelPort = lilv_new_uri(lilvWorld, LV2_CORE__freeWheeling); lv2CacheNodes.lv2_SampleRate = lilv_new_uri(lilvWorld, LV2_CORE__sampleRate); lv2CacheNodes.lv2_CVPort = lilv_new_uri(lilvWorld, LV2_CORE__CVPort); lv2CacheNodes.lv2_psetPreset = lilv_new_uri(lilvWorld, LV2_PRESETS__Preset); lv2CacheNodes.lv2_rdfsLabel = lilv_new_uri(lilvWorld, LILV_NS_RDFS "label"); lv2CacheNodes.lv2_actionSavePreset = lilv_new_uri(lilvWorld, "http://www.muse-sequencer.org/lv2host#lv2_actionSavePreset"); lv2CacheNodes.lv2_actionUpdatePresets= lilv_new_uri(lilvWorld, "http://www.muse-sequencer.org/lv2host#lv2_actionUpdatePresets"); lv2CacheNodes.lv2_portEnumeration = lilv_new_uri(lilvWorld, LV2_CORE__enumeration); lv2CacheNodes.lv2_rdfsRange = lilv_new_uri(lilvWorld, LILV_NS_RDFS "range"); lv2CacheNodes.pg_group = lilv_new_uri(lilvWorld, LV2_PORT_GROUPS__group); lv2CacheNodes.lv2_name = lilv_new_uri(lilvWorld, LV2_CORE__name); lv2CacheNodes.lv2_minimum = lilv_new_uri(lilvWorld, LV2_CORE__minimum); lv2CacheNodes.lv2_maximum = lilv_new_uri(lilvWorld, LV2_CORE__maximum); lv2CacheNodes.lv2_default = lilv_new_uri(lilvWorld, LV2_CORE__default); lv2CacheNodes.pp_notOnGui = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__notOnGUI); lv2CacheNodes.end = nullptr; lilv_world_load_all(lilvWorld); // "Return a list of all found plugins. // The returned list contains just enough references to query // or instantiate plugins. The data for a particular plugin will not be // loaded into memory until a call to an lilv_plugin_* function results in // a query (at which time the data is cached with the LilvPlugin so future // queries are very fast)." const LilvPlugins *plugins = lilv_world_get_all_plugins(lilvWorld); const char* message = "initLV2: "; const MusEPlugin::PluginScanList& scan_list = MusEPlugin::pluginList; for(MusEPlugin::ciPluginScanList isl = scan_list.begin(); isl != scan_list.end(); ++isl) { const MusEPlugin::PluginScanInfoRef inforef = *isl; const MusEPlugin::PluginScanInfoStruct& info = inforef->info(); switch(info._type) { case MusEPlugin::PluginScanInfoStruct::PluginTypeLV2: { if(MusEGlobal::loadLV2) { const QString inf_cbname = PLUGIN_GET_QSTRING(info._completeBaseName); const QString inf_name = PLUGIN_GET_QSTRING(info._name); const QString inf_uri = PLUGIN_GET_QSTRING(info._uri); const Plugin* plug_found = MusEGlobal::plugins.find( inf_cbname, inf_uri, inf_name); const Synth* synth_found = MusEGlobal::synthis.find( inf_cbname, inf_uri, inf_name); if(plug_found) { fprintf(stderr, "Ignoring LV2 effect name:%s uri:%s path:%s duplicate of path:%s\n", PLUGIN_GET_CSTRING(info._name), PLUGIN_GET_CSTRING(info._uri), PLUGIN_GET_CSTRING(info.filePath()), plug_found->filePath().toLatin1().constData()); } if(synth_found) { fprintf(stderr, "Ignoring LV2 synth name:%s uri:%s path:%s duplicate of path:%s\n", PLUGIN_GET_CSTRING(info._name), PLUGIN_GET_CSTRING(info._uri), PLUGIN_GET_CSTRING(info.filePath()), synth_found->filePath().toLatin1().constData()); } const bool is_effect = info._class & MusEPlugin::PluginScanInfoStruct::PluginClassEffect; const bool is_synth = info._class & MusEPlugin::PluginScanInfoStruct::PluginClassInstrument; const bool add_plug = (is_effect || is_synth) && info._inports > 0 && info._outports > 0 && !plug_found; // For now we allow effects as a synth track. Until we allow programs (and midi) in the effect rack. const bool add_synth = (is_synth || is_effect) && !synth_found; if(add_plug || add_synth) { const LilvPlugin* plugin = nullptr; LilvNode* plugin_uri_node = lilv_new_uri(lilvWorld, PLUGIN_GET_CSTRING(info._uri)); if(plugin_uri_node) { plugin = lilv_plugins_get_by_uri(plugins, plugin_uri_node); lilv_node_free(plugin_uri_node); if(!plugin) { std::cerr << "LV2Synth: Plugin not found " << PLUGIN_GET_CSTRING(info._label) << "!" << std::endl; break; } } //QString pluginName = MusEPlugin::getQString(info._name); QString pluginName; LilvNode *nameNode = lilv_plugin_get_name(plugin); if(nameNode) { if(lilv_node_is_string(nameNode)) pluginName = QString(lilv_node_as_string(nameNode)); lilv_node_free(nameNode); } if(!pluginName.isEmpty()) { //QString pluginUri = MusEPlugin::getQString(info._uri); //QString pluginUri; //const LilvNode *uriNode = lilv_plugin_get_uri(plugin); //if(uriNode) //{ // if(lilv_node_is_string(uriNode)) // pluginUri = QString(lilv_node_as_string(uriNode)); //} //const QString name = MusEPlugin::getQString(info._name); const QString name = QString(pluginName) + QString(" LV2"); //QString label = QString(pluginUri) + QString("_LV2"); //const QString label = MusEPlugin::getQString(info._label); //const QString author = MusEPlugin::getQString(info._maker); QString author; LilvNode *nAuthor = lilv_plugin_get_author_name(plugin); if(nAuthor) { author = lilv_node_as_string(nAuthor); lilv_node_free(nAuthor); } LV2Synth *new_synth = new LV2Synth( PLUGIN_GET_QSTRING(info.filePath()), inf_uri, name, name, author, plugin, info._requiredFeatures); if(new_synth->isConstructed()) { if(add_synth) { MusEGlobal::synthis.push_back(new_synth); } else { synthsToFree.push_back(new_synth); } if(add_plug) { if(MusEGlobal::debugMsg) info.dump(message); MusEGlobal::plugins.push_back(new LV2PluginWrapper(new_synth, info._requiredFeatures)); } } else { delete new_synth; } } } } } break; case MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA: case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI: case MusEPlugin::PluginScanInfoStruct::PluginTypeVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeMESS: case MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown: case MusEPlugin::PluginScanInfoStruct::PluginTypeNone: case MusEPlugin::PluginScanInfoStruct::PluginTypeAll: break; } } } void deinitLV2() { for(size_t i = 0; i < synthsToFree.size(); i++) { delete synthsToFree [i]; } synthsToFree.clear(); for(LilvNode **n = (LilvNode **)&lv2CacheNodes; *n; ++n) { lilv_node_free(*n); } #ifdef HAVE_GTK2 MusEGui::lv2Gtk2Helper_deinit(); #endif lilv_world_free(lilvWorld); lilvWorld = nullptr; } void LV2Synth::lv2ui_ExtUi_Closed(LV2UI_Controller contr) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)contr; assert(state != nullptr); //this shouldn't happen assert(state->widget != nullptr); // this too assert(state->pluginWindow != nullptr); state->pluginWindow->setClosing(true); //state->uiTimer->stopNextTime(false); } void LV2Synth::lv2ui_SendChangedControls(LV2PluginWrapper_State *state) { if(state != nullptr && state->uiDesc != nullptr && state->uiDesc->port_event != nullptr && state->uiInst != nullptr) { size_t numControls = 0; MusECore::Port *controls = nullptr; size_t numControlsOut = 0; MusECore::Port *controlsOut = nullptr; LV2Synth *synth = state->synth; if(state->plugInst != nullptr) { numControls = state->plugInst->controlPorts; controls = state->plugInst->controls; numControlsOut = state->plugInst->controlOutPorts; controlsOut = state->plugInst->controlsOut; } else if(state->sif != nullptr) { numControls = state->sif->_inportsControl; controls = state->sif->_controls; numControlsOut = state->sif->_outportsControl; controlsOut = state->sif->_controlsOut; } if(numControls > 0) { assert(controls != nullptr); } if(numControlsOut > 0) { assert(controlsOut != nullptr); } for(uint32_t i = 0; i < numControls; ++i) { if(state->controlTimers [i] > 0) { --state->controlTimers [i]; continue; } if(state->controlsMask [i]) { state->controlsMask [i] = false; // Force send if re-opening. if(state->uiIsOpening || state->lastControls [i] != controls [i].val) { state->lastControls [i] = controls [i].val; state->uiDesc->port_event(state->uiInst, controls [i].idx, sizeof(float), 0, &controls [i].val); } } } for(uint32_t i = 0; i < numControlsOut; ++i) { // Force send if re-opening. if(state->uiIsOpening || state->lastControlsOut [i] != controlsOut [i].val) { state->lastControlsOut [i] = controlsOut [i].val; state->uiDesc->port_event(state->uiInst, controlsOut [i].idx, sizeof(float), 0, &controlsOut [i].val); } } //process gui atom events (control events are already set by getData or apply. size_t fifoItemSize = state->plugControlEvt.getItemSize(); size_t dataSize = 0; uint32_t port_index = 0; char evtBuffer [fifoItemSize]; while(state->plugControlEvt.get(&port_index, &dataSize, evtBuffer)) { state->uiDesc->port_event(state->uiInst, port_index, dataSize, synth->_uAtom_EventTransfer, evtBuffer); } } } void LV2Synth::lv2ui_PortWrite(LV2UI_Controller controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, void const *buffer) { LV2Synth::lv2state_PortWrite(controller, port_index, buffer_size, protocol, buffer, true); } void LV2Synth::lv2ui_Touch(LV2UI_Controller /*controller*/, uint32_t /*port_index*/, bool grabbed __attribute__ ((unused))) { #ifdef DEBUG_LV2 std::cerr << "LV2Synth::lv2ui_UiTouch: port: %u " << (grabbed ? "grabbed" : "released") << std::endl; #endif } void LV2Synth::lv2state_FillFeatures(LV2PluginWrapper_State *state) { uint32_t i; LV2Synth *synth = state->synth; LV2_Feature *_ifeatures = state->_ifeatures; LV2_Feature **_ppifeatures = state->_ppifeatures; //state->uiTimer = new LV2PluginWrapper_Timer(state); state->wrkSched.handle = (LV2_Worker_Schedule_Handle)state; state->wrkSched.schedule_work = LV2Synth::lv2wrk_scheduleWork; state->wrkIface = nullptr; state->wrkThread = new LV2PluginWrapper_Worker(state); state->extHost.plugin_human_id = state->human_id = nullptr; state->extHost.ui_closed = LV2Synth::lv2ui_ExtUi_Closed; state->extData.data_access = nullptr; for(i = 0; i < SIZEOF_ARRAY(lv2Features); i++) { _ifeatures [i] = synth->_features [i]; if(_ifeatures [i].URI == nullptr) { break; } if(i == synth->_fInstanceAccess) { _ifeatures [i].data = nullptr; } else if(i == synth->_fExtUiHost) { _ifeatures [i].data = &state->extHost; } else if(i == synth->_fExtUiHostD) { _ifeatures [i].data = &state->extHost; } else if(i == synth->_fDataAccess) { _ifeatures [i].data = &state->extData; } else if(i == synth->_fWrkSchedule) { _ifeatures [i].data = &state->wrkSched; } else if(i == synth->_fUiResize) { _ifeatures [i].data = &state->uiResize; } else if(i == synth->_fPrgHost) { _ifeatures [i].data = &state->prgHost; } #ifdef MIDNAM_SUPPORT else if(i == synth->_fMidNamUpdate) { _ifeatures [i].data = &state->midnamUpdate; } #endif #ifdef LV2_MAKE_PATH_SUPPORT else if(i == synth->_fMakePath) { _ifeatures [i].data = &state->makePath; } #endif else if(i == synth->_fMapPath) { _ifeatures [i].data = &state->mapPath; } _ppifeatures [i] = &_ifeatures [i]; } _ppifeatures [i] = nullptr; // REMOVE Tim. lv2. Removed. // No need to fill all these (there are several more now) as they start with zero // and the next time the send transport routine is called it will update all of them. // state->curBpm = 0.0;//double(60000000.0/MusEGlobal::tempomap.tempo(MusEGlobal::song->cpos())); // state->curIsPlaying = MusEGlobal::audio->isPlaying(); // state->curFrame = MusEGlobal::audio->pos().frame(); lv2_atom_forge_init(&state->atomForge, &synth->_lv2_urid_map); LV2Synth::lv2state_InitMidiPorts(state); } void LV2Synth::lv2state_PostInstantiate(LV2PluginWrapper_State *state) { LV2Synth *synth = state->synth; const LV2_Descriptor *descr = lilv_instance_get_descriptor(state->handle); state->_ifeatures [synth->_fInstanceAccess].data = lilv_instance_get_handle(state->handle); if(descr->extension_data != nullptr) { state->extData.data_access = descr->extension_data; } else { state->_ppifeatures [synth->_fDataAccess] = nullptr; } state->controlsNameMap.clear(); size_t nCpIn = synth->_controlInPorts.size(); size_t nCpOut = synth->_controlOutPorts.size(); if(nCpIn > 0) { state->lastControls = new float [nCpIn]; state->controlsMask = new bool [nCpIn]; state->controlTimers = new int [nCpIn]; for(uint32_t i = 0; i < nCpIn; i++) { state->lastControls [i] = synth->_pluginControlsDefault [synth->_controlInPorts [i].index]; state->controlsMask [i] = false; state->controlTimers [i] = 0; state->controlsNameMap.insert(std::pair(QString(synth->_controlInPorts [i].cName).toLower(), i)); state->controlsSymMap.insert(std::pair(QString(synth->_controlInPorts [i].cSym).toLower(), i)); } } if(nCpOut > 0) { state->lastControlsOut = new float [nCpOut]; for(uint32_t i = 0; i < nCpOut; i++) { state->lastControlsOut [i] = synth->_pluginControlsDefault [synth->_controlOutPorts [i].index]; } } //fill pointers for CV port types; uint32_t numAllPorts = lilv_plugin_get_num_ports(synth->_handle); state->pluginCVPorts = new float *[numAllPorts]; #ifdef _WIN32 state->pluginCVPorts = (float **) _aligned_malloc(16, sizeof(float *) * numAllPorts); if(state->pluginCVPorts == nullptr) { fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: _aligned_malloc returned error: nullptr. Aborting!\n"); abort(); } #else int rv = posix_memalign((void **)&state->pluginCVPorts, 16, sizeof(float *) * numAllPorts); if(rv != 0) { fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif memset(state->pluginCVPorts, 0, sizeof(float *) * numAllPorts); for(size_t i = 0; i < synth->_controlInPorts.size(); ++i) { if(synth->_controlInPorts [i].isCVPort) { size_t idx = synth->_controlInPorts [i].index; #ifdef _WIN32 state->pluginCVPorts [idx] = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(state->pluginCVPorts == nullptr) { fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: _aligned_malloc returned error: nullptr. Aborting!\n"); abort(); } #else rv = posix_memalign((void **)&state->pluginCVPorts [idx], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif for(size_t k = 0; k < MusEGlobal::segmentSize; ++k) { state->pluginCVPorts [idx] [k] = synth->_controlInPorts [i].defVal; } lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); } } for(size_t i = 0; i < synth->_controlOutPorts.size(); ++i) { if(synth->_controlOutPorts [i].isCVPort) { size_t idx = synth->_controlOutPorts [i].index; #ifdef _WIN32 state->pluginCVPorts [idx] = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(state->pluginCVPorts == 0) { fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: _aligned_malloc returned error: nullptr. Aborting!\n"); abort(); } #else rv = posix_memalign((void **)&state->pluginCVPorts [idx], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif for(size_t k = 0; k < MusEGlobal::segmentSize; ++k) { state->pluginCVPorts [idx] [k] = synth->_controlOutPorts [i].defVal; } lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); } } for(size_t i = 0; i < state->midiInPorts.size(); i++) { lilv_instance_connect_port(state->handle, state->midiInPorts [i].index, (void *)state->midiInPorts [i].buffer->getRawBuffer()); } for(size_t i = 0; i < state->midiOutPorts.size(); i++) { lilv_instance_connect_port(state->handle, state->midiOutPorts [i].index, (void *)state->midiOutPorts [i].buffer->getRawBuffer()); } //query for state interface state->iState = (LV2_State_Interface *)lilv_instance_get_extension_data(state->handle, LV2_STATE__interface); //query for LV2Worker interface state->wrkIface = (LV2_Worker_Interface *)lilv_instance_get_extension_data(state->handle, LV2_F_WORKER_INTERFACE); //query for programs interface state->prgIface = (LV2_Programs_Interface *)lilv_instance_get_extension_data(state->handle, LV2_PROGRAMSNEW__Interface); if(state->prgIface != nullptr) { state->newPrgIface = true; } else { state->newPrgIface = false; state->prgIface = (LV2_Programs_Interface *)lilv_instance_get_extension_data(state->handle, LV2_PROGRAMS__Interface); } //query for midnam interface state->midnamIface = (LV2_Midnam_Interface *)lilv_instance_get_extension_data(state->handle, LV2_F_MIDNAM_INTERFACE); // TODO: Which one do we honour if both progs and midnam exist? I suppose the midnam takes priority... #ifdef MIDNAM_SUPPORT LV2Synth::lv2midnam_updateMidnam(state); #endif LV2Synth::lv2prg_updatePrograms(state); state->wrkThread->start(QThread::LowPriority); } void LV2Synth::lv2ui_FreeDescriptors(LV2PluginWrapper_State *state) { if(state->uiDesc != nullptr && state->uiInst != nullptr) state->uiDesc->cleanup(state->uiInst); state->uiInst = *(void **)(&state->uiDesc) = nullptr; #ifdef HAVE_GTK2 if(state->gtk2Plug != nullptr) MusEGui::lv2Gtk2Helper_gtk_widget_destroy(state->gtk2Plug); #endif state->gtk2Plug = nullptr; if(state->uiDlHandle != nullptr) { dlclose(state->uiDlHandle); state->uiDlHandle = nullptr; } } void LV2Synth::lv2state_FreeState(LV2PluginWrapper_State *state) { assert(state != nullptr); state->wrkThread->setClosing(); state->wrkThread->wait(); delete state->wrkThread; if(state->human_id != nullptr) free(state->human_id); if(state->lastControls) { delete [] state->lastControls; state->lastControls = nullptr; } if(state->controlsMask) { delete [] state->controlsMask; state->controlsMask = nullptr; } if(state->controlTimers) { delete [] state->controlTimers; state->controlTimers = nullptr; } if(state->lastControlsOut) { delete [] state->lastControlsOut; state->lastControlsOut = nullptr; } LV2Synth::lv2ui_FreeDescriptors(state); if(state->handle != nullptr) { lilv_instance_free(state->handle); state->handle = nullptr; } if(state->wrkDataBuffer) delete state->wrkDataBuffer; if(state->wrkRespDataBuffer) delete state->wrkRespDataBuffer; delete state; } //------------------------------------------- // lv2audio_SendTransport // send transport events if any //------------------------------------------- void LV2Synth::lv2audio_SendTransport(LV2PluginWrapper_State *state, unsigned long sample, unsigned long /*nsamp*/, float latency_corr) { LV2Synth *synth = state->synth; if(state->inPortsMidi == 0 || !synth || !synth->usesTimePosition()) return; const float frames_per_second = MusEGlobal::sampleRate; const bool extsync = MusEGlobal::extSyncFlag; unsigned int cur_frame = MusEGlobal::audio->pos().frame(); unsigned int cur_tick = MusEGlobal::audio->tickPos(); //unsigned int next_tick = MusEGlobal::audio->nextTick(); // Speed: // "The rate of the progress of time as a fraction of normal speed. // For example, a rate of 0.0 is stopped, 1.0 is rolling at normal speed, // 0.5 is rolling at half speed, -1.0 is reverse, and so on." // // TESTED: Setting 'speed' always to 1.0 cures problems with some synths // like TAL NoiseMak3r. Its modulator gets stuck repeating the same small // segment in stop mode if speed is zero. But that breaks LV2 Example Metronome // which continues playing in stop mode. *Sigh*. What to do? User configurable: bool fixedSpeed = false; // Is this a rack plugin? if(state->plugInst != nullptr) fixedSpeed = state->plugInst->cquirks()._fixedSpeed; // Is this a synthesizer track? else if(state->sif != nullptr) fixedSpeed = state->sif->cquirks()._fixedSpeed; const bool curIsPlaying = fixedSpeed ? true : MusEGlobal::audio->isPlaying(); unsigned int lat_offset = 0; //-------------------------------------------------------------------- // Account for the latency correction and/or compensation. //-------------------------------------------------------------------- // TODO How to handle when external sync is on. For now, don't try to correct. if(MusEGlobal::config.enableLatencyCorrection && !extsync) { // This value is negative for correction. if((int)latency_corr < 0) { // Convert to a positive offset. const unsigned int l = (unsigned int)(-latency_corr); if(l > lat_offset) lat_offset = l; } if(lat_offset != 0) { // Be sure to correct both the frame and the tick. // Some plugins may use the frame, others the beat. cur_frame += lat_offset; Pos ppp(cur_frame, false); cur_tick = ppp.tick(); //ppp += nsamp; //next_tick = ppp.tick(); } } // Since floating point bpm depends on integer 'global tempo' and 'tempo', // let's avoid a float bpm comparison below, by using those integers instead. const int curGlobalTempo = MusEGlobal::tempomap.globalTempo(); const int curTempo = MusEGlobal::tempomap.tempo(cur_tick); const float curBpm = MusEGlobal::tempomap.bpm(cur_tick); int z, n; MusEGlobal::sigmap.timesig(cur_tick, z, n); // By Tim: Send only if something changed. // Some plugins may not like repeated same settings. #if 1 // Nothing changed? if(state->curFrame == cur_frame && state->curTick == cur_tick && state->curIsPlaying == curIsPlaying && state->curGlobalTempo == curGlobalTempo && state->curTempo == curTempo && state->curBeatsPerBar == z && state->curBeatUnit == n) return; state->curFrame = cur_frame; state->curTick = cur_tick; state->curIsPlaying = curIsPlaying; state->curGlobalTempo = curGlobalTempo; state->curTempo = curTempo; state->curBeatsPerBar = z; state->curBeatUnit = n; #else // By danvd: Send transport/tempo changes always // as some plugins revert to default settings when not playing #endif int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(cur_tick, &bar, &beat, &tick); //const int ticks_per_beat = 24; const int ticks_per_beat = MusEGlobal::config.division; const float bar_beat = float(beat) + float(tick) / float(ticks_per_beat); const double lin_beat = double(bar * z + beat) + double(tick) / double(ticks_per_beat); DEBUG_LV2_PROCESS(stderr, "curIsPlaying:%d cur_frame:%d cur_tick:%d bar:%d beat:%d tick:%d bar_beat:%f lin_beat:%f\n", curIsPlaying, cur_frame, cur_tick, bar, beat, tick, bar_beat, lin_beat); for(unsigned int i = 0; i < state->inPortsMidi; ++i) { if(!state->midiInPorts [i].supportsTimePos) continue; LV2EvBuf *buffer = state->midiInPorts [i].buffer; uint8_t pos_buf[1024]; memset(pos_buf, 0, sizeof(pos_buf)); LV2_Atom* lv2_pos = (LV2_Atom*)pos_buf; /* Build an LV2 position object to report change to plugin */ LV2_Atom_Forge* atomForge = &state->atomForge; lv2_atom_forge_set_buffer(atomForge, pos_buf, sizeof(pos_buf)); LV2_Atom_Forge_Frame frame; lv2_atom_forge_object(atomForge, &frame, 1, synth->_uTime_Position); lv2_atom_forge_key(atomForge, synth->_uTime_frame); lv2_atom_forge_long(atomForge, cur_frame); lv2_atom_forge_key(atomForge, synth->_uTime_framesPerSecond); lv2_atom_forge_float(atomForge, frames_per_second); lv2_atom_forge_key(atomForge, synth->_uTime_speed); lv2_atom_forge_float(atomForge, curIsPlaying ? 1.0f : 0.0f); lv2_atom_forge_key(atomForge, synth->_uTime_beatsPerMinute); lv2_atom_forge_float(atomForge, curBpm); lv2_atom_forge_key(atomForge, synth->_uTime_beatsPerBar); lv2_atom_forge_float(atomForge, z); lv2_atom_forge_key(atomForge, synth->_uTime_beat); lv2_atom_forge_double(atomForge, lin_beat); lv2_atom_forge_key(atomForge, synth->_uTime_bar); lv2_atom_forge_long(atomForge, bar); lv2_atom_forge_key(atomForge, synth->_uTime_barBeat); lv2_atom_forge_float(atomForge, bar_beat); lv2_atom_forge_key(atomForge, synth->_uTime_beatUnit); lv2_atom_forge_int(atomForge, n); // REMOVE Tim. lv2. Added. TESTING. This should be required. Seems OK so far. lv2_atom_forge_pop(atomForge, &frame); #ifdef LV2_EVENT_BUFFER_SUPPORT buffer->write(sample, 0, lv2_pos->type, lv2_pos->size, (const uint8_t *)LV2_ATOM_BODY(lv2_pos)); #else buffer->write(sample, lv2_pos->type, lv2_pos->size, (const uint8_t *)LV2_ATOM_BODY(lv2_pos)); #endif } } void LV2Synth::lv2state_InitMidiPorts(LV2PluginWrapper_State *state) { LV2Synth *synth = state->synth; state->midiInPorts = synth->_midiInPorts; state->midiOutPorts = synth->_midiOutPorts; state->inPortsMidi= state->midiInPorts.size(); state->outPortsMidi = state->midiOutPorts.size(); //connect midi and control ports for(size_t i = 0; i < state->midiInPorts.size(); i++) { LV2EvBuf *newEvBuffer = new LV2EvBuf( true, #ifdef LV2_EVENT_BUFFER_SUPPORT state->midiInPorts [i].old_api, #endif synth->_uAtom_Sequence, synth->_uAtom_Chunk, LV2_EVBUF_SIZE); if(!newEvBuffer) { abort(); } state->midiInPorts [i].buffer = newEvBuffer; state->idx2EvtPorts.insert(std::make_pair(state->midiInPorts [i].index, newEvBuffer)); } for(size_t i = 0; i < state->midiOutPorts.size(); i++) { LV2EvBuf *newEvBuffer = new LV2EvBuf( false, #ifdef LV2_EVENT_BUFFER_SUPPORT state->midiOutPorts [i].old_api, #endif synth->_uAtom_Sequence, synth->_uAtom_Chunk, LV2_EVBUF_SIZE); if(!newEvBuffer) { abort(); } state->midiOutPorts [i].buffer = newEvBuffer; state->idx2EvtPorts.insert(std::make_pair(state->midiOutPorts [i].index, newEvBuffer)); } } void LV2Synth::lv2audio_preProcessMidiPorts(LV2PluginWrapper_State *state, unsigned long sample, unsigned long /*nsamp*/) { for(size_t j = 0; j < state->inPortsMidi; j++) { state->midiInPorts [j].buffer->resetBuffer(); } for(size_t j = 0; j < state->outPortsMidi; j++) { state->midiOutPorts [j].buffer->resetBuffer(); } //process gui atom events (control events are already set by getData or apply call). size_t fifoItemSize = state->uiControlEvt.getItemSize(); size_t dataSize = 0; uint32_t port_index = 0; char evtBuffer [fifoItemSize]; while(state->uiControlEvt.get(&port_index, &dataSize, evtBuffer)) { std::map::iterator it = state->idx2EvtPorts.find(port_index); if(it != state->idx2EvtPorts.end()) { LV2EvBuf *buffer = it->second; const LV2_Atom* const atom = (const LV2_Atom*)evtBuffer; #ifdef LV2_EVENT_BUFFER_SUPPORT buffer->write(sample, 0, atom->type, atom->size, static_cast(LV2_ATOM_BODY_CONST(atom))); #else buffer->write(sample, atom->type, atom->size, static_cast(LV2_ATOM_BODY_CONST(atom))); #endif } } } void LV2Synth::lv2audio_postProcessMidiPorts(LV2PluginWrapper_State *state, unsigned long /* sample */, unsigned long /* nsamp */) { //send Atom events to gui. //Synchronize send rate with gui update rate size_t fifoItemSize = state->plugControlEvt.getItemSize(); size_t outp = state->midiOutPorts.size(); for(size_t j = 0; j < outp; j++) { #ifdef LV2_EVENT_BUFFER_SUPPORT if(!state->midiOutPorts [j].old_api) #endif { do { uint32_t frames, type, size; uint8_t *data = nullptr; #ifdef LV2_EVENT_BUFFER_SUPPORT uint32_t subframes; if(!state->midiOutPorts [j].buffer->read(&frames, &subframes, &type, &size, &data)) #else if(!state->midiOutPorts [j].buffer->read(&frames, &type, &size, &data)) #endif { break; } if(type == state->synth->_uAtom_Object) { const LV2_Atom_Object_Body *aObjBody = reinterpret_cast(data); if(aObjBody->otype == state->synth->_uAtom_StateChanged) { //Just make song status dirty (pending event) - something had changed in the plugin controls state->songDirtyPending = true; } } if(state->uiInst == nullptr) { continue; } unsigned char atom_data [fifoItemSize]; LV2_Atom *atom_evt = reinterpret_cast(atom_data); atom_evt->type = type; atom_evt->size = size; if(fifoItemSize - sizeof(LV2_Atom) < size) { #ifdef DEBUG_LV2 std::cerr << "LV2Synth::lv2audio_postProcessMidiPorts(): Plugin event data is bigger than rt fifo item size. Skipping." << std::endl; #endif continue; } unsigned char *evt = static_cast(LV2_ATOM_BODY(atom_evt)); memcpy(evt, data, size); state->plugControlEvt.put(state->midiOutPorts [j].index, sizeof(LV2_Atom) + size, atom_evt); } while(true); } } } void LV2Synth::lv2ui_PostShow(LV2PluginWrapper_State *state) { assert(state->pluginWindow != nullptr); assert(state->uiDesc != nullptr); assert(state->uiInst != nullptr); if(state->uiDesc->port_event != nullptr) { uint32_t numControls = 0; Port *controls = nullptr; if(state->plugInst != nullptr) { numControls = state->plugInst->controlPorts; controls = state->plugInst->controls; } else if(state->sif != nullptr) { numControls = state->sif->_inportsControl; controls = state->sif->_controls; } if(numControls > 0) { assert(controls != nullptr); } for(uint32_t i = 0; i < numControls; ++i) { state->uiDesc->port_event(state->uiInst, controls [i].idx, sizeof(float), 0, &controls [i].val); } } // Set the flag to tell the update timer to force sending all controls and program. state->uiIsOpening = true; state->pluginWindow->startNextTime(); } int LV2Synth::lv2ui_Resize(LV2UI_Feature_Handle handle, int width, int height) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; if (state->widget != nullptr && state->hasGui && !state->fixedSizeGui) { QWidget *mainWin = ((LV2PluginWrapper_Window *)state->widget); bool sFixScaling = false; if (state->plugInst != nullptr) sFixScaling = state->plugInst->cquirks().fixNativeUIScaling(); else if (state->sif != nullptr) sFixScaling = state->sif->cquirks().fixNativeUIScaling(); if (sFixScaling && mainWin->devicePixelRatio() >= 1.0) { int w = qRound((qreal)width / mainWin->devicePixelRatio()); int h = qRound((qreal)height / mainWin->devicePixelRatio()); if (state->noUserResizeGui) mainWin->setFixedSize(w,h); else mainWin->setMinimumSize(w,h); mainWin->resize(w, h); } else { if (state->noUserResizeGui) mainWin->setFixedSize(width, height); else mainWin->setMinimumSize(width, height); mainWin->resize(width, height); } // this shouldn't be required QWidget *ewWin = ((LV2PluginWrapper_Window *)state->widget)->findChild(); if(ewWin != nullptr) { ewWin->resize(width, height); } else { #ifdef LV2_GUI_USE_QWIDGET // TODO Check this, maybe wrong widget, maybe need the one contained by it? QWidget *ewCent= ((LV2PluginWrapper_Window *)state->widget); #else QWidget *ewCent= ((LV2PluginWrapper_Window *)state->widget)->centralWidget(); #endif if(ewCent != nullptr) { ewCent->resize(width, height); } } state->uiX11Size.setWidth(width); state->uiX11Size.setHeight(height); return 0; } return 1; } void LV2Synth::lv2ui_Gtk2AllocateCb(int width, int height, void *arg) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)arg; if(state == nullptr) return; if(!state->gtk2AllocateCompleted && state->widget != nullptr && state->hasGui && state->gtk2Plug != nullptr) { state->gtk2AllocateCompleted = true; ((LV2PluginWrapper_Window *)state->widget)->setMinimumSize(width, height); } } void LV2Synth::lv2ui_Gtk2ResizeCb(int width, int height, void *arg) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)arg; if(state == nullptr) return; if(!state->gtk2ResizeCompleted && state->widget != nullptr && state->hasGui && state->gtk2Plug != nullptr) { state->gtk2ResizeCompleted = true; ((LV2PluginWrapper_Window *)state->widget)->resize(width, height); } } void LV2Synth::lv2ui_ShowNativeGui(LV2PluginWrapper_State *state, bool bShow, bool fixScaling) { LV2Synth* synth = state->synth; LV2PluginWrapper_Window *win = nullptr; if(synth->_pluginUiTypes.size() == 0) return; //state->uiTimer->stopNextTime(); if(state->pluginWindow != nullptr) state->pluginWindow->stopNextTime(); if(!bShow) return; LV2_PLUGIN_UI_TYPES::iterator itUi; if((state->uiCurrent == nullptr) || MusEGlobal::config.lv2UiBehavior == MusEGlobal::CONF_LV2_UI_ASK_ALWAYS) { state->uiCurrent = nullptr; state->gtk2ResizeCompleted = false; state->gtk2AllocateCompleted = false; QAction *aUiTypeSelected = nullptr; if((synth->_pluginUiTypes.size() == 1) || MusEGlobal::config.lv2UiBehavior == MusEGlobal::CONF_LV2_UI_USE_FIRST) { state->uiCurrent = synth->_pluginUiTypes.begin()->first; } else { QMenu mGuisPopup; MusEGui::MenuTitleItem *aUiTypeHeader = new MusEGui::MenuTitleItem(QObject::tr("Select gui type"), nullptr); aUiTypeHeader->setEnabled(false); QFont fHeader; fHeader.setBold(true); fHeader.setUnderline(true); aUiTypeHeader->setFont(fHeader); mGuisPopup.addAction(aUiTypeHeader); for(itUi = synth->_pluginUiTypes.begin(); itUi != synth->_pluginUiTypes.end(); itUi++) { const LilvUI *selectedUi = itUi->first; const LilvNode *pluginUiType = itUi->second.second; QAction *aUiType = new QAction(QString(lilv_node_as_string(pluginUiType)), nullptr); aUiType->setData(QVariant(reinterpret_cast(selectedUi))); mGuisPopup.addAction(aUiType); } aUiTypeSelected = mGuisPopup.exec(QCursor::pos()); if(aUiTypeSelected == nullptr) { return; } state->uiCurrent = reinterpret_cast(aUiTypeSelected->data().toLongLong()); } } itUi = synth->_pluginUiTypes.find(state->uiCurrent); assert(itUi != synth->_pluginUiTypes.end()); const LilvUI *selectedUi = itUi->first; bool bExtUi = itUi->second.first; const LilvNode *pluginUiType = itUi->second.second; state->uiIdleIface = nullptr; if(bExtUi) { state->hasGui = false; state->hasExternalGui = true; } else { state->hasGui = true; state->hasExternalGui = false; } const LilvNode* nUI = lilv_ui_get_uri(selectedUi); state->fixedSizeGui = lilv_world_ask(lilvWorld, nUI, lv2CacheNodes.lv2_OptionalFeature, lv2CacheNodes.uiFixedSize); state->noUserResizeGui = lilv_world_ask(lilvWorld, nUI, lv2CacheNodes.lv2_OptionalFeature, lv2CacheNodes.uiNoUserResize); #ifdef LV2_GUI_USE_QWIDGET win = new LV2PluginWrapper_Window(state, Q_NULLPTR, Qt::Window); #else win = new LV2PluginWrapper_Window(state); #endif state->uiX11Size.setWidth(0); state->uiX11Size.setHeight(0); if(win != nullptr) { state->widget = win; state->pluginWindow = win; const char *cUiUri = lilv_node_as_uri(pluginUiType); const char *cUiTypeUri = lilv_node_as_uri(nUI); bool bEmbed = false; bool bGtk = false; QWidget *ewWin = nullptr; #ifdef HAVE_GTK2 QWindow *x11QtWindow = nullptr; #endif state->gtk2Plug = nullptr; state->_ifeatures [synth->_fUiParent].data = nullptr; if(strcmp(LV2_UI__X11UI, cUiUri) == 0) { bEmbed = true; // ewWin = new QWidget(); #ifdef LV2_GUI_USE_QWIDGET QVBoxLayout* layout = new QVBoxLayout(); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(ewWin); win->setLayout(layout); #else // win->setCentralWidget(ewWin); #endif state->_ifeatures [synth->_fUiParent].data = (void*)win->winId(); // state->_ifeatures [synth->_fUiParent].data = (void*)ewWin->winId(); } else if(strcmp(LV2_UI__GtkUI, cUiUri) == 0) { #ifndef HAVE_GTK2 win->stopNextTime(); return; #else bEmbed = true; bGtk = true; #ifdef LV2_GUI_USE_GTKPLUG state->gtk2Plug = MusEGui::lv2Gtk2Helper_gtk_plug_new(0, state); #else state->gtk2Plug = MusEGui::lv2Gtk2Helper_gtk_window_new(state); #endif MusEGui::lv2Gtk2Helper_register_allocate_cb(static_cast(state->gtk2Plug), lv2ui_Gtk2AllocateCb); MusEGui::lv2Gtk2Helper_register_resize_cb(static_cast(state->gtk2Plug), lv2ui_Gtk2ResizeCb); #endif } else if(strcmp(LV2_F_UI_Qt5_UI, cUiUri) == 0) //Qt5 uis are handled natively { state->_ifeatures [synth->_fUiParent].data = win; } else //external uis { state->_ifeatures [synth->_fUiParent].data = nullptr; } //now open ui library file // lilv_uri_to_path is deprecated. Use lilv_file_uri_parse instead. Return value must be freed with lilv_free. const char *uiPath = lilv_file_uri_parse(lilv_node_as_uri(lilv_ui_get_binary_uri(selectedUi)), nullptr); #ifdef _WIN32 state->uiDlHandle = dlopen(uiPath, RTLD_NOW | RTLD_DEFAULT); #else state->uiDlHandle = dlopen(uiPath, RTLD_NOW); #endif lilv_free((void*)uiPath); // Must free. if(state->uiDlHandle == nullptr) { win->stopNextTime(); return; } //find lv2 ui descriptor function and call it to get ui descriptor struct LV2UI_DescriptorFunction lv2fUiDesc; *(void **)(&lv2fUiDesc) = dlsym(state->uiDlHandle, "lv2ui_descriptor"); if(lv2fUiDesc == nullptr) { win->stopNextTime(); return; } state->uiDesc = nullptr; for(uint32_t i = 0; ; ++i) { state->uiDesc = lv2fUiDesc(i); if(state->uiDesc == nullptr) break; if(strcmp(state->uiDesc->URI, cUiTypeUri) == 0) //found selected ui break; } if(state->uiDesc == nullptr) { win->stopNextTime(); return; } void *uiW = nullptr; // lilv_uri_to_path is deprecated. Use lilv_file_uri_parse instead. Return value must be freed with lilv_free. const char* bundle_path = lilv_file_uri_parse(lilv_node_as_uri(lilv_ui_get_bundle_uri(selectedUi)), nullptr); state->uiInst = state->uiDesc->instantiate(state->uiDesc, lilv_node_as_uri(lilv_plugin_get_uri(synth->_handle)), bundle_path, LV2Synth::lv2ui_PortWrite, state, &uiW, state->_ppifeatures); lilv_free((void*)bundle_path); // Must free. if(state->uiInst != nullptr) { state->uiIdleIface = nullptr; state->uiPrgIface = nullptr; if(state->uiDesc->extension_data != nullptr) { state->uiIdleIface = (LV2UI_Idle_Interface *)state->uiDesc->extension_data(LV2_F_UI_IDLE); state->uiPrgIface = (LV2_Programs_UI_Interface *)state->uiDesc->extension_data(LV2_PROGRAMSNEW__UIInterface); if(state->uiPrgIface != nullptr) { state->newPrgIface = true; } else { state->newPrgIface = false; state->uiPrgIface = (LV2_Programs_UI_Interface *)state->uiDesc->extension_data(LV2_PROGRAMS__UIInterface); } } if(state->hasGui) { if(!bEmbed) { #ifdef LV2_GUI_USE_QWIDGET QVBoxLayout* layout = new QVBoxLayout(); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(static_cast(uiW)); win->setLayout(layout); #else win->setCentralWidget(static_cast(uiW)); #endif } else { if(bGtk) { #ifdef HAVE_GTK2 MusEGui::lv2Gtk2Helper_gtk_container_add(state->gtk2Plug, uiW); MusEGui::lv2Gtk2Helper_gtk_widget_show_all(state->gtk2Plug); #ifdef LV2_GUI_USE_GTKPLUG unsigned long plugX11Id = MusEGui::lv2Gtk2Helper_gdk_x11_drawable_get_xid(state->gtk2Plug); #else unsigned long plugX11Id = MusEGui::lv2Gtk2Helper_gtk_window_get_xid(state->gtk2Plug); #endif x11QtWindow = QWindow::fromWinId(plugX11Id); ewWin = QWidget::createWindowContainer(x11QtWindow, win); state->pluginQWindow = x11QtWindow; #ifdef LV2_GUI_USE_QWIDGET QVBoxLayout* layout = new QVBoxLayout(); layout->setMargin(0); layout->setSpacing(0); layout->addWidget(ewWin); win->setLayout(layout); #else win->setCentralWidget(ewWin); #endif if(state->uiX11Size.width() == 0 || state->uiX11Size.height() == 0) { int w = 0; int h = 0; MusEGui::lv2Gtk2Helper_gtk_widget_get_allocation(uiW, &w, &h); if (fixScaling && win->devicePixelRatio() >= 1.0) { w = qRound((qreal)w / win->devicePixelRatio()); h = qRound((qreal)h / win->devicePixelRatio()); } if (state->fixedSizeGui || state->noUserResizeGui) win->setFixedSize(w, h); else win->setMinimumSize(w, h); win->resize(w, h); } #endif } else { if (uiW) { QWindow *xwin = QWindow::fromWinId(reinterpret_cast(uiW)); ewWin = QWidget::createWindowContainer(xwin, win); state->pluginQWindow = xwin; win->setCentralWidget(ewWin); } // Set the minimum size to the supplied uiX11Size. if (fixScaling && win->devicePixelRatio() >= 1.0) { state->uiX11Size.setWidth(qRound((qreal)state->uiX11Size.width() / win->devicePixelRatio())); state->uiX11Size.setHeight(qRound((qreal)state->uiX11Size.height() / win->devicePixelRatio())); } if (state->fixedSizeGui || state->noUserResizeGui) win->setFixedSize(state->uiX11Size.width(), state->uiX11Size.height()); else win->setMinimumSize(state->uiX11Size.width(), state->uiX11Size.height()); // this shouldn't be required // if(ewWin && state->uiX11Size.width() == 0 || state->uiX11Size.height() == 0) // win->resize(ewWin->size()); } } win->show(); win->setWindowTitle(state->extHost.plugin_human_id); } else if(state->hasExternalGui) { state->widget = uiW; LV2_EXTERNAL_UI_SHOW((LV2_External_UI_Widget *)state->widget); } LV2Synth::lv2ui_PostShow(state); return; } } if(win != nullptr) { win->stopNextTime(); } state->pluginWindow = nullptr; state->widget = nullptr; state->uiCurrent = nullptr; //no ui is shown state->hasExternalGui = state->hasGui = false; } const void *LV2Synth::lv2state_stateRetreive(LV2_State_Handle handle, uint32_t key, size_t *size, uint32_t *type, uint32_t *flags) { QMap >::const_iterator it; LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; LV2Synth *synth = state->synth; const char *cKey = synth->unmapUrid(key); assert(cKey != nullptr); //should'n happen QString strKey = QString(cKey); it = state->iStateValues.find(strKey); if(it != state->iStateValues.end()) { if(it.value().second.type() == QVariant::ByteArray) { QString sType = it.value().first; QByteArray arrType = sType.toUtf8(); *type = synth->mapUrid(arrType.constData()); *flags = LV2_STATE_IS_POD; QByteArray valArr = it.value().second.toByteArray(); #ifdef LV2_DEBUG QByteArray ba = QByteArray(valArr); ba.append(QChar(0)); DEBUG_LV2_GENERAL(stderr, "lv2state_stateRetreive synth:%s value:%s size:%d\n", synth->name().toLocal8Bit().constData(), ba.constData(), valArr.size()); #endif if(sType.compare(QString(LV2_ATOM__Path)) == 0) //prepend project folder to abstract path { // Make everything relative to the plugin config folder. QString cfg_path = MusEGlobal::museProject; #ifdef LV2_MAKE_PATH_SUPPORT const QString plug_name = (state->sif != nullptr) ? state->sif->name() : state->plugInst->name(); cfg_path += QDir::separator() + QFileInfo(MusEGlobal::muse->projectName()).fileName() + QString(".lv2state") + QDir::separator() + plug_name; // If the plugin config folder does not exist, the plugin did not create it first with makePath(). // So it likely won't and doesn't use makePath or a config folder. // Just make everything relative to the project folder. if(!QDir(cfg_path).exists()) cfg_path = MusEGlobal::museProject; #endif QString strPath = QString::fromUtf8(valArr.data()); const QFileInfo fiPath(strPath); if(fiPath.isRelative()) { strPath = cfg_path + QDir::separator() + strPath; valArr = strPath.toUtf8(); int len = strPath.length(); valArr.setRawData(strPath.toUtf8().constData(), len + 1); valArr [len] = 0; } } size_t i; size_t numValues = state->numStateValues; for(i = 0; i < numValues; ++i) { if(state->tmpValues [i] == nullptr) break; } assert(i < numValues); //sanity check size_t sz = valArr.size(); state->iStateValues.remove(strKey); if(sz > 0) { state->tmpValues [i] = new char [sz]; memset(state->tmpValues [i], 0, sz); memcpy(state->tmpValues [i], valArr.constData(), sz); *size = sz; return state->tmpValues [i]; } } } return nullptr; } LV2_State_Status LV2Synth::lv2state_stateStore(LV2_State_Handle handle, uint32_t key, const void *value, size_t size, uint32_t type, uint32_t flags) { if(flags & (LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE)) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; LV2Synth *synth = state->synth; const char *uriKey = synth->unmapUrid(key); const char *uriType = synth->unmapUrid(type); assert(uriType != nullptr && uriKey != nullptr); //FIXME: buggy plugin or uridBiMap realization? QString strKey = QString(uriKey); QMap >::const_iterator it = state->iStateValues.find(strKey); if(it == state->iStateValues.end()) { QString strUriType = uriType; QVariant varVal = QByteArray((const char *)value, size); #ifdef LV2_DEBUG QByteArray ba = QByteArray((const char *)value, size); ba.append(QChar(0)); DEBUG_LV2_GENERAL(stderr, "lv2state_stateStore: name:%s value:%s size:%d\n", synth->name().toLocal8Bit().constData(), ba.constData(), (int)size); #endif state->iStateValues.insert(strKey, QPair(strUriType, varVal)); } return LV2_STATE_SUCCESS; } return LV2_STATE_ERR_BAD_FLAGS; } LV2_Worker_Status LV2Synth::lv2wrk_scheduleWork(LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data) { #ifdef DEBUG_LV2 std::cerr << "LV2Synth::lv2wrk_scheduleWork" << std::endl; #endif LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; if(!state->wrkDataBuffer->put(data, size)) { fprintf(stderr, "lv2wrk_scheduleWork: Worker buffer overflow\n"); return LV2_WORKER_ERR_NO_SPACE; } if(MusEGlobal::audio->freewheel()) //don't wait for a thread. Do it now state->wrkThread->makeWork(); else return state->wrkThread->scheduleWork(); return LV2_WORKER_SUCCESS; } LV2_Worker_Status LV2Synth::lv2wrk_respond(LV2_Worker_Respond_Handle handle, uint32_t size, const void *data) { // Some plugins expose a work_response function, but it may be empty // and/or they don't bother calling respond (this). (LSP...) LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; if(!state->wrkRespDataBuffer->put(data, size)) { fprintf(stderr, "lv2wrk_respond: Response buffer overflow\n"); return LV2_WORKER_ERR_NO_SPACE; } return LV2_WORKER_SUCCESS; } void LV2Synth::lv2conf_write(LV2PluginWrapper_State *state, int level, Xml &xml) { state->iStateValues.clear(); state->numStateValues = 0; if(state->iState != nullptr) { state->iState->save(lilv_instance_get_handle(state->handle), LV2Synth::lv2state_stateStore, state, LV2_STATE_IS_POD, state->_ppifeatures); } if(state->sif != nullptr) // write control ports values only for synths { for(size_t c = 0; c < state->sif->_inportsControl; c++) { state->iStateValues.insert(QString(state->sif->_controlInPorts [c].cName), QPair(QString(""), QVariant((double)state->sif->_controls[c].val))); } } if(state->uiCurrent != nullptr) { const char *cUiUri = lilv_node_as_uri(lilv_ui_get_uri(state->uiCurrent)); state->iStateValues.insert(QString(cUiUri), QPair(QString(""), QVariant(QString(cUiUri)))); } QByteArray arrOut; QDataStream streamOut(&arrOut, QIODevice::WriteOnly); streamOut << state->iStateValues; // Weee! Compression! QByteArray outEnc64 = qCompress(arrOut).toBase64(); QString customData(outEnc64); for (int pos=0; pos < customData.size(); pos+=150) { customData.insert(pos++,'\n'); // add newlines for readability } xml.strTag(level, "customData", customData); } void LV2Synth::lv2conf_set(LV2PluginWrapper_State *state, const std::vector &customParams) { if(customParams.size() == 0) return; state->iStateValues.clear(); for(size_t i = 0; i < customParams.size(); i++) { QString param = customParams [i]; param.remove('\n'); // remove all linebreaks that may have been added to prettyprint the songs file QByteArray paramIn; paramIn.append(param); // Try to uncompress the data. QByteArray dec64 = qUncompress(QByteArray::fromBase64(paramIn)); // Failed? Try uncompressed. if(dec64.isEmpty()) dec64 = QByteArray::fromBase64(paramIn); QDataStream streamIn(&dec64, QIODevice::ReadOnly); streamIn >> state->iStateValues; break; //one customData tag includes all data in base64 } size_t numValues = state->iStateValues.size(); state->numStateValues = numValues; if(state->iState != nullptr && numValues > 0) { state->tmpValues = new char*[numValues]; memset(state->tmpValues, 0, numValues * sizeof(char *)); state->iState->restore(lilv_instance_get_handle(state->handle), LV2Synth::lv2state_stateRetreive, state, 0, state->_ppifeatures); for(size_t i = 0; i < numValues; ++i) { if(state->tmpValues [i] != nullptr) delete [] state->tmpValues [i]; } delete [] state->tmpValues; state->tmpValues = nullptr; } QMap >::const_iterator it; for(it = state->iStateValues.begin(); it != state->iStateValues.end(); ++it) { QString name = it.key(); QVariant qVal = it.value().second; if(!name.isEmpty() && qVal.isValid()) { if(qVal.type() == QVariant::String) // plugin ui uri { QString sUiUri = qVal.toString(); LV2_PLUGIN_UI_TYPES::iterator it; for(it = state->synth->_pluginUiTypes.begin(); it != state->synth->_pluginUiTypes.end(); ++it) { if(sUiUri == QString(lilv_node_as_uri(lilv_ui_get_uri(it->first)))) { state->uiCurrent = it->first; break; } } } else { if(state->sif != nullptr) //setting control value only for synths { bool ok = false; float val = (float)qVal.toDouble(&ok); if(ok) { std::map::iterator it = state->controlsNameMap.find(name.toLower()); if(it != state->controlsNameMap.end()) { size_t ctrlNum = it->second; state->sif->_controls [ctrlNum].val = state->sif->_controls [ctrlNum].tmpVal = val; } } } } } } } unsigned LV2Synth::lv2ui_IsSupported(const char *, const char *ui_type_uri) { if(strcmp(LV2_F_UI_Qt5_UI, ui_type_uri) == 0 || (strcmp(LV2_UI__GtkUI, ui_type_uri) == 0) || strcmp(LV2_UI__X11UI, ui_type_uri) == 0) { return 1; } return 0; } void LV2Synth::lv2prg_updateProgram(LV2PluginWrapper_State *state, int idx) { assert(state != nullptr); if(state->prgIface == nullptr || idx < 0) return; const uint32_t u_idx = idx; const LV2_Program_Descriptor *pDescr = state->prgIface->get_program( lilv_instance_get_handle(state->handle), u_idx); uint32_t hb = 0; uint32_t lb = 0; if(pDescr) { // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. hb = pDescr->bank >> 8; lb = pDescr->bank & 0xff; } // No program at that index? Out of range? Erase what we may have. if(!pDescr || hb >= 128 || lb >= 128 || pDescr->program >= 128) { // Erase the program to index item. for(std::map::iterator i = state->prg2index.begin(); i != state->prg2index.end(); ++i) { if(i->second == u_idx) { state->prg2index.erase(i); break; } } // Erase the index to program item. std::map::iterator i = state->index2prg.find(u_idx); if(i != state->index2prg.end()) state->index2prg.erase(i); return; } lv2ExtProgram extPrg; extPrg.index = u_idx; extPrg.bank = pDescr->bank; extPrg.prog = pDescr->program; extPrg.useIndex = true; extPrg.name = QString(pDescr->name); std::pair::iterator, bool > ii2p = state->index2prg.insert(std::make_pair(u_idx, extPrg)); if(!ii2p.second) ii2p.first->second = extPrg; hb &= 0x7f; lb &= 0x7f; uint32_t midiprg = (hb << 16) + (lb << 8) + extPrg.prog; std::pair::iterator, bool > ip2i = state->prg2index.insert(std::make_pair(midiprg, u_idx)); if(!ip2i.second) ip2i.first->second = u_idx; } void LV2Synth::lv2prg_updatePrograms(LV2PluginWrapper_State *state) { assert(state != nullptr); state->index2prg.clear(); state->prg2index.clear(); if(state->prgIface != nullptr) { uint32_t iPrg = 0; const LV2_Program_Descriptor *pDescr = nullptr; while((pDescr = state->prgIface->get_program( lilv_instance_get_handle(state->handle), iPrg)) != nullptr) { // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. uint32_t hb = pDescr->bank >> 8; uint32_t lb = pDescr->bank & 0xff; if(hb < 128 && lb < 128 && pDescr->program < 128) { lv2ExtProgram extPrg; extPrg.index = iPrg; extPrg.bank = pDescr->bank; extPrg.prog = pDescr->program; extPrg.useIndex = true; extPrg.name = QString(pDescr->name); state->index2prg.insert(std::make_pair(iPrg, extPrg)); hb &= 0x7f; lb &= 0x7f; uint32_t midiprg = (hb << 16) + (lb << 8) + extPrg.prog; state->prg2index.insert(std::make_pair(midiprg, iPrg)); } ++iPrg; } } } #ifdef MIDNAM_SUPPORT void LV2Synth::lv2midnam_updateMidnam(LV2PluginWrapper_State *state) { assert(state != nullptr); if(state->midnamIface == nullptr || state->sif == nullptr) return; char *pMidnam = state->midnamIface->midnam(lilv_instance_get_handle(state->handle)); //fprintf(stderr, "%s\n", pMidnam); if(pMidnam) { // Wrap an xml around the text. Xml xml(pMidnam); // Read the xml into our midnam container. state->sif->synthI()->readMidnamDocument(xml); // Free the text. state->midnamIface->free(pMidnam); } // Unused yet. //char *pModel = state->midnamIface->model(lilv_instance_get_handle(state->handle)); //state->midnamIface->free(pModel); } #endif int LV2Synth::lv2_printf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...) { va_list argptr; va_start(argptr, fmt); int ret = LV2Synth::lv2_vprintf(handle, type, fmt, argptr); va_end(argptr); return ret; } int LV2Synth::lv2_vprintf(LV2_Log_Handle, LV2_URID, const char *fmt, va_list ap) { return vprintf(fmt, ap); } #ifdef LV2_MAKE_PATH_SUPPORT char *LV2Synth::lv2state_makePath(LV2_State_Make_Path_Handle handle, const char *path) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; assert(state != nullptr); const QString plug_name = (state->sif != nullptr) ? state->sif->name() : state->plugInst->name(); // Dangerous, if a folder by that name happens to already exist. const QString cfg_path = MusEGlobal::museProject + QDir::separator() + QFileInfo(MusEGlobal::muse->projectName()).fileName() + QString(".lv2state") + QDir::separator() + plug_name; QDir dir; dir.setPath(cfg_path); // int i = 0; // const QString fin_Path = cfg_path + "-%1"; // do dir.setPath(fin_Path.arg(++i)); // while (dir.exists()); QFileInfo fi(path); if (fi.isRelative()) fi.setFile(dir, fi.filePath()); if (fi.isSymLink()) fi.setFile(fi.symLinkTarget()); const QString& absolute_dir = fi.absolutePath(); if (!QDir(absolute_dir).exists()) dir.mkpath(absolute_dir); const QString& absolute_path = fi.absoluteFilePath(); return ::strdup(absolute_path.toUtf8().constData()); } #endif #ifdef LV2_MAKE_PATH_SUPPORT char *LV2Synth::lv2state_abstractPath(LV2_State_Map_Path_Handle handle, const char *absolute_path) #else char *LV2Synth::lv2state_abstractPath(LV2_State_Map_Path_Handle /*handle*/, const char *absolute_path) #endif { //some plugins do not support abstract paths properly, //so return duplicate without modification for now //return strdup(absolute_path); // Make everything relative to the plugin config folder. QString cfg_path = MusEGlobal::museProject; #ifdef LV2_MAKE_PATH_SUPPORT LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; assert(state != nullptr); const QString plug_name = (state->sif != nullptr) ? state->sif->name() : state->plugInst->name(); cfg_path += QDir::separator() + QFileInfo(MusEGlobal::muse->projectName()).fileName() + QString(".lv2state") + QDir::separator() + plug_name; // If the plugin config folder does not exist, the plugin did not create it first with makePath(). // So it likely won't and doesn't use makePath or a config folder. // Just make everything relative to the project folder. if(!QDir(cfg_path).exists()) cfg_path = MusEGlobal::museProject; #endif const QFileInfo fi(absolute_path); const QString& afp = fi.absoluteFilePath(); QString abstract_path; // If the given path is absolute, all paths IN OR UNDER the project folder // (the folder containing the .med song file) are deemed RELATIVE - to the lv2 // state config folder. All paths OUTSIDE the PROJECT folder are deemed ABSOLUTE paths. // Does the path not start with the project folder? if(fi.isAbsolute() && !afp.startsWith(MusEGlobal::museProject)) abstract_path = afp; else abstract_path = QDir(cfg_path).relativeFilePath(afp); return ::strdup(abstract_path.toUtf8().constData()); } #ifdef LV2_MAKE_PATH_SUPPORT char *LV2Synth::lv2state_absolutePath(LV2_State_Map_Path_Handle handle, const char *abstract_path) #else char *LV2Synth::lv2state_absolutePath(LV2_State_Map_Path_Handle /*handle*/, const char *abstract_path) #endif { // Make everything relative to the plugin config folder. QString cfg_path = MusEGlobal::museProject; #ifdef LV2_MAKE_PATH_SUPPORT LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; assert(state != nullptr); const QString plug_name = (state->sif != nullptr) ? state->sif->name() : state->plugInst->name(); cfg_path += QDir::separator() + QFileInfo(MusEGlobal::muse->projectName()).fileName() + QString(".lv2state") + QDir::separator() + plug_name; // If the plugin config folder does not exist, the plugin did not create it first with makePath(). // So it likely won't and doesn't use makePath or a config folder. // Just make everything relative to the project folder. if(!QDir(cfg_path).exists()) cfg_path = MusEGlobal::museProject; #endif QFileInfo fi(abstract_path); if (fi.isRelative()) fi.setFile(QDir(cfg_path), fi.filePath()); const QString& absolute_path = fi.absoluteFilePath(); return ::strdup(absolute_path.toUtf8().constData()); } void LV2Synth::lv2state_populatePresetsMenu(LV2PluginWrapper_State *state, MusEGui::PopupMenu *menu) { menu->clear(); menu->setIcon(QIcon(*MusEGui::presetsNewIcon)); LV2Synth *synth = state->synth; //this is good by slow down menu population. //So it's called only on changes (preset save/manual update) // kybos: The menu build-up is slow anyway when there are many presets. // Better load the presets here for one plugin than on the program start for hundreds of them. // The presets are now loaded exactly once in the function below (only on the first call), unless updated LV2Synth::lv2state_UnloadLoadPresets(synth, true); MusEGui::MenuTitleItem *actPresetActionsHeader = new MusEGui::MenuTitleItem(QObject::tr("Preset actions"), menu); menu->addAction(actPresetActionsHeader); QAction *actSave = menu->addAction(QObject::tr("Save preset...")); actSave->setObjectName("lv2state_presets_save_action"); actSave->setData(QVariant::fromValue(static_cast(lv2CacheNodes.lv2_actionSavePreset))); QAction *actUpdate = menu->addAction(QObject::tr("Update list")); actUpdate->setObjectName("lv2state_presets_update_action"); actUpdate->setData(QVariant::fromValue(static_cast(lv2CacheNodes.lv2_actionUpdatePresets))); std::map::iterator it; MusEGui::MenuTitleItem *actSavedPresetsHeader = new MusEGui::MenuTitleItem(QObject::tr("Saved presets"), menu); menu->addAction(actSavedPresetsHeader); for(it = synth->_presets.begin(); it != synth->_presets.end(); ++it) { QAction *act = menu->addAction(it->first); act->setData(QVariant::fromValue(static_cast((it->second)))); } if(menu->actions().size() == 0) { QAction *act = menu->addAction(QObject::tr("No presets found")); act->setDisabled(true); act->setData(QVariant::fromValue(nullptr)); } } void LV2Synth::lv2state_PortWrite(LV2UI_Controller controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, const void *buffer, bool fromUi) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)controller; assert(state != nullptr); //this shouldn't happen assert(state->inst != nullptr || state->sif != nullptr); // this too if(protocol != 0 && protocol != state->synth->_uAtom_EventTransfer) { #ifdef DEBUG_LV2 std::cerr << "LV2Synth::lv2state_PortWrite: unsupported protocol (" << protocol << ")" << std::endl; #endif return; } if(protocol == state->synth->_uAtom_EventTransfer) //put atom transfers to dedicated ring buffer { #ifdef DEBUG_LV2 std::cerr << "LV2Synth::lv2state_PortWrite: atom_EventTransfer, port = " << port_index << ", size =" << buffer_size << std::endl; #endif state->uiControlEvt.put(port_index, buffer_size, buffer); return; } std::map::iterator it = state->synth->_idxToControlMap.find(port_index); if(it == state->synth->_idxToControlMap.end()) { #ifdef DEBUG_LV2 std::cerr << "LV2Synth::lv2state_PortWrite: wrong port index (" << port_index << ")" << std::endl; #endif return; } uint32_t cport = it->second; float value = *(float *)buffer; // Schedules a timed control change: ControlEvent ce; ce.unique = false; ce.fromGui = fromUi; // It came from the plugin's own GUI (or not). ce.idx = cport; ce.value = value; // Don't use timestamp(), because it's circular, which is making it impossible to deal // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. ce.frame = MusEGlobal::audio->curFrame(); ControlFifo *_controlFifo = nullptr; if(state->inst != nullptr) { _controlFifo = &state->plugInst->_controlFifo; if(fromUi) { // Record automation: // Take care of this immediately, because we don't want the silly delay associated with // processing the fifo one-at-a-time in the apply(). // NOTE: With some vsts we don't receive control events until the user RELEASES a control. // So the events all arrive at once when the user releases a control. // That makes this pretty useless... But what the heck... //AutomationType at = AUTO_OFF; if(state->plugInst->_track && state->plugInst->_id != -1) { unsigned long id = genACnum(state->plugInst->_id, cport); state->plugInst->_track->recordAutomation(id, value); //at = state->plugInst->_track->automationType(); } //state->plugInst->enableController(cport, false); } } else if(state->sif != nullptr) { _controlFifo = &state->sif->_controlFifo; if(fromUi) { // Record automation: // Take care of this immediately, because we don't want the silly delay associated with // processing the fifo one-at-a-time in the apply(). // NOTE: With some vsts we don't receive control events until the user RELEASES a control. // So the events all arrive at once when the user releases a control. // That makes this pretty useless... But what the heck... if(state->sif->id() != -1) { unsigned long pid = genACnum(state->sif->id(), cport); state->sif->synthI()->recordAutomation(pid, value); } //state->sif->enableController(cport, false); } } if(fromUi) { state->controlTimers [cport] = 1000 / 30; // 1 sec controllers will not be send to guis } assert(_controlFifo != nullptr); if(_controlFifo->put(ce)) std::cerr << "LV2Synth::lv2state_PortWrite: fifo overflow: in control number:" << cport << std::endl; #ifdef DEBUG_LV2 std::cerr << "LV2Synth::lv2state_PortWrite: port=" << cport << "(" << port_index << ")" << ", val=" << value << std::endl; #endif } void LV2Synth::lv2state_setPortValue(const char *port_symbol, void *user_data, const void *value, uint32_t size, uint32_t type) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)user_data; assert(state != nullptr); std::map::iterator it = state->controlsSymMap.find(QString::fromUtf8(port_symbol).toLower()); if(it != state->controlsSymMap.end()) { size_t ctrlNum = it->second; uint32_t ctrlIdx = state->synth->_controlInPorts [ctrlNum].index; float fvalue; if (type == state->atomForge.Float) { fvalue = *(const float*)value; } else if (type == state->atomForge.Double) { fvalue = *(const double*)value; } else if (type == state->atomForge.Int) { fvalue = *(const int32_t*)value; } else if (type == state->atomForge.Long) { fvalue = *(const int64_t*)value; } else { fprintf(stderr, "error: Preset `%s' value has bad type <%s>\n", port_symbol, state->synth->uridBiMap.unmap(type)); return; } LV2Synth::lv2state_PortWrite((LV2UI_Controller)user_data, ctrlIdx, size, 0, &fvalue, false); } } const void *LV2Synth::lv2state_getPortValue(const char *port_symbol, void *user_data, uint32_t *size, uint32_t *type) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)user_data; assert(state != nullptr); std::map::iterator it = state->controlsSymMap.find(QString::fromUtf8(port_symbol).toLower()); *size = *type = 0; if(it != state->controlsSymMap.end()) { size_t ctrlNum = it->second; MusECore::Port *controls = nullptr; if(state->plugInst != nullptr) { controls = state->plugInst->controls; } else if(state->sif != nullptr) { controls = state->sif->_controls; } if(controls != nullptr) { *size = sizeof(float); *type = state->atomForge.Float; return &controls [ctrlNum].val; } } return nullptr; } void LV2Synth::lv2state_applyPreset(LV2PluginWrapper_State *state, LilvNode *preset) { //handle special actions first if(preset == lv2CacheNodes.lv2_actionSavePreset) { bool isOk = false; QString presetName = QInputDialog::getText(MusEGlobal::muse, QObject::tr("Enter new preset name"), QObject::tr(("Preset name:")), QLineEdit::Normal, QString(""), &isOk); if(isOk && !presetName.isEmpty()) { presetName = presetName.trimmed(); QString synthName = state->synth->name().replace(' ', '_'); QString presetDir = MusEGlobal::museUser + QString("/.lv2/") + synthName + QString("_") + presetName + QString(".lv2/"); QString presetFile = synthName + QString("_") + presetName + QString(".ttl"); QString plugName = (state->sif != nullptr) ? state->sif->name() : state->plugInst->name(); // TODO Do we need to change this to the new lv2state folder ??? QString plugFileDirName = MusEGlobal::museProject + QString("/") + plugName; // const QString plug_name = (state->sif != nullptr) ? state->sif->name() : state->plugInst->name(); // // Make everything relative to the plugin config folder. // QString cfg_path = // MusEGlobal::museProject; // #ifdef LV2_MAKE_PATH_SUPPORT // const QString plug_name = (state->sif != nullptr) ? state->sif->name() : state->plugInst->name(); // cfg_path += QDir::separator() + // QFileInfo(MusEGlobal::muse->projectName()).fileName() + QString(".lv2state") + // QDir::separator() + plug_name; // // If the plugin config folder does not exist, the plugin did not create it first with makePath(). // // So it likely won't and doesn't use makePath or a config folder. // // Just make everything relative to the project folder. // if(!QDir(cfg_path).exists()) // cfg_path = // MusEGlobal::museProject; // #endif char *cPresetName = strdup(presetName.toUtf8().constData()); char *cPresetDir = strdup(presetDir.toUtf8().constData()); char *cPresetFile = strdup(presetFile.toUtf8().constData()); char *cPlugFileDirName = strdup(plugFileDirName.toUtf8().constData()); LilvState* const lilvState = lilv_state_new_from_instance( state->synth->_handle, state->handle, &state->synth->_lv2_urid_map, cPlugFileDirName, cPresetDir, cPresetDir, cPresetDir, LV2Synth::lv2state_getPortValue, state, LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE, nullptr); lilv_state_set_label(lilvState, cPresetName); lilv_state_save(lilvWorld, &state->synth->_lv2_urid_map, &state->synth->_lv2_urid_unmap, lilvState, nullptr, cPresetDir, cPresetFile); lilv_state_free(lilvState); free(cPresetName); free(cPresetDir); free(cPresetFile); free(cPlugFileDirName); LV2Synth::lv2state_UnloadLoadPresets(state->synth, true, true); } return; } else if(preset == lv2CacheNodes.lv2_actionUpdatePresets) { LV2Synth::lv2state_UnloadLoadPresets(state->synth, true, true); return; } LilvState* lilvState = lilv_state_new_from_world(lilvWorld, &state->synth->_lv2_urid_map, preset); if(lilvState) { lilv_state_restore(lilvState, state->handle, LV2Synth::lv2state_setPortValue, state, 0, nullptr); lilv_state_free(lilvState); } } void LV2Synth::lv2state_UnloadLoadPresets(LV2Synth *synth, bool load, bool update) { assert(synth != nullptr); static bool s_loaded = false; if (load && !update && s_loaded) return; //std::cerr << "LV2Synth::lv2state_UnloadLoadPresets: handling <" << synth->_name.toStdString() << ">" << std::endl; std::map::iterator it; for(it = synth->_presets.begin(); it != synth->_presets.end(); ++it) { lilv_world_unload_resource(lilvWorld, it->second); lilv_node_free(it->second); } synth->_presets.clear(); if(load) { if(update) { //rescan and refresh user-defined presets first QDirIterator dir_it(MusEGlobal::museUser + QString("/.lv2"), QStringList() << "*.lv2", QDir::Dirs, QDirIterator::NoIteratorFlags); while (dir_it.hasNext()) { QString nextDir = dir_it.next() + QString("/"); std::cerr << nextDir.toStdString() << std::endl; SerdNode sdir = serd_node_new_file_uri((const uint8_t*)nextDir.toUtf8().constData(), 0, 0, 0); LilvNode* ldir = lilv_new_uri(lilvWorld, (const char*)sdir.buf); lilv_world_unload_bundle(lilvWorld, ldir); lilv_world_load_bundle(lilvWorld, ldir); serd_node_free(&sdir); lilv_node_free(ldir); } } //scan for presets LilvNodes* presets = lilv_plugin_get_related(synth->_handle, lv2CacheNodes.lv2_psetPreset); LILV_FOREACH(nodes, i, presets) { const LilvNode* preset = lilv_nodes_get(presets, i); #ifdef DEBUG_LV2 std::cerr << "\tPreset: " << lilv_node_as_uri(preset) << std::endl; #endif lilv_world_load_resource(lilvWorld, preset); LilvNodes* pLabels = lilv_world_find_nodes(lilvWorld, preset, lv2CacheNodes.lv2_rdfsLabel, nullptr); if (pLabels != nullptr) { const LilvNode* pLabel = lilv_nodes_get_first(pLabels); synth->_presets.insert(std::make_pair(lilv_node_as_string(pLabel), lilv_node_duplicate(preset))); lilv_nodes_free(pLabels); } else { #ifdef DEBUG_LV2 std::cerr << "\t\tPreset <%s> has no rdfs:label" << lilv_node_as_string(lilv_nodes_get(presets, i)) << std::endl; #endif } } lilv_nodes_free(presets); s_loaded = true; } } void LV2SynthIF::lv2prg_Changed(LV2_Programs_Handle handle, int32_t index) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; #ifdef DEBUG_LV2 std::cerr << "LV2Synth::lv2prg_Changed: index: " << index << std::endl; #endif if(state->sif && state->sif->synthI_const()) state->operationsFifo.put(LV2OperationMessage(LV2OperationMessage::ProgramChanged, index)); } #ifdef MIDNAM_SUPPORT void LV2SynthIF::lv2midnam_Changed(LV2_Midnam_Handle handle) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; #ifdef DEBUG_LV2 std::cerr << "LV2Synth::lv2midnam_Changed:" << std::endl; #endif if(state->sif && state->sif->synthI_const()) state->operationsFifo.put(LV2OperationMessage(LV2OperationMessage::MidnamUpdate)); } #endif LV2Synth::LV2Synth(const QFileInfo &fi, const QString& uri, const QString& label, const QString& name, const QString& author, const LilvPlugin *_plugin, PluginFeatures_t reqFeatures) : Synth(fi, uri, label, name, author, QString(""), reqFeatures), _handle(_plugin), _features(nullptr), _ppfeatures(nullptr), _options(nullptr), _isSynth(false), _uis(nullptr), _hasFreeWheelPort(false), _freeWheelPortIndex(0), _hasLatencyPort(false), _latencyPortIndex(0), _usesTimePosition(false), _isConstructed(false), _pluginControlsDefault(nullptr), _pluginControlsMin(nullptr), _pluginControlsMax(nullptr) { //fake id for LV2PluginWrapper functionality // LV2 does not use unique id numbers and frowns upon using anything but the uri. // _uniqueID = uniqueID++; // _uniqueID = 0; _midi_event_id = mapUrid(LV2_MIDI__MidiEvent); _uTime_Position = mapUrid(LV2_TIME__Position); _uTime_frame = mapUrid(LV2_TIME__frame); _uTime_framesPerSecond = mapUrid(LV2_TIME__framesPerSecond); _uTime_speed = mapUrid(LV2_TIME__speed); _uTime_beatsPerMinute = mapUrid(LV2_TIME__beatsPerMinute); _uTime_beatsPerBar = mapUrid(LV2_TIME__beatsPerBar); _uTime_beat = mapUrid(LV2_TIME__beat); _uTime_bar = mapUrid(LV2_TIME__bar); _uTime_barBeat = mapUrid(LV2_TIME__barBeat); _uTime_beatUnit = mapUrid(LV2_TIME__beatUnit); _uAtom_EventTransfer = mapUrid(LV2_ATOM__eventTransfer); _uAtom_Chunk = mapUrid(LV2_ATOM__Chunk); _uAtom_Sequence = mapUrid(LV2_ATOM__Sequence); _uAtom_StateChanged = mapUrid(LV2_F_STATE_CHANGED); _uAtom_Object = mapUrid(LV2_ATOM__Object); _sampleRate = (double)MusEGlobal::sampleRate; _fSampleRate = (float)MusEGlobal::sampleRate; _scaleFactor = (float)qApp->devicePixelRatio(); //prepare features and options arrays LV2_Options_Option _tmpl_options [] = { {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_SAMPLE_RATE), sizeof(float), uridBiMap.map(LV2_ATOM__Float), &_fSampleRate}, { LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_MIN_BLKLEN), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &LV2Synth::minBlockSize }, // &MusEGlobal::segmentSize}, {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_MAX_BLKLEN), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &MusEGlobal::segmentSize}, {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_NOM_BLKLEN), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &MusEGlobal::segmentSize}, {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_SEQ_SIZE), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &MusEGlobal::segmentSize}, {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_CORE__sampleRate), sizeof(double), uridBiMap.map(LV2_ATOM__Double), &_sampleRate}, {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_UI_SCALE_FACTOR), sizeof(float), uridBiMap.map(LV2_ATOM__Float), &_scaleFactor}, {LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, nullptr} }; _options = new LV2_Options_Option[SIZEOF_ARRAY(_tmpl_options)]; // last option is nullptrs for(uint32_t i = 0; i < SIZEOF_ARRAY(_tmpl_options); i++) { _options [i] = _tmpl_options [i]; } _features = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; _ppfeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; _lv2_urid_map.map = Synth_Urid_Map; _lv2_urid_map.handle = this; _lv2_urid_unmap.unmap = Synth_Urid_Unmap; _lv2_urid_unmap.handle = this; #ifdef LV2_URI_MAP_SUPPORT _lv2_uri_map.uri_to_id = Synth_Uri_Map; _lv2_uri_map.callback_data = this; #endif _lv2_log_log.handle = this; _lv2_log_log.printf = LV2Synth::lv2_printf; _lv2_log_log.vprintf = LV2Synth::lv2_vprintf; uint32_t i; for(i = 0; i < SIZEOF_ARRAY(lv2Features); i++) { _features [i] = lv2Features [i]; if(_features [i].URI == nullptr) { break; } if(std::string(LV2_F_URID_MAP) == _features [i].URI) { _features [i].data = &_lv2_urid_map; } else if(std::string(LV2_F_URID_UNMAP) == _features [i].URI) { _features [i].data = &_lv2_urid_unmap; } #ifdef LV2_URI_MAP_SUPPORT else if(std::string(LV2_F_URI_MAP) == _features [i].URI) { _features [i].data = &_lv2_uri_map; } #endif else if(std::string(LV2_F_OPTIONS) == _features [i].URI) { _features [i].data = _options; } else if(std::string(LV2_F_INSTANCE_ACCESS) == _features [i].URI) { _fInstanceAccess = i; } else if(std::string(LV2_F_UI_PARENT) == _features [i].URI) { _fUiParent = i; } else if((std::string(LV2_F_UI_EXTERNAL_HOST) == _features [i].URI)) { _fExtUiHost = i; } else if((std::string(LV2_UI_EXTERNAL_DEPRECATED) == _features [i].URI)) { _fExtUiHostD = i; } else if((std::string(LV2_F_WORKER_SCHEDULE) == _features [i].URI)) { _fWrkSchedule = i; } else if((std::string(LV2_UI__resize) == _features [i].URI)) { _fUiResize = i; } else if((std::string(LV2_PROGRAMS__Host) == _features [i].URI)) { _fPrgHost = i; } #ifdef MIDNAM_SUPPORT else if((std::string(LV2_MIDNAM__update) == _features [i].URI)) { _fMidNamUpdate = i; } #endif else if((std::string(LV2_LOG__log) == _features [i].URI)) { _features [i].data = &_lv2_log_log; } #ifdef LV2_MAKE_PATH_SUPPORT else if((std::string(LV2_STATE__makePath) == _features [i].URI)) { _fMakePath = i; } #endif else if((std::string(LV2_STATE__mapPath) == _features [i].URI)) { _fMapPath = i; } else if(std::string(LV2_F_DATA_ACCESS) == _features [i].URI) { _fDataAccess = i; //must be the last! } _ppfeatures [i] = &_features [i]; } _ppfeatures [i] = nullptr; //enum plugin ports; uint32_t numPorts = lilv_plugin_get_num_ports(_handle); const LilvPort *lilvFreeWheelPort = lilv_plugin_get_port_by_designation(_handle, lv2CacheNodes.lv2_InputPort, lv2CacheNodes.lv2_FreeWheelPort); uint32_t latency_port = 0; const bool has_latency_port = lilv_plugin_has_latency(_handle); if(has_latency_port) latency_port = lilv_plugin_get_latency_port_index(_handle); _pluginControlsDefault = new float [numPorts]; _pluginControlsMin = new float [numPorts]; _pluginControlsMax = new float [numPorts]; memset(_pluginControlsDefault, 0, sizeof(float) * numPorts); memset(_pluginControlsMin, 0, sizeof(float) * numPorts); memset(_pluginControlsMax, 0, sizeof(float) * numPorts); lilv_plugin_get_port_ranges_float(_handle, _pluginControlsMin, _pluginControlsMax, _pluginControlsDefault); for(uint32_t i = 0; i < numPorts; i++) { const LilvPort *_port = lilv_plugin_get_port_by_index(_handle, i); LilvNode *_nPname = lilv_port_get_name(_handle, _port); const LilvNode *_nPsym = lilv_port_get_symbol(_handle, _port); char cAutoGenPortName [1024]; char cAutoGenPortSym [1024]; memset(cAutoGenPortName, 0, sizeof(cAutoGenPortName)); memset(cAutoGenPortSym, 0, sizeof(cAutoGenPortSym)); snprintf(cAutoGenPortName, sizeof(cAutoGenPortName) - 1, "autoport #%u", i); snprintf(cAutoGenPortSym, sizeof(cAutoGenPortSym) - 1, "autoport#%u", i); const char *_portName = cAutoGenPortName; const char *_portSym = cAutoGenPortSym; if(_nPname != nullptr) _portName = lilv_node_as_string(_nPname); if(_nPsym != nullptr) _portSym = lilv_node_as_string(_nPsym); const bool optional = lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_connectionOptional); LV2_MIDI_PORTS *mPorts = &_midiOutPorts; LV2_CONTROL_PORTS *cPorts = &_controlOutPorts; LV2_AUDIO_PORTS *aPorts = &_audioOutPorts; if(lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_InputPort)) { mPorts = &_midiInPorts; cPorts = &_controlInPorts; aPorts = &_audioInPorts; } else if(!lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_OutputPort)) { #ifdef DEBUG_LV2 std::cerr << "plugin has port with unknown direction - ignoring" << std::endl; #endif if(_nPname != nullptr) lilv_node_free(_nPname); continue; } bool isCVPort = lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_CVPort); if (lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_ControlPort) || isCVPort) { QString groupString; LilvNode* group = lilv_port_get(_handle, _port, lv2CacheNodes.pg_group); if (group) { LilvNode* groupName = lilv_world_get(lilvWorld, group, lv2CacheNodes.lv2_name, nullptr); if (!groupName) groupName = lilv_world_get(lilvWorld, group, lv2CacheNodes.lv2_rdfsLabel, nullptr); groupString = lilv_node_as_string(groupName); lilv_node_free(group); // if (!groupString.isEmpty() && !portGroups.contains(groupString)) // portGroups.append(groupString); // printf("*** Groups: %s\n", qPrintable(groupString)); } CtrlEnumValues *enumValues = nullptr; LV2ControlPortType _cType = LV2_PORT_CONTINUOUS; if (lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portEnumeration)) { LilvScalePoints* scalePoints = lilv_port_get_scale_points(_handle, _port); if (scalePoints) { enumValues = new CtrlEnumValues; LILV_FOREACH(scale_points, it, scalePoints) { const LilvScalePoint* sp = lilv_scale_points_get(scalePoints, it); const LilvNode* lab = lilv_scale_point_get_label(sp); const LilvNode* val = lilv_scale_point_get_value(sp); float value; if (lilv_node_is_int(val)) value = static_cast(lilv_node_as_int(val)); else value = lilv_node_as_float(val); enumValues->insert(std::pair(value, lilv_node_as_string(lab))); } lilv_scale_points_free(scalePoints); _cType = LV2_PORT_ENUMERATION; } } else if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portToggled)) _cType = LV2_PORT_TOGGLE; else if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portDiscrete)) _cType = LV2_PORT_DISCRETE; else if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portInteger)) _cType = LV2_PORT_INTEGER; else if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portLogarithmic)) _cType = LV2_PORT_LOGARITHMIC; bool notOnGui = false; if (lilv_port_has_property(_handle, _port, lv2CacheNodes.pp_notOnGui)) notOnGui = true; bool isTrigger = false; if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portTrigger)) isTrigger = true; cPorts->push_back(LV2ControlPort(_port, i, 0.0f, _portName, _portSym, _cType, isCVPort, enumValues, groupString, isTrigger, notOnGui)); if(std::isnan(_pluginControlsDefault [i])) _pluginControlsDefault [i] = 0; if(std::isnan(_pluginControlsMin [i])) _pluginControlsMin [i] = 0; if(std::isnan(_pluginControlsMax [i])) _pluginControlsMax [i] = 0; if(isCVPort) { _pluginControlsDefault [i] = 1; _pluginControlsMin [i] = 0; _pluginControlsMax [i] = 1; } else if (lilv_port_has_property (_handle, _port, lv2CacheNodes.lv2_SampleRate)) { _pluginControlsDefault [i] *= MusEGlobal::sampleRate; _pluginControlsMin [i] *= MusEGlobal::sampleRate; _pluginControlsMax [i] *= MusEGlobal::sampleRate; } } else if(lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_AudioPort)) { aPorts->push_back(LV2AudioPort(_port, i, nullptr, _portName)); } #ifdef LV2_EVENT_BUFFER_SUPPORT else if(lilv_port_is_a(_handle, _port, lv2CacheNodes.ev_EventPort)) { bool portSupportsTimePos = lilv_port_supports_event(_handle, _port, lv2CacheNodes.lv2_TimePosition); if(portSupportsTimePos) _usesTimePosition = true; mPorts->push_back(LV2MidiPort(_port, i, _portName, true /* old api is on */,portSupportsTimePos)); } #endif else if(lilv_port_is_a(_handle, _port, lv2CacheNodes.atom_AtomPort)) { bool portSupportsTimePos = lilv_port_supports_event(_handle, _port, lv2CacheNodes.lv2_TimePosition); if(portSupportsTimePos) _usesTimePosition = true; #ifdef LV2_EVENT_BUFFER_SUPPORT mPorts->push_back(LV2MidiPort(_port, i, _portName, false /* old api is off */, portSupportsTimePos)); #else mPorts->push_back(LV2MidiPort(_port, i, _portName, portSupportsTimePos)); #endif } else if(!optional) { //#ifdef DEBUG_LV2 std::cerr << "plugin has port with unknown type - ignoring plugin " << label.toStdString() << "!" << std::endl; //#endif if(_nPname != nullptr) lilv_node_free(_nPname); return; } if(_nPname != nullptr) lilv_node_free(_nPname); } const uint32_t ci_sz = _controlInPorts.size(); for(uint32_t i = 0; i < ci_sz; ++i) { _idxToControlMap.insert(std::pair(_controlInPorts [i].index, i)); if(lilvFreeWheelPort != nullptr) { if(lilv_port_get_index(_handle, _controlInPorts [i].port) == lilv_port_get_index(_handle, lilvFreeWheelPort)) { _hasFreeWheelPort = true; _freeWheelPortIndex = i; } } } if(has_latency_port) { const uint32_t co_sz = _controlOutPorts.size(); for(uint32_t i = 0; i < co_sz; ++i) { if(_controlOutPorts [i].index == latency_port) { _hasLatencyPort = true; _latencyPortIndex = i; } } } const LilvPluginClass *cls = lilv_plugin_get_class(_plugin); const LilvNode *ncuri = lilv_plugin_class_get_uri(cls); const char *clsname = lilv_node_as_uri(ncuri); if((strcmp(clsname, LV2_INSTRUMENT_CLASS) == 0) && (_midiInPorts.size() > 0)) { _isSynth = true; } const LilvNode *pluginUIType = nullptr; _uis = nullptr; _uis = lilv_plugin_get_uis(_handle); if(_uis) { LilvIter *it = lilv_uis_begin(_uis); #ifdef DEBUG_LV2 std::cerr << "Plugin support uis of type:" << std::endl; #endif while(!lilv_uis_is_end(_uis, it)) { const LilvUI *ui = lilv_uis_get(_uis, it); if(lilv_ui_is_supported(ui, LV2Synth::lv2ui_IsSupported, lv2CacheNodes.host_uiType, &pluginUIType)) { #ifdef DEBUG_LV2 const char *strUiType = lilv_node_as_string(pluginUIType); //internal uis are preferred std::cerr << "Plugin " << label.toStdString() << " supports ui of type " << strUiType << std::endl; #endif #ifndef HAVE_GTK2 const char *cUiUri = lilv_node_as_uri(pluginUIType); if(strcmp(LV2_UI__GtkUI, cUiUri) != 0) #endif _pluginUiTypes.insert(std::make_pair(ui, std::make_pair(false, pluginUIType))); } else { const LilvNodes *nUiClss = lilv_ui_get_classes(ui); LilvIter *nit = lilv_nodes_begin(nUiClss); while(!lilv_nodes_is_end(_uis, nit)) { const LilvNode *nUiCls = lilv_nodes_get(nUiClss, nit); #ifdef DEBUG_LV2 const char *ClsStr = lilv_node_as_string(nUiCls); std::cerr << ClsStr << std::endl; #endif bool extUi = lilv_node_equals(nUiCls, lv2CacheNodes.ext_uiType); bool extdUi = lilv_node_equals(nUiCls, lv2CacheNodes.ext_d_uiType); if(extUi || extdUi) { pluginUIType = extUi ? lv2CacheNodes.ext_uiType : lv2CacheNodes.ext_d_uiType; #ifdef DEBUG_LV2 std::cerr << "Plugin " << label.toStdString() << " supports ui of type " << LV2_UI_EXTERNAL << std::endl; #endif #ifndef HAVE_GTK2 const char *cUiUri = lilv_node_as_uri(pluginUIType); if(strcmp(LV2_UI__GtkUI, cUiUri) != 0) #endif _pluginUiTypes.insert(std::make_pair(ui, std::make_pair(true, pluginUIType))); } nit = lilv_nodes_next(nUiClss, nit); } } it = lilv_uis_next(_uis, it); } } _presets.clear(); // This is very slow, especially when done on start up for several hundreds lv2 plugins like here. // Better load the presets for individual plugin only when its menu is populated (kybos). // LV2Synth::lv2state_UnloadLoadPresets(this, true); _isConstructed = true; } LV2Synth::~LV2Synth() { if(_handle) LV2Synth::lv2state_UnloadLoadPresets(this); if(_ppfeatures) { delete [] _ppfeatures; _ppfeatures = nullptr; } if(_features) { delete [] _features; _features = nullptr; } if(_options) { delete [] _options; _options = nullptr; } if(_uis != nullptr) { lilv_uis_free(_uis); _uis = nullptr; } if(_pluginControlsDefault) { delete [] _pluginControlsDefault; _pluginControlsDefault = nullptr; } if(_pluginControlsMin) { delete [] _pluginControlsMin; _pluginControlsMin = nullptr; } if(_pluginControlsMax) { delete [] _pluginControlsMax; _pluginControlsMax = nullptr; } } SynthIF *LV2Synth::createSIF(SynthI *synthi) { ++_instances; LV2SynthIF *sif = new LV2SynthIF(synthi); if(!sif->init(this)) { delete sif; sif = nullptr; } return sif; } LV2_URID LV2Synth::mapUrid(const char *uri) { return uridBiMap.map(uri); } const char *LV2Synth::unmapUrid(LV2_URID id) { return uridBiMap.unmap(id); } LV2SynthIF::~LV2SynthIF() { if(_state != nullptr) { _state->deleteLater = true; //_uiState->uiTimer->stopNextTime(false); if(_state->pluginWindow != nullptr) _state->pluginWindow->stopNextTime(); else LV2Synth::lv2state_FreeState(_state); _state = nullptr; } LV2_AUDIO_PORTS::iterator _itA = _audioInPorts.begin(); for(; _itA != _audioInPorts.end(); ++_itA) { free((*_itA).buffer); } _itA = _audioOutPorts.begin(); for(; _itA != _audioOutPorts.end(); ++_itA) { free((*_itA).buffer); } if(_audioInSilenceBuf) free(_audioInSilenceBuf); if(_audioInBuffers) { delete [] _audioInBuffers; _audioInBuffers = nullptr; } if(_audioOutBuffers) { delete [] _audioOutBuffers; _audioOutBuffers = nullptr; } if(_controls) { delete [] _controls; } if(_controlsOut) { delete [] _controlsOut; } if(_ppifeatures) { delete [] _ppifeatures; _ppifeatures = nullptr; } if(_ifeatures) { delete [] _ifeatures; _ifeatures = nullptr; } } LV2SynthIF::LV2SynthIF(SynthI *s): SynthIF(s) { _synth = nullptr; _handle = nullptr; _audioInBuffers = nullptr; _audioOutBuffers = nullptr; _inports = 0; _outports = 0; _controls = nullptr; _controlsOut = nullptr; _inportsControl = 0; _outportsControl = 0; _inportsMidi = 0; _outportsMidi = 0; _audioInSilenceBuf = nullptr; _ifeatures = nullptr; _ppifeatures = nullptr; _state = nullptr; } bool LV2SynthIF::init(LV2Synth *s) { _synth = s; //use LV2Synth features as template _state = new LV2PluginWrapper_State; _state->inst = nullptr; _state->widget = nullptr; _state->uiInst = nullptr; _state->plugInst = nullptr; _state->_ifeatures = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; _state->_ppifeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; _state->sif = this; _state->synth = _synth; _state->wrkDataBuffer = new LockFreeDataRingBuffer(LV2_WRK_FIFO_SIZE); _state->wrkRespDataBuffer = new LockFreeDataRingBuffer(LV2_WRK_FIFO_SIZE); LV2Synth::lv2state_FillFeatures(_state); _state->handle = _handle = lilv_plugin_instantiate(_synth->_handle, (double)MusEGlobal::sampleRate, _state->_ppifeatures); if(_handle == nullptr) { delete [] _state->_ppifeatures; _state->_ppifeatures = nullptr; delete [] _state->_ifeatures; _state->_ifeatures = nullptr; return false; } _audioInPorts = s->_audioInPorts; _audioOutPorts = s->_audioOutPorts; _controlInPorts = s->_controlInPorts; _controlOutPorts = s->_controlOutPorts; _inportsMidi = _state->midiInPorts.size(); _outportsMidi = _state->midiOutPorts.size(); _inportsControl = _controlInPorts.size(); _outportsControl = _controlOutPorts.size(); if(_inportsControl != 0) { _controls = new Port[_inportsControl]; } else { _controls = nullptr; } if(_outportsControl != 0) { _controlsOut = new Port[_outportsControl]; } else { _controlsOut = nullptr; } _synth->midiCtl2PortMap.clear(); _synth->port2MidiCtlMap.clear(); for(size_t i = 0; i < _inportsControl; i++) { uint32_t idx = _controlInPorts [i].index; _controls [i].idx = idx; _controls [i].val = _controls [i].tmpVal = _controlInPorts [i].defVal = _synth->_pluginControlsDefault [idx]; if(_synth->_hasFreeWheelPort && _synth->_freeWheelPortIndex == i) _controls [i].enCtrl = false; else _controls [i].enCtrl = true; _controlInPorts [i].minVal = _synth->_pluginControlsMin [idx]; _controlInPorts [i].maxVal = _synth->_pluginControlsMax [idx]; int ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + i; // We have a controller number! Insert it and the lv2 port number into both maps. _synth->midiCtl2PortMap.insert(std::pair(ctlnum, i)); _synth->port2MidiCtlMap.insert(std::pair(i, ctlnum)); int id = genACnum(MusECore::MAX_PLUGINS, i); CtrlList *cl; CtrlListList *cll = track()->controller(); iCtrlList icl = cll->find(id); if(icl == cll->end()) { cl = new CtrlList(id); cll->add(cl); cl->setCurVal(_controls[i].val); } else { cl = icl->second; _controls[i].val = cl->curVal(); } cl->setRange(_synth->_pluginControlsMin [idx], _synth->_pluginControlsMax [idx]); cl->setName(QString(_controlInPorts [i].cName)); CtrlValueType vt = VAL_LINEAR; switch(_controlInPorts [i].cType) { case LV2_PORT_ENUMERATION: vt = VAL_ENUM; break; case LV2_PORT_CONTINUOUS: vt = VAL_LINEAR; break; case LV2_PORT_DISCRETE: case LV2_PORT_INTEGER: vt = VAL_INT; break; case LV2_PORT_LOGARITHMIC: vt = VAL_LOG; break; case LV2_PORT_TOGGLE: vt = VAL_BOOL; break; default: break; } cl->setValueType(vt); cl->setMode(((_controlInPorts [i].cType == LV2_PORT_CONTINUOUS) ||(_controlInPorts [i].cType == LV2_PORT_LOGARITHMIC))? CtrlList::INTERPOLATE : CtrlList::DISCRETE); if(!_controlInPorts [i].isCVPort) lilv_instance_connect_port(_handle, idx, &_controls [i].val); } for(size_t i = 0; i < _outportsControl; i++) { uint32_t idx = _controlOutPorts [i].index; _controlsOut[i].idx = idx; _controlsOut[i].val = 0.0; _controlsOut[i].tmpVal = 0.0; _controlsOut[i].enCtrl = false; _controlOutPorts [i].defVal = _controlOutPorts [i].minVal = _controlOutPorts [i].maxVal = 0.0; if(!_controlOutPorts [i].isCVPort) lilv_instance_connect_port(_handle, idx, &_controlsOut[i].val); } #ifdef _WIN32 _audioInSilenceBuf = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(_audioInSilenceBuf == nullptr) { fprintf(stderr, "ERROR: LV2SynthIF::init: _aligned_malloc returned error: nullptr. Aborting!\n"); abort(); } #else int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) { _audioInSilenceBuf[q] = MusEGlobal::denormalBias; } } else { memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); } //cache number of ports _inports = _audioInPorts.size(); _outports = _audioOutPorts.size(); if(_inports > 0) { _audioInBuffers = new float*[_inports]; for(size_t i = 0; i < _inports; i++) { #ifdef _WIN32 _audioInBuffers [i] = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(_audioInBuffers == nullptr) { fprintf(stderr, "ERROR: LV2SynthIF::init: _aligned_malloc returned error: nullptr. Aborting!\n"); abort(); } #else int rv = posix_memalign((void **)&_audioInBuffers [i], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) { _audioInBuffers [i][q] = MusEGlobal::denormalBias; } } else { memset(_audioInBuffers [i], 0, sizeof(float) * MusEGlobal::segmentSize); } _audioInPorts [i].buffer = _audioInBuffers [i]; lilv_instance_connect_port(_handle, _audioInPorts [i].index, _audioInBuffers [i]); } } if(_outports > 0) { _audioOutBuffers = new float*[_outports]; for(size_t i = 0; i < _outports; i++) { #ifdef _WIN32 _audioOutBuffers [i] = (float *) _aligned_malloc(16, sizeof(float) * MusEGlobal::segmentSize); if(_audioOutBuffers == nullptr) { fprintf(stderr, "ERROR: LV2SynthIF::init: _aligned_malloc returned error: nullptr. Aborting!\n"); abort(); } #else int rv = posix_memalign((void **)&_audioOutBuffers [i], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) { _audioOutBuffers [i][q] = MusEGlobal::denormalBias; } } else { memset(_audioOutBuffers [i], 0, sizeof(float) * MusEGlobal::segmentSize); } _audioOutPorts [i].buffer = _audioOutBuffers [i]; lilv_instance_connect_port(_handle, _audioOutPorts [i].index, _audioOutBuffers [i]); } } LV2Synth::lv2state_PostInstantiate(_state); activate(); return true; } bool LV2SynthIF::doSelectProgram(unsigned char channel, int bankH, int bankL, int prog) { // // Only if there's something to change... // if(bankH >= 128 && bankL >= 128 && prog >= 128) // return; if(_state && _state->prgIface && (_state->prgIface->select_program || _state->prgIface->select_program_for_channel)) { if(bankH > 127) // Map "dont care" to 0 bankH = 0; if(bankL > 127) bankL = 0; if(prog > 127) prog = 0; const int bank = (bankH << 8) | bankL; if(_state->newPrgIface) _state->prgIface->select_program_for_channel(lilv_instance_get_handle(_state->handle), channel, (uint32_t)bank, (uint32_t)prog); else _state->prgIface->select_program(lilv_instance_get_handle(_state->handle), (uint32_t)bank, (uint32_t)prog); /* * A plugin is permitted to re-write the values of its input * control ports when select_program is called. The host should * re-read the input control port values and update its own * records appropriately. (This is the only circumstance in which * a LV2 plugin is allowed to modify its own control-input ports.) */ if(id() != -1) { for(unsigned long k = 0; k < _inportsControl; ++k) { // We're in the audio thread context: no need to send a message, just modify directly. synti->setPluginCtrlVal(genACnum(id(), k), _controls[k].val); } } //set update ui program flag _state->uiChannel = channel; _state->uiBank = bank; _state->uiProg = prog; _state->uiDoSelectPrg = true; return true; } return false; } bool LV2SynthIF::doSendProgram(unsigned char channel, int bankH, int bankL, int prog, LV2EvBuf *evBuf, long frame) { // _curOutParamNums[chn].resetParamNums(); // Probably best to reset. // don't output program changes for GM drum channel //if (!(MusEGlobal::song->mtype() == MT_GM && chn == 9)) { const int hb = bankH & 0xff; const int lb = bankL & 0xff; const int pr = prog & 0xff; if(hb != 0xff || lb != 0xff || pr != 0xff) { if (hb != 0xff) { sendLv2MidiEvent(evBuf, frame, 3, (ME_CONTROLLER | channel) & 0xff, CTRL_HBANK, hb & 0x7f); } if (lb != 0xff) { sendLv2MidiEvent(evBuf, frame, 3, (ME_CONTROLLER | channel) & 0xff, CTRL_LBANK, lb & 0x7f); } if (pr != 0xff) { sendLv2MidiEvent(evBuf, frame, 2, (ME_PROGRAM | channel) & 0xff, pr & 0x7f, 0); } /* * A plugin is permitted to re-write the values of its input * control ports when select_program is called. The host should * re-read the input control port values and update its own * records appropriately. (This is the only circumstance in which * a LV2 plugin is allowed to modify its own control-input ports.) */ if(id() != -1) { for(unsigned long k = 0; k < _inportsControl; ++k) { // We're in the audio thread context: no need to send a message, just modify directly. synti->setPluginCtrlVal(genACnum(id(), k), _controls[k].val); } } return true; } //} return false; } void LV2SynthIF::sendLv2MidiEvent(LV2EvBuf *evBuf, long frame, int paramCount, uint8_t a, uint8_t b, uint8_t c) { if(paramCount < 1 || paramCount > 3) return; if(evBuf) { uint8_t midiEv [paramCount]; midiEv [0] = a; if(paramCount >= 2) midiEv [1] = b; if(paramCount == 3) midiEv [2] = c; #ifdef LV2_EVENT_BUFFER_SUPPORT evBuf->write(frame, 0, _synth->_midi_event_id, paramCount, midiEv); #else evBuf->write(frame, _synth->_midi_event_id, paramCount, midiEv); #endif } } int LV2SynthIF::channels() const { return (_outports) > MusECore::MAX_CHANNELS ? MusECore::MAX_CHANNELS : (_outports) ; } int LV2SynthIF::totalInChannels() const { return _inports; } int LV2SynthIF::totalOutChannels() const { return _outports; } void LV2SynthIF::activate() { if(_handle) { lilv_instance_activate(_handle); } } void LV2SynthIF::deactivate() { if(_handle) { lilv_instance_deactivate(_handle); } } void LV2SynthIF::deactivate3() { deactivate(); } int LV2SynthIF::eventsPending() const { //TODO: what's this? return 0; } bool LV2SynthIF::lv2MidiControlValues(size_t port, int ctlnum, int *min, int *max, int *def) { float fmin, fmax, fdef; int imin; float frng; fdef = _controlInPorts [port].defVal; fmin = _controlInPorts [port].minVal; fmax = _controlInPorts [port].maxVal; bool hasdef = (fdef == fdef); if(fmin != fmin) { fmin = 0.0; } if(fmax != fmax) { fmax = 0.0; } MidiController::ControllerType t = midiControllerType(ctlnum); #ifdef PLUGIN_DEBUGIN printf("lv2MidiControlValues: ctlnum:%d ladspa port:%lu has default?:%d default:%f\n", ctlnum, port, hasdef, fdef); #endif frng = fmax - fmin; imin = lrintf(fmin); //imax = lrintf(fmax); int ctlmn = 0; int ctlmx = 127;// Avoid divide-by-zero error below. #ifdef PLUGIN_DEBUGIN printf("lv2MidiControlValues: port min:%f max:%f \n", fmin, fmax); #endif bool isneg = (imin < 0); int bias = 0; switch(t) { case MidiController::RPN: case MidiController::NRPN: case MidiController::Controller7: if(isneg) { ctlmn = -64; ctlmx = 63; bias = -64; } else { ctlmn = 0; ctlmx = 127; } break; case MidiController::Controller14: case MidiController::RPN14: case MidiController::NRPN14: if(isneg) { ctlmn = -8192; ctlmx = 8191; bias = -8192; } else { ctlmn = 0; ctlmx = 16383; } break; case MidiController::Program: ctlmn = 0; ctlmx = 0x3fff; // FIXME: Really should not happen or be allowed. What to do here... break; case MidiController::Pitch: ctlmn = -8192; ctlmx = 8191; break; case MidiController::Velo: // cannot happen default: break; } float fctlrng = float(ctlmx - ctlmn); // It's a floating point control, just use wide open maximum range. *min = ctlmn; *max = ctlmx; float normdef = (frng == 0.0) ? 0.0 : fdef / frng; fdef = normdef * fctlrng; // FIXME: TODO: Incorrect... Fix this somewhat more trivial stuff later.... *def = (int)lrintf(fdef) + bias; #ifdef PLUGIN_DEBUGIN printf("lv2MidiControlValues: setting default:%d\n", *def); #endif return hasdef; } //--------------------------------------------------------- // midi2LadspaValue //--------------------------------------------------------- float LV2SynthIF::midi2Lv2Value(unsigned long port, int ctlnum, int val) { float fmin, fmax; int imin; float frng; MidiController::ControllerType t = midiControllerType(ctlnum); #ifdef PLUGIN_DEBUGIN printf("midi2Lv2Value: ctlnum:%d port:%lu val:%d\n", ctlnum, port, val); #endif fmin = _controlInPorts [port].minVal; fmax = _controlInPorts [port].maxVal; if(fmin != fmin) { fmin = 0.0; } if(fmax != fmax) { fmax = 0.0; } frng = fmax - fmin; imin = lrintf(fmin); int ctlmn = 0; int ctlmx = 127; #ifdef PLUGIN_DEBUGIN printf("midi2Lv2Value: port min:%f max:%f \n", fmin, fmax); #endif bool isneg = (imin < 0); int bval = val; //int cval = val; switch(t) { case MidiController::RPN: case MidiController::NRPN: case MidiController::Controller7: if(isneg) { ctlmn = -64; ctlmx = 63; bval -= 64; //cval -= 64; } else { ctlmn = 0; ctlmx = 127; //cval -= 64; } break; case MidiController::Controller14: case MidiController::RPN14: case MidiController::NRPN14: if(isneg) { ctlmn = -8192; ctlmx = 8191; bval -= 8192; //cval -= 8192; } else { ctlmn = 0; ctlmx = 16383; //cval -= 8192; } break; case MidiController::Program: ctlmn = 0; ctlmx = 0xffffff; break; case MidiController::Pitch: ctlmn = -8192; ctlmx = 8191; break; case MidiController::Velo: // cannot happen default: break; } int ctlrng = ctlmx - ctlmn; float fctlrng = float(ctlmx - ctlmn); // Avoid divide-by-zero error below. if(ctlrng == 0) { return 0.0; } // It's a floating point control, just use wide open maximum range. float normval = float(bval) / fctlrng; float ret = normval * frng + fmin; #ifdef PLUGIN_DEBUGIN printf("midi2Lv2Value: float returning:%f\n", ret); #endif return ret; } int LV2SynthIF::getControllerInfo(int id, QString* name, int *ctrl, int *min, int *max, int *initval) { size_t _id = (size_t)id; if(_id == _inportsControl || _id == _inportsControl + 1) { // // It is unknown at this point whether or not a synth recognizes aftertouch and poly aftertouch // (channel and key pressure) midi messages, so add support for them now (as controllers). // if(_id == _inportsControl) { *ctrl = CTRL_POLYAFTER; } else if(_id == _inportsControl + 1) { *ctrl = CTRL_AFTERTOUCH; } *min = 0; *max = 127; *initval = CTRL_VAL_UNKNOWN; *name = midiCtrlName(*ctrl); return ++_id; } else if(_id >= _inportsControl + 2) { return 0; } int ctlnum; // = DSSI_NONE; // No controller number? Give it one. //if(ctlnum == DSSI_NONE) //{ // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + _id; //} int def = CTRL_VAL_UNKNOWN; if(lv2MidiControlValues(_id, ctlnum, min, max, &def)) { *initval = def; } else { *initval = CTRL_VAL_UNKNOWN; } #ifdef DEBUG_LV2 printf("LV2SynthIF::getControllerInfo passed ctlnum:%d min:%d max:%d initval:%d\n", ctlnum, *min, *max, *initval); #endif *ctrl = ctlnum; *name = QString(_controlInPorts[_id].cName); return ++_id; } #ifdef MIDNAM_SUPPORT bool LV2SynthIF::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; return synthI_const()->midnamDocument().getNoteSampleName(drum, channel, patch, note, name); } #else bool LV2SynthIF::getNoteSampleName( bool /*drum*/, int /*channel*/, int /*patch*/, int /*note*/, QString* /*name*/) const { return false; } #endif bool LV2SynthIF::processEvent(const MidiPlayEvent &e, LV2EvBuf *evBuf, long frame) { int chn = e.channel(); int a = e.dataA(); int b = e.dataB(); int type = e.type(); #ifdef LV2_DEBUG fprintf(stderr, "LV2SynthIF::processEvent midi event type:%d chn:%d a:%d b:%d\n", e.type(), chn, a, b); #endif // REMOVE Tim. Noteoff. Added. const MidiInstrument::NoteOffMode nom = synti->noteOffMode(); switch(type) { case ME_NOTEON: #ifdef LV2_DEBUG fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_NOTEON\n"); #endif // REMOVE Tim. Noteoff. Changed. // if(b) // { // sendLv2MidiEvent(evBuf, frame, (type | chn) & 0xff, a & 0x7f, b & 0x7f); // } // else // { // sendLv2MidiEvent(evBuf, frame, (ME_NOTEOFF | chn) & 0xff, a & 0x7f, 0); // } if(b == 0) { // Handle zero-velocity note ons. Technically this is an error because internal midi paths // are now all 'note-off' without zero-vel note ons - they're converted to note offs. // Nothing should be setting a Note type Event's on velocity to zero. // But just in case... If we get this warning, it means there is still code to change. fprintf(stderr, "LV2SynthIF::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", e.time(), e.type(), chn, a, b); switch(nom) { // Instrument uses note offs. Convert to zero-vel note off. case MidiInstrument::NoteOffAll: //if(MusEGlobal::midiOutputTrace) // fprintf(stderr, "MidiOut: LV2: Following event will be converted to zero-velocity note off:\n"); sendLv2MidiEvent(evBuf, frame, 3, (ME_NOTEOFF | chn) & 0xff, a & 0x7f, 0); break; // Instrument uses no note offs at all. Send as-is. case MidiInstrument::NoteOffNone: // Instrument converts all note offs to zero-vel note ons. Send as-is. case MidiInstrument::NoteOffConvertToZVNoteOn: sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); break; } } else sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); break; case ME_NOTEOFF: #ifdef LV2_DEBUG fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_NOTEOFF\n"); #endif // REMOVE Tim. Noteoff. Changed. // sendLv2MidiEvent(evBuf, frame, (type | chn) & 0xff, a & 0x7f, b & 0x7f); switch(nom) { // Instrument uses note offs. Send as-is. case MidiInstrument::NoteOffAll: sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); break; // Instrument uses no note offs at all. Send nothing. Eat up the event - return false. case MidiInstrument::NoteOffNone: return false; // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. case MidiInstrument::NoteOffConvertToZVNoteOn: //if(MusEGlobal::midiOutputTrace) // fprintf(stderr, "MidiOut: LV2: Following event will be converted to zero-velocity note on:\n"); sendLv2MidiEvent(evBuf, frame, 3, (ME_NOTEON | chn) & 0xff, a & 0x7f, 0); break; } break; case ME_PROGRAM: { #ifdef LV2_DEBUG fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_PROGRAM\n"); #endif int hb, lb; synti->currentProg(chn, nullptr, &lb, &hb); synti->setCurrentProg(chn, a & 0xff, lb, hb); if(doSelectProgram(chn, hb, lb, a)) // Event pointer not filled. Return false. return false; else if(doSendProgram(chn, hb, lb, a, evBuf, frame)) // Event pointer filled. Return true. return true; // Event pointer not filled. Return false. return false; } break; case ME_CONTROLLER: { #ifdef LV2_DEBUG fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER\n"); #endif // Our internal hwCtrl controllers support the 'unknown' value. // Don't send 'unknown' values to the driver. Ignore and return no error. if(b == CTRL_VAL_UNKNOWN) return false; if(a == CTRL_PROGRAM) { #ifdef LV2_DEBUG fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PROGRAM\n"); #endif int hb = (b >> 16) & 0xff; int lb = (b >> 8) & 0xff; int pr = b & 0xff; synti->setCurrentProg(chn, pr, lb, hb); if(doSelectProgram(chn, hb, lb, pr)) // Event pointer not filled. Return false. return false; else if(doSendProgram(chn, hb, lb, pr, evBuf, frame)) // Event pointer filled. Return true. return true; // Event pointer not filled. Return false. return false; } if(a == CTRL_HBANK) { int lb, pr; synti->currentProg(chn, &pr, &lb, nullptr); synti->setCurrentProg(chn, pr, lb, b & 0xff); if(doSelectProgram(chn, b, lb, pr)) // Event pointer not filled. Return false. return false; else if(doSendProgram(chn, b, lb, pr, evBuf, frame)) // Event pointer filled. Return true. return true; // Event pointer not filled. Return false. return false; } if(a == CTRL_LBANK) { int hb, pr; synti->currentProg(chn, &pr, nullptr, &hb); synti->setCurrentProg(chn, pr, b & 0xff, hb); if(doSelectProgram(chn, hb, b, pr)) // Event pointer not filled. Return false. return false; else if(doSendProgram(chn, hb, b, pr, evBuf, frame)) // Event pointer filled. Return true. return true; // Event pointer not filled. Return false. return false; } if(a == CTRL_PITCH) { #ifdef LV2_DEBUG fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PITCH\n"); #endif b += 8192; sendLv2MidiEvent(evBuf, frame, 3, (ME_PITCHBEND | chn) & 0xff, b & 0x7f, (b >> 7) & 0x7f); // Event pointer filled. Return true. return true; } if(a == CTRL_AFTERTOUCH) { #ifdef LV2_DEBUG fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_AFTERTOUCH\n"); #endif sendLv2MidiEvent(evBuf, frame, 2, (ME_AFTERTOUCH | chn) & 0xff, b & 0x7f, 0); // Event pointer filled. Return true. return true; } if((a | 0xff) == CTRL_POLYAFTER) { #ifdef LV2_DEBUG fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_POLYAFTER\n"); #endif sendLv2MidiEvent(evBuf, frame, 3, (ME_POLYAFTER | chn) & 0xff, a & 0x7f, b & 0x7f); // Event pointer filled. Return true. return true; } ciMidiCtl2LadspaPort ip = _synth->midiCtl2PortMap.find(a); // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)? // NOTE: There's no way to tell which of these controllers is supported by the plugin. // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port. if(ip == _synth->midiCtl2PortMap.end()) { if(midiControllerType(a) == MidiController::NRPN14 || midiControllerType(a) == MidiController::NRPN) { //send full nrpn control sequence (4 values): // 99 + ((a & 0xff00) >> 8) - first byte // 98 + (a & 0xff) - second byte // 6 + ((b & 0x3f80) >> 7) - third byte // 38 + (b & 0x7f) - fourth byte sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 99, ((a & 0xff00) >> 8)); sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 98, (a & 0xff)); sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 6, ((b & 0x3f80) >> 7)); sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 38, (b & 0x7f)); return true; } else if(midiControllerType(a) != MidiController::Controller7) { return false; // Event pointer not filled. Return false. } // Fill the event. #ifdef LV2_DEBUG printf("LV2SynthIF::processEvent non-ladspa filling midi event chn:%d dataA:%d dataB:%d\n", chn, a, b); #endif sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); return true; } unsigned long k = ip->second; int ctlnum; // = DSSI_NONE; // No midi controller for the ladspa port? Send to ladspa control. //if(ctlnum == DSSI_NONE) //{ // Sanity check. if(k > _inportsControl) { return false; } // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. ctlnum = k + (CTRL_NRPN14_OFFSET + 0x2000); //} float val = midi2Lv2Value(k, ctlnum, b); #ifdef LV2_DEBUG //fprintf(stderr, "LV2SynthIF::processEvent control port:%lu port:%lu dataA:%d Converting val from:%d to lv2:%f\n", i, k, a, b, val); fprintf(stderr, "LV2SynthIF::processEvent port:%lu dataA:%d Converting val from:%d to lv2:%f\n", k, a, b, val); #endif // Set the lv2 port value. _controls[k].val = val; // Need to update the automation value, otherwise it overwrites later with the last automation value. if(id() != -1) // We're in the audio thread context: no need to send a message, just modify directly. { synti->setPluginCtrlVal(genACnum(id(), k), val); } // Since we absorbed the message as a lv2 control change, return false - the event is not filled. return false; } break; case ME_PITCHBEND: a += 8192; sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, (a >> 7) & 0x7f); break; case ME_AFTERTOUCH: sendLv2MidiEvent(evBuf, frame, 2, (type | chn) & 0xff, a & 0x7f, 0); break; case ME_POLYAFTER: sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); break; default: if(MusEGlobal::debugMsg) { fprintf(stderr, "LV2SynthIF::processEvent midi event unknown type:%d\n", e.type()); } // Event not filled. return false; break; } return true; } bool LV2SynthIF::getData(MidiPort *, unsigned int pos, int ports, unsigned int nframes, float **buffer) { const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); // All ports must be connected to something! const unsigned long nop = ((unsigned long) ports) > _outports ? _outports : ((unsigned long) ports); // FIXME Better support for PluginPowerOf2BlockSize, by quantizing the control period times. // For now we treat it like fixed size. const bool usefixedrate = (requiredFeatures() & (PluginFixedBlockSize | PluginPowerOf2BlockSize | PluginCoarseBlockSize)); const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; const unsigned long min_per_mask = min_per - 1; // min_per must be power of 2 // This value is negative for correction. float latency_corr = 0.0f; if(track()) { const TrackLatencyInfo& li = track()->transportSource().getLatencyInfo(false); latency_corr = li._sourceCorrectionValue; } unsigned long sample = 0; AudioTrack *atrack = track(); const AutomationType at = atrack->automationType(); const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; CtrlListList *cll = atrack->controller(); ciCtrlList icl_first; const int plug_id = id(); LV2EvBuf *evBuf = (_inportsMidi > 0) ? _state->midiInPorts [0].buffer : nullptr; //set freewheeling property if plugin supports it if(_synth->_hasFreeWheelPort) { _controls [_synth->_freeWheelPortIndex].val = MusEGlobal::audio->freewheel() ? 1.0f : 0.0f; } if(plug_id != -1 && ports != 0) // Don't bother if not 'running'. { icl_first = cll->lower_bound(genACnum(plug_id, 0)); } bool used_in_chan_array[_inports]; // Don't bother initializing if not 'running'. // Don't bother if not 'running'. if(ports != 0) { // Initialize the array. for(size_t i = 0; i < _inports; ++i) used_in_chan_array[i] = false; if(!atrack->noInRoute()) { RouteList *irl = atrack->inRoutes(); for(ciRoute i = irl->begin(); i != irl->end(); ++i) { if(i->track->isMidiTrack()) continue; // Only this synth knows how many destination channels there are, // while only the track knows how many source channels there are. // So take care of the destination channels here, and let the track handle the source channels. const int dst_ch = i->channel <= -1 ? 0 : i->channel; if((unsigned long)dst_ch >= _inports) continue; const int dst_chs = i->channels <= -1 ? _inports : i->channels; //const int total_ins = atrack->totalRoutableInputs(Route::TRACK_ROUTE); const int src_ch = i->remoteChannel <= -1 ? 0 : i->remoteChannel; const int src_chs = i->channels; int fin_dst_chs = dst_chs; if((unsigned long)(dst_ch + fin_dst_chs) > _inports) fin_dst_chs = _inports - dst_ch; static_cast(i->track)->copyData(pos, dst_ch, dst_chs, fin_dst_chs, src_ch, src_chs, nframes, &_audioInBuffers[0], false, used_in_chan_array); const int nxt_ch = dst_ch + fin_dst_chs; for(int ch = dst_ch; ch < nxt_ch; ++ch) used_in_chan_array[ch] = true; } } } int cur_slice = 0; while(sample < nframes) { unsigned long nsamp = nframes - sample; const unsigned long slice_frame = pos + sample; // // Process automation control values, while also determining the maximum acceptable // size of this run. Further processing, from FIFOs for example, can lower the size // from there, but this section determines where the next highest maximum frame // absolutely needs to be for smooth playback of the controller value stream... // if(ports != 0) // Don't bother if not 'running'. { ciCtrlList icl = icl_first; for(unsigned long k = 0; k < _inportsControl; ++k) { //don't process freewheel port if(_synth->_hasFreeWheelPort && _synth->_freeWheelPortIndex == k) continue; CtrlList *cl = (cll && plug_id != -1 && icl != cll->end()) ? icl->second : nullptr; CtrlInterpolate &ci = _controls[k].interp; // Always refresh the interpolate struct at first, since things may have changed. // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && (slice_frame < (unsigned long)ci.sFrame || (ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame)))) { if(cl && plug_id != -1 && (unsigned long)cl->id() == genACnum(plug_id, k)) { cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); if(icl != cll->end()) { ++icl; } } else { // No matching controller, or end. Just copy the current value into the interpolator. // Keep the current icl iterator, because since they are sorted by frames, // if the IDs didn't match it means we can just let k catch up with icl. ci.sFrame = 0; ci.eFrame = 0; ci.eFrameValid = false; ci.sVal = _controls[k].val; ci.eVal = ci.sVal; ci.doInterp = false; ci.eStop = false; } } else { if(ci.eStop && ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. { // Clear the stop condition and set up the interp struct appropriately as an endless value. ci.sFrame = 0; //ci->eFrame; ci.eFrame = 0; ci.eFrameValid = false; ci.sVal = ci.eVal; ci.doInterp = false; ci.eStop = false; } if(cl && cll && icl != cll->end()) { ++icl; } } if(!usefixedrate && MusEGlobal::audio->isPlaying()) { unsigned long samps = nsamp; if(ci.eFrameValid) { samps = (unsigned long)ci.eFrame - slice_frame; } if(!ci.doInterp && samps > min_per) { samps &= ~min_per_mask; if((samps & min_per_mask) != 0) { samps += min_per; } } else { samps = min_per; } if(samps < nsamp) { nsamp = samps; } } if(ci.doInterp && cl) { _controls[k].val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); } else { _controls[k].val = ci.sVal; } _state->controlsMask [k] = true; #ifdef LV2_DEBUG_PROCESS fprintf(stderr, "LV2SynthIF::getData k:%lu val:%f sample:%lu ci.eFrame:%d nsamp:%lu \n", k, _controls[k].val, sample, ci.eFrame, nsamp); #endif } } bool found = false; unsigned long frame = 0; unsigned long index = 0; // Get all control ring buffer items valid for this time period... while(!_controlFifo.isEmpty()) { unsigned long evframe; const ControlEvent& v = _controlFifo.peek(); // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; #ifdef DEBUG_LV2 fprintf(stderr, "LV2SynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", found, evframe, frame, v.frame, v.idx, v.value, v.unique); #endif // Protection. Observed this condition. Why? Supposed to be linear timestamps. if(found && evframe < frame) { fprintf(stderr, "LV2SynthIF::getData *** Error: Event out of order: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d syncFrame:%u nframes:%u v.frame:%lu\n", evframe, frame, v.idx, v.value, v.unique, syncFrame, nframes, v.frame); // No choice but to ignore it. _controlFifo.remove(); // Done with the ring buffer's item. Remove it. continue; } if(evframe >= nframes // Next events are for a later period. || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. || (usefixedrate && found && v.unique && v.idx == index)) // Fixed rate and must reply to all. { break; } // _controlFifo.remove(); // Done with the ring buffer's item. Remove it. found = true; frame = evframe; index = v.idx; if(index >= _inportsControl) // Sanity check. { _controlFifo.remove(); // Done with the ring buffer's item. Remove it. break; } //don't process freewheel port if(_synth->_hasFreeWheelPort && _synth->_freeWheelPortIndex == index) { _controlFifo.remove(); // Done with the ring buffer's item. Remove it. continue; } if(ports == 0) // Don't bother if not 'running'. { _controls[index].val = v.value; // Might as well at least update these. } else { CtrlInterpolate *ci = &_controls[index].interp; // Tell it to stop the current ramp at this frame, when it does stop, set this value: ci->eFrame = frame; ci->eFrameValid = true; ci->eVal = v.value; ci->eStop = true; } // Need to update the automation value, otherwise it overwrites later with the last automation value. if(plug_id != -1) { synti->setPluginCtrlVal(genACnum(plug_id, index), v.value); } if(v.fromGui) //don't send gui control changes back { _state->lastControls [index] = v.value; _state->controlsMask [index] = false; } _controlFifo.remove(); // Done with the ring buffer's item. Remove it. } if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. { nsamp = frame - sample; } if(sample + nsamp > nframes) // Safety check. { nsamp = nframes - sample; } // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. // Note this means it is still possible to get stuck in the top loop (at least for a while). if(nsamp != 0) { LV2Synth::lv2audio_preProcessMidiPorts(_state, sample, nsamp); // Send transport events if any // FIXME These need to be mixed and serialized with the the events below! // For now, SendTransport is not sliceable, it only sends one event at sample. LV2Synth::lv2audio_SendTransport(_state, sample, nsamp, latency_corr); // Get the state of the stop flag. const bool do_stop = synti->stopFlag(); MidiPlayEvent buf_ev; // Transfer the user lock-free buffer events to the user sorted multi-set. // False = don't use the size snapshot, but update it. const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); for(unsigned int i = 0; i < usr_buf_sz; ++i) { if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) synti->_outUserEvents.insert(buf_ev); } // Transfer the playback lock-free buffer events to the playback sorted multi-set. const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); for(unsigned int i = 0; i < pb_buf_sz; ++i) { // Are we stopping? Just remove the item. if(do_stop) synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); // Otherwise get the item. else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) synti->_outPlaybackEvents.insert(buf_ev); } // Are we stopping? if(do_stop) { // Transport has stopped, purge ALL further scheduled playback events now. synti->_outPlaybackEvents.clear(); // Reset the flag. synti->setStopFlag(false); } iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); iMPEvent impe_us = synti->_outUserEvents.begin(); bool using_pb; while(1) { if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) using_pb = *impe_pb < *impe_us; else if(impe_pb != synti->_outPlaybackEvents.end()) using_pb = true; else if(impe_us != synti->_outUserEvents.end()) using_pb = false; else break; const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us; #ifdef LV2_DEBUG fprintf(stderr, "LV2SynthIF::getData eventFifos event time:%d\n", e.time()); #endif if(e.time() >= (sample + nsamp + syncFrame)) break; if(ports != 0) // Don't bother if not 'running'. { // Time-stamp the event. unsigned int ft = (e.time() < syncFrame) ? 0 : e.time() - syncFrame; ft = (ft < sample) ? 0 : ft - sample; if(ft >= nsamp) { fprintf(stderr, "LV2SynthIF::getData: eventFifos event time:%d out of range. pos:%d syncFrame:%u ft:%u sample:%lu nsamp:%lu\n", e.time(), pos, syncFrame, ft, sample, nsamp); ft = nsamp - 1; } if(processEvent(e, evBuf, ft)) { } } // Done with ring buffer's event. Remove it. // C++11. if(using_pb) impe_pb = synti->_outPlaybackEvents.erase(impe_pb); else impe_us = synti->_outUserEvents.erase(impe_us); } if(ports != 0) // Don't bother if not 'running'. { //connect ports for(size_t j = 0; j < _inports; ++j) { if(used_in_chan_array [j]) { lilv_instance_connect_port(_handle, _audioInPorts [j].index, _audioInBuffers [j] + sample); } else { lilv_instance_connect_port(_handle, _audioInPorts [j].index, _audioInSilenceBuf + sample); } } for(size_t j = 0; j < nop; ++j) { lilv_instance_connect_port(_handle, _audioOutPorts [j].index, buffer [j] + sample); } for(size_t j = nop; j < _outports; j++) { lilv_instance_connect_port(_handle, _audioOutPorts [j].index, _audioOutBuffers [j] + sample); } for(size_t j = 0; j < _inportsControl; ++j) { uint32_t idx = _controlInPorts [j].index; if(_state->pluginCVPorts [idx] != nullptr) { float cvVal = _controls [j].val; for(size_t jj = 0; jj < nsamp; ++jj) { _state->pluginCVPorts [idx] [jj + sample] = cvVal; } lilv_instance_connect_port(_handle, idx, _state->pluginCVPorts [idx] + sample); } } #ifdef LV2_DEBUG_PROCESS //dump atom sequence events to stderr if(evBuf) { evBuf->dump(); } #endif lilv_instance_run(_handle, nsamp); } //notify worker about processed data (if any) // Do it always, even if not 'running', in case we have 'left over' responses etc, // even though work is only initiated from 'run'. const unsigned int rsp_buf_sz = _state->wrkRespDataBuffer->getSize(false); for(unsigned int i_sz = 0; i_sz < rsp_buf_sz; ++i_sz) { if(_state->wrkIface && _state->wrkIface->work_response) { void *wrk_data = nullptr; size_t wrk_data_sz = 0; if(_state->wrkRespDataBuffer->peek(&wrk_data, &wrk_data_sz)) _state->wrkIface->work_response(lilv_instance_get_handle(_handle), wrk_data_sz, wrk_data); } _state->wrkRespDataBuffer->remove(); } if(ports != 0) // Don't bother if not 'running'. { //notify worker that this run() finished if(_state->wrkIface && _state->wrkIface->end_run) _state->wrkIface->end_run(lilv_instance_get_handle(_handle)); LV2Synth::lv2audio_postProcessMidiPorts(_state, sample, nsamp); } sample += nsamp; } ++cur_slice; // Slice is done. Moving on to any next slice now... } return true; } void LV2SynthIF::getNativeGeometry(int *x, int *y, int *w, int *h) const { if(_state->pluginWindow != nullptr && !_state->hasExternalGui) { QRect g = _state->pluginWindow->geometry(); if(x) *x = g.x(); if(y) *y = g.y(); if(w) *w = g.width(); if(h) *h = g.height(); return; } // Fall back to blank geometry. SynthIF::getNativeGeometry(x, y, w, h); } double LV2SynthIF::getParameter(long unsigned int n) const { if(n >= _inportsControl) { std::cout << "LV2SynthIF::getParameter param number " << n << " out of range of ports: " << _inportsControl << std::endl; return 0.0; } if(!_controls) { return 0.0; } return _controls[n].val; } double LV2SynthIF::getParameterOut(long unsigned int n) const { if(n >= _outportsControl) { std::cout << "LV2SynthIF::getParameterOut param number " << n << " out of range of ports: " << _outportsControl << std::endl; return 0.0; } if(!_controlsOut) { return 0.0; } return _controlsOut[n].val; } #ifdef MIDNAM_SUPPORT QString LV2SynthIF::getPatchNameMidNam(int ch, int prog, bool /*drum*/) const { const MidiNamPatch* mnp = synthI_const()->midnamDocument().findPatch(ch, prog); if(!mnp) return QString("?"); return mnp->name(); } #endif QString LV2SynthIF::getPatchName(int ch, int prog, bool drum) const { // If there is a MidiName document, it takes priority over any LV2 programs. #ifdef MIDNAM_SUPPORT if(!synthI_const()->midnamDocument().isEmpty()) { return getPatchNameMidNam(ch, prog, drum); } else #endif { uint32_t program = prog & 0xff; uint32_t lbank = (prog >> 8) & 0xff; uint32_t hbank = (prog >> 16) & 0xff; if (program > 127) // Map "dont care" to 0 program = 0; if (lbank > 127) lbank = 0; if (hbank > 127) hbank = 0; const uint32_t patch = (hbank << 16) | (lbank << 8) | program; std::map::iterator itPrg = _state->prg2index.find(patch); if(itPrg == _state->prg2index.end()) return QString("?"); uint32_t index = itPrg->second; std::map::iterator itIndex = _state->index2prg.find(index); if(itIndex == _state->index2prg.end()) return QString("?"); return QString(itIndex->second.name); } } void LV2SynthIF::guiHeartBeat() { //check for pending song dirty status if(_state->songDirtyPending) { MusEGlobal::song->setDirty(); _state->songDirtyPending = false; } LV2OperationMessage msg; const unsigned int sz = _state->operationsFifo.getSize(false); for(unsigned int i = 0; i < sz; ++i) { if(!_state->operationsFifo.get(msg)) { fprintf(stderr, "Operations FIFO underrun\n"); break; } switch(msg._type) { case LV2OperationMessage::ProgramChanged: // TODO: Not this. Compose an operation instead. // "When index is -1, host should reload all the programs." if(msg._index < 0) LV2Synth::lv2prg_updatePrograms(_state); else LV2Synth::lv2prg_updateProgram(_state, msg._index); MusEGlobal::song->update(SC_MIDI_INSTRUMENT); break; case LV2OperationMessage::MidnamUpdate: { #ifdef MIDNAM_SUPPORT // TODO: Not this. Compose an operation instead. LV2Synth::lv2midnam_updateMidnam(_state); // Since this is a notification from the synth to update midnam, // tell the rest of the app that the update happened. const int port = synti->midiPort(); if(port >= 0 && port < MIDI_PORTS) { PendingOperationList operations; operations.add(PendingOperationItem(&MusEGlobal::midiPorts[port], PendingOperationItem::UpdateDrumMaps)); MusEGlobal::audio->msgExecutePendingOperations(operations, true /* SC_MIDI_INSTRUMENT */); } #endif } break; } } } bool LV2SynthIF::hasGui() const { return true; } bool LV2SynthIF::hasNativeGui() const { return (_synth->_pluginUiTypes.size() > 0); } bool LV2SynthIF::nativeGuiVisible() const { if(_state != nullptr) { if(_state->hasExternalGui) { return (_state->widget != nullptr); } else if(_state->hasGui && _state->widget != nullptr) { return ((QWidget *)_state->widget)->isVisible(); } } return false; } #ifdef MIDNAM_SUPPORT void LV2SynthIF::populatePatchPopupMidNam(MusEGui::PopupMenu *menu, int channel, bool) { const MidiNamPatchBankList* pbl = synthI_const()->midnamDocument().getPatchBanks(channel); if(!pbl) return; std::map submenus; for(MidiNamPatchBankList::const_iterator ipbl = pbl->cbegin(); ipbl != pbl->cend(); ++ipbl) { const MidiNamPatchBank* pb = ipbl->second; const MidiNamPatchNameList& pnl = pb->patchNameList(); const int pb_bankHL = pb->bankHL(); const int pb_bankH = (pb_bankHL >> 8) & 0xff; const int pb_bankL = pb_bankHL & 0xff; for(MidiNamPatchNameList::const_iterator ipnl = pnl.cbegin(); ipnl != pnl.cend(); ++ipnl) { const MidiNamPatch* mnp = ipnl->second; const int mnp_patch = mnp->patchNumber(); // Replace with patch bank high and/or low if they are valid. const int mnp_bankH = pb_bankH == 0xff ? ((mnp_patch >> 16) & 0xff) : pb_bankH; const int mnp_bankL = pb_bankL == 0xff ? ((mnp_patch >> 8) & 0xff) : pb_bankL; const int mnp_prog = mnp_patch & 0xff; const int fin_mnp_bankHL = (mnp_bankH << 8) | mnp_bankL; const bool vhb = mnp_bankH != 0xff; const bool vlb = mnp_bankL != 0xff; const bool vpr = mnp_prog != 0xff; QString astr; if(vhb || vlb || vpr) { if(vhb) astr += QString::number(mnp_bankH + 1) + QString(":"); if(vlb) astr += QString::number(mnp_bankL + 1) + QString(":"); else if(vhb) astr += QString("--:"); if(vpr) astr += QString::number(mnp_prog + 1); else if(vhb && vlb) astr += QString("--"); astr += QString(" "); } astr += mnp->name(); std::map::iterator itS = submenus.find(fin_mnp_bankHL); MusEGui::PopupMenu *submenu= nullptr; if(itS != submenus.end()) { submenu = itS->second; } else { // Use the parent stayOpen here. submenu = new MusEGui::PopupMenu(menu, menu->stayOpen()); const QString& pb_name = pb->name(); const QString& pnl_name = pnl.name(); const QString fin_bank_name = pb_name.isEmpty() ? (pnl_name.isEmpty() ? (QString("Bank #") + QString::number(fin_mnp_bankHL + 1)) : pnl_name) : pb_name; submenu->setTitle(fin_bank_name); menu->addMenu(submenu); submenus.insert(std::make_pair(fin_mnp_bankHL, submenu)); } const int fin_mnp_patch = (fin_mnp_bankHL << 8) | mnp_prog; QAction *act = submenu->addAction(astr); act->setData(fin_mnp_patch); } } } #endif void LV2SynthIF::populatePatchPopup(MusEGui::PopupMenu *menu, int channel, bool drum) { //LV2Synth::lv2prg_updatePrograms(_state); menu->clear(); // Use the parent stayOpen here. MusEGui::PopupMenu *subMenuPrograms = new MusEGui::PopupMenu(menu, menu->stayOpen()); subMenuPrograms->setTitle(QObject::tr("Midi programs")); subMenuPrograms->setIcon(QIcon(*MusEGui::pianoNewIcon)); menu->addMenu(subMenuPrograms); // Use the parent stayOpen here. MusEGui::PopupMenu *subMenuPresets = new MusEGui::PopupMenu(menu, menu->stayOpen()); subMenuPresets->setTitle(QObject::tr("Presets")); menu->addMenu(subMenuPresets); //First: fill programs submenu // If there is a MidiName document, it takes priority over any LV2 programs. #ifdef MIDNAM_SUPPORT if(!synthI_const()->midnamDocument().isEmpty()) { populatePatchPopupMidNam(subMenuPrograms, channel, drum); } else #endif { std::map submenus; std::map::iterator itIndex; for(itIndex = _state->index2prg.begin(); itIndex != _state->index2prg.end(); ++itIndex) { const lv2ExtProgram &extPrg = itIndex->second; uint32_t hb = extPrg.bank >> 8; uint32_t lb = extPrg.bank & 0xff; // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. if(hb > 127 || lb > 127 || extPrg.prog > 127) continue; hb &= 0x7f; lb &= 0x7f; const uint32_t patch_bank = (hb << 8) | lb; const uint32_t patch = (patch_bank << 8) | extPrg.prog; QString astr; astr += QString::number(hb + 1) + QString(":"); astr += QString::number(lb + 1) + QString(":"); astr += QString::number(extPrg.prog + 1); astr += QString(" "); astr += QString(extPrg.name); std::map::iterator itS = submenus.find(patch_bank); MusEGui::PopupMenu *submenu= nullptr; if(itS != submenus.end()) { submenu = itS->second; } else { // Use the parent stayOpen here. submenu = new MusEGui::PopupMenu(subMenuPrograms, subMenuPrograms->stayOpen()); submenu->setTitle(QString("Bank #") + QString::number(extPrg.bank + 1)); subMenuPrograms->addMenu(submenu); submenus.insert(std::make_pair(patch_bank, submenu)); } QAction *act = submenu->addAction(astr); act->setData(patch); } } //Second:: Fill presets submenu LV2Synth::lv2state_populatePresetsMenu(_state, subMenuPresets); } MidiPlayEvent LV2SynthIF::receiveEvent() { return MidiPlayEvent(); } void LV2SynthIF::setNativeGeometry(int x, int y, int w, int h) { // Store the native geometry. SynthIF::setNativeGeometry(x, y, w, h); if(_state->pluginWindow && !_state->hasExternalGui) { //_state->pluginWindow->move(x, y); //don't resize lv2 uis - this is handled at plugin level //_uiState->pluginWindow->resize(w, h); #ifdef QT_SHOW_POS_BUG_WORKAROUND // Because of the bug, no matter what we must supply a position, // even upon first showing... // Check if there is an X11 gui size. if(w == 0) w = _state->uiX11Size.width(); if(h == 0) h = _state->uiX11Size.height(); // Check sane size. if(w == 0) w = _state->pluginWindow->sizeHint().width(); if(h == 0) h = _state->pluginWindow->sizeHint().height(); // No size hint? Try minimum size. if(w == 0) w = _state->pluginWindow->minimumSize().width(); if(h == 0) h = _state->pluginWindow->minimumSize().height(); // Fallback. if(w == 0) w = 400; if(h == 0) h = 300; _state->pluginWindow->setGeometry(x, y, w, h); #else // If the saved geometry is valid, use it. // Otherwise this is probably the first time showing, // so do not set a geometry - let Qt pick one // (using auto-placement and sizeHint). if(!(x == 0 && y == 0 && w == 0 && h == 0)) { // Check sane size. if(w == 0) w = _state->pluginWindow->sizeHint().width(); if(h == 0) h = _state->pluginWindow->sizeHint().height(); // No size hint? Try minimum size. if(w == 0) w = _state->pluginWindow->minimumSize().width(); if(h == 0) h = _state->pluginWindow->minimumSize().height(); // Fallback. if(w == 0) w = 400; if(h == 0) h = 300; _state->pluginWindow->setGeometry(x, y, w, h); } #endif } } void LV2SynthIF::setParameter(long unsigned int idx, double value) { addScheduledControlEvent(idx, value, MusEGlobal::audio->curFrame()); } void LV2SynthIF::showNativeGui(bool bShow) { if(track() != nullptr) { if(_state->human_id != nullptr) { free(_state->human_id); } _state->extHost.plugin_human_id = _state->human_id = strdup((track()->name() + QString(": ") + name()).toUtf8().constData()); } LV2Synth::lv2ui_ShowNativeGui(_state, bShow, cquirks().fixNativeUIScaling()); } void LV2SynthIF::write(int level, Xml &xml) const { LV2Synth::lv2conf_write(_state, level, xml); } void LV2SynthIF::setCustomData(const std::vector< QString > &customParams) { LV2Synth::lv2conf_set(_state, customParams); } double LV2SynthIF::param(long unsigned int i) const { return getParameter(i); } long unsigned int LV2SynthIF::parameters() const { return _inportsControl; } long unsigned int LV2SynthIF::parametersOut() const { return _outportsControl; } const char *LV2SynthIF::paramName(long unsigned int i) { return _controlInPorts [i].cName; } const char *LV2SynthIF::paramOutName(long unsigned int i) { return _controlOutPorts [i].cName; } CtrlEnumValues* LV2SynthIF::ctrlEnumValues(unsigned long i) const { if (i >= _inportsControl) return nullptr; return _controlInPorts [i].scalePoints; } QString LV2SynthIF::portGroup(long unsigned int i) const { if (i >= _inportsControl) return QString(); return _controlInPorts[i].group; } bool LV2SynthIF::ctrlIsTrigger(long unsigned int i) const { if (i >= _inportsControl) return false; return _controlInPorts[i].isTrigger; } bool LV2SynthIF::ctrlNotOnGui(long unsigned int i) const { if (i >= _inportsControl) return false; return _controlInPorts[i].notOnGui; } CtrlValueType LV2SynthIF::ctrlValueType(unsigned long i) const { CtrlValueType vt = VAL_LINEAR; assert(i < _inportsControl); switch(_synth->_controlInPorts [i].cType) { case LV2_PORT_CONTINUOUS: vt = VAL_LINEAR; break; case LV2_PORT_DISCRETE: case LV2_PORT_INTEGER: vt = VAL_INT; break; case LV2_PORT_LOGARITHMIC: vt = VAL_LOG; break; case LV2_PORT_TOGGLE: vt = VAL_BOOL; break; case LV2_PORT_ENUMERATION: vt = VAL_ENUM; break; default: break; } return vt; } CtrlList::Mode LV2SynthIF::ctrlMode(unsigned long i) const { assert(i < _inportsControl); return ((_synth->_controlInPorts [i].cType == LV2_PORT_CONTINUOUS) ||(_synth->_controlInPorts [i].cType == LV2_PORT_LOGARITHMIC)) ? CtrlList::INTERPOLATE : CtrlList::DISCRETE; } LADSPA_PortRangeHint LV2SynthIF::range(unsigned long i) { assert(i < _inportsControl); LADSPA_PortRangeHint hint; hint.HintDescriptor = 0; hint.LowerBound = _controlInPorts [i].minVal; hint.UpperBound = _controlInPorts [i].maxVal; if(hint.LowerBound == hint.LowerBound) { hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; } if(hint.UpperBound == hint.UpperBound) { hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; } return hint; } LADSPA_PortRangeHint LV2SynthIF::rangeOut(unsigned long i) { assert(i < _outportsControl); LADSPA_PortRangeHint hint; hint.HintDescriptor = 0; hint.LowerBound = _controlOutPorts [i].minVal; hint.UpperBound = _controlOutPorts [i].maxVal; if(hint.LowerBound == hint.LowerBound) { hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; } if(hint.UpperBound == hint.UpperBound) { hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; } return hint; } inline bool LV2SynthIF::hasLatencyOutPort() const { return _synth->_hasLatencyPort; } inline unsigned long LV2SynthIF::latencyOutPortIndex() const { return _synth->_latencyPortIndex; } inline bool LV2SynthIF::usesTransportSource() const { return _synth->usesTimePosition(); } //--------------------------------------------------------- // latency //--------------------------------------------------------- float LV2SynthIF::latency() const { // Do not report any latency if the plugin is not on. if(!on()) return 0.0; if(cquirks()._overrideReportedLatency) return cquirks()._latencyOverrideValue; if(!hasLatencyOutPort()) return 0.0; return _controlsOut[latencyOutPortIndex()].val; } double LV2SynthIF::paramOut(long unsigned int i) const { return getParameterOut(i); } void LV2SynthIF::setParam(long unsigned int i, double val) { setParameter(i, val); } void LV2SynthIF::enableController(unsigned long i, bool v) { _controls[i].enCtrl = v; } bool LV2SynthIF::controllerEnabled(unsigned long i) const { return _controls[i].enCtrl; } void LV2SynthIF::enableAllControllers(bool v) { if(!_synth) return; for(unsigned long i = 0; i < _inportsControl; ++i) _controls[i].enCtrl = v; } void LV2SynthIF::updateControllers() { } void LV2SynthIF::populatePresetsMenu(MusEGui::PopupMenu *menu) { LV2Synth::lv2state_populatePresetsMenu(_state, menu); } void LV2SynthIF::applyPreset(void *preset) { LV2Synth::lv2state_applyPreset(_state, static_cast(preset)); } void LV2SynthIF::writeConfiguration(int level, Xml &xml) { MusECore::SynthIF::writeConfiguration(level, xml); } bool LV2SynthIF::readConfiguration(Xml &xml, bool readPreset) { return MusECore::SynthIF::readConfiguration(xml, readPreset); } void LV2PluginWrapper_Window::hideEvent(QHideEvent *e) { if(_state->plugInst != nullptr) _state->plugInst->saveNativeGeometry(geometry().x(), geometry().y(), geometry().width(), geometry().height()); else if(_state->sif != nullptr) _state->sif->saveNativeGeometry(geometry().x(), geometry().y(), geometry().width(), geometry().height()); e->ignore(); QMainWindow::hideEvent(e); } void LV2PluginWrapper_Window::showEvent(QShowEvent *e) { int x = 0, y = 0, w = 0, h = 0; if(_state->plugInst != nullptr) _state->plugInst->savedNativeGeometry(&x, &y, &w, &h); else if(_state->sif != nullptr) _state->sif->savedNativeGeometry(&x, &y, &w, &h); #ifdef QT_SHOW_POS_BUG_WORKAROUND // Because of the bug, no matter what we must supply a position, // even upon first showing... // Check if there is an X11 gui size. if(w == 0) w = _state->uiX11Size.width(); if(h == 0) h = _state->uiX11Size.height(); // Check sane size. if(w == 0) w = sizeHint().width(); if(h == 0) h = sizeHint().height(); // No size hint? Try minimum size. if(w == 0) w = minimumSize().width(); if(h == 0) h = minimumSize().height(); // Fallback. if(w == 0) w = 400; if(h == 0) h = 300; setGeometry(x, y, w, h); #else // If the saved geometry is valid, use it. // Otherwise this is probably the first time showing, // so do not set a geometry - let Qt pick one // (using auto-placement and sizeHint). if(!(x == 0 && y == 0 && w == 0 && h == 0)) { // Check sane size. if(w == 0) w = sizeHint().width(); if(h == 0) h = sizeHint().height(); // No size hint? Try minimum size. if(w == 0) w = minimumSize().width(); if(h == 0) h = minimumSize().height(); // Fallback. if(w == 0) w = 400; if(h == 0) h = 300; setGeometry(x, y, w, h); } #endif // Convenience: If the window was minimized, restore it. if(isMinimized()) setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); e->ignore(); QMainWindow::showEvent(e); } void LV2PluginWrapper_Window::closeEvent(QCloseEvent *event) { assert(_state != nullptr); event->accept(); stopUpdateTimer(); //if(_state->gtk2Plug != nullptr) //{ if(_state->pluginQWindow != nullptr) { _state->pluginQWindow->setParent(nullptr); delete _state->pluginQWindow; _state->pluginQWindow = nullptr; } //} if(_state->deleteLater) { LV2Synth::lv2state_FreeState(_state); } else { //_state->uiTimer->stopNextTime(false); _state->widget = nullptr; _state->pluginWindow = nullptr; _state->uiDoSelectPrg = false; _state->uiPrgIface = nullptr; LV2Synth::lv2ui_FreeDescriptors(_state); } // Reset the flag, just to be sure. _state->uiIsOpening = false; // The widget is automatically deleted by use of the // WA_DeleteOnClose attribute in the constructor. } void LV2PluginWrapper_Window::stopUpdateTimer() { if(updateTimer.isActive()) updateTimer.stop(); while(updateTimer.isActive()) { QCoreApplication::processEvents(); } } #ifdef LV2_GUI_USE_QWIDGET LV2PluginWrapper_Window::LV2PluginWrapper_Window(LV2PluginWrapper_State *state, QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags), _state ( state ), _closing(false) #else LV2PluginWrapper_Window::LV2PluginWrapper_Window(LV2PluginWrapper_State *state, QWidget *parent, Qt::WindowFlags flags) : QMainWindow(parent, flags), _state ( state ), _closing(false) #endif { // Automatically delete the widget when it closes. setAttribute(Qt::WA_DeleteOnClose, true); connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateGui())); connect(this, SIGNAL(makeStopFromGuiThread()), this, SLOT(stopFromGuiThread())); connect(this, SIGNAL(makeStartFromGuiThread()), this, SLOT(startFromGuiThread())); } LV2PluginWrapper_Window::~LV2PluginWrapper_Window() { #ifdef DEBUG_LV2 std::cout << "LV2PluginWrapper_Window::~LV2PluginWrapper_Window()" << std::endl; #endif } void LV2PluginWrapper_Window::startNextTime() { emit makeStartFromGuiThread(); } void LV2PluginWrapper_Window::stopNextTime() { setClosing(true); emit makeStopFromGuiThread(); } void LV2PluginWrapper_Window::updateGui() { if(_state->deleteLater || _closing) { stopNextTime(); return; } LV2Synth::lv2ui_SendChangedControls(_state); // REMOVE Tim. lv2. Changed. 2019/02/21 TESTING. // It was a bit of a hack for one particular synth if I recall. Removed, seems OK now. // // //send program change if any // // Force send if re-opening. // if(_state->uiIsOpening || _state->uiDoSelectPrg) // { // _state->uiDoSelectPrg = false; // if(_state->uiPrgIface != nullptr && (_state->uiPrgIface->select_program != nullptr || _state->uiPrgIface->select_program_for_channel != nullptr)) // { // if(_state->newPrgIface) // _state->uiPrgIface->select_program_for_channel(lilv_instance_get_handle(_state->handle), _state->uiChannel, (uint32_t)_state->uiBank, (uint32_t)_state->uiProg); // else // _state->uiPrgIface->select_program(lilv_instance_get_handle(_state->handle), (uint32_t)_state->uiBank, (uint32_t)_state->uiProg); // } // } //send program change if any // Force send if re-opening. if(_state->uiDoSelectPrg) { _state->uiDoSelectPrg = false; if(_state->uiPrgIface != nullptr && (_state->uiPrgIface->select_program != nullptr || _state->uiPrgIface->select_program_for_channel != nullptr)) { if(_state->newPrgIface) _state->uiPrgIface->select_program_for_channel(lilv_instance_get_handle(_state->handle), _state->uiChannel, (uint32_t)_state->uiBank, (uint32_t)_state->uiProg); else _state->uiPrgIface->select_program(lilv_instance_get_handle(_state->handle), (uint32_t)_state->uiBank, (uint32_t)_state->uiProg); } } // Reset the flag. _state->uiIsOpening = false; //call ui idle callback if any if(_state->uiIdleIface != nullptr) { int iRet = _state->uiIdleIface->idle(_state->uiInst); if(iRet != 0) // ui don't want us to call it's idle callback any more _state->uiIdleIface = nullptr; } if(_state->hasExternalGui) { LV2_EXTERNAL_UI_RUN((LV2_External_UI_Widget *)_state->widget); } //if(_closing) //stopNextTime(); } void LV2PluginWrapper_Window::stopFromGuiThread() { stopUpdateTimer(); emit close(); } void LV2PluginWrapper_Window::startFromGuiThread() { stopUpdateTimer(); updateTimer.start(1000/30); } LV2PluginWrapper::LV2PluginWrapper(LV2Synth *s, PluginFeatures_t reqFeatures) { _synth = s; _requiredFeatures = reqFeatures; _fakeLd.Label = strdup(_synth->name().toUtf8().constData()); _fakeLd.Name = strdup(_synth->name().toUtf8().constData()); // LV2 does not use unique id numbers and frowns upon using anything but the uri. // _fakeLd.UniqueID = _synth->_uniqueID; _fakeLd.UniqueID = 0; _fakeLd.Maker = strdup(_synth->maker().toUtf8().constData()); _fakeLd.Copyright = strdup(_synth->version().toUtf8().constData()); _isLV2Plugin = true; _isLV2Synth = s->_isSynth; int numPorts = _synth->_audioInPorts.size() + _synth->_audioOutPorts.size() + _synth->_controlInPorts.size() + _synth->_controlOutPorts.size() + _synth->_midiInPorts.size() + _synth->_midiOutPorts.size(); _fakeLd.PortCount = numPorts; _fakePds = new LADSPA_PortDescriptor [numPorts]; memset(_fakePds, 0, sizeof(int) * numPorts); for(size_t i = 0; i < _synth->_audioInPorts.size(); i++) { _fakePds [_synth->_audioInPorts [i].index] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; } for(size_t i = 0; i < _synth->_audioOutPorts.size(); i++) { _fakePds [_synth->_audioOutPorts [i].index] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; } for(size_t i = 0; i < _synth->_controlInPorts.size(); i++) { _fakePds [_synth->_controlInPorts [i].index] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; } for(size_t i = 0; i < _synth->_controlOutPorts.size(); i++) { _fakePds [_synth->_controlOutPorts [i].index] = LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL; } _fakeLd.PortNames = nullptr; _fakeLd.PortRangeHints = nullptr; _fakeLd.PortDescriptors = _fakePds; _fakeLd.Properties = 0; plugin = &_fakeLd; _isDssi = false; _isDssiSynth = false; _isVstNativePlugin = false; _isVstNativeSynth = false; #ifdef DSSI_SUPPORT dssi_descr = nullptr; #endif fi = _synth->info; _uri = _synth->uri(); ladspa = nullptr; _handle = 0; _references = 0; _instNo = 0; _label = _synth->name(); _name = _synth->description(); _uniqueID = plugin->UniqueID; _maker = _synth->maker(); _copyright = _synth->version(); _usesTimePosition = _synth->usesTimePosition(); _portCount = plugin->PortCount; _inports = 0; _outports = 0; _controlInPorts = 0; _controlOutPorts = 0; for(unsigned long k = 0; k < _portCount; ++k) { LADSPA_PortDescriptor pd = plugin->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) { if(pd & LADSPA_PORT_INPUT) { ++_inports; } else if(pd & LADSPA_PORT_OUTPUT) { ++_outports; } } else if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) { ++_controlInPorts; } else if(pd & LADSPA_PORT_OUTPUT) { ++_controlOutPorts; } } } } LV2PluginWrapper::~LV2PluginWrapper() { free((void*)_fakeLd.Label); free((void*)_fakeLd.Name); free((void*)_fakeLd.Maker); free((void*)_fakeLd.Copyright); delete [] _fakePds; } LADSPA_Handle LV2PluginWrapper::instantiate(PluginI *plugi) { LV2PluginWrapper_State *state = new LV2PluginWrapper_State; state->inst = this; state->widget = nullptr; state->uiInst = nullptr; state->plugInst = plugi; state->_ifeatures = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; state->_ppifeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; state->sif = nullptr; state->synth = _synth; state->wrkDataBuffer = new LockFreeDataRingBuffer(LV2_WRK_FIFO_SIZE); state->wrkRespDataBuffer = new LockFreeDataRingBuffer(LV2_WRK_FIFO_SIZE); LV2Synth::lv2state_FillFeatures(state); state->handle = lilv_plugin_instantiate(_synth->_handle, (double)MusEGlobal::sampleRate, state->_ppifeatures); if(state->handle == nullptr) { delete [] state->_ppifeatures; delete [] state->_ifeatures; return nullptr; } //_states.insert(std::pair(state->handle, state)); LV2Synth::lv2state_PostInstantiate(state); return (LADSPA_Handle)state; } void LV2PluginWrapper::connectPort(LADSPA_Handle handle, long unsigned int port, float *value) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; lilv_instance_connect_port((LilvInstance *)state->handle, port, (void *)value); } int LV2PluginWrapper::incReferences(int ref) { _synth->incInstances(ref); return _synth->instances(); } void LV2PluginWrapper::activate(LADSPA_Handle handle) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; lilv_instance_activate((LilvInstance *) state->handle); } void LV2PluginWrapper::deactivate(LADSPA_Handle handle) { if (handle) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; lilv_instance_deactivate((LilvInstance *) state->handle); } } void LV2PluginWrapper::cleanup(LADSPA_Handle handle) { if(handle != nullptr) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; state->deleteLater = true; if(state->pluginWindow != nullptr) state->pluginWindow->stopNextTime(); else LV2Synth::lv2state_FreeState(state); } } void LV2PluginWrapper::apply(LADSPA_Handle handle, unsigned long n, float latency_corr) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; LV2Synth::lv2audio_preProcessMidiPorts(state, 0, n); // Send transport events if any // FIXME These need to be mixed and serialized with any other events we might send! // For now, SendTransport is not sliceable, it only sends one event at sample zero. LV2Synth::lv2audio_SendTransport(state, 0, n, latency_corr); //set freewheeling property if plugin supports it if(state->synth->_hasFreeWheelPort) { state->plugInst->controls[_synth->_freeWheelPortIndex].val = MusEGlobal::audio->freewheel() ? 1.0f : 0.0f; } for(size_t j = 0; j < state->plugInst->controlPorts; ++j) { uint32_t idx = state->synth->_controlInPorts [j].index; if(state->pluginCVPorts [idx] != nullptr) { float cvVal = state->plugInst->controls [j].val; for(size_t jj = 0; jj < n; ++jj) { state->pluginCVPorts [idx] [jj] = cvVal; } lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); } } for(size_t j = 0; j < state->plugInst->controlOutPorts; ++j) { uint32_t idx = state->synth->_controlOutPorts [j].index; if(state->pluginCVPorts [idx] != nullptr) { float cvVal = state->plugInst->controlsOut [j].val; for(size_t jj = 0; jj < n; ++jj) { state->pluginCVPorts [idx] [jj] = cvVal; } lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); } } lilv_instance_run(state->handle, n); //notify worker about processed data (if any) // Do it always, even if not 'running', in case we have 'left over' responses etc, // even though work is only initiated from 'run'. const unsigned int rsp_buf_sz = state->wrkRespDataBuffer->getSize(false); for(unsigned int i_sz = 0; i_sz < rsp_buf_sz; ++i_sz) { if(state->wrkIface && state->wrkIface->work_response) { void *wrk_data = nullptr; size_t wrk_data_sz = 0; if(state->wrkRespDataBuffer->peek(&wrk_data, &wrk_data_sz)) state->wrkIface->work_response(lilv_instance_get_handle(state->handle), wrk_data_sz, wrk_data); } state->wrkRespDataBuffer->remove(); } //notify worker that this run() finished if(state->wrkIface && state->wrkIface->end_run) state->wrkIface->end_run(lilv_instance_get_handle(state->handle)); LV2Synth::lv2audio_postProcessMidiPorts(state, 0, n); } LADSPA_PortDescriptor LV2PluginWrapper::portd(unsigned long k) const { return _fakeLd.PortDescriptors[k]; } LADSPA_PortRangeHint LV2PluginWrapper::range(unsigned long i) { LADSPA_PortRangeHint hint; hint.HintDescriptor = 0; hint.LowerBound = _synth->_pluginControlsMin [i]; hint.UpperBound = _synth->_pluginControlsMax [i]; if(hint.LowerBound == hint.LowerBound) { hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; } if(hint.UpperBound == hint.UpperBound) { hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; } return hint; } void LV2PluginWrapper::range(unsigned long i, float *min, float *max) const { *min = _synth->_pluginControlsMin [i]; *max = _synth->_pluginControlsMax [i]; } double LV2PluginWrapper::defaultValue(unsigned long port) const { return _synth->_pluginControlsDefault [port]; } const char *LV2PluginWrapper::portName(unsigned long i) { return lilv_node_as_string(lilv_port_get_name(_synth->_handle, lilv_plugin_get_port_by_index(_synth->_handle, i))); } CtrlEnumValues* LV2PluginWrapper::ctrlEnumValues(unsigned long i) const { std::map::iterator it = _synth->_idxToControlMap.find(i); assert(it != _synth->_idxToControlMap.end()); i = it->second; assert(i < _controlInPorts); return _synth->_controlInPorts [i].scalePoints; } CtrlValueType LV2PluginWrapper::ctrlValueType(unsigned long i) const { CtrlValueType vt = VAL_LINEAR; std::map::iterator it = _synth->_idxToControlMap.find(i); assert(it != _synth->_idxToControlMap.end()); i = it->second; assert(i < _controlInPorts); switch(_synth->_controlInPorts [i].cType) { case LV2_PORT_CONTINUOUS: vt = VAL_LINEAR; break; case LV2_PORT_DISCRETE: case LV2_PORT_INTEGER: vt = VAL_INT; break; case LV2_PORT_LOGARITHMIC: vt = VAL_LOG; break; case LV2_PORT_TOGGLE: vt = VAL_BOOL; break; case LV2_PORT_ENUMERATION: vt = VAL_ENUM; break; default: break; } return vt; } CtrlList::Mode LV2PluginWrapper::ctrlMode(unsigned long i) const { std::map::iterator it = _synth->_idxToControlMap.find(i); assert(it != _synth->_idxToControlMap.end()); i = it->second; assert(i < _controlInPorts); return ((_synth->_controlInPorts [i].cType == LV2_PORT_CONTINUOUS) ||(_synth->_controlInPorts [i].cType == LV2_PORT_LOGARITHMIC)) ? CtrlList::INTERPOLATE : CtrlList::DISCRETE; } bool LV2PluginWrapper::hasNativeGui() const { return (_synth->_pluginUiTypes.size() > 0); } void LV2PluginWrapper::showNativeGui(PluginI *p, bool bShow) { assert(p->instances > 0); LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; if(p->track() != nullptr) { if(state->human_id != nullptr) { free(state->human_id); } state->extHost.plugin_human_id = state->human_id = strdup((p->track()->name() + QString(": ") + label()).toUtf8().constData()); } LV2Synth::lv2ui_ShowNativeGui(state, bShow, p->cquirks().fixNativeUIScaling()); } bool LV2PluginWrapper::nativeGuiVisible(const PluginI *p) const { assert(p->instances > 0); LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; return (state->widget != nullptr); } void LV2PluginWrapper::setLastStateControls(LADSPA_Handle handle, size_t index, bool bSetMask, bool bSetVal, bool bMask, float fVal) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; assert(state != nullptr); if(_controlInPorts == 0) return; if(bSetMask) state->controlsMask [index] = bMask; if(bSetVal) state->lastControls [index] = fVal; } void LV2PluginWrapper::writeConfiguration(LADSPA_Handle handle, int level, Xml &xml) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; assert(state != nullptr); LV2Synth::lv2conf_write(state, level, xml); } void LV2PluginWrapper::setCustomData(LADSPA_Handle handle, const std::vector &customParams) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; assert(state != nullptr); LV2Synth::lv2conf_set(state, customParams); } void LV2PluginWrapper::populatePresetsMenu(PluginI *p, MusEGui::PopupMenu *menu) { assert(p->instances > 0); LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; assert(state != nullptr); LV2Synth::lv2state_populatePresetsMenu(state, menu); } void LV2PluginWrapper::applyPreset(PluginI *p, void *preset) { assert(p->instances > 0); LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; assert(state != nullptr); LV2Synth::lv2state_applyPreset(state, static_cast(preset)); } void LV2PluginWrapper_Worker::run() { while(true) { _mSem.acquire(1); if(_closing) break; makeWork(); } } LV2_Worker_Status LV2PluginWrapper_Worker::scheduleWork() { if(_mSem.available() == 0) _mSem.release(1); return LV2_WORKER_SUCCESS; } void LV2PluginWrapper_Worker::makeWork() { #ifdef DEBUG_LV2 std::cerr << "LV2PluginWrapper_Worker::makeWork" << std::endl; #endif const unsigned int wrk_buf_sz = _state->wrkDataBuffer->getSize(false); for(unsigned int i_sz = 0; i_sz < wrk_buf_sz; ++i_sz) { if(_state->wrkIface && _state->wrkIface->work) { void *wrk_data = nullptr; size_t wrk_data_sz = 0; if(_state->wrkDataBuffer->peek(&wrk_data, &wrk_data_sz)) { // Some plugins expose a work_response function, but it may be empty // and/or they don't bother calling respond (our lv2wrk_respond). (LSP...) if(_state->wrkIface->work(lilv_instance_get_handle(_state->handle), LV2Synth::lv2wrk_respond, _state, wrk_data_sz, wrk_data) != LV2_WORKER_SUCCESS) { #ifdef DEBUG_LV2 std::cerr << "LV2PluginWrapper_Worker::makeWork: Error: work() != LV2_WORKER_SUCCESS" << std::endl; #endif } } } _state->wrkDataBuffer->remove(); } } #ifdef LV2_EVENT_BUFFER_SUPPORT LV2EvBuf::LV2EvBuf(bool isInput, bool oldApi, LV2_URID atomTypeSequence, LV2_URID atomTypeChunk, size_t /*size*/) :_isInput(isInput), _oldApi(oldApi), _uAtomTypeSequence(atomTypeSequence), _uAtomTypeChunk(atomTypeChunk) #else LV2EvBuf::LV2EvBuf(bool isInput, LV2_URID atomTypeSequence, LV2_URID atomTypeChunk, size_t /*size*/) :_isInput(isInput), _uAtomTypeSequence(atomTypeSequence), _uAtomTypeChunk(atomTypeChunk) #endif { // Resize and fill with initial value. _buffer.resize(LV2_EVBUF_SIZE, 0); #ifdef LV2_DEBUG std::cerr << "LV2EvBuf ctor: _buffer size:" << _buffer.size() << " capacity:" << _buffer.capacity() << std::endl; #endif resetBuffer(); } size_t LV2EvBuf::mkPadSize(size_t size) { return (size + 7U) & (~7U); } void LV2EvBuf::resetPointers(bool r, bool w) { if(!r && !w) return; size_t ptr = 0; #ifdef LV2_EVENT_BUFFER_SUPPORT if(_oldApi) { ptr = sizeof(LV2_Event_Buffer); } else #endif { ptr = sizeof(LV2_Atom_Sequence); } if(r) { curRPointer = ptr; } if(w) { curWPointer = ptr; } } void LV2EvBuf::resetBuffer() { #ifdef LV2_EVENT_BUFFER_SUPPORT if(_oldApi) { _evbuf = reinterpret_cast(&_buffer [0]); _evbuf->capacity = _buffer.size() - sizeof(LV2_Event_Buffer); _evbuf->data = reinterpret_cast(_evbuf + 1); _evbuf->event_count = 0; _evbuf->header_size = sizeof(LV2_Event_Buffer); _evbuf->stamp_type = LV2_EVENT_AUDIO_STAMP; _evbuf->size = 0; } else #endif { _seqbuf = reinterpret_cast(&_buffer [0]); if(!_isInput) { _seqbuf->atom.type = _uAtomTypeChunk; _seqbuf->atom.size = _buffer.size() - sizeof(LV2_Atom_Sequence); } else { _seqbuf->atom.type = _uAtomTypeSequence; _seqbuf->atom.size = sizeof(LV2_Atom_Sequence_Body); } _seqbuf->body.unit = 0; _seqbuf->body.pad = 0; } resetPointers(true, true); } #ifdef LV2_EVENT_BUFFER_SUPPORT bool LV2EvBuf::write(uint32_t frames, uint32_t subframes, uint32_t type, uint32_t size, const uint8_t *data) #else bool LV2EvBuf::write(uint32_t frames, uint32_t type, uint32_t size, const uint8_t *data) #endif { if(!_isInput) return false; #ifdef LV2_EVENT_BUFFER_SUPPORT if(_oldApi) { size_t paddedSize = mkPadSize(sizeof(LV2_Event) + size); size_t resSize = curWPointer + paddedSize; if(resSize > _buffer.size()) { std::cerr << "LV2 Event buffer overflow! frames=" << frames << ", size=" << size << std::endl; return false; } LV2_Event *ev = reinterpret_cast(&_buffer [curWPointer]); ev->frames = frames; ev->subframes = subframes; ev->type = type; ev->size = size; memcpy((void *)(ev + 1), data, size); curWPointer += paddedSize; _evbuf->size += paddedSize; _evbuf->event_count++; } else #endif { size_t paddedSize = mkPadSize(sizeof(LV2_Atom_Event) + size); size_t resSize = curWPointer + paddedSize; if(resSize > _buffer.size()) { std::cerr << "LV2 Atom_Event buffer overflow! frames=" << frames << ", size=" << size << std::endl; return false; } LV2_Atom_Event *ev = reinterpret_cast(&_buffer [curWPointer]); ev->time.frames = frames; ev->body.size = size; ev->body.type = type; memcpy(LV2_ATOM_BODY(&ev->body), data, size); _seqbuf->atom.size += paddedSize; curWPointer += paddedSize; } return true; } #ifdef LV2_EVENT_BUFFER_SUPPORT bool LV2EvBuf::read(uint32_t *frames, uint32_t *subframes, uint32_t *type, uint32_t *size, uint8_t **data) #else bool LV2EvBuf::read(uint32_t *frames, uint32_t *type, uint32_t *size, uint8_t **data) #endif { *frames = *type = *size = 0; #ifdef LV2_EVENT_BUFFER_SUPPORT *subframes = 0; #endif *data = nullptr; if(_isInput) return false; #ifdef LV2_EVENT_BUFFER_SUPPORT if(_oldApi) { LV2_Event *ev = reinterpret_cast(&_buffer [curRPointer]); if((_evbuf->size + sizeof(LV2_Event_Buffer) - curRPointer) < sizeof(LV2_Event)) { return false; } *frames = ev->frames; *subframes = ev->subframes; *type = ev->type; *size = ev->size; *data = reinterpret_cast(ev + 1); size_t padSize = mkPadSize(sizeof(LV2_Event) + ev->size); curRPointer += padSize; } else #endif { LV2_Atom_Event *ev = reinterpret_cast(&_buffer [curRPointer]); if((_seqbuf->atom.size + sizeof(LV2_Atom_Sequence) - curRPointer) < sizeof(LV2_Atom_Event)) { return false; } // Apparently some plugins fill in the buffer and/or atom.size, some don't. if(ev->body.size == 0) return false; DEBUG_LV2_PROCESS(stderr, "LV2EvBuf::read ev frames:%ld type:%d, size:%d\n", ev->time.frames, ev->body.type, ev->body.size); *frames = ev->time.frames; *type = ev->body.type; *size = ev->body.size; *data = reinterpret_cast(LV2_ATOM_BODY(&ev->body)); size_t padSize = mkPadSize(sizeof(LV2_Atom_Event) + ev->body.size); curRPointer += padSize; } return true; } uint8_t *LV2EvBuf::getRawBuffer() { return &_buffer [0]; } void LV2EvBuf::dump() { #ifdef LV2_EVENT_BUFFER_SUPPORT if(_oldApi) { return; } #endif int n = 1; LV2_Atom_Sequence *b = (LV2_Atom_Sequence *)&_buffer [0]; LV2_ATOM_SEQUENCE_FOREACH(b, s) { if(n == 1) { fprintf(stderr, "-------------- Atom seq dump START---------------\n"); } fprintf(stderr, "\tSeq. no.: %d\n", n); fprintf(stderr, "\t\tFrames: %ld\n", (long)s->time.frames); fprintf(stderr, "\t\tSize: %d\n", s->body.size); fprintf(stderr, "\t\tType: %d\n", s->body.type); fprintf(stderr, "\t\tData (hex):\n"); uint8_t *d = (uint8_t *)LV2_ATOM_BODY(&s->body); for(uint32_t i = 0; i < s->body.size; i++) { if((i % 10) == 0) { fprintf(stderr, "\n\t\t"); } else { fprintf(stderr, " "); } fprintf(stderr, "0x%02X", d [i]); } fprintf(stderr, "\n"); n++; } if(n > 1) { fprintf(stderr, "-------------- Atom seq dump END---------------\n\n"); } } LV2SimpleRTFifo::LV2SimpleRTFifo(size_t size): fifoSize(size), itemSize(LV2_RT_FIFO_ITEM_SIZE) { eventsBuffer.resize(fifoSize); assert(eventsBuffer.size() == fifoSize); readIndex = writeIndex = 0; for(size_t i = 0; i < fifoSize; ++i) { eventsBuffer [i].port_index = 0; eventsBuffer [i].buffer_size = 0; eventsBuffer [i].data = new char [itemSize]; } } LV2SimpleRTFifo::~LV2SimpleRTFifo() { for(size_t i = 0; i < fifoSize; ++i) { delete [] eventsBuffer [i].data; } } bool LV2SimpleRTFifo::put(uint32_t port_index, uint32_t size, const void *data) { if(size > itemSize) { #ifdef DEBUG_LV2 std::cerr << "LV2SimpleRTFifo:put(): size("< 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) // # pragma GCC diagnostic push // # pragma GCC diagnostic warning "-Wparentheses" // #endif #include "lilv/lilv.h" // #if defined(__clang__) // # pragma clang diagnostic pop // #elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) // # pragma GCC diagnostic pop // #endif #include "lv2/lv2plug.in/ns/ext/data-access/data-access.h" #include "lv2/lv2plug.in/ns/ext/state/state.h" #include "lv2/lv2plug.in/ns/ext/atom/atom.h" #include "lv2/lv2plug.in/ns/ext/midi/midi.h" #include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h" #ifdef LV2_EVENT_BUFFER_SUPPORT #include "lv2/lv2plug.in/ns/ext/event/event.h" #endif #include "lv2/lv2plug.in/ns/ext/options/options.h" #include "lv2/lv2plug.in/ns/ext/parameters/parameters.h" #include "lv2/lv2plug.in/ns/ext/patch/patch.h" #include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h" #include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/state/state.h" #include "lv2/lv2plug.in/ns/ext/time/time.h" #ifdef LV2_URI_MAP_SUPPORT #include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h" #endif #include "lv2/lv2plug.in/ns/ext/urid/urid.h" #include "lv2/lv2plug.in/ns/ext/worker/worker.h" #include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" #include "lv2/lv2plug.in/ns/ext/atom/forge.h" #include "lv2/lv2plug.in/ns/ext/log/log.h" #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #include "lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h" #include "lv2/lv2plug.in/ns/ext/resize-port/resize-port.h" #include "lv2extui.h" #include "lv2extprg.h" #ifdef MIDNAM_SUPPORT #include "midnam_lv2.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "globaldefs.h" #include "midictrl.h" #include "synth.h" #include "stringparam.h" #include "plugin.h" #include "plugin_list.h" #include "lock_free_buffer.h" #include "lock_free_data_buffer.h" #endif // Define to use QWidget instead of QMainWindow for the plugin gui container. // #define LV2_GUI_USE_QWIDGET ; namespace MusECore { #ifdef LV2_SUPPORT class LV2Synth; #define LV2_RT_FIFO_SIZE 128 #define LV2_RT_FIFO_ITEM_SIZE (std::max(size_t(4096 * 16), size_t(MusEGlobal::segmentSize * 16))) #define LV2_EVBUF_SIZE (2*LV2_RT_FIFO_ITEM_SIZE) #define OPERATIONS_FIFO_SIZE 256 // ( std::min( std::max(size_t(256), size_t(MusEGlobal::segmentSize * 16)), size_t(1024)) ) struct LV2MidiEvent { int64_t frame; uint8_t midi [4]; }; class LV2EvBuf { #ifdef LV2_EVENT_BUFFER_SUPPORT enum LV2_BUF_TYPE { LV2_BUF_EVENT, LV2_BUF_ATOM }; #endif std::vector _buffer; size_t curWPointer; size_t curRPointer; bool _isInput; #ifdef LV2_EVENT_BUFFER_SUPPORT bool _oldApi; #endif LV2_URID _uAtomTypeSequence; LV2_URID _uAtomTypeChunk; LV2_Atom_Sequence *_seqbuf; #ifdef LV2_EVENT_BUFFER_SUPPORT LV2_Event_Buffer *_evbuf; #endif public: #ifdef LV2_EVENT_BUFFER_SUPPORT LV2EvBuf(bool isInput, bool oldApi, LV2_URID atomTypeSequence, LV2_URID atomTypeChunk, size_t size); #else LV2EvBuf(bool isInput, LV2_URID atomTypeSequence, LV2_URID atomTypeChunk, size_t size); #endif inline size_t mkPadSize(size_t size); inline void resetPointers(bool r, bool w); inline void resetBuffer(); #ifdef LV2_EVENT_BUFFER_SUPPORT bool write(uint32_t frames, uint32_t subframes, uint32_t type, uint32_t size, const uint8_t *data); bool read(uint32_t *frames, uint32_t *subframes, uint32_t *type, uint32_t *size, uint8_t **data ); #else bool write(uint32_t frames, uint32_t type, uint32_t size, const uint8_t *data); bool read(uint32_t *frames, uint32_t *type, uint32_t *size, uint8_t **data ); #endif uint8_t *getRawBuffer(); void dump(); }; class LV2SimpleRTFifo { public: typedef struct _lv2_uiControlEvent { uint32_t port_index; long buffer_size; char *data; } lv2_uiControlEvent; private: //size_t numItems; std::vector eventsBuffer; size_t readIndex; size_t writeIndex; size_t fifoSize; size_t itemSize; public: LV2SimpleRTFifo(size_t size); ~LV2SimpleRTFifo(); inline size_t getItemSize(){return itemSize; } bool put(uint32_t port_index, uint32_t size, const void *data); bool get(uint32_t *port_index, size_t *szOut, char *data_out); }; typedef struct _lv2ExtProgram { uint32_t index; uint32_t bank; uint32_t prog; QString name; bool useIndex; bool operator<(const _lv2ExtProgram& other) const { if(useIndex == other.useIndex && useIndex == true) return index < other.index; if(bank < other.bank) return true; else if(bank == other.bank && prog < other.prog) return true; return false; } bool operator==(const _lv2ExtProgram& other) const { if(useIndex == other.useIndex && useIndex == true) return index == other.index; return (bank == other.bank && prog == other.prog); } } lv2ExtProgram; struct LV2MidiPort { #ifdef LV2_EVENT_BUFFER_SUPPORT LV2MidiPort (const LilvPort *_p, uint32_t _i, QString _n, bool _f, bool _supportsTimePos) : port ( _p ), index ( _i ), name ( _n ), old_api ( _f ), supportsTimePos(_supportsTimePos), buffer(0){} #else LV2MidiPort (const LilvPort *_p, uint32_t _i, QString _n, bool _supportsTimePos) : port ( _p ), index ( _i ), name ( _n ), supportsTimePos(_supportsTimePos), buffer(0){} #endif const LilvPort *port; uint32_t index; //plugin real port index QString name; #ifdef LV2_EVENT_BUFFER_SUPPORT bool old_api; //true for LV2_Event port #endif bool supportsTimePos; LV2EvBuf *buffer; }; enum LV2ControlPortType { LV2_PORT_DISCRETE = 1, LV2_PORT_INTEGER, LV2_PORT_CONTINUOUS, LV2_PORT_LOGARITHMIC, LV2_PORT_TOGGLE, LV2_PORT_ENUMERATION }; struct LV2ControlPort { LV2ControlPort ( const LilvPort *_p, uint32_t _i, float _c, const char *_n, const char *_s, LV2ControlPortType _ctype, bool _isCVPort = false, CtrlEnumValues* scalePoints = nullptr, QString group = QString(), bool isTrigger = false, bool notOnGui = false) : port ( _p ), index ( _i ), defVal ( _c ), minVal( _c ), maxVal ( _c ), cType(_ctype), isCVPort(_isCVPort), scalePoints(scalePoints), group(group), isTrigger(isTrigger), notOnGui(notOnGui) { cName = strdup ( _n ); cSym = strdup(_s); } LV2ControlPort ( const LV2ControlPort &other ) : port ( other.port ), index ( other.index ), defVal ( other.defVal ), minVal(other.minVal), maxVal(other.maxVal), cType(other.cType), isCVPort(other.isCVPort), scalePoints(other.scalePoints), group(other.group), isTrigger(other.isTrigger), notOnGui(other.notOnGui) { cName = strdup ( other.cName ); cSym = strdup(other.cSym); } ~LV2ControlPort() { free ( cName ); cName = NULL; free(cSym); cSym = NULL; } const LilvPort *port; uint32_t index; //plugin real port index float defVal; //default control value float minVal; //minimum control value float maxVal; //maximum control value char *cName; //cached value to share between function calls char *cSym; //cached port symbol LV2ControlPortType cType; bool isCVPort; CtrlEnumValues* scalePoints; QString group; bool isTrigger; bool notOnGui; }; struct LV2AudioPort { LV2AudioPort ( const LilvPort *_p, uint32_t _i, float *_b, QString _n ) : port ( _p ), index ( _i ), buffer ( _b ), name ( _n ) {} const LilvPort *port; uint32_t index; //plugin real port index float *buffer; //audio buffer QString name; }; struct cmp_str { bool operator() ( char const *a, char const *b ) const { return std::strcmp ( a, b ) < 0; } }; typedef std::vector LV2_MIDI_PORTS; typedef std::vector LV2_CONTROL_PORTS; typedef std::vector LV2_AUDIO_PORTS; typedef std::map LV2_SYNTH_URID_MAP; typedef std::map LV2_SYNTH_URID_RMAP; class LV2UridBiMap { private: LV2_SYNTH_URID_MAP _map; LV2_SYNTH_URID_RMAP _rmap; uint32_t nextId; QMutex idLock; public: LV2UridBiMap(); ~LV2UridBiMap(); LV2_URID map ( const char *uri ); const char *unmap ( uint32_t id ); }; class LV2SynthIF; struct LV2PluginWrapper_State; typedef std::map > LV2_PLUGIN_UI_TYPES; class LV2OperationMessage { public: enum Type { // For the programs extension. ProgramChanged=0, // For the midnam extension. MidnamUpdate }; Type _type; // For the programs extension. Index of program to update. // Can be -1 for update all programs. int _index; LV2OperationMessage() : _type(ProgramChanged), _index(-1) { } LV2OperationMessage(Type type, int index = 0) : _type(type), _index(index) { } }; typedef struct { QString symbol; QString label; PropType type; float min; ///< Minimum value float max; ///< Maximum value float def; ///< Default value } LV2Property; class LV2Synth : public Synth { private: const LilvPlugin *_handle; LV2UridBiMap uridBiMap; LV2_Feature *_features; LV2_Feature **_ppfeatures; LV2_Options_Option *_options; LV2_URID_Map _lv2_urid_map; LV2_URID_Unmap _lv2_urid_unmap; #ifdef LV2_URI_MAP_SUPPORT LV2_URI_Map_Feature _lv2_uri_map; #endif LV2_Log_Log _lv2_log_log; double _sampleRate; float _fSampleRate; float _scaleFactor; // Just so we can point to a zero. static const unsigned minBlockSize; bool _isSynth; // LV2 does not use unique id numbers and frowns upon using anything but the uri. // int _uniqueID; uint32_t _midi_event_id; LilvUIs *_uis; std::map _idxToControlMap; LV2_PLUGIN_UI_TYPES _pluginUiTypes; //templates for LV2SynthIF and LV2PluginWrapper instantiation LV2_MIDI_PORTS _midiInPorts; LV2_MIDI_PORTS _midiOutPorts; LV2_CONTROL_PORTS _controlInPorts; LV2_CONTROL_PORTS _controlOutPorts; LV2_AUDIO_PORTS _audioInPorts; LV2_AUDIO_PORTS _audioOutPorts; MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to LV2 port numbers. MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps LV2 port numbers to midi controller numbers. uint32_t _fInstanceAccess; uint32_t _fUiParent; uint32_t _fExtUiHost; uint32_t _fExtUiHostD; uint32_t _fDataAccess; uint32_t _fWrkSchedule; uint32_t _fUiResize; uint32_t _fPrgHost; #ifdef MIDNAM_SUPPORT uint32_t _fMidNamUpdate; #endif #ifdef LV2_MAKE_PATH_SUPPORT uint32_t _fMakePath; #endif uint32_t _fMapPath; //const LilvNode *_pluginUIType = NULL; LV2_URID _uTime_Position; LV2_URID _uTime_frame; LV2_URID _uTime_framesPerSecond; LV2_URID _uTime_speed; LV2_URID _uTime_beatsPerMinute; LV2_URID _uTime_beatsPerBar; LV2_URID _uTime_beat; LV2_URID _uTime_bar; LV2_URID _uTime_barBeat; LV2_URID _uTime_beatUnit; LV2_URID _uAtom_EventTransfer; LV2_URID _uAtom_Chunk; LV2_URID _uAtom_Sequence; LV2_URID _uAtom_StateChanged; LV2_URID _uAtom_Object; bool _hasFreeWheelPort; uint32_t _freeWheelPortIndex; bool _hasLatencyPort; uint32_t _latencyPortIndex; bool _usesTimePosition; bool _isConstructed; float *_pluginControlsDefault; float *_pluginControlsMin; float *_pluginControlsMax; std::map _presets; public: virtual Type synthType() const { return _isSynth ? LV2_SYNTH : LV2_EFFECT; } LV2Synth ( const QFileInfo &fi, const QString& uri, const QString& label, const QString& name, const QString& author, const LilvPlugin *_plugin, PluginFeatures_t reqFeatures = PluginNoFeatures ); virtual ~LV2Synth(); virtual SynthIF *createSIF ( SynthI * ); bool isSynth() { return _isSynth; } //own public functions LV2_URID mapUrid ( const char *uri ); const char *unmapUrid ( LV2_URID id ); size_t inPorts() { return _audioInPorts.size(); } size_t outPorts() { return _audioOutPorts.size(); } bool isConstructed() {return _isConstructed; } // Returns true if ANY of the midi input ports uses time position (transport). bool usesTimePosition() const { return _usesTimePosition; } static void lv2ui_PostShow ( LV2PluginWrapper_State *state ); static int lv2ui_Resize ( LV2UI_Feature_Handle handle, int width, int height ); static void lv2ui_Gtk2AllocateCb(int width, int height, void *arg); static void lv2ui_Gtk2ResizeCb(int width, int height, void *arg); static void lv2ui_ShowNativeGui (LV2PluginWrapper_State *state, bool bShow , bool fixScaling); static void lv2ui_PortWrite ( LV2UI_Controller controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, void const *buffer ); static void lv2ui_Touch (LV2UI_Controller controller, uint32_t port_index, bool grabbed); static void lv2ui_ExtUi_Closed ( LV2UI_Controller contr ); static void lv2ui_SendChangedControls(LV2PluginWrapper_State *state); static void lv2state_FillFeatures ( LV2PluginWrapper_State *state ); static void lv2state_PostInstantiate ( LV2PluginWrapper_State *state ); static void lv2ui_FreeDescriptors(LV2PluginWrapper_State *state); static void lv2state_FreeState(LV2PluginWrapper_State *state); static void lv2audio_SendTransport(LV2PluginWrapper_State *state, unsigned long sample, unsigned long nsamp, float latency_corr = 0.0f); static void lv2state_InitMidiPorts ( LV2PluginWrapper_State *state ); static void inline lv2audio_preProcessMidiPorts (LV2PluginWrapper_State *state, unsigned long sample, unsigned long nsamp); static void inline lv2audio_postProcessMidiPorts (LV2PluginWrapper_State *state, unsigned long sample, unsigned long nsamp); static const void *lv2state_stateRetreive ( LV2_State_Handle handle, uint32_t key, size_t *size, uint32_t *type, uint32_t *flags ); static LV2_State_Status lv2state_stateStore ( LV2_State_Handle handle, uint32_t key, const void *value, size_t size, uint32_t type, uint32_t flags ); static LV2_Worker_Status lv2wrk_scheduleWork(LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data); static LV2_Worker_Status lv2wrk_respond(LV2_Worker_Respond_Handle handle, uint32_t size, const void* data); static void lv2conf_write(LV2PluginWrapper_State *state, int level, Xml &xml); static void lv2conf_set(LV2PluginWrapper_State *state, const std::vector & customParams); static unsigned lv2ui_IsSupported (const char *, const char *ui_type_uri); static void lv2prg_updateProgram(LV2PluginWrapper_State *state, int idx); static void lv2prg_updatePrograms(LV2PluginWrapper_State *state); #ifdef MIDNAM_SUPPORT static void lv2midnam_updateMidnam(LV2PluginWrapper_State *state); #endif static int lv2_printf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...); static int lv2_vprintf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, va_list ap); #ifdef LV2_MAKE_PATH_SUPPORT static char *lv2state_makePath(LV2_State_Make_Path_Handle handle, const char *path); #endif static char *lv2state_abstractPath(LV2_State_Map_Path_Handle handle, const char *absolute_path); static char *lv2state_absolutePath(LV2_State_Map_Path_Handle handle, const char *abstract_path); static void lv2state_populatePresetsMenu(LV2PluginWrapper_State *state, MusEGui::PopupMenu *menu); static void lv2state_PortWrite ( LV2UI_Controller controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, void const *buffer, bool fromUi); static void lv2state_setPortValue(const char *port_symbol, void *user_data, const void *value, uint32_t size, uint32_t type); static const void* lv2state_getPortValue(const char *port_symbol, void *user_data, uint32_t *size, uint32_t *type); static void lv2state_applyPreset(LV2PluginWrapper_State *state, LilvNode *preset); static void lv2state_UnloadLoadPresets(LV2Synth *synth, bool load = false, bool update = false); friend class LV2SynthIF; friend class LV2PluginWrapper; friend class LV2SynthIF_Timer; }; class LV2SynthIF : public SynthIF { private: LV2Synth *_synth; LilvInstance *_handle; LV2_CONTROL_PORTS _controlInPorts; LV2_CONTROL_PORTS _controlOutPorts; LV2_AUDIO_PORTS _audioInPorts; LV2_AUDIO_PORTS _audioOutPorts; LV2_Feature *_ifeatures; LV2_Feature **_ppifeatures; Port *_controls; Port *_controlsOut; bool init ( LV2Synth *s ); size_t _inports; size_t _outports; size_t _inportsControl; size_t _outportsControl; size_t _inportsMidi; size_t _outportsMidi; float **_audioInBuffers; float **_audioOutBuffers; float *_audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. // For plugins that DO support the programs extension. Returns true if the selection succeeded // (ie the programs interface and functions exist). bool doSelectProgram(unsigned char channel, int bankH, int bankL, int prog); // For plugins that DO NOT support the programs extension. Sends as bankH/bankL/prog midi events. bool doSendProgram(unsigned char channel, int bankH, int bankL, int prog, LV2EvBuf *evBuf, long frame); inline void sendLv2MidiEvent(LV2EvBuf *evBuf, long frame, int paramCount, uint8_t a, uint8_t b = 0, uint8_t c = 0); bool processEvent (const MidiPlayEvent &, LV2EvBuf *evBuf, long frame); bool lv2MidiControlValues ( size_t port, int ctlnum, int *min, int *max, int *def ); float midi2Lv2Value ( unsigned long port, int ctlnum, int val ); #ifdef MIDNAM_SUPPORT QString getPatchNameMidNam (int, int, bool ) const; void populatePatchPopupMidNam (MusEGui::PopupMenu*, int channel, bool drum); #endif LV2PluginWrapper_State *_state; public: LV2SynthIF ( SynthI *s ); virtual ~LV2SynthIF(); //virtual methods from SynthIF virtual void guiHeartBeat(); virtual bool hasGui() const; virtual bool nativeGuiVisible() const; virtual void showNativeGui ( bool v ); virtual bool hasNativeGui() const; virtual void getNativeGeometry ( int *, int *, int *, int * ) const; virtual void setNativeGeometry (int x, int y, int w, int h); virtual bool getData ( MidiPort *, unsigned pos, int ports, unsigned n, float **buffer ); virtual MidiPlayEvent receiveEvent(); virtual int eventsPending() const; virtual int channels() const; virtual int totalOutChannels() const; virtual int totalInChannels() const; void activate(); virtual void deactivate(); virtual void deactivate3(); virtual QString getPatchName (int, int, bool ) const; virtual void populatePatchPopup ( MusEGui::PopupMenu *, int, bool ); virtual void write ( int level, Xml &xml ) const; virtual double getParameter ( unsigned long idx ) const; virtual double getParameterOut ( unsigned long n ) const; virtual void setParameter ( unsigned long idx, double value ); virtual int getControllerInfo ( int id, QString* name, int *ctrl, int *min, int *max, int *initval ); // Returns true if a note name list is found for the given patch. // If true, name either contains the note name, or is blank if no note name was found. // drum = Want percussion names, not melodic. virtual bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; virtual void writeConfiguration ( int level, Xml &xml ); virtual bool readConfiguration ( Xml &xml, bool readPreset=false ); virtual void setCustomData ( const std::vector & ); unsigned long parameters() const; unsigned long parametersOut() const; void setParam ( unsigned long i, double val ); double param ( unsigned long i ) const; double paramOut ( unsigned long i ) const; const char *paramName ( unsigned long i ); const char *paramOutName ( unsigned long i ); CtrlValueType ctrlValueType ( unsigned long ) const override; CtrlList::Mode ctrlMode ( unsigned long ) const override; CtrlEnumValues *ctrlEnumValues(unsigned long i) const override; QString portGroup(long unsigned int i) const override; bool ctrlIsTrigger(long unsigned int i) const override; bool ctrlNotOnGui(long unsigned int i) const override; virtual LADSPA_PortRangeHint range(unsigned long i); virtual LADSPA_PortRangeHint rangeOut(unsigned long i); bool hasLatencyOutPort() const; unsigned long latencyOutPortIndex() const; float latency() const; // Returns true if ANY of the midi input ports uses time position (transport). bool usesTransportSource() const; virtual void enableController(unsigned long i, bool v = true); virtual bool controllerEnabled(unsigned long i) const; virtual void enableAllControllers(bool v = true); virtual void updateControllers(); void populatePresetsMenu(MusEGui::PopupMenu *menu); void applyPreset(void *preset); int id() { return MusECore::MAX_PLUGINS; } static void lv2prg_Changed(LV2_Programs_Handle handle, int32_t index); #ifdef MIDNAM_SUPPORT static void lv2midnam_Changed(LV2_Midnam_Handle handle); #endif friend class LV2Synth; }; class LV2PluginWrapper; class LV2PluginWrapper_Worker; class LV2PluginWrapper_Window; struct LV2PluginWrapper_State { LV2PluginWrapper_State(): _ifeatures(NULL), _ppifeatures(NULL), widget(NULL), handle(NULL), uiDlHandle(NULL), uiDesc(NULL), uiInst(NULL), inst(NULL), lastControls(NULL), controlsMask(NULL), lastControlsOut(NULL), plugInst(NULL), sif(NULL), synth(NULL), human_id(NULL), iState(NULL), tmpValues(NULL), numStateValues(0), wrkDataBuffer(NULL), wrkRespDataBuffer(NULL), wrkThread(NULL), wrkIface(NULL), controlTimers(NULL), deleteLater(false), // Initialize these with invalid values such that the first call // of the send transport routine is guaranteed to update them. curGlobalTempo(0), curTempo(0), curIsPlaying(false), curFrame(0), curTick(0), curBeatsPerBar(0), curBeatUnit(0), hasGui(false), hasExternalGui(false), uiIdleIface(NULL), uiCurrent(NULL), uiX11Size(0, 0), pluginWindow(NULL), pluginQWindow(NULL), prgIface(NULL), uiPrgIface(NULL), uiDoSelectPrg(false), newPrgIface(false), #ifdef MIDNAM_SUPPORT midnamIface(NULL), #endif uiChannel(0), uiBank(0), uiProg(0), gtk2Plug(NULL), pluginCVPorts(NULL), uiControlEvt(LV2_RT_FIFO_SIZE), plugControlEvt(LV2_RT_FIFO_SIZE), gtk2ResizeCompleted(false), gtk2AllocateCompleted(false), songDirtyPending(false), uiIsOpening(false), operationsFifo(OPERATIONS_FIFO_SIZE) { extHost.plugin_human_id = NULL; extHost.ui_closed = NULL; uiResize.handle = (LV2UI_Feature_Handle)this; uiResize.ui_resize = LV2Synth::lv2ui_Resize; prgHost.handle = (LV2_Programs_Handle)this; prgHost.program_changed = LV2SynthIF::lv2prg_Changed; #ifdef MIDNAM_SUPPORT midnamUpdate.handle = (LV2_Midnam_Handle)this; midnamUpdate.update = LV2SynthIF::lv2midnam_Changed; #endif #ifdef LV2_MAKE_PATH_SUPPORT makePath.handle = (LV2_State_Make_Path_Handle)this; makePath.path = LV2Synth::lv2state_makePath; #endif mapPath.handle = (LV2_State_Map_Path_Handle)this; mapPath.absolute_path = LV2Synth::lv2state_absolutePath; mapPath.abstract_path = LV2Synth::lv2state_abstractPath; midiInPorts.clear(); midiOutPorts.clear(); idx2EvtPorts.clear(); inPortsMidi = outPortsMidi = 0; } LV2_Feature *_ifeatures; LV2_Feature **_ppifeatures; void *widget; LV2_External_UI_Host extHost; LV2_Extension_Data_Feature extData; LV2_Worker_Schedule wrkSched; #ifdef LV2_MAKE_PATH_SUPPORT LV2_State_Make_Path makePath; #endif LV2_State_Map_Path mapPath; LilvInstance *handle; void *uiDlHandle; const LV2UI_Descriptor *uiDesc; LV2UI_Handle uiInst; LV2PluginWrapper *inst; float *lastControls; bool *controlsMask; float *lastControlsOut; PluginI *plugInst; LV2SynthIF *sif; LV2Synth *synth; char *human_id; LV2_State_Interface *iState; QMap > iStateValues; char **tmpValues; size_t numStateValues; LockFreeDataRingBuffer *wrkDataBuffer; LockFreeDataRingBuffer *wrkRespDataBuffer; LV2PluginWrapper_Worker *wrkThread; LV2_Worker_Interface *wrkIface; int *controlTimers; bool deleteLater; LV2_Atom_Forge atomForge; // State of the transport, for testing if it changed. int curGlobalTempo; int curTempo; bool curIsPlaying; unsigned int curFrame; unsigned int curTick; int curBeatsPerBar; int curBeatUnit; bool hasGui; bool hasExternalGui; bool fixedSizeGui; bool noUserResizeGui; LV2UI_Idle_Interface *uiIdleIface; const LilvUI *uiCurrent; LV2UI_Resize uiResize; QSize uiX11Size; LV2PluginWrapper_Window *pluginWindow; QWindow *pluginQWindow; LV2_MIDI_PORTS midiInPorts; LV2_MIDI_PORTS midiOutPorts; size_t inPortsMidi; size_t outPortsMidi; LV2_Programs_Interface *prgIface; LV2_Programs_UI_Interface *uiPrgIface; bool uiDoSelectPrg; bool newPrgIface; std::map index2prg; std::map prg2index; LV2_Programs_Host prgHost; #ifdef MIDNAM_SUPPORT LV2_Midnam_Interface *midnamIface; LV2_Midnam midnamUpdate; #endif unsigned char uiChannel; int uiBank; int uiProg; void *gtk2Plug; std::map controlsNameMap; std::map controlsSymMap; float **pluginCVPorts; LV2SimpleRTFifo uiControlEvt; LV2SimpleRTFifo plugControlEvt; std::map idx2EvtPorts; bool gtk2ResizeCompleted; bool gtk2AllocateCompleted; bool songDirtyPending; bool uiIsOpening; LockFreeMPSCRingBuffer operationsFifo; }; class LV2PluginWrapper_Worker :public QThread { private: LV2PluginWrapper_State *_state; QSemaphore _mSem; bool _closing; public: explicit LV2PluginWrapper_Worker ( LV2PluginWrapper_State *s ) : QThread(), _state ( s ), _mSem(0), _closing(false) {} void run(); LV2_Worker_Status scheduleWork(); void makeWork(); void setClosing() {_closing = true; _mSem.release();} }; #ifdef LV2_GUI_USE_QWIDGET class LV2PluginWrapper_Window : public QWidget #else class LV2PluginWrapper_Window : public QMainWindow #endif { Q_OBJECT protected: void closeEvent ( QCloseEvent *event ); void showEvent(QShowEvent *e); void hideEvent(QHideEvent *e); private: LV2PluginWrapper_State *_state; bool _closing; QTimer updateTimer; void stopUpdateTimer(); public: explicit LV2PluginWrapper_Window ( LV2PluginWrapper_State *state, QWidget *parent = Q_NULLPTR, Qt::WindowFlags flags = Qt::WindowFlags()); ~LV2PluginWrapper_Window(); void startNextTime(); void stopNextTime(); void doChangeControls(); void setClosing(bool closing) {_closing = closing; } signals: void makeStopFromGuiThread(); void makeStartFromGuiThread(); public slots: void updateGui(); void stopFromGuiThread(); void startFromGuiThread(); }; class LV2PluginWrapper: public Plugin { private: LV2Synth *_synth; LADSPA_Descriptor _fakeLd; LADSPA_PortDescriptor *_fakePds; public: LV2PluginWrapper ( LV2Synth *s, PluginFeatures_t reqFeatures = PluginNoFeatures ); LV2Synth *synth() { return _synth; } virtual ~LV2PluginWrapper(); virtual LADSPA_Handle instantiate ( PluginI * ); virtual int incReferences ( int ref ); virtual void activate ( LADSPA_Handle handle ); virtual void deactivate ( LADSPA_Handle handle ); virtual void cleanup ( LADSPA_Handle handle ); virtual void connectPort ( LADSPA_Handle handle, unsigned long port, float *value ); virtual void apply ( LADSPA_Handle handle, unsigned long n, float latency_corr = 0.0f ); virtual LADSPA_PortDescriptor portd ( unsigned long k ) const; virtual LADSPA_PortRangeHint range ( unsigned long i ); virtual void range ( unsigned long i, float *min, float *max ) const; virtual double defaultValue ( unsigned long port ) const; virtual const char *portName ( unsigned long i ); virtual CtrlValueType ctrlValueType ( unsigned long ) const; virtual CtrlEnumValues* ctrlEnumValues ( unsigned long ) const; virtual CtrlList::Mode ctrlMode ( unsigned long ) const; virtual bool hasNativeGui() const; virtual void showNativeGui ( PluginI *p, bool bShow ); virtual bool nativeGuiVisible (const PluginI *p ) const; virtual void setLastStateControls(LADSPA_Handle handle, size_t index, bool bSetMask, bool bSetVal, bool bMask, float fVal); virtual void writeConfiguration(LADSPA_Handle handle, int level, Xml& xml); virtual void setCustomData (LADSPA_Handle handle, const std::vector & customParams); void populatePresetsMenu(PluginI *p, MusEGui::PopupMenu *menu); void applyPreset(PluginI *p, void *preset); }; #endif // LV2_SUPPORT extern void initLV2(); } // namespace MusECore #endif muse-4.0.0/src/muse/main.cpp000066400000000000000000002071051404105311000156460ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: app.cpp,v 1.113.2.68 2009/12/21 14:51:51 spamatica Exp $ // // (C) Copyright 1999-2011 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include //#include #include #ifndef _WIN32 #include #endif #include "config.h" #ifdef ALSA_SUPPORT #include #endif #include "al/al.h" #include "al/dsp.h" #include "app.h" #include "audio.h" #include "audiodev.h" #include "gconfig.h" #include "globals.h" #include "helper.h" #include "sync.h" #include "functions.h" #include "appearance.h" #include "midiseq.h" #include "minstrument.h" #include "midiport.h" #include "mididev.h" #include "plugin.h" #include "wavepreview.h" #include "plugin_cache_writer.h" #include "pluglist.h" #include "metronome_class.h" #include "audio_convert/audio_converter_plugin.h" #include "audio_convert/audio_converter_settings_group.h" #include "wave.h" #include "conf.h" #ifdef HAVE_LASH #include #endif namespace MusECore { extern bool initDummyAudio(); #ifdef HAVE_RTAUDIO extern bool initRtAudio(bool forceDefault = false); #endif extern bool initJackAudio(); extern void initMidiController(); extern void initMetronome(); //extern void initOSC(); extern void initVST(); extern void initVST_Native(); //extern void initPlugins(); extern void initDSSI(); #ifdef LV2_SUPPORT extern void initLV2(); extern void deinitLV2(); #endif //extern bool readConfiguration(); //extern void initMidiSequencer(); //extern void exitMidiSequencer(); extern void initAudio(); extern void initAudioPrefetch(); extern void initMidiSynth(); #ifdef ALSA_SUPPORT extern snd_seq_t * alsaSeq; #endif extern void setAlsaClientName(const char*); } namespace MusEGui { void initIcons(int cursorSize, const QString& gpath, const QString& upath); void initShortCuts(); #ifdef HAVE_LASH extern lash_client_t * lash_client; #endif extern QStringList projectRecentList; } enum AudioDriverSelect { DriverConfigSetting, DummyAudioOverride, JackAudioOverride, RtAudioOverride, }; static QString locale_override; //--------------------------------------------------------- // MuseApplication //--------------------------------------------------------- class MuseApplication : public QApplication { MusEGui::MusE* muse; public: MuseApplication(int& argc, char** argv) : QApplication(argc, argv) { muse = nullptr; } void setMuse(MusEGui::MusE* m) { muse = m; connect(this,SIGNAL(focusChanged(QWidget*,QWidget*)),muse,SLOT(focusChanged(QWidget*,QWidget*))); #ifdef HAVE_LASH if(MusEGlobal::useLASH) startTimer (300); #endif } bool notify(QObject* receiver, QEvent* event) override { const bool flag = QApplication::notify(receiver, event); const QEvent::Type type = event->type(); if (type == QEvent::KeyPress) { const QMetaObject * mo = receiver->metaObject(); if (mo){ if (strcmp(mo->className(), "QWidgetWindow") == 0) return false; } QKeyEvent* ke = (QKeyEvent*)event; MusEGlobal::globalKeyState = ke->modifiers(); bool accepted = ke->isAccepted(); if (!accepted) { int key = ke->key(); if (((QInputEvent*)ke)->modifiers() & Qt::ShiftModifier) key += Qt::SHIFT; if (((QInputEvent*)ke)->modifiers() & Qt::AltModifier) key += Qt::ALT; if (((QInputEvent*)ke)->modifiers() & Qt::ControlModifier) key+= Qt::CTRL; if(muse) muse->kbAccel(key); return true; } } else if (type == QEvent::KeyRelease) { QKeyEvent* ke = (QKeyEvent*)event; ///MusEGlobal::globalKeyState = ke->stateAfter(); MusEGlobal::globalKeyState = ke->modifiers(); } return flag; } #ifdef HAVE_LASH virtual void timerEvent (QTimerEvent*) { if(muse && MusEGlobal::useLASH) muse->lash_idle_cb (); } #endif /* HAVE_LASH */ }; //--------------------------------------------------------- // localeList //--------------------------------------------------------- static QString localeList() { // Find out what translations are available: QStringList deliveredLocaleListFiltered; QString distLocale = MusEGlobal::museGlobalShare + "/locale"; QFileInfo distLocaleFi(distLocale); if (distLocaleFi.isDir()) { QDir dir = QDir(distLocale); QStringList deliveredLocaleList = dir.entryList(); for(QStringList::iterator it = deliveredLocaleList.begin(); it != deliveredLocaleList.end(); ++it) { QString item = *it; if (item.endsWith(".qm")) { int inipos = item.indexOf("muse_") + 5; int finpos = item.lastIndexOf(".qm"); deliveredLocaleListFiltered << item.mid(inipos, finpos - inipos); } } return deliveredLocaleListFiltered.join(","); } return QString("No translations found!"); } void fallbackDummy() { fprintf(stderr, "Falling back to dummy audio driver\n"); QMessageBox::critical(nullptr, "MusE fatal error", "MusE failed to find selected audio server.

" "MusE will continue without audio support (-a switch)!"); MusEGlobal::realTimeScheduling = true; MusECore::initDummyAudio(); } //--------------------------------------------------------- // printExtraHelpText //--------------------------------------------------------- static void printExtraHelpText() { printf("\n"); #ifdef HAVE_LASH printf("LASH and "); #endif printf("Qt options are also accepted. Some common Qt options:\n"); printf(" -style [=] style Set application GUI style (Fusion, Windows etc.)\n" " -stylesheet [=] stylesheet Set application styleSheet\n" " -session [=] session Restore application from an earlier session\n" " -widgetcount Print debug message at end, about undestroyed/maximum widgets\n" " -reverse Set application's layout direction to Qt::RightToLeft\n" " -graphicssystem Set backend used for on-screen widgets/QPixmaps: raster or opengl\n" " -qmljsdebugger = port Activate QML/JS debugger with port, formatted port:1234[,block]\n" ); printf("\n"); printf("Some useful environment variables:\n\n"); printf(" LANG: Help browser language suffix (en etc.)\n\n"); printf("These variables are read ONCE upon first-time run, to fill the Plugin Paths\n" " in Global Settings. Afterwards the paths can be altered in Global Settings:\n\n"); printf(" LADSPA_PATH: Override where to look for ladspa plugins, or else\n" " ~/ladspa:~/.ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa\n\n"); #ifdef DSSI_SUPPORT printf(" DSSI_PATH: Override where to look for dssi plugins, or else\n" " ~/dssi:~/.dssi:/usr/local/lib64/dssi:/usr/lib64/dssi:/usr/local/lib/dssi:/usr/lib/dssi\n\n" ); printf(" VST_PATH: Override where dssi-vst (if installed) looks for Wine vst plugins, or else\n" " ~/vst win 32bit:~/.vst win 32bit or ~/vst:~/.vst on windows\n\n"); #endif #ifdef VST_NATIVE_SUPPORT printf(" LXVST_PATH: Override where to look for Linux vst plugins, or else VST_PATH, or else\n" " ~/lxvst:~/.lxvst:/usr/local/lib64/lxvst:/usr/local/lib/lxvst:/usr/lib64/lxvst:/usr/lib/lxvst\n" " also on Linux ~/vst:~/.vst:/usr/local/lib64/vst:/usr/local/lib/vst:/usr/lib64/vst:/usr/lib/vst\n\n"); #endif #ifdef LV2_SUPPORT printf(" LV2_PATH: Override where to look for LV2 plugins or else\n" " ~/.lv2:/usr/local/lib/lv2:/usr/lib/lv2\n\n"); #endif } enum CommandLineParseResult { CommandLineOk, CommandLineError, CommandLineVersionRequested, CommandLineHelpRequested }; CommandLineParseResult parseCommandLine( QCommandLineParser &parser, QString *errorMessage, QString& open_filename, AudioDriverSelect& audioType, bool& force_plugin_rescan, bool& dont_plugin_rescan) { parser.setApplicationDescription(APP_DESCRIPTION); const QString version_string(VERSION); const QString git_string(GITSTRING); if(git_string.isEmpty()) QCoreApplication::setApplicationVersion(version_string); else QCoreApplication::setApplicationVersion(version_string + ", (" + git_string + ")"); const QCommandLineOption helpOption = parser.addHelpOption(); const QCommandLineOption versionOption = parser.addVersionOption(); parser.addPositionalArgument("filename", QCoreApplication::translate("main", "File to open")); QCommandLineOption option_a("a", QCoreApplication::translate("main", "Alsa midi only (using dummy audio driver)")); parser.addOption(option_a); #ifdef HAVE_RTAUDIO QCommandLineOption option_t("t", QCoreApplication::translate("main", "Use RtAudio driver")); parser.addOption(option_t); #endif QCommandLineOption option_j("j", QCoreApplication::translate("main", "Use JAckAudio driver to connect to Jack audio server")); parser.addOption(option_j); QCommandLineOption option_J("J", QCoreApplication::translate("main", "Do not try to auto-start the Jack audio server")); parser.addOption(option_J); QCommandLineOption option_F("F", QCoreApplication::translate("main", "Do not auto-populate midi ports with midi devices found, at startup")); parser.addOption(option_F); QCommandLineOption option_A("A", QCoreApplication::translate("main", "Force inclusion of ALSA midi even if using Jack")); parser.addOption(option_A); QCommandLineOption option_P("P", QCoreApplication::translate("main", "Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.)"), "n"); parser.addOption(option_P); QCommandLineOption option_Y("Y", QCoreApplication::translate("main", "Force midi real time priority to n (default: audio driver prio -1)\n"), "n"); parser.addOption(option_Y); QCommandLineOption option_R("R", QCoreApplication::translate("main", "Force plugin cache re-creation. (Automatic if any plugin path directories changed.)")); parser.addOption(option_R); QCommandLineOption option_C("C", QCoreApplication::translate("main", "Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care.")); parser.addOption(option_C); QCommandLineOption option_p("p", QCoreApplication::translate("main", "Don't load LADSPA plugins")); parser.addOption(option_p); QCommandLineOption option_S("S", QCoreApplication::translate("main", "Don't load MESS plugins")); parser.addOption(option_S); #ifdef VST_SUPPORT QCommandLineOption option_V("V", QCoreApplication::translate("main", "Don't load VST plugins")); parser.addOption(option_V); #endif #ifdef VST_NATIVE_SUPPORT QCommandLineOption option_N("N", QCoreApplication::translate("main", "Don't load LinuxVST plugins")); parser.addOption(option_N); #endif #ifdef DSSI_SUPPORT QCommandLineOption option_I("I", QCoreApplication::translate("main", "Don't load DSSI plugins")); parser.addOption(option_I); #endif #ifdef LV2_SUPPORT QCommandLineOption option_2("2", QCoreApplication::translate("main", "Don't load LV2 plugins")); parser.addOption(option_2); #endif #ifdef HAVE_LASH QCommandLineOption option_L("L", QCoreApplication::translate("main", "Don't use LASH")); parser.addOption(option_L); #endif QCommandLineOption option_l(QCommandLineOption("l", QCoreApplication::translate("main", "Force locale to the given language/country code (xx = ") + localeList() + ")", "xx")); parser.addOption(option_l); QCommandLineOption option_u("u", QCoreApplication::translate("main", "Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins.")); parser.addOption(option_u); QCommandLineOption option_d("d", QCoreApplication::translate("main", "Debug mode: no threads, no RT")); parser.addOption(option_d); QCommandLineOption option_D("D", QCoreApplication::translate("main", "Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively!")); parser.addOption(option_D); QCommandLineOption option_m("m", QCoreApplication::translate("main", "Debug mode: trace midi Input")); parser.addOption(option_m); QCommandLineOption option_M("M", QCoreApplication::translate("main", "Debug mode: trace midi Output")); parser.addOption(option_M); QCommandLineOption option_s("s", QCoreApplication::translate("main", "Debug mode: trace sync\n")); parser.addOption(option_s); #ifdef PYTHON_SUPPORT QCommandLineOption option_y("y", QCoreApplication::translate("main", "Enable Python control support")); parser.addOption(option_y); QCommandLineOption option_pyro_ns_host("pyro-ns-host", QCoreApplication::translate("main", "Pyro nameserver host name"), "hostname"); parser.addOption(option_pyro_ns_host); QCommandLineOption option_pyro_ns_port("pyro-ns-port", QCoreApplication::translate("main", "Pyro nameserver host port"), "port"); parser.addOption(option_pyro_ns_port); QCommandLineOption option_pyro_daemon_host("pyro-daemon-host", QCoreApplication::translate("main", "Pyro daemon host name"), "hostname"); parser.addOption(option_pyro_daemon_host); QCommandLineOption option_pyro_daemon_port("pyro-daemon-port", QCoreApplication::translate("main", "Pyro daemon host port"), "port"); parser.addOption(option_pyro_daemon_port); QCommandLineOption option_pyro_comm_timeout("pyro-comm-timeout", QCoreApplication::translate("main", "Pyro communication timeout in seconds"), "timeout"); parser.addOption(option_pyro_comm_timeout); #endif if(!parser.parse(QCoreApplication::arguments())) { *errorMessage = parser.errorText(); return CommandLineError; } if(parser.isSet(versionOption)) return CommandLineVersionRequested; if(parser.isSet(helpOption)) return CommandLineHelpRequested; const QStringList used_positional_args = parser.positionalArguments(); const int used_positional_args_sz = used_positional_args.size(); if(used_positional_args_sz > 1) { *errorMessage = "Error: Expected only one positional argument"; return CommandLineError; } else if(used_positional_args_sz == 1) { open_filename = used_positional_args.first(); } if(parser.isSet(option_a)) audioType = DummyAudioOverride; if(parser.isSet(option_l)) locale_override = parser.value(option_l); #ifdef HAVE_RTAUDIO if(parser.isSet(option_t)) audioType = RtAudioOverride; #endif if(parser.isSet(option_J)) MusEGlobal::noAutoStartJack = true; if(parser.isSet(option_j)) audioType = JackAudioOverride; if(parser.isSet(option_F)) MusEGlobal::populateMidiPortsOnStart = false; if(parser.isSet(option_A)) MusEGlobal::useAlsaWithJack = true; if(parser.isSet(option_d)) { MusEGlobal::debugMode = true; MusEGlobal::realTimeScheduling = false; } if(parser.isSet(option_D)) { if(!MusEGlobal::debugMsg) MusEGlobal::debugMsg=true; else MusEGlobal::heavyDebugMsg=true; } if(parser.isSet(option_m)) MusEGlobal::midiInputTrace = true; if(parser.isSet(option_M)) MusEGlobal::midiOutputTrace = true; if(parser.isSet(option_s)) MusEGlobal::debugSync = true; if(parser.isSet(option_u)) MusEGlobal::unityWorkaround = true; if(parser.isSet(option_P)) MusEGlobal::realTimePriority = parser.value(option_P).toInt(); if(parser.isSet(option_Y)) MusEGlobal::midiRTPrioOverride = parser.value(option_Y).toInt(); if(parser.isSet(option_p)) MusEGlobal::loadPlugins = false; if(parser.isSet(option_R)) force_plugin_rescan = true; if(parser.isSet(option_C)) dont_plugin_rescan = true; if(parser.isSet(option_S)) MusEGlobal::loadMESS = false; #ifdef VST_SUPPORT if(parser.isSet(option_V)) MusEGlobal::loadVST = false; #endif #ifdef VST_NATIVE_SUPPORT if(parser.isSet(option_N)) MusEGlobal::loadNativeVST = false; #endif #ifdef DSSI_SUPPORT if(parser.isSet(option_I)) MusEGlobal::loadDSSI = false; #endif #ifdef HAVE_LASH if(parser.isSet(option_L)) MusEGlobal::useLASH = false; #endif #ifdef LV2_SUPPORT if(parser.isSet(option_2)) MusEGlobal::loadLV2 = false; #endif #ifdef PYTHON_SUPPORT if(parser.isSet(option_y)) { MusEGlobal::usePythonBridge = true; if(parser.isSet(option_pyro_ns_host)) MusEGlobal::pythonBridgePyroNSHostname = parser.value(option_pyro_ns_host); if(parser.isSet(option_pyro_ns_port)) MusEGlobal::pythonBridgePyroNSPort = parser.value(option_pyro_ns_port); if(parser.isSet(option_pyro_daemon_host)) MusEGlobal::pythonBridgePyroDaemonHostname = parser.value(option_pyro_daemon_host); if(parser.isSet(option_pyro_daemon_port)) MusEGlobal::pythonBridgePyroDaemonPort = parser.value(option_pyro_daemon_port); if(parser.isSet(option_pyro_comm_timeout)) MusEGlobal::pythonBridgePyroCommTimeout = parser.value(option_pyro_comm_timeout).toFloat(); } #endif return CommandLineOk; } //--------------------------------------------------------- // main //--------------------------------------------------------- int main(int argc, char* argv[]) { // Get the separator used for file paths. const QChar list_separator = QDir::listSeparator(); // Get environment variables for various paths. // "The Qt environment manipulation functions are thread-safe, but this requires that // the C library equivalent functions like getenv and putenv are not directly called." // "Note: on desktop Windows, qgetenv() may produce data loss if the original string // contains Unicode characters not representable in the ANSI encoding. // Use qEnvironmentVariable() instead. On Unix systems, this function is lossless." #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) const QString ladspa_path = qEnvironmentVariable("LADSPA_PATH"); const QString dssi_path = qEnvironmentVariable("DSSI_PATH"); const QString vst_path = qEnvironmentVariable("VST_PATH"); // This Linux VST path is known to be used by Ardour. const QString lxvst_path = qEnvironmentVariable("LXVST_PATH"); const QString lv2_path = qEnvironmentVariable("LV2_PATH"); #else // "To convert the data to a QString use QString::fromLocal8Bit()." const QString ladspa_path = QString::fromLocal8Bit(qgetenv("LADSPA_PATH")); const QString dssi_path = QString::fromLocal8Bit(qgetenv("DSSI_PATH")); const QString vst_path = QString::fromLocal8Bit(qgetenv("VST_PATH")); const QString lxvst_path = QString::fromLocal8Bit(qgetenv("LXVST_PATH")); const QString lv2_path = QString::fromLocal8Bit(qgetenv("LV2_PATH")); #endif QString last_project_filename; bool last_project_was_template = false; bool last_project_loaded_config = false; bool plugin_rescan_already_done = false; int rv = 0; //============================================== // BEGIN Restart loop. For (re)starting the app. //============================================== bool is_restarting = true; // First-time init true. while(is_restarting) { is_restarting = false; //QTime timer; // Deprecated QElapsedTimer timer; timer.start(); // Make working copies of the arguments. const int argument_count = argc; int argc_copy = argc; char** argv_copy = nullptr; if(argument_count > 0) { argv_copy = (char**)malloc(argument_count * sizeof(char*)); int len = 0; for(int i = 0; i < argument_count; ++i) { argv_copy[i] = nullptr; if(argv[i]) { len = strlen(argv[i]); argv_copy[i] = (char*)malloc(len + 2); strcpy(argv_copy[i], argv[i]); } } } // Let LASH remove its recognized arguments first (generally longer than Qt's). // Tip: LADISH's LASH emulation (current 1.0) does not take any arguments. #ifdef HAVE_LASH lash_args_t * lash_args = 0; lash_args = lash_extract_args (&argc_copy, &argv_copy); #endif // Now create the application, and let Qt remove recognized arguments. QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); // Qt style must be set before app object is created (->Qt docu) // Should the standard Qt style (Fusion) get removed in future, // we're OK to just keep the current system style here (kybos) if (QStyleFactory::keys().contains(MusEGlobal::defaultStyle, Qt::CaseInsensitive)) QApplication::setStyle(MusEGlobal::defaultStyle); //======================== // Application instance: //======================== MuseApplication app(argc_copy, argv_copy); // if (QStyle* def_style = app.style()) // { // const QString appStyleObjName = def_style->objectName(); // MusEGui::Appearance::getSetDefaultStyle(&appStyleObjName); // } qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Read configuration..."; app.setOrganizationName(ORGANIZATION_NAME); app.setOrganizationDomain(ORGANIZATION_DOMAIN); app.setApplicationName(PACKAGE_NAME); app.setApplicationDisplayName(APP_DISPLAY_NAME); // NOTE: 'GenericConfigLocation' returned config dir (ie. ~./config). // 'ConfigLocation' also returned config dir (ie. ~./config). // 'AppConfigLocation' (Qt 5.5) returned config + organization name + application name dirs // (ie. ~./config/MusE/MusE-qt). // Beware, setting application name and organization name influence these locations. // "Returns a directory location where user-specific configuration files should be written. // This is an application-specific directory, and the returned path is never empty. // This enum value was added in Qt 5.5." MusEGlobal::configPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); // "Returns a directory location where user-specific non-essential (cached) data should be written. // This is an application-specific directory. The returned path is never empty." // NOTE: This returned cache + organization name + application name dirs (ie. ~./cache/MusE/MusE-qt). MusEGlobal::cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); MusEGlobal::museUser = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); MusEGlobal::museGlobalLib = QString(LIBDIR); MusEGlobal::museGlobalShare = QString(SHAREDIR); const QByteArray appDir = qgetenv("APPDIR"); // running in AppImage if (!appDir.isEmpty()) { MusEGlobal::museGlobalLib = appDir + MusEGlobal::museGlobalLib; MusEGlobal::museGlobalShare = appDir + MusEGlobal::museGlobalShare; } MusEGlobal::museProject = MusEGlobal::museProjectInitPath; //getcwd(0, 0); MusEGlobal::museInstruments = MusEGlobal::museGlobalShare + "/instruments"; MusEGlobal::configName = MusEGlobal::configPath + "/MusE-seq.cfg"; const QString oldConfigPath(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/" + PACKAGE_NAME); const QString old_qtconfig_name(oldConfigPath + "/MusE-qt.conf"); const QString new_qtconfig_name(oldConfigPath + "/MusE.conf"); const QString new_plugin_cache_path(MusEGlobal::cachePath + "/scanner"); // If the new-style plugin cache directory location doesn't exist yet, and an // old-style plugin cache directory exists, rename the old one to the new one. if(!MusEGlobal::cachePath.isEmpty()) { QDir new_plugin_cache_dir(new_plugin_cache_path); if(!new_plugin_cache_dir.exists()) { const QString old_plugin_cache_path(oldConfigPath + "/scanner"); QDir old_plugin_cache_dir(old_plugin_cache_path); if(old_plugin_cache_dir.exists()) { QDir(MusEGlobal::cachePath).mkpath("."); if(!QDir().rename(old_plugin_cache_path, new_plugin_cache_path)) fprintf(stderr, "Error renaming plugin cache dir:<%s> to:<%s>\n", old_plugin_cache_path.toLocal8Bit().constData(), new_plugin_cache_path.toLocal8Bit().constData()); } } } // If the new-style config directory location doesn't exist yet, and an // old-style config directory exists, rename the old one to the new one. if(!MusEGlobal::configPath.isEmpty()) { QDir new_config_dir(MusEGlobal::configPath); if(!new_config_dir.exists()) { QFileInfoList fil; QDir old_config_dir(oldConfigPath); if(old_config_dir.exists()) fil = old_config_dir.entryInfoList(QDir::AllEntries| QDir::NoDotAndDotDot); // Create the new directory AFTER grabbing the existing list so that // if the new directory is a subdirectory of the existing one, // it will not show up in the list. new_config_dir.mkpath("."); if(!fil.isEmpty()) { QFileInfo fi; foreach(fi, fil) { const QString afp(fi.absoluteFilePath()); // DO NOT move the old MusE-qt config file. // Given an organization name and application name, that is where // QSettings are stored it (ie. ~/.config/MusE not ~/.config/MusE/MusE). if(afp == old_qtconfig_name) continue; const QString fn = fi.fileName(); const QString new_fn(MusEGlobal::configPath + "/" + fn); if(fi.isDir()) { if(!QDir().rename(afp, new_fn)) fprintf(stderr, "Error renaming config dir:<%s> to:<%s>\n", afp.toLocal8Bit().constData(), new_fn.toLocal8Bit().constData()); } else { QFile f(afp); if(!f.rename(new_fn)) fprintf(stderr, "Error renaming config file:<%s> to:<%s>\n", afp.toLocal8Bit().constData(), new_fn.toLocal8Bit().constData()); } } } } } { const QString old_config_name(MusEGlobal::configPath + "/MusE.cfg"); // Rename existing config file to new name. QFile oldConfigFile(old_config_name); if(oldConfigFile.exists()) oldConfigFile.rename(MusEGlobal::configName); } bool cConfExists = false; { QFile cConf (MusEGlobal::configName); cConfExists = cConf.exists(); } { QFile oldQtConfigFile(old_qtconfig_name); if(oldQtConfigFile.exists()) oldQtConfigFile.rename(new_qtconfig_name); } // User instruments dir: MusEGlobal::museUserInstruments = MusEGlobal::configPath + "/instruments"; // Create user instruments dir if it doesn't exist { QDir uinstrDir = QDir(MusEGlobal::museUserInstruments); if(!uinstrDir.exists()) { fprintf(stderr, "User instrument directory does not exist. Creating it.\n"); uinstrDir.mkpath("."); } } MusEGui::initShortCuts(); // Discover available MusE audio converters, before reading configuration MusEGlobal::audioConverterPluginList.discover(MusEGlobal::museGlobalLib, MusEGlobal::debugMsg); // Default, non-local settings. MusEGlobal::defaultAudioConverterSettings = new MusECore::AudioConverterSettingsGroup(false); MusEGlobal::defaultAudioConverterSettings->populate(&MusEGlobal::audioConverterPluginList, false); MusECore::readConfiguration(); // Need to put a sane defaults here because we can't use '~' in the file name strings. if(!cConfExists) { MusEGlobal::config.projectBaseFolder = MusEGlobal::museUser + QString("/MusE"); MusEGlobal::config.startSong = ""; } app.instance()->setAttribute(Qt::AA_DontShowIconsInMenus, !MusEGlobal::config.showIconsInMenus); app.instance()->setAttribute(Qt::AA_DontUseNativeDialogs, !MusEGlobal::config.useNativeStandardDialogs); #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0) app.styleHints()->setShowShortcutsInContextMenus(true); #endif //================= // LADSPA paths: //================= bool found = false; if(MusEGlobal::config.pluginLadspaPathList.isEmpty()) { if(ladspa_path.isEmpty()) { MusEGlobal::config.pluginLadspaPathList << (MusEGlobal::museUser + QString("/ladspa")) << (MusEGlobal::museUser + QString("/.ladspa")) << QString("/usr/local/lib64/ladspa") << QString("/usr/local/lib/ladspa") << QString("/usr/lib64/ladspa") << QString("/usr/lib/ladspa"); } else { // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginLadspaPathList = ladspa_path.split(list_separator, Qt::SkipEmptyParts); #else MusEGlobal::config.pluginLadspaPathList = ladspa_path.split(list_separator, QString::SkipEmptyParts); #endif found = true; } } if(!found && qputenv("LADSPA_PATH", MusEGlobal::config.pluginLadspaPathList.join(list_separator).toLocal8Bit()) == 0) fprintf(stderr, "Error setting LADSPA_PATH\n"); //=============== // DSSI paths: //=============== found = false; if(MusEGlobal::config.pluginDssiPathList.isEmpty()) { if(dssi_path.isEmpty()) { MusEGlobal::config.pluginDssiPathList << (MusEGlobal::museUser + QString("/dssi")) << (MusEGlobal::museUser + QString("/.dssi")) << QString("/usr/local/lib64/dssi") << QString("/usr/local/lib/dssi") << QString("/usr/lib64/dssi") << QString("/usr/lib/dssi"); } else { // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginDssiPathList = dssi_path.split(list_separator, Qt::SkipEmptyParts); #else MusEGlobal::config.pluginDssiPathList = dssi_path.split(list_separator, QString::SkipEmptyParts); #endif found = true; } } if(!found && qputenv("DSSI_PATH", MusEGlobal::config.pluginDssiPathList.join(list_separator).toLocal8Bit()) == 0) fprintf(stderr, "Error setting DSSI_PATH\n"); //======================= // Win VST (*.dll) paths: //======================= found = false; if(MusEGlobal::config.pluginVstPathList.isEmpty()) { if(vst_path.isEmpty()) { MusEGlobal::config.pluginVstPathList << // On win, vst is usually where *.dll files are found. We don't want that with Linux *.so vst files. // Otherwise on Linux for example, vst is where Linux vst *.so files are found. #ifdef Q_OS_WIN // TODO: Refine this for Q_OS_WIN. Where exactly do we look though? (MusEGlobal::museUser + QString("/vst")) << (MusEGlobal::museUser + QString("/.vst")); #else (MusEGlobal::museUser + QString("/vst win 32bit")) << (MusEGlobal::museUser + QString("/.vst win 32bit")); #endif } else { // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginVstPathList = vst_path.split(list_separator, Qt::SkipEmptyParts); #else MusEGlobal::config.pluginVstPathList = vst_path.split(list_separator, QString::SkipEmptyParts); #endif found = true; } } if(!found && qputenv("VST_PATH", MusEGlobal::config.pluginVstPathList.join(list_separator).toLocal8Bit()) == 0) fprintf(stderr, "Error setting VST_PATH\n"); //======================= // LinuxVST (*.so) paths: //======================= found = false; if(MusEGlobal::config.pluginLinuxVstPathList.isEmpty()) { if(lxvst_path.isEmpty()) { if(vst_path.isEmpty()) { MusEGlobal::config.pluginLinuxVstPathList << // On win, vst is usually where *.dll files are found. We don't want that with Linux *.so vst files. // Otherwise on Linux for example, vst is where Linux vst *.so files are found. // On win, lxvst should be safe, likely where Linux vst *.so files might be found (if that's even a thing!). #ifndef Q_OS_WIN (MusEGlobal::museUser + QString("/vst")) << #endif (MusEGlobal::museUser + QString("/lxvst")) << #ifndef Q_OS_WIN (MusEGlobal::museUser + QString("/.vst")) << #endif (MusEGlobal::museUser + QString("/.lxvst")) << #ifndef Q_OS_WIN QString("/usr/local/lib64/vst") << #endif QString("/usr/local/lib64/lxvst") << #ifndef Q_OS_WIN QString("/usr/local/lib/vst") << #endif QString("/usr/local/lib/lxvst") << #ifndef Q_OS_WIN QString("/usr/lib64/vst") << #endif QString("/usr/lib64/lxvst") << #ifndef Q_OS_WIN QString("/usr/lib/vst") << #endif QString("/usr/lib/lxvst"); } else { // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginLinuxVstPathList = vst_path.split(list_separator, Qt::SkipEmptyParts); #else MusEGlobal::config.pluginLinuxVstPathList = vst_path.split(list_separator, QString::SkipEmptyParts); #endif found = true; } } else { // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginLinuxVstPathList = lxvst_path.split(list_separator, Qt::SkipEmptyParts); #else MusEGlobal::config.pluginLinuxVstPathList = lxvst_path.split(list_separator, QString::SkipEmptyParts); #endif found = true; } } if(!found && qputenv("LXVST_PATH", MusEGlobal::config.pluginLinuxVstPathList.join(list_separator).toLocal8Bit()) == 0) fprintf(stderr, "Error setting LXVST_PATH\n"); //============== // LV2 paths: //============== // Special for LV2: Since we use the recommended lilv_world_load_all() // not lilv_world_load_bundle(), LV2_PATH seems to be the only way to set paths. found = false; if(MusEGlobal::config.pluginLv2PathList.isEmpty()) { if(lv2_path.isEmpty()) { MusEGlobal::config.pluginLv2PathList << (MusEGlobal::museUser + QString("/lv2")) << (MusEGlobal::museUser + QString("/.lv2")) << QString("/usr/local/lib64/lv2") << QString("/usr/local/lib/lv2") << QString("/usr/lib64/lv2") << QString("/usr/lib/lv2"); } else { // QString::*EmptyParts is deprecated, use Qt::*EmptyParts, new as of 5.14. #if QT_VERSION >= 0x050e00 MusEGlobal::config.pluginLv2PathList = lv2_path.split(list_separator, Qt::SkipEmptyParts); #else MusEGlobal::config.pluginLv2PathList = lv2_path.split(list_separator, QString::SkipEmptyParts); #endif found = true; } } if(!found && qputenv("LV2_PATH", MusEGlobal::config.pluginLv2PathList.join(list_separator).toLocal8Bit()) == 0) fprintf(stderr, "Error setting LV2_PATH\n"); // BEGIN Parse command line options //---------------------------------- QString open_filename; AudioDriverSelect audioType = DriverConfigSetting; bool force_plugin_rescan = false; bool dont_plugin_rescan = false; // A block because we don't want ths hanging around. Use it then lose it. { QCommandLineParser parser; QString errorMessage; switch (parseCommandLine(parser, &errorMessage, open_filename, audioType, force_plugin_rescan, dont_plugin_rescan)) { case CommandLineOk: break; case CommandLineError: fputs(qPrintable(errorMessage), stderr); fputs("\n\n", stderr); fputs(qPrintable(parser.helpText()), stderr); printExtraHelpText(); #ifdef HAVE_LASH if(lash_args) lash_args_destroy(lash_args); #endif return 1; case CommandLineVersionRequested: printf("%s %s\n", qPrintable(QCoreApplication::applicationName()), qPrintable(QCoreApplication::applicationVersion())); #ifdef HAVE_LASH if(lash_args) lash_args_destroy(lash_args); #endif return 0; case CommandLineHelpRequested: // Works OK, but we want extra help text. Also the lash args destroy thingy... //parser.showHelp(); //Q_UNREACHABLE(); fputs(qPrintable(parser.helpText()), stdout); printExtraHelpText(); #ifdef HAVE_LASH if(lash_args) lash_args_destroy(lash_args); #endif return 0; } } // END Parse command line options //---------------------------------- // Set some AL library namespace debug flags as well. // Make sure the AL namespace variables mirror our variables. AL::debugMsg = MusEGlobal::debugMsg; AL::denormalBias = MusEGlobal::denormalBias; AL::division = MusEGlobal::config.division; AL::sampleRate = MusEGlobal::sampleRate; AL::mtcType = MusEGlobal::mtcType; // REMOVE Tim. py. Removed. TEST Keep this? Think not. It was for getting the last option (the filename) // when we were using getopt() but now we use QCommandLineParser. Un-needed ? // argc_copy -= optind; // ++argc_copy; srand(time(nullptr)); // initialize random number generator //signal(SIGCHLD, catchSignal); // interferes with initVST(). see also app.cpp, function catchSignal() static QTranslator translator(nullptr); { QString locale(QLocale::system().name()); if (locale_override.length() >0 ) locale = locale_override; if (locale != "C") { QString loc("muse_"); loc += locale; if (!translator.load(loc, QString("."))) { QString lp(MusEGlobal::museGlobalShare); lp += QString("/locale"); if (!translator.load(loc, lp)) { fprintf(stderr, "no locale <%s>/<%s>\n", loc.toLatin1().constData(), lp.toLatin1().constData()); } } app.installTranslator(&translator); } QLocale def_loc(locale); QLocale::setDefault(def_loc); } fprintf(stderr, "LOCALE %s\n",QLocale().name().toLatin1().data()); if (QLocale().name() == "de" || locale_override == "de") { fprintf(stderr, "locale de - setting 'note h is B' override parameter.\n"); MusEGlobal::hIsB = false; } QApplication::addLibraryPath(MusEGlobal::museGlobalLib + "/qtplugins"); if (MusEGlobal::debugMsg) { QStringList list = app.libraryPaths(); QStringList::Iterator it = list.begin(); fprintf(stderr, "QtLibraryPath:\n"); while(it != list.end()) { fprintf(stderr, " <%s>\n", (*it).toLatin1().constData()); ++it; } } // NOTE: Set the stylesheet and style as early as possible! // Any later invites trouble - typically the colours may be off, // but currently with Breeze or Oxygen, MDI sub windows may be frozen! // Working with Breeze maintainer to fix problem... 2017/06/06 Tim. // MusEGui::updateThemeAndStyle(); qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Load theme..."; MusEGui::loadTheme(MusEGlobal::config.theme); // MusEGui::loadThemeColors(MusEGlobal::config.theme); //------------------------------------------------------- // BEGIN SHOW MUSE SPLASH SCREEN //------------------------------------------------------- QString splash_prefix; QSplashScreen* muse_splash = nullptr; if (MusEGlobal::config.showSplashScreen) { QPixmap splsh(MusEGlobal::museGlobalShare + "/splash.jpg"); if (!splsh.isNull()) { muse_splash = new QSplashScreen(splsh, Qt::WindowStaysOnTopHint); muse_splash->setAttribute(Qt::WA_DeleteOnClose); // Possibly also Qt::X11BypassWindowManagerHint splash_prefix = QString("MusE ") + QString(VERSION) + ":"; muse_splash->show(); } } //------------------------------------------------------- // END SHOW MUSE SPLASH SCREEN //------------------------------------------------------- //------------------------------------------------------- // BEGIN Plugin scanning //------------------------------------------------------- if(muse_splash) { muse_splash->showMessage(splash_prefix + QString(" Creating plugin cache files..."), Qt::AlignLeft|Qt::AlignBottom, Qt::yellow); qApp->processEvents(); } qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Scan plugins..."; bool do_rescan = false; if(force_plugin_rescan) { force_plugin_rescan = false; if(!plugin_rescan_already_done) { do_rescan = true; plugin_rescan_already_done = true; } } if(MusEGlobal::config.pluginCacheTriggerRescan) { do_rescan = true; // Done with rescan trigger. Reset it now. MusEGlobal::config.pluginCacheTriggerRescan = false; } if (MusEGlobal::debugMsg) qDebug() << "Cache path for plugin scan:" << new_plugin_cache_path; // Scan all known plugins from the cache file, or if it does not exist // create the cache file by reading plugins in a safe 'sandbox'. MusEPlugin::PluginScanInfoStruct::PluginType_t types = MusEPlugin::PluginScanInfoStruct::PluginTypeNone; if(MusEGlobal::loadPlugins) types |= MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA; if(MusEGlobal::loadMESS) types |= MusEPlugin::PluginScanInfoStruct::PluginTypeMESS; if(MusEGlobal::loadVST) types |= MusEPlugin::PluginScanInfoStruct::PluginTypeVST; if(MusEGlobal::loadNativeVST) types |= MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST; if(MusEGlobal::loadDSSI) types |= (MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI | MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST); if(MusEGlobal::loadLV2) types |= MusEPlugin::PluginScanInfoStruct::PluginTypeLV2; types |= MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown; MusEPlugin::checkPluginCacheFiles(new_plugin_cache_path, // List of plugins to scan into and write to cache files from. &MusEPlugin::pluginList, // Don't bother reading any port information that might exist in the cache. false, // Whether to force recreation. do_rescan, // Whether to NOT recreate. dont_plugin_rescan, // When creating, where to find the application's own plugins. MusEGlobal::museGlobalLib, // Plugin types to check. types, // Debug messages. MusEGlobal::debugMsg); // Done with rescan trigger. Reset it now. if(do_rescan) MusEGlobal::config.pluginCacheTriggerRescan = false; //------------------------------------------------------- // END Plugin scanning //------------------------------------------------------- qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Init audio..."; AL::initDsp(); if(muse_splash) { muse_splash->showMessage(splash_prefix + QString(" Initializing audio system..."), Qt::AlignLeft|Qt::AlignBottom, Qt::yellow); qApp->processEvents(); } MusECore::initAudio(); MusEGui::initIcons(MusEGlobal::config.cursorSize, MusEGlobal::museGlobalShare + "/themes/" + MusEGlobal::config.theme, MusEGlobal::configPath + "/themes/" + MusEGlobal::config.theme); if (MusEGlobal::loadMESS) MusECore::initMidiSynth(); // Need to do this now so that Add Track -> Synth menu is populated when MusE is created. MusEGlobal::muse = new MusEGui::MusE(); app.setMuse(MusEGlobal::muse); MusEGui::init_function_dialogs(); MusEGui::retranslate_function_dialogs(); if(muse_splash) { muse_splash->showMessage(splash_prefix + QString(" Initializing audio driver..."), Qt::AlignLeft|Qt::AlignBottom, Qt::yellow); qApp->processEvents(); } if (MusEGlobal::config.useDenormalBias) { fprintf(stderr, "Denormal protection enabled.\n"); } if (MusEGlobal::debugMsg) { fprintf(stderr, "global lib: <%s>\n", MusEGlobal::museGlobalLib.toLatin1().constData()); fprintf(stderr, "global share: <%s>\n", MusEGlobal::museGlobalShare.toLatin1().constData()); fprintf(stderr, "muse home: <%s>\n", MusEGlobal::museUser.toLatin1().constData()); fprintf(stderr, "project dir: <%s>\n", MusEGlobal::museProject.toLatin1().constData()); fprintf(stderr, "user instruments: <%s>\n", MusEGlobal::museUserInstruments.toLatin1().constData()); } //rlimit lim; getrlimit(RLIMIT_RTPRIO, &lim); //fprintf(stderr, "RLIMIT_RTPRIO soft:%d hard:%d\n", lim.rlim_cur, lim.rlim_max); // Reported 80, 80 even with non-RT kernel. if (MusEGlobal::realTimePriority < sched_get_priority_min(SCHED_FIFO)) MusEGlobal::realTimePriority = sched_get_priority_min(SCHED_FIFO); else if (MusEGlobal::realTimePriority > sched_get_priority_max(SCHED_FIFO)) MusEGlobal::realTimePriority = sched_get_priority_max(SCHED_FIFO); // If we requested to force the midi thread priority... if(MusEGlobal::midiRTPrioOverride > 0) { if (MusEGlobal::midiRTPrioOverride < sched_get_priority_min(SCHED_FIFO)) MusEGlobal::midiRTPrioOverride = sched_get_priority_min(SCHED_FIFO); else if (MusEGlobal::midiRTPrioOverride > sched_get_priority_max(SCHED_FIFO)) MusEGlobal::midiRTPrioOverride = sched_get_priority_max(SCHED_FIFO); } #ifdef HAVE_LASH bool using_jack = false; #endif if (MusEGlobal::debugMode) { MusEGlobal::realTimeScheduling = false; MusECore::initDummyAudio(); } else if (audioType == DummyAudioOverride) { fprintf(stderr, "Force Dummy Audio driver\n"); MusEGlobal::realTimeScheduling = true; MusECore::initDummyAudio(); } #ifdef HAVE_RTAUDIO else if (audioType == RtAudioOverride) { fprintf(stderr, "Force RtAudio with Pulse Backend\n"); MusEGlobal::realTimeScheduling = true; if(MusECore::initRtAudio(true)) fallbackDummy(); else fprintf(stderr, "Using rtAudio\n"); } #endif else if (audioType == JackAudioOverride) { if(MusECore::initJackAudio()) fallbackDummy(); else { #ifdef HAVE_LASH using_jack = true; #endif fprintf(stderr, "...Using Jack\n"); } } else if (audioType == DriverConfigSetting) { fprintf(stderr, "Select audio device from configuration : %d\n", MusEGlobal::config.deviceAudioBackend); switch (MusEGlobal::config.deviceAudioBackend) { case MusEGlobal::DummyAudio: { fprintf(stderr, "User DummyAudio backend - selected through configuration\n"); MusEGlobal::realTimeScheduling = true; MusECore::initDummyAudio(); break; } case MusEGlobal::RtAudioAlsa: case MusEGlobal::RtAudioOss: // case MusEGlobal::RtAudioJack: case MusEGlobal::RtAudioChoice: case MusEGlobal::RtAudioPulse: { fprintf(stderr, "User RtAudio backend - backend selected through configuration: "); if(MusEGlobal::config.deviceAudioBackend >= MusEGlobal::numRtAudioDevices) fprintf(stderr, "Unknown"); else fprintf(stderr, "%s", MusEGlobal::selectableAudioBackendDevices[MusEGlobal::config.deviceAudioBackend]. toLatin1().constData()); fprintf(stderr, "\n"); MusEGlobal::realTimeScheduling = true; #ifdef HAVE_RTAUDIO if(MusECore::initRtAudio()) fallbackDummy(); else fprintf(stderr, "Using rtAudio\n"); #else fallbackDummy(); #endif break; } case MusEGlobal::JackAudio: { fprintf(stderr, "User JackAudio backend - backend selected through configuration\n"); if (MusECore::initJackAudio()) { MusEGlobal::realTimeScheduling = true; // Force default Pulse. #ifdef HAVE_RTAUDIO if(MusECore::initRtAudio(true)) fallbackDummy(); else fprintf(stderr, "Using rtAudio Pulse\n"); #else fallbackDummy(); #endif } else { #ifdef HAVE_LASH using_jack = true; #endif fprintf(stderr, "Using Jack\n"); } break; } } } MusEGlobal::realTimeScheduling = MusEGlobal::audioDevice->isRealtime(); // ??? With Jack2 this reports true even if it is not running realtime. // Jack says: "Cannot use real-time scheduling (RR/10)(1: Operation not permitted)". The kernel is non-RT. // I cannot seem to find a reliable answer to the question, even with dummy audio and system calls. // setup the prefetch fifo length now that the segmentSize is known MusEGlobal::fifoLength = 131072 / MusEGlobal::segmentSize; MusECore::initAudioPrefetch(); // Set up the wave module now that sampleRate and segmentSize are known. MusECore::SndFile::initWaveModule( &MusEGlobal::sndFiles, &MusEGlobal::audioConverterPluginList, &MusEGlobal::defaultAudioConverterSettings, MusEGlobal::sampleRate, MusEGlobal::segmentSize); if(muse_splash) { muse_splash->showMessage(splash_prefix + QString(" Initializing midi devices..."), Qt::AlignLeft|Qt::AlignBottom, Qt::yellow); qApp->processEvents(); } qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Init MIDI..."; // REMOVE Tim. startup. Removed 2019/02/21. It's been six years since 1.9.9.5 release. // Remove this waiting part at some point if we're all good... // // // WARNING Must do it this way. Call registerClient long AFTER Jack client // // is created and MusE ALSA client is created (in initMidiDevices), // // otherwise random crashes can occur within Jack <= 1.9.8. // // Fixed in Jack 1.9.9. Tim. // This initMidiDevices will automatically initialize the midiSeq sequencer thread, // but not start it - that's a bit later on. MusECore::initMidiDevices(); // // Wait until things have settled. One second seems OK so far. // for(int t = 0; t < 100; ++t) // usleep(10000); // Now it is safe to call registerClient. MusEGlobal::audioDevice->registerClient(); MusECore::initMidiController(); MusECore::initMidiInstruments(); MusECore::initMidiPorts(); if(muse_splash) { muse_splash->showMessage(splash_prefix + QString(" Initializing plugins..."), Qt::AlignLeft|Qt::AlignBottom, Qt::yellow); qApp->processEvents(); } qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Init plugins..."; if (MusEGlobal::loadPlugins) MusECore::initPlugins(); qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Init VST plugins..."; if (MusEGlobal::loadVST) MusECore::initVST(); qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Init native VST plugins..."; if (MusEGlobal::loadNativeVST) MusECore::initVST_Native(); qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Init DSSI plugins..."; if(MusEGlobal::loadDSSI) MusECore::initDSSI(); #ifdef LV2_SUPPORT qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Init LV2 plugins..."; if(MusEGlobal::loadLV2) MusECore::initLV2(); #endif // Now that all the plugins are done loading from the global plugin cache list, // we are done with it. Clear it to free up memory. // TODO Future: Will need to keep it around if we ever switch to using the list all the time // instead of separate global plugin and synth lists. MusEPlugin::pluginList.clear(); qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Init OSC / metronome..."; MusECore::initOSC(); MusECore::initMetronome(); const QString metro_presets = MusEGlobal::museGlobalShare + QString("/metronome"); MusECore::initMetronomePresets(metro_presets, &MusEGlobal::metroAccentPresets, MusEGlobal::debugMsg); // If the global metronome accent settings are empty, it is unlikely the user did that, or wants that. // More likely it indicates this is a first-time init of the global settings. // In any case, if empty fill the global metronome accent settings with factory presets. if(MusEGlobal::metroGlobalSettings.metroAccentsMap && MusEGlobal::metroGlobalSettings.metroAccentsMap->empty()) { // Fill with defaults. MusEGlobal::metroAccentPresets.defaultAccents( MusEGlobal::metroGlobalSettings.metroAccentsMap, MusECore::MetroAccentsStruct::FactoryPreset); } MusECore::initWavePreview(MusEGlobal::segmentSize); MusECore::enumerateJackMidiDevices(); #ifdef HAVE_LASH qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Init LASH..."; { MusEGui::lash_client = 0; if(MusEGlobal::useLASH) { if(muse_splash) { muse_splash->showMessage(splash_prefix + QString(" Initializing LASH support..."), Qt::AlignLeft|Qt::AlignBottom, Qt::yellow); qApp->processEvents(); } int lash_flags = LASH_Config_File; const char *muse_name = PACKAGE_NAME; MusEGui::lash_client = lash_init (lash_args, muse_name, lash_flags, LASH_PROTOCOL(2,0)); #ifdef ALSA_SUPPORT if(MusECore::alsaSeq) lash_alsa_client_id (MusEGui::lash_client, snd_seq_client_id (MusECore::alsaSeq)); #endif //if (audioType != DummyAudio) { if (using_jack) { const char *jack_name = MusEGlobal::audioDevice->clientName(); lash_jack_client_name (MusEGui::lash_client, jack_name); } } if(lash_args) lash_args_destroy(lash_args); } #endif /* HAVE_LASH */ #ifndef _WIN32 if (!MusEGlobal::debugMode) { if (mlockall(MCL_CURRENT | MCL_FUTURE)) perror("WARNING: Cannot lock memory:"); } #endif qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Populating Track context menu..."; if(muse_splash) { muse_splash->showMessage(splash_prefix + QString(" Populating Track context menu..."), Qt::AlignLeft|Qt::AlignBottom, Qt::yellow); qApp->processEvents(); } MusEGlobal::muse->populateAddTrack(); // could possibly be done in a thread. qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Show GUI..."; MusEGlobal::muse->show(); // Let the configuration settings take effect. Do not save. MusEGlobal::muse->changeConfig(false); // Set style and stylesheet, and do not force the style //MusEGui::updateThemeAndStyle(); // Works better if called just after app created, above. MusEGlobal::muse->seqStart(); MusEGlobal::muse->initStatusBar(); // If the sequencer object was created, report timing. if(MusEGlobal::midiSeq) MusEGlobal::midiSeq->checkAndReportTimingResolution(); //-------------------------------------------------- // Set the audio device sync timeout value. //-------------------------------------------------- // Enforce a 30 second timeout. // TODO: Split this up and have user adjustable normal (2 or 10 second default) value, // plus a contribution from the total required precount time. // Too bad we likely can't set it dynamically in the audio sync callback. // NOTE: This is also enforced casually in Song:seqSignal after a stop, start, or seek. MusEGlobal::audioDevice->setSyncTimeout(30000000); //-------------------------------------------------- // Auto-fill the midi ports, if appropriate. // Only if NOT actually opening an existing file. // FIXME: Maybe check if it's a .med file (song may populate) // or .mid file (always populate) or .wav file etc. //-------------------------------------------------- if(MusEGlobal::populateMidiPortsOnStart && ((!open_filename.isEmpty() && !QFile(open_filename).exists()) || (open_filename.isEmpty() && (MusEGlobal::config.startMode == 1 || MusEGlobal::config.startMode == 2) && !MusEGlobal::config.startSongLoadConfig))) MusECore::populateMidiPorts(); if(muse_splash) { muse_splash->showMessage(splash_prefix + QString(" Click to close splash screen..."), Qt::AlignLeft|Qt::AlignBottom, Qt::yellow); // From this point on, slap a timer on it so that it stays up for few seconds, // since closing it now might be too short display time. QTimer::singleShot(3000, muse_splash, SLOT(close())); } qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Load default project"; //-------------------------------------------------- // Load the default song. //-------------------------------------------------- // When restarting, override with the last project file name used. if(last_project_filename.isEmpty()) { MusEGlobal::muse->loadDefaultSong(open_filename, false, false); } else { MusEGlobal::muse->loadDefaultSong( last_project_filename, last_project_was_template, last_project_loaded_config); } QTimer::singleShot(100, MusEGlobal::muse, SLOT(showDidYouKnowDialogIfEnabled())); //-------------------------------------------------- // Start the application... //-------------------------------------------------- qDebug() << "->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Start application loop..."; qDebug() << "Total start-up time:" << timer.elapsed() << "ms"; rv = app.exec(); //-------------------------------------------------- // ... Application finished. //-------------------------------------------------- if(MusEGlobal::debugMsg) fprintf(stderr, "app.exec() returned:%d\nDeleting main MusE object\n", rv); if (MusEGlobal::loadPlugins) { for (MusECore::iPlugin i = MusEGlobal::plugins.begin(); i != MusEGlobal::plugins.end(); ++i) delete (*i); MusEGlobal::plugins.clear(); } MusECore::exitWavePreview(); #ifdef LV2_SUPPORT if(MusEGlobal::loadLV2) MusECore::deinitLV2(); #endif // In case the sequencer object is still alive, make sure to destroy it now. MusECore::exitMidiSequencer(); // Grab the restart flag before deleting muse. is_restarting = MusEGlobal::muse->restartingApp(); if (is_restarting) qDebug() << "\n->" << qPrintable(QTime::currentTime().toString("hh:mm:ss.zzz")) << "Restarting application..."; { // If the current project file name exists, set the last_project_filename // variable so that if restarting, it starts with that file. // This should be OK since fresh untitled unsaved songs will not have a // file yet, they will have a unique file name that does not exist until // saved, so it will either use the existing open_filename, or default // to normal operation (template, last song, or blank). const QString s = MusEGlobal::muse->lastProjectFilePath(); const QFileInfo fi(s); if(fi.exists()) { last_project_filename = s; last_project_was_template = MusEGlobal::muse->lastProjectWasTemplate(); last_project_loaded_config = MusEGlobal::muse->lastProjectLoadedConfig(); } else { last_project_filename.clear(); last_project_was_template = false; last_project_loaded_config = false; } } // Now delete the application. delete MusEGlobal::muse; MusEGlobal::muse = nullptr; // These are owned by muse and deleted above. Reset to zero now. MusEGlobal::undoRedo = nullptr; MusEGlobal::undoAction = nullptr; MusEGlobal::redoAction = nullptr; // Reset the option index. // NOTE: See optind manual for special resetting values. // Traditionally 1 is set, but here we may need GNU specific 0. //optind = 0; // Free the working copies of the arguments. if(argv_copy) { for(int i = 0; i < argument_count; ++i) { if(argv_copy[i]) free(argv_copy[i]); } free(argv_copy); } // Reset these before restarting, seems to work better, // makes a difference with the MDI freezing problem, above. app.setStyleSheet(""); // app.setStyle(MusEGlobal::config.style); // Reset the recently opened list. MusEGui::projectRecentList.clear(); // Clear and delete these. if(MusEGlobal::defaultAudioConverterSettings) delete MusEGlobal::defaultAudioConverterSettings; MusEGlobal::defaultAudioConverterSettings = nullptr; MusEGlobal::audioConverterPluginList.clearDelete(); // Clear the mixer configurations. MusEGlobal::config.mixer1.stripOrder.clear(); MusEGlobal::config.mixer1.stripVisibility.clear(); MusEGlobal::config.mixer1.stripConfigList.clear(); MusEGlobal::config.mixer2.stripOrder.clear(); MusEGlobal::config.mixer2.stripVisibility.clear(); MusEGlobal::config.mixer2.stripConfigList.clear(); } //============================================ // END Restart loop. For (re)starting the app. //============================================ if(MusEGlobal::debugMsg) fprintf(stderr, "Finished! Exiting main, return value:%d\n", rv); return rv; } muse-4.0.0/src/muse/marker/000077500000000000000000000000001404105311000154725ustar00rootroot00000000000000muse-4.0.0/src/muse/marker/CMakeLists.txt000066400000000000000000000035641404105311000202420ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( marker_mocs markerview.h ) ## ## List of source files to compile ## file (GLOB marker_source_files marker.cpp markerview.cpp ) ## ## Define target ## add_library ( marker ${MODULES_BUILD} ${marker_source_files} # ${marker_ui_headers} ${marker_mocs} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${marker_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( marker PROPERTIES OUTPUT_NAME muse_marker ) ## ## Linkage ## target_link_libraries ( marker ${QT_LIBRARIES} icons ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS marker DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/marker/marker.cpp000066400000000000000000000130001404105311000174510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: marker.cpp,v 1.2 2003/12/10 18:34:22 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "marker.h" namespace MusECore { // Static. EventID_t Marker::_idGen = 0; //--------------------------------------------------------- // read //--------------------------------------------------------- void Marker::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: xml.unknown("Marker"); break; case Xml::Attribut: if (tag == "tick") { setType(TICKS); setTick(xml.s2().toUInt()); } else if (tag == "frame") { setType(FRAMES); setFrame(xml.s2().toUInt()); } else if (tag == "lock") // Obsolete. { setType(xml.s2().toInt() ? FRAMES:TICKS); } else if (tag == "name") { _name = xml.s2(); } break; case Xml::TagEnd: if (xml.s1() == "marker") return; default: break; } } } //--------------------------------------------------------- // assign // Assigns the members of the given marker to this one, EXCEPT for the ID. // Returns this marker. //--------------------------------------------------------- Marker& Marker::assign(const Marker& m) { setCurrent(m.current()); setName(m.name()); setTick(m.tick()); setType(m.type()); return *this; } //--------------------------------------------------------- // copy // Creates a copy of this marker but with a new ID. //--------------------------------------------------------- Marker Marker::copy() const { return Marker().assign(*this); } Marker* MarkerList::add(const Marker& marker) { iMarker i = MixedPosList_t::add(Marker(marker)); return &i->second; } Marker* MarkerList::add(const QString& s, unsigned t, bool lck) { Marker marker(s); marker.setType(lck ? Pos::FRAMES : Pos::TICKS); marker.setTick(t); iMarker i = MixedPosList_t::add(marker); return &i->second; } //--------------------------------------------------------- // write //--------------------------------------------------------- void MarkerList::write(int level, Xml& xml) const { for (ciMarker i = begin(); i != end(); ++i) { const Marker& m = i->second; if(m.type()==Pos::TICKS) xml.put(level, "", m.tick(), Xml::xmlString(m.name()).toLatin1().constData()); else if(m.type()==Pos::FRAMES) xml.put(level, "", m.frame(), Xml::xmlString(m.name()).toLatin1().constData()); } } //--------------------------------------------------------- // remove //--------------------------------------------------------- void MarkerList::remove(Marker* m) { for (iMarker i = begin(); i != end(); ++i) { Marker* mm = &i->second; if (mm == m) { erase(i); return; } } fprintf(stderr, "MarkerList::remove(): marker not found\n"); } void MarkerList::remove(const Marker& m) { const QString& s = m.name(); const EventID_t id = m.id(); std::pair rng = equal_range(m.tick()); for(iMarker i = rng.first; i != rng.second; ++i) { const Marker& mm = i->second; if(mm.id() == id && mm.name() == s) { erase(i); return; } } fprintf(stderr, "MarkerList::remove(): marker not found\n"); } iMarker MarkerList::findId(EventID_t id) { for(iMarker i = begin(); i != end(); ++i) if(i->second.id() == id) return i; return end(); } ciMarker MarkerList::findId(EventID_t id) const { for(ciMarker i = begin(); i != end(); ++i) if(i->second.id() == id) return i; return end(); } } // namespace MusECore muse-4.0.0/src/muse/marker/marker.h000066400000000000000000000057311404105311000171320ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: marker.h,v 1.2 2003/12/15 11:41:00 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MARKER_H__ #define __MARKER_H__ #include #include #include "xml.h" #include "pos.h" #include "type_defs.h" #include "mixed_pos_list.h" namespace MusECore { //--------------------------------------------------------- // Marker //--------------------------------------------------------- class Marker : public Pos { static EventID_t _idGen; EventID_t newId() { return _idGen++; } EventID_t _id; QString _name; bool _current; public: Marker() : _id(newId()), _name(""), _current(false) {} Marker(const QString& s, bool cur = false) : _id(newId()), _name(s), _current(cur) {} // Assigns the members of the given marker to this one, EXCEPT for the ID. // Returns this marker. Marker& assign(const Marker&); // Creates a copy of this marker but with a new ID. Marker copy() const; void read(Xml&); EventID_t id() const { return _id; } const QString name() const { return _name; } void setName(const QString& s) { _name = s; } bool current() const { return _current; } void setCurrent(bool f) { _current = f; } }; //--------------------------------------------------------- // MarkerList //--------------------------------------------------------- class MarkerList : public MixedPosList_t > { public: MarkerList() : MixedPosList_t(Pos::TICKS) { } // Normally to be called from the audio thread only. Marker* add(const Marker& m); Marker* add(const QString& s, unsigned t, bool lck); void remove(Marker*); void remove(const Marker&); MarkerList::const_iterator findId(EventID_t id) const; // Slow, index t is not known MarkerList::iterator findId(EventID_t id); // Slow, index t is not known void write(int, Xml&) const; }; typedef MarkerList::iterator iMarker; typedef MarkerList::const_iterator ciMarker; } // namespace MusECore #endif muse-4.0.0/src/muse/marker/markerview.cpp000066400000000000000000000546351404105311000203670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: markerview.cpp,v 1.7.2.6 2009/08/25 20:28:45 spamatica Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "sig.h" #include "markerview.h" #include "xml.h" #include "globals.h" #include "sync.h" #include "icons.h" #include "song.h" #include "posedit.h" #include "audio.h" #include "gconfig.h" #include "pos.h" #include "shortcuts.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace MusEGui { enum { COL_TICK = 0, COL_SMPTE, COL_LOCK, COL_NAME }; //--------------------------------------------------------- // tick //--------------------------------------------------------- unsigned MarkerItem::tick() const { return _marker.tick(); } //--------------------------------------------------------- // name //--------------------------------------------------------- const QString MarkerItem::name() const { return _marker.name(); } //--------------------------------------------------------- // lock //--------------------------------------------------------- bool MarkerItem::lock() const { return _marker.type() == MusECore::Pos::FRAMES; } MusECore::Marker MarkerItem::marker() const { return _marker; } void MarkerItem::setMarker(const MusECore::Marker& m) { _marker = m; setText(COL_NAME, m.name()); setPos(m); setLock(m.type() == MusECore::Pos::FRAMES); } //--------------------------------------------------------- // MarkerItem //--------------------------------------------------------- MarkerItem::MarkerItem(QTreeWidget* parent, const MusECore::Marker& m) : QTreeWidgetItem(parent) { setMarker(m); } MarkerItem::MarkerItem(const MusECore::Marker& m) : QTreeWidgetItem() { setMarker(m); } //--------------------------------------------------------- // setName //--------------------------------------------------------- void MarkerItem::setName(const QString& s) { setText(COL_NAME, s); } //--------------------------------------------------------- // setLock //--------------------------------------------------------- void MarkerItem::setLock(bool lck) { setIcon(COL_LOCK, lck ? QIcon(*lockSVGIcon) : QIcon()); } //--------------------------------------------------------- // setPos //--------------------------------------------------------- void MarkerItem::setPos(const MusECore::Pos& v) { QString s; int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(v.tick(), &bar, &beat, &tick); s = QString("%1.%2.%3") .arg(bar + 1, 4, 10, QLatin1Char('0')) .arg(beat + 1, 2, 10, QLatin1Char('0')) .arg(tick, 3, 10, QLatin1Char('0')); setText(COL_TICK, s); int hour, min, sec, frame, subframe; v.msf(&hour, &min, &sec, &frame, &subframe); s = QString("%1:%2:%3:%4:%5") .arg(hour, 2, 10, QLatin1Char('0')) .arg(min, 2, 10, QLatin1Char('0')) .arg(sec, 2, 10, QLatin1Char('0')) .arg(frame, 2, 10, QLatin1Char('0')) .arg(subframe, 2, 10, QLatin1Char('0')); setText(COL_SMPTE, s); } //--------------------------------------------------------- // MarkerView //--------------------------------------------------------- MarkerView::MarkerView(QWidget* parent) : QWidget(parent) { setObjectName("MarkerView"); QAction* markerAdd = new QAction(*plusSVGIcon, tr("Add marker"), this); markerAdd->setShortcut(Qt::Key_A); markerAdd->setToolTip(tr("Add marker") + " (A)"); addAction(markerAdd); markerAdd->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(markerAdd, SIGNAL(triggered()), SLOT(addMarker())); QAction* markerDelete = new QAction(*minusSVGIcon, tr("Delete marker"), this); markerDelete->setShortcut(shortcuts[SHRT_DELETE].key); markerDelete->setToolTip(tr("Delete marker") + " (" + markerDelete->shortcut().toString() + ")"); addAction(markerDelete); markerDelete->setShortcutContext(Qt::WidgetWithChildrenShortcut); connect(markerDelete, SIGNAL(triggered()), SLOT(deleteMarker())); // Toolbars --------------------------------------------------------- QToolBar* edit = new QToolBar(tr("Edit tools")); edit->setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize)); edit->setObjectName("marker edit tools"); edit->addAction(markerAdd); edit->addAction(markerDelete); //--------------------------------------------------- // master //--------------------------------------------------- QVBoxLayout* vbox = new QVBoxLayout(this); table = new QTreeWidget(this); table->setAllColumnsShowFocus(true); table->setSelectionMode(QAbstractItemView::SingleSelection); table->setIndentation(2); table->setFocusPolicy(Qt::StrongFocus); QStringList columnnames; columnnames << tr("Bar:Beat:Tick") << tr("Hr:Mn:Sc:Fr:Sf") << tr("Lock") << tr("Description"); table->setHeaderLabels(columnnames); table->setColumnWidth(2, 40); table->header()->setStretchLastSection(true); connect(table, SIGNAL(itemSelectionChanged()), SLOT(markerSelectionChanged())); connect(table, SIGNAL(itemClicked(QTreeWidgetItem*, int)), SLOT(clicked(QTreeWidgetItem*))); // QGroupBox* props = new QGroupBox(tr("Marker Properties")); QHBoxLayout *hbox = new QHBoxLayout; ///editTick = new PosEdit; editTick = new PosEdit; editTick->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); editTick->setToolTip(tr("Edit tick")); editSMPTE = new PosEdit; editSMPTE->setSmpte(true); editSMPTE->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); editSMPTE->setToolTip(tr("Edit SMPTE timecode")); lock = new QToolButton; lock->setIcon(*lockSVGIcon); lock->setCheckable(true); lock->setToolTip(tr("Toggle timecode editing")); editName = new QLineEdit; editName->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); editName->setToolTip(tr("Edit marker description")); editName->setPlaceholderText(tr("")); hbox->addWidget(editTick); hbox->addWidget(editSMPTE); hbox->addWidget(lock); hbox->addWidget(editName); // props->setLayout(hbox); connect(editName, SIGNAL(editingFinished()), SLOT(nameChanged())); connect(editTick, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(tickChanged(const MusECore::Pos&))); connect(editSMPTE, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(tickChanged(const MusECore::Pos&))); connect(editSMPTE, SIGNAL(valueChanged(const MusECore::Pos&)), editTick, SLOT(setValue(const MusECore::Pos&))); connect(editTick, SIGNAL(valueChanged(const MusECore::Pos&)), editSMPTE, SLOT(setValue(const MusECore::Pos&))); connect(lock, SIGNAL(toggled(bool)), SLOT(lockChanged(bool))); connect(MusEGlobal::song, SIGNAL(markerChanged(int)), SLOT(markerChanged(int))); vbox->addWidget(edit); vbox->addLayout(hbox); // vbox->addWidget(props); vbox->addWidget(table); //--------------------------------------------------- // Rest //--------------------------------------------------- connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); updateList(); // work around for probable QT/WM interaction bug. // for certain window managers, e.g xfce, this window is // is displayed although not specifically set to show(); // bug: 2811156 Softsynth GUI unclosable with XFCE4 (and a few others) // Nov 21, 2012 Hey this causes the thing not to open at all, EVER, on Lubuntu and some others! // And we had a request to remove this from a knowledgable tester. REMOVE Tim. ///show(); ///hide(); qApp->installEventFilter(this); } //--------------------------------------------------------- // MarkerView //--------------------------------------------------------- MarkerView::~MarkerView() { } bool MarkerView::eventFilter(QObject*, QEvent *e) { if (!table->hasFocus()) return false; if (e->type() == QEvent::Shortcut) { QShortcutEvent* sev = static_cast(e); if (sev->isAmbiguous()) { for (const auto& action : actions()) { if (action->shortcut() == sev->key()) { action->trigger(); return true; } } } } return false; } //--------------------------------------------------------- // addMarker //--------------------------------------------------------- void MarkerView::addMarker() { MusEGlobal::song->addMarker(QString(""), MusEGlobal::song->cpos(), false); } void MarkerView::addMarker(unsigned i) { // Changed p3.3.43 Let MusECore::Song::addMarker emit markerChanged(MARKER_ADD) // and handle it in MarkerView::markerChanged(int) //MusECore::Marker* m = MusEGlobal::song->addMarker(QString(""), i, false); //MarkerItem* newItem = new MarkerItem(table, m); //table->setSelected(newItem, true); // MusEGlobal::song->addMarker(QString(""), i, false); } //--------------------------------------------------------- // deleteMarker //--------------------------------------------------------- void MarkerView::deleteMarker() { MarkerItem* item = (MarkerItem*)table->currentItem(); if (item) { table->blockSignals(true); MusEGlobal::song->removeMarker(item->marker()); table->blockSignals(false); // Removed p3.3.43 Let MusECore::Song::removeMarker emit markerChanged(MARKER_REMOVE) // and handle it in MarkerView::markerChanged(int) //delete item; } } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MarkerView::songChanged(MusECore::SongChangedStruct_t flags) { // Try to minimize the work. Rebuild the list only if required. // The simpler updateList() just cannot handle this complex situation, // due to position comparisons ALREADY being equal etc. if(flags & (SC_MARKERS_REBUILT | SC_TEMPO | SC_MASTER | SC_SIG // Required so that bar/beat/tick of listed items are shown correctly. )) rebuildList(); // Can we get away with just an update? else if(flags & (SC_MARKER_INSERTED | SC_MARKER_REMOVED | SC_MARKER_MODIFIED)) updateList(); } //--------------------------------------------------------- // rebuildList //--------------------------------------------------------- void MarkerView::rebuildList() { MusECore::MarkerList* marker = MusEGlobal::song->marker(); const MarkerItem* selitem = (MarkerItem*)table->currentItem(); MusECore::EventID_t selitem_id = -1; if(selitem) selitem_id = selitem->marker().id(); int m_id; // Block signals added. Triggers itemSelectionChanged() causing crash. Tim. table->blockSignals(true); table->clear(); table->blockSignals(false); for (MusECore::iMarker i = marker->begin(); i != marker->end(); ++i) { const MusECore::Marker& m = i->second; m_id = m.id(); MarkerItem* new_item = new MarkerItem(m); table->blockSignals(true); table->addTopLevelItem(new_item); if(m_id == selitem_id) { //m->setCurrent(true); table->setCurrentItem(new_item); } //else //{ // m->setCurrent(false); //} table->blockSignals(false); } markerSelectionChanged(); } //--------------------------------------------------------- // updateList //--------------------------------------------------------- void MarkerView::updateList() { // Manage selected item, due to clearing of table... MusECore::MarkerList* marker = MusEGlobal::song->marker(); const MarkerItem* selitem = (MarkerItem*)table->currentItem(); MusECore::EventID_t selitem_id = -1; MarkerItem* new_selitem = nullptr; if(selitem) selitem_id = selitem->marker().id(); int m_id, mitem_id; MarkerItem* mitem; unsigned m_frame, mitem_frame; // Look for removed markers before added markers... for(int mitem_idx = 0; ; ) { mitem = (MarkerItem*)table->topLevelItem(mitem_idx); if(!mitem) break; MusECore::ciMarker im = marker->findId(mitem->marker().id()); if(im != marker->cend()) { ++mitem_idx; continue; } delete mitem; } MarkerItem* found_item; int found_item_idx; int insert_idx; bool prev_frame_found; bool next_frame_found; unsigned prev_frame; unsigned next_frame; // Look for added markers... for(MusECore::iMarker i = marker->begin(); i != marker->end(); ++i) { const MusECore::Marker& m = i->second; m_id = m.id(); m_frame = m.frame(); found_item = nullptr; found_item_idx = -1; insert_idx = -1; prev_frame_found = false; next_frame_found = false; prev_frame = 0; next_frame = 0; for(int mitem_idx = 0; ; ++mitem_idx) { mitem = (MarkerItem*)table->topLevelItem(mitem_idx); if(!mitem) break; // If all four pieces of information have been found, we're done. if(found_item && insert_idx != -1 && prev_frame_found && next_frame_found) break; const MusECore::Marker& mm = mitem->marker(); mitem_id = mm.id(); mitem_frame = mm.frame(); if(insert_idx == -1 && m_frame < mitem_frame) insert_idx = mitem_idx; if(m_id == mitem_id) { found_item = mitem; found_item_idx = mitem_idx; prev_frame_found = true; } else { if(!prev_frame_found) prev_frame = mitem_frame; if(found_item && !next_frame_found) { next_frame = mitem_frame; next_frame_found = true; // All four pieces of information have been found, we're done. //break; } } } // Did we find an item matching the marker ID? if(found_item) { const MusECore::Marker& found_m = found_item->marker(); const unsigned found_f = found_m.frame(); // Can we get away with simply modifying the existing item's marker properties with the new ones? // That means is the new requested position CLOSER than a previous (or next) item's marker positon? if(m_frame == found_f || (m_frame < found_f && (!prev_frame_found || m_frame >= prev_frame)) || (m_frame > found_f && (!next_frame_found || m_frame <= next_frame))) { found_item->setMarker(m); if(m_id == selitem_id) new_selitem = found_item; continue; } // The new requested position is not CLOSER than a previous (or next) item's marker positon. // We must delete the existing item, and re-add it at the new position. table->blockSignals(true); delete found_item; table->blockSignals(false); if(found_item_idx <= insert_idx) { if(insert_idx > 0) --insert_idx; } } // Add a new item, based on the given marker m. MarkerItem* new_item = new MarkerItem(m); table->blockSignals(true); if(insert_idx != -1) table->insertTopLevelItem(insert_idx, new_item); else table->addTopLevelItem(new_item); table->blockSignals(false); if(m_id == selitem_id) new_selitem = new_item; } if(new_selitem) { table->blockSignals(true); table->setCurrentItem(new_selitem); table->blockSignals(false); } markerSelectionChanged(); } //--------------------------------------------------------- // markerSelectionChanged //--------------------------------------------------------- void MarkerView::markerSelectionChanged() { MarkerItem* item = (MarkerItem*)table->currentItem(); editTick->blockSignals(true); editSMPTE->blockSignals(true); editName->blockSignals(true); lock->blockSignals(true); if (item == nullptr) { // never triggered editTick->setValue(0); editSMPTE->setValue(0); editName->setText(QString("")); lock->setChecked(false); editSMPTE->setEnabled(false); editTick->setEnabled(false); lock->setEnabled(false); editName->setEnabled(false); } else { editTick->setValue(item->marker()); editSMPTE->setValue(item->marker()); editName->setText(item->name()); editName->setEnabled(true); lock->setChecked(item->lock()); lock->setEnabled(true); editSMPTE->setEnabled(item->lock()); editTick->setEnabled(!item->lock()); } editTick->blockSignals(false); editSMPTE->blockSignals(false); editName->blockSignals(false); lock->blockSignals(false); } void MarkerView::clicked(QTreeWidgetItem* i) { MarkerItem* item = (MarkerItem*)i; if (item == nullptr) { table->clearSelection(); return; } // Hm I don't like the idea of letting song handle this. I feel that song->setPos() // should only be called as a RESULT of the transport repositioning, which it ALREADY will. MusEGlobal::song->setPos(MusECore::Song::CPOS, item->marker(), true, true, false); // MusEGlobal::audio->msgSeek(item->marker()); } //--------------------------------------------------------- // nameChanged //--------------------------------------------------------- void MarkerView::nameChanged() { MarkerItem* item = (MarkerItem*)table->currentItem(); if (item) { const QString txt = editName->text(); // Avoid multiple identical changes from return pressed and focus lost etc. if(item->marker().name() != txt) MusEGlobal::song->setMarkerName(item->marker(), txt); } } //--------------------------------------------------------- // tickChanged //--------------------------------------------------------- void MarkerView::tickChanged(const MusECore::Pos& pos) { MarkerItem* item = (MarkerItem*)table->currentItem(); if (item) { MusEGlobal::song->setMarkerPos(item->marker(), pos); } } //--------------------------------------------------------- // lockChanged //--------------------------------------------------------- void MarkerView::lockChanged(bool lck) { MarkerItem* item = (MarkerItem*)table->currentItem(); if (item) { MusEGlobal::song->setMarkerLock(item->marker(), lck); } } //--------------------------------------------------------- // markerChanged // select appropriate Marker //--------------------------------------------------------- void MarkerView::markerChanged(int val) { switch(val) { // MARKER_CUR, MARKER_ADD, MARKER_REMOVE, MARKER_NAME, // MARKER_TICK, MARKER_LOCK case MusECore::Song::MARKER_CUR: { MusECore::MarkerList* marker = MusEGlobal::song->marker(); for (MusECore::iMarker i = marker->begin(); i != marker->end(); ++i) { if (i->second.current()) { MarkerItem* item = (MarkerItem*)table->topLevelItem(0); while (item) { if (item->marker().id() == i->second.id()) { table->setCurrentItem(item); return; } item = (MarkerItem*)table->itemBelow(item); } } } } break; default: break; } } void MarkerView::nextMarker() { unsigned int curPos = MusEGlobal::song->cpos();//prevent compiler warning: comparison of sigend/unsigned unsigned int nextPos = 0xFFFFFFFF; MusECore::MarkerList* marker = MusEGlobal::song->marker(); for (MusECore::iMarker i = marker->begin(); i != marker->end(); ++i) { if (i->second.tick() > curPos && i->second.tick() < nextPos) nextPos = i->second.tick(); } if (nextPos == 0xFFFFFFFF) return; MusECore::Pos p(nextPos, true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, false); } void MarkerView::prevMarker() { unsigned int curPos = MusEGlobal::song->cpos();//prevent compiler warning: comparison of sigend/unsigned unsigned int nextPos = 0; MusECore::MarkerList* marker = MusEGlobal::song->marker(); for (MusECore::iMarker i = marker->begin(); i != marker->end(); ++i) { if (i->second.tick() < curPos && i->second.tick() > nextPos) nextPos = i->second.tick(); } MusECore::Pos p(nextPos, true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, false); } QSize MarkerView::sizeHint() const { return QSize(minimumWidth(), 400); } MarkerItem* MarkerView::findId(MusECore::EventID_t id) const { MarkerItem* item = (MarkerItem*)table->topLevelItem(0); while(item) { if(item->marker().id() == id) return item; item = (MarkerItem*)table->itemBelow(item); } return nullptr; } } // namespace MusEGui muse-4.0.0/src/muse/marker/markerview.h000066400000000000000000000057361404105311000200320ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: markerview.h,v 1.4.2.3 2008/08/18 00:15:25 terminator356 Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MARKERVIEW_H__ #define __MARKERVIEW_H__ #include #include #include "type_defs.h" #include "cobject.h" #include "marker.h" class QCloseEvent; class QLineEdit; class QToolBar; class QToolButton; class QTreeWidget; namespace MusECore { class Pos; class Xml; } namespace MusEGui { class PosEdit; //--------------------------------------------------------- // MarkerItem //--------------------------------------------------------- class MarkerItem : public QTreeWidgetItem { MusECore::Marker _marker; public: MarkerItem(QTreeWidget* parent, const MusECore::Marker& m); MarkerItem(const MusECore::Marker& m); MusECore::Marker marker() const; void setMarker(const MusECore::Marker& m); unsigned tick() const; const QString name() const; bool lock() const; void setName(const QString& s); void setPos(const MusECore::Pos& v); void setLock(bool lck); }; //--------------------------------------------------------- // MarkerView //--------------------------------------------------------- class MarkerView : public QWidget { Q_OBJECT QTreeWidget* table; QLineEdit* editName; PosEdit* editSMPTE; PosEdit* editTick; QToolButton* lock; QToolBar* tools; QSize sizeHint() const override; bool eventFilter(QObject *, QEvent *event) override; private slots: void addMarker(); void addMarker(unsigned); void deleteMarker(); void markerSelectionChanged(); void nameChanged(); void tickChanged(const MusECore::Pos&); void lockChanged(bool); void markerChanged(int); void clicked(QTreeWidgetItem*); void rebuildList(); void updateList(); void songChanged(MusECore::SongChangedStruct_t); public: MarkerView(QWidget* parent); ~MarkerView(); void nextMarker(); void prevMarker(); MarkerItem* findId(MusECore::EventID_t id) const; }; } #endif muse-4.0.0/src/muse/master/000077500000000000000000000000001404105311000155045ustar00rootroot00000000000000muse-4.0.0/src/muse/master/CMakeLists.txt000066400000000000000000000036671404105311000202600ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( master_mocs lmaster.h masteredit.h master.h tscale.h ) ## ## List of source files to compile ## file (GLOB master_source_files lmaster.cpp masteredit.cpp master.cpp tscale.cpp ) ## ## Define target ## add_library ( master ${MODULES_BUILD} ${master_source_files} ${master_mocs} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${master_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( master PROPERTIES OUTPUT_NAME muse_master ) ## ## Linkage ## target_link_libraries ( master ${QT_LIBRARIES} widgets ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS master DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/master/lmaster.cpp000066400000000000000000001164511404105311000176670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: lmaster.cpp,v 1.2.2.8 2009/03/09 02:05:18 terminator356 Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "lmaster.h" #include "song.h" #include "globals.h" #include "shortcuts.h" #include "debug.h" #include "gconfig.h" #include "undo.h" #include #include #include #include #include #include #include // Forwards from header: #include #include #include #include #include #include #include #include #include #include "tempo.h" #include "sig.h" #include "posedit.h" #include "sigedit.h" #include "xml.h" #define LMASTER_BEAT_COL 0 #define LMASTER_TIME_COL 1 #define LMASTER_TYPE_COL 2 #define LMASTER_VAL_COL 3 #define LMASTER_MSGBOX_STRING "Mastertrack List Editor" namespace MusEGui { LMasterLViewItem::LMasterLViewItem(QTreeWidget* parent) : QTreeWidgetItem(QTreeWidgetItem::UserType) { parent->insertTopLevelItem(0, this); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void LMaster::closeEvent(QCloseEvent* e) { _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. e->accept(); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void LMaster::songChanged(MusECore::SongChangedStruct_t type) { if(_isDeleting) // Ignore while while deleting to prevent crash. return; if (type & (SC_SIG | SC_TEMPO | SC_KEY )) updateList(); } //--------------------------------------------------------- // LMaster //--------------------------------------------------------- LMaster::LMaster(QWidget* parent) : QWidget(parent) { setObjectName("MasterTrackList"); _isDeleting = false; pos_editor = nullptr; tempo_editor = nullptr; sig_editor = nullptr; key_editor = nullptr; editedItem = nullptr; editingNewItem = false; setMinimumHeight(100); //setFixedWidth(400); // FIXME: Arbitrary. But without this, sig editor is too wide. Must fix sig editor width... setFocusPolicy(Qt::NoFocus); comboboxTimer=new QTimer(this); comboboxTimer->setInterval(150); comboboxTimer->setSingleShot(true); connect(comboboxTimer, SIGNAL(timeout()), this, SLOT(comboboxTimerSlot())); tempoAction = new QAction(tr("Tempo"), this); signAction = new QAction(tr("Signature"), this); keyAction = new QAction(tr("Key"), this); posAction = new QAction(tr("Position"), this); valAction = new QAction(tr("Value"), this); delAction = new QAction(tr("Delete"), this); tempoAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); signAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); keyAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); posAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); valAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); delAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); addAction(tempoAction); addAction(signAction); addAction(keyAction); addAction(posAction); addAction(valAction); addAction(delAction); connect(tempoAction, &QAction::triggered, [this]() { cmd(CMD_INSERT_TEMPO); } ); connect(signAction, &QAction::triggered, [this]() { cmd(CMD_INSERT_SIG); } ); connect(keyAction, &QAction::triggered, [this]() { cmd(CMD_INSERT_KEY); } ); connect(posAction, &QAction::triggered, [this]() { cmd(CMD_EDIT_BEAT); } ); connect(valAction, &QAction::triggered, [this]() { cmd(CMD_EDIT_VALUE); } ); connect(delAction, &QAction::triggered, [this]() { cmd(CMD_DELETE); } ); // Toolbars --------------------------------------------------------- // NOTICE: Please ensure that any tool bar object names here match the names assigned // to identical or similar toolbars in class MusE or other TopWin classes. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). QToolBar* edit = new QToolBar(tr("Edit tools"), this); edit->addAction(tempoAction); edit->addAction(signAction); edit->addAction(keyAction); edit->addAction(posAction); edit->addAction(valAction); edit->addAction(delAction); //--------------------------------------------------- // master //--------------------------------------------------- view = new QTreeWidget; view->setAllColumnsShowFocus(true); view->setSelectionMode(QAbstractItemView::SingleSelection); QStringList columnnames; columnnames << tr("Position") << tr("Time") << tr("Type") << tr("Value"); view->setHeaderLabels(columnnames); view->setColumnWidth(2,70); view->setIndentation(2); //--------------------------------------------------- // Rest //--------------------------------------------------- QGridLayout* mainGrid = new QGridLayout(this); mainGrid->setRowStretch(0, 100); mainGrid->setColumnStretch(0, 100); mainGrid->addWidget(edit, 0, 0); mainGrid->addWidget(view, 1, 0); updateList(); tempo_editor = new QLineEdit(view->viewport()); tempo_editor->setFrame(false); tempo_editor->hide(); connect(tempo_editor, SIGNAL(editingFinished()), SLOT(editingFinished())); sig_editor = new SigEdit(view->viewport()); sig_editor->setFrame(false); sig_editor->hide(); connect(sig_editor, SIGNAL(editingFinished()), SLOT(editingFinished())); pos_editor = new PosEdit(view->viewport()); pos_editor->setFrame(false); pos_editor->hide(); connect(pos_editor, SIGNAL(editingFinished()), SLOT(editingFinished())); key_editor = new QComboBox(view->viewport()); key_editor->setFrame(false); key_editor->addItems(MusECore::KeyEvent::keyStrs); key_editor->hide(); connect(key_editor, SIGNAL(activated(int)), SLOT(editingFinished())); // connect(view, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), SLOT(select(QTreeWidgetItem*, QTreeWidgetItem*))); connect(view, SIGNAL(itemPressed(QTreeWidgetItem*, int)), SLOT(itemPressed(QTreeWidgetItem*, int))); connect(view, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), SLOT(itemDoubleClicked(QTreeWidgetItem*))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(this, SIGNAL(seekTo(int)), MusEGlobal::song, SLOT(seekTo(int))); initShortcuts(); tempoAction->setToolTip(tr("Insert tempo change") + " (" + tempoAction->shortcut().toString() + ")"); signAction->setToolTip(tr("Insert time signature change") + " (" + signAction->shortcut().toString() + ")"); keyAction->setToolTip(tr("Insert key change") + " (" + keyAction->shortcut().toString() + ")"); posAction->setToolTip(tr("Edit position") + " (" + posAction->shortcut().toString() + ")"); valAction->setToolTip(tr("Edit value") + " (" + valAction->shortcut().toString() + ")"); delAction->setToolTip(tr("Delete event") + " (" + delAction->shortcut().toString() + ")"); qApp->installEventFilter(this); } //--------------------------------------------------------- // ~LMaster //--------------------------------------------------------- LMaster::~LMaster() { } //--------------------------------------------------------- // focusCanvas //--------------------------------------------------------- void LMaster::focusCanvas() { if(MusEGlobal::config.smartFocus) { view->setFocus(); view->activateWindow(); } } //--------------------------------------------------------- // insertSig //--------------------------------------------------------- void LMaster::insertSig(const MusECore::SigEvent* ev) { new LMasterSigEventItem(view, ev); } //--------------------------------------------------------- // insertTempo //--------------------------------------------------------- void LMaster::insertTempo(const MusECore::TEvent* ev) { new LMasterTempoItem(view, ev); } void LMaster::insertKey(const MusECore::KeyEvent& ev) { new LMasterKeyEventItem(view, ev); } //--------------------------------------------------------- // updateList //--------------------------------------------------------- void LMaster::updateList() { LMasterLViewItem* selected = (LMasterLViewItem*) view->currentItem(); LMASTER_LVTYPE type = LMASTER_TEMPO; unsigned tick = 0; if (selected) { type = selected->getType(); tick = selected->tick(); } view->clear(); const MusECore::TempoList* t = &MusEGlobal::tempomap; const MusECore::SigList* s = &MusEGlobal::sigmap; const MusECore::KeyList* k = &MusEGlobal::keymap; MusECore::criTEvent it = t->rbegin(); MusECore::criSigEvent is = s->rbegin(); MusECore::criKeyEvent ik = k->rbegin(); // three lists that should be added to the view. // question if it would not be easier to merge the lists and use a sorting algorithm? // how often is this function called? A: only on songChanged (SC_TEMPO && SC_SIG) for (;;) { // crazy long, must be possible to solve more elegantly... if (ik != k->rend() && is == s->rend() && it == t->rend()) {// ik biggest insertKey(ik->second); ++ik; } else if (is != s->rend() && ik == k->rend() && it == t->rend()) {// is biggest insertSig(is->second); ++is; } else if (it != t->rend() && ik == k->rend() && is == s->rend()) {// it biggest insertTempo(it->second); ++it; } else if ( ((ik != k->rend()) && (is == s->rend()) && (ik->second.tick >= it->second->tick)) || ((it == t->rend()) && (ik->second.tick >= is->second->tick ) )) {// ik biggest insertKey(ik->second); ++ik; } else if ( ((is != s->rend()) && (ik == k->rend()) && (is->second->tick >= it->second->tick)) || ((it == t->rend()) && (is->second->tick >= ik->second.tick ))) {// is biggest insertSig(is->second); ++is; } else if (((it != t->rend()) && (ik == k->rend()) && (it->second->tick >= is->second->tick)) || ((is == s->rend()) && (it->second->tick >= ik->second.tick ))) {// it biggest insertTempo(it->second); ++it; } else if (ik != k->rend() && ik->second.tick >= is->second->tick && ik->second.tick >= it->second->tick) {// ik biggest insertKey(ik->second); ++ik; } else if (is != s->rend() && is->second->tick >= it->second->tick && is->second->tick >= ik->second.tick) { // is biggest insertSig(is->second); ++is; } else if (it != t->rend() && it->second->tick >= is->second->tick && it->second->tick >= ik->second.tick) { // it biggest insertTempo(it->second); ++it; } if (ik == k->rend() && is == s->rend() && it == t->rend() ) break; } // Try to reselect the previous selection: if(selected) { LMasterLViewItem* tmp = getItemAtPos(tick, type); if (tmp) { view->clearSelection(); view->setCurrentItem(tmp); } } } //--------------------------------------------------------- // cmd //--------------------------------------------------------- void LMaster::cmd(int cmd) { editedItem = nullptr; tempo_editor->hide(); pos_editor->hide(); key_editor->hide(); sig_editor->hide(); switch(cmd) { case CMD_DELETE: { LMasterLViewItem* l = dynamic_cast(view->currentItem()); if (!l) return; // Delete item: if (l->tick() != 0) { if (l == view->topLevelItem(view->topLevelItemCount() - 1)) view->setCurrentItem(view->itemAbove(l)); else view->setCurrentItem(view->itemBelow(l)); switch (l->getType()) { case LMASTER_TEMPO: { LMasterTempoItem* t = (LMasterTempoItem*) l; MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteTempo, t->tick(), t->tempo())); break; } case LMASTER_SIGEVENT: { LMasterSigEventItem* s = (LMasterSigEventItem*) l; MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteSig, s->tick(), s->z(), s->n())); break; } case LMASTER_KEYEVENT: { LMasterKeyEventItem* k = (LMasterKeyEventItem*) l; MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteKey, k->tick(), k->key(), (int)k->isMinor())); break; } default: M_ERROR("Default switch statement reached"); break; } } break; } case CMD_INSERT_TEMPO: tempoButtonClicked(); break; case CMD_INSERT_SIG: timeSigButtonClicked(); break; case CMD_INSERT_KEY: insertKey(); break; case CMD_EDIT_BEAT: case CMD_EDIT_VALUE: cmd == CMD_EDIT_VALUE ? editorColumn = LMASTER_VAL_COL : editorColumn = LMASTER_BEAT_COL; if (view->currentItem() && !editedItem) { itemDoubleClicked(view->currentItem()); } break; } } /*! \fn LMaster::itemPressed(QListViewItem* i, const QPoint& p, int column) */ void LMaster::itemPressed(QTreeWidgetItem* i, int column) { if (editedItem) { if (editorColumn != column || editedItem != i) editingFinished(); } else { if (key_editor) key_editor->hide(); setFocus(); editorColumn = column; } } //--------------------------------------------------------- // itemDoubleClicked(QListViewItem* item) //! Sets lmaster in edit mode, and opens editor for selected value //--------------------------------------------------------- void LMaster::itemDoubleClicked(QTreeWidgetItem* i) { emit seekTo(((LMasterLViewItem*) i)->tick()); QFontMetrics fm(font()); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int fnt_w = fm.horizontalAdvance('0'); #else int fnt_w = fm.width('0'); #endif if (!editedItem && editorColumn == LMASTER_VAL_COL) { editedItem = (LMasterLViewItem*) i; QRect itemRect = view->visualItemRect(editedItem); int x1 = view->columnWidth(LMASTER_BEAT_COL) + view->columnWidth(LMASTER_TIME_COL) + view->columnWidth(LMASTER_TYPE_COL); itemRect.setX(x1); //Qt makes crazy things with itemRect if this is called directly.. if (editingNewItem) { int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth,0 , this); // ddskrjo 0 int h = fm.height() + fw * 2; itemRect.setWidth(view->columnWidth(LMASTER_VAL_COL)); itemRect.setY(1); itemRect.setHeight(h); } // Edit tempo value: if (editedItem->getType() == LMASTER_TEMPO) { tempo_editor->setText(editedItem->text(LMASTER_VAL_COL)); tempo_editor->setGeometry(itemRect); tempo_editor->show(); tempo_editor->setFocus(); tempo_editor->selectAll(); } else if (editedItem->getType() == LMASTER_SIGEVENT) { // Edit signatur value: sig_editor->setValue(((LMasterSigEventItem*)editedItem)->getEvent()->sig); int w = fnt_w * 14; if(w > itemRect.width()) w = itemRect.width(); sig_editor->setGeometry(itemRect.x(), itemRect.y(), w, itemRect.height()); sig_editor->show(); sig_editor->setFocus(); } else if (editedItem->getType() == LMASTER_KEYEVENT) { key_editor->setGeometry(itemRect); LMasterKeyEventItem* kei = static_cast(editedItem); key_editor->setCurrentIndex(MusECore::KeyEvent::keyToIndex(kei->key(), kei->isMinor())); key_editor->show(); key_editor->setFocus(); comboboxTimer->start(); } else { printf("illegal Master list type\n"); } } // Edit tempo or signal position: else if (!editedItem && editorColumn == LMASTER_BEAT_COL) { editedItem = (LMasterLViewItem*) i; // Don't allow movement of initial values: if (editedItem->tick() == 0) { QMessageBox::information(this, tr(LMASTER_MSGBOX_STRING), tr("Reposition of the initial tempo and signature events is not allowed") ); editedItem = nullptr; } // Everything OK else { pos_editor->setValue(editedItem->tick()); QRect itemRect = view->visualItemRect(editedItem); itemRect.setX(view->indentation()); int w = view->columnWidth(LMASTER_BEAT_COL) - view->indentation(); if(w < (fnt_w * 13)) w = fnt_w * 13; itemRect.setWidth(w); pos_editor->setGeometry(itemRect); pos_editor->show(); pos_editor->setFocus(); } } } //--------------------------------------------------------- // returnPressed() //! called when editor is closed //--------------------------------------------------------- void LMaster::editingFinished() { if (!editedItem) return; // setFocus(); // Tempo event: if (editedItem->getType() == LMASTER_TEMPO && editorColumn == LMASTER_VAL_COL) { if (tempo_editor->isHidden()) return; tempo_editor->hide(); repaint(); LMasterTempoItem* e = dynamic_cast(editedItem); if (!e) return; const MusECore::TEvent* t = e->getEvent(); QString input = tempo_editor->text(); unsigned tick = t->tick; bool conversionOK; double dbl_input = input.toDouble(&conversionOK); if (conversionOK && dbl_input < 250.0) { int tempo = (int) ((1000000.0 * 60.0)/dbl_input); if (!editingNewItem) { MusEGlobal::song->startUndo(); // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteTempo, tick, e->tempo()), MusECore::Song::OperationUndoable); // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddTempo, tick, tempo), MusECore::Song::OperationUndoable); MusEGlobal::song->endUndo(SC_TEMPO); } // // New item edited: // else { MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddTempo, tick, tempo)); } } else { QMessageBox::warning(this, tr("MusE: List Editor"), tr("Input error, conversion not OK or value out of range"), QMessageBox::Ok, Qt::NoButton ); } } // // Beat column, change position of a particular tempo or signature event // else if (editorColumn == LMASTER_BEAT_COL) { if (pos_editor->isHidden()) return; pos_editor->hide(); repaint(); int oldtick = editedItem->tick(); int newtick = pos_editor->pos().tick(); if (newtick == 0) { // Do not allow change of position to beginning of song QMessageBox::warning(this, tr(LMASTER_MSGBOX_STRING), tr("Reposition of tempo and signature events to start position is not allowed!"), QMessageBox::Ok, Qt::NoButton ); } else if (oldtick != newtick) { // Ignore if tick hasn't changed if (editedItem->getType() == LMASTER_TEMPO) { LMasterTempoItem* t = dynamic_cast(editedItem); if (!t) return; int tempo = t->tempo(); MusEGlobal::song->startUndo(); // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteTempo, oldtick, tempo), MusECore::Song::OperationUndoable); // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddTempo, newtick, tempo), MusECore::Song::OperationUndoable); MusEGlobal::song->endUndo(SC_TEMPO); // Select the item: QTreeWidgetItem* newSelected = (QTreeWidgetItem*) getItemAtPos(newtick, LMASTER_TEMPO); if (newSelected) { view->clearSelection(); view->setCurrentItem(newSelected); } } else if (editedItem->getType() == LMASTER_SIGEVENT) { LMasterSigEventItem* t = dynamic_cast(editedItem); if (!t) return; int z = t->z(); int n = t->n(); newtick = MusEGlobal::sigmap.raster1(newtick, 0); if (!editingNewItem) { MusEGlobal::song->startUndo(); //Delete first, in order to get sane tick-value // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteSig, oldtick, z, n), MusECore::Song::OperationUndoable); // Add will replace if found. // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddSig, newtick, z, n), MusECore::Song::OperationUndoable); MusEGlobal::song->endUndo(SC_SIG); } else //{ // Add will replace if found. // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddSig, newtick, z, n), MusECore::Song::OperationUndoable); // Select the item: QTreeWidgetItem* newSelected = dynamic_cast(getItemAtPos(newtick, LMASTER_SIGEVENT)); if (newSelected) { view->clearSelection(); view->setCurrentItem(newSelected); } // } } else if (editedItem->getType() == LMASTER_KEYEVENT) { LMasterKeyEventItem* k = dynamic_cast(editedItem); if (!k) return; MusEGlobal::song->startUndo(); // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteKey, oldtick, k->key(), (int)k->isMinor()), MusECore::Song::OperationUndoable); // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddKey, newtick, k->key(), (int)k->isMinor()), MusECore::Song::OperationUndoable); MusEGlobal::song->endUndo(SC_KEY); // Select the item: QTreeWidgetItem* newSelected = dynamic_cast(getItemAtPos(newtick, LMASTER_KEYEVENT)); if (newSelected) { view->clearSelection(); view->setCurrentItem(newSelected); } } else { printf("unknown master list event type!\n"); } } } // // SigEvent, value changed: // else if (editedItem->getType() == LMASTER_SIGEVENT && editorColumn == LMASTER_VAL_COL) { if (sig_editor->isHidden()) return; sig_editor->hide(); repaint(); MusECore::TimeSignature newSig = sig_editor->sig(); // Added p3.3.43 Prevents aborting with 0 z or n. if(newSig.isValid()) { LMasterSigEventItem* e = dynamic_cast(editedItem); if (!e) return; int tick = e->tick(); if (!editingNewItem) { MusEGlobal::song->startUndo(); // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteSig, tick, e->z(), e->n()), MusECore::Song::OperationUndoable); // Add will replace if found. // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddSig, tick, newSig.z, newSig.n), MusECore::Song::OperationUndoable); MusEGlobal::song->endUndo(SC_SIG); } else // Add will replace if found. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddSig, tick, newSig.z, newSig.n)); } else { printf("Signature is not valid!\n"); } } else if (editedItem->getType() == LMASTER_KEYEVENT && editorColumn == LMASTER_VAL_COL) { if (key_editor->isHidden()) return; key_editor->hide(); repaint(); LMasterKeyEventItem* e = dynamic_cast(editedItem); if (!e) return; const MusECore::KeyEvent& t = e->getEvent(); QString input = key_editor->currentText(); unsigned tick = t.tick; const MusECore::KeyEvent key = MusECore::KeyEvent::stringToKey(input); if (!editingNewItem) { MusEGlobal::song->startUndo(); // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteKey, tick, e->key(), (int)e->isMinor()), MusECore::Song::OperationUndoable); // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddKey, tick, key.key, (int)key.minor), MusECore::Song::OperationUndoable); MusEGlobal::song->endUndo(SC_KEY); } // // New item edited: // else { MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddKey, tick, key.key, (int)key.minor)); } } updateList(); view->setFocus(); // No item edited now: editedItem = nullptr; editorColumn = -1; editingNewItem = false; } /*! \fn LMasterLViewItem::text(int column) \brief Returns the initialized text to the View */ QString LMasterLViewItem::text(int column) const { QString ret = "?"; switch (column) { case LMASTER_BEAT_COL: ret = c1; break; case LMASTER_TIME_COL: ret = c2; break; case LMASTER_TYPE_COL: ret = c3; break; case LMASTER_VAL_COL: ret = c4; break; default: fprintf(stderr,"LMasterLViewItem::text(int): Default switch statement reached... Unknown column.\n"); break; } return ret; } //--------------------------------------------------------- // LMasterKeyEventItem //! Initializes a LMasterKeyEventItem with a KeyEvent //--------------------------------------------------------- LMasterKeyEventItem::LMasterKeyEventItem(QTreeWidget* parent, const MusECore::KeyEvent& ev) : LMasterLViewItem(parent) { keyEvent = ev; unsigned t = ev.tick; int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(t, &bar, &beat, &tick); c1 = QString("%1.%2.%3") .arg(bar + 1, 4, 10, QLatin1Char('0')) .arg(beat + 1, 2, 10, QLatin1Char('0')) .arg(tick, 3, 10, QLatin1Char('0')); double time = double(MusEGlobal::tempomap.tick2frame(t)) / double(MusEGlobal::sampleRate); int min = int(time) / 60; int sec = int(time) % 60; int msec = int((time - (min*60 + sec)) * 1000.0); c2 = QString("%1:%2:%3") .arg(min, 3, 10, QLatin1Char('0')) .arg(sec, 2, 10, QLatin1Char('0')) .arg(msec, 3, 10, QLatin1Char('0')); c3 = "Key"; c4 = ev.keyString(); setText(0, c1); setText(1, c2); setText(2, c3); setText(3, c4); } LMASTER_LVTYPE LMasterKeyEventItem::getType() const { return LMASTER_KEYEVENT; } const MusECore::KeyEvent& LMasterKeyEventItem::getEvent() const { return keyEvent; } unsigned LMasterKeyEventItem::tick() const { return keyEvent.tick; } MusECore::key_enum LMasterKeyEventItem::key() const { return keyEvent.key; } bool LMasterKeyEventItem::isMinor() const { return keyEvent.minor; } //--------------------------------------------------------- // LMasterTempoItem //! Initializes a LMasterTempoItem with a TEvent //--------------------------------------------------------- LMasterTempoItem::LMasterTempoItem(QTreeWidget* parent, const MusECore::TEvent* ev) : LMasterLViewItem(parent) { tempoEvent = ev; unsigned t = ev->tick; int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(t, &bar, &beat, &tick); c1 = QString("%1.%2.%3") .arg(bar + 1, 4, 10, QLatin1Char('0')) .arg(beat + 1, 2, 10, QLatin1Char('0')) .arg(tick, 3, 10, QLatin1Char('0')); double time = double(MusEGlobal::tempomap.tick2frame(t)) / double(MusEGlobal::sampleRate); int min = int(time) / 60; int sec = int(time) % 60; int msec = int((time - (min*60 + sec)) * 1000.0); c2 = QString("%1:%2:%3") .arg(min, 3, 10, QLatin1Char('0')) .arg(sec, 2, 10, QLatin1Char('0')) .arg(msec, 3, 10, QLatin1Char('0')); c3 = "Tempo"; double dt = (1000000.0 * 60.0)/ev->tempo; c4.setNum(dt, 'f', 3); setText(0, c1); setText(1, c2); setText(2, c3); setText(3, c4); } LMASTER_LVTYPE LMasterTempoItem::getType() const { return LMASTER_TEMPO; } const MusECore::TEvent* LMasterTempoItem::getEvent() const { return tempoEvent; } unsigned LMasterTempoItem::tick() const { return tempoEvent->tick; } int LMasterTempoItem::tempo() const { return tempoEvent->tempo; } //--------------------------------------------------------- // LMasterSigEventItem //! Initializes a ListView item with a SigEvent //--------------------------------------------------------- LMasterSigEventItem::LMasterSigEventItem(QTreeWidget* parent, const MusECore::SigEvent* ev) : LMasterLViewItem(parent) { sigEvent = ev; unsigned t = ev->tick; int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(t, &bar, &beat, &tick); c1 = QString("%1.%2.%3") .arg(bar + 1, 4, 10, QLatin1Char('0')) .arg(beat + 1, 2, 10, QLatin1Char('0')) .arg(tick, 3, 10, QLatin1Char('0')); double time = double(MusEGlobal::tempomap.tick2frame(t)) / double (MusEGlobal::sampleRate); int min = int(time) / 60; int sec = int(time) % 60; int msec = int((time - (min*60 + sec)) * 1000.0); c2 = QString("%1:%2:%3") .arg(min, 3, 10, QLatin1Char('0')) .arg(sec, 2, 10, QLatin1Char('0')) .arg(msec, 3, 10, QLatin1Char('0')); c3 = "Timesig"; c4 = QString("%1 / %2").arg(ev->sig.z).arg(ev->sig.n); setText(0, c1); setText(1, c2); setText(2, c3); setText(3, c4); } LMASTER_LVTYPE LMasterSigEventItem::getType() const { return LMASTER_SIGEVENT; } const MusECore::SigEvent* LMasterSigEventItem::getEvent() const { return sigEvent; } unsigned LMasterSigEventItem::tick() const { return sigEvent->tick; } int LMasterSigEventItem::z() const { return sigEvent->sig.z; } int LMasterSigEventItem::n() const { return sigEvent->sig.n; } //--------------------------------------------------------- // tempoButtonClicked() //! inserts a new tempo-item in the list and starts the editor for it //--------------------------------------------------------- void LMaster::tempoButtonClicked() { LMasterTempoItem* lastTempo = (LMasterTempoItem*) getLastOfType(LMASTER_TEMPO); // QString beatString = ((LMasterLViewItem*)lastTempo)->text(LMASTER_BEAT_COL); DELETETHIS? // int m, b, t; // Pos p = Pos(beatString); // p.mbt(&m, &b, &t); // m++; //Next bar // int newTick = MusEGlobal::sigmap.bar2tick(m, b, t); int newTick = MusEGlobal::song->cpos(); MusECore::TEvent* ev = new MusECore::TEvent(lastTempo->tempo(), newTick); new LMasterTempoItem(view, ev); QTreeWidgetItem* newTempoItem = view->topLevelItem(0); editingNewItem = true; // State editorColumn = LMASTER_VAL_COL; // Set that we edit editorColumn view->clearSelection(); view->setCurrentItem(newTempoItem); itemDoubleClicked(newTempoItem); } //--------------------------------------------------------- // timeSigButtonClicked() //! inserts a new sig-item in the list and starts the editor for it //--------------------------------------------------------- void LMaster::timeSigButtonClicked() { LMasterSigEventItem* lastSig = (LMasterSigEventItem*) getLastOfType(LMASTER_SIGEVENT); // QString beatString = ((LMasterLViewItem*)lastSig)->text(LMASTER_BEAT_COL); DELETETHIS // int m, b, t; // Pos p = Pos(beatString); // p.mbt(&m, &b, &t); // m++; // int newTick = MusEGlobal::sigmap.bar2tick(m, b, t); int newTick = MusEGlobal::song->cpos(); newTick = MusEGlobal::sigmap.raster1(newTick, 0); MusECore::SigEvent* ev = new MusECore::SigEvent(MusECore::TimeSignature(lastSig->z(), lastSig->n()), newTick); new LMasterSigEventItem(view, ev); QTreeWidgetItem* newSigItem = view->topLevelItem(0); editingNewItem = true; // State editorColumn = LMASTER_VAL_COL; // Set that we edit editorColumn view->clearSelection(); view->setCurrentItem(newSigItem); itemDoubleClicked(newSigItem); } //--------------------------------------------------------- // insertKey() //! inserts a new key in the list and starts the editor for it //--------------------------------------------------------- void LMaster::insertKey() { LMasterKeyEventItem* lastKey = (LMasterKeyEventItem*) getLastOfType(LMASTER_KEYEVENT); //QString beatString = ((LMasterLViewItem*)lastKey)->text(LMASTER_BEAT_COL); DELETETHIS //int m, b, t; //Pos p = Pos(beatString); //p.mbt(&m, &b, &t); //m++; //Next bar int newTick = MusEGlobal::song->cpos(); new LMasterKeyEventItem(view, MusECore::KeyEvent( lastKey ? lastKey->key() : MusECore::KEY_C, newTick, lastKey ? lastKey->isMinor() : false)); QTreeWidgetItem* newKeyItem = view->topLevelItem(0); editingNewItem = true; // State editorColumn = LMASTER_VAL_COL; // Set that we edit editorColumn view->clearSelection(); view->setCurrentItem(newKeyItem); itemDoubleClicked(newKeyItem); } /*! \fn LMaster::getLastOfType(LMASTER_LVTYPE t) */ LMasterLViewItem* LMaster::getLastOfType(LMASTER_LVTYPE t) { LMasterLViewItem* tmp = (LMasterLViewItem*) view->topLevelItem(view->topLevelItemCount() - 1); while (tmp->getType() != t) { tmp = (LMasterLViewItem*) view->itemAbove(tmp); } return tmp; } /*! \fn LMaster::getItemAtPos(unsigned tick, LMASTER_LVTYPE t) */ LMasterLViewItem* LMaster::getItemAtPos(unsigned tick, LMASTER_LVTYPE t) { LMasterLViewItem* tmp = (LMasterLViewItem*) view->topLevelItem(0); while (tmp) { if (tmp->getType() == t && tmp->tick() == tick) return tmp; tmp = (LMasterLViewItem*) view->itemBelow(tmp); } return nullptr; } /*! \fn LMaster::configChanged() */ void LMaster::configChanged() { initShortcuts(); } /*! \fn LMaster::initShortcuts() */ void LMaster::initShortcuts() { tempoAction->setShortcut(shortcuts[SHRT_LM_INS_TEMPO].key); signAction->setShortcut(shortcuts[SHRT_LM_INS_SIG].key); keyAction->setShortcut(shortcuts[SHRT_LM_INS_KEY].key); posAction->setShortcut(shortcuts[SHRT_LM_EDIT_BEAT].key); valAction->setShortcut(shortcuts[SHRT_LM_EDIT_VALUE].key); delAction->setShortcut(shortcuts[SHRT_DELETE].key); } void LMaster::comboboxTimerSlot() { key_editor->showPopup(); } QSize LMaster::sizeHint() const { return QSize(380, 400); } bool LMaster::eventFilter(QObject* obj, QEvent *e) { if (obj == view && e->type() == QEvent::KeyPress) { QKeyEvent* ke = static_cast(e); if (ke->key() == Qt::Key_Return) { editingFinished(); return true; } } if (!view->hasFocus()) return false; if (e->type() == QEvent::Shortcut) { QShortcutEvent* sev = static_cast(e); if (sev->isAmbiguous()) { for (const auto& action : actions()) { if (action->shortcut() == sev->key()) { action->trigger(); return true; } } } } return false; } } // namespace MusEGui muse-4.0.0/src/muse/master/lmaster.h000066400000000000000000000130131404105311000173220ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: lmaster.h,v 1.1.1.1.2.5 2005/12/11 21:29:23 spamatica Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LMASTER_EDIT_H__ #define __LMASTER_EDIT_H__ #include "type_defs.h" #include "keyevent.h" #include #include // Forward declarations: class QTreeWidget; class QLineEdit; class QComboBox; class QToolBar; class QMenu; class QTimer; class QKeyEvent; class QCloseEvent; class QAction; namespace MusECore { //class TopWin; struct SigEvent; struct TEvent; class Xml; }; namespace MusEGui { class PosEdit; class SigEdit; enum LMASTER_LVTYPE { LMASTER_TEMPO = 0, LMASTER_SIGEVENT, LMASTER_KEYEVENT }; //--------------------------------------------------------- // LMasterLViewItem //! QListViewItem base class for LMasterTempoItem and LMasterSigEventItem //--------------------------------------------------------- class LMasterLViewItem : public QTreeWidgetItem { protected: QString c1, c2, c3, c4; public: LMasterLViewItem(QTreeWidget* parent); virtual QString text(int column) const; virtual LMASTER_LVTYPE getType() const = 0; virtual unsigned tick() const = 0; }; //--------------------------------------------------------- // LMasterTempoItem //! QListViewItem which holds data for a TEvent //--------------------------------------------------------- class LMasterTempoItem : public LMasterLViewItem { private: const MusECore::TEvent* tempoEvent; public: LMasterTempoItem(QTreeWidget* parent, const MusECore::TEvent* t); virtual LMASTER_LVTYPE getType() const; const MusECore::TEvent* getEvent() const; virtual unsigned tick() const; int tempo() const; }; //--------------------------------------------------------- // LMasterKeyItem //! QListViewItem which holds data for a KetEvent //--------------------------------------------------------- class LMasterKeyEventItem : public LMasterLViewItem { private: MusECore::KeyEvent keyEvent; public: LMasterKeyEventItem(QTreeWidget* parent, const MusECore::KeyEvent& t); virtual LMASTER_LVTYPE getType() const; const MusECore::KeyEvent& getEvent() const; virtual unsigned tick() const; MusECore::key_enum key() const; bool isMinor() const; }; //--------------------------------------------------------- // LMasterTempoItem //! QListViewItem which holds data for a SigEvent //--------------------------------------------------------- class LMasterSigEventItem : public LMasterLViewItem { private: const MusECore::SigEvent* sigEvent; public: LMasterSigEventItem(QTreeWidget* parent, const MusECore::SigEvent* s); virtual LMASTER_LVTYPE getType() const; const MusECore::SigEvent* getEvent() const; virtual unsigned tick() const; int z() const; int n() const; }; //--------------------------------------------------------- // LMaster //--------------------------------------------------------- class LMaster : public QWidget { Q_OBJECT QTreeWidget* view; QToolBar* tools; QMenu* menuEdit; QTimer* comboboxTimer; bool _isDeleting; enum { CMD_DELETE, CMD_INSERT_SIG, CMD_INSERT_TEMPO, CMD_EDIT_BEAT, CMD_EDIT_VALUE, CMD_INSERT_KEY }; void updateList(); void insertTempo(const MusECore::TEvent*); void insertSig(const MusECore::SigEvent*); void insertKey(const MusECore::KeyEvent&); LMasterLViewItem* getItemAtPos(unsigned tick, LMASTER_LVTYPE t); void initShortcuts(); QLineEdit* tempo_editor; PosEdit* pos_editor; QComboBox* key_editor; // State-like members: LMasterLViewItem* editedItem; SigEdit* sig_editor; int editorColumn; bool editingNewItem; virtual void closeEvent(QCloseEvent*); bool eventFilter(QObject *, QEvent *event) override; virtual QSize sizeHint() const; QAction *tempoAction, *signAction, *posAction, *valAction, *delAction, *keyAction; private slots: void itemDoubleClicked(QTreeWidgetItem* item); void editingFinished(); void itemPressed(QTreeWidgetItem* i, int column); void tempoButtonClicked(); void timeSigButtonClicked(); void insertKey(); void cmd(int cmd); void comboboxTimerSlot(); public slots: void songChanged(MusECore::SongChangedStruct_t); void configChanged(); void focusCanvas(); signals: // void isDeleting(MusEGui::TopWin*); void seekTo(int tick); public: LMaster(QWidget* parent = 0); ~LMaster(); LMasterLViewItem* getLastOfType(LMASTER_LVTYPE t); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/master/master.cpp000066400000000000000000000425641404105311000175160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: master.cpp,v 1.3 2004/04/11 13:03:32 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include #include #include #include #include #include "globals.h" #include "master.h" #include "song.h" #include "scrollscale.h" #include "midi_consts.h" #include "midieditor.h" #include "icons.h" #include "audio.h" #include "gconfig.h" #include "shortcuts.h" #include "menutitleitem.h" #include "masteredit.h" namespace MusEGui { //--------------------------------------------------------- // Master //--------------------------------------------------------- Master::Master(MidiEditor* e, QWidget* parent, int xmag, int ymag) : View(parent, xmag, ymag) { editor = e; setBg(MusEGlobal::config.midiCanvasBg); vscroll = 0; pos[0] = MusEGlobal::song->cpos(); pos[1] = MusEGlobal::song->lpos(); pos[2] = MusEGlobal::song->rpos(); drag = DRAG_OFF; setTool(MusEGui::PencilTool); setFocusPolicy(Qt::StrongFocus); setMouseTracking(true); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), this, SLOT(setPos(int, unsigned, bool))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), this, SLOT(songChanged(MusECore::SongChangedStruct_t))); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void Master::songChanged(MusECore::SongChangedStruct_t type) { //if(_isDeleting) return; // todo: If things get complicated don't forget some mechanism to ignore while while deleting to prevent possible crash. if (type & (SC_SIG | SC_TEMPO | SC_KEY )) // TEST: Reasonable to start with, may need more. redraw(); } //--------------------------------------------------------- // setPos //--------------------------------------------------------- void Master::setPos(int idx, unsigned val, bool adjustScrollbar) { if (pos[idx] == val) return; int opos = mapx(pos[idx]); int npos = mapx(val); if (adjustScrollbar && idx == 0) { switch (MusEGlobal::song->follow()) { case MusECore::Song::NO: break; case MusECore::Song::JUMP: if (npos >= width()) { int ppos = val - rmapxDev(width()/8); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } else if (npos < 0) { int ppos = val - rmapxDev(width()*3/4); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } break; case MusECore::Song::CONTINUOUS: if (npos > (width()/2)) { int ppos = pos[idx] - rmapxDev(width()/2); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } else if (npos < (width()/2)) { int ppos = pos[idx] - rmapxDev(width()/2); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } break; } } int x; int w = 1; if (opos > npos) { w += opos - npos; x = npos; } else { w += npos - opos; x = opos; } pos[idx] = val; redraw(QRect(x-1, 0, w+2, height())); } //--------------------------------------------------------- // leaveEvent //--------------------------------------------------------- void Master::leaveEvent(QEvent*) { emit tempoChanged(-1); emit timeChanged(INT_MAX); } //--------------------------------------------------------- // pdraw //--------------------------------------------------------- void Master::pdraw(QPainter& p, const QRect& rect, const QRegion&) { View::pdraw(p, rect); // calls draw() p.resetTransform(); int x = rect.x(); int y = rect.y(); int w = rect.width() + 2; int h = rect.height(); int wh = height(); QColor graph_fg_color = MusEGlobal::config.ctrlGraphFg; graph_fg_color.setAlpha(MusEGlobal::config.globalAlphaBlend); QPen pen; pen.setCosmetic(true); //--------------------------------------------------- // draw Canvas Items //--------------------------------------------------- const MusECore::TempoList* tl = &MusEGlobal::tempomap; for (MusECore::ciTEvent i = tl->begin(); i != tl->end(); ++i) { MusECore::TEvent* e = i->second; int etick = mapx(i->first); int stick = mapx(i->second->tick); int tempo = mapy(280000 - int(60000000000.0/(e->tempo))); if (tempo < 0) tempo = 0; if (tempo < wh) p.fillRect(stick, tempo, etick-stick, wh, graph_fg_color); } //--------------------------------------------------- // draw marker //--------------------------------------------------- pen.setColor(MusEGlobal::config.rangeMarkerColor); p.setPen(pen); int xp = mapx(pos[1]); if (xp >= x && xp < x+w) { p.drawLine(xp, y, xp, y+h); } xp = mapx(pos[2]); if (xp >= x && xp < x+w) { p.drawLine(xp, y, xp, y+h); } pen.setColor(MusEGlobal::config.positionMarkerColor); p.setPen(pen); // Draw the red main position cursor last, on top of the others. xp = mapx(pos[0]); if (xp >= x && xp < x+w) { p.drawLine(xp, y, xp, y+h); } } //--------------------------------------------------------- // draw //--------------------------------------------------------- void Master::draw(QPainter& p, const QRect& rect, const QRegion& rg) { if (MusEGlobal::config.canvasShowGrid) { drawTickRaster(p, rect, rg, editor->raster(), false, false, false, MusEGlobal::config.midiCanvasBeatColor, MusEGlobal::config.midiCanvasBeatColor, MusEGlobal::config.midiCanvasFineColor, MusEGlobal::config.midiCanvasBarColor ); } if ((tool == MusEGui::DrawTool) && drawLineMode) { QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); p.drawLine(line1x, line1y, line2x, line2y); } } //--------------------------------------------------------- // newValRamp //--------------------------------------------------------- void Master::newValRamp(int x1, int y1, int x2, int y2, MusECore::Undo& operations) { // loop through all tick positions between x1 and x2 // remove all tempo changes and add new ones for changed if(x1 < 0) x1 = 0; if(x2 < 0) x2 = 0; // line drawn from right to left... if (x1 > x2) { qSwap(x1, x2); qSwap(y1, y2); } int tickStart = editor->rasterVal1(x1); int tickEnd = editor->rasterVal2(x2); const MusECore::TempoList* tl = &MusEGlobal::tempomap; for (MusECore::ciTEvent i = tl->begin(); i != tl->end(); ++i) { MusECore::TEvent* e = i->second; int startOldTick = i->second->tick; //if (startOldTick > tickStart && startOldTick <= tickEnd ) { // REMOVE Tim. Sharing. Wrong comparison? if (startOldTick >= tickStart && startOldTick > 0 && startOldTick < tickEnd ) { // Erasure at tick 0 forbidden. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteTempo, startOldTick, e->tempo)); //printf("tempo = %f %d %d\n", 60000000.0/e->tempo, endOldTick, startOldTick); } } int priorTick = editor->rasterVal1(x1); int tempoVal = int(60000000000.0/(280000 - y1)); operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTempo, tickStart, tempoVal)); int tick = editor->rasterVal1(x1); for (int i = x1; tick < tickEnd; i++) { tick = editor->rasterVal1(i); if (tick > priorTick) { double xproportion = double(tick-tickStart)/double(tickEnd-tickStart); int yproportion = double(y2 - y1) * xproportion; int yNew = y1 + yproportion; int tempoVal = int(60000000000.0/(280000 - yNew)); //printf("tickStart %d tickEnd %d yNew %d xproportion %f yproportion %d\n", tickStart, tickEnd, yNew, xproportion, yproportion); operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTempo, tick, tempoVal)); priorTick = tick; } } } //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- void Master::viewMousePressEvent(QMouseEvent* event) { if (event->button() == Qt::RightButton) { callContextMenu(); return; } start = event->pos(); int xpos = start.x(); int ypos = start.y(); _operations.clear(); MusEGui::Tool activeTool = tool; switch (activeTool) { case MusEGui::PencilTool: drag = DRAG_NEW; MusEGlobal::song->startUndo(); newVal(start.x(), start.x(), start.y(), _operations); break; case MusEGui::RubberTool: drag = DRAG_DELETE; MusEGlobal::song->startUndo(); deleteVal(start.x(), start.x(), _operations); break; case MusEGui::DrawTool: if (drawLineMode) { line2x = xpos; line2y = ypos; newValRamp(line1x, line1y, line2x, line2y, _operations); // Operation is undoable. MusEGlobal::song->applyOperationGroup(_operations); _operations.clear(); drawLineMode = false; } else { line2x = line1x = xpos; line2y = line1y = ypos; drawLineMode = true; } redraw(); return; break; default: break; } // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperationGroup(_operations, MusECore::Song::OperationUndoable); redraw(); } //--------------------------------------------------------- // viewMouseMoveEvent //--------------------------------------------------------- void Master::viewMouseMoveEvent(QMouseEvent* event) { QPoint pos = event->pos(); if (tool == MusEGui::DrawTool && drawLineMode) { line2x = pos.x(); line2y = pos.y(); redraw(); return; } _operations.clear(); switch (drag) { case DRAG_NEW: newVal(start.x(), pos.x(), pos.y(), _operations); start = pos; break; case DRAG_DELETE: deleteVal(start.x(), pos.x(), _operations); start = pos; break; default: break; } // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperationGroup(_operations, MusECore::Song::OperationUndoable); emit tempoChanged(280000 - event->y()); int x = pos.x(); if (x < 0) x = 0; emit timeChanged(editor->rasterVal(x)); } //--------------------------------------------------------- // viewMouseReleaseEvent //--------------------------------------------------------- void Master::viewMouseReleaseEvent(QMouseEvent*) { _operations.clear(); switch (drag) { case DRAG_RESIZE: case DRAG_NEW: case DRAG_DELETE: MusEGlobal::song->endUndo(SC_TEMPO); break; default: break; } drag = DRAG_OFF; } //--------------------------------------------------------- // deleteVal //--------------------------------------------------------- bool Master::deleteVal1(unsigned int x1, unsigned int x2, MusECore::Undo& operations) { QList< QPair > stuff_to_do; MusECore::TempoList* tl = &MusEGlobal::tempomap; for (MusECore::iTEvent i = tl->begin(); i != tl->end(); ++i) { if (i->first < x1) continue; if (i->first >= x2) break; MusECore::iTEvent ii = i; ++ii; if (ii != tl->end()) { int tempo = ii->second->tempo; // changed by flo: postpone the actual delete operation // to avoid race conditions and invalidating the iterator //MusEGlobal::audio->msgDeleteTempo(i->first, tempo, false); stuff_to_do.append(QPair(i->first, tempo)); } } for (QList< QPair >::iterator it=stuff_to_do.begin(); it!=stuff_to_do.end(); it++) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteTempo, it->first, it->second)); return !stuff_to_do.empty(); } void Master::deleteVal(int x1, int x2, MusECore::Undo& operations) { if(x1 < 0) x1 = 0; if(x2 < 0) x2 = 0; if (deleteVal1(editor->rasterVal1(x1), x2, operations)) redraw(); } //--------------------------------------------------------- // setTool //--------------------------------------------------------- void Master::setTool(int t) { if (tool == MusEGui::Tool(t)) return; tool = MusEGui::Tool(t); switch(tool) { case MusEGui::PencilTool: setCursor(*pencilCursor); break; case MusEGui::RubberTool: setCursor(*deleteCursor); break; case MusEGui::DrawTool: setCursor(*drawCursor); drawLineMode = false; break; default: setCursor(QCursor(Qt::ArrowCursor)); break; } } //--------------------------------------------------------- // newVal //--------------------------------------------------------- void Master::newVal(int x1, int x2, int y, MusECore::Undo& operations) { if(x1 < 0) x1 = 0; if(x2 < 0) x2 = 0; int xx1 = editor->rasterVal1(x1); int xx2 = editor->rasterVal2(x2); if (xx1 > xx2) { int tmp = xx2; xx2 = xx1; xx1 = tmp; } deleteVal1(xx1, xx2, operations); operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTempo, xx1, int(60000000000.0/(280000 - y)))); redraw(); } QMenu* Master::toolContextMenu() { QMenu* r_menu = new QMenu(this); QAction* act0 = nullptr; r_menu->addAction(new MenuTitleItem(tr("Tools"), r_menu)); int editTools = static_cast(editor)->getEditTools(); for (unsigned i = 0; i < static_cast(EditToolBar::toolList.size()); ++i) { if ((editTools & (1 << i)) == 0) continue; QAction* act = r_menu->addAction(QIcon(**EditToolBar::toolList[i].icon), tr(EditToolBar::toolList[i].tip)); if (MusEGui::EditToolBar::toolShortcuts.contains(1 << i)) { act->setShortcut(MusEGui::shortcuts[MusEGui::EditToolBar::toolShortcuts[1 << i]].key); } act->setData(editTools & (1 << i)); act->setCheckable(true); act->setChecked((1 << i) == tool); if (!act0) act0 = act; } r_menu->setActiveAction(act0); return r_menu; } void Master::callContextMenu() { QMenu * cm = toolContextMenu(); if (cm) { QAction *act = cm->exec(QCursor::pos()); if (act && act->data().isValid()) { int selTool = act->data().toInt(); static_cast(editor)->setEditTool(selTool); } delete cm; } } } // namespace MusEGui muse-4.0.0/src/muse/master/master.h000066400000000000000000000063501404105311000171540ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: master.h,v 1.3 2004/04/11 13:03:32 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MASTER_H__ #define __MASTER_H__ #include "type_defs.h" #include "view.h" #include "tools.h" #include "undo.h" class QMouseEvent; class QPainter; class QPoint; class QRect; class QToolBar; namespace MusEGui { class MidiEditor; class ScrollScale; //--------------------------------------------------------- // Master //--------------------------------------------------------- class Master : public MusEGui::View { Q_OBJECT enum DragMode { DRAG_OFF, DRAG_NEW, DRAG_MOVE_START, DRAG_MOVE, DRAG_DELETE, DRAG_COPY_START, DRAG_COPY, DRAG_RESIZE, DRAG_LASSO_START, DRAG_LASSO }; ScrollScale* vscroll; unsigned pos[3]; QPoint start; MusEGui::Tool tool; DragMode drag; MidiEditor* editor; int line1x; int line1y; int line2x; int line2y; bool drawLineMode; virtual void pdraw(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void viewMouseMoveEvent(QMouseEvent* event); virtual void leaveEvent(QEvent*e); virtual void viewMousePressEvent(QMouseEvent* event); virtual void viewMouseReleaseEvent(QMouseEvent*); void draw(QPainter&, const QRect&, const QRegion& = QRegion()); void newVal(int x1, int x2, int y, MusECore::Undo& operations); bool deleteVal1(unsigned int x1, unsigned int x2, MusECore::Undo& operations); void deleteVal(int x1, int x2, MusECore::Undo& operations); void newValRamp(int x1, int y1, int x2, int y2, MusECore::Undo& operations); QMenu* toolContextMenu(); void callContextMenu(); protected: // Accumulated operations during drawing etc. MusECore::Undo _operations; private slots: void songChanged(MusECore::SongChangedStruct_t); signals: void followEvent(int); void xposChanged(int); void yposChanged(int); void timeChanged(unsigned); void tempoChanged(int); public slots: void redrawGrid() { redraw(); } void setPos(int, unsigned, bool adjustScrollbar); void setTool(int t); public: Master(MidiEditor*, QWidget* parent, int xmag, int ymag); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/master/masteredit.cpp000066400000000000000000000443421404105311000203600ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: masteredit.cpp,v 1.4.2.5 2009/07/01 22:14:56 spamatica Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "masteredit.h" #include "utils.h" #include "lcombo.h" #include "doublelabel.h" #include "globals.h" #include "app.h" #include "gconfig.h" #include "audio.h" #include "song.h" #include "icons.h" #include #include #include #include #include #include #include #include #include // Forwards from header: #include #include #include #include "master.h" #include "mtscale.h" #include "poslabel.h" #include "scrollscale.h" #include "sigscale.h" #include "tempolabel.h" #include "tscale.h" #include "raster_widgets.h" #include "xml.h" #include "shortcuts.h" namespace MusEGui { int MasterEdit::_rasterInit = 0; //--------------------------------------------------------- // keyPressEvent //--------------------------------------------------------- void MasterEdit::keyPressEvent(QKeyEvent* e) { if (e->key() == Qt::Key_Escape) close(); else if (e->key() == shortcuts[SHRT_TOOL_PENCIL].key) tools2->set(MusEGui::PencilTool); else if (e->key() == shortcuts[SHRT_TOOL_RUBBER].key) tools2->set(MusEGui::RubberTool); else if (e->key() == shortcuts[SHRT_TOOL_LINEDRAW].key) tools2->set(MusEGui::DrawTool); else e->ignore(); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void MasterEdit::closeEvent(QCloseEvent* e) { _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. emit isDeleting(static_cast(this)); e->accept(); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MasterEdit::songChanged(MusECore::SongChangedStruct_t type) { if(_isDeleting) // Ignore while while deleting to prevent crash. return; if (type & SC_DIVISION_CHANGED) { // The division has changed. The raster table and raster model will have been // cleared and re-filled, so any views on the model will no longer have a // current item and our current raster value will be invalid. They WILL NOT // emit an activated signal. So we must manually select a current item and // raster value here. We could do something fancy to try to keep the current // index - for example stay on quarter note - by taking the ratio of the new // division to old division and apply that to the old raster value and try // to select that index, but the division has already changed. // So instead, simply try to select the current raster value. The index in the box may change. changeRaster(_raster); // Now set a reasonable zoom (mag) range. setupHZoomRange(); } if (type & SC_SIG) { sign->redraw(); } } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void MasterEdit::configChanged() { gridOnButton->blockSignals(true); gridOnButton->setChecked(MusEGlobal::config.canvasShowGrid); gridOnButton->blockSignals(false); canvas->redraw(); } //--------------------------------------------------------- // MasterEdit //--------------------------------------------------------- MasterEdit::MasterEdit(QWidget* parent, const char* name) : MidiEditor(TopWin::MASTER, _rasterInit, 0, parent, name) { isMdiWin() ? setWindowTitle(tr("Mastertrack")) : setWindowTitle(tr("MusE: Mastertrack")); setFocusPolicy(Qt::NoFocus); _canvasXOrigin = 0; // _minXMag = -100; _minXMag = -500; _maxXMag = 2; _rasterizerModel->setDisplayFormat(RasterizerModel::FractionFormat); _rasterizerModel->setMaxRows(7); QList rast_cols; rast_cols << Rasterizer::TripletColumn << Rasterizer::NormalColumn << Rasterizer::DottedColumn; _rasterizerModel->setVisibleColumns(rast_cols); // Request to set the raster, but be sure to use the one it chooses, // which may be different than the one requested. _rasterInit = _rasterizerModel->checkRaster(_rasterInit); _raster = _rasterInit; //---------Pulldown Menu---------------------------- QMenu* settingsMenu = menuBar()->addMenu(tr("&Display")); settingsMenu->menuAction()->setStatusTip(tr("Display menu: Display options specific to current editor.")); settingsMenu->addAction(subwinAction); // settingsMenu->addAction(shareAction); settingsMenu->addAction(fullscreenAction); // Toolbars --------------------------------------------------------- // NOTICE: Please ensure that any tool bar object names here match the names assigned // to identical or similar toolbars in class MusE or other TopWin classes. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). addToolBarBreak(); // Already has an object name. editTools = MusEGui::PencilTool | MusEGui::RubberTool| MusEGui::DrawTool; tools2 = new MusEGui::EditToolBar(this, editTools); addToolBar(tools2); QToolBar* info = addToolBar(tr("Info")); // Make it appear like a Toolbar1 object. info->setObjectName("Pos/Snap/Solo-tools"); QLabel* label = new QLabel(tr("Cursor")); label->setIndent(2); info->addWidget(label); cursorPos = new MusEGui::PosLabel(nullptr, "PosLabel"); cursorPos->setFixedHeight(22); cursorPos->setToolTip(tr("Time at cursor position")); info->addWidget(cursorPos); tempo = new MusEGui::TempoLabel(nullptr, "TempoLabel"); tempo->setFixedHeight(22); tempo->setToolTip(tr("Tempo at cursor position")); info->addWidget(tempo); gridOnButton = new QToolButton(); gridOnButton->setIcon(*gridOnSVGIcon); gridOnButton->setFocusPolicy(Qt::NoFocus); gridOnButton->setCheckable(true); gridOnButton->setToolTip(tr("Show grid")); gridOnButton->setWhatsThis(tr("Show grid")); info->addWidget(gridOnButton); connect(gridOnButton, &QToolButton::toggled, [this](bool v) { gridOnChanged(v); } ); rasterLabel = new RasterLabelCombo(RasterLabelCombo::TableView, _rasterizerModel, this, "RasterLabelCombo"); rasterLabel->setFocusPolicy(Qt::TabFocus); info->addWidget(rasterLabel); //--------------------------------------------------- // master //--------------------------------------------------- int xscale = -20; int yscale = -500; hscroll = new MusEGui::ScrollScale( (_minXMag * MusEGlobal::config.division) / 384, _maxXMag, xscale, MusEGlobal::song->len(), Qt::Horizontal, mainw); vscroll = new MusEGui::ScrollScale(-1000, -100, yscale, 120000, Qt::Vertical, mainw); vscroll->setRange(30000, 250000); time1 = new MusEGui::MTScale(_raster, mainw, xscale); sign = new MusEGui::SigScale(&_raster, mainw, xscale); canvas = new Master(this, mainw, xscale, yscale); time2 = new MusEGui::MTScale(_raster, mainw, xscale); tscale = new TScale(mainw, yscale); time2->setBarLocator(true); canvas->setOrigin(_canvasXOrigin, 0); time1->setOrigin(_canvasXOrigin, 0); changeRaster(_raster); //--------------------------------------------------- // Rest //--------------------------------------------------- mainGrid->setRowStretch(5, 100); mainGrid->setColumnStretch(1, 100); mainGrid->addWidget(MusECore::hLine(mainw), 0, 1); mainGrid->addWidget(time1, 1, 1); mainGrid->addWidget(MusECore::hLine(mainw), 2, 1); mainGrid->addWidget(sign, 3, 1); mainGrid->addWidget(MusECore::hLine(mainw), 4, 1); mainGrid->addWidget(canvas, 5, 1); mainGrid->addWidget(tscale, 5, 0); mainGrid->addWidget(MusECore::hLine(mainw), 6, 1); mainGrid->addWidget(time2, 7, 1); mainGrid->addWidget(hscroll, 8, 1); mainGrid->addWidget(vscroll, 0, 2, 10, 1); canvas->setFocus(); connect(tools2, SIGNAL(toolChanged(int)), canvas, SLOT(setTool(int))); connect(MusEGlobal::muse, &MusE::configChanged, tools2, &EditToolBar::configChanged); connect(vscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setYPos(int))); connect(vscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setYMag(int))); connect(vscroll, SIGNAL(scrollChanged(int)), tscale, SLOT(setYPos(int))); connect(vscroll, SIGNAL(scaleChanged(int)), tscale, SLOT(setYMag(int))); connect(hscroll, SIGNAL(scrollChanged(int)), time1, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scrollChanged(int)), sign, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scrollChanged(int)), time2, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), time1, SLOT(setXMag(int))); connect(hscroll, SIGNAL(scaleChanged(int)), sign, SLOT(setXMag(int))); connect(hscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setXMag(int))); connect(hscroll, SIGNAL(scaleChanged(int)), time2, SLOT(setXMag(int))); connect(time1, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(time2, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(tscale, SIGNAL(tempoChanged(int)), SLOT(setTempo(int))); connect(canvas, SIGNAL(tempoChanged(int)), SLOT(setTempo(int))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::muse, &MusE::configChanged, [this]() { configChanged(); } ); connect(canvas, SIGNAL(followEvent(int)), hscroll, SLOT(setOffset(int))); connect(canvas, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(rasterLabel, &RasterLabelCombo::rasterChanged, [this](int raster) { _setRaster(raster); } ); configChanged(); // set configuration values initTopwinState(); finalizeInit(); } //--------------------------------------------------------- // ~MasterEdit //--------------------------------------------------------- MasterEdit::~MasterEdit() { } //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void MasterEdit::readStatus(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "midieditor") MidiEditor::readStatus(xml); else if (tag == "xpos") hscroll->setPos(xml.parseInt()); else if (tag == "xmag") hscroll->setMag(xml.parseInt()); else if (tag == "ypos") vscroll->setPos(xml.parseInt()); else if (tag == "ymag") vscroll->setMag(xml.parseInt()); else xml.unknown("MasterEdit"); break; case MusECore::Xml::TagEnd: if (tag == "master") { // set raster changeRaster(_raster); return; } default: break; } } } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void MasterEdit::writeStatus(int level, MusECore::Xml& xml) const { xml.tag(level++, "master"); xml.intTag(level, "xmag", hscroll->mag()); xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", vscroll->mag()); xml.intTag(level, "ypos", vscroll->pos()); MidiEditor::writeStatus(level, xml); xml.tag(level, "/master"); } //--------------------------------------------------------- // readConfiguration //--------------------------------------------------------- void MasterEdit::readConfiguration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "raster") _rasterInit = xml.parseInt(); else if (tag == "topwin") TopWin::readConfiguration(MASTER, xml); else xml.unknown("MasterEdit"); break; case MusECore::Xml::TagEnd: if (tag == "masteredit") return; default: break; } } } //--------------------------------------------------------- // writeConfiguration //--------------------------------------------------------- void MasterEdit::writeConfiguration(int level, MusECore::Xml& xml) { xml.tag(level++, "masteredit"); xml.intTag(level, "raster", _rasterInit); TopWin::writeConfiguration(MASTER, level, xml); xml.tag(level, "/masteredit"); } //--------------------------------------------------------- // focusCanvas //--------------------------------------------------------- void MasterEdit::focusCanvas() { if(MusEGlobal::config.smartFocus) { canvas->setFocus(); canvas->activateWindow(); } } //--------------------------------------------------------- // _setRaster //--------------------------------------------------------- void MasterEdit::_setRaster(int raster) { MidiEditor::setRaster(raster); _rasterInit = _raster; time1->setRaster(_raster); time2->setRaster(_raster); canvas->redrawGrid(); for (auto it : ctrlEditList) it->redrawCanvas(); focusCanvas(); } //--------------------------------------------------------- // changeRaster //--------------------------------------------------------- int MasterEdit::changeRaster(int val) { const RasterizerModel* rast_mdl = rasterLabel->rasterizerModel(); MidiEditor::setRaster(rast_mdl->checkRaster(val)); _rasterInit = _raster; time1->setRaster(_raster); time2->setRaster(_raster); const QModelIndex mdl_idx = rast_mdl->modelIndexOfRaster(_raster); if(mdl_idx.isValid()) rasterLabel->setCurrentModelIndex(mdl_idx); else fprintf(stderr, "MasterEdit::changeRaster: _raster %d not found in box!\n", _raster); canvas->redrawGrid(); for (auto it : ctrlEditList) it->redrawCanvas(); return _raster; } //--------------------------------------------------------- // setTime //--------------------------------------------------------- void MasterEdit::setTime(unsigned tick) { if (tick == INT_MAX) cursorPos->setEnabled(false); else { cursorPos->setEnabled(true); cursorPos->setValue(tick); time1->setPos(3, tick, false); time2->setPos(3, tick, false); } } //--------------------------------------------------------- // setTempo //--------------------------------------------------------- void MasterEdit::setTempo(int val) { if (val == -1) tempo->setEnabled(false); else { tempo->setEnabled(true); tempo->setValue(val); } } //--------------------------------------------------------- // setupHZoomRange //--------------------------------------------------------- void MasterEdit::setupHZoomRange() { const int min = (_minXMag * MusEGlobal::config.division) / 384; hscroll->setScaleRange(min, _maxXMag); } //--------------------------------------------------------- // setEditTool //--------------------------------------------------------- void MasterEdit::setEditTool(int tool) { tools2->set(tool); } //--------------------------------------------------------- // gridOnChanged //--------------------------------------------------------- void MasterEdit::gridOnChanged(bool v) { MusEGlobal::config.canvasShowGrid = v; // We want the simple version, don't set the style or stylesheet yet. MusEGlobal::muse->changeConfig(true); } } // namespace MusEGui muse-4.0.0/src/muse/master/masteredit.h000066400000000000000000000064141404105311000200230ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: masteredit.h,v 1.3.2.2 2009/04/01 01:37:11 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MASTER_EDIT_H__ #define __MASTER_EDIT_H__ #include "type_defs.h" #include "midieditor.h" #include "noteinfo.h" #include "cobject.h" // Forward declarations: class QCloseEvent; class QToolBar; class QToolButton; namespace MusEGui { class Master; class MTScale; class PosLabel; class ScrollScale; class SigScale; class TempoEdit; class TempoLabel; class TScale; class Xml; class RasterLabelCombo; class EditToolBar; //--------------------------------------------------------- // MasterEdit //--------------------------------------------------------- class MasterEdit : public MidiEditor { Q_OBJECT Master* canvas; MusEGui::ScrollScale* hscroll; MusEGui::ScrollScale* vscroll; MusEGui::MTScale* time1; MusEGui::MTScale* time2; MusEGui::SigScale* sign; TScale* tscale; RasterLabelCombo* rasterLabel; QToolBar* tools; MusEGui::PosLabel* cursorPos; MusEGui::TempoLabel* tempo; MusEGui::EditToolBar* tools2; QToolButton* gridOnButton; int editTools; static int _rasterInit; virtual void keyPressEvent(QKeyEvent*); virtual void closeEvent(QCloseEvent*); // Sets up a reasonable zoom minimum and/or maximum based on // the current global midi division (ticks per quarter note) // which has a very wide range (48 - 12288). // Also sets the canvas and time scale offsets accordingly. void setupHZoomRange(); private slots: void _setRaster(int raster); void setTime(unsigned); void setTempo(int); void configChanged(); void gridOnChanged(bool v); public slots: void songChanged(MusECore::SongChangedStruct_t); void focusCanvas(); signals: void isDeleting(MusEGui::TopWin*); public: MasterEdit(QWidget* parent = 0, const char* name = 0); ~MasterEdit(); virtual void readStatus(MusECore::Xml&); virtual void writeStatus(int, MusECore::Xml&) const; static void readConfiguration(MusECore::Xml&); static void writeConfiguration(int, MusECore::Xml&); // Same as setRaster() but returns the actual value used. int changeRaster(int val); void setEditTool(int tool); int getEditTools() { return editTools; }; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/master/tscale.cpp000066400000000000000000000056101404105311000174650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tscale.cpp,v 1.2 2003/12/17 11:04:14 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "tscale.h" #include "globals.h" #include "gconfig.h" #include #include #include namespace MusEGui { //--------------------------------------------------------- // TScale //--------------------------------------------------------- TScale::TScale(QWidget* parent, int ymag) : View(parent, 1, ymag) { setFont(MusEGlobal::config.fonts[5]); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = 4 * fontMetrics().horizontalAdvance('0'); #else int w = 4 * fontMetrics().width('0'); #endif setFixedWidth(w); setMouseTracking(true); } //--------------------------------------------------------- // draw //--------------------------------------------------------- void TScale::pdraw(QPainter& p, const QRect& r, const QRegion&) { QStyleOption opt; opt.init(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); int y = r.y(); int h = r.height(); QString s; p.setPen(palette().color(QPalette::WindowText)); for (int i = 30000; i <= 250000; i += 10000) { int yy = mapy(280000 - i); if (yy < y) break; if (yy-15 > y+h) continue; p.drawLine(0, yy, width(), yy); s.setNum(i/1000); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 p.drawText(width() - fontMetrics().horizontalAdvance(s) - 1, yy-2, s); // Use the window font. Tim p4.0.31 #else p.drawText(width() - fontMetrics().width(s) - 1, yy-2, s); // Use the window font. Tim p4.0.31 #endif } } void TScale::viewMouseMoveEvent(QMouseEvent* event) { emit tempoChanged(280000 - event->y()); } void TScale::leaveEvent(QEvent*) { emit tempoChanged(-1); } } // namespace MusEGui muse-4.0.0/src/muse/master/tscale.h000066400000000000000000000031121404105311000171250ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tscale.h,v 1.1.1.1 2003/10/27 18:52:36 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TSCALE_H__ #define __TSCALE_H__ #include "view.h" namespace MusEGui { //--------------------------------------------------------- // Tscale //--------------------------------------------------------- class TScale : public View { Q_OBJECT double curTempo; virtual void viewMouseMoveEvent(QMouseEvent* event); virtual void leaveEvent(QEvent*e); protected: virtual void pdraw(QPainter&, const QRect&, const QRegion& = QRegion()); signals: void tempoChanged(int); public: TScale(QWidget*, int); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/metronome_class.cpp000066400000000000000000000513051404105311000201130ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // // metronome_class.cpp // (C) Copyright 2019 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 "metronome_class.h" namespace MusECore { // Static. std::uint64_t MetroAccentsStruct::_idGen = 0; //--------------------------------------------------------- // assign // Assigns the members of the given MetroAccentsStruct to this one, EXCEPT for the ID. // Returns this MetroAccentsStruct. //--------------------------------------------------------- MetroAccentsStruct& MetroAccentsStruct::assign(const MetroAccentsStruct& m) { _type = m._type; _accents = m._accents; return *this; } //--------------------------------------------------------- // isBlank // Returns true if all the accents are 'off'. //--------------------------------------------------------- bool MetroAccentsStruct::isBlank(MetroAccent::AccentTypes_t types) const { return _accents.isBlank(types); } //--------------------------------------------------------- // blank // Returns true if all the accents are 'off'. //--------------------------------------------------------- void MetroAccentsStruct::blank(MetroAccent::AccentTypes_t types) { _accents.blank(types); } //--------------------------------------------------------- // copy // Creates a copy of this MetroAccentsStruct but with a new ID. //--------------------------------------------------------- MetroAccentsStruct MetroAccentsStruct::copy() const { return MetroAccentsStruct(_type).assign(*this); } //--------------------------------------------------------- // loadMDF (Metronome Definition File) //--------------------------------------------------------- static void loadMDF(const QString& filepath, MetroAccentsPresetsMap* presetMap, bool debug = false) { QFile f(filepath); if(!f.open(QIODevice::ReadOnly /*| QIODevice::Text*/)) return; if (debug) fprintf(stderr, "READ MDF %s\n", filepath.toLatin1().constData()); Xml xml(&f); bool skipmode = true; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto loadMDF_end; case Xml::TagStart: if (skipmode && tag == "muse") skipmode = false; else if (skipmode) break; else if (tag == "metroAccPresets") { presetMap->read(xml); } else xml.unknown("muse"); break; case Xml::Attribut: break; case Xml::TagEnd: if (!skipmode && tag == "muse") goto loadMDF_end; default: break; } } loadMDF_end: f.close(); } void MetroAccentsPresetsMap::writeMDF(const QString& filepath, MetroAccentsStruct::MetroAccentsType type) const { QFile f(filepath); if(!f.open(QIODevice::WriteOnly /*| QIODevice::Text*/)) return; MusECore::Xml xml(&f); int level = 0; xml.header(); level = xml.putFileVersion(level); write(level, xml, type); level--; xml.etag(level, "muse"); f.close(); } void MetroAccentsPresetsMap::defaultAccents(MetroAccentsMap* accents, MetroAccentsStruct::MetroAccentsType type) const { const_iterator ipm_end = cend(); for(const_iterator i = cbegin(); i != ipm_end; ++i) { const int& beats = i->first; const MetroAccentsPresets& pre = i->second; if(!pre.empty()) { MetroAccentsPresets::const_iterator imap_end = pre.cend(); for(MetroAccentsPresets::const_iterator imap = pre.cbegin(); imap != imap_end; ++imap) { MetroAccentsStruct mas = *imap; if(mas._type != type) continue; // Change the type to user. mas._type = MetroAccentsStruct::MetroAccentsType::User; std::pair res = accents->insert(std::pair(beats, mas)); if(!res.second) res.first->second = mas; break; } } } } void MetroAccentsPresetsMap::write(int level, MusECore::Xml& xml, MetroAccentsStruct::MetroAccentsType type) const { for (const_iterator i = cbegin(); i != cend(); ++i) i->second.write(level, xml, i->first, type); } void MetroAccentsPresetsMap::read(MusECore::Xml& xml) { bool ok; int beats = 0; MetroAccentsStruct::MetroAccentsType type = MetroAccentsStruct::NoType; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: xml.unknown("metroAccPresets"); break; case MusECore::Xml::Attribut: if (tag == "type") { const int itype = xml.s2().toInt(); switch(itype) { case MetroAccentsStruct::UserPreset: type = MetroAccentsStruct::UserPreset; break; case MetroAccentsStruct::FactoryPreset: type = MetroAccentsStruct::FactoryPreset; break; // Don't want these. case MetroAccentsStruct::User: default: break; } } else if (tag == "beats") beats = xml.s2().toInt(); break; case Xml::Text: { if(type == MetroAccentsStruct::NoType) { fprintf(stderr, "MetroAccentsPresets::read: Unknown type\n"); break; } int len = tag.length(); int i = 0; for(;;) { int acctypes = MetroAccent::NoAccent; MetroAccentsStruct mas(type); for(;;) { while(i < len && (tag[i] == ',' || tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; QString fs; while(i < len && tag[i] != ',' && tag[i] != ' ') { fs.append(tag[i]); ++i; } if(i == len) break; acctypes = fs.toInt(&ok); if(!ok) { fprintf(stderr, "MetroAccentsPresets::read failed reading accent types string: %s\n", fs.toLatin1().constData()); break; } MetroAccent ma; ma._accentType = acctypes; mas._accents.push_back(ma); while(i < len && (tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len || tag[i] != ',') break; } // Don't bother reading if there are no accents set. if(beats > 0 && !mas.isBlank()) { std::pair pm_res = insert(std::pair(beats, MetroAccentsPresets())); iterator ipm = pm_res.first; MetroAccentsPresets& mp = ipm->second; MetroAccentsPresets::iterator imap = mp.find(mas, MetroAccentsStruct::FactoryPreset | MetroAccentsStruct::UserPreset); if(imap == mp.end()) mp.push_back(mas); else // In case of duplicates (an existing found), replace with latest. *imap = mas; } while(i < len && (tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; } } break; case MusECore::Xml::TagEnd: if (tag == "metroAccPresets") { return; } default: break; } } } MetroAccentsPresets::iterator MetroAccentsPresets::find( const MetroAccentsStruct& mas, const MetroAccentsStruct::MetroAccentsTypes_t& types) { iterator iend = end(); for(iterator i = begin(); i != iend; ++i) { const MetroAccentsStruct& m = *i; const MetroAccentsStruct::MetroAccentsType& m_type = m._type; if(m._accents == mas._accents && (types & m_type)) return i; } return iend; } MetroAccentsPresets::const_iterator MetroAccentsPresets::find( const MetroAccentsStruct& mas, const MetroAccentsStruct::MetroAccentsTypes_t& types) const { const_iterator iend = cend(); for(const_iterator i = cbegin(); i != iend; ++i) { const MetroAccentsStruct& m = *i; const MetroAccentsStruct::MetroAccentsType& m_type = m._type; if(m._accents == mas._accents && (types & m_type)) return i; } return iend; } MetroAccentsPresets::iterator MetroAccentsPresets::findId(std::uint64_t id) { iterator iend = end(); for(iterator i = begin(); i != iend; ++i) { if(i->id() == id) return i; } return iend; } MetroAccentsPresets::const_iterator MetroAccentsPresets::findId(std::uint64_t id) const { const_iterator iend = cend(); for(const_iterator i = cbegin(); i != iend; ++i) { if(i->id() == id) return i; } return iend; } void MetroAccentsPresets::write(int level, MusECore::Xml& xml, int beats, MetroAccentsStruct::MetroAccentsType type) const { if(empty()) return; // Check if there's anything to write. const_iterator imap = cbegin(); for( ; imap != cend(); ++imap) { if(imap->_type == type) break; } if(imap == cend()) return; xml.tag(level++, "metroAccPresets type=\"%d\" beats=\"%d\"", type, beats); for(const_iterator i = cbegin(); i != cend(); ++i) { if(i->_type != type) continue; i->write(level, xml); } xml.tag(--level, "/metroAccPresets"); } void MetroAccentsStruct::write(int level, MusECore::Xml& xml) const { // Don't bother writing if there are no accents set. if(isBlank()) return; QString acc_s; const int sz_m1 = _accents.size() - 1; int count1 = 0; int count2 = 0; int level_in = 0; for(MetroAccents::const_iterator i = _accents.cbegin(); i != _accents.cend(); ++count1, ++i) { const MetroAccent& acc = *i; acc_s += QString::number(acc._accentType); if(count1 < sz_m1) acc_s += ", "; ++count2; if(count2 >= 16) { xml.put(level + level_in, "%s", acc_s.toLatin1().constData()); if(level_in == 0) level_in = 1; acc_s.clear(); count2 = 0; } } if(count2) xml.put(level + level_in, "%s", acc_s.toLatin1().constData()); } void MetroAccentsStruct::read(MusECore::Xml& xml) { bool ok; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: xml.unknown("MetroAccentsStruct"); break; case MusECore::Xml::Attribut: break; case Xml::Text: { int len = tag.length(); int acctypes = MetroAccent::NoAccent; int i = 0; for(;;) { while(i < len && (tag[i] == ',' || tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; QString fs; while(i < len && tag[i] != ',' && tag[i] != ' ') { fs.append(tag[i]); ++i; } if(i == len) break; acctypes = fs.toInt(&ok); if(!ok) { fprintf(stderr, "MetroAccentsStruct::read failed reading accent types string: %s\n", fs.toLatin1().constData()); break; } MetroAccent ma; ma._accentType = acctypes; _accents.push_back(ma); while(i < len && (tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len || tag[i] != ',') break; } } break; case MusECore::Xml::TagEnd: if (tag == "metroAccents") { return; } default: break; } } return; } bool MetroAccent::operator==(const MetroAccent& other) const { return _accentType == other._accentType; } bool MetroAccent::operator!=(const MetroAccent& other) const { return _accentType != other._accentType; } bool MetroAccent::isBlank(AccentTypes_t types) const { return (_accentType & types) == NoAccent; } void MetroAccent::blank(AccentTypes_t types) { _accentType &= ~types; } bool MetroAccents::operator==(const MetroAccents& other) const { const size_type sz = size(); if(sz != other.size()) return false; for(size_type i = 0; i < sz; ++i) { if(at(i) != other.at(i)) return false; } return true; } bool MetroAccents::isBlank(MetroAccent::AccentTypes_t types) const { const size_type sz = size(); for(size_type i = 0; i < sz; ++i) { if(!at(i).isBlank(types)) return false; } return true; } void MetroAccents::blank(MetroAccent::AccentTypes_t types) { iterator iend = end(); for(iterator i = begin(); i != iend; ++i) i->blank(types); } // Returns beats. int MetroAccentsMap::read(MusECore::Xml& xml) { bool ok; int beats = 0; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return 0; case MusECore::Xml::TagStart: xml.unknown("MetroAccentsMap"); break; case MusECore::Xml::Attribut: if (tag == "beats") beats = xml.s2().toInt(); break; case Xml::Text: { int len = tag.length(); int i = 0; int acctypes = MetroAccent::NoAccent; MetroAccentsStruct mas(MetroAccentsStruct::User); for(;;) { while(i < len && (tag[i] == ',' || tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len) break; QString fs; while(i < len && tag[i] != ',' && tag[i] != ' ') { fs.append(tag[i]); ++i; } if(i == len) break; acctypes = fs.toInt(&ok); if(!ok) { fprintf(stderr, "MetroAccentsMap::read failed reading accent types string: %s\n", fs.toLatin1().constData()); break; } MetroAccent ma; ma._accentType = acctypes; mas._accents.push_back(ma); while(i < len && (tag[i] == ' ' || tag[i] == '\n')) ++i; if(i == len || tag[i] != ',') break; } // Don't bother reading if there are no accents set. if(beats > 0 && !mas.isBlank()) { std::pair res = insert(std::pair(beats, mas)); if(!res.second) // In case of duplicate beats (an existing beats entry found), replace with latest. res.first->second = mas; } } break; case MusECore::Xml::TagEnd: if (tag == "metroAccMap") { return beats; } default: break; } } return 0; } void MetroAccentsMap::write(int level, MusECore::Xml& xml) const { for(const_iterator i = cbegin(); i != cend(); ++i) { // Don't bother writing if there are no accents set. if(i->second.isBlank()) continue; xml.tag(level, "metroAccMap beats=\"%d\"", i->first); i->second.write(level + 1, xml); xml.tag(level, "/metroAccMap"); } } //--------------------------------------------------------- // initMetronomePresets //--------------------------------------------------------- void initMetronomePresets(const QString& dir, MetroAccentsPresetsMap* presetMap, bool debug) { if(!QDir(dir).exists()) { fprintf(stderr, "Metronome directory not found: %s\n", dir.toLatin1().constData()); return; } if (debug) fprintf(stderr, "Load metronome presets from <%s>\n", dir.toLatin1().constData()); QDirIterator metro_di(dir, QStringList() << "*.mdf", QDir::Files | QDir::Readable | QDir::NoDotAndDotDot); while(metro_di.hasNext()) loadMDF(metro_di.next(), presetMap, debug); } MetronomeSettings::MetronomeSettings() { preMeasures = 2; measureClickNote = 37; measureClickVelo = 127; beatClickNote = 42; beatClickVelo = 120; accentClick1 = 44; accentClick1Velo = 100; accentClick2 = 42; accentClick2Velo = 100; clickChan = 9; clickPort = 0; precountEnableFlag = false; precountFromMastertrackFlag = true; precountSigZ = 4; precountSigN = 4; precountOnPlay = false; precountMuteMetronome = false; precountPrerecord = false; precountPreroll = false; midiClickFlag = false; audioClickFlag = true; audioClickVolume = 0.5f; measClickVolume = 1.0f; beatClickVolume = 1.0f; accent1ClickVolume = 0.1f; accent2ClickVolume = 0.1f; clickSamples = newSamples; measSample = QString("klick1.wav"); beatSample = QString("klick2.wav"); accent1Sample = QString("klick3.wav"); accent2Sample = QString("klick4.wav"); metroAccentsMap = new MetroAccentsMap(); } MetronomeSettings::~MetronomeSettings() { if(metroAccentsMap) delete metroAccentsMap; metroAccentsMap = nullptr; } } // namespace MusECore muse-4.0.0/src/muse/metronome_class.h000066400000000000000000000135741404105311000175660ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // // metronome_class.h // (C) Copyright 2019 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __METRONOME_CLASS_H__ #define __METRONOME_CLASS_H__ #include #include #include #include #include "xml.h" namespace MusECore { struct MetroAccent { public: enum AccentType { NoAccent = 0x0, Accent1 = 0x1, Accent2 = 0x2, AllAccents = Accent1 | Accent2 }; typedef int AccentTypes_t; AccentTypes_t _accentType; public: MetroAccent() : _accentType(NoAccent) { } // Returns true if the accents are the same in both structs. bool operator==(const MetroAccent& other) const; // Returns true if the accents are NOT the same in both structs. bool operator!=(const MetroAccent& other) const; // Returns true if the accents are 'off' (NoAccent). bool isBlank(AccentTypes_t types = AllAccents) const; // Blanks the given type of accent. void blank(AccentTypes_t types); }; class MetroAccents : public std::vector { public: // Returns true if all the accents are the same in both lists. bool operator==(const MetroAccents& other) const; // Returns true if all the accents are 'off'. bool isBlank(MetroAccent::AccentTypes_t types = MetroAccent::AllAccents) const; // Blanks all accents of a given type. void blank(MetroAccent::AccentTypes_t types); }; class MetroAccentsStruct { private: static std::uint64_t _idGen; std::uint64_t newId() { return _idGen++; } std::uint64_t _id; public: enum MetroAccentsType { NoType = 0x0, User = 0x1, UserPreset = 0x2, FactoryPreset = 0x4, AllTypes = User | UserPreset | FactoryPreset }; typedef int MetroAccentsTypes_t; MetroAccents _accents; MetroAccentsType _type; std::uint64_t id() const { return _id; } // Returns true if all the accents are 'off'. bool isBlank(MetroAccent::AccentTypes_t types = MetroAccent::AllAccents) const; // Blanks all accents of a given type. void blank(MetroAccent::AccentTypes_t types); // Assigns the members of the given MetroAccentsStruct to this one, EXCEPT for the ID. // Returns this MetroAccentsStruct. MetroAccentsStruct& assign(const MetroAccentsStruct&); // Creates a copy of this MetroAccentsStruct but with a new ID. MetroAccentsStruct copy() const; void read(MusECore::Xml&); void write(int, MusECore::Xml&) const; MetroAccentsStruct(MetroAccentsType type) : _id(newId()), _type(type) { } }; class MetroAccentsMap : public std::map > { public: // Returns beats. int read(MusECore::Xml&); void write(int, MusECore::Xml&) const; }; class MetroAccentsPresets : public std::vector { public: //// Returns beats. //int read(MusECore::Xml&); void write(int, MusECore::Xml&, int beats, MetroAccentsStruct::MetroAccentsType type) const; iterator find(const MetroAccentsStruct&, const MetroAccentsStruct::MetroAccentsTypes_t& types); const_iterator find(const MetroAccentsStruct&, const MetroAccentsStruct::MetroAccentsTypes_t& types) const; iterator findId(std::uint64_t id); const_iterator findId(std::uint64_t id) const; }; class MetroAccentsPresetsMap : public std::map > { public: void read(MusECore::Xml&); void write(int, MusECore::Xml&, MetroAccentsStruct::MetroAccentsType type) const; void writeMDF(const QString& filepath, MetroAccentsStruct::MetroAccentsType type) const; // Fills the given accents map with the first items found in this presets map. void defaultAccents(MetroAccentsMap* accents, MetroAccentsStruct::MetroAccentsType type) const; }; struct MetronomeSettings { enum ClickSamples { origSamples = 0, newSamples }; int preMeasures; unsigned char measureClickNote; unsigned char measureClickVelo; unsigned char beatClickNote; unsigned char beatClickVelo; unsigned char accentClick1; unsigned char accentClick1Velo; unsigned char accentClick2; unsigned char accentClick2Velo; unsigned char clickChan; unsigned char clickPort; bool precountEnableFlag; bool precountFromMastertrackFlag; int precountSigZ; int precountSigN; bool precountOnPlay; bool precountMuteMetronome; bool precountPrerecord; bool precountPreroll; bool midiClickFlag; bool audioClickFlag; float audioClickVolume; float measClickVolume; float beatClickVolume; float accent1ClickVolume; float accent2ClickVolume; ClickSamples clickSamples; QString measSample; QString beatSample; QString accent1Sample; QString accent2Sample; MusECore::MetroAccentsPresetsMap metroAccentPresets; // This is a pointer so that it can quickly swapped with a new map in real-time. MusECore::MetroAccentsMap* metroAccentsMap; MetronomeSettings(); ~MetronomeSettings(); }; extern void initMetronomePresets(const QString& dir, MetroAccentsPresetsMap* presetMap, bool debug = false); } // namespace MusECore #endif muse-4.0.0/src/muse/midi.cpp000066400000000000000000004541741404105311000156560ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midi.cpp,v 1.43.2.22 2009/11/09 20:28:28 terminator356 Exp $ // // (C) Copyright 1999/2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include "song.h" #include "midi.h" #include "drummap.h" #include "event.h" #include "globals.h" #include "midictrl.h" #include "marker/marker.h" #include "midiport.h" #include "minstrument.h" #include "midictrl.h" #include "sync.h" #include "audio.h" #include "audiodev.h" #include "mididev.h" #include "driver/alsamidi.h" #include "driver/jackmidi.h" #include "wave.h" #include "synth.h" #include "sync.h" #include "midiseq.h" #include "gconfig.h" #include "ticksynth.h" #include "mpevent.h" #include "metronome_class.h" #include "tempo.h" #include "sig.h" #include "keyevent.h" #include "track.h" // REMOVE Tim. Persistent routes. Added. Make this permanent later if it works OK and makes good sense. #define _USE_MIDI_ROUTE_PER_CHANNEL_ // Undefine if and when multiple output routes are added to midi tracks. #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // For debugging output: Uncomment the fprintf section. #define DEBUG_MIDI(dev, format, args...) // fprintf(dev, format, ##args); // For debugging metronome and precount output: Uncomment the fprintf section. #define DEBUG_MIDI_METRONOME(dev, format, args...) // fprintf(dev, format, ##args); // For debugging midi timing: Uncomment the fprintf section. #define DEBUG_MIDI_TIMING(dev, format, args...) // fprintf(dev, format, ##args); // For debugging midi event time differences. //#define DEBUG_MIDI_TIMING_DIFFS ; namespace MusECore { #ifdef DEBUG_MIDI_TIMING_DIFFS // For testing. unsigned _lastEvTime = 0; #endif extern void dump(const unsigned char* p, int n); // Division can be zero meaning the event times are to be taken verbosely // (as ticks already), no conversion is to be applied. #define CALC_TICK(the_tick) (div > 0 ? lrintf((float(the_tick) * float(MusEGlobal::config.division) + float(div/2)) / float(div)) : the_tick); /*--------------------------------------------------------- * midi_meta_name *---------------------------------------------------------*/ QString midiMetaName(int meta) { const char* s = ""; switch (meta) { case 0: s = "Text 0: Sequence Number"; break; case 1: s = "Text 1: Track comment"; break; case 2: s = "Text 2: Copyright"; break; case 3: s = "Text 3: Sequence/Track Name"; break; case 4: s = "Text 4: Instrument Name"; break; case 5: s = "Text 5: Lyric"; break; case 6: s = "Text 6: Marker"; break; case 7: s = "Text 7: Cue Point"; break; case 8: s = "Text 8"; break; case 9: s = "Text 9: Device Name"; break; case 0x0a: s = "Text A"; break; case 0x0b: s = "Text B"; break; case 0x0c: s = "Text C"; break; case 0x0d: s = "Text D"; break; case 0x0e: s = "Text E"; break; case 0x0f: s = "Text F"; break; case 0x20: s = "Channel Prefix"; break; case 0x21: s = "Port Change"; break; case 0x2f: s = "End of Track"; break; case 0x51: s = "Set Tempo"; break; case 0x54: s = "SMPTE Offset"; break; case 0x58: s = "Time Signature"; break; case 0x59: s = "Key Signature"; break; case 0x74: s = "Sequencer-Specific1"; break; case 0x7f: s = "Sequencer-Specific2"; break; default: break; } return QString(s); } //--------------------------------------------------------- // QString nameSysex //--------------------------------------------------------- QString nameSysex(unsigned int len, const unsigned char* buf, MidiInstrument* instr) { QString s; if(len == 0) return s; switch(buf[0]) { case 0x00: if(len < 3) return s; if (buf[1] == 0 && buf[2] == 0x41) s = "Microsoft"; break; case 0x01: s = "Sequential Circuits"; break; case 0x02: s = "Big Briar"; break; case 0x03: s = "Octave / Plateau"; break; case 0x04: s = "Moog"; break; case 0x05: s = "Passport Designs"; break; case 0x06: s = "Lexicon"; break; case 0x07: s = "Kurzweil"; break; case 0x08: s = "Fender"; break; case 0x09: s = "Gulbransen"; break; case 0x0a: s = "Delta Labas"; break; case 0x0b: s = "Sound Comp."; break; case 0x0c: s = "General Electro"; break; case 0x0d: s = "Techmar"; break; case 0x0e: s = "Matthews Research"; break; case 0x10: s = "Oberheim"; break; case 0x11: s = "PAIA"; break; case 0x12: s = "Simmons"; break; case 0x13: s = "DigiDesign"; break; case 0x14: s = "Fairlight"; break; case 0x15: s = "JL Cooper"; break; case 0x16: s = "Lowery"; break; case 0x17: s = "Lin"; break; case 0x18: s = "Emu"; break; case 0x1b: s = "Peavy"; break; case 0x20: s = "Bon Tempi"; break; case 0x21: s = "S.I.E.L"; break; case 0x23: s = "SyntheAxe"; break; case 0x24: s = "Hohner"; break; case 0x25: s = "Crumar"; break; case 0x26: s = "Solton"; break; case 0x27: s = "Jellinghaus Ms"; break; case 0x28: s = "CTS"; break; case 0x29: s = "PPG"; break; case 0x2f: s = "Elka"; break; case 0x36: s = "Cheetah"; break; case 0x3e: s = "Waldorf"; break; case 0x40: s = "Kawai"; break; case 0x41: s = "Roland"; break; case 0x42: s = "Korg"; break; case 0x43: s = "Yamaha"; break; case 0x44: s = "Casio"; break; case 0x45: s = "Akai"; break; case MUSE_SYNTH_SYSEX_MFG_ID: s = "MusE Soft Synth"; break; case 0x7d: s = "Educational Use"; break; case 0x7e: s = "Universal: Non Real Time"; break; case 0x7f: s = "Universal: Real Time"; break; default: s = "??"; break; } if(instr) { // Check for user-defined sysex in instrument... foreach(const MusECore::SysEx* sx, instr->sysex()) { if((int)len == sx->dataLen && memcmp(buf, sx->data, len) == 0) return s + QString(": ") + sx->name; } } // // following messages should not show up in event list // they are filtered while importing midi files // if ((len == gmOnMsgLen) && memcmp(buf, gmOnMsg, gmOnMsgLen) == 0) s += ": GM-ON"; else if ((len == gm2OnMsgLen) && memcmp(buf, gm2OnMsg, gm2OnMsgLen) == 0) s += ": GM2-ON"; else if ((len == gmOffMsgLen) && memcmp(buf, gmOffMsg, gmOffMsgLen) == 0) s += ": GM-OFF"; else if ((len == gsOnMsgLen) && memcmp(buf, gsOnMsg, gsOnMsgLen) == 0) s += ": GS-ON"; else if ((len == xgOnMsgLen) && memcmp(buf, xgOnMsg, xgOnMsgLen) == 0) s += ": XG-ON"; return s; } //--------------------------------------------------------- // QString sysexComment //--------------------------------------------------------- QString sysexComment(unsigned int len, const unsigned char* buf, MidiInstrument* instr) { QString s; if(len == 0) return s; if(instr) { // Check for user-defined sysex in instrument... foreach(const MusECore::SysEx* sx, instr->sysex()) { if((int)len == sx->dataLen && memcmp(buf, sx->data, len) == 0) return sx->comment; } } // These are the common ones we know about so far... if ((len == gmOnMsgLen) && memcmp(buf, gmOnMsg, gmOnMsgLen) == 0) s = QObject::tr("Switch on General Midi Level 1 mode"); else if ((len == gm2OnMsgLen) && memcmp(buf, gm2OnMsg, gm2OnMsgLen) == 0) s = QObject::tr("Switch on General Midi Level 2 mode"); else if ((len == gmOffMsgLen) && memcmp(buf, gmOffMsg, gmOffMsgLen) == 0) s = QObject::tr("Switch off General Midi Level 1 or 2"); else if ((len == gsOnMsgLen) && memcmp(buf, gsOnMsg, gsOnMsgLen) == 0) s = QObject::tr("Switch on Roland GS mode"); else if ((len == xgOnMsgLen) && memcmp(buf, xgOnMsg, xgOnMsgLen) == 0) s = QObject::tr("Switch on Yamaha XG mode"); return s; } //--------------------------------------------------------- // buildMidiEventList // TODO: // parse data increment/decrement controller // NRPN/RPN fine/course data 7/14 Bit // must we set datah/datal to zero after change // of NRPN/RPN register? // generally: how to handle incomplete messages //--------------------------------------------------------- void buildMidiEventList(EventList* del, const MPEventList& el, MidiTrack* track, int div, bool addSysexMeta, bool doLoops) { int hbank = 0xff; int lbank = 0xff; int rpnh = -1; int rpnl = -1; int datah = 0; int datal = 0; int dataType = 0; // 0 : disabled, 0x20000 : rpn, 0x30000 : nrpn EventList mel; MidiInstrument::NoteOffMode nom = MidiInstrument::NoteOffAll; MidiPort* mp = 0; MidiInstrument* minstr = 0; const int port = track->outPort(); if(port >= 0 && port < MusECore::MIDI_PORTS) { mp = &MusEGlobal::midiPorts[port]; minstr = mp->instrument(); if(minstr) nom = minstr->noteOffMode(); } for (iMPEvent i = el.begin(); i != el.end(); ++i) { MidiPlayEvent ev = *i; if (!addSysexMeta && (ev.type() == ME_SYSEX || ev.type() == ME_META)) continue; if (!(ev.type() == ME_SYSEX || ev.type() == ME_META || ((ev.channel() == track->outChannel()) && (ev.port() == track->outPort())))) continue; unsigned tick = ev.time(); DEBUG_MIDI(stderr, "buildMidiEventList tick:%d dataA:%d dataB:%d\n", ev.time(), ev.dataA(), ev.dataB()); if(doLoops) { if(tick >= MusEGlobal::song->lPos().tick() && tick < MusEGlobal::song->rPos().tick()) { int loopn = ev.loopNum(); int loopc = MusEGlobal::audio->loopCount(); int cmode = MusEGlobal::song->cycleMode(); // CYCLE_NORMAL, CYCLE_MIX, CYCLE_REPLACE // If we want REPLACE and the event was recorded in a previous loop, // just ignore it. This will effectively ignore ALL previous loop events inside // the left and right markers, regardless of where recording was started or stopped. // We want to keep any loop 0 note-offs from notes which crossed over the left marker. // To avoid more searching here, just keep ALL note-offs from loop 0, and let code below // sort out and keep which ones had note-ons. if(!(ev.isNoteOff() && loopn == 0)) { if(cmode == Song::CYCLE_REPLACE && loopn < loopc) continue; // If we want NORMAL, same as REPLACE except keep all events from the previous loop // from rec stop position to right marker (and beyond). if(cmode == Song::CYCLE_NORMAL) { // Not sure of accuracy here. Adjust? Adjusted when used elsewhere? unsigned endRec = MusEGlobal::audio->getEndRecordPos().tick(); if((tick < endRec && loopn < loopc) || (tick >= endRec && loopn < (loopc - 1))) continue; } } } } Event e; switch(ev.type()) { case ME_NOTEON: e.setType(Note); if (track->isDrumTrack()) { int instr = track->map_drum_in(ev.dataA()); e.setPitch(instr); } else e.setPitch(ev.dataA()); e.setVelo(ev.dataB()); e.setLenTick(0); break; case ME_NOTEOFF: e.setType(Note); if (track->isDrumTrack()) { int instr = track->map_drum_in(ev.dataA()); e.setPitch(instr); } else e.setPitch(ev.dataA()); e.setVelo(0); e.setVeloOff(ev.dataB()); e.setLenTick(0); break; case ME_POLYAFTER: e.setType(Controller); e.setA((CTRL_POLYAFTER & ~0xff) | (ev.dataA() & 0x7f)); e.setB(ev.dataB()); break; case ME_CONTROLLER: { int val = ev.dataB(); switch(ev.dataA()) { case CTRL_HBANK: hbank = val; break; case CTRL_LBANK: lbank = val; break; case CTRL_HDATA: datah = val; // check if a CTRL_LDATA follows // e.g. wie have a 14 bit controller: { iMPEvent ii = i; ++ii; bool found = false; for (; ii != el.end(); ++ii) { MidiPlayEvent ev = *ii; if (ev.type() == ME_CONTROLLER) { if (ev.dataA() == CTRL_LDATA) { // handle later found = true; } break; } } if (!found) { if (rpnh == -1 || rpnl == -1) { fprintf(stderr, "parameter number not defined, data 0x%x\n", datah); } else { int ctrl = dataType | (rpnh << 8) | rpnl; e.setType(Controller); e.setA(ctrl); e.setB(datah); } } } break; case CTRL_LDATA: datal = val; if (rpnh == -1 || rpnl == -1) { fprintf(stderr, "parameter number not defined, data 0x%x 0x%x, tick %d, channel %d\n", datah, datal, tick, track->outChannel()); break; } // assume that the sequence is always // CTRL_HDATA - CTRL_LDATA // eg. that LDATA is always send last e.setType(Controller); // 14 Bit RPN/NRPN e.setA((dataType+0x30000) | (rpnh << 8) | rpnl); e.setB((datah << 7) | datal); break; case CTRL_HNRPN: rpnh = val; dataType = 0x30000; break; case CTRL_LNRPN: rpnl = val; dataType = 0x30000; break; case CTRL_HRPN: rpnh = val; dataType = 0x20000; break; case CTRL_LRPN: rpnl = val; dataType = 0x20000; break; default: e.setType(Controller); int ctl = ev.dataA(); e.setA(ctl); if(track->isDrumTrack()) { // Is it a drum controller event, according to the track port's instrument? MidiController *mc = MusEGlobal::midiPorts[track->outPort()].drumController(ctl); if(mc) // Store an index into the drum map. e.setA((ctl & ~0xff) | track->map_drum_in(ctl & 0x7f)); } e.setB(val); break; } } break; case ME_PROGRAM: e.setType(Controller); e.setA(CTRL_PROGRAM); e.setB((hbank << 16) | (lbank << 8) | ev.dataA()); break; case ME_AFTERTOUCH: e.setType(Controller); e.setA(CTRL_AFTERTOUCH); e.setB(ev.dataA()); break; case ME_PITCHBEND: e.setType(Controller); e.setA(CTRL_PITCH); e.setB(ev.dataA()); break; case ME_SYSEX: e.setType(Sysex); e.setData(ev.constData(), ev.len()); break; case ME_META: { const unsigned char* data = ev.constData(); switch (ev.dataA()) { case ME_META_TEXT_1_COMMENT: if (track->comment().isEmpty()) track->setComment(QString((const char*)data)); else track->setComment(track->comment() + "\n" + QString((const char*)data)); break; case ME_META_TEXT_3_TRACK_NAME: // Sequence-/TrackName track->setName(QString((char*)data)); break; case ME_META_TEXT_6_MARKER: { unsigned ltick = CALC_TICK(tick); MusEGlobal::song->addMarker(QString((const char*)(data)), ltick, false); } break; // Copyright is supposed to occur only at the beginning of the first track, but we don't // specifically catch it yet during import, so let's just allow it 'wherever' for now. case ME_META_TEXT_2_COPYRIGHT: // Lyrics are allowed anywhere. case ME_META_TEXT_5_LYRIC: // Cue points are supposed to occur only in the first track, but we don't support them // yet (need a list just like markers), so just allow them 'wherever' for now. case ME_META_TEXT_7_CUE_POINT: // Program name is allowed anywhere. case ME_META_TEXT_8_PROGRAM_NAME: // No documentation could be found for these, so just allow them 'wherever' for now. case ME_META_TEXT_A: case ME_META_TEXT_B: case ME_META_TEXT_C: case ME_META_TEXT_D: case ME_META_TEXT_E: { e.setType(Meta); e.setA(ev.dataA()); e.setData(ev.constData(), ev.len()); } break; // Instrument and device name metas are already handled by the midi importing code. case ME_META_TEXT_4_INSTRUMENT_NAME: case ME_META_TEXT_9_DEVICE_NAME: break; case ME_META_TEXT_F_TRACK_COMMENT: track->setComment(QString((char*)data)); break; case ME_META_SET_TEMPO: { unsigned tempo = data[2] + (data[1] << 8) + (data[0] <<16); unsigned ltick = CALC_TICK(tick); // FIXME: After ca 10 mins 32 bits will not be enough... This expression has to be changed/factorized or so in some "sane" way... MusEGlobal::tempomap.addTempo(ltick, tempo); } break; case ME_META_TIME_SIGNATURE: { int timesig_z = data[0]; int n = data[1]; int timesig_n = 1; for (int i = 0; i < n; i++) timesig_n *= 2; int ltick = CALC_TICK(tick); MusEGlobal::sigmap.add(ltick, MusECore::TimeSignature(timesig_z, timesig_n)); } break; case ME_META_KEY_SIGNATURE: { char kc = data[0]; bool minor = data[1]; key_enum key = KEY_SHARP_BEGIN; switch(kc) { case -5: key = KEY_DES; break; case -4: key = KEY_AS; break; case -3: key = KEY_ES; break; case -2: key = KEY_BES; break; case -1: key = KEY_F; break; case 0: key = KEY_C; break; case 1: key = KEY_G; break; case 2: key = KEY_D; break; case 3: key = KEY_A; break; case 4: key = KEY_E; break; case 5: key = KEY_B; break; default: break; } if(key != KEY_SHARP_BEGIN) { unsigned ltick = CALC_TICK(tick); MusEGlobal::keymap.addKey(ltick, key, minor); } } break; default: fprintf(stderr, "buildMidiEventList: unknown Meta 0x%x %d unabsorbed, adding instead to track:%s\n", ev.dataA(), ev.dataA(), track->name().toLatin1().constData()); e.setType(Meta); e.setA(ev.dataA()); e.setData(ev.constData(), ev.len()); break; } } break; } // switch(ev.type() if (!e.empty()) { e.setTick(tick); //------------------------------------------- // Check for and prevent duplicate events //------------------------------------------- const int midi_evtype = ev.type(); const bool midi_noteoff = (midi_evtype == ME_NOTEOFF) || (midi_evtype == ME_NOTEON && ev.dataB() == 0); const bool midi_noteon = midi_evtype == ME_NOTEON && ev.dataB() != 0; const bool midi_controller = midi_evtype == ME_CONTROLLER; bool noteon_found = false; bool noteoff_found = false; bool ctrlval_found = false; //bool other_ctrlval_found = false; if(i != el.begin()) { iMPEvent k = i; while(k != el.begin()) { --k; MidiPlayEvent k_ev = *k; if(k_ev.channel() != ev.channel() || k_ev.port() != ev.port()) continue; const int check_midi_evtype = k_ev.type(); const bool check_midi_noteoff = (check_midi_evtype == ME_NOTEOFF) || (check_midi_evtype == ME_NOTEON && k_ev.dataB() == 0); const bool check_midi_noteon = check_midi_evtype == ME_NOTEON && k_ev.dataB() != 0; const bool check_midi_controller = check_midi_evtype == ME_CONTROLLER; if(midi_noteon || midi_noteoff) { if(ev.dataA() == k_ev.dataA()) // Note { if(check_midi_noteon) { // Check the instrument's note-off mode: If it does not support note-offs, // don't bother doing duplicate note-on checks. // This allows drum input triggers (no note offs at all), although it is awkward to // first have to choose an output instrument with no note-off mode. if(!midi_noteon || (nom != MidiInstrument::NoteOffNone)) noteon_found = true; break; } if(check_midi_noteoff) { noteoff_found = true; break; } } } else if(midi_controller) { if(ev.dataA() == k_ev.dataA()) // Controller number { if(check_midi_controller) { // All we can really do is prevent multiple events at the same time. // We must allow multiple events at different times having the same value, // since the sender may have wanted it that way (a 'flat' graph). if(ev.time() == k_ev.time()) // Event time ctrlval_found = true; // Optimization: Do not allow multiple events at different times having the same value. // Nice, but can't really discard these, sender may have wanted it that way (a 'flat' graph). #if 0 if(ev.dataB() == k_ev.dataB()) // Controller value ctrlval_found = true; else other_ctrlval_found = true; #endif break; } } } else { // TODO: Other types! } } } // Accept the event only if no duplicate was found. // TODO: Other types! if((midi_noteon && !noteon_found) || (midi_noteoff && !noteoff_found) || //(midi_controller && (other_ctrlval_found || !ctrlval_found))) (midi_controller && !ctrlval_found) || // Accept any other type of event. (!midi_noteon && !midi_noteoff && !midi_controller) ) mel.add(e); } } // i != el.end() //--------------------------------------------------- // read NoteOn events and remove corresponding NoteOffs //--------------------------------------------------- for (iEvent i = mel.begin(); i != mel.end(); ++i) { Event ev = i->second; if (ev.isNote()) { if (!ev.isNoteOff()) { // If the event length is not zero, it means the event and its // note on/off have already been taken care of. So ignore it. if(ev.lenTick() != 0) continue; iEvent k; for (k = mel.lower_bound(ev.tick()); k != mel.end(); ++k) { Event event = k->second; if (ev.isNoteOff(event)) { int t = k->first - i->first; if (t <= 0) { if (MusEGlobal::debugMsg) { fprintf(stderr, "Note len is (%d-%d)=%d, set to 1\n", k->first, i->first, k->first - i->first); ev.dump(); event.dump(); } t = 1; } ev.setLenTick(t); ev.setVeloOff(event.veloOff()); break; } } if (k == mel.end()) { fprintf(stderr, "-no note-off! %d pitch %d velo %d\n", ev.tick(), ev.pitch(), ev.velo()); // // switch off at end of measure // int endTick = MusEGlobal::song->roundUpBar(ev.tick()+1); ev.setLenTick(endTick-ev.tick()); } else { if (k==i) { //this will never happen, because i->second has to be a NOTE ON, //while k has to be a NOTE OFF. but in case something changes: fprintf(stderr, "ERROR: THIS SHOULD NEVER HAPPEN: k==i in midi.cpp:buildMidiEventList()\n"); } else { mel.erase(k); } i = mel.begin(); continue; } } } } for (iEvent i = mel.begin(); i != mel.end(); ++i) { Event ev = i->second; if (ev.isNoteOff()) { fprintf(stderr, "+extra note-off! %d pitch %d velo %d\n", i->first, ev.pitch(), ev.velo()); continue; } int tick = CALC_TICK(ev.tick()); if (ev.isNote()) { int lenTick = CALC_TICK(ev.lenTick()); ev.setLenTick(lenTick); } ev.setTick(tick); del->add(ev); } } } // namespace MusECore namespace MusECore { //--------------------------------------------------------- // midiPortsChanged //--------------------------------------------------------- void Audio::midiPortsChanged() { write(sigFd, "P", 1); } //--------------------------------------------------------- // sendLocalOff // Can be called by any thread. //--------------------------------------------------------- void Audio::sendLocalOff() { MidiPlayEvent ev; ev.setTime(0); // Immediate processing. TODO Use curFrame? ev.setType(MusECore::ME_CONTROLLER); ev.setA(MusECore::CTRL_LOCAL_OFF); ev.setB(0); for (int k = 0; k < MusECore::MIDI_PORTS; ++k) { for (int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { ev.setPort(k); ev.setChannel(i); // This is a 'trigger' event. Send to the device, but do not send to the // midi port controllers because it leaves them in this state. if(MusEGlobal::midiPorts[k].device()) MusEGlobal::midiPorts[k].device()->putEvent(ev, MidiDevice::NotLate); } } } //--------------------------------------------------------- // panic // Can be called by any thread. //--------------------------------------------------------- void Audio::panic() { MidiPlayEvent ev; ev.setTime(0); // Immediate processing. TODO Use curFrame? ev.setType(MusECore::ME_CONTROLLER); ev.setB(0); // TODO Reset those controllers back to unknown! for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { MusECore::MidiPort* port = &MusEGlobal::midiPorts[i]; for (int chan = 0; chan < MusECore::MUSE_MIDI_CHANNELS; ++chan) { if (MusEGlobal::debugMsg) fprintf(stderr, "send all sound of to midi port %d channel %d\n", i, chan); ev.setPort(i); ev.setChannel(chan); ev.setA(MusECore::CTRL_ALL_SOUNDS_OFF); // This is a 'trigger' event. Send to the device, but do not send to the // midi port controllers because it leaves them in this state. //port->putHwCtrlEvent(ev); if(port->device()) port->device()->putEvent(ev, MidiDevice::NotLate); ev.setA(MusECore::CTRL_RESET_ALL_CTRL); // This is a 'trigger' event. Send to the device, but do not send to the // midi port controllers because it leaves them in this state. //port->putHwCtrlEvent(ev); if(port->device()) port->device()->putEvent(ev, MidiDevice::NotLate); } } } //--------------------------------------------------------- // initDevices // - called when instrument init sequences plus controller // defaults should be checked and/or sent // - called from arranger pulldown menu //--------------------------------------------------------- void Audio::initDevices(bool force) { for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { MusEGlobal::midiPorts[i].sendPendingInitializations(force); } } //--------------------------------------------------------- // seekMidi // Called from audio thread only. //--------------------------------------------------------- void Audio::seekMidi() { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; unsigned int pos = MusEGlobal::audio->tickPos(); const bool playing = isPlaying(); // Bit-wise channels that are used. int used_ports[MusECore::MIDI_PORTS]; // Initialize the array. for(int i = 0; i < MusECore::MIDI_PORTS; ++i) used_ports[i] = 0; // Find all used channels on all used ports. // bool drum_found = false; if(MusEGlobal::song->click() && metro_settings->clickPort < MusECore::MIDI_PORTS && metro_settings->clickChan < MusECore::MUSE_MIDI_CHANNELS) used_ports[metro_settings->clickPort] |= (1 << metro_settings->clickChan); MidiTrackList* tl = MusEGlobal::song->midis(); for(ciMidiTrack imt = tl->begin(); imt != tl->end(); ++imt) { MidiTrack* mt = *imt; //------------------------------------------------------------ // While we are at it, flush out any track-related playback stuck notes // (NOT 'live' notes) which were not put directly to the device //------------------------------------------------------------ MPEventList& mel = mt->stuckNotes; for(iMPEvent i = mel.begin(), i_next = i; i != mel.end(); i = i_next) { ++i_next; MidiPlayEvent ev(*i); const int ev_port = ev.port(); if(ev_port >= 0 && ev_port < MusECore::MIDI_PORTS) { MidiPort* mp = &MusEGlobal::midiPorts[ev_port]; ev.setTime(0); // Immediate processing. TODO Use curFrame? if(mp->device()) mp->device()->putEvent(ev, MidiDevice::NotLate); } mel.erase(i); } #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(mt->isDrumTrack()) { for(int i = 0; i < DRUM_MAPSIZE; ++i) { // Default to track port if -1 and track channel if -1. int mport = mt->drummap()[i].port; if(mport == -1) mport = mt->outPort(); int mchan = mt->drummap()[i].channel; if(mchan == -1) mchan = mt->outChannel(); if(mport >= 0 && mport < MusECore::MIDI_PORTS && mchan >= 0 && mchan < MusECore::MUSE_MIDI_CHANNELS) used_ports[mport] |= (1 << mchan); } } else { const int mport = mt->outPort(); const int mchan = mt->outChannel(); if(mport >= 0 && mport < MusECore::MIDI_PORTS && mchan >= 0 && mchan < MusECore::MUSE_MIDI_CHANNELS) used_ports[mport] |= (1 << mchan); } #else MusECore::RouteList* rl = mt->outRoutes(); for(MusECore::ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case MusECore::Route::MIDI_PORT_ROUTE: { if(mt->isDrumTrack()) { for(int i = 0; i < DRUM_MAPSIZE; ++i) { // Default to track port if -1 and track channel if -1. int mport = mt->drummap()[i].port; if(mport == -1) mport = mt->outPort(); int mchan = mt->drummap()[i].channel; if(mchan == -1) mchan = mt->outChannel(); if(mport >= 0 && mport < MIDI_PORTS && mchan >= 0 && mchan < MusECore::MUSE_MIDI_CHANNELS) used_ports[mport] |= (1 << mchan); } } else { const int mport = ir->midiPort; const int mchan = ir->channel; if(mport >= 0 && mport < MIDI_PORTS && mchan >= 0 && mchan < MusECore::MUSE_MIDI_CHANNELS) used_ports[mport] |= (1 << mchan); } } break; case MusECore::Route::TRACK_ROUTE: case MusECore::Route::JACK_ROUTE: case MusECore::Route::MIDI_DEVICE_ROUTE: break; } } #endif } for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { if(used_ports[i] == 0) continue; MidiPort* mp = &MusEGlobal::midiPorts[i]; MidiDevice* md = mp->device(); //--------------------------------------------------- // Send STOP //--------------------------------------------------- // Don't send if external sync is on. The master, and our sync routing system will take care of that. if(!MusEGlobal::extSyncFlag) { if(mp->syncInfo().MRTOut()) { // Shall we check for device write open flag to see if it's ok to send?... //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) //if(!(openFlags() & 1)) // continue; mp->sendStop(); } } //--------------------------------------------------- // If playing, clear all notes and flush out any // stuck notes which were put directly to the device //--------------------------------------------------- if(md && playing) md->handleSeek(); //--------------------------------------------------- // reset sustain //--------------------------------------------------- if(md) { for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { const MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); md->putEvent(ev, MidiDevice::NotLate); } } } MidiInstrument* instr = mp->instrument(); MidiCtrlValListList* cll = mp->controller(); for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) { MidiCtrlValList* vl = ivl->second; int chan = ivl->first >> 24; if(!(used_ports[i] & (1 << chan))) // Channel not used in song? continue; int ctlnum = vl->num(); // Find the first non-muted value at the given tick... bool values_found = false; bool found_value = false; iMidiCtrlVal imcv = vl->lower_bound(pos); if(imcv != vl->end() && imcv->first == pos) { for( ; imcv != vl->end() && imcv->first == pos; ++imcv) { const Part* p = imcv->second.part; if(!p) continue; // Ignore values that are outside of the part. if(pos < p->tick() || pos >= (p->tick() + p->lenTick())) continue; values_found = true; // Ignore if part or track is muted or off. if(p->mute()) continue; const Track* track = p->track(); if(track && (track->isMute() || track->off())) continue; found_value = true; break; } } else { while(imcv != vl->begin()) { --imcv; const Part* p = imcv->second.part; if(!p) continue; // Ignore values that are outside of the part. unsigned t = imcv->first; if(t < p->tick() || t >= (p->tick() + p->lenTick())) continue; values_found = true; // Ignore if part or track is muted or off. if(p->mute()) continue; const Track* track = p->track(); if(track && (track->isMute() || track->off())) continue; found_value = true; break; } } if(found_value) { int fin_port = i; MidiPort* fin_mp = mp; int fin_chan = chan; int fin_ctlnum = ctlnum; // Is it a drum controller event, according to the track port's instrument? if(mp->drumController(ctlnum)) { if(const Part* p = imcv->second.part) { if(Track* t = p->track()) { if(t->type() == MusECore::Track::DRUM) { MidiTrack* mt = static_cast(t); int v_idx = ctlnum & 0x7f; fin_ctlnum = (ctlnum & ~0xff) | mt->drummap()[v_idx].anote; int map_port = mt->drummap()[v_idx].port; if(map_port != -1) { fin_port = map_port; fin_mp = &MusEGlobal::midiPorts[fin_port]; } int map_chan = mt->drummap()[v_idx].channel; if(map_chan != -1) fin_chan = map_chan; } } } } const MidiPlayEvent ev(0, fin_port, fin_chan, ME_CONTROLLER, fin_ctlnum, imcv->second.val); // This is the audio thread. Just set directly. fin_mp->setHwCtrlState(ev); // Don't bother sending any sustain values to the device, because we already // just sent out zero sustain values, above. Just set the hw state. // When play resumes, the correct values are sent again if necessary in Audio::startRolling(). if(fin_ctlnum != CTRL_SUSTAIN && fin_mp->device()) fin_mp->device()->putEvent(ev, MidiDevice::NotLate); } // Either no value was found, or they were outside parts, or pos is in the unknown area before the first value. // Send instrument default initial values. NOT for syntis. Use midiState and/or initParams for that. //if((imcv == vl->end() || !done) && !MusEGlobal::song->record() && instr && !isSynti()) // Hmm, without refinement we can only do this at position 0, due to possible 'skipped' values outside parts, above. if(instr && md && !md->isSynti() && !values_found && MusEGlobal::config.midiSendCtlDefaults && !MusEGlobal::song->record() && pos == 0) { // NOTE: If this is a PROGRAM controller, this code is not as crazy as it looks. // (How can we ask for, and then set, an initial program when we are first asking // for the current program? Seems like a 'circular' conflict.) // Midnam does not contain a program controller, so the setHwCtrlState() below // has no effect on the current controller list since in midnam there's no // program controller which would make it patch-dependent. There is only our // instrument's global PROGRAM controller (does not care about channel or current patch). const int patch = mp->hwCtrlState(chan, CTRL_PROGRAM); const MidiController* mc = instr->findController(vl->num(), chan, patch); if(mc->initVal() != CTRL_VAL_UNKNOWN) { //fprintf(stderr, "Audio::seekMidi: !values_found: calling sendEvent: ctlnum:%d val:%d\n", ctlnum, mc->initVal() + mc->bias()); // Use sendEvent to get the optimizations and limiting. No force sending. Note the addition of bias. const MidiPlayEvent ev(0, i, chan, ME_CONTROLLER, ctlnum, mc->initVal() + mc->bias()); // This is the audio thread. Just set directly. mp->setHwCtrlState(ev); md->putEvent(ev, MidiDevice::NotLate); } } //--------------------------------------------------- // Send STOP and "set song position pointer" //--------------------------------------------------- // Don't send if external sync is on. The master, and our sync routing system will take care of that. if(!MusEGlobal::extSyncFlag) { if(mp->syncInfo().MRTOut()) { int beat = (pos * 4) / MusEGlobal::config.division; mp->sendSongpos(beat); } } } } } //--------------------------------------------------------- // extClockHistoryTick2Frame // Convert tick to frame using the external clock history list. // The function takes a tick relative to zero (ie. relative to the first event in a processing batch). // The returned clock frames occurred during the previous audio cycle(s), so you may want to shift // the frames forward by one audio segment size for scheduling purposes. // CAUTION: There must be at least one valid clock in the history, // otherwise it returns zero. Don't feed this a tick // greater than or equal to the next tick, it will simply return // the very last frame, which is not very useful since // that will just bunch the events together at the last frame. //--------------------------------------------------------- unsigned int Audio::extClockHistoryTick2Frame(unsigned int tick) const { if(_extClockHistorySize == 0) { fprintf(stderr, "Error: Audio::extClockTickToFrame(): empty list\n"); return 0; } const int div = MusEGlobal::config.division / 24; if(div == 0) return 0; // Prevent divide by zero. int index = tick / div; if(index >= _extClockHistorySize) { fprintf(stderr, "Error: Audio::extClockTickToFrame(): index:%d >= size:%d\n", index, _extClockHistorySize); index = _extClockHistorySize - 1; } // Divide the clock period by the division and interpolate for even better resolution. // FIXME: Darn, too bad we can't use this. It would work, but the previous cycle // has no knowledge of what to put at the end, and the current cycle // would end up lumping together events at the start which should have // been played at end of previous cycle. // const unsigned int subtick = tick % div; // const unsigned int frame = _extClockLastFrame + double(_extClockHistory[index] - _extClockLastFrame) * (double(subtick) / double(div)); const unsigned int frame = _extClockHistory[index].frame(); return frame; } //--------------------------------------------------------- // extClockHistoryTick2Frame // Convert frame to tick using the external clock history list. // The function takes an absolute linearly increasing frame and returns a tick relative to zero // (ie. relative to the first event in a processing batch). // CAUTION: There must be at least one valid clock in the history, // otherwise it returns zero. Don't feed this a frame // greater than or equal to the next frame, it will simply return // the very last tick, which is not very useful since // that will just bunch the events together at the last tick. //--------------------------------------------------------- unsigned int Audio::extClockHistoryFrame2Tick(unsigned int frame) const { if(_extClockHistorySize == 0) { fprintf(stderr, "Error: Audio::extClockHistoryFrame2Tick(): empty list\n"); return curTickPos; } const unsigned int div = MusEGlobal::config.division / 24; bool found = false; unsigned int val = 0; for(int i = _extClockHistorySize - 1; i >= 0; --i) { DEBUG_MIDI(stderr, "Audio::extClockHistoryFrame2Tick(): frame:%u i:%d _extClockHistory[i]._frame:%u\n", frame, i, _extClockHistory[i].frame()); if(_extClockHistory[i].frame() <= frame) { if(!found) { found = true; int clocks = 0; unsigned int offset = curTickPos; for(int k = i; k >= 0; --k) { if(_extClockHistory[k].isFirstClock()) { if(_extClockHistory[k].externState() == ExtMidiClock::ExternStarted) offset = 0; } if(!_extClockHistory[k].isPlaying()) break; if(k < i) // Ignore first clock. ++clocks; } val = offset + clocks * div; } } } if(found) return val; fprintf(stderr, "Error: Audio::extClockHistoryFrame2Tick(): frame:%u out of range. Returning zero. _extClockHistorySize:%u\n", frame, _extClockHistorySize); // We don't know the state of the last clock, we can only assume it was playing. if(curTickPos >= div) return curTickPos - div; return curTickPos; } //--------------------------------------------------------- // collectEvents // collect events for next audio segment //--------------------------------------------------------- void Audio::collectEvents(MusECore::MidiTrack* track, unsigned int cts, unsigned int nts, unsigned int frames, unsigned int latency_offset) { DEBUG_MIDI_TIMING(stderr, "Audio::collectEvents: cts:%u nts:%u\n", cts, nts); const bool extsync = MusEGlobal::extSyncFlag; const int delay = track->delay; // If external sync is not on, we can take advantage of frame accuracy but // first we must allow the next tick position to be included in the search // even if it is equal to the current tick position. if((extsync && cts >= nts) || (!extsync && cts > nts)) return; int port = track->outPort(); int channel = track->outChannel(); int defaultPort = port; MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiDevice* md = mp->device(); const unsigned int pos_fr = _pos.frame() + latency_offset; const unsigned int next_pos_fr = pos_fr + frames; // at least one punch is set at this point const bool replaceMode = recording && track->recordFlag() && MusEGlobal::song->recMode() == MusEGlobal::song->REC_REPLACE; const bool punchboth = MusEGlobal::song->punchin() && MusEGlobal::song->punchout(); const bool punchin = MusEGlobal::song->punchin() && !MusEGlobal::song->punchout(); const bool punchout = MusEGlobal::song->punchout() && !MusEGlobal::song->punchin(); const unsigned int rangeStart = MusEGlobal::song->lPos().tick(); const unsigned int rangeEnd = MusEGlobal::song->rPos().tick(); DEBUG_MIDI_TIMING(stderr, "Audio::collectEvents: pos_fr:%u next_pos_fr:%u\n", pos_fr, next_pos_fr); PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart*)(p->second); // don't play muted parts if (part->mute()) continue; const EventList& events = part->events(); unsigned partTick = part->tick(); unsigned partLen = part->lenTick(); unsigned offset = delay + partTick; if (offset > nts) continue; unsigned stick = (offset > cts) ? 0 : cts - offset; unsigned etick = nts - offset; // Do not play events which are past the end of this part. if(etick > partLen) continue; // The start and end tick are a rough range to make the loop faster instead of having // to iterate the whole list each time, comparing frames. // Use upper_bound because we need to include the 'next' last item because it may have a // fractional tick component that we would otherwise miss with lower_bound. The loop will // decide whether to process iterated items or not by precisely comparing frames. // We rely on the tempo value being 'stable' during the process period - that is // no user changes in-between cycles. We don't have that capability currently anyway - // to break the process up into chunks (like our controllers) depending on tempo frames, // our tempo map is not frame-accurate, only tick-accurate. ciEvent ie = events.lower_bound(stick); ciEvent iend = events.upper_bound(etick); DEBUG_MIDI_TIMING(stderr, "Audio::collectEvents: part events stick:%u etick:%u\n", stick, etick); for (; ie != iend; ++ie) { Event ev = ie->second; port = defaultPort; //Reset each loop // // don't play any meta events // if (ev.type() == Meta) continue; if (track->isDrumTrack()) { int instr = ev.pitch(); // ignore muted drums if (ev.isNote() && track->drummap()[instr].mute) continue; } if (replaceMode) { unsigned eventStart = ev.tick() + partTick; if (punchboth && (eventStart >= rangeStart && eventStart < rangeEnd)) continue; else if (punchin && eventStart >= rangeStart) continue; else if (punchout && eventStart < rangeEnd) continue; } unsigned tick = ev.tick() + offset; DEBUG_MIDI_TIMING(stderr, "Audio::collectEvents: event tick:%u\n", tick); //----------------------------------------------------------------- // Determining the playback scheduling frame from the event's tick: //----------------------------------------------------------------- unsigned frame; if(extsync) // If external sync is on, look up the scheduling frame from the tick, // in the external clock history list (which is cleared, re-composed, and processed each cycle). // The function takes a tick relative to zero (ie. relative to the first event in this batch). // The returned clock frame occurred during the previous audio cycle(s), so shift the frame // forward by one audio segment size. frame = extClockHistoryTick2Frame(tick - stick) + MusEGlobal::segmentSize; else { // If external sync is off, look up the scheduling frame from our tempo list // ie. normal playback. const unsigned int fr = MusEGlobal::tempomap.tick2frame(tick); DEBUG_MIDI_TIMING(stderr, "Audio::collectEvents: event: frame:%u\n", fr); // Take advantage of frame-accurate comparison ability here. // At some point, the event's frame time and the 'swept' current range of pos frame will intersect, // so all events should be accounted for. if(fr < pos_fr || fr >= next_pos_fr) { DEBUG_MIDI_TIMING(stderr, "Audio::collectEvents: Ignoring event\n"); continue; } frame = fr - pos_fr; frame += syncFrame; } DEBUG_MIDI(stderr, "Audio::collectEvents: event: tick:%u final frame:%u\n", tick, frame); switch (ev.type()) { case Note: { int len = ev.lenTick(); int pitch = ev.pitch(); int velo = ev.velo(); int veloOff = ev.veloOff(); if (track->isDrumTrack()) { // Map drum-notes to the drum-map values int instr = ev.pitch(); pitch = track->drummap()[instr].anote; // Default to track port if -1 and track channel if -1. port = track->drummap()[instr].port; //This changes to non-default port if(port == -1) port = track->outPort(); channel = track->drummap()[instr].channel; if(channel == -1) channel = track->outChannel(); velo = int(double(velo) * (double(track->drummap()[instr].vol) / 100.0)) ; veloOff = int(double(veloOff) * (double(track->drummap()[instr].vol) / 100.0)) ; } else if (track->type() == Track::MIDI) { // transpose non drum notes pitch += (track->transposition + MusEGlobal::song->globalPitchShift()); } if (pitch > 127) pitch = 127; if (pitch < 0) pitch = 0; // Apply track velocity and compression to both note-on and note-off velocity... velo += track->velocity; velo = (velo * track->compression) / 100; if (velo > 127) velo = 127; if (velo < 1) // no off event // Zero means zero. Should mean no note at all? //velo = 1; continue; veloOff += track->velocity; veloOff = (veloOff * track->compression) / 100; if (veloOff > 127) veloOff = 127; if (veloOff < 1) veloOff = 0; len = (len * track->len) / 100; if (len <= 0) // don't allow zero length len = 1; if (port == defaultPort) { if (md) { md->putEvent( MusECore::MidiPlayEvent(frame, port, channel, MusECore::ME_NOTEON, pitch, velo), MidiDevice::NotLate, MidiDevice::PlaybackBuffer); track->addStuckNote(MusECore::MidiPlayEvent(tick + len, port, channel, MusECore::ME_NOTEOFF, pitch, veloOff)); } } else { //Handle events to different port than standard. MidiDevice* mdAlt = MusEGlobal::midiPorts[port].device(); if (mdAlt) { mdAlt->putEvent( MusECore::MidiPlayEvent(frame, port, channel, MusECore::ME_NOTEON, pitch, velo), MidiDevice::NotLate, MidiDevice::PlaybackBuffer); track->addStuckNote(MusECore::MidiPlayEvent(tick + len, port, channel, MusECore::ME_NOTEOFF, pitch, veloOff)); } } if(velo > track->activity()) track->setActivity(velo); } break; case Controller: { if (track->isDrumTrack()) { int ctl = ev.dataA(); // Is it a drum controller event, according to the track port's instrument? MusECore::MidiController *mc = MusEGlobal::midiPorts[defaultPort].drumController(ctl); if(mc) { int instr = ctl & 0x7f; ctl &= ~0xff; int pitch = track->drummap()[instr].anote & 0x7f; // Default to track port if -1 and track channel if -1. port = track->drummap()[instr].port; //This changes to non-default port if(port == -1) port = track->outPort(); channel = track->drummap()[instr].channel; if(channel == -1) channel = track->outChannel(); MusECore::MidiPlayEvent mpeAlt(frame, port, channel, MusECore::ME_CONTROLLER, ctl | pitch, ev.dataB()); MidiPort* mpAlt = &MusEGlobal::midiPorts[port]; // TODO Maybe grab the flag from the 'Optimize Controllers' Global Setting, // which so far was meant for (N)RPN stuff. For now, just force it. // This is the audio thread. Just set directly. mpAlt->setHwCtrlState(mpeAlt); if(MidiDevice* mdAlt = mpAlt->device()) mdAlt->putEvent(mpeAlt, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); break; // Break out. } } MusECore::MidiPlayEvent mpe = ev.asMidiPlayEvent(frame, port, channel); // TODO Maybe grab the flag from the 'Optimize Controllers' Global Setting, // which so far was meant for (N)RPN stuff. For now, just force it. // This is the audio thread. Just set directly. mp->setHwCtrlState(mpe); if(md) md->putEvent(mpe, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); } break; default: if(md) { md->putEvent(ev.asMidiPlayEvent(frame, port, channel), MidiDevice::NotLate, MidiDevice::PlaybackBuffer); } break; } } } } //--------------------------------------------------------- // processMidi // - collects midi events for current audio segment and // sends them to midi thread // - current audio segment position is (curTickPos, nextTickPos) // - called from midiseq thread, // executed in audio thread //--------------------------------------------------------- void Audio::processMidi(unsigned int frames) { const bool extsync = MusEGlobal::extSyncFlag; const bool playing = isPlaying(); for (iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { MidiDevice* md = *id; int port = md->midiPort(); // Port should be same as event.port() from this device. Same idea event.channel(). // Process events sent by synthesizers (which in turn may have been passed by their GUI -> synth FIFOs). // Receive events sent from a synth's gui thread (which might be different than our gui thread) to the audio thread. if(md->isSynti()) { SynthI* s = (SynthI*)md; while (s->eventsPending()) { MidiRecordEvent ev = s->receiveEvent(); // FIXME: This is for recording the events sent by GUI. // It never gets a chance to be processed since reading of // record FIFOs is done only by connected input ROUTES, below. // To be useful, the synth itself must be allowed to be chosen // as an input route, which is simple enough, but we currently don't // list synths as inputs for fear of too many INCOMPATIBLE messages // from DIFFERING synths. However, we could allow ONLY THIS synth // to be listed and therefore be automatically connected too, if desired. //md->recordEvent(ev); // // For now, instead of recording, here is the minimum that we must do: // Intercept any special MusE system sysex messages. (This IS the system right here.) bool intercepted = false; const int type = ev.type(); switch(type) { case ME_SYSEX: { const unsigned char* p = ev.constData(); int n = ev.len(); if(n >= 3) { if(p[0] == MUSE_SYNTH_SYSEX_MFG_ID) { if(p[1] == MUSE_SYSEX_SYSTEM_ID && p[2] == MUSE_SYSEX_SYSTEM_UPDATE_DRUM_MAPS_ID) { intercepted = true; if(port >= 0 && port < MusECore::MIDI_PORTS) MusEGlobal::midiPorts[port].updateDrumMaps(); } } } } break; default: break; } // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. // Same code as in MidiPort::sendEvent() if(!intercepted && port != -1) // This is the audio thread. Just set directly. MusEGlobal::midiPorts[port].setHwCtrlState(MidiPlayEvent(ev)); } } // Take snapshots of the current sizes of the recording fifos, // because they may change while here in process, asynchronously. md->beforeProcess(); // // --------- Handle midi events for audio tracks ----------- // if(port < 0) continue; for(int chan = 0; chan < MusECore::MUSE_MIDI_CHANNELS; ++chan) { MusECore::MidiRecFifo& rf = md->recordEvents(chan); int count = md->tmpRecordCount(chan); for(int i = 0; i < count; ++i) { const MusECore::MidiRecordEvent& event(rf.peek(i)); int etype = event.type(); if(etype == MusECore::ME_CONTROLLER || etype == MusECore::ME_PITCHBEND || etype == MusECore::ME_PROGRAM) { int ctl, val; if(etype == MusECore::ME_CONTROLLER) { ctl = event.dataA(); val = event.dataB(); } else if(etype == MusECore::ME_PITCHBEND) { ctl = MusECore::CTRL_PITCH; val = event.dataA(); } else //if(etype == MusECore::ME_PROGRAM) { ctl = MusECore::CTRL_PROGRAM; val = event.dataA(); } // Midi learn! MusEGlobal::midiLearnPort = port; MusEGlobal::midiLearnChan = chan; MusEGlobal::midiLearnCtrl = ctl; // Send to audio tracks... for (MusECore::iTrack t = MusEGlobal::song->tracks()->begin(); t != MusEGlobal::song->tracks()->end(); ++t) { if((*t)->isMidiTrack()) continue; MusECore::AudioTrack* track = static_cast(*t); MidiAudioCtrlMap* macm = track->controller()->midiControls(); int h = macm->index_hash(port, chan, ctl); std::pair range = macm->equal_range(h); for(ciMidiAudioCtrlMap imacm = range.first; imacm != range.second; ++imacm) { const MidiAudioCtrlStruct* macs = &imacm->second; int actrl = macs->audioCtrlId(); iCtrlList icl = track->controller()->find(actrl); if(icl == track->controller()->end()) continue; CtrlList* cl = icl->second; double dval = midi2AudioCtrlValue(cl, macs, ctl, val); // Time here needs to be frames always. unsigned int ev_t = event.time(); unsigned int t = ev_t; unsigned int pframe = _pos.frame(); if(pframe > t) // Technically that's an error, shouldn't happen t = 0; else // Subtract the current audio position frame t -= pframe; // Add the current running sync frame to make the control processing happy t += syncFrame; track->addScheduledControlEvent(actrl, dval, t); // Rec automation... // For the record time, if stopped we don't want the circular running position, // just the static one. unsigned int rec_t = playing ? ev_t : pframe; if(!MusEGlobal::automation) continue; AutomationType at = track->automationType(); // Unlike our built-in gui controls, there is not much choice here but to // just do this: if ( (at == AUTO_WRITE) || (at == AUTO_READ && !playing) || (at == AUTO_TOUCH) ) track->enableController(actrl, false); if(playing) { if(at == AUTO_WRITE || at == AUTO_TOUCH) track->recEvents()->push_back(CtrlRecVal(rec_t, actrl, dval)); } else { if(at == AUTO_WRITE) track->recEvents()->push_back(CtrlRecVal(rec_t, actrl, dval)); else if(at == AUTO_TOUCH) // In touch mode and not playing. Send directly to controller list. // Add will replace if found. cl->add(rec_t, dval); } } } } } } } MidiTrackList* mtl = MusEGlobal::song->midis(); for (iMidiTrack t = mtl->begin(); t != mtl->end(); ++t) { MidiTrack* track = *t; const int t_port = track->outPort(); const int t_channel = track->outChannel(); MidiPort* mp = nullptr; if(t_port >= 0 && t_port < MusECore::MIDI_PORTS) mp = &MusEGlobal::midiPorts[t_port]; MidiDevice* md = nullptr; if(mp) md = mp->device(); // only add track events if the track is unmuted and turned on if(!track->isMute() && !track->off()) { // don't render existing events in replace mode (except when punch is active - to be filtered later) if (!(recording && track->recordFlag() && MusEGlobal::song->recMode() == MusEGlobal::song->REC_REPLACE && !MusEGlobal::song->punchin() && !MusEGlobal::song->punchout())) { if(playing) { unsigned int lat_offset = 0; unsigned int cur_tick = curTickPos; unsigned int next_tick = nextTickPos; //-------------------------------------------------------------------- // Account for the midi track's latency correction and/or compensation. //-------------------------------------------------------------------- // TODO How to handle when external sync is on. For now, don't try to correct. if(MusEGlobal::config.enableLatencyCorrection && !extsync) { const TrackLatencyInfo& li = track->getLatencyInfo(false); // This value is negative for correction. const float mlat = li._sourceCorrectionValue; if((int)mlat < 0) { // Convert to a positive offset. const unsigned int l = (unsigned int)(-mlat); if(l > lat_offset) lat_offset = l; } if(lat_offset != 0) { Pos ppp(_pos.frame() + lat_offset, false); cur_tick = ppp.tick(); ppp += frames; next_tick = ppp.tick(); } } collectEvents(track, cur_tick, next_tick, frames, lat_offset); } } } // //----------midi recording // const bool track_rec_flag = track->recordFlag(); // REMOVE Tim. monitor. Changed. // const bool track_rec_monitor = track->recMonitor(); // Separate monitor and record functions. const bool track_rec_monitor = track->isRecMonitored(); // Separate monitor and record functions. if(track_rec_monitor || track_rec_flag) { MPEventList& rl = track->mpevents; RouteList* irl = track->inRoutes(); for(ciRoute r = irl->begin(); r != irl->end(); ++r) { if(!r->isValid() || (r->type != Route::MIDI_PORT_ROUTE)) continue; int devport = r->midiPort; if (devport == -1) continue; MidiDevice* dev = MusEGlobal::midiPorts[devport].device(); if(!dev) continue; #ifdef _USE_MIDI_ROUTE_PER_CHANNEL_ const int r_chan = r->channel; #else const int channelMask = r->channel; if(channelMask == -1 || channelMask == 0) continue; #endif // _USE_MIDI_ROUTE_PER_CHANNEL_ for(int channel = 0; channel < MusECore::MUSE_MIDI_CHANNELS; ++channel) { #ifdef _USE_MIDI_ROUTE_PER_CHANNEL_ if(r_chan != -1 && channel != r_chan) continue; #else // _USE_MIDI_ROUTE_PER_CHANNEL_ if(!(channelMask & (1 << channel))) continue; #endif // _USE_MIDI_ROUTE_PER_CHANNEL_ if(!dev->sysexFIFOProcessed()) { // Set to the sysex fifo at first. MidiRecFifo& rf = dev->recordEvents(MusECore::MUSE_MIDI_CHANNELS); // Get the frozen snapshot of the size. int count = dev->tmpRecordCount(MusECore::MUSE_MIDI_CHANNELS); for(int i = 0; i < count; ++i) { MidiRecordEvent event(rf.peek(i)); event.setPort(t_port); event.setChannel(t_channel); // don't echo controller changes back to software // synthesizer: if(md && track_rec_monitor) { // Do not echo synth events back to the same synth instance under any circumstances, // not even if monitor (echo) is on. if(!dev->isSynti() || dev != md) { // All recorded events arrived in the previous period. Shift into this period for playback. unsigned int et = event.time(); // The events arrived in the previous period. Shift into this period for playback. // The events are already biased with the last frame time. unsigned int t = et + MusEGlobal::segmentSize; // Protection from slight errors in estimated frame time. if(t >= (syncFrame + MusEGlobal::segmentSize)) { DEBUG_MIDI(stderr, "Error: Audio::processMidi(): sysex: t:%u >= syncFrame:%u + segmentSize:%u (==%u)\n", t, syncFrame, MusEGlobal::segmentSize, syncFrame + MusEGlobal::segmentSize); t = syncFrame + (MusEGlobal::segmentSize - 1); } event.setTime(t); md->putEvent(event, MidiDevice::NotLate); event.setTime(et); // Restore for recording. } } unsigned int et = event.time(); // Make sure the event is recorded in units of ticks. if(extsync) { const unsigned int xt = extClockHistoryFrame2Tick(event.time()); DEBUG_MIDI(stderr, "processMidi: event time:%d dataA:%d dataB:%d curTickPos:%u set time:%u\n", event.time(), event.dataA(), event.dataB(), curTickPos, xt); event.setTime(xt); } else { // All recorded events arrived in the previous period. Shift into this period for record. unsigned int t = et + MusEGlobal::segmentSize; // Protection from slight errors in estimated frame time. if(t >= (syncFrame + MusEGlobal::segmentSize)) { DEBUG_MIDI(stderr, "Error: Audio::processMidi(): record sysex: t:%u >= syncFrame:%u + segmentSize:%u (==%u)\n", t, syncFrame, MusEGlobal::segmentSize, syncFrame + MusEGlobal::segmentSize); t = syncFrame + (MusEGlobal::segmentSize - 1); } // Be sure to allow for some (very) late events, such as // the first chunk's time in a multi-chunk sysex. const unsigned int a_fr = pos().frame() + t; const unsigned int fin_fr = syncFrame > a_fr ? 0 : a_fr - syncFrame; event.setTime(MusEGlobal::tempomap.frame2tick(fin_fr)); } // Is the transport recording, or, is it about to be from external sync? if((recording || (MusEGlobal::song->record() && extsync && MusEGlobal::midiSyncContainer.isPlaying())) && track_rec_flag) rl.add(event); event.setTime(et); // Restore. } dev->setSysexFIFOProcessed(true); } MidiRecFifo& rf = dev->recordEvents(channel); int count = dev->tmpRecordCount(channel); for(int i = 0; i < count; ++i) { MidiRecordEvent event(rf.peek(i)); int defaultPort = devport; int drumRecPitch=0; //prevent compiler warning: variable used without initialization MidiController *mc = nullptr; int ctl = 0; int prePitch = 0, preVelo = 0; event.setPort(t_port); event.setChannel(t_channel); if (event.isNote() || event.isNoteOff()) { // // apply track values // //Apply drum inkey: if (track->isDrumTrack()) { int pitch = event.dataA(); int dmindex = track->map_drum_in(pitch); //Map note that is played according to MusEGlobal::drumInmap drumRecPitch = track->drummap()[dmindex].enote; // Default to track port if -1 and track channel if -1. devport = track->drummap()[dmindex].port; if(devport == -1) devport = track->outPort(); event.setPort(devport); int mapchan = track->drummap()[dmindex].channel; if(mapchan != -1) event.setChannel(mapchan); event.setA(track->drummap()[dmindex].anote); if (MusEGlobal::config.newDrumRecordCondition & MusECore::DONT_REC_HIDDEN && track->drummap()[dmindex].hide ) continue; // skip that event, proceed with the next if (MusEGlobal::config.newDrumRecordCondition & MusECore::DONT_REC_MUTED && track->drummap()[dmindex].mute ) continue; // skip that event, proceed with the next } else { //Track transpose if non-drum prePitch = event.dataA(); int pitch = prePitch + track->transposition; if (pitch > 127) pitch = 127; if (pitch < 0) pitch = 0; event.setA(pitch); } // Apply track velocity and compression to note-on AND note-off events. preVelo = event.dataB(); int velo = preVelo + track->velocity; velo = (velo * track->compression) / 100; if (velo > 127) velo = 127; if (velo < 1) // Zero means zero. Should mean no note at all? //velo = 1; velo = 0; // Use zero as a marker to tell the playback (below) not to sound the note. event.setB(velo); } else if(event.type() == MusECore::ME_CONTROLLER) { if (track->isDrumTrack()) //FINDMICHJETZT TEST { ctl = event.dataA(); // Regardless of what port the event came from, is it a drum controller event // according to the track port's instrument? if(mp) mc = mp->drumController(ctl); if(mc) { int pitch = ctl & 0x7f; // pitch is now the incoming pitch ctl &= ~0xff; int dmindex = track->map_drum_in(pitch) & 0x7f; //Map note that is played according to drumInmap drumRecPitch = track->drummap()[dmindex].enote; // Default to track port if -1 and track channel if -1. devport = track->drummap()[dmindex].port; if(devport == -1) devport = track->outPort(); event.setPort(devport); int mapchan = track->drummap()[dmindex].channel; if(mapchan != -1) event.setChannel(mapchan); event.setA(ctl | track->drummap()[dmindex].anote); if (MusEGlobal::config.newDrumRecordCondition & MusECore::DONT_REC_HIDDEN && track->drummap()[dmindex].hide ) continue; // skip that event, proceed with the next if (MusEGlobal::config.newDrumRecordCondition & MusECore::DONT_REC_MUTED && track->drummap()[dmindex].mute ) continue; // skip that event, proceed with the next } } } // MusE uses a fixed clocks per quarternote of 24. // At standard 384 ticks per quarternote for example, // 384/24=16 for a division of 16 sub-frames (16 MusE 'ticks'). // If ext sync, events are now time-stamped with last tick in MidiDevice::recordEvent(). p3.3.35 // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. // don't echo controller changes back to software // synthesizer: // Zero means zero. Should mean no note at all? // If the event is marked as a note with zero velocity (above), do not sound the note. if(!event.isNote() || event.dataB() != 0) { // All recorded events arrived in previous period. Shift into this period for playback. // frameoffset needed to make process happy. unsigned int et = event.time(); // The events arrived in the previous period. Shift into this period for playback. // The events are already biased with the last frame time. unsigned int t = et + MusEGlobal::segmentSize; // Protection from slight errors in estimated frame time. if(t >= (syncFrame + MusEGlobal::segmentSize)) { DEBUG_MIDI(stderr, "Error: Audio::processMidi(): event: t:%u >= syncFrame:%u + segmentSize:%u (==%u)\n", t, syncFrame, MusEGlobal::segmentSize, syncFrame + MusEGlobal::segmentSize); t = syncFrame + (MusEGlobal::segmentSize - 1); } event.setTime(t); // Check if we're outputting to another port than default: if (devport == defaultPort) { event.setPort(t_port); // REMOVE Tim. monitor. Changed. //if(md && track_rec_monitor && !track->off() && !track->isMute()) if(md && track_rec_monitor) { // Do not echo synth events back to the same synth instance under any circumstances, // not even if monitor (echo) is on. if(!dev->isSynti() || dev != md) { MidiInstrument* minstr = MusEGlobal::midiPorts[t_port].instrument(); const MidiInstrument::NoteOffMode nom = minstr->noteOffMode(); // If the instrument has no note-off mode, do not use the stuck notes mechanism, send as is. // This allows drum input triggers (no note offs at all), although it is awkward to // first have to choose an output instrument with no note-off mode. if(nom == MidiInstrument::NoteOffNone) { if(event.isNoteOff()) // Try to remove any corresponding stuck live note. track->removeStuckLiveNote(t_port, event.channel(), event.dataA()); // md->addScheduledEvent(event); md->putEvent(event, MidiDevice::NotLate); } else if(event.isNoteOff()) { // Try to remove any corresponding stuck live note. // Only if a stuck live note existed do we schedule the note off to play. if(track->removeStuckLiveNote(t_port, event.channel(), event.dataA())) // md->addScheduledEvent(event); md->putEvent(event, MidiDevice::NotLate); } else if(event.isNote()) { // Check if a stuck live note exists on any track. ciMidiTrack it_other = mtl->begin(); for( ; it_other != mtl->end(); ++it_other) { if((*it_other)->stuckLiveNoteExists(t_port, event.channel(), event.dataA())) break; } // Only if NO stuck live note existed do we schedule the note on to play. if(it_other == mtl->end()) { if(track->addStuckLiveNote(t_port, event.channel(), event.dataA())) // md->addScheduledEvent(event); md->putEvent(event, MidiDevice::NotLate); } } else { // TODO Maybe grab the flag from the 'Optimize Controllers' Global Setting, // which so far was meant for (N)RPN stuff. For now, just force it. // This is the audio thread. Just set directly. MusEGlobal::midiPorts[t_port].setHwCtrlState(event); md->putEvent(event, MidiDevice::NotLate); } } } } else { MidiDevice* mdAlt = MusEGlobal::midiPorts[devport].device(); // REMOVE Tim. monitor. Changed. //if(mdAlt && track_rec_monitor && !track->off() && !track->isMute()) if(mdAlt && track_rec_monitor) { // Do not echo synth events back to the same synth instance under any circumstances, // not even if monitor (echo) is on. if(!dev->isSynti() || dev != mdAlt) { MidiInstrument* minstr = MusEGlobal::midiPorts[devport].instrument(); MidiInstrument::NoteOffMode nom = minstr->noteOffMode(); // If the instrument has no note-off mode, do not use the // stuck notes mechanism, just send as is. // If the instrument has no note-off mode, do not use the stuck notes mechanism, send as is. // This allows drum input triggers (no note offs at all), although it is awkward to // first have to choose an output instrument with no note-off mode. if(nom == MidiInstrument::NoteOffNone) { if(event.isNoteOff()) // Try to remove any corresponding stuck live note. track->removeStuckLiveNote(event.port(), event.channel(), event.dataA()); // mdAlt->addScheduledEvent(event); mdAlt->putEvent(event, MidiDevice::NotLate); } else if(event.isNoteOff()) { // Try to remove any corresponding stuck live note. // Only if a stuck live note existed do we schedule the note off to play. if(track->removeStuckLiveNote(event.port(), event.channel(), event.dataA())) // mdAlt->addScheduledEvent(event); mdAlt->putEvent(event, MidiDevice::NotLate); } else if(event.isNote()) { // Check if a stuck live note exists on any track. ciMidiTrack it_other = mtl->begin(); for( ; it_other != mtl->end(); ++it_other) { if((*it_other)->stuckLiveNoteExists(event.port(), event.channel(), event.dataA())) break; } // Only if NO stuck live note existed do we schedule the note on to play. if(it_other == mtl->end()) { if(track->addStuckLiveNote(event.port(), event.channel(), event.dataA())) mdAlt->putEvent(event, MidiDevice::NotLate); } } else { // TODO Maybe grab the flag from the 'Optimize Controllers' Global Setting, // which so far was meant for (N)RPN stuff. For now, just force it. // This is the audio thread. Just set directly. MusEGlobal::midiPorts[devport].setHwCtrlState(event); mdAlt->putEvent(event, MidiDevice::NotLate); } } } } event.setTime(et); // Restore for recording. // Shall we activate meters even while rec echo is off? Sure, why not... if(event.isNote() && event.dataB() > track->activity()) track->setActivity(event.dataB()); } // Is the transport recording, or, is it about to be from external sync? if((recording || (MusEGlobal::song->record() && extsync && MusEGlobal::midiSyncContainer.isPlaying())) && track_rec_flag) { unsigned int et = event.time(); // Make sure the event is recorded in units of ticks. if(extsync) { const unsigned int xt = extClockHistoryFrame2Tick(event.time()); DEBUG_MIDI(stderr, "processMidi: event time:%d dataA:%d dataB:%d curTickPos:%u set time:%u\n", event.time(), event.dataA(), event.dataB(), curTickPos, xt); event.setTime(xt); } else { // REMOVE Tim. latency. Removed. Oops, with ALSA this adds undesired shift forward! // // All recorded events arrived in the previous period. Shift into this period for record. // REMOVE Tim. latency. Changed. Oops, with ALSA this adds undesired shift forward! // And with Jack midi we currently already shift forward, in the input routine! // But I'm debating where to add the correction factor - I really need to add it here // (exactly like the WaveTrack recording correction) but a very simple fix would be // in the Jack midi input routine to replace the current fixed segSize correction // factor with the MidiDevice::_latencyInfo._outputLatency, // but that is wrong although it would work. // To add the correction here may be more complicated than the WaveTrack, // some things in the ALSA and Jack midi input routines depend on the event time // (like midi clock, other rt events). // // // unsigned int t = et + MusEGlobal::segmentSize; // // Protection from slight errors in estimated frame time. // if(t >= (syncFrame + MusEGlobal::segmentSize)) // { // DEBUG_MIDI(stderr, "Error: Audio::processMidi(): record event: t:%u >= syncFrame:%u + segmentSize:%u (==%u)\n", // t, syncFrame, MusEGlobal::segmentSize, syncFrame + MusEGlobal::segmentSize); // // t = syncFrame + (MusEGlobal::segmentSize - 1); // } unsigned int t = et; // Protection from slight errors in estimated frame time. if(t >= syncFrame) { DEBUG_MIDI(stderr, "Error: Audio::processMidi(): record event: t:%u >= syncFrame:%u\n", t, syncFrame); t = syncFrame - 1; } // Be sure to allow for some (very) late events, such as // the first chunk's time in a multi-chunk sysex. const unsigned int a_fr = pos().frame() + t; const unsigned int fin_fr = syncFrame > a_fr ? 0 : a_fr - syncFrame; event.setTime(MusEGlobal::tempomap.frame2tick(fin_fr)); } // In these next steps, it is essential to set the recorded event's port // to the track port so buildMidiEventList will accept it. Even though // the port may have no device "". // if (track->isDrumTrack()) { // Is it a drum controller event? if(mc) { MusECore::MidiPlayEvent drumRecEvent = event; drumRecEvent.setA(ctl | drumRecPitch); // In this case, preVelo is simply the controller value. drumRecEvent.setB(preVelo); drumRecEvent.setPort(t_port); //rec-event to current port drumRecEvent.setChannel(t_channel); //rec-event to current channel track->mpevents.add(drumRecEvent); } else { MusECore::MidiPlayEvent drumRecEvent = event; drumRecEvent.setA(drumRecPitch); drumRecEvent.setB(preVelo); // Changed to 'port'. Events were not being recorded for a drum map entry pointing to a // different port. That must have been wrong - buildMidiEventList would ignore that. Tim. drumRecEvent.setPort(t_port); //rec-event to current port drumRecEvent.setChannel(t_channel); //rec-event to current channel track->mpevents.add(drumRecEvent); } } else { // Restore record-pitch to non-transposed value since we don't want the note transposed twice next MusECore::MidiPlayEvent recEvent = event; if (prePitch) recEvent.setA(prePitch); if (preVelo) recEvent.setB(preVelo); recEvent.setPort(t_port); recEvent.setChannel(t_channel); track->mpevents.add(recEvent); } // Restore. Not required. //event.setTime(et); } } } } } // Must be playing for valid nextTickPos, right? But wasn't checked in Audio::processMidi(). // MusEGlobal::audio->isPlaying() might not be true during seek right now. //if(MusEGlobal::audio->isPlaying()) //if(playing) //{ ciMPEvent k; MidiDevice* mdev; int mport; // What is the current transport frame? const unsigned int pos_fr = _pos.frame(); // What is the (theoretical) next transport frame? const unsigned int next_pos_fr = pos_fr + frames; // If muted or off we want to send all playback note-offs immediately. if(track->isMute() || track->off()) { //--------------------------------------------------- // Send all track-related playback note-offs (NOT 'live' note-offs) // which were not put directly to the device //--------------------------------------------------- MPEventList& mel = track->stuckNotes; if(!mel.empty()) { for(k = mel.begin(); k != mel.end(); ++k) { MidiPlayEvent ev(*k); mport = ev.port(); if(mport < 0) continue; mdev = MusEGlobal::midiPorts[mport].device(); if(!mdev) continue; ev.setTime(0); // Mark for immediate delivery. //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(k->time())); mdev->putEvent(ev, MidiDevice::NotLate); } mel.clear(); } } else // If not muted and not off, we want to schedule all playback note-offs normally. { //--------------------------------------------------- // Schedule all track-related playback note-offs (NOT 'live' note-offs) // which were not put directly to the device // To save time this was put here instead of MidiDevice::processStuckNotes() //--------------------------------------------------- MPEventList& mel = track->stuckNotes; if(!mel.empty()) { for(k = mel.begin(); k != mel.end(); ++k) { MidiPlayEvent ev(*k); unsigned int off_tick = ev.time(); // If external sync is not on, we can take advantage of frame accuracy but // first we must allow the next tick position to be included in the search // even if it is equal to the current tick position. if(extsync ? (off_tick >= nextTickPos) : (off_tick > nextTickPos)) break; mport = ev.port(); if(mport < 0) continue; mdev = MusEGlobal::midiPorts[mport].device(); if(!mdev) continue; unsigned int off_frame = 0; if(extsync) { if(off_tick < curTickPos) off_tick = curTickPos; off_frame = extClockHistoryTick2Frame(off_tick - curTickPos) + MusEGlobal::segmentSize; } else { // What is the exact transport frame that the event should be played at? const unsigned int fr = MusEGlobal::tempomap.tick2frame(off_tick); // Is the event frame outside of the current transport frame range? if(fr >= next_pos_fr) break; off_frame = (fr < pos_fr) ? 0 : fr - pos_fr; off_frame += syncFrame; } ev.setTime(off_frame); // TODO: DECIDE: Hm, we don't want the device to miss any note offs. // So I guess schedule this as a user event rather than a playback event. mdev->putEvent(ev, MidiDevice::NotLate); } mel.erase(mel.begin(), k); } } // If no monitor or off, or not rec-armed (or muted), we want to cancel all 'live' (rec) stuck notes immediately. // REMOVE Tim. monitor. Changed. //if(!track_rec_monitor || track->off() || track->isMute()) if(!track_rec_monitor) { //------------------------------------------------------------ // Send all track-related 'live' (rec) note-offs // which were not put directly to the device //------------------------------------------------------------ MPEventList& mel = track->stuckLiveNotes; if(!mel.empty()) { for(k = mel.begin(); k != mel.end(); ++k) { MidiPlayEvent ev(*k); mport = ev.port(); if(mport < 0) continue; mdev = MusEGlobal::midiPorts[mport].device(); if(!mdev) continue; ev.setTime(0); // Mark for immediate delivery. //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(k->time())); mdev->putEvent(ev, MidiDevice::NotLate); } mel.clear(); } } //} } //--------------------------------------------------- // insert metronome clicks //--------------------------------------------------- // If in PRECOUNT state, process the precount events. processPrecount(frames); // Since the latency for audio and midi may be different, // process the audio and midi metronomes separately. processAudioMetronome(frames); processMidiMetronome(frames); // REMOVE Tim. clock. Added. // //--------------------------------------------------- // // send midi clock output events // //--------------------------------------------------- // // _clockOutputQueueSize = 0; // if(!extsync) // { // const unsigned curr_audio_frame = syncFrame; // const unsigned next_audio_frame = curr_audio_frame + frames; // // const uint64_t numer = (uint64_t)MusEGlobal::config.division * (uint64_t)MusEGlobal::tempomap.globalTempo() * 10000UL; // const uint64_t denom = (uint64_t)MusEGlobal::config.division * (uint64_t)MusEGlobal::tempomap.globalTempo() * 10000UL; // const unsigned int div = MusEGlobal::config.division/24; // // // // Do not round up here since (audio) frame resolution is higher than tick resolution. // // const unsigned int clock_tick = muse_multiply_64_div_64_to_64(numer, curr_audio_frame, // // (uint64_t)MusEGlobal::sampleRate * (uint64_t)MusEGlobal::tempomap.tempo(curTickPos)); // // // unsigned int clock_tick_end; // // Is the transport moving? // if(playing) // { // // unsigned int delta_tick; // // // Did tick position wrap around? // // if(curTickPos > nextTickPos) // // delta_tick = curTickPos - nextTickPos; // // else // // delta_tick = nextTickPos - curTickPos; // // clock_tick_end = clock_tick + delta_tick; // } // else // { // // // Do not round up here since (audio) frame resolution is higher than tick resolution. // // clock_tick_end = muse_multiply_64_div_64_to_64(numer, next_audio_frame, // // (uint64_t)MusEGlobal::sampleRate * (uint64_t)MusEGlobal::tempomap.tempo(curTickPos)); // // uint64_t div_remainder; // const uint64_t div_frames = muse_multiply_64_div_64_to_64( // (uint64_t)MusEGlobal::sampleRate * (uint64_t)MusEGlobal::tempomap.tempo(curTickPos), div, // denom, LargeIntRoundNone, &div_remainder); // // // Counter too far in future? Reset. // if(_clockOutputCounter >= curr_audio_frame && _clockOutputCounter - curr_audio_frame >= div_frames) // { // _clockOutputCounter = curr_audio_frame; // _clockOutputCounterRemainder = 0; // } // // Counter too far in past? Reset. // else if(_clockOutputCounter < curr_audio_frame) // { // _clockOutputCounter = curr_audio_frame; // _clockOutputCounterRemainder = 0; // } // // //const uint64_t curr_clock_out_count = _clockOutputCounter + div_frames + (_clockOutputCounterRemainder + div_remainder) / denom; // //uint64_t next_clock_out_frame = _clockOutputCounter + div_frames + (_clockOutputCounterRemainder + div_remainder) / denom; // uint64_t raccum; // //while(next_clock_out_frame <= next_audio_frame) // while(_clockOutputCounter < next_audio_frame) // { // if(_clockOutputQueueSize >= _clockOutputQueueCapacity) // break; // // //_clockOutputQueue[_clockOutputQueueSize] = _clockOutputCounter - curr_audio_frame; // _clockOutputQueue[_clockOutputQueueSize] = _clockOutputCounter; // ++_clockOutputQueueSize; // raccum = _clockOutputCounterRemainder + div_remainder; // _clockOutputCounter += div_frames + (raccum / denom); // _clockOutputCounterRemainder = raccum % denom; // } // //const uint64_t next_clock_out_count = _clockOutputCounter + div_frames + (_clockOutputCounterRemainder + div_remainder) / denom; // // //if(next_audio_frame >= next_clock_out_count) // //{ // // //} // } // // // // Did clock_tick wrap around? // // if(_clockOutputCounter > clock_tick) // // _clockOutputCounter = clock_tick; // // // // //const unsigned int div = MusEGlobal::config.division/24; // // if(clock_tick_end >= _clockOutputCounter + div) // // { // // // This will always be at least 1. // // const unsigned int num_clocks = (clock_tick_end - _clockOutputCounter) / div; // // const unsigned int clk_frame_step = frames / num_clocks; // // const unsigned int clk_frame_step_rem = frames % num_clocks; // // unsigned int clk_frame_off; // // for(unsigned int c = 0; c < num_clocks; ++c) // // { // // if(c >= _clockOutputQueueCapacity) // // break; // // clk_frame_off = c * clk_frame_step + (c * clk_frame_step_rem) / num_clocks; // // } // // // // _clockOutputCounter = clock_tick_end; // // } // // // // // // unsigned cc, f; // // while(1) // // { // // cc = _clockOutputCounter + div; // // f = Pos(cc, true).frame(); // // //if(f // // } // } // // Play all midi events up to curFrame. // for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { MidiDevice* pl_md = *id; // const int pl_port = pl_md->midiPort(); // We are done with the 'frozen' recording fifos, remove the events. pl_md->afterProcess(); pl_md->processStuckNotes(); // REMOVE Tim. clock. Added. // While we are at it, to avoid the overhead of yet another device loop, // handle midi clock output here, for all device types. // if(!extsync && pl_port >= 0 && pl_port < MIDI_PORTS) // { // MidiPort* clk_mp = &MusEGlobal::midiPorts[pl_port]; // // Clock out turned on? // if(clk_mp->syncInfo().MCOut()) // { // for(unsigned int i = 0; i < _clockOutputQueueSize; ++i) // { // const MidiPlayEvent clk_ev(_clockOutputQueue[i], pl_port, 0, MusECore::ME_CLOCK, 0, 0); // pl_md->putEvent(clk_ev, MidiDevice::NotLate /*,MidiDevice::PlaybackBuffer*/); // } // } // } // ALSA devices handled by another thread. const MidiDevice::MidiDeviceType typ = pl_md->deviceType(); switch(typ) { case MidiDevice::ALSA_MIDI: break; case MidiDevice::JACK_MIDI: case MidiDevice::SYNTH_MIDI: // The frame is not used by these devices but we pass it along anyway. // Only ALSA devices need the frame. pl_md->processMidi(syncFrame); break; } } // Receive hardware state events sent from various threads to this audio thread. // Update hardware state so gui controls are updated. MusEGlobal::song->processIpcOutEventBuffers(); } //--------------------------------------------------------- // processPrecount //--------------------------------------------------------- void Audio::processPrecount(unsigned int frames) { //DEBUG_MIDI_METRONOME(stderr, "Audio::processPrecount: state:%d\n", state); if(state != PRECOUNT) return; MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; const unsigned int nextPrecountFramePos = _precountFramePos + frames; DEBUG_MIDI_METRONOME(stderr, "Audio::processPrecount: precount: syncFrame:%u _pos.frame():%u" "_precountFramePos:%u precountMidiClickFrame:%u nextPrecountFramePos:%u clickno:%d\n", syncFrame, _pos.frame(), _precountFramePos, precountMidiClickFrame, nextPrecountFramePos, clickno); MidiDevice* md = nullptr; if(metro_settings->midiClickFlag) md = MusEGlobal::midiPorts[metro_settings->clickPort].device(); AudioTickSound audioTickSound = MusECore::beatSound; while(true) { const unsigned precount_click_frame = precountMidiClickFrame + (precountMidiClickFrameRemainder ? 1 :0); if(precount_click_frame >= nextPrecountFramePos) break; // Don't actually sound anything if we're just running out the time. // There will be times when _precountFramePos >= precountTotalFrames // and we must let it run out. if(_precountFramePos < precountTotalFrames) { if(MusEGlobal::song->click()) { if ((clickno % clicksMeasure) == 0) audioTickSound = MusECore::measureSound; else audioTickSound = MusECore::beatSound; // We need to shift ahead in time because Jack waits one more cycle, // unlike our own built-in transport which starts immediately. const unsigned int evtime = syncFrame + MusEGlobal::audioDevice->transportSyncToPlayDelay() + ((precount_click_frame < _precountFramePos) ? 0 : precount_click_frame - _precountFramePos); MusECore::MidiPlayEvent ev(evtime, metro_settings->clickPort, metro_settings->clickChan, MusECore::ME_NOTEON, metro_settings->beatClickNote, metro_settings->beatClickVelo); if (audioTickSound == MusECore::measureSound) { ev.setA(metro_settings->measureClickNote); ev.setB(metro_settings->measureClickVelo); } if (md) { MusECore::MidiPlayEvent evmidi = ev; #ifdef DEBUG_MIDI_TIMING_DIFFS fprintf(stderr, "EVENT TIME DIFF:%u\n", ev.time() - _lastEvTime); _lastEvTime = ev.time(); #endif md->putEvent(evmidi, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); // Internal midi paths are now all note off aware. Driver handles note offs. Convert. // Ticksynth has been modified too. evmidi.setType(MusECore::ME_NOTEOFF); evmidi.setB(0); evmidi.setTime(ev.time() + MusEGlobal::tempomap.ticks2frames(10, curTickPos)); // The precount CANNOT use the stuck notes mechanism because the stuck notes // mechanism wants ticks not frames, and it later compares those ticks // with nextTickPos and converts them into frames using midiQueueTimeStamp(), // which is not a valid mechanism during precount! // Therefore we have no choice but to directly schedule a note-off. // Since it is important that they not be missed, schedule these as userBuffer events. // Should be OK, it's unlikely that the note-off would be missed unless the user could // somehow seek during precount or something. (The userBuffer tries not to miss anything.) // The idea behind the stuck notes mechanism was that it converts to frames // only at scheduling time so that if the user might change the tempo while // playing a song, the note-off times will be properly adjusted. // But this precount mechanism CANNOT honour any tempo changes anyway because // we must know the total required precount frames BEFOREHAND so that it can be aligned // with the metronome clicks properly. Thus negating any possible tempo change support. //md->addStuckNote(evmidi); md->putEvent(evmidi, MidiDevice::NotLate, MidiDevice::UserBuffer); } if (metro_settings->audioClickFlag) { ev.setA(audioTickSound); DEBUG_MIDI_METRONOME(stderr, "Audio::processMidi: precount: metronome->putEvent\n"); metronome->putEvent(ev, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); // Built-in metronome synth does not use stuck notes... } } } precountMidiClickFrame += framesBeat; precountMidiClickFrameRemainder += framesBeatRemainder; if(precountMidiClickFrameRemainder >= framesBeatDivisor) { precountMidiClickFrame++; precountMidiClickFrameRemainder -= framesBeatDivisor; } ++clickno; } _precountFramePos += frames; } //--------------------------------------------------------- // processAudioMetronome //--------------------------------------------------------- void Audio::processMidiMetronome(unsigned int frames) { const MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; const bool extsync = MusEGlobal::extSyncFlag; const bool playing = isPlaying(); // Should the metronome be muted after precount? const bool precount_mute_metronome = metro_settings->precountEnableFlag && MusEGlobal::song->click() && !extsync && ((!MusEGlobal::song->record() && metro_settings->precountOnPlay) || MusEGlobal::song->record()) && metro_settings->precountMuteMetronome; MidiDevice* md = 0; if (metro_settings->midiClickFlag && !precount_mute_metronome) md = MusEGlobal::midiPorts[metro_settings->clickPort].device(); if (playing) { int bar, beat, z, n; unsigned tick; AudioTickSound audioTickSound = MusECore::beatSound; const MusECore::MetroAccents* accents; int accents_sz; unsigned int lat_offset_midi = 0; unsigned int cur_tick_midi = curTickPos; unsigned int next_tick_midi = nextTickPos; //-------------------------------------------------------------------- // Account for the metronome's latency correction and/or compensation. //-------------------------------------------------------------------- // TODO How to handle when external sync is on. For now, don't try to correct. if(MusEGlobal::config.enableLatencyCorrection && !extsync) { if(metro_settings->midiClickFlag) { const TrackLatencyInfo& li = metronome->getLatencyInfoMidi(false /*playback*/, false); // This value is negative for correction. const float mlat = li._sourceCorrectionValue; if((int)mlat < 0) { // Convert to a positive offset. const unsigned int l = (unsigned int)(-mlat); if(l > lat_offset_midi) lat_offset_midi = l; } if(lat_offset_midi != 0) { Pos ppp(_pos.frame() + lat_offset_midi, false); cur_tick_midi = ppp.tick(); ppp += frames; next_tick_midi = ppp.tick(); } } } // What is the current transport frame, adjusted? const unsigned int pos_fr_midi = _pos.frame() + lat_offset_midi; // What is the (theoretical) next transport frame? const unsigned int next_pos_fr_midi = pos_fr_midi + frames; // If external sync is not on, we can take advantage of frame accuracy but // first we must allow the next tick position to be included in the search // even if it is equal to the current tick position. while (extsync ? (midiClick < next_tick_midi) : (midiClick <= next_tick_midi)) { bool do_play = true; unsigned int evtime = 0; if(extsync) { if(midiClick < cur_tick_midi) midiClick = cur_tick_midi; evtime = extClockHistoryTick2Frame(midiClick - cur_tick_midi) + MusEGlobal::segmentSize; } else { // What is the exact transport frame that the midiClick should be played at? const unsigned int fr = MusEGlobal::tempomap.tick2frame(midiClick); // Is the midiClick frame outside of the current transport frame range? if(fr < pos_fr_midi || fr >= next_pos_fr_midi) { // Break out of the loop if midiClick equals nextTickPos. if(midiClick == next_tick_midi) break; // Continue on, but do not play any notes. do_play = false; } evtime = fr - pos_fr_midi; evtime += syncFrame; } DEBUG_MIDI_METRONOME(stderr, "Audio::processMidiMetronome: playing: syncFrame:%u _pos.frame():%u midiClick:%u next_tick_midi:%u clickno:%d\n", syncFrame, _pos.frame(), midiClick, next_tick_midi, clickno); MusEGlobal::sigmap.tickValues(midiClick, &bar, &beat, &tick); MusEGlobal::sigmap.timesig(midiClick, z, n); // How many ticks per beat? const int ticks_beat = MusEGlobal::sigmap.ticks_beat(n); if (do_play && MusEGlobal::song->click() && (metro_settings->midiClickFlag) && !precount_mute_metronome) { if (tick == 0 && beat == 0) { audioTickSound = MusECore::measureSound; if (MusEGlobal::debugMsg) fprintf(stderr, "meas: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d ticks_beat %d\n", midiClick, next_tick_midi, bar, beat, tick, z, n, ticks_beat); } else if (tick == unsigned(ticks_beat - (ticks_beat/(n*2)))) { audioTickSound = MusECore::accent2Sound; if (MusEGlobal::debugMsg) fprintf(stderr, "acc2: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d ticks_beat %d\n", midiClick, next_tick_midi, bar, beat, tick, z, n, ticks_beat); } else if (tick == unsigned(ticks_beat - (ticks_beat/n))) { audioTickSound = MusECore::accent1Sound; if (MusEGlobal::debugMsg) fprintf(stderr, "acc1: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d ticks_beat %d\n", midiClick, next_tick_midi, bar, beat, tick, z, n, ticks_beat); } else { if (MusEGlobal::debugMsg) fprintf(stderr, "beat: midiClick %d nextPos %d bar %d beat %d tick %d z %d n %d div %d\n", midiClick, next_tick_midi, bar, beat, tick, z, n, ticks_beat); } MusECore::MidiPlayEvent ev(evtime, metro_settings->clickPort, metro_settings->clickChan, MusECore::ME_NOTEON, metro_settings->beatClickNote, metro_settings->beatClickVelo); if (audioTickSound == MusECore::measureSound) { ev.setA(metro_settings->measureClickNote); ev.setB(metro_settings->measureClickVelo); } if (audioTickSound == MusECore::accent1Sound) { ev.setA(metro_settings->accentClick1); ev.setB(metro_settings->accentClick1Velo); } if (audioTickSound == MusECore::accent2Sound) { ev.setA(metro_settings->accentClick2); ev.setB(metro_settings->accentClick2Velo); } // Should the metronome be played after precount? if(!precount_mute_metronome) { // Don't bother sending to midi out if velocity is zero. if (metro_settings->midiClickFlag && md && ev.dataB() > 0) { MusECore::MidiPlayEvent evmidi = ev; #ifdef DEBUG_MIDI_TIMING_DIFFS fprintf(stderr, "EVENT TIME DIFF:%u\n", evmidi.time() - _lastEvTime); _lastEvTime = evmidi.time(); #endif md->putEvent(evmidi, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); // Internal midi paths are now all note off aware. Driver handles note offs. Convert. // Ticksynth has been modified too. evmidi.setType(MusECore::ME_NOTEOFF); evmidi.setB(0); evmidi.setTime(midiClick+10); md->addStuckNote(evmidi); } } } const int beat_mod = (beat + 1) % z; MetroAccent::AccentTypes_t acc_types = MetroAccent::NoAccent; if(metro_settings->metroAccentsMap) { MusECore::MetroAccentsMap::const_iterator imap = metro_settings->metroAccentsMap->find(z); if(imap != metro_settings->metroAccentsMap->cend()) { const MusECore::MetroAccentsStruct& mas = imap->second; accents = &mas._accents; accents_sz = accents->size(); if(beat_mod < accents_sz) acc_types = accents->at(beat_mod)._accentType; } } // State machine to select next midiClick position. if (metro_settings->clickSamples == MetronomeSettings::newSamples) { if (tick == 0) {// ON key if(acc_types & MetroAccent::Accent1) { // Cue an accent 1. (This part 'triggers' an accent sequence to begin // which automatically cues accent 2 if required then a normal beat, as shown below.) midiClick = MusEGlobal::sigmap.bar2tick(bar, beat, ticks_beat - ((ticks_beat/n))); } else if(acc_types & MetroAccent::Accent2) { // Cue accent 2. midiClick = MusEGlobal::sigmap.bar2tick(bar, beat, ticks_beat - (ticks_beat/(n*2))); } else { // Cue a normal beat. midiClick = MusEGlobal::sigmap.bar2tick(bar, beat+1, 0); } } else if (tick >= unsigned(ticks_beat - (ticks_beat/(n*2)))) { // second accent tick // Finished accent 2. Cue a normal beat. midiClick = MusEGlobal::sigmap.bar2tick(bar, beat+1, 0); } else if (tick < unsigned(ticks_beat - ((ticks_beat/(n*2))))) { // first accent tick if(acc_types & MetroAccent::Accent2) // Finished accent 1. Cue accent 2. midiClick = MusEGlobal::sigmap.bar2tick(bar, beat, ticks_beat - (ticks_beat/(n*2))); else // Finished accent 1. Cue a normal beat. midiClick = MusEGlobal::sigmap.bar2tick(bar, beat+1, 0); } } else { midiClick = MusEGlobal::sigmap.bar2tick(bar, beat+1, 0); } } } } //--------------------------------------------------------- // processAudioMetronome //--------------------------------------------------------- void Audio::processAudioMetronome(unsigned int frames) { const MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; const bool extsync = MusEGlobal::extSyncFlag; const bool playing = isPlaying(); // Should the metronome be muted after precount? const bool precount_mute_metronome = metro_settings->precountEnableFlag && MusEGlobal::song->click() && !extsync && ((!MusEGlobal::song->record() && metro_settings->precountOnPlay) || MusEGlobal::song->record()) && metro_settings->precountMuteMetronome; if (playing) { int bar, beat, z, n; unsigned tick; AudioTickSound audioTickSound = MusECore::beatSound; const MusECore::MetroAccents* accents; int accents_sz; unsigned int lat_offset = 0; unsigned int cur_tick = curTickPos; unsigned int next_tick = nextTickPos; //-------------------------------------------------------------------- // Account for the metronome's latency correction and/or compensation. //-------------------------------------------------------------------- // TODO How to handle when external sync is on. For now, don't try to correct. if(MusEGlobal::config.enableLatencyCorrection && !extsync) { if(metro_settings->audioClickFlag) { const TrackLatencyInfo& li = metronome->getLatencyInfo(false); // This value is negative for correction. const float mlat = li._sourceCorrectionValue; if((int)mlat < 0) { // Convert to a positive offset. const unsigned int l = (unsigned int)(-mlat); if(l > lat_offset) lat_offset = l; } if(lat_offset != 0) { Pos ppp(_pos.frame() + lat_offset, false); cur_tick = ppp.tick(); ppp += frames; next_tick = ppp.tick(); } } } // What is the current transport frame, adjusted? const unsigned int pos_fr = _pos.frame() + lat_offset; // What is the (theoretical) next transport frame? const unsigned int next_pos_fr = pos_fr + frames; // If external sync is not on, we can take advantage of frame accuracy but // first we must allow the next tick position to be included in the search // even if it is equal to the current tick position. while (extsync ? (audioClick < next_tick) : (audioClick <= next_tick)) { bool do_play = true; unsigned int evtime = 0; if(extsync) { if(audioClick < cur_tick) audioClick = cur_tick; evtime = extClockHistoryTick2Frame(audioClick - cur_tick) + MusEGlobal::segmentSize; } else { // What is the exact transport frame that the click should be played at? const unsigned int fr = MusEGlobal::tempomap.tick2frame(audioClick); // Is the click frame outside of the current transport frame range? if(fr < pos_fr || fr >= next_pos_fr) { // Break out of the loop if midiClick equals nextTickPos. if(audioClick == next_tick) break; // Continue on, but do not play any notes. do_play = false; } evtime = fr - pos_fr; evtime += syncFrame; } DEBUG_MIDI_METRONOME(stderr, "Audio::processAudioMetronome: playing: syncFrame:%u _pos.frame():%u audioClick:%u next_tick:%u clickno:%d\n", syncFrame, _pos.frame(), audioClick, next_tick, clickno); MusEGlobal::sigmap.tickValues(audioClick, &bar, &beat, &tick); MusEGlobal::sigmap.timesig(audioClick, z, n); // How many ticks per beat? const int ticks_beat = MusEGlobal::sigmap.ticks_beat(n); if (do_play && MusEGlobal::song->click() && (metro_settings->audioClickFlag) && !precount_mute_metronome) { if (tick == 0 && beat == 0) { audioTickSound = MusECore::measureSound; if (MusEGlobal::debugMsg) fprintf(stderr, "meas: audioClick %d next_tick %d bar %d beat %d tick %d z %d n %d ticks_beat %d\n", audioClick, next_tick, bar, beat, tick, z, n, ticks_beat); } else if (tick == unsigned(ticks_beat - (ticks_beat/(n*2)))) { audioTickSound = MusECore::accent2Sound; if (MusEGlobal::debugMsg) fprintf(stderr, "acc2: audioClick %d next_tick %d bar %d beat %d tick %d z %d n %d ticks_beat %d\n", audioClick, next_tick, bar, beat, tick, z, n, ticks_beat); } else if (tick == unsigned(ticks_beat - (ticks_beat/n))) { audioTickSound = MusECore::accent1Sound; if (MusEGlobal::debugMsg) fprintf(stderr, "acc1: audioClick %d next_tick %d bar %d beat %d tick %d z %d n %d ticks_beat %d\n", audioClick, next_tick, bar, beat, tick, z, n, ticks_beat); } else { if (MusEGlobal::debugMsg) fprintf(stderr, "beat: audioClick %d next_tick %d bar %d beat %d tick %d z %d n %d div %d\n", audioClick, next_tick, bar, beat, tick, z, n, ticks_beat); } // Should the metronome be played after precount? if(!precount_mute_metronome) { if (metro_settings->audioClickFlag) { MusECore::MidiPlayEvent ev(evtime, 0, 0, MusECore::ME_NOTEON, audioTickSound, 0); DEBUG_MIDI_METRONOME(stderr, "Audio::processAudioMetronome: playing: metronome->putEvent\n"); metronome->putEvent(ev, MidiDevice::NotLate, MidiDevice::PlaybackBuffer); // Built-in metronome synth does not use stuck notes... } } } const int beat_mod = (beat + 1) % z; MetroAccent::AccentTypes_t acc_types = MetroAccent::NoAccent; if(metro_settings->metroAccentsMap) { MusECore::MetroAccentsMap::const_iterator imap = metro_settings->metroAccentsMap->find(z); if(imap != metro_settings->metroAccentsMap->cend()) { const MusECore::MetroAccentsStruct& mas = imap->second; accents = &mas._accents; accents_sz = accents->size(); if(beat_mod < accents_sz) acc_types = accents->at(beat_mod)._accentType; } } // State machine to select next midiClick position. if (metro_settings->clickSamples == MetronomeSettings::newSamples) { if (tick == 0) {// ON key if(acc_types & MetroAccent::Accent1) { // Cue an accent 1. (This part 'triggers' an accent sequence to begin // which automatically cues accent 2 if required then a normal beat, as shown below.) audioClick = MusEGlobal::sigmap.bar2tick(bar, beat, ticks_beat - ((ticks_beat/n))); } else if(acc_types & MetroAccent::Accent2) { // Cue accent 2. audioClick = MusEGlobal::sigmap.bar2tick(bar, beat, ticks_beat - (ticks_beat/(n*2))); } else { // Cue a normal beat. audioClick = MusEGlobal::sigmap.bar2tick(bar, beat+1, 0); } } else if (tick >= unsigned(ticks_beat - (ticks_beat/(n*2)))) { // second accent tick // Finished accent 2. Cue a normal beat. audioClick = MusEGlobal::sigmap.bar2tick(bar, beat+1, 0); } else if (tick < unsigned(ticks_beat - ((ticks_beat/(n*2))))) { // first accent tick if(acc_types & MetroAccent::Accent2) // Finished accent 1. Cue accent 2. audioClick = MusEGlobal::sigmap.bar2tick(bar, beat, ticks_beat - (ticks_beat/(n*2))); else // Finished accent 1. Cue a normal beat. audioClick = MusEGlobal::sigmap.bar2tick(bar, beat+1, 0); } } else { audioClick = MusEGlobal::sigmap.bar2tick(bar, beat+1, 0); } } } } } // namespace MusECore muse-4.0.0/src/muse/midi.h000066400000000000000000000035231404105311000153070ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midi.h,v 1.4.2.2 2009/11/09 20:28:28 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDI_H__ #define __MIDI_H__ #include #include #include #include #include "midi_consts.h" namespace MusECore { class EventList; class MPEventList; class MidiTrack; class MidiInstrument; extern QString nameSysex(unsigned int len, const unsigned char* buf, MidiInstrument* instr = 0); extern QString sysexComment(unsigned int len, const unsigned char* buf, MidiInstrument* instr = 0); extern QString midiMetaName(int meta); // Division can be zero meaning the event times are to be taken verbosely // (as ticks already), no conversion is to be applied. extern void buildMidiEventList(EventList* mel, const MPEventList& el, MidiTrack* track, int division, bool addSysexMeta, bool doLoops); } // namespace MusECore #endif muse-4.0.0/src/muse/midi_consts.h000066400000000000000000000120571404105311000167020ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // midi_consts.h // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDI_CONSTS_H__ #define __MIDI_CONSTS_H__ namespace MusECore { enum { ME_NOTEOFF = 0x80, ME_NOTEON = 0x90, ME_POLYAFTER = 0xa0, ME_CONTROLLER = 0xb0, ME_PROGRAM = 0xc0, ME_AFTERTOUCH = 0xd0, ME_PITCHBEND = 0xe0, ME_SYSEX = 0xf0, ME_META = 0xff, ME_MTC_QUARTER = 0xf1, ME_SONGPOS = 0xf2, ME_SONGSEL = 0xf3, ME_TUNE_REQ = 0xf6, ME_SYSEX_END = 0xf7, ME_CLOCK = 0xf8, ME_TICK = 0xf9, ME_START = 0xfa, ME_CONTINUE = 0xfb, ME_STOP = 0xfc, ME_SENSE = 0xfe, ME_RESET = 0xff }; //-------------------------------------- // Recognized / transmitted meta events: //-------------------------------------- enum { ME_META_TEXT_0_SEQUENCE_NUMBER = 0x00, ME_META_TEXT_1_COMMENT = 0x01, ME_META_TEXT_2_COPYRIGHT = 0x02, ME_META_TEXT_3_TRACK_NAME = 0x03, ME_META_TEXT_4_INSTRUMENT_NAME = 0x04, ME_META_TEXT_5_LYRIC = 0x05, ME_META_TEXT_6_MARKER = 0x06, ME_META_TEXT_7_CUE_POINT = 0x07, ME_META_TEXT_8_PROGRAM_NAME = 0x08, ME_META_TEXT_9_DEVICE_NAME = 0x09, ME_META_TEXT_A = 0x0a, ME_META_TEXT_B = 0x0b, ME_META_TEXT_C = 0x0c, ME_META_TEXT_D = 0x0d, ME_META_TEXT_E = 0x0e, ME_META_TEXT_F_TRACK_COMMENT = 0x0f, ME_META_CHANNEL_CHANGE = 0x20, ME_META_PORT_CHANGE = 0x21, ME_META_END_OF_TRACK = 0x2f, ME_META_SET_TEMPO = 0x51, ME_META_SMPTE_OFFSET = 0x54, ME_META_TIME_SIGNATURE = 0x58, ME_META_KEY_SIGNATURE = 0x59, ME_META_SEQ_SPECIFIC_1 = 0x74, ME_META_SEQ_SPECIFIC_2 = 0x7f }; enum AudioTickSound { beatSound, measureSound, accent1Sound, accent2Sound }; const unsigned char gmOnMsg[] = { 0x7e, 0x7f, 0x09, 0x01 }; const unsigned char gm2OnMsg[] = { 0x7e, 0x7f, 0x09, 0x03 }; const unsigned char gmOffMsg[] = { 0x7e, 0x7f, 0x09, 0x02 }; const unsigned char gsOnMsg[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7f, 0x00, 0x41 }; const unsigned char gsOnMsg2[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x33, 0x50, 0x3c }; const unsigned char gsOnMsg3[] = { 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x34, 0x50, 0x3b }; const unsigned char xgOnMsg[] = { 0x43, 0x10, 0x4c, 0x00, 0x00, 0x7e, 0x00 }; const unsigned int gmOnMsgLen = sizeof(gmOnMsg); const unsigned int gm2OnMsgLen = sizeof(gm2OnMsg); const unsigned int gmOffMsgLen = sizeof(gmOffMsg); const unsigned int gsOnMsgLen = sizeof(gsOnMsg); const unsigned int gsOnMsg2Len = sizeof(gsOnMsg2); const unsigned int gsOnMsg3Len = sizeof(gsOnMsg3); const unsigned int xgOnMsgLen = sizeof(xgOnMsg); const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; const unsigned char mmcStopMsg[] = { 0x7f, 0x7f, 0x06, 0x01 }; const unsigned char mmcLocateMsg[] = { 0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, 0, 0, 0, 0, 0 }; const unsigned int mmcDeferredPlayMsgLen = sizeof(mmcDeferredPlayMsg); const unsigned int mmcStopMsgLen = sizeof(mmcStopMsg); const unsigned int mmcLocateMsgLen = sizeof(mmcLocateMsg); // Use these in all the synths and their guis. // NOTE: Some synths and hosts might not use this scheme. For example, MESS requires it for IPC, // and both MESS and DSSI use it to store init data in the form of a sysex. // // Added this here for ease, since they all include this file. // // A special MusE soft synth sysex manufacturer ID. #define MUSE_SYNTH_SYSEX_MFG_ID 0x7c // Following the MFG_ID, besides synth specific IDs, this reserved special ID indicates // a MusE SYSTEM ID will follow in the next byte. #define MUSE_SYSEX_SYSTEM_ID 0x7f // This SYSTEM command will force any relevant drum maps to update. // When a synth's note names have changed, it should issue this command. // So far, this command is really just a special requirement for the fluidsynth MESS plugin. // It is the only way to inform the host to update the maps. #define MUSE_SYSEX_SYSTEM_UPDATE_DRUM_MAPS_ID 0x00 } // namespace MusECore #endif muse-4.0.0/src/muse/midi_editor_layout.cpp000066400000000000000000000120511404105311000206010ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // midi_editor_layout.cpp // (C) Copyright 2020 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "midi_editor_layout.h" #include #include namespace MusEGui { //--------------------------------------------------------- // MidiEditorCanvasLayout //--------------------------------------------------------- void MidiEditorCanvasLayout::setGeometry(const QRect &rect) { QGridLayout::setGeometry(rect); // Tell the hbox to update as well, as if it was part of this layout. if(_hBox) { //_hBox->activate(); _hBox->update(); } } //--------------------------------------------------------- // MidiEditorHScrollLayout //--------------------------------------------------------- MidiEditorHScrollLayout::MidiEditorHScrollLayout(QWidget *parent, QWidget* button1, QWidget* button2, QWidget* sb, QWidget* corner, QWidget* editor) : QHBoxLayout(parent), _button1(button1), _button2(button2), _sb(sb), _corner(corner), _editor(editor), _button1Li(nullptr), _button2Li(nullptr), _cornerLi(nullptr) { _spacerLi = new QSpacerItem(0, 0); if(_button1) _button1Li = new QWidgetItem(_button1); if(_button2) _button2Li = new QWidgetItem(_button2); _sbLi = new QWidgetItem(_sb); if(_corner) _cornerLi = new QWidgetItem(_corner); addItem(_spacerLi); if(_button1Li) addItem(_button1Li); if(_button2Li) addItem(_button2Li); addItem(_sbLi); if(_cornerLi) { addItem(_cornerLi); setAlignment(_corner, Qt::AlignBottom|Qt::AlignRight); } }; void MidiEditorHScrollLayout::setGeometry(const QRect &rect) { if(!_editor) { QHBoxLayout::setGeometry(rect); return; } // const int ti_w = _button1Li->sizeHint().width() + spacing() + // (_button2Li ? (_button2Li->sizeHint().width() + spacing()) : 0); // The buttons use a fixed width! sizeHint and minimumSizeHint are no help here! const int ti_w = (_button1 ? (_button1->width() + spacing()) : 0) + (_button2 ? (_button2->width() + spacing()) : 0); const int corner_w = (_corner ? (_corner->sizeHint().width() + spacing()) : 0); int x = _editor->x(); if(x < ti_w) x = ti_w; // int b2x = x - (_button2 ? (_button2->sizeHint().width() + spacing()) : 0); int b2x = x - (_button2 ? (_button2->width() + spacing()) : 0); if(b2x < 0) b2x = 0; if(b2x > rect.width() - (_sb->minimumSizeHint().width() + corner_w)) b2x = rect.width() - (_sb->minimumSizeHint().width() + corner_w); //fprintf(stderr, "spacing:%d x:%d button minimumSizeHint width:%d button sizeHint width:%d\n", // spacing(), x, _button1->minimumSizeHint().width(), _button1->sizeHint().width()); //int b1x = b2x - (_button1 ? (_button1->sizeHint().width() + spacing()) : 0); int b1x = b2x - (_button1 ? (_button1->width() + spacing()) : 0); if(b1x < 0) b1x = 0; if(b1x > rect.width() - (_sb->minimumSizeHint().width() + corner_w)) b1x = rect.width() - (_sb->minimumSizeHint().width() + corner_w); if(_button1Li) _button1Li->setGeometry( QRect( b1x, rect.y(), _button1->width() + spacing(), rect.height() )); if(_button2Li) _button2Li->setGeometry( QRect( b2x, rect.y(), _button2->width() + spacing(), rect.height() )); if(_editor->width() > 0) { _sb->setVisible(true); int new_w = rect.width() - x - corner_w; if(new_w < _sb->minimumSizeHint().width() + corner_w) { new_w = _sb->minimumSizeHint().width() + corner_w; x = rect.width() - new_w; } QRect r(x, rect.y(), new_w, rect.height()); _sbLi->setGeometry(r); _spacerLi->setGeometry(QRect(0, rect.y(), b1x, rect.height())); } else { _sb->setVisible(false); _spacerLi->setGeometry(QRect(0, rect.y(), b1x, rect.height())); } if(_cornerLi) _cornerLi->setGeometry( QRect( rect.width() - _corner->sizeHint().width(), rect.y(), _corner->sizeHint().width(), rect.height() )); } } // namespace MusEGui muse-4.0.0/src/muse/midi_editor_layout.h000066400000000000000000000057021404105311000202530ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // midi_editor_layout.h // (C) Copyright 2020 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDI_EDITOR_LAYOUT_H__ #define __MIDI_EDITOR_LAYOUT_H__ #include #include #include #include #include namespace MusEGui { //--------------------------------------------------------- // MidiEditorCanvasLayout // For laying out a canvas as a last splitter widget and // automatically adjusting the width of its corresponding // horizontal scrollbar which is in another layout. //--------------------------------------------------------- class MidiEditorCanvasLayout : public QGridLayout { Q_OBJECT QHBoxLayout* _hBox; public: MidiEditorCanvasLayout(QWidget *parent, QHBoxLayout* hBox = nullptr) : QGridLayout(parent), _hBox(hBox) { }; QHBoxLayout* hBox() { return _hBox; } void setHBox(QHBoxLayout* l) { _hBox = l; } virtual void setGeometry(const QRect &rect); }; //--------------------------------------------------------- // MidiEditorHScrollLayout // For laying out the bottom buttons and hscroll in the arranger. //--------------------------------------------------------- class MidiEditorHScrollLayout : public QHBoxLayout { Q_OBJECT QWidget* _button1; QWidget* _button2; QWidget* _sb; QWidget* _corner; // This is not actually in the layout, but used anyway. QWidget* _editor; QWidgetItem* _button1Li; QWidgetItem* _button2Li; QSpacerItem* _spacerLi; QWidgetItem* _sbLi; QWidgetItem* _cornerLi; public: MidiEditorHScrollLayout(QWidget *parent, QWidget* button1, QWidget* button2, QWidget* sb, QWidget* corner = nullptr, QWidget* editor = nullptr); QWidget* editor() { return _editor; } void setEditor(QWidget* w) { _editor = w; } virtual void setGeometry(const QRect &rect); }; } #endif muse-4.0.0/src/muse/midictrl.cpp000066400000000000000000001115541404105311000165330ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midictrl.cpp,v 1.17.2.10 2009/06/10 00:34:59 terminator356 Exp $ // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include "globaldefs.h" #include "midictrl.h" #include "globals.h" #include "audio.h" #include "midi_consts.h" #include "midiport.h" #include "minstrument.h" #include "track.h" #include "mpevent.h" namespace MusECore { enum { COL_NAME = 0, COL_TYPE, COL_HNUM, COL_LNUM, COL_MIN, COL_MAX }; MidiControllerList defaultMidiController; // // some global controller which are always available: // // Zero note on vel is not allowed now. MidiController veloCtrl("Velocity", CTRL_VELOCITY, 1, 127, 0, 0); MidiController pitchCtrl("PitchBend", CTRL_PITCH, -8192, +8191, 0, 0); MidiController programCtrl("Program", CTRL_PROGRAM, 0, 0xffffff, 0, 0); MidiController mastervolCtrl("MasterVolume", CTRL_MASTER_VOLUME, 0, 0x3fff, 0x3000, 0x3000); MidiController volumeCtrl("MainVolume", CTRL_VOLUME, 0, 127, 100, 100); MidiController panCtrl("Pan", CTRL_PANPOT, -64, 63, 0, 0); MidiController reverbSendCtrl("ReverbSend", CTRL_REVERB_SEND, 0, 127, 0, 0); MidiController chorusSendCtrl("ChorusSend", CTRL_CHORUS_SEND, 0, 127, 0, 0); MidiController variationSendCtrl("VariationSend", CTRL_VARIATION_SEND, 0, 127, 0, 0); //--------------------------------------------------------- // initMidiController //--------------------------------------------------------- void initMidiController() { defaultMidiController.add(&veloCtrl); defaultMidiController.add(&pitchCtrl); defaultMidiController.add(&programCtrl); defaultMidiController.add(&mastervolCtrl); defaultMidiController.add(&volumeCtrl); defaultMidiController.add(&panCtrl); defaultMidiController.add(&reverbSendCtrl); defaultMidiController.add(&chorusSendCtrl); defaultMidiController.add(&variationSendCtrl); } //--------------------------------------------------------- // MidiCtrlValList //--------------------------------------------------------- MidiCtrlValList::MidiCtrlValList(int c) { ctrlNum = c; _hwVal = _lastValidHWVal = _lastValidByte2 = _lastValidByte1 = _lastValidByte0 = CTRL_VAL_UNKNOWN; } //--------------------------------------------------------- // MidiCtrlValListList //--------------------------------------------------------- MidiCtrlValListList::MidiCtrlValListList() { _RPN_Ctrls_Reserved = false; } // TODO: Finish copy constructor, but first MidiCtrlValList might need one too ? // MidiCtrlValListList::MidiCtrlValListList(const MidiCtrlValListList& mcvl) : std::map() // { // for(ciMidiCtrlValList i = mcvl.begin(); i != mcvl.end(); ++i) // { // MidiCtrlValList* mcl = i->second; // add(new MidiCtrlValList(*mcl)); // } // update_RPN_Ctrls_Reserved(); // } void MidiCtrlValListList::add(int channel, MidiCtrlValList* vl, bool update) { // TODO: If per-channel instruments are ever added to MusE, this routine would need changing. const int num = vl->num(); if(!_RPN_Ctrls_Reserved && update) { const bool isCtl7 = ((num & CTRL_OFFSET_MASK) == CTRL_7_OFFSET); const bool isCtl14 = ((num & CTRL_OFFSET_MASK) == CTRL_14_OFFSET); if(isCtl14 || isCtl7) { const int l = num & 0xff; if(l == CTRL_HDATA || l == CTRL_LDATA || l == CTRL_DATA_INC || l == CTRL_DATA_DEC || l == CTRL_HNRPN || l == CTRL_LNRPN || l == CTRL_HRPN || l == CTRL_LRPN) _RPN_Ctrls_Reserved = true; } if(!_RPN_Ctrls_Reserved && isCtl14) { const int h = (num >> 8) & 0xff; if(h == CTRL_HDATA || h == CTRL_LDATA || h == CTRL_DATA_INC || h == CTRL_DATA_DEC || h == CTRL_HNRPN || h == CTRL_LNRPN || h == CTRL_HRPN || h == CTRL_LRPN) _RPN_Ctrls_Reserved = true; } } insert(std::pair((channel << 24) + num, vl)); } void MidiCtrlValListList::del(iMidiCtrlValList ictl, bool update) { erase(ictl); if(update) update_RPN_Ctrls_Reserved(); } MidiCtrlValListList::size_type MidiCtrlValListList::del(int num, bool update) { MidiCtrlValListList::size_type res = erase(num); if(update) update_RPN_Ctrls_Reserved(); return res; } void MidiCtrlValListList::del(iMidiCtrlValList first, iMidiCtrlValList last, bool update) { erase(first, last); if(update) update_RPN_Ctrls_Reserved(); } void MidiCtrlValListList::clr() { clear(); update_RPN_Ctrls_Reserved(); } //--------------------------------------------------------- // clearDelete //--------------------------------------------------------- void MidiCtrlValListList::clearDelete(bool deleteLists) { for(iMidiCtrlValList imcvl = begin(); imcvl != end(); ++imcvl) { if(imcvl->second) { imcvl->second->clear(); if(deleteLists) delete imcvl->second; } } if(deleteLists) clr(); } //--------------------------------------------------------- // resetAllHwVals //--------------------------------------------------------- bool MidiCtrlValListList::resetAllHwVals(bool doLastHwValue) { bool changed = false; for(iMidiCtrlValList imcvl = begin(); imcvl != end(); ++imcvl) { if(imcvl->second) { if(imcvl->second->resetHwVal(doLastHwValue)) changed = true; } } return changed; } //--------------------------------------------------------- // searchControllers //--------------------------------------------------------- iMidiCtrlValList MidiCtrlValListList::searchControllers(int channel, int ctl) { const int type = ctl & CTRL_OFFSET_MASK; const unsigned ch_bits = (channel << 24); int n; // Looking for Controller7? See if any Controller14 contains the number and should be used instead. if(type == CTRL_7_OFFSET) { const int num = ctl & 0xff; for(iMidiCtrlValList imc = lower_bound(ch_bits | CTRL_14_OFFSET); imc != end(); ++imc) { // There is ->second->num(), but this is only way to get channel. n = imc->first; // Stop if we went beyond this channel number or this ctrl14 block. if((n & 0xff000000) != ch_bits || (n & CTRL_OFFSET_MASK) != CTRL_14_OFFSET) break; if(((n >> 8) & 0xff) == num || (n & 0xff) == num) return imc; } } // Looking for RPN? See if any RPN14 also uses the number and should be used instead. else if (type == CTRL_RPN_OFFSET) { const int num = ctl & 0xffff; for(iMidiCtrlValList imc = lower_bound(ch_bits | CTRL_RPN14_OFFSET); imc != end(); ++imc) { // There is ->second->num(), but this is only way to get channel. n = imc->first; // Stop if we went beyond this channel number or this RPN14 block. if((n & 0xff000000) != ch_bits || (n & CTRL_OFFSET_MASK) != CTRL_RPN14_OFFSET) break; if((n & 0xffff) == num) return imc; } } // Looking for NRPN? See if any NRPN14 also uses the number and should be used instead. else if (type == CTRL_NRPN_OFFSET) { const int num = ctl & 0xffff; for(iMidiCtrlValList imc = lower_bound(ch_bits | CTRL_NRPN14_OFFSET); imc != end(); ++imc) { // There is ->second->num(), but this is only way to get channel. n = imc->first; // Stop if we went beyond this channel number or this NRPN14 block. if((n & 0xff000000) != ch_bits || (n & CTRL_OFFSET_MASK) != CTRL_NRPN14_OFFSET) break; if((n & 0xffff) == num) return imc; } } // Looking for any other type? Do a regular find. return std::map >::find(ch_bits | ctl); } //--------------------------------------------------------- // update_RPN_Ctrls_Reserved // Manual check and update of the flag. For convenience, returns the flag. // Cost depends on types and number of list controllers, so it is good for deferring // an update until AFTER some lengthy list operation. JUST BE SURE to call this! //--------------------------------------------------------- bool MidiCtrlValListList::update_RPN_Ctrls_Reserved() { // TODO: If per-channel instruments are ever added to MusE, this routine would need changing. int num, h, l; for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { const unsigned ch_bits = (ch << 24); if(find(ch, CTRL_HDATA) != end() || find(ch, CTRL_LDATA) != end() || find(ch, CTRL_DATA_INC) != end() || find(ch, CTRL_DATA_DEC) != end() || find(ch, CTRL_HNRPN) != end() || find(ch, CTRL_LNRPN) != end() || find(ch, CTRL_HRPN) != end() || find(ch, CTRL_LRPN) != end()) { _RPN_Ctrls_Reserved = true; return true; } // Search: Get a head-start by taking lower bound. for(iMidiCtrlValList imc = lower_bound(ch_bits | CTRL_14_OFFSET); imc != end(); ++imc) { // There is ->second->num(), but this is only way to get channel. num = imc->first; // Stop if we went beyond this channel number or its ctrl14 block. if((num & 0xff000000) != ch_bits || (num & CTRL_OFFSET_MASK) != CTRL_14_OFFSET) { _RPN_Ctrls_Reserved = false; return false; } h = (num >> 8) & 0xff; l = num & 0xff; if(h == CTRL_HDATA || l == CTRL_HDATA || h == CTRL_LDATA || l == CTRL_LDATA || h == CTRL_DATA_INC || l == CTRL_DATA_INC || h == CTRL_DATA_DEC || l == CTRL_DATA_DEC || h == CTRL_HNRPN || l == CTRL_HNRPN || h == CTRL_LNRPN || l == CTRL_LNRPN || h == CTRL_HRPN || l == CTRL_HRPN || h == CTRL_LRPN || l == CTRL_LRPN) { _RPN_Ctrls_Reserved = true; return true; } } } _RPN_Ctrls_Reserved = false; return false; } //--------------------------------------------------------- // Catch all insert, erase, clear etc. //--------------------------------------------------------- MidiCtrlValListList& MidiCtrlValListList::operator=(const MidiCtrlValListList& cl) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiCtrlValListList::operator=\n"); #endif _RPN_Ctrls_Reserved = cl._RPN_Ctrls_Reserved; // Let map copy the items. std::map >::operator=(cl); return *this; } //========================================================= #ifdef _MIDI_CTRL_METHODS_DEBUG_ void MidiCtrlValListList::swap(MidiCtrlValListList& cl) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiCtrlValListList::swap\n"); #endif std::map >::swap(cl); } std::pair MidiCtrlValListList::insert(const std::pair& p) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiCtrlValListList::insert num:%d\n", p.second->num()); #endif std::pair res = std::map >::insert(p); return res; } iMidiCtrlValList MidiCtrlValListList::insert(iMidiCtrlValList ic, const std::pair& p) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiCtrlValListList::insertAt num:%d\n", p.second->num()); #endif iMidiCtrlValList res = std::map >::insert(ic, p); return res; } void MidiCtrlValListList::erase(iMidiCtrlValList ictl) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiCtrlValListList::erase iMidiCtrlValList num:%d\n", ictl->second->num()); #endif std::map >::erase(ictl); } MidiCtrlValListList::size_type MidiCtrlValListList::erase(int num) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiCtrlValListList::erase num:%d\n", num); #endif size_type res = std::map >::erase(num); return res; } void MidiCtrlValListList::erase(iMidiCtrlValList first, iMidiCtrlValList last) { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiCtrlValListList::erase range first num:%d second num:%d\n", first->second->num(), last->second->num()); #endif std::map >::erase(first, last); } void MidiCtrlValListList::clear() { #ifdef _MIDI_CTRL_DEBUG_ printf("MidiCtrlValListList::clear\n"); #endif std::map >::clear(); } #endif // ========================================================= bool MidiCtrlValList::resetHwVal(bool doLastHwValue) { bool changed = false; if(!hwValIsUnknown()) { _hwVal = CTRL_VAL_UNKNOWN; changed = true; } if(doLastHwValue) { if(!lastHwValIsUnknown()) changed = true; _lastValidHWVal = _lastValidByte2 = _lastValidByte1 = _lastValidByte0 = CTRL_VAL_UNKNOWN; } return changed; } //--------------------------------------------------------- // setHwVal // Returns false if value is already equal, true if value is changed. //--------------------------------------------------------- bool MidiCtrlValList::setHwVal(const double v) { const double r_v = muse_round2micro(v); if(_hwVal == r_v) return false; _hwVal = r_v; const int i_val = MidiController::dValToInt(_hwVal); if(!MidiController::iValIsUnknown(i_val)) { _lastValidHWVal = _hwVal; const int hb = (i_val >> 16) & 0xff; const int lb = (i_val >> 8) & 0xff; const int pr = i_val & 0xff; if(hb >= 0 && hb <= 127) _lastValidByte2 = hb; if(lb >= 0 && lb <= 127) _lastValidByte1 = lb; if(pr >= 0 && pr <= 127) _lastValidByte0 = pr; } return true; } //--------------------------------------------------------- // setHwVals // Sets current and last HW values. // Handy for forcing labels to show 'off' and knobs to show specific values // without having to send two messages. // Returns false if both values are already set, true if either value is changed. //--------------------------------------------------------- bool MidiCtrlValList::setHwVals(const double v, const double lastv) { const double r_v = muse_round2micro(v); const double r_lastv = muse_round2micro(lastv); if(_hwVal == r_v && _lastValidHWVal == r_lastv) return false; _hwVal = r_v; // Don't want to break our own rules - _lastValidHWVal can't be unknown while _hwVal is valid... // But _hwVal can be unknown while _lastValidHWVal is valid... if(MidiController::dValIsUnknown(r_lastv)) _lastValidHWVal = _hwVal; else _lastValidHWVal = r_lastv; const int i_lasthwval = MidiController::dValToInt(_lastValidHWVal); if(!MidiController::iValIsUnknown(i_lasthwval)) { const int hb = (i_lasthwval >> 16) & 0xff; const int lb = (i_lasthwval >> 8) & 0xff; const int pr = i_lasthwval & 0xff; if(hb >= 0 && hb <= 127) _lastValidByte2 = hb; if(lb >= 0 && lb <= 127) _lastValidByte1 = lb; if(pr >= 0 && pr <= 127) _lastValidByte0 = pr; } return true; } //--------------------------------------------------------- // partAtTick //--------------------------------------------------------- Part* MidiCtrlValList::partAtTick(unsigned int tick) const { // Determine (first) part at tick. Return 0 if none found. ciMidiCtrlVal i = lower_bound(tick); if (i == end() || i->first != tick) { if (i == begin()) return 0; --i; } return i->second.part; } //--------------------------------------------------------- // iValue //--------------------------------------------------------- iMidiCtrlVal MidiCtrlValList::iValue(unsigned int tick) { // Determine value at tick, using values stored by ANY part... iMidiCtrlVal i = lower_bound(tick); if (i == end() || i->first != tick) { if (i == begin()) return end(); --i; } return i; } //--------------------------------------------------------- // value //--------------------------------------------------------- int MidiCtrlValList::value(unsigned int tick) const { // Determine value at tick, using values stored by ANY part... ciMidiCtrlVal i = lower_bound(tick); if (i == end() || i->first != tick) { if (i == begin()) return CTRL_VAL_UNKNOWN; --i; } return i->second.val; } int MidiCtrlValList::value(unsigned int tick, Part* part) const { // Determine value at tick, using values stored by the SPECIFIC part... // Get the first value found with with a tick equal or greater than specified tick. ciMidiCtrlVal i = lower_bound(tick); // Since values from different parts can have the same tick, scan for part in all values at that tick. for(ciMidiCtrlVal j = i; j != end() && j->first == tick; ++j) { if(j->second.part == part) return j->second.val; } // Scan for part in all previous values, regardless of tick. while(i != begin()) { --i; if(i->second.part == part) return i->second.val; } // No previous values were found belonging to the specified part. return CTRL_VAL_UNKNOWN; } int MidiCtrlValList::visibleValue(unsigned int tick, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const { // Determine value at tick, using values stored by ANY part, // ignoring values that are OUTSIDE of their parts, or muted or off parts or tracks... // Get the first value found with with a tick equal or greater than specified tick. ciMidiCtrlVal i = lower_bound(tick); // Since values from different parts can have the same tick, scan for part in all values at that tick. for(ciMidiCtrlVal j = i; j != end() && j->first == tick; ++j) { const Part* part = j->second.part; // Ignore values that are outside of the part. if(tick < part->tick() || tick >= (part->tick() + part->lenTick())) continue; // Ignore if part or track is muted or off. if(!inclMutedParts && part->mute()) continue; const Track* track = part->track(); if(track && ((!inclMutedTracks && track->isMute()) || (!inclOffTracks && track->off()))) continue; return j->second.val; } // Scan for part in all previous values, regardless of tick. while(i != begin()) { --i; const Part* part = i->second.part; // Ignore values that are outside of the part. if(tick < part->tick() || tick >= (part->tick() + part->lenTick())) continue; // Ignore if part or track is muted or off. if(!inclMutedParts && part->mute()) continue; const Track* track = part->track(); if(track && ((!inclMutedTracks && track->isMute()) || (!inclOffTracks && track->off()))) continue; return i->second.val; } // No previous values were found belonging to the specified part. return CTRL_VAL_UNKNOWN; } int MidiCtrlValList::visibleValue(unsigned int tick, Part* part, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const { // Determine value at tick, using values stored by the SPECIFIC part, // ignoring values that are OUTSIDE of the part, or muted or off part or track... if((!inclMutedParts && part->mute()) || (part->track() && ((!inclMutedTracks && part->track()->isMute()) || (!inclOffTracks && part->track()->off())))) return CTRL_VAL_UNKNOWN; // Get the first value found with with a tick equal or greater than specified tick. ciMidiCtrlVal i = lower_bound(tick); // Ignore if part or track is muted or off. // Since values from different parts can have the same tick, scan for part in all values at that tick. for(ciMidiCtrlVal j = i; j != end() && j->first == tick; ++j) { if(j->second.part == part) { // Ignore values that are outside of the part. if(tick < part->tick() || tick >= (part->tick() + part->lenTick())) continue; return j->second.val; } } // Scan for part in all previous values, regardless of tick. while(i != begin()) { --i; if(i->second.part == part) return i->second.val; } // No previous values were found belonging to the specified part. return CTRL_VAL_UNKNOWN; } //--------------------------------------------------------- // add // return true if new controller value added // Accepts duplicate controller items at the same position, to accurately reflect // what is really in the event lists. // REMOVE Tim. Ctrl. Added comment. // NOTE: It is FORBIDDEN to have multiple controller events at the same time with the same controller number. // To preserve speed, we RELY on catching that at higher levels like the add event dialog or during song loading. // See detailed comments in EventList::add(). //--------------------------------------------------------- bool MidiCtrlValList::addMCtlVal(unsigned int tick, int val, Part* part) { insert(MidiCtrlValListInsertPair_t(tick, MidiCtrlVal(part, val))); return true; } //--------------------------------------------------------- // del //--------------------------------------------------------- void MidiCtrlValList::delMCtlVal(unsigned int tick, Part* part, int val) { iMidiCtrlVal e = findMCtlVal(tick, part, val); if (e == end()) { if(MusEGlobal::debugMsg) printf("MidiCtrlValList::delMCtlVal(%u): not found (size %zd)\n", tick, size()); return; } erase(e); } //--------------------------------------------------------- // find //--------------------------------------------------------- iMidiCtrlVal MidiCtrlValList::findMCtlVal(unsigned int tick, Part* part, int val) { MidiCtrlValRange range = equal_range(tick); for(iMidiCtrlVal i = range.first; i != range.second; ++i) { if(i->second.part == part && (val == -1 || i->second.val == val)) return i; } return end(); } //--------------------------------------------------------- // MidiEncoder //--------------------------------------------------------- MidiEncoder::MidiEncoder() { _curMode = EncIdle; // Try starting with ParamModeUnknown. Requires either RPN or NRPN params at least once. // Possibly may want to start with ParamModeRPN or ParamModeNRPN, // possibly make it depend on planned all-encompassing 'Optimizations' Setting, // and provide reset with 'Panic' button, just as it now resets output optimizations. _curParamMode = ParamModeUnknown; _curData = 0; _curTime = 0; _timer = 0; _curCtrl = 0; _nextCtrl = 0; _curRPNH = 0; _curRPNL = 0; _curNRPNL = 0; _curNRPNH = 0; } //--------------------------------------------------------- // encodeEvent //--------------------------------------------------------- void MidiEncoder::encodeEvent(const MidiRecordEvent& ev, int port, int channel) { const int type = ev.type(); if(type != ME_PITCHBEND && type != ME_AFTERTOUCH && type != ME_POLYAFTER && type != ME_PROGRAM && type != ME_CONTROLLER) return; MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiCtrlValListList* mcvll = mp->controller(); MidiInstrument* instr = mp->instrument(); MidiControllerList* mcl = instr->controller(); int num; int data; //int ctrlH; //int ctrlL; //const int ch_bits = channel << 24; if(_curMode != EncIdle) { if(_curMode == EncCtrl14) num = CTRL_14_OFFSET + ((_curCtrl << 8) | _nextCtrl); else if(_curMode == EncRPN14) num = CTRL_RPN14_OFFSET + ((_curRPNH << 8) | _curRPNL); else if(_curMode == EncNRPN14) num = CTRL_NRPN14_OFFSET + ((_curNRPNH << 8) | _curNRPNL); else { // Error _curMode = EncIdle; return; } iMidiCtrlValList imcvl = mcvll->find(channel, num); if(imcvl == mcvll->end()) { // Error, controller should exist _curMode = EncIdle; return; } MidiCtrlValList* mcvl = imcvl->second; if(type == ME_CONTROLLER && ev.dataA() == _nextCtrl) { data = (_curData << 7) | (ev.dataB() & 0x7f); mcvl->setHwVal(data); _curMode = EncIdle; return; } else { data = (_curData << 7) | (mcvl->hwVal() & 0x7f); mcvl->setHwVal(data); //_curMode = EncIdle; //return; } //return; } if(type == ME_CONTROLLER) { num = ev.dataA(); const int val = ev.dataB(); // Is it one of the 8 reserved GM controllers for RPN/NRPN? if(num == CTRL_HDATA || num == CTRL_LDATA || num == CTRL_DATA_INC || num == CTRL_DATA_DEC || num == CTRL_HNRPN || num == CTRL_LNRPN || num == CTRL_HRPN || num == CTRL_LRPN) { // Does the working controller list, and instrument, allow RPN/NRPN? // (If EITHER the working controller list or instrument defines ANY of these // 8 controllers as plain 7-bit, then we cannot accept this data as RPN/NRPN.) const bool rpn_reserved = mcvll->RPN_Ctrls_Reserved() | mcl->RPN_Ctrls_Reserved(); if(!rpn_reserved) { switch(num) { case CTRL_HDATA: { _curData = val; switch(_curParamMode) { case ParamModeUnknown: return; // Sorry, we shouldn't accept it without valid (N)RPN numbers. case ParamModeRPN: { const int param_num = (_curRPNH << 8) | _curRPNL; iMidiCtrlValList imcvl = mcvll->searchControllers(channel, CTRL_RPN_OFFSET | param_num); if(imcvl == mcvll->end()) { // Set mode, _curTime, and _timer to wait for next event. _curMode = EncDiscoverRPN; _curTime = MusEGlobal::audio->curFrame(); _timer = 0; return; } else if((imcvl->first & CTRL_OFFSET_MASK) == CTRL_RPN_OFFSET) { // Done. Take _curData and param_num and compose something to return, // and set the HWval... TODO _curMode = EncIdle; return; } else if((imcvl->first & CTRL_OFFSET_MASK) == CTRL_RPN14_OFFSET) { // Set mode, _curTime, and _timer to wait for next event. _curMode = EncRPN14; _curTime = MusEGlobal::audio->curFrame(); _timer = 0; return; } else { fprintf(stderr, "MidiEncoder::encodeEvent unknown type %d\n", imcvl->first & CTRL_OFFSET_MASK); return; } break; } case ParamModeNRPN: break; default: fprintf(stderr, "MidiEncoder::encodeEvent unknown ParamMode %d\n", _curParamMode); return; } break; } case CTRL_LDATA: break; case CTRL_DATA_INC: break; case CTRL_DATA_DEC: break; case CTRL_HRPN: _curRPNH = val; _curParamMode = ParamModeRPN; return; case CTRL_LRPN: _curRPNL = val; _curParamMode = ParamModeRPN; return; case CTRL_HNRPN: _curNRPNH = val; _curParamMode = ParamModeNRPN; return; case CTRL_LNRPN: _curNRPNL = val; _curParamMode = ParamModeNRPN; return; default: break; } } } // for(iMidiCtrlValList imcvl = mcvll->begin(); imcvl != mcvll->end(); ++imcvl) // { // if(((imcvl->first >> 24) & 0xf) != channel) // continue; // MidiCtrlValList* mcvl = imcvl->second; // } } _curMode = EncIdle; return; // if(_curMode != EncIdle) // { // if(_curMode == EncCtrl14 && type == ME_CONTROLLER && ev.dataA() == _nextCtrl) // { // num = CTRL_14_OFFSET + ((_curCtrl << 8) | (_nextCtrl & 0x7f)); // iMidiCtrlValList imcvl = mcvll->find(channel, num); // if(imcvl == mcvll->end()) // return; // Error, controller should exist // MidiCtrlValList* mcvl = imcvl->second; // data = (_curData << 7) | (ev.dataB() & 0x7f); // mcvl->setHwVal(data); // //_timer = 0; // _curMode = EncIdle; // return; // } // else if(_curMode == EncRPN14 && type == ME_CONTROLLER && ev.dataA() == CTRL_LDATA) // { // // } // } //mcvl->find(); // for(ciMidiCtrlValList imcvl = mcvl->begin(); imcvl != mcvl->end(); ++imcvl) // { // const int ch = imcvl->first >> 24; // if(ch != channel) // continue; // MidiCtrlValList* mcvl = imcvl->second; // num = mcvl->num(); // ctrlH = (num >> 8) & 0x7f; // ctrlL = num & 0xff; // if(type == ME_CONTROLLER) // { // const int ev_num = ev.dataA(); // if(num < CTRL_14_OFFSET) // 7-bit controller 0 - 0x10000 // { // if(ev_num == num) // { // // } // } // else if(num < CTRL_RPN_OFFSET) // 14-bit controller 0x10000 - 0x20000 // { // // } // } // } // int num; // // switch(type) // { // // TODO // case ME_PITCHBEND: // num = CTRL_PITCH; // break; // case ME_AFTERTOUCH: // num = CTRL_AFTERTOUCH; // break; // case ME_POLYAFTER: // num = CTRL_POLYAFTER | (ev.dataA() & 0x7f); // break; // case ME_PROGRAM: // num = CTRL_PROGRAM; // break; // // case ME_CONTROLLER: // { // //num = CTRL_; // } // break; // // default: // return; // } // if(instr) // { // int num; // int ctrlH; // int ctrlL; // MidiControllerList* mcl = instr->controller(); // MidiController* mc; // // if (_outputInstrument) { // MidiControllerList* mcl = _outputInstrument->controller(); // for (iMidiController i = mcl->begin(); i != mcl->end(); ++i) { // int cn = i->second->num(); // if (cn == num) // return i->second; // // wildcard? // if (i->second->isPerNoteController() && ((cn & ~0xff) == (num & ~0xff))) // return i->second; // } // } // // for (iMidiController i = defaultMidiController.begin(); i != defaultMidiController.end(); ++i) { // int cn = i->second->num(); // if (cn == num) // return i->second; // // wildcard? // if (i->second->isPerNoteController() && ((cn & ~0xff) == (num & ~0xff))) // return i->second; // } // // // QString name = midiCtrlName(num); // int min = 0; // int max = 127; // // MidiController::ControllerType t = midiControllerType(num); // switch (t) { // case MidiController::RPN: // case MidiController::NRPN: // case MidiController::Controller7: // case MidiController::PolyAftertouch: // case MidiController::Aftertouch: // max = 127; // break; // case MidiController::Controller14: // case MidiController::RPN14: // case MidiController::NRPN14: // max = 16383; // break; // case MidiController::Program: // max = 0xffffff; // break; // case MidiController::Pitch: // max = 8191; // min = -8192; // break; // case MidiController::Velo: // cannot happen // break; // } // MidiController* c = new MidiController(name, num, min, max, 0); // defaultMidiController.add(c); // return c; // // for(ciMidiController imc = mcl->begin(); imc != mcl->end(); ++imc) // { // mc = imc->second; // num = mc->num(); // ctrlH = (num >> 8) & 0x7f; // ctrlL = num & 0xff; // if(num < CTRL_14_OFFSET) // 7-bit controller 0 - 0x10000 // { // if(ctrlL == 0xff || ctrlL == a) // is_7bit = true; // // if(ctrlL == 0xff) // RPNH_reserved = RPNL_reserved = NRPNH_reserved = NRPNL_reserved = DATAH_reserved = DATAL_reserved = true; // else if(ctrlL == CTRL_HRPN) // RPNH_reserved = true; // else if(ctrlL == CTRL_LRPN) // RPNL_reserved = true; // else if(ctrlL == CTRL_HNRPN) // NRPNH_reserved = true; // else if(ctrlL == CTRL_LNRPN) // NRPNL_reserved = true; // else if(ctrlL == CTRL_HDATA) // DATAH_reserved = true; // else if(ctrlL == CTRL_LDATA) // DATAL_reserved = true; // } // else if(num < CTRL_RPN_OFFSET) // 14-bit controller 0x10000 - 0x20000 // { // if(ctrlH == a) // { // //is_14bitH = true; // is_14bit = true; // if(!instr->waitForLSB()) // { // MidiRecordEvent single_ev; // single_ev.setChannel(chn); // single_ev.setType(ME_CONTROLLER); // single_ev.setA(CTRL_14_OFFSET + (a << 8) + ctrlL); // single_ev.setB((b << 7) + mcs->ctrls[ctrlL]); // mdev->recordEvent(single_ev); // } // } // if(ctrlL == 0xff || ctrlL == a) // { // //is_14bitL = true; // is_14bit = true; // MidiRecordEvent single_ev; // single_ev.setChannel(chn); // single_ev.setType(ME_CONTROLLER); // single_ev.setA(CTRL_14_OFFSET + (ctrlH << 8) + a); // single_ev.setB((mcs->ctrls[ctrlH] << 7) + b); // mdev->recordEvent(single_ev); // } // // if(ctrlL == 0xff) // RPNH_reserved = RPNL_reserved = NRPNH_reserved = NRPNL_reserved = DATAH_reserved = DATAL_reserved = true; // else if(ctrlL == CTRL_HRPN || ctrlH == CTRL_HRPN) // RPNH_reserved = true; // else if(ctrlL == CTRL_LRPN || ctrlH == CTRL_LRPN) // RPNL_reserved = true; // else if(ctrlL == CTRL_HNRPN || ctrlH == CTRL_HNRPN) // NRPNH_reserved = true; // else if(ctrlL == CTRL_LNRPN || ctrlH == CTRL_LNRPN) // NRPNL_reserved = true; // else if(ctrlL == CTRL_HDATA || ctrlH == CTRL_HDATA) // DATAH_reserved = true; // else if(ctrlL == CTRL_LDATA || ctrlH == CTRL_LDATA) // DATAL_reserved = true; // } // else if(num < CTRL_NRPN_OFFSET) // RPN 7-Bit Controller 0x20000 - 0x30000 // { // //if(a == CTRL_HDATA && mcs->ctrls[CTRL_HRPN] < 128 && mcs->ctrls[CTRL_LRPN] < 128) // if(a == CTRL_HDATA && !mcs->modeIsNRP && ctrlH == mcs->ctrls[CTRL_HRPN] && (ctrlL == 0xff || ctrlL == mcs->ctrls[CTRL_LRPN])) // is_RPN = true; // } // else if(num < CTRL_INTERNAL_OFFSET) // NRPN 7-Bit Controller 0x30000 - 0x40000 // { // //if(a == CTRL_HDATA && mcs->ctrls[CTRL_HNRPN] < 128 && mcs->ctrls[CTRL_LNRPN] < 128) // if(a == CTRL_HDATA && mcs->modeIsNRP && ctrlH == mcs->ctrls[CTRL_HNRPN] && (ctrlL == 0xff || ctrlL == mcs->ctrls[CTRL_LNRPN])) // is_NRPN = true; // } // else if(num < CTRL_RPN14_OFFSET) // Unaccounted for internal controller 0x40000 - 0x50000 // continue; // else if(num < CTRL_NRPN14_OFFSET) // RPN14 Controller 0x50000 - 0x60000 // { // //if(a == CTRL_LDATA && mcs->ctrls[CTRL_HRPN] < 128 && mcs->ctrls[CTRL_LRPN] < 128) // if(a == CTRL_LDATA && !mcs->modeIsNRP && ctrlH == mcs->ctrls[CTRL_HRPN] && (ctrlL == 0xff || ctrlL == mcs->ctrls[CTRL_LRPN])) // is_RPN14 = true; // } // else if(num < CTRL_NONE_OFFSET) // NRPN14 Controller 0x60000 - 0x70000 // { // //if(a == CTRL_LDATA && mcs->ctrls[CTRL_HNRPN] < 128 && mcs->ctrls[CTRL_LNRPN] < 128) // if(a == CTRL_LDATA && mcs->modeIsNRP && ctrlH == mcs->ctrls[CTRL_HNRPN] && (ctrlL == 0xff || ctrlL == mcs->ctrls[CTRL_LNRPN])) // is_NRPN14 = true; // } // } // } } //--------------------------------------------------------- // endCycle //--------------------------------------------------------- void MidiEncoder::endCycle(unsigned int /*blockSize*/) { // TODO } } // namespace MusECore muse-4.0.0/src/muse/midictrl.h000066400000000000000000000305301404105311000161720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midictrl.h,v 1.16.2.8 2009/11/25 09:09:43 terminator356 Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDICTRL_H__ #define __MIDICTRL_H__ #include #include "midi_controller.h" //#define _MIDI_CTRL_DEBUG_ // For finding exactly who may be calling insert, erase clear etc. in // the controller list classes. (KDevelop 'Find uses'.) //#define _MIDI_CTRL_METHODS_DEBUG_ namespace MusECore { class Part; class MidiRecordEvent; struct MidiCtrlVal { // The part containing the event which this value came from. Used for searching and deleting. Part* part; // The stored value. int val; MidiCtrlVal(Part* p, int v) { part = p; val = v; } bool operator==(const MidiCtrlVal& mcv) { return part == mcv.part && val == mcv.val; } }; //--------------------------------------------------------- // MidiCtrlValList // arrange controller events of a specific type in a // list for easy retrieval //--------------------------------------------------------- typedef std::pair MidiCtrlValListInsertPair_t; typedef std::multimap > MidiCtrlValList_t; class MidiCtrlValList : public MidiCtrlValList_t { // The controller number. int ctrlNum; // Current set value in midi hardware. Can be CTRL_VAL_UNKNOWN. double _hwVal; // The last value that was not CTRL_VAL_UNKNOWN. Can still be CTRL_VAL_UNKNOWN (typically at startup). // Note that in the case of PROGRAM for example, HBank/LBank bytes can still be 0xff (OFF). double _lastValidHWVal; // The last byte values that were not CTRL_VAL_UNKNOWN or 0xff (Off). // Can never be 0xff (OFF), but can still be CTRL_VAL_UNKNOWN (typically at startup). // Special for example PROGRAM controller, has 3 separate values: HBank, LBank and Program. int _lastValidByte2; int _lastValidByte1; int _lastValidByte0; // Hide built-in finds. iterator find(const unsigned int&) { return end(); }; const_iterator find(const unsigned int&) const { return end(); }; public: MidiCtrlValList(int num); Part* partAtTick(unsigned int tick) const; // Determine value at tick, using values stored by ANY part. iterator iValue(unsigned int tick); // Determine value at tick, using values stored by ANY part. int value(unsigned int tick) const; // Determine value at tick, using values stored by the SPECIFIC part. int value(unsigned int tick, Part* part) const; // Determine value at tick, using values stored by ANY part, // ignoring values that are OUTSIDE of their parts, or muted or off parts or tracks. int visibleValue(unsigned int tick, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const; // Determine value at tick, using values stored by the SPECIFIC part, // ignoring values that are OUTSIDE of the part, or muted or off part or track. int visibleValue(unsigned int tick, Part* part, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const; // Adds the new value. Accepts duplicate controller items at the same position, to accurately reflect // what is really in the event lists. // REMOVE Tim. Ctrl. Changed comment. // // Mostly for the purpose of dragging and dropping controller events and allowing them to be // // on top of each other TEMPORARILY. // // But ultimately once dropping is finished there must be only ONE value per controller // // per position per part. // NOTE: It is FORBIDDEN to have multiple controller events at the same time with the same controller number. // To preserve speed, we RELY on catching that at higher levels like the add event dialog or during song loading. // See detailed comments in EventList::add(). bool addMCtlVal(unsigned int tick, int value, Part* part); // If val is not -1 it will search for that value. void delMCtlVal(unsigned int tick, Part* part, int val/* = -1*/); // If val is not -1 it will search for that value. iterator findMCtlVal(unsigned int tick, Part* part, int val/* = -1*/); // Current set value in midi hardware. Can be CTRL_VAL_UNKNOWN. inline int hwVal() const { return MidiController::dValToInt(_hwVal); } double hwDVal() const { return _hwVal; } inline bool hwValIsUnknown() const { return MidiController::iValIsUnknown(MidiController::dValToInt(_hwVal)); } // Resets the current, and optionally the last, hardware value to CTRL_VAL_UNKNOWN. // Returns true if either value was changed. bool resetHwVal(bool doLastHwValue = false); // Set current value in midi hardware. Can be CTRL_VAL_UNKNOWN. // Returns false if value is already equal, true if value is changed. bool setHwVal(const double v); // Sets current and last HW values. // Handy for forcing labels to show 'off' and knobs to show specific values // without having to send two messages. // Returns false if both values are already set, true if either value is changed. bool setHwVals(const double v, const double lastv); // The controller number. int num() const { return ctrlNum; } // The last value that was not CTRL_VAL_UNKNOWN. Can still be CTRL_VAL_UNKNOWN (typically at startup). // Note that in the case of PROGRAM for example, HBank/LBank bytes can still be 0xff (OFF). inline int lastValidHWVal() const { return MidiController::dValToInt(_lastValidHWVal); } double lastValidHWDVal() const { return _lastValidHWVal; } inline bool lastHwValIsUnknown() const { return MidiController::iValIsUnknown(MidiController::dValToInt(_lastValidHWVal)); } // The last byte values that were not CTRL_VAL_UNKNOWN or 0xff (Off). // Can never be 0xff (OFF), but can still be CTRL_VAL_UNKNOWN (typically at startup). // Special for example PROGRAM controller, has 3 separate values: HBank, LBank and Program. int lastValidByte2() const { return _lastValidByte2; } int lastValidByte1() const { return _lastValidByte1; } int lastValidByte0() const { return _lastValidByte0; } }; typedef MidiCtrlValList::iterator iMidiCtrlVal; typedef MidiCtrlValList::const_iterator ciMidiCtrlVal; typedef std::pair MidiCtrlValRange; //--------------------------------------------------------- // MidiCtrlValListList // List of midi controller value lists. // This list represents the controller state of a // midi port. // index = (channelNumber << 24) + ctrlNumber //--------------------------------------------------------- typedef std::map > MidiCtrlValListList_t; typedef MidiCtrlValListList_t::iterator iMidiCtrlValList; typedef MidiCtrlValListList_t::const_iterator ciMidiCtrlValList; class MidiCtrlValListList : public MidiCtrlValListList_t { bool _RPN_Ctrls_Reserved; public: MidiCtrlValListList(); //MidiCtrlValListList(const MidiCtrlValListList&); // TODO iterator find(int channel, int ctrl) { return std::map >::find((channel << 24) + ctrl); } const_iterator find(int channel, int ctrl) const { return ((const MidiCtrlValListList_t*)this)->find((channel << 24) + ctrl); } void clearDelete(bool deleteLists); // Like 'find', finds a controller given fully qualified type + number. // But it returns controller with highest priority if multiple controllers use the // given number such as {Controller7, num = 0x55} + {Controller14, num = 0x5544}. // Note if given number is one of the eight reserved General Midi (N)RPN controllers, // this will only return Controller7 or Controller14, not anything (N)RPN related. // That is, it will not 'encode' (N)RPNs. Use a MidiEncoder instance for that. iMidiCtrlValList searchControllers(int channel, int ctl); // Returns true if any of the EIGHT reserved General Midi (N)RPN control numbers are ALREADY // defined as Controller7 or part of Controller14. Cached, for speed. // Used (at least) by midi input encoders to quickly arbitrate new input. bool RPN_Ctrls_Reserved() { return _RPN_Ctrls_Reserved; } // Manual check and update of the flag. For convenience, returns the flag. // Cost depends on types and number of list controllers, so it is good for deferring // an update until AFTER some lengthy list operation. JUST BE SURE to call this! bool update_RPN_Ctrls_Reserved(); // NOTICE: If update is false or these are bypassed by using insert, erase, clear etc. for speed, // then BE SURE to call update_RPN_Ctrls_Reserved() later. void add(int channel, MidiCtrlValList* vl, bool update = true); void del(iMidiCtrlValList ictl, bool update = true); size_type del(int num, bool update = true); void del(iMidiCtrlValList first, iMidiCtrlValList last, bool update = true); void clr(); // Convenience method: Resets all current, and optionally the last, hardware controller values to CTRL_VAL_UNKNOWN. // Equivalent to calling resetAllHwVal() on each MidiCtrlValList. // Returns true if either value was changed in any controller. bool resetAllHwVals(bool doLastHwValue); #ifdef _MIDI_CTRL_METHODS_DEBUG_ // Need to catch all insert, erase, clear etc... void swap(MidiCtrlValListList&); std::pair insert(const std::pair& p); iMidiCtrlValList insert(iMidiCtrlValList ic, const std::pair& p); void erase(iMidiCtrlValList ictl); size_type erase(int num); void erase(iMidiCtrlValList first, iMidiCtrlValList last); void clear(); #endif // Some IDEs won't "Find uses" of operators. So, no choice but to trust always catching it here. MidiCtrlValListList& operator=(const MidiCtrlValListList&); }; //--------------------------------------------------------- // MidiEncoder //--------------------------------------------------------- class MidiEncoder { public: enum Mode { EncIdle, EncCtrl14, EncDiscoverRPN, EncDiscoverNRPN, EncRPN, EncNRPN, EncRPN14, EncNRPN14 }; enum ParamMode { ParamModeUnknown, ParamModeRPN, ParamModeNRPN }; private: Mode _curMode; ParamMode _curParamMode; unsigned int _timer; // unsigned char _curCtrl; // Ctl num of first event unsigned char _curData; // Data of first event unsigned int _curTime; // Time of first event unsigned char _nextCtrl; // Expected next event ctl num (for ctrl14 only) unsigned char _curRPNH; unsigned char _curRPNL; unsigned char _curNRPNH; unsigned char _curNRPNL; public: MidiEncoder(); void encodeEvent(const MidiRecordEvent& ev, int port, int channel); void endCycle(unsigned int blockSize); }; extern MidiControllerList defaultMidiController; extern void initMidiController(); extern MidiController veloCtrl; extern MidiController pitchCtrl; extern MidiController programCtrl; extern MidiController mastervolCtrl; extern MidiController volumeCtrl; extern MidiController panCtrl; extern MidiController reverbSendCtrl; extern MidiController chorusSendCtrl; extern MidiController variationSendCtrl; } // namespace MusECore #endif muse-4.0.0/src/muse/mididev.cpp000066400000000000000000001655011404105311000163460ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mididev.cpp,v 1.10.2.6 2009/11/05 03:14:35 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011, 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 "midictrl.h" #include "song.h" #include "midi_consts.h" #include "midiport.h" #include "mididev.h" #include "config.h" #include "gconfig.h" #include "globals.h" #include "audio.h" #include "audiodev.h" #include "midiseq.h" #include "midiitransform.h" #include "mitplugin.h" #include "part.h" #include "drummap.h" #include "helper.h" #include "ticksynth.h" // Forwards from header: #include "xml.h" // Undefine if and when multiple output routes are added to midi tracks. #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // For debugging output: Uncomment the fprintf section. //#define DEBUG_MIDI_DEVICE(dev, format, args...) //fprintf(dev, format, ##args); namespace MusEGlobal { MusECore::MidiDeviceList midiDevices; } namespace MusECore { #ifdef MIDI_DRIVER_MIDI_SERIAL extern void initMidiSerial(); #endif extern bool initMidiAlsa(); extern bool initMidiJack(); extern void processMidiInputTransformPlugins(MEvent&); // Static. const int MidiDevice::extClockHistoryCapacity = 1024; //--------------------------------------------------------- // initMidiDevices //--------------------------------------------------------- void initMidiDevices() { #ifdef MIDI_DRIVER_MIDI_SERIAL initMidiSerial(); #endif #ifdef ALSA_SUPPORT if(MusEGlobal::config.enableAlsaMidiDriver || // User setting MusEGlobal::useAlsaWithJack || // Command line override MusEGlobal::audioDevice->deviceType() != AudioDevice::JACK_AUDIO) // Jack not running { if(initMidiAlsa()) { QMessageBox::critical(NULL, "MusE fatal error.", "MusE failed to initialize the\n" "Alsa midi subsystem, check\n" "your configuration."); exit(-1); } } #endif if(initMidiJack()) { QMessageBox::critical(NULL, "MusE fatal error.", "MusE failed to initialize the\n" "Jack midi subsystem, check\n" "your configuration."); exit(-1); } } //--------------------------------------------------------- // init //--------------------------------------------------------- void MidiDevice::init() { _extClockHistoryFifo = new LockFreeBuffer(extClockHistoryCapacity); // TODO: Scale these according to the current audio segment size. _playbackEventBuffers = new LockFreeMPSCRingBuffer(1024); _userEventBuffers = new LockFreeMPSCRingBuffer(1024); _sysExOutDelayedEvents = new std::vector; // Initially reserve a fair number of items to hold potentially a lot // of messages when the sysex processor is busy (in the Sending state). _sysExOutDelayedEvents->reserve(1024); _stopFlag.store(false); _state = QString("Closed"); _readEnable = false; _writeEnable = false; _rwFlags = 3; _openFlags = 3; _port = -1; } //--------------------------------------------------------- // MidiDevice //--------------------------------------------------------- MidiDevice::MidiDevice() { for(unsigned int i = 0; i < MusECore::MUSE_MIDI_CHANNELS + 1; ++i) _tmpRecordCount[i] = 0; _sysexFIFOProcessed = false; init(); } MidiDevice::MidiDevice(const QString& n) : _name(n) { for(unsigned int i = 0; i < MusECore::MUSE_MIDI_CHANNELS + 1; ++i) _tmpRecordCount[i] = 0; _sysexFIFOProcessed = false; init(); } MidiDevice::~MidiDevice() { if(_sysExOutDelayedEvents) delete _sysExOutDelayedEvents; if(_extClockHistoryFifo) delete _extClockHistoryFifo; if(_userEventBuffers) delete _userEventBuffers; if(_playbackEventBuffers) delete _playbackEventBuffers; } QString MidiDevice::deviceTypeString() const { switch(deviceType()) { case ALSA_MIDI: return "ALSA"; case JACK_MIDI: return "JACK"; case SYNTH_MIDI: { const SynthI* s = dynamic_cast(this); if(s && s->synth()) return MusECore::synthType2String(s->synth()->synthType()); else return "SYNTH"; } } return "UNKNOWN"; } void MidiDevice::setPort(int p) { _port = p; if(_port != -1) MusEGlobal::midiPorts[_port].clearInitSent(); } //--------------------------------------------------------- // filterEvent // return true if event filtered //--------------------------------------------------------- bool filterEvent(const MEvent& event, int type, bool thru) { switch(event.type()) { case ME_NOTEON: case ME_NOTEOFF: if (type & MIDI_FILTER_NOTEON) return true; break; case ME_POLYAFTER: if (type & MIDI_FILTER_POLYP) return true; break; case ME_CONTROLLER: if (type & MIDI_FILTER_CTRL) return true; if (!thru && (MusEGlobal::midiFilterCtrl1 == event.dataA() || MusEGlobal::midiFilterCtrl2 == event.dataA() || MusEGlobal::midiFilterCtrl3 == event.dataA() || MusEGlobal::midiFilterCtrl4 == event.dataA())) { return true; } break; case ME_PROGRAM: if (type & MIDI_FILTER_PROGRAM) return true; break; case ME_AFTERTOUCH: if (type & MIDI_FILTER_AT) return true; break; case ME_PITCHBEND: if (type & MIDI_FILTER_PITCH) return true; break; case ME_SYSEX: if (type & MIDI_FILTER_SYSEX) return true; break; default: break; } return false; } //--------------------------------------------------------- // afterProcess // clear all recorded events after a process cycle //--------------------------------------------------------- void MidiDevice::afterProcess() { for(unsigned int i = 0; i < MusECore::MUSE_MIDI_CHANNELS + 1; ++i) { while (_tmpRecordCount[i]--) _recordFifo[i].remove(); } } //--------------------------------------------------------- // beforeProcess // "freeze" fifo for this process cycle //--------------------------------------------------------- void MidiDevice::beforeProcess() { for(unsigned int i = 0; i < MusECore::MUSE_MIDI_CHANNELS + 1; ++i) _tmpRecordCount[i] = _recordFifo[i].getSize(); // Reset this. _sysexFIFOProcessed = false; } //--------------------------------------------------------- // midiClockInput // Midi clock (24 ticks / quarter note) //--------------------------------------------------------- void MidiDevice::midiClockInput(unsigned int frame) { // Put a midi clock record event into the clock history fifo. Ignore port and channel. // Timestamp with the current frame. const ExtMidiClock ext_clk = MusEGlobal::midiSyncContainer.midiClockInput(midiPort(), frame); if(ext_clk.isValid() && extClockHistory()) extClockHistory()->put(ext_clk); } //--------------------------------------------------------- // recordEvent //--------------------------------------------------------- void MidiDevice::recordEvent(MidiRecordEvent& event) { if(MusEGlobal::audio->isPlaying()) event.setLoopNum(MusEGlobal::audio->loopCount()); if (MusEGlobal::midiInputTrace) { fprintf(stderr, "MidiInput: "); dumpMPEvent(&event); } int typ = event.type(); if(_port != -1) { int idin = MusEGlobal::midiPorts[_port].syncInfo().idIn(); //--------------------------------------------------- // filter some SYSEX events //--------------------------------------------------- if (typ == ME_SYSEX) { const unsigned char* p = event.data(); int n = event.len(); if (n >= 4) { if ((p[0] == 0x7f) && ((p[1] == 0x7f) || (idin == 0x7f) || (p[1] == idin))) { if (p[2] == 0x06) { MusEGlobal::midiSyncContainer.mmcInput(_port, p, n); return; } if (p[2] == 0x01) { MusEGlobal::midiSyncContainer.mtcInputFull(_port, p, n); return; } } else if (p[0] == 0x7e) { MusEGlobal::midiSyncContainer.nonRealtimeSystemSysex(_port, p, n); return; } } } else // Trigger general activity indicator detector. Sysex has no channel, don't trigger. MusEGlobal::midiPorts[_port].syncInfo().trigActDetect(event.channel()); } // // process midi event input filtering and // transformation // processMidiInputTransformPlugins(event); if (filterEvent(event, MusEGlobal::midiRecordType, false)) return; if (!applyMidiInputTransformation(event)) { if (MusEGlobal::midiInputTrace) fprintf(stderr, " midi input transformation: event filtered\n"); return; } // // transfer noteOn and Off events to gui for step recording and keyboard // remote control (changed by flo93: added noteOff-events) // if (typ == ME_NOTEON) { int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff); MusEGlobal::song->putEvent(pv); } else if (typ == ME_NOTEOFF) { int pv = ((event.dataA() & 0xff)<<8) + (0x00); //send an event with velo=0 MusEGlobal::song->putEvent(pv); } else if (MusEGlobal::rcEnableCC && typ == ME_CONTROLLER) { char cc = static_cast(event.dataA() & 0xff); printf("*** Input CC: %d\n", cc); MusEGlobal::song->putEventCC(cc); } // Do not bother recording if it is NOT actually being used by a port. // Because from this point on, process handles things, by selected port. if(_port == -1) return; // Split the events up into channel fifos. Special 'channel' number 17 for sysex events. unsigned int ch = (typ == ME_SYSEX)? MusECore::MUSE_MIDI_CHANNELS : event.channel(); if(_recordFifo[ch].put(event)) fprintf(stderr, "MidiDevice::recordEvent: fifo channel %d overflow\n", ch); } //--------------------------------------------------------- // find //--------------------------------------------------------- MidiDevice* MidiDeviceList::find(const QString& s, int typeHint) { for (iMidiDevice i = begin(); i != end(); ++i) if( (typeHint == -1 || typeHint == (*i)->deviceType()) && ((*i)->name() == s) ) return *i; return 0; } //--------------------------------------------------------- // add //--------------------------------------------------------- void MidiDeviceList::add(MidiDevice* dev) { bool gotUniqueName=false; int increment = 0; const QString origname = dev->name(); QString newName = origname; while (!gotUniqueName) { gotUniqueName = true; // check if the name's been taken for (iMidiDevice i = begin(); i != end(); ++i) { const QString s = (*i)->name(); if (s == newName) { newName = origname + QString("_%1").arg(++increment); gotUniqueName = false; } } } if(origname != newName) dev->setName(newName); push_back(dev); } //--------------------------------------------------------- // remove //--------------------------------------------------------- void MidiDeviceList::remove(MidiDevice* dev) { for (iMidiDevice i = begin(); i != end(); ++i) { if (*i == dev) { erase(i); break; } } } //--------------------------------------------------------- // resetCurParamNums // Reset output channel's current parameter numbers to -1. // All channels if chan = -1. //--------------------------------------------------------- void MidiDevice::resetCurOutParamNums(int chan) { if(chan == -1) { for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) _curOutParamNums[i].resetParamNums(); return; } _curOutParamNums[chan].resetParamNums(); } //--------------------------------------------------------- // putEvent // return true if event cannot be delivered //--------------------------------------------------------- bool MidiDevice::putEvent(const MidiPlayEvent& ev, LatencyType latencyType, EventBufferType bufferType) { // TODO: Decide whether we want the driver cached values always updated like this, // even if not writeable or if error. // if(!_writeEnable) // return true; // Automatically shift the time forward if specified. MidiPlayEvent fin_ev = ev; switch(latencyType) { case NotLate: break; case Late: fin_ev.setTime(fin_ev.time() + pbForwardShiftFrames()); break; } //DEBUG_MIDI_DEVICE(stderr, "MidiDevice::putUserEvent devType:%d time:%d type:%d ch:%d A:%d B:%d\n", // deviceType(), fin_ev.time(), fin_ev.type(), fin_ev.channel(), fin_ev.dataA(), fin_ev.dataB()); if (MusEGlobal::midiOutputTrace) { fprintf(stderr, "MidiDevice::putEvent: %s: <%s>: ", deviceTypeString().toLatin1().constData(), name().toLatin1().constData()); dumpMPEvent(&fin_ev); } bool rv = true; switch(bufferType) { case PlaybackBuffer: rv = !_playbackEventBuffers->put(fin_ev); break; case UserBuffer: rv = !_userEventBuffers->put(fin_ev); break; } if(rv) fprintf(stderr, "MidiDevice::putEvent: Error: Device buffer overflow. bufferType:%d\n", bufferType); return rv; } //--------------------------------------------------------- // processStuckNotes // To be called by audio thread only. //--------------------------------------------------------- void MidiDevice::processStuckNotes() { // Must be playing for valid nextTickPos, right? But wasn't checked in Audio::processMidi(). // MusEGlobal::audio->isPlaying() might not be true during seek right now. //if(MusEGlobal::audio->isPlaying()) { const bool extsync = MusEGlobal::extSyncFlag; const unsigned syncFrame = MusEGlobal::audio->curSyncFrame(); const unsigned curTickPos = MusEGlobal::audio->tickPos(); const unsigned nextTick = MusEGlobal::audio->nextTick(); // What is the current transport frame? const unsigned int pos_fr = MusEGlobal::audio->pos().frame(); // What is the (theoretical) next transport frame? const unsigned int next_pos_fr = pos_fr + MusEGlobal::audio->curCycleFrames(); ciMPEvent k; //--------------------------------------------------- // Play any stuck notes which were put directly to the device //--------------------------------------------------- for (k = _stuckNotes.begin(); k != _stuckNotes.end(); ++k) { MidiPlayEvent ev(*k); unsigned int off_tick = ev.time(); // If external sync is not on, we can take advantage of frame accuracy but // first we must allow the next tick position to be included in the search // even if it is equal to the current tick position. if (extsync ? (off_tick >= nextTick) : (off_tick > nextTick)) break; unsigned int off_frame = 0; if(extsync) { if(off_tick < curTickPos) off_tick = curTickPos; off_frame = MusEGlobal::audio->extClockHistoryTick2Frame(off_tick - curTickPos) + MusEGlobal::segmentSize; } else { // What is the exact transport frame that the event should be played at? const unsigned int fr = MusEGlobal::tempomap.tick2frame(off_tick); // Is the event frame outside of the current transport frame range? if(fr >= next_pos_fr) break; off_frame = (fr < pos_fr) ? 0 : fr - pos_fr; off_frame += syncFrame; } ev.setTime(off_frame); _userEventBuffers->put(ev); } _stuckNotes.erase(_stuckNotes.begin(), k); //------------------------------------------------------------ // To save time, playing of any track-related playback stuck notes (NOT 'live' notes) // which were not put directly to the device, is done in Audio::processMidi(). //------------------------------------------------------------ } } //--------------------------------------------------------- // handleStop // To be called by audio thread only. //--------------------------------------------------------- void MidiDevice::handleStop() { // If the device is not in use by a port, don't bother it. if(_port == -1) return; MidiPort* mp = &MusEGlobal::midiPorts[_port]; //--------------------------------------------------- // send midi stop //--------------------------------------------------- // Don't send if external sync is on. The master, and our sync routing system will take care of that. if(!MusEGlobal::extSyncFlag) { // Shall we check open flags? DELETETHIS 4? //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) //if(!(dev->openFlags() & 1)) // return; MidiSyncInfo& si = mp->syncInfo(); if(si.MMCOut()) mp->sendMMCStop(); if(si.MRTOut()) { mp->sendStop(); //DELETETHIS 5? // Added check of option send continue not start. Hmm, is this required? Seems to make other devices unhappy. // (Could try now that this is in MidiDevice.) //if(!si.sendContNotStart()) // mp->sendSongpos(MusEGlobal::audio->tickPos() * 4 / MusEGlobal::config.division); } } //--------------------------------------------------- // Clear all notes and flush out any stuck notes // which were put directly to the device //--------------------------------------------------- setStopFlag(true); for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) { MidiPlayEvent ev(*i); ev.setTime(0); // Immediate processing. TODO Use curFrame? //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); putEvent(ev, MidiDevice::NotLate); } _stuckNotes.clear(); //------------------------------------------------------------ // Flush out any track-related playback stuck notes (NOT 'live' notes) // which were not put directly to the device //------------------------------------------------------------ for(ciMidiTrack imt = MusEGlobal::song->midis()->begin(); imt != MusEGlobal::song->midis()->end(); ++imt) { MPEventList& mel = (*imt)->stuckNotes; for(iMPEvent i = mel.begin(), i_next = i; i != mel.end(); i = i_next) { ++i_next; if((*i).port() != _port) continue; MidiPlayEvent ev(*i); ev.setTime(0); // Immediate processing. TODO Use curFrame? //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); putEvent(ev, MidiDevice::NotLate); mel.erase(i); } } //--------------------------------------------------- // reset sustain //--------------------------------------------------- for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); // Immediate processing. TODO Use curFrame? //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); putEvent(ev, MidiDevice::NotLate); } } } //--------------------------------------------------------- // handleSeek // To be called by audio thread only. //--------------------------------------------------------- void MidiDevice::handleSeek() { //--------------------------------------------------- // If playing, clear all notes and flush out any // stuck notes which were put directly to the device //--------------------------------------------------- if(MusEGlobal::audio->isPlaying()) { // TODO: Don't clear, let it play whatever was scheduled ? //setStopFlag(true); for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i) { MidiPlayEvent ev(*i); ev.setTime(0); // Immediate processing. TODO Use curFrame? //ev.setTime(MusEGlobal::audio->midiQueueTimeStamp(ev.time())); putEvent(ev, MidiDevice::NotLate); } _stuckNotes.clear(); } } //================================================ // BEGIN Latency correction/compensation routines. //================================================ void MidiDevice::prepareLatencyScan() { // Reset some latency info to prepare for (re)computation. _captureLatencyInfo.initialize(); _playbackLatencyInfo.initialize(); } bool MidiDevice::isLatencyInputTerminalMidi(bool capture) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if(tli->_isLatencyInputTerminalProcessed) return tli->_isLatencyInputTerminal; const int port = midiPort(); // Playback devices are considered a termination point. if(!capture || port < 0 || port >= MusECore::MIDI_PORTS) { tli->_isLatencyInputTerminal = true; tli->_isLatencyInputTerminalProcessed = true; return true; } MidiPort* mp = &MusEGlobal::midiPorts[port]; const RouteList* rl = mp->outRoutes(); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { Track* track = ir->track; if(track->off()) // || //(atrack->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !atrack->isRecMonitored()))) //&& atrack->canRecord() && !atrack->recordFlag())) continue; tli->_isLatencyInputTerminal = false; tli->_isLatencyInputTerminalProcessed = true; return false; } break; default: break; } } tli->_isLatencyInputTerminal = true; tli->_isLatencyInputTerminalProcessed = true; return true; } bool MidiDevice::isLatencyOutputTerminalMidi(bool capture) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if(tli->_isLatencyOutputTerminalProcessed) return tli->_isLatencyOutputTerminal; const int port = midiPort(); // Playback devices are considered a termination point. if(!capture || port < 0 || port >= MusECore::MIDI_PORTS) { tli->_isLatencyOutputTerminal = true; tli->_isLatencyOutputTerminalProcessed = true; return true; } MidiPort* mp = &MusEGlobal::midiPorts[port]; const RouteList* rl = mp->outRoutes(); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { Track* track = ir->track; if(track->off()) // || //(atrack->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !atrack->isRecMonitored()))) //&& atrack->canRecord() && !atrack->recordFlag())) continue; tli->_isLatencyOutputTerminal = false; tli->_isLatencyOutputTerminalProcessed = true; return false; } break; default: break; } } tli->_isLatencyOutputTerminal = true; tli->_isLatencyOutputTerminalProcessed = true; return true; } //--------------------------------------------------------- // getWorstSelfLatencyMidi //--------------------------------------------------------- float MidiDevice::getWorstSelfLatencyMidi(bool capture) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if(tli->_worstSelfLatencyMidiProcessed) return tli->_worstSelfLatencyMidi; // REMOVE Tim. latency. Changed. TESTING. Reinstate. // for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { //if(!used_chans[i]) // continue; // const float lat = selfLatencyMidi(i, capture); const float lat = selfLatencyMidi(0, capture); //const float lat = selfLatencyMidi(i, 0 /*playback*/); if(lat > tli->_worstSelfLatencyMidi) tli->_worstSelfLatencyMidi = lat; } // The absolute latency of signals leaving this track is the sum of // any connected route latencies and this track's latency. tli->_worstSelfLatencyMidiProcessed = true; return tli->_worstSelfLatencyMidi; } inline bool MidiDevice::canDominateOutputLatencyMidi(bool capture) const { if(capture) return true; return false; } inline bool MidiDevice::canDominateInputLatencyMidi(bool /*capture*/) const { return false; } inline bool MidiDevice::canDominateEndPointLatencyMidi(bool capture) const { if(capture) return false; return true; } inline bool MidiDevice::canPassThruLatencyMidi(bool /*capture*/) const { return true; } //--------------------------------------------------------- // getDominanceInfoMidi //--------------------------------------------------------- TrackLatencyInfo& MidiDevice::getDominanceInfoMidi(bool capture, bool input) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if((input && tli->_canDominateInputProcessed) || (!input && tli->_canDominateProcessed)) return *tli; // Get the default domination for this track type. bool can_dominate_lat = input ? canDominateInputLatencyMidi(capture) : canDominateOutputLatencyMidi(capture); bool can_correct_lat = canCorrectOutputLatencyMidi(); const bool passthru = canPassThruLatencyMidi(capture); bool item_found = false; const int port = midiPort(); const int open_flags = openFlags(); // Gather latency info from all connected input branches, // but ONLY if the track is not off. // Currently there are no routes FROM tracks (audio or midi) TO midi capture devices, // only TO midi playback devices. // CAUTION: The ABSENCE of the '!capture' caused an infinite loop crash, where // MidiDevice::getDominanceInfoMidi called Track::getDominanceInfo which called // MidiDevice::getDominanceInfoMidi again, and repeat inf... // When that happens, all the "Have we been here before...?" checks say 'no' // because each call has not finished yet, where at the end we say 'yes'. // So I'm not sure how we could support the above future plan, if any. if(!capture && (open_flags & (/*capture ? 2 :*/ 1)) && (passthru || input) && port >= 0 && port < MusECore::MIDI_PORTS) { // bool used_chans[MusECore::MUSE_MIDI_CHANNELS]; // for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) // used_chans[i] = false; // bool all_chans = false; #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; //if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* rl = mp->inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; // if(ir->channel < 0) // all_chans = true; // else // used_chans[ir->channel] = true; //if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } break; default: break; } } #endif // Special for the built-in metronome. //if(!capture) //{ MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if(metro_settings->midiClickFlag && metro_settings->clickPort == port) { //if((open_flags & (/*capture ? 2 :*/ 1)) && !MusECore::metronome->off() && (passthru || input)) if(!MusECore::metronome->off()) { const TrackLatencyInfo& li = MusECore::metronome->getDominanceInfoMidi(capture, false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. //route_worst_out_corr = li._outputAvailableCorrection; can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } //} } // Set the correction of all connected input branches, // but ONLY if the track is not off. if((open_flags & (capture ? 2 : 1))) { if(input) { tli->_canDominateInputLatency = can_dominate_lat; } else { tli->_canDominateOutputLatency = can_dominate_lat; // If any of the branches can dominate, then this node cannot correct. tli->_canCorrectOutputLatency = can_correct_lat && !can_dominate_lat; } } if(input) tli->_canDominateInputProcessed = true; else tli->_canDominateProcessed = true; return *tli; } //--------------------------------------------------------- // getDominanceLatencyInfoMidi //--------------------------------------------------------- TrackLatencyInfo& MidiDevice::getDominanceLatencyInfoMidi(bool capture, bool input) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if((input && tli->_dominanceInputProcessed) || (!input && tli->_dominanceProcessed)) return *tli; float route_worst_latency = 0.0f; const bool passthru = canPassThruLatencyMidi(capture); bool item_found = false; const int open_flags = openFlags(); float worst_self_latency = 0.0f; if(!input && (open_flags & (capture ? 2 : 1))) worst_self_latency = getWorstSelfLatencyMidi(capture); const int port = midiPort(); // Gather latency info from all connected input branches, // but ONLY if the track is not off. // Currently there are no routes FROM tracks (audio or midi) TO midi capture devices, // only TO midi playback devices. // CAUTION: See the warning in getDominanceInfoMidi about infinite recursion. if(!capture && (open_flags & (/*capture ? 2 :*/ 1)) && (passthru || input) && port >= 0 && port < MusECore::MIDI_PORTS) { // bool used_chans[MusECore::MUSE_MIDI_CHANNELS]; // for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) // used_chans[i] = false; // bool all_chans = false; #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; //if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* rl = mp->inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; // if(ir->channel < 0) // all_chans = true; // else // used_chans[ir->channel] = true; //if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } break; default: break; } } #endif // Special for the built-in metronome. //if(!capture) //{ MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; //if(sendMetronome()) if(metro_settings->midiClickFlag && metro_settings->clickPort == port) { //if((open_flags & (/*capture ? 2 :*/ 1)) && !MusECore::metronome->off() && (passthru || input)) if(!MusECore::metronome->off()) { const TrackLatencyInfo& li = MusECore::metronome->getDominanceLatencyInfoMidi(capture, false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } //} } // Set the correction of all connected input branches, // but ONLY if the track is not off. if((open_flags & (capture ? 2 : 1))) { if(input) { tli->_inputLatency = route_worst_latency; } else { if(passthru) { tli->_outputLatency = worst_self_latency + route_worst_latency; tli->_inputLatency = route_worst_latency; } else { tli->_outputLatency = worst_self_latency + tli->_sourceCorrectionValue; } } } if(input) tli->_dominanceInputProcessed = true; else tli->_dominanceProcessed = true; return *tli; } //--------------------------------------------------------- // setCorrectionLatencyInfoMidi //--------------------------------------------------------- TrackLatencyInfo& MidiDevice::setCorrectionLatencyInfoMidi(bool capture, bool input, float finalWorstLatency, float callerBranchLatency) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; const bool passthru = canPassThruLatencyMidi(capture); const int open_flags = openFlags(); float worst_self_latency = 0.0f; if(!input && (open_flags & 1 /*write*/)) worst_self_latency = getWorstSelfLatencyMidi(capture); // The _trackLatency should already be calculated in the dominance scan. const float branch_lat = callerBranchLatency + worst_self_latency; const int port = midiPort(); // Currently there are no routes FROM tracks (audio or midi) TO midi capture devices, // only TO midi playback devices. // CAUTION: See the warning in getDominanceInfoMidi about infinite recursion. if(!capture && (open_flags & 1 /*write*/) && (passthru || input) && port >= 0 && port < MusECore::MIDI_PORTS) { // Set the correction of all connected input branches. // The _trackLatency should already be calculated in the dominance scan. #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; //if((open_flags & 1 /*write*/) && !track->off() && (passthru || input)) if(!track->off()) track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* mrl = mp->inRoutes(); for (iRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; //if((open_flags & 1 /*write*/) && !track->off() && (passthru || input)) if(!track->off()) track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } break; default: break; } } #endif // Special for the built-in metronome. //if(!capture) //{ MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; //if(sendMetronome()) if(metro_settings->midiClickFlag && metro_settings->clickPort == port) { //if((open_flags & 1 /*write*/) && !MusECore::metronome->off() && (passthru || input)) if(!MusECore::metronome->off()) MusECore::metronome->setCorrectionLatencyInfoMidi(capture, false, finalWorstLatency, branch_lat); } //} } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(open_flags & 1 /*write*/ && !capture/*Tim*/) { if(input) { } else { if(canCorrectOutputLatencyMidi() && tli->_canCorrectOutputLatency) { float corr = 0.0f; if(MusEGlobal::config.commonProjectLatency) corr -= finalWorstLatency; corr -= branch_lat; // The _sourceCorrectionValue is initialized to zero. // Whichever calling branch needs the most correction gets it. if(corr < tli->_sourceCorrectionValue) tli->_sourceCorrectionValue = corr; } } } return *tli; } //--------------------------------------------------------- // getLatencyInfoMidi //--------------------------------------------------------- TrackLatencyInfo& MidiDevice::getLatencyInfoMidi(bool capture, bool input) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if((input && tli->_inputProcessed) || (!input && tli->_processed)) return *tli; MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; float route_worst_latency = tli->_inputLatency; const bool passthru = canPassThruLatencyMidi(capture); const int port = midiPort(); const int open_flags = openFlags(); if(passthru || input) { // Currently there are no routes FROM tracks (audio or midi) TO midi capture devices, // only TO midi playback devices. // CAUTION: See the warning in getDominanceInfoMidi about infinite recursion. if(!capture && port >= 0 && port < MusECore::MIDI_PORTS) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; // TODO: FIXME: Where to store? We have no route to store it in. // Default to zero. //ir->audioLatencyOut = 0.0f; if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off()) { TrackLatencyInfo& li = track->getLatencyInfo(false); const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // TODO: FIXME: Where to store? We have no route to store it in. // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. // ir->audioLatencyOut = route_worst_latency - li._outputLatency; // // Should not happen, but just in case. // if((long int)ir->audioLatencyOut < 0) // ir->audioLatencyOut = 0.0f; // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet // because we don't have multiple Midi Track outputs yet, only a single output port. // So we must store this information here just for Midi Tracks. li._latencyOutMidiTrack = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)li._latencyOutMidiTrack < 0) li._latencyOutMidiTrack = 0.0f; } } } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* rl = mp->inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; // Default to zero. ir->audioLatencyOut = 0.0f; if((open_flags & (/*capture ? 2 :*/ 1)) && !track->off()) { const TrackLatencyInfo& li = track->getLatencyInfo(false); const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. ir->audioLatencyOut = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)ir->audioLatencyOut < 0) ir->audioLatencyOut = 0.0f; } } } break; default: break; } } #endif // Special for the built-in metronome. //if(!capture) //{ // TODO: FIXME: Where to store? We have no route to store it in. // Default to zero. //ir->audioLatencyOut = 0.0f; if((open_flags & (/*capture ? 2 :*/ 1)) && !MusECore::metronome->off() && // sendMetronome() && metro_settings->midiClickFlag && metro_settings->clickPort == port) { TrackLatencyInfo& li = MusECore::metronome->getLatencyInfoMidi(capture, false); const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // TODO: FIXME: Where to store? We have no route to store it in. // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. // ir->audioLatencyOut = route_worst_latency - ir->audioLatencyOut; // // Should not happen, but just in case. // if((long int)ir->audioLatencyOut < 0) // ir->audioLatencyOut = 0.0f; // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet // because we don't have multiple Midi Track outputs yet, only a single output port. // So we must store this information here just for Midi Tracks. // li._latencyOutMidiTrack = route_worst_latency - li._outputLatency; // // Should not happen, but just in case. // if((long int)li._latencyOutMidiTrack < 0) // li._latencyOutMidiTrack = 0.0f; // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet // because we don't have multiple Midi Track outputs yet, only a single output port. // So we must store this information here just for Midi Tracks. li._latencyOutMetronome = route_worst_latency - li._latencyOutMetronome; // Should not happen, but just in case. if((long int)li._latencyOutMetronome < 0) li._latencyOutMetronome = 0.0f; } } //} } } if(input) tli->_inputProcessed = true; else tli->_processed = true; return *tli; } //--------------------------------------------------------- // latencyCompWriteOffset //--------------------------------------------------------- inline unsigned long MidiDevice::latencyCompWriteOffsetMidi(bool capture) const { return capture ? _captureLatencyInfo._compensatorWriteOffset : _playbackLatencyInfo._compensatorWriteOffset; } void MidiDevice::setLatencyCompWriteOffsetMidi(float worstCase, bool capture) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // If independent branches are NOT to affect project latency, // then there should be no need for any extra delay in the branch. if(!MusEGlobal::config.commonProjectLatency) { tli->_compensatorWriteOffset = 0; //fprintf(stderr, "MidiDevice::setLatencyCompWriteOffset() name:%s capture:%d worstCase:%f _outputLatency:%f _compensatorWriteOffset:%lu\n", // name().toLatin1().constData(), capture, worstCase, tli->_outputLatency, tli->_compensatorWriteOffset); return; } if(tli->_canDominateOutputLatency) { const long unsigned int wc = worstCase; const long unsigned int ol = tli->_outputLatency; if(ol > wc) tli->_compensatorWriteOffset = 0; else tli->_compensatorWriteOffset = wc - ol; } else { // if(tli->_outputLatency < 0) tli->_compensatorWriteOffset = 0; // else // tli->_compensatorWriteOffset = tli->_outputLatency; } //fprintf(stderr, // "MidiDevice::setLatencyCompWriteOffset() name:%s capture:%d worstCase:%f" // " _outputLatency:%f _canDominateOutputLatency:%d _compensatorWriteOffset:%lu\n", // name().toLatin1().constData(), capture, worstCase, tli->_outputLatency, // tli->_canDominateOutputLatency, tli->_compensatorWriteOffset); } //================================================ // END Latency correction/compensation routines. //================================================ } // namespace MusECore muse-4.0.0/src/muse/mididev.h000066400000000000000000000451231404105311000160100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mididev.h,v 1.3.2.4 2009/04/04 01:49:50 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011, 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDIDEV_H__ #define __MIDIDEV_H__ #include #include "mpevent.h" #include "route.h" #include "globaldefs.h" #include #include #include "lock_free_buffer.h" #include "sync.h" #include "evdata.h" #include "latency_info.h" #include namespace MusECore { // Forward declarations: class Xml; struct MidiOutputParams { int BANKH; int BANKL; int PROG; int RPNL; int RPNH; int NRPNL; int NRPNH; int DATAH; int DATAL; MidiOutputParams() { reset(); } void reset() { BANKH = BANKL = PROG = 0xff; RPNL = RPNH = NRPNL = NRPNH = DATAH = DATAL = -1; } void resetParamNums() { RPNL = RPNH = NRPNL = NRPNH = DATAH = DATAL = -1; } void resetPatch() { BANKH = BANKL = PROG = 0xff; } void setRPNL(int a) { RPNL = a; NRPNL = NRPNH = -1; } void setRPNH(int a) { RPNH = a; NRPNL = NRPNH = -1; } void setNRPNL(int a) { NRPNL = a; RPNL = RPNH = -1; } void setNRPNH(int a) { NRPNH = a; RPNL = RPNH = -1; } void setDATAH(int a) { DATAH = a; } void setDATAL(int a) { DATAL = a; } void setBANKH(int a) { BANKH = a;} void setBANKL(int a) { BANKL = a;} void setPROG(int a) { PROG = a;} void currentProg(int *prg, int *lbank, int *hbank) { if(prg) *prg=PROG&0xff; if(lbank) *lbank=BANKL&0xff; if(hbank) *hbank=BANKH&0xff; } void setCurrentProg(int prg, int lbank, int hbank) { PROG=prg&0xff; BANKL=lbank&0xff; BANKH=hbank&0xff; } }; //--------------------------------------------------------- // MidiDevice //--------------------------------------------------------- class MidiDevice { public: // Types of MusE midi devices. enum MidiDeviceType { ALSA_MIDI=0, JACK_MIDI=1, SYNTH_MIDI=2 }; // IDs for the various IPC FIFOs that are used. enum EventFifoIds { // Playback queued events put by the audio process thread. PlayFifo=0, // Gui events put by our gui thread. GuiFifo=1, // OSC events put by the OSC thread. OSCFifo=2, // Monitor input passthrough events put by Jack devices (audio process thread). JackFifo=3, // Monitor input passthrough events put by ALSA devices (midi seq thread). ALSAFifo=4 }; // The desired event buffer when putting an event. enum EventBufferType { // Playback queue for events that are scheduled by the playback engine. PlaybackBuffer=0, // User queue for non-playback events such as from GUI controls or external hw. UserBuffer=1 }; // Describes latency of events passed to putEvent(). enum LatencyType { // The given event's time will be used 'as is' without modification. NotLate = 0, // The given event's time has some latency. Automatically compensate // by adding an appropriate number of frames, depending on device type. // For example, events sent from a GUI control to the GuiFifo are usually // time-stamped in the past. For Synths and Jack midi (buffer-based systems) // we add a forward offset (one segment size). For ALSA (a poll-based system), // no offset is required. Similarly, events sent from OSC handlers to the // OSCFifo are also usually time-stamped in the past. // Another example, events sent by the play scheduler to the PlayFifo are // /already/ scheduled properly for the future and need /no/ further compensation // for either Jack midi or ALSA devices. Late = 1 }; private: // Used for multiple reads of fifos during process. int _tmpRecordCount[MusECore::MUSE_MIDI_CHANNELS + 1]; bool _sysexFIFOProcessed; protected: QString _name; int _port; // connected to midi port; -1 - not connected int _rwFlags; // possible open flags, 1 write, 2 read, 3 rw int _openFlags; // configured open flags bool _readEnable; // set when opened/closed. bool _writeEnable; // QString _state; std::atomic _stopFlag; // For processing system exclusive input chunks. SysExInputProcessor _sysExInProcessor; // For processing system exclusive output chunks. SysExOutputProcessor _sysExOutProcessor; // Holds all non-realtime events while the sysex processor is in the Sending state. // The official midi specs say only realtime messages can be mingled in the middle of a sysex. std::vector *_sysExOutDelayedEvents; MPEventList _stuckNotes; // Playback: Pending note-offs put directly to the device corresponding to currently playing notes // Playback IPC buffers. For playback events ONLY. Any thread can use this. LockFreeMPSCRingBuffer *_playbackEventBuffers; // Various IPC buffers. NOT for playback events. Any thread can use this. LockFreeMPSCRingBuffer *_userEventBuffers; // Recording fifos. To speed up processing, one per channel plus one special system 'channel' for channel-less events like sysex. MidiRecFifo _recordFifo[MusECore::MUSE_MIDI_CHANNELS + 1]; // To hold current output program, and RPN/NRPN parameter numbers and values. MidiOutputParams _curOutParamNums[MusECore::MUSE_MIDI_CHANNELS]; RouteList _inRoutes, _outRoutes; // Fifo holds brief history of incoming external clock messages. // Timestamped with both tick and frame so that pending play events can // be scheduled by frame. // The audio thread processes this fifo and clears it. LockFreeBuffer *_extClockHistoryFifo; // Holds latency computations each cycle. TrackLatencyInfo _captureLatencyInfo; TrackLatencyInfo _playbackLatencyInfo; // Returns the number of frames to shift forward output event scheduling times when putting events // into the eventFifos. This is not quite the same as latency (requiring a backwards shift) // although its requirement is a result of the latency. // For any driver running in the audio thread (Jack midi, synth, metro etc) this value typically // will equal one segment size. // For drivers running in their own thread (ALSA, OSC input) this will typically be near zero: // 1 ms for ALSA given a standard sequencer timer f = 1000Hz, or near zero for OSC input. inline virtual unsigned int pbForwardShiftFrames() const { return 0; } // Various IPC buffers. Any thread can use this. inline LockFreeMPSCRingBuffer *eventBuffers(EventBufferType bufferType) { switch(bufferType) { case PlaybackBuffer: return _playbackEventBuffers; case UserBuffer: return _userEventBuffers; } return _userEventBuffers; } // Informs the device to clear (flush) the outEvents and event buffers. // To be called by audio thread only. Typically from the device's handleStop routine. void setStopFlag(bool flag) { _stopFlag.store(flag); } // Returns whether the device is flagged to clear the outEvents and event buffers. // To be called from the device's thread in the process routine. bool stopFlag() const { return _stopFlag.load(); } void init(); public: MidiDevice(); MidiDevice(const QString& name); virtual ~MidiDevice(); inline SysExInputProcessor* sysExInProcessor() { return &_sysExInProcessor; } inline SysExOutputProcessor* sysExOutProcessor() { return &_sysExOutProcessor; } virtual MidiDeviceType deviceType() const = 0; virtual QString deviceTypeString() const; // The meaning of the returned pointer depends on the driver. // For Jack it returns the address of a Jack port, for ALSA it return the address of a snd_seq_addr_t. inline virtual void* inClientPort() { return 0; } inline virtual void* outClientPort() { return 0; } // These three are generally for ALSA. inline virtual void setAddressClient(int) { } inline virtual void setAddressPort(int) { } // We (ab)use the ALSA value SND_SEQ_ADDRESS_UNKNOWN to // mean 'unavailable' if either client and port equal it. inline virtual bool isAddressUnknown() const { return true; } virtual QString open() = 0; virtual void close() = 0; virtual void writeRouting(int, Xml&) const { }; RouteList* inRoutes() { return &_inRoutes; } RouteList* outRoutes() { return &_outRoutes; } bool noInRoute() const { return _inRoutes.empty(); } bool noOutRoute() const { return _outRoutes.empty(); } inline const QString& name() const { return _name; } // setName can be overloaded to do other things like setting port names, while setNameText just sets the text. inline virtual void setName(const QString& s) { _name = s; } // setNameText just sets the text, while setName can be overloaded to do other things like setting port names. inline void setNameText(const QString& s) { _name = s; } inline int midiPort() const { return _port; } void setPort(int p); inline int rwFlags() const { return _rwFlags; } inline int openFlags() const { return _openFlags; } inline void setOpenFlags(int val) { _openFlags = val; } inline void setrwFlags(int val) { _rwFlags = val; } inline const QString& state() const { return _state; } inline void setState(const QString& s) { _state = s; } inline virtual bool isSynti() const { return false; } inline virtual int selectRfd() { return -1; } inline virtual int selectWfd() { return -1; } inline virtual int bytesToWrite() { return 0; } inline virtual void flush() {} inline virtual void processInput() {} inline virtual void discardInput() {} // Event time and tick must be set by caller beforehand. virtual void recordEvent(MidiRecordEvent&); // Add a stuck note. Returns false if event cannot be delivered. virtual bool addStuckNote(const MidiPlayEvent& ev) { _stuckNotes.add(ev); return true; } // Put either a playback or a user event. Returns true if event cannot be delivered. virtual bool putEvent(const MidiPlayEvent& ev, LatencyType latencyType, EventBufferType bufferType = UserBuffer); MidiOutputParams* curOutParamNums(int chan) { return &_curOutParamNums[chan]; } void resetCurOutParamNums(int chan = -1); // Reset channel's current parameter numbers to -1. All channels if chan = -1. virtual void handleStop(); virtual void handleSeek(); virtual void processStuckNotes(); virtual void collectMidiEvents() {} // Process midi events. The frame is used by devices such as ALSA // that require grabbing a timestamp as early as possible and // passing it along to all the devices. The other devices don't // require the frame since they 'compose' a buffer based on the // frame at cycle start. virtual void processMidi(unsigned int /*curFrame*/ = 0) {} void beforeProcess(); void afterProcess(); int tmpRecordCount(const unsigned int ch) { return _tmpRecordCount[ch]; } MidiRecFifo& recordEvents(const unsigned int ch) { return _recordFifo[ch]; } bool sysexFIFOProcessed() { return _sysexFIFOProcessed; } void setSysexFIFOProcessed(bool v) { _sysexFIFOProcessed = v; } static const int extClockHistoryCapacity; LockFreeBuffer *extClockHistory() { return _extClockHistoryFifo; } void midiClockInput(unsigned int frame); // Initializes this track's latency information in preparation for a latency scan. virtual void prepareLatencyScan(); // Returns the latency of a given capture or playback port of the device. inline virtual unsigned int portLatency(void* /*port*/, bool /*capture*/) const { return 0; } // The contribution to latency by the device's own members (midi effect rack, Jack ports etc). // A midi device can contain both an input and an output. The 'capture' parameter determines which one. inline virtual float selfLatencyMidi(int /*channel*/, bool /*capture*/) const { return 0.0f; } // The cached worst latency of all the contributions from the track's own members (audio effect rack, etc) // plus any port latency if applicable. virtual float getWorstSelfLatencyMidi(bool capture); // Whether this track (and the branch it is in) can force other parallel branches to // increase their latency compensation to match this one. // If false, this branch will NOT disturb other parallel branches' compensation, // intead only allowing compensation UP TO the worst case in other branches. virtual bool canDominateOutputLatencyMidi(bool capture) const; virtual bool canDominateInputLatencyMidi(bool capture) const; // Whether this track (and the branch it is in) can force other parallel branches to // increase their latency compensation to match this one - IF this track is an end-point // and the branch allows domination. // If false, this branch will NOT disturb other parallel branches' compensation, // intead only allowing compensation UP TO the worst case in other branches. virtual bool canDominateEndPointLatencyMidi(bool capture) const; // Whether this track and its branch can correct for latency, not just compensate. inline virtual bool canCorrectOutputLatencyMidi() const { return false; } // Whether the track can pass latency values through, the SAME as if record monitor is // supported and on BUT does not require record monitor support. // This is for example in the metronome MetronomeSynthI, since it is unique in that it // can correct its own latency unlike other synths, but it does not 'pass through' // the latency values to what drives it like other synths. virtual bool canPassThruLatencyMidi(bool capture) const; // Whether any of the connected output routes are effectively connected. // That means track is not off, track is monitored where applicable, etc, // ie. signal can actually flow. // For Wave Tracks for example, asks whether the track is an end-point from the view of the input side. virtual bool isLatencyInputTerminalMidi(bool capture); // Whether any of the connected output routes are effectively connected. // That means track is not off, track is monitored where applicable, etc, // ie. signal can actually flow. // For Wave Tracks for example, asks whether the track is an end-point from the view of the playback side. virtual bool isLatencyOutputTerminalMidi(bool capture); virtual TrackLatencyInfo& getDominanceInfoMidi(bool capture, bool input); virtual TrackLatencyInfo& getDominanceLatencyInfoMidi(bool capture, bool input); // The finalWorstLatency is the grand final worst-case latency, of any output track or open branch, // determined in the complete getDominanceLatencyInfo() scan. // The callerBranchLatency is the inherent branch latency of the calling track, or zero if calling from // the very top outside of the branch heads (outside of output tracks or open branches). // The callerBranchLatency is accumulated as setCorrectionLatencyInfo() is called on each track // in a branch of the graph. virtual TrackLatencyInfo& setCorrectionLatencyInfoMidi(bool capture, bool input, float finalWorstLatency, float callerBranchLatency = 0.0f); virtual TrackLatencyInfo& getLatencyInfoMidi(bool capture, bool input); // Used during latency compensation processing. When analyzing in 'reverse' this mechansim is // needed only to equalize the timing of all the AudioOutput tracks. // It is applied as a direct offset in the latency delay compensator in getData(). virtual unsigned long latencyCompWriteOffsetMidi(bool capture) const; virtual void setLatencyCompWriteOffsetMidi(float worstCase, bool capture); }; //--------------------------------------------------------- // MidiDeviceList //--------------------------------------------------------- typedef std::list::iterator iMidiDevice; typedef std::list::const_iterator ciMidiDevice; class MidiDeviceList : public std::list { public: void add(MidiDevice* dev); void remove(MidiDevice* dev); MidiDevice* find(const QString& name, int typeHint = -1); iterator find(const MidiDevice* dev) { for(iterator i = begin(); i != end(); ++i) if(*i == dev) return i; return end(); } const_iterator find(const MidiDevice* dev) const { for(const_iterator i = begin(); i != end(); ++i) if(*i == dev) return i; return end(); } bool contains(const MidiDevice* dev) const { for(const_iterator i = begin(); i != end(); ++i) if(*i == dev) return true; return false; } }; extern void initMidiDevices(); extern bool filterEvent(const MEvent& event, int type, bool thru); } // namespace MusECore namespace MusEGlobal { extern MusECore::MidiDeviceList midiDevices; } #endif muse-4.0.0/src/muse/midiedit/000077500000000000000000000000001404105311000160015ustar00rootroot00000000000000muse-4.0.0/src/muse/midiedit/CMakeLists.txt000066400000000000000000000050741404105311000205470ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( midiedit_mocs # gatetime.h # midicmd.h # midieditor.h # miditracker.h # trackpattern.h # velocity.h dcanvas.h dlist.h drumedit.h ecanvas.h piano.h pianoroll.h prcanvas.h scoreedit.h ) ## ## List of source files to compile ## file (GLOB midiedit_source_files # citem.cpp # gatetime.cpp # midicmd.cpp # midieditor.cpp # miditracker.cpp # trackpattern.cpp # velocity.cpp dcanvas.cpp dlist.cpp drumedit.cpp drummap.cpp drum_ordering.cpp ecanvas.cpp piano.cpp pianoroll.cpp prcanvas.cpp scoreedit.cpp ) ## ## Define target ## add_library ( midiedit ${MODULES_BUILD} ${midiedit_source_files} ${midiedit_mocs} ) add_dependencies(midiedit components) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${midiedit_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( midiedit PROPERTIES OUTPUT_NAME muse_midiedit ) ## ## Linkage ## target_link_libraries ( midiedit ${QT_LIBRARIES} ctrl icons widgets widgets_functiondialogs ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS midiedit DESTINATION ${MusE_MODULES_DIR} ) else ( ${MODULES_BUILD} STREQUAL SHARED ) target_link_libraries (midiedit core) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/midiedit/cmd.h000066400000000000000000000025671404105311000167270ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: cmd.h,v 1.1.1.1 2003/10/27 18:52:20 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CMD_H__ #define __CMD_H__ #define CMD_LEFT 0 #define CMD_RIGHT 1 #define CMD_INSERT 2 #define CMD_DELETE 3 #define CMD_BACKSPACE 4 #define CMD_1 5 #define CMD_2 6 #define CMD_3 7 #define CMD_4 8 #define CMD_5 9 #define CMD_6 10 #define CMD_7 11 #define CMD_T 12 #define CMD_period 13 #define CMD_LEFT_NOSNAP 14 #define CMD_RIGHT_NOSNAP 15 #endif muse-4.0.0/src/muse/midiedit/dcanvas.cpp000066400000000000000000002110341404105311000201250ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dcanvas.cpp,v 1.16.2.10 2009/10/15 22:45:50 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include #include "dcanvas.h" #include "midieditor.h" #include "drumedit.h" #include "drummap.h" #include "drum_ordering.h" #include "event.h" #include "mpevent.h" #include "xml.h" #include "globals.h" #include "midiport.h" #include "audio.h" #include "midi_consts.h" #include "shortcuts.h" #include "icons.h" #include "functions.h" #include "helper.h" #include "operations.h" #include "gconfig.h" #include "app.h" #define CARET 10 #define CARET2 5 using MusEGlobal::debugMsg; using MusEGlobal::heavyDebugMsg; using MusECore::Track; using MusECore::MidiTrack; namespace MusEGui { //--------------------------------------------------------- // DEvent //--------------------------------------------------------- DEvent::DEvent(MusECore::Event e, MusECore::Part* p, int instr) : EItem(e, p) { int y = instr * TH + TH/2; int tick = e.tick() + p->tick(); setPos(QPoint(tick, y)); setBBox(QRect(-CARET2, -CARET2, CARET, CARET)); // Give the moving point an initial value. setMp(pos()); } //--------------------------------------------------------- // addItem //--------------------------------------------------------- CItem* DrumCanvas::addItem(MusECore::Part* part, const MusECore::Event& event) { if (signed(event.tick())<0) { printf("ERROR: trying to add event before current part!\n"); return nullptr; } int instr=pitch_and_track_to_instrument(event.pitch(), part->track()); if (instr<0) { if (heavyDebugMsg) printf("trying to add event which is hidden or not in any part known to me\n"); return nullptr; } DEvent* ev = new DEvent(event, part, instr); items.add(ev); return ev; } //--------------------------------------------------------- // DrumCanvas //--------------------------------------------------------- DrumCanvas::DrumCanvas(MidiEditor* pr, QWidget* parent, int sx, int sy, const char* name) : EventCanvas(pr, parent, sx, sy, name) { setObjectName("DrumCanvas"); drumEditor=static_cast(pr); setStatusTip(tr("Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help.")); ourDrumMap=nullptr; rebuildOurDrumMap(); setVirt(false); cursorPos= QPoint(0,0); _stepSize=1; steprec=new MusECore::StepRec(nullptr); songChanged(SC_TRACK_INSERTED); connect(MusEGlobal::song, SIGNAL(midiNote(int, int)), SLOT(midiNote(int,int))); } DrumCanvas::~DrumCanvas() { if (must_delete_our_drum_map && ourDrumMap!=nullptr) delete [] ourDrumMap; delete steprec; } //--------------------------------------------------------- // index2Note // Return false if invalid index //--------------------------------------------------------- bool DrumCanvas::index2Note(int index, int* port, int* channel, int* note) { if ((index<0) || (index>=getOurDrumMapSize())) return false; int mport, ch; // Default to track port if -1 and track channel if -1. MusECore::Track* track = nullptr; MusECore::MidiTrack* mt = nullptr; if(ourDrumMap[index].port == -1) { track = *instrument_map[index].tracks.begin(); if(!track->isMidiTrack()) return false; mt = static_cast(track); mport = mt->outPort(); } else mport = ourDrumMap[index].port; if(ourDrumMap[index].channel == -1) { if(!track) { track = *instrument_map[index].tracks.begin(); if(!track->isMidiTrack()) return false; mt = static_cast(track); } ch = mt->outChannel(); } else ch = ourDrumMap[index].channel; if(port) *port = mport; if(channel) *channel = ch; if(note) //*note = old_style_drummap_mode ? ourDrumMap[index].anote : instrument_map[index].pitch; *note = ourDrumMap[index].anote; return true; } //--------------------------------------------------------- // moveCanvasItems //--------------------------------------------------------- MusECore::Undo DrumCanvas::moveCanvasItems(CItemMap& items, int dp, int dx, DragType dtype, bool rasterize) { if(editor->parts()->empty()) return MusECore::Undo(); //return empty list MusECore::PartsToChangeMap parts2change; MusECore::Undo operations; for(MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { MusECore::Part* part = ip->second; if(!part) continue; int npartoffset = 0; for(iCItem ici = items.begin(); ici != items.end(); ++ici) { CItem* ci = ici->second; ci->setMoving(false); if(ci->part() != part) continue; int x = ci->pos().x() + dx; int y = pitch2y(y2pitch(ci->pos().y()) + dp); QPoint newpos = QPoint(x, y); if(rasterize) newpos = raster(newpos); // Test moving the item... DEvent* nevent = (DEvent*) ci; MusECore::Event event = nevent->event(); x = newpos.x(); if(x < 0) x = 0; int ntick = (rasterize ? editor->rasterVal(x) : x) - part->tick(); if(ntick < 0) ntick = 0; int diff = ntick + event.lenTick() - part->lenTick(); // If moving the item would require a new part size... if(diff > npartoffset) npartoffset = diff; } if(npartoffset > 0) { MusECore::iPartToChange ip2c = parts2change.find(part); if(ip2c == parts2change.end()) { MusECore::PartToChange p2c = {nullptr, npartoffset}; parts2change.insert(std::pair (part, p2c)); } else ip2c->second.xdiff = npartoffset; } } bool forbidden=false; for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c) { MusECore::Part* opart = ip2c->first; if (opart->hasHiddenEvents() & MusECore::Part::RightEventsHidden) { forbidden=true; break; } } if (!forbidden) { std::vector< CItem* > doneList; typedef std::vector< CItem* >::iterator iDoneList; for(iCItem ici = items.begin(); ici != items.end(); ++ici) { CItem* ci = ici->second; const QPoint oldpos(ci->pos()); int x = ci->pos().x(); int y = ci->pos().y(); int nx = x + dx; int ny = pitch2y(y2pitch(y) + dp); QPoint newpos = QPoint(nx, ny); if(rasterize) newpos = raster(newpos); selectItem(ci, true); iDoneList idl; for(idl = doneList.begin(); idl != doneList.end(); ++idl) // This compares EventBase pointers to see if they're the same... if((*idl)->event() == ci->event()) break; // Do not process if the event has already been processed (meaning it's an event in a clone part)... if (idl == doneList.end()) { if (moveItem(operations, ci, newpos, dtype, rasterize) == false) //error? { QMessageBox::warning(this, tr("Moving items failed"), tr("The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track.\nChanging the current part with ALT+LEFT/RIGHT may help.")); return MusECore::Undo(); //return empty list } doneList.push_back(ci); } ci->move(newpos); itemReleased(ci, oldpos); if(dtype == MOVE_COPY || dtype == MOVE_CLONE) selectItem(ci, false); } itemsReleased(); for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c) { MusECore::Part* opart = ip2c->first; int diff = ip2c->second.xdiff; schedule_resize_all_same_len_clone_parts(opart, opart->lenTick() + diff, operations); } return operations; } else { return MusECore::Undo(); //return empty list } } //--------------------------------------------------------- // moveItem //--------------------------------------------------------- bool DrumCanvas::moveItem(MusECore::Undo& operations, CItem* item, const QPoint& pos, DragType dtype, bool rasterize) { DEvent* nevent = (DEvent*) item; MusECore::MidiPart* part = (MusECore::MidiPart*)nevent->part(); MusECore::MidiPart* dest_part = part; int instrument = y2pitch(pos.y()); if (instrument<0) instrument=0; if (instrument>=getOurDrumMapSize()) instrument=getOurDrumMapSize()-1; MusECore::Event event = nevent->event(); if (!instrument_map[instrument].tracks.contains(dest_part->track())) { if (debugMsg) printf("trying to move an event into a different track. checking if curPart is set correctly...\n"); if (!instrument_map[instrument].tracks.contains(curPart->track())) { printf ("ERROR: tried to move an event into a track which is different from both the initial part's and the curPart's track! ignoring this one...\n"); return false; } else dest_part=(MusECore::MidiPart*)curPart; } int x = pos.x(); if (x < 0) x = 0; int ntick = (rasterize ? editor->rasterVal(x) : x) - dest_part->tick(); if (ntick < 0) ntick = 0; event.setSelected(false); MusECore::Event newEvent = event.clone(); newEvent.setSelected(true); int ev_pitch = instrument_map[instrument].pitch; newEvent.setPitch(ev_pitch); newEvent.setTick(ntick); // don't check, whether the new event is within the part // at this place. with operation groups, the part isn't // resized yet. (flo93) if (dtype == MOVE_COPY || dtype == MOVE_CLONE) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, newEvent, dest_part, false, false)); else { if (dest_part == part) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); else { operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, event, part, false, false)); operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, newEvent, dest_part, false, false)); } } return true; } //--------------------------------------------------------- // newItem //--------------------------------------------------------- CItem* DrumCanvas::newItem(const QPoint& p, int state) { int instr = y2pitch(p.y()); if ((instr<0) || (instr>=getOurDrumMapSize())) return nullptr; int k4 = (Qt::MetaModifier | Qt::AltModifier); //int nk4 = Qt::ControlModifier; int k2 = Qt::MetaModifier; int nk2 = (Qt::ControlModifier | Qt::AltModifier); int k1 = (Qt::ControlModifier | Qt::MetaModifier); int nk1 = Qt::AltModifier; int velo = ourDrumMap[instr].lv3; if ((state & k4) == k4) // && !(state & nk4)) velo = ourDrumMap[instr].lv4; else if ((state & k2) == k2 && !(state & nk2)) velo = ourDrumMap[instr].lv2; else if ((state & k1) == k1 && !(state & nk1)) velo = ourDrumMap[instr].lv1; int tick = p.x(); if(tick < 0) tick = 0; if(!(state & Qt::ShiftModifier)) tick = editor->rasterVal(tick); return newItem(tick, instr, velo); } //--------------------------------------------------------- // newItem //--------------------------------------------------------- CItem* DrumCanvas::newItem(int tick, int instrument, int velocity) { if ((instrument<0) || (instrument>=getOurDrumMapSize())) return nullptr; if (!instrument_map[instrument].tracks.contains(curPart->track())) { if (debugMsg) printf("tried to create a new Item which cannot be inside the current track. looking for destination part...\n"); QSet parts = parts_at_tick(tick, instrument_map[instrument].tracks); if (parts.count() != 1) { QMessageBox::warning(this, tr("Creating event failed"), tr("Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous.\nSelect the destination part, then try again.")); return nullptr; } else { setCurrentPart(*parts.begin()); } } // else or if we found an alternative part (which has now been set as curPart) tick -= curPart->tick(); if (tick < 0) return nullptr; MusECore::Event e(MusECore::Note); e.setTick(tick); e.setPitch(instrument_map[instrument].pitch); e.setVelo(velocity); e.setLenTick(ourDrumMap[instrument].len); if(_playEvents) { int pitch, port, channel; if(index2Note(instrument, &port, &channel, &pitch)) startPlayEvent(pitch, e.velo(), port, channel); } return new DEvent(e, curPart, instrument); } //--------------------------------------------------------- // newItem //--------------------------------------------------------- void DrumCanvas::newItem(CItem* item, bool noSnap) { newItem(item, noSnap,true); } void DrumCanvas::newItem(CItem* item, bool noSnap, bool replace) { if(!item) { printf("THIS SHOULD NEVER HAPPEN: DrumCanvas::newItem called with nullptr item!\n"); return; } DEvent* nevent = (DEvent*) item; MusECore::Event event = nevent->event(); MusECore::Part* part = nevent->part(); int ptick = part->tick(); int x = item->x(); if (xrasterVal(x); if (xy()); if ((npitch<0) || (npitch>=getOurDrumMapSize())) return; npitch = instrument_map[npitch].pitch; event.setPitch(npitch); event.setSelected(true); // check for existing event // if found change command semantic from insert to delete MusECore::Undo operations; std::pair range = part->events().equal_range(event.type() == MusECore::Wave ? event.frame() : event.tick()); MusECore::Event oev; bool found = false; for(MusECore::ciEvent i = range.first; i != range.second; ++i) { oev = i->second; if(!oev.isNote()) continue; if(oev.pitch() == npitch) { found = true; break; } } // Indicate do undo, and do not do port controller values and clone parts. int diff = event.endTick()-part->lenTick(); if (! ((diff > 0) && (part->hasHiddenEvents() & MusECore::Part::RightEventsHidden)) ) //operation is allowed { if(found) { if(replace) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent,event, oev, part, false, false)); else operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent,oev, part, false, false)); } else operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent,event, part, false, false)); if (diff > 0) // part must be extended? { schedule_resize_all_same_len_clone_parts(part, event.endTick(), operations); printf("newItem: extending\n"); } } else // forbid action by not applying it { if(found) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent,oev, part, false, false)); } if(!operations.empty()) MusEGlobal::song->applyOperationGroup(operations); else songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is necessary //to remove "forbidden" events from the list again } //--------------------------------------------------------- // deleteItem //--------------------------------------------------------- bool DrumCanvas::deleteItem(CItem* item) { MusECore::Event ev = ((DEvent*)item)->event(); // Indicate do undo, and do not do port controller values and clone parts. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, ev, ((DEvent*)item)->part(), false, false)); return false; } //--------------------------------------------------------- // itemPressed //--------------------------------------------------------- void DrumCanvas::itemPressed(const CItem* item) { if (!_playEvents) return; MusECore::Event e = ((DEvent*)item)->event(); int index = e.pitch(); // play note: for (int i = 0; i < instrument_map.size(); ++i) { if (instrument_map.at(i).pitch == index) { index = i; break; } } int pitch, port, channel; if(index2Note(index, &port, &channel, &pitch)) startPlayEvent(pitch, e.velo(), port, channel); } //--------------------------------------------------------- // itemReleased //--------------------------------------------------------- void DrumCanvas::itemReleased(const CItem* item, const QPoint&) { const int oindex = y2pitch(item->mp().y()); // for (int i = 0; i < instrument_map.size(); ++i) { // if (instrument_map.at(i).pitch == index) { // index = i; // break; // } // } int opitch, oport, ochannel; if(!index2Note(oindex, &oport, &ochannel, &opitch)) { // Stop any playing notes: stopPlayEvents(); return; } // stop note: stopStuckNote(oport, ochannel, opitch); } //--------------------------------------------------------- // itemMoving //--------------------------------------------------------- void DrumCanvas::itemMoving(const CItem* item, const QPoint& newMP) { const int oindex = y2pitch(item->mp().y()); const int index = y2pitch(newMP.y()); int opitch, oport, ochannel, pitch, port, channel; if(!index2Note(oindex, &oport, &ochannel, &opitch)) { // Stop any playing notes: stopPlayEvents(); return; } if(!index2Note(index, &port, &channel, &pitch)) { // Stop any playing notes: stopPlayEvents(); return; } if(port == oport && channel == ochannel && pitch == opitch) return; // Stop any playing note: stopStuckNote(port, channel, opitch); } //--------------------------------------------------------- // itemMoved //--------------------------------------------------------- void DrumCanvas::itemMoved(const CItem* item, const QPoint& oldMP) { const int oindex = y2pitch(oldMP.y()); const int index = y2pitch(item->mp().y()); int opitch, oport, ochannel, pitch, port, channel; if(!index2Note(oindex, &oport, &ochannel, &opitch)) { // Stop any playing notes: stopPlayEvents(); return; } if(!index2Note(index, &port, &channel, &pitch)) { // Stop any playing notes: stopPlayEvents(); return; } if(port == oport && channel == ochannel && pitch == opitch) return; if(_playEvents) { // Unlike with PianoRoll::itemMoved(), don't play multiple notes (no concept of chords). if(item == curItem) { const MusECore::Event e = ((DEvent*)item)->event(); // play note: startPlayEvent(pitch, e.velo(), port, channel); } } } //--------------------------------------------------------- // drawItem //--------------------------------------------------------- void DrumCanvas::drawItem(QPainter&p, const CItem*item, const QRect& mr, const QRegion&) { DEvent* e = (DEvent*) item; int mx = 0, my = 0; mx = mapx(item->pos().x()); my = mapy(item->pos().y()); QPolygon pa(4); pa.setPoint(0, mx - CARET2, my); pa.setPoint(1, mx, my - CARET2); pa.setPoint(2, mx + CARET2, my); pa.setPoint(3, mx, my + CARET2); QRect r(pa.boundingRect()); r = r.intersected(mr); if(!r.isValid()) return; QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); if (e->part() != curPart) { if(item->isMoving()) p.setBrush(Qt::gray); else if(item->isSelected()) p.setBrush(Qt::black); else p.setBrush(Qt::lightGray); } else if (item->isMoving()) { p.setBrush(Qt::gray); } else if (item->isSelected()) { p.setBrush(MusEGlobal::config.midiItemSelectedColor); // p.setBrush(Qt::black); } else { int velo = e->event().velo(); MusECore::DrumMap* dm = &ourDrumMap[y2pitch(my)]; //Get the drum item QColor color; if (velo < dm->lv1) color.setRgb(240, 240, 255); else if (velo < dm->lv2) color.setRgb(200, 200, 255); else if (velo < dm->lv3) color.setRgb(170, 170, 255); else color = MusEGlobal::config.midiItemColor; // color.setRgb(0, 0, 255); p.setBrush(color); } p.drawPolygon(pa); } //--------------------------------------------------------- // drawMoving // draws moving items //--------------------------------------------------------- void DrumCanvas::drawMoving(QPainter& p, const CItem* item, const QRect& rect, const QRegion&) { QPolygon pa(4); QPoint pt = map(item->mp()); int x = pt.x(); int y = pt.y(); pa.setPoint(0, x-CARET2, y + TH/2); pa.setPoint(1, x, y + TH/2+CARET2); pa.setPoint(2, x+CARET2, y + TH/2); pa.setPoint(3, x, y + (TH-CARET)/2); QRect mr(pa.boundingRect()); mr = mr.intersected(rect); if(!mr.isValid()) return; QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); p.setBrush(Qt::black); p.drawPolygon(pa); } //--------------------------------------------------------- // drawCanvas //--------------------------------------------------------- void DrumCanvas::drawCanvas(QPainter& p, const QRect& mr, const QRegion& rg) { const QRect ur = mapDev(mr); int ux = ur.x(); if(ux < 0) ux = 0; const int uy = ur.y(); const int uw = ur.width(); const int uh = ur.height(); const int ux_2 = ux + uw; const int uy_2 = uy + uh; QPen pen; pen.setCosmetic(true); pen.setColor(MusEGlobal::config.midiDividerColor); p.setPen(pen); if (MusEGlobal::config.canvasShowGrid || MusEGlobal::config.canvasShowGridHorizontalAlways) { //--------------------------------------------------- // horizontal lines //--------------------------------------------------- int uyy = ((uy-1) / TH) * TH + TH; // For testing... // fprintf(stderr, "DrumCanvas::drawCanvas ux:%d uy:%d uw:%d uh:%d uyy:%d\n", ux, uy, uw, uh, uyy); for (; uyy < uy_2; uyy += TH) { p.drawLine(ux, uyy, ux_2, uyy); } } if (MusEGlobal::config.canvasShowGrid) { //--------------------------------------------------- // vertical lines //--------------------------------------------------- if (MusEGlobal::config.canvasShowGrid) drawTickRaster(p, mr, rg, editor->raster(), false, false, false, MusEGlobal::config.midiCanvasBeatColor, MusEGlobal::config.midiCanvasBeatColor, MusEGlobal::config.midiCanvasFineColor, MusEGlobal::config.midiCanvasBarColor); } } //--------------------------------------------------------- // drawTopItem //--------------------------------------------------------- void DrumCanvas::drawTopItem(QPainter& p, const QRect&, const QRegion&) { // draw cursor if (_tool == CursorTool) { QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); int y = mapy(TH * cursorPos.y()); // p.drawPixmap(mapx(cursorPos.x())-TH/2,y,TH,TH, *cursorIconSVG); cursorIconSVG->paint(&p, mapx(cursorPos.x())-TH/2, y, TH, TH); // need to figure out a coordinate system for the cursor, complicated stuff. } } //--------------------------------------------------------- // y2pitch //--------------------------------------------------------- int DrumCanvas::y2pitch(int y) const { int pitch = y/TH; if (pitch >= instrument_map.size()) pitch = instrument_map.size()-1; else if (pitch<0) pitch = 0; return pitch; } //--------------------------------------------------------- // pitch2y //--------------------------------------------------------- int DrumCanvas::pitch2y(int pitch) const { return pitch * TH; } //--------------------------------------------------------- // cmd //--------------------------------------------------------- void DrumCanvas::cmd(int cmd) { switch (cmd) { case CMD_SELECT_ALL: // select all for (iCItem k = items.begin(); k != items.end(); ++k) { if (!k->second->isSelected()) selectItem(k->second, true); } break; case CMD_SELECT_NONE: // select none deselectAll(); break; case CMD_SELECT_INVERT: // invert selection for (iCItem k = items.begin(); k != items.end(); ++k) { selectItem(k->second, !k->second->isSelected()); } break; case CMD_SELECT_ILOOP: // select inside loop for (iCItem k = items.begin(); k != items.end(); ++k) { DEvent* nevent =(DEvent*)(k->second); MusECore::Part* part = nevent->part(); MusECore::Event event = nevent->event(); unsigned tick = event.tick() + part->tick(); if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos()) selectItem(k->second, false); else selectItem(k->second, true); } break; case CMD_SELECT_OLOOP: // select outside loop for (iCItem k = items.begin(); k != items.end(); ++k) { DEvent* nevent = (DEvent*)(k->second); MusECore::Part* part = nevent->part(); MusECore::Event event = nevent->event(); unsigned tick = event.tick() + part->tick(); if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos()) selectItem(k->second, true); else selectItem(k->second, false); } break; case CMD_RANGE_TO_SELECTION: setRangeToSelection(); break; case CMD_SELECT_PREV_PART: // select previous part { MusECore::Part* pt = editor->curCanvasPart(); MusECore::Part* newpt = pt; MusECore::PartList* pl = editor->parts(); for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) if(ip->second == pt) { if(ip == pl->begin()) ip = pl->end(); --ip; newpt = ip->second; break; } if(newpt != pt) editor->setCurCanvasPart(newpt); } break; case CMD_SELECT_NEXT_PART: // select next part { MusECore::Part* pt = editor->curCanvasPart(); MusECore::Part* newpt = pt; MusECore::PartList* pl = editor->parts(); for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) if(ip->second == pt) { ++ip; if(ip == pl->end()) ip = pl->begin(); newpt = ip->second; break; } if(newpt != pt) editor->setCurCanvasPart(newpt); } break; case CMD_FIXED_LEN: //Set notes to the length specified in the drummap if (!selectionSize()) break; MusEGlobal::song->startUndo(); for (iCItem k = items.begin(); k != items.end(); ++k) { if (k->second->isSelected()) { DEvent* devent = (DEvent*)(k->second); MusECore::Event event = devent->event(); MusECore::Event newEvent = event.clone(); // newEvent.setLenTick(drumMap[event.pitch()].len); newEvent.setLenTick(ourDrumMap[y2pitch(devent->y())].len); // Operation is undoable but do not start/end undo. // Indicate do not do port controller values and clone parts. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, devent->part(), false, false), MusECore::Song::OperationUndoable); } } MusEGlobal::song->endUndo(SC_EVENT_MODIFIED); break; case CMD_LEFT: { int spos = pos[0]; if(spos > 0) { spos -= 1; // Nudge by -1, then snap down with raster1. spos = MusEGlobal::sigmap.raster1(spos, editor->rasterStep(pos[0])); } if(spos < 0) spos = 0; MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); } break; case CMD_RIGHT: { int spos = MusEGlobal::sigmap.raster2(pos[0] + 1, editor->rasterStep(pos[0])); // Nudge by +1, then snap up with raster2. MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); } break; case CMD_LEFT_NOSNAP: { printf("left no snap\n"); int spos = pos[0] - editor->rasterStep(pos[0]); if (spos < 0) spos = 0; MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW } break; case CMD_RIGHT_NOSNAP: { MusECore::Pos p(pos[0] + editor->rasterStep(pos[0]), true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW } break; } itemSelectionsChanged(); redraw(); } //--------------------------------------------------------- // startDrag //--------------------------------------------------------- void DrumCanvas::startDrag(CItem* /* item*/, DragType t) { QMimeData* md = selected_events_to_mime(partlist_to_set(editor->parts()), 1); if (md) { // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can // clean up after the drag and drop operation has been completed. " QDrag* drag = new QDrag(this); drag->setMimeData(md); if (t == MOVE_COPY || t == MOVE_CLONE) drag->exec(Qt::CopyAction); else drag->exec(Qt::MoveAction); } } //--------------------------------------------------------- // dragEnterEvent //--------------------------------------------------------- void DrumCanvas::dragEnterEvent(QDragEnterEvent* event) { event->acceptProposedAction(); // TODO CHECK Tim. } //--------------------------------------------------------- // dragMoveEvent //--------------------------------------------------------- void DrumCanvas::dragMoveEvent(QDragMoveEvent*) { } //--------------------------------------------------------- // dragLeaveEvent //--------------------------------------------------------- void DrumCanvas::dragLeaveEvent(QDragLeaveEvent*) { } //--------------------------------------------------------- // keyPressed - called from DList //--------------------------------------------------------- void DrumCanvas::keyPressed(int index, int velocity) { if(velocity > 127) velocity = 127; else if(velocity <= 0) velocity = 1; // Stop all notes. stopPlayEvents(); if ((index<0) || (index>=getOurDrumMapSize())) return; // called from DList - play event // play note: if(_playEvents) { int pitch, port, channel; if(index2Note(index, &port, &channel, &pitch)) startPlayEvent(pitch, velocity, port, channel); } if (_steprec) /* && pos[0] >= start_tick && pos[0] < end_tick [removed by flo93: this is handled in steprec->record] */ { if ( curPart && instrument_map[index].tracks.contains(curPart->track()) ) steprec->record(curPart,instrument_map[index].pitch,ourDrumMap[index].len,editor->raster(),velocity,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier, -1 /* invalid pitch as "really played" -> the "insert rest" feature is never triggered */); else { QSet parts = parts_at_tick(pos[0], instrument_map[index].tracks); if (parts.count() != 1) QMessageBox::warning(this, tr("Recording event failed"), tr("Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous.\nSelect the destination part, then try again.")); else steprec->record(*parts.begin(), instrument_map[index].pitch,ourDrumMap[index].len,editor->raster(),velocity,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier, -1 /* invalid pitch as "really played" -> the "insert rest" feature is never triggered */); } } } //--------------------------------------------------------- // keyReleased //--------------------------------------------------------- void DrumCanvas::keyReleased(int, bool) { // release note: if(_playEvents) stopPlayEvents(); } //--------------------------------------------------------- // mapChanged //--------------------------------------------------------- void DrumCanvas::mapChanged(int spitch, int dpitch) { // spitch may be the same as dpitch! and something in here must be executed // even if they're same (i assume it's song->update(SC_DRUMMAP)) (flo93) if (dpitch!=spitch) { using MusEGlobal::global_drum_ordering_t; using MusEGlobal::global_drum_ordering; for (QSet::iterator it=instrument_map[spitch].tracks.begin(); it!=instrument_map[spitch].tracks.end(); it++) { if (dynamic_cast(*it)) dynamic_cast(*it)->set_drummap_ordering_tied_to_patch(false); } for (QSet::iterator it=instrument_map[dpitch].tracks.begin(); it!=instrument_map[dpitch].tracks.end(); it++) { if (dynamic_cast(*it)) dynamic_cast(*it)->set_drummap_ordering_tied_to_patch(false); } MusECore::DrumMap dm_temp = ourDrumMap[spitch]; instrument_number_mapping_t im_temp = instrument_map[spitch]; global_drum_ordering_t order_temp; for (global_drum_ordering_t::iterator it=global_drum_ordering.begin(); it!=global_drum_ordering.end();) { if (im_temp.pitch==it->second && im_temp.tracks.contains(it->first)) { order_temp.push_back(*it); it=global_drum_ordering.erase(it); } else it++; } // the instrument represented by instrument_map[dpitch] is always the instrument // which will be immediately AFTER our dragged instrument. or it's invalid if (dpitch < getOurDrumMapSize()) { for (global_drum_ordering_t::iterator it=global_drum_ordering.begin(); it!=global_drum_ordering.end(); it++) if (instrument_map[dpitch].pitch==it->second && instrument_map[dpitch].tracks.contains(it->first)) { while (!order_temp.empty()) it=global_drum_ordering.insert(it, order_temp.takeLast()); break; } } else { global_drum_ordering_t::iterator it=global_drum_ordering.end(); while (!order_temp.empty()) it=global_drum_ordering.insert(it, order_temp.takeLast()); } if (dpitch > spitch) { for (int i=spitch; i dpitch) { for (int i=spitch; i>dpitch; i--) { ourDrumMap[i]=ourDrumMap[i-1]; instrument_map[i]=instrument_map[i-1]; } ourDrumMap[dpitch] = dm_temp; instrument_map[dpitch] = im_temp; } } MusEGlobal::song->update(SC_DRUMMAP); // this causes a complete rebuild of ourDrumMap // which also handles the changed order in all // other drum editors } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void DrumCanvas::resizeEvent(QResizeEvent* ev) { if (ev->size().width() != ev->oldSize().width()) emit newWidth(ev->size().width()); EventCanvas::resizeEvent(ev); } //--------------------------------------------------------- // modifySelected //--------------------------------------------------------- void DrumCanvas::modifySelected(NoteInfo::ValType type, int val, bool delta_mode) { QList< QPair > already_done; MusECore::Undo operations; for (iCItem i = items.begin(); i != items.end(); ++i) { if (!(i->second->isSelected())) continue; DEvent* e = (DEvent*)(i->second); MusECore::Event event = e->event(); if (event.type() != MusECore::Note) continue; MusECore::MidiPart* part = (MusECore::MidiPart*)(e->part()); if (already_done.contains(QPair(part->clonemaster_sn(), event))) continue; MusECore::Event newEvent = event.clone(); switch (type) { case NoteInfo::VAL_TIME: { int newTime = val; if(delta_mode) newTime += event.tick(); else newTime -= part->tick(); if (newTime < 0) newTime = 0; newEvent.setTick(newTime); } break; case NoteInfo::VAL_LEN: { int len = val; if(delta_mode) len += event.lenTick(); if (len < 1) len = 1; newEvent.setLenTick(len); } break; case NoteInfo::VAL_VELON: { int velo = val; if(delta_mode) velo += event.velo(); if (velo > 127) velo = 127; else if (velo < 0) // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now. // velo = 0; velo = 1; newEvent.setVelo(velo); } break; case NoteInfo::VAL_VELOFF: { int velo = val; if(delta_mode) velo += event.veloOff(); if (velo > 127) velo = 127; else if (velo < 0) velo = 0; newEvent.setVeloOff(velo); } break; case NoteInfo::VAL_PITCH: { int direction = -val; for (int i = 0; i < instrument_map.size(); ++i) { if (instrument_map.at(i).pitch == event.pitch()) { int nextPos = i + direction; if (nextPos> -1 && nextPos < instrument_map.size()) newEvent.setPitch(instrument_map.at(nextPos).pitch); break; } } } break; } operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); already_done.append(QPair(part->clonemaster_sn(), event)); } MusEGlobal::song->applyOperationGroup(operations); } //--------------------------------------------------------- // curPartChanged //--------------------------------------------------------- void DrumCanvas::curPartChanged() { EventCanvas::curPartChanged(); editor->setWindowTitle(getCaption()); } //--------------------------------------------------------- // getNextStep - gets next tick in the chosen direction // when raster and stepSize are taken into account //--------------------------------------------------------- int DrumCanvas::getNextStep(unsigned int currentPos, int basicStep, int stepSize) { int newPos = currentPos; // newPos will be updated for every loop for (int i = 0; i < stepSize; i++) { if (basicStep > 0) // moving right { // Nudge by +1, then snap up with raster2. newPos = MusEGlobal::sigmap.raster2(newPos + basicStep, editor->rasterStep(newPos)); if (newPos > signed(curPart->endTick() - editor->rasterStep(curPart->endTick()))) { newPos = curPart->tick(); } } else // moving left { // Nudge by -1, then snap up with raster1. newPos = MusEGlobal::sigmap.raster1(newPos + basicStep, editor->rasterStep(newPos)); if (newPos < signed(curPart->tick())) { newPos = MusEGlobal::sigmap.raster1(curPart->endTick()-1, editor->rasterStep(curPart->endTick())); } } } return newPos; } //--------------------------------------------------------- // keyPress //--------------------------------------------------------- void DrumCanvas::keyPress(QKeyEvent* event) { if (_tool == CursorTool) { int key = event->key(); if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) key += Qt::SHIFT; if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) key += Qt::ALT; if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) key+= Qt::CTRL; // Select items by key (PianoRoll & DrumEditor) if (key == shortcuts[SHRT_SEL_RIGHT].key) { cursorPos.setX(getNextStep(cursorPos.x(),1)); selectCursorEvent(getEventAtCursorPos()); if (mapx(cursorPos.x()) < 0 || mapx(cursorPos.x()) > width()) emit followEvent(cursorPos.x()); update(); return; } else if (key == shortcuts[SHRT_SEL_LEFT].key) { cursorPos.setX(getNextStep(cursorPos.x(),-1)); selectCursorEvent(getEventAtCursorPos()); if (mapx(cursorPos.x()) < 0 || mapx(cursorPos.x()) > width()) emit followEvent(cursorPos.x()); update(); return; } // NOTE: The inner NewItem may play the note. But let us not stop the note so shortly after playing it. // So it is up to the corresponding keyRelease() to stop the note. else if ( key == shortcuts[SHRT_ADDNOTE_1].key || key == shortcuts[SHRT_ADDNOTE_2].key || key == shortcuts[SHRT_ADDNOTE_3].key || key == shortcuts[SHRT_ADDNOTE_4].key) { if (key ==shortcuts[SHRT_ADDNOTE_1].key) { newItem(newItem(cursorPos.x(), cursorPos.y(), ourDrumMap[cursorPos.y()].lv1),false,true); } else if(key ==shortcuts[SHRT_ADDNOTE_2].key) { newItem(newItem(cursorPos.x(), cursorPos.y(), ourDrumMap[cursorPos.y()].lv2),false,true); } else if(key ==shortcuts[SHRT_ADDNOTE_3].key) { newItem(newItem(cursorPos.x(), cursorPos.y(), ourDrumMap[cursorPos.y()].lv3),false,true); } else if(key ==shortcuts[SHRT_ADDNOTE_4].key) { newItem(newItem(cursorPos.x(), cursorPos.y(), ourDrumMap[cursorPos.y()].lv4),false,true); } cursorPos.setX(getNextStep(cursorPos.x(),1, _stepSize)); selectCursorEvent(getEventAtCursorPos()); if (mapx(cursorPos.x()) < 0 || mapx(cursorPos.x()) > width()) emit followEvent(cursorPos.x()); return; } } EventCanvas::keyPress(event); } //--------------------------------------------------------- // keyRelease //--------------------------------------------------------- void DrumCanvas::keyRelease(QKeyEvent* event) { if (_tool == CursorTool) { if (_playEvents) { int key = event->key(); if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) key += Qt::SHIFT; if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) key += Qt::ALT; if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) key+= Qt::CTRL; if (key == shortcuts[SHRT_ADDNOTE_1].key || key == shortcuts[SHRT_ADDNOTE_2].key || key == shortcuts[SHRT_ADDNOTE_3].key || key == shortcuts[SHRT_ADDNOTE_4].key) { // Must stop note that was played. stopPlayEvents(); return; } } } EventCanvas::keyRelease(event); } //--------------------------------------------------------- // setTool2 //--------------------------------------------------------- void DrumCanvas::setTool2(int) { if (_tool == CursorTool) deselectAll(); if (unsigned(cursorPos.x()) < curPart->tick()) cursorPos.setX(curPart->tick()); update(); } //--------------------------------------------------------- // setCurDrumInstrument //--------------------------------------------------------- void DrumCanvas::setCurDrumInstrument(int i) { cursorPos.setY(i); update(); } //--------------------------------------------------------- // setStep //--------------------------------------------------------- void DrumCanvas::setStep(int v) { _stepSize=v; } //--------------------------------------------------------- // getEventAtCursorPos //--------------------------------------------------------- const MusECore::Event* DrumCanvas::getEventAtCursorPos() { if (_tool != CursorTool) return 0; if (instrument_map[cursorPos.y()].tracks.contains(curPart->track())) { MusECore::ciEvent lower = curPart->events().lower_bound(cursorPos.x()-curPart->tick()); MusECore::ciEvent upper = curPart->events().upper_bound(cursorPos.x()-curPart->tick()); int curPitch = instrument_map[cursorPos.y()].pitch; for (MusECore::ciEvent i = lower; i != upper; ++i) { const MusECore::Event& ev = i->second; if (ev.isNote() && ev.pitch() == curPitch) return &ev; } } // else or if the for loop didn't find anything return 0; } //--------------------------------------------------------- // selectCursorEvent //--------------------------------------------------------- void DrumCanvas::selectCursorEvent(const MusECore::Event* ev) { for (iCItem i = items.begin(); i != items.end(); ++i) { MusECore::Event e = i->second->event(); if (ev && ev->tick() == e.tick() && ev->pitch() == e.pitch() && e.isNote()) i->second->setSelected(true); else i->second->setSelected(false); } itemSelectionsChanged(); } //--------------------------------------------------------- // midiNote //--------------------------------------------------------- void DrumCanvas::midiNote(int pitch, int velo) { using MusECore::Track; using MusECore::Part; if (debugMsg) printf("DrumCanvas::midiNote: pitch=%i, velo=%i\n", pitch, velo); if (_midiin && _steprec && !MusEGlobal::audio->isPlaying() && velo && !(MusEGlobal::globalKeyState & Qt::AltModifier) /* && pos[0] >= start_tick && pos[0] < end_tick [removed by flo93: this is handled in steprec->record()] */ ) { if (pitch == MusEGlobal::rcSteprecNote) // skip the fancy code below, simply record a rest { if (curPart) steprec->record(curPart,0xdead,0xbeef,editor->raster(),velo,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier, pitch); } else { QSet possible_dest_tracks; Part* rec_part=nullptr; int rec_index=-1; int ourDrumMapSize=getOurDrumMapSize(); int i; for (i=0;itrack()) && ourDrumMap[i].enote==pitch) { rec_part=curPart; rec_index=i; break; } else if (ourDrumMap[i].enote==pitch) possible_dest_tracks.unite(instrument_map[i].tracks); } if (rec_part == nullptr) // if recording to curPart isn't possible { QSet possible_dest_parts = parts_at_tick(pos[0], possible_dest_tracks); if (possible_dest_parts.count() != 1) QMessageBox::warning(this, tr("Recording event failed"), tr("Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous.\nSelect the destination part, then try again.")); else { rec_part = *possible_dest_parts.begin(); Track* dest_track=rec_part->track(); for (i=0;irecord(rec_part,instrument_map[rec_index].pitch,ourDrumMap[rec_index].len,editor->raster(),velo,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier, pitch); } } } int DrumCanvas::pitch_and_track_to_instrument(int pitch, MusECore::Track* track) { for (int i=0; i& tracks=instrument_map[instrument].tracks; int index=instrument_map[instrument].pitch; MusECore::DrumMapTrackOperation* dmop = new MusECore::DrumMapTrackOperation; dmop->_isReset = isReset; dmop->_includeDefault = includeDefault; dmop->_doWholeMap = doWholeMap; dmop->_isInstrumentMod = isInstrumentMod; MusECore::PendingOperationList operations; MusECore::Track* t; for(QSet::const_iterator it = tracks.begin(); it != tracks.end(); it++) { t = *it; if(!t->isDrumTrack()) continue; MusECore::MidiTrack* mt = static_cast(t); dmop->_tracks.push_back(mt); } if(isReset) dmop->_workingItemList.add(index, MusECore::WorkingDrumMapEntry(MusECore::DrumMap(), fields)); // Fixme: Dummy map. Should just be fields. else dmop->_workingItemList.add(index, MusECore::WorkingDrumMapEntry(ourDrumMap[instrument], fields)); operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ModifyTrackDrumMapItem)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } int DrumCanvas::isWorkingMapInstrument(int instr, int fields) const { const QSet& tracks=instrument_map[instr].tracks; int index=instrument_map[instr].pitch; MusECore::Track* t; MusECore::MidiTrack* mt; int ret = MusECore::WorkingDrumMapEntry::NoOverride; for (QSet::const_iterator it = tracks.begin(); it != tracks.end(); it++) { t = *it; if(!t->isDrumTrack()) continue; mt = static_cast(t); // Don't pass a patch - ask it to take care of patch number for us. ret |= mt->isWorkingMapItem(index, fields); } return ret; } bool DrumCanvas::hasOverrides(int instr) const { const QSet& tracks=instrument_map[instr].tracks; MusECore::Track* t; MusECore::MidiTrack* mt; for (QSet::const_iterator it = tracks.begin(); it != tracks.end(); it++) { t = *it; if(!t->isDrumTrack()) continue; mt = static_cast(t); if(!mt->workingDrumMap()->empty()) return true; } return false; } void DrumCanvas::resetOverridesForAllPatches(int instr) { if(QMessageBox::warning(this, tr("Drum map"), tr("Reset the track's drum map with instrument defaults?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok) { MusECore::PendingOperationList operations; const QSet& tracks=instrument_map[instr].tracks; MusECore::Track* t; MusECore::MidiTrack* mt; MusECore::WorkingDrumMapPatchList* new_wdmpl; MusECore::DrumMapTrackPatchReplaceOperation* dmop; for (QSet::const_iterator it = tracks.begin(); it != tracks.end(); it++) { t = *it; if(!t->isDrumTrack()) continue; mt = static_cast(t); if(!mt->workingDrumMap()->empty()) { // Completely blank replacement list. new_wdmpl = new MusECore::WorkingDrumMapPatchList(); // The allocated WorkingDrumMapPatchList wdmpl will become the new list and the // original lists will be deleted, in the operation following. dmop = new MusECore::DrumMapTrackPatchReplaceOperation; dmop->_isInstrumentMod = false; // Not instrument operation. dmop->_workingItemPatchList = new_wdmpl; dmop->_track = static_cast(t); operations.add(MusECore::PendingOperationItem(dmop, MusECore::PendingOperationItem::ReplaceTrackDrumMapPatchList)); } } if(!operations.empty()) MusEGlobal::audio->msgExecutePendingOperations(operations, true); } } void DrumCanvas::rebuildOurDrumMap() { using MusECore::drummaps_almost_equal; using MusECore::Track; using MusECore::MidiTrack; using MusECore::TrackList; using MusECore::ciTrack; using MusECore::ciPart; using MusECore::DrumMap; using MusEGlobal::global_drum_ordering_t; using MusEGlobal::global_drum_ordering; //fprintf(stderr, "DrumCanvas::rebuildOurDrumMap\n"); bool need_update = false; TrackList* tl=MusEGlobal::song->tracks(); QList< QSet > track_groups; QVector old_instrument_map = instrument_map; instrument_map.clear(); for (ciTrack track = tl->begin(); track!=tl->end(); track++) { ciPart p_it; for (p_it=drumEditor->parts()->begin(); p_it!=drumEditor->parts()->end(); p_it++) if (p_it->second->track() == *track) break; if (p_it!=drumEditor->parts()->end()) // if *track is represented by some part in this editor { bool inserted=false; switch (drumEditor->group_mode()) { case DrumEdit::GROUP_SAME_CHANNEL: for (QList< QSet >::iterator group=track_groups.begin(); group!=track_groups.end(); group++) if ( ((MidiTrack*)*group->begin())->outChannel() == ((MidiTrack*)*track)->outChannel() && ((MidiTrack*)*group->begin())->outPort() == ((MidiTrack*)*track)->outPort() && (drummaps_almost_equal(((MidiTrack*)*group->begin())->drummap(), ((MidiTrack*)*track)->drummap())) ) { group->insert(*track); inserted=true; break; } break; case DrumEdit::GROUP_MAX: for (QList< QSet >::iterator group=track_groups.begin(); group!=track_groups.end(); group++) if (drummaps_almost_equal(((MidiTrack*)*group->begin())->drummap(), ((MidiTrack*)*track)->drummap())) { group->insert(*track); inserted=true; break; } break; case DrumEdit::DONT_GROUP: inserted=false; break; default: printf("THIS SHOULD NEVER HAPPEN: group_mode() is invalid!\n"); inserted=false; } if (!inserted) { QSet temp; temp.insert(*track); track_groups.push_back(temp); } } } // from now, we assume that every track_group's entry only groups tracks with identical // drum maps, but not necessarily identical hide-lists together. QVector< std::pair > ignore_order_entries; for (global_drum_ordering_t::iterator order_it=global_drum_ordering.begin(); order_it!=global_drum_ordering.end(); order_it++) { // if this entry should be ignored, ignore it. if (ignore_order_entries.contains(*order_it)) continue; // look if we have order_it->first (the MidiTrack*) in any of our track groups QList< QSet >::iterator group; for (group=track_groups.begin(); group!=track_groups.end(); group++) if (group->contains(order_it->first)) break; if (group!=track_groups.end()) // we have { int pitch=order_it->second; bool mute=true; bool hidden=true; if (drumEditor->ignore_hide()) hidden=false; for (QSet::iterator track=group->begin(); track!=group->end() && (mute || hidden); track++) { if (dynamic_cast(*track)->drummap()[pitch].mute == false) mute=false; if (dynamic_cast(*track)->drummap()[pitch].hide == false) hidden=false; } if (!hidden) { for (QSet::iterator track=group->begin(); track!=group->end(); track++) { DrumMap* dm = &dynamic_cast(*track)->drummap()[pitch]; if (dm->mute != mute) { dm->mute=mute; need_update = true; } } instrument_map.append(instrument_number_mapping_t(*group, pitch)); } for (QSet::iterator track=group->begin(); track!=group->end(); track++) ignore_order_entries.append(std::pair(dynamic_cast(*track), pitch)); } // else ignore it } // maybe delete and then populate ourDrumMap if (must_delete_our_drum_map && ourDrumMap!=nullptr) delete [] ourDrumMap; int size = instrument_map.size(); ourDrumMap=new DrumMap[size]; must_delete_our_drum_map=true; Track* t; MidiTrack* mt; int index; for (int i=0;iisMidiTrack()) continue; mt = static_cast(t); index = instrument_map[i].pitch; ourDrumMap[i] = mt->drummap()[index]; } if (instrument_map!=old_instrument_map) { if (debugMsg) printf("rebuilt drummap and instrument map, size is now %i\n",size); songChanged(SC_EVENT_INSERTED); // force an update of the itemlist emit ourDrumMapChanged(true); } else emit ourDrumMapChanged(false); if (need_update) MusEGlobal::song->update(SC_DRUMMAP, true); // i know, this causes a recursion, which possibly // isn't the most elegant solution here. but it will // never be an infinite recursion } void DrumCanvas::mouseMove(QMouseEvent* event) { EventCanvas::mouseMove(event); if (MusEGlobal::config.showNoteTooltips) showNoteTooltip(event); if (MusEGlobal::config.showStatusBar) showStatusTip(event); } void DrumCanvas::showNoteTooltip(QMouseEvent* event) { static CItem* hoverItem = nullptr; if (!(_tool & (MusEGui::PointerTool | MusEGui::PencilTool | MusEGui::RubberTool | MusEGui::CursorTool))) return; CItem* item = findCurrentItem(event->pos()); if (item && hoverItem == item) return; // careful, drum list can be hidden/reduced by various display options // int pitch = drumEditor->get_instrument_map()[y2pitch(event->pos().y())].pitch; const auto& imap = drumEditor->get_instrument_map(); if (imap.isEmpty()) return; const int ipitch = y2pitch(event->pos().y()); if (ipitch < 0 || ipitch >= imap.size()) return; const int pitch = imap.at(ipitch).pitch; QString str; if (track()->drummap()[pitch].name.isEmpty()) str = MusECore::pitch2string(pitch) + " (" + QString::number(pitch) + ")"; else str = track()->drummap()[pitch].name + " (" + MusECore::pitch2string(pitch) + "/" + QString::number(pitch) + ")"; if (item) { hoverItem = item; MusECore::Pos start(item->event().tick() + item->part()->tick()); int bar, beat, tick, hour, min, sec, msec; start.mbt(&bar, &beat, &tick); QString str_bar = QString("%1.%2.%3") .arg(bar + 1, 4, 10, QLatin1Char('0')) .arg(beat + 1, 2, 10, QLatin1Char('0')) .arg(tick, 3, 10, QLatin1Char('0')); start.msmu(&hour, &min, &sec, &msec, nullptr); QString str_time = QString("%1:%2:%3.%4") .arg(hour, 2, 10, QLatin1Char('0')) .arg(min, 2, 10, QLatin1Char('0')) .arg(sec, 2, 10, QLatin1Char('0')) .arg(msec, 3, 10, QLatin1Char('0')); str = tr("Note: ") + str + "\n" + tr("Velocity: ") + QString::number(item->event().velo()) + "\n" + tr("Start (bar): ") + str_bar + "\n" + tr("Start (time): ") + str_time; } else { hoverItem = nullptr; } QToolTip::showText(QPoint(event->globalX(), event->globalY() + 10), str); } void DrumCanvas::showStatusTip(QMouseEvent* event) { static CItem* hoverItem = nullptr; static Tool localTool; CItem* item = findCurrentItem(event->pos()); if (item) { if (hoverItem == item && localTool == _tool) return; hoverItem = item; localTool = _tool; QString s; if (_tool & (MusEGui::PointerTool )) s = tr("LMB: Select/Move | CTRL+LMB: Multi select/Move© | SHIFT+LMB: Select pitch | MMB: Delete"); else if (_tool & (MusEGui::PencilTool)) s = tr("LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete"); else if (_tool & (MusEGui::RubberTool)) s = tr("LMB: Delete"); else if (_tool & (MusEGui::CursorTool)) s = tr("Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete."); if (!s.isEmpty()) MusEGlobal::muse->setStatusBarText(s); } else { if (hoverItem != nullptr) { MusEGlobal::muse->clearStatusBarText(); hoverItem = nullptr; } } } void DrumCanvas::setCursor() { // Avoid duplication, just do it below. //showCursor(); switch (drag) { case DRAGX_MOVE: case DRAGX_COPY: case DRAGX_CLONE: // Make sure to do this. showCursor(); QWidget::setCursor(*pencilMoveHorizCursor); break; case DRAGY_MOVE: case DRAGY_COPY: case DRAGY_CLONE: // Make sure to do this. showCursor(); QWidget::setCursor(*pencilMoveVertCursor); break; case DRAG_MOVE: case DRAG_COPY: case DRAG_CLONE: // Make sure to do this. showCursor(); QWidget::setCursor(*pencilMove4WayCursor); break; case DRAG_RESIZE: // Make sure to do this. showCursor(); QWidget::setCursor(*pencilMoveHorizCursor); break; default: // Let the Canvas handle it, and call showCursor(). Canvas::setCursor(); break; } } //--------------------------------------------------------- // setMouseOverItemCursor //--------------------------------------------------------- void DrumCanvas::setMouseOverItemCursor() { //showCursor(); QWidget::setCursor(*pencilMove4WayCursor); } } // namespace MusEGui muse-4.0.0/src/muse/midiedit/dcanvas.h000066400000000000000000000172261404105311000176010ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dcanvas.h,v 1.8.2.2 2009/02/02 21:38:00 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DCANVAS_H__ #define __DCANVAS_H__ #include "ecanvas.h" #include "song.h" #include "steprec.h" #include #include #include #define TH 18 class QResizeEvent; class QDragEnterEvent; class QDropEvent; class QDragMoveEvent; class QDragLeaveEvent; namespace MusECore { struct DrumMap; } namespace MusEGui { class MidiEditor; class DrumEdit; //--------------------------------------------------------- // DEvent // ''visual'' Drum Event //--------------------------------------------------------- class DEvent : public EItem { public: DEvent(MusECore::Event e, MusECore::Part* p, int instr); }; struct instrument_number_mapping_t //FINDMICH TODO move into a suitable namespace! { QSet tracks; int pitch; instrument_number_mapping_t() { pitch=-1; tracks.clear(); } instrument_number_mapping_t(const QSet& tr, int p) { tracks=tr; pitch=p; } bool operator==(const instrument_number_mapping_t& that) const //TODO maybe compare the Track* serial numbers, not the pointers themselves? { return (this->tracks == that.tracks && this->pitch==that.pitch); } bool operator!=(const instrument_number_mapping_t& that) { return !operator==(that); } }; //--------------------------------------------------------- // DrumCanvas //--------------------------------------------------------- class DrumCanvas : public EventCanvas { Q_OBJECT MusECore::DrumMap* ourDrumMap; bool must_delete_our_drum_map; //FINDMICH really delete it! QVector instrument_map; DrumEdit* drumEditor; MusECore::StepRec* steprec; // Cursor tool position QPoint cursorPos; int _stepSize; virtual void drawCanvas(QPainter&, const QRect&, const QRegion& = QRegion()) override; virtual void drawItem(QPainter&, const CItem*, const QRect&, const QRegion& = QRegion()) override; void drawTopItem(QPainter& p, const QRect& rect, const QRegion& = QRegion()) override; virtual void drawMoving(QPainter&, const CItem*, const QRect&, const QRegion& = QRegion()) override; virtual MusECore::Undo moveCanvasItems(CItemMap&, int, int, DragType, bool rasterize = true) override; virtual bool moveItem(MusECore::Undo&, CItem*, const QPoint&, DragType, bool rasterize = true) override; virtual CItem* newItem(const QPoint&, int) override; virtual void resizeItem(CItem*, bool, bool) override { } // Non-virt width is meaningless, such as drums. virtual void newItem(CItem*, bool) override; virtual void newItem(CItem*, bool, bool replace ); virtual bool deleteItem(CItem*) override; virtual void itemPressed(const CItem*) override; virtual void itemReleased(const CItem*, const QPoint& /*oldpos*/) override; virtual void itemMoving(const CItem*, const QPoint& newMP) override; virtual void itemMoved(const CItem*, const QPoint& oldMP) override; CItem* newItem(int tick, int instrument, int velocity); bool index2Note(int index, int* port, int* channel, int* note); int y2pitch(int y) const override; int pitch2y(int pitch) const override; inline int y2height(int) const override { return TH; } inline int yItemOffset() const override { return -TH/2; } void startDrag(CItem*, DragType) override; void dragEnterEvent(QDragEnterEvent* event) override; void dragMoveEvent(QDragMoveEvent*) override; void dragLeaveEvent(QDragLeaveEvent*) override; virtual CItem* addItem(MusECore::Part*, const MusECore::Event&) override; virtual void resizeEvent(QResizeEvent*) override; virtual void curPartChanged() override; int getNextStep(unsigned int pos, int basicStep, int stepSize=1); void mouseMove(QMouseEvent* event) override; void showNoteTooltip(QMouseEvent* event); void showStatusTip(QMouseEvent *event); protected: void setCursor() override; void setMouseOverItemCursor() override; signals: void newWidth(int); void ourDrumMapChanged(bool /*instrumentMap changed as well?*/); private slots: void midiNote(int pitch, int velo); public slots: void mapChanged(int, int); void keyPressed(int, int); void keyReleased(int, bool); void setTool2(int); void setCurDrumInstrument(int); virtual void setStep(int); public: enum { CMD_CUT, CMD_COPY, CMD_COPY_RANGE, CMD_PASTE, CMD_PASTE_TO_CUR_PART, CMD_PASTE_DIALOG, CMD_SELECT_ALL, CMD_SELECT_NONE, CMD_SELECT_INVERT, CMD_SELECT_ILOOP, CMD_SELECT_OLOOP, CMD_SELECT_PREV_PART, CMD_SELECT_NEXT_PART, CMD_DEL, CMD_FIXED_LEN, CMD_RIGHT, CMD_LEFT, CMD_RIGHT_NOSNAP, CMD_LEFT_NOSNAP, CMD_MODIFY_VELOCITY, CMD_CRESCENDO, CMD_QUANTIZE, CMD_ERASE_EVENT, CMD_NOTE_SHIFT, CMD_DELETE_OVERLAPS, CMD_GROUP_NONE, CMD_GROUP_CHAN, CMD_GROUP_MAX, CMD_RANGE_TO_SELECTION }; DrumCanvas(MidiEditor*, QWidget*, int, int, const char* name = 0); virtual ~DrumCanvas(); void cmd(int); virtual void modifySelected(NoteInfo::ValType type, int val, bool delta_mode = true) override; virtual void keyPress(QKeyEvent* event) override; virtual void keyRelease(QKeyEvent* event) override; const MusECore::Event* getEventAtCursorPos(); void selectCursorEvent(const MusECore::Event* ev); int pitch_and_track_to_instrument(int pitch, MusECore::Track* track); // Returns OR'd WorkingDrumMapEntry::OverrideType flags indicating whether a map item's members, // given by 'fields' (OR'd WorkingDrumMapEntry::Fields), are either the original or working map item. // Here in DrumCanvas the flags can be NoOverride, TrackOverride, and InstrumentOverride. int isWorkingMapInstrument(int instr, int fields) const; // Returns true if any of the instrument's group tracks have non-empty working lists. bool hasOverrides(int instr) const; // Resets all overrides in all patches (clears all override lists), in the instrument's group tracks. void resetOverridesForAllPatches(int instr); MusECore::DrumMap* getOurDrumMap() { return ourDrumMap; } int getOurDrumMapSize() { return instrument_map.size(); } QVector& get_instrument_map() { return instrument_map; } void propagate_drummap_change(int instrument, int fields, bool isReset, bool includeDefault, bool isInstrumentMod, bool doWholeMap); void rebuildOurDrumMap(); DrumEdit* drumEdit() { return drumEditor; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/midiedit/dlist.cpp000066400000000000000000002327731404105311000176420ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dlist.cpp,v 1.9.2.7 2009/10/16 21:50:16 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include #include #include "globals.h" #include "globaldefs.h" #include "gconfig.h" #include "app.h" #include "audio.h" #include "midiport.h" #include "drummap.h" #include "drumedit.h" #include "helper.h" #include "icons.h" #include "dlist.h" #include "song.h" #include "dcanvas.h" #include "minstrument.h" namespace MusEGui { //--------------------------------------------------------- // DLineEdit //--------------------------------------------------------- DLineEdit::DLineEdit(QWidget* parent) : QLineEdit(parent) { // Reset these since our parent will typically turn them on for speed. setAutoFillBackground(true); setAttribute(Qt::WA_NoSystemBackground, false); setAttribute(Qt::WA_StaticContents, false); setAttribute(Qt::WA_OpaquePaintEvent, false); setAlignment(Qt::AlignLeft | Qt::AlignVCenter); setContentsMargins(0, 0, 0, 0); } bool DLineEdit::event(QEvent* e) { switch(e->type()) { case QEvent::KeyPress: { QKeyEvent* ke = static_cast(e); switch(ke->key()) { // For return, we want to close the editor but don't want the // parent to receive the event which will just open the box again. case Qt::Key_Return: case Qt::Key_Enter: e->accept(); emit returnPressed(); return true; break; case Qt::Key_Escape: { e->accept(); emit escapePressed(); return true; } break; default: break; } } break; case QEvent::NonClientAreaMouseButtonPress: // FIXME: Doesn't work. //fprintf(stderr, "DLineEdit::event NonClientAreaMouseButtonPress\n"); case QEvent::FocusOut: e->accept(); emit returnPressed(); return true; break; default: break; } // Do not pass ANY events on to the parent. QLineEdit::event(e); e->accept(); return true; } //--------------------------------------------------------- // DrumListSpinBox //--------------------------------------------------------- DrumListSpinBox::DrumListSpinBox(QWidget* parent) : QSpinBox(parent) { // Reset these since our parent will typically turn them on for speed. setAutoFillBackground(true); setAttribute(Qt::WA_NoSystemBackground, false); setAttribute(Qt::WA_StaticContents, false); setAttribute(Qt::WA_OpaquePaintEvent, false); setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); setContentsMargins(0, 0, 0, 0); } bool DrumListSpinBox::event(QEvent* e) { switch(e->type()) { case QEvent::KeyPress: { QKeyEvent* ke = static_cast(e); switch(ke->key()) { // For return, we want to close the editor but don't want the // parent to receive the event which will just open the box again. case Qt::Key_Return: case Qt::Key_Enter: e->accept(); emit returnPressed(); return true; break; case Qt::Key_Escape: { e->accept(); emit escapePressed(); return true; } break; default: break; } } break; case QEvent::NonClientAreaMouseButtonPress: // FIXME: Doesn't work. //fprintf(stderr, "DLineEdit::event NonClientAreaMouseButtonPress\n"); case QEvent::FocusOut: e->accept(); emit returnPressed(); return true; break; default: break; } // Do not pass ANY events on to the parent. QSpinBox::event(e); e->accept(); return true; } //--------------------------------------------------------- // DPitchEdit //--------------------------------------------------------- DPitchEdit::DPitchEdit(QWidget* parent) : PitchEdit(parent) { // Reset these since our parent will typically turn them on for speed. setAutoFillBackground(true); setAttribute(Qt::WA_NoSystemBackground, false); setAttribute(Qt::WA_StaticContents, false); setAttribute(Qt::WA_OpaquePaintEvent, false); setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); setContentsMargins(0, 0, 0, 0); } bool DPitchEdit::event(QEvent* e) { switch(e->type()) { case QEvent::KeyPress: { QKeyEvent* ke = static_cast(e); switch(ke->key()) { // For return, we want to close the editor but don't want the // parent to receive the event which will just open the box again. case Qt::Key_Return: case Qt::Key_Enter: e->accept(); emit returnPressed(); return true; break; case Qt::Key_Escape: { e->accept(); emit escapePressed(); return true; } break; default: break; } } break; case QEvent::NonClientAreaMouseButtonPress: // FIXME: Doesn't work. //fprintf(stderr, "DPitchEdit::event NonClientAreaMouseButtonPress\n"); case QEvent::FocusOut: e->accept(); emit returnPressed(); return true; break; default: break; } // Do not pass ANY events on to the parent. PitchEdit::event(e); e->accept(); return true; } //--------------------------------------------------------- // draw //--------------------------------------------------------- void DList::draw(QPainter& p, const QRect& mr, const QRegion&) { using MusECore::WorkingDrumMapEntry; const QRect ur = mapDev(mr); int ux = ur.x(); int uy = ur.y(); int uw = ur.width(); int uh = ur.height(); //--------------------------------------------------- // Tracks //--------------------------------------------------- p.setPen(MusEGlobal::config.drumListFont); QColor override_col(Qt::gray); override_col.setAlpha(_alphaOverlay); QFont fnt(p.font()); QRect rtmp = map(QRect(0, 0, 0, TH)); if (rtmp.height() < TH) fnt.setPixelSize(rtmp.height() - 1); for (int instrument = 0; instrument < ourDrumMapSize; ++instrument) { int yy = instrument * TH; if (yy+TH < uy) continue; if (yy > uy + uh) break; MusECore::DrumMap* dm = &ourDrumMap[instrument]; if (dm == currentlySelected) { p.fillRect(ux, yy, uw, TH, MusEGlobal::config.drumListSel); p.setPen(MusEGlobal::config.drumListSelFont); } else if(instrument == curPitch) { p.fillRect(ux, yy, uw, TH, MusEGlobal::config.drumListSel.darker()); // TODO Create a new colour. p.setPen(MusEGlobal::config.drumListSelFont); // TODO Would likely need its own colour too. } else { p.setPen(MusEGlobal::config.drumListFont); } // else // p.eraseRect(x, yy, w, TH); DELETETHIS? QHeaderView *h = header; p.save(); p.setWorldMatrixEnabled(false); for (int k = 0; k < h->count(); ++k) { if (h->isSectionHidden(k)) continue; int isWorkingItem = MusECore::WorkingDrumMapEntry::NoOverride; int x = h->sectionPosition(k); int w = h->sectionSize(k); //QRect r = p.combinedTransform().mapRect(QRect(x+2, yy, w-4, TH)); // Gives inconsistent positions. Source shows wrong operation for our needs. QRect r = map(QRect(x, yy, w, TH)); // Use our own map instead. QString s; int align = Qt::AlignVCenter | Qt::AlignHCenter; bool doOverrideFill = true; switch (k) { case COL_VOLUME: s.setNum(dm->vol); if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::VolField); break; case COL_QUANT: s.setNum(dm->quant); if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::QuantField); break; case COL_NOTELENGTH: s.setNum(dm->len); if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::LenField); break; case COL_NOTE: s = MusECore::pitch2string(dm->anote); if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::ANoteField); break; case COL_INPUTTRIGGER: s = MusECore::pitch2string(dm->enote); if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::ENoteField); break; case COL_LEVEL1: s.setNum(dm->lv1); if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::Lv1Field); break; case COL_LEVEL2: s.setNum(dm->lv2); if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::Lv2Field); break; case COL_LEVEL3: s.setNum(dm->lv3); if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::Lv3Field); break; case COL_LEVEL4: s.setNum(dm->lv4); if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::Lv4Field); break; case COL_HIDE: { if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::HideField); if(isWorkingItem == MusECore::WorkingDrumMapEntry::NoOverride) p.fillRect(r, override_col); doOverrideFill = false; if(dcanvas) { bool hidden=false; bool shown=false; QSet* group = &dcanvas->get_instrument_map()[instrument].tracks; int pitch = dcanvas->get_instrument_map()[instrument].pitch; for (QSet::iterator track=group->begin(); track!=group->end() && !(hidden&&shown); track++) // if (dynamic_cast(*track)->drummap_hidden()[pitch]) if (dynamic_cast(*track)->drummap()[pitch].hide) hidden=true; else shown=true; if (!hidden && !shown) printf("THIS SHOULD NEVER HAPPEN: in DList::draw(): instrument %i's track group is empty. strange...\n", instrument); const QIcon* pm = nullptr; if (shown && !hidden) pm = eyeSVGIcon; else if (!shown && hidden) pm = eyeCrossedSVGIcon; else if (shown && hidden) pm = eyeGreySVGIcon; else //if (!shown && !hidden) pm = nullptr; if (pm) pm->paint(&p, r.x(), r.y(), r.width(), r.height()); } else { const QIcon* pm = dm->hide ? eyeCrossedSVGIcon : eyeSVGIcon; pm->paint(&p, r.x(), r.y(), r.width(), r.height()); } break; } case COL_MUTE: if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::MuteField); doOverrideFill = false; if(isWorkingItem == MusECore::WorkingDrumMapEntry::NoOverride) p.fillRect(r, override_col); if (dm->mute) { muteOnSVGIcon->paint(&p, r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2); } break; case COL_NAME: { if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::NameField); doOverrideFill = false; if(isWorkingItem == MusECore::WorkingDrumMapEntry::NoOverride) p.fillRect(r, override_col); if(dcanvas && dcanvas->part()) { MusECore::Part* cur_part = dcanvas->part(); if(cur_part->track() && cur_part->track()->isMidiTrack()) { MusECore::MidiTrack* cur_track = static_cast(cur_part->track()); int cur_channel = cur_track->outChannel(); MusECore::MidiPort* cur_port = &MusEGlobal::midiPorts[cur_track->outPort()]; QSet* group = &dcanvas->get_instrument_map()[instrument].tracks; bool found = false; bool used = false; bool off = true; // REMOVE Tim. Or FIXME. An attempt to light the dots while respecting grouping. //if(!group->empty()) { // int group_size = group->size(); // if(group_size == 1) // { // MusECore::Track* t = *group->begin(); // MusECore::PartList* part_list = dcanvas->drumEdit()->parts(); // for(MusECore::ciPart ip = part_list->cbegin(); ip != part_list->cend(); ++ip) { // if(ip->second->track() == t) { // } // } // } int instr_pitch = dcanvas->get_instrument_map()[instrument].pitch; //if(//dcanvas->drumEdit()->group_mode() == DrumEdit::DONT_GROUP || //dcanvas->drumEdit()->group_mode() == DrumEdit::GROUP_MAX || //dcanvas->drumEdit()->group_mode() == DrumEdit::GROUP_SAME_CHANNEL || //group_size >= 2 && group->find(cur_track) != group->end()) //for(QSet::iterator it = group->begin(); it != group->end(); ++it) if(group->find(cur_track) != group->end()) { //if(!(*it)->isMidiTrack()) // continue; //MusECore::MidiTrack* track = static_cast(*it); //MusECore::MidiPort* mp = &MusEGlobal::midiPorts[track->outPort()]; //MusECore::MidiCtrlValListList* cll = mp->controller(); MusECore::MidiCtrlValListList* cll = cur_port->controller(); //int channel = track->outChannel(); //const int min = channel << 24; const int min = cur_channel << 24; const int max = min + 0x1000000; for(MusECore::iMidiCtrlValList imcvl = cll->lower_bound(min); imcvl != cll->lower_bound(max); ++imcvl) { MusECore::MidiCtrlValList* cl = imcvl->second; MusECore::MidiController* c = cur_port->midiController(cl->num(), cur_channel); if(!c->isPerNoteController()) continue; int cnum = c->num(); int num = cl->num(); int pitch = num & 0x7f; if(pitch != instr_pitch) continue; found = true; const MusECore::EventList& el = cur_part->events(); //MusECore::PartList* part_list = dcanvas->drumEdit()->parts(); //for(MusECore::ciPart ip = part_list->cbegin(); ip != part_list->cend(); ++ip) { //MusECore::Part* part = ip->second; ///if(part->track() != //const MusECore::EventList& el = part->events(); for(MusECore::ciEvent ie = el.begin(); ie != el.end(); ++ie) { MusECore::Event e = ie->second; if(e.type() != MusECore::Controller) continue; int ctl_num = e.dataA(); if((ctl_num | 0xff) == cnum && (ctl_num & 0x7f) == pitch) { used = true; break; } } } off = cl->hwVal() == MusECore::CTRL_VAL_UNKNOWN; // Does it have a value or is it 'off'? if(used && !off) break; // We have all the info we need, done. } } if(found) { p.save(); p.setPen(Qt::black); if(used) { if(off) p.setBrush(Qt::green); else p.setBrush(Qt::yellow); } else { if(off) p.setBrush(Qt::gray); else p.setBrush(Qt::blue); } p.drawEllipse(QPointF(r.x()+5, r.y()+r.height()/2), 2.5, 2.5); p.restore(); } } } } QString str = dm->name; align = Qt::AlignVCenter | Qt::AlignLeft; fnt.setItalic(false); fnt.setBold(false); #ifdef _USE_INSTRUMENT_OVERRIDES_ if(isWorkingItem & (MusECore::WorkingDrumMapEntry::TrackOverride | MusECore::WorkingDrumMapEntry::TrackDefaultOverride)) fnt.setBold(true); else if(isWorkingItem & (MusECore::WorkingDrumMapEntry::InstrumentOverride | MusECore::WorkingDrumMapEntry::InstrumentDefaultOverride)) fnt.setItalic(true); #else if(isWorkingItem & (MusECore::WorkingDrumMapEntry::TrackOverride | MusECore::WorkingDrumMapEntry::TrackDefaultOverride)) fnt.setBold(true); if( (isWorkingItem & MusECore::WorkingDrumMapEntry::TrackDefaultOverride) && !(isWorkingItem & MusECore::WorkingDrumMapEntry::TrackOverride)) fnt.setItalic(true); #endif p.setFont(fnt); p.drawText(r.x() + 8, r.y(), r.width() - 8, r.height(), align, str); } break; // Default to track port if -1 and track channel if -1. case COL_OUTCHANNEL: if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::ChanField); if(dm->channel != -1) s.setNum(dm->channel+1); break; case COL_OUTPORT: if(dcanvas) isWorkingItem = dcanvas->isWorkingMapInstrument(instrument, WorkingDrumMapEntry::PortField); if(dm->port >= 0 && dm->port < MusECore::MIDI_PORTS) { // muse can now keep port info for synths that are not properly instantiated. // This seems however to lead to dm->port being set to an abnormal value. // We make a check that it is within bounds before continuing s = QString("%1:%2") .arg(dm->port + 1) .arg(MusEGlobal::midiPorts[dm->port].portname()); } align = Qt::AlignVCenter | Qt::AlignLeft; break; } if(doOverrideFill && isWorkingItem == MusECore::WorkingDrumMapEntry::NoOverride) p.fillRect(r, override_col); if (!s.isEmpty()) { fnt.setItalic(false); fnt.setBold(false); #ifdef _USE_INSTRUMENT_OVERRIDES_ if(isWorkingItem & (MusECore::WorkingDrumMapEntry::TrackOverride | MusECore::WorkingDrumMapEntry::TrackDefaultOverride)) fnt.setBold(true); else if(isWorkingItem & (MusECore::WorkingDrumMapEntry::InstrumentOverride | MusECore::WorkingDrumMapEntry::InstrumentDefaultOverride)) fnt.setItalic(true); #else if(isWorkingItem & (MusECore::WorkingDrumMapEntry::TrackOverride | MusECore::WorkingDrumMapEntry::TrackDefaultOverride)) fnt.setBold(true); if( (isWorkingItem & MusECore::WorkingDrumMapEntry::TrackDefaultOverride) && !(isWorkingItem & MusECore::WorkingDrumMapEntry::TrackOverride)) fnt.setItalic(true); #endif p.setFont(fnt); p.drawText(r, align, s); } } p.restore(); } //--------------------------------------------------- // horizontal lines //--------------------------------------------------- p.setPen(MusEGlobal::config.midiDividerColor); int yy = (uy / TH) * TH; for (; yy < uy + uh; yy += TH) { p.drawLine(ux, yy, ux + uw, yy); } if (drag == DRAG) { int y = (startY/TH) * TH; int dy = startY - y; int y2 = curY - dy; p.setPen(Qt::green); p.drawLine(ux, y2, ux + uw, y2); p.drawLine(ux, y2 + TH, ux + uw, y2 + TH); p.setPen(Qt::gray); } //--------------------------------------------------- // vertical Lines //--------------------------------------------------- p.setWorldMatrixEnabled(false); int n = header->count(); ux = 0; for (int i = 0; i < n; i++) { ux += header->sectionSize(header->visualIndex(i)); p.drawLine(ux, 0, ux, height()); } p.setWorldMatrixEnabled(true); } //--------------------------------------------------------- // devicesPopupMenu //--------------------------------------------------------- bool DList::devicesPopupMenu(MusECore::DrumMap* t, int x, int y) { // Include a "" entry. Do not pass parent! Causes accelerators to be returned in QAction::text() ! QMenu* p = MusEGui::midiPortsPopup(nullptr, t->port, true); QAction* act = p->exec(mapToGlobal(QPoint(x, y)), nullptr); if(!act) { delete p; return false; } int n = act->data().toInt(); delete p; const int openConfigId = MusECore::MIDI_PORTS; const int defaultId = MusECore::MIDI_PORTS + 1; if(n < 0 || n > defaultId) // Invalid item. return false; if(n == openConfigId) // Show port config dialog. { MusEGlobal::muse->configMidiPorts(); return false; } if(n == defaultId) // Means the -1 n = -1; bool changed = false; if(n != t->port) { t->port = n; // -1 is allowed changed = true; } return changed; } //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- void DList::viewMousePressEvent(QMouseEvent* ev) { ev->accept(); int x = ev->x(); int y = ev->y(); int button = ev->button(); int instrument = y / TH; if (instrument >= ourDrumMapSize) instrument=ourDrumMapSize-1; if (instrument < 0) instrument=0; if (ourDrumMapSize==0) return; setCurDrumInstrument(instrument); MusECore::DrumMap* dm = &ourDrumMap[instrument]; MusECore::DrumMap dm_old = *dm; // restrict to left button, otherwise drag starts after every context menu call if(button == Qt::LeftButton) { startY = y; sInstrument = instrument; drag = START_DRAG; } DrumColumn col = DrumColumn(x2col(x)); if(button == Qt::RightButton) { if(dcanvas) { enum MapPopupIDs { HideInstrumentID = 0, ShowInstrumentID, ResetFieldID, ResetItemID, ResetColumnID, ResetMapID, SetFieldID, SetItemID, SetColumnID, SetMapID, SetDefaultFieldID, SetDefaultItemID, SetDefaultColumnID, SetDefaultMapID, ResetAllPatchMapsID, #ifdef _USE_INSTRUMENT_OVERRIDES_ ResetInstrumentFieldID, ResetInstrumentItemID, ResetInstrumentMapID, SetInstrumentFieldID, SetInstrumentItemID, SetInstrumentMapID #endif }; const int field = col2Field(col); const int overrides = dcanvas->isWorkingMapInstrument(instrument, field); const bool has_overrides = dcanvas->hasOverrides(instrument); const bool track_override = overrides & MusECore::WorkingDrumMapEntry::TrackOverride; const bool track_def_override = overrides & MusECore::WorkingDrumMapEntry::TrackDefaultOverride; #ifdef _USE_INSTRUMENT_OVERRIDES_ const bool instr_override = overrides & MusECore::WorkingDrumMapEntry::InstrumentOverride; const bool instr_def_override = overrides & MusECore::WorkingDrumMapEntry::InstrumentDefaultOverride; #endif const int all_fields_overrides = dcanvas->isWorkingMapInstrument(instrument, MusECore::WorkingDrumMapEntry::AllFields); const bool all_fields_track_override = all_fields_overrides & MusECore::WorkingDrumMapEntry::TrackOverride; const bool all_fields_track_def_override = all_fields_overrides & MusECore::WorkingDrumMapEntry::TrackDefaultOverride; #ifdef _USE_INSTRUMENT_OVERRIDES_ const bool all_fields_instr_override = all_fields_overrides & MusECore::WorkingDrumMapEntry::InstrumentOverride; const bool all_fields_instr_def_override = all_fields_overrides & MusECore::WorkingDrumMapEntry::InstrumentDefaultOverride; #endif bool hidden=false; bool shown=false; QSet* group = &dcanvas->get_instrument_map()[instrument].tracks; int pitch = dcanvas->get_instrument_map()[instrument].pitch; for (QSet::iterator track=group->begin(); track!=group->end() && !(hidden&&shown); track++) if (dynamic_cast(*track)->drummap()[pitch].hide) hidden=true; else shown=true; QMenu* popup = new QMenu(nullptr /* intendedly not "this" */); popup->setToolTipsVisible(true); QAction* act = popup->addAction(tr("Hide This Instrument")); if(!shown) act->setEnabled(false); act->setData(HideInstrumentID); act->setToolTip(tr("This turns a blue eye into a crossed eye")); act = popup->addAction(tr("Show This Instrument")); if(!hidden) act->setEnabled(false); act->setData(ShowInstrumentID); act->setToolTip(tr("This turns a crossed eye into a blue eye")); popup->addSeparator(); act = popup->addAction(tr("Set Field")); //act->setEnabled(field != MusECore::WorkingDrumMapEntry::ENoteField); act->setData(SetFieldID); act->setToolTip(tr("Sets a field")); act = popup->addAction(tr("Set Row")); //act->setEnabled(field != MusECore::WorkingDrumMapEntry::ENoteField); act->setData(SetItemID); act->setToolTip(tr("Sets a row")); act = popup->addAction(tr("Set Column")); act->setEnabled(field != MusECore::WorkingDrumMapEntry::ENoteField); act->setData(SetColumnID); act->setToolTip(tr("Sets a whole column to the field")); act = popup->addAction(tr("Set List")); //act->setEnabled(field != MusECore::WorkingDrumMapEntry::ENoteField); act->setData(SetMapID); act->setToolTip(tr("Sets the whole list")); popup->addSeparator(); act = popup->addAction(tr("Reset Field")); act->setEnabled(track_override || track_def_override); act->setData(ResetFieldID); act->setToolTip(tr("Resets a field in a row to default patch or instrument value")); act = popup->addAction(tr("Reset Row")); act->setEnabled(all_fields_track_override || all_fields_track_def_override); act->setData(ResetItemID); act->setToolTip(tr("Resets a row to the instrument values")); act = popup->addAction(tr("Reset Column")); //act->setEnabled(!all_fields_track_def_override); act->setData(ResetColumnID); act->setToolTip(tr("Resets a whole column to the instrument values")); act = popup->addAction(tr("Reset List")); act->setEnabled(has_overrides); act->setData(ResetMapID); act->setToolTip(tr("Resets the whole list to the instrument values")); popup->addSeparator(); act = popup->addAction(tr("Reset Track's Drum List")); act->setEnabled(dcanvas->hasOverrides(instrument)); act->setData(ResetAllPatchMapsID); act->setToolTip(tr("Resets all lists on all patches to the instrument values")); popup->addSeparator(); act = popup->addAction(tr("Promote Field to Default Patch")); act->setData(SetDefaultFieldID); act->setToolTip(tr("Promotes a field in a row to the default patch")); act = popup->addAction(tr("Promote Row to Default Patch")); act->setData(SetDefaultItemID); act->setToolTip(tr("Promotes a row to the default patch")); act = popup->addAction(tr("Promote Column to Default Patch")); act->setData(SetDefaultColumnID); act->setToolTip(tr("Promotes a Column to the Default Patch")); act = popup->addAction(tr("Promote List to Default Patch")); act->setData(SetDefaultMapID); act->setToolTip(tr("Promotes the Whole List to the Default Patch")); #ifdef _USE_INSTRUMENT_OVERRIDES_ popup->addSeparator(); act = popup->addAction(tr("Reset instrument field")); act->setEnabled(instr_override || instr_def_override); act->setData(ResetInstrumentFieldID); act = popup->addAction(tr("Reset instrument row")); act->setEnabled(all_fields_instr_override || all_fields_instr_def_override); act->setData(ResetInstrumentItemID); act = popup->addAction(tr("Reset instrument list")); act->setEnabled(all_fields_instr_override || all_fields_instr_def_override); act->setData(ResetInstrumentMapID); popup->addSeparator(); act = popup->addAction(tr("Set instrument field")); act->setEnabled(track_override); act->setData(SetInstrumentFieldID); act = popup->addAction(tr("Set instrument row")); act->setEnabled(track_override); act->setData(SetInstrumentItemID); act = popup->addAction(tr("Set instrument list")); act->setEnabled(track_override); act->setData(SetInstrumentMapID); #endif int id = -1; QAction* result = popup->exec(ev->globalPos()); if(result) id = result->data().toInt(); delete popup; switch(id) { case HideInstrumentID: dm->hide = true; dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::HideField, false, false, false, false); break; case ShowInstrumentID: dm->hide = false; dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::HideField, false, false, false, false); break; case ResetFieldID: // If there is a track override do not include defaults. This way a track override will be removed // first, then if reset field is clicked again any track default override will removed. dcanvas->propagate_drummap_change(instrument, field, true, !track_override, false, false); //update(); break; case ResetItemID: dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, true, true, false, false); //update(); break; case ResetMapID: dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, true, true, false, true); break; case SetDefaultFieldID: dcanvas->propagate_drummap_change(instrument, field, false, true, false, false); break; case SetDefaultItemID: dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, true, false, false); break; case SetDefaultMapID: dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, true, false, true); break; case SetFieldID: dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); break; case SetItemID: dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, false, false, false); break; case SetColumnID: dcanvas->propagate_drummap_change(instrument, field, false, false, false, true); break; case SetMapID: dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, false, false, true); break; case SetDefaultColumnID: dcanvas->propagate_drummap_change(instrument, field, false, true, false, true); break; case ResetColumnID: dcanvas->propagate_drummap_change(instrument, field, true, true, false, true); break; case ResetAllPatchMapsID: dcanvas->resetOverridesForAllPatches(instrument); break; #ifdef _USE_INSTRUMENT_OVERRIDES_ case ResetInstrumentFieldID: // If there is an instrument override do not include defaults. This way an instrument override will be removed // first, then if reset instrument field is clicked again any instrument default override will removed. dcanvas->propagate_drummap_change(instrument, field, true, false, true, false); //update(); break; case ResetInstrumentItemID: dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, true, true, true, false); //update(); break; case ResetInstrumentMapID: dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, true, true, true, true); break; case SetInstrumentFieldID: dcanvas->propagate_drummap_change(instrument, field, false, false, true, false); //update(); break; case SetInstrumentItemID: dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, false, true, false); //update(); break; case SetInstrumentMapID: dcanvas->propagate_drummap_change(instrument, MusECore::WorkingDrumMapEntry::AllFields, false, false, true, true); break; #endif default: return; } } return; } int field = MusECore::WorkingDrumMapEntry::NoField; switch (col) { case COL_NONE: case COL_VOLUME: case COL_QUANT: case COL_INPUTTRIGGER: case COL_NOTELENGTH: case COL_NOTE: case COL_OUTCHANNEL: // this column isn't visible in new style drum mode case COL_LEVEL1: case COL_LEVEL2: case COL_LEVEL3: case COL_LEVEL4: break; case COL_HIDE: if (button == Qt::LeftButton) { field = MusECore::WorkingDrumMapEntry::HideField; dm->hide = !dm->hide; } break; case COL_MUTE: field = MusECore::WorkingDrumMapEntry::MuteField; if (button == Qt::LeftButton) dm->mute = !dm->mute; break; case COL_OUTPORT: // this column isn't visible in new style drum mode field = MusECore::WorkingDrumMapEntry::PortField; if ((button == Qt::RightButton) || (button == Qt::LeftButton)) { devicesPopupMenu(dm, mapx(x), mapy(instrument * TH)); } break; case COL_NAME: field = MusECore::WorkingDrumMapEntry::NameField; if (button == Qt::LeftButton) { int velo = 127 * (ev->x() - header->sectionPosition(COL_NAME)) / (header->sectionSize(COL_NAME) - 10); if (velo <= 0) velo = 1; // Zero note on vel is not allowed. else if (velo > 127 ) velo = 127; emit keyPressed(instrument, velo); //Mapping done on other side, send index } else if (button == Qt::MidButton && dcanvas) // hide that instrument { dm->hide = true; } break; default: break; } update(); if (dm_old != *dm && dcanvas) //something changed and we're in new style mode? dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); MusEGlobal::song->update(SC_DRUM_SELECTION); } //--------------------------------------------------------- // viewMouseDoubleClickEvent //--------------------------------------------------------- void DList::viewMouseDoubleClickEvent(QMouseEvent* ev) { int x = ev->x(); int y = ev->y(); unsigned instrument = y / TH; int section = header->logicalIndexAt(x); if (section == COL_NAME && (ev->button() == Qt::LeftButton)) { lineEdit(instrument, section); } else if ((section == COL_VOLUME || section == COL_NOTELENGTH || section == COL_LEVEL1 || section == COL_LEVEL2 || section == COL_LEVEL3 || section == COL_LEVEL4 || section == COL_QUANT || section == COL_OUTCHANNEL ) && (ev->button() == Qt::LeftButton)) { valEdit(instrument, section); } else if ((section == COL_NOTE || section == COL_INPUTTRIGGER) && (ev->button() == Qt::LeftButton)) pitchEdit(instrument, section); else viewMousePressEvent(ev); } //--------------------------------------------------------- // lineEdit //--------------------------------------------------------- void DList::lineEdit(int line, int section) { if (line >= ourDrumMapSize) line=ourDrumMapSize-1; if (line < 0) line=0; if (ourDrumMapSize==0) return; MusECore::DrumMap* dm = &ourDrumMap[line]; editEntry = dm; if (editor == 0) { editor = new DLineEdit(this); connect(editor, SIGNAL(returnPressed()), SLOT(returnPressed())); connect(editor, SIGNAL(escapePressed()), SLOT(escapePressed())); editor->setFrame(true); } int colx = mapx(header->sectionPosition(section)); int colw = rmapx(header->sectionSize(section)); int coly = mapy(line * TH); int colh = rmapy(TH); selectedColumn = section; //Store selected column to have an idea of which one was selected when return is pressed switch (section) { case COL_NAME: editor->setText(dm->name); break; } editor->end(false); editor->setGeometry(colx, coly, colw, colh); editor->show(); editor->setFocus(); } //--------------------------------------------------------- // valEdit //--------------------------------------------------------- void DList::valEdit(int line, int section) { if (line >= ourDrumMapSize) line=ourDrumMapSize-1; if (line < 0) line=0; if (ourDrumMapSize==0) return; MusECore::DrumMap* dm = &ourDrumMap[line]; editEntry = dm; if (val_editor == 0) { val_editor = new DrumListSpinBox(this); connect(val_editor, SIGNAL(returnPressed()), SLOT(valEdited())); connect(val_editor, SIGNAL(escapePressed()), SLOT(escapePressed())); val_editor->setFrame(true); } int colx = mapx(header->sectionPosition(section)); int colw = rmapx(header->sectionSize(section)); int coly = mapy(line * TH); int colh = rmapy(TH); selectedColumn = section; //Store selected column to have an idea of which one was selected when return is pressed switch (section) { case COL_VOLUME: val_editor->setRange(0, 250); val_editor->setValue(dm->vol); break; case COL_NOTELENGTH: val_editor->setRange(1, 1000000); val_editor->setValue(dm->len); break; case COL_LEVEL1: // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now. val_editor->setRange(1, 127); val_editor->setValue(dm->lv1); break; case COL_LEVEL2: val_editor->setRange(1, 127); val_editor->setValue(dm->lv2); break; case COL_LEVEL3: val_editor->setRange(1, 127); val_editor->setValue(dm->lv3); break; case COL_LEVEL4: val_editor->setRange(1, 127); val_editor->setValue(dm->lv4); break; case COL_QUANT: val_editor->setRange(0, 1000000); val_editor->setValue(dm->quant); break; case COL_OUTCHANNEL: val_editor->setRange(0, MusECore::MUSE_MIDI_CHANNELS); // Default to track port if -1 and track channel if -1. if(dm->channel != -1) val_editor->setValue(dm->channel+1); break; } val_editor->setGeometry(colx, coly, colw, colh); val_editor->selectAll(); val_editor->show(); val_editor->setFocus(); } //--------------------------------------------------------- // pitchEdit //--------------------------------------------------------- void DList::pitchEdit(int line, int section) { if (line >= ourDrumMapSize) line=ourDrumMapSize-1; if (line < 0) line=0; if (ourDrumMapSize==0) return; MusECore::DrumMap* dm = &ourDrumMap[line]; editEntry = dm; if (pitch_editor == 0) { pitch_editor = new DPitchEdit(this); connect(pitch_editor, SIGNAL(returnPressed()), SLOT(pitchEdited())); connect(pitch_editor, SIGNAL(escapePressed()), SLOT(escapePressed())); connect(MusEGlobal::song, SIGNAL(midiNote(int, int)), pitch_editor, SLOT(midiNote(int,int))); pitch_editor->setFrame(true); } int colx = mapx(header->sectionPosition(section)); int colw = rmapx(header->sectionSize(section)); int coly = mapy(line * TH); int colh = rmapy(TH); selectedColumn = section; //Store selected column to have an idea of which one was selected when return is pressed switch (section) { case COL_INPUTTRIGGER: pitch_editor->setValue(dm->enote); break; case COL_NOTE: pitch_editor->setValue(dm->anote); break; } pitch_editor->setGeometry(colx, coly, colw, colh); pitch_editor->show(); pitch_editor->setFocus(); } //--------------------------------------------------------- // x2col //--------------------------------------------------------- int DList::x2col(int x) const { int col = 0; int w = 0; for (; col < header->count(); col++) { w += header->sectionSize(col); if (x < w) break; } if (col == header->count()) return -1; return header->logicalIndex(col); } int DList::col2Field(int col) const { switch(col) { case COL_NONE: return MusECore::WorkingDrumMapEntry::NoField; case COL_HIDE: return MusECore::WorkingDrumMapEntry::HideField; case COL_MUTE: return MusECore::WorkingDrumMapEntry::MuteField; case COL_NAME: return MusECore::WorkingDrumMapEntry::NameField; case COL_VOLUME: return MusECore::WorkingDrumMapEntry::VolField; case COL_QUANT: return MusECore::WorkingDrumMapEntry::QuantField; case COL_INPUTTRIGGER: return MusECore::WorkingDrumMapEntry::ENoteField; case COL_NOTELENGTH: return MusECore::WorkingDrumMapEntry::LenField; case COL_NOTE: return MusECore::WorkingDrumMapEntry::ANoteField; case COL_OUTCHANNEL: return MusECore::WorkingDrumMapEntry::ChanField; case COL_OUTPORT: return MusECore::WorkingDrumMapEntry::PortField; case COL_LEVEL1: return MusECore::WorkingDrumMapEntry::Lv1Field; case COL_LEVEL2: return MusECore::WorkingDrumMapEntry::Lv2Field; case COL_LEVEL3: return MusECore::WorkingDrumMapEntry::Lv3Field; case COL_LEVEL4: return MusECore::WorkingDrumMapEntry::Lv4Field; } return MusECore::WorkingDrumMapEntry::NoField; } int DList::field2Col(int field) const { switch(field) { case MusECore::WorkingDrumMapEntry::NoField: return COL_NONE; case MusECore::WorkingDrumMapEntry::HideField: return COL_HIDE; case MusECore::WorkingDrumMapEntry::MuteField: return COL_MUTE; case MusECore::WorkingDrumMapEntry::NameField: return COL_NAME; case MusECore::WorkingDrumMapEntry::VolField: return COL_VOLUME; case MusECore::WorkingDrumMapEntry::QuantField: return COL_QUANT; case MusECore::WorkingDrumMapEntry::ENoteField: return COL_INPUTTRIGGER; case MusECore::WorkingDrumMapEntry::LenField: return COL_NOTELENGTH; case MusECore::WorkingDrumMapEntry::ANoteField: return COL_NOTE; case MusECore::WorkingDrumMapEntry::ChanField: return COL_OUTCHANNEL; case MusECore::WorkingDrumMapEntry::PortField: return COL_OUTPORT; case MusECore::WorkingDrumMapEntry::Lv1Field: return COL_LEVEL1; case MusECore::WorkingDrumMapEntry::Lv2Field: return COL_LEVEL2; case MusECore::WorkingDrumMapEntry::Lv3Field: return COL_LEVEL3; case MusECore::WorkingDrumMapEntry::Lv4Field: return COL_LEVEL4; } return COL_NONE; } //--------------------------------------------------------- // setCurDrumInstrument //--------------------------------------------------------- void DList::setCurDrumInstrument(int instr) { if (instr < 0 || instr >= ourDrumMapSize) return; // illegal instrument MusECore::DrumMap* dm = &ourDrumMap[instr]; if (currentlySelected != dm) { currentlySelected = dm; emit curDrumInstrumentChanged(instr); //MusEGlobal::song->update(SC_DRUMMAP); //FINDMICHJETZT what for?? wtf? redraw(); // FINDMICHJETZT using redraw() instead of the above. } } //--------------------------------------------------------- // sizeChange //--------------------------------------------------------- void DList::sizeChange(int section, int, int) { redraw(); if(editEntry==nullptr) return; const int line = (editEntry-ourDrumMap); int colx = mapx(header->sectionPosition(section)); int colw = rmapx(header->sectionSize(section)); int coly = mapy(line * TH); int colh = rmapy(TH); if(editor && editor->isVisible()) editor->setGeometry(colx, coly, colw, colh); if(val_editor && val_editor->isVisible()) val_editor->setGeometry(colx, coly, colw, colh); if(pitch_editor && pitch_editor->isVisible()) pitch_editor->setGeometry(colx, coly, colw, colh); } //--------------------------------------------------------- // pitchValueChanged //--------------------------------------------------------- void DList::escapePressed() { selectedColumn = -1; if(editor) { editor->blockSignals(true); editor->hide(); editor->blockSignals(false); } if(val_editor) { val_editor->blockSignals(true); val_editor->hide(); val_editor->blockSignals(false); } if(pitch_editor) { pitch_editor->blockSignals(true); pitch_editor->hide(); pitch_editor->blockSignals(false); } editEntry = nullptr; setFocus(); update(); } //--------------------------------------------------------- // returnPressed //--------------------------------------------------------- void DList::returnPressed() { if (editor==nullptr) { printf("THIS SHOULD NEVER HAPPEN: editor is NULL in DList::returnPressed()!\n"); return; } if (editEntry==nullptr) { printf("THIS SHOULD NEVER HAPPEN: editEntry is NULL in DList::returnPressed()!\n"); selectedColumn = -1; editor->blockSignals(true); editor->hide(); editor->blockSignals(false); setFocus(); update(); return; } const int instrument = (editEntry-ourDrumMap); int field = MusECore::WorkingDrumMapEntry::NoField; MusECore::DrumMap editEntryOld = *editEntry; switch(selectedColumn) { case COL_NAME: editEntry->name = editor->text(); field = MusECore::WorkingDrumMapEntry::NameField; break; default: printf("Return pressed in unknown column\n"); break; } const bool do_prop = (editEntryOld != *editEntry && dcanvas); // Clear these before the operations. selectedColumn = -1; editor->blockSignals(true); editor->hide(); editor->blockSignals(false); editEntry = nullptr; setFocus(); update(); if(do_prop) dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); } //--------------------------------------------------------- // valEdited //--------------------------------------------------------- void DList::valEdited() { if (val_editor==nullptr) { printf("THIS SHOULD NEVER HAPPEN: val_editor is NULL in DList::returnPressed()!\n"); return; } if (editEntry==nullptr) { printf("THIS SHOULD NEVER HAPPEN: editEntry is NULL in DList::returnPressed()!\n"); selectedColumn = -1; val_editor->blockSignals(true); val_editor->hide(); val_editor->blockSignals(false); setFocus(); update(); return; } const int instrument = (editEntry-ourDrumMap); int val = val_editor->value(); switch (selectedColumn) { case COL_VOLUME: if (val > 250) val = 250; if (val < 0) val = 0; break; case COL_LEVEL1: case COL_LEVEL2: case COL_LEVEL3: case COL_LEVEL4: if (val > 127) //Check bounds for lv1-lv4 values val = 127; // Zero note on vel is not allowed now. else if (val <= 0) val = 1; break; case COL_OUTCHANNEL: // Default to track port if -1 and track channel if -1. if(val <= 0) val = -1; else val--; if (val >= MusECore::MUSE_MIDI_CHANNELS) val = MusECore::MUSE_MIDI_CHANNELS - 1; break; default: break; } int field = MusECore::WorkingDrumMapEntry::NoField; MusECore::DrumMap editEntryOld = *editEntry; switch(selectedColumn) { case COL_NOTELENGTH: editEntry->len = val; field = MusECore::WorkingDrumMapEntry::LenField; break; case COL_VOLUME: editEntry->vol = val; field = MusECore::WorkingDrumMapEntry::VolField; break; case COL_LEVEL1: editEntry->lv1 = val; field = MusECore::WorkingDrumMapEntry::Lv1Field; break; case COL_LEVEL2: editEntry->lv2 = val; field = MusECore::WorkingDrumMapEntry::Lv2Field; break; case COL_LEVEL3: editEntry->lv3 = val; field = MusECore::WorkingDrumMapEntry::Lv3Field; break; case COL_LEVEL4: editEntry->lv4 = val; field = MusECore::WorkingDrumMapEntry::Lv4Field; break; case COL_QUANT: editEntry->quant = val; field = MusECore::WorkingDrumMapEntry::QuantField; break; case COL_OUTCHANNEL: editEntry->channel = val; field = MusECore::WorkingDrumMapEntry::ChanField; break; default: printf("Value edited in unknown column\n"); break; } const bool do_prop = (editEntryOld != *editEntry && dcanvas); // Clear these before the operations. selectedColumn = -1; val_editor->blockSignals(true); val_editor->hide(); val_editor->blockSignals(false); editEntry = nullptr; setFocus(); update(); if(do_prop) dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); } //--------------------------------------------------------- // pitchEdited //--------------------------------------------------------- void DList::pitchEdited() { if (pitch_editor==nullptr) { printf("THIS SHOULD NEVER HAPPEN: pitch_editor is NULL in DList::pitchEdited()!\n"); return; } if (editEntry==nullptr) { printf("THIS SHOULD NEVER HAPPEN: editEntry is NULL in DList::pitchEdited()!\n"); selectedColumn = -1; pitch_editor->blockSignals(true); pitch_editor->hide(); pitch_editor->blockSignals(false); setFocus(); update(); return; } const int val=pitch_editor->value(); const int instrument=(editEntry-ourDrumMap); int field = MusECore::WorkingDrumMapEntry::NoField; MusECore::DrumMap editEntryOld=*editEntry; switch(selectedColumn) { case COL_NOTE: field = MusECore::WorkingDrumMapEntry::ANoteField; if(val != editEntry->anote) editEntry->anote = val; break; case COL_INPUTTRIGGER: field = MusECore::WorkingDrumMapEntry::ENoteField; if (dcanvas) { // Clear these before the operations. selectedColumn = -1; pitch_editor->blockSignals(true); pitch_editor->hide(); pitch_editor->blockSignals(false); setFocus(); update(); if(editEntry->enote != val) { editEntry->enote = val; editEntry = nullptr; dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); } else editEntry = nullptr; return; } else { for (int i=0;i<128;i++) if (ourDrumMap[i].enote==val) { ourDrumMap[i].enote=editEntry->enote; break; } } editEntry->enote = val; break; default: printf("ERROR: THIS SHOULD NEVER HAPPEN: Value changed in unknown column\n"); break; } const bool do_prop = (editEntryOld != *editEntry && dcanvas); // Clear these before the operations. selectedColumn = -1; pitch_editor->blockSignals(true); pitch_editor->hide(); pitch_editor->blockSignals(false); editEntry = nullptr; setFocus(); update(); if(do_prop) dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); } //--------------------------------------------------------- // moved //--------------------------------------------------------- void DList::moved(int section, int, int) { redraw(); if(editEntry==nullptr) return; const int line = (editEntry-ourDrumMap); int colx = mapx(header->sectionPosition(section)); int colw = rmapx(header->sectionSize(section)); int coly = mapy(line * TH); int colh = rmapy(TH); if(editor && editor->isVisible()) editor->setGeometry(colx, coly, colw, colh); if(val_editor && val_editor->isVisible()) val_editor->setGeometry(colx, coly, colw, colh); if(pitch_editor && pitch_editor->isVisible()) pitch_editor->setGeometry(colx, coly, colw, colh); } //--------------------------------------------------------- // tracklistChanged //--------------------------------------------------------- void DList::tracklistChanged() { } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void DList::songChanged(MusECore::SongChangedStruct_t flags) { if (flags & SC_DRUMMAP) { redraw(); } } //--------------------------------------------------------- // DList //--------------------------------------------------------- void DList::init(QHeaderView* h, QWidget* parent) { setBg(MusEGlobal::config.drumListBg); if (!h) h = new QHeaderView(Qt::Horizontal, parent); header = h; connect(header, SIGNAL(sectionResized(int,int,int)), SLOT(sizeChange(int,int,int))); connect(header, SIGNAL(sectionMoved(int, int,int)), SLOT(moved(int,int,int))); setFocusPolicy(Qt::StrongFocus); drag = NORMAL; editor = nullptr; val_editor = nullptr; pitch_editor = nullptr; editEntry = nullptr; if (ourDrumMapSize!=0) { // always select a drum instrument currentlySelected = &ourDrumMap[0]; } else { currentlySelected = nullptr; } selectedColumn = -1; } DList::DList(QHeaderView* h, QWidget* parent, int ymag, DrumCanvas* dcanvas_) : MusEGui::View(parent, 1, ymag) { setMouseTracking(true); _alphaOverlay = 64; curPitch = -1; dcanvas=dcanvas_; ourDrumMap=dcanvas->getOurDrumMap(); ourDrumMapSize=dcanvas->getOurDrumMapSize(); connect(dcanvas, SIGNAL(ourDrumMapChanged(bool)), SLOT(ourDrumMapChanged(bool))); setStatusTip(tr("Drum list: Edit instrument-specific settings. Click in the Sound column to play the sounds at different velocities.")); init(h, parent); } DList::DList(QHeaderView* h, QWidget* parent, int ymag, MusECore::DrumMap* dm, int dmSize) : MusEGui::View(parent, 1, ymag) { setMouseTracking(true); _alphaOverlay = 64; curPitch = -1; dcanvas=nullptr; ourDrumMap=dm; ourDrumMapSize=dmSize; init(h, parent); } //--------------------------------------------------------- // ~DList //--------------------------------------------------------- DList::~DList() { } //--------------------------------------------------------- // viewMouseMoveEvent //--------------------------------------------------------- void DList::viewMouseMoveEvent(QMouseEvent* ev) { curY = ev->y(); int dInstrument; dInstrument = curY / TH; if (dInstrument >= ourDrumMapSize) dInstrument = ourDrumMapSize - 1; if (dInstrument < 0) dInstrument = 0; emit pitchChanged(dInstrument); setPitch(dInstrument); if (!(ev->buttons() & Qt::LeftButton)) { drag = NORMAL; return; } int delta = curY - startY; switch (drag) { case START_DRAG: // this cannot happen if ourDrumMapSize==0 if (delta < 0) delta = -delta; if (delta <= 2) return; drag = DRAG; setCursor(QCursor(Qt::SizeVerCursor)); redraw(); break; case NORMAL: break; case DRAG: redraw(); break; } } //--------------------------------------------------------- // viewMouseReleaseEvent //--------------------------------------------------------- void DList::viewMouseReleaseEvent(QMouseEvent* ev) { if (drag == DRAG) { int y = ev->y(); int dInstrument; dInstrument = (y+TH/2) / TH; if (dInstrument < 0) dInstrument=0; { if (dInstrument > ourDrumMapSize) dInstrument=ourDrumMapSize; // allow moving something below the last element } int cur_sel = dInstrument>sInstrument ? dInstrument-1 : dInstrument; setCursor(QCursor(Qt::ArrowCursor)); currentlySelected = &ourDrumMap[cur_sel]; emit curDrumInstrumentChanged((unsigned)cur_sel); emit mapChanged(sInstrument, (unsigned)dInstrument); //Track instrument change done in canvas } drag = NORMAL; int x = ev->x(); int y = ev->y(); bool shift = ev->modifiers() & Qt::ShiftModifier; unsigned instrument = y / TH; DrumColumn col = DrumColumn(x2col(x)); switch (col) { case COL_NAME: emit keyReleased(instrument, shift); break; case COL_NOTE: emit keyReleased(instrument, shift); break; default: break; } } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void DList::wheelEvent(QWheelEvent* ev) { ev->accept(); Qt::MouseButtons buttons = ev->buttons(); int keyState = ev->modifiers(); //bool shift = keyState & Qt::ShiftModifier; bool ctrl = keyState & Qt::ControlModifier; #if QT_VERSION >= 0x050e00 const QPoint ev_pos = ev->position().toPoint(); int x = ev_pos.x(); int y = ev_pos.y(); #else int x = ev->x(); int y = ev->y(); #endif DrumColumn col = DrumColumn(x2col(x)); if(buttons != Qt::LeftButton || ourDrumMapSize == 0 || col == COL_NONE) { //ev->ignore(); //View::wheelEvent(ev); //ev->accept(); emit redirectWheelEvent(ev); return; } int instrument = y / TH; if (instrument >= ourDrumMapSize) instrument = ourDrumMapSize-1; if (instrument < 0) instrument = 0; // setCurDrumInstrument(instrument); MusECore::DrumMap* dm = &ourDrumMap[instrument]; MusECore::DrumMap dm_old = *dm; //startY = y; //sInstrument = instrument; //drag = START_DRAG; int val; // int incVal = 0; // if (button == Qt::RightButton) // incVal = 1; // else if (button == Qt::MidButton) // incVal = -1; const QPoint pixelDelta = ev->pixelDelta(); const QPoint angleDegrees = ev->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; int field = MusECore::WorkingDrumMapEntry::NoField; switch (col) { case COL_NONE: case COL_HIDE: case COL_MUTE: case COL_NAME: break; case COL_OUTPORT: // TODO // field = MusECore::WorkingDrumMapEntry::PortField; // if ((button == Qt::RightButton) || (button == Qt::LeftButton)) { // bool changeAll = ev->modifiers() & Qt::ControlModifier; // devicesPopupMenu(dm, mapx(x), mapy(instrument * TH), changeAll); // } break; case COL_VOLUME: field = MusECore::WorkingDrumMapEntry::VolField; val = dm->vol + delta; if (val < 0) val = 0; else if (val > 250) val = 250; dm->vol = (unsigned char)val; break; case COL_QUANT: field = MusECore::WorkingDrumMapEntry::QuantField; dm->quant += delta; // ?? range break; case COL_INPUTTRIGGER: field = MusECore::WorkingDrumMapEntry::ENoteField; val = dm->enote + delta; if (val < 0) val = 0; else if (val > 127) val = 127; if (dcanvas) { if(dm->enote != val) { dm->enote = val; update(); dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); } return; } else { for (int i=0;i<128;i++) if (ourDrumMap[i].enote==val) { ourDrumMap[i].enote=dm->enote; break; } } dm->enote = val; break; case COL_NOTELENGTH: field = MusECore::WorkingDrumMapEntry::LenField; val = dm->len + delta; if (val < 0) val = 0; dm->len = val; break; case COL_NOTE: field = MusECore::WorkingDrumMapEntry::ANoteField; val = dm->anote + delta; if (val < 0) val = 0; else if (val > 127) val = 127; if(val != dm->anote) { MusEGlobal::audio->msgIdle(true); MusEGlobal::song->remapPortDrumCtrlEvents(instrument, val, -1, -1); MusEGlobal::audio->msgIdle(false); dm->anote = val; MusEGlobal::song->update(SC_DRUMMAP); } emit keyPressed(instrument, 100); break; case COL_OUTCHANNEL: field = MusECore::WorkingDrumMapEntry::ChanField; val = dm->channel + delta; // Default to track port if -1 and track channel if -1. if (val < -1) val = -1; else if (val > 127) val = 127; if (ctrl) { MusEGlobal::audio->msgIdle(true); // Delete all port controller events. MusEGlobal::song->changeMidiCtrlCacheEvents(false, true, false, true, false); for (int i = 0; i < ourDrumMapSize; i++) ourDrumMap[i].channel = val; // Add all port controller events. MusEGlobal::song->changeMidiCtrlCacheEvents(true, true, false, true, false); MusEGlobal::audio->msgIdle(false); MusEGlobal::song->update(SC_DRUMMAP); } else { if(val != dm->channel) { MusEGlobal::audio->msgIdle(true); int mchan = val; if(mchan == -1 && dcanvas && dcanvas->part() && dcanvas->part()->track() && dcanvas->part()->track()->isMidiTrack()) mchan = static_cast(dcanvas->part()->track())->outChannel(); if(val != -1) MusEGlobal::song->remapPortDrumCtrlEvents(instrument, -1, val, -1); MusEGlobal::audio->msgIdle(false); dm->channel = val; MusEGlobal::song->update(SC_DRUMMAP); } } break; case COL_LEVEL1: field = MusECore::WorkingDrumMapEntry::Lv1Field; val = dm->lv1 + delta; // Zero note on vel is not allowed now. if (val <= 0) val = 1; else if (val > 127) val = 127; dm->lv1 = val; break; case COL_LEVEL2: field = MusECore::WorkingDrumMapEntry::Lv2Field; val = dm->lv2 + delta; // Zero note on vel is not allowed now. if (val <= 0) val = 1; else if (val > 127) val = 127; dm->lv2 = val; break; case COL_LEVEL3: field = MusECore::WorkingDrumMapEntry::Lv3Field; val = dm->lv3 + delta; // Zero note on vel is not allowed now. if (val <= 0) val = 1; else if (val > 127) val = 127; dm->lv3 = val; break; case COL_LEVEL4: field = MusECore::WorkingDrumMapEntry::Lv4Field; val = dm->lv4 + delta; // Zero note on vel is not allowed now. if (val <= 0) val = 1; else if (val > 127) val = 127; dm->lv4 = val; break; default: break; } update(); if (dm_old != *dm && dcanvas) //something changed and we're in new style mode? dcanvas->propagate_drummap_change(instrument, field, false, false, false, false); } //--------------------------------------------------------- // getSelectedInstrument //--------------------------------------------------------- int DList::getSelectedInstrument() { if (currentlySelected == nullptr) return -1; return currentlySelected - ourDrumMap; } void DList::ourDrumMapChanged(bool instrMapChanged) { int selIdx = currentlySelected ? (currentlySelected - ourDrumMap) : -1; int editIdx = editEntry ? (editEntry - ourDrumMap) : -1; ourDrumMap=dcanvas->getOurDrumMap(); ourDrumMapSize=dcanvas->getOurDrumMapSize(); if (instrMapChanged) { if (editEntry!=nullptr) { printf("THIS SHOULD NEVER HAPPEN: DList::ourDrumMapChanged(true) caused editEntry to be\n" " invalidated. The current active editor will have no\n" " effect, expect potential breakage...\n"); editEntry=nullptr; } } else // that is: if (!instrMapChanged) { // if the instrumentMap has not changed, then its size and so // ourDrumMapSize cannot have changed as well. if (editIdx >= ourDrumMapSize) { printf("THIS SHOULD NEVER HAPPEN: editIdx got out of bounds although ourDrumMapSize\n" " cannot have changed (actually)\n"); editIdx=-1; } editEntry=(editIdx>=0) ? &ourDrumMap[editIdx] : nullptr; } if (selIdx >= ourDrumMapSize) selIdx=ourDrumMapSize-1; if (selIdx < 0) selIdx=0; currentlySelected = (ourDrumMapSize!=0) ? &ourDrumMap[selIdx] : nullptr; if (ourDrumMapSize==0) drag = NORMAL; redraw(); } //--------------------------------------------------------- // leaveEvent //--------------------------------------------------------- void DList::leaveEvent(QEvent*) { emit pitchChanged(-1); setPitch(-1); } //--------------------------------------------------------- // setPitch //--------------------------------------------------------- void DList::setPitch(int pitch) { if (curPitch == pitch) return; curPitch = pitch; redraw(); } } // namespace MusEGui muse-4.0.0/src/muse/midiedit/dlist.h000066400000000000000000000133101404105311000172670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dlist.h,v 1.5.2.3 2009/10/16 21:50:16 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DLIST_H__ #define __DLIST_H__ #include #include #include #include "type_defs.h" #include "pitchedit.h" #include "view.h" #include "dcanvas.h" #define TH 18 // normal Track-hight // REMOVE Tim. newdrums. Added. // Adds the ability to override at instrument level. // But it just makes things too complex for the user. // And in a way is unnecessary and overkill, since we // already allow modifying an instrument. //#define _USE_INSTRUMENT_OVERRIDES_ class QHeaderView; class QMouseEvent; class QPainter; class QWheelEvent; class QEvent; namespace MusECore { struct DrumMap; } namespace MusEGui { class DrumCanvas; //--------------------------------------------------------- // DLineEdit //--------------------------------------------------------- class DLineEdit: public QLineEdit { Q_OBJECT protected: virtual bool event(QEvent*); signals: void returnPressed(); void escapePressed(); public: DLineEdit(QWidget* parent); }; //--------------------------------------------------------- // DrumListSpinBox //--------------------------------------------------------- class DrumListSpinBox : public QSpinBox { Q_OBJECT protected: virtual bool event(QEvent*); signals: void returnPressed(); void escapePressed(); public: DrumListSpinBox(QWidget* parent=0); }; //--------------------------------------------------------- // DPitchEdit //--------------------------------------------------------- class DPitchEdit: public PitchEdit { Q_OBJECT protected: virtual bool event(QEvent*); //signals: //void returnPressed(); //void escapePressed(); public: DPitchEdit(QWidget* parent); }; //--------------------------------------------------------- // DList //--------------------------------------------------------- class DList : public View { Q_OBJECT Q_PROPERTY(quint8 alphaOverlay READ alphaOverlay WRITE setAlphaOverlay) public: enum DrumColumn { COL_HIDE = 0, COL_MUTE, COL_NAME, COL_VOLUME, COL_QUANT, COL_INPUTTRIGGER, COL_NOTELENGTH, COL_NOTE, COL_OUTCHANNEL, COL_OUTPORT, COL_LEVEL1, COL_LEVEL2, COL_LEVEL3, COL_LEVEL4, COL_NONE = -1 }; private: int _alphaOverlay; MusEGui::DrumCanvas* dcanvas; MusECore::DrumMap* ourDrumMap; int ourDrumMapSize; QHeaderView* header; QLineEdit* editor; DrumListSpinBox* val_editor; DPitchEdit* pitch_editor; MusECore::DrumMap* editEntry; MusECore::DrumMap* currentlySelected; int curPitch; int selectedColumn; int startY; int curY; int sInstrument; enum { NORMAL, START_DRAG, DRAG } drag; virtual void draw(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void viewMousePressEvent(QMouseEvent* event); virtual void viewMouseReleaseEvent(QMouseEvent* event); virtual void viewMouseDoubleClickEvent(QMouseEvent*); virtual void viewMouseMoveEvent(QMouseEvent*); virtual void wheelEvent(QWheelEvent* e); virtual void leaveEvent(QEvent*e); int x2col(int x) const; // Returns -1 if invalid. int col2Field(int col) const; // Returns -1 if invalid. int field2Col(int field) const; bool devicesPopupMenu(MusECore::DrumMap* t, int x, int y); void init(QHeaderView*, QWidget*); private slots: void sizeChange(int, int, int); void escapePressed(); void returnPressed(); void valEdited(); void pitchEdited(); void moved(int, int, int); signals: void channelChanged(); void mapChanged(int, int); void keyPressed(int, int); void keyReleased(int, bool); void curDrumInstrumentChanged(int); void redirectWheelEvent(QWheelEvent*); void pitchChanged(int); public slots: void tracklistChanged(); void songChanged(MusECore::SongChangedStruct_t); void ourDrumMapChanged(bool); void setPitch(int); public: void lineEdit(int line, int section); void valEdit(int line, int section); void pitchEdit(int line, int section); void setCurDrumInstrument(int n); DList(QHeaderView*, QWidget* parent, int ymag, DrumCanvas* dcanvas); DList(QHeaderView* h, QWidget* parent, int ymag, MusECore::DrumMap* dm, int dmSize=128); virtual ~DList(); int getSelectedInstrument(); quint8 alphaOverlay() const { return _alphaOverlay; } void setAlphaOverlay(const quint8 i) { _alphaOverlay = i; } }; } // namespace MusEGui #endif // __DLIST_H_ muse-4.0.0/src/muse/midiedit/drum_ordering.cpp000066400000000000000000000137541404105311000213570ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // drum_ordering.cpp // Moved out of drummap.h by Tim. // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "drum_ordering.h" #include "song.h" namespace MusEGlobal { global_drum_ordering_t global_drum_ordering; void global_drum_ordering_t::cleanup() { using MusEGlobal::song; using MusECore::MidiTrack; using MusECore::ciTrack; QSet tracks; for (ciTrack it = song->tracks()->begin(); it != song->tracks()->end(); it++) tracks.insert( dynamic_cast(*it) ); for (iterator it = begin(); it != end();) { if (!tracks.contains(it->first)) it=erase(it); else it++; } } void global_drum_ordering_t::write(int level, MusECore::Xml& xml) { cleanup(); xml.tag(level++, "drum_ordering"); for (iterator it = begin(); it != end(); it++) write_single(level, xml, *it); xml.etag(level, "drum_ordering"); } void global_drum_ordering_t::write_single(int level, MusECore::Xml& xml, const entry_t& entry) { const int trk_idx = MusEGlobal::song->tracks()->index(entry.first); if(trk_idx >= 0) { const QString s= QString("").arg(trk_idx).arg(entry.second); xml.put(level, "%s", s.toLatin1().constData()); } } void global_drum_ordering_t::read(MusECore::Xml& xml) { using MusECore::Xml; clear(); for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; const QString& tag = xml.s1(); switch (token) { case Xml::TagStart: // OBSOLETE. Keep for backwards compatibility. if (tag == "entry") append(read_single(xml)); else if (tag == "item") append(read_item(xml)); else xml.unknown("global_drum_ordering_t"); break; case Xml::TagEnd: if (tag == "drum_ordering") return; default: break; } } } global_drum_ordering_t::entry_t global_drum_ordering_t::read_single(MusECore::Xml& xml) { using MusECore::Xml; using MusEGlobal::song; using MusECore::ciTrack; entry_t entry; entry.first=NULL; entry.second=-1; for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; const QString& tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "track") { QString track_name=xml.parse1(); ciTrack it; for (it = song->tracks()->begin(); it != song->tracks()->end(); it++) if (track_name == (*it)->name()) break; if (it != song->tracks()->end()) entry.first=dynamic_cast(*it); } else if (tag == "instrument") entry.second=xml.parseInt(); else xml.unknown("global_drum_ordering_t (single entry)"); break; case Xml::TagEnd: if (tag == "entry") goto end_of_read_single; default: break; } } end_of_read_single: if (entry.first == NULL) fprintf(stderr, "ERROR: global_drum_ordering_t::read_single() couldn't find the specified track!\n"); if (entry.second < 0 || entry.second > 127) fprintf(stderr, "ERROR: global_drum_ordering_t::read_single(): instrument number is out of bounds (%i)!\n", entry.second); return entry; } global_drum_ordering_t::entry_t global_drum_ordering_t::read_item(MusECore::Xml& xml) { using MusECore::Xml; using MusEGlobal::song; using MusECore::ciTrack; entry_t entry; entry.first=NULL; entry.second=-1; int trk_idx = -1; int instr = -1; for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; const QString& tag = xml.s1(); switch (token) { case Xml::TagStart: xml.unknown("global_drum_ordering_t (single item)"); break; case Xml::Attribut: if (tag == "track") trk_idx = xml.s2().toInt(); else if (tag == "instr") instr = xml.s2().toInt(); else fprintf(stderr, "unknown tag %s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (tag == "item") goto end_of_read_item; break; default: break; } } end_of_read_item: if(trk_idx < 0) { fprintf(stderr, "ERROR: global_drum_ordering_t::read_single() invalid track index (%i)!\n", trk_idx); return entry; } if(instr < 0 || instr > 127) { fprintf(stderr, "ERROR: global_drum_ordering_t::read_single(): instrument number is out of bounds (%i)!\n", instr); return entry; } MusECore::Track* trk = MusEGlobal::song->tracks()->index(trk_idx); if(!trk) { fprintf(stderr, "ERROR: global_drum_ordering_t::read_single() couldn't find the specified track at idx %i !\n", trk_idx); return entry; } if(!trk->isMidiTrack()) { fprintf(stderr, "ERROR: global_drum_ordering_t::read_single() track is not a midi track at idx %i !\n", trk_idx); return entry; } entry.first = static_cast(trk); entry.second = instr; return entry; } } // namespace MusEGlobal muse-4.0.0/src/muse/midiedit/drum_ordering.h000066400000000000000000000032751404105311000210210ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // drum_ordering.h // Moved out of drummap.h by Tim. // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DRUM_ORDERING_H__ #define __DRUM_ORDERING_H__ #include #include #include #include "xml.h" #include "track.h" namespace MusEGlobal { class global_drum_ordering_t : public QList< std::pair > { public: void cleanup(); void write(int level, MusECore::Xml& xml); void read(MusECore::Xml& xml); private: typedef std::pair entry_t; void write_single(int level, MusECore::Xml& xml, const entry_t& entry); entry_t read_item(MusECore::Xml& xml); // OBSOLETE. Keep for backwards compatibility. entry_t read_single(MusECore::Xml& xml); }; extern global_drum_ordering_t global_drum_ordering; } #endif muse-4.0.0/src/muse/midiedit/drumedit.cpp000066400000000000000000002612211404105311000203260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: drumedit.cpp,v 1.22.2.21 2009/11/16 11:29:33 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include "globaldefs.h" #include "drumedit.h" #include "midiport.h" #include "mtscale.h" #include "utils.h" #include "globals.h" #include "app.h" #include "icons.h" #include "filedialog.h" #include "drummap.h" #include "audio.h" #include "gconfig.h" #include "functions.h" #include "helper.h" #include "menutitleitem.h" #include "operations.h" #include "trackinfo_layout.h" #include "midi_editor_layout.h" #include "shortcuts.h" // Forwards from header: #include #include #include #include #include #include #include #include #include #include "event.h" #include "xml.h" #include "ctrl/ctrledit.h" #include "dlist.h" #include "scrollscale.h" #include "splitter.h" #include "tb1.h" #include "popupmenu.h" #include "cobject.h" #include "header.h" #include "tools.h" namespace MusEGui { int DrumEdit::_rasterInit = 96; int DrumEdit::_trackInfoWidthInit = 50; int DrumEdit::_canvasWidthInit = 300; int DrumEdit::_dlistWidthInit = 50; int DrumEdit::_dcanvasWidthInit = 300; bool DrumEdit::_ignore_hide_init = false; static const int DefXScale = -10; static const int DefYScale = 1; static const int MinYScale = -2; static const int MaxYScale = 1; static const int drumeditTools = MusEGui::PointerTool | MusEGui::PencilTool | MusEGui::RubberTool | MusEGui::CursorTool | MusEGui::DrawTool | PanTool | ZoomTool; //--------------------------------------------------------- // setHeaderWhatsThis //--------------------------------------------------------- void DrumEdit::setHeaderWhatsThis() { header->setWhatsThis(DList::COL_HIDE, tr("Hide instrument")); header->setWhatsThis(DList::COL_MUTE, tr("Mute instrument")); header->setWhatsThis(DList::COL_NAME, tr("Sound name")); header->setWhatsThis(DList::COL_VOLUME, tr("Volume percent")); header->setWhatsThis(DList::COL_QUANT, tr("Quantisation")); header->setWhatsThis(DList::COL_INPUTTRIGGER, tr("This input note triggers the sound")); header->setWhatsThis(DList::COL_NOTELENGTH, tr("Note length")); header->setWhatsThis(DList::COL_NOTE, tr("This is the note which is played")); header->setWhatsThis(DList::COL_OUTCHANNEL, tr("Override track output channel (hold ctl to affect all rows)")); header->setWhatsThis(DList::COL_OUTPORT, tr("Override track output port (hold ctl to affect all rows)")); header->setWhatsThis(DList::COL_LEVEL1, tr("Control + meta keys: Draw velocity level 1")); header->setWhatsThis(DList::COL_LEVEL2, tr("Meta key: Draw velocity level 2")); header->setWhatsThis(DList::COL_LEVEL3, tr("Draw default velocity level 3")); header->setWhatsThis(DList::COL_LEVEL4, tr("Meta + alt keys: Draw velocity level 4")); } void DrumEdit::setHeaderStatusTip() { header->setStatusTip(DList::COL_HIDE, tr("Hide instrument")); header->setStatusTip(DList::COL_MUTE, tr("Mute instrument")); header->setStatusTip(DList::COL_NAME, tr("Sound name")); header->setStatusTip(DList::COL_VOLUME, tr("Volume percent")); header->setStatusTip(DList::COL_QUANT, tr("Quantisation")); header->setStatusTip(DList::COL_INPUTTRIGGER, tr("Note which triggers the sound")); header->setStatusTip(DList::COL_NOTELENGTH, tr("Note length")); header->setStatusTip(DList::COL_NOTE, tr("Note which is played")); header->setStatusTip(DList::COL_OUTCHANNEL, tr("Override track output channel (hold CTRL to affect all rows).")); header->setStatusTip(DList::COL_OUTPORT, tr("Override track output port (hold CTRL to affect all rows).")); header->setStatusTip(DList::COL_LEVEL1, tr("CTRL + META keys: Draw velocity level 1")); header->setStatusTip(DList::COL_LEVEL2, tr("META key: Draw velocity level 2")); header->setStatusTip(DList::COL_LEVEL3, tr("Draw default velocity level 3")); header->setStatusTip(DList::COL_LEVEL4, tr("META + ALT keys: Draw velocity level 4")); } //--------------------------------------------------------- // setHeaderToolTips //--------------------------------------------------------- void DrumEdit::setHeaderToolTips() { header->setToolTip(DList::COL_HIDE, tr("Hide instrument")); header->setToolTip(DList::COL_MUTE, tr("Mute instrument")); header->setToolTip(DList::COL_NAME, tr("Sound name")); header->setToolTip(DList::COL_VOLUME, tr("Volume percent")); header->setToolTip(DList::COL_QUANT, tr("Quantisation")); header->setToolTip(DList::COL_INPUTTRIGGER, tr("This input note triggers the sound")); header->setToolTip(DList::COL_NOTELENGTH, tr("Note length")); header->setToolTip(DList::COL_NOTE, tr("This is the note which is played")); header->setToolTip(DList::COL_OUTCHANNEL, tr("Override track output channel (ctl: affect all rows)")); header->setToolTip(DList::COL_OUTPORT, tr("Override track output port (ctl: affect all rows)")); header->setToolTip(DList::COL_LEVEL1, tr("Control + meta keys: Draw velocity level 1")); header->setToolTip(DList::COL_LEVEL2, tr("Meta key: Draw velocity level 2")); header->setToolTip(DList::COL_LEVEL3, tr("Draw default velocity level 3")); header->setToolTip(DList::COL_LEVEL4, tr("Meta + alt keys: Draw velocity level 4")); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void DrumEdit::closeEvent(QCloseEvent* e) { _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. storeSettings(); emit isDeleting(static_cast(this)); e->accept(); } void DrumEdit::storeSettings() { QSettings settings; settings.setValue("Drumedit/windowState", saveState()); //Store values of the horizontal splitter QList sizes = split2->sizes(); QList::iterator it = sizes.begin(); //There are only 2 values stored in the sizelist, size of dlist widget and dcanvas widget _dlistWidthInit = *it; it++; _dcanvasWidthInit = *it; //Store values of the horizontal splitter sizes.clear(); sizes = hsplitter->sizes(); it = sizes.begin(); //There are only 2 values stored in the sizelist, size of trackinfo widget and canvas widget _trackInfoWidthInit = *it; it++; _canvasWidthInit = *it; } //--------------------------------------------------------- // DrumEdit //--------------------------------------------------------- DrumEdit::DrumEdit(MusECore::PartList* pl, QWidget* parent, const char* name, unsigned initPos, bool showDefaultControls) : MidiEditor(TopWin::DRUM, _rasterInit, pl, parent, name) { setFocusPolicy(Qt::NoFocus); deltaMode = false; tickValue = 0; lenValue = 0; pitchValue = 0; // Zero note on vel is not allowed now. veloOnValue = 1; veloOffValue = 0; firstValueSet = false; tickOffset = 0; lenOffset = 0; pitchOffset = 0; veloOnOffset = 0; veloOffOffset = 0; lastSelections = 0; split1w1 = nullptr; //selPart = 0; _playEvents = true; _canvasXOrigin = DefaultCanvasXOrigin; _minXMag = -25; _maxXMag = 2; _group_mode = GROUP_SAME_CHANNEL; _ignore_hide = _ignore_hide_init; // Request to set the raster, but be sure to use the one it chooses, // which may be different than the one requested. _rasterInit = _rasterizerModel->checkRaster(_rasterInit); _raster = _rasterInit; const MusECore::PartList* part_list = parts(); // Default initial view state. _viewState = MusECore::MidiPartViewState (0, 0, DefXScale, qBound(MinYScale, DefYScale, MaxYScale)); // Include a velocity controller in the default initial view state. _viewState.addController(MusECore::MidiCtrlViewState(MusECore::CTRL_VELOCITY)); // grouping actions are only relevant when there are parts from different tracks bool multiTrack = false; if(part_list && !part_list->empty()) { MusECore::Track* trackSaved = nullptr; // If the parts' view states have never been initialized before, // do it now with the desired initial state. for(const auto& it : *part_list) { if(!it.second->viewState().isValid()) { it.second->setViewState(_viewState); } else if (it.second->viewState().yscale() < MinYScale || it.second->viewState().yscale() > MaxYScale) { it.second->viewState().setYScale(DefYScale); } if (!trackSaved) trackSaved = it.second->track(); if (!multiTrack && it.second->track() != trackSaved) multiTrack = true; } // Now take our initial view state from the first part found in the list. // Don't bother if not showing default controls, since something else // will likely take care of it, like the song file loading routines. if(showDefaultControls) _viewState = part_list->begin()->second->viewState(); } //---------Pulldown Menu---------------------------- menuEdit = menuBar()->addMenu(tr("&Edit")); menuEdit->addActions(MusEGlobal::undoRedo->actions()); menuEdit->addSeparator(); cutAction = menuEdit->addAction(*cutSVGIcon, tr("C&ut")); copyAction = menuEdit->addAction(*copySVGIcon, tr("&Copy")); copyRangeAction = menuEdit->addAction(*copyRangeSVGIcon, tr("Copy Events in Range")); pasteAction = menuEdit->addAction(*pasteSVGIcon, tr("&Paste")); pasteToCurPartAction = menuEdit->addAction(*pasteSelectedTrackSVGIcon, tr("Paste to Current Part")); pasteDialogAction = menuEdit->addAction(*pasteDialogSVGIcon, tr("Paste (With Dialog)...")); menuEdit->addSeparator(); deleteAction = menuEdit->addAction(*deleteSVGIcon, tr("Delete &Events")); connect(cutAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_CUT); } ); connect(copyAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_COPY); } ); connect(copyRangeAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_COPY_RANGE); } ); connect(pasteAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_PASTE); } ); connect(pasteToCurPartAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_PASTE_TO_CUR_PART); } ); connect(pasteDialogAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_PASTE_DIALOG); } ); connect(deleteAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_DEL); } ); menuEdit->addSeparator(); menuSelect = menuEdit->addMenu(tr("&Select")); sallAction = menuSelect->addAction(*selectAllSVGIcon, tr("Select All")); snoneAction = menuSelect->addAction(*deselectAllSVGIcon, tr("Deselect All")); invAction = menuSelect->addAction(*selectInvertSVGIcon, tr("Invert")); inAction = menuSelect->addAction(*selectInsideLoopSVGIcon, tr("Inside Loop")); outAction = menuSelect->addAction(*selectOutsideLoopSVGIcon, tr("Outside Loop")); menuSelect->addSeparator(); selectRangeToSelectionAction = menuSelect->addAction(*rangeToSelectionSVGIcon, tr("Set &Range to Selection")); connect(selectRangeToSelectionAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_RANGE_TO_SELECTION); } ); if (parts()->size() > 1) { menuEdit->addSeparator(); nextAction = menuEdit->addAction(*nextPartSVGIcon, tr("Next Part")); prevAction = menuEdit->addAction(*lastPartSVGIcon, tr("Previous Part")); connect(prevAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_SELECT_PREV_PART); } ); connect(nextAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_SELECT_NEXT_PART); } ); } else { prevAction = nullptr; nextAction = nullptr; } connect(sallAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_SELECT_ALL); } ); connect(snoneAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_SELECT_NONE); } ); connect(invAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_SELECT_INVERT); } ); connect(inAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_SELECT_ILOOP); } ); connect(outAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_SELECT_OLOOP); } ); menuEdit->addSeparator(); startListEditAction = menuEdit->addAction(*listeditSVGIcon, tr("Event List...")); connect(startListEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startListEditor())); // Functions menuFunctions = menuBar()->addMenu(tr("Fu&nctions")); // menuFunctions->setTearOffEnabled(true); quantizeAction = menuFunctions->addAction(*quantizeSVGIcon, tr("Quantize")); veloAction = menuFunctions->addAction(tr("Modify Velocity")); crescAction = menuFunctions->addAction(tr("Crescendo/Decrescendo")); QAction* noteShiftAction = menuFunctions->addAction(tr("Move Notes")); QAction* delOverlapsAction = menuFunctions->addAction(tr("Delete Overlaps")); QAction* eraseEventAction = menuFunctions->addAction(tr("Erase Events")); menuFunctions->addSeparator(); fixedAction = menuFunctions->addAction(*dummySVGIcon, tr("Set Length to Drummap Value")); connect(fixedAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_FIXED_LEN); } ); connect(veloAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_MODIFY_VELOCITY); } ); connect(crescAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_CRESCENDO); } ); connect(quantizeAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_QUANTIZE); } ); connect(eraseEventAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_ERASE_EVENT); } ); connect(noteShiftAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_NOTE_SHIFT); } ); connect(delOverlapsAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_DELETE_OVERLAPS); } ); //---------------------- // Scripts: //---------------------- menuScripts = menuBar()->addMenu(tr("&Scripts")); menuScripts->menuAction()->setStatusTip(tr("Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki.")); scripts.populateScriptMenu(menuScripts); connect(&scripts, &MusECore::Scripts::execDeliveredScriptReceived, [this](int id) { execDeliveredScript(id); } ); connect(&scripts, &MusECore::Scripts::execUserScriptReceived, [this](int id) { execUserScript(id); } ); QMenu* settingsMenu = menuBar()->addMenu(tr("&Display")); settingsMenu->menuAction()->setStatusTip(tr("Display menu: Display options specific to current editor.")); settingsMenu->addAction(subwinAction); settingsMenu->addAction(fullscreenAction); settingsMenu->addSeparator(); QMenu* menuGrouping=settingsMenu->addMenu(tr("Group")); menuGrouping->setObjectName("CheckmarkOnly"); auto groupAG = new QActionGroup(this); groupAG->setExclusive(true); groupNoneAction = groupAG->addAction(tr("Don't Group")); groupChanAction = groupAG->addAction(tr("Group by Channel")); groupMaxAction = groupAG->addAction(tr("Group Maximally")); if (!multiTrack) { groupNoneAction->setEnabled(false); groupChanAction->setEnabled(false); groupMaxAction->setEnabled(false); } menuGrouping->addActions(groupAG->actions()); QMenu* menuShowHide=settingsMenu->addMenu(tr("Show/Hide")); QAction* ignoreHideAction = menuShowHide->addAction(tr("Also Show Hidden Instruments")); menuShowHide->addSeparator(); QAction* showAllAction = menuShowHide->addAction(*dummySVGIcon, tr("Show All Instruments")); QAction* hideAllAction = menuShowHide->addAction(tr("Hide All Instruments")); QAction* hideUnusedAction = menuShowHide->addAction(tr("Show Only Used Instruments")); QAction* hideEmptyAction = menuShowHide->addAction(tr("Show Only Named and Used Instruments")); settingsMenu->addSeparator(); groupNoneAction->setCheckable(true); groupChanAction->setCheckable(true); groupChanAction->setChecked(true); groupMaxAction ->setCheckable(true); ignoreHideAction->setCheckable(true); ignoreHideAction->setChecked(_ignore_hide); connect(ignoreHideAction, SIGNAL(toggled(bool)), SLOT(set_ignore_hide(bool))); connect(showAllAction, SIGNAL(triggered()), this, SLOT(showAllInstruments())); connect(hideAllAction, SIGNAL(triggered()), this, SLOT(hideAllInstruments())); connect(hideUnusedAction, SIGNAL(triggered()), this, SLOT(hideUnusedInstruments())); connect(hideEmptyAction, SIGNAL(triggered()), this, SLOT(hideEmptyInstruments())); connect(groupNoneAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_GROUP_NONE); } ); connect(groupChanAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_GROUP_CHAN); } ); connect(groupMaxAction, &QAction::triggered, [this]() { cmd(DrumCanvas::CMD_GROUP_MAX); } ); addControllerMenu = new PopupMenu(tr("Add Controller View"), this, true); addControllerMenu->setIcon(*midiControllerNewSVGIcon); settingsMenu->addMenu(addControllerMenu); connect(addControllerMenu, &QMenu::aboutToShow, [this]() { ctrlMenuAboutToShow(); } ); connect(addControllerMenu, &QMenu::aboutToHide, [this]() { ctrlMenuAboutToHide(); } ); connect(addControllerMenu, &QMenu::triggered, [this](QAction* act) { ctrlPopupTriggered(act); } ); //--------------------------------------------------- // Toolbars //--------------------------------------------------- // NOTICE: Please ensure that any tool bar object names here match the names assigned // to identical or similar toolbars in class MusE or other TopWin classes. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). addToolBarBreak(); // Already has an object name. tools2 = new MusEGui::EditToolBar(this, drumeditTools); addToolBar(tools2); tools = addToolBar(tr("Drum tools")); tools->setObjectName("Drum tools"); addctrl = new QToolButton(); addctrl->setToolTip(tr("Add controller view")); addctrl->setIcon(*midiControllerNewSVGIcon); addctrl->setFocusPolicy(Qt::NoFocus); connect(addctrl, &QToolButton::pressed, [this]() { addCtrlClicked(); } ); tools->addWidget(addctrl); srec = new QToolButton(); srec->setToolTip(tr("Step record")); srec->setIcon(*steprecSVGIcon); srec->setCheckable(true); srec->setFocusPolicy(Qt::NoFocus); tools->addWidget(srec); midiin = new QToolButton(); midiin->setToolTip(tr("Midi input")); midiin->setIcon(*midiInSVGIcon); midiin->setCheckable(true); midiin->setFocusPolicy(Qt::NoFocus); tools->addWidget(midiin); speaker = new QToolButton(); speaker->setToolTip(tr("Play events")); speaker->setIcon(*speakerSVGIcon); speaker->setCheckable(true); speaker->setChecked(true); speaker->setFocusPolicy(Qt::NoFocus); tools->addWidget(speaker); // QAction* whatsthis = QWhatsThis::createAction(this); // whatsthis->setIcon(*whatsthisSVGIcon); // tools->addAction(whatsthis); // don't show pitch value in toolbar toolbar = new MusEGui::Toolbar1(_rasterizerModel, this, _rasterInit, false); toolbar->setObjectName("Drum Pos/Snap/Solo-tools"); addToolBar(toolbar); addToolBarBreak(); info = new MusEGui::NoteInfo(this); info->setObjectName("Drum note info"); addToolBar(info); QToolBar* cursorToolbar = addToolBar(tr("Cursor tools")); cursorToolbar->setObjectName("Cursor step tools"); QLabel *stepStr = new QLabel(tr("Cursor step")); cursorToolbar->addWidget(stepStr); stepLenWidget = new QComboBox(); stepLenWidget->setToolTip(tr("Set step size for cursor edit")); stepLenWidget->addItem("1"); stepLenWidget->addItem("2"); stepLenWidget->addItem("3"); stepLenWidget->addItem("4"); stepLenWidget->addItem("5"); stepLenWidget->addItem("6"); stepLenWidget->addItem("7"); stepLenWidget->addItem("8"); stepLenWidget->addItem("16"); stepLenWidget->setCurrentIndex(0); stepLenWidget->setFocusPolicy(Qt::TabFocus); connect(stepLenWidget, SIGNAL(currentIndexChanged(QString)), SLOT(setStep(QString))); cursorToolbar->addWidget(stepLenWidget); //--------------------------------------------------- // split //--------------------------------------------------- hsplitter = new MusEGui::Splitter(Qt::Horizontal, mainw, "hsplitter"); hsplitter->setChildrenCollapsible(true); //hsplitter->setHandleWidth(4); trackInfoWidget = new TrackInfoWidget(hsplitter); genTrackInfo(trackInfoWidget); split1 = new MusEGui::Splitter(Qt::Vertical, mainw, "split1"); hscroll = new MusEGui::ScrollScale( (_minXMag * MusEGlobal::config.division) / 384, _maxXMag, _viewState.xscale(), 20000, Qt::Horizontal, mainw); hscroll->setFocusPolicy(Qt::NoFocus); hscroll->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); QSizeGrip* corner = new QSizeGrip(mainw); corner->setFixedHeight(hscroll->sizeHint().height()); MidiEditorHScrollLayout* gridSplit1 = new MidiEditorHScrollLayout(nullptr, nullptr, nullptr, hscroll, corner, nullptr); gridSplit1->setContentsMargins(0, 0, 0, 0); gridSplit1->setSpacing(0); QWidget* split1_w = new QWidget(hsplitter); split1_w->setObjectName("split1_w"); split1_w->setContentsMargins(0, 0, 0, 0); QVBoxLayout* split1_w_vbox = new QVBoxLayout(split1_w); split1_w_vbox->setContentsMargins(0, 0, 0, 0); split1_w_vbox->setSpacing(0); // Let the vertical splitter take up room so that the next widget // (gridSplit1 - the bottom space layout) does not expand. split1_w_vbox->addWidget(split1, 1000); split1_w_vbox->addLayout(gridSplit1); QSizePolicy tipolicy, epolicy; hsplitter->setStretchFactor(hsplitter->indexOf(trackInfoWidget), 0); tipolicy = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); tipolicy.setHorizontalStretch(0); tipolicy.setVerticalStretch(100); trackInfoWidget->setSizePolicy(tipolicy); hsplitter->setStretchFactor(hsplitter->indexOf(split1_w), 1); epolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); epolicy.setHorizontalStretch(255); epolicy.setVerticalStretch(100); split1->setSizePolicy(epolicy); mainGrid->addWidget(hsplitter, 0, 0, 1, 1); split2 = new MusEGui::Splitter(Qt::Horizontal, split1, "split2"); split1w1 = new QWidget(split2); QWidget* split1w2 = new QWidget(split2); split1w1->setContentsMargins(0, 0, 0, 0); split1w2->setContentsMargins(0, 0, 0, 0); split1->setContentsMargins(0, 0, 0, 0); split2->setContentsMargins(0, 0, 0, 0); hsplitter->setContentsMargins(0, 0, 0, 0); // NOTICE: For vertical splitter split1, we need to ignore the horizontal size // otherwise we get strange runaway resizing when split2 is moved towards // the left, which also depends oddly on the minimum size of the widget // in the left of hsplitter. For the other splitters, I guess we'll // do the same since it took a whole day to solve this problem. Tim. split1->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); split2->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored); hsplitter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored); split2->setStretchFactor(split2->indexOf(split1w1), 0); tipolicy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); tipolicy.setHorizontalStretch(0); tipolicy.setVerticalStretch(100); split1w1->setSizePolicy(tipolicy); split2->setStretchFactor(split2->indexOf(split1w2), 1); epolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); epolicy.setHorizontalStretch(255); epolicy.setVerticalStretch(100); split1w2->setSizePolicy(epolicy); // Now that we have the second widget in splitter 2 (split1w2), // set up the horizontal scroll layout to follow its width. gridSplit1->setEditor(split1w2); QGridLayout* gridS1 = new QGridLayout(split1w1); MidiEditorCanvasLayout* gridS2 = new MidiEditorCanvasLayout(split1w2, gridSplit1); gridS1->setContentsMargins(0, 0, 0, 0); gridS1->setSpacing(0); gridS2->setContentsMargins(0, 0, 0, 0); gridS2->setSpacing(0); time = new MusEGui::MTScale(_raster, split1w2, _viewState.xscale()); canvas = new DrumCanvas(this, split1w2, _viewState.xscale(), _viewState.yscale()); vscroll = new MusEGui::ScrollScale(MinYScale, MaxYScale, _viewState.yscale(), dynamic_cast(canvas)->getOurDrumMapSize()*TH, Qt::Vertical, split1w2); canvas->setOrigin(_canvasXOrigin, 0); canvas->setCanvasTools(drumeditTools); canvas->setFocus(); connect(canvas, SIGNAL(toolChanged(int)), tools2, SLOT(set(int))); connect(MusEGlobal::muse, &MusE::configChanged, tools2, &EditToolBar::configChanged); connect(canvas, SIGNAL(horizontalZoom(bool,const QPoint&)), SLOT(horizontalZoom(bool, const QPoint&))); connect(canvas, SIGNAL(horizontalZoom(int, const QPoint&)), SLOT(horizontalZoom(int, const QPoint&))); connect(canvas, SIGNAL(ourDrumMapChanged(bool)), SLOT(ourDrumMapChanged(bool))); connect(canvas, SIGNAL(curPartHasChanged(MusECore::Part*)), SLOT(updateTrackInfo())); time->setOrigin(_canvasXOrigin, 0); QList mops; mops.append(_dlistWidthInit); mops.append(_dcanvasWidthInit); split2->setSizes(mops); mops.clear(); mops.append(_trackInfoWidthInit); mops.append(_canvasWidthInit); hsplitter->setSizes(mops); gridS2->setRowStretch(1, 100); gridS2->setColumnStretch(0, 100); gridS2->addWidget(time, 0, 0, 1, 2); gridS2->addWidget(MusECore::hLine(split1w2), 1, 0, 1, 2); gridS2->addWidget(canvas, 2, 0); gridS2->addWidget(vscroll, 2, 1); // Ordering is hardcoded in dlist.c ("Dcols") header = new MusEGui::Header(split1w1, "header"); header->setFixedHeight(31); //: hide header->setColumnLabel(tr("H"), DList::COL_HIDE, 20); //: mute header->setColumnLabel(tr("M"), DList::COL_MUTE, 20); header->setColumnLabel(tr("Sound"), DList::COL_NAME, 120); header->setColumnLabel(tr("Vol"), DList::COL_VOLUME); header->setColumnLabel(tr("QNT"), DList::COL_QUANT, 40); header->setColumnLabel(tr("E-Note"), DList::COL_INPUTTRIGGER, 50); header->setColumnLabel(tr("Len"), DList::COL_NOTELENGTH, 40); header->setColumnLabel(tr("A-Note"), DList::COL_NOTE, 50); header->setColumnLabel(tr("Ch"), DList::COL_OUTCHANNEL); header->setColumnLabel(tr("Port"), DList::COL_OUTPORT, 70); header->setColumnLabel(tr("LV1"), DList::COL_LEVEL1); header->setColumnLabel(tr("LV2"), DList::COL_LEVEL2); header->setColumnLabel(tr("LV3"), DList::COL_LEVEL3); header->setColumnLabel(tr("LV4"), DList::COL_LEVEL4); setHeaderToolTips(); setHeaderWhatsThis(); setHeaderStatusTip(); if (_ignore_hide) header->showSection(DList::COL_HIDE); else header->hideSection(DList::COL_HIDE); dlist = new DList(header, split1w1, _viewState.yscale(), (DrumCanvas*)canvas); setCurDrumInstrument(dlist->getSelectedInstrument()); connect(dlist, SIGNAL(keyPressed(int, int)), canvas, SLOT(keyPressed(int, int))); connect(dlist, SIGNAL(keyReleased(int, bool)), canvas, SLOT(keyReleased(int, bool))); connect(dlist, SIGNAL(mapChanged(int, int)), canvas, SLOT(mapChanged(int, int))); connect(dlist, SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*))); connect(dlist, SIGNAL(curDrumInstrumentChanged(int)), SLOT(setCurDrumInstrument(int))); connect(dlist, SIGNAL(curDrumInstrumentChanged(int)), canvas, SLOT(setCurDrumInstrument(int))); gridS1->setRowStretch(1, 100); gridS1->setColumnStretch(0, 100); gridS1->addWidget(header, 0, 0); gridS1->addWidget(dlist, 1, 0); connect(canvas, SIGNAL(newWidth(int)), SLOT(newCanvasWidth(int))); connect(canvas, SIGNAL(verticalScroll(unsigned)), vscroll, SLOT(setPos(unsigned))); connect(canvas, SIGNAL(horizontalScroll(unsigned)),hscroll, SLOT(setPos(unsigned))); connect(canvas, SIGNAL(horizontalScrollNoLimit(unsigned)),hscroll, SLOT(setPosNoLimit(unsigned))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged1(MusECore::SongChangedStruct_t))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), dlist, SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(vscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setYPos(int))); connect(vscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setYMag(int))); connect(vscroll, SIGNAL(scaleChanged(int)), dlist, SLOT(setYMag(int))); connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setXMag(int))); connect(srec, SIGNAL(toggled(bool)), canvas, SLOT(setSteprec(bool))); connect(midiin, SIGNAL(toggled(bool)), canvas, SLOT(setMidiin(bool))); connect(speaker, SIGNAL(toggled(bool)), SLOT(setSpeaker(bool))); connect(vscroll, SIGNAL(scrollChanged(int)), dlist, SLOT(setYPos(int))); connect(hscroll, SIGNAL(scrollChanged(int)), time, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), time, SLOT(setXMag(int))); connect(tools2, SIGNAL(toolChanged(int)), canvas, SLOT(setTool(int))); // in Canvas connect(tools2, SIGNAL(toolChanged(int)), canvas, SLOT(setTool2(int))); // in DrumCanvas connect(canvas, SIGNAL(selectionChanged(int, MusECore::Event&, MusECore::Part*, bool)), this, SLOT(setSelection(int, MusECore::Event&, MusECore::Part*, bool))); connect(canvas, SIGNAL(followEvent(int)), SLOT(follow(int))); connect(canvas, SIGNAL(pitchChanged(int)), dlist, SLOT(setPitch(int))); connect(canvas, &EventCanvas::pitchChanged, [this](int p) { dlist->setPitch(p); } ); connect(hscroll, SIGNAL(scaleChanged(int)), SLOT(updateHScrollRange())); setWindowTitle(canvas->getCaption()); dlist->setYPos(_viewState.yscroll()); canvas->setYPos(_viewState.yscroll()); vscroll->setOffset(_viewState.yscroll()); updateHScrollRange(); // connect toolbar connect(canvas, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(time, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(toolbar, &Toolbar1::rasterChanged, [this](int raster) { setRaster(raster); } ); connect(toolbar, SIGNAL(soloChanged(bool)), SLOT(soloChanged(bool))); connect(toolbar, &Toolbar1::gridOnChanged, [this](bool v) { gridOnChanged(v); } ); connect(info, SIGNAL(valueChanged(MusEGui::NoteInfo::ValType, int)), SLOT(noteinfoChanged(MusEGui::NoteInfo::ValType, int))); connect(info, SIGNAL(deltaModeChanged(bool)), SLOT(deltaModeChanged(bool))); connect(info, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(info, SIGNAL(escapePressed()), SLOT(focusCanvas())); connect(MusEGlobal::song, SIGNAL(midiNote(int, int)), SLOT(midiNote(int,int))); QClipboard* cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged())); clipboardChanged(); // enable/disable "Paste" selectionChanged(); // enable/disable "Copy" & "Paste" configChanged(); // set configuration values, initialize shortcuts // Don't bother if not showing default stuff, since something else // will likely take care of it, like the song file loading routines. if(showDefaultControls) { const MusECore::Pos cpos=MusEGlobal::song->cPos(); canvas->setPos(0, cpos.tick(), true); canvas->selectAtTick(cpos.tick()); //canvas->selectFirst(); unsigned pos=0; if(initPos >= INT_MAX) pos = MusEGlobal::song->cpos(); else pos = initPos; if(pos > INT_MAX) pos = INT_MAX; //if (pos) hscroll->setOffset((int)pos); } if(canvas->track()) { updateTrackInfo(); toolbar->setSolo(canvas->track()->solo()); } initTopwinState(); finalizeInit(); // Add initial controllers. // Don't bother if not showing default stuff, since something else // will likely take care of it, like the song file loading routines. if(showDefaultControls) { CtrlEdit* ctrl_edit; const MusECore::MidiCtrlViewStateList& mcvsl = _viewState.controllers(); for(MusECore::ciMidiCtrlViewState i = mcvsl.cbegin(); i != mcvsl.cend(); ++i) { const MusECore::MidiCtrlViewState& mcvs = *i; ctrl_edit = addCtrl(mcvs._num); if(ctrl_edit) ctrl_edit->setPerNoteVel(mcvs._perNoteVel); } } } //--------------------------------------------------------- // songChanged1 //--------------------------------------------------------- void DrumEdit::songChanged1(MusECore::SongChangedStruct_t bits) { if(_isDeleting) // Ignore while deleting to prevent crash. return; // We must catch this first and be sure to update the strips. if(bits & SC_TRACK_REMOVED) checkTrackInfoTrack(); if (bits & SC_DIVISION_CHANGED) { // The division has changed. The raster table and raster model will have been // cleared and re-filled, so any views on the model will no longer have a // current item and our current raster value will be invalid. They WILL NOT // emit an activated signal. So we must manually select a current item and // raster value here. We could do something fancy to try to keep the current // index - for example stay on quarter note - by taking the ratio of the new // division to old division and apply that to the old raster value and try // to select that index, but the division has already changed. // So instead, simply try to select the current raster value. The index in the box may change. // Be sure to use what it chooses. changeRaster(_raster); // Now set a reasonable zoom (mag) range. setupHZoomRange(); } if (bits & SC_SOLO) { if(canvas->track()) toolbar->setSolo(canvas->track()->solo()); } if ( ( bits & (SC_DRUMMAP | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_PART_INSERTED | SC_PART_REMOVED | SC_PART_MODIFIED) ) ) ((DrumCanvas*)(canvas))->rebuildOurDrumMap(); songChanged(bits); // We'll receive SC_SELECTION if a different part is selected. // Addition - also need to respond here to moving part to another track. (Tim) if (bits & (SC_PART_INSERTED | SC_PART_REMOVED)) updateTrackInfo(); // We must marshall song changed instead of connecting to the strip's song changed // otherwise it crashes when loading another song because track is no longer valid // and the strip's songChanged() seems to be called before Pianoroll songChanged() // gets called and has a chance to stop the crash. // Also, calling updateTrackInfo() from here is too heavy, it destroys and recreates // the strips each time no matter what the flags are ! else trackInfoSongChange(bits); } //--------------------------------------------------------- // midiNote //--------------------------------------------------------- void DrumEdit::midiNote(int pitch, int velo) { if (MusEGlobal::debugMsg) printf("DrumEdit::midiNote: pitch=%i, velo=%i\n", pitch, velo); // *note = old_style_drummap_mode ? ourDrumMap[index].anote : instrument_map[index].pitch; if ((DrumCanvas*)(canvas)->midiin()) { int index=0; for (index = 0; index < get_instrument_map().size(); ++index) { if (get_instrument_map().at(index).pitch == pitch) break; } dlist->setCurDrumInstrument(index); } } //--------------------------------------------------------- // horizontalZoom //--------------------------------------------------------- void DrumEdit::horizontalZoom(bool zoom_in, const QPoint& glob_pos) { int mag = hscroll->mag(); int zoomlvl = MusEGui::ScrollScale::getQuickZoomLevel(mag); if(zoom_in) { if (zoomlvl < MusEGui::ScrollScale::zoomLevels-1) zoomlvl++; } else { if (zoomlvl > 1) zoomlvl--; } int newmag = MusEGui::ScrollScale::convertQuickZoomLevelToMag(zoomlvl); QPoint cp = canvas->mapFromGlobal(glob_pos); QPoint sp = split1->mapFromGlobal(glob_pos); if(cp.x() >= 0 && cp.x() < canvas->width() && sp.y() >= 0 && sp.y() < split1->height()) hscroll->setMag(newmag, cp.x()); } void DrumEdit::horizontalZoom(int mag, const QPoint& glob_pos) { QPoint cp = canvas->mapFromGlobal(glob_pos); QPoint sp = split1->mapFromGlobal(glob_pos); if(cp.x() >= 0 && cp.x() < canvas->width() && sp.y() >= 0 && sp.y() < split1->height()) hscroll->setMag(hscroll->mag() + mag, cp.x()); } //--------------------------------------------------------- // updateHScrollRange //--------------------------------------------------------- void DrumEdit::updateHScrollRange() { int s, e; canvas->range(&s, &e); // Show one more measure. e += MusEGlobal::sigmap.ticksMeasure(e); // Show another quarter measure due to imprecise drawing at canvas end point. e += MusEGlobal::sigmap.ticksMeasure(e) / 4; // Compensate for drum list, splitter handle, and vscroll widths. e += canvas->rmapxDev(split2->handleWidth() - vscroll->width()); int s1, e1; hscroll->range(&s1, &e1); if(s != s1 || e != e1) hscroll->setRange(s, e); } //--------------------------------------------------------- // follow //--------------------------------------------------------- void DrumEdit::follow(int pos) { int s, e; canvas->range(&s, &e); if (pos < e && pos >= s) hscroll->setOffset(pos); if (pos < s) hscroll->setOffset(s); } //--------------------------------------------------------- // setTime //--------------------------------------------------------- void DrumEdit::setTime(unsigned tick) { toolbar->setTime(tick); time->setPos(3, tick, false); } //--------------------------------------------------------- // setSelection // update Info Line //--------------------------------------------------------- void DrumEdit::setSelection(int tick, MusECore::Event& e, MusECore::Part*, bool update) { int selections = canvas->selectionSize(); // Diagnostics: //printf("DrumEdit::setSelection selections:%d event empty:%d firstValueSet:%d\n", selections, e.empty(), firstValueSet); //if(!e.empty()) // e.dump(); if(update) { // Selections have changed. Reset these. tickOffset = 0; lenOffset = 0; pitchOffset = 0; veloOnOffset = 0; veloOffOffset = 0; // Force 'suggested' modes: if (selections == 1) { deltaMode = false; info->setDeltaMode(deltaMode); } else if (selections > 1) { // A feeble attempt to hold on to the user's setting. Should try to bring back // selEvent (removed), but there were problems using it (it's a reference). //if(lastSelections <= 1) { deltaMode = true; info->setDeltaMode(deltaMode); } } } lastSelections = selections; if ((selections == 1) || (selections > 1 && !firstValueSet)) { tickValue = tick; lenValue = e.lenTick(); pitchValue = e.pitch(); veloOnValue = e.velo(); // Zero note on vel is not allowed now. if(veloOnValue == 0) { veloOnValue = 1; fprintf(stderr, "DrumEdit::setSelection: Warning: Zero note on velocity!\n"); } veloOffValue = e.veloOff(); firstValueSet = true; } if (selections > 0) { info->setEnabled(true); if (deltaMode) info->setValues(tickOffset, lenOffset, pitchOffset, veloOnOffset, veloOffOffset); else info->setValues(tickValue, lenValue, pitchValue, veloOnValue, veloOffValue); } else { info->setEnabled(false); // Zero note on vel is not allowed now. info->setValues(0, 0, 0, deltaMode ? 0 : 1, 0); firstValueSet = false; tickValue = 0; lenValue = 0; pitchValue = 0; // Zero note on vel is not allowed now. veloOnValue = 1; veloOffValue = 0; tickOffset = 0; lenOffset = 0; pitchOffset = 0; veloOnOffset = 0; veloOffOffset = 0; } info->setReturnMode(selections >= 2); selectionChanged(); } //--------------------------------------------------------- // focusCanvas //--------------------------------------------------------- void DrumEdit::focusCanvas() { if(MusEGlobal::config.smartFocus) { canvas->setFocus(); canvas->activateWindow(); } } //--------------------------------------------------------- // deltaModeChanged //--------------------------------------------------------- void DrumEdit::deltaModeChanged(bool delta_on) { if(deltaMode == delta_on) return; deltaMode = delta_on; int selections = canvas->selectionSize(); if(deltaMode) { if(selections > 0) info->setValues(tickOffset, lenOffset, pitchOffset, veloOnOffset, veloOffOffset); } else { if(selections > 0) info->setValues(tickValue, lenValue, pitchValue, veloOnValue, veloOffValue); } } //--------------------------------------------------------- // soloChanged //--------------------------------------------------------- void DrumEdit::soloChanged(bool flag) { if(canvas->track()) { // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(canvas->track(), flag, MusECore::PendingOperationItem::SetTrackSolo)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } } //--------------------------------------------------------- // gridOnChanged //--------------------------------------------------------- void DrumEdit::gridOnChanged(bool flag) { MusEGlobal::config.canvasShowGrid = flag; // We want the simple version, don't set the style or stylesheet yet. MusEGlobal::muse->changeConfig(true); } //--------------------------------------------------------- // setRaster //--------------------------------------------------------- void DrumEdit::setRaster(int val) { // Request to set the raster, but be sure to use the one it chooses, // which may be different than the one requested. val = _rasterizerModel->checkRaster(val); MidiEditor::setRaster(val); _rasterInit = _raster; time->setRaster(_raster); canvas->redrawGrid(); for (auto it : ctrlEditList) it->redrawCanvas(); focusCanvas(); // give back focus after kb input } //--------------------------------------------------------- // changeRaster //--------------------------------------------------------- int DrumEdit::changeRaster(int val) { // Request to set the raster, but be sure to use the one it chooses, // which may be different than the one requested. MidiEditor::setRaster(toolbar->changeRaster(val)); _rasterInit = _raster; time->setRaster(_raster); canvas->redrawGrid(); for (auto it : ctrlEditList) it->redrawCanvas(); return _raster; } //--------------------------------------------------------- // edit currently selected Event //--------------------------------------------------------- void DrumEdit::noteinfoChanged(MusEGui::NoteInfo::ValType type, int val) { int selections = canvas->selectionSize(); if (selections == 0) { printf("noteinfoChanged while nothing selected\n"); } else if (selections > 0) { if(deltaMode) { // treat noteinfo values as offsets to event values int delta = 0; switch (type) { case MusEGui::NoteInfo::VAL_TIME: delta = val - tickOffset; tickOffset = val; break; case MusEGui::NoteInfo::VAL_LEN: delta = val - lenOffset; lenOffset = val; break; case MusEGui::NoteInfo::VAL_VELON: delta = val - veloOnOffset; veloOnOffset = val; break; case MusEGui::NoteInfo::VAL_VELOFF: delta = val - veloOffOffset; veloOffOffset = val; break; case MusEGui::NoteInfo::VAL_PITCH: delta = val - pitchOffset; pitchOffset = val; break; } if (delta) canvas->modifySelected(type, delta); } else { switch (type) { case MusEGui::NoteInfo::VAL_TIME: tickValue = val; break; case MusEGui::NoteInfo::VAL_LEN: lenValue = val; break; case MusEGui::NoteInfo::VAL_VELON: veloOnValue = val; break; case MusEGui::NoteInfo::VAL_VELOFF: veloOffValue = val; break; case MusEGui::NoteInfo::VAL_PITCH: pitchValue = val; break; } canvas->modifySelected(type, val, false); // No delta mode. } } } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void DrumEdit::writeStatus(int level, MusECore::Xml& xml) const { writePartList(level, xml); xml.tag(level++, "drumedit"); MidiEditor::writeStatus(level, xml); for (std::list::const_iterator i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) { (*i)->writeStatus(level, xml); } split1->writeStatus(level, xml); split2->writeStatus(level, xml); header->writeStatus(level, xml); xml.intTag(level, "steprec", canvas->steprec()); xml.intTag(level, "midiin", canvas->midiin()); xml.intTag(level, "tool", int(canvas->tool())); xml.intTag(level, "playEvents", _playEvents); xml.intTag(level, "xmag", hscroll->mag()); xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", vscroll->mag()); xml.intTag(level, "ypos", vscroll->pos()); xml.intTag(level, "ignore_hide", _ignore_hide); xml.tag(level, "/drumedit"); } //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void DrumEdit::readStatus(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "steprec") { int val = xml.parseInt(); canvas->setSteprec(val); srec->setChecked(val); } else if (tag == "midiin") { int val = xml.parseInt(); canvas->setMidiin(val); midiin->setChecked(val); } else if (tag == "tool") { int tool = xml.parseInt(); canvas->setTool(tool); tools2->set(tool); } else if (tag == "ctrledit") { CtrlEdit* ctrl = addCtrl(); ctrl->readStatus(xml); } else if (tag == split1->objectName()) split1->readStatus(xml); else if (tag == split2->objectName()) split2->readStatus(xml); else if (tag == "midieditor") MidiEditor::readStatus(xml); else if (tag == header->objectName()) header->readStatus(xml); else if (tag == "playEvents") { _playEvents = xml.parseInt(); canvas->setPlayEvents(_playEvents); speaker->setChecked(_playEvents); } else if (tag == "xmag") hscroll->setMag(xml.parseInt()); else if (tag == "xpos") hscroll->setPos(xml.parseInt()); else if (tag == "ymag") vscroll->setMag(xml.parseInt()); else if (tag == "ypos") vscroll->setPos(xml.parseInt()); else if (tag == "ignore_hide") _ignore_hide=xml.parseInt(); else xml.unknown("DrumEdit"); break; case MusECore::Xml::TagEnd: if (tag == "drumedit") { changeRaster(_raster); return; } default: break; } } } //--------------------------------------------------------- // readConfiguration //--------------------------------------------------------- void DrumEdit::readConfiguration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "raster") _rasterInit = xml.parseInt(); else if (tag == "trackinfowidth") _trackInfoWidthInit = xml.parseInt(); else if (tag == "canvaswidth") _canvasWidthInit = xml.parseInt(); else if (tag == "dcanvaswidth") _dcanvasWidthInit = xml.parseInt(); else if (tag == "dlistwidth") _dlistWidthInit = xml.parseInt(); else if (tag == "ignore_hide_init") _ignore_hide_init = xml.parseInt(); else if (tag == "topwin") TopWin::readConfiguration(DRUM, xml); else xml.unknown("DrumEdit"); break; case MusECore::Xml::TagEnd: if (tag == "drumedit") { return; } default: break; } } } //--------------------------------------------------------- // writeConfiguration //--------------------------------------------------------- void DrumEdit::writeConfiguration(int level, MusECore::Xml& xml) { xml.tag(level++, "drumedit"); xml.intTag(level, "raster", _rasterInit); xml.intTag(level, "trackinfowidth", _trackInfoWidthInit); xml.intTag(level, "canvaswidth", _canvasWidthInit); xml.intTag(level, "dlistwidth", _dlistWidthInit); xml.intTag(level, "dcanvaswidth", _dcanvasWidthInit); xml.intTag(level, "ignore_hide_init", _ignore_hide_init); TopWin::writeConfiguration(DRUM, level,xml); xml.tag(level, "/drumedit"); } //--------------------------------------------------------- // load //--------------------------------------------------------- void DrumEdit::load() { QString fn = MusEGui::getOpenFileName("drummaps", MusEGlobal::drum_map_file_pattern, this, tr("Muse: Load Drum Map"), 0); if (fn.isEmpty()) return; bool popenFlag; FILE* f = MusEGui::fileOpen(this, fn, QString(".map"), "r", popenFlag, true); if (f == 0) return; MusECore::Xml xml(f); int mode = 0; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (mode == 0 && tag == "muse") mode = 1; else if (mode == 1 && tag == "drummap") { MusEGlobal::audio->msgIdle(true); // Delete all port controller events. MusEGlobal::song->changeMidiCtrlCacheEvents(false, true, false, true, true); readDrumMap(xml, true); // Add all port controller events. MusEGlobal::song->changeMidiCtrlCacheEvents(true, true, false, true, true); MusEGlobal::audio->msgIdle(false); mode = 0; } else xml.unknown("DrumEdit"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (!mode && tag == "muse") goto ende; default: break; } } ende: if (popenFlag) pclose(f); else fclose(f); dlist->redraw(); canvas->redraw(); } //--------------------------------------------------------- // save //--------------------------------------------------------- void DrumEdit::save() { QString fn = MusEGui::getSaveFileName(QString("drummaps"), MusEGlobal::drum_map_file_save_pattern, this, tr("MusE: Store Drum Map")); if (fn.isEmpty()) return; bool popenFlag; FILE* f = MusEGui::fileOpen(this, fn, QString(".map"), "w", popenFlag, false, true); if (f == 0) return; MusECore::Xml xml(f); xml.header(); xml.tag(0, "muse version=\"1.0\""); writeDrumMap(1, xml, true); xml.tag(1, "/muse"); if (popenFlag) pclose(f); else fclose(f); } //--------------------------------------------------------- // reset //--------------------------------------------------------- void DrumEdit::reset() { if(QMessageBox::warning(this, tr("Drum map"), tr("Reset the drum map with GM defaults?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) == QMessageBox::Ok) { MusEGlobal::audio->msgIdle(true); // Delete all port controller events. MusEGlobal::song->changeMidiCtrlCacheEvents(false, true, false, true, true); MusECore::resetGMDrumMap(); // Add all port controller events. MusEGlobal::song->changeMidiCtrlCacheEvents(true, true, false, true, true); MusEGlobal::audio->msgIdle(false); dlist->redraw(); canvas->redraw(); } } //--------------------------------------------------------- // cmd // pulldown menu commands //--------------------------------------------------------- void DrumEdit::cmd(int cmd) { // Don't process if user is dragging or has clicked on the events. // Causes crashes later in Canvas::viewMouseMoveEvent and viewMouseReleaseEvent. if(canvas->getCurrentDrag()) return; MusECore::TagEventList tag_list; const FunctionDialogElements_t fn_element_dflt = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton | FunctionAllPartsButton | FunctionSelectedPartsButton; switch(cmd) { case DrumCanvas::CMD_CUT: tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::cut_items(&tag_list); break; case DrumCanvas::CMD_COPY: tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::copy_items(&tag_list); break; case DrumCanvas::CMD_COPY_RANGE: tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions(!itemsAreSelected(), true, true, MusEGlobal::song->lPos(), MusEGlobal::song->rPos())); MusECore::copy_items(&tag_list); break; case DrumCanvas::CMD_PASTE: ((DrumCanvas*)canvas)->cmd(DrumCanvas::CMD_SELECT_NONE); MusECore::paste_items(partlist_to_set(parts()), 3072, MusECore::FunctionOptionsStruct( MusECore::FunctionEraseItemsDefault | MusECore::FunctionPasteNeverNewPart)); break; case DrumCanvas::CMD_PASTE_TO_CUR_PART: ((DrumCanvas*)canvas)->cmd(DrumCanvas::CMD_SELECT_NONE); MusECore::paste_items(partlist_to_set(parts()), 3072, MusECore::FunctionOptionsStruct( MusECore::FunctionEraseItemsDefault | MusECore::FunctionPasteNeverNewPart), canvas->part()); break; case DrumCanvas::CMD_PASTE_DIALOG: ((DrumCanvas*)canvas)->cmd(DrumCanvas::CMD_SELECT_NONE); MusECore::paste_items(partlist_to_set(parts()), (canvas->part())); break; case DrumCanvas::CMD_MODIFY_VELOCITY: { FunctionDialogReturnVelocity ret = velocity_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::modify_velocity_items(&tag_list, ret._rateVal, ret._offsetVal); } break; } case DrumCanvas::CMD_CRESCENDO: { FunctionDialogReturnCrescendo ret = crescendo_items_dialog(FunctionDialogMode( FunctionLoopedButton | FunctionSelectedLoopedButton | FunctionAllPartsButton | FunctionSelectedPartsButton)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::crescendo_items(&tag_list, ret._start_val, ret._end_val, ret._absolute); } break; } case DrumCanvas::CMD_QUANTIZE: { FunctionDialogReturnQuantize ret = quantize_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::quantize_items(&tag_list, ret._raster_index, /*ret._quant_len*/ false, // DELETETHIS ret._strength, ret._swing, ret._threshold); } break; } case DrumCanvas::CMD_ERASE_EVENT: { FunctionDialogReturnErase ret = erase_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::erase_items(&tag_list, ret._veloThreshold, ret._veloThresUsed, ret._lenThreshold, ret._lenThresUsed); } } break; case DrumCanvas::CMD_DEL: tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::erase_items(&tag_list); break; case DrumCanvas::CMD_DELETE_OVERLAPS: { FunctionDialogReturnDelOverlaps ret = deloverlaps_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::delete_overlaps_items(&tag_list); } break; } case DrumCanvas::CMD_NOTE_SHIFT: { FunctionDialogReturnMove ret = move_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::move_items(&tag_list, ret._amount); } break; } //case DrumCanvas::CMD_FIXED_LEN: // this must be handled by the drum canvas, due to its // special nature (each drum has its own length) case DrumCanvas::CMD_GROUP_NONE: _group_mode=DONT_GROUP; ((DrumCanvas*)(canvas))->rebuildOurDrumMap(); break; case DrumCanvas::CMD_GROUP_CHAN: _group_mode=GROUP_SAME_CHANNEL; ((DrumCanvas*)(canvas))->rebuildOurDrumMap(); break; case DrumCanvas::CMD_GROUP_MAX: _group_mode=GROUP_MAX; ((DrumCanvas*)(canvas))->rebuildOurDrumMap(); break; default: ((DrumCanvas*)(canvas))->cmd(cmd); } } //--------------------------------------------------------- // clipboardChanged //--------------------------------------------------------- void DrumEdit::clipboardChanged() { const bool has_gel = QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists")); pasteAction->setEnabled(has_gel); pasteToCurPartAction->setEnabled(has_gel); pasteDialogAction->setEnabled(has_gel); } //--------------------------------------------------------- // selectionChanged //--------------------------------------------------------- void DrumEdit::selectionChanged() { bool flag = itemsAreSelected(); cutAction->setEnabled(flag); copyAction->setEnabled(flag); deleteAction->setEnabled(flag); } //--------------------------------------------------------- // ctrlPopupTriggered //--------------------------------------------------------- void DrumEdit::ctrlPopupTriggered(QAction* act) { // TODO Merge most of this with duplicate code in piano roll, // maybe by putting it in a new function near populateMidiCtrlMenu. if(!act || (act->data().toInt() == -1)) return; int newCtlNum = -1; MusECore::Part* part = curCanvasPart(); MusECore::MidiTrack* track = (MusECore::MidiTrack*)(part->track()); int channel = track->outChannel(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; MusECore::MidiCtrlValListList* cll = port->controller(); const int min = channel << 24; const int max = min + 0x1000000; const int edit_ins = max + 3; const int velo = max + 0x101; int rv = act->data().toInt(); if (rv == velo) { // special case velocity newCtlNum = MusECore::CTRL_VELOCITY; } else if (rv == edit_ins) { // edit instrument MusECore::MidiInstrument* instr = port->instrument(); MusEGlobal::muse->startEditInstrument(instr ? instr->iname() : QString(), EditInstrumentControllers); } else { // Select a control if(cll->find(channel, rv) == cll->end()) cll->add(channel, new MusECore::MidiCtrlValList(rv)); newCtlNum = rv; if(port->drumController(rv)) newCtlNum |= 0xff; } if(newCtlNum != -1) { CtrlEdit* ctrlEdit = new CtrlEdit(split1, this, _viewState.xscale(), _canvasXOrigin, 0, true, "drumCtrlEdit"); ctrlEdit->setController(newCtlNum); setupNewCtrl(ctrlEdit); } } //--------------------------------------------------------- // addCtrlClicked //--------------------------------------------------------- void DrumEdit::addCtrlClicked() { PopupMenu* pup = new PopupMenu(true); // true = enable stay open. Don't bother with parent. connect(pup, &QMenu::triggered, [this](QAction* act) { ctrlPopupTriggered(act); } ); int cur_instr = curDrumInstrument(); // HACK! New drum ctrl canvas current drum index is not the same as the editor current drum index. // Should try to fix this situation - two different values exist. Tim. cur_instr = (cur_instr & ~0xff) | get_instrument_map()[cur_instr].pitch; /*int est_width =*/ populateMidiCtrlMenu(pup, parts(), curCanvasPart(), cur_instr); QPoint ep = addctrl->mapToGlobal(QPoint(0,0)); //int newx = ep.x() - pup->width(); // Too much! Width says 640. Maybe because it hasn't been shown yet . // int newx = ep.x() - est_width; // if(newx < 0) // newx = 0; // ep.setX(newx); pup->exec(ep); delete pup; addctrl->setDown(false); } //--------------------------------------------------------- // ctrlMenuAboutToShow //--------------------------------------------------------- void DrumEdit::ctrlMenuAboutToShow() { // Clear the menu and delete the contents. // "Removes all the menu's actions. Actions owned by the menu and not shown // in any other widget are deleted." addControllerMenu->clear(); // Grab the current drum instrument so the menu can use it. int cur_instr = curDrumInstrument(); // HACK! New drum ctrl canvas current drum index is not the same as the editor current drum index. // Should try to fix this situation - two different values exist. Tim. cur_instr = (cur_instr & ~0xff) | get_instrument_map()[cur_instr].pitch; /*int est_width =*/ populateMidiCtrlMenu(addControllerMenu, parts(), curCanvasPart(), cur_instr); } //--------------------------------------------------------- // ctrlMenuAboutToHide //--------------------------------------------------------- void DrumEdit::ctrlMenuAboutToHide() { // Clear the menu and delete the contents, since it's going to be cleared // and refilled anyway next time opened, so we can save memory. // "Removes all the menu's actions. Actions owned by the menu and not shown // in any other widget are deleted." // FIXME: This crashes, of course... // addControllerMenu->clear(); } //--------------------------------------------------------- // addCtrl //--------------------------------------------------------- CtrlEdit* DrumEdit::addCtrl(int ctl_num) { CtrlEdit* ctrlEdit = new CtrlEdit(split1, this, _viewState.xscale(), _canvasXOrigin, 0, true, "drumCtrlEdit"); ctrlEdit->setController(ctl_num); setupNewCtrl(ctrlEdit); return ctrlEdit; } //--------------------------------------------------------- // setupNewCtrl //--------------------------------------------------------- void DrumEdit::setupNewCtrl(CtrlEdit* ctrlEdit) { connect(hscroll, SIGNAL(scrollChanged(int)), ctrlEdit, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), ctrlEdit, SLOT(setXMag(int))); connect(ctrlEdit, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(ctrlEdit, SIGNAL(destroyedCtrl(CtrlEdit*)), SLOT(removeCtrl(CtrlEdit*))); connect(ctrlEdit, SIGNAL(yposChanged(int)), toolbar, SLOT(setInt(int))); connect(ctrlEdit, SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*))); connect(tools2, SIGNAL(toolChanged(int)), ctrlEdit, SLOT(setTool(int))); connect(canvas, SIGNAL(curPartHasChanged(MusECore::Part*)), ctrlEdit, SLOT(curPartHasChanged(MusECore::Part*))); setCurDrumInstrument(dlist->getSelectedInstrument()); ctrlEdit->setTool(tools2->curTool()); ctrlEdit->setXPos(hscroll->pos()); ctrlEdit->setXMag(hscroll->getScaleValue()); if(split1w1) { split2->setCollapsible(split2->indexOf(split1w1), false); split1w1->setMinimumWidth(CTRL_PANEL_FIXED_WIDTH); } int dw = vscroll->width() - 18;// 18 is the fixed width of the CtlEdit VScale widget. if(dw < 1) dw = 1; ctrlEdit->setCanvasWidth(canvas->width() + dw); ctrlEdit->show(); ctrlEditList.push_back(ctrlEdit); } //--------------------------------------------------------- // removeCtrl //--------------------------------------------------------- void DrumEdit::removeCtrl(CtrlEdit* ctrl) { for (std::list::iterator i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) { if (*i == ctrl) { ctrlEditList.erase(i); break; } } if(split1w1) { if(ctrlEditList.empty()) { split1w1->setMinimumWidth(0); split2->setCollapsible(split2->indexOf(split1w1), true); } } } //--------------------------------------------------------- // getViewState //--------------------------------------------------------- MusECore::MidiPartViewState DrumEdit::getViewState() const { MusECore::MidiPartViewState vs; vs.setXScroll(hscroll->offset()); vs.setYScroll(vscroll->offset()); vs.setXScale(hscroll->getScaleValue()); vs.setYScale(vscroll->getScaleValue()); CtrlEdit* ce; for(CtrlEditList::const_iterator i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) { ce = *i; vs.addController(MusECore::MidiCtrlViewState(ce->ctrlNum(), ce->perNoteVel())); } return vs; } void DrumEdit::storeInitialViewState() const { const MusECore::PartList* pl = parts(); if(pl) { const MusECore::MidiPartViewState vs = getViewState(); for(MusECore::ciPart i = pl->begin(); i != pl->end(); ++i) i->second->setViewState(vs); } } //--------------------------------------------------------- // newCanvasWidth //--------------------------------------------------------- void DrumEdit::newCanvasWidth(int w) { int nw = w + (vscroll->width() - 18); // 18 is the fixed width of the CtlEdit VScale widget. if(nw < 1) nw = 1; for (std::list::iterator i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) (*i)->setCanvasWidth(nw); updateHScrollRange(); } //TODO: Make the dlist not expand/shrink, but the canvas instead //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void DrumEdit::configChanged() { if (MusEGlobal::config.canvasBgPixmap.isEmpty()) { canvas->setBg(MusEGlobal::config.midiCanvasBg); canvas->setBg(QPixmap()); } else { canvas->setBg(QPixmap(MusEGlobal::config.canvasBgPixmap)); } dlist->setBg(MusEGlobal::config.drumListBg); toolbar->setGridOn(MusEGlobal::config.canvasShowGrid); initShortcuts(); canvas->redraw(); } //--------------------------------------------------------- // keyPressEvent //--------------------------------------------------------- void DrumEdit::keyPressEvent(QKeyEvent* event) { DrumCanvas* dc = (DrumCanvas*)canvas; RasterizerModel::RasterPick rast_pick = RasterizerModel::NoPick; const int cur_rast = raster(); int key = event->key(); if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) key += Qt::SHIFT; if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) key += Qt::ALT; if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) key+= Qt::CTRL; if (key == Qt::Key_Escape) { close(); return; } else if (key == shortcuts[SHRT_CURSOR_STEP_DOWN].key) { int newIndex=stepLenWidget->currentIndex()-1; if (newIndex < 0) newIndex=0; stepLenWidget->setCurrentIndex(newIndex); return; } else if (key == shortcuts[SHRT_CURSOR_STEP_UP].key) { int newIndex=stepLenWidget->currentIndex()+1; if (newIndex > stepLenWidget->count()-1) newIndex=stepLenWidget->count()-1; stepLenWidget->setCurrentIndex(newIndex); return; } else if (key == Qt::Key_F2) { dlist->lineEdit(dlist->getSelectedInstrument(),(int)DList::COL_NAME); return; } else if (key == shortcuts[SHRT_INSTRUMENT_STEP_UP].key) { dlist->setCurDrumInstrument(dlist->getSelectedInstrument()-1); dlist->redraw(); ((DrumCanvas*)canvas)->selectCursorEvent(((DrumCanvas*)canvas)->getEventAtCursorPos()); ((DrumCanvas*)canvas)->keyPressed(dlist->getSelectedInstrument(),100); MusEGlobal::song->update(SC_DRUMMAP); return; } else if (key == shortcuts[SHRT_INSTRUMENT_STEP_DOWN].key) { dlist->setCurDrumInstrument(dlist->getSelectedInstrument()+1); dlist->redraw(); ((DrumCanvas*)canvas)->selectCursorEvent(((DrumCanvas*)canvas)->getEventAtCursorPos()); ((DrumCanvas*)canvas)->keyPressed(dlist->getSelectedInstrument(),100); MusEGlobal::song->update(SC_DRUMMAP); return; } else if (key == shortcuts[SHRT_POS_INC].key) { dc->cmd(DrumCanvas::CMD_RIGHT); return; } else if (key == shortcuts[SHRT_POS_DEC].key) { dc->cmd(DrumCanvas::CMD_LEFT); return; } else if (key == shortcuts[SHRT_POS_INC_NOSNAP].key) { dc->cmd(DrumCanvas::CMD_RIGHT_NOSNAP); return; } else if (key == shortcuts[SHRT_POS_DEC_NOSNAP].key) { dc->cmd(DrumCanvas::CMD_LEFT_NOSNAP); return; } else if (key == shortcuts[SHRT_TOOL_POINTER].key) { tools2->set(MusEGui::PointerTool); return; } else if (key == shortcuts[SHRT_TOOL_PENCIL].key) { tools2->set(MusEGui::PencilTool); return; } else if (key == shortcuts[SHRT_TOOL_RUBBER].key) { tools2->set(MusEGui::RubberTool); return; } else if (key == shortcuts[SHRT_TOOL_LINEDRAW].key) { tools2->set(MusEGui::DrawTool); return; } else if (key == shortcuts[SHRT_TOOL_CURSOR].key) { tools2->set(MusEGui::CursorTool); canvas->setFocus(); return; } else if (key == shortcuts[SHRT_TOOL_PAN].key) { tools2->set(MusEGui::PanTool); return; } else if (key == shortcuts[SHRT_TOOL_ZOOM].key) { tools2->set(MusEGui::ZoomTool); return; } else if (key == shortcuts[SHRT_ZOOM_IN].key) { horizontalZoom(true, QCursor::pos()); return; } else if (key == shortcuts[SHRT_ZOOM_OUT].key) { horizontalZoom(false, QCursor::pos()); return; } else if (key == shortcuts[SHRT_SCROLL_LEFT].key) { int pos = hscroll->pos() - MusEGlobal::config.division; if (pos < 0) pos = 0; hscroll->setPos(pos); return; } else if (key == shortcuts[SHRT_SCROLL_RIGHT].key) { int pos = hscroll->pos() + MusEGlobal::config.division; hscroll->setPos(pos); return; } else if (key == shortcuts[SHRT_SET_QUANT_BAR].key) rast_pick = RasterizerModel::GotoBar; else if (key == shortcuts[SHRT_SET_QUANT_OFF].key) //this hack has the downside that the next shortcut will use triols, but it's better than not having it, I think... rast_pick = RasterizerModel::GotoOff; else if (key == shortcuts[SHRT_SET_QUANT_1].key) rast_pick = RasterizerModel::Goto1; else if (key == shortcuts[SHRT_SET_QUANT_2].key) rast_pick = RasterizerModel::Goto2; else if (key == shortcuts[SHRT_SET_QUANT_3].key) rast_pick = RasterizerModel::Goto4; else if (key == shortcuts[SHRT_SET_QUANT_4].key) rast_pick = RasterizerModel::Goto8; else if (key == shortcuts[SHRT_SET_QUANT_5].key) rast_pick = RasterizerModel::Goto16; else if (key == shortcuts[SHRT_SET_QUANT_6].key) rast_pick = RasterizerModel::Goto32; else if (key == shortcuts[SHRT_SET_QUANT_7].key) rast_pick = RasterizerModel::Goto64; else if (key == shortcuts[SHRT_TOGGLE_TRIOL].key) rast_pick = RasterizerModel::ToggleTriple; else if (key == shortcuts[SHRT_TOGGLE_PUNCT].key) rast_pick = RasterizerModel::ToggleDotted; else if (key == shortcuts[SHRT_TOGGLE_PUNCT2].key) rast_pick = RasterizerModel::ToggleHigherDotted; else if (key == shortcuts[SHRT_MOVE_PLAY_TO_NOTE].key){ movePlayPointerToSelectedEvent(); return; } else if (key == shortcuts[SHRT_STEP_RECORD].key) { canvas->setSteprec(!srec->isChecked()); srec->setChecked(!srec->isChecked()); return; } else if (key == shortcuts[SHRT_MIDI_INPUT].key) { canvas->setMidiin(!midiin->isChecked()); midiin->setChecked(!midiin->isChecked()); return; } else if (key == shortcuts[SHRT_PLAY_EVENTS].key) { canvas->setPlayEvents(!speaker->isChecked()); speaker->setChecked(!speaker->isChecked()); return; } else if (key == shortcuts[SHRT_INC_VELOCITY].key) { // modify_velocity(partlist_to_set(parts()), 1, 100, 1); MusECore::TagEventList tag_list; tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::modify_velocity_items(&tag_list, 100, 1); return; } else if (key == shortcuts[SHRT_DEC_VELOCITY].key) { // modify_velocity(partlist_to_set(parts()), 1, 100, -1); MusECore::TagEventList tag_list; tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::modify_velocity_items(&tag_list, 100, -1); return; } else { //Default: event->ignore(); return; } if(rast_pick != RasterizerModel::NoPick) { const int new_rast = _rasterizerModel->pickRaster(cur_rast, rast_pick); if(new_rast != cur_rast) { setRaster(new_rast); toolbar->setRaster(_raster); } } } //--------------------------------------------------------- // setSpeaker //--------------------------------------------------------- void DrumEdit::setSpeaker(bool val) { _playEvents = val; canvas->setPlayEvents(_playEvents); } //--------------------------------------------------------- // initShortcuts //--------------------------------------------------------- void DrumEdit::initShortcuts() { cutAction->setShortcut(shortcuts[SHRT_CUT].key); copyAction->setShortcut(shortcuts[SHRT_COPY].key); copyRangeAction->setShortcut(shortcuts[SHRT_COPY_RANGE].key); pasteAction->setShortcut(shortcuts[SHRT_PASTE].key); pasteToCurPartAction->setShortcut(shortcuts[SHRT_PASTE_TO_CUR_PART].key); pasteDialogAction->setShortcut(shortcuts[SHRT_PASTE_DIALOG].key); deleteAction->setShortcut(shortcuts[SHRT_DELETE].key); fixedAction->setShortcut(shortcuts[SHRT_FIXED_LEN].key); veloAction->setShortcut(shortcuts[SHRT_MODIFY_VELOCITY].key); sallAction->setShortcut(shortcuts[SHRT_SELECT_ALL].key); snoneAction->setShortcut(shortcuts[SHRT_SELECT_NONE].key); invAction->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); inAction->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key); outAction->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); selectRangeToSelectionAction->setShortcut(shortcuts[SHRT_LOCATORS_TO_SELECTION].key); if (prevAction && nextAction) { prevAction->setShortcut(shortcuts[SHRT_SELECT_PREV_PART].key); nextAction->setShortcut(shortcuts[SHRT_SELECT_NEXT_PART].key); } startListEditAction->setShortcut(shortcuts[SHRT_OPEN_LIST].key); } //--------------------------------------------------------- // execDeliveredScript //--------------------------------------------------------- void DrumEdit::execDeliveredScript(int id) { QString scriptfile = scripts.getScriptPath(id, true); scripts.executeScript(this, scriptfile.toLatin1().constData(), parts(), raster(), true); } //--------------------------------------------------------- // execUserScript //--------------------------------------------------------- void DrumEdit::execUserScript(int id) { QString scriptfile = scripts.getScriptPath(id, false); scripts.executeScript(this, scriptfile.toLatin1().constData(), parts(), raster(), true); } void DrumEdit::setStep(QString v) { ((DrumCanvas*)canvas)->setStep(v.toInt()); focusCanvas(); } void DrumEdit::ourDrumMapChanged(bool instrMapChanged) { if (instrMapChanged) { int vmin,vmax; vscroll->range(&vmin, &vmax); vscroll->setRange(vmin, dynamic_cast(canvas)->getOurDrumMapSize()*TH); } } void DrumEdit::set_ignore_hide(bool val) { _ignore_hide=val; _ignore_hide_init=val; // this may only called be from the action's toggled signal. // if called otherwise, the action's checked state isn't updated! if (_ignore_hide) header->showSection(DList::COL_HIDE); else header->hideSection(DList::COL_HIDE); ((DrumCanvas*)(canvas))->rebuildOurDrumMap(); } void DrumEdit::showAllInstruments() { using MusECore::MidiTrack; QSet tracks; for (const auto& it : *parts()) tracks.insert((MidiTrack*)it.second->track()); for (auto& it : tracks) { MidiTrack* track = it; for (int i=0;i<128;i++) track->drummap()[i].hide=false; } MusEGlobal::song->update(SC_DRUMMAP); } void DrumEdit::hideAllInstruments() { using MusECore::MidiTrack; QSet tracks; for (const auto& it : *parts()) tracks.insert((MidiTrack*)it.second->track()); for (auto& it : tracks) { MidiTrack* track = it; for (int i=0;i<128;i++) track->drummap()[i].hide=true; } MusEGlobal::song->update(SC_DRUMMAP); } void DrumEdit::hideUnusedInstruments() { using MusECore::MidiTrack; QSet tracks; for (const auto& it : *parts()) tracks.insert((MidiTrack*)it.second->track()); for (auto& it : tracks) { MidiTrack* track = it; bool hide[128]; for (int i=0; i<128; i++) hide[i]=true; for (const auto& itp : *parts()) if (itp.second->track() == track) { for (const auto& ev : itp.second->events()) { if (ev.second.type() != MusECore::Note) continue; hide[ev.second.pitch()] = false; } } for (int i=0; i<128; i++) track->drummap()[i].hide = hide[i]; } MusEGlobal::song->update(SC_DRUMMAP); } void DrumEdit::hideEmptyInstruments() { using MusECore::MidiTrack; QSet tracks; for (const auto& it : *parts()) tracks.insert((MidiTrack*)it.second->track()); for (auto& it : tracks) { MidiTrack* track = it; bool hide[128]; for (int i=0; i<128; i++) hide[i]=track->drummap()[i].name.isEmpty(); for (const auto& itp : *parts()) if (itp.second->track() == track) { for (const auto& ev : itp.second->events()) { if (ev.second.type() != MusECore::Note) continue; hide[ev.second.pitch()] = false; } } for (int i=0; i<128;i++) track->drummap()[i].hide=hide[i]; } MusEGlobal::song->update(SC_DRUMMAP); } //--------------------------------------------------------- // setupHZoomRange //--------------------------------------------------------- void DrumEdit::setupHZoomRange() { const int min = (_minXMag * MusEGlobal::config.division) / 384; hscroll->setScaleRange(min, _maxXMag); } } // namespace MusEGui muse-4.0.0/src/muse/midiedit/drumedit.h000066400000000000000000000146351404105311000200000ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: drumedit.h,v 1.9.2.7 2009/11/16 11:29:33 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DRUM_EDIT_H__ #define __DRUM_EDIT_H__ #include #include "type_defs.h" #include "part.h" #include "midieditor.h" #include "noteinfo.h" #include "dcanvas.h" #include "scripts.h" #include #include // Forward declarations: class QAction; class QCloseEvent; class QMenu; class QKeyEvent; class QToolButton; class QWidget; class QComboBox; class QPoint; class QToolBar; namespace MusECore { class Event; class Xml; } namespace MusEGui { class TopWin; class CtrlEdit; class DList; class Header; class Splitter; class Toolbar1; class PopupMenu; class EditToolBar; //--------------------------------------------------------- // DrumEdit //--------------------------------------------------------- class DrumEdit : public MidiEditor { Q_OBJECT public: enum group_mode_t { DONT_GROUP, GROUP_SAME_CHANNEL, GROUP_MAX }; private: group_mode_t _group_mode; bool _ignore_hide; QMenu* menuEdit, *menuFunctions, *menuSelect, *menuScripts; PopupMenu* addControllerMenu; int tickValue; int lenValue; int pitchValue; int veloOnValue; int veloOffValue; bool firstValueSet; int tickOffset; int lenOffset; int pitchOffset; int veloOnOffset; int veloOffOffset; bool deltaMode; int lastSelections; MusEGui::NoteInfo* info; QToolButton* addctrl; QToolButton* srec; QToolButton* midiin; QToolButton* speaker; MusEGui::EditToolBar* tools2; bool _playEvents; MusECore::Scripts scripts; MusEGui::Toolbar1* toolbar; MusEGui::Splitter* hsplitter; MusEGui::Splitter* split1; MusEGui::Splitter* split2; QWidget* split1w1; DList* dlist; MusEGui::Header* header; QToolBar* tools; QComboBox *stepLenWidget; static int _rasterInit; static int _trackInfoWidthInit; static int _canvasWidthInit; static int _dlistWidthInit, _dcanvasWidthInit; static bool _ignore_hide_init; // Initial view state. MusECore::MidiPartViewState _viewState; QAction *cutAction, *copyAction, *copyRangeAction, *pasteAction; QAction *pasteToCurPartAction, *pasteDialogAction, *deleteAction; QAction *fixedAction, *veloAction, *crescAction, *quantizeAction; QAction *sallAction, *snoneAction, *invAction, *inAction , *outAction; QAction *prevAction, *nextAction; QAction *groupNoneAction, *groupChanAction, *groupMaxAction; QAction *addControllerAction; QAction *startListEditAction; QAction* selectRangeToSelectionAction; void initShortcuts(); void setupNewCtrl(CtrlEdit* ctrlEdit); void closeEvent(QCloseEvent*) override; QWidget* genToolbar(QWidget* parent); void keyPressEvent(QKeyEvent*) override; void setHeaderToolTips(); void setHeaderWhatsThis(); void setHeaderStatusTip(); // Sets up a reasonable zoom minimum and/or maximum based on // the current global midi division (ticks per quarter note) // which has a very wide range (48 - 12288). // Also sets the canvas and time scale offsets accordingly. void setupHZoomRange(); private slots: void setRaster(int) override; void noteinfoChanged(MusEGui::NoteInfo::ValType type, int val); void removeCtrl(CtrlEdit* ctrl); void cmd(int); void clipboardChanged(); // enable/disable "Paste" void selectionChanged(); // enable/disable "Copy" & "Paste" void load(); void save(); void reset(); void setTime(unsigned); void follow(int); void newCanvasWidth(int); void configChanged(); void songChanged1(MusECore::SongChangedStruct_t); void setStep(QString); void setSpeaker(bool); void addCtrlClicked(); void ctrlPopupTriggered(QAction* act); void ctrlMenuAboutToShow(); void ctrlMenuAboutToHide(); void gridOnChanged(bool); void set_ignore_hide(bool); void showAllInstruments(); void hideAllInstruments(); void hideUnusedInstruments(); void hideEmptyInstruments(); void deltaModeChanged(bool); void midiNote(int pitch, int velo); public slots: void setSelection(int tick, MusECore::Event&, MusECore::Part*, bool update); void soloChanged(bool); // called by Solo button void execDeliveredScript(int); void execUserScript(int); void focusCanvas() override; void ourDrumMapChanged(bool); void horizontalZoom(bool zoom_in, const QPoint& glob_pos); void horizontalZoom(int mag, const QPoint& glob_pos); virtual void updateHScrollRange() override; void storeInitialViewState() const override; signals: void isDeleting(MusEGui::TopWin*); public: DrumEdit(MusECore::PartList*, QWidget* parent = nullptr, const char* name = nullptr, unsigned initPos = INT_MAX, bool showDefaultControls = false); virtual void readStatus(MusECore::Xml&) override; virtual void writeStatus(int, MusECore::Xml&) const override; static void readConfiguration(MusECore::Xml& xml); static void writeConfiguration(int, MusECore::Xml&); CtrlEdit* addCtrl(int ctl_num = MusECore::CTRL_VELOCITY); MusECore::MidiPartViewState getViewState() const; int changeRaster(int val); group_mode_t group_mode() { return _group_mode; } bool ignore_hide() { return _ignore_hide; } QVector& get_instrument_map() { return static_cast(canvas)->get_instrument_map(); } void storeSettings() override; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/midiedit/drummap.cpp000066400000000000000000000672651404105311000201720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: drummap.cpp,v 1.3.2.6 2009/10/29 02:14:37 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "audio.h" #include "drummap.h" #include "xml.h" #include "song.h" #include "helper.h" #include namespace MusEGlobal { char drumOutmap[DRUM_MAPSIZE]; char drumInmap[128]; MusECore::DrumMap drumMap[DRUM_MAPSIZE]; } namespace MusECore { //--------------------------------------------------------- // GM default drum map //--------------------------------------------------------- // Default to track port if -1 and track channel if -1. (These used to say 9, 0 for chan, port). const DrumMap blankdm = { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 127, 127, false, false }; // this map should have 128 entries, as it's used for initialising iNewDrumMap as well. // iNewDrumMap only has 128 entries. also, the every "out-note" ("anote") should be // represented exactly once in idrumMap, and there shall be no duplicate or unused // "out-notes". // reason: iNewDrumMap is inited as follows: iterate through the full idrumMap[], // iNewDrumMap[ idrumMap[i].anote ] = idrumMap[i] // if you ever want to change this, you will need to fix the initNewDrumMap() function. const DrumMap idrumMap[DRUM_MAPSIZE] = { { QString("Acoustic Bass Drum"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 35, 35, false, false }, { QString("Bass Drum 1"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 36, 36, false, false }, { QString("Side Stick"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 37, 37, false, false }, { QString("Acoustic Snare"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 38, 38, false, false }, { QString("Hand Clap"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 39, 39, false, false }, { QString("Electric Snare"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 40, 40, false, false }, { QString("Low Floor Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 41, 41, false, false }, { QString("Closed Hi-Hat"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 42, 42, false, false }, { QString("High Floor Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 43, 43, false, false }, { QString("Pedal Hi-Hat"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 44, 44, false, false }, { QString("Low Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 45, 45, false, false }, { QString("Open Hi-Hat"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 46, 46, false, false }, { QString("Low-Mid Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 47, 47, false, false }, { QString("Hi-Mid Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 48, 48, false, false }, { QString("Crash Cymbal 1"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 49, 49, false, false }, { QString("High Tom"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 50, 50, false, false }, { QString("Ride Cymbal 1"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 51, 51, false, false }, { QString("Chinese Cymbal"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 52, 52, false, false }, { QString("Ride Bell"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 53, 53, false, false }, { QString("Tambourine"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 54, 54, false, false }, { QString("Splash Cymbal"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 55, 55, false, false }, { QString("Cowbell"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 56, 56, false, false }, { QString("Crash Cymbal 2"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 57, 57, false, false }, { QString("Vibraslap"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 58, 58, false, false }, { QString("Ride Cymbal 2"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 59, 59, false, false }, { QString("Hi Bongo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 60, 60, false, false }, { QString("Low Bongo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 61, 61, false, false }, { QString("Mute Hi Conga"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 62, 62, false, false }, { QString("Open Hi Conga"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 63, 63, false, false }, { QString("Low Conga"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 64, 64, false, false }, { QString("High Timbale"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 65, 65, false, false }, { QString("Low Timbale"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 66, 66, false, false }, { QString("High Agogo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 67, 67, false, false }, { QString("Low Agogo"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 68, 68, false, false }, { QString("Cabasa"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 69, 69, false, false }, { QString("Maracas"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 70, 70, false, false }, { QString("Short Whistle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 71, 71, false, false }, { QString("Long Whistle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 72, 72, false, false }, { QString("Short Guiro"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 73, 73, false, false }, { QString("Long Guiro"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 74, 74, false, false }, { QString("Claves"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 75, 75, false, false }, { QString("Hi Wood Block"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 76, 76, false, false }, { QString("Low Wood Block"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 77, 77, false, false }, { QString("Mute Cuica"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 78, 78, false, false }, { QString("Open Cuica"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 79, 79, false, false }, { QString("Mute Triangle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 80, 80, false, false }, { QString("Open Triangle"), 100, 16, 32, -1, -1, 70, 90, 110, 127, 81, 81, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 82, 82, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 83, 83, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 84, 84, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 85, 85, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 86, 86, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 87, 87, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 88, 88, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 89, 89, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 90, 90, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 91, 91, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 92, 92, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 93, 93, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 94, 94, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 95, 95, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 96, 96, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 97, 97, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 98, 98, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 99, 99, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 100, 100, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 101, 101, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 102, 102, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 103, 103, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 104, 104, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 105, 105, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 106, 106, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 107, 107, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 108, 108, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 109, 109, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 110, 110, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 111, 111, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 112, 112, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 113, 113, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 114, 114, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 115, 115, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 116, 116, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 117, 117, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 118, 118, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 119, 119, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 120, 120, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 121, 121, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 122, 122, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 123, 123, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 124, 124, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 125, 125, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 126, 126, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 127, 127, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 0, 0, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 1, 1, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 2, 2, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 3, 3, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 4, 4, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 5, 5, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 6, 6, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 7, 7, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 8, 8, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 9, 9, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 10, 10, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 11, 11, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 12, 12, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 13, 13, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 14, 14, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 15, 15, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 16, 16, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 17, 17, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 18, 18, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 19, 19, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 20, 20, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 21, 21, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 22, 22, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 23, 23, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 24, 24, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 25, 25, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 26, 26, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 27, 27, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 28, 28, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 29, 29, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 30, 30, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 31, 31, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 32, 32, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 33, 33, false, false }, { QString(""), 100, 16, 32, -1, -1, 70, 90, 110, 127, 34, 34, false, false } }; DrumMap iNewDrumMap[128]; void initNewDrumMap() { bool done[128]; for (int i=0;i<128;i++) done[i]=false; for (int i=0;i= 128) fprintf(stderr, "ERROR: THIS SHOULD NEVER HAPPEN: idrumMap[%i].anote is not within 0..127!\n", idx); else { if (done[idx]==true) { fprintf(stderr, "ERROR: iNewDrumMap[%i] is already initialized!\n" " this will be probably not a problem, but some programmer didn't read\n" " flo's comment at drummap.cpp, above idrumMap[].\n", idx); } else { iNewDrumMap[idx]=idrumMap[i]; done[idx]=true; } } } for (int i=0;i<128;i++) { if (done[i]==false) { fprintf(stderr, "ERROR: iNewDrumMap[%i] is uninitialized!\n" " this will be probably not a problem, but some programmer didn't read\n" " flo's comment at drummap.cpp, above idrumMap[].\n", i); iNewDrumMap[i].name=""; iNewDrumMap[i].vol=100; iNewDrumMap[i].quant=16; iNewDrumMap[i].len=32; iNewDrumMap[i].lv1=70; iNewDrumMap[i].lv2=90; iNewDrumMap[i].lv3=127; iNewDrumMap[i].lv4=110; iNewDrumMap[i].enote=i; iNewDrumMap[i].anote=i; iNewDrumMap[i].mute=false; iNewDrumMap[i].hide=false; } } } //--------------------------------------------------------- // clearDrumMap // One-time only early init //--------------------------------------------------------- void clearDrumMap() { for (int i = 0; i < DRUM_MAPSIZE; ++i) { DrumMap& d = MusEGlobal::drumMap[i]; d.vol = d.len = d.channel = d.port = d.lv1 = d.lv2 = d.lv3 = d.lv4 = d.enote = d.anote = 0; d.mute = d.hide = false; } } //--------------------------------------------------------- // initDrumMap // populate Inmap and Outmap //--------------------------------------------------------- void initDrumMap() { for (int i = 0; i < DRUM_MAPSIZE; ++i) { DrumMap& d = MusEGlobal::drumMap[i]; //Make sure we're not overwriting any values loaded //On init, all these values are zero. If so, just set the drummap entry to the initial drummap entry. if (!(d.vol || d.len || d.channel || d.port || d.lv1 || d.lv2 || d.lv3 || d.lv4 || d.enote || d.anote || d.mute || d.hide)) MusEGlobal::drumMap[i] = idrumMap[i]; } //Finally, setup the inMap, outMap-values memset(MusEGlobal::drumInmap, 0, sizeof(MusEGlobal::drumInmap)); memset(MusEGlobal::drumOutmap, 0, sizeof(MusEGlobal::drumOutmap)); for (int i = 0; i < DRUM_MAPSIZE; ++i) { MusEGlobal::drumInmap[(unsigned int)(MusEGlobal::drumMap[i].enote)] = i; MusEGlobal::drumOutmap[(unsigned int)(MusEGlobal::drumMap[i].anote)] = i; } } //--------------------------------------------------------- // resetGMDrumMap //--------------------------------------------------------- void resetGMDrumMap() { for(int i = 0; i < DRUM_MAPSIZE; ++i) MusEGlobal::drumMap[i] = idrumMap[i]; memset(MusEGlobal::drumInmap, 0, sizeof(MusEGlobal::drumInmap)); memset(MusEGlobal::drumOutmap, 0, sizeof(MusEGlobal::drumOutmap)); for (int i = 0; i < DRUM_MAPSIZE; ++i) { MusEGlobal::drumInmap[(unsigned int)(MusEGlobal::drumMap[i].enote)] = i; MusEGlobal::drumOutmap[(unsigned int)(MusEGlobal::drumMap[i].anote)] = i; } } //--------------------------------------------------------- // operator == //--------------------------------------------------------- bool DrumMap::operator==(const DrumMap& map) const { // Everything equal, including mute and hide settings? return almost_equals(map) && mute == map.mute && hide == map.hide; } bool DrumMap::almost_equals(const DrumMap& map) const { // Disregarding mute and hide settings. return name == map.name && vol == map.vol && quant == map.quant && len == map.len && channel == map.channel && port == map.port && lv1 == map.lv1 && lv2 == map.lv2 && lv3 == map.lv3 && lv4 == map.lv4 && enote == map.enote && anote == map.anote; } void DrumMap::dump() { fprintf(stderr, "%s\t\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d(%s)\t\t%d(%s)\t\t%d\t%d\t\n", name.toLatin1().constData(), vol, quant, len, channel, port, lv1, lv2, lv3, lv4, enote, pitch2string(enote).toLatin1().constData(), anote, pitch2string(anote).toLatin1().constData(), mute, hide); } //--------------------------------------------------------- // writeDrumMap //--------------------------------------------------------- void writeDrumMap(int level, Xml& xml, bool external) { xml.tag(level++, "drummap"); for (int i = 0; i < DRUM_MAPSIZE; ++i) { DrumMap* dm = &MusEGlobal::drumMap[i]; const DrumMap* idm = &idrumMap[i]; if (external) { xml.tag(level++, "entry"); xml.strTag(level, "name", dm->name); xml.intTag(level, "vol", dm->vol); xml.intTag(level, "quant", dm->quant); xml.intTag(level, "len", dm->len); xml.intTag(level, "channel", dm->channel); xml.intTag(level, "port", dm->port); xml.intTag(level, "lv1", dm->lv1); xml.intTag(level, "lv2", dm->lv2); xml.intTag(level, "lv3", dm->lv3); xml.intTag(level, "lv4", dm->lv4); xml.intTag(level, "enote", dm->enote); xml.intTag(level, "anote", dm->anote); xml.intTag(level, "hide", dm->hide); } else { // write only, if entry is different from initial entry if (!external && *dm == *idm) continue; xml.tag(level++, "entry idx=\"%d\"", i); if (dm->name != idm->name) xml.strTag(level, "name", dm->name); if (dm->vol != idm->vol) xml.intTag(level, "vol", dm->vol); if (dm->quant != idm->quant) xml.intTag(level, "quant", dm->quant); if (dm->len != idm->len) xml.intTag(level, "len", dm->len); if (dm->channel != idm->channel) xml.intTag(level, "channel", dm->channel); if (dm->port != idm->port) xml.intTag(level, "port", dm->port); if (dm->lv1 != idm->lv1) xml.intTag(level, "lv1", dm->lv1); if (dm->lv2 != idm->lv2) xml.intTag(level, "lv2", dm->lv2); if (dm->lv3 != idm->lv3) xml.intTag(level, "lv3", dm->lv3); if (dm->lv4 != idm->lv4) xml.intTag(level, "lv4", dm->lv4); if (dm->enote != idm->enote) xml.intTag(level, "enote", dm->enote); if (dm->anote != idm->anote) xml.intTag(level, "anote", dm->anote); if (dm->mute != idm->mute) xml.intTag(level, "mute", dm->mute); if (dm->hide != idm->hide) xml.intTag(level, "hide", dm->hide); } xml.tag(level--, "/entry"); } xml.tag(level--, "/drummap"); } //--------------------------------------------------------- // readDrummapEntry //--------------------------------------------------------- static void readDrummapEntry(Xml& xml, DrumMap* dm) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "name") dm->name = xml.parse(QString("name")); else if (tag == "vol") dm->vol = (unsigned char)xml.parseInt(); else if (tag == "quant") dm->quant = xml.parseInt(); else if (tag == "len") dm->len = xml.parseInt(); else if (tag == "channel") dm->channel = xml.parseInt(); else if (tag == "port") dm->port = xml.parseInt(); else if (tag == "lv1") dm->lv1 = xml.parseInt(); else if (tag == "lv2") dm->lv2 = xml.parseInt(); else if (tag == "lv3") dm->lv3 = xml.parseInt(); else if (tag == "lv4") dm->lv4 = xml.parseInt(); else if (tag == "enote") dm->enote = xml.parseInt(); else if (tag == "anote") dm->anote = xml.parseInt(); else if (tag == "mute") dm->mute = xml.parseInt(); else if (tag == "hide") dm->hide = xml.parseInt(); else if (tag == "selected") //; // dm->selected = xml.parseInt(); xml.skip(tag); else xml.unknown("entry"); break; case Xml::Attribut: if (tag == "idx") { int idx = xml.s2().toInt() & 0x7f; dm = &MusEGlobal::drumMap[idx]; } break; case Xml::TagEnd: if (tag == "entry") { return; } default: break; } } } //--------------------------------------------------------- // readDrummap //--------------------------------------------------------- void readDrumMap(Xml& xml, bool external) { if (external) { for (int i = 0; i < DRUM_MAPSIZE; ++i) MusEGlobal::drumMap[i] = blankdm; } else { for (int i = 0; i < DRUM_MAPSIZE; ++i) MusEGlobal::drumMap[i] = idrumMap[i]; } int i = 0; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: // goto read_drummap_end; return; case Xml::TagStart: if (tag == "entry") { if(i >= DRUM_MAPSIZE) // goto read_drummap_end; return; readDrummapEntry(xml, external ? &MusEGlobal::drumMap[i] : 0); ++i; } else if (tag == "comment") xml.parse(); else xml.unknown("DrumMap"); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "drummap") { memset(MusEGlobal::drumInmap, 0, sizeof(MusEGlobal::drumInmap)); memset(MusEGlobal::drumOutmap, 0, sizeof(MusEGlobal::drumOutmap)); for (int i = 0; i < DRUM_MAPSIZE; ++i) { MusEGlobal::drumInmap[(unsigned int)(MusEGlobal::drumMap[i].enote)] = i; MusEGlobal::drumOutmap[(unsigned int)(MusEGlobal::drumMap[i].anote)] = i; } // goto read_drummap_end; return; } default: break; } } } } // namespace MusECore muse-4.0.0/src/muse/midiedit/drummap.h000066400000000000000000000057051404105311000176260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: drummap.h,v 1.3.2.3 2009/10/29 02:14:37 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DRUMMAP_H__ #define __DRUMMAP_H__ #include namespace MusECore { class Xml; //--------------------------------------------------------- // DrumMap //--------------------------------------------------------- struct DrumMap { QString name; unsigned char vol; // playback volume, percent. int quant; int len; // len of event in ticks // Default to track port if -1 and track channel if -1. int channel; // midi channel int port; // midi port char lv1, lv2, lv3, lv4; // velocities char enote, anote; // input note - output note bool mute; bool hide; // Initializes the structure. NOTE: Be sure the enote value // does not conflict with another map item's enote value. void init() { vol = 100; quant = 16; len = 32; channel = -1; port = -1; lv1 = 70; lv2 = 90; lv3 = 110; lv4 = 127; enote = 0; anote = 0; mute = false; }; bool operator==(const DrumMap& map) const; bool operator!=(const DrumMap& map) const { return !operator==(map); } bool almost_equals(const DrumMap& map) const; void dump(); }; // please let this at "128". idrumMap should have length 128 (see drummap.cpp for details) #define DRUM_MAPSIZE 128 extern DrumMap iNewDrumMap[128]; extern void initNewDrumMap(); extern void clearDrumMap(); // One-time only early init extern void initDrumMap(); extern void writeDrumMap(int level, Xml& xml, bool external); extern void readDrumMap(Xml& xml, bool external); extern void resetGMDrumMap(); } // namespace MusECore namespace MusEGlobal { extern char drumOutmap[DRUM_MAPSIZE]; extern char drumInmap[DRUM_MAPSIZE]; extern MusECore::DrumMap drumMap[DRUM_MAPSIZE]; } #endif muse-4.0.0/src/muse/midiedit/ecanvas.cpp000066400000000000000000000565771404105311000201510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ecanvas.cpp,v 1.8.2.6 2009/05/03 04:14:00 terminator356 Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include #ifndef _WIN32 #include #endif #include #include #include #include "xml.h" #include "ecanvas.h" #include "song.h" #include "shortcuts.h" #include "audio.h" #include "functions.h" #include "midi_consts.h" #include "gconfig.h" #include "globals.h" #include "midiport.h" #include "config.h" // Forwards from header: #include #include #include #include #include "track.h" #include "part.h" #include "event.h" #include "undo.h" #include "midieditor.h" #include "citem.h" namespace MusEGui { //--------------------------------------------------------- // EventCanvas //--------------------------------------------------------- EventCanvas::EventCanvas(MidiEditor* pr, QWidget* parent, int sx, int sy, const char* name) : Canvas(parent, sx, sy, name) { editor = pr; _steprec = false; _midiin = false; _playEvents = true; _playEventsMode = PlayEventsSingleNote; _setCurPartIfOnlyOneEventIsSelected = true; curVelo = 70; setBg(MusEGlobal::config.midiCanvasBg); setAcceptDrops(true); setFocusPolicy(Qt::StrongFocus); setMouseTracking(true); curPart = (MusECore::MidiPart*)(editor->parts()->begin()->second); curPartId = curPart->sn(); } EventCanvas::~EventCanvas() { if(_playEvents) stopPlayEvents(); } //--------------------------------------------------------- // getCaption //--------------------------------------------------------- QString EventCanvas::getCaption() const { int bar1, bar2, xx; unsigned x; MusEGlobal::sigmap.tickValues(curPart->tick(), &bar1, &xx, &x); MusEGlobal::sigmap.tickValues(curPart->tick() + curPart->lenTick(), &bar2, &xx, &x); QString s; if (editor->parts()->size() > 1) s = curPart->name() + QString(" (%1-%2) [%3:%4]") .arg(bar1+1) .arg(bar2+1) .arg(editor->parts()->index(curPart) + 1) .arg(editor->parts()->size()); else s = curPart->name() + QString(" (%1-%2)").arg(bar1+1).arg(bar2+1); return s; } //--------------------------------------------------------- // leaveEvent //--------------------------------------------------------- void EventCanvas::leaveEvent(QEvent*) { emit pitchChanged(-1); emit timeChanged(INT_MAX); } //--------------------------------------------------------- // enterEvent //--------------------------------------------------------- void EventCanvas::enterEvent(QEvent*) { emit enterCanvas(); } //--------------------------------------------------------- // raster //--------------------------------------------------------- QPoint EventCanvas::raster(const QPoint& p) const { int x = p.x(); if (x < 0) x = 0; x = editor->rasterVal(x); int pitch = y2pitch(p.y()); int y = pitch2y(pitch); return QPoint(x, y); } //--------------------------------------------------------- // mouseMove //--------------------------------------------------------- void EventCanvas::mouseMove(QMouseEvent* event) { emit pitchChanged(y2pitch(event->pos().y())); int x = event->pos().x(); if(x < 0) x = 0; emit timeChanged(editor->rasterVal(x)); } //--------------------------------------------------------- // updateItems //--------------------------------------------------------- void EventCanvas::updateItems() { bool curItemNeedsRestore=false; MusECore::Event storedEvent; int partSn = 0xDEADBEEF; // to prevent compiler warning; partSn is unused anyway if curItemNeedsRestore==false. if (curItem) { curItemNeedsRestore=true; storedEvent=curItem->event(); partSn=curItem->part()->sn(); } curItem=NULL; items.clearDelete(); start_tick = INT_MAX; end_tick = 0; curPart = 0; for (MusECore::iPart p = editor->parts()->begin(); p != editor->parts()->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart*)(p->second); if (part->sn() == curPartId) curPart = part; unsigned stick = part->tick(); unsigned len = part->lenTick(); unsigned etick = stick + len; if (stick < start_tick) start_tick = stick; if (etick > end_tick) end_tick = etick; for (MusECore::ciEvent i = part->events().begin(); i != part->events().end(); ++i) { MusECore::Event e = i->second; // Do not add events which are past the end of the part. #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)e.tick() /*+ (int)e.lenTick()*/ < 0) continue; if((int)e.tick() >= (int)len) break; #else if(e.tick() > len) break; #endif if (e.isNote()) { CItem* temp = addItem(part, e); if(temp) temp->setSelected(e.selected()); if (temp && curItemNeedsRestore && e==storedEvent && part->sn()==partSn) { if (curItem!=NULL) fprintf(stderr, "THIS SHOULD NEVER HAPPEN: curItemNeedsRestore=true, event fits, but there was already a fitting event!?\n"); curItem=temp; } } } } } //--------------------------------------------------------- // itemSelectionsChanged //--------------------------------------------------------- bool EventCanvas::itemSelectionsChanged(MusECore::Undo* operations, bool deselectAll) { MusECore::Undo ops; MusECore::Undo* opsp = operations ? operations : &ops; bool item_selected; bool obj_selected; bool changed=false; // If we are deselecting all, globally deselect all events, // and don't bother individually deselecting objects, below. if(deselectAll) { opsp->push_back(MusECore::UndoOp(MusECore::UndoOp::GlobalSelectAllEvents, false, 0, 0)); changed = true; } for (iCItem i = items.begin(); i != items.end(); ++i) { CItem* item = i->second; item_selected = item->isSelected(); obj_selected = item->objectIsSelected(); // Don't bother deselecting objects if we have already deselected all, above. if((item_selected || !deselectAll) && ((item_selected != obj_selected) || // Need to force this because after the 'deselect all events' command executes, // if the item is selected another select needs to be executed even though it // appears nothing changed here. (item_selected && deselectAll))) { opsp->push_back(MusECore::UndoOp(MusECore::UndoOp::SelectEvent, item->event(), item->part(), item_selected, obj_selected)); changed=true; } } if (!operations && changed) { // Set the 'sender' to this so that we can ignore self-generated songChanged signals. // Here we have a choice of whether to allow undoing of selections. if(MusEGlobal::config.selectionsUndoable) MusEGlobal::song->applyOperationGroup(ops, MusECore::Song::OperationUndoMode, this); else MusEGlobal::song->applyOperationGroup(ops, MusECore::Song::OperationExecuteUpdate, this); // For testing... // fprintf(stderr, "EventCanvas::updateSelection: Applied SelectPart operations, redrawing\n"); } return changed; } bool EventCanvas::stuckNoteExists(int port, int channel, int pitch) const { const int sz = _stuckNotes.size(); for(int i = 0; i < sz; ++i) { const MusECore::MidiPlayEvent& s_ev(_stuckNotes.at(i)); if(s_ev.type() == MusECore::ME_NOTEON && port == s_ev.port() && channel == s_ev.channel() && pitch == s_ev.dataA()) return true; } return false; } bool EventCanvas::stopStuckNote(int port, int channel, int pitch) { int playedPitch = pitch; // Apply track transposition, but only for midi tracks, not drum tracks. if(track()->isMidiTrack() && !track()->isDrumTrack()) playedPitch += track()->transposition; const int sz = _stuckNotes.size(); for(int i = 0; i < sz; ++i) { MusECore::MidiPlayEvent s_ev(_stuckNotes.at(i)); if(s_ev.type() == MusECore::ME_NOTEON && port == s_ev.port() && channel == s_ev.channel() && playedPitch == s_ev.dataA()) { unsigned int frame = MusEGlobal::audio->curFrame(); s_ev.setType(MusECore::ME_NOTEOFF); s_ev.setTime(frame); if(s_ev.dataB() == 0) s_ev.setB(64); MusEGlobal::midiPorts[port].putEvent(s_ev); _stuckNotes.remove(i); return true; } } return false; } //--------------------------------------------------------- // songChanged(type) //--------------------------------------------------------- void EventCanvas::songChanged(MusECore::SongChangedStruct_t flags) { if (flags & ~(SC_SELECTION | SC_PART_SELECTION | SC_TRACK_SELECTION)) { // TODO FIXME: don't we actually only want SC_PART_*, and maybe SC_TRACK_DELETED? // (same in waveview.cpp) updateItems(); } MusECore::Event event; MusECore::MidiPart* part = 0; int x = 0; CItem* nevent = 0; int n = 0; // count selections for (iCItem k = items.begin(); k != items.end(); ++k) { MusECore::Event ev = k->second->event(); if (ev.selected()) { ++n; if (!nevent) { nevent = k->second; curVelo = ev.velo(); } } } start_tick = MusEGlobal::song->roundDownBar(start_tick); end_tick = MusEGlobal::song->roundUpBar(end_tick); if (n >= 1) { x = nevent->x(); event = nevent->event(); part = (MusECore::MidiPart*)nevent->part(); if (_setCurPartIfOnlyOneEventIsSelected && n == 1 && curPart != part) { curPart = part; curPartId = curPart->sn(); curPartChanged(); } } if(flags & (SC_SELECTION)) { // Prevent race condition: Ignore if the change was ultimately sent by the canvas itself. if(flags._sender != this) updateItemSelections(); } bool f1 = static_cast(flags & (SC_EVENT_INSERTED | SC_EVENT_MODIFIED | SC_EVENT_REMOVED | SC_PART_INSERTED | SC_PART_MODIFIED | SC_PART_REMOVED | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_SIG | SC_TEMPO | SC_KEY | SC_MASTER | SC_CONFIG | SC_DRUMMAP)); bool f2 = static_cast(flags & SC_SELECTION); // Try to avoid all unnecessary emissions. if(f1 || f2) emit selectionChanged(x, event, part, !f1); if (curPart == 0) curPart = (MusECore::MidiPart*)(editor->parts()->begin()->second); redraw(); } //--------------------------------------------------------- // selectAtTick //--------------------------------------------------------- void EventCanvas::selectAtTick(unsigned int tick) { //Select note nearest tick, if none selected and there are any if (!items.empty() && selectionSize() == 0) { iCItem i = items.begin(); CItem* nearest = i->second; while (i != items.end()) { CItem* cur=i->second; unsigned int curtk=abs(cur->x() + (int)cur->part()->tick() - (int)tick); unsigned int neartk=abs(nearest->x() + (int)nearest->part()->tick() - (int)tick); if (curtk < neartk) { nearest=cur; } i++; } if (!nearest->isSelected()) { selectItem(nearest, true); songChanged(SC_SELECTION); } } } //--------------------------------------------------------- // track //--------------------------------------------------------- MusECore::MidiTrack* EventCanvas::track() const { if(!curPart) return 0; return ((MusECore::MidiPart*)curPart)->track(); } //--------------------------------------------------------- // keyPress //--------------------------------------------------------- void EventCanvas::keyPress(QKeyEvent* event) { int key = event->key(); if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) key += Qt::SHIFT; if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) key += Qt::ALT; if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) key+= Qt::CTRL; // Select items by key (PianoRoll & DrumEditor) if (key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key) { rciCItem i; if (items.empty()) return; for (i = items.rbegin(); i != items.rend(); ++i) if (i->second->isSelected()) break; if(i == items.rend()) i = items.rbegin(); if(i != items.rbegin()) --i; if(i->second) { if (key != shortcuts[SHRT_SEL_RIGHT_ADD].key) deselectAll(); CItem* sel = i->second; sel->setSelected(true); redraw(); if (sel->x() + sel->width() > mapxDev(width())) { int mx = rmapx(sel->x()); int newx = mx + rmapx(sel->width()) - width(); // Leave a bit of room for the specially-drawn drum notes. But good for piano too. emit horizontalScroll( (newx > mx ? mx - 10: newx + 10) - rmapx(xorg) ); } } } //Select items by key: (PianoRoll & DrumEditor) else if (key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key) { ciCItem i; if (items.empty()) return; for (i = items.begin(); i != items.end(); ++i) if (i->second->isSelected()) break; if(i == items.end()) i = items.begin(); if(i != items.begin()) --i; if(i->second) { if (key != shortcuts[SHRT_SEL_LEFT_ADD].key) deselectAll(); CItem* sel = i->second; sel->setSelected(true); redraw(); if (sel->x() <= mapxDev(0)) emit horizontalScroll(rmapx(sel->x() - xorg) - 10); // Leave a bit of room. } } else if (key == shortcuts[SHRT_INC_PITCH].key) { modifySelected(NoteInfo::VAL_PITCH, 1); } else if (key == shortcuts[SHRT_DEC_PITCH].key) { modifySelected(NoteInfo::VAL_PITCH, -1); } else if (key == shortcuts[SHRT_INC_POS].key) { // TODO: Check boundaries modifySelected(NoteInfo::VAL_TIME, editor->raster()); } else if (key == shortcuts[SHRT_DEC_POS].key) { // TODO: Check boundaries modifySelected(NoteInfo::VAL_TIME, 0 - editor->raster()); } else if (key == shortcuts[SHRT_INCREASE_LEN].key) { // TODO: Check boundaries modifySelected(NoteInfo::VAL_LEN, editor->raster()); } else if (key == shortcuts[SHRT_DECREASE_LEN].key) { // TODO: Check boundaries modifySelected(NoteInfo::VAL_LEN, 0 - editor->raster()); } else event->ignore(); } //--------------------------------------------------------- // keyRelease //--------------------------------------------------------- void EventCanvas::keyRelease(QKeyEvent* event) { const int key = event->key(); // We do not want auto-repeat events. // It does press and release repeatedly. Wait till the last release comes. if(!event->isAutoRepeat()) { // For testing... // fprintf(stderr, "EventCanvas::keyRelease not isAutoRepeat\n"); //event->accept(); // Select part to the right if(key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key || // Select part to the left key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key) { itemSelectionsChanged(); } return; } Canvas::keyRelease(event); } //--------------------------------------------------------- // dropEvent //--------------------------------------------------------- void EventCanvas::viewDropEvent(QDropEvent* event) { QString text; if (event->source() == this) { fprintf(stderr, "local DROP\n"); //event->acceptProposedAction(); //event->ignore(); // TODO CHECK Tim. return; } if (event->mimeData()->hasFormat("text/x-muse-groupedeventlists")) { text = QString(event->mimeData()->data("text/x-muse-groupedeventlists")); int x = event->pos().x(); if(x < 0) x = 0; x = editor->rasterVal(x); if (x < 0) x = 0; paste_at(text,x,3072,false,false,curPart); //event->accept(); // TODO } else { fprintf(stderr, "cannot decode drop\n"); //event->acceptProposedAction(); //event->ignore(); // TODO CHECK Tim. } } //--------------------------------------------------------- // endMoveItems // dir = 0 move in all directions // 1 move only horizontal // 2 move only vertical //--------------------------------------------------------- void EventCanvas::endMoveItems(const QPoint& pos, DragType dragtype, int dir, bool rasterize) { int dp = y2pitch(pos.y()) - y2pitch(Canvas::start.y()); int dx = pos.x() - Canvas::start.x(); if (dir == 1) dp = 0; else if (dir == 2) dx = 0; MusECore::Undo operations = moveCanvasItems(moving, dp, dx, dragtype, rasterize); if (operations.empty()) songChanged(SC_EVENT_MODIFIED); //this is a hack to force the canvas to repopulate //itself. otherwise, if a moving operation was forbidden, //the canvas would still show the movement else MusEGlobal::song->applyOperationGroup(operations); moving.clear(); itemSelectionsChanged(); redraw(); } //--------------------------------------------------------- // startPlayEvent //--------------------------------------------------------- void EventCanvas::startPlayEvent(int note, int velocity, int port, int channel) { // REMOVE Tim. Noteoff. Added. Zero note on vel is not allowed now. if(velocity == 0) { fprintf(stderr, "EventCanvas::startPlayEvent: Warning: Zero note on velocity!\n"); velocity = 1; } if (MusEGlobal::debugMsg) fprintf(stderr, "EventCanvas::startPlayEvent %d %d %d %d\n", note, velocity, port, channel); if(!track()) { stopPlayEvents(); return; } int playedPitch = note; // Apply track transposition, but only for midi tracks, not drum tracks. if(track()->isMidiTrack() && !track()->isDrumTrack()) playedPitch += track()->transposition; // Release any current note. stopStuckNote(port, channel, note); // play note: const MusECore::MidiPlayEvent e(MusEGlobal::audio->curFrame(), port, channel, MusECore::ME_NOTEON, playedPitch, velocity); _stuckNotes.push_back(e); // Send to the port and device. MusEGlobal::midiPorts[port].putEvent(e); } void EventCanvas::startPlayEvent(int note, int velocity) { if(!track()) return; int port = track()->outPort(); int channel = track()->outChannel(); startPlayEvent(note, velocity, port, channel); } //--------------------------------------------------------- // stopPlayEvent //--------------------------------------------------------- void EventCanvas::stopPlayEvents() { if (!MusEGlobal::audioDevice) return; // Stop all currently playing notes. unsigned int frame = MusEGlobal::audio->curFrame(); int port; const int sz = _stuckNotes.size(); for(int i = 0; i < sz; ++i) { MusECore::MidiPlayEvent ev(_stuckNotes.at(i)); port = ev.port(); if(port < 0 || port >= MusECore::MIDI_PORTS) continue; ev.setType(MusECore::ME_NOTEOFF); ev.setTime(frame); if(ev.dataB() == 0) ev.setB(64); // Send to the port and device. MusEGlobal::midiPorts[port].putEvent(ev); } // Clear the stuck notes list. _stuckNotes.clear(); } void EventCanvas::setRangeToSelection() { int tick_max = 0; int tick_min = INT_MAX; bool found = false; for (const auto& i : items) { if (!i.second->isSelected()) continue; int tick = i.second->x(); int len = i.second->event().lenTick(); found = true; if (tick + len > tick_max) tick_max = tick + len; if (tick < tick_min) tick_min = tick; } if (found) { MusECore::Pos p1(tick_min, true); MusECore::Pos p2(tick_max, true); if (p1 < MusEGlobal::song->lPos()) { MusEGlobal::song->setPos(MusECore::Song::LPOS, p1); MusEGlobal::song->setPos(MusECore::Song::RPOS, p2); } else { MusEGlobal::song->setPos(MusECore::Song::RPOS, p2); MusEGlobal::song->setPos(MusECore::Song::LPOS, p1); } } } } // namespace MusEGui muse-4.0.0/src/muse/midiedit/ecanvas.h000066400000000000000000000116071404105311000175770ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ecanvas.h,v 1.5.2.4 2009/02/02 21:38:00 terminator356 Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ECANVAS_H__ #define __ECANVAS_H__ #include "type_defs.h" #include "canvas.h" #include "noteinfo.h" #include "mpevent.h" #include #include #include #define KH 13 // Forward declarations: class QDropEvent; class QEvent; class QKeyEvent; class QToolTip; namespace MusECore { class MidiPart; class MidiTrack; class Part; class Event; class Undo; struct PartToChange { Part* npart; int xdiff; }; typedef std::map PartsToChangeMap; typedef std::map::iterator iPartToChange; } namespace MusEGui { class CItem; class MidiEditor; //--------------------------------------------------------- // EventCanvas //--------------------------------------------------------- class EventCanvas : public Canvas { Q_OBJECT public: enum PlayEventsMode { PlayEventsSingleNote = 0, PlayEventsChords }; private: virtual void leaveEvent(QEvent*e); virtual void enterEvent(QEvent*e); protected: bool _playEvents; PlayEventsMode _playEventsMode; MidiEditor* editor; unsigned start_tick, end_tick; int curVelo; bool _steprec; bool _midiin; bool _setCurPartIfOnlyOneEventIsSelected; // Notes that are currently being played in the piano or drum list etc. QVector _stuckNotes; bool stuckNoteExists(int port, int channel, int pitch) const; // Returns true if a note was actually stopped and removed from the stuck notes list. bool stopStuckNote(int port, int channel, int pitch); virtual void setRangeToSelection(); bool itemSelectionsChanged(MusECore::Undo* operations = 0, bool deselectAll = false); virtual CItem* addItem(MusECore::Part*, const MusECore::Event&) = 0; virtual QPoint raster(const QPoint&) const; virtual MusECore::Undo moveCanvasItems(CItemMap&, int, int, DragType, bool rasterize = true) = 0; virtual bool moveItem(MusECore::Undo&, CItem*, const QPoint&, DragType, bool rasterize = true) = 0; virtual void endMoveItems(const QPoint&, DragType, int dir, bool rasterize = true); virtual void startPlayEvent(int note, int velocity); virtual void startPlayEvent(int note, int velocity, int port, int channel); virtual void stopPlayEvents(); virtual void mouseMove(QMouseEvent* event); public slots: void redrawGrid() { redraw(); } void setSteprec(bool f) { _steprec = f; } void setMidiin(bool f) { _midiin = f; } signals: void pitchChanged(int); // current cursor position void timeChanged(unsigned); void selectionChanged(int /*tick or frame*/ , MusECore::Event&, MusECore::Part*, bool /*update*/); void enterCanvas(); public: EventCanvas(MidiEditor*, QWidget*, int, int, const char* name = 0); virtual ~EventCanvas(); MusECore::MidiTrack* track() const; virtual unsigned start() const { return start_tick; } virtual unsigned end() const { return end_tick; } bool midiin() const { return _midiin; } bool steprec() const { return _steprec; } virtual QString getCaption() const; virtual void songChanged(MusECore::SongChangedStruct_t); virtual void range(int* s, int* e) const { *s = start_tick; *e = end_tick; } bool playEvents() const { return _playEvents; } PlayEventsMode playEventsMode() const { return _playEventsMode; } void setPlayEvents(bool flag) { _playEvents = flag; } void setPlayEventsMode(PlayEventsMode mode) { _playEventsMode = mode; } virtual void selectAtTick(unsigned int tick); virtual void viewDropEvent(QDropEvent* event); virtual void modifySelected(NoteInfo::ValType, int /*val*/, bool /*delta_mode*/ = true) {} virtual void keyPress(QKeyEvent*); virtual void keyRelease(QKeyEvent* event); virtual void updateItems(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/midiedit/piano.cpp000066400000000000000000000500041404105311000176120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: piano.cpp,v 1.3 2004/05/31 11:48:55 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "piano.h" #include "globals.h" #include "song.h" #include "midiport.h" #include "track.h" #include "midictrl.h" #include "icons.h" #include "utils.h" #include "gconfig.h" // Forwards from header: #include #include #include #include #include #include "midieditor.h" namespace MusEGui { /* static const char *oct_xpm[] = { // w h colors "40 91 2 1", ". c #c0c0c0", "# c #000000", // x "########################################", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", // 10 ".......................................#", ".......................................#", ".......................................#", ".......................................#", //------------------------ "########################...............#", "########################...............#", "########################...............#", "########################################", // 7 "########################...............#", "########################...............#", "########################...............#", //------------------------ ".......................................#", ".......................................#", ".......................................#", // 6 ".......................................#", ".......................................#", ".......................................#", //------------------------ "########################...............#", "########################...............#", "########################...............#", // 7 "########################################", "########################...............#", "########################...............#", "########################...............#", //------------------------ ".......................................#", ".......................................#", ".......................................#", // 6 ".......................................#", ".......................................#", ".......................................#", //------------------------ "########################...............#", "########################...............#", "########################...............#", // 7 "########################################", "########################...............#", "########################...............#", "########################...............#", //------------------------ ".......................................#", ".......................................#", ".......................................#", ".......................................#", // 10 ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", "########################################", //---------------------- ".......................................#", ".......................................#", ".......................................#", ".......................................#", // 9 ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", //------------------------ "########################...............#", "########################...............#", "########################...............#", "########################################", // 7 "########################...............#", "########################...............#", "########################...............#", //------------------------ ".......................................#", ".......................................#", ".......................................#", // 6 ".......................................#", ".......................................#", ".......................................#", //-------------------------- "########################...............#", "########################...............#", "########################...............#", // 7 "########################################", "########################...............#", "########################...............#", "########################...............#", //------------------------ ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", ".......................................#", // 9 ".......................................#", ".......................................#", ".......................................#", }; */ /* 0 1 2 3 4 5 6 7 8 9 10 c-2 c-1 C0 C1 C2 C3 C4 C5 C6 C7 C8 - G8 Grid über Oktave: +------------+ ------------------------------ 11 | | | h | 7 +------+ | 10 | a# +-----+ .............................. +------+ a | 9 | | 6 +------+ | 8 | g# +-----+ .............................. +------+ g | 7 | | 5 +------+ | 6 | f# +-----+ .............................. +------+ f | 5 | | 4 | | +------------+ ------------------------------ 4 | | | e | 3 +------+ | 3 | d# +-----+ .............................. +------+ d | 2 | | 2 +------+ | 1 | c# +-----+ .............................. +------+ c | | | 1 0 | | +------------+ ------------------------------ */ //--------------------------------------------------------- // Piano //--------------------------------------------------------- Piano::Piano(QWidget* parent, int ymag, int width, MidiEditor* editor) : View(parent, 1, ymag), pianoWidth(width) { setMouseTracking(true); _midiEditor = editor; curPitch = -1; selectedPitch = 60; // Start with 'C3" keyDown = -1; button = Qt::NoButton; setStatusTip(tr("Piano: Press key to play or enter events in step record mode (SHIFT for chords). RMB: Set cursor for polyphonic control events. CTRL+Mousewheel to zoom view vertically.")); } //--------------------------------------------------------- // draw //--------------------------------------------------------- void Piano::draw(QPainter& p, const QRect&, const QRegion&) { // QRect ur = mapDev(mr); // if (ur.height() > pianoHeight) // ur.setHeight(pianoHeight); // // FIXME: For some reason need the expansion otherwise drawing // // artifacts (incomplete drawing). Can't figure out why. // ur.adjust(0, -4, 0, 4); const int selPitchY = pitch2y(selectedPitch); const int curPitchY = pitch2y(curPitch); const QColor colKeyCur = MusEGlobal::config.pianoCurrentKey; const QColor colKeyCurP = MusEGlobal::config.pianoPressedKey; const QColor colKeySel = MusEGlobal::config.pianoSelectedKey; const qreal rad = 1.0; QPen pen(QColor(80,80,80)); pen.setCosmetic(true); pen.setWidth(2); p.setPen(pen); p.setRenderHint(QPainter::Antialiasing); // draw white keys { const QColor colKeyW("Ivory"); p.setBrush(colKeyW); int y = 0; for (int i = 0; i < 75; i++) { y = i * KH; if (y + 1 == selPitchY) p.setBrush(colKeySel); p.fillRect(0, y, pianoWidth, KH, p.brush()); p.drawLine(0, y, pianoWidth, y); p.drawLine(pianoWidth, y, pianoWidth, y + KH); if (y + 1 == selPitchY) p.setBrush(colKeyW); if (y + 1 == curPitchY) { p.save(); if (curPitch == keyDown) p.setBrush(colKeyCurP); else p.setBrush(colKeyCur); p.setPen(Qt::NoPen); p.drawRoundedRect(pianoWidth * 0.65, y + 2, pianoWidth * 0.3, 9, rad, rad); p.restore(); } } y = 75 * KH; p.drawLine(0, y, pianoWidth, y); } // draw black keys { const int keyHeightB = 7; const int distSmallB = 6; const int distLargeB = 19; const int topOffsetB = 10; QLinearGradient g(0.0, 0.0, 1.0, 0.0); g.setCoordinateMode(QGradient::ObjectBoundingMode); g.setColorAt(0.0, QColor(120, 120, 120)); g.setColorAt(0.79, QColor(70, 70, 70)); g.setColorAt(0.8, QColor(40, 40, 40)); g.setColorAt(0.83, QColor(20, 20, 20)); g.setColorAt(1.0, QColor(20, 20, 20)); p.setBrush(g); int cnt = 2; bool flag = true; int y = topOffsetB; int wb = pianoWidth * .6; for (int i = 0; i < 53; i++) { if ((y - 3) == selPitchY) { p.setBrush(colKeySel); p.drawRoundedRect(0, y, wb, keyHeightB, rad, rad); p.setBrush(g); } else p.drawRoundedRect(0, y, wb, keyHeightB, rad, rad); if ((y - 3) == curPitchY) { p.save(); if (curPitch == keyDown) p.setBrush(colKeyCurP); else p.setBrush(colKeyCur); p.setPen(Qt::NoPen); p.drawRoundedRect(pianoWidth * 0.2, y + 1, pianoWidth * 0.3, 5, rad, rad); p.restore(); } if ((flag && ++cnt == 3) || (!flag && ++cnt == 2)) { y = y + distLargeB + keyHeightB; flag = !flag; cnt = 0; } else y = y + distSmallB + keyHeightB; } } // draw shadow { const int pianoHeight = (7 * KH * 10) + (KH * 5) + 1; QLinearGradient g(0.0, 0.0, 1.0, 0.0); g.setCoordinateMode(QGradient::ObjectBoundingMode); g.setColorAt(0.0, Qt::black); g.setColorAt(1.0, QColor(127, 127, 127, 0)); p.setBrush(g); p.fillRect(0, 0, pianoWidth * .1, pianoHeight, g); } // draw C notes { const int octaveHeight = 7 * KH; QFont f(MusEGlobal::config.fonts[0].family(), 7); QFontMetrics fm(f); p.setFont(f); p.setPen(Qt::black); int y = 5 * KH; for (int i = 0; i < 11; i++) { QString s("C" + QString::number(8 - i)); p.drawText(pianoWidth - fm.size(0, s).width() - (pianoWidth / 10 - 3), y - 3, s); y += octaveHeight; } } if(!_midiEditor) return; MusECore::PartList* part_list = _midiEditor->parts(); MusECore::Part* cur_part = _midiEditor->curCanvasPart(); if(!part_list || !cur_part || !cur_part->track()->isMidiTrack()) return; MusECore::MidiTrack* track = (MusECore::MidiTrack*)(cur_part->track()); int channel = track->outChannel(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; MusECore::MidiCtrlValListList* cll = port->controller(); const int min = channel << 24; const int max = min + 0x1000000; for(MusECore::ciMidiCtrlValList it = cll->lower_bound(min); it != cll->lower_bound(max); ++it) { MusECore::MidiCtrlValList* cl = it->second; MusECore::MidiController* c = port->midiController(cl->num(), channel); if(!c->isPerNoteController()) continue; int cnum = c->num(); int num = cl->num(); int pitch = num & 0x7f; bool used = false; for (MusECore::ciEvent ie = cur_part->events().begin(); ie != cur_part->events().end(); ++ie) { MusECore::Event e = ie->second; if(e.type() != MusECore::Controller) continue; int ctl_num = e.dataA(); if((ctl_num | 0xff) == cnum && (ctl_num & 0x7f) == pitch) { used = true; break; } } bool off = cl->hwVal() == MusECore::CTRL_VAL_UNKNOWN; // Does it have a value or is it 'off'? if (used) { if (off) p.setBrush(QColor("MediumSeaGreen")); else p.setBrush(QColor("OrangeRed")); } else { if (off) p.setBrush(QColor(179, 179,179)); // Gray else p.setBrush(QColor("DodgerBlue")); } qreal y = pitch2y(pitch) + 4.; qreal w = pianoWidth; QPainterPath path; path.moveTo(w * 0.1, y); path.lineTo(w * 0.1, y + 5.0); path.lineTo(w * 0.2, y + 3.0); path.lineTo(w * 0.1, y); p.fillPath(path, p.brush()); } } //--------------------------------------------------------- // pitch2y //--------------------------------------------------------- int Piano::pitch2y(int pitch) const { int tt[] = { 12, 19, 25, 32, 38, 51, 58, 64, 71, 77, 84, 90 }; int y = (75 * KH) - (tt[pitch%12] + (7 * KH) * (pitch/12)); if (y < 0) y = 0; return y; } //--------------------------------------------------------- // y2pitch //--------------------------------------------------------- int Piano::y2pitch(int y) const { if (y < KH) return 127; const int total = (10 * 7 + 5) * KH; // 75 full tone steps y = total - y; if (y < 0) return 0; int oct = (y / (7 * KH)) * 12; char kt[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 }; return kt[y % 91] + oct; } //--------------------------------------------------------- // leaveEvent //--------------------------------------------------------- void Piano::leaveEvent(QEvent*) { if (keyDown != -1) { emit keyReleased(keyDown, shift); keyDown = -1; } emit pitchChanged(-1); setPitch(-1); } //--------------------------------------------------------- // setPitch //--------------------------------------------------------- void Piano::setPitch(int pitch) { if (curPitch == pitch) return; curPitch = pitch; redraw(); } //--------------------------------------------------------- // viewMouseMoveEvent //--------------------------------------------------------- void Piano::viewMouseMoveEvent(QMouseEvent* event) { int pitch = y2pitch(event->y()); emit pitchChanged(pitch); setPitch(pitch); if (button == Qt::LeftButton) { int nk = y2pitch(event->y()); if (nk < 0 || nk > 127) nk = -1; if (nk != keyDown) { if (keyDown != -1) { emit keyReleased(keyDown, shift); } keyDown = nk; if (keyDown != -1) { int velocity = (event->x() + 1) * 127 / pianoWidth; if(velocity > 127) velocity = 127; else if(velocity <= 0) velocity = 1; emit keyPressed(keyDown, velocity, shift); } redraw(); } } if (!MusEGlobal::config.showNoteTooltips) return; int v = qMax(1, qMin(127, (event->x() + 1) * 127 / pianoWidth)); QString str = tr("Velocity: ") + QString::number(v); // QString str = tr("Left click: Play") + " (Vel: " + QString::number(v) + ")\n" // + tr("Right click: Select key"); QToolTip::showText(event->globalPos(), str); } //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- void Piano::viewMousePressEvent(QMouseEvent* event) { button = event->button(); shift = event->modifiers() & Qt::ShiftModifier; if (button == Qt::LeftButton) { if (keyDown != -1) { emit keyReleased(keyDown, shift); keyDown = -1; } keyDown = y2pitch(event->y()); if (keyDown < 0 || keyDown > 127) { keyDown = -1; } else { int velocity = (event->x() + 1) * 127 / pianoWidth; if(velocity > 127) velocity = 127; else if(velocity <= 0) velocity = 1; emit keyPressed(keyDown, velocity, shift); } } if (button == Qt::RightButton) { selectedPitch = y2pitch(event->y()); emit curSelectedPitchChanged(selectedPitch); redraw(); MusEGlobal::song->update(SC_DRUMMAP); } redraw(); } //--------------------------------------------------------- // viewMouseReleaseEvent //--------------------------------------------------------- void Piano::viewMouseReleaseEvent(QMouseEvent* event) { if (button == Qt::LeftButton) { shift = event->modifiers() & Qt::ShiftModifier; if (keyDown != -1) { emit keyReleased(keyDown, shift); keyDown = -1; } redraw(); } button = Qt::NoButton; } //--------------------------------------------------------- // setCurSelectedPitch //--------------------------------------------------------- void Piano::setCurSelectedPitch(int pitch) { if (pitch < 0 || pitch >= 128) return; if (pitch != selectedPitch) { selectedPitch = pitch; emit curSelectedPitchChanged(selectedPitch); redraw(); } } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void Piano::wheelEvent(QWheelEvent* ev) { if (ev->modifiers() & Qt::ControlModifier) { const QPoint pixelDelta = ev->pixelDelta(); const QPoint angleDegrees = ev->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; emit wheelStep(delta > 0 ? true : false); return; } emit redirectWheelEvent(ev); } } // namespace MusEGui muse-4.0.0/src/muse/midiedit/piano.h000066400000000000000000000050621404105311000172630ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: piano.h,v 1.2 2004/05/31 11:48:55 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PIANO_H__ #define __PIANO_H__ #include #include #include "view.h" // Forward declarations: class QEvent; class QMouseEvent; class QWheelEvent; class QPainter; class QPixmap; #define KH 13 namespace MusEGui { class MidiEditor; //--------------------------------------------------------- // Piano //--------------------------------------------------------- class Piano : public View { Q_OBJECT MidiEditor* _midiEditor; int curPitch; int selectedPitch; int keyDown; bool shift; int button; int pianoWidth; int y2pitch(int) const; int pitch2y(int) const; void viewMouseMoveEvent(QMouseEvent* event); virtual void leaveEvent(QEvent*e); virtual void viewMousePressEvent(QMouseEvent* event); virtual void viewMouseReleaseEvent(QMouseEvent*); virtual void wheelEvent(QWheelEvent* e); protected: virtual void draw(QPainter&, const QRect&, const QRegion& = QRegion()); signals: void pitchChanged(int); void keyPressed(int, int, bool); void keyReleased(int, bool); void curSelectedPitchChanged(int); void redirectWheelEvent(QWheelEvent*); void wheelStep(bool); public slots: void setPitch(int); public: Piano(QWidget* parent, int ymag, int width, MidiEditor* editor = 0); int curSelectedPitch() const { return selectedPitch; } void setCurSelectedPitch(int pitch); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/midiedit/pianoroll.cpp000066400000000000000000002274321404105311000205160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pianoroll.cpp,v 1.25.2.15 2009/11/16 11:29:33 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2012-2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include "pianoroll.h" #include "mtscale.h" #include "prcanvas.h" #include "scrollbar.h" #include "utils.h" #include "globals.h" #include "app.h" #include "song.h" #include "midiport.h" #include "gconfig.h" #include "icons.h" #include "audio.h" #include "functions.h" #include "helper.h" #include "menutitleitem.h" #include "operations.h" #include "cmd.h" #include "shortcuts.h" #include "trackinfo_layout.h" #include "midi_editor_layout.h" // Forwards from header: #include #include #include #include #include #include #include #include #include "cobject.h" #include "event.h" #include "xml.h" #include "ctrl/ctrledit.h" #include "pitchlabel.h" #include "scrollscale.h" #include "splitter.h" #include "tb1.h" #include "piano.h" #include "popupmenu.h" #include "tools.h" namespace MusEGui { int PianoRoll::_rasterInit = 96; int PianoRoll::_trackInfoWidthInit = 50; int PianoRoll::_canvasWidthInit = 300; MidiEventColorMode PianoRoll::colorModeInit = MidiEventColorMode::blueEvents; // Initial zoom levels: static const int xscale = -10; static const int yscale = 2; static int pianorollTools = MusEGui::PointerTool | MusEGui::PencilTool | MusEGui::RubberTool | MusEGui::DrawTool | PanTool | ZoomTool; //--------------------------------------------------------- // PianoRoll //--------------------------------------------------------- PianoRoll::PianoRoll(MusECore::PartList* pl, QWidget* parent, const char* name, unsigned initPos, bool showDefaultControls) : MidiEditor(TopWin::PIANO_ROLL, _rasterInit, pl, parent, name) { deltaMode = false; tickValue = 0; lenValue = 0; pitchValue = 0; veloOnValue = 1; veloOffValue = 0; firstValueSet = false; tickOffset = 0; lenOffset = 0; pitchOffset = 0; veloOnOffset = 0; veloOffOffset = 0; lastSelections = 0; _playEvents = true; _playEventsMode = EventCanvas::PlayEventsSingleNote; colorMode = colorModeInit; _canvasXOrigin = DefaultCanvasXOrigin; _minXMag = -25; _maxXMag = 2; // Request to set the raster, but be sure to use the one it chooses, // which may be different than the one requested. _rasterInit = _rasterizerModel->checkRaster(_rasterInit); _raster = _rasterInit; _pianoWidth = 40; ensurePolished(); if (_pianoWidth < 40) _pianoWidth = 40; const MusECore::PartList* part_list = parts(); // Default initial pianoroll view state. _viewState = MusECore::MidiPartViewState (0, KH * 30, xscale, yscale); // Include a velocity controller in the default initial view state. _viewState.addController(MusECore::MidiCtrlViewState(MusECore::CTRL_VELOCITY)); if(part_list && !part_list->empty()) { // If the parts' view states have never been initialized before, // do it now with the desired pianoroll initial state. for(MusECore::ciPart i = part_list->begin(); i != part_list->end(); ++i) { if(!i->second->viewState().isValid()) i->second->setViewState(_viewState); } // Now take our initial view state from the first part found in the list. // Don't bother if not showing default controls, since something else // will likely take care of it, like the song file loading routines. if(showDefaultControls) _viewState = part_list->begin()->second->viewState(); } //---------Menu---------------------------------- menuEdit = menuBar()->addMenu(tr("&Edit")); menuEdit->addActions(MusEGlobal::undoRedo->actions()); menuEdit->addSeparator(); editCutAction = menuEdit->addAction(*cutSVGIcon, tr("C&ut")); connect(editCutAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_CUT); } ); editCopyAction = menuEdit->addAction(*copySVGIcon, tr("&Copy")); connect(editCopyAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_COPY); } ); editCopyRangeAction = menuEdit->addAction(*copyRangeSVGIcon, tr("Copy Events in Range")); connect(editCopyRangeAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_COPY_RANGE); } ); editPasteAction = menuEdit->addAction(*pasteSVGIcon, tr("&Paste")); connect(editPasteAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_PASTE); } ); editPasteToCurPartAction = menuEdit->addAction(*pasteSelectedTrackSVGIcon, tr("Paste to Current Part")); connect(editPasteToCurPartAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_PASTE_TO_CUR_PART); } ); editPasteDialogAction = menuEdit->addAction(*pasteDialogSVGIcon, tr("Paste (With Dialog)...")); connect(editPasteDialogAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_PASTE_DIALOG); } ); menuEdit->addSeparator(); editDelEventsAction = menuEdit->addAction(*deleteSVGIcon, tr("Delete &Events")); connect(editDelEventsAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_DEL); } ); menuEdit->addSeparator(); menuSelect = menuEdit->addMenu(tr("&Select")); selectAllAction = menuSelect->addAction(*selectAllSVGIcon, tr("Select &All")); connect(selectAllAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_SELECT_ALL); } ); selectNoneAction = menuSelect->addAction(*deselectAllSVGIcon, tr("&Deselect All")); connect(selectNoneAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_SELECT_NONE); } ); selectInvertAction = menuSelect->addAction(*selectInvertSVGIcon, tr("Invert &Selection")); connect(selectInvertAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_SELECT_INVERT); } ); menuSelect->addSeparator(); selectInsideLoopAction = menuSelect->addAction(*selectInsideLoopSVGIcon, tr("&Inside Loop")); connect(selectInsideLoopAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_SELECT_ILOOP); } ); selectOutsideLoopAction = menuSelect->addAction(*selectOutsideLoopSVGIcon, tr("&Outside Loop")); connect(selectOutsideLoopAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_SELECT_OLOOP); } ); menuSelect->addSeparator(); selectRangeToSelectionAction = menuSelect->addAction(*rangeToSelectionSVGIcon, tr("Set &Range to Selection")); connect(selectRangeToSelectionAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_RANGE_TO_SELECTION); } ); if (parts()->size() > 1) { menuEdit->addSeparator(); selectNextPartAction = menuEdit->addAction(*nextPartSVGIcon, tr("&Next Part")); selectPrevPartAction = menuEdit->addAction(*lastPartSVGIcon, tr("&Previous Part")); connect(selectPrevPartAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_SELECT_PREV_PART); } ); connect(selectNextPartAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_SELECT_NEXT_PART); } ); } else { selectNextPartAction = nullptr; selectPrevPartAction = nullptr; } menuEdit->addSeparator(); startListEditAction = menuEdit->addAction(*listeditSVGIcon, tr("Event List...")); connect(startListEditAction, SIGNAL(triggered()), MusEGlobal::muse, SLOT(startListEditor())); menuFunctions = menuBar()->addMenu(tr("Fu&nctions")); // menuFunctions->setTearOffEnabled(true); funcQuantizeAction = menuFunctions->addAction(*quantizeSVGIcon, tr("Quantize")); connect(funcQuantizeAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_QUANTIZE); } ); funcModVelAction = menuFunctions->addAction(tr("Change Event Velocity")); connect(funcModVelAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_MODIFY_VELOCITY); } ); funcCrescAction = menuFunctions->addAction(tr("Crescendo/Decrescendo")); connect(funcCrescAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_CRESCENDO); } ); funcNoteShiftAction = menuFunctions->addAction(tr("Move Events")); connect(funcNoteShiftAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_NOTE_SHIFT); } ); funcDelOverlapsAction = menuFunctions->addAction(tr("Delete Overlaps")); connect(funcDelOverlapsAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_DELETE_OVERLAPS); } ); funcEraseEventAction = menuFunctions->addAction(tr("Erase Events")); connect(funcEraseEventAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_ERASE_EVENT); } ); menuFunctions->addSeparator(); funcGateTimeAction = menuFunctions->addAction(tr("Change Event Length")); connect(funcGateTimeAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_MODIFY_GATE_TIME); } ); funcSetFixedLenAction = menuFunctions->addAction(tr("Set Fixed Length")); connect(funcSetFixedLenAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_FIXED_LEN); } ); funcTransposeAction = menuFunctions->addAction(tr("Transpose")); connect(funcTransposeAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_TRANSPOSE); } ); QAction* funcLegatoAction = menuFunctions->addAction(tr("Legato")); connect(funcLegatoAction, &QAction::triggered, [this]() { cmd(PianoCanvas::CMD_LEGATO); } ); menuEdit->addSeparator(); editUseLastEditEventAction = menuEdit->addAction(tr("Use Last Event Length")); editUseLastEditEventAction->setCheckable(true); editUseLastEditEventAction->setChecked(MusEGlobal::config.useLastEditedEvent); connect(editUseLastEditEventAction, &QAction::triggered, [this]() { MusEGlobal::config.useLastEditedEvent = editUseLastEditEventAction->isChecked(); } ); //---------------------- // Scripts: //---------------------- menuScripts = menuBar()->addMenu(tr("&Scripts")); menuScripts->menuAction()->setStatusTip(tr("Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki.")); scripts.populateScriptMenu(menuScripts); connect(&scripts, &MusECore::Scripts::execDeliveredScriptReceived, [this](int id) { execDeliveredScript(id); } ); connect(&scripts, &MusECore::Scripts::execUserScriptReceived, [this](int id) { execUserScript(id); } ); menuConfig = menuBar()->addMenu(tr("&Display")); menuConfig->menuAction()->setStatusTip(tr("Display menu: Display options specific to current editor.")); menuConfig->addAction(subwinAction); // menuConfig->addAction(shareAction); menuConfig->addAction(fullscreenAction); menuConfig->addSeparator(); eventColor = menuConfig->addMenu(tr("&Event Color")); eventColor->setObjectName("CheckmarkOnly"); QActionGroup* actgrp = new QActionGroup(this); actgrp->setExclusive(true); evColorBlueAction = actgrp->addAction(tr("&Blue")); evColorBlueAction->setCheckable(true); connect(evColorBlueAction, &QAction::triggered, [this]() { eventColorModeChanged(MidiEventColorMode::blueEvents); } ); evColorPitchAction = actgrp->addAction(tr("&Pitch Colors")); evColorPitchAction->setCheckable(true); connect(evColorPitchAction, &QAction::triggered, [this]() { eventColorModeChanged(MidiEventColorMode::pitchColorEvents); } ); evColorVelAction = actgrp->addAction(tr("&Velocity Colors")); evColorVelAction->setCheckable(true); connect(evColorVelAction, &QAction::triggered, [this]() { eventColorModeChanged(MidiEventColorMode::velocityColorEvents); } ); eventColor->addActions(actgrp->actions()); // menuConfig->addSeparator(); addControllerMenu = new PopupMenu(tr("Add Controller View"), this, true); addControllerMenu->setIcon(*midiControllerNewSVGIcon); menuConfig->addMenu(addControllerMenu); connect(addControllerMenu, &QMenu::aboutToShow, [this]() { ctrlMenuAboutToShow(); } ); connect(addControllerMenu, &QMenu::aboutToHide, [this]() { ctrlMenuAboutToHide(); } ); connect(addControllerMenu, &QMenu::triggered, [this](QAction* act) { ctrlPopupTriggered(act); } ); //---------ToolBar---------------------------------- // NOTICE: Please ensure that any tool bar object names here match the names assigned // to identical or similar toolbars in class MusE or other TopWin classes. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). addToolBarBreak(); // Already has an object name. tools2 = new MusEGui::EditToolBar(this, pianorollTools); addToolBar(tools2); tools = addToolBar(tr("Pianoroll tools")); tools->setObjectName("Pianoroll tools"); addctrl = new QToolButton(); addctrl->setToolTip(tr("Add controller view")); addctrl->setIcon(*midiControllerNewSVGIcon); addctrl->setFocusPolicy(Qt::NoFocus); connect(addctrl, &QToolButton::pressed, [this]() { addCtrlClicked(); } ); tools->addWidget(addctrl); srec = new QToolButton(); srec->setToolTip(tr("Step record")); srec->setIcon(*steprecSVGIcon); srec->setCheckable(true); srec->setFocusPolicy(Qt::NoFocus); tools->addWidget(srec); midiin = new QToolButton(); midiin->setToolTip(tr("Midi input")); midiin->setIcon(*midiInSVGIcon); midiin->setCheckable(true); midiin->setFocusPolicy(Qt::NoFocus); tools->addWidget(midiin); speaker = new QToolButton(); speaker->setToolTip(tr("Play events")); speaker->setIcon(*speakerSingleNoteSVGIcon); speaker->setCheckable(true); speaker->setChecked(true); speaker->setFocusPolicy(Qt::NoFocus); QMenu* speakerPopupMenu = new QMenu(this); speaker->setMenu(speakerPopupMenu); speaker->setPopupMode(QToolButton::MenuButtonPopup); speakerSingleNote = new QAction(*speakerSingleNoteSVGIcon, tr("Play single note"), this); speakerChords = new QAction(*speakerChordsSVGIcon, tr("Play chords"), this); speakerPopupMenu->addAction(speakerSingleNote); speakerPopupMenu->addAction(speakerChords); connect(speakerSingleNote, &QAction::triggered, [this](bool checked) { setSpeakerSingleNoteMode(checked); } ); connect(speakerChords, &QAction::triggered, [this](bool checked) { setSpeakerChordMode(checked); } ); tools->addWidget(speaker); // QAction* whatsthis = QWhatsThis::createAction(this); // whatsthis->setIcon(*whatsthisSVGIcon); // tools->addAction(whatsthis); toolbar = new MusEGui::Toolbar1(_rasterizerModel, this, _rasterInit); toolbar->setObjectName("Pianoroll Pos/Snap/Solo-tools"); addToolBar(toolbar); addToolBarBreak(); info = new MusEGui::NoteInfo(this); info->setObjectName("Pianoroll Note Info"); addToolBar(info); //--------------------------------------------------- // split //--------------------------------------------------- splitter = new MusEGui::Splitter(Qt::Vertical, mainw, "splitter"); //splitter->setHandleWidth(2); hsplitter = new MusEGui::Splitter(Qt::Horizontal, mainw, "hsplitter"); hsplitter->setChildrenCollapsible(true); //hsplitter->setHandleWidth(4); hscroll = new MusEGui::ScrollScale( (_minXMag * MusEGlobal::config.division) / 384, _maxXMag, _viewState.xscale(), 20000, Qt::Horizontal, mainw); QSizeGrip* corner = new QSizeGrip(mainw); trackInfoWidget = new TrackInfoWidget(hsplitter); genTrackInfo(trackInfoWidget); QWidget* gridS2_w = new QWidget(); gridS2_w->setObjectName("gridS2_w"); gridS2_w->setContentsMargins(0, 0, 0, 0); QGridLayout* gridS2 = new QGridLayout(gridS2_w); gridS2->setContentsMargins(0, 0, 0, 0); gridS2->setSpacing(0); gridS2->setRowStretch(0, 100); gridS2->setColumnStretch(1, 100); gridS2->addItem(new QSpacerItem(_pianoWidth, 0), 0, 0); gridS2->addWidget(hscroll, 0, 1); gridS2->addWidget(corner, 0, 2, Qt::AlignBottom|Qt::AlignRight); gridS2_w->setMaximumHeight(hscroll->sizeHint().height()); gridS2_w->setMinimumHeight(hscroll->sizeHint().height()); QWidget* splitter_w = new QWidget(); splitter_w->setObjectName("splitter_w"); splitter_w->setContentsMargins(0, 0, 0, 0); QVBoxLayout* splitter_vbox = new QVBoxLayout(splitter_w); splitter_vbox->setContentsMargins(0, 0, 0, 0); splitter_vbox->setSpacing(0); splitter_vbox->addWidget(splitter); splitter_vbox->addWidget(gridS2_w); hsplitter->addWidget(splitter_w); hsplitter->setStretchFactor(hsplitter->indexOf(trackInfoWidget), 0); QSizePolicy tipolicy = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); tipolicy.setHorizontalStretch(0); tipolicy.setVerticalStretch(100); trackInfoWidget->setSizePolicy(tipolicy); hsplitter->setStretchFactor(hsplitter->indexOf(splitter_w), 1); QSizePolicy epolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); epolicy.setHorizontalStretch(255); epolicy.setVerticalStretch(100); splitter->setSizePolicy(epolicy); mainGrid->addWidget(hsplitter, 0, 0, 1, 1); QList mops; mops.append(_trackInfoWidthInit); mops.append(_canvasWidthInit); hsplitter->setSizes(mops); QWidget* split1 = new QWidget(splitter); split1->setObjectName("split1"); QGridLayout* gridS1 = new QGridLayout(split1); gridS1->setContentsMargins(0, 0, 0, 0); gridS1->setSpacing(0); time = new MusEGui::MTScale(_raster, split1, _viewState.xscale()); piano = new Piano(split1, _viewState.yscale(), _pianoWidth, this); canvas = new PianoCanvas(this, split1, _viewState.xscale(), _viewState.yscale()); vscroll = new MusEGui::ScrollScale(-2, 3, _viewState.yscale(), KH * 75, Qt::Vertical, split1); setCurDrumInstrument(piano->curSelectedPitch()); canvas->setOrigin(_canvasXOrigin, 0); canvas->setCanvasTools(pianorollTools); canvas->setFocus(); connect(canvas, SIGNAL(toolChanged(int)), tools2, SLOT(set(int))); connect(MusEGlobal::muse, &MusE::configChanged, tools2, &EditToolBar::configChanged); connect(canvas, SIGNAL(horizontalZoom(bool, const QPoint&)), SLOT(horizontalZoom(bool, const QPoint&))); connect(canvas, SIGNAL(horizontalZoom(int, const QPoint&)), SLOT(horizontalZoom(int, const QPoint&))); connect(canvas, SIGNAL(curPartHasChanged(MusECore::Part*)), SLOT(updateTrackInfo())); time->setOrigin(_canvasXOrigin, 0); gridS1->setRowStretch(2, 100); gridS1->setColumnStretch(1, 100); gridS1->addWidget(time, 0, 1, 1, 2); gridS1->addWidget(MusECore::hLine(split1), 1, 0, 1, 3); gridS1->addWidget(piano, 2, 0); gridS1->addWidget(canvas, 2, 1); gridS1->addWidget(vscroll, 2, 2); piano->setFixedWidth(_pianoWidth); connect(tools2, SIGNAL(toolChanged(int)), canvas, SLOT(setTool(int))); connect(info, SIGNAL(valueChanged(MusEGui::NoteInfo::ValType, int)), SLOT(noteinfoChanged(MusEGui::NoteInfo::ValType, int))); connect(info, SIGNAL(deltaModeChanged(bool)), SLOT(deltaModeChanged(bool))); connect(vscroll, SIGNAL(scrollChanged(int)), piano, SLOT(setYPos(int))); connect(vscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setYPos(int))); connect(vscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setYMag(int))); connect(vscroll, SIGNAL(scaleChanged(int)), piano, SLOT(setYMag(int))); connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scrollChanged(int)), time, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setXMag(int))); connect(hscroll, SIGNAL(scaleChanged(int)), time, SLOT(setXMag(int))); connect(canvas, SIGNAL(newWidth(int)), SLOT(newCanvasWidth(int))); connect(canvas, SIGNAL(pitchChanged(int)), piano, SLOT(setPitch(int))); connect(canvas, SIGNAL(verticalScroll(unsigned)), vscroll, SLOT(setPos(unsigned))); connect(canvas, SIGNAL(horizontalScroll(unsigned)),hscroll, SLOT(setPos(unsigned))); connect(canvas, SIGNAL(horizontalScrollNoLimit(unsigned)),hscroll, SLOT(setPosNoLimit(unsigned))); connect(canvas, SIGNAL(selectionChanged(int, MusECore::Event&, MusECore::Part*, bool)), this, SLOT(setSelection(int, MusECore::Event&, MusECore::Part*, bool))); connect(piano, SIGNAL(keyPressed(int, int, bool)), canvas, SLOT(pianoPressed(int, int, bool))); connect(piano, SIGNAL(keyReleased(int, bool)), canvas, SLOT(pianoReleased(int, bool))); connect(piano, SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*))); connect(piano, SIGNAL(wheelStep(bool)), vscroll, SLOT(stepScale(bool))); connect(srec, SIGNAL(toggled(bool)), SLOT(setSteprec(bool))); connect(midiin, SIGNAL(toggled(bool)), canvas, SLOT(setMidiin(bool))); connect(speaker, SIGNAL(toggled(bool)), SLOT(setSpeaker(bool))); connect(canvas, SIGNAL(followEvent(int)), SLOT(follow(int))); connect(info, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(info, SIGNAL(escapePressed()), SLOT(focusCanvas())); connect(hscroll, SIGNAL(scaleChanged(int)), SLOT(updateHScrollRange())); piano->setYPos(_viewState.yscroll()); canvas->setYPos(_viewState.yscroll()); vscroll->setOffset(_viewState.yscroll()); info->setEnabled(false); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged1(MusECore::SongChangedStruct_t))); setWindowTitle(canvas->getCaption()); updateHScrollRange(); // connect to toolbar connect(canvas, SIGNAL(pitchChanged(int)), toolbar, SLOT(setPitch(int))); connect(canvas, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(piano, SIGNAL(pitchChanged(int)), toolbar, SLOT(setPitch(int))); connect(time, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(toolbar, &Toolbar1::rasterChanged, [this](int raster) { setRaster(raster); } ); connect(toolbar, SIGNAL(soloChanged(bool)), SLOT(soloChanged(bool))); connect(toolbar, &Toolbar1::gridOnChanged, [this](bool v) { gridOnChanged(v); } ); setFocusPolicy(Qt::NoFocus); setEventColorMode(colorMode); QClipboard* cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(clipboardChanged())); clipboardChanged(); // enable/disable "Paste" selectionChanged(); // enable/disable "Copy" & "Paste" // initShortcuts(); // initialize shortcuts configChanged(); // set configuration values, initialize shortcuts // Don't bother if not showing default stuff, since something else // will likely take care of it, like the song file loading routines. if(showDefaultControls) { const MusECore::Pos cpos=MusEGlobal::song->cPos(); canvas->setPos(0, cpos.tick(), true); canvas->selectAtTick(cpos.tick()); unsigned pos=0; if(initPos >= INT_MAX) pos = MusEGlobal::song->cpos(); else pos = initPos; if(pos > INT_MAX) pos = INT_MAX; //if (pos) hscroll->setOffset((int)pos); } if(canvas->track()) { updateTrackInfo(); toolbar->setSolo(canvas->track()->solo()); } setSpeakerMode(_playEventsMode); initTopwinState(); finalizeInit(); // Add initial controllers. // Don't bother if not showing default stuff, since something else // will likely take care of it, like the song file loading routines. if(showDefaultControls) { CtrlEdit* ctrl_edit; const MusECore::MidiCtrlViewStateList& mcvsl = _viewState.controllers(); for(MusECore::ciMidiCtrlViewState i = mcvsl.cbegin(); i != mcvsl.cend(); ++i) { const MusECore::MidiCtrlViewState& mcvs = *i; ctrl_edit = addCtrl(mcvs._num); if(ctrl_edit) ctrl_edit->setPerNoteVel(mcvs._perNoteVel); } } } //--------------------------------------------------------- // songChanged1 //--------------------------------------------------------- void PianoRoll::songChanged1(MusECore::SongChangedStruct_t bits) { if(_isDeleting) // Ignore while while deleting to prevent crash. return; // We must catch this first and be sure to update the strips. if(bits & SC_TRACK_REMOVED) { checkTrackInfoTrack(); } if (bits & SC_DIVISION_CHANGED) { // The division has changed. The raster table and raster model will have been // cleared and re-filled, so any views on the model will no longer have a // current item and our current raster value will be invalid. They WILL NOT // emit an activated signal. So we must manually select a current item and // raster value here. We could do something fancy to try to keep the current // index - for example stay on quarter note - by taking the ratio of the new // division to old division and apply that to the old raster value and try // to select that index, but the division has already changed. // So instead, simply try to select the current raster value. The index in the box may change. // Be sure to use what it chooses. changeRaster(_raster); // Now set a reasonable zoom (mag) range. setupHZoomRange(); } if (bits & SC_SOLO) { if(canvas->track()) toolbar->setSolo(canvas->track()->solo()); } songChanged(bits); // We'll receive SC_SELECTION if a different part is selected. // Addition - also need to respond here to moving part to another track. (Tim) if (bits & (SC_PART_INSERTED | SC_PART_REMOVED)) updateTrackInfo(); // We must marshall song changed instead of connecting to the strip's song changed // otherwise it crashes when loading another song because track is no longer valid // and the strip's songChanged() seems to be called before Pianoroll songChanged() // gets called and has a chance to stop the crash. // Also, calling updateTrackInfo() from here is too heavy, it destroys and recreates // the strips each time no matter what the flags are ! else trackInfoSongChange(bits); } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void PianoRoll::configChanged() { if (MusEGlobal::config.canvasBgPixmap.isEmpty()) { canvas->setBg(MusEGlobal::config.midiCanvasBg); canvas->setBg(QPixmap()); } else { canvas->setBg(QPixmap(MusEGlobal::config.canvasBgPixmap)); } toolbar->setGridOn(MusEGlobal::config.canvasShowGrid); initShortcuts(); canvas->redraw(); } //--------------------------------------------------------- // horizontalZoom //--------------------------------------------------------- void PianoRoll::horizontalZoom(bool zoom_in, const QPoint& glob_pos) { int mag = hscroll->mag(); int zoomlvl = MusEGui::ScrollScale::getQuickZoomLevel(mag); if(zoom_in) { if (zoomlvl < MusEGui::ScrollScale::zoomLevels-1) zoomlvl++; } else { if (zoomlvl > 1) zoomlvl--; } int newmag = MusEGui::ScrollScale::convertQuickZoomLevelToMag(zoomlvl); QPoint cp = canvas->mapFromGlobal(glob_pos); QPoint sp = splitter->mapFromGlobal(glob_pos); if(cp.x() >= 0 && cp.x() < canvas->width() && sp.y() >= 0 && sp.y() < splitter->height()) hscroll->setMag(newmag, cp.x()); } void PianoRoll::horizontalZoom(int mag, const QPoint& glob_pos) { QPoint cp = canvas->mapFromGlobal(glob_pos); QPoint sp = splitter->mapFromGlobal(glob_pos); if(cp.x() >= 0 && cp.x() < canvas->width() && sp.y() >= 0 && sp.y() < splitter->height()) hscroll->setMag(hscroll->mag() + mag, cp.x()); } //--------------------------------------------------------- // updateHScrollRange //--------------------------------------------------------- void PianoRoll::updateHScrollRange() { int s, e; canvas->range(&s, &e); // Show one more measure. e += MusEGlobal::sigmap.ticksMeasure(e); // Show another quarter measure due to imprecise drawing at canvas end point. e += MusEGlobal::sigmap.ticksMeasure(e) / 4; // Compensate for the fixed piano and vscroll widths. e += canvas->rmapxDev(_pianoWidth - vscroll->width()); int s1, e1; hscroll->range(&s1, &e1); if(s != s1 || e != e1) hscroll->setRange(s, e); } //--------------------------------------------------------- // follow //--------------------------------------------------------- void PianoRoll::follow(int pos) { int s, e; canvas->range(&s, &e); if (pos < e && pos >= s) hscroll->setOffset(pos); if (pos < s) hscroll->setOffset(s); } //--------------------------------------------------------- // setTime //--------------------------------------------------------- void PianoRoll::setTime(unsigned tick) { toolbar->setTime(tick); time->setPos(3, tick, false); } //--------------------------------------------------------- // cmd // pulldown menu commands //--------------------------------------------------------- void PianoRoll::cmd(int cmd) { // Don't process if user is dragging or has clicked on the events. // Causes crashes later in Canvas::viewMouseMoveEvent and viewMouseReleaseEvent. if(canvas->getCurrentDrag()) return; MusECore::TagEventList tag_list; const FunctionDialogElements_t fn_element_dflt = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton | FunctionAllPartsButton | FunctionSelectedPartsButton; switch (cmd) { case PianoCanvas::CMD_CUT: tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::cut_items(&tag_list); break; case PianoCanvas::CMD_COPY: tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::copy_items(&tag_list); break; case PianoCanvas::CMD_COPY_RANGE: tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( !itemsAreSelected(), true, true, MusEGlobal::song->lPos(), MusEGlobal::song->rPos())); MusECore::copy_items(&tag_list); break; case PianoCanvas::CMD_PASTE: ((PianoCanvas*)canvas)->cmd(PianoCanvas::CMD_SELECT_NONE); MusECore::paste_items(partlist_to_set(parts()), 3072, MusECore::FunctionOptionsStruct( MusECore::FunctionEraseItemsDefault | MusECore::FunctionPasteNeverNewPart)); break; case PianoCanvas::CMD_PASTE_TO_CUR_PART: ((PianoCanvas*)canvas)->cmd(PianoCanvas::CMD_SELECT_NONE); MusECore::paste_items(partlist_to_set(parts()), 3072, MusECore::FunctionOptionsStruct( MusECore::FunctionEraseItemsDefault | MusECore::FunctionPasteNeverNewPart), canvas->part()); break; case PianoCanvas::CMD_PASTE_DIALOG: ((PianoCanvas*)canvas)->cmd(PianoCanvas::CMD_SELECT_NONE); MusECore::paste_items(partlist_to_set(parts()), (canvas->part())); break; case PianoCanvas::CMD_MODIFY_GATE_TIME: { FunctionDialogReturnGateTime ret = gatetime_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::modify_notelen_items(&tag_list, ret._rateVal, ret._offsetVal); } break; } case PianoCanvas::CMD_MODIFY_VELOCITY: { FunctionDialogReturnVelocity ret = velocity_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::modify_velocity_items(&tag_list, ret._rateVal, ret._offsetVal); } break; } case PianoCanvas::CMD_CRESCENDO: { FunctionDialogReturnCrescendo ret = crescendo_items_dialog(FunctionDialogMode( FunctionLoopedButton | FunctionSelectedLoopedButton | FunctionAllPartsButton | FunctionSelectedPartsButton)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::crescendo_items(&tag_list, ret._start_val, ret._end_val, ret._absolute); } break; } case PianoCanvas::CMD_QUANTIZE: { FunctionDialogReturnQuantize ret = quantize_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::quantize_items(&tag_list, ret._raster_index, ret._quant_len, ret._strength, ret._swing, ret._threshold); } break; } case PianoCanvas::CMD_TRANSPOSE: { FunctionDialogReturnTranspose ret = transpose_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::transpose_items(&tag_list, ret._amount); } break; } case PianoCanvas::CMD_ERASE_EVENT: { FunctionDialogReturnErase ret = erase_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::erase_items(&tag_list, ret._veloThreshold, ret._veloThresUsed, ret._lenThreshold, ret._lenThresUsed); } } break; case PianoCanvas::CMD_DEL: tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::erase_items(&tag_list); break; case PianoCanvas::CMD_NOTE_SHIFT: { FunctionDialogReturnMove ret = move_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::move_items(&tag_list, ret._amount); } break; } case PianoCanvas::CMD_FIXED_LEN: { FunctionDialogReturnSetLen ret = setlen_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::set_notelen_items(&tag_list, ret._len); } break; } case PianoCanvas::CMD_DELETE_OVERLAPS: { FunctionDialogReturnDelOverlaps ret = deloverlaps_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::delete_overlaps_items(&tag_list); } break; } case PianoCanvas::CMD_LEGATO: { FunctionDialogReturnLegato ret = legato_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::legato_items(&tag_list, ret._min_len, !ret._allow_shortening); } break; } default: ((PianoCanvas*)canvas)->cmd(cmd); } } //--------------------------------------------------------- // setSelection // update Info Line //--------------------------------------------------------- void PianoRoll::setSelection(int tick, MusECore::Event& e, MusECore::Part* /*part*/, bool update) { int selections = canvas->selectionSize(); // Diagnostics: //printf("PianoRoll::setSelection selections:%d event empty:%d firstValueSet:%d\n", selections, e.empty(), firstValueSet); //if(!e.empty()) // e.dump(); if(update) { // Selections have changed. Reset these. tickOffset = 0; lenOffset = 0; pitchOffset = 0; veloOnOffset = 0; veloOffOffset = 0; // Force 'suggested' modes: if (selections == 1) { deltaMode = false; info->setDeltaMode(deltaMode); } else if (selections > 1) { // A feeble attempt to hold on to the user's setting. Should try to bring back // selEvent (removed), but there were problems using it (it's a reference). //if(lastSelections <= 1) { deltaMode = true; info->setDeltaMode(deltaMode); } } } lastSelections = selections; if ((selections == 1) || (selections > 1 && !firstValueSet)) { tickValue = tick; lenValue = e.lenTick(); pitchValue = e.pitch(); veloOnValue = e.velo(); if(veloOnValue == 0) { veloOnValue = 1; fprintf(stderr, "PianoRoll::setSelection: Warning: Zero note on velocity!\n"); } veloOffValue = e.veloOff(); firstValueSet = true; } if (selections > 0) { info->setEnabled(true); if (deltaMode) info->setValues(tickOffset, lenOffset, pitchOffset, veloOnOffset, veloOffOffset); else info->setValues(tickValue, lenValue, pitchValue, veloOnValue, veloOffValue); } else { info->setEnabled(false); info->setValues(0, 0, 0, deltaMode ? 0 : 1, 0); firstValueSet = false; tickValue = 0; lenValue = 0; pitchValue = 0; veloOnValue = 1; veloOffValue = 0; tickOffset = 0; lenOffset = 0; pitchOffset = 0; veloOnOffset = 0; veloOffOffset = 0; } info->setReturnMode(selections >= 2); selectionChanged(); } //--------------------------------------------------------- // focusCanvas //--------------------------------------------------------- void PianoRoll::focusCanvas() { if(MusEGlobal::config.smartFocus) { canvas->setFocus(); canvas->activateWindow(); } } //--------------------------------------------------------- // deltaModeChanged //--------------------------------------------------------- void PianoRoll::deltaModeChanged(bool delta_on) { if(deltaMode == delta_on) return; deltaMode = delta_on; if(canvas->selectionSize() > 0) { if(deltaMode) info->setValues(tickOffset, lenOffset, pitchOffset, veloOnOffset, veloOffOffset); else info->setValues(tickValue, lenValue, pitchValue, veloOnValue, veloOffValue); } } //--------------------------------------------------------- // edit currently selected Event //--------------------------------------------------------- void PianoRoll::noteinfoChanged(MusEGui::NoteInfo::ValType type, int val) { int selections = canvas->selectionSize(); if (selections == 0) { printf("noteinfoChanged while nothing selected\n"); } else if (selections > 0) { if(deltaMode) { // treat noteinfo values as offsets to event values int delta = 0; switch (type) { case MusEGui::NoteInfo::VAL_TIME: delta = val - tickOffset; tickOffset = val; break; case MusEGui::NoteInfo::VAL_LEN: delta = val - lenOffset; lenOffset = val; break; case MusEGui::NoteInfo::VAL_VELON: delta = val - veloOnOffset; veloOnOffset = val; break; case MusEGui::NoteInfo::VAL_VELOFF: delta = val - veloOffOffset; veloOffOffset = val; break; case MusEGui::NoteInfo::VAL_PITCH: delta = val - pitchOffset; pitchOffset = val; break; } if (delta) canvas->modifySelected(type, delta); } else { switch (type) { case MusEGui::NoteInfo::VAL_TIME: tickValue = val; break; case MusEGui::NoteInfo::VAL_LEN: lenValue = val; break; case MusEGui::NoteInfo::VAL_VELON: veloOnValue = val; break; case MusEGui::NoteInfo::VAL_VELOFF: veloOffValue = val; break; case MusEGui::NoteInfo::VAL_PITCH: pitchValue = val; break; } canvas->modifySelected(type, val, false); // No delta mode. } } } //--------------------------------------------------------- // ctrlPopupTriggered //--------------------------------------------------------- void PianoRoll::ctrlPopupTriggered(QAction* act) { // TODO Merge most of this with duplicate code in drum edit, // maybe by putting it in a new function near populateMidiCtrlMenu. if(!act || (act->data().toInt() == -1)) return; int newCtlNum = -1; MusECore::Part* part = curCanvasPart(); MusECore::MidiTrack* track = (MusECore::MidiTrack*)(part->track()); int channel = track->outChannel(); MusECore::MidiPort* port = &MusEGlobal::midiPorts[track->outPort()]; MusECore::MidiCtrlValListList* cll = port->controller(); const int min = channel << 24; const int max = min + 0x1000000; const int edit_ins = max + 3; const int velo = max + 0x101; int rv = act->data().toInt(); if (rv == velo) { // special case velocity newCtlNum = MusECore::CTRL_VELOCITY; } else if (rv == edit_ins) { // edit instrument MusECore::MidiInstrument* instr = port->instrument(); MusEGlobal::muse->startEditInstrument(instr ? instr->iname() : QString(), EditInstrumentControllers); } else { // Select a control if(cll->find(channel, rv) == cll->end()) cll->add(channel, new MusECore::MidiCtrlValList(rv)); newCtlNum = rv; if(port->drumController(rv)) newCtlNum |= 0xff; } if(newCtlNum != -1) { CtrlEdit* ctrlEdit = new CtrlEdit(splitter, this, _viewState.xscale(), _canvasXOrigin, 0, false, "pianoCtrlEdit"); ctrlEdit->setController(newCtlNum); setupNewCtrl(ctrlEdit); } } //--------------------------------------------------------- // addCtrlClicked //--------------------------------------------------------- void PianoRoll::addCtrlClicked() { PopupMenu* pup = new PopupMenu(true); // true = enable stay open. Don't bother with parent. connect(pup, &QMenu::triggered, [this](QAction* act) { ctrlPopupTriggered(act); } ); /*int est_width =*/ populateMidiCtrlMenu(pup, parts(), curCanvasPart(), curDrumInstrument()); QPoint ep = addctrl->mapToGlobal(QPoint(0,0)); //int newx = ep.x() - ctrlMainPop->width(); // Too much! Width says 640. Maybe because it hasn't been shown yet . // int newx = ep.x() - est_width; // if(newx < 0) // newx = 0; // ep.setX(newx); pup->exec(ep); delete pup; addctrl->setDown(false); } //--------------------------------------------------------- // ctrlMenuAboutToShow //--------------------------------------------------------- void PianoRoll::ctrlMenuAboutToShow() { // Clear the menu and delete the contents. // "Removes all the menu's actions. Actions owned by the menu and not shown // in any other widget are deleted." addControllerMenu->clear(); populateMidiCtrlMenu(addControllerMenu, parts(), curCanvasPart(), curDrumInstrument()); } //--------------------------------------------------------- // ctrlMenuAboutToHide //--------------------------------------------------------- void PianoRoll::ctrlMenuAboutToHide() { // Clear the menu and delete the contents, since it's going to be cleared // and refilled anyway next time opened, so we can save memory. // "Removes all the menu's actions. Actions owned by the menu and not shown // in any other widget are deleted." // FIXME: This crashes, of course... // addControllerMenu->clear(); } //--------------------------------------------------------- // addCtrl //--------------------------------------------------------- CtrlEdit* PianoRoll::addCtrl(int ctl_num) { CtrlEdit* ctrlEdit = new CtrlEdit(splitter, this, _viewState.xscale(), _canvasXOrigin, 0, false, "pianoCtrlEdit"); ctrlEdit->setController(ctl_num); ctrlEdit->setPanelWidth(_pianoWidth); setupNewCtrl(ctrlEdit); return ctrlEdit; } //--------------------------------------------------------- // setupNewCtrl //--------------------------------------------------------- void PianoRoll::setupNewCtrl(CtrlEdit* ctrlEdit) { connect(tools2, SIGNAL(toolChanged(int)), ctrlEdit, SLOT(setTool(int))); connect(hscroll, SIGNAL(scrollChanged(int)), ctrlEdit, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), ctrlEdit, SLOT(setXMag(int))); connect(ctrlEdit, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(ctrlEdit, SIGNAL(destroyedCtrl(CtrlEdit*)), SLOT(removeCtrl(CtrlEdit*))); connect(ctrlEdit, SIGNAL(yposChanged(int)), toolbar, SLOT(setInt(int))); connect(ctrlEdit, SIGNAL(redirectWheelEvent(QWheelEvent*)), canvas, SLOT(redirectedWheelEvent(QWheelEvent*))); connect(piano, SIGNAL(curSelectedPitchChanged(int)), SLOT(setCurDrumInstrument(int))); //connect(piano, SIGNAL(curSelectedPitchChanged(int)), canvas, SLOT(setCurDrumInstrument(int))); connect(canvas, SIGNAL(curPartHasChanged(MusECore::Part*)), ctrlEdit, SLOT(curPartHasChanged(MusECore::Part*))); setCurDrumInstrument(piano->curSelectedPitch()); ctrlEdit->setTool(tools2->curTool()); ctrlEdit->setXPos(hscroll->pos()); ctrlEdit->setXMag(hscroll->getScaleValue()); ctrlEdit->setPanelWidth(_pianoWidth); ctrlEdit->show(); ctrlEditList.push_back(ctrlEdit); } //--------------------------------------------------------- // removeCtrl //--------------------------------------------------------- void PianoRoll::removeCtrl(CtrlEdit* ctrl) { for (std::list::iterator i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) { if (*i == ctrl) { ctrlEditList.erase(i); break; } } } //--------------------------------------------------------- // getViewState //--------------------------------------------------------- MusECore::MidiPartViewState PianoRoll::getViewState() const { MusECore::MidiPartViewState vs; vs.setXScroll(hscroll->offset()); vs.setYScroll(vscroll->offset()); vs.setXScale(hscroll->getScaleValue()); vs.setYScale(vscroll->getScaleValue()); CtrlEdit* ce; for(CtrlEditList::const_iterator i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) { ce = *i; vs.addController(MusECore::MidiCtrlViewState(ce->ctrlNum(), ce->perNoteVel())); } return vs; } void PianoRoll::storeInitialViewState() const { const MusECore::PartList* pl = parts(); if(pl) { const MusECore::MidiPartViewState vs = getViewState(); for(MusECore::ciPart i = pl->begin(); i != pl->end(); ++i) i->second->setViewState(vs); } } //--------------------------------------------------------- // closeEvent // Save state. // Disconnect signals which may cause crash due to Qt deferred deletion on close. //--------------------------------------------------------- void PianoRoll::closeEvent(QCloseEvent* e) { _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. storeSettings(); emit isDeleting(static_cast(this)); e->accept(); } void PianoRoll::storeSettings() { QSettings settings; settings.setValue("Pianoroll/windowState", saveState()); //Store values of the horizontal splitter QList sizes = hsplitter->sizes(); QList::iterator it = sizes.begin(); _trackInfoWidthInit = *it; //There are only 2 values stored in the sizelist, size of trackinfo widget and canvas widget it++; _canvasWidthInit = *it; } //--------------------------------------------------------- // readConfiguration //--------------------------------------------------------- void PianoRoll::readConfiguration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "raster") _rasterInit = xml.parseInt(); else if (tag == "trackinfowidth") _trackInfoWidthInit = xml.parseInt(); else if (tag == "canvaswidth") _canvasWidthInit = xml.parseInt(); else if (tag == "colormode") colorModeInit = (MidiEventColorMode) xml.parseInt(); else if (tag == "topwin") TopWin::readConfiguration(PIANO_ROLL,xml); else xml.unknown("PianoRoll"); break; case MusECore::Xml::TagEnd: if (tag == "pianoroll") return; default: break; } } } //--------------------------------------------------------- // writeConfiguration //--------------------------------------------------------- void PianoRoll::writeConfiguration(int level, MusECore::Xml& xml) { xml.tag(level++, "pianoroll"); xml.intTag(level, "raster", _rasterInit); xml.intTag(level, "trackinfowidth", _trackInfoWidthInit); xml.intTag(level, "canvaswidth", _canvasWidthInit); xml.intTag(level, "colormode", (int)colorModeInit); TopWin::writeConfiguration(PIANO_ROLL, level, xml); xml.etag(level, "pianoroll"); } //--------------------------------------------------------- // soloChanged // signal from solo button //--------------------------------------------------------- void PianoRoll::soloChanged(bool flag) { if(canvas->track()) { // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(canvas->track(), flag, MusECore::PendingOperationItem::SetTrackSolo)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } } //--------------------------------------------------------- // gridOnChanged //--------------------------------------------------------- void PianoRoll::gridOnChanged(bool flag) { MusEGlobal::config.canvasShowGrid = flag; // We want the simple version, don't set the style or stylesheet yet. MusEGlobal::muse->changeConfig(true); } //--------------------------------------------------------- // setRaster //--------------------------------------------------------- void PianoRoll::setRaster(int val) { // Request to set the raster, but be sure to use the one it chooses, // which may be different than the one requested. val = _rasterizerModel->checkRaster(val); MidiEditor::setRaster(val); _rasterInit = _raster; time->setRaster(_raster); canvas->redrawGrid(); for (auto it : ctrlEditList) it->redrawCanvas(); focusCanvas(); // give back focus after kb input } //--------------------------------------------------------- // changeRaster //--------------------------------------------------------- int PianoRoll::changeRaster(int val) { // Request to set the raster, but be sure to use the one it chooses, // which may be different than the one requested. MidiEditor::setRaster(toolbar->changeRaster(val)); time->setRaster(_raster); canvas->redrawGrid(); for (auto it : ctrlEditList) it->redrawCanvas(); return _raster; } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void PianoRoll::writeStatus(int level, MusECore::Xml& xml) const { writePartList(level, xml); xml.tag(level++, "pianoroll"); MidiEditor::writeStatus(level, xml); splitter->writeStatus(level, xml); if(hsplitter) hsplitter->writeStatus(level, xml); for (std::list::const_iterator i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) { (*i)->writeStatus(level, xml); } xml.intTag(level, "steprec", canvas->steprec()); xml.intTag(level, "midiin", canvas->midiin()); xml.intTag(level, "tool", int(canvas->tool())); xml.intTag(level, "playEvents", _playEvents); xml.intTag(level, "playEventsMode", _playEventsMode); xml.intTag(level, "xmag", hscroll->mag()); xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", vscroll->mag()); xml.intTag(level, "ypos", vscroll->pos()); xml.tag(level, "/pianoroll"); } //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void PianoRoll::readStatus(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "steprec") { int val = xml.parseInt(); canvas->setSteprec(val); srec->setChecked(val); } else if (tag == "midiin") { int val = xml.parseInt(); canvas->setMidiin(val); midiin->setChecked(val); } else if (tag == "tool") { int tool = xml.parseInt(); canvas->setTool(tool); tools2->set(tool); } else if (tag == "midieditor") MidiEditor::readStatus(xml); else if (tag == "ctrledit") { CtrlEdit* ctrl = addCtrl(); ctrl->readStatus(xml); } else if (tag == splitter->objectName()) splitter->readStatus(xml); else if (hsplitter && tag == hsplitter->objectName()) hsplitter->readStatus(xml); else if (tag == "playEvents") { _playEvents = xml.parseInt(); canvas->setPlayEvents(_playEvents); speaker->setChecked(_playEvents); } else if (tag == "playEventsMode") { setSpeakerMode(EventCanvas::PlayEventsMode(xml.parseInt())); } else if (tag == "xmag") hscroll->setMag(xml.parseInt()); else if (tag == "xpos") hscroll->setPos(xml.parseInt()); else if (tag == "ymag") vscroll->setMag(xml.parseInt()); else if (tag == "ypos") vscroll->setPos(xml.parseInt()); else xml.unknown("PianoRoll"); break; case MusECore::Xml::TagEnd: if (tag == "pianoroll") { changeRaster(_raster); return; } default: break; } } } //--------------------------------------------------------- // viewKeyPressEvent //--------------------------------------------------------- void PianoRoll::keyPressEvent(QKeyEvent* event) { if (info->hasFocus()) { event->ignore(); return; } RasterizerModel::RasterPick rast_pick = RasterizerModel::NoPick; const int cur_rast = raster(); PianoCanvas* pc = (PianoCanvas*)canvas; int key = event->key(); if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) key += Qt::SHIFT; if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) key += Qt::ALT; if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) key+= Qt::CTRL; if (key == Qt::Key_Escape) { close(); return; } else if (key == shortcuts[SHRT_TOOL_POINTER].key) { tools2->set(MusEGui::PointerTool); return; } else if (key == shortcuts[SHRT_TOOL_PENCIL].key) { tools2->set(MusEGui::PencilTool); return; } else if (key == shortcuts[SHRT_TOOL_RUBBER].key) { tools2->set(MusEGui::RubberTool); return; } else if (key == shortcuts[SHRT_TOOL_LINEDRAW].key) { tools2->set(MusEGui::DrawTool); return; } else if (key == shortcuts[SHRT_TOOL_PAN].key) { tools2->set(MusEGui::PanTool); return; } else if (key == shortcuts[SHRT_TOOL_ZOOM].key) { tools2->set(MusEGui::ZoomTool); return; } else if (key == shortcuts[SHRT_INSTRUMENT_STEP_UP].key) { piano->setCurSelectedPitch(piano->curSelectedPitch()+1); MusEGlobal::song->update(SC_DRUMMAP); return; } else if (key == shortcuts[SHRT_INSTRUMENT_STEP_DOWN].key) { piano->setCurSelectedPitch(piano->curSelectedPitch()-1); MusEGlobal::song->update(SC_DRUMMAP); return; } else if (key == shortcuts[SHRT_POS_INC].key) { pc->pianoCmd(CMD_RIGHT); return; } else if (key == shortcuts[SHRT_POS_DEC].key) { pc->pianoCmd(CMD_LEFT); return; } else if (key == shortcuts[SHRT_POS_INC_NOSNAP].key) { pc->pianoCmd(CMD_RIGHT_NOSNAP); return; } else if (key == shortcuts[SHRT_POS_DEC_NOSNAP].key) { pc->pianoCmd(CMD_LEFT_NOSNAP); return; } else if (key == shortcuts[SHRT_INSERT_AT_LOCATION].key) { pc->pianoCmd(CMD_INSERT); return; } else if (key == Qt::Key_Backspace) { pc->pianoCmd(CMD_BACKSPACE); return; } else if (key == shortcuts[SHRT_ZOOM_IN].key) { horizontalZoom(true, QCursor::pos()); return; } else if (key == shortcuts[SHRT_ZOOM_OUT].key) { horizontalZoom(false, QCursor::pos()); return; } else if (key == shortcuts[SHRT_GOTO_CPOS].key) { MusECore::PartList* p = this->parts(); MusECore::Part* first = p->begin()->second; hscroll->setPos(MusEGlobal::song->cpos() - first->tick() ); return; } else if (key == shortcuts[SHRT_SCROLL_LEFT].key) { int pos = hscroll->pos() - MusEGlobal::config.division; if (pos < 0) pos = 0; hscroll->setPos(pos); return; } else if (key == shortcuts[SHRT_SCROLL_RIGHT].key) { int pos = hscroll->pos() + MusEGlobal::config.division; hscroll->setPos(pos); return; } else if (key == shortcuts[SHRT_SET_QUANT_BAR].key) rast_pick = RasterizerModel::GotoBar; else if (key == shortcuts[SHRT_SET_QUANT_OFF].key) //this hack has the downside that the next shortcut will use triols, but it's better than not having it, I think... rast_pick = RasterizerModel::GotoOff; else if (key == shortcuts[SHRT_SET_QUANT_1].key) rast_pick = RasterizerModel::Goto1; else if (key == shortcuts[SHRT_SET_QUANT_2].key) rast_pick = RasterizerModel::Goto2; else if (key == shortcuts[SHRT_SET_QUANT_3].key) rast_pick = RasterizerModel::Goto4; else if (key == shortcuts[SHRT_SET_QUANT_4].key) rast_pick = RasterizerModel::Goto8; else if (key == shortcuts[SHRT_SET_QUANT_5].key) rast_pick = RasterizerModel::Goto16; else if (key == shortcuts[SHRT_SET_QUANT_6].key) rast_pick = RasterizerModel::Goto32; else if (key == shortcuts[SHRT_SET_QUANT_7].key) rast_pick = RasterizerModel::Goto64; else if (key == shortcuts[SHRT_TOGGLE_TRIOL].key) rast_pick = RasterizerModel::ToggleTriple; else if (key == shortcuts[SHRT_TOGGLE_PUNCT].key) rast_pick = RasterizerModel::ToggleDotted; else if (key == shortcuts[SHRT_TOGGLE_PUNCT2].key) rast_pick = RasterizerModel::ToggleHigherDotted; else if (key == shortcuts[SHRT_EVENT_COLOR].key) { colorMode = MidiEventColorMode(int(colorMode) + 1); if (colorMode == MidiEventColorMode::lastInList) colorMode = MidiEventColorMode::blueEvents; setEventColorMode(colorMode); return; } else if (key == shortcuts[SHRT_MOVE_PLAY_TO_NOTE].key){ movePlayPointerToSelectedEvent(); return; } else if (key == shortcuts[SHRT_STEP_RECORD].key) { canvas->setSteprec(!srec->isChecked()); srec->setChecked(!srec->isChecked()); return; } else if (key == shortcuts[SHRT_MIDI_INPUT].key) { canvas->setMidiin(!midiin->isChecked()); midiin->setChecked(!midiin->isChecked()); return; } else if (key == shortcuts[SHRT_PLAY_EVENTS].key) { canvas->setPlayEvents(!speaker->isChecked()); speaker->setChecked(!speaker->isChecked()); return; } else if (key == shortcuts[SHRT_INC_VELOCITY].key) { MusECore::TagEventList tag_list; tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::modify_velocity_items(&tag_list, 100, 1); return; } else if (key == shortcuts[SHRT_DEC_VELOCITY].key) { MusECore::TagEventList tag_list; tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::modify_velocity_items(&tag_list, 100, -1); return; } else { //Default: event->ignore(); return; } if(rast_pick != RasterizerModel::NoPick) { const int new_rast = _rasterizerModel->pickRaster(cur_rast, rast_pick); if(new_rast != cur_rast) { setRaster(new_rast); toolbar->setRaster(_raster); } } } //--------------------------------------------------------- // setSteprec //--------------------------------------------------------- void PianoRoll::setSteprec(bool flag) { canvas->setSteprec(flag); if (flag == false) midiin->setChecked(flag); } //--------------------------------------------------------- // eventColorModeChanged //--------------------------------------------------------- void PianoRoll::eventColorModeChanged(MidiEventColorMode mode) { colorMode = mode; colorModeInit = colorMode; ((PianoCanvas*)(canvas))->setColorMode(colorMode); } //--------------------------------------------------------- // setEventColorMode //--------------------------------------------------------- void PianoRoll::setEventColorMode(MidiEventColorMode mode) { colorMode = mode; colorModeInit = colorMode; evColorBlueAction->setChecked(mode == MidiEventColorMode::blueEvents); evColorPitchAction->setChecked(mode == MidiEventColorMode::pitchColorEvents); evColorVelAction->setChecked(mode == MidiEventColorMode::velocityColorEvents); ((PianoCanvas*)(canvas))->setColorMode(colorMode); } //--------------------------------------------------------- // clipboardChanged //--------------------------------------------------------- void PianoRoll::clipboardChanged() { const bool has_gel = QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists")); editPasteAction->setEnabled(has_gel); editPasteToCurPartAction->setEnabled(has_gel); editPasteDialogAction->setEnabled(has_gel); } //--------------------------------------------------------- // selectionChanged //--------------------------------------------------------- void PianoRoll::selectionChanged() { bool flag = itemsAreSelected(); editCutAction->setEnabled(flag); editCopyAction->setEnabled(flag); editDelEventsAction->setEnabled(flag); } //--------------------------------------------------------- // setSpeaker //--------------------------------------------------------- void PianoRoll::setSpeaker(bool val) { _playEvents = val; canvas->setPlayEvents(_playEvents); } void PianoRoll::setSpeakerSingleNoteMode(bool) { setSpeakerMode(EventCanvas::PlayEventsSingleNote); } void PianoRoll::setSpeakerChordMode(bool) { setSpeakerMode(EventCanvas::PlayEventsChords); } void PianoRoll::setSpeakerMode(EventCanvas::PlayEventsMode mode) { _playEventsMode = mode; canvas->setPlayEventsMode(_playEventsMode); switch(_playEventsMode) { case EventCanvas::PlayEventsSingleNote: speaker->setIcon(*speakerSingleNoteSVGIcon); break; case EventCanvas::PlayEventsChords: speaker->setIcon(*speakerChordsSVGIcon); break; } } //--------------------------------------------------------- // setupHZoomRange //--------------------------------------------------------- void PianoRoll::setupHZoomRange() { const int min = (_minXMag * MusEGlobal::config.division) / 384; hscroll->setScaleRange(min, _maxXMag); } //--------------------------------------------------------- // initShortcuts //--------------------------------------------------------- void PianoRoll::initShortcuts() { editCutAction->setShortcut(shortcuts[SHRT_CUT].key); editCopyAction->setShortcut(shortcuts[SHRT_COPY].key); editCopyRangeAction->setShortcut(shortcuts[SHRT_COPY_RANGE].key); editPasteAction->setShortcut(shortcuts[SHRT_PASTE].key); editPasteToCurPartAction->setShortcut(shortcuts[SHRT_PASTE_TO_CUR_PART].key); editPasteDialogAction->setShortcut(shortcuts[SHRT_PASTE_DIALOG].key); editDelEventsAction->setShortcut(shortcuts[SHRT_DELETE].key); selectAllAction->setShortcut(shortcuts[SHRT_SELECT_ALL].key); selectNoneAction->setShortcut(shortcuts[SHRT_SELECT_NONE].key); selectInvertAction->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); selectInsideLoopAction->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key); selectOutsideLoopAction->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); selectRangeToSelectionAction->setShortcut(shortcuts[SHRT_LOCATORS_TO_SELECTION].key); if (selectNextPartAction && selectPrevPartAction) { selectPrevPartAction->setShortcut(shortcuts[SHRT_SELECT_PREV_PART].key); selectNextPartAction->setShortcut(shortcuts[SHRT_SELECT_NEXT_PART].key); } startListEditAction->setShortcut(shortcuts[SHRT_OPEN_LIST].key); // eventColor->menuAction()->setShortcut(shortcuts[SHRT_EVENT_COLOR].key); //evColorBlueAction->setShortcut(shortcuts[ ].key); //evColorPitchAction->setShortcut(shortcuts[ ].key); //evColorVelAction->setShortcut(shortcuts[ ].key); funcQuantizeAction->setShortcut(shortcuts[SHRT_QUANTIZE].key); funcGateTimeAction->setShortcut(shortcuts[SHRT_MODIFY_GATE_TIME].key); funcModVelAction->setShortcut(shortcuts[SHRT_MODIFY_VELOCITY].key); funcTransposeAction->setShortcut(shortcuts[SHRT_TRANSPOSE].key); funcEraseEventAction->setShortcut(shortcuts[SHRT_ERASE_EVENT].key); funcNoteShiftAction->setShortcut(shortcuts[SHRT_NOTE_SHIFT].key); funcSetFixedLenAction->setShortcut(shortcuts[SHRT_FIXED_LEN].key); funcDelOverlapsAction->setShortcut(shortcuts[SHRT_DELETE_OVERLAPS].key); editUseLastEditEventAction->setShortcut(shortcuts[SHRT_USE_LAST_EDITED_EVENT].key); } //--------------------------------------------------------- // execDeliveredScript //--------------------------------------------------------- void PianoRoll::execDeliveredScript(int id) { QString scriptfile = scripts.getScriptPath(id, true); scripts.executeScript(this, scriptfile.toLatin1().data(), parts(), raster(), true); } //--------------------------------------------------------- // execUserScript //--------------------------------------------------------- void PianoRoll::execUserScript(int id) { QString scriptfile = scripts.getScriptPath(id, false); scripts.executeScript(this, scriptfile.toLatin1().data(), parts(), raster(), true); } //--------------------------------------------------------- // newCanvasWidth //--------------------------------------------------------- void PianoRoll::newCanvasWidth(int /*w*/) { /* DELETETHIS whole function? int nw = w + (vscroll->width() - 18); // 18 is the fixed width of the CtlEdit VScale widget. if(nw < 1) nw = 1; for (std::list::iterator i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) { // Changed by Tim. p3.3.7 //(*i)->setCanvasWidth(w); (*i)->setCanvasWidth(nw); } updateHScrollRange(); */ } } // namespace MusEGui muse-4.0.0/src/muse/midiedit/pianoroll.h000066400000000000000000000156641404105311000201650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pianoroll.h,v 1.5.2.4 2009/11/16 11:29:33 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2012-2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PIANOROLL_H__ #define __PIANOROLL_H__ #include #include "type_defs.h" #include "globaldefs.h" #include "part.h" #include "midieditor.h" #include "ecanvas.h" #include "noteinfo.h" #include "midictrl_consts.h" #include "scripts.h" // Forward declarations: class QAction; class QMenu; class QToolBar; class QToolButton; class QWidget; class QPoint; class QCloseEvent; class QKeyEvent; namespace MusECore { class Event; class Xml; } namespace MusEGui { class TopWin; class CtrlEdit; class PitchLabel; class Splitter; class Toolbar1; class Piano; class PopupMenu; class EditToolBar; //--------------------------------------------------------- // PianoRoll //--------------------------------------------------------- class PianoRoll : public MidiEditor { Q_OBJECT Q_PROPERTY(int pianoWidth READ pianoWidth WRITE setPianoWidth) int _pianoWidth; QMenu *menuEdit, *menuFunctions, *menuSelect, *menuConfig, *eventColor, *menuScripts; PopupMenu* addControllerMenu; QAction* editCutAction; QAction* editCopyAction; QAction* editCopyRangeAction; QAction* editPasteAction; QAction* editPasteToCurPartAction; QAction* editPasteDialogAction; QAction* editDelEventsAction; QAction* editUseLastEditEventAction; QAction* selectAllAction; QAction* selectNoneAction; QAction* selectInvertAction; QAction* selectInsideLoopAction; QAction* selectOutsideLoopAction; QAction* selectPrevPartAction; QAction* selectNextPartAction; QAction *startListEditAction; QAction* selectRangeToSelectionAction; QAction* evColorBlueAction; QAction* evColorPitchAction; QAction* evColorVelAction; QAction* funcQuantizeAction; QAction* funcGateTimeAction; QAction* funcModVelAction; QAction* funcCrescAction; QAction* funcTransposeAction; QAction* funcEraseEventAction; QAction* funcNoteShiftAction; QAction* funcSetFixedLenAction; QAction* funcDelOverlapsAction; QAction* speakerSingleNote; QAction* speakerChords; QAction *addControllerAction; int tickValue; int lenValue; int pitchValue; int veloOnValue; int veloOffValue; bool firstValueSet; int tickOffset; int lenOffset; int pitchOffset; int veloOnOffset; int veloOffOffset; bool deltaMode; int lastSelections; MusEGui::NoteInfo* info; QToolButton* addctrl; QToolButton* srec; QToolButton* midiin; Piano* piano; MusEGui::Toolbar1* toolbar; MusEGui::Splitter* splitter; MusEGui::Splitter* hsplitter; QToolButton* speaker; QToolBar* tools; MusEGui::EditToolBar* tools2; MidiEventColorMode colorMode; static int _rasterInit; static int _trackInfoWidthInit; static int _canvasWidthInit; static MidiEventColorMode colorModeInit; // Initial view state. MusECore::MidiPartViewState _viewState; MusECore::Scripts scripts; bool _playEvents; EventCanvas::PlayEventsMode _playEventsMode; void initShortcuts(); void setupNewCtrl(CtrlEdit* ctrlEdit); void setEventColorMode(MidiEventColorMode); QWidget* genToolbar(QWidget* parent); virtual void closeEvent(QCloseEvent*) override; virtual void keyPressEvent(QKeyEvent*) override; void setSpeakerMode(EventCanvas::PlayEventsMode mode); // Sets up a reasonable zoom minimum and/or maximum based on // the current global midi division (ticks per quarter note) // which has a very wide range (48 - 12288). // Also sets the canvas and time scale offsets accordingly. void setupHZoomRange(); private slots: void setSelection(int /*tick*/, MusECore::Event&, MusECore::Part*, bool /*update*/); void noteinfoChanged(MusEGui::NoteInfo::ValType, int); void removeCtrl(CtrlEdit* ctrl); void soloChanged(bool flag); void gridOnChanged(bool flag); void setRaster(int) override; void cmd(int); void setSteprec(bool); void eventColorModeChanged(MidiEventColorMode); void clipboardChanged(); // enable/disable "Paste" void selectionChanged(); // enable/disable "Copy" & "Paste" void setSpeaker(bool); void setSpeakerSingleNoteMode(bool); void setSpeakerChordMode(bool); void setTime(unsigned); void follow(int pos); void songChanged1(MusECore::SongChangedStruct_t); void configChanged(); void newCanvasWidth(int); void deltaModeChanged(bool); void addCtrlClicked(); void ctrlPopupTriggered(QAction* act); void ctrlMenuAboutToShow(); void ctrlMenuAboutToHide(); signals: void isDeleting(MusEGui::TopWin*); public slots: void horizontalZoom(bool zoom_in, const QPoint& glob_pos); void horizontalZoom(int mag, const QPoint& glob_pos); void updateHScrollRange() override; void execDeliveredScript(int id); void execUserScript(int id); void focusCanvas() override; void storeInitialViewState() const override; public: PianoRoll(MusECore::PartList*, QWidget* parent = nullptr, const char* name = nullptr, unsigned initPos = INT_MAX, bool showDefaultControls = false); void readStatus(MusECore::Xml&) override; void writeStatus(int, MusECore::Xml&) const override; static void readConfiguration(MusECore::Xml&); static void writeConfiguration(int, MusECore::Xml&); void storeSettings() override; CtrlEdit* addCtrl(int ctl_num = MusECore::CTRL_VELOCITY); MusECore::MidiPartViewState getViewState() const; int pianoWidth() const { return _pianoWidth; } void setPianoWidth(int w) { _pianoWidth = w; } int changeRaster(int val); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/midiedit/prcanvas.cpp000066400000000000000000002027761404105311000203400ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: prcanvas.cpp,v 1.20.2.19 2009/11/16 11:29:33 lunar_shuttle Exp $ // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include #include "muse_math.h" #include #include "prcanvas.h" #include "view.h" #include "mpevent.h" #include "globals.h" #include "cmd.h" #include "song.h" #include "audio.h" #include "functions.h" #include "gconfig.h" #include "helper.h" #include "app.h" #include "type_defs.h" // Forwards from header: #include #include #include #include #include #include #include #include #include "part.h" #include "event.h" #include "pianoroll.h" #include "midieditor.h" #include "citem.h" #include "scrollscale.h" #include "steprec.h" #define CHORD_TIMEOUT 75 namespace MusEGui { //--------------------------------------------------------- // NEvent //--------------------------------------------------------- NEvent::NEvent(const MusECore::Event& e, MusECore::Part* p, int y) : EItem(e, p) { y = y - KH/4; unsigned tick = e.tick() + p->tick(); setPos(QPoint(tick, y)); setBBox(QRect(tick, y, e.lenTick(), KH/2)); // Give the moving point an initial value. setMp(pos()); } void PianoCanvas::setLastEdited(MusECore::Event& e) { if (lastEditedEvent==0) lastEditedEvent = new MusECore::Event(); *lastEditedEvent = e.clone(); } //--------------------------------------------------------- // addItem //--------------------------------------------------------- CItem* PianoCanvas::addItem(MusECore::Part* part, const MusECore::Event& event) { // if (signed(event.tick())<0) { // printf("ERROR: trying to add event before current part!\n"); // return NULL; // } NEvent* ev = new NEvent(event, part, pitch2y(event.pitch())); items.add(ev); return ev; } //--------------------------------------------------------- // PianoCanvas //--------------------------------------------------------- PianoCanvas::PianoCanvas(MidiEditor* pr, QWidget* parent, int sx, int sy) : EventCanvas(pr, parent, sx, sy) { setObjectName("Pianoroll"); colorMode = MidiEventColorMode::blueEvents; for (int i=0;i<128;i++) noteHeldDown[i]=false; supportsResizeToTheLeft = true; supportsMultipleResize = true; setStatusTip(tr("Pianoroll canvas: Use Pencil tool to draw and edit MIDI events, Pointer tool to select and edit. Press F1 for help.")); steprec=new MusECore::StepRec(noteHeldDown); songChanged(SC_TRACK_INSERTED); connect(MusEGlobal::song, SIGNAL(midiNote(int, int)), SLOT(midiNote(int,int))); } PianoCanvas::~PianoCanvas() { delete steprec; } //--------------------------------------------------------- // pitch2y //--------------------------------------------------------- int PianoCanvas::pitch2y(int pitch) const { int tt[] = { 5, 13, 19, 26, 34, 44, 52, 58, 65, 71, 78, 85 }; int y = (75 * KH) - (tt[pitch%12] + (7 * KH) * (pitch/12)); if (y < 0) y = 0; return y; } //--------------------------------------------------------- // y2pitch //--------------------------------------------------------- int PianoCanvas::y2pitch(int y) const { if (y < KH) return 127; const int total = (10 * 7 + 5) * KH; // 75 Ganztonschritte y = total - y; if (y < 0) return 0; int oct = (y / (7 * KH)) * 12; char kt[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 1, 1, 1, 1, 1, 1, 1, // 13 2, 2, 2, 2, 2, 2, // 19 3, 3, 3, 3, 3, 3, 3, // 26 4, 4, 4, 4, 4, 4, 4, 4, 4, // 34 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 43 6, 6, 6, 6, 6, 6, 6, // 52 7, 7, 7, 7, 7, 7, // 58 8, 8, 8, 8, 8, 8, 8, // 65 9, 9, 9, 9, 9, 9, // 71 10, 10, 10, 10, 10, 10, 10, // 78 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 // 87 }; return kt[y % 91] + oct; } // REMOVE Tim. citem. Changed. The original code... // //--------------------------------------------------------- // // drawEvent // // draws a note // //--------------------------------------------------------- // // void PianoCanvas::drawItem(QPainter& p, const MusEGui::CItem* item, // const QRect& rect) // { // QRect r = item->bbox(); // if(!virt()) // r.moveCenter(map(item->pos())); // // //QRect rr = p.transform().mapRect(rect); // Gives inconsistent positions. Source shows wrong operation for our needs. // QRect rr = map(rect); // Use our own map instead. // QRect mer = map(r); // // QRect mr = rr & mer; // if(mr.isNull()) // return; // // p.setPen(Qt::black); // struct Triple { // int r, g, b; // }; // // static Triple myColors [12] = { // ddskrjp // { 0xff, 0x3d, 0x39 }, // { 0x39, 0xff, 0x39 }, // { 0x39, 0x3d, 0xff }, // { 0xff, 0xff, 0x39 }, // { 0xff, 0x3d, 0xff }, // { 0x39, 0xff, 0xff }, // { 0xff, 0x7e, 0x7a }, // { 0x7a, 0x7e, 0xff }, // { 0x7a, 0xff, 0x7a }, // { 0xff, 0x7e, 0xbf }, // { 0x7a, 0xbf, 0xff }, // { 0xff, 0xbf, 0x7a } // }; // // QColor color; // NEvent* nevent = (NEvent*) item; // MusECore::Event event = nevent->event(); // if (nevent->part() != curPart){ // if(item->isMoving()) // color = Qt::gray; // else if(item->isSelected()) // color = Qt::black; // else // color = Qt::lightGray; // } // else { // if (item->isMoving()) { // color = Qt::gray; // } // else if (item->isSelected()) { // color = Qt::black; // } // else { // color.setRgb(0, 0, 255); // switch(colorMode) { // case 0: // break; // case 1: // pitch // { // Triple* c = &myColors[event.pitch() % 12]; // color.setRgb(c->r, c->g, c->b); // } // break; // case 2: // velocity // { // int velo = event.velo(); // if (velo < 64) // color.setRgb(velo*4, 0, 0xff); // else // color.setRgb(0xff, 0, (127-velo) * 4); // } // break; // } // } // } // // bool wmtxen = p.worldMatrixEnabled(); // p.setWorldMatrixEnabled(false); // int mx = mr.x(); // int my = mr.y(); // int mw = mr.width(); // int mh = mr.height(); // int mex = mer.x(); // int mey = mer.y(); // int mew = mer.width(); // int meh = mer.height(); // color.setAlpha(MusEGlobal::config.globalAlphaBlend); // QBrush brush(color); // p.fillRect(mr, brush); // // if(mex >= mx && mex <= mx + mw) // p.drawLine(mex, my, mex, my + mh - 1); // The left edge // if(mex + mew >= mx && mex + mew <= mx + mw) // p.drawLine(mex + mew, my, mex + mew, my + mh - 1); // The right edge // if(mey >= my && mey <= my + mh) // p.drawLine(mx, mey, mx + mw - 1, mey); // The top edge // if(mey + meh >= my && mey + meh <= my + mh) // p.drawLine(mx, mey + meh - 1, mx + mw - 1, mey + meh - 1); // The bottom edge // // // print note name on the drawn notes // if (MusEGlobal::config.showNoteNamesInPianoRoll) { // QFont f(MusEGlobal::config.fonts[1]); // // f.setPointSize(f.pointSize() * 0.85); // p.setFont(f); // // if (color.lightnessF() > 0.6f) { // // p.setPen(Qt::black); // // } else { // // p.setPen(Qt::white); // // } // QString noteStr = MusECore::pitch2string(event.pitch()); // // p.drawText(mer,Qt::AlignHCenter|Qt::AlignCenter, noteStr.toUpper()); // } // // // p.setWorldMatrixEnabled(wmtxen); // } #if 1 // Unmapped version works OK. But let's use the mapped version below for now... // The "mapped version" causes drawing artefacts, at least with HiDPI. // This one is OK, and probably faster too (kybos) //--------------------------------------------------------- // drawEvent // draws a note //--------------------------------------------------------- void PianoCanvas::drawItem(QPainter& p, const MusEGui::CItem* item, const QRect& mr, const QRegion&) { QRect ur = mapDev(mr).adjusted(0, 0, 0, 1); // This is the update comparison rectangle. This would normally be the same as the item's bounding rectangle // but in this case we have a one-pixel wide border. To accommodate for our border, expand the right edge right // by one, and the bottom edge down by one. This way we catch the full // necessary drawing rectangle when checking the requested update rectangle. // Note that this is units of ticks. QRect ubbr = item->bbox().adjusted(0, 0, 0, -1); if(!virt()) ubbr.moveCenter(map(item->pos())); const QRect mbbr = map(ubbr); // Use our own map instead. QRect ubr = ur & ubbr; const int ux = ur.x(); const int uy = ur.y(); const int uw = ur.width(); const int uh = ur.height(); const int ux_2 = ux + uw; const int uy_2 = uy + uh; const int ubbx = ubbr.x(); const int ubby = ubbr.y(); const int ubbw = ubbr.width(); const int ubbh = ubbr.height(); const int ubbx_2 = ubbx + ubbw; const int ubby_2 = ubby + ubbh; QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); struct Triple { int r, g, b; }; static Triple myColors [12] = { // ddskrjp { 0xff, 0x3d, 0x39 }, { 0x39, 0xff, 0x39 }, { 0x39, 0x3d, 0xff }, { 0xff, 0xff, 0x39 }, { 0xff, 0x3d, 0xff }, { 0x39, 0xff, 0xff }, { 0xff, 0x7e, 0x7a }, { 0x7a, 0x7e, 0xff }, { 0x7a, 0xff, 0x7a }, { 0xff, 0x7e, 0xbf }, { 0x7a, 0xbf, 0xff }, { 0xff, 0xbf, 0x7a } }; QColor color; NEvent* nevent = (NEvent*) item; MusECore::Event event = nevent->event(); if (nevent->part() != curPart){ if(item->isMoving()) color = Qt::gray; else if(item->isSelected()) color = Qt::black; else color = Qt::lightGray; } else { if (item->isMoving()) { color = Qt::gray; } else if (item->isSelected()) { color = MusEGlobal::config.midiItemSelectedColor; } else { color = MusEGlobal::config.midiItemColor; switch(colorMode) { case MidiEventColorMode::blueEvents: break; case MidiEventColorMode::pitchColorEvents: { Triple* c = &myColors[event.pitch() % 12]; color.setRgb(c->r, c->g, c->b); } break; case MidiEventColorMode::velocityColorEvents: { int velo = event.velo(); if (velo < 64) color.setRgb(velo*4, 0, 0xff); else color.setRgb(0xff, 0, (127-velo) * 4); break; } default: break; } } } color.setAlpha(MusEGlobal::config.globalAlphaBlend); QBrush brush(color); if(!ubr.isEmpty()) p.fillRect(ubr, brush); if(ubbx >= ux && ubbx < ux_2) p.drawLine(ubbx, ubby, ubbx, ubby_2); // The left edge if(ubbx_2 >= ux && ubbx_2 <= ux_2) p.drawLine(ubbx_2, ubby, ubbx_2, ubby_2); // The right edge if(ubby >= uy && ubby < uy_2) p.drawLine(ubbx, ubby, ubbx_2, ubby); // The top edge if(ubby_2 >= uy && ubby_2 <= uy_2) p.drawLine(ubbx, ubby_2, ubbx_2, ubby_2); // The bottom edge // print note name on the drawn notes if (!ubr.isEmpty() && MusEGlobal::config.showNoteNamesInPianoRoll) { QFont f(MusEGlobal::config.fonts[1]); f.setPointSize(f.pointSize() * 0.85); p.setFont(f); if (color.lightnessF() > 0.6f) { pen.setColor(Qt::black); p.setPen(pen); } else { pen.setColor(Qt::white); p.setPen(pen); } QString noteStr = MusECore::pitch2string(event.pitch()); const bool wmtxen = p.worldMatrixEnabled(); p.setWorldMatrixEnabled(false); p.drawText(mbbr,Qt::AlignHCenter|Qt::AlignCenter, noteStr.toUpper()); p.setWorldMatrixEnabled(wmtxen); } } #endif #if 0 // Mapped version also works OK. (Except the text is shifted up one pixel from current release or master). //--------------------------------------------------------- // drawEvent // draws a note //--------------------------------------------------------- void PianoCanvas::drawItem(QPainter& p, const MusEGui::CItem* item, const QRect& mr, const QRegion&) { QRect ur = mapDev(mr).adjusted(0, 0, 0, 1); QRect ubbr = item->bbox(); // This is the update comparison rectangle. This would normally be the same as the item's bounding rectangle // but in this case we have a one-pixel wide border. To accommodate for our border, expand the right edge right // by one, and the bottom edge down by one. This way we catch the full // necessary drawing rectangle when checking the requested update rectangle. // Note that this is units of ticks. if(!virt()) ubbr.moveCenter(map(item->pos())); //QRect rr = p.transform().mapRect(rect); // Gives inconsistent positions. Source shows wrong operation for our needs. const QRect mbbr = map(ubbr).adjusted(0, 0, 0, -1); const QRect mbr = mr & mbbr; const bool wmtxen = p.worldMatrixEnabled(); p.setWorldMatrixEnabled(false); const int ux = ur.x(); const int uy = ur.y(); const int uw = ur.width(); const int uh = ur.height(); const int ux_2 = ux + uw; const int uy_2 = uy + uh; const int ubbx = ubbr.x(); const int ubby = ubbr.y(); const int ubbw = ubbr.width(); const int ubbh = ubbr.height(); const int ubbx_2 = ubbx + ubbw; const int ubby_2 = ubby + ubbh; const int mbbx = mapx(ubbx); const int mbby = mapy(ubby); const int mbbx_2 = mapx(ubbx_2); const int mbby_2 = mapy(ubby_2); QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); struct Triple { int r, g, b; }; static Triple myColors [12] = { // ddskrjp { 0xff, 0x3d, 0x39 }, { 0x39, 0xff, 0x39 }, { 0x39, 0x3d, 0xff }, { 0xff, 0xff, 0x39 }, { 0xff, 0x3d, 0xff }, { 0x39, 0xff, 0xff }, { 0xff, 0x7e, 0x7a }, { 0x7a, 0x7e, 0xff }, { 0x7a, 0xff, 0x7a }, { 0xff, 0x7e, 0xbf }, { 0x7a, 0xbf, 0xff }, { 0xff, 0xbf, 0x7a } }; QColor color; NEvent* nevent = (NEvent*) item; MusECore::Event event = nevent->event(); if (nevent->part() != curPart){ if(item->isMoving()) color = Qt::gray; else if(item->isSelected()) color = Qt::black; else color = Qt::lightGray; } else { if (item->isMoving()) { color = Qt::gray; } else if (item->isSelected()) { color = Qt::black; } else { color.setRgb(0, 0, 255); switch(colorMode) { case 0: break; case 1: // pitch { Triple* c = &myColors[event.pitch() % 12]; color.setRgb(c->r, c->g, c->b); } break; case 2: // velocity { int velo = event.velo(); if (velo < 64) color.setRgb(velo*4, 0, 0xff); else color.setRgb(0xff, 0, (127-velo) * 4); } break; } } } color.setAlpha(MusEGlobal::config.globalAlphaBlend); QBrush brush(color); if(!mbr.isEmpty()) p.fillRect(mbr, brush); if(ubbx >= ux && ubbx < ux_2) p.drawLine(mbbx, mbby, mbbx, mbby_2 - 1); // The left edge if(ubbx_2 >= ux && ubbx_2 <= ux_2) p.drawLine(mbbx_2, mbby, mbbx_2, mbby_2 - 1); // The right edge if(ubby >= uy && ubby < uy_2) p.drawLine(mbbx, mbby, mbbx_2, mbby); // The top edge if(ubby_2 >= uy && ubby_2 <= uy_2) p.drawLine(mbbx, mbby_2 - 1, mbbx_2, mbby_2 - 1); // The bottom edge // print note name on the drawn notes if (!mbr.isEmpty() && MusEGlobal::config.showNoteNamesInPianoRoll) { QFont f(MusEGlobal::config.fonts[1]); f.setPointSize(f.pointSize() * 0.85); p.setFont(f); if (color.lightnessF() > 0.6f) { pen.setColor(Qt::black); p.setPen(pen); } else { pen.setColor(Qt::white); p.setPen(pen); } QString noteStr = MusECore::pitch2string(event.pitch()); p.drawText(mbbr,Qt::AlignHCenter|Qt::AlignVCenter, noteStr.toUpper()); } p.setWorldMatrixEnabled(wmtxen); } #endif // This version uses the ViewRect and ViewCoordinate classes for a more 'agnostic' technique. #if 0 //--------------------------------------------------------- // drawEvent // draws a note //--------------------------------------------------------- void PianoCanvas::drawItem(QPainter& p, const CItem* item, const QRect& mr, const QRegion&) { QRect ubbr = item->bbox(); if(!virt()) ubbr.moveCenter(map(item->pos())); //QRect rr = p.transform().mapRect(rect); // Gives inconsistent positions. Source shows wrong operation for our needs. const ViewRect vr(mr, true); const ViewRect vbbr(ubbr, false); // This is the update comparison rectangle. This would normally be the same as the item's bounding rectangle // but in this case we have a one-pixel wide border. To accommodate for our border, expand the right edge right // by one, and the bottom edge down by one. This way we catch the full // necessary drawing rectangle when checking the requested update rectangle. // Note that this is units of ticks. ViewRect vbbr_exp(item->bbox(), false); // adjustRect(vbbr_exp, // ViewWCoordinate(0, true), // ViewHCoordinate(0, true), // ViewWCoordinate(0, true), // // Normally we would use the y + h for our border, but here we need to // // use the bottom because of the uneven black/white key 6 - 7 - 6 height progressions. // ViewHCoordinate(-1, true)); const ViewRect vbr = intersected(vr, vbbr); const QRect mbr = asQRectMapped(vbr); const QRect mbbr_exp = asQRectMapped(vbbr_exp); QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); struct Triple { int r, g, b; }; static Triple myColors [12] = { // ddskrjp { 0xff, 0x3d, 0x39 }, { 0x39, 0xff, 0x39 }, { 0x39, 0x3d, 0xff }, { 0xff, 0xff, 0x39 }, { 0xff, 0x3d, 0xff }, { 0x39, 0xff, 0xff }, { 0xff, 0x7e, 0x7a }, { 0x7a, 0x7e, 0xff }, { 0x7a, 0xff, 0x7a }, { 0xff, 0x7e, 0xbf }, { 0x7a, 0xbf, 0xff }, { 0xff, 0xbf, 0x7a } }; QColor color; NEvent* nevent = (NEvent*) item; MusECore::Event event = nevent->event(); if (nevent->part() != curPart){ if(item->isMoving()) color = Qt::gray; else if(item->isSelected()) color = Qt::black; else color = Qt::lightGray; } else { if (item->isMoving()) { color = Qt::gray; } else if (item->isSelected()) { color = MusEGlobal::config.midiItemSelectedColor; // color = Qt::black; } else { color = MusEGlobal::config.midiItemColor; switch(colorMode) { case 0: break; case 1: // pitch { Triple* c = &myColors[event.pitch() % 12]; color.setRgb(c->r, c->g, c->b); } break; case 2: // velocity { int velo = event.velo(); if (velo < 64) color.setRgb(velo*4, 0, 0xff); else color.setRgb(0xff, 0, (127-velo) * 4); } break; } } } bool wmtxen = p.worldMatrixEnabled(); p.setWorldMatrixEnabled(false); const ViewWCoordinate vw1m(1, true); const ViewHCoordinate vh1m(1, true); color.setAlpha(MusEGlobal::config.globalAlphaBlend); QBrush brush(color); p.fillRect(mbr, brush); p.drawRect(mbr); // print note name on the drawn notes if (MusEGlobal::config.showNoteNamesInPianoRoll) { QFont f(MusEGlobal::config.fonts[1]); f.setPointSize(f.pointSize() * 0.85); p.setFont(f); if (color.lightnessF() > 0.6f) { pen.setColor(Qt::black); p.setPen(pen); } else { pen.setColor(Qt::white); p.setPen(pen); } QString noteStr = MusECore::pitch2string(event.pitch()); p.drawText(mbbr_exp,Qt::AlignHCenter|Qt::AlignVCenter, noteStr.toUpper()); } p.setWorldMatrixEnabled(wmtxen); } #endif //--------------------------------------------------------- // drawTopItem //--------------------------------------------------------- void PianoCanvas::drawTopItem(QPainter& , const QRect&, const QRegion&) {} //--------------------------------------------------------- // drawMoving // draws moving items //--------------------------------------------------------- void PianoCanvas::drawMoving(QPainter& p, const CItem* item, const QRect& mr, const QRegion&) { const QRect ur = mapDev(mr); QRect ur_item = QRect(item->mp().x(), item->mp().y() - item->height()/2, item->width(), item->height()); ur_item = ur_item.intersected(ur); if(!ur_item.isValid()) return; QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); p.setBrush(Qt::NoBrush); p.drawRect(ur_item); } //--------------------------------------------------------- // viewMouseDoubleClickEvent //--------------------------------------------------------- void PianoCanvas::viewMouseDoubleClickEvent(QMouseEvent* event) { if ((_tool != MusEGui::PointerTool) && (event->button() != Qt::LeftButton)) { mousePress(event); return; } } //--------------------------------------------------------- // moveCanvasItems //--------------------------------------------------------- MusECore::Undo PianoCanvas::moveCanvasItems(CItemMap& items, int dp, int dx, DragType dtype, bool rasterize) { if(editor->parts()->empty()) return MusECore::Undo(); //return empty list MusECore::PartsToChangeMap parts2change; MusECore::Undo operations; for(MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { MusECore::Part* part = ip->second; if(!part) continue; int npartoffset = 0; for(iCItem ici = items.begin(); ici != items.end(); ++ici) { CItem* ci = ici->second; ci->setMoving(false); if(ci->part() != part) continue; int x = ci->pos().x() + dx; int y = pitch2y(y2pitch(ci->pos().y()) + dp); QPoint newpos = QPoint(x, y); if(rasterize) newpos = raster(newpos); // Test moving the item... NEvent* nevent = (NEvent*) ci; MusECore::Event event = nevent->event(); x = newpos.x(); if(x < 0) x = 0; int ntick = (rasterize ? editor->rasterVal(x) : x) - part->tick(); if(ntick < 0) ntick = 0; int diff = ntick + event.lenTick() - part->lenTick(); // If moving the item would require a new part size... if(diff > npartoffset) npartoffset = diff; } if(npartoffset > 0) { MusECore::iPartToChange ip2c = parts2change.find(part); if(ip2c == parts2change.end()) { MusECore::PartToChange p2c = {0, npartoffset}; parts2change.insert(std::pair (part, p2c)); } else ip2c->second.xdiff = npartoffset; } } bool forbidden=false; for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c) { MusECore::Part* opart = ip2c->first; if (opart->hasHiddenEvents() & MusECore::Part::RightEventsHidden) { forbidden=true; break; } } if (!forbidden) { std::vector< CItem* > doneList; typedef std::vector< CItem* >::iterator iDoneList; for(iCItem ici = items.begin(); ici != items.end(); ++ici) { CItem* ci = ici->second; const QPoint oldpos(ci->pos()); const int x = ci->pos().x(); const int y = ci->pos().y(); int nx = x + dx; int ny = pitch2y(y2pitch(y) + dp); QPoint newpos = QPoint(nx, ny); if(rasterize) newpos = raster(newpos); selectItem(ci, true); iDoneList idl; for(idl = doneList.begin(); idl != doneList.end(); ++idl) // This compares EventBase pointers to see if they're the same... if((*idl)->event() == ci->event()) break; // Do not process if the event has already been processed (meaning it's an event in a clone part)... if (idl == doneList.end()) { moveItem(operations, ci, newpos, dtype, rasterize); // always returns true. if not, change is necessary here! doneList.push_back(ci); } ci->move(newpos); itemReleased(ci, oldpos); if(dtype == MOVE_COPY || dtype == MOVE_CLONE) selectItem(ci, false); } itemsReleased(); for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c) { MusECore::Part* opart = ip2c->first; int diff = ip2c->second.xdiff; schedule_resize_all_same_len_clone_parts(opart, opart->lenTick() + diff, operations); } return operations; } else { return MusECore::Undo(); //return empty list } } //--------------------------------------------------------- // moveItem // called after moving an object //--------------------------------------------------------- bool PianoCanvas::moveItem(MusECore::Undo& operations, CItem* item, const QPoint& pos, DragType dtype, bool rasterize) { NEvent* nevent = (NEvent*) item; MusECore::Event event = nevent->event(); int npitch = y2pitch(pos.y()); event.setSelected(false); MusECore::Event newEvent = (dtype == MOVE_COPY) ? event.duplicate() : event.clone(); newEvent.setSelected(true); int x = pos.x(); if (x < 0) x = 0; MusECore::Part* part = nevent->part(); newEvent.setPitch(npitch); int ntick = (rasterize ? editor->rasterVal(x) : x) - part->tick(); if (ntick < 0) ntick = 0; newEvent.setTick(ntick); newEvent.setLenTick(event.lenTick()); // don't check, whether the new event is within the part // at this place. with operation groups, the part isn't // resized yet. (flo93) if (dtype == MOVE_COPY || dtype == MOVE_CLONE) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, newEvent, part, false, false)); else operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); return true; } //--------------------------------------------------------- // newItem(p, state) //--------------------------------------------------------- CItem* PianoCanvas::newItem(const QPoint& p, int state) { int pitch = y2pitch(p.y()); int tick = p.x(); if(tick < 0) tick = 0; if(!(state & Qt::ShiftModifier)) tick = editor->rasterVal1(tick); int len = p.x() - tick; int eventVelocity = curVelo; // if option is enabled and there was a prior event we clone velocity and length from that event if (MusEGlobal::config.useLastEditedEvent && lastEditedEvent != 0) { // curVelo = lastEditedEvent->velo(); currently not used, last changed velocity overrides it, haven't quite figured out how. len = lastEditedEvent->lenTick(); } tick -= curPart->tick(); if (tick < 0) return 0; MusECore::Event e = MusECore::Event(MusECore::Note); e.setTick(tick); e.setPitch(pitch); e.setVelo(eventVelocity); e.setLenTick(len); NEvent *newEvent = new NEvent(e, curPart, pitch2y(pitch)); if(_playEvents) startPlayEvent(e.pitch(), e.velo()); return newEvent; } void PianoCanvas::newItem(CItem* item, bool noSnap) { NEvent* nevent = (NEvent*) item; MusECore::Event event = nevent->event(); MusECore::Part* part = nevent->part(); int ptick = part->tick(); int x = item->x(); if (xrasterVal1(x); //round down if (xwidth(); event.setTick(x - ptick); if (!noSnap) w = editor->rasterVal(w); if (w == 0) w = editor->rasterStep(ptick); event.setLenTick(w); event.setPitch(y2pitch(item->y())); event.setSelected(true); MusECore::Undo operations; int diff = event.endTick()-part->lenTick(); if (! ((diff > 0) && (part->hasHiddenEvents() & MusECore::Part::RightEventsHidden)) ) //operation is allowed { operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent,event, part, false, false)); if (diff > 0)// part must be extended? { schedule_resize_all_same_len_clone_parts(part, event.endTick(), operations); printf("newItem: extending\n"); } MusEGlobal::song->applyOperationGroup(operations); setLastEdited(event); } else // forbid action by not applying it songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is necessary //to remove "forbidden" events from the list again } //--------------------------------------------------------- // resizeItem //--------------------------------------------------------- void PianoCanvas::resizeItem(CItem* item, bool noSnap, bool rasterize) // experimental changes to try dynamically extending parts { Q_UNUSED(item) Q_UNUSED(rasterize) MusECore::Undo operations; unsigned max_diff_len = 0; MusECore::Part* part; for (auto &it: items) { if (!it.second->isSelected()) continue; part = it.second->part(); QPoint topLeft = QPoint(qMax((unsigned)it.second->x(), part->tick()), it.second->y()); it.second->setTopLeft(raster(topLeft)); NEvent* nevent = (NEvent*) it.second; MusECore::Event event = nevent->event(); MusECore::Event newEvent = event.clone(); int len; if (noSnap) len = nevent->width(); else { unsigned tick = event.tick() + part->tick(); len = editor->rasterVal(tick + nevent->width()) - tick; if (len <= 0) len = editor->raster(); } int diff = event.tick() + len - part->lenTick(); if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT) { int x = qMax(0, nevent->x()); int ntick = qMax(0u, x - part->tick()); newEvent.setTick(ntick); } if (! ((diff > 0) && (part->hasHiddenEvents() & MusECore::Part::RightEventsHidden)) ) //operation is allowed { newEvent.setLenTick(len); operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, nevent->part(), false, false)); if (diff > 0) // part must be extended? max_diff_len = qMax(event.tick() + len, max_diff_len); } setLastEdited(newEvent); } if (max_diff_len > 0) { schedule_resize_all_same_len_clone_parts(part, max_diff_len, operations); printf("resizeItem: extending\n"); } //else forbid action by not performing it MusEGlobal::song->applyOperationGroup(operations); songChanged(SC_EVENT_MODIFIED); // this forces an update of the itemlist, which is necessary // to remove "forbidden" events from the list again } //--------------------------------------------------------- // deleteItem //--------------------------------------------------------- bool PianoCanvas::deleteItem(CItem* item) { NEvent* nevent = (NEvent*) item; if (nevent->part() == curPart) { MusECore::Event ev = nevent->event(); // Indicate do undo, and do not do port controller values and clone parts. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, ev, curPart, false, false)); return true; } return false; } //--------------------------------------------------------- // pianoCmd //--------------------------------------------------------- void PianoCanvas::pianoCmd(int cmd) { switch(cmd) { case CMD_LEFT: { int spos = pos[0]; if(spos > 0) { spos -= 1; // Nudge by -1, then snap down with raster1. spos = MusEGlobal::sigmap.raster1(spos, editor->rasterStep(pos[0])); } if(spos < 0) spos = 0; MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); } break; case CMD_RIGHT: { int spos = MusEGlobal::sigmap.raster2(pos[0] + 1, editor->rasterStep(pos[0])); // Nudge by +1, then snap up with raster2. MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); } break; case CMD_LEFT_NOSNAP: { int spos = pos[0] - editor->rasterStep(pos[0]); if (spos < 0) spos = 0; MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW } break; case CMD_RIGHT_NOSNAP: { MusECore::Pos p(pos[0] + editor->rasterStep(pos[0]), true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW } break; case CMD_INSERT: { if (pos[0] < start() || pos[0] >= end()) break; MusECore::MidiPart* part = (MusECore::MidiPart*)curPart; if (part == 0) break; MusECore::Undo operations; std::list elist; for (MusECore::ciEvent e = part->events().lower_bound(pos[0] - part->tick()); e != part->events().end(); ++e) elist.push_back((MusECore::Event)e->second); for (std::list::iterator i = elist.begin(); i != elist.end(); ++i) { MusECore::Event event = *i; MusECore::Event newEvent = event.clone(); newEvent.setTick(event.tick() + editor->raster());// - part->tick()); DELETETHIS // Do not do port controller values and clone parts. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); } MusEGlobal::song->applyOperationGroup(operations); MusECore::Pos p(editor->rasterVal(pos[0] + editor->rasterStep(pos[0])), true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, false, true); } return; case CMD_BACKSPACE: if (pos[0] < start() || pos[0] >= end()) break; { MusECore::MidiPart* part = (MusECore::MidiPart*)curPart; if (part == 0) break; MusECore::Undo operations; std::list elist; for (MusECore::ciEvent e = part->events().lower_bound(pos[0]); e != part->events().end(); ++e) elist.push_back((MusECore::Event)e->second); for (std::list::iterator i = elist.begin(); i != elist.end(); ++i) { MusECore::Event event = *i; MusECore::Event newEvent = event.clone(); newEvent.setTick(event.tick() - editor->raster() - part->tick()); // Do not do port controller values and clone parts. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); } MusEGlobal::song->applyOperationGroup(operations); MusECore::Pos p(editor->rasterVal(pos[0] - editor->rasterStep(pos[0])), true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, false, true); } break; } } //--------------------------------------------------------- // pianoPressed //--------------------------------------------------------- void PianoCanvas::pianoPressed(int pitch, int velocity, bool shift) { // REMOVE Tim. Noteoff. Added. Zero note on vel is not allowed now. if(velocity > 127) velocity = 127; else if(velocity <= 0) velocity = 1; // Stop all notes. stopPlayEvents(); // play note: if(_playEvents) { startPlayEvent(pitch, velocity); } if (_steprec && curPart) // && pos[0] >= start_tick && pos[0] < end_tick [removed by flo93: this is handled in steprec->record] steprec->record(curPart,pitch,editor->raster(),editor->raster(),velocity,MusEGlobal::globalKeyState&Qt::ControlModifier,shift, -1 /* anything which is != rcSteprecNote */); } //--------------------------------------------------------- // pianoReleased //--------------------------------------------------------- void PianoCanvas::pianoReleased(int /*pitch*/, bool) { // release key: if(_playEvents) stopPlayEvents(); } // NOTE Keep this for now in case we can get it to work... #if 0 //--------------------------------------------------------- // draw //--------------------------------------------------------- void PianoCanvas::drawCanvas(QPainter& p, const QRect& mr, const QRegion& mrg) { const ViewRect vr(mr, true); const ViewXCoordinate& vx = vr._x; const ViewYCoordinate& vy = vr._y; const ViewWCoordinate& vw = vr._width; const ViewHCoordinate& vh = vr._height; const ViewYCoordinate vy_2 = mathYCoordinates(vy, vh, MathAdd); const ViewHCoordinate vkh(KH, false); const ViewYCoordinate vy0(0, false); const ViewYCoordinate vy3(3, false); const ViewHCoordinate vh1(1, false); const ViewHCoordinate vh3(3, false); const ViewHCoordinate vh6(6, false); const ViewHCoordinate vh7(7, false); // const ViewHCoordinate vh_m1(-1, false); // const ViewHCoordinate vh_75(75, false); const ViewYCoordinate vy75(75, false); int mx = mr.x(); // int y = mr.y(); int mw = mr.width(); // int h = mr.height(); QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); bool wmtxen = p.worldMatrixEnabled(); p.setWorldMatrixEnabled(false); //--------------------------------------------------- // horizontal lines //--------------------------------------------------- // int yy = ((y-1) / KH) * KH + KH; ViewYCoordinate vyy = mathYCoordinates(vy, vh1, MathSubtract); // For testing... fprintf(stderr, "PianoCanvas::drawCanvas: A vkh:%d map:%d vyy:%d map:%d\n", vkh._value, vkh.isMapped(), vyy._value, vyy.isMapped()); mathRefYCoordinates(vyy, vkh, MathDivide); fprintf(stderr, "PianoCanvas::drawCanvas: B vyy:%d map:%d\n", vyy._value, vyy.isMapped()); mathRefYCoordinates(vyy, vkh, MathMultiply); fprintf(stderr, "PianoCanvas::drawCanvas: C vyy:%d map:%d\n", vyy._value, vyy.isMapped()); mathRefYCoordinates(vyy, vkh, MathAdd); fprintf(stderr, "PianoCanvas::drawCanvas: D vyy:%d map:%d\n", vyy._value, vyy.isMapped()); // int key = 75 - (yy / KH); const ViewYCoordinate vkeyy = mathYCoordinates(vyy, vkh, MathDivide); const ViewHCoordinate vkeyh(vkeyy._value, vkeyy.isMapped()); ViewYCoordinate vkey = mathYCoordinates(vy75, vkeyh, MathSubtract); // for (; yy < y + h; yy += KH) { // switch (key % 7) { // case 0: // case 3: // For testing... // fprintf(stderr, "PianoCanvas::drawCanvas: Drawing horizontal line at x:%d yy:%d x + w:%d yy:%d\n", x, yy, x + w, yy); // // p.drawLine(x, yy, x + w, yy); // break; // default: // For testing... // fprintf(stderr, "PianoCanvas::drawCanvas: Filling rectangle at x:%d yy - 3:%d w:%d h:%d\n", x, yy - 3, w, 6); // // p.fillRect(x, yy-3, w, 6, MusEGlobal::config.midiCanvasBg.darker(110)); // break; // } // --key; const int myy = asIntMapped(vyy); // For testing... fprintf(stderr, "PianoCanvas::drawCanvas: x:%d vy:%d map:%d vyy:%d map:%d mx + mw:%d vy_2:%d map:%d\n", mx, vy._value, vy.isMapped(), vyy._value, vyy.isMapped(), mx + mw, vy_2._value, vy_2.isMapped()); for (; compareYCoordinates(vyy, vy_2, CompareLess); mathRefYCoordinates(vyy, vkh, MathAdd)) { ViewYCoordinate vkey_md7 = mathYCoordinates(vkey, vh7, MathModulo); // For testing... fprintf(stderr, "...Comparing y coordinates vkey_md7:%d vy0:%d vy3:%d\n", vkey_md7._value, vy0._value, vy3._value); if(compareYCoordinates(vkey_md7, vy0, CompareEqual) || compareYCoordinates(vkey_md7, vy3, CompareEqual)) { // For testing... fprintf(stderr, "...Drawing horizontal line at x:%d myy:%d x + w:%d myy:%d\n", mx, myy, mx + mw, myy); p.drawLine(mx, myy, mx + mw, myy); } else { const ViewRect vfr(vx, mathYCoordinates(vyy, vh3, MathSubtract), vw, vh6); // For testing... fprintf(stderr, "...Filling rectangle at x:%d myy - 3:%d w:%d h:%d\n", mx, myy - 3, mw, 6); p.fillRect(asQRectMapped(vfr), MusEGlobal::config.midiCanvasBg.darker(110)); } mathRefYCoordinates(vkey, vh1, MathSubtract); } p.setWorldMatrixEnabled(wmtxen); //--------------------------------------------------- // vertical lines //--------------------------------------------------- drawTickRaster(p, mr, mrg, editor->raster(), false, false, false, MusEGlobal::config.midiCanvasBarColor, MusEGlobal::config.midiCanvasBeatColor); } #endif //--------------------------------------------------------- // draw //--------------------------------------------------------- void PianoCanvas::drawCanvas(QPainter& p, const QRect& mr, const QRegion& rg) { const int pianoHeight = 91 * 10 + KH * 5 + 1; QRect ur = mapDev(mr); if (ur.height() > pianoHeight) ur.setHeight(pianoHeight); // FIXME: For some reason need the expansion otherwise drawing // artifacts (incomplete drawing). Can't figure out why. ur.adjust(0, -4, 0, 4); int ux = ur.x(); if(ux < 0) ux = 0; const int uy = ur.y(); const int uw = ur.width(); const int uh = ur.height(); const int ux_2 = ux + uw; const int uy_2 = uy + uh; QPen pen; pen.setCosmetic(true); pen.setColor(MusEGlobal::config.midiDividerColor); // pen.setColor(Qt::black); p.setPen(pen); //--------------------------------------------------- // horizontal lines //--------------------------------------------------- int uyy = ((uy-1) / KH) * KH + KH; int key = 75 - (uyy / KH); // For testing... // fprintf(stderr, "PianoCanvas::drawCanvas: x:%d y:%d yy:%d x + w:%d y + h:%d\n", x, y, yy, x + w, y + h); for (; uyy < uy_2; uyy += KH) { switch (key % 7) { case 0: case 3: // For testing... // fprintf(stderr, "...Drawing horizontal line at x:%d yy:%d x + w:%d yy:%d\n", x, yy, x + w, yy); if (MusEGlobal::config.canvasShowGrid || MusEGlobal::config.canvasShowGridHorizontalAlways) p.drawLine(ux, uyy, ux_2, uyy); break; default: // For testing... // fprintf(stderr, "...Filling rectangle at x:%d yy - 3:%d w:%d h:%d\n", x, yy - 3, w, 6); p.fillRect(ux, uyy-3, uw, 6, MusEGlobal::config.midiCanvasBg.darker(110)); break; } --key; } if (MusEGlobal::config.canvasShowGrid) { //--------------------------------------------------- // vertical lines //--------------------------------------------------- drawTickRaster(p, mr, rg, editor->raster(), false, false, false, MusEGlobal::config.midiCanvasBeatColor, MusEGlobal::config.midiCanvasBeatColor, MusEGlobal::config.midiCanvasFineColor, MusEGlobal::config.midiCanvasBarColor); } } //--------------------------------------------------------- // cmd // pulldown menu commands //--------------------------------------------------------- void PianoCanvas::cmd(int cmd) { switch (cmd) { case CMD_SELECT_ALL: // select all for (iCItem k = items.begin(); k != items.end(); ++k) { if (!k->second->isSelected()) selectItem(k->second, true); } break; case CMD_SELECT_NONE: // select none deselectAll(); break; case CMD_SELECT_INVERT: // invert selection for (iCItem k = items.begin(); k != items.end(); ++k) { selectItem(k->second, !k->second->isSelected()); } break; case CMD_SELECT_ILOOP: // select inside loop for (iCItem k = items.begin(); k != items.end(); ++k) { NEvent* nevent = (NEvent*)(k->second); MusECore::Part* part = nevent->part(); MusECore::Event event = nevent->event(); unsigned tick = event.tick() + part->tick(); if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos()) selectItem(k->second, false); else selectItem(k->second, true); } break; case CMD_SELECT_OLOOP: // select outside loop for (iCItem k = items.begin(); k != items.end(); ++k) { NEvent* nevent = (NEvent*)(k->second); MusECore::Part* part = nevent->part(); MusECore::Event event = nevent->event(); unsigned tick = event.tick() + part->tick(); if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos()) selectItem(k->second, true); else selectItem(k->second, false); } break; case CMD_RANGE_TO_SELECTION: setRangeToSelection(); break; case CMD_SELECT_PREV_PART: // select previous part { MusECore::Part* pt = editor->curCanvasPart(); MusECore::Part* newpt = pt; MusECore::PartList* pl = editor->parts(); for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) if(ip->second == pt) { if(ip == pl->begin()) ip = pl->end(); --ip; newpt = ip->second; break; } if(newpt != pt) editor->setCurCanvasPart(newpt); } break; case CMD_SELECT_NEXT_PART: // select next part { MusECore::Part* pt = editor->curCanvasPart(); MusECore::Part* newpt = pt; MusECore::PartList* pl = editor->parts(); for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) if(ip->second == pt) { ++ip; if(ip == pl->end()) ip = pl->begin(); newpt = ip->second; break; } if(newpt != pt) editor->setCurCanvasPart(newpt); } break; case CMD_FIXED_LEN: case CMD_CRESCENDO: case CMD_TRANSPOSE: case CMD_THIN_OUT: case CMD_ERASE_EVENT: case CMD_NOTE_SHIFT: case CMD_MOVE_CLOCK: case CMD_COPY_MEASURE: case CMD_ERASE_MEASURE: case CMD_DELETE_MEASURE: case CMD_CREATE_MEASURE: break; default: // printf("unknown ecanvas cmd %d\n", cmd); break; } itemSelectionsChanged(); redraw(); } //--------------------------------------------------------- // midiNote //--------------------------------------------------------- void PianoCanvas::midiNote(int pitch, int velo) { if (MusEGlobal::debugMsg) printf("PianoCanvas::midiNote: pitch=%i, velo=%i\n", pitch, velo); if (velo) noteHeldDown[pitch]=true; else noteHeldDown[pitch]=false; if (MusEGlobal::heavyDebugMsg) { printf(" held down notes are: "); for (int i=0;i<128;i++) if (noteHeldDown[i]) printf("%i ",i); printf("\n"); } if (_midiin && _steprec && curPart && !MusEGlobal::audio->isPlaying() && velo && pos[0] >= start_tick /* && pos[0] < end_tick [removed by flo93: this is handled in steprec->record] */ && !(MusEGlobal::globalKeyState & Qt::AltModifier)) { steprec->record(curPart,pitch,editor->raster(),editor->raster(),velo,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier); } } //--------------------------------------------------------- // startDrag //--------------------------------------------------------- void PianoCanvas::startDrag(CItem* /* item*/, DragType t) { QMimeData* md = selected_events_to_mime(partlist_to_set(editor->parts()), 1); if (md) { // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can // clean up after the drag and drop operation has been completed. " QDrag* drag = new QDrag(this); drag->setMimeData(md); if (t == MOVE_COPY || t == MOVE_CLONE) drag->exec(Qt::CopyAction); else drag->exec(Qt::MoveAction); } } //--------------------------------------------------------- // dragEnterEvent //--------------------------------------------------------- void PianoCanvas::dragEnterEvent(QDragEnterEvent* event) { //event->accept(Q3TextDrag::canDecode(event)); event->acceptProposedAction(); // TODO CHECK Tim. } //--------------------------------------------------------- // dragMoveEvent //--------------------------------------------------------- void PianoCanvas::dragMoveEvent(QDragMoveEvent*) { } //--------------------------------------------------------- // dragLeaveEvent //--------------------------------------------------------- void PianoCanvas::dragLeaveEvent(QDragLeaveEvent*) { } //--------------------------------------------------------- // itemPressed //--------------------------------------------------------- void PianoCanvas::itemPressed(const CItem* item) { if (!_playEvents) return; MusECore::Event e = ((NEvent*)item)->event(); startPlayEvent(e.pitch(), e.velo()); } //--------------------------------------------------------- // itemReleased //--------------------------------------------------------- void PianoCanvas::itemReleased(const CItem* item, const QPoint&) { if(!track()) { // Stop all playing notes: stopPlayEvents(); return; } const int opitch = y2pitch(item->mp().y()); const int port = track()->outPort(); const int channel = track()->outChannel(); // Stop any playing note: stopStuckNote(port, channel, opitch); } //--------------------------------------------------------- // itemMoving //--------------------------------------------------------- void PianoCanvas::itemMoving(const CItem* item, const QPoint& newMP) { if(!track()) { // Stop all playing notes: stopPlayEvents(); return; } const int opitch = y2pitch(item->mp().y()); const int npitch = y2pitch(newMP.y()); if(opitch == npitch) return; const int port = track()->outPort(); const int channel = track()->outChannel(); // Stop any playing note: stopStuckNote(port, channel, opitch); } //--------------------------------------------------------- // itemMoved //--------------------------------------------------------- void PianoCanvas::itemMoved(const CItem* item, const QPoint& oldMP) { const int opitch = y2pitch(oldMP.y()); const int npitch = y2pitch(item->mp().y()); if(opitch == npitch) return; if(_playEvents) { if((_playEventsMode == PlayEventsSingleNote && item == curItem) || (_playEventsMode == PlayEventsChords && curItem && curItem->mp().x() == item->mp().x())) { const MusECore::Event e = ((NEvent*)item)->event(); // play note: startPlayEvent(npitch, e.velo()); } } } //--------------------------------------------------------- // curPartChanged //--------------------------------------------------------- void PianoCanvas::curPartChanged() { EventCanvas::curPartChanged(); editor->setWindowTitle(getCaption()); } //--------------------------------------------------------- // modifySelected //--------------------------------------------------------- void PianoCanvas::modifySelected(MusEGui::NoteInfo::ValType type, int val, bool delta_mode) { QList< QPair > already_done; MusECore::Undo operations; for (iCItem i = items.begin(); i != items.end(); ++i) { if (!(i->second->isSelected())) continue; NEvent* e = (NEvent*)(i->second); MusECore::Event event = e->event(); if (event.type() != MusECore::Note) continue; MusECore::MidiPart* part = (MusECore::MidiPart*)(e->part()); if (already_done.contains(QPair(part->clonemaster_sn(), event))) continue; MusECore::Event newEvent = event.clone(); switch (type) { case MusEGui::NoteInfo::VAL_TIME: { int newTime = val; if(delta_mode) newTime += event.tick(); else newTime -= part->tick(); if (newTime < 0) newTime = 0; newEvent.setTick(newTime); } break; case MusEGui::NoteInfo::VAL_LEN: { int len = val; if(delta_mode) len += event.lenTick(); if (len < 1) len = 1; newEvent.setLenTick(len); } break; case MusEGui::NoteInfo::VAL_VELON: { int velo = val; if(delta_mode) velo += event.velo(); if (velo > 127) velo = 127; else if (velo < 0) // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now. // velo = 0; velo = 1; newEvent.setVelo(velo); } break; case MusEGui::NoteInfo::VAL_VELOFF: { int velo = val; if(delta_mode) velo += event.veloOff(); if (velo > 127) velo = 127; else if (velo < 0) velo = 0; newEvent.setVeloOff(velo); } break; case MusEGui::NoteInfo::VAL_PITCH: { int pitch = val; if(delta_mode) pitch += event.pitch(); if (pitch > 127) pitch = 127; else if (pitch < 0) pitch = 0; newEvent.setPitch(pitch); } break; } operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); already_done.append(QPair(part->clonemaster_sn(), event)); //setLastEdited(newEvent); } MusEGlobal::song->applyOperationGroup(operations); } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void PianoCanvas::resizeEvent(QResizeEvent* ev) { if (ev->size().width() != ev->oldSize().width()) emit newWidth(ev->size().width()); EventCanvas::resizeEvent(ev); } //--------------------------------------------------------- // mouseMove (override) //--------------------------------------------------------- void PianoCanvas::mouseMove(QMouseEvent* event) { EventCanvas::mouseMove(event); if (MusEGlobal::config.showNoteTooltips) showNoteTooltip(event); if (MusEGlobal::config.showStatusBar) showStatusTip(event); } //--------------------------------------------------------- // genItemPopup (override) //--------------------------------------------------------- QMenu* PianoCanvas::genItemPopup(MusEGui::CItem* item) { // no context menu available, use for single item selection deselectAll(); item->setSelected(true); return nullptr; } //--------------------------------------------------------- // setColorMode //--------------------------------------------------------- void PianoCanvas::setColorMode(MidiEventColorMode mode) { colorMode = mode; redraw(); } void PianoCanvas::showNoteTooltip(QMouseEvent* event) { static CItem* hoverItem = nullptr; if (_tool & (MusEGui::PointerTool | MusEGui::PencilTool | MusEGui::RubberTool)) { QString str; CItem* item = findCurrentItem(event->pos()); if (item) { if (hoverItem == item) return; hoverItem = item; int pitch = item->event().pitch(); MusECore::Pos start(item->event().tick() + item->part()->tick()); int bar, beat, tick, hour, min, sec, msec; start.mbt(&bar, &beat, &tick); QString str_bar = QString("%1.%2.%3") .arg(bar + 1, 4, 10, QLatin1Char('0')) .arg(beat + 1, 2, 10, QLatin1Char('0')) .arg(tick, 3, 10, QLatin1Char('0')); start.msmu(&hour, &min, &sec, &msec, nullptr); QString str_time = QString("%1:%2:%3.%4") .arg(hour, 2, 10, QLatin1Char('0')) .arg(min, 2, 10, QLatin1Char('0')) .arg(sec, 2, 10, QLatin1Char('0')) .arg(msec, 3, 10, QLatin1Char('0')); str = tr("Note: ") + MusECore::pitch2string(pitch) + " (" + QString::number(pitch) + ")\n" + tr("Velocity: ") + QString::number(item->event().velo()) + "\n" + tr("Start (bar): ") + str_bar + "\n" + tr("Start (time): ") + str_time + "\n" + tr("Length (ticks): ") + QString::number(item->event().lenTick()); } else { hoverItem = nullptr; int pitch = y2pitch(event->pos().y()); str = MusECore::pitch2string(pitch) + " (" + QString::number(pitch) + ")"; } QToolTip::showText(QPoint(event->globalX(), event->globalY() + 20), str); } } void PianoCanvas::showStatusTip(QMouseEvent* event) { static CItem* hoverItem = nullptr; static Tool localTool; CItem* item = findCurrentItem(event->pos()); if (item) { if (hoverItem == item && localTool == _tool) return; hoverItem = item; localTool = _tool; QString s; if (_tool & (MusEGui::PointerTool )) s = tr("LMB: Select/Move | CTRL+LMB: Multi select/Move© | SHIFT+LMB: Select pitch | MMB: Delete | CTRL+RMB: Trim length"); else if (_tool & (MusEGui::PencilTool)) s = tr("LMB: Resize | CTRL+LMB: Multi select | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length"); else if (_tool & (MusEGui::RubberTool)) s = tr("LMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length"); if (!s.isEmpty()) MusEGlobal::muse->setStatusBarText(s); } else { if (hoverItem != nullptr) { MusEGlobal::muse->clearStatusBarText(); hoverItem = nullptr; } } } } // namespace MusEGui muse-4.0.0/src/muse/midiedit/prcanvas.h000066400000000000000000000125671404105311000200020ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: prcanvas.h,v 1.5.2.6 2009/11/16 11:29:33 lunar_shuttle Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PRCANVAS_H__ #define __PRCANVAS_H__ #include "ecanvas.h" #include #include #include #include "noteinfo.h" #include "globaldefs.h" // Forward declarations: class QDragEnterEvent; class QDropEvent; class QMouseEvent; class QDragMoveEvent; class QDragLeaveEvent; class QResizeEvent; class QPainter; class QWidget; namespace MusECore { class StepRec; class Part; class Event; class CItem; class CItemMap; } namespace MusEGui { class PianoRoll; class MidiEditor; //--------------------------------------------------------- // NEvent // ''visual'' Note Event //--------------------------------------------------------- class NEvent : public EItem { public: NEvent(const MusECore::Event& e, MusECore::Part* p, int y); }; //--------------------------------------------------------- // PianoCanvas //--------------------------------------------------------- class PianoCanvas : public EventCanvas { Q_OBJECT MidiEventColorMode colorMode; bool noteHeldDown[128]; MusECore::StepRec* steprec; MusECore::Event* lastEditedEvent = 0; virtual void viewMouseDoubleClickEvent(QMouseEvent*) override; virtual void drawItem(QPainter&, const CItem*, const QRect&, const QRegion& = QRegion()) override; void drawTopItem(QPainter& p, const QRect& rect, const QRegion& = QRegion()) override; virtual void drawMoving(QPainter&, const CItem*, const QRect&, const QRegion& = QRegion()) override; virtual MusECore::Undo moveCanvasItems(CItemMap&, int, int, DragType, bool rasterize = true) override; virtual bool moveItem(MusECore::Undo&, CItem*, const QPoint&, DragType, bool rasterize = true) override; virtual CItem* newItem(const QPoint&, int) override; virtual void resizeItem(CItem*, bool noSnap, bool) override; virtual void newItem(CItem*, bool noSnap) override; virtual bool deleteItem(CItem*) override; virtual void startDrag(CItem* item, DragType t) override; virtual void dragEnterEvent(QDragEnterEvent* event) override; virtual void dragMoveEvent(QDragMoveEvent*) override; virtual void dragLeaveEvent(QDragLeaveEvent*) override; virtual CItem* addItem(MusECore::Part*, const MusECore::Event&) override; int y2pitch(int) const override; int pitch2y(int) const override; inline int y2height(int) const override { return KH/2; } inline int yItemOffset() const override { return KH/4; } virtual void drawCanvas(QPainter&, const QRect&, const QRegion& = QRegion()) override; virtual void itemPressed(const CItem*) override; virtual void itemReleased(const CItem*, const QPoint&) override; virtual void itemMoving(const CItem*, const QPoint& newMP) override; virtual void itemMoved(const CItem*, const QPoint& oldMP) override; virtual void curPartChanged() override; virtual void resizeEvent(QResizeEvent*) override; void mouseMove(QMouseEvent* event) override; QMenu* genItemPopup(MusEGui::CItem* item) override; void showNoteTooltip(QMouseEvent* event); void showStatusTip(QMouseEvent *event); void setLastEdited(MusECore::Event& e); private slots: void midiNote(int pitch, int velo); signals: void quantChanged(int); void rasterChanged(int); void newWidth(int); public slots: void pianoCmd(int); void pianoPressed(int pitch, int velocity, bool shift); void pianoReleased(int pitch, bool); public: enum { CMD_CUT, CMD_COPY, CMD_COPY_RANGE, CMD_PASTE, CMD_PASTE_TO_CUR_PART, CMD_PASTE_DIALOG, CMD_DEL, CMD_QUANTIZE, CMD_SELECT_ALL, CMD_SELECT_NONE, CMD_SELECT_INVERT, CMD_SELECT_ILOOP, CMD_SELECT_OLOOP, CMD_SELECT_PREV_PART, CMD_SELECT_NEXT_PART, CMD_MODIFY_GATE_TIME, CMD_MODIFY_VELOCITY, CMD_CRESCENDO, CMD_TRANSPOSE, CMD_THIN_OUT, CMD_ERASE_EVENT, CMD_NOTE_SHIFT, CMD_MOVE_CLOCK, CMD_COPY_MEASURE, CMD_ERASE_MEASURE, CMD_DELETE_MEASURE, CMD_CREATE_MEASURE, CMD_FIXED_LEN, CMD_DELETE_OVERLAPS, CMD_LEGATO, CMD_RANGE_TO_SELECTION }; PianoCanvas(MidiEditor*, QWidget*, int, int); virtual ~PianoCanvas(); void cmd(int cmd); void setColorMode(MidiEventColorMode mode); virtual void modifySelected(NoteInfo::ValType type, int val, bool delta_mode = true) override; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/midiedit/scoreedit.cpp000066400000000000000000005555621404105311000205100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // scoreedit.cpp // (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "muse_math.h" #include #include using namespace std; #include "app.h" #include "xml.h" #include "sig.h" #include "scoreedit.h" #include "ttoolbar.h" #include "tb1.h" #include "globals.h" #include "gconfig.h" #include "icons.h" #include "audio.h" #include "functions.h" #include "helper.h" #include "cmd.h" #include "song.h" #include "shortcuts.h" #include "menutitleitem.h" // Forwards from header: #include #include #include #include #include #include #include #include #include #include #include #include "part.h" #include "mtscale_flo.h" #include "steprec.h" #include "spinbox.h" #include "tools.h" using MusEGlobal::debugMsg; using MusEGlobal::heavyDebugMsg; using MusECore::UndoOp; using MusECore::Undo; namespace MusEGui { string IntToStr(int i); QString IntToQStr(int i); #define SPLIT_NOTE 60 //PIXELS_PER_NOTEPOS must be greater or equal to 3*NOTE_XLEN + 2*NOTE_SHIFT //because if tick 0 is at x=0: the notes can be shifted by NOTE_SHIFT. //additionally, they can be moved by NOTE_XLEN (collision avoiding) //then, they have their own width, which is NOTE_XLEN/2 into the x>0-area //the same thing applies to the x<0-area // OOO // | // ^actual calculated x, without shifting or moving // ^ and // ^ : moved note (by XLEN) // additionally, a shift is possible // total_width = shift + move + note_xlen + move + shift, where move==note_xlen // total_width = 2*shift + 3*note_xlen // if total_width is greater than px_per_notepos, there will be collisions! //do NOT put parentheses around this! and always right-multiply it, //that is: foo * PAGESTEP, never PAGESTEP * foo! #define PAGESTEP 3/4 #define SCROLL_MARGIN 10 #define SCROLL_SPEED 5 //SCROLL_SPEED is in (scroll_pixels per second) per mouse-move-pixel #define SCROLL_SPEED_MAX 500 //SCROLL_SPEED_MAX is in scroll_pixels_per_second #define AKKOLADE_WIDTH 8 #define AKKOLADE_LEFTMARGIN 0 #define AKKOLADE_RIGHTMARGIN 2 #define STAFF_DISTANCE (10*YLEN) #define GRANDSTAFF_DISTANCE (8*YLEN) #define NOTE_YDIST 20 //NOTE_YDIST is the number of pixels which are between two notes //which exceed their staves' y-boundaries, so that these boundaries //must be expanded. #define MAX_QUANT_POWER 5 QString create_random_string(int len=8) { string result; for (int i=0;i ScoreEdit::names; int ScoreCanvas::_quant_power2_init=3; int ScoreCanvas::_pixels_per_whole_init=300; int ScoreCanvas::note_velo_init=64; int ScoreCanvas::note_velo_off_init=64; int ScoreCanvas::new_len_init=0; ScoreCanvas::coloring_mode_t ScoreCanvas::coloring_mode_init=COLOR_MODE_BLACK; bool ScoreCanvas::preamble_contains_timesig_init=true; bool ScoreCanvas::preamble_contains_keysig_init=true; //--------------------------------------------------------- // ScoreEdit //--------------------------------------------------------- ScoreEdit::ScoreEdit(QWidget* parent, const char* name, unsigned initPos) : TopWin(TopWin::SCORE, parent, name) { //setAttribute(Qt::WA_DeleteOnClose); setFocusPolicy(Qt::NoFocus); mainw = new QWidget(this); mainGrid = new QGridLayout(); mainw->setLayout(mainGrid); mainGrid->setContentsMargins(0, 0, 0, 0); mainGrid->setSpacing(0); setCentralWidget(mainw); apply_velo=false; score_canvas=new ScoreCanvas(this, mainw); xscroll = new QScrollBar(Qt::Horizontal, mainw); yscroll = new QScrollBar(Qt::Vertical, mainw); time_bar = new MusEGui::MTScaleFlo(score_canvas, mainw); connect(xscroll, SIGNAL(valueChanged(int)), score_canvas, SLOT(x_scroll_event(int))); connect(score_canvas, SIGNAL(xscroll_changed(int)), xscroll, SLOT(setValue(int))); connect(yscroll, SIGNAL(valueChanged(int)), score_canvas, SLOT(y_scroll_event(int))); connect(score_canvas, SIGNAL(yscroll_changed(int)), yscroll, SLOT(setValue(int))); connect(score_canvas, SIGNAL(canvas_width_changed(int)), SLOT(canvas_width_changed(int))); connect(score_canvas, SIGNAL(viewport_width_changed(int)), SLOT(viewport_width_changed(int))); connect(score_canvas, SIGNAL(canvas_height_changed(int)), SLOT(canvas_height_changed(int))); connect(score_canvas, SIGNAL(viewport_height_changed(int)), SLOT(viewport_height_changed(int))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), score_canvas, SLOT(song_changed(MusECore::SongChangedStruct_t))); connect(xscroll, SIGNAL(valueChanged(int)), time_bar, SLOT(set_xpos(int))); connect(score_canvas, SIGNAL(pos_add_changed()), time_bar, SLOT(pos_add_changed())); connect(score_canvas, SIGNAL(preamble_width_changed(int)), time_bar, SLOT(set_xoffset(int))); mainGrid->addWidget(time_bar, 0,0); mainGrid->addWidget(score_canvas, 1,0); mainGrid->addWidget(xscroll,2,0); mainGrid->addWidget(yscroll,1,1); xscroll->setMinimum(0); yscroll->setMinimum(0); xscroll->setValue(0); yscroll->setValue(0); // Toolbars --------------------------------------------------------- // NOTICE: Please ensure that any tool bar object names here match the names assigned // to identical or similar toolbars in class MusE or other TopWin classes. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). addToolBarBreak(); edit_tools = new MusEGui::EditToolBar(this, scoreTools); addToolBar(edit_tools); edit_tools->set(MusEGui::PointerTool); score_canvas->set_tool(MusEGui::PointerTool); connect(edit_tools, SIGNAL(toolChanged(int)), score_canvas, SLOT(set_tool(int))); connect(MusEGlobal::muse, &MusE::configChanged, edit_tools, &EditToolBar::configChanged); QToolBar* steprec_tools=addToolBar(tr("Step recording tools")); steprec_tools->setObjectName("Score tools"); srec = new QToolButton(); srec->setToolTip(tr("Step record")); srec->setIcon(*steprecSVGIcon); srec->setCheckable(true); srec->setFocusPolicy(Qt::NoFocus); steprec_tools->addWidget(srec); connect(srec, SIGNAL(toggled(bool)), score_canvas, SLOT(set_steprec(bool))); QToolBar* quant_toolbar = addToolBar(tr("Quantisation settings")); quant_toolbar->setObjectName("Score quantisation toolbar"); quant_toolbar->addWidget(new QLabel(tr("Quantisation"), quant_toolbar)); quant_combobox = new QComboBox(this); quant_combobox->addItem("2"); // if you add or remove items from quant_combobox->addItem("4"); // here, also change all code regarding quant_combobox->addItem("8"); // _quant_power2 and _quant_power2_init quant_combobox->addItem("16"); // and MAX_QUANT_POWER (must be log2(largest_value)) quant_combobox->addItem("32"); quant_combobox->setFocusPolicy(Qt::TabFocus); quant_combobox->setCurrentIndex(score_canvas->quant_power2()-1); // the above is intendedly executed BEFORE connecting. otherwise this would // destroy pixels_per_whole_init! //connect(quant_combobox, SIGNAL(currentIndexChanged(int)), score_canvas, SLOT(set_quant(int))); connect(quant_combobox, SIGNAL(activated(int)), SLOT(quant_combobox_changed(int))); // Tim quant_toolbar->addWidget(quant_combobox); QLabel* label = new QLabel(tr("Pixels per whole")); label->setIndent(3); quant_toolbar->addWidget(label); px_per_whole_spinbox = new SpinBox(this); px_per_whole_spinbox->setFocusPolicy(Qt::StrongFocus); px_per_whole_spinbox->setRange(10, 1200); px_per_whole_spinbox->setSingleStep(50); connect(px_per_whole_spinbox, SIGNAL(valueChanged(int)), score_canvas, SLOT(set_pixels_per_whole(int))); connect(score_canvas, SIGNAL(pixels_per_whole_changed(int)), px_per_whole_spinbox, SLOT(setValue(int))); connect(px_per_whole_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(px_per_whole_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas())); quant_toolbar->addWidget(px_per_whole_spinbox); px_per_whole_spinbox->setValue(ScoreCanvas::_pixels_per_whole_init); addToolBarBreak(); QToolBar* note_settings_toolbar = addToolBar(tr("Note settings")); //don't change that name, or you will lose toolbar settings note_settings_toolbar->setObjectName("New note settings"); note_settings_toolbar->addWidget(new QLabel(tr("Note length"), note_settings_toolbar)); len_actions=new QActionGroup(this); n1_action = note_settings_toolbar->addAction("1"); n2_action = note_settings_toolbar->addAction("2"); n4_action = note_settings_toolbar->addAction("4"); n8_action = note_settings_toolbar->addAction("8"); n16_action = note_settings_toolbar->addAction("16"); n32_action = note_settings_toolbar->addAction("32"); nlast_action = note_settings_toolbar->addAction(tr("last")); connect(n1_action, &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_1); } ); connect(n2_action, &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_2); } ); connect(n4_action, &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_4); } ); connect(n8_action, &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_8); } ); connect(n16_action, &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_16); } ); connect(n32_action, &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_32); } ); connect(nlast_action, &QAction::triggered, [this]() { menu_command(CMD_NOTELEN_LAST); } ); n1_action->setCheckable(true); n2_action->setCheckable(true); n4_action->setCheckable(true); n8_action->setCheckable(true); n16_action->setCheckable(true); n32_action->setCheckable(true); nlast_action->setCheckable(true); len_actions->addAction(n1_action); len_actions->addAction(n2_action); len_actions->addAction(n4_action); len_actions->addAction(n8_action); len_actions->addAction(n16_action); len_actions->addAction(n32_action); len_actions->addAction(nlast_action); switch (ScoreCanvas::new_len_init) { case 0: nlast_action->setChecked(true); menu_command(CMD_NOTELEN_LAST); break; case 1: n1_action->setChecked(true); menu_command(CMD_NOTELEN_1); break; case 2: n2_action->setChecked(true); menu_command(CMD_NOTELEN_2); break; case 4: n4_action->setChecked(true); menu_command(CMD_NOTELEN_4); break; case 8: n8_action->setChecked(true); menu_command(CMD_NOTELEN_8); break; case 16: n16_action->setChecked(true); menu_command(CMD_NOTELEN_16); break; case 32: n32_action->setChecked(true); menu_command(CMD_NOTELEN_32); break; default: cerr << "ERROR: THIS SHOULD NEVER HAPPEN. newLen is invalid in ScoreEdit::ScoreEdit.\n" << " (newLen="<setChecked(true); menu_command(CMD_NOTELEN_LAST); } label = new QLabel(tr("Velocity")); label->setIndent(3); note_settings_toolbar->addWidget(label); velo_spinbox = new SpinBox(this); velo_spinbox->setRange(0, 127); velo_spinbox->setSingleStep(1); velo_spinbox->setToolTip(tr("Apply to selected notes, or new notes if none is selected")); //we do not use the valueChanged signal, because that would generate //many many undos when using the spin buttons. connect(velo_spinbox, SIGNAL(editingFinished()), SLOT(velo_box_changed())); connect(this,SIGNAL(velo_changed(int)), score_canvas, SLOT(set_velo(int))); connect(velo_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(velo_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas())); note_settings_toolbar->addWidget(velo_spinbox); velo_spinbox->setValue(ScoreCanvas::note_velo_init); label = new QLabel(tr("Off-Velocity")); label->setIndent(3); note_settings_toolbar->addWidget(label); velo_off_spinbox = new SpinBox(this); velo_off_spinbox->setRange(0, 127); velo_off_spinbox->setSingleStep(1); velo_off_spinbox->setToolTip(tr("Apply to selected notes, or new notes if none is selected")); //we do not use the valueChanged signal, because that would generate //many many undos when using the spin buttons. connect(velo_off_spinbox, SIGNAL(editingFinished()), SLOT(velo_off_box_changed())); connect(this,SIGNAL(velo_off_changed(int)), score_canvas, SLOT(set_velo_off(int))); connect(velo_off_spinbox, SIGNAL(returnPressed()), SLOT(focusCanvas())); connect(velo_off_spinbox, SIGNAL(escapePressed()), SLOT(focusCanvas())); note_settings_toolbar->addWidget(velo_off_spinbox); velo_off_spinbox->setValue(ScoreCanvas::note_velo_off_init); QMenu* edit_menu = menuBar()->addMenu(tr("&Edit")); edit_menu->addActions(MusEGlobal::undoRedo->actions()); edit_menu->addSeparator(); cut_action = edit_menu->addAction(*cutSVGIcon, tr("C&ut")); connect(cut_action, &QAction::triggered, [this]() { menu_command(CMD_CUT); } ); copy_action = edit_menu->addAction(*copySVGIcon, tr("&Copy")); connect(copy_action, &QAction::triggered, [this]() { menu_command(CMD_COPY); } ); copy_range_action = edit_menu->addAction(*copyRangeSVGIcon, tr("Copy Events in Range")); connect(copy_range_action, &QAction::triggered, [this]() { menu_command(CMD_COPY_RANGE); } ); paste_action = edit_menu->addAction(*pasteSVGIcon, tr("&Paste")); connect(paste_action, &QAction::triggered, [this]() { menu_command(CMD_PASTE); } ); paste_dialog_action = edit_menu->addAction(*pasteDialogSVGIcon, tr("Paste (With Dialog)...")); connect(paste_dialog_action, &QAction::triggered, [this]() { menu_command(CMD_PASTE_DIALOG); } ); edit_menu->addSeparator(); del_action = edit_menu->addAction(*deleteSVGIcon, tr("Delete &Events")); connect(del_action, &QAction::triggered, [this]() { menu_command(CMD_DEL); } ); edit_menu->addSeparator(); QMenu* select_menu = edit_menu->addMenu(tr("&Select")); select_all_action = select_menu->addAction(*selectAllSVGIcon, tr("Select &All")); connect(select_all_action, &QAction::triggered, [this]() { menu_command(CMD_SELECT_ALL); } ); select_none_action = select_menu->addAction(*deselectAllSVGIcon, tr("&Deselect All")); connect(select_none_action, &QAction::triggered, [this]() { menu_command(CMD_SELECT_NONE); } ); select_invert_action = select_menu->addAction(*selectInvertSVGIcon, tr("Invert &Selection")); connect(select_invert_action, &QAction::triggered, [this]() { menu_command(CMD_SELECT_INVERT); } ); select_menu->addSeparator(); select_iloop_action = select_menu->addAction(*selectInsideLoopSVGIcon, tr("&Inside Loop")); connect(select_iloop_action, &QAction::triggered, [this]() { menu_command(CMD_SELECT_ILOOP); } ); select_oloop_action = select_menu->addAction(*selectOutsideLoopSVGIcon, tr("&Outside Loop")); connect(select_oloop_action, &QAction::triggered, [this]() { menu_command(CMD_SELECT_OLOOP); } ); QMenu* functions_menu = menuBar()->addMenu(tr("Fu&nctions")); func_quantize_action = functions_menu->addAction(*quantizeSVGIcon, tr("&Quantize")); func_velocity_action = functions_menu->addAction(tr("Change Note &Velocity")); func_cresc_action = functions_menu->addAction(tr("Crescendo/Decrescendo")); func_move_action = functions_menu->addAction(tr("Move Notes")); func_del_overlaps_action = functions_menu->addAction(tr("Delete Overlaps")); func_erase_action = functions_menu->addAction(tr("Erase Events")); functions_menu->addSeparator(); func_notelen_action = functions_menu->addAction(tr("Change Note &Length")); func_fixed_len_action = functions_menu->addAction(tr("Set Fixed Length")); func_transpose_action = functions_menu->addAction(tr("Transpose")); func_legato_action = functions_menu->addAction(tr("Legato")); connect(func_quantize_action, &QAction::triggered, [this]() { menu_command(CMD_QUANTIZE); } ); connect(func_notelen_action, &QAction::triggered, [this]() { menu_command(CMD_NOTELEN); } ); connect(func_velocity_action, &QAction::triggered, [this]() { menu_command(CMD_VELOCITY); } ); connect(func_cresc_action, &QAction::triggered, [this]() { menu_command(CMD_CRESCENDO); } ); connect(func_transpose_action, &QAction::triggered, [this]() { menu_command(CMD_TRANSPOSE); } ); connect(func_erase_action, &QAction::triggered, [this]() { menu_command(CMD_ERASE); } ); connect(func_move_action, &QAction::triggered, [this]() { menu_command(CMD_MOVE); } ); connect(func_fixed_len_action, &QAction::triggered, [this]() { menu_command(CMD_FIXED_LEN); } ); connect(func_del_overlaps_action, &QAction::triggered, [this]() { menu_command(CMD_DELETE_OVERLAPS); } ); connect(func_legato_action, &QAction::triggered, [this]() { menu_command(CMD_LEGATO); } ); QMenu* settings_menu = menuBar()->addMenu(tr("&Display")); settings_menu->menuAction()->setStatusTip(tr("Display menu: Display options specific to current editor.")); settings_menu->addAction(subwinAction); // settings_menu->addAction(shareAction); settings_menu->addAction(fullscreenAction); settings_menu->addSeparator(); color_menu = settings_menu->addMenu(tr("Note Head &Colors")); color_actions = new QActionGroup(this); color_actions->setObjectName("CheckmarkOnly"); color_black_action = color_menu->addAction(tr("&Default")); color_velo_action = color_menu->addAction(tr("&Velocity")); color_part_action = color_menu->addAction(tr("&Part")); color_black_action->setCheckable(true); color_velo_action->setCheckable(true); color_part_action->setCheckable(true); color_actions->addAction(color_black_action); color_actions->addAction(color_velo_action); color_actions->addAction(color_part_action); connect(color_black_action, &QAction::triggered, [this]() { menu_command(CMD_COLOR_BLACK); } ); connect(color_velo_action, &QAction::triggered, [this]() { menu_command(CMD_COLOR_VELO); } ); connect(color_part_action, &QAction::triggered, [this]() { menu_command(CMD_COLOR_PART); } ); switch (ScoreCanvas::coloring_mode_init) { case 0: color_black_action->setChecked(true); menu_command(CMD_COLOR_BLACK); break; case 1: color_velo_action->setChecked(true); menu_command(CMD_COLOR_VELO); break; case 2: color_part_action->setChecked(true); menu_command(CMD_COLOR_PART); break; default: cerr << "ERROR: THIS SHOULD NEVER HAPPEN. noteColor is invalid in ScoreEdit::ScoreEdit.\n" << " (noteColor="<setChecked(true); menu_command(CMD_COLOR_BLACK); } QMenu* preamble_menu = settings_menu->addMenu(tr("Set Up &Preamble")); preamble_menu->setObjectName("CheckmarkOnly"); preamble_keysig_action = preamble_menu->addAction(tr("Display &Key Signature")); preamble_timesig_action = preamble_menu->addAction(tr("Display &Time Signature")); connect(preamble_keysig_action, SIGNAL(toggled(bool)), score_canvas, SLOT(preamble_keysig_slot(bool))); connect(preamble_timesig_action, SIGNAL(toggled(bool)), score_canvas, SLOT(preamble_timesig_slot(bool))); preamble_keysig_action->setCheckable(true); preamble_timesig_action->setCheckable(true); preamble_keysig_action->setChecked(ScoreCanvas::preamble_contains_keysig_init); preamble_timesig_action->setChecked(ScoreCanvas::preamble_contains_timesig_init); QAction* set_name_action = settings_menu->addAction(*dummySVGIcon, tr("Set Score &Name...")); connect(set_name_action, &QAction::triggered, [this]() { menu_command(CMD_SET_NAME); } ); config_changed(); // set configuration values, initialize shortcuts connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(config_changed())); QClipboard* cb = QApplication::clipboard(); connect(cb, SIGNAL(dataChanged()), SLOT(clipboard_changed())); clipboard_changed(); selection_changed(); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(song_changed(MusECore::SongChangedStruct_t))); connect(MusEGlobal::song, SIGNAL(newPartsCreated(const std::map< const MusECore::Part*, std::set >&)), score_canvas, SLOT(add_new_parts(const std::map< const MusECore::Part*, std::set >&))); score_canvas->fully_recalculate(); score_canvas->goto_tick(initPos,true); score_canvas->setFocus(); if (name!=nullptr) set_name(name, false, true); else init_name(); apply_velo=true; initTopwinState(); finalizeInit(); } void ScoreEdit::config_changed() { if(score_canvas) score_canvas->config_changed(); init_shortcuts(); } void ScoreEdit::init_shortcuts() { cut_action->setShortcut(shortcuts[SHRT_CUT].key); copy_action->setShortcut(shortcuts[SHRT_COPY].key); copy_range_action->setShortcut(shortcuts[SHRT_COPY_RANGE].key); paste_action->setShortcut(shortcuts[SHRT_PASTE].key); paste_dialog_action->setShortcut(shortcuts[SHRT_PASTE_DIALOG].key); del_action->setShortcut(shortcuts[SHRT_DELETE].key); select_all_action->setShortcut(shortcuts[SHRT_SELECT_ALL].key); select_none_action->setShortcut(shortcuts[SHRT_SELECT_NONE].key); select_invert_action->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); select_iloop_action->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key); select_oloop_action->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); // color_menu->menuAction()->setShortcut(shortcuts[SHRT_EVENT_COLOR].key); func_quantize_action->setShortcut(shortcuts[SHRT_QUANTIZE].key); func_notelen_action->setShortcut(shortcuts[SHRT_MODIFY_GATE_TIME].key); func_velocity_action->setShortcut(shortcuts[SHRT_MODIFY_VELOCITY].key); func_transpose_action->setShortcut(shortcuts[SHRT_TRANSPOSE].key); func_erase_action->setShortcut(shortcuts[SHRT_ERASE_EVENT].key); func_move_action->setShortcut(shortcuts[SHRT_NOTE_SHIFT].key); func_fixed_len_action->setShortcut(shortcuts[SHRT_FIXED_LEN].key); func_del_overlaps_action->setShortcut(shortcuts[SHRT_DELETE_OVERLAPS].key); } void ScoreEdit::add_parts(MusECore::PartList* pl, bool all_in_one) { score_canvas->add_staves(pl, all_in_one); } //--------------------------------------------------------- // itemsAreSelected //--------------------------------------------------------- bool ScoreEdit::itemsAreSelected() const { if(score_canvas) return score_canvas->itemsAreSelected(); return false; } void ScoreEdit::setEditTool(int tool) { edit_tools->set(tool); } //--------------------------------------------------------- // tagItems //--------------------------------------------------------- void ScoreEdit::tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const { if(score_canvas) score_canvas->tagItems(tag_list, options); } void ScoreEdit::init_name() { int no=1; QString temp; while (true) { temp="Score "+IntToQStr(no); if (set_name(temp, false, false)) break; else no++; } } bool ScoreEdit::set_name(QString newname, bool emit_signal, bool emergency_name) { if (names.find(newname)==names.end()) { names.erase(name); names.insert(newname); name=newname; isMdiWin() ? setWindowTitle(name) : setWindowTitle("MusE: " + name); if (emit_signal) emit name_changed(); return true; } else { if (emergency_name) { while (set_name(create_random_string(), emit_signal, false) == false) ; return true; } else return false; } } //--------------------------------------------------------- // ~ScoreEdit //--------------------------------------------------------- ScoreEdit::~ScoreEdit() { names.erase(name); } void ScoreEdit::focusCanvas() { if(MusEGlobal::config.smartFocus) { score_canvas->setFocus(); score_canvas->activateWindow(); } } void ScoreEdit::velo_box_changed() { emit velo_changed(velo_spinbox->value()); } void ScoreEdit::velo_off_box_changed() { emit velo_off_changed(velo_off_spinbox->value()); } void ScoreEdit::quant_combobox_changed(int idx) { score_canvas->set_quant(idx); focusCanvas(); } void ScoreEdit::song_changed(MusECore::SongChangedStruct_t flags) { if(_isDeleting) // Ignore while while deleting to prevent crash. return; if (flags & (SC_SELECTION | SC_EVENT_MODIFIED | SC_EVENT_REMOVED)) { map selection=get_events(score_canvas->get_all_parts(),1, MusECore::AllEventsRelevant); if (!selection.empty()) { int velo=-1; int velo_off=-1; for (map::iterator it=selection.begin(); it!=selection.end(); it++) if (it->first->type()==MusECore::Note) { if (velo==-1) velo=it->first->velo(); else if ((velo>=0) && (velo!=it->first->velo())) velo=-2; if (velo_off==-1) velo_off=it->first->veloOff(); else if ((velo_off>=0) && (velo_off!=it->first->veloOff())) velo_off=-2; } if (velo>=0) velo_spinbox->setValue(velo); if (velo_off>=0) velo_off_spinbox->setValue(velo_off); } selection_changed(); } } void ScoreEdit::canvas_width_changed(int width) { xscroll->setMaximum(width); } void ScoreEdit::viewport_width_changed(int width) { xscroll->setPageStep(width * PAGESTEP); } void ScoreEdit::canvas_height_changed(int height) { int val=height - score_canvas->viewport_height(); if (val<=0) val=0; yscroll->setMaximum(val); if (val==0) yscroll->hide(); else yscroll->show(); } void ScoreEdit::viewport_height_changed(int height) { int val=score_canvas->canvas_height() - height; // FINDMICHJETZT canvas_height() is uninitialized! if (val<0) val=0; yscroll->setPageStep(height * PAGESTEP); yscroll->setMaximum(val); if (val==0) yscroll->hide(); else yscroll->show(); } void ScoreEdit::closeEvent(QCloseEvent* e) { _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. names.erase(name); storeSettings(); emit isDeleting(static_cast(this)); e->accept(); } void ScoreEdit::storeSettings() { QSettings settings; //settings.setValue("ScoreEdit/geometry", saveGeometry()); settings.setValue("ScoreEdit/windowState", saveState()); } void ScoreEdit::menu_command(int cmd) { MusECore::TagEventList tag_list; const FunctionDialogElements_t fn_element_dflt = FunctionAllEventsButton | FunctionSelectedEventsButton | FunctionLoopedButton | FunctionSelectedLoopedButton; // | FunctionAllPartsButton | // FunctionSelectedPartsButton switch (cmd) { case CMD_SET_NAME: { bool ok; QString newname = QInputDialog::getText(this, tr("Set Score Name"), tr("Enter the new score title"), QLineEdit::Normal, name, &ok); if (ok) { if (!set_name(newname)) QMessageBox::warning(this, tr("Error"), tr("Changing score title failed:\nthe selected title is not unique")); } } break; case CMD_SELECT_ALL: select_all(score_canvas->get_all_parts()); break; case CMD_SELECT_NONE: select_none(score_canvas->get_all_parts()); break; case CMD_SELECT_INVERT: select_invert(score_canvas->get_all_parts()); break; case CMD_SELECT_ILOOP: select_in_loop(score_canvas->get_all_parts()); break; case CMD_SELECT_OLOOP: select_not_in_loop(score_canvas->get_all_parts()); break; case CMD_CUT: tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::cut_items(&tag_list); break; case CMD_COPY: tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::copy_items(&tag_list); break; case CMD_COPY_RANGE: //tagItems(!itemsAreSelected(), false, true, MusEGlobal::song->lPos(), MusEGlobal::song->rPos()); // For now, unlike the other editors, just tag all parts. tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( !itemsAreSelected(), true, true, MusEGlobal::song->lPos(), MusEGlobal::song->rPos())); MusECore::copy_items(&tag_list); break; case CMD_PASTE: menu_command(CMD_SELECT_NONE); MusECore::paste_items(score_canvas->get_all_parts(), 3072, MusECore::FunctionOptionsStruct( MusECore::FunctionEraseItemsDefault | MusECore::FunctionPasteNeverNewPart)); break; case CMD_PASTE_DIALOG: menu_command(CMD_SELECT_NONE); MusECore::paste_items(score_canvas->get_all_parts(), score_canvas->get_selected_part()); break; case CMD_QUANTIZE: { FunctionDialogReturnQuantize ret = quantize_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::quantize_items(&tag_list, ret._raster_index, ret._quant_len, ret._strength, ret._swing, ret._threshold); } break; } case CMD_VELOCITY: { FunctionDialogReturnVelocity ret = velocity_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::modify_velocity_items(&tag_list, ret._rateVal, ret._offsetVal); } break; } case CMD_CRESCENDO: { FunctionDialogReturnCrescendo ret = crescendo_items_dialog(FunctionDialogMode( FunctionLoopedButton | FunctionSelectedLoopedButton // | FunctionAllPartsButton | // FunctionSelectedPartsButton )); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::crescendo_items(&tag_list, ret._start_val, ret._end_val, ret._absolute); } break; } case CMD_NOTELEN: { FunctionDialogReturnGateTime ret = gatetime_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::modify_notelen_items(&tag_list, ret._rateVal, ret._offsetVal); } break; } case CMD_TRANSPOSE: { FunctionDialogReturnTranspose ret = transpose_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::transpose_items(&tag_list, ret._amount); } break; } case CMD_ERASE: { FunctionDialogReturnErase ret = erase_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::erase_items(&tag_list, ret._veloThreshold, ret._veloThresUsed, ret._lenThreshold, ret._lenThresUsed); } } break; case CMD_DEL: tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::erase_items(&tag_list); break; case CMD_MOVE: { FunctionDialogReturnMove ret = move_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::move_items(&tag_list, ret._amount); } break; } case CMD_FIXED_LEN: { FunctionDialogReturnSetLen ret = setlen_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::set_notelen_items(&tag_list, ret._len); } break; } case CMD_DELETE_OVERLAPS: { FunctionDialogReturnDelOverlaps ret = deloverlaps_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::delete_overlaps_items(&tag_list); } break; } case CMD_LEGATO: { FunctionDialogReturnLegato ret = legato_items_dialog(FunctionDialogMode(fn_element_dflt)); if(ret._valid) { tagItems(&tag_list, MusECore::EventTagOptionsStruct::fromOptions( ret._allEvents, ret._allParts, ret._range, ret._pos0, ret._pos1)); MusECore::legato_items(&tag_list, ret._min_len, !ret._allow_shortening); } break; } default: score_canvas->menu_command(cmd); } } void ScoreEdit::clipboard_changed() { paste_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists"))); paste_dialog_action->setEnabled(QApplication::clipboard()->mimeData()->hasFormat(QString("text/x-muse-groupedeventlists"))); } void ScoreEdit::selection_changed() { bool flag = !get_events(score_canvas->get_all_parts(),1).empty(); cut_action->setEnabled(flag); copy_action->setEnabled(flag); del_action->setEnabled(flag); } //duplicated from songfile.cpp's MusE::readPart(); the only differences: //"none" is supported and tag_name is settable MusECore::Part* read_part(MusECore::Xml& xml, QString tag_name="part") { MusECore::Part* part = 0; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return part; case MusECore::Xml::Text: { int trackIdx, partIdx; if (tag=="none") part=NULL; else { sscanf(tag.toLatin1().constData(), "%d:%d", &trackIdx, &partIdx); if (debugMsg) cout << "read_part: trackIdx="<::iterator part=parts.begin(); part!=parts.end(); part++) { MusECore::Track* track = (*part)->track(); int trkIdx = MusEGlobal::song->tracks()->index(track); int partIdx = track->parts()->index(*part); if((trkIdx == -1) || (partIdx == -1)) cerr << "ERROR: staff_t::write_status: trkIdx:"<%d:%d", trkIdx, partIdx); } xml.tag(level, "/staff"); } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void ScoreEdit::writeStatus(int level, MusECore::Xml& xml) const { xml.tag(level++, "scoreedit"); TopWin::writeStatus(level, xml); xml.strTag(level, "name", name); xml.intTag(level, "tool", edit_tools->curTool()); xml.intTag(level, "steprec", srec->isChecked()); xml.intTag(level, "quantPower", score_canvas->quant_power2()); xml.intTag(level, "pxPerWhole", score_canvas->pixels_per_whole()); xml.intTag(level, "newNoteVelo", velo_spinbox->value()); xml.intTag(level, "newNoteVeloOff", velo_off_spinbox->value()); xml.intTag(level, "lastLen", score_canvas->get_last_len()); int len=0; if (n1_action->isChecked()) len=1; else if (n2_action->isChecked()) len=2; else if (n4_action->isChecked()) len=4; else if (n8_action->isChecked()) len=8; else if (n16_action->isChecked()) len=16; else if (n32_action->isChecked()) len=32; else if (nlast_action->isChecked()) len=0; // means "last" xml.intTag(level, "newLen", len); int color=0; if (color_black_action->isChecked()) color=0; else if (color_velo_action->isChecked()) color=1; else if (color_part_action->isChecked()) color=2; xml.intTag(level, "noteColor", color); xml.intTag(level, "xscroll", xscroll->value()); xml.intTag(level, "yscroll", yscroll->value()); xml.intTag(level, "preambleContainsKeysig", preamble_keysig_action->isChecked()); xml.intTag(level, "preambleContainsTimesig", preamble_timesig_action->isChecked()); const MusECore::Part* selected_part=score_canvas->get_selected_part(); if (selected_part==NULL) { xml.put(level, "none"); } else { MusECore::Track* track = selected_part->track(); int trkIdx = MusEGlobal::song->tracks()->index(track); int partIdx = track->parts()->index(selected_part); if((trkIdx == -1) || (partIdx == -1)) cerr << "ERROR: ScoreEdit::write_status: trkIdx:"<%d:%d", trkIdx, partIdx); } score_canvas->write_staves(level,xml); xml.tag(level, "/scoreedit"); } void ScoreCanvas::write_staves(int level, MusECore::Xml& xml) const { for (list::const_iterator staff=staves.begin(); staff!=staves.end(); staff++) staff->write_status(level, xml); } //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void ScoreEdit::readStatus(MusECore::Xml& xml) { // never "return;" inside that function. // instead, goto end_of_scoreedit_read_status; // (there is a command which must be executed!) bool apply_velo_temp=apply_velo; apply_velo=false; for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "name") set_name(xml.parse1()); else if (tag == "tool") edit_tools->set(xml.parseInt()); else if (tag == "steprec") srec->setChecked(xml.parseInt()); else if (tag == "quantPower") quant_combobox->setCurrentIndex(xml.parseInt()-1); else if (tag == "pxPerWhole") px_per_whole_spinbox->setValue(xml.parseInt()); else if (tag == "newNoteVelo") velo_spinbox->setValue(xml.parseInt()); else if (tag == "newNoteVeloOff") velo_off_spinbox->setValue(xml.parseInt()); else if (tag == "lastLen") score_canvas->set_last_len(xml.parseInt()); else if (tag == "newLen") { int val=xml.parseInt(); switch (val) { case 0: nlast_action->setChecked(true); break; case 1: n1_action->setChecked(true); break; case 2: n2_action->setChecked(true); break; case 4: n4_action->setChecked(true); break; case 8: n8_action->setChecked(true); break; case 16: n16_action->setChecked(true); break; case 32: n32_action->setChecked(true); break; default: cerr << "ERROR: THIS SHOULD NEVER HAPPEN. newLen is invalid in ScoreEdit::readStatus.\n" << " (newLen="<setChecked(true); } } else if (tag == "noteColor") { int val=xml.parseInt(); switch (val) { case 0: color_black_action->setChecked(true); break; case 1: color_velo_action->setChecked(true); break; case 2: color_part_action->setChecked(true); break; default: cerr << "ERROR: THIS SHOULD NEVER HAPPEN. noteColor is invalid in ScoreEdit::readStatus.\n" << " (noteColor="<setChecked(true); } } else if (tag == "xscroll") xscroll->setValue(xml.parseInt()); else if (tag == "yscroll") yscroll->setValue(xml.parseInt()); else if (tag == "preambleContainsKeysig") preamble_keysig_action->setChecked(xml.parseInt()); else if (tag == "preambleContainsTimesig") preamble_timesig_action->setChecked(xml.parseInt()); else if (tag == "topwin") TopWin::readStatus(xml); else if (tag == "selectedPart") score_canvas->set_selected_part(read_part(xml, "selectedPart")); else if (tag == "staff") { staff_t staff(score_canvas); staff.read_status(xml); score_canvas->push_back_staff(staff); } else xml.unknown("ScoreEdit"); break; case MusECore::Xml::TagEnd: if (tag == "scoreedit") goto end_of_scoreedit_read_status; default: break; } } end_of_scoreedit_read_status: apply_velo=apply_velo_temp; } void ScoreEdit::read_configuration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag=="quantPowerInit") ScoreCanvas::_quant_power2_init=xml.parseInt(); else if (tag=="pxPerWholeInit") ScoreCanvas::_pixels_per_whole_init=xml.parseInt(); else if (tag=="newNoteVeloInit") ScoreCanvas::note_velo_init=xml.parseInt(); else if (tag=="newNoteVeloOffInit") ScoreCanvas::note_velo_off_init=xml.parseInt(); else if (tag=="newLenInit") ScoreCanvas::new_len_init=xml.parseInt(); else if (tag=="noteColorInit") ScoreCanvas::coloring_mode_init=(ScoreCanvas::coloring_mode_t)xml.parseInt(); else if (tag=="preambleContainsKeysig") ScoreCanvas::preamble_contains_keysig_init=xml.parseInt(); else if (tag=="preambleContainsTimesig") ScoreCanvas::preamble_contains_timesig_init=xml.parseInt(); else if (tag == "topwin") TopWin::readConfiguration(SCORE, xml); else xml.unknown("ScoreEdit"); break; case MusECore::Xml::TagEnd: if (tag == "scoreedit") return; default: break; } } } void ScoreEdit::write_configuration(int level, MusECore::Xml& xml) { xml.tag(level++, "scoreedit"); xml.intTag(level, "quantPowerInit", ScoreCanvas::_quant_power2_init); xml.intTag(level, "pxPerWholeInit", ScoreCanvas::_pixels_per_whole_init); xml.intTag(level, "newNoteVeloInit", ScoreCanvas::note_velo_init); xml.intTag(level, "newNoteVeloOffInit", ScoreCanvas::note_velo_off_init); xml.intTag(level, "newLenInit", ScoreCanvas::new_len_init); xml.intTag(level, "noteColorInit", ScoreCanvas::coloring_mode_init); xml.intTag(level, "preambleContainsKeysig", ScoreCanvas::preamble_contains_keysig_init); xml.intTag(level, "preambleContainsTimesig", ScoreCanvas::preamble_contains_timesig_init); TopWin::writeConfiguration(SCORE, level, xml); xml.etag(level, "scoreedit"); } //--------------------------------------------------------- // itemsAreSelected //--------------------------------------------------------- bool ScoreCanvas::itemsAreSelected() const { FloItem fi; for(list::const_iterator is = staves.begin(); is != staves.end(); ++is) { const staff_t& staff = *is; const ScoreItemList& sil = staff.itemlist; for(ScoreItemList::const_iterator isi = sil.begin(); isi != sil.end(); ++isi) { const set& si = isi->second; for(set::const_iterator efi = si.begin(); efi != si.end(); ++efi) { fi = *efi; if(fi.source_event && (*fi.source_event).selected()) return true; } } } return false; } //--------------------------------------------------------- // tagItems //--------------------------------------------------------- void ScoreCanvas::tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const { const bool tagSelected = options._flags & MusECore::TagSelected; //const bool tagMoving = options._flags & MusECore::TagMoving; const bool tagAllItems = options._flags & MusECore::TagAllItems; const bool tagAllParts = options._flags & MusECore::TagAllParts; const bool range = options._flags & MusECore::TagRange; const MusECore::Pos& p0 = options._p0; const MusECore::Pos& p1 = options._p1; // Here we can choose between using tagAllParts for parts or staves. // If we choose parts, it's kind of difficult for the user to tell which part is selected. // Whereas, if we choose staves, it's much easier to see that a staff is currently selected. // TODO: Possibly add 'staves' radio buttons to the function dialogs instead. const bool use_staves_for_tag_all_parts = true; // For now we don't use the tagAllParts flag. const bool use_tag_all_parts = false; const MusECore::Part* part; MusECore::Pos pos; FloItem fi; for(list::const_iterator is = staves.begin(); is != staves.end(); ++is) { const staff_t& staff = *is; if(use_tag_all_parts && use_staves_for_tag_all_parts && !tagAllParts && (&staff != &(*current_staff))) continue; const ScoreItemList& sil = staff.itemlist; for(ScoreItemList::const_iterator isi = sil.begin(); isi != sil.end(); ++isi) { const set& si = isi->second; for(set::const_iterator efi = si.begin(); efi != si.end(); ++efi) { fi = *efi; // Make sure the event and part pointers are valid. if(!fi.source_event || !fi.source_part) continue; const MusECore::Event& e = *fi.source_event; part = fi.source_part; if(use_tag_all_parts && !use_staves_for_tag_all_parts && !tagAllParts && part != selected_part) continue; if(range) { // Don't forget to add the part's position. pos = e.pos() + *part; // p1 should be considered outside (one past) the very last position in the range. if(pos < p0 || pos >= p1) continue; } if(tagAllItems || (tagSelected && e.selected()) //|| (tagMoving && e.isMoving()) //TODO ) { tag_list->add(part, e); } } } } } void ScoreCanvas::add_staves(MusECore::PartList* pl, bool all_in_one) { if (!pl->empty()) { staff_t staff(this); if (all_in_one) { clefTypes clef=((MusECore::MidiTrack*)pl->begin()->second->track())->getClef(); staff.parts.clear(); for (MusECore::ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) { if (((MusECore::MidiTrack*)part_it->second->track())->getClef() != clef) clef=grandStaff; staff.parts.insert(part_it->second); } staff.cleanup_parts(); staff.update_part_indices(); switch (clef) { case trebleClef: staff.type=NORMAL; staff.clef=VIOLIN; staves.push_back(staff); break; case bassClef: staff.type=NORMAL; staff.clef=BASS; staves.push_back(staff); break; case grandStaff: staff.type=GRAND_TOP; staff.clef=VIOLIN; staves.push_back(staff); staff.type=GRAND_BOTTOM; staff.clef=BASS; staves.push_back(staff); break; } } else { set tracks; for (MusECore::ciPart it=pl->begin(); it!=pl->end(); it++) tracks.insert(it->second->track()); MusECore::TrackList* tracklist = MusEGlobal::song->tracks(); // this loop is used for inserting track-staves in the // correct order. simply iterating through tracks's contents // would sort after the pointer values, i.e. randomly for (MusECore::ciTrack track_it=tracklist->begin(); track_it!=tracklist->end(); track_it++) if (tracks.find(*track_it)!=tracks.end()) { staff.parts.clear(); for (MusECore::ciPart part_it=pl->begin(); part_it!=pl->end(); part_it++) if (part_it->second->track() == *track_it) staff.parts.insert(part_it->second); staff.cleanup_parts(); staff.update_part_indices(); switch (((MusECore::MidiTrack*)(*track_it))->getClef()) { case trebleClef: staff.type=NORMAL; staff.clef=VIOLIN; staves.push_back(staff); break; case bassClef: staff.type=NORMAL; staff.clef=BASS; staves.push_back(staff); break; case grandStaff: staff.type=GRAND_TOP; staff.clef=VIOLIN; staves.push_back(staff); staff.type=GRAND_BOTTOM; staff.clef=BASS; staves.push_back(staff); break; } } } cleanup_staves(); fully_recalculate(); recalc_staff_pos(); } } ScoreCanvas::ScoreCanvas(ScoreEdit* pr, QWidget* parent_widget) : View(parent_widget, 1, 1) { parent = pr; setFocusPolicy(Qt::StrongFocus); setBg(MusEGlobal::config.midiCanvasBg); setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); srec=false; for (int i=0;i<128;i++) held_notes[i]=false; steprec=new MusECore::StepRec(held_notes); connect(MusEGlobal::song, SIGNAL(midiNote(int, int)), SLOT(midi_note(int,int))); x_pos=0; x_left=0; y_pos=0; have_lasso=false; inserting=false; dragging=false; drag_cursor_changed=false; mouse_erases_notes=false; mouse_inserts_notes=true; undo_started=false; selected_part=nullptr; dragged_event_part=nullptr; last_len=384; new_len=-1; // will be initialized with new_len_init by ScoreEdit::ScoreEdit(); _quant_power2=_quant_power2_init; // ScoreEdit relies on this to be done! _pixels_per_whole = _pixels_per_whole_init; note_velo=note_velo_init; note_velo_off=note_velo_off_init; dragging_staff=false; coloring_mode=coloring_mode_init; preamble_contains_keysig=preamble_contains_keysig_init; preamble_contains_timesig=preamble_contains_timesig_init; x_scroll_speed=0; x_scroll_pos=0; y_scroll_speed=0; y_scroll_pos=0; connect (MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartbeat_timer_event())); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(pos_changed(int,unsigned,bool))); connect(MusEGlobal::song, SIGNAL(playChanged(bool)), SLOT(play_changed(bool))); staff_menu=new QMenu(this); staffmode_treble_action = staff_menu->addAction(tr("Treble")); connect(staffmode_treble_action, SIGNAL(triggered()), SLOT(staffmode_treble_slot())); staffmode_bass_action = staff_menu->addAction(tr("Bass")); connect(staffmode_bass_action, SIGNAL(triggered()), SLOT(staffmode_bass_slot())); staffmode_both_action = staff_menu->addAction(tr("Grand Staff")); connect(staffmode_both_action, SIGNAL(triggered()), SLOT(staffmode_both_slot())); remove_staff_action = staff_menu->addAction(tr("Remove staff")); connect(remove_staff_action, SIGNAL(triggered()), SLOT(remove_staff_slot())); unsetCursor(); ensurePolished(); init_pixmaps(); } ScoreCanvas::~ScoreCanvas() { delete steprec; } void ScoreCanvas::staffmode_treble_slot() { set_staffmode(current_staff, MODE_TREBLE); } void ScoreCanvas::staffmode_bass_slot() { set_staffmode(current_staff, MODE_BASS); } void ScoreCanvas::staffmode_both_slot() { set_staffmode(current_staff, MODE_BOTH); } void ScoreCanvas::remove_staff_slot() { remove_staff(current_staff); } void ScoreCanvas::set_staffmode(list::iterator it, staff_mode_t mode) { if (it->type == GRAND_BOTTOM) { it--; if (it->type!=GRAND_TOP) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type==GRAND_TOP) { list::iterator tmp=it; tmp++; if (tmp->type!=GRAND_BOTTOM) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<type=NORMAL; it->clef=VIOLIN; break; case MODE_BASS: it->type=NORMAL; it->clef=BASS; break; case MODE_BOTH: it->type=GRAND_BOTTOM; it->clef=BASS; staves.insert(it, staff_t(this, GRAND_TOP, VIOLIN, it->parts)); break; default: cerr << "ERROR: ILLEGAL FUNCTION CALL: invalid mode in set_staffmode" << endl; } fully_recalculate(); recalc_staff_pos(); } void ScoreCanvas::remove_staff(list::iterator it) { if (it->type == GRAND_BOTTOM) { it--; if (it->type!=GRAND_TOP) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type == NORMAL) { staves.erase(it); } else if (it->type == GRAND_TOP) { staves.erase(it++); if (it->type!=GRAND_BOTTOM) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<::iterator dest, list::iterator src) { if (dest->type == GRAND_BOTTOM) { dest--; if (dest->type!=GRAND_TOP) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type == GRAND_BOTTOM) { src--; if (src->type!=GRAND_TOP) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<parts.insert(src->parts.begin(), src->parts.end()); if (dest->type == GRAND_TOP) { dest++; if (dest->type != GRAND_BOTTOM) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"<parts.insert(src->parts.begin(), src->parts.end()); } dest->update_part_indices(); remove_staff(src); fully_recalculate(); recalc_staff_pos(); } void ScoreCanvas::move_staff_above(list::iterator dest, list::iterator src) { if (dest->type == GRAND_BOTTOM) { dest--; if (dest->type!=GRAND_TOP) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<type == GRAND_BOTTOM) { src--; if (src->type!=GRAND_TOP) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_bottom without top!"<::iterator src_end=src; src_end++; //point _after_ src if (src->type==GRAND_TOP) //if this is a grand staff, we need to splice two list-entries src_end++; staves.splice(dest, staves, src, src_end); fully_recalculate(); recalc_staff_pos(); } void ScoreCanvas::move_staff_below(list::iterator dest, list::iterator src) { if (dest->type == GRAND_TOP) { dest++; if (dest->type!=GRAND_BOTTOM) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: grand_top without bottom!"< ScoreCanvas::get_all_parts() { set result; for (list::iterator it=staves.begin(); it!=staves.end(); it++) result.insert(it->parts.begin(), it->parts.end()); return result; } void ScoreCanvas::fully_recalculate() { song_changed(SC_EVENT_MODIFIED); } void ScoreCanvas::song_changed(MusECore::SongChangedStruct_t flags) { if(parent && parent->deleting()) // Ignore while while deleting to prevent crash. return; if (flags & (SC_PART_MODIFIED | SC_PART_REMOVED | SC_PART_INSERTED | SC_TRACK_REMOVED)) { update_parts(); if (flags & (SC_PART_REMOVED | SC_TRACK_REMOVED)) { for (list::iterator it=staves.begin(); it!=staves.end(); it++) it->cleanup_parts(); cleanup_staves(); for (list::iterator it=staves.begin(); it!=staves.end(); it++) it->recalculate(); recalc_staff_pos(); redraw(); } } if (flags & (SC_PART_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_MODIFIED | SC_EVENT_REMOVED | SC_SIG | SC_KEY) ) { calc_pos_add_list(); for (list::iterator it=staves.begin(); it!=staves.end(); it++) it->recalculate(); recalc_staff_pos(); redraw(); emit canvas_width_changed(canvas_width()); } if (flags & SC_SELECTION) { redraw(); } } int ScoreCanvas::canvas_width() { //return tick_to_x(staves.begin()->itemlist.rbegin()->first); return tick_to_x(SONG_LENGTH); } int ScoreCanvas::canvas_height() { return staves.empty() ? 0 : staves.rbegin()->y_bottom; } int ScoreCanvas::viewport_width() { return (width() - x_left); } int ScoreCanvas::viewport_height() { return height(); } string IntToStr(int i) { ostringstream s; s<b.height) return false; return a.vorzeichen::const_iterator part_it=parts.begin(); part_it!=parts.end(); part_it++) { const MusECore::Part* part=*part_it; for (MusECore::ciEvent it=part->events().begin(); it!=part->events().end(); it++) { const MusECore::Event& event=it->second; if ( ( event.isNote() && !event.isNoteOff() && // (event.endTick() <= part->lenTick()) ) && (event.tick() <= part->lenTick()) ) && // changed to accord to prcanvas.cpp and others (flo93) ( ((type==GRAND_TOP) && (event.pitch() >= SPLIT_NOTE)) || ((type==GRAND_BOTTOM) && (event.pitch() < SPLIT_NOTE)) || (type==NORMAL) ) ) { unsigned begin, end; begin=flo_quantize(event.tick()+part->tick(), parent->quant_ticks()); end=flo_quantize(event.endTick()+part->tick(), parent->quant_ticks()); if (end==begin) { if (heavyDebugMsg) cout << "note len would be quantized to zero. using minimal possible length" << endl; end=begin+parent->quant_ticks(); } if (heavyDebugMsg) cout << "inserting note on at "<second->sig.z<<"/"<second->sig.n<<"; ticks per measure = "<(from, FloEvent(from, FloEvent::TIME_SIG, it->second->sig.z, it->second->sig.n) ) ); for (unsigned t=from; t(t, FloEvent(t,0,0,ticks_per_measure,FloEvent::BAR) ) ); } //insert key changes for (MusECore::iKeyEvent it=MusEGlobal::keymap.begin(); it!=MusEGlobal::keymap.end(); it++) eventlist.insert(pair( it->second.tick, FloEvent(it->second.tick,FloEvent::KEY_CHANGE, it->second.key, it->second.minor ) ) ); // phase two: deal with overlapping notes --------------------------- ScoreEventList::iterator it, it2; //iterate through all note_on - events for (it=eventlist.begin(); it!=eventlist.end(); it++) { if (it->second.type==FloEvent::NOTE_ON) { unsigned end_tick=it->first + it->second.len; //iterate though all (relevant) later note_ons which are //at the same pitch. if there's a collision, shorten it's len for (it2=it, it2++; it2!=eventlist.end() && it2->first < end_tick; it2++) if ((it2->second.type==FloEvent::NOTE_ON) && (it2->second.pitch == it->second.pitch)) it->second.len=it2->first - it->first; } } // phase three: eliminate zero-length-notes ------------------------- for (it=eventlist.begin(); it!=eventlist.end();) { if ((it->second.type==FloEvent::NOTE_ON) && (it->second.len<=0)) eventlist.erase(it++); else it++; } } bool is_sharp_key(MusECore::key_enum t) { return ((t>=MusECore::KEY_SHARP_BEGIN) && (t<=MusECore::KEY_SHARP_END)); } bool is_b_key(MusECore::key_enum t) { return ((t>=MusECore::KEY_B_BEGIN) && (t<=MusECore::KEY_B_END)); } int n_accidentials(MusECore::key_enum t) { if (is_sharp_key(t)) return t-MusECore::KEY_SHARP_BEGIN-1; else return t-MusECore::KEY_B_BEGIN-1; } //note needs to be 0..11 //always assumes violin clef //only for internal use note_pos_t note_pos_(int note, MusECore::key_enum key) { note_pos_t result; //C CIS D DIS E F FIS G GIS A AIS H int foo[12]={0,-1, 1,-1, 2,3,-1, 4,-1, 5, -1,6}; if ((note<0) || (note>=12)) cerr << "ERROR: ILLEGAL FUNCTION CALL (note_pos, note out of range)" << endl; if (foo[note]!=-1) { result.height=foo[note]; result.vorzeichen=NONE; } else { if (is_sharp_key(key)) { result.height=foo[note-1]; result.vorzeichen=SHARP; } else // if is_b_key { result.height=foo[note+1]; result.vorzeichen=B; } } // Special cases for GES / FIS keys if (key==MusECore::KEY_GES) { // convert a H to a Ces if (note==11) { result.height=12; result.vorzeichen=B; } } else if (key==MusECore::KEY_FIS) { // convert a F to an Eis if (note==5) { result.height=2; result.vorzeichen=SHARP; } } return result; } // V -------------------------- <-- height=10 // I C -------------------------- <-- height=8 // O L -------------------------- <-- height=6 // L E -------------------------- <-- height=4 // I F -------------------------- <-- height=2 // N --o-- <-- this is C4. height=0 // the "spaces" in between the lines have odd numbers. // that is, the space between line 2 and 4 is numbered 3. // these numbers do not change when clef changes. line 2 // is always the "bottom line" of the system. // in violin clef, line 2 is E4 // in bass clef, line 2 is G2 note_pos_t note_pos (unsigned note, MusECore::key_enum key, clef_t clef) { int octave=(note/12)-1; //integer division. note is unsigned note=note%12; //now octave contains the octave the note is in //(A4 is the 440Hz tone. C4 is the "low C" in the violin clef //and the "high C" in the bass clef. //note contains 0 for C, 1 for Cis, ..., 11 for H (or B if you're not german) note_pos_t pos=note_pos_(note,key); switch (clef) //CLEF_MARKER { case VIOLIN: pos.height=pos.height + (octave-4)*7; break; case BASS: pos.height=pos.height + (octave-3)*7 + 5; break; } return pos; } int calc_len(int l, int d) { // l=0,1,2 -> whole, half, quarter (think of 2^0, 2^1, 2^2) // d=number of dots int tmp=0; for (int i=0;i<=d;i++) tmp+=TICKS_PER_WHOLE / (1 << (l+i)); return tmp; } int calc_measure_len(const list& nums, int denom) { int sum=0; for (list::const_iterator it=nums.begin(); it!=nums.end(); it++) sum+=*it; return 64* sum/denom; } vector create_emphasize_list(const list& nums, int denom) { if (heavyDebugMsg) { cout << "creating emphasize list for "; for (list::const_iterator it=nums.begin(); it!=nums.end(); it++) cout << *it << " "; cout << "/ "< result(len); for (int i=0;i::const_iterator it=nums.begin(); it!=nums.end(); it++) { result[pos]=1; for (int i=1;i<*it;i++) result[pos + i*64/denom]=2; pos+= *it * 64 / denom; } result[0]=0; if (heavyDebugMsg) { for (int i=0;i create_emphasize_list(int num, int denom) { list nums; if (num%3 ==0) { for (int i=0;i parse_note_len(int len_ticks, int begin_tick, vector& foo, bool allow_dots, bool allow_normal) { list retval; if (len_ticks<0) cerr << "ERROR: ILLEGAL FUNCTION CALL in parse_note_len: len_ticks < 0" << endl; if (begin_tick<0) cerr << "ERROR: ILLEGAL FUNCTION CALL in parse_note_len: begin_tick < 0" << endl; if (allow_normal) { int dot_max = allow_dots ? MAX_QUANT_POWER : 0; for (int i=0;i<=MAX_QUANT_POWER;i++) for (int j=0;j<=dot_max-i;j++) if (calc_len(i,j) == len_ticks) { retval.push_back(note_len_t (i,j)); return retval; } } //if !allow_normal or if the above failed int begin=begin_tick * 64 / TICKS_PER_WHOLE; int len=len_ticks * 64 / TICKS_PER_WHOLE; unsigned pos=begin; int len_done=0; while (len_done& acc_list, const QPixmap& pix) { int n_acc_drawn=0; for (list::const_iterator acc_it=acc_list.begin(); acc_it!=acc_list.end(); acc_it++) { int y_coord=2*YLEN - ( *acc_it -2)*YLEN/2; draw_pixmap(p,x + n_acc_drawn*KEYCHANGE_ACC_DIST,y_offset + y_coord,pix); n_acc_drawn++; } } void staff_t::create_itemlist() { MusECore::key_enum tmp_key=MusECore::KEY_C; int lastevent=0; int next_measure=-1; int last_measure=-1; vector emphasize_list=create_emphasize_list(4,4); //actually unnecessary, for safety itemlist.clear(); for (ScoreEventList::iterator it=eventlist.begin(); it!=eventlist.end(); it++) { int t, pitch, len, velo, actual_tick; FloEvent::typeEnum type; t=it->first; pitch=it->second.pitch; velo=it->second.vel; len=it->second.len; type=it->second.type; actual_tick=it->second.tick; if (actual_tick==-1) actual_tick=t; note_pos_t notepos=note_pos(pitch,tmp_key,clef); if (heavyDebugMsg) { printf("FLO: t=%i\ttype=%i\tpitch=%i\tvel=%i\tlen=%i\n",it->first, it->second.type, it->second.pitch, it->second.vel, it->second.len); cout << "\tline="<quant_ticks())/2; if (heavyDebugMsg) cout << "\tend-of-measure: this was an empty measure. inserting rest in between at t="< lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS); unsigned tmppos=lastevent; for (list::iterator x=lens.begin(); x!=lens.end(); x++) { if (heavyDebugMsg) cout << "\t\tpartial rest with len="<len<<", dots="<dots<len,x->dots) ); tmppos+=calc_len(x->len,x->dots); itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) ); } } } } lastevent=t; last_measure=t; next_measure=t+len; itemlist[t].insert( FloItem(FloItem::BAR,no_notepos,0,0) ); } else if (type==FloEvent::NOTE_ON) { int rest=t-lastevent; if (rest) { if (heavyDebugMsg) printf("\tset rest at %i with len %i\n",lastevent,rest); // no need to check if the rest crosses measure boundaries; // it can't. list lens=parse_note_len(rest,lastevent-last_measure,emphasize_list,DOTTED_RESTS,UNSPLIT_RESTS); unsigned tmppos=lastevent; for (list::iterator x=lens.begin(); x!=lens.end(); x++) { if (heavyDebugMsg) cout << "\t\tpartial rest with len="<len<<", dots="<dots<len,x->dots) ); tmppos+=calc_len(x->len,x->dots); itemlist[tmppos].insert( FloItem(FloItem::REST_END,notepos,0,0) ); } } if (heavyDebugMsg) printf("\tset note at %i with len=%i\n", t, len); int tmplen; bool tied_note; // if the note exceeds the current measure, split it. if (t+len>next_measure) { tmplen=next_measure-t; tied_note=true; //append the "remainder" of the note to our EventList, so that //it gets processed again when entering the new measure int newlen=len-tmplen; eventlist.insert(pair(next_measure, FloEvent(actual_tick,pitch, velo,0,FloEvent::NOTE_OFF, it->second.source_part, it->second.source_event))); eventlist.insert(pair(next_measure, FloEvent(actual_tick,pitch, velo,newlen,FloEvent::NOTE_ON, it->second.source_part, it->second.source_event))); if (heavyDebugMsg) cout << "\t\tnote was split to length "<(t+len, FloEvent(t+len,pitch, velo,0,FloEvent::NOTE_OFF,it->second.source_part, it->second.source_event))); } list lens=parse_note_len(tmplen,t-last_measure,emphasize_list,true,true); unsigned tmppos=t; int n_lens=lens.size(); int count=0; for (list::iterator x=lens.begin(); x!=lens.end(); x++) { if (heavyDebugMsg) cout << "\t\tpartial note with len="<len<<", dots="<dots<len,x->dots, tie, actual_tick, it->second.source_part, it->second.source_event) ); tmppos+=calc_len(x->len,x->dots); itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,notepos,0,0) ); } } else if (type==FloEvent::NOTE_OFF) { lastevent=t; } else if (type==FloEvent::TIME_SIG) { if (heavyDebugMsg) cout << "inserting TIME SIGNATURE "<second.num<<"/"<second.denom<<" at "<second.num, it->second.denom) ); emphasize_list=create_emphasize_list(it->second.num, it->second.denom); } else if (type==FloEvent::KEY_CHANGE) { if (heavyDebugMsg) cout << "inserting KEY CHANGE ("<second.key<<") at "<second.key, it->second.minor) ); tmp_key=it->second.key; } } } void staff_t::process_itemlist() { map occupied; int last_measure=0; vector emphasize_list=create_emphasize_list(4,4); //unnecessary, only for safety //iterate through all times with items for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++) { set& curr_items=it2->second; if (heavyDebugMsg) cout << "at t="<first<::iterator it=curr_items.begin(); it!=curr_items.end(); it++) { if ((it->type==FloItem::NOTE) || (it->type==FloItem::REST)) occupied[it->pos.height]++; else if ((it->type==FloItem::NOTE_END) || (it->type==FloItem::REST_END)) occupied[it->pos.height]--; else if (it->type==FloItem::BAR) last_measure=it2->first; else if (it->type==FloItem::TIME_SIG) emphasize_list=create_emphasize_list(it->num, it->denom); } if (heavyDebugMsg) { cout << "occupied: "; for (map::iterator i=occupied.begin(); i!=occupied.end(); i++) if (i->second) cout << i->first << "("<second<<") "; cout << endl; } // phase 1: group rests together ----------------------------------- int n_groups=0; bool dont_group=false; //iterate through all rests R at that time // iterate through all rests X at that time below R // if something is between X and R ("barrier"), stop // else: group them together for (set::iterator it=curr_items.begin(); it!=curr_items.end();) { //only operate on rests; ignore rests which are created by this code //(can be seen on already_grouped) if ((it->type==FloItem::REST) && (it->already_grouped==false)) { if (heavyDebugMsg) cout << "trying to group" << endl; int lastheight; int height_cumulative=0; int counter=0; lastheight=it->pos.height; set::iterator tmp; for (tmp=it; tmp!=curr_items.end();) { if (heavyDebugMsg) cout << "checking if we can proceed with an item at height="<pos.height<pos.height-1; i++) if (occupied[i]!=0) { if (heavyDebugMsg) cout << "we can NOT, because occ["<pos.height; // the current item is a rest with equal len? cool! if (tmp->type==FloItem::REST && tmp->len==it->len && tmp->dots==it->dots) { // füge diese pause zur gruppe dazu und entferne sie von diesem set hier // entfernen aber nur, wenn sie nicht it, also die erste pause ist, die brauchen wir noch! if (heavyDebugMsg) cout << "\tgrouping rest at height="<pos.height<pos.height; counter++; if (tmp!=it) curr_items.erase(tmp++); else tmp++; } else //it's something else? well, we can stop grouping that rest then { if (heavyDebugMsg) cout << "we can NOT, because that item is not a rest" << endl; //stop grouping that rest goto get_out_here; } } if (heavyDebugMsg) cout << "no items to proceed on left, continuing" << endl; get_out_here: n_groups++; // entferne it vom set und // füge eine pause mit dem "mittelwert" ein. // occupied und die "_END"-events bleiben unberührt FloItem temp=*it; temp.already_grouped=true; // have we grouped all available rests into one single? if ( (n_groups==1) && (tmp==curr_items.end()) && !dont_group) { if (heavyDebugMsg) cout << "wow, we were able to group all rests into one single" << endl; if (temp.len==0) //the whole rest is shifted one line (one space and one line) temp.pos.height=DEFAULT_REST_HEIGHT+2; else temp.pos.height=DEFAULT_REST_HEIGHT; } else { if (heavyDebugMsg) cout << "creating group #"<height, the set considers temp and it equal (it doesn't // take already_grouped into account) // the result of this: insert does nothing, and erase erases // the item. effect: you don't have the rest at all curr_items.erase(it++); if (heavyDebugMsg) cout << "replacing all grouped rests with a rest at height="<type==FloItem::NOTE) dont_group=true; it++; } } // phase 2: avoid collisions of items ------------------------------ set::iterator lastit, groupbegin, invalid; invalid=curr_items.end(); lastit=invalid; groupbegin=invalid; int count = 0; //TODO: is "grouping" notes and rests together okay? // or is it better to ignore rests when grouping? for (set::iterator it=curr_items.begin(); it!=curr_items.end(); it++) { if ( (it->type==FloItem::NOTE) || (it->type==FloItem::REST) ) { if (lastit != invalid) { if (it->pos.height == lastit->pos.height+1) // they would collide? { if (groupbegin==invalid) // we have no group atm? { groupbegin=lastit; // start a new group count=1; // because lastit has to be taken into account. // for "it", there's a count++ later } // the following will work even on start-new-group, // because lastit will be "untouched", and that's why // still be initialized to "false" it->ausweich=!lastit->ausweich; count++; } else { if (groupbegin!=invalid) //this is the first item which { //doesn't belong to the previous group any more if (count%2 == 0) //count is even? if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT) for (set::iterator tmp=groupbegin; tmp!=it; tmp++) tmp->ausweich=!tmp->ausweich; groupbegin=invalid; } // else: everything is ok :) } } lastit=it; } } // this could be the case if the last processed item before end() // still belonged to a group. finalize this last group as well: if (groupbegin!=invalid) { if (count%2 == 0) //count is even? if (modulo(groupbegin->pos.height, 2) == AUSWEICHEN_BEVORZUGT) for (set::iterator tmp=groupbegin; tmp!=curr_items.end(); tmp++) tmp->ausweich=!tmp->ausweich; } // else: everything is ok :) // phase 3: group notes by their length and ------------------------ // find out appropriate stem directions group_them_again: map lengths; bool has_whole=false; // find out which note lengths are present at that time for (set::iterator it=curr_items.begin(); it!=curr_items.end(); it++) if (it->type==FloItem::NOTE) lengths[it->len].add(it->pos.height); if (heavyDebugMsg) { cout << "note lengths at that time are:"; for (map::iterator it=lengths.begin(); it!=lengths.end(); it++) cout << it->first << "("<< it->second.mean() <<") "; cout << endl; } if (lengths.erase(0)) // in case "0" is in the set, erase it has_whole=true; // but remember there were whole notes if (lengths.size()==0) { if (heavyDebugMsg) cout << "no notes other than wholes, or no notes at all. we can relax" << endl; } else if (lengths.size()==1) { pair& group=*(lengths.begin()); stem_t stem; int shift=0; if (heavyDebugMsg) cout << "only one non-whole note group (len="<::iterator it=curr_items.begin(); it!=curr_items.end();) if (it->type==FloItem::NOTE) { //if *it belongs to group1 and has not already its destination length if (heavyDebugMsg) cout << "\tprocessing note-item with len="<len<lenfirst+group1_len_ticks; itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); list lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true); unsigned tmppos=t; int n_lens=lens.size(); int count=0; for (list::iterator x=lens.begin(); x!=lens.end(); x++) { if (heavyDebugMsg) cout << "\t\twhile regrouping: partial note with len="<len<<", dots="<dots<len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) ); tmppos+=calc_len(x->len,x->dots); itemlist[tmppos].insert( FloItem(FloItem::NOTE_END, tmp.pos,0,0) ); } } //else if *it belongs to group2 and has not already its destination length else if ((it->lenlen>group1_len)) { if (heavyDebugMsg) cout << "\t\thas to be changed to fit into group 2" << endl; FloItem tmp=*it; curr_items.erase(it++); int len_ticks_remaining=calc_len(tmp.len, tmp.dots)-group2_len_ticks; bool tied_note=tmp.tied; //shorten the current item to it's group's length tmp.len=group2_len; tmp.dots=0; tmp.tied=true; curr_items.insert(tmp); //create items for the remaining lengths (and a note_END for the just created shortened note) int t=it2->first+group2_len_ticks; itemlist[t].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); list lens=parse_note_len(len_ticks_remaining,t-last_measure,emphasize_list,true,true); unsigned tmppos=t; int n_lens=lens.size(); int count=0; for (list::iterator x=lens.begin(); x!=lens.end(); x++) { if (heavyDebugMsg) cout << "\t\twhile regrouping: partial note with len="<len<<", dots="<dots<len,x->dots, tie, tmp.begin_tick, tmp.source_part, tmp.source_event) ); tmppos+=calc_len(x->len,x->dots); itemlist[tmppos].insert( FloItem(FloItem::NOTE_END,tmp.pos,0,0) ); } } else //nothing to do? { if (heavyDebugMsg) cout << "\t\tnothing to do" << endl; it++; } } else it++; goto group_them_again; //do it again } } } //draw a pixmap centered void ScoreCanvas::draw_pixmap(QPainter& p, int x, int y, const QPixmap& pm) { if (heavyDebugMsg) cout << "drawing pixmap with size="<size()); } void ScoreCanvas::draw_note_lines(QPainter& p, int y, bool reserve_akkolade_space) { int xbegin = reserve_akkolade_space ? AKKOLADE_LEFTMARGIN+AKKOLADE_WIDTH+AKKOLADE_RIGHTMARGIN : 0; int xend=width(); // FINDMICHJETZT y is uninitialized! p.setPen(mycolors[DEFAULT]); for (int i=0;i<5;i++) p.drawLine(xbegin, y + i*YLEN - 2*YLEN, xend, y + i*YLEN - 2*YLEN); } void staff_t::calc_item_pos() { //this has to be KEY_C or KEY_C_B and nothing else, //because only with these two keys the next (initial) //key signature is properly drawn. MusECore::key_enum curr_key=MusECore::KEY_C; int pos_add=0; max_y_coord=0; min_y_coord=0; for (ScoreItemList::iterator it2=itemlist.begin(); it2!=itemlist.end(); it2++) { for (set::iterator it=it2->second.begin(); it!=it2->second.end();it++) { it->x=it2->first * parent->pixels_per_whole()/TICKS_PER_WHOLE +pos_add; //if this changes, also change the line(s) with YLEN (but not all). don't change it. it->y=2*YLEN - (it->pos.height-2)*YLEN/2; if (it->type==FloItem::NOTE) { if (it->y > max_y_coord) max_y_coord=it->y; if (it->y < min_y_coord) min_y_coord=it->y; it->x+=parent->note_x_indent() + it->shift*NOTE_SHIFT; switch (it->len) { case 0: it->pix=pix_whole; break; case 1: it->pix=pix_half; break; default: it->pix=pix_quarter; break; } it->stem_x=it->x; if (it->ausweich) { if ((it->stem==UPWARDS) || (it->len==0)) it->x += it->pix->width()-1; //AUSWEICH_X else it->x -= it->pix->width()-1; //AUSWEICH_X } //if there's a tie, try to find the tie's destination and set is_tie_dest if (it->tied) { set::iterator dest; set& desttime = itemlist[it2->first+calc_len(it->len,it->dots)]; for (dest=desttime.begin(); dest!=desttime.end();dest++) if ((dest->type==FloItem::NOTE) && (dest->pos==it->pos)) { dest->is_tie_dest=true; dest->tie_from_x=it->x; break; } if (dest==desttime.end()) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: did not find destination note for tie!" << endl; } } else if (it->type==FloItem::REST) { switch (it->len) { case 0: it->pix=pix_r1; break; case 1: it->pix=pix_r2; break; case 2: it->pix=pix_r4; break; case 3: it->pix=pix_r8; break; case 4: it->pix=pix_r16; break; case 5: it->pix=pix_r32; break; } it->x+=parent->note_x_indent() + (it->ausweich ? REST_AUSWEICH_X : 0); //AUSWEICH_X } else if (it->type==FloItem::BAR) { //nothing to do :) } else if (it->type==FloItem::TIME_SIG) { int add=calc_timesig_width(it->num, it->denom); pos_add+=add; } else if (it->type==FloItem::KEY_CHANGE) { MusECore::key_enum new_key=it->key; list aufloes_list=calc_accidentials(curr_key, clef, new_key); list new_acc_list=calc_accidentials(new_key, clef); int n_acc_drawn=aufloes_list.size() + new_acc_list.size(); pos_add+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST; curr_key=new_key; } } } max_y_coord+= (pix_quarter->height()/2 +NOTE_YDIST/2); min_y_coord-= (pix_quarter->height()/2 +NOTE_YDIST/2); } void ScoreCanvas::calc_pos_add_list() { using MusEGlobal::sigmap; using MusECore::iSigEvent; pos_add_list.clear(); //process time signatures for (iSigEvent it=MusEGlobal::sigmap.begin(); it!=MusEGlobal::sigmap.end(); it++) pos_add_list[it->second->tick]+=calc_timesig_width(it->second->sig.z, it->second->sig.n); //process key changes //this has to be KEY_C or KEY_C_B and nothing else, //because only with these two keys the next (initial) //key signature is properly calculated. MusECore::key_enum curr_key=MusECore::KEY_C; for (MusECore::iKeyEvent it=MusEGlobal::keymap.begin(); it!=MusEGlobal::keymap.end(); it++) { MusECore::key_enum new_key=it->second.key; list aufloes_list=calc_accidentials(curr_key, VIOLIN, new_key); //clef argument is unnecessary list new_acc_list=calc_accidentials(new_key, VIOLIN); //in this case int n_acc_drawn=aufloes_list.size() + new_acc_list.size(); pos_add_list[it->second.tick]+=n_acc_drawn*KEYCHANGE_ACC_DIST+ KEYCHANGE_ACC_LEFTDIST+ KEYCHANGE_ACC_RIGHTDIST; curr_key=new_key; } emit pos_add_changed(); } void ScoreCanvas::draw_items(QPainter& p, int y, staff_t& staff, int x1, int x2) { int from_tick, to_tick; ScoreItemList::iterator from_it, to_it; //drawing too much isn't bad. drawing too few is. from_tick=x_to_tick(x1); from_it=staff.itemlist.lower_bound(from_tick); //from_it now contains the first time which is fully drawn //however, the previous beat could still be relevant, when it's //partly drawn. so we decrement from_it if (from_it!=staff.itemlist.begin()) from_it--; //decrement until we're at a time with a bar //otherwise, drawing accidentials will be broken while (from_it!=staff.itemlist.begin() && from_it->second.find(FloItem(FloItem::BAR))==from_it->second.end()) from_it--; to_tick=x_to_tick(x2); to_it=staff.itemlist.upper_bound(to_tick); //to_it now contains the first time which is not drawn at all any more //however, a tie from 1:04 to 2:01 is stored in 2:01, not in 1:04, //so for drawing ties, we need to increment to_it, so that the //"first time not drawn at all any more" is the last which gets //actually drawn. if (to_it!=staff.itemlist.end()) to_it++; //do one tick more than necessary. this will draw ties draw_items(p,y, staff, from_it, to_it); } void ScoreCanvas::draw_items(QPainter& p, int y, staff_t& staff) { draw_items(p,y, staff,x_pos,x_pos+width()-x_left); } void ScoreCanvas::draw_items(QPainter& p, int y_offset, staff_t& staff, ScoreItemList::iterator from_it, ScoreItemList::iterator to_it) { // init accidentials properly vorzeichen_t curr_accidential[7]; vorzeichen_t default_accidential[7]; MusECore::key_enum curr_key; curr_key=key_at_tick(from_it->first).key; list new_acc_list=calc_accidentials(curr_key, staff.clef); vorzeichen_t new_accidential = is_sharp_key(curr_key) ? SHARP : B; for (int i=0;i<7;i++) curr_accidential[i]=default_accidential[i]=NONE; for (list::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++) default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential; for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++) { if (heavyDebugMsg) cout << "at t="<first << endl; int upstem_y1 = -1, upstem_y2=-1, upstem_x=-1, upflag=-1; int downstem_y1 = -1, downstem_y2=-1, downstem_x=-1, downflag=-1; for (set::iterator it=it2->second.begin(); it!=it2->second.end();it++) { if (it->type==FloItem::NOTE) { if (heavyDebugMsg) { cout << "\tNOTE at line"<pos.height<<" with acc.="<pos.vorzeichen<<", len="<len); for (int i=0;idots;i++) cout << "."; cout << " , stem="; if (it->stem==UPWARDS) cout << "UPWARDS"; else cout << "DOWNWARDS"; cout << " , shift="<shift<<", ausweich="<ausweich<<", "; if (!it->tied) cout << "un"; cout << "tied, is_tie_dest="<is_tie_dest<len!=0) //only for non-whole notes the stems are relevant! { if (it->stem==UPWARDS) { if (upstem_y1 == -1) upstem_y1=it->y; upstem_y2=it->y; if ((upflag!=-1) && (upflag!=it->len)) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: upflag != this->flag" << endl; upflag=it->len; if ((upstem_x!=-1) && (upstem_x!=it->stem_x )) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: upstem_x != x_result" << endl; upstem_x=it->stem_x; } else { if (downstem_y1 == -1) downstem_y1=it->y; downstem_y2=it->y; if ((downflag!=-1) && (downflag!=it->len)) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: downflag != this->flag" << endl; downflag=it->len; if ((downstem_x!=-1) && (downstem_x!=it->stem_x)) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: downstem_x != x_result" << endl; downstem_x=it->stem_x; //important: before the below calculation! } } if (it->pos.height <= 0) //we need auxiliary lines on the bottom? { p.setPen(mycolors[DEFAULT]); for (int i=0; i>=it->pos.height; i-=2) p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2); } else if (it->pos.height >= 12) //we need auxiliary lines on the top? { p.setPen(mycolors[DEFAULT]); for (int i=12; i<=it->pos.height; i+=2) p.drawLine(it->x-it->pix->width()*AUX_LINE_LEN/2 -x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2,it->x+it->pix->width()*AUX_LINE_LEN/2-x_pos+x_left,y_offset + 2*YLEN - (i-2)*YLEN/2); } it->is_active= ( (MusEGlobal::song->cpos() >= it->source_event->tick() + it->source_part->tick()) && (MusEGlobal::song->cpos() < it->source_event->endTick() + it->source_part->tick()) ); int color_index; switch (coloring_mode) { case COLOR_MODE_BLACK: color_index=DEFAULT; break; case COLOR_MODE_PART: color_index=it->source_part->colorIndex(); break; case COLOR_MODE_VELO: color_index=VELO_PIXMAP_BEGIN + it->source_event->velo(); break; default: cerr << "ERROR: THIS CANNOT HAPPEN: coloring_mode (="<source_event->selected()) color_index=SELECTED_PIXMAP; if (MusEGlobal::audio->isPlaying() && it->is_active) color_index=HIGHLIGHTED_PIXMAP; draw_pixmap(p,it->x -x_pos+x_left,y_offset + it->y,it->pix[color_index]); //draw dots int x_dot=DOT_XBEGIN; int y_dot; if (modulo(it->pos.height, 2) == 0) //note is on a line? y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space else //note is between two lines? y_dot=YLEN * 0.1; if (it->stem==DOWNWARDS) y_dot=-y_dot; //else y_dot=y_dot; for (int i=0;idots;i++) { draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[color_index]); x_dot+=DOT_XDIST; } //draw accidentials if (it->pos.vorzeichen != curr_accidential[modulo(it->pos.height,7)]) { QPixmap* acc_pix; switch (it->pos.vorzeichen) { case NONE: acc_pix=pix_noacc; break; case SHARP: acc_pix=pix_sharp; break; case B: acc_pix=pix_b; break; default: cerr << "ERROR: THIS CANNOT HAPPEN: it->pos.vorzeichen (="<pos.vorzeichen<<") is invalid! defaulting to NONE." << endl; acc_pix=pix_noacc; break; } draw_pixmap(p,it->x-ACCIDENTIAL_DIST -x_pos+x_left,y_offset + it->y, acc_pix[color_index]); curr_accidential[modulo(it->pos.height,7)]=it->pos.vorzeichen; } //if needed, draw tie if (it->is_tie_dest) { if (heavyDebugMsg) cout << "drawing tie" << endl; draw_tie(p,it->tie_from_x-x_pos+x_left,it->x -x_pos+x_left,y_offset + it->y, (it->len==0) ? true : (it->stem==DOWNWARDS) , mycolors[DEFAULT]); // in english: "if it's a whole note, tie is upwards (true). if not, tie is upwards if // stem is downwards and vice versa" } } else if (it->type==FloItem::REST) { if (heavyDebugMsg) { cout << "\tREST at line"<pos.height<<" with len="<len); for (int i=0;idots;i++) cout << "."; cout << " , ausweich="<ausweich<x -x_pos+x_left,y_offset + it->y,*it->pix); //draw dots int x_dot=DOT_XBEGIN_REST; int y_dot; if (modulo(it->pos.height, 2) == 0) //rest is on a line? y_dot=YLEN * 0.33; // actually 0.5, but that would be _exactly_ in the space else //note is between two lines? y_dot=YLEN * 0.1; if (it->len!=0) // all rests except the whole are treated as y_dot=-y_dot; // if they had a downwards stem for (int i=0;idots;i++) { draw_pixmap(p,it->x+x_dot -x_pos+x_left,y_offset + it->y+y_dot,pix_dot[DEFAULT]); x_dot+=DOT_XDIST; } } else if (it->type==FloItem::BAR) { if (heavyDebugMsg) cout << "\tBAR" << endl; p.setPen(mycolors[DEFAULT]); p.drawLine(it->x -x_pos+x_left,y_offset -2*YLEN,it->x -x_pos+x_left,y_offset +2*YLEN); for (int i=0;i<7;i++) curr_accidential[i]=default_accidential[i]; } else if (it->type==FloItem::TIME_SIG) { if (heavyDebugMsg) cout << "\tTIME SIGNATURE: "<num<<"/"<denom<x - x_pos+x_left, y_offset, it->num, it->denom); } else if (it->type==FloItem::KEY_CHANGE) { MusECore::key_enum new_key=it->key; if (heavyDebugMsg) cout << "\tKEY CHANGE: from "<minor; const int str_y_coord = -4 * YLEN + 2 /* margin */; const QString kstr = MusECore::KeyEvent::keyToString(new_key, is_minor); const int x_1 = -x_pos + x_left; const int kstrx = x_1 + it->x + KEYCHANGE_ACC_LEFTDIST; // Don't bother drawing the string if it would begin just before the left edge // because the preamble will already change to show the new key. if(!preamble_contains_timesig || kstrx >= x_1) p.drawText(kstrx, y_offset + str_y_coord, kstr); list aufloes_list=calc_accidentials(curr_key, staff.clef, new_key); list new_acc_list=calc_accidentials(new_key, staff.clef); // cancel accidentials from curr_key draw_accidentials(p, it->x + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, aufloes_list, pix_noacc[DEFAULT]); // draw all accidentials from new_key QPixmap* pix = is_sharp_key(new_key) ? &pix_sharp[DEFAULT] : &pix_b[DEFAULT]; vorzeichen_t new_accidential = is_sharp_key(new_key) ? SHARP : B; draw_accidentials(p, it->x + aufloes_list.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_LEFTDIST - x_pos+x_left, y_offset, new_acc_list, *pix); for (int i=0;i<7;i++) curr_accidential[i]=default_accidential[i]=NONE; for (list::iterator acc_it=new_acc_list.begin(); acc_it!=new_acc_list.end(); acc_it++) default_accidential[*acc_it % 7]=curr_accidential[*acc_it % 7]=new_accidential; curr_key=new_key; } } p.setPen(mycolors[DEFAULT]); //note: y1 is bottom, y2 is top! if (upstem_x!=-1) { upstem_x=upstem_x-pix_quarter[0].width()/2 +pix_quarter[0].width() -1; p.drawLine(upstem_x -x_pos+x_left, y_offset + upstem_y1, upstem_x -x_pos+x_left, y_offset + upstem_y2-STEM_LEN); if (upflag>=3) //if the note needs a flag p.drawPixmap(upstem_x -x_pos+x_left,y_offset + upstem_y2-STEM_LEN,pix_flag_up[upflag-3]); } if (downstem_x!=-1) { downstem_x=downstem_x-pix_quarter[0].width()/2; p.drawLine(downstem_x -x_pos+x_left, y_offset + downstem_y1+STEM_LEN, downstem_x -x_pos+x_left, y_offset + downstem_y2); if (downflag>=3) //if the note needs a flag p.drawPixmap(downstem_x -x_pos+x_left,y_offset + downstem_y1+STEM_LEN-pix_flag_down[downflag-3].height(),pix_flag_down[downflag-3]); } } } bool ScoreCanvas::need_redraw_for_hilighting() { for (list::iterator it=staves.begin(); it!=staves.end(); it++) if (need_redraw_for_hilighting(it->itemlist)) return true; return false; } bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList& itemlist) { return need_redraw_for_hilighting(itemlist, x_pos,x_pos+width()-x_left); } bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList& itemlist, int x1, int x2) { int from_tick, to_tick; ScoreItemList::iterator from_it, to_it; from_tick=x_to_tick(x1); from_it=itemlist.lower_bound(from_tick); //from_it now contains the first time which is fully drawn //however, the previous beat could still be relevant, when it's //partly drawn. so we decrement from_it if (from_it!=itemlist.begin()) from_it--; to_tick=x_to_tick(x2); to_it=itemlist.upper_bound(to_tick); //to_it now contains the first time which is not drawn at all any more return need_redraw_for_hilighting(from_it, to_it); } bool ScoreCanvas::need_redraw_for_hilighting(ScoreItemList::iterator from_it, ScoreItemList::iterator to_it) { //if we aren't playing, there will never be a need for redrawing due to highlighting things if (MusEGlobal::audio->isPlaying()==false) return false; for (ScoreItemList::iterator it2=from_it; it2!=to_it; it2++) for (set::iterator it=it2->second.begin(); it!=it2->second.end();it++) if (it->type==FloItem::NOTE) { bool is_active= ( (MusEGlobal::song->cpos() >= it->source_event->tick() + it->source_part->tick()) && (MusEGlobal::song->cpos() < it->source_event->endTick() + it->source_part->tick()) ); if (it->is_active != is_active) return true; } return false; } int clef_height(clef_t clef) { switch (clef) //CLEF_MARKER { case VIOLIN: return 4; case BASS: return 8; default: cerr << "ERROR: ILLEGAL FUNCTION CALL in clef_height()" << endl; return 6; } } #define TIMESIG_LEFTMARGIN 5 #define TIMESIG_RIGHTMARGIN 5 #define DIGIT_YDIST 9 #define DIGIT_WIDTH 12 #define CLEF_LEFTMARGIN 5 #define CLEF_RIGHTMARGIN 5 void ScoreCanvas::draw_preamble(QPainter& p, int y_offset, clef_t clef, bool reserve_akkolade_space, bool with_akkolade) { int x_left_old=x_left; int tick=x_to_tick(x_pos); // maybe draw akkolade ---------------------------------------------- if (reserve_akkolade_space) { if (with_akkolade) { p.setBrush(mycolors[DEFAULT]); draw_akkolade(p, AKKOLADE_LEFTMARGIN, y_offset+GRANDSTAFF_DISTANCE/2); } x_left= AKKOLADE_LEFTMARGIN + AKKOLADE_WIDTH + AKKOLADE_RIGHTMARGIN; } else x_left=0; // draw clef -------------------------------------------------------- QPixmap* pix_clef= (clef==BASS) ? pix_clef_bass : pix_clef_violin; int y_coord=2*YLEN - ( clef_height(clef) -2)*YLEN/2; draw_pixmap(p,x_left + CLEF_LEFTMARGIN + pix_clef->width()/2,y_offset + y_coord,*pix_clef); x_left+= CLEF_LEFTMARGIN + pix_clef->width() + CLEF_RIGHTMARGIN; // draw accidentials ------------------------------------------------ if (preamble_contains_keysig) { MusECore::KeyEvent key=key_at_tick(tick); QPixmap* pix_acc=is_sharp_key(key.key) ? &pix_sharp[DEFAULT] : &pix_b[DEFAULT]; list acclist=calc_accidentials(key.key,clef); const int str_y_coord = -4 * YLEN + 2 /* margin */; const QString kstr = key.keyString(); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 const int ksw = fontMetrics().horizontalAdvance(kstr); #else const int ksw = fontMetrics().width(kstr); #endif int kstrx = x_left - ksw / 2; if(kstrx < 0) kstrx = 0; p.drawText(kstrx, y_offset + str_y_coord, kstr); x_left+=KEYCHANGE_ACC_LEFTDIST; draw_accidentials(p,x_left, y_offset, acclist ,*pix_acc); x_left+=acclist.size()*KEYCHANGE_ACC_DIST + KEYCHANGE_ACC_RIGHTDIST; } // draw time signature ---------------------------------------------- if (preamble_contains_timesig) { x_left+=TIMESIG_LEFTMARGIN; timesig_t timesig=timesig_at_tick(tick); draw_timesig(p, x_left, y_offset, timesig.num, timesig.denom); x_left+=calc_timesig_width(timesig.num, timesig.denom)+TIMESIG_RIGHTMARGIN; } // draw bar --------------------------------------------------------- p.setPen(mycolors[DEFAULT]); p.drawLine(x_left,y_offset -2*YLEN,x_left,y_offset +2*YLEN); if (x_left_old!=x_left) { emit viewport_width_changed(viewport_width()); emit preamble_width_changed(x_left); } } void ScoreCanvas::draw_timesig(QPainter& p, int x, int y_offset, int num, int denom) { int num_width=calc_number_width(num); int denom_width=calc_number_width(denom); int width=((num_width > denom_width) ? num_width : denom_width); int num_indent=(width-num_width)/2 + TIMESIG_LEFTMARGIN; int denom_indent=(width-denom_width)/2 + TIMESIG_LEFTMARGIN; draw_number(p, x+num_indent, y_offset -DIGIT_YDIST, num); draw_number(p, x+denom_indent, y_offset +DIGIT_YDIST, denom); } int calc_timesig_width(int num, int denom) { int num_width=calc_number_width(num); int denom_width=calc_number_width(denom); int width=((num_width > denom_width) ? num_width : denom_width); return width+TIMESIG_LEFTMARGIN+TIMESIG_RIGHTMARGIN; } int calc_number_width(int n) { string str=IntToStr(n); return (str.length()*DIGIT_WIDTH); } void ScoreCanvas::draw_number(QPainter& p, int x, int y, int n) { string str=IntToStr(n); int curr_x=x+DIGIT_WIDTH/2; for (size_t i=0;i::iterator it=staves.begin(); it!=staves.end(); it++) if (it->type==GRAND_TOP) { reserve_akkolade_space=true; break; } for (list::iterator it=staves.begin(); it!=staves.end(); it++) { //TODO: maybe only draw visible staves? draw_note_lines(p,it->y_draw - y_pos, reserve_akkolade_space); draw_preamble(p,it->y_draw - y_pos, it->clef, reserve_akkolade_space, (it->type==GRAND_TOP)); p.setClipRect(x_left+1,0,p.device()->width(),p.device()->height()); draw_items(p,it->y_draw - y_pos, *it); p.setClipping(false); } if (have_lasso) { p.setPen(Qt::blue); p.setBrush(Qt::NoBrush); p.drawRect(lasso); } if (debugMsg) cout << "drawing done." << endl; } list calc_accidentials(MusECore::key_enum key, clef_t clef, MusECore::key_enum next_key) { list result; int violin_sharp_pos[]={10,7,11,8,5,9,6}; //CLEF_MARKER int violin_b_pos[]={6,9,5,8,4,7,3}; int bass_sharp_pos[]={8,5,9,6,3,7,4}; int bass_b_pos[]={4,7,3,6,2,5,1}; int* accidential_pos = NULL; switch (clef) { case VIOLIN: accidential_pos = is_sharp_key(key) ? violin_sharp_pos : violin_b_pos; break; case BASS: accidential_pos = is_sharp_key(key) ? bass_sharp_pos : bass_b_pos; break; } int begin=0; if (is_sharp_key(key)==is_sharp_key(next_key)) //same kind of key (both b or both #)? begin=n_accidentials(next_key); else begin=0; int end=n_accidentials(key); for (int i=begin; i::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->first<=t; it++) x+=it->second; return x; } int ScoreCanvas::delta_tick_to_delta_x(int t) { return t*pixels_per_whole()/TICKS_PER_WHOLE; } int ScoreCanvas::calc_posadd(int t) { int result=0; for (std::map::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->firstsecond; return result; } //doesn't round mathematically correct, but i don't think this //will be a problem, because a tick is pretty small int ScoreCanvas::x_to_tick(int x) { int t=TICKS_PER_WHOLE * x/pixels_per_whole(); int min_t=0; for (std::map::iterator it=pos_add_list.begin(); it!=pos_add_list.end() && it->firstfirst; x-=it->second; t=TICKS_PER_WHOLE * x/pixels_per_whole(); } return t > min_t ? t : min_t; } MusECore::KeyEvent ScoreCanvas::key_at_tick(int t_) { unsigned int t= (t_>=0) ? t_ : 0; return MusEGlobal::keymap.keyAtTick(t); } timesig_t ScoreCanvas::timesig_at_tick(int t_) { timesig_t tmp; unsigned int t= (t_>=0) ? t_ : 0; MusEGlobal::sigmap.timesig(t, tmp.num, tmp.denom); return tmp; } int ScoreCanvas::height_to_pitch(int h, clef_t clef) { int foo[]={0,2,4,5,7,9,11}; switch(clef) //CLEF_MARKER { case VIOLIN: return foo[modulo(h,7)] + ( divide_floor(h,7)*12 ) + 60; case BASS: return foo[modulo((h-5),7)] + ( divide_floor(h-5,7)*12 ) + 48; default: cerr << "ERROR: THIS SHOULD NEVER HAPPEN: unknown clef in height_to_pitch" << endl; return 60; } } int ScoreCanvas::height_to_pitch(int h, clef_t clef, MusECore::key_enum key) { int add=0; list accs=calc_accidentials(key,clef); for (list::iterator it=accs.begin(); it!=accs.end(); it++) { if (modulo(*it,7) == modulo(h,7)) { add=is_sharp_key(key) ? 1 : -1; break; } } return height_to_pitch(h,clef)+add; } int ScoreCanvas::y_to_height(int y) { return int(nearbyint(float(2*YLEN - y)*2.0/YLEN))+2 ; } int ScoreCanvas::y_to_pitch(int y, int t, clef_t clef) { return height_to_pitch(y_to_height(y), clef, key_at_tick(t).key); } #define DRAG_INIT_DISTANCE 5 void ScoreCanvas::mousePressEvent (QMouseEvent* event) { keystate=event->modifiers(); bool ctrl=keystate & Qt::ControlModifier; // always round DOWN. // because the "area" of a beat goes from "beat_begin" to "nextbeat_begin-1", // but notes are drawn in the middle of that area! list::iterator staff_it=staff_at_y(event->y() + y_pos); int y=event->y() + y_pos - staff_it->y_draw; int x=event->x()+x_pos-x_left; int tick=flo_quantize_floor(x_to_tick(x), quant_ticks()); if (staff_it!=staves.end()) { if (event->x() <= x_left) //clicked in the preamble? { if (event->button() == Qt::RightButton) //right-click? { current_staff=staff_it; staff_menu->popup(event->globalPos()); } else if (event->button() == Qt::MidButton) //middle click? { remove_staff(staff_it); } else if (event->button() == Qt::LeftButton) //left click? { current_staff=staff_it; setCursor(Qt::SizeAllCursor); dragging_staff=true; } } else { ScoreItemList& itemlist=staff_it->itemlist; if (debugMsg) cout << "mousePressEvent at "<len, found->dots); } } while (found->tied); int total_begin=set_it->begin_tick; int total_end=t; int this_begin=tick; int this_end=this_begin+calc_len(set_it->len, set_it->dots); set_selected_part(set_it->source_part); //that's the only note corresponding to the event? if (this_begin==total_begin && this_end==total_end) { if (x < set_it->x) mouse_x_drag_operation=BEGIN; else mouse_x_drag_operation=LENGTH; } //that's NOT the only note? else { if (this_begin==total_begin) mouse_x_drag_operation=BEGIN; else if (this_end==total_end) mouse_x_drag_operation=LENGTH; else mouse_x_drag_operation=NO_OP; } if (debugMsg) cout << "you clicked at a note with begin at "<begin_tick<<" and end at "<source_part << endl; if (set_it->source_part == NULL) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: set_it->source_part is NULL!" << endl; clicked_event_ptr=set_it->source_event; dragged_event=*set_it->source_event; original_dragged_event=dragged_event.clone(); set_dragged_event_part(set_it->source_part); if ((mouse_erases_notes) || (event->button()==Qt::MidButton)) //erase? { MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent,dragged_event, dragged_event_part, false, false)); } else if (event->button()==Qt::LeftButton) //edit? { setMouseTracking(true); dragging=true; drag_cursor_changed=false; } } else //we found nothing? if (event->button()==Qt::LeftButton) { if (mouse_inserts_notes) { const MusECore::Part* curr_part = NULL; set possible_dests=staff_it->parts_at_tick(tick); if (!possible_dests.empty()) { if (possible_dests.size()==1) curr_part=*possible_dests.begin(); else { if (possible_dests.find(selected_part)!=possible_dests.end()) curr_part=selected_part; else QMessageBox::information(this, tr("Ambiguous part"), tr("There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part.")); } } else QMessageBox::information(this, tr("No part"), tr("There are no parts you could add the note to.")); if (curr_part!=NULL) { signed int relative_tick=(signed) tick - curr_part->tick(); if (relative_tick<0) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: relative_tick is negative!" << endl; if (!ctrl) deselect_all(); MusECore::Event newevent(MusECore::Note); newevent.setPitch(y_to_pitch(y,tick, staff_it->clef)); newevent.setVelo(note_velo); newevent.setVeloOff(note_velo_off); newevent.setTick(relative_tick); newevent.setLenTick((new_len>0)?new_len:last_len); newevent.setSelected(true); // No need to select clones, AddEvent operation below will take care of that. if (flo_quantize(newevent.lenTick(), quant_ticks()) <= 0) { newevent.setLenTick(quant_ticks()); if (debugMsg) cout << "inserted note's length would be invisible after quantisation (too short)." << endl << " setting it to " << newevent.lenTick() << endl; } if (newevent.endTick() > curr_part->lenTick()) { if (debugMsg) cout << "clipping inserted note from len="<undo(); MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent,dragged_event, dragged_event_part, false, false)); } else { last_len=flo_quantize(dragged_event.lenTick(), quant_ticks()); } } if (mouse_operation==NO_OP && !inserting) { if (event->button()==Qt::LeftButton) if (!ctrl) deselect_all(); MusEGlobal::song->applyOperation( UndoOp(UndoOp::SelectEvent, *clicked_event_ptr, selected_part, !clicked_event_ptr->selected(), clicked_event_ptr->selected()), MusECore::Song::OperationExecuteUpdate); } setMouseTracking(false); setCursor(active_tool_cursor); inserting=false; dragging=false; drag_cursor_changed=false; undo_started=false; x_scroll_speed=0; x_scroll_pos=0; } if (dragging_staff && event->button()==Qt::LeftButton) { int y=event->y()+y_pos; list::iterator mouse_staff=staff_at_y(y); if (mouse_staff!=staves.end()) { if ( ((mouse_staff->type==NORMAL) && (y >= mouse_staff->y_draw-2*YLEN) && (y <= mouse_staff->y_draw+2*YLEN)) || ((mouse_staff->type==GRAND_TOP) && (y >= mouse_staff->y_draw-2*YLEN)) || ((mouse_staff->type==GRAND_BOTTOM) && (y <= mouse_staff->y_draw+2*YLEN)) ) merge_staves(mouse_staff, current_staff); else if (y >= mouse_staff->y_draw+2*YLEN) //will never happen when mouse_staff->type==GRAND_TOP move_staff_below(mouse_staff, current_staff); else if (y <= mouse_staff->y_draw-2*YLEN) //will never happen when mouse_staff->type==GRAND_BOTTOM move_staff_above(mouse_staff, current_staff); } dragging_staff=false; setCursor(active_tool_cursor); y_scroll_speed=0; y_scroll_pos=0; } if (have_lasso && event->button()==Qt::LeftButton) { if (!ctrl) deselect_all(); set already_processed; for (list::iterator it=staves.begin(); it!=staves.end(); it++) it->apply_lasso(lasso.translated(x_pos-x_left, y_pos - it->y_draw), already_processed); MusEGlobal::song->update(SC_SELECTION); have_lasso=false; redraw(); } } #define PITCH_DELTA 5 void ScoreCanvas::mouseMoveEvent (QMouseEvent* event) { keystate=event->modifiers(); bool ctrl=keystate & Qt::ControlModifier; if (dragging) { int dx=event->x()-mouse_down_pos.x(); int dy=event->y()-mouse_down_pos.y(); int x=event->x()+x_pos-x_left; int tick=flo_quantize_floor(x_to_tick(x), quant_ticks()); if ((drag_cursor_changed==false) && ((dx!=0) || (dy!=0))) { setCursor(Qt::SizeAllCursor); drag_cursor_changed=true; } if (mouse_operation==NO_OP) { if ((abs(dx)>DRAG_INIT_DISTANCE) && (mouse_x_drag_operation!=NO_OP)) { if (debugMsg) cout << "mouse-operation is now "<DRAG_INIT_DISTANCE) { if (debugMsg) cout << "mouse-operation is now PITCH" << endl; mouse_operation=PITCH; setCursor(Qt::SizeVerCursor); } if (mouse_operation!=NO_OP) { if (!inserting && clicked_event_ptr->selected()==false) { if (!ctrl) deselect_all(); MusEGlobal::song->applyOperation( UndoOp(UndoOp::SelectEvent, *clicked_event_ptr, selected_part, true, clicked_event_ptr->selected()), MusECore::Song::OperationExecuteUpdate); } old_pitch=-1; old_dest_tick=INT_MAX; old_len=-1; } } int new_pitch; switch (mouse_operation) { case NO_OP: break; case PITCH: if (heavyDebugMsg) cout << "trying to change pitch, delta="<<-nearbyint((float)dy/PITCH_DELTA)< 127) new_pitch=127; if (new_pitch != old_pitch) { if (debugMsg) cout << "changing pitch, delta="<undo(); undo_started=transpose_notes(part_to_set(dragged_event_part),1, new_pitch-original_dragged_event.pitch()); old_pitch=new_pitch; } break; case BEGIN: { signed relative_tick=tick-signed(dragged_event_part->tick()); unsigned dest_tick; if (relative_tick >= 0) dest_tick=relative_tick; else { dest_tick=0; if (debugMsg) cout << "not moving note before begin of part; setting it directly to the begin" << endl; } if (dest_tick != old_dest_tick) { if (undo_started) MusEGlobal::song->undo(); //FINDMICH EXTEND undo_started=move_notes(part_to_set(dragged_event_part),1, (signed)dest_tick-original_dragged_event.tick()); old_dest_tick=dest_tick; } } break; case LENGTH: tick+=quant_ticks(); if (dragged_event.tick()+old_len + dragged_event_part->tick() != unsigned(tick)) { MusECore::Event tmp=dragged_event.clone(); signed relative_tick=tick-signed(dragged_event_part->tick()); signed new_len=relative_tick-dragged_event.tick(); if (new_len>=0) tmp.setLenTick(new_len); else { tmp.setLenTick(0); if (debugMsg) cout << "not setting len to a negative value. using 0 instead" << endl; } unsigned newpartlen=dragged_event_part->lenTick(); if (tmp.endTick() > dragged_event_part->lenTick()) { if (dragged_event_part->hasHiddenEvents() & MusECore::Part::RightEventsHidden) // do not allow autoexpand { tmp.setLenTick(dragged_event_part->lenTick() - tmp.tick()); if (debugMsg) cout << "resized note would exceed its part; limiting length to " << tmp.lenTick() << endl; } else { newpartlen=tmp.endTick(); if (debugMsg) cout << "resized note would exceeds its part; expanding the part..." << endl; } } if (undo_started) MusEGlobal::song->undo(); MusECore::Undo operations; operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, tmp, dragged_event, dragged_event_part, false, false)); if (newpartlen != dragged_event_part->lenTick()) schedule_resize_all_same_len_clone_parts(dragged_event_part, newpartlen, operations); undo_started=MusEGlobal::song->applyOperationGroup(operations); old_len=new_len; } break; } if ((mouse_operation==LENGTH) || (mouse_operation==BEGIN)) //x-scrolling enabled? { int win_x=event->x(); if (win_x < x_left + SCROLL_MARGIN) { x_scroll_speed=(win_x - (x_left + SCROLL_MARGIN)) * SCROLL_SPEED; if (x_scroll_speed < -SCROLL_SPEED_MAX) x_scroll_speed=-SCROLL_SPEED_MAX; } else if (win_x > width() - SCROLL_MARGIN) { x_scroll_speed=(win_x - (width() - SCROLL_MARGIN)) * SCROLL_SPEED; if (x_scroll_speed > SCROLL_SPEED_MAX) x_scroll_speed=SCROLL_SPEED_MAX; } else x_scroll_speed=0; } else { x_scroll_speed=0; } } if (dragging_staff) //y-scrolling enabled? { int win_y=event->y(); if (win_y < SCROLL_MARGIN) { y_scroll_speed=(win_y - SCROLL_MARGIN) * SCROLL_SPEED; if (y_scroll_speed < -SCROLL_SPEED_MAX) y_scroll_speed=-SCROLL_SPEED_MAX; } else if (win_y > height() - SCROLL_MARGIN) { y_scroll_speed=(win_y - (height() - SCROLL_MARGIN)) * SCROLL_SPEED; if (y_scroll_speed > SCROLL_SPEED_MAX) y_scroll_speed=SCROLL_SPEED_MAX; } else y_scroll_speed=0; } else { y_scroll_speed=0; } if (have_lasso) { lasso=QRect(lasso_start, event->pos()).normalized(); redraw(); } } void ScoreCanvas::heartbeat_timer_event() { if (x_scroll_speed) { int old_xpos=x_pos; x_scroll_pos+=x_scroll_speed*MusEGlobal::heartBeatTimer->interval()/1000.0; int tmp=int(x_scroll_pos); if (tmp!=0) x_pos+=tmp; x_scroll_pos-=tmp; if (x_pos<0) x_pos=0; if (x_pos>canvas_width()) x_pos=canvas_width(); if (old_xpos!=x_pos) emit xscroll_changed(x_pos); } if (y_scroll_speed) { int old_ypos=y_pos; y_scroll_pos+=y_scroll_speed*MusEGlobal::heartBeatTimer->interval()/1000.0; int tmp=int(y_scroll_pos); if (tmp!=0) y_pos+=tmp; y_scroll_pos-=tmp; if (y_pos<0) y_pos=0; if (y_pos>canvas_height()) y_pos=canvas_height(); if (old_ypos!=y_pos) emit yscroll_changed(y_pos); } } void ScoreCanvas::x_scroll_event(int x) { if (debugMsg) cout << "SCROLL EVENT: x="<y_bottom; } emit canvas_height_changed( canvas_height() ); } list::iterator ScoreCanvas::staff_at_y(int y) { for (list::iterator it=staves.begin(); it!=staves.end(); it++) if ((y >= it->y_top) && (y < it->y_bottom)) return it; return staves.end(); } void ScoreCanvas::play_changed(bool) { redraw(); } void ScoreCanvas::config_changed() { if (MusEGlobal::config.canvasBgPixmap.isEmpty()) { setBg(MusEGlobal::config.midiCanvasBg); setBg(QPixmap()); } else { setBg(QPixmap(MusEGlobal::config.canvasBgPixmap)); } redraw(); } void ScoreCanvas::set_tool(int tool) { switch (tool) { case MusEGui::PointerTool: setCursor(QCursor(Qt::ArrowCursor)); mouse_erases_notes=false; mouse_inserts_notes=false; break; case MusEGui::RubberTool: setCursor(*deleteCursor); mouse_erases_notes=true; mouse_inserts_notes=false; break; case MusEGui::PencilTool: setCursor(*pencilCursor); mouse_erases_notes=false; mouse_inserts_notes=true; break; default: cerr << "ERROR: THIS SHOULD NEVER HAPPEN: set_tool called with unknown tool ("<=0) && (val<5)) { int old_len=quant_len(); _quant_power2=val+1; _quant_power2_init=_quant_power2; set_pixels_per_whole(pixels_per_whole() * quant_len() / old_len ); fully_recalculate(); } else { cerr << "ERROR: ILLEGAL FUNCTION CALL: set_quant called with invalid value of "<::iterator it=staves.begin(); it!=staves.end(); it++) it->calc_item_pos(); emit pixels_per_whole_changed(val); if (old_xpos!=0) { x_pos=tick_to_x(tick); if (debugMsg) cout << "x_pos was not zero, readjusting to " << x_pos << endl; emit xscroll_changed(x_pos); } redraw(); } void ScoreCanvas::cleanup_staves() { for (list::iterator it=staves.begin(); it!=staves.end();) { if (it->parts.empty()) staves.erase(it++); else it++; } maybe_close_if_empty(); } void ScoreCanvas::maybe_close_if_empty() { if (staves.empty()) { if (!parent->close()) cerr << "ERROR: THIS SHOULD NEVER HAPPEN: tried to close, but event hasn't been accepted!" << endl; } } void ScoreCanvas::set_velo(int velo) { note_velo=velo; note_velo_init=velo; if (parent->get_apply_velo()) { MusECore::TagEventList tag_list; tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::modify_velocity_items(&tag_list, 0, velo); } } void ScoreCanvas::set_velo_off(int velo) { note_velo_off=velo; note_velo_off_init=velo; if (parent->get_apply_velo()) { MusECore::TagEventList tag_list; tagItems(&tag_list, MusECore::EventTagOptionsStruct(MusECore::TagSelected | MusECore::TagAllParts)); MusECore::modify_off_velocity_items(&tag_list, 0, velo); } } void ScoreCanvas::deselect_all() { set all_parts=get_all_parts(); Undo operations; operations.combobreaker=true; for (set::iterator part=all_parts.begin(); part!=all_parts.end(); part++) for (MusECore::ciEvent event=(*part)->events().begin(); event!=(*part)->events().end(); event++) operations.push_back(UndoOp(UndoOp::SelectEvent, event->second, *part, false, event->second.selected())); MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate); } bool staff_t::cleanup_parts() { bool did_something=false; for (set::iterator it=parts.begin(); it!=parts.end();) { bool valid=false; for (MusECore::iTrack track=MusEGlobal::song->tracks()->begin(); track!=MusEGlobal::song->tracks()->end(); track++) if ((*track)->type() == MusECore::Track::MIDI) { MusECore::PartList* pl=(*track)->parts(); for (MusECore::iPart part=pl->begin(); part!=pl->end(); part++) if (*it == part->second) { valid=true; goto get_out_here2; } } get_out_here2: if (!valid) { parts.erase(it++); did_something=true; } else it++; } if (did_something) update_part_indices(); return did_something; } set staff_t::parts_at_tick(unsigned tick) { set result; for (set::iterator it=parts.begin(); it!=parts.end(); it++) if ((tick >= (*it)->tick()) && (tick<=(*it)->endTick())) result.insert(*it); return result; } void staff_t::apply_lasso(QRect rect, set& already_processed) { Undo operations; for (ScoreItemList::iterator it=itemlist.begin(); it!=itemlist.end(); it++) for (set::iterator it2=it->second.begin(); it2!=it->second.end(); it2++) if (it2->type==FloItem::NOTE) { if (rect.contains(it2->x, it2->y)) if (already_processed.find(it2->source_event)==already_processed.end()) { operations.push_back(UndoOp(UndoOp::SelectEvent,*it2->source_event,it2->source_part, !it2->source_event->selected(),it2->source_event->selected())); // Here we have a choice of whether to allow undoing of selections. // Disabled for now, it's too tedious in use. Possibly make the choice user settable. // operations.push_back(UndoOp(UndoOp::SelectEvent,*it2->source_event,it2->source_part, // !it2->source_event->selected(),it2->source_event->selected(), false)); already_processed.insert(it2->source_event); } } MusEGlobal::song->applyOperationGroup(operations, MusECore::Song::OperationExecuteUpdate); } void ScoreCanvas::set_steprec(bool flag) { srec=flag; } void ScoreCanvas::midi_note(int pitch, int velo) { if (velo) held_notes[pitch]=true; else held_notes[pitch]=false; if ( srec && selected_part && !MusEGlobal::audio->isPlaying() && velo ) steprec->record(selected_part,pitch,quant_ticks(),quant_ticks(),velo,MusEGlobal::globalKeyState&Qt::ControlModifier,MusEGlobal::globalKeyState&Qt::ShiftModifier); } void ScoreCanvas::update_parts() { if (selected_part!=NULL) //if it's null, let it be null selected_part=MusECore::partFromSerialNumber(selected_part_index); if (dragged_event_part!=NULL) //same thing here dragged_event_part=MusECore::partFromSerialNumber(dragged_event_part_index); for (list::iterator it=staves.begin(); it!=staves.end(); it++) it->update_parts(); } void staff_t::update_parts() { parts.clear(); for (set::iterator it=part_indices.begin(); it!=part_indices.end(); it++) parts.insert(MusECore::partFromSerialNumber(*it)); } void staff_t::update_part_indices() { part_indices.clear(); for (set::iterator it=parts.begin(); it!=parts.end(); it++) part_indices.insert((*it)->sn()); } void ScoreEdit::keyPressEvent(QKeyEvent* event) { int key = event->key(); if (key == Qt::Key_Escape) { close(); return; } else if (key == shortcuts[SHRT_TOOL_POINTER].key) { edit_tools->set(MusEGui::PointerTool); return; } else if (key == shortcuts[SHRT_TOOL_PENCIL].key) { edit_tools->set(MusEGui::PencilTool); return; } else if (key == shortcuts[SHRT_TOOL_RUBBER].key) { edit_tools->set(MusEGui::RubberTool); return; } else if (key == shortcuts[SHRT_EVENT_COLOR].key) { if (score_canvas->coloring_mode_init == score_canvas->COLOR_MODE_BLACK) color_velo_action->trigger(); else if (score_canvas->coloring_mode_init == score_canvas->COLOR_MODE_VELO) color_part_action->trigger(); else color_black_action->trigger(); return; } else //Default: { event->ignore(); return; } } void ScoreCanvas::add_new_parts(const std::map< const MusECore::Part*, std::set >& param) { for (list::iterator staff=staves.begin(); staff!=staves.end(); staff++) { for (std::map< const MusECore::Part*, set >::const_iterator it = param.begin(); it!=param.end(); it++) if (staff->parts.find(it->first)!=staff->parts.end()) staff->parts.insert(it->second.begin(), it->second.end()); //staff->cleanup_parts(); // don't cleanup here, because at this point, the parts might exist only // in the operation group. cleanup could remove them immediately staff->update_part_indices(); } fully_recalculate(); } QMenu* ScoreCanvas::toolContextMenu() { QMenu* r_menu = new QMenu(this); QAction* act0 = nullptr; r_menu->addAction(new MenuTitleItem(tr("Tools"), r_menu)); for (unsigned i = 0; i < static_cast(EditToolBar::toolList.size()); ++i) { if ((scoreTools & (1 << i)) == 0) continue; QAction* act = r_menu->addAction(QIcon(**EditToolBar::toolList[i].icon), tr(EditToolBar::toolList[i].tip)); if (MusEGui::EditToolBar::toolShortcuts.contains(1 << i)) { act->setShortcut(MusEGui::shortcuts[MusEGui::EditToolBar::toolShortcuts[1 << i]].key); } act->setData(scoreTools & (1 << i)); act->setCheckable(true); act->setChecked((1 << i) == active_tool); if (!act0) act0 = act; } r_menu->setActiveAction(act0); return r_menu; } void ScoreCanvas::callContextMenu() { QMenu * cm = toolContextMenu(); if (cm) { QAction *act = cm->exec(QCursor::pos()); if (act && act->data().isValid()) { int tool = act->data().toInt(); parent->setEditTool(tool); } delete cm; } } } // namespace MusEGui //the following assertions are made: // pix_quarter.width() == pix_half.width() // pix->width()-1 + 1/2*pix->width() + SHIFT + ADD_SPACE // 10-1+5+3+3=20 <- um so viel wird der taktstrich verschoben // um das doppelte (20*2=40) werden die kleinsten schläge gegeneinander versetzt //note: recalculating event- and itemlists "from zero" // could happen in realtime, as it is pretty fast. // however, this adds unnecessary cpu usage. // it is NO problem to recalc the stuff "from zero" // every time something changes. /* BUGS and potential bugs * o tied notes don't work properly when there's a key-change in * between, for example, when a cis is tied to a des [ will not fix ] * (reason: this actually never happens if dealing with a sane piece) * > o when changing toolbarstate when sharing and immediately after that * changing "share" status, the changed state isn't stored * (could be solved by storing the current window when quitting/saving whatever) * ? pasting in editors sometimes fails oO? ( ERROR: reading eventlist * from clipboard failed. ignoring this one... ) [ not reproducible ] * o test drum controllers * o test old- and new drumtrack recording, steprecording * o velo-controller doesn't work in new-style drum tracks * * CURRENT TODO * o better and visual means for fading etc * o rename E- and A-note to I- and O- * o storing into a template file seems to ignore * the arranger's "MDI-ness", sets is at subwin all the time! * * o add controller editor "like search-and-replace": * acts on all specified type's events, and edits the value: * - apply some MIN or MAX on it * - scale it * - offset it * - only act on values in a certain value range * - maybe do curve-mapping * o remove the silly song type! * o remove the song length spinbox * o logical/physical device mapping: * song only contains logical information: not some "synth port", * but a "synth name", like "Yamaha DX7" or "TR-808". * the global config contains physical information, like * "'Yamaha DX7' is on 'USB Midi Bridge, Output two'", * "'TR-808' is on 'jack-midi port foo, connected to bar'" * if loading a song with unknown synth name, ask the user. * o prerecord feature. * o part templates * * o find and fix FINDMICHJETZT * o fix valgrind problems (the two "FINDMICHJETZT" lines in scoreedit.cpp) * * IMPORTANT TODO * o allow steprec-insert-rest-note to be set to "off" / "unused" * o all places where i added doubleclick-edits: only react on left-click double clicks! * o support "new style" reordering with old style drum tracks as well * (not swapping but inserting!) * * o canvas editor: create clone via "alt+drag" moves window instead * o controller view in score editor * o solo button * o do partial recalculating; recalculating can take pretty long * (0,5 sec) when displaying a whole song in scores * o transpose etc. must also transpose key-pressure events * o transpose: support in-key-transpose * o thin out: remove unneeded ctrl messages * o support edge-scrolling when opening a lasso * o add "dotted quarter" quantize option (for 6/8 beat) * o ticks-to-quarter spinboxes * o mirror most menus to an additional right-click context menu to avoid the long mouse pointer * journey to the menu bar. try to find a way which does not involve duplicate code! * o implement borland-style maximize: free windows do not cover the main menu, even when maximized * o smart range selection: if range markers have been used recently (that is, a dialog with * "range" setting, or they've been modified), default to "in range" or "selected in range" * * o rename stuff with F2 key * * o shrink a part from its beginning as well! watch out for clones! * * less important stuff * o allow "fixating" toolbars? * o quantize-templates (everything is forced into a specified * rhythm) * o part-templates (you specify some notes and a control-chord; * the notes are set according to the chord then) * o add functions like set velo, mod/set velo-off * o use bars instead of flags over groups of 8ths / 16ths etc * o support different keys in different tracks at the same time * calc_pos_add_list and calc_item_pos will be affected by this * calc_pos_add_list must be called before calc_item_pos then, * and calc_item_pos must respect the pos_add_list instead of * keeping its own pos_add variable (which is only an optimisation) * * really unimportant nice-to-haves * o support in-song clef-changes * o draw measure numbers * o use timesig_t in all timesig-stuff * o refuse to resize so that width gets smaller or equal than x_left * o draw a margin around notes which are in a bright color * o support drum tracks in the score editor (x-note-heads etc.) * o drum list: scroll while dragging: probably unnecessary with the "reorder list" function * * * stuff for the other muse developers * o process accurate timesignatures from muse's list (has to be implemented first in muse) * ( (2+2+3)/4 or (3+2+2)/4 instead of 7/4 ) */ /* R O A D M A P * ============= * * 1. finish the score editor, without transposing instruments and * with only a global keymap * * REASON: a score editor with few functions is better than * no score editor at all * * * 2. support transposing by octave-steps * * REASON: the main problem with transposing is, that the * editor needs different key signatures and needs * to align them against each other. this problem * doesn't exist when only transposing by octaves * * * 3. support transposing instruments, but only one * transposing-setting per score window. that is, you won't be * able to display your (C-)strings in the same window as your * B-trumpet. this will be very easy to implement * * REASON: the above problem still exists, but is circumvented * by simply not having to align them against each other * (because they're in different windows) * * * 4. support different transposing instruments in the same score * window. this will be some hassle, because we need to align * the scores properly. for example, when the C-violin has * C-major (no accidentials), then the B-trumpet need some * accidentials. we now must align the staves so that the * "note-after-keychange"s of both staves are again at the * same x-position * * REASON: some solution for that problem must be written. * this is a large step, which atm isn't very important * * * 5. support different keys per track. this wouldn't be that * hard, when 4) is already done; because we then already have * the "align it properly" functionality, and can use it * * REASON: this is only a nice-to-have, which can however be * easily implemented when 4) is done */ muse-4.0.0/src/muse/midiedit/scoreedit.h000066400000000000000000000525461404105311000201470ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // scoreedit.h // (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SCOREEDIT_H__ #define __SCOREEDIT_H__ #include #include "type_defs.h" #include "cobject.h" #include "event.h" #include "view.h" #include "gconfig.h" #include "keyevent.h" #include "cleftypes.h" #include "helper.h" #include "event_tag_list.h" #include #include #include #include #include using std::set; using std::pair; using std::map; using std::list; using std::vector; using std::string; #define TICKS_PER_WHOLE (MusEGlobal::config.division*4) #define SONG_LENGTH (MusEGlobal::song->len()) enum {CMD_COLOR_BLACK, CMD_COLOR_VELO, CMD_COLOR_PART, CMD_SET_NAME, CMD_NOTELEN_1, CMD_NOTELEN_2, CMD_NOTELEN_4, CMD_NOTELEN_8, CMD_NOTELEN_16, CMD_NOTELEN_32, CMD_NOTELEN_LAST, CMD_QUANTIZE, CMD_VELOCITY, CMD_CRESCENDO, CMD_NOTELEN, CMD_TRANSPOSE, CMD_ERASE, CMD_MOVE, CMD_FIXED_LEN, CMD_DELETE_OVERLAPS, CMD_LEGATO, CMD_CUT, CMD_COPY, CMD_COPY_RANGE, CMD_PASTE, CMD_PASTE_DIALOG, CMD_DEL, CMD_SELECT_ALL, CMD_SELECT_NONE, CMD_SELECT_INVERT, CMD_SELECT_ILOOP, CMD_SELECT_OLOOP}; // Forward declarations: class QCloseEvent; class QResizeEvent; class QKeyEvent; class QPainter; class QPixmap; class QScrollBar; class QComboBox; class QAction; class QActionGroup; class QGridLayout; class QToolButton; namespace MusECore { class Part; class StepRec; } namespace MusEGui { class EditToolBar; class ScoreCanvas; class MTScaleFlo; class SpinBox; //--------------------------------------------------------- // ScoreEdit //--------------------------------------------------------- class ScoreEdit : public TopWin { Q_OBJECT private: void closeEvent(QCloseEvent*) override; void init_name(); QGridLayout* mainGrid; QWidget* mainw; MusEGui::EditToolBar* edit_tools; SpinBox* velo_spinbox; SpinBox* velo_off_spinbox; QComboBox* quant_combobox; SpinBox* px_per_whole_spinbox; QAction* preamble_keysig_action; QAction* preamble_timesig_action; QActionGroup* len_actions; QAction* n1_action; QAction* n2_action; QAction* n4_action; QAction* n8_action; QAction* n16_action; QAction* n32_action; QAction* nlast_action; QActionGroup* color_actions; QAction* color_black_action; QAction* color_velo_action; QAction* color_part_action; QMenu* color_menu; QAction* cut_action; QAction* copy_action; QAction* copy_range_action; QAction* paste_action; QAction* paste_dialog_action; QAction* del_action; QAction* select_all_action; QAction* select_none_action; QAction* select_invert_action; QAction* select_iloop_action; QAction* select_oloop_action; QAction* func_quantize_action; QAction* func_notelen_action; QAction* func_velocity_action; QAction* func_cresc_action; QAction* func_transpose_action; QAction* func_erase_action; QAction* func_move_action; QAction* func_fixed_len_action; QAction* func_del_overlaps_action; QAction* func_legato_action; QToolButton* srec; QScrollBar* xscroll; QScrollBar* yscroll; ScoreCanvas* score_canvas; MusEGui::MTScaleFlo* time_bar; bool apply_velo; static set names; QString name; bool set_name(QString newname, bool emit_signal=true, bool emergency_name=false); void keyPressEvent(QKeyEvent*) override; private slots: void menu_command(int); void velo_box_changed(); void velo_off_box_changed(); void quant_combobox_changed(int); void init_shortcuts(); void selection_changed(); void clipboard_changed(); void config_changed(); signals: void isDeleting(MusEGui::TopWin*); void name_changed(); void velo_changed(int); void velo_off_changed(int); public slots: void canvas_width_changed(int); void viewport_width_changed(int); void canvas_height_changed(int); void viewport_height_changed(int); void song_changed(MusECore::SongChangedStruct_t); void focusCanvas() override; public: ScoreEdit(QWidget* parent = nullptr, const char* name = nullptr, unsigned initPos = INT_MAX); ~ScoreEdit() override; void writeStatus(int level, MusECore::Xml& xml) const override; void readStatus(MusECore::Xml& xml) override; static void read_configuration(MusECore::Xml&); static void write_configuration(int, MusECore::Xml&); void storeSettings() override; void add_parts(MusECore::PartList* pl, bool all_in_one=false); QString get_name() { return name; } bool get_apply_velo() { return apply_velo; } bool itemsAreSelected() const; // Appends given tag list with item objects according to options. Avoids duplicate events or clone events. // Special: We 'abuse' a controller event's length, normally 0, to indicate visual item length. void tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const; void setEditTool(int tool); }; enum stem_t { UPWARDS, DOWNWARDS }; enum vorzeichen_t { B=-1, NONE=0, SHARP=1 }; struct note_pos_t { int height; // 0 means "C-line", 1 "D-line" and so on vorzeichen_t vorzeichen; bool operator== (const note_pos_t& that) const { return (this->height==that.height) && (this->vorzeichen == that.vorzeichen); } }; bool operator< (const note_pos_t& a, const note_pos_t& b); class FloEvent { public: enum typeEnum { NOTE_ON = 30, NOTE_OFF = 10, BAR = 20, KEY_CHANGE=23, TIME_SIG=26 }; //the order matters! typeEnum type; unsigned tick; const MusECore::Part* source_part; const MusECore::Event* source_event; int pitch; mutable int vel; mutable int len; int num; int denom; MusECore::key_enum key; bool minor; FloEvent(unsigned ti, int p,int v,int l,typeEnum t, const MusECore::Part* part=nullptr, const MusECore::Event* event=nullptr) { pitch=p; vel=v; len=l; type= t; tick=ti; source_event=event; source_part=part; num=denom=0xdeadbeef; //unused, but valgrind complains if uninited key=MusECore::KEY_C; minor = false; } FloEvent(unsigned ti, typeEnum t, int num_, int denom_) { type=t; num=num_; denom=denom_; tick=ti; source_event=NULL; source_part=NULL; len=vel=pitch=0xdeadbeef; //unused, but valgrind complains if uninited key=MusECore::KEY_C; minor = false; } FloEvent(unsigned ti, typeEnum t, MusECore::key_enum k, bool isMinor) { type=t; key=k; minor = isMinor; tick=ti; source_event=NULL; source_part=NULL; pitch=vel=len=num=denom=0xdeadbeef; //unused, but valgrind complains if uninited } }; class FloItem { public: enum typeEnum { NOTE=21, REST=22, NOTE_END=01, REST_END=02, BAR =10, KEY_CHANGE=13, TIME_SIG=16}; //the order matters! typeEnum type; unsigned begin_tick; const MusECore::Event* source_event; const MusECore::Part* source_part; note_pos_t pos; int len; int dots; bool tied; bool already_grouped; int num; int denom; MusECore::key_enum key; bool minor; mutable stem_t stem; mutable int shift; mutable bool ausweich; mutable bool is_tie_dest; mutable int tie_from_x; mutable int x; mutable int y; mutable int stem_x; mutable QPixmap* pix; mutable bool is_active; QRect bbox() const; FloItem(typeEnum t, note_pos_t p, int l=0,int d=0, bool ti=false, unsigned beg=0, const MusECore::Part* part=NULL, const MusECore::Event* event=NULL) { pos=p; dots=d; len=l; type=t; already_grouped=false; tied=ti; shift=0; ausweich=false; is_tie_dest=false; begin_tick=beg; source_event=event; source_part=part; is_active=false; } FloItem(typeEnum t, int num_, int denom_) { type=t; num=num_; denom=denom_; begin_tick=-1; source_event=NULL; source_part=NULL; } FloItem(typeEnum t, MusECore::key_enum k, bool isMinor) { type=t; key=k; minor = isMinor; begin_tick=-1; source_event=NULL; source_part=NULL; } FloItem(typeEnum t) { type=t; already_grouped=false; tied=false; shift=0; ausweich=false; is_tie_dest=false; begin_tick=-1; source_event=NULL; source_part=NULL; } FloItem() { already_grouped=false; tied=false; shift=0; ausweich=false; is_tie_dest=false; begin_tick=-1; source_event=NULL; source_part=NULL; } bool operator==(const FloItem& that) { if (this->type != that.type) return false; switch(type) { case NOTE: case REST: case NOTE_END: case REST_END: return (this->pos == that.pos); //the following may only occur once in a set //so we don't search for "the time signature with 4/4 //at t=0", but only for "some time signature at t=0" //that's why true is returned, and not some conditional //expression case BAR: case KEY_CHANGE: case TIME_SIG: return true; } } }; struct floComp { bool operator() (const pair& a, const pair& b ) { if (a.first < b.first) return true; if (a.first > b.first) return false; if (a.second.typeb.second.type) return false; return (a.second.pitch b.type) return false; switch(a.type) { case FloItem::NOTE: case FloItem::REST: case FloItem::NOTE_END: case FloItem::REST_END: return (a.pos < b.pos); //the following may only occur once in a set //so we don't search for "the time signature with 4/4 //at t=0", but only for "some time signature at t=0" //that's why true is returned, and not some conditional //expression case FloItem::BAR: case FloItem::KEY_CHANGE: case FloItem::TIME_SIG: return false; } return (a.pos < b.pos); } }; typedef set< pair, floComp > ScoreEventList; typedef map< unsigned, set > ScoreItemList; enum clef_t { VIOLIN, BASS }; struct note_len_t { int len; int dots; note_len_t(int l, int d) { len=l; dots=d; } note_len_t(int l) { len=l; dots=0; } }; struct cumulative_t { int count; int cumul; cumulative_t() { count=0; cumul=0; } void add(int v) { count++; cumul+=v; } float mean() { return (float)cumul/count; } }; #define DEFAULT (NUM_PARTCOLORS) #define HIGHLIGHTED_PIXMAP (NUM_PARTCOLORS + 1) #define SELECTED_PIXMAP (NUM_PARTCOLORS + 2) #define NUM_MYCOLORS (NUM_PARTCOLORS + 3 + 128) #define VELO_PIXMAP_BEGIN (NUM_PARTCOLORS + 3) struct timesig_t { int num; int denom; }; enum staff_type_t { NORMAL, GRAND_TOP, GRAND_BOTTOM }; enum staff_mode_t { MODE_TREBLE, MODE_BASS, MODE_BOTH }; struct staff_t { set parts; set part_indices; ScoreEventList eventlist; ScoreItemList itemlist; int y_top; int y_draw; int y_bottom; int min_y_coord; int max_y_coord; staff_type_t type; clef_t clef; ScoreCanvas* parent; void create_appropriate_eventlist(); void create_itemlist(); void process_itemlist(); void calc_item_pos(); void apply_lasso(QRect rect, set& already_processed); void recalculate() { create_appropriate_eventlist(); create_itemlist(); process_itemlist(); calc_item_pos(); } staff_t(ScoreCanvas* parent_) { type=NORMAL; clef=VIOLIN; parent=parent_; } staff_t (ScoreCanvas* parent_, staff_type_t type_, clef_t clef_, set parts_) { type=type_; clef=clef_; parts=parts_; parent=parent_; update_part_indices(); } bool cleanup_parts(); set parts_at_tick(unsigned tick); void read_status(MusECore::Xml& xml); void write_status(int level, MusECore::Xml& xml) const; void update_parts(); //re-populates the set from the set void update_part_indices(); //re-populates the set from the set }; list calc_accidentials(MusECore::key_enum key, clef_t clef, MusECore::key_enum next_key=MusECore::KEY_C); note_pos_t note_pos_(int note, MusECore::key_enum key); note_pos_t note_pos (unsigned note, MusECore::key_enum key, clef_t clef); int calc_len(int l, int d); list parse_note_len(int len_ticks, int begin_tick, vector& foo, bool allow_dots=true, bool allow_normal=true); int clef_height(clef_t clef); int calc_timesig_width(int num, int denom); int calc_number_width(int n); class ScoreCanvas : public MusEGui::View { Q_OBJECT private: void init_pixmaps(); static void draw_pixmap(QPainter& p, int x, int y, const QPixmap& pm); static void draw_tie (QPainter& p, int x1, int x4, int yo, bool up=true, QColor color=Qt::black); static void draw_akkolade (QPainter& p, int x, int y); static void draw_accidentials(QPainter& p, int x, int y_offset, const list& acc_list, const QPixmap& pix); static void draw_timesig(QPainter& p, int x, int y_offset, int num, int denom); static void draw_number(QPainter& p, int x, int y, int n); static int height_to_pitch(int h, clef_t clef, MusECore::key_enum key); static int height_to_pitch(int h, clef_t clef); static int y_to_height(int y); int y_to_pitch(int y, int t, clef_t clef); void draw_note_lines(QPainter& p, int y, bool reserve_akkolade_space=false); void draw_preamble(QPainter& p, int y, clef_t clef, bool reserve_akkolade_space=false, bool with_akkolade=false); void draw_items(QPainter& p, int y, staff_t& staff, ScoreItemList::iterator from_it, ScoreItemList::iterator to_it); void draw_items(QPainter& p, int y, staff_t& staff, int x1, int x2); void draw_items(QPainter& p, int y, staff_t& staff); void calc_pos_add_list(); void recalc_staff_pos(); list::iterator staff_at_y(int y); bool need_redraw_for_hilighting(ScoreItemList::iterator from_it, ScoreItemList::iterator to_it); bool need_redraw_for_hilighting(ScoreItemList& itemlist, int x1, int x2); bool need_redraw_for_hilighting(ScoreItemList& itemlist); bool need_redraw_for_hilighting(); void set_staffmode(list::iterator it, staff_mode_t mode); void remove_staff(list::iterator it); void merge_staves(list::iterator dest, list::iterator src); void move_staff_above(list::iterator dest, list::iterator src); void move_staff_below(list::iterator dest, list::iterator src); void cleanup_staves(); void maybe_close_if_empty(); QMenu* toolContextMenu(); void callContextMenu(); // defaults ---------------------------------------------------------- public: enum coloring_mode_t {COLOR_MODE_BLACK, COLOR_MODE_PART, COLOR_MODE_VELO}; static int _quant_power2_init; static int _pixels_per_whole_init; static int note_velo_init, note_velo_off_init; static int new_len_init; static coloring_mode_t coloring_mode_init; static bool preamble_contains_timesig_init; static bool preamble_contains_keysig_init; // member variables --------------------------------------------------- private: int _quant_power2; int _pixels_per_whole; int note_velo; int note_velo_off; std::map pos_add_list; list staves; MusECore::StepRec* steprec; // the drawing area is split into a "preamble" containing clef, // key and time signature, and the "item's area" containing the // actual items (notes, bars, rests, etc.) // x_pos is responsible for scrolling. an item with item->x==x_pos // will be drawn exactly at the left beginning of the item's area // x_left could also be called "preamble's width". it defines // where the item's area begins // when multiple note systems are drawn into one window, the // preamble's length is the same for each system int x_pos; int x_left; int y_pos; //for mouse-scrolling float x_scroll_speed; float x_scroll_pos; float y_scroll_speed; float y_scroll_pos; const MusECore::Part* selected_part; int selected_part_index; int last_len; int new_len; //when zero or negative, last_len is used Qt::KeyboardModifiers keystate; QPoint mouse_down_pos; bool mouse_down; enum operation_t { NO_OP=0, BEGIN=1, LENGTH=2, PITCH=3 }; operation_t mouse_operation; operation_t mouse_x_drag_operation; bool mouse_erases_notes; bool mouse_inserts_notes; bool inserting; bool dragging; bool drag_cursor_changed; const MusECore::Part* dragged_event_part; int dragged_event_part_index; MusECore::Event dragged_event; MusECore::Event original_dragged_event; const MusECore::Event* clicked_event_ptr; int old_pitch; unsigned old_dest_tick; int old_len; bool have_lasso; QPoint lasso_start; QRect lasso; bool undo_started; bool temp_undo; bool srec; bool held_notes[128]; coloring_mode_t coloring_mode; bool preamble_contains_keysig; bool preamble_contains_timesig; //menu stuff QAction* staffmode_treble_action; QAction* staffmode_bass_action; QAction* staffmode_both_action; QAction* remove_staff_action; QMenu* staff_menu; list::iterator current_staff; bool dragging_staff; QCursor active_tool_cursor; int active_tool; private slots: void staffmode_treble_slot(); void staffmode_bass_slot(); void staffmode_both_slot(); void remove_staff_slot(); void play_changed(bool); void deselect_all(); void midi_note(int pitch, int velo); void add_new_parts(const std::map< const MusECore::Part*, std::set >&); public slots: void x_scroll_event(int); void y_scroll_event(int); void song_changed(MusECore::SongChangedStruct_t); void fully_recalculate(); void goto_tick(int,bool); void pos_changed(int i, unsigned u, bool b); void heartbeat_timer_event(); void config_changed(); void set_tool(int); void set_quant(int); void menu_command(int); void preamble_keysig_slot(bool); void preamble_timesig_slot(bool); void set_pixels_per_whole(int); void set_velo(int); void set_velo_off(int); void set_steprec(bool); void update_parts(); //re-populates the sets from the sets signals: void xscroll_changed(int); void yscroll_changed(int); void viewport_width_changed(int); void canvas_width_changed(int); void preamble_width_changed(int); void viewport_height_changed(int); void canvas_height_changed(int); void pixels_per_whole_changed(int); void pos_add_changed(); protected: virtual void draw(QPainter& p, const QRect& rect, const QRegion& = QRegion()); ScoreEdit* parent; virtual void mousePressEvent (QMouseEvent* event); virtual void mouseMoveEvent (QMouseEvent* event); virtual void mouseReleaseEvent (QMouseEvent* event); virtual void resizeEvent(QResizeEvent*); public: ScoreCanvas(ScoreEdit*, QWidget*); ~ScoreCanvas(); void add_staves(MusECore::PartList* pl, bool all_in_one); void push_back_staff(staff_t& staff) { staves.push_back(staff); } //FINDMICH dirty. very dirty. int canvas_width(); int canvas_height(); int viewport_width(); int viewport_height(); int quant_power2() { return _quant_power2; } int quant_len() { return (1<<_quant_power2); } int quant_ticks() { return TICKS_PER_WHOLE / (1<<_quant_power2); } int pixels_per_whole() { return _pixels_per_whole; } int note_x_indent() { return pixels_per_whole()/quant_len()/2; } int get_last_len() {return last_len;} void set_last_len(int l) {last_len=l;} const MusECore::Part* get_selected_part() const {return selected_part;} void set_selected_part(const MusECore::Part* p) {selected_part=p; if (selected_part) selected_part_index=selected_part->sn();} const MusECore::Part* get_dragged_event_part() const {return dragged_event_part;} void set_dragged_event_part(const MusECore::Part* p) {dragged_event_part=p; if (dragged_event_part) dragged_event_part_index=dragged_event_part->sn();} set get_all_parts(); void write_staves(int level, MusECore::Xml& xml) const; timesig_t timesig_at_tick(int t); MusECore::KeyEvent key_at_tick(int t); int tick_to_x(int t); int delta_tick_to_delta_x(int t); int x_to_tick(int x); int calc_posadd(int t); bool itemsAreSelected() const; // Appends given tag list with item objects according to options. Avoids duplicate events or clone events. // Special: We 'abuse' a controller event's length, normally 0, to indicate visual item length. void tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const; }; int calc_measure_len(const list& nums, int denom); vector create_emphasize_list(const list& nums, int denom); vector create_emphasize_list(int num, int denom); } // namespace MusEGui #endif muse-4.0.0/src/muse/midieditor.cpp000066400000000000000000000446311404105311000170560ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midieditor.cpp,v 1.2.2.2 2009/02/02 21:38:00 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "midieditor.h" #include "globals.h" #include "midiedit/ecanvas.h" #include "waveedit/waveview.h" #include "scrollscale.h" #include "mtscale.h" #include "xml.h" #include "part.h" #include "track.h" #include "song.h" #include "trackinfo_layout.h" #include "icons.h" #include "astrip.h" #include "mstrip.h" #include "gconfig.h" #include "app.h" #include "rasterizer.h" #include #include #include #include #include #include // For debugging output: Uncomment the fprintf section. #define ERROR_MIDIEDITOR(dev, format, args...) fprintf(dev, format, ##args) #define DEBUG_MIDIEDITOR(dev, format, args...) // fprintf(dev, format, ##args) namespace MusEGui { //--------------------------------------------------------- // MidiEditor //--------------------------------------------------------- MidiEditor::MidiEditor(ToplevelType t, int r, MusECore::PartList* pl, QWidget* parent, const char* name) : TopWin(t, parent, name) { _pl = pl; if (_pl) for (const auto& i : *_pl) _parts.insert(i.second->sn()); QList rast_cols; rast_cols << Rasterizer::TripletColumn << Rasterizer::NormalColumn << Rasterizer::DottedColumn; _rasterizerModel = new RasterizerModel( MusEGlobal::globalRasterizer, this, -1, rast_cols); _raster = _rasterizerModel->checkRaster(r); _canvasXOrigin = 0; _minXMag = -25; _maxXMag = 2; canvas = nullptr; trackInfoWidget = nullptr; selected = nullptr; //wview = 0; _curDrumInstrument = -1; mainw = new QWidget(this); ///mainGrid = new QGridLayout(mainw); mainGrid = new QGridLayout(); mainw->setLayout(mainGrid); mainGrid->setContentsMargins(0, 0, 0, 0); mainGrid->setSpacing(0); setCentralWidget(mainw); connect(MusEGlobal::song, SIGNAL(newPartsCreated(const std::map< const MusECore::Part*, std::set >&)), SLOT(addNewParts(const std::map< const MusECore::Part*, std::set >&))); } int MidiEditor::rasterStep(unsigned tick) const { return MusEGlobal::sigmap.rasterStep(tick, _raster); } unsigned MidiEditor::rasterVal(unsigned v) const { return MusEGlobal::sigmap.raster(v, _raster); } unsigned MidiEditor::rasterVal1(unsigned v) const { return MusEGlobal::sigmap.raster1(v, _raster); } unsigned MidiEditor::rasterVal2(unsigned v) const { return MusEGlobal::sigmap.raster2(v, _raster); } //--------------------------------------------------------- // genPartlist //--------------------------------------------------------- void MidiEditor::genPartlist() { if(!_pl) return; _pl->clear(); for (std::set::iterator i = _parts.begin(); i != _parts.end(); ++i) { MusECore::TrackList* tl = MusEGlobal::song->tracks(); for (MusECore::iTrack it = tl->begin(); it != tl->end(); ++it) { MusECore::PartList* pl = (*it)->parts(); MusECore::iPart ip; for (ip = pl->begin(); ip != pl->end(); ++ip) { if (ip->second->sn() == *i) { _pl->add(ip->second); break; } } if (ip != pl->end()) break; } } } //--------------------------------------------------------- // genTrackInfo //--------------------------------------------------------- void MidiEditor::genTrackInfo(TrackInfoWidget* trackInfo) { trackInfo->addWidget(nullptr, 1); } //--------------------------------------------------------- // switchInfo //--------------------------------------------------------- void MidiEditor::switchInfo(int n) { const int idx = 1; if(n == idx) { // MidiStrip* w = (MidiStrip*)(trackInfoWidget->getWidget(idx)); Strip* w = (Strip*)(trackInfoWidget->getWidget(idx)); if (w == nullptr || selected != w->getTrack()) { if (w) { //fprintf(stderr, "MidiEditor::switchInfo deleting strip\n"); delete w; //w->deleteLater(); } if(selected->isMidiTrack()) w = new MidiStrip(trackInfoWidget, static_cast (selected)); else w = new AudioStrip(trackInfoWidget, static_cast (selected)); // Leave broadcasting changes to other selected tracks off. // Set focus yielding to the canvas. if(MusEGlobal::config.smartFocus) { w->setFocusYieldWidget(canvas); //w->setFocusPolicy(Qt::WheelFocus); } // We must marshall song changed instead of connecting to the strip's song changed // otherwise it crashes when loading another song because track is no longer valid // and the strip's songChanged() seems to be called before Pianoroll songChanged() // gets called and has a chance to stop the crash. //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), w, SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::muse, SIGNAL(configChanged()), w, SLOT(configChanged())); w->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); trackInfoWidget->addWidget(w, idx); w->show(); //setTabOrder(midiTrackInfo, w); } } if (trackInfoWidget->curIdx() == n) return; trackInfoWidget->raiseWidget(n); } //--------------------------------------------------------- // trackInfoSongChange //--------------------------------------------------------- void MidiEditor::trackInfoSongChange(MusECore::SongChangedStruct_t flags) { if(!selected) return; Strip* w = static_cast(trackInfoWidget->getWidget(1)); if(w) w->songChanged(flags); } //--------------------------------------------------------- // updateTrackInfo //--------------------------------------------------------- void MidiEditor::updateTrackInfo() { MusECore::Part* part = curCanvasPart(); if(part) selected = part->track(); else selected = nullptr; if (selected == nullptr) { switchInfo(0); return; } // if (selected->isMidiTrack()) switchInfo(1); } //--------------------------------------------------------- // checkTrackInfoTrack //--------------------------------------------------------- void MidiEditor::checkTrackInfoTrack() { const int idx = 1; { Strip* w = static_cast(trackInfoWidget->getWidget(idx)); if(w) { MusECore::Track* t = w->getTrack(); if(t) { MusECore::TrackList* tl = MusEGlobal::song->tracks(); MusECore::iTrack it = tl->find(t); if(it == tl->end()) { delete w; trackInfoWidget->addWidget(nullptr, idx); selected = nullptr; switchInfo(0); } } } } } //--------------------------------------------------------- // movePlayPointerToSelectedEvent //--------------------------------------------------------- void MidiEditor::movePlayPointerToSelectedEvent() { const MusECore::EventList & evl = curCanvasPart()->events(); int tickPos = -1; for (MusECore::ciEvent ev=evl.begin(); ev!=evl.end(); ev++) { if (ev->second.selected()) { tickPos = ev->second.tick(); printf("found selected event, moving to pos %d\n", tickPos); break; } } if (tickPos > -1) { MusECore::Pos p0(curCanvasPart()->tick() + tickPos, true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p0); } } //--------------------------------------------------------- // addPart //--------------------------------------------------------- void MidiEditor::addPart(MusECore::Part* p) { if(!_pl || !p) return; _pl->add(p); _parts.insert(p->sn()); } //--------------------------------------------------------- // itemsAreSelected //--------------------------------------------------------- bool MidiEditor::itemsAreSelected() const { bool res = false; if(canvas && canvas->itemsAreSelected()) res = true; for(ciCtrlEdit i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) if((*i)->itemsAreSelected()) res = true; return res; } //--------------------------------------------------------- // tagItems //--------------------------------------------------------- void MidiEditor::tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const { const bool tagAllItems = options._flags & MusECore::TagAllItems; const bool tagAllParts = options._flags & MusECore::TagAllParts; const bool range = options._flags & MusECore::TagRange; const MusECore::Pos& p0 = options._p0; const MusECore::Pos& p1 = options._p1; // If tagging all items, don't bother with the controller editors below, // since everything that they could tag will already be tagged. if(tagAllItems) { const MusECore::Part* part; MusECore::Pos pos, part_pos, part_endpos; if(tagAllParts) { if(_pl) { for(MusECore::ciPart ip = _pl->begin(); ip != _pl->end(); ++ip) { part = ip->second; if(range) { part_pos = *part; part_endpos = part->end(); // Optimize: Is the part within the range? // p1 should be considered outside (one past) the very last position in the range. if(part_endpos <= p0 || part_pos >= p1) continue; } const MusECore::EventList& el = part->events(); for(MusECore::ciEvent ie = el.cbegin(); ie != el.cend(); ++ie) { const MusECore::Event& e = ie->second; if(range) { // Don't forget to add the part's position. pos = e.pos() + part_pos; // If the event position is before p0, keep looking... if(pos < p0) continue; // If the event position is at or after p1 then we are done. // p1 should be considered outside (one past) the very last position in the range. if(pos >= p1) break; } tag_list->add(part, e); } } } } else { if(canvas && canvas->part()) { part = canvas->part(); if(range) { part_pos = *part; part_endpos = part->end(); // Optimize: Is the part within the range? // p1 should be considered outside (one past) the very last position in the range. if(part_endpos <= p0 || part_pos >= p1) return; } const MusECore::EventList& el = part->events(); for(MusECore::ciEvent ie = el.cbegin(); ie != el.cend(); ++ie) { const MusECore::Event& e = ie->second; if(range) { // Don't forget to add the part's position. pos = e.pos() + part_pos; // If the event position is before p0, keep looking... if(pos < p0) continue; // If the event position is at or after p1 then we are done. // p1 should be considered outside (one past) the very last position in the range. if(pos >= p1) break; } tag_list->add(part, e); } } } } else { MusECore::EventTagOptionsStruct opts = options; opts.removeFlags(MusECore::TagAllItems); // These two steps use the tagging features to mark the objects (events) // as having been visited already, to avoid duplicates in the list. if(canvas) canvas->tagItems(tag_list, opts); for(ciCtrlEdit i = ctrlEditList.begin(); i != ctrlEditList.end(); ++i) (*i)->tagItems(tag_list, opts); } } //--------------------------------------------------------- // MidiEditor //--------------------------------------------------------- MidiEditor::~MidiEditor() { DEBUG_MIDIEDITOR(stderr, "MidiEditor dtor\n"); if (_pl) delete _pl; } //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void MidiEditor::readStatus(MusECore::Xml& xml) { if (_pl == nullptr) _pl = new MusECore::PartList; for (;;) { MusECore::Xml::Token token = xml.parse(); QString tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "raster") _raster = _rasterizerModel->checkRaster(xml.parseInt()); else if (tag == "topwin") TopWin::readStatus(xml); else xml.unknown("MidiEditor"); break; case MusECore::Xml::TagEnd: if (tag == "midieditor") return; default: break; } } } //--------------------------------------------------------- // writePartList //--------------------------------------------------------- void MidiEditor::writePartList(int level, MusECore::Xml& xml) const { if(!_pl) return; for (MusECore::ciPart p = _pl->begin(); p != _pl->end(); ++p) { MusECore::Part* part = p->second; MusECore::Track* track = part->track(); int trkIdx = MusEGlobal::song->tracks()->index(track); int partIdx = track->parts()->index(part); if((trkIdx == -1) || (partIdx == -1)) printf("MidiEditor::writePartList error: trkIdx:%d partIdx:%d\n", trkIdx, partIdx); xml.put(level, "%d:%d", trkIdx, partIdx); } } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void MidiEditor::writeStatus(int level, MusECore::Xml& xml) const { xml.tag(level++, "midieditor"); TopWin::writeStatus(level, xml); xml.intTag(level, "raster", _raster); xml.tag(level, "/midieditor"); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MidiEditor::songChanged(MusECore::SongChangedStruct_t type) { if (type) { if (type & (SC_PART_REMOVED | SC_PART_MODIFIED | SC_PART_INSERTED | SC_TRACK_REMOVED)) { genPartlist(); // close window if editor has no parts anymore if (parts()->empty()) { close(); return; } } if (canvas) canvas->songChanged(type); if (type & (SC_PART_REMOVED | SC_PART_MODIFIED | SC_PART_INSERTED | SC_TRACK_REMOVED)) { updateHScrollRange(); if (canvas) setWindowTitle(canvas->getCaption()); if (time && type & SC_SIG) time->update(); } } } //--------------------------------------------------------- // setCurDrumInstrument //--------------------------------------------------------- void MidiEditor::setCurDrumInstrument(int instr) { _curDrumInstrument = instr; emit curDrumInstrumentChanged(_curDrumInstrument); } //--------------------------------------------------------- // curCanvasPart //--------------------------------------------------------- MusECore::Part* MidiEditor::curCanvasPart() { if(canvas) return canvas->part(); else return nullptr; } //--------------------------------------------------------- // setCurCanvasPart //--------------------------------------------------------- void MidiEditor::setCurCanvasPart(MusECore::Part* part) { if(canvas) canvas->setCurrentPart(part); } void MidiEditor::addNewParts(const std::map< const MusECore::Part*, std::set >& param) { if(!_pl) return; using std::map; using std::set; for (map< const MusECore::Part*, set >::const_iterator it = param.begin(); it!=param.end(); it++) if (_pl->index(it->first) != -1) for (set::const_iterator it2=it->second.begin(); it2!=it->second.end(); it2++) addPart(const_cast(*it2)); // FIXME make this const-correct! } void MidiEditor::setHScrollOffset(const int value) { int min, max; hscroll->range(&min, &max); int pos = qMin(qMax(min, value), max); hscroll->setOffset(pos); } } // namespace MusEGui muse-4.0.0/src/muse/midieditor.h000066400000000000000000000110131404105311000165070ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midieditor.h,v 1.3.2.2 2009/02/02 21:38:00 terminator356 Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDIEDITOR_H__ #define __MIDIEDITOR_H__ #include "type_defs.h" #include "cobject.h" #include "ctrl/ctrledit.h" #include "event_tag_list.h" #include class QGridLayout; class QWidget; class QPoint; namespace MusECore { class Track; class Part; class PartList; class WavePart; class Xml; } namespace MusEGui { class EventCanvas; class MTScale; class ScrollScale; class TrackInfoWidget; class RasterizerModel; //--------------------------------------------------------- // MidiEditor //--------------------------------------------------------- class MidiEditor : public TopWin { Q_OBJECT MusECore::PartList* _pl; std::set _parts; int _curDrumInstrument; // currently selected instrument if drum // editor protected: MusEGui::ScrollScale* hscroll; MusEGui::ScrollScale* vscroll; MusEGui::MTScale* time; EventCanvas* canvas; TrackInfoWidget* trackInfoWidget; MusECore::Track* selected; CtrlEditList ctrlEditList; int _raster; RasterizerModel *_rasterizerModel; // The X origin that is applied to any canvases. int _canvasXOrigin; // The X mag minimum that is applied to any ScrollScale controls. // Note that global midi division will also be taken into account. int _minXMag; // The X mag maximum that is applied to any ScrollScale controls. int _maxXMag; QGridLayout* mainGrid; QWidget* mainw; virtual void readStatus(MusECore::Xml&); virtual void writeStatus(int, MusECore::Xml&) const; void writePartList(int, MusECore::Xml&) const; void genPartlist(); void movePlayPointerToSelectedEvent(); void genTrackInfo(TrackInfoWidget* trackInfo); void switchInfo(int); void trackInfoSongChange(MusECore::SongChangedStruct_t flags); // Checks if track info track is valid and deletes the strip if the track is not found. void checkTrackInfoTrack(); protected slots: void updateTrackInfo(); private slots: void addNewParts(const std::map< const MusECore::Part*, std::set >&); public slots: void songChanged(MusECore::SongChangedStruct_t type); void setCurDrumInstrument(int instr); virtual void updateHScrollRange() { }; signals: void curDrumInstrumentChanged(int); public: MidiEditor(ToplevelType t, int, MusECore::PartList*, QWidget* parent = 0, const char* name = 0); virtual ~MidiEditor(); int rasterStep(unsigned tick) const; unsigned rasterVal(unsigned v) const; unsigned rasterVal1(unsigned v) const; unsigned rasterVal2(unsigned v) const; int raster() const { return _raster; } virtual void setRaster(int val) { _raster = val; } MusECore::PartList* parts() { return _pl; } const MusECore::PartList* parts() const { return _pl; } int curDrumInstrument() const { return _curDrumInstrument; } MusECore::Part* curCanvasPart(); void setCurCanvasPart(MusECore::Part*); void addPart(MusECore::Part*); bool itemsAreSelected() const; // Appends given tag list with item objects according to options. Avoids duplicate events or clone events. // Special: We 'abuse' a controller event's length, normally 0, to indicate visual item length. void tagItems(MusECore::TagEventList* tag_list, const MusECore::EventTagOptionsStruct& options) const; void setHScrollOffset(const int); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/midievent.cpp000066400000000000000000000230131404105311000167000ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midievent.cpp,v 1.7.2.2 2009/05/24 21:43:44 terminator356 Exp $ // // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "midievent.h" #include "xml.h" #include "mpevent.h" #include "midictrl.h" namespace MusECore { //--------------------------------------------------------- // MidiEventBase //--------------------------------------------------------- MidiEventBase::MidiEventBase(EventType t) : EventBase(t) { a = 0; b = 0; c = 0; } MidiEventBase::MidiEventBase(const MidiEventBase& ev, bool duplicate_not_clone) : EventBase(ev, duplicate_not_clone) { a = ev.a; b = ev.b; c = ev.c; if(duplicate_not_clone) edata.setData(ev.constData(), ev.dataLen()); // Makes a copy. else // NOTE: Even non-shared clone events ALWAYS share edata. edata does NOT currently require // separate instances, unlike wave events which absolutely do. // Be aware when iterating or modifying clones for example. (It can save time.) edata = ev.edata; } //--------------------------------------------------------- // assign //--------------------------------------------------------- void MidiEventBase::assign(const EventBase& ev) { if(ev.type() != type()) return; EventBase::assign(ev); a = ev.dataA(); b = ev.dataB(); c = ev.dataC(); // NOTE: Even non-shared clone events ALWAYS share edata. edata does NOT currently require // separate instances, unlike wave events which absolutely do. // Be aware when iterating or modifying clones for example. (It can save time.) if(edata.constData() != ev.constData()) edata.setData(ev.constData(), ev.dataLen()); // Makes a copy. } bool MidiEventBase::isSimilarTo(const EventBase& other_) const { const MidiEventBase* other = dynamic_cast(&other_); if (other==NULL) // dynamic cast hsa failed: "other_" is not of type MidiEventBase. return false; if ((a==other->a && b==other->b && c==other->c && edata.dataLen()==other->edata.dataLen() && this->PosLen::operator==(*other)) == false) return false; if (edata.dataLen() > 0) return (memcmp(edata.constData(), other->edata.constData(), edata.dataLen() ) == 0); else return true; // no data equals no data. } //--------------------------------------------------------- // MidiEventBase::mid //--------------------------------------------------------- EventBase* MidiEventBase::mid(unsigned b, unsigned e) const { if (tick() < b || tick() >= e) return 0; return new MidiEventBase(*this); } //--------------------------------------------------------- // isNoteOff //--------------------------------------------------------- bool MidiEventBase::isNoteOff() const { return (type() == Note && (velo() == 0)); } void MidiEventBase::dump(int n) const { EventBase::dump(n); const char* p; switch(type()) { case Note: p = "Note "; break; case Controller: p = "Ctrl "; break; case Sysex: p = "Sysex "; break; case Meta: p = "Meta "; break; default: p = "?? "; break; } for (int i = 0; i < (n+2); ++i) putchar(' '); printf("<%s> a:0x%x(%d) b:0x%x(%d) c:0x%x(%d)\n", p, a, a, b, b, c, c); } //--------------------------------------------------------- // MidiEventBase::write //--------------------------------------------------------- void MidiEventBase::write(int level, Xml& xml, const Pos& offset, bool /*forcePath*/) const { xml.nput(level++, "\n", edata.dataLen() ); xml.nput(level, ""); for (int i = 0; i < edata.dataLen(); ++i) { if (i && ((i % 16) == 0)) { xml.nput("\n"); xml.nput(level, ""); } xml.nput("%02x ", edata.constData()[i] & 0xff); } xml.nput("\n"); xml.tag(level, "/event"); } else { xml.nput(" />\n"); } } //--------------------------------------------------------- // MidiEventBase::read //--------------------------------------------------------- void MidiEventBase::read(Xml& xml) { int ev_type = Note; a = 0; b = 0; c = 0; int dataLen = 0; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: xml.unknown("Event"); break; case Xml::Text: { QByteArray ba = tag.toLatin1(); const char*s = ba.constData(); edata.resize(dataLen); unsigned char* d = edata.data(); for (int i = 0; i < dataLen; ++i) { char* endp; *d++ = strtol(s, &endp, 16); s = endp; } } break; case Xml::Attribut: if (tag == "tick") setTick(xml.s2().toInt()); else if (tag == "type") //setType(EventType(xml.s2().toInt())); ev_type = xml.s2().toInt(); else if (tag == "len") setLenTick(xml.s2().toInt()); else if (tag == "a") a = xml.s2().toInt(); else if (tag == "b") b = xml.s2().toInt(); else if (tag == "c") c = xml.s2().toInt(); else if (tag == "datalen") dataLen = xml.s2().toInt(); break; case Xml::TagEnd: if (tag == "event") { // Convert obsolete PAfter and CAfter events to newer controllers if(ev_type == 3) // PAfter { ev_type = Controller; a = (a & 0x7f) | (CTRL_POLYAFTER & ~0xff); // Controller number + pitch. B is the value. } else if(ev_type == 4) // CAfter { ev_type = Controller; b = a; // Value a = CTRL_AFTERTOUCH; // Controller number } setType(EventType(ev_type)); // HACK: Repair controllers saved with lo byte 0xff. // No such control. It was supposed to be 0x00. // It's an error caused by a previous bug, fixed now. if((ev_type == Controller) && ((a & 0xff) == 0xff)) a &= ~0xff; return; } default: break; } } } } // namespace MusECore muse-4.0.0/src/muse/midievent.h000066400000000000000000000101571404105311000163520ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midievent.h,v 1.7.2.1 2009/05/24 21:43:44 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDI_EVENT_H__ #define __MIDI_EVENT_H__ #include "eventbase.h" namespace MusECore { //--------------------------------------------------------- // MidiEventBase //--------------------------------------------------------- class MidiEventBase : public EventBase { int a, b, c; // pitch, velo-on, velo-off // Sysex event data. // NOTE: Even non-shared clone events ALWAYS share edata. edata does NOT currently require // separate instances, unlike wave events which absolutely do. // Be aware when iterating or modifying clones for example. (It can save time.) EvData edata; // Creates a non-shared clone (copies event base), including the same 'group' id. virtual EventBase* clone() const { return new MidiEventBase(*this); } // Creates a copy of the event base, excluding the 'group' _id. virtual EventBase* duplicate() const { return new MidiEventBase(*this, true); } public: MidiEventBase(EventType t); // Creates a non-shared clone with same id, or duplicate with unique id, and 0 ref count and invalid Pos sn. MidiEventBase(const MidiEventBase& ev, bool duplicate_not_clone = false); virtual ~MidiEventBase() {} virtual void assign(const EventBase& ev); // Assigns to this event, excluding the _id. virtual bool isSimilarTo(const EventBase& other) const; virtual bool isNote() const { return type() == Note; } virtual bool isNoteOff() const; virtual int pitch() const { return a; } virtual int program() const { return a; } virtual int cntrl() const { return a; } virtual int dataA() const { return a; } virtual void setA(int val) { a = val; } virtual void setPitch(int v) { a = v; } virtual int cntrlVal() const { return b; } virtual int dataB() const { return b; } virtual int velo() const { return b; } virtual void setB(int val) { b = val; } virtual void setVelo(int v) { b = v; } virtual int veloOff() const { return c; } virtual int dataC() const { return c; } virtual void setC(int val) { c = val; } virtual void setVeloOff(int v) { c = v; } virtual const unsigned char* constData() const { return edata.constData(); } virtual int dataLen() const { return edata.dataLen(); } virtual void setData(const unsigned char* data, int len) { edata.setData(data, len); } virtual const EvData eventData() const { return edata; } virtual void dump(int n = 0) const; virtual void read(Xml&); virtual void write(int, Xml&, const Pos& offset, bool forcePath = false) const; virtual EventBase* mid(unsigned, unsigned) const; }; } // namespace MusECore #endif muse-4.0.0/src/muse/midifile.cpp000066400000000000000000000732401404105311000165050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midifile.cpp,v 1.17 2004/06/18 08:36:43 wschweer Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "song.h" #include "midi_consts.h" #include "midifile.h" #include "drummap.h" #include "event.h" #include "globals.h" #include "midictrl.h" #include "marker/marker.h" #include "midiport.h" #include "midictrl.h" #include "mpevent.h" #include "gconfig.h" #include "tempo.h" #include "muse_time.h" namespace MusECore { const char* errString[] = { "no Error", "unexpected EOF", "read Error", "write Error", "bad midifile: 'MTrk' expected", "bad midifile: 'MThd' expected", "bad midi fileformat", }; #ifdef _WIN32 enum ERROR2 { // ERROR is already used in Windows.h #else enum ERROR { #endif MF_NO_ERROR, MF_EOF, MF_READ, MF_WRITE, MF_MTRK, //MF_MTRK_ZERO_DATA, MF_MTHD, MF_FORMAT }; MuseTick_t linearTime2tick(uint64_t time, uint64_t time_div, LargeIntRoundMode round_mode = LargeIntRoundDown) { const uint64_t ft = ((uint64_t)MusEGlobal::sampleRate * time); MuseTick_t tick; // Didn't want to include global tempo percent here, but found that I had to, I think the reason is because // when global tempo is changed, it causes a re-normalization of the tempo map. const uint64_t numer = (uint64_t)MusEGlobal::config.division * (uint64_t)MusEGlobal::tempomap.globalTempo() * 10000UL; //const uint64_t numer = (uint64_t)MusEGlobal::config.division * 1000000UL; const uint64_t denom = (uint64_t)MusEGlobal::sampleRate * time_div; if (MusEGlobal::tempomap.masterFlag()) { ciTEvent e; for (e = MusEGlobal::tempomap.cbegin(); e != MusEGlobal::tempomap.cend();) { ciTEvent ee = e; ++ee; if (ee == MusEGlobal::tempomap.cend()) break; if (ft < (uint64_t)ee->second->frame * time_div) break; e = ee; } // Normally do not round up here since (audio) frame resolution is higher than tick resolution. tick = e->second->tick + muse_multiply_64_div_64_to_64( numer, ft - (uint64_t)e->second->frame * time_div, denom * (uint64_t)e->second->tempo, round_mode); } else // Normally do not round up here since (audio) frame resolution is higher than tick resolution. tick = muse_multiply_64_div_64_to_64(numer, ft, denom * (uint64_t)MusEGlobal::tempomap.staticTempo(), round_mode); return tick; } //--------------------------------------------------------- // error //--------------------------------------------------------- QString MidiFile::error() { return QString(errString[_error]); } //--------------------------------------------------------- // MidiFile //--------------------------------------------------------- MidiFile::MidiFile(FILE* f) { fp = f; curPos = 0; lastMtype = MT_UNKNOWN; _error = MF_NO_ERROR; _tracks = new MidiFileTrackList; _usedPortMap = new MidiFilePortMap; _divisionIsLinearTime = false; } MidiFile::~MidiFile() { if(_tracks) { _tracks->clearDelete(); delete _tracks; _tracks = 0; } delete _usedPortMap; } void MidiFile::setTrackList(MidiFileTrackList* tr, int n) { if(_tracks) { _tracks->clearDelete(); delete _tracks; _tracks = 0; } _tracks = tr; ntracks = n; } //--------------------------------------------------------- // read // return true on error //--------------------------------------------------------- bool MidiFile::read(void* p, size_t len) { for (;;) { curPos += len; size_t rv = fread(p, 1, len, fp); if (rv == len) return false; if (feof(fp)) { _error = MF_EOF; return true; } _error = MF_READ; return true; } return false; } //--------------------------------------------------------- // write // return true on error //--------------------------------------------------------- bool MidiFile::write(const void* p, size_t len) { size_t rv = fwrite(p, 1, len, fp); if (rv == len) return false; _error = MF_WRITE; return true; } //--------------------------------------------------------- // writeShort // return true on error //--------------------------------------------------------- bool MidiFile::writeShort(int i) { short format = BE_SHORT(i); return write(&format, 2); } //--------------------------------------------------------- // writeLong // return true on error //--------------------------------------------------------- bool MidiFile::writeLong(int i) { int format = BE_LONG(i); return write(&format, 4); } //--------------------------------------------------------- // readShort //--------------------------------------------------------- int MidiFile::readShort() { short format; read(&format, 2); return BE_SHORT(format); } //--------------------------------------------------------- // readLong // writeLong //--------------------------------------------------------- int MidiFile::readLong() { int format; read(&format, 4); return BE_LONG(format); } /*--------------------------------------------------------- * skip * This is meant for skipping a few bytes in a * file or fifo. *---------------------------------------------------------*/ bool MidiFile::skip(size_t len) { char tmp[len]; return read(tmp, len); } /*--------------------------------------------------------- * getvl * Read variable-length number (7 bits per byte, MSB first) *---------------------------------------------------------*/ int MidiFile::getvl() { int l = 0; for (int i = 0; i < 16; i++) { uchar c; if (read(&c, 1)) return -1; l += (c & 0x7f); if (!(c & 0x80)) return l; l <<= 7; } return -1; } /*--------------------------------------------------------- * putvl * Write variable-length number (7 bits per byte, MSB first) *---------------------------------------------------------*/ void MidiFile::putvl(unsigned val) { unsigned long buf = val & 0x7f; while ((val >>= 7) > 0) { buf <<= 8; buf |= 0x80; buf += (val & 0x7f); } for (;;) { put(buf); if (buf & 0x80) buf >>= 8; else break; } } //--------------------------------------------------------- // readTrack // return true on error //--------------------------------------------------------- bool MidiFile::readTrack(MidiFileTrack* t) { MPEventList* el = &(t->events); char tmp[4]; if (read(tmp, 4)) return true; if (memcmp(tmp, "MTrk", 4)) { _error = MF_MTRK; return true; } int len = readLong(); // len if(len <= 0) return false; int endPos = curPos + len; status = -1; sstatus = -1; // running status, not reset scanning meta or sysex click = 0; int port = 0; int channel = 0; for (;;) { MidiPlayEvent event; lastport = -1; lastchannel = -1; lastMtype = MT_UNKNOWN; lastInstrName.clear(); lastDeviceName.clear(); int rv = readEvent(&event, t); if (lastport != -1) { port = lastport; if (port >= MusECore::MIDI_PORTS) { printf("port %d >= %d, reset to 0\n", port, MusECore::MIDI_PORTS); port = 0; } } if (lastchannel != -1) { channel = lastchannel; if (channel >= MusECore::MUSE_MIDI_CHANNELS) { printf("channel %d >= %d, reset to 0\n", port, MusECore::MUSE_MIDI_CHANNELS); channel = 0; } } if(!lastDeviceName.isEmpty()) { iMidiFilePort iup = _usedPortMap->begin(); for( ; iup != _usedPortMap->end(); ++iup) { if(iup->second._subst4DevName == lastDeviceName) { port = iup->first; break; } } if(iup == _usedPortMap->end()) { MidiDevice* md = MusEGlobal::midiDevices.find(lastDeviceName); if(md) { int pn = md->midiPort(); if(pn != -1) port = pn; else { for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { iMidiFilePort ip = _usedPortMap->find(i); MidiPort* mp = &MusEGlobal::midiPorts[i]; if(!mp->device() && (ip == _usedPortMap->end() || ip->second._subst4DevName.isEmpty())) { //mp->setMidiDevice(); // No, done in importMidi //msgSetMidiDevice( port = i; break; } } } } } } iMidiFilePort iup = _usedPortMap->find(port); if(iup == _usedPortMap->end()) { MidiFilePort up; if(lastMtype != MT_UNKNOWN) up._midiType = lastMtype; if(!lastInstrName.isEmpty()) up._instrName = lastInstrName; if(!lastDeviceName.isEmpty()) up._subst4DevName = lastDeviceName; _usedPortMap->insert(std::pair(port, up)); } else { if(lastMtype != MT_UNKNOWN) iup->second._midiType = lastMtype; if(!lastInstrName.isEmpty()) iup->second._instrName = lastInstrName; if(!lastDeviceName.isEmpty()) iup->second._subst4DevName = lastDeviceName; } if (rv == 0) break; else if (rv == -1) continue; else if (rv == -2) // error return true; event.setPort(port); if (event.type() == ME_SYSEX || event.type() == ME_META) event.setChannel(channel); else channel = event.channel(); el->add(event); } //fprintf(stderr, "MidiFile::readTrack(): division:%d last click:%d\n", _division, click); int end = curPos; if (end != endPos) { printf("MidiFile::readTrack(): TRACKLEN does not fit %d+%d != %d, %d too much\n", endPos-len, len, end, endPos-end); if (end < endPos) skip(endPos - end); } return false; } //--------------------------------------------------------- // readEvent // returns: // 3 OK // 0 End of track // -1 Event filtered // -2 Error //--------------------------------------------------------- int MidiFile::readEvent(MidiPlayEvent* event, MidiFileTrack* t) { uchar me, type, a, b; int nclick = getvl(); if (nclick == -1) { printf("readEvent: error 1\n"); return 0; } click += nclick; for (;;) { if (read(&me, 1)) { printf("readEvent: error 2\n"); return 0; } if (me >= 0xf8 && me <= 0xfe) printf("Midi: Real Time Message 0x%02x??\n", me & 0xff); else break; } if(divisionIsLinearTime()) event->setTime(linearTime2tick(click, division())); else event->setTime(click); int len; unsigned char* buffer; if ((me & 0xf0) == 0xf0) { if (me == 0xf0 || me == 0xf7) { // // SYSEX // status = -1; // no running status len = getvl(); if (len == -1) { printf("readEvent: error 3\n"); return -2; } // Buffer can be deleted by caller's event when it goes out of scope. buffer = new unsigned char[len]; if (read(buffer, len)) { printf("readEvent: error 4\n"); delete[] buffer; return -2; } if (buffer[len-1] != 0xf7) { printf("SYSEX doesn't end with 0xf7!\n"); // to be continued? } else --len; // don't count 0xf7 event->setType(ME_SYSEX); event->setData(buffer, len); if (((unsigned)len == gmOnMsgLen) && memcmp(buffer, gmOnMsg, gmOnMsgLen) == 0) { lastMtype = MT_GM; return -1; } if (((unsigned)len == gm2OnMsgLen) && memcmp(buffer, gm2OnMsg, gm2OnMsgLen) == 0) { lastMtype = MT_GM2; return -1; } if (((unsigned)len == gsOnMsgLen) && memcmp(buffer, gsOnMsg, gsOnMsgLen) == 0) { lastMtype = MT_GS; return -1; } if (((unsigned)len == xgOnMsgLen) && memcmp(buffer, xgOnMsg, xgOnMsgLen) == 0) { lastMtype = MT_XG; return -1; } if (buffer[0] == 0x41) { // Roland lastMtype = MT_GS; } else if (buffer[0] == 0x43) { // Yamaha lastMtype = MT_XG; int type = buffer[1] & 0xf0; switch (type) { case 0x00: // bulk dump buffer[1] = 0; break; case 0x10: if (buffer[1] != 0x10) { buffer[1] = 0x10; // fix to Device 1 } if (len == 7 && buffer[2] == 0x4c && buffer[3] == 0x08 && buffer[5] == 7) { // part mode // 0 - normal // 1 - DRUM // 2 - DRUM 1 // 3 - DRUM 2 // 4 - DRUM 3 // 5 - DRUM 4 printf("xg set part mode channel %d to %d\n", buffer[4]+1, buffer[6]); if (buffer[6] != 0) t->_isDrumTrack = true; } break; case 0x20: printf("YAMAHA DUMP REQUEST\n"); return -1; case 0x30: printf("YAMAHA PARAMETER REQUEST\n"); return -1; default: printf("YAMAHA unknown SYSEX: data[2]=%02x\n", buffer[1]); return -1; } } if(MusEGlobal::debugMsg) printf("MidiFile::readEvent: unknown Sysex 0x%02x unabsorbed, passing thru instead\n", me & 0xff); return 3; } if (me == 0xff) { // // META // status = -1; // no running status if (read(&type, 1)) { // read type printf("readEvent: error 5\n"); return -2; } len = getvl(); // read len if (len == -1) { printf("readEvent: error 6\n"); return -2; } buffer = new unsigned char[len+1]; if (len) { if (read(buffer, len)) { printf("readEvent: error 7\n"); delete[] buffer; return -2; } } buffer[len] = 0; switch(type) { case ME_META_TEXT_9_DEVICE_NAME: // device name lastDeviceName = QString((const char*)buffer); delete[] buffer; return -1; case ME_META_TEXT_4_INSTRUMENT_NAME: // instrument name lastInstrName = QString((const char*)buffer); delete[] buffer; return -1; case ME_META_PORT_CHANGE: // switch port lastport = buffer[0]; delete[] buffer; return -1; case ME_META_CHANNEL_CHANGE: // switch channel lastchannel = buffer[0]; delete[] buffer; return -1; case ME_META_END_OF_TRACK: // End of Track delete[] buffer; return 0; default: if(MusEGlobal::debugMsg) printf("MidiFile::readEvent: unknown Meta 0x%x %d unabsorbed, passing thru instead\n", type, type); event->setType(ME_META); event->setData(buffer, len+1); event->setA(type); return 3; } } else { printf("Midi: unknown Message 0x%02x\n", me & 0xff); return -1; } } if (me & 0x80) { // status byte status = me; sstatus = status; if (read(&a, 1)) { printf("readEvent: error 9\n"); return -2; } a &= 0x7F; } else { if (status == -1) { printf("readEvent: no running status, read 0x%02x sstatus %x\n", me, sstatus); if (sstatus == -1) return -1; status = sstatus; } a = me; } b = 0; switch (status & 0xf0) { case ME_NOTEOFF: case ME_NOTEON: case ME_POLYAFTER: case ME_CONTROLLER: case ME_PITCHBEND: if (read(&b, 1)) { printf("readEvent: error 15\n"); return -2; } event->setB(b & 0x80 ? 0 : b); break; case ME_PROGRAM: case ME_AFTERTOUCH: break; default: // f1 f2 f3 f4 f5 f6 f7 f8 f9 printf("BAD STATUS 0x%02x, me 0x%02x\n", status, me); return -2; } event->setA(a & 0x7f); event->setType(status & 0xf0); event->setChannel(status & 0xf); if ((a & 0x80) || (b & 0x80)) { printf("8'tes Bit in Daten(%02x %02x): tick %d read 0x%02x status:0x%02x\n", a & 0xff, b & 0xff, click, me, status); printf("readEvent: error 16\n"); if (b & 0x80) { // Try to fix: interpret as channel byte status = b & 0xf0; sstatus = status; return 3; } return -1; } if (event->type() == ME_PITCHBEND) { int val = (event->dataB() << 7) + event->dataA(); val -= 8192; event->setA(val); } return 3; } //--------------------------------------------------------- // writeTrack //--------------------------------------------------------- bool MidiFile::writeTrack(const MidiFileTrack* t) { //FIXME: By T356 01/19/2010 // If saving as a compressed file (gz or bz2), // the file is a pipe, and pipes can't seek ! // This results in a corrupted midi file. // So exporting compressed midi has been disabled (elsewhere) // for now... const MPEventList* events = &(t->events); write("MTrk", 4); int lenpos = ftell(fp); writeLong(0); // dummy len status = -1; int tick = 0; for (iMPEvent i = events->begin(); i != events->end(); ++i) { int ntick = i->time(); if (ntick < tick) { printf("MidiFile::writeTrack: ntick %d < tick %d\n", ntick, tick); ntick = tick; } putvl(((ntick - tick) * MusEGlobal::config.midiDivision + MusEGlobal::config.division/2)/MusEGlobal::config.division); tick = ntick; writeEvent(&(*i)); } //--------------------------------------------------- // write "End Of Track" Meta // write Track Len // putvl(0); put(0xff); // Meta put(0x2f); // EOT putvl(0); // len 0 int endpos = ftell(fp); fseek(fp, lenpos, SEEK_SET); writeLong(endpos-lenpos-4); // tracklen fseek(fp, endpos, SEEK_SET); return false; } //--------------------------------------------------------- // writeEvent //--------------------------------------------------------- void MidiFile::writeEvent(const MidiPlayEvent* event) { int c = event->channel(); int nstat = event->type(); // we don't save meta data into smf type 0 files: DELETETHIS 4 ??? // Oct 16, 2011: Apparently it is legal to do that. Part of fix for bug tracker 3293339. //if (MusEGlobal::config.smfFormat == 0 && nstat == ME_META) // return; nstat |= c; // // running status; except for Sysex- and Meta Events // if (((nstat & 0xf0) != 0xf0) && ((nstat != status) || !MusEGlobal::config.expRunningStatus)) { status = nstat; put(nstat); } switch (event->type()) { case ME_NOTEOFF: case ME_NOTEON: case ME_POLYAFTER: case ME_CONTROLLER: case ME_PITCHBEND: put(event->dataA()); put(event->dataB()); break; case ME_PROGRAM: // Program Change case ME_AFTERTOUCH: // Channel Aftertouch put(event->dataA()); break; case ME_SYSEX: put(0xf0); putvl(event->len() + 1); // including 0xf7 write(event->constData(), event->len()); put(0xf7); status = -1; // invalidate running status break; case ME_META: put(0xff); put(event->dataA()); putvl(event->len()); write(event->constData(), event->len()); status = -1; break; } } //--------------------------------------------------------- // write // returns true on error //--------------------------------------------------------- bool MidiFile::write() { write("MThd", 4); writeLong(6); // header len writeShort(MusEGlobal::config.smfFormat); if (MusEGlobal::config.smfFormat == 0) { // DELETETHIS 30 /* // REMOVE Tim. MidiFileTrack dst; for (iMidiFileTrack i = _tracks->begin(); i != _tracks->end(); ++i) { MPEventList* sl = &((*i)->events); for (iMPEvent ie = sl->begin(); ie != sl->end(); ++ie) { // ALERT: Observed a problem here, apparently some of the events are being added too fast. // The dump below tells me some of the events (sysex/meta) are missing from the list! // Apparently it's a timing problem. Very puzzling. // Attempting wild-guess fix now to eliminate multiple MidiFileTracks in MusE::exportMidi()... // Nope. Didn't help. Now that it's a single MidiFileTrack, try skipping this section altogether... // Yes that appears to have fixed it. Weird. What's the difference - the local 'dst' variable ? // Or are there still lurking problems, or something more fundamentally wrong with Event or MPEvent? printf("MidiFile::write adding event to dst:\n"); ie->dump(); dst.events.add(*ie); } } writeShort(1); writeShort(_division); writeTrack(&dst); */ writeShort(1); //writeShort(_division); DELETETHIS 3 //if(!_tracks->empty()) // writeTrack(*(_tracks->begin())); } else { writeShort(ntracks); } writeShort(_division); for (ciMidiFileTrack i = _tracks->begin(); i != _tracks->end(); ++i) writeTrack(*i); return (ferror(fp) != 0); } //--------------------------------------------------------- // readMidi // returns true on error //--------------------------------------------------------- bool MidiFile::read() { _error = MF_NO_ERROR; int i; char tmp[4]; if (read(tmp, 4)) return true; int len = readLong(); if (memcmp(tmp, "MThd", 4) || len < 6) { _error = MF_MTHD; return true; } format = readShort(); ntracks = readShort(); short div = readShort(); //fprintf(stderr, "MidiFile::read(): div:%d\n", div); _divisionIsLinearTime = false; if (div < 0) { _divisionIsLinearTime = true; char fps = -((char)(div >> 8)); div = fps * (div & 0xff); } _division = div; if (len > 6) skip(len-6); // skip excess bytes switch (format) { case 0: { MidiFileTrack* t = new MidiFileTrack; if (readTrack(t)) { delete t; return true; } else _tracks->push_back(t); } break; case 1: for (i = 0; i < ntracks; ++i) { MidiFileTrack* t = new MidiFileTrack; if (readTrack(t)) { delete t; return true; } else _tracks->push_back(t); } break; default: _error = MF_FORMAT; return true; } return false; } void MidiFileTrackList::clearDelete() { for(iterator i = begin(); i != end(); ++i) { if(*i) delete *i; } clear(); } } // namespace MusECore muse-4.0.0/src/muse/midifile.h000066400000000000000000000116271404105311000161530ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midifile.h,v 1.3 2004/01/04 18:24:43 wschweer Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDIFILE_H__ #define __MIDIFILE_H__ #include #include #include #include "globaldefs.h" #include "mpevent.h" namespace MusECore { class MPEventList; class MidiPlayEvent; class MidiInstrument; //--------------------------------------------------------- // MidiFileTrack //--------------------------------------------------------- struct MidiFilePort { bool _isStandardDrums; MType _midiType; QString _instrName; QString _subst4DevName; MidiFilePort() { _midiType = MT_UNKNOWN; _isStandardDrums = false; } }; typedef std::map MidiFilePortMap; typedef MidiFilePortMap::iterator iMidiFilePort; typedef MidiFilePortMap::const_iterator ciMidiFilePort; //--------------------------------------------------------- // MidiFileTrack //--------------------------------------------------------- struct MidiFileTrack { MPEventList events; bool _isDrumTrack; MidiFileTrack() { _isDrumTrack = false; } }; class MidiFileTrackList : public std::list { public: void clearDelete(); }; typedef MidiFileTrackList::iterator iMidiFileTrack; typedef MidiFileTrackList::const_iterator ciMidiFileTrack; //--------------------------------------------------------- // MidiFile //--------------------------------------------------------- class MidiFile { int _error; int format; // smf file format int ntracks; // number of midi tracks int _division; // False: division is standard ticks based musical time. True: division is SMPTE/MTC linear time. bool _divisionIsLinearTime; //MType _mtype; MidiFileTrackList* _tracks; int status, click; int sstatus; int lastport, lastchannel; MType lastMtype; //QString lastMtypeInstrument; QString lastInstrName; QString lastDeviceName; //MidiInstrument* def_instr; MidiFilePortMap* _usedPortMap; FILE* fp; int curPos; bool read(void*, size_t); bool write(const void*, size_t); void put(unsigned char c) { write(&c, 1); } bool skip(size_t); int readShort(); bool writeShort(int); int readLong(); bool writeLong(int); int getvl(); void putvl(unsigned); bool readTrack(MidiFileTrack*); bool writeTrack(const MidiFileTrack*); // returns: // 3 OK // 0 End of track // -1 Event filtered // -2 Error int readEvent(MidiPlayEvent*, MidiFileTrack*); void writeEvent(const MidiPlayEvent*); public: MidiFile(FILE* f); ~MidiFile(); bool read(); bool write(); QString error(); MidiFilePortMap* usedPortMap() { return _usedPortMap; } MidiFileTrackList* trackList() { return _tracks; } int tracks() const { return ntracks; } // Takes ownership of list and its contents. void setTrackList(MidiFileTrackList* tr, int n); void setDivision(int d) { _division = d; } int division() const { return _division; } bool divisionIsLinearTime() const { return _divisionIsLinearTime; } }; } // namespace MusECore #define XCHG_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) #ifdef __i486__ #define XCHG_LONG(x) \ ({ int __value; \ asm ("bswap %1; movl %1,%0" : "=g" (__value) : "r" (x)); \ __value; }) #else #define XCHG_LONG(x) ((((x)&0xFF)<<24) | \ (((x)&0xFF00)<<8) | \ (((x)&0xFF0000)>>8) | \ (((x)>>24)&0xFF)) #endif #if __BYTE_ORDER == __LITTLE_ENDIAN #define BE_SHORT(x) XCHG_SHORT(x) #define BE_LONG(x) XCHG_LONG(x) #else #define BE_SHORT(x) x #define BE_LONG(x) x #endif #endif muse-4.0.0/src/muse/midiport.cpp000066400000000000000000001652601404105311000165560ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midiport.cpp,v 1.21.2.15 2009/12/07 20:11:51 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2012, 2017 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "midiport.h" #include "midi_consts.h" #include "midiseq.h" #include "gconfig.h" #include "globals.h" #include "synth.h" #include "app.h" #include "song.h" #include "menutitleitem.h" #include "icons.h" #include "track.h" #include "drummap.h" #include "audio.h" #include "muse_math.h" #include "sysex_helper.h" // Forwards from header: #include "mididev.h" #include "instruments/minstrument.h" #include "part.h" #include "midi_controller.h" #include "midictrl.h" #include "mpevent.h" #include "xml.h" namespace MusEGlobal { MusECore::MidiPort midiPorts[MusECore::MIDI_PORTS]; } namespace MusECore { MidiControllerList defaultManagedMidiController; //--------------------------------------------------------- // initMidiPorts //--------------------------------------------------------- void initMidiPorts() { defaultManagedMidiController.add(&pitchCtrl); defaultManagedMidiController.add(&programCtrl); defaultManagedMidiController.add(&volumeCtrl); defaultManagedMidiController.add(&panCtrl); defaultManagedMidiController.add(&reverbSendCtrl); defaultManagedMidiController.add(&chorusSendCtrl); defaultManagedMidiController.add(&variationSendCtrl); for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { MidiPort* port = &MusEGlobal::midiPorts[i]; // // create minimum set of managed controllers // to make midi mixer operational // port->addDefaultControllers(); port->changeInstrument(registerMidiInstrument("GM")); port->syncInfo().setPort(i); // Set the first channel on the first port to auto-connect to midi track outputs. if(i == 0) { // robert: removing the default init on several places to allow for the case // where you rather want the midi track to default to the last created port // this can only happen if there is _no_ default set // // port->setDefaultOutChannels(1); port->setDefaultInChannels(1); } } } //--------------------------------------------------------- // MidiPort //--------------------------------------------------------- MidiPort::MidiPort() : _state("not configured") { _initializationsSent = false; _defaultInChannels = 0; _defaultOutChannels = 0; _device = 0; _instrument = 0; _tmpTrackIdx = -1; _controller = new MidiCtrlValListList(); _foundInSongFile = false; } //--------------------------------------------------------- // MidiPort //--------------------------------------------------------- MidiPort::~MidiPort() { delete _controller; } //--------------------------------------------------------- // guiVisible //--------------------------------------------------------- bool MidiPort::guiVisible() const { if(!_device) return false; SynthI* synth = 0; if(_device->isSynti()) synth = static_cast(_device); if(!synth) return false; return synth->guiVisible(); } //--------------------------------------------------------- // showGui //--------------------------------------------------------- void MidiPort::showGui(bool v) { if(!_device) return; SynthI* synth = 0; if(_device->isSynti()) synth = static_cast(_device); if(!synth) return; synth->showGui(v); } //--------------------------------------------------------- // hasGui //--------------------------------------------------------- bool MidiPort::hasGui() const { if(!_device) return false; SynthI* synth = 0; if(_device->isSynti()) synth = static_cast(_device); if(!synth) return false; return synth->hasGui(); } //--------------------------------------------------------- // nativeGuiVisible //--------------------------------------------------------- bool MidiPort::nativeGuiVisible() const { if(!_device) return false; SynthI* synth = 0; if(_device->isSynti()) synth = static_cast(_device); if(!synth) return false; return synth->nativeGuiVisible(); } //--------------------------------------------------------- // showNativeGui //--------------------------------------------------------- void MidiPort::showNativeGui(bool v) { if(!_device) return; SynthI* synth = 0; if(_device->isSynti()) synth = static_cast(_device); if(!synth) return; synth->showNativeGui(v); } //--------------------------------------------------------- // hasNativeGui //--------------------------------------------------------- bool MidiPort::hasNativeGui() const { if(!_device) return false; SynthI* synth = 0; if(_device->isSynti()) synth = static_cast(_device); if(!synth) return false; return synth->hasNativeGui(); } //--------------------------------------------------------- // setDevice //--------------------------------------------------------- void MidiPort::setMidiDevice(MidiDevice* dev, MidiInstrument* instrument) { if (_device) { if (_device->isSynti()) _instrument = genericMidiInstrument; _device->setPort(-1); _device->close(); _initializationsSent = false; // Wait until upcoming process call has finished. Otherwise Jack may crash! MusEGlobal::audio->msgAudioWait(); } if (dev) { for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; if (mp->device() == dev) { if(dev->isSynti()) mp->changeInstrument(genericMidiInstrument); // move device _state = mp->state(); mp->clearDevice(); break; } } _device = dev; // If an instrument was given, use it. Otherwise don't touch the instrument. if(instrument) _instrument = instrument; _state = _device->open(); _device->setPort(portno()); _initializationsSent = false; } else clearDevice(); } //--------------------------------------------------------- // sendPendingInitializations // Return true if success. // To be called from realtime audio thread only. //--------------------------------------------------------- bool MidiPort::sendPendingInitializations(bool force) { if(!_device || !(_device->openFlags() & 1)) // Not writable? return false; bool rv = true; int port = portno(); // // test for explicit instrument initialization // // unsigned last_tick = 0; unsigned last_frame = 0; MusECore::MidiInstrument* instr = instrument(); if(instr && MusEGlobal::config.midiSendInit && (force || !_initializationsSent)) { // Send the Instrument Init sequences. EventList* events = instr->midiInit(); if(!events->empty()) { for(iEvent ie = events->begin(); ie != events->end(); ++ie) { if(ie->second.type() == Sysex) last_frame += sysexDuration(ie->second.dataLen(), MusEGlobal::sampleRate); MusECore::MidiPlayEvent ev = ie->second.asMidiPlayEvent(last_frame + MusEGlobal::audio->curSyncFrame(), port, 0); _device->putEvent(ev, MidiDevice::NotLate); } // Give a bit of time for the last Init sysex to settle? last_frame += 100; } _initializationsSent = true; // Mark as having been sent. } // Send the Instrument controller default values. sendInitialControllers(last_frame); return rv; } //--------------------------------------------------------- // sendInitialControllers // Return true if success. //--------------------------------------------------------- bool MidiPort::sendInitialControllers(unsigned start_time) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; bool rv = true; int port = portno(); // Find all channels of this port used in the song... bool usedChans[MusECore::MUSE_MIDI_CHANNELS]; int usedChanCount = 0; for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) usedChans[i] = false; if(MusEGlobal::song->click() && metro_settings->clickPort == port) { usedChans[metro_settings->clickChan] = true; ++usedChanCount; } for(ciMidiTrack imt = MusEGlobal::song->midis()->begin(); imt != MusEGlobal::song->midis()->end(); ++imt) { if((*imt)->type() == MusECore::Track::DRUM) { for(int i = 0; i < DRUM_MAPSIZE; ++i) { // Default to track port if -1 and track channel if -1. int mport = (*imt)->drummap()[i].port; if(mport == -1) mport = (*imt)->outPort(); int mchan = (*imt)->drummap()[i].channel; if(mchan == -1) mchan = (*imt)->outChannel(); if(mport != port || usedChans[mchan]) continue; usedChans[mchan] = true; ++usedChanCount; if(usedChanCount >= MusECore::MUSE_MIDI_CHANNELS) break; // All are used, done searching. } } else { if((*imt)->outPort() != port || usedChans[(*imt)->outChannel()]) continue; usedChans[(*imt)->outChannel()] = true; ++usedChanCount; } if(usedChanCount >= MusECore::MUSE_MIDI_CHANNELS) break; // All are used, done searching. } // NOT for syntis. Use midiState and/or initParams for that. if(MusEGlobal::config.midiSendInit && MusEGlobal::config.midiSendCtlDefaults && _instrument && !_device->isSynti()) { MusECore::MidiControllerList* cl = new MusECore::MidiControllerList(); MidiController* mc; for(int chan = 0; chan < MusECore::MUSE_MIDI_CHANNELS; ++chan) { if(!usedChans[chan]) continue; // This channel on this port is not used in the song. const int patch = hwCtrlState(chan, CTRL_PROGRAM); cl->clear(); instrument()->getControllers(cl, chan, patch); for(ciMidiController imc = cl->begin(); imc != cl->end(); ++imc) { mc = imc->second; ciMidiCtrlValList i; // Look for an initial value for this midi controller, on this midi channel, in the song... for(i = _controller->begin(); i != _controller->end(); ++i) { int channel = i->first >> 24; int cntrl = i->first & 0xffffff; int val = i->second->hwVal(); if(channel == chan && cntrl == mc->num() && val != CTRL_VAL_UNKNOWN) break; } // If no initial value was found for this midi controller, on this midi channel, in the song... if(i == _controller->end()) { // If the instrument's midi controller has an initial value, send it now. if(mc->initVal() != CTRL_VAL_UNKNOWN) { int ctl = mc->num(); // Note the addition of bias! _device->putEvent(MidiPlayEvent(start_time, port, chan, ME_CONTROLLER, ctl, mc->initVal() + mc->bias()), MidiDevice::NotLate); // Set it once so the 'last HW value' is set, and control knobs are positioned at the value... // Set it again so that control labels show 'off'... setHwCtrlStates(chan, ctl, CTRL_VAL_UNKNOWN, mc->initVal() + mc->bias()); } } } } delete cl; } // init HW controller state for (iMidiCtrlValList i = _controller->begin(); i != _controller->end(); ++i) { int channel = i->first >> 24; if(!usedChans[channel]) continue; // This channel on this port is not used in the song. int cntrl = i->first & 0xffffff; int val = i->second->hwVal(); if (val != CTRL_VAL_UNKNOWN) { _device->putEvent(MidiPlayEvent(start_time, port, channel, ME_CONTROLLER, cntrl, val), MidiDevice::NotLate); // Set it once so the 'last HW value' is set, and control knobs are positioned at the value... setHwCtrlState(channel, cntrl, val); } } return rv; } //--------------------------------------------------------- // changeInstrument // If audio is running (and not idle) this should only be called by the rt audio thread. //--------------------------------------------------------- void MidiPort::changeInstrument(MidiInstrument* i) { if(_instrument == i) return; _instrument = i; _initializationsSent = false; updateDrumMaps(); } //--------------------------------------------------------- // clearDevice //--------------------------------------------------------- void MidiPort::clearDevice() { _device = 0; _initializationsSent = false; _state = "not configured"; } //--------------------------------------------------------- // portno //--------------------------------------------------------- int MidiPort::portno() const { for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { if (&MusEGlobal::midiPorts[i] == this) return i; } return -1; } //--------------------------------------------------------- // portname //--------------------------------------------------------- const QString& MidiPort::portname() const { static const QString none(QT_TRANSLATE_NOOP("@default", "")); if (_device) return _device->name(); else return none; } //--------------------------------------------------------- // tryCtrlInitVal // To be called from realtime audio thread only. //--------------------------------------------------------- void MidiPort::tryCtrlInitVal(int chan, int ctl, int val) { // Look for an initial value in the song for this midi controller, on this midi channel... (p4.0.27) iMidiCtrlValList i = _controller->find(chan, ctl); if(i != _controller->end()) { int v = i->second->value(0); // Value at tick 0. if(v != CTRL_VAL_UNKNOWN) { if(_device) _device->putEvent(MidiPlayEvent(0, portno(), chan, ME_CONTROLLER, ctl, v), MidiDevice::NotLate); // Set it once so the 'last HW value' is set, and control knobs are positioned at the value... setHwCtrlState(chan, ctl, v); return; } } // No initial value was found in the song for this midi controller on this midi channel. Try the instrument... if(_instrument) { const int patch = hwCtrlState(chan, CTRL_PROGRAM); const MidiController* mc = instrument()->findController(ctl, chan, patch); int initval = mc->initVal(); // Initialize from either the instrument controller's initial value, or the supplied value. if(initval != CTRL_VAL_UNKNOWN) { if(_device) { MidiPlayEvent ev(0, portno(), chan, ME_CONTROLLER, ctl, initval + mc->bias()); _device->putEvent(ev, MidiDevice::NotLate); } setHwCtrlStates(chan, ctl, CTRL_VAL_UNKNOWN, initval + mc->bias()); return; } } // No initial value was found in the song or instrument for this midi controller. Just send the given value. if(_device) { MidiPlayEvent ev(0, portno(), chan, ME_CONTROLLER, ctl, val); _device->putEvent(ev, MidiDevice::NotLate); } setHwCtrlStates(chan, ctl, CTRL_VAL_UNKNOWN, val); } //--------------------------------------------------------- // sendGmInitValues //--------------------------------------------------------- void MidiPort::sendGmInitValues() { for (int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { // By T356. Initialize from instrument controller if it has an initial value, otherwise use the specified value. // Tested: Ultimately, a track's controller stored values take priority by sending any 'zero time' value // AFTER these GM/GS/XG init routines are called via initDevices(). tryCtrlInitVal(i, CTRL_PROGRAM, 0); tryCtrlInitVal(i, CTRL_PITCH, 0); tryCtrlInitVal(i, CTRL_VOLUME, 100); tryCtrlInitVal(i, CTRL_PANPOT, 64); tryCtrlInitVal(i, CTRL_REVERB_SEND, 40); tryCtrlInitVal(i, CTRL_CHORUS_SEND, 0); } } //--------------------------------------------------------- // sendGsInitValues //--------------------------------------------------------- void MidiPort::sendGsInitValues() { sendGmInitValues(); } //--------------------------------------------------------- // sendXgInitValues //--------------------------------------------------------- void MidiPort::sendXgInitValues() { for (int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { // By T356. Initialize from instrument controller if it has an initial value, otherwise use the specified value. tryCtrlInitVal(i, CTRL_PROGRAM, 0); tryCtrlInitVal(i, CTRL_MODULATION, 0); tryCtrlInitVal(i, CTRL_PORTAMENTO_TIME, 0); tryCtrlInitVal(i, CTRL_VOLUME, 0x64); tryCtrlInitVal(i, CTRL_PANPOT, 0x40); tryCtrlInitVal(i, CTRL_EXPRESSION, 0x7f); tryCtrlInitVal(i, CTRL_SUSTAIN, 0x0); tryCtrlInitVal(i, CTRL_PORTAMENTO, 0x0); tryCtrlInitVal(i, CTRL_SOSTENUTO, 0x0); tryCtrlInitVal(i, CTRL_SOFT_PEDAL, 0x0); tryCtrlInitVal(i, CTRL_HARMONIC_CONTENT, 0x40); tryCtrlInitVal(i, CTRL_RELEASE_TIME, 0x40); tryCtrlInitVal(i, CTRL_ATTACK_TIME, 0x40); tryCtrlInitVal(i, CTRL_BRIGHTNESS, 0x40); tryCtrlInitVal(i, CTRL_REVERB_SEND, 0x28); tryCtrlInitVal(i, CTRL_CHORUS_SEND, 0x0); tryCtrlInitVal(i, CTRL_VARIATION_SEND, 0x0); } } //--------------------------------------------------------- // sendGmOn // send GM-On message to midi device and keep track // of device state //--------------------------------------------------------- void MidiPort::sendGmOn() { sendSysex(gmOnMsg, gmOnMsgLen); } //--------------------------------------------------------- // sendGsOn // send Roland GS-On message to midi device and keep track // of device state //--------------------------------------------------------- void MidiPort::sendGsOn() { sendSysex(gsOnMsg2, gsOnMsg2Len); sendSysex(gsOnMsg3, gsOnMsg3Len); } //--------------------------------------------------------- // sendXgOn // send Yamaha XG-On message to midi device and keep track // of device state //--------------------------------------------------------- void MidiPort::sendXgOn() { sendSysex(xgOnMsg, xgOnMsgLen); } //--------------------------------------------------------- // sendSysex // send SYSEX message to midi device //--------------------------------------------------------- void MidiPort::sendSysex(const unsigned char* p, int n) { if (_device) { MidiPlayEvent event(0, 0, ME_SYSEX, p, n); _device->putEvent(event, MidiDevice::NotLate); } } //--------------------------------------------------------- // sendMMCLocate //--------------------------------------------------------- void MidiPort::sendMMCLocate(unsigned char ht, unsigned char m, unsigned char s, unsigned char f, unsigned char sf, int devid) { unsigned char msg[mmcLocateMsgLen]; memcpy(msg, mmcLocateMsg, mmcLocateMsgLen); if(devid != -1) msg[1] = devid; else msg[1] = _syncInfo.idOut(); msg[6] = ht; msg[7] = m; msg[8] = s; msg[9] = f; msg[10] = sf; sendSysex(msg, mmcLocateMsgLen); } //--------------------------------------------------------- // sendMMCStop //--------------------------------------------------------- void MidiPort::sendMMCStop(int devid) { unsigned char msg[mmcStopMsgLen]; memcpy(msg, mmcStopMsg, mmcStopMsgLen); if(devid != -1) msg[1] = devid; else msg[1] = _syncInfo.idOut(); sendSysex(msg, mmcStopMsgLen); } //--------------------------------------------------------- // sendMMCDeferredPlay //--------------------------------------------------------- void MidiPort::sendMMCDeferredPlay(int devid) { unsigned char msg[mmcDeferredPlayMsgLen]; memcpy(msg, mmcDeferredPlayMsg, mmcDeferredPlayMsgLen); if(devid != -1) msg[1] = devid; else msg[1] = _syncInfo.idOut(); sendSysex(msg, mmcDeferredPlayMsgLen); } //--------------------------------------------------------- // sendStart //--------------------------------------------------------- void MidiPort::sendStart() { if (_device) { MidiPlayEvent event(0, 0, 0, ME_START, 0, 0); _device->putEvent(event, MidiDevice::NotLate); } } //--------------------------------------------------------- // sendStop //--------------------------------------------------------- void MidiPort::sendStop() { if (_device) { MidiPlayEvent event(0, 0, 0, ME_STOP, 0, 0); _device->putEvent(event, MidiDevice::NotLate); } } //--------------------------------------------------------- // sendClock //--------------------------------------------------------- void MidiPort::sendClock() { if (_device) { MidiPlayEvent event(0, 0, 0, ME_CLOCK, 0, 0); _device->putEvent(event, MidiDevice::NotLate); } } //--------------------------------------------------------- // sendContinue //--------------------------------------------------------- void MidiPort::sendContinue() { if (_device) { MidiPlayEvent event(0, 0, 0, ME_CONTINUE, 0, 0); _device->putEvent(event, MidiDevice::NotLate); } } //--------------------------------------------------------- // sendSongpos //--------------------------------------------------------- void MidiPort::sendSongpos(int pos) { if (_device) { MidiPlayEvent event(0, 0, 0, ME_SONGPOS, pos, 0); _device->putEvent(event, MidiDevice::NotLate); } } //--------------------------------------------------------- // addManagedController //--------------------------------------------------------- MidiCtrlValList* MidiPort::addManagedController(int channel, int ctrl) { iMidiCtrlValList cl = _controller->find(channel, ctrl); if (cl == _controller->end()) { MidiCtrlValList* pvl = new MidiCtrlValList(ctrl); _controller->add(channel, pvl); return pvl; } else return cl->second; } //--------------------------------------------------------- // addDefaultControllers //--------------------------------------------------------- void MidiPort::addDefaultControllers() { for (int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { for(ciMidiController imc = defaultManagedMidiController.begin(); imc != defaultManagedMidiController.end(); ++imc) addManagedController(i, imc->second->num()); _automationType[i] = AUTO_READ; } } //--------------------------------------------------------- // limitValToInstrCtlRange //--------------------------------------------------------- int MidiPort::limitValToInstrCtlRange(const MidiController* mc, int val) { if(!_instrument || !mc || val == CTRL_VAL_UNKNOWN) return val; //MidiController* mc = imc->second; int mn = mc->minVal(); int mx = mc->maxVal(); int bias = mc->bias(); // Subtract controller bias from value. val -= bias; // Limit value to controller range. if(val < mn) val = mn; else if(val > mx) val = mx; // Re-add controller bias to value. val += bias; return val; } int MidiPort::limitValToInstrCtlRange(int ctl, int val, int chan) { if(!_instrument || val == CTRL_VAL_UNKNOWN) return val; // FIXME: This might be optimized by calling midiController instead, // and simply asking if it's a drum controller. Saves one list iteration. // Is it a drum controller? // Note: Midnam apparently has no concept of drum controllers, so the fact // that this only asks in the instrument's controllers and not the midnam is OK. const MidiController *mc = drumController(ctl); if(!mc) { // It's not a drum controller. Find it as a regular controller instead. const int patch = hwCtrlState(chan, CTRL_PROGRAM); mc = _instrument->findController(ctl, chan, patch); } // If it's a valid controller, limit the value to the instrument controller range. if(mc) return limitValToInstrCtlRange(mc, val); return val; } double MidiPort::limitValToInstrCtlRange(const MidiController* mc, double val) { if(!_instrument || !mc || int(val) == CTRL_VAL_UNKNOWN) return val; const double mn = double(mc->minVal()); const double mx = double(mc->maxVal()); const double bias = double(mc->bias()); // Subtract controller bias from value. val -= double(bias); // Limit value to controller range. if(val < mn) val = mn; else if(val > mx) val = mx; // Re-add controller bias to value. val += bias; return val; } double MidiPort::limitValToInstrCtlRange(int ctl, double val, int chan) { if(!_instrument || int(val) == CTRL_VAL_UNKNOWN) return val; // FIXME: This might be optimized by calling midiController instead, // and simply asking if it's a drum controller. Saves one list iteration. // Is it a drum controller? // Note: Midnam apparently has no concept of drum controllers, so the fact // that this only asks in the instrument's controllers and not the midnam is OK. const MidiController *mc = drumController(ctl); if(!mc) { // It's not a drum controller. Find it as a regular controller instead. const int patch = hwCtrlState(chan, CTRL_PROGRAM); mc = _instrument->findController(ctl, chan, patch); } // If it's a valid controller, limit the value to the instrument controller range. if(mc) return limitValToInstrCtlRange(mc, val); return val; } //--------------------------------------------------------- // createController // Creates a controller in this port's controller list. // Returns true if the controller was created. // To be called by gui thread only. //--------------------------------------------------------- bool MidiPort::createController(int chan, int ctrl) { if(ctrl < 0 || chan < 0 || chan >= MusECore::MUSE_MIDI_CHANNELS) return false; PendingOperationList operations; // Make sure the controller exists, create it if not. iMidiCtrlValList cl = _controller->find(chan, ctrl); if(cl != _controller->end()) return false; PendingOperationItem poi(_controller, 0, chan, ctrl, PendingOperationItem::AddMidiCtrlValList); // This step is intended in case we ever pass an operations list to this function. if(operations.findAllocationOp(poi) != operations.end()) return false; MidiCtrlValList* mcvl = new MidiCtrlValList(ctrl); poi._mcvl = mcvl; operations.add(poi); // This waits for audio process thread to execute it. MusEGlobal::audio->msgExecutePendingOperations(operations, true); return true; } //--------------------------------------------------------- // putHwCtrlEvent // To be called from gui thread only. // Returns true if event cannot be delivered. //--------------------------------------------------------- bool MidiPort::putHwCtrlEvent(const MidiPlayEvent& ev) { const int ctrl = ev.translateCtrlNum(); // Event not translatable to a controller? if(ctrl < 0) return true; // Make sure to create the controller if necessary. //createController(chan, ctrl); // Make sure the controller exists, create it if not. const int chan = ev.channel(); ciMidiCtrlValList cl = _controller->find(chan, ctrl); // Controller does not exist? if(cl == _controller->end()) { // Tell the gui thread to create and add a new controller. // It will store and re-deliver the events directly to the buffers // after the controller is created. MusEGlobal::song->putIpcInEvent(ev); // Technically the event is being delivered. return false; } if(!MusEGlobal::song->putIpcOutEvent(ev)) { fprintf(stderr, "MidiPort::putHwCtrlEvent: Error: gui2AudioFifo fifo overflow\n"); return true; } return false; } //--------------------------------------------------------- // putEvent // To be called from gui thread only. // Returns true if event cannot be delivered. //--------------------------------------------------------- bool MidiPort::putEvent(const MidiPlayEvent& ev) { // Send the event to the device first so that current parameters could be updated on process. bool res = false; if(_device) { res = !_device->putEvent(ev, MidiDevice::Late); } putHwCtrlEvent(ev); return res; } //--------------------------------------------------------- // putControllerValue // To be called from gui thread only. // Returns true if event cannot be delivered. //--------------------------------------------------------- bool MidiPort::putControllerValue(int port, int chan, int ctlnum, double val, bool isDb) { iMidiCtrlValList imcvl = _controller->find(chan, ctlnum); if(imcvl == _controller->end()) return true; // Don't create if not found. MusECore::MidiController* mc = midiController(ctlnum, chan, false); if(!mc) return true; const int max = mc->maxVal(); if(isDb) val = double(max) * muse_db2val(val / 2.0); const int i_new_val = MidiController::dValToInt(val); // Time-stamp the event. MidiPlayEvent ev(MusEGlobal::audio->curFrame(), port, chan, MusECore::ME_CONTROLLER, ctlnum, i_new_val); bool res = false; if(_device) { res = !_device->putEvent(ev, MidiDevice::Late); } putHwCtrlEvent(ev); return res; } //--------------------------------------------------------- // handleGui2AudioEvent // To be called from audio thread only. // Returns true on success. // If createAsNeeded is true, automatically send a message to the gui thread to // create items such as controllers, and cache the events sent to it and re-put // them after the controller has been created. //--------------------------------------------------------- bool MidiPort::handleGui2AudioEvent(const MidiPlayEvent& ev, bool createAsNeeded) { const int chn = ev.channel(); const int type = ev.type(); const int i_dataA = ev.dataA(); const double d_dataB = ev.dataB(); const int i_dataB = MidiController::dValToInt(d_dataB); int fin_db = i_dataB; switch(type) { case ME_CONTROLLER: switch(i_dataA) { case CTRL_HBANK: { // Does the CTRL_PROGRAM controller exist? iMidiCtrlValList imcvl = _controller->find(chn, CTRL_PROGRAM); if(imcvl == _controller->end()) { // Tell the gui to create the controller and add the value. if(createAsNeeded) return MusEGlobal::song->putIpcInEvent(ev); return false; } int hb = 0xff; if(!MidiController::iValIsUnknown(i_dataB)) hb = i_dataB & 0xff; if(hb != 0xff) hb = limitValToInstrCtlRange(i_dataA, hb, chn); int lb = 0xff; int pr = 0xff; MidiCtrlValList* mcvl = imcvl->second; if(!mcvl->hwValIsUnknown()) { const int hw_val = mcvl->hwVal(); lb = (hw_val >> 8) & 0xff; pr = hw_val & 0xff; } if((hb != 0xff || lb != 0xff) && pr == 0xff) pr = 0x01; if(hb == 0xff && lb == 0xff && pr == 0xff) fin_db = CTRL_VAL_UNKNOWN; else fin_db = (hb << 16) | (lb << 8) | pr; // Set the value. Be sure to update drum maps (and inform the gui). if(mcvl->setHwVal(fin_db)) updateDrumMaps(chn, fin_db); return true; } break; case CTRL_LBANK: { // Does the CTRL_PROGRAM controller exist? iMidiCtrlValList imcvl = _controller->find(chn, CTRL_PROGRAM); if(imcvl == _controller->end()) { // Tell the gui to create the controller and add the value. if(createAsNeeded) return MusEGlobal::song->putIpcInEvent(ev); return false; } int hb = 0xff; int lb = 0xff; if(!MidiController::iValIsUnknown(i_dataB)) lb = i_dataB & 0xff; if(lb != 0xff) lb = limitValToInstrCtlRange(i_dataA, lb, chn); int pr = 0xff; MidiCtrlValList* mcvl = imcvl->second; if(!mcvl->hwValIsUnknown()) { const int hw_val = mcvl->hwVal(); hb = (hw_val >> 16) & 0xff; pr = hw_val & 0xff; } if((hb != 0xff || lb != 0xff) && pr == 0xff) pr = 0x01; if(hb == 0xff && lb == 0xff && pr == 0xff) fin_db = CTRL_VAL_UNKNOWN; else fin_db = (hb << 16) | (lb << 8) | pr; // Set the value. Be sure to update drum maps (and inform the gui). if(mcvl->setHwVal(fin_db)) updateDrumMaps(chn, fin_db); return true; } break; case CTRL_PROGRAM: { // TODO: Maybe update CTRL_HBANK/CTRL_LBANK - but ONLY if they are specifically // defined by the user in the controller list. // Does the CTRL_PROGRAM controller exist? iMidiCtrlValList imcvl = _controller->find(chn, CTRL_PROGRAM); if(imcvl == _controller->end()) { // Tell the gui to create the controller and add the value. if(createAsNeeded) return MusEGlobal::song->putIpcInEvent(ev); return false; } // Set the value. Be sure to update drum maps (and inform the gui). if(imcvl->second->setHwVal(fin_db)) updateDrumMaps(chn, fin_db); return true; } break; default: { // Does the controller exist? iMidiCtrlValList imcvl = _controller->find(chn, i_dataA); if(imcvl == _controller->end()) { // Tell the gui to create the controller and add the value. if(createAsNeeded) return MusEGlobal::song->putIpcInEvent(ev); return false; } fin_db = limitValToInstrCtlRange(i_dataA, i_dataB, chn); // Set the value. imcvl->second->setHwVal(fin_db); return true; } break; } break; case ME_POLYAFTER: { const int pitch = i_dataA & 0x7f; const int fin_da = (CTRL_POLYAFTER & ~0xff) | pitch; // Does the controller exist? iMidiCtrlValList imcvl = _controller->find(chn, fin_da); if(imcvl == _controller->end()) { // Tell the gui to create the controller and add the value. if(createAsNeeded) return MusEGlobal::song->putIpcInEvent(ev); return false; } fin_db = limitValToInstrCtlRange(fin_da, i_dataB, chn); // Set the value. imcvl->second->setHwVal(fin_db); return true; } break; case ME_AFTERTOUCH: { // Does the controller exist? iMidiCtrlValList imcvl = _controller->find(chn, CTRL_AFTERTOUCH); if(imcvl == _controller->end()) { // Tell the gui to create the controller and add the value. if(createAsNeeded) return MusEGlobal::song->putIpcInEvent(ev); return false; } fin_db = limitValToInstrCtlRange(CTRL_AFTERTOUCH, i_dataA, chn); // Set the value. imcvl->second->setHwVal(fin_db); return true; } break; case ME_PITCHBEND: { // Does the controller exist? iMidiCtrlValList imcvl = _controller->find(chn, CTRL_PITCH); if(imcvl == _controller->end()) { // Tell the gui to create the controller and add the value. if(createAsNeeded) return MusEGlobal::song->putIpcInEvent(ev); return false; } fin_db = limitValToInstrCtlRange(CTRL_PITCH, i_dataA, chn); // Set the value. imcvl->second->setHwVal(fin_db); return true; } break; case ME_PROGRAM: { // Does the controller exist? iMidiCtrlValList imcvl = _controller->find(chn, CTRL_PROGRAM); if(imcvl == _controller->end()) { // Tell the gui to create the controller and add the value. if(createAsNeeded) return MusEGlobal::song->putIpcInEvent(ev); return false; } int hb = 0xff; int lb = 0xff; int pr = 0xff; if(!MidiController::iValIsUnknown(i_dataA)) pr = i_dataA & 0xff; //if(pr != 0xff) // pr = limitValToInstrCtlRange(da, pr, chn); MidiCtrlValList* mcvl = imcvl->second; if(!mcvl->hwValIsUnknown()) { const int hw_val = mcvl->hwVal(); hb = (hw_val >> 16) & 0xff; lb = (hw_val >> 8) & 0xff; } if((hb != 0xff || lb != 0xff) && pr == 0xff) pr = 0x01; if(hb == 0xff && lb == 0xff && pr == 0xff) fin_db = CTRL_VAL_UNKNOWN; else fin_db = (hb << 16) | (lb << 8) | pr; // Set the value. Be sure to update drum maps (and inform the gui). if(mcvl->setHwVal(fin_db)) updateDrumMaps(chn, fin_db); return true; } break; default: break; } return false; } //--------------------------------------------------------- // sendHwCtrlState // Return true if it is OK to go ahead and deliver the event. //--------------------------------------------------------- bool MidiPort::sendHwCtrlState(const MidiPlayEvent& ev, bool forceSend) { const int type = ev.type(); const int chn = ev.channel(); const int da = ev.dataA(); int fin_da = da; const int db = ev.dataB(); switch(type) { case ME_CONTROLLER: switch(da) { case CTRL_HBANK: { int hb = 0xff; if(!MidiController::iValIsUnknown(db)) hb = db & 0xff; if(hb != 0xff) hb = limitValToInstrCtlRange(da, hb, chn); int lb = 0xff; int pr = 0xff; fin_da = CTRL_PROGRAM; // This will create a new value list if necessary, otherwise it returns the existing list. // FIXME: This is not realtime safe because it may allocate. MidiCtrlValList* mcvl = addManagedController(chn, fin_da); if(!mcvl->hwValIsUnknown()) { const int hw_val = mcvl->hwVal(); lb = (hw_val >> 8) & 0xff; pr = hw_val & 0xff; } if((hb != 0xff || lb != 0xff) && pr == 0xff) pr = 0x01; } break; case CTRL_LBANK: { int hb = 0xff; int lb = 0xff; if(!MidiController::iValIsUnknown(db)) lb = db & 0xff; if(lb != 0xff) lb = limitValToInstrCtlRange(da, lb, chn); int pr = 0xff; fin_da = CTRL_PROGRAM; // This will create a new value list if necessary, otherwise it returns the existing list. // FIXME: This is not realtime safe because it may allocate. MidiCtrlValList* mcvl = addManagedController(chn, fin_da); if(!mcvl->hwValIsUnknown()) { const int hw_val = mcvl->hwVal(); hb = (hw_val >> 16) & 0xff; pr = hw_val & 0xff; } if((hb != 0xff || lb != 0xff) && pr == 0xff) pr = 0x01; } break; case CTRL_PROGRAM: // This will create a new value list if necessary, otherwise it returns the existing list. // FIXME: This is not realtime safe because it may allocate. addManagedController(chn, da); // TODO: Maybe update CTRL_HBANK/CTRL_LBANK - but ONLY if they are specifically // defined by the user in the controller list. break; default: // This will create a new value list if necessary, otherwise it returns the existing list. // FIXME: This is not realtime safe because it may allocate. addManagedController(chn, da); break; } break; case ME_POLYAFTER: { const int pitch = da & 0x7f; fin_da = (CTRL_POLYAFTER & ~0xff) | pitch; // This will create a new value list if necessary, otherwise it returns the existing list. // FIXME: This is not realtime safe because it may allocate. addManagedController(chn, fin_da); } break; case ME_AFTERTOUCH: { fin_da = CTRL_AFTERTOUCH; // This will create a new value list if necessary, otherwise it returns the existing list. // FIXME: This is not realtime safe because it may allocate. addManagedController(chn, fin_da); } break; case ME_PITCHBEND: { fin_da = CTRL_PITCH; // This will create a new value list if necessary, otherwise it returns the existing list. // FIXME: This is not realtime safe because it may allocate. addManagedController(chn, fin_da); } break; case ME_PROGRAM: { int hb = 0xff; int lb = 0xff; int pr = 0xff; if(!MidiController::iValIsUnknown(da)) pr = da & 0xff; //if(pr != 0xff) // pr = limitValToInstrCtlRange(da, pr, chn); fin_da = CTRL_PROGRAM; // This will create a new value list if necessary, otherwise it returns the existing list. // FIXME: This is not realtime safe because it may allocate. MidiCtrlValList* mcvl = addManagedController(chn, fin_da); if(!mcvl->hwValIsUnknown()) { const int hw_val = mcvl->hwVal(); hb = (hw_val >> 16) & 0xff; lb = (hw_val >> 8) & 0xff; } if((hb != 0xff || lb != 0xff) && pr == 0xff) pr = 0x01; } break; default: // For all other event types, just return true. return true; break; } if(!setHwCtrlState(chn, da, db)) { if (MusEGlobal::debugMsg && forceSend) printf("sendHwCtrlState: State already set. Forcing anyway...\n"); if (!forceSend) return false; } return true; } //--------------------------------------------------------- // lastValidHWCtrlState //--------------------------------------------------------- int MidiPort::lastValidHWCtrlState(int ch, int ctrl) const { ch &= 0xff; ciMidiCtrlValList cl = ((const MidiCtrlValListList*)_controller)->find(ch, ctrl); if (cl == _controller->end()) { return CTRL_VAL_UNKNOWN; } MidiCtrlValList* vl = cl->second; return vl->lastValidHWVal(); } //--------------------------------------------------------- // lastValidHWCtrlState //--------------------------------------------------------- double MidiPort::lastValidHWDCtrlState(int ch, int ctrl) const { ch &= 0xff; ciMidiCtrlValList cl = ((const MidiCtrlValListList*)_controller)->find(ch, ctrl); if (cl == _controller->end()) { return CTRL_VAL_UNKNOWN; } MidiCtrlValList* vl = cl->second; return vl->lastValidHWDVal(); } //--------------------------------------------------------- // hwCtrlState //--------------------------------------------------------- int MidiPort::hwCtrlState(int ch, int ctrl) const { ch &= 0xff; ciMidiCtrlValList cl = ((const MidiCtrlValListList*)_controller)->find(ch, ctrl); if (cl == _controller->end()) return CTRL_VAL_UNKNOWN; MidiCtrlValList* vl = cl->second; return vl->hwVal(); } //--------------------------------------------------------- // hwDCtrlState //--------------------------------------------------------- double MidiPort::hwDCtrlState(int ch, int ctrl) const { ch &= 0xff; ciMidiCtrlValList cl = ((const MidiCtrlValListList*)_controller)->find(ch, ctrl); if (cl == _controller->end()) return CTRL_VAL_UNKNOWN; MidiCtrlValList* vl = cl->second; return vl->hwDVal(); } //--------------------------------------------------------- // setHwCtrlState // If audio is running (and not idle) this should only be called by the rt audio thread. // Returns false if value is already equal, true if value is set. //--------------------------------------------------------- bool MidiPort::setHwCtrlState(const MidiPlayEvent& ev) { const int port = ev.port(); if(port < 0 || port >= MusECore::MIDI_PORTS) return false; // Handle the event. Tell the gui to create controllers as needed. return MusEGlobal::midiPorts[port].handleGui2AudioEvent(ev, true); } bool MidiPort::setHwCtrlState(int ch, int ctrl, int val) { // This will create a new value list if necessary, otherwise it returns the existing list. MidiCtrlValList* vl = addManagedController(ch, ctrl); bool res = vl->setHwVal(val); // If program controller be sure to update drum maps (and inform the gui). if(res && ctrl == CTRL_PROGRAM) updateDrumMaps(ch, val); return res; } bool MidiPort::setHwCtrlState(int ch, int ctrl, double val) { // This will create a new value list if necessary, otherwise it returns the existing list. MidiCtrlValList* vl = addManagedController(ch, ctrl); bool res = vl->setHwVal(val); // If program controller be sure to update drum maps (and inform the gui). if(res && ctrl == CTRL_PROGRAM) updateDrumMaps(ch, val); return res; } //--------------------------------------------------------- // setHwCtrlStates // If audio is running (and not idle) this should only be called by the rt audio thread. // Sets current and last HW values. // Handy for forcing labels to show 'off' and knobs to show specific values // without having to send two messages. // Returns false if both values are already set, true if either value is changed. //--------------------------------------------------------- bool MidiPort::setHwCtrlStates(int ch, int ctrl, int val, int lastval) { // This will create a new value list if necessary, otherwise it returns the existing list. MidiCtrlValList* vl = addManagedController(ch, ctrl); // This is not perfectly ideal, drum maps should not have to (check) change if last hw val changed. bool res = vl->setHwVals(val, lastval); // If program controller be sure to update drum maps (and inform the gui). if(res && ctrl == CTRL_PROGRAM) updateDrumMaps(ch, val); return res; } bool MidiPort::setHwCtrlStates(int ch, int ctrl, double val, double lastval) { // This will create a new value list if necessary, otherwise it returns the existing list. MidiCtrlValList* vl = addManagedController(ch, ctrl); // This is not perfectly ideal, drum maps should not have to (check) change if last hw val changed. bool res = vl->setHwVals(val, lastval); // If program controller be sure to update drum maps (and inform the gui). if(res && ctrl == CTRL_PROGRAM) updateDrumMaps(ch, val); return res; } //--------------------------------------------------------- // setControllerVal // This function sets a controller value, // creating the controller if necessary. // Returns true if a value was actually added. //--------------------------------------------------------- bool MidiPort::setControllerVal(int ch, unsigned int tick, int ctrl, int val, Part* part) { MidiCtrlValList* pvl; iMidiCtrlValList cl = _controller->find(ch, ctrl); if (cl == _controller->end()) { pvl = new MidiCtrlValList(ctrl); _controller->add(ch, pvl); } else pvl = cl->second; return pvl->addMCtlVal(tick, val, part); } //--------------------------------------------------------- // updateDrumMaps // If audio is running (and not idle) this should only be called by the rt audio thread. // Returns true if maps were changed. //--------------------------------------------------------- bool MidiPort::updateDrumMaps(int chan, int patch) { int port; int tpatch; int tchan; bool map_changed = false; MidiTrack* mt; for(iMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t) { mt = *t; if(mt->type() != Track::DRUM) continue; port = mt->outPort(); if(port < 0 || port >= MusECore::MIDI_PORTS || &MusEGlobal::midiPorts[port] != this) continue; tchan = mt->outChannel(); if(tchan != chan) continue; tpatch = hwCtrlState(tchan, CTRL_PROGRAM); if(tpatch != patch) continue; if(mt->updateDrummap(false)) // false = don't signal gui thread, we'll do that here. map_changed = true; } if(map_changed) { // It is possible we are being called from gui thread already, in audio idle mode. // Will this still work, and not conflict with audio sending the same message? // Are we are not supposed to write to an fd from different threads? if(!MusEGlobal::audio || MusEGlobal::audio->isIdle()) // Directly emit SC_DRUMMAP song changed signal. MusEGlobal::song->update(SC_DRUMMAP); else // Tell the gui to emit SC_DRUMMAP song changed signal. MusEGlobal::audio->sendMsgToGui('D'); // Drum map changed. return true; } return false; } //--------------------------------------------------------- // updateDrumMaps // If audio is running (and not idle) this should only be called by the rt audio thread. // Returns true if maps were changed. //--------------------------------------------------------- bool MidiPort::updateDrumMaps() { int port; bool map_changed = false; MidiTrack* mt; for(iMidiTrack t = MusEGlobal::song->midis()->begin(); t != MusEGlobal::song->midis()->end(); ++t) { mt = *t; if(mt->type() != Track::DRUM) continue; port = mt->outPort(); if(port < 0 || port >= MusECore::MIDI_PORTS || &MusEGlobal::midiPorts[port] != this) continue; if(mt->updateDrummap(false)) // false = don't signal gui thread, we'll do that here. map_changed = true; } if(map_changed) { // It is possible we are being called from gui thread already, in audio idle mode. // Will this still work, and not conflict with audio sending the same message? // Are we are not supposed to write to an fd from different threads? //if(MusEGlobal::audio && MusEGlobal::audio->isIdle() && MusEGlobal::midiSeq && MusEGlobal::midiSeq->isIdle()) if(!MusEGlobal::audio || MusEGlobal::audio->isIdle()) // Directly emit SC_DRUMMAP song changed signal. //MusEGlobal::song->update(SC_DRUMMAP, true); MusEGlobal::song->update(SC_DRUMMAP); else // Tell the gui to emit SC_DRUMMAP song changed signal. MusEGlobal::audio->sendMsgToGui('D'); // Drum map changed. return true; } return false; } //--------------------------------------------------------- // getCtrl //--------------------------------------------------------- int MidiPort::getCtrl(int ch, unsigned int tick, int ctrl) const { iMidiCtrlValList cl = _controller->find(ch, ctrl); if (cl == _controller->end()) return CTRL_VAL_UNKNOWN; return cl->second->value(tick); } int MidiPort::getCtrl(int ch, unsigned int tick, int ctrl, Part* part) const { iMidiCtrlValList cl = _controller->find(ch, ctrl); if (cl == _controller->end()) return CTRL_VAL_UNKNOWN; return cl->second->value(tick, part); } int MidiPort::getVisibleCtrl(int ch, unsigned int tick, int ctrl, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const { iMidiCtrlValList cl = _controller->find(ch, ctrl); if (cl == _controller->end()) return CTRL_VAL_UNKNOWN; return cl->second->visibleValue(tick, inclMutedParts, inclMutedTracks, inclOffTracks); } int MidiPort::getVisibleCtrl(int ch, unsigned int tick, int ctrl, Part* part, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const { iMidiCtrlValList cl = _controller->find(ch, ctrl); if (cl == _controller->end()) return CTRL_VAL_UNKNOWN; return cl->second->visibleValue(tick, part, inclMutedParts, inclMutedTracks, inclOffTracks); } //--------------------------------------------------------- // deleteController //--------------------------------------------------------- void MidiPort::deleteController(int ch, unsigned int tick, int ctrl, int val, Part* part) { iMidiCtrlValList cl = _controller->find(ch, ctrl); if (cl == _controller->end()) { if (MusEGlobal::debugMsg) printf("deleteController: controller %d(0x%x) for channel %d not found size %zd\n", ctrl, ctrl, ch, _controller->size()); return; } cl->second->delMCtlVal(tick, part, val); } //--------------------------------------------------------- // midiController //--------------------------------------------------------- MidiController* MidiPort::midiController(int num, int chan, bool createIfNotFound) const { MidiController* mc = nullptr; // Search the instrument's controller lists (including midnam controllers). if (_instrument) { const int patch = hwCtrlState(chan, CTRL_PROGRAM); mc = _instrument->findController(num, chan, patch); if(mc) return mc; } // Search the global default controller list. mc = defaultMidiController.findController(num); if(mc) return mc; if(!createIfNotFound) return nullptr; const QString name = midiCtrlName(num); int min = 0; int max = 127; MidiController::ControllerType t = midiControllerType(num); switch (t) { case MidiController::RPN: case MidiController::NRPN: case MidiController::Controller7: case MidiController::PolyAftertouch: case MidiController::Aftertouch: max = 127; break; case MidiController::Controller14: case MidiController::RPN14: case MidiController::NRPN14: max = 16383; break; case MidiController::Program: max = 0xffffff; break; case MidiController::Pitch: max = 8191; min = -8192; break; case MidiController::Velo: // cannot happen return nullptr; break; } mc = new MidiController(name, num, min, max, 0, 0); defaultMidiController.add(mc); return mc; } //--------------------------------------------------------- // drumController // Returns instrument drum controller if ctl is a drum controller number. // Otherwise returns zero. // NOTE: Midnam apparently has no concept of drum controllers, so the fact // that this only asks in the instrument's controllers and not the midnam is OK. //--------------------------------------------------------- MidiController* MidiPort::drumController(int ctl) { if(!_instrument) return nullptr; MidiControllerList* cl = _instrument->controller(); if(!cl) return nullptr; return cl->perNoteController(ctl); } //--------------------------------------------------------- // writeRouting //--------------------------------------------------------- void MidiPort::writeRouting(int level, Xml& xml) const { // If this device is not actually in use by the song, do not write any routes. // This prevents bogus routes from being saved and propagated in the med file. // p4.0.17 Reverted. Allow ports with no device to save. //if(!device()) // return; QString s; for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r) { if(r->type == Route::TRACK_ROUTE && r->track) { // Ignore Midi Port to Audio Input routes. Handled by Track route writer. p4.0.14 Tim. if(r->track->type() == Track::AUDIO_INPUT) continue; s = "Route"; if(r->channel != -1) s += QString(" channel=\"%1\"").arg(r->channel); xml.tag(level++, s.toLatin1().constData()); xml.tag(level, "source mport=\"%d\"/", portno()); s = "dest"; s += QString(" track=\"%1\"/").arg(MusEGlobal::song->tracks()->index(r->track)); xml.tag(level, s.toLatin1().constData()); xml.etag(level--, "Route"); } } } // p4.0.17 Turn off if and when multiple output routes supported. #if 1 //--------------------------------------------------------- // setPortExclusiveDefOutChan //--------------------------------------------------------- void setPortExclusiveDefOutChan(int port, int c) { if(port < 0 || port >= MusECore::MIDI_PORTS) return; MusEGlobal::midiPorts[port].setDefaultOutChannels(c); for(int i = 0; i < MusECore::MIDI_PORTS; ++i) if(i != port) MusEGlobal::midiPorts[i].setDefaultOutChannels(0); } #endif } // namespace MusECore muse-4.0.0/src/muse/midiport.h000066400000000000000000000276531404105311000162260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midiport.h,v 1.9.2.6 2009/11/17 22:08:22 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2012, 2017 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDIPORT_H__ #define __MIDIPORT_H__ #include #include "globaldefs.h" #include "sync.h" #include "route.h" namespace MusECore { // Forward declarations: class MidiDevice; class MidiInstrument; class MidiController; class MidiControllerList; class MidiCtrlValListList; class MidiCtrlValList; class Part; class MidiPlayEvent; class Xml; //--------------------------------------------------------- // MidiPort //--------------------------------------------------------- class MidiPort { public: // IDs for the various IPC FIFOs that are used. enum EventFifoIds { // Playback queued events put by the audio process thread. PlayFifo=0, // Gui events put by our gui thread. GuiFifo=1, // OSC events put by the OSC thread. OSCFifo=2, // Monitor input passthrough events put by Jack devices (audio process thread). JackFifo=3, // Monitor input passthrough events put by ALSA devices (midi seq thread). ALSAFifo=4 }; private: MidiCtrlValListList* _controller; MidiDevice* _device; QString _state; // result of device open MidiInstrument* _instrument; // A corresponding synth track's index in the song file, if used. Can be -1. // Temporary during loading to avoid using globally or locally // 'unique' identifiers in the song file, such as the serial, // to resolve references. int _tmpTrackIdx; // Temporary during loading. String ref to an instrument. // It also helps support older files where only a string was used. QString _tmpInstrRef; AutomationType _automationType[MusECore::MUSE_MIDI_CHANNELS]; // Holds sync settings and detection monitors. MidiSyncInfo _syncInfo; // Just a flag to say the port was found in the song file, even if it has no device right now. bool _foundInSongFile; // When creating a new midi track, add these global default channel routes to/from this port. Ignored if 0. int _defaultInChannels; // These are bit-wise channel masks. int _defaultOutChannels; // // Whether Init sysexes and default controller values have been sent. To be reset whenever // something about the port changes like device, Jack routes, or instrument. bool _initializationsSent; RouteList _inRoutes, _outRoutes; void clearDevice(); // Creates a controller in this port's controller list. // Returns true if the controller was created. // To be called by gui thread only. bool createController(int chan, int ctrl); public: MidiPort(); ~MidiPort(); // Temporary, used during loading to resolve references to instruments. void setTmpFileRefs(int i, const QString& s) { _tmpTrackIdx = i; _tmpInstrRef = s; } void clearTmpFileRefs() { _tmpTrackIdx = -1; _tmpInstrRef = QString(); } int tmpTrackRef() const { return _tmpTrackIdx; } const QString& tmpInstrRef() const { return _tmpInstrRef; } // // manipulate active midi controller // MidiCtrlValListList* controller() { return _controller; } // Determine controller value at tick on channel, using values stored by ANY part. int getCtrl(int ch, unsigned int tick, int ctrl) const; // Determine controller value at tick on channel, using values stored by the SPECIFIC part. int getCtrl(int ch, unsigned int tick, int ctrl, Part* part) const; // Determine controller value at tick on channel, using values stored by ANY part, // ignoring values that are OUTSIDE of their parts, or muted or off parts or tracks. int getVisibleCtrl(int ch, unsigned int tick, int ctrl, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const; // Determine controller value at tick on channel, using values stored by the SPECIFIC part, // ignoring values that are OUTSIDE of the part, or muted or off part or track. int getVisibleCtrl(int ch, unsigned int tick, int ctrl, Part* part, bool inclMutedParts, bool inclMutedTracks, bool inclOffTracks) const; bool setControllerVal(int ch, unsigned int tick, int ctrl, int val, Part* part); // Can be CTRL_VAL_UNKNOWN until a valid state is set int lastValidHWCtrlState(int ch, int ctrl) const; double lastValidHWDCtrlState(int ch, int ctrl) const; int hwCtrlState(int ch, int ctrl) const; double hwDCtrlState(int ch, int ctrl) const; bool setHwCtrlState(int ch, int ctrl, int val); bool setHwCtrlState(int ch, int ctrl, double val); bool setHwCtrlStates(int ch, int ctrl, int val, int lastval); bool setHwCtrlStates(int ch, int ctrl, double val, double lastval); bool setHwCtrlState(const MidiPlayEvent&); void deleteController(int ch, unsigned int tick, int ctrl, int val, Part* part); void addDefaultControllers(); bool guiVisible() const; void showGui(bool); bool hasGui() const; bool nativeGuiVisible() const; void showNativeGui(bool); bool hasNativeGui() const; int portno() const; bool foundInSongFile() const { return _foundInSongFile; } void setFoundInSongFile(bool b) { _foundInSongFile = b; } MidiDevice* device() const { return _device; } const QString& state() const { return _state; } void setState(const QString& s) { _state = s; } // If instrument is given it will be set, otherwise it won't touch the existing instrument. void setMidiDevice(MidiDevice* dev, MidiInstrument* instrument = nullptr); const QString& portname() const; MidiInstrument* instrument() const { return _instrument; } void setInstrument(MidiInstrument* i) { _instrument = i; } void changeInstrument(MidiInstrument* i); MidiController* midiController(int num, int chan = -1, bool createIfNotFound = true) const; MidiCtrlValList* addManagedController(int channel, int ctrl); // To be called from realtime audio thread only. void tryCtrlInitVal(int chan, int ctl, int val); int limitValToInstrCtlRange(int ctl, int val, int chan = -1); double limitValToInstrCtlRange(int ctl, double val, int chan = -1); int limitValToInstrCtlRange(const MidiController* mc, int val); double limitValToInstrCtlRange(const MidiController* mc, double val); MidiController* drumController(int ctl); // Update drum maps when patch is known. // If audio is running (and not idle) this should only be called by the rt audio thread. // Returns true if maps were changed. bool updateDrumMaps(int chan, int patch); // Update drum maps when patch is not known. // If audio is running (and not idle) this should only be called by the rt audio thread. // Returns true if maps were changed. bool updateDrumMaps(); int defaultInChannels() const { return _defaultInChannels; } int defaultOutChannels() const { return _defaultOutChannels; } void setDefaultInChannels(int c) { _defaultInChannels = c; } void setDefaultOutChannels(int c) { _defaultOutChannels = c; } RouteList* inRoutes() { return &_inRoutes; } RouteList* outRoutes() { return &_outRoutes; } bool noInRoute() const { return _inRoutes.empty(); } bool noOutRoute() const { return _outRoutes.empty(); } void writeRouting(int, Xml&) const; // send events to midi device and keep track of // device state: void sendGmOn(); void sendGsOn(); void sendXgOn(); void sendGmInitValues(); void sendGsInitValues(); void sendXgInitValues(); void sendStart(); void sendStop(); void sendContinue(); void sendSongpos(int); void sendClock(); void sendSysex(const unsigned char* p, int n); void sendMMCLocate(unsigned char ht, unsigned char m, unsigned char s, unsigned char f, unsigned char sf, int devid = -1); void sendMMCStop(int devid = -1); void sendMMCDeferredPlay(int devid = -1); // Send Instrument Init sequences and controller defaults etc. Return true if success. // To be called from realtime audio thread only. bool sendPendingInitializations(bool force = true); // Per port // Send initial controller values. Called by above method, and elsewhere. bool sendInitialControllers(unsigned start_time = 0); bool initSent() const { return _initializationsSent; } void clearInitSent() { _initializationsSent = false; } // Put an event into the gui2audio fifo for playback. Calls stageEvent(). // Called from gui thread only. Returns true if event cannot be delivered. bool putHwCtrlEvent(const MidiPlayEvent&); // Put an event into both the device and the gui2audio fifos for playback. Calls stageEvent(). // Called from gui thread only. Returns true if event cannot be delivered. bool putEvent(const MidiPlayEvent&); // Special method for incrementing a value: Handles getting the current hw value, // incrementing it (as dB if specified), and sending it and setting the current hw value. // Called from gui thread only. Returns true if event cannot be delivered. // NOTE: Caller should use the Audio::msgAudioWait() to wait for the current value // to change in the audio thread before calling again, especially rapidly. // This method looks at the current value, so the current value must be up to date. // It will not call Audio::msgAudioWait(), to allow caller to optimize multiple calls. // TODO: An increment method seems possible: Wait for gui2audio to increment, then send to driver, // which incurs up to one extra segment delay (if Jack midi). bool putControllerValue(int port, int chan, int ctlnum, double val, bool isDb); // To be called from audio thread only. Returns true on success. // If createAsNeeded is true, automatically send a message to the gui thread to // create items such as controllers, and cache the events sent to it and re-put // them after the controller has been created. bool handleGui2AudioEvent(const MidiPlayEvent&, bool createAsNeeded); bool sendHwCtrlState(const MidiPlayEvent&, bool forceSend = false ); AutomationType automationType(int channel) { return _automationType[channel]; } void setAutomationType(int channel, AutomationType t) { _automationType[channel] = t; } MidiSyncInfo& syncInfo() { return _syncInfo; } }; extern void initMidiPorts(); // p4.0.17 Turn off if and when multiple output routes supported. #if 1 extern void setPortExclusiveDefOutChan(int /*port*/, int /*chan*/); #endif extern MidiControllerList defaultManagedMidiController; } // namespace MusECore namespace MusEGlobal { extern MusECore::MidiPort midiPorts[MusECore::MIDI_PORTS]; } #endif muse-4.0.0/src/muse/midiseq.cpp000066400000000000000000000460021404105311000163520ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midiseq.cpp,v 1.30.2.21 2009/12/20 05:00:35 terminator356 Exp $ // // high priority task for scheduling midi events // // (C) Copyright 2003 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #ifndef _WIN32 #include #include #endif #include "muse_math.h" #include #include "config.h" #include "app.h" #include "globals.h" #ifdef _WIN32 #include "driver/qttimer.h" #else #include "driver/alsatimer.h" #include "driver/rtctimer.h" #include "driver/posixtimer.h" #endif #include "midi_consts.h" #include "midiseq.h" #include "midiport.h" #include "mididev.h" #include "audio.h" #include "audiodev.h" #include "driver/alsamidi.h" #include "sync.h" #include "song.h" #include "gconfig.h" #include "warn_bad_timing.h" #include "large_int.h" namespace MusEGlobal { MusECore::MidiSeq* midiSeq = NULL; } namespace MusECore { int MidiSeq::ticker = 0; void initMidiSequencer() { if(!MusEGlobal::midiSeq) MusEGlobal::midiSeq = new MidiSeq("Midi"); } void exitMidiSequencer() { // Sequencer should be stopped before calling exitMidiSequencer(). Todo: maybe check that here. if(MusEGlobal::midiSeq) { //MusEGlobal::midiSeqRunning = false; // Done in stop. delete MusEGlobal::midiSeq; MusEGlobal::midiSeq = NULL; } } //--------------------------------------------------------- // readMsg //--------------------------------------------------------- static void readMsg(void* p, void*) { MidiSeq* at = (MidiSeq*)p; at->readMsg(); } //--------------------------------------------------------- // processMsg //--------------------------------------------------------- void MidiSeq::processMsg(const ThreadMsg* m) { MusECore::AudioMsg* msg = (MusECore::AudioMsg*)m; switch(msg->id) { case MusECore::SEQM_SEEK: processSeek(); break; case MusECore::MS_STOP: processStop(); break; case MusECore::MS_SET_RTC: MusEGlobal::doSetuid(); setRtcTicks(); MusEGlobal::undoSetuid(); break; case MusECore::MS_UPDATE_POLL_FD: updatePollFd(); break; case MusECore::SEQM_IDLE: idle = msg->a; break; default: fprintf(stderr, "MidiSeq::processMsg() unknown id %d\n", msg->id); break; } } //--------------------------------------------------------- // processStop //--------------------------------------------------------- void MidiSeq::processStop() { // Clear Alsa midi device notes and stop stuck notes. for(iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { MidiDevice* md = *id; const MidiDevice::MidiDeviceType type = md->deviceType(); // Only for ALSA devices. switch(type) { case MidiDevice::ALSA_MIDI: md->handleStop(); break; case MidiDevice::JACK_MIDI: case MidiDevice::SYNTH_MIDI: break; } } } //--------------------------------------------------------- // processSeek //--------------------------------------------------------- void MidiSeq::processSeek() { //--------------------------------------------------- // Set all controllers //--------------------------------------------------- for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { MidiDevice* md = *i; const MidiDevice::MidiDeviceType type = md->deviceType(); // Only for ALSA devices. switch(type) { case MidiDevice::ALSA_MIDI: md->handleSeek(); break; case MidiDevice::JACK_MIDI: case MidiDevice::SYNTH_MIDI: break; } } } //--------------------------------------------------------- // MidiSeq //--------------------------------------------------------- MidiSeq::MidiSeq(const char* name) : Thread(name) { prio = 0; idle = false; MusEGlobal::doSetuid(); timerFd=selectTimer(); MusEGlobal::undoSetuid(); } //--------------------------------------------------------- // ~MidiSeq //--------------------------------------------------------- MidiSeq::~MidiSeq() { if(timer) delete timer; } //--------------------------------------------------------- // selectTimer() // select one of the supported timers to use during this run //--------------------------------------------------------- signed int MidiSeq::selectTimer() { #ifdef _WIN32 fprintf(stderr, "Trying Qt timer...\n"); timer = new QtTimer(); int qt_tmrFd = timer->initTimer(MusEGlobal::config.rtcTicks); if (qt_tmrFd != -1) { // ok! fprintf(stderr, "got timer = %d\n", qt_tmrFd); return qt_tmrFd; } delete timer; #else #ifdef ALSA_SUPPORT fprintf(stderr, "Trying RTC timer...\n"); timer = new RtcTimer(); int rtc_tmrFd = timer->initTimer(MusEGlobal::config.rtcTicks); if (rtc_tmrFd != -1) { // ok! fprintf(stderr, "got timer = %d\n", rtc_tmrFd); return rtc_tmrFd; } delete timer; #endif #endif #ifdef POSIX_TIMER_SUPPORT fprintf(stderr, "Trying POSIX timer...\n"); timer = new PosixTimer(); int posix_tmrFd = timer->initTimer(MusEGlobal::config.rtcTicks); if (posix_tmrFd!= -1) { // ok! fprintf(stderr, "got timer = %d\n", posix_tmrFd); return posix_tmrFd; } delete timer; #endif #ifdef ALSA_SUPPORT fprintf(stderr, "Trying ALSA timer...\n"); timer = new AlsaTimer(); int alsa_tmrFd = timer->initTimer(MusEGlobal::config.rtcTicks); if (alsa_tmrFd!= -1) { // ok! fprintf(stderr, "got timer = %d\n", alsa_tmrFd); return alsa_tmrFd; } delete timer; #endif timer=NULL; QMessageBox::critical( 0, /*tr*/(QString("Failed to start timer!")), /*tr*/(QString("No functional timer was available.\n" "RTC timer not available, check if /dev/rtc is available and readable by current user\n" "Alsa timer not available, check if module snd_timer is available and /dev/snd/timer is available"))); fprintf(stderr, "No functional timer available!!!\n"); exit(1); } //--------------------------------------------------------- // deleteTimer() // Destroy timer if valid. //--------------------------------------------------------- bool MidiSeq::deleteTimer() { if(timer) { delete timer; timer = NULL; return true; } return false; } //--------------------------------------------------------- // threadStart // called from loop() //--------------------------------------------------------- void MidiSeq::threadStart(void*) { int policy; if ((policy = sched_getscheduler (0)) < 0) { printf("Cannot get current client scheduler: %s\n", strerror(errno)); } if (policy != SCHED_FIFO) printf("midi thread %d _NOT_ running SCHED_FIFO\n", getpid()); updatePollFd(); } //--------------------------------------------------------- // alsaMidiRead //--------------------------------------------------------- static void alsaMidiRead(void*, void*) { // calls itself midiDevice->recordEvent(MidiRecordEvent): alsaProcessMidiInput(); } //--------------------------------------------------------- // midiRead //--------------------------------------------------------- static void midiRead(void*, void* d) { MidiDevice* dev = (MidiDevice*) d; dev->processInput(); } //--------------------------------------------------------- // midiWrite //--------------------------------------------------------- static void midiWrite(void*, void* d) { MidiDevice* dev = (MidiDevice*) d; dev->flush(); } void MidiSeq::addAlsaPollFd() { // special handling for alsa midi: // (one fd for all devices) // this allows for processing of some alsa events // even if no alsa driver is active (assigned to a port) addPollFd(alsaSelectRfd(), POLLIN, MusECore::alsaMidiRead, this, 0); } void MidiSeq::removeAlsaPollFd() { removePollFd(alsaSelectRfd(), POLLIN); } //--------------------------------------------------------- // updatePollFd //--------------------------------------------------------- void MidiSeq::updatePollFd() { if (!isRunning()) return; clearPollFd(); addPollFd(timerFd, POLLIN, midiTick, this, 0); if (timerFd == -1) { fprintf(stderr, "updatePollFd: no timer fd\n"); if (!MusEGlobal::debugMode) exit(-1); } addPollFd(toThreadFdr, POLLIN, MusECore::readMsg, this, 0); //--------------------------------------------------- // midi ports //--------------------------------------------------- for (iMidiDevice imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) { MidiDevice* dev = *imd; int port = dev->midiPort(); if (port == -1) continue; if ((dev->rwFlags() & 0x2) || (MusEGlobal::extSyncFlag && (MusEGlobal::midiPorts[port].syncInfo().MCIn()))) addPollFd(dev->selectRfd(), POLLIN, MusECore::midiRead, this, dev); if (dev->bytesToWrite()) addPollFd(dev->selectWfd(), POLLOUT, MusECore::midiWrite, this, dev); } // special handling for alsa midi: // (one fd for all devices) // this allows for processing of some alsa events // even if no alsa driver is active (assigned to a port) addAlsaPollFd(); } //--------------------------------------------------------- // threadStop // called from loop() //--------------------------------------------------------- void MidiSeq::threadStop() { timer->stopTimer(); } //--------------------------------------------------------- // setRtcTicks // returns actual tick frequency //--------------------------------------------------------- int MidiSeq::setRtcTicks() { int gotTicks = timer->setTimerFreq(MusEGlobal::config.rtcTicks); if(gotTicks == 0) return 0; if (MusEGlobal::config.rtcTicks-24 > gotTicks) { fprintf(stderr, "INFO: Could not get the wanted frequency %d, got %d, still it should suffice.\n", MusEGlobal::config.rtcTicks, gotTicks); } else fprintf(stderr, "INFO: Requested timer frequency:%d actual:%d\n", MusEGlobal::config.rtcTicks, gotTicks); timer->startTimer(); return gotTicks; } //--------------------------------------------------------- // start // return true on error //--------------------------------------------------------- void MidiSeq::start(int /*priority*/, void*) { // Already running? if(isRunning()) return; if(!MusEGlobal::audioDevice) { fprintf(stderr, "MusE::seqStartMidi: audioDevice is NULL\n"); return; } if(!MusEGlobal::audio->isRunning()) { fprintf(stderr, "MusE::seqStartMidi: audio is not running\n"); return; } int midiprio = 0; // NOTE: MusEGlobal::realTimeScheduling can be true (gotten using jack_is_realtime()), // while the determined MusEGlobal::realTimePriority can be 0. // MusEGlobal::realTimePriority is gotten using pthread_getschedparam() on the client thread // in JackAudioDevice::realtimePriority() which is a bit flawed - it reports there's no RT... if(MusEGlobal::realTimeScheduling) { if(MusEGlobal::realTimePriority - 1 >= 0) midiprio = MusEGlobal::realTimePriority - 1; } if(MusEGlobal::midiRTPrioOverride > 0) midiprio = MusEGlobal::midiRTPrioOverride; // FIXME: The MusEGlobal::realTimePriority of the Jack thread seems to always be 5 less than the value passed to jackd command. prio = midiprio; MusEGlobal::doSetuid(); int freq = setRtcTicks(); MusEGlobal::undoSetuid(); if(freq == 0) { fprintf(stderr, "Error setting timer frequency! Midi playback will not work!\n"); } Thread::start(prio); int counter=0; while (++counter) { if (counter > 1000) { fprintf(stderr,"midi sequencer thread does not start!? Exiting...\n"); // exit(33); break; } MusEGlobal::midiSeqRunning = MusEGlobal::midiSeq->isRunning(); if (MusEGlobal::midiSeqRunning) break; usleep(1000); if(MusEGlobal::debugMsg) printf("looping waiting for sequencer thread to start\n"); } if(!MusEGlobal::midiSeqRunning) { // fprintf(stderr, "midiSeq is not running! Exiting...\n"); // exit(33); fprintf(stderr, "midiSeq is still not running!\n"); } } //--------------------------------------------------------- // checkAndReportTimingResolution //--------------------------------------------------------- void MidiSeq::checkAndReportTimingResolution() { int freq = timer->getTimerFreq(); fprintf(stderr, "Acquired timer frequency: %d\n", freq); if (freq < 500) { if(MusEGlobal::config.warnIfBadTiming) { MusEGui::WarnBadTimingDialog dlg; dlg.setLabelText(qApp->translate("@default", QT_TRANSLATE_NOOP("@default", "Timing source frequency is %1hz, which is below the recommended minimum: 500hz!\n" "This could lead to audible timing problems for MIDI.\n" "Please see the FAQ on http://muse-sequencer.org for remedies.\n" "Also please check console output for any further error messages.\n ")).arg(freq) ); dlg.exec(); bool warn = !dlg.dontAsk(); if(warn != MusEGlobal::config.warnIfBadTiming) { MusEGlobal::config.warnIfBadTiming = warn; // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. //MusEGlobal::muse->changeConfig(true); // Save settings? No, wait till close. } } } } //--------------------------------------------------------- // midiTick //--------------------------------------------------------- void MidiSeq::midiTick(void* p, void*) { MidiSeq* at = (MidiSeq*)p; at->processTimerTick(); if (TIMER_DEBUG) { if(MidiSeq::ticker++ > 100) { fprintf(stderr, "ticker:%i\n", MidiSeq::ticker); MidiSeq::ticker=0; } } } //--------------------------------------------------------- // processTimerTick //--------------------------------------------------------- void MidiSeq::processTimerTick() { //--------------------------------------------------- // read elapsed rtc timer ticks //--------------------------------------------------- // This is required otherwise it freezes. unsigned long nn; if (timerFd != -1) { nn = timer->getTimerTicks(); nn >>= 8; } if (idle) return; unsigned curFrame = MusEGlobal::audio->curFrame(); // REMOVE Tim. clock. Removed. Done with scheduling now in audio thread process. if (!MusEGlobal::extSyncFlag) { // Do not round up here since (audio) frame resolution is higher than tick resolution. const unsigned int curTick = muse_multiply_64_div_64_to_64( (uint64_t)MusEGlobal::config.division * (uint64_t)MusEGlobal::tempomap.globalTempo() * 10000UL, curFrame, (uint64_t)MusEGlobal::sampleRate * (uint64_t)MusEGlobal::tempomap.tempo(MusEGlobal::song->cpos())); unsigned int mclock = MusEGlobal::midiSyncContainer.midiClock(); if(mclock > curTick) { mclock = curTick; MusEGlobal::midiSyncContainer.setMidiClock(mclock); } const unsigned int div = MusEGlobal::config.division/24; if(curTick >= mclock + div) { const unsigned int perr = (curTick - mclock) / div; bool used = false; for(int port = 0; port < MusECore::MIDI_PORTS; ++port) { MidiPort* mp = &MusEGlobal::midiPorts[port]; // No device? Clock out not turned on? if(!mp->device() || !mp->syncInfo().MCOut()) continue; used = true; mp->sendClock(); } if(MusEGlobal::debugMsg && used && perr > 1) printf("Dropped %u midi out clock(s). curTick:%u midiClock:%u div:%u\n", perr, curTick, MusEGlobal::midiSyncContainer.midiClock(), div); // Using equalization periods... MusEGlobal::midiSyncContainer.setMidiClock(mclock + (perr * div)); } } // Play all events up to curFrame. for (iMidiDevice id = MusEGlobal::midiDevices.begin(); id != MusEGlobal::midiDevices.end(); ++id) { MidiDevice* md = *id; const MidiDevice::MidiDeviceType type = md->deviceType(); // Only for ALSA devices. switch(type) { case MidiDevice::ALSA_MIDI: md->processMidi(curFrame); break; case MidiDevice::JACK_MIDI: case MidiDevice::SYNTH_MIDI: break; } } } //--------------------------------------------------------- // msgMsg //--------------------------------------------------------- void MidiSeq::msgMsg(int id) { MusECore::AudioMsg msg; msg.id = id; Thread::sendMsg(&msg); } void MidiSeq::msgSeek() { msgMsg(MusECore::SEQM_SEEK); } void MidiSeq::msgStop() { msgMsg(MusECore::MS_STOP); } void MidiSeq::msgSetRtc() { msgMsg(MusECore::MS_SET_RTC); } void MidiSeq::msgUpdatePollFd() { msgMsg(MusECore::MS_UPDATE_POLL_FD); } } // namespace MusECore muse-4.0.0/src/muse/midiseq.h000066400000000000000000000050611404105311000160170ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midiseq.h,v 1.6.2.11 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDISEQ_H__ #define __MIDISEQ_H__ #include "thread.h" #include "timerdev.h" namespace MusECore { class MidiDevice; class MidiPort; class MPEventList; class MTC; class SynthI; //--------------------------------------------------------- // MidiSeq //--------------------------------------------------------- class MidiSeq : public Thread { int timerFd; int idle; int prio; // realtime priority static int ticker; Timer *timer; int setRtcTicks(); static void midiTick(void* p, void*); void processTimerTick(); void processSeek(); void processStop(); virtual void processMsg(const ThreadMsg*); void updatePollFd(); public: MidiSeq(const char* name); ~MidiSeq(); virtual void start(int, void* ptr=0); virtual void threadStop(); virtual void threadStart(void*); signed int selectTimer(); Timer* getTimer() { return timer; } // Destroy timer if valid. Returns true if successful. bool deleteTimer(); void addAlsaPollFd(); void removeAlsaPollFd(); bool isIdle() const { return idle; } void checkAndReportTimingResolution(); void msgMsg(int id); void msgSeek(); void msgStop(); void msgSetRtc(); void msgUpdatePollFd(); }; extern void initMidiSequencer(); extern void exitMidiSequencer(); } //namespace MusECore namespace MusEGlobal { extern MusECore::MidiSeq* midiSeq; } // namespace MusEGlobal #endif muse-4.0.0/src/muse/mixed_pos_list.h000066400000000000000000000122331404105311000174050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // mixed_pos_list.h // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef MIXED_POS_LIST_H #define MIXED_POS_LIST_H #include #include // #include #include "pos.h" namespace MusECore { //--------------------------------------------------------- // MixedPosList_t // A C++11 multimap template that can store any type of value // as long as it has three methods: type(), tick(), and frame(). // BOTH ticks and frame based values are allowed. // For example, both midi events and wave events can be stored. // They can be locked/unlocked by switching their time type // from/to ticks to/from frames. //--------------------------------------------------------- template, class Alloc = std::allocator > > class MixedPosList_t : public std::multimap { private: typedef std::multimap vlist; typedef typename std::list::const_iterator cil_t; protected: Pos::TType _type; public: typedef typename vlist::iterator iMixedPosList_t; typedef typename vlist::const_iterator ciMixedPosList_t; typedef std::pair cMixedPosListRange_t; typedef std::pair MixedPosListInsertPair_t; MixedPosList_t(Pos::TType type = Pos::TICKS) : vlist(), _type(type) {} virtual ~MixedPosList_t() {} inline Pos::TType type() const { return _type; } inline void setType(const Pos::TType& t) { _type = t; rebuild(); } // Returns an iterator that points to the inserted event. // Note that the pair's key part is IGNORED. // All information is gathered from the pair's value type T. // Returns end() if an error occurred. iMixedPosList_t insert (const MixedPosListInsertPair_t& v) { return add(v.second); } // TODO: // template iMixedPosList insert (P&& v) { return vlist::insert(v); } // iMixedPosList insert (ciMixedPosList pos, const T& v) { return vlist::insert(pos, v); } // template iMixedPosList insert (ciMixedPosList pos, P&& v) { return vlist::insert(pos, v); } // template // void insert (InputIterator first, InputIterator last) { return vlist::insert(first, last); } // void insert (std::initializer_list il) { return vlist::insert(il); } // Returns an iterator that points to the added event. // Returns end() if an error occurred. iMixedPosList_t add(const T& v) { const unsigned v_frame = v.frame(); const unsigned v_tick = v.tick(); ciMixedPosList_t pos = vlist::end(); cMixedPosListRange_t r; // If list type is ticks, compare frame. If list type is frames, compare tick. switch(type()) { case Pos::TICKS: r = vlist::equal_range(v_tick); for(pos = r.first; pos != r.second; ++pos) if(v_frame < pos->second.frame()) break; return vlist::insert(pos, MixedPosListInsertPair_t(v_tick, v)); break; case Pos::FRAMES: r = vlist::equal_range(v_frame); for(pos = r.first; pos != r.second; ++pos) if(v_tick < pos->second.tick()) break; return vlist::insert(pos, MixedPosListInsertPair_t(v_frame, v)); break; } return vlist::end(); } // After any tempo changes, it is essential to rebuild the list // so that any 'locked' items are re-sorted properly by tick. // Returns true if any items were rebuilt. bool rebuild() { std::list to_be_added; for(ciMixedPosList_t i = vlist::begin(); i != vlist::end(); ) { const T& m = i->second; if((type() == Pos::TICKS && m.type() == Pos::FRAMES) || (type() == Pos::FRAMES && m.type() == Pos::TICKS)) { to_be_added.push_back(m); i = vlist::erase(i); } else ++i; } for(cil_t ai = to_be_added.begin(); ai != to_be_added.end(); ++ai) add(*ai); return !to_be_added.empty(); } }; } // namespace MusECore #endif muse-4.0.0/src/muse/mixer/000077500000000000000000000000001404105311000153355ustar00rootroot00000000000000muse-4.0.0/src/muse/mixer/CMakeLists.txt000066400000000000000000000040021404105311000200710ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( mixer_mocs amixer.h astrip.h auxknob.h mstrip.h rack.h strip.h ) ## ## List of source files to compile ## file (GLOB mixer_source_files amixer.cpp astrip.cpp auxknob.cpp mstrip.cpp rack.cpp strip.cpp ) ## ## Define target ## add_library ( mixer ${MODULES_BUILD} ${mixer_source_files} ${mixer_mocs} ) add_dependencies(mixer components) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${mixer_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties ( mixer PROPERTIES OUTPUT_NAME muse_mixer ) ## ## Linkage ## target_link_libraries ( mixer ${QT_LIBRARIES} widgets ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS mixer DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/mixer/amixer.cpp000066400000000000000000001163731404105311000173410ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: amixer.cpp,v 1.49.2.5 2009/11/16 01:55:55 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include #include #include #include #include "amixer.h" #include "app.h" #include "helper.h" #include "icons.h" #include "song.h" #include "audio.h" #include "astrip.h" #include "mstrip.h" #include "shortcuts.h" #include "globals.h" #include "undo.h" // Forwards from header: #include #include #include #include #include #include #include #include #include #include "xml.h" #include "track.h" #include "meter.h" #include "combobox.h" #include "doublelabel.h" #include "knob.h" #include "slider.h" #include "strip.h" #include "components/routedialog.h" #define __WIDTH_COMPENSATION 4 // For debugging output: Uncomment the fprintf section. #define DEBUG_MIXER(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { ScrollArea::ScrollArea(QWidget* parent) : QScrollArea(parent) { } bool ScrollArea::viewportEvent(QEvent* event) { DEBUG_MIXER(stderr, "viewportEvent type %d\n", (int)event->type()); // event->ignore(); // Let it do the layout now, before we emit. const bool res = QScrollArea::viewportEvent(event); if(event->type() == QEvent::LayoutRequest) emit layoutRequest(); // return false; // return true; return res; } //--------------------------------------------------------- // AudioMixer // // inputs | synthis | tracks | groups | master //--------------------------------------------------------- AudioMixerApp::AudioMixerApp(QWidget* parent, MusEGlobal::MixerConfig* c) : QMainWindow(parent) { _resizeFlag = false; _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; cfg = c; oldAuxsSize = 0; routingDialog = nullptr; setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding)); setWindowTitle(cfg->name); setWindowIcon(*museIcon); // Due to issues with certain window managers, such as maximize IGNORING // our maximum size imposed on the window, maximizing is disabled. // Several attempts to allow maximizing were unsuccessful. // The window is full screen width but the scroll area is cut off at our maximum width. // Seems internally some layer IS respecting our maximum width imposed, cutting it off. // Tried end spacers, blank widgets autoFillBackground etc. No luck. // Might be possible but more work needed. Easier to do this: setWindowFlags(windowFlags() & ~Qt::WindowFlags(Qt::WindowMaximizeButtonHint)); //cfg->displayOrder = MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW; QMenu* menuConfig = menuBar()->addMenu(tr("&Create")); MusEGui::populateAddTrack(menuConfig,true); connect(menuConfig, &QMenu::triggered, [](QAction* a) { MusEGlobal::song->addNewTrack(a); } ); QMenu* menuView = menuBar()->addMenu(tr("&View")); menuStrips = menuView->addMenu(tr("Strips")); connect(menuStrips, &QMenu::aboutToShow, [this]() { stripsMenu(); } ); routingId = menuView->addAction(tr("Routing")); routingId->setCheckable(true); connect(routingId, &QAction::triggered, [this]() { toggleRouteDialog(); } ); menuView->addSeparator(); QActionGroup* actionItems = new QActionGroup(this); actionItems->setExclusive(false); showMidiTracksId = new QAction(tr("Show Midi Tracks"), actionItems); // showDrumTracksId = new QAction(tr("Show Drum Tracks"), actionItems); showNewDrumTracksId = new QAction(tr("Show Drum Tracks"), actionItems); showWaveTracksId = new QAction(tr("Show Wave Tracks"), actionItems); QAction *separator = new QAction(this); separator->setSeparator(true); actionItems->addAction(separator); showInputTracksId = new QAction(tr("Show Inputs"), actionItems); showOutputTracksId = new QAction(tr("Show Outputs"), actionItems); showGroupTracksId = new QAction(tr("Show Groups"), actionItems); showAuxTracksId = new QAction(tr("Show Auxs"), actionItems); showSyntiTracksId = new QAction(tr("Show Synthesizers"), actionItems); // showDrumTracksId->setVisible(false); showMidiTracksId->setCheckable(true); // showDrumTracksId->setCheckable(true); showNewDrumTracksId->setCheckable(true); showWaveTracksId->setCheckable(true); showInputTracksId->setCheckable(true); showOutputTracksId->setCheckable(true); showGroupTracksId->setCheckable(true); showAuxTracksId->setCheckable(true); showSyntiTracksId->setCheckable(true); connect(showMidiTracksId, &QAction::triggered, [this](bool v) { showMidiTracksChanged(v); } ); // connect(showDrumTracksId, &QAction::triggered, [this](bool v) { showDrumTracksChanged(v); } ); connect(showNewDrumTracksId, &QAction::triggered, [this](bool v) { showNewDrumTracksChanged(v); } ); connect(showWaveTracksId, &QAction::triggered, [this](bool v) { showWaveTracksChanged(v); } ); connect(showInputTracksId, &QAction::triggered, [this](bool v) { showInputTracksChanged(v); } ); connect(showOutputTracksId, &QAction::triggered, [this](bool v) { showOutputTracksChanged(v); } ); connect(showGroupTracksId, &QAction::triggered, [this](bool v) { showGroupTracksChanged(v); } ); connect(showAuxTracksId, &QAction::triggered, [this](bool v) { showAuxTracksChanged(v); } ); connect(showSyntiTracksId, &QAction::triggered, [this](bool v) { showSyntiTracksChanged(v); } ); menuView->addActions(actionItems->actions()); ///view = new QScrollArea(); view = new ScrollArea(); view->setFocusPolicy(Qt::NoFocus); view->setContentsMargins(0, 0, 0, 0); //view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setCentralWidget(view); central = new QWidget(view); central->setFocusPolicy(Qt::NoFocus); central->setContentsMargins(0, 0, 0, 0); //splitter = new QSplitter(view); mixerLayout = new QHBoxLayout(); central->setLayout(mixerLayout); mixerLayout->setSpacing(0); mixerLayout->setContentsMargins(0, 0, 0, 0); view->setWidget(central); //view->setWidget(splitter); view->setWidgetResizable(true); // Ensure the rightmost item is always a spacer. // Although this does not have any effect because we disabled the maximize button, just in case // the maximize button is ever re-added and/or the maximum width imposed on the mixer window // is ever removed, keep this - it will be required. No harm so far in leaving it in. QSpacerItem* right_spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Expanding); mixerLayout->addSpacerItem(right_spacer); // FIXME: Neither of these two replacement functor version work. What's wrong here? connect(view, SIGNAL(layoutRequest()), SLOT(setSizing())); //connect(view, &ScrollArea::layoutRequest, [this]() { setSizing(); } ); //connect(view, QOverload<>::of(&ScrollArea::layoutRequest), [=]() { setSizing(); } ); connect(MusEGlobal::song, &MusECore::Song::songChanged, [this](MusECore::SongChangedStruct_t f) { songChanged(f); } ); connect(MusEGlobal::muse, &MusEGui::MusE::configChanged, [this]() { configChanged(); } ); initMixer(); updateStripList(); updateSelectedStrips(); redrawMixer(); central->installEventFilter(this); mixerLayout->installEventFilter(this); view ->installEventFilter(this); } void AudioMixerApp::stripsMenu() { menuStrips->clear(); connect(menuStrips, &QMenu::triggered, [this](QAction* a) { handleMenu(a); } ); QAction *act; act = menuStrips->addAction(tr("Traditional order")); act->setData(MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW); act->setCheckable(true); if (cfg->displayOrder == MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW) act->setChecked(true); act = menuStrips->addAction(tr("Arranger order")); act->setData(MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW); act->setCheckable(true); if (cfg->displayOrder == MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW) act->setChecked(true); act = menuStrips->addAction(tr("User order")); act->setData(MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW); act->setCheckable(true); if (cfg->displayOrder == MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW) act->setChecked(true); menuStrips->addSeparator(); act = menuStrips->addAction(tr("Show all hidden strips")); act->setData(UNHIDE_STRIPS); menuStrips->addSeparator(); // loop through all tracks and show the hidden ones int i=0,h=0; foreach (Strip *s, stripList) { if (!s->getStripVisible()){ act = menuStrips->addAction(tr("Unhide strip: ") + s->getTrack()->name()); act->setData(i); h++; } i++; } if (h==0) { act = menuStrips->addAction(tr("(no hidden strips)")); act->setData(UNHANDLED_NUMBER); } } void AudioMixerApp::handleMenu(QAction *act) { DEBUG_MIXER(stderr, "handleMenu %d\n", act->data().toInt()); int operation = act->data().toInt(); if (operation >= 0) { Strip* s = stripList.at(act->data().toInt()); s->setStripVisible(true); stripVisibleChanged(s, true); } else if (operation == UNHIDE_STRIPS) { foreach (Strip *s, stripList) { s->setStripVisible(true); stripVisibleChanged(s, true); } } else if (operation == MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW) { cfg->displayOrder = MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW; } else if (operation == MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW) { cfg->displayOrder = MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW; } else if (operation == MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW) { cfg->displayOrder = MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW; } redrawMixer(); } bool AudioMixerApp::stripIsVisible(Strip* s) { if (!s->getStripVisible()) return false; MusECore::Track *t = s->getTrack(); switch (t->type()) { case MusECore::Track::AUDIO_SOFTSYNTH: if (!cfg->showSyntiTracks) return false; break; case MusECore::Track::AUDIO_INPUT: if (!cfg->showInputTracks) return false; break; case MusECore::Track::AUDIO_OUTPUT: if (!cfg->showOutputTracks) return false; break; case MusECore::Track::AUDIO_AUX: if (!cfg->showAuxTracks) return false; break; case MusECore::Track::AUDIO_GROUP: if (!cfg->showGroupTracks) return false; break; case MusECore::Track::WAVE: if (!cfg->showWaveTracks) return false; break; case MusECore::Track::MIDI: case MusECore::Track::DRUM: if (!cfg->showMidiTracks) return false; break; } return true; } void AudioMixerApp::redrawMixer() { DEBUG_MIXER(stderr, "redrawMixer type %d, mixerLayout count %d\n", cfg->displayOrder, mixerLayout->count()); // empty layout const int lc = mixerLayout->count(); if(lc > 0) { // Count down from second last item. Last item should be a spacer or blank widget. for (int i = lc - 2; i >= 0; --i) { // Remove only widgets from the layout, not spacers/stretchers etc. QLayoutItem* li = mixerLayout->itemAt(i); if(!li) continue; QWidget* w = li->widget(); if(!w) continue; mixerLayout->takeAt(i); } } switch (cfg->displayOrder) { case MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW: { DEBUG_MIXER(stderr, "Draw strips with arranger view\n"); MusECore::TrackList *tl = MusEGlobal::song->tracks(); MusECore::TrackList::iterator tli = tl->begin(); for (; tli != tl->end(); tli++) { DEBUG_MIXER(stderr, "Adding strip %s\n", (*tli)->name().toLatin1().data()); StripList::iterator si = stripList.begin(); for (; si != stripList.end(); si++) { if((*si)->getTrack() == *tli) { addStripToLayoutIfVisible(*si); } } } } break; case MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW: { DEBUG_MIXER(stderr, "Draw strips with edited view\n"); // add them back in the selected order StripList::iterator si = stripList.begin(); for (; si != stripList.end(); ++si) { DEBUG_MIXER(stderr, "Adding strip %s\n", (*si)->getTrack()->name().toLatin1().data()); addStripToLayoutIfVisible(*si); } DEBUG_MIXER(stderr, "mixerLayout count is now %d\n", mixerLayout->count()); } break; case MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW: { DEBUG_MIXER(stderr, "TRADITIONAL VIEW mixerLayout count is now %d\n", mixerLayout->count()); addStripsTraditionalLayout(); } break; } // Setup all tabbing for each strip, and between strips. setupComponentTabbing(); update(); } Strip* AudioMixerApp::findStripForTrack(StripList &sl, MusECore::Track *t) { StripList::iterator si = sl.begin(); for (;si != sl.end(); si++) { if ((*si)->getTrack() == t) return *si; } DEBUG_MIXER(stderr, "AudioMixerApp::findStripForTrack - ERROR: there was no strip for this track!\n"); return nullptr; } void AudioMixerApp::fillStripListTraditional() { StripList oldList = stripList; stripList.clear(); MusECore::TrackList *tl = MusEGlobal::song->tracks(); // add Input Strips MusECore::TrackList::iterator tli = tl->begin(); for (; tli != tl->end(); ++tli) { if ((*tli)->type() == MusECore::Track::AUDIO_INPUT) stripList.append(findStripForTrack(oldList,*tli)); } // Synthesizer Strips tli = tl->begin(); for (; tli != tl->end(); ++tli) { if ((*tli)->type() == MusECore::Track::AUDIO_SOFTSYNTH) stripList.append(findStripForTrack(oldList,*tli)); } // generate Wave Track Strips tli = tl->begin(); for (; tli != tl->end(); ++tli) { if ((*tli)->type() == MusECore::Track::WAVE) stripList.append(findStripForTrack(oldList,*tli)); } // generate Midi channel/port Strips tli = tl->begin(); for (; tli != tl->end(); ++tli) { if ((*tli)->type() == MusECore::Track::MIDI || (*tli)->type() == MusECore::Track::DRUM) stripList.append(findStripForTrack(oldList,*tli)); } // Groups tli = tl->begin(); for (; tli != tl->end(); ++tli) { if ((*tli)->type() == MusECore::Track::AUDIO_GROUP) stripList.append(findStripForTrack(oldList,*tli)); } // Aux tli = tl->begin(); for (; tli != tl->end(); ++tli) { if ((*tli)->type() == MusECore::Track::AUDIO_AUX) stripList.append(findStripForTrack(oldList,*tli)); } // Master tli = tl->begin(); for (; tli != tl->end(); ++tli) { if ((*tli)->type() == MusECore::Track::AUDIO_OUTPUT) stripList.append(findStripForTrack(oldList,*tli)); } } void AudioMixerApp::moveStrip(Strip *s) { DEBUG_MIXER(stderr, "Recreate stripList\n"); if (cfg->displayOrder == MusEGlobal::MixerConfig::STRIPS_ARRANGER_VIEW) { const int tracks_sz = MusEGlobal::song->tracks()->size(); for (int i=0; i< stripList.size(); i++) { Strip *s2 = stripList.at(i); if (s2 == s) continue; DEBUG_MIXER(stderr, "loop loop %d %d width %d\n", s->pos().x(),s2->pos().x(), s2->width()); if (s->pos().x()+s->width()/2 < s2->pos().x()+s2->width() // upper limit && s->pos().x()+s->width()/2 > s2->pos().x() ) // lower limit { // found relevant pos. int sTrack = MusEGlobal::song->tracks()->index(s->getTrack()); int dTrack = MusEGlobal::song->tracks()->index(s2->getTrack()); if (sTrack >= 0 && dTrack >= 0) // sanity check { if (sTrack < tracks_sz && dTrack < tracks_sz) // sanity check MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::MoveTrack, sTrack, dTrack)); } } } } else if (cfg->displayOrder == MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW) { fillStripListTraditional(); cfg->displayOrder = MusEGlobal::MixerConfig::STRIPS_EDITED_VIEW; } DEBUG_MIXER(stderr, "moveStrip %s! stripList.size = %d\n", s->getLabelText().toLatin1().data(), stripList.size()); for (int i=0; i< stripList.size(); i++) { Strip *s2 = stripList.at(i); // Ignore the given strip, or invisible strips. if(s2 == s || !s2->getStripVisible()) continue; DEBUG_MIXER(stderr, "loop loop %d %d width %d\n", s->pos().x(),s2->pos().x(), s2->width()); if (s->pos().x()+s->width()/2 < s2->pos().x()+s2->width() // upper limit && s->pos().x()+s->width()/2 > s2->pos().x() ) // lower limit { DEBUG_MIXER(stderr, "got new pos: %d\n", i); #if DEBUG_MIXER bool isSuccess = stripList.removeOne(s); DEBUG_MIXER(stderr, "Removed strip %d", isSuccess); #else stripList.removeOne(s); #endif stripList.insert(i,s); DEBUG_MIXER(stderr, "Inserted strip at %d", i); // Move the corresponding config list item. moveConfig(s, i); break; } } redrawMixer(); update(); } void AudioMixerApp::addStripToLayoutIfVisible(Strip *s) { if (stripIsVisible(s)) { s->setVisible(true); stripVisibleChanged(s, true); // Ensure that if a right spacer or blank widget item exits, we insert // at that position, otherwise just append. const int lc = mixerLayout->count(); if(lc == 0) { mixerLayout->addWidget(s/*, 0, Qt::AlignLeft*/); } else { mixerLayout->insertWidget(lc - 1, s/*, 0, Qt::AlignLeft*/); } } else { s->setVisible(false); stripVisibleChanged(s, false); } } void AudioMixerApp::addStripsTraditionalLayout() { // generate Input Strips StripList::iterator si = stripList.begin(); for (; si != stripList.end(); ++si) { if ((*si)->getTrack()->type() == MusECore::Track::AUDIO_INPUT) addStripToLayoutIfVisible(*si); } // Synthesizer Strips si = stripList.begin(); for (; si != stripList.end(); ++si) { if ((*si)->getTrack()->type() == MusECore::Track::AUDIO_SOFTSYNTH) addStripToLayoutIfVisible(*si); } // generate Wave Track Strips si = stripList.begin(); for (; si != stripList.end(); ++si) { if ((*si)->getTrack()->type() == MusECore::Track::WAVE) addStripToLayoutIfVisible(*si); } // generate Midi channel/port Strips si = stripList.begin(); for (; si != stripList.end(); ++si) { if ((*si)->getTrack()->type() == MusECore::Track::MIDI || (*si)->getTrack()->type() == MusECore::Track::DRUM) addStripToLayoutIfVisible(*si); } // Groups si = stripList.begin(); for (; si != stripList.end(); ++si) { if ((*si)->getTrack()->type() == MusECore::Track::AUDIO_GROUP) addStripToLayoutIfVisible(*si); } // Aux si = stripList.begin(); for (; si != stripList.end(); ++si) { if ((*si)->getTrack()->type() == MusECore::Track::AUDIO_AUX) addStripToLayoutIfVisible(*si); } // Master si = stripList.begin(); for (; si != stripList.end(); ++si) { if ((*si)->getTrack()->type() == MusECore::Track::AUDIO_OUTPUT) addStripToLayoutIfVisible(*si); } } void AudioMixerApp::stripVisibleChanged(Strip* s, bool v) { const MusECore::Track* t = s->getTrack(); const int sn = t->serial(); if (!cfg->stripConfigList.empty()) { const int sz = cfg->stripConfigList.size(); for (int i=0; i < sz; i++) { MusEGlobal::StripConfig& sc = cfg->stripConfigList[i]; DEBUG_MIXER(stderr, "stripVisibleChanged() processing strip [%d][%d]\n", sc._serial, sc._visible); if(!sc.isNull() && sc._serial == sn) { sc._visible = v; return; } } } fprintf(stderr, "stripVisibleChanged() StripConfig not found [%d]\n", sn); } void AudioMixerApp::stripUserWidthChanged(Strip* s, int w) { const MusECore::Track* t = s->getTrack(); const int sn = t->serial(); if (!cfg->stripConfigList.empty()) { const int sz = cfg->stripConfigList.size(); for (int i=0; i < sz; i++) { MusEGlobal::StripConfig& sc = cfg->stripConfigList[i]; DEBUG_MIXER(stderr, "stripUserWidthChanged() processing strip [%d][%d]\n", sc._serial, sc._width); if(!sc.isNull() && sc._serial == sn) { sc._width = w; return; } } } fprintf(stderr, "stripUserWidthChanged() StripConfig not found [%d]\n", sn); } void AudioMixerApp::setSizing() { DEBUG_MIXER(stderr, "setSizing\n"); int w = 0; w = mixerLayout->minimumSize().width(); if(const QStyle* st = style()) { st = st->proxy(); w += 2 * st->pixelMetric(QStyle::PM_DefaultFrameWidth); } if(w < 40) w = 40; view->setUpdatesEnabled(false); setUpdatesEnabled(false); if(stripList.size() <= 6) setMinimumWidth(w); // Hack flag to prevent overwriting the config geometry when resizing... _resizeFlag = true; // At this point the width may be UNEXPANDABLE and may be small ! // This line forces the maximum width, to at least ALLOW expansion ... setMaximumWidth(w); // ... and now, this line restores the desired size. if(size() != cfg->geometry.size()) resize(cfg->geometry.size()); // Now reset the flag in case it wasn't already by the resize handler... _resizeFlag = false; setUpdatesEnabled(true); view->setUpdatesEnabled(true); } //--------------------------------------------------------- // addStrip //--------------------------------------------------------- void AudioMixerApp::addStrip(const MusECore::Track* t, const MusEGlobal::StripConfig& sc, int insert_pos) { DEBUG_MIXER(stderr, "addStrip\n"); Strip* strip; // Make them non-embedded: Moveable, hideable, and with an expander handle. if (t->isMidiTrack()) strip = new MidiStrip(central, (MusECore::MidiTrack*)t, true, false); else strip = new AudioStrip(central, (MusECore::AudioTrack*)t, true, false); // Broadcast changes to other selected tracks. strip->setBroadcastChanges(true); // Set focus yielding to the mixer window. if(MusEGlobal::config.smartFocus) { strip->setFocusYieldWidget(this); } connect(strip, &Strip::clearStripSelection, [this]() { clearStripSelection(); } ); connect(strip, &Strip::moveStrip, [this](Strip* s) { moveStrip(s); } ); connect(strip, &Strip::visibleChanged, [this](Strip* s, bool v) { stripVisibleChanged(s, v); } ); connect(strip, &Strip::userWidthChanged, [this](Strip* s, int w) { stripUserWidthChanged(s, w); } ); // No insert position given? if(insert_pos == -1) { DEBUG_MIXER(stderr, "putting new strip [%s] at end\n", t->name().toLatin1().data()); stripList.append(strip); } else { DEBUG_MIXER(stderr, "inserting new strip [%s] at %d\n", t->name().toLatin1().data(), insert_pos); stripList.insert(insert_pos, strip); } strip->setVisible(sc._visible); strip->setStripVisible(sc._visible); if(sc._width >= 0) strip->setUserWidth(sc._width); // Create a strip config now if no strip config exists yet for this strip (sc is default, with invalid sn). if(sc.isNull()) { const MusEGlobal::StripConfig sc(t->serial(), strip->getStripVisible(), strip->userWidth()); cfg->stripConfigList.append(sc); } } //--------------------------------------------------------- // clearAndDelete //--------------------------------------------------------- void AudioMixerApp::clearAndDelete() { DEBUG_MIXER(stderr, "clearAndDelete\n"); // Remove and delete only strip widgets from the layout, not spacers/stretchers etc. StripList::iterator si = stripList.begin(); for (; si != stripList.end(); ++si) { mixerLayout->removeWidget(*si); //(*si)->deleteLater(); delete (*si); } cfg->stripConfigList.clear(); stripList.clear(); // Obsolete. Support old files. cfg->stripOrder.clear(); oldAuxsSize = -1; } //--------------------------------------------------------- // initMixer //--------------------------------------------------------- void AudioMixerApp::initMixer() { DEBUG_MIXER(stderr, "initMixer %d\n", cfg->stripOrder.empty() ? cfg->stripConfigList.size() : cfg->stripOrder.size()); setWindowTitle(cfg->name); //clearAndDelete(); showMidiTracksId->setChecked(cfg->showMidiTracks); // showDrumTracksId->setChecked(cfg->showDrumTracks); showNewDrumTracksId->setChecked(cfg->showNewDrumTracks); showInputTracksId->setChecked(cfg->showInputTracks); showOutputTracksId->setChecked(cfg->showOutputTracks); showWaveTracksId->setChecked(cfg->showWaveTracks); showGroupTracksId->setChecked(cfg->showGroupTracks); showAuxTracksId->setChecked(cfg->showAuxTracks); showSyntiTracksId->setChecked(cfg->showSyntiTracks); int auxsSize = MusEGlobal::song->auxs()->size(); oldAuxsSize = auxsSize; const MusECore::TrackList *tl = MusEGlobal::song->tracks(); // NOTE: stripOrder string list is obsolete. Must support old songs. if (!cfg->stripOrder.empty()) { const int sz = cfg->stripOrder.size(); for (int i=0; i < sz; i++) { DEBUG_MIXER(stderr, "processing strip [%s][%d]\n", cfg->stripOrder.at(i).toLatin1().data(), cfg->stripVisibility.at(i)); for (MusECore::ciTrack tli = tl->cbegin(); tli != tl->cend(); ++tli) { if ((*tli)->name() == cfg->stripOrder.at(i)) { MusEGlobal::StripConfig sc; sc._visible = cfg->stripVisibility.at(i); addStrip(*tli, sc); break; } } } } // NOTE: stripConfigList is the replacement for obsolete stripOrder string list. else if (!cfg->stripConfigList.empty()) { const int sz = cfg->stripConfigList.size(); for (int i=0; i < sz; i++) { const MusEGlobal::StripConfig& sc = cfg->stripConfigList.at(i); DEBUG_MIXER(stderr, "processing strip #:%d [%d][%d]\n", i, sc._serial, sc._visible); if(!sc._deleted && !sc.isNull()) { const MusECore::Track* t = tl->findSerial(sc._serial); if (t) addStrip(t, sc); } } } else { for (MusECore::ciTrack tli = tl->cbegin(); tli != tl->cend(); ++tli) { addStrip(*tli); } } } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void AudioMixerApp::configChanged() { DEBUG_MIXER(stderr, "configChanged\n"); StripList::iterator si = stripList.begin(); // Catch when fonts change, viewable tracks, etc. No full rebuild. for (; si != stripList.end(); ++si) (*si)->configChanged(); // Detect when knobs are preferred. // TODO: Later if added: Detect when a rack component is added or removed. // Or other stuff requiring this retabbing. if(_preferKnobs != MusEGlobal::config.preferKnobsVsSliders) { _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; // Now set up all tabbing on all strips. setupComponentTabbing(); } } //--------------------------------------------------------- // updateStripList //--------------------------------------------------------- bool AudioMixerApp::updateStripList() { DEBUG_MIXER(stderr, "updateStripList stripList %d tracks %zd\n", stripList.size(), MusEGlobal::song->tracks()->size()); if (stripList.empty() && // Both obsolete. Support old files. (!cfg->stripOrder.empty() || !cfg->stripConfigList.empty())) { initMixer(); return true; } bool changed = false; const MusECore::TrackList *tl = MusEGlobal::song->tracks(); // check for superfluous strips for (StripList::iterator si = stripList.begin(); si != stripList.end(); ) { if (!tl->contains((*si)->getTrack())) { DEBUG_MIXER(stderr, "Did not find track for strip %s - Removing\n", (*si)->getLabelText().toLatin1().data()); //(*si)->deleteLater(); delete (*si); si = stripList.erase(si); changed = true; } else ++si; } // Mark deleted tracks' strip configs as deleted. // Do NOT remove them. They must hang around so undeleted tracks can find them again (below). const int config_sz = cfg->stripConfigList.size(); for (int i = 0; i < config_sz; ++i ) { MusEGlobal::StripConfig& sc = cfg->stripConfigList[i]; if(!sc._deleted && tl->indexOfSerial(sc._serial) < 0) sc._deleted = true; } // check for new tracks for (MusECore::ciTrack tli = tl->cbegin(); tli != tl->end();++tli) { const MusECore::Track* track = *tli; const int sn = track->serial(); StripList::const_iterator si = stripList.cbegin(); for (; si != stripList.cend(); ++si) { if ((*si)->getTrack() == track) { break; } } if (si == stripList.cend()) { DEBUG_MIXER(stderr, "Did not find strip for track %s - Adding\n", track->name().toLatin1().data()); // Check if there's an existing strip config for this track. int idx = 0; int i = 0; for(; i < config_sz; ++i) { MusEGlobal::StripConfig& sc = cfg->stripConfigList[i]; if(!sc.isNull() && sc._serial == sn) { // Be sure to mark the strip config as undeleted (in use). sc._deleted = false; // Insert a new strip at the index, with the given config. addStrip(track, sc, idx); changed = true; break; } if(!sc._deleted) ++idx; } // No config found? Append a new strip with a default config. if(i == config_sz) { addStrip(track); changed = true; } } } return changed; } void AudioMixerApp::updateSelectedStrips() { for (Strip *s : stripList) { if(MusECore::Track* t = s->getTrack()) { if(s->isSelected() != t->selected()) s->setSelected(t->selected()); } } } void AudioMixerApp::moveConfig(const Strip* s, int new_pos) { if(cfg->stripConfigList.empty()) return; const MusECore::Track* track = s->getTrack(); if(!track) return; const int sn = track->serial(); // The config list may also contain configs marked as 'deleted'. // Get the 'real' new_pos index, skipping over them. const int config_sz = cfg->stripConfigList.size(); int new_pos_idx = -1; int s_idx = -1; int j = 0; for (int i = 0; i < config_sz; ++i) { const MusEGlobal::StripConfig& sc = cfg->stripConfigList.at(i); // Only 'count' configs that are not deleted. if(!sc._deleted) { if(new_pos_idx == -1 && j == new_pos) new_pos_idx = i; ++j; } // While we're at it, remember the index of the given strip. if(s_idx == -1 && sc._serial == sn) s_idx = i; // If we have both pieces of information we're done. if(new_pos_idx != -1 && s_idx != -1) break; } // Nothing to do? if(new_pos_idx == -1 || s_idx == -1 || new_pos_idx == s_idx) return; // To avoid keeping a copy of the item if doing REMOVE then INSERT, do INSERT then REMOVE. // QList::swap() is available but is too new at 5.13. // Pre-increment the new pos index if removing an item would affect it. if(new_pos_idx > s_idx) ++new_pos_idx; cfg->stripConfigList.insert(new_pos_idx, cfg->stripConfigList.at(s_idx)); // Pre-increment the original pos index if removing an item would affect it. if(s_idx > new_pos_idx) ++s_idx; cfg->stripConfigList.removeAt(s_idx); } QWidget* AudioMixerApp::setupComponentTabbing(QWidget* previousWidget) { QWidget* prev = previousWidget; QLayoutItem* li; QWidget* widget; Strip* strip; const int cnt = mixerLayout->count(); for(int i = 0; i < cnt; ++i) { li = mixerLayout->itemAt(i); if(!li) continue; widget = li->widget(); if(!widget) continue; strip = qobject_cast(widget); if(!strip) continue; prev = strip->setupComponentTabbing(prev); } return prev; } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void AudioMixerApp::songChanged(MusECore::SongChangedStruct_t flags) { bool changed = false; if (flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED)) { if(updateStripList()) changed = true; } // Moving a track is not detected by updateStripList(). Do it here :-) if (flags & SC_TRACK_MOVED) changed = true; // Redrawing is costly to do every time. If no strips were added, removed, or moved, // then there should be no reason to redraw the mixer. if(changed) { if (flags & // The only flags which would require a redraw, which is very costly, are the following: (SC_TRACK_REMOVED | SC_TRACK_INSERTED | SC_TRACK_MOVED //| SC_CHANNELS // Channels due to one/two meters and peak labels can change the strip width. //| SC_AUX )) redrawMixer(); } StripList::iterator si = stripList.begin(); for (; si != stripList.end(); ++si) { (*si)->songChanged(flags); } if(flags & SC_TRACK_SELECTION) updateSelectedStrips(); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void AudioMixerApp::closeEvent(QCloseEvent* e) { emit closed(); e->accept(); } //--------------------------------------------------------- // toggleRouteDialog //--------------------------------------------------------- void AudioMixerApp::toggleRouteDialog() { showRouteDialog(routingId->isChecked()); } //--------------------------------------------------------- // showRouteDialog //--------------------------------------------------------- void AudioMixerApp::showRouteDialog(bool on) { if (on && routingDialog == nullptr) { routingDialog = new MusEGui::RouteDialog(this); connect(routingDialog, &RouteDialog::closed, [this]() { routingDialogClosed(); } ); } if (routingDialog) routingDialog->setVisible(on); //menuView->setItemChecked(routingId, on); routingId->setChecked(on); } //--------------------------------------------------------- // routingDialogClosed //--------------------------------------------------------- void AudioMixerApp::routingDialogClosed() { routingId->setChecked(false); } //--------------------------------------------------------- // show hide track groups //--------------------------------------------------------- void AudioMixerApp::showMidiTracksChanged(bool v) { cfg->showMidiTracks = v; redrawMixer(); } //void AudioMixerApp::showDrumTracksChanged(bool v) //{ // cfg->showDrumTracks = v; // redrawMixer(); //} void AudioMixerApp::showNewDrumTracksChanged(bool v) { cfg->showNewDrumTracks = v; redrawMixer(); } void AudioMixerApp::showWaveTracksChanged(bool v) { cfg->showWaveTracks = v; redrawMixer(); } void AudioMixerApp::showInputTracksChanged(bool v) { cfg->showInputTracks = v; redrawMixer(); } void AudioMixerApp::showOutputTracksChanged(bool v) { cfg->showOutputTracks = v; redrawMixer(); } void AudioMixerApp::showGroupTracksChanged(bool v) { cfg->showGroupTracks = v; redrawMixer(); } void AudioMixerApp::showAuxTracksChanged(bool v) { cfg->showAuxTracks = v; redrawMixer(); } void AudioMixerApp::showSyntiTracksChanged(bool v) { cfg->showSyntiTracks = v; redrawMixer(); } void AudioMixerApp::keyPressEvent(QKeyEvent *ev) { // Set to accept by default. ev->accept(); const int kb_code = ev->key() | ev->modifiers(); if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_SELECT_STRIP_LEFT].key) { selectNextStrip(false); return; } else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_SELECT_STRIP_RIGHT].key) { selectNextStrip(true); return; } ev->ignore(); return QMainWindow::keyPressEvent(ev); } void AudioMixerApp::resizeEvent(QResizeEvent* e) { e->ignore(); QMainWindow::resizeEvent(e); // Make sure to update the config size. // Hack flag to prevent overwriting the config geometry when resizing. // Do NOT overwrite if we set the flag before calling. if(!_resizeFlag) cfg->geometry.setSize(e->size()); } void AudioMixerApp::moveEvent(QMoveEvent* e) { e->ignore(); QMainWindow::moveEvent(e); // Make sure to update the config geometry position. cfg->geometry.setTopLeft(e->pos()); } void AudioMixerApp::clearStripSelection() { foreach (Strip *s, stripList) s->setSelected(false); } void AudioMixerApp::selectNextStrip(bool isRight) { Strip *prev = nullptr; for (int i = 0; i < mixerLayout->count(); i++) { QWidget *w = mixerLayout->itemAt(i)->widget(); if (w) { if (prev && !prev->isEmbedded() && prev->isSelected() && isRight) // got it { Strip* st = static_cast(w); MusEGlobal::song->selectAllTracks(false); clearStripSelection(); st->setSelected(true); if(st->getTrack()) st->getTrack()->setSelected(true); MusEGlobal::song->update(SC_TRACK_SELECTION); return; } else if( !static_cast(w)->isEmbedded() && static_cast(w)->isSelected() && prev && !prev->isEmbedded() && !isRight) { MusEGlobal::song->selectAllTracks(false); clearStripSelection(); prev->setSelected(true); if(prev->getTrack()) prev->getTrack()->setSelected(true); MusEGlobal::song->update(SC_TRACK_SELECTION); return; } else { prev = static_cast(w); } } } QWidget *w; if (isRight) w = mixerLayout->itemAt(0)->widget(); else w = mixerLayout->itemAt(mixerLayout->count()-1)->widget(); Strip* st = static_cast(w); if(st && !st->isEmbedded()) { MusEGlobal::song->selectAllTracks(false); clearStripSelection(); st->setSelected(true); if(st->getTrack()) st->getTrack()->setSelected(true); MusEGlobal::song->update(SC_TRACK_SELECTION); } } bool AudioMixerApp::eventFilter(QObject *obj, QEvent *event) { DEBUG_MIXER(stderr, "eventFilter type %d\n", (int)event->type()); QKeyEvent *keyEvent = nullptr;//event data, if this is a keystroke event bool result = false;//return true to consume the keystroke if (event->type() == QEvent::KeyPress) { keyEvent = dynamic_cast(event); this->keyPressEvent(keyEvent); result = true; }//if type() else if (event->type() == QEvent::KeyRelease) { keyEvent = dynamic_cast(event); this->keyReleaseEvent(keyEvent); result = true; }//else if type() //### Standard event processing ### else result = QObject::eventFilter(obj, event); return result; }//eventFilter } // namespace MusEGui muse-4.0.0/src/muse/mixer/amixer.h000066400000000000000000000123311404105311000167730ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: amixer.h,v 1.27.2.2 2009/10/18 06:13:00 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __AMIXER_H__ #define __AMIXER_H__ #include #include #include "type_defs.h" #include "gconfig.h" #define EFX_HEIGHT 16 // Forward declarations: class QWidget; class QMenu; class QAction; class QHBoxLayout; class QResizeEvent; class QMoveEvent; class QCloseEvent; class QKeyEvent; class QEvent; namespace MusECore { class Xml; class AudioTrack; class Meter; class Track; } namespace MusEGui { class ComboBox; class DoubleLabel; class Knob; class Slider; class Strip; class RouteDialog; typedef QList StripList; //--------------------------------------------------------- // ScrollArea //--------------------------------------------------------- class ScrollArea : public QScrollArea { Q_OBJECT signals: void layoutRequest(); protected: virtual bool viewportEvent(QEvent* event); public: ScrollArea(QWidget* parent = 0); }; //--------------------------------------------------------- // AudioMixerApp //--------------------------------------------------------- class AudioMixerApp : public QMainWindow { Q_OBJECT enum StripMenuOperations { UNHIDE_STRIPS = -1000, UNHANDLED_NUMBER = -1001 }; MusEGlobal::MixerConfig* cfg; StripList stripList; QScrollArea* view; QWidget* central; QHBoxLayout* mixerLayout; QMenu* menuStrips; RouteDialog* routingDialog; QAction* routingId; int oldAuxsSize; QAction* showMidiTracksId; // QAction* showDrumTracksId; QAction* showNewDrumTracksId; QAction* showInputTracksId; QAction* showOutputTracksId; QAction* showWaveTracksId; QAction* showGroupTracksId; QAction* showAuxTracksId; QAction* showSyntiTracksId; bool mixerClicked; // Current local state of knobs versus sliders preference global setting. bool _preferKnobs; // Hack flag to prevent overwriting the config geometry when resizing. bool _resizeFlag; bool stripIsVisible(Strip* s); void redrawMixer(); void addStrip(const MusECore::Track* t, const MusEGlobal::StripConfig& sc = MusEGlobal::StripConfig(), int insert_pos = -1); void showRouteDialog(bool); // Returns true if anything changed. bool updateStripList(); void fillStripListTraditional(); Strip* findStripForTrack(StripList &s, MusECore::Track *t); void updateSelectedStrips(); void moveConfig(const Strip* s, int new_pos); enum UpdateAction { NO_UPDATE, UPDATE_ALL, UPDATE_MIDI, STRIP_INSERTED, STRIP_REMOVED }; void initMixer(); void addStripsTraditionalLayout(); void addStripToLayoutIfVisible(Strip *s); void selectNextStrip(bool isRight); bool eventFilter(QObject *obj,QEvent *event); signals: void closed(); private slots: void songChanged(MusECore::SongChangedStruct_t); void configChanged(); void setSizing(); void toggleRouteDialog(); void routingDialogClosed(); void showMidiTracksChanged(bool); // void showDrumTracksChanged(bool); void showNewDrumTracksChanged(bool); void showWaveTracksChanged(bool); void showInputTracksChanged(bool); void showOutputTracksChanged(bool); void showGroupTracksChanged(bool); void showAuxTracksChanged(bool); void showSyntiTracksChanged(bool); void stripsMenu(); void handleMenu(QAction *); void clearStripSelection(); void moveStrip(Strip*); void stripVisibleChanged(Strip*, bool); void stripUserWidthChanged(Strip*, int); protected: //virtual bool event(QEvent* event); virtual void closeEvent(QCloseEvent*); virtual void keyPressEvent(QKeyEvent*); virtual void resizeEvent(QResizeEvent*); virtual void moveEvent(QMoveEvent*); public: AudioMixerApp(QWidget* parent, MusEGlobal::MixerConfig* c); void clearAndDelete(); // Sets up tabbing for the entire mixer. Strip by strip. // Accepts a previousWidget which can be null and returns the last widget in the last strip, // which allows chaining other widgets. virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mixer/astrip.cpp000066400000000000000000002250521404105311000173510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: astrip.cpp,v 1.23.2.17 2009/11/16 01:55:55 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "app.h" #include "globals.h" #include "audio.h" #include "midi_consts.h" #include "song.h" #include "slider.h" #include "compact_knob.h" #include "combobox.h" #include "astrip.h" #include "synth.h" #include "audio_fifo.h" #include "amixer.h" #include "icons.h" #include "gconfig.h" #include "menutitleitem.h" #include "routepopup.h" #include "ctrl.h" #include "utils.h" #include "muse_math.h" #include "operations.h" // Forwards from header: #include #include "track.h" #include "doublelabel.h" #include "rack.h" #include "slider.h" #include "compact_slider.h" #include "pixmap_button.h" #include "clipper_label.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_AUDIO_STRIP(dev, format, args...) //fprintf(dev, format, ##args); namespace MusEGui { const double AudioStrip::volSliderStep = 0.5; const double AudioStrip::volSliderMax = 10.0; const int AudioStrip::volSliderPrec = 1; const double AudioStrip::auxSliderStep = 1.0; const double AudioStrip::auxSliderMax = 10.0; const int AudioStrip::auxSliderPrec = 0; const double AudioStrip::gainSliderStep = 0.1; const double AudioStrip::gainSliderMin = 0.5; const double AudioStrip::gainSliderMax = 20.0; const int AudioStrip::gainSliderPrec = 1; const int AudioStrip::xMarginHorSlider = 1; const int AudioStrip::yMarginHorSlider = 1; const int AudioStrip::upperRackSpacerHeight = 2; const int AudioStrip::rackFrameWidth = 1; //--------------------------------------------------------- // AudioComponentRack //--------------------------------------------------------- AudioComponentRack::AudioComponentRack(MusECore::AudioTrack* track, int id, bool manageAuxs, QWidget* parent, Qt::WindowFlags f) : ComponentRack(id, parent, f), _track(track), _manageAuxs(manageAuxs) { } void AudioComponentRack::newComponent( ComponentDescriptor* desc, const ComponentWidget& before ) { double min = 0.0; double max = 0.0; double val = 0.0; int prec = 0.0; double step = 0.0; bool showval = MusEGlobal::config.showControlValues; switch(desc->_componentType) { case aStripAuxComponent: { val = _track->auxSend(desc->_index); //if(val == 0.0) if(val < MusEGlobal::config.minSlider) val = MusEGlobal::config.minSlider; else { val = muse_val2dbr(val); if(val < MusEGlobal::config.minSlider) val = MusEGlobal::config.minSlider; } min = MusEGlobal::config.minSlider; max = AudioStrip::auxSliderMax; prec = AudioStrip::auxSliderPrec; step = AudioStrip::auxSliderStep; // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. desc->_enabled = _track->auxRefCount() == 0; if(!desc->_color.isValid()) desc->_color = MusEGlobal::config.auxSliderColor; if(desc->_label.isEmpty()) { desc->_label = ((MusECore::AudioAux*)(MusEGlobal::song->auxs()->at(desc->_index)))->name(); } if(desc->_toolTipText.isEmpty()) desc->_toolTipText = tr("Aux send level (dB)"); } break; case controllerComponent: { MusECore::iCtrlList ic = _track->controller()->find(desc->_index); if(ic == _track->controller()->end()) return; MusECore::CtrlList* cl = ic->second; val = _track->pluginCtrlVal(desc->_index); cl->range(&min, &max); prec = 2; step = 0.01; if(desc->_label.isEmpty()) { switch(desc->_index) { case MusECore::AC_VOLUME: desc->_label = tr("Vol"); break; case MusECore::AC_PAN: desc->_label = tr("Pan"); break; case MusECore::AC_MUTE: desc->_label = tr("Mute"); break; default: desc->_label = cl->name(); break; } } if(desc->_toolTipText.isEmpty()) { switch(desc->_index) { case MusECore::AC_VOLUME: desc->_toolTipText = tr("Volume/gain"); break; case MusECore::AC_PAN: desc->_toolTipText = tr("Panorama/Balance"); break; case MusECore::AC_MUTE: desc->_toolTipText = tr("Mute"); break; default: desc->_toolTipText = cl->name(); break; } } if(!desc->_color.isValid()) { switch(desc->_index) { case MusECore::AC_PAN: desc->_color = MusEGlobal::config.panSliderColor; break; default: desc->_color = MusEGlobal::config.audioControllerSliderColor; break; } } } break; case propertyComponent: { switch(desc->_index) { case aStripGainProperty: { val = _track->gain(); min = AudioStrip::gainSliderMin; max = AudioStrip::gainSliderMax; prec = AudioStrip::gainSliderPrec; step = AudioStrip::gainSliderStep; if(desc->_label.isEmpty()) desc->_label = tr("Gain"); if(desc->_toolTipText.isEmpty()) desc->_toolTipText = tr("Calibration gain"); if(!desc->_color.isValid()) desc->_color = MusEGlobal::config.gainSliderColor; } break; default: if(!desc->_color.isValid()) desc->_color = MusEGlobal::config.audioPropertySliderColor; break; } } break; } switch(desc->_widgetType) { case CompactKnobComponentWidget: { CompactKnobComponentDescriptor* d = static_cast(desc); d->_min = min; d->_max = max; d->_precision = prec; d->_step = step; d->_initVal = val; d->_showValue = showval; if(!d->_color.isValid()) d->_color = MusEGlobal::config.sliderBackgroundColor; // test kybos // if(!d->_faceColor.isValid()) // d->_faceColor = MusEGlobal::config.sliderBackgroundColor; // if(!d->_shinyColor.isValid()) // d->_shinyColor = MusEGlobal::config.sliderBackgroundColor; // if(!d->_rimColor.isValid()) // d->_rimColor = MusEGlobal::config.sliderBackgroundColor; // Adds a component. Creates a new component using the given desc values if the desc widget is not given. // Connects known widget types' signals to slots. newComponentWidget(d, before); // Handle special slots for audio strip. switch(desc->_componentType) { case aStripAuxComponent: { if(d->_compactKnob->specialValueText().isEmpty()) { d->_compactKnob->setSpecialValueText(QString('-') + QString(QChar(0x221e))); // The infinity character } connect(d->_compactKnob, SIGNAL(valueStateChanged(double,bool,int,int)), SLOT(auxChanged(double,bool,int,int))); connect(d->_compactKnob, SIGNAL(sliderMoved(double,int,bool)), SLOT(auxMoved(double,int,bool))); connect(d->_compactKnob, SIGNAL(sliderPressed(double, int)), SLOT(auxPressed(double, int))); connect(d->_compactKnob, SIGNAL(sliderReleased(double, int)), SLOT(auxReleased(double, int))); connect(d->_compactKnob, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(auxRightClicked(QPoint,int))); } break; } } break; case CompactSliderComponentWidget: { CompactSliderComponentDescriptor* d = static_cast(desc); d->_min = min; d->_max = max; d->_precision = prec; d->_step = step; d->_initVal = val; d->_showValue = showval; if(!d->_color.isValid()) // wrong color, but hopefully set before... d->_color = MusEGlobal::config.sliderBackgroundColor; if(!d->_barColor.isValid()) d->_barColor = MusEGlobal::config.sliderBarColor; if(!d->_slotColor.isValid()) d->_slotColor = MusEGlobal::config.sliderBackgroundColor; // if(!d->_color.isValid()) // d->_color = MusEGlobal::config.sliderBackgroundColor; // // Set the bar color the same. // if(!d->_barColor.isValid()) // //d->_barColor = d->_color; // d->_barColor = MusEGlobal::config.sliderBarColor; // Adds a component. Creates a new component using the given desc values if the desc widget is not given. // Connects known widget types' signals to slots. newComponentWidget(d, before); // Handle special slots for audio strip. switch(desc->_componentType) { case aStripAuxComponent: { if(d->_compactSlider->specialValueText().isEmpty()) { d->_compactSlider->setSpecialValueText(QString('-') + QString(QChar(0x221e))); // The infinity character } connect(d->_compactSlider, SIGNAL(valueStateChanged(double,bool,int,int)), SLOT(auxChanged(double,bool,int,int))); connect(d->_compactSlider, SIGNAL(sliderMoved(double,int,bool)), SLOT(auxMoved(double,int,bool))); connect(d->_compactSlider, SIGNAL(sliderPressed(double, int)), SLOT(auxPressed(double, int))); connect(d->_compactSlider, SIGNAL(sliderReleased(double, int)), SLOT(auxReleased(double, int))); connect(d->_compactSlider, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(auxRightClicked(QPoint,int))); } break; } } break; } } void AudioComponentRack::scanControllerComponents() { std::vector to_be_erased; for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { ComponentWidget& cw = *ic; if(!cw._widget) continue; switch(cw._componentType) { case controllerComponent: { MusECore::iCtrlList ictrl = _track->controller()->find(cw._index); if(ictrl == _track->controller()->end()) to_be_erased.push_back(ic); } break; } } for(std::vector::iterator i = to_be_erased.begin(); i != to_be_erased.end(); ++i) { iComponentWidget icw = *i; ComponentWidget& cw = *icw; DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::scanControllerComponents: deleting controller component index:%d\n", cw._index); if(cw._widget) cw._widget->deleteLater(); _components.erase(icw); } } void AudioComponentRack::scanAuxComponents() { std::vector to_be_erased; for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { ComponentWidget& cw = *ic; if(!cw._widget) continue; switch(cw._componentType) { case aStripAuxComponent: { // TODO: This is just brute-force deletion and recreation of all the auxs. // Make this more efficient by only removing what's necessary and updating/re-using the rest. to_be_erased.push_back(ic); } break; } } for(std::vector::iterator i = to_be_erased.begin(); i != to_be_erased.end(); ++i) { iComponentWidget icw = *i; ComponentWidget& cw = *icw; DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::scanAuxComponents: deleting aux component index:%d\n", cw._index); if(cw._widget) cw._widget->deleteLater(); _components.erase(icw); } // Add auxs, only if we want this rack to manage auxs. if(_manageAuxs) { int auxsSize = MusEGlobal::song->auxs()->size(); if(_track->hasAuxSend()) { for (int idx = 0; idx < auxsSize; ++idx) { // the thought was to acquire the correct Aux name for each Aux // now they are only called Aux1, Aux2, which isn't too usable. // QString title = ((MusECore::AudioAux*)(MusEGlobal::song->auxs()->at(idx)))->auxName(); // if (title.length() > 8) { // shorten name // title = title.mid(0,8) + "."; // } // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. // const bool enable = _track->auxRefCount() == 0; if(MusEGlobal::config.preferKnobsVsSliders) { CompactKnobComponentDescriptor aux_desc ( aStripAuxComponent, "MixerStripAudioAux", idx ); DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::scanAuxComponents: adding aux component index:%d\n", idx); newComponent(&aux_desc); } else { CompactSliderComponentDescriptor aux_desc ( aStripAuxComponent, "MixerStripAudioAux", idx ); DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::scanAuxComponents: adding aux component index:%d\n", idx); newComponent(&aux_desc); } } } } } void AudioComponentRack::updateComponents() { for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { ComponentWidget& cw = *ic; if(!cw._widget) continue; switch(cw._componentType) { case controllerComponent: { // Inhibit the controller stream if control is currently pressed. // Note _pressed operates differently than simply checking if the control is pressed! if(cw._pressed) continue; const double val = _track->pluginCtrlVal(cw._index); setComponentValue(cw, val); // Signals blocked. Redundant ignored. } break; case propertyComponent: { switch(cw._index) { case aStripGainProperty: { const double val = _track->gain(); setComponentValue(cw, val); // Signals blocked. Redundant ignored. } break; } } break; case aStripAuxComponent: { double val = _track->auxSend(cw._index); if(val == 0.0) val = MusEGlobal::config.minSlider; else { val = muse_val2dbr(val); if(val < MusEGlobal::config.minSlider) val = MusEGlobal::config.minSlider; } setComponentValue(cw, val); // Signals blocked. Redundant ignored. } break; } } } void AudioComponentRack::setAuxEnabled(bool enable) { for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { ComponentWidget& cw = *ic; switch(cw._componentType) { case aStripAuxComponent: setComponentEnabled(cw, enable); break; } } } void AudioComponentRack::controllerChanged(double val, bool off, int id, int scrollMode) { DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::controllerChanged id:%d val:%.20f scrollMode:%d\n", id, val, scrollMode); // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. // ScrDirect mode is one-time only on press with modifier. if(scrollMode != SliderBase::ScrDirect) _track->recordAutomation(id, val); _track->setParam(id, val); // Schedules a timed control change. //_track->setPluginCtrlVal(id, val); // TODO Try this instead. setParam gives a slight jump at release, in tracking off temp mode. _track->enableController(id, false); emit componentChanged(controllerComponent, val, off, id, scrollMode); } void AudioComponentRack::controllerMoved(double val, int id, bool shift_pressed) { DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::controllerMoved id:%d val:%.20f\n", id, val); emit componentMoved(controllerComponent, val, id, shift_pressed); } void AudioComponentRack::controllerPressed(double v, int id) { DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::controllerPressed id:%d\n", id); double val = 0.0; iComponentWidget ic = _components.find(controllerComponent, -1, id); if(ic != _components.end()) { ComponentWidget& cw = *ic; cw._pressed = true; val = componentValue(cw); DEBUG_AUDIO_STRIP(stderr, " val:%.20f\n", val); } _track->startAutoRecord(id, val); _track->setPluginCtrlVal(id, val); //_track->setParam(id, val); // Schedules a timed control change. // TODO Try this instead DEBUG_AUDIO_STRIP(stderr, " calling enableController(false)\n"); _track->enableController(id, false); emit componentPressed(controllerComponent, v, id); } void AudioComponentRack::controllerReleased(double v, int id) { DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::controllerReleased id:%d\n", id); MusECore::AutomationType at = _track->automationType(); double val = 0.0; iComponentWidget ic = _components.find(controllerComponent, -1, id); if(ic != _components.end()) { ComponentWidget& cw = *ic; val = componentValue(cw); DEBUG_AUDIO_STRIP(stderr, " val:%.20f\n", val); cw._pressed = false; } _track->stopAutoRecord(id, val); if(at == MusECore::AUTO_OFF || at == MusECore::AUTO_TOUCH) { DEBUG_AUDIO_STRIP(stderr, " calling enableController(true)\n"); _track->enableController(id, true); } emit componentReleased(controllerComponent, v, id); } void AudioComponentRack::controllerRightClicked(QPoint p, int id) { DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::controllerRightClicked id:%d\n", id); MusEGlobal::song->execAutomationCtlPopup(_track, p, id); } void AudioComponentRack::propertyChanged(double val, bool off, int id, int scrollMode) { DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::propertyChanged id:%d val:%.20f\n", id, val); switch(id) { case aStripGainProperty: if(_track->gain() != val) _track->setGain(val); // FIXME: Realtime safe? break; } emit componentChanged(propertyComponent, val, off, id, scrollMode); } void AudioComponentRack::propertyMoved(double val, int id, bool shift_pressed) { DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::propertyMoved id:%d val:%.20f\n", id, val); emit componentMoved(propertyComponent, val, id, shift_pressed); } void AudioComponentRack::propertyPressed(double val, int id) { emit componentPressed(propertyComponent, val, id); } void AudioComponentRack::propertyReleased(double val, int id) { emit componentReleased(propertyComponent, val, id); } void AudioComponentRack::propertyRightClicked(QPoint, int) { } void AudioComponentRack::auxChanged(double val, bool off, int id, int scrollMode) { DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::auxChanged id:%d val:%.20f\n", id, val); double vol; if (val <= MusEGlobal::config.minSlider) vol = 0.0; else vol = muse_db2val(val); MusEGlobal::audio->msgSetAux(_track, id, vol); emit componentChanged(aStripAuxComponent, val, off, id, scrollMode); } void AudioComponentRack::auxMoved(double val, int id, bool shift_pressed) { DEBUG_AUDIO_STRIP(stderr, "AudioComponentRack::auxMoved id:%d val:%.20f\n", id, val); emit componentMoved(aStripAuxComponent, val, id, shift_pressed); } void AudioComponentRack::auxPressed(double val, int id) { emit componentPressed(aStripAuxComponent, val, id); } void AudioComponentRack::auxReleased(double val, int id) { emit componentReleased(aStripAuxComponent, val, id); } void AudioComponentRack::auxRightClicked(QPoint, int) { } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void AudioComponentRack::songChanged(MusECore::SongChangedStruct_t flags) { // Scan controllers. if(flags & (SC_RACK | SC_AUDIO_CONTROLLER_LIST)) { scanControllerComponents(); } // Take care of scanning aux before setting aux enabled below. if(flags & SC_AUX) { scanAuxComponents(); } if(flags & SC_ROUTE) { // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. setAuxEnabled(_track->auxRefCount() == 0); } } //--------------------------------------------------------- // configChanged // Catch when label font, or configuration min slider and meter values change, or viewable tracks etc. //--------------------------------------------------------- void AudioComponentRack::configChanged() { // Handle font changes etc. ComponentRack::configChanged(); for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { ComponentWidget& cw = *ic; // Whether to show values along with labels for certain controls. setComponentShowValue(cw, MusEGlobal::config.showControlValues); switch(cw._componentType) { // Special for Aux controls. case aStripAuxComponent: // Adjust aux minimum value. setComponentRange(cw, MusEGlobal::config.minSlider, AudioStrip::auxSliderMax, true, AudioStrip::auxSliderStep); break; } } setComponentColors(); } //--------------------------------------------------------- // setComponentColors //--------------------------------------------------------- void AudioComponentRack::setComponentColors() { for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { const ComponentWidget& cw = *ic; if(!cw._widget) continue; QColor color = MusEGlobal::config.sliderBackgroundColor; switch(cw._componentType) { case aStripAuxComponent: color = MusEGlobal::config.auxSliderColor; break; case controllerComponent: { switch(cw._index) { case MusECore::AC_PAN: color = MusEGlobal::config.panSliderColor; break; default: color = MusEGlobal::config.audioControllerSliderColor; break; } } break; case propertyComponent: { switch(cw._index) { case aStripGainProperty: color = MusEGlobal::config.gainSliderColor; break; default: color = MusEGlobal::config.audioPropertySliderColor; break; } } break; } switch(cw._widgetType) { case CompactKnobComponentWidget: { CompactKnob* w = static_cast(cw._widget); w->setFaceColor(color); } break; case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); w->setBorderColor(color); w->setThumbColor(color); w->setBarColor(MusEGlobal::config.sliderBarColor); w->setSlotColor(MusEGlobal::config.sliderBackgroundColor); } break; } } } //--------------------------------------------------------- // AudioStripProperties //--------------------------------------------------------- AudioStripProperties::AudioStripProperties() { _sliderRadius = 4; _sliderRadiusHandle = 2; _sliderHandleHeight = 16; _sliderHandleWidth = 16; _sliderFillOver = true; _sliderUseGradient = true; _sliderBackbone = false; _sliderFillHandle = true; _sliderGrooveWidth = 14; _sliderScalePos = Slider::InsideVertical; _sliderFrame = false; _sliderFrameColor = Qt::darkGray; _meterWidth = Strip::FIXED_METER_WIDTH; _meterWidthPerChannel = false; _meterSpacing = 4; _meterFrame = false; _meterFrameColor = Qt::darkGray; ensurePolished(); } //--------------------------------------------------------- // AudioStrip //--------------------------------------------------------- //--------------------------------------------------------- // heartBeat //--------------------------------------------------------- void AudioStrip::heartBeat() { const int tch = track->channels(); for (int ch = 0; ch < tch; ++ch) { if (meter[ch]) { meter[ch]->setVal(track->meter(ch), track->peak(ch), false); } if(_clipperLabel[ch]) { _clipperLabel[ch]->setVal(track->peak(ch)); _clipperLabel[ch]->setClipped(track->isClipped(ch)); } } updateVolume(); _upperRack->updateComponents(); // _infoRack->updateComponents(); _lowerRack->updateComponents(); // if(_recMonitor && _recMonitor->isChecked() && MusEGlobal::blinkTimerPhase != _recMonitor->blinkPhase()) // _recMonitor->setBlinkPhase(MusEGlobal::blinkTimerPhase); Strip::heartBeat(); } void AudioStrip::updateRackSizes(bool upper, bool lower) { // const QFontMetrics fm = fontMetrics(); if(upper) { // Make room for 3 CompactSliders and one CompactPatchEdit. // TODO: Add the instrument select label height! // // const int csh = CompactSlider::getMinimumSizeHint(fm, // // Qt::Horizontal, // // CompactSlider::None, // // xMarginHorSlider, yMarginHorSlider).height(); // // const int cpeh = CompactPatchEdit::getMinimumSizeHint(fm, // // Qt::Horizontal, // // CompactSlider::None, // // xMarginHorSlider, yMarginHorSlider).height(); // // const int ilh = _instrLabel->sizeHint().height(); // // // DEBUG_AUDIO_STRIP(stderr, "MidiStrip::updateRackSizes: CompactSlider h:%d CompactPatchEdit h:%d instrLabel h:%d upper frame w:%d \n", // // csh, cpeh, ilh, _upperRack->frameWidth()); // _upperRack->setMinimumHeight( // 3 * CompactSlider::getMinimumSizeHint(fm, // Qt::Horizontal, // CompactSlider::None, // xMarginHorSlider, yMarginHorSlider).height() + // 1 * CompactPatchEdit::getMinimumSizeHint(fm, // Qt::Horizontal, // CompactSlider::None, // xMarginHorSlider, yMarginHorSlider).height() + // upperRackSpacerHeight + // // _instrLabel->sizeHint().height() + // // 2 * rackFrameWidth); } if(lower) { // Make room for 1 CompactSlider (Pan, so far). //DEBUG_AUDIO_STRIP(stderr, "MidiStrip::updateRackSizes: lower frame w:%d \n", _lowerRack->frameWidth()); // _lowerRack->setMinimumHeight( // 1 * CompactSlider::getMinimumSizeHint(fm, // Qt::Horizontal, // CompactSlider::None, // xMarginHorSlider, yMarginHorSlider).height() + // 2 * rackFrameWidth); } } //--------------------------------------------------------- // configChanged // Catch when label font, or configuration min slider and meter values change, or viewable tracks etc. //--------------------------------------------------------- void AudioStrip::configChanged() { // Detect when knobs are preferred and rebuild. if(_preferKnobs != MusEGlobal::config.preferKnobsVsSliders) { _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; // Rebuild the strip components. buildStrip(); // Now set up all tabbing on the strip. // Don't bother if the strip is part of the mixer (not embedded), // the non-embedding parent (mixer) should set up all the tabs and make this call. if(isEmbedded()) setupComponentTabbing(); } // Set the whole strip's font, except for the label. if (font() != MusEGlobal::config.fonts[1]) { // DEBUG_AUDIO_STRIP(stderr, "AudioStrip::configChanged changing font: current size:%d\n", font().pointSize()); setStripStyle(); } // Set the strip label's font. setLabelText(); slider->setFillColor(MusEGlobal::config.audioVolumeSliderColor); slider->setHandleColor(MusEGlobal::config.audioVolumeHandleColor); // Adjust minimum volume slider and label values. slider->setRange(MusEGlobal::config.minSlider, volSliderMax, volSliderStep); slider->setScale(MusEGlobal::config.minSlider, volSliderMax, 6.0, false); sl->setRange(MusEGlobal::config.minSlider, volSliderMax); sl->setOff(MusEGlobal::config.minSlider); // Enable special hack for line edits. // if(sl->enableStyleHack() != MusEGlobal::config.lineEditStyleHack) // sl->setEnableStyleHack(MusEGlobal::config.lineEditStyleHack); // REMOVE Tim. mixer. Added. // In case something in the slider changed, update the meter layout. // TODO This is somewhat crude, might miss automatic changes. // Later link up MeterLayout and Slider better. _meterLayout->setMeterEndsMargin(slider->scaleEndpointsMargin()); // Possible, but leave it to the background painter for now. //rack->setActiveColor(MusEGlobal::config.rackItemBackgroundColor); _upperRack->configChanged(); // _infoRack->configChanged(); _lowerRack->configChanged(); // Ensure updateGeometry is called in case the number of rack items changed. // Not required for at least suse, but required for at least mint cinnamon. rack->updateGeometry(); rack->update(); // Adjust minimum meter values, and colours. for(int c = 0; c < channel; ++c) { meter[c]->setRange(MusEGlobal::config.minMeter, volSliderMax); meter[c]->setPrimaryColor(MusEGlobal::config.audioMeterPrimaryColor, MusEGlobal::config.meterBackgroundColor); meter[c]->setRefreshRate(MusEGlobal::config.guiRefresh); } } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void AudioStrip::songChanged(MusECore::SongChangedStruct_t val) { MusECore::AudioTrack* src = static_cast(track); // Do channels before MusEGlobal::config... if (val & SC_CHANNELS) updateChannels(); // Catch when label font, or configuration min slider and meter values change. if (val & SC_CONFIG) { // So far only 1 instance of sending SC_CONFIG in the entire app, in instrument editor when a new instrument is saved. } if (mute && (val & SC_MUTE)) { // mute && off mute->blockSignals(true); mute->setChecked(src->mute()); mute->blockSignals(false); updateMuteIcon(); updateOffState(); } if (solo && (val & (SC_SOLO | SC_ROUTE))) { solo->blockSignals(true); solo->setChecked(track->solo()); solo->blockSignals(false); // solo->setIconSetB(track->internalSolo()); if (track->internalSolo()) { if (solo->isChecked()) solo->setIcon(*soloAndProxyOnSVGIcon); else solo->setIcon(*soloProxyOnAloneSVGIcon); } else { solo->setIcon(*soloStateSVGIcon); } updateMuteIcon(); } if (val & SC_RECFLAG) { setRecordFlag(track->recordFlag()); } if (val & SC_TRACK_MODIFIED) { setLabelText(); } //if (val & SC_CHANNELS) // updateChannels(); if (val & SC_ROUTE) { updateRouteButtons(); if (pre) { pre->blockSignals(true); pre->setChecked(src->prefader()); pre->blockSignals(false); } } if(val & SC_TRACK_REC_MONITOR) { // Set record monitor. if(_recMonitor) // && (_recMonitor->isChecked() != track->recMonitor())) { _recMonitor->blockSignals(true); _recMonitor->setChecked(track->recMonitor()); _recMonitor->blockSignals(false); } } // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. _upperRack->songChanged(val); // _infoRack->songChanged(val); _lowerRack->songChanged(val); if (autoType && (val & SC_AUTOMATION)) { autoType->blockSignals(true); autoType->setCurrentItem(track->automationType()); colorAutoType(); autoType->blockSignals(false); } } //--------------------------------------------------------- // updateVolume //--------------------------------------------------------- void AudioStrip::updateVolume() { if(_volPressed) // Inhibit the controller stream if control is currently pressed. return; double vol = static_cast(track)->volume(); if (vol != volume) { double val; if(vol == 0.0) val = MusEGlobal::config.minSlider; else { val = muse_val2dbr(vol); if(val < MusEGlobal::config.minSlider) val = MusEGlobal::config.minSlider; } slider->blockSignals(true); sl->blockSignals(true); // Slider::fitValue should not be required since the log function is accurate but rounded to the nearest .000001 slider->setValue(val); sl->setValue(val); sl->blockSignals(false); slider->blockSignals(false); volume = vol; } } //--------------------------------------------------------- // offToggled //--------------------------------------------------------- void AudioStrip::offToggled(bool val) { if(!track) return; // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackOff)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // updateOffState //--------------------------------------------------------- void AudioStrip::updateOffState() { bool val = !track->off(); slider->setEnabled(val); sl->setEnabled(val); _upperRack->setEnabled(val); // _infoRack->setEnabled(val); _lowerRack->setEnabled(val); if (track->type() != MusECore::Track::AUDIO_SOFTSYNTH) stereo->setEnabled(val); label->setEnabled(val); // Are there any Aux Track routing paths to this track? Then we cannot process aux for this track! // Hate to do this, but as a quick visual reminder, seems most logical to disable Aux knobs and labels. const bool ae = track->auxRefCount() == 0 && val; _upperRack->setAuxEnabled(ae); // _infoRack->setAuxEnabled(ae); _lowerRack->setAuxEnabled(ae); if (_recMonitor) _recMonitor->setEnabled(val); if (pre) pre->setEnabled(val); if (record) record->setEnabled(val); if (solo) solo->setEnabled(val); if (mute) mute->setEnabled(val); if (off) { off->blockSignals(true); off->setChecked(track->off()); off->blockSignals(false); } } //--------------------------------------------------------- // preToggled //--------------------------------------------------------- void AudioStrip::preToggled(bool val) { if(!track) return; MusEGlobal::audio->msgSetPrefader(static_cast(track), val); resetPeaks(); MusEGlobal::song->update(SC_ROUTE); } //--------------------------------------------------------- // stereoToggled //--------------------------------------------------------- void AudioStrip::stereoToggled(bool val) { if(!track) return; const int nc = val ? 2 : 1; const int oc = track->channels(); if (oc == nc) return; MusEGlobal::audio->msgSetChannels(static_cast(track), nc); MusEGlobal::song->update(SC_CHANNELS); } //--------------------------------------------------------- // recMonitorToggled //--------------------------------------------------------- void AudioStrip::recMonitorToggled(bool v) { if(!track) return; // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(track, v, MusECore::PendingOperationItem::SetTrackRecMonitor)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // volumeMoved //--------------------------------------------------------- void AudioStrip::volumeMoved(double val, int id, bool shift_pressed) { DEBUG_AUDIO_STRIP(stderr, "AudioStrip::volumeMoved id:%d val:%.20f\n", id, val); componentMoved(ComponentRack::controllerComponent, val, id, shift_pressed); } //--------------------------------------------------------- // volumeChanged //--------------------------------------------------------- void AudioStrip::volumeChanged(double val, int id, int scrollMode) { DEBUG_AUDIO_STRIP(stderr, "AudioStrip::volumeChanged id:%d val:%.20f scrollMode:%d\n", id, val, scrollMode); if(!track || track->isMidiTrack()) return; double vol; if (val <= MusEGlobal::config.minSlider) vol = 0.0; else vol = muse_db2val(val); volume = vol; MusECore::AudioTrack* at = static_cast(track); // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. // ScrDirect mode is one-time only on press with modifier. if(scrollMode != SliderBase::ScrDirect) at->recordAutomation(id, vol); at->setParam(id, vol); // Schedules a timed control change. at->enableController(id, false); componentChanged(ComponentRack::controllerComponent, val, false, id, scrollMode); } //--------------------------------------------------------- // volumePressed //--------------------------------------------------------- void AudioStrip::volumePressed(double val, int id) { DEBUG_AUDIO_STRIP(stderr, "AudioStrip::volumePressed\n"); if(!track || track->isMidiTrack()) return; _volPressed = true; double vol; if (val <= MusEGlobal::config.minSlider) vol = 0.0; else vol = muse_db2val(val); volume = vol; DEBUG_AUDIO_STRIP(stderr, " val:%.20f\n", volume); MusECore::AudioTrack* at = static_cast(track); at->startAutoRecord(id, vol); at->setVolume(vol); //at->setParam(MusECore::AC_VOLUME, val); // Schedules a timed control change. // TODO Try this instead DEBUG_AUDIO_STRIP(stderr, " calling enableController(false)\n"); at->enableController(id, false); componentPressed(ComponentRack::controllerComponent, val, id); } //--------------------------------------------------------- // volumeReleased //--------------------------------------------------------- void AudioStrip::volumeReleased(double val, int id) { DEBUG_AUDIO_STRIP(stderr, "AudioStrip::volumeReleased\n"); if(!track || track->isMidiTrack()) return; MusECore::AudioTrack* at = static_cast(track); MusECore::AutomationType atype = at->automationType(); DEBUG_AUDIO_STRIP(stderr, " val:%.20f\n", volume); at->stopAutoRecord(id, volume); if(atype == MusECore::AUTO_OFF || atype == MusECore::AUTO_TOUCH) { DEBUG_AUDIO_STRIP(stderr, " calling enableController(true)\n"); at->enableController(id, true); } componentReleased(ComponentRack::controllerComponent, val, id); _volPressed = false; } //--------------------------------------------------------- // volumeRightClicked //--------------------------------------------------------- void AudioStrip::volumeRightClicked(QPoint p) { MusEGlobal::song->execAutomationCtlPopup(static_cast(track), p, MusECore::AC_VOLUME); } //--------------------------------------------------------- // volLabelChanged //--------------------------------------------------------- void AudioStrip::volLabelChanged(double val) { if(!track || track->isMidiTrack()) return; MusECore::AudioTrack* t = static_cast(track); double v = val; double vol; if (v <= MusEGlobal::config.minSlider) { vol = 0.0; v = MusEGlobal::config.minSlider; } else vol = muse_db2val(v); volume = vol; slider->blockSignals(true); slider->setValue(v); slider->blockSignals(false); t->startAutoRecord(MusECore::AC_VOLUME, vol); t->setParam(MusECore::AC_VOLUME, vol); // Schedules a timed control change. t->enableController(MusECore::AC_VOLUME, false); componentChanged(ComponentRack::controllerComponent, val, false, MusECore::AC_VOLUME, SliderBase::ScrNone); } void AudioStrip::resetClipper() { if(track) { track->resetClipper(); resetPeaks(); } } //--------------------------------------------------------- // updateChannels //--------------------------------------------------------- void AudioStrip::updateChannels() { MusECore::AudioTrack* t = static_cast(track); int c = t->channels(); DEBUG_AUDIO_STRIP(stderr, "AudioStrip::updateChannels track channels:%d current channels:%d\n", c, channel); if (c > channel) { for (int cc = channel; cc < c; ++cc) { _clipperLabel[cc] = new ClipperLabel(); _clipperLabel[cc]->setContentsMargins(0, 0, 0, 0); _clipperLabel[cc]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setClipperTooltip(cc); _clipperLayout->addWidget(_clipperLabel[cc]); connect(_clipperLabel[cc], SIGNAL(clicked()), SLOT(resetClipper())); meter[cc] = new Meter(this, Meter::DBMeter, Qt::Vertical, MusEGlobal::config.minMeter, volSliderMax); meter[cc]->setRefreshRate(MusEGlobal::config.guiRefresh); meter[cc]->setFixedWidth(props.meterWidth()); meter[cc]->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); meter[cc]->setPrimaryColor(MusEGlobal::config.audioMeterPrimaryColor, MusEGlobal::config.meterBackgroundColor); meter[cc]->setFrame(props.meterFrame(), props.meterFrameColor()); connect(meter[cc], SIGNAL(mousePress()), this, SLOT(resetClipper())); _meterLayout->hlayout()->addWidget(meter[cc], Qt::AlignLeft); // meter[cc]->show(); } } else if (c < channel) { for (int cc = channel-1; cc >= c; --cc) { if(_clipperLabel[cc]) delete _clipperLabel[cc]; _clipperLabel[cc] = nullptr; if(meter[cc]) delete meter[cc]; meter[cc] = nullptr; } } if (meter[0] && !meter[0]->vu3d() && !props.meterWidthPerChannel()) { for (int ch = 0; ch < c; ++ch) { meter[ch]->setFixedWidth(props.meterWidth() / c); } } channel = c; stereo->blockSignals(true); stereo->setChecked(channel == 2); stereo->blockSignals(false); update(); } //--------------------------------------------------------- // AudioStrip //--------------------------------------------------------- AudioStrip::~AudioStrip() { } //--------------------------------------------------------- // AudioStrip // create mixer strip //--------------------------------------------------------- AudioStrip::AudioStrip(QWidget* parent, MusECore::AudioTrack* at, bool hasHandle, bool isEmbedded) : Strip(parent, at, hasHandle, isEmbedded) { _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; MusECore::Track::TrackType type = at->type(); volume = -1.0; _volPressed = false; slider = nullptr; sl = nullptr; off = nullptr; _recMonitor = nullptr; // Start the layout in mode A (normal, racks on left). _isExpanded = false; setStripStyle(); channel = at->channels(); _routePos = GridPosStruct(_curGridRow, 0, 1, 3); _effectRackPos = GridPosStruct(_curGridRow + 1, 0, 1, 3); _stereoPrePos = GridPosStruct(_curGridRow + 2, 0, 1, 3); _upperRackPos = GridPosStruct(_curGridRow + 3, 0, 1, 3); _sliderMeterPos = GridPosStruct(_curGridRow + 4, 0, 1, 3); _lowerRackPos = GridPosStruct(_curGridRow + 5, 0, 1, 3); _bottomPos = GridPosStruct(_curGridRow + 6, 0, 1, 3); // _routePos = GridPosStruct(_curGridRow, 0, 1, 2); // _effectRackPos = GridPosStruct(_curGridRow + 1, 0, 1, 3); // _stereoPrePos = GridPosStruct(_curGridRow + 2, 0, 1, 2); // _upperRackPos = GridPosStruct(_curGridRow + 3, 0, 1, 3); // _sliderMeterPos = GridPosStruct(_curGridRow + 4, 0, 1, 2); // _lowerRackPos = GridPosStruct(_curGridRow + 5, 0, 1, 3); // _bottomPos = GridPosStruct(_curGridRow + 6, 0, 1, 2); //--------------------------------------------------- // routing //--------------------------------------------------- QHBoxLayout *routeLayout = new QHBoxLayout; routeLayout->setContentsMargins(1,3,1,2); routeLayout->setSpacing(1); if (type != MusECore::Track::AUDIO_AUX) { // iR = new IconButton(routingInputSVGIcon, routingInputSVGIcon, // routingInputUnconnectedSVGIcon, routingInputUnconnectedSVGIcon, false, true); iR = new QPushButton(this); iR->setIcon(*routingInputSVGIcon); iR->setStatusTip(tr("Input routing. Hold CTRL to keep menu open. Press F1 for help.")); iR->setFocusPolicy(Qt::NoFocus); iR->setCheckable(false); iR->setToolTip(MusEGlobal::inputRoutingToolTipBase); connect(iR, SIGNAL(pressed()), SLOT(iRoutePressed())); routeLayout->addWidget(iR); } else { QPushButton *iRx = new QPushButton(this); iRx->setIcon(*routingInputSVGIcon); iRx->setEnabled(false); routeLayout->addWidget(iRx); } // oR = new IconButton(routingOutputSVGIcon, routingOutputSVGIcon, // routingOutputUnconnectedSVGIcon, routingOutputUnconnectedSVGIcon, false, true); oR = new QPushButton(this); oR->setIcon(*routingOutputSVGIcon); oR->setObjectName("OutputRouteButton"); oR->setStatusTip(tr("Output routing. Hold CTRL to keep menu open. Press F1 for help.")); oR->setFocusPolicy(Qt::NoFocus); oR->setCheckable(false); oR->setToolTip(MusEGlobal::outputRoutingToolTipBase); connect(oR, SIGNAL(pressed()), SLOT(oRoutePressed())); routeLayout->addWidget(oR); updateRouteButtons(); addGridLayout(routeLayout, _routePos); // _infoRack = new AudioComponentRack(at, aStripInfoRack, false); // //_infoRack->setVisible(false); // Not visible unless expanded. // _infoRack->setVisible(false); // Hide until we figure out what to put in there.... // // FIXME For some reason StyledPanel has trouble, intermittent sometimes panel is drawn, sometimes not. // //_infoRack->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); // _infoRack->setFrameStyle(QFrame::Box | QFrame::Sunken); // _infoRack->setLineWidth(rackFrameWidth); // _infoRack->setMidLineWidth(0); // _infoRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); // _infoRack->setContentsMargins(rackFrameWidth, rackFrameWidth, rackFrameWidth, rackFrameWidth); // _infoRack->setFocusPolicy(Qt::NoFocus); // _infoRack->addStretch(); // addGridWidget(_infoRack, _propertyRackPos); // grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding), // _infoSpacerTop._row, _infoSpacerTop._col, _infoSpacerTop._rowSpan, _infoSpacerTop._colSpan); _upperRack = new AudioComponentRack(at, aStripUpperRack, true); // True = manage auxs. // FIXME For some reason StyledPanel has trouble, intermittent sometimes panel is drawn, sometimes not. //_upperRack->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); _upperRack->setFrameStyle(QFrame::Box | QFrame::Sunken); _upperRack->setLineWidth(rackFrameWidth); _upperRack->setMidLineWidth(0); // We do set a minimum height on this widget. Tested: Must be on fixed. Thankfully, it'll expand if more controls are added. _upperRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); _upperRack->setContentsMargins(rackFrameWidth, rackFrameWidth, rackFrameWidth, rackFrameWidth); _upperRack->setFocusPolicy(Qt::NoFocus); //--------------------------------------------------- // plugin rack //--------------------------------------------------- rack = new EffectRack(this, at); rack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); addGridWidget(rack, _effectRackPos); addGridWidget(_upperRack, _upperRackPos); //--------------------------------------------------- // mono/stereo pre/post //--------------------------------------------------- QHBoxLayout *stereoPreLayout = new QHBoxLayout; stereoPreLayout->setContentsMargins(1,2,1,2); stereoPreLayout->setSpacing(1); // stereo = new IconButton(stereoOnSVGIcon, stereoOffSVGIcon, nullptr, nullptr, false, true); stereo = new QPushButton(this); stereo->setIcon(*stereoOnSVGIcon); stereo->setFocusPolicy(Qt::NoFocus); stereo->setCheckable(true); stereo->setToolTip(tr("1/2 channel")); stereo->setChecked(channel == 2); // disable mono/stereo for Synthesizer-Plugins if (type == MusECore::Track::AUDIO_SOFTSYNTH) stereo->setEnabled(false); connect(stereo, SIGNAL(toggled(bool)), SLOT(stereoToggled(bool))); stereoPreLayout->addWidget(stereo); // pre = new IconButton(preFaderOnSVGIcon, preFaderOffSVGIcon, nullptr, nullptr, false, true); pre = new QPushButton(this); pre->setIcon(*preFaderOnSVGIcon); pre->setFocusPolicy(Qt::NoFocus); pre->setCheckable(true); pre->setToolTip(tr("Pre Fader Listening (PFL)")); pre->setChecked(at->prefader()); connect(pre, SIGNAL(toggled(bool)), SLOT(preToggled(bool))); stereoPreLayout->addWidget(pre); addGridLayout(stereoPreLayout, _stereoPrePos); //--------------------------------------------------- // slider, label, meter //--------------------------------------------------- sliderGrid = new QGridLayout(); sliderGrid->setContentsMargins(2, 0, 3, 2); sliderGrid->setSpacing(0); sliderGrid->setHorizontalSpacing(2); /*-------------- clipper label -------------------*/ _clipperLayout = new QHBoxLayout(); _clipperLayout->setSpacing(0); { int ch = 0; for (; ch < channel; ++ch) { _clipperLabel[ch] = new ClipperLabel(this); _clipperLabel[ch]->setContentsMargins(0, 0, 0, 0); _clipperLabel[ch]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setClipperTooltip(ch); connect(_clipperLabel[ch], SIGNAL(clicked()), SLOT(resetClipper())); _clipperLayout->addWidget(_clipperLabel[ch]); } for (; ch < MusECore::MAX_CHANNELS; ++ch) { _clipperLabel[ch] = nullptr; meter[ch] = nullptr; } } sliderGrid->addLayout(_clipperLayout, 0, 0, 1, 2, Qt::AlignCenter); slider = new Slider(this, "vol", Qt::Vertical, MusEGui::Slider::InsideVertical, 14, MusEGlobal::config.audioVolumeSliderColor, ScaleDraw::TextHighlightSplitAndShadow, MusEGlobal::config.audioVolumeHandleColor); slider->setId(MusECore::AC_VOLUME); slider->setFocusPolicy(Qt::NoFocus); slider->setContentsMargins(0, 0, 0, 0); slider->setCursorHoming(true); DEBUG_AUDIO_STRIP(stderr, "AudioStrip::AudioStrip new slider: step:%.20f\n", volSliderStep); slider->setRange(MusEGlobal::config.minSlider, volSliderMax, volSliderStep); //slider->setScaleMaxMinor(5); slider->setScale(MusEGlobal::config.minSlider, volSliderMax, 6.0, false); slider->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. slider->setScaleBackBone(props.sliderBackbone()); slider->setRadius(props.sliderRadius()); slider->setRadiusHandle(props.sliderRadiusHandle()); slider->setHandleHeight(props.sliderHandleHeight()); slider->setHandleWidth(props.sliderHandleWidth()); slider->setFillThumb(props.sliderFillHandle()); slider->setGrooveWidth(props.sliderGrooveWidth()); slider->setFillEmptySide(props.sliderFillOver()); slider->setUseGradient(props.sliderUseGradient()); slider->setScalePos(static_cast(props.sliderScalePos())); slider->setFrame(props.sliderFrame()); slider->setFrameColor(props.sliderFrameColor()); slider->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); slider->setMinimumHeight(80); double track_vol = at->volume(); if(track_vol == 0.0) track_vol = MusEGlobal::config.minSlider; else { track_vol = muse_val2dbr(track_vol); if(track_vol < MusEGlobal::config.minSlider) track_vol = MusEGlobal::config.minSlider; } // Slider::fitValue() not required so far. The log function is accurate but rounded to the nearest .000001 slider->setValue(track_vol); sliderGrid->addWidget(slider, 1, 0, Qt::AlignHCenter); _meterLayout = new MeterLayout(slider->scaleEndpointsMargin()); _meterLayout->setMargin(0); _meterLayout->setSpacing(props.meterSpacing()); sliderGrid->addLayout(_meterLayout, 1, 1, Qt::AlignHCenter); for (int i = 0; i < channel; ++i) { meter[i] = new Meter(this, Meter::DBMeter, Qt::Vertical, MusEGlobal::config.minMeter, volSliderMax); meter[i]->setRefreshRate(MusEGlobal::config.guiRefresh); if (meter[i]->vu3d() || props.meterWidthPerChannel()) { meter[i]->setFixedWidth(props.meterWidth()); } else { meter[i]->setFixedWidth(props.meterWidth() / channel); } meter[i]->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); meter[i]->setRange(MusEGlobal::config.minMeter, volSliderMax); meter[i]->setPrimaryColor(MusEGlobal::config.audioMeterPrimaryColor, MusEGlobal::config.meterBackgroundColor); meter[i]->setFrame(props.meterFrame(), props.meterFrameColor()); connect(meter[i], SIGNAL(mousePress()), this, SLOT(resetClipper())); _meterLayout->hlayout()->addWidget(meter[i], Qt::AlignHCenter); } sl = new DoubleLabel(0.0, MusEGlobal::config.minSlider, volSliderMax, this); sl->setObjectName("VolumeEditAudio"); sl->setContentsMargins(0, 0, 0, 0); sl->setTextMargins(0, 0, 0, 0); sl->setFocusPolicy(Qt::WheelFocus); sl->setMouseTracking(true); sl->setFrame(true); sl->setAlignment(Qt::AlignCenter); //sl->setAutoFillBackground(true); sl->setSlider(slider); //sl->setBackgroundRole(QPalette::Mid); sl->setToolTip(tr("Volume/Gain")); sl->setSuffix("dB"); sl->setSpecialText(QString('-') + QChar(0x221e) + QChar(' ') + "dB"); sl->setOff(MusEGlobal::config.minSlider); sl->setPrecision(volSliderPrec); sl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); sl->setValue(track_vol); // sl->setEnableStyleHack(MusEGlobal::config.lineEditStyleHack); // If smart focus is on redirect strip focus to slider label. //if(MusEGlobal::config.smartFocus) setFocusProxy(sl); connect(sl, SIGNAL(valueChanged(double,int)), SLOT(volLabelChanged(double))); connect(slider, SIGNAL(valueChanged(double,int)), sl, SLOT(setValue(double))); connect(slider, SIGNAL(valueChanged(double,int,int)), SLOT(volumeChanged(double,int,int))); connect(slider, SIGNAL(sliderMoved(double,int,bool)), SLOT(volumeMoved(double,int,bool))); connect(slider, SIGNAL(sliderPressed(double, int)), SLOT(volumePressed(double,int))); connect(slider, SIGNAL(sliderReleased(double, int)), SLOT(volumeReleased(double,int))); connect(slider, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(volumeRightClicked(QPoint))); sliderGrid->addWidget(sl, 2, 0, 1, 2, Qt::AlignHCenter); // sliderGrid->setColumnStretch(0, slider->sizeHint().width()); // sliderGrid->setColumnStretch(1, _meterLayout->sizeHint().width()); QHBoxLayout *sliderHLayout = new QHBoxLayout(); sliderHLayout->setContentsMargins(0,0,0,0); sliderHLayout->setSpacing(0); sliderHLayout->addStretch(); sliderHLayout->addLayout(sliderGrid); sliderHLayout->addStretch(); sliderHLayout->setAlignment(Qt::AlignHCenter); QFrame *sliderMeterFrame = new QFrame; sliderMeterFrame->setObjectName("SliderMeterFrameAudio"); sliderMeterFrame->setLayout(sliderHLayout); sliderMeterFrame->setMinimumWidth(cMinStripWidth); QHBoxLayout *sliderMeterLayout = new QHBoxLayout(); sliderMeterLayout->setContentsMargins(1,2,1,2); sliderMeterLayout->addWidget(sliderMeterFrame); addGridLayout(sliderMeterLayout, _sliderMeterPos); //--------------------------------------------------- // pan, balance //--------------------------------------------------- _lowerRack = new AudioComponentRack(at, aStripLowerRack, false); // FIXME For some reason StyledPanel has trouble, intermittent sometimes panel is drawn, sometimes not. //_lowerRack->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); _lowerRack->setFrameStyle(QFrame::Box | QFrame::Sunken); _lowerRack->setLineWidth(rackFrameWidth); _lowerRack->setMidLineWidth(0); // We do set a minimum height on this widget. Tested: Must be on fixed. Thankfully, it'll expand if more controls are added. _lowerRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); _lowerRack->setContentsMargins(rackFrameWidth, rackFrameWidth, rackFrameWidth, rackFrameWidth); _lowerRack->setFocusPolicy(Qt::NoFocus); addGridWidget(_lowerRack, _lowerRackPos); _upperRack->setEnabled(!at->off()); // _infoRack->setEnabled(!at->off()); _lowerRack->setEnabled(!at->off()); //--------------------------------------------------- // mute, solo, record //--------------------------------------------------- QGridLayout *bottomLayout = new QGridLayout; bottomLayout->setContentsMargins(1,1,1,2); bottomLayout->setSpacing(1); if (track && track->canRecordMonitor()) { // _recMonitor = new IconButton(monitorOnSVGIcon, monitorOffSVGIcon, nullptr, nullptr, false, true); _recMonitor = new QPushButton; _recMonitor->setIcon(*monitorStateSVGIcon); _recMonitor->setFocusPolicy(Qt::NoFocus); _recMonitor->setCheckable(true); _recMonitor->setToolTip(tr("Input monitor")); _recMonitor->setWhatsThis(tr("Pass input through to output")); _recMonitor->setStatusTip(tr("Input monitor: Pass input through to output.")); _recMonitor->setChecked(at->recMonitor()); connect(_recMonitor, SIGNAL(toggled(bool)), SLOT(recMonitorToggled(bool))); bottomLayout->addWidget(_recMonitor, 0, 0, 1, 1); } else { QPushButton *recMonitorx = new QPushButton(this); recMonitorx->setIcon(*monitorOnSVGIcon); recMonitorx->setEnabled(false); bottomLayout->addWidget(recMonitorx, 0, 0, 1, 1); } if (track->canRecord()) { // record = new IconButton(recArmOnSVGIcon, recArmOffSVGIcon, nullptr, nullptr, false, true); record = new QPushButton(this); if (type == MusECore::Track::AUDIO_OUTPUT) record->setIcon(*downmixStateSVGIcon); else record->setIcon(*recArmStateSVGIcon); record->setFocusPolicy(Qt::NoFocus); record->setCheckable(true); if (type == MusECore::Track::AUDIO_OUTPUT) record->setToolTip(tr("Record downmix to a file...")); else record->setToolTip(tr("Record arm")); record->setChecked(at->recordFlag()); connect(record, SIGNAL(toggled(bool)), SLOT(recordToggled(bool))); bottomLayout->addWidget(record, 0, 1, 1, 1); } else { QPushButton *recordx = new QPushButton(this); recordx->setIcon(*recArmOnSVGIcon); recordx->setFocusPolicy(Qt::NoFocus); recordx->setEnabled(false); bottomLayout->addWidget(recordx, 0, 1, 1, 1); } // mute = new IconButton(muteOnSVGIcon, muteOffSVGIcon, muteAndProxyOnSVGIcon, muteProxyOnSVGIcon, false, true); mute = new QPushButton(this); mute->setIcon(*muteStateSVGIcon); mute->setFocusPolicy(Qt::NoFocus); mute->setCheckable(true); mute->setToolTip(tr("Mute or proxy mute")); mute->setStatusTip(tr("Mute or proxy mute. Connected tracks are 'phantom' muted.")); mute->setChecked(at->mute()); updateMuteIcon(); connect(mute, SIGNAL(toggled(bool)), SLOT(muteToggled(bool))); bottomLayout->addWidget(mute, 1, 0, 1, 1); // solo = new IconButton(soloOnSVGIcon, soloOffSVGIcon, soloAndProxyOnSVGIcon, soloProxyOnSVGIcon, false, true); solo = new QPushButton(this); solo->setIcon(*soloStateSVGIcon); solo->setObjectName("SoloButton"); solo->setToolTip(tr("Solo or proxy solo")); solo->setStatusTip(tr("Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help.")); solo->setFocusPolicy(Qt::NoFocus); solo->setCheckable(true); if (at->internalSolo()) solo->setIcon(*soloAndProxyOnSVGIcon); // solo->setIconSetB(at->internalSolo()); solo->setChecked(at->solo()); connect(solo, SIGNAL(toggled(bool)), SLOT(soloToggled(bool))); bottomLayout->addWidget(solo, 1, 1, 1, 1); // off = new IconButton(trackOffSVGIcon, trackOnSVGIcon, nullptr, nullptr, false, true); off = new QPushButton(this); off->setObjectName("TrackOffButton"); off->setIcon(*trackOnSVGIcon); off->setFocusPolicy(Qt::NoFocus); off->setCheckable(true); off->setToolTip(tr("Track off")); off->setChecked(at->off()); connect(off, SIGNAL(toggled(bool)), SLOT(offToggled(bool))); bottomLayout->addWidget(off, 3, 0, 1, 2); //--------------------------------------------------- // automation type //--------------------------------------------------- autoType = new CompactComboBox(); autoType->setObjectName("AudioAutoType"); autoType->setContentsMargins(0, 0, 0, 0); autoType->setFocusPolicy(Qt::NoFocus); autoType->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); //autoType->setAutoFillBackground(true); autoType->addAction(tr("Auto off"), MusECore::AUTO_OFF); autoType->addAction(tr("Read"), MusECore::AUTO_READ); autoType->addAction(tr("Touch"), MusECore::AUTO_TOUCH); autoType->addAction(tr("Write"), MusECore::AUTO_WRITE); autoType->setCurrentItem(at->automationType()); autoType->ensurePolished(); colorNameButton = autoType->palette().color(QPalette::Button).name(); colorAutoType(); autoType->setToolTip(tr("Automation type")); autoType->setStatusTip(tr("Automation type: Off, Read, Touch or Write. Press F1 for help.")); connect(autoType, SIGNAL(activated(int)), SLOT(setAutomationType(int))); bottomLayout->addWidget(autoType, 2, 0, 1, 2); addGridLayout(bottomLayout, _bottomPos); grid->setColumnStretch(2, 10); off->blockSignals(true); updateOffState(); // init state off->blockSignals(false); // Now build the strip components. buildStrip(); // Now set up all tabbing on the strip. // Don't bother if the strip is part of the mixer (not embedded), // the non-embedding parent (mixer) should set up all the tabs and make this call. if(isEmbedded) setupComponentTabbing(); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); updateRouteButtons(); connect(_upperRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); connect(_upperRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); connect(_upperRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); connect(_upperRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); // connect(_infoRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); // connect(_infoRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); // connect(_infoRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); // connect(_infoRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); connect(_lowerRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); connect(_lowerRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); connect(_lowerRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); connect(_lowerRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); } void AudioStrip::setStripStyle() { // Set the whole strip's font, except for the label. // May be good to keep this. In the midi strip without it the upper rack is too tall at first. So avoid trouble. setFont(MusEGlobal::config.fonts[1]); int iconSize = MusEGlobal::config.fonts[1].pointSize() * 2; setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1]) + "#Strip > QAbstractButton { padding: 0px; qproperty-iconSize:" + QString::number(iconSize) + "px; }" + "#Strip #TrackOffButton { qproperty-iconSize:" + QString::number(iconSize - 2) + "px; }"); } void AudioStrip::colorAutoType() { if (track->automationType() == MusECore::AUTO_TOUCH || track->automationType() == MusECore::AUTO_WRITE) { autoType->setStyleSheet("QToolButton { background: rgb(150, 0, 0); }"); } else if (track->automationType() == MusECore::AUTO_READ) { autoType->setStyleSheet("QToolButton { background: rgb(0, 100, 50); }"); } else { autoType->setStyleSheet("QToolButton { background:" + colorNameButton + "; }"); } } //--------------------------------------------------- // buildStrip // Destroy and rebuild strip components. //--------------------------------------------------- void AudioStrip::buildStrip() { // Destroys all components and clears the component list. // _infoRack->clearDelete(); _upperRack->clearDelete(); _lowerRack->clearDelete(); MusECore::AudioTrack* at = static_cast(track); //--------------------------------------------------- // Upper rack //--------------------------------------------------- // Gain... if(_preferKnobs) { CompactKnobComponentDescriptor gain_desc ( ComponentRack::propertyComponent, "MixerStripAudioGain", AudioComponentRack::aStripGainProperty ); _upperRack->newComponent(&gain_desc); } else { CompactSliderComponentDescriptor gain_desc ( ComponentRack::propertyComponent, "MixerStripAudioGain", AudioComponentRack::aStripGainProperty ); _upperRack->newComponent(&gain_desc); } // Aux sends... int auxsSize = MusEGlobal::song->auxs()->size(); if(at->hasAuxSend()) { for (int idx = 0; idx < auxsSize; ++idx) { if(_preferKnobs) { CompactKnobComponentDescriptor aux_desc ( AudioComponentRack::aStripAuxComponent, "MixerStripAudioAux", idx ); _upperRack->newComponent(&aux_desc); } else { CompactSliderComponentDescriptor aux_desc ( AudioComponentRack::aStripAuxComponent, "MixerStripAudioAux", idx ); _upperRack->newComponent(&aux_desc); } } } else { ///if (auxsSize) //layout->addSpacing((STRIP_WIDTH/2 + 2) * auxsSize); ///grid->addSpacing((STRIP_WIDTH/2 + 2) * auxsSize); // ??? } // Keep this if dynamic layout (flip to right side) is desired. _upperRack->addStretch(); // noop updateRackSizes(true, false); //--------------------------------------------------- // Lower rack //--------------------------------------------------- // Pan... if(_preferKnobs) { CompactKnobComponentDescriptor pan_desc ( ComponentRack::controllerComponent, "MixerStripAudioPan", MusECore::AC_PAN ); _lowerRack->newComponent(&pan_desc); } else { CompactSliderComponentDescriptor pan_desc ( ComponentRack::controllerComponent, "MixerStripAudioPan", MusECore::AC_PAN ); _lowerRack->newComponent(&pan_desc); } // Keep this if dynamic layout (flip to right side) is desired. _lowerRack->addStretch(); // noop updateRackSizes(false, true); } QWidget* AudioStrip::setupComponentTabbing(QWidget* previousWidget) { QWidget* prev = previousWidget; prev = _upperRack->setupComponentTabbing(prev); // prev = _infoRack->setupComponentTabbing(prev); if(sl) { if(prev) QWidget::setTabOrder(prev, sl); prev = sl; } prev = _lowerRack->setupComponentTabbing(prev); return prev; } void AudioStrip::setClipperTooltip(int ch) { QString clip_tt; switch(ch) { case 0: clip_tt = tr("L meter peak/clip"); break; case 1: clip_tt = tr("R meter peak/clip"); break; default: clip_tt = locale().toString(ch); break; } _clipperLabel[ch]->setToolTip(clip_tt); } //--------------------------------------------------------- // iRoutePressed //--------------------------------------------------------- void AudioStrip::iRoutePressed() { RoutePopupMenu* pup = new RoutePopupMenu(nullptr, false, _broadcastChanges); pup->exec(QCursor::pos(), track, false); delete pup; iR->setDown(false); } //--------------------------------------------------------- // oRoutePressed //--------------------------------------------------------- void AudioStrip::oRoutePressed() { RoutePopupMenu* pup = new RoutePopupMenu(nullptr, true, _broadcastChanges); pup->exec(QCursor::pos(), track, true); delete pup; oR->setDown(false); } void AudioStrip::incVolume(int increaseValue) { if(!track || track->isMidiTrack()) return; const int id = MusECore::AC_VOLUME; MusECore::AudioTrack* at = static_cast(track); // Get the slider's current value. const double prev_val = slider->value(); // Increment the slider. Do not allow signalling. slider->blockSignals(true); slider->incValue(increaseValue); slider->blockSignals(false); // Now grab the control's new value. const double new_val = slider->value(); sl->blockSignals(true); sl->setValue(new_val); sl->blockSignals(false); double d_new_val = new_val; if (d_new_val <= MusEGlobal::config.minSlider) d_new_val = 0.0; else d_new_val = muse_db2val(d_new_val); volume = d_new_val; // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. // ScrDirect mode is one-time only on press with modifier. // if(scrollMode != SliderBase::ScrDirect) at->recordAutomation(id, d_new_val); at->setParam(id, d_new_val); // Schedules a timed control change. at->enableController(id, false); componentIncremented(ComponentRack::controllerComponent, prev_val, new_val, false, id, Slider::ScrNone); } void AudioStrip::incPan(int increaseValue) { if(!track || track->isMidiTrack()) return; const int id = MusECore::AC_PAN; MusECore::AudioTrack* at = static_cast(track); ComponentRack* rack = nullptr; ComponentWidget* cw = nullptr; // Be sure to search all racks. Even if pan is in multiple racks, only one hit is // needed since after the value is set, the other pan controls will be updated too. if((cw = _upperRack->findComponent(ComponentRack::controllerComponent, -1, id))) rack = _upperRack; // else if((cw = _infoRack->findComponent(ComponentRack::controllerComponent, -1, id))) // rack = _infoRack; else if((cw = _lowerRack->findComponent(ComponentRack::controllerComponent, -1, id))) rack = _lowerRack; if(!cw || !rack) return; // Get the component's current value. const double prev_val = rack->componentValue(*cw); // Now increment the component. Do not allow signalling. rack->incComponentValue(*cw, increaseValue, true); // Now grab its value. const double d_new_val = rack->componentValue(*cw); // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. // ScrDirect mode is one-time only on press with modifier. // if(scrollMode != SliderBase::ScrDirect) at->recordAutomation(id, d_new_val); at->setParam(id, d_new_val); // Schedules a timed control change. at->enableController(id, false); componentIncremented(ComponentRack::controllerComponent, prev_val, d_new_val, false, id, Slider::ScrNone); } } // namespace MusEGui muse-4.0.0/src/muse/mixer/astrip.h000066400000000000000000000276351404105311000170250ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: astrip.h,v 1.8.2.6 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ASTRIP_H__ #define __ASTRIP_H__ #include #include #include "type_defs.h" #include "strip.h" #include "meter.h" // Forward declarations: class QHBoxLayout; namespace MusECore { class AudioTrack; } namespace MusEGui { class DoubleLabel; class EffectRack; class Knob; class Slider; class CompactSlider; //class IconButton; class ClipperLabel; //--------------------------------------------------------- // AudioComponentRack //--------------------------------------------------------- class AudioComponentRack : public ComponentRack { Q_OBJECT public: // Type of component. enum AStripComponentType { aStripAuxComponent = userComponent }; // Possible widget types. //enum AStripComponentWidgetType { type = userComponentWidget }; // Possible component properties. enum AStripComponentProperties { aStripGainProperty = userComponentProperty }; protected: MusECore::AudioTrack* _track; bool _manageAuxs; // Creates a new component widget from the given desc. Called by newComponent(). // Connects known widget types' signals to slots. //virtual void newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before = ComponentWidget() ); // Scan and automatically remove missing / add new controllers. void scanControllerComponents(); // Scan and automatically remove missing / add new aux. void scanAuxComponents(); // Set component colours upon config changed. void setComponentColors(); protected slots: virtual void controllerChanged(double val, bool isOff, int id, int scrollMode); virtual void controllerMoved(double, int, bool); virtual void controllerPressed(double, int); virtual void controllerReleased(double, int); virtual void controllerRightClicked(QPoint, int); virtual void propertyChanged(double val, bool isOff, int id, int scrollMode); virtual void propertyMoved(double, int, bool); virtual void propertyPressed(double, int); virtual void propertyReleased(double, int); virtual void propertyRightClicked(QPoint, int); virtual void auxChanged(double val, bool isOff, int id, int scrollMode); virtual void auxMoved(double, int, bool); virtual void auxPressed(double, int); virtual void auxReleased(double, int); virtual void auxRightClicked(QPoint p, int); public slots: virtual void configChanged(); virtual void songChanged(MusECore::SongChangedStruct_t); public: AudioComponentRack(MusECore::AudioTrack* track, int id = -1, bool _manageAuxs = false, QWidget* parent = 0, Qt::WindowFlags f = Qt::Widget); MusECore::AudioTrack* track() { return _track; } // Adds a component to the layout and the list. Creates a new component using // the given desc values if the desc widget is not given. virtual void newComponent( ComponentDescriptor* desc, const ComponentWidget& before = ComponentWidget() ); // Updates all the components, for example updating the values of sliders. void updateComponents(); // Enable or disable all the aux components in this rack. void setAuxEnabled(bool enable); }; //--------------------------------------------------------- // AudioStripProperties //--------------------------------------------------------- class AudioStripProperties : QWidget { Q_OBJECT // Q_PROPERTY(QColor bgColor READ bgColor WRITE setBgColor) Q_PROPERTY(int sliderRadius READ sliderRadius WRITE setSliderRadius) Q_PROPERTY(int sliderRadiusHandle READ sliderRadiusHandle WRITE setSliderRadiusHandle) Q_PROPERTY(int sliderHandleHeight READ sliderHandleHeight WRITE setSliderHandleHeight) Q_PROPERTY(int sliderHandleWidth READ sliderHandleWidth WRITE setSliderHandleWidth) Q_PROPERTY(int sliderGrooveWidth READ sliderGrooveWidth WRITE setSliderGrooveWidth) Q_PROPERTY(bool sliderFillOver READ sliderFillOver WRITE setSliderFillOver) Q_PROPERTY(bool sliderUseGradient READ sliderUseGradient WRITE setSliderUseGradient) Q_PROPERTY(bool sliderBackbone READ sliderBackbone WRITE setSliderBackbone) Q_PROPERTY(bool sliderFillHandle READ sliderFillHandle WRITE setSliderFillHandle) Q_PROPERTY(int sliderScalePos READ sliderScalePos WRITE setSliderScalePos) Q_PROPERTY(bool sliderFrame READ sliderFrame WRITE setSliderFrame) Q_PROPERTY(QColor sliderFrameColor READ sliderFrameColor WRITE setSliderFrameColor) Q_PROPERTY(int meterWidth READ meterWidth WRITE setMeterWidth) Q_PROPERTY(bool meterWidthPerChannel READ meterWidthPerChannel WRITE setMeterWidthPerChannel) Q_PROPERTY(int meterSpacing READ meterSpacing WRITE setMeterSpacing) Q_PROPERTY(bool meterFrame READ meterFrame WRITE setMeterFrame) Q_PROPERTY(QColor meterFrameColor READ meterFrameColor WRITE setMeterFrameColor) // QColor _bgColor; int _sliderRadius; int _sliderRadiusHandle; int _sliderHandleHeight; int _sliderHandleWidth; int _sliderGrooveWidth; bool _sliderFillOver; bool _sliderUseGradient; bool _sliderBackbone; bool _sliderFillHandle; int _sliderScalePos; bool _sliderFrame; QColor _sliderFrameColor; int _meterWidth; bool _meterWidthPerChannel; int _meterSpacing; bool _meterFrame; QColor _meterFrameColor; public: AudioStripProperties(); // QColor bgColor() const { return _bgColor; } // void setBgColor(const QColor c) { _bgColor = c; } int sliderRadius() const { return _sliderRadius; } void setSliderRadius(int radius) { _sliderRadius = radius; } int sliderRadiusHandle() const { return _sliderRadiusHandle; } void setSliderRadiusHandle(int radiusHandle) { _sliderRadiusHandle = radiusHandle; } int sliderHandleHeight() const { return _sliderHandleHeight; } void setSliderHandleHeight(int h) { _sliderHandleHeight = h; } int sliderHandleWidth() const { return _sliderHandleWidth; } void setSliderHandleWidth(int w) { _sliderHandleWidth = w; } int sliderGrooveWidth() const { return _sliderGrooveWidth; } void setSliderGrooveWidth(int w) { _sliderGrooveWidth = w; } bool sliderFillOver() const { return _sliderFillOver; } void setSliderFillOver(bool b) { _sliderFillOver = b; } bool sliderUseGradient() const { return _sliderUseGradient; } void setSliderUseGradient(bool b) { _sliderUseGradient = b; } bool sliderBackbone() const { return _sliderBackbone; } void setSliderBackbone(bool b) { _sliderBackbone = b; } bool sliderFillHandle() const { return _sliderFillHandle; } void setSliderFillHandle(bool b) { _sliderFillHandle = b; } int sliderScalePos() const { return _sliderScalePos; } void setSliderScalePos(int p) { _sliderScalePos = p; } bool sliderFrame() const { return _sliderFrame; } void setSliderFrame(bool b) { _sliderFrame = b; } QColor sliderFrameColor() const { return _sliderFrameColor; } void setSliderFrameColor(const QColor c) { _sliderFrameColor = c; } int meterWidth() const { return _meterWidth; } void setMeterWidth(int w) { _meterWidth = w; } bool meterWidthPerChannel() const { return _meterWidthPerChannel; } void setMeterWidthPerChannel(bool b) { _meterWidthPerChannel = b; } int meterSpacing() const { return _meterSpacing; } void setMeterSpacing(int s) { _meterSpacing = s; } bool meterFrame() const { return _meterFrame; } void setMeterFrame(bool b) { _meterFrame = b; } QColor meterFrameColor() const { return _meterFrameColor; } void setMeterFrameColor(const QColor c) { _meterFrameColor = c; } }; //--------------------------------------------------------- // AudioStrip //--------------------------------------------------------- class AudioStrip : public Strip { Q_OBJECT public: // ID numbers for each rack in this strip. enum AStripRacks { aStripUpperRack = 0, aStripInfoRack = 1, aStripLowerRack = 2 }; private: GridPosStruct _upperRackPos; GridPosStruct _effectRackPos; GridPosStruct _stereoPrePos; GridPosStruct _gainToolPos; GridPosStruct _sliderMeterPos; GridPosStruct _lowerRackPos; GridPosStruct _routePos; GridPosStruct _bottomPos; AudioStripProperties props; int channel; MusEGui::Slider* slider; MusEGui::DoubleLabel* sl; EffectRack* rack; MeterLayout* _meterLayout; AudioComponentRack* _upperRack; AudioComponentRack* _lowerRack; // AudioComponentRack* _infoRack; // Whether the layout is in mode A (normal, racks on left) or B (racks on right). bool _isExpanded; // Current local state of knobs versus sliders preference global setting. bool _preferKnobs; QPushButton* _recMonitor; QPushButton* stereo; QPushButton* pre; QPushButton* off; double volume; bool _volPressed; QString colorNameButton; ClipperLabel* _clipperLabel[MusECore::MAX_CHANNELS]; QHBoxLayout* _clipperLayout; void setClipperTooltip(int ch); void colorAutoType(); void updateOffState(); void updateVolume(); void updateChannels(); void updateRackSizes(bool upper, bool lower); void setStripStyle(); private slots: void recMonitorToggled(bool); void stereoToggled(bool); void preToggled(bool); void offToggled(bool); void iRoutePressed(); void oRoutePressed(); void volumeMoved(double val, int id, bool shift_pressed); void volumeChanged(double val, int id, int scrollMode); void volumePressed(double val, int id); void volumeReleased(double val, int id); void volLabelChanged(double); void volumeRightClicked(QPoint); void resetClipper(); protected slots: virtual void heartBeat(); public slots: virtual void configChanged(); virtual void songChanged(MusECore::SongChangedStruct_t); void incVolume(int v); void incPan(int v); public: AudioStrip(QWidget* parent, MusECore::AudioTrack*, bool hasHandle = false, bool isEmbedded = true); virtual ~AudioStrip(); static const double volSliderStep; static const double volSliderMax; static const int volSliderPrec; static const double auxSliderStep; static const double auxSliderMax; static const int auxSliderPrec; static const double gainSliderStep; static const double gainSliderMin; static const double gainSliderMax; static const int gainSliderPrec; static const int xMarginHorSlider; static const int yMarginHorSlider; static const int upperRackSpacerHeight; static const int rackFrameWidth; // Destroy and rebuild strip components. virtual void buildStrip(); // Sets up tabbing for the entire strip. // Accepts a previousWidget which can be null and returns the last widget in the strip, // which allows chaining other widgets. virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mixer/auxknob.cpp000066400000000000000000000036001404105311000175070ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: auxknob.cpp,v 1.7 2004/07/11 16:26:46 wschweer Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include "auxknob.h" #include "gconfig.h" namespace MusEGui { //--------------------------------------------------------- // Aux //--------------------------------------------------------- AuxKnob::AuxKnob(QWidget* parent, int i) : MusEGui::Knob(parent, "aux") { idx = i; setRange(MusEGlobal::config.minSlider-0.1, 10.0); connect(this, SIGNAL(valueChanged(double,int)), SLOT(valueChanged(double))); } //--------------------------------------------------------- // panChanged //--------------------------------------------------------- void AuxKnob::valueChanged(double val) { double vol; if (val <= MusEGlobal::config.minSlider) { vol = 0.0; val -= 1.0; // display special value "off" } else vol = muse_db2val(val); emit auxChanged(idx, vol); } } // namespace MusEGui muse-4.0.0/src/muse/mixer/auxknob.h000066400000000000000000000027001404105311000171540ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: auxknob.h,v 1.3 2003/11/08 15:10:18 wschweer Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __AUXKNOB_H__ #define __AUXKNOB_H__ #include "knob.h" namespace MusEGui { //--------------------------------------------------------- // AuxKnob //--------------------------------------------------------- class AuxKnob : public Knob { Q_OBJECT int idx; private slots: void valueChanged(double v); signals: void auxChanged(int, double); public: AuxKnob(QWidget* parent, int idx); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mixer/mstrip.cpp000066400000000000000000003061261404105311000173670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mstrip.cpp,v 1.9.2.13 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011 - 2017 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 #include #include #include #include #include "app.h" #include "midi_consts.h" #include "midictrl.h" #include "ctrl.h" #include "mstrip.h" #include "midiport.h" #include "globals.h" #include "audio.h" #include "song.h" #include "slider.h" #include "knob.h" #include "combobox.h" #include "track.h" #include "doublelabel.h" #include "rack.h" #include "amixer.h" #include "icons.h" #include "gconfig.h" #include "pixmap_button.h" #include "popupmenu.h" #include "routepopup.h" #include "minstrument.h" #include "midievent.h" #include "compact_knob.h" #include "compact_slider.h" #include "compact_patch_edit.h" #include "lcd_widgets.h" #include "elided_label.h" #include "utils.h" #include "muse_math.h" #include "operations.h" #include "synth.h" #ifdef LV2_SUPPORT #include "lv2host.h" #endif // For debugging output: Uncomment the fprintf section. #define DEBUG_MIDI_STRIP(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { const double MidiStrip::volSliderStepLin = 1.0; const double MidiStrip::volSliderStepDb = 0.5; const double MidiStrip::volSliderMaxDb = 0.0; const int MidiStrip::volSliderPrecDb = 1; const int MidiStrip::xMarginHorSlider = 1; const int MidiStrip::yMarginHorSlider = 1; const int MidiStrip::upperRackSpacerHeight = 2; const int MidiStrip::rackFrameWidth = 1; //--------------------------------------------------------- // MidiComponentRack //--------------------------------------------------------- MidiComponentRack::MidiComponentRack(MusECore::MidiTrack* track, int id, QWidget* parent, Qt::WindowFlags f) : ComponentRack(id, parent, f), _track(track) { } void MidiComponentRack::newComponent( ComponentDescriptor* desc, const ComponentWidget& before ) { int min = 0; int max = 0; int val = 0; bool hasOffMode = false; bool off = false; bool showval = MusEGlobal::config.showControlValues; switch(desc->_componentType) { case controllerComponent: { const int midiCtrlNum = desc->_index; const int chan = _track->outChannel(); const int port = _track->outPort(); if(chan < 0 || chan >= MusECore::MUSE_MIDI_CHANNELS || port < 0 || port >= MusECore::MIDI_PORTS) return; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiController* mc = mp->midiController(midiCtrlNum, chan); // Auto-create the controller if necessary. if(!mc) return; min = mc->minVal(); max = mc->maxVal(); if(midiCtrlNum == MusECore::CTRL_PROGRAM) { val = mp->hwCtrlState(chan, midiCtrlNum); if(val == MusECore::CTRL_VAL_UNKNOWN) { int lastv = mp->lastValidHWCtrlState(chan, midiCtrlNum); if(lastv == MusECore::CTRL_VAL_UNKNOWN) { if(mc->initVal() == MusECore::CTRL_VAL_UNKNOWN) val = 0; else val = mc->initVal(); } off = true; } } else { hasOffMode = true; val = mp->hwCtrlState(chan, midiCtrlNum); if(val == MusECore::CTRL_VAL_UNKNOWN) { int lastv = mp->lastValidHWCtrlState(chan, midiCtrlNum); if(lastv == MusECore::CTRL_VAL_UNKNOWN) { if(mc->initVal() == MusECore::CTRL_VAL_UNKNOWN) val = 0; else val = mc->initVal(); } else val = lastv - mc->bias(); off = true; } else { // Auto bias... val -= mc->bias(); } } if(desc->_label.isEmpty()) { QString ctlname = mc->name(); if(ctlname.isEmpty()) { switch(midiCtrlNum) { case MusECore::CTRL_PROGRAM: ctlname = tr("Pro"); break; case MusECore::CTRL_VARIATION_SEND: ctlname = tr("Var"); break; case MusECore::CTRL_REVERB_SEND: ctlname = tr("Rev"); break; case MusECore::CTRL_CHORUS_SEND: ctlname = tr("Cho"); break; case MusECore::CTRL_PANPOT: ctlname = tr("Pan"); break; default: ctlname = QString("#%1").arg(midiCtrlNum); break; } } desc->_label = ctlname; } if(desc->_toolTipText.isEmpty()) { QString ctlname = mc->name(); if(ctlname.isEmpty()) { switch(midiCtrlNum) { case MusECore::CTRL_PROGRAM: ctlname = tr("Program"); break; case MusECore::CTRL_VARIATION_SEND: ctlname = tr("VariationSend"); break; case MusECore::CTRL_REVERB_SEND: ctlname = tr("ReverbSend"); break; case MusECore::CTRL_CHORUS_SEND: ctlname = tr("ChorusSend"); break; case MusECore::CTRL_PANPOT: ctlname = tr("Pan/Balance"); break; default: ctlname = tr("Controller"); break; } } desc->_toolTipText = QString("%1 (# %2)\n%3").arg(ctlname).arg(midiCtrlNum).arg(tr("(Ctrl-double-click on/off)")); } if(!desc->_color.isValid()) { switch(midiCtrlNum) { case MusECore::CTRL_PANPOT: desc->_color = MusEGlobal::config.panSliderColor; break; case MusECore::CTRL_PROGRAM: desc->_color = MusEGlobal::config.midiPatchReadoutColor; break; default: desc->_color = MusEGlobal::config.midiControllerSliderColor; break; } } } break; case propertyComponent: { switch(desc->_index) { case mStripInstrumentProperty: { const int port = _track->outPort(); if(port >= 0 && port < MusECore::MIDI_PORTS) { if(MusECore::MidiInstrument* minstr = MusEGlobal::midiPorts[_track->outPort()].instrument()) { //desc->_enabled = !minstr->isSynti(); if(desc->_label.isEmpty()) desc->_label = minstr->iname(); } else { desc->_enabled = false; if(desc->_label.isEmpty()) desc->_label = tr(""); } } if(desc->_toolTipText.isEmpty()) desc->_toolTipText = tr("Instrument"); } break; case mStripTranspProperty: { val = _track->transposition; min = -127; max = 127; if(desc->_label.isEmpty()) desc->_label = tr("Transpose"); if(desc->_toolTipText.isEmpty()) desc->_toolTipText = tr("Transpose notes up or down"); if(!desc->_color.isValid()) desc->_color = MusEGlobal::config.midiPropertySliderColor; } break; case mStripDelayProperty: { val = _track->delay; min = -1000; max = 1000; if(desc->_label.isEmpty()) desc->_label = tr("Delay"); if(desc->_toolTipText.isEmpty()) desc->_toolTipText = tr("Offset playback of notes before or after actual note"); if(!desc->_color.isValid()) desc->_color = MusEGlobal::config.midiPropertySliderColor; } break; case mStripLenProperty: { val = _track->len; min = 25; max = 200; if(desc->_label.isEmpty()) desc->_label = tr("Length"); if(desc->_toolTipText.isEmpty()) desc->_toolTipText = tr("Change note length in percent of actual length"); if(!desc->_color.isValid()) desc->_color = MusEGlobal::config.midiPropertySliderColor; } break; case mStripVeloProperty: { val = _track->velocity; min = -127; max = 127; if(desc->_label.isEmpty()) desc->_label = tr("Velocity"); if(desc->_toolTipText.isEmpty()) desc->_toolTipText = tr("

Add or substract velocity to notes" " on track.

Since" " the midi note range is 0-127 this
might mean that the" " notes do not reach
the combined velocity, note + " " Velocity.

"); if(!desc->_color.isValid()) desc->_color = MusEGlobal::config.midiPropertySliderColor; } break; case mStripComprProperty: { val = _track->compression; min = 25; max = 200; if(desc->_label.isEmpty()) desc->_label = tr("Compress"); if(desc->_toolTipText.isEmpty()) desc->_toolTipText = tr("Compress the notes velocity range, in percent of actual velocity"); if(!desc->_color.isValid()) desc->_color = MusEGlobal::config.midiPropertySliderColor; } break; } } break; } switch(desc->_widgetType) { case ElidedLabelComponentWidget: { ElidedLabelComponentDescriptor* d = static_cast(desc); d->_color = MusEGlobal::config.midiInstrumentBorderColor; d->_bgColor = MusEGlobal::config.midiInstrumentBackgroundColor; d->_bgActiveColor = MusEGlobal::config.midiInstrumentBgActiveColor; d->_fontColor = MusEGlobal::config.midiInstrumentFontColor; d->_fontActiveColor = MusEGlobal::config.midiInstrumentFontActiveColor; // Adds a component. Creates a new component using the given desc values if the desc widget is not given. // Connects known widget types' signals to slots. newComponentWidget(d, before); } break; case CompactKnobComponentWidget: { CompactKnobComponentDescriptor* d = static_cast(desc); d->_min = min; d->_max = max; d->_precision = 0; d->_step = 1.0; d->_initVal = val; d->_hasOffMode = hasOffMode; d->_isOff = off; d->_showValue = showval; if(!d->_color.isValid()) d->_color = MusEGlobal::config.sliderBackgroundColor; // Adds a component. Creates a new component using the given desc values if the desc widget is not given. // Connects known widget types' signals to slots. newComponentWidget(d, before); } break; case CompactSliderComponentWidget: { CompactSliderComponentDescriptor* d = static_cast(desc); d->_min = min; d->_max = max; d->_precision = 0; d->_step = 1.0; d->_initVal = val; d->_hasOffMode = hasOffMode; d->_isOff = off; d->_showValue = showval; if(!d->_color.isValid()) // wrong color, but hopefully set before... d->_color = MusEGlobal::config.sliderBackgroundColor; if(!d->_barColor.isValid()) d->_barColor = MusEGlobal::config.sliderBarColor; if(!d->_slotColor.isValid()) d->_slotColor = MusEGlobal::config.sliderBackgroundColor; // Adds a component. Creates a new component using the given desc values if the desc widget is not given. // Connects known widget types' signals to slots. newComponentWidget(d, before); } break; case mStripCompactPatchEditComponentWidget: { CompactPatchEditComponentDescriptor* d = static_cast(desc); d->_initVal = val; d->_isOff = off; if(!d->_color.isValid()) d->_color = MusEGlobal::config.midiPatchReadoutColor; // Adds a component. Creates a new component using the given desc values if the desc widget is not given. // Connects known widget types' signals to slots. newComponentWidget(d, before); } break; } } void MidiComponentRack::newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before ) { switch(desc->_widgetType) { case mStripCompactPatchEditComponentWidget: { CompactPatchEditComponentDescriptor* d = static_cast(desc); if(!d->_compactPatchEdit) { CompactPatchEdit* control = new CompactPatchEdit(nullptr, d->_objName, CompactSlider::None); d->_compactPatchEdit = control; control->setId(d->_index); control->setValue(d->_initVal); // Don't allow anything here, it interferes with the CompactPatchEdit which sets it's own controls' tooltips. //control->setToolTip(d->_toolTipText); control->setEnabled(d->_enabled); control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); control->setContentsMargins(0, 0, 0, 0); if(d->_color.isValid()) control->setReadoutColor(d->_color); control->setBgColor(MusEGlobal::config.midiInstrumentBackgroundColor); control->setBgActiveColor(MusEGlobal::config.midiInstrumentBgActiveColor); control->setBorderColor(MusEGlobal::config.midiInstrumentBorderColor); // control->setBorderColorPatchEdit(MusEGlobal::config.midiInstrumentBgActiveColor); control->setFontColor(MusEGlobal::config.midiInstrumentFontColor); control->setFontActiveColor(MusEGlobal::config.midiInstrumentFontActiveColor); control->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); connect(d->_compactPatchEdit, SIGNAL(valueChanged(int,int)), SLOT(controllerChanged(int,int))); connect(d->_compactPatchEdit, SIGNAL(patchValueRightClicked(QPoint,int)), SLOT(controllerRightClicked(QPoint,int))); connect(d->_compactPatchEdit, SIGNAL(patchNameClicked(QPoint,int)), SLOT(patchEditNameClicked(QPoint,int))); connect(d->_compactPatchEdit, SIGNAL(patchNameRightClicked(QPoint,int)), SLOT(controllerRightClicked(QPoint,int))); } ComponentWidget cw = ComponentWidget( d->_compactPatchEdit, d->_widgetType, d->_componentType, d->_index ); addComponentWidget(cw, before); return; } break; } // If not handled above, let ancestor handle it. ComponentRack::newComponentWidget(desc, before); } void MidiComponentRack::scanControllerComponents() { const int chan = _track->outChannel(); const int port = _track->outPort(); if(chan < 0 || chan >= MusECore::MUSE_MIDI_CHANNELS || port < 0 || port >= MusECore::MIDI_PORTS) return; QString namestr; std::vector to_be_erased; for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { ComponentWidget& cw = *ic; if(!cw._widget) continue; switch(cw._componentType) { case controllerComponent: { MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiCtrlValListList* mcvll = mp->controller(); MusECore::ciMidiCtrlValList imcvll = mcvll->find(chan, cw._index); if(imcvll == mcvll->end()) to_be_erased.push_back(ic); else { // While we are here, let's update the name of the control, in case the instrument changed. switch(cw._widgetType) { case CompactKnobComponentWidget: case CompactSliderComponentWidget: { // false = do not create the controller if not found. MusECore::MidiController* mc = mp->midiController(cw._index, chan, false); if(mc) setComponentText(cw, mc->name()); } break; } } } break; } } for(std::vector::iterator i = to_be_erased.begin(); i != to_be_erased.end(); ++i) { iComponentWidget icw = *i; ComponentWidget& cw = *icw; DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::scanControllerComponents: deleting controller component index:%d\n", cw._index); if(cw._widget) cw._widget->deleteLater(); _components.erase(icw); } } void MidiComponentRack::updateComponents() { for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { ComponentWidget& cw = *ic; if(!cw._widget) continue; switch(cw._componentType) { case controllerComponent: { // Inhibit the controller stream if control is currently pressed. // Note _pressed operates differently than simply checking if the control is pressed! if(cw._pressed) continue; const int channel = _track->outChannel(); const int port = _track->outPort(); if(channel < 0 || channel >= MusECore::MUSE_MIDI_CHANNELS || port < 0 || port >= MusECore::MIDI_PORTS) continue; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiCtrlValListList* mcvll = mp->controller(); MusECore::ciMidiCtrlValList imcvl = mcvll->find(channel, cw._index); const bool enable = imcvl != mcvll->end() && !_track->off(); if(cw._widget->isEnabled() != enable) cw._widget->setEnabled(enable); if(enable) { MusECore::MidiCtrlValList* mcvl = imcvl->second; switch(cw._index) { case MusECore::CTRL_PROGRAM: { switch(cw._widgetType) { case mStripCompactPatchEditComponentWidget: { // Special for new LCD patch edit control: Need to give both current and last values. // Keeping a local last value with the control won't work. CompactPatchEdit* control = static_cast(cw._widget); const int hwVal = mcvl->hwVal(); control->blockSignals(true); control->setLastValidValue(mcvl->lastValidHWVal()); control->setLastValidBytes(mcvl->lastValidByte2(), mcvl->lastValidByte1(), mcvl->lastValidByte0()); control->setValue(hwVal); control->blockSignals(false); if(hwVal == MusECore::CTRL_VAL_UNKNOWN) { control->setPatchNameOff(true); const QString patchName(tr("")); if(control->patchName() != patchName) control->setPatchName(patchName); } else { // Try to avoid calling MidiInstrument::getPatchName too often. // if(_heartBeatCounter == 0) { control->setPatchNameOff(false); MusECore::MidiInstrument* instr = mp->instrument(); QString patchName(instr->getPatchName(channel, hwVal, _track->isDrumTrack(), true)); // Include default. if(patchName.isEmpty()) patchName = QString("???"); if(control->patchName() != patchName) control->setPatchName(patchName); } } } break; } } break; default: { switch(cw._widgetType) { case CompactKnobComponentWidget: { CompactKnob* control = static_cast(cw._widget); int hwVal = mcvl->hwVal(); int min = 0; int max = 127; int bias = 0; int initval = 0; MusECore::MidiController* mc = mp->midiController(cw._index, channel, false); if(mc) { bias = mc->bias(); min = mc->minVal(); max = mc->maxVal(); initval = mc->initVal(); if(initval == MusECore::CTRL_VAL_UNKNOWN) initval = 0; } const double dmin = (double)min; const double dmax = (double)max; const double c_dmin = control->minValue(); const double c_dmax = control->maxValue(); if(c_dmin != min && c_dmax != max) { control->blockSignals(true); control->setRange(dmin, dmax, 1.0); control->blockSignals(false); } else if(c_dmin != min) { control->blockSignals(true); control->setMinValue(min); control->blockSignals(false); } else if(c_dmax != max) { control->blockSignals(true); control->setMaxValue(max); control->blockSignals(false); } if(hwVal == MusECore::CTRL_VAL_UNKNOWN) { hwVal = mcvl->lastValidHWVal(); if(hwVal == MusECore::CTRL_VAL_UNKNOWN) { hwVal = initval; if(!control->isOff() || hwVal != control->value()) { control->blockSignals(true); control->setValueState(hwVal, true); control->blockSignals(false); } } else { hwVal -= bias; if(!control->isOff() || hwVal != control->value()) { control->blockSignals(true); control->setValueState(hwVal, true); control->blockSignals(false); } } } else { hwVal -= bias; if(control->isOff() || hwVal != control->value()) { control->blockSignals(true); control->setValueState(hwVal, false); control->blockSignals(false); } } } break; case CompactSliderComponentWidget: { CompactSlider* control = static_cast(cw._widget); int hwVal = mcvl->hwVal(); int min = 0; int max = 127; int bias = 0; int initval = 0; MusECore::MidiController* mc = mp->midiController(cw._index, channel, false); if(mc) { bias = mc->bias(); min = mc->minVal(); max = mc->maxVal(); initval = mc->initVal(); if(initval == MusECore::CTRL_VAL_UNKNOWN) initval = 0; } const double dmin = (double)min; const double dmax = (double)max; const double c_dmin = control->minValue(); const double c_dmax = control->maxValue(); if(c_dmin != min && c_dmax != max) { control->blockSignals(true); control->setRange(dmin, dmax, 1.0); control->blockSignals(false); } else if(c_dmin != min) { control->blockSignals(true); control->setMinValue(min); control->blockSignals(false); } else if(c_dmax != max) { control->blockSignals(true); control->setMaxValue(max); control->blockSignals(false); } if(hwVal == MusECore::CTRL_VAL_UNKNOWN) { hwVal = mcvl->lastValidHWVal(); if(hwVal == MusECore::CTRL_VAL_UNKNOWN) { hwVal = initval; if(!control->isOff() || hwVal != control->value()) { control->blockSignals(true); control->setValueState(hwVal, true); control->blockSignals(false); } } else { hwVal -= bias; if(!control->isOff() || hwVal != control->value()) { control->blockSignals(true); control->setValueState(hwVal, true); control->blockSignals(false); } } } else { hwVal -= bias; if(control->isOff() || hwVal != control->value()) { control->blockSignals(true); control->setValueState(hwVal, false); control->blockSignals(false); } } } break; } } break; } } } break; case propertyComponent: { switch(cw._index) { case mStripInstrumentProperty: { const int port = _track->outPort(); if(port >= 0 && port < MusECore::MIDI_PORTS) { if(MusECore::MidiInstrument* minstr = MusEGlobal::midiPorts[_track->outPort()].instrument()) { //setComponentEnabled(cw, !minstr->isSynti()); setComponentText(cw, minstr->iname()); } else { setComponentEnabled(cw, false); setComponentText(cw, tr("")); } } } break; case mStripTranspProperty: setComponentValue(cw, _track->transposition); // Signals blocked. Redundant ignored. break; case mStripDelayProperty: setComponentValue(cw, _track->delay); // Signals blocked. Redundant ignored. break; case mStripLenProperty: setComponentValue(cw, _track->len); // Signals blocked. Redundant ignored. break; case mStripVeloProperty: setComponentValue(cw, _track->velocity); // Signals blocked. Redundant ignored. break; case mStripComprProperty: setComponentValue(cw, _track->compression); // Signals blocked. Redundant ignored. break; } } break; } } } //--------------------------------------------------------- // instrPopup //--------------------------------------------------------- void MidiComponentRack::instrPopup(QPoint p) { const int port = _track->outPort(); if(port < 0 || port >= MusECore::MIDI_PORTS) return; MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); //if(!instr) // return; PopupMenu* pup = new PopupMenu(false); //MusECore::MidiInstrument::populateInstrPopup(pup, port, false); MusECore::MidiInstrument::populateInstrPopup(pup, port, true); if(pup->actions().count() == 0) { delete pup; return; } QAction *act = pup->exec(p); if(!act) { delete pup; return; } const QString s = act->text(); const int actid = act->data().toInt(); delete pup; // Edit instrument if(actid == 100) { MusEGlobal::muse->startEditInstrument(instr && !instr->isSynti() ? instr->iname() : QString()); } else { for(MusECore::iMidiInstrument i = MusECore::midiInstruments.begin(); i != MusECore::midiInstruments.end(); ++i) { if((*i)->iname() == s) { MusEGlobal::audio->msgIdle(true); // Make it safe to edit structures MusEGlobal::midiPorts[port].changeInstrument(*i); MusEGlobal::audio->msgIdle(false); // Make sure device initializations are sent if necessary. MusEGlobal::audio->msgInitMidiDevices(false); // false = Don't force MusEGlobal::song->update(SC_MIDI_INSTRUMENT); break; } } } } //--------------------------------------------------------- // patchPopup //--------------------------------------------------------- void MidiComponentRack::patchPopup(QPoint p) { const int channel = _track->outChannel(); const int port = _track->outPort(); if(channel < 0 || channel >= MusECore::MUSE_MIDI_CHANNELS || port < 0 || port >= MusECore::MIDI_PORTS) return; MusECore::MidiInstrument* instr = MusEGlobal::midiPorts[port].instrument(); PopupMenu* pup = new PopupMenu(true); instr->populatePatchPopup(pup, channel, _track->isDrumTrack()); if(pup->actions().count() == 0) { delete pup; return; } connect(pup, SIGNAL(triggered(QAction*)), SLOT(patchPopupActivated(QAction*))); pup->exec(p); delete pup; } //--------------------------------------------------------- // patchPopupActivated //--------------------------------------------------------- void MidiComponentRack::patchPopupActivated(QAction* act) { if(!act) return; const int channel = _track->outChannel(); const int port = _track->outPort(); if(channel < 0 || channel >= MusECore::MUSE_MIDI_CHANNELS || port < 0 || port >= MusECore::MIDI_PORTS) return; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiInstrument* instr = mp->instrument(); if(!instr) return; if(act->data().type() == QVariant::Int || act->data().type() == QVariant::UInt) { bool ok; int rv = act->data().toInt(&ok); if(ok && rv != -1) { // If the chosen patch's number is don't care (0xffffff), // then by golly since we "don't care" let's just set it to '-/-/1'. // 0xffffff cannot be a valid patch number... yet... if(rv == MusECore::CTRL_PROGRAM_VAL_DONT_CARE) rv = 0xffff00; MusECore::MidiPlayEvent ev(MusEGlobal::audio->curFrame(), port, channel, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, rv); mp->putEvent(ev); } } else if(instr->isSynti() && act->data().canConvert()) { #ifdef LV2_SUPPORT MusECore::SynthI *si = static_cast(instr); MusECore::Synth *s = si->synth(); //only for lv2 synths call applyPreset function. if(s && s->synthType() == MusECore::Synth::LV2_SYNTH) { MusECore::LV2SynthIF *sif = static_cast(si->sif()); //be pedantic about checks if(sif) { if(mp) { if(mp->hwCtrlState(channel, MusECore::CTRL_PROGRAM) != MusECore::CTRL_VAL_UNKNOWN) mp->putHwCtrlEvent(MusECore::MidiPlayEvent(MusEGlobal::audio->curFrame(), port, channel, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, MusECore::CTRL_VAL_UNKNOWN)); sif->applyPreset(act->data().value()); } } } #endif } } void MidiComponentRack::controllerChanged(int v, int id) { DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerChanged id:%d val:%d\n", id, val); // if (inHeartBeat) // return; int val = v; int port = _track->outPort(); int channel = _track->outChannel(); if(channel < 0 || channel >= MusECore::MUSE_MIDI_CHANNELS || port < 0 || port >= MusECore::MIDI_PORTS) { emit componentChanged(controllerComponent, val, false, id, 0); return; } MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiCtrlValListList* mcvll = mp->controller(); MusECore::ciMidiCtrlValList imcvl = mcvll->find(channel, id); if(imcvl == mcvll->end()) { emit componentChanged(controllerComponent, val, false, id, 0); return; } MusECore::MidiController* mc = mp->midiController(id, channel, false); if(mc) { int ival = val; //if(off || ival < mc->minVal() || ival > mc->maxVal()) if(ival < mc->minVal() || ival > mc->maxVal()) ival = MusECore::CTRL_VAL_UNKNOWN; if(ival != MusECore::CTRL_VAL_UNKNOWN) // Auto bias... ival += mc->bias(); MusECore::MidiPlayEvent ev(MusEGlobal::audio->curFrame(), port, channel, MusECore::ME_CONTROLLER, id, ival); mp->putEvent(ev); } emit componentChanged(controllerComponent, v, false, id, 0); } void MidiComponentRack::controllerChanged(double val, int id) { DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerChanged id:%d val:%.20f\n", id, val); controllerChanged(int(lrint(val)), id); } void MidiComponentRack::controllerChanged(double val, bool off, int id, int scrollMode) { DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerChanged id:%d val:%.20f scrollMode:%d\n", id, val, scrollMode); int port = _track->outPort(); int channel = _track->outChannel(); if(channel < 0 || channel >= MusECore::MUSE_MIDI_CHANNELS || port < 0 || port >= MusECore::MIDI_PORTS) { emit componentChanged(controllerComponent, val, off, id, scrollMode); return; } MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiCtrlValListList* mcvll = mp->controller(); MusECore::ciMidiCtrlValList imcvl = mcvll->find(channel, id); if(imcvl == mcvll->end()) { emit componentChanged(controllerComponent, val, off, id, scrollMode); return; } MusECore::MidiController* mc = mp->midiController(id, channel, false); if(mc) { int ival = lrint(val); if(off || ival < mc->minVal() || ival > mc->maxVal()) ival = MusECore::CTRL_VAL_UNKNOWN; if(ival != MusECore::CTRL_VAL_UNKNOWN) // Auto bias... ival += mc->bias(); MusECore::MidiPlayEvent ev(MusEGlobal::audio->curFrame(), port, channel, MusECore::ME_CONTROLLER, id, ival); mp->putEvent(ev); } emit componentChanged(controllerComponent, val, off, id, scrollMode); } void MidiComponentRack::controllerMoved(double val, int id, bool shift_pressed) { DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerMoved id:%d val:%.20f\n", id, val); emit componentMoved(controllerComponent, val, id, shift_pressed); } void MidiComponentRack::controllerPressed(double val, int id) { DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerPressed id:%d\n", id); emit componentPressed(controllerComponent, val, id); } void MidiComponentRack::controllerReleased(double val, int id) { DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerReleased id:%d\n", id); emit componentReleased(controllerComponent, val, id); } void MidiComponentRack::controllerRightClicked(QPoint p, int id) { DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::controllerRightClicked id:%d\n", id); MusEGlobal::song->execMidiAutomationCtlPopup(_track, 0, p, id); // Do not give a parent here, otherwise accelerators are returned in text() ! } void MidiComponentRack::propertyChanged(double val, bool off, int id, int scrollMode) { DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::propertyChanged id:%d val:%.20f\n", id, val); const int ival = lrint(val); // FIXME ! This direct setting is probably not safe. Use a FIFO buffer or something. // A problem with using MidiPort::putEvent is that it does not appear to be safe // when directly setting the hwValues. switch(id) { case mStripTranspProperty: _track->transposition = ival; break; case mStripDelayProperty: _track->delay = ival; break; case mStripLenProperty: _track->len = ival; break; case mStripVeloProperty: _track->velocity = ival; break; case mStripComprProperty: _track->compression = ival; break; } emit componentChanged(propertyComponent, val, off, id, scrollMode); } void MidiComponentRack::propertyMoved(double val, int id, bool shift_pressed) { DEBUG_MIDI_STRIP(stderr, "MidiComponentRack::propertyMoved id:%d val:%.20f\n", id, val); emit componentMoved(propertyComponent, val, id, shift_pressed); } void MidiComponentRack::propertyPressed(double val, int id) { emit componentPressed(propertyComponent, val, id); } void MidiComponentRack::propertyReleased(double val, int id) { emit componentReleased(propertyComponent, val, id); } void MidiComponentRack::propertyRightClicked(QPoint, int) { } void MidiComponentRack::labelPropertyPressed(QPoint p, int id, Qt::MouseButtons /*buttons*/, Qt::KeyboardModifiers keys) { labelPropertyPressHandler(p, id, keys); } void MidiComponentRack::labelPropertyReleased(QPoint /*p*/, int /*id*/, Qt::MouseButtons /*buttons*/, Qt::KeyboardModifiers /*keys*/) { } void MidiComponentRack::labelPropertyReturnPressed(QPoint p, int id, Qt::KeyboardModifiers keys) { labelPropertyPressHandler(p, id, keys); } void MidiComponentRack::labelPropertyPressHandler(QPoint /*p*/, int id, Qt::KeyboardModifiers /*keys*/) { switch(id) { case mStripInstrumentProperty: { ciComponentWidget icw = _components.find(propertyComponent, -1, id); if(icw == _components.end()) return; const ComponentWidget& cw = *icw; if(!cw._widget) return; instrPopup(cw._widget->mapToGlobal(QPoint(10,5))); } break; } } void MidiComponentRack::patchEditNameClicked(QPoint /*p*/, int id) { ciComponentWidget icw = _components.find(controllerComponent, -1, id); if(icw == _components.end()) return; const ComponentWidget& cw = *icw; if(!cw._widget) return; patchPopup(cw._widget->mapToGlobal(QPoint(10,5))); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MidiComponentRack::songChanged(MusECore::SongChangedStruct_t flags) { // Scan controllers. if(flags & (SC_RACK | SC_MIDI_CONTROLLER_ADD | SC_MIDI_INSTRUMENT)) { scanControllerComponents(); } } //--------------------------------------------------------- // configChanged // Catch when label font, or configuration min slider and meter values change, or viewable tracks etc. //--------------------------------------------------------- void MidiComponentRack::configChanged() { // Handle font changes etc. ComponentRack::configChanged(); for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { const ComponentWidget& cw = *ic; if(!cw._widget) continue; // Whether to show values along with labels for certain controls. setComponentShowValue(cw, MusEGlobal::config.showControlValues); switch(cw._widgetType) { case mStripCompactPatchEditComponentWidget: { //CompactPatchEdit* w = static_cast(cw._widget); //w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); } break; case CompactKnobComponentWidget: { //CompactKnob* w = static_cast(cw._widget); //w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); } break; } } setComponentColors(); } //--------------------------------------------------------- // setComponentColors //--------------------------------------------------------- void MidiComponentRack::setComponentColors() { for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { const ComponentWidget& cw = *ic; if(!cw._widget) continue; QColor color = MusEGlobal::config.sliderBackgroundColor; switch(cw._componentType) { case controllerComponent: { switch(cw._index) { case MusECore::CTRL_PANPOT: color = MusEGlobal::config.panSliderColor; break; case MusECore::CTRL_PROGRAM: color = MusEGlobal::config.midiPatchReadoutColor; break; default: color = MusEGlobal::config.midiControllerSliderColor; break; } } break; case propertyComponent: { switch(cw._index) { case mStripInstrumentProperty: break; case mStripTranspProperty: case mStripDelayProperty: case mStripLenProperty: case mStripVeloProperty: case mStripComprProperty: color = MusEGlobal::config.midiPropertySliderColor; break; } } break; } switch(cw._widgetType) { case CompactKnobComponentWidget: { CompactKnob* w = static_cast(cw._widget); w->setFaceColor(color); } break; case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); w->setBorderColor(color); w->setThumbColor(color); w->setBarColor(MusEGlobal::config.sliderBarColor); w->setSlotColor(MusEGlobal::config.sliderBackgroundColor); } break; case mStripCompactPatchEditComponentWidget: { CompactPatchEdit* w = static_cast(cw._widget); w->setReadoutColor(color); w->setBgColor(MusEGlobal::config.midiInstrumentBackgroundColor); w->setBgActiveColor(MusEGlobal::config.midiInstrumentBgActiveColor); w->setBorderColor(MusEGlobal::config.midiInstrumentBorderColor); // w->setBorderColorPatchEdit(MusEGlobal::config.midiInstrumentBgActiveColor); w->setFontColor(MusEGlobal::config.midiInstrumentFontColor); w->setFontActiveColor(MusEGlobal::config.midiInstrumentFontActiveColor); } break; case ElidedLabelComponentWidget: { ElidedLabel* w = static_cast(cw._widget); w->setBgColor(MusEGlobal::config.midiInstrumentBackgroundColor); w->setBgActiveColor(MusEGlobal::config.midiInstrumentBgActiveColor); w->setBorderColor(MusEGlobal::config.midiInstrumentBorderColor); w->setFontColor(MusEGlobal::config.midiInstrumentFontColor); w->setFontActiveColor(MusEGlobal::config.midiInstrumentFontActiveColor); } break; } } } QWidget* MidiComponentRack::setupComponentTabbing(QWidget* previousWidget) { QWidget* prev = previousWidget; for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { const ComponentWidget& cw = *ic; if(cw._widget) { switch(cw._widgetType) { case mStripCompactPatchEditComponentWidget: { CompactPatchEdit* w = static_cast(cw._widget); prev = w->setupComponentTabbing(prev); } break; default: if(prev) QWidget::setTabOrder(prev, cw._widget); prev = cw._widget; break; } } } return prev; } //--------------------------------------------------------- // MidiStripProperties //--------------------------------------------------------- MidiStripProperties::MidiStripProperties() { _sliderRadius = 4; _sliderRadiusHandle = 2; _sliderHandleHeight = 16; _sliderHandleWidth = 16; _sliderFillOver = true; _sliderUseGradient = true; _sliderBackbone = false; _sliderFillHandle = true; _sliderGrooveWidth = 14; _sliderScalePos = Slider::InsideVertical; _sliderFrame = false; _sliderFrameColor = Qt::darkGray; _meterWidth = Strip::FIXED_METER_WIDTH; _meterSpacing = 2; _meterFrame = false; _meterFrameColor = Qt::darkGray; ensurePolished(); } //--------------------------------------------------------- // MidiStrip //--------------------------------------------------------- MidiStrip::MidiStrip(QWidget* parent, MusECore::MidiTrack* t, bool hasHandle, bool isEmbedded) : Strip(parent, t, hasHandle, isEmbedded) { inHeartBeat = true; _heartBeatCounter = 0; volume = MusECore::CTRL_VAL_UNKNOWN; _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; _preferMidiVolumeDb = MusEGlobal::config.preferMidiVolumeDb; slider = nullptr; sl = nullptr; off = nullptr; _recMonitor = nullptr; // Start the layout in mode A (normal, racks on left). _isExpanded = false; setStripStyle(); // Clear so the meters don't start off by showing stale values. t->setActivity(0); t->setLastActivity(0); _routePos = GridPosStruct(_curGridRow, 0, 1, 3); _upperStackTabPos = GridPosStruct(_curGridRow + 1, 0, 1, 3); _sliderMeterPos = GridPosStruct(_curGridRow + 2, 0, 1, 3); _lowerRackPos = GridPosStruct(_curGridRow + 3, 0, 1, 3); _bottomPos = GridPosStruct(_curGridRow + 4, 0, 1, 3); // _routePos = GridPosStruct(_curGridRow, 0, 1, 2); // _upperStackTabPos = GridPosStruct(_curGridRow + 1, 0, 1, 3); // _sliderMeterPos = GridPosStruct(_curGridRow + 2, 0, 1, 2); // _lowerRackPos = GridPosStruct(_curGridRow + 3, 0, 1, 3); // _bottomPos = GridPosStruct(_curGridRow + 4, 0, 1, 2); //--------------------------------------------------- // routing //--------------------------------------------------- QHBoxLayout *routeLayout = new QHBoxLayout; routeLayout->setContentsMargins(1,3,1,2); routeLayout->setSpacing(1); // iR = new IconButton(routingInputSVGIcon, routingInputSVGIcon, // routingInputUnconnectedSVGIcon, routingInputUnconnectedSVGIcon, false, true); iR = new QPushButton(this); iR->setIcon(*routingInputSVGIcon); iR->setObjectName("InputRouteButton"); iR->setStatusTip(tr("Input routing. Hold CTRL to keep menu open. Press F1 for help.")); iR->setFocusPolicy(Qt::NoFocus); iR->setToolTip(MusEGlobal::inputRoutingToolTipBase); connect(iR, SIGNAL(pressed()), SLOT(iRoutePressed())); routeLayout->addWidget(iR); // oR = new IconButton(routingOutputSVGIcon, routingOutputSVGIcon, // routingOutputUnconnectedSVGIcon, routingOutputUnconnectedSVGIcon, false, true); oR = new QPushButton(this); oR->setIcon(*routingOutputSVGIcon); oR->setObjectName("OutputRouteButton"); oR->setStatusTip(tr("Output routing. Hold CTRL to keep menu open. Press F1 for help.")); oR->setFocusPolicy(Qt::NoFocus); oR->setToolTip(MusEGlobal::outputRoutingToolTipBase); connect(oR, SIGNAL(pressed()), SLOT(oRoutePressed())); routeLayout->addWidget(oR); updateRouteButtons(); addGridLayout(routeLayout, _routePos); tabwidget = new QTabWidget(this); tabwidget->setObjectName("MidiStripTabWidget"); tabwidget->setUsesScrollButtons(false); _infoRack = new MidiComponentRack(t, mStripInfoRack); _infoRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); _infoRack->setContentsMargins(0,0,0,0); _infoRack->setFocusPolicy(Qt::NoFocus); _upperRack = new MidiComponentRack(t, mStripUpperRack); _upperRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); _upperRack->setContentsMargins(0,0,0,0); _upperRack->setFocusPolicy(Qt::NoFocus); tabwidget->addTab(_upperRack, tr("Inst")); tabwidget->addTab(_infoRack, tr("Prop")); tabwidget->setTabToolTip(0, tr("Instruments and controllers")); tabwidget->setTabToolTip(1, tr("Properties")); tabwidget->setContentsMargins(0,0,0,0); tabwidget->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum); addGridWidget(tabwidget, _upperStackTabPos); //--------------------------------------------------- // slider, label, meter //--------------------------------------------------- MusECore::MidiPort* mp = &MusEGlobal::midiPorts[t->outPort()]; const int chan = t->outChannel(); MusECore::MidiController* mc = mp->midiController(MusECore::CTRL_VOLUME, chan); // Auto-create the controller if necessary. slider = new Slider(nullptr, "vol", Qt::Vertical, Slider::InsideVertical, 14, MusEGlobal::config.midiVolumeSliderColor, ScaleDraw::TextHighlightSplitAndShadow, MusEGlobal::config.midiVolumeHandleColor); slider->setId(MusECore::CTRL_VOLUME); slider->setFocusPolicy(Qt::NoFocus); slider->setContentsMargins(0, 0, 0, 0); slider->setCursorHoming(true); slider->setSpecialText(tr("off")); slider->setScaleBackBone(props.sliderBackbone()); slider->setRadius(props.sliderRadius()); slider->setRadiusHandle(props.sliderRadiusHandle()); slider->setHandleHeight(props.sliderHandleHeight()); slider->setHandleWidth(props.sliderHandleWidth()); slider->setFillThumb(props.sliderFillHandle()); slider->setGrooveWidth(props.sliderGrooveWidth()); slider->setFillEmptySide(props.sliderFillOver()); slider->setUseGradient(props.sliderUseGradient()); slider->setScalePos(static_cast(props.sliderScalePos())); slider->setFrame(props.sliderFrame()); slider->setFrameColor(props.sliderFrameColor()); slider->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); slider->setMinimumHeight(80); _meterLayout = new MeterLayout(slider->scaleEndpointsMargin()); _meterLayout->setMargin(0); _meterLayout->setSpacing(props.meterSpacing()); meter[0] = new Meter(nullptr, Meter::LinMeter, Qt::Vertical, 0.0, 127.0); meter[0]->setRefreshRate(MusEGlobal::config.guiRefresh); meter[0]->setContentsMargins(0, 0, 0, 0); meter[0]->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); meter[0]->setFixedWidth(props.meterWidth()); meter[0]->setPrimaryColor(MusEGlobal::config.midiMeterPrimaryColor, MusEGlobal::config.meterBackgroundColor); meter[0]->setFrame(props.meterFrame(), props.meterFrameColor()); connect(meter[0], SIGNAL(mousePress()), this, SLOT(resetPeaks())); _meterLayout->hlayout()->addWidget(meter[0], Qt::AlignHCenter); sl = new MusEGui::DoubleLabel(0.0, -98.0, 0.0); sl->setObjectName("VolumeEditMidi"); sl->setContentsMargins(0, 0, 0, 0); sl->setTextMargins(0, 0, 0, 0); sl->setFocusPolicy(Qt::WheelFocus); sl->setMouseTracking(true); sl->setFrame(true); sl->setAlignment(Qt::AlignCenter); //sl->setAutoFillBackground(true); //sl->setBackgroundRole(QPalette::Mid); sl->setSpecialText(tr("off")); sl->setToolTip(tr("Volume/Gain\n(Ctrl-double-click on/off)")); sl->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); // Set the label's slider 'buddy'. sl->setSlider(slider); // sl->setEnableStyleHack(MusEGlobal::config.lineEditStyleHack); // Special for midi volume slider and label: Setup midi volume as decibel preference. setupMidiVolume(); // If smart focus is on redirect strip focus to slider label. //if(MusEGlobal::config.smartFocus) setFocusProxy(sl); if(mc) { double dlv; double v = mp->hwDCtrlState(chan, MusECore::CTRL_VOLUME); if(MusECore::MidiController::dValIsUnknown(v)) { double lastv = mp->lastValidHWDCtrlState(chan, MusECore::CTRL_VOLUME); if(MusECore::MidiController::dValIsUnknown(lastv)) { if(mc->initValIsUnknown()) v = 0.0; else v = double(mc->initVal()); } else v = lastv - double(mc->bias()); dlv = sl->off() - 1.0; } else { if(v <= 0.0) dlv = sl->minValue() - 0.5 * (sl->minValue() - sl->off()); else { dlv = _preferMidiVolumeDb ? (muse_val2dbr(v / double(mc->maxVal())) * 2.0) : v; if(dlv > sl->maxValue()) dlv = sl->maxValue(); } // Auto bias... v -= double(mc->bias()); } double slv; if(v <= 0.0) { if(_preferMidiVolumeDb) slv = MusEGlobal::config.minSlider; else slv = 0.0; } else slv = _preferMidiVolumeDb ? (muse_val2dbr(v / double(mc->maxVal())) * 2.0) : v; slider->setValue(slv); sl->setValue(dlv); } connect(slider, SIGNAL(valueChanged(double,int,int)), SLOT(setVolume(double,int,int))); connect(slider, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(controlRightClicked(QPoint,int))); connect(slider, SIGNAL(sliderPressed(double, int)), SLOT(volumePressed(double, int))); connect(slider, SIGNAL(sliderReleased(double, int)), SLOT(volumeReleased(double, int))); connect(sl, SIGNAL(valueChanged(double, int)), SLOT(volLabelChanged(double))); connect(sl, SIGNAL(ctrlDoubleClicked(int)), SLOT(volLabelDoubleClicked())); sliderGrid = new QGridLayout(); sliderGrid->setSpacing(0); sliderGrid->setHorizontalSpacing(2); sliderGrid->setContentsMargins(2, 0, 3, 2); sliderGrid->addWidget(slider, 0, 0, Qt::AlignHCenter); sliderGrid->addLayout(_meterLayout, 0, 1, Qt::AlignHCenter); sliderGrid->addWidget(sl, 2, 0, 1, 2, Qt::AlignHCenter); // sliderGrid->setColumnStretch(0, slider->sizeHint().width()); // sliderGrid->setColumnStretch(1, meter[0]->sizeHint().width()); QHBoxLayout *sliderHLayout = new QHBoxLayout(); sliderHLayout->setContentsMargins(0,0,0,0); sliderHLayout->setSpacing(0); sliderHLayout->addStretch(); sliderHLayout->addLayout(sliderGrid); sliderHLayout->addStretch(); sliderHLayout->setAlignment(Qt::AlignHCenter); QFrame *sliderMeterFrame = new QFrame; sliderMeterFrame->setObjectName("SliderMeterFrameMidi"); sliderMeterFrame->setLayout(sliderHLayout); // sliderMeterFrame->setLayout(sliderGrid); sliderMeterFrame->setMinimumWidth(cMinStripWidth); QHBoxLayout *sliderMeterLayout = new QHBoxLayout(); sliderMeterLayout->setContentsMargins(1,0,1,2); sliderMeterLayout->addWidget(sliderMeterFrame); addGridLayout(sliderMeterLayout, _sliderMeterPos); //--------------------------------------------------- // pan, balance //--------------------------------------------------- _lowerRack = new MidiComponentRack(t, mStripLowerRack); // FIXME For some reason StyledPanel has trouble, intermittent sometimes panel is drawn, sometimes not. //_lowerRack->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); _lowerRack->setFrameStyle(QFrame::Box | QFrame::Sunken); _lowerRack->setLineWidth(rackFrameWidth); _lowerRack->setMidLineWidth(0); // We do set a minimum height on this widget. Tested: Must be on fixed. Thankfully, it'll expand if more controls are added. _lowerRack->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); _lowerRack->setContentsMargins(rackFrameWidth, rackFrameWidth, rackFrameWidth, rackFrameWidth); _lowerRack->setFocusPolicy(Qt::NoFocus); addGridWidget(_lowerRack, _lowerRackPos); _upperRack->setEnabled(!t->off()); _infoRack->setEnabled(!t->off()); _lowerRack->setEnabled(!t->off()); //--------------------------------------------------- // mute, solo // or // record, mixdownfile //--------------------------------------------------- QGridLayout *bottomLayout = new QGridLayout; bottomLayout->setContentsMargins(1,1,1,2); bottomLayout->setSpacing(1); if (track && track->canRecordMonitor()) { // _recMonitor = new IconButton(monitorOnSVGIcon, monitorOffSVGIcon, nullptr, nullptr, false, true); _recMonitor = new QPushButton; _recMonitor->setIcon(*monitorStateSVGIcon); _recMonitor->setFocusPolicy(Qt::NoFocus); _recMonitor->setCheckable(true); _recMonitor->setToolTip(tr("Input monitor")); _recMonitor->setWhatsThis(tr("Pass input through to output")); _recMonitor->setStatusTip(tr("Input monitor: Pass input through to output.")); _recMonitor->setChecked(t->recMonitor()); connect(_recMonitor, SIGNAL(toggled(bool)), SLOT(recMonitorToggled(bool))); bottomLayout->addWidget(_recMonitor, 0, 0, 1, 1); } else { QPushButton *recMonitorx = new QPushButton(this); recMonitorx->setIcon(*monitorOnSVGIcon); recMonitorx->setEnabled(false); bottomLayout->addWidget(recMonitorx, 0, 0, 1, 1); } // record = new IconButton(recArmOnSVGIcon, recArmOffSVGIcon, 0, 0, false, true); record = new QPushButton(this); record->setIcon(*recArmStateSVGIcon); record->setFocusPolicy(Qt::NoFocus); record->setCheckable(true); record->setToolTip(tr("Record arm")); record->setChecked(track->recordFlag()); connect(record, SIGNAL(toggled(bool)), SLOT(recordToggled(bool))); bottomLayout->addWidget(record, 0, 1, 1, 1); // mute = new IconButton(muteOnSVGIcon, muteOffSVGIcon, muteAndProxyOnSVGIcon, muteProxyOnSVGIcon, false, true); mute = new QPushButton(this); mute->setIcon(*muteStateSVGIcon); mute->setFocusPolicy(Qt::NoFocus); mute->setCheckable(true); mute->setToolTip(tr("Mute or proxy mute")); mute->setStatusTip(tr("Mute or proxy mute. Connected tracks are 'phantom' muted.")); mute->setChecked(track->mute()); updateMuteIcon(); connect(mute, SIGNAL(toggled(bool)), SLOT(muteToggled(bool))); bottomLayout->addWidget(mute, 1, 0, 1, 1); // solo = new IconButton(soloOnSVGIcon, soloOffSVGIcon, soloAndProxyOnSVGIcon, soloProxyOnSVGIcon, false, true); solo = new QPushButton(this); solo->setIcon(*soloStateSVGIcon); solo->setObjectName("SoloButton"); solo->setToolTip(tr("Solo or proxy solo")); solo->setStatusTip(tr("Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help.")); solo->setFocusPolicy(Qt::NoFocus); solo->setCheckable(true); if (track->internalSolo()) solo->setIcon(*soloAndProxyOnSVGIcon); // solo->setIconSetB(track->internalSolo()); solo->setChecked(track->solo()); connect(solo, SIGNAL(toggled(bool)), SLOT(soloToggled(bool))); bottomLayout->addWidget(solo, 1, 1, 1, 1); // off = new IconButton(trackOffSVGIcon, trackOnSVGIcon, 0, 0, false, true); off = new QPushButton(this); off->setObjectName("TrackOffButton"); off->setIcon(*trackOnSVGIcon); off->setFocusPolicy(Qt::NoFocus); off->setCheckable(true); off->setToolTip(tr("Track off")); off->setChecked(track->off()); connect(off, SIGNAL(toggled(bool)), SLOT(offToggled(bool))); bottomLayout->addWidget(off, 3, 0, 1, 2); //--------------------------------------------------- // automation mode //--------------------------------------------------- autoType = new CompactComboBox(); autoType->setObjectName("MidiAutoType"); autoType->setContentsMargins(0, 0, 0, 0); autoType->setFocusPolicy(Qt::NoFocus); autoType->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); autoType->setEnabled(false); // Removed by T356. // Disabled for now. There is no midi automation mechanism yet... //autoType->addAction(tr("Off"), AUTO_OFF); //autoType->addAction(tr("Read"), AUTO_READ); //autoType->addAction(tr("Touch"), AUTO_TOUCH); //autoType->addAction(tr("Write"), AUTO_WRITE); //autoType->setCurrentItem(t->automationType()); //autoType->setToolTip(tr("automation type")); //connect(autoType, SIGNAL(activated(int)), SLOT(setAutomationType(int))); // autoType->addAction("", MusECore::AUTO_OFF); // Just a dummy text to fix sizing problems. REMOVE later if full automation added. autoType->addAction("n/a", MusECore::AUTO_OFF); autoType->setCurrentItem(MusECore::AUTO_OFF); bottomLayout->addWidget(autoType, 2, 0, 1, 2); addGridLayout(bottomLayout, _bottomPos); grid->setColumnStretch(2, 10); off->blockSignals(true); updateOffState(); // init state off->blockSignals(false); // Now build the strip components. buildStrip(); // Now set up all tabbing on the strip. // Don't bother if the strip is part of the mixer (not embedded), // the non-embedding parent (mixer) should set up all the tabs and make this call. if(isEmbedded) setupComponentTabbing(); // TODO: Activate this. But owners want to marshall this signal and send it themselves. Change that. //connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); connect(_upperRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); connect(_upperRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); connect(_upperRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); connect(_upperRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); connect(_infoRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); connect(_infoRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); connect(_infoRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); connect(_infoRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); connect(_lowerRack, SIGNAL(componentChanged(int,double,bool,int,int)), SLOT(componentChanged(int,double,bool,int,int))); connect(_lowerRack, SIGNAL(componentMoved(int,double,int,bool)), SLOT(componentMoved(int,double,int,bool))); connect(_lowerRack, SIGNAL(componentPressed(int,double,int)), SLOT(componentPressed(int,double,int))); connect(_lowerRack, SIGNAL(componentReleased(int,double,int)), SLOT(componentReleased(int,double,int))); inHeartBeat = false; } void MidiStrip::setStripStyle() { // Set the whole strip's font, except for the label. setFont(MusEGlobal::config.fonts[1]); // For some reason must keep this, the upper rack is too tall at first. int iconSize = MusEGlobal::config.fonts[1].pointSize() * 2; setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1]) + "#Strip > QAbstractButton { padding: 0px; qproperty-iconSize:" + QString::number(iconSize) + "px; }" + "#Strip #TrackOffButton { qproperty-iconSize:" + QString::number(iconSize - 2) + "px; }"); } //--------------------------------------------------- // buildStrip // Destroy and rebuild strip components. //--------------------------------------------------- void MidiStrip::buildStrip() { // Destroys all components and clears the component list. _infoRack->clearDelete(); _upperRack->clearDelete(); _lowerRack->clearDelete(); //--------------------------------------------------- // Upper rack //--------------------------------------------------- ElidedLabelComponentDescriptor instrPropertyDesc(ComponentRack::propertyComponent, "MixerStripInstrumentProperty", MidiComponentRack::mStripInstrumentProperty, Qt::ElideNone); _upperRack->newComponent(&instrPropertyDesc); CompactPatchEditComponentDescriptor progControllerDesc(ComponentRack::controllerComponent, "MixerStripMidiProgramController", MusECore::CTRL_PROGRAM); _upperRack->newComponent(&progControllerDesc); if(_preferKnobs) { _upperRack->layout()->addItem(new QSpacerItem(0, 4)); CompactKnobComponentDescriptor varSendControllerDesc(ComponentRack::controllerComponent, "MixerStripMidiVarSendController", MusECore::CTRL_VARIATION_SEND); CompactKnobComponentDescriptor revSendControllerDesc(ComponentRack::controllerComponent, "MixerStripMidiRevSendController", MusECore::CTRL_REVERB_SEND); CompactKnobComponentDescriptor choSendControllerDesc(ComponentRack::controllerComponent, "MixerStripMidiChoSendController", MusECore::CTRL_CHORUS_SEND); _upperRack->newComponent(&varSendControllerDesc); _upperRack->newComponent(&revSendControllerDesc); _upperRack->newComponent(&choSendControllerDesc); } else { _upperRack->layout()->addItem(new QSpacerItem(0, 2)); // To avoid too bright or annoying joined borders which are twice the normal width, // show no bottom borders except for last one! CompactSliderComponentDescriptor varSendControllerDesc( ComponentRack::controllerComponent, "MixerStripMidiVarSendController", MusECore::CTRL_VARIATION_SEND, CompactSlider::AllBordersExceptBottom); CompactSliderComponentDescriptor revSendControllerDesc( ComponentRack::controllerComponent, "MixerStripMidiRevSendController", MusECore::CTRL_REVERB_SEND, CompactSlider::AllBordersExceptBottom); CompactSliderComponentDescriptor choSendControllerDesc( ComponentRack::controllerComponent, "MixerStripMidiChoSendController", MusECore::CTRL_CHORUS_SEND, CompactSlider::AllBorders); _upperRack->newComponent(&varSendControllerDesc); _upperRack->newComponent(&revSendControllerDesc); _upperRack->newComponent(&choSendControllerDesc); } // Keep this if dynamic layout (flip to right side) is desired. _upperRack->addStretch(); updateRackSizes(true, false); //--------------------------------------------------- // Track properties rack //--------------------------------------------------- if(_preferKnobs) { CompactKnobComponentDescriptor transpPropertyDesc(ComponentRack::propertyComponent, "MixerStripMidiTranspProperty", MidiComponentRack::mStripTranspProperty); CompactKnobComponentDescriptor delayPropertyDesc(ComponentRack::propertyComponent, "MixerStripMidiDelayProperty", MidiComponentRack::mStripDelayProperty); CompactKnobComponentDescriptor lenPropertyDesc(ComponentRack::propertyComponent, "MixerStripMidiLenProperty", MidiComponentRack::mStripLenProperty); CompactKnobComponentDescriptor veloPropertyDesc(ComponentRack::propertyComponent, "MixerStripMidiVeloProperty", MidiComponentRack::mStripVeloProperty); CompactKnobComponentDescriptor comprPropertyDesc(ComponentRack::propertyComponent, "MixerStripMidiComprProperty", MidiComponentRack::mStripComprProperty); _infoRack->newComponent(&transpPropertyDesc); _infoRack->newComponent(&delayPropertyDesc); _infoRack->newComponent(&lenPropertyDesc); _infoRack->newComponent(&veloPropertyDesc); _infoRack->newComponent(&comprPropertyDesc); } else { // To avoid too bright or annoying joined borders which are twice the normal width, // show no bottom borders except for last one! CompactSliderComponentDescriptor transpPropertyDesc( ComponentRack::propertyComponent, "MixerStripMidiTranspProperty", MidiComponentRack::mStripTranspProperty, CompactSlider::AllBordersExceptBottom); CompactSliderComponentDescriptor delayPropertyDesc( ComponentRack::propertyComponent, "MixerStripMidiDelayProperty", MidiComponentRack::mStripDelayProperty, CompactSlider::AllBordersExceptBottom); CompactSliderComponentDescriptor lenPropertyDesc( ComponentRack::propertyComponent, "MixerStripMidiLenProperty", MidiComponentRack::mStripLenProperty, CompactSlider::AllBordersExceptBottom); CompactSliderComponentDescriptor veloPropertyDesc( ComponentRack::propertyComponent, "MixerStripMidiVeloProperty", MidiComponentRack::mStripVeloProperty, CompactSlider::AllBordersExceptBottom); CompactSliderComponentDescriptor comprPropertyDesc( ComponentRack::propertyComponent, "MixerStripMidiComprProperty", MidiComponentRack::mStripComprProperty, CompactSlider::AllBorders); _infoRack->newComponent(&transpPropertyDesc); _infoRack->newComponent(&delayPropertyDesc); _infoRack->newComponent(&lenPropertyDesc); _infoRack->newComponent(&veloPropertyDesc); _infoRack->newComponent(&comprPropertyDesc); } _infoRack->addStretch(); //--------------------------------------------------- // Lower rack //--------------------------------------------------- // Pan... if(_preferKnobs) { CompactKnobComponentDescriptor panControllerDesc ( ComponentRack::controllerComponent, "MixerStripMidiPanController", MusECore::CTRL_PANPOT ); _lowerRack->newComponent(&panControllerDesc); } else { CompactSliderComponentDescriptor panControllerDesc ( ComponentRack::controllerComponent, "MixerStripMidiPanController", MusECore::CTRL_PANPOT ); _lowerRack->newComponent(&panControllerDesc); } // Keep this if dynamic layout (flip to right side) is desired. _lowerRack->addStretch(); updateRackSizes(false, true); } QWidget* MidiStrip::setupComponentTabbing(QWidget* previousWidget) { QWidget* prev = previousWidget; if(tabwidget->currentIndex() == 0) { if(prev) QWidget::setTabOrder(prev, tabwidget->currentWidget()); prev = tabwidget->currentWidget(); } if(tabwidget->currentIndex() == 1) { if(prev) QWidget::setTabOrder(prev, tabwidget->currentWidget()); prev = tabwidget->currentWidget(); } // if(_upperStackTabButtonA) // { // if(prev) // QWidget::setTabOrder(prev, _upperStackTabButtonA); // prev = _upperStackTabButtonA; // } // if(_upperStackTabButtonB) // { // if(prev) // QWidget::setTabOrder(prev, _upperStackTabButtonB); // prev = _upperStackTabButtonB; // } prev = _upperRack->setupComponentTabbing(prev); prev = _infoRack->setupComponentTabbing(prev); if(sl) { if(prev) QWidget::setTabOrder(prev, sl); prev = sl; } prev = _lowerRack->setupComponentTabbing(prev); return prev; } void MidiStrip::setupMidiVolume() { const bool show_db = MusEGlobal::config.preferMidiVolumeDb; if(track && track->isMidiTrack()) { const int num = MusECore::CTRL_VOLUME; MusECore::MidiTrack* mt = static_cast(track); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; const int chan = mt->outChannel(); MusECore::MidiController* mc = mp->midiController(num, chan, false); if(!mc) return; const int mn = mc->minVal(); const int mx = mc->maxVal(); if(show_db) { slider->setRange(MusEGlobal::config.minSlider, volSliderMaxDb, volSliderStepDb); //slider->setScaleMaxMinor(5); slider->setScale(MusEGlobal::config.minSlider, volSliderMaxDb, 6.0, false); //slider->setSpecialText(tr("off")); //slider->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. sl->setPrecision(volSliderPrecDb); sl->setRange(MusEGlobal::config.minSlider, volSliderMaxDb); sl->setOff(MusEGlobal::config.minSlider); //sl->setSpecialText(tr("off")); //sl->setSpecialText(QString('-') + QChar(0x221e) + QChar(' ') + "dB"); // The infinity character. //sl->setToolTip(tr("Volume/gain")); sl->setSuffix("dB"); } else { slider->setRange(double(mn), double(mx), volSliderStepLin); //slider->setScaleMaxMinor(5); slider->setScale(double(mn), double(mx), 10.0, false); //slider->setSpecialText(tr("off")); //slider->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. sl->setPrecision(0); sl->setRange(double(mn), double(mx)); sl->setOff(double(mn) - 1.0); // Reset to default. //sl->setSpecialText(tr("off")); //sl->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. //sl->setToolTip(tr("Volume/gain\n(Ctrl-double-click on/off)")); sl->setSuffix(QString()); } // Invalidate the cached volume so that the next heartbeat updates with a new value. volume = MusECore::CTRL_VAL_UNKNOWN; if(_preferMidiVolumeDb != show_db) { const int chan = mt->outChannel(); const double d_lastv = mp->lastValidHWDCtrlState(chan, num); const double d_curv = mp->hwDCtrlState(chan, num); if(MusECore::MidiController::dValIsUnknown(d_curv)) { // If no value has ever been set yet, use the current knob value // (or the controller's initial value?) to 'turn on' the controller. if(MusECore::MidiController::dValIsUnknown(d_lastv)) { double slider_v = slider->value(); if(slider_v == 0.0) { if(show_db) slider_v = MusEGlobal::config.minSlider; } else { if(show_db) slider_v = muse_val2dbr(slider_v / double(mx)) * 2.0; else slider_v = double(mx) * muse_db2val(slider_v / 2.0); } slider->blockSignals(true); slider->setValue(slider_v); slider->blockSignals(false); } } } } _preferMidiVolumeDb = show_db; } //--------------------------------------------------------- // updateOffState //--------------------------------------------------------- void MidiStrip::updateOffState() { if(!track) return; bool val = !track->off(); slider->setEnabled(val); sl->setEnabled(val); _upperRack->setEnabled(val); _infoRack->setEnabled(val); _lowerRack->setEnabled(val); label->setEnabled(val); if (_recMonitor) _recMonitor->setEnabled(val); if (record) record->setEnabled(val); if (solo) solo->setEnabled(val); if (mute) mute->setEnabled(val); // TODO: Disabled for now. //if (autoType) // autoType->setEnabled(val); //if (iR) // iR->setEnabled(val); //if (oR) // oR->setEnabled(val); if (off) { off->blockSignals(true); off->setChecked(track->off()); off->blockSignals(false); } } void MidiStrip::updateRackSizes(bool upper, bool lower) { // const QFontMetrics fm = fontMetrics(); if(upper) { // Make room for 3 CompactSliders and one CompactPatchEdit. // TODO: Add the instrument select label height! // // const int csh = CompactSlider::getMinimumSizeHint(fm, // // Qt::Horizontal, // // CompactSlider::None, // // xMarginHorSlider, yMarginHorSlider).height(); // // const int cpeh = CompactPatchEdit::getMinimumSizeHint(fm, // // Qt::Horizontal, // // CompactSlider::None, // // xMarginHorSlider, yMarginHorSlider).height(); // // const int ilh = _instrLabel->sizeHint().height(); // // // DEBUG_MIDI_STRIP(stderr, "MidiStrip::updateRackSizes: CompactSlider h:%d CompactPatchEdit h:%d instrLabel h:%d upper frame w:%d \n", // // csh, cpeh, ilh, _upperRack->frameWidth()); // // _upperRack->setMinimumHeight( // 3 * CompactSlider::getMinimumSizeHint(fm, // Qt::Horizontal, // CompactSlider::None, // xMarginHorSlider, yMarginHorSlider).height() + // 1 * CompactPatchEdit::getMinimumSizeHint(fm, // Qt::Horizontal, // CompactSlider::None, // xMarginHorSlider, yMarginHorSlider).height() + // upperRackSpacerHeight + // // _instrLabel->sizeHint().height() + // // 2 * rackFrameWidth); } if(lower) { // Make room for 1 CompactSlider (Pan, so far). //DEBUG_MIDI_STRIP(stderr, "MidiStrip::updateRackSizes: lower frame w:%d \n", _lowerRack->frameWidth()); // _lowerRack->setMinimumHeight( // 1 * CompactSlider::getMinimumSizeHint(fm, // Qt::Horizontal, // CompactSlider::None, // xMarginHorSlider, yMarginHorSlider).height() + // 2 * rackFrameWidth); } } //--------------------------------------------------------- // configChanged // Catch when config label font changes, viewable tracks etc. //--------------------------------------------------------- void MidiStrip::configChanged() { // Detect when knobs are preferred and rebuild. if(_preferKnobs != MusEGlobal::config.preferKnobsVsSliders) { _preferKnobs = MusEGlobal::config.preferKnobsVsSliders; // Rebuild the strip components. buildStrip(); // Now set up all tabbing on the strip. // Don't bother if the strip is part of the mixer (not embedded), // the non-embedding parent (mixer) should set up all the tabs and make this call. if(isEmbedded()) setupComponentTabbing(); } // Set the whole strip's font, except for the label. if(font() != MusEGlobal::config.fonts[1]) { //DEBUG_MIDI_STRIP(stderr, "MidiStrip::configChanged changing font: current size:%d\n", font().pointSize()); setStripStyle(); // Update in case font changed. // updateRackSizes(true, true); // function has no content } // Update always, in case style, stylesheet, or font changed. //updateRackSizes(true, true); // Set the strip label's font. setLabelText(); slider->setFillColor(MusEGlobal::config.midiVolumeSliderColor); slider->setHandleColor(MusEGlobal::config.midiVolumeHandleColor); // _upperStackTabButtonA->setBgColor(MusEGlobal::config.palSwitchBackgroundColor); // _upperStackTabButtonB->setBgColor(MusEGlobal::config.palSwitchBackgroundColor); // _upperStackTabButtonA->setBgActiveColor(MusEGlobal::config.palSwitchBgActiveColor); // _upperStackTabButtonB->setBgActiveColor(MusEGlobal::config.palSwitchBgActiveColor); // _upperStackTabButtonA->setBorderColor(MusEGlobal::config.palSwitchBorderColor); // _upperStackTabButtonB->setBorderColor(MusEGlobal::config.palSwitchBorderColor); // _upperStackTabButtonA->setFontColor(MusEGlobal::config.palSwitchFontColor); // _upperStackTabButtonB->setFontColor(MusEGlobal::config.palSwitchFontColor); // _upperStackTabButtonA->setFontActiveColor(MusEGlobal::config.palSwitchFontActiveColor); // _upperStackTabButtonB->setFontActiveColor(MusEGlobal::config.palSwitchFontActiveColor); // Enable special hack for line edits. // if(sl->enableStyleHack() != MusEGlobal::config.lineEditStyleHack) // sl->setEnableStyleHack(MusEGlobal::config.lineEditStyleHack); // Special for midi volume slider and label: Setup midi volume as decibel preference. setupMidiVolume(); // REMOVE Tim. mixer. Added. // In case something in the slider changed, update the meter layout. // TODO This is somewhat crude, might miss automatic changes. // Later link up MeterLayout and Slider better. _meterLayout->setMeterEndsMargin(slider->scaleEndpointsMargin()); _upperRack->configChanged(); _infoRack->configChanged(); _lowerRack->configChanged(); // Adjust meter and colour. meter[0]->setPrimaryColor(MusEGlobal::config.midiMeterPrimaryColor, MusEGlobal::config.meterBackgroundColor); meter[0]->setRefreshRate(MusEGlobal::config.guiRefresh); // If smart focus is on redirect strip focus to slider label. // if(MusEGlobal::config.smartFocus) // setFocusProxy(sl); // else // setFocusProxy(0); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MidiStrip::songChanged(MusECore::SongChangedStruct_t val) { if (mute && (val & SC_MUTE)) { // mute && off mute->blockSignals(true); mute->setChecked(track->mute()); mute->blockSignals(false); updateMuteIcon(); updateOffState(); } if (solo && (val & (SC_SOLO | SC_ROUTE))) { solo->blockSignals(true); solo->setChecked(track->solo()); solo->blockSignals(false); // solo->setIconSetB(track->internalSolo()); if (track->internalSolo()) { if (solo->isChecked()) solo->setIcon(*soloAndProxyOnSVGIcon); else solo->setIcon(*soloProxyOnAloneSVGIcon); } else { solo->setIcon(*soloStateSVGIcon); } updateMuteIcon(); } if (val & SC_RECFLAG) { setRecordFlag(track->recordFlag()); } if (val & SC_TRACK_MODIFIED) { setLabelText(); } // Catch when label font changes. if (val & SC_CONFIG) { // So far only 1 instance of sending SC_CONFIG in the entire app, in instrument editor when a new instrument is saved. } _upperRack->songChanged(val); _infoRack->songChanged(val); _lowerRack->songChanged(val); if (val & SC_ROUTE) { updateRouteButtons(); } if(val & SC_TRACK_REC_MONITOR) { // Set record monitor. if(_recMonitor) { _recMonitor->blockSignals(true); _recMonitor->setChecked(track->recMonitor()); _recMonitor->blockSignals(false); } } } //--------------------------------------------------------- // controlRightClicked //--------------------------------------------------------- void MidiStrip::controlRightClicked(QPoint p, int id) { MusEGlobal::song->execMidiAutomationCtlPopup(static_cast(track), 0, p, id); } //void MidiStrip::upperStackTabButtonAPressed() //{ // _infoRack->hide(); // _upperRack->show(); // _upperStackTabButtonA->setOff(false); // _upperStackTabButtonB->setOff(true); //} //void MidiStrip::upperStackTabButtonBPressed() //{ // _upperRack->hide(); // _infoRack->show(); // _upperStackTabButtonA->setOff(true); // _upperStackTabButtonB->setOff(false); //} //--------------------------------------------------------- // recMonitorToggled //--------------------------------------------------------- void MidiStrip::recMonitorToggled(bool v) { if(!track) return; // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(track, v, MusECore::PendingOperationItem::SetTrackRecMonitor)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // volLabelDoubleClicked //--------------------------------------------------------- void MidiStrip::volLabelDoubleClicked() { const int num = MusECore::CTRL_VOLUME; const int outport = static_cast(track)->outPort(); const int chan = static_cast(track)->outChannel(); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[outport]; MusECore::MidiController* mc = mp->midiController(num, chan, false); if(!mc) return; const double lastv = mp->lastValidHWDCtrlState(chan, num); const double curv = mp->hwDCtrlState(chan, num); if(MusECore::MidiController::dValIsUnknown(curv)) { // If no value has ever been set yet, use the current knob value // (or the controller's initial value?) to 'turn on' the controller. if(MusECore::MidiController::dValIsUnknown(lastv)) { double slv = slider->value(); if(_preferMidiVolumeDb) slv = double(mc->maxVal()) * muse_db2val(slv / 2.0); if(slv < double(mc->minVal())) slv = mc->minVal(); if(slv > double(mc->maxVal())) slv = mc->maxVal(); slv += double(mc->bias()); mp->putControllerValue(outport, chan, num, slv, false); } else { mp->putControllerValue(outport, chan, num, lastv, false); } } else { if(mp->hwCtrlState(chan, num) != MusECore::CTRL_VAL_UNKNOWN) mp->putHwCtrlEvent(MusECore::MidiPlayEvent(MusEGlobal::audio->curFrame(), outport, chan, MusECore::ME_CONTROLLER, num, MusECore::CTRL_VAL_UNKNOWN)); } } //--------------------------------------------------------- // offToggled //--------------------------------------------------------- void MidiStrip::offToggled(bool val) { if(!track) return; // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackOff)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // heartBeat //--------------------------------------------------------- void MidiStrip::heartBeat() { inHeartBeat = true; // Try to avoid calling MidiInstrument::getPatchName too often. if(++_heartBeatCounter >= 10) _heartBeatCounter = 0; if(track && track->isMidiTrack()) { int act = track->activity(); double m_val = slider->value(); if(_preferMidiVolumeDb) { MusECore::MidiTrack* t = static_cast(track); const int port = t->outPort(); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; const int chan = t->outChannel(); MusECore::MidiController* mctl = mp->midiController(MusECore::CTRL_VOLUME, chan, false); if(!mctl) return; m_val = double(mctl->maxVal()) * muse_db2val(m_val / 2.0); m_val += double(mctl->bias()); if(m_val < double(mctl->minVal())) m_val = double(mctl->minVal()); if(m_val > double(mctl->maxVal())) m_val = double(mctl->maxVal()); } double dact = double(act) * (m_val / 127.0); if((int)dact > track->lastActivity()) track->setLastActivity((int)dact); if(meter[0]) meter[0]->setVal(dact, track->lastActivity(), false); // Gives reasonable decay with gui update set to 20/sec. if(act) track->setActivity((int)((double)act * 0.8)); } updateControls(); _upperRack->updateComponents(); _infoRack->updateComponents(); _lowerRack->updateComponents(); //if(_recMonitor && _recMonitor->isChecked() && MusEGlobal::blinkTimerPhase != _recMonitor->blinkPhase()) // _recMonitor->setBlinkPhase(MusEGlobal::blinkTimerPhase); Strip::heartBeat(); inHeartBeat = false; } //--------------------------------------------------------- // updateControls //--------------------------------------------------------- void MidiStrip::updateControls() { MusECore::MidiTrack* mt = dynamic_cast(track); if (!mt) return; const int channel = mt->outChannel(); const int port = mt->outPort(); if(channel < 0 || channel >= MusECore::MUSE_MIDI_CHANNELS || port < 0 || port >= MusECore::MIDI_PORTS) return; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiCtrlValListList* mcvll = mp->controller(); MusECore::ciMidiCtrlValList imcvl = mcvll->find(channel, MusECore::CTRL_VOLUME); const bool enable = imcvl != mcvll->end() && !mt->off(); if(slider->isEnabled() != enable) slider->setEnabled(enable); if(sl->isEnabled() != enable) sl->setEnabled(enable); if(enable) { MusECore::MidiCtrlValList* mcvl = imcvl->second; double d_hwVal = mcvl->hwDVal(); int max = 127; int bias = 0; MusECore::MidiController* mc = mp->midiController(MusECore::CTRL_VOLUME, channel, false); if(mc) { max = mc->maxVal(); bias = mc->bias(); } if(mcvl->hwValIsUnknown()) { sl->setValue(sl->off() - 1.0); volume = MusECore::CTRL_VAL_UNKNOWN; d_hwVal = mcvl->lastValidHWDVal(); if(mcvl->lastHwValIsUnknown()) { // TODO // if(!control->isOff()) // { // control->blockSignals(true); // control->setOff(true); // control->blockSignals(false); // } } else { d_hwVal -= double(bias); double slider_v; if(d_hwVal <= 0.0) { if(_preferMidiVolumeDb) slider_v = MusEGlobal::config.minSlider; else slider_v = 0.0; } else { if(_preferMidiVolumeDb) { slider_v = muse_val2dbr(d_hwVal / double(max)) * 2.0; if(slider_v < MusEGlobal::config.minSlider) slider_v = MusEGlobal::config.minSlider; } else slider_v = d_hwVal; } if(slider_v != slider->value()) { slider->blockSignals(true); slider->setValue(slider_v); slider->blockSignals(false); } } } else { double d_vol = d_hwVal; d_hwVal -= double(bias); if(d_hwVal != volume) { double slider_v; if(d_hwVal <= 0.0) { if(_preferMidiVolumeDb) slider_v = MusEGlobal::config.minSlider; else slider_v = 0.0; } else { if(_preferMidiVolumeDb) { slider_v = muse_val2dbr(d_hwVal / double(max)) * 2.0; if(slider_v < MusEGlobal::config.minSlider) slider_v = MusEGlobal::config.minSlider; } else slider_v = d_hwVal; } if(slider_v != slider->value()) { slider->blockSignals(true); slider->setValue(slider_v); slider->blockSignals(false); } if(d_vol <= 0.0) sl->setValue(sl->minValue() - 0.5 * (sl->minValue() - sl->off())); else { double sl_v = _preferMidiVolumeDb ? (muse_val2dbr(d_vol / double(max)) * 2.0) : d_vol; if(sl_v > sl->maxValue()) sl->setValue(sl->maxValue()); else sl->setValue(sl_v); } volume = d_hwVal; } } } } //--------------------------------------------------------- // ctrlChanged //--------------------------------------------------------- void MidiStrip::ctrlChanged(double v, bool off, int num, int scrollMode) { if (inHeartBeat) return; if(!track || !track->isMidiTrack()) return; MusECore::MidiTrack* t = static_cast(track); int port = t->outPort(); int chan = t->outChannel(); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiController* mctl = mp->midiController(num, chan, false); if(mctl) { double m_val = v; if(_preferMidiVolumeDb) m_val = double(mctl->maxVal()) * muse_db2val(m_val / 2.0); if(off || (m_val < double(mctl->minVal())) || (m_val > double(mctl->maxVal()))) { if(mp->hwCtrlState(chan, num) != MusECore::CTRL_VAL_UNKNOWN) mp->putHwCtrlEvent(MusECore::MidiPlayEvent(MusEGlobal::audio->curFrame(), port, chan, MusECore::ME_CONTROLLER, num, MusECore::CTRL_VAL_UNKNOWN)); } else { m_val += double(mctl->bias()); mp->putControllerValue(port, chan, num, m_val, false); } } componentChanged(ComponentRack::controllerComponent, v, off, num, scrollMode); } //--------------------------------------------------------- // volLabelChanged //--------------------------------------------------------- void MidiStrip::volLabelChanged(double val) { ctrlChanged(val, false, MusECore::CTRL_VOLUME, SliderBase::ScrNone); } //--------------------------------------------------------- // setVolume //--------------------------------------------------------- void MidiStrip::setVolume(double val, int id, int scrollMode) { DEBUG_MIDI_STRIP("Vol %d\n", lrint(val)); ctrlChanged(val, false, id, scrollMode); } //--------------------------------------------------------- // volumePressed //--------------------------------------------------------- void MidiStrip::volumePressed(double val, int id) { DEBUG_MIDI_STRIP(stderr, "MidiStrip::volumePressed\n"); if(!track || !track->isMidiTrack()) return; componentPressed(ComponentRack::controllerComponent, val, id); } //--------------------------------------------------------- // volumeReleased //--------------------------------------------------------- void MidiStrip::volumeReleased(double val, int id) { DEBUG_MIDI_STRIP(stderr, "MidiStrip::volumeReleased\n"); if(!track || !track->isMidiTrack()) return; componentReleased(ComponentRack::controllerComponent, val, id); } //--------------------------------------------------------- // iRoutePressed //--------------------------------------------------------- void MidiStrip::iRoutePressed() { RoutePopupMenu* pup = new RoutePopupMenu(0, false, _broadcastChanges); pup->exec(QCursor::pos(), track, false); delete pup; iR->setDown(false); } //--------------------------------------------------------- // oRoutePressed //--------------------------------------------------------- void MidiStrip::oRoutePressed() { RoutePopupMenu* pup = new RoutePopupMenu(0, true, _broadcastChanges); pup->exec(QCursor::pos(), track, true); delete pup; oR->setDown(false); } void MidiStrip::incVolume(int incrementValue) { if(!track || !track->isMidiTrack()) return; const int id = MusECore::CTRL_VOLUME; MusECore::MidiTrack* t = static_cast(track); const int port = t->outPort(); const int chan = t->outChannel(); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiController* mctl = mp->midiController(id, chan, false); if(mctl) { // Get the slider's current value. const double prev_val = slider->value(); double d_prev_val = prev_val; if(_preferMidiVolumeDb) d_prev_val = double(mctl->maxVal()) * muse_db2val(d_prev_val / 2.0); // Increment the slider. Do not allow signalling. slider->blockSignals(true); slider->incValue(incrementValue * 2); slider->blockSignals(false); // Now grab the control's new value. const double new_val = slider->value(); double d_new_val = new_val; if(_preferMidiVolumeDb) d_new_val = double(mctl->maxVal()) * muse_db2val(d_new_val / 2.0); if((d_new_val < double(mctl->minVal())) || (d_new_val > double(mctl->maxVal()))) { if(mp->hwCtrlState(chan, id) != MusECore::CTRL_VAL_UNKNOWN) mp->putHwCtrlEvent(MusECore::MidiPlayEvent(MusEGlobal::audio->curFrame(), port, chan, MusECore::ME_CONTROLLER, id, MusECore::CTRL_VAL_UNKNOWN)); } else { d_new_val += double(mctl->bias()); mp->putControllerValue(port, chan, id, d_new_val, false); } componentIncremented(ComponentRack::controllerComponent, prev_val, new_val, false, id, Slider::ScrNone); } } void MidiStrip::incPan(int v) { if(!track || !track->isMidiTrack()) return; const int id = MusECore::CTRL_PANPOT; ComponentRack* rack = 0; ComponentWidget* cw = 0; // Be sure to search all racks. Even if pan is in multiple racks, only one hit is // needed since after the value is set, the other pan controls will be updated too. if((cw = _upperRack->findComponent(ComponentRack::controllerComponent, -1, id))) rack = _upperRack; else if((cw = _infoRack->findComponent(ComponentRack::controllerComponent, -1, id))) rack = _infoRack; else if((cw = _lowerRack->findComponent(ComponentRack::controllerComponent, -1, id))) rack = _lowerRack; if(!cw || !rack) return; MusECore::MidiTrack* t = static_cast(track); const int port = t->outPort(); const int chan = t->outChannel(); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiController* mctl = mp->midiController(id, chan, false); if(mctl) { // Get the component's current value. double prev_val = rack->componentValue(*cw); // Now increment the component. Do not allow signalling. rack->incComponentValue(*cw, v, true); // Now grab its value. const double d_new_val = rack->componentValue(*cw); double d_fin_val = d_new_val; if((d_fin_val < double(mctl->minVal())) || (d_fin_val > double(mctl->maxVal()))) { if(mp->hwCtrlState(chan, MusECore::CTRL_PANPOT) != MusECore::CTRL_VAL_UNKNOWN) mp->putHwCtrlEvent(MusECore::MidiPlayEvent(MusEGlobal::audio->curFrame(), port, chan, MusECore::ME_CONTROLLER, id, MusECore::CTRL_VAL_UNKNOWN)); } else { d_fin_val += double(mctl->bias()); mp->putControllerValue(port, chan, id, d_fin_val, false); } componentIncremented(ComponentRack::controllerComponent, prev_val, d_new_val, false, id, Slider::ScrNone); } } } // namespace MusEGui muse-4.0.0/src/muse/mixer/mstrip.h000066400000000000000000000335011404105311000170260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mstrip.h,v 1.4.2.4 2009/10/25 19:26:29 lunar_shuttle Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011 - 2017 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MSTRIP_H__ #define __MSTRIP_H__ #include "type_defs.h" #include "strip.h" #include "meter.h" class QWidget; class QAction; class QDialog; class QString; class QString; class QPoint; class QVBoxLayout; class QSpacerItem; class QTabWidget; namespace MusECore { class MidiTrack; } namespace MusEGui { class ElidedLabel; class DoubleLabel; class Slider; class CompactSlider; class CompactPatchEdit; //class IconButton; //--------------------------------------------------------- // MidiComponentRack //--------------------------------------------------------- class MidiComponentRack : public ComponentRack { Q_OBJECT public: // Type of component. //enum MStripComponentType { type = userComponent }; // Possible widget types. enum MStripComponentWidgetType { mStripCompactPatchEditComponentWidget = userComponentWidget }; // Some controller types. enum MStripControlType { mStripPanControl = 0, mStripVarSendControl, mStripRevSendControl, mStripChoSendControl, mStripProgramControl }; // Possible component properties. enum MStripComponentProperties { mStripInstrumentProperty = userComponentProperty, mStripTranspProperty, mStripDelayProperty, mStripLenProperty, mStripVeloProperty, mStripComprProperty }; protected: MusECore::MidiTrack* _track; // Creates a new component widget from the given desc. Called by newComponent(). // Connects known widget types' signals to slots. virtual void newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before = ComponentWidget() ); // Scan and automatically remove missing / add new controllers. void scanControllerComponents(); // Set component colours upon config changed. void setComponentColors(); void labelPropertyPressHandler(QPoint p, int id, Qt::KeyboardModifiers keys); protected slots: virtual void controllerChanged(int val, int id); virtual void controllerChanged(double val, int id); virtual void controllerChanged(double val, bool isOff, int id, int scrollMode); virtual void controllerMoved(double, int, bool); virtual void controllerPressed(double, int); virtual void controllerReleased(double, int); virtual void controllerRightClicked(QPoint, int); virtual void propertyChanged(double val, bool isOff, int id, int scrollMode); virtual void propertyMoved(double, int, bool); virtual void propertyPressed(double, int); virtual void propertyReleased(double, int); virtual void propertyRightClicked(QPoint, int); virtual void labelPropertyPressed(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); virtual void labelPropertyReleased(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); virtual void labelPropertyReturnPressed(QPoint p, int id, Qt::KeyboardModifiers keys); void patchPopup(QPoint p); void patchPopupActivated(QAction*); void instrPopup(QPoint p); virtual void patchEditNameClicked(QPoint p, int id); public slots: virtual void configChanged(); virtual void songChanged(MusECore::SongChangedStruct_t); public: MidiComponentRack(MusECore::MidiTrack* track, int id = -1, QWidget* parent = 0, Qt::WindowFlags f = Qt::Widget); MusECore::MidiTrack* track() { return _track; } // Adds a component to the layout and the list. Creates a new component using // the given desc values if the desc widget is not given. virtual void newComponent( ComponentDescriptor* desc, const ComponentWidget& before = ComponentWidget() ); // Updates all the components, for example updating the values of sliders. void updateComponents(); // Enable or disable all the aux components in this rack. void setAuxEnabled(bool enable); // Sets up tabbing for the existing controls in the rack. // Accepts a previousWidget which can be null and returns the last widget in the rack, // which allows chaining racks or other widgets. virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); }; //--------------------------------------------- // CompactPatchEditComponentDescriptor // Class defining a CompactPatchEdit to be added to a rack layout. //--------------------------------------------- class CompactPatchEditComponentDescriptor : public ComponentDescriptor { public: // Return value pointer created by the function, corresponding to a ComponentWidgetType: CompactPatchEdit* _compactPatchEdit; double _initVal; bool _isOff; QColor _readoutColor; public: CompactPatchEditComponentDescriptor() : ComponentDescriptor(ComponentRack::CompactSliderComponentWidget, ComponentRack::controllerComponent), _compactPatchEdit(0), _initVal(0.0), _isOff(false) { } CompactPatchEditComponentDescriptor( ComponentWidget::ComponentType componentType, const char* objName = 0, int index = 0, const QString& toolTipText = QString(), const QString& label = QString(), const QColor& readoutColour = QColor(), bool enabled = true, double initVal = 0.0, bool isOff = false ) : ComponentDescriptor(MidiComponentRack::mStripCompactPatchEditComponentWidget, componentType, objName, index, toolTipText, label, readoutColour, enabled ), _compactPatchEdit(0), _initVal(initVal), _isOff(isOff) { } }; //--------------------------------------------------------- // MidiStripProperties //--------------------------------------------------------- class MidiStripProperties : QWidget { Q_OBJECT // Q_PROPERTY(QColor bgColor READ bgColor WRITE setBgColor) Q_PROPERTY(int sliderRadius READ sliderRadius WRITE setSliderRadius) Q_PROPERTY(int sliderRadiusHandle READ sliderRadiusHandle WRITE setSliderRadiusHandle) Q_PROPERTY(int sliderHandleHeight READ sliderHandleHeight WRITE setSliderHandleHeight) Q_PROPERTY(int sliderHandleWidth READ sliderHandleWidth WRITE setSliderHandleWidth) Q_PROPERTY(int sliderGrooveWidth READ sliderGrooveWidth WRITE setSliderGrooveWidth) Q_PROPERTY(bool sliderFillOver READ sliderFillOver WRITE setSliderFillOver) Q_PROPERTY(bool sliderUseGradient READ sliderUseGradient WRITE setSliderUseGradient) Q_PROPERTY(bool sliderBackbone READ sliderBackbone WRITE setSliderBackbone) Q_PROPERTY(bool sliderFillHandle READ sliderFillHandle WRITE setSliderFillHandle) Q_PROPERTY(int sliderScalePos READ sliderScalePos WRITE setSliderScalePos) Q_PROPERTY(bool sliderFrame READ sliderFrame WRITE setSliderFrame) Q_PROPERTY(QColor sliderFrameColor READ sliderFrameColor WRITE setSliderFrameColor) Q_PROPERTY(int meterWidth READ meterWidth WRITE setMeterWidth) Q_PROPERTY(int meterSpacing READ meterSpacing WRITE setMeterSpacing) Q_PROPERTY(bool meterFrame READ meterFrame WRITE setMeterFrame) Q_PROPERTY(QColor meterFrameColor READ meterFrameColor WRITE setMeterFrameColor) // QColor _bgColor; int _sliderRadius; int _sliderRadiusHandle; int _sliderHandleHeight; int _sliderHandleWidth; int _sliderGrooveWidth; bool _sliderFillOver; bool _sliderUseGradient; bool _sliderBackbone; bool _sliderFillHandle; int _sliderScalePos; bool _sliderFrame; QColor _sliderFrameColor; int _meterWidth; int _meterSpacing; bool _meterFrame; QColor _meterFrameColor; public: MidiStripProperties(); // QColor bgColor() const { return _bgColor; } // void setBgColor(const QColor c) { _bgColor = c; } int sliderRadius() const { return _sliderRadius; } void setSliderRadius(int radius) { _sliderRadius = radius; } int sliderRadiusHandle() const { return _sliderRadiusHandle; } void setSliderRadiusHandle(int radiusHandle) { _sliderRadiusHandle = radiusHandle; } int sliderHandleHeight() const { return _sliderHandleHeight; } void setSliderHandleHeight(int h) { _sliderHandleHeight = h; } int sliderHandleWidth() const { return _sliderHandleWidth; } void setSliderHandleWidth(int w) { _sliderHandleWidth = w; } int sliderGrooveWidth() const { return _sliderGrooveWidth; } void setSliderGrooveWidth(int w) { _sliderGrooveWidth = w; } bool sliderFillOver() const { return _sliderFillOver; } void setSliderFillOver(bool b) { _sliderFillOver = b; } bool sliderUseGradient() const { return _sliderUseGradient; } void setSliderUseGradient(bool b) { _sliderUseGradient = b; } bool sliderBackbone() const { return _sliderBackbone; } void setSliderBackbone(bool b) { _sliderBackbone = b; } bool sliderFillHandle() const { return _sliderFillHandle; } void setSliderFillHandle(bool b) { _sliderFillHandle = b; } int sliderScalePos() const { return _sliderScalePos; } void setSliderScalePos(int p) { _sliderScalePos = p; } bool sliderFrame() const { return _sliderFrame; } void setSliderFrame(bool b) { _sliderFrame = b; } QColor sliderFrameColor() const { return _sliderFrameColor; } void setSliderFrameColor(const QColor c) { _sliderFrameColor = c; } int meterWidth() const { return _meterWidth; } void setMeterWidth(int w) { _meterWidth = w; } int meterSpacing() const { return _meterSpacing; } void setMeterSpacing(int s) { _meterSpacing = s; } bool meterFrame() const { return _meterFrame; } void setMeterFrame(bool b) { _meterFrame = b; } QColor meterFrameColor() const { return _meterFrameColor; } void setMeterFrameColor(const QColor c) { _meterFrameColor = c; } }; //--------------------------------------------------------- // MidiStrip //--------------------------------------------------------- class MidiStrip : public Strip { Q_OBJECT public: // ID numbers for each rack in this strip. enum MStripRacks { mStripUpperRack = 0, mStripInfoRack = 1, mStripLowerRack = 2 }; private: GridPosStruct _upperStackTabPos; GridPosStruct _infoSpacerTop; GridPosStruct _infoSpacerBottom; GridPosStruct _sliderMeterPos; GridPosStruct _lowerRackPos; GridPosStruct _routePos; GridPosStruct _bottomPos; MidiStripProperties props; Slider* slider; DoubleLabel* sl; QPushButton* off; QPushButton* _recMonitor; MeterLayout* _meterLayout; MidiComponentRack* _upperRack; MidiComponentRack* _lowerRack; MidiComponentRack* _infoRack; QTabWidget *tabwidget; // Whether the layout is in mode A (normal, racks on left) or B (racks on right). bool _isExpanded; // Current local state of knobs versus sliders preference global setting. bool _preferKnobs; // Current local state of midi volume as decibels preference. bool _preferMidiVolumeDb; int _heartBeatCounter; double volume; bool inHeartBeat; void updateControls(); void updateOffState(); void updateRackSizes(bool upper, bool lower); void setStripStyle(); protected: void setupMidiVolume(); private slots: void recMonitorToggled(bool); void offToggled(bool); void iRoutePressed(); void oRoutePressed(); void setVolume(double val, int id, int scrollMode); void volumePressed(double val, int id); void volumeReleased(double val, int id); void ctrlChanged(double val, bool off, int num, int scrollMode); void volLabelDoubleClicked(); void volLabelChanged(double); void controlRightClicked(QPoint, int); // void upperStackTabButtonAPressed(); // void upperStackTabButtonBPressed(); protected slots: virtual void heartBeat(); public slots: virtual void songChanged(MusECore::SongChangedStruct_t); virtual void configChanged(); void incVolume(int v); void incPan(int v); public: MidiStrip(QWidget* parent, MusECore::MidiTrack*, bool hasHandle = false, bool isEmbedded = true); static const double volSliderStepLin; static const double volSliderStepDb; static const double volSliderMaxDb; static const int volSliderPrecDb; static const int xMarginHorSlider; static const int yMarginHorSlider; static const int upperRackSpacerHeight; static const int rackFrameWidth; // Destroy and rebuild strip components. virtual void buildStrip(); // Sets up tabbing for the entire strip. // Accepts a previousWidget which can be null and returns the last widget in the strip, // which allows chaining other widgets. virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mixer/panknob.cpp000066400000000000000000000033511404105311000174730ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: panknob.cpp,v 1.5 2004/01/23 08:41:38 wschweer Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= //#include "../audio.h" #include "song.h" #include "panknob.h" #include "track.h" namespace MusEGui { //--------------------------------------------------------- // PanKnob //--------------------------------------------------------- PanKnob::PanKnob(QWidget* parent, AudioTrack* s) : MusEGui::Knob(parent, "pan") { src = s; connect(this, SIGNAL(valueChanged(double,int)), SLOT(valueChanged(double))); } //--------------------------------------------------------- // panChanged //--------------------------------------------------------- void PanKnob::valueChanged(double val) { //audio->msgSetPan(src, val); // p4.0.21 audio->msgXXX waits. Do we really need to? src->setPan(val); } } // namespace MusEGui muse-4.0.0/src/muse/mixer/panknob.h000066400000000000000000000026541404105311000171450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: panknob.h,v 1.3 2003/11/08 15:10:18 wschweer Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PANKNOB_H__ #define __PANKNOB_H__ #include "knob.h" class AudioTrack; namespace MusEGui { //--------------------------------------------------------- // PanKnob //--------------------------------------------------------- class PanKnob : public Knob { Q_OBJECT AudioTrack* src; private slots: void valueChanged(double); public: PanKnob(QWidget* parent, AudioTrack*); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mixer/rack.cpp000066400000000000000000000660451404105311000167740ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: rack.cpp,v 1.7.2.7 2007/01/27 14:52:43 spamatica Exp $ // // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 #include #include #include #include #include #include "popupmenu.h" #include #include "xml.h" #include "rack.h" #include "song.h" #include "audio.h" #include "icons.h" #include "gconfig.h" #include "globaldefs.h" #include "plugin.h" #include "plugindialog.h" #include "filedialog.h" #ifdef LV2_SUPPORT #include "lv2host.h" #endif #include #include #include #include #include #include "track.h" #include "background_painter.h" namespace MusEGui { QString MUSE_MIME_TYPE = "text/x-muse-plugin"; //--------------------------------------------------------- // class EffectRackDelegate //--------------------------------------------------------- class EffectRackDelegate : public QStyledItemDelegate { EffectRack* er; MusECore::AudioTrack* tr; public: void paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; EffectRackDelegate(QObject * parent, MusECore::AudioTrack* at ); virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; static const int itemXMargin; static const int itemYMargin; static const int itemTextXMargin; static const int itemTextYMargin; }; const int EffectRackDelegate::itemXMargin = 1; const int EffectRackDelegate::itemYMargin = 1; const int EffectRackDelegate::itemTextXMargin = 1; const int EffectRackDelegate::itemTextYMargin = 1; EffectRackDelegate::EffectRackDelegate(QObject * parent, MusECore::AudioTrack* at ) : QStyledItemDelegate(parent) { er = (EffectRack*) parent; tr = at; } QSize EffectRackDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const { return QSize(10, option.fontMetrics.height() + 2 * itemYMargin + 2 * itemTextYMargin); } void EffectRackDelegate::paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const { painter->save(); painter->setRenderHint(QPainter::Antialiasing); const QRect rr = option.rect; QRect cr = QRect(rr.x() + itemXMargin, rr.y() + itemYMargin, rr.width() - 2 * itemXMargin, rr.height() - 2 * itemYMargin); const QRect onrect = (tr->efxPipe() && tr->efxPipe()->isOn(index.row())) ? rr : QRect(); ItemBackgroundPainter* ibp = er->getBkgPainter(); ibp->drawBackground(painter, rr, option.palette, itemXMargin, itemYMargin, onrect, er->radius(), er->style3d(), MusEGlobal::config.rackItemBgActiveColor, MusEGlobal::config.rackItemBorderColor, MusEGlobal::config.rackItemBackgroundColor); QString name = tr->efxPipe() ? tr->efxPipe()->name(index.row()) : QString(); if (option.state & QStyle::State_MouseOver) painter->setPen(MusEGlobal::config.rackItemFontColorHover); else if (onrect.isNull()) painter->setPen(MusEGlobal::config.rackItemFontColor); else painter->setPen(MusEGlobal::config.rackItemFontActiveColor); painter->drawText(cr.x() + itemTextXMargin, cr.y() + itemTextYMargin, cr.width() - 2 * itemTextXMargin, cr.height() - 2 * itemTextYMargin, Qt::AlignLeft | Qt::AlignVCenter, name); painter->restore(); } //--------------------------------------------------------- // class RackSlot //--------------------------------------------------------- class RackSlot : public QListWidgetItem { int idx; MusECore::AudioTrack* node; public: RackSlot(QListWidget* lb, MusECore::AudioTrack* t, int i, int h); ~RackSlot(); //void setBackgroundColor(const QBrush& brush) {setBackground(brush);} }; RackSlot::~RackSlot() { node = nullptr; } //--------------------------------------------------------- // RackSlot //--------------------------------------------------------- RackSlot::RackSlot(QListWidget* b, MusECore::AudioTrack* t, int i, int /*h*/) : QListWidgetItem(b) { node = t; idx = i; } //--------------------------------------------------------- // EffectRack //--------------------------------------------------------- EffectRack::EffectRack(QWidget* parent, MusECore::AudioTrack* t) : QListWidget(parent) { setObjectName("Rack"); viewport()->setObjectName("EffectRack"); // needed for context help setStatusTip(tr("Effect rack: Double-click a slot to insert/edit effect. RMB to open context menu. Press F1 for help.")); setAttribute(Qt::WA_DeleteOnClose); _bkgPainter = new ItemBackgroundPainter(this); track = t; itemheight = 19; _style3d = true; _radius = 2; _customScrollbar = true; setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); ensurePolished(); if (_customScrollbar) { // FIXME: put into external stylesheet // I tried, but there is a bug in QT, not possible to address scrollbar in individual widget (kybos) QFile file(":/qss/scrollbar_small_vertical.qss"); file.open(QFile::ReadOnly); QString style = file.readAll(); style.replace("darkgrey", MusEGlobal::config.rackItemBackgroundColor.name()); style.replace("lightgrey", MusEGlobal::config.rackItemBackgroundColor.lighter().name()); style.replace("grey", MusEGlobal::config.rackItemBackgroundColor.darker().name()); verticalScrollBar()->setStyleSheet(style); } setSelectionMode(QAbstractItemView::SingleSelection); for (int i = 0; i < MusECore::PipelineDepth; ++i) new RackSlot(this, track, i, itemheight); updateContents(); connect(this, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(doubleClicked(QListWidgetItem*))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); EffectRackDelegate* er_delegate = new EffectRackDelegate(this, track); setItemDelegate(er_delegate); viewport()->setAttribute( Qt::WA_Hover ); setSpacing(0); setAcceptDrops(true); setFocusPolicy(Qt::NoFocus); } void EffectRack::updateContents() { if(!track) return; MusECore::Pipeline* pipe = track->efxPipe(); if(!pipe) return; for (int i = 0; i < MusECore::PipelineDepth; ++i) { const QString name = pipe->name(i); const QString uri = pipe->uri(i); item(i)->setText(name); const QString ttname = name + (uri.isEmpty() ? QString() : QString(" \n") + uri); item(i)->setToolTip(pipe->empty(i) ? tr("Effect rack\nDouble-click a slot to insert FX") : ttname ); //item(i)->setBackground(track->efxPipe()->isOn(i) ? activeColor : palette().dark()); if(viewport()) { QRect r(visualItemRect(item(i))); viewport()->update(r); } } } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void EffectRack::songChanged(MusECore::SongChangedStruct_t typ) { if (typ & (SC_TRACK_REMOVED)) { if(!MusEGlobal::song->trackExists(track)) { track = nullptr; return; } } if (typ & (SC_ROUTE | SC_RACK)) { updateContents(); } } //--------------------------------------------------------- // minimumSizeHint //--------------------------------------------------------- QSize EffectRack::minimumSizeHint() const { return QSize(10, 2 * frameWidth() + (fontMetrics().height() + 2 * EffectRackDelegate::itemYMargin + 2 * EffectRackDelegate::itemTextYMargin) * MusEGlobal::config.audioEffectsRackVisibleItems); } //--------------------------------------------------------- // SizeHint //--------------------------------------------------------- QSize EffectRack::sizeHint() const { return minimumSizeHint(); } void EffectRack::choosePlugin(QListWidgetItem* it, bool replace) { if(!it || !track) return; MusECore::Plugin* plugin = PluginDialog::getPlugin(this); if (plugin) { MusECore::PluginI* plugi = new MusECore::PluginI(); if (plugi->initPluginInstance(plugin, track->channels())) { printf("cannot instantiate plugin <%s>\n", plugin->name().toLatin1().constData()); delete plugi; return; } int idx = row(it); if (replace) MusEGlobal::audio->msgAddPlugin(track, idx, nullptr); MusEGlobal::audio->msgAddPlugin(track, idx, plugi); updateContents(); } } //--------------------------------------------------------- // menuRequested //--------------------------------------------------------- void EffectRack::menuRequested(QListWidgetItem* it) { if (it == nullptr || track == nullptr) return; RackSlot* curitem = (RackSlot*)it; int idx = row(curitem); QString name; //bool mute; MusECore::Pipeline* pipe = track->efxPipe(); if (pipe) { name = pipe->name(idx); //mute = pipe->isOn(idx); } //enum { NEW, CHANGE, UP, DOWN, REMOVE, BYPASS, SHOW, SAVE }; enum { NEW, CHANGE, UP, DOWN, REMOVE, BYPASS, SHOW, SHOW_NATIVE, SAVE }; QMenu* menu = new QMenu; QAction* newAction = menu->addAction(*dummySVGIcon, tr("New")); QAction* changeAction = menu->addAction(tr("Change")); QAction* upAction = menu->addAction(tr("Move Up"));//, UP, UP); QAction* downAction = menu->addAction(tr("Move Down"));//, DOWN, DOWN); QAction* removeAction = menu->addAction(tr("Remove"));//, REMOVE, REMOVE); menu->addSeparator(); QAction* bypassAction = menu->addAction(tr("Bypass"));//, BYPASS, BYPASS); menu->addSeparator(); QAction* showGuiAction = menu->addAction(tr("Show Generic GUI"));//, SHOW, SHOW); QAction* showNativeGuiAction = menu->addAction(tr("Show Native GUI"));//, SHOW_NATIVE, SHOW_NATIVE); QAction* saveAction = menu->addAction(tr("Save Preset")); newAction->setData(NEW); changeAction->setData(CHANGE); upAction->setData(UP); downAction->setData(DOWN); removeAction->setData(REMOVE); bypassAction->setData(BYPASS); showGuiAction->setData(SHOW); showNativeGuiAction->setData(SHOW_NATIVE); saveAction->setData(SAVE); bypassAction->setCheckable(true); showGuiAction->setCheckable(true); showNativeGuiAction->setCheckable(true); bypassAction->setChecked(!pipe->isOn(idx)); showGuiAction->setChecked(pipe->guiVisible(idx)); showNativeGuiAction->setChecked(pipe->nativeGuiVisible(idx)); #ifdef LV2_SUPPORT PopupMenu *mSubPresets = nullptr; #endif if (pipe->empty(idx)) { menu->removeAction(changeAction); menu->removeAction(saveAction); upAction->setEnabled(false); downAction->setEnabled(false); removeAction->setEnabled(false); bypassAction->setEnabled(false); showGuiAction->setEnabled(false); showNativeGuiAction->setEnabled(false); } else { menu->removeAction(newAction); if (idx == 0) upAction->setEnabled(true); if (idx == (MusECore::PipelineDepth-1)) downAction->setEnabled(false); //if(!pipe->isDssiPlugin(idx)) if(!pipe->has_dssi_ui(idx)) // p4.0.19 Tim. showNativeGuiAction->setEnabled(false); #ifdef LV2_SUPPORT //show presets submenu for lv2 plugins mSubPresets = new PopupMenu(tr("Presets")); if(pipe->isLV2Plugin(idx)) { menu->addMenu(mSubPresets); MusECore::PluginI *plugI = pipe->at(idx); static_cast(plugI->plugin())->populatePresetsMenu(plugI, mSubPresets); } else { delete mSubPresets; mSubPresets = nullptr; } #endif } #ifndef OSC_SUPPORT showNativeGuiAction->setEnabled(false); #endif QPoint pt = QCursor::pos(); QAction* act = menu->exec(pt, nullptr); //delete menu; if (!act) { delete menu; return; } #ifdef LV2_SUPPORT if (mSubPresets != nullptr) { QWidget *mwidget = act->parentWidget(); if (mwidget != nullptr) { if(mSubPresets == dynamic_cast(mwidget)) { MusECore::PluginI *plugI = pipe->at(idx); static_cast(plugI->plugin())->applyPreset(plugI, act->data().value()); delete menu; return; } } } #endif int sel = act->data().toInt(); delete menu; switch(sel) { case NEW: { choosePlugin(it); break; } case CHANGE: { choosePlugin(it, true); break; } case REMOVE: MusEGlobal::audio->msgAddPlugin(track, idx, nullptr); break; case BYPASS: { bool flag = !pipe->isOn(idx); pipe->setOn(idx, flag); break; } case SHOW: { bool flag = !pipe->guiVisible(idx); pipe->showGui(idx, flag); break; } case SHOW_NATIVE: { bool flag = !pipe->nativeGuiVisible(idx); pipe->showNativeGui(idx, flag); break; } case UP: if (idx > 0) { setCurrentItem(item(idx-1)); pipe->move(idx, true); } break; case DOWN: if (idx < (MusECore::PipelineDepth-1)) { setCurrentItem(item(idx+1)); pipe->move(idx, false); } break; case SAVE: savePreset(idx); break; } updateContents(); MusEGlobal::song->update(SC_RACK); } //--------------------------------------------------------- // doubleClicked // toggle gui //--------------------------------------------------------- void EffectRack::doubleClicked(QListWidgetItem* it) { if (it == nullptr || track == nullptr) return; RackSlot* item = (RackSlot*)it; int idx = row(item); MusECore::Pipeline* pipe = track->efxPipe(); if (!pipe) return; if (pipe->empty(idx)) { choosePlugin(it); return; } bool flag; if (pipe->has_dssi_ui(idx)) { flag = !pipe->nativeGuiVisible(idx); pipe->showNativeGui(idx, flag); } else { flag = !pipe->guiVisible(idx); pipe->showGui(idx, flag); } } void EffectRack::savePreset(int idx) { if(!track) return; //QString name = MusEGui::getSaveFileName(QString(""), plug_file_pattern, this, QString name = MusEGui::getSaveFileName(QString(""), MusEGlobal::preset_file_save_pattern, this, tr("MusE: Save Preset")); if(name.isEmpty()) return; //FILE* presetFp = fopen(name.ascii(),"w+"); bool popenFlag; FILE* presetFp = MusEGui::fileOpen(this, name, QString(".pre"), "w", popenFlag, false, true); if (presetFp == nullptr) { //fprintf(stderr, "EffectRack::savePreset() fopen failed: %s\n", // strerror(errno)); return; } MusECore::Xml xml(presetFp); MusECore::Pipeline* pipe = track->efxPipe(); if (pipe) { if ((*pipe)[idx] != nullptr) { xml.header(); xml.tag(0, "muse version=\"1.0\""); (*pipe)[idx]->writeConfiguration(1, xml); xml.tag(0, "/muse"); } else { printf("no plugin!\n"); //fclose(presetFp); if (popenFlag) pclose(presetFp); else fclose(presetFp); return; } } else { printf("no pipe!\n"); //fclose(presetFp); if (popenFlag) pclose(presetFp); else fclose(presetFp); return; } //fclose(presetFp); if (popenFlag) pclose(presetFp); else fclose(presetFp); } void EffectRack::startDragItem(int idx) { if(!track) return; if (idx < 0) { printf("illegal to drag index %d\n",idx); return; } FILE *tmp; if (MusEGlobal::debugMsg) { QString fileName; MusEGlobal::getUniqueTmpfileName("tmp","preset", fileName); tmp = fopen(fileName.toLatin1().data(), "w+"); } else tmp = tmpfile(); if (tmp == nullptr) { fprintf(stderr, "EffectRack::startDrag fopen failed: %s\n", strerror(errno)); return; } MusECore::Xml xml(tmp); MusECore::Pipeline* pipe = track->efxPipe(); if (pipe) { if ((*pipe)[idx] != nullptr) { xml.header(); xml.tag(0, "muse version=\"1.0\""); (*pipe)[idx]->writeConfiguration(1, xml); xml.tag(0, "/muse"); } else { //printf("no plugin!\n"); return; } } else { //printf("no pipe!\n"); return; } QString xmlconf; xml.dump(xmlconf); QMimeData* md = new QMimeData(); QByteArray data(xmlconf.toLatin1().constData()); if (MusEGlobal::debugMsg) printf("Sending %d [%s]\n", data.length(), xmlconf.toLatin1().constData()); md->setData(MUSE_MIME_TYPE, data); QDrag* drag = new QDrag(this); drag->setMimeData(md); drag->exec(Qt::CopyAction); } Qt::DropActions EffectRack::supportedDropActions () const { return Qt::CopyAction | Qt::MoveAction; } QStringList EffectRack::mimeTypes() const { QStringList mTypes; mTypes << "text/uri-list"; mTypes << MUSE_MIME_TYPE; return mTypes; } void EffectRack::dropEvent(QDropEvent *event) { if(!event || !track) return; QListWidgetItem *i = itemAt( event->pos() ); if (!i) return; int idx = row(i); MusECore::Pipeline* pipe = track->efxPipe(); if (pipe) { if ((*pipe)[idx] != nullptr) { QWidget *sw = static_cast(event->source()); if(sw) { if(strcmp(sw->metaObject()->className(), "EffectRack") == 0) { EffectRack *ser = (EffectRack*)sw; MusECore::Pipeline* spipe = ser->getTrack()->efxPipe(); if(!spipe) return; QListWidgetItem *i = ser->itemAt(ser->getDragPos()); int idx0 = ser->row(i); if (!(*spipe)[idx0] || (idx == idx0 && (ser == this || ser->getTrack()->name() == track->name()))) return; } } if(QMessageBox::question(this, tr("Replace effect"),tr("Do you really want to replace the effect %1?").arg(pipe->name(idx)), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) { MusEGlobal::audio->msgAddPlugin(track, idx, nullptr); MusEGlobal::song->update(SC_RACK); } else { return; } } if(event->mimeData()->hasFormat(MUSE_MIME_TYPE)) { QByteArray mimeData = event->mimeData()->data(MUSE_MIME_TYPE).constData(); MusECore::Xml xml(mimeData.constData()); if (MusEGlobal::debugMsg) printf("received %d [%s]\n", mimeData.size(), mimeData.constData()); initPlugin(xml, idx); } else if (event->mimeData()->hasUrls()) { // Multiple urls not supported here. Grab the first one. QString text = event->mimeData()->urls()[0].path(); if (text.endsWith(".pre", Qt::CaseInsensitive) || text.endsWith(".pre.gz", Qt::CaseInsensitive) || text.endsWith(".pre.bz2", Qt::CaseInsensitive)) { bool popenFlag; FILE* fp = MusEGui::fileOpen(this, text, ".pre", "r", popenFlag, false, false); if (fp) { MusECore::Xml xml(fp); initPlugin(xml, idx); if (popenFlag) pclose(fp); else fclose(fp); } } } } } void EffectRack::dragEnterEvent(QDragEnterEvent *event) { event->acceptProposedAction(); // TODO CHECK Tim. } void EffectRack::mousePressEvent(QMouseEvent *event) { if(event && track) { RackSlot* item = (RackSlot*) itemAt(event->pos()); if(event->button() & Qt::LeftButton) { dragPos = event->pos(); } else if(event->button() & Qt::RightButton) { setCurrentItem(item); menuRequested(item); return; } else if(event->button() & Qt::MidButton) { int idx = row(item); bool flag = !track->efxPipe()->isOn(idx); track->efxPipe()->setOn(idx, flag); updateContents(); } } QListWidget::mousePressEvent(event); } void EffectRack::mouseMoveEvent(QMouseEvent *event) { if(event && track) { if (event->buttons() & Qt::LeftButton) { MusECore::Pipeline* pipe = track->efxPipe(); if(!pipe) return; QListWidgetItem *i = itemAt(dragPos); int idx0 = row(i); if (!(*pipe)[idx0]) return; int distance = (dragPos-event->pos()).manhattanLength(); if (distance > QApplication::startDragDistance()) { QListWidgetItem *i = itemAt( event->pos() ); if (i) { int idx = row(i); startDragItem(idx); } } } } QListWidget::mouseMoveEvent(event); } void EffectRack::enterEvent(QEvent *event) { setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); QListWidget::enterEvent(event); } void EffectRack::leaveEvent(QEvent *event) { setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QListWidget::leaveEvent(event); } void EffectRack::initPlugin(MusECore::Xml xml, int idx) { if(!track) return; for (;;) { MusECore::Xml::Token token = xml.parse(); QString tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "plugin") { MusECore::PluginI* plugi = new MusECore::PluginI(); if (plugi->readConfiguration(xml, false)) { //QString d; //xml.dump(d); //printf("cannot instantiate plugin [%s]\n", d.toLatin1().data()); delete plugi; } else { //printf("instantiated!\n"); MusEGlobal::audio->msgAddPlugin(track, idx, plugi); MusEGlobal::song->update(SC_RACK); if (plugi->guiVisible()) plugi->gui()->updateWindowTitle(); return; } } else if (tag =="muse") break; else xml.unknown("EffectRack"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "muse") return; default: break; } } } } // namespace MusEGui muse-4.0.0/src/muse/mixer/rack.h000066400000000000000000000067061404105311000164370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: rack.h,v 1.5.2.3 2006/09/24 19:32:31 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __RACK_H__ #define __RACK_H__ #include #include "type_defs.h" #include class QDragEnterEvent; class QDragLeaveEvent; class QDropEvent; class QMouseEvent; class QEvent; namespace MusECore { class AudioTrack; class Xml; } namespace MusEGui { class ItemBackgroundPainter; //--------------------------------------------------------- // EffectRack //--------------------------------------------------------- class EffectRack : public QListWidget { Q_OBJECT Q_PROPERTY( bool style3d READ style3d WRITE setStyle3d ) Q_PROPERTY( int radius READ radius WRITE setRadius ) Q_PROPERTY( bool customScrollbar READ customScrollbar WRITE setCustomScrollbar ) bool _style3d; int _radius; bool _customScrollbar; MusECore::AudioTrack* track; int itemheight; QColor _activeColor; ItemBackgroundPainter* _bkgPainter; virtual QSize minimumSizeHint() const; virtual QSize sizeHint() const; void startDragItem(int idx); void initPlugin(MusECore::Xml xml, int idx); QPoint dragPos; void savePreset(int idx); void choosePlugin(QListWidgetItem* item, bool replace = false); private slots: void menuRequested(QListWidgetItem*); void doubleClicked(QListWidgetItem*); void songChanged(MusECore::SongChangedStruct_t); void updateContents(); protected: void dropEvent(QDropEvent *event); void dragEnterEvent(QDragEnterEvent *event); void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void enterEvent(QEvent *event); void leaveEvent(QEvent *event); QStringList mimeTypes() const; Qt::DropActions supportedDropActions () const; public: EffectRack(QWidget*, MusECore::AudioTrack* t); MusECore::AudioTrack* getTrack() const { return track; } QPoint getDragPos() const { return dragPos; } ItemBackgroundPainter* getBkgPainter() const { return _bkgPainter; } bool style3d() const { return _style3d; } void setStyle3d(const bool style3d) { _style3d = style3d; } int radius() const { return _radius; } void setRadius(const int radius) { _radius = radius; } bool customScrollbar() const { return _customScrollbar; } void setCustomScrollbar(const bool customScrollbar) { _customScrollbar = customScrollbar; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mixer/strip.cpp000066400000000000000000002726211404105311000172140ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: strip.cpp,v 1.6.2.5 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011 - 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include #include #include #include #include #include #include #include #include #include #include "globals.h" #include "gconfig.h" #include "app.h" #include "midiport.h" #include "audio.h" #include "song.h" #include "strip.h" #include "utils.h" #include "muse_math.h" #include "ctrl.h" #include "midi_consts.h" #include "midictrl.h" #include "icons.h" #include "undo.h" #include "operations.h" #include "amixer.h" #include "menutitleitem.h" #include "shortcuts.h" // Forwards from header: #include #include #include #include #include #include "track.h" #include "combobox.h" #include "compact_knob.h" #include "compact_slider.h" #include "pixmap_button.h" #include "meter.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_STRIP(dev, format, args...) // fprintf(dev, format, ##args); using MusECore::UndoOp; namespace MusEGui { //--------------------------------------------------------- // ComponentRack //--------------------------------------------------------- ComponentRack::ComponentRack(int id, QWidget* parent, Qt::WindowFlags f) : QFrame(parent, f), _id(id) { _layout = new ComponentRackLayout(this); // Install a layout manager. _layout->setSpacing(0); _layout->setContentsMargins(0, 0, 0, 0); } ComponentWidget* ComponentRack::findComponent( ComponentWidget::ComponentType componentType, int componentWidgetType, int index, QWidget* widget) { iComponentWidget icw = _components.find(componentType, componentWidgetType, index, widget); if(icw != _components.end()) return &(*icw); return 0; } void ComponentRack::clearDelete() { for(iComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { ComponentWidget& cw = *ic; if(cw._widget) cw._widget->deleteLater(); } _components.clear(); // layout items must be removed too to avoid artefacts! (kybos) while(_layout->takeAt(0)); } void ComponentRack::addComponentWidget( const ComponentWidget& cw, const ComponentWidget& before ) { if(cw._widget) { int idx = -1; if(before.isValid()) { iComponentWidget ibcw = _components.find(before); if(ibcw == _components.end()) { DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' item not found. Pushing back.\n"); _components.push_back(cw); } else { idx = _layout->indexOf(before._widget); if(idx == -1) { DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' widget not found. Pushing back.\n"); _components.push_back(cw); } else { DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' widget found. Inserting. Layout idx:%d.\n", idx); _components.insert(ibcw, cw); } } } else { DEBUG_STRIP(stderr, "ComponentRack::addComponent: 'before' item not valid. Pushing back.\n"); _components.push_back(cw); } if(idx == -1) _layout->addWidget(cw._widget); else _layout->insertWidget(idx, cw._widget); } } void ComponentRack::newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before ) { QPalette pal(palette()); ComponentWidget cw; switch(desc->_widgetType) { case CompactKnobComponentWidget: { CompactKnobComponentDescriptor* d = static_cast(desc); if(!d->_compactKnob) { CompactKnob* control = new CompactKnob(nullptr, d->_objName, CompactKnob::Right, d->_label); d->_compactKnob = control; control->setId(d->_index); control->setRange(d->_min, d->_max, d->_step); control->setValueDecimals(d->_precision); control->setSpecialValueText(d->_specialValueText); control->setHasOffMode(d->_hasOffMode); control->setValueState(d->_initVal, d->_isOff); control->setValPrefix(d->_prefix); control->setValSuffix(d->_suffix); control->setShowValue(d->_showValue); // Do not set. Compact knob needs to manage it's own tooltips. //control->setToolTip(d->_toolTipText); control->setEnabled(d->_enabled); control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); control->setContentsMargins(0, 0, 0, 0); if(d->_color.isValid()) control->setFaceColor(d->_color); //if(d->_rimColor.isValid()) // control->setRimColor(d->_rimColor); if(d->_faceColor.isValid()) control->setFaceColor(d->_faceColor); if(d->_shinyColor.isValid()) control->setShinyColor(d->_shinyColor); //control->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); switch(d->_componentType) { case controllerComponent: connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(controllerChanged(double,bool,int,int))); connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(controllerMoved(double,int,bool))); connect(control, SIGNAL(sliderPressed(double, int)), SLOT(controllerPressed(double, int))); connect(control, SIGNAL(sliderReleased(double, int)), SLOT(controllerReleased(double, int))); connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(controllerRightClicked(QPoint,int))); break; case propertyComponent: connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(propertyChanged(double,bool,int,int))); connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(propertyMoved(double,int,bool))); connect(control, SIGNAL(sliderPressed(double, int)), SLOT(propertyPressed(double, int))); connect(control, SIGNAL(sliderReleased(double, int)), SLOT(propertyReleased(double, int))); connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(propertyRightClicked(QPoint,int))); break; } } cw = ComponentWidget( d->_compactKnob, d->_widgetType, d->_componentType, d->_index ); } break; case CompactSliderComponentWidget: { CompactSliderComponentDescriptor* d = static_cast(desc); if(!d->_compactSlider) { CompactSlider* control = new CompactSlider(nullptr, d->_objName, Qt::Horizontal, CompactSlider::None, d->_label); d->_compactSlider = control; control->setId(d->_index); control->setRange(d->_min, d->_max, d->_step); control->setValueDecimals(d->_precision); control->setSpecialValueText(d->_specialValueText); control->setHasOffMode(d->_hasOffMode); control->setValueState(d->_initVal, d->_isOff); control->setValPrefix(d->_prefix); control->setValSuffix(d->_suffix); control->setShowValue(d->_showValue); control->setActiveBorders(d->_activeBorders); control->setToolTip(d->_toolTipText); control->setEnabled(d->_enabled); control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); control->setContentsMargins(0, 0, 0, 0); if(d->_color.isValid()) control->setBorderColor(d->_color); if(d->_barColor.isValid()) control->setBarColor(d->_barColor); if(d->_slotColor.isValid()) control->setSlotColor(d->_slotColor); if(d->_thumbColor.isValid()) control->setThumbColor(d->_thumbColor); control->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); switch(d->_componentType) { case controllerComponent: connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(controllerChanged(double,bool,int,int))); connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(controllerMoved(double,int,bool))); connect(control, SIGNAL(sliderPressed(double, int)), SLOT(controllerPressed(double, int))); connect(control, SIGNAL(sliderReleased(double, int)), SLOT(controllerReleased(double, int))); connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(controllerRightClicked(QPoint,int))); break; case propertyComponent: connect(control, SIGNAL(valueStateChanged(double,bool,int, int)), SLOT(propertyChanged(double,bool,int,int))); connect(control, SIGNAL(sliderMoved(double,int,bool)), SLOT(propertyMoved(double,int,bool))); connect(control, SIGNAL(sliderPressed(double, int)), SLOT(propertyPressed(double, int))); connect(control, SIGNAL(sliderReleased(double, int)), SLOT(propertyReleased(double, int))); connect(control, SIGNAL(sliderRightClicked(QPoint,int)), SLOT(propertyRightClicked(QPoint,int))); break; } } cw = ComponentWidget( d->_compactSlider, d->_widgetType, d->_componentType, d->_index ); } break; case ElidedLabelComponentWidget: { ElidedLabelComponentDescriptor* d = static_cast(desc); if(!d->_elidedLabel) { ElidedLabel* control = new ElidedLabel(nullptr, d->_elideMode); d->_elidedLabel = control; control->setObjectName(d->_objName); control->setId(d->_index); control->setToolTip(d->_toolTipText); control->setEnabled(d->_enabled); control->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); control->setContentsMargins(0, 0, 0, 0); // if(d->_color.isValid()) // { // pal.setColor(QPalette::Active, QPalette::Button, d->_color); // Border // pal.setColor(QPalette::Inactive, QPalette::Button, d->_color); // Border // control->setPalette(pal); // } if(d->_bgColor.isValid()) control->setBgColor(d->_bgColor); if(d->_bgActiveColor.isValid()) control->setBgActiveColor(d->_bgActiveColor); if(d->_color.isValid()) control->setBorderColor(d->_color); if(d->_fontColor.isValid()) control->setFontColor(d->_fontColor); if(d->_fontActiveColor.isValid()) control->setFontActiveColor(d->_fontActiveColor); switch(d->_componentType) { case propertyComponent: connect(control, SIGNAL(pressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)), SLOT(labelPropertyPressed(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers))); connect(control, SIGNAL(released(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers)), SLOT(labelPropertyReleased(QPoint,int,Qt::MouseButtons,Qt::KeyboardModifiers))); connect(control, SIGNAL(returnPressed(QPoint,int,Qt::KeyboardModifiers)), SLOT(labelPropertyReturnPressed(QPoint,int,Qt::KeyboardModifiers))); break; } } cw = ComponentWidget( d->_elidedLabel, d->_widgetType, d->_componentType, d->_index ); } break; case ExternalComponentWidget: { WidgetComponentDescriptor* d = static_cast(desc); QWidget* widget = d->_widget; if(widget) { widget->setToolTip(d->_toolTipText); widget->setEnabled(d->_enabled); widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); if(d->_color.isValid()) { pal.setColor(QPalette::Active, QPalette::Button, d->_color); // Border pal.setColor(QPalette::Inactive, QPalette::Button, d->_color); // Border widget->setPalette(pal); } cw = ComponentWidget( d->_widget, d->_widgetType, d->_componentType, d->_index ); } } break; } if(cw._widget) addComponentWidget(cw, before); } void ComponentRack::setComponentMinValue(const ComponentWidget& cw, double min, bool updateOnly) { if(!cw._widget) return; switch(cw._widgetType) { case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); if(min != w->minValue()) { if(updateOnly) w->blockSignals(true); w->setMinValue(min); if(updateOnly) w->blockSignals(false); } } break; case CompactKnobComponentWidget: { CompactKnob* w = static_cast(cw._widget); if(min != w->minValue()) { if(updateOnly) w->blockSignals(true); w->setMinValue(min); if(updateOnly) w->blockSignals(false); } } break; } } void ComponentRack::setComponentMaxValue(const ComponentWidget& cw, double max, bool updateOnly) { if(!cw._widget) return; switch(cw._widgetType) { case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); if(max != w->maxValue()) { if(updateOnly) w->blockSignals(true); w->setMaxValue(max); if(updateOnly) w->blockSignals(false); } } break; case CompactKnobComponentWidget: { CompactKnob* w = static_cast(cw._widget); if(max != w->maxValue()) { if(updateOnly) w->blockSignals(true); w->setMaxValue(max); if(updateOnly) w->blockSignals(false); } } break; } } double ComponentRack::componentMinValue(const ComponentWidget& cw) const { if(cw._widget) { switch(cw._widgetType) { case CompactSliderComponentWidget: return static_cast(cw._widget)->minValue(); break; case CompactKnobComponentWidget: return static_cast(cw._widget)->minValue(); break; } } return 0.0; } double ComponentRack::componentMaxValue(const ComponentWidget& cw) const { if(cw._widget) { switch(cw._widgetType) { case CompactSliderComponentWidget: return static_cast(cw._widget)->maxValue(); break; case CompactKnobComponentWidget: return static_cast(cw._widget)->maxValue(); break; } } return 0.0; } void ComponentRack::setComponentRange(const ComponentWidget& cw, double min, double max, bool updateOnly, double step, int pageSize, DoubleRange::ConversionMode mode) { if(!cw._widget) return; switch(cw._widgetType) { case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); if(min != w->minValue() || max != w->maxValue()) { if(updateOnly) w->blockSignals(true); if(min != w->minValue() && max != w->maxValue()) w->setRange(min, max, step, pageSize, mode); else if(min != w->minValue()) w->setMinValue(max); else w->setMaxValue(max); if(updateOnly) w->blockSignals(false); } } break; case CompactKnobComponentWidget: { CompactKnob* w = static_cast(cw._widget); if(min != w->minValue() || max != w->maxValue()) { if(updateOnly) w->blockSignals(true); if(min != w->minValue() && max != w->maxValue()) w->setRange(min, max, step, pageSize, mode); else if(min != w->minValue()) w->setMinValue(max); else w->setMaxValue(max); if(updateOnly) w->blockSignals(false); } } break; } } double ComponentRack::componentValue(const ComponentWidget& cw) const { if(cw._widget) { switch(cw._widgetType) { case CompactSliderComponentWidget: return static_cast(cw._widget)->value(); break; case CompactKnobComponentWidget: return static_cast(cw._widget)->value(); break; } } return 0.0; } void ComponentRack::setComponentValue(const ComponentWidget& cw, double val, bool updateOnly) { if(!cw._widget) return; switch(cw._widgetType) { case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); //if(val != cw->_currentValue) // TODO ? if(val != w->value()) { if(updateOnly) w->blockSignals(true); w->setValue(val); if(updateOnly) w->blockSignals(false); //cw->_currentValue = val; // TODO ? } } break; case CompactKnobComponentWidget: { CompactKnob* w = static_cast(cw._widget); //if(val != cw->_currentValue) // TODO ? if(val != w->value()) { if(updateOnly) w->blockSignals(true); w->setValue(val); if(updateOnly) w->blockSignals(false); //cw->_currentValue = val; // TODO ? } } break; } } void ComponentRack::fitComponentValue(const ComponentWidget& cw, double val, bool updateOnly) { if(!cw._widget) return; switch(cw._widgetType) { case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); //if(val != cw->_currentValue) // TODO ? if(val != w->value()) { if(updateOnly) w->blockSignals(true); w->fitValue(val); if(updateOnly) w->blockSignals(false); //cw->_currentValue = val; // TODO ? } } break; case CompactKnobComponentWidget: { CompactKnob* w = static_cast(cw._widget); //if(val != cw->_currentValue) // TODO ? if(val != w->value()) { if(updateOnly) w->blockSignals(true); w->fitValue(val); if(updateOnly) w->blockSignals(false); //cw->_currentValue = val; // TODO ? } } break; } } void ComponentRack::incComponentValue(const ComponentWidget& cw, int steps, bool updateOnly) { if(!cw._widget) return; switch(cw._widgetType) { case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); if(updateOnly) w->blockSignals(true); w->incValue(steps); if(updateOnly) w->blockSignals(false); } break; case CompactKnobComponentWidget: { CompactKnob* w = static_cast(cw._widget); if(updateOnly) w->blockSignals(true); w->incValue(steps); if(updateOnly) w->blockSignals(false); } break; } } void ComponentRack::setComponentText(const ComponentWidget& cw, const QString& text, bool updateOnly) { if(!cw._widget) return; switch(cw._widgetType) { case ElidedLabelComponentWidget: { ElidedLabel* w = static_cast(cw._widget); if(text != w->text()) { if(updateOnly) w->blockSignals(true); w->setText(text); if(updateOnly) w->blockSignals(false); } } break; case CompactKnobComponentWidget: { CompactKnob* w = static_cast(cw._widget); if(text != w->labelText()) { if(updateOnly) w->blockSignals(true); w->setLabelText(text); if(updateOnly) w->blockSignals(false); } } break; case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); if(text != w->labelText()) { if(updateOnly) w->blockSignals(true); w->setLabelText(text); if(updateOnly) w->blockSignals(false); } } break; } } void ComponentRack::setComponentEnabled(const ComponentWidget& cw, bool enable, bool /*updateOnly*/) { if(!cw._widget) return; // Nothing special for now. Just operate on the widget itself. cw._widget->setEnabled(enable); } void ComponentRack::setComponentShowValue(const ComponentWidget& cw, bool show, bool updateOnly) { if(!cw._widget) return; switch(cw._widgetType) { case CompactKnobComponentWidget: { CompactKnob* w = static_cast(cw._widget); if(show != w->showValue()) { if(updateOnly) w->blockSignals(true); w->setShowValue(show); if(updateOnly) w->blockSignals(false); } } break; case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); if(show != w->showValue()) { if(updateOnly) w->blockSignals(true); w->setShowValue(show); if(updateOnly) w->blockSignals(false); } } break; } } QWidget* ComponentRack::setupComponentTabbing(QWidget* previousWidget) { QWidget* prev = previousWidget; for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { const ComponentWidget& cw = *ic; if(cw._widget) { if(prev) QWidget::setTabOrder(prev, cw._widget); prev = cw._widget; } } return prev; } //--------------------------------------------------------- // configChanged // Catch when label font, or configuration min slider and meter values change, or viewable tracks etc. //--------------------------------------------------------- void ComponentRack::configChanged() { // FIXME For some reason we have to set the font and stylesheet here as well as the strip. // Strip font and stylesheet changes don't seem to be propagated to these racks. // FIXME They also don't seem to be propagated to our CompactPatchEdit component on the midi strip. if(font() != MusEGlobal::config.fonts[1]) { setFont(MusEGlobal::config.fonts[1]); // should be redundant, overridden by style sheet setStyleSheet(MusECore::font2StyleSheetFull(MusEGlobal::config.fonts[1])); } for(ciComponentWidget ic = _components.begin(); ic != _components.end(); ++ic) { const ComponentWidget& cw = *ic; if(!cw._widget) continue; switch(cw._widgetType) { case CompactKnobComponentWidget: { //CompactKnob* w = static_cast(cw._widget); //w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); } break; case CompactSliderComponentWidget: { CompactSlider* w = static_cast(cw._widget); w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); } break; // case ElidedLabelComponentWidget: // { // ElidedLabel* w = static_cast(cw._widget); // //w->setMaxAliasedPointSize(MusEGlobal::config.maxAliasedPointSize); // } // break; default: break; } } } //--------------------------------------------------------- // TrackNameLabel //--------------------------------------------------------- const int TrackNameLabel::_expandIconWidth = 14; TrackNameLabel::TrackNameLabel(QWidget* parent) : QLabel(parent) { _style3d = true; _hasExpandIcon = false; _expandIconPressed = false; _hovered = false; } //TrackNameLabel::TrackNameLabel(const QString& text, QWidget* parent, const char* name, Qt::WindowFlags f) // : QLabel(text, parent, name, f) //{ // _style3d = true; // _hasExpandIcon = false; // _expandIconPressed = false; //} void TrackNameLabel::mouseDoubleClickEvent(QMouseEvent* ev) { ev->accept(); if (hasExpandIcon() && _hovered && (ev->pos().x() < _expandIconWidth)) return; emit doubleClicked(); } void TrackNameLabel::paintEvent(QPaintEvent* ev) { ev->ignore(); QLabel::paintEvent(ev); if(!hasExpandIcon() || !_hovered) return; if(rect().width() <= 0 || rect().height() <= 0) return; QPainter p(this); p.save(); const QRect r = rect(); p.fillRect(r.x(), r.y(), _expandIconWidth, r.height(), palette().mid()); //expandLeftRightSVGIcon->paint(&p, r.x() + r.width() - _expandIconWidth, r.y(), _expandIconWidth, r.height()); expandLeftRightSVGIcon->paint(&p, r.x(), r.y(), _expandIconWidth, r.height()); p.restore(); } void TrackNameLabel::mousePressEvent(QMouseEvent* ev) { if (hasExpandIcon() && _hovered && (ev->pos().x() < _expandIconWidth)) { _expandIconPressed = true; ev->accept(); emit expandClicked(); } else { ev->ignore(); QLabel::mousePressEvent(ev); } } void TrackNameLabel::mouseReleaseEvent(QMouseEvent* ev) { if(_expandIconPressed) { _expandIconPressed = false; ev->accept(); } else { ev->ignore(); QLabel::mouseReleaseEvent(ev); } } void TrackNameLabel::mouseMoveEvent(QMouseEvent* ev) { if(_expandIconPressed) { ev->accept(); } else { ev->ignore(); QLabel::mouseMoveEvent(ev); } } void TrackNameLabel::leaveEvent(QEvent *e) { if(_hovered) { _hovered = false; update(); } e->ignore(); QLabel::leaveEvent(e); } void TrackNameLabel::enterEvent(QEvent *e) { if (!_hovered) { _hovered = true; update(); } e->ignore(); QLabel::enterEvent(e); } //--------------------------------------------------------- // Strip //--------------------------------------------------------- //const int Strip::FIXED_METER_WIDTH = 7; //--------------------------------------------------------- // setRecordFlag //--------------------------------------------------------- void Strip::setRecordFlag(bool flag) { if (record) { record->blockSignals(true); record->setChecked(flag); record->blockSignals(false); } if (!flag) { for (const auto& it : *MusEGlobal::song->tracks()) { if (it->canRecord() && it->recordFlag()) return; } MusEGlobal::song->setRecord(false); } } //--------------------------------------------------------- // resetPeaks //--------------------------------------------------------- void Strip::resetPeaks() { track->resetPeaks(); } //--------------------------------------------------------- // recordToggled //--------------------------------------------------------- void Strip::recordToggled(bool val) { if (track->type() == MusECore::Track::AUDIO_OUTPUT) { if (val && !track->recordFlag()) { MusEGlobal::muse->bounceToFile((MusECore::AudioOutput*)track); if (!((MusECore::AudioOutput*)track)->recFile()) { if(record) { record->blockSignals(true); record->setChecked(false); record->blockSignals(false); } } } return; } MusEGlobal::song->setRecordFlag(track, val); } //--------------------------------------------------------- // returnPressed //--------------------------------------------------------- void Strip::changeTrackName() { if(!track) return; const QString oldname = track->name(); QInputDialog dlg(this); dlg.setWindowTitle(tr("Track Name")); dlg.setLabelText(tr("Enter track name:")); dlg.setTextValue(oldname); // set standard font size explicitly, otherwise the font is too small (inherited from mixer strip) dlg.setStyleSheet("font-size:" + QString::number(MusEGlobal::config.fonts[0].pointSize()) + "pt"); const int res = dlg.exec(); if(res == QDialog::Rejected) return; const QString newname = dlg.textValue(); if(newname == oldname) return; MusECore::TrackList* tl = MusEGlobal::song->tracks(); for (MusECore::iTrack i = tl->begin(); i != tl->end(); ++i) { if ((*i)->name() == newname) { QMessageBox::critical(this, tr("MusE: Bad Trackname"), tr("Please choose a unique track name"), QMessageBox::Ok, Qt::NoButton, Qt::NoButton); return; } } MusEGlobal::song->applyOperation( MusECore::UndoOp(MusECore::UndoOp::ModifyTrackName, track, oldname, newname)); } //--------------------------------------------------------- // trackNameLabelExpandClicked //--------------------------------------------------------- void Strip::trackNameLabelExpandClicked() { setExpanded(!isExpanded()); } //--------------------------------------------------------- // heartBeat //--------------------------------------------------------- void Strip::heartBeat() { } void Strip::paintEvent(QPaintEvent * ev) { QFrame::paintEvent(ev); QPainter p(this); if (_highlight) { QPen pen(Qt::yellow); pen.setWidth(1); p.setPen(pen); p.drawRect(0,0,width()-1,height()-1); } ev->accept(); } //--------------------------------------------------------- // setLabelText //--------------------------------------------------------- void Strip::setLabelText() { if(!track) return; QFont fnt(MusEGlobal::config.fonts[6]); const bool fit = MusECore::autoAdjustFontSize(label, track->name(), fnt, false, true, fnt.pointSize(), 7); if (fit) { label->setText(track->name()); label->setToolTip(QString()); } else { QFontMetrics fm = QFontMetrics(fnt); QString elidedText = fm.elidedText(track->name(), Qt::ElideMiddle, label->width()); label->setText(elidedText); label->setToolTip(track->name()); } if (track->isSynthTrack()) { MusECore::SynthI *s = static_cast(track); if(!s->uri().isEmpty()) label->setToolTip(QString(track->name() + "\n") + s->uri()); } QString stxt; if (label->style3d()) { QColor c(track->labelColor()); QColor c2(c.lighter()); c.setAlpha(190); c2.setAlpha(190); stxt = QString("* { background-color: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:1," "stop:0.263158 rgba(%1, %2, %3, %4), stop:0.7547368 rgba(%5, %6, %7, %8));") .arg(c2.red()).arg(c2.green()).arg(c2.blue()).arg(c2.alpha()).arg(c.red()).arg(c.green()).arg(c.blue()).arg(c.alpha()); stxt += QString("color: black;"); } else { QColor c(track->color()); if (!MusECore::isColorBright(c)) c = c.lighter(130); stxt = "QLabel { background-color:" + c.name() + ";"; if (MusECore::getPerceivedLuminance(c) < 64) stxt += QStringLiteral("color: white;"); else stxt += QStringLiteral("color: black;"); } stxt += MusECore::font2StyleSheet(fnt) + "}"; stxt += "QToolTip {font-size:" + QString::number(qApp->font().pointSize()) + "pt}"; label->setStyleSheet(stxt); } //--------------------------------------------------------- // muteToggled //--------------------------------------------------------- void Strip::muteToggled(bool val) { if(track) { // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackMute)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } updateMuteIcon(); } //--------------------------------------------------------- // soloToggled //--------------------------------------------------------- void Strip::soloToggled(bool val) { if (track && track->internalSolo()) { if (solo->isChecked()) solo->setIcon(*soloAndProxyOnSVGIcon); else solo->setIcon(*soloProxyOnAloneSVGIcon); } else { solo->setIcon(*soloStateSVGIcon); } // solo->setIconSetB(track && track->internalSolo()); if (!track) return; // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackSolo)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // Strip // create mixer strip //--------------------------------------------------------- Strip::Strip(QWidget* parent, MusECore::Track* t, bool hasHandle, bool isEmbedded) : QFrame(parent) { setObjectName("Strip"); setMouseTracking(true); setAttribute(Qt::WA_DeleteOnClose); setFrameStyle(Panel | Raised); setLineWidth(1); // Set so that strip can redirect focus proxy to volume label in descendants. // Nope. Seemed like a good idea but no. Do not allow the strip to gain focus. // Just in case it does, which is possible, keep descendants' focus proxy code // so that the slider label will be a sensible place for focus to land. setFocusPolicy(Qt::NoFocus); _focusYieldWidget = nullptr; _isEmbedded = isEmbedded; _broadcastChanges = false; _selected = false; _highlight = false; _curGridRow = 0; _userWidth = 0; _isExpanded = false; _visible = true; dragOn=false; sliderGrid = nullptr; record = nullptr; solo = nullptr; mute = nullptr; iR = nullptr; oR = nullptr; autoType = nullptr; track = t; meter[0] = nullptr; meter[1] = nullptr; setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); grid = new QGridLayout(); grid->setContentsMargins(0, 0, 0, 0); grid->setSpacing(0); _handle = nullptr; if(hasHandle) { _expanderWidth = 4; ensurePolished(); _handle = new ExpanderHandle(nullptr, _expanderWidth); connect(_handle, &ExpanderHandle::moved, [this](int d) { changeUserWidth(d); } ); QHBoxLayout* hlayout = new QHBoxLayout(this); hlayout->setContentsMargins(0, 0, 0, 0); hlayout->setSpacing(0); hlayout->addLayout(grid); hlayout->addWidget(_handle); } else { setLayout(grid); } //--------------------------------------------- // label //--------------------------------------------- label = new TrackNameLabel(this); label->setFocusPolicy(Qt::NoFocus); label->setObjectName("TrackNameLabel"); label->setContentsMargins(0, 0, 0, 0); label->setAlignment(Qt::AlignCenter); label->setAutoFillBackground(true); label->ensurePolished(); if (label->style3d()) { label->setLineWidth(2); label->setFrameStyle(Sunken | StyledPanel); label->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Minimum)); } else { label->setFrameStyle(NoFrame); label->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); label->setFixedHeight(16); } label->setHasExpandIcon(!_isEmbedded); setLabelText(); grid->addWidget(label, _curGridRow++, 0, 1, 3); connect(label, &TrackNameLabel::doubleClicked, [this]() { changeTrackName(); } ); connect(label, &TrackNameLabel::expandClicked, [this]() { trackNameLabelExpandClicked(); } ); } //--------------------------------------------------------- // Strip //--------------------------------------------------------- Strip::~Strip() { } void Strip::setFocusYieldWidget(QWidget* w) { if(_focusYieldWidget == w) return; if(_focusYieldWidget) disconnect(_focusYieldWidget, SIGNAL(destroyed(QObject*)), this, SLOT(focusYieldWidgetDestroyed(QObject*))); _focusYieldWidget = w; if(_focusYieldWidget) connect(_focusYieldWidget, SIGNAL(destroyed(QObject*)), this, SLOT(focusYieldWidgetDestroyed(QObject*))); } void Strip::focusYieldWidgetDestroyed(QObject* obj) { if(obj != _focusYieldWidget) return; //disconnect(_focusYieldWidget, SIGNAL(destroyed(QObject*)), this, SLOT(focusYieldWidgetDestroyed(QObject*))); _focusYieldWidget = nullptr; } void Strip::addGridWidget(QWidget* w, const GridPosStruct& pos, Qt::Alignment alignment) { grid->addWidget(w, pos._row, pos._col, pos._rowSpan, pos._colSpan, alignment); } void Strip::addGridLayout(QLayout* l, const GridPosStruct& pos, Qt::Alignment alignment) { grid->addLayout(l, pos._row, pos._col, pos._rowSpan, pos._colSpan, alignment); } //--------------------------------------------------------- // setAutomationType //--------------------------------------------------------- void Strip::setAutomationType(int t) { // REMOVE Tim. mixer. Removed. TESTING... // // If going to OFF mode, need to update current 'manual' values from the automation values at this time... // if(t == AUTO_OFF && track->automationType() != AUTO_OFF) // && track->automationType() != AUTO_WRITE) // { // // May have a lot to do in updateCurValues, so try using idle. // MusEGlobal::audio->msgIdle(true); // track->setAutomationType(AutomationType(t)); // if(!track->isMidiTrack()) // (static_cast(track))->controller()->updateCurValues(MusEGlobal::audio->curFramePos()); // MusEGlobal::audio->msgIdle(false); // } // else // Try it within one message. MusEGlobal::audio->msgSetTrackAutomationType(track, t); MusEGlobal::song->update(SC_AUTOMATION); } void Strip::resizeEvent(QResizeEvent* ev) { DEBUG_STRIP(stderr, "Strip::resizeEvent\n"); QFrame::resizeEvent(ev); setLabelText(); } void Strip::updateRouteButtons() { if (iR) { // iR->setIconSetB(track->noInRoute()); if (track->noInRoute()) { iR->setToolTip(MusEGlobal::noInputRoutingToolTipWarn); iR->setIcon(*routingInputUnconnectedSVGIcon); } else { iR->setToolTip(MusEGlobal::inputRoutingToolTipBase); iR->setIcon(*routingInputSVGIcon); } } if (oR) { // oR->setIconSetB(track->noOutRoute()); if (track->noOutRoute()) { oR->setToolTip(MusEGlobal::noOutputRoutingToolTipWarn); oR->setIcon(*routingOutputUnconnectedSVGIcon); } else { oR->setToolTip(MusEGlobal::outputRoutingToolTipBase); oR->setIcon(*routingOutputSVGIcon); } } } void Strip::mousePressEvent(QMouseEvent* ev) { ev->accept(); // Only one button at a time. if(ev->buttons() ^ ev->button()) return; QPoint mousePos = QCursor::pos(); mouseWidgetOffset = pos() - mousePos; if (ev->button() == Qt::RightButton) { QMenu* menu = new QMenu; menu->addAction(new MenuTitleItem(tr("Configuration"), menu)); QAction* act = menu->addAction(tr("Prefer Knobs, Not Sliders")); act->setData(int(2)); act->setCheckable(true); act->setChecked(MusEGlobal::config.preferKnobsVsSliders); act = menu->addAction(tr("Show Values in Controls")); act->setData(int(3)); act->setCheckable(true); act->setChecked(MusEGlobal::config.showControlValues); act = menu->addAction(tr("Prefer Midi Volume As Decibels")); act->setData(int(4)); act->setCheckable(true); act->setChecked(MusEGlobal::config.preferMidiVolumeDb); menu->addSeparator(); act = menu->addAction(tr("Monitor on Record-arm Automatically")); act->setData(int(5)); act->setCheckable(true); act->setChecked(MusEGlobal::config.monitorOnRecord); QMenu* audioEffectsRackVisibleItemsMenu = new QMenu(tr("Visible Audio Effects")); QActionGroup* agroup = new QActionGroup(this); agroup->setExclusive(true); for(int i = 0; i <= MusECore::PipelineDepth; ++i) { act = audioEffectsRackVisibleItemsMenu->addAction(QString::number(i)); act->setData(int(5000 + i)); act->setCheckable(true); act->setActionGroup(agroup); if(i == MusEGlobal::config.audioEffectsRackVisibleItems) act->setChecked(MusEGlobal::config.monitorOnRecord); } menu->addMenu(audioEffectsRackVisibleItemsMenu); menu->addAction(new MenuTitleItem(tr("Actions"), menu)); act = menu->addAction(tr("Change Track Name")); act->setData(int(1001)); if(!_isEmbedded) { // act = menu->addAction(tr("Remove track")); // act->setData(int(0)); // menu->addSeparator(); act = menu->addAction(tr("Hide Strip")); act->setData(int(1)); } QPoint pt = QCursor::pos(); act = menu->exec(pt, nullptr); if (!act) { delete menu; return; } DEBUG_STRIP("Menu finished, data returned %d\n", act->data().toInt()); const int sel = act->data().toInt(); const bool checked = act->isChecked(); delete menu; switch(sel) { case 0: // DEBUG_STRIP(stderr, "Strip:: delete track\n"); // MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(track), track)); break; case 1: DEBUG_STRIP(stderr, "Strip:: setStripVisible false \n"); setStripVisible(false); setVisible(false); emit visibleChanged(this, false); break; case 2: if(MusEGlobal::config.preferKnobsVsSliders != checked) { MusEGlobal::config.preferKnobsVsSliders = checked; MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version. } break; case 3: if(MusEGlobal::config.showControlValues != checked) { MusEGlobal::config.showControlValues = checked; MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version. } break; case 4: if(MusEGlobal::config.preferMidiVolumeDb != checked) { MusEGlobal::config.preferMidiVolumeDb = checked; MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version. } break; case 5: if(MusEGlobal::config.monitorOnRecord != checked) { MusEGlobal::config.monitorOnRecord = checked; MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version. } break; case 1001: changeTrackName(); break; default: if(sel >= 5000 && sel <= 5000 + MusECore::PipelineDepth) { MusEGlobal::config.audioEffectsRackVisibleItems = sel - 5000; MusEGlobal::muse->changeConfig(true); // Save settings immediately, and use simple version. } break; } ev->accept(); return; } else if (ev->button() == Qt::LeftButton) { if(!_isEmbedded) { if (ev->modifiers() & Qt::ControlModifier) { setSelected(!isSelected()); track->setSelected(isSelected()); MusEGlobal::song->update(SC_TRACK_SELECTION); } else { emit clearStripSelection(); MusEGlobal::song->selectAllTracks(false); setSelected(true); track->setSelected(true); MusEGlobal::song->update(SC_TRACK_SELECTION); } } } } QSize Strip::sizeHint() const { const QSize sz = QFrame::sizeHint(); // printf("*** Strip size hint width: %d ***\n", sz.width()); // const QSize szm = QFrame::minimumSizeHint(); // printf("*** Strip size minimum hint width: %d ***\n", szm.width()); return QSize(sz.width() + (_isExpanded ? _userWidth : 0), sz.height()); // return QSize(_isExpanded ? _userWidth : 0, sz.height()); } void Strip::setUserWidth(int w) { _userWidth = w; if(_userWidth < 0) _userWidth = 0; _isExpanded = _userWidth > 0; // grid->invalidate(); // grid->activate(); // grid->update(); // adjustSize(); updateGeometry(); } void Strip::changeUserWidth(int delta) { if(!_isExpanded) _userWidth = 0; _userWidth += delta; if(_userWidth < 0) _userWidth = 0; _isExpanded = _userWidth > 0; updateGeometry(); emit userWidthChanged(this, _userWidth); } void Strip::setExpanded(bool v) { if(_isExpanded == v) return; _isExpanded = v; if(_isExpanded && _userWidth <= 0) _userWidth = 60; updateGeometry(); } void Strip::setEmbedded(bool embed) { _isEmbedded = embed; label->setHasExpandIcon(!embed); } //============================================================ //--------------------------------------------------------- // ExpanderHandle //--------------------------------------------------------- ExpanderHandle::ExpanderHandle(QWidget* parent, int handleWidth, Qt::WindowFlags f) : QFrame(parent, f), _handleWidth(handleWidth) { setObjectName("ExpanderHandle"); setCursor(Qt::SplitHCursor); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding); setFixedWidth(_handleWidth); setContentsMargins(0, 0, 0, 0); _resizeMode = ResizeModeNone; } void ExpanderHandle::paintEvent(QPaintEvent * ev) { QPainter p(this); if(const QStyle* st = style()) { st = st->proxy(); QStyleOption o; o.initFrom(this); o.rect = rect(); o.state = QStyle::State_Active | QStyle::State_Enabled; st->drawControl(QStyle::CE_Splitter, &o, &p); } ev->accept(); } void ExpanderHandle::mousePressEvent(QMouseEvent* e) { // Only one button at a time. // if(e->buttons() ^ e->button()) // { // //_resizeMode = ResizeModeNone; // //unsetCursor(); // e->accept(); // return; // } //if(_resizeMode == ResizeModeHovering) //{ // Switch to dragging mode. //_resizeMode = ResizeModeDragging; //ev->ignore(); //return; //} switch(_resizeMode) { case ResizeModeNone: case ResizeModeHovering: _dragLastGlobPos = e->globalPos(); _resizeMode = ResizeModeDragging; e->accept(); return; break; case ResizeModeDragging: e->accept(); return; break; } e->ignore(); QFrame::mousePressEvent(e); } void ExpanderHandle::mouseMoveEvent(QMouseEvent* e) { // const QPoint p = e->pos(); switch(_resizeMode) { case ResizeModeNone: { // if(p.x() >= (width() - frameWidth()) && p.x() < width() && p.y() >= 0 && p.y() < height()) // { // _resizeMode = ResizeModeHovering; // setCursor(Qt::SizeHorCursor); // } // e->accept(); // return; } break; case ResizeModeHovering: { // if(p.x() < (width() - frameWidth()) || p.x() >= width() || p.y() < 0 || p.y() >= height()) // { // _resizeMode = ResizeModeNone; // unsetCursor(); // } // e->accept(); // return; } break; case ResizeModeDragging: { const QPoint gp = e->globalPos(); const QPoint delta = gp -_dragLastGlobPos; _dragLastGlobPos = gp; emit moved(delta.x()); e->accept(); return; } break; } e->ignore(); QFrame::mouseMoveEvent(e); } void ExpanderHandle::mouseReleaseEvent(QMouseEvent* e) { // switch(_resizeMode) // { // case ResizeModeNone: // case ResizeModeHovering: // break; // // case ResizeModeDragging: // { // const QPoint p = ev->pos(); // if(p.x() >= (width() - frameWidth()) && p.x() < width() && p.y() >= 0 && p.y() < height()) // { // _resizeMode = ResizeModeHovering; // setCursor(Qt::SizeHorCursor); // } // else // { // _resizeMode = ResizeModeNone; // unsetCursor(); // } // ev->ignore(); // return; // } // break; // } _resizeMode = ResizeModeNone; e->ignore(); QFrame::mouseReleaseEvent(e); } // void ExpanderHandle::leaveEvent(QEvent* e) // { // e->ignore(); // QFrame::leaveEvent(e); // switch(_resizeMode) // { // case ResizeModeDragging: // return; // break; // // case ResizeModeHovering: // _resizeMode = ResizeModeNone; // Fall through... // case ResizeModeNone: // unsetCursor(); // return; // break; // } // } QSize ExpanderHandle::sizeHint() const { QSize sz = QFrame::sizeHint(); sz.setWidth(_handleWidth); return sz; } void Strip::mouseReleaseEvent(QMouseEvent* ev) { ev->accept(); if (!_isEmbedded && dragOn) { emit moveStrip(this); } dragOn=false; } void Strip::mouseMoveEvent(QMouseEvent* e) { e->accept(); if(e->buttons() == Qt::LeftButton) { if(!_isEmbedded) { if(dragOn) { QPoint mousePos = QCursor::pos(); move(mousePos + mouseWidgetOffset); } else { raise(); dragOn = true; } } } } bool Strip::handleForwardedKeyPress(QKeyEvent* event) { const int kb_code = event->key() | event->modifiers(); if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_VOL_DOWN].key) { incVolume(-1); return true; } else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_VOL_UP].key) { incVolume(1); return true; } else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_PAN_LEFT].key) { incPan(-1); return true; } else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_PAN_RIGHT].key) { incPan(1); return true; } else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_VOL_DOWN_PAGE].key) { incVolume(-5); return true; } else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_VOL_UP_PAGE].key) { incVolume(5); return true; } else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_PAN_LEFT_PAGE].key) { incPan(-5); return true; } else if(kb_code == MusEGui::shortcuts[MusEGui::SHRT_MIXER_STRIP_PAN_RIGHT_PAGE].key) { incPan(5); return true; } else if (kb_code == MusEGui::shortcuts[MusEGui::SHRT_MUTE_CURRENT_TRACKS].key) { mute->setChecked(!mute->isChecked()); return true; } else if (kb_code == MusEGui::shortcuts[MusEGui::SHRT_SOLO_CURRENT_TRACKS].key) { solo->setChecked(!solo->isChecked()); return true; } return false; } void Strip::keyPressEvent(QKeyEvent* ev) { // Set to accept by default. ev->accept(); if (ev->key() == Qt::Key_Escape) { if(_focusYieldWidget) { // Yield the focus to the given widget. _focusYieldWidget->setFocus(); // Activate the window. if(!_focusYieldWidget->isActiveWindow()) { _focusYieldWidget->activateWindow(); } // Yield the focus to the given widget. _focusYieldWidget->setFocus(); // Activate the window. if(!_focusYieldWidget->isActiveWindow()) _focusYieldWidget->activateWindow(); return; } } bool handled = handleForwardedKeyPress(ev); if (handled) return; // Let mixer window or other higher up handle it. ev->ignore(); QFrame::keyPressEvent(ev); } void Strip::setSelected(bool v) { if(_selected == v) return; if(_isEmbedded) { _selected = false; return; } if (v) { if (label->style3d()) label->setFrameStyle(Raised | StyledPanel); setHighLight(true); // First time selected? Set the focus. setFocus(); } else { if (label->style3d()) label->setFrameStyle(Sunken | StyledPanel); setHighLight(false); } _selected=v; } void Strip::setHighLight(bool highlight) { _highlight = highlight; update(); } QString Strip::getLabelText() { return label->text(); } void Strip::updateMuteIcon() { if(!track) return; bool found = false; MusECore::TrackList* tl = MusEGlobal::song->tracks(); for(MusECore::ciTrack it = tl->begin(); it != tl->end(); ++it) { MusECore::Track* t = *it; // Ignore this track. if(t != track && (t->internalSolo() || t->solo())) { found = true; break; } } // mute->setIconSetB(found && !track->internalSolo() && !track->solo()); if (found && !track->internalSolo() && !track->solo()) { if (mute->isChecked()) mute->setIcon(*muteAndProxyOnSVGIcon); else mute->setIcon(*muteProxyOnSVGIcon); } else { mute->setIcon(*muteStateSVGIcon); } } //-------------------------------- // Control ganging support. //-------------------------------- struct MidiIncListStruct { int _port; int _chan; MidiIncListStruct(int port, int chan) : _port(port), _chan(chan) { } bool operator==(const MidiIncListStruct& other) { return other._port == _port && other._chan == _chan; } }; void Strip::componentChanged(int type, double val, bool off, int id, int scrollMode) { // The track has already taken care of its own controllers. // Don't bother other tracks if the track is not selected. ie part of a selected group. // Don't bother if broadcasting changes is disabled. if(!track || !track->selected() || !_broadcastChanges) return; // TODO: Only controller components handled for now. if(type != ComponentRack::controllerComponent) return; QList doneMidiTracks; QList doneAudioTracks; if(track->isMidiTrack()) { // TODO: Only volume and pan handled for now. int a_ctlnum; switch(id) { case MusECore::CTRL_VOLUME: a_ctlnum = MusECore::AC_VOLUME; break; case MusECore::CTRL_PANPOT: a_ctlnum = MusECore::AC_PAN; break; default: return; break; } MusECore::MidiTrack* m_track = static_cast(track); const int m_port = m_track->outPort(); const int m_chan = m_track->outChannel(); MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port]; MusECore::MidiController* m_mctl = m_mp->midiController(id, m_chan, false); if(!m_mctl) return; int i_m_min = m_mctl->minVal(); const int i_m_max = m_mctl->maxVal(); const int i_m_bias = m_mctl->bias(); //---------------------------------------------------------- // For midi volume, the formula given by MMA is: // volume dB = 40 * log(midivol / 127) and the inverse is: // midi volume = 127 * 10 ^ (volume dB / 40) // Unusual, it is a factor of 40 not 20. Since muse_db2val() // does 10 ^ (volume dB / 20), just divide volume dB by 2. //---------------------------------------------------------- double m_val = val; double ma_val = val; if(id == MusECore::CTRL_VOLUME) { if(MusEGlobal::config.preferMidiVolumeDb) { if(ma_val <= MusEGlobal::config.minSlider) m_val = ma_val = 0.0; else { m_val = double(i_m_max) * muse_db2val(m_val); ma_val = double(i_m_max) * muse_db2val(ma_val / 2.0); } } else { // It's a linear scale. We need to convert to Db and // scale the linear value by 2 since the midi scale is // twice the usual Db per step. Then convert back to linear. m_val = muse_val2dbr(m_val / double(i_m_max)) * 2.0; m_val *= 2.0; m_val = double(i_m_max) * muse_db2val(m_val / 2.0); } } //-------------------------------------------------------------- // NOTE: Midi int to audio float conversion: // If the control has a bias at all, it is supposed // to define the 'middle', like pan. // But if the control's range is odd (127), that // makes an uneven, uncentered float conversion. // So the policy here is to force an even range for // symmetrical +/- float conversion. (0.0 in the middle.) // Treat value '0' (-64 pan) and '1' (-63 pan) the same. //-------------------------------------------------------------- if(i_m_bias != 0 && ((i_m_max - i_m_min) & 0x1)) ++i_m_min; const int i_m_range = i_m_max - i_m_min; if(i_m_range == 0) // Avoid divide by zero. return; if(m_val < i_m_min) m_val = i_m_min; if(m_val > i_m_max) m_val = i_m_max; const double m_fact = (m_val - (double)i_m_min) / (double)i_m_range; // Make sure to include this track as already done. doneMidiTracks.append(MidiIncListStruct(m_port, m_chan)); MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Do selected tracks. Ignore this track, it has already taken care of its own controllers. if(t == track || !t->selected()) continue; if(t->isMidiTrack()) { MusECore::MidiTrack* mt = static_cast(t); const int port = mt->outPort(); const int chan = mt->outChannel(); // Make sure midi tracks on same port and channel are only done once. const MidiIncListStruct mils(port, chan); if(doneMidiTracks.contains(mils)) continue; doneMidiTracks.append(mils); double d_val = ma_val; MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiController* mctl = mp->midiController(id, chan, false); if(!mctl) continue; if(off || (d_val < double(mctl->minVal())) || (d_val > double(mctl->maxVal()))) { if(mp->hwCtrlState(chan, id) != MusECore::CTRL_VAL_UNKNOWN) mp->putHwCtrlEvent(MusECore::MidiPlayEvent(0, port, chan, MusECore::ME_CONTROLLER, id, MusECore::CTRL_VAL_UNKNOWN)); } else { d_val += double(mctl->bias()); mp->putControllerValue(port, chan, id, d_val, false); } } else { // Make sure we're not doing the same track more than once. if(doneAudioTracks.contains(t)) continue; doneAudioTracks.append(t); MusECore::AudioTrack* at = static_cast(t); MusECore::iCtrlList icl = at->controller()->find(a_ctlnum); if(icl == at->controller()->end()) continue; MusECore::CtrlList* cl = icl->second; // The audio volume can go above 0dB (amplification) while // the top midi value of 127 represents 0dB. Cut it off at 0dB. const double a_min = cl->minVal(); const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal(); const double a_range = a_max - a_min; const double a_val = (m_fact * a_range) + a_min; // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. // ScrDirect mode is one-time only on press with modifier. if(scrollMode != SliderBase::ScrDirect) at->recordAutomation(a_ctlnum, a_val); at->setParam(a_ctlnum, a_val); // Schedules a timed control change. at->enableController(a_ctlnum, false); } } } else { // TODO: Only volume and pan handled for now. int m_ctlnum; switch(id) { case MusECore::AC_VOLUME: m_ctlnum = MusECore::CTRL_VOLUME; break; case MusECore::AC_PAN: m_ctlnum = MusECore::CTRL_PANPOT; break; default: return; break; } MusECore::AudioTrack* a_track = static_cast(track); MusECore::iCtrlList icl = a_track->controller()->find(id); if(icl == a_track->controller()->end()) return; MusECore::CtrlList* cl = icl->second; //---------------------------------------------------------- // For midi volume, the formula given by MMA is: // volume dB = 40 * log(midivol / 127) and the inverse is: // midi volume = 127 * 10 ^ (volume dB / 40) // Unusual, it is a factor of 40 not 20. Since muse_db2val() // does 10 ^ (volume dB / 20), just divide volume dB by 2. //---------------------------------------------------------- double a_val = val; double ma_val = val; if(id == MusECore::AC_VOLUME) { if(ma_val <= MusEGlobal::config.minSlider) a_val = ma_val = 0.0; else { a_val = muse_db2val(a_val); ma_val = muse_db2val(ma_val / 2.0); } } // The audio volume can go above 0dB (amplification) while // the top midi value of 127 represents 0dB. Cut it off at 0dB. const double a_min = cl->minVal(); const double a_max = (id == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal(); const double a_range = a_max - a_min; if(a_range < 0.0001) // Avoid divide by zero. return; const double a_fact = (ma_val - a_min) / a_range; MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Do selected tracks. Ignore this track, it has already taken care of its own controllers. if(t == track || !t->selected()) continue; if(t->isMidiTrack()) { MusECore::MidiTrack* mt = static_cast(t); const int port = mt->outPort(); const int chan = mt->outChannel(); // Make sure midi tracks on same port and channel are only done once. const MidiIncListStruct mils(port, chan); if(doneMidiTracks.contains(mils)) continue; doneMidiTracks.append(mils); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiController* mctl = mp->midiController(m_ctlnum, chan, false); if(mctl) { int min = mctl->minVal(); const int max = mctl->maxVal(); const int bias = mctl->bias(); //-------------------------------------------------------------- // NOTE: Midi int to audio float conversion: // If the control has a bias at all, it is supposed // to define the 'middle', like pan. // But if the control's range is odd (127), that // makes an uneven, uncentered float conversion. // So the policy here is to force an even range for // symmetrical +/- float conversion. (0.0 in the middle.) // Treat value '0' (-64 pan) and '1' (-63 pan) the same. //-------------------------------------------------------------- if(bias != 0 && ((max - min) & 0x1)) ++min; const double d_min = (double)min; const double d_range = double(max - min); double d_val = a_fact * d_range + d_min; if(d_val < double(mctl->minVal())) d_val = mctl->minVal(); if(d_val > double(mctl->maxVal())) d_val = mctl->maxVal(); d_val += double(mctl->bias()); mp->putControllerValue(port, chan, m_ctlnum, d_val, false); } } else { // Make sure we're not doing the same track more than once. if(doneAudioTracks.contains(t)) continue; doneAudioTracks.append(t); MusECore::AudioTrack* at = static_cast(t); // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. // ScrDirect mode is one-time only on press with modifier. if(scrollMode != SliderBase::ScrDirect) at->recordAutomation(id, a_val); at->setParam(id, a_val); // Schedules a timed control change. at->enableController(id, false); } } } } void Strip::componentMoved(int /*type*/, double /*val*/, int /*id*/, bool /*shift_pressed*/) { //emit componentMoved(this, type, val, id, shift_pressed); } void Strip::componentPressed(int type, double val, int id) { // The track has already taken care of its own controllers. // Don't bother other tracks if the track is not selected. ie part of a selected group. // Don't bother if broadcasting changes is disabled. if(!track || !track->selected() || !_broadcastChanges) return; // TODO: Only controller components handled for now. if(type != ComponentRack::controllerComponent) return; QList doneMidiTracks; QList doneAudioTracks; if(track->isMidiTrack()) { // TODO: Only volume and pan handled for now. int a_ctlnum; switch(id) { case MusECore::CTRL_VOLUME: a_ctlnum = MusECore::AC_VOLUME; break; case MusECore::CTRL_PANPOT: a_ctlnum = MusECore::AC_PAN; break; default: return; break; } MusECore::MidiTrack* m_track = static_cast(track); const int m_port = m_track->outPort(); const int m_chan = m_track->outChannel(); MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port]; MusECore::MidiController* m_mctl = m_mp->midiController(id, m_chan, false); if(!m_mctl) return; int i_m_min = m_mctl->minVal(); const int i_m_max = m_mctl->maxVal(); const int i_m_bias = m_mctl->bias(); //---------------------------------------------------------- // For midi volume, the formula given by MMA is: // volume dB = 40 * log(midivol / 127) and the inverse is: // midi volume = 127 * 10 ^ (volume dB / 40) // Unusual, it is a factor of 40 not 20. Since muse_db2val() // does 10 ^ (volume dB / 20), just divide volume dB by 2. //---------------------------------------------------------- double m_val = val; double ma_val = val; if(id == MusECore::CTRL_VOLUME) { if(MusEGlobal::config.preferMidiVolumeDb) { if(ma_val <= MusEGlobal::config.minSlider) m_val = ma_val = 0.0; else { m_val = double(i_m_max) * muse_db2val(m_val); ma_val = double(i_m_max) * muse_db2val(ma_val / 2.0); } } else { // It's a linear scale. We need to convert to Db and // scale the linear value by 2 since the midi scale is // twice the usual Db per step. Then convert back to linear. m_val = muse_val2dbr(m_val / double(i_m_max)) * 2.0; m_val *= 2.0; m_val = double(i_m_max) * muse_db2val(m_val / 2.0); } } //-------------------------------------------------------------- // NOTE: Midi int to audio float conversion: // If the control has a bias at all, it is supposed // to define the 'middle', like pan. // But if the control's range is odd (127), that // makes an uneven, uncentered float conversion. // So the policy here is to force an even range for // symmetrical +/- float conversion. (0.0 in the middle.) // Treat value '0' (-64 pan) and '1' (-63 pan) the same. //-------------------------------------------------------------- if(i_m_bias != 0 && ((i_m_max - i_m_min) & 0x1)) ++i_m_min; const int i_m_range = i_m_max - i_m_min; if(i_m_range == 0) // Avoid divide by zero. return; if(m_val < i_m_min) m_val = i_m_min; if(m_val > i_m_max) m_val = i_m_max; const double m_fact = (m_val - (double)i_m_min) / (double)i_m_range; // Make sure to include this track as already done. doneMidiTracks.append(MidiIncListStruct(m_port, m_chan)); MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Do selected tracks. Ignore this track, it has already taken care of its own controllers. if(t == track || !t->selected()) continue; if(t->isMidiTrack()) { } else { // Make sure we're not doing the same track more than once. if(doneAudioTracks.contains(t)) continue; doneAudioTracks.append(t); MusECore::AudioTrack* at = static_cast(t); MusECore::iCtrlList icl = at->controller()->find(a_ctlnum); if(icl == at->controller()->end()) continue; MusECore::CtrlList* cl = icl->second; // The audio volume can go above 0dB (amplification) while // the top midi value of 127 represents 0dB. Cut it off at 0dB. const double a_min = cl->minVal(); const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal(); const double a_range = a_max - a_min; const double a_val = (m_fact * a_range) + a_min; at->startAutoRecord(a_ctlnum, a_val); at->setPluginCtrlVal(a_ctlnum, a_val); //at->setParam(a_ctlnum, val); // Schedules a timed control change. // TODO Try this instead at->enableController(a_ctlnum, false); } } } else { // TODO: Only volume and pan handled for now. switch(id) { case MusECore::AC_VOLUME: break; case MusECore::AC_PAN: break; default: return; break; } double a_val = val; if(id == MusECore::AC_VOLUME) { if(a_val <= MusEGlobal::config.minSlider) a_val = 0.0; else a_val = muse_db2val(a_val); } MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Do selected tracks. Ignore this track, it has already taken care of its own controllers. if(t == track || !t->selected()) continue; if(t->isMidiTrack()) { } else { // Make sure we're not doing the same track more than once. if(doneAudioTracks.contains(t)) continue; doneAudioTracks.append(t); MusECore::AudioTrack* at = static_cast(t); at->startAutoRecord(id, a_val); at->setPluginCtrlVal(id, a_val); //at->setParam(id, val); // Schedules a timed control change. // TODO Try this instead at->enableController(id, false); } } } } void Strip::componentReleased(int type, double val, int id) { // The track has already taken care of its own controllers. // Don't bother other tracks if the track is not selected. ie part of a selected group. // Don't bother if broadcasting changes is disabled. if(!track || !track->selected() || !_broadcastChanges) return; // TODO: Only controller components handled for now. if(type != ComponentRack::controllerComponent) return; QList doneMidiTracks; QList doneAudioTracks; if(track->isMidiTrack()) { // TODO: Only volume and pan handled for now. int a_ctlnum; switch(id) { case MusECore::CTRL_VOLUME: a_ctlnum = MusECore::AC_VOLUME; break; case MusECore::CTRL_PANPOT: a_ctlnum = MusECore::AC_PAN; break; default: return; break; } MusECore::MidiTrack* m_track = static_cast(track); const int m_port = m_track->outPort(); const int m_chan = m_track->outChannel(); MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port]; MusECore::MidiController* m_mctl = m_mp->midiController(id, m_chan, false); if(!m_mctl) return; int i_m_min = m_mctl->minVal(); const int i_m_max = m_mctl->maxVal(); const int i_m_bias = m_mctl->bias(); //---------------------------------------------------------- // For midi volume, the formula given by MMA is: // volume dB = 40 * log(midivol / 127) and the inverse is: // midi volume = 127 * 10 ^ (volume dB / 40) // Unusual, it is a factor of 40 not 20. Since muse_db2val() // does 10 ^ (volume dB / 20), just divide volume dB by 2. //---------------------------------------------------------- double m_val = val; double ma_val = val; if(id == MusECore::CTRL_VOLUME) { if(MusEGlobal::config.preferMidiVolumeDb) { if(ma_val <= MusEGlobal::config.minSlider) m_val = ma_val = 0.0; else { m_val = double(i_m_max) * muse_db2val(m_val); ma_val = double(i_m_max) * muse_db2val(ma_val / 2.0); } } else { // It's a linear scale. We need to convert to Db and // scale the linear value by 2 since the midi scale is // twice the usual Db per step. Then convert back to linear. m_val = muse_val2dbr(m_val / double(i_m_max)) * 2.0; m_val *= 2.0; m_val = double(i_m_max) * muse_db2val(m_val / 2.0); } } //-------------------------------------------------------------- // NOTE: Midi int to audio float conversion: // If the control has a bias at all, it is supposed // to define the 'middle', like pan. // But if the control's range is odd (127), that // makes an uneven, uncentered float conversion. // So the policy here is to force an even range for // symmetrical +/- float conversion. (0.0 in the middle.) // Treat value '0' (-64 pan) and '1' (-63 pan) the same. //-------------------------------------------------------------- if(i_m_bias != 0 && ((i_m_max - i_m_min) & 0x1)) ++i_m_min; const int i_m_range = i_m_max - i_m_min; if(i_m_range == 0) // Avoid divide by zero. return; if(m_val < i_m_min) m_val = i_m_min; if(m_val > i_m_max) m_val = i_m_max; const double m_fact = (m_val - (double)i_m_min) / (double)i_m_range; // Make sure to include this track as already done. doneMidiTracks.append(MidiIncListStruct(m_port, m_chan)); MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Do selected tracks. Ignore this track, it has already taken care of its own controllers. if(t == track || !t->selected()) continue; if(t->isMidiTrack()) { } else { // Make sure we're not doing the same track more than once. if(doneAudioTracks.contains(t)) continue; doneAudioTracks.append(t); MusECore::AudioTrack* at = static_cast(t); MusECore::iCtrlList icl = at->controller()->find(a_ctlnum); if(icl == at->controller()->end()) continue; MusECore::CtrlList* cl = icl->second; // The audio volume can go above 0dB (amplification) while // the top midi value of 127 represents 0dB. Cut it off at 0dB. const double a_min = cl->minVal(); const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal(); const double a_range = a_max - a_min; const double a_val = (m_fact * a_range) + a_min; MusECore::AutomationType atype = at->automationType(); at->stopAutoRecord(a_ctlnum, a_val); if(atype == MusECore::AUTO_OFF || atype == MusECore::AUTO_TOUCH) at->enableController(a_ctlnum, true); } } } else { // TODO: Only volume and pan handled for now. switch(id) { case MusECore::AC_VOLUME: break; case MusECore::AC_PAN: break; default: return; break; } //---------------------------------------------------------- // For midi volume, the formula given by MMA is: // volume dB = 40 * log(midivol / 127) and the inverse is: // midi volume = 127 * 10 ^ (volume dB / 40) // Unusual, it is a factor of 40 not 20. Since muse_db2val() // does 10 ^ (volume dB / 20), just divide volume dB by 2. //---------------------------------------------------------- double a_val = val; if(id == MusECore::AC_VOLUME) { if(a_val <= MusEGlobal::config.minSlider) a_val = 0.0; else a_val = muse_db2val(a_val); } MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Do selected tracks. Ignore this track, it has already taken care of its own controllers. if(t == track || !t->selected()) continue; if(t->isMidiTrack()) { } else { // Make sure we're not doing the same track more than once. if(doneAudioTracks.contains(t)) continue; doneAudioTracks.append(t); MusECore::AudioTrack* at = static_cast(t); MusECore::AutomationType atype = at->automationType(); at->stopAutoRecord(id, a_val); if(atype == MusECore::AUTO_OFF || atype == MusECore::AUTO_TOUCH) at->enableController(id, true); } } } } void Strip::componentIncremented(int type, double oldCompVal, double newCompVal, bool off, int id, int /*scrollMode*/) { // The track has already taken care of its own controllers. // Don't bother other tracks if the track is not selected. ie part of a selected group. // Don't bother if broadcasting changes is disabled. if(!track || !track->selected() || !_broadcastChanges) return; // TODO: Only controller components handled for now. if(type != ComponentRack::controllerComponent) return; bool wait_required = false; QList doneMidiTracks; QList doneAudioTracks; const double d_comp_val_delta = newCompVal - oldCompVal; if(track->isMidiTrack()) { // TODO: Only volume and pan handled for now. int a_ctlnum; switch(id) { case MusECore::CTRL_VOLUME: a_ctlnum = MusECore::AC_VOLUME; break; case MusECore::CTRL_PANPOT: a_ctlnum = MusECore::AC_PAN; break; default: return; break; } MusECore::MidiTrack* m_track = static_cast(track); const int m_port = m_track->outPort(); const int m_chan = m_track->outChannel(); MusECore::MidiPort* m_mp = &MusEGlobal::midiPorts[m_port]; MusECore::MidiController* m_mctl = m_mp->midiController(id, m_chan, false); if(!m_mctl) return; const int i_m_min = m_mctl->minVal(); const int i_m_max = m_mctl->maxVal(); const int i_m_bias = m_mctl->bias(); int i_ma_min = i_m_min; const int i_ma_max = i_m_max; //---------------------------------------------------------- // For midi volume, the formula given by MMA is: // volume dB = 40 * log(midivol / 127) and the inverse is: // midi volume = 127 * 10 ^ (volume dB / 40) // Unusual, it is a factor of 40 not 20. Since muse_db2val() // does 10 ^ (volume dB / 20), just divide volume dB by 2. //---------------------------------------------------------- double m_old_val = oldCompVal; double ma_old_val = oldCompVal; double m_new_val = newCompVal; double ma_new_val = newCompVal; if(id == MusECore::CTRL_VOLUME) { if(MusEGlobal::config.preferMidiVolumeDb) { if(ma_old_val <= MusEGlobal::config.minSlider) m_old_val = ma_old_val = 0.0; else { m_old_val = double(i_m_max) * muse_db2val(m_old_val / 2.0); ma_old_val = double(i_ma_max) * muse_db2val(ma_old_val); } if(ma_new_val <= MusEGlobal::config.minSlider) m_new_val = ma_new_val = 0.0; else { m_new_val = double(i_m_max) * muse_db2val(m_new_val / 2.0); ma_new_val = double(i_ma_max) * muse_db2val(ma_new_val); } } else { // It's a linear scale. We need to convert to Db and // scale the linear value by 2 since the midi scale is // twice the usual Db per step. Then convert back to linear. ma_old_val = muse_val2dbr(ma_old_val / double(i_ma_max)) * 2.0; ma_old_val *= 2.0; ma_old_val = double(i_ma_max) * muse_db2val(ma_old_val / 2.0); ma_new_val = muse_val2dbr(ma_new_val / double(i_ma_max)) * 2.0; ma_new_val *= 2.0; ma_new_val = double(i_ma_max) * muse_db2val(ma_new_val / 2.0); } } //-------------------------------------------------------------- // NOTE: Midi int to audio float conversion: // If the control has a bias at all, it is supposed // to define the 'middle', like pan. // But if the control's range is odd (127), that // makes an uneven, uncentered float conversion. // So the policy here is to force an even range for // symmetrical +/- float conversion. (0.0 in the middle.) // Treat value '0' (-64 pan) and '1' (-63 pan) the same. //-------------------------------------------------------------- if(i_m_bias != 0 && ((i_ma_max - i_ma_min) & 0x1)) ++i_ma_min; const int i_m_range = i_m_max - i_m_min; const int i_ma_range = i_ma_max - i_ma_min; if(m_old_val < i_m_min) m_old_val = i_m_min; if(m_old_val > i_m_max) m_old_val = i_m_max; if(m_new_val < i_m_min) m_new_val = i_m_min; if(m_new_val > i_m_max) m_new_val = i_m_max; if(ma_old_val < i_ma_min) ma_old_val = i_ma_min; if(ma_old_val > i_ma_max) ma_old_val = i_ma_max; if(ma_new_val < i_ma_min) ma_new_val = i_ma_min; if(ma_new_val > i_ma_max) ma_new_val = i_ma_max; if(i_m_range == 0 || i_ma_range == 0) // Avoid divide by zero. return; // Get the current or last valid actual controller value. const double m_val_delta = m_new_val - m_old_val; const double ma_val_delta = ma_new_val - ma_old_val; const double ma_new_delta_fact = ma_val_delta / (double)i_ma_range; // Make sure to include this track as already done. doneMidiTracks.append(MidiIncListStruct(m_port, m_chan)); MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Do selected tracks. Ignore this track, it has already taken care of its own controllers. if(t == track || !t->selected()) continue; if(t->isMidiTrack()) { MusECore::MidiTrack* mt = static_cast(t); const int port = mt->outPort(); const int chan = mt->outChannel(); // Make sure midi tracks on same port and channel are only done once. const MidiIncListStruct mils(port, chan); if(doneMidiTracks.contains(mils)) continue; doneMidiTracks.append(mils); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiController* mctl = mp->midiController(id, chan, false); if(!mctl) continue; int min = mctl->minVal(); const int max = mctl->maxVal(); if(off) { if(mp->hwCtrlState(chan, id) != MusECore::CTRL_VAL_UNKNOWN) mp->putHwCtrlEvent(MusECore::MidiPlayEvent(0, port, chan, MusECore::ME_CONTROLLER, id, MusECore::CTRL_VAL_UNKNOWN)); } else { double d_fin_val = m_new_val; // Get the current or last valid value. double d_cur_val = mp->hwDCtrlState(chan, id); if(MusECore::MidiController::dValIsUnknown(d_cur_val)) d_cur_val = mp->lastValidHWDCtrlState(chan, id); if(MusECore::MidiController::dValIsUnknown(d_cur_val)) d_fin_val = m_new_val; else { d_cur_val = d_cur_val - double(mctl->bias()); if(id == MusECore::CTRL_VOLUME && MusEGlobal::config.preferMidiVolumeDb) { d_fin_val = muse_val2dbr(d_cur_val / double(max)) * 2.0; d_fin_val += d_comp_val_delta; d_fin_val = double(max) * muse_db2val(d_fin_val / 2.0); } else d_fin_val = d_cur_val + m_val_delta; } if(d_fin_val < double(min)) d_fin_val = min; if(d_fin_val > double(max)) d_fin_val = max; d_fin_val += double(mctl->bias()); // False = linear not dB because we are doing the conversion here. mp->putControllerValue(port, chan, id, d_fin_val, false); // Trip the wait flag. wait_required = true; } } else { // Make sure we're not doing the same track more than once. if(doneAudioTracks.contains(t)) continue; doneAudioTracks.append(t); MusECore::AudioTrack* at = static_cast(t); MusECore::iCtrlList icl = at->controller()->find(a_ctlnum); if(icl == at->controller()->end()) continue; MusECore::CtrlList* cl = icl->second; double d_cur_val = cl->curVal(); // The audio volume can go above 0dB (amplification) while // the top midi value of 127 represents 0dB. Cut it off at 0dB. const double a_min = cl->minVal(); const double a_max = (a_ctlnum == MusECore::AC_VOLUME) ? 1.0 : cl->maxVal(); const double a_range = a_max - a_min; const double a_val_delta = ma_new_delta_fact * a_range; if(id == MusECore::CTRL_VOLUME && MusEGlobal::config.preferMidiVolumeDb) { if(d_cur_val <= 0.0) d_cur_val = MusEGlobal::config.minSlider; else d_cur_val = muse_val2dbr(d_cur_val); d_cur_val += d_comp_val_delta; if(d_cur_val < MusEGlobal::config.minSlider) d_cur_val = MusEGlobal::config.minSlider; if(d_cur_val > 10.0) d_cur_val = 10.0; d_cur_val = muse_db2val(d_cur_val); } else d_cur_val += a_val_delta; if(d_cur_val < a_min) d_cur_val = a_min; if(d_cur_val > a_max) d_cur_val = a_max; at->recordAutomation(a_ctlnum, d_cur_val); at->setParam(a_ctlnum, d_cur_val); // Schedules a timed control change. at->enableController(a_ctlnum, false); // Trip the wait flag. wait_required = true; } } } else { // TODO: Only volume and pan handled for now. int m_ctlnum; switch(id) { case MusECore::AC_VOLUME: m_ctlnum = MusECore::CTRL_VOLUME; break; case MusECore::AC_PAN: m_ctlnum = MusECore::CTRL_PANPOT; break; default: return; break; } MusECore::AudioTrack* a_track = static_cast(track); MusECore::iCtrlList icl = a_track->controller()->find(id); if(icl == a_track->controller()->end()) return; MusECore::CtrlList* cl = icl->second; //---------------------------------------------------------- // For midi volume, the formula given by MMA is: // volume dB = 40 * log(midivol / 127) and the inverse is: // midi volume = 127 * 10 ^ (volume dB / 40) // Unusual, it is a factor of 40 not 20. Since muse_db2val() // does 10 ^ (volume dB / 20), just divide volume dB by 2. //---------------------------------------------------------- double a_val = newCompVal; double ma_val = newCompVal; if(id == MusECore::AC_VOLUME) { if(ma_val <= MusEGlobal::config.minSlider) a_val = ma_val = 0.0; else { a_val = muse_db2val(a_val); ma_val = muse_db2val(ma_val / 2.0); } } // The audio volume can go above 0dB (amplification) while // the top midi value of 127 represents 0dB. Cut it off at 0dB. const double a_min = cl->minVal(); const double a_max = cl->maxVal(); const double a_range = a_max - a_min; if(a_range < 0.0001) // Avoid divide by zero. return; const double a_fact_delta = muse_round2micro((newCompVal - oldCompVal) / a_range); MusECore::TrackList* tracks = MusEGlobal::song->tracks(); for(MusECore::iTrack it = tracks->begin(); it != tracks->end(); ++it) { MusECore::Track* t = *it; // Do selected tracks. Ignore this track, it has already taken care of its own controllers. if(t == track || !t->selected()) continue; if(t->isMidiTrack()) { MusECore::MidiTrack* mt = static_cast(t); const int port = mt->outPort(); const int chan = mt->outChannel(); // Make sure midi tracks on same port and channel are only done once. const MidiIncListStruct mils(port, chan); if(doneMidiTracks.contains(mils)) continue; doneMidiTracks.append(mils); MusECore::MidiPort* mp = &MusEGlobal::midiPorts[port]; MusECore::MidiController* mctl = mp->midiController(m_ctlnum, chan, false); if(mctl) { int min = mctl->minVal(); const int max = mctl->maxVal(); const int bias = mctl->bias(); //-------------------------------------------------------------- // NOTE: Midi int to audio float conversion: // If the control has a bias at all, it is supposed // to define the 'middle', like pan. // But if the control's range is odd (127), that // makes an uneven, uncentered float conversion. // So the policy here is to force an even range for // symmetrical +/- float conversion. (0.0 in the middle.) // Treat value '0' (-64 pan) and '1' (-63 pan) the same. //-------------------------------------------------------------- if(bias != 0 && ((max - min) & 0x1)) ++min; const double d_min = (double)min; const double d_max = (double)max; const double d_range = double(max - min); const double m_val_delta = muse_round2micro(a_fact_delta * d_range); double d_fin_val = 0.0; // Get the current or last valid value. double d_cur_val = mp->hwDCtrlState(chan, m_ctlnum); if(MusECore::MidiController::dValIsUnknown(d_cur_val)) d_cur_val = mp->lastValidHWDCtrlState(chan, m_ctlnum); if(MusECore::MidiController::dValIsUnknown(d_cur_val)) { if(!mctl->initValIsUnknown()) d_cur_val = double(mctl->initVal()) + double(bias); } if(MusECore::MidiController::dValIsUnknown(d_cur_val)) d_fin_val = 0.0; else { d_cur_val = d_cur_val - double(mctl->bias()); if(m_ctlnum == MusECore::CTRL_VOLUME) { d_fin_val = muse_val2dbr(d_cur_val / d_max) * 2.0; d_fin_val += d_comp_val_delta; d_fin_val = d_max * muse_db2val(d_fin_val / 2.0); } else d_fin_val = d_cur_val + m_val_delta; } if(d_fin_val < d_min) d_fin_val = d_min; if(d_fin_val > d_max) d_fin_val = d_max; d_fin_val += double(mctl->bias()); // False = linear not dB because we are doing the conversion here. mp->putControllerValue(port, chan, m_ctlnum, d_fin_val, false); // Trip the wait flag. wait_required = true; } } else { // Make sure we're not doing the same track more than once. if(doneAudioTracks.contains(t)) continue; doneAudioTracks.append(t); MusECore::AudioTrack* at = static_cast(t); MusECore::iCtrlList icl = at->controller()->find(id); if(icl == at->controller()->end()) continue; MusECore::CtrlList* cl = icl->second; double d_cur_val = cl->curVal(); // The audio volume can go above 0dB (amplification) while // the top midi value of 127 represents 0dB. Cut it off at 0dB. const double d_min = cl->minVal(); const double d_max = cl->maxVal(); const double d_range = d_max - d_min; const double d_val_delta = a_fact_delta * d_range; if(id == MusECore::AC_VOLUME) { if(d_cur_val <= 0.0) d_cur_val = MusEGlobal::config.minSlider; else d_cur_val = muse_val2dbr(d_cur_val); d_cur_val += d_comp_val_delta; if(d_cur_val < MusEGlobal::config.minSlider) d_cur_val = MusEGlobal::config.minSlider; if(d_cur_val > 10.0) d_cur_val = 10.0; d_cur_val = muse_db2val(d_cur_val); } else d_cur_val += d_val_delta; if(d_cur_val < d_min) d_cur_val = d_min; if(d_cur_val > d_max) d_cur_val = d_max; // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. // ScrDirect mode is one-time only on press with modifier. //if(scrollMode != SliderBase::ScrDirect) at->recordAutomation(id, d_cur_val); at->setParam(id, d_cur_val); // Schedules a timed control change. at->enableController(id, false); // Trip the wait flag. wait_required = true; } } } // This is a DELTA operation. Unfortunately we may need to WAIT for the hw controls to update // in the audio thread before we can apply ANOTHER delta to the soon-to-be 'current' value. if(wait_required) MusEGlobal::audio->msgAudioWait(); } } // namespace MusEGui muse-4.0.0/src/muse/mixer/strip.h000066400000000000000000000717701404105311000166630ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: strip.h,v 1.3.2.2 2009/11/14 03:37:48 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011 - 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __STRIP_H__ #define __STRIP_H__ #include #include #include #include #include #include "type_defs.h" #include "globaldefs.h" #include "drange.h" #include "elided_label.h" // Forward declarations: class QMouseEvent; class QResizeEvent; class QGridLayout; class QLayout; class QPushButton; namespace MusECore { class Track; } namespace MusEGui { class CompactComboBox; class CompactKnob; class CompactSlider; class CompactToolButton; class Meter; //--------------------------------------------- // ComponentWidget // Defines an item in a rack layout. //--------------------------------------------- class ComponentWidget { public: // Type of components. typedef int ComponentType; // Type of component widget. typedef int ComponentWidgetType; // The component widget. QWidget* _widget; public: // Type of widget. ComponentWidgetType _widgetType; // Type of component: Controller, gain property, aux property etc. ComponentType _componentType; // User index value (controller index, aux property index etc.) provided to slots. int _index; // Cached current value, for widgets that use it. //double _currentValue; // Cached flag, for widgets that use it. bool _pressed; public: ComponentWidget() : _widget(0), _widgetType(0), _componentType(0), _index(0), _pressed(false) { } ComponentWidget(QWidget* widget, ComponentWidgetType widgetType, ComponentType componentType, int index ) : _widget(widget), _widgetType(widgetType), _componentType(componentType), _index(index), _pressed(false) { } bool isValid() const { return _widget != 0; } }; //--------------------------------------------- // ComponentWidgetList // List of components. //--------------------------------------------- class ComponentWidgetList : public std::list { public: iterator find(ComponentWidget::ComponentType componentType, int componentWidgetType = -1, int index = -1, QWidget* widget = 0) { for(iterator i = begin(); i != end(); ++i) { ComponentWidget& cw = *i; if(cw._componentType == componentType) { if((componentWidgetType == -1 || cw._widgetType == componentWidgetType) && (index == -1 || cw._index == index) && (!widget || cw._widget == widget)) return i; } } return end(); } const_iterator find(ComponentWidget::ComponentType componentType, int componentWidgetType = -1, int index = -1, const QWidget* widget = 0) const { for(const_iterator i = begin(); i != end(); ++i) { const ComponentWidget& cw = *i; if(cw._componentType == componentType) { if((componentWidgetType == -1 || cw._widgetType == componentWidgetType) && (index == -1 || cw._index == index) && (!widget || cw._widget == widget)) return i; } } return end(); } iterator find(const ComponentWidget& cw) { return find(cw._componentType, cw._widgetType, cw._index, cw._widget); } // FIXME: I don't think this is calling the const version. const_iterator find(const ComponentWidget& cw) const { return find(cw._componentType, cw._widgetType, cw._index, cw._widget); } }; typedef ComponentWidgetList::iterator iComponentWidget; typedef ComponentWidgetList::const_iterator ciComponentWidget; //--------------------------------------------- // ComponentDescriptor // Base class defining an item to be added to a rack layout. //--------------------------------------------- class ComponentDescriptor { public: // Type of widget to create. ComponentWidget::ComponentWidgetType _widgetType; // Type of component: Controller, gain property, aux property etc. ComponentWidget::ComponentType _componentType; // Object name to assign to the created widget. const char* _objName; // User index value (controller index, aux property index etc.) provided to given slots. int _index; QString _toolTipText; QString _label; QColor _color; bool _enabled; public: ComponentDescriptor() : _widgetType(0), _componentType(0), _objName(0), _index(0), _enabled(true) { } ComponentDescriptor(ComponentWidget::ComponentWidgetType widgetType, ComponentWidget::ComponentType type, const char* objName = 0, int index = 0, const QString& toolTipText = QString(), const QString& label = QString(), const QColor& color = QColor(), bool enabled = true ) : _widgetType(widgetType), _componentType(type), _objName(objName), _index(index), _toolTipText(toolTipText), _label(label), _color(color), _enabled(enabled) { } }; class ComponentRackLayout : public QVBoxLayout { Q_OBJECT public: ComponentRackLayout() : QVBoxLayout() { } ComponentRackLayout(QWidget* parent) : QVBoxLayout(parent) { } }; //--------------------------------------------- // ComponentRack // Base class rack containing components. //--------------------------------------------- class ComponentRack : public QFrame { Q_OBJECT public: // Type of component. enum ComponentTypes { controllerComponent = 0, propertyComponent = 1, userComponent = 1000 }; // Possible component properties. enum ComponentProperties { userComponentProperty = 1000 }; // Possible widget types. enum ComponentWidgetTypes { ExternalComponentWidget = 0, CompactKnobComponentWidget = 1, CompactSliderComponentWidget = 2, ElidedLabelComponentWidget = 3, userComponentWidget = 1000 }; protected: int _id; ComponentWidgetList _components; ComponentRackLayout* _layout; // Creates a new component widget from the given desc. Called by newComponent(). // Connects known widget types' signals to slots. virtual void newComponentWidget( ComponentDescriptor* desc, const ComponentWidget& before = ComponentWidget() ); // Adds a component widget created by newComponentWidget. void addComponentWidget( const ComponentWidget& cw, const ComponentWidget& before = ComponentWidget() ); protected slots: virtual void controllerChanged(double /*val*/, bool /*isOff*/, int /*id*/, int /*scrollMode*/) { } virtual void controllerMoved(double /*val*/, int /*id*/, bool /*shift_pressed*/) { } virtual void controllerPressed(double /*val*/, int /*id*/) { } virtual void controllerReleased(double /*val*/, int /*id*/) { } virtual void controllerRightClicked(QPoint /*p*/, int /*id*/) { } virtual void propertyChanged(double /*val*/, bool /*isOff*/, int /*id*/, int /*scrollMode*/) { } virtual void propertyMoved(double /*val*/, int /*id*/, bool /*shift_pressed*/) { } virtual void propertyPressed(double /*val*/, int /*id*/) { } virtual void propertyReleased(double /*val*/, int /*id*/) { } virtual void propertyRightClicked(QPoint /*p*/, int /*id*/) { } virtual void labelPropertyPressed(QPoint /*p*/, int /*id*/, Qt::MouseButtons /*buttons*/, Qt::KeyboardModifiers /*keys*/) { } virtual void labelPropertyReleased(QPoint /*p*/, int /*id*/, Qt::MouseButtons /*buttons*/, Qt::KeyboardModifiers /*keys*/) { } virtual void labelPropertyReturnPressed(QPoint /*p*/, int /*id*/, Qt::KeyboardModifiers /*keys*/) { } public slots: virtual void songChanged(MusECore::SongChangedStruct_t) { } virtual void configChanged(); signals: // Argument int 'type' is ComponentRack::ComponentTypes or // other user type (Audio/MidiComponentRack::ComponentTypes for ex). void componentChanged(int type, double val, bool off, int id, int scrollMode); void componentMoved(int type, double val, int id, bool shift_pressed); void componentPressed(int type, double val, int id); void componentReleased(int type, double val, int id); public: ComponentRack(int id = -1, QWidget* parent = 0, Qt::WindowFlags f = Qt::Widget); int id() const { return _id; } ComponentWidgetList* components() { return &_components; } ComponentWidget* findComponent( ComponentWidget::ComponentType componentType, int componentWidgetType = -1, int index = -1, QWidget* widget = 0); // Destroys all components and clears the component list. void clearDelete(); // Adds a component to the layout and the list. Creates a new component using // the given desc values if the desc widget is not given. virtual void newComponent( ComponentDescriptor*, const ComponentWidget& /*before*/ = ComponentWidget() ) = 0; // Add a stretch to the layout. virtual void addStretch() { _layout->addStretch(); } // Add spacing to the layout. virtual void addSpacing(int spc) { _layout->addSpacing(spc); } virtual double componentMinValue(const ComponentWidget&) const; virtual double componentMaxValue(const ComponentWidget&) const; virtual void setComponentRange(const ComponentWidget&, double min, double max, bool updateOnly = true, double step = 0.0, int pageSize = 1, DoubleRange::ConversionMode mode = DoubleRange::ConvertDefault); virtual void setComponentMinValue(const ComponentWidget&, double min, bool updateOnly = true); virtual void setComponentMaxValue(const ComponentWidget&, double max, bool updateOnly = true); virtual double componentValue(const ComponentWidget&) const; virtual void setComponentValue(const ComponentWidget&, double val, bool updateOnly = true); virtual void fitComponentValue(const ComponentWidget&, double val, bool updateOnly = true); virtual void incComponentValue(const ComponentWidget&, int steps, bool updateOnly = true); virtual void setComponentText(const ComponentWidget&, const QString& text, bool updateOnly = true); virtual void setComponentEnabled(const ComponentWidget&, bool enable, bool updateOnly = true); virtual void setComponentShowValue(const ComponentWidget&, bool show, bool updateOnly = true); // Sets up tabbing for the existing controls in the rack. // Accepts a previousWidget which can be null and returns the last widget in the rack, // which allows chaining racks or other widgets. virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0); }; //--------------------------------------------- // WidgetComponentDescriptor // Class defining a general external QWidget item // to be added to a rack layout. //--------------------------------------------- class WidgetComponentDescriptor : public ComponentDescriptor { public: // External widget pointer set by caller, corresponding to WidgetComponent type. QWidget* _widget; public: WidgetComponentDescriptor() : ComponentDescriptor(ComponentRack::ExternalComponentWidget, ComponentRack::propertyComponent), _widget(0) { } WidgetComponentDescriptor( QWidget* widget, ComponentWidget::ComponentType componentType, const char* objName = 0, int index = 0, const QString& toolTipText = QString(), const QString& label = QString(), const QColor& color = QColor(), bool enabled = true ) : ComponentDescriptor(ComponentRack::ExternalComponentWidget, componentType, objName, index, toolTipText, label, color, enabled ), _widget(widget) { } }; //--------------------------------------------- // CompactKnobComponentDescriptor // Class defining a CompactKnob to be added to a rack layout. //--------------------------------------------- class CompactKnobComponentDescriptor : public ComponentDescriptor { public: // Return value pointer created by the function, corresponding to a ComponentWidgetType: CompactKnob* _compactKnob; double _min; double _max; int _precision; double _step; double _initVal; bool _hasOffMode; bool _isOff; bool _showValue; QColor _rimColor; QColor _faceColor; QColor _shinyColor; QColor _markerColor; QString _prefix; QString _suffix; QString _specialValueText; public: CompactKnobComponentDescriptor() : ComponentDescriptor(ComponentRack::CompactKnobComponentWidget, ComponentRack::propertyComponent), _compactKnob(0), _min(0.0), _max(0.0), _precision(0), _step(1.0), _initVal(0.0), _hasOffMode(false), _isOff(false), _showValue(true) { } CompactKnobComponentDescriptor( ComponentWidget::ComponentType componentType, const char* objName = 0, int index = 0, const QString& toolTipText = QString(), const QString& label = QString(), const QColor& borderColour = QColor(), const QColor& rimColour = QColor(), const QColor& faceColour = QColor(), const QColor& shinyColour = QColor(), const QColor& markerColour = QColor(), const QString& prefix = QString(), const QString& suffix = QString(), const QString& specialValueText = QString(), bool enabled = true, double min = 0.0, double max = 100.0, int precision = 0, double step = 1.0, double initVal = 0.0, bool hasOffMode = false, bool isOff = false, bool showValue = true ) : ComponentDescriptor(ComponentRack::CompactKnobComponentWidget, componentType, objName, index, toolTipText, label, borderColour, enabled ), _compactKnob(0), _min(min), _max(max), _precision(precision), _step(step), _initVal(initVal), _hasOffMode(hasOffMode), _isOff(isOff), _showValue(showValue), _rimColor(rimColour), _faceColor(faceColour), _shinyColor(shinyColour), _markerColor(markerColour), _prefix(prefix), _suffix(suffix), _specialValueText(specialValueText) { } }; //--------------------------------------------- // CompactSliderComponentDescriptor // Class defining a CompactSlider to be added to a rack layout. //--------------------------------------------- class CompactSliderComponentDescriptor : public ComponentDescriptor { public: // Return value pointer created by the function, corresponding to a ComponentWidgetType: CompactSlider* _compactSlider; int _activeBorders; double _min; double _max; int _precision; double _step; double _initVal; bool _hasOffMode; bool _isOff; bool _showValue; QColor _barColor; QColor _slotColor; QColor _thumbColor; QString _prefix; QString _suffix; QString _specialValueText; public: CompactSliderComponentDescriptor() : ComponentDescriptor(ComponentRack::CompactSliderComponentWidget, ComponentRack::propertyComponent), _compactSlider(0), _activeBorders(0xf), // All borders. _min(0.0), _max(0.0), _precision(0), _step(1.0), _initVal(0.0), _hasOffMode(false), _isOff(false), _showValue(true) { } CompactSliderComponentDescriptor( ComponentWidget::ComponentType componentType, const char* objName = 0, int index = 0, int activeBorders = 0xf, // All four borders. const QString& toolTipText = QString(), const QString& label = QString(), const QColor& borderColour = QColor(), const QColor& barColour = QColor(), const QColor& slotColour = QColor(), const QColor& thumbColour = QColor(), const QString& prefix = QString(), const QString& suffix = QString(), const QString& specialValueText = QString(), bool enabled = true, double min = 0.0, double max = 100.0, int precision = 0, double step = 1.0, double initVal = 0.0, bool hasOffMode = false, bool isOff = false, bool showValue = true ) : ComponentDescriptor(ComponentRack::CompactSliderComponentWidget, componentType, objName, index, toolTipText, label, borderColour, enabled ), _compactSlider(0), _activeBorders(activeBorders), _min(min), _max(max), _precision(precision), _step(step), _initVal(initVal), _hasOffMode(hasOffMode), _isOff(isOff), _showValue(showValue), _barColor(barColour), _slotColor(slotColour), _thumbColor(thumbColour), _prefix(prefix), _suffix(suffix), _specialValueText(specialValueText) { } }; //--------------------------------------------- // ElidedLabelComponentDescriptor // Class defining a ElidedLabel to be added to a rack layout. //--------------------------------------------- class ElidedLabelComponentDescriptor : public ComponentDescriptor { public: // Return value pointer created by the function, corresponding to a ComponentWidgetType: ElidedLabel* _elidedLabel; Qt::TextElideMode _elideMode; int _minFontPoint; bool _ignoreHeight; bool _ignoreWidth; // _color from base class seems to be used for border color QColor _bgColor; QColor _bgActiveColor; QColor _fontColor; QColor _fontActiveColor; public: ElidedLabelComponentDescriptor() : ComponentDescriptor(ComponentRack::ElidedLabelComponentWidget, ComponentRack::propertyComponent), _elidedLabel(0), _elideMode(Qt::ElideNone), _minFontPoint(5), _ignoreHeight(true), _ignoreWidth(false) { } ElidedLabelComponentDescriptor( ComponentWidget::ComponentType componentType, const char* objName = 0, int index = 0, Qt::TextElideMode elideMode = Qt::ElideNone, const QString& toolTipText = QString(), const QString& text = QString(), const QColor& borderColour = QColor(), int minFontPoint = 5, bool ignoreHeight = true, bool ignoreWidth = false, bool enabled = true ) : ComponentDescriptor(ComponentRack::ElidedLabelComponentWidget, componentType, objName, index, toolTipText, text, borderColour, enabled ), _elidedLabel(0), _elideMode(elideMode), _minFontPoint(minFontPoint), _ignoreHeight(ignoreHeight), _ignoreWidth(ignoreWidth) { } }; //--------------------------------------------------------- // TrackNameLabel //--------------------------------------------------------- class TrackNameLabel : public QLabel { Q_OBJECT Q_PROPERTY(bool style3d READ style3d WRITE setStyle3d) bool _style3d; bool _hovered; protected: static const int _expandIconWidth; bool _hasExpandIcon; bool _expandIconPressed; virtual void mouseDoubleClickEvent(QMouseEvent*); virtual void paintEvent(QPaintEvent*); virtual void mousePressEvent(QMouseEvent*); virtual void mouseReleaseEvent(QMouseEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual void leaveEvent(QEvent*); virtual void enterEvent(QEvent*); signals: void doubleClicked(); void expandClicked(); public: TrackNameLabel(QWidget* parent = nullptr); // TrackNameLabel(const QString & text, QWidget* parent = 0, const char* name = 0, Qt::WindowFlags f = Qt::Widget); int style3d() const { return _style3d; } void setStyle3d(int style3d) { _style3d = style3d; } bool hasExpandIcon() const { return _hasExpandIcon; } void setHasExpandIcon(bool v) { _hasExpandIcon = v; } }; struct GridPosStruct { int _row; int _col; int _rowSpan; int _colSpan; GridPosStruct() : _row(0), _col(0), _rowSpan(0), _colSpan(0) { } GridPosStruct(int row, int col, int rowSpan, int colSpan) : _row(row), _col(col), _rowSpan(rowSpan), _colSpan(colSpan) { } }; //--------------------------------------------------------- // ExpanderHandle //--------------------------------------------------------- class ExpanderHandle : public QFrame { Q_OBJECT protected: enum ResizeMode { ResizeModeNone, ResizeModeHovering, ResizeModeDragging }; virtual void paintEvent(QPaintEvent*); private: int _handleWidth; ResizeMode _resizeMode; QPoint _dragLastGlobPos; protected: virtual void mousePressEvent(QMouseEvent*); virtual void mouseMoveEvent(QMouseEvent*); virtual void mouseReleaseEvent(QMouseEvent*); //virtual void leaveEvent(QEvent*); virtual QSize sizeHint() const; signals: void moved(int xDelta); public: ExpanderHandle(QWidget * parent = 0, int handleWidth = 4, Qt::WindowFlags f = Qt::Widget); }; //--------------------------------------------------------- // Strip //--------------------------------------------------------- class Strip : public QFrame { Q_OBJECT Q_PROPERTY( int expanderWidth READ expanderWidth WRITE setExpanderWidth ) int _expanderWidth; private: // Embedded strips cannot be selected, moved, or hidden. For example arranger and pianoroll. bool _isEmbedded; QPoint mouseWidgetOffset; bool dragOn; bool _visible; bool _selected; bool _highlight; protected: static constexpr int cMinStripWidth = 52; // Whether to propagate changes to other selected tracks. // This includes operating a control or using the universal up/down volume/ pan keys etc. bool _broadcastChanges; MusECore::Track* track; TrackNameLabel* label; QGridLayout* grid; int _curGridRow; Meter* meter[MusECore::MAX_CHANNELS]; // Extra width applied to the sizeHint, from user expanding the strip. int _userWidth; // Whether the strip is currently expanded with the user width. bool _isExpanded; ExpanderHandle* _handle; // The widget that will receive focus when we want to clear focus. QWidget* _focusYieldWidget; QPushButton* record; QPushButton* solo; QPushButton* mute; QPushButton* iR; // Input routing button QPushButton* oR; // Output routing button QGridLayout* sliderGrid; CompactComboBox* autoType; void setLabelText(); virtual void resizeEvent(QResizeEvent*); virtual void mousePressEvent(QMouseEvent *); virtual void mouseReleaseEvent(QMouseEvent *); virtual void mouseMoveEvent(QMouseEvent *); virtual void keyPressEvent(QKeyEvent *); virtual void paintEvent(QPaintEvent *); virtual void updateRouteButtons(); protected slots: virtual void componentChanged(int type, double val, bool off, int id, int scrollMode); virtual void componentMoved(int type, double val, int id, bool shift_pressed); virtual void componentPressed(int type, double val, int id); virtual void componentReleased(int type, double val, int id); virtual void componentIncremented(int type, double oldCompVal, double newCompVal, bool off, int id, int scrollMode); virtual void recordToggled(bool); void soloToggled(bool); void muteToggled(bool); virtual void focusYieldWidgetDestroyed(QObject*); virtual void heartBeat(); void setAutomationType(int t); virtual void changeTrackName(); virtual void trackNameLabelExpandClicked(); public slots: void resetPeaks(); virtual void songChanged(MusECore::SongChangedStruct_t) = 0; virtual void configChanged() = 0; virtual void changeUserWidth(int delta); virtual void incVolume(int v) = 0; virtual void incPan(int v) = 0; signals: void clearStripSelection(); void moveStrip(Strip *s); void visibleChanged(Strip *s, bool v); void userWidthChanged(Strip *s, int w); public: Strip(QWidget* parent, MusECore::Track* t, bool hasHandle = false, bool isEmbedded = true); virtual ~Strip(); // Destroy and rebuild strip components. virtual void buildStrip() { } // The widget that will receive focus when we want to clear focus. QWidget* focusYieldWidget() const { return _focusYieldWidget; } // Sets the widget that will receive focus when we want to clear focus. virtual void setFocusYieldWidget(QWidget*); // Sets up tabbing for the entire strip. // Accepts a previousWidget which can be null and returns the last widget in the strip, // which allows chaining other widgets. virtual QWidget* setupComponentTabbing(QWidget* previousWidget = 0) = 0; bool getStripVisible() const { return _visible; } void setStripVisible(bool v) { _visible = v; } static constexpr int FIXED_METER_WIDTH = 7; void setRecordFlag(bool flag); MusECore::Track* getTrack() const { return track; } void setHighLight(bool highlight); QString getLabelText(); // Setting to zero is deprecated. Use default constructor, new in Qt 5.15. #if QT_VERSION >= 0x050f00 void addGridWidget(QWidget* w, const GridPosStruct& pos, Qt::Alignment alignment = Qt::Alignment()); void addGridLayout(QLayout* l, const GridPosStruct& pos, Qt::Alignment alignment = Qt::Alignment()); #else void addGridWidget(QWidget* w, const GridPosStruct& pos, Qt::Alignment alignment = 0); void addGridLayout(QLayout* l, const GridPosStruct& pos, Qt::Alignment alignment = 0); #endif int userWidth() const { return _userWidth; } void setUserWidth(int w); bool isExpanded() const { return _isExpanded; } void setExpanded(bool v); bool handleForwardedKeyPress(QKeyEvent* ev); virtual QSize sizeHint() const; bool isSelected() { return _selected; } void setSelected(bool s); bool isEmbedded() const { return _isEmbedded; } void setEmbedded(bool embed); bool broadcastChanges() const { return _broadcastChanges; } void setBroadcastChanges(bool v) { _broadcastChanges = v; } void updateMuteIcon(); int expanderWidth() const { return _expanderWidth; } void setExpanderWidth(const int i) { _expanderWidth = i; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mman.c000066400000000000000000000100151404105311000153020ustar00rootroot00000000000000 #include #include #include #include "mman.h" #ifndef FILE_MAP_EXECUTE #define FILE_MAP_EXECUTE 0x0020 #endif /* FILE_MAP_EXECUTE */ static int __map_mman_error(const DWORD err, const int deferr) { if (err == 0) return 0; //TODO: implement return err; } static DWORD __map_mmap_prot_page(const int prot) { DWORD protect = 0; if (prot == PROT_NONE) return protect; if ((prot & PROT_EXEC) != 0) { protect = ((prot & PROT_WRITE) != 0) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; } else { protect = ((prot & PROT_WRITE) != 0) ? PAGE_READWRITE : PAGE_READONLY; } return protect; } static DWORD __map_mmap_prot_file(const int prot) { DWORD desiredAccess = 0; if (prot == PROT_NONE) return desiredAccess; if ((prot & PROT_READ) != 0) desiredAccess |= FILE_MAP_READ; if ((prot & PROT_WRITE) != 0) desiredAccess |= FILE_MAP_WRITE; if ((prot & PROT_EXEC) != 0) desiredAccess |= FILE_MAP_EXECUTE; return desiredAccess; } void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off) { HANDLE fm, h; void * map = MAP_FAILED; #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable: 4293) #endif const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD)) ? (DWORD)off : (DWORD)(off & 0xFFFFFFFFL); const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ? (DWORD)0 : (DWORD)((off >> 32) & 0xFFFFFFFFL); const DWORD protect = __map_mmap_prot_page(prot); const DWORD desiredAccess = __map_mmap_prot_file(prot); const off_t maxSize = off + (off_t)len; const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD)) ? (DWORD)maxSize : (DWORD)(maxSize & 0xFFFFFFFFL); const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD)) ? (DWORD)0 : (DWORD)((maxSize >> 32) & 0xFFFFFFFFL); #ifdef _MSC_VER #pragma warning(pop) #endif errno = 0; if (len == 0 /* Unsupported flag combinations */ || (flags & MAP_FIXED) != 0 /* Usupported protection combinations */ || prot == PROT_EXEC) { errno = EINVAL; return MAP_FAILED; } h = ((flags & MAP_ANONYMOUS) == 0) ? (HANDLE)_get_osfhandle(fildes) : INVALID_HANDLE_VALUE; if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE) { errno = EBADF; return MAP_FAILED; } fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL); if (fm == NULL) { errno = __map_mman_error(GetLastError(), EPERM); return MAP_FAILED; } map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len); CloseHandle(fm); if (map == NULL) { errno = __map_mman_error(GetLastError(), EPERM); return MAP_FAILED; } return map; } int munmap(void *addr, size_t len) { if (UnmapViewOfFile(addr)) return 0; errno = __map_mman_error(GetLastError(), EPERM); return -1; } int mprotect(void *addr, size_t len, int prot) { DWORD newProtect = __map_mmap_prot_page(prot); DWORD oldProtect = 0; if (VirtualProtect(addr, len, newProtect, &oldProtect)) return 0; errno = __map_mman_error(GetLastError(), EPERM); return -1; } int msync(void *addr, size_t len, int flags) { if (FlushViewOfFile(addr, len)) return 0; errno = __map_mman_error(GetLastError(), EPERM); return -1; } int mlock(const void *addr, size_t len) { if (VirtualLock((LPVOID)addr, len)) return 0; errno = __map_mman_error(GetLastError(), EPERM); return -1; } int munlock(const void *addr, size_t len) { if (VirtualUnlock((LPVOID)addr, len)) return 0; errno = __map_mman_error(GetLastError(), EPERM); return -1; } muse-4.0.0/src/muse/mman.h000066400000000000000000000024641404105311000153200ustar00rootroot00000000000000/* * sys/mman.h * mman-win32 */ #ifndef _SYS_MMAN_H_ #define _SYS_MMAN_H_ #ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. #define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. #endif /* All the headers include this file. */ #ifndef _MSC_VER #include <_mingw.h> #endif #include #define S_IRWXG (S_IRWXU >> 3) #define S_IRWXO (S_IRWXG >> 3) #ifdef __cplusplus extern "C" { #endif #define PROT_NONE 0 #define PROT_READ 1 #define PROT_WRITE 2 #define PROT_EXEC 4 #define MAP_FILE 0 #define MAP_SHARED 1 #define MAP_PRIVATE 2 #define MAP_TYPE 0xf #define MAP_FIXED 0x10 #define MAP_ANONYMOUS 0x20 #define MAP_ANON MAP_ANONYMOUS #define MAP_FAILED ((void *)-1) /* Flags for msync. */ #define MS_ASYNC 1 #define MS_SYNC 2 #define MS_INVALIDATE 4 void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); int munmap(void *addr, size_t len); int mprotect(void *addr, size_t len, int prot); int msync(void *addr, size_t len, int flags); int mlock(const void *addr, size_t len); int munlock(const void *addr, size_t len); #ifdef __cplusplus }; #endif #endif /* _SYS_MMAN_H_ */ muse-4.0.0/src/muse/mplugins/000077500000000000000000000000001404105311000160475ustar00rootroot00000000000000muse-4.0.0/src/muse/mplugins/.cvsignore000066400000000000000000000000341404105311000200440ustar00rootroot00000000000000mrconfigbase.h midifilter.h muse-4.0.0/src/muse/mplugins/CMakeLists.txt000066400000000000000000000054511404105311000206140ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## if ( ENABLE_EXPERIMENTAL ) QT5_WRAP_UI ( mplugins_experimental_uis rhythmbase.ui) set ( experimental_hdrs rhythm.h ) set ( experimental_srcs rhythm.cpp ${mplugins_experimental_uis} ) endif ( ENABLE_EXPERIMENTAL ) QT5_WRAP_CPP ( mplugins_mocs midifilterimpl.h miditransform.h midiitransform.h mittranspose.h mrconfig.h ${experimental_hdrs} ) ## ## UI files ## file (GLOB mplugins_ui_files itransformbase.ui midifilter.ui mittransposebase.ui transformbase.ui mrconfigbase.ui ) QT5_WRAP_UI ( mplugins_uis ${mplugins_ui_files} ) ## ## List of source files to compile ## file (GLOB mplugins_source_files midifilterimpl.cpp miditransform.cpp midiitransform.cpp mitplugin.cpp mittranspose.cpp mrconfig.cpp random.cpp ) ## ## Define target ## add_library ( mplugins ${MODULES_BUILD} ${mplugins_source_files} ${mplugins_mocs} ${mplugins_uis} ${experimental_srcs} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${mplugins_source_files} ${mplugins_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## # - tell cmake to name target transform.so instead of # libtransform.so # - use precompiled header files # set_target_properties ( mplugins # PROPERTIES PREFIX "" PROPERTIES OUTPUT_NAME muse_mplugins ) ## ## Linkage ## target_link_libraries( mplugins # midiplugin ${QT_LIBRARIES} widgets ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS mplugins DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/mplugins/itransformbase.ui000066400000000000000000001014471404105311000214340ustar00rootroot00000000000000 MidiInputTransformDialogBase 0 0 864 587 MusE: Midi Input Transformator true 11 11 11 11 6 Filter 11 11 11 11 6 All Equal Unequal false Note Poly Pressure Control Change Aftertouch Pitch Bend NRPN RPN Program Value 2 false Value 1 false Event Type false Ignore Equal Unequal Higher Lower Inside Outside 127 127 127 Ignore Equal Unequal Higher Lower Inside Outside Ignore Equal Unequal Higher Lower Inside Outside Ignore Equal Unequal Higher Lower Inside Outside Channel false Port false 127 127 127 127 127 Processing 11 11 11 11 6 Event Type false Keep Fix false Poly Pressure Control Change Aftertouch Pitch Bend NRPN RPN Program Keep Plus Minus Multiply Divide Fix Value 2 Invert ScaleMap Flip Dyn Random Value 1 false Value 2 false 9999 9999 99999999.000000000000000 127 127 Port false Keep Plus Minus Multiply Divide Fix Value 1 Invert Dyn Random Toggle Keep Plus Minus Multiply Divide Fix Value 1 Invert Dyn Random Keep Plus Minus Multiply Divide Fix Value 1 Invert Dyn Random Channel false 99999999.000000000000000 99999999.000000000000000 99999999.000000000000000 0 0 Modules 11 11 11 11 6 0 0 true 0 true 1 true true 2 true 3 true 4 true 0 0 Enable module 1 0 0 Enable module 2 0 0 Enable module 3 0 0 Enable module 4 0 0 Preset 6 11 11 11 11 6 0 0 0 0 Name: false 0 0 Comment: false 0 0 0 0 Function 6 11 11 11 11 6 0 0 0 0 create new preset &New true delete preset &Delete true Qt::Horizontal QSizePolicy::Expanding 20 20 &Dismiss true true 6 0 0 0 0 PresetList false 0 0 MusEGui::SpinBoxFP QDoubleSpinBox
spinboxFP.h
selEventOp selType selVal1Op selVal1a selVal1b selVal2Op selVal2a selVal2b selPortOp selPortVala selPortValb selChannelOp selChannelVala selChannelValb procEventOp procType procVal1Op procVal1b procVal2Op procVal2b procPortOp procPortValb procChannelOp procChannelValb modul1enable modul2enable modul3enable modul4enable funcOp nameEntry commentEntry presetList buttonNew buttonDelete buttonOk buttonOk clicked() MidiInputTransformDialogBase accept() 20 20 20 20
muse-4.0.0/src/muse/mplugins/midifilter.ui000066400000000000000000000507651404105311000205530ustar00rootroot00000000000000 MidiFilterConfigBase 0 0 348 431 0 0 MusE: Midi Input Filter 11 6 0 0 Record Filter 6 11 0 0 Note On 0 0 Poly Pressure 0 0 Controller 0 0 Program Change 0 0 After Touch 0 0 Pitch Bend 0 0 Sysex 0 0 Thru Filter 6 11 0 0 Note On 0 0 Poly Pressure 0 0 Controller 0 0 Program Change 0 0 After Touch 0 0 Pitch Bend 0 0 Sysex 0 0 Controller Filter 6 11 0 0 Channel Filter 11 3 0 0 30 32767 14 true 0 0 30 32767 10 true 0 0 30 32767 6 true 0 0 30 32767 12 true 0 0 25 0 30 32767 4 true 0 0 25 0 30 32767 2 true 0 0 30 32767 9 true 0 0 30 32767 8 true 0 0 25 0 30 32767 3 true 0 0 30 32767 13 true 0 0 30 32767 15 true 0 0 30 32767 16 true 0 0 30 32767 7 true 0 0 30 32767 11 true 0 0 30 32767 5 true 0 0 25 0 30 32767 1 true MusEGui::CtrlComboBox QWidget
ctrlcombo.h
rf1 rf2 rf3 rf4 rf5 rf6 rf7 tf1 tf2 tf3 tf4 tf5 tf6 tf7 cf1 cf2 cf3 cf4 cf5 cf6 cf7 cf8 cf9 cf10 cf11 cf12 cf13 cf14 cf15 cf16 ctrlcombo.h
muse-4.0.0/src/muse/mplugins/midifilterimpl.cpp000066400000000000000000000146551404105311000216000ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midifilterimpl.cpp,v 1.1.1.1 2003/10/27 18:52:49 wschweer Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "midifilterimpl.h" #include "ctrlcombo.h" #include #include namespace MusEGui { //--------------------------------------------------------- // setCtrl //--------------------------------------------------------- void MidiFilterConfig::setCtrl1(int n) { MusEGlobal::midiFilterCtrl1 = n-1; } void MidiFilterConfig::setCtrl2(int n) { MusEGlobal::midiFilterCtrl2 = n-1; } void MidiFilterConfig::setCtrl3(int n) { MusEGlobal::midiFilterCtrl3 = n-1; } void MidiFilterConfig::setCtrl4(int n) { MusEGlobal::midiFilterCtrl4 = n-1; } //--------------------------------------------------------- // MidiFilterConfig //--------------------------------------------------------- MidiFilterConfig::MidiFilterConfig(QDialog* parent) : QDialog(parent) { setupUi(this); cb1->setCurrentIndex(MusEGlobal::midiFilterCtrl1); cb2->setCurrentIndex(MusEGlobal::midiFilterCtrl2); cb3->setCurrentIndex(MusEGlobal::midiFilterCtrl3); cb4->setCurrentIndex(MusEGlobal::midiFilterCtrl4); rf1->setChecked(MusEGlobal::midiRecordType & 1); rf2->setChecked(MusEGlobal::midiRecordType & 2); rf3->setChecked(MusEGlobal::midiRecordType & 4); rf4->setChecked(MusEGlobal::midiRecordType & 8); rf5->setChecked(MusEGlobal::midiRecordType & 16); rf6->setChecked(MusEGlobal::midiRecordType & 32); rf7->setChecked(MusEGlobal::midiRecordType & 64); connect(rf1, SIGNAL(toggled(bool)), SLOT(recordChanged1(bool))); connect(rf2, SIGNAL(toggled(bool)), SLOT(recordChanged2(bool))); connect(rf3, SIGNAL(toggled(bool)), SLOT(recordChanged3(bool))); connect(rf4, SIGNAL(toggled(bool)), SLOT(recordChanged4(bool))); connect(rf5, SIGNAL(toggled(bool)), SLOT(recordChanged5(bool))); connect(rf6, SIGNAL(toggled(bool)), SLOT(recordChanged6(bool))); connect(rf7, SIGNAL(toggled(bool)), SLOT(recordChanged7(bool))); tf1->setChecked(MusEGlobal::midiThruType & 1); tf2->setChecked(MusEGlobal::midiThruType & 2); tf3->setChecked(MusEGlobal::midiThruType & 4); tf4->setChecked(MusEGlobal::midiThruType & 8); tf5->setChecked(MusEGlobal::midiThruType & 16); tf6->setChecked(MusEGlobal::midiThruType & 32); tf7->setChecked(MusEGlobal::midiThruType & 64); connect(tf1, SIGNAL(toggled(bool)), SLOT(thruChanged1(bool))); connect(tf2, SIGNAL(toggled(bool)), SLOT(thruChanged2(bool))); connect(tf3, SIGNAL(toggled(bool)), SLOT(thruChanged3(bool))); connect(tf4, SIGNAL(toggled(bool)), SLOT(thruChanged4(bool))); connect(tf5, SIGNAL(toggled(bool)), SLOT(thruChanged5(bool))); connect(tf6, SIGNAL(toggled(bool)), SLOT(thruChanged6(bool))); connect(tf7, SIGNAL(toggled(bool)), SLOT(thruChanged7(bool))); cf1->setChecked(MusEGlobal::midiInputChannel & 1); cf2->setChecked(MusEGlobal::midiInputChannel & 2); cf3->setChecked(MusEGlobal::midiInputChannel & 4); cf4->setChecked(MusEGlobal::midiInputChannel & 8); cf5->setChecked(MusEGlobal::midiInputChannel & 0x10); cf6->setChecked(MusEGlobal::midiInputChannel & 0x20); cf7->setChecked(MusEGlobal::midiInputChannel & 0x40); cf8->setChecked(MusEGlobal::midiInputChannel & 0x80); cf9->setChecked(MusEGlobal::midiInputChannel & 0x100); cf10->setChecked(MusEGlobal::midiInputChannel & 0x200); cf11->setChecked(MusEGlobal::midiInputChannel & 0x400); cf12->setChecked(MusEGlobal::midiInputChannel & 0x800); cf13->setChecked(MusEGlobal::midiInputChannel & 0x1000); cf14->setChecked(MusEGlobal::midiInputChannel & 0x2000); cf15->setChecked(MusEGlobal::midiInputChannel & 0x4000); cf16->setChecked(MusEGlobal::midiInputChannel & 0x8000); connect(cb1, SIGNAL(activated(int)), SLOT(setCtrl1(int))); connect(cb2, SIGNAL(activated(int)), SLOT(setCtrl2(int))); connect(cb3, SIGNAL(activated(int)), SLOT(setCtrl3(int))); connect(cb4, SIGNAL(activated(int)), SLOT(setCtrl4(int))); connect(cf1, SIGNAL(toggled(bool)), SLOT(channelChanged1(bool))); connect(cf2, SIGNAL(toggled(bool)), SLOT(channelChanged2(bool))); connect(cf3, SIGNAL(toggled(bool)), SLOT(channelChanged3(bool))); connect(cf4, SIGNAL(toggled(bool)), SLOT(channelChanged4(bool))); connect(cf5, SIGNAL(toggled(bool)), SLOT(channelChanged5(bool))); connect(cf6, SIGNAL(toggled(bool)), SLOT(channelChanged6(bool))); connect(cf7, SIGNAL(toggled(bool)), SLOT(channelChanged7(bool))); connect(cf8, SIGNAL(toggled(bool)), SLOT(channelChanged8(bool))); connect(cf9, SIGNAL(toggled(bool)), SLOT(channelChanged9(bool))); connect(cf10, SIGNAL(toggled(bool)), SLOT(channelChanged10(bool))); connect(cf11, SIGNAL(toggled(bool)), SLOT(channelChanged11(bool))); connect(cf12, SIGNAL(toggled(bool)), SLOT(channelChanged12(bool))); connect(cf13, SIGNAL(toggled(bool)), SLOT(channelChanged13(bool))); connect(cf14, SIGNAL(toggled(bool)), SLOT(channelChanged14(bool))); connect(cf15, SIGNAL(toggled(bool)), SLOT(channelChanged15(bool))); connect(cf16, SIGNAL(toggled(bool)), SLOT(channelChanged16(bool))); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void MidiFilterConfig::closeEvent(QCloseEvent* ev) { emit hideWindow(); QWidget::closeEvent(ev); } } // namespace MusEGui muse-4.0.0/src/muse/mplugins/midifilterimpl.h000066400000000000000000000077561404105311000212510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midifilterimpl.h,v 1.1.1.1 2003/10/27 18:52:40 wschweer Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDIFILTERIMP_H__ #define __MIDIFILTERIMP_H__ #include "globals.h" #include "ui_midifilter.h" class QCloseEvent; namespace MusEGui { //--------------------------------------------------------- // MidiFilterConfig //--------------------------------------------------------- class MidiFilterConfig : public QDialog, public Ui::MidiFilterConfigBase { Q_OBJECT void rChanged(bool f, int val) { if (f) MusEGlobal::midiRecordType |= val; else MusEGlobal::midiRecordType &= ~val; } void tChanged(bool f, int val) { if (f) MusEGlobal::midiThruType |= val; else MusEGlobal::midiThruType &= ~val; } void chChanged(bool f, int val) { if (f) MusEGlobal::midiInputChannel |= val; else MusEGlobal::midiInputChannel &= ~val; } virtual void closeEvent(QCloseEvent*); signals: void hideWindow(); private slots: void channelChanged1(bool f) { chChanged(f, 0x01); } void channelChanged2(bool f) { chChanged(f, 0x02); } void channelChanged3(bool f) { chChanged(f, 0x04); } void channelChanged4(bool f) { chChanged(f, 0x08); } void channelChanged5(bool f) { chChanged(f, 0x10); } void channelChanged6(bool f) { chChanged(f, 0x20); } void channelChanged7(bool f) { chChanged(f, 0x40); } void channelChanged8(bool f) { chChanged(f, 0x80); } void channelChanged9(bool f) { chChanged(f, 0x100); } void channelChanged10(bool f) { chChanged(f, 0x200); } void channelChanged11(bool f) { chChanged(f, 0x400); } void channelChanged12(bool f) { chChanged(f, 0x800); } void channelChanged13(bool f) { chChanged(f, 0x1000); } void channelChanged14(bool f) { chChanged(f, 0x2000); } void channelChanged15(bool f) { chChanged(f, 0x4000); } void channelChanged16(bool f) { chChanged(f, 0x8000); } void recordChanged1(bool f) { rChanged(f, 1); } void recordChanged2(bool f) { rChanged(f, 2); } void recordChanged3(bool f) { rChanged(f, 4); } void recordChanged4(bool f) { rChanged(f, 8); } void recordChanged5(bool f) { rChanged(f, 16); } void recordChanged6(bool f) { rChanged(f, 32); } void recordChanged7(bool f) { rChanged(f, 64); } void thruChanged1(bool f) { tChanged(f, 1); } void thruChanged2(bool f) { tChanged(f, 2); } void thruChanged3(bool f) { tChanged(f, 4); } void thruChanged4(bool f) { tChanged(f, 8); } void thruChanged5(bool f) { tChanged(f, 16); } void thruChanged6(bool f) { tChanged(f, 32); } void thruChanged7(bool f) { tChanged(f, 64); } void setCtrl1(int); void setCtrl2(int); void setCtrl3(int); void setCtrl4(int); public: MidiFilterConfig(QDialog* parent=0); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mplugins/midiitransform.cpp000066400000000000000000001727521404105311000216200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midiitransform.cpp,v 1.2.2.2 2009/05/24 21:43:44 terminator356 Exp $ // // (C) Copyright 2001-2003 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include "spinboxFP.h" #include "midi_consts.h" #include "midictrl.h" #include "midiitransform.h" #include "track.h" #include "song.h" #include "globals.h" #include "gconfig.h" #include "helper.h" // Forwards from header: #include #include #include "xml.h" #include "mpevent.h" #define MIDITRANSFORM_NOTE 0 #define MIDITRANSFORM_POLY 1 #define MIDITRANSFORM_CTRL 2 #define MIDITRANSFORM_ATOUCH 3 #define MIDITRANSFORM_PITCHBEND 4 #define MIDITRANSFORM_NRPN 5 #define MIDITRANSFORM_RPN 6 #define MIDITRANSFORM_PROGRAM 7 namespace MusECore { static int selTypeTable[] = { MIDITRANSFORM_NOTE, MIDITRANSFORM_POLY, MIDITRANSFORM_CTRL, MIDITRANSFORM_ATOUCH, MIDITRANSFORM_PITCHBEND, MIDITRANSFORM_NRPN, MIDITRANSFORM_RPN, MIDITRANSFORM_PROGRAM }; static int procTypeTable[] = { MIDITRANSFORM_POLY, MIDITRANSFORM_CTRL, MIDITRANSFORM_ATOUCH, MIDITRANSFORM_PITCHBEND, MIDITRANSFORM_NRPN, MIDITRANSFORM_RPN, MIDITRANSFORM_PROGRAM }; static int procVal2Map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12 }; struct TDict { TransformFunction id; const QString text; TDict(TransformFunction f, const QString& s) : id(f), text(s) {} }; static const TDict oplist[] = { TDict(Transform, QString("Transform")), TDict(Delete, QString("Filter")) }; //--------------------------------------------------------- // MidiInputTransform //--------------------------------------------------------- class MidiInputTransformation { public: QString name; QString comment; MusECore::TransformToggleState toggleState; ValOp selEventOp; int selType; ValOp selVal1; int selVal1a, selVal1b; ValOp selVal2; int selVal2a, selVal2b; ValOp selPort; int selPorta, selPortb; ValOp selChannel; int selChannela, selChannelb; InputTransformProcEventOp procEvent; int eventType; TransformOperator procVal1; int procVal1a, procVal1b; TransformOperator procVal2; int procVal2a, procVal2b; TransformOperator procPort; int procPorta, procPortb; TransformOperator procChannel; int procChannela, procChannelb; TransformFunction funcOp; int quantVal; MidiInputTransformation(const QString& s) { name = s; selEventOp = All; selType = MIDITRANSFORM_NOTE; selVal1 = Ignore; selVal1a = 0; selVal1b = 0; selVal2 = Ignore; selVal2a = 0; selVal2b = 0; procEvent = KeepType; eventType = MIDITRANSFORM_POLY; procVal1 = Keep; procVal1a = 0; procVal1b = 0; procVal2 = Keep; procVal2a = 0; procVal2b = 0; funcOp = Transform; quantVal = MusEGlobal::config.division; selPort = Ignore; selChannel = Ignore; selChannela = 0; selChannelb = 0; procPort = Keep; procChannel = Keep; procPorta = 0; procPortb = 0; procChannela = 0; procChannelb = 0; } void write(int level, Xml& xml) const; int apply(MidiRecordEvent& ev); bool typesMatch(MidiRecordEvent& e, int selType) const; }; typedef std::list MidiInputTransformationList; typedef std::list::iterator iMidiInputTransformation; typedef std::list::const_iterator ciMidiInputTransformation; // this is the list of defined transformations: static MidiInputTransformationList mtlist; // list of modules to apply: struct ITransModul { bool valid; MidiInputTransformation* transform; }; const int MIDI_INPUT_TRANSFORMATIONS = 4; static ITransModul modules[MIDI_INPUT_TRANSFORMATIONS]; //--------------------------------------------------------- // applyMidiInputTransformation // return false if event should be dropped // (filter) //--------------------------------------------------------- bool applyMidiInputTransformation(MidiRecordEvent& event) { for (int i = 0; i < 4; ++i) { if (modules[i].valid && modules[i].transform) { int rv = modules[i].transform->apply(event); if (rv == 1) { if(MusEGlobal::debugMsg) printf("drop input event\n"); } if (rv) return rv != 1; } } return true; } //--------------------------------------------------------- // filterValOp //--------------------------------------------------------- static bool filterValOp(ValOp op, int val, int val1, int val2) { switch (op) { case Ignore: break; case Equal: if (val != val1) return true; break; case Unequal: if (val == val1) return true; break; case Higher: if (val <= val1) return true; break; case Lower: if (val >= val1) return true; break; case Inside: if ((val < val1) || (val >= val2)) return true; break; case Outside: if ((val >= val1) && (val < val2)) return true; break; } return false; } //--------------------------------------------------------- // apply // apply Select filter // return 0 - not applied // 1 - drop event // 2 - event changed //--------------------------------------------------------- int MidiInputTransformation::apply(MidiRecordEvent& event) { int t = event.type(); switch (selEventOp) { case Equal: switch(t) { case ME_NOTEON: case ME_NOTEOFF: if (selType != MIDITRANSFORM_NOTE) return 0; break; default: if(!typesMatch(event, selType)) return 0; break; } break; case Unequal: switch(event.type()) { case ME_NOTEON: case ME_NOTEOFF: if (selType == MIDITRANSFORM_NOTE) return 0; break; default: if(typesMatch(event, selType)) return 0; break; } break; default: break; } if (filterValOp(selVal1, event.dataA(), selVal1a, selVal1b)) return 0; if (filterValOp(selVal2, event.dataB(), selVal2a, selVal2b)) return 0; if (filterValOp(selPort, event.port(), selPorta, selPortb)) return 0; if (filterValOp(selChannel, event.channel(), selChannela, selChannelb)) return 0; if (funcOp == Delete) return 1; // discard event // transform event //printf("transform\n"); if (procEvent != KeepType) { switch(eventType) { case MIDITRANSFORM_POLY: event.setType(ME_POLYAFTER); break; case MIDITRANSFORM_CTRL: event.setType(ME_CONTROLLER); break; case MIDITRANSFORM_ATOUCH: event.setType(ME_AFTERTOUCH); break; case MIDITRANSFORM_PITCHBEND: { event.setType(ME_PITCHBEND); } break; case MIDITRANSFORM_NRPN: { event.setA(MidiController::NRPN); event.setType(ME_CONTROLLER); } break; case MIDITRANSFORM_RPN: { event.setA(MidiController::RPN); event.setType(ME_CONTROLLER); } break; case MIDITRANSFORM_PROGRAM: event.setType(ME_PROGRAM); break; default: break; } } //--------------------------------------------------- // transform value A //--------------------------------------------------- int val = event.dataA(); switch (procVal1) { case Keep: break; case Plus: val += procVal1a; break; case Minus: val -= procVal1a; break; case Multiply: val = int(val * (procVal1a/100.0) + .5); break; case Divide: val = int(val / (procVal1a/100.0) + .5); break; case Fix: val = procVal1a; break; case Value: val = procVal2a; break; case Invert: val = 127 - val; break; case ScaleMap: printf("scale map not implemented\n"); break; case Flip: val = procVal1a - val; break; case Dynamic: // "crescendo" printf("transform not implemented\n"); break; case Random: { int range = procVal1b - procVal1a; if (range > 0) val = (rand() % range) + procVal1a; else if (range < 0) val = (rand() % -range) + procVal1b; else val = procVal1a; } break; default: break; } if (val < 0) val = 0; if (val > 127) val = 127; event.setA(val); //--------------------------------------------------- // transform value B //--------------------------------------------------- val = event.dataB(); switch (procVal2) { case Plus: val += procVal2a; break; case Minus: val -= procVal2a; break; case Multiply: val = int(val * (procVal2a/100.0) + .5); break; case Divide: val = int(val / (procVal2a/100.0) + .5); break; case Fix: val = procVal2a; break; case Value: val = procVal1a; break; case Invert: val = 127 - val; break; case Dynamic: printf("transform not implemented\n"); break; case Random: { int range = procVal2b - procVal2a; if (range > 0) val = (rand() % range) + procVal2a; else if (range < 0) val = (rand() % -range) + procVal2b; else val = procVal2a; } break; case Toggle: { if(event.type() == ME_CONTROLLER) { int num = event.dataA(); bool state = toggleState.ctrlState(num); if(state) { state = false; val = procVal2a; } else { state = true; val = procVal2b; } toggleState.setCtrlState(num, state); } else printf("toggle implemented only for controllers\n"); } break; case ScaleMap: case Keep: case Flip: break; } if (val < 0) val = 0; if (val > 127) val = 127; event.setB(val); //--------------------------------------------------- // transform port //--------------------------------------------------- val = event.port(); switch (procPort) { case Plus: val += procPorta; break; case Minus: val -= procPorta; break; case Multiply: val = int(val * (procPorta/100.0) + .5); break; case Divide: val = int(val / (procPorta/100.0) + .5); break; case Fix: val = procPorta; break; case Value: val = procPorta; break; case Invert: val = 15 - val; break; case Dynamic: printf("transform not implemented\n"); break; case Random: { int range = procPortb - procPorta; if (range > 0) val = (rand() % range) + procPorta; else if (range < 0) val = (rand() % -range) + procPortb; else val = procPorta; } break; case ScaleMap: case Keep: case Flip: case Toggle: break; } if (val < 0) val = 0; if (val > 15) val = 15; event.setPort(val); //--------------------------------------------------- // transform channel //--------------------------------------------------- val = event.channel(); switch (procChannel) { case Plus: val += procChannela; break; case Minus: val -= procChannela; break; case Multiply: val = int(val * (procChannela/100.0) + .5); break; case Divide: val = int(val / (procChannela/100.0) + .5); break; case Fix: val = procChannela; break; case Value: val = procChannela; break; case Invert: val = 16 - val; break; case Dynamic: printf("transform not implemented\n"); break; case Random: { int range = procChannelb - procChannela; if (range > 0) val = (rand() % range) + procChannela; else if (range < 0) val = (rand() % -range) + procChannelb; else val = procChannela; } break; case ScaleMap: case Keep: case Flip: case Toggle: break; } if (val < 0) val = 0; if (val > 15) val = 15; event.setChannel(val); return 2; } //--------------------------------------------------------- // typesMatch //--------------------------------------------------------- bool MidiInputTransformation::typesMatch(MidiRecordEvent& e, int selType) const { bool matched = false; int t = e.type(); switch (selType) { case MIDITRANSFORM_NOTE: matched = ((t == ME_NOTEON) || (t == ME_NOTEOFF)); break; case MIDITRANSFORM_POLY: matched = (t == ME_POLYAFTER); break; case MIDITRANSFORM_CTRL: matched = (t == ME_CONTROLLER); break; case MIDITRANSFORM_ATOUCH: matched = (t == ME_AFTERTOUCH); break; case MIDITRANSFORM_PITCHBEND: { //if (t == ME_CONTROLLER) { // MidiController::ControllerType c = midiControllerType(e.dataA()); // matched = (c == MidiController::Pitch); matched = (t = ME_PITCHBEND); } break; case MIDITRANSFORM_NRPN: { if (t == ME_CONTROLLER) { MidiController::ControllerType c = midiControllerType(e.dataA()); matched = (c == MidiController::NRPN); } } break; case MIDITRANSFORM_RPN: { if (t == ME_CONTROLLER) { MidiController::ControllerType c = midiControllerType(e.dataA()); matched = (c == MidiController::RPN); } } break; case MIDITRANSFORM_PROGRAM: matched = (t == ME_PROGRAM); break; default: fprintf(stderr, "Error matching type in MidiTransformerDialog: unknown eventtype!\n"); break; } //printf("Event type=%d, selType =%d matched=%d\n", e.type(), selType, matched); return matched; } //--------------------------------------------------------- // writeMidiTransforms //--------------------------------------------------------- void writeMidiInputTransforms(int level, Xml& xml) { for (iMidiInputTransformation i = mtlist.begin(); i != mtlist.end(); ++i) { (*i)->write(level, xml); } } //--------------------------------------------------------- // write //--------------------------------------------------------- void MidiInputTransformation::write(int level, Xml& xml) const { xml.tag(level++, "midiInputTransform"); xml.strTag(level, "name", name); xml.strTag(level, "comment", comment); xml.intTag(level, "function", int(funcOp)); // apply this transformation? for (int i = 0; i < MIDI_INPUT_TRANSFORMATIONS; ++i) { if (modules[i].transform == this) { xml.intTag(level, "apply", int(modules[i].valid)); break; } } if (funcOp == Quantize) { xml.intTag(level, "quantVal", quantVal); } if (funcOp == Transform || funcOp == Insert) { if (procEvent != KeepType) { xml.intTag(level, "procEventOp", int(procEvent)); xml.intTag(level, "eventType", int(eventType)); } if (procVal1 != Keep) { xml.intTag(level, "procVal1Op", int(procVal1)); xml.intTag(level, "procVal1a", procVal1a); xml.intTag(level, "procVal1b", procVal1b); } if (procVal2 != Keep) { xml.intTag(level, "procVal2Op", int(procVal2)); xml.intTag(level, "procVal2a", procVal2a); xml.intTag(level, "procVal2b", procVal2b); } if (procPort != Keep) { xml.intTag(level, "procPortOp", int(procPort)); xml.intTag(level, "procPorta", procPorta); xml.intTag(level, "procPortb", procPortb); } if (procChannel != Keep) { xml.intTag(level, "procChannelOp", int(procChannel)); xml.intTag(level, "procChannela", procChannela); xml.intTag(level, "procChannelb", procChannelb); } } if (selEventOp != Ignore) { xml.intTag(level, "selEventOp", int(selEventOp)); xml.intTag(level, "selEventType", int(selType)); } if (selVal1 != Ignore) { xml.intTag(level, "selVal1Op", int(selVal1)); xml.intTag(level, "selVal1a", selVal1a); xml.intTag(level, "selVal1b", selVal1b); } if (selVal2 != Ignore) { xml.intTag(level, "selVal2Op", int(selVal2)); xml.intTag(level, "selVal2a", selVal2a); xml.intTag(level, "selVal2b", selVal2b); } if (selPort != Ignore) { xml.intTag(level, "selPortOp", int(selPort)); xml.intTag(level, "selPorta", selPorta); xml.intTag(level, "selPortb", selPortb); } if (selChannel != Ignore) { xml.intTag(level, "selChannelOp", int(selChannel)); xml.intTag(level, "selChannela", selChannela); xml.intTag(level, "selChannelb", selChannelb); } xml.etag(level, "midiInputTransform"); } //--------------------------------------------------------- // readMidiTransform //--------------------------------------------------------- void readMidiInputTransform(Xml& xml) { MidiInputTransformation trans(QString("new")); int apply = -1; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "name") trans.name = xml.parse1(); else if (tag == "comment") trans.comment = xml.parse1(); else if (tag == "function") trans.funcOp = TransformFunction(xml.parseInt()); else if (tag == "quantVal") trans.quantVal = xml.parseInt(); else if (tag == "procEventOp") trans.procEvent = InputTransformProcEventOp(xml.parseInt()); else if (tag == "eventType") trans.eventType = xml.parseInt(); else if (tag == "procVal1Op") trans.procVal1 = TransformOperator(xml.parseInt()); else if (tag == "procVal1a") trans.procVal1a = xml.parseInt(); else if (tag == "procVal1b") trans.procVal1b = xml.parseInt(); else if (tag == "procVal2Op") trans.procVal2 = TransformOperator(xml.parseInt()); else if (tag == "procVal2a") trans.procVal2a = xml.parseInt(); else if (tag == "procVal2b") trans.procVal2b = xml.parseInt(); else if (tag == "selEventOp") trans.selEventOp = ValOp(xml.parseInt()); else if (tag == "selEventType") trans.selType = xml.parseInt(); else if (tag == "selVal1Op") trans.selVal1 = ValOp(xml.parseInt()); else if (tag == "selVal1a") trans.selVal1a = xml.parseInt(); else if (tag == "selVal1b") trans.selVal1b = xml.parseInt(); else if (tag == "selVal2Op") trans.selVal2 = ValOp(xml.parseInt()); else if (tag == "selVal2a") trans.selVal2a = xml.parseInt(); else if (tag == "selVal2b") trans.selVal2b = xml.parseInt(); else if (tag == "procPortOp") trans.procPort = TransformOperator(xml.parseInt()); else if (tag == "procPorta") trans.procPorta = xml.parseInt(); else if (tag == "procPortb") trans.procPortb = xml.parseInt(); else if (tag == "procChannelOp") trans.procChannel = TransformOperator(xml.parseInt()); else if (tag == "procChannela") trans.procChannela = xml.parseInt(); else if (tag == "procChannelb") trans.procChannelb = xml.parseInt(); else if (tag == "selPortOp") trans.selPort = ValOp(xml.parseInt()); else if (tag == "selPorta") trans.selPorta = xml.parseInt(); else if (tag == "selPortb") trans.selPortb = xml.parseInt(); else if (tag == "selChannelOp") trans.selChannel = ValOp(xml.parseInt()); else if (tag == "selChannela") trans.selChannela = xml.parseInt(); else if (tag == "selChannelb") trans.selChannelb = xml.parseInt(); else if (tag == "apply") apply = xml.parseInt(); else xml.unknown("midiInputTransform"); break; case Xml::TagEnd: if (xml.s1() == "midiInputTransform") { // printf("midi Input transform <%s> apply %d\n", // trans.name.toLatin1().constData(), apply); // By T356. A previous bug caused some .med files to grow very large // with duplicate transforms. Here we can eliminate those duplicates. for(iMidiInputTransformation i = mtlist.begin(); i != mtlist.end(); ++i) { if((*i)->name == trans.name) { return; } } MidiInputTransformation* t = new MidiInputTransformation(trans); // search free slot in modules if (apply != -1) { for (int i = 0; i < MIDI_INPUT_TRANSFORMATIONS; ++i) { if (modules[i].transform == 0) { modules[i].transform = t; modules[i].valid = apply; break; } } } mtlist.push_back(t); return; } default: break; } } } //--------------------------------------------------------- // clearMidiInputTransforms //--------------------------------------------------------- void clearMidiInputTransforms() { for (int i = 0; i < MIDI_INPUT_TRANSFORMATIONS; ++i) { modules[i].transform = 0; modules[i].valid = false; } for (iMidiInputTransformation i = mtlist.begin(); i != mtlist.end(); ++i) { MidiInputTransformation* t = *i; if(t) delete t; } mtlist.clear(); } } // namespace MusECore namespace MusEGui { //--------------------------------------------------------- // MidiInputTransformDialog // Widgets: // presetList nameEntry commentEntry // selEventOp selType // selVal1Op selVal1a selVal1b // selVal2Op selVal2a selVal2b // // procEventOp procType // procVal1Op procVal1a procVal1b // procVal2Op procVal2a procVal2b // funcOp funcQuantVal // buttonNew buttonDelete // // modulGroup // modul1select modul1enable // modul2select modul2enable // modul3select modul3enable // modul4select modul4enable // // selPortOp selPortVala selPortValb // selChannelOp selChannelVala selChannelValb // // procPortOp procPortVala procPortValb // procChannelOp procChannelVala procChannelValb //--------------------------------------------------------- MidiInputTransformDialog::MidiInputTransformDialog(QDialog* parent, Qt::WindowFlags fl) : QDialog(parent, fl) { setupUi(this); cindex = 0; cmodul = 0; cmt = 0; modulGroup = new QButtonGroup; modulGroup->addButton(modul1select,0); modulGroup->addButton(modul2select,1); modulGroup->addButton(modul3select,2); modulGroup->addButton(modul4select,3); for (unsigned i = 0; i < sizeof(MusECore::oplist)/sizeof(*MusECore::oplist); ++i) funcOp->insertItem(i, MusECore::oplist[i].text); connect(buttonNew, SIGNAL(clicked()), SLOT(presetNew())); connect(buttonDelete, SIGNAL(clicked()), SLOT(presetDelete())); connect(selEventOp, SIGNAL(activated(int)), SLOT(selEventOpSel(int))); connect(selType, SIGNAL(activated(int)), SLOT(selTypeSel(int))); connect(selVal1Op, SIGNAL(activated(int)), SLOT(selVal1OpSel(int))); connect(selVal2Op, SIGNAL(activated(int)), SLOT(selVal2OpSel(int))); connect(procEventOp, SIGNAL(activated(int)), SLOT(procEventOpSel(int))); connect(procType, SIGNAL(activated(int)), SLOT(procEventTypeSel(int))); connect(procVal1Op, SIGNAL(activated(int)), SLOT(procVal1OpSel(int))); connect(procVal2Op, SIGNAL(activated(int)), SLOT(procVal2OpSel(int))); connect(funcOp, SIGNAL(activated(int)), SLOT(funcOpSel(int))); connect(presetList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), SLOT(presetChanged(QListWidgetItem*, QListWidgetItem*))); connect(nameEntry, SIGNAL(textChanged(const QString&)), SLOT(nameChanged(const QString&))); connect(commentEntry, SIGNAL(textChanged()), SLOT(commentChanged())); connect(selVal1a, SIGNAL(valueChanged(int)), SLOT(selVal1aChanged(int))); connect(selVal1b, SIGNAL(valueChanged(int)), SLOT(selVal1bChanged(int))); connect(selVal2a, SIGNAL(valueChanged(int)), SLOT(selVal2aChanged(int))); connect(selVal2b, SIGNAL(valueChanged(int)), SLOT(selVal2bChanged(int))); connect(procVal1a, SIGNAL(valueChanged(int)), SLOT(procVal1aChanged(int))); connect(procVal1b, SIGNAL(valueChanged(int)), SLOT(procVal1bChanged(int))); connect(procVal2a, SIGNAL(valueChanged(int)), SLOT(procVal2aChanged(int))); connect(procVal2b, SIGNAL(valueChanged(int)), SLOT(procVal2bChanged(int))); connect(modul1enable, SIGNAL(toggled(bool)), SLOT(modul1enableChanged(bool))); connect(modul2enable, SIGNAL(toggled(bool)), SLOT(modul2enableChanged(bool))); connect(modul3enable, SIGNAL(toggled(bool)), SLOT(modul3enableChanged(bool))); connect(modul4enable, SIGNAL(toggled(bool)), SLOT(modul4enableChanged(bool))); connect(modulGroup, SIGNAL(buttonClicked(int)), SLOT(changeModul(int))); connect(selPortOp, SIGNAL(activated(int)), SLOT(selPortOpSel(int))); connect(selPortVala, SIGNAL(valueChanged(int)), SLOT(selPortValaChanged(int))); connect(selPortValb, SIGNAL(valueChanged(int)), SLOT(selPortValbChanged(int))); connect(selChannelOp, SIGNAL(activated(int)), SLOT(selChannelOpSel(int))); connect(selChannelVala, SIGNAL(valueChanged(int)), SLOT(selChannelValaChanged(int))); connect(selChannelValb, SIGNAL(valueChanged(int)), SLOT(selChannelValbChanged(int))); connect(procPortOp, SIGNAL(activated(int)), SLOT(procPortOpSel(int))); connect(procPortVala, SIGNAL(valueChanged(int)), SLOT(procPortValaChanged(int))); connect(procPortValb, SIGNAL(valueChanged(int)), SLOT(procPortValbChanged(int))); connect(procChannelOp, SIGNAL(activated(int)), SLOT(procChannelOpSel(int))); connect(procChannelVala, SIGNAL(valueChanged(int)), SLOT(procChannelValaChanged(int))); connect(procChannelValb, SIGNAL(valueChanged(int)), SLOT(procChannelValbChanged(int))); //--------------------------------------------------- // populate preset list //--------------------------------------------------- updatePresetList(); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MidiInputTransformDialog::songChanged(MusECore::SongChangedStruct_t flags) { // Whenever a song is loaded, flags is -1. Since transforms are part of configuration, // use SC_CONFIG here, to filter unwanted song change events. if(flags & SC_CONFIG) updatePresetList(); } //--------------------------------------------------------- // createDefaultPreset //--------------------------------------------------------- MusECore::MidiInputTransformation* MidiInputTransformDialog::createDefaultPreset() { // create default "New" preset MusECore::MidiInputTransformation* pre = new MusECore::MidiInputTransformation(tr("New")); MusECore::mtlist.push_back(pre); presetList->blockSignals(true); presetList->addItem(tr("New")); presetList->setCurrentRow(0); presetList->blockSignals(false); return pre; } //--------------------------------------------------------- // updatePresetList //--------------------------------------------------------- void MidiInputTransformDialog::updatePresetList() { cmt = 0; cindex = -1; presetList->clear(); modul1select->setChecked(true); for (MusECore::iMidiInputTransformation i = MusECore::mtlist.begin(); i != MusECore::mtlist.end(); ++i) { presetList->blockSignals(true); presetList->addItem((*i)->name); presetList->blockSignals(false); if (cmt == 0) cmt = *i; } if (cmt == 0) { // create default "New" preset cmt = createDefaultPreset(); } // Force it! Don't rely on a signal. presetChanged(presetList->item(0), 0); changeModul(0); modul1enable->setChecked(MusECore::modules[0].valid); modul2enable->setChecked(MusECore::modules[1].valid); modul3enable->setChecked(MusECore::modules[2].valid); modul4enable->setChecked(MusECore::modules[3].valid); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void MidiInputTransformDialog::closeEvent(QCloseEvent* ev) { emit hideWindow(); QWidget::closeEvent(ev); } //--------------------------------------------------------- // accept //--------------------------------------------------------- void MidiInputTransformDialog::accept() { reject(); } //--------------------------------------------------------- // reject //--------------------------------------------------------- void MidiInputTransformDialog::reject() { close(); } //--------------------------------------------------------- // setValOp //--------------------------------------------------------- void MidiInputTransformDialog::setValOp(QWidget* a, QWidget* b, MusECore::ValOp op) { switch (op) { case MusECore::Ignore: a->setEnabled(false); b->setEnabled(false); break; case MusECore::Equal: case MusECore::Unequal: case MusECore::Higher: case MusECore::Lower: a->setEnabled(true); b->setEnabled(false); break; case MusECore::Inside: case MusECore::Outside: a->setEnabled(true); b->setEnabled(true); break; } } //--------------------------------------------------------- // selEventOpSel //--------------------------------------------------------- void MidiInputTransformDialog::selEventOpSel(int val) { selType->setEnabled(val != MusECore::All); cmt->selEventOp = MusECore::ValOp(val); selVal1aChanged(cmt->selVal1a); selVal1bChanged(cmt->selVal1b); } //--------------------------------------------------------- // selTypeSel //--------------------------------------------------------- void MidiInputTransformDialog::selTypeSel(int val) { cmt->selType = MusECore::selTypeTable[val]; selVal1aChanged(cmt->selVal1a); selVal1bChanged(cmt->selVal1b); } //--------------------------------------------------------- // selVal1OpSel //--------------------------------------------------------- void MidiInputTransformDialog::selVal1OpSel(int val) { setValOp(selVal1a, selVal1b, MusECore::ValOp(val)); cmt->selVal1 = MusECore::ValOp(val); } //--------------------------------------------------------- // selVal2OpSel //--------------------------------------------------------- void MidiInputTransformDialog::selVal2OpSel(int val) { setValOp(selVal2a, selVal2b, MusECore::ValOp(val)); cmt->selVal2 = MusECore::ValOp(val); } //--------------------------------------------------------- // procEventOpSel //--------------------------------------------------------- void MidiInputTransformDialog::procEventOpSel(int val) { MusECore::InputTransformProcEventOp op = val == 0 ? MusECore::KeepType : MusECore::FixType; procType->setEnabled(op == MusECore::FixType); cmt->procEvent = op; procVal1aChanged(cmt->procVal1a); procVal1bChanged(cmt->procVal1b); } //--------------------------------------------------------- // procEventTypeSel //--------------------------------------------------------- void MidiInputTransformDialog::procEventTypeSel(int val) { cmt->eventType = MusECore::procTypeTable[val]; procVal1aChanged(cmt->procVal1a); procVal1bChanged(cmt->procVal1b); } //--------------------------------------------------------- // procVal1OpSel //--------------------------------------------------------- void MidiInputTransformDialog::procVal1OpSel(int val) { cmt->procVal1 = MusECore::TransformOperator(val); switch(MusECore::TransformOperator(val)) { case MusECore::Keep: case MusECore::Invert: procVal1a->setEnabled(false); procVal1b->setEnabled(false); break; case MusECore::Multiply: case MusECore::Divide: procVal1a->setEnabled(true); procVal1a->setDecimals(2); procVal1b->setEnabled(false); break; case MusECore::Plus: case MusECore::Minus: case MusECore::Fix: case MusECore::Value: case MusECore::Flip: procVal1a->setDecimals(0); procVal1a->setEnabled(true); procVal1b->setEnabled(false); break; case MusECore::Random: case MusECore::ScaleMap: case MusECore::Dynamic: case MusECore::Toggle: procVal1a->setDecimals(0); procVal1a->setEnabled(true); procVal1b->setEnabled(true); break; } procVal1aChanged(cmt->procVal1a); procVal1bChanged(cmt->procVal1b); } //--------------------------------------------------------- // procVal2OpSel //--------------------------------------------------------- void MidiInputTransformDialog::procVal2OpSel(int val) { MusECore::TransformOperator op = MusECore::TransformOperator(MusECore::procVal2Map[val]); cmt->procVal2 = op; procVal2OpUpdate(op); } //--------------------------------------------------------- // procVal2OpUpdate //--------------------------------------------------------- void MidiInputTransformDialog::procVal2OpUpdate(MusECore::TransformOperator op) { switch (op) { case MusECore::Keep: case MusECore::Invert: procVal2a->setEnabled(false); procVal2b->setEnabled(false); break; case MusECore::Multiply: case MusECore::Divide: procVal2a->setEnabled(true); procVal2a->setDecimals(2); procVal2b->setEnabled(false); break; case MusECore::Plus: case MusECore::Minus: case MusECore::Fix: case MusECore::Value: procVal2a->setDecimals(0); procVal2a->setEnabled(true); procVal2b->setEnabled(false); break; case MusECore::Random: case MusECore::Dynamic: case MusECore::Toggle: procVal2a->setDecimals(0); procVal2a->setEnabled(true); procVal2b->setEnabled(true); break; default: break; } } //--------------------------------------------------------- // funcOpSel //--------------------------------------------------------- void MidiInputTransformDialog::funcOpSel(int val) { MusECore::TransformFunction op = MusECore::oplist[val].id; bool isFuncOp(op == MusECore::Transform); procEventOp->setEnabled(isFuncOp); procType->setEnabled(isFuncOp); procVal1Op->setEnabled(isFuncOp); procVal1a->setEnabled(isFuncOp); procVal1b->setEnabled(isFuncOp); procVal2Op->setEnabled(isFuncOp); procVal2a->setEnabled(isFuncOp); procVal2b->setEnabled(isFuncOp); procPortOp->setEnabled(isFuncOp); procPortVala->setEnabled(isFuncOp); procPortValb->setEnabled(isFuncOp); procChannelOp->setEnabled(isFuncOp); procChannelVala->setEnabled(isFuncOp); procChannelValb->setEnabled(isFuncOp); if (isFuncOp) { procEventOpSel(cmt->procEvent); procVal1OpSel(cmt->procVal1); procVal2OpUpdate(cmt->procVal2); procPortOpSel(cmt->procPort); procChannelOpSel(cmt->procChannel); } cmt->funcOp = op; } //--------------------------------------------------------- // presetNew //--------------------------------------------------------- void MidiInputTransformDialog::presetNew() { QString name; for (int i = 0;; ++i) { name = QString("New-") + QString::number(i); MusECore::iMidiInputTransformation imt; for (imt = MusECore::mtlist.begin(); imt != MusECore::mtlist.end(); ++imt) { if (name == (*imt)->name) break; } if (imt == MusECore::mtlist.end()) break; } MusECore::MidiInputTransformation* mt = new MusECore::MidiInputTransformation(name); MusECore::mtlist.push_back(mt); QListWidgetItem* lbi = new QListWidgetItem(name); presetList->blockSignals(true); presetList->addItem(lbi); presetList->setCurrentItem(lbi); presetList->blockSignals(false); // Force it! Don't rely on a signal. presetChanged(lbi, 0); } //--------------------------------------------------------- // presetDelete //--------------------------------------------------------- void MidiInputTransformDialog::presetDelete() { if (presetList->count() == 0 || cindex < 0) return; MusECore::iMidiInputTransformation mt = MusECore::mtlist.begin(); for (int i = 0; i < cindex && mt != MusECore::mtlist.end(); ++i, ++mt) { } if(mt == MusECore::mtlist.end()) return; MusECore::mtlist.erase(mt); presetList->blockSignals(true); QListWidgetItem* take_item = presetList->takeItem(cindex); presetList->blockSignals(false); if(take_item) delete take_item; // Make sure there's a default item. if(presetList->count() == 0) cmt = createDefaultPreset(); // Force it! Don't rely on a signal. presetChanged(presetList->currentItem(), 0); } //--------------------------------------------------------- // nameChanged //--------------------------------------------------------- void MidiInputTransformDialog::nameChanged(const QString& s) { if(cindex < 0) return; cmt->name = s; QListWidgetItem* item = presetList->item(cindex); if (item && s != item->text()) { item->setText(s); } } //--------------------------------------------------------- // commentChanged //--------------------------------------------------------- void MidiInputTransformDialog::commentChanged() { cmt->comment = commentEntry->toPlainText(); } //--------------------------------------------------------- // selVal1aChanged //--------------------------------------------------------- void MidiInputTransformDialog::selVal1aChanged(int val) { cmt->selVal1a = val; if ((cmt->selEventOp != MusECore::All) && (cmt->selType == MIDITRANSFORM_NOTE)) { selVal1a->setSuffix(" - " + MusECore::pitch2string(val)); } else { if(!selVal1a->suffix().isEmpty()) selVal1a->setSuffix(QString("")); } } //--------------------------------------------------------- // selVal1bChanged //--------------------------------------------------------- void MidiInputTransformDialog::selVal1bChanged(int val) { cmt->selVal1b = val; if ((cmt->selEventOp != MusECore::All) && (cmt->selType == MIDITRANSFORM_NOTE)) { selVal1b->setSuffix(" - " + MusECore::pitch2string(val)); } else { if(!selVal1b->suffix().isEmpty()) selVal1b->setSuffix(QString("")); } } //--------------------------------------------------------- // selVal2aChanged //--------------------------------------------------------- void MidiInputTransformDialog::selVal2aChanged(int val) { cmt->selVal2a = val; } //--------------------------------------------------------- // selVal2bChanged //--------------------------------------------------------- void MidiInputTransformDialog::selVal2bChanged(int val) { cmt->selVal2b = val; } //--------------------------------------------------------- // procVal1aChanged //--------------------------------------------------------- void MidiInputTransformDialog::procVal1aChanged(int val) { cmt->procVal1a = val; if((cmt->procEvent == MusECore::KeepType && cmt->selType == MIDITRANSFORM_NOTE) && (cmt->procVal1 == MusECore::Fix || cmt->procVal1 == MusECore::ScaleMap || cmt->procVal1 == MusECore::Dynamic || cmt->procVal1 == MusECore::Random || cmt->procVal1 == MusECore::Flip)) { procVal1a->setSuffix(" - " + MusECore::pitch2string(val)); } else { if(!procVal1a->suffix().isEmpty()) procVal1a->setSuffix(QString("")); } } //--------------------------------------------------------- // procVal1bChanged //--------------------------------------------------------- void MidiInputTransformDialog::procVal1bChanged(int val) { cmt->procVal1b = val; if((cmt->procEvent == MusECore::KeepType && cmt->selType == MIDITRANSFORM_NOTE) && (cmt->procVal1 == MusECore::Fix || cmt->procVal1 == MusECore::ScaleMap || cmt->procVal1 == MusECore::Dynamic || cmt->procVal1 == MusECore::Random || cmt->procVal1 == MusECore::Flip)) { procVal1b->setSuffix(" - " + MusECore::pitch2string(val)); } else { if(!procVal1b->suffix().isEmpty()) procVal1b->setSuffix(QString("")); } } //--------------------------------------------------------- // procVal2aChanged //--------------------------------------------------------- void MidiInputTransformDialog::procVal2aChanged(int val) { cmt->procVal2a = val; } //--------------------------------------------------------- // procVal2bChanged //--------------------------------------------------------- void MidiInputTransformDialog::procVal2bChanged(int val) { cmt->procVal2b = val; } //--------------------------------------------------------- // modul1enableChanged //--------------------------------------------------------- void MidiInputTransformDialog::modul1enableChanged(bool val) { MusECore::modules[0].valid = val; } //--------------------------------------------------------- // modul2enableChanged //--------------------------------------------------------- void MidiInputTransformDialog::modul2enableChanged(bool val) { MusECore::modules[1].valid = val; } //--------------------------------------------------------- // modul3enableChanged //--------------------------------------------------------- void MidiInputTransformDialog::modul3enableChanged(bool val) { MusECore::modules[2].valid = val; } //--------------------------------------------------------- // modul4enableChanged //--------------------------------------------------------- void MidiInputTransformDialog::modul4enableChanged(bool val) { MusECore::modules[3].valid = val; } //--------------------------------------------------------- // selPortOpSel //--------------------------------------------------------- void MidiInputTransformDialog::selPortOpSel(int val) { setValOp(selPortVala, selPortValb, MusECore::ValOp(val)); cmt->selPort = MusECore::ValOp(val); } //--------------------------------------------------------- // selPortValaChanged //--------------------------------------------------------- void MidiInputTransformDialog::selPortValaChanged(int val) { cmt->selPorta = val; } //--------------------------------------------------------- // selPortValbChanged //--------------------------------------------------------- void MidiInputTransformDialog::selPortValbChanged(int val) { cmt->selPortb = val; } //--------------------------------------------------------- // selChannelOpSel //--------------------------------------------------------- void MidiInputTransformDialog::selChannelOpSel(int val) { setValOp(selChannelVala, selChannelValb, MusECore::ValOp(val)); cmt->selChannel = MusECore::ValOp(val); } //--------------------------------------------------------- // selChannelValaChanged //--------------------------------------------------------- void MidiInputTransformDialog::selChannelValaChanged(int val) { cmt->selChannela = val; } //--------------------------------------------------------- // selChannelValbChanged //--------------------------------------------------------- void MidiInputTransformDialog::selChannelValbChanged(int val) { cmt->selChannelb = val; } //--------------------------------------------------------- // procPortOpSel //--------------------------------------------------------- void MidiInputTransformDialog::procPortOpSel(int val) { cmt->procPort = MusECore::TransformOperator(val); switch(MusECore::TransformOperator(val)) { case MusECore::Keep: case MusECore::Invert: procPortVala->setEnabled(false); procPortValb->setEnabled(false); break; case MusECore::Multiply: case MusECore::Divide: procPortVala->setEnabled(true); procPortVala->setDecimals(2); procPortValb->setEnabled(false); break; case MusECore::Plus: case MusECore::Minus: case MusECore::Fix: case MusECore::Value: case MusECore::Flip: procPortVala->setDecimals(0); procPortVala->setEnabled(true); procPortValb->setEnabled(false); break; case MusECore::Random: case MusECore::ScaleMap: case MusECore::Dynamic: case MusECore::Toggle: procPortVala->setDecimals(0); procPortVala->setEnabled(true); procPortValb->setEnabled(true); break; } } //--------------------------------------------------------- // procPortValaChanged //--------------------------------------------------------- void MidiInputTransformDialog::procPortValaChanged(int val) { cmt->procPorta = val; } //--------------------------------------------------------- // procPortValbChanged //--------------------------------------------------------- void MidiInputTransformDialog::procPortValbChanged(int val) { cmt->procPortb = val; } //--------------------------------------------------------- // procChannelOpSel //--------------------------------------------------------- void MidiInputTransformDialog::procChannelOpSel(int val) { cmt->procChannel = MusECore::TransformOperator(val); switch(MusECore::TransformOperator(val)) { case MusECore::Keep: case MusECore::Invert: procChannelVala->setEnabled(false); procChannelValb->setEnabled(false); break; case MusECore::Multiply: case MusECore::Divide: procChannelVala->setEnabled(true); procChannelVala->setDecimals(2); procChannelValb->setEnabled(false); break; case MusECore::Plus: case MusECore::Minus: case MusECore::Fix: case MusECore::Value: case MusECore::Flip: procChannelVala->setDecimals(0); procChannelVala->setEnabled(true); procChannelValb->setEnabled(false); break; case MusECore::Random: case MusECore::ScaleMap: case MusECore::Dynamic: case MusECore::Toggle: procChannelVala->setDecimals(0); procChannelVala->setEnabled(true); procChannelValb->setEnabled(true); break; } } //--------------------------------------------------------- // procChannelValaChanged //--------------------------------------------------------- void MidiInputTransformDialog::procChannelValaChanged(int val) { cmt->procChannela = val; } //--------------------------------------------------------- // procChannelValbChanged //--------------------------------------------------------- void MidiInputTransformDialog::procChannelValbChanged(int val) { cmt->procChannelb = val; } //--------------------------------------------------------- // changeModul //--------------------------------------------------------- void MidiInputTransformDialog::changeModul(int k) { //printf("change modul %d\n", k); cmodul = k; // current modul if (MusECore::modules[k].transform == 0) { //printf("transform %d ist null\n", k); MusECore::modules[k].transform = cmt; } else { //--------------------------------------------- // search transformation in list //--------------------------------------------- int idx = 0; MusECore::iMidiInputTransformation i; for (i = MusECore::mtlist.begin(); i != MusECore::mtlist.end(); ++i, ++idx) { if (*i == MusECore::modules[k].transform) { presetList->setCurrentItem(presetList->item(idx)); break; } } if (i == MusECore::mtlist.end()) printf("change to unknown transformation!\n"); } } //--------------------------------------------------------- // presetChanged //--------------------------------------------------------- void MidiInputTransformDialog::presetChanged(QListWidgetItem* item, QListWidgetItem*) { if(!item) { cindex = -1; return; } cindex = presetList->row(item); //--------------------------------------------------- // search transformation in list and set // cmt //--------------------------------------------------- MusECore::iMidiInputTransformation i; for (i = MusECore::mtlist.begin(); i != MusECore::mtlist.end(); ++i) { if (item->text() == (*i)->name) { if(MusEGlobal::debugMsg) printf("found %s\n", (*i)->name.toLatin1().constData()); cmt = *i; if (cmodul != -1) { MusECore::modules[cmodul].transform = *i; } break; } } if (i == MusECore::mtlist.end()) { printf("MidiInputTransformDialog::presetChanged: not found\n"); return; } nameEntry->setText(cmt->name); commentEntry->setText(cmt->comment); selEventOp->setCurrentIndex(cmt->selEventOp); selEventOpSel(cmt->selEventOp); for (unsigned i = 0; i < sizeof(MusECore::selTypeTable)/sizeof(*MusECore::selTypeTable); ++i) { if (MusECore::selTypeTable[i] == cmt->selType) { selType->setCurrentIndex(i); break; } } selVal1Op->setCurrentIndex(cmt->selVal1); selVal1OpSel(cmt->selVal1); selVal2Op->setCurrentIndex(cmt->selVal2); selVal2OpSel(cmt->selVal2); selPortOp->setCurrentIndex(cmt->selPort); selPortOpSel(cmt->selPort); selChannelOp->setCurrentIndex(cmt->selChannel); selChannelOpSel(cmt->selChannel); { unsigned i; for (i = 0; i < sizeof(MusECore::oplist)/sizeof(*MusECore::oplist); ++i) { if (MusECore::oplist[i].id == cmt->funcOp) { funcOp->setCurrentIndex(i); break; } } if (i == sizeof(MusECore::oplist)/sizeof(*MusECore::oplist)) printf("internal error: bad OpCode\n"); funcOpSel(i); } for (unsigned i = 0; i < sizeof(MusECore::procTypeTable)/sizeof(*MusECore::procTypeTable); ++i) { if (MusECore::procTypeTable[i] == cmt->eventType) { procType->setCurrentIndex(i); break; } } procEventOp->setCurrentIndex(cmt->procEvent); procEventOpSel(cmt->procEvent); procVal1Op->setCurrentIndex(cmt->procVal1); procVal1OpSel(cmt->procVal1); for (unsigned i = 0; i < sizeof(MusECore::procVal2Map)/sizeof(*MusECore::procVal2Map); ++i) { if (MusECore::procVal2Map[i] == cmt->procVal2) { procVal2Op->setCurrentIndex(i); break; } } selVal1a->setValue(cmt->selVal1a); selVal1b->setValue(cmt->selVal1b); selVal1aChanged(cmt->selVal1a); selVal1bChanged(cmt->selVal1b); selVal2a->setValue(cmt->selVal2a); selVal2b->setValue(cmt->selVal2b); selPortVala->setValue(cmt->selPorta); selPortValb->setValue(cmt->selPortb); selChannelVala->setValue(cmt->selChannela); selChannelValb->setValue(cmt->selChannelb); procVal1a->setValue(cmt->procVal1a); procVal1b->setValue(cmt->procVal1b); procVal2a->setValue(cmt->procVal2a); procVal2b->setValue(cmt->procVal2b); procPortVala->setValue(cmt->procPorta); procPortValb->setValue(cmt->procPortb); procChannelVala->setValue(cmt->procChannela); procChannelValb->setValue(cmt->procChannelb); procPortOp->setCurrentIndex(cmt->procPort); procPortOpSel(cmt->procPort); procChannelOp->setCurrentIndex(cmt->procChannel); procChannelOpSel(cmt->procChannel); } } // namespace MusEGui muse-4.0.0/src/muse/mplugins/midiitransform.h000066400000000000000000000076761404105311000212670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: midiitransform.h,v 1.1.1.1.2.1 2009/02/02 21:38:01 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDIITRANSFORM_H__ #define __MIDIITRANSFORM_H__ #include "ui_itransformbase.h" #include #include "type_defs.h" #include "miditransform.h" // Forward declarations: class QCloseEvent; class QButtonGroup; namespace MusECore { class MidiInputTransformation; class Xml; class MidiRecordEvent; enum InputTransformProcEventOp { KeepType, FixType }; extern void writeMidiInputTransforms(int level, Xml& xml); extern void readMidiInputTransform(Xml&); extern bool applyMidiInputTransformation(MidiRecordEvent& event); extern void clearMidiInputTransforms(); } namespace MusEGui { //--------------------------------------------------------- // MidiInputTransform //--------------------------------------------------------- class MidiInputTransformDialog : public QDialog, public Ui::MidiInputTransformDialogBase { Q_OBJECT MusECore::MidiInputTransformation* cmt; int cindex; // current index in preset list int cmodul; // current index in modules list virtual void accept(); virtual void reject(); void setValOp(QWidget* a, QWidget* b, MusECore::ValOp op); virtual void closeEvent(QCloseEvent*); MusECore::MidiInputTransformation* createDefaultPreset(); void updatePresetList(); QButtonGroup* modulGroup; signals: void hideWindow(); private slots: void presetNew(); void presetDelete(); void changeModul(int k); void selEventOpSel(int); void selTypeSel(int); void selVal1OpSel(int); void selVal2OpSel(int); void procEventOpSel(int); void procEventTypeSel(int); void procVal1OpSel(int); void procVal2OpSel(int); void procVal2OpUpdate(MusECore::TransformOperator op); void funcOpSel(int); void presetChanged(QListWidgetItem*, QListWidgetItem*); void nameChanged(const QString&); void commentChanged(); void selVal1aChanged(int); void selVal1bChanged(int); void selVal2aChanged(int); void selVal2bChanged(int); void procVal1aChanged(int); void procVal1bChanged(int); void procVal2aChanged(int); void procVal2bChanged(int); void modul1enableChanged(bool); void modul2enableChanged(bool); void modul3enableChanged(bool); void modul4enableChanged(bool); void selPortOpSel(int); void selPortValaChanged(int); void selPortValbChanged(int); void selChannelOpSel(int); void selChannelValaChanged(int); void selChannelValbChanged(int); void procPortOpSel(int); void procPortValaChanged(int); void procPortValbChanged(int); void procChannelOpSel(int); void procChannelValaChanged(int); void procChannelValbChanged(int); public slots: void songChanged(MusECore::SongChangedStruct_t); public: MidiInputTransformDialog(QDialog* parent = 0, Qt::WindowFlags fl = Qt::Widget); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mplugins/miditransform.cpp000066400000000000000000002013101404105311000214260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: miditransform.cpp,v 1.8.2.3 2009/05/24 21:43:44 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "helper.h" #include "spinboxFP.h" #include "event.h" #include "miditransform.h" #include "track.h" #include "song.h" #include "xml.h" #include "globals.h" #include "comboQuant.h" #include "audio.h" #include "gconfig.h" #include "midictrl.h" #include "undo.h" using MusECore::Undo; using MusECore::UndoOp; namespace MusECore { // // Order of events: // Note, Poly Pressure, Control, AfterTouch, Pitch Bend, NRPN, RPN, Program // #define MIDITRANSFORM_NOTE 0 #define MIDITRANSFORM_POLY 1 #define MIDITRANSFORM_CTRL 2 #define MIDITRANSFORM_ATOUCH 3 #define MIDITRANSFORM_PITCHBEND 4 #define MIDITRANSFORM_NRPN 5 #define MIDITRANSFORM_RPN 6 #define MIDITRANSFORM_PROGRAM 7 static int eventTypeTable[] = { MIDITRANSFORM_NOTE, MIDITRANSFORM_POLY, MIDITRANSFORM_CTRL, MIDITRANSFORM_ATOUCH, MIDITRANSFORM_PITCHBEND, MIDITRANSFORM_NRPN, MIDITRANSFORM_RPN, MIDITRANSFORM_PROGRAM }; static int procVal2Map[] = { 0, 1, 2, 3, 4, 5, 6, 7, 10, 11 }; //--------------------------------------------------------- // MidiTransformation //--------------------------------------------------------- class MidiTransformation { public: QString name; QString comment; ValOp selEventOp; MusECore::EventType selType; ValOp selVal1; int selVal1a, selVal1b; ValOp selVal2; int selVal2a, selVal2b; ValOp selLen; int selLenA, selLenB; ValOp selRange; int selRangeA, selRangeB; TransformOperator procEvent; MusECore::EventType eventType; TransformOperator procVal1; int procVal1a, procVal1b; TransformOperator procVal2; int procVal2a, procVal2b; TransformOperator procLen; int procLenA; TransformOperator procPos; int procPosA; TransformFunction funcOp; int quantVal; bool selectedTracks; bool insideLoop; MidiTransformation(const QString& s) { name = s; selEventOp = All; selType = Note; selVal1 = Ignore; selVal1a = 0; selVal1b = 0; selVal2 = Ignore; selVal2a = 0; selVal2b = 0; selLen = Ignore; selLenA = 0; selLenB = 0; selRange = Ignore; selRangeA = 0; selRangeB = 0; procEvent = Keep; eventType = Note; procVal1 = Keep; procVal1a = 0; procVal1b = 0; procVal2 = Keep; procVal2a = 0; procVal2b = 0; procLen = Keep; procLenA = 0; procPos = Keep; procPosA = 0; funcOp = Select; quantVal = MusEGlobal::config.division; selectedTracks = false; insideLoop = false; } void write(int level, Xml& xml); }; class MidiTransformPrivate { public: MidiTransformation* cmt; int cindex; // current index in preset list }; typedef std::list MidiTransformationList; typedef std::list::iterator iMidiTransformation; typedef std::list::const_iterator ciMidiTransformation; static MusECore::MidiTransformationList mtlist; //--------------------------------------------------------- // writeMidiTransforms //--------------------------------------------------------- void writeMidiTransforms(int level, Xml& xml) { for (iMidiTransformation i = mtlist.begin(); i != mtlist.end(); ++i) { (*i)->write(level, xml); } } //--------------------------------------------------------- // write //--------------------------------------------------------- void MidiTransformation::write(int level, Xml& xml) { xml.tag(level++, "midiTransform"); xml.strTag(level, "name", name); xml.strTag(level, "comment", comment); xml.intTag(level, "function", int(funcOp)); xml.intTag(level, "selectedTracks", selectedTracks); xml.intTag(level, "insideLoop", insideLoop); if (funcOp == Quantize) { xml.intTag(level, "quantVal", quantVal); } if (funcOp == Transform || funcOp == Insert) { if (procEvent != Keep) { xml.intTag(level, "procEventOp", int(procEvent)); xml.intTag(level, "eventType", int(eventType)); } if (procVal1 != Keep) { xml.intTag(level, "procVal1Op", int(procVal1)); xml.intTag(level, "procVal1a", procVal1a); xml.intTag(level, "procVal1b", procVal1b); } if (procVal2 != Keep) { xml.intTag(level, "procVal2Op", int(procVal2)); xml.intTag(level, "procVal2a", procVal2a); xml.intTag(level, "procVal2b", procVal2b); } if (procLen != Keep) { xml.intTag(level, "procLenOp", int(procLen)); xml.intTag(level, "procLen", procLenA); } if (procPos != Keep) { xml.intTag(level, "procPosOp", int(procPos)); xml.intTag(level, "procPos", procPosA); } } if (selEventOp != Ignore) { xml.intTag(level, "selEventOp", int(selEventOp)); xml.intTag(level, "selEventType", int(selType)); } if (selVal1 != Ignore) { xml.intTag(level, "selVal1Op", int(selVal1)); xml.intTag(level, "selVal1a", selVal1a); xml.intTag(level, "selVal1b", selVal1b); } if (selVal2 != Ignore) { xml.intTag(level, "selVal2Op", int(selVal2)); xml.intTag(level, "selVal2a", selVal2a); xml.intTag(level, "selVal2b", selVal2b); } if (selLen != Ignore) { xml.intTag(level, "selLenOp", int(selLen)); xml.intTag(level, "selLenA", selLenA); xml.intTag(level, "selLenB", selLenB); } if (selRange != Ignore) { xml.intTag(level, "selRangeOp", int(selRange)); xml.intTag(level, "selRangeA", selRangeA); xml.intTag(level, "selRangeB", selRangeB); } xml.etag(level, "midiTransform"); } //--------------------------------------------------------- // readMidiTransform //--------------------------------------------------------- void readMidiTransform(Xml& xml) { MidiTransformation trans(QWidget::tr("New")); for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "name") trans.name = xml.parse1(); else if (tag == "comment") trans.comment = xml.parse1(); else if (tag == "function") trans.funcOp = TransformFunction(xml.parseInt()); else if (tag == "selectedTracks") trans.selectedTracks = xml.parseInt(); else if (tag == "insideLoop") trans.insideLoop = xml.parseInt(); else if (tag == "quantVal") trans.quantVal = xml.parseInt(); else if (tag == "procEventOp") trans.procEvent = TransformOperator(xml.parseInt()); else if (tag == "eventType") trans.eventType = EventType(xml.parseInt()); else if (tag == "procVal1Op") trans.procVal1 = TransformOperator(xml.parseInt()); else if (tag == "procVal1a") trans.procVal1a = xml.parseInt(); else if (tag == "procVal1b") trans.procVal1b = xml.parseInt(); else if (tag == "procVal2Op") trans.procVal2 = TransformOperator(xml.parseInt()); else if (tag == "procVal2a") trans.procVal2a = xml.parseInt(); else if (tag == "procVal2b") trans.procVal2b = xml.parseInt(); else if (tag == "procLenOp") trans.procLen = TransformOperator(xml.parseInt()); else if (tag == "procLen") trans.procLenA = xml.parseInt(); else if (tag == "procPosOp") trans.procPos = TransformOperator(xml.parseInt()); else if (tag == "procPos") trans.procPosA = xml.parseInt(); else if (tag == "selEventOp") trans.selEventOp = ValOp(xml.parseInt()); else if (tag == "selEventType") trans.selType = EventType(xml.parseInt()); else if (tag == "selVal1Op") trans.selVal1 = ValOp(xml.parseInt()); else if (tag == "selVal1a") trans.selVal1a = xml.parseInt(); else if (tag == "selVal1b") trans.selVal1b = xml.parseInt(); else if (tag == "selVal2Op") trans.selVal2 = ValOp(xml.parseInt()); else if (tag == "selVal2a") trans.selVal2a = xml.parseInt(); else if (tag == "selVal2b") trans.selVal2b = xml.parseInt(); else if (tag == "selLenOp") trans.selLen = ValOp(xml.parseInt()); else if (tag == "selLenA") trans.selLenA = xml.parseInt(); else if (tag == "selLenB") trans.selLenB = xml.parseInt(); else if (tag == "selRangeOp") trans.selRange = ValOp(xml.parseInt()); else if (tag == "selRangeA") trans.selRangeA = xml.parseInt(); else if (tag == "selRangeB") trans.selRangeB = xml.parseInt(); else xml.unknown("midiTransform"); break; case Xml::TagEnd: if (xml.s1() == "midiTransform") { // By T356. A previous bug caused some .med files to grow very large // with duplicate transforms. Here we can eliminate those duplicates. for(iMidiTransformation i = mtlist.begin(); i != mtlist.end(); ++i) { if((*i)->name == trans.name) return; } MidiTransformation* t = new MidiTransformation(trans); mtlist.push_back(t); return; } default: break; } } } //--------------------------------------------------------- // clearMidiTransforms //--------------------------------------------------------- void clearMidiTransforms() { for (iMidiTransformation i = mtlist.begin(); i != mtlist.end(); ++i) { MidiTransformation* t = *i; if(t) delete t; } mtlist.clear(); } } // namespace MusECore namespace MusEGui { //--------------------------------------------------------- // MidiTransformDialog // Widgets: // presetList nameEntry commentEntry // selEventOp selType // selVal1Op selVal1a selVal1b // selVal2Op selVal2a selVal2b // selLenOp selLenA selLenB // selRangeOp selBarA selBarB // // procEventOp procType // procVal1Op procVal1a procVal1b // procVal2Op procVal2a procVal2b // procLenOp procLenA // procPosOp procPosA // funcOp funcQuantVal // processAll selectedTracks insideLoop // buttonNew buttonDelete buttonApply buttonOk //--------------------------------------------------------- MidiTransformerDialog::MidiTransformerDialog(QDialog* parent, Qt::WindowFlags fl) : QDialog(parent, fl) { setupUi(this); data = new MusECore::MidiTransformPrivate; data->cmt = 0; data->cindex = -1; connect(buttonApply, SIGNAL(clicked()), SLOT(apply())); connect(buttonNew, SIGNAL(clicked()), SLOT(presetNew())); connect(buttonDelete, SIGNAL(clicked()), SLOT(presetDelete())); connect(selEventOp, SIGNAL(activated(int)), SLOT(selEventOpSel(int))); connect(selType, SIGNAL(activated(int)), SLOT(selTypeSel(int))); connect(selVal1Op, SIGNAL(activated(int)), SLOT(selVal1OpSel(int))); connect(selVal2Op, SIGNAL(activated(int)), SLOT(selVal2OpSel(int))); connect(selLenOp, SIGNAL(activated(int)), SLOT(selLenOpSel(int))); connect(selRangeOp, SIGNAL(activated(int)), SLOT(selRangeOpSel(int))); connect(procEventOp, SIGNAL(activated(int)), SLOT(procEventOpSel(int))); connect(procType, SIGNAL(activated(int)), SLOT(procEventTypeSel(int))); connect(procVal1Op, SIGNAL(activated(int)), SLOT(procVal1OpSel(int))); connect(procVal2Op, SIGNAL(activated(int)), SLOT(procVal2OpSel(int))); connect(procLenOp, SIGNAL(activated(int)), SLOT(procLenOpSel(int))); connect(procPosOp, SIGNAL(activated(int)), SLOT(procPosOpSel(int))); connect(funcOp, SIGNAL(activated(int)), SLOT(funcOpSel(int))); connect(funcQuantVal, SIGNAL(valueChanged(int)), SLOT(funcQuantValSel(int))); connect(presetList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), SLOT(presetChanged(QListWidgetItem*, QListWidgetItem*))); connect(nameEntry, SIGNAL(textChanged(const QString&)), SLOT(nameChanged(const QString&))); connect(commentEntry, SIGNAL(textChanged()), SLOT(commentChanged())); connect(selVal1a, SIGNAL(valueChanged(int)), SLOT(selVal1aChanged(int))); connect(selVal1b, SIGNAL(valueChanged(int)), SLOT(selVal1bChanged(int))); connect(selVal2a, SIGNAL(valueChanged(int)), SLOT(selVal2aChanged(int))); connect(selVal2b, SIGNAL(valueChanged(int)), SLOT(selVal2bChanged(int))); connect(selLenA, SIGNAL(valueChanged(int)), SLOT(selLenAChanged(int))); connect(selLenB, SIGNAL(valueChanged(int)), SLOT(selLenBChanged(int))); connect(selBarA, SIGNAL(valueChanged(int)), SLOT(selBarAChanged(int))); connect(selBarB, SIGNAL(valueChanged(int)), SLOT(selBarBChanged(int))); connect(procVal1a, SIGNAL(valueChanged(int)), SLOT(procVal1aChanged(int))); connect(procVal1b, SIGNAL(valueChanged(int)), SLOT(procVal1bChanged(int))); connect(procVal2a, SIGNAL(valueChanged(int)), SLOT(procVal2aChanged(int))); connect(procVal2b, SIGNAL(valueChanged(int)), SLOT(procVal2bChanged(int))); connect(procLenA, SIGNAL(valueChanged(int)), SLOT(procLenAChanged(int))); connect(procPosA, SIGNAL(valueChanged(int)), SLOT(procPosAChanged(int))); connect(processAll, SIGNAL(toggled(bool)), SLOT(processAllChanged(bool))); connect(selectedTracks, SIGNAL(toggled(bool)), SLOT(selectedTracksChanged(bool))); connect(insideLoop, SIGNAL(toggled(bool)), SLOT(insideLoopChanged(bool))); //--------------------------------------------------- // populate preset list //--------------------------------------------------- updatePresetList(); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); } //--------------------------------------------------------- // ~MidiTransformDialog //--------------------------------------------------------- MidiTransformerDialog::~MidiTransformerDialog() { delete data; } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void MidiTransformerDialog::songChanged(MusECore::SongChangedStruct_t flags) { // Whenever a song is loaded, flags is -1. Since transforms are part of configuration, // use SC_CONFIG here, to filter unwanted song change events. if(flags & SC_CONFIG) updatePresetList(); } //--------------------------------------------------------- // createDefaultPreset //--------------------------------------------------------- MusECore::MidiTransformation* MidiTransformerDialog::createDefaultPreset() { // create default "New" preset MusECore::MidiTransformation* pre = new MusECore::MidiTransformation(tr("New")); MusECore::mtlist.push_back(pre); presetList->blockSignals(true); presetList->addItem(tr("New")); presetList->setCurrentRow(0); presetList->blockSignals(false); return pre; } //--------------------------------------------------------- // updatePresetList //--------------------------------------------------------- void MidiTransformerDialog::updatePresetList() { data->cmt = 0; data->cindex = -1; presetList->clear(); for (MusECore::iMidiTransformation i = MusECore::mtlist.begin(); i != MusECore::mtlist.end(); ++i) { presetList->addItem((*i)->name); if (data->cmt == 0) data->cmt = *i; } if (data->cmt == 0) { // create default "New" preset data->cmt = createDefaultPreset(); } // Force it! Don't rely on a signal. presetChanged(presetList->item(0), 0); } //--------------------------------------------------------- // accept //--------------------------------------------------------- void MidiTransformerDialog::accept() { apply(); reject(); } //--------------------------------------------------------- // transformEvent // subfunction of processEvent() //--------------------------------------------------------- void MidiTransformerDialog::transformEvent(MusECore::Event& event, MusECore::MidiPart* part, MusECore::MidiPart* newPart, MusECore::Undo& operations) { MusECore::MidiTransformation* cmt = data->cmt; MusECore::Event newEvent = event.clone(); if (cmt->procEvent != MusECore::Keep) newEvent.setType(cmt->eventType); //--------------------------------------------------- // transform value A //--------------------------------------------------- int val = newEvent.dataA(); switch (cmt->procVal1) { case MusECore::Keep: break; case MusECore::Plus: val += cmt->procVal1a; break; case MusECore::Minus: val -= cmt->procVal1a; break; case MusECore::Multiply: val = int(val * (cmt->procVal1a/100.0) + .5); break; case MusECore::Divide: val = int(val / (cmt->procVal1a/100.0) + .5); break; case MusECore::Fix: val = cmt->procVal1a; break; case MusECore::Value: val = cmt->procVal2a; break; case MusECore::Invert: val = 128 - val; break; case MusECore::ScaleMap: printf("scale map not implemented\n"); break; case MusECore::Flip: val = cmt->procVal1a - val; break; case MusECore::Dynamic: // "crescendo" val = (((cmt->procVal2b-cmt->procVal2a) * (newEvent.tick() - MusEGlobal::song->lpos())) / (MusEGlobal::song->rpos() - MusEGlobal::song->lpos())) + cmt->procVal2a; break; case MusECore::Random: { int range = cmt->procVal1b - cmt->procVal1a; if (range > 0) val = (rand() % range) + cmt->procVal1a; else if (range < 0) val = (rand() % -range) + cmt->procVal1b; else val = cmt->procVal1a; } break; default: break; } if (val < 0) val = 0; if (val > 127) val = 127; newEvent.setA(val); //--------------------------------------------------- // transform value B //--------------------------------------------------- val = newEvent.dataB(); switch (cmt->procVal2) { case MusECore::Plus: val += cmt->procVal2a; break; case MusECore::Minus: val -= cmt->procVal2a; break; case MusECore::Multiply: val = int(val * (cmt->procVal2a/100.0) + .5); break; case MusECore::Divide: val = int(val / (cmt->procVal2a/100.0) + .5); break; case MusECore::Fix: val = cmt->procVal2a; break; case MusECore::Value: val = cmt->procVal1a; break; case MusECore::Invert: val = 128 - val; break; case MusECore::Dynamic: val = (((cmt->procVal2b-cmt->procVal2a) * (newEvent.tick() - MusEGlobal::song->lpos())) / (MusEGlobal::song->rpos() - MusEGlobal::song->lpos())) + cmt->procVal2a; break; case MusECore::Random: { int range = cmt->procVal2b - cmt->procVal2a; if (range > 0) val = (rand() % range) + cmt->procVal2a; else if (range < 0) val = (rand() % -range) + cmt->procVal2b; else val = cmt->procVal1a; } break; case MusECore::ScaleMap: case MusECore::Keep: case MusECore::Flip: case MusECore::Toggle: break; } if (val < 0) val = 0; if (val > 127) val = 127; newEvent.setB(val); //--------------------------------------------------- // transform len //--------------------------------------------------- int len = newEvent.lenTick(); switch (cmt->procLen) { case MusECore::Plus: len += cmt->procLenA; break; case MusECore::Minus: len -= cmt->procLenA; break; case MusECore::Multiply: len = int(len * (cmt->procLenA/100.0) + .5); break; case MusECore::Divide: len = int(len / (cmt->procLenA/100.0) + .5); break; case MusECore::Fix: len = cmt->procLenA; break; case MusECore::Invert: case MusECore::ScaleMap: case MusECore::Dynamic: case MusECore::Random: case MusECore::Keep: case MusECore::Flip: case MusECore::Value: case MusECore::Toggle: break; } if (len < 0) len = 0; newEvent.setLenTick(len); //--------------------------------------------------- // transform pos //--------------------------------------------------- int pos = newEvent.tick(); switch (cmt->procPos) { case MusECore::Plus: pos += cmt->procPosA; break; case MusECore::Minus: pos -= cmt->procPosA; break; case MusECore::Multiply: pos = int(pos * (cmt->procPosA/100.0) + .5); break; case MusECore::Divide: pos = int(pos / (cmt->procPosA/100.0) + .5); break; case MusECore::Fix: case MusECore::Invert: case MusECore::ScaleMap: case MusECore::Dynamic: case MusECore::Random: { int range = cmt->procPosA; int val = rand() % (2 * range) - range; pos = pos + val; } break; case MusECore::Keep: case MusECore::Flip: case MusECore::Value: case MusECore::Toggle: break; } if (pos < 0) pos = 0; newEvent.setTick(pos); switch(data->cmt->funcOp) { case MusECore::Transform: operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, true, true)); break; case MusECore::Insert: // Indicate do port controller values and clone parts. operations.push_back(UndoOp(UndoOp::AddEvent, newEvent, part, true, true)); break; case MusECore::Extract: operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, true, true)); // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case MusECore::Copy: newPart->addEvent(newEvent); break; default: break; } } //--------------------------------------------------------- // processEvent //--------------------------------------------------------- void MidiTransformerDialog::processEvent(MusECore::Event& event, MusECore::MidiPart* part, MusECore::MidiPart* newPart, MusECore::Undo& operations) { switch(data->cmt->funcOp) { case MusECore::Select: break; case MusECore::Quantize: { int tick = event.tick(); int rt = MusEGlobal::sigmap.raster(tick, data->cmt->quantVal) - tick; if (tick != rt) { MusECore::Event newEvent = event.clone(); newEvent.setTick(rt); operations.push_back(UndoOp(UndoOp::ModifyEvent,newEvent,event,part,true,true)); } } break; case MusECore::Delete: { operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, true, true)); } break; case MusECore::Transform: case MusECore::Insert: case MusECore::Copy: case MusECore::Extract: transformEvent(event, part, newPart, operations); break; } } //--------------------------------------------------------- // isSelected // apply Select filter // return true if event is selected //--------------------------------------------------------- bool MidiTransformerDialog::isSelected(const MusECore::Event& event) { MusECore::MidiTransformation* cmt = data->cmt; switch (cmt->selEventOp) { case MusECore::Equal: if (!typesMatch(event, cmt->selType)) { return false; } break; case MusECore::Unequal: if (typesMatch(event, cmt->selType)) return false; break; default: break; } switch (cmt->selVal1) { case MusECore::Ignore: break; case MusECore::Equal: if (event.dataA() != cmt->selVal1a) return false; break; case MusECore::Unequal: if (event.dataA() == cmt->selVal1a) return false; break; case MusECore::Higher: if (event.dataA() <= cmt->selVal1a) return false; break; case MusECore::Lower: if (event.dataA() >= cmt->selVal1a) return false; break; case MusECore::Inside: if ((event.dataA() < cmt->selVal1a) || (event.dataA() >= cmt->selVal1b)) return false; break; case MusECore::Outside: if ((event.dataA() >= cmt->selVal1a) && (event.dataA() < cmt->selVal1b)) return false; break; } switch (cmt->selVal2) { case MusECore::Ignore: break; case MusECore::Equal: if (event.dataB() != cmt->selVal2a) return false; break; case MusECore::Unequal: if (event.dataB() == cmt->selVal2a) return false; break; case MusECore::Higher: if (event.dataB() <= cmt->selVal2a) return false; break; case MusECore::Lower: if (event.dataB() >= cmt->selVal2a) return false; break; case MusECore::Inside: if ((event.dataB() < cmt->selVal2a) || (event.dataB() >= cmt->selVal2b)) return false; break; case MusECore::Outside: if ((event.dataB() >= cmt->selVal2a) && (event.dataB() < cmt->selVal2b)) return false; break; } int len = event.lenTick(); switch (cmt->selLen) { case MusECore::Ignore: break; case MusECore::Equal: if (len != cmt->selLenA) return false; break; case MusECore::Unequal: if (len == cmt->selLenA) return false; break; case MusECore::Higher: if (len <= cmt->selLenA) return false; break; case MusECore::Lower: if (len >= cmt->selLenA) return false; break; case MusECore::Inside: if ((len < cmt->selLenA) || (len >= cmt->selLenB)) return false; break; case MusECore::Outside: if ((len >= cmt->selLenA) && (len < cmt->selLenB)) return false; break; } int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(event.tick(), &bar, &beat, &tick); int beat1 = cmt->selRangeA / 1000; unsigned tick1 = cmt->selRangeA % 1000; int beat2 = cmt->selRangeB / 1000; unsigned tick2 = cmt->selRangeB % 1000; switch (cmt->selRange) { case MusECore::Ignore: break; case MusECore::Equal: if (beat != beat1 || tick != tick1) return false; break; case MusECore::Unequal: if (beat == beat1 && tick == tick1) return false; break; case MusECore::Higher: if (beat <= beat1) return false; if (beat == beat1 && tick <= tick1) return false; break; case MusECore::Lower: if (beat >= beat1) return false; if (beat == beat1 && tick >= tick1) return false; break; case MusECore::Inside: if ((beat < beat1) || (beat >= beat2)) return false; if (beat == beat1 && tick < tick1) return false; if (beat == beat2 && tick >= tick2) return false; break; case MusECore::Outside: if ((beat >= beat1) || (beat < beat2)) return false; if (beat == beat1 && tick >= tick1) return false; if (beat == beat2 && tick < tick2) return false; break; } return true; } //--------------------------------------------------------- // apply //--------------------------------------------------------- void MidiTransformerDialog::apply() { MusECore::SongChangedStruct_t flags = 0; Undo operations; bool copyExtract = (data->cmt->funcOp == MusECore::Copy) || (data->cmt->funcOp == MusECore::Extract); QSet< int > doneList; typedef std::set< int >::iterator iDoneList; iDoneList idl; MusECore::MidiTrackList* tracks = MusEGlobal::song->midis(); MusECore::MidiTrackList tl; for (MusECore::iMidiTrack t = tracks->begin(); t != tracks->end(); ++t) { if (data->cmt->selectedTracks && !(*t)->selected()) continue; MusECore::MidiTrack* newTrack = 0; MusECore::PartList *pl = (*t)->parts(); if (copyExtract) { // check whether we must generate a new track for (MusECore::iPart p = pl->begin(); p != pl->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart *) p->second; const MusECore::EventList& el = part->events(); // Check if the event list has already been done. Skip repeated clones. if (doneList.contains(part->clonemaster_sn())) continue; doneList.insert(part->clonemaster_sn()); for (MusECore::ciEvent i = el.begin(); i != el.end(); ++i) { const MusECore::Event& event = i->second; unsigned tick = event.tick(); if (data->cmt->insideLoop && (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos())) continue; if (isSelected(event)) { newTrack = new MusECore::MidiTrack(); tl.push_back(newTrack); break; } } if (newTrack) break; } } for (MusECore::iPart p = pl->begin(); p != pl->end(); ++p) { MusECore::MidiPart* part = (MusECore::MidiPart *) p->second; MusECore::MidiPart* newPart = 0; const MusECore::EventList& el = part->events(); // Check if the event list has already been done. Skip repeated clones. if (doneList.contains(part->clonemaster_sn())) continue; doneList.insert(part->clonemaster_sn()); if (copyExtract) { // check whether we must generate a new part for (MusECore::ciEvent i = el.begin(); i != el.end(); ++i) { const MusECore::Event& event = i->second; unsigned tick = event.tick(); if (data->cmt->insideLoop && (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos())) continue; if (isSelected(event)) { newPart = new MusECore::MidiPart(newTrack); newPart->setName(part->name()); newPart->setColorIndex(part->colorIndex()); newPart->setTick(part->tick()); newPart->setLenTick(part->lenTick()); operations.push_back(UndoOp(UndoOp::AddPart,newPart)); flags |= SC_PART_INSERTED; break; } } } MusECore::EventList pel; for (MusECore::ciEvent i = el.begin(); i != el.end(); ++i) { const MusECore::Event& event = i->second; unsigned tick = event.tick(); if (data->cmt->insideLoop && (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos())) continue; int flag = isSelected(event); if (data->cmt->funcOp == MusECore::Select) // Here we have a choice of whether to allow undoing of selections. // Disabled for now, it's too tedious in use. Possibly make the choice user settable. operations.push_back(UndoOp(UndoOp::SelectEvent, event, part, flag, event.selected(), false)); else if (flag) pel.add(const_cast(event)); // ough, FIXME, what an ugly hack. } for (MusECore::iEvent i = pel.begin(); i != pel.end(); ++i) { MusECore::Event event = i->second; processEvent(event, part, newPart, operations); } } } if (!tl.empty()) { flags |= SC_TRACK_INSERTED; for (MusECore::iTrack t = tl.begin(); t != tl.end(); ++t) operations.push_back(UndoOp(UndoOp::AddTrack, -1, *t)); } MusEGlobal::song->applyOperationGroup(operations); } //--------------------------------------------------------- // setValOp //--------------------------------------------------------- void MidiTransformerDialog::setValOp(QWidget* a, QWidget* b, MusECore::ValOp op) { switch (op) { case MusECore::Ignore: a->setEnabled(false); b->setEnabled(false); break; case MusECore::Equal: case MusECore::Unequal: case MusECore::Higher: case MusECore::Lower: a->setEnabled(true); b->setEnabled(false); break; case MusECore::Inside: case MusECore::Outside: a->setEnabled(true); b->setEnabled(true); break; } } //--------------------------------------------------------- // selEventOpSel //--------------------------------------------------------- void MidiTransformerDialog::selEventOpSel(int val) { selType->setEnabled(val != MusECore::All); data->cmt->selEventOp = MusECore::ValOp(val); selVal1aChanged(data->cmt->selVal1a); selVal1bChanged(data->cmt->selVal1b); } //--------------------------------------------------------- // selTypeSel //--------------------------------------------------------- void MidiTransformerDialog::selTypeSel(int val) { data->cmt->selType = MusECore::EventType(MusECore::eventTypeTable[val]); selVal1aChanged(data->cmt->selVal1a); selVal1bChanged(data->cmt->selVal1b); } //--------------------------------------------------------- // selVal1OpSel //--------------------------------------------------------- void MidiTransformerDialog::selVal1OpSel(int val) { setValOp(selVal1a, selVal1b, MusECore::ValOp(val)); data->cmt->selVal1 = MusECore::ValOp(val); } //--------------------------------------------------------- // selVal2OpSel //--------------------------------------------------------- void MidiTransformerDialog::selVal2OpSel(int val) { setValOp(selVal2a, selVal2b, MusECore::ValOp(val)); data->cmt->selVal2 = MusECore::ValOp(val); } //--------------------------------------------------------- // selLenOpSel //--------------------------------------------------------- void MidiTransformerDialog::selLenOpSel(int val) { setValOp(selLenA, selLenB, MusECore::ValOp(val)); data->cmt->selLen = MusECore::ValOp(val); } //--------------------------------------------------------- // selRangeOpSel //--------------------------------------------------------- void MidiTransformerDialog::selRangeOpSel(int val) { setValOp(selBarA, selBarB, MusECore::ValOp(val)); data->cmt->selRange = MusECore::ValOp(val); } //--------------------------------------------------------- // procEventOpSel //--------------------------------------------------------- void MidiTransformerDialog::procEventOpSel(int val) { MusECore::TransformOperator op = val == 0 ? MusECore::Keep : MusECore::Fix; procType->setEnabled(op == MusECore::Fix); data->cmt->procEvent = op; procVal1aChanged(data->cmt->procVal1a); procVal1bChanged(data->cmt->procVal1b); } //--------------------------------------------------------- // procEventTypeSel //--------------------------------------------------------- void MidiTransformerDialog::procEventTypeSel(int val) { data->cmt->eventType = MusECore::EventType(MusECore::eventTypeTable[val]); procVal1aChanged(data->cmt->procVal1a); procVal1bChanged(data->cmt->procVal1b); } //--------------------------------------------------------- // procVal1OpSel //--------------------------------------------------------- void MidiTransformerDialog::procVal1OpSel(int val) { data->cmt->procVal1 = MusECore::TransformOperator(val); switch(MusECore::TransformOperator(val)) { case MusECore::Keep: case MusECore::Invert: procVal1a->setEnabled(false); procVal1b->setEnabled(false); break; case MusECore::Multiply: case MusECore::Divide: procVal1a->setEnabled(true); procVal1a->setDecimals(2); procVal1b->setEnabled(false); break; case MusECore::Plus: case MusECore::Minus: case MusECore::Fix: case MusECore::Value: case MusECore::Flip: procVal1a->setDecimals(0); procVal1a->setEnabled(true); procVal1b->setEnabled(false); break; case MusECore::Random: case MusECore::ScaleMap: case MusECore::Dynamic: procVal1a->setDecimals(0); procVal1a->setEnabled(true); procVal1b->setEnabled(true); break; default: break; } procVal1aChanged(data->cmt->procVal1a); procVal1bChanged(data->cmt->procVal1b); } //--------------------------------------------------------- // procVal2OpSel //--------------------------------------------------------- void MidiTransformerDialog::procVal2OpSel(int val) { MusECore::TransformOperator op = MusECore::TransformOperator(MusECore::procVal2Map[val]); data->cmt->procVal2 = op; procVal2OpUpdate(op); } //--------------------------------------------------------- // procVal2OpUpdate //--------------------------------------------------------- void MidiTransformerDialog::procVal2OpUpdate(MusECore::TransformOperator op) { switch (op) { case MusECore::Keep: case MusECore::Invert: procVal2a->setEnabled(false); procVal2b->setEnabled(false); break; case MusECore::Multiply: case MusECore::Divide: procVal2a->setEnabled(true); procVal2a->setDecimals(2); procVal2b->setEnabled(false); break; case MusECore::Plus: case MusECore::Minus: case MusECore::Fix: case MusECore::Value: procVal2a->setDecimals(0); procVal2a->setEnabled(true); procVal2b->setEnabled(false); break; case MusECore::Random: case MusECore::Dynamic: procVal2a->setDecimals(0); procVal2a->setEnabled(true); procVal2b->setEnabled(true); break; default: break; } } //--------------------------------------------------------- // procLenOpSel //--------------------------------------------------------- void MidiTransformerDialog::procLenOpSel(int val) { MusECore::TransformOperator op = MusECore::TransformOperator(val); data->cmt->procLen = op; switch (op) { case MusECore::Keep: case MusECore::Invert: procLenA->setEnabled(false); break; case MusECore::Plus: case MusECore::Minus: case MusECore::Fix: procLenA->setDecimals(0); procLenA->setEnabled(true); break; case MusECore::Multiply: case MusECore::Divide: procLenA->setDecimals(2); procLenA->setEnabled(true); break; default: break; } } //--------------------------------------------------------- // procPosOpSel //--------------------------------------------------------- void MidiTransformerDialog::procPosOpSel(int val) { // Added the randomize functionality for the // MIDI note position. Since we left out the // other operators, random would be 5 (as // we get it from the combo box but in the // enum used it would be 11, so we do this // conditionally MusECore::TransformOperator op; if(val == 5) { op = MusECore::Random; } else { op = MusECore::TransformOperator(val); } data->cmt->procPos = op; switch (op) { case MusECore::Keep: case MusECore::Invert: procPosA->setEnabled(false); break; case MusECore::Multiply: case MusECore::Divide: procPosA->setDecimals(2); procPosA->setEnabled(true); break; case MusECore::Random: case MusECore::Plus: case MusECore::Minus: procPosA->setDecimals(0); procPosA->setEnabled(true); break; default: break; } } //--------------------------------------------------------- // funcOpSel //--------------------------------------------------------- void MidiTransformerDialog::funcOpSel(int val) { funcQuantVal->setEnabled(val == MusECore::Quantize); bool isFuncOp = val == MusECore::Transform || val == MusECore::Insert; procEventOp->setEnabled(isFuncOp); procType->setEnabled(isFuncOp); procVal1Op->setEnabled(isFuncOp); procVal1a->setEnabled(isFuncOp); procVal1b->setEnabled(isFuncOp); procVal2Op->setEnabled(isFuncOp); procVal2a->setEnabled(isFuncOp); procVal2b->setEnabled(isFuncOp); procLenOp->setEnabled(isFuncOp); procLenA->setEnabled(isFuncOp); procPosOp->setEnabled(isFuncOp); procPosA->setEnabled(isFuncOp); if (isFuncOp) { procEventOpSel(data->cmt->procEvent); procVal1OpSel(data->cmt->procVal1); procVal2OpSel(data->cmt->procVal2); procLenOpSel(data->cmt->procLen); procPosOpSel(data->cmt->procPos); } data->cmt->funcOp = MusECore::TransformFunction(val); } //--------------------------------------------------------- // presetNew //--------------------------------------------------------- void MidiTransformerDialog::presetNew() { QString name; for (int i = 0;; ++i) { name = QString("New-") + QString::number(i); MusECore::iMidiTransformation imt; for (imt = MusECore::mtlist.begin(); imt != MusECore::mtlist.end(); ++imt) { if (name == (*imt)->name) break; } if (imt == MusECore::mtlist.end()) break; } MusECore::MidiTransformation* mt = new MusECore::MidiTransformation(name); MusECore::mtlist.push_back(mt); QListWidgetItem* lbi = new QListWidgetItem(name); presetList->blockSignals(true); presetList->addItem(lbi); presetList->setCurrentItem(lbi); presetList->blockSignals(false); // Force it! Don't rely on a signal. presetChanged(lbi, 0); } //--------------------------------------------------------- // presetDelete //--------------------------------------------------------- void MidiTransformerDialog::presetDelete() { if (presetList->count() == 0 || data->cindex < 0) return; MusECore::iMidiTransformation mt = MusECore::mtlist.begin(); for (int i = 0; i < data->cindex && mt != MusECore::mtlist.end(); ++i, ++mt) { } if(mt == MusECore::mtlist.end()) return; MusECore::mtlist.erase(mt); presetList->blockSignals(true); QListWidgetItem* take_item = presetList->takeItem(data->cindex); presetList->blockSignals(false); if(take_item) delete take_item; // Make sure there's a default item. if(presetList->count() == 0) data->cmt = createDefaultPreset(); // Force it! Don't rely on a signal. presetChanged(presetList->currentItem(), 0); } //--------------------------------------------------------- // presetChanged //--------------------------------------------------------- void MidiTransformerDialog::presetChanged(QListWidgetItem* item, QListWidgetItem*) { if(!item) { data->cindex = -1; return; } data->cindex = presetList->row(item); //--------------------------------------------------- // search transformation in list and set // cmt //--------------------------------------------------- MusECore::iMidiTransformation i; for (i = MusECore::mtlist.begin(); i != MusECore::mtlist.end(); ++i) { if (item->text() == (*i)->name) { data->cmt = *i; break; } } if (i == MusECore::mtlist.end()) { printf("MidiTransformerDialog::presetChanged: not found\n"); return; } nameEntry->setText(data->cmt->name); commentEntry->setText(data->cmt->comment); selEventOp->setCurrentIndex(data->cmt->selEventOp); selEventOpSel(data->cmt->selEventOp); for (unsigned i = 0; i < sizeof(MusECore::eventTypeTable)/sizeof(*MusECore::eventTypeTable); ++i) { if (MusECore::eventTypeTable[i] == data->cmt->selType) { selType->setCurrentIndex(i); break; } } selVal1Op->setCurrentIndex(data->cmt->selVal1); selVal1OpSel(data->cmt->selVal1); selVal2Op->setCurrentIndex(data->cmt->selVal2); selVal2OpSel(data->cmt->selVal2); selLenOp->setCurrentIndex(data->cmt->selLen); selLenOpSel(data->cmt->selLen); selRangeOp->setCurrentIndex(data->cmt->selRange); selRangeOpSel(data->cmt->selRange); funcOp->setCurrentIndex(data->cmt->funcOp); funcOpSel(data->cmt->funcOp); // MusECore::TransformOperator procEvent: MusECore::Keep, MusECore::Fix procEventOp->setCurrentIndex(data->cmt->procEvent == MusECore::Fix); procEventOpSel(data->cmt->procEvent); procVal1Op->setCurrentIndex(data->cmt->procVal1); procVal1OpSel(data->cmt->procVal1); for (unsigned i = 0; i < sizeof(MusECore::procVal2Map)/sizeof(*MusECore::procVal2Map); ++i) { if (MusECore::procVal2Map[i] == data->cmt->procVal2) { procVal2Op->setCurrentIndex(i); break; } } procLenOp->setCurrentIndex(data->cmt->procLen); procLenOpSel(data->cmt->procLen); procPosOp->setCurrentIndex(data->cmt->procPos); procPosOpSel(data->cmt->procPos); selVal1aChanged(data->cmt->selVal1a); selVal1bChanged(data->cmt->selVal1b); selVal2a->setValue(data->cmt->selVal2a); selVal2b->setValue(data->cmt->selVal2b); selLenA->setValue(data->cmt->selLenA); selLenB->setValue(data->cmt->selLenB); selBarA->setValue(data->cmt->selRangeA); selBarB->setValue(data->cmt->selRangeB); procVal1a->setValue(data->cmt->procVal1a); procVal1b->setValue(data->cmt->procVal1b); procVal2a->setValue(data->cmt->procVal2a); procVal2b->setValue(data->cmt->procVal2b); procLenA->setValue(data->cmt->procLenA); procPosA->setValue(data->cmt->procPosA); funcQuantVal->setValue(data->cmt->quantVal); selectedTracks->setChecked(data->cmt->selectedTracks); selectedTracksChanged(data->cmt->selectedTracks); insideLoop->setChecked(data->cmt->insideLoop); insideLoopChanged(data->cmt->insideLoop); } //--------------------------------------------------------- // nameChanged //--------------------------------------------------------- void MidiTransformerDialog::nameChanged(const QString& s) { if(data->cindex < 0) return; data->cmt->name = s; QListWidgetItem* item = presetList->item(data->cindex); if (item && s != item->text()) { item->setText(s); } } //--------------------------------------------------------- // commentChanged //--------------------------------------------------------- void MidiTransformerDialog::commentChanged() { data->cmt->comment = commentEntry->toPlainText(); } //-----------------------------op---------------------------- // selVal1aChanged //--------------------------------------------------------- void MidiTransformerDialog::selVal1aChanged(int val) { data->cmt->selVal1a = val; if ((data->cmt->selEventOp != MusECore::All) && (data->cmt->selType == MusECore::Note)) { selVal1a->setSuffix(" - " + MusECore::pitch2string(val)); } else { if(!selVal1a->suffix().isEmpty()) selVal1a->setSuffix(QString("")); } } //--------------------------------------------------------- // selVal1bChanged //--------------------------------------------------------- void MidiTransformerDialog::selVal1bChanged(int val) { data->cmt->selVal1b = val; if ((data->cmt->selEventOp != MusECore::All) && (data->cmt->selType == MusECore::Note)) { selVal1b->setSuffix(" - " + MusECore::pitch2string(val)); } else { if(!selVal1b->suffix().isEmpty()) selVal1b->setSuffix(QString("")); } } //--------------------------------------------------------- // selVal2aChanged //--------------------------------------------------------- void MidiTransformerDialog::selVal2aChanged(int val) { data->cmt->selVal2a = val; } //--------------------------------------------------------- // selVal2bChanged //--------------------------------------------------------- void MidiTransformerDialog::selVal2bChanged(int val) { data->cmt->selVal2b = val; } //--------------------------------------------------------- // selLenAChanged //--------------------------------------------------------- void MidiTransformerDialog::selLenAChanged(int val) { data->cmt->selLenA = val; } //--------------------------------------------------------- // selLenBChanged //--------------------------------------------------------- void MidiTransformerDialog::selLenBChanged(int val) { data->cmt->selLenB = val; } //--------------------------------------------------------- // selBarAChanged //--------------------------------------------------------- void MidiTransformerDialog::selBarAChanged(int val) { data->cmt->selRangeA = val; } //--------------------------------------------------------- // selBarBChanged //--------------------------------------------------------- void MidiTransformerDialog::selBarBChanged(int val) { data->cmt->selRangeB = val; } //--------------------------------------------------------- // procVal1aChanged //--------------------------------------------------------- void MidiTransformerDialog::procVal1aChanged(int val) { data->cmt->procVal1a = val; if((data->cmt->procEvent == MusECore::Keep && data->cmt->selType == MIDITRANSFORM_NOTE) && (data->cmt->procVal1 == MusECore::Fix || data->cmt->procVal1 == MusECore::ScaleMap || data->cmt->procVal1 == MusECore::Dynamic || data->cmt->procVal1 == MusECore::Random || data->cmt->procVal1 == MusECore::Flip)) { procVal1a->setSuffix(" - " + MusECore::pitch2string(val)); } else { if(!procVal1a->suffix().isEmpty()) procVal1a->setSuffix(QString("")); } } //--------------------------------------------------------- // procVal1bChanged //--------------------------------------------------------- void MidiTransformerDialog::procVal1bChanged(int val) { data->cmt->procVal1b = val; if((data->cmt->procEvent == MusECore::Keep && data->cmt->selType == MIDITRANSFORM_NOTE) && (data->cmt->procVal1 == MusECore::Fix || data->cmt->procVal1 == MusECore::ScaleMap || data->cmt->procVal1 == MusECore::Dynamic || data->cmt->procVal1 == MusECore::Random || data->cmt->procVal1 == MusECore::Flip)) { procVal1b->setSuffix(" - " + MusECore::pitch2string(val)); } else { if(!procVal1b->suffix().isEmpty()) procVal1b->setSuffix(QString("")); } } //--------------------------------------------------------- // procVal2aChanged //--------------------------------------------------------- void MidiTransformerDialog::procVal2aChanged(int val) { data->cmt->procVal2a = val; } //--------------------------------------------------------- // procVal2bChanged //--------------------------------------------------------- void MidiTransformerDialog::procVal2bChanged(int val) { data->cmt->procVal2b = val; } //--------------------------------------------------------- // procLenAChanged //--------------------------------------------------------- void MidiTransformerDialog::procLenAChanged(int val) { data->cmt->procLenA = val; } //--------------------------------------------------------- // procPosAChanged //--------------------------------------------------------- void MidiTransformerDialog::procPosAChanged(int val) { data->cmt->procPosA = val; } //--------------------------------------------------------- // funcQuantValSel //--------------------------------------------------------- void MidiTransformerDialog::funcQuantValSel(int val) { data->cmt->quantVal = val; } //--------------------------------------------------------- // processAllChanged //--------------------------------------------------------- void MidiTransformerDialog::processAllChanged(bool val) { if (val == true) { selectedTracks->setChecked(false); insideLoop->setChecked(false); data->cmt->selectedTracks = false; data->cmt->insideLoop = false; } } //--------------------------------------------------------- // selectedTracksChanged //--------------------------------------------------------- void MidiTransformerDialog::selectedTracksChanged(bool val) { data->cmt->selectedTracks = val; processAll->setChecked(!val && !data->cmt->insideLoop); } //--------------------------------------------------------- // insideLoopChanged //--------------------------------------------------------- void MidiTransformerDialog::insideLoopChanged(bool val) { data->cmt->insideLoop = val; processAll->setChecked(!data->cmt->selectedTracks && !val); } /*! \fn MidiTransformerDialog::typesMatch(const MusECore::MidiEvent e, unsigned t) */ bool MidiTransformerDialog::typesMatch(const MusECore::Event& e, unsigned selType) { bool matched = false; switch (selType) { case MIDITRANSFORM_NOTE: matched = (e.type() == MusECore::Note); break; case MIDITRANSFORM_POLY: { if (e.type() == MusECore::Controller) { MusECore::MidiController::ControllerType c = MusECore::midiControllerType(e.dataA()); matched = (c == MusECore::MidiController::PolyAftertouch); } break; } case MIDITRANSFORM_CTRL: matched = (e.type() == MusECore::Controller); break; case MIDITRANSFORM_ATOUCH: { if (e.type() == MusECore::Controller) { MusECore::MidiController::ControllerType c = MusECore::midiControllerType(e.dataA()); matched = (c == MusECore::MidiController::Aftertouch); } break; } case MIDITRANSFORM_PITCHBEND: { if (e.type() == MusECore::Controller) { MusECore::MidiController::ControllerType c = MusECore::midiControllerType(e.dataA()); matched = (c == MusECore::MidiController::Pitch); } break; } case MIDITRANSFORM_NRPN: { if (e.type() == MusECore::Controller) { MusECore::MidiController::ControllerType c = MusECore::midiControllerType(e.dataA()); matched = (c == MusECore::MidiController::NRPN); } break; } case MIDITRANSFORM_RPN: { if (e.type() == MusECore::Controller) { MusECore::MidiController::ControllerType c = MusECore::midiControllerType(e.dataA()); matched = (c == MusECore::MidiController::RPN); } break; } case MIDITRANSFORM_PROGRAM: { if (e.type() == MusECore::Controller) { MusECore::MidiController::ControllerType c = MusECore::midiControllerType(e.dataA()); matched = (c == MusECore::MidiController::Program); } break; } default: fprintf(stderr, "Error matching type in MidiTransformerDialog: unknown eventtype!\n"); break; } //printf("Event type=%d, selType =%d matched=%d\n", e.type(), selType, matched); return matched; } } // namespace MusEGui muse-4.0.0/src/muse/mplugins/miditransform.h000066400000000000000000000106731404105311000211050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: miditransform.h,v 1.2.2.2 2009/02/02 21:38:00 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MIDITRANSFORM_H__ #define __MIDITRANSFORM_H__ #include "ui_transformbase.h" #include "type_defs.h" class QListWidgetItem; class QDialog; namespace MusECore { class Undo; class Event; class MidiPart; class MidiTransformation; class MidiTransformPrivate; class Xml; enum ValOp { All=0, Ignore=0, Equal=1, Unequal=2, Higher=3, Lower=4, Inside=5, Outside=6 }; enum TransformFunction { Select, Quantize, Delete, Transform, Insert, Copy, Extract }; enum TransformOperator { Keep, Plus, Minus, Multiply, Divide, Fix, Value, Invert, ScaleMap, Flip, Dynamic, Random, Toggle }; class TransformToggleState { private: bool _ctrlStates[128]; public: TransformToggleState() { for(int i = 0; i < 128; ++i) _ctrlStates[i] = false; } bool ctrlState(int ctl_num) { return _ctrlStates[ctl_num & 0x7f]; } void setCtrlState(int ctl_num, bool state) { _ctrlStates[ctl_num & 0x7f] = state; } }; extern void writeMidiTransforms(int level, Xml& xml); extern void readMidiTransform(Xml&); extern void clearMidiTransforms(); } // namespace MusECore namespace MusEGui { //--------------------------------------------------------- // MidiTransformDialog //--------------------------------------------------------- class MidiTransformerDialog : public QDialog, public Ui::MidiTransformDialogBase { Q_OBJECT MusECore::MidiTransformPrivate* data; virtual void accept(); void setValOp(QWidget* a, QWidget* b, MusECore::ValOp op); void processEvent(MusECore::Event&, MusECore::MidiPart*, MusECore::MidiPart*, MusECore::Undo& operations); bool isSelected(const MusECore::Event&); void transformEvent(MusECore::Event&, MusECore::MidiPart*, MusECore::MidiPart*, MusECore::Undo& operations); bool typesMatch(const MusECore::Event& e, unsigned selType); MusECore::MidiTransformation* createDefaultPreset(); void updatePresetList(); private slots: void apply(); void presetNew(); void presetDelete(); void selEventOpSel(int); void selTypeSel(int); void selVal1OpSel(int); void selVal2OpSel(int); void selLenOpSel(int); void selRangeOpSel(int); void procEventOpSel(int); void procEventTypeSel(int); void procVal1OpSel(int); void procVal2OpSel(int); void procVal2OpUpdate(MusECore::TransformOperator op); void procLenOpSel(int); void procPosOpSel(int); void funcOpSel(int); void presetChanged(QListWidgetItem*, QListWidgetItem*); void nameChanged(const QString&); void commentChanged(); void selVal1aChanged(int); void selVal1bChanged(int); void selVal2aChanged(int); void selVal2bChanged(int); void selLenAChanged(int); void selLenBChanged(int); void selBarAChanged(int); void selBarBChanged(int); void procVal1aChanged(int); void procVal1bChanged(int); void procVal2aChanged(int); void procVal2bChanged(int); void procLenAChanged(int); void procPosAChanged(int); void funcQuantValSel(int); void processAllChanged(bool); void selectedTracksChanged(bool); void insideLoopChanged(bool); public slots: void songChanged(MusECore::SongChangedStruct_t); public: MidiTransformerDialog(QDialog* parent = 0, Qt::WindowFlags fl = Qt::Widget); ~MidiTransformerDialog(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mplugins/mitplugin.cpp000066400000000000000000000142621404105311000205700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mitplugin.cpp,v 1.1.1.1 2003/10/27 18:52:40 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "mitplugin.h" #include "app.h" #include "event.h" #include "midiitransform.h" #include "mittranspose.h" #include "midifilterimpl.h" #include "mrconfig.h" #ifdef BUILD_EXPERIMENTAL #include "rhythm.h" #endif // Forwards from header: #include "xml.h" #include "mpevent.h" namespace MusEGui { //--------------------------------------------------------- // startMidiInputPlugin //--------------------------------------------------------- void MusE::startMidiInputPlugin(int id) { bool flag = false; QWidget* w = 0; QAction* act = 0; if (id == 0) { if (!MusEGlobal::mitPluginTranspose) { // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). MusEGlobal::mitPluginTranspose = new MITPluginTranspose(); MusECore::mitPlugins.push_back(MusEGlobal::mitPluginTranspose); connect(MusEGlobal::mitPluginTranspose, SIGNAL(hideWindow()), SLOT(hideMitPluginTranspose())); } w = MusEGlobal::mitPluginTranspose; act = midiTrpAction; } else if (id == 1) { if (!midiInputTransform) { // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). midiInputTransform = new MidiInputTransformDialog(); connect(midiInputTransform, SIGNAL(hideWindow()), SLOT(hideMidiInputTransform())); } w = midiInputTransform; act = midiInputTrfAction; } else if (id == 2) { if (!midiFilterConfig) { // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). midiFilterConfig = new MidiFilterConfig(); connect(midiFilterConfig, SIGNAL(hideWindow()), SLOT(hideMidiFilterConfig())); } w = midiFilterConfig; act = midiInputFilterAction; } else if (id == 3) { if (!midiRemoteConfig) { // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). midiRemoteConfig = new MRConfig(); connect(midiRemoteConfig, SIGNAL(hideWindow()), SLOT(hideMidiRemoteConfig())); } w = midiRemoteConfig; act = midiRemoteAction; } #ifdef BUILD_EXPERIMENTAL else if (id == 4) { if (!midiRhythmGenerator) { // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). midiRhythmGenerator = new RhythmGen(); connect(midiRhythmGenerator, SIGNAL(hideWindow()), SLOT(hideMidiRhythmGenerator())); } w = midiRhythmGenerator; act = midiRhythmAction; } #endif if (w) { flag = !w->isVisible(); if (flag) w->show(); else w->hide(); } if (act) act->setChecked(flag); } void MusE::hideMitPluginTranspose() { midiTrpAction->setChecked(false); } void MusE::hideMidiInputTransform() { midiInputTrfAction->setChecked(false); } void MusE::hideMidiFilterConfig() { midiInputFilterAction->setChecked(false); } void MusE::hideMidiRemoteConfig() { midiRemoteAction->setChecked(false); } #ifdef BUILD_EXPERIMENTAL void MusE::hideMidiRhythmGenerator() { midiRhythmAction->setChecked(false); } #endif //--------------------------------------------------------- // startMidiTransformer //--------------------------------------------------------- void MusE::startMidiTransformer() { if (midiTransformerDialog == 0) // NOTE: For deleting parentless dialogs and widgets, please add them to MusE::deleteParentlessDialogs(). midiTransformerDialog = new MidiTransformerDialog; midiTransformerDialog->show(); } } // namespace MusEGui namespace MusECore { MITPluginList mitPlugins; MITPlugin::~MITPlugin(){} void MITPlugin::readStatus(Xml&) {} void MITPlugin::writeStatus(int, Xml&) const {} //--------------------------------------------------------- // processMidiInputTransformPlugins //--------------------------------------------------------- void processMidiInputTransformPlugins(MEvent& event) { for (iMITPlugin i = mitPlugins.begin(); i != mitPlugins.end(); ++i) (*i)->process(event); } //--------------------------------------------------------- // writeStatusMidiInputTransformPlugins //--------------------------------------------------------- void writeStatusMidiInputTransformPlugins(int level, Xml& xml) { for (iMITPlugin i = mitPlugins.begin(); i != mitPlugins.end(); ++i) { xml.tag(level++, "mplugin name=\"%d\""); (*i)->writeStatus(level, xml); xml.etag(level, "mplugin"); } } } // namespace MusECore muse-4.0.0/src/muse/mplugins/mitplugin.h000066400000000000000000000033751404105311000202400ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mitplugin.h,v 1.1.1.1.2.1 2007/01/27 14:52:43 spamatica Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MITPLUGIN_H__ #define __MITPLUGIN_H__ #include namespace MusECore { // Forward declarations: class MEvent; class Xml; //--------------------------------------------------------- // MITPlugin // midi input transform plugin //--------------------------------------------------------- class MITPlugin { public: virtual ~MITPlugin(); virtual void process(MEvent& event) = 0; virtual void readStatus(Xml&); virtual void writeStatus(int, Xml&) const; }; typedef std::list MITPluginList; typedef MITPluginList::iterator iMITPlugin; extern MITPluginList mitPlugins; extern void processMidiInputTransformPlugins(MEvent&); extern void writeStatusMidiInputTransformPlugins(int, Xml&); } // namespace MusECore #endif muse-4.0.0/src/muse/mplugins/mittranspose.cpp000066400000000000000000000141161404105311000213060ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mittranspose.cpp,v 1.2.2.1 2009/05/03 04:14:00 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "mittranspose.h" #include "song.h" #include "mpevent.h" #include "pitchedit.h" #include "xml.h" #include "globals.h" namespace MusEGlobal { MusEGui::MITPluginTranspose* mitPluginTranspose = 0; } namespace MusEGui { //--------------------------------------------------------- // MITPluginTranspose //--------------------------------------------------------- MITPluginTranspose::MITPluginTranspose(QWidget* parent, Qt::WindowFlags fl) : QWidget(parent, fl) { setupUi(this); on = false; transpose = 0; trigger = 24; transposeChangedFlag = false; triggerKeySpinBox->setValue(trigger); onToggled(false); connect(onCheckBox, SIGNAL(toggled(bool)), SLOT(onToggled(bool))); connect(triggerKeySpinBox, SIGNAL(valueChanged(int)), SLOT(triggerKeyChanged(int))); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(noteReceived())); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void MITPluginTranspose::closeEvent(QCloseEvent* ev) { emit hideWindow(); QWidget::closeEvent(ev); } //--------------------------------------------------------- // noteReceived //--------------------------------------------------------- void MITPluginTranspose::noteReceived() { if (transposeChangedFlag) { // Added by Tim. p3.3.6 //printf("MITPluginTranspose::noteReceived\n"); transposeChanged(); } } //--------------------------------------------------------- // triggerKeyChanged //--------------------------------------------------------- void MITPluginTranspose::triggerKeyChanged(int val) { trigger = val; } //--------------------------------------------------------- // transposeChanged //--------------------------------------------------------- void MITPluginTranspose::transposeChanged() { QString s = QString(transpose >= 0 ? '-' : ' ') + QString::number(transpose); transposeLabel->setText(s); transposeChangedFlag = false; } //--------------------------------------------------------- // onToggled //--------------------------------------------------------- void MITPluginTranspose::onToggled(bool f) { on = f; if (!on) { transpose = 0; transposeChanged(); keyOnList.clear(); } transposeLabel->setEnabled(on); triggerKeySpinBox->setEnabled(on); } //--------------------------------------------------------- // process //--------------------------------------------------------- void MITPluginTranspose::process(MusECore::MEvent& ev) { if (!on || (ev.type() != 0x90)) return; int pitch = ev.dataA(); if (pitch >= trigger && pitch < (trigger+12)) { // process control keys int diff = transpose - (pitch-trigger); transpose -= diff; transposeChangedFlag = true; return; } if (ev.dataB() == 0) { // Note Off for (MusECore::iKeyOn i = keyOnList.begin(); i != keyOnList.end(); ++i) { if (i->pitch == pitch && i->channel == ev.channel() && i->port == ev.port()) { pitch += i->transpose; keyOnList.erase(i); break; } } } else { // Note On keyOnList.push_back(MusECore::KeyOn(pitch, ev.channel(), ev.port(), transpose)); pitch += transpose; } ev.setA(pitch); } //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void MITPluginTranspose::readStatus(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::Text: if (tag == "on") on = xml.parseInt(); else if (tag == "trigger") trigger = xml.parseInt(); else xml.unknown("TransposePlugin"); break; case MusECore::Xml::TagEnd: if (xml.s1() == "mplugin") return; default: break; } } } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void MITPluginTranspose::writeStatus(int level, MusECore::Xml& xml) const { xml.intTag(level, "on", on); xml.intTag(level, "trigger", trigger); } } // namespace MusEGui muse-4.0.0/src/muse/mplugins/mittranspose.h000066400000000000000000000050671404105311000207600ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mittranspose.h,v 1.1.1.1 2003/10/27 18:52:40 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MITTRANSPOSE_H__ #define __MITTRANSPOSE_H__ #include #include "mitplugin.h" #include "ui_mittransposebase.h" #include class QCloseEvent; namespace MusECore { class Xml; struct KeyOn { unsigned char pitch; unsigned char channel; unsigned char port; char transpose; KeyOn(int a, int b, int c, int d) { pitch = a; transpose = d; channel = b; port = c; } }; typedef std::list KeyOnList; typedef KeyOnList::iterator iKeyOn; } // namespace MusECore namespace MusEGui { //--------------------------------------------------------- // MITPluginTranspose //--------------------------------------------------------- class MITPluginTranspose : public QWidget, public Ui::MITTransposeBase, public MusECore::MITPlugin { Q_OBJECT MusECore::KeyOnList keyOnList; int transpose; // current pitch offset int trigger; bool on; bool transposeChangedFlag; void transposeChanged(); virtual void closeEvent(QCloseEvent*); signals: void hideWindow(); private slots: void onToggled(bool); void triggerKeyChanged(int); void noteReceived(); public: MITPluginTranspose(QWidget* parent = 0, Qt::WindowFlags fl = Qt::Widget); virtual void process(MusECore::MEvent&); virtual void readStatus(MusECore::Xml&); virtual void writeStatus(int, MusECore::Xml&) const; }; } // namespace MusEGui namespace MusEGlobal { extern MusEGui::MITPluginTranspose* mitPluginTranspose; } #endif muse-4.0.0/src/muse/mplugins/mittransposebase.ui000066400000000000000000000061011404105311000217670ustar00rootroot00000000000000 MITTransposeBase 0 0 423 50 MusE: Midi Input Plugin: Transpose 6 11 11 11 11 On Trigger Key Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 5 Transpose: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 5 0 0 QFrame::Panel 2 2 +0 Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter false 2 5 MusEGui::PitchEdit QWidget
pitchedit.h
pitchedit.h
muse-4.0.0/src/muse/mplugins/mrconfig.cpp000066400000000000000000000130101404105311000203520ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mrconfig.cpp,v 1.1.1.1 2003/10/27 18:52:43 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "pitchedit.h" #include "mrconfig.h" #include "globals.h" #include namespace MusEGui { //--------------------------------------------------------- // MRConfig // Midi Remote Control Config //--------------------------------------------------------- MRConfig::MRConfig(QWidget* parent, Qt::WindowFlags fl) : QWidget(parent, fl) { setupUi(this); b1->setChecked(MusEGlobal::rcEnable); sbStop->setValue(MusEGlobal::rcStopNote); sbRecord->setValue(MusEGlobal::rcRecordNote); sbGotoLeftMark->setValue(MusEGlobal::rcGotoLeftMarkNote); sbPlay->setValue(MusEGlobal::rcPlayNote); sbForward->setValue(MusEGlobal::rcForwardNote); sbRewind->setValue(MusEGlobal::rcBackwardNote); steprec_box->setValue(MusEGlobal::rcSteprecNote); b2->setChecked(MusEGlobal::rcEnableCC); sbPlayCC->setValue(MusEGlobal::rcPlayCC); sbStopCC->setValue(MusEGlobal::rcStopCC); sbRecCC->setValue(MusEGlobal::rcRecordCC); sbGotoLeftMarkCC->setValue(MusEGlobal::rcGotoLeftMarkCC); // sbInsertRestCC->setValue(MusEGlobal::rcInsertPauseCC); sbForwardCC->setValue(MusEGlobal::rcForwardCC); sbBackwardCC->setValue(MusEGlobal::rcBackwardCC); connect(b1, &QCheckBox::toggled, this, &MRConfig::setRcEnable); connect(sbStop, &PitchEdit::editingFinished, this, &MRConfig::setRcStopNote); connect(sbRecord, &PitchEdit::editingFinished, this, &MRConfig::setRcRecordNote); connect(sbGotoLeftMark, &PitchEdit::editingFinished, this, &MRConfig::setRcGotoLeftMarkNote); connect(sbPlay, &PitchEdit::editingFinished, this, &MRConfig::setRcPlayNote); connect(sbForward, &PitchEdit::editingFinished, this, &MRConfig::setRcForwardNote); connect(sbRewind, &PitchEdit::editingFinished, this, &MRConfig::setRcBackwardNote); connect(steprec_box, &PitchEdit::editingFinished, this, &MRConfig::setRcSteprecNote); connect(b2, &QCheckBox::toggled, this, &MRConfig::setRcEnableCC); connect(sbPlayCC, &QSpinBox::editingFinished, this, &MRConfig::setRcPlayCC); connect(sbStopCC, &QSpinBox::editingFinished, this, &MRConfig::setRcStopCC); connect(sbRecCC, &QSpinBox::editingFinished, this, &MRConfig::setRcRecordCC); connect(sbGotoLeftMarkCC, &QSpinBox::editingFinished, this, &MRConfig::setRcGotoLeftMarkCC); connect(sbForwardCC, &QSpinBox::editingFinished, this, &MRConfig::setRcForwardCC); connect(sbBackwardCC, &QSpinBox::editingFinished, this, &MRConfig::setRcBackwardCC); // connect(sbInsertRestCC, &QSpinBox::editingFinished, this, &MRConfig::setRcInsertRestCC); } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void MRConfig::closeEvent(QCloseEvent* ev) { emit hideWindow(); QWidget::closeEvent(ev); } void MRConfig::setRcEnable(bool f) { MusEGlobal::rcEnable = f; } void MRConfig::setRcPlayNote() { MusEGlobal::rcPlayNote = static_cast(sbPlay->value()); } void MRConfig::setRcStopNote() { MusEGlobal::rcStopNote = static_cast(sbStop->value()); } void MRConfig::setRcRecordNote() { MusEGlobal::rcRecordNote = static_cast(sbRecord->value()); } void MRConfig::setRcGotoLeftMarkNote() { MusEGlobal::rcGotoLeftMarkNote = static_cast(sbGotoLeftMark->value()); } void MRConfig::setRcSteprecNote() { MusEGlobal::rcSteprecNote = static_cast(steprec_box->value()); } void MRConfig::setRcForwardNote() { MusEGlobal::rcForwardNote = static_cast(sbForward->value()); } void MRConfig::setRcBackwardNote() { MusEGlobal::rcBackwardNote = static_cast(sbRewind->value()); } void MRConfig::setRcEnableCC(bool b) { MusEGlobal::rcEnableCC = b; } void MRConfig::setRcPlayCC() { MusEGlobal::rcPlayCC = static_cast(sbPlayCC->value()); } void MRConfig::setRcStopCC() { MusEGlobal::rcStopCC = static_cast(sbStopCC->value()); } void MRConfig::setRcRecordCC() { MusEGlobal::rcRecordCC = static_cast(sbRecCC->value()); } void MRConfig::setRcGotoLeftMarkCC() { MusEGlobal::rcGotoLeftMarkCC = static_cast(sbGotoLeftMarkCC->value()); } //void MRConfig::setRcInsertRestCC() //{ // MusEGlobal::rcInsertPauseCC = static_cast(sbInsertRestCC->value()); //} void MRConfig::setRcForwardCC() { MusEGlobal::rcForwardCC = static_cast(sbForwardCC->value()); } void MRConfig::setRcBackwardCC() { MusEGlobal::rcBackwardCC = static_cast(sbBackwardCC->value()); } } // namespace MusEGui muse-4.0.0/src/muse/mplugins/mrconfig.h000066400000000000000000000040051404105311000200230ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mrconfig.h,v 1.1.1.1 2003/10/27 18:52:43 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MRCONFIG_H__ #define __MRCONFIG_H__ #include "ui_mrconfigbase.h" class QCloseEvent; class QWidget; namespace MusEGui { //--------------------------------------------------------- // MRConfig //--------------------------------------------------------- class MRConfig : public QWidget, public Ui::MRConfigBase { Q_OBJECT virtual void closeEvent(QCloseEvent*); signals: void hideWindow(); private slots: void setRcEnable(bool); void setRcStopNote(); void setRcRecordNote(); void setRcGotoLeftMarkNote(); void setRcPlayNote(); void setRcSteprecNote(); void setRcForwardNote(); void setRcBackwardNote(); void setRcEnableCC(bool); void setRcPlayCC(); void setRcStopCC(); void setRcRecordCC(); void setRcGotoLeftMarkCC(); // void setRcInsertRestCC(); void setRcForwardCC(); void setRcBackwardCC(); public: MRConfig(QWidget* parent=0, Qt::WindowFlags fl = Qt::Widget); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mplugins/mrconfigbase.ui000066400000000000000000000270421404105311000210520ustar00rootroot00000000000000 MRConfigBase 0 0 434 354 MusE: Midi Input Plugin: Remote Control Actions Note on value Transport Goto Left Mark false Qt::Horizontal QSizePolicy::Fixed 20 20 Forward false Qt::Horizontal 40 20 127 Rewind false Qt::Horizontal QSizePolicy::Fixed 20 20 127 Record false Qt::Horizontal 40 20 127 Qt::Horizontal 40 20 CC on Qt::Horizontal QSizePolicy::Fixed 20 20 Qt::Horizontal QSizePolicy::Fixed 20 20 Note on Stop false Qt::Horizontal QSizePolicy::Fixed 20 20 Qt::Horizontal 40 20 Qt::Horizontal 40 20 127 127 127 Qt::Horizontal QSizePolicy::Fixed 20 20 Qt::Horizontal 40 20 Play false Insert rest (step recording) Qt::Horizontal 137 20 MusEGui::PitchEdit QWidget
pitchedit.h
pitchedit.h
muse-4.0.0/src/muse/mplugins/random.cpp000066400000000000000000000340571404105311000200440ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: random.cpp,v 1.1.1.1 2003/10/27 18:52:39 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // // This code is an adaption of the random rhythm generator taken // from "The JAZZ++ Midi Sequencer" // Copyright (C) 1994-2000 Andreas Voss and Per Sigmond, all // rights reserved. // Distributed under the GNU General Public License //========================================================= #if 0 #include "random.h" #include "util.h" #include #if USE_ACG ACG rnd(0, 55); #else #include double tRandomGenerator::asDouble() { return double(rand()) / double(RAND_MAX); } tRandomGenerator rnd; #endif // Array of probabilities tRndArray::tRndArray(int nn, int mmin, int mmax) { int i; n = nn; for (i = 0; i < n; i++) array[i] = mmin; min = mmin; max = mmax; nul = min > 0 ? min : 0; } void tRndArray::SetMinMax(int mi, int ma) { min = mi; max = ma; nul = min > 0 ? min : 0; for (int i = 0; i < array.GetSize(); i++) { if (array[i] < min) array[i] = min; else if (array[i] > max) array[i] = max; } } tRndArray::~tRndArray() { } #ifdef FOR_MSW double tRndArray::operator[](double f) #else double tRndArray::operator[](double f) const #endif { int i = (int)f; if (i < 0) i = 0; else if (i >= n - 2) i = n - 2; tMapper map(i, i+1, array[i], array[i+1]); return map(f); } tRndArray & tRndArray::operator = (const tRndArray &o) { if (this == &o) return *this; array = o.array; n = o.n; min = o.min; max = o.max; nul = o.nul; return *this; } tRndArray::tRndArray(const tRndArray &o) : array(o.array) { n = o.n; min = o.min; max = o.max; nul = o.nul; } int tRndArray::Random() { return Random(rnd.asDouble()); } int tRndArray::Random(double rndval) { double sum, dec; int i; assert(n > 0); sum = 0.0; for (i = 0; i < n; i++) { assert(array[i] >= 0); sum += array[i]; } if (sum <= 0) return 0; dec = sum * rndval * 0.99999; assert(dec < sum); i = 0; while (dec >= 0.0) { dec -= array[i]; i++; } i--; assert(i >= 0 && i < n); return i; } int tRndArray::Interval(int seed) { if (seed < 0) // initial ? seed = int(rnd.asDouble() * n); int delta = Random(); if (rnd.asDouble() < 0.5) delta = -delta; seed = (seed + n + delta) % n; return seed; } int tRndArray::Random(int i) { return rnd.asDouble() * (max - min) < array[i]; } void tRndArray::SetUnion(tRndArray &o, int fuzz) { for (int i = 0; i < n; i++) { int val = array[i]; if (o.array[i] > val) val = o.array[i]; array[i] = Fuzz(fuzz, array[i], val); } } void tRndArray::SetIntersection(tRndArray &o, int fuzz) { for (int i = 0; i < n; i++) { int val = array[i]; if (o.array[i] < val) val = o.array[i]; array[i] = Fuzz(fuzz, array[i], val); } } void tRndArray::SetDifference(tRndArray &o, int fuzz) { tRndArray tmp(o); tmp.SetInverse(tmp.Max()); SetIntersection(tmp, fuzz); } void tRndArray::SetInverse(int fuzz) { for (int i = 0; i < n; i++) array[i] = Fuzz(fuzz, array[i], min + max - array[i]); } int tRndArray::Fuzz(int fuz, int v1, int v2) const { // interpolate between v1 and v2 return (fuz - min) * v2 / (max - min) + (max - fuz) * v1 / (max - min); } void tRndArray::Clear() { for (int i = 0; i < n; i++) array[i] = min; } ostream & operator << (ostream &os, tRndArray const &a) { int i; os << a.n << " " << a.min << " " << a.max << endl; for (i = 0; i < a.n; i++) os << a.array[i] << " "; os << endl; return os; } istream & operator >> (istream &is, tRndArray &a) { int i; is >> a.n >> a.min >> a.max; for (i = 0; i < a.n; i++) is >> a.array[i]; return is; } // --------------------------------- tArrayEdit ------------------------------------- // length of tickmark line #define TICK_LINE 0 tArrayEdit::tArrayEdit(wxFrame *frame, tRndArray &ar, long xx, long yy, long ww, long hh, int sty) : wxCanvas(frame, xx, yy, ww, hh), array(ar), n(ar.n), min(ar.min), max(ar.max), nul(ar.nul) { draw_bars = 0; enabled = 1; dragging = 0; index = -1; label = 0; style_bits = sty; xmin = 0; xmax = n; x = 0; // draw to topleft corner of canvas y = 0; w = ww; h = hh; float tw, th; wxDC *dc = GetDC(); dc->SetFont(wxSMALL_FONT); dc->GetTextExtent("123", &tw, &th); if (style_bits & ARED_XTICKS) { // leave space for bottomline h -= (int)th; } if (style_bits & (ARED_MINMAX | ARED_YTICKS)) { // leave space to display min / max x = (int)(tw + TICK_LINE); w -= (int)(tw + TICK_LINE); } ynul = y + h - h * (nul - min) / (max - min); } void tArrayEdit::OnSize(int ww, int hh) { w = ww; h = hh; wxCanvas::OnSize(w, h); float tw, th; GetDC()->GetTextExtent("123", &tw, &th); if (style_bits & ARED_XTICKS) h -= (int)th; if (style_bits & (ARED_MINMAX | ARED_YTICKS)) { x = (int)(tw + TICK_LINE); w -= (int)(tw + TICK_LINE); } ynul = y + h - h * (nul - min) / (max - min); } tArrayEdit::~tArrayEdit() { delete [] label; } void tArrayEdit::DrawBar(int i, int Qt::black) { wxDC *dc = GetDC(); if (style_bits & ARED_LINES) { if (!Qt::black) dc->SetPen(wxWHITE_PEN); tMapper xmap(0, n, 0, w); tMapper ymap(min, max, h, 0); float x1 = (float)xmap(i + 0.5); float y1 = (float)ymap(array[i]); if (i > 0) { // draw line to prev position float x0 = (float)xmap(i - 0.5); float y0 = (float)ymap(array[i-1]); dc->DrawLine(x0, y0, x1, y1); } if (i < n-1) { // draw line to next position float x2 = (float)xmap(i + 1.5); float y2 = (float)ymap(array[i+1]); dc->DrawLine(x1, y1, x2, y2); } if (!Qt::black) dc->SetPen(wxBLACK_PEN); return; } int gap = 0; if (style_bits & ARED_GAP) { gap = w / n / 6; if (!gap && w / n > 3) gap = 1; } long xbar, ybar, wbar, hbar; wbar = w / n - 2 * gap; xbar = x + i * w / n + gap; hbar = h * (array[i] - nul) / (max - min); if (style_bits & ARED_BLOCKS) { /* ybar = ynul - hbar; if (hbar < 0) hbar = -hbar; hbar = (hbar < 2) ? hbar : 2; */ int hblk = 12; ybar = ynul - hbar - hblk/2; hbar = hblk; if (ybar < y) { int d = y - ybar; ybar += d; hbar -= d; } if (ybar + hbar > y + h) { int d = (ybar + hbar) - (y + h); hbar -= d; } if (hbar < 2) hbar = 2; } else if (hbar < 0) { ybar = ynul; hbar = -hbar; } else ybar = ynul - hbar; if (ybar == y) ++ybar, --hbar; if (!Qt::black) { dc->SetBrush(wxWHITE_BRUSH); dc->SetPen(wxWHITE_PEN); } if (wbar && hbar) dc->DrawRectangle(xbar, ybar, wbar, hbar); if (!Qt::black) { dc->SetBrush(wxBLACK_BRUSH); dc->SetPen(wxBLACK_PEN); } } const char *tArrayEdit::GetXText(int xval) { static char buf[8]; sprintf(buf, "%d", xval); return buf; } const char *tArrayEdit::GetYText(int yval) { static char buf[8]; sprintf(buf, "%d", yval); return buf; } void tArrayEdit::DrawXTicks() { float tw, th; if (!(style_bits & ARED_XTICKS)) return; wxDC *dc = GetDC(); dc->SetFont(wxSMALL_FONT); // compute tickmark x-distance dc->GetTextExtent("-123", &tw, &th); int max_labels = (int)(w / (tw + tw/2)); if (max_labels > 0) { int step = (xmax - xmin + 1) / max_labels; if (step <= 0) step = 1; for (int val = xmin; val <= xmax; val += step) { const char *buf = GetXText(val); //sprintf(buf, "%d", val); dc->GetTextExtent((char *)buf, &tw, &th); float yy = y + h; float xx = x + w * (val - xmin) / (xmax - xmin + 1); xx -= tw/2; // center text xx += 0.5 * w / n; // middle of bar dc->DrawText(buf, xx, yy); //dc->DrawLine(x - TICK_LINE, yy, x, yy); } } dc->SetFont(wxNORMAL_FONT); } void tArrayEdit::DrawYTicks() { wxDC *dc = GetDC(); dc->SetFont(wxSMALL_FONT); if (style_bits & ARED_YTICKS) { // compute tickmark y-distance float tw, th; dc->GetTextExtent("-123", &tw, &th); int max_labels = (int)(h / (th + th/2)); if (max_labels > 0) { int step = (max - min) / max_labels; if (step <= 0) step = 1; for (int val = min; val < max; val += step) { const char *buf = GetYText(val); //sprintf(buf, "%d", val); dc->GetTextExtent((char *)buf, &tw, &th); float yy = y + h - h * (val - min) / (max - min) - th/2; dc->DrawText(buf, x - tw - TICK_LINE, yy); //dc->DrawLine(x - TICK_LINE, yy, x, yy); } } } else if (style_bits & ARED_MINMAX) { // min/max float tw, th; char buf[20]; sprintf(buf, "%d", max); dc->GetTextExtent(buf, &tw, &th); dc->DrawText(buf, x - tw, y); sprintf(buf, "%d", min); dc->GetTextExtent(buf, &tw, &th); dc->DrawText(buf, x - tw, y + h - th); } dc->SetFont(wxNORMAL_FONT); } void tArrayEdit::DrawLabel() { wxDC *dc = GetDC(); dc->SetFont(wxSMALL_FONT); if (label) dc->DrawText(label, x + 5, y + 2); dc->SetFont(wxNORMAL_FONT); } void tArrayEdit::OnPaint() { int i; wxDC *dc = GetDC(); // surrounding rectangle dc->Clear(); if (enabled) dc->SetBrush(wxWHITE_BRUSH); else dc->SetBrush(wxGREY_BRUSH); dc->SetPen(wxBLACK_PEN); if (w && h) dc->DrawRectangle(x, y, w, h); // sliders dc->SetBrush(wxBLACK_BRUSH); for (i = 0; i < n; i++) DrawBar(i, 1); DrawXTicks(); DrawLabel(); DrawYTicks(); DrawNull(); if (draw_bars) draw_bars->DrawBars(); } void tArrayEdit::DrawNull() { wxDC *dc = GetDC(); dc->SetPen(wxCYAN_PEN); // draw y-null line if (min < nul && nul < max) dc->DrawLine(x, ynul, x+w, ynul); // draw x-null line if (xmin < 0 && 0 < xmax) { float x0 = w * (0 - xmin) / (xmax - xmin); dc->DrawLine(x0, y, x0, y + h); } dc->SetPen(wxBLACK_PEN); } void tArrayEdit::SetXMinMax(int xmi, int xma) { xmin = xmi; xmax = xma; } int tArrayEdit::Index(wxMouseEvent &e) { float ex, ey; e.Position(&ex, &ey); int i = (int)( ((short)ex - x) * n / w); i = i < 0 ? 0 : i; i = i >= n ? n-1 : i; return i; } int tArrayEdit::Dragging(wxMouseEvent &e) { if (!dragging) return 0; if (index < 0) index = Index(e); int val = nul; if (e.LeftIsDown()) { float ex, ey; e.Position(&ex, &ey); // $blk$ val = (int)( (y + h - (short)ey) * (max - min) / h + min); val = (int)( (double)(y + h - ey) * (max - min) / h + min + 0.5); val = val > max ? max : val; val = val < min ? min : val; } #if 0 { // in msw ex,ey are 65536 for negative values! wxDC *dc = GetDC(); char buf[500]; sprintf(buf, "x %4.0f, y %4.0f, sh %d", ex, ey, e.ShiftDown()); dc->DrawText(buf, 50, 50); } #endif if (e.ShiftDown()) { int k; for (k = 0; k < n; k++) { DrawBar(k, 0); array[k] = val; DrawBar(k, 1); } } else if (e.ControlDown()) { DrawBar(index, 0); array[index] = val; DrawBar(index, 1); } else { int i = Index(e); int k = i; if (i < index) for (; i <= index; i++) { DrawBar(i, 0); array[i] = val; DrawBar(i, 1); } else for (; i >= index; i--) { DrawBar(i, 0); array[i] = val; DrawBar(i, 1); } index = k; } return 0; } int tArrayEdit::ButtonDown(wxMouseEvent &e) { #ifdef wx_msw CaptureMouse(); #endif dragging = 1; index = Index(e); Dragging(e); return 0; } int tArrayEdit::ButtonUp(wxMouseEvent &e) { #ifdef wx_msw ReleaseMouse(); #endif dragging = 0; index = -1; DrawLabel(); DrawNull(); return 0; } void tArrayEdit::OnEvent(wxMouseEvent &e) { if (!enabled) return; if (e.ButtonDown()) ButtonDown(e); else if (e.Dragging()) Dragging(e); else if (e.ButtonUp()) ButtonUp(e); } void tArrayEdit::Enable(int e) { enabled = e; } void tArrayEdit::SetLabel(char const *llabel) { delete label; label = copystring(llabel); } void tArrayEdit::SetYMinMax(int mi, int ma) { array.SetMinMax(mi, ma); ynul = y + h - h * (nul - min) / (max - min); } void tArrayEdit::DrawBarLine (long xx) { wxDC *dc = GetDC (); // fprintf(stderr,"x: %ld, xx: %ld\n",x,xx); if (xx > x && xx + 1 < x + w) { dc->SetPen (wxLIGHT_GREY_PEN); dc->DrawLine (xx, y + 1, xx, y + h - 2); dc->SetPen (wxBLACK_PEN); } } tRhyArrayEdit::tRhyArrayEdit(wxFrame *parent, tRndArray &array, long xx, long yy, long ww, long hh, int sty) : tArrayEdit(parent, array, xx, yy, ww, hh, sty) { steps_per_count = 4; count_per_bar = 4; n_bars = 4; } void tRhyArrayEdit::SetMeter(int s, int c, int b) { steps_per_count = s; count_per_bar = c; n_bars = b; array.Resize(s * c * b); SetXMinMax(1, s * c * b); } void tRhyArrayEdit::DrawXTicks() { if (!(style_bits & ARED_RHYTHM)) { tArrayEdit::DrawXTicks(); return; } char buf[20]; float tw, th; wxDC *dc = GetDC(); dc->SetFont(wxSMALL_FONT); // tick marks assert(steps_per_count && count_per_bar && n_bars); int i; for (i = 0; i < n; i += steps_per_count) { int mark = (i / steps_per_count) % count_per_bar + 1; sprintf(buf, "%d", mark); float yy = y + h; float xx = x + (i + 0.5) * w / n; dc->GetTextExtent(buf, &tw, &th); xx -= tw/2.0; dc->DrawText(buf, xx, yy); } dc->SetFont(wxNORMAL_FONT); } #endif muse-4.0.0/src/muse/mplugins/random.h000066400000000000000000000125051404105311000175030ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: random.h,v 1.1.1.1 2003/10/27 18:52:43 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // // This code is an adaption of the random rhythm generator taken // from "The JAZZ++ Midi Sequencer" // Copyright (C) 1994-2000 Andreas Voss and Per Sigmond, all // rights reserved. // Distributed under the GNU General Public License //========================================================= #if 0 #ifndef random_h #define random_h #ifndef wx_wxh #include "wx.h" #endif #ifndef _FSTREAM_H #include #endif #ifndef dynarray_h #include "dynarray.h" #endif // gcc > 2.7.2 does not have ACG anymore? #define USE_ACG 0 #if USE_ACG #include // random generator from libg++ extern ACG rnd; #else class tRandomGenerator { public: double asDouble(); }; extern tRandomGenerator rnd; #endif #undef min #undef max // array of probabilities class tRndArray { friend class tArrayEdit; protected: tIntArray array; int n; // number of elements in array int nul, min, max; public: int Null() { return nul; } void SetNull(int n) { nul = n; } tRndArray(int n, int min, int max); tRndArray & operator = (const tRndArray &); tRndArray(tRndArray const &); virtual ~tRndArray(); int &operator[] (int i) { return array[i]; } int operator[] (int i) const { return array[i]; } #ifdef FOR_MSW double operator[](double f); float operator[](float f) { #else double operator[](double f) const; float operator[](float f) const { #endif return (float)operator[]((double)f); } int Size() const { return n; } int Min() const { return min; } int Max() const { return max; } void SetMinMax(int min, int max); void Resize(int nn) { n = nn; } friend ostream & operator << (ostream &, tRndArray const &); friend istream & operator >> (istream &, tRndArray &); int Random(); // returns index 0..n-1 (arrayvalues -> empiric distribution) int Random(double rndval); // returns index 0..n-1 (arrayvalues -> empiric distribution) int Random(int i); // return 0/1 int Interval(int seed); void SetUnion(tRndArray &o, int fuzz); void SetDifference(tRndArray &o, int fuzz); void SetIntersection(tRndArray &o, int fuzz); void SetInverse(int fuzz); int Fuzz(int fuzz, int v1, int v2) const; void Clear(); }; #define ARED_GAP 1 #define ARED_XTICKS 2 #define ARED_YTICKS 4 #define ARED_MINMAX 8 #define ARED_RHYTHM 16 #define ARED_BLOCKS 32 #define ARED_LINES 64 class tArrayEditDrawBars { public: virtual void DrawBars() = 0; }; class tArrayEdit : public wxCanvas { protected: // paint position long x, y, w, h, ynul; void DrawBar(int i, int Qt::black); int dragging; // Dragging-Event valid int index; // ctrl down: drag this one tRndArray &array; int &n, &min, &max, &nul; // shorthand for array.n, array.min, ... char *label; tArrayEditDrawBars *draw_bars; // array size is mapped to this range for x-tick marks int xmin, xmax; virtual void DrawXTicks(); virtual void DrawYTicks(); virtual void DrawLabel(); virtual void DrawNull(); int Index(wxMouseEvent &e); int enabled; int style_bits; virtual const char *GetXText(int xval); // Text for x-tickmarks virtual const char *GetYText(int yval); // Text for y-tickmarks public: tArrayEdit(wxFrame *parent, tRndArray &array, long xx, long yy, long ww, long hh, int style_bits = (ARED_GAP | ARED_XTICKS)); virtual ~tArrayEdit(); virtual void OnPaint(); virtual void OnSize(int ww, int hh); virtual void OnEvent(wxMouseEvent &e); virtual int Dragging(wxMouseEvent &); virtual int ButtonDown(wxMouseEvent &); virtual int ButtonUp(wxMouseEvent &); virtual void SetLabel(char const *llabel); void Enable(int enable = 1); void SetStyle(int style) { style_bits = style; } // min and max value in array (both values inclusive) void SetYMinMax(int min, int max); // for display x-axis only, does not resize the array (both values inclusive) void SetXMinMax(int xmin, int xmax); void DrawBarLine (long xx); void SetDrawBars(tArrayEditDrawBars *x) { draw_bars = x; } void Init() {} }; class tRhyArrayEdit : public tArrayEdit { int steps_per_count; int count_per_bar; int n_bars; protected: virtual void DrawXTicks(); public: tRhyArrayEdit(wxFrame *parent, tRndArray &array, long xx, long yy, long ww, long hh, int style_bits = (ARED_GAP | ARED_XTICKS | ARED_RHYTHM)); void SetMeter(int steps_per_count, int count_per_bar, int n_bars); }; #endif #endif muse-4.0.0/src/muse/mplugins/rhythm.cpp000066400000000000000000000405161404105311000200740ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: rhythm.cpp,v 1.1.1.1 2003/10/27 18:52:49 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // // This code is an adaption of the random rhythm generator taken // from "The JAZZ++ Midi Sequencer" // Copyright (C) 1994-2000 Andreas Voss and Per Sigmond, all // rights reserved. // Distributed under the GNU General Public License //========================================================= #include "rhythm.h" namespace MusEGui { //--------------------------------------------------------- // RhythmGen //--------------------------------------------------------- RhythmGen::RhythmGen(QWidget* parent, Qt::WindowFlags fo) : QMainWindow(parent, fo) { setupUi(this); } RhythmGen::~RhythmGen() { } //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void RhythmGen::closeEvent(QCloseEvent* ev) { emit hideWindow(); QWidget::closeEvent(ev); } #if 0 /**************************************************************************** ** Form implementation generated from reading ui file 'rhythm.ui' ** ** Created: Tue Feb 26 13:43:04 2002 ** by: The User Interface Compiler (uic) ** ** WARNING! All changes made in this file will be lost! ****************************************************************************/ #include "rhythm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char* const image0_data[] = { "16 16 56 1", ". c None", "F c #000000", "L c #000101", "E c #010304", "C c #06101d", "j c #071728", "w c #07182a", "1 c #071c2c", "0 c #081c2d", "e c #081e31", "a c #0a121f", "Z c #0a1929", "Q c #0a263b", "T c #0a273b", "z c #0e97bf", "s c #0ea0c5", "K c #0f5d7e", "B c #105375", "G c #114760", "N c #1197b9", "x c #124c6d", "# c #124d6f", "u c #1290b5", "v c #135476", "h c #1390b4", "Y c #14839f", "b c #155474", "i c #155678", "o c #155a7a", "J c #159abc", "d c #17587a", "V c #178eac", "f c #194a6a", "r c #19b3ce", "k c #1b4b6a", "p c #1b4f6f", "A c #1ca2c3", "D c #20374f", "P c #2294b1", "I c #22bad1", "S c #2a98b3", "U c #2cc7d5", "n c #3cd7e1", "O c #43d5de", "t c #48dfe9", "X c #58acc5", "m c #59c3da", "q c #71d1e0", "W c #80c2db", "c c #819eba", "H c #9ddeee", "M c #cdebf6", "g c #ddeff8", "y c #e2f0f9", "R c #fdfafd", "l c #fdfcfd", "................", "................", ".......#a.......", "......bcde......", ".....fcghij.....", "....kclmnhoj....", "...pclqrstuvw...", "..xcymrzzsnABC..", ".BDEFGHzIJKLFFF.", ".....BMNOPQ.....", ".....BRNOST.....", ".....BMNUVT.....", ".....BWXIYF.....", ".....BZ01FF.....", "................", "................"}; static const char* const image1_data[] = { "16 16 50 1", ". c None", "E c #010001", "O c #03060c", "U c #050b12", "u c #05151e", "S c #060f19", "m c #06141d", "q c #06141f", "h c #071620", "D c #0b293e", "c c #0d324c", "v c #0d3652", "A c #0e5775", "J c #0e8fb6", "x c #0e94b9", "r c #0e97bf", "Q c #0e9ec3", "n c #0eadcb", "R c #0fa0c4", "N c #106589", "V c #10698f", "B c #114560", "# c #124d6f", "z c #1299bb", "C c #144059", "T c #14a5c9", "K c #15c1da", "b c #18425f", "y c #1bbad1", "M c #1ca2c3", "t c #1f95b2", "a c #254a64", "p c #2695b1", "l c #2b91ae", "F c #337f9e", "o c #40d1db", "s c #46dbe6", "L c #48dfe9", "I c #54bdd7", "j c #5fc0d8", "g c #64a3bd", "k c #8fcce2", "d c #a0cbdf", "H c #a7dcec", "w c #bfdfee", "f c #d3e5f0", "G c #dfeff8", "i c #e0f1fb", "e c #eff3fc", "P c #ffffff", "................", "................", ".....###abc.....", ".....#defgh.....", ".....#ijklm.....", ".....#enopq.....", ".....airstu.....", ".v##aawxyzABCDE.", "..vFGHIJrKLMNO..", "...vFPQrRLMNS...", "....vFPTLMNU....", ".....vFGMNU.....", "......vVNS......", ".......vO.......", "................", "................"}; /* * Constructs a RhythmGenerator which is a child of 'parent', with the * name 'name' and widget flags set to 'f'. * * The dialog will by default be modeless, unless you set 'modal' to * TRUE to construct a modal dialog. */ RhythmGenerator::RhythmGenerator( QWidget* parent, const char* name, bool modal, Qt::WindowFlags fl ) : QDialog( parent, name, modal, fl ) { QPixmap image0( ( const char** ) image0_data ); QPixmap image1( ( const char** ) image1_data ); if ( !name ) setName( "RhythmGenerator" ); resize( 500, 777 ); setCaption( trUtf8( "Form3" ) ); RhythmGeneratorLayout = new Q3VBoxLayout( this, 11, 6, "RhythmGeneratorLayout"); Frame6 = new QFrame( this); Frame6->setFrameShape( QFrame::StyledPanel ); Frame6->setFrameShadow( QFrame::Raised ); Frame6Layout = new Q3VBoxLayout( Frame6, 11, 6, "Frame6Layout"); TextLabel1 = new QLabel( Frame6, "TextLabel1" ); QFont TextLabel1_font( TextLabel1->font() ); TextLabel1_font.setBold( TRUE ); TextLabel1_font.setUnderline( TRUE ); TextLabel1->setFont( TextLabel1_font ); TextLabel1->setText( trUtf8( "Instrument Settings:" ) ); Frame6Layout->addWidget( TextLabel1 ); Layout17 = new Q3HBoxLayout( 0, 0, 15, "Layout17"); Layout16 = new Q3VBoxLayout( 0, 0, 6, "Layout16"); InstrumentListLabel = new QLabel( Frame6, "InstrumentListLabel" ); InstrumentListLabel->setText( trUtf8( "Instrument" ) ); Layout16->addWidget( InstrumentListLabel ); InstrumentList = new Q3ListBox( Frame6, "InstrumentList" ); InstrumentList->insertItem( trUtf8( "Hi-Hat" ) ); InstrumentList->setMinimumSize( QSize( 150, 70 ) ); Layout16->addWidget( InstrumentList ); Layout11 = new Q3HBoxLayout( 0, 0, 2, "Layout11"); InstrumentUp = new QToolButton( Frame6, "InstrumentUp" ); InstrumentUp->setMinimumSize( QSize( 40, 20 ) ); InstrumentUp->setText( trUtf8( "" ) ); InstrumentUp->setPixmap( image0 ); Layout11->addWidget( InstrumentUp ); InstrumentDown = new QToolButton( Frame6, "InstrumentDown" ); InstrumentDown->setMinimumSize( QSize( 40, 20 ) ); InstrumentDown->setText( trUtf8( "" ) ); InstrumentDown->setPixmap( image1 ); Layout11->addWidget( InstrumentDown ); InstrumentAdd = new QToolButton( Frame6, "InstrumentAdd" ); InstrumentAdd->setMinimumSize( QSize( 40, 20 ) ); InstrumentAdd->setText( trUtf8( "add" ) ); Layout11->addWidget( InstrumentAdd ); InstrumentDel = new QToolButton( Frame6, "InstrumentDel" ); InstrumentDel->setMinimumSize( QSize( 40, 20 ) ); InstrumentDel->setText( trUtf8( "delete" ) ); Layout11->addWidget( InstrumentDel ); Layout16->addLayout( Layout11 ); Layout17->addLayout( Layout16 ); Layout20 = new Q3VBoxLayout( 0, 0, 6, "Layout20"); StepsLabel = new QLabel( Frame6, "StepsLabel" ); StepsLabel->setText( trUtf8( "steps/count" ) ); Layout20->addWidget( StepsLabel ); StepsLayout = new Q3HBoxLayout( 0, 0, 6, "StepsLayout"); StepsSlider = new QSlider( Frame6, "StepsSlider" ); StepsSlider->setMaxValue( 16 ); StepsSlider->setValue( 4 ); StepsSlider->setOrientation( Qt::Horizontal ); StepsLayout->addWidget( StepsSlider ); StepsNumber = new QLCDNumber( Frame6, "StepsNumber" ); StepsNumber->setPaletteBackgroundColor( QColor( 212, 212, 192 ) ); StepsNumber->setLineWidth( 1 ); StepsNumber->setNumDigits( 2 ); StepsNumber->setSegmentStyle( QLCDNumber::Flat ); StepsNumber->setProperty( "value", 4 ); StepsLayout->addWidget( StepsNumber ); Layout20->addLayout( StepsLayout ); CountLabel = new QLabel( Frame6, "CountLabel" ); CountLabel->setText( trUtf8( "count/bar" ) ); Layout20->addWidget( CountLabel ); CountLayout = new Q3HBoxLayout( 0, 0, 6, "CountLayout"); CountSlider = new QSlider( Frame6, "CountSlider" ); CountSlider->setMaxValue( 16 ); CountSlider->setValue( 4 ); CountSlider->setOrientation( Qt::Horizontal ); CountLayout->addWidget( CountSlider ); CountNumber = new QLCDNumber( Frame6, "CountNumber" ); CountNumber->setPaletteBackgroundColor( QColor( 212, 212, 192 ) ); CountNumber->setNumDigits( 2 ); CountNumber->setSegmentStyle( QLCDNumber::Flat ); CountNumber->setProperty( "value", 4 ); CountLayout->addWidget( CountNumber ); Layout20->addLayout( CountLayout ); BarsLabel = new QLabel( Frame6, "BarsLabel" ); BarsLabel->setText( trUtf8( "# bars" ) ); Layout20->addWidget( BarsLabel ); BarsLayout = new Q3HBoxLayout( 0, 0, 6, "BarsLayout"); BarsSlider = new QSlider( Frame6, "BarsSlider" ); BarsSlider->setMaxValue( 16 ); BarsSlider->setValue( 1 ); BarsSlider->setOrientation( Qt::Horizontal ); BarsLayout->addWidget( BarsSlider ); BarsNumber = new QLCDNumber( Frame6, "BarsNumber" ); BarsNumber->setPaletteBackgroundColor( QColor( 212, 212, 192 ) ); BarsNumber->setNumDigits( 2 ); BarsNumber->setSegmentStyle( QLCDNumber::Flat ); BarsNumber->setProperty( "value", 1 ); BarsLayout->addWidget( BarsNumber ); Layout20->addLayout( BarsLayout ); Layout17->addLayout( Layout20 ); Frame6Layout->addLayout( Layout17 ); RhythmGeneratorLayout->addWidget( Frame6 ); Layout27 = new Q3HBoxLayout( 0, 0, 15, "Layout27"); Frame5 = new QFrame( this ); Frame5->setFrameShape( QFrame::StyledPanel ); Frame5->setFrameShadow( QFrame::Raised ); Frame5Layout = new Q3VBoxLayout( Frame5, 11, 6, "Frame5Layout"); TextLabel2 = new QLabel( Frame5, "TextLabel2" ); QFont TextLabel2_font( TextLabel2->font() ); TextLabel2_font.setBold( TRUE ); TextLabel2_font.setUnderline( TRUE ); TextLabel2->setFont( TextLabel2_font ); TextLabel2->setText( trUtf8( "Group Settings:" ) ); Frame5Layout->addWidget( TextLabel2 ); Layout20_2 = new Q3HBoxLayout( 0, 0, 15, "Layout20_2"); GroupListLayout = new Q3VBoxLayout( 0, 0, 6, "GroupListLayout"); GroupListLabel = new QLabel( Frame5, "GroupListLabel" ); GroupListLabel->setText( trUtf8( "Group" ) ); GroupListLayout->addWidget( GroupListLabel ); GroupList = new Q3ListBox( Frame5, "GroupList" ); GroupList->insertItem( trUtf8( "Group 1" ) ); GroupList->insertItem( trUtf8( "Group 2" ) ); GroupList->insertItem( trUtf8( "Group 3" ) ); GroupList->insertItem( trUtf8( "Group 4" ) ); GroupList->insertItem( trUtf8( "Group 5" ) ); GroupList->setMinimumSize( QSize( 150, 90 ) ); GroupListLayout->addWidget( GroupList ); Layout20_2->addLayout( GroupListLayout ); Layout19 = new Q3VBoxLayout( 0, 0, 6, "Layout19"); ContribLabel = new QLabel( Frame5, "ContribLabel" ); ContribLabel->setText( trUtf8( "contrib" ) ); Layout19->addWidget( ContribLabel ); ContribLayout = new Q3HBoxLayout( 0, 0, 6, "ContribLayout"); ContribSlider = new QSlider( Frame5, "ContribSlider" ); ContribSlider->setMinimumSize( QSize( 100, 0 ) ); ContribSlider->setMaxValue( 100 ); ContribSlider->setOrientation( Qt::Horizontal ); ContribLayout->addWidget( ContribSlider ); ContribNumber = new QLCDNumber( Frame5, "ContribNumber" ); ContribNumber->setPaletteBackgroundColor( QColor( 212, 212, 192 ) ); ContribNumber->setNumDigits( 3 ); ContribNumber->setSegmentStyle( QLCDNumber::Flat ); ContribLayout->addWidget( ContribNumber ); Layout19->addLayout( ContribLayout ); ListenLabel = new QLabel( Frame5, "ListenLabel" ); ListenLabel->setText( trUtf8( "listen" ) ); Layout19->addWidget( ListenLabel ); ListenLayout = new Q3HBoxLayout( 0, 0, 6, "ListenLayout"); ListenSlider = new QSlider( Frame5, "ListenSlider" ); ListenSlider->setMinimumSize( QSize( 100, 0 ) ); ListenSlider->setMinValue( -99 ); ListenSlider->setMaxValue( 100 ); ListenSlider->setOrientation( Qt::Horizontal ); ListenLayout->addWidget( ListenSlider ); ListenNumber = new QLCDNumber( Frame5, "ListenNumber" ); ListenNumber->setPaletteBackgroundColor( QColor( 212, 212, 192 ) ); ListenNumber->setNumDigits( 3 ); ListenNumber->setSegmentStyle( QLCDNumber::Flat ); ListenLayout->addWidget( ListenNumber ); Layout19->addLayout( ListenLayout ); Layout20_2->addLayout( Layout19 ); Frame5Layout->addLayout( Layout20_2 ); RandomizeCheck = new QCheckBox( Frame5, "RandomizeCheck" ); RandomizeCheck->setText( trUtf8( "Randomize" ) ); Frame5Layout->addWidget( RandomizeCheck ); Layout27->addWidget( Frame5 ); Frame5_2 = new QFrame( this ); Frame5_2->setFrameShape( QFrame::StyledPanel ); Frame5_2->setFrameShadow( QFrame::Raised ); Frame5_2Layout = new Q3VBoxLayout( Frame5_2, 11, 6, "Frame5_2Layout"); TextLabel3 = new QLabel( Frame5_2, "TextLabel3" ); QFont TextLabel3_font( TextLabel3->font() ); TextLabel3_font.setBold( TRUE ); TextLabel3_font.setUnderline( TRUE ); TextLabel3->setFont( TextLabel3_font ); TextLabel3->setText( trUtf8( "Rhythm Style:" ) ); Frame5_2Layout->addWidget( TextLabel3 ); Layout21 = new Q3VBoxLayout( 0, 0, 2, "Layout21"); ToolButton7 = new QToolButton( Frame5_2, "ToolButton7" ); ToolButton7->setText( trUtf8( "Clear" ) ); Layout21->addWidget( ToolButton7 ); ToolButton8 = new QToolButton( Frame5_2, "ToolButton8" ); ToolButton8->setText( trUtf8( "Open..." ) ); Layout21->addWidget( ToolButton8 ); ToolButton9 = new QToolButton( Frame5_2, "ToolButton9" ); ToolButton9->setText( trUtf8( "Save" ) ); Layout21->addWidget( ToolButton9 ); ToolButton10 = new QToolButton( Frame5_2, "ToolButton10" ); ToolButton10->setText( trUtf8( "Save as..." ) ); Layout21->addWidget( ToolButton10 ); Frame5_2Layout->addLayout( Layout21 ); Layout22 = new Q3VBoxLayout( 0, 0, 2, "Layout22"); ToolButton5 = new QToolButton( Frame5_2, "ToolButton5" ); ToolButton5->setText( trUtf8( "Generate" ) ); Layout22->addWidget( ToolButton5 ); ToolButton6 = new QToolButton( Frame5_2, "ToolButton6" ); ToolButton6->setText( trUtf8( "Close" ) ); Layout22->addWidget( ToolButton6 ); Frame5_2Layout->addLayout( Layout22 ); Layout27->addWidget( Frame5_2 ); RhythmGeneratorLayout->addLayout( Layout27 ); Layout29 = new Q3HBoxLayout( 0, 0, 6, "Layout29"); Frame6_2 = new QFrame( this ); Frame6_2->setMinimumSize( QSize( 200, 150 ) ); Frame6_2->setFrameShape( QFrame::StyledPanel ); Frame6_2->setFrameShadow( QFrame::Raised ); Layout29->addWidget( Frame6_2 ); Frame7 = new QFrame( this ); Frame7->setMinimumSize( QSize( 200, 150 ) ); Frame7->setFrameShape( QFrame::StyledPanel ); Frame7->setFrameShadow( QFrame::Raised ); Layout29->addWidget( Frame7 ); RhythmGeneratorLayout->addLayout( Layout29 ); Frame8 = new QFrame( this ); Frame8->setMinimumSize( QSize( 400, 150 ) ); Frame8->setFrameShape( QFrame::StyledPanel ); Frame8->setFrameShadow( QFrame::Raised ); RhythmGeneratorLayout->addWidget( Frame8 ); // signals and slots connections connect( StepsSlider, SIGNAL( valueChanged(int) ), StepsNumber, SLOT( display(int) ) ); connect( CountSlider, SIGNAL( valueChanged(int) ), CountNumber, SLOT( display(int) ) ); connect( BarsSlider, SIGNAL( valueChanged(int) ), BarsNumber, SLOT( display(int) ) ); connect( ContribSlider, SIGNAL( valueChanged(int) ), ContribNumber, SLOT( display(int) ) ); connect( ListenSlider, SIGNAL( valueChanged(int) ), ListenNumber, SLOT( display(int) ) ); connect( InstrumentDel, SIGNAL( pressed() ), InstrumentList, SLOT( clearSelection() ) ); } /* * Destroys the object and frees any allocated resources */ RhythmGenerator::~RhythmGenerator() { // no need to delete child widgets, Qt does it all for us } #endif } // namespace MusEGui muse-4.0.0/src/muse/mplugins/rhythm.h000066400000000000000000000134121404105311000175340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: rhythm.h,v 1.1.1.1 2003/10/27 18:52:44 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // // This code is an adaption of the random rhythm generator taken // from "The JAZZ++ Midi Sequencer" // Copyright (C) 1994-2000 Andreas Voss and Per Sigmond, all // rights reserved. // Distributed under the GNU General Public License //========================================================= #ifndef __RHYTHM_H__ #define __RHYTHM_H__ #include "ui_rhythmbase.h" #include #define MAX_GROUPS 5 #define MAX_KEYS 20 class QCloseEvent; namespace MusECore { class Xml; } namespace MusEGui { class tTrack; class tEventWin; class tSong; class tBarInfo; #if 0 //--------------------------------------------------------- // tRhyGroup //--------------------------------------------------------- struct tRhyGroup { int contrib; int listen; tRhyGroup() { listen = 0; contrib = 0; } // void write(int, MusECore::Xml&); // void read(MusECore::Xml&); }; //--------------------------------------------------------- // tRhyGroups //--------------------------------------------------------- struct tRhyGroups { tRhyGroup g[MAX_GROUPS]; tRhyGroup& operator [] (int i) { return g[i]; } // void write(int, MusECore::Xml&); // void read(MusECore::Xml&); }; //--------------------------------------------------------- // tRhythm //--------------------------------------------------------- class tRhythm { friend class tRhythmWin; char* label; // tRndArray rhythm; // tRndArray length; // tRndArray veloc; int steps_per_count; int count_per_bar; int n_bars; int keys[MAX_KEYS]; int n_keys; int mode; int parm; int randomize; tRhyGroups groups; // tRndArray history; // set by GenInit() long start_clock; long next_clock; // void GenGroup(tRndArray& out, int grp, tBarInfo &bi, tRhythm *rhy[], int n_rhy); int Clock2i(long clock, tBarInfo &bi) const; int ClocksPerStep(tBarInfo &bi) const; public: tRhythm(int key); tRhythm(const tRhythm &o); tRhythm & operator= (const tRhythm &o); virtual ~tRhythm(); char const * GetLabel() { return label; } void SetLabel(char const *); void Generate(tTrack *track, long fr_clock, long to_clock, long ticks_per_bar); void Generate(tTrack *track, tBarInfo &bi, tRhythm *rhy[], int n_rhy); void GenInit(long start_clock); void GenerateEvent(tTrack *track, long clock, short vel, short len); void write(int, MusECore::Xml&); void read(MusECore::Xml&); }; #endif //--------------------------------------------------------- // RhythmGen //--------------------------------------------------------- class RhythmGen : public QMainWindow, public Ui::RhythmBase { Q_OBJECT #if 0 wxPanel *inst_panel; wxText *label; wxSlider *steps_per_count; wxSlider *count_per_bar; wxSlider *n_bars; wxListBox *instrument_list; wxCheckBox *rand_checkbox; wxPanel *group_panel; wxListBox *group_list; wxSlider *group_contrib; wxSlider *group_listen; int act_group; tArrayEdit *length_edit; tArrayEdit *veloc_edit; tRhyArrayEdit *rhythm_edit; enum { MAX_INSTRUMENTS = 20 }; tRhythm *instruments[MAX_INSTRUMENTS]; int n_instruments; int act_instrument; // -1 if none // this one is edited and copied from/to instruments[i] tRhythm edit; // ignore Updates while creating the window (motif) Bool in_create; // callbacks static void ItemCallback(wxItem& item, wxCommandEvent& event); static void SelectInstr(wxListBox& list, wxCommandEvent& event); static void SelectGroup(wxListBox& list, wxCommandEvent& event); static void Add(wxButton &but, wxCommandEvent& event); static void Del(wxButton &but, wxCommandEvent& event); static void Generate(wxButton &but, wxCommandEvent& event); static void Help(); void Instrument2Win(int i = -1); // instrument[act_instrument] -> win void Win2Instrument(int i = -1); // win -> instrument[act_instrument] void AddInstrumentDlg(); void AddInstrument(tRhythm *r); void DelInstrument(); tEventWin *event_win; tSong *song; void RndEnable(); char *default_filename; int has_changed; wxToolBar *tool_bar; float tb_width, tb_height; void UpInstrument(); void DownInstrument(); void InitInstrumentList(); #endif virtual void closeEvent(QCloseEvent*); signals: void hideWindow(); public: // virtual void OnMenuCommand(int id); // virtual void OnSize(int w, int h); RhythmGen(QWidget* parent = 0, Qt::WindowFlags fo = Qt::Window); virtual ~RhythmGen(); // void OnPaint(); // void GenRhythm(); // bool OnClose(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/mplugins/rhythmbase.ui000066400000000000000000000703501404105311000205610ustar00rootroot00000000000000 RhythmBase 0 0 465 605 MusE: Random Rhythm Generator 0 59 465 546 6 6 Instrument Properties 6 11 0 6 counts/bar false steps/count false # bars false test false true 0 100 0 contrib false randomize true false Group 1 Group 2 Group 3 Group 4 Group 5 1 16 4 1 16 1 listen false Instrument false 1 16 4 -100 100 0 Group false 6 0 create new entry pressing the New button you create a new entry in the MusE list of defined controllers &New true delete selected entry &Delete true Qt::Horizontal QSizePolicy::Expanding 20 20 Up Down 0 0 0 150 32767 150 list of defined controllers This is the MusE list of defined controllers. QFrame::StyledPanel QFrame::Sunken Instrument true true Group true true steps/count true true counts/bar true true # bars true true contrib true true listen true true 0 0 Qt::Vertical 0 0 Qt::Horizontal 0 0 50 50 QFrame::StyledPanel QFrame::Raised 0 0 50 50 QFrame::StyledPanel QFrame::Raised 0 0 0 150 QFrame::StyledPanel QFrame::Raised 64 26 250 90 <b>Notice!</b><br> Random Rhythm Generator is not enabled yet! false 0 0 111 38 Tools 0 0 465 21 &File &Edit &Help image0image0 &New New Ctrl+N fileNewAction image1image1 &Open... Open Ctrl+O fileOpenAction image2image2 &Save Save Ctrl+S fileSaveAction Save &As... Save As fileSaveAsAction image3image3 &Print... Print Ctrl+P filePrintAction E&xit Exit fileExitAction image4image4 &Undo Undo Ctrl+Z editUndoAction image5image5 &Redo Redo Ctrl+Y editRedoAction image6image6 &Cut Cut Ctrl+X editCutAction image7image7 C&opy Copy Ctrl+C editCopyAction image8image8 &Paste Paste Ctrl+V editPasteAction image9image9 &Find... Find Ctrl+F editFindAction &Contents... Contents helpContentsAction &Index... Index helpIndexAction &About... About helpAboutAction fileNewAction activated() RhythmBase fileNew() -1 -1 20 20 fileOpenAction activated() RhythmBase fileOpen() -1 -1 20 20 fileSaveAction activated() RhythmBase fileSave() -1 -1 20 20 fileSaveAsAction activated() RhythmBase fileSaveAs() -1 -1 20 20 filePrintAction activated() RhythmBase filePrint() -1 -1 20 20 fileExitAction activated() RhythmBase fileExit() -1 -1 20 20 editUndoAction activated() RhythmBase editUndo() -1 -1 20 20 editRedoAction activated() RhythmBase editRedo() -1 -1 20 20 editCutAction activated() RhythmBase editCut() -1 -1 20 20 editCopyAction activated() RhythmBase editCopy() -1 -1 20 20 editPasteAction activated() RhythmBase editPaste() -1 -1 20 20 editFindAction activated() RhythmBase editFind() -1 -1 20 20 helpIndexAction activated() RhythmBase helpIndex() -1 -1 20 20 helpContentsAction activated() RhythmBase helpContents() -1 -1 20 20 helpAboutAction activated() RhythmBase helpAbout() -1 -1 20 20 muse-4.0.0/src/muse/mplugins/transformbase.ui000066400000000000000000000727711404105311000212720ustar00rootroot00000000000000 MidiTransformDialogBase 0 0 885 562 MusE: Midi Transformator true 11 11 11 11 6 6 0 0 0 0 &New true &Delete true Qt::Horizontal QSizePolicy::Expanding 20 20 &Apply true &OK true true &Cancel true 6 0 0 0 0 PresetList false 0 0 Processing 11 11 11 11 6 Event Type false Keep Fix false Note Poly Pressure Control Change Aftertouch Pitch Bend NRPN RPN Keep Plus Minus Multiply Divide Fix Value 2 Invert ScaleMap Flip Dyn Random Value 1 false Value 2 false Length false Position false Keep Plus Minus Multiply Divide Fix Value 1 Invert Dyn Random Keep Plus Minus Multiply Divide Fix 9999 9999 99999999.000000000000000 Keep Plus Minus Multiply Divide Randomize 99999999.000000000000000 99999999.000000000000000 99999999.000000000000000 Filter 11 11 11 11 6 All Equal Unequal false Note Poly Pressure Control Change Aftertouch Pitch Bend NRPN RPN Value 2 false Value 1 false Event Type false Ignore Equal Unequal Higher Lower Inside Outside Length false Bar Range false Ignore Equal Unequal Higher Lower Inside Outside Ignore Equal Unequal Higher Lower Inside Outside Ignore Equal Unequal Higher Lower Inside Outside 127 127 127 127 127 127 3 3 1000.000000000000000 6 0 0 0 0 Preset 6 11 11 11 11 6 0 0 0 0 Name: false Comment: false Range 6 11 11 11 11 process all events selected tracks inside loop Function 6 11 11 11 11 Select Quantize Delete Transform Insert Copy Extract Quantize Value false MusEGui::SpinBoxFP QDoubleSpinBox
spinboxFP.h
MusEGui::ComboQuant QComboBox
comboQuant.h
selEventOp selType selVal1Op selVal1a selVal1b selVal2Op selVal2a selVal2b selLenOp selLenA selLenB selRangeOp procEventOp procType procVal1Op procVal1b procVal2Op procVal2b procLenOp procPosOp nameEntry commentEntry processAll selectedTracks insideLoop funcOp presetList buttonNew buttonDelete buttonApply buttonOk buttonCancel buttonOk clicked() MidiTransformDialogBase accept() 20 20 20 20 buttonCancel clicked() MidiTransformDialogBase reject() 20 20 20 20
muse-4.0.0/src/muse/mtc.cpp000066400000000000000000000100301404105311000154720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mtc.cpp,v 1.1.1.1 2003/10/27 18:51:48 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "mtc.h" #include "sync.h" #include namespace MusEGlobal { extern int mtcType; } namespace MusECore { //--------------------------------------------------------- // MTC::timeUS // converts MTC Time to microseconds according to // global mtcType //--------------------------------------------------------- uint64_t MTC::timeUS(int type) const { uint64_t time = 1000000UL * (_h * 3600UL + _m * 60UL + _s); uint64_t f = 10000UL * (_f * 100UL + _sf); if(type == -1) type = MusEGlobal::mtcType; switch (type) { case 0: // 24 frames sec time += f / 24UL; break; case 1: // 25 time += f / 25UL; break; case 2: // 30 drop frame TODO case 3: // 30 non drop frame default: time += f / 30UL; break; } return time; } //--------------------------------------------------------- // MTC //--------------------------------------------------------- MTC::MTC(double t, int type) { _h = int(t/3600); t -= _h * 3600; _m = int(t/60); t -= _m * 60; _s = int(t); t -= _s; double ft = 1.0/24.0; if(type == -1) type = MusEGlobal::mtcType; switch (type) { case 0: // 24 frames sec ft = 1.0/24.0; break; case 1: // 25 ft = 0.04; break; case 2: // 30 drop frame case 3: // 30 non drop frame default: ft = 1.0/30.0; break; } double frames = t / ft; _f = int(frames); frames -= _f; _sf = int(frames * 100); } //--------------------------------------------------------- // incQuarter // increment MTC time one quarter frame time //--------------------------------------------------------- void MTC::incQuarter(int type) { int frames = 24; if(type == -1) type = MusEGlobal::mtcType; switch (type) { case 0: frames = 24; break; case 1: frames = 25; break; case 2: case 3: default: frames = 30; break; } _sf += 25; if (_sf >= 100) { ++_f; _sf -= 100; } if (_f == frames) { ++_s; _f = 0; } if (_s == 60) { ++_m; _s = 0; } if (_m == 60) { ++_h; _m = 0; } if (_h == 24) { _h = 0; } } //--------------------------------------------------------- // print //--------------------------------------------------------- void MTC::print() const { printf("%02d:%02d:%02d:%02d:%02d", _h, _m, _s, _f, _sf); } } // namespace MusECore muse-4.0.0/src/muse/mtc.h000066400000000000000000000043211404105311000151450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mtc.h,v 1.1.1.1 2003/10/27 18:51:25 wschweer Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MTC_H__ #define __MTC_H__ #include namespace MusECore { //--------------------------------------------------------- // MTC //--------------------------------------------------------- class MTC { unsigned char _h, _m, _s, _f, _sf; public: MTC(int h, int m, int s, int f, int sf=0) { _h = h; _m = m; _s = s; _f = f; _sf = sf; } MTC() { _h = _m = _s = _f = _sf = 0; } MTC(double, int type = -1); void set(int h, int m, int s, int f, int sf=0) { _h = h; _m = m; _s = s; _f = f; _sf = sf; } void incQuarter(int type = -1); void setH(int val) { _h = val; } void setM(int val) { _m = val; } void setS(int val) { _s = val; } void setF(int val) { _f = val; } void setSf(int val) { _sf = val; } int h() const { return _h; } int m() const { return _m; } int s() const { return _s; } int f() const { return _f; } int sf() const { return _sf; } uint64_t timeUS(int type = -1) const; void print() const; }; } // namespace MusECore #endif muse-4.0.0/src/muse/muse_atomic.h000066400000000000000000000046431404105311000166760ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MUSE_ATOMIC_H__ #define __MUSE_ATOMIC_H__ namespace MusECore { // FIXME: Compiler knows this define but where is it? Is compiler recognizing it? __i386__ and __i386 work. // danvd: change i386 to more common __i386__ though boths are known to gcc and clang // danvd: replace pthreads calls with builtin atomic operations // danvd: turn on asm atomic operations for both i386 and x86_64 typedef struct { int counter; } muse_atomic_t; static inline int muse_atomic_read(muse_atomic_t *v) { #ifndef __i386__ return __sync_fetch_and_add(&v->counter, 0); #else return v->counter; #endif } static inline void muse_atomic_set(muse_atomic_t *v, int i) { #ifndef __i386__ __sync_val_compare_and_swap(&v->counter, v->counter, i); #else v->counter = i; #endif } static inline void muse_atomic_inc(muse_atomic_t *v) { #if !defined(__i386__) && !defined(__x86_64__) __sync_fetch_and_add(&v->counter, 1); #else __asm__ __volatile__( "lock ; " "incl %0" :"=m" (v->counter) :"m" (v->counter)); #endif } static inline void muse_atomic_dec(muse_atomic_t *v) { #if !defined(__i386__) && !defined(__x86_64__) __sync_fetch_and_sub(&v->counter, 1); #else __asm__ __volatile__( "lock ; " "decl %0" :"=m" (v->counter) :"m" (v->counter)); #endif } static inline void muse_atomic_init(muse_atomic_t*) {} static inline void muse_atomic_destroy(muse_atomic_t*) {} } // namespace MusECore #endif muse-4.0.0/src/muse/muse_math.h000066400000000000000000000035701404105311000163510ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // muse_math.h // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MUSE_MATH_H__ #define __MUSE_MATH_H__ #include #include "config.h" // FIXME See top level cmake. On MingW it said M_PI exists but here M_PI was not found! // So just look for the define for now... // #ifndef HAVE_M_PI #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef HAVE_EXP10 #define exp10(x) (pow(10.0, x)) #endif #ifndef HAVE_EXP10F #define exp10f(x) (powf(10.0f, x)) #endif #ifndef HAVE_EXP10L #define exp10l(x) (powl(10.0l, x)) #endif // Convert value to log10, rounding to nearest .000001 due to inaccuracy with log. #define muse_log10r(x) (round(log10(x) * 1000000.0) * 0.000001) // Convert value to dB, rounding to nearest .000001 due to inaccuracy with log. #define muse_val2dbr(x) (round(log10(x) * 20000000.0) * 0.000001) // Convert dB to val. #define muse_db2val(x) pow(10, (x * 0.05)) // Round to the nearest .000001 #define muse_round2micro(x) (round(x * 1000000.0) * 0.000001) #endif muse-4.0.0/src/muse/muse_time.cpp000066400000000000000000000017321404105311000167070ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // muse_time.cpp // Copyright (C) 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= muse-4.0.0/src/muse/muse_time.h000066400000000000000000000022561404105311000163560ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // muse_time.h // Copyright (C) 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MUSE_TIME_H__ #define __MUSE_TIME_H__ #include namespace MusECore { typedef int64_t MuseFrame_t; typedef int64_t MuseCount_t; typedef int64_t MuseTick_t; } // namespace MusECore #endif muse-4.0.0/src/muse/name_factory.cpp000066400000000000000000000071441404105311000173720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // tracks_duplicate.cpp // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "name_factory.h" #include "song.h" namespace MusECore { NameFactoryBase::NameFactoryBase() : QStringList() { } TrackNameFactory::TrackNameFactory() : NameFactoryBase() { } TrackNameFactory::TrackNameFactory(Track::TrackType type, QString base, int copies) : NameFactoryBase() { genUniqueNames(type, base, copies); } //--------------------------------------------------------- // genUniqueNames //--------------------------------------------------------- bool TrackNameFactory::genUniqueNames(Track::TrackType type, QString base, int copies) { clear(); int numberIndex=0; int num_base = 1; if(base.isEmpty()) { switch(type) { case Track::MIDI: case Track::DRUM: case Track::WAVE: base = QString("Track"); break; case Track::AUDIO_OUTPUT: base = QString("Out"); break; case Track::AUDIO_GROUP: base = QString("Group"); break; case Track::AUDIO_AUX: base = QString("Aux"); break; case Track::AUDIO_INPUT: base = QString("Input"); break; case Track::AUDIO_SOFTSYNTH: base = QString("Synth"); break; }; base += " "; } else { // assign new names to copied tracks. numberIndex = base.lastIndexOf("#"); // according to Qt doc for lastIndexOf it should return -1 when not found // apparently it returns str_size+1 ?! Let's catch both if (numberIndex == -1 || numberIndex > base.size()) { num_base = 2; base += " #"; } else { bool ok; num_base = base.right(base.size() - numberIndex - 1).toInt(&ok); if(ok) { // The characters to the right of the '#' converted successfully to a number. // Increment the number, and truncate the string to remove the existing number. ++num_base; base.truncate(numberIndex + 1); } else { // The characters to the right of the '#' did not convert to a number. // So leave it alone and append a new '#' and number. num_base = 2; base += " #"; } } } for(int cp = 0; cp < copies; ++cp) { for (int i = num_base; true; ++i) { const QString s = base + QString::number(i); const Track* track = MusEGlobal::song->findTrack(s); if(track == 0) { if(_usedNames.indexOf(s) == -1) { _usedNames.append(s); append(s); break; } } } } return true; } } // namespace MusECore muse-4.0.0/src/muse/name_factory.h000066400000000000000000000043451404105311000170370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // name_factory.h // (C) Copyright 2020 Tim E. Real (terminator356 on sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __NAME_FACTORY_H__ #define __NAME_FACTORY_H__ #include #include #include "track.h" namespace MusECore { //--------------------------------- // NameFactoryBase //--------------------------------- class NameFactoryBase : public QStringList { protected: QStringList _usedNames; public: NameFactoryBase(); }; //---------------------------------------------------------------------------- // TrackNameFactory // This convenience class generates unique track names from the given type // and base name, for a given number of copies. // To look for existing track names, the song's track list is searched first, // followed by searching an internal accumulated 'used names' list. // This is useful for creating multiple tracks BEFORE they are added to // the song's track list. Each generated name is added to the 'used names' list. //---------------------------------------------------------------------------- class TrackNameFactory : public NameFactoryBase { public: TrackNameFactory(); TrackNameFactory(Track::TrackType type, QString base = QString(), int copies = 1); // Returns true on success. bool genUniqueNames( Track::TrackType type, QString base = QString(), int copies = 1); }; } // namespace MusECore #endif muse-4.0.0/src/muse/node.cpp000066400000000000000000002375641404105311000156630ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: node.cpp,v 1.36.2.25 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2013 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include #include #include #include "globals.h" #include "gconfig.h" #include "song.h" #include "xml.h" #include "plugin.h" #include "synth.h" #include "audiodev.h" #include "midiport.h" #include "audio.h" #include "wave.h" #include "utils.h" //debug #include "ticksynth.h" // metronome #include "wavepreview.h" #include "al/dsp.h" #include "latency_compensator.h" // REMOVE Tim. Persistent routes. Added. Make this permanent later if it works OK and makes good sense. #define _USE_SIMPLIFIED_SOLO_CHAIN_ // Turn on debugging messages //#define NODE_DEBUG // Turn on constant flow of process debugging messages //#define NODE_DEBUG_PROCESS // Turn on some cool terminal 'peak' meters for debugging // presence of actual audio at various places //#define NODE_DEBUG_TERMINAL_PEAK_METERS //#define FIFO_DEBUG //#define METRONOME_DEBUG namespace MusECore { //--------------------------------------------------------- // setSolo //--------------------------------------------------------- void MidiTrack::setSolo(bool val) { if(_solo != val) { _solo = val; updateSoloStates(false); } } void AudioTrack::setSolo(bool val) { if(_solo != val) { _solo = val; updateSoloStates(false); } if (isMute()) resetMeter(); } //--------------------------------------------------------- // setInternalSolo //--------------------------------------------------------- void Track::setInternalSolo(unsigned int val) { _internalSolo = val; } //--------------------------------------------------------- // clearSoloRefCounts // This is a static member function. Required for outside access. // Clears the internal static reference counts. //--------------------------------------------------------- void Track::clearSoloRefCounts() { _soloRefCnt = 0; } //--------------------------------------------------------- // updateSoloState //--------------------------------------------------------- void Track::updateSoloState() { if(_solo) _soloRefCnt++; else if(_soloRefCnt && !_tmpSoloChainNoDec) _soloRefCnt--; } //--------------------------------------------------------- // updateInternalSoloStates //--------------------------------------------------------- void Track::updateInternalSoloStates() { if(_tmpSoloChainTrack->solo()) { _internalSolo++; _soloRefCnt++; } else if(!_tmpSoloChainNoDec) { if(_internalSolo) _internalSolo--; if(_soloRefCnt) _soloRefCnt--; } } //--------------------------------------------------------- // updateInternalSoloStates //--------------------------------------------------------- void MidiTrack::updateInternalSoloStates() { if(_nodeTraversed) // Anti circular mechanism. { fprintf(stderr, "MidiTrack::updateInternalSoloStates %s :\n MusE Warning: Please check your routes: Circular path found!\n", name().toLatin1().constData()); return; } //if(this == _tmpSoloChainTrack) // return; _nodeTraversed = true; Track::updateInternalSoloStates(); _nodeTraversed = false; // Reset. } //--------------------------------------------------------- // updateInternalSoloStates //--------------------------------------------------------- void AudioTrack::updateInternalSoloStates() { if(_nodeTraversed) // Anti circular mechanism. { fprintf(stderr, "AudioTrack::updateInternalSoloStates %s :\n MusE Warning: Please check your routes: Circular path found!\n", name().toLatin1().constData()); return; } //if(this == _tmpSoloChainTrack) // return; _nodeTraversed = true; Track::updateInternalSoloStates(); if(_tmpSoloChainDoIns) { if(type() == AUDIO_SOFTSYNTH) { const MusECore::MidiTrackList* ml = MusEGlobal::song->midis(); for(MusECore::ciMidiTrack im = ml->begin(); im != ml->end(); ++im) { MusECore::MidiTrack* mt = *im; if(mt->outPort() >= 0 && mt->outPort() == ((SynthI*)this)->midiPort()) mt->updateInternalSoloStates(); } } const RouteList* rl = inRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); #ifndef _USE_SIMPLIFIED_SOLO_CHAIN_ else // Support Midi Port -> Audio Input solo chains. if(ir->type == Route::MIDI_PORT_ROUTE) { const MidiTrackList* ml = MusEGlobal::song->midis(); for(ciMidiTrack im = ml->begin(); im != ml->end(); ++im) { MidiTrack* mt = *im; if(mt->outPort() == ir->midiPort && ((1 << mt->outChannel()) & ir->channel) ) mt->updateInternalSoloStates(); } } #endif } } else { const RouteList* rl = outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); } } _nodeTraversed = false; // Reset. } //--------------------------------------------------------- // updateSoloStates //--------------------------------------------------------- void MidiTrack::updateSoloStates(bool noDec) { if(noDec && !_solo) return; _nodeTraversed = true; // Anti circular mechanism. _tmpSoloChainTrack = this; _tmpSoloChainDoIns = false; _tmpSoloChainNoDec = noDec; updateSoloState(); if(outPort() >= 0) { MidiPort* mp = &MusEGlobal::midiPorts[outPort()]; MidiDevice *md = mp->device(); if(md && md->isSynti()) ((SynthI*)md)->updateInternalSoloStates(); } #ifdef _USE_SIMPLIFIED_SOLO_CHAIN_ // Support Midi Track to Audio Input track soloing chain routes. // Support omni routes only, because if channels are supported, the graphical router becomes more complicated. const RouteList* rl = outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == Route::TRACK_ROUTE && ir->track && ir->track->type() == Track::AUDIO_INPUT && ir->channel == -1) ir->track->updateInternalSoloStates(); } #else // Support Midi Port -> Audio Input solo chains. const int chbits = 1 << outChannel(); const RouteList* rl = mp->outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == Route::TRACK_ROUTE && ir->track && ir->track->type() == Track::AUDIO_INPUT && (ir->channel & chbits) ) ir->track->updateInternalSoloStates(); } #endif _nodeTraversed = false; // Reset. } //--------------------------------------------------------- // updateSoloStates //--------------------------------------------------------- void AudioTrack::updateSoloStates(bool noDec) { if(noDec && !_solo) return; _nodeTraversed = true; // Anti circular mechanism. _tmpSoloChainTrack = this; _tmpSoloChainNoDec = noDec; updateSoloState(); _tmpSoloChainDoIns = true; if(type() == AUDIO_SOFTSYNTH) { const MidiTrackList* ml = MusEGlobal::song->midis(); for(ciMidiTrack im = ml->begin(); im != ml->end(); ++im) { MidiTrack* mt = *im; if(mt->outPort() >= 0 && mt->outPort() == ((SynthI*)this)->midiPort()) mt->updateInternalSoloStates(); } } const RouteList* rl = inRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); #ifndef _USE_SIMPLIFIED_SOLO_CHAIN_ else // Support Midi Port -> Audio Input solo chains. if(ir->type == Route::MIDI_PORT_ROUTE) { const MidiTrackList* ml = MusEGlobal::song->midis(); for(ciMidiTrack im = ml->begin(); im != ml->end(); ++im) { MidiTrack* mt = *im; if(mt->outPort() == ir->midiPort && ((1 << mt->outChannel()) & ir->channel) ) mt->updateInternalSoloStates(); } } #endif } _tmpSoloChainDoIns = false; { const RouteList* rl = outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type == Route::TRACK_ROUTE) ir->track->updateInternalSoloStates(); } } _nodeTraversed = false; // Reset. } void AudioTrack::preProcessAlways() { _processed = false; Track::preProcessAlways(); } //--------------------------------------------------------- // processTrackCtrls // If trackChans is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- void AudioTrack::processTrackCtrls(unsigned pos, int trackChans, unsigned nframes, float** buffer) { const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); const unsigned long min_per = (MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; unsigned long sample = 0; const AutomationType at = automationType(); const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; CtrlListList* cll = controller(); CtrlList* vol_ctrl = 0; CtrlList* pan_ctrl = 0; { ciCtrlList icl = cll->find(AC_VOLUME); if(icl == cll->end()) return; vol_ctrl = icl->second; icl = cll->find(AC_PAN); if(icl == cll->end()) return; pan_ctrl = icl->second; } int cur_slice = 0; while(sample < nframes) { unsigned long nsamp = nframes - sample; const unsigned long slice_frame = pos + sample; // Process automation control values, while also determining the maximum acceptable // size of this run. Further processing, from FIFOs for example, can lower the size // from there, but this section determines where the next highest maximum frame // absolutely needs to be for smooth playback of the controller value stream... // if(trackChans != 0 && !_prefader) // Don't bother if we don't want to run, or prefader is on. { ciCtrlList icl = cll->begin(); for(unsigned long k = 0; k < _controlPorts; ++k) { CtrlList* cl = (icl != cll->end() ? icl->second : NULL); CtrlInterpolate& ci = _controls[k].interp; // Always refresh the interpolate struct at first, since things may have changed. // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && (slice_frame < (unsigned long)ci.sFrame || (ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame)) ) ) { if(cl && (unsigned long)cl->id() == k) { cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); if(icl != cll->end()) ++icl; } else { // No matching controller, or end. Just copy the current value into the interpolator. // Keep the current icl iterator, because since they are sorted by frames, // if the IDs didn't match it means we can just let k catch up with icl. ci.sFrame = 0; ci.eFrame = 0; ci.eFrameValid = false; ci.sVal = _controls[k].dval; ci.eVal = ci.sVal; ci.doInterp = false; ci.eStop = false; } } else { if(ci.eStop && ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. { // Clear the stop condition and set up the interp struct appropriately as an endless value. ci.sFrame = 0; //ci->eFrame; ci.eFrame = 0; ci.eFrameValid = false; ci.sVal = ci.eVal; ci.doInterp = false; ci.eStop = false; } if(icl != cll->end()) ++icl; } if(MusEGlobal::audio->isPlaying()) { unsigned long samps = nsamp; if(ci.eFrameValid) samps = (unsigned long)ci.eFrame - slice_frame; if(samps < nsamp) nsamp = samps; } #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "AudioTrack::processTrackCtrls k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, slice_frame, ci.eFrame, nsamp); #endif } } #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "AudioTrack::processTrackCtrls sample:%lu nsamp:%lu\n", sample, nsamp); #endif // // Process all control ring buffer items valid for this time period... // bool found = false; unsigned long frame = 0; unsigned long evframe; while(!_controlFifo.isEmpty()) { const ControlEvent& v = _controlFifo.peek(); // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; // Protection. Observed this condition. Why? Supposed to be linear timestamps. if(found && evframe < frame) { fprintf(stderr, "AudioTrack::processTrackCtrls *** Error: Event out of order: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d syncFrame:%lu nframes:%u v.frame:%lu\n", evframe, frame, v.idx, v.value, v.unique, syncFrame, nframes, v.frame); // No choice but to ignore it. _controlFifo.remove(); // Done with the ring buffer's item. Remove it. continue; } if(evframe >= nframes // Next events are for a later period. || (!found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) || (found && !v.unique && (evframe - sample >= min_per))) // Eat up events within minimum slice - they're too close. break; // _controlFifo.remove(); // Done with the ring buffer's item. Remove it. if(v.idx >= _controlPorts) // Sanity check { _controlFifo.remove(); // Done with the ring buffer's item. Remove it. break; } found = true; frame = evframe; if(trackChans != 0 && !_prefader) // Only if we want to run, and prefader is off. { CtrlInterpolate* ci = &_controls[v.idx].interp; ci->eFrame = frame; ci->eFrameValid = true; ci->eVal = v.value; ci->eStop = true; } // Need to update the automation value, otherwise it overwrites later with the last automation value. setPluginCtrlVal(v.idx, v.value); _controlFifo.remove(); // Done with the ring buffer's item. Remove it. } if(found && trackChans != 0 && !_prefader) // If a control FIFO item was found, takes priority over automation controller stream. nsamp = frame - sample; if(sample + nsamp > nframes) // Safety check. nsamp = nframes - sample; // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. // Note this means it is still possible to get stuck in the top loop (at least for a while). if(nsamp != 0) { if(trackChans != 0 && !_prefader) { const CtrlInterpolate& vol_interp = _controls[AC_VOLUME].interp; const CtrlInterpolate& pan_interp = _controls[AC_PAN].interp; unsigned long k; //const float up_fact = 1.002711275; // 3.01.. dB / 256 //const float down_fact = 0.997296056; const double up_fact = 1.003471749; // 3.01.. dB / 200 const double down_fact = 0.996540262; float *sp1, *sp2, *dp1, *dp2; sp1 = sp2 = dp1 = dp2 = NULL; double _volume, v, _pan, v1, v2; if(trackChans == 1) { sp1 = sp2 = buffer[0] + sample; dp1 = outBuffersExtraMix[0] + sample; dp2 = outBuffersExtraMix[1] + sample; } else { sp1 = buffer[0] + sample; sp2 = buffer[1] + sample; dp1 = outBuffers[0] + sample; dp2 = outBuffers[1] + sample; } if(trackChans != 2) { const int start_ch = trackChans == 1 ? 0 : 2; k = 0; if(vol_interp.doInterp && MusEGlobal::audio->isPlaying()) { for( ; k < nsamp; ++k) { _volume = vol_ctrl->interpolate(slice_frame + k, vol_interp); v = _volume * _gain; if(v > _curVolume) { if(_curVolume == 0.0) _curVolume = 0.001; // Kick-start it from zero at -30dB. _curVolume *= up_fact; if(_curVolume >= v) _curVolume = v; } else if(v < _curVolume) { _curVolume *= down_fact; if(_curVolume <= v || _curVolume <= 0.001) // Or if less than -30dB. _curVolume = v; } const unsigned long smp = sample + k; for(int ch = start_ch; ch < trackChans; ++ch) *(outBuffers[ch] + smp) = *(buffer[ch] + smp) * _curVolume; } _controls[AC_VOLUME].dval = _volume; // Update the port. } else { if(vol_interp.doInterp) // And not playing... _volume = vol_ctrl->interpolate(pos, vol_interp); else _volume = vol_interp.sVal; _controls[AC_VOLUME].dval = _volume; // Update the port. v = _volume * _gain; if(v > _curVolume) { //fprintf(stderr, "A %f %f\n", v, _curVolume); if(_curVolume == 0.0) _curVolume = 0.001; // Kick-start it from zero at -30dB. for( ; k < nsamp; ++k) { _curVolume *= up_fact; if(_curVolume >= v) { _curVolume = v; break; } const unsigned long smp = sample + k; for(int ch = start_ch; ch < trackChans; ++ch) *(outBuffers[ch] + smp) = *(buffer[ch] + smp) * _curVolume; } } else if(v < _curVolume) { //fprintf(stderr, "B %f %f\n", v, _curVolume); for( ; k < nsamp; ++k) { _curVolume *= down_fact; if(_curVolume <= v || _curVolume <= 0.001) // Or if less than -30dB. { _curVolume = v; break; } const unsigned long smp = sample + k; for(int ch = start_ch; ch < trackChans; ++ch) *(outBuffers[ch] + smp) = *(buffer[ch] + smp) * _curVolume; } } const unsigned long next_smp = sample + nsamp; float* o_buf_p; const float* buf_p; for(int ch = start_ch; ch < trackChans; ++ch) { o_buf_p = outBuffers[ch]; buf_p = buffer[ch]; for(unsigned long smp = sample + k; smp < next_smp; ++smp) o_buf_p[smp] = buf_p[smp] * _curVolume; } } } k = 0; if((vol_interp.doInterp || pan_interp.doInterp) && MusEGlobal::audio->isPlaying()) { for( ; k < nsamp; ++k) { _volume = vol_ctrl->interpolate(slice_frame + k, vol_interp); v = _volume * _gain; _pan = pan_ctrl->interpolate(slice_frame + k, pan_interp); v1 = v * (1.0 - _pan); v2 = v * (1.0 + _pan); if(v1 > _curVol1) { //fprintf(stderr, "C %f %f \n", v1, _curVol1); if(_curVol1 == 0.0) _curVol1 = 0.001; // Kick-start it from zero at -30dB. _curVol1 *= up_fact; if(_curVol1 >= v1) _curVol1 = v1; } else if(v1 < _curVol1) { //fprintf(stderr, "D %f %f \n", v1, _curVol1); _curVol1 *= down_fact; if(_curVol1 <= v1 || _curVol1 <= 0.001) // Or if less than -30dB. _curVol1 = v1; } *dp1++ = *sp1++ * _curVol1; if(v2 > _curVol2) { //fprintf(stderr, "E %f %f \n", v2, _curVol2); if(_curVol2 == 0.0) _curVol2 = 0.001; // Kick-start it from zero at -30dB. _curVol2 *= up_fact; if(_curVol2 >= v2) _curVol2 = v2; } else if(v2 < _curVol2) { //fprintf(stderr, "F %f %f \n", v2, _curVol2); _curVol2 *= down_fact; if(_curVol2 <= v2 || _curVol2 <= 0.001) // Or if less than -30dB. _curVol2 = v2; } *dp2++ = *sp2++ * _curVol2; } _controls[AC_VOLUME].dval = _volume; // Update the ports. _controls[AC_PAN].dval = _pan; } else { if(vol_interp.doInterp) // And not playing... _volume = vol_ctrl->interpolate(pos, vol_interp); else _volume = vol_interp.sVal; if(pan_interp.doInterp) // And not playing... _pan = pan_ctrl->interpolate(pos, pan_interp); else _pan = pan_interp.sVal; _controls[AC_VOLUME].dval = _volume; // Update the ports. _controls[AC_PAN].dval = _pan; v = _volume * _gain; v1 = v * (1.0 - _pan); v2 = v * (1.0 + _pan); if(v1 > _curVol1) { //fprintf(stderr, "C %f %f \n", v1, _curVol1); if(_curVol1 == 0.0) _curVol1 = 0.001; // Kick-start it from zero at -30dB. for( ; k < nsamp; ++k) { _curVol1 *= up_fact; if(_curVol1 >= v1) { _curVol1 = v1; break; } *dp1++ = *sp1++ * _curVol1; } } else if(v1 < _curVol1) { //fprintf(stderr, "D %f %f \n", v1, _curVol1); for( ; k < nsamp; ++k) { _curVol1 *= down_fact; if(_curVol1 <= v1 || _curVol1 <= 0.001) // Or if less than -30dB. { _curVol1 = v1; break; } *dp1++ = *sp1++ * _curVol1; } } for( ; k < nsamp; ++k) *dp1++ = *sp1++ * _curVol1; k = 0; if(v2 > _curVol2) { //fprintf(stderr, "E %f %f \n", v2, _curVol2); if(_curVol2 == 0.0) _curVol2 = 0.001; // Kick-start it from zero at -30dB. for( ; k < nsamp; ++k) { _curVol2 *= up_fact; if(_curVol2 >= v2) { _curVol2 = v2; break; } *dp2++ = *sp2++ * _curVol2; } } else if(v2 < _curVol2) { //fprintf(stderr, "F %f %f \n", v2, _curVol2); for( ; k < nsamp; ++k) { _curVol2 *= down_fact; if(_curVol2 <= v2 || _curVol2 <= 0.001) // Or if less than -30dB. { _curVol2 = v2; break; } *dp2++ = *sp2++ * _curVol2; } } for( ; k < nsamp; ++k) *dp2++ = *sp2++ * _curVol2; } } #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "AudioTrack::processTrackCtrls end of sample:%lu nsamp:%lu\n", sample, nsamp); #endif sample += nsamp; } #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "AudioTrack::processTrackCtrls end of cur_slice:%d\n", cur_slice); #endif ++cur_slice; // Slice is done. Moving on to any next slice now... } } //--------------------------------------------------------- // copyData //--------------------------------------------------------- void AudioTrack::copyData(unsigned pos, int dstStartChan, int requestedDstChannels, int availDstChannels, int srcStartChan, int srcChannels, unsigned nframes, float** dstBuffer, bool add, const bool* addArray) { //Changed by T356. 12/12/09. // Overhaul and streamline to eliminate multiple processing during one process loop. // Was causing ticking sound with synths + multiple out routes because synths were being processed multiple times. // Make better use of AudioTrack::outBuffers as a post-effect pre-volume cache system for multiple calls here during processing. // Previously only WaveTrack used them. (Changed WaveTrack as well). #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "MusE: AudioTrack::copyData name:%s processed:%d _haveData:%d\n", name().toLatin1().constData(), processed(), _haveData); #endif if(srcStartChan == -1) srcStartChan = 0; if(dstStartChan == -1) dstStartChan = 0; // Only the destination knows how many destination channels there are, // while only the source (this track) knows how many source channels there are. // So take care of the source channels here, and let the caller handle the destination channels. const int trackChans = channels(); const int srcTotalOutChans = totalProcessBuffers(); const int requestedSrcChans = (srcChannels == -1) ? srcTotalOutChans : srcChannels; int availableSrcChans = requestedSrcChans; // Force a source range to fit actual available total out channels. if((srcStartChan + availableSrcChans) > srcTotalOutChans) availableSrcChans = srcTotalOutChans - srcStartChan; // Special consideration for metronome: It is not part of the track list, // and it has no in or out routes, yet multiple output tracks may call addData on it! // We can't tell how many output tracks call it, so we can only assume there might be more than one. // Not strictly necessary here because only addData is ever called, but just to be consistent... int i; // Protection for pre-allocated _dataBuffers. if(nframes > MusEGlobal::segmentSize) { fprintf(stderr, "MusE: Error: AudioTrack::copyData: nframes:%u > segmentSize:%u\n", nframes, MusEGlobal::segmentSize); nframes = MusEGlobal::segmentSize; } float* buffer[srcTotalOutChans]; double meter[trackChans]; #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "MusE: AudioTrack::copyData " "trackChans:%d srcTotalOutChans:%d srcStartChan:%d srcChannels:%d " "dstStartChan:%d" "requestedSrcChans:%d availableSrcChans:%d " "requestedDstChannels:%d availDstChannels:%d\n", trackChans, srcTotalOutChans, srcStartChan, srcChannels, dstStartChan, requestedSrcChans, availableSrcChans, requestedDstChannels, availDstChannels); #endif // Have we been here already during this process cycle? if(processed()) { // Is there already some data gathered from a previous call during this process cycle? if(_haveData) { if(requestedSrcChans == 1 && requestedDstChannels >= 2) { const int cnt = availDstChannels > 2 ? 2 : availDstChannels; int c = 0; if(availableSrcChans >= 1) { for( ; c < cnt; ++c) { float* sp; if(!_prefader && srcStartChan == 0 && trackChans == 1) sp = outBuffersExtraMix[c]; // Use the pre-panned mono-to-stereo extra buffers. else sp = outBuffers[srcStartChan]; // In all other cases use the main buffers. float* dp = dstBuffer[c + dstStartChan]; if(dp) { if(addArray ? addArray[c + dstStartChan] : add) { for(unsigned k = 0; k < nframes; ++k) *dp++ += *sp++; } else AL::dsp->cpy(dp, sp, nframes); } } } // Zero the rest of the supplied buffers. for(i = dstStartChan + c; i < (dstStartChan + availDstChannels); ++i) { if(addArray ? addArray[i] : add) continue; if(dstBuffer[i]) { if(MusEGlobal::config.useDenormalBias) { for(unsigned int q = 0; q < nframes; ++q) dstBuffer[i][q] = MusEGlobal::denormalBias; } else memset(dstBuffer[i], 0, sizeof(float) * nframes); } } } else if(requestedSrcChans >= 2 && requestedDstChannels == 1) { const int cnt = availableSrcChans > 2 ? 2 : availableSrcChans; if(availDstChannels >= 1) { for(int sch = 0; sch < cnt; ++sch) { float* sp = outBuffers[srcStartChan + sch]; float* dp = dstBuffer[dstStartChan]; if(dp) { if((addArray ? addArray[dstStartChan] : add) || sch != 0) { for(unsigned k = 0; k < nframes; ++k) *dp++ += *sp++; } else AL::dsp->cpy(dp, sp, nframes); } } } else if(addArray ? !addArray[dstStartChan] : !add) { if(dstBuffer[dstStartChan]) { // Zero the supplied buffer. if(MusEGlobal::config.useDenormalBias) { for(unsigned int q = 0; q < nframes; ++q) dstBuffer[dstStartChan][q] = MusEGlobal::denormalBias; } else memset(dstBuffer[dstStartChan], 0, sizeof(float) * nframes); } } } else { const int cnt = availableSrcChans < availDstChannels ? availableSrcChans : availDstChannels; for(int c = 0; c < cnt; ++c) { float* sp = outBuffers[c + srcStartChan]; float* dp = dstBuffer[c + dstStartChan]; if(dp) { if(addArray ? addArray[c + dstStartChan] : add) { for(unsigned k = 0; k < nframes; ++k) *dp++ += *sp++; } else AL::dsp->cpy(dp, sp, nframes); } } // Zero the rest of the supplied buffers. for(i = dstStartChan + cnt; i < (dstStartChan + availDstChannels); ++i) { if(addArray ? addArray[i] : add) continue; if(dstBuffer[i]) { if(MusEGlobal::config.useDenormalBias) { for(unsigned int q = 0; q < nframes; ++q) dstBuffer[i][q] = MusEGlobal::denormalBias; } else memset(dstBuffer[i], 0, sizeof(float) * nframes); } } } } else { // No data was available from a previous call during this process cycle. //Zero the supplied buffers and just return. for(i = dstStartChan; i < (dstStartChan + availDstChannels); ++i) { if(addArray ? addArray[i] : add) continue; if(dstBuffer[i]) { if(MusEGlobal::config.useDenormalBias) { for(unsigned int q = 0; q < nframes; ++q) dstBuffer[i][q] = MusEGlobal::denormalBias; } else memset(dstBuffer[i], 0, sizeof(float) * nframes); } } } return; } else { // First time here during this process cycle. _haveData = false; // Reset. _processed = true; // Set this now. // Start by clearing the meters. There may be multiple contributions to them below. for(i = 0; i < trackChans; ++i) _meter[i] = 0.0; if(off()) { #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "MusE: AudioTrack::copyData name:%s dstChannels:%d Off, zeroing buffers\n", name().toLatin1().constData(), availDstChannels); #endif // Track is off. Zero the supplied buffers. for(i = dstStartChan; i < (dstStartChan + availDstChannels); ++i) { if(addArray ? addArray[i] : add) continue; if(dstBuffer[i]) { if(MusEGlobal::config.useDenormalBias) { for(unsigned int q = 0; q < nframes; ++q) dstBuffer[i][q] = MusEGlobal::denormalBias; } else memset(dstBuffer[i], 0, sizeof(float) * nframes); } } _efxPipe->apply(pos, 0, nframes, 0); // Just process controls only, not audio (do not 'run'). processTrackCtrls(pos, 0, nframes, 0); //for(i = 0; i < trackChans; ++i) // _meter[i] = 0.0; return; } // Point the input buffers at a temporary buffer. for(i = 0; i < srcTotalOutChans; ++i) buffer[i] = _dataBuffers[i]; // getData can use the supplied buffers, or change buffer to point to its own local buffers or Jack buffers etc. // For ex. if this is an audio input, Jack will set the pointers for us in AudioInput::getData! // Don't do any processing at all if off. Whereas, mute needs to be ready for action at all times, // so still call getData before it. Off is NOT meant to be toggled rapidly, but mute is ! // Since the meters are cleared above, getData can contribute (add) to them directly and return HaveMeterDataOnly // if it does not want to pass the audio for listening. if(!getData(pos, srcTotalOutChans, nframes, buffer)) { #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "MusE: AudioTrack::copyData name:%s srcTotalOutChans:%d zeroing buffers\n", name().toLatin1().constData(), srcTotalOutChans); #endif // No data was available. Track is not off. Zero the working buffers and continue on. unsigned int q; for(i = 0; i < srcTotalOutChans; ++i) { float* buf_p = buffer[i]; if(MusEGlobal::config.useDenormalBias) { for(q = 0; q < nframes; /*++q*/) buf_p[q++] = MusEGlobal::denormalBias; } else memset(buf_p, 0, sizeof(float) * nframes); } } //--------------------------------------------------- // apply plugin chain //--------------------------------------------------- // Allow it to process even if muted so that when mute is turned off, left-over buffers (reverb tails etc) can die away. _efxPipe->apply(pos, trackChans, nframes, buffer); //--------------------------------------------------- // apply volume, pan //--------------------------------------------------- processTrackCtrls(pos, trackChans, nframes, buffer); const int valid_out_bufs = _prefader ? 0 : trackChans; //--------------------------------------------------- // metering //--------------------------------------------------- // FIXME TODO Need multichannel changes here? for(int c = 0; c < trackChans; ++c) { meter[c] = 0.0; float* sp = (c >= valid_out_bufs) ? buffer[c] : outBuffers[c]; // Optimize: Don't all valid outBuffers just for meters for(unsigned k = 0; k < nframes; ++k) { const double f = fabs(*sp++); // If the track is mono pan has no effect on meters. if(f > meter[c]) meter[c] = f; } if(meter[c] > _meter[c]) _meter[c] = meter[c]; if(_meter[c] > _peak[c]) _peak[c] = _meter[c]; if(_meter [c] > 1.0) _isClipped[c] = true; } // REMOVE Tim. monitor. Changed. // if(isMute()) // Are both playback and input are muted? if(isMute() && !isRecMonitored()) { // Nothing to do. Zero the supplied buffers. for(i = dstStartChan; i < (dstStartChan + availDstChannels); ++i) { if(addArray ? addArray[i] : add) continue; if(dstBuffer[i]) { if(MusEGlobal::config.useDenormalBias) { for(unsigned int q = 0; q < nframes; q++) dstBuffer[i][q] = MusEGlobal::denormalBias; } else memset(dstBuffer[i], 0, sizeof(float) * nframes); } } return; // We're outta here. } // Copy whole blocks that we can get away with here outside of the track control processing loop. for(i = valid_out_bufs; i < srcTotalOutChans; ++i) AL::dsp->cpy(outBuffers[i], buffer[i], nframes); // We now have some data! Set to true. _haveData = true; //--------------------------------------------------- // aux sends //--------------------------------------------------- if(hasAuxSend()) { AuxList* al = MusEGlobal::song->auxs(); unsigned naux = al->size(); for(unsigned k = 0; k < naux; ++k) { double m = _auxSend[k]; if(m <= 0.0001) // optimize continue; AudioAux* a = (AudioAux*)((*al)[k]); float** dst = a->sendBuffer(); int auxChannels = a->channels(); if((trackChans ==1 && auxChannels==1) || trackChans == 2) { for(int ch = 0; ch < trackChans; ++ch) { float* db = dst[ch % a->channels()]; // no matter whether there's one or two dst buffers float* sb = outBuffers[ch]; for(unsigned f = 0; f < nframes; ++f) *db++ += (*sb++ * m); // add to mix } } else if(trackChans==1 && auxChannels==2) // copy mono to both channels { for(int ch = 0; ch < auxChannels; ++ch) { float* db = dst[ch % a->channels()]; float* sb = outBuffers[0]; for(unsigned f = 0; f < nframes; ++f) *db++ += (*sb++ * m); // add to mix } } } } //--------------------------------------------------- // copy to destination buffers //--------------------------------------------------- // FIXME TODO Need multichannel changes here? // Sanity check. Is source starting channel out of range? Just zero and return. if(srcStartChan >= srcTotalOutChans) { for(i = dstStartChan; i < (dstStartChan + availDstChannels); ++i) { if(addArray ? addArray[i] : add) continue; if(dstBuffer[i]) { if(MusEGlobal::config.useDenormalBias) { for(unsigned int q = 0; q < nframes; q++) dstBuffer[i][q] = MusEGlobal::denormalBias; } else memset(dstBuffer[i], 0, sizeof(float) * nframes); } } return; } if(requestedSrcChans == 1 && requestedDstChannels >= 2) { const int cnt = availDstChannels > 2 ? 2 : availDstChannels; int c = 0; if(availableSrcChans >= 1) { for( ; c < cnt; ++c) { float* sp; if(!_prefader && srcStartChan == 0 && trackChans == 1) sp = outBuffersExtraMix[c]; // Use the pre-panned mono-to-stereo extra buffers. else sp = outBuffers[srcStartChan]; // In all other cases use the main buffers. float* dp = dstBuffer[c + dstStartChan]; if(dp) { if(addArray ? addArray[c + dstStartChan] : add) { for(unsigned k = 0; k < nframes; ++k) *dp++ += *sp++; } else AL::dsp->cpy(dp, sp, nframes); } } } // Zero the rest of the supplied buffers. for(i = dstStartChan + c; i < (dstStartChan + availDstChannels); ++i) { if(addArray ? addArray[i] : add) continue; if(dstBuffer[i]) { if(MusEGlobal::config.useDenormalBias) { for(unsigned int q = 0; q < nframes; ++q) dstBuffer[i][q] = MusEGlobal::denormalBias; } else memset(dstBuffer[i], 0, sizeof(float) * nframes); } } } else if(requestedSrcChans >= 2 && requestedDstChannels == 1) { const int cnt = availableSrcChans > 2 ? 2 : availableSrcChans; if(availDstChannels >= 1) { for(int sch = 0; sch < cnt; ++sch) { float* sp = outBuffers[srcStartChan + sch]; float* dp = dstBuffer[dstStartChan]; if(dp) { if((addArray ? addArray[dstStartChan] : add) || sch != 0) { for(unsigned k = 0; k < nframes; ++k) *dp++ += *sp++; } else AL::dsp->cpy(dp, sp, nframes); } } } else if(addArray ? !addArray[dstStartChan] : !add) { if(dstBuffer[dstStartChan]) { // Zero the supplied buffer. if(MusEGlobal::config.useDenormalBias) { for(unsigned int q = 0; q < nframes; ++q) dstBuffer[dstStartChan][q] = MusEGlobal::denormalBias; } else memset(dstBuffer[dstStartChan], 0, sizeof(float) * nframes); } } } else //if(srcChans == dstChans) { const int cnt = availableSrcChans < availDstChannels ? availableSrcChans : availDstChannels; for(int c = 0; c < cnt; ++c) { float* sp = outBuffers[c + srcStartChan]; float* dp = dstBuffer[c + dstStartChan]; if(dp) { if(addArray ? addArray[c + dstStartChan] : add) { for(unsigned k = 0; k < nframes; ++k) *dp++ += *sp++; } else AL::dsp->cpy(dp, sp, nframes); } } // Zero the rest of the supplied buffers. for(i = dstStartChan + cnt; i < (dstStartChan + availDstChannels); ++i) { if(addArray ? addArray[i] : add) continue; if(dstBuffer[i]) { if(MusEGlobal::config.useDenormalBias) { for(unsigned int q = 0; q < nframes; ++q) dstBuffer[i][q] = MusEGlobal::denormalBias; } else memset(dstBuffer[i], 0, sizeof(float) * nframes); } } } } } //--------------------------------------------------------- // readVolume //--------------------------------------------------------- void AudioTrack::readVolume(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: xml.unknown("readVolume"); break; case Xml::Text: setVolume(xml.s1().toDouble()); break; case Xml::Attribut: if (xml.s1() == "ch") //ch = xml.s2().toInt(); xml.s2(); break; case Xml::TagEnd: if (xml.s1() == "volume") return; default: break; } } } //--------------------------------------------------------- // setChannels //--------------------------------------------------------- void Track::setChannels(int n) { if(n > MusECore::MAX_CHANNELS) _channels = MusECore::MAX_CHANNELS; else _channels = n; for (int i = 0; i < _channels; ++i) { _meter[i] = 0.0; _peak[i] = 0.0; } } bool AudioTrack::isLatencyInputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyInputTerminalProcessed) return _latencyInfo._isLatencyInputTerminal; // If we're asking for the view from the record side, check if we're // passing the signal through the track via monitoring. if(!canPassThruLatency()) { _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } const RouteList* rl = outRoutes(); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO } else { Track* track = ir->track; if(track->off()) // || //(atrack->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !atrack->isRecMonitored()))) //&& atrack->canRecord() && !atrack->recordFlag())) continue; _latencyInfo._isLatencyInputTerminal = false; _latencyInfo._isLatencyInputTerminalProcessed = true; return false; } break; default: break; } } _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } bool AudioTrack::isLatencyOutputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyOutputTerminalProcessed) return _latencyInfo._isLatencyOutputTerminal; const RouteList* rl = outRoutes(); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO } else { Track* track = ir->track; if(track->off()) // || //(atrack->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !atrack->isRecMonitored()))) //&& atrack->canRecord() && !atrack->recordFlag())) continue; _latencyInfo._isLatencyOutputTerminal = false; _latencyInfo._isLatencyOutputTerminalProcessed = true; return false; } break; default: break; } } _latencyInfo._isLatencyOutputTerminal = true; _latencyInfo._isLatencyOutputTerminalProcessed = true; return true; } //--------------------------------------------------------- // putFifo //--------------------------------------------------------- bool AudioTrack::putFifo(int channels, unsigned long n, float** bp) { float route_worst_case_latency = 0.0f; const bool use_latency_corr = useLatencyCorrection(); if(use_latency_corr) { // Are we bouncing this (audio output) track to a file, // or bouncing an audio output track to this (wave) track? if(MusEGlobal::song->bounceOutput == this || (MusEGlobal::song->bounceOutput && MusEGlobal::song->bounceTrack == this)) { // We want the bounce audio output track's output latency - without the port latency. const TrackLatencyInfo& li = MusEGlobal::song->bounceOutput->getLatencyInfo(false /*output*/); route_worst_case_latency = li._inputLatency + li._worstPluginLatency; } else { // We want this (wave) track's input latency. const TrackLatencyInfo& li = getLatencyInfo(true /*input*/); route_worst_case_latency = li._inputLatency; } } const unsigned int fin_frame = MusEGlobal::audio->pos().frame(); //fprintf(stderr, "AudioTrack::putFifo: latency:%f\n", route_worst_case_latency); if(fifo.put(channels, n, bp, fin_frame, route_worst_case_latency)) { fprintf(stderr, "AudioTrack::putFifo: fifo overrun: frame:%d, channels:%d, nframes:%lu\n", fin_frame, channels, n); return false; } return true; } //--------------------------------------------------------- // getData // return false if no data available //--------------------------------------------------------- bool AudioTrack::getData(unsigned pos, int channels, unsigned nframes, float** buffer) { // use supplied buffers const RouteList* rl = inRoutes(); const bool use_latency_corr = useLatencyCorrection(); #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "AudioTrack::getData name:%s channels:%d inRoutes:%d\n", name().toLatin1().constData(), channels, int(rl->size())); #endif int dst_ch, dst_chs, src_ch, src_chs, fin_dst_chs, next_chan, i; unsigned int q; unsigned long int l; bool have_data = false; bool used_in_chan_array[channels]; for(i = 0; i < channels; ++i) used_in_chan_array[i] = false; for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::TRACK_ROUTE || !ir->track || ir->track->isMidiTrack()) continue; // Only this track knows how many destination channels there are, // while only the route track knows how many source channels there are. // So take care of the destination channels here, and let the route track handle the source channels. dst_ch = ir->channel <= -1 ? 0 : ir->channel; if(dst_ch >= channels) continue; dst_chs = ir->channels <= -1 ? channels : ir->channels; src_ch = ir->remoteChannel <= -1 ? 0 : ir->remoteChannel; src_chs = ir->channels; fin_dst_chs = dst_chs; if(dst_ch + fin_dst_chs > channels) fin_dst_chs = channels - dst_ch; #ifdef NODE_DEBUG_PROCESS fprintf(stderr, " calling copy/addData on %s dst_ch:%d dst_chs:%d fin_dst_chs:%d src_ch:%d src_chs:%d ...\n", ir->track->name().toLatin1().constData(), dst_ch, dst_chs, fin_dst_chs, src_ch, src_chs); #endif static_cast(ir->track)->copyData(pos, dst_ch, dst_chs, fin_dst_chs, src_ch, src_chs, nframes, buffer, false, use_latency_corr ? nullptr : used_in_chan_array); // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. if((long int)ir->audioLatencyOut < 0) l = 0; else l = ir->audioLatencyOut; next_chan = dst_ch + fin_dst_chs; for(i = dst_ch; i < next_chan; ++i) { if(use_latency_corr) { // Write the buffers to the latency compensator. // By now, each copied channel should have the same latency. // They will be read back later, in-place. _latencyComp->write(i, nframes, l + latencyCompWriteOffset(), buffer[i]); } used_in_chan_array[i] = true; } have_data = true; } for(i = 0; i < channels; ++i) { if(used_in_chan_array[i]) { // Read back the latency compensated signals, using the buffers in-place. if(use_latency_corr) _latencyComp->read(i, nframes, buffer[i]); continue; } // Fill unused channels with silence. // Channel is unused. Zero the supplied buffer. if(MusEGlobal::config.useDenormalBias) { for(q = 0; q < nframes; ++q) buffer[i][q] = MusEGlobal::denormalBias; } else memset(buffer[i], 0, sizeof(float) * nframes); } return have_data; } //--------------------------------------------------------- // getData // return true if data //--------------------------------------------------------- bool AudioInput::getData(unsigned, int channels, unsigned nframes, float** buffer) { if (!MusEGlobal::checkAudioDevice()) return false; const bool use_latency_corr = useLatencyCorrection(); unsigned long latency_array[channels]; unsigned long worst_case_latency = 0; for(int i = 0; i < channels; ++i) { // For Audio Input tracks this is the track latency (rack plugins etc) // PLUS the Jack latency, on the given channel. // Note that if there are multiple connections to one of our Jack ports, // we ask Jack for the maximum latency, even though there's nothing we // can do about it since the data has already been mixed. // (Therefore always best for user to use separate ports or channels or // input tracks for each input connection.) const float f = selfLatencyAudio(i); latency_array[i] = f; if(f > worst_case_latency) worst_case_latency = f; } // Adjust the array values to arrive at forward write offsets to be passed to the latency compensator. for(int i = 0; i < channels; ++i) latency_array[i] = worst_case_latency - latency_array[i]; for (int ch = 0; ch < channels; ++ch) { void* jackPort = jackPorts[ch]; // Do not get buffers of unconnected client ports. Causes repeating leftover data, can be loud, or DC ! if (jackPort && MusEGlobal::audioDevice->connections(jackPort)) { //buffer[ch] = MusEGlobal::audioDevice->getBuffer(jackPort, nframes); // If the client port buffer is also used by another channel (connected to the same jack port), // don't directly set pointer, copy the data instead. // Otherwise the next channel will interfere - it will overwrite the buffer ! // Verified symptoms: Can't use a splitter. Mono noise source on a stereo track sounds in mono. Etc... // TODO: Problem: What if other Audio Input tracks share the same jack ports as this Audio Input track? // Users will expect that Audio Inputs just work even if the input routes originate from the same jack port. // Solution: Rather than having to iterate all other channels, and all other Audio Input tracks and check // their channel port buffers (if that's even possible) in order to determine if the buffer is shared, // let's just copy always, for now shall we ? float* jackbuf = MusEGlobal::audioDevice->getBuffer(jackPort, nframes); AL::dsp->cpy(buffer[ch], jackbuf, nframes); // Take care of the denormals now if there's no latency compensator. if (!use_latency_corr && MusEGlobal::config.useDenormalBias) { for (unsigned int i=0; i < nframes; i++) buffer[ch][i] += MusEGlobal::denormalBias; } // Write the buffers to the latency compensator. // They will be read back later, in-place. if(use_latency_corr) _latencyComp->write(ch, nframes, latency_array[ch], buffer[ch]); } else { // Don't bother setting the denormal data here if using the latency compensator. if(!use_latency_corr) { if (MusEGlobal::config.useDenormalBias) { for (unsigned int i=0; i < nframes; i++) buffer[ch][i] = MusEGlobal::denormalBias; } else { memset(buffer[ch], 0, nframes * sizeof(float)); } } } } //#ifdef NODE_DEBUG_TERMINAL_PEAK_METERS #if 0 if(MusEGlobal::audio->isPlaying()) { fprintf(stderr, "AudioInput::getData() name:%s\n", name().toLatin1().constData()); for(int ch = 0; ch < _channels; ++ch) { fprintf(stderr, "channel:%d peak:", ch); float val; float peak = 0.0f; const float* buf = buffer[ch]; for(unsigned int smp = 0; smp < nframes; ++smp) { val = buf[smp]; if(val > peak) peak = val; } const int dots = peak * 20; for(int d = 0; d < dots; ++d) fprintf(stderr, "*"); fprintf(stderr, "\n"); } } #endif if(use_latency_corr) { // Read back the latency compensated signals, using the buffers in-place. _latencyComp->read(nframes, buffer); if(MusEGlobal::config.useDenormalBias) { for(int ch = 0; ch < channels; ++ch) { for(unsigned int i = 0; i < nframes; i++) buffer[ch][i] += MusEGlobal::denormalBias; } } } return true; } //--------------------------------------------------------- // registerPorts //--------------------------------------------------------- bool AudioInput::registerPorts(int idx) { if (!MusEGlobal::checkAudioDevice()) return false; int b, e; if(idx < 0) { b = 0; e = channels(); } else { /*if(idx >= channels()) return false;*/ b = idx; e = idx + 1; } bool res = false; for (int i = b; i < e; ++i) { if (!jackPorts[i]) { const QString s = QString("%1-%2").arg(name()).arg(i).left(127); jackPorts[i] = MusEGlobal::audioDevice->registerInPort(s.toLatin1().constData(), false); if(jackPorts[i]) res = true; else fprintf(stderr, "AudioInput::registerPorts: Port <%s> registration FAILED !\n", s.toLatin1().constData()); } } return res; } //--------------------------------------------------------- // setName //--------------------------------------------------------- void AudioInput::setName(const QString& s) { AudioTrack::setName(s); if (!MusEGlobal::checkAudioDevice()) return; const QString n("%1-%2"); for (int i = 0; i < channels(); ++i) { if (jackPorts[i]) MusEGlobal::audioDevice->setPortName(jackPorts[i], n.arg(name()).arg(i).left(127).toLatin1().constData()); } } //--------------------------------------------------------- // resetMeter //--------------------------------------------------------- void Track::resetMeter() { for (int i = 0; i < _channels; ++i) _meter[i] = 0.0; } //--------------------------------------------------------- // resetPeaks //--------------------------------------------------------- void Track::resetPeaks() { for (int i = 0; i < _channels; ++i) { _peak[i] = 0.0; } _lastActivity = 0; } //--------------------------------------------------------- // resetAllMeter //--------------------------------------------------------- void Track::resetAllMeter() { TrackList* tl = MusEGlobal::song->tracks(); for (iTrack i = tl->begin(); i != tl->end(); ++i) (*i)->resetMeter(); } //--------------------------------------------------------- // setRecordFlag2 // real time part (executed in audio thread) //--------------------------------------------------------- void AudioTrack::setRecordFlag2(bool f) { if(!canRecord()) return; if (f == _recordFlag) return; _recordFlag = f; if (!_recordFlag) resetMeter(); } bool AudioTrack::setRecordFlag2AndCheckMonitor(bool f) { if (f != _recordFlag && canRecord()) { _recordFlag = f; if (!_recordFlag) resetMeter(); } if(MusEGlobal::config.monitorOnRecord && canRecordMonitor()) { if(f != _recMonitor) { _recMonitor = f; return true; } } return false; } //--------------------------------------------------------- // setMute //--------------------------------------------------------- void AudioTrack::setMute(bool f) { _mute = f; if (_mute) resetAllMeter(); } //--------------------------------------------------------- // setOff //--------------------------------------------------------- void AudioTrack::setOff(bool val) { _off = val; if (val) resetAllMeter(); } //--------------------------------------------------------- // setPrefader //--------------------------------------------------------- void AudioTrack::setPrefader(bool val) { _prefader = val; if (!_prefader && isMute()) resetAllMeter(); } //--------------------------------------------------------- // canEnableRecord //--------------------------------------------------------- bool WaveTrack::canEnableRecord() const { return (!noInRoute() || (this == MusEGlobal::song->bounceTrack)); } //--------------------------------------------------------- // record //--------------------------------------------------------- void AudioTrack::record() { MuseCount_t pos = 0; float latency = 0.0f; const bool use_latency_corr = useLatencyCorrection(); float* buffer[_channels]; while(fifo.getCount()) { if (fifo.get(_channels, MusEGlobal::segmentSize, buffer, &pos, &latency)) { fprintf(stderr, "AudioTrack::record(): empty fifo\n"); return; } if (_recFile) { // Line removed by Tim. Oct 28, 2009 //_recFile->seek(pos, 0); // // Fix for recorded waves being shifted ahead by an amount // equal to start record position. // // From libsndfile ChangeLog: // 2008-05-11 Erik de Castro Lopo // * src/sndfile.c // Allow seeking past end of file during write. // // I don't know why this line would even be called, because the FIFOs' // 'pos' members operate in absolute frames, which at this point // would be shifted ahead by the start of the wave part. // So if you begin recording a new wave part at bar 4, for example, then // this line is seeking the record file to frame 288000 even before any audio is written! // Therefore, just let the write do its thing and progress naturally, // it should work OK since everything was OK before the libsndfile change... // // Tested: With the line, audio record looping sort of works, albeit with the start offset added to // the wave file. And it overwrites existing audio. (Check transport window 'overwrite' function. Tie in somehow...) // With the line, looping does NOT work with libsndfile from around early 2007 (my distro's version until now). // Therefore it seems sometime between libsndfile ~2007 and today, libsndfile must have allowed // "seek (behind) on write", as well as the "seek past end" change of 2008... // // Ok, so removing that line breaks *possible* record audio 'looping' functionality, revealed with // later libsndfile. // Try this... And while we're at it, honour the punchin/punchout, and loop functions ! // // If punchin is on, or we have looped at least once, use left marker as offset. // Note that audio::startRecordPos is reset to (roughly) the left marker pos upon loop ! // (Not any more! I changed Audio::Process) // Since it is possible to start loop recording before the left marker (with punchin off), we must // use startRecordPos or loopFrame or left marker, depending on punchin and whether we have looped yet. unsigned fr; if(MusEGlobal::song->punchin() && (MusEGlobal::audio->loopCount() == 0)) fr = MusEGlobal::audio->getStartRecordPos().frame() > MusEGlobal::song->lPos().frame() ? MusEGlobal::audio->getStartRecordPos().frame() : MusEGlobal::song->lPos().frame(); else if((MusEGlobal::audio->loopCount() > 0) && (MusEGlobal::audio->getStartRecordPos().frame() > MusEGlobal::audio->loopFrame())) fr = MusEGlobal::audio->loopFrame(); else fr = MusEGlobal::audio->getStartRecordPos().frame(); // Now seek and write. If we are looping and punchout is on, don't let punchout point interfere with looping point. if( (pos >= fr) && (!MusEGlobal::song->punchout() || (!MusEGlobal::song->loop() && pos < MusEGlobal::song->rPos().frame())) ) { pos -= fr; // Let's try to avoid accidental very large files by very large latency values? if(use_latency_corr && (latency < -1000000 || latency > 1000000)) { // Try not to flood, normally. if(MusEGlobal::debugMsg) fprintf(stderr, "AudioNode::record(): Error: Latency seems excessively high:%f Trimming to +/-1000000\n", latency); if(latency < -1000000) latency = -1000000; else if(latency > 1000000) latency = 1000000; } if(!use_latency_corr || (pos >= latency)) { #ifdef NODE_DEBUG_TERMINAL_PEAK_METERS fprintf(stderr, "AudioNode::record(): pos:%u latency:%f\n", pos, latency); for(int ch = 0; ch < _channels; ++ch) { fprintf(stderr, "channel:%d peak:", ch); float val; float peak = 0.0f; const float* buf = buffer[ch]; for(unsigned int smp = 0; smp < MusEGlobal::segmentSize; ++smp) { val = buf[smp]; if(val > peak) peak = val; } const int dots = peak * 20; for(int d = 0; d < dots; ++d) fprintf(stderr, "*"); fprintf(stderr, "\n"); } #endif if(use_latency_corr) pos -= latency; // FIXME If we are to support writing compressed file types, we probably shouldn't be seeking here. REMOVE Tim. Wave. _recFile->seek(pos, 0); _recFile->write(_channels, buffer, MusEGlobal::segmentSize, MusEGlobal::config.liveWaveUpdate); } } } else { fprintf(stderr, "AudioNode::record(): no recFile\n"); } } } //--------------------------------------------------------- // processInit //--------------------------------------------------------- void AudioOutput::processInit(unsigned nframes) { _nframes = nframes; if (!MusEGlobal::checkAudioDevice()) return; for (int i = 0; i < channels(); ++i) { buffer[i] = nullptr; if (jackPorts[i]) { buffer[i] = MusEGlobal::audioDevice->getBuffer(jackPorts[i], nframes); if(buffer[i]) { if (MusEGlobal::config.useDenormalBias) { for (unsigned int j=0; j < nframes; j++) buffer[i][j] += MusEGlobal::denormalBias; } } } } } //--------------------------------------------------------- // process // synthesize "n" frames at buffer offset "offset" // current frame position is "pos" //--------------------------------------------------------- void AudioOutput::process(unsigned pos, unsigned offset, unsigned n) { #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "MusE: AudioOutput::process name:%s processed:%d\n", name().toLatin1().constData(), processed()); #endif for (int i = 0; i < _channels; ++i) { // Safety check. The port, and therefore the buffer, may be null. if(!buffer[i]) return; buffer1[i] = buffer[i] + offset; } copyData(pos, -1, _channels, _channels, -1, -1, n, buffer1); //#ifdef NODE_DEBUG_TERMINAL_PEAK_METERS #if 0 if(MusEGlobal::audio->isPlaying()) { fprintf(stderr, "AudioOutput::process() name:%s pos:%u offset:%u\n", name().toLatin1().constData(), pos, offset); for(int ch = 0; ch < _channels; ++ch) { fprintf(stderr, "channel:%d peak:", ch); float val; float peak = 0.0f; const float* buf = buffer1[ch]; for(unsigned int smp = 0; smp < n; ++smp) { val = buf[smp]; if(val > peak) peak = val; } const int dots = peak * 20; for(int d = 0; d < dots; ++d) fprintf(stderr, "*"); fprintf(stderr, "\n"); } } #endif } //--------------------------------------------------------- // silence //--------------------------------------------------------- void AudioOutput::silence(unsigned n) { processInit(n); for (int i = 0; i < channels(); ++i) if(buffer[i]) { if (MusEGlobal::config.useDenormalBias) { for (unsigned int j=0; j < n; j++) buffer[i][j] = MusEGlobal::denormalBias; } else { memset(buffer[i], 0, n * sizeof(float)); } } } //--------------------------------------------------------- // processWrite //--------------------------------------------------------- void AudioOutput::processWrite() { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if (MusEGlobal::audio->isRecording() && MusEGlobal::song->bounceOutput == this) { if (MusEGlobal::audio->freewheel()) { // NOTE: Tests showed that during freewheel, Jack reports zero latency on all ports. // But let's go through the motions here anyway. float latency = 0.0f; const bool use_latency_corr = useLatencyCorrection(); if(use_latency_corr) { // We want this audio output track's output latency - without the port latency. const TrackLatencyInfo& li = getLatencyInfo(false /*output*/); latency = li._inputLatency + li._worstPluginLatency; // Let's try to avoid accidental very large files by very large latency values? if(latency < -1000000 || latency > 1000000) { // Try not to flood, normally. if(MusEGlobal::debugMsg) fprintf(stderr, "AudioOutput::processWrite(): Error: Latency seems excessively high:%f Trimming to +/-1000000\n", latency); if(latency < -1000000) latency = -1000000; else if(latency > 1000000) latency = 1000000; } } //fprintf(stderr, "AudioOutput::processWrite(): Freewheel: _previousLatency:%f latency:%f _recFilePos:%ld audio pos frame:%u\n", // _previousLatency, latency, _recFilePos, MusEGlobal::audio->pos().frame()); MusECore::WaveTrack* track = MusEGlobal::song->bounceTrack; if (track && track->recordFlag() && track->recFile()) { if(!use_latency_corr || _recFilePos >= latency) { // Has the latency changed? if(use_latency_corr && latency != _previousLatency) { long int pos = _recFilePos; pos -= latency; //fprintf(stderr, "AudioOutput::processWrite(): latency:%f Seeking track _recFile to:%ld\n", latency, pos); track->recFile()->seek(pos, 0); _previousLatency = latency; } //fprintf(stderr, "AudioOutput::processWrite(): Writing track _recFile\n"); track->recFile()->write(_channels, buffer, _nframes, MusEGlobal::config.liveWaveUpdate); } _recFilePos += _nframes; } if (recordFlag() && recFile()) { if(!use_latency_corr || _recFilePos >= latency) { // Has the latency changed? if(use_latency_corr && latency != _previousLatency) { long int pos = _recFilePos; pos -= latency; //fprintf(stderr, "AudioOutput::processWrite(): latency:%f Seeking _recFile to:%ld\n", latency, pos); _recFile->seek(pos, 0); _previousLatency = latency; } //fprintf(stderr, "AudioOutput::processWrite(): Writing _recFile\n"); _recFile->write(_channels, buffer, _nframes, MusEGlobal::config.liveWaveUpdate); } _recFilePos += _nframes; } } else { MusECore::WaveTrack* track = MusEGlobal::song->bounceTrack; if (track && track->recordFlag() && track->recFile()) track->putFifo(_channels, _nframes, buffer); if (recordFlag() && recFile()) putFifo(_channels, _nframes, buffer); } } if (sendMetronome() && metro_settings->audioClickFlag && MusEGlobal::song->click()) { #ifdef METRONOME_DEBUG fprintf(stderr, "MusE: AudioOutput::processWrite Calling metronome->addData frame:%u channels:%d frames:%lu\n", MusEGlobal::audio->pos().frame(), _channels, _nframes); #endif metronome->copyData(MusEGlobal::audio->pos().frame(), -1, _channels, _channels, -1, -1, _nframes, buffer, true); } MusEGlobal::wavePreview->addData(_channels, _nframes, buffer); } //--------------------------------------------------------- // registerPorts //--------------------------------------------------------- bool AudioOutput::registerPorts(int idx) { if (!MusEGlobal::checkAudioDevice()) return false; int b, e; if(idx < 0) { b = 0; e = channels(); } else { /*if(idx >= channels()) return false;*/ b = idx; e = idx + 1; } bool res = false; for (int i = b; i < e; ++i) { if (!jackPorts[i]) { const QString s = QString("%1-%2").arg(name()).arg(i).left(127); jackPorts[i] = MusEGlobal::audioDevice->registerOutPort(s.toLatin1().constData(), false); if(jackPorts[i]) res = true; else fprintf(stderr, "AudioOutput::registerPorts: Port <%s> registration FAILED !\n", s.toLatin1().constData()); } } return res; } //--------------------------------------------------------- // setName //--------------------------------------------------------- void AudioOutput::setName(const QString& s) { AudioTrack::setName(s); if (!MusEGlobal::checkAudioDevice()) return; const QString n("%1-%2"); for (int i = 0; i < channels(); ++i) { if (jackPorts[i]) MusEGlobal::audioDevice->setPortName(jackPorts[i], n.arg(name()).arg(i).left(127).toLatin1().constData()); } } //--------------------------------------------------------- // setParam //--------------------------------------------------------- void AudioTrack::setParam(unsigned long i, double val) { addScheduledControlEvent(i, val, MusEGlobal::audio->curFrame()); } //--------------------------------------------------------- // param //--------------------------------------------------------- double AudioTrack::param(unsigned long i) const { return _controls[i].dval; } //--------------------------------------------------------- // setChannels //--------------------------------------------------------- void AudioTrack::setChannels(int n) { Track::setChannels(n); if (_efxPipe) _efxPipe->setChannels(_channels); if(useLatencyCorrection()) _latencyComp->setChannels(totalProcessBuffers()); } //--------------------------------------------------------- // setTotalOutChannels //--------------------------------------------------------- void AudioTrack::setTotalOutChannels(int num) { int chans = _totalOutChannels; if(num != chans) { if(_dataBuffers) { for(int i = 0; i < _totalOutChannels; ++i) { if(_dataBuffers[i]) { free(_dataBuffers[i]); _dataBuffers[i] = NULL; } } delete[] _dataBuffers; _dataBuffers = NULL; } _totalOutChannels = num; int new_chans = num; // Number of allocated buffers is always MAX_CHANNELS or more, even if _totalOutChannels is less. if(new_chans < MusECore::MAX_CHANNELS) new_chans = MusECore::MAX_CHANNELS; if(chans < MusECore::MAX_CHANNELS) chans = MusECore::MAX_CHANNELS; if(new_chans != chans) { if(outBuffers) { for(int i = 0; i < chans; ++i) { if(outBuffers[i]) { free(outBuffers[i]); outBuffers[i] = NULL; } } delete[] outBuffers; outBuffers = NULL; } } initBuffers(); } chans = num; // Limit the actual track (meters, copying etc, all 'normal' operation) to two-channel stereo. if(chans > MusECore::MAX_CHANNELS) chans = MusECore::MAX_CHANNELS; setChannels(chans); } //--------------------------------------------------------- // setTotalInChannels //--------------------------------------------------------- void AudioTrack::setTotalInChannels(int num) { if(num == _totalInChannels) return; _totalInChannels = num; } } // namespace MusECore muse-4.0.0/src/muse/operations.cpp000066400000000000000000004177071404105311000171200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // operations.cpp // (C) Copyright 2014, 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "operations.h" #include "song.h" #include "globals.h" #include "synth.h" #include "muse_time.h" #include "config.h" // Forwards from header: #include "tempo.h" #include "sig.h" #include "keyevent.h" #include "midiport.h" #include "metronome_class.h" #include "audio_convert/audio_converter_plugin.h" #include "audio_convert/audio_converter_settings_group.h" // Enable for debugging: //#define _PENDING_OPS_DEBUG_ // For debugging output: Uncomment the fprintf section. #define ERROR_OPERATIONS(dev, format, args...) fprintf(dev, format, ##args) #define DEBUG_OPERATIONS(dev, format, args...) //fprintf(dev, format, ##args) namespace MusECore { //----------------------------------- // MidiCtrlValListIterators //----------------------------------- MidiCtrlValListIterators::iterator MidiCtrlValListIterators::findList(const MidiCtrlValList* valList) { for(iterator i = begin(); i != end(); ++i) if((*i)->second == valList) return i; return end(); } MidiCtrlValListIterators::const_iterator MidiCtrlValListIterators::findList(const MidiCtrlValList* valList) const { for(const_iterator i = begin(); i != end(); ++i) if((*i)->second == valList) return i; return end(); } //----------------------------------- // MidiCtrlValLists2bErased //----------------------------------- void MidiCtrlValLists2bErased::add(int port, const iMidiCtrlValList& item) { iterator i = find(port); if(i == end()) { MidiCtrlValListIterators mcvli; mcvli.push_back(item); insert(MidiCtrlValLists2bErasedInsertPair_t(port, mcvli)); return; } MidiCtrlValListIterators& mcvli = i->second; for(iMidiCtrlValListIterators_t imcvli = mcvli.begin(); imcvli != mcvli.end(); ++imcvli) { iMidiCtrlValList imcvl = *imcvli; // Compare list pointers. if(imcvl->second == item->second) return; // Already exists. } mcvli.push_back(item); } MidiCtrlValLists2bErased::iterator MidiCtrlValLists2bErased::findList(int port, const MidiCtrlValList* valList) { iterator i = find(port); if(i == end()) return end(); if(i->second.findList(valList) != i->second.end()) return i; return end(); } MidiCtrlValLists2bErased::const_iterator MidiCtrlValLists2bErased::findList(int port, const MidiCtrlValList* valList) const { const_iterator i = find(port); if(i == end()) return end(); if(i->second.findList(valList) != i->second.end()) return i; return end(); } //----------------------------------- // PendingOperationItem //----------------------------------- bool PendingOperationItem::isAllocationOp(const PendingOperationItem& op) const { switch(op._type) { case AddMidiCtrlValList: // A is channel B is control. if(_type == AddMidiCtrlValList && _mcvll == op._mcvll && _intA == op._intA && _intB == op._intB) return true; break; // In the case of type AddMidiDevice, this searches for the name only. case AddMidiDevice: if(_type == AddMidiDevice && _midi_device_list == op._midi_device_list && _midi_device->name() == op._midi_device->name()) return true; break; default: break; } return false; } unsigned int PendingOperationItem::getIndex() const { switch(_type) { case Uninitialized: case AddAuxSendValue: case AddMidiInstrument: case DeleteMidiInstrument: case ReplaceMidiInstrument: case AddMidiDevice: case DeleteMidiDevice: case ModifyMidiDeviceAddress: case ModifyMidiDeviceFlags: case ModifyMidiDeviceName: case SetInstrument: case AddTrack: case DeleteTrack: case MoveTrack: case ModifyTrackName: case ModifyTrackDrumMapItem: case ReplaceTrackDrumMapPatchList: case RemapDrumControllers: case UpdateDrumMaps: case SetTrackRecord: case SetTrackMute: case SetTrackSolo: case SetTrackRecMonitor: case SetTrackOff: case ModifyPartName: case ModifySongLength: case AddMidiCtrlValList: case ModifyAudioCtrlValList: case SetGlobalTempo: case AddRoute: case DeleteRoute: case AddRouteNode: case DeleteRouteNode: case ModifyRouteNode: case UpdateSoloStates: case EnableAllAudioControllers: case GlobalSelectAllEvents: case SwitchMetronomeSettings: case ModifyMetronomeAccentMap: case SetExternalSyncFlag: case SetUseJackTransport: case SetUseMasterTrack: case ModifyAudioSamples: case SetStaticTempo: case ModifyLocalAudioConverterSettings: case ModifyLocalAudioConverter: case ModifyDefaultAudioConverterSettings: case ModifyStretchListRatio: case SetAudioConverterOfflineMode: case ModifyMarkerList: case ModifyTempoList: case ModifySigList: case ModifyKeyList: case ModifyEventList: case ModifyMidiCtrlValList: // To help speed up searches of these ops, let's (arbitrarily) set index = type instead of all of them being at index 0! return _type; case ModifyPartStart: return _part->posValue(); case ModifyPartLength: return _part->posValue(); case MovePart: // _part is used here rather than _iPart since _iPart can be end(). return _part->posValue(); case AddPart: return _part->posValue(); case DeletePart: return _iPart->second->posValue(); case SelectPart: return _part->posValue(); case AddEvent: return _ev.posValue(); case DeleteEvent: return _ev.posValue(); case SelectEvent: return _ev.posValue(); case AddMidiCtrlVal: return _posLenVal; // Tick case DeleteMidiCtrlVal: return _imcv->first; // Tick case ModifyMidiCtrlVal: return _imcv->first; // Tick case AddAudioCtrlVal: return _posLenVal; // Frame case DeleteAudioCtrlVal: return _iCtrl->first; // Frame case ModifyAudioCtrlVal: return _iCtrl->first; // Frame case AddStretchListRatioAt: return _museFrame; // Frame case DeleteStretchListRatioAt: return _iStretchEvent->first; // Frame case ModifyStretchListRatioAt: return _iStretchEvent->first; // Frame default: ERROR_OPERATIONS(stderr, "PendingOperationItem::getIndex unknown op type: %d\n", _type); return 0; break; } } SongChangedStruct_t PendingOperationItem::executeRTStage() { SongChangedStruct_t flags = 0; switch(_type) { case ModifyDefaultAudioConverterSettings: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyDefaultAudioConverterSettings: " "settings:%p\n", _audio_converter_settings); if(_audio_converter_settings) { // Grab the current settings. AudioConverterSettingsGroup* cur_settings = MusEGlobal::defaultAudioConverterSettings; // Now change the actual global pointer variable. MusEGlobal::defaultAudioConverterSettings = _audio_converter_settings; // Transfer the original pointer into the member, so it can be deleted in the non-RT stage. _audio_converter_settings = cur_settings; flags |= SC_AUDIO_CONVERTER; } break; case ModifyLocalAudioConverterSettings: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyLocalAudioConverterSettings: " "sndFile:%p settings:%p\n", *_sndFileR, _audio_converter_settings); // _audio_converter_settings can be NULL meaning don't touch, settings can only be // 'replaced' but not deleted. if(_audio_converter_settings) { AudioConverterSettingsGroup* cur_settings = _sndFileR.audioConverterSettings(); _sndFileR.setAudioConverterSettings(_audio_converter_settings); // Transfer the original pointer into the member, so it can be deleted in the non-RT stage. _audio_converter_settings = cur_settings; flags |= SC_AUDIO_CONVERTER; } } break; case ModifyLocalAudioConverter: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyLocalAudioConverter: " "sndFile:%p audio_converter:%p audio_converter_ui:%p\n", *_sndFileR, _audio_converter, _audio_converter_ui); // _audio_converter and _audio_converter_ui can be NULL meaning delete them. AudioConverterPluginI* cur_conv = _sndFileR.staticAudioConverter(AudioConverterSettings::RealtimeMode); //if(_audio_converter) _sndFileR.setStaticAudioConverter(_audio_converter, AudioConverterSettings::RealtimeMode); // Transfer the original pointer into the member, so it can be deleted in the non-RT stage. _audio_converter = cur_conv; AudioConverterPluginI* cur_convUI = _sndFileR.staticAudioConverter(AudioConverterSettings::GuiMode); //if(_audio_converter_ui) _sndFileR.setStaticAudioConverter(_audio_converter_ui, AudioConverterSettings::GuiMode); // Transfer the original pointer into the member, so it can be deleted in the non-RT stage. _audio_converter_ui = cur_convUI; flags |= SC_AUDIO_CONVERTER; } break; case SetAudioConverterOfflineMode: { // DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage SetAudioConverterOfflineMode: " // "sndFile:%p audio_converter:%p\n", // *_sndFile, _audio_converter); AudioConverterPluginI* cur_conv = _sndFileR.staticAudioConverter(AudioConverterSettings::RealtimeMode); //if(_audio_converter) _sndFileR.setStaticAudioConverter(_audio_converter, AudioConverterSettings::RealtimeMode); // Transfer the original pointer into the member, so it can be deleted in the non-RT stage. _audio_converter = cur_conv; flags |= SC_AUDIO_CONVERTER; } break; case ModifyTrackDrumMapItem: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage ModifyTrackDrumMapItem drummap operation:%p\n", _drum_map_track_operation); #endif MidiTrack* mt; MidiTrackList& mtl = _drum_map_track_operation->_tracks; for(iMidiTrack imt = mtl.begin(); imt != mtl.end(); ++imt) { mt = *imt; // FIXME Possible non realtime-friendly allocation. mt->modifyWorkingDrumMap(_drum_map_track_operation->_workingItemList, _drum_map_track_operation->_isReset, _drum_map_track_operation->_includeDefault, _drum_map_track_operation->_isInstrumentMod, _drum_map_track_operation->_doWholeMap); flags |= (SC_DRUMMAP); } // If this is an instrument modification we must now do a // general update of all drum track drum maps. // Ideally we would like to update only the required ones, // but it is too difficult to tell which maps need updating // from inside the above loop (or inside modifyWorkingDrumMap). if(_drum_map_track_operation->_isInstrumentMod) { MidiTrackList* mtlp = MusEGlobal::song->midis(); for(iMidiTrack imt = mtlp->begin(); imt != mtlp->end(); ++imt) { mt = *imt; if(mt->type() != Track::DRUM) continue; if(mt->updateDrummap(false)) flags |= (SC_DRUMMAP); } } flags |= (SC_DRUMMAP); } break; case ReplaceTrackDrumMapPatchList: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage ReplaceTrackDrumMapPatchList drummap operation:%p\n", _drum_map_track_patch_replace_operation); #endif MidiTrack* mt = _drum_map_track_patch_replace_operation->_track; WorkingDrumMapPatchList* orig_wdmpl = mt->workingDrumMap(); // Simply switch pointers. Be sure to delete the original pointers later in the non-realtime stage. // After the list pointers have been switched, swap with the replacement so that it can be deleted later. mt->setWorkingDrumMap(_drum_map_track_patch_replace_operation->_workingItemPatchList, _drum_map_track_patch_replace_operation->_isInstrumentMod); _drum_map_track_patch_replace_operation->_workingItemPatchList = orig_wdmpl; // If this is an instrument modification we must now do a // general update of all drum track drum maps. // Ideally we would like to update only the required ones, // but it is too difficult to tell which maps need updating // from inside the above loop (or inside modifyWorkingDrumMap). if(_drum_map_track_patch_replace_operation->_isInstrumentMod) { MidiTrackList* mtlp = MusEGlobal::song->midis(); for(iMidiTrack imt = mtlp->begin(); imt != mtlp->end(); ++imt) { mt = *imt; if(mt->type() != Track::DRUM) continue; if(mt->updateDrummap(false)) flags |= (SC_DRUMMAP); } } flags |= (SC_DRUMMAP); } break; case RemapDrumControllers: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage RemapDrumControllers remap operation:%p\n", _midi_ctrl_val_remap_operation); #endif for(iMidiCtrlValLists2bErased_t imcvle = _midi_ctrl_val_remap_operation->_midiCtrlValLists2bErased.begin(); imcvle != _midi_ctrl_val_remap_operation->_midiCtrlValLists2bErased.end(); ++imcvle) { const int port = imcvle->first; MidiCtrlValListIterators& mcvli = imcvle->second; MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiCtrlValListList* mcvll = mp->controller(); for(iMidiCtrlValListIterators_t imcvli = mcvli.begin(); imcvli != mcvli.end(); ++imcvli) mcvll->del(*imcvli); } for(iMidiCtrlValLists2bAdded_t imcvla = _midi_ctrl_val_remap_operation->_midiCtrlValLists2bAdded.begin(); imcvla != _midi_ctrl_val_remap_operation->_midiCtrlValLists2bAdded.end(); ++imcvla) { const int port = imcvla->first; MidiCtrlValListList* mcvll_a = imcvla->second; MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiCtrlValListList* mcvll = mp->controller(); for(iMidiCtrlValList imcvl = mcvll_a->begin(); imcvl != mcvll_a->end(); ++imcvl) mcvll->add(imcvl->first >> 24, imcvl->second); } // TODO: What to use here? We don't have anything SC_* related... yet. //flags |= (SC_MIDI_CONTROLLER_ADD); flags |= (SC_EVERYTHING); } break; case UpdateDrumMaps: #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage UpdateDrumMaps: midi_port:%p:\n", _midi_port); #endif if(_midi_port->updateDrumMaps()) flags |= SC_DRUMMAP; break; case UpdateSoloStates: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage UpdateSoloStates: track_list:%p:\n", _track_list); // TODO Use the track_list, or simply keep as dummy parameter to identify UpdateSoloStates? MusEGlobal::song->updateSoloStates(); flags |= SC_SOLO; break; // TODO: Try to break this operation down so that only the actual operation is executed stage-2. case AddRoute: #ifdef _PENDING_OPS_DEBUG_ DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddRoute: src/dst routes:\n"); _src_route.dump(); _dst_route.dump(); #endif if(addRoute(_src_route, _dst_route)) flags |= SC_ROUTE; break; // TODO: Try to break this operation down so that only the actual operation is executed stage-2. case DeleteRoute: #ifdef _PENDING_OPS_DEBUG_ DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteRoute: src/dst routes:\n"); _src_route.dump(); _dst_route.dump(); #endif if(removeRoute(_src_route, _dst_route)) flags |= SC_ROUTE; break; case AddRouteNode: #ifdef _PENDING_OPS_DEBUG_ DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddRouteNode: route_list:%p route:\n", _route_list); _src_route.dump(); #endif _route_list->push_back(_src_route); flags |= SC_ROUTE; break; case DeleteRouteNode: #ifdef _PENDING_OPS_DEBUG_ DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteRouteNode: route_list:%p route:\n", _route_list); _iRoute->dump(); #endif _route_list->erase(_iRoute); flags |= SC_ROUTE; break; case ModifyRouteNode: #ifdef _PENDING_OPS_DEBUG_ DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyRouteNode: src/dst routes:\n"); _src_route.dump(); _dst_route_pointer->dump(); #endif *_dst_route_pointer = _src_route; flags |= SC_ROUTE; break; case AddAuxSendValue: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddAuxSendValue aux_send_value_list:%p val:%f\n", _aux_send_value_list, _aux_send_value); _aux_send_value_list->push_back(_aux_send_value); break; case AddMidiInstrument: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddMidiInstrument instrument_list:%p instrument:%p\n", _midi_instrument_list, _midi_instrument); _midi_instrument_list->push_back(_midi_instrument); flags |= SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD; break; case DeleteMidiInstrument: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteMidiInstrument instrument_list:%p instrument:%p\n", _midi_instrument_list, *_iMidiInstrument); _midi_instrument_list->erase(_iMidiInstrument); flags |= SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD; break; case ReplaceMidiInstrument: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage ReplaceMidiInstrument instrument_list:%p instrument:%p new_ instrument:%p\n", _midi_instrument_list, *_iMidiInstrument, _midi_instrument); #endif // Grab the existing pointer to be deleted. MidiInstrument* orig = *_iMidiInstrument; // Erase from the list. _midi_instrument_list->erase(_iMidiInstrument); // Add the new instrument. _midi_instrument_list->push_back(_midi_instrument); // Change all ports which used the original instrument. for(int port = 0; port < MusECore::MIDI_PORTS; ++port) { MidiPort* mp = &MusEGlobal::midiPorts[port]; if(mp->instrument() != orig) continue; // Set the new instrument and nothing more (ie. don't use MidiPort::changeInstrument()). // Here we will flag the initializations, and normalize and update the drum maps. mp->setInstrument(_midi_instrument); // Flag the port to send initializations next time it bothers to check. // TODO: Optimize: We only need this if the user changed the initialization // lists or sysex list. Find a way to pass that info here. mp->clearInitSent(); } // Since this is an instrument modification we must now do a // general update of all drum track drum maps using this instrument. // Ideally we would like to update only the required ones, // but it is too difficult to tell which maps need updating // from inside the above loop (or inside modifyWorkingDrumMap). MidiTrack* mt; int mt_port; MidiPort* mt_mp; MidiTrackList* mtlp = MusEGlobal::song->midis(); for(iMidiTrack imt = mtlp->begin(); imt != mtlp->end(); ++imt) { mt = *imt; if(mt->type() != Track::DRUM) continue; mt_port = mt->outPort(); if(mt_port < 0 || mt_port >= MusECore::MIDI_PORTS) continue; mt_mp = &MusEGlobal::midiPorts[mt_port]; // We are looking for tracks which are now using the new instrument. if(mt_mp->instrument() != _midi_instrument) continue; // Ensure there are NO duplicate enote fields. //mt->normalizeWorkingDrumMapPatchList(); // Finally, update the track's drum map (and drum in map). mt->updateDrummap(false); } // Transfer the original pointer back to _midi_instrument so it can be deleted in the non-RT stage. _midi_instrument = orig; flags |= SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD; } break; case AddMidiDevice: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddMidiDevice devicelist:%p device:%p\n", _midi_device_list, _midi_device); _midi_device_list->push_back(_midi_device); flags |= SC_CONFIG; break; case DeleteMidiDevice: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteMidiDevice devicelist:%p device:%p\n", _midi_device_list, *_iMidiDevice); _midi_device_list->erase(_iMidiDevice); flags |= SC_CONFIG; break; case ModifyMidiDeviceAddress: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyMidiDeviceAddress device:%p client:%d port:%d\n", _midi_device, _address_client, _address_port); _midi_device->setAddressClient(_address_client); _midi_device->setAddressPort(_address_port); _midi_device->setOpenFlags(_open_flags); flags |= SC_CONFIG; break; case ModifyMidiDeviceFlags: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyMidiDeviceFlags device:%p rwFlags:%d openFlags:%d\n", _midi_device, _rw_flags, _open_flags); _midi_device->setrwFlags(_rw_flags); _midi_device->setOpenFlags(_open_flags); flags |= SC_CONFIG; break; case ModifyMidiDeviceName: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyMidiDeviceName device:%p name:%s\n", _midi_device, _name->toLocal8Bit().data()); _midi_device->setName(*_name); flags |= SC_CONFIG; break; case SetInstrument: #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage SetInstrument port:%p instr:%p\n", _midi_port, _midi_instrument); #endif _midi_port->setInstrument(_midi_instrument); // Flag the port to send initializations next time it bothers to check. _midi_port->clearInitSent(); flags |= SC_MIDI_INSTRUMENT; break; case AddTrack: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddTrack track_list:%p track:%p\n", _track_list, _track); if(_void_track_list) { switch(_track->type()) { case Track::MIDI: case Track::DRUM: static_cast(_void_track_list)->push_back(static_cast(_track)); break; case Track::WAVE: static_cast(_void_track_list)->push_back(static_cast(_track)); break; case Track::AUDIO_OUTPUT: //-------------------------------------------------------------- // Connect metronome audio click //-------------------------------------------------------------- // Are there no other existing AudioOutput tracks? if(static_cast(_void_track_list)->empty()) // Do the user a favour: Auto-connect the metronome to the track. static_cast(_track)->setSendMetronome(true); static_cast(_void_track_list)->push_back(static_cast(_track)); break; case Track::AUDIO_GROUP: static_cast(_void_track_list)->push_back(static_cast(_track)); break; case Track::AUDIO_AUX: static_cast(_void_track_list)->push_back(static_cast(_track)); // Special for aux, make it easier to detect their changes. flags |= SC_AUX; break; case Track::AUDIO_INPUT: static_cast(_void_track_list)->push_back(static_cast(_track)); break; case Track::AUDIO_SOFTSYNTH: static_cast(_void_track_list)->push_back(static_cast(_track)); break; default: ERROR_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddTrack: Unknown track type %d\n", _track->type()); return flags; } } iTrack track_it = _track_list->index2iterator(_insert_at); _track_list->insert(track_it, _track); flags |= SC_TRACK_INSERTED; // Add routes: if(_track->isMidiTrack()) { // Add any port output routes to this track const RouteList* rl = _track->inRoutes(); for(ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::MIDI_PORT_ROUTE: { Route src(_track, r->channel); MusEGlobal::midiPorts[r->midiPort].outRoutes()->push_back(src); flags |= SC_ROUTE; } break; case Route::TRACK_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } // Add any port input routes from this track rl = _track->outRoutes(); for(ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::MIDI_PORT_ROUTE: { Route src(_track, r->channel); MusEGlobal::midiPorts[r->midiPort].inRoutes()->push_back(src); flags |= SC_ROUTE; } break; case Route::TRACK_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } } else { // Add other tracks' output routes to this track const RouteList* rl = _track->inRoutes(); for(ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::TRACK_ROUTE: { Route src(_track, r->remoteChannel, r->channels); src.remoteChannel = r->channel; r->track->outRoutes()->push_back(src); flags |= SC_ROUTE; // Is the source an Aux Track or else does it have Aux Tracks routed to it? // Update this track's aux ref count. if(r->track->auxRefCount()) { _track->updateAuxRoute(r->track->auxRefCount(), NULL); } else if(r->track->type() == Track::AUDIO_AUX) { _track->updateAuxRoute(1, NULL); } } break; case Route::MIDI_PORT_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } // Add other tracks' input routes from this track rl = _track->outRoutes(); for(ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::TRACK_ROUTE: { Route src(_track, r->remoteChannel, r->channels); src.remoteChannel = r->channel; r->track->inRoutes()->push_back(src); flags |= SC_ROUTE; // Is this track an Aux Track or else does it have Aux Tracks routed to it? // Update the other track's aux ref count and all tracks it is connected to. if(_track->auxRefCount()) { r->track->updateAuxRoute(_track->auxRefCount(), NULL); } else if(_track->type() == Track::AUDIO_AUX) { r->track->updateAuxRoute(1, NULL); } } break; case Route::MIDI_PORT_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } } chainTrackParts(_track); // Be sure to mark the parts as not deleted if they exist in the global copy/paste clone list. const PartList* pl = _track->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { if(i->cp == ip->second) i->is_deleted = false; } } } break; case DeleteTrack: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteTrack track_list:%p track:%p sec_track_list:%p\n", _track_list, _track, _void_track_list); unchainTrackParts(_track); if(_void_track_list) { switch(_track->type()) { case Track::MIDI: case Track::DRUM: static_cast(_void_track_list)->erase(_track); break; case Track::WAVE: static_cast(_void_track_list)->erase(_track); break; case Track::AUDIO_OUTPUT: static_cast(_void_track_list)->erase(_track); break; case Track::AUDIO_GROUP: static_cast(_void_track_list)->erase(_track); break; case Track::AUDIO_AUX: static_cast(_void_track_list)->erase(_track); // Special for aux, make it easier to detect their changes. flags |= SC_AUX; break; case Track::AUDIO_INPUT: static_cast(_void_track_list)->erase(_track); break; case Track::AUDIO_SOFTSYNTH: { static_cast(_void_track_list)->erase(_track); // Change all ports which used the instrument. // FIXME TODO: We want to make this undoable but ATM a few other things can // set the instrument without an undo operation so the undo sequence would // not be correct. So we don't have much choice but to just reset for now. // Still, everything else is in place for undoable setting of instrument... const SynthI* si = static_cast(_track); for(int port = 0; port < MusECore::MIDI_PORTS; ++port) { MidiPort* mp = &MusEGlobal::midiPorts[port]; if(mp->instrument() != si) continue; // Just revert to GM. mp->setInstrument(registerMidiInstrument("GM")); // Flag the port to send initializations next time it bothers to check. mp->clearInitSent(); flags |= SC_MIDI_INSTRUMENT; } } break; default: ERROR_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteTrack: Unknown track type %d\n", _track->type()); return flags; } } _track_list->erase(_track); flags |= SC_TRACK_REMOVED; // Remove routes: if(_track->type() == Track::AUDIO_OUTPUT) { // Clear the track's jack ports for(int ch = 0; ch < _track->channels(); ++ch) { ((AudioOutput*)_track)->setJackPort(ch, 0); flags |= SC_ROUTE; } // Clear the track's output routes' jack ports RouteList* orl = _track->outRoutes(); for(iRoute r = orl->begin(); r != orl->end(); ++r) { if(r->type != Route::JACK_ROUTE) continue; r->jackPort = 0; flags |= SC_ROUTE; } } else if(_track->type() == Track::AUDIO_INPUT) { // Clear the track's jack ports for(int ch = 0; ch < _track->channels(); ++ch) { ((AudioInput*)_track)->setJackPort(ch, 0); flags |= SC_ROUTE; } // Clear the track's input routes' jack ports RouteList* irl = _track->inRoutes(); for(iRoute r = irl->begin(); r != irl->end(); ++r) { if(r->type != Route::JACK_ROUTE) continue; r->jackPort = 0; flags |= SC_ROUTE; } } if(_track->isMidiTrack()) { // Remove any port output routes to this track const RouteList* rl = _track->inRoutes(); for(ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::MIDI_PORT_ROUTE: { Route src(_track, r->channel); MusEGlobal::midiPorts[r->midiPort].outRoutes()->removeRoute(src); flags |= SC_ROUTE; } break; case Route::TRACK_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } // Remove any port input routes from this track rl = _track->outRoutes(); for(ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::MIDI_PORT_ROUTE: { Route src(_track, r->channel); MusEGlobal::midiPorts[r->midiPort].inRoutes()->removeRoute(src); flags |= SC_ROUTE; } break; case Route::TRACK_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } } else { // Remove other tracks' output routes to this track const RouteList* rl = _track->inRoutes(); for(ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::TRACK_ROUTE: { Route src(_track, r->remoteChannel, r->channels); src.remoteChannel = r->channel; r->track->outRoutes()->removeRoute(src); flags |= SC_ROUTE; // Is the source an Aux Track or else does it have Aux Tracks routed to it? // Update this track's aux ref count. if(r->track->auxRefCount()) { _track->updateAuxRoute(-r->track->auxRefCount(), NULL); } else if(r->track->type() == Track::AUDIO_AUX) { _track->updateAuxRoute(-1, NULL); } } break; case Route::MIDI_PORT_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } // Remove other tracks' input routes from this track rl = _track->outRoutes(); for(ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::TRACK_ROUTE: { Route src(_track, r->remoteChannel, r->channels); src.remoteChannel = r->channel; r->track->inRoutes()->removeRoute(src); flags |= SC_ROUTE; // Is this track an Aux Track or else does it have Aux Tracks routed to it? // Update the other track's aux ref count and all tracks it is connected to. if(_track->auxRefCount()) { r->track->updateAuxRoute(-_track->auxRefCount(), NULL); } else if(_track->type() == Track::AUDIO_AUX) { r->track->updateAuxRoute(-1, NULL); } } break; case Route::MIDI_PORT_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } } // Be sure to mark the parts as deleted if they exist in the global copy/paste clone list. const PartList* pl = _track->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { if(i->cp == ip->second) i->is_deleted = true; } } } break; case MoveTrack: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage MoveTrack from:%d to:%d\n", _from_idx, _to_idx); const int sz = _track_list->size(); if(_from_idx >= sz) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationItem::executeRTStage MoveTrack from index out of range:%d\n", _from_idx); return flags; } ciTrack fromIt = _track_list->cbegin() + _from_idx; Track* track = *fromIt; _track_list->erase(fromIt); ciTrack toIt = (_to_idx >= sz) ? _track_list->cend() : _track_list->cbegin() + _to_idx; _track_list->insert(toIt, track); flags |= SC_TRACK_MOVED; } break; case ModifyTrackName: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyTrackName track:%p new_val:%s\n", _track, _name->toLocal8Bit().data()); _track->setName(*_name); flags |= (SC_TRACK_MODIFIED | SC_MIDI_TRACK_PROP); // If it's an aux track, notify aux UI controls to reload, or change their names etc. if(_track->type() == Track::AUDIO_AUX) flags |= SC_AUX; break; case SetTrackRecord: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage SetTrackRecord track:%p new_val:%d\n", _track, _boolA); const bool mon = _track->setRecordFlag2AndCheckMonitor(_boolA); flags |= SC_RECFLAG; if(mon) flags |= SC_TRACK_REC_MONITOR; } break; case SetTrackMute: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage SetTrackMute track:%p new_val:%d\n", _track, _boolA); _track->setMute(_boolA); flags |= SC_MUTE; break; case SetTrackSolo: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage SetTrackSolo track:%p new_val:%d\n", _track, _boolA); _track->setSolo(_boolA); flags |= SC_SOLO; break; case SetTrackRecMonitor: #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage SetTrackRecMonitor track:%p new_val:%d\n", _track, _boolA); #endif _track->setRecMonitor(_boolA); flags |= SC_TRACK_REC_MONITOR; break; case SetTrackOff: #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage SetTrackOff track:%p new_val:%d\n", _track, _boolA); #endif _track->setOff(_boolA); flags |= SC_MUTE; break; case AddPart: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddPart part:%p\n", _part); _part_list->add(_part); _part->rechainClone(); // Be sure to mark the part as not deleted if it exists in the global copy/paste clone list. for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { if(i->cp == _part) i->is_deleted = false; } flags |= SC_PART_INSERTED; // If the part has events, then treat it as if they were inserted with separate AddEvent operations. // Even if some will be inserted later in this operations group with actual separate AddEvent operations, // that's an SC_EVENT_INSERTED anyway, so hopefully no harm. if(!_part->events().empty()) flags |= SC_EVENT_INSERTED; break; case DeletePart: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeletePart part:%p\n", _iPart->second); Part* p = _iPart->second; _part_list->erase(_iPart); p->unchainClone(); // Be sure to mark the part as deleted if it exists in the global copy/paste clone list. for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { if(i->cp == p) i->is_deleted = true; } flags |= SC_PART_REMOVED; // If the part had events, then treat it as if they were removed with separate DeleteEvent operations. // Even if they will be deleted later in this operations group with actual separate DeleteEvent operations, // that's an SC_EVENT_REMOVED anyway, so hopefully no harm. This fixes a problem with midi controller canvas // not updating after such a 'delete part with events, no separate AddEvents were used when creating the part'. if(!p->events().empty()) flags |= SC_EVENT_REMOVED; } break; case ModifyPartStart: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyPartStart part:%p old_val:%d new_val:%u\n", _part, _part->posValue(), _posLenVal); // Since we are modifying a part's position we must remove the part from the list // and reinsert it afterwards for proper sorting (position is the sorting key). if(_part->track() && _iPart != _part->track()->parts()->end()) { _part->track()->parts()->erase(_iPart); flags |= SC_PART_REMOVED; } // Was an event list supplied to be wholesale swapped? if(_event_list) { // Since the original event list is not an allocated pointer, there are no pointers to quickly exchange. // Instead use swap() which is also constant in time. Just like quickly exchanging pointers. // Transfers the original list back to _event_list so it can be deleted in the non-RT stage. (&_part->nonconst_events())->swap(*_event_list); flags |= (SC_EVENT_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED); } _part->setLenValue(_lenVal); _part->setPosValue(_posLenVal); if(_part->track()) { _part->track()->parts()->add(_part); flags |= SC_PART_INSERTED; } flags |= SC_PART_MODIFIED; } break; case ModifyPartLength: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyPartLength part:%p old_val:%d new_val:%u\n", _part, _part->lenValue(), _posLenVal); // Was an event list supplied to be wholesale swapped? if(_event_list) { // Since the original event list is not an allocated pointer, there are no pointers to quickly exchange. // Instead use swap() which is also constant in time. Just like quickly exchanging pointers. // Transfers the original list back to _event_list so it can be deleted in the non-RT stage. (&_part->nonconst_events())->swap(*_event_list); flags |= (SC_EVENT_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED); } _part->setLenValue(_posLenVal); flags |= SC_PART_MODIFIED; } break; case MovePart: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage MovePart part:%p track:%p new_pos:%u\n", _part, _track, _posLenVal); if(_track) { if(_part->track() && _iPart != _part->track()->parts()->end()) { _part->track()->parts()->erase(_iPart); flags |= SC_PART_REMOVED; } _part->setTrack(_track); //_part->setTick(_posLenVal); _part->setPosValue(_posLenVal); _track->parts()->add(_part); flags |= SC_PART_INSERTED; } else { //_part->setTick(_posLenVal); _part->setPosValue(_posLenVal); } flags |= SC_PART_MODIFIED; break; case SelectPart: #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage SelectPart part:%p select:%u\n", _part, _boolA); #endif if(_part) _part->setSelected(_boolA); flags |= SC_PART_SELECTION; break; case ModifyPartName: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyPartName part:%p new_val:%s\n", _part, _name->toLocal8Bit().data()); _part->setName(*_name); flags |= SC_PART_MODIFIED; break; case AddEvent: #ifdef _PENDING_OPS_DEBUG_ DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddEvent pre: "); _ev.dump(); #endif _part->addEvent(_ev); #ifdef _PENDING_OPS_DEBUG_ DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddEvent post: "); _ev.dump(); #endif flags |= SC_EVENT_INSERTED; break; case DeleteEvent: #ifdef _PENDING_OPS_DEBUG_ DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteEvent pre: "); _ev.dump(); #endif _part->nonconst_events().erase(_iev); #ifdef _PENDING_OPS_DEBUG_ DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteEvent post: "); _ev.dump(); #endif flags |= SC_EVENT_REMOVED; break; case SelectEvent: #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage SelectEvent part:%p select:%d\n", _part, _intA); #endif // Make sure we let song handle this important job, it selects corresponding events in clone parts. MusEGlobal::song->selectEvent(_ev, _part, _intA); flags |= SC_SELECTION; break; case ModifyEventList: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyEventList: orig eventlist:%p new eventlist:%p\n", _orig_event_list, _event_list); if(_orig_event_list && _event_list) { // Since the original event list is not an allocated pointer, there are no pointers to quickly exchange. // Instead use swap() which is also constant in time. Just like quickly exchanging pointers. // Transfers the original list back to _event_list so it can be deleted in the non-RT stage. _orig_event_list->swap(*_event_list); flags |= (SC_EVENT_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED); } break; case AddMidiCtrlValList: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddMidiCtrlValList: mcvll:%p mcvl:%p chan:%d\n", _mcvll, _mcvl, _intA); _mcvll->add(_intA, _mcvl); flags |= SC_MIDI_CONTROLLER_ADD; break; case ModifyMidiCtrlValList: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyMidiCtrlValList: orig_mcvl:%p mcvl:%p\n", _orig_mcvl, _mcvl); if(_orig_mcvl && _mcvl) { // Since the original list is not an allocated pointer, there are no pointers to quickly exchange. // Instead use swap() which is also constant in time. Just like quickly exchanging pointers. // Transfers the original list back to _mcvl so it can be deleted in the non-RT stage. _orig_mcvl->swap(*_mcvl); // No song changed flags are required to be set here. } break; case AddMidiCtrlVal: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddMidiCtrlVal: mcvl:%p part:%p tick:%u val:%d\n", _mcvl, _part, _posLenVal, _intB); // Do not attempt to add cached events which are outside of the part. // But do allow muted parts, and muted tracks, and 'off' tracks. Otherwise adding values // to muted parts fails to add them when unmuted. The cache mechanism catches this anyways. #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)_posLenVal >= (int)_part->posValue() && (int)_posLenVal < (int)_part->posValue() + (int)_part->lenValue()) #else if(_posLenVal >= _part->posValue() && _posLenVal < _part->posValue() + _part->lenValue()) #endif // FIXME FINDMICHJETZT XTicks!! _mcvl->insert(MidiCtrlValListInsertPair_t(_posLenVal, MidiCtrlVal(_part, _intB))); // No song changed flags are required to be set here. break; case DeleteMidiCtrlVal: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteMidiCtrlVal: mcvl:%p tick:%u part:%p val:%d\n", _mcvl, _imcv->first, _imcv->second.part, _imcv->second.val); _mcvl->erase(_imcv); // No song changed flags are required to be set here. break; case ModifyMidiCtrlVal: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyMidiCtrlVal: part:%p old_val:%d new_val:%d\n", _imcv->second.part, _imcv->second.val, _intA); _imcv->second.val = _intA; break; case ModifyAudioCtrlValList: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyAudioCtrlValList: old ctrl_l:%p new ctrl_l:%p\n", _iCtrlList->second, _aud_ctrl_list); CtrlList* orig = _iCtrlList->second; _iCtrlList->second = _aud_ctrl_list; // Transfer the original pointer back to _aud_ctrl_list so it can be deleted in the non-RT stage. _aud_ctrl_list = orig; flags |= SC_AUDIO_CONTROLLER_LIST; } break; case AddAudioCtrlVal: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddAudioCtrlVal: ctrl_l:%p frame:%u val:%f\n", _aud_ctrl_list, _posLenVal, _ctl_dbl_val); //_aud_ctrl_list->insert(CtrlListInsertPair_t(_posLenVal, CtrlVal(_posLenVal, _ctl_dbl_val))); // Add will replace if found. _aud_ctrl_list->add(_posLenVal, _ctl_dbl_val); flags |= SC_AUDIO_CONTROLLER; break; case DeleteAudioCtrlVal: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteAudioCtrlVal: ctrl_l:%p ctrl_num:%d frame:%d val:%f\n", _aud_ctrl_list, _aud_ctrl_list->id(), _iCtrl->first, _iCtrl->second.val); _aud_ctrl_list->erase(_iCtrl); flags |= SC_AUDIO_CONTROLLER; break; case ModifyAudioCtrlVal: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyAudioCtrlVal: frame:%u old_val:%f new_val:%f\n", _iCtrl->first, _iCtrl->second.val, _ctl_dbl_val); // If the frame is the same, just change the value. if(_iCtrl->second.frame == _posLenVal) { _iCtrl->second.val = _ctl_dbl_val; } // Otherwise erase + add is required. else { _aud_ctrl_list->erase(_iCtrl); _aud_ctrl_list->insert(CtrlListInsertPair_t(_posLenVal, CtrlVal(_posLenVal, _ctl_dbl_val))); } flags |= SC_AUDIO_CONTROLLER; break; case ModifyTempoList: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyTempoList: orig tempolist:%p new tempolist:%p\n", _orig_tempo_list, _tempo_list); if(_orig_tempo_list && _tempo_list) { // Since the original tempo list is not an allocated pointer, there are no pointers to quickly exchange. // Instead use swap() which is also constant in time. Just like quickly exchanging pointers. // Transfers the original list back to _tempo_list so it can be deleted in the non-RT stage. _orig_tempo_list->swap(*_tempo_list); flags |= SC_TEMPO; } break; case SetStaticTempo: #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage SetStaticTempo: tempolist:%p new_tempo:%d\n", _tempo_list, _intA); #endif _tempo_list->setStaticTempo(_intA); flags |= SC_TEMPO; break; case SetGlobalTempo: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage SetGlobalTempo: tempolist:%p new_tempo:%d\n", _tempo_list, _intA); _tempo_list->setGlobalTempo(_intA); flags |= SC_TEMPO; break; case ModifySigList: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifySigList: orig siglist:%p new siglist:%p\n", _orig_sig_list, _sig_list); if(_orig_sig_list && _sig_list) { // Since the original sig list is not an allocated pointer, there are no pointers to quickly exchange. // Instead use swap() which is also constant in time. Just like quickly exchanging pointers. // Transfers the original list back to _sig_list so it can be deleted in the non-RT stage. _orig_sig_list->swap(*_sig_list); flags |= SC_SIG; } break; case ModifyKeyList: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyKeyList: orig keylist:%p new keylist:%p\n", _orig_key_list, _key_list); if(_orig_key_list && _key_list) { // Since the original key list is not an allocated pointer, there are no pointers to quickly exchange. // Instead use swap() which is also constant in time. Just like quickly exchanging pointers. // Transfers the original list back to _sig_list so it can be deleted in the non-RT stage. _orig_key_list->swap(*_key_list); flags |= SC_KEY; } break; case ModifyStretchListRatio: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyStretchListRatio: stretchType:%d stretchlist:%p new_ratio:%f\n", _stretch_type, _stretch_list, _audio_converter_value); // Defer normalize until end of stage 2. _stretch_list->setRatio(StretchListItem::StretchEventType(_stretch_type), _audio_converter_value, false); flags |= SC_AUDIO_STRETCH; break; case AddStretchListRatioAt: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddStretchListRatioAt: stretchType:%d stretchlist:%p ratio:%f frame:%ld\n", _stretch_type, _stretch_list, _audio_converter_value, _museFrame); // Defer normalize until end of stage 2. _stretch_list->addRatioAt(StretchListItem::StretchEventType(_stretch_type), _museFrame, _audio_converter_value, false); flags |= SC_AUDIO_STRETCH; break; case DeleteStretchListRatioAt: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteStretchListRatioAt: stretchlist:%p frame:%ld types:%d\n", _stretch_list, _iStretchEvent->first, _stretch_type); // Defer normalize until end of stage 2. _stretch_list->del(_stretch_type, _iStretchEvent, false); flags |= SC_AUDIO_STRETCH; break; case ModifyStretchListRatioAt: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyStretchListRatioAt: " "stretchType:%d stretchlist:%p frame:%ld new_frame:%ld new_ratio:%f\n", _stretch_type, _stretch_list, _iStretchEvent->first, _museFrame, _audio_converter_value); // If the frame is the same, just change the value. if(_iStretchEvent->first == _museFrame) // Defer normalize until end of stage 2. _stretch_list->setRatioAt(StretchListItem::StretchEventType(_stretch_type), _iStretchEvent, _audio_converter_value, false); // Otherwise erase + add is required. else { // Defer normalize until end of stage 2. _stretch_list->del(_stretch_type, _iStretchEvent, false); _stretch_list->add(StretchListItem::StretchEventType(_stretch_type), _museFrame, _audio_converter_value, false); } flags |= SC_AUDIO_STRETCH; break; case ModifySongLength: DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifySongLength: len:%d\n", _posLenVal); MusEGlobal::song->setLen(_posLenVal, false); // false = Do not emit update signals here ! flags |= SC_EVERYTHING; break; case EnableAllAudioControllers: { DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage EnableAllAudioControllers\n"); TrackList* tl = MusEGlobal::song->tracks(); for (iTrack it = tl->begin(); it != tl->end(); ++it) { Track* t = *it; if(t->isMidiTrack()) continue; AudioTrack *at = static_cast(t); // Re-enable all track and plugin controllers, and synth controllers if applicable. at->enableAllControllers(); flags |= SC_AUDIO_CONTROLLER; } } break; case GlobalSelectAllEvents: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage GlobalSelectAllEvents\n"); #endif for (iTrack it = _track_list->begin(); it != _track_list->end(); ++it) { //Track* t = *it; //if(t->isMidiTrack()) // continue; if((*it)->selectEvents(_boolA)) flags |= SC_SELECTION; } } break; case ModifyAudioSamples: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage ModifyAudioSamples: " "audioSamplesPointer:%p newAudioSamples:%p audioSamplesLen:%p newAudioSamplesLen:%d\n", _audioSamplesPointer, _newAudioSamples, _audioSamplesLen, _newAudioSamplesLen); #endif if(_audioSamplesPointer) { float* orig = *_audioSamplesPointer; *_audioSamplesPointer = _newAudioSamples; // Transfer the original pointer back to _audioSamplesPointer so it can be deleted in the non-RT stage. _newAudioSamples = orig; } if(_audioSamplesLen) *_audioSamplesLen = _newAudioSamplesLen; // Currently no flags for this. //flags |= SC_; } break; case ModifyMarkerList: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage ModifyMarkerList: " "orig list:%p new list:%p\n", _orig_marker_list, _marker_list); #endif if(_orig_marker_list && _marker_list) { MarkerList* orig = *_orig_marker_list; *_orig_marker_list = _marker_list; // Transfer the original pointer back to _marker_list so it can be deleted in the non-RT stage. _marker_list = orig; } // Currently no flags for this. //flags |= SC_MARKERS_REBUILT; } break; case SwitchMetronomeSettings: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage SwitchMetronomeSettings: settings:%p val:%d\n", _bool_pointer, _boolA); #endif *_bool_pointer = _boolA; flags |= SC_METRONOME; } break; case ModifyMetronomeAccentMap: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage ModifyMetronomeAccentMap: old map:%p new map:%p\n", _metroAccentsMap, _newMetroAccentsMap); #endif MetroAccentsMap* orig = *_metroAccentsMap; *_metroAccentsMap = _newMetroAccentsMap; // Transfer the original pointer back to _newMetroAccentsMap so it can be deleted in the non-RT stage. _newMetroAccentsMap = orig; flags |= SC_METRONOME; } break; case SetExternalSyncFlag: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage SetExternalSyncFlag: pointer:%p val:%d\n", _bool_pointer, _boolA); #endif *_bool_pointer = _boolA; flags |= SC_EXTERNAL_MIDI_SYNC; } break; case SetUseJackTransport: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage SetUseJackTransport: pointer:%p val:%d\n", _bool_pointer, _boolA); #endif *_bool_pointer = _boolA; flags |= SC_USE_JACK_TRANSPORT; } break; case SetUseMasterTrack: { #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationItem::executeRTStage SetUseMasterTrack: pointer:%p val:%d\n", _tempo_list, _boolA); #endif _tempo_list->setMasterFlag(0, _boolA); flags |= SC_MASTER; } break; case Uninitialized: break; default: ERROR_OPERATIONS(stderr, "PendingOperationItem::executeRTStage unknown type %d\n", _type); break; } return flags; } SongChangedStruct_t PendingOperationItem::executeNonRTStage() { SongChangedStruct_t flags = 0; switch(_type) { case ModifyPartStart: case ModifyPartLength: // At this point _event_list contains all the items that were in the original event list, via swap(). Delete it now. if(_event_list) delete _event_list; break; case ModifyEventList: // At this point _event_list contains all the items that were in the original event list, via swap(). Delete it now. if(_event_list) delete _event_list; break; case ModifyMidiCtrlValList: // At this point _mcvl contains all the items that were in the original event list, via swap(). Delete it now. if(_mcvl) delete _mcvl; break; case AddRoute: if(MusEGlobal::song->connectJackRoutes(_src_route, _dst_route)) flags |= SC_ROUTE; break; case DeleteRoute: if(MusEGlobal::song->connectJackRoutes(_src_route, _dst_route, true)) flags |= SC_ROUTE; break; case ModifyTempoList: // At this point _tempo_list contains all the items that were in the original tempo list, via swap(). Delete it now. if(_tempo_list) delete _tempo_list; break; case ModifySigList: // At this point _sig_list contains all the items that were in the original sig list, via swap(). Delete it now. if(_sig_list) delete _sig_list; break; case ModifyKeyList: // At this point _key_list contains all the items that were in the original key list, via swap(). Delete it now. if(_key_list) delete _key_list; break; case ModifyLocalAudioConverterSettings: // At this point these are the original pointers that were replaced. Delete the original objects now. if(_audio_converter_settings) delete _audio_converter_settings; break; case ModifyLocalAudioConverter: // At this point these are the original pointers that were replaced. Delete the original objects now. if(_audio_converter) delete _audio_converter; if(_audio_converter_ui) delete _audio_converter_ui; break; case SetAudioConverterOfflineMode: // At this point this is the original pointer that were replaced. Delete the original object now. if(_audio_converter) delete _audio_converter; break; case ModifyDefaultAudioConverterSettings: // At this point this is the original pointer that was replaced. Delete the original object now. if(_audio_converter_settings) delete _audio_converter_settings; break; case ReplaceMidiInstrument: // At this point _midi_instrument is the original instrument that was replaced. Delete it now. if(_midi_instrument) delete _midi_instrument; break; case ModifyAudioCtrlValList: // At this point _aud_ctrl_list is the original list that was replaced. Delete it now. if(_aud_ctrl_list) delete _aud_ctrl_list; break; case ModifyTrackDrumMapItem: // Discard the operation, it has already completed. if(_drum_map_track_operation) delete _drum_map_track_operation; break; case ReplaceTrackDrumMapPatchList: // Discard the operation, it has already completed. if(_drum_map_track_patch_operation) { // At this point _workingItemPatchList is the original list that was replaced. Delete it now. if(_drum_map_track_patch_replace_operation->_workingItemPatchList) delete _drum_map_track_patch_replace_operation->_workingItemPatchList; delete _drum_map_track_patch_replace_operation; } break; case RemapDrumControllers: // Discard the operation, it has already completed. if(_midi_ctrl_val_remap_operation) { // At this point _midiCtrlValLists2bDeleted contains the original lists that were replaced. Delete them now. for(iMidiCtrlValLists2bDeleted_t imvld = _midi_ctrl_val_remap_operation->_midiCtrlValLists2bDeleted.begin(); imvld != _midi_ctrl_val_remap_operation->_midiCtrlValLists2bDeleted.end(); ++imvld) delete *imvld; delete _midi_ctrl_val_remap_operation; } break; case ModifyAudioSamples: // At this point _newAudioSamples points to the original memory that was replaced. Delete it now. if(_newAudioSamples) delete _newAudioSamples; break; case ModifyMarkerList: // At this point _marker_list points to the original memory that was replaced. Delete it now. if(_marker_list) delete _marker_list; break; case ModifyMetronomeAccentMap: // At this point _newMetroAccentsMap is the original list that was replaced. Delete it now. if(_newMetroAccentsMap) delete _newMetroAccentsMap; break; default: break; } return flags; } SongChangedStruct_t PendingOperationList::executeRTStage() { DEBUG_OPERATIONS(stderr, "PendingOperationList::executeRTStage executing...\n"); for(iPendingOperation ip = begin(); ip != end(); ++ip) _sc_flags |= ip->executeRTStage(); // To avoid doing this item by item, do it here. if(_sc_flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_ROUTE)) { MusEGlobal::song->updateSoloStates(); _sc_flags |= SC_SOLO; } // To avoid doing this item by item, do it here. StretchList* sl; for(iPendingOperation ip = begin(); ip != end(); ++ip) { const PendingOperationItem& poi = *ip; switch(poi._type) { //case PendingOperationItem::AddSamplerateRatioAt: //case PendingOperationItem::DeleteSamplerateRatioAt: //case PendingOperationItem::ModifySamplerateRatioAt: case PendingOperationItem::AddStretchListRatioAt: case PendingOperationItem::DeleteStretchListRatioAt: case PendingOperationItem::ModifyStretchListRatioAt: case PendingOperationItem::ModifyStretchListRatio: sl = poi._stretch_list; if(sl && !sl->isNormalized()) { sl->normalizeListFrames(); _sc_flags |= SC_AUDIO_STRETCH; } break; default: break; } } return _sc_flags; } SongChangedStruct_t PendingOperationList::executeNonRTStage() { DEBUG_OPERATIONS(stderr, "PendingOperationList::executeNonRTStage executing...\n"); for(iPendingOperation ip = begin(); ip != end(); ++ip) _sc_flags |= ip->executeNonRTStage(); return _sc_flags; } void PendingOperationList::clear() { _sc_flags = 0; _map.clear(); std::list::clear(); DEBUG_OPERATIONS(stderr, "PendingOperationList::clear * post map size:%d list size:%d\n", (int)_map.size(), (int)size()); } bool PendingOperationList::add(PendingOperationItem op) { unsigned int t = op.getIndex(); switch(op._type) { // For these special allocation ops, searching has already been done before hand. Just add them. case PendingOperationItem::AddMidiCtrlValList: { iPendingOperation iipo = insert(end(), op); _map.insert(std::pair(t, iipo)); return true; } break; default: break; } iPendingOperationSortedRange r = _map.equal_range(t); iPendingOperationSorted ipos = r.second; while(ipos != r.first) { --ipos; PendingOperationItem& poi = *ipos->second; switch(op._type) { case PendingOperationItem::ModifyDefaultAudioConverterSettings: if(poi._type == PendingOperationItem::ModifyDefaultAudioConverterSettings && poi._audio_converter_settings == op._audio_converter_settings) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyDefaultAudioConverterSettings. Ignoring.\n"); return false; } break; case PendingOperationItem::ModifyLocalAudioConverterSettings: if(poi._type == PendingOperationItem::ModifyLocalAudioConverterSettings && *poi._sndFileR == *op._sndFileR && poi._audio_converter_settings == op._audio_converter_settings) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyLocalAudioConverterSettings. Ignoring.\n"); return false; } break; case PendingOperationItem::ModifyLocalAudioConverter: if(poi._type == PendingOperationItem::ModifyLocalAudioConverter && *poi._sndFileR == *op._sndFileR && poi._audio_converter == op._audio_converter && poi._audio_converter_ui == op._audio_converter_ui) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyLocalAudioConverter. Ignoring.\n"); return false; } break; case PendingOperationItem::SetAudioConverterOfflineMode: if(poi._type == PendingOperationItem::SetAudioConverterOfflineMode && *poi._sndFileR == *op._sndFileR && poi._audio_converter == op._audio_converter) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double SetAudioConverterOfflineMode. Ignoring.\n"); return false; } break; case PendingOperationItem::ModifyTrackDrumMapItem: if(poi._type == PendingOperationItem::ModifyTrackDrumMapItem && poi._drum_map_track_operation == op._drum_map_track_operation) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double ModifyTrackDrumMapItem. Ignoring.\n"); return false; } break; case PendingOperationItem::ReplaceTrackDrumMapPatchList: if(poi._type == PendingOperationItem::ReplaceTrackDrumMapPatchList && poi._drum_map_track_patch_replace_operation == op._drum_map_track_patch_replace_operation) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double ReplaceTrackDrumMapPatchList. Ignoring.\n"); return false; } break; case PendingOperationItem::RemapDrumControllers: if(poi._type == PendingOperationItem::RemapDrumControllers && poi._midi_ctrl_val_remap_operation == op._midi_ctrl_val_remap_operation) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double RemapDrumControllers. Ignoring.\n"); return false; } break; case PendingOperationItem::UpdateDrumMaps: if(poi._type == PendingOperationItem::UpdateDrumMaps && poi._midi_port == op._midi_port) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double UpdateDrumMaps. Ignoring.\n"); return false; } break; case PendingOperationItem::UpdateSoloStates: if(poi._type == PendingOperationItem::UpdateSoloStates && poi._track_list == op._track_list) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double UpdateSoloStates. Ignoring.\n"); return false; } break; case PendingOperationItem::AddRoute: if(poi._type == PendingOperationItem::AddRoute && poi._src_route == op._src_route && poi._dst_route == op._dst_route) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddRoute. Ignoring.\n"); return false; } break; case PendingOperationItem::DeleteRoute: if(poi._type == PendingOperationItem::DeleteRoute && poi._src_route == op._src_route && poi._dst_route == op._dst_route) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteRoute. Ignoring.\n"); return false; } break; case PendingOperationItem::AddRouteNode: if(poi._type == PendingOperationItem::AddRouteNode && poi._route_list == op._route_list && poi._src_route == op._src_route) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddRouteNode. Ignoring.\n"); return false; } break; case PendingOperationItem::DeleteRouteNode: if(poi._type == PendingOperationItem::DeleteRouteNode && poi._route_list == op._route_list && poi._iRoute == op._iRoute) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteRouteNode. Ignoring.\n"); return false; } break; case PendingOperationItem::ModifyRouteNode: if(poi._type == PendingOperationItem::ModifyRouteNode && poi._src_route == op._src_route && poi._dst_route_pointer == op._dst_route_pointer) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyRouteNode. Ignoring.\n"); return false; } break; case PendingOperationItem::AddAuxSendValue: if(poi._type == PendingOperationItem::AddAuxSendValue && poi._aux_send_value_list == op._aux_send_value_list) { // Do nothing. So far. } break; case PendingOperationItem::AddMidiInstrument: if(poi._type == PendingOperationItem::AddMidiInstrument && poi._midi_instrument_list == op._midi_instrument_list && poi._midi_instrument == op._midi_instrument) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddMidiInstrument. Ignoring.\n"); return false; } break; case PendingOperationItem::DeleteMidiInstrument: if(poi._type == PendingOperationItem::DeleteMidiInstrument && poi._midi_instrument_list == op._midi_instrument_list && poi._iMidiInstrument == op._iMidiInstrument) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteMidiInstrument. Ignoring.\n"); return false; } break; case PendingOperationItem::ReplaceMidiInstrument: if(poi._type == PendingOperationItem::ReplaceMidiInstrument && poi._midi_instrument_list == op._midi_instrument_list && (poi._midi_instrument == op._midi_instrument || poi._iMidiInstrument == op._iMidiInstrument)) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double ReplaceMidiInstrument. Ignoring.\n"); return false; } break; case PendingOperationItem::AddMidiDevice: if(poi._type == PendingOperationItem::AddMidiDevice && poi._midi_device_list == op._midi_device_list && poi._midi_device == op._midi_device) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddMidiDevice. Ignoring.\n"); return false; } break; case PendingOperationItem::DeleteMidiDevice: if(poi._type == PendingOperationItem::DeleteMidiDevice && poi._midi_device_list == op._midi_device_list && poi._iMidiDevice == op._iMidiDevice) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteMidiDevice. Ignoring.\n"); return false; } break; case PendingOperationItem::ModifyMidiDeviceAddress: if(poi._type == PendingOperationItem::ModifyMidiDeviceAddress && poi._midi_device == op._midi_device && poi._address_client == op._address_client && poi._address_port == op._address_port) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyMidiDeviceAddress. Ignoring.\n"); return false; } break; case PendingOperationItem::ModifyMidiDeviceFlags: if(poi._type == PendingOperationItem::ModifyMidiDeviceFlags && poi._midi_device == op._midi_device && poi._rw_flags == op._rw_flags && poi._open_flags == op._open_flags) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyMidiDeviceFlags. Ignoring.\n"); return false; } break; case PendingOperationItem::ModifyMidiDeviceName: if(poi._type == PendingOperationItem::ModifyMidiDeviceName && poi._midi_device == op._midi_device && poi._name == op._name) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyMidiDeviceName. Ignoring.\n"); return false; } break; case PendingOperationItem::SetInstrument: if(poi._type == PendingOperationItem::SetInstrument && poi._midi_port == op._midi_port && poi._midi_instrument == op._midi_instrument) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetInstrument. Ignoring.\n"); return false; } break; case PendingOperationItem::AddTrack: if(poi._type == PendingOperationItem::AddTrack && poi._track_list == op._track_list && poi._track == op._track) { // Simply replace the insert point. poi._insert_at = op._insert_at; // An operation will still take place. return true; } else if(poi._type == PendingOperationItem::DeleteTrack && poi._track_list == op._track_list && poi._track == op._track) { // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. //erase(ipos->second); //_map.erase(ipos); // No operation will take place. //return false; } break; case PendingOperationItem::DeleteTrack: if(poi._type == PendingOperationItem::DeleteTrack && poi._track_list == op._track_list && poi._track == op._track) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteTrack. Ignoring.\n"); return false; } else if(poi._type == PendingOperationItem::AddTrack && poi._track_list == op._track_list && poi._track == op._track) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. //erase(ipos->second); //_map.erase(ipos); // No operation will take place. //return false; } break; case PendingOperationItem::MoveTrack: if(poi._type == PendingOperationItem::MoveTrack && poi._track == op._track && poi._track_list == op._track_list) { // Simply replace the 'to' index. poi._to_idx = op._to_idx; // An operation will still take place. return true; } break; case PendingOperationItem::ModifyTrackName: if(poi._type == PendingOperationItem::ModifyTrackName && poi._track == op._track && poi._name == op._name) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyTrackName. Ignoring.\n"); return false; } break; case PendingOperationItem::SetTrackRecord: if(poi._type == PendingOperationItem::SetTrackRecord && poi._track == op._track) { if(poi._boolA == op._boolA) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double SetTrackRecord. Ignoring.\n"); return false; } else { // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } } break; case PendingOperationItem::SetTrackMute: if(poi._type == PendingOperationItem::SetTrackMute && poi._track == op._track) { if(poi._boolA == op._boolA) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double SetTrackMute. Ignoring.\n"); return false; } else { // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } } break; case PendingOperationItem::SetTrackSolo: if(poi._type == PendingOperationItem::SetTrackSolo && poi._track == op._track) { if(poi._boolA == op._boolA) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double SetTrackSolo. Ignoring.\n"); return false; } else { // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } } break; case PendingOperationItem::SetTrackRecMonitor: if(poi._type == PendingOperationItem::SetTrackRecMonitor && poi._track == op._track) { if(poi._boolA == op._boolA) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetTrackRecMonitor. Ignoring.\n"); return false; } else { // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } } break; case PendingOperationItem::SetTrackOff: if(poi._type == PendingOperationItem::SetTrackOff && poi._track == op._track) { if(poi._boolA == op._boolA) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetTrackOff. Ignoring.\n"); return false; } else { // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } } break; case PendingOperationItem::AddPart: if(poi._type == PendingOperationItem::AddPart && poi._part_list == op._part_list && poi._part == op._part) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddPart. Ignoring.\n"); return false; } else if(poi._type == PendingOperationItem::DeletePart && poi._part_list == op._part_list && poi._iPart->second == op._part) { // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } break; case PendingOperationItem::DeletePart: if(poi._type == PendingOperationItem::DeletePart && poi._part_list == op._part_list && poi._iPart->second == op._iPart->second) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeletePart. Ignoring.\n"); return false; } else if(poi._type == PendingOperationItem::AddPart && poi._part_list == op._part_list && poi._part == op._iPart->second) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } break; case PendingOperationItem::SelectPart: if(poi._type == PendingOperationItem::SelectPart && poi._part == op._part) { // Simply replace the value. poi._boolA = op._boolA; // An operation will still take place. return true; } break; case PendingOperationItem::MovePart: if(poi._type == PendingOperationItem::MovePart && poi._part == op._part) { // Simply replace the values. poi._iPart = op._iPart; poi._track = op._track; poi._posLenVal = op._posLenVal; // An operation will still take place. return true; } break; case PendingOperationItem::ModifyPartStart: if(poi._type == PendingOperationItem::ModifyPartStart) { // If the given list is not null and is already part of a previous ModifyPartStart command, // it's an error, the list would be deleted twice. if(poi._part != op._part && op._event_list && op._event_list == poi._event_list) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): ModifyPartStart: Same _event_list for two different parts. Ignoring.\n"); return false; } if(poi._part == op._part) { // From here on in this block no matter what, we are re-using the existing command. // Simply replace the values. poi._posLenVal = op._posLenVal; poi._lenVal = op._lenVal; // If a list was given use it otherwise if no list was given don't touch the existing list. if(op._event_list) { // If the given list is the same as the existing list, it's really an error. We'll let it go but don't touch the existing list. // It should be safe to proceed without worrying about deleting the existing or given list here since it would be impossible // to allocate the same pointer twice, beforehand. if(op._event_list == poi._event_list) { //ERROR_OPERATIONS(stderr, "MusE warning: PendingOperationList::add(): ModifyPartStart: Double _event_list. Ignoring second list.\n"); //return false; } else { // Done with the existing original replacement list. If it exists, delete it. if(poi._event_list) delete poi._event_list; // Replace the existing list pointer with the given one. poi._event_list = op._event_list; } } // An operation will still take place. return true; } } break; case PendingOperationItem::ModifyPartLength: if(poi._type == PendingOperationItem::ModifyPartLength) { // If the given list is not null and is already part of a previous ModifyPartLength command, // it's an error, the list would be deleted twice. if(poi._part != op._part && op._event_list && op._event_list == poi._event_list) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): ModifyPartLength: Same _event_list for two different parts. Ignoring.\n"); return false; } if(poi._part == op._part) { // From here on in this block no matter what, we are re-using the existing command. // Simply replace the value. poi._posLenVal = op._posLenVal; // If a list was given use it otherwise if no list was given don't touch the existing list. if(op._event_list) { // If the given list is the same as the existing list, it's really an error. We'll let it go but don't touch the existing list. // It should be safe to proceed without worrying about deleting the existing or given list here since it would be impossible // to allocate the same pointer twice, beforehand. if(op._event_list == poi._event_list) { //ERROR_OPERATIONS(stderr, "MusE warning: PendingOperationList::add(): ModifyPartLength: Double _event_list. Ignoring second list.\n"); //return false; } else { // Done with the existing original replacement list. If it exists, delete it. if(poi._event_list) delete poi._event_list; // Replace the existing list pointer with the given one. poi._event_list = op._event_list; } } // An operation will still take place. return true; } } break; case PendingOperationItem::ModifyPartName: if(poi._type == PendingOperationItem::ModifyPartName && poi._part == op._part && poi._name == op._name) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyPartName. Ignoring.\n"); return false; } break; case PendingOperationItem::AddEvent: if(poi._type == PendingOperationItem::AddEvent && poi._part == op._part && poi._ev == op._ev) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddEvent. Ignoring.\n"); return false; } else if(poi._type == PendingOperationItem::DeleteEvent && poi._part == op._part && poi._iev->second == op._ev) { // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } break; case PendingOperationItem::DeleteEvent: if(poi._type == PendingOperationItem::DeleteEvent && poi._part == op._part && poi._iev->second == op._iev->second) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteEvent. Ignoring.\n"); return false; } else if(poi._type == PendingOperationItem::AddEvent && poi._part == op._part && poi._ev == op._iev->second) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } break; case PendingOperationItem::SelectEvent: if(poi._type == PendingOperationItem::SelectEvent && poi._part == op._part && poi._ev == op._ev) { // Simply replace the value. poi._intA = op._intA; // An operation will still take place. return true; } break; case PendingOperationItem::ModifyEventList: // TODO Not quite right yet. // if(poi._type == PendingOperationItem::ModifyEventList && // // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). // (poi._orig_event_list == op._orig_event_list || poi._event_list == op._event_list)) // { // // Simply replace the list. // poi._event_list = op._event_list; break; case PendingOperationItem::ModifyMidiCtrlValList: // TODO Not quite right yet. // if(poi._type == PendingOperationItem::ModifyMidiCtrlValList && // // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). // (poi._orig_mcvl == op._orig_mcvl || poi._mcvl == op._mcvl)) // { // // Simply replace the list. // poi._mcvl = op._mcvl; break; case PendingOperationItem::AddMidiCtrlVal: if(poi._type == PendingOperationItem::DeleteMidiCtrlVal && poi._mcvl == op._mcvl && poi._imcv->second.part == op._part && poi._imcv->second.val == op._intB) { // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } break; case PendingOperationItem::DeleteMidiCtrlVal: // Be sure _intB is set. if(poi._type == PendingOperationItem::AddMidiCtrlVal && poi._mcvl == op._mcvl && poi._part == op._imcv->second.part && poi._intB == op._imcv->second.val) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } break; case PendingOperationItem::ModifyMidiCtrlVal: // TODO FIXME Finish this // Be sure _intB/A is set // if(poi._type == PendingOperationItem::ModifyMidiCtrlVal && // poi._mcvl == op._mcvl && // poi._imcv->second.part == op._imcv->second.part && // poi._imcv->second.val == op._imcv->second.val) // { // // Simply replace the value. // poi._intA = op._intA; // return true; // } // else if(poi._type == PendingOperationItem::DeleteMidiCtrlVal && poi._mcvl == op._mcvl && poi._imcv->second.part == op._imcv->second.part) // { // // Transform existing delete command into a modify command. // poi._type = PendingOperationItem::ModifyMidiCtrlVal; // poi._intA = op._intA; // return true; // } // else if(poi._type == PendingOperationItem::AddMidiCtrlVal && poi._mcvl == op._mcvl && poi._part == op._imcv->second.part) // { // // Simply replace the add value with the modify value. // poi._intB = op._intA; // return true; // } break; case PendingOperationItem::ModifyAudioCtrlValList: if(poi._type == PendingOperationItem::ModifyAudioCtrlValList && // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). (poi._iCtrlList->second == op._iCtrlList->second || poi._aud_ctrl_list == op._iCtrlList->second)) { // Simply replace the list. poi._aud_ctrl_list = op._aud_ctrl_list; // An operation will still take place. return true; } break; case PendingOperationItem::AddAudioCtrlVal: if(poi._type == PendingOperationItem::AddAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) { // Simply replace the value. poi._ctl_dbl_val = op._ctl_dbl_val; // An operation will still take place. return true; } else if(poi._type == PendingOperationItem::DeleteAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) { // Transform existing delete command into a modify command. poi._type = PendingOperationItem::ModifyAudioCtrlVal; poi._ctl_dbl_val = op._ctl_dbl_val; // An operation will still take place. return true; } else if(poi._type == PendingOperationItem::ModifyAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) { // Simply replace the value. poi._ctl_dbl_val = op._ctl_dbl_val; // An operation will still take place. return true; } break; case PendingOperationItem::DeleteAudioCtrlVal: if(poi._type == PendingOperationItem::DeleteAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) { // Multiple delete commands not allowed! ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteAudioCtrlVal. Ignoring.\n"); return false; } else if(poi._type == PendingOperationItem::AddAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } else if(poi._type == PendingOperationItem::ModifyAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) { // Modify followed by delete is equivalent to just deleting. // Transform existing modify command into a delete command. poi._type = PendingOperationItem::DeleteAudioCtrlVal; // An operation will still take place. return true; } break; case PendingOperationItem::ModifyAudioCtrlVal: if(poi._type == PendingOperationItem::ModifyAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) { // Simply replace the value. poi._ctl_dbl_val = op._ctl_dbl_val; // An operation will still take place. return true; } else if(poi._type == PendingOperationItem::DeleteAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) { // Transform existing delete command into a modify command. poi._type = PendingOperationItem::ModifyAudioCtrlVal; poi._ctl_dbl_val = op._ctl_dbl_val; // An operation will still take place. return true; } else if(poi._type == PendingOperationItem::AddAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list) { // Simply replace the add value with the modify value. poi._ctl_dbl_val = op._ctl_dbl_val; // An operation will still take place. return true; } break; case PendingOperationItem::ModifyTempoList: // TODO Not quite right yet. // if(poi._type == PendingOperationItem::ModifyTempoList && // // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). // (poi._orig_tempo_list == op._orig_tempo_list || poi._tempo_list == op._tempo_list)) // { // // Simply replace the list. // poi._tempo_list = op._tempo_list; break; case PendingOperationItem::SetStaticTempo: #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationList::add() SetStaticTempo\n"); #endif if(poi._type == PendingOperationItem::SetStaticTempo && poi._tempo_list == op._tempo_list) { // Simply replace the value. poi._intA = op._intA; // An operation will still take place. return true; } break; case PendingOperationItem::SetGlobalTempo: DEBUG_OPERATIONS(stderr, "PendingOperationList::add() SetGlobalTempo\n"); if(poi._type == PendingOperationItem::SetGlobalTempo && poi._tempo_list == op._tempo_list) { // Simply replace the new value. poi._intA = op._intA; // An operation will still take place. return true; } break; case PendingOperationItem::ModifySigList: // TODO Not quite right yet. // if(poi._type == PendingOperationItem::ModifySigList && // // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). // (poi._orig_sig_list == op._orig_sig_list || poi._sig_list == op._sig_list)) // { // // Simply replace the list. // poi._sig_list = op._sig_list; break; case PendingOperationItem::ModifyKeyList: // TODO Not quite right yet. // if(poi._type == PendingOperationItem::ModifyKeyList && // // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). // (poi._orig_key_list == op._orig_key_list || poi._key_list == op._key_list)) // { // // Simply replace the list. // poi._key_list = op._key_list; break; case PendingOperationItem::AddStretchListRatioAt: if(poi._type == PendingOperationItem::AddStretchListRatioAt && poi._stretch_list == op._stretch_list && poi._stretch_type == op._stretch_type) { // Simply replace the value. poi._audio_converter_value = op._audio_converter_value; return true; } // Todo? // else if(poi._type == PendingOperationItem::DeleteStretchListRatioAt && poi._stretch_list == op._stretch_list && // poi._stretch_type == op._stretch_type) // { // // Transform existing delete command into a modify command. // poi._type = PendingOperationItem::ModifyStretchListRatioAt; // poi._audio_converter_value = op._audio_converter_value; // return true; // } else if(poi._type == PendingOperationItem::ModifyStretchListRatioAt && poi._stretch_list == op._stretch_list && poi._stretch_type == op._stretch_type) { // Simply replace the value. poi._audio_converter_value = op._audio_converter_value; return true; } break; case PendingOperationItem::DeleteStretchListRatioAt: if(poi._type == PendingOperationItem::DeleteStretchListRatioAt && poi._stretch_list == op._stretch_list && poi._stretch_type == op._stretch_type) { // Multiple delete commands not allowed! ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteStretchRatioAt. Ignoring.\n"); return false; } // Todo? // else if(poi._type == PendingOperationItem::AddStretchListRatioAt && poi._stretch_list == op._stretch_list) // { // // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. // erase(ipos->second); // _map.erase(ipos); // return true; // } // else if(poi._type == PendingOperationItem::ModifyStretchListRatioAt && poi._stretch_list == op._stretch_list) // { // // Modify followed by delete is equivalent to just deleting. // // Transform existing modify command into a delete command. // poi._type = PendingOperationItem::DeleteStretchListRatioAt; // return true; // } break; case PendingOperationItem::ModifyStretchListRatioAt: if(poi._type == PendingOperationItem::ModifyStretchListRatioAt && poi._stretch_list == op._stretch_list && poi._stretch_type == op._stretch_type) { // Simply replace the value. poi._audio_converter_value = op._audio_converter_value; return true; } // Todo? // else if(poi._type == PendingOperationItem::DeleteStretchListRatioAt && poi._stretch_list == op._stretch_list && // poi._stretch_type == op._stretch_type) // { // // Transform existing delete command into a modify command. // poi._type = PendingOperationItem::ModifyStretchListRatioAt; // poi._audio_converter_value = op._audio_converter_value; // return true; // } else if(poi._type == PendingOperationItem::AddStretchListRatioAt && poi._stretch_list == op._stretch_list && poi._stretch_type == op._stretch_type) { // Simply replace the add value with the modify value. poi._audio_converter_value = op._audio_converter_value; return true; } break; case PendingOperationItem::ModifyStretchListRatio: if(poi._type == PendingOperationItem::ModifyStretchListRatio && poi._stretch_list == op._stretch_list && poi._stretch_type == op._stretch_type) { // Simply replace the value. poi._audio_converter_value = op._audio_converter_value; return true; } break; case PendingOperationItem::ModifySongLength: DEBUG_OPERATIONS(stderr, "PendingOperationList::add() ModifySongLength\n"); if(poi._type == PendingOperationItem::ModifySongLength) { // Simply replace the value. poi._intA = op._intA; // An operation will still take place. return true; } break; case PendingOperationItem::EnableAllAudioControllers: DEBUG_OPERATIONS(stderr, "PendingOperationList::add() EnableAllAudioControllers\n"); if(poi._type == PendingOperationItem::EnableAllAudioControllers) { ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double EnableAllAudioControllers. Ignoring.\n"); return false; } break; case PendingOperationItem::GlobalSelectAllEvents: #ifdef _PENDING_OPS_DEBUG_ fprintf(stderr, "PendingOperationList::add() GlobalSelectAllEvents\n"); #endif if(poi._type == PendingOperationItem::GlobalSelectAllEvents && poi._track_list == op._track_list) { if(poi._boolA == op._boolA) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double GlobalSelectAllEvents. Ignoring.\n"); return false; } else { // Special: Do not 'cancel' out this one. The selecions may need to affect all events. // Simply replace the value. poi._boolA = op._boolA; // An operation will still take place. return true; } } break; case PendingOperationItem::ModifyAudioSamples: // TODO Not quite right yet. // if(poi._type == PendingOperationItem::ModifyAudioSamples && // // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). // poi._audioSamplesPointer && op._audioSamplesPointer && // (*poi._audioSamplesPointer == *op._audioSamplesPointer || poi._newAudioSamples == op._newAudioSamples)) // { // // Simply replace the list. // poi._newAudioSamples = op._newAudioSamples; // poi._newAudioSamplesLen = op._newAudioSamplesLen; // return true; // } break; case PendingOperationItem::ModifyMarkerList: // TODO Not quite right yet. // if(poi._type == PendingOperationItem::ModifyMarkerList && // // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). // poi._orig_marker_list && op._orig_marker_list && // (*poi._orig_marker_list == *op._orig_marker_list || poi._marker_list == op._marker_list)) // { // // Simply replace the list. // poi._newAudioSamples = op._newAudioSamples; // poi._newAudioSamplesLen = op._newAudioSamplesLen; break; case PendingOperationItem::SwitchMetronomeSettings: if(poi._type == PendingOperationItem::SwitchMetronomeSettings && (poi._bool_pointer == op._bool_pointer)) { if(poi._boolA == op._boolA) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double SwitchMetronomeSettings. Ignoring.\n"); // No operation will take place. return false; } else { // Enable or disable followed by disable or enable is useless. Cancel out both by erasing the command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } } break; case PendingOperationItem::ModifyMetronomeAccentMap: // TODO Not quite right yet. // if(poi._type == PendingOperationItem::ModifyMetronomeAccentMap && // // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc). // (poi._metroAccentsMap == op._metroAccentsMap || poi._newMetroAccentsMap == op._newMetroAccentsMap)) // { // // Simply replace the list. // poi._newMetroAccentsMap = op._newMetroAccentsMap; // // An operation will still take place. // return true; // } break; case PendingOperationItem::SetExternalSyncFlag: if(poi._type == PendingOperationItem::SetExternalSyncFlag && (poi._bool_pointer == op._bool_pointer)) { if(poi._boolA == op._boolA) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetExternalSyncFlag. Ignoring.\n"); // No operation will take place. return false; } else { // Enable or disable followed by disable or enable is useless. Cancel out both by erasing the command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } } break; case PendingOperationItem::SetUseJackTransport: if(poi._type == PendingOperationItem::SetUseJackTransport && (poi._bool_pointer == op._bool_pointer)) { if(poi._boolA == op._boolA) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetUseJackTransport. Ignoring.\n"); // No operation will take place. return false; } else { // Enable or disable followed by disable or enable is useless. Cancel out both by erasing the command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } } break; case PendingOperationItem::SetUseMasterTrack: if(poi._type == PendingOperationItem::SetUseMasterTrack && (poi._tempo_list == op._tempo_list)) { if(poi._boolA == op._boolA) { fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetUseMasterTrack. Ignoring.\n"); // No operation will take place. return false; } else { // Enable or disable followed by disable or enable is useless. Cancel out both by erasing the command. erase(ipos->second); _map.erase(ipos); // No operation will take place. return false; } } break; case PendingOperationItem::Uninitialized: ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Uninitialized item. Ignoring.\n"); return false; break; default: break; } } iPendingOperation iipo = insert(end(), op); _map.insert(std::pair(t, iipo)); return true; } iPendingOperation PendingOperationList::findAllocationOp(const PendingOperationItem& op) { iPendingOperationSortedRange r = _map.equal_range(op.getIndex()); iPendingOperationSorted ipos = r.second; while(ipos != r.first) { --ipos; const PendingOperationItem& poi = *ipos->second; if(poi.isAllocationOp(op)) // Comparison. return ipos->second; } return end(); } //======================================================================== //--------------------------------------------------------- // addDeviceOperation //--------------------------------------------------------- void PendingOperationList::addDeviceOperation(MidiDeviceList* devlist, MidiDevice* dev) { bool gotUniqueName=false; int increment = 0; const QString origname = dev->name(); QString newName = origname; PendingOperationItem poi(devlist, dev, PendingOperationItem::AddMidiDevice); // check if the name's been taken while(!gotUniqueName) { if(increment >= 10000) { fprintf(stderr, "MusE Error: PendingOperationList::addDeviceOperation(): Out of 10000 unique midi device names!\n"); return; } gotUniqueName = true; // In the case of type AddMidiDevice, this searches for the name only. iPendingOperation ipo = findAllocationOp(poi); if(ipo != end()) { PendingOperationItem& poif = *ipo; if(poif._midi_device == poi._midi_device) return; // Device itself is already added! newName = origname + QString("_%1").arg(++increment); gotUniqueName = false; } for(ciMidiDevice i = devlist->cbegin(); i != devlist->cend(); ++i) { const QString s = (*i)->name(); if(s == newName) { newName = origname + QString("_%1").arg(++increment); gotUniqueName = false; } } } if(origname != newName) dev->setName(newName); add(poi); } //--------------------------------------------------------- // addPartPortCtrlEvents //--------------------------------------------------------- void PendingOperationList::addPartPortCtrlEvents( const Event& event, Part* part, unsigned int tick, unsigned int /*len*/, Track* track) { if(!track || !track->isMidiTrack()) return; if(event.type() == Controller) { unsigned int tck = event.tick() + tick; int cntrl = event.dataA(); int val = event.dataB(); MidiTrack* mt = (MidiTrack*)track; MidiPort* mp; int ch; mt->mappedPortChanCtrl(&cntrl, nullptr, &mp, &ch); MidiCtrlValListList* mcvll = mp->controller(); MidiCtrlValList* mcvl = NULL; iMidiCtrlValList imcvll = mcvll->find(ch, cntrl); if(imcvll == mcvll->end()) { PendingOperationItem poi(mcvll, 0, ch, cntrl, PendingOperationItem::AddMidiCtrlValList); if(findAllocationOp(poi) == end()) { mcvl = new MidiCtrlValList(cntrl); poi._mcvl = mcvl; add(poi); } } else { mcvl = imcvll->second; } //assert(mcvl != NULL); //FIXME: Can this happen? (danvd). UPDATE: Yes, it can (danvd) if(mcvl != NULL) { // The operation will catch and ignore events which are past the end of the part. add(PendingOperationItem(mcvl, part, tck, val, PendingOperationItem::AddMidiCtrlVal)); } } } void PendingOperationList::addPartPortCtrlEvents(Part* part, unsigned int tick, unsigned int len, Track* track) { if(!track || !track->isMidiTrack()) return; for(ciEvent ie = part->events().begin(); ie != part->events().end(); ++ie) { // The operation will catch and ignore events which are past the end of the part. addPartPortCtrlEvents(ie->second, part, tick, len, track); } } //--------------------------------------------------------- // removePartPortCtrlEvents //--------------------------------------------------------- bool PendingOperationList::removePartPortCtrlEvents(const Event& event, Part* part, Track* track) { if(!track || !track->isMidiTrack()) return false; if(event.type() == Controller) { MidiTrack* mt = (MidiTrack*)track; // MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; // int ch = mt->outChannel(); unsigned int tck = event.tick() + part->tick(); int cntrl = event.dataA(); int val = event.dataB(); // Is it a drum controller event, according to the track port's instrument? MidiPort* mp; int ch; mt->mappedPortChanCtrl(&cntrl, nullptr, &mp, &ch); MidiCtrlValListList* mcvll = mp->controller(); iMidiCtrlValList cl = mcvll->find(ch, cntrl); if (cl == mcvll->end()) { fprintf(stderr, "removePartPortCtrlEvents: controller %d(0x%x) for channel %d not found size %zd\n", cntrl, cntrl, ch, mcvll->size()); return false; } MidiCtrlValList* mcvl = cl->second; iMidiCtrlVal imcv = mcvl->findMCtlVal(tck, part, val); if (imcv == mcvl->end()) { // Let's throw up the error only if we were expecting the cache event to be there, // as is the case when the tick is inside the part. When the tick is NOT inside the part // a cache event should really not be there. But if one is found it should be deleted anyway. #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)tck >= (int)part->tick() && (int)tck < (int)part->tick() + (int)part->lenTick()) #else if(tck < part->tick() + part->lenTick()) #endif fprintf(stderr, "removePartPortCtrlEvents: (tick: %u): not found (size %zd)\n", tck, mcvl->size()); return false; } return add(PendingOperationItem(mcvl, imcv, PendingOperationItem::DeleteMidiCtrlVal)); } return false; } void PendingOperationList::removePartPortCtrlEvents(Part* part, Track* track) { if(!track || !track->isMidiTrack()) return; for(ciEvent ie = part->events().begin(); ie != part->events().end(); ++ie) { removePartPortCtrlEvents(ie->second, part, track); } } //--------------------------------------------------------- // addPortCtrlEvents //--------------------------------------------------------- void PendingOperationList::addTrackPortCtrlEvents(Track* track) { if(!track || !track->isMidiTrack()) return; const PartList* pl = track->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* part = ip->second; addPartPortCtrlEvents(part, part->tick(), part->lenTick(), track); } } //--------------------------------------------------------- // removePortCtrlEvents //--------------------------------------------------------- void PendingOperationList::removeTrackPortCtrlEvents(Track* track) { if(!track || !track->isMidiTrack()) return; const PartList* pl = track->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* part = ip->second; removePartPortCtrlEvents(part, track); } } void PendingOperationList::modifyPartPortCtrlEvents(const Event& old_event, const Event& event, Part* part) { Track* t = part->track(); if(!t || !t->isMidiTrack()) return; if(old_event.type() != Controller || event.type() != Controller) return; MidiTrack* mt = static_cast(t); unsigned int tck_erase = old_event.tick() + part->tick(); int cntrl_erase = old_event.dataA(); int val_erase = old_event.dataB(); iMidiCtrlVal imcv_erase; bool found_erase = false; // Is it a drum controller old_event, according to the track port's instrument? int ch_erase; MidiPort* mp_erase; mt->mappedPortChanCtrl(&cntrl_erase, nullptr, &mp_erase, &ch_erase); MidiCtrlValListList* mcvll_erase = mp_erase->controller(); MidiCtrlValList* mcvl_erase = 0; iMidiCtrlValList cl_erase = mcvll_erase->find(ch_erase, cntrl_erase); if(cl_erase == mcvll_erase->end()) { if(MusEGlobal::debugMsg) printf("modifyPartPortCtrlEvents: controller %d(0x%x) for channel %d not found size %zd\n", cntrl_erase, cntrl_erase, ch_erase, mcvll_erase->size()); } else { mcvl_erase = cl_erase->second; imcv_erase = mcvl_erase->findMCtlVal(tck_erase, part, val_erase); if(imcv_erase == mcvl_erase->end()) { if(MusEGlobal::debugMsg) printf("modifyPartPortCtrlEvents(tick:%u val:%d): not found (size %zd)\n", tck_erase, val_erase, mcvl_erase->size()); } else found_erase = true; } unsigned int tck_add = event.tick() + part->tick(); int cntrl_add = event.dataA(); int val_add = event.dataB(); // FIXME FIXME CHECK THIS // // Why wasn't 'ch' given its own 'ch_add' variable in the original code? // And why did 'mp_add' default to mp_erase above. // That means the channel and port would have defaulted to the ones // being erased above, not the track's. That can't be right ! // Is it a drum controller event, according to the track port's instrument? int ch_add; MidiPort* mp_add; mt->mappedPortChanCtrl(&cntrl_add, nullptr, &mp_add, &ch_add); MidiCtrlValList* mcvl_add; MidiCtrlValListList* mcvll_add = mp_add->controller(); iMidiCtrlValList imcvll_add = mcvll_add->find(ch_add, cntrl_add); if(imcvll_add == mcvll_add->end()) { if(found_erase) add(PendingOperationItem(mcvl_erase, imcv_erase, PendingOperationItem::DeleteMidiCtrlVal)); PendingOperationItem poi(mcvll_add, 0, ch_add, cntrl_add, PendingOperationItem::AddMidiCtrlValList); if(findAllocationOp(poi) == end()) { poi._mcvl = new MidiCtrlValList(cntrl_add); add(poi); } // The operation will catch and ignore events which are past the end of the part. add(PendingOperationItem(poi._mcvl, part, tck_add, val_add, PendingOperationItem::AddMidiCtrlVal)); return; } else { mcvl_add = imcvll_add->second; iMidiCtrlVal imcv_add = mcvl_add->findMCtlVal(tck_add, part, val_add); if(imcv_add != mcvl_add->end()) { if(tck_erase == tck_add && mcvl_erase == mcvl_add) { // The operation will catch and ignore events which are past the end of the part. add(PendingOperationItem(mcvl_add, imcv_add, val_add, PendingOperationItem::ModifyMidiCtrlVal)); } else { if(found_erase) { add(PendingOperationItem(mcvl_erase, imcv_erase, PendingOperationItem::DeleteMidiCtrlVal)); } // The operation will catch and ignore events which are past the end of the part. add(PendingOperationItem(mcvl_add, part, tck_add, val_add, PendingOperationItem::AddMidiCtrlVal)); } return; } else { if(found_erase) add(PendingOperationItem(mcvl_erase, imcv_erase, PendingOperationItem::DeleteMidiCtrlVal)); // The operation will catch and ignore events which are past the end of the part. add(PendingOperationItem(mcvl_add, part, tck_add, val_add, PendingOperationItem::AddMidiCtrlVal)); } } } void PendingOperationList::addPartOperation(PartList *partlist, Part* part) { // There is protection, in the catch-all Undo::insert(), from failure here (such as double add, del + add, add + del) // which might cause addPortCtrlEvents() without parts or without corresponding removePortCtrlEvents etc. add(PendingOperationItem(partlist, part, PendingOperationItem::AddPart)); addPartPortCtrlEvents(part, part->posValue(), part->lenValue(), part->track()); } void PendingOperationList::delPartOperation(PartList *partlist, Part* part) { // There is protection, in the catch-all Undo::insert(), from failure here (such as double del, del + add, add + del) // which might cause addPortCtrlEvents() without parts or without corresponding removePortCtrlEvents etc. removePartPortCtrlEvents(part, part->track()); iPart i; for (i = partlist->begin(); i != partlist->end(); ++i) { if (i->second == part) { add(PendingOperationItem(partlist, i, PendingOperationItem::DeletePart)); return; } } printf("THIS SHOULD NEVER HAPPEN: could not find the part in PendingOperationList::delPartOperation()!\n"); } void PendingOperationList::movePartOperation(PartList *partlist, Part* part, unsigned int new_pos, Track* track) { removePartPortCtrlEvents(part, part->track()); iPart i = partlist->end(); if(track) { for (i = partlist->begin(); i != partlist->end(); ++i) { if (i->second == part) break; } if(i == partlist->end()) printf("THIS SHOULD NEVER HAPPEN: could not find the part in PendingOperationList::movePartOperation()!\n"); } add(PendingOperationItem(i, part, new_pos, PendingOperationItem::MovePart, track)); if(!track) track = part->track(); addPartPortCtrlEvents(part, new_pos, part->lenValue(), track); } void PendingOperationList::modifyPartStartOperation( Part* part, unsigned int new_pos, unsigned int new_len, int64_t events_offset, Pos::TType events_offset_time_type) { if(!part->track()) return; PartList* partlist = part->track()->parts(); iPart ip = partlist->end(); for (ip = partlist->begin(); ip != partlist->end(); ++ip) { if (ip->second == part) break; } if(ip == partlist->end()) { fprintf(stderr, "THIS SHOULD NEVER HAPPEN: could not find part in PendingOperationList::modifyPartStartOperation()!\n"); return; } EventList* new_el = nullptr; // If we are dragging the part's events with the border, their positions relative to the border don't change. // If we are not dragging the events, their positions relative to the border change so we MUST move ALL the events. if(events_offset != 0) { // Compose a complete new list to quickly swap with the existing list. const EventList& el = part->events(); new_el = new EventList(); for(ciEvent ie = el.cbegin(); ie != el.cend(); ++ie) { Event e = ie->second.clone(); if(e.pos().type() == events_offset_time_type) { // NOTE: Don't alter the offset here or below in the conversions. It messes with the ability of the undo system // to properly undo a movement. It also breaks the rule that all clone parts MUST have the same event times. // Checks and limits should be done before calling this function. //if((int64_t)e.posValue() + events_offset < 0) // e.setPosValue(0); //else e.setPosValue(e.posValue() + events_offset); } else { // In case the event and part pos types differ, the event dominates. const unsigned int new_part_pos_val = Pos::convert(new_pos, part->type(), e.pos().type()); const unsigned int old_abs_ev_pos_val = Pos::convert(e.posValue() + new_part_pos_val, e.pos().type(), events_offset_time_type); const unsigned int new_abs_ev_pos_val = Pos::convert(old_abs_ev_pos_val + events_offset, events_offset_time_type, e.pos().type()); const unsigned int new_ev_pos_val = new_abs_ev_pos_val - new_part_pos_val; e.setPosValue(new_ev_pos_val); } new_el->add(e); } } // If we are dragging the part's events with the border, we must update the midi controller cache. // If we are not dragging the events, their absolute positions don't change so there should be no need to update the cache. // First half of the midi controller cache update: removePartPortCtrlEvents(part, part->track()); add(PendingOperationItem(ip, part, new_pos, new_len, new_el, PendingOperationItem::ModifyPartStart)); // Second half of the midi controller cache update: // The operation will catch and ignore events which are outside of the part. // In case the new_pos and events_offset types differ, the events_offset dominates. const unsigned int new_cache_offset = Pos::convert(events_offset + Pos::convert(new_pos, part->type(), events_offset_time_type), events_offset_time_type, Pos::TICKS); addPartPortCtrlEvents(part, new_cache_offset, part->lenValue(), part->track()); } void PendingOperationList::modifyPartLengthOperation( Part* part, unsigned int new_len, int64_t events_offset, Pos::TType events_offset_time_type) { if(!part->track()) return; PartList* partlist = part->track()->parts(); iPart ip = partlist->end(); for (ip = partlist->begin(); ip != partlist->end(); ++ip) { if (ip->second == part) break; } if(ip == partlist->end()) { fprintf(stderr, "THIS SHOULD NEVER HAPPEN: could not find part in PendingOperationList::modifyPartLengthOperation()!\n"); return; } EventList* new_el = nullptr; // If we are dragging the part's events with the border, their positions relative to the border change so we MUST move ALL the events. // If we are not dragging the events, their positions relative to the border don't change. if(events_offset != 0) { // Compose a complete new list to quickly swap with the existing list. const EventList& el = part->events(); new_el = new EventList(); for(ciEvent ie = el.cbegin(); ie != el.cend(); ++ie) { Event e = ie->second.clone(); if(e.pos().type() == events_offset_time_type) { // NOTE: Don't alter the offset here or below in the conversions. It messes with the ability of the undo system // to properly undo a movement. It also breaks the rule that all clone parts MUST have the same event times. // Checks and limits should be done before calling this function. //if((int64_t)e.posValue() + events_offset < 0) // e.setPosValue(0); //else e.setPosValue(e.posValue() + events_offset); } else { // In case the event and part pos types differ, the event dominates. const unsigned int part_pos_val = part->posValue(e.pos().type()); const unsigned int old_abs_ev_pos_val = Pos::convert(e.posValue() + part_pos_val, e.pos().type(), events_offset_time_type); const unsigned int new_abs_ev_pos_val = Pos::convert(old_abs_ev_pos_val + events_offset, events_offset_time_type, e.pos().type()); const unsigned int new_ev_pos_val = new_abs_ev_pos_val - part_pos_val; e.setPosValue(new_ev_pos_val); } new_el->add(e); } } // If we are dragging the part's events with the border, we must update the midi controller cache. // If we are not dragging the events, their absolute positions don't change so there should be no need to update the cache. // First half of the midi controller cache update: removePartPortCtrlEvents(part, part->track()); add(PendingOperationItem(ip, part, new_len, new_el, PendingOperationItem::ModifyPartLength)); // Second half of the midi controller cache update: // The operation will catch and ignore events which are outside of the part. // In case the new_pos and events_offset types differ, the events_offset dominates. const unsigned int new_cache_offset = Pos::convert(events_offset + part->posValue(events_offset_time_type), events_offset_time_type, Pos::TICKS); addPartPortCtrlEvents(part, new_cache_offset, part->lenValue(), part->track()); } //--------------------------------------------------------- // addTrackAuxSendOperation //--------------------------------------------------------- void PendingOperationList::addTrackAuxSendOperation(AudioTrack *atrack, int n) { AuxSendValueList *vl = atrack->getAuxSendValueList(); const int nn = vl->size(); for (int i = nn; i < n; ++i) add(PendingOperationItem(vl, 0.0, PendingOperationItem::AddAuxSendValue)); } //--------------------------------------------------------- // TrackMidiCtrlRemapOperation //--------------------------------------------------------- void TrackMidiCtrlRemapOperation( MidiTrack *mtrack, int index, int newPort, int newChan, int newNote, MidiCtrlValRemapOperation* rmop) { const int out_port = mtrack->outPort(); const int out_chan = mtrack->outChannel(); if(mtrack->type() != Track::DRUM || out_port < 0 || out_port >= MusECore::MIDI_PORTS) return; // Default to track port if -1 and track channel if -1. if(newPort == -1) newPort = out_port; if(newChan == -1) newChan = out_chan; MidiPort* trackmp = &MusEGlobal::midiPorts[out_port]; const DrumMap *drum_map = mtrack->drummap(); int dm_ch = drum_map[index].channel; if(dm_ch == -1) dm_ch = out_chan; int dm_port = drum_map[index].port; if(dm_port == -1) dm_port = out_port; MidiPort* dm_mp = &MusEGlobal::midiPorts[dm_port]; MidiCtrlValListList* dm_mcvll = dm_mp->controller(); MidiCtrlValList* v_mcvl; int v_ch, v_ctrl, v_idx; for(iMidiCtrlValList idm_mcvl = dm_mcvll->begin(); idm_mcvl != dm_mcvll->end(); ++idm_mcvl) { v_ch = idm_mcvl->first >> 24; if(v_ch != dm_ch) continue; v_mcvl = idm_mcvl->second; v_ctrl = v_mcvl->num(); // Is it a drum controller, according to the track port's instrument? if(!trackmp->drumController(v_ctrl)) continue; v_idx = v_ctrl & 0xff; if(v_idx != drum_map[index].anote) continue; // Does this midi control value list need to be changed (values moved etc)? iMidiCtrlVal imcv = v_mcvl->begin(); for( ; imcv != v_mcvl->end(); ++imcv) { const MidiCtrlVal& mcv = imcv->second; if(mcv.part && mcv.part->track() == mtrack) break; } if(imcv != v_mcvl->end()) { // A contribution from a part on this track was found. // We must compose a new list, or get an existing one and schedule the existing // one for iterator erasure and pointer deletion. // Add the erase iterator. Add will ignore if the erase iterator already exists. rmop->_midiCtrlValLists2bErased.add(dm_port, idm_mcvl); // Insert the delete pointer. Insert will ignore if the delete pointer already exists. rmop->_midiCtrlValLists2bDeleted.insert(v_mcvl); MidiCtrlValListList* op_mcvll; iMidiCtrlValLists2bAdded_t imcvla = rmop->_midiCtrlValLists2bAdded.find(dm_port); if(imcvla == rmop->_midiCtrlValLists2bAdded.end()) { op_mcvll = new MidiCtrlValListList(); rmop->_midiCtrlValLists2bAdded.insert(MidiCtrlValLists2bAddedInsertPair_t(dm_port, op_mcvll)); } else op_mcvll = imcvla->second; MidiCtrlValList* op_mcvl; iMidiCtrlValList imcvl = op_mcvll->find(dm_ch, v_ctrl); if(imcvl == op_mcvll->end()) { op_mcvl = new MidiCtrlValList(v_ctrl); op_mcvll->add(dm_ch, op_mcvl); // Assign the contents of the original list to the new list. *op_mcvl = *v_mcvl; } else op_mcvl = imcvl->second; // Remove from the list any contributions from this track. iMidiCtrlVal iopmcv = op_mcvl->begin(); for( ; iopmcv != op_mcvl->end(); ) { const MidiCtrlVal& mcv = iopmcv->second; if(mcv.part && mcv.part->track() == mtrack) { iMidiCtrlVal iopmcv_save = iopmcv; ++iopmcv_save; op_mcvl->erase(iopmcv); iopmcv = iopmcv_save; } else ++iopmcv; } } // We will be making changes to the list pointed to by the new settings. // We must schedule the existing one for iterator erasure and pointer deletion. MidiPort* dm_mp_new = &MusEGlobal::midiPorts[newPort]; MidiCtrlValListList* dm_mcvll_new = dm_mp_new->controller(); MidiCtrlValList* v_mcvl_new = 0; const int v_ctrl_new = (v_ctrl & ~0xff) | newNote; iMidiCtrlValList idm_mcvl_new = dm_mcvll_new->find(newChan, v_ctrl_new); if(idm_mcvl_new != dm_mcvll_new->end()) { v_mcvl_new = idm_mcvl_new->second; // Add the erase iterator. Add will ignore if the erase iterator already exists. rmop->_midiCtrlValLists2bErased.add(newPort, idm_mcvl_new); // Insert the delete pointer. Insert will ignore if the delete pointer already exists. rmop->_midiCtrlValLists2bDeleted.insert(v_mcvl_new); } // Create a new list of lists, or get an existing one. MidiCtrlValListList* op_mcvll_new; iMidiCtrlValLists2bAdded_t imcvla_new = rmop->_midiCtrlValLists2bAdded.find(newPort); if(imcvla_new == rmop->_midiCtrlValLists2bAdded.end()) { op_mcvll_new = new MidiCtrlValListList(); rmop->_midiCtrlValLists2bAdded.insert(MidiCtrlValLists2bAddedInsertPair_t(newPort, op_mcvll_new)); } else op_mcvll_new = imcvla_new->second; // Compose a new list for replacement, or get an existing one. MidiCtrlValList* op_mcvl_new; iMidiCtrlValList imcvl_new = op_mcvll_new->find(newChan, v_ctrl_new); if(imcvl_new == op_mcvll_new->end()) { op_mcvl_new = new MidiCtrlValList(v_ctrl_new); op_mcvll_new->add(newChan, op_mcvl_new); // Assign the contents of the original list to the new list. if(v_mcvl_new) *op_mcvl_new = *v_mcvl_new; } else op_mcvl_new = imcvl_new->second; // Add to the list any contributions from this track. for(ciMidiCtrlVal imcv_new = v_mcvl->begin(); imcv_new != v_mcvl->end(); ++imcv_new) { const MidiCtrlVal& mcv = imcv_new->second; if(mcv.part && mcv.part->track() == mtrack) { op_mcvl_new->addMCtlVal(imcv_new->first, mcv.val, mcv.part); } } } } } // namespace MusECore muse-4.0.0/src/muse/operations.h000066400000000000000000000717731404105311000165640ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // operations.h // (C) Copyright 2014, 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __OPERATIONS_H__ #define __OPERATIONS_H__ #include #include #include #include #include "type_defs.h" #include "muse_time.h" #include "event.h" #include "midictrl.h" #include "ctrl.h" #include "time_stretch.h" #include "part.h" #include "track.h" #include "route.h" #include "mididev.h" #include "marker/marker.h" #include "instruments/minstrument.h" #include "wave.h" #include "pos.h" namespace MusECore { // Forward declarations: class TempoList; class SigList; class KeyList; class MidiPort; class MetroAccentsMap; class AudioConverterSettingsGroup; class AudioConverterPluginI; typedef std::list < iMidiCtrlValList > MidiCtrlValListIterators_t; typedef MidiCtrlValListIterators_t::iterator iMidiCtrlValListIterators_t; typedef MidiCtrlValListIterators_t::const_iterator ciMidiCtrlValListIterators_t; class MidiCtrlValListIterators : public MidiCtrlValListIterators_t { public: iterator findList(const MidiCtrlValList* valList); const_iterator findList(const MidiCtrlValList* valList) const; }; typedef std::map < int /*port*/, MidiCtrlValListIterators, std::less > MidiCtrlValLists2bErased_t; typedef MidiCtrlValLists2bErased_t::iterator iMidiCtrlValLists2bErased_t; typedef MidiCtrlValLists2bErased_t::const_iterator ciMidiCtrlValLists2bErased_t; typedef std::pair MidiCtrlValLists2bErasedInsertResult_t; typedef std::pair MidiCtrlValLists2bErasedInsertPair_t; typedef std::pair MidiCtrlValLists2bErasedRangePair_t; class MidiCtrlValLists2bErased : public MidiCtrlValLists2bErased_t { public: void add(int port, const iMidiCtrlValList& item); iterator findList(int port, const MidiCtrlValList* valList); const_iterator findList(int port, const MidiCtrlValList* valList) const; }; typedef std::set < MidiCtrlValList* > MidiCtrlValLists2bDeleted_t; typedef MidiCtrlValLists2bDeleted_t::iterator iMidiCtrlValLists2bDeleted_t; typedef MidiCtrlValLists2bDeleted_t::const_iterator ciMidiCtrlValLists2bDeleted_t; class MidiCtrlValLists2bDeleted : public MidiCtrlValLists2bDeleted_t { }; typedef std::map < int /*port*/, MidiCtrlValListList*, std::less > MidiCtrlValLists2bAdded_t; typedef MidiCtrlValLists2bAdded_t::iterator iMidiCtrlValLists2bAdded_t; typedef MidiCtrlValLists2bAdded_t::const_iterator ciMidiCtrlValLists2bAdded_t; typedef std::pair MidiCtrlValLists2bAddedInsertResult_t; typedef std::pair MidiCtrlValLists2bAddedInsertPair_t; typedef std::pair MidiCtrlValLists2bAddedRangePair_t; class MidiCtrlValLists2bAdded : public MidiCtrlValLists2bAdded_t { // public: // void add(int port, const MidiCtrlValListList* item); // void remove(int port, const MidiCtrlValListList* item); }; struct MidiCtrlValRemapOperation { // Iterators to be erased in realtime stage. MidiCtrlValLists2bErased _midiCtrlValLists2bErased; // New items to be added in realtime stage. MidiCtrlValLists2bAdded _midiCtrlValLists2bAdded; // Orphaned pointers after the iterators have been erased, deleted in post non-realtime stage. // Automatically filled by constructor. MidiCtrlValLists2bDeleted_t _midiCtrlValLists2bDeleted; }; struct DrumMapTrackOperation { // Whether this is a setting operation or a reset to defaults. bool _isReset; bool _isInstrumentMod; bool _doWholeMap; bool _includeDefault; WorkingDrumMapList _workingItemList; // List of tracks to apply to. MidiTrackList _tracks; }; struct DrumMapTrackPatchOperation { // Whether to clear the list of overrides. bool _clear; // Whether this is a setting operation or a reset to defaults. bool _isReset; bool _isInstrumentMod; WorkingDrumMapPatchList _workingItemPatchList; // List of tracks to apply to. MidiTrackList _tracks; }; struct DrumMapTrackPatchReplaceOperation { bool _isInstrumentMod; WorkingDrumMapPatchList* _workingItemPatchList; // Track to apply to. MidiTrack* _track; }; // New items created in GUI thread awaiting addition in audio thread. struct PendingOperationItem { enum PendingOperationType { Uninitialized = 0, ModifySongLength, AddMidiInstrument, DeleteMidiInstrument, ReplaceMidiInstrument, AddMidiDevice, DeleteMidiDevice, ModifyMidiDeviceAddress, ModifyMidiDeviceFlags, ModifyMidiDeviceName, SetInstrument, AddTrack, DeleteTrack, MoveTrack, ModifyTrackName, SetTrackRecord, SetTrackMute, SetTrackSolo, SetTrackRecMonitor, SetTrackOff, ModifyTrackDrumMapItem, ReplaceTrackDrumMapPatchList, UpdateDrumMaps, AddPart, DeletePart, MovePart, SelectPart, ModifyPartStart, ModifyPartLength, ModifyPartName, AddEvent, DeleteEvent, SelectEvent, ModifyEventList, AddMidiCtrlVal, DeleteMidiCtrlVal, ModifyMidiCtrlVal, AddMidiCtrlValList, ModifyMidiCtrlValList, RemapDrumControllers, AddAudioCtrlVal, DeleteAudioCtrlVal, ModifyAudioCtrlVal, ModifyAudioCtrlValList, ModifyTempoList, SetStaticTempo, SetGlobalTempo, ModifySigList, ModifyKeyList, ModifyDefaultAudioConverterSettings, ModifyLocalAudioConverterSettings, ModifyLocalAudioConverter, SetAudioConverterOfflineMode, AddStretchListRatioAt, DeleteStretchListRatioAt, ModifyStretchListRatioAt, ModifyStretchListRatio, AddAuxSendValue, AddRoute, DeleteRoute, AddRouteNode, DeleteRouteNode, ModifyRouteNode, UpdateSoloStates, EnableAllAudioControllers, GlobalSelectAllEvents, ModifyAudioSamples, SwitchMetronomeSettings, ModifyMetronomeAccentMap, SetExternalSyncFlag, SetUseJackTransport, SetUseMasterTrack, ModifyMarkerList }; PendingOperationType _type; union { Part* _part; MidiPort* _midi_port; void* _void_track_list; int* _audioSamplesLen; }; union { EventList* _orig_event_list; MidiCtrlValList* _orig_mcvl; MidiCtrlValListList* _mcvll; CtrlListList* _aud_ctrl_list_list; TempoList* _orig_tempo_list; MusECore::SigList* _orig_sig_list; KeyList* _orig_key_list; StretchList* _stretch_list; PartList* _part_list; TrackList* _track_list; MidiDeviceList* _midi_device_list; MidiInstrumentList* _midi_instrument_list; AuxSendValueList* _aux_send_value_list; RouteList* _route_list; MarkerList** _orig_marker_list; float** _audioSamplesPointer; MetroAccentsMap** _metroAccentsMap; }; union { EventList* _event_list; MidiInstrument* _midi_instrument; MidiDevice* _midi_device; Track* _track; MidiCtrlValList* _mcvl; CtrlList* _aud_ctrl_list; MarkerList* _marker_list; TempoList* _tempo_list; MusECore::SigList* _sig_list; KeyList* _key_list; Route* _dst_route_pointer; float* _newAudioSamples; bool* _bool_pointer; MetroAccentsMap* _newMetroAccentsMap; AudioConverterSettingsGroup* _audio_converter_settings; }; iPart _iPart; Event _ev; iEvent _iev; iMidiCtrlVal _imcv; iCtrl _iCtrl; iCtrlList _iCtrlList; iStretchListItem _iStretchEvent; iMidiInstrument _iMidiInstrument; iMidiDevice _iMidiDevice; iRoute _iRoute; iMarker _iMarker; Route _src_route; Route _dst_route; // SndFileR is only 8 bytes but can't be in a union becuase of non-trivial destructor (?). SndFileR _sndFileR; union { int _intA; unsigned int _uintA; unsigned int _posLenVal; bool _boolA; const QString *_name; double _aux_send_value; int _insert_at; int _from_idx; int _address_client; int _rw_flags; int _newAudioSamplesLen; //DrumMapOperation* _drum_map_operation; DrumMapTrackOperation* _drum_map_track_operation; DrumMapTrackPatchOperation* _drum_map_track_patch_operation; DrumMapTrackPatchReplaceOperation* _drum_map_track_patch_replace_operation; MidiCtrlValRemapOperation* _midi_ctrl_val_remap_operation; MuseFrame_t _museFrame; AudioConverterPluginI* _audio_converter; }; union { int _intB; unsigned int _uintB; unsigned int _lenVal; unsigned int _marker_tick; int _to_idx; int _address_port; int _open_flags; int _ctl_num; int _stretch_type; AudioConverterPluginI* _audio_converter_ui; }; union { int _intC; unsigned int _uintC; int _ctl_val; double _ctl_dbl_val; double _audio_converter_value; bool _marker_lock; }; PendingOperationItem(AudioConverterSettingsGroup* new_settings, PendingOperationType type = ModifyDefaultAudioConverterSettings) { _type = type; _audio_converter_settings = new_settings; } PendingOperationItem(SndFileR sf, AudioConverterSettingsGroup* new_settings, PendingOperationType type = ModifyLocalAudioConverterSettings) { _type = type; _sndFileR = sf; _audio_converter_settings = new_settings; } PendingOperationItem(SndFileR sf, AudioConverterPluginI* newAudioConverter, AudioConverterPluginI* newAudioConverterUI, PendingOperationType type = ModifyLocalAudioConverter) { _type = type; _sndFileR = sf; _audio_converter = newAudioConverter; _audio_converter_ui = newAudioConverterUI; } PendingOperationItem(SndFileR sf, AudioConverterPluginI* newAudioConverter, PendingOperationType type = SetAudioConverterOfflineMode) { _type = type; _sndFileR = sf; _audio_converter = newAudioConverter; } PendingOperationItem(float** samples, float* new_samples, int* samples_len, int new_samples_len, PendingOperationType type = ModifyAudioSamples) { _type = type; _audioSamplesPointer = samples; _newAudioSamples = new_samples; _audioSamplesLen = samples_len, _newAudioSamplesLen = new_samples_len; } // The operation is constructed and allocated in non-realtime before the call, then the controllers modified in realtime stage, // then operation is deleted in non-realtime stage. PendingOperationItem(MidiCtrlValRemapOperation* operation, PendingOperationType type = RemapDrumControllers) { _type = type; _midi_ctrl_val_remap_operation = operation; } // The operation is constructed and allocated in non-realtime before the call, then the track's map is modified in realtime stage, // then operation is deleted in non-realtime stage. PendingOperationItem(DrumMapTrackOperation* operation, PendingOperationType type = ModifyTrackDrumMapItem) { _type = type; _drum_map_track_operation = operation; } // The operation is constructed and allocated in non-realtime before the call, then the track's map is modified in realtime stage, // then operation is deleted in non-realtime stage. PendingOperationItem(DrumMapTrackPatchReplaceOperation* operation, PendingOperationType type = ReplaceTrackDrumMapPatchList) { _type = type; _drum_map_track_patch_replace_operation = operation; } PendingOperationItem(MidiPort* mp, PendingOperationType type = UpdateDrumMaps) { _type = type; _midi_port = mp; } PendingOperationItem(TrackList* tl, PendingOperationType type = UpdateSoloStates) { _type = type; _track_list = tl; } // TODO: Try to break this operation down so that only the actual operation is executed stage-2. PendingOperationItem(const Route& src_route, const Route& dst_route, PendingOperationType type) // Type is AddRoute or DeleteRoute. { _type = type; _src_route = src_route; _dst_route = dst_route; } PendingOperationItem(RouteList* route_list, const Route& route, PendingOperationType type = AddRouteNode) { _type = type; _route_list = route_list; _src_route = route; } PendingOperationItem(RouteList* route_list, const iRoute& ir, PendingOperationType type = DeleteRouteNode) { _type = type; _route_list = route_list; _iRoute = ir; } PendingOperationItem(const Route& src_route, Route* dst_route, PendingOperationType type = ModifyRouteNode) { _type = type; _src_route = src_route; _dst_route_pointer = dst_route; } PendingOperationItem(AuxSendValueList* asvl, double val, PendingOperationType type = AddAuxSendValue) { _type = type; _aux_send_value_list = asvl; _aux_send_value = val; } PendingOperationItem(MidiInstrumentList* mil, MidiInstrument* midi_instrument, PendingOperationType type = AddMidiInstrument) { _type = type; _midi_instrument_list = mil; _midi_instrument = midi_instrument; } PendingOperationItem(MidiInstrumentList* mil, const iMidiInstrument& imi, PendingOperationType type = DeleteMidiInstrument) { _type = type; _midi_instrument_list = mil; _iMidiInstrument = imi; } PendingOperationItem(MidiInstrumentList* mil, const iMidiInstrument& imi, MidiInstrument* new_instrument, PendingOperationType type = ReplaceMidiInstrument) { _type = type; _midi_instrument_list = mil; _iMidiInstrument = imi; _midi_instrument = new_instrument; } PendingOperationItem(MidiDeviceList* mdl, MidiDevice* midi_device, PendingOperationType type = AddMidiDevice) { _type = type; _midi_device_list = mdl; _midi_device = midi_device; } PendingOperationItem(MidiDeviceList* mdl, const iMidiDevice& imd, PendingOperationType type = DeleteMidiDevice) { _type = type; _midi_device_list = mdl; _iMidiDevice = imd; } // Type is ModifyMidiDeviceAddress or ModifyMidiDeviceFlags PendingOperationItem(MidiDevice* midi_device, int address_client_or_rw_flags, int address_port_or_open_flags, PendingOperationType type) { _type = type; _midi_device = midi_device; _intA = address_client_or_rw_flags; _intB = address_port_or_open_flags; } PendingOperationItem(MidiDevice* midi_device, const QString* new_name, PendingOperationType type = ModifyMidiDeviceName) { _type = type; _midi_device = midi_device; _name = new_name; } PendingOperationItem(MidiPort* midi_port, MidiInstrument* midi_instrument, PendingOperationType type = SetInstrument) { _type = type; _midi_port = midi_port; _midi_instrument = midi_instrument; } PendingOperationItem(TrackList* tl, Track* track, int insert_at, PendingOperationType type = AddTrack, void* sec_track_list = 0) { _type = type; _track_list = tl; _track = track; _insert_at = insert_at; _void_track_list = sec_track_list; } PendingOperationItem(TrackList* tl, Track* track, PendingOperationType type = DeleteTrack, void* sec_track_list = 0) { _type = type; _track_list = tl; _track = track; _void_track_list = sec_track_list; } PendingOperationItem(TrackList* tl, int from_idx, int to_idx, PendingOperationType type = MoveTrack) { _type = type; _track_list = tl; _from_idx = from_idx; _to_idx = to_idx; } PendingOperationItem(TrackList* tl, bool select, unsigned long /*t0*/, unsigned long /*t1*/, PendingOperationType type = GlobalSelectAllEvents) { _type = type; _track_list = tl; _boolA = select; } PendingOperationItem(Track* track, const QString* new_name, PendingOperationType type = ModifyTrackName) { _type = type; _track = track; _name = new_name; } // type is SetTrackRecord, SetTrackMute, SetTrackSolo, SetTrackRecMonitor, SetTrackOff PendingOperationItem(Track* track, bool v, PendingOperationType type) { _type = type; _track = track; _boolA = v; } PendingOperationItem(Part* part, const QString* new_name, PendingOperationType type = ModifyPartName) { _type = type; _part = part; _name = new_name; } PendingOperationItem(Part* part, bool v, PendingOperationType type = SelectPart) { _type = type; _part = part; _boolA = v; } // new_pos and new_len must already be in the part's time domain (ticks or frames). // The new_event_list can be set to null, or if the part's events are to be dragged with the border // it can be supplied to do a wholesale fast constant-time swap of the event lists. PendingOperationItem(iPart ip, Part* part, unsigned int new_pos, unsigned int new_len, EventList* new_event_list, PendingOperationType type = ModifyPartStart) { _type = type; _iPart = ip, _part = part; _event_list = new_event_list; _posLenVal = new_pos; _lenVal = new_len; } // new_len must already be in the part's time domain (ticks or frames). // The new_event_list can be set to null, or if the part's events are to be dragged with the border // it can be supplied to do a wholesale fast constant-time swap of the event lists. PendingOperationItem(iPart ip, Part* part, unsigned int new_len, EventList* new_event_list, PendingOperationType type = ModifyPartLength) { _type = type; _iPart = ip, _part = part; _event_list = new_event_list; _posLenVal = new_len; } // Erases ip from part->track()->parts(), then adds part to new_track. NOTE: ip may be part->track()->parts()->end(). // new_pos must already be in the part's time domain (ticks or frames). PendingOperationItem(iPart ip, Part* part, unsigned int new_pos, PendingOperationType type = MovePart, Track* new_track = 0) { _type = type; _iPart = ip; _part = part; _track = new_track; _posLenVal = new_pos;} PendingOperationItem(PartList* pl, Part* part, PendingOperationType type = AddPart) { _type = type; _part_list = pl; _part = part; } PendingOperationItem(PartList* pl, const iPart& ip, PendingOperationType type = DeletePart) { _type = type; _part_list = pl; _iPart = ip; } PendingOperationItem(Part* part, const Event& ev, PendingOperationType type = AddEvent) { _type = type; _part = part; _ev = ev; } // NOTE: To avoid possibly deleting the event in RT stage 2 when the event is erased from the list, // _ev is used simply to hold a reference until non-RT stage 3 or after, when the list is cleared. PendingOperationItem(Part* part, const iEvent& iev, PendingOperationType type = DeleteEvent) { _type = type; _part = part; _iev = iev; _ev = iev->second; } // Type is SelectEvent, or some (likely) future boolean operation. PendingOperationItem(Part* part, const Event& ev, int v, PendingOperationType type) { _type = type; _part = part; _ev = ev; _intA = v; } PendingOperationItem(MidiCtrlValListList* mcvll, MidiCtrlValList* mcvl, int channel, int control_num, PendingOperationType type = AddMidiCtrlValList) { _type = type; _mcvll = mcvll; _mcvl = mcvl; _intA = channel; _intB = control_num; } PendingOperationItem(const iCtrlList& ictl_l, CtrlList* ctrl_l, PendingOperationType type = ModifyAudioCtrlValList) { _type = type; _iCtrlList = ictl_l; _aud_ctrl_list = ctrl_l; } PendingOperationItem(MidiCtrlValList* mcvl, Part* part, unsigned int tick, int val, PendingOperationType type = AddMidiCtrlVal) { _type = type; _mcvl = mcvl; _part = part; _posLenVal = tick; _intB = val; } PendingOperationItem(MidiCtrlValList* mcvl, const iMidiCtrlVal& imcv, PendingOperationType type = DeleteMidiCtrlVal) { _type = type; _mcvl = mcvl; _imcv = imcv; } // NOTE: mcvl is supplied in case the operation needs to be merged, or transformed into an AddMidiCtrlVal. PendingOperationItem(MidiCtrlValList* mcvl, const iMidiCtrlVal& imcv, int val, PendingOperationType type = ModifyMidiCtrlVal) { _type = type; _mcvl = mcvl; _imcv = imcv; _intA = val; } PendingOperationItem(MidiCtrlValList* orig_mcvl, MidiCtrlValList* mcvl, PendingOperationType type = ModifyMidiCtrlValList) { _type = type; _orig_mcvl = orig_mcvl; _mcvl = mcvl; } PendingOperationItem(CtrlList* ctrl_l, unsigned int frame, double ctrl_val, PendingOperationType type = AddAudioCtrlVal) { _type = type; _aud_ctrl_list = ctrl_l; _posLenVal = frame; _ctl_dbl_val = ctrl_val; } PendingOperationItem(CtrlList* ctrl_l, const iCtrl& ictl, PendingOperationType type = DeleteAudioCtrlVal) { _type = type; _aud_ctrl_list = ctrl_l; _iCtrl = ictl; } // NOTE: ctrl_l is supplied in case the operation needs to be merged, or transformed into an AddAudioCtrlVal. PendingOperationItem(CtrlList* ctrl_l, const iCtrl& ictl, unsigned int new_frame, double new_ctrl_val, PendingOperationType type = ModifyAudioCtrlVal) { _type = type; _aud_ctrl_list = ctrl_l; _iCtrl = ictl; _posLenVal = new_frame; _ctl_dbl_val = new_ctrl_val; } // Takes ownership of the original list so it can be deleted in the non-RT stage. PendingOperationItem(TempoList* orig_tempo_l, TempoList* new_tempo_l, PendingOperationType type = ModifyTempoList) { _type = type; _orig_tempo_list = orig_tempo_l; _tempo_list = new_tempo_l; } // type is SetGlobalTempo, SetStaticTempo. PendingOperationItem(TempoList* tl, int tempo, PendingOperationType type) { _type = type; _tempo_list = tl; _intA = tempo; } PendingOperationItem(TempoList* tl, bool v, PendingOperationType type = SetUseMasterTrack) { _type = type; _tempo_list = tl; _boolA = v; } // Takes ownership of the original list so it can be deleted in the non-RT stage. PendingOperationItem(SigList* orig_sig_l, SigList* new_sig_l, PendingOperationType type = ModifySigList) { _type = type; _orig_sig_list = orig_sig_l; _sig_list = new_sig_l; } // Takes ownership of the original list so it can be deleted in the non-RT stage. PendingOperationItem(KeyList* orig_key_l, KeyList* new_key_l, PendingOperationType type = ModifyKeyList) { _type = type; _orig_key_list = orig_key_l; _key_list = new_key_l; } PendingOperationItem(int stretchType, StretchList* sl, MuseFrame_t frame, double ratio, PendingOperationType type = AddStretchListRatioAt) { _type = type; _stretch_type = stretchType, _stretch_list = sl; _museFrame = frame; _audio_converter_value = ratio; } PendingOperationItem(int stretchTypes, StretchList* sl, const iStretchListItem& ise, PendingOperationType type = DeleteStretchListRatioAt) { _type = type; _stretch_type = stretchTypes, _stretch_list = sl; _iStretchEvent = ise; } PendingOperationItem(int stretchType, StretchList* sl, const iStretchListItem& ise, MuseFrame_t new_frame, double ratio, PendingOperationType type = ModifyStretchListRatioAt) { _type = type; _stretch_type = stretchType, _stretch_list = sl; _iStretchEvent = ise; _museFrame = new_frame; _audio_converter_value = ratio; } PendingOperationItem(int stretchType, StretchList* sl, double ratio, PendingOperationType type = ModifyStretchListRatio) { _type = type; _stretch_type = stretchType, _stretch_list = sl; _audio_converter_value = ratio; } PendingOperationItem(unsigned int len, PendingOperationType type = ModifySongLength) { _type = type; _posLenVal = len; } PendingOperationItem(MetroAccentsMap** old_map, MetroAccentsMap* new_map, PendingOperationType type = ModifyMetronomeAccentMap) { _type = type; _metroAccentsMap = old_map; _newMetroAccentsMap = new_map; } // Type is SwitchMetronomeSettings, SetExternalSyncFlag, SetUseJackTransport. PendingOperationItem(bool* bool_pointer, bool v, PendingOperationType type) { _type = type; _bool_pointer = bool_pointer; _boolA = v; } PendingOperationItem(PendingOperationType type) // type is EnableAllAudioControllers. { _type = type; } // Takes ownership of the original list so it can be deleted in the non-RT stage. PendingOperationItem(MarkerList** orig_marker_l, MarkerList* new_marker_l, PendingOperationType type = ModifyMarkerList) { _type = type; _orig_marker_list = orig_marker_l; _marker_list = new_marker_l; } PendingOperationItem() { _type = Uninitialized; } // Execute the operation. Called only from RT stage 2. SongChangedStruct_t executeRTStage(); // Execute the operation. Called only from post RT stage 3. SongChangedStruct_t executeNonRTStage(); // Get an appropriate indexing value from ops like AddEvent that use it. Other ops like AddMidiCtrlValList return their type (rather than say, zero). unsigned int getIndex() const; // Whether the two special allocating ops (like AddMidiCtrlValList) are the same. // The comparison ignores the actual allocated value, so that such commands can be found before they do their allocating. bool isAllocationOp(const PendingOperationItem&) const; }; class PendingOperationList : public std::list { private: // Holds sorted version of list. Index is time value for items which have it like events or parts, // otherwise it is the operation type for other items. It doesn't matter too much that ticks and frames // are mixed here, sorting by time is just to speed up searches, we look for operation types. std::multimap > _map; // Accumulated song changed flags. SongChangedStruct_t _sc_flags; public: PendingOperationList() : _sc_flags(0) { } // Add an operation. Returns false if the operation already exists or could not be added // (such as with a DeleteEvent being cancelled by an AddEvent on identical base events), // or various other optimizations that essentially render the operation ineffectual. // Some operations optimize by replacing an existing operation with the requested one. // For example requesting operations which simply change a value replace any existing // such operation with the requested new value. Thus only one operation exists - the LATEST. // Returns true in those cases so that the caller can proceed to perform other operations // on the item even though the operation was not officially added to the list (it replaced one). // Otherwise returns true. Optimizes all added items (merge, discard, alter, embellish etc.) bool add(PendingOperationItem); // Execute the RT portion of the operations contained in the list. Called only from RT stage 2. SongChangedStruct_t executeRTStage(); // Execute the Non-RT portion of the operations contained in the list. Called only from post RT stage 3. SongChangedStruct_t executeNonRTStage(); // Clear both the list and the map, and flags. void clear(); // Returns the accumulated song changed flags. SongChangedStruct_t flags() const { return _sc_flags; } // Find an existing special allocation command (like AddMidiCtrlValList). // The comparison ignores the actual allocated value, so that such commands can be found before they do their allocating. iterator findAllocationOp(const PendingOperationItem& op); void addDeviceOperation(MidiDeviceList* devlist, MidiDevice* dev); void addTrackPortCtrlEvents(Track* track); void removeTrackPortCtrlEvents(Track* track); void addPartPortCtrlEvents( const Event& event, Part* part, unsigned int tick, unsigned int /*len*/, Track* track); void addPartPortCtrlEvents(Part* part, unsigned int tick, unsigned int len, Track* track); bool removePartPortCtrlEvents(const Event& event, Part* part, Track* track); void removePartPortCtrlEvents(Part* part, Track* track); void modifyPartPortCtrlEvents(const Event& old_event, const Event& event, Part* part); void addPartOperation(PartList *partlist, Part* part); void delPartOperation(PartList *partlist, Part* part); void movePartOperation(PartList *partlist, Part* part, unsigned int new_pos, Track* track = 0); void modifyPartStartOperation(Part* part, unsigned int new_pos, unsigned int new_len, int64_t events_offset, Pos::TType events_offset_time_type); void modifyPartLengthOperation(Part* part, unsigned int new_len, int64_t events_offset, Pos::TType events_offset_time_type); void addTrackAuxSendOperation(AudioTrack *atrack, int n); //void TrackMidiCtrlRemapOperation(int index, int newPort, int newChan, int newNote, MidiCtrlValRemapOperation* rmop); }; typedef PendingOperationList::iterator iPendingOperation; typedef std::multimap >::iterator iPendingOperationSorted; typedef std::multimap >::reverse_iterator riPendingOperationSorted; typedef std::pair iPendingOperationSortedRange; extern void TrackMidiCtrlRemapOperation( MidiTrack *mtrack, int index, int newPort, int newChan, int newNote, MidiCtrlValRemapOperation* rmop); } // namespace MusECore #endif muse-4.0.0/src/muse/osc.cpp000066400000000000000000001107521404105311000155070ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // $Id: osc.cpp,v 1.0.0.0 2010/04/22 03:39:58 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "config.h" #ifdef OSC_SUPPORT // Turn on debugging messages //#define OSC_DEBUG // Whether to use a QProcess or fork + execlp to start the gui. (Note fork + execlp give problems - zombies when synth window closed.) #define _USE_QPROCESS_FOR_GUI_ 1 #include #include #include #include "muse_math.h" #include #include #include #ifdef _USE_QPROCESS_FOR_GUI_ #include #else #include #include #endif #include #ifdef DSSI_SUPPORT #include "dssihost.h" #endif #include "stringparam.h" #include "plugin.h" #include "track.h" #include "song.h" #include "synth.h" #include "app.h" #include "globals.h" #include "globaldefs.h" #endif // OSC_SUPPORT namespace MusECore { #ifdef OSC_SUPPORT static lo_server_thread serverThread = 0; static char* url = 0; static bool oscServerRunning = false; //--------------------------------------------------------- // oscError //--------------------------------------------------------- static void oscError(int num, const char *msg, const char *path) { fprintf(stderr, "MusE: liblo server error %d in path %s: %s\n", num, path, msg); } //--------------------------------------------------------- // oscDebugHandler //--------------------------------------------------------- static int oscDebugHandler(const char* path, const char* types, lo_arg** argv, int argc, void*, void*) { fprintf(stderr, "MusE: got unhandled OSC message:\n path: <%s>\n", path); for (int i = 0; i < argc; i++) { fprintf(stderr, " arg %d '%c' ", i, types[i]); lo_arg_pp(lo_type(types[i]), argv[i]); fprintf(stderr, "\n"); } return 1; } //--------------------------------------------------------- // oscMessageHandler //--------------------------------------------------------- int oscMessageHandler(const char* path, const char* types, lo_arg** argv, int argc, void* data, void* user_data) { const char* p = path; // NOTE: Tried this, always returns 0 sec and 1 fractional. Shame, looks like timestamps are not used. //lo_timetag lo_tt = lo_message_get_timestamp(data); #ifdef OSC_DEBUG if(argc) { fprintf(stderr, "oscMessageHandler: path:%s argc:%d\n", path, argc); for(int i = 0; i < argc; ++i) { fprintf(stderr, " "); lo_arg_pp((lo_type)types[i], argv[i]); } fprintf(stderr, "\n"); } else { fprintf(stderr, "%s\n", path); fprintf(stderr, "oscMessageHandler: no args, path:%s\n", path); } #endif #if defined(DSSI_SUPPORT) || defined(OSC_DEBUG) bool isSynth = false; #endif #ifdef DSSI_SUPPORT if(strncmp(p, "/dssi_synth/", 12) == 0) { isSynth = true; p += 12; } else #endif if(strncmp(p, "/ladspa_efx/", 12) == 0) { p += 12; } else return oscDebugHandler(path, types, argv, argc, data, user_data); TrackList* tl = MusEGlobal::song->tracks(); #ifdef OSC_DEBUG if(isSynth) fprintf(stderr, "oscMessageHandler: got message for dssi synth...\n"); else fprintf(stderr, "oscMessageHandler: got message for ladspa effect...\n"); #endif #ifdef DSSI_SUPPORT if(isSynth) { // Message is meant for a dssi synth. Check dssi synth instances... SynthIList* sl = MusEGlobal::song->syntis(); for(iSynthI si = sl->begin(); si != sl->end(); ++si) { SynthI* synti = *si; #ifdef OSC_DEBUG fprintf(stderr, "oscMessageHandler: searching for:%s checking synth instance:%s\n", p, synti->name().toLatin1().constData()); #endif QByteArray ba = synti->name().toLatin1(); const char* sub = strstr(p, ba.constData()); if(sub == NULL) continue; if(!synti->sif() || !synti->synth() || synti->synth()->synthType() != MusECore::Synth::DSSI_SYNTH) continue; DssiSynthIF* instance = static_cast(synti->sif()); p = sub + ba.length(); if (*p != '/' || *(p + 1) == 0) { fprintf(stderr, "oscMessageHandler error: synth: end of path or no /\n"); return oscDebugHandler(path, types, argv, argc, data, user_data); } ++p; #ifdef OSC_DEBUG fprintf(stderr, "oscMessageHandler: synth track:%s method:%s\n", synti->name().toLatin1().constData(), p); #endif OscIF& oscif = instance->oscIF(); if (!strcmp(p, "configure") && argc == 2 && !strcmp(types, "ss")) return oscif.oscConfigure(argv); else if (!strcmp(p, "control") && argc == 2 && !strcmp(types, "if")) return oscif.oscControl(argv); else if (!strcmp(p, "midi") && argc == 1 && !strcmp(types, "m")) return oscif.oscMidi(argv); else if (!strcmp(p, "program") && argc == 2 && !strcmp(types, "ii")) return oscif.oscProgram(argv); else if (!strcmp(p, "update") && argc == 1 && !strcmp(types, "s")) return oscif.oscUpdate(argv); else if (!strcmp(p, "exiting") && argc == 0) return oscif.oscExiting(argv); return oscDebugHandler(path, types, argv, argc, data, user_data); } } else #endif //DSSI_SUPPORT // Message is meant for a ladspa effect. Check all ladspa effect instances... for(ciTrack it = tl->begin(); it != tl->end(); ++it) { if((*it)->isMidiTrack()) continue; Pipeline* efxPipe = ((AudioTrack*)*it)->efxPipe(); if(efxPipe) { for(ciPluginI ip = efxPipe->begin(); ip != efxPipe->end(); ++ip) { PluginI* instance = *ip; if(!instance) continue; #ifdef OSC_DEBUG fprintf(stderr, "oscMessageHandler: searching for:%s checking effect instance:%s label:%s lib:%s\n", p, instance->name().toLatin1().constData(), instance->label().toLatin1().constData(), instance->lib().toLatin1().constData()); #endif QByteArray ba = instance->label().toLatin1(); const char* sub = strstr(p, ba.constData()); if(sub == NULL) continue; Plugin* plugin = instance->plugin(); if(!plugin) break; p = sub + ba.length(); if (*p != '/' || *(p + 1) == 0) { fprintf(stderr, "oscMessageHandler: error: effect: end of path or no /\n"); return oscDebugHandler(path, types, argv, argc, data, user_data); } ++p; #ifdef OSC_DEBUG fprintf(stderr, "oscMessageHandler: effect:%s method:%s\n", instance->label().toLatin1().constData(), p); #endif OscIF& oscif = instance->oscIF(); if (!strcmp(p, "configure") && argc == 2 && !strcmp(types, "ss")) return oscif.oscConfigure(argv); else if (!strcmp(p, "control") && argc == 2 && !strcmp(types, "if")) return oscif.oscControl(argv); else if (!strcmp(p, "midi") && argc == 1 && !strcmp(types, "m")) return oscif.oscMidi(argv); else if (!strcmp(p, "program") && argc == 2 && !strcmp(types, "ii")) return oscif.oscProgram(argv); else if (!strcmp(p, "update") && argc == 1 && !strcmp(types, "s")) return oscif.oscUpdate(argv); else if (!strcmp(p, "exiting") && argc == 0) return oscif.oscExiting(argv); return oscDebugHandler(path, types, argv, argc, data, user_data); } } } fprintf(stderr, "oscMessageHandler: timeout error: no synth or effect instance found for given path\n"); return oscDebugHandler(path, types, argv, argc, data, user_data); } //--------------------------------------------------------- // initOSC //--------------------------------------------------------- void initOSC() { if(url) free(url); url = 0; // Create OSC thread // Only if not created yet. if(!serverThread) { serverThread = lo_server_thread_new(0, oscError); if(!serverThread) { fprintf(stderr, "initOSC() Failed to create OSC server!\n"); return; } } url = lo_server_thread_get_url(serverThread); if(!url) { lo_server_thread_free(serverThread); fprintf(stderr, "initOSC() Failed to get OSC server thread url !\n"); return; } lo_method meth = 0; meth = lo_server_thread_add_method(serverThread, 0, 0, oscMessageHandler, 0); if(!meth) { fprintf(stderr, "initOSC() Failed to add oscMessageHandler method to OSC server!\n"); // Does not return a value. lo_server_thread_free(serverThread); serverThread = 0; free(url); url = 0; return; } #ifdef OSC_DEBUG fprintf(stderr, "initOSC() url:%s\n", url); #endif // Does not return a value. lo_server_thread_start(serverThread); } //--------------------------------------------------------- // exitOSC //--------------------------------------------------------- void exitOSC() { oscServerRunning = false; if(serverThread) { // Does not return a value. lo_server_thread_stop(serverThread); lo_server_thread_free(serverThread); serverThread = 0; } if(url) { free(url); url = 0; } } //--------------------------------------------------------- // startOSC //--------------------------------------------------------- void startOSC() { if(serverThread) // Does not return a value. lo_server_thread_start(serverThread); oscServerRunning = true; } //--------------------------------------------------------- // stopOSC //--------------------------------------------------------- void stopOSC() { if(serverThread) // Does not return a value. lo_server_thread_stop(serverThread); oscServerRunning = false; } //--------------------------------------------------------- // OscIF // Open Sound Control Interface //--------------------------------------------------------- OscIF::OscIF() { _uiOscTarget = 0; _uiOscSampleRatePath = 0; _uiOscShowPath = 0; _uiOscControlPath = 0; _uiOscConfigurePath = 0; _uiOscProgramPath = 0; _uiOscPath = 0; #ifdef _USE_QPROCESS_FOR_GUI_ _oscGuiQProc = 0; #else _guiPid = -1; #endif _oscGuiVisible = false; old_prog=old_bank=0xDEADBEEF; old_control=NULL; control_port_mapper=NULL; maxDssiPort=0; } OscIF::~OscIF() { #ifdef _USE_QPROCESS_FOR_GUI_ if(_oscGuiQProc) { if(_oscGuiQProc->state() != QProcess::NotRunning) { #ifdef OSC_DEBUG fprintf(stderr, "OscIF::~OscIF terminating _oscGuiQProc\n"); #endif _oscGuiQProc->terminate(); _oscGuiQProc->waitForFinished(3000); } delete _oscGuiQProc; } #else // NOT _USE_QPROCESS_FOR_GUI_ if (_guiPid != -1) { if(kill(_guiPid, SIGHUP) != -1) _guiPid = -1; } #endif // _USE_QPROCESS_FOR_GUI_ if(_uiOscTarget) lo_address_free(_uiOscTarget); if(_uiOscSampleRatePath) free(_uiOscSampleRatePath); if(_uiOscShowPath) free(_uiOscShowPath); if(_uiOscControlPath) free(_uiOscControlPath); if(_uiOscConfigurePath) free(_uiOscConfigurePath); if(_uiOscProgramPath) free(_uiOscProgramPath); if(_uiOscPath) free(_uiOscPath); if (old_control) delete [] old_control; } //--------------------------------------------------------- // oscUpdate //--------------------------------------------------------- int OscIF::oscUpdate(lo_arg **argv) { const char *purl = (char *)&argv[0]->s; if(_uiOscTarget) lo_address_free(_uiOscTarget); _uiOscTarget = 0; char* host = lo_url_get_hostname(purl); char* port = lo_url_get_port(purl); _uiOscTarget = lo_address_new(host, port); free(host); free(port); if(!_uiOscTarget) return 0; if (_uiOscPath) free(_uiOscPath); _uiOscPath = lo_url_get_path(purl); int pl = strlen(_uiOscPath); if(_uiOscSampleRatePath) free(_uiOscSampleRatePath); _uiOscSampleRatePath = (char *)malloc(pl + 14); sprintf(_uiOscSampleRatePath, "%s/sample-rate", _uiOscPath); if (_uiOscControlPath) free(_uiOscControlPath); _uiOscControlPath = (char *)malloc(pl + 10); sprintf(_uiOscControlPath, "%s/control", _uiOscPath); if (_uiOscConfigurePath) free(_uiOscConfigurePath); _uiOscConfigurePath = (char *)malloc(pl + 12); sprintf(_uiOscConfigurePath, "%s/configure", _uiOscPath); if (_uiOscProgramPath) free(_uiOscProgramPath); _uiOscProgramPath = (char *)malloc(pl + 10); sprintf(_uiOscProgramPath, "%s/program", _uiOscPath); if (_uiOscShowPath) free(_uiOscShowPath); _uiOscShowPath = (char *)malloc(pl + 10); sprintf(_uiOscShowPath, "%s/show", _uiOscPath); /* At this point a more substantial host might also call * configure() on the UI to set any state that it had remembered * for the plugin instance. But we don't remember state for * plugin instances (see our own configure() implementation in * osc_configure_handler), and so we have nothing to send except * the optional project directory. */ #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscUpdate: _uiOscTarget:%p\n", _uiOscTarget); if(url) fprintf(stderr, " server url:%s\n", url); else fprintf(stderr, " no server url!\n"); fprintf(stderr, " update path:%s\n", purl); fprintf(stderr, " _uiOscPath:%s\n", _uiOscPath); fprintf(stderr, " _uiOscSampleRatePath:%s\n", _uiOscSampleRatePath); fprintf(stderr, " _uiOscConfigurePath:%s\n", _uiOscConfigurePath); fprintf(stderr, " _uiOscProgramPath:%s\n", _uiOscProgramPath); fprintf(stderr, " _uiOscControlPath:%s\n",_uiOscControlPath); fprintf(stderr, " _uiOscShowPath:%s\n", _uiOscShowPath); fprintf(stderr, " museProject:%s\n", MusEGlobal::museProject.toLatin1().constData()); #endif // Send sample rate. lo_send(_uiOscTarget, _uiOscSampleRatePath, "i", MusEGlobal::sampleRate); // DELETETHIS 46 // Send project directory. //lo_send(_uiOscTarget, _uiOscConfigurePath, "ss", // DSSI_PROJECT_DIRECTORY_KEY, museProject.toLatin1().constData()); // MusEGlobal::song->projectPath() // Done in sub-classes. /* #ifdef DSSI_SUPPORT //lo_send(_uiOscTarget, _uiOscConfigurePath, "ss", //DSSI_PROJECT_DIRECTORY_KEY, MusEGlobal::song->projectPath().toLatin1().data()); lo_send(_uiOscTarget, _uiOscConfigurePath, "ss", DSSI_PROJECT_DIRECTORY_KEY, museProject.toLatin1().constData()); if(_oscSynthIF) { for(ciStringParamMap r = _oscSynthIF->synthI->_stringParamMap.begin(); r != synti->_stringParamMap.end(); ++r) { rv = 0; rv = dssi->configure(handle, r->first.c_str(), r->second.c_str()); if(rv) { fprintf(stderr, "MusE: Warning: plugin config key: %s value: %s \"%s\"\n", r->first.c_str(), r->second.c_str(), rv); free(rv); } } } #endif */ /* char uiOscGuiPath[strlen(_uiOscPath)+6]; sprintf(uiOscGuiPath, "%s/%s", _uiOscPath, "show"); #ifdef OSC_DEBUG printf("OscIF::oscUpdate Sending show uiOscGuiPath:%s\n", uiOscGuiPath); #endif lo_send(_uiOscTarget, uiOscGuiPath, ""); sprintf(uiOscGuiPath, "%s/%s", _uiOscPath, "hide"); #ifdef OSC_DEBUG printf("OscIF::oscUpdate Sending hide uiOscGuiPath:%s\n", uiOscGuiPath); #endif lo_send(_uiOscTarget, uiOscGuiPath, ""); */ // DELETETHIS 22 #if 0 /* Send current bank/program (-FIX- another race...) */ if (instance->pendingProgramChange < 0) { unsigned long bank = instance->currentBank; unsigned long program = instance->currentProgram; instance->uiNeedsProgramUpdate = 0; if (instance->uiTarget) { lo_send(instance->uiTarget, instance->ui_osc_program_path, "ii", bank, program); } } /* Send control ports */ for (i = 0; i < instance->plugin->controlIns; i++) { int in = i + instance->firstControlIn; int port = pluginControlInPortNumbers[in]; lo_send(instance->uiTarget, instance->ui_osc_control_path, "if", port, pluginControlIns[in]); /* Avoid overloading the GUI if there are lots and lots of ports */ if ((i+1) % 50 == 0) usleep(300000); } #endif return 0; } //--------------------------------------------------------- // oscExiting //--------------------------------------------------------- int OscIF::oscExiting(lo_arg**) { // The gui is gone now, right? _oscGuiVisible = false; // DELETETHIS 52 // Just an attempt to really kill the process, an attempt to fix dssi-vst gui // not re-showing after closing. Doesn't help. It's a design problem with dssi-vst. /* #ifdef _USE_QPROCESS_FOR_GUI_ if(_oscGuiQProc) { if(_oscGuiQProc->state() != QProcess::NotRunning) { #ifdef OSC_DEBUG printf("OscIF::oscExiting terminating _oscGuiQProc\n"); #endif //_oscGuiQProc->kill(); // "This tries to terminate the process the nice way. If the process is still running after 5 seconds, // it terminates the process the hard way. The timeout should be chosen depending on the time the // process needs to do all its cleanup: use a higher value if the process is likely to do a lot of // computation or I/O on cleanup." _oscGuiQProc->terminate(); // FIXME: In Qt4 this can only be used with threads started with QThread. // Kill is bad anyway, app should check at close if all these guis closed or not // and ask user if they really want to close, possibly with kill. // Terminate might not terminate the thread. It is given a chance to prompt for saving etc. // so kill is not desirable. // We could wait until terminate finished but don't think that's good here. ///QTimer::singleShot( 5000, _oscGuiQProc, SLOT( kill() ) ); _oscGuiQProc->waitForFinished(3000); } //delete _oscGuiQProc; //_oscGuiQProc = 0; } #else // NOT _USE_QPROCESS_FOR_GUI_ if(_guiPid != -1) { #ifdef OSC_DEBUG printf("OscIF::oscExiting hanging up _guiPid:%d\n", _guiPid); #endif //if(kill(_guiPid, SIGHUP) != -1) //if(kill(_guiPid, SIGTERM) != -1) if(kill(_guiPid, SIGKILL) != -1) { #ifdef OSC_DEBUG printf(" hang up sent\n"); #endif _guiPid = -1; } } #endif // _USE_QPROCESS_FOR_GUI_ */ if(_uiOscTarget) lo_address_free(_uiOscTarget); _uiOscTarget = 0; if(_uiOscSampleRatePath) free(_uiOscSampleRatePath); _uiOscSampleRatePath = 0; if(_uiOscShowPath) free(_uiOscShowPath); _uiOscShowPath = 0; if(_uiOscControlPath) free(_uiOscControlPath); _uiOscControlPath = 0; if(_uiOscConfigurePath) free(_uiOscConfigurePath); _uiOscConfigurePath = 0; if(_uiOscProgramPath) free(_uiOscProgramPath); _uiOscProgramPath = 0; if(_uiOscPath) free(_uiOscPath); _uiOscPath = 0; // DELETETHIS 20 //const DSSI_Descriptor* dssi = synth->dssi; //const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; //if(ld->deactivate) // ld->deactivate(handle); /* if (_uiOscPath == 0) { printf("OscIF::oscExiting(): no _uiOscPath\n"); return 1; } char uiOscGuiPath[strlen(_uiOscPath)+6]; sprintf(uiOscGuiPath, "%s/%s", _uiOscPath, "quit"); #ifdef OSC_DEBUG printf("OscIF::oscExiting(): sending quit to uiOscGuiPath:%s\n", uiOscGuiPath); #endif lo_send(_uiOscTarget, uiOscGuiPath, ""); */ // DELETETHIS 37 #if 0 int i; if (verbose) { printf("MusE: OSC: got exiting notification for instance %d\n", instance->number); } if (instance->plugin) { /*!!! No, this isn't safe -- plugins deactivated in this way would still be included in a run_multiple_synths call unless we re-jigged the instance array at the same time -- leave it for now if (instance->plugin->descriptor->LADSPA_Plugin->deactivate) { instance->plugin->descriptor->LADSPA_Plugin->deactivate (instanceHandles[instance->number]); } */ /* Leave this flag though, as we need it to determine when to exit */ instance->inactive = 1; } /* Do we have any plugins left running? */ for (i = 0; i < instance_count; ++i) { if (!instances[i].inactive) return 0; } if (verbose) { printf("MusE: That was the last remaining plugin, exiting...\n"); } exiting = 1; #endif return 0; } //--------------------------------------------------------- // oscSendProgram //--------------------------------------------------------- void OscIF::oscSendProgram(unsigned long prog, unsigned long bank, bool force) { if(_uiOscTarget && _uiOscProgramPath && (bank!=old_bank || prog!=old_prog || force) ) { lo_send(_uiOscTarget, _uiOscProgramPath, "ii", bank, prog); old_bank=bank; old_prog=prog; } } //--------------------------------------------------------- // oscSendControl //--------------------------------------------------------- void OscIF::oscSendControl(unsigned long dssiPort, float v, bool force) { if(_uiOscTarget && _uiOscControlPath && ((dssiPortat(dssiPort)]!=v) || force) ) { lo_send(_uiOscTarget, _uiOscControlPath, "if", dssiPort, v); old_control[control_port_mapper->at(dssiPort)]=v; } } //--------------------------------------------------------- // oscSendConfigure //--------------------------------------------------------- void OscIF::oscSendConfigure(const char *key, const char *val) { if(_uiOscTarget && _uiOscConfigurePath) lo_send(_uiOscTarget, _uiOscConfigurePath, "ss", key, val); } //--------------------------------------------------------- // oscInitGui //--------------------------------------------------------- bool OscIF::oscInitGui(const QString& typ, const QString& baseName, const QString& name, const QString& label, const QString& filePath, const QString& guiPath, const std::vector* control_port_mapper_) { if (old_control==NULL) { control_port_mapper=control_port_mapper_; unsigned long nDssiPorts=0; for (unsigned i=0;isize();i++) if (control_port_mapper->at(i)!=(unsigned long)-1 && control_port_mapper->at(i)+1 > nDssiPorts) nDssiPorts=control_port_mapper->at(i)+1; old_control=new float[nDssiPorts]; for (unsigned long i=0;isize();i++) if (control_port_mapper->at(i)!=(unsigned long)-1 && control_port_mapper->at(i)+1 > nDssiPorts) nDssiPorts=control_port_mapper->at(i)+1; if (maxDssiPort!=nDssiPorts) { // this should never happen, right? fprintf(stderr, "STRANGE: nDssiPorts has changed (old=%lu, now=%lu)!\n", maxDssiPort, nDssiPorts); delete [] old_control; old_control=new float[nDssiPorts]; for (unsigned long i=0;istate() != QProcess::NotRunning)) return false; #else if(_guiPid != -1) return false; #endif #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscInitGui\n"); #endif if(!url) { fprintf(stderr, "OscIF::oscInitGui no server url!\n"); return false; } if(guiPath.isEmpty()) { fprintf(stderr, "OscIF::oscInitGui guiPath is empty\n"); return false; } QString oscUrl; oscUrl = QString("%1%2/%3/%4").arg(QString( url)).arg(typ).arg(baseName).arg(label); #ifdef _USE_QPROCESS_FOR_GUI_ // fork + execlp cause the process to remain (zombie) after closing gui, requiring manual kill. // Using QProcess works OK. // No QProcess created yet? Do it now. Only once per SynthIF instance. Exists until parent destroyed. if(_oscGuiQProc == 0) _oscGuiQProc = new QProcess(); QString program(guiPath); QStringList arguments; arguments << oscUrl << filePath << name << (titlePrefix() + label); #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscInitGui starting QProcess\n"); #endif _oscGuiQProc->start(program, arguments); if(_oscGuiQProc->waitForStarted(10000)) // 10 secs. { #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscInitGui started QProcess\n"); fprintf(stderr, "guiPath:%s oscUrl:%s filePath:%s name:%s\n", guiPath.toLatin1().constData(), oscUrl.toLatin1().constData(), filePath.toLatin1().constData(), name.toLatin1().constData()); #endif } else { fprintf(stderr, "exec %s %s %s %s failed: %s\n", guiPath.toLatin1().constData(), oscUrl.toLatin1().constData(), filePath.toLatin1().constData(), name.toLatin1().constData(), strerror(errno)); return false; } #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscInitGui after QProcess\n"); #endif #else // NOT _USE_QPROCESS_FOR_GUI_ #ifdef OSC_DEBUG fprintf(stderr, "forking...\n"); #endif QString guiName = QFileInfo(guiPath).fileName(); // Note: fork + execlp cause the process to remain (zombie) after closing gui, requiring manual kill. Use QProcess instead. if((_guiPid = fork()) == 0) { execlp( guiPath.toLatin1().constData(), guiName.toLatin1().constData(), oscUrl.toLatin1().constData(), filePath.toLatin1().constData(), name.toLatin1().constData(), //"channel 1", (void*)0); label.toLatin1().constData(), (void*)0); // Should not return after execlp. If so it's an error. fprintf(stderr, "exec %s %s %s %s %s failed: %s\n", guiPath.toLatin1().constData(), guiName.toLatin1().constData(), oscUrl.toLatin1().constData(), filePath.toLatin1().constData(), name.toLatin1().constData(), strerror(errno)); //exit(1); return false; } #endif // _USE_QPROCESS_FOR_GUI_ return true; } //--------------------------------------------------------- // oscShowGui //--------------------------------------------------------- void OscIF::oscShowGui(bool v) { #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscShowGui(): v:%d visible:%d\n", v, oscGuiVisible()); #endif if (v == oscGuiVisible()) return; #ifdef _USE_QPROCESS_FOR_GUI_ if((_oscGuiQProc == 0) || (_oscGuiQProc->state() == QProcess::NotRunning)) #else if(_guiPid == -1) #endif { // We need an indicator that update was called - update must have been called to get new path etc... // If the process is not running this path is invalid, right? if(_uiOscPath) free(_uiOscPath); _uiOscPath = 0; #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscShowGui(): No QProcess or process not running. Starting gui...\n"); #endif if(!oscInitGui()) { fprintf(stderr, "OscIF::oscShowGui(): failed to initialize gui on oscInitGui()\n"); return; } } for (int i = 0; i < 10; ++i) { if (_uiOscPath) break; sleep(1); } if (_uiOscPath == 0) { fprintf(stderr, "OscIF::oscShowGui(): no _uiOscPath. Error: Timeout - synth gui did not start within 10 seconds.\n"); return; } char uiOscGuiPath[strlen(_uiOscPath)+6]; sprintf(uiOscGuiPath, "%s/%s", _uiOscPath, v ? "show" : "hide"); #ifdef OSC_DEBUG fprintf(stderr, "OscIF::oscShowGui(): Sending show/hide uiOscGuiPath:%s\n", uiOscGuiPath); #endif lo_send(_uiOscTarget, uiOscGuiPath, ""); _oscGuiVisible = v; } //--------------------------------------------------------- // oscGuiVisible //--------------------------------------------------------- bool OscIF::oscGuiVisible() const { return _oscGuiVisible; } #ifdef DSSI_SUPPORT //--------------------------------------------------------- // OscDssiIF:: // oscSetSynthIF //--------------------------------------------------------- void OscDssiIF::oscSetSynthIF(DssiSynthIF* s) { _oscSynthIF = s; } //--------------------------------------------------------- // oscUpdate //--------------------------------------------------------- int OscDssiIF::oscUpdate(lo_arg **argv) { // Make sure to call base method. OscIF::oscUpdate(argv); // Send sample rate. No, done in base class. DELETETHIS 7 //lo_send(_uiOscTarget, _uiOscSampleRatePath, "i", sampleRate); // Send project directory. No, done in DssiSynthIF. //lo_send(_uiOscTarget, _uiOscConfigurePath, "ss", // DSSI_PROJECT_DIRECTORY_KEY, museProject.toLatin1().constData()); // MusEGlobal::song->projectPath() if(_oscSynthIF) _oscSynthIF->oscUpdate(); // DELETETHIS 23 #if 0 /* Send current bank/program (-FIX- another race...) */ if (instance->pendingProgramChange < 0) { unsigned long bank = instance->currentBank; unsigned long program = instance->currentProgram; instance->uiNeedsProgramUpdate = 0; if (instance->uiTarget) { lo_send(instance->uiTarget, instance->ui_osc_program_path, "ii", bank, program); } } /* Send control ports */ for (i = 0; i < instance->plugin->controlIns; i++) { int in = i + instance->firstControlIn; int port = pluginControlInPortNumbers[in]; lo_send(instance->uiTarget, instance->ui_osc_control_path, "if", port, pluginControlIns[in]); /* Avoid overloading the GUI if there are lots and lots of ports */ if ((i+1) % 50 == 0) usleep(300000); } #endif return 0; } //--------------------------------------------------------- // oscConfigure //--------------------------------------------------------- int OscDssiIF::oscConfigure(lo_arg** argv) { if(_oscSynthIF) _oscSynthIF->oscConfigure((const char*)&argv[0]->s, (const char*)&argv[1]->s); return 0; } //--------------------------------------------------------- // oscMidi //--------------------------------------------------------- int OscDssiIF::oscMidi(lo_arg** argv) { if(_oscSynthIF) _oscSynthIF->oscMidi(argv[0]->m[1], argv[0]->m[2], argv[0]->m[3]); return 0; } //--------------------------------------------------------- // oscProgram //--------------------------------------------------------- int OscDssiIF::oscProgram(lo_arg** argv) { if(_oscSynthIF) { _oscSynthIF->oscProgram(argv[1]->i, argv[0]->i); old_prog=argv[1]->i; old_bank=argv[0]->i; } return 0; } //--------------------------------------------------------- // oscControl //--------------------------------------------------------- int OscDssiIF::oscControl(lo_arg** argv) { int port = argv[0]->i; if(port < 0) return 0; if(_oscSynthIF) { _oscSynthIF->oscControl(argv[0]->i, argv[1]->f); if (port<(int)maxDssiPort) old_control[control_port_mapper->at(port)]=argv[1]->f; } return 0; } //--------------------------------------------------------- // oscInitGui //--------------------------------------------------------- bool OscDssiIF::oscInitGui() { if(!_oscSynthIF) return false; return OscIF::oscInitGui("dssi_synth", _oscSynthIF->dssiSynth()->baseName(), _oscSynthIF->dssiSynth()->name(), _oscSynthIF->dssiSynthI()->name(), _oscSynthIF->dssiSynth()->fileName(), _oscSynthIF->dssi_ui_filename(), _oscSynthIF->dssiSynth()->getRpIdx()); } QString OscDssiIF::titlePrefix() const { return _oscSynthIF ? _oscSynthIF->titlePrefix() : QString(); } #endif // DSSI_SUPPORT //--------------------------------------------------------- // OscEffectIF:: // oscSetPluginI //--------------------------------------------------------- void OscEffectIF::oscSetPluginI(PluginI* s) { _oscPluginI = s; } //--------------------------------------------------------- // oscUpdate //--------------------------------------------------------- int OscEffectIF::oscUpdate(lo_arg** argv) { // Make sure to call base method. OscIF::oscUpdate(argv); if(_oscPluginI) _oscPluginI->oscUpdate(); return 0; } //--------------------------------------------------------- // oscConfigure //--------------------------------------------------------- int OscEffectIF::oscConfigure(lo_arg** argv) { if(_oscPluginI) _oscPluginI->oscConfigure((const char*)&argv[0]->s, (const char*)&argv[1]->s); return 0; } //--------------------------------------------------------- // oscControl //--------------------------------------------------------- int OscEffectIF::oscControl(lo_arg** argv) { int port = argv[0]->i; if(port < 0) return 0; if(_oscPluginI) { _oscPluginI->oscControl(argv[0]->i, argv[1]->f); if (port<(int)maxDssiPort) old_control[control_port_mapper->at(port)]=argv[1]->f; } return 0; } //--------------------------------------------------------- // oscInitGui //--------------------------------------------------------- bool OscEffectIF::oscInitGui() { if(!_oscPluginI) return false; return OscIF::oscInitGui("ladspa_efx", _oscPluginI->plugin()->lib(false), _oscPluginI->plugin()->label(), _oscPluginI->label(), _oscPluginI->plugin()->fileName(), _oscPluginI->dssi_ui_filename(), _oscPluginI->plugin()->getRpIdx()); } QString OscEffectIF::titlePrefix() const { return _oscPluginI ? _oscPluginI->titlePrefix() : QString(); } #else //OSC_SUPPORT void initOSC() {} void exitOSC() {} #endif } // namespace MusECore muse-4.0.0/src/muse/osc.h000066400000000000000000000100601404105311000151430ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // $Id: osc.h,v 1.0.0.0 2010/04/22 10:05:00 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __OSC_H__ #define __OSC_H__ #include "config.h" #ifdef OSC_SUPPORT #include class QProcess; class QString; #endif // OSC_SUPPORT namespace MusECore { #ifdef OSC_SUPPORT #ifdef DSSI_SUPPORT class DssiSynthIF; #endif class PluginI; class OscIF; class OscIF { protected: pid_t _guiPid; QProcess* _oscGuiQProc; void* _uiOscTarget; char* _uiOscPath; char* _uiOscSampleRatePath; char* _uiOscConfigurePath; char* _uiOscProgramPath; char* _uiOscControlPath; char* _uiOscShowPath; bool _oscGuiVisible; unsigned long old_prog; unsigned long old_bank; float* old_control; unsigned long maxDssiPort; const std::vector* control_port_mapper; virtual bool oscInitGui(const QString& typ, const QString& baseName, const QString& name, const QString& label, const QString& filePath, const QString& guiPath, const std::vector* control_port_mapper_); public: OscIF(); virtual ~OscIF(); virtual int oscUpdate(lo_arg**); virtual int oscProgram(lo_arg**) { return 0; } virtual int oscControl(lo_arg**) { return 0; } virtual int oscExiting(lo_arg**); virtual int oscMidi(lo_arg**) { return 0; } virtual int oscConfigure(lo_arg**) { return 0; } virtual void oscSendProgram(unsigned long prog, unsigned long bank, bool force=false); virtual void oscSendControl(unsigned long dssiPort, float val, bool force=false); virtual void oscSendConfigure(const char *key, const char *val); virtual bool oscInitGui() { return false; } virtual void oscShowGui(bool); virtual bool oscGuiVisible() const; virtual QString titlePrefix() const { return QString(); } }; class OscEffectIF : public OscIF { protected: PluginI* _oscPluginI; public: OscEffectIF() {} void oscSetPluginI(PluginI*); // this MUST be called with NULL-argument from PluginI's destructor! virtual int oscUpdate(lo_arg**); virtual int oscControl(lo_arg**); virtual int oscConfigure(lo_arg**); virtual bool oscInitGui(); virtual QString titlePrefix() const; }; #ifdef DSSI_SUPPORT class OscDssiIF : public OscIF { protected: DssiSynthIF* _oscSynthIF; public: OscDssiIF() {} void oscSetSynthIF(DssiSynthIF*); // this MUST be called with NULL-argument from DssiSynthIF's destructor! virtual int oscUpdate(lo_arg**); virtual int oscProgram(lo_arg**); virtual int oscControl(lo_arg**); virtual int oscMidi(lo_arg**); virtual int oscConfigure(lo_arg**); virtual bool oscInitGui(); virtual QString titlePrefix() const; }; #endif // DSSI_SUPPORT #endif // OSC_SUPPORT extern void initOSC(); } // namespace MusECore #endif muse-4.0.0/src/muse/part.cpp000066400000000000000000000631271404105311000156740ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: part.cpp,v 1.12.2.17 2009/06/25 05:13:02 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "muse_math.h" #include "part.h" #include "song.h" #include "globals.h" #include "audio.h" #include "wave.h" #include "midiport.h" #include "drummap.h" #include "midictrl.h" #include "gconfig.h" #include "config.h" // Forwards from header: #include "track.h" #include "xml.h" namespace MusECore { int Part::snGen=0; //--------------------------------------------------------- // MidiCtrlViewState::write //--------------------------------------------------------- void MidiCtrlViewState::write(int level, Xml& xml) const { xml.nput(level, ""); } //--------------------------------------------------------- // MidiPartViewState::read //--------------------------------------------------------- void MidiCtrlViewState::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::Attribut: if (tag == "num") _num = xml.s2().toInt(); else if (tag == "perNoteVel") _perNoteVel = xml.s2().toInt(); break; case Xml::TagEnd: if (xml.s1() == "ctrlViewState") return; default: break; } } } //--------------------------------------------------------- // MidiPartViewState::write //--------------------------------------------------------- void MidiPartViewState::write(int level, Xml& xml) const { // Don't bother if it's an invalid state. if(!isValid()) return; xml.tag(level++, "viewState xscroll=\"%d\" yscroll=\"%d\" xscale=\"%d\" yscale=\"%d\"", xscroll(), yscroll(), xscale(), yscale()); if(!_controllers.empty()) { for (ciMidiCtrlViewState i = _controllers.cbegin(); i != _controllers.cend(); ++i) { (*i).write(level, xml); } } xml.tag(level, "/viewState"); } //--------------------------------------------------------- // MidiPartViewState::read //--------------------------------------------------------- void MidiPartViewState::read(Xml& xml) { // Make sure to clear the controllers list first. clearControllers(); for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case MusECore::Xml::TagStart: if (tag == "ctrlViewState") { MidiCtrlViewState mcvs; mcvs.read(xml); addController(mcvs); } else xml.unknown("MidiPartViewState"); break; case Xml::Attribut: if (tag == "xscroll") setXScroll(xml.s2().toInt()); else if (tag == "yscroll") setYScroll(xml.s2().toInt()); else if (tag == "xscale") setXScale(xml.s2().toInt()); else if (tag == "yscale") setYScale(xml.s2().toInt()); break; case Xml::TagEnd: if (xml.s1() == "viewState") return; default: break; } } } void Part::unchainClone() { chainCheckErr(this); // FIXME proper assert! if (_backupClone) printf("THIS SHOULD NEVER HAPPEN: Part::unchainClone() called, but _backupClone was non-NULL\n"); _backupClone=_prevClone; // Unchain the part. _prevClone->_nextClone = _nextClone; _nextClone->_prevClone = _prevClone; // Isolate the part. _prevClone = this; _nextClone = this; _clonemaster_sn = this->_sn; } void Part::chainClone(Part* p) { // FIXME assertion assert(p); if (! (_prevClone==this && _nextClone==this)) // the part is still part of a clone chain! { printf("ERROR: THIS SHOULD NEVER HAPPEN: Part::chainClone() called, but part is already chained! I'll unchain for now, but better fix that!\n"); this->unchainClone(); } // Make our links to the chain this->_prevClone = p; this->_nextClone = p->_nextClone; // Make the chain's links to us this->_nextClone->_prevClone = this; p->_nextClone = this; // we only chain clones. we must trust in the GUI thread that the eventlist is consistent. this->_clonemaster_sn = p->_sn; } void Part::rechainClone() { if(_backupClone) { this->chainClone(_backupClone); _backupClone = NULL; } } bool Part::isCloneOf(const Part* other) const { return this->_clonemaster_sn == other->_clonemaster_sn; } int Part::nClones() const { int n=1; for(const Part* it = this->_nextClone; it!=this; it=it->_nextClone) n++; return n; } // FIXME FINDMICHJETZT TODO: weg damit! //--------------------------------------------------------- // unchainTrackParts //--------------------------------------------------------- void unchainTrackParts(Track* t) { PartList* pl = t->parts(); for(iPart ip = pl->begin(); ip != pl->end(); ++ip) ip->second->unchainClone(); } //--------------------------------------------------------- // chainTrackParts //--------------------------------------------------------- void chainTrackParts(Track* t) { PartList* pl = t->parts(); for(riPart ip = pl->rbegin(); ip != pl->rend(); ++ip) // walk through in opposite direction than we unchained them. ip->second->rechainClone(); } //--------------------------------------------------------- // chainCheckErr //--------------------------------------------------------- void chainCheckErr(Part* p) { // At all times these must be false... if(p->nextClone()->prevClone() != p) printf("chainCheckErr: Next clone:%s %p prev clone:%s %p != %s %p\n", p->nextClone()->name().toLatin1().constData(), p->nextClone(), p->nextClone()->prevClone()->name().toLatin1().constData(), p->nextClone()->prevClone(), p->name().toLatin1().constData(), p); if(p->prevClone()->nextClone() != p) printf("chainCheckErr: Prev clone:%s %p next clone:%s %p != %s %p\n", p->prevClone()->name().toLatin1().constData(), p->prevClone(), p->prevClone()->nextClone()->name().toLatin1().constData(), p->prevClone()->nextClone(), p->name().toLatin1().constData(), p); } //--------------------------------------------------------- // addPortCtrlEvents //--------------------------------------------------------- void addPortCtrlEvents(Part* part, bool doClones) { // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. Part* p = part; while(1) { Track* t = p->track(); if(t && t->isMidiTrack()) { MidiTrack* mt = (MidiTrack*)t; unsigned int len = p->lenTick(); for(ciEvent ie = p->events().begin(); ie != p->events().end(); ++ie) { const Event& ev = ie->second; // Do not add events which are past the end of the part. #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)ev.tick() >= (int)len) #else if(ev.tick() >= len) #endif break; #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)ev.tick() < 0) continue; #endif if(ev.type() == Controller) { unsigned int tck = ev.tick() + p->tick(); int cntrl = ev.dataA(); int val = ev.dataB(); MidiPort* mp; int ch; mt->mappedPortChanCtrl(&cntrl, nullptr, &mp, &ch); mp->setControllerVal(ch, tck, cntrl, val, p); } } } if(!doClones) break; // Get the next clone in the chain ring. p = p->nextClone(); // Same as original part? Finished. if(p == part) break; } } //--------------------------------------------------------- // removePortCtrlEvents //--------------------------------------------------------- void removePortCtrlEvents(Part* part, bool doClones) { // Traverse and process the clone chain ring until we arrive at the same part again. // The loop is a safety net. Part* p = part; while(1) { Track* t = p->track(); if(t && t->isMidiTrack()) { MidiTrack* mt = (MidiTrack*)t; // MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()]; // int ch = mt->outChannel(); for(ciEvent ie = p->events().begin(); ie != p->events().end(); ++ie) { const Event& ev = ie->second; if(ev.type() == Controller) { unsigned int tck = ev.tick() + p->tick(); int cntrl = ev.dataA(); int val = ev.dataB(); // Is it a drum controller event, according to the track port's instrument? MidiPort* mp; int ch; mt->mappedPortChanCtrl(&cntrl, nullptr, &mp, &ch); mp->deleteController(ch, tck, cntrl, val, p); } } } if(!doClones) break; // Get the next clone in the chain ring. p = p->nextClone(); // Same as original part? Finished. if(p == part) break; } } //--------------------------------------------------------- // addEvent //--------------------------------------------------------- iEvent Part::addEvent(Event& p) { return _events.add(p); } //--------------------------------------------------------- // index //--------------------------------------------------------- int PartList::index(const Part* part) const { int index = 0; for (ciPart i = begin(); i != end(); ++i, ++index) if (i->second == part) { return index; } if(MusEGlobal::debugMsg) printf("PartList::index(): not found!\n"); return -1; // don't change that value. at least MidiEditor::addNewParts relies on this } //--------------------------------------------------------- // find //--------------------------------------------------------- Part* PartList::find(int idx) { int index = 0; for (iPart i = begin(); i != end(); ++i, ++index) if (index == idx) return i->second; return nullptr; } //--------------------------------------------------------- // Part //--------------------------------------------------------- Part::Part(Track* t) { _hiddenEvents = NoEventsHidden; _prevClone = this; _nextClone = this; _backupClone = nullptr; _sn = newSn(); _clonemaster_sn = _sn; _track = t; _selected = false; _mute = false; _colorIndex = 0; } Part::~Part() { if (_prevClone!=this || _nextClone!=this) { if (MusEGlobal::debugMsg) { fprintf(stderr, "Part isn't unchained in ~Part()! Unchaining now...\n"); } unchainClone(); } } WavePart* WavePart::duplicateEmpty() const { WavePart* part = new WavePart((WaveTrack*)this->_track); part->setName(name()); part->setColorIndex(colorIndex()); *(PosLen*)part = *(PosLen*)this; part->setMute(mute()); return part; } WavePart* WavePart::duplicate() const { return (WavePart*)Part::duplicate(); } WavePart* WavePart::createNewClone() const { return (WavePart*)Part::createNewClone(); } MidiPart* MidiPart::duplicateEmpty() const { MidiPart* part = new MidiPart((MidiTrack*)this->_track); part->setName(name()); part->setColorIndex(colorIndex()); *(PosLen*)part = *(PosLen*)this; part->setMute(mute()); return part; } MidiPart* MidiPart::duplicate() const { return (MidiPart*)Part::duplicate(); } MidiPart* MidiPart::createNewClone() const { return (MidiPart*)Part::createNewClone(); } Part* Part::createNewClone() const { Part* clone = duplicateEmpty(); for (MusECore::ciEvent i = _events.begin(); i != _events.end(); ++i) { Event nev = i->second.clone(); // Create a non-shared clone of the event, having the same id. clone->addEvent(nev); } clone->_backupClone=const_cast(this); return clone; } Part* Part::duplicate() const { Part* dup = duplicateEmpty(); // copy the eventlist; duplicate each Event(Ptr!). for (MusECore::ciEvent i = _events.begin(); i != _events.end(); ++i) { Event nev = i->second.duplicate(); // Create a duplicate of the event, excluding the _id. dup->addEvent(nev); } return dup; } bool Part::selectEvents(bool select, unsigned long /*t0*/, unsigned long /*t1*/) { bool ret = false; EventList& el = nonconst_events(); for(iEvent ie = el.begin(); ie != el.end(); ++ie) { Event& e = ie->second; // if(e.type() ???) // For t0 and t1 if(e.selected() != select) ret = true; e.setSelected(select); } return ret; } //--------------------------------------------------------- // WavePart //--------------------------------------------------------- WavePart::WavePart(WaveTrack* t) : Part(t) { setType(FRAMES); } //--------------------------------------------------------- // findPart //--------------------------------------------------------- iPart PartList::findPart(unsigned tick) { iPart i; for (i = begin(); i != end(); ++i) if (i->second->tick() == tick) break; return i; } //--------------------------------------------------------- // add //--------------------------------------------------------- iPart PartList::add(Part* part) { // Added by T356. A part list containing wave parts should be sorted by // frames. WaveTrack::fetchData() relies on the sorting order, and // there was a bug that waveparts were sometimes muted because of // incorrect sorting order (by ticks). // Also, when the tempo map is changed, every wavepart would have to be // re-added to the part list so that the proper sorting order (by ticks) // could be achieved. // Note that in a med file, the tempo list is loaded AFTER all the tracks. // There was a bug that all the wave parts' tick values were not correct, // since they were computed BEFORE the tempo map was loaded. if(part->type() == Pos::FRAMES) return insert(PartListInsertPair_t(part->frame(), part)); else return insert(PartListInsertPair_t(part->tick(), part)); } //--------------------------------------------------------- // remove //--------------------------------------------------------- void PartList::remove(Part* part) { iPart i; for (i = begin(); i != end(); ++i) { if (i->second == part) { erase(i); break; } } if (i == end()) printf("THIS SHOULD NEVER HAPPEN: could not find the part in PartList::remove()!\n"); } //--------------------------------------------------------- // addPart //--------------------------------------------------------- void Song::addPart(Part* part) { // adjust song len: unsigned int epos = part->tick() + part->lenTick(); #ifdef ALLOW_LEFT_HIDDEN_EVENTS if ((int)epos > (int)len()) #else if (epos > len()) #endif _len = epos; part->track()->addPart(part); // Indicate do not do clones. addPortCtrlEvents(part, false); } //--------------------------------------------------------- // removePart //--------------------------------------------------------- void Song::removePart(Part* part) { // Indicate do not do clones. removePortCtrlEvents(part, false); Track* track = part->track(); track->parts()->remove(part); } //--------------------------------------------------------- // splitPart // split part "part" at "tick" position // create two new parts p1 and p2 //--------------------------------------------------------- void Part::splitPart(unsigned int tickpos, Part*& p1, Part*& p2) const { unsigned int l1 = 0; // len of first new part (ticks or samples) unsigned int l2 = 0; // len of second new part unsigned int samplepos = MusEGlobal::tempomap.tick2frame(tickpos); switch (track()->type()) { case Track::WAVE: if(samplepos <= frame() || lenFrame() <= l1) return; l1 = samplepos - frame(); l2 = lenFrame() - l1; break; case Track::MIDI: case Track::DRUM: if(tickpos <= tick() || lenTick() <= l1) return; l1 = tickpos - tick(); l2 = lenTick() - l1; break; default: return; } p1 = this->duplicateEmpty(); // new left part p2 = this->duplicateEmpty(); // new right part switch (track()->type()) { case Track::WAVE: p1->setLenFrame(l1); p2->setFrame(samplepos); p2->setLenFrame(l2); break; case Track::MIDI: case Track::DRUM: p1->setLenTick(l1); p2->setTick(tickpos); p2->setLenTick(l2); break; default: break; } if (track()->type() == Track::WAVE) { unsigned int ps = this->frame(); unsigned int d1p1 = p1->frame(); unsigned int d2p1 = p1->endFrame(); unsigned int d1p2 = p2->frame(); unsigned int d2p2 = p2->endFrame(); for (ciEvent ie = _events.begin(); ie != _events.end(); ++ie) { const Event& event = ie->second; unsigned int s1 = event.frame() + ps; unsigned int s2 = event.endFrame() + ps; if ((s2 > d1p1) && (s1 < d2p1)) { Event si = event.mid(d1p1 - ps, d2p1); p1->addEvent(si); } if ((s2 > d1p2) && (s1 < d2p2)) { Event si = event.mid(d1p2 - ps, d2p2); p2->addEvent(si); } } } else { for (ciEvent ie = _events.begin(); ie != _events.end(); ++ie) { Event event = ie->second.clone(); unsigned int t = event.tick(); if (t >= l1) { event.move(-l1); p2->addEvent(event); } else p1->addEvent(event); } } } //--------------------------------------------------------- // changePart //--------------------------------------------------------- void Song::changePart(Part* oPart, Part* nPart) { nPart->setSn(oPart->sn()); Track* oTrack = oPart->track(); Track* nTrack = nPart->track(); oTrack->parts()->remove(oPart); nTrack->parts()->add(nPart); // Added by T356. // adjust song len: unsigned int epos = nPart->tick() + nPart->lenTick(); if (epos > len()) _len = epos; } //--------------------------------------------------------- // setViewState //--------------------------------------------------------- void Part::setViewState(const MidiPartViewState& vs) { _viewState = vs; } //--------------------------------------------------------- // dump //--------------------------------------------------------- void Part::dump(int n) const { for (int i = 0; i < n; ++i) putchar(' '); printf("Part: <%s> ", _name.toLatin1().constData()); for (int i = 0; i < n; ++i) putchar(' '); PosLen::dump(); } void WavePart::dump(int n) const { Part::dump(n); for (int i = 0; i < n; ++i) putchar(' '); printf("WavePart\n"); } void MidiPart::dump(int n) const { Part::dump(n); for (int i = 0; i < n; ++i) putchar(' '); printf("MidiPart\n"); } //--------------------------------------------------------- // hasHiddenEvents // Returns combination of HiddenEventsType enum. //--------------------------------------------------------- int MidiPart::hasHiddenEvents() const { // TODO An idea for left-border hidden events with unsigned integer positions. // It actually worked, as far as the left hidden events arrow, but really it can't work without much bigger switchovers to integer math. // I guess we'll try it like this. #ifdef ALLOW_LEFT_HIDDEN_EVENTS unsigned int len = lenTick(); _hiddenEvents = NoEventsHidden; // Cache the result for later. const int c_comp = LeftEventsHidden | RightEventsHidden; for(ciEvent ev=_events.begin(); ev!=_events.end(); ++ev) { // Is the time value less than zero? Note the conversion to signed here! if(signed(ev->second.tick()) < 0) _hiddenEvents |= LeftEventsHidden; // Cache the result for later. if(signed(ev->second.endTick()) > signed(len)) _hiddenEvents |= RightEventsHidden; // Cache the result for later. if(_hiddenEvents == c_comp) break; } return _hiddenEvents; #else unsigned int len = lenTick(); // TODO: For now, we don't support events before the left border, only events past the right border. for(ciEvent ev=_events.begin(); ev!=_events.end(); ev++) { if(ev->second.endTick() > len) { _hiddenEvents = RightEventsHidden; // Cache the result for later. return _hiddenEvents; } } _hiddenEvents = NoEventsHidden; // Cache the result for later. return _hiddenEvents; #endif } //--------------------------------------------------------- // hasHiddenEvents // Returns combination of HiddenEventsType enum. //--------------------------------------------------------- int WavePart::hasHiddenEvents() const { // An idea for left-border hidden events with unsigned integer positions. // It actually worked, as far as the left hidden events arrow, but really it can't work without much bigger switchovers to integer math. // I guess we'll try it like this. #ifdef ALLOW_LEFT_HIDDEN_EVENTS unsigned int len = lenFrame(); _hiddenEvents = NoEventsHidden; // Cache the result for later. const int c_comp = LeftEventsHidden | RightEventsHidden; for(ciEvent ev=_events.begin(); ev!=_events.end(); ++ev) { // Is the time value less than zero? Note the conversion to signed here! if(signed(ev->second.frame()) < 0) _hiddenEvents |= LeftEventsHidden; // Cache the result for later. if(signed(ev->second.endFrame()) > signed(len)) _hiddenEvents |= RightEventsHidden; // Cache the result for later. if(_hiddenEvents == c_comp) break; } return _hiddenEvents; #else unsigned int len = lenFrame(); // TODO: For now, we don't support events before the left border, only events past the right border. for(ciEvent ev=_events.begin(); ev!=_events.end(); ev++) { if(ev->second.endFrame() > len) { _hiddenEvents = RightEventsHidden; // Cache the result for later. return _hiddenEvents; } } _hiddenEvents = NoEventsHidden; // Cache the result for later. return _hiddenEvents; #endif } bool WavePart::openAllEvents() { bool opened = false; const EventList& el = events(); for(ciEvent ie = el.begin(); ie != el.end(); ++ie) { const Event& e = ie->second; if(e.empty()) continue; SndFileR f = e.sndFile(); if(!f.isNull() && !f.isOpen()) { opened = true; f.openRead(); } } return opened; } bool WavePart::closeAllEvents() { bool closed = false; const EventList& el = events(); for(ciEvent ie = el.begin(); ie != el.end(); ++ie) { const Event& e = ie->second; if(e.empty()) continue; SndFileR f = e.sndFile(); if(!f.isNull() && f.isOpen()) { closed = true; f.close(); } } return closed; } //--------------------------------------------------------- // ClonePart //--------------------------------------------------------- ClonePart::ClonePart(const Part* p, int i) { cp = p; id = i; is_deleted = false; _uuid = QUuid::createUuid(); } } // namespace MusECore muse-4.0.0/src/muse/part.h000066400000000000000000000256641404105311000153450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: part.h,v 1.5.2.4 2009/05/24 21:43:44 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // Additions, modifications (C) Copyright 2011 Tim E. Real (terminator356 on users DOT sourceforge DOT net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PART_H__ #define __PART_H__ #include #include #include #include #include #include "pos.h" #include "event.h" namespace MusECore { // Forward declarations: class Track; class MidiTrack; class WaveTrack; class Xml; //--------------------------------------------------------- // MidiCtrlViewState // Stores the initial controller view state //--------------------------------------------------------- struct MidiCtrlViewState { // Midi controller number. int _num; // Per note velocity display. bool _perNoteVel; MidiCtrlViewState() : _num(0), _perNoteVel(false) { } MidiCtrlViewState(int num, bool perNoteVel = false) : _num(num), _perNoteVel(perNoteVel) { } void read(Xml&); void write(int, Xml&) const; }; typedef std::vector MidiCtrlViewStateList; typedef MidiCtrlViewStateList::iterator iMidiCtrlViewState; typedef MidiCtrlViewStateList::const_iterator ciMidiCtrlViewState; //--------------------------------------------------------- // MidiPartViewState // Stores the initial view state //--------------------------------------------------------- class MidiPartViewState { private: int _xscroll; int _yscroll; int _xscale; int _yscale; MidiCtrlViewStateList _controllers; public: MidiPartViewState() : _xscroll(INT_MAX), _yscroll(INT_MAX), _xscale(INT_MAX), _yscale(INT_MAX) { } MidiPartViewState(int xscroll, int yscroll, int xscale, int yscale) : _xscroll(xscroll), _yscroll(yscroll), _xscale(xscale), _yscale(yscale) { } MidiPartViewState(const MidiPartViewState& vs) : _xscroll(vs._xscroll), _yscroll(vs._yscroll), _xscale(vs._xscale), _yscale(vs._yscale), _controllers(vs._controllers) { } MidiPartViewState& operator=(const MidiPartViewState& vs) { _xscroll = vs._xscroll; _yscroll = vs._yscroll; _xscale = vs._xscale; _yscale = vs._yscale; _controllers = vs._controllers; return *this; } bool isValid() const { return _xscroll != INT_MAX && _yscroll != INT_MAX && _xscale != INT_MAX && _yscale != INT_MAX; } int xscroll() const { return _xscroll; } int yscroll() const { return _yscroll; } int xscale() const { return _xscale; } int yscale() const { return _yscale; } void setXScroll(int x) { _xscroll = x; } void setYScroll(int y) { _yscroll = y; } void setXScale(int xs) { _xscale = xs; } void setYScale(int ys) { _yscale = ys; } const MidiCtrlViewStateList& controllers() { return _controllers; } void addController(const MidiCtrlViewState& viewState) { _controllers.push_back(viewState); } void clearControllers() { _controllers.clear(); } void read(Xml&); void write(int, Xml&) const; }; //--------------------------------------------------------- // Part //--------------------------------------------------------- class Part : public PosLen { public: enum HiddenEventsType { NoEventsHidden = 0x00, LeftEventsHidden = 0x01, RightEventsHidden = 0x02}; enum PartType { MidiPartType = 0x01, WavePartType = 0x02 }; static Part* readFromXml(Xml&, Track*, bool doClone = false, bool toTrack = true); private: static int snGen; int _sn; int _clonemaster_sn; // the serial number of some clone in the chain. every member of the chain has the same value here. QString _name; bool _selected; bool _mute; int _colorIndex; protected: Track* _track; EventList _events; Part* _prevClone; Part* _nextClone; Part* _backupClone; // when a part gets removed, it's still there; and for undo-ing the remove, it must know about where it was clone-chained to. mutable int _hiddenEvents; // Combination of HiddenEventsType. MidiPartViewState _viewState; public: Part(Track*); virtual ~Part(); virtual PartType partType() const = 0; virtual Part* duplicate() const; virtual Part* duplicateEmpty() const = 0; virtual Part* createNewClone() const; // this does NOT chain clones yet. Chain is updated only when the part is really added! virtual void splitPart(unsigned int tickpos, Part*& p1, Part*& p2) const; void setSn(int n) { _sn = n; } int clonemaster_sn() const { return _clonemaster_sn; } int sn() const { return _sn; } int newSn() { return snGen++; } const QString& name() const { return _name; } void setName(const QString& s) { _name = s; } bool selected() const { return _selected; } void setSelected(bool f) { _selected = f; } // Select or unselect a range of events. If t0 == t1, ALL events will be affected. // The t0 and t1 can be ticks or frames depending on the type of events. Unused for now. // Returns true if anything changed. bool selectEvents(bool select, unsigned long t0 = 0, unsigned long t1 = 0); bool mute() const { return _mute; } void setMute(bool b) { _mute = b; } Track* track() const { return _track; } void setTrack(Track*t) { _track = t; } const EventList& events() const { return _events; } EventList& nonconst_events() { return _events; } int colorIndex() const { return _colorIndex; } void setColorIndex(int idx) { _colorIndex = idx; } bool isCloneOf(const Part*) const; bool hasClones() const { return _prevClone!=this || _nextClone!=this; } int nClones() const; Part* prevClone() const { return _prevClone; } // FINDMICHJETZT make it const Part*! Part* nextClone() const { return _nextClone; } Part* backupClone() const { return _backupClone; } void unchainClone(); void chainClone(Part* p); // *this is made a sibling of p! p is not touched (except for its clone-chain), whereas this->events will get altered void rechainClone(); // re-chains the part to the same clone chain it was unchained before // Returns combination of HiddenEventsType enum. virtual int hasHiddenEvents() const { return _hiddenEvents; } iEvent addEvent(Event& p); // this does not care about clones! If the part is a clone, be sure to execute this on all clones (with duplicated Events, that is!) // Returns true if any event was opened. Does not operate on the part's clones, if any. virtual bool openAllEvents() { return false; }; // Returns true if any event was closed. Does not operate on the part's clones, if any. virtual bool closeAllEvents() { return false; }; virtual void write(int, Xml&, bool isCopy = false, bool forceWavePaths = false) const; virtual void dump(int n = 0) const; const MidiPartViewState& viewState() const { return _viewState; } MidiPartViewState& viewState() { return _viewState; } virtual void setViewState(const MidiPartViewState& vs); }; //--------------------------------------------------------- // MidiPart //--------------------------------------------------------- class MidiPart : public Part { public: MidiPart(MidiTrack* t) : Part((Track*)t) {} virtual ~MidiPart() {} virtual PartType partType() const { return MidiPartType; } virtual MidiPart* duplicate() const; virtual MidiPart* duplicateEmpty() const; virtual MidiPart* createNewClone() const; MidiTrack* track() const { return (MidiTrack*)Part::track(); } // Returns combination of HiddenEventsType enum. int hasHiddenEvents() const; virtual void dump(int n = 0) const; }; //--------------------------------------------------------- // WavePart //--------------------------------------------------------- class WavePart : public Part { // p3.3.31 //AudioConvertMap _converters; public: WavePart(WaveTrack* t); virtual ~WavePart() {} virtual PartType partType() const { return WavePartType; } virtual WavePart* duplicate() const; virtual WavePart* duplicateEmpty() const; virtual WavePart* createNewClone() const; WaveTrack* track() const { return (WaveTrack*)Part::track(); } // Returns combination of HiddenEventsType enum. int hasHiddenEvents() const; // Returns true if any event was opened. Does not operate on the part's clones, if any. bool openAllEvents(); // Returns true if any event was closed. Does not operate on the part's clones, if any. bool closeAllEvents(); virtual void dump(int n = 0) const; }; struct ClonePart { const Part* cp; int id; QUuid _uuid; bool is_deleted; ClonePart(const Part*, int i = -1); }; typedef std::list CloneList; typedef CloneList::iterator iClone; //--------------------------------------------------------- // PartList //--------------------------------------------------------- typedef std::pair PartListInsertPair_t; typedef std::multimap > PartList_t; class PartList : public PartList_t { public: iterator findPart(unsigned tick); iterator add(Part*); void remove(Part* part); int index(const Part*) const; Part* find(int idx); void clearDelete() { for (iterator i = begin(); i != end(); ++i) delete i->second; clear(); } }; typedef PartList_t::iterator iPart; typedef PartList_t::reverse_iterator riPart; typedef PartList_t::const_iterator ciPart; extern void chainCheckErr(Part* p); extern void unchainTrackParts(Track* t); extern void chainTrackParts(Track* t); extern void addPortCtrlEvents(Part* part, bool doClones); extern void removePortCtrlEvents(Part* part, bool doClones); } // namespace MusECore namespace MusEGlobal { extern MusECore::CloneList cloneList; } #endif muse-4.0.0/src/muse/plugin.cpp000066400000000000000000004776241404105311000162370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: plugin.cpp,v 1.21.2.23 2009/12/15 22:07:12 spamatica Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "muse_math.h" #include #include #include #include #include #include #include #include #include #include #include "globals.h" #include "gconfig.h" #include "filedialog.h" #include "slider.h" #include "midictrl_consts.h" #include "plugin.h" #include "controlfifo.h" #include "icons.h" #include "song.h" #include "doublelabel.h" #include "fastlog.h" #include "checkbox.h" #include "comboboxpi.h" #include "meter.h" #include "utils.h" #include "pluglist.h" #include "gui.h" #include "pluginsettings.h" #ifdef LV2_SUPPORT #include "lv2host.h" #endif #ifdef VST_NATIVE_SUPPORT #include "vst_native.h" #endif #include "audio.h" #include "al/dsp.h" #include "muse_math.h" // Forwards from header: #include #include #include #include #include #include "xml.h" #include "plugin_list.h" #include "track.h" #ifdef _WIN32 #define S_ISLNK(X) 0 #endif // Turn on debugging messages. //#define PLUGIN_DEBUGIN // Turn on constant stream of debugging messages. //#define PLUGIN_DEBUGIN_PROCESS namespace MusEGlobal { MusECore::PluginList plugins; MusECore::PluginGroups plugin_groups; QList plugin_group_names; } namespace MusECore { //--------------------------------------------------------- // ladspa2MidiControlValues //--------------------------------------------------------- bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long port, int ctlnum, int* min, int* max, int* def) { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; float fmin, fmax, fdef; int imin, imax; float frng; bool hasdef = ladspaDefaultValue(plugin, port, &fdef); MidiController::ControllerType t = midiControllerType(ctlnum); #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: ctlnum:%d ladspa port:%lu has default?:%d default:%f\n", ctlnum, port, hasdef, fdef); #endif if(desc & LADSPA_HINT_TOGGLED) { #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: has LADSPA_HINT_TOGGLED\n"); #endif *min = 0; *max = 1; *def = (int)lrintf(fdef); return hasdef; } float m = 1.0; if(desc & LADSPA_HINT_SAMPLE_RATE) { #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: has LADSPA_HINT_SAMPLE_RATE\n"); #endif m = float(MusEGlobal::sampleRate); } if(desc & LADSPA_HINT_BOUNDED_BELOW) { #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: has LADSPA_HINT_BOUNDED_BELOW\n"); #endif fmin = range.LowerBound * m; } else fmin = 0.0; if(desc & LADSPA_HINT_BOUNDED_ABOVE) { #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: has LADSPA_HINT_BOUNDED_ABOVE\n"); #endif fmax = range.UpperBound * m; } else fmax = 1.0; frng = fmax - fmin; imin = lrintf(fmin); imax = lrintf(fmax); int ctlmn = 0; int ctlmx = 127; #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: port min:%f max:%f \n", fmin, fmax); #endif bool isneg = (imin < 0); int bias = 0; switch(t) { case MidiController::RPN: case MidiController::NRPN: case MidiController::Controller7: if(isneg) { ctlmn = -64; ctlmx = 63; bias = -64; } else { ctlmn = 0; ctlmx = 127; } break; case MidiController::Controller14: case MidiController::RPN14: case MidiController::NRPN14: if(isneg) { ctlmn = -8192; ctlmx = 8191; bias = -8192; } else { ctlmn = 0; ctlmx = 16383; } break; case MidiController::Program: ctlmn = 0; ctlmx = 0x3fff; // FIXME: Really should not happen or be allowed. What to do here... break; case MidiController::Pitch: ctlmn = -8192; ctlmx = 8191; break; case MidiController::Velo: // cannot happen default: break; } float fctlrng = float(ctlmx - ctlmn); // Is it an integer control? if(desc & LADSPA_HINT_INTEGER) { #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: has LADSPA_HINT_INTEGER\n"); #endif // just clip the limits instead of scaling the whole range. ie fit the range into clipped space. if(imin < ctlmn) imin = ctlmn; if(imax > ctlmx) imax = ctlmx; *min = imin; *max = imax; *def = (int)lrintf(fdef); return hasdef; } // It's a floating point control, just use wide open maximum range. *min = ctlmn; *max = ctlmx; float normdef = fdef / frng; fdef = normdef * fctlrng; // FIXME: TODO: Incorrect... Fix this somewhat more trivial stuff later.... *def = (int)lrintf(fdef) + bias; #ifdef PLUGIN_DEBUGIN printf("ladspa2MidiControlValues: setting default:%d\n", *def); #endif return hasdef; } //--------------------------------------------------------- // midi2LadspaValue //--------------------------------------------------------- float midi2LadspaValue(const LADSPA_Descriptor* plugin, unsigned long port, int ctlnum, int val) { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; float fmin, fmax; int imin; float frng; MidiController::ControllerType t = midiControllerType(ctlnum); #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: ctlnum:%d ladspa port:%lu val:%d\n", ctlnum, port, val); #endif float m = 1.0; if(desc & LADSPA_HINT_SAMPLE_RATE) { #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: has LADSPA_HINT_SAMPLE_RATE\n"); #endif m = float(MusEGlobal::sampleRate); } if(desc & LADSPA_HINT_BOUNDED_BELOW) { #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: has LADSPA_HINT_BOUNDED_BELOW\n"); #endif fmin = range.LowerBound * m; } else fmin = 0.0; if(desc & LADSPA_HINT_BOUNDED_ABOVE) { #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: has LADSPA_HINT_BOUNDED_ABOVE\n"); #endif fmax = range.UpperBound * m; } else fmax = 1.0; frng = fmax - fmin; imin = lrintf(fmin); if(desc & LADSPA_HINT_TOGGLED) { #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: has LADSPA_HINT_TOGGLED\n"); #endif if(val > 0) return fmax; else return fmin; } int ctlmn = 0; int ctlmx = 127; #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: port min:%f max:%f \n", fmin, fmax); #endif bool isneg = (imin < 0); int bval = val; int cval = val; switch(t) { case MidiController::RPN: case MidiController::NRPN: case MidiController::Controller7: if(isneg) { ctlmn = -64; ctlmx = 63; bval -= 64; cval -= 64; } else { ctlmn = 0; ctlmx = 127; cval -= 64; } break; case MidiController::Controller14: case MidiController::RPN14: case MidiController::NRPN14: if(isneg) { ctlmn = -8192; ctlmx = 8191; bval -= 8192; cval -= 8192; } else { ctlmn = 0; ctlmx = 16383; cval -= 8192; } break; case MidiController::Program: ctlmn = 0; ctlmx = 0xffffff; break; case MidiController::Pitch: ctlmn = -8192; ctlmx = 8191; break; case MidiController::Velo: // cannot happen default: break; } int ctlrng = ctlmx - ctlmn; float fctlrng = float(ctlmx - ctlmn); // Is it an integer control? if(desc & LADSPA_HINT_INTEGER) { float ret = float(cval); if(ret < fmin) ret = fmin; if(ret > fmax) ret = fmax; #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: has LADSPA_HINT_INTEGER returning:%f\n", ret); #endif return ret; } // Avoid divide-by-zero error below. if(ctlrng == 0) return 0.0; // It's a floating point control, just use wide open maximum range. float normval = float(bval) / fctlrng; float ret = normval * frng + fmin; #ifdef PLUGIN_DEBUGIN printf("midi2LadspaValue: float returning:%f\n", ret); #endif return ret; } //--------------------------------------------------------- // ladspaCtrlValueType //--------------------------------------------------------- CtrlValueType ladspaCtrlValueType(const LADSPA_Descriptor* plugin, int port) { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; if(desc & LADSPA_HINT_INTEGER) return VAL_INT; else if(desc & LADSPA_HINT_LOGARITHMIC) return VAL_LOG; else if(desc & LADSPA_HINT_TOGGLED) return VAL_BOOL; else return VAL_LINEAR; } //--------------------------------------------------------- // ladspaCtrlMode //--------------------------------------------------------- CtrlList::Mode ladspaCtrlMode(const LADSPA_Descriptor* plugin, int port) { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; if(desc & LADSPA_HINT_INTEGER) return CtrlList::DISCRETE; else if(desc & LADSPA_HINT_LOGARITHMIC) return CtrlList::INTERPOLATE; else if(desc & LADSPA_HINT_TOGGLED) return CtrlList::DISCRETE; else return CtrlList::INTERPOLATE; } // DELETETHIS 20 // Works but not needed. /* //--------------------------------------------------------- // ladspa2MidiController //--------------------------------------------------------- MidiController* ladspa2MidiController(const LADSPA_Descriptor* plugin, unsigned long port, int ctlnum) { int min, max, def; if(!ladspa2MidiControlValues(plugin, port, ctlnum, &min, &max, &def)) return 0; MidiController* mc = new MidiController(QString(plugin->PortNames[port]), ctlnum, min, max, def); return mc; } */ //---------------------------------------------------------------------------------- // defaultValue // If no default ladspa value found, still sets *def to 1.0, but returns false. //--------------------------------------------------------------------------------- bool ladspaDefaultValue(const LADSPA_Descriptor* plugin, unsigned long port, float* val) { if(port < plugin->PortCount) { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor; float m = (rh & LADSPA_HINT_SAMPLE_RATE) ? float(MusEGlobal::sampleRate) : 1.0f; if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh)) { *val = range.LowerBound * m; return true; } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh)) { *val = range.UpperBound*m; return true; } else if (LADSPA_IS_HINT_DEFAULT_LOW(rh)) { if (LADSPA_IS_HINT_LOGARITHMIC(rh)) { *val = expf(logf(range.LowerBound * m) * .75 + logf(range.UpperBound * m) * .25); return true; } else { *val = range.LowerBound*.75*m + range.UpperBound*.25*m; return true; } } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh)) { if (LADSPA_IS_HINT_LOGARITHMIC(rh)) { *val = expf(logf(range.LowerBound * m) * .5 + logf(range.UpperBound * m) * .5); return true; } else { *val = range.LowerBound*.5*m + range.UpperBound*.5*m; return true; } } else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh)) { if (LADSPA_IS_HINT_LOGARITHMIC(rh)) { *val = expf(logf(range.LowerBound * m) * .25 + logf(range.UpperBound * m) * .75); return true; } else { *val = range.LowerBound*.25*m + range.UpperBound*.75*m; return true; } } else if (LADSPA_IS_HINT_DEFAULT_0(rh)) { *val = 0.0; return true; } else if (LADSPA_IS_HINT_DEFAULT_1(rh)) { *val = 1.0; return true; } else if (LADSPA_IS_HINT_DEFAULT_100(rh)) { *val = 100.0; return true; } else if (LADSPA_IS_HINT_DEFAULT_440(rh)) { *val = 440.0; return true; } // No default found. Make one up... else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh) && LADSPA_IS_HINT_BOUNDED_ABOVE(rh)) { if (LADSPA_IS_HINT_LOGARITHMIC(rh)) { *val = expf(logf(range.LowerBound * m) * .5 + logf(range.UpperBound * m) * .5); return true; } else { *val = range.LowerBound*.5*m + range.UpperBound*.5*m; return true; } } else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh)) { *val = range.LowerBound; return true; } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(rh)) { // Hm. What to do here... Just try 0.0 or the upper bound if less than zero. //if(range.UpperBound > 0.0) // *val = 0.0; //else // *val = range.UpperBound * m; // Instead try this: Adopt an 'attenuator-like' policy, where upper is the default. *val = range.UpperBound * m; return true; } } // No default found. Set return value to 0.0, but return false. *val = 0.0; return false; } //--------------------------------------------------------- // ladspaControlRange //--------------------------------------------------------- void ladspaControlRange(const LADSPA_Descriptor* plugin, unsigned long port, float* min, float* max) { LADSPA_PortRangeHint range = plugin->PortRangeHints[port]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; if (desc & LADSPA_HINT_TOGGLED) { *min = 0.0; *max = 1.0; return; } float m = 1.0; if (desc & LADSPA_HINT_SAMPLE_RATE) m = float(MusEGlobal::sampleRate); if (desc & LADSPA_HINT_BOUNDED_BELOW) *min = range.LowerBound * m; else *min = 0.0; if (desc & LADSPA_HINT_BOUNDED_ABOVE) *max = range.UpperBound * m; else *max = 1.0; } //============================================================== // BEGIN PluginQuirks //============================================================== //--------------------------------------------------------- // write //--------------------------------------------------------- void PluginQuirks::write(int level, Xml& xml) const { // Defaults? Nothing to save. if(!_fixedSpeed && !_transportAffectsAudioLatency && !_overrideReportedLatency && _latencyOverrideValue == 0 && _fixNativeUIScaling == NatUISCaling::GLOBAL) return; xml.tag(level++, "quirks"); if(_fixedSpeed) xml.intTag(level, "fixedSpeed", _fixedSpeed); if(_transportAffectsAudioLatency) xml.intTag(level, "trnspAffAudLat", _transportAffectsAudioLatency); if(_overrideReportedLatency) xml.intTag(level, "ovrRepAudLat", _overrideReportedLatency); if(_latencyOverrideValue != 0) xml.intTag(level, "latOvrVal", _latencyOverrideValue); if(_fixNativeUIScaling != NatUISCaling::GLOBAL) xml.intTag(level, "fixNatUIScal", _fixNativeUIScaling); xml.etag(--level, "quirks"); } //--------------------------------------------------------- // read // return true on error //--------------------------------------------------------- bool PluginQuirks::read(Xml& xml) { for (;;) { Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case Xml::Error: case Xml::End: return true; case Xml::TagStart: if (tag == "fixedSpeed") _fixedSpeed = xml.parseInt(); else if (tag == "trnspAffAudLat") _transportAffectsAudioLatency = xml.parseInt(); else if (tag == "ovrRepAudLat") _overrideReportedLatency = xml.parseInt(); else if (tag == "latOvrVal") _latencyOverrideValue = xml.parseInt(); else if (tag == "fixNatUIScal") _fixNativeUIScaling = (NatUISCaling)xml.parseInt(); else xml.unknown("PluginQuirks"); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "quirks") { return false; } return true; default: break; } } return true; } bool PluginQuirks::fixNativeUIScaling() const { if (qApp->devicePixelRatio() <= 1.0) return false; // no hidpi monitor in use, no need to fix anything return ((_fixNativeUIScaling == NatUISCaling::GLOBAL && MusEGlobal::config.noPluginScaling) || _fixNativeUIScaling == NatUISCaling::ON); } //============================================================== // END PluginQuirks //============================================================== //--------------------------------------------------------- // Plugin //--------------------------------------------------------- Plugin::Plugin(QFileInfo* f, const LADSPA_Descriptor* d, const QString& uri, bool isDssi, bool isDssiSynth, bool isDssiVst, PluginFeatures_t reqFeatures) { _isDssi = isDssi; _isDssiSynth = isDssiSynth; _isDssiVst = isDssiVst; _isLV2Plugin = false; _isLV2Synth = false; _isVstNativePlugin = false; _isVstNativeSynth = false; _requiredFeatures = reqFeatures; _usesTimePosition = false; #ifdef DSSI_SUPPORT dssi_descr = nullptr; #endif if(f) fi = *f; _uri = uri; plugin = nullptr; ladspa = nullptr; _handle = nullptr; _references = 0; _instNo = 0; _label = QString(d->Label); _name = QString(d->Name); _uniqueID = d->UniqueID; _maker = QString(d->Maker); _copyright = QString(d->Copyright); _portCount = d->PortCount; _inports = 0; _outports = 0; _controlInPorts = 0; _controlOutPorts = 0; for(unsigned long k = 0; k < _portCount; ++k) { LADSPA_PortDescriptor pd = d->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) { if(pd & LADSPA_PORT_INPUT) ++_inports; else if(pd & LADSPA_PORT_OUTPUT) ++_outports; } else if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) ++_controlInPorts; else if(pd & LADSPA_PORT_OUTPUT) ++_controlOutPorts; } } // Hack: Blacklist vst plugins in-place, configurable for now. if ((_inports != _outports) || (_isDssiVst && !MusEGlobal::config.vstInPlace)) _requiredFeatures |= PluginNoInPlaceProcessing; } Plugin::Plugin(const MusEPlugin::PluginScanInfoStruct& info) { _isDssi = false; _isDssiSynth = false; _isDssiVst = false; _isLV2Plugin = false; _isLV2Synth = false; _isVstNativePlugin = false; _isVstNativeSynth = false; _requiredFeatures = info._requiredFeatures; switch(info._type) { case MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA: break; case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI: { _isDssi = true; if(info._class & MusEPlugin::PluginScanInfoStruct::PluginClassInstrument) _isDssiSynth = true; } break; case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST: { _isDssi = true; _isDssiVst = true; if(info._class & MusEPlugin::PluginScanInfoStruct::PluginClassInstrument) _isDssiSynth = true; } break; case MusEPlugin::PluginScanInfoStruct::PluginTypeVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeLV2: case MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeMESS: case MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown: case MusEPlugin::PluginScanInfoStruct::PluginTypeNone: case MusEPlugin::PluginScanInfoStruct::PluginTypeAll: break; } #ifdef DSSI_SUPPORT dssi_descr = nullptr; #endif // fi = info._fi; fi = QFileInfo(PLUGIN_GET_QSTRING(info.filePath())); _uri = PLUGIN_GET_QSTRING(info._uri); plugin = nullptr; ladspa = nullptr; _handle = nullptr; _references = 0; _instNo = 0; _label = PLUGIN_GET_QSTRING(info._label); _name = PLUGIN_GET_QSTRING(info._name); _uniqueID = info._uniqueID; _maker = PLUGIN_GET_QSTRING(info._maker); _copyright = PLUGIN_GET_QSTRING(info._copyright); _portCount = info._portCount; _inports = info._inports; _outports = info._outports; _controlInPorts = info._controlInPorts; _controlOutPorts = info._controlOutPorts; // Hack: Blacklist vst plugins in-place, configurable for now. if(_isDssiVst && !MusEGlobal::config.vstInPlace) _requiredFeatures |= PluginNoInPlaceProcessing; _usesTimePosition = info._pluginFlags & MusEPlugin::PluginScanInfoStruct::SupportsTimePosition; } Plugin::~Plugin() { if(plugin && !isLV2Plugin() && !isVstNativePlugin()) // delete plugin; printf("Plugin::~Plugin Error: plugin is not NULL\n"); } //--------------------------------------------------------- // incReferences //--------------------------------------------------------- int Plugin::incReferences(int val) { #ifdef PLUGIN_DEBUGIN fprintf(stderr, "Plugin::incReferences _references:%d val:%d\n", _references, val); #endif int newref = _references + val; if(newref <= 0) { _references = 0; if(_handle) { #ifdef PLUGIN_DEBUGIN fprintf(stderr, "Plugin::incReferences no more instances, closing library\n"); #endif dlclose(_handle); } _handle = nullptr; ladspa = nullptr; plugin = nullptr; rpIdx.clear(); #ifdef DSSI_SUPPORT dssi_descr = nullptr; #endif return 0; } if(_handle == nullptr) { _handle = dlopen(fi.filePath().toLatin1().constData(), RTLD_NOW); if(_handle == 0) { fprintf(stderr, "Plugin::incReferences dlopen(%s) failed: %s\n", fi.filePath().toLatin1().constData(), dlerror()); return 0; } #ifdef DSSI_SUPPORT DSSI_Descriptor_Function dssi = (DSSI_Descriptor_Function)dlsym(_handle, "dssi_descriptor"); if(dssi) { const DSSI_Descriptor* descr; for(unsigned long i = 0;; ++i) { descr = dssi(i); if(descr == nullptr) break; QString label(descr->LADSPA_Plugin->Label); if(label == _label) { _isDssi = true; ladspa = nullptr; dssi_descr = descr; plugin = descr->LADSPA_Plugin; break; } } } else #endif // DSSI_SUPPORT { LADSPA_Descriptor_Function ladspadf = (LADSPA_Descriptor_Function)dlsym(_handle, "ladspa_descriptor"); if(ladspadf) { const LADSPA_Descriptor* descr; for(unsigned long i = 0;; ++i) { descr = ladspadf(i); if(descr == nullptr) break; QString label(descr->Label); if(label == _label) { _isDssi = false; ladspa = ladspadf; plugin = descr; #ifdef DSSI_SUPPORT dssi_descr = nullptr; #endif break; } } } } if(plugin != nullptr) { _name = QString(plugin->Name); _uniqueID = plugin->UniqueID; _maker = QString(plugin->Maker); _copyright = QString(plugin->Copyright); _portCount = plugin->PortCount; _inports = 0; _outports = 0; _controlInPorts = 0; _controlOutPorts = 0; for(unsigned long k = 0; k < _portCount; ++k) { LADSPA_PortDescriptor pd = plugin->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) { if(pd & LADSPA_PORT_INPUT) ++_inports; else if(pd & LADSPA_PORT_OUTPUT) ++_outports; rpIdx.push_back((unsigned long)-1); } else if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) { rpIdx.push_back(_controlInPorts); ++_controlInPorts; } else if(pd & LADSPA_PORT_OUTPUT) { rpIdx.push_back((unsigned long)-1); ++_controlOutPorts; } } } // Hack: Blacklist vst plugins in-place, configurable for now. if ((_inports != _outports) || (_isDssiVst && !MusEGlobal::config.vstInPlace)) _requiredFeatures |= PluginNoInPlaceProcessing; } } if(plugin == nullptr) { dlclose(_handle); _handle = 0; _references = 0; fprintf(stderr, "Plugin::incReferences Error: %s no plugin!\n", fi.filePath().toLatin1().constData()); return 0; } _references = newref; return _references; } //--------------------------------------------------------- // range //--------------------------------------------------------- void Plugin::range(unsigned long i, float* min, float* max) const { ladspaControlRange(plugin, i, min, max); } //--------------------------------------------------------- // defaultValue //--------------------------------------------------------- double Plugin::defaultValue(unsigned long port) const { float val; ladspaDefaultValue(plugin, port, &val); return val; } //--------------------------------------------------------- // ctrlValueType //--------------------------------------------------------- CtrlValueType Plugin::ctrlValueType(unsigned long i) const { return ladspaCtrlValueType(plugin, i); } //--------------------------------------------------------- // ctrlMode //--------------------------------------------------------- CtrlList::Mode Plugin::ctrlMode(unsigned long i) const { return ladspaCtrlMode(plugin, i); } CtrlEnumValues* Plugin::ctrlEnumValues ( unsigned long ) const { return nullptr; } void PluginGroups::shift_left(int first, int last) { for (int i=first; i<=last; i++) replace_group(i, i-1); } void PluginGroups::shift_right(int first, int last) { for (int i=last; i>=first; i--) replace_group(i,i+1); } void PluginGroups::erase(int index) { for (PluginGroups::iterator it=begin(); it!=end(); it++) { it->remove(index); } } void PluginGroups::replace_group(int old, int now) { for (PluginGroups::iterator it=begin(); it!=end(); it++) { if (it->contains(old)) { it->remove(old); it->insert(now); } } } //--------------------------------------------------------- // initPlugins //--------------------------------------------------------- void initPlugins() { const char* message = "Plugins: loadPluginLib: "; const MusEPlugin::PluginScanList& scan_list = MusEPlugin::pluginList; for(MusEPlugin::ciPluginScanList isl = scan_list.begin(); isl != scan_list.end(); ++isl) { const MusEPlugin::PluginScanInfoRef inforef = *isl; const MusEPlugin::PluginScanInfoStruct& info = inforef->info(); switch(info._type) { case MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA: { if(MusEGlobal::loadPlugins) { // Make sure it doesn't already exist. if(const Plugin* pl = MusEGlobal::plugins.find( PLUGIN_GET_QSTRING(info._completeBaseName), PLUGIN_GET_QSTRING(info._uri), PLUGIN_GET_QSTRING(info._label))) { fprintf(stderr, "Ignoring LADSPA effect label:%s uri:%s path:%s duplicate of path:%s\n", PLUGIN_GET_CSTRING(info._label), PLUGIN_GET_CSTRING(info._uri), PLUGIN_GET_CSTRING(info.filePath()), pl->filePath().toLatin1().constData()); } else { if(MusEGlobal::debugMsg) info.dump(message); MusEGlobal::plugins.add(info); } } } break; case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI: case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST: { #ifdef DSSI_SUPPORT if(MusEGlobal::loadDSSI) { // Allow both effects and instruments for now. if(info._class & MusEPlugin::PluginScanInfoStruct::PluginClassEffect || info._class & MusEPlugin::PluginScanInfoStruct::PluginClassInstrument) { // Make sure it doesn't already exist. if(const Plugin* pl = MusEGlobal::plugins.find( PLUGIN_GET_QSTRING(info._completeBaseName), PLUGIN_GET_QSTRING(info._uri), PLUGIN_GET_QSTRING(info._label))) { fprintf(stderr, "Ignoring DSSI effect label:%s uri:%s path:%s duplicate of path:%s\n", PLUGIN_GET_CSTRING(info._label), PLUGIN_GET_CSTRING(info._uri), PLUGIN_GET_CSTRING(info.filePath()), pl->filePath().toLatin1().constData()); } else { if(MusEGlobal::debugMsg) info.dump(message); MusEGlobal::plugins.add(info); } } } #endif } break; case MusEPlugin::PluginScanInfoStruct::PluginTypeVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeLV2: case MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeMESS: case MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown: case MusEPlugin::PluginScanInfoStruct::PluginTypeNone: case MusEPlugin::PluginScanInfoStruct::PluginTypeAll: break; } } } //--------------------------------------------------------- // find //--------------------------------------------------------- Plugin* PluginList::find(const QString& file, const QString& uri, const QString& label) const { const bool f_empty = file.isEmpty(); const bool u_empty = uri.isEmpty(); const bool l_empty = label.isEmpty(); for (ciPlugin i = begin(); i != end(); ++i) { if ( (!u_empty || f_empty || file == (*i)->lib()) && (u_empty || uri == (*i)->uri()) && (!u_empty || l_empty || label == (*i)->label())) return *i; } return 0; } //--------------------------------------------------------- // Pipeline //--------------------------------------------------------- Pipeline::Pipeline() : std::vector() { for(int i = 0; i < MusECore::MAX_CHANNELS; ++i) buffer[i] = nullptr; initBuffers(); for (int i = 0; i < MusECore::PipelineDepth; ++i) push_back(nullptr); } //--------------------------------------------------------- // Pipeline copy constructor //--------------------------------------------------------- Pipeline::Pipeline(const Pipeline& p, AudioTrack* t) : std::vector() { for(int i = 0; i < MusECore::MAX_CHANNELS; ++i) buffer[i] = nullptr; initBuffers(); for(int i = 0; i < MusECore::PipelineDepth; ++i) { PluginI* pli = p[i]; if(pli) { Plugin* pl = pli->plugin(); if(pl) { PluginI* new_pl = new PluginI(); if(new_pl->initPluginInstance(pl, t->channels())) { fprintf(stderr, "cannot instantiate plugin <%s>\n", pl->name().toLatin1().constData()); delete new_pl; } else { // Assigns valid ID and track to plugin, and creates controllers for plugin. t->setupPlugin(new_pl, i); push_back(new_pl); continue; } } } push_back(nullptr); // No plugin. Initialize with NULL. } } //--------------------------------------------------------- // ~Pipeline //--------------------------------------------------------- Pipeline::~Pipeline() { removeAll(); for (int i = 0; i < MusECore::MAX_CHANNELS; ++i) if(buffer[i]) ::free(buffer[i]); } void Pipeline::initBuffers() { for(int i = 0; i < MusECore::MAX_CHANNELS; ++i) { if(!buffer[i]) { #ifdef _WIN32 buffer[i] = (float *) _aligned_malloc(16, sizeof(float *) * MusEGlobal::segmentSize); if(buffer[i] == NULL) { fprintf(stderr, "ERROR: Pipeline ctor: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)(buffer + i), 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: Pipeline ctor: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif } } for(int i = 0; i < MusECore::MAX_CHANNELS; ++i) { if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) buffer[i][q] = MusEGlobal::denormalBias; } else memset(buffer[i], 0, sizeof(float) * MusEGlobal::segmentSize); } } //--------------------------------------------------------- // latency //--------------------------------------------------------- float Pipeline::latency() const { float l = 0.0f; const PluginI* p; for(int i = 0; i < MusECore::PipelineDepth; ++i) { p = (*this)[i]; if(p) { // REMOVE Tim. lv2. Added. TESTING. Do we need to leave this alone for reporting? // I think so... It seemed we do that with tracks but then those are wave and midi tracks... #if 0 // If the transport affects audio latency, it means we can completely correct // for the latency by adjusting the transport, therefore meaning zero // resulting audio latency. As far as the rest of the app knows, the plugin // in this rack position has zero audio latency. Yet we still retain the // original latency value in each plugin so we can use it. if(!p->cquirks()._transportAffectsAudioLatency) #endif l+= p->latency(); } } return l; } //--------------------------------------------------------- // addScheduledControlEvent // track_ctrl_id is the fully qualified track audio controller number // Returns true if event cannot be delivered //--------------------------------------------------------- bool Pipeline::addScheduledControlEvent(int track_ctrl_id, double val, unsigned frame) { // If a track controller, or the special dssi synth controller block, just return. if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MusECore::MAX_PLUGINS, 0)) return true; int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; for (int i = 0; i < MusECore::PipelineDepth; ++i) { PluginI* p = (*this)[i]; if(p && p->id() == rack_idx) return p->addScheduledControlEvent(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK, val, frame); } return true; } //--------------------------------------------------------- // controllerEnabled // Returns whether automation control stream is enabled or disabled. // Used during automation recording to inhibit gui controls //--------------------------------------------------------- bool Pipeline::controllerEnabled(int track_ctrl_id) { // If a track controller, or the special dssi synth controller block, just return. if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MusECore::MAX_PLUGINS, 0)) return false; int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; for (int i = 0; i < MusECore::PipelineDepth; ++i) { PluginI* p = (*this)[i]; if(p && p->id() == rack_idx) return p->controllerEnabled(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK); } return false; } //--------------------------------------------------------- // enableController // Enable or disable gui automation control stream. // Used during automation recording to inhibit gui controls //--------------------------------------------------------- void Pipeline::enableController(int track_ctrl_id, bool en) { // If a track controller, or the special dssi synth controller block, just return. if(track_ctrl_id < AC_PLUGIN_CTL_BASE || track_ctrl_id >= (int)genACnum(MusECore::MAX_PLUGINS, 0)) return; int rack_idx = (track_ctrl_id - AC_PLUGIN_CTL_BASE) >> AC_PLUGIN_CTL_BASE_POW; for (int i = 0; i < MusECore::PipelineDepth; ++i) { PluginI* p = (*this)[i]; if(p && p->id() == rack_idx) { p->enableController(track_ctrl_id & AC_PLUGIN_CTL_ID_MASK, en); return; } } } //--------------------------------------------------------- // setChannels //--------------------------------------------------------- void Pipeline::setChannels(int n) { for (int i = 0; i < MusECore::PipelineDepth; ++i) if ((*this)[i]) (*this)[i]->setChannels(n); } //--------------------------------------------------------- // insert // give ownership of object plugin to Pipeline //--------------------------------------------------------- void Pipeline::insert(PluginI* plugin, int index) { remove(index); (*this)[index] = plugin; } //--------------------------------------------------------- // remove //--------------------------------------------------------- void Pipeline::remove(int index) { PluginI* plugin = (*this)[index]; if (plugin) delete plugin; (*this)[index] = 0; } //--------------------------------------------------------- // removeAll //--------------------------------------------------------- void Pipeline::removeAll() { for (int i = 0; i < MusECore::PipelineDepth; ++i) remove(i); } //--------------------------------------------------------- // isOn //--------------------------------------------------------- bool Pipeline::isOn(int idx) const { PluginI* p = (*this)[idx]; if (p) return p->on(); return false; } //--------------------------------------------------------- // setOn //--------------------------------------------------------- void Pipeline::setOn(int idx, bool flag) { PluginI* p = (*this)[idx]; if (p) { p->setOn(flag); if (p->gui()) p->gui()->setOn(flag); } } //--------------------------------------------------------- // label //--------------------------------------------------------- QString Pipeline::label(int idx) const { PluginI* p = (*this)[idx]; if (p) return p->label(); return QString(""); } //--------------------------------------------------------- // name //--------------------------------------------------------- QString Pipeline::name(int idx) const { PluginI* p = (*this)[idx]; if (p) return p->name(); return "<" + QObject::tr("FX slot") + " " + QString::number(idx + 1) + ">"; } //--------------------------------------------------------- // uri //--------------------------------------------------------- QString Pipeline::uri(int idx) const { PluginI* p = (*this)[idx]; if (p) return p->uri(); return QString(); } //--------------------------------------------------------- // empty //--------------------------------------------------------- bool Pipeline::empty(int idx) const { PluginI* p = (*this)[idx]; return p == 0; } //--------------------------------------------------------- // move //--------------------------------------------------------- void Pipeline::move(int idx, bool up) { PluginI* p1 = (*this)[idx]; if (up) { (*this)[idx] = (*this)[idx-1]; if((*this)[idx]) { (*this)[idx]->setID(idx); } (*this)[idx-1] = p1; if(p1) { p1->setID(idx - 1); if(p1->track()) MusEGlobal::audio->msgSwapControllerIDX(p1->track(), idx, idx - 1); } } else { (*this)[idx] = (*this)[idx+1]; if((*this)[idx]) { (*this)[idx]->setID(idx); } (*this)[idx+1] = p1; if(p1) { p1->setID(idx + 1); if(p1->track()) MusEGlobal::audio->msgSwapControllerIDX(p1->track(), idx, idx + 1); } } } //--------------------------------------------------------- // isDssiPlugin //--------------------------------------------------------- bool Pipeline::isDssiPlugin(int idx) const { PluginI* p = (*this)[idx]; if(p) return p->isDssiPlugin(); return false; } bool Pipeline::isLV2Plugin(int idx) const { PluginI* p = (*this)[idx]; if(p) return p->isLV2Plugin(); return false; } bool Pipeline::isVstNativePlugin(int idx) const { PluginI* p = (*this)[idx]; if(p) return p->isVstNativePlugin(); return false; } //--------------------------------------------------------- // has_dssi_ui //--------------------------------------------------------- bool Pipeline::has_dssi_ui(int idx) const { PluginI* p = (*this)[idx]; if(p) { #ifdef LV2_SUPPORT if(p->plugin() && p->plugin()->isLV2Plugin()) return ((LV2PluginWrapper *)p->plugin())->hasNativeGui(); #endif #ifdef VST_NATIVE_SUPPORT if(p->plugin() && p->plugin()->isVstNativePlugin()) return ((VstNativePluginWrapper *)p->plugin())->hasNativeGui(); #endif return !p->dssi_ui_filename().isEmpty(); } return false; } //--------------------------------------------------------- // showGui //--------------------------------------------------------- void Pipeline::showGui(int idx, bool flag) { PluginI* p = (*this)[idx]; if (p) p->showGui(flag); } //--------------------------------------------------------- // showNativeGui //--------------------------------------------------------- #if defined(LV2_SUPPORT) || defined(VST_NATIVE_SUPPORT) || defined(OSC_SUPPORT) void Pipeline::showNativeGui(int idx, bool flag) { PluginI* p = (*this)[idx]; #ifdef LV2_SUPPORT if(p && p->plugin()->isLV2Plugin()) { ((LV2PluginWrapper *)p->plugin())->showNativeGui(p, flag); return; } #endif #ifdef VST_NATIVE_SUPPORT if(p && p->plugin()->isVstNativePlugin()) { ((VstNativePluginWrapper *)p->plugin())->showNativeGui(p, flag); return; } #endif #ifdef OSC_SUPPORT if (p) p->oscIF().oscShowGui(flag); #endif } #else // defined(LV2_SUPPORT) || defined(VST_NATIVE_SUPPORT) || defined(OSC_SUPPORT) void Pipeline::showNativeGui(int /*idx*/, bool /*flag*/) { } #endif // defined(LV2_SUPPORT) || defined(VST_NATIVE_SUPPORT) || defined(OSC_SUPPORT) //--------------------------------------------------------- // deleteGui //--------------------------------------------------------- void Pipeline::deleteGui(int idx) { if(idx >= MusECore::PipelineDepth) return; PluginI* p = (*this)[idx]; if(p) p->deleteGui(); #ifdef LV2_SUPPORT if(p && p->plugin()->isLV2Plugin()) { ((LV2PluginWrapper *)p->plugin())->showNativeGui(p, false); } #endif #ifdef VST_NATIVE_SUPPORT if(p && p->plugin()->isVstNativePlugin()) { ((VstNativePluginWrapper *)p->plugin())->showNativeGui(p, false); } #endif } //--------------------------------------------------------- // deleteAllGuis //--------------------------------------------------------- void Pipeline::deleteAllGuis() { for(int i = 0; i < MusECore::PipelineDepth; i++) deleteGui(i); } //--------------------------------------------------------- // guiVisible //--------------------------------------------------------- bool Pipeline::guiVisible(int idx) { PluginI* p = (*this)[idx]; if (p) return p->guiVisible(); return false; } //--------------------------------------------------------- // nativeGuiVisible //--------------------------------------------------------- bool Pipeline::nativeGuiVisible(int idx) { PluginI* p = (*this)[idx]; if (p) { #ifdef LV2_SUPPORT if(p->plugin()->isLV2Plugin()) return ((LV2PluginWrapper *)p->plugin())->nativeGuiVisible(p); #endif #ifdef VST_NATIVE_SUPPORT if(p->plugin()->isVstNativePlugin()) return ((VstNativePluginWrapper *)p->plugin())->nativeGuiVisible(p); #endif return p->nativeGuiVisible(); } return false; } //--------------------------------------------------------- // apply // If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- void Pipeline::apply(unsigned pos, unsigned long ports, unsigned long nframes, float** buffer1) { bool swap = false; // Divide up the total pipeline latency to distribute latency correction // among the plugins according to the latency of each plugin. Each has // more correction than the next. The values are negative, meaning 'correction'. const int sz = size(); float latency_corr_offsets[sz]; float latency_corr_offset = 0.0f; for(int i = sz - 1; i >= 0; --i) { const PluginI* p = (*this)[i]; if(!p) continue; const float lat = p->latency(); // If the transport affects audio latency, it means we can completely correct // for the latency by adjusting the transport, therefore meaning zero // resulting audio latency. As far as the rest of the app knows, the plugin // in this rack position has zero audio latency. Yet we still retain the // original latency value in each plugin so we can use it. // Here we use a neat trick to conditionally subtract as we go, yet still // set the right transport correction offset value for each plugin. latency_corr_offsets[i] = latency_corr_offset - lat; if(!p->cquirks()._transportAffectsAudioLatency) latency_corr_offset -= lat; } for (int i = 0; i < sz; ++i) { PluginI* p = (*this)[i]; if(!p) continue; const float corr_offset = latency_corr_offsets[i]; if (p->on()) { if (!(p->requiredFeatures() & PluginNoInPlaceProcessing)) { if (swap) p->apply(pos, nframes, ports, buffer, buffer, corr_offset); else p->apply(pos, nframes, ports, buffer1, buffer1, corr_offset); } else { if (swap) p->apply(pos, nframes, ports, buffer, buffer1, corr_offset); else p->apply(pos, nframes, ports, buffer1, buffer, corr_offset); swap = !swap; } } else { p->apply(pos, nframes, 0, 0, 0, corr_offset); // Do not process (run) audio, process controllers only. } } if (ports != 0 && swap) { for (unsigned long i = 0; i < ports; ++i) AL::dsp->cpy(buffer1[i], buffer[i], nframes); } } //--------------------------------------------------------- // PluginIBase //--------------------------------------------------------- PluginIBase::PluginIBase() { _gui = 0; } PluginIBase::~PluginIBase() { if(_gui) delete _gui; } //--------------------------------------------------------- // showGui //--------------------------------------------------------- void PluginIBase::showGui() { if(_gui == 0) makeGui(); _gui->updateWindowTitle(); if(_gui->isVisible()) _gui->hide(); else _gui->show(); } void PluginIBase::showGui(bool flag) { if(flag) { if(_gui == 0) makeGui(); _gui->show(); } else { if(_gui) _gui->hide(); } } //--------------------------------------------------------- // guiVisible //--------------------------------------------------------- bool PluginIBase::guiVisible() const { return _gui && _gui->isVisible(); } void PluginIBase::setGeometry(int x, int y, int w, int h) { _guiGeometry = QRect(x, y, w, h); if(_gui) { #ifdef QT_SHOW_POS_BUG_WORKAROUND // Because of the bug, no matter what we must supply a position, // even upon first showing... // Check sane size. if(w == 0) w = _gui->sizeHint().width(); if(h == 0) h = _gui->sizeHint().height(); // No size hint? Try minimum size. if(w == 0) w = _gui->minimumSize().width(); if(h == 0) h = _gui->minimumSize().height(); // Fallback. if(w == 0) w = 200; if(h == 0) h = 200; _gui->setGeometry(x, y, w, h); #else // If the saved geometry is valid, use it. // Otherwise this is probably the first time showing, // so do not set a geometry - let Qt pick one // (using auto-placement and sizeHint). if(!(x == 0 && y == 0 && w == 0 && h == 0)) { // Check sane size. if(w == 0) w = _gui->sizeHint().width(); if(h == 0) h = _gui->sizeHint().height(); // No size hint? Try minimum size. if(w == 0) w = _gui->minimumSize().width(); if(h == 0) h = _gui->minimumSize().height(); // Fallback. if(w == 0) w = 200; if(h == 0) h = 200; _gui->setGeometry(x, y, w, h); } #endif } } // Returns the current geometry of the gui, or if the gui does not exist, // the saved gui geometry. void PluginIBase::getGeometry(int *x, int *y, int *w, int *h) const { // If gui does not exist return the saved geometry. if(!_gui) { if(x) *x = _guiGeometry.x(); if(y) *y = _guiGeometry.y(); if(w) *w = _guiGeometry.width(); if(h) *h = _guiGeometry.height(); return; } // Return the actual gui geometry. if(x) *x = _gui->geometry().x(); if(y) *y = _gui->geometry().y(); if(w) *w = _gui->geometry().width(); if(h) *h = _gui->geometry().height(); } // Saves the current gui geometry. void PluginIBase::saveGeometry(int x, int y, int w, int h) { _guiGeometry = QRect(x, y, w, h); } // Returns the saved gui geometry. void PluginIBase::savedGeometry(int *x, int *y, int *w, int *h) const { if(x) *x = _guiGeometry.x(); if(y) *y = _guiGeometry.y(); if(w) *w = _guiGeometry.width(); if(h) *h = _guiGeometry.height(); } // Sets the gui's geometry. Also updates the saved geometry. void PluginIBase::setNativeGeometry(int x, int y, int w, int h) { _nativeGuiGeometry = QRect(x, y, w, h); } // Returns the current geometry of the gui, or if the gui does not exist, // the saved gui geometry. void PluginIBase::getNativeGeometry(int *x, int *y, int *w, int *h) const { if(x) *x = 0; if(y) *y = 0; if(w) *w = 0; if(h) *h = 0; } // Saves the current gui geometry. void PluginIBase::saveNativeGeometry(int x, int y, int w, int h) { _nativeGuiGeometry = QRect(x, y, w, h); } // Returns the saved gui geometry. void PluginIBase::savedNativeGeometry(int *x, int *y, int *w, int *h) const { if(x) *x = _nativeGuiGeometry.x(); if(y) *y = _nativeGuiGeometry.y(); if(w) *w = _nativeGuiGeometry.width(); if(h) *h = _nativeGuiGeometry.height(); } //--------------------------------------------------------- // addScheduledControlEvent // i is the specific index of the control input port // Returns true if event cannot be delivered //--------------------------------------------------------- bool PluginIBase::addScheduledControlEvent(unsigned long i, double val, unsigned frame) { if(i >= parameters()) { printf("PluginIBase::addScheduledControlEvent param number %lu out of range of ports:%lu\n", i, parameters()); return true; } ControlEvent ce; ce.unique = false; ce.fromGui = false; ce.idx = i; ce.value = val; // Time-stamp the event. This does a possibly slightly slow call to gettimeofday via timestamp(). // timestamp() is more or less an estimate of the current frame. (This is exactly how ALSA events // are treated when they arrive in our ALSA driver.) //ce.frame = MusEGlobal::audio->timestamp(); // p4.0.23 timestamp() is circular, which is making it impossible to deal with 'modulo' events which // slip in 'under the wire' before processing the ring buffers. So try this linear timestamp instead: ce.frame = frame; if(_controlFifo.put(ce)) { fprintf(stderr, "PluginIBase::addScheduledControlEvent: fifo overflow: in control number:%lu\n", i); return true; } return false; } CtrlEnumValues* PluginIBase::ctrlEnumValues(unsigned long i) const { Q_UNUSED(i) return nullptr; } QString PluginIBase::portGroup(long unsigned int i) const { Q_UNUSED(i) return QString(); } bool PluginIBase::ctrlIsTrigger(long unsigned int i) const { Q_UNUSED(i) return false; } bool PluginIBase::ctrlNotOnGui(long unsigned int i) const { Q_UNUSED(i) return false; } QString PluginIBase::dssi_ui_filename() const { QString libr(lib()); if(dirPath().isEmpty() || libr.isEmpty()) return QString(); QString guiPath(dirPath() + "/" + libr); QDir guiDir(guiPath, "*", QDir::Unsorted, QDir::Files); if(!guiDir.exists()) return QString(); QStringList list = guiDir.entryList(); QString plug(pluginLabel()); QString lib_qt_ui; QString lib_any_ui; QString plug_qt_ui; QString plug_any_ui; for(int i = 0; i < list.count(); ++i) { QFileInfo fi(guiPath + QString("/") + list[i]); QString gui(fi.filePath()); struct stat buf; if(stat(gui.toLatin1().constData(), &buf)) continue; if(!((S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) && (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))) continue; // FIXME: Qt::CaseInsensitive - a quick and dirty way to accept any suffix. Should be case sensitive... if(!libr.isEmpty()) { if(lib_qt_ui.isEmpty() && list[i].contains(libr + QString("_qt"), Qt::CaseInsensitive)) lib_qt_ui = gui; if(lib_any_ui.isEmpty() && list[i].contains(libr + QString('_') /*, Qt::CaseInsensitive*/)) lib_any_ui = gui; } if(!plug.isEmpty()) { if(plug_qt_ui.isEmpty() && list[i].contains(plug + QString("_qt"), Qt::CaseInsensitive)) plug_qt_ui = gui; if(plug_any_ui.isEmpty() && list[i].contains(plug + QString('_') /*, Qt::CaseInsensitive*/)) plug_any_ui = gui; } } // Prefer qt plugin ui if(!plug_qt_ui.isEmpty()) return plug_qt_ui; // Prefer any plugin ui if(!plug_any_ui.isEmpty()) return plug_any_ui; // Prefer qt lib ui if(!lib_qt_ui.isEmpty()) return lib_qt_ui; // Prefer any lib ui if(!lib_any_ui.isEmpty()) return lib_any_ui; // No suitable UI file found return QString(); }; //--------------------------------------------------------- // PluginI //--------------------------------------------------------- void PluginI::init() { _plugin = 0; instances = 0; handle = 0; controls = 0; controlsOut = 0; controlsOutDummy = 0; controlPorts = 0; controlOutPorts = 0; _audioInSilenceBuf = 0; _audioOutDummyBuf = 0; _hasLatencyOutPort = false; _latencyOutPort = 0; _on = true; initControlValues = false; _showNativeGuiPending = false; } PluginI::PluginI() { _id = -1; _track = 0; init(); } //--------------------------------------------------------- // PluginI //--------------------------------------------------------- PluginI::~PluginI() { #ifdef OSC_SUPPORT _oscif.oscSetPluginI(nullptr); #endif if (_plugin) { deactivate(); _plugin->incReferences(-1); } if(_audioInSilenceBuf) free(_audioInSilenceBuf); if(_audioOutDummyBuf) free(_audioOutDummyBuf); if (controlsOutDummy) delete[] controlsOutDummy; if (controlsOut) delete[] controlsOut; if (controls) delete[] controls; if (handle) delete[] handle; } //--------------------------------------------------------- // setID //--------------------------------------------------------- void PluginI::setID(int i) { _id = i; } //--------------------------------------------------------- // updateControllers //--------------------------------------------------------- void PluginI::updateControllers() { if(!_track) return; for(unsigned long i = 0; i < controlPorts; ++i) _track->setPluginCtrlVal(genACnum(_id, i), controls[i].val); // TODO A faster bulk message } //--------------------------------------------------------- // setChannel //--------------------------------------------------------- void PluginI::setChannels(int c) { channel = c; unsigned long ins = _plugin->inports(); unsigned long outs = _plugin->outports(); int ni = 1; if(outs) ni = c / outs; else if(ins) ni = c / ins; if(ni < 1) ni = 1; if (ni == instances) return; LADSPA_Handle* handles = new LADSPA_Handle[ni]; if(ni > instances) { for(int i = 0; i < ni; ++i) { if(i < instances) // Transfer existing handle from old array to new array. handles[i] = handle[i]; else { // Create a new plugin instance with handle. handles[i] = _plugin->instantiate(this); if(handles[i] == nullptr) { fprintf(stderr, "PluginI::setChannels: cannot instantiate instance %d\n", i); // Although this is a messed up state not easy to get out of (final # of channels?), try not to assert(). // Whoever uses these will have to check instance count or null handle, and try to gracefully fix it and allow a song save. for(int k = i; k < ni; ++k) handles[i] = nullptr; ni = i + 1; //channel = ?; break; } } } } else { for(int i = 0; i < instances; ++i) { if(i < ni) // Transfer existing handle from old array to new array. handles[i] = handle[i]; else { // Delete existing plugin instance. // Previously we deleted all the instances and rebuilt from scratch. // One side effect of this: Since a GUI is constructed only on the first handle, // previously the native GUI would close when changing channels. Now it doesn't, which is good. _plugin->deactivate(handle[i]); _plugin->cleanup(handle[i]); } } } // Delete the old array, and set the new array. delete[] handle; handle = handles; // Connect ports: unsigned long curPort = 0; unsigned long curOutPort = 0; unsigned long ports = _plugin->ports(); for(unsigned long k = 0; k < ports; ++k) { LADSPA_PortDescriptor pd = _plugin->portd(k); if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) { for(int i = instances; i < ni; ++i) _plugin->connectPort(handle[i], k, &controls[curPort].val); controls[curPort].idx = k; ++curPort; } else if(pd & LADSPA_PORT_OUTPUT) { // Connect only the first instance's output controls. // We don't have a mechanism to display the other instances' outputs. _plugin->connectPort(handle[0], k, &controlsOut[curOutPort].val); // Connect the rest to dummy ports. for(int i = 1; i < ni; ++i) _plugin->connectPort(handle[i], k, &controlsOutDummy[curOutPort].val); controlsOut[curOutPort].idx = k; ++curOutPort; } } } // Activate new instances. for(int i = instances; i < ni; ++i) _plugin->activate(handle[i]); // Initialize control values. if(initControlValues) { for(unsigned long i = 0; i < controlPorts; ++i) controls[i].val = controls[i].tmpVal; } else { // get initial control values from plugin for(unsigned long i = 0; i < controlPorts; ++i) controls[i].tmpVal = controls[i].val; } // Finally, set the new number of instances. instances = ni; } //--------------------------------------------------------- // setParam //--------------------------------------------------------- void PluginI::setParam(unsigned long i, double val) { addScheduledControlEvent(i, val, MusEGlobal::audio->curFrame()); } //--------------------------------------------------------- // defaultValue //--------------------------------------------------------- double PluginI::defaultValue(unsigned long param) const { if(param >= controlPorts) return 0.0; return _plugin->defaultValue(controls[param].idx); } void PluginI::setCustomData(const std::vector& #if defined(LV2_SUPPORT) || defined(VST_NATIVE_SUPPORT) customParams #endif ) { if(_plugin == nullptr) return; #ifdef LV2_SUPPORT if(_plugin->isLV2Plugin()) //now only do it for lv2 plugs { LV2PluginWrapper *lv2Plug = static_cast(_plugin); for(int i = 0; i < instances; ++i) { lv2Plug->setCustomData(handle [i], customParams); } } #endif #ifdef VST_NATIVE_SUPPORT if(_plugin->isVstNativePlugin()) //now only do it for lv2 plugs { VstNativePluginWrapper *vstPlug = static_cast(_plugin); for(int i = 0; i < instances; ++i) { vstPlug->setCustomData(handle [i], customParams); } } #endif } LADSPA_Handle Plugin::instantiate(PluginI *) { LADSPA_Handle h = plugin->instantiate(plugin, MusEGlobal::sampleRate); if(h == nullptr) { fprintf(stderr, "Plugin::instantiate() Error: plugin:%s instantiate failed!\n", plugin->Label); return nullptr; } return h; } //--------------------------------------------------------- // initPluginInstance // return true on error //--------------------------------------------------------- bool PluginI::initPluginInstance(Plugin* plug, int c) { channel = c; if(plug == nullptr) { printf("initPluginInstance: zero plugin\n"); return true; } _plugin = plug; if (_plugin->incReferences(1)==0) return true; #ifdef OSC_SUPPORT _oscif.oscSetPluginI(this); #endif QString inst("-" + QString::number(_plugin->instNo())); _name = _plugin->name() + inst; _label = _plugin->label() + inst; unsigned long ins = plug->inports(); unsigned long outs = plug->outports(); if(outs) { instances = channel / outs; if(instances < 1) instances = 1; } else if(ins) { instances = channel / ins; if(instances < 1) instances = 1; } else instances = 1; handle = new LADSPA_Handle[instances]; for(int i = 0; i < instances; ++i) handle[i]=nullptr; for(int i = 0; i < instances; ++i) { #ifdef PLUGIN_DEBUGIN fprintf(stderr, "PluginI::initPluginInstance instance:%d\n", i); #endif handle[i] = _plugin->instantiate(this); if(handle[i] == nullptr) return true; } unsigned long ports = _plugin->ports(); controlPorts = 0; controlOutPorts = 0; for(unsigned long k = 0; k < ports; ++k) { LADSPA_PortDescriptor pd = _plugin->portd(k); if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) ++controlPorts; else if(pd & LADSPA_PORT_OUTPUT) ++controlOutPorts; } } controls = new Port[controlPorts]; controlsOut = new Port[controlOutPorts]; controlsOutDummy = new Port[controlOutPorts]; unsigned long curPort = 0; unsigned long curOutPort = 0; for(unsigned long k = 0; k < ports; ++k) { LADSPA_PortDescriptor pd = _plugin->portd(k); if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) { controls[curPort].idx = k; double val = _plugin->defaultValue(k); controls[curPort].val = val; controls[curPort].tmpVal = val; controls[curPort].enCtrl = true; for(int i = 0; i < instances; ++i) _plugin->connectPort(handle[i], k, &controls[curPort].val); ++curPort; } else if(pd & LADSPA_PORT_OUTPUT) { const char* pname = _plugin->portName(k); if(pname == QString("latency") || pname == QString("_latency")) { _hasLatencyOutPort = true; _latencyOutPort = curOutPort; } controlsOut[curOutPort].idx = k; controlsOut[curOutPort].val = 0.0; controlsOut[curOutPort].tmpVal = 0.0; controlsOut[curOutPort].enCtrl = false; // Connect only the first instance's output controls. // We don't have a mechanism to display the other instances' outputs. _plugin->connectPort(handle[0], k, &controlsOut[curOutPort].val); // Connect the rest to dummy ports. for(int i = 1; i < instances; ++i) _plugin->connectPort(handle[i], k, &controlsOutDummy[curOutPort].val); ++curOutPort; } } } #ifdef _WIN32 _audioInSilenceBuf = (float *) _aligned_malloc(16, sizeof(float *) * MusEGlobal::segmentSize); if(_audioInSilenceBuf == NULL) { fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioInSilenceBuf _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioInSilenceBuf posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) { _audioInSilenceBuf[q] = MusEGlobal::denormalBias; } } else { memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); } #ifdef _WIN32 _audioOutDummyBuf = (float *) _aligned_malloc(16, sizeof(float *) * MusEGlobal::segmentSize); if(_audioOutDummyBuf == NULL) { fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioOutDummyBuf _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else rv = posix_memalign((void **)&_audioOutDummyBuf, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: PluginI::initPluginInstance: _audioOutDummyBuf posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif activate(); return false; } //--------------------------------------------------------- // connect //--------------------------------------------------------- void PluginI::connect(unsigned long ports, unsigned long offset, float** src, float** dst) { unsigned long port = 0; for (int i = 0; i < instances; ++i) { for (unsigned long k = 0; k < _plugin->ports(); ++k) { if (isAudioIn(k)) { if(port < ports) _plugin->connectPort(handle[i], k, src[port] + offset); else // Connect to an input silence buffer. _plugin->connectPort(handle[i], k, _audioInSilenceBuf + offset); ++port; } } } port = 0; for (int i = 0; i < instances; ++i) { for (unsigned long k = 0; k < _plugin->ports(); ++k) { if (isAudioOut(k)) { if(port < ports) _plugin->connectPort(handle[i], k, dst[port] + offset); else // Connect to a dummy buffer. _plugin->connectPort(handle[i], k, _audioOutDummyBuf + offset); ++port; } } } } //--------------------------------------------------------- // deactivate //--------------------------------------------------------- void PluginI::deactivate() { for (int i = 0; i < instances; ++i) { _plugin->deactivate(handle[i]); _plugin->cleanup(handle[i]); } } //--------------------------------------------------------- // activate //--------------------------------------------------------- void PluginI::activate() { for (int i = 0; i < instances; ++i) _plugin->activate(handle[i]); if (initControlValues) { for (unsigned long i = 0; i < controlPorts; ++i) { controls[i].val = controls[i].tmpVal; } } else { // get initial control values from plugin for (unsigned long i = 0; i < controlPorts; ++i) { controls[i].tmpVal = controls[i].val; } } } //--------------------------------------------------------- // latency //--------------------------------------------------------- float PluginI::latency() const { // Do not report any latency if the plugin is not on. if(!on()) return 0.0; if(cquirks()._overrideReportedLatency) return cquirks()._latencyOverrideValue; if(!hasLatencyOutPort()) return 0.0; return controlsOut[latencyOutPortIndex()].val; } //--------------------------------------------------------- // setControl // set plugin instance controller value by name //--------------------------------------------------------- bool PluginI::setControl(const QString& s, double val) { for (unsigned long i = 0; i < controlPorts; ++i) { if (_plugin->portName(controls[i].idx) == s) { setParam(i, val); return false; } } printf("PluginI:setControl(%s, %f) controller not found\n", s.toLatin1().constData(), val); return true; } //--------------------------------------------------------- // saveConfiguration //--------------------------------------------------------- void PluginI::writeConfiguration(int level, Xml& xml) { if(_plugin->uri().isEmpty()) { xml.tag(level++, "plugin file=\"%s\" label=\"%s\" channel=\"%d\"", Xml::xmlString(_plugin->lib()).toLatin1().constData(), Xml::xmlString(_plugin->label()).toLatin1().constData(), channel); } else { xml.tag(level++, "plugin uri=\"%s\" label=\"%s\" channel=\"%d\"", Xml::xmlString(_plugin->uri()).toLatin1().constData(), Xml::xmlString(_plugin->label()).toLatin1().constData(), channel); } #ifdef LV2_SUPPORT if(_plugin != nullptr && _plugin->isLV2Plugin())//save lv2 plugin state custom data before controls { LV2PluginWrapper *lv2Plug = static_cast(_plugin); //for multi-instance plugins write only first instance's state if(instances > 0) { lv2Plug->writeConfiguration(handle [0], level, xml); } } #endif #ifdef VST_NATIVE_SUPPORT if(_plugin != nullptr && _plugin->isVstNativePlugin())//save vst plugin state custom data before controls { VstNativePluginWrapper *vstPlug = static_cast(_plugin); //for multi-instance plugins write only first instance's state if(instances > 0) { vstPlug->writeConfiguration(handle [0], level, xml); } } #endif for (unsigned long i = 0; i < controlPorts; ++i) { unsigned long idx = controls[i].idx; QString s("control name=\"%1\" val=\"%2\" /"); xml.tag(level, s.arg(Xml::xmlString(_plugin->portName(idx)).toLatin1().constData()).arg(double(controls[i].tmpVal)).toLatin1().constData()); } if (_on == false) xml.intTag(level, "on", _on); _quirks.write(level, xml); if(guiVisible()) xml.intTag(level, "gui", 1); int x, y, w, h; getGeometry(&x, &y, &w, &h); QRect r(x, y, w, h); xml.qrectTag(level, "geometry", r); if (nativeGuiVisible()) xml.intTag(level, "nativegui", 1); getNativeGeometry(&x, &y, &w, &h); QRect nr(x, y, w, h); xml.qrectTag(level, "nativeGeometry", nr); xml.etag(--level, "plugin"); } //--------------------------------------------------------- // loadControl //--------------------------------------------------------- bool PluginI::loadControl(Xml& xml) { QString file; QString label; QString name("mops"); double val = 0.0; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return true; case Xml::TagStart: xml.unknown("PluginI-Control"); break; case Xml::Attribut: if (tag == "name") name = xml.s2(); else if (tag == "val") val = xml.s2().toDouble(); break; case Xml::TagEnd: if (tag == "control") { if(_plugin) { bool found = false; for(unsigned long i = 0; i < controlPorts; ++i) { if(_plugin->portName(controls[i].idx) == name) { controls[i].val = controls[i].tmpVal = val; found = true; } } if(!found) { printf("PluginI:loadControl(%s, %f) controller not found\n", name.toLatin1().constData(), val); return false; } initControlValues = true; } return false; } return true; default: break; } } return true; } //--------------------------------------------------------- // readConfiguration // return true on error //--------------------------------------------------------- bool PluginI::readConfiguration(Xml& xml, bool readPreset) { QString file; QString label; QString uri; //custom params in xml song file , synth tag, that will be passed to new PluginI:setCustomData(Xml &) method //now only lv2host uses them, others simply ignore std::vector accumulatedCustomParams; if (!readPreset) channel = 1; for (;;) { Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case Xml::Error: case Xml::End: return true; case Xml::TagStart: if (!readPreset && _plugin == 0) { _plugin = MusEGlobal::plugins.find(file, uri, label); if (_plugin) { if(initPluginInstance(_plugin, channel)) { _plugin = 0; xml.parse1(); printf("Error initializing plugin instance (%s, %s, %s)\n", file.toLatin1().constData(), uri.toLatin1().constData(), label.toLatin1().constData()); //break; // Don't break - let it read any control tags. } } } if (tag == "control") loadControl(xml); else if (tag == "on") { bool flag = xml.parseInt(); if (!readPreset) _on = flag; } else if (tag == "quirks") { PluginQuirks q; q.read(xml); if (!readPreset) _quirks = q; } else if (tag == "gui") { const bool flag = xml.parseInt(); if (_plugin) showGui(flag); } else if (tag == "nativegui") { // We can't tell OSC to show the native plugin gui // until the parent track is added to the lists. // OSC needs to find the plugin in the track lists. // Use this 'pending' flag so it gets done later. _showNativeGuiPending = xml.parseInt(); } else if (tag == "geometry") { QRect r(readGeometry(xml, tag)); setGeometry(r.x(), r.y(), r.width(), r.height()); } else if (tag == "nativeGeometry") { QRect r(readGeometry(xml, tag)); setNativeGeometry(r.x(), r.y(), r.width(), r.height()); } else if (tag == "customData") { //just place tag contents in accumulatedCustomParams QString customData = xml.parse1(); if(!customData.isEmpty()){ accumulatedCustomParams.push_back(customData); //now process custom data immidiatly //because it MUST be processed before plugin controls //writeConfiguration places custom data before plugin controls values setCustomData(accumulatedCustomParams); accumulatedCustomParams.clear(); } } else xml.unknown("PluginI"); break; case Xml::Attribut: if (tag == "file") { QString s = xml.s2(); if (readPreset) { if (s != plugin()->lib()) { printf("Error: Wrong preset type %s. Type must be a %s\n", s.toLatin1().constData(), plugin()->lib().toLatin1().constData()); return true; } } else { file = s; } } else if (tag == "uri") { QString s = xml.s2(); if (readPreset) { if (s != plugin()->uri()) { printf("Error: Wrong preset uri %s. Uri must be a %s\n", s.toLatin1().constData(), plugin()->uri().toLatin1().constData()); return true; } } else { uri = s; } } else if (tag == "label") { if (!readPreset) label = xml.s2(); } else if (tag == "channel") { if (!readPreset) channel = xml.s2().toInt(); } break; case Xml::TagEnd: if (tag == "plugin") { if (!readPreset && _plugin == 0) { _plugin = MusEGlobal::plugins.find(file, uri, label); if (_plugin == 0) { QMessageBox::warning(0,"Plugin not found!", "Plugin: " + label + " not found, if the project is saved it will be removed from the project"); fprintf(stderr, "Warning: - Plugin not found (%s, %s, %s)\n", file.toLatin1().constData(), uri.toLatin1().constData(), label.toLatin1().constData()); return true; } if (initPluginInstance(_plugin, channel)) { fprintf(stderr, "Error initializing plugin instance (%s, %s, %s)\n", file.toLatin1().constData(), uri.toLatin1().constData(), label.toLatin1().constData()); return true; } } if (_gui) _gui->updateValues(); return false; } return true; default: break; } } return true; } //--------------------------------------------------------- // showGui //--------------------------------------------------------- void PluginI::showGui() { if(_plugin) PluginIBase::showGui(); } void PluginI::showGui(bool flag) { if(_plugin) PluginIBase::showGui(flag); } //--------------------------------------------------------- // showNativeGui //--------------------------------------------------------- void PluginI::showNativeGui() { #ifdef LV2_SUPPORT if(plugin() && plugin()->isLV2Plugin()) { if(((LV2PluginWrapper *)plugin())->nativeGuiVisible(this)) ((LV2PluginWrapper *)plugin())->showNativeGui(this, false); else ((LV2PluginWrapper *)plugin())->showNativeGui(this, true); return; } #endif #ifdef VST_NATIVE_SUPPORT if(plugin() && plugin()->isVstNativePlugin()) { if(((VstNativePluginWrapper *)plugin())->nativeGuiVisible(this)) ((VstNativePluginWrapper *)plugin())->showNativeGui(this, false); else ((VstNativePluginWrapper *)plugin())->showNativeGui(this, true); return; } #endif #ifdef OSC_SUPPORT if (_plugin) { if (_oscif.oscGuiVisible()) _oscif.oscShowGui(false); else _oscif.oscShowGui(true); } #endif _showNativeGuiPending = false; } void PluginI::showNativeGui( bool #if defined(LV2_SUPPORT) || defined(VST_NATIVE_SUPPORT) || defined(OSC_SUPPORT) flag #endif ) { #ifdef LV2_SUPPORT if(plugin() && plugin()->isLV2Plugin()) { ((LV2PluginWrapper *)plugin())->showNativeGui(this, flag); return; } #endif #ifdef VST_NATIVE_SUPPORT if(plugin() && plugin()->isVstNativePlugin()) { ((VstNativePluginWrapper *)plugin())->showNativeGui(this, flag); return; } #endif #ifdef OSC_SUPPORT if(_plugin) { _oscif.oscShowGui(flag); } #endif _showNativeGuiPending = false; } //--------------------------------------------------------- // nativeGuiVisible //--------------------------------------------------------- bool PluginI::nativeGuiVisible() const { #ifdef LV2_SUPPORT if(plugin() && plugin()->isLV2Plugin()) return ((LV2PluginWrapper *)plugin())->nativeGuiVisible(this); #endif #ifdef VST_NATIVE_SUPPORT if(plugin() && plugin()->isVstNativePlugin()) return ((VstNativePluginWrapper *)plugin())->nativeGuiVisible(this); #endif #ifdef OSC_SUPPORT return _oscif.oscGuiVisible(); #endif return false; } //--------------------------------------------------------- // makeGui //--------------------------------------------------------- void PluginIBase::makeGui() { _gui = new MusEGui::PluginGui(this); } //--------------------------------------------------------- // deleteGui //--------------------------------------------------------- void PluginIBase::deleteGui() { if(_gui) { delete _gui; _gui = 0; } } //--------------------------------------------------------- // enableAllControllers //--------------------------------------------------------- void PluginI::enableAllControllers(bool v) { for(unsigned long i = 0; i < controlPorts; ++i) controls[i].enCtrl = v; } //--------------------------------------------------------- // titlePrefix //--------------------------------------------------------- QString PluginI::titlePrefix() const { if (_track) return _track->name() + QString(": "); else return ":"; } //--------------------------------------------------------- // apply // If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- void PluginI::apply(unsigned pos, unsigned long n, unsigned long ports, float** bufIn, float** bufOut, float latency_corr_offset) { const unsigned long syncFrame = MusEGlobal::audio->curSyncFrame(); unsigned long sample = 0; // FIXME Better support for PluginPowerOf2BlockSize, by quantizing the control period times. // For now we treat it like fixed size. const bool usefixedrate = (requiredFeatures() & (PluginFixedBlockSize | PluginPowerOf2BlockSize | PluginCoarseBlockSize)); // Note for dssi-vst this MUST equal audio period. It doesn't like broken-up runs (it stutters), // even with fixed sizes. Could be a Wine + Jack thing, wanting a full Jack buffer's length. // For now, the fixed size is clamped to the audio buffer size. // TODO: We could later add slower processing over several cycles - // so that users can select a small audio period but a larger control period. const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > n) ? n : MusEGlobal::config.minControlProcessPeriod; const unsigned long min_per_mask = min_per-1; // min_per must be power of 2 AutomationType at = AUTO_OFF; CtrlListList* cll = nullptr; ciCtrlList icl_first; if(_track) { // Correction value is negative for correction. latency_corr_offset += _track->getLatencyInfo(false)._sourceCorrectionValue; at = _track->automationType(); cll = _track->controller(); if(_id != -1 && ports != 0) // Don't bother if not 'running'. icl_first = cll->lower_bound(genACnum(_id, 0)); } const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; const unsigned long in_ctrls = _plugin->controlInPorts(); // Special for plugins: Deal with tmpVal. TODO: Get rid of tmpVal, maybe by using the FIFO... for(unsigned long k = 0; k < controlPorts; ++k) controls[k].val = controls[k].tmpVal; int cur_slice = 0; while(sample < n) { unsigned long nsamp = n - sample; const unsigned long slice_frame = pos + sample; // Process automation control values, while also determining the maximum acceptable // size of this run. Further processing, from FIFOs for example, can lower the size // from there, but this section determines where the next highest maximum frame // absolutely needs to be for smooth playback of the controller value stream... // if(ports != 0) // Don't bother if not 'running'. { ciCtrlList icl = icl_first; for(unsigned long k = 0; k < controlPorts; ++k) { CtrlList* cl = (cll && _id != -1 && icl != cll->end()) ? icl->second : nullptr; CtrlInterpolate& ci = controls[k].interp; // Always refresh the interpolate struct at first, since things may have changed. // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && (slice_frame < (unsigned long)ci.sFrame || (ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame)) ) ) { if(cl && _id != -1 && (unsigned long)cl->id() == genACnum(_id, k)) { cl->getInterpolation(slice_frame, no_auto || !controls[k].enCtrl, &ci); if(icl != cll->end()) ++icl; } else { // No matching controller, or end. Just copy the current value into the interpolator. // Keep the current icl iterator, because since they are sorted by frames, // if the IDs didn't match it means we can just let k catch up with icl. ci.sFrame = 0; ci.eFrame = 0; ci.eFrameValid = false; ci.sVal = controls[k].val; ci.eVal = ci.sVal; ci.doInterp = false; ci.eStop = false; } } else { if(ci.eStop && ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. { // Clear the stop condition and set up the interp struct appropriately as an endless value. ci.sFrame = 0; //ci->eFrame; ci.eFrame = 0; ci.eFrameValid = false; ci.sVal = ci.eVal; ci.doInterp = false; ci.eStop = false; } if(cl && cll && icl != cll->end()) ++icl; } if(!usefixedrate && MusEGlobal::audio->isPlaying()) { unsigned long samps = nsamp; if(ci.eFrameValid) samps = (unsigned long)ci.eFrame - slice_frame; if(!ci.doInterp && samps > min_per) { samps &= ~min_per_mask; if((samps & min_per_mask) != 0) samps += min_per; } else samps = min_per; if(samps < nsamp) nsamp = samps; } if(ci.doInterp && cl) controls[k].val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); else controls[k].val = ci.sVal; #ifdef LV2_SUPPORT if(_plugin->isLV2Plugin()) { for(int i = 0; i < instances; ++i) { (reinterpret_cast(_plugin))->setLastStateControls(handle [i], k, true, false, true, 0.0f); } } #endif controls[k].tmpVal = controls[k].val; // Special for plugins: Deal with tmpVal. #ifdef PLUGIN_DEBUGIN_PROCESS printf("PluginI::apply k:%lu sample:%lu frame:%lu nextFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); #endif } } #ifdef PLUGIN_DEBUGIN_PROCESS printf("PluginI::apply sample:%lu nsamp:%lu\n", sample, nsamp); #endif // // Process all control ring buffer items valid for this time period... // bool found = false; unsigned long frame = 0; unsigned long index = 0; unsigned long evframe; // Get all control ring buffer items valid for this time period... while(!_controlFifo.isEmpty()) { const ControlEvent& v = _controlFifo.peek(); // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. evframe = (syncFrame > v.frame + n) ? 0 : v.frame - syncFrame + n; #ifdef PLUGIN_DEBUGIN_PROCESS fprintf(stderr, "PluginI::apply found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", found, evframe, frame, v.frame, v.idx, v.value, v.unique); #endif // Protection. Observed this condition. Why? Supposed to be linear timestamps. if(found && evframe < frame) { fprintf(stderr, "PluginI::apply *** Error: Event out of order: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d syncFrame:%lu nframes:%lu v.frame:%lu\n", evframe, frame, v.idx, v.value, v.unique, syncFrame, n, v.frame); // No choice but to ignore it. _controlFifo.remove(); // Done with the ring buffer's item. Remove it. continue; } if(evframe >= n // Next events are for a later period. || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. || (usefixedrate && found && v.unique && v.idx == index)) // Special for dssi-vst: Fixed rate and must reply to all. break; // _controlFifo.remove(); // Done with the ring buffer's item. Remove it. if(v.idx >= in_ctrls) // Sanity check { _controlFifo.remove(); // Done with the ring buffer's item. Remove it. break; } found = true; frame = evframe; index = v.idx; if(ports == 0) // Don't bother if not 'running'. controls[v.idx].val = controls[v.idx].tmpVal = v.value; // Might as well at least update these. else { CtrlInterpolate* ci = &controls[v.idx].interp; // Tell it to stop the current ramp at this frame, when it does stop, set this value: ci->eFrame = frame; ci->eFrameValid = true; ci->eVal = v.value; ci->eStop = true; } // Need to update the automation value, otherwise it overwrites later with the last automation value. if(_track && _id != -1) _track->setPluginCtrlVal(genACnum(_id, v.idx), v.value); #ifdef LV2_SUPPORT if(v.fromGui) { if(_plugin->isLV2Plugin()) { for(int i = 0; i < instances; ++i) { (reinterpret_cast(_plugin))->setLastStateControls(handle [i], v.idx, true, true, false, v.value); } } } #endif _controlFifo.remove(); // Done with the ring buffer's item. Remove it. } if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. nsamp = frame - sample; if(sample + nsamp > n) // Safety check. nsamp = n - sample; // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. // Note this means it is still possible to get stuck in the top loop (at least for a while). if(nsamp != 0) { if(ports != 0) // Don't bother if not 'running'. { connect(ports, sample, bufIn, bufOut); for(int i = 0; i < instances; ++i) _plugin->apply(handle[i], nsamp, latency_corr_offset); } sample += nsamp; } ++cur_slice; // Slice is done. Moving on to any next slice now... } } //--------------------------------------------------------- // oscConfigure //--------------------------------------------------------- #ifdef OSC_SUPPORT int Plugin::oscConfigure( LADSPA_Handle #if defined(DSSI_SUPPORT) handle #endif , const char* #if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN) key #endif , const char* #if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN) value #endif ) { #ifdef PLUGIN_DEBUGIN printf("Plugin::oscConfigure effect plugin label:%s key:%s value:%s\n", plugin->Label, key, value); #endif #ifdef DSSI_SUPPORT if(!dssi_descr || !dssi_descr->configure) return 0; if (!strncmp(key, DSSI_RESERVED_CONFIGURE_PREFIX, strlen(DSSI_RESERVED_CONFIGURE_PREFIX))) { fprintf(stderr, "Plugin::oscConfigure OSC: UI for plugin '%s' attempted to use reserved configure key \"%s\", ignoring\n", plugin->Label, key); return 0; } char* message = dssi_descr->configure(handle, key, value); if (message) { printf("Plugin::oscConfigure on configure '%s' '%s', plugin '%s' returned error '%s'\n", key, value, plugin->Label, message); free(message); } // also call back on UIs for plugins other than the one // that requested this: // if (n != instance->number && instances[n].uiTarget) { // lo_send(instances[n].uiTarget, // instances[n].ui_osc_configure_path, "ss", key, value); // } #endif // DSSI_SUPPORT return 0; } //--------------------------------------------------------- // oscConfigure //--------------------------------------------------------- int PluginI::oscConfigure( const char * #if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN) key #endif , const char * #if defined(DSSI_SUPPORT) || defined(PLUGIN_DEBUGIN) value #endif ) { if(!_plugin) return 0; // "The host has the option to remember the set of (key,value) // pairs associated with a particular instance, so that if it // wants to restore the "same" instance on another occasion it can // just call configure() on it for each of those pairs and so // restore state without any input from a GUI. Any real-world GUI // host will probably want to do that." #ifdef PLUGIN_DEBUGIN printf("PluginI::oscConfigure effect plugin name:%s label:%s key:%s value:%s\n", _name.toLatin1().constData(), _label.toLatin1().constData(), key, value); #endif #ifdef DSSI_SUPPORT // FIXME: Don't think this is right, should probably do as example shows below. for(int i = 0; i < instances; ++i) _plugin->oscConfigure(handle[i], key, value); #endif // DSSI_SUPPORT return 0; } //--------------------------------------------------------- // oscUpdate //--------------------------------------------------------- int PluginI::oscUpdate() { #ifdef DSSI_SUPPORT // Send project directory. _oscif.oscSendConfigure(DSSI_PROJECT_DIRECTORY_KEY, MusEGlobal::museProject.toLatin1().constData()); // MusEGlobal::song->projectPath() /* DELETETHIS 20 // Send current string configuration parameters. StringParamMap& map = synti->stringParameters(); int i = 0; for(ciStringParamMap r = map.begin(); r != map.end(); ++r) { _oscIF.oscSendConfigure(r->first.c_str(), r->second.c_str()); // Avoid overloading the GUI if there are lots and lots of params. if((i+1) % 50 == 0) usleep(300000); ++i; } // Send current bank and program. unsigned long bank, prog; synti->currentProg(&prog, &bank, 0); _oscIF.oscSendProgram(prog, bank, true); // "true" means "force" */ // FIXME: TESTING FLAM: I have to put a delay because flammer hasn't opened yet. // How to make sure gui is ready? usleep(300000); // Send current control values. for(unsigned long i = 0; i < controlPorts; ++i) { _oscif.oscSendControl(controls[i].idx, controls[i].val, true /*force*/); // Avoid overloading the GUI if there are lots and lots of ports. if((i+1) % 50 == 0) usleep(300000); } #endif // DSSI_SUPPORT return 0; } //--------------------------------------------------------- // oscControl //--------------------------------------------------------- int PluginI::oscControl(unsigned long port, float value) { #ifdef PLUGIN_DEBUGIN printf("PluginI::oscControl received oscControl port:%lu val:%f\n", port, value); #endif if(port >= _plugin->rpIdx.size()) { fprintf(stderr, "PluginI::oscControl: port number:%lu is out of range of index list size:%zd\n", port, _plugin->rpIdx.size()); return 0; } // Convert from DSSI port number to control input port index. unsigned long cport = _plugin->rpIdx[port]; if((int)cport == -1) { fprintf(stderr, "PluginI::oscControl: port number:%lu is not a control input\n", port); return 0; } // Record automation: // Take care of this immediately, because we don't want the silly delay associated with // processing the fifo one-at-a-time in the apply(). // NOTE: With some vsts we don't receive control events until the user RELEASES a control. // So the events all arrive at once when the user releases a control. // That makes this pretty useless... But what the heck... if(_track && _id != -1) { unsigned long id = genACnum(_id, cport); _track->recordAutomation(id, value); } // (From DSSI module). // p3.3.39 Set the DSSI control input port's value. // Observations: With a native DSSI synth like LessTrivialSynth, the native GUI's controls do not change the sound at all // ie. they don't update the DSSI control port values themselves. // Hence in response to the call to this oscControl, sent by the native GUI, it is required to do that here. /// controls[cport].val = value; // DSSI-VST synths however, unlike DSSI synths, DO change their OWN sound in response to their gui controls. // AND this function is called ! // Despite the descrepancy we are STILL required to update the DSSI control port values here // because dssi-vst is WAITING FOR A RESPONSE! (A CHANGE in the control port value). // It will output something like "...4 events expected..." and count that number down as 4 actual control port value CHANGES // are done here in response. Normally it says "...0 events expected..." when MusE is the one doing the DSSI control changes. // TODO: May need FIFOs on each control(!) so that the control changes get sent one per process cycle! // Observed countdown not actually going to zero upon string of changes. // Try this ... // Schedules a timed control change: ControlEvent ce; ce.unique = _plugin->_isDssiVst; // Special for messages from vst gui to host - requires processing every message. ce.fromGui = true; // It came from the plugin's own GUI. ce.idx = cport; ce.value = value; // Don't use timestamp(), because it's circular, which is making it impossible to deal // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. ce.frame = MusEGlobal::audio->curFrame(); if(_controlFifo.put(ce)) fprintf(stderr, "PluginI::oscControl: fifo overflow: in control number:%lu\n", cport); enableController(cport, false); //TODO maybe re-enable the ctrl soon? /* DELETETHIS 12 const DSSI_Descriptor* dssi = synth->dssi; const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; ciMidiCtl2LadspaPort ip = synth->port2MidiCtlMap.find(cport); if(ip != synth->port2MidiCtlMap.end()) { // TODO: TODO: Update midi MusE's midi controller knobs, sliders, boxes etc with a call to the midi port's setHwCtrlState() etc. // But first we need a ladspa2MidiValue() function! ... // // //float val = ladspa2MidiValue(ld, i, ?, ?); } */ return 0; } #endif // OSC_SUPPORT } // namespace MusECore namespace MusEGui { // TODO: We need to use .qrc files to use icons in WhatsThis bubbles. See Qt // Resource System in Qt documentation - ORCAN //const char* presetOpenText = " " // "Click this button to load a saved preset."; static const char* presetOpenText = "Click this button to load a saved preset."; static const char* presetSaveText = "Click this button to save curent parameter " "settings as a preset. You will be prompted for a file name."; static const char* presetBypassText = "Click this button to bypass effect unit"; //--------------------------------------------------------- // PluginGui //--------------------------------------------------------- PluginGui::PluginGui(MusECore::PluginIBase* p) : QMainWindow(nullptr) { gw = nullptr; params = nullptr; paramsOut = nullptr; plugin = p; updateWindowTitle(); QToolBar* tools = addToolBar(tr("File Buttons")); tools->setIconSize(QSize(MusEGlobal::config.iconSize, MusEGlobal::config.iconSize)); QAction* fileOpen = new QAction(*fileopenSVGIcon, tr("Load Preset"), this); connect(fileOpen, &QAction::triggered, [this]() { load(); } ); tools->addAction(fileOpen); QAction* fileSave = new QAction(*filesaveasSVGIcon, tr("Save Preset"), this); connect(fileSave, &QAction::triggered, [this]() { save(); } ); tools->addAction(fileSave); QAction* whatsthis = QWhatsThis::createAction(this); whatsthis->setIcon(*whatsthisSVGIcon); tools->addAction(whatsthis); tools->addSeparator(); onOff = new QAction(*muteSVGIcon, tr("Bypass plugin"), this); onOff->setCheckable(true); onOff->setChecked(!plugin->on()); onOff->setEnabled(plugin->hasBypass()); onOff->setToolTip(tr("Bypass plugin")); connect(onOff, &QAction::toggled, [this](bool v) { bypassToggled(v); } ); tools->addAction(onOff); QAction* settings = new QAction(*settingsSVGIcon, tr("Plugin settings"), this); connect(settings, &QAction::triggered, this, &PluginGui::showSettings); tools->addAction(settings); fileOpen->setWhatsThis(tr(presetOpenText)); onOff->setWhatsThis(tr(presetBypassText)); fileSave->setWhatsThis(tr(presetSaveText)); QString id; id.setNum(plugin->pluginID()); QString name(MusEGlobal::museGlobalShare + QString("/plugins/") + id + QString(".ui")); QFile uifile(name); if (uifile.exists()) constructGUIFromFile(uifile); else constructGUIFromPluginMetadata(); connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), SLOT(heartBeat())); } //--------------------------------------------------------- // PluginGui //--------------------------------------------------------- PluginGui::~PluginGui() { if (gw) delete[] gw; if (params) delete[] params; if (paramsOut) delete[] paramsOut; } //--------------------------------------------------------- // construct GUI from *.ui file //--------------------------------------------------------- void PluginGui::constructGUIFromFile(QFile& uifile) { PluginLoader loader; QFile file(uifile.fileName()); file.open(QFile::ReadOnly); mw = loader.load(&file, this); file.close(); setCentralWidget(mw); QObjectList l = mw->children(); QObject *obj; nobj = 0; QList::iterator it; for (it = l.begin(); it != l.end(); ++it) { obj = *it; QByteArray ba = obj->objectName().toLatin1(); const char* name = ba.constData(); if (*name !='P') continue; unsigned long parameter; int rv = sscanf(name, "P%lu", ¶meter); if(rv != 1) continue; ++nobj; } it = l.begin(); gw = new GuiWidgets[nobj]; nobj = 0; // FIXME: There's no unsigned for gui params. We would need to limit nobj to MAXINT. // FIXME: Our MusEGui::Slider class uses doubles for values, giving some problems with float conversion. DoubleLabel* dl_obj; QCheckBox* cb_obj; QComboBox* combobox_obj; unsigned long int nn; for (it = l.begin(); it != l.end(); ++it) { obj = *it; QByteArray ba = obj->objectName().toLatin1(); const char* name = ba.constData(); if (*name !='P') continue; unsigned long parameter; int rv = sscanf(name, "P%lu", ¶meter); if(rv != 1) continue; // For some reason lambdas need this local copy (nn) of nobj otherwise they fail and crash. nn = nobj; gw[nobj].widget = (QWidget*)obj; gw[nobj].param = parameter; gw[nobj].type = -1; gw[nobj].pressed = false; if (strcmp(obj->metaObject()->className(), "MusEGui::Slider") == 0) { gw[nobj].type = GuiWidgets::SLIDER; Slider* s = static_cast(obj); s->setId(nobj); s->setCursorHoming(true); LADSPA_PortRangeHint range = plugin->range(parameter); double lower = 0.0; // default values double upper = 1.0; double dlower = lower; double dupper = upper; double val = plugin->param(parameter); double dval = val; getPluginConvertedValues(range, lower, upper, dlower, dupper, dval); // TODO //s->setThumbLength(1); //s->setRange(MusEGlobal::config.minSlider, volSliderMax, volSliderStep); //s->setScaleMaxMinor(5); //s->setScale(MusEGlobal::config.minSlider-0.1, 10.0, 6.0, false); //s->setScale(dlower, dupper, 1.0, false); //s->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. //s->setScaleBackBone(false); //s->setFillThumb(false); QFont fnt; fnt.setFamily("Sans"); fnt.setPixelSize(9); //fnt.setStyleStrategy(QFont::PreferBitmap); fnt.setStyleStrategy(QFont::NoAntialias); fnt.setHintingPreference(QFont::PreferVerticalHinting); s->setFont(fnt); s->setStyleSheet(MusECore::font2StyleSheetFull(fnt)); s->setSizeHint(200, 8); for(unsigned long i = 0; i < nobj; i++) { if(gw[i].type == GuiWidgets::DOUBLE_LABEL && gw[i].param == parameter) ((DoubleLabel*)gw[i].widget)->setSlider(s); } connect(s, QOverload::of(&Slider::valueChanged), [=]() { guiParamChanged(nn); } ); connect(s, &Slider::sliderPressed, [this](double v, int i) { guiSliderPressed(v, i); } ); connect(s, &Slider::sliderReleased, [this](double v, int i) { guiSliderReleased(v, i); } ); connect(s, &Slider::sliderRightClicked, [this](const QPoint &p, int i) { guiSliderRightClicked(p, i); } ); } else if (strcmp(obj->metaObject()->className(), "MusEGui::DoubleLabel") == 0) { gw[nobj].type = GuiWidgets::DOUBLE_LABEL; dl_obj = static_cast(obj); dl_obj->setId(nobj); dl_obj->setAlignment(Qt::AlignCenter); for(unsigned long i = 0; i < nobj; i++) { if(gw[i].type == GuiWidgets::SLIDER && gw[i].param == parameter) { dl_obj->setSlider((Slider*)gw[i].widget); break; } } connect((DoubleLabel*)obj, &DoubleLabel::valueChanged, [this, nn]() { guiParamChanged(nn); } ); } else if (strcmp(obj->metaObject()->className(), "QCheckBox") == 0) { gw[nobj].type = GuiWidgets::QCHECKBOX; gw[nobj].widget->setContextMenuPolicy(Qt::CustomContextMenu); cb_obj = static_cast(obj); connect(cb_obj, &QCheckBox::toggled, [this, nn]() { guiParamChanged(nn); } ); connect(cb_obj, &QCheckBox::pressed, [this, nn]() { guiParamPressed(nn); } ); connect(cb_obj, &QCheckBox::released, [this, nn]() { guiParamReleased(nn); } ); connect(cb_obj, &QCheckBox::customContextMenuRequested, [this, nn]() { guiContextMenuReq(nn); } ); } else if (strcmp(obj->metaObject()->className(), "QComboBox") == 0) { gw[nobj].type = GuiWidgets::QCOMBOBOX; gw[nobj].widget->setContextMenuPolicy(Qt::CustomContextMenu); combobox_obj = static_cast(obj); connect(combobox_obj, QOverload::of(&QComboBox::activated), [=]() { guiParamChanged(nn); } ); connect(combobox_obj, &QComboBox::customContextMenuRequested, [this, nn]() { guiContextMenuReq(nn); } ); } else { printf("unknown widget class %s\n", obj->metaObject()->className()); continue; } ++nobj; } updateValues(); // otherwise the GUI won't have valid data } //--------------------------------------------------------- // construct GUI from plugin's meta data //--------------------------------------------------------- void PluginGui::constructGUIFromPluginMetadata() { view = new QScrollArea; view->setWidgetResizable(true); setCentralWidget(view); mw = new QWidget(view); view->setWidget(mw); QVBoxLayout* vbox = new QVBoxLayout(mw); vbox->setSizeConstraint(QLayout::SetMinAndMaxSize); QGroupBox* groupBox = nullptr; QGridLayout* grid = nullptr; // input ports unsigned long paramCnt = plugin->parameters(); params = new GuiParam[paramCnt]; QFontMetrics fm = fontMetrics(); int h = fm.height() + 4; Slider* sl_obj; CheckBox* cb_obj; ComboBoxPI* cmb_obj; QString lastGroup; for (unsigned long i = 0; i < paramCnt; ++i) { if (!i || plugin->portGroup(i) != lastGroup) { if (plugin->portGroup(i).isEmpty()) { grid = new QGridLayout(); grid->setColumnMinimumWidth(0, 100); vbox->addLayout(grid); } else { groupBox = new QGroupBox(plugin->portGroup(i)); grid = new QGridLayout(groupBox); grid->setColumnMinimumWidth(0, 100); groupBox->setLayout(grid); vbox->addWidget(groupBox); } lastGroup = plugin->portGroup(i); } QLabel* label = nullptr; LADSPA_PortRangeHint range = plugin->range(i); double lower = 0.0; // default values double upper = 1.0; double dlower = lower; double dupper = upper; double val = plugin->param(i); double dval = val; params[i].pressed = false; params[i].hint = range.HintDescriptor; params[i].label = nullptr; getPluginConvertedValues(range, lower, upper, dlower, dupper, dval); if (LADSPA_IS_HINT_TOGGLED(range.HintDescriptor) || plugin->ctrlValueType(i) == MusECore::CtrlValueType::VAL_BOOL) { // TODO: There should be special handling for triggers (=button), // but I can't find any plugin with trigger property (kybos) // if (plugin->ctrlIsTrigger(i))... params[i].type = GuiParam::GUI_SWITCH; label = new QLabel(QString(plugin->paramName(i)), nullptr); CheckBox* cb = new CheckBox(mw, i, "param"); cb->setId(i); cb->setChecked(plugin->param(i) != 0.0); cb->setFixedHeight(h); params[i].actuator = cb; grid->addWidget(label, i, 0); grid->addWidget(params[i].actuator, i, 1); cb_obj = (CheckBox*)params[i].actuator; connect(cb_obj, &CheckBox::checkboxPressed, [this](int i) { switchPressed(i); } ); connect(cb_obj, &CheckBox::checkboxReleased, [this](int i) { switchReleased(i); } ); connect(cb_obj, &CheckBox::checkboxRightClicked, [this](const QPoint &p, int i) { ctrlRightClicked(p, i); } ); } else if (plugin->ctrlValueType(i) == MusECore::CtrlValueType::VAL_ENUM && plugin->ctrlEnumValues(i)) { label = new QLabel(QString(plugin->paramName(i)), nullptr); params[i].type = GuiParam::GUI_ENUM; ComboBoxPI* cmb = new ComboBoxPI(mw, i, "enum"); int curItem = -1; int cnt = 0; for (const auto& it : *plugin->ctrlEnumValues(i)) { cmb->addItem(it.second, it.first); if (curItem == -1 && it.first == static_cast(val)) curItem = cnt; cnt++; } cmb->setCurrentIndex(curItem); params[i].actuator = cmb; grid->addWidget(label, i, 0); grid->addWidget(params[i].actuator, i, 1, 1, 2); cmb_obj = static_cast(params[i].actuator); connect(cmb_obj, QOverload::of(&ComboBoxPI::currentIndexChanged), [=]() { comboChanged(i); } ); connect(cmb_obj, &ComboBoxPI::rightClicked, [this](const QPoint &p, int i) { ctrlRightClicked(p, i); } ); } else { label = new QLabel(QString(plugin->paramName(i)), nullptr); params[i].type = GuiParam::GUI_SLIDER; params[i].label = new DoubleLabel(val, lower, upper, nullptr); params[i].label->setFrame(true); params[i].label->setAlignment(Qt::AlignCenter); params[i].label->setId(i); if (plugin->ctrlValueType(i) == MusECore::CtrlValueType::VAL_INT) params[i].label->setPrecision(0); else params[i].label->setPrecision(2); // Let sliders all have different but unique colors // Some prime number magic uint j = i+1; uint c1 = j * 211 % 256; uint c2 = j * j * 137 % 256; uint c3 = j * j * j * 43 % 256; QColor color(c1, c2, c3); Slider* s = new Slider(0, "param", Qt::Horizontal, Slider::InsideHorizontal, 8, color, ScaleDraw::TextHighlightSplitAndShadow); // TODO //s->setThumbLength(1); //s->setRange(MusEGlobal::config.minSlider, volSliderMax, volSliderStep); //s->setScaleMaxMinor(5); //s->setScale(MusEGlobal::config.minSlider-0.1, 10.0, 6.0, false); //s->setScale(dlower, dupper, 1.0, false); //s->setSpecialText(QString('-') + QChar(0x221e)); // The infinity character. //s->setScaleBackBone(false); //s->setFillThumb(false); QFont fnt; fnt.setFamily("Sans"); fnt.setPixelSize(9); fnt.setStyleStrategy(QFont::NoAntialias); fnt.setHintingPreference(QFont::PreferVerticalHinting); s->setFont(fnt); s->setStyleSheet(MusECore::font2StyleSheetFull(fnt)); s->setCursorHoming(true); s->setId(i); s->setSizeHint(200, 8); s->setRange(dlower, dupper); if(LADSPA_IS_HINT_INTEGER(range.HintDescriptor)) s->setStep(1.0); s->setValue(dval); params[i].actuator = s; params[i].label->setSlider(s); label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); params[i].label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); grid->addWidget(label, i, 0); grid->addWidget(params[i].label, i, 1); grid->addWidget(params[i].actuator, i, 2); sl_obj = static_cast(params[i].actuator); connect(sl_obj, QOverload::of(&Slider::valueChanged), [=](double v, int id, int scroll_mode) { sliderChanged(v, id, scroll_mode); } ); connect(params[i].label, &DoubleLabel::valueChanged, [this](double v, int i) { labelChanged(v, i); } ); connect(sl_obj, &Slider::sliderPressed, [this](double v, int i) { sliderPressed(v, i); } ); connect(sl_obj, &Slider::sliderReleased, [this](double v, int i) { sliderReleased(v, i); } ); connect(sl_obj, &Slider::sliderRightClicked, [this](const QPoint &p, int i) { ctrlRightClicked(p, i); } ); } params[i].actuator->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed)); if (plugin->ctrlNotOnGui(i)) { params[i].actuator->setVisible(false); label->setVisible(false); if (params[i].label) params[i].label->setVisible(false); } } paramCnt = plugin->parametersOut(); if (paramCnt > 0) { paramsOut = new GuiParam[paramCnt]; groupBox = new QGroupBox(tr("Output controls")); grid = new QGridLayout(groupBox); // grid->setSpacing(8); groupBox->setLayout(grid); vbox->addWidget(groupBox); for (unsigned long i = 0; i < paramCnt; ++i) { QLabel* label = nullptr; LADSPA_PortRangeHint range = plugin->rangeOut(i); double lower = 0.0; // default values double upper = 32768.0; // Many latency outs have no hints so set this arbitrarily high double dlower = lower; double dupper = upper; double val = plugin->paramOut(i); double dval = val; paramsOut[i].pressed = false; paramsOut[i].hint = range.HintDescriptor; getPluginConvertedValues(range, lower, upper, dlower, dupper, dval); label = new QLabel(QString(plugin->paramOutName(i)), nullptr); paramsOut[i].type = GuiParam::GUI_METER; paramsOut[i].label = new DoubleLabel(val, lower, upper, nullptr); paramsOut[i].label->setFrame(true); paramsOut[i].label->setAlignment(Qt::AlignCenter); paramsOut[i].label->setPrecision(2); paramsOut[i].label->setId(i); Meter::MeterType mType=Meter::LinMeter; //if(LADSPA_IS_HINT_INTEGER(range.HintDescriptor)) if(LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) mType=Meter::DBMeter; Meter* m = new Meter(this, mType, Qt::Horizontal, dlower, dupper, Meter::InsideHorizontal); //, ScaleDraw::TextHighlightNone); m->setRefreshRate(MusEGlobal::config.guiRefresh); m->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum); m->setVal(dval, dval, false); m->setScaleBackBone(false); m->setPrimaryColor(MusEGlobal::config.audioMeterPrimaryColor); QFont fnt; fnt.setFamily("Sans"); fnt.setPixelSize(9); fnt.setStyleStrategy(QFont::NoAntialias); fnt.setHintingPreference(QFont::PreferVerticalHinting); m->setFont(fnt); m->setStyleSheet(MusECore::font2StyleSheetFull(fnt)); paramsOut[i].actuator = m; label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); paramsOut[i].label->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); grid->addWidget(label, paramCnt+i, 0); grid->addWidget(paramsOut[i].label, paramCnt+i, 1); grid->addWidget(paramsOut[i].actuator, paramCnt+i, 2); } } vbox->addStretch(0); mw->setLayout(vbox); } void PluginGui::updateWindowTitle() { if(plugin) setWindowTitle(plugin->titlePrefix() + plugin->name() + (plugin->uri().isEmpty() ? QString() : QString(" : ") + plugin->uri())); } void PluginGui::hideEvent(QHideEvent *e) { if(plugin) plugin->saveGeometry(geometry().x(), geometry().y(), geometry().width(), geometry().height()); e->ignore(); QMainWindow::hideEvent(e); } void PluginGui::showEvent(QShowEvent *e) { int x = 0, y = 0, w = 0, h = 0; if(plugin) plugin->savedGeometry(&x, &y, &w, &h); #ifdef QT_SHOW_POS_BUG_WORKAROUND // Because of the bug, no matter what we must supply a position, // even upon first showing... // Check sane size. if(w == 0) w = sizeHint().width(); if(h == 0) h = sizeHint().height(); // No size hint? Try minimum size. if(w == 0) w = minimumSize().width(); if(h == 0) h = minimumSize().height(); // Fallback. if(w == 0) w = 200; if(h == 0) h = 200; setGeometry(x, y, w, h); #else // If the saved geometry is valid, use it. // Otherwise this is probably the first time showing, // so do not set a geometry - let Qt pick one // (using auto-placement and sizeHint). if(!(x == 0 && y == 0 && w == 0 && h == 0)) { // Check sane size. if(w == 0) w = sizeHint().width(); if(h == 0) h = sizeHint().height(); // No size hint? Try minimum size. if(w == 0) w = minimumSize().width(); if(h == 0) h = minimumSize().height(); // Fallback. if(w == 0) w = 200; if(h == 0) h = 200; setGeometry(x, y, w, h); } #endif // Convenience: If the window was minimized, restore it. if(isMinimized()) setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); e->ignore(); QMainWindow::showEvent(e); } void PluginGui::getPluginConvertedValues(LADSPA_PortRangeHint range, double &lower, double &upper, double &dlower, double &dupper, double &dval) { if (LADSPA_IS_HINT_BOUNDED_BELOW(range.HintDescriptor)) { dlower = lower = range.LowerBound; } if (LADSPA_IS_HINT_BOUNDED_ABOVE(range.HintDescriptor)) { dupper = upper = range.UpperBound; } if (LADSPA_IS_HINT_SAMPLE_RATE(range.HintDescriptor)) { lower *= MusEGlobal::sampleRate; upper *= MusEGlobal::sampleRate; dlower = lower; dupper = upper; } if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) { if (lower == 0.0) lower = 0.001; dlower = MusECore::fast_log10(lower)*20.0; dupper = MusECore::fast_log10(upper)*20.0; dval = MusECore::fast_log10(dval) * 20.0; } } //--------------------------------------------------------- // heartBeat //--------------------------------------------------------- void PluginGui::heartBeat() { updateControls(); } //--------------------------------------------------------- // ctrlPressed //--------------------------------------------------------- void PluginGui::sliderPressed(double /*val*/, int param) { params[param].pressed = true; MusECore::AudioTrack* track = plugin->track(); int id = plugin->id(); if(id != -1) { id = MusECore::genACnum(id, param); double val = ((Slider*)params[param].actuator)->value(); if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) val = muse_db2val(val); else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) val = rint(val); params[param].label->blockSignals(true); params[param].label->setValue(val); params[param].label->blockSignals(false); if(track) { track->startAutoRecord(id, val); track->setPluginCtrlVal(id, val); } } plugin->enableController(param, false); } //--------------------------------------------------------- // ctrlReleased //--------------------------------------------------------- void PluginGui::sliderReleased(double /*val*/, int param) { MusECore::AudioTrack* track = plugin->track(); int id = plugin->id(); if(track && id != -1) { id = MusECore::genACnum(id, param); double val = ((Slider*)params[param].actuator)->value(); if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) val = muse_db2val(val); else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) val = rint(val); track->stopAutoRecord(id, val); } params[param].pressed = false; } //--------------------------------------------------------- // ctrlRightClicked //--------------------------------------------------------- void PluginGui::ctrlRightClicked(const QPoint &p, int param) { int id = plugin->id(); if(id != -1) MusEGlobal::song->execAutomationCtlPopup(plugin->track(), p, MusECore::genACnum(id, param)); } //--------------------------------------------------------- // switchPressed //--------------------------------------------------------- void PluginGui::switchPressed(int param) { params[param].pressed = true; MusECore::AudioTrack* track = plugin->track(); int id = plugin->id(); if (track && id != -1) { id = MusECore::genACnum(id, param); float val = (float)((CheckBox*)params[param].actuator)->isChecked(); track->startAutoRecord(id, val); track->setPluginCtrlVal(id, val); } plugin->enableController(param, false); } //--------------------------------------------------------- // switchReleased //--------------------------------------------------------- void PluginGui::switchReleased(int param) { MusECore::AutomationType at = MusECore::AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); if(track) at = track->automationType(); // don't enable controller until transport stopped. if ((at == MusECore::AUTO_OFF) || (at == MusECore::AUTO_TOUCH && !MusEGlobal::audio->isPlaying()) ) plugin->enableController(param, true); params[param].pressed = false; } //--------------------------------------------------------- // sliderChanged //--------------------------------------------------------- void PluginGui::sliderChanged(double val, int param, int scrollMode) { MusECore::AudioTrack* track = plugin->track(); if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) val = muse_db2val(val); else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) val = rint(val); params[param].label->blockSignals(true); params[param].label->setValue(val); params[param].label->blockSignals(false); int id = plugin->id(); if(track && id != -1) { id = MusECore::genACnum(id, param); // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. // ScrDirect mode is one-time only on press with modifier. if(scrollMode != SliderBase::ScrDirect) track->recordAutomation(id, val); } plugin->setParam(param, val); // Schedules a timed control change. plugin->enableController(param, false); } //--------------------------------------------------------- // labelChanged //--------------------------------------------------------- void PluginGui::labelChanged(double val, int param) { MusECore::AudioTrack* track = plugin->track(); double dval = val; if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) dval = MusECore::fast_log10(val) * 20.0; else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) dval = rint(val); params[param].actuator->blockSignals(true); ((Slider*)params[param].actuator)->setValue(dval); params[param].actuator->blockSignals(false); int id = plugin->id(); if(track && id != -1) { id = MusECore::genACnum(id, param); track->startAutoRecord(id, val); } plugin->setParam(param, val); // Schedules a timed control change. plugin->enableController(param, false); } //--------------------------------------------------------- // comboChanged //--------------------------------------------------------- void PluginGui::comboChanged(unsigned long param) { MusECore::AudioTrack* track = plugin->track(); ComboBoxPI *c = static_cast(params[param].actuator); double val = rint( c->currentData().toDouble() ); int id = plugin->id(); if(track && id != -1) { id = MusECore::genACnum(id, param); track->startAutoRecord(id, val); } plugin->setParam(param, val); // Schedules a timed control change. plugin->enableController(param, false); } //--------------------------------------------------------- // load //--------------------------------------------------------- void PluginGui::load() { QString s("presets/plugins/"); s += plugin->pluginLabel(); s += "/"; QString fn = getOpenFileName(s, MusEGlobal::preset_file_pattern, this, tr("MusE: load preset"), nullptr); if (fn.isEmpty()) return; bool popenFlag; FILE* f = fileOpen(this, fn, QString(".pre"), "r", popenFlag, true); if (f == nullptr) return; MusECore::Xml xml(f); int mode = 0; for (;;) { MusECore::Xml::Token token = xml.parse(); QString tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (mode == 0 && tag == "muse") mode = 1; else if (mode == 1 && tag == "plugin") { if(plugin->readConfiguration(xml, true)) { QMessageBox::critical(this, QString("MusE"), tr("Error reading preset. Might not be right type for this plugin")); goto ende; } mode = 0; } else xml.unknown("PluginGui"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (!mode && tag == "muse") { plugin->updateControllers(); goto ende; } default: break; } } ende: if (popenFlag) pclose(f); else fclose(f); } //--------------------------------------------------------- // save //--------------------------------------------------------- void PluginGui::save() { QString s("presets/plugins/"); s += plugin->pluginLabel(); s += "/"; QString fn = getSaveFileName(s, MusEGlobal::preset_file_save_pattern, this, tr("MusE: Save preset")); if (fn.isEmpty()) return; bool popenFlag; FILE* f = fileOpen(this, fn, QString(".pre"), "w", popenFlag, false, true); if (f == nullptr) return; MusECore::Xml xml(f); xml.header(); xml.tag(0, "muse version=\"1.0\""); plugin->writeConfiguration(1, xml); xml.tag(1, "/muse"); if (popenFlag) pclose(f); else fclose(f); } //--------------------------------------------------------- // bypassToggled //--------------------------------------------------------- void PluginGui::bypassToggled(bool val) { updateWindowTitle(); plugin->setOn(!val); MusEGlobal::song->update(SC_ROUTE); } void PluginGui::showSettings() { PluginSettings settingsDialog(plugin, MusEGlobal::config.noPluginScaling, this); settingsDialog.setWindowTitle(tr("Plugin Settings")); settingsDialog.exec(); } //--------------------------------------------------------- // setOn //--------------------------------------------------------- void PluginGui::setOn(bool val) { updateWindowTitle(); onOff->blockSignals(true); onOff->setChecked(!val); onOff->blockSignals(false); } //--------------------------------------------------------- // updateValues //--------------------------------------------------------- void PluginGui::updateValues() { if (params) { for (unsigned long i = 0; i < plugin->parameters(); ++i) { GuiParam* gp = ¶ms[i]; if (gp->type == GuiParam::GUI_SLIDER) { double lv = plugin->param(i); double sv = lv; if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint)) sv = MusECore::fast_log10(lv) * 20.0; else if (LADSPA_IS_HINT_INTEGER(params[i].hint)) { sv = rint(lv); lv = sv; } gp->label->blockSignals(true); gp->actuator->blockSignals(true); gp->label->setValue(lv); ((Slider*)(gp->actuator))->setValue(sv); gp->label->blockSignals(false); gp->actuator->blockSignals(false); } else if (gp->type == GuiParam::GUI_SWITCH) { gp->actuator->blockSignals(true); ((CheckBox*)(gp->actuator))->setChecked(int(plugin->param(i))); gp->actuator->blockSignals(false); } else if (gp->type == GuiParam::GUI_ENUM) { float sv = static_cast(plugin->param(i)); ComboBoxPI *c = static_cast(gp->actuator); int idx = c->findData(sv); gp->actuator->blockSignals(true); c->setCurrentIndex(idx); gp->actuator->blockSignals(false); } } } else if (gw) { for (unsigned long i = 0; i < nobj; ++i) { QWidget* widget = gw[i].widget; int type = gw[i].type; unsigned long param = gw[i].param; double val = plugin->param(param); widget->blockSignals(true); switch(type) { case GuiWidgets::SLIDER: ((Slider*)widget)->setValue(val); // Note conversion to double break; case GuiWidgets::DOUBLE_LABEL: ((DoubleLabel*)widget)->setValue(val); // Note conversion to double break; case GuiWidgets::QCHECKBOX: ((QCheckBox*)widget)->setChecked(int(val)); break; case GuiWidgets::QCOMBOBOX: ((QComboBox*)widget)->setCurrentIndex(int(val)); break; } widget->blockSignals(false); } } } //--------------------------------------------------------- // updateControls //--------------------------------------------------------- void PluginGui::updateControls() { if (!plugin->track() || plugin->id() == -1) return; // update outputs if (paramsOut) { for (unsigned long i = 0; i < plugin->parametersOut(); ++i) { GuiParam* gp = ¶msOut[i]; if (gp->type == GuiParam::GUI_METER) { double lv = plugin->paramOut(i); double sv = lv; if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint)) sv = MusECore::fast_log10(lv) * 20.0; else if (LADSPA_IS_HINT_INTEGER(params[i].hint)) { sv = rint(lv); lv = sv; } ((Meter*)(gp->actuator))->setVal(sv, sv, false); gp->label->setValue(lv); } } } if (params) { for (unsigned long i = 0; i < plugin->parameters(); ++i) { GuiParam* gp = ¶ms[i]; if(gp->pressed) // Inhibit the controller stream if control is currently pressed. continue; double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), i), MusEGlobal::audio->curFramePos(), !MusEGlobal::automation || plugin->track()->automationType() == MusECore::AUTO_OFF || !plugin->controllerEnabled(i)); if (gp->type == GuiParam::GUI_SLIDER) { { double sv = v; if (LADSPA_IS_HINT_LOGARITHMIC(params[i].hint)) sv = MusECore::fast_log10(v) * 20.0; else if (LADSPA_IS_HINT_INTEGER(params[i].hint)) { sv = rint(v); v = sv; } if(((Slider*)(gp->actuator))->value() != sv) { gp->label->blockSignals(true); gp->actuator->blockSignals(true); ((Slider*)(gp->actuator))->setValue(sv); gp->label->setValue(v); gp->actuator->blockSignals(false); gp->label->blockSignals(false); } } } else if (gp->type == GuiParam::GUI_SWITCH) { { bool b = (int)v; if(((CheckBox*)(gp->actuator))->isChecked() != b) { gp->actuator->blockSignals(true); ((CheckBox*)(gp->actuator))->setChecked(b); gp->actuator->blockSignals(false); } } } else if (gp->type == GuiParam::GUI_ENUM) { { float sv = static_cast(v); ComboBoxPI *c = static_cast(gp->actuator); if (c->currentData().toFloat() != sv) { int idx = c->findData(sv); gp->actuator->blockSignals(true); c->setCurrentIndex(idx); gp->actuator->blockSignals(false); } } } } } else if (gw) { for (unsigned long i = 0; i < nobj; ++i) { if(gw[i].pressed) // Inhibit the controller stream if control is currently pressed. continue; QWidget* widget = gw[i].widget; int type = gw[i].type; unsigned long param = gw[i].param; double v = plugin->track()->controller()->value(MusECore::genACnum(plugin->id(), param), MusEGlobal::audio->curFramePos(), !MusEGlobal::automation || plugin->track()->automationType() == MusECore::AUTO_OFF || !plugin->controllerEnabled(param)); widget->blockSignals(true); switch(type) { case GuiWidgets::SLIDER: { if(((Slider*)widget)->value() != v) ((Slider*)widget)->setValue(v); } break; case GuiWidgets::DOUBLE_LABEL: { if(((DoubleLabel*)widget)->value() != v) ((DoubleLabel*)widget)->setValue(v); } break; case GuiWidgets::QCHECKBOX: { bool b = (bool)v; if(((QCheckBox*)widget)->isChecked() != b) ((QCheckBox*)widget)->setChecked(b); } break; case GuiWidgets::QCOMBOBOX: { int n = (int)v; if(((QComboBox*)widget)->currentIndex() != n) ((QComboBox*)widget)->setCurrentIndex(n); } break; } widget->blockSignals(false); } } } //--------------------------------------------------------- // guiParamChanged //--------------------------------------------------------- void PluginGui::guiParamChanged(unsigned long int idx) { QWidget* w = gw[idx].widget; unsigned long param = gw[idx].param; int type = gw[idx].type; MusECore::AudioTrack* track = plugin->track(); double val = 0.0; bool ignoreRecAutomation = false; switch(type) { case GuiWidgets::SLIDER: val = ((Slider*)w)->value(); // Hack: Be sure to ignore in ScrDirect mode since we get both pressed AND changed signals. // ScrDirect mode is one-time only on press with modifier. if(((Slider*)w)->scrollMode() == Slider::ScrDirect) ignoreRecAutomation = true; break; case GuiWidgets::DOUBLE_LABEL: val = ((DoubleLabel*)w)->value(); break; case GuiWidgets::QCHECKBOX: val = double(((QCheckBox*)w)->isChecked()); break; case GuiWidgets::QCOMBOBOX: val = double(((QComboBox*)w)->currentIndex()); break; } for (unsigned long i = 0; i < nobj; ++i) { QWidget* widget = gw[i].widget; if (widget == w || param != gw[i].param) continue; int type = gw[i].type; widget->blockSignals(true); switch(type) { case GuiWidgets::SLIDER: ((Slider*)widget)->setValue(val); break; case GuiWidgets::DOUBLE_LABEL: ((DoubleLabel*)widget)->setValue(val); break; case GuiWidgets::QCHECKBOX: ((QCheckBox*)widget)->setChecked(int(val)); break; case GuiWidgets::QCOMBOBOX: ((QComboBox*)widget)->setCurrentIndex(int(val)); break; } widget->blockSignals(false); } int id = plugin->id(); if(track && id != -1) { id = MusECore::genACnum(id, param); switch(type) { case GuiWidgets::DOUBLE_LABEL: case GuiWidgets::QCHECKBOX: track->startAutoRecord(id, val); break; default: if(!ignoreRecAutomation) track->recordAutomation(id, val); break; } } plugin->setParam(param, val); // Schedules a timed control change. plugin->enableController(param, false); } //--------------------------------------------------------- // guiParamPressed //--------------------------------------------------------- void PluginGui::guiParamPressed(unsigned long int idx) { gw[idx].pressed = true; unsigned long param = gw[idx].param; plugin->enableController(param, false); //MusECore::AudioTrack* track = plugin->track(); //int id = plugin->id(); //if(!track || id == -1) // return; //id = MusECore::genACnum(id, param); // NOTE: For this to be of any use, the freeverb gui 2142.ui // would have to be used, and changed to use CheckBox and ComboBox // instead of QCheckBox and QComboBox, since both of those would // need customization (Ex. QCheckBox doesn't check on click). RECHECK: Qt4 it does? /* switch(type) { case GuiWidgets::QCHECKBOX: double val = (double)((CheckBox*)w)->isChecked(); track->startAutoRecord(id, val); break; case GuiWidgets::QCOMBOBOX: double val = (double)((ComboBox*)w)->currentIndex(); track->startAutoRecord(id, val); break; } */ } //--------------------------------------------------------- // guiParamReleased //--------------------------------------------------------- void PluginGui::guiParamReleased(unsigned long int idx) { unsigned long param = gw[idx].param; int type = gw[idx].type; MusECore::AutomationType at = MusECore::AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); if(track) at = track->automationType(); // Special for switch - don't enable controller until transport stopped. if ((at == MusECore::AUTO_OFF) || (at == MusECore::AUTO_TOUCH && (type != GuiWidgets::QCHECKBOX || !MusEGlobal::audio->isPlaying()) ) ) plugin->enableController(param, true); //int id = plugin->id(); //if(!track || id == -1) // return; //id = MusECore::genACnum(id, param); // NOTE: For this to be of any use, the freeverb gui 2142.ui // would have to be used, and changed to use CheckBox and ComboBox // instead of QCheckBox and QComboBox, since both of those would // need customization (Ex. QCheckBox doesn't check on click). // RECHECK Qt4 it does? /* switch(type) { case GuiWidgets::QCHECKBOX: double val = (double)((CheckBox*)w)->isChecked(); track->stopAutoRecord(id, param); break; case GuiWidgets::QCOMBOBOX: double val = (double)((ComboBox*)w)->currentIndex(); track->stopAutoRecord(id, param); break; } */ gw[idx].pressed = false; } //--------------------------------------------------------- // guiSliderPressed //--------------------------------------------------------- void PluginGui::guiSliderPressed(double /*val*/, unsigned long int idx) { gw[idx].pressed = true; unsigned long param = gw[idx].param; QWidget *w = gw[idx].widget; MusECore::AudioTrack* track = plugin->track(); int id = plugin->id(); if(track && id != -1) { id = MusECore::genACnum(id, param); double val = ((Slider*)w)->value(); track->startAutoRecord(id, val); // Needed so that paging a slider updates a label or other buddy control. for (unsigned long i = 0; i < nobj; ++i) { QWidget* widget = gw[i].widget; if (widget == w || param != gw[i].param) continue; int type = gw[i].type; widget->blockSignals(true); switch(type) { case GuiWidgets::SLIDER: ((Slider*)widget)->setValue(val); break; case GuiWidgets::DOUBLE_LABEL: ((DoubleLabel*)widget)->setValue(val); break; case GuiWidgets::QCHECKBOX: ((QCheckBox*)widget)->setChecked(int(val)); break; case GuiWidgets::QCOMBOBOX: ((QComboBox*)widget)->setCurrentIndex(int(val)); break; } widget->blockSignals(false); } track->setPluginCtrlVal(id, val); } plugin->enableController(param, false); } //--------------------------------------------------------- // guiSliderReleased //--------------------------------------------------------- void PluginGui::guiSliderReleased(double /*val*/, unsigned long int idx) { int param = gw[idx].param; QWidget *w = gw[idx].widget; MusECore::AutomationType at = MusECore::AUTO_OFF; MusECore::AudioTrack* track = plugin->track(); if(track) at = track->automationType(); int id = plugin->id(); if(track && id != -1) { id = MusECore::genACnum(id, param); double val = ((Slider*)w)->value(); track->stopAutoRecord(id, val); } if (at == MusECore::AUTO_OFF || at == MusECore::AUTO_TOUCH) plugin->enableController(param, true); gw[idx].pressed = false; } //--------------------------------------------------------- // guiSliderRightClicked //--------------------------------------------------------- void PluginGui::guiSliderRightClicked(const QPoint &p, unsigned long int idx) { int param = gw[idx].param; int id = plugin->id(); if(id != -1) MusEGlobal::song->execAutomationCtlPopup(plugin->track(), p, MusECore::genACnum(id, param)); } //--------------------------------------------------------- // guiContextMenuReq //--------------------------------------------------------- void PluginGui::guiContextMenuReq(unsigned long int idx) { guiSliderRightClicked(QCursor().pos(), idx); } //--------------------------------------------------------- // PluginLoader //--------------------------------------------------------- QWidget* PluginLoader::createWidget(const QString & className, QWidget * parent, const QString & name) { if(className == QString("MusEGui::DoubleLabel")) return new DoubleLabel(parent, name.toLatin1().constData()); if(className == QString("MusEGui::Slider")) return new Slider(parent, name.toLatin1().constData(), Qt::Horizontal, Slider::InsideHorizontal, 8, QColor(), ScaleDraw::TextHighlightSplitAndShadow); return QUiLoader::createWidget(className, parent, name); } } // namespace MusEGui namespace MusEGlobal { static void writePluginGroupNames(int level, MusECore::Xml& xml) { xml.tag(level++, "group_names"); for (QList::iterator it=plugin_group_names.begin(); it!=plugin_group_names.end(); it++) xml.strTag(level, "name", *it); xml.etag(--level, "group_names"); } static void writePluginGroupMap(int level, MusECore::Xml& xml) { using MusECore::PluginGroups; xml.tag(level++, "group_map"); for (PluginGroups::iterator it=plugin_groups.begin(); it!=plugin_groups.end(); it++) if (!it.value().empty()) { xml.tag(level++, "entry"); xml.strTag(level, "lib", it.key().first); xml.strTag(level, "label", it.key().second); for (QSet::iterator it2=it.value().begin(); it2!=it.value().end(); it2++) xml.intTag(level, "group", *it2); xml.etag(--level, "entry"); } xml.etag(--level, "group_map"); } void writePluginGroupConfiguration(int level, MusECore::Xml& xml) { xml.tag(level++, "plugin_groups"); writePluginGroupNames(level, xml); writePluginGroupMap(level, xml); xml.etag(--level, "plugin_groups"); } static void readPluginGroupNames(MusECore::Xml& xml) { plugin_group_names.clear(); for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag=="name") plugin_group_names.append(xml.parse1()); else xml.unknown("readPluginGroupNames"); break; case MusECore::Xml::TagEnd: if (tag == "group_names") return; default: break; } } } static void readPluginGroupMap(MusECore::Xml& xml) { plugin_groups.clear(); for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag=="entry") { QString lib; QString label; QSet groups; bool read_lib=false, read_label=false; for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag=="lib") { lib=xml.parse1(); read_lib=true; } else if (tag=="label") { label=xml.parse1(); read_label=true; } else if (tag=="group") groups.insert(xml.parseInt()); else xml.unknown("readPluginGroupMap"); break; case MusECore::Xml::TagEnd: if (tag == "entry") goto done_reading_entry; default: break; } } done_reading_entry: if (read_lib && read_label) plugin_groups.get(lib,label)=groups; else fprintf(stderr,"ERROR: plugin group map entry without lib or label!\n"); } else xml.unknown("readPluginGroupMap"); break; case MusECore::Xml::TagEnd: if (tag == "group_map") return; default: break; } } } void readPluginGroupConfiguration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag=="group_names") readPluginGroupNames(xml); else if (tag=="group_map") readPluginGroupMap(xml); else xml.unknown("readPluginGroupConfiguration"); break; case MusECore::Xml::TagEnd: if (tag == "plugin_groups") return; default: break; } } } } // namespace MusEGlobal muse-4.0.0/src/muse/plugin.h000066400000000000000000000700711404105311000156650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: plugin.h,v 1.9.2.13 2009/12/06 01:25:21 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PLUGIN_H__ #define __PLUGIN_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include "globaldefs.h" #include "ctrl.h" #include "controlfifo.h" #include "config.h" #ifdef OSC_SUPPORT #include "osc.h" #endif #ifdef DSSI_SUPPORT #include #endif // BUG I have filed a Qt bug report 64773. If we do NOT // set a position on a new QWidget, Qt seems to take control // of the position management such that it will NOT accept // any new position after that! It seems to decide to // remain in 'auto-placement' mode forever. // If the bug is ever fixed by Qt, this define should then // become version-sensitive. 2017/11/28 Tim. #define QT_SHOW_POS_BUG_WORKAROUND 1; // Forward declarations: class QScrollArea; class QShowEvent; class QHideEvent; class QAction; class QSpinBox; class QToolButton; namespace MusEGui { class PluginGui; class DoubleLabel; } namespace MusEPlugin { class PluginScanInfoStruct; } namespace MusECore { class AudioTrack; class Xml; class PluginI; //--------------------------------------------------------- // Plugin //--------------------------------------------------------- class Plugin { protected: friend class PluginI; void* _handle; int _references; int _instNo; QFileInfo fi; // Universal resource identifier, for plugins that use it (LV2). // If this exists, it should be used INSTEAD of file info, for comparison etc. // Never rely on the file info if a uri exists. QString _uri; LADSPA_Descriptor_Function ladspa; const LADSPA_Descriptor *plugin; unsigned long _uniqueID; QString _label; QString _name; QString _maker; QString _copyright; bool _isDssiSynth; bool _isDssi; bool _isLV2Synth; bool _isLV2Plugin; // Hack: Special flag required. bool _isDssiVst; bool _isVstNativeSynth; bool _isVstNativePlugin; #ifdef DSSI_SUPPORT const DSSI_Descriptor* dssi_descr; #endif unsigned long _portCount; unsigned long _inports; unsigned long _outports; unsigned long _controlInPorts; unsigned long _controlOutPorts; std::vector rpIdx; // Port number to control input index. Item is -1 if it's not a control input. bool _usesTimePosition; PluginFeatures_t _requiredFeatures; public: Plugin() {} //empty constructor for LV2PluginWrapper Plugin(QFileInfo* f, const LADSPA_Descriptor* d, const QString& uri, bool isDssi = false, bool isDssiSynth = false, bool isDssiVst = false, PluginFeatures_t reqFeatures = PluginNoFeatures); Plugin(const MusEPlugin::PluginScanInfoStruct&); virtual ~Plugin(); virtual PluginFeatures_t requiredFeatures() const { return _requiredFeatures; } QString uri() const { return _uri; } virtual QString label() const { return _label; } QString name() const { return _name; } unsigned long id() const { return _uniqueID; } QString maker() const { return _maker; } QString copyright() const { return _copyright; } QString lib(bool complete = true) const { return complete ? fi.completeBaseName() : fi.baseName(); } // ddskrjo const QString dirPath(bool complete = true) const { return complete ? fi.absolutePath() : fi.path(); } QString filePath() const { return fi.filePath(); } QString fileName() const { return fi.fileName(); } int references() const { return _references; } virtual int incReferences(int); int instNo() { return _instNo++; } bool isDssiPlugin() const { return _isDssi; } bool isDssiSynth() const { return _isDssiSynth; } inline bool isLV2Plugin() const { return _isLV2Plugin; } //inline it to use in RT audio thread bool isLV2Synth() const { return _isLV2Synth; } inline bool isVstNativePlugin() const { return _isVstNativePlugin; } //inline it to use in RT audio thread bool isVstNativeSynth() const { return _isVstNativeSynth; } virtual LADSPA_Handle instantiate(PluginI *); virtual void activate(LADSPA_Handle handle) { if (plugin && plugin->activate) plugin->activate(handle); } virtual void deactivate(LADSPA_Handle handle) { if (plugin && plugin->deactivate) plugin->deactivate(handle); } virtual void cleanup(LADSPA_Handle handle) { if (plugin && plugin->cleanup) plugin->cleanup(handle); } virtual void connectPort(LADSPA_Handle handle, unsigned long port, float* value) { if(plugin) plugin->connect_port(handle, port, value); } virtual void apply(LADSPA_Handle handle, unsigned long n, float /*latency_corr*/ = 0.0f) { if(plugin) plugin->run(handle, n); } #ifdef OSC_SUPPORT int oscConfigure(LADSPA_Handle handle, const char* key, const char* value); #endif unsigned long ports() { return _portCount; } virtual LADSPA_PortDescriptor portd(unsigned long k) const { return plugin ? plugin->PortDescriptors[k] : 0; } virtual LADSPA_PortRangeHint range(unsigned long i) { // FIXME: //return plugin ? plugin->PortRangeHints[i] : 0; DELETETHIS return plugin->PortRangeHints[i]; } virtual double defaultValue(unsigned long port) const; virtual void range(unsigned long i, float*, float*) const; virtual CtrlValueType ctrlValueType(unsigned long i) const; virtual CtrlList::Mode ctrlMode(unsigned long i) const; virtual CtrlEnumValues* ctrlEnumValues ( unsigned long ) const; virtual const char* portName(unsigned long i) { return plugin ? plugin->PortNames[i] : 0; } bool usesTimePosition() const { return _usesTimePosition; } unsigned long inports() const { return _inports; } unsigned long outports() const { return _outports; } unsigned long controlInPorts() const { return _controlInPorts; } unsigned long controlOutPorts() const { return _controlOutPorts; } const std::vector* getRpIdx() { return &rpIdx; } }; typedef std::list::iterator iPlugin; typedef std::list::const_iterator ciPlugin; class PluginGroups : public QMap< QPair, QSet > { public: QSet& get(QString a, QString b) { return (*this)[(QPair(a,b))]; } QSet& get(const Plugin *p) { return (*this)[(QPair(p->uri().isEmpty() ? p->lib() : p->uri(), p->label()))]; } void shift_left(int first, int last); void shift_right(int first, int last); void erase(int index); private: void replace_group(int old, int now); }; //--------------------------------------------------------- // PluginList //--------------------------------------------------------- class PluginList : public std::list { public: void add(QFileInfo* fi, const LADSPA_Descriptor* d, const QString& uri, bool isDssi = false, bool isDssiSynth = false, bool isDssiVst = false, PluginFeatures_t reqFeatures = PluginNoFeatures) { push_back(new Plugin(fi, d, uri, isDssi, isDssiSynth, isDssiVst, reqFeatures)); } void add(const MusEPlugin::PluginScanInfoStruct& scan_info) { push_back(new Plugin(scan_info)); } // Each argument optional, can be empty. // If uri is not empty, the search is based solely on it, the other arguments are ignored. Plugin* find(const QString& file, const QString& uri, const QString& label) const; PluginList() {} }; //--------------------------------------------------------- // Port //--------------------------------------------------------- struct Port { unsigned long idx; // NOTE: These values represent the lowest level of control value storage. // The choice of float or double depends on the underlying system using this struct. // For example plugins and synthesizers usually use floats to represent control values // and they are directly pointed to this float, while our own track controls // (volume, pan etc.) take advantage of the double precision. // Double precision is preferred if possible because above this lowest level all other // controller usage is in double precision. Thus our very own track controllers are // perfectly matched double precision throughout the system. union { float val; double dval; }; union { float tmpVal; // TODO Try once again and for all to remove this, was it really required? //double dtmpVal; // Not used, should not be required. }; bool enCtrl; // Enable controller stream. CtrlInterpolate interp; }; //--------------------------------------------------------- // PluginQuirks // An ugly but necessary class due to differences in plugin behaviour. //--------------------------------------------------------- class PluginQuirks { public: enum NatUISCaling {GLOBAL, ON, OFF}; // Whether the LV2 'speed' timePosition property switches to 0.0 in stop mode, or remains // fixed at 1.0 regardless of play or stop mode. Fixes plugins like TAL NoiseMak3r // stuck repeating small modulator segment at speed = 0.0. bool _fixedSpeed; // If the plugin uses our transport (ex. as an LV2 timePosition), sets whether the timePosition // affects actual audio latency (not just say, beat or sequencer or arpeggiator latency). // Example: LV2 Example Metronome. bool _transportAffectsAudioLatency; // Override the plugin's reported latency. Some plugins get it wrong or not quite right. bool _overrideReportedLatency; // Value to override the reported latency. int _latencyOverrideValue; PluginQuirks() : _fixedSpeed(false), _transportAffectsAudioLatency(false), _overrideReportedLatency(false), _latencyOverrideValue(0), _fixNativeUIScaling(NatUISCaling::GLOBAL) { } void write(int level, Xml& xml) const; // Return true on error. bool read(Xml& xml); bool fixNativeUIScaling() const; void setFixNativeUIScaling(NatUISCaling fixScaling) { _fixNativeUIScaling = fixScaling; }; NatUISCaling getFixNativeUIScaling() const { return _fixNativeUIScaling; }; private: // Reverse scaling of native UI windows on HiDPI NatUISCaling _fixNativeUIScaling; }; typedef enum { PROP_NONE = -1, PROP_INT = 0, PROP_LONG, PROP_FLOAT, PROP_DOUBLE, PROP_BOOL, PROP_STRING, PROP_PATH } PropType; //--------------------------------------------------------- // PluginIBase //--------------------------------------------------------- class PluginIBase { protected: ControlFifo _controlFifo; MusEGui::PluginGui* _gui; QRect _guiGeometry; QRect _nativeGuiGeometry; PluginQuirks _quirks; void makeGui(); public: PluginIBase(); virtual ~PluginIBase(); virtual PluginFeatures_t requiredFeatures() const = 0; virtual bool hasBypass() const = 0; virtual bool on() const = 0; virtual void setOn(bool val) = 0; virtual unsigned long pluginID() = 0; virtual int id() = 0; virtual QString pluginLabel() const = 0; virtual QString name() const = 0; virtual QString uri() const = 0; virtual QString lib() const = 0; virtual QString dirPath() const = 0; virtual QString fileName() const = 0; virtual QString titlePrefix() const = 0; virtual AudioTrack* track() = 0; virtual void enableController(unsigned long i, bool v = true) = 0; virtual bool controllerEnabled(unsigned long i) const = 0; virtual void enableAllControllers(bool v = true) = 0; virtual void updateControllers() = 0; virtual void activate() = 0; virtual void deactivate() = 0; virtual void writeConfiguration(int level, Xml& xml) = 0; virtual bool readConfiguration(Xml& xml, bool readPreset=false) = 0; virtual bool addScheduledControlEvent(unsigned long i, double val, unsigned frame); // returns true if event cannot be delivered virtual unsigned long parameters() const = 0; virtual unsigned long parametersOut() const = 0; virtual void setParam(unsigned long i, double val) = 0; virtual double param(unsigned long i) const = 0; virtual double paramOut(unsigned long i) const = 0; virtual const char* paramName(unsigned long i) = 0; virtual const char* paramOutName(unsigned long i) = 0; // FIXME TODO: Either find a way to agnosticize these two ranges, or change them from ladspa ranges to a new MusE range class. virtual LADSPA_PortRangeHint range(unsigned long i) = 0; virtual LADSPA_PortRangeHint rangeOut(unsigned long i) = 0; virtual bool usesTransportSource() const = 0; virtual bool hasLatencyOutPort() const = 0; virtual unsigned long latencyOutPortIndex() const = 0; virtual float latency() const = 0; const PluginQuirks& cquirks() const { return _quirks; } PluginQuirks& quirks() { return _quirks; } virtual void setCustomData(const std::vector &) {/* Do nothing by default */} virtual CtrlValueType ctrlValueType(unsigned long i) const = 0; virtual CtrlList::Mode ctrlMode(unsigned long i) const = 0; virtual CtrlEnumValues *ctrlEnumValues(unsigned long i) const; virtual QString portGroup(long unsigned int i) const; virtual bool ctrlIsTrigger(long unsigned int i) const; virtual bool ctrlNotOnGui(long unsigned int i) const; QString dssi_ui_filename() const; MusEGui::PluginGui* gui() const { return _gui; } void deleteGui(); virtual void showGui(); virtual void showGui(bool); virtual bool guiVisible() const; // Sets the gui's geometry. Also updates the saved geometry. virtual void setGeometry(int x, int y, int w, int h); // Returns the current geometry of the gui, or if the gui does not exist, // the saved gui geometry. virtual void getGeometry(int *x, int *y, int *w, int *h) const; // Saves the current gui geometry. virtual void saveGeometry(int x, int y, int w, int h); // Returns the saved gui geometry. virtual void savedGeometry(int *x, int *y, int *w, int *h) const; virtual void showNativeGui() { } virtual void showNativeGui(bool) { } virtual bool nativeGuiVisible() const { return false; } // Sets the gui's geometry. Also updates the saved geometry. virtual void setNativeGeometry(int x, int y, int w, int h); // Returns the current geometry of the gui, or if the gui does not exist, // the saved gui geometry. virtual void getNativeGeometry(int *x, int *y, int *w, int *h) const; // Saves the current gui geometry. virtual void saveNativeGeometry(int x, int y, int w, int h); // Returns the saved gui geometry. virtual void savedNativeGeometry(int *x, int *y, int *w, int *h) const; }; //--------------------------------------------------------- // PluginI // plugin instance //--------------------------------------------------------- #define IS_AUDIO_IN (LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT) #define IS_AUDIO_OUT (LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT) class PluginI : public PluginIBase { #ifdef LV2_SUPPORT friend class LV2PluginWrapper; friend class LV2Synth; #endif #ifdef VST_NATIVE_SUPPORT friend class VstNativeSynth; friend class VstNativePluginWrapper; #endif Plugin* _plugin; int channel; int instances; AudioTrack* _track; int _id; LADSPA_Handle* handle; // per instance Port* controls; Port* controlsOut; Port* controlsOutDummy; unsigned long controlPorts; unsigned long controlOutPorts; bool _hasLatencyOutPort; unsigned long _latencyOutPort; float *_audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. float *_audioOutDummyBuf; // A place to connect unused outputs. bool _on; bool initControlValues; QString _name; QString _label; #ifdef OSC_SUPPORT OscEffectIF _oscif; #endif bool _showNativeGuiPending; void init(); public: PluginI(); virtual ~PluginI(); Plugin* plugin() const { return _plugin; } virtual PluginFeatures_t requiredFeatures() const { return _plugin->requiredFeatures(); } bool hasBypass() const { return true; }; bool on() const { return _on; } void setOn(bool val) { _on = val; } void setTrack(AudioTrack* t) { _track = t; } AudioTrack* track() { return _track; } unsigned long pluginID() { return _plugin->id(); } void setID(int i); int id() { return _id; } void updateControllers(); bool initPluginInstance(Plugin*, int channels); void setChannels(int); void connect(unsigned long ports, unsigned long offset, float** src, float** dst); void apply(unsigned pos, unsigned long n, unsigned long ports, float** bufIn, float** bufOut, float latency_corr_offset = 0.0f); void enableController(unsigned long i, bool v = true) { controls[i].enCtrl = v; } bool controllerEnabled(unsigned long i) const { return controls[i].enCtrl; } void enableAllControllers(bool v = true); void activate(); void deactivate(); QString pluginLabel() const { return _plugin->label(); } QString label() const { return _label; } QString name() const { return _name; } QString lib() const { return _plugin->lib(); } QString uri() const { return _plugin->uri(); } QString dirPath() const { return _plugin->dirPath(); } QString fileName() const { return _plugin->fileName(); } QString titlePrefix() const; #ifdef OSC_SUPPORT OscEffectIF& oscIF() { return _oscif; } int oscControl(unsigned long dssiPort, float val); int oscConfigure(const char *key, const char *val); int oscUpdate(); #endif void writeConfiguration(int level, Xml& xml); bool readConfiguration(Xml& xml, bool readPreset=false); bool loadControl(Xml& xml); bool setControl(const QString& s, double val); void showGui(); void showGui(bool); bool isDssiPlugin() const { return _plugin->isDssiPlugin(); } bool isLV2Plugin() const { return _plugin->isLV2Plugin(); } bool isVstNativePlugin() const { return _plugin->isVstNativePlugin(); } void showNativeGui(); void showNativeGui(bool); bool isShowNativeGuiPending() { return _showNativeGuiPending; } bool nativeGuiVisible() const; unsigned long parameters() const { return controlPorts; } unsigned long parametersOut() const { return controlOutPorts; } void setParam(unsigned long i, double val); void putParam(unsigned long i, double val) { controls[i].val = controls[i].tmpVal = val; } double param(unsigned long i) const { return controls[i].val; } double paramOut(unsigned long i) const { return controlsOut[i].val; } double defaultValue(unsigned long param) const; const char* paramName(unsigned long i) { return _plugin->portName(controls[i].idx); } const char* paramOutName(unsigned long i) { return _plugin->portName(controlsOut[i].idx); } LADSPA_PortDescriptor portd(unsigned long i) const { return _plugin->portd(controls[i].idx); } void range(unsigned long i, float* min, float* max) const { _plugin->range(controls[i].idx, min, max); } bool isAudioIn(unsigned long k) { return (_plugin->portd(k) & IS_AUDIO_IN) == IS_AUDIO_IN; } bool isAudioOut(unsigned long k) { return (_plugin->portd(k) & IS_AUDIO_OUT) == IS_AUDIO_OUT; } LADSPA_PortRangeHint range(unsigned long i) { return _plugin->range(controls[i].idx); } LADSPA_PortRangeHint rangeOut(unsigned long i) { return _plugin->range(controlsOut[i].idx); } bool usesTransportSource() const { return _plugin->usesTimePosition(); }; bool hasLatencyOutPort() const { return _hasLatencyOutPort; } unsigned long latencyOutPortIndex() const { return _latencyOutPort; } float latency() const; CtrlValueType ctrlValueType(unsigned long i) const { return _plugin->ctrlValueType(controls[i].idx); } CtrlEnumValues* ctrlEnumValues( unsigned long i) const { return _plugin->ctrlEnumValues(controls[i].idx); } CtrlList::Mode ctrlMode(unsigned long i) const { return _plugin->ctrlMode(controls[i].idx); } virtual void setCustomData(const std::vector &customParams); }; //--------------------------------------------------------- // Pipeline // chain of connected efx inserts //--------------------------------------------------------- class Pipeline : public std::vector { private: float* buffer[MusECore::MAX_CHANNELS]; void initBuffers(); public: Pipeline(); Pipeline(const Pipeline&, AudioTrack*); ~Pipeline(); void insert(PluginI* p, int index); void remove(int index); void removeAll(); bool isOn(int idx) const; void setOn(int, bool); QString label(int idx) const; QString name(int idx) const; QString uri(int idx) const; void showGui(int, bool); bool isDssiPlugin(int) const; bool isLV2Plugin(int idx) const; bool isVstNativePlugin(int idx) const; bool has_dssi_ui(int idx) const; void showNativeGui(int, bool); void deleteGui(int idx); void deleteAllGuis(); bool guiVisible(int); bool nativeGuiVisible(int); void apply(unsigned pos, unsigned long ports, unsigned long nframes, float** buffer); void move(int idx, bool up); bool empty(int idx) const; void setChannels(int); bool addScheduledControlEvent(int track_ctrl_id, double val, unsigned frame); // returns true if event cannot be delivered void enableController(int track_ctrl_id, bool en); bool controllerEnabled(int track_ctrl_id); float latency() const; }; typedef Pipeline::iterator iPluginI; typedef Pipeline::const_iterator ciPluginI; extern void initPlugins(); extern bool ladspaDefaultValue(const LADSPA_Descriptor* plugin, unsigned long port, float* val); extern void ladspaControlRange(const LADSPA_Descriptor* plugin, unsigned long port, float* min, float* max); extern bool ladspa2MidiControlValues(const LADSPA_Descriptor* plugin, unsigned long port, int ctlnum, int* min, int* max, int* def); extern float midi2LadspaValue(const LADSPA_Descriptor* plugin, unsigned long port, int ctlnum, int val); extern CtrlValueType ladspaCtrlValueType(const LADSPA_Descriptor* plugin, int port); extern CtrlList::Mode ladspaCtrlMode(const LADSPA_Descriptor* plugin, int port); } // namespace MusECore namespace MusEGui { class DoubleLabel; // PluginLoader //--------------------------------------------------------- class PluginLoader : public QUiLoader { public: virtual QWidget* createWidget(const QString & className, QWidget * parent = 0, const QString & name = QString()); PluginLoader(QObject * parent = 0) : QUiLoader(parent) {} }; //--------------------------------------------------------- // GuiParam //--------------------------------------------------------- struct GuiParam { enum { GUI_SLIDER, GUI_SWITCH, GUI_METER, GUI_ENUM }; int type; int hint; bool pressed; MusEGui::DoubleLabel* label; QWidget* actuator; // Slider or Toggle Button (SWITCH) }; //--------------------------------------------------------- // GuiWidgets //--------------------------------------------------------- struct GuiWidgets { enum { SLIDER, DOUBLE_LABEL, QCHECKBOX, QCOMBOBOX }; QWidget* widget; int type; unsigned long param; bool pressed; }; //--------------------------------------------------------- // PluginGui //--------------------------------------------------------- class PluginGui : public QMainWindow { Q_OBJECT MusECore::PluginIBase* plugin; // plugin instance GuiParam* params; GuiParam* paramsOut; unsigned long nobj; // number of widgets in gw GuiWidgets* gw; QAction* onOff; // QAction* transpGovLatencyAct; // QAction* fixedSpeedAct; // QAction* overrideLatencyAct; // QSpinBox* latencyOverrideEntry; QWidget* mw; // main widget QScrollArea* view; // QToolButton* fixNativeUIScalingTB; // QString fixScalingTooltip[3]; void updateControls(); void getPluginConvertedValues(LADSPA_PortRangeHint range, double &lower, double &upper, double &dlower, double &dupper, double &dval); void constructGUIFromFile(QFile& uifile); void constructGUIFromPluginMetadata(); protected: virtual void showEvent(QShowEvent *e); virtual void hideEvent(QHideEvent *e); private slots: void load(); void save(); void bypassToggled(bool); void showSettings(); // void transportGovernsLatencyToggled(bool); // void fixNativeUIScalingTBClicked(); // void fixedSpeedToggled(bool); // void overrideReportedLatencyToggled(bool); // void latencyOverrideValueChanged(int); void sliderChanged(double value, int id, int scrollMode); void labelChanged(double, int); void comboChanged(unsigned long); void guiParamChanged(unsigned long int); void sliderPressed(double, int); void sliderReleased(double, int); void switchPressed(int); void switchReleased(int); void guiParamPressed(unsigned long int); void guiParamReleased(unsigned long int); void guiSliderPressed(double, unsigned long int); void guiSliderReleased(double, unsigned long int); void ctrlRightClicked(const QPoint &, int); void guiSliderRightClicked(const QPoint &, unsigned long int); void guiContextMenuReq(unsigned long int idx); protected slots: virtual void heartBeat(); public: PluginGui(MusECore::PluginIBase*); ~PluginGui(); void setOn(bool); void updateValues(); void updateWindowTitle(); }; } // namespace MusEGui namespace MusEGlobal { extern MusECore::PluginList plugins; extern MusECore::PluginGroups plugin_groups; extern QList plugin_group_names; void writePluginGroupConfiguration(int level, MusECore::Xml& xml); void readPluginGroupConfiguration(MusECore::Xml& xml); } #endif muse-4.0.0/src/muse/pluglist.cpp000066400000000000000000000021011404105311000165520ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // pluglist.cpp // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "pluglist.h" namespace MusEPlugin { PluginScanList pluginList; } // namespace MusEPlugin muse-4.0.0/src/muse/pluglist.h000066400000000000000000000022311404105311000162230ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // pluglist.h // (C) Copyright 2018 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PLUGLIST_H__ #define __PLUGLIST_H__ #include "plugin_scan.h" #include "plugin_list.h" namespace MusEPlugin { extern PluginScanList pluginList; } // namespace MusEPlugin #endif muse-4.0.0/src/muse/poll.h000066400000000000000000000030541404105311000153320ustar00rootroot00000000000000/* * Public domain * * poll(2) emulation for Windows * * This emulates just-enough poll functionality on Windows to work in the * context of the openssl(1) program. This is not a replacement for * POSIX.1-2001 poll(2). * * Dongsheng Song * Brent Cook */ #ifndef LIBCRYPTOCOMPAT_POLL_H #define LIBCRYPTOCOMPAT_POLL_H #ifndef _WIN32 #include_next #else #include /* Type used for the number of file descriptors. */ typedef unsigned long int nfds_t; #if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0600) /* Data structure describing a polling request. */ struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ }; /* Event types that can be polled */ #define POLLIN 0x001 /* There is data to read. */ #define POLLPRI 0x002 /* There is urgent data to read. */ #define POLLOUT 0x004 /* Writing now will not block. */ # define POLLRDNORM 0x040 /* Normal data may be read. */ # define POLLRDBAND 0x080 /* Priority data may be read. */ # define POLLWRNORM 0x100 /* Writing now will not block. */ # define POLLWRBAND 0x200 /* Priority data may be written. */ /* Event types always implicitly polled. */ #define POLLERR 0x008 /* Error condition. */ #define POLLHUP 0x010 /* Hung up. */ #define POLLNVAL 0x020 /* Invalid polling request. */ #endif #ifdef __cplusplus extern "C" { #endif int poll(struct pollfd *pfds, nfds_t nfds, int timeout); #ifdef __cplusplus } #endif #endif /* HAVE_POLL */ #endif /* LIBCRYPTOCOMPAT_POLL_H */ muse-4.0.0/src/muse/poll_win.c000066400000000000000000000153311404105311000162030ustar00rootroot00000000000000/* * Public domain * * poll(2) emulation for Windows * * This emulates just-enough poll functionality on Windows to work in the * context of the openssl(1) program. This is not a replacement for * POSIX.1-2001 poll(2), though it may come closer than I care to admit. * * Dongsheng Song * Brent Cook */ #include #include #include #include #include static int conn_is_closed(int fd) { char buf[1]; int ret = recv(fd, buf, 1, MSG_PEEK); if (ret == -1) { switch (WSAGetLastError()) { case WSAECONNABORTED: case WSAECONNRESET: case WSAENETRESET: case WSAESHUTDOWN: return 1; } } return 0; } static int conn_has_oob_data(int fd) { char buf[1]; return (recv(fd, buf, 1, MSG_PEEK | MSG_OOB) == 1); } static int is_socket(int fd) { if (fd < 3) return 0; WSANETWORKEVENTS events; return (WSAEnumNetworkEvents((SOCKET)fd, NULL, &events) == 0); } static int compute_select_revents(int fd, short events, fd_set *rfds, fd_set *wfds, fd_set *efds) { int rc = 0; if ((events & (POLLIN | POLLRDNORM | POLLRDBAND)) && FD_ISSET(fd, rfds)) { if (conn_is_closed(fd)) rc |= POLLHUP; else rc |= POLLIN | POLLRDNORM; } if ((events & (POLLOUT | POLLWRNORM | POLLWRBAND)) && FD_ISSET(fd, wfds)) rc |= POLLOUT; if (FD_ISSET(fd, efds)) { if (conn_is_closed(fd)) rc |= POLLHUP; else if (conn_has_oob_data(fd)) rc |= POLLRDBAND | POLLPRI; } return rc; } static int compute_wait_revents(HANDLE h, short events, int object, int wait_rc) { int rc = 0; INPUT_RECORD record; DWORD num_read; /* * Assume we can always write to file handles (probably a bad * assumption but works for now, at least it doesn't block). */ if (events & (POLLOUT | POLLWRNORM)) rc |= POLLOUT; /* * Check if this handle was signaled by WaitForMultipleObjects */ if (wait_rc >= WAIT_OBJECT_0 && (object == (wait_rc - WAIT_OBJECT_0)) && (events & (POLLIN | POLLRDNORM))) { /* * Check if this file is stdin, and if so, if it is a console. */ if (h == GetStdHandle(STD_INPUT_HANDLE) && PeekConsoleInput(h, &record, 1, &num_read) == 1) { /* * Handle the input console buffer differently, * since it can signal on other events like * window and mouse, but read can still block. */ if (record.EventType == KEY_EVENT && record.Event.KeyEvent.bKeyDown) { rc |= POLLIN; } else { /* * Flush non-character events from the * console buffer. */ ReadConsoleInput(h, &record, 1, &num_read); } } else { rc |= POLLIN; } } return rc; } static int wsa_select_errno(int err) { switch (err) { case WSAEINTR: case WSAEINPROGRESS: errno = EINTR; break; case WSAEFAULT: /* * Windows uses WSAEFAULT for both resource allocation failures * and arguments not being contained in the user's address * space. So, we have to choose EFAULT or ENOMEM. */ errno = EFAULT; break; case WSAEINVAL: errno = EINVAL; break; case WSANOTINITIALISED: errno = EPERM; break; case WSAENETDOWN: errno = ENOMEM; break; } return -1; } int poll(struct pollfd *pfds, nfds_t nfds, int timeout_ms) { nfds_t i; int timespent_ms, looptime_ms; /* * select machinery */ fd_set rfds, wfds, efds; int rc; int num_sockets; /* * wait machinery */ DWORD wait_rc; HANDLE handles[FD_SETSIZE]; int num_handles; if (pfds == NULL) { errno = EINVAL; return -1; } if (nfds <= 0) { return 0; } FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); num_sockets = 0; num_handles = 0; for (i = 0; i < nfds; i++) { if ((int)pfds[i].fd < 0) continue; if (is_socket(pfds[i].fd)) { if (num_sockets >= FD_SETSIZE) { errno = EINVAL; return -1; } FD_SET(pfds[i].fd, &efds); if (pfds[i].events & (POLLIN | POLLRDNORM | POLLRDBAND)) { FD_SET(pfds[i].fd, &rfds); } if (pfds[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) { FD_SET(pfds[i].fd, &wfds); } num_sockets++; } else { if (num_handles >= FD_SETSIZE) { errno = EINVAL; return -1; } handles[num_handles++] = (HANDLE)_get_osfhandle(pfds[i].fd); } } /* * Determine if the files, pipes, sockets, consoles, etc. have signaled. * * Do this by alternating a loop between WaitForMultipleObjects for * non-sockets and and select for sockets. * * I tried to implement this all in terms of WaitForMultipleObjects * with a select-based 'poll' of the sockets at the end to get extra * specific socket status. * * However, the cost of setting up an event handle for each socket and * cleaning them up reliably was pretty high. Since the event handle * associated with a socket is also global, creating a new one here * cancels one that may exist externally to this function. * * At any rate, even if global socket event handles were not an issue, * the 'FD_WRITE' status of a socket event handle does not behave in an * expected fashion, being triggered by an edge on a write buffer rather * than simply triggering if there is space available. */ timespent_ms = 0; wait_rc = WAIT_FAILED; if (timeout_ms < 0) timeout_ms = INFINITE; looptime_ms = timeout_ms > 100 ? 100 : timeout_ms; do { struct timeval tv; tv.tv_sec = 0; tv.tv_usec = looptime_ms * 1000; int handle_signaled = 0; /* * Check if any file handles have signaled */ if (num_handles) { wait_rc = WaitForMultipleObjects(num_handles, handles, FALSE, 0); if (wait_rc == WAIT_FAILED) { /* * The documentation for WaitForMultipleObjects * does not specify what values GetLastError * may return here. Rather than enumerate * badness like for wsa_select_errno, assume a * general errno value. */ errno = ENOMEM; return 0; } } /* * If we signaled on a file handle, don't wait on the sockets. */ if (wait_rc >= WAIT_OBJECT_0 && (wait_rc <= WAIT_OBJECT_0 + num_handles - 1)) { tv.tv_usec = 0; handle_signaled = 1; } /* * Check if any sockets have signaled */ rc = select(0, &rfds, &wfds, &efds, &tv); if (!handle_signaled && rc == SOCKET_ERROR) return wsa_select_errno(WSAGetLastError()); if (handle_signaled || (num_sockets && rc > 0)) break; timespent_ms += looptime_ms; } while (timespent_ms < timeout_ms); rc = 0; num_handles = 0; for (i = 0; i < nfds; i++) { pfds[i].revents = 0; if ((int)pfds[i].fd < 0) continue; if (is_socket(pfds[i].fd)) { pfds[i].revents = compute_select_revents(pfds[i].fd, pfds[i].events, &rfds, &wfds, &efds); } else { pfds[i].revents = compute_wait_revents( handles[num_handles], pfds[i].events, num_handles, wait_rc); num_handles++; } if (pfds[i].revents) rc++; } return rc; } muse-4.0.0/src/muse/pos.cpp000066400000000000000000000746071404105311000155340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pos.cpp,v 1.11.2.1 2006/09/19 19:07:08 spamatica Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include #include "pos.h" #include "xml.h" #include "tempo.h" #include "globals.h" #include "sync.h" #include "sig.h" namespace MusEGlobal { extern int mtcType; } namespace MusECore { //--------------------------------------------------------- // Pos //--------------------------------------------------------- Pos::Pos() { _type = TICKS; _tick = 0; _frame = 0; _lock = false; sn = -1; } Pos::Pos(const Pos& p) { _type = p._type; sn = p.sn; _tick = p._tick; _frame = p._frame; _lock = p._lock; } Pos::Pos(unsigned t, bool ticks) { if (ticks) { _type = TICKS; _tick = t; } else { _type = FRAMES; _frame = t; } sn = -1; _lock = false; } Pos::Pos(const QString& s) { int m, b, t; sscanf(s.toLatin1(), "%04d.%02d.%03d", &m, &b, &t); _tick = MusEGlobal::sigmap.bar2tick(m, b, t); _type = TICKS; sn = -1; _lock = false; } Pos::Pos(int measure, int beat, int tick) { _tick = MusEGlobal::sigmap.bar2tick(measure, beat, tick); _type = TICKS; sn = -1; _lock = false; } Pos::Pos(int min, int sec, int frame, int subframe, bool ticks, LargeIntRoundMode round_mode) { _lock = false; const int64_t sr = (int64_t)MusEGlobal::sampleRate; int64_t time = sr * (min * 60L + sec); const int64_t f = sr * (frame * 100L + subframe); int64_t divisor = 2400L; switch(MusEGlobal::mtcType) { case 0: // 24 frames sec divisor = 2400L; break; case 1: // 25 divisor = 2500L; break; case 2: // 30 drop frame divisor = 3000L; break; case 3: // 30 non drop frame divisor = 3000L; break; } time += f / divisor; if(time < 0) time = 0; //else if(time > INT_MAX) // time = INT_MAX; if(round_mode == LargeIntRoundUp && (f % divisor) != 0) ++time; else if(round_mode == LargeIntRoundNearest && (f % divisor) >= divisor / 2) ++time; _frame = time; if (ticks) { _type = TICKS; // Convert from frames to ticks _tick = MusEGlobal::tempomap.frame2tick(_frame, &sn, round_mode); } else { _type = FRAMES; sn = -1; } } Pos::Pos(int hour, int min, int sec, int msec, int usec, bool ticks, LargeIntRoundMode round_mode) { _lock = false; const int64_t sr = (int64_t)MusEGlobal::sampleRate; const int64_t uf = sr * (1000L * msec + usec); const int64_t mf = uf / 1000000L; int64_t f = sr * (hour * 3600L + min * 60L + sec) + mf; if(f < 0) f = 0; //else if(f > INT_MAX) // f = INT_MAX; if(round_mode == LargeIntRoundUp && (uf % 1000000L) != 0) ++f; else if(round_mode == LargeIntRoundNearest && (uf % 1000000L) >= 500000L) ++f; _frame = f; if (ticks) { _type = TICKS; // Convert from frames to ticks _tick = MusEGlobal::tempomap.frame2tick(_frame, &sn, round_mode); } else { _type = FRAMES; sn = -1; } } //--------------------------------------------------------- // setType //--------------------------------------------------------- void Pos::setType(TType t) { if (t == _type) return; if(!_lock) { if (_type == TICKS) { // convert from ticks to frames _frame = MusEGlobal::tempomap.tick2frame(_tick, _frame, &sn); } else { // convert from frames to ticks _tick = MusEGlobal::tempomap.frame2tick(_frame, _tick, &sn); } } _type = t; } //--------------------------------------------------------- // setLock //--------------------------------------------------------- void Pos::setLock(bool v) { _lock = v; sn = -1; } //--------------------------------------------------------- // snValid //--------------------------------------------------------- bool Pos::snValid() const { return sn == MusEGlobal::tempomap.tempoSN(); } //--------------------------------------------------------- // operator= //--------------------------------------------------------- Pos& Pos::operator=(const Pos& p) { _type = p._type; sn = p.sn; _tick = p._tick; _frame = p._frame; _lock = p._lock; return *this; } //--------------------------------------------------------- // operator+= //--------------------------------------------------------- Pos& Pos::operator+=(Pos a) { switch(_type) { case FRAMES: _frame += a.frame(); break; case TICKS: _tick += a.tick(); break; } sn = -1; // invalidate cached values return *this; } //--------------------------------------------------------- // operator+= //--------------------------------------------------------- Pos& Pos::operator+=(int a) { switch(_type) { case FRAMES: _frame += a; break; case TICKS: _tick += a; break; } sn = -1; // invalidate cached values return *this; } //--------------------------------------------------------- // operator-= //--------------------------------------------------------- Pos& Pos::operator-=(Pos a) { switch(_type) { case FRAMES: _frame -= a.frame(); break; case TICKS: _tick -= a.tick(); break; } sn = -1; // invalidate cached values return *this; } //--------------------------------------------------------- // operator-= //--------------------------------------------------------- Pos& Pos::operator-=(int a) { switch(_type) { case FRAMES: _frame -= a; break; case TICKS: _tick -= a; break; } sn = -1; // invalidate cached values return *this; } Pos& Pos::operator++() { switch(_type) { case FRAMES: ++_frame; break; case TICKS: ++_tick; break; } sn = -1; // invalidate cached values return *this; } Pos& Pos::operator--() { switch(_type) { case FRAMES: --_frame; break; case TICKS: --_tick; break; } sn = -1; // invalidate cached values return *this; } Pos operator+(Pos a, int b) { Pos c = a; c.setType(a.type()); return c += b; } Pos operator+(Pos a, Pos b) { Pos c = a; return c += b; } Pos operator-(Pos a, int b) { Pos c = a; c.setType(a.type()); return c -= b; } Pos operator-(Pos a, Pos b) { Pos c = a; return c -= b; } bool Pos::operator>=(const Pos& s) const { if (_type == FRAMES) return _frame >= s.frame(); else return _tick >= s.tick(); } bool Pos::operator>(const Pos& s) const { if (_type == FRAMES) return _frame > s.frame(); else return _tick > s.tick(); } bool Pos::operator<(const Pos& s) const { if (_type == FRAMES) return _frame < s.frame(); else return _tick < s.tick(); } bool Pos::operator<=(const Pos& s) const { if (_type == FRAMES) return _frame <= s.frame(); else return _tick <= s.tick(); } bool Pos::operator==(const Pos& s) const { if (_type == FRAMES) return _frame == s.frame(); else return _tick == s.tick(); } bool Pos::operator!=(const Pos& s) const { if (_type == FRAMES) return _frame != s.frame(); else return _tick != s.tick(); } //--------------------------------------------------------- // tick //--------------------------------------------------------- unsigned Pos::tick(LargeIntRoundMode round_mode) const { if(!_lock) { if (_type == FRAMES) _tick = MusEGlobal::tempomap.frame2tick(_frame, _tick, &sn, round_mode); } return _tick; } //--------------------------------------------------------- // frame //--------------------------------------------------------- unsigned Pos::frame(LargeIntRoundMode round_mode) const { if(!_lock) { if (_type == TICKS) _frame = MusEGlobal::tempomap.tick2frame(_tick, _frame, &sn, round_mode); } return _frame; } //--------------------------------------------------------- // posValue //--------------------------------------------------------- unsigned Pos::posValue() const { switch(type()) { case FRAMES: return _frame; case TICKS: return _tick; } return tick(); } unsigned Pos::posValue(TType time_type) const { switch(time_type) { case FRAMES: if(!_lock) { if (_type == TICKS) _frame = MusEGlobal::tempomap.tick2frame(_tick, _frame, &sn); } return _frame; case TICKS: if(!_lock) { if (_type == FRAMES) _tick = MusEGlobal::tempomap.frame2tick(_frame, _tick, &sn); } return _tick; } return tick(); } //--------------------------------------------------------- // setTick //--------------------------------------------------------- void Pos::setTick(unsigned pos, LargeIntRoundMode round_mode) { _tick = pos; sn = -1; if(!_lock) { if (_type == FRAMES) _frame = MusEGlobal::tempomap.tick2frame(pos, &sn, round_mode); } } //--------------------------------------------------------- // setFrame //--------------------------------------------------------- void Pos::setFrame(unsigned pos, LargeIntRoundMode round_mode) { _frame = pos; sn = -1; if(!_lock) { if (_type == TICKS) _tick = MusEGlobal::tempomap.frame2tick(pos, &sn, round_mode); } } //--------------------------------------------------------- // setPosValue //--------------------------------------------------------- void Pos::setPosValue(unsigned val) { sn = -1; switch(type()) { case FRAMES: _frame = val; break; case TICKS: _tick = val; break; } } void Pos::setPosValue(unsigned val, TType time_type) { sn = -1; switch(time_type) { case FRAMES: _frame = val; if(!_lock) { if (_type == TICKS) _tick = MusEGlobal::tempomap.frame2tick(_frame, &sn); } break; case TICKS: _tick = val; if(!_lock) { if (_type == FRAMES) _frame = MusEGlobal::tempomap.tick2frame(_tick, &sn); } break; } } void Pos::setPos(const Pos& s) { sn = -1; switch(s.type()) { case FRAMES: _frame = s.posValue(); if(_lock) { _tick = s.tick(); } else { if (_type == TICKS) _tick = MusEGlobal::tempomap.frame2tick(_frame, &sn); } break; case TICKS: _tick = s.posValue(); if(_lock) { _frame = s.frame(); } else { if (_type == FRAMES) _frame = MusEGlobal::tempomap.tick2frame(_tick, &sn); } break; } } //--------------------------------------------------------- // setTickAndFrame //--------------------------------------------------------- void Pos::setTickAndFrame(unsigned tick, unsigned frame) { sn = MusEGlobal::tempomap.tempoSN(); _tick = tick; _frame = frame; } void Pos::setTickAndFrame(const Pos p) { sn = MusEGlobal::tempomap.tempoSN(); _tick = p.tick(); _frame = p.frame(); } //--------------------------------------------------------- // convert (static) //--------------------------------------------------------- unsigned Pos::convert(unsigned val, TType from_type, TType to_type) { switch(from_type) { case FRAMES: switch(to_type) { case FRAMES: return val; case TICKS: return MusEGlobal::tempomap.frame2tick(val); } break; case TICKS: switch(to_type) { case FRAMES: return MusEGlobal::tempomap.tick2frame(val); case TICKS: return val; } break; } return val; } //--------------------------------------------------------- // write //--------------------------------------------------------- void Pos::write(int level, Xml& xml, const char* name) const { xml.nput(level++, "<%s ", name); switch(_type) { case TICKS: xml.nput("tick=\"%d\"", _tick); break; case FRAMES: xml.nput("frame=\"%d\"", _frame); break; } xml.put(" />", name); } //--------------------------------------------------------- // read //--------------------------------------------------------- void Pos::read(Xml& xml, const char* name) { sn = -1; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: xml.unknown(name); break; case Xml::Attribut: if (tag == "tick") { _tick = xml.s2().toInt(); _type = TICKS; } else if (tag == "frame" || tag == "sample") { // For now, the conversion only has a TEMPORARY effect during song loading. // See comments in Song::read at the "samplerate" tag. _frame = MusEGlobal::convertFrame4ProjectSampleRate(xml.s2().toInt()); _type = FRAMES; } else xml.unknown(name); break; case Xml::TagEnd: if (tag == name) return; default: break; } } } //--------------------------------------------------------- // PosLen //--------------------------------------------------------- PosLen::PosLen(bool ticks, unsigned pos, unsigned len) : Pos(pos, ticks) { if (ticks) { _lenTick = len; _lenFrame = 0; } else { _lenTick = 0; _lenFrame = len; } sn = -1; } PosLen::PosLen(const PosLen& p) : Pos(p) { _lenTick = p._lenTick; _lenFrame = p._lenFrame; sn = -1; } PosLen& PosLen::operator=(const PosLen& p) { Pos::operator=(p); _lenTick = p._lenTick; _lenFrame = p._lenFrame; sn = -1; return *this; } //--------------------------------------------------------- // dump //--------------------------------------------------------- void PosLen::dump(int n) const { Pos::dump(n); printf(" Len("); switch(type()) { case FRAMES: printf("samples=%d)\n", _lenFrame); break; case TICKS: printf("ticks=%d)\n", _lenTick); break; } } void Pos::dump(int /*n*/) const { printf("Pos(%s, sn=%d, ", type() == FRAMES ? "Frames" : "Ticks", sn); switch(type()) { case FRAMES: printf("samples=%d)", _frame); break; case TICKS: printf("ticks=%d)", _tick); break; } } //--------------------------------------------------------- // write //--------------------------------------------------------- void PosLen::write(int level, Xml& xml, const char* name) const { xml.nput(level++, "<%s ", name); switch(type()) { case TICKS: xml.nput("tick=\"%d\" len=\"%d\"", tick(), _lenTick); break; case FRAMES: xml.nput("sample=\"%d\" len=\"%d\"", frame(), _lenFrame); break; } xml.put(" />", name); } //--------------------------------------------------------- // read //--------------------------------------------------------- void PosLen::read(Xml& xml, const char* name) { sn = -1; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: xml.unknown(name); break; case Xml::Attribut: if (tag == "tick") { setType(TICKS); setTick(xml.s2().toInt()); } else if (tag == "sample") { setType(FRAMES); // For now, the conversion only has a TEMPORARY effect during song loading. // See comments in Song::read at the "samplerate" tag. setFrame(MusEGlobal::convertFrame4ProjectSampleRate(xml.s2().toInt())); } else if (tag == "len") { int n = xml.s2().toInt(); switch(type()) { case TICKS: setLenTick(n); break; case FRAMES: // For now, the conversion only has a TEMPORARY effect during song loading. // See comments in Song::read at the "samplerate" tag. setLenFrame(MusEGlobal::convertFrame4ProjectSampleRate(n)); break; } } else xml.unknown(name); break; case Xml::TagEnd: if (tag == name) return; default: break; } } } //--------------------------------------------------------- // setLenTick //--------------------------------------------------------- void PosLen::setLenTick(unsigned len) { _lenTick = len; sn = -1; if (type() == FRAMES) _lenFrame = MusEGlobal::tempomap.deltaTick2frame(tick(), tick() + len, &sn); } //--------------------------------------------------------- // setLenFrame //--------------------------------------------------------- void PosLen::setLenFrame(unsigned len) { _lenFrame = len; sn = -1; if (type() == TICKS) _lenTick = MusEGlobal::tempomap.deltaFrame2tick(frame(), frame() + len, &sn); } //--------------------------------------------------------- // setLenValue //--------------------------------------------------------- void PosLen::setLenValue(unsigned val) { sn = -1; switch(type()) { case FRAMES: _lenFrame = val; break; case TICKS: _lenTick = val; break; } } void PosLen::setLenValue(unsigned val, TType time_type) { sn = -1; switch(time_type) { case FRAMES: _lenFrame = val; if (type() == TICKS) _lenTick = MusEGlobal::tempomap.deltaFrame2tick(frame(), frame() + _lenFrame, &sn); break; case TICKS: _lenTick = val; if (type() == FRAMES) _lenFrame = MusEGlobal::tempomap.deltaTick2frame(tick(), tick() + _lenTick, &sn); break; } } void PosLen::setLen(const PosLen& s) { sn = -1; switch(s.type()) { case FRAMES: _lenFrame = s.lenValue(); if (type() == TICKS) _lenTick = MusEGlobal::tempomap.deltaFrame2tick(frame(), frame() + _lenFrame, &sn); break; case TICKS: _lenTick = s.lenValue(); if (type() == FRAMES) _lenFrame = MusEGlobal::tempomap.deltaTick2frame(tick(), tick() + _lenTick, &sn); break; } } //--------------------------------------------------------- // convertLen (static) //--------------------------------------------------------- unsigned PosLen::convertLen(unsigned val, unsigned len, TType from_type, TType to_type) { switch(from_type) { case FRAMES: switch(to_type) { case FRAMES: return val; case TICKS: return MusEGlobal::tempomap.deltaFrame2tick(val, val + len); } break; case TICKS: switch(to_type) { case FRAMES: return MusEGlobal::tempomap.deltaTick2frame(val, val + len); case TICKS: return val; } break; } return len; } //--------------------------------------------------------- // lenTick //--------------------------------------------------------- unsigned PosLen::lenTick() const { if (type() == FRAMES) _lenTick = MusEGlobal::tempomap.deltaFrame2tick(frame(), frame() + _lenFrame, &sn); return _lenTick; } //--------------------------------------------------------- // lenFrame //--------------------------------------------------------- unsigned PosLen::lenFrame() const { if (type() == TICKS) _lenFrame = MusEGlobal::tempomap.deltaTick2frame(tick(), tick() + _lenTick, &sn); return _lenFrame; } //--------------------------------------------------------- // lenValue //--------------------------------------------------------- unsigned PosLen::lenValue() const { switch(type()) { case FRAMES: return _lenFrame; case TICKS: return _lenTick; } return lenTick(); } unsigned PosLen::lenValue(TType time_type) const { switch(time_type) { case FRAMES: if (type() == TICKS) _lenFrame = MusEGlobal::tempomap.deltaTick2frame(tick(), tick() + _lenTick, &sn); return _lenFrame; case TICKS: if (type() == FRAMES) _lenTick = MusEGlobal::tempomap.deltaFrame2tick(frame(), frame() + _lenFrame, &sn); return _lenTick; } return lenTick(); } //--------------------------------------------------------- // end //--------------------------------------------------------- Pos PosLen::end() const { Pos pos(*this); pos.invalidSn(); switch(type()) { case FRAMES: pos.setFrame(pos.frame() + _lenFrame); break; case TICKS: pos.setTick(pos.tick() + _lenTick); break; } return pos; } void PosLen::setEnd(const Pos& pos) { switch(pos.type()) { case FRAMES: if(pos.frame() > frame()) setLenFrame(pos.frame() - frame()); else setLenFrame(0); break; case TICKS: if(pos.tick() > tick()) setLenTick(pos.tick() - tick()); else setLenTick(0); break; } } //--------------------------------------------------------- // endValue //--------------------------------------------------------- unsigned PosLen::endValue() const { switch(type()) { case FRAMES: return frame() + lenFrame(); break; case TICKS: return tick() + lenTick(); break; } return 0; } unsigned PosLen::endValue(TType time_type) const { switch(time_type) { case FRAMES: return frame() + lenFrame(); break; case TICKS: return tick() + lenTick(); break; } return 0; } void PosLen::setEndValue(unsigned val) { switch(type()) { case FRAMES: if(val > frame()) setLenFrame(val - frame()); else setLenFrame(0); break; case TICKS: if(val > tick()) setLenTick(val - tick()); else setLenTick(0); break; } } void PosLen::setEndValue(unsigned val, TType time_type) { switch(time_type) { case FRAMES: if(val > frame()) setLenFrame(val - frame()); else setLenFrame(0); break; case TICKS: if(val > tick()) setLenTick(val - tick()); else setLenTick(0); break; } } //--------------------------------------------------------- // setPos //--------------------------------------------------------- void PosLen::setPos(const Pos& pos) { switch(pos.type()) { case FRAMES: setFrame(pos.frame()); break; case TICKS: setTick(pos.tick()); break; } } //--------------------------------------------------------- // mbt //--------------------------------------------------------- void Pos::mbt(int* bar, int* beat, int* tk) const { MusEGlobal::sigmap.tickValues(tick(), bar, beat, (unsigned*)tk); } //--------------------------------------------------------- // msf //--------------------------------------------------------- void Pos::msf(int* hour, int* min, int* sec, int* fr, int* subFrame, LargeIntRoundMode round_mode) const { const uint64_t sr = (uint64_t)MusEGlobal::sampleRate; const unsigned int f = frame(); const unsigned int time = f / sr; if(hour) { *hour = time / 3600; if(min) *min = (time / 60) % 60; } else if(min) *min = time / 60; if(sec) *sec = time % 60; unsigned int rest_fact = 24; switch(MusEGlobal::mtcType) { case 0: // 24 frames sec rest_fact = 24; break; case 1: // 25 rest_fact = 25; break; case 2: // 30 drop frame rest_fact = 30; break; case 3: // 30 non drop frame rest_fact = 30; break; } const uint64_t ss = (uint64_t)(f % sr) * 100 * rest_fact; uint64_t sf = ss / sr; if(round_mode == LargeIntRoundUp && (ss % sr) != 0) ++sf; else if(round_mode == LargeIntRoundNearest && (ss % sr) >= sr / 2) ++sf; if(subFrame) *subFrame = sf % 100; if(fr) *fr = sf / 100; } //--------------------------------------------------------- // msmu //--------------------------------------------------------- void Pos::msmu(int* hour, int* min, int* sec, int* msec, int* usec, LargeIntRoundMode round_mode) const { const uint64_t sr = (uint64_t)MusEGlobal::sampleRate; const unsigned int f = frame(); const unsigned int time = f / sr; if(hour) { *hour = time / 3600; if(min) *min = (time / 60) % 60; } else if(min) *min = time / 60; if(sec) *sec = time % 60; const uint64_t uf = (uint64_t)(f % sr) * 1000000; uint64_t us = uf / sr; if(round_mode == LargeIntRoundUp && (uf % sr) != 0) ++us; else if(round_mode == LargeIntRoundNearest && (uf % sr) >= sr / 2) ++us; if(usec) *usec = us % 1000; if(msec) *msec = us / 1000; } //--------------------------------------------------------- // isValid //--------------------------------------------------------- bool Pos::isValid(int,int,int) { return true; } //--------------------------------------------------------- // isValid //--------------------------------------------------------- bool Pos::isValid(int,int,int,int) { return true; } } // namespace MusECore muse-4.0.0/src/muse/pos.h000066400000000000000000000211011404105311000151560ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: pos.h,v 1.8 2004/07/14 15:27:26 wschweer Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __POS_H__ #define __POS_H__ #include "large_int.h" class QString; namespace MusECore { class Xml; //--------------------------------------------------------- // Pos // depending on type _tick or _frame is a cached // value. When the tempomap changes, all cached values // are invalid. Sn is used to check for tempomap // changes. //--------------------------------------------------------- class Pos { public: enum TType { TICKS, FRAMES }; private: TType _type; mutable int sn; mutable unsigned _tick; mutable unsigned _frame; // This flag indicates the frame and tick are separate // and are not influenced by the tempomap at all. // Frame and tick can be set independently without being // altered by the tempomap. // When this flag is set, the _type is effectively ignored // for several functions and the serial number is set to -1. // It allows storing independent frame and tick values // conveniently housed in a Pos. // It is mainly for use during external midi sync, // where frame and tick are completely independent. // When asked for frame or tick, Pos will simply return // the stored frame or tick verbosely, without conversion // using the tempomap. bool _lock; public: Pos(); Pos(const Pos&); Pos(int measure, int beat, int tick); //------------------------------------------------------------------------------------------ // To make it easier to work with automatic section increment/decrement over/under-flow, // these constructors accept integers which can be negative. If the end result is out of range, // the result is adjusted accordingly. //------------------------------------------------------------------------------------------ // Construct a position from a minute-second-frame-subframe value. If round_mode is up or nearest, any // fractional frame in the result is either rounded up or to the nearest frame. // round_mode should normally be left as down. If ticks is true, the resulting frame is further // rounded up or to the nearest tick, and the type is set to TICKS. Pos(int min, int sec, int frame, int subframe, bool ticks = false, LargeIntRoundMode round_mode = LargeIntRoundDown); // Construct a position from a time value. If round_mode is up or nearest, any fractional frame in the result // is either rounded up or to the nearest frame. round_mode should normally be left as down. If ticks // is true, the resulting frame is further rounded up or to the nearest tick, and the type // is set to TICKS. Pos(int hour, int min, int sec, int msec, int usec, bool ticks = false, LargeIntRoundMode round_mode = LargeIntRoundDown); Pos(unsigned, bool ticks=true); Pos(const QString&); void dump(int n = 0) const; void mbt(int* bar, int* beat, int* tk) const; // If hour is given, hours and minutes will be split accordingly, with min being a maximum of 59. // Otherwise if hour is null, min will be unlimited. void msf(int* hour, int* min, int* sec, int* fr, int* subFrame, LargeIntRoundMode round_mode = LargeIntRoundUp) const; // msmu resolution is normally greater than ticks or frames. Round up by default. // If hour is given, hours and minutes will be split accordingly, with min being a maximum of 59. // Otherwise if hour is null, min will be unlimited. void msmu(int* hour, int* min, int* sec, int* msec, int* usec, LargeIntRoundMode round_mode = LargeIntRoundUp) const; void invalidSn() { sn = -1; } // Returns whether the serial number is the same as the tempomap serial number. bool snValid() const; TType type() const { return _type; } void setType(TType t); bool lock() const { return _lock; } void setLock(bool v); Pos& operator=(const Pos&); Pos& operator+=(Pos a); Pos& operator+=(int a); Pos& operator-=(Pos a); Pos& operator-=(int a); Pos& operator++(); Pos& operator--(); bool operator>=(const Pos& s) const; bool operator>(const Pos& s) const; bool operator<(const Pos& s) const; bool operator<=(const Pos& s) const; bool operator==(const Pos& s) const; bool operator!=(const Pos& s) const; friend Pos operator+(Pos a, Pos b); friend Pos operator+(Pos a, int b); friend Pos operator-(Pos a, Pos b); friend Pos operator-(Pos a, int b); unsigned tick(LargeIntRoundMode round_mode = LargeIntRoundDown) const; unsigned frame(LargeIntRoundMode round_mode = LargeIntRoundUp) const; unsigned posValue() const; unsigned posValue(TType time_type) const; void setTick(unsigned, LargeIntRoundMode round_mode = LargeIntRoundUp); void setFrame(unsigned, LargeIntRoundMode round_mode = LargeIntRoundDown); void setPosValue(unsigned val); void setPosValue(unsigned val, TType time_type); // This is not the same as assigning or =. // The type is kept and the value converted if required. void setPos(const Pos&); // This is a convenience function that sets both tick and frame at once. // It allows setting independent tick and frame at once. // They will both be valid until the next tempomap serial number change // ie. whenever tempo changes. Certain situations benefit from this, // for example passing around audio transport tick and frame which may // need to be separate especially during external sync. // The important thing is that calling tick() or frame() will NOT cause it // to try and recalculate using the tempomap, until the next tempo change. void setTickAndFrame(unsigned tick, unsigned frame); void setTickAndFrame(const Pos); static unsigned convert(unsigned val, TType from_type, TType to_type); void write(int level, Xml&, const char*) const; void read(Xml& xml, const char*); bool isValid() const { return true; } static bool isValid(int m, int b, int t); static bool isValid(int, int, int, int); }; //--------------------------------------------------------- // PosLen //--------------------------------------------------------- class PosLen : public Pos { mutable unsigned _lenTick; mutable unsigned _lenFrame; mutable int sn; public: PosLen(bool ticks = true, unsigned pos = 0, unsigned len = 0); PosLen(const PosLen&); void dump(int n = 0) const; PosLen& operator=(const PosLen&); void write(int level, Xml&, const char*) const; void read(Xml& xml, const char*); void setLenTick(unsigned); void setLenFrame(unsigned); void setLenValue(unsigned val); void setLenValue(unsigned val, TType time_type); // This is not the same as assigning or =. // The type is kept and the value converted if required. void setLen(const PosLen&); unsigned lenTick() const; unsigned lenFrame() const; unsigned lenValue() const; unsigned lenValue(TType time_type) const; Pos end() const; void setEnd(const Pos&); void setEndValue(unsigned val); void setEndValue(unsigned val, TType time_type); unsigned endTick() const { return end().tick(); } unsigned endFrame() const { return end().frame(); } unsigned endValue() const; unsigned endValue(TType time_type) const; void setPos(const Pos&); static unsigned convertLen(unsigned val, unsigned len, TType from_type, TType to_type); }; } // namespace MusECore #endif muse-4.0.0/src/muse/rasterizer.cpp000066400000000000000000000475411404105311000171220ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // rasterizer.cpp // Copyright (C) 2020 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "sig.h" #include "rasterizer.h" #include "globals.h" namespace MusEGui { Rasterizer::Rasterizer(int division, QObject *parent) : QObject(parent), _division(division) { _rows = 0; _rasterArray = nullptr; updateRasterizer(); } Rasterizer::~Rasterizer() { if(_rasterArray) delete [] _rasterArray; _rasterArray = nullptr; } int Rasterizer::columnCount() const { return 3; } void Rasterizer::updateColumn(Column col) { int div, row, d; const int col_num = col; const int col_offset = col_num * _rows; // For the 'off' row. _rasterArray[col_offset] = 1; // For the 'bar' row. _rasterArray[col_offset + _rows - 1] = 0; if(col == TripletColumn) { d = _division * 4 * 2; // Can't use it if it has a remainder. if(d % 3 != 0) return; div = d / 3; } else if(col == NormalColumn) div = _division * 4; else if(col == DottedColumn) { d = _division * 4 * 3; // Can't use it if it has a remainder. if(d % 2 != 0) return; div = d / 2; } // Set all rows, except 'off' and 'bar' which are already done above. for(row = _rows - 2; row >= 1; --row) { _rasterArray[col_offset + row] = div; // Can't use it if it has a remainder. if(div % 2 != 0) break; div /= 2; } } void Rasterizer::updateRasterizer() { emit dataAboutToBeReset(); if(_rasterArray) delete [] _rasterArray; _rasterArray = nullptr; _rows = 0; int num_cols = columnCount(); if(num_cols > 0) { // The actual number of rows displayed depends on the division value. // The actual number and the maximum will always be at least 5: Bar, off, whole, half, // and quarter notes are always available, regardless of division value. // The other two columns, triplet (x2/3) and dotted (x3/2), may contain up to the // same rows as the centre, but some values may be missing if they have a remainder - // values are unusable if they have a remainder after the divisions. int div = _division; int rows = 5; while(1) { // Must be integral values, no remainder. if(div % 2 != 0) break; div /= 2; // Can't go any lower than 6 due to the triplet (x2/3) column. if(div < 6) break; ++rows; } _rows = rows; const int num_rasters = _rows * num_cols; _rasterArray = new int[num_rasters]; // Initalize the array elements to -1 (invalid). for(int i = 0; i < num_rasters; ++i) _rasterArray[i] = -1; updateColumn(TripletColumn); updateColumn(NormalColumn); updateColumn(DottedColumn); } emit dataReset(); } void Rasterizer::setDivision(int div) { if(_division == div) return; _division = div; updateRasterizer(); } //--------------------------------------------------------- // indexOf //--------------------------------------------------------- int Rasterizer::indexOf(int val) const { int idx; const int cols = columnCount(); for(int row = 0; row < _rows; ++row) { for(int col = 0; col < cols; ++col) { idx = row + col * _rows; if(_rasterArray[idx] == val) return idx; } } // Raster was not found. Return -1. return -1; } int Rasterizer::checkRaster(int val) const { int rast; const int cols = columnCount(); for(int row = 0; row < _rows; ++row) { for(int col = 0; col < cols; ++col) { rast = _rasterArray[row + col * _rows]; if(rast == val) return rast; } } // No suitable raster was found. Just return 1 division (1 quarter note). return _division; } int Rasterizer::rasterAt(int row, int col) const { if(row >= _rows || col >= columnCount() || !_rasterArray) return -1; return _rasterArray[row + col * _rows]; } bool Rasterizer::isBarRaster(int row, int col) const { return rasterAt(row, col) == 0; } bool Rasterizer::isOffRaster(int row, int col) const { return rasterAt(row, col) == 1; } int Rasterizer::barRow() const { return rowCount() - 1; } int Rasterizer::offRow() const { return 0; } int Rasterizer::commonRaster(CommonRasters commonRast) const { const int rows = rowCount(); int inv_row = 0; switch(commonRast) { case CommonRasterBar: return 0; break; case CommonRasterOff: return 1; break; case CommonRaster1: inv_row = 2; break; case CommonRaster2: inv_row = 3; break; case CommonRaster4: inv_row = 4; break; case CommonRaster8: inv_row = 5; break; case CommonRaster16: inv_row = 6; break; case CommonRaster32: inv_row = 7; break; case CommonRaster64: inv_row = 8; break; } const int row = rows - inv_row; if(row >= 0 && row != offRow() && row != barRow()) return rasterAt(row, NormalColumn); return -1; } bool Rasterizer::isLessThanNormalRaster(int row, int col, int normalRaster) const { const int rast = rasterAt(row, col); if(rast < 0) return true; // The last row is for 'bar'. if(rast == 0) return false; switch(col) { case TripletColumn: return rast < (normalRaster * 2) / 3; break; case NormalColumn: return rast < normalRaster; break; case DottedColumn: return rast < (normalRaster * 3) / 2; break; } return true; } int Rasterizer::rasterDenomAt(int row) const { // The first row is for 'off'. if(row == 0) return 0; const int row_count = rowCount(); // The last row is for 'bar'. if(row == row_count - 1) return 0; return 1 << (row_count - row - 2); } // ========================================================================= RasterizerModel::RasterizerModel( Rasterizer *rasterizer, QObject *parent, int max_rows, QList visible_columns, DisplayFormat displayFormat) : QAbstractTableModel(parent), _rasterizer(rasterizer), _displayFormat(displayFormat) { setVisibleColumns(visible_columns); setMaxRows(max_rows); // Receive signals to satisfy Qt's model requirement of being informed // when the data is about to change and has changed. _dataAboutToBeResetConnection = connect(_rasterizer, &Rasterizer::dataAboutToBeReset, [this]() { beginResetModel(); } ); _dataResetConnection = connect(_rasterizer, &Rasterizer::dataReset, [this]() { endResetModelHandler(); } ); } RasterizerModel::~RasterizerModel() { disconnect(_dataAboutToBeResetConnection); disconnect(_dataResetConnection); } void RasterizerModel::endResetModelHandler() { //endResetModel(); ??? updateRows(); endResetModel(); } void RasterizerModel::updateRows() { _modelToRasterRowList.clear(); _rasterToModelRowMap.clear(); const int mdl_row_count = rowCount(); const int rast_row_count = _rasterizer->rowCount(); if(mdl_row_count <= 0 || rast_row_count <= 0) return; // There is always a row zero - the 'off' row. _modelToRasterRowList.append(0); _rasterToModelRowMap.insert(0, 0); int rast_row = rast_row_count - mdl_row_count + 1; for(int mdl_row = 1; mdl_row < mdl_row_count; ++mdl_row, ++rast_row) { _modelToRasterRowList.append(rast_row); _rasterToModelRowMap.insert(rast_row, mdl_row); } } int RasterizerModel::modelToRasterRow(int row) const { if(row >= _modelToRasterRowList.size()) return -1; return _modelToRasterRowList.at(row); } Rasterizer::Column RasterizerModel::modelToRasterCol(int col) const { if(col >= _modelToRasterColumnList.size()) return Rasterizer::InvalidColumn; return _modelToRasterColumnList.at(col); } QString RasterizerModel::textAt(int row, int col) const { const int rast_row = modelToRasterRow(row); if(rast_row < 0) return QString(); const Rasterizer::Column rast_col = modelToRasterCol(col); if(rast_col == Rasterizer::InvalidColumn) return QString(); // All columns of row zero say 'off'. if(_rasterizer->isOffRaster(rast_row, rast_col)) return QT_TRANSLATE_NOOP("MusECore::RasterizerModel", "Off"); // All columns of the last row say 'Bar'. if(_rasterizer->isBarRaster(rast_row, rast_col)) return QT_TRANSLATE_NOOP("MusECore::RasterizerModel", "Bar"); const int rast = _rasterizer->rasterAt(rast_row, rast_col); if(rast < 0) return QString(); // Is the raster value length less than a triplet, normal, or dotted 64th note? const bool show_tick = _rasterizer->isLessThanNormalRaster(rast_row, rast_col, _rasterizer->division() / 16); if(show_tick) return QString("%1tk").arg(rast); else { const int denom_num = _rasterizer->rasterDenomAt(rast_row); QString s; switch(_displayFormat) { case FractionFormat: s += QString("1/%1").arg(denom_num); break; case DenominatorFormat: s += QString("%1").arg(denom_num); break; } if(rast_col == Rasterizer::TripletColumn) s += QString("T"); else if(rast_col == Rasterizer::DottedColumn) s += QString("."); return s; } } int RasterizerModel::rasterAt(int row, int col) const { const int rast_row = modelToRasterRow(row); if(rast_row < 0) return -1; Rasterizer::Column rast_col = modelToRasterCol(col); if(rast_col == Rasterizer::InvalidColumn) return -1; return _rasterizer->rasterAt(rast_row, rast_col); } void RasterizerModel::setRasterizer(const Rasterizer *r) { if(_rasterizer == r) return; disconnect(_dataAboutToBeResetConnection); disconnect(_dataResetConnection); beginResetModel(); _rasterizer = r; updateRows(); endResetModel(); // Receive signals to satisfy Qt's model requirement of being informed // when the data is about to change and has changed. _dataAboutToBeResetConnection = connect(_rasterizer, &Rasterizer::dataAboutToBeReset, [this]() { beginResetModel(); } ); _dataResetConnection = connect(_rasterizer, &Rasterizer::dataReset, [this]() { endResetModelHandler(); } ); } int RasterizerModel::division() const { return _rasterizer->division(); } int RasterizerModel::maxRows() const { return _maxRows; } void RasterizerModel::setMaxRows(int rows) { if(_maxRows == rows) return; beginResetModel(); _maxRows = rows; updateRows(); endResetModel(); } QList RasterizerModel::visibleRows() const { return _modelToRasterRowList; } QList RasterizerModel::visibleColumns() const { return _modelToRasterColumnList; } void RasterizerModel::setVisibleColumns(const QList& cols) { beginResetModel(); _modelToRasterColumnList = cols; _rasterToModelColumnMap.clear(); const int sz = _modelToRasterColumnList.size(); for(int i = 0; i < sz; ++i) _rasterToModelColumnMap.insert(_modelToRasterColumnList.at(i), i); updateRows(); endResetModel(); } RasterizerModel::DisplayFormat RasterizerModel::displayFormat() const { return _displayFormat; } void RasterizerModel::setDisplayFormat(DisplayFormat format) { beginResetModel(); _displayFormat = format; endResetModel(); } int RasterizerModel::indexOfRaster(int val) const { const int mdl_rows = _modelToRasterRowList.size(); const int mdl_cols = _modelToRasterColumnList.size(); int rast_row, rast_col; for(int mdl_row = 0; mdl_row < mdl_rows; ++mdl_row) { rast_row = _modelToRasterRowList.at(mdl_row); for(int mdl_col = 0; mdl_col < mdl_cols; ++mdl_col) { rast_col = _modelToRasterColumnList.at(mdl_col); if(_rasterizer->rasterAt(rast_row, rast_col) == val) return mdl_row + mdl_col * mdl_rows; } } return -1; } QModelIndex RasterizerModel::modelIndexOfRaster(int val) const { const int mdl_rows = _modelToRasterRowList.size(); const int mdl_cols = _modelToRasterColumnList.size(); int rast_row, rast_col; for(int mdl_row = 0; mdl_row < mdl_rows; ++mdl_row) { rast_row = _modelToRasterRowList.at(mdl_row); for(int mdl_col = 0; mdl_col < mdl_cols; ++mdl_col) { rast_col = _modelToRasterColumnList.at(mdl_col); if(_rasterizer->rasterAt(rast_row, rast_col) == val) return index(mdl_row, mdl_col); } } return QModelIndex(); } int RasterizerModel::checkRaster(int val) const { const int mdl_rows = _modelToRasterRowList.size(); const int mdl_cols = _modelToRasterColumnList.size(); int rast, rast_row, rast_col; for(int mdl_row = 0; mdl_row < mdl_rows; ++mdl_row) { rast_row = _modelToRasterRowList.at(mdl_row); for(int mdl_col = 0; mdl_col < mdl_cols; ++mdl_col) { rast_col = _modelToRasterColumnList.at(mdl_col); rast = _rasterizer->rasterAt(rast_row, rast_col); if(rast == val) return rast; } } // No suitable raster was found. Just return 1 division (1 quarter note). return _rasterizer->division(); } int RasterizerModel::barRow() const { const int rast_bar_row = _rasterizer->barRow(); QMap::const_iterator imbr = _rasterToModelRowMap.find(rast_bar_row); if(imbr == _rasterToModelRowMap.constEnd()) return -1; return imbr.value(); } int RasterizerModel::offRow() const { const int rast_off_row = _rasterizer->offRow(); QMap::const_iterator imbr = _rasterToModelRowMap.find(rast_off_row); if(imbr == _rasterToModelRowMap.constEnd()) return -1; return imbr.value(); } bool RasterizerModel::isBarRaster(int row, int col) const { const int rast_row = modelToRasterRow(row); if(rast_row < 0) return false; const Rasterizer::Column rast_col = modelToRasterCol(col); if(rast_col == Rasterizer::InvalidColumn) return false; return _rasterizer->isBarRaster(rast_row, rast_col); } bool RasterizerModel::isOffRaster(int row, int col) const { const int rast_row = modelToRasterRow(row); if(rast_row < 0) return false; const Rasterizer::Column rast_col = modelToRasterCol(col); if(rast_col == Rasterizer::InvalidColumn) return false; return _rasterizer->isOffRaster(rast_row, rast_col); } int RasterizerModel::commonRaster(Rasterizer::CommonRasters commonRast) const { const int rows = rowCount(); int inv_row = 0; switch(commonRast) { case Rasterizer::CommonRasterBar: return 0; break; case Rasterizer::CommonRasterOff: return 1; break; case Rasterizer::CommonRaster1: inv_row = 2; break; case Rasterizer::CommonRaster2: inv_row = 3; break; case Rasterizer::CommonRaster4: inv_row = 4; break; case Rasterizer::CommonRaster8: inv_row = 5; break; case Rasterizer::CommonRaster16: inv_row = 6; break; case Rasterizer::CommonRaster32: inv_row = 7; break; case Rasterizer::CommonRaster64: inv_row = 8; break; } const int row = rows - inv_row; if(row >= 0 && row != offRow() && row != barRow()) return rasterAt(row, Rasterizer::NormalColumn); return -1; } int RasterizerModel::pickRaster(int raster, RasterPick pick) const { const QModelIndex mdl_idx = modelIndexOfRaster(raster); if(!mdl_idx.isValid()) return raster; const int mdl_row = mdl_idx.row(); const int mdl_col = mdl_idx.column(); const int mdl_rows = rowCount(); const bool has_triple_col = _rasterToModelColumnMap.find(Rasterizer::TripletColumn) != _rasterToModelColumnMap.constEnd(); const bool has_normal_col = _rasterToModelColumnMap.find(Rasterizer::NormalColumn) != _rasterToModelColumnMap.constEnd(); const bool has_dotted_col = _rasterToModelColumnMap.find(Rasterizer::DottedColumn) != _rasterToModelColumnMap.constEnd(); const bool is_off = isOffRaster(mdl_row, mdl_col); const bool is_bar = isBarRaster(mdl_row, mdl_col); const int off_row = offRow(); const int bar_row = barRow(); int new_mdl_row = mdl_row; int new_mdl_col = mdl_col; int new_raster = -1; switch(pick) { case ToggleTriple: // Special for 'off' and 'bar' rows. if(is_off || is_bar) return raster; if(mdl_col == Rasterizer::TripletColumn && has_normal_col) new_mdl_col = Rasterizer::NormalColumn; else if(has_triple_col) new_mdl_col = Rasterizer::TripletColumn; break; case ToggleDotted: if(is_off || is_bar) return raster; if(mdl_col == Rasterizer::DottedColumn && has_normal_col) new_mdl_col = Rasterizer::NormalColumn; else if(has_dotted_col) new_mdl_col = Rasterizer::DottedColumn; break; case ToggleHigherDotted: if(is_off || is_bar) return raster; if(mdl_col == Rasterizer::DottedColumn && has_normal_col) { // Exclude 'off' and 'bar' rows. const int nrow = new_mdl_row - 1; if(nrow >= 0 && nrow != off_row && nrow != bar_row) { --new_mdl_row; new_mdl_col = Rasterizer::NormalColumn; } } else if(has_dotted_col) { const int nrow = new_mdl_row + 1; if(nrow < mdl_rows && nrow != off_row && nrow != bar_row) { new_mdl_col = Rasterizer::DottedColumn; ++new_mdl_row; } } break; case GotoBar: new_raster = _rasterizer->commonRaster(Rasterizer::CommonRasterBar); break; case GotoOff: new_raster = _rasterizer->commonRaster(Rasterizer::CommonRasterOff); break; case Goto1: new_raster = _rasterizer->commonRaster(Rasterizer::CommonRaster1); break; case Goto2: new_raster = _rasterizer->commonRaster(Rasterizer::CommonRaster2); break; case Goto4: new_raster = _rasterizer->commonRaster(Rasterizer::CommonRaster4); break; case Goto8: new_raster = _rasterizer->commonRaster(Rasterizer::CommonRaster8); break; case Goto16: new_raster = _rasterizer->commonRaster(Rasterizer::CommonRaster16); break; case Goto32: new_raster = _rasterizer->commonRaster(Rasterizer::CommonRaster32); break; case Goto64: new_raster = _rasterizer->commonRaster(Rasterizer::CommonRaster64); break; case NoPick: break; } // Was there a new raster? if(new_raster >= 0) return new_raster; // Otherwise was there no new model row and column? if(new_mdl_row == mdl_row && new_mdl_col == mdl_col) return raster; // Return the raster at the new model row and column return rasterAt(new_mdl_row, new_mdl_col); } int RasterizerModel::rowCount(const QModelIndex &/*parent*/) const { const int rast_rows = _rasterizer->rowCount(); // Ignore _maxRows if -1. if(_maxRows >= 0 && rast_rows > _maxRows) return _maxRows; return rast_rows; } int RasterizerModel::columnCount(const QModelIndex &/*parent*/) const { return _modelToRasterColumnList.size(); } QVariant RasterizerModel::data(const QModelIndex &index, int role) const { const int row = index.row(); const int col = index.column(); if(role == RasterTextRole) return textAt(row, col); else if(role == RasterValueRole) return rasterAt(row, col); else if(role == Qt::TextAlignmentRole) { // Special for 'off' and 'bar' rows: In the view we set them to span all columns. // Don't bother aligning if there is only one column (it just looks a little weird). if(columnCount() > 1 && (row == offRow() || row == barRow())) return Qt::AlignCenter; } return QVariant(); } } // namespace MusEGui muse-4.0.0/src/muse/rasterizer.h000066400000000000000000000212421404105311000165550ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // rasterizer.h // Copyright (C) 2020 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __RASTERIZER_H__ #define __RASTERIZER_H__ #include #include #include #include #include #include #include #include #include namespace MusEGui { class Rasterizer : public QObject { private: Q_OBJECT public: enum Column { InvalidColumn = -1, TripletColumn = 0, NormalColumn = 1, DottedColumn = 2 }; enum CommonRasters { CommonRasterBar, CommonRasterOff, CommonRaster1, CommonRaster2, CommonRaster4, CommonRaster8, CommonRaster16, CommonRaster32, CommonRaster64 }; private: int _division; // System midi division (ticks per quarter note) setting. int _rows; // Current number of rows in the raster array. int *_rasterArray; // Two-dimensional array of raster values. -1 = invalid. 0 = Snap to bar. 1 = 'off'. void updateColumn(Column col); signals: // This signal is emitted just before the data will be rebuilt. void dataAboutToBeReset(); // This signal is emitted just after the data has been rebuilt. void dataReset(); public: Rasterizer(int division, QObject *parent = nullptr); ~Rasterizer(); int division() const { return _division; } void setDivision(int div); int rowCount() const { return _rows; } // Returns the number of columns. int columnCount() const; // Rebuilds the raster array. void updateRasterizer(); // Returns the given raster, or returns 1 division (1 quarter note) if no suitable raster could be found. int checkRaster(int val) const; // Returns the index into the array of the given raster value, or -1 if raster not found. int indexOf(int val) const; // Returns raster value at given row and column, or -1 if no raster // at that location, or row or column are out of range. int rasterAt(int row, int col) const; // Returns true if the raster at the given row and column is 0 (snap to bar). bool isBarRaster(int row, int col) const; // Returns true if the raster at the given row and column is 1 ('off'). bool isOffRaster(int row, int col) const; // Returns the row number of the 'bar' row. int barRow() const; // Returns the row number of the 'off' row. int offRow() const; // Returns the raster value of some often-used denominator values. // Returns -1 if the raster is not available. int commonRaster(CommonRasters commonRast) const; // Returns true if the raster at the given row and column is less than // a triple, normal, or dotted version of the given normal raster value. bool isLessThanNormalRaster(int row, int col, int normalRaster) const; // Returns a denominator value for the row, suitable for display (1 2 4 8 16 32 etc.) // Returns zero if the row is a 'bar' row. int rasterDenomAt(int row) const; }; class RasterizerModel : public QAbstractTableModel { private: Q_OBJECT public: enum Roles { RasterTextRole = Qt::DisplayRole, RasterValueRole = Qt::UserRole}; enum DisplayFormat { FractionFormat, DenominatorFormat }; enum RasterPick { NoPick, ToggleTriple, ToggleDotted, ToggleHigherDotted, GotoBar, GotoOff, Goto1, Goto2, Goto4, Goto8, Goto16, Goto32, Goto64 }; private: // The external rasterizer array used in this model. const Rasterizer *_rasterizer; // Maximum number of rows. If set to -1, all rows are included. int _maxRows = 0; // How text is displayed. DisplayFormat _displayFormat; // Lookup lists for model <> raster rows and columns. QList _modelToRasterRowList; QMap _rasterToModelRowMap; QList _modelToRasterColumnList; QMap _rasterToModelColumnMap; QMetaObject::Connection _dataAboutToBeResetConnection; QMetaObject::Connection _dataResetConnection; void updateRows(); // Converts model row to rasterizer row. Returns -1 if row out of bounds. int modelToRasterRow(int row) const; // Converts model column to rasterizer column. // Returns Rasterizer::InvalidColumn if column out of bounds. Rasterizer::Column modelToRasterCol(int col) const; // Returns raster text at given row and column, or empty string if no raster // at that location, or row or column are out of range. QString textAt(int row, int col) const; // Returns raster value at given row and column, or -1 if no raster // at that location, or row or column are out of range. int rasterAt(int row, int col) const; protected slots: void endResetModelHandler(); public: RasterizerModel( Rasterizer *rasterizer, QObject *parent = nullptr, int max_rows = -1, QList visible_columns = QList(), DisplayFormat displayFormat = DenominatorFormat); virtual ~RasterizerModel(); // Required overrides for QAbstractTableModel. int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; // Returns the external Rasterizer array used in this model. const Rasterizer *rasterizer() { return _rasterizer; } // Sets the external Rasterizer array used in this model. void setRasterizer(const Rasterizer *r); // Returns the system midi division that the rasterizer is using. int division() const; // Returns maximum number of rows. If -1, all rows are included. int maxRows() const; // Sets maximum number of rows. If set to -1, all rows are included. void setMaxRows(int rows); // Returns a model-to-raster row list, ie. which raster rows are included in the model. QList visibleRows() const; // Returns the model-to-raster column list, ie. which raster columns are included in the model. QList visibleColumns() const; // Sets the model-to-raster column list, ie. which raster columns are included in the model. void setVisibleColumns(const QList& cols); // How text is displayed. DisplayFormat displayFormat() const; // Sets how text is displayed. void setDisplayFormat(DisplayFormat format); // Returns the model row number of the 'bar' row. int barRow() const; // Returns the model row number of the 'off' row. int offRow() const; // Returns true if the raster at the given row and column is 0 (snap to bar). bool isBarRaster(int row, int col) const; // Returns true if the raster at the given row and column is 1 ('off'). bool isOffRaster(int row, int col) const; // Returns the raster value of some often-used denominator values. // Returns -1 if the raster is not available. int commonRaster(Rasterizer::CommonRasters commonRast) const; // Returns the index into the model of the given raster value, or -1 if raster not found. int indexOfRaster(int val) const; // Returns the model index of the given raster value, or invalid model index if raster not found. QModelIndex modelIndexOfRaster(int val) const; // Returns the given raster, or returns 1 division (1 quarter note) if no suitable raster could be found. int checkRaster(int val) const; // Given a raster, picks another raster based on the RasterPick, for example toggle the triple or dotted // version of raster, or pick a raster from the same column that raster is in. int pickRaster(int raster, RasterPick pick) const; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/remote/000077500000000000000000000000001404105311000155045ustar00rootroot00000000000000muse-4.0.0/src/muse/remote/CMakeLists.txt000066400000000000000000000035721404105311000202530ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( remote_moc_headers pyapi.h ) ## ## List of source files to compile ## file (GLOB remote_source_files pyapi.cpp ) ## ## Define target ## add_library(remote ${MODULES_BUILD} ${remote_moc_headers} ${remote_source_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${remote_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( remote PROPERTIES COMPILE_FLAGS "-I${PYTHON_INCLUDE_DIRS}" OUTPUT_NAME muse_remote ) target_link_libraries(remote widgets # through app.h ${PYTHON_LIBRARIES} ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS remote DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/remote/pyapi.cpp000066400000000000000000001411561404105311000173420ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include "pyapi.h" #include "globaldefs.h" #include "song.h" #include "tempo.h" #include "track.h" #include "audio.h" #include "gconfig.h" #include "midictrl.h" #include "midiport.h" #include "plugin.h" #include "midi_consts.h" #include "app.h" #include "globals.h" #include "undo.h" // Steals ref: PyList_SetItem, PyTuple_SetItem using namespace std; namespace MusECore { PyroServerThread g_pyroServerThread; PyObject* g_pMainModule = nullptr; PyObject* g_pMainDictionary = nullptr; class QPybridgeEvent : public QEvent { public: enum EventType { SONG_UPDATE=0, SONGLEN_CHANGE, SONG_POSCHANGE, SONG_SETPLAY, SONG_SETSTOP, SONG_REWIND, SONG_SETMUTE, SONG_SETCTRL, SONG_SETAUDIOVOL, SONG_IMPORT_PART, SONG_TOGGLE_EFFECT, SONG_ADD_TRACK, SONG_CHANGE_TRACKNAME, SONG_DELETE_TRACK }; QPybridgeEvent( QPybridgeEvent::EventType _type, int _p1=0, int _p2=0, const SongChangedStruct_t& _sc1 = SongChangedStruct_t()); EventType getType() { return type; } int getP1() { return p1; } int getP2() { return p2; } void setS1(QString in) { s1 = in; } void setS2(QString in) { s2 = in; } const QString& getS1() { return s1; } const QString& getS2() { return s2; } double getD1() { return d1; } void setD1(double _d1) { d1 = _d1; } SongChangedStruct_t getSC1() { return sc1; } private: EventType type; int p1, p2; double d1; QString s1; QString s2; SongChangedStruct_t sc1; }; //------------------------------------------------------------ QPybridgeEvent::QPybridgeEvent(QPybridgeEvent::EventType _type, int _p1, int _p2, const SongChangedStruct_t& _sc1) :QEvent(QEvent::User), type(_type), p1(_p1), p2(_p2), sc1(_sc1) { } //------------------------------------------------------------ // Get current position //------------------------------------------------------------ PyObject* getCPos(PyObject*, PyObject*) { return Py_BuildValue("i", MusEGlobal::song->cpos()); } //------------------------------------------------------------ // Get position of left locator //------------------------------------------------------------ PyObject* getLPos(PyObject*, PyObject*) { return Py_BuildValue("i", MusEGlobal::song->lpos()); } //------------------------------------------------------------ // Get position of right locator //------------------------------------------------------------ PyObject* getRPos(PyObject*, PyObject*) { return Py_BuildValue("i", MusEGlobal::song->rpos()); } //------------------------------------------------------------ // Start playing from current position //------------------------------------------------------------ PyObject* startPlay(PyObject*, PyObject*) { //MusEGlobal::song->setPlay(true); QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_SETPLAY); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // Stop playing //------------------------------------------------------------ PyObject* stopPlay(PyObject*, PyObject*) { //MusEGlobal::song->setStop(true); QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_SETSTOP); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // Rewind to start //------------------------------------------------------------ PyObject* rewindStart(PyObject*, PyObject*) { //MusEGlobal::song->rewindStart(); QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_REWIND); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // Get tempo at specific position //------------------------------------------------------------ PyObject* getTempo(PyObject*, PyObject* args) { int tick; if (!PyArg_ParseTuple(args, "i", &tick)) { return Py_BuildValue("i", 1000); } int tempovalue = MusEGlobal::tempomap.tempo(tick); return Py_BuildValue("i", tempovalue); } //------------------------------------------------------------ // Get track names //------------------------------------------------------------ PyObject* getTrackNames(PyObject*, PyObject*) { TrackList* tracks = MusEGlobal::song->tracks(); PyObject* res = Py_BuildValue("[]"); for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) { Track* track = *t; PyObject* ptrackname = Py_BuildValue("s", track->name().toLatin1().constData()); PyList_Append(res, ptrackname); Py_DECREF(ptrackname); } return res; } //------------------------------------------------------------ // Find part by serial nr //------------------------------------------------------------ Part* findPartBySerial(int sn) { TrackList* tracks = MusEGlobal::song->tracks(); for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) { Track* track = *t; PartList* parts = track->parts(); for (ciPart p = parts->begin(); p != parts->end(); p++) { Part* part = p->second; if (part->sn() == sn) return part; } } return NULL; } //------------------------------------------------------------ // Get parts from track //------------------------------------------------------------ PyObject* getParts(PyObject*, PyObject* args) { TrackList* tracks = MusEGlobal::song->tracks(); const char* trackname; if (!PyArg_ParseTuple(args, "s", &trackname)) { Py_RETURN_NONE; } PyObject* pyparts = Py_BuildValue("[]"); for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) { Track* track = *t; if (track->name() != trackname) continue; PartList* parts = track->parts(); for (ciPart p = parts->begin(); p != parts->end(); p++) { Part* part = p->second; MidiPart* mpart = (MidiPart*) part; PyObject* pypart = PyDict_New(); int tick = mpart->tick(); int lentick = mpart->lenTick(); int serialnr = mpart->sn(); PyObject* pstrtick = Py_BuildValue("s","tick"); PyObject* pitick = Py_BuildValue("i", tick); PyObject* pstrid = Py_BuildValue("s","id"); PyObject* pstrserial = Py_BuildValue("i", serialnr); PyObject* pstrlen = Py_BuildValue("s","len"); PyObject* pstrtick2 = Py_BuildValue("i", lentick); PyDict_SetItem(pypart, pstrtick, pitick); PyDict_SetItem(pypart, pstrid, pstrserial); PyDict_SetItem(pypart, pstrlen, pstrtick2); Py_DECREF(pstrtick); Py_DECREF(pitick); Py_DECREF(pstrid); Py_DECREF(pstrserial); Py_DECREF(pstrlen); Py_DECREF(pstrtick2); // Pack midi events into list before wrapping it all up const EventList& events = mpart->events(); PyObject* pyevents = Py_BuildValue("[]"); for (ciEvent e = events.begin(); e != events.end(); e++) { PyObject* pyevent = PyDict_New(); // The event structure - a dictionary with keys 'type','tick','data' const Event& event = e->second; unsigned tick = e->first; PyObject* eventdata = Py_BuildValue("[i,i,i]", event.dataA(), event.dataB(), event.dataC()); PyObject* pstrdata = Py_BuildValue("s", "data"); pstrtick = Py_BuildValue("s", "tick"); PyObject* pitickval = Py_BuildValue("i", tick); PyDict_SetItem(pyevent, pstrdata, eventdata); PyDict_SetItem(pyevent, pstrtick, pitickval); Py_DECREF(eventdata); Py_DECREF(pstrdata); Py_DECREF(pstrtick); Py_DECREF(pitickval); switch(event.type()) { case Note: { PyObject* pstrtype = Py_BuildValue("s", "type"); PyObject* pstrnote = Py_BuildValue("s", "note"); PyObject* pstrlen = Py_BuildValue("s", "len"); PyObject* pilentick = Py_BuildValue("i", event.lenTick()); PyDict_SetItem(pyevent, pstrtype, pstrnote); PyDict_SetItem(pyevent, pstrlen, pilentick); Py_DECREF(pstrtype); Py_DECREF(pstrnote); Py_DECREF(pstrlen); Py_DECREF(pilentick); break; } case Controller: { PyObject* pstrtype = Py_BuildValue("s", "type"); PyObject* pstrctrl = Py_BuildValue("s", "ctrl"); PyDict_SetItem(pyevent, pstrtype, pstrctrl); Py_DECREF(pstrtype); Py_DECREF(pstrctrl); break; } default: printf("Event type not supported yet: %d\n", event.type()); break; } PyList_Append(pyevents, pyevent); Py_DECREF(pyevent); } // Add the event list to the pypart dictionary PyObject* pystrevents = Py_BuildValue("s", "events"); PyDict_SetItem(pypart, pystrevents, pyevents); Py_DECREF(pyevents); Py_DECREF(pystrevents); PyList_Append(pyparts, pypart); Py_DECREF(pypart); } return pyparts; } Py_RETURN_NONE; } //------------------------------------------------------------ // parsePythonPart // get part id/serialno from python part structure //------------------------------------------------------------ int getPythonPartId(PyObject* part) { PyObject* pyid = PyDict_GetItemString(part, "id"); int id = PyLong_AsLong(pyid); return id; } //------------------------------------------------------------ // addPyPartEventsToMusePart // parse events from python part structure into muse part //------------------------------------------------------------ bool addPyPartEventsToMusePart(MidiPart* npart, PyObject* part) { PyObject* events; if (PyDict_Check(part) == false) { printf("Not a dict!\n"); return false; } PyObject* pstrevents = Py_BuildValue("s","events"); if (PyDict_Contains(part, pstrevents) == false) { Py_DECREF(pstrevents); printf("No events in part data...\n"); return false; } Py_DECREF(pstrevents); events = PyDict_GetItemString(part, "events"); if (PyList_Check(events) == false) { printf("Events not a list!\n"); return false; } // // Go through event list, create MusE events of them and add to new part // Py_ssize_t len = PyList_Size(events); for (Py_ssize_t i=0; i= 4) || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 3) const char* bytes = PyUnicode_AsUTF8(p_type); #else PyObject* p_bytes = PyUnicode_AsUTF8String(p_type); const char* bytes = PyBytes_AsString(p_bytes); #endif if(bytes && bytes[0] != 0) { string type = string(bytes); int data[3]; // Traverse data list: for (int j=0; j<3; j++) { PyObject* plItem = PyList_GetItem(p_data, j); data[j] = PyLong_AsLong (plItem); } if (type == "note" || type == "ctrl") { Event event(Note); event.setA(data[0]); event.setB(data[1]); event.setC(data[2]); event.setTick(etick); event.setLenTick(elen); npart->addEvent(event); } else printf("Unhandled event type from python: %s\n", type.c_str()); } } return true; } //------------------------------------------------------------ // Create a new part at a particular tick and track //------------------------------------------------------------ PyObject* createPart(PyObject*, PyObject* args) { const char* trackname; unsigned tick, tickLen; PyObject* part; if (!PyArg_ParseTuple(args, "siiO", &trackname, &tick, &tickLen, &part)) { Py_RETURN_NONE; } QString qtrackname(trackname); Track* t = MusEGlobal::song->findTrack(trackname); if (t == NULL || t->isMidiTrack() == false) { Py_RETURN_NONE; } MidiTrack* track = (MidiTrack*)t; MidiPart* npart = new MidiPart(track); npart->setTick(tick); npart->setLenTick(tickLen); addPyPartEventsToMusePart(npart, part); MusEGlobal::song->addPart(npart); QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, 0, 0, SC_TRACK_MODIFIED); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // Modify a particular part: // args: new part data, old part data is used from the part with the same id as the one sent here // TODO: Lots and lots of refcount stuff //------------------------------------------------------------ PyObject* modifyPart(PyObject*, PyObject* part) { int id = getPythonPartId(part); Part* opart = NULL; // Verify a part with that id actually exists, then get it TrackList* tracks = MusEGlobal::song->tracks(); for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) { Track* track = *t; for (ciPart p = track->parts()->begin(); p != track->parts()->end(); p++) { if (p->second->sn() == id) { opart = p->second; break; } } } if (opart == NULL) { printf("Part doesn't exist!\n"); Py_RETURN_NONE; } // Remove all note and controller events from current part eventlist std::list< std::pair > elist; MidiPart* npart = new MidiPart((MidiTrack*)opart->track()); npart->setTick(opart->tick()); npart->setLenTick(opart->lenTick()); npart->setSn(opart->sn()); for (ciEvent e = opart->events().begin(); e != opart->events().end(); e++) { Event& event = (Event &)(e->second); if (event.type() == Note || event.type() == Controller) continue; npart->addEvent(event); } addPyPartEventsToMusePart(npart, part); //MusEGlobal::song->startUndo(); MusEGlobal::song->changePart(opart, npart); //MusEGlobal::song->endUndo(SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED); // Crash! Probably since the call ends up in Qt GUI thread from this thread QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, 0, 0, SC_TRACK_MODIFIED | SC_PART_MODIFIED | SC_PART_INSERTED); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // deletePart // delete part by serial nr //------------------------------------------------------------ PyObject* deletePart(PyObject*, PyObject* args) { int id; if (!PyArg_ParseTuple(args, "i", &id)) { Py_RETURN_NONE; } Part* part = findPartBySerial(id); if (part == NULL) { Py_RETURN_NONE; } MusEGlobal::song->removePart(part); QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, 0, 0, SC_TRACK_MODIFIED | SC_PART_REMOVED); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // setPos //------------------------------------------------------------ PyObject* setPos(PyObject*, PyObject* args) { int index; int ticks; if (!PyArg_ParseTuple(args, "ii", &index, &ticks)) { Py_RETURN_NONE; } //MusEGlobal::song->setPos(index, ticks); QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_POSCHANGE, index, ticks); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // setLen //------------------------------------------------------------ PyObject* setSongLen(PyObject*, PyObject* args) { unsigned len; if (!PyArg_ParseTuple(args, "i", &len)) { Py_RETURN_NONE; } //MusEGlobal::song->setLen(len);// Appears to not be ok to call from python thread, we do it with event instead QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONGLEN_CHANGE, len); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // getLen //------------------------------------------------------------ PyObject* getSongLen(PyObject*, PyObject*) { PyObject* pylen = Py_BuildValue("i", MusEGlobal::song->len()); return pylen; } //------------------------------------------------------------ // getDivision //------------------------------------------------------------ PyObject* getDivision(PyObject*, PyObject*) { return Py_BuildValue("i", MusEGlobal::config.division); } //------------------------------------------------------------ // setTrackParameter //------------------------------------------------------------ PyObject* setMidiTrackParameter(PyObject*, PyObject* args) { const char* trackname; const char* paramname; int value; if(!PyArg_ParseTuple(args, "ssi", &trackname, ¶mname, &value)) { Py_RETURN_NONE; } Track* track = MusEGlobal::song->findTrack(QString(trackname)); if (track == NULL || track->isMidiTrack() == false) { Py_RETURN_NONE; } MidiTrack* mt = (MidiTrack*) track; QString qparamname(paramname); bool changed = false; if (qparamname == "velocity") { changed = true; mt->velocity = value; } else if (qparamname == "compression") { changed = true; mt->compression = value; } else if (qparamname == "transposition") { changed = true; mt->transposition = value; } else if (qparamname == "delay") { changed = true; mt->delay = value; } if (changed) { QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, 0, 0, SC_TRACK_MODIFIED); QApplication::postEvent(MusEGlobal::song, pyevent); } return Py_BuildValue("b", changed); // true/false depending on whether anythin was changed } //------------------------------------------------------------ // Set loop //------------------------------------------------------------ PyObject* setLoop(PyObject*, PyObject* args) { bool loopFlag; if(!PyArg_ParseTuple(args, "b", &loopFlag)) { Py_RETURN_NONE; } MusEGlobal::song->setLoop(loopFlag); Py_RETURN_NONE; } //------------------------------------------------------------ // Get loop value //------------------------------------------------------------ PyObject* getLoop(PyObject*, PyObject*) { return Py_BuildValue("b", MusEGlobal::song->getLoop()); } //------------------------------------------------------------ // getMute trackname //------------------------------------------------------------ PyObject* getMute(PyObject*, PyObject* args) { const char* trackname; if (!PyArg_ParseTuple(args, "s", &trackname)) { Py_RETURN_NONE; } Track* track = MusEGlobal::song->findTrack(QString(trackname)); if (track == NULL) { Py_RETURN_NONE; } return Py_BuildValue("b", track->isMute()); } //------------------------------------------------------------ // setMute (trackname, boolean) //------------------------------------------------------------ PyObject* setMute(PyObject*, PyObject* args) { const char* trackname; bool muted; if (!PyArg_ParseTuple(args, "sb", &trackname, &muted)) { Py_RETURN_NONE; } Track* track = MusEGlobal::song->findTrack(QString(trackname)); if (track == NULL) { Py_RETURN_NONE; } int mutedint = 1; if (muted == false) mutedint = 0; QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_SETMUTE, mutedint); pyevent->setS1(trackname); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // setController //------------------------------------------------------------ void setController(const char* trackname, int ctrltype, int ctrlval) { QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_SETCTRL, ctrltype, ctrlval); pyevent->setS1(trackname); QApplication::postEvent(MusEGlobal::song, pyevent); } //------------------------------------------------------------ // setMidiControllerValue //------------------------------------------------------------ PyObject* setMidiControllerValue(PyObject*, PyObject* args) { const char* trackname; int ctrltype; int value; if (!PyArg_ParseTuple(args, "sii", &trackname, &ctrltype, &value)) { Py_RETURN_NONE; } setController(trackname, ctrltype, value); Py_RETURN_NONE; } //------------------------------------------------------------ // getMidiControllerValue //------------------------------------------------------------ PyObject* getMidiControllerValue(PyObject*, PyObject* args) { const char* trackname; int ctrltype; if (!PyArg_ParseTuple(args, "si", &trackname, &ctrltype)) { Py_RETURN_NONE; } Track* t = MusEGlobal::song->findTrack(QString(trackname)); if (t == NULL) { Py_RETURN_NONE; } if (t->isMidiTrack() == false) { Py_RETURN_NONE; } MidiTrack* track = (MidiTrack*) t; int channel = track->outChannel(); int outport = track->outPort(); MidiPort* mp = &MusEGlobal::midiPorts[outport]; if (mp == NULL) return Py_BuildValue("i", -1); int value = mp->hwCtrlState(channel, ctrltype); return Py_BuildValue("i", value); } //------------------------------------------------------------ // setAudioTrackVolume //------------------------------------------------------------ PyObject* setAudioTrackVolume(PyObject*, PyObject* args) { const char* trackname; double volume = 0.0f; if (!PyArg_ParseTuple(args, "sd", &trackname, &volume)) { Py_RETURN_NONE; } QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_SETAUDIOVOL); pyevent->setD1(volume); pyevent->setS1(trackname); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // getAudioTrackVolume //------------------------------------------------------------ PyObject* getAudioTrackVolume(PyObject*, PyObject* args) { const char* trackname; if (!PyArg_ParseTuple(args, "s", &trackname)) { Py_RETURN_NONE; } Track* t = MusEGlobal::song->findTrack(QString(trackname)); if (t == NULL) { Py_RETURN_NONE; } //if (t->type() == Track::DRUM || t->type() == Track::MIDI || t->type() == Track::NEW_DRUM) if (t->isMidiTrack()) { // changed by flo. should do the same thing and is better maintainable Py_RETURN_NONE; } AudioTrack* track = (AudioTrack*) t; return Py_BuildValue("d", track->volume()); } //------------------------------------------------------------ // getSelectedTrack //------------------------------------------------------------ PyObject* getSelectedTrack(PyObject*, PyObject*) { TrackList* tracks = MusEGlobal::song->tracks(); for (ciTrack t = tracks->begin(); t != tracks->end(); ++t) { Track* track = *t; if (track->selected()) return Py_BuildValue("s", track->name().toLatin1().constData()); } Py_RETURN_NONE; } //------------------------------------------------------------ // importPart //------------------------------------------------------------ PyObject* importPart(PyObject*, PyObject* args) { const char* trackname; const char* filename; int tick; if (!PyArg_ParseTuple(args, "ssi", &trackname, &filename, &tick)) { Py_RETURN_NONE; } QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_IMPORT_PART, tick); pyevent->setS1(trackname); pyevent->setS2(filename); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // getTrackEffects //------------------------------------------------------------ PyObject* getTrackEffects(PyObject*, PyObject* args) { const char* trackname; if (!PyArg_ParseTuple(args, "s", &trackname)) { Py_RETURN_NONE; } Track* t = MusEGlobal::song->findTrack(QString(trackname)); if (t == NULL) { Py_RETURN_NONE; } if (t->type() != Track::WAVE) { Py_RETURN_NONE; } AudioTrack* track = (AudioTrack*) t; PyObject* pyfxnames = Py_BuildValue("[]"); const Pipeline* pipeline = track->efxPipe(); for (int i = 0; i < MusECore::PipelineDepth; i++) { QString name = pipeline->name(i); printf("fx %d name: %s\n", i, name.toLatin1().constData()); PyObject* pyname = Py_BuildValue("s", name.toLatin1().constData()); PyList_Append(pyfxnames, pyname); Py_DECREF(pyname); } return pyfxnames; } //------------------------------------------------------------ // toggleTrackEffect //------------------------------------------------------------ PyObject* toggleTrackEffect(PyObject*, PyObject* args) { const char* trackname; int fxid; bool onoff; if (!PyArg_ParseTuple(args, "sib", &trackname, &fxid, &onoff)) { Py_RETURN_NONE; } Track* t = MusEGlobal::song->findTrack(QString(trackname)); if (t == NULL) { Py_RETURN_NONE; } if (t->type() != Track::WAVE) { Py_RETURN_NONE; } QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_TOGGLE_EFFECT, fxid, onoff); pyevent->setS1(trackname); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // changeTrackName //------------------------------------------------------------ PyObject* changeTrackName(PyObject*, PyObject* args) { const char* trackname; const char* newname; if (!PyArg_ParseTuple(args, "ss", &trackname, &newname)) { Py_RETURN_NONE; } Track* t = MusEGlobal::song->findTrack(QString(trackname)); if (t == NULL) return Py_BuildValue("b", false); QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_CHANGE_TRACKNAME); pyevent->setS1(trackname); pyevent->setS2(newname); QApplication::postEvent(MusEGlobal::song, pyevent); QPybridgeEvent* pyevent2 = new QPybridgeEvent(QPybridgeEvent::SONG_UPDATE, 0, 0, SC_TRACK_MODIFIED); QApplication::postEvent(MusEGlobal::song, pyevent2); return Py_BuildValue("b", true); } //------------------------------------------------------------ // addMidiTrack //------------------------------------------------------------ PyObject* addMidiTrack(PyObject*, PyObject*) { QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_ADD_TRACK, Track::MIDI); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // addWaveTrack //------------------------------------------------------------ PyObject* addWaveTrack(PyObject*, PyObject*) { QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_ADD_TRACK, Track::WAVE); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // addInput //------------------------------------------------------------ PyObject* addInput(PyObject*, PyObject*) { QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_ADD_TRACK, Track::AUDIO_INPUT); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // addOutput //------------------------------------------------------------ PyObject* addOutput(PyObject*, PyObject*) { QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_ADD_TRACK, Track::AUDIO_OUTPUT); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // addGroup //------------------------------------------------------------ PyObject* addGroup(PyObject*, PyObject*) { QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_ADD_TRACK, Track::AUDIO_GROUP); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // deleteTrack //------------------------------------------------------------ PyObject* deleteTrack(PyObject*, PyObject* args) { const char* trackname; if (!PyArg_ParseTuple(args, "s", &trackname)) { Py_RETURN_NONE; } QPybridgeEvent* pyevent = new QPybridgeEvent(QPybridgeEvent::SONG_DELETE_TRACK); pyevent->setS1(trackname); QApplication::postEvent(MusEGlobal::song, pyevent); Py_RETURN_NONE; } //------------------------------------------------------------ // getOutputRoute //------------------------------------------------------------ /* PyObject* getOutputRoute(PyObject*, PyObject* args) { const char* trackname; if (!PyArg_ParseTuple(args, "s", &trackname)) { Py_RETURN_NONE; } Track* tt = MusEGlobal::song->findTrack(QString(trackname)); if (tt == NULL) return Py_BuildValue("b", false); PyObject* routes = Py_BuildValue("[]"); if (tt->type() == Track::WAVE && tt->type() == Track::AUDIO_AUX) { AudioTrack* t = (AudioTrack*)tt; RouteList* r = t->outRoutes(); OutputList* al = MusEGlobal::song->outputs(); for (iAudioOutput i = al->begin(); i != al->end(); ++i) { Track* track = *i; if (t == track) continue; QString s(track->name()); // for (iRoute ir = r->begin(); ir != r->end(); ++ir) { // if (ir->type == 0 && ir->track == track) { // s += "*"; // PyList_Append(routes, Py_BuildValue("s", s.toLatin1())); // break; // } // } // } } else if (tt->type() == Track::AUDIO_OUTPUT) { } return routes; } */ //------------------------------------------------------------ // Get user supplied Pyro nameserver host name //------------------------------------------------------------ PyObject* getConfigPyroNSHostname(PyObject*, PyObject*) { return Py_BuildValue("s", MusEGlobal::pythonBridgePyroNSHostname.toLatin1().constData()); } //------------------------------------------------------------ // Get user supplied Pyro nameserver host port //------------------------------------------------------------ PyObject* getConfigPyroNSPort(PyObject*, PyObject*) { return Py_BuildValue("s", MusEGlobal::pythonBridgePyroNSPort.toLatin1().constData()); } //------------------------------------------------------------ // Get user supplied Pyro daemon host name //------------------------------------------------------------ PyObject* getConfigPyroDaemonHostname(PyObject*, PyObject*) { return Py_BuildValue("s", MusEGlobal::pythonBridgePyroDaemonHostname.toLatin1().constData()); } //------------------------------------------------------------ // Get user supplied Pyro daemon host port //------------------------------------------------------------ PyObject* getConfigPyroDaemonPort(PyObject*, PyObject*) { return Py_BuildValue("s", MusEGlobal::pythonBridgePyroDaemonPort.toLatin1().constData()); } //------------------------------------------------------------ // Get user supplied Pyro communication timeout //------------------------------------------------------------ PyObject* getConfigPyroCommTimeout(PyObject*, PyObject*) { return Py_BuildValue("f", MusEGlobal::pythonBridgePyroCommTimeout); } //------------------------------------------------------------ // Check whether the server should run (True) or stop //------------------------------------------------------------ PyObject* serverRunFlag(PyObject*, PyObject*) { return Py_BuildValue("b", g_pyroServerThread.serverRunFlag()); } //------------------------------------------------------------ // Global method definitions for MusE:s Python API // // This is where global functions in Python is linked to their equivalent C/C++ functions //------------------------------------------------------------ PyMethodDef g_methodDefinitions[] = { { "getConfigPyroNSHostname", getConfigPyroNSHostname, METH_NOARGS, "Get configured Pyro nameserver host name" }, { "getConfigPyroNSPort", getConfigPyroNSPort, METH_NOARGS, "Get configured Pyro nameserver host port" }, { "getConfigPyroDaemonHostname", getConfigPyroDaemonHostname, METH_NOARGS, "Get configured Pyro daemon host name" }, { "getConfigPyroDaemonPort", getConfigPyroDaemonPort, METH_NOARGS, "Get configured Pyro daemon host port" }, { "getConfigPyroCommTimeout", getConfigPyroCommTimeout, METH_NOARGS, "Get configured Pyro communication timeout" }, { "serverRunFlag", serverRunFlag, METH_NOARGS, "Check whether the server should run (True) or stop" }, { "startPlay", startPlay, METH_VARARGS, "Starts playing the song from current position" }, { "stopPlay", stopPlay, METH_VARARGS, "Stops playback if currently playing" }, { "rewindStart", rewindStart, METH_VARARGS, "Set current position to beginning of song" }, { "getCPos", getCPos, METH_NOARGS, "Get current position (in ticks)" }, { "getLPos", getLPos, METH_NOARGS, "Get position of left locator (in ticks)" }, { "getRPos", getRPos, METH_NOARGS, "Get position of right locator (in ticks)" }, { "setPos", setPos, METH_VARARGS, "Set position of locators or current position" }, { "getTempo", getTempo, METH_VARARGS, "Get tempo of the song at a particular tick" }, { "setLoop", setLoop, METH_VARARGS, "Set loop mode on/off" }, { "getLoop", getLoop, METH_NOARGS, "Get loop value" }, { "getTrackNames", getTrackNames, METH_VARARGS, "Get track names (which are unique)" }, { "getParts", getParts, METH_VARARGS, "Get part data from a track" }, { "createPart", createPart, METH_VARARGS, "Create a part" }, { "modifyPart", modifyPart, METH_O, "Modify a particular part" }, { "deletePart", deletePart, METH_VARARGS, "Remove part with a particular serial nr" }, { "getSelectedTrack", getSelectedTrack, METH_NOARGS, "Get first selected track" }, { "importPart", importPart, METH_VARARGS, "Import part file to a track at a particular position" }, { "changeTrackName", changeTrackName, METH_VARARGS, "Change track name" }, { "addMidiTrack", addMidiTrack, METH_NOARGS, "Add a midi track" }, { "addWaveTrack", addWaveTrack, METH_NOARGS, "Add a wave track" }, { "addInput", addInput, METH_NOARGS, "Add audio input" }, { "addOutput", addOutput, METH_NOARGS, "Add audio output" }, { "addGroup", addGroup, METH_NOARGS, "Add audio group" }, { "deleteTrack", deleteTrack, METH_VARARGS, "Delete a track" }, { "getTrackEffects", getTrackEffects, METH_VARARGS, "Get names of LADSPA effects on a track" }, { "toggleTrackEffect", toggleTrackEffect, METH_VARARGS, "Toggle LADSPA effect on/off" }, //{ "getOutputRoute", getOutputRoute, METH_VARARGS, "Get route for an audio output" }, { "setSongLen", setSongLen, METH_VARARGS, "Set length of song (in ticks)" }, { "getSongLen", getSongLen, METH_VARARGS, "Get length of song (in ticks)" }, { "getMute", getMute, METH_VARARGS, "Get track mute property (if track is played or not)" }, { "setMute", setMute, METH_VARARGS, "Set track mute property (if track should be played or not)" }, { "setMidiControllerValue", setMidiControllerValue, METH_VARARGS, "Set midi controller value for a track" }, { "getMidiControllerValue", getMidiControllerValue, METH_VARARGS, "Get midi controller value for a track" }, { "setAudioTrackVolume", setAudioTrackVolume, METH_VARARGS, "Set volume on audio track/aux/output/input" }, { "getAudioTrackVolume", getAudioTrackVolume, METH_VARARGS, "Get audio track/aux/output/input volume" }, { "setMidiTrackParameter", setMidiTrackParameter, METH_VARARGS, "Set transposition, velocity, compression or delay on track level" }, { "getDivision", getDivision, METH_VARARGS, "Number of ticks per 1/4 (?)" }, {NULL, NULL, 0, NULL} }; bool PyroServerThread::initServer() { _runServer = false; const char* mod_name = "muse"; Py_Initialize(); // For Python 3.5 and above: #if (PY_MAJOR_VERSION >= 4) || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 5) PyObject *pMuseModule = PyImport_AddModule(mod_name); PyModule_AddFunctions(pMuseModule, g_methodDefinitions); // For Python 3.0 to 3.4: #elif PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION < 5 PyObject *m = PyImport_AddModule(mod_name); // Stupid HACK required. Tested OK. Borrowed from and based on these: // http://code.activestate.com/recipes/579110-add-function-to-pythons-__builtin__-module-through/ // https://stackoverflow.com/questions/6565175/adding-new-command-to-module-through-c-api PyObject *po_mod_dict = PyModule_GetDict(m); PyObject *po_mod_name = PyUnicode_FromString(mod_name); PyObject *po_new_func; int i = 0; while(g_methodDefinitions[i].ml_name && g_methodDefinitions[i].ml_meth) { po_new_func = PyCFunction_NewEx(&g_methodDefinitions[i], (PyObject*)NULL, po_mod_name); PyDict_SetItemString(po_mod_dict, g_methodDefinitions[i].ml_name, po_new_func); ++i; } // For Python below 3.0: #else // Life was simple, eh? Py_InitModule( mod_name, g_methodDefinitions ); #endif // // Access the "__main__" module and its name-space dictionary. // g_pMainModule = PyImport_AddModule( "__main__" ); g_pMainDictionary = PyModule_GetDict( g_pMainModule ); return true; } /** * This function launches the Pyro name service, which blocks execution * Thus it needs its own thread **/ void PyroServerThread::run() { if(!g_pMainDictionary) return; _runServer = true; string launcherfilename = string(SHAREDIR) + string("/pybridge/museplauncher.py"); printf("Initiating MusE Pybridge launcher from %s\n", launcherfilename.c_str()); FILE* fp = fopen(launcherfilename.c_str(),"r"); if(!fp) { printf("MusE Pybridge open launcher file failed\n"); } else { PyObject* p_res = PyRun_File(fp, launcherfilename.c_str(), Py_file_input, g_pMainDictionary, g_pMainDictionary); if(p_res == NULL) { printf("MusE Pybridge initialization failed\n"); PyErr_Print(); } fclose(fp); printf("MusE Pybridge finished\n"); } } void PyroServerThread::stop() { // Flag the server to stop, when it eventually gets around to reading this... _runServer = false; } /** * This function currently only launches the thread. There should be some kind of check that * things are up and running as they are supposed to */ bool startPythonBridge() { g_pyroServerThread.initServer(); g_pyroServerThread.start(); return true; } bool stopPythonBridge() { g_pyroServerThread.stop(); // Be sure to set this to something higher than what the daemon will use, // so that the thread does not timeout before the daemon does. Say, * 1.5. // In units of ms. return g_pyroServerThread.wait(1500.0 * MusEGlobal::pythonBridgePyroCommTimeout); } //--------------------------------------------------------- // event // // Function in Song class, run in the Qt event thread context. // Handles events sent from the Python bridge subsystem // // This is part of Qt:s event handling and events are fed // here via QApplication::postEvent since gui updates should // be done by Qt:s GUI thread. QApplication::postEvent is // a static method, which is threadsafe. Using the song object // from the Python thread is dangerous when it comes to // operations that manipulate the gui itself (read is ok) //--------------------------------------------------------- bool Song::event(QEvent* _e) { if (_e->type() != QEvent::User) return false; //ignore all events except user events, which are events from Python bridge subsystem QPybridgeEvent* e = (QPybridgeEvent*) _e; switch (e->getType()) { case QPybridgeEvent::SONG_UPDATE: this->update(e->getSC1()); break; case QPybridgeEvent::SONGLEN_CHANGE: this->setLen(e->getP1()); break; case QPybridgeEvent::SONG_POSCHANGE: this->setPos(POSTYPE(e->getP1()), e->getP2()); break; case QPybridgeEvent::SONG_SETPLAY: this->setPlay(true); break; case QPybridgeEvent::SONG_SETSTOP: this->setStop(true); break; case QPybridgeEvent::SONG_REWIND: this->rewindStart(); break; case QPybridgeEvent::SONG_SETMUTE: { Track* track = this->findTrack(e->getS1()); if (track == NULL) return false; bool muted = e->getP1() == 1; // No undo. // Operation is undoable but do not start/end undo. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetTrackMute, track, muted), MusECore::Song::OperationUndoableUpdate); break; } case QPybridgeEvent::SONG_SETCTRL: { Track* t = this->findTrack(e->getS1()); if (t == NULL) return false; if (t->isMidiTrack() == false) return false; MidiTrack* track = (MidiTrack*) t; int chan = track->outChannel(); int num = e->getP1(); int val = e->getP2(); // Schedule for immediate playback. MidiPlayEvent ev(0, track->outPort(), chan, ME_CONTROLLER, num, val); MusEGlobal::audio->msgPlayMidiEvent(&ev); break; } case QPybridgeEvent::SONG_SETAUDIOVOL: { Track* t = this->findTrack(e->getS1()); if (t == NULL) return false; if (t->isMidiTrack()) // changed by flo. is better maintainable //if (t->type() == Track::DRUM || t->type() == Track::NEW_DRUM || t->type() == Track::MIDI) return false; AudioTrack* track = (AudioTrack*) t; track->setVolume(e->getD1()); break; } case QPybridgeEvent::SONG_IMPORT_PART: { Track* track = this->findTrack(e->getS1()); QString filename = e->getS2(); unsigned int tick = e->getP1(); if (track == NULL) return false; MusEGlobal::muse->importPartToTrack(filename, tick, track); break; } case QPybridgeEvent::SONG_TOGGLE_EFFECT: { Track* t = this->findTrack(e->getS1()); if (t == NULL) return false; if (t->type() != Track::WAVE) return false; int fxid = e->getP1(); if (fxid > PipelineDepth) return false; int onoff = (e->getP2() == 1); AudioTrack* track = (AudioTrack*) t; Pipeline* pipeline = track->efxPipe(); pipeline->setOn(fxid, onoff); break; } case QPybridgeEvent::SONG_ADD_TRACK: { MusEGlobal::song->addTrack((Track::TrackType)e->getP1()); // Add at end of list. break; } case QPybridgeEvent::SONG_CHANGE_TRACKNAME: { Track* t = this->findTrack(e->getS1()); if (t == NULL) return false; t->setName(e->getS2()); break; } case QPybridgeEvent::SONG_DELETE_TRACK: { Track* t = this->findTrack(e->getS1()); if (t == NULL) return false; MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(t), t)); break; } default: printf("Unknown pythonthread event received: %d\n", e->getType()); break; } return true; } } // namespace MusECore muse-4.0.0/src/muse/remote/pyapi.h000066400000000000000000000026431404105311000170040ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef PYAPI_H #define PYAPI_H #include #include namespace MusECore { class PyroServerThread : public QThread { Q_OBJECT bool _runServer; public slots: void run() override; void stop(); public: PyroServerThread(QObject *parent = nullptr) : QThread(parent), _runServer(false) { } bool initServer(); bool serverRunFlag() const { return _runServer; } }; bool startPythonBridge(); bool stopPythonBridge(); } // namespace MusECore #endif muse-4.0.0/src/muse/route.cpp000066400000000000000000002455311404105311000160650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: route.cpp,v 1.18.2.3 2008/05/21 00:28:52 terminator356 Exp $ // // (C) Copyright 2003-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011, 2015 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "song.h" #include "route.h" #include "audio.h" #include "track.h" #include "synth.h" #include "audiodev.h" #include "xml.h" #include "mididev.h" #include "midiport.h" #include "icons.h" #include "driver/jackmidi.h" #include "driver/alsamidi.h" #include "strntcpy.h" #include "globals.h" //#define ROUTE_DEBUG // Undefine if and when multiple output routes are added to midi tracks. #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ namespace MusECore { const QString ROUTE_MIDIPORT_NAME_PREFIX = "MusE MidiPort "; //--------------------------------------------------------- // Route //--------------------------------------------------------- Route::Route(void* t, int ch) { jackPort = t; persistentJackPortName[0] = 0; if(MusEGlobal::checkAudioDevice()) MusEGlobal::audioDevice->portName(jackPort, persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); midiPort = -1; channel = ch; channels = -1; remoteChannel = -1; type = JACK_ROUTE; } Route::Route(Track* t, int ch, int chans) { track = t; midiPort = -1; channel = ch; channels = chans; remoteChannel = -1; type = TRACK_ROUTE; persistentJackPortName[0] = 0; } Route::Route(MidiDevice* d, int ch) { device = d; midiPort = -1; channel = ch; channels = -1; remoteChannel = -1; type = MIDI_DEVICE_ROUTE; persistentJackPortName[0] = 0; } Route::Route(int port, int ch) { track = 0; midiPort = port; channel = ch; channels = -1; remoteChannel = -1; type = MIDI_PORT_ROUTE; persistentJackPortName[0] = 0; } Route::Route(const QString& s, bool dst, int ch, int rtype) { Route node(name2route(s, dst, rtype)); channel = node.channel; if(channel == -1) channel = ch; channels = node.channels; remoteChannel = node.remoteChannel; type = node.type; persistentJackPortName[0] = 0; if(type == TRACK_ROUTE) { track = node.track; midiPort = -1; } else if(type == JACK_ROUTE) { jackPort = node.jackPort; char* res = 0; if(jackPort && MusEGlobal::checkAudioDevice()) res = MusEGlobal::audioDevice->portName(jackPort, persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); if(!res) MusELib::strntcpy(persistentJackPortName, s.toLatin1().constData(), ROUTE_PERSISTENT_NAME_SIZE); midiPort = -1; } else if(type == MIDI_DEVICE_ROUTE) { device = node.device; midiPort = -1; } else if(type == MIDI_PORT_ROUTE) { track = 0; midiPort = node.midiPort; } } Route::Route() { track = 0; midiPort = -1; channel = -1; channels = -1; remoteChannel = -1; type = TRACK_ROUTE; persistentJackPortName[0] = 0; } Route::Route(RouteType type_, int midi_port_num_, void* void_pointer_, int channel_, int channels_, int remote_channel_, const char* name_) { type = type_; midiPort = midi_port_num_; voidPointer = void_pointer_; channel = channel_; channels = channels_; remoteChannel = remote_channel_; persistentJackPortName[0] = 0; MusELib::strntcpy(persistentJackPortName, name_, ROUTE_PERSISTENT_NAME_SIZE); } Route::Route(const Route& a) { type = a.type; midiPort = a.midiPort; voidPointer = a.voidPointer; channel = a.channel; channels = a.channels; remoteChannel = a.remoteChannel; persistentJackPortName[0] = 0; strcpy(persistentJackPortName, a.persistentJackPortName); } Route& Route::operator=(const Route& a) { type = a.type; midiPort = a.midiPort; voidPointer = a.voidPointer; channel = a.channel; channels = a.channels; remoteChannel = a.remoteChannel; persistentJackPortName[0] = 0; strcpy(persistentJackPortName, a.persistentJackPortName); return *this; } //--------------------------------------------------------- // addRoute //--------------------------------------------------------- bool addRoute(Route src, Route dst) { #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute:\n"); #endif if (!src.isValid() || !dst.isValid()) { if(!src.isValid()) fprintf(stderr, "addRoute: invalid src\n"); if(!dst.isValid()) fprintf(stderr, "addRoute: invalid dst\n"); return false; } // fprintf(stderr, "addRoute %d.%d:<%s> %d.%d:<%s>\n", // src.type, src.channel, src.name().toLatin1().constData(), // dst.type, dst.channel, dst.name().toLatin1().constData()); if (src.type == Route::JACK_ROUTE) { if (dst.type == Route::TRACK_ROUTE) { if (dst.track->type() != Track::AUDIO_INPUT) { fprintf(stderr, "addRoute: source is jack, dest:%s is track but not audio input\n", dst.track->name().toLatin1().constData()); return false; } if (dst.channel < 0) { fprintf(stderr, "addRoute: source is jack, dest:%s is track but invalid channel:%d\n", dst.track->name().toLatin1().constData(), dst.channel); return false; } src.channel = dst.channel; if(dst.track->inRoutes()->contains(src)) { fprintf(stderr, "addRoute: src track route already exists.\n"); return false; } #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src Jack dst track name: %s pushing source route\n", dst.track->name().toLatin1().constData()); #endif dst.track->inRoutes()->push_back(src); return true; } else if (dst.type == Route::MIDI_DEVICE_ROUTE) { if(dst.device->deviceType() == MidiDevice::JACK_MIDI) { src.channel = dst.channel; if(dst.device->inRoutes()->contains(src)) { fprintf(stderr, "addRoute: src Jack midi route already exists.\n"); return false; } #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src Jack dst Jack midi name: %s pushing source route\n", dst.device->name().toLatin1().constData()); #endif dst.device->inRoutes()->push_back(src); return true; } else { fprintf(stderr, "addRoute: source is Jack, but destination is not jack midi - type:%d\n", dst.device->deviceType()); return false; } } else if(dst.type == Route::JACK_ROUTE) { // Do nothing - it's a direct Jack connection! return false; } else { fprintf(stderr, "addRoute: source is Jack, but destination is not track or midi - type:%d \n", dst.type); return false; } return false; } else if (dst.type == Route::JACK_ROUTE) { if (src.type == Route::TRACK_ROUTE) { if (src.track->type() != Track::AUDIO_OUTPUT) { fprintf(stderr, "addRoute: destination is jack, source is track but not audio output\n"); return false; } if (src.channel < 0) { fprintf(stderr, "addRoute: destination is jack, source:%s is track but invalid channel:%d\n", src.track->name().toLatin1().constData(), src.channel); return false; } dst.channel = src.channel; if(src.track->outRoutes()->contains(dst)) { fprintf(stderr, "addRoute: dst track route already exists.\n"); return false; } #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: dst Jack src track name: %s pushing destination route\n", src.track->name().toLatin1().constData()); #endif src.track->outRoutes()->push_back(dst); return true; } else if (src.type == Route::MIDI_DEVICE_ROUTE) { if(src.device->deviceType() == MidiDevice::JACK_MIDI) { dst.channel = src.channel; if(src.device->outRoutes()->contains(dst)) { fprintf(stderr, "addRoute: dst Jack midi route already exists.\n"); return false; } #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: dst Jack src Jack midi name: %s pushing destination route\n", src.device->name().toLatin1().constData()); #endif if(src.device->midiPort() != -1) // Initializations sysex etc. need to be sent to the new connection. MusEGlobal::midiPorts[src.device->midiPort()].clearInitSent(); src.device->outRoutes()->push_back(dst); return true; } else { fprintf(stderr, "addRoute: destination is Jack, but source is not jack midi - type:%d\n", src.device->deviceType()); return false; } return false; } else if(src.type == Route::JACK_ROUTE) { // Do nothing - it's a direct Jack connection! return false; } else { fprintf(stderr, "addRoute: destination is Jack, but source is not track or midi - type:%d \n", src.type); return false; } return false; } else if(src.type == Route::MIDI_PORT_ROUTE) { if(dst.type != Route::TRACK_ROUTE || !dst.track->isMidiTrack()) { fprintf(stderr, "addRoute: source is midi port:%d, but destination is not midi track\n", src.midiPort); return false; } if(dst.channel < -1 || dst.channel >= MusECore::MUSE_MIDI_CHANNELS) { fprintf(stderr, "addRoute: source is midi port:%d, but destination track channel:%d out of range\n", src.midiPort, dst.channel); return false; } MidiPort *mp = &MusEGlobal::midiPorts[src.midiPort]; // Do not allow synth ports to connect to any track. It may be useful in some cases, // may be desired later, but for now it's just a routing hassle. p4.0.35 //if(mp->device() && mp->device()->isSynti()) // return false; bool ret1 = false; bool ret2 = false; src.channel = dst.channel; RouteList* rl; rl = mp->outRoutes(); if(!rl->contains(dst)) { rl->push_back(dst); ret1 = true; } rl = dst.track->inRoutes(); if(!rl->contains(src)) { rl->push_back(src); ret2 = true; } return ret1 || ret2; } else if(dst.type == Route::MIDI_PORT_ROUTE) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ fprintf(stderr, "addRoute: destination is midi port:%d, but source is not allowed\n", dst.midiPort); return false; #else if(src.type != Route::TRACK_ROUTE || !src.track->isMidiTrack()) { fprintf(stderr, "addRoute: destination is midi port:%d, but source is not midi track\n", dst.midiPort); return false; } if(src.channel < -1 || src.channel >= MusECore::MUSE_MIDI_CHANNELS) { fprintf(stderr, "addRoute: destination is midi port:%d, but source track channel:%d out of range\n", dst.midiPort, src.channel); return false; } bool ret1 = false; bool ret2 = false; dst.channel = src.channel; RouteList* rl; rl = src.track->outRoutes(); if(!rl->contains(dst)) { rl->push_back(dst); ret1 = true; } rl = MusEGlobal::midiPorts[dst.midiPort].inRoutes(); if(!rl->contains(src)) { rl->push_back(src); ret2 = true; } return ret1 || ret2; #endif } else { if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) { fprintf(stderr, "addRoute: source or destination are not track routes\n"); return false; } RouteList* outRoutes = src.track->outRoutes(); if((src.channel == -1 && dst.channel != -1) || (dst.channel == -1 && src.channel != -1)) { fprintf(stderr, "addRoute: source and destination are track routes but channels incompatible: src:%d dst:%d\n", src.channel, dst.channel); return false; } // Don't bother checking valid channel ranges, to support persistent routes... if(src.channels != dst.channels) { fprintf(stderr, "addRoute: source and destination are track routes but number of channels incompatible: src:%d dst:%d\n", src.channels, dst.channels); return false; } // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 // After: src TrackA, Channel 4, Remote Channel 2 dst: TrackB channel 2 Remote Channel 4 // // Ex. (Handled above, not used here. For example only.) // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 src.remoteChannel = src.channel; dst.remoteChannel = dst.channel; const int src_chan = src.channel; src.channel = dst.channel; dst.channel = src_chan; #ifdef ROUTE_DEBUG fprintf(stderr, "addRoute: src track ch:%d chs:%d remch:%d dst track ch:%d chs:%d remch:%d name: %s pushing dest and source routes\n", src.channel, src.channels, src.remoteChannel, dst.channel, dst.channels, dst.remoteChannel, dst.track->name().toLatin1().constData()); #endif const bool o_found = outRoutes->contains(dst); if(o_found) fprintf(stderr, "addRoute: dst track route already exists in src track out routes list. Ignoring.\n"); else outRoutes->push_back(dst); RouteList* inRoutes = dst.track->inRoutes(); const bool i_found = inRoutes->contains(src); if(i_found) fprintf(stderr, "addRoute: src track route already exists in dst track out routes list. Ignoring.\n"); else { // make sure AUDIO_AUX is processed last if(src.track->type() == Track::AUDIO_AUX) // REMOVE Tim. Or not? This special aux code may not be useful or needed now. TBD inRoutes->push_back(src); else inRoutes->insert(inRoutes->begin(), src); } // Only if a route was established: if(!o_found || !i_found) { // Is the source an Aux Track or else does it have Aux Tracks routed to it? // Update the destination track's aux ref count, and all tracks it is routed to. if(src.track->auxRefCount()) src.track->updateAuxRoute( src.track->auxRefCount(), dst.track ); else if(src.track->type() == Track::AUDIO_AUX) src.track->updateAuxRoute( 1, dst.track ); return true; } return false; } return false; } //--------------------------------------------------------- // removeRoute //--------------------------------------------------------- bool removeRoute(Route src, Route dst) { if(src.type == Route::JACK_ROUTE) { if(dst.isValid()) { switch(dst.type) { case Route::TRACK_ROUTE: src.channel = dst.channel; return dst.track->inRoutes()->removeRoute(src); break; case Route::MIDI_DEVICE_ROUTE: return dst.device->inRoutes()->removeRoute(src); break; case Route::MIDI_PORT_ROUTE: return MusEGlobal::midiPorts[dst.midiPort].inRoutes()->removeRoute(src); break; case Route::JACK_ROUTE: // Do nothing - it's a direct Jack disconnection! return false; break; } } return false; } else if(dst.type == Route::JACK_ROUTE) { if(src.isValid()) { switch(src.type) { case Route::TRACK_ROUTE: dst.channel = src.channel; return src.track->outRoutes()->removeRoute(dst); break; case Route::MIDI_DEVICE_ROUTE: return src.device->outRoutes()->removeRoute(dst); break; case Route::MIDI_PORT_ROUTE: return MusEGlobal::midiPorts[src.midiPort].outRoutes()->removeRoute(dst); break; case Route::JACK_ROUTE: // Do nothing - it's a direct Jack disconnection! return false; break; } } return false; } else if(src.type == Route::MIDI_PORT_ROUTE) { bool ret1 = false; bool ret2 = false; if(src.isValid()) ret1 = MusEGlobal::midiPorts[src.midiPort].outRoutes()->removeRoute(dst); if(dst.isValid()) { switch(dst.type) { case Route::TRACK_ROUTE: ret2 = dst.track->inRoutes()->removeRoute(src); break; case Route::MIDI_DEVICE_ROUTE: ret2 = dst.device->inRoutes()->removeRoute(src); break; case Route::MIDI_PORT_ROUTE: ret2 = MusEGlobal::midiPorts[dst.midiPort].inRoutes()->removeRoute(src); break; case Route::JACK_ROUTE: ret2 = false; break; } } return ret1 || ret2; } else if(dst.type == Route::MIDI_PORT_ROUTE) { bool ret1 = false; bool ret2 = false; if(src.isValid()) { switch(src.type) { case Route::TRACK_ROUTE: ret2 = src.track->outRoutes()->removeRoute(dst); break; case Route::MIDI_DEVICE_ROUTE: ret2 = src.device->outRoutes()->removeRoute(dst); break; case Route::MIDI_PORT_ROUTE: ret2 = MusEGlobal::midiPorts[src.midiPort].outRoutes()->removeRoute(dst); break; case Route::JACK_ROUTE: ret2 = false; break; } } if(dst.isValid()) ret1 = MusEGlobal::midiPorts[dst.midiPort].inRoutes()->removeRoute(src); return ret1 || ret2; } else { if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) { fprintf(stderr, "removeRoute: source and destination are not tracks\n"); return false; } // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 // After: src TrackA, Channel 4, Remote Channel 2 dst: TrackB channel 2 Remote Channel 4 // // Ex. (Handled above, not used here. For example only.) // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 src.remoteChannel = src.channel; dst.remoteChannel = dst.channel; const int src_chan = src.channel; src.channel = dst.channel; dst.channel = src_chan; // Is the source an Aux Track or else does it have Aux Tracks routed to it? // Update the destination track's aux ref count, and all tracks it is routed to. if(src.isValid() && dst.isValid() && src.track->outRoutes()->contains(dst) && dst.track->inRoutes()->contains(src)) { if(src.track->auxRefCount()) src.track->updateAuxRoute( -src.track->auxRefCount(), dst.track ); else if(src.track->type() == Track::AUDIO_AUX) src.track->updateAuxRoute( -1, dst.track ); } bool ret1 = false; bool ret2 = false; if(src.isValid()) { RouteList* o_rl = src.track->outRoutes(); MusECore::iRoute o_ir = o_rl->find(dst); if(o_ir != o_rl->end()) { o_rl->erase(o_ir); ret1 = true; } } else fprintf(stderr, "removeRoute: source is track but invalid\n"); if(dst.isValid()) { RouteList* i_rl = dst.track->inRoutes(); MusECore::iRoute i_ir = i_rl->find(src); if(i_ir != i_rl->end()) { i_rl->erase(i_ir); ret2 = true; } } else fprintf(stderr, "removeRoute: destination is track but invalid\n"); return ret1 || ret2; } return false; } //--------------------------------------------------------- // removeAllRoutes // If src is valid, disconnects all output routes from it. // If dst is valid, disconnects all input routes to it. // src and dst Route are used SIMPLY because Route provides convenient way to // specify the different pointer types (track, port, jack) // This routine will ONLY look at the pointer, not the channel or port etc... // So far it only works with MidiDevice <-> Jack. //--------------------------------------------------------- void removeAllRoutes(Route src, Route dst) { // TODO: Is the source an Aux Track or else does it have Aux Tracks routed to it? // Update the destination track's aux ref count, and all tracks it is routed to. /* if(src.isValid() && dst.isValid()) DELETETHIS 8 { if(src.track->auxRefCount()) dst.track->updateAuxStates( -src.track->auxRefCount() ); else if(src.track->type() == Track::TrackType::AUDIO_AUX)) dst.track->updateAuxStates( -1 ); } */ if(src.isValid()) { if(src.type == Route::MIDI_DEVICE_ROUTE) src.device->outRoutes()->clear(); else fprintf(stderr, "removeAllRoutes: source is not midi device\n"); } if(dst.isValid()) { if(dst.type == Route::MIDI_DEVICE_ROUTE) dst.device->inRoutes()->clear(); else fprintf(stderr, "removeAllRoutes: dest is not midi device\n"); } } //--------------------------------------------------------- // track2name // create string name representation for audio node //--------------------------------------------------------- static QString track2name(const Track* n) { if (n == nullptr) return QWidget::tr("None"); return n->name(); } //--------------------------------------------------------- // icon //--------------------------------------------------------- QIcon* Route::icon(bool isSource, bool isMidi) const { switch(type) { case TRACK_ROUTE: if(track) return track->icon(); break; case JACK_ROUTE: if(isMidi) return isSource ? MusEGui::routeInMidiSVGIcon : MusEGui::routeOutMidiSVGIcon; else return isSource ? MusEGui::routeInSVGIcon : MusEGui::routeOutSVGIcon; case MIDI_DEVICE_ROUTE: return MusEGui::midiInSVGIcon; // break; case MIDI_PORT_ROUTE: return MusEGui::midiPortSVGIcon; } return nullptr; } //--------------------------------------------------------- // name // create string name representation for audio node //--------------------------------------------------------- QString Route::name(int preferred_name_or_alias) const { if(type == MIDI_DEVICE_ROUTE) { if(device) return device->name(); return QWidget::tr("None"); } else if(type == JACK_ROUTE) { if(MusEGlobal::checkAudioDevice() && jackPort) { char s[ROUTE_PERSISTENT_NAME_SIZE]; return QString(MusEGlobal::audioDevice->portName(jackPort, s, ROUTE_PERSISTENT_NAME_SIZE, preferred_name_or_alias)); } return QString(persistentJackPortName); } else if(type == MIDI_PORT_ROUTE) { return ROUTE_MIDIPORT_NAME_PREFIX + QString().setNum(midiPort); } else return track2name(track); } //--------------------------------------------------------- // name // fill and return str char name representation for audio node //--------------------------------------------------------- char* Route::name(char* str, int str_size, int preferred_name_or_alias) const { if(type == MIDI_DEVICE_ROUTE) return MusELib::strntcpy(str, device ? device->name().toLatin1().constData() : 0, str_size); else if(type == JACK_ROUTE) { if(MusEGlobal::checkAudioDevice() && jackPort) return MusEGlobal::audioDevice->portName(jackPort, str, str_size, preferred_name_or_alias); return MusELib::strntcpy(str, persistentJackPortName, str_size); } else if(type == MIDI_PORT_ROUTE) { return MusELib::strntcpy(str, (ROUTE_MIDIPORT_NAME_PREFIX + QString().setNum(midiPort)).toLatin1().constData(), str_size); } else return MusELib::strntcpy(str, track ? track->name().toLatin1().constData() : 0, str_size); } //--------------------------------------------------------- // displayName // create string name representation for audio node //--------------------------------------------------------- QString Route::displayName(int preferred_name_or_alias) const { if(type == MIDI_DEVICE_ROUTE) { if(device) return device->name(); return QWidget::tr("None"); } else if(type == JACK_ROUTE) { if(MusEGlobal::checkAudioDevice() && jackPort) { char s[ROUTE_PERSISTENT_NAME_SIZE]; return QString(MusEGlobal::audioDevice->portName(jackPort, s, ROUTE_PERSISTENT_NAME_SIZE, preferred_name_or_alias)); } return QString(persistentJackPortName); } else if(type == MIDI_PORT_ROUTE) { return ROUTE_MIDIPORT_NAME_PREFIX + QString().setNum(midiPort); } else return QString("%1:%2").arg(MusEGlobal::song->tracks()->index(track) + 1).arg(track2name(track)); } //--------------------------------------------------------- // name2route //--------------------------------------------------------- Route name2route(const QString& rn, bool /*dst*/, int rtype) { int channel = -1; QString s(rn); // Support old route style in med files. Obsolete. if (rn[0].isNumber() && rn[1]==':') { channel = rn[0].toLatin1() - int('1'); s = rn.mid(2); } if(rtype == -1) { if(MusEGlobal::checkAudioDevice()) { void* p = MusEGlobal::audioDevice->findPort(s.toLatin1().constData()); if(p) return Route(p, channel); } TrackList* tl = MusEGlobal::song->tracks(); for(iTrack i = tl->begin(); i != tl->end(); ++i) { if((*i)->isMidiTrack()) { MidiTrack* track = (MidiTrack*)*i; if(track->name() == s) return Route(track, channel); } else { AudioTrack* track = (AudioTrack*)*i; if(track->name() == s) return Route(track, channel); } } for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { if((*i)->name() == s) return Route(*i, channel); } if(s.left(ROUTE_MIDIPORT_NAME_PREFIX.length()) == ROUTE_MIDIPORT_NAME_PREFIX) { bool ok = false; int port = s.mid(ROUTE_MIDIPORT_NAME_PREFIX.length()).toInt(&ok); if(ok) return Route(port, channel); } } else { if(rtype == Route::TRACK_ROUTE) { TrackList* tl = MusEGlobal::song->tracks(); for(iTrack i = tl->begin(); i != tl->end(); ++i) { if((*i)->isMidiTrack()) { MidiTrack* track = (MidiTrack*)*i; if(track->name() == s) return Route(track, channel); } else { AudioTrack* track = (AudioTrack*)*i; if(track->name() == s) return Route(track, channel); } } return Route((Track*) 0, channel); } else // TODO Distinguish the device types if(rtype == Route::MIDI_DEVICE_ROUTE) { for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { if((*i)->name() == s) return Route(*i, channel); } return Route((MidiDevice*) 0, channel); } else if(rtype == Route::JACK_ROUTE) { if(MusEGlobal::checkAudioDevice()) { // REMOVE Tim. Persistent routes. TODO FIXME: Use new cached jack port names. void* p = MusEGlobal::audioDevice->findPort(s.toLatin1().constData()); if(p) return Route(p, channel); } return Route((void*) 0, channel); } else if(rtype == Route::MIDI_PORT_ROUTE) { if(s.left(ROUTE_MIDIPORT_NAME_PREFIX.length()) == ROUTE_MIDIPORT_NAME_PREFIX) { bool ok = false; int port = s.mid(ROUTE_MIDIPORT_NAME_PREFIX.length()).toInt(&ok); if(ok) return Route(port, channel); } return Route((int) 0, channel); } } fprintf(stderr, " name2route: <%s> not found\n", rn.toLatin1().constData()); return Route((Track*) 0, channel); } //--------------------------------------------------------- // routeCanConnect //--------------------------------------------------------- bool routeCanConnect(const Route& src, const Route& dst) { if(!src.isValid() || !dst.isValid()) return false; if(src.type == Route::JACK_ROUTE) { if(!MusEGlobal::checkAudioDevice() || !src.jackPort || MusEGlobal::audioDevice->portDirection(src.jackPort) != MusECore::AudioDevice::OutputPort) return false; if(dst.type == Route::TRACK_ROUTE) { if(MusEGlobal::audioDevice->portType(src.jackPort) != MusECore::AudioDevice::AudioPort || dst.track->type() != Track::AUDIO_INPUT || dst.channel < 0) return false; const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, src.channel, src.persistentJackPortName); return !dst.track->inRoutes()->contains(v_src); } else if(dst.type == Route::MIDI_DEVICE_ROUTE) { if(MusEGlobal::audioDevice->portType(src.jackPort) != MusECore::AudioDevice::MidiPort || dst.device->deviceType() != MidiDevice::JACK_MIDI) return false; const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, src.channel, src.persistentJackPortName); return !dst.device->inRoutes()->contains(v_src); } else if(dst.type == Route::JACK_ROUTE) { // Allow direct Jack connections! return MusEGlobal::audioDevice && MusEGlobal::audioDevice->portsCanConnect(src.jackPort, dst.jackPort); } else return false; } else if(dst.type == Route::JACK_ROUTE) { if(!MusEGlobal::checkAudioDevice() || !dst.jackPort || MusEGlobal::audioDevice->portDirection(dst.jackPort) != MusECore::AudioDevice::InputPort) return false; if(src.type == Route::TRACK_ROUTE) { if(MusEGlobal::audioDevice->portType(dst.jackPort) != MusECore::AudioDevice::AudioPort || src.track->type() != Track::AUDIO_OUTPUT || src.channel < 0) return false; const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, -1, dst.persistentJackPortName); return !src.track->outRoutes()->contains(v_dst); } else if(src.type == Route::MIDI_DEVICE_ROUTE) { if(MusEGlobal::audioDevice->portType(dst.jackPort) != MusECore::AudioDevice::MidiPort || src.device->deviceType() != MidiDevice::JACK_MIDI) return false; const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, -1, dst.persistentJackPortName); return !src.device->outRoutes()->contains(v_dst); } else if(src.type == Route::JACK_ROUTE) { // Allow direct Jack connections! return MusEGlobal::audioDevice && MusEGlobal::audioDevice->portsCanConnect(src.jackPort, dst.jackPort); } else return false; } else if(src.type == Route::MIDI_PORT_ROUTE) { if(dst.type != Route::TRACK_ROUTE || !dst.track->isMidiTrack() || dst.channel < -1 || dst.channel >= MusECore::MUSE_MIDI_CHANNELS) return false; //MidiPort *mp = &MusEGlobal::midiPorts[src.midiPort]; // Do not allow synth ports to connect to any track. It may be useful in some cases, // may be desired later, but for now it's just a routing hassle. p4.0.35 //if(mp->device() && mp->device()->isSynti()) // return false; const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, src.channel, src.persistentJackPortName); // If one route node exists and one is missing, it's OK to reconnect, addRoute will take care of it. if(!MusEGlobal::midiPorts[src.midiPort].outRoutes()->contains(dst) || !dst.track->inRoutes()->contains(v_src)) return true; return false; } else if(dst.type == Route::MIDI_PORT_ROUTE) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ return false; #else if(src.type != Route::TRACK_ROUTE || !src.track->isMidiTrack() || src.channel < -1 || src.channel >= MusECore::MUSE_MIDI_CHANNELS) return false; const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, dst.channel, dst.persistentJackPortName); // If one route node exists and one is missing, it's OK to reconnect, addRoute will take care of it. if(!src.track->outRoutes()->contains(v_dst) || !MusEGlobal::midiPorts[dst.midiPort].inRoutes()->contains(src)) return true; return false; #endif } else { if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) return false; if(src.track && dst.track && src.track == dst.track) return false; switch(src.track->type()) { case Track::MIDI: case Track::DRUM: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_GROUP: case Track::AUDIO_AUX: case Track::AUDIO_SOFTSYNTH: return false; case Track::AUDIO_INPUT: if(src.channel >= 0) return false; break; } break; case Track::AUDIO_OUTPUT: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_GROUP: case Track::AUDIO_AUX: case Track::AUDIO_SOFTSYNTH: return false; case Track::AUDIO_INPUT: if(src.channel >= 0 || dst.channel >= 0) return false; break; } break; case Track::AUDIO_INPUT: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::AUDIO_INPUT: case Track::AUDIO_AUX: return false; case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_GROUP: case Track::AUDIO_SOFTSYNTH: break; } break; case Track::WAVE: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::AUDIO_INPUT: case Track::AUDIO_AUX: return false; case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_GROUP: case Track::AUDIO_SOFTSYNTH: break; } break; case Track::AUDIO_GROUP: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::AUDIO_INPUT: case Track::AUDIO_AUX: return false; case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_GROUP: case Track::AUDIO_SOFTSYNTH: break; } break; case Track::AUDIO_AUX: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::AUDIO_INPUT: case Track::AUDIO_AUX: return false; case Track::AUDIO_GROUP: case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_SOFTSYNTH: break; } break; case Track::AUDIO_SOFTSYNTH: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::AUDIO_INPUT: case Track::AUDIO_AUX: return false; case Track::AUDIO_GROUP: case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_SOFTSYNTH: break; } break; } if((src.channel == -1 && dst.channel != -1) || (dst.channel == -1 && src.channel != -1)) return false; if(src.channels != dst.channels) return false; // Allow for -1 = omni route. if(src.channel >= src.track->routeCapabilities()._trackChannels._outChannels || dst.channel >= dst.track->routeCapabilities()._trackChannels._inChannels) return false; if(src.track->isCircularRoute(dst.track)) return false; // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 // After: src TrackA, Channel 4, Remote Channel 2 dst: TrackB channel 2 Remote Channel 4 // // Ex. (Handled above, not used here. For example only.) // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, src.channel, src.persistentJackPortName); const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, dst.channel, dst.persistentJackPortName); // Allow it to reconnect a partial route. if(!v_src.isValid() || !v_dst.isValid() || (src.track->outRoutes()->contains(v_dst) && dst.track->inRoutes()->contains(v_src))) return false; return true; } return false; } //--------------------------------------------------------- // routeCanDisconnect //--------------------------------------------------------- bool routeCanDisconnect(const Route& src, const Route& dst) { if(src.type == Route::JACK_ROUTE) { //if(!dst.isValid()) if(!dst.exists()) return false; if(dst.type == Route::TRACK_ROUTE) { if(dst.track->type() != Track::AUDIO_INPUT) return false; const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, -1, src.persistentJackPortName); return dst.track->inRoutes()->contains(v_src); } else if(dst.type == Route::MIDI_DEVICE_ROUTE) { return dst.device->inRoutes()->contains(src); } else if(dst.type == Route::JACK_ROUTE) { // Allow direct Jack connections! Pass the port names here instead of ports so that // persistent routes (where jackPort = NULL) can be removed. return MusEGlobal::audioDevice && MusEGlobal::audioDevice->portsCanDisconnect(src.persistentJackPortName, dst.persistentJackPortName); } else return false; } else if(dst.type == Route::JACK_ROUTE) { if(!src.exists()) return false; if(src.type == Route::TRACK_ROUTE) { if(src.track->type() != Track::AUDIO_OUTPUT) return false; const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, -1, dst.persistentJackPortName); return src.track->outRoutes()->contains(v_dst); } else if(src.type == Route::MIDI_DEVICE_ROUTE) { return src.device->outRoutes()->contains(dst); } else if(src.type == Route::JACK_ROUTE) { // Allow direct Jack disconnections! Pass the port names here instead of ports so that // persistent routes (where jackPort = NULL) can be removed. return MusEGlobal::audioDevice && MusEGlobal::audioDevice->portsCanDisconnect(src.persistentJackPortName, dst.persistentJackPortName); } else return false; } else if(src.type == Route::MIDI_PORT_ROUTE) { if(!src.isValid() || src.channel < -1 || src.channel >= MusECore::MUSE_MIDI_CHANNELS || dst.type != Route::TRACK_ROUTE || !dst.exists() || !dst.track->isMidiTrack() || dst.channel < -1 || dst.channel >= MusECore::MUSE_MIDI_CHANNELS) return false; // Allow it to disconnect a partial route. if(MusEGlobal::midiPorts[src.midiPort].outRoutes()->contains(dst) || dst.track->inRoutes()->contains(src)) return true; return false; } else if(dst.type == Route::MIDI_PORT_ROUTE) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ return false; #else if(!dst.isValid() || dst.channel < -1 || dst.channel >= MusECore::MUSE_MIDI_CHANNELS || src.type != Route::TRACK_ROUTE || !src.exists() || !src.track->isMidiTrack() || src.channel < -1 || src.channel >= MusECore::MUSE_MIDI_CHANNELS) return false; // Allow it to disconnect a partial route. if(src.track->outRoutes()->contains(dst) || MusEGlobal::midiPorts[dst.midiPort].inRoutes()->contains(src)) return true; return false; #endif } else { if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) return false; if(src.track && dst.track && src.track == dst.track) return false; // Ex. Params: src: TrackA, Channel 2, Remote Channel -1 dst: TrackB channel 4 Remote Channel -1 // After: src TrackA, Channel 4, Remote Channel 2 dst: TrackB channel 2 Remote Channel 4 // // Ex. (Handled above, not used here. For example only.) // Params: src: TrackA, Channel 2, Remote Channel -1 dst: JackAA channel -1 Remote Channel -1 // After: (src TrackA, Channel -1, Remote Channel 2) dst: JackAA channel 2 Remote Channel -1 const Route v_src(src.type, src.midiPort, src.voidPointer, dst.channel, src.channels, src.channel, src.persistentJackPortName); const Route v_dst(dst.type, dst.midiPort, dst.voidPointer, src.channel, dst.channels, dst.channel, dst.persistentJackPortName); // Allow it to disconnect a partial route. if((v_src.exists() && src.track->outRoutes()->contains(v_dst)) || (v_dst.exists() && dst.track->inRoutes()->contains(v_src))) return true; return false; } return false; } //--------------------------------------------------------- // routeConnectionPossible //--------------------------------------------------------- bool routesCompatible(const Route& src, const Route& dst, bool check_types_only) { if(!src.isValid() || !dst.isValid()) return false; if(src.type == Route::JACK_ROUTE) { if(!MusEGlobal::checkAudioDevice() || !src.jackPort || MusEGlobal::audioDevice->portDirection(src.jackPort) != MusECore::AudioDevice::OutputPort) return false; if(dst.type == Route::TRACK_ROUTE) { if(MusEGlobal::audioDevice->portType(src.jackPort) != MusECore::AudioDevice::AudioPort || dst.track->type() != Track::AUDIO_INPUT || (!check_types_only && dst.channel < 0)) return false; return true; } else if(dst.type == Route::MIDI_DEVICE_ROUTE) { if(MusEGlobal::audioDevice->portType(src.jackPort) != MusECore::AudioDevice::MidiPort || dst.device->deviceType() != MidiDevice::JACK_MIDI) return false; return true; } else if(dst.type == Route::JACK_ROUTE) // Allow direct Jack connections! return MusEGlobal::audioDevice->portsCompatible(src.jackPort, dst.jackPort); else return false; } else if(dst.type == Route::JACK_ROUTE) { if(!MusEGlobal::checkAudioDevice() || !dst.jackPort || MusEGlobal::audioDevice->portDirection(dst.jackPort) != MusECore::AudioDevice::InputPort) return false; if(src.type == Route::TRACK_ROUTE) { if(MusEGlobal::audioDevice->portType(dst.jackPort) != MusECore::AudioDevice::AudioPort || src.track->type() != Track::AUDIO_OUTPUT || (!check_types_only && src.channel < 0)) return false; return true; } else if(src.type == Route::MIDI_DEVICE_ROUTE) { if(MusEGlobal::audioDevice->portType(dst.jackPort) != MusECore::AudioDevice::MidiPort || src.device->deviceType() != MidiDevice::JACK_MIDI) return false; return true; } // Unnecessary. This condition is handled above. //else if(src.type == Route::JACK_ROUTE) // // Allow direct Jack connections! // return MusEGlobal::audioDevice->portsCompatible(src.jackPort, dst.jackPort); else return false; } else if(src.type == Route::MIDI_PORT_ROUTE) { if(dst.type != Route::TRACK_ROUTE || !dst.track->isMidiTrack()) return false; //MidiPort *mp = &MusEGlobal::midiPorts[src.midiPort]; // Do not allow synth ports to connect to any track. It may be useful in some cases, // may be desired later, but for now it's just a routing hassle. p4.0.35 //if(mp->device() && mp->device()->isSynti()) // return false; if(check_types_only) return true; if(dst.channel < -1 || dst.channel >= MusECore::MUSE_MIDI_CHANNELS) return false; // If one route node exists and one is missing, it's OK to reconnect, addRoute will take care of it. return true; } else if(dst.type == Route::MIDI_PORT_ROUTE) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ return false; #else if(src.type != Route::TRACK_ROUTE || !src.track->isMidiTrack()) return false; if(check_types_only) return true; if(src.channel < -1 || src.channel >= MusECore::MUSE_MIDI_CHANNELS) return false; // If one route node exists and one is missing, it's OK to reconnect, addRoute will take care of it. return true; #endif } else { if(src.type != Route::TRACK_ROUTE || dst.type != Route::TRACK_ROUTE) return false; if(src.track && dst.track && src.track == dst.track) return false; switch(src.track->type()) { case Track::MIDI: case Track::DRUM: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_GROUP: case Track::AUDIO_AUX: case Track::AUDIO_SOFTSYNTH: return false; case Track::AUDIO_INPUT: if(!check_types_only && src.channel >= 0) return false; break; } break; case Track::AUDIO_OUTPUT: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_GROUP: case Track::AUDIO_AUX: case Track::AUDIO_SOFTSYNTH: return false; case Track::AUDIO_INPUT: if(!check_types_only && (src.channel >= 0 || dst.channel >= 0)) return false; break; } break; case Track::AUDIO_INPUT: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::AUDIO_INPUT: case Track::AUDIO_AUX: return false; case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_GROUP: case Track::AUDIO_SOFTSYNTH: break; } break; case Track::WAVE: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::AUDIO_INPUT: case Track::AUDIO_AUX: return false; case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_GROUP: case Track::AUDIO_SOFTSYNTH: break; } break; case Track::AUDIO_GROUP: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::AUDIO_INPUT: case Track::AUDIO_AUX: return false; case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_GROUP: case Track::AUDIO_SOFTSYNTH: break; } break; case Track::AUDIO_AUX: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::AUDIO_INPUT: case Track::AUDIO_AUX: return false; case Track::AUDIO_GROUP: case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_SOFTSYNTH: break; } break; case Track::AUDIO_SOFTSYNTH: switch(dst.track->type()) { case Track::MIDI: case Track::DRUM: case Track::AUDIO_INPUT: case Track::AUDIO_AUX: return false; case Track::AUDIO_GROUP: case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_SOFTSYNTH: break; } break; } // Don't bother with the circular route check, below. if(check_types_only) return true; if((src.channel == -1 && dst.channel != -1) || (dst.channel == -1 && src.channel != -1) || (src.channels != dst.channels)) return false; // Allow for -1 = omni route. if(src.channel >= src.track->routeCapabilities()._trackChannels._outChannels || dst.channel >= dst.track->routeCapabilities()._trackChannels._inChannels || src.track->isCircularRoute(dst.track)) return false; return true; } return false; } //--------------------------------------------------------- // read //--------------------------------------------------------- void Route::read(Xml& xml) { QString s; int dtype = MidiDevice::ALSA_MIDI; int port = -1; int track_idx = -1; RouteType rtype = Route::TRACK_ROUTE; for (;;) { const QString& tag = xml.s1(); Xml::Token token = xml.parse(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::Attribut: #ifdef ROUTE_DEBUG fprintf(stderr, "Route::read(): attribute:%s\n", tag.toLatin1().constData()); #endif if(tag == "type") rtype = RouteType(xml.s2().toInt()); else if(tag == "devtype") { dtype = xml.s2().toInt(); rtype = Route::MIDI_DEVICE_ROUTE; } else if(tag == "name") s = xml.s2(); else if(tag == "track") track_idx = xml.s2().toInt(); else if(tag == "mport") { port = xml.s2().toInt(); rtype = Route::MIDI_PORT_ROUTE; } else fprintf(stderr, "Route::read(): unknown attribute:%s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: #ifdef ROUTE_DEBUG fprintf(stderr, "Route::read(): tag end type:%d channel:%d name:%s\n", rtype, channel, s.toLatin1().constData()); #endif if(rtype == MIDI_PORT_ROUTE) { if(port >= 0 && port < MusECore::MIDI_PORTS) { type = rtype; midiPort = port; } else fprintf(stderr, "Route::read(): midi port <%d> out of range\n", port); } else // New track index method replaces obsolete track name method below. if(track_idx >= 0) { if(rtype == TRACK_ROUTE) { const TrackList* tl = MusEGlobal::song->tracks(); Track* t = tl->index(track_idx); if(t) { track = t; type = rtype; } else fprintf(stderr, "Route::read(): track index <%d> not found\n", track_idx); } } else if(!s.isEmpty()) { // OBSOLETE. Keep for backwards compatibility. Replaced by track index method above. if(rtype == TRACK_ROUTE) { TrackList* tl = MusEGlobal::song->tracks(); iTrack i = tl->begin(); for ( ; i != tl->end(); ++i) { Track* t = *i; if (t->name() == s) { track = t; type = rtype; break; } } if(i == tl->end()) fprintf(stderr, "Route::read(): track <%s> not found\n", s.toLocal8Bit().constData()); } else if(rtype == JACK_ROUTE) { type = rtype; jackPort = 0; if(MusEGlobal::audioDevice) // fix crash if jack is zombified at this point { jackPort = MusEGlobal::audioDevice->findPort(s.toLatin1().constData()); if(jackPort) // Replace the name with a more appropriate one at this time. MusEGlobal::audioDevice->portName(jackPort, persistentJackPortName, ROUTE_PERSISTENT_NAME_SIZE); } // The graph change handler will replace persistentJackPortName with a more appropriate name if necessary. if(!jackPort) MusELib::strntcpy(persistentJackPortName, s.toLatin1().constData(), ROUTE_PERSISTENT_NAME_SIZE); } else if(rtype == MIDI_DEVICE_ROUTE) { iMidiDevice imd = MusEGlobal::midiDevices.begin(); for( ; imd != MusEGlobal::midiDevices.end(); ++imd) { MidiDevice* md = *imd; if(md->name() == s && md->deviceType() == dtype) { // We found a device, but if it is not in use by the song (port is -1), ignore it. // This prevents loading and propagation of bogus routes in the med file. // We found a device, but if it is not a jack midi and in use by the song (port is -1), ignore it. // This prevents loading and propagation of bogus routes in the med file. if(md->midiPort() == -1 && md->deviceType() != MidiDevice::JACK_MIDI) break; device = md; type = rtype; break; } } if(imd == MusEGlobal::midiDevices.end()) fprintf(stderr, "Route::read(): midi device <%s> not found\n", s.toLatin1().constData()); } } return; default: break; } } } //--------------------------------------------------------- // read //--------------------------------------------------------- void Song::readRoute(Xml& xml) { QString src; QString dst; int ch = -1; int chmask = -1; int chs = -1; int remch = -1; bool midi_track_out_set = false; Route sroute, droute; for (;;) { const QString& tag = xml.s1(); Xml::Token token = xml.parse(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: // 2010/02/03 Support old routes in med files. Now obsolete! if (tag == "srcNode") src = xml.parse1(); else if (tag == "dstNode") dst = xml.parse1(); // Support new routes. else if (tag == "source") { sroute.read(xml); sroute.channel = ch; sroute.channels = chs; sroute.remoteChannel = remch; } else if (tag == "dest") { droute.read(xml); droute.channels = chs; // If channels was given, it should be a multi-channel audio route. // Convert to new scheme by switching them around for the destination route: if(chs > 0) { droute.channel = remch; droute.remoteChannel = ch; } else { droute.channel = ch; droute.remoteChannel = remch; } } else xml.unknown("readRoute"); break; case Xml::Attribut: #ifdef ROUTE_DEBUG fprintf(stderr, "Song::readRoute(): attribute:%s\n", tag.toLatin1().constData()); #endif if(tag == "channel") ch = xml.s2().toInt(); else if(tag == "channels") chs = xml.s2().toInt(); else if(tag == "remch") remch = xml.s2().toInt(); else if(tag == "channelMask") // New channel mask for midi port-track routes. chmask = xml.s2().toInt(); else fprintf(stderr, "Song::readRoute(): unknown attribute:%s\n", tag.toLatin1().constData()); break; case Xml::TagEnd: if (xml.s1() == "Route") { // Support old routes in med files. Now obsolete! if(!src.isEmpty() && !dst.isEmpty()) { Route s = name2route(src, false); Route d = name2route(dst, true); addRoute(s, d); } else // Support new routes. if(sroute.isValid() && droute.isValid()) { // Support pre- 1.1-RC2 midi device to track routes. Obsolete. Replaced with midi port routes. if(sroute.type == Route::MIDI_DEVICE_ROUTE && droute.type == Route::TRACK_ROUTE) { if(sroute.device->midiPort() >= 0 && sroute.device->midiPort() < MusECore::MIDI_PORTS && ch >= 0 && ch < MusECore::MUSE_MIDI_CHANNELS) { sroute.midiPort = sroute.device->midiPort(); sroute.device = 0; sroute.type = Route::MIDI_PORT_ROUTE; sroute.channel = ch; droute.channel = sroute.channel; addRoute(sroute, droute); } else fprintf(stderr, " Warning - device:%s to track route, no device midi port or chan:%d out of range. Ignoring route!\n", sroute.device->name().toLatin1().constData(), ch); } // Support pre- 1.1-RC2 track to midi device routes. Obsolete. Replaced with midi port routes. else if(sroute.type == Route::TRACK_ROUTE && droute.type == Route::MIDI_DEVICE_ROUTE) { // Device and track already validated in ::read(). const int port = droute.device->midiPort(); if(port >= 0 && port < MusECore::MIDI_PORTS && ch >= 0 && ch < MusECore::MUSE_MIDI_CHANNELS && sroute.track->isMidiTrack()) { MidiTrack* mt = static_cast(sroute.track); #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(!midi_track_out_set) { midi_track_out_set = true; MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; MusEGlobal::audio->msgIdle(true); changed |= mt->setOutPortAndChannelAndUpdate(port, ch, false); MusEGlobal::audio->msgIdle(false); MusEGlobal::audio->msgUpdateSoloStates(); MusEGlobal::song->update(SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); } #else droute.midiPort = droute.device->midiPort(); droute.device = 0; droute.type = Route::MIDI_PORT_ROUTE; droute.channel = ch; sroute.channel = droute.channel; addRoute(sroute, droute); #endif } else fprintf(stderr, " Warning - track to device:%s route, no device midi port or chan:%d out of range. Ignoring route!\n", droute.device->name().toLatin1().constData(), ch); } // Support old bit-wise channel mask for midi port to midi track routes and midi port to audio input soling chain routes. Obsolete! // Check for song file version 2.0 or below: else if(chmask > 0 && (xml.majorVersion() * 1000000 + xml.minorVersion()) <= 2000000) // Arbitrary shift, and add { fprintf(stderr, " Warning - Route: Converting old single-route bitwise channel mask:%d to individual routes\n", chmask); if(sroute.type == Route::MIDI_PORT_ROUTE && droute.type == Route::TRACK_ROUTE) { if(droute.track->isMidiTrack()) { // All channels set? Convert to new Omni route. if(chmask == ((1 << MusECore::MUSE_MIDI_CHANNELS) - 1)) { sroute.channel = -1; droute.channel = -1; addRoute(sroute, droute); } else { // Check each channel bit: for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { const int chbit = 1 << i; // Is channel bit set? if(chmask & chbit) { // Convert to individual routes: sroute.channel = i; droute.channel = i; addRoute(sroute, droute); } } } } // Support old midi port to audio input soloing chain routes. Obsolete! else if(droute.track->type() == Track::AUDIO_INPUT) { const int port = sroute.midiPort; // Check each channel bit: for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { const int chbit = 1 << i; // Is channel bit set? if(chmask & chbit) { const MusECore::MidiTrackList* const mtl = MusEGlobal::song->midis(); for(ciMidiTrack imt = mtl->begin(); imt != mtl->end(); ++imt) { MidiTrack* const mt = *imt; if(mt->outPort() == port && mt->outChannel() == i) { // Convert to a midi track to audio input route: sroute.type = Route::TRACK_ROUTE; sroute.track = mt; sroute.midiPort = -1; sroute.channel = sroute.channels = sroute.remoteChannel = droute.channel = droute.channels = droute.remoteChannel = -1; addRoute(sroute, droute); } } } } } } } // If channels was given, it must be a multi-channel audio route: else if(chs > 0) { // If EITHER the channel or the remote channel are zero but the other not given, convert to an Omni route: if((ch == -1 && remch == -1) || (ch == 0 && remch == -1) || (remch == 0 && ch == -1)) { sroute.channel = sroute.remoteChannel = sroute.channels = droute.channel = droute.remoteChannel = droute.channels = -1; addRoute(sroute, droute); } // Otherwise convert to individual routes: else { sroute.channels = droute.channels = 1; if(sroute.channel == -1) sroute.channel = 0; if(sroute.remoteChannel == -1) sroute.remoteChannel = 0; if(droute.channel == -1) droute.channel = 0; if(droute.remoteChannel == -1) droute.remoteChannel = 0; for(int i = 0; i < chs; ++i) { addRoute(sroute, droute); ++sroute.channel; ++sroute.remoteChannel; ++droute.channel; ++droute.remoteChannel; } } } else addRoute(sroute, droute); } else fprintf(stderr, " Warning - route invalid. Ignoring route!\n"); return; } default: break; } } } //--------------------------------------------------------- // dump //--------------------------------------------------------- void Route::dump() const { if (type == TRACK_ROUTE) { if(track) fprintf(stderr, "Route dump: track <%s> channel %d channels %d\n", track->name().toLocal8Bit().constData(), channel, channels); } else if (type == JACK_ROUTE) { if(MusEGlobal::checkAudioDevice()) { if(jackPort) { char s[ROUTE_PERSISTENT_NAME_SIZE]; fprintf(stderr, "Route dump: jack audio port %p <%s> persistent name <%s> channel %d\n", jackPort, MusEGlobal::audioDevice->portName(jackPort, s, ROUTE_PERSISTENT_NAME_SIZE), persistentJackPortName, channel); } else fprintf(stderr, "Route dump: jack audio port %p persistent name <%s> channel %d\n", jackPort, persistentJackPortName, channel); } } else if (type == MIDI_PORT_ROUTE) { fprintf(stderr, "Route dump: midi port <%d> channel mask %d\n", midiPort, channel); } else if (type == MIDI_DEVICE_ROUTE) { fprintf(stderr, "Route dump: "); if(device) { if(device->deviceType() == MidiDevice::JACK_MIDI) { if(MusEGlobal::checkAudioDevice()) { fprintf(stderr, "jack midi device <%s> ", device->name().toLatin1().constData()); if(device->inClientPort()) { char s[ROUTE_PERSISTENT_NAME_SIZE]; fprintf(stderr, "input port <%s> ", //MusEGlobal::audioDevice->portName(device->inClientPort()).toLatin1().constData()); MusEGlobal::audioDevice->portName(device->inClientPort(), s, ROUTE_PERSISTENT_NAME_SIZE)); } if(device->outClientPort()) { char s[ROUTE_PERSISTENT_NAME_SIZE]; fprintf(stderr, "output port <%s> ", //MusEGlobal::audioDevice->portName(device->outClientPort()).toLatin1().constData()); MusEGlobal::audioDevice->portName(device->outClientPort(), s, ROUTE_PERSISTENT_NAME_SIZE)); } } } else if(device->deviceType() == MidiDevice::ALSA_MIDI) fprintf(stderr, "alsa midi device <%s> ", device->name().toLatin1().constData()); else if(device->deviceType() == MidiDevice::SYNTH_MIDI) fprintf(stderr, "synth midi device <%s> ", device->name().toLatin1().constData()); else fprintf(stderr, "is midi but unknown device type:%d, ", device->deviceType()); } else fprintf(stderr, "is midi but invalid device, "); fprintf(stderr, "channel:%d\n", channel); } else fprintf(stderr, "Route dump: unknown route type:%d\n", type); } //--------------------------------------------------------- // operator== //--------------------------------------------------------- bool Route::operator==(const Route& a) const { if ((type == a.type) && (channel == a.channel)) { if (type == TRACK_ROUTE) { return track == a.track && channels == a.channels && remoteChannel == a.remoteChannel; } else { if (type == JACK_ROUTE) { // If the ports are valid compare them, otherwise compare the persistent port names. if(jackPort && a.jackPort) return jackPort == a.jackPort; // Simplified. else return strcmp(persistentJackPortName, a.persistentJackPortName) == 0; } else if (type == MIDI_PORT_ROUTE) { return midiPort == a.midiPort; } else if (type == MIDI_DEVICE_ROUTE) { return device == a.device; } } } return false; } //--------------------------------------------------------- // exists //--------------------------------------------------------- bool Route::exists() const { switch(type) { case MusECore::Route::TRACK_ROUTE: return MusEGlobal::song->tracks()->contains(track); break; case MusECore::Route::JACK_ROUTE: return MusEGlobal::checkAudioDevice() && MusEGlobal::audioDevice->findPort(persistentJackPortName); break; case MusECore::Route::MIDI_DEVICE_ROUTE: return MusEGlobal::midiDevices.contains(device); break; case MusECore::Route::MIDI_PORT_ROUTE: return isValid(); break; } return false; } //--------------------------------------------------------- // compare //--------------------------------------------------------- bool Route::compare(const Route& a) const { //if ((type == a.type) && (channel == a.channel)) if (type == a.type) { if (type == TRACK_ROUTE) { return track == a.track && channels == a.channels && ((a.channel == -1) ? (channel == -1) : (channel != -1)) && //remoteChannel == a.remoteChannel; ((a.remoteChannel == -1) ? (remoteChannel == -1) : (remoteChannel != -1)); // TODO: Want this? Seems logical. } else //if(channel == a.channel) { if (type == JACK_ROUTE && channel == a.channel) { // If the ports are valid compare them, otherwise compare the persistent port names. if(jackPort && a.jackPort) return jackPort == a.jackPort; // Simplified. else return strcmp(persistentJackPortName, a.persistentJackPortName) == 0; } else if (type == MIDI_PORT_ROUTE) { return midiPort == a.midiPort; } else if (type == MIDI_DEVICE_ROUTE) { return device == a.device; } } } return false; } /* Please keep this just in case... //--------------------------------------------------------- // isCircularRoute // Recursive. // If dst is valid, start traversal from there, not from src. // Returns true if circular. //--------------------------------------------------------- bool isCircularRoutePath(Track* src, Track* dst) { bool rv = false; if(dst) { src->setNodeTraversed(true); rv = isCircularRoutePath(dst, NULL); src->setNodeTraversed(false); return rv; } if(src->nodeTraversed()) return true; src->setNodeTraversed(true); RouteList* orl = src->outRoutes(); for (iRoute i = orl->begin(); i != orl->end(); ++i) { if( !(*i).isValid() || (*i).type != Route::TRACK_ROUTE ) continue; Track* t = (*i).track; //if(t->isMidiTrack()) // continue; rv = isCircularRoutePath(src, NULL); if(rv) break; } src->setNodeTraversed(false); return rv; } */ } // namespace MusECore muse-4.0.0/src/muse/route.h000066400000000000000000000225571404105311000155330ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: route.h,v 1.5.2.1 2008/05/21 00:28:52 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // (C) Copyright 2011, 2015 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ROUTE_H__ #define __ROUTE_H__ #include #include #include #include "globaldefs.h" #define ROUTE_PERSISTENT_NAME_SIZE 256 // Size of char array Route::persistentName, including the terminating null character. class QPixmap; namespace MusECore { // Forward declarations: class MidiDevice; class Track; class Xml; struct RouteChannelsDescriptor { // Independent of _inChannels/_outChannels. Typically represents 'Omni' for objects // which have channels, otherwise it just means the object itself is routable. bool _inRoutable; bool _outRoutable; int _inChannels; int _outChannels; RouteChannelsDescriptor() : _inRoutable(false), _outRoutable(false), _inChannels(0), _outChannels(0) { } RouteChannelsDescriptor(bool inRoutable, bool outRoutable, int inChannels, int outChannels) : _inRoutable(inRoutable), _outRoutable(outRoutable), _inChannels(inChannels), _outChannels(outChannels) { } }; typedef RouteChannelsDescriptor TrackRouteDescriptor; typedef RouteChannelsDescriptor JackRouteDescriptor; typedef RouteChannelsDescriptor MidiDeviceRouteDescriptor; typedef RouteChannelsDescriptor MidiPortRouteDescriptor; struct RouteCapabilitiesStruct { TrackRouteDescriptor _trackChannels; JackRouteDescriptor _jackChannels; MidiDeviceRouteDescriptor _midiDeviceChannels; MidiPortRouteDescriptor _midiPortChannels; RouteCapabilitiesStruct() : _trackChannels(TrackRouteDescriptor()), _jackChannels(JackRouteDescriptor()), _midiDeviceChannels(MidiDeviceRouteDescriptor()), _midiPortChannels(MidiPortRouteDescriptor()) { } RouteCapabilitiesStruct(const TrackRouteDescriptor& trackChannels, const JackRouteDescriptor& jackChannels, const MidiDeviceRouteDescriptor& midiDeviceChannels, const MidiPortRouteDescriptor& midiPortChannels) : _trackChannels(trackChannels), _jackChannels(jackChannels), _midiDeviceChannels(midiDeviceChannels), _midiPortChannels(midiPortChannels) { } }; //--------------------------------------------------------- // Route //--------------------------------------------------------- class Route { public: enum RouteType { TRACK_ROUTE=0, JACK_ROUTE=1, MIDI_DEVICE_ROUTE=2, MIDI_PORT_ROUTE=3 }; union { Track* track; MidiDevice* device; void* jackPort; void* voidPointer; }; int midiPort; // Midi port number. Best not to put this in the union to avoid problems? //snd_seq_addr_t alsaAdr; // TODO // Midi channel, or starting audio source channel (of the owner of this route). // Normally zero for mono or stereo audio tracks, higher for multi-channel audio tracks. int channel; // Number of (audio) channels being routed. int channels; // Starting (audio) destination channel of the remote object (pointed to by the union or port etc.). int remoteChannel; RouteType type; // Always same as the port name. When connection disappears, this holds on to the name. char persistentJackPortName[ROUTE_PERSISTENT_NAME_SIZE]; //-------------------------------------------------------- // Temporary variables used during latency calculations: // Holds the output latency of this node, so that it can be compared with others. float audioLatencyOut; //-------------------------------------------------------- Route(void* t, int ch=-1); Route(Track* t, int ch = -1, int chans = -1); Route(MidiDevice* d, int ch = -1); Route(int port, int ch = -1); Route(const QString&, bool dst, int ch, int rtype = -1); Route(); Route(const Route&); // Copy constructor // Useful for generic complete construction. Route(RouteType type_, int midi_port_num_, void* void_pointer_, int channel_, int channels_, int remote_channel_, const char* name_); // Returns a suitable icon for the route based on type, track type etc. // isSource determines whether to return an in-facing icon or an out-facing icon. // isMidi determines whether to return an audio or midi themed icon. QIcon *icon(bool isSource = true, bool isMidi = false) const; // Create string name representation. // preferred_name_or_alias (mainly for Jack routes): -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. QString name(int preferred_name_or_alias = -1) const; // Fill and return str char name representation. // preferred_name_or_alias (mainly for Jack routes): -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. char* name(char* str, int str_size, int preferred_name_or_alias = -1) const; // Returns a name suitable for display like "1:Track 5" where the number is the track's index in the track list. // This is useful because in the case of importing a midi file we allow duplicate, often blank, names. // This display string will help identify them. Like "1:", "2:" etc. // preferred_name_or_alias (mainly for Jack routes): -1: No preference 0: Prefer canonical name 1: Prefer 1st alias 2: Prefer 2nd alias. QString displayName(int preferred_name_or_alias = -1) const; bool operator==(const Route&) const; // If the routes support channels, if the given route's channel is -1 meaning all channels, compare matches ONLY if this channel == -1, // and if the given route's channel is >= 0, compare matches on ANY channel. Useful for example finding router treeview items. bool compare(const Route&) const; bool exists() const; Route& operator=(const Route&); bool isValid() const { return ((type == TRACK_ROUTE) && (track != 0)) || (type == JACK_ROUTE) || // For persistent Jack routes: A NULL jackPort is actually valid. ((type == MIDI_DEVICE_ROUTE) && (device != 0)) || ((type == MIDI_PORT_ROUTE) && (midiPort >= 0) && (midiPort < MusECore::MIDI_PORTS)); } void read(Xml& xml); void dump() const; }; //--------------------------------------------------------- // RouteList //--------------------------------------------------------- class RouteList : public std::vector { public: iterator find(const Route& r) { return std::find(begin(), end(), r); } const_iterator find(const Route& r) const { return std::find(begin(), end(), r); } bool contains(const Route& r) const { return std::find(begin(), end(), r) != end(); } bool removeRoute(const Route& r) { iterator i = std::find(begin(), end(), r); if(i == end()) return false; erase(i); return true; } }; typedef RouteList::iterator iRoute; typedef RouteList::const_iterator ciRoute; // Returns true if something changed. extern bool addRoute(Route src, Route dst); // Returns true if something changed. extern bool removeRoute(Route src, Route dst); extern void removeAllRoutes(Route src, Route dst); extern Route name2route(const QString&, bool dst, int rtype = -1); // Returns true if the routes are found and they are connected. extern bool routeCanDisconnect(const Route& src, const Route& dst); // Returns true if the routes are found and they are not connected and CAN be connected. extern bool routeCanConnect(const Route& src, const Route& dst); // Returns true if the routes are found and they CAN be connected (even if they are already connected). // If check_types_only is true, it only compares route types. // Otherwise other parameters such as channels are also compared. extern bool routesCompatible(const Route& src, const Route& dst, bool check_types_only = false); } // namespace MusECore // Allow Routes to be a QVariant Q_DECLARE_METATYPE(MusECore::Route) #endif muse-4.0.0/src/muse/scripts.cpp000066400000000000000000000235311404105311000164100ustar00rootroot00000000000000#include #include #include "scripts.h" #include "part.h" #include "song.h" #include "debug.h" #include "undo.h" #include "icons.h" #include #include #include #include #include #include namespace MusECore { Scripts::Scripts(QObject *parent) : QObject(parent) {} //--------------------------------------------------------- // executeScript //--------------------------------------------------------- void Scripts::executeScript(QWidget *parent, const char* scriptfile, PartList* parts, int quant, bool onlyIfSelected) { // a simple format for external processing // will be extended if there is a need // // Semantics: // TIMESIG // PARTLEN // BEATLEN // QUANTLEN // NOTE // CONTROLLER
// if (onlyIfSelected) // if this is set means we are probably inside a midi editor and we ask again to be sure { QMessageBox m; m.setText(tr("Do you want to process all or only selected events?")); m.addButton(tr("&Selected"), QMessageBox::YesRole); m.addButton(tr("&All"), QMessageBox::NoRole); m.addButton(tr("&Cancel"), QMessageBox::RejectRole); m.exec(); if (m.buttonRole(m.clickedButton()) == QMessageBox::RejectRole) return; else if (m.buttonRole(m.clickedButton()) == QMessageBox::NoRole) onlyIfSelected = false; } QProgressDialog progress(parent); progress.setLabelText("Process parts"); progress.setRange(0,parts->size()); progress.setValue(0); progress.setCancelButton(nullptr); MusEGlobal::song->startUndo(); // undo this entire block for (const auto& i : *parts) { //const char* tmp = tmpnam(NULL); char tmp[16] = "muse-tmp-XXXXXX"; char tempStr[200]; int fd = mkstemp(tmp); if (MusEGlobal::debugMsg) fprintf(stderr, "executeScript: script input filename=%s\n",tmp); FILE *fp = fdopen(fd , "w"); MidiPart *part = (MidiPart*)(i.second); if (MusEGlobal::debugMsg) fprintf(stderr, "SENDING TO SCRIPT, part start: %d\n", part->tick()); int z, n; MusEGlobal::sigmap.timesig(part->tick(), z, n); sprintf(tempStr, "TIMESIG %d %d\n", z, n); writeStringToFile(fp,tempStr); sprintf(tempStr, "PART %d %d\n", part->tick(), part->lenTick()); writeStringToFile(fp,tempStr); sprintf(tempStr, "BEATLEN %d\n", MusEGlobal::sigmap.ticksBeat(part->tick())); writeStringToFile(fp,tempStr); sprintf(tempStr, "QUANTLEN %d\n", quant); writeStringToFile(fp,tempStr); if (MusEGlobal::debugMsg) std::cout << "Events in part " << part->events().size() << std::endl; EventList elist = part->events(); for (const auto& e : elist) { Event ev = e.second; if (ev.isNote()) { if (onlyIfSelected && ev.selected() == false) continue; sprintf(tempStr,"NOTE %d %d %d %d\n", ev.tick(), ev.dataA(), ev.lenTick(), ev.dataB()); writeStringToFile(fp,tempStr); // Operation is undoable but do not start/end undo. // Indicate do not do port controller values and clone parts. MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent, ev, part, false, false), Song::OperationUndoable); } else if (ev.type()==Controller) { sprintf(tempStr,"CONTROLLER %d %d %d %d\n", ev.tick(), ev.dataA(), ev.dataB(), ev.dataC()); writeStringToFile(fp,tempStr); // Operation is undoable but do not start/end undo. // Indicate do not do port controller values and clone parts. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, ev, part, false, false), Song::OperationUndoable); } } fclose(fp); QStringList arguments; arguments << tmp; QProcess *myProcess = new QProcess(parent); myProcess->start(scriptfile, arguments); myProcess->waitForFinished(); QByteArray errStr = myProcess->readAllStandardError(); if (myProcess->exitCode()) { QMessageBox::warning(parent, tr("MusE - external script failed"), tr("MusE was unable to launch the script, error message:\n%1").arg(QString(errStr))); MusEGlobal::song->endUndo(SC_EVENT_REMOVED); return; } if (errStr.size()> 0) { fprintf(stderr, "script execution produced the following error:\n%s\n", QString(errStr).toLatin1().data()); } QFile file(tmp); if (MusEGlobal::debugMsg) file.copy(file.fileName() + "_input"); if ( file.open( QIODevice::ReadOnly ) ) { QTextStream stream( &file ); QString line; if (MusEGlobal::debugMsg) fprintf(stderr, "RECEIVED FROM SCRIPT:\n"); while ( !stream.atEnd() ) { line = stream.readLine(); // line of text excluding '\n' if (MusEGlobal::debugMsg) { std::cout << line.toStdString() << std::endl; } if (line.startsWith("NOTE")) { QStringList sl = line.split(" "); Event e(Note); int tick = sl[1].toInt(); int pitch = sl[2].toInt(); int len = sl[3].toInt(); int velo = sl[4].toInt(); fprintf(stderr, "extracted %d %d %d %d\n", tick, pitch, len, velo); e.setTick(tick); e.setPitch(pitch); e.setVelo(velo); e.setLenTick(len); // Operation is undoable but do not start/end undo. // Indicate do not do port controller values and clone parts. MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddEvent, e, part, false, false), Song::OperationUndoable); } if (line.startsWith("CONTROLLER")) { QStringList sl = line.split(" "); Event e(Controller); int a = sl[2].toInt(); int b = sl[3].toInt(); int c = sl[4].toInt(); e.setA(a); e.setB(b); e.setB(c); // Operation is undoable but do not start/end undo. // Indicate do not do port controller values and clone parts. MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddEvent, e, part, false, false), Song::OperationUndoable); } } file.close(); } if (!MusEGlobal::debugMsg) // if we are writing debug info we also keep the script data remove(tmp); progress.setValue(progress.value()+1); } // for MusEGlobal::song->endUndo(SC_EVENT_REMOVED); } void Scripts::populateScriptMenu(QMenu* menuScripts) { menuScripts->clear(); // List scripts QString distScripts = MusEGlobal::museGlobalShare + "/scripts"; QString userScripts = MusEGlobal::configPath + "/scripts"; QFileInfo distScriptsFi(distScripts); if (distScriptsFi.isDir()) { QDir dir = QDir(distScripts); dir.setFilter(QDir::Executable | QDir::Files); deliveredScriptNames = dir.entryList(); } QFileInfo userScriptsFi(userScripts); if (userScriptsFi.isDir()) { QDir dir(userScripts); dir.setFilter(QDir::Executable | QDir::Files); userScriptNames = dir.entryList(); } int id = 0; if (deliveredScriptNames.size() > 0) { for (QStringList::Iterator it = deliveredScriptNames.begin(); it != deliveredScriptNames.end(); it++, id++) { QAction* act = menuScripts->addAction(*it); connect(act, &QAction::triggered, [this, id]() { receiveExecDeliveredScript(id); } ); } menuScripts->addSeparator(); } if (userScriptNames.size() > 0) { for (QStringList::Iterator it = userScriptNames.begin(); it != userScriptNames.end(); it++, id++) { QAction* act = menuScripts->addAction(*it); connect(act, &QAction::triggered, [this, id]() { receiveExecUserScript(id); } ); } menuScripts->addSeparator(); } QAction* refreshScriptsAction = menuScripts->addAction(tr("Reload Script Names from Disc")); refreshScriptsAction->setIcon(*MusEGui::fileopenSVGIcon); connect(refreshScriptsAction, &QAction::triggered, [this, menuScripts]() { populateScriptMenu(menuScripts); } ); } //--------------------------------------------------------- // getScriptPath //--------------------------------------------------------- QString Scripts::getScriptPath(int id, bool isdelivered) { if (isdelivered) { QString path = MusEGlobal::museGlobalShare + "/scripts/" + deliveredScriptNames[id]; return path; } QString path = MusEGlobal::configPath + "/scripts/" + userScriptNames[id - deliveredScriptNames.size()]; return path; } void Scripts::writeStringToFile(FILE *filePointer, const char *writeString) { if (MusEGlobal::debugMsg) std::cout << writeString; fputs(writeString, filePointer); } void Scripts::receiveExecDeliveredScript(int id) { execDeliveredScriptReceived(id); } void Scripts::receiveExecUserScript(int id) { execUserScriptReceived(id); } } muse-4.0.0/src/muse/scripts.h000066400000000000000000000014141404105311000160510ustar00rootroot00000000000000#ifndef SCRIPTS_H #define SCRIPTS_H #include class QMenu; namespace MusECore { class PartList; class Scripts : public QObject { Q_OBJECT QStringList deliveredScriptNames; QStringList userScriptNames; void writeStringToFile(FILE *filePointer, const char *writeString); void receiveExecDeliveredScript(int id); void receiveExecUserScript(int id); signals: void execDeliveredScriptReceived(int); void execUserScriptReceived(int); public: explicit Scripts(QObject *parent = nullptr); void populateScriptMenu(QMenu* menuScripts); void executeScript(QWidget *parent, const char* scriptfile, PartList* parts, int quant, bool onlyIfSelected); QString getScriptPath(int id, bool delivered); }; } #endif // SCRIPTS_H muse-4.0.0/src/muse/seqmsg.cpp000066400000000000000000000666761404105311000162410ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: seqmsg.cpp,v 1.32.2.17 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "song.h" #include "midiseq.h" #include "midiport.h" #include "minstrument.h" #include "app.h" #include "amixer.h" #include "tempo.h" ///#include "sig.h" #include "sig.h" #include "audio.h" #include "mididev.h" #include "audiodev.h" #include "alsamidi.h" #include "audio.h" #include "arranger.h" #include "plugin.h" #include "driver/jackmidi.h" #include "midi_warn_init_pending_impl.h" #include "gconfig.h" #include "operations.h" #include "ctrl.h" #include "globals.h" #include "metronome_class.h" #include "undo.h" namespace MusECore { //--------------------------------------------------------- // sendMsg //--------------------------------------------------------- // this function blocks until the request has been processed void Audio::sendMsg(AudioMsg* m) { static int sno = 0; if (_running) { m->serialNo = sno++; //DEBUG: msg = m; // wait for next audio "process" call to finish operation int no = -1; int rv = read(fromThreadFdr, &no, sizeof(int)); if (rv != sizeof(int)) perror("Audio: read pipe failed"); else if (no != (sno-1)) { fprintf(stderr, "audio: bad serial number, read %d expected %d\n", no, sno-1); } } else { // if audio is not running (during initialization) // process commands immediately processMsg(m); } } //--------------------------------------------------------- // sendMessage // send request from gui to sequencer // wait until request is processed //--------------------------------------------------------- bool Audio::sendMessage(AudioMsg* m, bool doUndo) { if (doUndo) MusEGlobal::song->startUndo(); sendMsg(m); if (doUndo) MusEGlobal::song->endUndo(0); return false; } //--------------------------------------------------------- // msgRemoveRoute //--------------------------------------------------------- void Audio::msgRemoveRoute(Route src, Route dst) { //fprintf(stderr, "Audio::msgRemoveRoute:\n"); msgRemoveRoute1(src, dst); MusEGlobal::song->connectJackRoutes(src, dst, true); } //--------------------------------------------------------- // msgRemoveRoute1 //--------------------------------------------------------- void Audio::msgRemoveRoute1(Route src, Route dst) { AudioMsg msg; msg.id = AUDIO_ROUTEREMOVE; msg.sroute = src; msg.droute = dst; sendMsg(&msg); } //--------------------------------------------------------- // msgAddRoute //--------------------------------------------------------- void Audio::msgAddRoute(Route src, Route dst) { //fprintf(stderr, "Audio::msgAddRoute:\n"); msgAddRoute1(src, dst); MusEGlobal::song->connectJackRoutes(src, dst); } //--------------------------------------------------------- // msgAddRoute1 //--------------------------------------------------------- void Audio::msgAddRoute1(Route src, Route dst) { AudioMsg msg; msg.id = AUDIO_ROUTEADD; msg.sroute = src; msg.droute = dst; sendMsg(&msg); } //--------------------------------------------------------- // msgAddPlugin //--------------------------------------------------------- void Audio::msgAddPlugin(AudioTrack* node, int idx, PluginI* plugin) { AudioMsg msg; msg.id = AUDIO_ADDPLUGIN; msg.snode = node; msg.ival = idx; msg.plugin = plugin; sendMsg(&msg); } //--------------------------------------------------------- // msgSetPrefader //--------------------------------------------------------- void Audio::msgSetPrefader(AudioTrack* node, int val) { AudioMsg msg; msg.id = AUDIO_SET_PREFADER; msg.snode = node; msg.ival = val; sendMsg(&msg); } //--------------------------------------------------------- // msgSetChannels //--------------------------------------------------------- void Audio::msgSetChannels(AudioTrack* node, int n) { if (n == node->channels()) return; QString name = node->name(); int mc = std::max(n, node->channels()); if (!name.isEmpty()) { if (node->type() == Track::AUDIO_INPUT) { if (!MusEGlobal::checkAudioDevice()) return; AudioInput* ai = (AudioInput*)node; for (int i = 0; i < mc; ++i) { if (i < n && ai->jackPort(i) == 0) { ai->registerPorts(i); } else if ((i >= n) && ai->jackPort(i)) { RouteList* ir = node->inRoutes(); for (ciRoute ii = ir->begin(); ii != ir->end(); ++ii) { Route r = *ii; if ((r.type == Route::JACK_ROUTE) && (r.channel == i)) { msgRemoveRoute(r, Route(node,i)); break; } } MusEGlobal::audioDevice->unregisterPort(ai->jackPort(i)); ai->setJackPort(i, 0); } } } else if (node->type() == Track::AUDIO_OUTPUT) { if (!MusEGlobal::checkAudioDevice()) return; AudioOutput* ao = (AudioOutput*)node; for (int i = 0; i < mc; ++i) { void* jp = ao->jackPort(i); if (i < n && jp == 0) { ao->registerPorts(i); } else if (i >= n && jp) { RouteList* ir = node->outRoutes(); for (ciRoute ii = ir->begin(); ii != ir->end(); ++ii) { Route r = *ii; if ((r.type == Route::JACK_ROUTE) && (r.channel == i)) { msgRemoveRoute(Route(node,i), r); break; } } MusEGlobal::audioDevice->unregisterPort(jp); ao->setJackPort(i, 0); } } } } // DELETETHIS 47 /* TODO TODO: Change all stereo routes to mono. // If we are going from stereo to mono we need to disconnect any stray synti 'mono last channel'... if(n == 1 && node->channels() > 1) { // This should always happen - syntis are fixed channels, user cannot change them. But to be safe... if(node->type() != Track::AUDIO_SOFTSYNTH) { if(node->type() != Track::AUDIO_INPUT) { RouteList* rl = node->inRoutes(); for(iRoute r = rl->begin(); r != rl->end(); ++r) { // Only interested in synth tracks. if(r->type != Route::TRACK_ROUTE || r->track->type() != Track::AUDIO_SOFTSYNTH) continue; // If it's the last channel... if(r->channel + 1 == ((AudioTrack*)r->track)->totalOutChannels()) { msgRemoveRoute(*r, Route(node, r->channel)); //msgRemoveRoute(r, Route(node, r->remoteChannel)); break; } } } if(node->type() != Track::AUDIO_OUTPUT) { RouteList* rl = node->outRoutes(); for(iRoute r = rl->begin(); r != rl->end(); ++r) { // Only interested in synth tracks. if(r->type != Route::TRACK_ROUTE || r->track->type() != Track::AUDIO_SOFTSYNTH) continue; // If it's the last channel... if(r->channel + 1 == ((AudioTrack*)r->track)->totalOutChannels()) { msgRemoveRoute(Route(node, r->channel), *r); //msgRemoveRoute(Route(node, r->remoteChannel), r); break; } } } } } */ AudioMsg msg; msg.id = AUDIO_SET_CHANNELS; msg.snode = node; msg.ival = n; sendMsg(&msg); } //--------------------------------------------------------- // msgSwapControllerIDX //--------------------------------------------------------- void Audio::msgSwapControllerIDX(AudioTrack* node, int idx1, int idx2) { AudioMsg msg; msg.id = AUDIO_SWAP_CONTROLLER_IDX; msg.snode = node; msg.a = idx1; msg.b = idx2; sendMsg(&msg); } //--------------------------------------------------------- // msgClearControllerEvents //--------------------------------------------------------- void Audio::msgClearControllerEvents(AudioTrack* node, int acid) { // AudioMsg msg; // // msg.id = AUDIO_CLEAR_CONTROLLER_EVENTS; // msg.snode = node; // msg.ival = acid; // sendMsg(&msg); ciCtrlList icl = node->controller()->find(acid); if(icl == node->controller()->end()) return; CtrlList* cl = icl->second; if(cl->empty()) return; CtrlList& clr = *icl->second; // The Undo system will take 'ownership' of these and delete them at the appropriate time. CtrlList* erased_list_items = new CtrlList(clr, CtrlList::ASSIGN_PROPERTIES); erased_list_items->insert(cl->begin(), cl->end()); if(erased_list_items->empty()) { delete erased_list_items; return; } MusEGlobal::song->applyOperation(UndoOp(UndoOp::ModifyAudioCtrlValList, node->controller(), erased_list_items, 0)); } //--------------------------------------------------------- // msgSeekPrevACEvent //--------------------------------------------------------- void Audio::msgSeekPrevACEvent(AudioTrack* node, int acid) { AudioMsg msg; msg.id = AUDIO_SEEK_PREV_AC_EVENT; msg.snode = node; msg.ival = acid; sendMsg(&msg); } //--------------------------------------------------------- // msgSeekNextACEvent //--------------------------------------------------------- void Audio::msgSeekNextACEvent(AudioTrack* node, int acid) { AudioMsg msg; msg.id = AUDIO_SEEK_NEXT_AC_EVENT; msg.snode = node; msg.ival = acid; sendMsg(&msg); } //--------------------------------------------------------- // msgEraseRangeACEvents //--------------------------------------------------------- void Audio::msgEraseRangeACEvents(AudioTrack* node, int acid, unsigned int frame1, unsigned int frame2) { ciCtrlList icl = node->controller()->find(acid); if(icl == node->controller()->end()) return; CtrlList* cl = icl->second; if(cl->empty()) return; if(frame2 < frame1) { const unsigned int tmp = frame1; frame1 = frame2; frame2 = tmp; } iCtrl s = cl->lower_bound(frame1); iCtrl e = cl->lower_bound(frame2); // No elements to erase? if(s == cl->end()) return; CtrlList& clr = *icl->second; // The Undo system will take 'ownership' of these and delete them at the appropriate time. CtrlList* erased_list_items = new CtrlList(clr, CtrlList::ASSIGN_PROPERTIES); erased_list_items->insert(s, e); if(erased_list_items->empty()) { delete erased_list_items; return; } MusEGlobal::song->applyOperation(UndoOp(UndoOp::ModifyAudioCtrlValList, node->controller(), erased_list_items, 0)); } //--------------------------------------------------------- // msgChangeACEvent //--------------------------------------------------------- void Audio::msgChangeACEvent(AudioTrack* node, int acid, int frame, int newFrame, double val) { AudioMsg msg; msg.id = AUDIO_CHANGE_AC_EVENT; msg.snode = node; msg.ival = acid; msg.a = frame; msg.b = newFrame; msg.dval = val; sendMsg(&msg); } //--------------------------------------------------------- // msgSeek //--------------------------------------------------------- void Audio::msgSeek(const Pos& pos) { if (!MusEGlobal::checkAudioDevice()) return; MusEGlobal::audioDevice->seekTransport(pos); } //--------------------------------------------------------- // msgExecuteOperationGroup //--------------------------------------------------------- void Audio::msgExecuteOperationGroup(Undo& operations) { MusEGlobal::song->executeOperationGroup1(operations); AudioMsg msg; msg.id = SEQM_EXECUTE_OPERATION_GROUP; msg.operations=&operations; sendMsg(&msg); MusEGlobal::song->executeOperationGroup3(operations); } //--------------------------------------------------------- // msgRevertOperationGroup //--------------------------------------------------------- void Audio::msgRevertOperationGroup(Undo& operations) { MusEGlobal::song->revertOperationGroup1(operations); AudioMsg msg; msg.id = SEQM_REVERT_OPERATION_GROUP; msg.operations=&operations; sendMsg(&msg); MusEGlobal::song->revertOperationGroup3(operations); } //--------------------------------------------------------- // msgExecutePendingOperations // Bypass the Undo system and directly execute the pending operations. //--------------------------------------------------------- void Audio::msgExecutePendingOperations(PendingOperationList& operations, bool doUpdate, SongChangedStruct_t extraFlags) { if(operations.empty()) return; AudioMsg msg; msg.id = SEQM_EXECUTE_PENDING_OPERATIONS; msg.pendingOps=&operations; sendMsg(&msg); operations.executeNonRTStage(); const SongChangedStruct_t flags = operations.flags() | extraFlags; if(doUpdate && flags != SC_NOTHING) { MusEGlobal::song->update(flags); MusEGlobal::song->setDirty(); } } //--------------------------------------------------------- // msgPlay //--------------------------------------------------------- void Audio::msgPlay(bool val) { if (val) { if (MusEGlobal::audioDevice) { // REMOVE Tim. countin. Removed. This is not good. It's been here for years, and the idea // was that the transport frame should start at what the user sees - the cursor tick position. // But cursor tick position should follow audio frame position, not the other way around. // The transport stops on some frame in-between ticks, and here virtually always it wants to jump back // to the cursor position's frame value upon play. It is almost never the same as the device frame position. // Nothing should ever set the cursor position then seek the audio. All seeking is done // through audio, and gui is informed that way. This causes problems with audio sync callback: // the seek happens while it is starting play mode, which it supports, but we don't want that. // Since this only happens with OUR transport buttons and NOT any another Jack Transport client (QJackCtl), // it is a strong argument among others pro and con, to remove this for consistency with the other clients. // Whatever issues removing this may cause should be fixable (they might be mistakes in the first place). // User will need to be aware that the transport may be in-between ticks, ie. should view the frames, // which currently are really only seen via the BigTime window. // unsigned sfr = MusEGlobal::song->cPos().frame(); // unsigned dcfr = MusEGlobal::audioDevice->getCurFrame(); // if(dcfr != sfr) // MusEGlobal::audioDevice->seekTransport(MusEGlobal::song->cPos()); MusEGlobal::audioDevice->startTransport(); } }else { if (MusEGlobal::audioDevice) MusEGlobal::audioDevice->stopTransport(); _bounceState = BounceOff; } } //--------------------------------------------------------- // msgExternalPlay //--------------------------------------------------------- void Audio::msgExternalPlay(bool val, bool doRewind) { if (val) { // Force the state to play immediately. state = PLAY; if (MusEGlobal::audioDevice) { //unsigned sfr = MusEGlobal::song->cPos().frame(); //unsigned dcfr = MusEGlobal::audioDevice->getCurFrame(); //if(dcfr != sfr) if(doRewind) MusEGlobal::audioDevice->seekTransport(0); MusEGlobal::audioDevice->startTransport(); } }else { state = STOP; if (MusEGlobal::audioDevice) MusEGlobal::audioDevice->stopTransport(); _bounceState = BounceOff; } } //--------------------------------------------------------- // msgRemoveTracks // remove all selected tracks //--------------------------------------------------------- void Audio::msgRemoveTracks() { Undo operations; TrackList* tl = MusEGlobal::song->tracks(); // NOTICE: This must be done in reverse order so that // 'undo' will repopulate in ascending index order! ciTrack it = tl->end(); while(it != tl->begin()) { --it; Track* tr = *it; if(tr->selected()) operations.push_back(UndoOp(UndoOp::DeleteTrack, MusEGlobal::song->tracks()->index(tr), tr)); } MusEGlobal::song->applyOperationGroup(operations); } //--------------------------------------------------------- // msgResetMidiDevices //--------------------------------------------------------- void Audio::msgResetMidiDevices() { AudioMsg msg; msg.id = SEQM_RESET_DEVICES; sendMessage(&msg, false); } //--------------------------------------------------------- // msgInitMidiDevices //--------------------------------------------------------- void Audio::msgInitMidiDevices(bool force) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; // // test for explicit instrument initialization // if(!force && MusEGlobal::config.warnInitPending) { bool found = false; if(MusEGlobal::song->click()) { MidiPort* mp = &MusEGlobal::midiPorts[metro_settings->clickPort]; if(mp->device() && (mp->device()->openFlags() & 1) && mp->instrument() && !mp->instrument()->midiInit()->empty() && !mp->initSent()) found = true; } if(!found) { for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; if(mp->device() && (mp->device()->openFlags() & 1) && mp->instrument() && !mp->instrument()->midiInit()->empty() && !mp->initSent()) { found = true; break; } } } if(found) { MusEGui::MidiWarnInitPendingDialog dlg; int rv = dlg.exec(); bool warn = !dlg.dontAsk(); if(warn != MusEGlobal::config.warnInitPending) { MusEGlobal::config.warnInitPending = warn; // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. //MusEGlobal::muse->changeConfig(true); // Save settings? No, wait till close. } if(rv != QDialog::Accepted) { if(MusEGlobal::config.midiSendInit) MusEGlobal::config.midiSendInit = false; //return; } else { if(!MusEGlobal::config.midiSendInit) MusEGlobal::config.midiSendInit = true; } } } // We can either try to do it in one cycle with one message, // or by idling the sequencer (gaining safe access to all structures) // for as much time as we need. // Here we COULD get away with the audio 'hiccup' that idling causes, // because it's unlikely someone would initialize during play... // But no midi is processed, so let's switch this only if requiring // large numbers of init values causes a problem later... #if 1 AudioMsg msg; msg.id = SEQM_INIT_DEVICES; msg.a = force; sendMessage(&msg, false); #else msgIdle(true); initDevices(force); msgIdle(false); #endif } //--------------------------------------------------------- // panic //--------------------------------------------------------- void Audio::msgPanic() { AudioMsg msg; msg.id = SEQM_PANIC; sendMessage(&msg, false); } //--------------------------------------------------------- // localOff //--------------------------------------------------------- void Audio::msgLocalOff() { AudioMsg msg; msg.id = SEQM_MIDI_LOCAL_OFF; sendMessage(&msg, false); } //--------------------------------------------------------- // msgUpdateSoloStates //--------------------------------------------------------- void Audio::msgUpdateSoloStates() { AudioMsg msg; msg.id = SEQM_UPDATE_SOLO_STATES; sendMsg(&msg); } //--------------------------------------------------------- // msgSetAux //--------------------------------------------------------- void Audio::msgSetAux(AudioTrack* track, int idx, double val) { AudioMsg msg; msg.id = SEQM_SET_AUX; msg.snode = track; msg.ival = idx; msg.dval = val; sendMessage(&msg, false); } //--------------------------------------------------------- // msgPlayMidiEvent //--------------------------------------------------------- void Audio::msgPlayMidiEvent(const MidiPlayEvent* event) { AudioMsg msg; msg.id = SEQM_PLAY_MIDI_EVENT; msg.p1 = event; sendMessage(&msg, false); } //--------------------------------------------------------- // msgSetHwCtrlState //--------------------------------------------------------- void Audio::msgSetHwCtrlState(MidiPort* port, int ch, int ctrl, int val) { AudioMsg msg; msg.id = SEQM_SET_HW_CTRL_STATE; msg.p1 = port; msg.a = ch; msg.b = ctrl; msg.c = val; sendMessage(&msg, false); } //--------------------------------------------------------- // msgSetHwCtrlState //--------------------------------------------------------- void Audio::msgSetHwCtrlStates(MidiPort* port, int ch, int ctrl, int val, int lastval) { AudioMsg msg; msg.id = SEQM_SET_HW_CTRL_STATES; msg.p1 = port; msg.a = ch; msg.b = ctrl; msg.c = val; msg.ival = lastval; sendMessage(&msg, false); } //--------------------------------------------------------- // msgSetTrackAutomationType //--------------------------------------------------------- void Audio::msgSetTrackAutomationType(Track* track, int type) { AudioMsg msg; msg.id = SEQM_SET_TRACK_AUTO_TYPE; msg.track = track; msg.ival = type; sendMessage(&msg, false); } //--------------------------------------------------------- // msgSetSendMetronome //--------------------------------------------------------- void Audio::msgSetSendMetronome(AudioTrack* track, bool b) { AudioMsg msg; msg.id = AUDIO_SET_SEND_METRONOME; msg.snode = track; msg.ival = (int)b; sendMessage(&msg, false); } //--------------------------------------------------------- // msgStartMidiLearn // Start learning midi //--------------------------------------------------------- void Audio::msgStartMidiLearn() { AudioMsg msg; msg.id = AUDIO_START_MIDI_LEARN; sendMessage(&msg, false); } //--------------------------------------------------------- // msgBounce // start bounce operation //--------------------------------------------------------- void Audio::msgBounce() { if (!MusEGlobal::checkAudioDevice()) return; MusEGlobal::audioDevice->seekTransport(MusEGlobal::song->lPos()); // Wait until seek takes effect. msgAudioWait(); msgAudioWait(); for(int i = 0; i < 100; ++i) { if(_syncReady) break; msgAudioWait(); } // Check if seek is really done. if(!_syncReady) { fprintf(stderr, "ERROR: Audio::msgBounce(): Sync not ready!\n"); return; } _bounceState = BounceStart; // REMOVE Tim. latency. Added. Moved here from audio thread process code (via Song::seqSignal()). if(MusEGlobal::config.freewheelMode) { MusEGlobal::audioDevice->setFreewheel(true); // Wait a few cycles for the freewheel to take effect. for(int i = 0; i < 4; ++i) { if(freewheel()) break; msgAudioWait(); } // Check if freewheel was really set. if(!freewheel()) { fprintf(stderr, "ERROR: Audio::msgBounce(): Freewheel mode did not start yet!\n"); } } } //--------------------------------------------------------- // msgIdle //--------------------------------------------------------- void Audio::msgIdle(bool on) { AudioMsg msg; msg.id = SEQM_IDLE; msg.a = on; sendMessage(&msg, false); } //--------------------------------------------------------- // msgAudioWait //--------------------------------------------------------- void Audio::msgAudioWait() { AudioMsg msg; msg.id = AUDIO_WAIT; sendMsg(&msg); } //--------------------------------------------------------- // msgSetMidiDevice // to avoid timeouts in the RT-thread, setMidiDevice // is done in GUI context after setting the audio and midi threads // into idle mode //--------------------------------------------------------- void Audio::msgSetMidiDevice(MidiPort* port, MidiDevice* device, MidiInstrument* instrument) { MusECore::AudioMsg msg; msg.id = MusECore::SEQM_IDLE; msg.a = true; //MusEGlobal::midiSeq->sendMsg(&msg); sendMsg(&msg); // Idle both audio and midi. port->setMidiDevice(device, instrument); msg.id = MusECore::SEQM_IDLE; msg.a = false; //MusEGlobal::midiSeq->sendMsg(&msg); sendMsg(&msg); // Idle both audio and midi. } } // namespace MusECore muse-4.0.0/src/muse/shortcuts.cpp000066400000000000000000001372271404105311000167670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/shortcuts.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // Author: Mathias Lundgren , (C) 2003 // // Copyright: Mathias Lundgren (lunar_shuttle@users.sourceforge.net) (C) 2003 // // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // // C++ Implementation: shortcuts // // Description: // Definition of shortcuts used in the application // #include "shortcuts.h" #include #include namespace MusEGui { ShortCut shortcuts[SHRT_NUM_OF_ELEMENTS]; void defShrt(int shrt, int key, const char* descr, int type, const char* xml) { shortcuts[shrt].key = key; shortcuts[shrt].descr = descr; shortcuts[shrt].type = type; shortcuts[shrt].xml = xml; } void initShortCuts() { //Global: defShrt(SHRT_PLAY_SONG, Qt::Key_Enter, QT_TRANSLATE_NOOP("shortcuts", "Transport: Start playback from current location"), GLOBAL_SHRT, "play"); defShrt(SHRT_TOGGLE_METRO, Qt::Key_C, QT_TRANSLATE_NOOP("shortcuts", "Transport: Toggle metronome"), GLOBAL_SHRT,"toggle_metro"); defShrt(SHRT_STOP, Qt::Key_Insert,QT_TRANSLATE_NOOP("shortcuts", "Transport: Stop Playback"), GLOBAL_SHRT, "stop"); defShrt(SHRT_GOTO_START, Qt::Key_W, QT_TRANSLATE_NOOP("shortcuts", "Transport: Goto Start"), GLOBAL_SHRT, "goto_start"); defShrt(SHRT_GOTO_END, Qt::SHIFT + Qt::Key_E, QT_TRANSLATE_NOOP("shortcuts", "Transport: Goto End"), GLOBAL_SHRT, "goto_end"); defShrt(SHRT_PLAY_TOGGLE, Qt::Key_Space, QT_TRANSLATE_NOOP("shortcuts", "Transport: Play, Stop, Rewind"), GLOBAL_SHRT, "play_toggle"); defShrt(SHRT_REC_RESTART, Qt::CTRL + Qt::Key_Space, QT_TRANSLATE_NOOP("shortcuts", "Transport: Restart recording"), GLOBAL_SHRT, "rec_restart"); defShrt(SHRT_REC_RESTART_MULTI, Qt::SHIFT + Qt::Key_Space, QT_TRANSLATE_NOOP("shortcuts", "Transport: Restart recording (multi take)"), GLOBAL_SHRT, "rec_restart_multi"); defShrt(SHRT_GOTO_LEFT, Qt::Key_End, QT_TRANSLATE_NOOP("shortcuts", "Transport: Goto left marker") , GLOBAL_SHRT, "goto_left"); defShrt(SHRT_GOTO_RIGHT, Qt::Key_PageDown, QT_TRANSLATE_NOOP("shortcuts", "Transport: Goto right marker") , GLOBAL_SHRT, "goto_right"); defShrt(SHRT_TOGGLE_LOOP, Qt::Key_Slash, QT_TRANSLATE_NOOP("shortcuts", "Transport: Toggle Loop section"), GLOBAL_SHRT, "toggle_loop"); defShrt(SHRT_START_REC, Qt::Key_Asterisk, QT_TRANSLATE_NOOP("shortcuts", "Transport: Toggle Record"), GLOBAL_SHRT, "toggle_rec"); defShrt(SHRT_REC_ARM_TRACK, Qt::CTRL + Qt::Key_Return, QT_TRANSLATE_NOOP("shortcuts", "Transport: Arm current track"), GLOBAL_SHRT, "rec_arm_track"); defShrt(SHRT_REC_CLEAR, Qt::Key_Backspace, QT_TRANSLATE_NOOP("shortcuts", "Transport: Clear all rec enabled tracks"), GLOBAL_SHRT, "rec_clear"); defShrt(SHRT_FULLSCREEN, Qt::CTRL + Qt::Key_F, QT_TRANSLATE_NOOP("shortcuts", "Toggle fullscreen"), GLOBAL_SHRT, "fullscreen"); defShrt(SHRT_TABBED_WIN, Qt::Key_F4, QT_TRANSLATE_NOOP("shortcuts", "Toggle tabbed/floating editor window"), GLOBAL_SHRT, "tabbed_win"); defShrt(SHRT_TOGGLE_DOCKS, Qt::Key_F7, QT_TRANSLATE_NOOP("shortcuts", "Toggle docks display"), GLOBAL_SHRT, "docks_display"); defShrt(SHRT_TOGGLE_REWINDONSTOP, Qt::CTRL + Qt::SHIFT + Qt::Key_R, QT_TRANSLATE_NOOP("shortcuts", "Toggle rewind on stop"), GLOBAL_SHRT, "rewindonstop"); defShrt(SHRT_ARRANGER, Qt::META + Qt::Key_A, QT_TRANSLATE_NOOP("shortcuts", "Show Arranger window"), GLOBAL_SHRT, "arranger"); defShrt(SHRT_CONFIG_SHORTCUTS, Qt::CTRL + Qt::SHIFT + Qt::Key_F1, QT_TRANSLATE_NOOP("shortcuts", "Settings: Configure shortcuts"), GLOBAL_SHRT, "configure_key_shortcuts"); defShrt(SHRT_COPY, Qt::CTRL + Qt::Key_C, QT_TRANSLATE_NOOP("shortcuts", "Edit: Copy"), INVIS_SHRT, "copy"); defShrt(SHRT_COPY_RANGE, Qt::CTRL + Qt::SHIFT + Qt::Key_C, QT_TRANSLATE_NOOP("shortcuts", "Edit: Copy in range"), SCORE_SHRT + PROLL_SHRT + DEDIT_SHRT + ARRANG_SHRT, "copy_range"); defShrt(SHRT_UNDO, Qt::CTRL + Qt::Key_Z, QT_TRANSLATE_NOOP("shortcuts", "Edit: Undo"), INVIS_SHRT, "undo"); defShrt(SHRT_REDO, Qt::CTRL + Qt::SHIFT + Qt::Key_Z, QT_TRANSLATE_NOOP("shortcuts", "Edit: Redo"), INVIS_SHRT, "redo"); defShrt(SHRT_CUT, Qt::CTRL + Qt::Key_X, QT_TRANSLATE_NOOP("shortcuts", "Edit: Cut"), INVIS_SHRT, "cut"); defShrt(SHRT_PASTE, Qt::CTRL + Qt::Key_V, QT_TRANSLATE_NOOP("shortcuts", "Edit: Paste"), INVIS_SHRT, "paste"); defShrt(SHRT_PASTE_TO_CUR_PART, Qt::CTRL + Qt::SHIFT + Qt::Key_V, QT_TRANSLATE_NOOP("shortcuts", "Edit: Paste to current part"), PROLL_SHRT + DEDIT_SHRT, "paste_to_current_part"); defShrt(SHRT_PASTE_TO_TRACK, Qt::CTRL + Qt::SHIFT + Qt::Key_V, QT_TRANSLATE_NOOP("shortcuts", "Edit: Paste to selected track"), ARRANG_SHRT, "paste_to_track"); defShrt(SHRT_PASTE_CLONE, Qt::CTRL + Qt::Key_B, QT_TRANSLATE_NOOP("shortcuts", "Edit: Paste clone"), ARRANG_SHRT, "paste_clones"); defShrt(SHRT_PASTE_CLONE_TO_TRACK, Qt::CTRL + Qt::SHIFT + Qt::Key_B, QT_TRANSLATE_NOOP("shortcuts", "Edit: Paste clone to selected track"), ARRANG_SHRT, "paste_clones_to_track"); defShrt(SHRT_PASTE_DIALOG, Qt::CTRL + Qt::Key_G, QT_TRANSLATE_NOOP("shortcuts", "Edit: Paste (with dialog)"), SCORE_SHRT + PROLL_SHRT + DEDIT_SHRT + ARRANG_SHRT, "paste_with_dialog"); defShrt(SHRT_DELETE, Qt::Key_Delete, QT_TRANSLATE_NOOP("shortcuts", "Edit: Delete"), INVIS_SHRT, "delete"); //----------------------------------------------------------- // Arranger: defShrt(SHRT_NEW, Qt::CTRL + Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "File: New project"), ARRANG_SHRT + DEDIT_SHRT, "new_project"); defShrt(SHRT_NEW_FROM_TEMPLATE, Qt::CTRL + Qt::SHIFT + Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "File: New project from template"), ARRANG_SHRT + DEDIT_SHRT, "new_project_from_template"); defShrt(SHRT_OPEN, Qt::CTRL + Qt::Key_O, QT_TRANSLATE_NOOP("shortcuts", "File: Open from disk"), ARRANG_SHRT + DEDIT_SHRT, "open_project"); defShrt(SHRT_SAVE, Qt::CTRL + Qt::Key_S, QT_TRANSLATE_NOOP("shortcuts", "File: Save project"), ARRANG_SHRT + DEDIT_SHRT, "save_project"); //----------------------------------------------------------- defShrt(SHRT_OPEN_RECENT, Qt::CTRL + Qt::Key_1, QT_TRANSLATE_NOOP("shortcuts", "File: Open recent file"), ARRANG_SHRT, "open_recent"); defShrt(SHRT_SAVE_AS, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Save as"), ARRANG_SHRT, "save_project_as"); defShrt(SHRT_SAVE_AS_NEW_PROJECT, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Save as new project"), ARRANG_SHRT, "save_new_project_as"); defShrt(SHRT_SAVE_REVISION, Qt::SHIFT + Qt::Key_R, QT_TRANSLATE_NOOP("shortcuts", "File: Save revision"), ARRANG_SHRT, "save_project_new_revision"); defShrt(SHRT_SAVE_AS_TEMPLATE, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Save as template"), ARRANG_SHRT, "save_project_template"); defShrt(SHRT_LOAD_TEMPLATE, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Load template"), ARRANG_SHRT, "load_template"); // defShrt(SHRT_CONFIG_PRINTER, Qt::CTRL + Qt::Key_P, QT_TRANSLATE_NOOP("shortcuts", "Configure printer"), ARRANG_SHRT, "config_printer"); defShrt(SHRT_IMPORT_MIDI, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Import midi file"), ARRANG_SHRT, "import_midi"); defShrt(SHRT_EXPORT_MIDI, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Export midi file"), ARRANG_SHRT, "export_midi"); defShrt(SHRT_IMPORT_PART, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Import midi part"), ARRANG_SHRT, "import_part"); defShrt(SHRT_IMPORT_AUDIO, 0 , QT_TRANSLATE_NOOP("shortcuts", "File: Import audio file"), ARRANG_SHRT, "import_audio"); defShrt(SHRT_QUIT, 0, QT_TRANSLATE_NOOP("shortcuts", "File: Quit MusE"), ARRANG_SHRT, "quit"); // defShrt(SHRT_DESEL_PARTS, Qt::CTRL + Qt::Key_B, QT_TRANSLATE_NOOP("shortcuts", "Deselect all parts"), ARRANG_SHRT, "deselect_parts"); defShrt(SHRT_RENAME_PART, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Rename part"), ARRANG_SHRT, "rename_part_on_track"); defShrt(SHRT_SELECT_PRTSTRACK, Qt::CTRL+ Qt::ALT + Qt::Key_P, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select parts on track"), ARRANG_SHRT, "select_parts_on_track"); defShrt(SHRT_OPEN_PIANO, Qt::CTRL + Qt::Key_E, QT_TRANSLATE_NOOP("shortcuts", "Open pianoroll"), ARRANG_SHRT, "open_pianoroll"); defShrt(SHRT_OPEN_DRUMS, Qt::CTRL + Qt::Key_D, QT_TRANSLATE_NOOP("shortcuts", "Open drumeditor"), ARRANG_SHRT, "open_drumedit"); defShrt(SHRT_OPEN_LIST, Qt::CTRL + Qt::Key_L, QT_TRANSLATE_NOOP("shortcuts", "Open listeditor"), ARRANG_SHRT, "open_listedit"); defShrt(SHRT_OPEN_WAVE, Qt::CTRL + Qt::Key_Y, QT_TRANSLATE_NOOP("shortcuts", "Open waveeditor"), ARRANG_SHRT, "open_waveeditor"); defShrt(SHRT_OPEN_GRAPHIC_MASTER, Qt::CTRL + Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Open graphical mastertrack editor"), ARRANG_SHRT, "open_graph_master"); defShrt(SHRT_OPEN_LIST_MASTER, Qt::CTRL + Qt::SHIFT + Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Open list mastertrack editor"), ARRANG_SHRT, "open_list_master"); defShrt(SHRT_OPEN_MIDI_TRANSFORM, Qt::CTRL + Qt::Key_T, QT_TRANSLATE_NOOP("shortcuts", "Open midi transformer"), ARRANG_SHRT, "open_midi_transform"); defShrt(SHRT_OPEN_PLUGIN_GUI, Qt::CTRL + Qt::Key_U, QT_TRANSLATE_NOOP("shortcuts", "Open track Synth GUI"), ARRANG_SHRT, "open_plugin_gui"); defShrt(SHRT_OPEN_PIANO_NEW, Qt::CTRL + Qt::SHIFT + Qt::Key_E, QT_TRANSLATE_NOOP("shortcuts", "Open pianoroll in new window"), ARRANG_SHRT, "open_pianoroll_new"); defShrt(SHRT_OPEN_DRUMS_NEW, Qt::CTRL + Qt::SHIFT + Qt::ALT + Qt::Key_D, QT_TRANSLATE_NOOP("shortcuts", "Open drumeditor in new window"), ARRANG_SHRT, "open_drumedit_new"); defShrt(SHRT_OPEN_LIST_NEW, Qt::CTRL + Qt::SHIFT + Qt::Key_L, QT_TRANSLATE_NOOP("shortcuts", "Open listeditor in new window"), ARRANG_SHRT, "open_listedit_new"); defShrt(SHRT_OPEN_WAVE_NEW, Qt::CTRL + Qt::SHIFT + Qt::Key_Y, QT_TRANSLATE_NOOP("shortcuts", "Open waveeditor in new window"), ARRANG_SHRT, "open_waveeditor_new"); defShrt(SHRT_ADD_MIDI_TRACK, Qt::SHIFT + Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Add midi track"), ARRANG_SHRT, "add_midi_track"); defShrt(SHRT_ADD_DRUM_TRACK, Qt::SHIFT + Qt::Key_D, QT_TRANSLATE_NOOP("shortcuts", "Add drum track"), ARRANG_SHRT, "add_drum_track"); defShrt(SHRT_ADD_WAVE_TRACK, Qt::SHIFT + Qt::Key_W, QT_TRANSLATE_NOOP("shortcuts", "Add wave track"), ARRANG_SHRT, "add_wave_track"); defShrt(SHRT_ADD_AUDIO_OUTPUT, Qt::SHIFT + Qt::Key_O, QT_TRANSLATE_NOOP("shortcuts", "Add audio output track"), ARRANG_SHRT, "add_audio_output"); defShrt(SHRT_ADD_AUDIO_GROUP, Qt::SHIFT + Qt::Key_G, QT_TRANSLATE_NOOP("shortcuts", "Add audio group track"), ARRANG_SHRT, "add_audio_group"); defShrt(SHRT_ADD_AUDIO_INPUT, Qt::SHIFT + Qt::Key_I, QT_TRANSLATE_NOOP("shortcuts", "Add audio input track"), ARRANG_SHRT, "add_audio_input"); defShrt(SHRT_ADD_AUDIO_AUX , Qt::SHIFT + Qt::Key_A, QT_TRANSLATE_NOOP("shortcuts", "Add audio aux track"), ARRANG_SHRT, "add_audio_aux"); defShrt(SHRT_ADD_SYNTH_TRACK, Qt::SHIFT + Qt::Key_S, QT_TRANSLATE_NOOP("shortcuts", "Add synth track dialog"), ARRANG_SHRT, "add_synth_track"); defShrt(SHRT_INSERT_MIDI_TRACK, 0, QT_TRANSLATE_NOOP("shortcuts", "Insert midi track"), ARRANG_SHRT, "insert_midi_track"); defShrt(SHRT_INSERT_DRUM_TRACK, 0, QT_TRANSLATE_NOOP("shortcuts", "Insert drum track"), ARRANG_SHRT, "insert_drum_track"); defShrt(SHRT_INSERT_WAVE_TRACK, 0, QT_TRANSLATE_NOOP("shortcuts", "Insert wave track"), ARRANG_SHRT, "insert_wave_track"); defShrt(SHRT_INSERT_AUDIO_OUTPUT, 0, QT_TRANSLATE_NOOP("shortcuts", "Insert audio output track"), ARRANG_SHRT, "insert_audio_output"); defShrt(SHRT_INSERT_AUDIO_GROUP, 0, QT_TRANSLATE_NOOP("shortcuts", "Insert audio group track"), ARRANG_SHRT, "insert_audio_group"); defShrt(SHRT_INSERT_AUDIO_INPUT, 0, QT_TRANSLATE_NOOP("shortcuts", "Insert audio input track"), ARRANG_SHRT, "insert_audio_input"); defShrt(SHRT_INSERT_AUDIO_AUX , 0, QT_TRANSLATE_NOOP("shortcuts", "Insert audio aux track"), ARRANG_SHRT, "insert_audio_aux"); defShrt(SHRT_INSERT_SYNTH_TRACK, 0, QT_TRANSLATE_NOOP("shortcuts", "Insert synth track dialog"), ARRANG_SHRT, "insert_synth_track"); defShrt(SHRT_GLOBAL_CUT, 0, QT_TRANSLATE_NOOP("shortcuts", "Structure: Global cut"), ARRANG_SHRT, "global_cut"); defShrt(SHRT_GLOBAL_INSERT, 0, QT_TRANSLATE_NOOP("shortcuts", "Structure: Global insert"), ARRANG_SHRT, "global_insert"); defShrt(SHRT_GLOBAL_SPLIT, 0, QT_TRANSLATE_NOOP("shortcuts", "Structure: Global split"), ARRANG_SHRT, "global_split"); defShrt(SHRT_CUT_EVENTS, 0, QT_TRANSLATE_NOOP("shortcuts", "Structure: Cut events"), ARRANG_SHRT, "cut_events"); //defShrt(SHRT_OPEN_MIXER, Qt::Key_F10, QT_TRANSLATE_NOOP("shortcuts", "View: Open mixer window"), ARRANG_SHRT, "toggle_mixer"); defShrt(SHRT_OPEN_MIXER, Qt::Key_F10, QT_TRANSLATE_NOOP("shortcuts", "View: Open mixer #1 window"), ARRANG_SHRT, "toggle_mixer"); defShrt(SHRT_OPEN_MIXER2, Qt::CTRL + Qt::Key_F10, QT_TRANSLATE_NOOP("shortcuts", "View: Open mixer #2 window"), ARRANG_SHRT, "toggle_mixer2"); defShrt(SHRT_OPEN_TRANSPORT, Qt::Key_F11, QT_TRANSLATE_NOOP("shortcuts", "View: Toggle transport window"), ARRANG_SHRT, "toggle_transport"); defShrt(SHRT_OPEN_BIGTIME, Qt::Key_F12, QT_TRANSLATE_NOOP("shortcuts", "View: Toggle bigtime window"), ARRANG_SHRT, "toggle_bigtime"); defShrt(SHRT_OPEN_MARKER, Qt::Key_F9, QT_TRANSLATE_NOOP("shortcuts", "View: Open marker window"), ARRANG_SHRT, "marker_window"); defShrt(SHRT_HIDE_MIXER_STRIP, Qt::Key_F3, QT_TRANSLATE_NOOP("shortcuts", "View: Toggle mixer strip display"), ARRANG_SHRT, "toggle_mixer_strip"); defShrt(SHRT_FOLLOW_JUMP, 0, QT_TRANSLATE_NOOP("shortcuts", "Settings: Follow song by page"), ARRANG_SHRT, "follow_jump"); defShrt(SHRT_FOLLOW_NO, 0, QT_TRANSLATE_NOOP("shortcuts", "Settings: Follow song off"), ARRANG_SHRT, "follow_no"); defShrt(SHRT_FOLLOW_CONTINUOUS, 0, QT_TRANSLATE_NOOP("shortcuts", "Settings: Follow song continuous"), ARRANG_SHRT, "follow_continuous"); defShrt(SHRT_GLOBAL_CONFIG, 0, QT_TRANSLATE_NOOP("shortcuts", "Settings: Global configuration"), ARRANG_SHRT, "configure_global"); //defShrt(SHRT_CONFIG_SHORTCUTS, Qt::CTRL + Qt::SHIFT + Qt::Key_F1, QT_TRANSLATE_NOOP("shortcuts", "Settings: Configure shortcuts"), ARRANG_SHRT, "configure_key_shortcuts"); defShrt(SHRT_CONFIG_METRONOME, 0, QT_TRANSLATE_NOOP("shortcuts", "Settings: Configure metronome"), ARRANG_SHRT, "configure_metronome"); defShrt(SHRT_CONFIG_MIDISYNC, 0, QT_TRANSLATE_NOOP("shortcuts", "Settings: Midi sync configuration"), ARRANG_SHRT, "configure_midi_sync"); defShrt(SHRT_MIDI_FILE_CONFIG, 0, QT_TRANSLATE_NOOP("shortcuts", "Settings: Midi file import/export configuration"), ARRANG_SHRT, "configure_midi_file"); defShrt(SHRT_APPEARANCE_SETTINGS, 0, QT_TRANSLATE_NOOP("shortcuts", "Settings: Appearance settings"), ARRANG_SHRT, "configure_appearance_settings"); defShrt(SHRT_CONFIG_MIDI_PORTS, 0, QT_TRANSLATE_NOOP("shortcuts", "Settings: Midi ports / Soft synths"), ARRANG_SHRT, "configure_midi_ports"); defShrt(SHRT_CONFIG_AUDIO_PORTS, 0, QT_TRANSLATE_NOOP("shortcuts", "Settings: Audio subsystem configuration"), ARRANG_SHRT, "configure_audio_ports"); //defShrt(SHRT_SAVE_GLOBAL_CONFIG, 0, QT_TRANSLATE_NOOP("shortcuts", "Save global configuration"), ARRANG_SHRT, "configure_save_global"); defShrt(SHRT_MIDI_EDIT_INSTRUMENTS, 0, QT_TRANSLATE_NOOP("shortcuts", "Midi: Edit midi instruments"), ARRANG_SHRT, "midi_edit_instruments"); defShrt(SHRT_MIDI_INPUT_TRANSFORM, 0, QT_TRANSLATE_NOOP("shortcuts", "Midi: Open midi input transform"), ARRANG_SHRT, "midi_open_input_transform"); defShrt(SHRT_MIDI_INPUT_FILTER, 0, QT_TRANSLATE_NOOP("shortcuts", "Midi: Open midi input filter"), ARRANG_SHRT, "midi_open_input_filter"); defShrt(SHRT_MIDI_INPUT_TRANSPOSE, 0, QT_TRANSLATE_NOOP("shortcuts", "Midi: Midi input transpose"), ARRANG_SHRT, "midi_open_input_transpose"); defShrt(SHRT_MIDI_REMOTE_CONTROL, 0, QT_TRANSLATE_NOOP("shortcuts", "Midi: Midi remote control"), ARRANG_SHRT, "midi_remote_control"); #ifdef BUILD_EXPERIMENTAL defShrt(SHRT_RANDOM_RHYTHM_GENERATOR,0,QT_TRANSLATE_NOOP("shortcuts", "Midi: Random rhythm generator"), ARRANG_SHRT, "midi_random_rhythm_generator"); #endif defShrt(SHRT_MIDI_RESET, 0, QT_TRANSLATE_NOOP("shortcuts", "Midi: Reset midi"), ARRANG_SHRT, "midi_reset"); defShrt(SHRT_MIDI_INIT, 0, QT_TRANSLATE_NOOP("shortcuts", "Midi: Init midi"), ARRANG_SHRT, "midi_init"); defShrt(SHRT_MIDI_LOCAL_OFF, 0, QT_TRANSLATE_NOOP("shortcuts", "Midi: Midi local off"), ARRANG_SHRT, "midi_local_off"); defShrt(SHRT_AUDIO_BOUNCE_TO_TRACK, 0, QT_TRANSLATE_NOOP("shortcuts", "Audio: Bounce audio to track"), ARRANG_SHRT, "audio_bounce_to_track"); defShrt(SHRT_AUDIO_BOUNCE_TO_FILE, 0, QT_TRANSLATE_NOOP("shortcuts", "Audio: Bounce audio to file"), ARRANG_SHRT, "audio_bounce_to_file"); defShrt(SHRT_AUDIO_RESTART, 0, QT_TRANSLATE_NOOP("shortcuts", "Audio: Restart audio"), ARRANG_SHRT, "audio_restart"); // REMOVE Tim. automation. Removed. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. // defShrt(SHRT_MIXER_AUTOMATION, 0, QT_TRANSLATE_NOOP("shortcuts", "Automation: Mixer automation"), ARRANG_SHRT, "mixer_automation"); defShrt(SHRT_MIXER_SNAPSHOT, 0, QT_TRANSLATE_NOOP("shortcuts", "Automation: Take mixer snapshot"), ARRANG_SHRT, "mixer_snapshot"); defShrt(SHRT_MIXER_AUTOMATION_CLEAR,0, QT_TRANSLATE_NOOP("shortcuts", "Automation: Clear mixer automation"), ARRANG_SHRT, "mixer_automation_clear"); // defShrt(SHRT_OPEN_CLIPS, 0, QT_TRANSLATE_NOOP("shortcuts", "View audio clips"), ARRANG_SHRT, "view_audio_clips"); defShrt(SHRT_OPEN_HELP, Qt::Key_F1, QT_TRANSLATE_NOOP("shortcuts", "Help: Open Manual"), ARRANG_SHRT, "open_help"); defShrt(SHRT_START_WHATSTHIS, Qt::SHIFT + Qt::Key_F1, QT_TRANSLATE_NOOP("shortcuts", "Help: Toggle whatsthis mode"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT, "toggle_whatsthis"); defShrt(SHRT_EDIT_PART, Qt::Key_Return, QT_TRANSLATE_NOOP("shortcuts", "Edit: Edit selected part"), ARRANG_SHRT, "edit_selected_part"); defShrt(SHRT_SEL_ABOVE, Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select nearest part on track above"), ARRANG_SHRT, "sel_part_above"); defShrt(SHRT_SEL_ABOVE_ADD, Qt::SHIFT + Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Edit: Add nearest part on track above"), ARRANG_SHRT, "sel_part_above_add"); defShrt(SHRT_SEL_BELOW, Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select nearest part on track below"), ARRANG_SHRT, "sel_part_below"); defShrt(SHRT_SEL_BELOW_ADD, Qt::SHIFT + Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Edit: Add nearest part on track below"), ARRANG_SHRT, "sel_part_below_add"); defShrt(SHRT_INSERTMEAS, Qt::CTRL + Qt::SHIFT + Qt::Key_O, QT_TRANSLATE_NOOP("shortcuts", "Edit: Insert empty measure"), ARRANG_SHRT, "insert_measure"); defShrt(SHRT_DUPLICATE_TRACK, Qt::CTRL + Qt::SHIFT + Qt::Key_D, QT_TRANSLATE_NOOP("shortcuts", "Edit: Duplicate track"), ARRANG_SHRT, "duplicate_track"); defShrt(SHRT_MOVEUP_TRACK, Qt::CTRL + Qt::SHIFT + Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Edit: Move tracks up"), ARRANG_SHRT, "moveup_track"); defShrt(SHRT_MOVEDOWN_TRACK, Qt::CTRL + Qt::SHIFT + Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Edit: Move tracks down"), ARRANG_SHRT, "movedown_track"); defShrt(SHRT_MOVETOP_TRACK, Qt::CTRL + Qt::SHIFT + Qt::Key_Home, QT_TRANSLATE_NOOP("shortcuts", "Edit: Move tracks to top"), ARRANG_SHRT, "movetop_track"); defShrt(SHRT_MOVEBOTTOM_TRACK, Qt::CTRL + Qt::SHIFT + Qt::Key_End, QT_TRANSLATE_NOOP("shortcuts", "Edit: Move tracks to bottom"), ARRANG_SHRT, "movebottom_track"); defShrt(SHRT_SEL_TRACK_ABOVE, Qt::CTRL + Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Select track above"), ARRANG_SHRT, "sel_track_above"); defShrt(SHRT_SEL_TRACK_BELOW, Qt::CTRL + Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Select track below"), ARRANG_SHRT, "sel_track_below"); defShrt(SHRT_EDIT_TRACK_NAME, Qt::Key_F2, QT_TRANSLATE_NOOP("shortcuts", "Edit selected track name"), ARRANG_SHRT, "edit_track_name"); defShrt(SHRT_MUTE_CURRENT_TRACKS, Qt::META + Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Mute all currently selected tracks"), ARRANG_SHRT, "mute_curren_tracks"); defShrt(SHRT_SOLO_CURRENT_TRACKS, Qt::META + Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "Solo all currently selected tracks"), ARRANG_SHRT, "solo_curren_tracks"); //----------------------------------------------------------- defShrt(SHRT_TRANSPOSE, 0, QT_TRANSLATE_NOOP("shortcuts", "Midi: Transpose"), ARRANG_SHRT + PROLL_SHRT + SCORE_SHRT, "midi_transpose"); //----------------------------------------------------------- defShrt(SHRT_SELECT_ALL, Qt::CTRL + Qt::Key_A, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select all"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT + SCORE_SHRT, "sel_all"); defShrt(SHRT_SELECT_NONE, Qt::CTRL + Qt::SHIFT + Qt::Key_A, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select none"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT + SCORE_SHRT, "sel_none"); defShrt(SHRT_SELECT_INVERT, Qt::CTRL + Qt::Key_I, QT_TRANSLATE_NOOP("shortcuts", "Edit: Invert Selection"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "sel_inv"); defShrt(SHRT_SELECT_ILOOP, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select events/parts inside locators"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "sel_ins_loc"); defShrt(SHRT_SELECT_OLOOP, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select events/parts outside locators"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "sel_out_loc"); defShrt(SHRT_SELECT_PREV_PART, Qt::ALT + Qt::Key_Left, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select previous part"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_prv_prt"); defShrt(SHRT_SELECT_NEXT_PART, Qt::ALT + Qt::Key_Right, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select next part"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_nxt_prt"); defShrt(SHRT_SEL_LEFT, Qt::Key_Left, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select nearest part/event to the left or move cursor"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_left"); defShrt(SHRT_SEL_LEFT_ADD, Qt::Key_Left + Qt::SHIFT, QT_TRANSLATE_NOOP("shortcuts", "Edit: Add nearest part/event to the left to selection"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_left_add"); defShrt(SHRT_SEL_RIGHT, Qt::Key_Right, QT_TRANSLATE_NOOP("shortcuts", "Edit: Select nearest part/event to the right or move cursor"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT,"sel_right"); defShrt(SHRT_SEL_RIGHT_ADD, Qt::Key_Right + Qt::SHIFT, QT_TRANSLATE_NOOP("shortcuts", "Edit: Add nearest part/event to the right to selection"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT,"sel_right_add"); defShrt(SHRT_LOCATORS_TO_SELECTION, Qt::ALT + Qt::Key_P, QT_TRANSLATE_NOOP("shortcuts", "Edit: Set range markers to selection"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "loc_to_sel"); defShrt(SHRT_INC_PITCH, Qt::CTRL + Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Edit: Increase pitch"), PROLL_SHRT + DEDIT_SHRT, "sel_inc_pitch"); defShrt(SHRT_DEC_PITCH, Qt::CTRL + Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Edit: Decrease pitch"), PROLL_SHRT + DEDIT_SHRT, "sel_dec_pitch"); defShrt(SHRT_INC_POS, Qt::CTRL + Qt::Key_Right, QT_TRANSLATE_NOOP("shortcuts", "Edit: Increase event position"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_inc_pos"); defShrt(SHRT_DEC_POS, Qt::CTRL + Qt::Key_Left, QT_TRANSLATE_NOOP("shortcuts", "Edit: Decrease event position"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "sel_dec_pos"); defShrt(SHRT_ZOOM_IN, Qt::CTRL + Qt::Key_PageUp, QT_TRANSLATE_NOOP("shortcuts", "View: Zoom in"), PROLL_SHRT + DEDIT_SHRT + ARRANG_SHRT + WAVE_SHRT, "zoom_in"); defShrt(SHRT_ZOOM_OUT, Qt::CTRL + Qt::Key_PageDown, QT_TRANSLATE_NOOP("shortcuts", "View: Zoom out"), PROLL_SHRT + DEDIT_SHRT + ARRANG_SHRT + WAVE_SHRT, "zoom_out"); defShrt(SHRT_GOTO_CPOS, Qt::Key_I, QT_TRANSLATE_NOOP("shortcuts", "View: Goto Current Position"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "goto_cpos"); defShrt(SHRT_SCROLL_LEFT, Qt::Key_H, QT_TRANSLATE_NOOP("shortcuts", "View: Scroll left"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "scroll_left"); defShrt(SHRT_SCROLL_RIGHT, Qt::Key_L, QT_TRANSLATE_NOOP("shortcuts", "View: Scroll left"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "scroll_right"); defShrt(SHRT_STEP_RECORD, Qt::CTRL + Qt::Key_R, QT_TRANSLATE_NOOP("shortcuts", "Transport: Step record"), PROLL_SHRT + DEDIT_SHRT, "step_record"); defShrt(SHRT_MIDI_INPUT, Qt::CTRL + Qt::Key_U, QT_TRANSLATE_NOOP("shortcuts", "Transport: Midi input"), PROLL_SHRT + DEDIT_SHRT, "midi_input"); defShrt(SHRT_PLAY_EVENTS, Qt::CTRL + Qt::Key_L, QT_TRANSLATE_NOOP("shortcuts", "Transport: Play events"), PROLL_SHRT + DEDIT_SHRT, "play_events"); defShrt(SHRT_INC_VELOCITY, Qt::ALT + Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Edit: Increase velocity"), PROLL_SHRT + DEDIT_SHRT, "increase_velocity"); defShrt(SHRT_DEC_VELOCITY, Qt::ALT + Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Edit: Decrease velocity"), PROLL_SHRT + DEDIT_SHRT, "decrease_velocity"); //----------------------------------------------------------- //Drum: //----------------------------------------------------------- defShrt(SHRT_FIXED_LEN, Qt::ALT + Qt::Key_L, QT_TRANSLATE_NOOP("shortcuts", "Edit: Set Fixed Length on Midi Events"), PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "midi_fixed_len"); //----------------------------------------------------------- //Pianoroll: //----------------------------------------------------------- defShrt(SHRT_QUANTIZE, 0, QT_TRANSLATE_NOOP("shortcuts", "Quantize"), PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "midi_quant"); defShrt(SHRT_MODIFY_GATE_TIME, 0, QT_TRANSLATE_NOOP("shortcuts", "Modify Note Length"), PROLL_SHRT + SCORE_SHRT, "midi_mod_gate_time"); defShrt(SHRT_MODIFY_VELOCITY, 0, QT_TRANSLATE_NOOP("shortcuts", "Modify Velocity"), PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "midi_mod_velo"); defShrt(SHRT_CRESCENDO, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Crescendo"), PROLL_SHRT + DEDIT_SHRT, "midi_crescendo"); defShrt(SHRT_THIN_OUT, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Thin Out"), PROLL_SHRT + DEDIT_SHRT, "midi_thin_out"); defShrt(SHRT_ERASE_EVENT, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Erase Event"), PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "midi_erase_event"); defShrt(SHRT_DELETE_OVERLAPS, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Delete Overlaps"), PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "midi_delete_overlaps"); defShrt(SHRT_NOTE_SHIFT, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Note Shift"), PROLL_SHRT + DEDIT_SHRT + SCORE_SHRT, "midi_note_shift"); defShrt(SHRT_MOVE_CLOCK, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Move Clock"), PROLL_SHRT + DEDIT_SHRT, "midi_move_clock"); defShrt(SHRT_COPY_MEASURE, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Copy Measure"), PROLL_SHRT + DEDIT_SHRT, "midi_copy_measure"); defShrt(SHRT_ERASE_MEASURE, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Erase Measure"), PROLL_SHRT + DEDIT_SHRT,"midi_erase_measure"); defShrt(SHRT_DELETE_MEASURE, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Delete Measure"), PROLL_SHRT + DEDIT_SHRT, "midi_delete_measure"); defShrt(SHRT_CREATE_MEASURE, 0, QT_TRANSLATE_NOOP("shortcuts", "Edit: Create Measure"), PROLL_SHRT + DEDIT_SHRT, "midi_create_measure"); defShrt(SHRT_EVENT_COLOR, Qt::Key_E, QT_TRANSLATE_NOOP("shortcuts", "Edit: Change Event Color"), PROLL_SHRT + SCORE_SHRT, "change_event_color"); defShrt(SHRT_MOVE_PLAY_TO_NOTE, Qt::CTRL + Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Move: Move to selected note"), PROLL_SHRT + DEDIT_SHRT, "move_to_note"); defShrt(SHRT_USE_LAST_EDITED_EVENT, Qt::SHIFT + Qt::Key_L, QT_TRANSLATE_NOOP("shortcuts", "Edit: Use Last Edited event"), PROLL_SHRT, "use_last_edited_event"); // Shortcuts for tools // global defShrt(SHRT_TOOL_POINTER, Qt::Key_A, QT_TRANSLATE_NOOP("shortcuts", "Tool: Pointer"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT + SCORE_SHRT, "pointer_tool"); defShrt(SHRT_TOOL_PENCIL, Qt::Key_D, QT_TRANSLATE_NOOP("shortcuts", "Tool: Pencil"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT + SCORE_SHRT, "pencil_tool"); defShrt(SHRT_TOOL_RUBBER, Qt::Key_R, QT_TRANSLATE_NOOP("shortcuts", "Tool: Eraser"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT + SCORE_SHRT, "eraser_tool"); defShrt(SHRT_TOOL_PAN, Qt::Key_P, QT_TRANSLATE_NOOP("shortcuts", "Tool: Pan"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "pan_tool"); defShrt(SHRT_TOOL_ZOOM, Qt::Key_Z, QT_TRANSLATE_NOOP("shortcuts", "Tool: Zoom"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "zoom_tool"); // piano roll & drum editor defShrt(SHRT_TOOL_LINEDRAW, Qt::Key_F, QT_TRANSLATE_NOOP("shortcuts", "Tool: Line Draw"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT, "line_draw_tool"); // drum editor defShrt(SHRT_TOOL_CURSOR, Qt::Key_U, QT_TRANSLATE_NOOP("shortcuts", "Tool: Cursor"), DEDIT_SHRT, "cursor_tool"); defShrt(SHRT_ADDNOTE_1, Qt::Key_V, QT_TRANSLATE_NOOP("shortcuts", "Add note velocity 1"), DEDIT_SHRT, "add_note_velocity_1"); defShrt(SHRT_ADDNOTE_2, Qt::Key_B, QT_TRANSLATE_NOOP("shortcuts", "Add note velocity 2"), DEDIT_SHRT, "add_note_velocity_2"); defShrt(SHRT_ADDNOTE_3, Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "Add note velocity 3"), DEDIT_SHRT, "add_note_velocity_3"); defShrt(SHRT_ADDNOTE_4, Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Add note velocity 4"), DEDIT_SHRT, "add_note_velocity_4"); defShrt(SHRT_CURSOR_STEP_UP, Qt::Key_0, QT_TRANSLATE_NOOP("shortcuts", "Cursor step size: larger"), DEDIT_SHRT, "cursor_step_up"); defShrt(SHRT_CURSOR_STEP_DOWN, Qt::Key_9, QT_TRANSLATE_NOOP("shortcuts", "Cursor step size: smaller"), DEDIT_SHRT, "cursor_step_down"); defShrt(SHRT_INSTRUMENT_STEP_UP, Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Instrument/Cursor up"), DEDIT_SHRT, "instrument_up"); defShrt(SHRT_INSTRUMENT_STEP_DOWN, Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Instrument/Cursor down"), DEDIT_SHRT, "instrument_down"); // wave editor defShrt(SHRT_TOOL_RANGE, Qt::Key_Y, QT_TRANSLATE_NOOP("shortcuts", "Tool: Range"), WAVE_SHRT, "range_tool"); defShrt(SHRT_TOOL_STRETCH, Qt::Key_T, QT_TRANSLATE_NOOP("shortcuts", "Tool: Stretch"), WAVE_SHRT, "stretch_tool"); defShrt(SHRT_TOOL_SAMPLERATE, Qt::Key_Q, QT_TRANSLATE_NOOP("shortcuts", "Tool: Sample rate"), WAVE_SHRT, "samplerate_tool"); // arranger defShrt(SHRT_TOOL_SCISSORS, Qt::Key_S, QT_TRANSLATE_NOOP("shortcuts", "Tool: Scissor"), ARRANG_SHRT, "scissor_tool"); defShrt(SHRT_TOOL_GLUE, Qt::Key_G, QT_TRANSLATE_NOOP("shortcuts", "Tool: Glue"), ARRANG_SHRT, "glue_tool"); defShrt(SHRT_TOOL_MUTE, Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Tool: Mute"), ARRANG_SHRT, "mute_tool"); //Increase/decrease current position, is going to be in arranger & drumeditor as well // p4.0.10 Editors and arranger handle these by themselves, otherwise global handler will now use them, too. defShrt(SHRT_POS_INC, Qt::Key_Plus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase"); defShrt(SHRT_POS_DEC, Qt::Key_Minus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease"); defShrt(SHRT_POS_INC_NOSNAP, Qt::SHIFT + Qt::Key_Plus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position, no snap"), GLOBAL_SHRT, "curpos_increase_nosnap"); defShrt(SHRT_POS_DEC_NOSNAP, Qt::SHIFT + Qt::Key_Minus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position, no snap"), GLOBAL_SHRT, "curpos_decrease_nosnap"); /* defShrt(SHRT_POS_INC_BAR, Qt::CTRL + Qt::ALT + Qt::Key_Plus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_bar"); defShrt(SHRT_POS_DEC_BAR, Qt::CTRL + Qt::ALT + Qt::Key_Minus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_bar"); defShrt(SHRT_POS_INC_BAR_NOSNAP, Qt::SHIFT + Qt::CTRL + Qt::ALT + Qt::Key_Plus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_bar_nosnap"); defShrt(SHRT_POS_DEC_BAR_NOSNAP, Qt::SHIFT + Qt::CTRL + Qt::ALT + Qt::Key_Minus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_bar_nosnap"); defShrt(SHRT_POS_INC_BEAT, Qt::ALT + Qt::Key_Plus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_beat"); defShrt(SHRT_POS_DEC_BEAT, Qt::ALT + Qt::Key_Minus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_beat"); defShrt(SHRT_POS_INC_BEAT_NOSNAP, Qt::SHIFT + Qt::ALT + Qt::Key_Plus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_beat_nosnap"); defShrt(SHRT_POS_DEC_BEAT_NOSNAP, Qt::SHIFT + Qt::ALT + Qt::Key_Minus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_beat_nosnap"); defShrt(SHRT_POS_INC_TICK, Qt::CTRL + Qt::Key_Plus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_tick"); defShrt(SHRT_POS_DEC_TICK, Qt::CTRL + Qt::Key_Minus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_tick"); defShrt(SHRT_POS_INC_TICK_NOSNAP, Qt::SHIFT + Qt::CTRL + Qt::Key_Plus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_tick"); defShrt(SHRT_POS_DEC_TICK_NOSNAP, Qt::SHIFT + Qt::CTRL + Qt::Key_Minus, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_tick"); defShrt(SHRT_POS_INC_FRAME, Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_frame"); defShrt(SHRT_POS_DEC_FRAME, Qt::Key_B, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_frame"); defShrt(SHRT_POS_INC_SECOND, Qt::CTRL + Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_second"); defShrt(SHRT_POS_DEC_SECOND, Qt::CTRL + Qt::Key_B, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_second"); defShrt(SHRT_POS_INC_SECOND_NOSNAP, Qt::SHIFT + Qt::CTRL + Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_second_nosnap"); defShrt(SHRT_POS_DEC_SECOND_NOSNAP, Qt::SHIFT + Qt::CTRL + Qt::Key_B, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_second_nosnap"); defShrt(SHRT_POS_INC_MINUTE, Qt::ALT + Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_minute"); defShrt(SHRT_POS_DEC_MINUTE, Qt::ALT + Qt::Key_B, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_minute"); defShrt(SHRT_POS_INC_MINUTE_NOSNAP, Qt::SHIFT + Qt::ALT + Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "Transport: Increase current position"), GLOBAL_SHRT, "curpos_increase_minute_nosnap"); defShrt(SHRT_POS_DEC_MINUTE_NOSNAP, Qt::SHIFT + Qt::ALT + Qt::Key_B, QT_TRANSLATE_NOOP("shortcuts", "Transport: Decrease current position"), GLOBAL_SHRT, "curpos_decrease_minute_nosnap"); */ defShrt(SHRT_SET_QUANT_BAR, 0, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Set quantize to bar"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_bar"); defShrt(SHRT_SET_QUANT_OFF, Qt::Key_8, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Set quantize to off"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_off"); defShrt(SHRT_SET_QUANT_1, Qt::Key_1, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Set quantize to 1/1 note"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_1"); defShrt(SHRT_SET_QUANT_2, Qt::Key_2, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Set quantize to 1/2 note"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_2"); defShrt(SHRT_SET_QUANT_3, Qt::Key_3, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Set quantize to 1/4 note"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_3"); defShrt(SHRT_SET_QUANT_4, Qt::Key_4, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Set quantize to 1/8 note"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_4"); defShrt(SHRT_SET_QUANT_5, Qt::Key_5, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Set quantize to 1/16 note"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_5"); defShrt(SHRT_SET_QUANT_6, Qt::Key_6, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Set quantize to 1/32 note"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_6"); defShrt(SHRT_SET_QUANT_7, Qt::Key_7, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Set quantize to 1/64 note"), ARRANG_SHRT + PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_7"); defShrt(SHRT_TOGGLE_TRIOL, Qt::Key_T, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Toggle triol quantization"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_triol"); defShrt(SHRT_TOGGLE_PUNCT, Qt::Key_Period, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Toggle punctuation quantization"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_punct"); defShrt(SHRT_TOGGLE_PUNCT2, Qt::Key_Comma, QT_TRANSLATE_NOOP("shortcuts", "Quantize: Toggle punctuation quantization (2)"), PROLL_SHRT + DEDIT_SHRT + WAVE_SHRT, "midi_quant_punct2"); defShrt(SHRT_INSERT_AT_LOCATION, Qt::SHIFT + Qt::Key_Right, QT_TRANSLATE_NOOP("shortcuts", "Edit: Insert at location"), PROLL_SHRT, "midi_insert_at_loc"); defShrt(SHRT_INCREASE_LEN, Qt::CTRL + Qt::SHIFT + Qt::Key_Right, QT_TRANSLATE_NOOP("shortcuts", "Edit: Increase length"), PROLL_SHRT, "increase_len"); defShrt(SHRT_DECREASE_LEN, Qt::CTRL + Qt::SHIFT + Qt::Key_Left, QT_TRANSLATE_NOOP("shortcuts", "Edit: Decrease length"), PROLL_SHRT, "decrease_len"); //----------------------------------------------------------- // List edit: //----------------------------------------------------------- defShrt(SHRT_LE_INS_NOTES, Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "Insert Note"), LEDIT_SHRT, "le_ins_note"); defShrt(SHRT_LE_INS_SYSEX, Qt::Key_S, QT_TRANSLATE_NOOP("shortcuts", "Insert SysEx"), LEDIT_SHRT, "le_ins_sysex"); defShrt(SHRT_LE_INS_CTRL, Qt::Key_C, QT_TRANSLATE_NOOP("shortcuts", "Insert Ctrl"), LEDIT_SHRT, "le_ins_ctrl"); defShrt(SHRT_LE_INS_META, Qt::Key_M, QT_TRANSLATE_NOOP("shortcuts", "Insert Meta"), LEDIT_SHRT, "le_ins_meta"); // defShrt(SHRT_LE_INS_CHAN_AFTERTOUCH, Qt::CTRL + Qt::Key_A, QT_TRANSLATE_NOOP("shortcuts", "Insert Channel Aftertouch"), LEDIT_SHRT, "le_ins_afttouch"); // defShrt(SHRT_LE_INS_POLY_AFTERTOUCH, Qt::CTRL + Qt::Key_P, QT_TRANSLATE_NOOP("shortcuts", "Insert Key Aftertouch"), LEDIT_SHRT, "le_ins_poly"); //----------------------------------------------------------- // List masteredit: //----------------------------------------------------------- defShrt(SHRT_LM_INS_TEMPO, Qt::Key_T, QT_TRANSLATE_NOOP("shortcuts", "Insert Tempo"), LMEDIT_SHRT, "lm_ins_tempo"); defShrt(SHRT_LM_INS_SIG , Qt::Key_S, QT_TRANSLATE_NOOP("shortcuts", "Insert Signature"), LMEDIT_SHRT, "lm_ins_sig"); defShrt(SHRT_LM_INS_KEY, Qt::Key_K, QT_TRANSLATE_NOOP("shortcuts", "Insert Key"), LMEDIT_SHRT, "lm_ins_key"); defShrt(SHRT_LM_EDIT_BEAT, Qt::Key_P, QT_TRANSLATE_NOOP("shortcuts", "Change Event Position"), LMEDIT_SHRT, "lm_edit_beat"); defShrt(SHRT_LM_EDIT_VALUE, Qt::Key_V, QT_TRANSLATE_NOOP("shortcuts", "Edit Event Value"), LMEDIT_SHRT, "lm_edit_val"); // Since the marker window is always created at start, these are actually global // because they are handled there, and affect the whole app. defShrt(SHRT_NEXT_MARKER, Qt::Key_F6, QT_TRANSLATE_NOOP("shortcuts", "Goto Next Marker"), GLOBAL_SHRT, "me_sel_next"); defShrt(SHRT_PREV_MARKER, Qt::Key_F5, QT_TRANSLATE_NOOP("shortcuts", "Goto Prev Marker"), GLOBAL_SHRT, "me_sel_prev"); defShrt(SHRT_PART_NORMALIZE, Qt::ALT + Qt::Key_N, QT_TRANSLATE_NOOP("shortcuts", "Normalize"), ARRANG_SHRT, "wave_part_normalize"); //----------------------------------------------------------- // Mixer: //----------------------------------------------------------- defShrt(SHRT_MIXER_SELECT_STRIP_LEFT, Qt::CTRL + Qt::Key_Home, QT_TRANSLATE_NOOP("shortcuts", "Select strip to the left"), MIXER_SHRT, "mixer_select_left"); defShrt(SHRT_MIXER_SELECT_STRIP_RIGHT, Qt::CTRL + Qt::Key_End, QT_TRANSLATE_NOOP("shortcuts", "Select strip to the right"), MIXER_SHRT, "mixer_select_right"); // TODO Feature marked as todo in AudioMixerApp::selectNextStrip(). //defShrt(SHRT_MIXER_MULTI_SELECT_STRIP_LEFT, Qt::CTRL + Qt::SHIFT + Qt::Key_Home, // QT_TRANSLATE_NOOP("shortcuts", "Select multiple strips to the left"), MIXER_SHRT, "mixer_multi_select_left"); //defShrt(SHRT_MIXER_MULTI_SELECT_STRIP_RIGHT, Qt::CTRL + Qt::SHIFT + Qt::Key_End, // QT_TRANSLATE_NOOP("shortcuts", "Select multiple strips to the right"), MIXER_SHRT, "mixer_multi_select_right"); defShrt(SHRT_MIXER_STRIP_VOL_DOWN, Qt::ALT + Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Mixer strip volume down"), ARRANG_SHRT + MIXER_SHRT, "mixer_strip_vol_dn"); defShrt(SHRT_MIXER_STRIP_VOL_UP, Qt::ALT + Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Mixer strip volume up"), ARRANG_SHRT + MIXER_SHRT, "mixer_strip_vol_up"); defShrt(SHRT_MIXER_STRIP_PAN_LEFT, Qt::ALT + Qt::Key_Left, QT_TRANSLATE_NOOP("shortcuts", "Mixer strip pan left"), ARRANG_SHRT + MIXER_SHRT, "mixer_strip_pan_left"); defShrt(SHRT_MIXER_STRIP_PAN_RIGHT, Qt::ALT + Qt::Key_Right, QT_TRANSLATE_NOOP("shortcuts", "Mixer strip pan right"), ARRANG_SHRT + MIXER_SHRT, "mixer_strip_pan_right"); defShrt(SHRT_MIXER_STRIP_VOL_DOWN_PAGE, Qt::ALT + Qt::SHIFT + Qt::Key_Down, QT_TRANSLATE_NOOP("shortcuts", "Mixer strip volume down page"), ARRANG_SHRT + MIXER_SHRT, "mixer_strip_vol_dn_pg"); defShrt(SHRT_MIXER_STRIP_VOL_UP_PAGE, Qt::ALT + Qt::SHIFT + Qt::Key_Up, QT_TRANSLATE_NOOP("shortcuts", "Mixer strip volume up page"), ARRANG_SHRT + MIXER_SHRT, "mixer_strip_vol_up_pg"); defShrt(SHRT_MIXER_STRIP_PAN_LEFT_PAGE, Qt::ALT + Qt::SHIFT + Qt::Key_Left, QT_TRANSLATE_NOOP("shortcuts", "Mixer strip pan left page"), ARRANG_SHRT + MIXER_SHRT, "mixer_strip_pan_left_pg"); defShrt(SHRT_MIXER_STRIP_PAN_RIGHT_PAGE, Qt::ALT + Qt::SHIFT + Qt::Key_Right, QT_TRANSLATE_NOOP("shortcuts", "Mixer strip pan right page"), ARRANG_SHRT + MIXER_SHRT, "mixer_strip_pan_right_pg"); } const shortcut_cg shortcut_category[SHRT_NUM_OF_CATEGORIES] = { { GLOBAL_SHRT, "Global" }, { ARRANG_SHRT, "Arranger" }, { PROLL_SHRT, "Pianoroll" }, { DEDIT_SHRT, "Drumeditor" }, { LEDIT_SHRT, "List editor" }, { LMEDIT_SHRT, "List Mastertrack" }, { SCORE_SHRT, "Score editor" }, { WAVE_SHRT, "Wave editor" }, { MIXER_SHRT, "Mixer" }, { ALL_SHRT , "All categories" } }; int getShrtByTag(const char* xml) { for (int i=0; i, (C) 2003 // // Copyright: Mathias Lundgren (lunar_shuttle@users.sourceforge.net) (C) 2003 // // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // // C++ Interface: shortcuts // // Description: // Datastructures and declaration of shortcuts used in the application // #ifndef __SHORTCUTS_H__ #define __SHORTCUTS_H__ #include #include "xml.h" // // Shortcut categories // #define PROLL_SHRT 1 // Pianoroll shortcut #define DEDIT_SHRT 2 // Drumedit shortcut #define LEDIT_SHRT 4 // Listedit shortcut #define SCORE_SHRT 8 // Score shortcut #define ARRANG_SHRT 16 // Arranger shortcut #define TRANSP_SHRT 32 // Transport shortcut #define WAVE_SHRT 64 // Waveedit shortcut #define GLOBAL_SHRT 128 // Global shortcuts #define LMEDIT_SHRT 256 // List masteredit #define MEDIT_SHRT 512 // Master editor #define MIXER_SHRT 1024 // Mixer #define ALL_SHRT 2047 // All shortcuts #define INVIS_SHRT 2048 // Shortcuts not shown in the config-dialog. Hard-coded. To avoid conflicts #define SHRT_NUM_OF_CATEGORIES 10 //Number of shortcut categories namespace MusEGui { struct shortcut { int key; const char* descr; const char* xml; //xml-tag for config-file int type; shortcut() : key(0), descr(nullptr), xml(nullptr), type(0) { } }; struct shortcut_cg { int id_flag; const char* name; }; QString shrtToStr(long int shrt); typedef struct shortcut ShortCut ; enum { //Transport/Positioning SHRT_PLAY_SONG, //Enter SHRT_PLAY_TOGGLE, //Space SHRT_REC_RESTART, //Ctrl+Space SHRT_REC_RESTART_MULTI, //Shift+Space SHRT_STOP, //Insert SHRT_GOTO_START, // W SHRT_GOTO_END, // Ctrl+W SHRT_GOTO_LEFT, //End-keypad SHRT_GOTO_RIGHT, //Cursordown-keypad SHRT_POS_INC, // Plus SHRT_POS_DEC, // Minus SHRT_TOGGLE_LOOP, // Slash SHRT_TOGGLE_METRO, // C SHRT_START_REC, // *(keypad) SHRT_REC_ARM_TRACK, // Shift+A SHRT_REC_CLEAR, // backspace SHRT_FULLSCREEN, SHRT_TABBED_WIN, SHRT_TOGGLE_DOCKS, SHRT_TOGGLE_REWINDONSTOP, SHRT_ARRANGER, //Main + Drumeditor SHRT_NEW, //Ctrl+N SHRT_NEW_FROM_TEMPLATE, //Ctrl+Shift+N SHRT_OPEN, //Ctrl+O SHRT_SAVE, //Ctrl+S //Used throughout the app: SHRT_UNDO, //Ctrl+Z SHRT_REDO, //Ctrl+Y SHRT_COPY, //Ctrl+C SHRT_COPY_RANGE, //Ctrl+Shift+C SHRT_CUT, //Ctrl+X SHRT_PASTE, //Ctrl+V SHRT_PASTE_TO_CUR_PART, //CTRL+Key_B SHRT_PASTE_CLONE, //CTRL+Shift+V SHRT_PASTE_TO_TRACK, //CTRL+Key_B SHRT_PASTE_CLONE_TO_TRACK, //CTRL+SHIFT+Key_B SHRT_PASTE_DIALOG, //Ctrl+G SHRT_DELETE,//Delete //Main: SHRT_SAVE_AS, //Default: undefined SHRT_SAVE_AS_NEW_PROJECT, SHRT_SAVE_REVISION, SHRT_SAVE_AS_TEMPLATE, SHRT_OPEN_RECENT, //Ctrl+1 SHRT_LOAD_TEMPLATE, //Default: undefined SHRT_CONFIG_PRINTER, //Ctrl+P SHRT_IMPORT_MIDI, //Default: undefined SHRT_EXPORT_MIDI, //Default: undefined SHRT_IMPORT_PART, //!< Import midi part to current track & location, Default: undefined SHRT_IMPORT_AUDIO, //Default: undefined SHRT_QUIT, //Default: Ctrl+Q SHRT_DESEL_PARTS, //Ctrl+B SHRT_RENAME_PART, // Default: undefined SHRT_SELECT_PRTSTRACK, //Default: undefined SHRT_OPEN_PIANO, //Ctrl+E SHRT_OPEN_SCORE, //Ctrl+R SHRT_OPEN_DRUMS, //Ctrl+D SHRT_OPEN_LIST, //Ctrl+L SHRT_OPEN_WAVE, //Ctrl+W SHRT_OPEN_GRAPHIC_MASTER, //Ctrl+M SHRT_OPEN_LIST_MASTER, //Ctrl+Shift+M SHRT_OPEN_MIDI_TRANSFORM, //Ctrl+T SHRT_OPEN_PLUGIN_GUI, SHRT_OPEN_PIANO_NEW, //Ctrl+Alt+E SHRT_OPEN_DRUMS_NEW, //Ctrl+Alt+D SHRT_OPEN_LIST_NEW, //Ctrl+Alt+L SHRT_OPEN_WAVE_NEW, //Ctrl+Alt+W SHRT_GLOBAL_CUT, //Default: undefined SHRT_GLOBAL_INSERT, //Default: undefined SHRT_GLOBAL_SPLIT, //Default: undefined SHRT_CUT_EVENTS, //Default: undefined SHRT_OPEN_TRANSPORT, //F11 SHRT_OPEN_BIGTIME, //F12 SHRT_OPEN_MIXER, //Ctrl+* SHRT_OPEN_MIXER2, //Ctrl+* SHRT_OPEN_MARKER, // F9 SHRT_HIDE_MIXER_STRIP, // F3 SHRT_OPEN_CLIPS, //Default: undefined SHRT_FOLLOW_JUMP, //Default: undefined SHRT_FOLLOW_NO, //Default: undefined SHRT_FOLLOW_CONTINUOUS, //Default: undefined SHRT_GLOBAL_CONFIG, //Default: undefined SHRT_CONFIG_SHORTCUTS, //Default: Ctrl+K SHRT_CONFIG_METRONOME, //Default: undefined SHRT_CONFIG_MIDISYNC, //Default: undefined SHRT_MIDI_FILE_CONFIG, //Default: undefined SHRT_APPEARANCE_SETTINGS, //Default: undefined SHRT_CONFIG_MIDI_PORTS, //Default: undefined SHRT_CONFIG_AUDIO_PORTS, //Default: undefined //SHRT_SAVE_GLOBAL_CONFIG, //Default: undefined SHRT_MIDI_EDIT_INSTRUMENTS, //Default: undefined SHRT_MIDI_INPUT_TRANSFORM, //Default: undefined SHRT_MIDI_INPUT_FILTER, //Default: undefined SHRT_MIDI_INPUT_TRANSPOSE, //Default: undefined SHRT_MIDI_REMOTE_CONTROL, //Default: undefined SHRT_RANDOM_RHYTHM_GENERATOR, //Default: undefined SHRT_MIDI_RESET, //Default: undefined SHRT_MIDI_INIT, //Default: undefined SHRT_MIDI_LOCAL_OFF, //Default: undefined SHRT_AUDIO_BOUNCE_TO_TRACK, //Default: undefined SHRT_AUDIO_BOUNCE_TO_FILE, //Default: undefined SHRT_AUDIO_RESTART, //Default: undefined // REMOVE Tim. automation. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. // Must keep this enum for proper ordering. SHRT_MIXER_AUTOMATION, //Default: undefined SHRT_MIXER_SNAPSHOT, //Default: undefined SHRT_MIXER_AUTOMATION_CLEAR, //Default: undefined SHRT_ADD_MIDI_TRACK, //Default: Ctrl+J SHRT_ADD_DRUM_TRACK, //Default: Ctrl+k // SHRT_ADD_NEW_STYLE_DRUM_TRACK, //Default: undefined SHRT_ADD_SYNTH_TRACK, SHRT_ADD_WAVE_TRACK, //Default: undefined SHRT_ADD_AUDIO_OUTPUT, //Default: undefined SHRT_ADD_AUDIO_GROUP, //Default: undefined SHRT_ADD_AUDIO_INPUT, //Default: undefined SHRT_ADD_AUDIO_AUX, //Default: undefined SHRT_INSERT_MIDI_TRACK, //Default: undefined SHRT_INSERT_DRUM_TRACK, //Default: undefined // SHRT_INSERT_NEW_STYLE_DRUM_TRACK, //Default: undefined SHRT_INSERT_SYNTH_TRACK, SHRT_INSERT_WAVE_TRACK, //Default: undefined SHRT_INSERT_AUDIO_OUTPUT, //Default: undefined SHRT_INSERT_AUDIO_GROUP, //Default: undefined SHRT_INSERT_AUDIO_INPUT, //Default: undefined SHRT_INSERT_AUDIO_AUX, //Default: undefined SHRT_RESET_MIDI, //Ctrl+Alt+Z SHRT_OPEN_HELP, //F1 SHRT_START_WHATSTHIS, //Shift-F1 //Arranger, parts: SHRT_EDIT_PART, //Enter SHRT_SEL_ABOVE, //Up SHRT_SEL_ABOVE_ADD, //move up and add to selection SHRT_SEL_BELOW, //Down SHRT_SEL_BELOW_ADD, //move down and add to selection SHRT_INSERTMEAS, //Ctrl+Shift+M - insert measures SHRT_DUPLICATE_TRACK, // Ctrl+Shift+D SHRT_MOVEUP_TRACK, SHRT_MOVEDOWN_TRACK, SHRT_MOVETOP_TRACK, SHRT_MOVEBOTTOM_TRACK, //Arranger tracks SHRT_SEL_TRACK_BELOW, SHRT_SEL_TRACK_ABOVE, SHRT_EDIT_TRACK_NAME, SHRT_MUTE_CURRENT_TRACKS, SHRT_SOLO_CURRENT_TRACKS, //To be in arranger, pianoroll & drumeditor. p4.0.10 now globally handled, too. SHRT_SELECT_ALL, //Ctrl+A SHRT_SELECT_NONE, //Ctrl+Shift+A SHRT_SELECT_INVERT, //Ctrl+I SHRT_SELECT_ILOOP, //Default: Undefined SHRT_SELECT_OLOOP, //Default: Undefined SHRT_SELECT_PREV_PART, // Ctrl+- SHRT_SELECT_NEXT_PART, // Ctrl++ SHRT_SEL_LEFT, //left SHRT_SEL_LEFT_ADD, //move left and add to selection SHRT_SEL_RIGHT, //Right SHRT_SEL_RIGHT_ADD, //move right and add to selection SHRT_INC_PITCH, SHRT_DEC_PITCH, SHRT_INC_POS, SHRT_DEC_POS, SHRT_STEP_RECORD, SHRT_MIDI_INPUT, SHRT_PLAY_EVENTS, SHRT_INC_VELOCITY, SHRT_DEC_VELOCITY, SHRT_POS_INC_NOSNAP, SHRT_POS_DEC_NOSNAP, /* SHRT_POS_INC_BAR, SHRT_POS_DEC_BAR, SHRT_POS_INC_BAR_NOSNAP, SHRT_POS_DEC_BAR_NOSNAP, SHRT_POS_INC_BEAT, SHRT_POS_DEC_BEAT, SHRT_POS_INC_BEAT_NOSNAP, SHRT_POS_DEC_BEAT_NOSNAP, SHRT_POS_INC_TICK, SHRT_POS_DEC_TICK, SHRT_POS_INC_TICK_NOSNAP, SHRT_POS_DEC_TICK_NOSNAP, SHRT_POS_INC_MINUTE, SHRT_POS_DEC_MINUTE, SHRT_POS_INC_MINUTE_NOSNAP, SHRT_POS_DEC_MINUTE_NOSNAP, SHRT_POS_INC_SECOND, SHRT_POS_DEC_SECOND, SHRT_POS_INC_SECOND_NOSNAP, SHRT_POS_DEC_SECOND_NOSNAP, SHRT_POS_INC_FRAME, SHRT_POS_DEC_FRAME, */ SHRT_LOCATORS_TO_SELECTION, //Alt+P, currently in arranger & pianoroll SHRT_INSERT_AT_LOCATION, //Shift+CrsrRight SHRT_INCREASE_LEN, SHRT_DECREASE_LEN, //SHRT_TOOL_1,//Shift+1 Pointer //SHRT_TOOL_2,//Shift+2 Pen //SHRT_TOOL_3,//Shift+3 Rubber //SHRT_TOOL_4,//Shift+4 //SHRT_TOOL_5,//Shift+5 //SHRT_TOOL_6,//Shift+6 SHRT_TRANSPOSE, //Default: undefined //Shortcuts to be in pianoroll & drumeditor SHRT_ZOOM_IN, // PgUp SHRT_ZOOM_OUT, // PgDown SHRT_GOTO_CPOS, // c SHRT_SCROLL_LEFT, // h SHRT_SCROLL_RIGHT, // l SHRT_FIXED_LEN, //Alt+L, currently only drumeditor SHRT_QUANTIZE, //q SHRT_MODIFY_GATE_TIME, //Default: undefined SHRT_MODIFY_VELOCITY, SHRT_CRESCENDO, SHRT_DELETE_OVERLAPS, SHRT_THIN_OUT, SHRT_ERASE_EVENT, SHRT_NOTE_SHIFT, SHRT_MOVE_CLOCK, SHRT_COPY_MEASURE, SHRT_ERASE_MEASURE, SHRT_DELETE_MEASURE, SHRT_CREATE_MEASURE, SHRT_SET_QUANT_BAR, SHRT_SET_QUANT_OFF, SHRT_SET_QUANT_1, SHRT_SET_QUANT_2, SHRT_SET_QUANT_3, SHRT_SET_QUANT_4, SHRT_SET_QUANT_5, SHRT_SET_QUANT_6, SHRT_SET_QUANT_7, SHRT_TOGGLE_TRIOL, SHRT_TOGGLE_PUNCT, SHRT_TOGGLE_PUNCT2, SHRT_EVENT_COLOR, SHRT_MOVE_PLAY_TO_NOTE, SHRT_USE_LAST_EDITED_EVENT, // Shortcuts for tools // global SHRT_TOOL_POINTER, // SHRT_TOOL_PENCIL, SHRT_TOOL_RUBBER, SHRT_TOOL_PAN, SHRT_TOOL_ZOOM, // pianoroll and drum editor SHRT_TOOL_LINEDRAW, // drum editor SHRT_TOOL_CURSOR, SHRT_ADDNOTE_1, SHRT_ADDNOTE_2, SHRT_ADDNOTE_3, SHRT_ADDNOTE_4, SHRT_CURSOR_STEP_UP, SHRT_CURSOR_STEP_DOWN, SHRT_INSTRUMENT_STEP_DOWN, SHRT_INSTRUMENT_STEP_UP, // wave editor SHRT_TOOL_RANGE, // arranger SHRT_TOOL_SCISSORS, SHRT_TOOL_GLUE, SHRT_TOOL_MUTE, //Listeditor: SHRT_LE_INS_NOTES, // N SHRT_LE_INS_SYSEX, // S SHRT_LE_INS_CTRL, // C SHRT_LE_INS_META, // M // unused // SHRT_LE_INS_CHAN_AFTERTOUCH,//Ctrl+A // SHRT_LE_INS_POLY_AFTERTOUCH,//Ctrl+P //List master editor: SHRT_LM_INS_TEMPO, // Ctrl+T SHRT_LM_INS_SIG, // Ctrl+R SHRT_LM_EDIT_BEAT, // Ctrl+Shift+E SHRT_LM_EDIT_VALUE,// Ctrl+E SHRT_LM_INS_KEY, // Ctrl+K // Marker view SHRT_NEXT_MARKER, SHRT_PREV_MARKER, // Arranger SHRT_PART_NORMALIZE, // Alt+N SHRT_TOOL_STRETCH, SHRT_TOOL_SAMPLERATE, // Mixer SHRT_MIXER_SELECT_STRIP_LEFT, SHRT_MIXER_SELECT_STRIP_RIGHT, // TODO Feature marked as todo in AudioMixerApp::selectNextStrip(). //SHRT_MIXER_MULTI_SELECT_STRIP_LEFT, //SHRT_MIXER_MULTI_SELECT_STRIP_RIGHT, SHRT_MIXER_STRIP_VOL_DOWN, SHRT_MIXER_STRIP_VOL_UP, SHRT_MIXER_STRIP_PAN_LEFT, SHRT_MIXER_STRIP_PAN_RIGHT, SHRT_MIXER_STRIP_VOL_DOWN_PAGE, SHRT_MIXER_STRIP_VOL_UP_PAGE, SHRT_MIXER_STRIP_PAN_LEFT_PAGE, SHRT_MIXER_STRIP_PAN_RIGHT_PAGE, //Last item: SHRT_NUM_OF_ELEMENTS // must be last }; extern ShortCut shortcuts[SHRT_NUM_OF_ELEMENTS]; //size of last entry extern void initShortCuts(); extern void writeShortCuts(int level, MusECore::Xml& xml); extern void readShortCuts (MusECore::Xml& xml); extern const shortcut_cg shortcut_category[SHRT_NUM_OF_CATEGORIES]; } // namespace MusEGui #endif muse-4.0.0/src/muse/sig.cpp000066400000000000000000000425741404105311000155130ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sig.cpp,v 1.5.2.2 2009/03/09 02:05:17 terminator356 Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "sig.h" #include "gconfig.h" namespace MusEGlobal { MusECore::SigList sigmap; } namespace MusECore { //--------------------------------------------------------- //--------------------------------------------------------- // isValid //--------------------------------------------------------- bool TimeSignature::isValid() const { if((z < 1) || (z > 63)) return false; switch(n) { case 1: case 2: case 3: case 4: case 8: case 16: case 32: case 64: case 128: return true; default: return false; } } //--------------------------------------------------------- // SigList //--------------------------------------------------------- SigList::SigList() { insert(std::pair (MAX_TICK, new SigEvent(TimeSignature(4, 4), 0))); } SigList::~SigList() { for (iSigEvent i = begin(); i != end(); ++i) delete i->second; } //--------------------------------------------------------- // copy //--------------------------------------------------------- void SigList::copy(const SigList& src) { // Clear and delete the existing destination list. for (iSigEvent i = begin(); i != end(); ++i) delete i->second; SIGLIST::clear(); for (ciSigEvent i = src.cbegin(); i != src.cend(); ++i) { SigEvent* new_e = new SigEvent(*i->second); std::pair res = insert(std::pair (i->first, new_e)); if(!res.second) { fprintf(stderr, "SigList::copy insert failed: siglist:%p sig:%p %d/%d tick:%d\n", this, new_e, new_e->sig.z, new_e->sig.n, new_e->tick); } } } //--------------------------------------------------------- // add // signatures are only allowed at the beginning of // a bar //--------------------------------------------------------- void SigList::add(unsigned tick, const TimeSignature& s, bool do_normalize) { if (s.z == 0 || s.n == 0) { printf("illegal signature %d/%d\n", s.z, s.n); return; } tick = raster1(tick, 0); iSigEvent e = upper_bound(tick); if(e == end()) { printf("SigList::add Signal not found tick:%d\n", tick); return; } if (tick == e->second->tick) { e->second->sig = s; } else { SigEvent* ne = e->second; SigEvent* ev = new SigEvent(ne->sig, ne->tick); ne->sig = s; ne->tick = tick; insert(std::pair (tick, ev)); } if(do_normalize) normalize(); } void SigList::add(unsigned tick, SigEvent* e, bool do_normalize) { TimeSignature ts = e->sig; std::pair res = insert(std::pair (tick, e)); if(!res.second) { fprintf(stderr, "SigList::add insert failed: siglist:%p sig:%p %d/%d tick:%d\n", this, e, ts.z, ts.n, e->tick); } else { iSigEvent ise = res.first; ++ise; // There is always a 'next' sig event - there is always one at index MAX_TICK. SigEvent* ne = ise->second; // Swap the values. (This is how the sig list works.) e->sig = ne->sig; e->tick = ne->tick; ne->sig = ts; ne->tick = tick; if(do_normalize) normalize(); } } //--------------------------------------------------------- // del //--------------------------------------------------------- void SigList::del(unsigned tick, bool do_normalize) { // printf("SigList::del(%d)\n", tick); iSigEvent e = find(tick); if (e == end()) { printf("SigList::del(%d): not found\n", tick); return; } iSigEvent ne = e; ++ne; if (ne == end()) { printf("SigList::del() next event not found!\n"); return; } ne->second->sig = e->second->sig; ne->second->tick = e->second->tick; erase(e); if(do_normalize) normalize(); } void SigList::del(iSigEvent e, bool do_normalize) { iSigEvent ne = e; ++ne; if (ne == end()) { printf("SigList::del() HALLO\n"); return; } ne->second->sig = e->second->sig; ne->second->tick = e->second->tick; erase(e); if(do_normalize) normalize(); } //--------------------------------------------------------- // SigList::normalize //--------------------------------------------------------- void SigList::normalize() { TimeSignature sig(0, 0); unsigned tick = 0; iSigEvent ee; for (iSigEvent e = begin(); e != end();) { if (sig.z == e->second->sig.z && sig.n == e->second->sig.n) { e->second->tick = tick; erase(ee); } sig = e->second->sig; ee = e; tick = e->second->tick; ++e; } int bar = 0; for (iSigEvent e = begin(); e != end();) { e->second->bar = bar; int delta = e->first - e->second->tick; int ticksB = ticks_beat(e->second->sig.n); int ticksM = ticksB * e->second->sig.z; bar += delta / ticksM; if (delta % ticksM) // Teil eines Taktes ++bar; ++e; } } //--------------------------------------------------------- // SigList::dump //--------------------------------------------------------- void SigList::dump() const { printf("\nSigList:\n"); for (ciSigEvent i = begin(); i != end(); ++i) { printf("%6d %06d Bar %3d %02d/%d\n", i->first, i->second->tick, i->second->bar, i->second->sig.z, i->second->sig.n); } } void SigList::clear() { for (iSigEvent i = begin(); i != end(); ++i) delete i->second; SIGLIST::clear(); insert(std::pair (MAX_TICK, new SigEvent(TimeSignature(4, 4), 0))); } //--------------------------------------------------------- // ticksMeasure //--------------------------------------------------------- int SigList::ticksMeasure(const TimeSignature& sig) const { return ticks_beat(sig.n) * sig.z; } int SigList::ticksMeasure(int Z, int N) const { return ticks_beat(N) * Z; } int SigList::ticksMeasure(unsigned tick) const { ciSigEvent i = upper_bound(tick); if (i == end()) { printf("ticksMeasure: not found %d\n", tick); return 0; } return ticksMeasure(i->second->sig); } //--------------------------------------------------------- // ticksBeat //--------------------------------------------------------- int SigList::ticksBeat(unsigned tick) const { ciSigEvent i = upper_bound(tick); if(i == end()) { printf("SigList::ticksBeat event not found! tick:%d\n", tick); return 0; } return ticks_beat(i->second->sig.n); } int SigList::ticks_beat(int n) const { int m = MusEGlobal::config.division; switch (n) { case 1: m <<= 2; break; // 1536 case 2: m <<= 1; break; // 768 case 3: m += m >> 1; break; // 384+192 case 4: break; // 384 case 8: m >>= 1; break; // 192 case 16: m >>= 2; break; // 96 case 32: m >>= 3; break; // 48 case 64: m >>= 4; break; // 24 case 128: m >>= 5; break; // 12 default: break; } return m; } //--------------------------------------------------------- // timesig //--------------------------------------------------------- TimeSignature SigList::timesig(unsigned tick) const { ciSigEvent i = upper_bound(tick); if (i == end()) { printf("timesig(%d): not found\n", tick); return TimeSignature(4,4); } return i->second->sig; } void SigList::timesig(unsigned tick, int& z, int& n) const { ciSigEvent i = upper_bound(tick); if (i == end()) { printf("timesig(%d): not found\n", tick); z = 4; n = 4; } else { z = i->second->sig.z; n = i->second->sig.n; } } //--------------------------------------------------------- // tickValues //--------------------------------------------------------- void SigList::tickValues(unsigned t, int* bar, int* beat, unsigned* tick) const { ciSigEvent e = upper_bound(t); if (e == end()) { fprintf(stderr, "tickValues(0x%x) not found(%zd)\n", t, size()); *bar = 0; *beat = 0; *tick = 0; return; } int delta = t - e->second->tick; int ticksB = ticks_beat(e->second->sig.n); int ticksM = ticksB * e->second->sig.z; *bar = e->second->bar + delta / ticksM; int rest = delta % ticksM; *beat = rest / ticksB; *tick = rest % ticksB; } //--------------------------------------------------------- // bar2tick //--------------------------------------------------------- unsigned SigList::bar2tick(int bar, int beat, unsigned tick) const { ciSigEvent e; if (bar < 0) bar = 0; for (e = begin(); e != end();) { ciSigEvent ee = e; ++ee; if (ee == end()) break; if (bar < ee->second->bar) break; e = ee; } int ticksB = ticks_beat(e->second->sig.n); int ticksM = ticksB * e->second->sig.z; return e->second->tick + (bar-e->second->bar)*ticksM + ticksB*beat + tick; } //--------------------------------------------------------- // raster //--------------------------------------------------------- unsigned SigList::raster(unsigned t, int raster) const { if (raster == 1) return t; ciSigEvent e = upper_bound(t); if (e == end()) { printf("SigList::raster(%x,)\n", t); return t; } int delta = t - e->second->tick; int ticksM = ticks_beat(e->second->sig.n) * e->second->sig.z; // If the raster is on 'bar' or is greater than a full bar, we limit the raster to a full bar. if (raster == 0 || raster > ticksM) raster = ticksM; int rest = delta % ticksM; int bb = (delta/ticksM)*ticksM; int rr = ((rest + raster/2)/raster)*raster; // If the remaining length is less than a raster (ex. 4/4 + 4. snap) be sure to use that shorter length. const int r_cur = (rest / raster) * raster; const int len_cur = ticksM - r_cur; if(len_cur < raster && rest >= (r_cur + len_cur / 2)) rr = ticksM; return e->second->tick + bb + rr; } //--------------------------------------------------------- // raster1 // round down //--------------------------------------------------------- unsigned SigList::raster1(unsigned t, int raster) const { if (raster == 1) return t; ciSigEvent e = upper_bound(t); if(e == end()) { printf("SigList::raster1 event not found tick:%d\n", t); return t; } int delta = t - e->second->tick; int ticksM = ticks_beat(e->second->sig.n) * e->second->sig.z; // If the raster is on 'bar' or is greater than a full bar, we limit the raster to a full bar. if (raster == 0 || raster > ticksM) raster = ticksM; int rest = delta % ticksM; int bb = (delta/ticksM)*ticksM; return e->second->tick + bb + (rest/raster)*raster; } //--------------------------------------------------------- // raster2 // round up //--------------------------------------------------------- unsigned SigList::raster2(unsigned t, int raster) const { if (raster == 1) return t; ciSigEvent e = upper_bound(t); if(e == end()) { printf("SigList::raster2 event not found tick:%d\n", t); return t; } int delta = t - e->second->tick; int ticksM = ticks_beat(e->second->sig.n) * e->second->sig.z; // If the raster is on 'bar' or is greater than a full bar, we limit the raster to a full bar. if (raster == 0 || raster > ticksM) raster = ticksM; int rest = delta % ticksM; int bb = (delta/ticksM)*ticksM; int rr = ((rest+raster-1)/raster)*raster; // If the result is beyond a bar (ex. last shorter division of 4/4 + 4. snap), limit to that bar. if(rr > ticksM) rr = ticksM; return e->second->tick + bb + rr; } //--------------------------------------------------------- // rasterStep //--------------------------------------------------------- int SigList::rasterStep(unsigned t, int raster) const { ciSigEvent e = upper_bound(t); if(e == end()) { printf("SigList::rasterStep event not found tick:%d\n", t); return raster; } const int ticksM = ticks_beat(e->second->sig.n) * e->second->sig.z; // If the raster is on 'bar' or is greater than a full bar, we limit the raster to a full bar. if (raster == 0 || raster > ticksM) raster = ticksM; return raster; } void SigList::write(int level, MusECore::Xml& xml) const { xml.tag(level++, "siglist"); for (ciSigEvent i = begin(); i != end(); ++i) i->second->write(level, xml, i->first); xml.tag(level, "/siglist"); } void SigList::read(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (tag == "sig") { SigEvent* t = new SigEvent(); unsigned tick = t->read(xml); iSigEvent pos = find(tick); if (pos != end()) erase(pos); insert(std::pair (tick, t)); } else xml.unknown("SigList"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "siglist") { normalize(); return; } default: break; } } } void SigEvent::write(int level, MusECore::Xml& xml, int at) const { xml.tag(level++, "sig at=\"%d\"", at); xml.intTag(level, "tick", tick); xml.intTag(level, "nom", sig.z); xml.intTag(level, "denom", sig.n); xml.tag(level, "/sig"); } int SigEvent::read(MusECore::Xml& xml) { int at = 0; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return 0; case MusECore::Xml::TagStart: if (tag == "tick") tick = xml.parseInt(); else if (tag == "nom") sig.z = xml.parseInt(); else if (tag == "denom") sig.n = xml.parseInt(); else xml.unknown("SigEvent"); break; case MusECore::Xml::Attribut: if (tag == "at") at = xml.s2().toInt(); break; case MusECore::Xml::TagEnd: if (tag == "sig") return at; default: break; } } return 0; } } // namespace MusECore muse-4.0.0/src/muse/sig.h000066400000000000000000000076021404105311000151510ustar00rootroot00000000000000// THIS FILE IS ORPHANED: nothing uses its functions //========================================================= // MusE // Linux Music Editor // $Id: sig.h,v 1.2 2004/01/11 18:55:34 wschweer Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MUSE_SIG_H__ #define __MUSE_SIG_H__ #include #include "xml.h" #ifndef MAX_TICK #define MAX_TICK (0x7fffffff/100) #endif namespace MusECore { //--------------------------------------------------------- // TimeSignature //--------------------------------------------------------- struct TimeSignature { int z, n; TimeSignature() { z = 4; n = 4; } TimeSignature(int a, int b) { z = a; n = b; } bool isValid() const; }; //--------------------------------------------------------- // Signature Event //--------------------------------------------------------- struct SigEvent { TimeSignature sig; unsigned tick; // signature valid from this position int bar; // precomputed int read(MusECore::Xml&); void write(int, MusECore::Xml&, int) const; SigEvent() { } SigEvent(const TimeSignature& s, unsigned tk) { sig = s; tick = tk; bar = 0; } }; //--------------------------------------------------------- // SigList //--------------------------------------------------------- typedef std::map > SIGLIST; typedef SIGLIST::iterator iSigEvent; typedef SIGLIST::const_iterator ciSigEvent; typedef SIGLIST::reverse_iterator riSigEvent; typedef SIGLIST::const_reverse_iterator criSigEvent; class SigList : public SIGLIST { int ticksMeasure(const TimeSignature&) const; int ticksMeasure(int z, int n) const; public: SigList(); ~SigList(); // Makes a copy of the source list including all allocated items. // This clears and deletes existing items in the destination list. void copy(const SigList& src); void clear(); void add(unsigned tick, const TimeSignature& s, bool do_normalize = true); void add(unsigned tick, SigEvent* e, bool do_normalize = true); void del(unsigned tick, bool do_normalize = true); void del(iSigEvent, bool do_normalize = true); void normalize(); void read(MusECore::Xml&); void write(int, MusECore::Xml&) const; void dump() const; int ticks_beat(int N) const; TimeSignature timesig(unsigned tick) const; void timesig(unsigned tick, int& z, int& n) const; void tickValues(unsigned t, int* bar, int* beat, unsigned* tick) const; unsigned bar2tick(int bar, int beat, unsigned tick) const; int ticksMeasure(unsigned tick) const; int ticksBeat(unsigned tick) const; unsigned raster(unsigned tick, int raster) const; unsigned raster1(unsigned tick, int raster) const; // round down unsigned raster2(unsigned tick, int raster) const; // round up int rasterStep(unsigned tick, int raster) const; }; } // namespace MusECore namespace MusEGlobal { extern MusECore::SigList sigmap; } // namespace MusEGlobal #endif muse-4.0.0/src/muse/song.cpp000066400000000000000000005055421404105311000156760ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: song.cpp,v 1.59.2.52 2009/12/15 03:39:58 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include #include #include #include #include #include "app.h" #include "driver/jackmidi.h" #include "driver/alsamidi.h" #include "song.h" #include "key.h" #include "globals.h" #include "drummap.h" #include "amixer.h" #include "midiseq.h" #include "gconfig.h" #include "sync.h" #include "midictrl.h" #include "menutitleitem.h" #include "midi_audio_control.h" #include "tracks_duplicate.h" #include "midi_consts.h" #include "keyevent.h" #ifndef _WIN32 #include #endif #include "strntcpy.h" #include "name_factory.h" #include "synthdialog.h" // Forwards from header: #include #include #include "undo.h" #include "track.h" #include "event.h" #include "xml.h" #include "track.h" #include "part.h" #include "marker/marker.h" #include "route.h" #include "audio.h" #include "midiport.h" #include "audiodev.h" // For debugging output: Uncomment the fprintf section. #define ERROR_TIMESTRETCH(dev, format, args...) fprintf(dev, format, ##args) #define ERROR_WAVE(dev, format, args...) fprintf(dev, format, ##args) #define INFO_WAVE(dev, format, args...) // fprintf(dev, format, ##args) // Undefine if and when multiple output routes are added to midi tracks. #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ namespace MusEGlobal { MusECore::Song* song = 0; } namespace MusECore { extern void clearMidiTransforms(); extern void clearMidiInputTransforms(); //--------------------------------------------------------- // Song //--------------------------------------------------------- Song::Song(const char* name) :QObject(0) { setObjectName(name); _ipcInEventBuffers = new LockFreeMPSCRingBuffer(16384); _ipcOutEventBuffers = new LockFreeMPSCRingBuffer(16384); _fCpuLoad = 0.0; _fDspLoad = 0.0; _xRunsCount = 0; noteFifoSize = 0; noteFifoWindex = 0; noteFifoRindex = 0; undoList = new UndoList(true); // "true" means "this is an undoList", redoList = new UndoList(false); // "false" means "redoList" _markerList = new MarkerList; _globalPitchShift = 0; bounceTrack = NULL; bounceOutput = NULL; showSongInfo=true; clearDrumMap(); // One-time only early init clear(false); } //--------------------------------------------------------- // Song //--------------------------------------------------------- Song::~Song() { delete undoList; delete redoList; delete _markerList; if(_ipcOutEventBuffers) delete _ipcOutEventBuffers; if(_ipcInEventBuffers) delete _ipcInEventBuffers; } //--------------------------------------------------------- // putEvent //--------------------------------------------------------- void Song::putEvent(int pv) { if (noteFifoSize < REC_NOTE_FIFO_SIZE) { recNoteFifo[noteFifoWindex] = pv; noteFifoWindex = (noteFifoWindex + 1) % REC_NOTE_FIFO_SIZE; ++noteFifoSize; } } void Song::putEventCC(char cc) { if (MusEGlobal::rcEnableCC) rcCC = cc; } // REMOVE Tim. samplerate. Added. TODO #if 0 //--------------------------------------------------------- // setProjectSampleRate //--------------------------------------------------------- void Song::setProjectSampleRate(int rate) { if(rate != MusEGlobal::projectSampleRate) // TODO: Do the permanent conversion. convertProjectSampleRate(rate); // Now set the rate. MusEGlobal::projectSampleRate; } //--------------------------------------------------------- // projectSampleRateDiffers //--------------------------------------------------------- bool Song::projectSampleRateDiffers() const { return MusEGlobal::projectSampleRate != MusEGlobal::sampleRate; } //--------------------------------------------------------- // projectSampleRateRatio //--------------------------------------------------------- double Song::projectSampleRateRatio() const { return (double)MusEGlobal::projectSampleRate / (double)MusEGlobal::sampleRate; } #endif //--------------------------------------------------------- // setTempo // public slot //--------------------------------------------------------- void Song::setTempo(int newTempo) { applyOperation(UndoOp(UndoOp::SetTempo, pos[0].tick(), newTempo)); } //--------------------------------------------------------- // setSig // called from transport window //--------------------------------------------------------- void Song::setSig(int z, int n) { // Add will replace if found. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddSig, pos[0].tick(), z, n)); } void Song::setSig(const MusECore::TimeSignature& sig) { // Add will replace if found. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddSig, pos[0].tick(), sig.z, sig.n)); } //--------------------------------------------------------- // addNewTrack // Called from GUI context // If insertAt is valid, inserts before insertAt. Else at the end after all tracks. // Besides normal track types, n includes synth menu ids from populateAddTrack() //--------------------------------------------------------- Track* Song::addNewTrack(QAction* action, Track* insertAt) { int n = action->data().toInt(); // Ignore negative numbers since this slot could be called by a menu or list etc. passing -1. if(n < 0) return nullptr; // Synth sub-menu id? if(n >= MENU_ADD_SYNTH_ID_BASE || n == MusECore::Track::AUDIO_SOFTSYNTH) { if (n == MusECore::Track::AUDIO_SOFTSYNTH) { n = MusEGui::SynthDialog().getSynthIndex(nullptr); if (n < 0 || n >= static_cast(MusEGlobal::synthis.size())) return nullptr; } else { n -= MENU_ADD_SYNTH_ID_BASE; int ntype = n / MENU_ADD_SYNTH_ID_BASE; if(ntype >= Synth::SYNTH_TYPE_END) return nullptr; // if we ever support Wine VSTs through some other means than through dssi-vst this must be adapted if (ntype == MusECore::Synth::VST_SYNTH) ntype=MusECore::Synth::DSSI_SYNTH; if (ntype == MusECore::Synth::LV2_EFFECT) ntype=MusECore::Synth::LV2_SYNTH; // the LV2_EFFECT is a specialization used in the menu only, we reassign it to regular LV2_SYNTH n %= MENU_ADD_SYNTH_ID_BASE; if(n >= (int)MusEGlobal::synthis.size()) return nullptr; if (MusEGlobal::debugMsg) fprintf(stderr, "Song::addNewTrack synth: type:%d idx:%d class:%s label:%s\n", ntype, n, MusEGlobal::synthis[n]->baseName().toLatin1().constData(), MusEGlobal::synthis[n]->name().toLatin1().constData()); } SynthI* si = createSynthI(MusEGlobal::synthis[n]->baseName(), MusEGlobal::synthis[n]->uri(), MusEGlobal::synthis[n]->name(), MusEGlobal::synthis[n]->synthType(), insertAt); // SynthI* si = createSynthI(MusEGlobal::synthis[n]->baseName(), MusEGlobal::synthis[n]->uri(), // MusEGlobal::synthis[n]->name(), (Synth::Type)ntype, insertAt); if(!si) return nullptr; if (MusEGlobal::config.unhideTracks) SynthI::setVisible(true); // Add instance last in midi device list. for (int i = 0; i < MusECore::MIDI_PORTS; ++i) { MidiPort* port = &MusEGlobal::midiPorts[i]; MidiDevice* dev = port->device(); if (dev==nullptr) { // This is a brand new instance. Set the instrument as well for convenience. MusEGlobal::audio->msgSetMidiDevice(port, si, si); // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. MusEGlobal::muse->changeConfig(true); if (SynthI::visible()) { selectAllTracks(false); si->setSelected(true); update(); } return si; } } if (SynthI::visible()) { selectAllTracks(false); si->setSelected(true); update(SC_TRACK_SELECTION); } return si; } // Normal track. else { // Ignore AUDIO_SOFTSYNTH (or anything greater, to allow for other entries in some menu), // now that we have it as the synth menu id, since addTrack doesn't like it. if((Track::TrackType)n >= Track::AUDIO_SOFTSYNTH) return nullptr; Track* t = addTrack((Track::TrackType)n, insertAt); if (t && t->isVisible()) { selectAllTracks(false); t->setSelected(true); update(SC_TRACK_SELECTION); } return t; } } //--------------------------------------------------------- // createTrack //--------------------------------------------------------- Track* Song::createTrack(Track::TrackType type, bool setDefaults) { Track* track = nullptr; switch(type) { case Track::MIDI: track = new MidiTrack(); track->setType(Track::MIDI); break; case Track::DRUM: track = new MidiTrack(); track->setType(Track::DRUM); ((MidiTrack*)track)->setOutChannel(9); break; case Track::WAVE: track = new MusECore::WaveTrack(); break; case Track::AUDIO_OUTPUT: track = new AudioOutput(); break; case Track::AUDIO_GROUP: track = new AudioGroup(); break; case Track::AUDIO_AUX: track = new AudioAux(); break; case Track::AUDIO_INPUT: track = new AudioInput(); break; case Track::AUDIO_SOFTSYNTH: fprintf(stderr, "not implemented: Song::createTrack(SOFTSYNTH)\n"); return nullptr; default: fprintf(stderr, "THIS SHOULD NEVER HAPPEN: Song::createTrack() illegal type %d. returning NULL.\n" "save your work if you can and expect soon crashes!\n", type); return nullptr; } if(setDefaults) { // Add default track <-> midiport routes. if(track->isMidiTrack()) { MidiTrack* mt = (MidiTrack*)track; int c; bool defOutFound = false; /// TODO: Remove this if and when multiple output routes supported. const int chmask = (1 << MusECore::MUSE_MIDI_CHANNELS) - 1; for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; if(!mp->device()) // Only if device is valid. continue; if(mp->device()->rwFlags() & 0x02) // Readable { c = mp->defaultInChannels(); if(c) { // All channels set or Omni? Use an Omni route: if(c == -1 || c == chmask) track->inRoutes()->push_back(Route(i)); else // Add individual channels: for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if(c & (1 << ch)) track->inRoutes()->push_back(Route(i, ch)); } } } if(mp->device()->rwFlags() & 0x01) // Writeable { if(!defOutFound) /// { c = mp->defaultOutChannels(); if(c) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(c == -1) c = 1; // Just to be safe, shouldn't happen, default to channel 0. for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if(c & (1 << ch)) { defOutFound = true; mt->setOutPort(i); if(type != Track::DRUM) // Leave drum tracks at channel 10. mt->setOutChannel(ch); //updateFlags |= SC_ROUTE; break; } } #else // All channels set or Omni? Use an Omni route: if(c == -1 || c == chmask) track->outRoutes()->push_back(Route(i)); else // Add individual channels: for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if(c & (1 << ch)) track->outRoutes()->push_back(Route(i, ch)); } #endif } } } } if (!defOutFound) { // no default port found // set it to the port with highest number for(int i = MusECore::MIDI_PORTS-1; i >= 0; --i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; if (mp->device() != NULL) { mt->setOutPort(i); break; } } } } // // add default route to master // OutputList* ol = MusEGlobal::song->outputs(); if (!ol->empty()) { AudioOutput* ao = ol->front(); switch(type) { case Track::WAVE: case Track::AUDIO_AUX: //fprintf(stderr, "Song::addTrack(): WAVE or AUDIO_AUX type:%d name:%s pushing default route to master\n", track->type(), track->name().toLatin1().constData()); track->outRoutes()->push_back(Route(ao)); break; // It should actually never get here now, but just in case. case Track::AUDIO_SOFTSYNTH: track->outRoutes()->push_back(Route(ao)); break; default: break; } } } return track; } //--------------------------------------------------------- // addTrack // called from GUI context // type is track type // If insertAt is valid, inserts before insertAt. Else at the end after all tracks. //--------------------------------------------------------- Track* Song::addTrack(Track::TrackType type, Track* insertAt) { // Try to generate a unique track name. TrackNameFactory names(type); if(names.isEmpty()) return nullptr; Track* track = createTrack(type, true); if(!track) return nullptr; switch(type) { case Track::MIDI: if (MusEGlobal::config.unhideTracks) MidiTrack::setVisible(true); break; case Track::DRUM: if (MusEGlobal::config.unhideTracks) MidiTrack::setVisible(true); break; case Track::WAVE: if (MusEGlobal::config.unhideTracks) WaveTrack::setVisible(true); break; case Track::AUDIO_OUTPUT: if (MusEGlobal::config.unhideTracks) AudioOutput::setVisible(true); break; case Track::AUDIO_GROUP: if (MusEGlobal::config.unhideTracks) AudioGroup::setVisible(true); break; case Track::AUDIO_AUX: if (MusEGlobal::config.unhideTracks) AudioAux::setVisible(true); break; case Track::AUDIO_INPUT: if (MusEGlobal::config.unhideTracks) AudioInput::setVisible(true); break; case Track::AUDIO_SOFTSYNTH: fprintf(stderr, "not implemented: Song::addTrack(SOFTSYNTH)\n"); return nullptr; default: fprintf(stderr, "THIS SHOULD NEVER HAPPEN: Song::addTrack() illegal type %d. returning NULL.\n" "save your work if you can and expect soon crashes!\n", type); return nullptr; } track->setName(names.first()); int idx = insertAt ? _tracks.index(insertAt) : -1; applyOperation(UndoOp(UndoOp::AddTrack, idx, track)); return track; } //--------------------------------------------------------- // duplicateTracks // Called from GUI context //--------------------------------------------------------- void Song::duplicateTracks(Track *t) { const TrackList& tl = _tracks; int audio_found = 0; int midi_found = 0; int new_drum_found = 0; if (t) { if (t->type() == Track::DRUM) ++new_drum_found; else if (t->type() == Track::MIDI) ++midi_found; else ++audio_found; } else { for (ciTrack it = tl.cbegin(); it != tl.cend(); ++it) if ((*it)->selected()) { Track::TrackType type = (*it)->type(); if (type == Track::DRUM) ++new_drum_found; else if (type == Track::MIDI) ++midi_found; else ++audio_found; } } if(audio_found == 0 && midi_found == 0 && new_drum_found==0) return; MusEGui::DuplicateTracksDialog* dlg = new MusEGui::DuplicateTracksDialog(audio_found, midi_found, new_drum_found); int rv = dlg->exec(); if(rv == QDialog::Rejected) { delete dlg; return; } int copies = dlg->copies(); int flags = Track::ASSIGN_PROPERTIES; if(dlg->copyStdCtrls()) flags |= Track::ASSIGN_STD_CTRLS; if(dlg->copyPlugins()) flags |= Track::ASSIGN_PLUGINS; if(dlg->copyPluginCtrls()) flags |= Track::ASSIGN_PLUGIN_CTRLS; if(dlg->allRoutes()) flags |= Track::ASSIGN_ROUTES; if(dlg->defaultRoutes()) flags |= Track::ASSIGN_DEFAULT_ROUTES; // These three are exclusive. if(dlg->duplicateParts()) flags |= Track::ASSIGN_DUPLICATE_PARTS; else if(dlg->copyParts()) flags |= Track::ASSIGN_COPY_PARTS; else if(dlg->cloneParts()) flags |= Track::ASSIGN_CLONE_PARTS; if(dlg->copyDrumlist()) flags |= Track::ASSIGN_DRUMLIST; delete dlg; int idx; int trackno = tl.size(); TrackNameFactory names; Undo operations; if (t) { if (names.genUniqueNames(t->type(), t->name(), copies)) { for (int cp = 0; cp < copies; ++cp) { Track* new_track = t->clone(flags); if (!new_track) break; new_track->setName(names.at(cp)); idx = trackno + cp; operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTrack, idx, new_track)); } } t->setSelected(false); } else { for(TrackList::const_reverse_iterator it = tl.crbegin(); it != tl.crend(); ++it) { Track* track = *it; if(track->selected()) { if(names.genUniqueNames(track->type(), track->name(), copies)) { for(int cp = 0; cp < copies; ++cp) { Track* new_track = track->clone(flags); if(!new_track) break; new_track->setName(names.at(cp)); idx = trackno + cp; operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTrack, idx, new_track)); } } track->setSelected(false); } --trackno; } } applyOperationGroup(operations); } bool Song::addEventOperation(const Event& event, Part* part, bool do_port_ctrls, bool do_clone_port_ctrls) { // Event ev(event); bool added = false; Part* p = part; while(1) { // This will find the event even if it has been modified. As long as the IDs AND the position are the same, it's a match. // NOTE: Multiple events with the same event base pointer or the same id number, in one event list, are FORBIDDEN. // This precludes using them for 'pattern groups' such as arpeggios or chords. Instead, create a new event type. ciEvent ie = p->events().findWithId(event); if(ie == p->events().cend()) { if(pendingOperations.add(PendingOperationItem(p, event, PendingOperationItem::AddEvent))) { added = true; // Include addition of any corresponding cached controller value. // By default, here we MUST include all clones so that in the case of multiple events // at the same position the cache reader can quickly look at each part and if one // is MUTED pick an event from a different unmuted part at that position. if(do_port_ctrls && (do_clone_port_ctrls || (!do_clone_port_ctrls && p == part))) // addPortCtrlEvents(ev, p, p->tick(), p->lenTick(), p->track(), pendingOperations); pendingOperations.addPartPortCtrlEvents(event, p, p->tick(), p->lenTick(), p->track()); } } p = p->nextClone(); if(p == part) break; // ev = event.clone(); // Makes a new copy with the same id. } return added; } Event Song::changeEventOperation(const Event& oldEvent, const Event& newEvent, Part* part, bool do_port_ctrls, bool do_clone_port_ctrls) { Event p_res, res; // If position is changed we need to reinsert into the list, and all clone lists. Part* p = part; do { // This will find the event even if it has been modified. // As long as the IDs AND the position are the same, it's a match. iEvent ie = p->nonconst_events().findWithId(oldEvent); if(ie == p->nonconst_events().end()) { // The old event was not found. Just go ahead and include the addition of the new event. // Make sure the new event doesn't already exist. if(p->events().findWithId(newEvent) == p->events().cend()) { if(pendingOperations.add(PendingOperationItem(p, newEvent, PendingOperationItem::AddEvent))) { if(do_port_ctrls && (do_clone_port_ctrls || (!do_clone_port_ctrls && p == part))) pendingOperations.addPartPortCtrlEvents(newEvent, p, p->tick(), p->lenTick(), p->track()); // Port controller values. } } } else { // Use the actual old found event, not the given oldEvent. const Event& e = ie->second; // Prefer to return the event found in the given part's event list, not a clone part's. if(p == part) p_res = e; if(res.empty()) res = e; // Go ahead and include deletion of the old event. if(pendingOperations.add(PendingOperationItem(p, ie, PendingOperationItem::DeleteEvent))) { // If the new and old event IDs are the same we bypass looking for the new event // because it hasn't been deleted yet and would always be found. // This is safe since the event is deleted then added again. // But if the new and old event IDs are not the same we MUST make sure the // new event does not already exist. if((newEvent.id() == oldEvent.id() || p->events().findWithId(newEvent) == p->events().cend()) && pendingOperations.add(PendingOperationItem(p, newEvent, PendingOperationItem::AddEvent))) { if(do_port_ctrls && (do_clone_port_ctrls || (!do_clone_port_ctrls && p == part))) pendingOperations.modifyPartPortCtrlEvents(e, newEvent, p); // Port controller values. } else { // Adding the new event failed. // Just go ahead and include removal of the old cached value. if(do_port_ctrls && (do_clone_port_ctrls || (!do_clone_port_ctrls && p == part))) pendingOperations.removePartPortCtrlEvents(e, p, p->track()); // Port controller values. } } } p = p->nextClone(); } while(p != part); // Prefer to return the event found in the given part's event list, not a clone part's. if(!p_res.empty()) return p_res; return res; } //--------------------------------------------------------- // deleteEvent //--------------------------------------------------------- Event Song::deleteEventOperation(const Event& event, Part* part, bool do_port_ctrls, bool do_clone_port_ctrls) { Event p_res, res; Part* p = part; do { // This will find the event even if it has been modified. // As long as the IDs AND the position are the same, it's a match. iEvent ie = p->nonconst_events().findWithId(event); if(ie != p->nonconst_events().end()) { const Event& e = ie->second; // Prefer to return the event found in the given part's event list, not a clone part's. if(p == part) p_res = e; if(res.empty()) res = e; // Include removal of the event. if(pendingOperations.add(PendingOperationItem(p, ie, PendingOperationItem::DeleteEvent))) { // Include removal of any corresponding cached controller value. // By using the found existing event instead of the given one, this allows // us to pre-modify an event - EXCEPT the event's time and ID - before // passing it here. We will find it by ID and delete the event. // Also these following cached controller values DEPEND on finding the // ORIGINAL event and cannot find a modified event. if(do_port_ctrls && (do_clone_port_ctrls || (!do_clone_port_ctrls && p == part))) pendingOperations.removePartPortCtrlEvents(e, p, p->track()); // Port controller values. } } p = p->nextClone(); } while(p != part); // Prefer to return the event found in the given part's event list, not a clone part's. if(!p_res.empty()) return p_res; return res; } //--------------------------------------------------------- // selectEvent //--------------------------------------------------------- void Song::selectEvent(Event& event, Part* part, bool select) { Part* p = part; do { iEvent ie = p->nonconst_events().findWithId(event); if(ie == p->nonconst_events().end()) { // This can be normal for some (redundant) operations. if(MusEGlobal::debugMsg) fprintf(stderr, "Song::selectEvent event not found in part:%s size:%zd\n", p->name().toLatin1().constData(), p->nonconst_events().size()); } else ie->second.setSelected(select); p = p->nextClone(); } while(p != part); } //--------------------------------------------------------- // selectAllEvents //--------------------------------------------------------- void Song::selectAllEvents(Part* part, bool select) { Part* p = part; do { EventList& el = p->nonconst_events(); for(iEvent ie = el.begin(); ie != el.end(); ++ie) ie->second.setSelected(select); p = p->nextClone(); } while(p != part); } //--------------------------------------------------------- // remapPortDrumCtrlEvents // Called when drum map anote, channel, or port is changed. //--------------------------------------------------------- void Song::remapPortDrumCtrlEvents(int mapidx, int newnote, int newchan, int newport) { if(mapidx == -1) return; for(ciMidiTrack it = _midis.begin(); it != _midis.end(); ++it) { MidiTrack* mt = *it; if(mt->type() != Track::DRUM) continue; MidiPort* trackmp = &MusEGlobal::midiPorts[mt->outPort()]; const PartList* pl = mt->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { MidiPart* part = (MidiPart*)(ip->second); const EventList& el = part->events(); for(ciEvent ie = el.begin(); ie != el.end(); ++ie) { const Event& ev = ie->second; if(ev.type() != Controller) continue; int cntrl = ev.dataA(); int val = ev.dataB(); // Is it a drum controller event, according to the track port's instrument? MidiController* mc = trackmp->drumController(cntrl); if(!mc) continue; int note = cntrl & 0x7f; // Does the index match? if(note == mapidx) { int tick = ev.tick() + part->tick(); if(mt->type() == Track::DRUM) { // Default to track port if -1 and track channel if -1. int ch = mt->drummap()[note].channel; if(ch == -1) ch = mt->outChannel(); int port = mt->drummap()[note].port; if(port == -1) port = mt->outPort(); MidiPort* mp = &MusEGlobal::midiPorts[port]; cntrl = (cntrl & ~0xff) | mt->drummap()[note].anote; // Remove the port controller value. mp->deleteController(ch, tick, cntrl, val, part); // FIXME FIXME CHECK THIS // // Why wasn't 'ch' given its own 'ch_add' variable? // Why wasn't 'mp' given its own 'mp_add' variable? // That means the channel and port will default to the ones // being erased above, not the track's. That can't be right ! // Checked callers: Looks OK for this routine only. // If newnote, newchan, or newport are -1 it means // "don't touch, use the original". IOW it means we only // want to change what is not -1. if(newnote != -1 && newnote != mt->drummap()[note].anote) cntrl = (cntrl & ~0xff) | newnote; if(newchan != -1 && newchan != ch) ch = newchan; if(newport != -1 && newport != port) port = newport; mp = &MusEGlobal::midiPorts[port]; // Add the port controller value. mp->setControllerVal(ch, tick, cntrl, val, part); } } } } } } //--------------------------------------------------------- // changeMidiCtrlCacheEvents //--------------------------------------------------------- void Song::changeMidiCtrlCacheEvents( bool add, bool drum_tracks, bool midi_tracks, bool drum_ctls, bool non_drum_ctls) { if(!drum_tracks && !midi_tracks) return; for(ciMidiTrack it = _midis.begin(); it != _midis.end(); ++it) { MidiTrack* mt = *it; if((mt->type() == Track::DRUM && drum_tracks) || ((mt->type() == Track::MIDI && midi_tracks))) { if(add) addPortCtrlEvents(mt, drum_ctls, non_drum_ctls); else removePortCtrlEvents(mt, drum_ctls, non_drum_ctls); } } } //--------------------------------------------------------- // cmdAddRecordedEvents // add recorded Events into part //--------------------------------------------------------- void Song::cmdAddRecordedEvents(MidiTrack* mt, const EventList& events, unsigned startTick, Undo& operations) { if (events.empty()) { if (MusEGlobal::debugMsg) fprintf(stderr, "no events recorded\n"); return; } ciEvent s; ciEvent e; unsigned endTick; if((MusEGlobal::audio->loopCount() > 0 && startTick > lPos().tick()) || (punchin() && startTick < lPos().tick())) { startTick = lpos(); s = events.lower_bound(startTick); } else { s = events.begin(); } // search for last noteOff: endTick = 0; for (ciEvent i = events.begin(); i != events.end(); ++i) { Event ev = i->second; unsigned l = ev.endTick(); if (l > endTick) endTick = l; } if((MusEGlobal::audio->loopCount() > 0) || (punchout() && endTick > rPos().tick()) ) { endTick = rpos(); e = events.lower_bound(endTick); } else e = events.end(); if (startTick > endTick) { if (MusEGlobal::debugMsg) fprintf(stderr, "no events in record area\n"); return; } //--------------------------------------------------- // if startTick points into a part, // record to that part // else // create new part //--------------------------------------------------- PartList* pl = mt->parts(); const MidiPart* part = 0; iPart ip; for (ip = pl->begin(); ip != pl->end(); ++ip) { part = (MidiPart*)(ip->second); unsigned partStart = part->tick(); unsigned partEnd = part->endTick(); if (startTick >= partStart && startTick < partEnd) break; } if (ip == pl->end()) { if (MusEGlobal::debugMsg) fprintf(stderr, "create new part for recorded events\n"); // create new part MidiPart* newpart; newpart = new MidiPart(mt); // Round the start down using the Arranger part snap raster value. startTick = MusEGlobal::sigmap.raster1(startTick, MusEGlobal::muse->arrangerRaster()); // Round the end up using the Arranger part snap raster value. endTick = MusEGlobal::sigmap.raster2(endTick, MusEGlobal::muse->arrangerRaster()); newpart->setTick(startTick); newpart->setLenTick(endTick - startTick); newpart->setName(mt->name()); newpart->setColorIndex(MusEGlobal::muse->currentPartColorIndex()); // copy events for (ciEvent i = s; i != e; ++i) { const Event& old = i->second; Event event = old.clone(); event.setTick(old.tick() - startTick); // addEvent also adds port controller values. So does msgAddPart, below. Let msgAddPart handle them. //addEvent(event, part); if(newpart->events().find(event) == newpart->events().end()) newpart->addEvent(event); } operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddPart, newpart)); return; } unsigned partTick = part->tick(); if (endTick > part->endTick()) { // Determine new part length... endTick = 0; for (ciEvent i = s; i != e; ++i) { const Event& event = i->second; unsigned tick = event.tick() - partTick + event.lenTick(); if (endTick < tick) endTick = tick; } // Round the end up (again) using the Arranger part snap raster value. endTick = MusEGlobal::sigmap.raster2(endTick, MusEGlobal::muse->arrangerRaster()); operations.push_back(UndoOp(UndoOp::ModifyPartLength, part, part->lenValue(), endTick, 0, Pos::TICKS)); } if (_recMode == REC_REPLACE) { ciEvent si = part->events().lower_bound(startTick - part->tick()); ciEvent ei = part->events().lower_bound(endTick - part->tick()); for (ciEvent i = si; i != ei; ++i) { const Event& event = i->second; // Indicate that controller values and clone parts were handled. operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, true, true)); } } for (ciEvent i = s; i != e; ++i) { Event event = i->second.clone(); event.setTick(event.tick() - partTick); // Indicate that controller values and clone parts were handled. operations.push_back(UndoOp(UndoOp::AddEvent, event, part, true, true)); } } //--------------------------------------------------------- // cmdAddRecordedWave //--------------------------------------------------------- void Song::cmdAddRecordedWave(MusECore::WaveTrack* track, MusECore::Pos s, MusECore::Pos e, Undo& operations) { if (MusEGlobal::debugMsg) { INFO_WAVE(stderr, "cmdAddRecordedWave - loopCount = %d, punchin = %d", MusEGlobal::audio->loopCount(), punchin()); } // Driver should now be in transport 'stop' mode and no longer pummping the recording wave fifo, // but the fifo may not be empty yet, it's in the prefetch thread. // Wait a few seconds for the fifo to be empty, until it has been fully transferred to the // track's recFile sndfile, which is done via Audio::process() sending periodic 'tick' messages // to the prefetch thread to write its fifo to the sndfile, always UNLESS in stop or idle mode. // It now sends one final tick message at stop, so we /should/ have all our buffers available here. // This GUI thread is notified of the stop condition via the audio thread sending a message // as soon as the state change is read from the driver. // NOTE: The fifo scheme is used only if NOT in transport freewheel mode where the data is directly // written to the sndfile and therefore stops immediately when the transport stops and thus is // safe to read here regardless of waiting. int tout = 100; // Ten seconds. Otherwise we gotta move on. while(track->recordFifoCount() != 0) { usleep(100000); --tout; if(tout == 0) { ERROR_WAVE(stderr, "Song::cmdAddRecordedWave: Error: Timeout waiting for _tempoFifo to empty! Count:%d\n", track->prefetchFifo()->getCount()); break; } } // It should now be safe to work with the resultant sndfile here in the GUI thread. // No other thread should be touching it right now. MusECore::SndFileR f = track->recFile(); if (f.isNull()) { ERROR_WAVE(stderr, "cmdAddRecordedWave: no snd file for track <%s>\n", track->name().toLocal8Bit().constData()); return; } // If externally clocking (and therefore master was forced off), // tempos may have been recorded. We really should temporarily force // the master tempo map on in order to properly determine the ticks below. // Else internal clocking, the user decided to record either with or without // master on, so let it be. // FIXME: We really should allow the master flag to be on at the same time as // the external sync flag! AFAIR when external sync is on, no part of the app shall // depend on the tempo map anyway, so it should not matter whether it's on or off. // If we do that, then we may be able to remove this section and user simply decides // whether master is on/off, because we may be able to use the flag to determine // whether to record external tempos at all, because we may want a switch for it! bool master_was_on = MusEGlobal::tempomap.masterFlag(); if(MusEGlobal::extSyncFlag && !master_was_on) MusEGlobal::tempomap.setMasterFlag(0, true); if((MusEGlobal::audio->loopCount() > 0 && s.tick() > lPos().tick()) || (punchin() && s.tick() < lPos().tick())) s.setTick(lPos().tick()); // If we are looping, just set the end to the right marker, since we don't know how many loops have occurred. // (Fixed: Added Audio::loopCount) // Otherwise if punchout is on, limit the end to the right marker. if((MusEGlobal::audio->loopCount() > 0) || (punchout() && e.tick() > rPos().tick()) ) e.setTick(rPos().tick()); // No part to be created? Delete the rec sound file. if(s.frame() >= e.frame()) { QString st = f->path(); // The function which calls this function already does this immediately after. But do it here anyway. track->setRecFile(NULL); // upon "return", f is removed from the stack, the WaveTrack::_recFile's // counter has dropped by 2 and _recFile will probably deleted then remove(st.toLocal8Bit().constData()); if(MusEGlobal::debugMsg) { INFO_WAVE(stderr, "Song::cmdAddRecordedWave: remove file %s - startframe=%d endframe=%d\n", st.toLocal8Bit().constData(), s.frame(), e.frame()); } // Restore master flag. if(MusEGlobal::extSyncFlag && !master_was_on) MusEGlobal::tempomap.setMasterFlag(0, false); return; } // REMOVE Tim. Wave. Removed. Probably I should never have done this. It's more annoying than helpful. Look at it another way: Importing a wave DOES NOT do this. // // Round the start down using the Arranger part snap raster value. // int a_rast = MusEGlobal::song->arrangerRaster(); // unsigned sframe = (a_rast == 1) ? s.frame() : Pos(MusEGlobal::sigmap.raster1(s.tick(), MusEGlobal::song->arrangerRaster())).frame(); // // Round the end up using the Arranger part snap raster value. // unsigned eframe = (a_rast == 1) ? e.frame() : Pos(MusEGlobal::sigmap.raster2(e.tick(), MusEGlobal::song->arrangerRaster())).frame(); // // unsigned etick = Pos(eframe, false).tick(); unsigned sframe = s.frame(); unsigned eframe = e.frame(); // Done using master tempo map. Restore master flag. if(MusEGlobal::extSyncFlag && !master_was_on) MusEGlobal::tempomap.setMasterFlag(0, false); f->update(); MusECore::WavePart* part = new MusECore::WavePart(track); part->setFrame(sframe); part->setLenFrame(eframe - sframe); part->setName(track->name()); part->setColorIndex(MusEGlobal::muse->currentPartColorIndex()); // create Event MusECore::Event event(MusECore::Wave); event.setSndFile(f); // We are done with the _recFile member. Set to zero. track->setRecFile(0); event.setSpos(0); // Since the part start was snapped down, we must apply the difference so that the // wave event tick lines up with when the user actually started recording. event.setFrame(s.frame() - sframe); // NO Can't use this. SF reports too long samples at first part recorded in sequence. See samples() - funny business with SEEK ? //event.setLenFrame(f.samples()); event.setLenFrame(e.frame() - s.frame()); part->addEvent(event); operations.push_back(UndoOp(UndoOp::AddPart, part)); } //--------------------------------------------------------- // cmdChangeWave // called from GUI context //--------------------------------------------------------- void Song::cmdChangeWave(const Event& original, const QString& tmpfile, unsigned sx, unsigned ex) { addUndo(UndoOp(UndoOp::ModifyClip,original,tmpfile,sx,ex)); temporaryWavFiles.push_back(tmpfile); } //--------------------------------------------------------- // findTrack //--------------------------------------------------------- Track* Song::findTrack(const Part* part) const { for (ciTrack t = _tracks.begin(); t != _tracks.end(); ++t) { Track* track = *t; if (track == 0) continue; PartList* pl = track->parts(); for (iPart p = pl->begin(); p != pl->end(); ++p) { if (part == p->second) return track; } } return 0; } //--------------------------------------------------------- // findTrack // find track by name //--------------------------------------------------------- Track* Song::findTrack(const QString& name) const { for (ciTrack i = _tracks.begin(); i != _tracks.end(); ++i) { if ((*i)->name() == name) return *i; } return 0; } //--------------------------------------------------------- // setLoop // set transport loop flag //--------------------------------------------------------- void Song::setLoop(bool f) { if (loopFlag != f) { loopFlag = f; MusEGlobal::loopAction->setChecked(loopFlag); emit loopChanged(loopFlag); } } //--------------------------------------------------------- // clearTrackRec //--------------------------------------------------------- void Song::clearTrackRec() { // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; for(iTrack it = tracks()->begin(); it != tracks()->end(); ++it) { if(!(*it)->setRecordFlag1(false)) { //continue; } operations.add(MusECore::PendingOperationItem((*it), false, MusECore::PendingOperationItem::SetTrackRecord)); } MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // setRecord //--------------------------------------------------------- void Song::setRecord(bool f, bool autoRecEnable) { if(MusEGlobal::debugMsg) fprintf(stderr, "setRecord recordflag =%d f(record state)=%d autoRecEnable=%d\n", recordFlag, f, autoRecEnable); if (f && MusEGlobal::config.useProjectSaveDialog && MusEGlobal::museProject == MusEGlobal::museProjectInitPath ) { // check that there is a project stored before commencing // no project, we need to create one. if (!MusEGlobal::muse->saveAs()) { MusEGlobal::recordAction->setChecked(false); return; // could not store project, won't enable record } } if (recordFlag != f) { if (f && autoRecEnable) { bool alreadyRecEnabled = false; TrackList selectedTracks; // loop through list and check if any track is rec enabled // if not then rec enable the selected track MusECore::WaveTrackList* wtl = waves(); for (MusECore::iWaveTrack i = wtl->begin(); i != wtl->end(); ++i) { if((*i)->recordFlag()) { alreadyRecEnabled = true; break; } if((*i)->selected()) selectedTracks.push_back(*i); } if (!alreadyRecEnabled) { MidiTrackList* mtl = midis(); for (iMidiTrack it = mtl->begin(); it != mtl->end(); ++it) { if((*it)->recordFlag()) { alreadyRecEnabled = true; break; } if((*it)->selected()) selectedTracks.push_back(*it); } } if (!alreadyRecEnabled && !selectedTracks.empty()) { // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; foreach (Track *t, selectedTracks) { if(!t->setRecordFlag1(true)) continue; operations.add(MusECore::PendingOperationItem(t, true, MusECore::PendingOperationItem::SetTrackRecord)); } MusEGlobal::audio->msgExecutePendingOperations(operations, true); } else if (alreadyRecEnabled) { // do nothing } else { // if there no tracks or no track is selected, warn the user and don't enable record if (selectedTracks.empty()) { QMessageBox::warning(nullptr, "MusE", tr("Record: At least one track must be armed for recording first.")); f = false; } // // if there are no tracks, do not enable record // if (waves()->empty() && midis()->empty()) { // fprintf(stderr, "No track to select, won't enable record\n"); // f = false; // } } // prepare recording of wave files for all record enabled wave tracks for (MusECore::iWaveTrack i = wtl->begin(); i != wtl->end(); ++i) { if((*i)->recordFlag()) // || (selectedTracks.find(*i)!=wtl->end() && autoRecEnable)) // prepare if record flag or if it is set to recenable { // setRecordFlag may take too long time to complete // so we try this case specifically (*i)->prepareRecording(); } } // DELETETHIS? 14 #if 0 // check for midi devices suitable for recording bool portFound = false; for (int i = 0; i < MIDI_PORTS; ++i) { MidiDevice* dev = MusEGlobal::midiPorts[i].device(); if (dev && (dev->rwFlags() & 0x2)) portFound = true; } if (!portFound) { QMessageBox::critical(qApp->mainWidget(), "MusE: Record", "There are no midi devices configured for recording"); f = false; } #endif } else { bounceTrack = 0; } if (MusEGlobal::audio->isPlaying() && f) f = false; recordFlag = f; MusEGlobal::recordAction->setChecked(recordFlag); emit recordChanged(recordFlag); } } //--------------------------------------------------------- // setPunchin // set punchin flag //--------------------------------------------------------- void Song::setPunchin(bool f) { if (punchinFlag != f) { punchinFlag = f; MusEGlobal::punchinAction->setChecked(punchinFlag); emit punchinChanged(punchinFlag); } } //--------------------------------------------------------- // setPunchout // set punchout flag //--------------------------------------------------------- void Song::setPunchout(bool f) { if (punchoutFlag != f) { punchoutFlag = f; MusEGlobal::punchoutAction->setChecked(punchoutFlag); emit punchoutChanged(punchoutFlag); } } //--------------------------------------------------------- // setClick //--------------------------------------------------------- void Song::setClick(bool val) { if (_click != val) { _click = val; emit clickChanged(_click); } } //--------------------------------------------------------- // setQuantize //--------------------------------------------------------- void Song::setQuantize(bool val) { if (_quantize != val) { _quantize = val; emit quantizeChanged(_quantize); } } //--------------------------------------------------------- // setMasterFlag //--------------------------------------------------------- void Song::setMasterFlag(bool val) { // Here we have a choice of whether to allow undoing of setting the master. // TODO: Add a separate config flag just for this ? //if(MusEGlobal::config.selectionsUndoable) // MusEGlobal::song->applyOperation(UndoOp(UndoOp::EnableMasterTrack, val, 0), MusECore::Song::OperationUndoMode); //else MusEGlobal::song->applyOperation(UndoOp(UndoOp::EnableMasterTrack, val, 0), MusECore::Song::OperationExecuteUpdate); } //--------------------------------------------------------- // setPlay // set transport play flag //--------------------------------------------------------- void Song::setPlay(bool f) { if (MusEGlobal::extSyncFlag) { if (MusEGlobal::debugMsg) fprintf(stderr, "not allowed while using external sync"); return; } // only allow the user to set the button "on" if (!f) MusEGlobal::playAction->setChecked(true); else { // keep old transport position for rewinding // position if "Rewind on Stop" option is enabled _startPlayPosition = MusEGlobal::audio->pos(); MusEGlobal::audio->msgPlay(true); } } void Song::setStop(bool f) { if (MusEGlobal::extSyncFlag) { if (MusEGlobal::debugMsg) fprintf(stderr, "not allowed while using external sync"); return; } // only allow the user to set the button "on" if (!f) MusEGlobal::stopAction->setChecked(true); else { MusEGlobal::audio->msgPlay(false); } } void Song::setStopPlay(bool f) { MusEGlobal::playAction->blockSignals(true); MusEGlobal::stopAction->blockSignals(true); emit playChanged(f); // signal transport window MusEGlobal::playAction->setChecked(f); MusEGlobal::stopAction->setChecked(!f); MusEGlobal::stopAction->blockSignals(false); MusEGlobal::playAction->blockSignals(false); } //--------------------------------------------------------- // seekTo // setPos slot, only active when not doing playback //--------------------------------------------------------- void Song::seekTo(int tick) { if (!MusEGlobal::audio->isPlaying()) { Pos p(tick, true); setPos(CPOS, p); } } //--------------------------------------------------------- // setPos // MusEGlobal::song->setPos(Song::CPOS, pos, true, true, true); //--------------------------------------------------------- void Song::setPos(POSTYPE posType, const Pos& val, bool sig, bool isSeek, bool adjustScrollbar, bool /*force*/) { if (MusEGlobal::heavyDebugMsg) { fprintf(stderr, "setPos %d sig=%d,seek=%d,scroll=%d ", posType, sig, isSeek, adjustScrollbar); val.dump(0); fprintf(stderr, "\n"); fprintf(stderr, "Song::setPos before MusEGlobal::audio->msgSeek posType:%d isSeek:%d frame:%d\n", posType, isSeek, val.frame()); } if (posType == CPOS) { _vcpos = val; if (isSeek && !MusEGlobal::extSyncFlag) { if (val == MusEGlobal::audio->pos()) { if (MusEGlobal::heavyDebugMsg) fprintf(stderr, "Song::setPos seek MusEGlobal::audio->pos already == val tick:%d frame:%d\n", val.tick(), val.frame()); return; } MusEGlobal::audio->msgSeek(val); if (MusEGlobal::heavyDebugMsg) fprintf(stderr, "Song::setPos after MusEGlobal::audio->msgSeek posTYpe:%d isSeek:%d frame:%d\n", posType, isSeek, val.frame()); return; } } if (val == pos[posType]) { if (MusEGlobal::heavyDebugMsg) fprintf(stderr, "Song::setPos MusEGlobal::song->pos already == val tick:%d frame:%d\n", val.tick(), val.frame()); return; } pos[posType] = val; bool swap = pos[LPOS] > pos[RPOS]; if (swap) { // swap lpos/rpos if lpos > rpos Pos tmp = pos[LPOS]; pos[LPOS] = pos[RPOS]; pos[RPOS] = tmp; } if (sig) { if (swap) { emit posChanged(LPOS, pos[LPOS].tick(), adjustScrollbar); emit posChanged(RPOS, pos[RPOS].tick(), adjustScrollbar); if (posType != LPOS && posType != RPOS) emit posChanged(posType, pos[posType].tick(), adjustScrollbar); } else emit posChanged(posType, pos[posType].tick(), adjustScrollbar); } if(posType == CPOS) { const unsigned int vframe = val.frame(); iMarker i1 = _markerList->begin(); bool currentChanged = false; for(; i1 != _markerList->end(); ++i1) { const unsigned fr = i1->second.frame(); // If there are multiple items at this frame and any one of them is current, // leave it alone. It's arbitrary which one would be selected and it would // normally choose the first one, but we'll let it stick with the one that's current, // to avoid jumping around in the marker view window. iMarker i2 = i1; while(i2 != _markerList->end() && i2->second.frame() == fr) { i1 = i2; ++i2; } if(vframe >= fr && (i2==_markerList->end() || vframe < i2->second.frame())) { if(i1->second.current()) return; i1->second.setCurrent(true); if(currentChanged) { emit markerChanged(MARKER_CUR); return; } for(; i2 != _markerList->end(); ++i2) { if(i2->second.current()) i2->second.setCurrent(false); } emit markerChanged(MARKER_CUR); return; } else { if(i1->second.current()) { currentChanged = true; i1->second.setCurrent(false); } } } if(currentChanged) emit markerChanged(MARKER_CUR); } } //--------------------------------------------------------- // forward //--------------------------------------------------------- void Song::forward() { unsigned newPos = pos[0].tick() + MusEGlobal::config.division; MusEGlobal::audio->msgSeek(Pos(newPos, true)); } //--------------------------------------------------------- // rewind //--------------------------------------------------------- void Song::rewind() { unsigned newPos; if (unsigned(MusEGlobal::config.division) > pos[0].tick()) newPos = 0; else newPos = pos[0].tick() - MusEGlobal::config.division; MusEGlobal::audio->msgSeek(Pos(newPos, true)); } //--------------------------------------------------------- // rewindStart //--------------------------------------------------------- void Song::rewindStart() { MusEGlobal::audio->msgSeek(Pos(0, true)); } //--------------------------------------------------------- // update //--------------------------------------------------------- void Song::update(MusECore::SongChangedStruct_t flags, bool allowRecursion) { static int level = 0; // DEBUG if (level && !allowRecursion) { fprintf(stderr, "THIS SHOULD NEVER HAPPEN: unallowed recursion in Song::update(%08lx %08lx), level %d!\n" " the songChanged() signal is NOT emitted. this will\n" " probably cause windows being not up-to-date.\n", flags.flagsHi(), flags.flagsLo(), level); return; } ++level; emit songChanged(flags); --level; } //--------------------------------------------------------- // updatePos //--------------------------------------------------------- void Song::updatePos() { emit posChanged(0, pos[0].tick(), false); emit posChanged(1, pos[1].tick(), false); emit posChanged(2, pos[2].tick(), false); } //--------------------------------------------------------- // len //--------------------------------------------------------- void Song::initLen() { _len = MusEGlobal::sigmap.bar2tick(40, 0, 0); // default song len for (iTrack t = _tracks.begin(); t != _tracks.end(); ++t) { Track* track = dynamic_cast(*t); if (track == 0) continue; PartList* parts = track->parts(); for (iPart p = parts->begin(); p != parts->end(); ++p) { unsigned last = p->second->tick() + p->second->lenTick(); if (last > _len) _len = last; } } _len = roundUpBar(_len); } //--------------------------------------------------------- // roundUpBar //--------------------------------------------------------- int Song::roundUpBar(int t) const { int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(t, &bar, &beat, &tick); if (beat || tick) return MusEGlobal::sigmap.bar2tick(bar+1, 0, 0); return t; } //--------------------------------------------------------- // roundUpBeat //--------------------------------------------------------- int Song::roundUpBeat(int t) const { int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(t, &bar, &beat, &tick); if (tick) return MusEGlobal::sigmap.bar2tick(bar, beat+1, 0); return t; } //--------------------------------------------------------- // roundDownBar //--------------------------------------------------------- int Song::roundDownBar(int t) const { int bar, beat; unsigned tick; MusEGlobal::sigmap.tickValues(t, &bar, &beat, &tick); return MusEGlobal::sigmap.bar2tick(bar, 0, 0); } //--------------------------------------------------------- // dumpMaster //--------------------------------------------------------- void Song::dumpMaster() { MusEGlobal::tempomap.dump(); MusEGlobal::sigmap.dump(); } void Song::normalizePart(MusECore::Part *part) { const MusECore::EventList& evs = part->events(); for(MusECore::ciEvent it = evs.begin(); it != evs.end(); ++it) { const Event& ev = (*it).second; if(ev.empty()) continue; MusECore::SndFileR file = ev.sndFile(); if(file.isNull()) continue; QString tmpWavFile; if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", tmpWavFile)) { return; } MusEGlobal::audio->msgIdle(true); // Not good with playback during operations MusECore::SndFile tmpFile(tmpWavFile); unsigned int file_channels = file.channels(); tmpFile.setFormat(file.format(), file_channels, file.samplerate()); if (tmpFile.openWrite()) { MusEGlobal::audio->msgIdle(false); fprintf(stderr, "Could not open temporary file...\n"); return; } float* tmpdata[file_channels]; unsigned tmpdatalen = file.samples(); for (unsigned i=0; i loudest) { loudest = tmpdata[i][j]; } } } double scale = 0.99 / (double)loudest; for (unsigned i=0; icmdChangeWave(ev, tmpWavFile, 0, tmpdatalen); MusEGlobal::audio->msgIdle(false); // Not good with playback during operations //sf.update(); } } void Song::normalizeWaveParts(Part *partCursor) { MusECore::TrackList* tracks=MusEGlobal::song->tracks(); bool undoStarted = false; for (MusECore::TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++) { if((*t_it)->type() != MusECore::Track::WAVE) { continue; } const MusECore::PartList* parts=(*t_it)->cparts(); for (MusECore::ciPart p_it=parts->begin(); p_it!=parts->end(); p_it++) { if (p_it->second->selected()) { MusECore::Part* part = p_it->second; if(!undoStarted) { undoStarted = true; MusEGlobal::song->startUndo(); } normalizePart(part); } } } //if nothing selected, normilize current part under mouse (if given) if(!undoStarted && partCursor) { undoStarted = true; MusEGlobal::song->startUndo(); normalizePart(partCursor); } if(undoStarted) { MusEGlobal::song->endUndo(SC_CLIP_MODIFIED); } } //--------------------------------------------------------- // beat //--------------------------------------------------------- void Song::beat() { // Watchdog for checking and setting timebase master state. static int _timebaseMasterCounter = 0; if(MusEGlobal::audioDevice && MusEGlobal::audioDevice->hasOwnTransport() && MusEGlobal::audioDevice->hasTimebaseMaster() && MusEGlobal::config.useJackTransport && (--_timebaseMasterCounter <= 0)) { if(MusEGlobal::config.timebaseMaster) { if(!MusEGlobal::timebaseMasterState || !MusEGlobal::audio->isPlaying()) MusEGlobal::audioDevice->setMaster(true); } // Set for once per second. _timebaseMasterCounter = MusEGlobal::config.guiRefresh; } //First: update cpu load toolbar _fCpuLoad = MusEGlobal::muse->getCPULoad(); _fDspLoad = 0.0f; if (MusEGlobal::audioDevice) _fDspLoad = MusEGlobal::audioDevice->getDSP_Load(); _xRunsCount = MusEGlobal::audio->getXruns(); // Keep the sync detectors running... for(int port = 0; port < MusECore::MIDI_PORTS; ++port) MusEGlobal::midiPorts[port].syncInfo().setTime(); if (MusEGlobal::audio->isPlaying()) setPos(CPOS, MusEGlobal::audio->tickPos(), true, false, true); // Process external tempo changes: while(!_tempoFifo.isEmpty()) MusEGlobal::tempo_rec_list.addTempo(_tempoFifo.get()); // Update anything related to audio controller graphs etc. for(ciTrack it = _tracks.begin(); it != _tracks.end(); ++ it) { if((*it)->isMidiTrack()) continue; AudioTrack* at = static_cast(*it); CtrlListList* cll = at->controller(); for(ciCtrlList icl = cll->begin(); icl != cll->end(); ++icl) { CtrlList* cl = icl->second; if(cl->isVisible() && !cl->dontShow() && cl->guiUpdatePending()) emit controllerChanged(at, cl->id()); cl->setGuiUpdatePending(false); } } // Update synth native guis at the heartbeat rate. for(ciSynthI is = _synthIs.begin(); is != _synthIs.end(); ++is) (*is)->guiHeartBeat(); while (noteFifoSize) { int pv = recNoteFifo[noteFifoRindex]; noteFifoRindex = (noteFifoRindex + 1) % REC_NOTE_FIFO_SIZE; int pitch = (pv >> 8) & 0xff; int velo = pv & 0xff; //--------------------------------------------------- // filter midi remote control events //--------------------------------------------------- bool consumed = false; if (MusEGlobal::rcEnable && velo != 0) { if (pitch == MusEGlobal::rcStopNote) { setStop(true); consumed = true; } else if (pitch == MusEGlobal::rcRecordNote) { setRecord(true); consumed = true; } else if (pitch == MusEGlobal::rcGotoLeftMarkNote) { setPos(CPOS, pos[LPOS].tick(), true, true, true); consumed = true; } else if (pitch == MusEGlobal::rcPlayNote) { setPlay(true); consumed = true; } else if (pitch == MusEGlobal::rcForwardNote) { forward(); consumed = true; } else if (pitch == MusEGlobal::rcBackwardNote) { rewind(); consumed = true; } } if (!consumed) emit MusEGlobal::song->midiNote(pitch, velo); --noteFifoSize; } if (MusEGlobal::rcEnableCC && rcCC > -1) { int cc = rcCC & 0xff; printf("*** CC in: %d\n", cc); if (cc == MusEGlobal::rcStopCC) setStop(true); else if (cc == MusEGlobal::rcPlayCC) setPlay(true); else if (cc == MusEGlobal::rcRecordCC) setRecord(true); else if (cc == MusEGlobal::rcGotoLeftMarkCC) setPos(CPOS, pos[LPOS].tick(), true, true, true); else if (cc == MusEGlobal::rcForwardCC) forward(); else if (cc == MusEGlobal::rcBackwardCC) rewind(); rcCC = -1; } } //--------------------------------------------------------- // setLen //--------------------------------------------------------- void Song::setLen(unsigned l, bool do_update) { _len = l; if(do_update) update(); } //--------------------------------------------------------- // addMarker //--------------------------------------------------------- void Song::addMarker(const QString& s, unsigned t, bool lck) { Marker m(s); m.setType(lck ? Pos::FRAMES : Pos::TICKS); m.setTick(t); MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddMarker, m)); } void Song::addMarker(const QString& s, const Pos& p) { Marker m(s); m.setType(p.type()); m.setPos(p); MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddMarker, m)); } //--------------------------------------------------------- // addMarker //--------------------------------------------------------- iMarker Song::getMarkerAt(unsigned t) { return _markerList->find(t); } //--------------------------------------------------------- // removeMarker //--------------------------------------------------------- void Song::removeMarker(const Marker& marker) { MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteMarker, marker)); } void Song::setMarkerName(const Marker& marker, const QString& s) { Marker m(marker); m.setName(s); MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyMarker, marker, m)); } void Song::setMarkerPos(const Marker& marker, const Pos& pos) { // Here we use the separate SetMarkerPos operation, which is 'combo-breaker' aware, to optimize repeated adjustments. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::SetMarkerPos, marker, pos.posValue(), pos.type())); } void Song::setMarkerLock(const Marker& marker, bool f) { Marker m(marker); m.setType(f ? Pos::FRAMES : Pos::TICKS); MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::ModifyMarker, marker, m)); } //--------------------------------------------------------- // setRecordFlag //--------------------------------------------------------- void Song::setRecordFlag(Track* track, bool val, Undo* operations) { if(operations) { // The undo system calls setRecordFlag1 for us. operations->push_back(UndoOp(UndoOp::SetTrackRecord, track, val)); //operations->push_back(UndoOp(UndoOp::SetTrackRecord, track, val, true)); // No undo. } else { // The pending operations system does not call setRecordFlag1 for us. Call it now. if(!track->setRecordFlag1(val)) return; // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(track, val, MusECore::PendingOperationItem::SetTrackRecord)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } } //--------------------------------------------------------- // endMsgCmd //--------------------------------------------------------- void Song::endMsgCmd() { if (updateFlags) { redoList->clearDelete(); // It is possible the undo list is empty after removal of an empty undo, // either by optimization or no given operations. if(MusEGlobal::undoAction) MusEGlobal::undoAction->setEnabled(!undoList->empty()); if(MusEGlobal::redoAction) MusEGlobal::redoAction->setEnabled(false); setUndoRedoText(); emit songChanged(updateFlags); } } //--------------------------------------------------------- // undo //--------------------------------------------------------- void Song::undo() { if (MusEGlobal::audio->isRecording()) { return; } updateFlags = SongChangedStruct_t(); Undo& opGroup = undoList->back(); if (opGroup.empty()) return; MusEGlobal::audio->msgRevertOperationGroup(opGroup); redoList->push_back(opGroup); undoList->pop_back(); if(MusEGlobal::redoAction) MusEGlobal::redoAction->setEnabled(true); if(MusEGlobal::undoAction) MusEGlobal::undoAction->setEnabled(!undoList->empty()); setUndoRedoText(); emit songChanged(updateFlags); emit sigDirty(); } //--------------------------------------------------------- // redo //--------------------------------------------------------- void Song::redo() { if (MusEGlobal::audio->isRecording()) { return; } updateFlags = SongChangedStruct_t(); Undo& opGroup = redoList->back(); if (opGroup.empty()) return; MusEGlobal::audio->msgExecuteOperationGroup(opGroup); undoList->push_back(opGroup); redoList->pop_back(); if(MusEGlobal::undoAction) MusEGlobal::undoAction->setEnabled(true); if(MusEGlobal::redoAction) MusEGlobal::redoAction->setEnabled(!redoList->empty()); setUndoRedoText(); emit songChanged(updateFlags); emit sigDirty(); } //--------------------------------------------------------- // processMsg // executed in realtime thread context //--------------------------------------------------------- void Song::processMsg(AudioMsg* msg) { switch(msg->id) { case SEQM_UPDATE_SOLO_STATES: updateSoloStates(); break; case SEQM_EXECUTE_PENDING_OPERATIONS: msg->pendingOps->executeRTStage(); break; case SEQM_EXECUTE_OPERATION_GROUP: executeOperationGroup2(*msg->operations); break; case SEQM_REVERT_OPERATION_GROUP: revertOperationGroup2(*msg->operations); break; default: fprintf(stderr, "unknown seq message %d\n", msg->id); break; } } //--------------------------------------------------------- // panic //--------------------------------------------------------- void Song::panic() { MusEGlobal::audio->msgPanic(); } //--------------------------------------------------------- // clear // signal - emit signals for changes if true // called from constructor as clear(false) and // from MusE::clearSong() as clear(false) // If clear_all is false, it will not touch things like midi ports. //--------------------------------------------------------- void Song::clear(bool signal, bool clear_all) { if(MusEGlobal::debugMsg) fprintf(stderr, "Song::clear\n"); bounceTrack = 0; _tracks.clear(); _midis.clearDelete(); _waves.clearDelete(); _inputs.clearDelete(); // audio input ports _outputs.clearDelete(); // audio output ports _groups.clearDelete(); // mixer groups _auxs.clearDelete(); // aux sends // p3.3.45 Clear all midi port devices. for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { // p3.3.50 Since midi ports are not deleted, clear all midi port in/out routes. They point to non-existant tracks now. MusEGlobal::midiPorts[i].inRoutes()->clear(); MusEGlobal::midiPorts[i].outRoutes()->clear(); // p3.3.50 Reset this. MusEGlobal::midiPorts[i].setFoundInSongFile(false); if(clear_all) // Allow not touching devices. p4.0.17 TESTING: Maybe some problems... // This will also close the device. MusEGlobal::midiPorts[i].setMidiDevice(0); } _synthIs.clearDelete(); // p3.3.45 Make sure to delete Jack midi devices, and remove all ALSA midi device routes... // Otherwise really nasty things happen when loading another song when one is already loaded. // The loop is a safe way to delete while iterating. bool loop; do { loop = false; for(iMidiDevice imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) { if(dynamic_cast< MidiJackDevice* >(*imd)) { if(clear_all) // Allow not touching devices. p4.0.17 TESTING: Maybe some problems... { // Remove the device from the list. MusEGlobal::midiDevices.erase(imd); // Since Jack midi devices are created dynamically, we must delete them. // The destructor unregisters the device from Jack, which also disconnects all device-to-jack routes. // This will also delete all midi-track-to-device routes, they point to non-existant midi tracks // which were all deleted above delete (*imd); loop = true; break; } } #ifdef ALSA_SUPPORT else if(dynamic_cast< MidiAlsaDevice* >(*imd)) { // With alsa devices, we must not delete them (they're always in the list). But we must // clear all routes. They point to non-existant midi tracks, which were all deleted above. (*imd)->inRoutes()->clear(); (*imd)->outRoutes()->clear(); } #endif } } while (loop); MusEGlobal::tempomap.clear(); MusEGlobal::tempo_rec_list.clear(); MusEGlobal::sigmap.clear(); MusEGlobal::keymap.clear(); // Clear these metronome settings. // A loaded song can override these if it chooses. MusEGlobal::metroUseSongSettings = false; if(MusEGlobal::metroSongSettings.metroAccentsMap) MusEGlobal::metroSongSettings.metroAccentsMap->clear(); undoList->clearDelete(); redoList->clearDelete(); if(MusEGlobal::undoAction) MusEGlobal::undoAction->setEnabled(false); if(MusEGlobal::redoAction) MusEGlobal::redoAction->setEnabled(false); setUndoRedoText(); _markerList->clear(); pos[0].setTick(0); pos[1].setTick(0); pos[2].setTick(0); _vcpos.setTick(0); Track::clearSoloRefCounts(); clearMidiTransforms(); clearMidiInputTransforms(); // Clear all midi port controller values. for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { // Remove the controllers AND the values so we start with a clean slate. MusEGlobal::midiPorts[i].controller()->clearDelete(true); // Don't forget to re-add the default managed controllers. MusEGlobal::midiPorts[i].addDefaultControllers(); } MusEGlobal::tempomap.setMasterFlag(0, true); loopFlag = false; loopFlag = false; punchinFlag = false; punchoutFlag = false; recordFlag = false; soloFlag = false; _recMode = REC_OVERDUP; _cycleMode = CYCLE_NORMAL; _click = false; _quantize = false; _len = MusEGlobal::sigmap.bar2tick(150, 0, 0); // default song len in ticks set for 150 bars _follow = JUMP; dirty = false; initDrumMap(); initNewDrumMap(); if (signal) { emit loopChanged(false); emit recordChanged(false); emit songChanged(-1); } } //--------------------------------------------------------- // cleanupForQuit // called from Muse::closeEvent //--------------------------------------------------------- void Song::cleanupForQuit() { bounceTrack = nullptr; if(MusEGlobal::debugMsg) fprintf(stderr, "MusE: Song::cleanupForQuit...\n"); _tracks.clear(); if(MusEGlobal::debugMsg) fprintf(stderr, "deleting _midis\n"); _midis.clearDelete(); if(MusEGlobal::debugMsg) fprintf(stderr, "deleting _waves\n"); _waves.clearDelete(); if(MusEGlobal::debugMsg) fprintf(stderr, "deleting _inputs\n"); _inputs.clearDelete(); // audio input ports if(MusEGlobal::debugMsg) fprintf(stderr, "deleting _outputs\n"); _outputs.clearDelete(); // audio output ports if(MusEGlobal::debugMsg) fprintf(stderr, "deleting _groups\n"); _groups.clearDelete(); // mixer groups if(MusEGlobal::debugMsg) fprintf(stderr, "deleting _auxs\n"); _auxs.clearDelete(); // aux sends if(MusEGlobal::debugMsg) fprintf(stderr, "deleting _synthIs\n"); _synthIs.clearDelete(); // each ~SynthI() -> deactivate3() -> ~SynthIF() MusEGlobal::tempomap.clear(); MusEGlobal::sigmap.clear(); MusEGlobal::keymap.clear(); if(MusEGlobal::debugMsg) fprintf(stderr, "deleting undoList and redoList\n"); undoList->clearDelete(); redoList->clearDelete(); _markerList->clear(); if(MusEGlobal::debugMsg) fprintf(stderr, "deleting transforms\n"); clearMidiTransforms(); // Deletes stuff. clearMidiInputTransforms(); // Deletes stuff. if(MusEGlobal::debugMsg) fprintf(stderr, "deleting midiport controllers\n"); // Clear all midi port controllers and values. for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { MusEGlobal::midiPorts[i].controller()->clearDelete(true); // Remove the controllers and the values. MusEGlobal::midiPorts[i].setMidiDevice(0); } // Can't do this here. Jack isn't running. Fixed. Test OK so far. DELETETHIS (the comment and #if/#endif) #if 1 if(MusEGlobal::debugMsg) fprintf(stderr, "deleting midi devices except synths\n"); for(iMidiDevice imd = MusEGlobal::midiDevices.begin(); imd != MusEGlobal::midiDevices.end(); ++imd) { // Close the device. Handy to do all devices here, including synths. (*imd)->close(); // Since Syntis are midi devices, there's no need to delete them below. if((*imd)->isSynti()) continue; delete (*imd); } MusEGlobal::midiDevices.clear(); // midi devices #endif if(MusEGlobal::debugMsg) fprintf(stderr, "deleting global available synths\n"); // Delete all synths. std::vector::iterator is; for(is = MusEGlobal::synthis.begin(); is != MusEGlobal::synthis.end(); ++is) { Synth* s = *is; if(s) delete s; } MusEGlobal::synthis.clear(); if(MusEGlobal::debugMsg) fprintf(stderr, "deleting midi instruments\n"); for(iMidiInstrument imi = midiInstruments.begin(); imi != midiInstruments.end(); ++imi) { // Since Syntis are midi instruments, there's no need to delete them below. // Tricky, must cast as SynthI*. SynthI* s = dynamic_cast (*imi); if(s) continue; delete (*imi); } midiInstruments.clear(); // midi instruments // Nothing required for ladspa plugin list, and rack instances of them // are handled by ~AudioTrack. if(MusEGlobal::debugMsg) fprintf(stderr, "...finished cleaning up.\n"); } void Song::seqSignal(int fd) { const int buf_size = 256; char buffer[buf_size]; int n = ::read(fd, buffer, buf_size); if (n < 0) { fprintf(stderr, "Song: seqSignal(): READ PIPE failed: %s\n", strerror(errno)); return; } bool do_set_sync_timeout = false; for (int i = 0; i < n; ++i) { switch(buffer[i]) { case '0': // STOP do_set_sync_timeout = true; stopRolling(); break; case '1': // PLAY do_set_sync_timeout = true; setStopPlay(true); break; case '2': // record setRecord(true); break; case '3': // START_PLAY + jack STOP do_set_sync_timeout = true; abortRolling(); break; case 'P': // alsa ports changed alsaScanMidiPorts(); break; case 'G': // Seek // Hm, careful here, will multiple seeks cause this // to interfere with Jack's transport timeout countdown? do_set_sync_timeout = true; clearRecAutomation(true); setPos(CPOS, MusEGlobal::audio->tickPos(), true, false, true); _startPlayPosition = MusEGlobal::audio->pos(); // update start position break; case 'S': // shutdown audio MusEGlobal::muse->seqStop(); { // give the user a sensible explanation int btn = QMessageBox::critical( MusEGlobal::muse, tr("Jack shutdown!"), tr("Jack has detected a performance problem which has lead to\n" "MusE being disconnected.\n" "This could happen due to a number of reasons:\n" "- a performance issue with your particular setup.\n" "- a bug in MusE (or possibly in another connected software).\n" "- a random hiccup which might never occur again.\n" "- jack was voluntary stopped by you or someone else\n" "- jack crashed\n" "If there is a persisting problem you are much welcome to discuss it\n" "on the MusE mailinglist.\n" "(there is information about joining the mailinglist on the MusE\n" " homepage which is available through the help menu)\n" "\n" "To proceed check the status of Jack and try to restart it and then .\n" "click on the Restart button."), "restart", "cancel"); if (btn == 0) { fprintf(stderr, "restarting!\n"); MusEGlobal::muse->seqRestart(); } } break; // REMOVE Tim. latency. Removed. We now do this in MusE::bounceToFile() and MusE::bounceToTrack(), BEFORE the transport is started. // case 'f': // start freewheel // if(MusEGlobal::debugMsg) // fprintf(stderr, "Song: seqSignal: case f: setFreewheel start\n"); // // if(MusEGlobal::config.freewheelMode) // MusEGlobal::audioDevice->setFreewheel(true); // // break; case 'F': // stop freewheel if(MusEGlobal::debugMsg) fprintf(stderr, "Song: seqSignal: case F: setFreewheel stop\n"); if(MusEGlobal::config.freewheelMode) MusEGlobal::audioDevice->setFreewheel(false); break; case 'A': // Abort rolling + Special stop bounce (offline) mode do_set_sync_timeout = true; abortRolling(); // Switch all the wave converters back to online mode. setAudioConvertersOfflineOperation(false); break; case 'B': // Stop + Special stop bounce mode do_set_sync_timeout = true; stopRolling(); // Switch all the wave converters back to online mode. setAudioConvertersOfflineOperation(false); break; case 'C': // Graph changed if (MusEGlobal::audioDevice) MusEGlobal::audioDevice->graphChanged(); break; case 'R': // Registration changed if (MusEGlobal::audioDevice) MusEGlobal::audioDevice->registrationChanged(); break; case 'J': // Port connections changed if (MusEGlobal::audioDevice) MusEGlobal::audioDevice->connectionsChanged(); break; // case 'U': // Send song changed signal // { // int d_len = sizeof(SongChangedStruct_t); // if((n - (i + 1)) < d_len) // i + 1 = data after this 'U' // { // fprintf(stderr, "Song: seqSignal: case U: Not enough bytes read for SongChangedStruct_t !\n"); // break; // } // SongChangedStruct_t f; // memcpy(&f, &buffer[i + 1], d_len); // i += d_len; // Move pointer ahead. Loop will also add one ++i. // update(f); // } // break; case 'D': // Drum map changed update(SC_DRUMMAP); break; // case 'E': // Midi events are available in the ipc event buffer. // if(MusEGlobal::song) // MusEGlobal::song->processIpcInEventBuffers(); // break; case 'T': // We are now the timebase master. MusEGlobal::timebaseMasterState = true; update(SC_TIMEBASE_MASTER); break; case 't': // We are no longer the timebase master. MusEGlobal::timebaseMasterState = false; update(SC_TIMEBASE_MASTER); break; default: fprintf(stderr, "unknown Seq Signal <%c>\n", buffer[i]); break; } } // Since other Jack clients might also set the sync timeout at any time, // we need to be constantly enforcing our desired limit! // Since setSyncTimeout() may not be realtime friendly (Jack driver), // we set the driver's sync timeout here in the gui thread. // Sadly, we likely cannot get away with setting it in the audio sync callback. // So whenever stop, start or seek occurs, we'll try to casually enforce the timeout here. // It's casual, unfortunately we can't set the EXACT timeout amount when we really need to // (that's in audio sync callback) so we try this for now... if(do_set_sync_timeout && MusEGlobal::checkAudioDevice()) { // Enforce a 30 second timeout. // TODO: Split this up and have user adjustable normal (2 or 10 second default) value, // plus a contribution from the total required precount time. // Too bad we likely can't set it dynamically in the audio sync callback. MusEGlobal::audioDevice->setSyncTimeout(30000000); } } //--------------------------------------------------------- // recordEvent //--------------------------------------------------------- void Song::recordEvent(MidiTrack* mt, Event& event) { //--------------------------------------------------- // if tick points into a part, // record to that part // else // create new part //--------------------------------------------------- unsigned tick = event.tick(); PartList* pl = mt->parts(); const MidiPart* part = 0; iPart ip; for (ip = pl->begin(); ip != pl->end(); ++ip) { part = (MidiPart*)(ip->second); unsigned partStart = part->tick(); unsigned partEnd = partStart + part->lenTick(); if (tick >= partStart && tick < partEnd) break; } updateFlags |= SC_EVENT_INSERTED; if (ip == pl->end()) { // create new part MidiPart* part = new MidiPart(mt); int startTick = roundDownBar(tick); int endTick = roundUpBar(tick + 1); part->setTick(startTick); part->setLenTick(endTick - startTick); part->setName(mt->name()); event.move(-startTick); part->addEvent(event); MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddPart, part)); return; } part = (MidiPart*)(ip->second); tick -= part->tick(); event.setTick(tick); Event ev; if(event.type() == Controller) { cEventRange range = part->events().equal_range(tick); for(ciEvent i = range.first; i != range.second; ++i) { ev = i->second; if(ev.type() == Controller && ev.dataA() == event.dataA()) { if(ev.dataB() == event.dataB()) // Don't bother if already set. return; MusEGlobal::song->applyOperation(UndoOp(UndoOp::ModifyEvent,event,ev,part,true,true)); return; } } } MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddEvent, event, part, true,true)); } //--------------------------------------------------------- // execAutomationCtlPopup //--------------------------------------------------------- int Song::execAutomationCtlPopup(AudioTrack* track, const QPoint& menupos, int acid) { enum { PREV_EVENT=0, NEXT_EVENT, ADD_EVENT, CLEAR_EVENT, CLEAR_RANGE, CLEAR_ALL_EVENTS, MIDI_ASSIGN, MIDI_CLEAR }; QMenu* menu = new QMenu; int count = 0; bool isEvent = false, canSeekPrev = false, canSeekNext = false, canEraseRange = false; bool canAdd = false; double ctlval = 0.0; unsigned int frame = 0; if(track) { ciCtrlList icl = track->controller()->find(acid); if(icl != track->controller()->end()) { CtrlList *cl = icl->second; canAdd = true; frame = MusEGlobal::audio->pos().frame(); bool en = track->controllerEnabled(acid); AutomationType at = track->automationType(); if(!MusEGlobal::automation || at == AUTO_OFF || !en) ctlval = cl->curVal(); else ctlval = cl->value(frame); count = cl->size(); if(count) { iCtrl s = cl->lower_bound(frame); iCtrl e = cl->upper_bound(frame); isEvent = (s != cl->end() && s->second.frame == frame); canSeekPrev = s != cl->begin(); canSeekNext = e != cl->end(); s = cl->lower_bound(pos[1].frame()); canEraseRange = s != cl->end() && pos[2].frame() > s->second.frame; } } } menu->addAction(new MusEGui::MenuTitleItem(tr("Automation"), menu)); QAction* prevEvent = menu->addAction(tr("Previous event")); prevEvent->setData(PREV_EVENT); prevEvent->setEnabled(canSeekPrev); QAction* nextEvent = menu->addAction(tr("Next event")); nextEvent->setData(NEXT_EVENT); nextEvent->setEnabled(canSeekNext); menu->addSeparator(); QAction* addEvent = new QAction(menu); menu->addAction(addEvent); if(isEvent) addEvent->setText(tr("Set event")); else addEvent->setText(tr("Add event")); addEvent->setData(ADD_EVENT); addEvent->setEnabled(canAdd); QAction* eraseEventAction = menu->addAction(tr("Erase event")); eraseEventAction->setData(CLEAR_EVENT); eraseEventAction->setEnabled(isEvent); QAction* eraseRangeAction = menu->addAction(tr("Erase range")); eraseRangeAction->setData(CLEAR_RANGE); eraseRangeAction->setEnabled(canEraseRange); QAction* clearAction = menu->addAction(tr("Clear automation")); clearAction->setData(CLEAR_ALL_EVENTS); clearAction->setEnabled((bool)count); menu->addSeparator(); menu->addAction(new MusEGui::MenuTitleItem(tr("Midi control"), menu)); QAction *assign_act = menu->addAction(tr("Assign")); assign_act->setCheckable(false); assign_act->setData(MIDI_ASSIGN); MidiAudioCtrlMap* macm = track->controller()->midiControls(); AudioMidiCtrlStructMap amcs; macm->find_audio_ctrl_structs(acid, &amcs); if(!amcs.empty()) { QAction *cact = menu->addAction(tr("Clear")); cact->setData(MIDI_CLEAR); menu->addSeparator(); } for(iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) { int port, chan, mctrl; macm->hash_values((*iamcs)->first, &port, &chan, &mctrl); //QString s = QString("Port:%1 Chan:%2 Ctl:%3-%4").arg(port + 1) QString s = QString("Port:%1 Chan:%2 Ctl:%3").arg(port + 1) .arg(chan + 1) //.arg((mctrl >> 8) & 0xff) //.arg(mctrl & 0xff); .arg(midiCtrlName(mctrl, true)); QAction *mact = menu->addAction(s); mact->setEnabled(false); mact->setData(-1); // Not used } QAction* act = menu->exec(menupos); if (!act || !track) { delete menu; return -1; } int sel = act->data().toInt(); delete menu; Undo operations; switch(sel) { case ADD_EVENT: MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddAudioCtrlVal, track, acid, frame, ctlval)); break; case CLEAR_EVENT: MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteAudioCtrlVal, track, acid, frame)); break; case CLEAR_RANGE: MusEGlobal::audio->msgEraseRangeACEvents(track, acid, pos[1].frame(), pos[2].frame()); break; case CLEAR_ALL_EVENTS: if(QMessageBox::question(MusEGlobal::muse, QString("Muse"), tr("Clear all controller events?"), tr("&Ok"), tr("&Cancel"), QString(), 0, 1 ) == 0) MusEGlobal::audio->msgClearControllerEvents(track, acid); break; case PREV_EVENT: MusEGlobal::audio->msgSeekPrevACEvent(track, acid); break; case NEXT_EVENT: MusEGlobal::audio->msgSeekNextACEvent(track, acid); break; case MIDI_ASSIGN: { int port = -1, chan = 0, ctrl = 0; for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) { macm->hash_values((*iamcs)->first, &port, &chan, &ctrl); break; // Only a single item for now, thanks! } MusEGui::MidiAudioControl* pup = new MusEGui::MidiAudioControl(port, chan, ctrl); if(pup->exec() == QDialog::Accepted) { MusEGlobal::audio->msgIdle(true); // Gain access to structures, and sync with audio // Erase all for now. for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) macm->erase(*iamcs); port = pup->port(); chan = pup->chan(); ctrl = pup->ctrl(); if(port >= 0 && chan >=0 && ctrl >= 0) // Add will replace if found. macm->add_ctrl_struct(port, chan, ctrl, MusECore::MidiAudioCtrlStruct(acid)); MusEGlobal::audio->msgIdle(false); } delete pup; } break; case MIDI_CLEAR: if(!amcs.empty()) MusEGlobal::audio->msgIdle(true); // Gain access to structures, and sync with audio for(MusECore::iAudioMidiCtrlStructMap iamcs = amcs.begin(); iamcs != amcs.end(); ++iamcs) macm->erase(*iamcs); if(!amcs.empty()) MusEGlobal::audio->msgIdle(false); break; default: return -1; break; } if(!operations.empty()) MusEGlobal::song->applyOperationGroup(operations); return sel; } //--------------------------------------------------------- // execMidiAutomationCtlPopup //--------------------------------------------------------- int Song::execMidiAutomationCtlPopup(MidiTrack* track, MidiPart* part, const QPoint& menupos, int ctlnum) { if(!track && !part) return -1; enum { BYPASS_CONTROLLER, ADD_EVENT, CLEAR_EVENT }; bool isEvent = false; MidiTrack* mt; if(track) mt = track; else mt = (MidiTrack*)part->track(); int dctl = ctlnum; // Is it a drum controller, according to the track port's instrument? int channel; MidiPort* mp; mt->mappedPortChanCtrl(&dctl, nullptr, &mp, &channel); unsigned tick = cpos(); if(!part) { PartList* pl = mt->parts(); iPart ip; for(ip = pl->begin(); ip != pl->end(); ++ip) { MidiPart* tpart = (MidiPart*)(ip->second); unsigned partStart = tpart->tick(); unsigned partEnd = partStart + tpart->lenTick(); if(tick >= partStart && tick < partEnd) { // Prefer a selected part, otherwise keep looking... if(tpart->selected()) { part = tpart; break; } else // Remember the first part found... if(!part) part = tpart; } } } Event ev; if(part) { unsigned partStart = part->tick(); unsigned partEnd = partStart + part->lenTick(); if(tick >= partStart && tick < partEnd) { cEventRange range = part->events().equal_range(tick - partStart); for(ciEvent i = range.first; i != range.second; ++i) { ev = i->second; if(ev.type() == Controller) { if(ev.dataA() == ctlnum) { isEvent = true; break; } } } } } int initval = 0; MidiController* mc = mp->midiController(ctlnum, channel, false); if(mc) { const int bias = mc->bias(); initval = mc->initVal(); if(initval == CTRL_VAL_UNKNOWN) { if(ctlnum == CTRL_PROGRAM) // Special for program controller: Set HBank and LBank off (0xff), and program to 0. initval = 0xffff00; else // Otherwise start with the bias. initval = bias; } else // Auto bias. initval += bias; } const int cur_val = mp->hwCtrlState(channel, dctl); QMenu* menu = new QMenu; menu->addAction(new MusEGui::MenuTitleItem(tr("Controller"), menu)); QAction* bypassEvent = new QAction(menu); menu->addAction(bypassEvent); bypassEvent->setText(tr("Bypass")); bypassEvent->setData(BYPASS_CONTROLLER); bypassEvent->setEnabled(true); bypassEvent->setCheckable(true); bypassEvent->setChecked(cur_val == CTRL_VAL_UNKNOWN); menu->addAction(new MusEGui::MenuTitleItem(tr("Automation"), menu)); QAction* addEvent = new QAction(menu); menu->addAction(addEvent); if(isEvent) addEvent->setText(tr("Set event")); else addEvent->setText(tr("Add event")); addEvent->setData(ADD_EVENT); addEvent->setEnabled(true); QAction* eraseEventAction = menu->addAction(tr("Erase event")); eraseEventAction->setData(CLEAR_EVENT); eraseEventAction->setEnabled(isEvent); QAction* act = menu->exec(menupos); if (!act) { delete menu; return -1; } const int sel = act->data().toInt(); const bool checked = act->isChecked(); delete menu; switch(sel) { case BYPASS_CONTROLLER: { if(checked) MusEGlobal::audio->msgSetHwCtrlState(mp, channel, dctl, MusECore::CTRL_VAL_UNKNOWN); else { int v = mp->lastValidHWCtrlState(channel, dctl); if(v == MusECore::CTRL_VAL_UNKNOWN) v = initval; MusEGlobal::audio->msgSetHwCtrlState(mp, channel, dctl, v); } } break; case ADD_EVENT: { int v = cur_val; if(v == CTRL_VAL_UNKNOWN) { v = mp->lastValidHWCtrlState(channel, dctl); if(v == MusECore::CTRL_VAL_UNKNOWN) v = initval; } Event e(Controller); e.setA(ctlnum); e.setB(v); // Do we replace an old event? if(isEvent) { if(ev.dataB() == v) // Don't bother if already set. return -1; e.setTick(tick - part->tick()); // Indicate do undo, and do port controller values and clone parts. MusEGlobal::song->applyOperation(UndoOp(UndoOp::ModifyEvent, e, ev, part, true, true)); } else { // Store a new event... if(part) { e.setTick(tick - part->tick()); // Indicate do undo, and do port controller values and clone parts. MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddEvent, e, part, true, true)); } else { // Create a new part... part = new MidiPart(mt); int startTick = roundDownBar(tick); int endTick = roundUpBar(tick + 1); part->setTick(startTick); part->setLenTick(endTick - startTick); part->setName(mt->name()); e.setTick(tick - startTick); part->addEvent(e); MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddPart, part)); } } } break; case CLEAR_EVENT: // Indicate do undo, and do port controller values and clone parts. MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent, ev, part, true, true)); break; default: return -1; break; } return sel; } //--------------------------------------------------------- // putIpcInEvent // Put an event into the IPC event ring buffer for the gui thread to process. Returns true on success. // NOTE: Although the ring buffer is multi-writer, call this from audio thread only for now, unless // you know what you are doing because the thread needs to ask whether the controller exists before // calling, and that may not be safe from threads other than gui or audio. //--------------------------------------------------------- bool Song::putIpcInEvent(const MidiPlayEvent& ev) { if(!_ipcInEventBuffers->put(ev)) { fprintf(stderr, "Error: Song::putIpcInEvent: Buffer overflow\n"); return false; } return true; } //--------------------------------------------------------- // putIpcOutEvent // Put an event into the IPC event ring buffer for the audio thread to process. // Called by gui thread only. Returns true on success. //--------------------------------------------------------- bool Song::putIpcOutEvent(const MidiPlayEvent& ev) { if(!_ipcOutEventBuffers->put(ev)) { fprintf(stderr, "Error: Song::putIpcOutEvent: Buffer overflow\n"); return false; } return true; } //--------------------------------------------------------- // processIpcInEventBuffers // Called by gui thread only. // Returns true on success. //--------------------------------------------------------- bool Song::processIpcInEventBuffers() { PendingOperationList operations; MidiPlayEvent buf_ev; int port, chan, ctrl; MidiPort* mp; iMidiCtrlValList imcvl; MidiCtrlValListList* mcvll; MidiCtrlValList* mcvl; //----------------------------------------------------------- // First pass: Peek into the buffers and find out if any // controllers need to be created here in the gui thread. //----------------------------------------------------------- // False = don't use the size snapshot, but update it. const unsigned int sz = _ipcInEventBuffers->getSize(false); for(unsigned int i = 0; i < sz; ++i) { buf_ev = _ipcInEventBuffers->peek(i); port = buf_ev.port(); if(port < 0 || port >= MusECore::MIDI_PORTS) continue; chan = buf_ev.channel(); if(chan < 0 || chan >= MusECore::MUSE_MIDI_CHANNELS) continue; ctrl = buf_ev.translateCtrlNum(); // Event translates to a controller? if(ctrl < 0) continue; mp = &MusEGlobal::midiPorts[port]; mcvll = mp->controller(); // Does the controller exist? imcvl = mcvll->find(chan, ctrl); if(imcvl == mcvll->end()) { // Controller does not exist. Prepare a pending operation. PendingOperationItem poi(mcvll, 0, chan, ctrl, PendingOperationItem::AddMidiCtrlValList); // Have we already created and prepared this controller? Look in the operations list. iPendingOperation ipos = operations.findAllocationOp(poi); if(ipos == operations.end()) { // We have not created and prepared the controller. Create it now. mcvl = new MidiCtrlValList(ctrl); // Set the operation controller member now. poi._mcvl = mcvl; // Add the operation to the pending operations. operations.add(poi); } } } // Execute any operations to create controllers. // This waits for audio process thread to execute it. if(!operations.empty()) MusEGlobal::audio->msgExecutePendingOperations(operations, true); //----------------------------------------------------------- // Second pass: Read the buffers and set the controller values. // For the moment, the writer threads may have also put some more events // into these buffers while they checked if the controller existed. //----------------------------------------------------------- for(unsigned int i = 0; i < sz; ++i) { if(!_ipcInEventBuffers->get(buf_ev)) continue; port = buf_ev.port(); if(port < 0 || port >= MusECore::MIDI_PORTS) continue; chan = buf_ev.channel(); if(chan < 0 || chan >= MusECore::MUSE_MIDI_CHANNELS) continue; ctrl = buf_ev.translateCtrlNum(); // Event translates to a controller? if(ctrl < 0) continue; mp = &MusEGlobal::midiPorts[port]; mcvll = mp->controller(); // Put the event BACK INTO the midi port's event buffer so that // the port will process it 'where it left off' before it put // this controller creation event into this ring buffer. // It also allows the port to call updateDrumMap in the audio thread. // Keep the time intact, so the driver will at least play them in // sequence even though they will all be 'bunched up' at frame zero. // Make sure the controller REALLY was created before proceeding, // otherwise the mechanism might get stuck in a continuous loop. // imcvl = mcvll->find(chan, ctrl); // if(imcvl != mcvll->end()) { //mp->putHwCtrlEvent(buf_ev); // Let's bypass the putHwCtrlEvent and save some time - // put directly into the midi port's controller event buffers. // This will also prevent getting stuck in continuous loop. if(!_ipcOutEventBuffers->put(buf_ev)) { fprintf(stderr, "Error: Song::processIpcInEventBuffers(): Midi port controller fifo overflow\n"); continue; } } } return true; } //--------------------------------------------------------- // processIpcOutEventBuffers // Called from audio thread only. // Returns true on success. //--------------------------------------------------------- bool Song::processIpcOutEventBuffers() { // Receive hardware state events sent from various threads to this audio thread. // Update hardware state so gui controls are updated. // False = don't use the size snapshot, but update it. const int sz = _ipcOutEventBuffers->getSize(false); MidiPlayEvent ev; for(int i = 0; i < sz; ++i) { if(!_ipcOutEventBuffers->get(ev)) continue; const int port = ev.port(); if(port < 0 || port >= MusECore::MIDI_PORTS) continue; // Handle the event. Tell the gui NOT to create controllers as needed, // that should be done before it ever gets here. MusEGlobal::midiPorts[port].handleGui2AudioEvent(ev, false); } return true; } //--------------------------------------------------------- // updateSoloStates // This will properly set all soloing variables (including other tracks) based entirely // on the current values of all the tracks' _solo members. //--------------------------------------------------------- void Song::updateSoloStates() { Track::clearSoloRefCounts(); for(ciTrack i = _tracks.begin(); i != _tracks.end(); ++i) (*i)->setInternalSolo(0); for(ciTrack i = _tracks.begin(); i != _tracks.end(); ++i) (*i)->updateSoloStates(true); } //--------------------------------------------------------- // reenableTouchedControllers // Enable all track and plugin controllers, and synth controllers if applicable, which are NOT in AUTO_WRITE mode. //--------------------------------------------------------- void Song::reenableTouchedControllers() { for(iTrack it = _tracks.begin(); it != _tracks.end(); ++it) { if((*it)->isMidiTrack()) continue; AudioTrack* t = static_cast(*it); AutomationType at = t->automationType(); if(at == AUTO_WRITE) // Exclude write mode because controls need to remain disabled if pressed before play. continue; t->enableAllControllers(); } } //--------------------------------------------------------- // clearRecAutomation //--------------------------------------------------------- void Song::clearRecAutomation(bool clearList) { // Clear all pan/vol pressed and touched flags, and all rec event lists, if needed. for (iTrack it = tracks()->begin(); it != tracks()->end(); ++it) ((Track*)(*it))->clearRecAutomation(clearList); } //--------------------------------------------------------- // processAutomationEvents //--------------------------------------------------------- void Song::processAutomationEvents(Undo* operations) { Undo ops; Undo* opsp = operations ? operations : &ops; // Clear all pressed and touched flags. // This is a non-undoable 'one-time' operation, removed after execution. opsp->push_back(UndoOp(UndoOp::EnableAllAudioControllers)); for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i) { if(!(*i)->isMidiTrack()) // Process (and clear) rec events. processTrackAutomationEvents((AudioTrack*)*i, opsp); } if(!operations) MusEGlobal::song->applyOperationGroup(ops); } //--------------------------------------------------------- // processMasterRec //--------------------------------------------------------- void Song::processMasterRec() { // bool do_tempo = false; // Wait a few seconds for the tempo fifo to be empty. int tout = 100; // Ten seconds. Otherwise we gotta move on. while(!_tempoFifo.isEmpty()) { usleep(100000); --tout; if(tout == 0) { fprintf(stderr, "Song::processMasterRec: Error: Timeout waiting for _tempoFifo to empty!\n"); break; } } const int tempo_rec_list_sz = MusEGlobal::tempo_rec_list.size(); if(tempo_rec_list_sz != 0) { if(QMessageBox::question(MusEGlobal::muse, tr("MusE: Tempo list"), tr("External tempo changes were recorded.\nTransfer them to master tempo list?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Ok) { // FIXME TODO: Change the tempomap and tempo_rec_list to allocated pointers so they can be quickly swapped in realtime without idling. MusEGlobal::audio->msgIdle(true); // gain access to all data structures // Erase from master tempo the (approximate) recording start/end tick range according to the recorded tempo map, //MusEGlobal::tempomap.eraseRange(MusEGlobal::tempo_rec_list.frame2tick(MusEGlobal::audio->getStartRecordPos().frame()), // MusEGlobal::tempo_rec_list.frame2tick(MusEGlobal::audio->getEndRecordPos().frame())); // This is more accurate but lacks resolution: MusEGlobal::tempomap.eraseRange(MusEGlobal::audio->getStartExternalRecTick(), MusEGlobal::audio->getEndExternalRecTick()); // Add the recorded tempos to the master tempo list: for(int i = 0; i < tempo_rec_list_sz; ++i) MusEGlobal::tempomap.addTempo(MusEGlobal::tempo_rec_list[i].tick, MusEGlobal::tempo_rec_list[i].tempo, false); // False: Defer normalize MusEGlobal::tempomap.normalize(); MusEGlobal::audio->msgIdle(false); update(SC_TEMPO); } // It should be safe to do this here in the GUI thread, the driver should not be touching it anymore. MusEGlobal::tempo_rec_list.clear(); } } //--------------------------------------------------------- // abortRolling //--------------------------------------------------------- void Song::abortRolling() { if(MusEGlobal::audio->freewheel()) MusEGlobal::audioDevice->setFreewheel(false); if (record()) MusEGlobal::audio->recordStop(); setStopPlay(false); } //--------------------------------------------------------- // stopRolling //--------------------------------------------------------- void Song::stopRolling(Undo* operations) { if(MusEGlobal::audio->freewheel()) MusEGlobal::audioDevice->setFreewheel(false); Undo ops; Undo* opsp = operations ? operations : &ops; if (record()) MusEGlobal::audio->recordStop(false, opsp); setStopPlay(false); processAutomationEvents(opsp); if (MusEGlobal::config.useRewindOnStop) { setPos(Song::CPOS, _startPlayPosition); } if(!operations) MusEGlobal::song->applyOperationGroup(ops); } //--------------------------------------------------------- // connectJackRoutes //--------------------------------------------------------- bool Song::connectJackRoutes(const MusECore::Route& src, const MusECore::Route& dst, bool disconnect) { //fprintf(stderr, "connectJackRoutes:\n"); if(!MusEGlobal::checkAudioDevice() || !MusEGlobal::audio->isRunning()) return false; switch(src.type) { case Route::JACK_ROUTE: switch(dst.type) { case Route::JACK_ROUTE: if(disconnect) return MusEGlobal::audioDevice->disconnect(src.persistentJackPortName, dst.persistentJackPortName); else return MusEGlobal::audioDevice->connect(src.persistentJackPortName, dst.persistentJackPortName); break; case Route::MIDI_DEVICE_ROUTE: if(dst.device && dst.device->deviceType() == MidiDevice::JACK_MIDI && dst.device->inClientPort()) { if(disconnect) return MusEGlobal::audioDevice->disconnect(src.persistentJackPortName, MusEGlobal::audioDevice->canonicalPortName(dst.device->inClientPort())); else return MusEGlobal::audioDevice->connect(src.persistentJackPortName, MusEGlobal::audioDevice->canonicalPortName(dst.device->inClientPort())); } break; case Route::TRACK_ROUTE: if(dst.track && dst.track->type() == Track::AUDIO_INPUT && dst.channel >= 0) { AudioInput* ai = static_cast(dst.track); if(ai->jackPort(dst.channel)) { if(disconnect) return MusEGlobal::audioDevice->disconnect(src.persistentJackPortName, MusEGlobal::audioDevice->canonicalPortName(ai->jackPort(dst.channel))); else return MusEGlobal::audioDevice->connect(src.persistentJackPortName, MusEGlobal::audioDevice->canonicalPortName(ai->jackPort(dst.channel))); } } break; case Route::MIDI_PORT_ROUTE: break; } break; case Route::MIDI_DEVICE_ROUTE: switch(dst.type) { case Route::JACK_ROUTE: if(src.device && src.device->deviceType() == MidiDevice::JACK_MIDI && src.device->outClientPort()) { if(disconnect) return MusEGlobal::audioDevice->disconnect(MusEGlobal::audioDevice->canonicalPortName(src.device->outClientPort()), dst.persistentJackPortName); else return MusEGlobal::audioDevice->connect(MusEGlobal::audioDevice->canonicalPortName(src.device->outClientPort()), dst.persistentJackPortName); } break; case Route::MIDI_DEVICE_ROUTE: case Route::TRACK_ROUTE: case Route::MIDI_PORT_ROUTE: break; } break; case Route::TRACK_ROUTE: switch(dst.type) { case Route::JACK_ROUTE: if(src.track && src.track->type() == Track::AUDIO_OUTPUT && src.channel >= 0) { AudioOutput* ao = static_cast(src.track); if(ao->jackPort(src.channel)) { if(disconnect) return MusEGlobal::audioDevice->disconnect(MusEGlobal::audioDevice->canonicalPortName(ao->jackPort(src.channel)), dst.persistentJackPortName); else return MusEGlobal::audioDevice->connect(MusEGlobal::audioDevice->canonicalPortName(ao->jackPort(src.channel)), dst.persistentJackPortName); } } break; case Route::MIDI_DEVICE_ROUTE: case Route::TRACK_ROUTE: case Route::MIDI_PORT_ROUTE: break; } break; case Route::MIDI_PORT_ROUTE: break; } return false; } //--------------------------------------------------------- // connectMidiPorts //--------------------------------------------------------- void Song::connectMidiPorts() { // Connect midi device ports to Jack ports... for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) { MidiDevice* md = *i; if(md->deviceType() != MidiDevice::JACK_MIDI) continue; // Midi outputs... if(md->rwFlags() & 1) { void* our_port = md->outClientPort(); if(our_port) { const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); if(our_port_name) { RouteList* rl = md->outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::JACK_ROUTE) continue; const char* route_name = ir->persistentJackPortName; if(!MusEGlobal::audioDevice->findPort(route_name)) continue; //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) MusEGlobal::audioDevice->connect(our_port_name, route_name); } } } } // Midi inputs... if(md->rwFlags() & 2) { void* our_port = md->inClientPort(); if(our_port) { const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); if(our_port_name) { RouteList* rl = md->inRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::JACK_ROUTE) continue; const char* route_name = ir->persistentJackPortName; if(!MusEGlobal::audioDevice->findPort(route_name)) continue; //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) MusEGlobal::audioDevice->connect(route_name, our_port_name); } } } } } } //--------------------------------------------------------- // connectAudioPorts //--------------------------------------------------------- void Song::connectAudioPorts() { if(!MusEGlobal::audioDevice) return; // Connect audio output ports to Jack ports... OutputList* ol = outputs(); for(iAudioOutput i = ol->begin(); i != ol->end(); ++i) { AudioOutput* ao = *i; int channel = ao->channels(); for(int ch = 0; ch < channel; ++ch) { void* our_port = ao->jackPort(ch); if(!our_port) continue; const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); if(!our_port_name) continue; RouteList* rl = ao->outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::JACK_ROUTE || ir->channel != ch) continue; const char* route_name = ir->persistentJackPortName; if(!MusEGlobal::audioDevice->findPort(route_name)) continue; //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) MusEGlobal::audioDevice->connect(our_port_name, route_name); } } } // Connect Jack ports to audio input ports... InputList* il = inputs(); for(iAudioInput i = il->begin(); i != il->end(); ++i) { AudioInput* ai = *i; int channel = ai->channels(); for(int ch = 0; ch < channel; ++ch) { void* our_port = ai->jackPort(ch); if(!our_port) continue; const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); if(!our_port_name) continue; RouteList* rl = ai->inRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::JACK_ROUTE || ir->channel != ch) continue; const char* route_name = ir->persistentJackPortName; if(!MusEGlobal::audioDevice->findPort(route_name)) continue; //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) MusEGlobal::audioDevice->connect(route_name, our_port_name); } } } } //--------------------------------------------------------- // insertTrack0 //--------------------------------------------------------- void Song::insertTrack0(Track* track, int idx) { int n; switch(track->type()) { case Track::MIDI: case Track::DRUM: _midis.push_back((MidiTrack*)track); break; case Track::WAVE: _waves.push_back((MusECore::WaveTrack*)track); break; case Track::AUDIO_OUTPUT: _outputs.push_back((AudioOutput*)track); break; case Track::AUDIO_GROUP: _groups.push_back((AudioGroup*)track); break; case Track::AUDIO_AUX: _auxs.push_back((AudioAux*)track); break; case Track::AUDIO_INPUT: _inputs.push_back((AudioInput*)track); break; case Track::AUDIO_SOFTSYNTH: { SynthI* s = (SynthI*)track; Synth* sy = s->synth(); if (!s->isActivated()) { // Persistent storage: If the synth is not found allow the track to load. // It's OK if s is NULL. initInstance needs to do a few things. s->initInstance(sy, s->name()); } MusEGlobal::midiDevices.add(s); midiInstruments.push_back(s); _synthIs.push_back(s); } break; default: fprintf(stderr, "unknown track type %d\n", track->type()); return; } // initialize missing aux send iTrack i = _tracks.index2iterator(idx); _tracks.insert(i, track); n = _auxs.size(); for (iTrack i = _tracks.begin(); i != _tracks.end(); ++i) { if ((*i)->isMidiTrack()) continue; MusECore::AudioTrack* wt = (MusECore::AudioTrack*)*i; if (wt->hasAuxSend()) { wt->addAuxSend(n); } } // add routes if (track->isMidiTrack()) // p3.3.50 { const RouteList* rl = track->inRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::MIDI_PORT_ROUTE: { Route src(track, r->channel); MusEGlobal::midiPorts[r->midiPort].outRoutes()->push_back(src); } break; case Route::TRACK_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } rl = track->outRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::MIDI_PORT_ROUTE: { Route src(track, r->channel); MusEGlobal::midiPorts[r->midiPort].inRoutes()->push_back(src); } break; case Route::TRACK_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } } else { const RouteList* rl = track->inRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::TRACK_ROUTE: { Route src(track, r->remoteChannel, r->channels); src.remoteChannel = r->channel; r->track->outRoutes()->push_back(src); // Is the source an Aux Track or else does it have Aux Tracks routed to it? // Update this track's aux ref count. p4.0.37 if(r->track->auxRefCount()) track->updateAuxRoute( r->track->auxRefCount(), NULL ); else if(r->track->type() == Track::AUDIO_AUX) track->updateAuxRoute( 1, NULL ); } break; case Route::MIDI_PORT_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } rl = track->outRoutes(); for (ciRoute r = rl->begin(); r != rl->end(); ++r) { switch(r->type) { case Route::TRACK_ROUTE: { Route src(track, r->remoteChannel, r->channels); src.remoteChannel = r->channel; r->track->inRoutes()->push_back(src); // Is this track an Aux Track or else does it have Aux Tracks routed to it? // Update the other track's aux ref count and all tracks it is connected to. if(track->auxRefCount()) r->track->updateAuxRoute( track->auxRefCount(), NULL ); else if(track->type() == Track::AUDIO_AUX) r->track->updateAuxRoute( 1, NULL ); } break; case Route::MIDI_PORT_ROUTE: case Route::JACK_ROUTE: case Route::MIDI_DEVICE_ROUTE: break; } } } } //--------------------------------------------------------- // insertTrackOperation //--------------------------------------------------------- void Song::insertTrackOperation(Track* track, int idx, PendingOperationList& ops) { //int n; void* sec_track_list = 0; switch(track->type()) { case Track::MIDI: case Track::DRUM: sec_track_list = &_midis; break; case Track::WAVE: sec_track_list = &_waves; break; case Track::AUDIO_OUTPUT: sec_track_list = &_outputs; break; case Track::AUDIO_GROUP: sec_track_list = &_groups; break; case Track::AUDIO_AUX: sec_track_list = &_auxs; break; case Track::AUDIO_INPUT: sec_track_list = &_inputs; break; case Track::AUDIO_SOFTSYNTH: { SynthI* s = static_cast(track); ops.addDeviceOperation(&MusEGlobal::midiDevices, s); ops.add(PendingOperationItem(&midiInstruments, s, PendingOperationItem::AddMidiInstrument)); sec_track_list = &_synthIs; } break; default: fprintf(stderr, "unknown track type %d\n", track->type()); return; } ops.add(PendingOperationItem(&_tracks, track, idx, PendingOperationItem::AddTrack, sec_track_list)); ops.addTrackPortCtrlEvents(track); // NOTE: Aux sends: // Initializing of this track and/or others' aux sends is done at the end of Song::execute/revertOperationGroup2(). // NOTE: Routes: // Routes are added in the PendingOperationItem::AddTrack section of PendingOperationItem::executeRTStage(). } //void writeStringToFile(FILE *filePointer, const char *writeString) //{ // if (MusEGlobal::debugMsg) // std::cout << writeString; // fputs(writeString, filePointer); //} //--------------------------------------------------------- // removeTrackOperation //--------------------------------------------------------- void Song::removeTrackOperation(Track* track, PendingOperationList& ops) { ops.removeTrackPortCtrlEvents(track); void* sec_track_list = 0; switch(track->type()) { case Track::MIDI: case Track::DRUM: sec_track_list = &_midis; break; case Track::WAVE: sec_track_list = &_waves; break; case Track::AUDIO_OUTPUT: sec_track_list = &_outputs; break; case Track::AUDIO_INPUT: sec_track_list = &_inputs; break; case Track::AUDIO_GROUP: sec_track_list = &_groups; break; case Track::AUDIO_AUX: sec_track_list = &_auxs; break; case Track::AUDIO_SOFTSYNTH: { SynthI* s = static_cast(track); iMidiInstrument imi = midiInstruments.find(s); if(imi != midiInstruments.end()) ops.add(PendingOperationItem(&midiInstruments, imi, PendingOperationItem::DeleteMidiInstrument)); iMidiDevice imd = MusEGlobal::midiDevices.find(s); if(imd != MusEGlobal::midiDevices.end()) ops.add(PendingOperationItem(&MusEGlobal::midiDevices, imd, PendingOperationItem::DeleteMidiDevice)); if(s->midiPort() != -1) { // synthi is attached // Oops, hey this was wrong before, should have been zero. MusEGlobal::audio->msgSetMidiDevice(&MusEGlobal::midiPorts[s->midiPort()], 0); } sec_track_list = &_synthIs; } break; } ops.add(PendingOperationItem(&_tracks, track, PendingOperationItem::DeleteTrack, sec_track_list)); // NOTE: Routes: // Routes are removed in the PendingOperationItem::DeleteTrack section of PendingOperationItem::executeRTStage(). } //--------------------------------------------------------- // restartRecording // Called from gui thread only. //--------------------------------------------------------- void Song::restartRecording(bool discard) { // FIXME After recording, it never makes it past here because recording has long since stopped. // Although, it should work WHILE recording. // We may need a track flag like 'justRecorded' or 'lastRecorded' something... if(!MusEGlobal::audio->isRecording() || !MusEGlobal::audio->isRunning()) return; // Do not copy parts or controller graphs. When ASSIGN_STD_CTRLS is NOT included, it will // copy just the standard controller current values, but not the graphs. // FIXME: Although we would like to copy plugins, that may get expensive after a while. // So instead try to create a group track with the plugins and route the track and all // the retake tracks through the group track. const int clone_flags = Track::ASSIGN_PROPERTIES | Track::ASSIGN_ROUTES | Track::ASSIGN_DEFAULT_ROUTES | Track::ASSIGN_DRUMLIST; MusECore::Undo operations; if(!discard) { MusEGlobal::audio->recordStop(true /*restart record*/, &operations); processAutomationEvents(&operations); } //clear all recorded midi events and wave files TrackNameFactory new_track_names; int idx_cnt = 0; for(size_t i = 0; i < _tracks.size(); i++) { Track *cTrk = _tracks[i]; if(!cTrk->recordFlag()) continue; Track *nTrk = NULL; if(!discard) { if(!new_track_names.genUniqueNames(cTrk->type(), cTrk->name(), 1)) continue; nTrk = cTrk->clone(clone_flags); nTrk->setName(new_track_names.first()); const int idx = _tracks.index(cTrk) + idx_cnt++; operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddTrack, idx + 1, nTrk)); operations.push_back(UndoOp(UndoOp::SetTrackMute, cTrk, true)); operations.push_back(UndoOp(UndoOp::SetTrackRecord, cTrk, false)); setRecordFlag(nTrk, true, &operations); } if (cTrk->isMidiTrack()) { if(discard) { ((MidiTrack *)cTrk)->mpevents.clear(); } } else if (cTrk->type() == Track::WAVE) { if(discard) { ((WaveTrack*)cTrk)->setRecFile(NULL); ((WaveTrack*)cTrk)->resetMeter(); ((WaveTrack*)cTrk)->prepareRecording(); } else { ((WaveTrack*)nTrk)->prepareRecording(); } } } applyOperationGroup(operations); setPos(Song::CPOS, MusEGlobal::audio->getStartRecordPos()); //MusEGlobal::audioDevice->startTransport(); } void Song::informAboutNewParts(const std::map< const Part*, std::set >& param) { emit newPartsCreated(param); } void Song::informAboutNewParts(const Part* orig, const Part* p1, const Part* p2, const Part* p3, const Part* p4, const Part* p5, const Part* p6, const Part* p7, const Part* p8, const Part* p9) { std::map > temp; temp[orig].insert(p1); temp[orig].insert(p2); temp[orig].insert(p3); temp[orig].insert(p4); temp[orig].insert(p5); temp[orig].insert(p6); temp[orig].insert(p7); temp[orig].insert(p8); temp[orig].insert(p9); temp[orig].erase(static_cast(NULL)); temp[orig].erase(orig); informAboutNewParts(temp); } //--------------------------------------------------------- // StretchList operations //--------------------------------------------------------- void Song::stretchModifyOperation( StretchList* stretch_list, StretchListItem::StretchEventType type, double value, PendingOperationList& ops) const { ops.add(PendingOperationItem(type, stretch_list, value, PendingOperationItem::ModifyStretchListRatio)); } void Song::stretchListAddOperation( StretchList* stretch_list, StretchListItem::StretchEventType type, MuseFrame_t frame, double value, PendingOperationList& ops) const { iStretchListItem ie = stretch_list->find(frame); if(ie != stretch_list->end()) ops.add(PendingOperationItem(type, stretch_list, ie, frame, value, PendingOperationItem::ModifyStretchListRatioAt)); else ops.add(PendingOperationItem(type, stretch_list, frame, value, PendingOperationItem::AddStretchListRatioAt)); } void Song::stretchListDelOperation( StretchList* stretch_list, int types, MuseFrame_t frame, PendingOperationList& ops) const { // Do not delete the item at zeroth frame. if(frame == 0) return; iStretchListItem e = stretch_list->find(frame); if (e == stretch_list->end()) { ERROR_TIMESTRETCH(stderr, "Song::stretchListDelOperation frame:%ld not found\n", frame); return; } PendingOperationItem poi(types, stretch_list, e, PendingOperationItem::DeleteStretchListRatioAt); // NOTE: Deletion is done in post-RT stage 3. ops.add(poi); } void Song::stretchListModifyOperation( StretchList* stretch_list, StretchListItem::StretchEventType type, MuseFrame_t frame, double value, PendingOperationList& ops) const { iStretchListItem ie = stretch_list->find(frame); if(ie == stretch_list->end()) { ERROR_TIMESTRETCH(stderr, "Song::stretchListModifyOperation frame:%ld not found\n", frame); return; } ops.add(PendingOperationItem(type, stretch_list, ie, frame, value, PendingOperationItem::ModifyStretchListRatioAt)); } void Song::setAudioConvertersOfflineOperation( bool isOffline ) { WaveTrackList* wtl = waves(); if(wtl->empty()) return; PendingOperationList ops; AudioConverterSettingsGroup* settings; bool isLocalSettings; bool doStretch; bool doResample; AudioConverterSettings::ModeType cur_mode; AudioConverterPluginI* cur_converter; AudioConverterPluginI* converter; const WaveTrack* wt; const PartList* pl; const Part* pt; ciPart pl_end; ciEvent el_end; SndFileR sndfile; ciWaveTrack wtl_end = wtl->cend(); for(ciWaveTrack iwt = wtl->cbegin(); iwt != wtl_end; ++iwt) { wt = *iwt; pl = wt->cparts(); pl_end = pl->cend(); for(ciPart ip = pl->cbegin(); ip != pl_end; ++ip) { pt = ip->second; const EventList& el = pt->events(); el_end = el.end(); for(ciEvent ie = el.cbegin(); ie != el_end; ++ie) { const Event& e = ie->second; sndfile = e.sndFile(); //if(sndfile.isNull()) // continue; // Check if we are to use any converters at all. if(!sndfile.useConverter()) continue; // Check if the current realtime converter is already in offline mode. cur_converter = sndfile.staticAudioConverter(AudioConverterSettings::RealtimeMode); if(cur_converter) { cur_mode = cur_converter->mode(); if((isOffline && cur_mode == AudioConverterSettings::OfflineMode) || (!isOffline && cur_mode == AudioConverterSettings::RealtimeMode)) continue; } settings = sndfile.audioConverterSettings()->useSettings() ? sndfile.audioConverterSettings() : MusEGlobal::defaultAudioConverterSettings; isLocalSettings = sndfile.audioConverterSettings()->useSettings(); doStretch = sndfile.isStretched(); doResample = sndfile.isResampled(); // For offline mode, we COULD create a third converter just for it, apart from the main // and UI converters. But our system doesn't have a third converter (yet) - and it may // or may not get one, we'll see. Still, the operation supports setting it, in case. // So instead, in offline mode we switch out the main converter for one with with offline settings. converter = sndfile.setupAudioConverter( settings, MusEGlobal::defaultAudioConverterSettings, isLocalSettings, isOffline ? AudioConverterSettings::OfflineMode : AudioConverterSettings::RealtimeMode, doResample, doStretch); // No point if there's already no converter. if(!converter && !cur_converter) continue; // REMOVE Tim. samplerate. Added. Diagnostics. fprintf(stderr, "Song::setAudioConvertersOfflineOperation Setting sndfile:%s to isOffline:%d\n", sndfile.name().toLocal8Bit().constData(), isOffline); ops.add(PendingOperationItem( sndfile, converter, // Main converter. PendingOperationItem::SetAudioConverterOfflineMode)); } } } MusEGlobal::audio->msgExecutePendingOperations(ops, true); } void Song::modifyAudioConverterSettingsOperation( SndFileR sndfile, AudioConverterSettingsGroup* settings, AudioConverterSettingsGroup* defaultSettings, bool isLocalSettings, PendingOperationList& ops ) const { if(!sndfile.useConverter()) return; const bool isOffline = sndfile.isOffline(); const bool doStretch = sndfile.isStretched(); const bool doResample = sndfile.isResampled(); // For offline mode, we COULD create a third converter just for it, apart from the main // and UI converters. But our system doesn't have a third converter (yet) - and it may // or may not get one, we'll see. Still, the operation supports setting it, in case. // So instead, in offline mode we switch out the main converter for one with with offline settings. AudioConverterPluginI* converter = sndfile.setupAudioConverter( settings, defaultSettings, isLocalSettings, isOffline ? AudioConverterSettings::OfflineMode : AudioConverterSettings::RealtimeMode, doResample, doStretch); AudioConverterPluginI* converterUI = sndfile.setupAudioConverter( settings, defaultSettings, isLocalSettings, AudioConverterSettings::GuiMode, doResample, doStretch); // if(!converter && !converterUI) // return; // We want to change the settings, and the converters if necessary... ops.add(PendingOperationItem( sndfile, settings, PendingOperationItem::ModifyLocalAudioConverterSettings)); ops.add(PendingOperationItem( sndfile, converter, // Main converter. converterUI, // UI converter. PendingOperationItem::ModifyLocalAudioConverter)); } void Song::modifyAudioConverterOperation( SndFileR sndfile, PendingOperationList& ops, bool doResample, bool doStretch) const { if(!sndfile.useConverter()) return; const bool isOffline = sndfile.isOffline(); AudioConverterSettingsGroup* settings = sndfile.audioConverterSettings()->useSettings() ? sndfile.audioConverterSettings() : MusEGlobal::defaultAudioConverterSettings; const bool isLocalSettings = sndfile.audioConverterSettings()->useSettings(); // For offline mode, we COULD create a third converter just for it, apart from the main // and UI converters. But our system doesn't have a third converter (yet) - and it may // or may not get one, we'll see. Still, the operation supports setting it, in case. // So instead, in offline mode we switch out the main converter for one with with offline settings. AudioConverterPluginI* converter = sndfile.setupAudioConverter( settings, MusEGlobal::defaultAudioConverterSettings, isLocalSettings, isOffline ? AudioConverterSettings::OfflineMode : AudioConverterSettings::RealtimeMode, doResample, doStretch); AudioConverterPluginI* converterUI = sndfile.setupAudioConverter( settings, MusEGlobal::defaultAudioConverterSettings, isLocalSettings, AudioConverterSettings::GuiMode, doResample, doStretch); // if(!converter && !converterUI) // return; ops.add(PendingOperationItem( sndfile, converter, // Main converter. converterUI, // UI converter. PendingOperationItem::ModifyLocalAudioConverter)); } void Song::modifyStretchListOperation(SndFileR sndfile, int type, double value, PendingOperationList& ops) const { if(!sndfile.useConverter()) return; ops.add(PendingOperationItem(type, sndfile.stretchList(), value, PendingOperationItem::ModifyStretchListRatio)); } void Song::addAtStretchListOperation(SndFileR sndfile, int type, MuseFrame_t frame, double value, PendingOperationList& ops) const { if(!sndfile.useConverter()) return; StretchList* sl = sndfile.stretchList(); stretchListAddOperation(sl, StretchListItem::StretchEventType(type), frame, value, ops); bool wantStretch = false; bool wantResample = sndfile.sampleRateDiffers(); bool wantPitch = false; const bool haveStretch = sndfile.isStretched(); const bool haveResample = sndfile.isResampled() || wantResample; const bool havePitch = sndfile.isPitchShifted(); //// If the requested value is anything other than 1.0, request converters. //if(value != 1.0) //{ switch(type) { case StretchListItem::StretchEvent: wantStretch = true; break; case StretchListItem::SamplerateEvent: wantResample = true; break; case StretchListItem::PitchEvent: wantPitch = true; break; } //} if((wantStretch && !haveStretch) || (wantResample && !haveResample) || (wantPitch && !havePitch)) { const bool doStretch = wantStretch ? true : haveStretch; const bool doResample = wantResample ? true : haveResample; //const bool doPitch = wantPitch ? true : havePitch; modifyAudioConverterOperation(sndfile, ops, doResample, doStretch); } } void Song::delAtStretchListOperation(SndFileR sndfile, int types, MuseFrame_t frame, PendingOperationList& ops) const { if(!sndfile.useConverter()) return; // Do not delete the item at zeroth frame. if(frame == 0) return; StretchList* sl = sndfile.stretchList(); stretchListDelOperation(sl, types, frame, ops); StretchListInfo info = sl->testDelListOperation(types, frame); const bool srdiffers = sndfile.sampleRateDiffers(); const bool wantStretch = info._isStretched; const bool wantResample = info._isResampled || srdiffers; const bool wantPitch = info._isPitchShifted; const bool haveStretch = sndfile.isStretched(); const bool haveResample = sndfile.isResampled() || srdiffers; const bool havePitch = sndfile.isPitchShifted(); if((!wantStretch && haveStretch) || (!wantResample && haveResample) || (!wantPitch && havePitch)) { const bool doStretch = !wantStretch ? false : haveStretch; const bool doResample = !wantResample ? false : haveResample; //const bool doPitch = !wantPitch ? false : havePitch; modifyAudioConverterOperation(sndfile, ops, doResample, doStretch); } } void Song::modifyAtStretchListOperation(SndFileR sndfile, int type, MuseFrame_t frame, double value, PendingOperationList& ops) const { if(!sndfile.useConverter()) return; StretchList* sl = sndfile.stretchList(); stretchListModifyOperation(sl, StretchListItem::StretchEventType(type), frame, value, ops); bool wantStretch = false; bool wantResample = sndfile.sampleRateDiffers(); bool wantPitch = false; const bool haveStretch = sndfile.isStretched(); const bool haveResample = sndfile.isResampled() || wantResample; const bool havePitch = sndfile.isPitchShifted(); //// If the requested value is anything other than 1.0, request converters. //if(value != 1.0) //{ switch(type) { case StretchListItem::StretchEvent: wantStretch = true; break; case StretchListItem::SamplerateEvent: wantResample = true; break; case StretchListItem::PitchEvent: wantPitch = true; break; } //} if((wantStretch && !haveStretch) || (wantResample && !haveResample) || (wantPitch && !havePitch)) { const bool doStretch = wantStretch ? true : haveStretch; const bool doResample = wantResample ? true : haveResample; //const bool doPitch = wantPitch ? true : havePitch; modifyAudioConverterOperation(sndfile, ops, doResample, doStretch); } } void Song::modifyDefaultAudioConverterSettingsOperation(AudioConverterSettingsGroup* settings, PendingOperationList& ops) { // First, schedule the change to the actual default settings pointer variable. ops.add(PendingOperationItem(settings, PendingOperationItem::ModifyDefaultAudioConverterSettings)); // Now, schedule changes to each wave event if necessary. // Note that at this point the above default change has not occurred yet, // so we must tell it to use what the settings WILL BE, not what they are now. for(ciWaveTrack it = MusEGlobal::song->waves()->cbegin(); it != MusEGlobal::song->waves()->cend(); ++it) { const WaveTrack* wtrack = *it; for(ciPart ip = wtrack->cparts()->cbegin(); ip != wtrack->cparts()->cend(); ++ip) { const Part* part = ip->second; for(ciEvent ie = part->events().cbegin(); ie != part->events().cend(); ++ie) { const Event& e = ie->second; if(e.type() != Wave) continue; SndFileR sndfile = e.sndFile(); if(!sndfile.useConverter()) continue; const AudioConverterSettingsGroup* cur_ev_settings = sndfile.audioConverterSettings(); // Is the event using its own local settings? Ignore. if(!cur_ev_settings || cur_ev_settings->useSettings()) continue; const bool isOffline = sndfile.isOffline(); const bool doStretch = sndfile.isStretched(); const bool doResample = sndfile.isResampled(); // For offline mode, we COULD create a third converter just for it, apart from the main // and UI converters. But our system doesn't have a third converter (yet) - and it may // or may not get one, we'll see. Still, the operation supports setting it, in case. // So instead, in offline mode we switch out the main converter for one with with offline settings. AudioConverterPluginI* converter = sndfile.setupAudioConverter( settings, settings, false, // false = Default, non-local settings. isOffline ? AudioConverterSettings::OfflineMode : AudioConverterSettings::RealtimeMode, doResample, doStretch); AudioConverterPluginI* converterUI = sndfile.setupAudioConverter( settings, settings, false, // false = Default, non-local settings. AudioConverterSettings::GuiMode, doResample, doStretch); // if(!converter && !converterUI) // return; // We only want to change the converters, if necessary. ops.add(PendingOperationItem( sndfile, converter, // Main converter. converterUI, // UI converter. PendingOperationItem::ModifyLocalAudioConverter)); } } } } //--------------------------------------------------------- // updateTransportPos // called from GUI context // SPECIAL for tempo or master changes: In stop mode we want // the transport to locate to the correct frame. In play mode // we simply let the transport progress naturally but we 'fake' // a new representation of transport position (_pos) in Audio::reSyncAudio() // as part of the realtime part of the tempo change operation. ////// By now, our audio transport position (_pos) has the new tick and frame. // We need to seek AFTER any song changed slots are called in case widgets // are removed etc. etc. before the posChanged signal is emitted when setPos() // is called from the seek recognition. //--------------------------------------------------------- void Song::updateTransportPos(const SongChangedStruct_t& flags) { if(!MusEGlobal::audio->isPlaying() && (flags & (SC_TEMPO | SC_MASTER))) { //const MusECore::Pos ap = MusEGlobal::audio->tickAndFramePos(); // Don't touch Audio::_pos, make a copy. //const MusECore::Pos ap = MusEGlobal::audio->pos(); // Don't touch Audio::_pos, make a copy or take the tick. // Note that this is only tick accurate, not frame accurate, // ie. it can only recalculate a new frame from the given tick. const MusECore::Pos p(MusEGlobal::audio->tickPos()); // // Don't touch the original, make a copy. // const MusECore::Pos p(cPos()); MusEGlobal::audioDevice->seekTransport(p.frame()); } } //--------------------------------------------------------- // adjustMarkerListOperation // Items between startPos and startPos + diff are removed. // Items after startPos + diff are adjusted 'diff' number of ticks. //--------------------------------------------------------- bool Song::adjustMarkerListOperation(MarkerList* markerlist, unsigned int startPos, int diff, PendingOperationList& ops) { if(!markerlist || markerlist->empty() || diff == 0) return false; MarkerList* new_markerlist = new MarkerList(); for(ciMarker i = markerlist->begin(); i != markerlist->end(); ++i) { const Marker& m = i->second; unsigned int tick = m.tick(); if(tick >= startPos) { if(tick >= startPos + diff) { // Grab a copy but with a new ID. Marker newMarker = m.copy(); newMarker.setTick(tick - diff); new_markerlist->add(newMarker); } } else { // Grab a copy but with a new ID. new_markerlist->add(m.copy()); } } PendingOperationItem poi(markerlist, new_markerlist, PendingOperationItem::ModifyMarkerList); ops.add(poi); return true; } //--------------------------------------------------------- // processTrackAutomationEvents //--------------------------------------------------------- void Song::processTrackAutomationEvents(AudioTrack *atrack, Undo* operations) { const AutomationType atype = atrack->automationType(); if(atype != AUTO_TOUCH && atype != AUTO_WRITE) return; // Use either the supplied operations list or a local one. Undo ops; Undo& opsr = operations ? (*operations) : ops; CtrlListList *cll = atrack->controller(); CtrlRecList *crl = atrack->recEvents(); for(ciCtrlList icl = cll->cbegin(); icl != cll->cend(); ++icl) { CtrlList* cl = icl->second; CtrlList& clr = *icl->second; int id = cl->id(); // Were there any recorded events for this controller? bool do_it = false; for(ciCtrlRec icr = crl->cbegin(); icr != crl->cend(); ++icr) { if(icr->id == id) { do_it = true; break; } } if(!do_it) continue; // The Undo system will take 'ownership' of these and delete them at the appropriate time. CtrlList* erased_list_items = new CtrlList(clr, CtrlList::ASSIGN_PROPERTIES); CtrlList* added_list_items = new CtrlList(clr, CtrlList::ASSIGN_PROPERTIES); // Remove old events from record region. if(atype == AUTO_WRITE) { int start = MusEGlobal::audio->getStartRecordPos().frame(); int end = MusEGlobal::audio->getEndRecordPos().frame(); iCtrl s = cl->lower_bound(start); iCtrl e = cl->lower_bound(end); erased_list_items->insert(s, e); } else { // type AUTO_TOUCH for(ciCtrlRec icr = crl->cbegin(); icr != crl->cend(); ++icr) { // Don't bother looking for start, it's OK, just take the first one. // Needed for mousewheel and paging etc. if(icr->id != id) continue; int start = icr->frame; if(icr == crl->cend()) { int end = MusEGlobal::audio->getEndRecordPos().frame(); iCtrl s = cl->lower_bound(start); iCtrl e = cl->lower_bound(end); erased_list_items->insert(s, e); break; } ciCtrlRec icrlast = icr; ++icr; for(; ; ++icr) { if(icr == crl->cend()) { int end = icrlast->frame; iCtrl s = cl->lower_bound(start); iCtrl e = cl->lower_bound(end); erased_list_items->insert(s, e); break; } if(icr->id == id && icr->type == ARVT_STOP) { int end = icr->frame; iCtrl s = cl->lower_bound(start); iCtrl e = cl->lower_bound(end); erased_list_items->insert(s, e); break; } if(icr->id == id) icrlast = icr; } if(icr == crl->end()) break; } } // Extract all recorded events for controller "id" // from CtrlRecList and put into new_list. for(ciCtrlRec icr = crl->cbegin(); icr !=crl->cend(); ++icr) { if(icr->id == id) { // Must optimize these types otherwise multiple vertices appear on flat straight lines in the graphs. CtrlValueType vtype = cl->valueType(); if(!cl->empty() && (cl->mode() == CtrlList::DISCRETE || vtype == VAL_BOOL || vtype == VAL_INT)) { iCtrl icl_prev = cl->lower_bound(icr->frame); if(icl_prev != cl->begin()) --icl_prev; if(icl_prev->second.val == icr->val) continue; } // Now add the value. added_list_items->add(icr->frame, icr->val); } } if(erased_list_items->empty() && added_list_items->empty()) { delete erased_list_items; delete added_list_items; } else opsr.push_back(UndoOp(UndoOp::ModifyAudioCtrlValList, cll, erased_list_items, added_list_items)); } // Done with the recorded automation event list. Clear it. crl->clear(); if(!operations) MusEGlobal::song->applyOperationGroup(ops); } void Song::setRecMode(int val) { _recMode = val; emit recModeChanged(val); } void Song::setCycleMode(int val) { _cycleMode = val; emit cycleModeChanged(val); } } // namespace MusECore muse-4.0.0/src/muse/song.h000066400000000000000000000610701404105311000153340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: song.h,v 1.35.2.25 2009/12/15 03:39:58 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SONG_H__ #define __SONG_H__ #include #include #include #include #include #include #include "type_defs.h" #include "pos.h" #include "globaldefs.h" #include "tempo.h" #include "sig.h" #include "synth.h" #include "operations.h" #include "lock_free_buffer.h" #include "mpevent.h" #include "wave.h" #define IPC_EVENT_FIFO_SIZE ( std::min( std::max(size_t(256), size_t(MusEGlobal::segmentSize * 16)), size_t(16384)) ) #include "time_stretch.h" #include "audio_convert/audio_converter_settings_group.h" // Forward declarations: class QAction; class QMenu; namespace MusECore { class Event; class Xml; class Track; class Part; class PartList; class EventList; class MarkerList; class Marker; class Route; struct AudioMsg; class MidiPart; class Undo; struct UndoOp; class UndoList; #define REC_NOTE_FIFO_SIZE 16 //--------------------------------------------------------- // Song //--------------------------------------------------------- class Song : public QObject { Q_OBJECT public: enum POSTYPE { CPOS = 0, LPOS, RPOS }; enum FollowMode { NO, JUMP, CONTINUOUS }; enum { REC_OVERDUP, REC_REPLACE }; enum { CYCLE_NORMAL, CYCLE_MIX, CYCLE_REPLACE }; enum { MARKER_CUR }; enum OperationType { // Execute the operation only, the operation is not un-doable. No song update. OperationExecute, // Execute the operation only, the operation is not un-doable. Song is updated. OperationExecuteUpdate, // Execute the operation, the operation is un-doable, // and do not 'start' and 'end' the undo mode. No song update. OperationUndoable, // Execute the operation, the operation is un-doable, // and do not 'start' and 'end' the undo mode. Song is updated. OperationUndoableUpdate, // Execute the operation, the operation is un-doable, // and 'start' and 'end' the undo mode. Song is updated. OperationUndoMode }; private: // fifo for note-on events // - this events are read by the heart beat interrupt // - used for single step recording in midi editors int recNoteFifo[REC_NOTE_FIFO_SIZE]; volatile int noteFifoSize; int noteFifoWindex; int noteFifoRindex; volatile char rcCC = -1; // CC remote control TempoFifo _tempoFifo; // External tempo changes, processed in heartbeat. MusECore::SongChangedStruct_t updateFlags; TrackList _tracks; // tracklist as seen by arranger MidiTrackList _midis; WaveTrackList _waves; InputList _inputs; // audio input ports OutputList _outputs; // audio output ports GroupList _groups; // mixer groups AuxList _auxs; // aux sends SynthIList _synthIs; UndoList* undoList; UndoList* redoList; // New items created in GUI thread awaiting addition in audio thread. PendingOperationList pendingOperations; Pos pos[3]; Pos _vcpos; // virtual CPOS (locate in progress) MarkerList* _markerList; float _fCpuLoad; float _fDspLoad; long _xRunsCount; // Receives events from any threads. For now, specifically for creating new // controllers in the gui thread and adding them safely to the controller lists. LockFreeMPSCRingBuffer *_ipcInEventBuffers; LockFreeMPSCRingBuffer *_ipcOutEventBuffers; bool loopFlag; bool punchinFlag; bool punchoutFlag; bool recordFlag; bool soloFlag; int _recMode; int _cycleMode; Pos _startPlayPosition; bool _click; bool _quantize; unsigned _len; // song len in ticks FollowMode _follow; int _globalPitchShift; void readMarker(Xml&); QString songInfoStr; // contains user supplied song information, stored in song file. bool showSongInfo; // Private: Update the audio device's real transport position after a tempo or master change for ex. void updateTransportPos(const SongChangedStruct_t& flags); // These are called from non-RT thread operations execution stage 1. void insertTrackOperation(Track* track, int idx, PendingOperationList& ops); bool adjustMarkerListOperation(MarkerList* markerlist, unsigned int startPos, int diff, PendingOperationList& ops); void removeTrackOperation(Track* track, PendingOperationList& ops); bool addEventOperation(const Event&, Part*, bool do_port_ctrls = true, bool do_clone_port_ctrls = true); // Special: Returns the real actual event to be deleted found in the event lists. // This way even a modified event can be passed in, and as long as // the ID AND position values match it will find and return the ORIGINAL event. // Useful for example for passing a pre-modified event to a DeleteEvent or ModyfyEvent operation // in the Undo system, and it will automatically replace the Undo item's event with // the real one returned here. (Otherwise when the user hits 'undo' it would restore // that modified passed-in event sitting in the Undo item. That's not the right event!) Event changeEventOperation(const Event& oldEvent, const Event& newEvent, Part*, bool do_port_ctrls = true, bool do_clone_port_ctrls = true); Event deleteEventOperation(const Event&, Part*, bool do_port_ctrls = true, bool do_clone_port_ctrls = true); void checkSongSampleRate(); void normalizePart(MusECore::Part *part); // Fills operations if given, otherwise creates and executes its own operations list. void processTrackAutomationEvents(AudioTrack *atrack, Undo* operations = 0); public: Song(const char* name = 0); ~Song(); /** It is not allowed nor checked(!) to AddPart a clone, and * to AddEvent/DeleteEvent/ModifyEvent/SelectEvent events which * would need to be replicated to the newly added clone part! */ // group may be changed! prepareOperationGroup is called on group! // Sender can be set to the caller object and used by it // to ignore self-generated songChanged signals. // The songChanged structure will contain this pointer. bool applyOperationGroup(Undo& group, OperationType type = OperationUndoMode, void* sender = 0); bool applyOperation(const UndoOp& op, OperationType type = OperationUndoMode, void* sender = 0); /** this sends emits a signal to each MidiEditor or whoever is interested. * For each part which is 1) opened in this MidiEditor and 2) which is * a key in this map, the Editors shall no more edit this part, but instead * all parts in the_map[old_part] (which is a std::set) */ void informAboutNewParts(const std::map< const Part*, std::set >&); /** this sends emits a signal to each MidiEditor or whoever is interested. * For each part which is 1) opened in this MidiEditor and 2) which is * a key in this map, the Editors shall no more edit this part, but instead * all parts in the_map[old_part] (which is a std::set) * this is a special case of the general function, which only replaces one part * by up to nine different. */ void informAboutNewParts(const Part* orig, const Part* p1, const Part* p2=NULL, const Part* p3=NULL, const Part* p4=NULL, const Part* p5=NULL, const Part* p6=NULL, const Part* p7=NULL, const Part* p8=NULL, const Part* p9=NULL); void putEvent(int pv); void putEventCC(char cc); void endMsgCmd(); void processMsg(AudioMsg* msg); void setFollow(FollowMode m) { _follow = m; } FollowMode follow() const { return _follow; } bool dirty; WaveTrack* bounceTrack; AudioOutput* bounceOutput; void updatePos(); void read(Xml&, bool isTemplate=false); void write(int, Xml&) const; // void writeFont(int level, Xml& xml, const char* name, // const QFont& font) const; // QFont readFont(Xml& xml, const char* name); // After a song file is successfully loaded with read(), some items which // reference other items in the file need to be resolved. (Mixer strip configs etc.) void resolveSongfileReferences(); QString getSongInfo() { return songInfoStr; } void setSongInfo(QString info, bool show) { songInfoStr = info; showSongInfo = show; } bool showSongInfoOnStartup() { return showSongInfo; } // If clear_all is false, it will not touch things like midi ports. void clear(bool signal, bool clear_all = true); void cleanupForQuit(); int globalPitchShift() const { return _globalPitchShift; } void setGlobalPitchShift(int val) { _globalPitchShift = val; } // REMOVE Tim. samplerate. Added. TODO #if 0 // Set the project's sample rate. This can be different than the current actual sample rate. void setProjectSampleRate(int rate); // Ratio of the project's sample rate to the current audio sample rate. double projectSampleRateRatio() const; // Whether the project's sample rate ratio is exactly 1. bool projectSampleRateDiffers() const; // This scales the sample rate, by scaling the frame values of all objects or properties that use or store // a value in 'frames', such as wave parts/events position and length, and audio automation graphs. // It does NOT resample audio files, that is another function. // Caution: Slight rounding errors can degrade timing accuracy, especially if repeated scalings are done. void convertProjectSampleRate(int newRate); #endif //----------------------------------------- // Marker //----------------------------------------- MarkerList* marker() const { return _markerList; } // For wholesale swapping of a new list. Takes ownership of the given list. void setMarkerList(MarkerList* ml) { _markerList = ml; } void addMarker(const QString& s, unsigned t, bool lck); void addMarker(const QString& s, const Pos& p); iMarker getMarkerAt(unsigned t); void removeMarker(const Marker&); void setMarkerName(const Marker&, const QString&); void setMarkerPos(const Marker&, const Pos& pos); void setMarkerLock(const Marker&, bool); //----------------------------------------- // transport //----------------------------------------- void setPos(POSTYPE posType, const Pos&, bool sig = true, bool isSeek = true, bool adjustScrollbar = false, bool force = false); const Pos& cPos() const { return pos[0]; } const Pos& lPos() const { return pos[1]; } const Pos& rPos() const { return pos[2]; } unsigned cpos() const { return pos[0].tick(); } unsigned vcpos() const { return _vcpos.tick(); } const Pos& vcPos() const { return _vcpos; } unsigned lpos() const { return pos[1].tick(); } unsigned rpos() const { return pos[2].tick(); } bool loop() const { return loopFlag; } bool record() const { return recordFlag; } bool punchin() const { return punchinFlag; } bool punchout() const { return punchoutFlag; } void setRecMode(int val); int recMode() const { return _recMode; } void setCycleMode(int val); int cycleMode() const { return _cycleMode; } bool click() const { return _click; } bool quantize() const { return _quantize; } void setStopPlay(bool); // Fills operations if given, otherwise creates and executes its own operations list. void stopRolling(Undo* operations = 0); void abortRolling(); float cpuLoad() const { return _fCpuLoad; } float dspLoad() const { return _fDspLoad; } long xRunsCount() const { return _xRunsCount; } //----------------------------------------- // access tempomap/MusEGlobal::sigmap (Mastertrack) //----------------------------------------- unsigned len() const { return _len; } void setLen(unsigned l, bool do_update = true); // set songlen in ticks int roundUpBar(int tick) const; int roundUpBeat(int tick) const; int roundDownBar(int tick) const; void initLen(); //----------------------------------------- // event manipulations //----------------------------------------- void cmdAddRecordedWave(WaveTrack* track, Pos s, Pos e, Undo& operations); void cmdAddRecordedEvents(MidiTrack*, const EventList&, unsigned, Undo& operations); // May be called from GUI or audio thread. Also selects events in clone parts. Safe for now because audio/midi processing doesn't // depend on it, and all calls to part altering functions from GUI are synchronized with (wait for) audio thread. void selectEvent(Event&, Part*, bool select); void selectAllEvents(Part*, bool select); // See selectEvent(). void cmdChangeWave(const Event& original, const QString& tmpfile, unsigned sx, unsigned ex); void remapPortDrumCtrlEvents(int mapidx, int newnote, int newchan, int newport); // called from GUI thread // Adds or removes midi event controller cache values. // Called from GUI thread // add true: add events. false: remove events // drum_tracks: Include drum tracks. midi_tracks: Include midi tracks. // drum_ctls: Do drum (per-note) controller events. non_drum_ctls: Do non-drum controller events. void changeMidiCtrlCacheEvents( bool add, bool drum_tracks = true, bool midi_tracks = true, bool drum_ctls = true, bool non_drum_ctls = true); void addExternalTempo(const TempoRecEvent& e) { _tempoFifo.put(e); } //-------------------------------------------- // Time-stretch / samplerate manipulations //-------------------------------------------- void stretchModifyOperation( StretchList* stretch_list, StretchListItem::StretchEventType type, double value, PendingOperationList& ops) const; void stretchListAddOperation( StretchList* stretch_list, StretchListItem::StretchEventType type, MuseFrame_t frame, double value, PendingOperationList& ops) const; void stretchListDelOperation( StretchList* stretch_list, int types, MuseFrame_t frame, PendingOperationList& ops) const; void stretchListModifyOperation( StretchList* stretch_list, StretchListItem::StretchEventType type, MuseFrame_t frame, double value, PendingOperationList& ops) const; void setAudioConvertersOfflineOperation( bool isOffline ); void modifyAudioConverterSettingsOperation( SndFileR sndfile, AudioConverterSettingsGroup* settings, AudioConverterSettingsGroup* defaultSettings, bool isLocalSettings, PendingOperationList& ops ) const; void modifyAudioConverterOperation( SndFileR sndfile, PendingOperationList& ops, bool doResample, bool doStretch ) const; void modifyStretchListOperation( SndFileR sndfile, int type, double value, PendingOperationList& ops) const; void addAtStretchListOperation( SndFileR sndfile, int type, MuseFrame_t frame, double value, PendingOperationList& ops) const; void delAtStretchListOperation( SndFileR sndfile, int types, MuseFrame_t frame, PendingOperationList& ops) const; void modifyAtStretchListOperation( SndFileR sndfile, int type, MuseFrame_t frame, double value, PendingOperationList& ops) const; void modifyDefaultAudioConverterSettingsOperation( AudioConverterSettingsGroup* settings, PendingOperationList& ops); //----------------------------------------- // part manipulations //----------------------------------------- void addPart(Part* part); void removePart(Part* part); void changePart(Part*, Part*); void normalizeWaveParts(Part *partCursor = NULL); //----------------------------------------- // track manipulations //----------------------------------------- TrackList* tracks() { return &_tracks; } MidiTrackList* midis() { return &_midis; } WaveTrackList* waves() { return &_waves; } InputList* inputs() { return &_inputs; } OutputList* outputs() { return &_outputs; } GroupList* groups() { return &_groups; } AuxList* auxs() { return &_auxs; } SynthIList* syntis() { return &_synthIs; } Track* findTrack(const Part* part) const; Track* findTrack(const QString& name) const; bool trackExists(Track* t) const { return _tracks.find(t) != _tracks.cend(); } void setRecordFlag(Track*, bool val, Undo* operations = 0); // This is a non- realtime safe operation mainly used when loading files, while the engine is paused or idle. void insertTrack0(Track*, int idx); // The currently selected track (in a multi-selection the last one selected), or null. Track* selectedTrack() const { return _tracks.currentSelection(); } // Total number of selected tracks. int countSelectedTracks() const { return _tracks.countSelected(); } // Selects or deselects all tracks. void selectAllTracks(bool select) { _tracks.selectAll(select); if(!select) // Not essential, but if unselecting ALL tracks, clear the static counter. Track::clearSelectionOrderCounter(); } void readRoute(Xml& xml); void recordEvent(MidiTrack*, Event&); // Enable all track and plugin controllers, and synth controllers if applicable, which are NOT in AUTO_WRITE mode. void reenableTouchedControllers(); void clearRecAutomation(bool clearList); // Fills operations if given, otherwise creates and executes its own operations list. void processAutomationEvents(Undo* operations = 0); void processMasterRec(); int execAutomationCtlPopup(AudioTrack*, const QPoint&, int); int execMidiAutomationCtlPopup(MidiTrack*, MidiPart*, const QPoint&, int); bool connectJackRoutes(const MusECore::Route& src, const MusECore::Route& dst, bool disconnect = false); void connectAudioPorts(); void connectMidiPorts(); void connectAllPorts() { connectAudioPorts(); connectMidiPorts(); } void updateSoloStates(); // Put an event into the IPC event ring buffer for the gui thread to process. Returns true on success. // NOTE: Although the ring buffer is multi-writer, call this from audio thread only for now, unless // you know what you are doing because the thread needs to ask whether the controller exists before // calling, and that may not be safe from threads other than gui or audio. bool putIpcInEvent(const MidiPlayEvent& ev); // Put an event into the IPC event ring buffer for the audio thread to process. // Called by gui thread only. Returns true on success. bool putIpcOutEvent(const MidiPlayEvent& ev); // Process any special IPC audio thread - to - gui thread messages. // Called by gui thread only. Returns true on success. bool processIpcInEventBuffers(); // Process any special gui thread - to - IPC audio thread messages. // Called by audio thread only. Returns true on success. bool processIpcOutEventBuffers(); //----------------------------------------- // undo, redo, operation groups //----------------------------------------- // Sender can be set to the caller object and used by it // to ignore self-generated songChanged signals. // The songChanged structure will contain this pointer. void startUndo(void* sender = 0); void endUndo(MusECore::SongChangedStruct_t); void executeOperationGroup1(Undo& operations); void executeOperationGroup2(Undo& operations); void executeOperationGroup3(Undo& operations); void revertOperationGroup1(Undo& operations); void revertOperationGroup2(Undo& operations); void revertOperationGroup3(Undo& operations); void addUndo(UndoOp i); void setUndoRedoText(); //----------------------------------------- // Configuration //----------------------------------------- SynthI* createSynthI(const QString& sclass, const QString& uri, const QString& label = QString(), Synth::Type type = Synth::SYNTH_TYPE_END, Track* insertAt = 0); //----------------------------------------- // Debug //----------------------------------------- void dumpMaster(); void addUpdateFlags(MusECore::SongChangedStruct_t f) { updateFlags |= f; } //----------------------------------------- // Python bridge related //----------------------------------------- #ifdef PYTHON_SUPPORT virtual bool event (QEvent* e ); #endif public slots: void seekTo(int tick); // use allowRecursion with care! this could lock up muse if you // aren't sure that your recursion will be finite! void update(SongChangedStruct_t flags = SongChangedStruct_t(SC_EVERYTHING), bool allowRecursion=false); void beat(); void undo(); void redo(); void setTempo(int t); void setSig(int a, int b); void setSig(const MusECore::TimeSignature&); void setTempo(double tempo) { setTempo(int(60000000.0/tempo)); } void setMasterFlag(bool flag); bool getLoop() { return loopFlag; } void setLoop(bool f); void setRecord(bool f, bool autoRecEnable = true); void clearTrackRec(); void setPlay(bool f); void setStop(bool); void forward(); void rewindStart(); void rewind(); void setPunchin(bool f); void setPunchout(bool f); void setClick(bool val); void setQuantize(bool val); void panic(); void seqSignal(int fd); // Creates a track but does not add it to the track list, the caller must do that. // The track name is not set and must be set by the caller. // If setDefaults is true, adds default in/out routes/channels to the track. Track* createTrack(Track::TrackType type, bool setDefaults = true); Track* addTrack(Track::TrackType type, Track* insertAt = 0); Track* addNewTrack(QAction* action, Track* insertAt = 0); void duplicateTracks(Track* t = nullptr); void setDirty() { emit sigDirty(); } /* restarts recording from last start position * if discard is true (default) then * recording will start on existing tracks, * else new copies of armed tracks will be created * and current armed tracks will be muted and unarmed * Called from gui thread only. */ void restartRecording(bool discard = true); signals: void songChanged(MusECore::SongChangedStruct_t); void posChanged(int, unsigned, bool); void posChanged(int, const MusECore::Pos, bool); void loopChanged(bool); void recordChanged(bool); void playChanged(bool); void punchinChanged(bool); void punchoutChanged(bool); void clickChanged(bool); void quantizeChanged(bool); void markerChanged(int); void midiNote(int pitch, int velo); void controllerChanged(MusECore::Track*, int); void newPartsCreated(const std::map< const MusECore::Part*, std::set >&); void sigDirty(); void recModeChanged(int); void cycleModeChanged(int); }; } // namespace MusECore namespace MusEGlobal { extern MusECore::Song* song; } #endif muse-4.0.0/src/muse/songfile.cpp000066400000000000000000001431571404105311000165360ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: songfile.cpp,v 1.25.2.12 2009/11/04 15:06:07 spamatica Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include "app.h" #include "song.h" #include "arranger.h" #include "arrangerview.h" #include "cobject.h" #include "drumedit.h" #include "pianoroll.h" #include "scoreedit.h" #include "globals.h" #include "xml.h" #include "drummap.h" #include "drum_ordering.h" #include "event.h" #include "marker/marker.h" #include "midiport.h" #include "audio.h" #include "mitplugin.h" #include "wave.h" #include "midictrl.h" #include "audiodev.h" #include "conf.h" #include "keyevent.h" #include "gconfig.h" #include "config.h" namespace MusEGlobal { MusECore::CloneList cloneList; } namespace MusECore { //--------------------------------------------------------- // NKey::write //--------------------------------------------------------- void NKey::write(int level, Xml& xml) const { xml.intTag(level, "key", val); } //--------------------------------------------------------- // NKey::read //--------------------------------------------------------- void NKey::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::Text: val = xml.s1().toInt(); break; case Xml::TagEnd: if (xml.s1() == "key") return; default: break; } } } //--------------------------------------------------------- // Scale::write //--------------------------------------------------------- void Scale::write(int level, Xml& xml) const { xml.intTag(level, "scale", val); } //--------------------------------------------------------- // Scale::read //--------------------------------------------------------- void Scale::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::Text: val = xml.s1().toInt(); break; case Xml::TagEnd: if (xml.s1() == "scale") return; default: break; } } } //--------------------------------------------------------- // Part::readFromXml //--------------------------------------------------------- Part* Part::readFromXml(Xml& xml, Track* track, bool doClone, bool toTrack) { int id = -1; Part* npart = nullptr; QUuid uuid; bool uuidvalid = false; bool clone = true; bool wave = false; bool isclone = false; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return npart; case Xml::TagStart: if(!npart) // If the part has not been created yet... { if(id != -1) // If an id was found... { for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { if(i->is_deleted) // Is the clone item marked as deleted? Ignore it. continue; if(i->id == id) // Is a matching part found in the clone list? { // Create a clone. It must still be added later in a operationgroup npart = track->newPart((Part*)i->cp, true); break; } } } else if(uuidvalid) // If a uuid was found... { for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { if(uuid == i->_uuid) // Is a matching part found in the clone list? { Track* cpt = i->cp->track(); if(toTrack) // If we want to paste to the given track... { // If the given track type is not the same as the part's // original track type, we can't continue. Just return. if(!track || cpt->type() != track->type()) { xml.skip("part"); return nullptr; } } else // ...else we want to paste to the part's original track. { // Make sure the track exists (has not been deleted). if((cpt->isMidiTrack() && MusEGlobal::song->midis()->find(cpt) != MusEGlobal::song->midis()->end()) || (cpt->type() == Track::WAVE && MusEGlobal::song->waves()->find(cpt) != MusEGlobal::song->waves()->end())) track = cpt; else // Track was not found. Try pasting to the given track, as above... { if(!track || cpt->type() != track->type()) { // No luck. Just return. xml.skip("part"); return nullptr; } } } if(i->is_deleted) // Is the clone item marked as deleted? Don't create a clone, create a copy. break; // If it's a regular paste (not paste clone), and the original part is // not a clone, defer so that a new copy is created in TagStart above. if(!doClone && !isclone) break; // Create a clone. It must still be added later in a operationgroup npart = track->newPart((Part*)i->cp, true); break; } } } if(!npart) // If the part still has not been created yet... { if(!track) // A clone was not created from any matching { // part. Create a non-clone part now. xml.skip("part"); return nullptr; } // If we're pasting to selected track and the 'wave' // variable is valid, check for mismatch... if(toTrack && uuidvalid) { // If both the part and track are not midi or wave... if((wave && track->isMidiTrack()) || (!wave && track->type() == Track::WAVE)) { xml.skip("part"); return nullptr; } } if (track->isMidiTrack()) npart = new MidiPart((MidiTrack*)track); else if (track->type() == Track::WAVE) npart = new MusECore::WavePart((MusECore::WaveTrack*)track); else { xml.skip("part"); return nullptr; } // Signify a new non-clone part was created. // Even if the original part was itself a clone, clear this because the // attribute section did not create a clone from any matching part. clone = false; // If an id or uuid was found, add the part to the clone list // so that subsequent parts can look it up and clone from it... if(id != -1) { ClonePart ncp(npart, id); MusEGlobal::cloneList.push_back(ncp); } else if(uuidvalid) { ClonePart ncp(npart); // New ClonePart creates its own uuid, but we need to replace it. ncp._uuid = uuid; // OK for non-windows? MusEGlobal::cloneList.push_back(ncp); } } } if (tag == "name") npart->setName(xml.parse1()); else if (tag == "viewState") { npart->viewState().read(xml); } else if (tag == "poslen") { ((PosLen*)npart)->read(xml, "poslen"); } else if (tag == "pos") { Pos pos; pos.read(xml, "pos"); // obsolete npart->setTick(pos.tick()); } else if (tag == "len") { Pos len; len.read(xml, "len"); // obsolete npart->setLenTick(len.tick()); } else if (tag == "selected") npart->setSelected(xml.parseInt()); else if (tag == "color") npart->setColorIndex(xml.parseInt()); else if (tag == "mute") npart->setMute(xml.parseInt()); else if (tag == "event") { // If a new non-clone part was created, accept the events... if(!clone) { EventType type = Wave; if(track->isMidiTrack()) type = Note; Event e(type); e.read(xml); // stored pos for event has absolute value. However internally // pos is relative to start of part, we substract part pos. // In case the event and part pos types differ, the event dominates. e.setPosValue(e.posValue() - npart->posValue(e.pos().type())); #ifdef ALLOW_LEFT_HIDDEN_EVENTS npart->addEvent(e); #else const int posval = e.posValue(); if(posval < 0) { printf("readClone: warning: event at posval:%d not in part:%s, discarded\n", posval, npart->name().toLatin1().constData()); } #endif } else // ...Otherwise a clone was created, so we don't need the events. xml.skip(tag); } else xml.unknown("readXmlPart"); break; case Xml::Attribut: if (tag == "type") { if(xml.s2() == "wave") wave = true; } else if (tag == "cloneId") { id = xml.s2().toInt(); } else if (tag == "uuid") { uuid = QUuid(xml.s2()); if(!uuid.isNull()) { uuidvalid = true; } } else if(tag == "isclone") isclone = xml.s2().toInt(); break; case Xml::TagEnd: if (tag == "part") return npart; default: break; } } return npart; } //--------------------------------------------------------- // Part::write // If isCopy is true, write the xml differently so that // we can have 'Paste Clone' feature. //--------------------------------------------------------- void Part::write(int level, Xml& xml, bool isCopy, bool forceWavePaths) const { int id = -1; QUuid uuid; bool dumpEvents = true; bool wave = _track->type() == Track::WAVE; // NOTE ::write() should never be called on a deleted part, so no checking here for cloneList items marked as deleted. // Checking is awkward anyway and doesn't fit well here. if(isCopy) { for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { if(i->cp->isCloneOf(this)) { uuid = i->_uuid; dumpEvents = false; break; } } if(uuid.isNull()) { ClonePart cp(this); uuid = cp._uuid; MusEGlobal::cloneList.push_back(cp); } } else { if (this->hasClones()) { for (iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i) { if (i->cp->isCloneOf(this)) { id = i->id; dumpEvents = false; break; } } if (id == -1) { id = MusEGlobal::cloneList.size(); ClonePart cp(this, id); MusEGlobal::cloneList.push_back(cp); } } } // Special markers if this is a copy operation and the // part is a clone. if(isCopy) { if(wave) xml.nput(level, ""); level++; } else if (id != -1) { xml.tag(level++, "part cloneId=\"%d\"", id); } else xml.tag(level++, "part"); xml.strTag(level, "name", _name); // This won't bother writing if the state is invalid. viewState().write(level, xml); PosLen::write(level, xml, "poslen"); xml.intTag(level, "selected", _selected); xml.intTag(level, "color", _colorIndex); if (_mute) xml.intTag(level, "mute", _mute); if (dumpEvents) { for (ciEvent e = events().begin(); e != events().end(); ++e) e->second.write(level, xml, *this, forceWavePaths); } xml.etag(level, "part"); } //--------------------------------------------------------- // writeFont //--------------------------------------------------------- //void Song::writeFont(int level, Xml& xml, const char* name, // const QFont& font) const // { // xml.nput(level, "<%s family=\"%s\" size=\"%d\"", // name, Xml::xmlString(font.family()).toLatin1().constData(), font.pointSize()); // if (font.weight() != QFont::Normal) // xml.nput(" weight=\"%d\"", font.weight()); // if (font.italic()) // xml.nput(" italic=\"1\""); // xml.nput(" />\n"); // } //--------------------------------------------------------- // readFont //--------------------------------------------------------- //QFont Song::readFont(Xml& xml, const char* name) // { // QFont f; // for (;;) { // Xml::Token token = xml.parse(); // switch (token) { // case Xml::Error: // case Xml::End: // return f; // case Xml::TagStart: // xml.unknown("readFont"); // break; // case Xml::Attribut: // if (xml.s1() == "family") // f.setFamily(xml.s2()); // else if (xml.s1() == "size") // f.setPointSize(xml.s2().toInt()); // else if (xml.s1() == "weight") // f.setWeight(xml.s2().toInt()); // else if (xml.s1() == "italic") // f.setItalic(xml.s2().toInt()); // break; // case Xml::TagEnd: // if (xml.s1() == name) // return f; // default: // break; // } // } // return f; // } //--------------------------------------------------------- // readMarker //--------------------------------------------------------- void Song::readMarker(Xml& xml) { Marker m; m.read(xml); _markerList->add(m); } //--------------------------------------------------------- // checkSongSampleRate // Called by gui thread. //--------------------------------------------------------- void Song::checkSongSampleRate() { std::map waveRates; for(ciWaveTrack iwt = waves()->begin(); iwt != waves()->end(); ++iwt) { WaveTrack* wt = *iwt; for(ciPart ipt = wt->parts()->begin(); ipt != wt->parts()->end(); ++ipt) { Part* pt = ipt->second; for(ciEvent ie = pt->events().begin(); ie != pt->events().end(); ++ie) { const Event e(ie->second); if(e.sndFile().isOpen()) { const int sr = e.sndFile().samplerate(); std::map::iterator iwr = waveRates.find(sr); if(iwr == waveRates.end()) { waveRates.insert(std::pair(sr, 1)); } else { ++iwr->second; } } } } } for(std::map::const_iterator iwr = waveRates.cbegin(); iwr != waveRates.cend(); ++iwr) { } } //--------------------------------------------------------- // read //--------------------------------------------------------- void Song::read(Xml& xml, bool /*isTemplate*/) { MusEGlobal::cloneList.clear(); for (;;) { if (MusEGlobal::muse->progress) { MusEGlobal::muse->progress->setValue(MusEGlobal::muse->progress->value()+1); } Xml::Token token; token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto song_read_end; case Xml::TagStart: if (tag == "master") { // Avoid emitting songChanged. // Tick parameter is not used. MusEGlobal::tempomap.setMasterFlag(0, xml.parseInt()); } else if (tag == "info") songInfoStr = xml.parse1(); else if (tag == "showinfo") showSongInfo = xml.parseInt(); else if (tag == "loop") setLoop(xml.parseInt()); else if (tag == "punchin") setPunchin(xml.parseInt()); else if (tag == "punchout") setPunchout(xml.parseInt()); else if (tag == "record") // This doesn't work as there are no tracks yet at this point and this is checked // in setRecord. So better make it clear and explicit. // (Using the default autoRecEnable==true would seem wrong too at this point.) // setRecord(xml.parseInt()); setRecord(false); else if (tag == "solo") soloFlag = xml.parseInt(); else if (tag == "type") // Obsolete. xml.parseInt(); else if (tag == "recmode") _recMode = xml.parseInt(); else if (tag == "cycle") _cycleMode = xml.parseInt(); else if (tag == "click") setClick(xml.parseInt()); else if (tag == "quantize") _quantize = xml.parseInt(); else if (tag == "len") _len = xml.parseInt(); else if (tag == "follow") _follow = FollowMode(xml.parseInt()); else if (tag == "midiDivision") { // TODO: Compare with current global setting and convert the // song if required - similar to how the song vs. global // sample rate ratio is handled. Ignore for now. xml.parseInt(); } else if (tag == "sampleRate") { // Ignore. Sample rate setting is handled by the // song discovery mechanism (in MusE::loadProjectFile1()). xml.parseInt(); } else if (tag == "tempolist") { MusEGlobal::tempomap.read(xml); } else if (tag == "siglist") MusEGlobal::sigmap.read(xml); else if (tag == "keylist") { MusEGlobal::keymap.read(xml); } else if (tag == "miditrack") { MidiTrack* track = new MidiTrack(); track->read(xml); insertTrack0(track, -1); } else if (tag == "drumtrack") { // Old drumtrack is obsolete. MidiTrack* track = new MidiTrack(); track->setType(Track::DRUM); track->read(xml); track->convertToType(Track::DRUM); // Convert the notes and controllers. insertTrack0(track, -1); } else if (tag == "newdrumtrack") { MidiTrack* track = new MidiTrack(); track->setType(Track::DRUM); track->read(xml); insertTrack0(track, -1); } else if (tag == "wavetrack") { MusECore::WaveTrack* track = new MusECore::WaveTrack(); track->read(xml); insertTrack0(track,-1); // Now that the track has been added to the lists in insertTrack2(), // OSC can find the track and its plugins, and start their native guis if required... track->showPendingPluginNativeGuis(); } else if (tag == "AudioInput") { AudioInput* track = new AudioInput(); track->read(xml); insertTrack0(track,-1); track->showPendingPluginNativeGuis(); } else if (tag == "AudioOutput") { AudioOutput* track = new AudioOutput(); track->read(xml); insertTrack0(track,-1); track->showPendingPluginNativeGuis(); } else if (tag == "AudioGroup") { AudioGroup* track = new AudioGroup(); track->read(xml); insertTrack0(track,-1); track->showPendingPluginNativeGuis(); } else if (tag == "AudioAux") { AudioAux* track = new AudioAux(); track->read(xml); insertTrack0(track,-1); track->showPendingPluginNativeGuis(); } else if (tag == "SynthI") { SynthI* track = new SynthI(); track->read(xml); // Done in SynthI::read() // insertTrack(track,-1); //track->showPendingPluginNativeGuis(); } else if (tag == "Route") { readRoute(xml); } else if (tag == "marker") readMarker(xml); else if (tag == "globalPitchShift") _globalPitchShift = xml.parseInt(); // REMOVE Tim. automation. Removed. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. else if (tag == "automation") // MusEGlobal::automation = xml.parseInt(); xml.parseInt(); else if (tag == "cpos") { int pos = xml.parseInt(); Pos p(pos, true); setPos(Song::CPOS, p, false, false, false); } else if (tag == "lpos") { int pos = xml.parseInt(); Pos p(pos, true); setPos(Song::LPOS, p, false, false, false); } else if (tag == "rpos") { int pos = xml.parseInt(); Pos p(pos, true); setPos(Song::RPOS, p, false, false, false); } else if (tag == "drummap") readDrumMap(xml, false); else if (tag == "drum_ordering") MusEGlobal::global_drum_ordering.read(xml); else xml.unknown("Song"); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "song") { goto song_read_end; } default: break; } } song_read_end: dirty = false; // Since cloneList is also used for copy/paste operations, // clear the copy clone list again. MusEGlobal::cloneList.clear(); } //--------------------------------------------------------- // write //--------------------------------------------------------- void Song::write(int level, Xml& xml) const { xml.tag(level++, "song"); xml.strTag(level, "info", songInfoStr); xml.intTag(level, "showinfo", showSongInfo); // REMOVE Tim. automation. Removed. // Deprecated. MusEGlobal::automation is now fixed TRUE // for now until we decide what to do with it. // xml.intTag(level, "automation", MusEGlobal::automation); xml.intTag(level, "cpos", MusEGlobal::song->cpos()); xml.intTag(level, "rpos", MusEGlobal::song->rpos()); xml.intTag(level, "lpos", MusEGlobal::song->lpos()); xml.intTag(level, "master", MusEGlobal::tempomap.masterFlag()); xml.intTag(level, "loop", loopFlag); xml.intTag(level, "punchin", punchinFlag); xml.intTag(level, "punchout", punchoutFlag); xml.intTag(level, "record", recordFlag); xml.intTag(level, "solo", soloFlag); xml.intTag(level, "recmode", _recMode); xml.intTag(level, "cycle", _cycleMode); xml.intTag(level, "click", _click); xml.intTag(level, "quantize", _quantize); xml.intTag(level, "len", _len); xml.intTag(level, "follow", _follow); // Save the current global midi division as well as current sample rate // so the song can be scaled properly if the values differ on reload. xml.intTag(level, "midiDivision", MusEGlobal::config.division); xml.intTag(level, "sampleRate", MusEGlobal::sampleRate); if (_globalPitchShift) xml.intTag(level, "globalPitchShift", _globalPitchShift); // Make a backup of the current clone list, to retain any 'copy' items, // so that pasting works properly after. CloneList copyCloneList = MusEGlobal::cloneList; MusEGlobal::cloneList.clear(); // write tracks for (ciTrack i = _tracks.begin(); i != _tracks.end(); ++i) (*i)->write(level, xml); // write routing for (ciTrack i = _tracks.begin(); i != _tracks.end(); ++i) (*i)->writeRouting(level, xml); // Write midi device routing. for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) (*i)->writeRouting(level, xml); // Write midi port routing. for (int i = 0; i < MusECore::MIDI_PORTS; ++i) MusEGlobal::midiPorts[i].writeRouting(level, xml); MusEGlobal::tempomap.write(level, xml); MusEGlobal::sigmap.write(level, xml); MusEGlobal::keymap.write(level, xml); _markerList->write(level, xml); writeDrumMap(level, xml, false); MusEGlobal::global_drum_ordering.write(level, xml); xml.tag(level, "/song"); // Restore backup of the clone list, to retain any 'copy' items, // so that pasting works properly after. MusEGlobal::cloneList.clear(); MusEGlobal::cloneList = copyCloneList; } //-------------------------------- // resolveInstrumentReferences //-------------------------------- static void resolveInstrumentReferences() { for(int i = 0; i < MIDI_PORTS; ++i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; const QString& name = mp->tmpInstrRef(); const int idx = mp->tmpTrackRef(); if(idx >= 0) { Track* track = MusEGlobal::song->tracks()->index(idx); if(track && track->isSynthTrack()) { SynthI* si = static_cast(track); mp->changeInstrument(si); } } else if(!name.isEmpty()) { mp->changeInstrument(registerMidiInstrument(name)); } // Done with temporary file references. Clear them. mp->clearTmpFileRefs(); } } //-------------------------------- // resolveStripReferences //-------------------------------- static void resolveStripReferences(MusEGlobal::MixerConfig* mconfig) { MusECore::TrackList* tl = MusEGlobal::song->tracks(); MusECore::Track* track; MusEGlobal::StripConfigList_t& scl = mconfig->stripConfigList; if(!scl.empty()) { for(MusEGlobal::iStripConfigList isc = scl.begin(); isc != scl.end(); ) { MusEGlobal::StripConfig& sc = *isc; // Is it a dud? Delete it. if(sc.isNull() && sc._tmpFileIdx < 0) { isc = scl.erase(isc); continue; } // Does it have a temporary track index? if(sc._tmpFileIdx >= 0) { // Find an existing track at that index. track = tl->index(sc._tmpFileIdx); // No corresponding track found? Delete the strip config. if(!track) { isc = scl.erase(isc); continue; } // Link the strip config to the track, via serial number. sc._serial = track->serial(); // Done with temporary index. Reset it. sc._tmpFileIdx = -1; } // As for other configs with a serial and no _tmpFileIdx, // leave them alone. They may already be valid. If not, // they may need to persist for the undo system to restore from. // Ultimately when saving, the strip config write() function removes duds anyway. ++isc; } } } //-------------------------------- // resolveSongfileReferences //-------------------------------- void Song::resolveSongfileReferences() { //----------------------------------------------- // Resolve instrument references: //----------------------------------------------- resolveInstrumentReferences(); //----------------------------------------------- // Resolve mixer strip configuration references: //----------------------------------------------- resolveStripReferences(&MusEGlobal::config.mixer1); resolveStripReferences(&MusEGlobal::config.mixer2); } // REMOVE Tim. samplerate. Added. TODO #if 0 //--------------------------------------------------------- // convertProjectSampleRate //--------------------------------------------------------- void Song::convertProjectSampleRate(int newRate) { double sratio = double(newRate) / double(MusEGlobal::sampleRate); for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i) { Track* track = *i; //if(track->isMidiTrack) // continue; PartList* parts = track->parts(); for(iPart ip = parts->begin(); i != parts->end(); ++i) { Part* part = *ip; EventList& el = part->nonconst_events(); for(iEvent ie = el.begin(); ie != el.end(); ++ie) { Event& e = *ie; } if(part->type() == Part::FRAMES) { part->setFrame(double(part->frame()) * sratio); part->setLenFrame(double(part->lenFrame()) * sratio); } } if(track->type == Track::WAVE) { } } } #endif } // namespace MusECore namespace MusEGui { //--------------------------------------------------------- // readPart //--------------------------------------------------------- MusECore::Part* MusE::readPart(MusECore::Xml& xml) { MusECore::Part* part = nullptr; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return part; case MusECore::Xml::Text: { int trackIdx, partIdx; sscanf(tag.toLatin1().constData(), "%d:%d", &trackIdx, &partIdx); MusECore::Track* track = nullptr; //check if track index is in bounds before getting it (danvd) if(trackIdx < (int)MusEGlobal::song->tracks()->size()) { track = MusEGlobal::song->tracks()->index(trackIdx); } if (track) part = track->parts()->find(partIdx); } break; case MusECore::Xml::TagStart: xml.unknown("readPart"); break; case MusECore::Xml::TagEnd: if (tag == "part") return part; default: break; } } } //--------------------------------------------------------- // readToplevels //--------------------------------------------------------- void MusE::readToplevels(MusECore::Xml& xml) { MusECore::PartList* pl = new MusECore::PartList; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: delete pl; return; case MusECore::Xml::TagStart: if (tag == "part") { MusECore::Part* part = readPart(xml); if (part) pl->add(part); } else if (tag == "pianoroll") { // p3.3.34 // Do not open if there are no parts. // Had bogus '-1' part index for list edit in med file, // causing list edit to segfault on song load. // Somehow that -1 was put there on write, because the // current part didn't exist anymore, so no index number // could be found for it on write. Watching... may be fixed. // But for now be safe for all the top levels... if(!pl->empty()) { startPianoroll(pl); toplevels.back()->readStatus(xml); pl = new MusECore::PartList; } } else if (tag == "scoreedit") { MusEGui::ScoreEdit* score = new MusEGui::ScoreEdit(this, 0, _arranger->cursorValue()); toplevels.push_back(score); connect(score, SIGNAL(isDeleting(MusEGui::TopWin*)), SLOT(toplevelDeleting(MusEGui::TopWin*))); connect(score, SIGNAL(name_changed()), arrangerView, SLOT(scoreNamingChanged())); score->show(); score->readStatus(xml); } else if (tag == "drumedit") { if(!pl->empty()) { startDrumEditor(pl); toplevels.back()->readStatus(xml); pl = new MusECore::PartList; } } else if (tag == "master") { startMasterEditor(); toplevels.back()->readStatus(xml); } else if (tag == "arrangerview") { TopWin* tw = toplevels.findType(TopWin::ARRANGER); tw->readStatus(xml); tw->showMaximized(); } else if (tag == "waveedit") { if(!pl->empty()) { startWaveEditor(pl); toplevels.back()->readStatus(xml); pl = new MusECore::PartList; } } else xml.unknown("MusE"); break; case MusECore::Xml::Attribut: break; case MusECore::Xml::TagEnd: if (tag == "toplevels") { delete pl; return; } default: break; } } } //--------------------------------------------------------- // read // read song //--------------------------------------------------------- void MusE::read(MusECore::Xml& xml, bool doReadMidiPorts, bool isTemplate) { bool skipmode = true; writeTopwinState=true; for (;;) { if (progress) progress->setValue(progress->value()+1); MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (skipmode && tag == "muse") skipmode = false; else if (skipmode) break; else if (tag == "configuration") readConfiguration(xml, doReadMidiPorts, false /* do NOT read global settings, see below */); /* Explanation for why "do NOT read global settings": * if you would use true here, then muse would overwrite certain global config stuff * by the settings stored in the song. but you don't want this. imagine that you * send a friend a .med file. your friend opens it and baaam, his configuration is * garbled. why? well, because these readConfigurations here would have overwritten * parts (but not all) of his global config (like MDI/SDI, toolbar states etc.) * with the data stored in the song. (it IS stored there. dunny why, i find it pretty * senseless.) * * If you've a problem which seems to be solved by replacing "false" with "true", i've * a better solution for you: go into conf.cpp, in void readConfiguration(Xml& xml, bool readOnlySequencer, bool doReadGlobalConfig) * (around line 525), look for a comment like this: * "Global and/or per-song config stuff ends here" (alternatively just search for * "----"). Your problem is probably that some non-global setting should be loaded but * is not. Fix it by either placing the else if (foo)... clause responsible for that * setting to be loaded into the first part, that is, before "else if (!doReadGlobalConfig)" * or (if the settings actually IS global and not per-song), ensure that the routine * which writes the global (and not the song-)configuration really writes that setting. * (it might happen that it formerly worked because it was written to the song instead * of the global config by mistake, and now isn't loaded anymore. write it to the * correct location.) * * -- flo93 */ else if (tag == "song") { MusEGlobal::song->read(xml, isTemplate); // Now that the song file has been fully loaded, resolve any references in the file. MusEGlobal::song->resolveSongfileReferences(); // Now that all track and instrument references have been resolved, // it is safe to add all the midi controller cache values. MusEGlobal::song->changeMidiCtrlCacheEvents(true, true, true, true, true); MusEGlobal::audio->msgUpdateSoloStates(); // Inform the rest of the app that the song (may) have changed, using these flags. // After this function is called, the caller can do a general Song::update() MINUS these flags, // like in MusE::loadProjectFile1() - the only place calling so far, as of this writing. // Some existing windows need this, like arranger, some don't which are dynamically created after this. MusEGlobal::song->update(SC_TRACK_INSERTED); } else if (tag == "toplevels") readToplevels(xml); else if (tag == "no_toplevels") { if (!isTemplate) writeTopwinState=false; xml.skip("no_toplevels"); } else xml.unknown("muse"); break; case MusECore::Xml::Attribut: if (tag == "version") { int major = xml.s2().section('.', 0, 0).toInt(); int minor = xml.s2().section('.', 1, 1).toInt(); xml.setVersion(major, minor); } break; case MusECore::Xml::TagEnd: if(!xml.isVersionEqualToLatest()) { fprintf(stderr, "\n***WARNING***\nLoaded file version is %d.%d\nCurrent version is %d.%d\n" "Conversions may be applied if file is saved!\n\n", xml.majorVersion(), xml.minorVersion(), xml.latestMajorVersion(), xml.latestMinorVersion()); // Cannot construct QWidgets until QApplication created! // Check MusEGlobal::muse which is created shortly after the application... if(MusEGlobal::muse && MusEGlobal::config.warnOnFileVersions) { QString txt = tr("File version is %1.%2\nCurrent version is %3.%4\n" "Conversions may be applied if file is saved!") .arg(xml.majorVersion()).arg(xml.minorVersion()) .arg(xml.latestMajorVersion()).arg(xml.latestMinorVersion()); QMessageBox* mb = new QMessageBox(QMessageBox::Warning, tr("Opening file"), txt, QMessageBox::Ok, MusEGlobal::muse); QCheckBox* cb = new QCheckBox(tr("Do not warn again")); cb->setChecked(!MusEGlobal::config.warnOnFileVersions); mb->setCheckBox(cb); mb->exec(); if(!mb->checkBox()->isChecked() != MusEGlobal::config.warnOnFileVersions) { MusEGlobal::config.warnOnFileVersions = !mb->checkBox()->isChecked(); // Save settings. Use simple version - do NOT set style or stylesheet, this has nothing to do with that. //MusEGlobal::muse->changeConfig(true); // Save settings? No, wait till close. } delete mb; } } if (!skipmode && tag == "muse") return; default: break; } } } //--------------------------------------------------------- // write // write song //--------------------------------------------------------- void MusE::write(MusECore::Xml& xml, bool writeTopwins) const { xml.header(); int level = 0; xml.nput(level++, "\n", xml.latestMajorVersion(), xml.latestMinorVersion()); writeConfiguration(level, xml); writeStatusMidiInputTransformPlugins(level, xml); MusEGlobal::song->write(level, xml); if (writeTopwins && !toplevels.empty()) { xml.tag(level++, "toplevels"); for (const auto& i : toplevels) { if (i->isVisible()) i->writeStatus(level, xml); } xml.tag(level--, "/toplevels"); } else if (!writeTopwins) { xml.tag(level, "no_toplevels"); xml.etag(level, "no_toplevels"); } xml.tag(level, "/muse"); } } // namespace MusEGui muse-4.0.0/src/muse/songfile_discovery.cpp000066400000000000000000000231131404105311000206120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // songfile_discovery.cpp // Copyright (C) 2019 Tim E. Real (terminator356 at users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "songfile_discovery.h" #include #include namespace MusECore { SongfileDiscoveryWaveItem::SongfileDiscoveryWaveItem(const QString& filename) : _filename(filename) { _valid = false; if(!_filename.isEmpty() && QFile::exists(_filename)) { _sfinfo.format = 0; SNDFILE* sf = sf_open(_filename.toLocal8Bit().constData(), SFM_READ, &_sfinfo); _valid = sf != NULL; if(sf) sf_close(sf); } } //--------------------------------------------------------- // readWaveEvent //--------------------------------------------------------- void SongfileDiscovery::readWaveEvent(Xml& xml) { QString filename; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: case Xml::Attribut: return; case Xml::TagStart: if (tag == "file") { filename = xml.parse1(); } else // Same as unknown() without the error message. xml.parse1(); break; case Xml::TagEnd: if (tag == "event") { if(!filename.isEmpty()) { QString name = filename; if (QFileInfo(name).isRelative()) { name = _projectPath + QString("/") + name; } else { if (!QFile::exists(name)) { if (QFile::exists(_projectPath + QString("/") + name)) { name = _projectPath + QString("/") + name; } } } SongfileDiscoveryWaveItem item(name); if(item._valid) { _waveList.push_back(item); SongSampleratesInsRes_t res = _waveList._samplerates.insert(SongSampleratesIns_t(item._sfinfo.samplerate, 0)); ++res.first->second; } } return; } default: break; } } } //--------------------------------------------------------- // readWavePart //--------------------------------------------------------- void SongfileDiscovery::readWavePart(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "event") { readWaveEvent(xml); } else // Same as unknown() without the error message. xml.parse1(); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "part") return; default: break; } } return; } //--------------------------------------------------------- // readWaveTrack //--------------------------------------------------------- void SongfileDiscovery::readWaveTrack(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "part") { readWavePart(xml); } else // Same as unknown() without the error message. xml.parse1(); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "wavetrack") { return; } default: break; } } } //--------------------------------------------------------- // readSong //--------------------------------------------------------- void SongfileDiscovery::readSong(Xml& xml) { for (;;) { Xml::Token token; token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "sampleRate") _waveList.setProjectSampleRate(xml.parseInt()); else if (tag == "wavetrack") { readWaveTrack(xml); } else // Same as unknown() without the error message. xml.parse1(); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "song") { return; } default: break; } } } //--------------------------------------------------------- // readSongfile //--------------------------------------------------------- void SongfileDiscovery::readSongfile(Xml& xml) { // Start with resetting. _waveList._projectSampleRate = 0; _waveList._projectSampleRateValid = false; bool skipmode = true; for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::Error: case MusECore::Xml::End: return; case MusECore::Xml::TagStart: if (skipmode && tag == "muse") skipmode = false; else if (skipmode) break; else if (tag == "song") { readSong(xml); } else // Same as unknown() without the error message. xml.parse1(); break; case MusECore::Xml::Attribut: if (tag == "version") { int major = xml.s2().section('.', 0, 0).toInt(); int minor = xml.s2().section('.', 1, 1).toInt(); xml.setVersion(major, minor); } break; case MusECore::Xml::TagEnd: if(!xml.isVersionEqualToLatest()) { fprintf(stderr, "Songfile discovery: Loaded file version is %d.%d\nCurrent version is %d.%d\n", xml.majorVersion(), xml.minorVersion(), xml.latestMajorVersion(), xml.latestMinorVersion()); } if (!skipmode && tag == "muse") return; default: break; } } } int SongfileDiscoveryWaveList::getMostCommonSamplerate() const { int rate = 0; int count = 0; for(SongSamplerates_t::const_iterator it = _samplerates.cbegin(); it != _samplerates.cend(); ++it) { const int& c = it->second; if(c > count) { count = c; rate = it->first; } } return rate; } } // namespace MusECore muse-4.0.0/src/muse/songfile_discovery.h000066400000000000000000000045241404105311000202640ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // songfile_discovery.h // Copyright (C) 2019 Tim E. Real (terminator356 at users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SONGFILE_DISCOVERY_H__ #define __SONGFILE_DISCOVERY_H__ #include #include #include #include "sndfile.h" #include "xml.h" namespace MusECore { struct SongfileDiscoveryWaveItem { QString _filename; SF_INFO _sfinfo; bool _valid; SongfileDiscoveryWaveItem(const QString& filename); }; typedef std::map SongSamplerates_t; typedef std::pair SongSampleratesIns_t; typedef std::pair SongSampleratesInsRes_t; typedef std::pair SongSampleratesRange_t; class SongfileDiscoveryWaveList : public std::list { public: int _projectSampleRate; bool _projectSampleRateValid; SongSamplerates_t _samplerates; void setProjectSampleRate(int r) { _projectSampleRate = r; _projectSampleRateValid = true; } int getMostCommonSamplerate() const; }; class SongfileDiscovery { private: QString _projectPath; public: SongfileDiscovery(const QString& projectPath) : _projectPath(projectPath) {} SongfileDiscoveryWaveList _waveList; void readWaveEvent(Xml& xml); void readWavePart(Xml& xml); void readWaveTrack(Xml&); void readSong(Xml&); void readSongfile(Xml& xml); }; } // namespace MusECore #endif muse-4.0.0/src/muse/steprec.cpp000066400000000000000000000133711404105311000163670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // steprec.cpp // (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "steprec.h" #include "part.h" #include "event.h" #include "globals.h" #include "functions.h" #include "song.h" #include "audio.h" #include #define CHORD_TIMEOUT 75 namespace MusECore { StepRec::StepRec(bool* note_held_down_array) { note_held_down=note_held_down_array; chord_timer=new QTimer(this); chord_timer->setSingleShot(true); chord_timer->setInterval(CHORD_TIMEOUT); chord_timer->stop(); connect(chord_timer, SIGNAL(timeout()), SLOT(timeout())); } void StepRec::timeout() { if (chord_timer_set_to_tick != MusEGlobal::song->cpos()) { Pos p(chord_timer_set_to_tick, true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, false, true); } } void StepRec::record(const Part* part, int pitch, int len, int step, int velo, bool ctrl, bool shift, int incoming_pitch) { unsigned tick = MusEGlobal::song->cpos(); unsigned lasttick=0; Undo operations; if (tick < part->tick()) //insert note before the part to insert? { if (MusEGlobal::debugMsg) printf("StepRec::record(): tick (%i) is before part (begin tick is %i), ignoring...\n",tick, part->tick()); return; } // if incoming_pitch wasn't specified, set it to pitch if (incoming_pitch == 1337) incoming_pitch=pitch; if (incoming_pitch != MusEGlobal::rcSteprecNote) { chord_timer->stop(); // extend len of last note? const EventList& events = part->events(); if (ctrl) { for (ciEvent i = events.begin(); i != events.end(); ++i) { const Event& ev = i->second; if (ev.isNote() && ev.pitch() == pitch && ((ev.tick() + ev.lenTick() + part->tick()) == tick)) { Event e = ev.clone(); e.setLenTick(ev.lenTick() + len); operations.push_back(UndoOp(UndoOp::ModifyEvent, e,ev, part, false, false)); if (!shift) { chord_timer_set_to_tick = tick + step; chord_timer->start(); } lasttick=tick+len - part->tick(); goto steprec_record_foot; } } } if (tick<=part->endTick()) { // if we already entered the note, delete it // if we would find a note after part->lenTick(), the above "if" // avoids this. this has to be avoided because then part->hasHiddenEvents() is true // which results in forbidding any action beyond its end cEventRange range = events.equal_range(tick - part->tick()); for (ciEvent i = range.first; i != range.second; ++i) { const Event& ev = i->second; if (ev.isNote() && ev.pitch() == pitch) { MusEGlobal::song->applyOperation(UndoOp(UndoOp::DeleteEvent,ev, part, true,true)); if (!shift) { chord_timer_set_to_tick = tick + step; chord_timer->start(); } return; } } } Event e(Note); e.setTick(tick - part->tick()); e.setPitch(pitch); e.setVelo(velo); e.setLenTick(len); operations.push_back(UndoOp(UndoOp::AddEvent, e, part, false, false)); lasttick=e.endTick(); if (! (MusEGlobal::globalKeyState & Qt::ShiftModifier)) { chord_timer_set_to_tick = tick + step; chord_timer->start(); } goto steprec_record_foot; // this is actually unnecessary, but for clarity } else // equals if (incoming_pitch==MusEGlobal::rcSteprecNote) { bool held_notes=false; if (note_held_down != nullptr) { for (int i=0;i<128;i++) if (note_held_down[i]) { held_notes=true; break; } } else held_notes=false; if (held_notes) { chord_timer->stop(); // extend len of last note(s) using std::set; set extend_set; const EventList& events = part->events(); for (ciEvent i = events.begin(); i != events.end(); ++i) { const Event& ev = i->second; if (ev.isNote() && note_held_down[ev.pitch()] && ((ev.tick() + ev.lenTick() + part->tick()) == tick)) extend_set.insert(&ev); } for (set::iterator it=extend_set.begin(); it!=extend_set.end(); it++) { const Event& ev=**it; Event e = ev.clone(); e.setLenTick(ev.lenTick() + len); operations.push_back(UndoOp(UndoOp::ModifyEvent,e, ev, part, false, false)); } if (!shift) { chord_timer_set_to_tick = tick + step; chord_timer->start(); } lasttick=tick+len - part->tick(); goto steprec_record_foot; // this is actually unnecessary, but for clarity } else // equals if (!held_notes) { chord_timer->stop(); // simply proceed, inserting a rest Pos p(MusEGlobal::song->cpos() + step, true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, false, true); return; } } steprec_record_foot: if (!((lasttick > part->lenTick()) && (part->hasHiddenEvents() & Part::RightEventsHidden))) // allowed? { if (lasttick > part->lenTick()) // we have to expand the part? schedule_resize_all_same_len_clone_parts(part, lasttick, operations); MusEGlobal::song->applyOperationGroup(operations); } } } // namespace MusECore muse-4.0.0/src/muse/steprec.h000066400000000000000000000027341404105311000160350ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // steprec.h // (C) Copyright 2011 Florian Jung (flo93@users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __STEPREC_H__ #define __STEPREC_H__ #include #include #include "part.h" namespace MusECore { class StepRec : public QObject { Q_OBJECT public: StepRec(bool* note_held_down_array); void record(const Part* part, int recorded_pitch, int len, int step, int velo=80, bool ctrl=false, bool shift=false, int incoming_pitch=1337); private slots: void timeout(); private: QTimer* chord_timer; unsigned int chord_timer_set_to_tick; bool* note_held_down; }; } // namespace MusECore #endif muse-4.0.0/src/muse/stringparam.cpp000066400000000000000000000065301404105311000172500ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // $Id: stringparam.cpp,v 1.0.0.0 2010/04/24 01:01:01 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // String parameter module added by Tim. // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "stringparam.h" #include "xml.h" namespace MusECore { //--------------------------------------------------------- // findKey //--------------------------------------------------------- iStringParamMap StringParamMap::findKey(const char* key) { iStringParamMap icm = find(std::string(key)); return icm; } //--------------------------------------------------------- // set //--------------------------------------------------------- void StringParamMap::set(const char* key, const char* value) { iStringParamMap icm = find(std::string(key)); if(icm == end()) insert(std::pair(key, value)); else icm->second = std::string(value); } //--------------------------------------------------------- // remove //--------------------------------------------------------- void StringParamMap::remove(const char* key) { erase(std::string(key)); } //--------------------------------------------------------- // read //--------------------------------------------------------- void StringParamMap::read(Xml& xml, const QString& name) { QString n; QString value; for (;;) { Xml::Token token = xml.parse(); const QString tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: xml.unknown(name.toLatin1().constData()); break; case Xml::Attribut: if(tag == "name") n = xml.s2(); else if(tag == "val") value = xml.s2(); else xml.unknown(name.toLatin1().constData()); break; case Xml::TagEnd: if(tag == name) { // Add or modify the item. set(n.toLatin1(), value.toLatin1()); return; } default: break; } } } //--------------------------------------------------------- // write //--------------------------------------------------------- void StringParamMap::write(int level, Xml& xml, const char* name) const { if(empty()) return; for(ciStringParamMap r = begin(); r != end(); ++r) xml.tag(level, "%s name=\"%s\" val=\"%s\"/", name, r->first.c_str(), r->second.c_str()); } } // namespace MusECore muse-4.0.0/src/muse/stringparam.h000066400000000000000000000033111404105311000167070ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // $Id: stringparam.h,v 1.0.0.0 2010/04/24 01:01:01 terminator356 Exp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #ifndef __STRINGPARAM_H__ #define __STRINGPARAM_H__ #include #include class QString; namespace MusECore { class Xml; typedef std::map::iterator iStringParamMap; typedef std::map::const_iterator ciStringParamMap; class StringParamMap : public std::map { public: void set(const char* key, const char* value); void remove(const char* key); iStringParamMap findKey(const char* key); void read(Xml& xml, const QString& name); void write(int level, Xml& xml, const char* name) const; }; } // namespace MusECore #endif //__STRINGPARAM_H__ muse-4.0.0/src/muse/sync.cpp000066400000000000000000001325741404105311000157050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sync.cpp,v 1.6.2.12 2009/06/20 22:20:41 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include "sync.h" #include "song.h" #include "utils.h" #include "midiport.h" #include "mididev.h" #include "globals.h" #include "midiseq.h" #include "audio.h" #include "audiodev.h" #include "gconfig.h" #include "xml.h" #include "midi_consts.h" #include "large_int.h" namespace MusEGlobal { bool debugSync = false; int mtcType = 1; MusECore::MTC mtcOffset; bool extSyncFlag = false; // false - MASTER, true - SLAVE bool timebaseMasterState = false; // Hack when loading songs to force master. bool timebaseMasterForceFlag = false; static MusECore::MTC mtcCurTime; static int mtcState; // 0-7 next expected quarter message static bool mtcValid; static int mtcLost; static bool mtcSync; // receive complete mtc frame? unsigned int syncSendFirstClockDelay = 1; // In milliseconds. unsigned int volatile curExtMidiSyncTick = 0; unsigned int volatile lastExtMidiSyncTick = 0; unsigned int volatile curExtMidiSyncFrame = 0; unsigned int volatile lastExtMidiSyncFrame = 0; MusECore::MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset = MusECore::MidiSyncInfo::SMALL; double syncRecTempoValQuant = 1.0; MusECore::MidiSyncContainer midiSyncContainer; // Not used yet. DELETETHIS? // static bool mcStart = false; // static int mcStartTick; // From the "Introduction to the Volatile Keyword" at Embedded dot com /* A variable should be declared volatile whenever its value could change unexpectedly. ... global variables within a multi-threaded application ... So all shared global variables should be declared volatile */ unsigned int volatile midiExtSyncTicks = 0; } // namespace MusEGlobal namespace MusECore { //--------------------------------------------------------- // MidiSyncInfo //--------------------------------------------------------- MidiSyncInfo::MidiSyncInfo() { _port = -1; _idOut = 127; _idIn = 127; _sendMC = false; _sendMRT = false; _sendMMC = false; _sendMTC = false; _recMC = false; _recMRT = false; _recMMC = false; _recMTC = false; _lastClkTime = 0; _lastTickTime = 0; _lastMRTTime = 0; _lastMMCTime = 0; _lastMTCTime = 0; _clockTrig = false; _tickTrig = false; _MRTTrig = false; _MMCTrig = false; _MTCTrig = false; _clockDetect = false; _tickDetect = false; _MRTDetect = false; _MMCDetect = false; _MTCDetect = false; _recMTCtype = 0; _recRewOnStart = true; _actDetectBits = 0; for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { _lastActTime[i] = 0.0; _actTrig[i] = false; _actDetect[i] = false; } } //--------------------------------------------------------- // operator = //--------------------------------------------------------- MidiSyncInfo& MidiSyncInfo::operator=(const MidiSyncInfo &sp) { copyParams(sp); _lastClkTime = sp._lastClkTime; _lastTickTime = sp._lastTickTime; _lastMRTTime = sp._lastMRTTime; _lastMMCTime = sp._lastMMCTime; _lastMTCTime = sp._lastMTCTime; _clockTrig = sp._clockTrig; _tickTrig = sp._tickTrig; _MRTTrig = sp._MRTTrig; _MMCTrig = sp._MMCTrig; _MTCTrig = sp._MTCTrig; _clockDetect = sp._clockDetect; _tickDetect = sp._tickDetect; _MRTDetect = sp._MRTDetect; _MMCDetect = sp._MMCDetect; _MTCDetect = sp._MTCDetect; _recMTCtype = sp._recMTCtype; for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) { _lastActTime[i] = sp._lastActTime[i]; _actTrig[i] = sp._actTrig[i]; _actDetect[i] = sp._actDetect[i]; } return *this; } //--------------------------------------------------------- // copyParams //--------------------------------------------------------- MidiSyncInfo& MidiSyncInfo::copyParams(const MidiSyncInfo &sp) { _idOut = sp._idOut; _idIn = sp._idIn; _sendMC = sp._sendMC; _sendMRT = sp._sendMRT; _sendMMC = sp._sendMMC; _sendMTC = sp._sendMTC; setMCIn(sp._recMC); _recMRT = sp._recMRT; _recMMC = sp._recMMC; _recMTC = sp._recMTC; _recRewOnStart = sp._recRewOnStart; return *this; } //--------------------------------------------------------- // setTime //--------------------------------------------------------- void MidiSyncInfo::setTime() { // Note: CurTime() makes a system call to gettimeofday(), // which apparently can be slow in some cases. So I avoid calling this function // too frequently by calling it (at the heartbeat rate) in Song::beat(). T356 uint64_t t = curTimeUS(); if(_clockTrig) { _clockTrig = false; _lastClkTime = t; } else if(_clockDetect && (t - _lastClkTime >= 1000000UL)) // Set detect indicator timeout to about 1 second. { _clockDetect = false; } if(_tickTrig) { _tickTrig = false; _lastTickTime = t; } else // if(_tickDetect && (t - _lastTickTime) >= 1.0) // Set detect indicator timeout to about 1 second. if(_tickDetect && (t - _lastTickTime) >= 1000000UL) // Set detect indicator timeout to about 1 second. _tickDetect = false; if(_MRTTrig) { _MRTTrig = false; _lastMRTTime = t; } else // if(_MRTDetect && (t - _lastMRTTime) >= 1.0) // Set detect indicator timeout to about 1 second. if(_MRTDetect && (t - _lastMRTTime) >= 1000000UL) // Set detect indicator timeout to about 1 second. { _MRTDetect = false; } if(_MMCTrig) { _MMCTrig = false; _lastMMCTime = t; } else // if(_MMCDetect && (t - _lastMMCTime) >= 1.0) // Set detect indicator timeout to about 1 second. if(_MMCDetect && (t - _lastMMCTime) >= 1000000UL) // Set detect indicator timeout to about 1 second. { _MMCDetect = false; } if(_MTCTrig) { _MTCTrig = false; _lastMTCTime = t; } else // if(_MTCDetect && (t - _lastMTCTime) >= 1.0) // Set detect indicator timeout to about 1 second. if(_MTCDetect && (t - _lastMTCTime) >= 1000000UL) // Set detect indicator timeout to about 1 second. { _MTCDetect = false; } for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; i++) { if(_actTrig[i]) { _actTrig[i] = false; _lastActTime[i] = t; } else // if(_actDetect[i] && (t - _lastActTime[i]) >= 1.0) // Set detect indicator timeout to about 1 second. if(_actDetect[i] && (t - _lastActTime[i]) >= 1000000UL) // Set detect indicator timeout to about 1 second. { _actDetect[i] = false; _actDetectBits &= ~(1 << i); } } } //--------------------------------------------------------- // setMCIn //--------------------------------------------------------- void MidiSyncInfo::setMCIn(const bool v) { _recMC = v; } //--------------------------------------------------------- // setMRTIn //--------------------------------------------------------- void MidiSyncInfo::setMRTIn(const bool v) { _recMRT = v; } //--------------------------------------------------------- // setMMCIn //--------------------------------------------------------- void MidiSyncInfo::setMMCIn(const bool v) { _recMMC = v; } //--------------------------------------------------------- // setMTCIn //--------------------------------------------------------- void MidiSyncInfo::setMTCIn(const bool v) { _recMTC = v; } //--------------------------------------------------------- // trigMCSyncDetect //--------------------------------------------------------- void MidiSyncInfo::trigMCSyncDetect() { _clockDetect = true; _clockTrig = true; } //--------------------------------------------------------- // trigTickDetect //--------------------------------------------------------- void MidiSyncInfo::trigTickDetect() { _tickDetect = true; _tickTrig = true; } //--------------------------------------------------------- // trigMRTDetect //--------------------------------------------------------- void MidiSyncInfo::trigMRTDetect() { _MRTDetect = true; _MRTTrig = true; } //--------------------------------------------------------- // trigMMCDetect //--------------------------------------------------------- void MidiSyncInfo::trigMMCDetect() { _MMCDetect = true; _MMCTrig = true; } //--------------------------------------------------------- // trigMTCDetect //--------------------------------------------------------- void MidiSyncInfo::trigMTCDetect() { _MTCDetect = true; _MTCTrig = true; } //--------------------------------------------------------- // actDetect //--------------------------------------------------------- bool MidiSyncInfo::actDetect(const int ch) const { if(ch < 0 || ch >= MusECore::MUSE_MIDI_CHANNELS) return false; return _actDetect[ch]; } //--------------------------------------------------------- // trigActDetect //--------------------------------------------------------- void MidiSyncInfo::trigActDetect(const int ch) { if(ch < 0 || ch >= MusECore::MUSE_MIDI_CHANNELS) return; _actDetectBits |= (1 << ch); _actDetect[ch] = true; _actTrig[ch] = true; } //--------------------------------------------------------- // isDefault //--------------------------------------------------------- bool MidiSyncInfo::isDefault() const { return(_idOut == 127 && _idIn == 127 && !_sendMC && !_sendMRT && !_sendMMC && !_sendMTC && !_recMC && !_recMRT && !_recMMC && !_recMTC && _recRewOnStart); } //--------------------------------------------------------- // read //--------------------------------------------------------- void MidiSyncInfo::read(Xml& xml) { for (;;) { Xml::Token token(xml.parse()); const QString& tag(xml.s1()); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "idOut") _idOut = xml.parseInt(); else if (tag == "idIn") _idIn = xml.parseInt(); else if (tag == "sendMC") _sendMC = xml.parseInt(); else if (tag == "sendMRT") _sendMRT = xml.parseInt(); else if (tag == "sendMMC") _sendMMC = xml.parseInt(); else if (tag == "sendMTC") _sendMTC = xml.parseInt(); else if (tag == "recMC") _recMC = xml.parseInt(); else if (tag == "recMRT") _recMRT = xml.parseInt(); else if (tag == "recMMC") _recMMC = xml.parseInt(); else if (tag == "recMTC") _recMTC = xml.parseInt(); else if (tag == "recRewStart") _recRewOnStart = xml.parseInt(); else xml.unknown("midiSyncInfo"); break; case Xml::TagEnd: if (tag == "midiSyncInfo") return; default: break; } } } //--------------------------------------------------------- // write //--------------------------------------------------------- void MidiSyncInfo::write(int level, Xml& xml) { if(isDefault()) return; xml.tag(level++, "midiSyncInfo"); if(_idOut != 127) xml.intTag(level, "idOut", _idOut); if(_idIn != 127) xml.intTag(level, "idIn", _idIn); if(_sendMC) xml.intTag(level, "sendMC", true); if(_sendMRT) xml.intTag(level, "sendMRT", true); if(_sendMMC) xml.intTag(level, "sendMMC", true); if(_sendMTC) xml.intTag(level, "sendMTC", true); if(_recMC) xml.intTag(level, "recMC", true); if(_recMRT) xml.intTag(level, "recMRT", true); if(_recMMC) xml.intTag(level, "recMMC", true); if(_recMTC) xml.intTag(level, "recMTC", true); if(!_recRewOnStart) xml.intTag(level, "recRewStart", false); xml.etag(level, "midiSyncInfo"); } //--------------------------------------------------------- // MidiSyncContainer //--------------------------------------------------------- MidiSyncContainer::MidiSyncContainer() { // REMOVE Tim. clock. Removed. _midiClock = 0; mclock1 = 0.0; mclock2 = 0.0; songtick1 = songtick2 = 0; lastTempo = 0; storedtimediffs = 0; playStateExt = ExtMidiClock::ExternStopped; recTick = 0; recTick1 = 0; recTick2 = 0; _clockAveragerStages = new int[16]; // Max stages is 16! _syncRecFilterPreset = MidiSyncInfo::SMALL; setSyncRecFilterPresetArrays(); for(int i = 0; i < _clockAveragerPoles; ++i) { _avgClkDiffCounter[i] = 0; _averagerFull[i] = false; } _tempoQuantizeAmount = 1.0; _lastRealTempo = 0.0; } MidiSyncContainer::~MidiSyncContainer() { if(_clockAveragerStages) delete[] _clockAveragerStages; } //--------------------------------------------------------- // mmcInput // Midi Machine Control Input received //--------------------------------------------------------- void MidiSyncContainer::mmcInput(int port, const unsigned char* p, int n) { if (MusEGlobal::debugSync) fprintf(stderr, "mmcInput: n:%d %02x %02x %02x %02x\n", n, p[2], p[3], p[4], p[5]); MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiSyncInfo& msync = mp->syncInfo(); // Trigger MMC detect in. msync.trigMMCDetect(); // MMC locate SMPTE time code may contain format type bits. Grab them. if(p[3] == 0x44 && p[4] == 6 && p[5] == 1) msync.setRecMTCtype((p[6] >> 5) & 3); // MMC in not turned on? Forget it. if(!msync.MMCIn()) return; switch(p[3]) { case 1: if (MusEGlobal::debugSync) fprintf(stderr, " MMC: STOP\n"); playStateExt = ExtMidiClock::ExternStopped; if (MusEGlobal::audio->isPlaying()) { MusEGlobal::audio->msgPlay(false); } alignAllTicks(); break; case 2: if (MusEGlobal::debugSync) fprintf(stderr, " MMC: PLAY\n"); // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case 3: if (MusEGlobal::debugSync) fprintf(stderr, " MMC: DEFERRED PLAY\n"); MusEGlobal::mtcState = 0; MusEGlobal::mtcValid = false; MusEGlobal::mtcLost = 0; MusEGlobal::mtcSync = false; alignAllTicks(); playStateExt = ExtMidiClock::ExternStarting; if(MusEGlobal::audio->isRunning() && !MusEGlobal::audio->isPlaying() && MusEGlobal::checkAudioDevice()) MusEGlobal::audioDevice->startTransport(); break; case 4: fprintf(stderr, "MMC: FF not implemented\n"); break; case 5: fprintf(stderr, "MMC: REWIND not implemented\n"); break; case 6: fprintf(stderr, "MMC: REC STROBE not implemented\n"); break; case 7: fprintf(stderr, "MMC: REC EXIT not implemented\n"); break; case 0xd: fprintf(stderr, "MMC: RESET not implemented\n"); break; case 0x44: if (p[5] == 0) { fprintf(stderr, "MMC: LOCATE IF not implemented\n"); break; } else if (p[5] == 1) { if (!MusEGlobal::checkAudioDevice()) return; MTC mtc(p[6] & 0x1f, p[7], p[8], p[9], p[10]); int type = (p[6] >> 5) & 3; // MTC time resolution is less than frame resolution. // Round up so that the reciprocal function (frame to time) matches value for value. unsigned mmcPos = muse_multiply_64_div_64_to_64(mtc.timeUS(type), MusEGlobal::sampleRate, 1000000UL, LargeIntRoundUp); Pos tp(mmcPos, false); MusEGlobal::audioDevice->seekTransport(tp); alignAllTicks(); if (MusEGlobal::debugSync) { fprintf(stderr, "MMC: LOCATE mtc type:%d timeUS:%lu frame:%u mtc: ", type, (long unsigned)mtc.timeUS(), mmcPos); mtc.print(); fprintf(stderr, "\n"); } break; } // fall through default: fprintf(stderr, "MMC %x %x, unknown\n", p[3], p[4]); break; } } //--------------------------------------------------------- // mtcInputQuarter // process Quarter Frame Message //--------------------------------------------------------- void MidiSyncContainer::mtcInputQuarter(int port, unsigned char c) { static int hour, min, sec, frame; int valL = c & 0xf; int valH = valL << 4; int _state = (c & 0x70) >> 4; if (MusEGlobal::mtcState != _state) MusEGlobal::mtcLost += _state - MusEGlobal::mtcState; MusEGlobal::mtcState = _state + 1; switch(_state) { case 7: hour = (hour & 0x0f) | valH; break; case 6: hour = (hour & 0xf0) | valL; break; case 5: min = (min & 0x0f) | valH; break; case 4: min = (min & 0xf0) | valL; break; case 3: sec = (sec & 0x0f) | valH; break; case 2: sec = (sec & 0xf0) | valL; break; case 1: frame = (frame & 0x0f) | valH; break; case 0: frame = (frame & 0xf0) | valL; break; } frame &= 0x1f; // 0-29 sec &= 0x3f; // 0-59 min &= 0x3f; // 0-59 int tmphour = hour; int type = (hour >> 5) & 3; hour &= 0x1f; if(MusEGlobal::mtcState == 8) { MusEGlobal::mtcValid = (MusEGlobal::mtcLost == 0); MusEGlobal::mtcState = 0; MusEGlobal::mtcLost = 0; if(MusEGlobal::mtcValid) { MusEGlobal::mtcCurTime.set(hour, min, sec, frame); if(port != -1) { MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiSyncInfo& msync = mp->syncInfo(); msync.setRecMTCtype(type); msync.trigMTCDetect(); // Not for the current in port? External sync not turned on? MTC in not turned on? Forget it. if(port == MusEGlobal::config.curMidiSyncInPort && MusEGlobal::extSyncFlag && msync.MTCIn()) { if(MusEGlobal::debugSync) fprintf(stderr, "MidiSyncContainer::mtcInputQuarter hour byte:%x\n", (unsigned int)tmphour); mtcSyncMsg(MusEGlobal::mtcCurTime, type, !MusEGlobal::mtcSync); } } MusEGlobal::mtcSync = true; } } else if (MusEGlobal::mtcValid && (MusEGlobal::mtcLost == 0)) { MusEGlobal::mtcCurTime.incQuarter(type); } } //--------------------------------------------------------- // mtcInputFull // process Frame Message //--------------------------------------------------------- void MidiSyncContainer::mtcInputFull(int port, const unsigned char* p, int n) { if (MusEGlobal::debugSync) fprintf(stderr, "mtcInputFull\n"); if (p[3] != 1) { if (p[3] != 2) { // silently ignore user bits fprintf(stderr, "unknown mtc msg subtype 0x%02x\n", p[3]); dump(p, n); } return; } int hour = p[4]; int min = p[5]; int sec = p[6]; int frame = p[7]; frame &= 0x1f; // 0-29 sec &= 0x3f; // 0-59 min &= 0x3f; // 0-59 int type = (hour >> 5) & 3; hour &= 0x1f; MusEGlobal::mtcCurTime.set(hour, min, sec, frame); MusEGlobal::mtcState = 0; MusEGlobal::mtcValid = true; MusEGlobal::mtcLost = 0; // Added by Tim. if(MusEGlobal::debugSync) fprintf(stderr, "mtcInputFull: timeUS:%lu stimeUS:%lu hour byte (all bits):%hhx\n", (long unsigned)MusEGlobal::mtcCurTime.timeUS(), (long unsigned)MusEGlobal::mtcCurTime.timeUS(type), p[4]); if(port != -1) { MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiSyncInfo& msync = mp->syncInfo(); msync.setRecMTCtype(type); msync.trigMTCDetect(); // MTC in not turned on? Forget it. if(msync.MTCIn()) { // MTC time resolution is less than frame resolution. // Round up so that the reciprocal function (frame to time) matches value for value. const unsigned t_frame = muse_multiply_64_div_64_to_64(MusEGlobal::mtcCurTime.timeUS(type), MusEGlobal::sampleRate, 1000000UL, LargeIntRoundUp); Pos tp(t_frame, false); MusEGlobal::audioDevice->seekTransport(tp); alignAllTicks(); } } } //--------------------------------------------------------- // nonRealtimeSystemSysex //--------------------------------------------------------- void MidiSyncContainer::nonRealtimeSystemSysex(int /*port*/, const unsigned char* p, int n) { switch(p[3]) { case 4: fprintf(stderr, "NRT Setup\n"); break; default: fprintf(stderr, "unknown NRT Msg 0x%02x\n", p[3]); dump(p, n); break; } } //--------------------------------------------------------- // setSongPosition // MidiBeat is a 14 Bit value. Each MidiBeat spans // 6 MIDI Clocks. Inother words, each MIDI Beat is a // 16th note (since there are 24 MIDI Clocks in a // quarter note). //--------------------------------------------------------- void MidiSyncContainer::setSongPosition(int port, int midiBeat) { if (MusEGlobal::midiInputTrace) fprintf(stderr, "set song position port:%d %d\n", port, midiBeat); MusEGlobal::midiPorts[port].syncInfo().trigMRTDetect(); if(!MusEGlobal::extSyncFlag || !MusEGlobal::midiPorts[port].syncInfo().MRTIn()) return; // Re-transmit song position to other devices if clock out turned on. for(int p = 0; p < MusECore::MIDI_PORTS; ++p) if(p != port && MusEGlobal::midiPorts[p].syncInfo().MRTOut()) MusEGlobal::midiPorts[p].sendSongpos(midiBeat); MusEGlobal::curExtMidiSyncTick = (MusEGlobal::config.division * midiBeat) / 4; MusEGlobal::lastExtMidiSyncTick = MusEGlobal::curExtMidiSyncTick; Pos pos(MusEGlobal::curExtMidiSyncTick, true); if (!MusEGlobal::checkAudioDevice()) return; MusEGlobal::audioDevice->seekTransport(pos); alignAllTicks(pos.frame()); if (MusEGlobal::debugSync) fprintf(stderr, "setSongPosition %d\n", pos.tick()); } //--------------------------------------------------------- // set all runtime variables to the "in sync" value //--------------------------------------------------------- void MidiSyncContainer::alignAllTicks(int frameOverride) { unsigned curFrame; if (!frameOverride && MusEGlobal::audio) curFrame = MusEGlobal::audio->pos().frame(); else curFrame = frameOverride; int tempo = MusEGlobal::tempomap.tempo(0); // use the last old values to give start values for the tripple buffering int recTickSpan = recTick1 - recTick2; int songTickSpan = (int)(songtick1 - songtick2); //prevent compiler warning: casting double to int storedtimediffs = 0; // pretend there is no sync history mclock2=mclock1=0.0; // set all clock values to "in sync" recTick = (int) ((double(curFrame)/double(MusEGlobal::sampleRate)) * double(MusEGlobal::config.division * 1000000.0) / double(tempo) //prevent compiler warning: casting double to int ); songtick1 = recTick - songTickSpan; if (songtick1 < 0) songtick1 = 0; songtick2 = songtick1 - songTickSpan; if (songtick2 < 0) songtick2 = 0; recTick1 = recTick - recTickSpan; if (recTick1 < 0) recTick1 = 0; recTick2 = recTick1 - recTickSpan; if (recTick2 < 0) recTick2 = 0; if (MusEGlobal::debugSync) fprintf(stderr, "alignAllTicks curFrame=%d recTick=%d tempo=%.3f frameOverride=%d\n",curFrame,recTick,(float)((1000000.0 * 60.0)/tempo), frameOverride); lastTempo = 0; for(int i = 0; i < _clockAveragerPoles; ++i) { _avgClkDiffCounter[i] = 0; _averagerFull[i] = false; } _lastRealTempo = 0.0; } //--------------------------------------------------------- // realtimeSystemInput // real time message received //--------------------------------------------------------- void MidiSyncContainer::realtimeSystemInput(int port, int c) { if (MusEGlobal::midiInputTrace) fprintf(stderr, "realtimeSystemInput port:%d 0x%x\n", port+1, c); MidiPort* mp = &MusEGlobal::midiPorts[port]; // Trigger on any tick, clock, or realtime command. if(c == ME_TICK) // Tick mp->syncInfo().trigTickDetect(); else mp->syncInfo().trigMRTDetect(); // Other // External sync not on? Clock in not turned on? Otherwise realtime in not turned on? if(!MusEGlobal::extSyncFlag) return; if(!mp->syncInfo().MRTIn()) return; switch(c) { case ME_TICK: // midi tick (every 10 msec) //DELETETHIS 6 // FIXME: Unfinished? mcStartTick is uninitialized and Song::setPos doesn't set it either. Dangerous to allow this. //if (mcStart) { // song->setPos(0, mcStartTick); // mcStart = false; // return; // } break; case ME_START: // start // Re-transmit start to other devices if clock out turned on. for(int p = 0; p < MusECore::MIDI_PORTS; ++p) if(p != port && MusEGlobal::midiPorts[p].syncInfo().MRTOut()) { // If we aren't rewinding on start, there's no point in re-sending start. // Re-send continue instead, for consistency. if(MusEGlobal::midiPorts[port].syncInfo().recRewOnStart()) MusEGlobal::midiPorts[p].sendStart(); else MusEGlobal::midiPorts[p].sendContinue(); } if (MusEGlobal::debugSync) fprintf(stderr, " start\n"); // DELETETHIS, remove the wrapping if(true) if (1 /* !MusEGlobal::audio->isPlaying()*/ /*state == IDLE*/) { if (!MusEGlobal::checkAudioDevice()) return; playStateExt = ExtMidiClock::ExternStarting; // Rew on start option. if(MusEGlobal::midiPorts[port].syncInfo().recRewOnStart()) { MusEGlobal::curExtMidiSyncTick = 0; MusEGlobal::lastExtMidiSyncTick = MusEGlobal::curExtMidiSyncTick; MusEGlobal::audioDevice->seekTransport(Pos(0, false)); } alignAllTicks(); storedtimediffs = 0; MusEGlobal::midiExtSyncTicks = 0; } break; case ME_CONTINUE: // continue // Re-transmit continue to other devices if clock out turned on. for(int p = 0; p < MusECore::MIDI_PORTS; ++p) if(p != port && MusEGlobal::midiPorts[p].syncInfo().MRTOut()) MusEGlobal::midiPorts[p].sendContinue(); if (MusEGlobal::debugSync) fprintf(stderr, "realtimeSystemInput continue\n"); //printf("continue:%f\n", curTime()); if (1 /* !MusEGlobal::audio->isPlaying() */ /*state == IDLE */) { // Begin incrementing immediately upon first clock reception. playStateExt = ExtMidiClock::ExternContinuing; } break; case ME_STOP: // stop { // Stop the increment right away. MusEGlobal::midiExtSyncTicks = 0; playStateExt = ExtMidiClock::ExternStopped; // Re-transmit stop to other devices if clock out turned on. for(int p = 0; p < MusECore::MIDI_PORTS; ++p) if(p != port && MusEGlobal::midiPorts[p].syncInfo().MRTOut()) MusEGlobal::midiPorts[p].sendStop(); if (MusEGlobal::audio->isPlaying()) MusEGlobal::audio->msgPlay(false); if (MusEGlobal::debugSync) fprintf(stderr, "realtimeSystemInput stop\n"); //DELETETHIS 7 // Just in case the process still runs a cycle or two and causes the // audio tick position to increment, reset the incrementer and force // the transport position to what the hardware thinks is the current position. //MusEGlobal::midiExtSyncTicks = 0; //Pos pos((MusEGlobal::config.division * lastStoppedBeat) / 4, true); //Pos pos(MusEGlobal::curExtMidiSyncTick, true); //MusEGlobal::audioDevice->seekTransport(pos); } break; //case 0xfd: // unknown DELETETHIS 3 //case ME_SENSE: // active sensing //case ME_META: // system reset (reset is 0xff same enumeration as file meta event) default: break; } } //--------------------------------------------------------- // midiClockInput // Midi clock (24 ticks / quarter note) // Starts transport if necessary. Adds clock to tempo list. // Returns whether the clock was a 'first clock' after a start or continue message. //--------------------------------------------------------- ExtMidiClock MidiSyncContainer::midiClockInput(int port, unsigned int frame) { if(port < 0 || port >= MusECore::MIDI_PORTS) return ExtMidiClock(); MidiPort* mp = &MusEGlobal::midiPorts[port]; mp->syncInfo().trigMCSyncDetect(); // External sync not on? Clock in not turned on? Otherwise realtime in not turned on? if(!MusEGlobal::extSyncFlag) return ExtMidiClock(); if(!mp->syncInfo().MCIn()) return ExtMidiClock(); // Not for the current in port? Forget it. if(port != MusEGlobal::config.curMidiSyncInPort) return ExtMidiClock(); //fprintf(stderr, "MidiSyncContainer::midiClockInput: CLOCK port:%d time:%u\n", port, frame); // Re-transmit clock to other devices if clock out turned on. // Must be careful not to allow more than one clock input at a time. // Would re-transmit mixture of multiple clocks - confusing receivers. // Solution: Added MusEGlobal::curMidiSyncInPort. // Maybe in MidiSyncContainer::processTimerTick(), call sendClock for the other devices, instead of here. for(int p = 0; p < MusECore::MIDI_PORTS; ++p) if(p != port && MusEGlobal::midiPorts[p].syncInfo().MCOut()) MusEGlobal::midiPorts[p].sendClock(); MusEGlobal::lastExtMidiSyncFrame = MusEGlobal::curExtMidiSyncFrame; MusEGlobal::curExtMidiSyncFrame = frame; if(MusEGlobal::lastExtMidiSyncFrame > MusEGlobal::curExtMidiSyncFrame) { fprintf(stderr, "MusE: Warning: MidiSyncContainer::midiClockInput(): lastExtMidiSyncFrame:%u > curExtMidiSyncFrame:%u Setting last to cur...\n", MusEGlobal::lastExtMidiSyncFrame, MusEGlobal::curExtMidiSyncFrame); MusEGlobal::lastExtMidiSyncFrame = MusEGlobal::curExtMidiSyncFrame; } const int div = MusEGlobal::config.division/24; //------------------------------- // State changes: //------------------------------- bool first_clock = false; if(playStateExt == ExtMidiClock::ExternStarting || playStateExt == ExtMidiClock::ExternContinuing) { first_clock = true; if(playStateExt == ExtMidiClock::ExternStarting) playStateExt = ExtMidiClock::ExternStarted; if(playStateExt == ExtMidiClock::ExternContinuing) playStateExt = ExtMidiClock::ExternContinued; if(MusEGlobal::audio->isRunning() && !MusEGlobal::audio->isPlaying() && MusEGlobal::checkAudioDevice()) MusEGlobal::audioDevice->startTransport(); } //else DELETETHIS? // This part will be run on the second and subsequent clocks, after start. // Can't check audio state, might not be playing yet, we might miss some increments. if(isRunning()) { MusEGlobal::midiExtSyncTicks += div; MusEGlobal::lastExtMidiSyncTick = MusEGlobal::curExtMidiSyncTick; MusEGlobal::curExtMidiSyncTick += div; if(MusEGlobal::song->record() && MusEGlobal::curExtMidiSyncFrame > MusEGlobal::lastExtMidiSyncFrame) { double diff = double(MusEGlobal::curExtMidiSyncFrame - MusEGlobal::lastExtMidiSyncFrame) / double(MusEGlobal::sampleRate); if(diff != 0.0) { if(_clockAveragerPoles == 0) { double real_tempo = 60.0/(diff * 24.0); if(_tempoQuantizeAmount > 0.0) { double f_mod = fmod(real_tempo, _tempoQuantizeAmount); if(f_mod < _tempoQuantizeAmount/2.0) real_tempo -= f_mod; else real_tempo += _tempoQuantizeAmount - f_mod; } int new_tempo = ((1000000.0 * 60.0) / (real_tempo)); if(new_tempo != lastTempo) { lastTempo = new_tempo; // Compute tick for this tempo - it is one step back in time. int add_tick = MusEGlobal::curExtMidiSyncTick - div; if(MusEGlobal::debugSync) fprintf(stderr, "adding new tempo tick:%d curExtMidiSyncTick:%d avg_diff:%f real_tempo:%f new_tempo:%d = %f\n", add_tick, MusEGlobal::curExtMidiSyncTick, diff, real_tempo, new_tempo, (double)((1000000.0 * 60.0)/new_tempo)); MusEGlobal::song->addExternalTempo(TempoRecEvent(add_tick, new_tempo)); } } else { double avg_diff = diff; for(int pole = 0; pole < _clockAveragerPoles; ++pole) { timediff[pole][_avgClkDiffCounter[pole]] = avg_diff; ++_avgClkDiffCounter[pole]; if(_avgClkDiffCounter[pole] >= _clockAveragerStages[pole]) { _avgClkDiffCounter[pole] = 0; _averagerFull[pole] = true; } // Each averager needs to be full before we can pass the data to // the next averager or use the data if all averagers are full... if(!_averagerFull[pole]) break; else { avg_diff = 0.0; for(int i = 0; i < _clockAveragerStages[pole]; ++i) avg_diff += timediff[pole][i]; avg_diff /= _clockAveragerStages[pole]; int fin_idx = _clockAveragerPoles - 1; // On the first pole? Check for large differences. if(_preDetect && pole == 0) { double real_tempo = 60.0/(avg_diff * 24.0); double real_tempo_diff = fabs(real_tempo - _lastRealTempo); // If the tempo changed a large amount, reset. if(real_tempo_diff >= 10.0) // TODO: User-adjustable? { if(_tempoQuantizeAmount > 0.0) { double f_mod = fmod(real_tempo, _tempoQuantizeAmount); if(f_mod < _tempoQuantizeAmount/2.0) real_tempo -= f_mod; else real_tempo += _tempoQuantizeAmount - f_mod; } _lastRealTempo = real_tempo; int new_tempo = ((1000000.0 * 60.0) / (real_tempo)); if(new_tempo != lastTempo) { lastTempo = new_tempo; // Compute tick for this tempo - it is way back in time. int add_tick = MusEGlobal::curExtMidiSyncTick - _clockAveragerStages[0] * div; if(add_tick < 0) { fprintf(stderr, "FIXME sync: adding restart tempo curExtMidiSyncTick:%d: add_tick:%d < 0 !\n", MusEGlobal::curExtMidiSyncTick, add_tick); add_tick = 0; } if(MusEGlobal::debugSync) fprintf(stderr, "adding restart tempo tick:%d curExtMidiSyncTick:%d tick_idx_sub:%d avg_diff:%f real_tempo:%f real_tempo_diff:%f new_tempo:%d = %f\n", add_tick, MusEGlobal::curExtMidiSyncTick, _clockAveragerStages[0], avg_diff, real_tempo, real_tempo_diff, new_tempo, (double)((1000000.0 * 60.0)/new_tempo)); MusEGlobal::song->addExternalTempo(TempoRecEvent(add_tick, new_tempo)); } // Reset all the poles. //for(int i = 0; i < clockAveragerPoles; ++i) // We have a value for this pole, let's keep it but reset the other poles. for(int i = 1; i < _clockAveragerPoles; ++i) { _avgClkDiffCounter[i] = 0; _averagerFull[i] = false; } break; } } // On the last pole? // All averagers need to be full before we can use the data... if(pole == fin_idx) { double real_tempo = 60.0/(avg_diff * 24.0); double real_tempo_diff = fabs(real_tempo - _lastRealTempo); if(real_tempo_diff >= _tempoQuantizeAmount/2.0) // Anti-hysteresis { if(_tempoQuantizeAmount > 0.0) { double f_mod = fmod(real_tempo, _tempoQuantizeAmount); if(f_mod < _tempoQuantizeAmount/2.0) real_tempo -= f_mod; else real_tempo += _tempoQuantizeAmount - f_mod; } _lastRealTempo = real_tempo; int new_tempo = ((1000000.0 * 60.0) / (real_tempo)); if(new_tempo != lastTempo) { lastTempo = new_tempo; // Compute tick for this tempo - it is way back in time. int tick_idx_sub = 0; for(int i = 0; i <= pole; ++i) tick_idx_sub += _clockAveragerStages[i]; // Compensate: Each pole > 0 has a delay one less than its number of stages. // For example three pole {8, 8, 8} has a delay of 22 not 24. tick_idx_sub -= pole; int add_tick = MusEGlobal::curExtMidiSyncTick - tick_idx_sub * div; if(add_tick < 0) { fprintf(stderr, "FIXME sync: adding new tempo curExtMidiSyncTick:%d: add_tick:%d < 0 !\n", MusEGlobal::curExtMidiSyncTick, add_tick); add_tick = 0; } if(MusEGlobal::debugSync) fprintf(stderr, "adding new tempo tick:%d curExtMidiSyncTick:%d tick_idx_sub:%d avg_diff:%f real_tempo:%f new_tempo:%d = %f\n", add_tick, MusEGlobal::curExtMidiSyncTick, tick_idx_sub, avg_diff, real_tempo, new_tempo, (double)((1000000.0 * 60.0)/new_tempo)); MusEGlobal::song->addExternalTempo(TempoRecEvent(add_tick, new_tempo)); } } } } } } } } } return ExtMidiClock(frame, playStateExt, first_clock); } //--------------------------------------------------------- // MusEGlobal::mtcSyncMsg // process received mtc Sync // seekFlag - first complete mtc frame received after // start //--------------------------------------------------------- void MidiSyncContainer::mtcSyncMsg(const MTC& mtc, int type, bool seekFlag) { const uint64_t time = mtc.timeUS(); const uint64_t stime = mtc.timeUS(type); if (MusEGlobal::debugSync) fprintf(stderr, "MidiSyncContainer::mtcSyncMsg timeUS:%lu stimeUS:%lu seekFlag:%d\n", (long unsigned)time, (long unsigned)stime, seekFlag); if (seekFlag && MusEGlobal::audio->isRunning() && !MusEGlobal::audio->isPlaying() && MusEGlobal::checkAudioDevice()) { if (MusEGlobal::debugSync) fprintf(stderr, "MidiSyncContainer::mtcSyncMsg starting transport.\n"); MusEGlobal::audioDevice->startTransport(); return; } /*if (tempoSN != MusEGlobal::tempomap.tempoSN()) { DELETETHIS 13 double cpos = MusEGlobal::tempomap.tick2time(_midiTick, 0); samplePosStart = samplePos - lrint(cpos * MusEGlobal::sampleRate); rtcTickStart = rtcTick - lrint(cpos * realRtcTicks); tempoSN = MusEGlobal::tempomap.tempoSN(); }*/ // // diff is the time in sec MusE is out of sync // /*double diff = time - (double(samplePosStart)/double(MusEGlobal::sampleRate)); if (MusEGlobal::debugSync) printf(" state %d diff %f\n", MusEGlobal::mtcState, diff); */ } //--------------------------------------------------------- // setSyncRecFilterPresetArrays // To be called in realtime thread only. //--------------------------------------------------------- void MidiSyncContainer::setSyncRecFilterPresetArrays() { switch(_syncRecFilterPreset) { // NOTE: Max _clockAveragerPoles is 16 and maximum stages is 48 per pole ! case MidiSyncInfo::NONE: _clockAveragerPoles = 0; _preDetect = false; break; case MidiSyncInfo::TINY: _clockAveragerPoles = 2; _clockAveragerStages[0] = 4; _clockAveragerStages[1] = 4; _preDetect = false; break; case MidiSyncInfo::SMALL: _clockAveragerPoles = 3; _clockAveragerStages[0] = 12; _clockAveragerStages[1] = 8; _clockAveragerStages[2] = 4; _preDetect = false; break; case MidiSyncInfo::MEDIUM: _clockAveragerPoles = 3; _clockAveragerStages[0] = 28; _clockAveragerStages[1] = 12; _clockAveragerStages[2] = 8; _preDetect = false; break; case MidiSyncInfo::LARGE: _clockAveragerPoles = 4; _clockAveragerStages[0] = 48; _clockAveragerStages[1] = 48; _clockAveragerStages[2] = 48; _clockAveragerStages[3] = 48; _preDetect = false; break; case MidiSyncInfo::LARGE_WITH_PRE_DETECT: _clockAveragerPoles = 4; _clockAveragerStages[0] = 8; _clockAveragerStages[1] = 48; _clockAveragerStages[2] = 48; _clockAveragerStages[3] = 48; _preDetect = true; break; default: fprintf(stderr, "MidiSyncContainer::setSyncRecFilterPresetArrays unknown preset type:%d\n", (int)_syncRecFilterPreset); } } //--------------------------------------------------------- // setSyncRecFilterPreset // To be called in realtime thread only. //--------------------------------------------------------- void MidiSyncContainer::setSyncRecFilterPreset(MidiSyncInfo::SyncRecFilterPresetType type) { _syncRecFilterPreset = type; setSyncRecFilterPresetArrays(); alignAllTicks(); } } // namespace MusECore muse-4.0.0/src/muse/sync.h000066400000000000000000000243441404105311000153450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sync.h,v 1.1.1.1.2.2 2009/04/01 01:37:11 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SYNC_H__ #define __SYNC_H__ #include "mtc.h" #include "globaldefs.h" #include namespace MusECore { class Xml; class MidiSyncInfo { public: enum SyncRecFilterPresetType { NONE=0, TINY, SMALL, MEDIUM, LARGE, LARGE_WITH_PRE_DETECT, TYPE_END }; private: int _port; int _idOut; int _idIn; bool _sendMC; bool _sendMRT; bool _sendMMC; bool _sendMTC; bool _recMC; bool _recMRT; bool _recMMC; bool _recMTC; int _recMTCtype; bool _recRewOnStart; uint64_t _lastClkTime; uint64_t _lastTickTime; uint64_t _lastMRTTime; uint64_t _lastMMCTime; uint64_t _lastMTCTime; uint64_t _lastActTime[MusECore::MUSE_MIDI_CHANNELS]; bool _clockTrig; bool _tickTrig; bool _MRTTrig; bool _MMCTrig; bool _MTCTrig; bool _actTrig[MusECore::MUSE_MIDI_CHANNELS]; bool _clockDetect; bool _tickDetect; bool _MRTDetect; bool _MMCDetect; bool _MTCDetect; bool _actDetect[MusECore::MUSE_MIDI_CHANNELS]; int _actDetectBits; public: MidiSyncInfo(); MidiSyncInfo& operator= (const MidiSyncInfo &sp); MidiSyncInfo& copyParams(const MidiSyncInfo &sp); int port() const { return _port; } void setPort(const int p) { _port = p; } int idOut() const { return _idOut; } int idIn() const { return _idIn; } void setIdOut(const int v) { _idOut = v; } void setIdIn(const int v) { _idIn = v; } bool MCOut() const { return _sendMC; } bool MRTOut() const { return _sendMRT; } bool MMCOut() const { return _sendMMC; } bool MTCOut() const { return _sendMTC; } bool MCIn() const { return _recMC; } bool MRTIn() const { return _recMRT; } bool MMCIn() const { return _recMMC; } bool MTCIn() const { return _recMTC; } void setMCOut(const bool v) { _sendMC = v; } void setMRTOut(const bool v) { _sendMRT = v; } void setMMCOut(const bool v) { _sendMMC = v; } void setMTCOut(const bool v) { _sendMTC = v; } void setMCIn(const bool v); void setMRTIn(const bool v); void setMMCIn(const bool v); void setMTCIn(const bool v); void setTime(); bool recRewOnStart() const { return _recRewOnStart; } void setRecRewOnStart(const bool v) { _recRewOnStart = v; } bool MCSyncDetect() const { return _clockDetect; } void trigMCSyncDetect(); bool tickDetect() const { return _tickDetect; } void trigTickDetect(); bool MTCDetect() const { return _MTCDetect; } void trigMTCDetect(); int recMTCtype() const { return _recMTCtype; } void setRecMTCtype(int t) { _recMTCtype = t; } bool MRTDetect() const { return _MRTDetect; } void trigMRTDetect(); bool MMCDetect() const { return _MMCDetect; } void trigMMCDetect(); int actDetectBits() const { return _actDetectBits; } bool actDetect(const int ch) const; void trigActDetect(const int ch); bool isDefault() const; void read(Xml& xml); void write(int level, Xml& xml); }; //--------------------------------------------------------- // ExtMidiClock // Holds the frame of each external clock, // and play state at that time. //--------------------------------------------------------- class ExtMidiClock { public: enum ExternState { ExternStopped = 0, ExternStarting, ExternContinuing, ExternStarted, ExternContinued }; private: // The frame at which this clock arrived. unsigned int _frame; // The play state of the external device when this clock arrived. ExternState _externState; // Whether this clock is the first clock after a start or continue. bool _isFirstClock; // Whether this is a valid structure. bool _isValid; public: ExtMidiClock() : _frame(0), _externState(ExternStopped), _isFirstClock(false), _isValid(false) { }; ExtMidiClock(unsigned int frame, ExternState extState, bool firstClock) : _frame(frame), _externState(extState), _isFirstClock(firstClock), _isValid(true) { }; // The frame at which this clock arrived. unsigned int frame() const { return _frame; } // The play state of the external device when this clock arrived. ExternState externState() const { return _externState; } // Whether this clock is the first clock after a start or continue. bool isFirstClock() const { return _isFirstClock; } // Whether this is a valid structure. bool isValid() const { return _isValid; } bool isPlaying() const { switch(_externState) { case ExternStopped: case ExternStarting: case ExternContinuing: return false; break; case ExternStarted: case ExternContinued: return true; break; }; return false; } bool isRunning() const { switch(_externState) { case ExternStopped: return false; break; case ExternStarting: case ExternContinuing: case ExternStarted: case ExternContinued: return true; break; }; return false; } }; //--------------------------------------------------------- // MidiSyncContainer //--------------------------------------------------------- class MidiSyncContainer { private: // REMOVE Tim. clock. Removed. unsigned int _midiClock; // Accumulator for clock output. /* Testing */ ExtMidiClock::ExternState playStateExt; // used for keeping play state in sync functions int recTick; // ext sync tick position double mclock1, mclock2; double songtick1, songtick2; int recTick1, recTick2; int lastTempo; double timediff[16][48]; int storedtimediffs; int _avgClkDiffCounter[16]; double _lastRealTempo; bool _averagerFull[16]; int _clockAveragerPoles; int* _clockAveragerStages; bool _preDetect; double _tempoQuantizeAmount; MidiSyncInfo::SyncRecFilterPresetType _syncRecFilterPreset; void setSyncRecFilterPresetArrays(); void alignAllTicks(int frameOverride = 0); /* Testing */ void mtcSyncMsg(const MTC&, int, bool); public: MidiSyncContainer(); virtual ~MidiSyncContainer(); // REMOVE Tim. clock. Removed. unsigned int midiClock() const { return _midiClock; } void setMidiClock(unsigned int val) { _midiClock = val; } ExtMidiClock::ExternState externalPlayState() const { return playStateExt; } void setExternalPlayState(ExtMidiClock::ExternState v) { playStateExt = v; } bool isPlaying() const { switch(playStateExt) { case ExtMidiClock::ExternStopped: case ExtMidiClock::ExternStarting: case ExtMidiClock::ExternContinuing: return false; break; case ExtMidiClock::ExternStarted: case ExtMidiClock::ExternContinued: return true; break; }; return false; } bool isRunning() const { switch(playStateExt) { case ExtMidiClock::ExternStopped: return false; break; case ExtMidiClock::ExternStarting: case ExtMidiClock::ExternContinuing: case ExtMidiClock::ExternStarted: case ExtMidiClock::ExternContinued: return true; break; }; return false; } void realtimeSystemInput(int port, int type); // Starts transport if necessary. Adds clock to tempo list. // Returns a clock structure including frame, state, and whether the clock was a // 'first clock' after a start or continue message. ExtMidiClock midiClockInput(int port, unsigned int frame); void mtcInputQuarter(int, unsigned char); void setSongPosition(int, int); void mmcInput(int, const unsigned char*, int); void mtcInputFull(int, const unsigned char*, int); void nonRealtimeSystemSysex(int, const unsigned char*, int); MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset() const { return _syncRecFilterPreset; } void setSyncRecFilterPreset(MidiSyncInfo::SyncRecFilterPresetType type); double recTempoValQuant() const { return _tempoQuantizeAmount; } void setRecTempoValQuant(double q) { _tempoQuantizeAmount = q; } }; } // namespace MusECore namespace MusEGlobal { extern bool debugSync; extern int mtcType; extern MusECore::MTC mtcOffset; extern bool extSyncFlag; extern bool timebaseMasterState; extern bool timebaseMasterForceFlag; extern unsigned int syncSendFirstClockDelay; // In milliseconds. extern unsigned int volatile lastExtMidiSyncTick; extern unsigned int volatile curExtMidiSyncTick; extern MusECore::MidiSyncInfo::SyncRecFilterPresetType syncRecFilterPreset; extern double syncRecTempoValQuant; extern MusECore::MidiSyncContainer midiSyncContainer; } // namespace MusEGlobal #endif muse-4.0.0/src/muse/synth.cpp000066400000000000000000004350731404105311000160760ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: synth.cpp,v 1.43.2.23 2009/12/15 03:39:58 terminator356 Exp $ // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "config.h" #ifndef _WIN32 #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "app.h" #include "synth.h" #include "midi_consts.h" #include "song.h" #include "audio.h" #include "event.h" #include "mpevent.h" #include "audio.h" #include "midiseq.h" #include "midictrl.h" #include "midiitransform.h" #include "mitplugin.h" #include "helper.h" #include "gconfig.h" #include "globals.h" #include "plugin_list.h" #include "pluglist.h" #include "ticksynth.h" #include "undo.h" // Forwards from header: #include "midiport.h" #include "synti/libsynti/mess.h" #include "popupmenu.h" #include "xml.h" // Undefine if and when multiple output routes are added to midi tracks. #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ // For debugging output: Uncomment the fprintf section. #define DEBUG_SYNTH(dev, format, args...) //fprintf(dev, format, ##args); namespace MusEGlobal { MusECore::SynthList synthis; // array of available MusEGlobal::synthis } namespace MusECore { extern void connectNodes(AudioTrack*, AudioTrack*); bool SynthI::_isVisible=false; const char* synthTypes[] = { "METRONOME", "MESS", "DSSI", "Wine VST", "VST (synths)", "VST (effects)", "LV2 (synths)", "LV2 (effects)", "UNKNOWN" }; QString synthType2String(Synth::Type type) { return QString(synthTypes[type]); } Synth::Type string2SynthType(const QString& type) { for(int i = 0; i < Synth::SYNTH_TYPE_END; ++i) { if(synthType2String((Synth::Type)i) == type) return (Synth::Type)i; } return Synth::SYNTH_TYPE_END; } //--------------------------------------------------------- // find //--------------------------------------------------------- Synth* SynthList::find(const QString& fileCompleteBaseName, const QString& pluginUri, const QString& pluginName) const { const bool f_empty = fileCompleteBaseName.isEmpty(); const bool u_empty = pluginUri.isEmpty(); const bool l_empty = pluginName.isEmpty(); for (ciSynthList i = begin(); i != end(); ++i) { if ((!u_empty || f_empty || fileCompleteBaseName == (*i)->completeBaseName()) && (u_empty || pluginUri == (*i)->uri()) && (!u_empty || l_empty || pluginName == (*i)->name())) return *i; } return nullptr; } //-------------------------------- // SynthIF //-------------------------------- //-------------------------------- // Methods for PluginIBase: //-------------------------------- inline PluginFeatures_t SynthIF::requiredFeatures() const { return PluginNoFeatures; } // Synth is not part of a rack plugin chain. It has no on/off (bypass) feature. inline bool SynthIF::hasBypass() const { return false; } // Synth is not part of a rack plugin chain. Always on. inline bool SynthIF::on() const { return true; } inline void SynthIF::setOn(bool /*val*/) { } inline unsigned long SynthIF::pluginID() { return 0; } inline int SynthIF::id() { return MusECore::MAX_PLUGINS; } // Set for special block reserved for synth. inline QString SynthIF::pluginLabel() const { return QString(); } inline QString SynthIF::name() const { return synti->name(); } inline QString SynthIF::lib() const { return QString(); } inline QString SynthIF::uri() const { return synti->uri(); } inline QString SynthIF::dirPath() const { return QString(); } inline QString SynthIF::fileName() const { return QString(); } inline QString SynthIF::titlePrefix() const { return QString(); } inline MusECore::AudioTrack* SynthIF::track() { return static_cast < MusECore::AudioTrack* > (synti); } inline void SynthIF::enableController(unsigned long, bool) { } inline bool SynthIF::controllerEnabled(unsigned long) const { return true;} inline void SynthIF::enableAllControllers(bool) { } inline void SynthIF::updateControllers() { } inline void SynthIF::activate() { } inline void SynthIF::deactivate() { } inline void SynthIF::writeConfiguration(int /*level*/, Xml& /*xml*/) { } inline bool SynthIF::readConfiguration(Xml& /*xml*/, bool /*readPreset*/) { return false; } inline unsigned long SynthIF::parameters() const { return 0; } inline unsigned long SynthIF::parametersOut() const { return 0; } inline void SynthIF::setParam(unsigned long, double) { } inline double SynthIF::param(unsigned long) const { return 0.0; } inline double SynthIF::paramOut(unsigned long) const { return 0.0; } inline const char* SynthIF::paramName(unsigned long) { return NULL; } inline const char* SynthIF::paramOutName(unsigned long) { return NULL; } LADSPA_PortRangeHint SynthIF::range(unsigned long) { LADSPA_PortRangeHint h; h.HintDescriptor = 0; h.LowerBound = 0.0; h.UpperBound = 1.0; return h; } LADSPA_PortRangeHint SynthIF::rangeOut(unsigned long) { LADSPA_PortRangeHint h; h.HintDescriptor = 0; h.LowerBound = 0.0; h.UpperBound = 1.0; return h; } inline bool SynthIF::hasLatencyOutPort() const { return false; } inline unsigned long SynthIF::latencyOutPortIndex() const { return 0; } inline float SynthIF::latency() const { return 0.0; } inline CtrlValueType SynthIF::ctrlValueType(unsigned long) const { return VAL_LINEAR; } inline CtrlList::Mode SynthIF::ctrlMode(unsigned long) const { return CtrlList::INTERPOLATE; } //------------------------------------------------------------------------- bool MessSynthIF::nativeGuiVisible() const { return _mess ? _mess->nativeGuiVisible() : false; } void MessSynthIF::showNativeGui(bool v) { if (v == nativeGuiVisible()) return; if (_mess) _mess->showNativeGui(v); } bool MessSynthIF::hasNativeGui() const { if (_mess) return _mess->hasNativeGui(); return false; } void MessSynthIF::guiHeartBeat() { if(_mess) _mess->guiHeartBeat(); } MidiPlayEvent MessSynthIF::receiveEvent() { if (_mess) return _mess->receiveEvent(); return MidiPlayEvent(); } int MessSynthIF::eventsPending() const { if (_mess) return _mess->eventsPending(); return 0; } void MessSynthIF::getNativeGeometry(int* x, int* y, int* w, int* h) const { if (_mess) _mess->getNativeGeometry(x, y, w, h); } void MessSynthIF::setNativeGeometry(int x, int y, int w, int h) { if (_mess) _mess->setNativeGeometry(x, y, w, h); } //--------------------------------------------------------- // findSynth // search for synthesizer base class // Each argument optional, can be empty. // If uri is not empty, the search is based solely on it, // the other arguments are ignored. //--------------------------------------------------------- static Synth* findSynth(const QString& sclass, const QString& uri, const QString& label, Synth::Type type = Synth::SYNTH_TYPE_END) { for (std::vector::iterator i = MusEGlobal::synthis.begin(); i != MusEGlobal::synthis.end(); ++i) { if( (!uri.isEmpty() || sclass.isEmpty() || (*i)->baseName() == sclass) && (uri.isEmpty() || ((*i)->uri() == uri)) && (!uri.isEmpty() || label.isEmpty() || ((*i)->name() == label)) && (type == Synth::SYNTH_TYPE_END || type == (*i)->synthType() || (type == Synth::LV2_SYNTH && (*i)->synthType() == Synth::LV2_EFFECT)) ) return *i; } fprintf(stderr, "synthi type:%d class:%s uri:%s label:%s not found\n", type, sclass.toLatin1().constData(), uri.toLatin1().constData(), label.toLatin1().constData()); QMessageBox::warning(0,"Synth not found!", "Synth: " + label + " not found. Settings are preserved if the project is saved."); return 0; } //--------------------------------------------------------- // createSynthInstance // create a synthesizer instance of class "label" //--------------------------------------------------------- static SynthI* createSynthInstance( const QString& sclass, const QString& uri, const QString& label, Synth::Type type = Synth::SYNTH_TYPE_END) { Synth* s = findSynth(sclass, uri, label, type); SynthI* si = nullptr; if (s) { si = new SynthI(); QString n; n.setNum(s->instances()); QString instance_name = s->name() + "-" + n; //Andrew Deryabin: check si->_sif for NULL as synth instance may not be created. if (si->initInstance(s, instance_name)) { delete si; fprintf(stderr, "createSynthInstance: synthi class:%s label:%s can not be created\n", sclass.toLatin1().constData(), label.toLatin1().constData()); QMessageBox::warning(0,"Synth instantiation error!", "Synth: " + label + " can not be created!"); return nullptr; } } else { fprintf(stderr, "createSynthInstance: synthi class:%s uri:%s label:%s not found\n", sclass.toLatin1().constData(), uri.toLatin1().constData(), label.toLatin1().constData()); QMessageBox::warning(0,"Synth not found!", "Synth: " + label + " not found, if the project is saved it will be removed from the project"); } return si; } //--------------------------------------------------------- // Synth //--------------------------------------------------------- Synth::Synth(const QFileInfo& fi, const QString& uri, QString label, QString descr, QString maker, QString ver, PluginFeatures_t reqFeatures) : info(fi), _uri(uri), _name(label), _description(descr), _maker(maker), _version(ver), _requiredFeatures(reqFeatures) { _instances = 0; } //--------------------------------------------------------- // instantiate //--------------------------------------------------------- void* MessSynth::instantiate(const QString& instanceName) { ++_instances; MusEGlobal::doSetuid(); QByteArray ba = info.filePath().toLatin1(); const char* path = ba.constData(); // load Synti dll void* handle = dlopen(path, RTLD_NOW); if (handle == nullptr) { fprintf(stderr, "Synth::instantiate: dlopen(%s) failed: %s\n", path, dlerror()); MusEGlobal::undoSetuid(); return nullptr; } MESS_Descriptor_Function msynth = (MESS_Descriptor_Function)dlsym(handle, "mess_descriptor"); if (!msynth) { const char *txt = dlerror(); if (txt) { fprintf(stderr, "Unable to find msynth_descriptor() function in plugin " "library file \"%s\": %s.\n" "Are you sure this is a MESS plugin file?\n", info.filePath().toLatin1().constData(), txt); MusEGlobal::undoSetuid(); return nullptr; } } _descr = msynth(); if (_descr == nullptr) { fprintf(stderr, "Synth::instantiate: no MESS descr found\n"); MusEGlobal::undoSetuid(); return nullptr; } QByteArray configPathBA = MusEGlobal::configPath.toLatin1(); QByteArray cachePathBA = MusEGlobal::cachePath.toLatin1(); QByteArray museGlobalLibBA = MusEGlobal::museGlobalLib.toLatin1(); QByteArray museGlobalShareBA = MusEGlobal::museGlobalShare.toLatin1(); QByteArray museUserBA = MusEGlobal::museUser.toLatin1(); QByteArray museProjectBA = MusEGlobal::museProject.toLatin1(); MessConfig mcfg(MusEGlobal::segmentSize, MusEGlobal::sampleRate, MusEGlobal::config.minMeter, MusEGlobal::config.useDenormalBias, MusEGlobal::denormalBias, MusEGlobal::config.leftMouseButtonCanDecrease, configPathBA.constData(), cachePathBA.constData(), museGlobalLibBA.constData(), museGlobalShareBA.constData(), museUserBA.constData(), museProjectBA.constData()); Mess* mess = _descr->instantiate((unsigned long long)MusEGlobal::muse->winId(), instanceName.toLatin1().constData(), &mcfg); MusEGlobal::undoSetuid(); return mess; } //--------------------------------------------------------- // SynthI //--------------------------------------------------------- SynthI::SynthI() : AudioTrack(AUDIO_SOFTSYNTH) { synthesizer = 0; _sif = 0; // Allow synths to be readable, ie send midi back to the host. _rwFlags = 3; _openFlags = 3; _readEnable = false; _writeEnable = false; setVolume(1.0); setPan(0.0); } SynthI::SynthI(const SynthI& si, int flags) : AudioTrack(si, flags) { synthesizer = 0; _sif = 0; // Allow synths to be readable, ie send midi back to the host. _rwFlags = 3; _openFlags = 3; _readEnable = false; _writeEnable = false; setVolume(1.0); setPan(0.0); Synth* s = si.synth(); if (s) { QString n; n.setNum(s->instances()); QString instance_name = s->name() + "-" + n; if(!initInstance(s, instance_name)) { // false if success return; } } fprintf(stderr, "SynthI copy ctor: error initializing synth s:%p\n", s); } //--------------------------------------------------------- // ~SynthI //--------------------------------------------------------- SynthI::~SynthI() { deactivate2(); deactivate3(); } //--------------------------------------------------------- // height in arranger //--------------------------------------------------------- int SynthI::height() const { if (_isVisible) return _height; return 0; } //--------------------------------------------------------- // open //--------------------------------------------------------- QString SynthI::open() { // Make it behave like a regular midi device. _readEnable = false; _writeEnable = (_openFlags & 0x01); _state = QString("OK"); return _state; } //--------------------------------------------------------- // close //--------------------------------------------------------- void SynthI::close() { _readEnable = false; _writeEnable = false; _state = QString("Closed"); } //--------------------------------------------------------- // processMidi //--------------------------------------------------------- void SynthI::processMidi(unsigned int /*curFrame*/) { } //--------------------------------------------------------- // setName //--------------------------------------------------------- void SynthI::setName(const QString& s) { AudioTrack::setName(s); MidiDevice::setName(s); } //--------------------------------------------------------- // recordEvent //--------------------------------------------------------- void SynthI::recordEvent(MidiRecordEvent& event) { if(MusEGlobal::audio->isPlaying()) event.setLoopNum(MusEGlobal::audio->loopCount()); if (MusEGlobal::midiInputTrace) { fprintf(stderr, "MidiInput from synth: "); dumpMPEvent(&event); } int typ = event.type(); if(_port != -1) { int idin = MusEGlobal::midiPorts[_port].syncInfo().idIn(); //--------------------------------------------------- // filter some SYSEX events //--------------------------------------------------- if (typ == ME_SYSEX) { const unsigned char* p = event.constData(); int n = event.len(); if (n >= 4) { if ((p[0] == 0x7f) && ((p[1] == 0x7f) || (idin == 0x7f) || (p[1] == idin))) { if (p[2] == 0x06) { MusEGlobal::midiSyncContainer.mmcInput(_port, p, n); return; } if (p[2] == 0x01) { MusEGlobal::midiSyncContainer.mtcInputFull(_port, p, n); return; } } else if (p[0] == 0x7e) { MusEGlobal::midiSyncContainer.nonRealtimeSystemSysex(_port, p, n); return; } } } else // Trigger general activity indicator detector. Sysex has no channel, don't trigger. MusEGlobal::midiPorts[_port].syncInfo().trigActDetect(event.channel()); } // // process midi event input filtering and // transformation // processMidiInputTransformPlugins(event); if (filterEvent(event, MusEGlobal::midiRecordType, false)) return; if (!applyMidiInputTransformation(event)) { if (MusEGlobal::midiInputTrace) fprintf(stderr, " midi input transformation: event filtered\n"); return; } // TODO Maybe support this later, but for now it's not a good idea to control from the synths. // Especially since buggy ones may repeat events multiple times. #if 1 // // transfer noteOn and Off events to gui for step recording and keyboard // remote control (changed by flo93: added noteOff-events) // if (typ == ME_NOTEON) { int pv = ((event.dataA() & 0xff)<<8) + (event.dataB() & 0xff); MusEGlobal::song->putEvent(pv); } else if (typ == ME_NOTEOFF) { int pv = ((event.dataA() & 0xff)<<8) + (0x00); //send an event with velo=0 MusEGlobal::song->putEvent(pv); } #endif // Do not bother recording if it is NOT actually being used by a port. // Because from this point on, process handles things, by selected port. if(_port == -1) return; // Split the events up into channel fifos. Special 'channel' number 17 for sysex events. unsigned int ch = (typ == ME_SYSEX)? MusECore::MUSE_MIDI_CHANNELS : event.channel(); if(_recordFifo[ch].put(event)) fprintf(stderr, "SynthI::recordEvent: fifo channel %d overflow\n", ch); } RouteCapabilitiesStruct SynthI::routeCapabilities() const { RouteCapabilitiesStruct s = AudioTrack::routeCapabilities(); s._trackChannels._inChannels = totalInChannels(); s._trackChannels._inRoutable = (s._trackChannels._inChannels != 0); return s; } void SynthI::getMapItem(int channel, int patch, int index, DrumMap& dest_map, int #ifdef _USE_INSTRUMENT_OVERRIDES_ overrideType #endif ) const { bool has_note_name_list = false; QString note_name; if(_sif) { // true = Want percussion names, not melodic. has_note_name_list = _sif->getNoteSampleName(true, channel, patch, index, ¬e_name); } // Not found? Search the global mapping list. const patch_drummap_mapping_list_t* def_pdml = genericMidiInstrument->get_patch_drummap_mapping(channel, true); // Include default. if(def_pdml) { ciPatchDrummapMapping_t ipdm = def_pdml->find(patch, true); // Include default. if(ipdm == def_pdml->end()) { // Not found? Is there a default patch mapping? #ifdef _USE_INSTRUMENT_OVERRIDES_ if(overrideType & WorkingDrumMapEntry::InstrumentDefaultOverride) #endif ipdm = def_pdml->find(CTRL_PROGRAM_VAL_DONT_CARE, true); // Include default. if(ipdm != def_pdml->end()) { dest_map = (*ipdm).drummap[index]; if(has_note_name_list) // It has a note name list. The note name can be blank meaning no note found. dest_map.name = note_name; return; } } } dest_map = iNewDrumMap[index]; if(has_note_name_list) // It has a note name list. The note name can be blank meaning no note found. dest_map.name = note_name; } //--------------------------------------------------------- // init //--------------------------------------------------------- bool MessSynthIF::init(Synth* s, SynthI* si) { _mess = (Mess*)((MessSynth*)s)->instantiate(si->name()); return (_mess != nullptr); } int MessSynthIF::channels() const { return _mess->channels(); } int MessSynthIF::totalOutChannels() const { return _mess->channels(); } int MessSynthIF::totalInChannels() const { return 0; } SynthIF* MessSynth::createSIF(SynthI* si) { MessSynthIF* sif = new MessSynthIF(si); if (!sif->init(this, si)) { delete sif; sif = nullptr; } return sif; } //--------------------------------------------------------- // initInstance // returns false on success //--------------------------------------------------------- bool SynthI::initInstance(Synth* s, const QString& instanceName) { synthesizer = s; setName(instanceName); // set midi device name setIName(instanceName); // set instrument name // Persistent storage. Even if the synth is not found, allow the track to load. if(!s) { _sif = nullptr; return true; } _sif = s->createSIF(this); //Andrew Deryabin: add check for NULL here to get rid of segfaults if(_sif == nullptr) { return true; //true if error (?) } AudioTrack::setTotalOutChannels(_sif->totalOutChannels()); AudioTrack::setTotalInChannels(_sif->totalInChannels()); //--------------------------------------------------- // read available controller from synti //--------------------------------------------------- int id = 0; MidiControllerList* cl = MidiInstrument::controller(); for (;;) { QString name; int ctrl; int min; int max; int initval = CTRL_VAL_UNKNOWN; id = _sif->getControllerInfo(id, &name, &ctrl, &min, &max, &initval); if (id == 0) break; // Override existing program controller. iMidiController i = cl->end(); if(ctrl == CTRL_PROGRAM) { for(i = cl->begin(); i != cl->end(); ++i) { if(i->second->num() == CTRL_PROGRAM) { delete i->second; cl->del(i); break; } } } MidiController* c = new MidiController(name, ctrl, min, max, initval, initval); cl->add(c); } // Restore the midi state... EventList* iel = midiState(); if (!iel->empty()) { for (iEvent i = iel->begin(); i != iel->end(); ++i) { Event ev = i->second; // p4.0.27 A kludge to support old midistates by wrapping them in the proper header. if(ev.type() == Sysex && _tmpMidiStateVersion < SYNTH_MIDI_STATE_SAVE_VERSION) { int len = ev.dataLen(); if(len > 0) { const unsigned char* data = ev.data(); const unsigned char* hdr; // Get the unique header for the synth. int hdrsz = _sif->oldMidiStateHeader(&hdr); if(hdrsz > 0) { int newlen = hdrsz + len; unsigned char* d = new unsigned char[newlen]; memcpy(d, hdr, hdrsz); memcpy(d + hdrsz, data, len); ev.setData(d, newlen); delete[] d; } } } MidiPlayEvent pev = ev.asMidiPlayEvent(0, 0, 0); _userEventBuffers->put(pev); } iel->clear(); } unsigned long idx = 0; for (std::vector::iterator i = _initConfig.initParams.begin(); i != _initConfig.initParams.end(); ++i, ++idx) _sif->setParameter(idx, *i); // p3.3.40 Since we are done with the (sometimes huge) initial parameters list, clear it. // TODO: Decide: Maybe keep them around for a 'reset to previously loaded values' (revert) command? ... _initConfig.initParams.clear(); //call SynthIF::setCustomData(...) with accumulated custom params _sif->setCustomData(_initConfig.accumulatedCustomParams); _initConfig.accumulatedCustomParams.clear(); return false; } //--------------------------------------------------------- // pbForwardShiftFrames //--------------------------------------------------------- unsigned int SynthI::pbForwardShiftFrames() const { return MusEGlobal::segmentSize; } //--------------------------------------------------------- // getControllerInfo //--------------------------------------------------------- int MessSynthIF::getControllerInfo(int id, QString* name, int* ctrl, int* min, int* max, int* initval) { int i_ctrl; int i_min; int i_max; int i_initval; const char* s_name; int ret = _mess->getControllerInfo(id, &s_name, &i_ctrl, &i_min, &i_max, &i_initval); if(ctrl) *ctrl = i_ctrl; if(min) *min = i_min; if(max) *max = i_max; if(initval) *initval = i_initval; if(name) *name = QString(s_name); return ret; } bool MessSynthIF::getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const { if(!name) return false; const char* str; // Returns true if a note name list was found. // str is NULL if no note was found. // drum = Want percussion names, not melodic. if(_mess->getNoteSampleName(drum, channel, patch, note, &str)) { // str could be null. *name = QString(str); // A note name list was found. return true; } // No note name list was found. return false; } //--------------------------------------------------------- // SynthI::deactivate //--------------------------------------------------------- void SynthI::deactivate2() { removeMidiInstrument(this); MusEGlobal::midiDevices.remove(this); if (midiPort() != -1) { // synthi is attached MusEGlobal::midiPorts[midiPort()].setMidiDevice(0); } } //--------------------------------------------------------- // deactivate3 //--------------------------------------------------------- void SynthI::deactivate3() { //Andrew Deryabin: add checks for NULLness of _sif and syntheeizer instances if(_sif) { _sif->deactivate3(); } //synthesizer->incInstances(-1); // Moved below by Tim. p3.3.14 if(MusEGlobal::debugMsg) fprintf(stderr, "SynthI::deactivate3 deleting _sif...\n"); if(_sif) { delete _sif; _sif = 0; } if(MusEGlobal::debugMsg) fprintf(stderr, "SynthI::deactivate3 decrementing synth instances...\n"); if(synthesizer) synthesizer->incInstances(-1); } void MessSynthIF::deactivate3() { if (_mess) { delete _mess; _mess = 0; } } //--------------------------------------------------------- // initMidiSynth // search for software MusEGlobal::synthis and advertise //--------------------------------------------------------- void initMidiSynth() { const MusEPlugin::PluginScanList& scan_list = MusEPlugin::pluginList; for(MusEPlugin::ciPluginScanList isl = scan_list.begin(); isl != scan_list.end(); ++isl) { const MusEPlugin::PluginScanInfoRef inforef = *isl; const MusEPlugin::PluginScanInfoStruct& info = inforef->info(); switch(info._type) { case MusEPlugin::PluginScanInfoStruct::PluginTypeMESS: { if(MusEGlobal::loadMESS) { const QString uri = PLUGIN_GET_QSTRING(info._uri); // Make sure it doesn't already exist. if(const Synth* sy = MusEGlobal::synthis.find( PLUGIN_GET_QSTRING(info._completeBaseName), uri, PLUGIN_GET_QSTRING(info._name))) { fprintf(stderr, "Ignoring MESS synth name:%s uri:%s path:%s duplicate of path:%s\n", PLUGIN_GET_CSTRING(info._name), PLUGIN_GET_CSTRING(info._uri), PLUGIN_GET_CSTRING(info.filePath()), sy->filePath().toLatin1().constData()); } else { MusEGlobal::synthis.push_back( new MessSynth(PLUGIN_GET_QSTRING(info.filePath()), uri, PLUGIN_GET_QSTRING(info._name), PLUGIN_GET_QSTRING(info._description), QString(""), PLUGIN_GET_QSTRING(info._version))); } } } break; case MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA: case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI: case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeLV2: case MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown: case MusEPlugin::PluginScanInfoStruct::PluginTypeNone: case MusEPlugin::PluginScanInfoStruct::PluginTypeAll: break; } } if(MusEGlobal::debugMsg) fprintf(stderr, "%zd soft synth found\n", MusEGlobal::synthis.size()); } //--------------------------------------------------------- // createSynthI // create a synthesizer instance of class "label" // If insertAt is valid, inserts before insertAt. Else at the end after all tracks. //--------------------------------------------------------- SynthI* Song::createSynthI(const QString& sclass, const QString& uri, const QString& label, Synth::Type type, Track* insertAt) { SynthI* si = createSynthInstance(sclass, uri, label, type); if(!si) return nullptr; int idx = insertAt ? _tracks.index(insertAt) : -1; OutputList* ol = MusEGlobal::song->outputs(); // Add an omnibus default route to master (first audio output) if (!ol->empty()) { AudioOutput* ao = ol->front(); // AddTrack operation 'mirrors' the route. static_cast(si)->outRoutes()->push_back(Route(ao)); } MusEGlobal::song->applyOperation(UndoOp(UndoOp::AddTrack, idx, si)); return si; } //--------------------------------------------------------- // write //--------------------------------------------------------- void SynthI::write(int level, Xml& xml) const { xml.tag(level++, "SynthI"); AudioTrack::writeProperties(level, xml); xml.strTag(level, "synthType", synthType2String(synth() ? synth()->synthType() : _initConfig._type)); const QString uri = synth() ? synth()->uri() : _initConfig._uri; if(uri.isEmpty()) { xml.strTag(level, "class", synth() ? synth()->baseName() : _initConfig._class); } else { xml.strTag(level, "uri", uri); } // To support plugins like dssi-vst where all the baseNames are the same 'dssi-vst' and the label is the name of the dll file. xml.strTag(level, "label", synth() ? synth()->name() : _initConfig._label); if(openFlags() != 1) xml.intTag(level, "openFlags", openFlags()); if(_sif) _sif->cquirks().write(level, xml); //--------------------------------------------- // if soft synth is attached to a midi port, // write out port number //--------------------------------------------- if (midiPort() != -1) xml.intTag(level, "port", midiPort()); if(_sif) { if (hasGui()) { xml.intTag(level, "guiVisible", guiVisible()); int x, y, w, h; w = 0; h = 0; getGeometry(&x, &y, &w, &h); if (h || w) xml.qrectTag(level, "geometry", QRect(x, y, w, h)); } if (hasNativeGui()) { xml.intTag(level, "nativeGuiVisible", nativeGuiVisible()); int x, y, w, h; w = 0; h = 0; getNativeGeometry(&x, &y, &w, &h); if (h || w) xml.qrectTag(level, "nativeGeometry", QRect(x, y, w, h)); } } else { if(_initConfig._guiVisible) xml.intTag(level, "guiVisible", _initConfig._guiVisible); if (_initConfig._geometry.height() || _initConfig._geometry.width()) xml.qrectTag(level, "geometry", _initConfig._geometry); if(_initConfig._nativeGuiVisible) xml.intTag(level, "nativeGuiVisible", _initConfig._nativeGuiVisible); if (_initConfig._nativeGeometry.height() || _initConfig._nativeGeometry.width()) xml.qrectTag(level, "nativeGeometry", _initConfig._nativeGeometry); } _initConfig._stringParamMap.write(level, xml, "stringParam"); if(_sif) { _sif->write(level, xml); } else { // Try to preserve existing settings... if(!_initConfig.initParams.empty()) { const int sz = _initConfig.initParams.size(); for(int i = 0; i < sz; ++i) xml.doubleTag(level, "param", _initConfig.initParams.at(i)); } // Try to preserve existing settings... if(!_initConfig.accumulatedCustomParams.empty()) { const int sz = _initConfig.accumulatedCustomParams.size(); for(int i = 0; i < sz; ++i) { const QString& cps = _initConfig.accumulatedCustomParams.at(i); // FIXME: For some reason this does not print the first newline of cps, // which should exist (all the others do). xml.strTag(level, "customData", cps); } } // Try to preserve existing settings... const EventList* msl = midiState(); if(msl && !msl->empty()) { xml.tag(level++, "midistate version=\"%d\"", SYNTH_MIDI_STATE_SAVE_VERSION); for(ciEvent ie = msl->cbegin(); ie != msl->cend(); ++ie) ie->second.write(level, xml, 0); xml.etag(level--, "midistate"); } } xml.etag(level, "SynthI"); } void MessSynthIF::write(int level, Xml& xml) const { //--------------------------------------------- // dump current state of synth //--------------------------------------------- int len = 0; const unsigned char* p; _mess->getInitData(&len, &p); if (len) { ///xml.tag(level++, "midistate"); xml.tag(level++, "midistate version=\"%d\"", SYNTH_MIDI_STATE_SAVE_VERSION); xml.nput(level++, "\n", len); xml.nput(level, ""); for (int i = 0; i < len; ++i) { if (i && ((i % 16) == 0)) { xml.nput("\n"); xml.nput(level, ""); } xml.nput("%02x ", p[i] & 0xff); } xml.nput("\n"); xml.tag(level--, "/event"); xml.etag(level--, "midistate"); } } //--------------------------------------------------------- // SynthI::read //--------------------------------------------------------- void SynthI::read(Xml& xml) { int port = -1; int oflags = 1; PluginQuirks quirks; for (;;) { Xml::Token token = xml.parse(); const QString tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto synth_read_end; case Xml::TagStart: if (tag == "synthType") _initConfig._type = string2SynthType(xml.parse1()); else if (tag == "class") _initConfig._class = xml.parse1(); else if (tag == "uri") _initConfig._uri = xml.parse1(); else if (tag == "label") _initConfig._label = xml.parse1(); else if (tag == "openFlags") oflags = xml.parseInt(); else if (tag == "quirks") quirks.read(xml); else if (tag == "port") port = xml.parseInt(); else if (tag == "guiVisible") _initConfig._guiVisible = xml.parseInt(); else if (tag == "nativeGuiVisible") _initConfig._nativeGuiVisible = xml.parseInt(); else if (tag == "midistate") readMidiState(xml); else if (tag == "param") { double val = xml.parseDouble(); _initConfig.initParams.push_back(val); } else if (tag == "stringParam") _initConfig._stringParamMap.read(xml, tag); else if (tag == "geometry") _initConfig._geometry = readGeometry(xml, tag); else if (tag == "nativeGeometry") _initConfig._nativeGeometry = readGeometry(xml, tag); else if (tag == "customData") { //just place tag contents in accumulatedCustomParams QString customData = xml.parse1(); if(!customData.isEmpty()){ _initConfig.accumulatedCustomParams.push_back(customData); } } else if (AudioTrack::readProperties(xml, tag)) xml.unknown("softSynth"); break; case Xml::TagEnd: if (tag == "SynthI") { // NOTICE: This is a hack to quietly change songs to use the new 'fluid_synth' name instead of 'fluidsynth'. // Recent linker changes required the name change in fluidsynth's cmakelists. Nov 8, 2011 By Tim. if(_initConfig._class == QString("fluidsynth") && (_initConfig._type == Synth::SYNTH_TYPE_END || _initConfig._type == Synth::MESS_SYNTH) && (_initConfig._label.isEmpty() || _initConfig._label == QString("FluidSynth")) ) _initConfig._class = QString("fluid_synth"); Synth* s = findSynth( _initConfig._class, _initConfig._uri, _initConfig._label, _initConfig._type); // Persistent storage: If synth is not found allow the track to load. // It's OK if s is NULL. initInstance needs to do a few things. initInstance(s, name()); if(_sif) _sif->quirks() = quirks; setOpenFlags(oflags); MusEGlobal::song->insertTrack0(this, -1); if (port != -1 && port < MusECore::MIDI_PORTS) MusEGlobal::midiPorts[port].setMidiDevice(this); // DELETETHIS 5 // Now that the track has been added to the lists in insertTrack2(), // if it's a dssi synth, OSC can find the synth, and initialize (and show) its native gui. // No, initializing OSC without actually showing the gui doesn't work, at least for // dssi-vst plugins - without showing the gui they exit after ten seconds. //initGui(); setNativeGeometry(_initConfig._nativeGeometry.x(), _initConfig._nativeGeometry.y(), _initConfig._nativeGeometry.width(), _initConfig._nativeGeometry.height()); showNativeGui(_initConfig._nativeGuiVisible); mapRackPluginsToControllers(); setGeometry(_initConfig._geometry.x(), _initConfig._geometry.y(), _initConfig._geometry.width(), _initConfig._geometry.height()); showGui(_initConfig._guiVisible); // Now that the track has been added to the lists in insertTrack2(), if it's a dssi synth // OSC can find the track and its plugins, and start their native guis if required... showPendingPluginNativeGuis(); return; } default: break; } } synth_read_end: AudioTrack::mapRackPluginsToControllers(); } //--------------------------------------------------------- // getPatchName //--------------------------------------------------------- QString MessSynthIF::getPatchName(int channel, int prog, bool drum) const { if (_mess) return QString(_mess->getPatchName(channel, prog, drum)); return ""; } //--------------------------------------------------------- // populatePatchPopup //--------------------------------------------------------- void MessSynthIF::populatePatchPopup(MusEGui::PopupMenu* menu, int ch, bool) { MusEGui::PopupMenu* hbank_menu = 0; MusEGui::PopupMenu* lbank_menu = 0; menu->clear(); const MidiPatch* mp = _mess->getPatchInfo(ch, 0); while (mp) { if(mp->typ == MP_TYPE_HBANK) { lbank_menu = 0; hbank_menu = new MusEGui::PopupMenu(QString(mp->name), menu, true); menu->addMenu(hbank_menu); } else if(mp->typ == MP_TYPE_LBANK) { lbank_menu = new MusEGui::PopupMenu(QString(mp->name), menu, true); hbank_menu->addMenu(lbank_menu); } else { const int hb = mp->hbank & 0xff; const int lb = mp->lbank & 0xff; const int pr = mp->prog & 0xff; const int id = (hb << 16) | (lb << 8) | pr; const bool vhb = hb != 0xff; const bool vlb = lb != 0xff; const bool vpr = pr != 0xff; QString astr; if(vhb || vlb || vpr) { if(vhb) astr += QString::number(hb + 1) + QString(":"); if(vlb) astr += QString::number(lb + 1) + QString(":"); else if(vhb) astr += QString("--:"); if(vpr) astr += QString::number(pr + 1); else if(vhb && vlb) astr += QString("--"); astr += QString(" "); } astr += QString(mp->name); MusEGui::PopupMenu* m; if(lbank_menu) m = lbank_menu; else if(hbank_menu) m = hbank_menu; else m = menu; QAction *act = m->addAction(astr); act->setData(id); } mp = _mess->getPatchInfo(ch, mp); } } //================================================ // BEGIN Latency correction/compensation routines. //================================================ //--------------------------------------------------------- // getWorstPluginLatencyAudio //--------------------------------------------------------- float SynthI::getWorstPluginLatencyAudio() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._worstPluginLatencyProcessed) return _latencyInfo._worstPluginLatency; // Include the synth's own latency. float worst_lat = _sif ? _sif->latency() : 0.0f; // Include the effects rack latency. if(_efxPipe) worst_lat += _efxPipe->latency(); _latencyInfo._worstPluginLatency = worst_lat; _latencyInfo._worstPluginLatencyProcessed = true; return _latencyInfo._worstPluginLatency; } //--------------------------------------------------------- // setCorrectionLatencyInfo //--------------------------------------------------------- TrackLatencyInfo& SynthI::setCorrectionLatencyInfo(bool input, float finalWorstLatency, float callerBranchLatency) { const bool passthru = canPassThruLatency(); float worst_self_latency = 0.0f; if(!input && !off()) { //worst_self_latency = getWorstSelfLatency(); worst_self_latency = getWorstSelfLatencyAudio(); const float worst_midi = getWorstSelfLatencyMidi(false /*playback*/); if(worst_midi > worst_self_latency) worst_self_latency = worst_midi; } // The _trackLatency should already be calculated in the dominance scan. const float branch_lat = callerBranchLatency + worst_self_latency; if(!off() && (passthru || input)) { // We want the AudioTrack in routes, not the MidiDevice in routes. RouteList* rl = AudioTrack::inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::TRACK_ROUTE || !ir->track || ir->track->isMidiTrack()) continue; Track* track = ir->track; //if(!off() && !track->off() && (passthru || input)) if(!track->off()) track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } if(openFlags() & 1 /*write*/) { const int port = midiPort(); if(port >= 0 && port < MusECore::MIDI_PORTS) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; //if(!off() && !track->off() && (openFlags() & 1 /*write*/) && (passthru || input)) if(!track->off()) track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* mrl = mp->inRoutes(); for (iRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; //if(!off() && !track->off() && (openFlags() & 1 /*write*/) && (passthru || input)) if(!track->off()) track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } break; default: break; } } #endif } } // Special for the built-in metronome. //if(!off() && !MusECore::metronome->off() && (passthru || input) && sendMetronome()) if(!MusECore::metronome->off() && sendMetronome()) { MusECore::metronome->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } // Special for the transport source. //if(!off() && /*!_transportSource.off() &&*/ usesTransportSource() && (passthru || input)) if(/*!_transportSource.off() &&*/ usesTransportSource()) { _transportSource.setCorrectionLatencyInfo( false, finalWorstLatency, branch_lat, MusEGlobal::config.commonProjectLatency); } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off()) { if(input) { } else { if(canCorrectOutputLatency() && _latencyInfo._canCorrectOutputLatency) { float corr = 0.0f; if(MusEGlobal::config.commonProjectLatency) corr -= finalWorstLatency; corr -= branch_lat; // The _sourceCorrectionValue is initialized to zero. // Whichever calling branch needs the most correction gets it. if(corr < _latencyInfo._sourceCorrectionValue) _latencyInfo._sourceCorrectionValue = corr; } } //fprintf(stderr, "AudioTrack::setCorrectionLatencyInfo() name:%s finalWorstLatency:%f branch_lat:%f corr:%f _sourceCorrectionValue:%f\n", // name().toLatin1().constData(), finalWorstLatency, branch_lat, corr, _latencyInfo._sourceCorrectionValue); } return _latencyInfo; } bool SynthI::isLatencyInputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyInputTerminalProcessed) return _latencyInfo._isLatencyInputTerminal; // Ultimately if the track is off there is no audio or midi processing, so it's a terminal. if(off()) { _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } if(!canRecordMonitor() || (MusEGlobal::config.monitoringAffectsLatency && isRecMonitored())) { const RouteList* rl = AudioTrack::outRoutes(); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO } else { Track* track = ir->track; if(track->off()) // || //(track->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !track->isRecMonitored()))) //&& track->canRecord() && !track->recordFlag())) continue; _latencyInfo._isLatencyInputTerminal = false; _latencyInfo._isLatencyInputTerminalProcessed = true; return false; } break; default: break; } } } const int port = midiPort(); if((openFlags() & 1 /*write*/) && port >= 0 && port < MusECore::MIDI_PORTS) { MidiPort* mp = &MusEGlobal::midiPorts[port]; const RouteList* mrl = mp->outRoutes(); for (ciRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { Track* track = ir->track; if(track->off()) // || //(track->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !track->isRecMonitored()))) //&& atrack->canRecord() && !atrack->recordFlag())) continue; _latencyInfo._isLatencyInputTerminal = false; _latencyInfo._isLatencyInputTerminalProcessed = true; return false; } //else //{ // // TODO ? //} break; default: break; } } } _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } bool SynthI::isLatencyOutputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyOutputTerminalProcessed) return _latencyInfo._isLatencyOutputTerminal; const RouteList* rl = AudioTrack::outRoutes(); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO ? } else { Track* track = ir->track; if(track->off()) // || //(track->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !track->isRecMonitored()))) //&& track->canRecord() && !track->recordFlag())) continue; _latencyInfo._isLatencyOutputTerminal = false; _latencyInfo._isLatencyOutputTerminalProcessed = true; return false; } break; default: break; } } const int port = midiPort(); if((openFlags() & 1 /*write*/) && port >= 0 && port < MusECore::MIDI_PORTS) { MidiPort* mp = &MusEGlobal::midiPorts[port]; const RouteList* mrl = mp->outRoutes(); for (ciRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { Track* track = ir->track; if(track->off()) // || //(track->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !track->isRecMonitored()))) //&& atrack->canRecord() && !atrack->recordFlag())) continue; _latencyInfo._isLatencyOutputTerminal = false; _latencyInfo._isLatencyOutputTerminalProcessed = true; return false; } //else //{ // // TODO ? //} break; default: break; } } } _latencyInfo._isLatencyOutputTerminal = true; _latencyInfo._isLatencyOutputTerminalProcessed = true; return true; } //--------------------------------------------------------- // getDominanceInfo //--------------------------------------------------------- TrackLatencyInfo& SynthI::getDominanceInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._canDominateInputProcessed) || (!input && _latencyInfo._canDominateProcessed)) return _latencyInfo; // Get the default domination for this track type. bool can_dominate_lat = input ? canDominateInputLatency() : canDominateOutputLatency(); bool can_correct_lat = canCorrectOutputLatency(); const bool passthru = canPassThruLatency(); bool item_found = false; if(!off() && (passthru || input)) { // We want the AudioTrack in routes, not the MidiDevice in routes. RouteList* rl = AudioTrack::inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO ? } else { Track* track = ir->track; //if(!off() && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } break; default: break; } } if(openFlags() & 1 /*write*/) { const int port = midiPort(); if(port >= 0 && port < MusECore::MIDI_PORTS) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; //if(!off() && !track->off() && (openFlags() & 1 /*write*/) && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* mrl = mp->inRoutes(); for (iRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; // if(ir->channel < 0) // all_chans = true; // else // used_chans[ir->channel] = true; //if(!off() && !track->off() && (openFlags() & 1 /*write*/) && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_out_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. can_dominate_out_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } break; default: break; } } #endif } } // Special for the built-in metronome. //if(!off() && !MusECore::metronome->off() && (passthru || input) && sendMetronome()) if(!MusECore::metronome->off() && sendMetronome()) { const TrackLatencyInfo& li = MusECore::metronome->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } // Special for the transport source. //if(!off() && /*!_transportSource.off() &&*/ usesTransportSource() && (passthru || input)) if(/*!_transportSource.off() &&*/ usesTransportSource()) { const TrackLatencyInfo& li = _transportSource.getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off()) { if(input) { _latencyInfo._canDominateInputLatency = can_dominate_lat; } else { _latencyInfo._canDominateOutputLatency = can_dominate_lat; // If any of the branches can dominate, then this node cannot correct. _latencyInfo._canCorrectOutputLatency = can_correct_lat && !can_dominate_lat; } } if(input) _latencyInfo._canDominateInputProcessed = true; else _latencyInfo._canDominateProcessed = true; return _latencyInfo; } //--------------------------------------------------------- // getDominanceLatencyInfo //--------------------------------------------------------- TrackLatencyInfo& SynthI::getDominanceLatencyInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._dominanceInputProcessed) || (!input && _latencyInfo._dominanceProcessed)) return _latencyInfo; float route_worst_latency = 0.0f; const bool passthru = canPassThruLatency(); bool item_found = false; float worst_self_latency = 0.0f; if(!input && !off()) { worst_self_latency = getWorstSelfLatencyAudio(); const float worst_midi = getWorstSelfLatencyMidi(false /*playback*/); if(worst_midi > worst_self_latency) worst_self_latency = worst_midi; } if(!off() && (passthru || input)) { // We want the AudioTrack in routes, not the MidiDevice in routes. RouteList* rl = AudioTrack::inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO ? } else { Track* track = ir->track; //if(!off() && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } break; default: break; } } if(openFlags() & 1 /*write*/) { const int port = midiPort(); if(port >= 0 && port < MusECore::MIDI_PORTS) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; //if(!off() && !track->off() && (openFlags() & 1 /*write*/) && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* mrl = mp->inRoutes(); for (iRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; // if(ir->channel < 0) // all_chans = true; // else // used_chans[ir->channel] = true; //if(!off() && !track->off() && (openFlags() & 1 /*write*/) && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } break; default: break; } } #endif } } // Special for the built-in metronome. //if(!off() && !MusECore::metronome->off() && (passthru || input) && sendMetronome()) if(!MusECore::metronome->off() && sendMetronome()) { const TrackLatencyInfo& li = MusECore::metronome->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } // Special for the transport source. //if(!off() && /*!_transportSource.off() &&*/ usesTransportSource() && (passthru || input)) if(/*!_transportSource.off() &&*/ usesTransportSource()) { const TrackLatencyInfo& li = _transportSource.getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off()) { if(input) { _latencyInfo._inputLatency = route_worst_latency; } else { if(passthru) { _latencyInfo._outputLatency = worst_self_latency + route_worst_latency; _latencyInfo._inputLatency = route_worst_latency; } else { _latencyInfo._outputLatency = worst_self_latency + _latencyInfo._sourceCorrectionValue; } } } if(input) _latencyInfo._dominanceInputProcessed = true; else _latencyInfo._dominanceProcessed = true; return _latencyInfo; } //--------------------------------------------------------- // getLatencyInfo //--------------------------------------------------------- TrackLatencyInfo& SynthI::getLatencyInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._inputProcessed) || (!input && _latencyInfo._processed)) return _latencyInfo; float route_worst_latency = _latencyInfo._inputLatency; const bool passthru = canPassThruLatency(); if(passthru || input) { // We want the AudioTrack in routes, not the MidiDevice in routes. RouteList* rl = AudioTrack::inRoutes(); // Now that we know the worst-case latency of the connected branches, // adjust each of the conveniently stored temporary latency values // in the routes according to whether they can dominate... for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::TRACK_ROUTE || !ir->track || ir->track->isMidiTrack()) continue; Track* track = ir->track; // Default to zero. ir->audioLatencyOut = 0.0f; if(!off() && !track->off()) { const TrackLatencyInfo& li = track->getLatencyInfo(false); const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. ir->audioLatencyOut = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)ir->audioLatencyOut < 0) ir->audioLatencyOut = 0.0f; } } } const int port = midiPort(); if(port >= 0 && port < MusECore::MIDI_PORTS) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; // Default to zero. // TODO: FIXME: Where to store? We have no route to store it in. //ir->audioLatencyOut = 0.0f; //li._latencyOutMidiTrack = 0.0f; if(!off() && !track->off() && (openFlags() & 1 /*write*/)) { TrackLatencyInfo& li = track->getLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency; if(participate) { // TODO: FIXME: Where to store? We have no route to store it in. // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. // ir->audioLatencyOut = route_worst_latency - ir->audioLatencyOut; // // Should not happen, but just in case. // if((long int)ir->audioLatencyOut < 0) // ir->audioLatencyOut = 0.0f; // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet // because we don't have multiple Midi Track outputs yet, only a single output port. // So we must store this information here just for Midi Tracks. li._latencyOutMidiTrack = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)li._latencyOutMidiTrack < 0) li._latencyOutMidiTrack = 0.0f; } } } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* mrl = mp->inRoutes(); for (iRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; // if(ir->channel < 0) // all_chans = true; // else // used_chans[ir->channel] = true; // Default to zero. ir->audioLatencyOut = 0.0f; if(!off() && !track->off() && (openFlags() & 1 /*write*/) { TrackLatencyInfo& li = track->getLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency; if(participate) { // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. ir->audioLatencyOut = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)ir->audioLatencyOut < 0) ir->audioLatencyOut = 0.0f; } } } break; default: break; } } #endif } // Special for the built-in metronome. // Default to zero. _latencyInfo._latencyOutMetronome = 0.0f; if(!off() && !MusECore::metronome->off() && sendMetronome()) { TrackLatencyInfo& li = MusECore::metronome->getLatencyInfo(false); const bool participate = li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency; if(participate) { // TODO: FIXME: Where to store? We have no route to store it in. // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. // ir->audioLatencyOut = route_worst_latency - ir->audioLatencyOut; // // Should not happen, but just in case. // if((long int)ir->audioLatencyOut < 0) // ir->audioLatencyOut = 0.0f; // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet // because we don't have multiple Midi Track outputs yet, only a single output port. // So we must store this information here just for Midi Tracks. li._latencyOutMetronome = route_worst_latency - li._latencyOutMetronome; // Should not happen, but just in case. if((long int)li._latencyOutMetronome < 0) li._latencyOutMetronome = 0.0f; } } // Special for the transport source. // Default to zero. _transportSource.setTransportLatencyOut(0.0f); if(!off() && /*!_transportSource.off() &&*/ usesTransportSource()) { TrackLatencyInfo& li = _transportSource.getLatencyInfo(false); const bool participate = li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency; if(participate) { // TODO: FIXME: Where to store? We have no route to store it in. // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. // ir->audioLatencyOut = route_worst_latency - ir->audioLatencyOut; // // Should not happen, but just in case. // if((long int)ir->audioLatencyOut < 0) // ir->audioLatencyOut = 0.0f; // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet // because we don't have multiple Midi Track outputs yet, only a single output port. // So we must store this information here just for Midi Tracks. _transportSource.setTransportLatencyOut(route_worst_latency - li._latencyOutMetronome); // Should not happen, but just in case. if((long int)_transportSource.transportLatencyOut() < 0) _transportSource.setTransportLatencyOut(0.0f); } } } if(input) _latencyInfo._inputProcessed = true; else _latencyInfo._processed = true; return _latencyInfo; } bool SynthI::isLatencyInputTerminalMidi(bool capture) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if(tli->_isLatencyInputTerminalProcessed) return tli->_isLatencyInputTerminal; // Ultimately if the track is off there is no audio or midi processing, so it's a terminal. if(off()) { tli->_isLatencyInputTerminal = true; tli->_isLatencyInputTerminalProcessed = true; return true; } if(!canRecordMonitor() || (MusEGlobal::config.monitoringAffectsLatency && isRecMonitored())) { const RouteList* rl = AudioTrack::outRoutes(); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO } else { Track* track = ir->track; if(track->off()) // || //(track->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !track->isRecMonitored()))) //&& track->canRecord() && !track->recordFlag())) continue; tli->_isLatencyInputTerminal = false; tli->_isLatencyInputTerminalProcessed = true; return false; } break; default: break; } } } const int port = midiPort(); if(capture/*Tim*/ && (openFlags() & (/*capture ?*/ 2 /*: 1*/)) && port >= 0 && port < MusECore::MIDI_PORTS) { MidiPort* mp = &MusEGlobal::midiPorts[port]; const RouteList* mrl = mp->outRoutes(); for (ciRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { Track* track = ir->track; if(track->off()) // || //(atrack->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !atrack->isRecMonitored()))) //&& atrack->canRecord() && !atrack->recordFlag())) continue; tli->_isLatencyInputTerminal = false; tli->_isLatencyInputTerminalProcessed = true; return false; } break; default: break; } } } tli->_isLatencyInputTerminal = true; tli->_isLatencyInputTerminalProcessed = true; return true; } bool SynthI::isLatencyOutputTerminalMidi(bool capture) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if(tli->_isLatencyOutputTerminalProcessed) return tli->_isLatencyOutputTerminal; if(!canRecordMonitor() || (MusEGlobal::config.monitoringAffectsLatency && isRecMonitored())) { const RouteList* rl = AudioTrack::outRoutes(); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO } else { Track* track = ir->track; if(track->off()) // || //(track->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !track->isRecMonitored()))) //&& track->canRecord() && !track->recordFlag())) continue; tli->_isLatencyOutputTerminal = false; tli->_isLatencyOutputTerminalProcessed = true; return false; } break; default: break; } } } const int port = midiPort(); if(capture/*Tim*/ && (openFlags() & (/*capture ?*/ 2 /*: 1*/)) && port >= 0 && port < MusECore::MIDI_PORTS) { MidiPort* mp = &MusEGlobal::midiPorts[port]; const RouteList* mrl = mp->outRoutes(); for (ciRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { Track* track = ir->track; if(track->off()) // || //(atrack->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !atrack->isRecMonitored()))) //&& atrack->canRecord() && !atrack->recordFlag())) continue; tli->_isLatencyOutputTerminal = false; tli->_isLatencyOutputTerminalProcessed = true; return false; } break; default: break; } } } tli->_isLatencyOutputTerminal = true; tli->_isLatencyOutputTerminalProcessed = true; return true; } //--------------------------------------------------------- // getDominanceInfoMidi //--------------------------------------------------------- TrackLatencyInfo& SynthI::getDominanceInfoMidi(bool capture, bool input) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if((input && tli->_canDominateInputProcessed) || (!input && tli->_canDominateProcessed)) return *tli; // Get the default domination for this track type. bool can_dominate_lat = input ? canDominateInputLatencyMidi(capture) : canDominateOutputLatencyMidi(capture); bool can_correct_lat = canCorrectOutputLatencyMidi(); const bool passthru = canPassThruLatencyMidi(capture); bool item_found = false; if(!off() && (passthru || input)) { // Gather latency info from all connected input branches, // but ONLY if the track is not off. RouteList* rl = AudioTrack::inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO ? } else { Track* track = ir->track; //if(!off() && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } break; default: break; } } const int port = midiPort(); if(!capture/*Tim*/ && port >= 0 && port < MusECore::MIDI_PORTS) { if((openFlags() & (/*capture ? 2 :*/ 1))) { // bool used_chans[MusECore::MUSE_MIDI_CHANNELS]; // for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) // used_chans[i] = false; // bool all_chans = false; { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; //if(!off() && (openFlags() & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* mrl = mp->inRoutes(); for (iRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; // if(ir->channel < 0) // all_chans = true; // else // used_chans[ir->channel] = true; //if(!off() && (openFlags() & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_out_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. can_dominate_out_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } break; default: break; } } #endif } } // Special for the built-in metronome. if(!capture) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; //if(sendMetronome()) if(metro_settings->midiClickFlag && metro_settings->clickPort == port) { //if(!off() && (openFlags() & (capture ? 2 : 1)) && !MusECore::metronome->off() && (passthru || input)) if((openFlags() & (capture ? 2 : 1)) && !MusECore::metronome->off()) { const TrackLatencyInfo& li = MusECore::metronome->getDominanceInfoMidi(capture, false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. //route_worst_out_corr = li._outputAvailableCorrection; can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } } } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off() && (openFlags() & (capture ? 2 : 1))) { if(input) { tli->_canDominateInputLatency = can_dominate_lat; } else { tli->_canDominateOutputLatency = can_dominate_lat; // If any of the branches can dominate, then this node cannot correct. tli->_canCorrectOutputLatency = can_correct_lat && !can_dominate_lat; } } if(input) tli->_canDominateInputProcessed = true; else tli->_canDominateProcessed = true; return *tli; } //--------------------------------------------------------- // getDominanceLatencyInfoMidi //--------------------------------------------------------- TrackLatencyInfo& SynthI::getDominanceLatencyInfoMidi(bool capture, bool input) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if((input && tli->_dominanceInputProcessed) || (!input && tli->_dominanceProcessed)) return *tli; float route_worst_latency = 0.0f; const bool passthru = canPassThruLatencyMidi(capture); bool item_found = false; float worst_self_latency = 0.0f; if(!input && !off() && (openFlags() & (capture ? 2 : 1))) { worst_self_latency = getWorstSelfLatencyAudio(); const float worst_midi = getWorstSelfLatencyMidi(false /*playback*/); if(worst_midi > worst_self_latency) worst_self_latency = worst_midi; } if(!off() && (passthru || input)) { // Gather latency info from all connected input branches, // but ONLY if the track is not off. RouteList* rl = AudioTrack::inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO ? } else { Track* track = ir->track; //if(!off() && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } break; default: break; } } const int port = midiPort(); if(!capture/*Tim*/ && port >= 0 && port < MusECore::MIDI_PORTS) { // bool used_chans[MusECore::MUSE_MIDI_CHANNELS]; // for(int i = 0; i < MusECore::MUSE_MIDI_CHANNELS; ++i) // used_chans[i] = false; // bool all_chans = false; if(openFlags() & (/*capture ? 2 :*/ 1)) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; //if(!off() && (openFlags() & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* mrl = mp->inRoutes(); for (iRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; // if(ir->channel < 0) // all_chans = true; // else // used_chans[ir->channel] = true; //if(!off() && (openFlags() & (/*capture ? 2 :*/ 1)) && !track->off() && (passthru || input)) if(!track->off()) { const TrackLatencyInfo& li = track->getDominanceLatencyInfo(false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } break; default: break; } } #endif } // Special for the built-in metronome. if(!capture) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; //if(sendMetronome()) if(metro_settings->midiClickFlag && metro_settings->clickPort == port) { //if(!off() && (openFlags() & (capture ? 2 : 1)) && !MusECore::metronome->off() && (passthru || input)) if((openFlags() & (capture ? 2 : 1)) && !MusECore::metronome->off()) { const TrackLatencyInfo& li = MusECore::metronome->getDominanceLatencyInfoMidi(capture, false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } } } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off() && (openFlags() & (capture ? 2 : 1))) { if(input) { tli->_inputLatency = route_worst_latency; } else { if(passthru) { tli->_outputLatency = worst_self_latency + route_worst_latency; tli->_inputLatency = route_worst_latency; } else { tli->_outputLatency = worst_self_latency + tli->_sourceCorrectionValue; } } } if(input) tli->_dominanceInputProcessed = true; else tli->_dominanceProcessed = true; return *tli; } //--------------------------------------------------------- // setCorrectionLatencyInfoMidi //--------------------------------------------------------- TrackLatencyInfo& SynthI::setCorrectionLatencyInfoMidi(bool capture, bool input, float finalWorstLatency, float callerBranchLatency) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; const bool passthru = canPassThruLatencyMidi(capture); float worst_self_latency = 0.0f; if(!capture/*Tim*/ && !input && !off() && (openFlags() & 1 /*write*/)) { worst_self_latency = getWorstSelfLatencyAudio(); const float worst_midi = getWorstSelfLatencyMidi(false /*playback*/); if(worst_midi > worst_self_latency) worst_self_latency = worst_midi; } // The _trackLatency should already be calculated in the dominance scan. const float branch_lat = callerBranchLatency + worst_self_latency; if(!off() && (passthru || input)) { // We want the AudioTrack in routes, not the MidiDevice in routes. RouteList* rl = AudioTrack::inRoutes(); for (ciRoute ir = rl->cbegin(); ir != rl->cend(); ++ir) { if(ir->type != Route::TRACK_ROUTE || !ir->track || ir->track->isMidiTrack()) continue; Track* track = ir->track; //if(!off() && !track->off() && (passthru || input)) if(!track->off()) track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } const int port = midiPort(); if(!capture/*Tim*/ && port >= 0 && port < MusECore::MIDI_PORTS) { if((openFlags() & 1 /*write*/)) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; //if(!off() && (openFlags() & 1 /*write*/) && !track->off() && (passthru || input)) if(!track->off()) track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } #else MidiPort* mp = &MusEGlobal::midiPorts[port]; RouteList* mrl = mp->inRoutes(); for (iRoute ir = mrl->begin(); ir != mrl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; // if(ir->channel < 0) // all_chans = true; // else // used_chans[ir->channel] = true; //if(!off() && (openFlags() & 1 /*write*/) && !track->off() && (passthru || input)) if(!track->off()) track->setCorrectionLatencyInfo(false, finalWorstLatency, branch_lat); } break; default: break; } } #endif } } // Special for the built-in metronome. if(!capture) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; //if(sendMetronome()) if(metro_settings->midiClickFlag && metro_settings->clickPort == port) { //if(!off() && (openFlags() & 1 /*write*/) && !MusECore::metronome->off() && (passthru || input)) if((openFlags() & 1 /*write*/) && !MusECore::metronome->off()) MusECore::metronome->setCorrectionLatencyInfoMidi(capture, finalWorstLatency, branch_lat); } } } // Set the correction of all connected input branches, // but ONLY if the track is not off. // Capture devices cannot be corrected. Only playback devices. Tim. if(!off() && (openFlags() & 1 /*write*/) && !capture/*Tim*/) { if(input) { } else { if(canCorrectOutputLatencyMidi() &&tli->_canCorrectOutputLatency) { float corr = 0.0f; if(MusEGlobal::config.commonProjectLatency) corr -= finalWorstLatency; corr -= branch_lat; // The _sourceCorrectionValue is initialized to zero. // Whichever calling branch needs the most correction gets it. if(corr < tli->_sourceCorrectionValue) tli->_sourceCorrectionValue = corr; } } } return *tli; } //--------------------------------------------------------- // getLatencyInfoMidi //--------------------------------------------------------- TrackLatencyInfo& SynthI::getLatencyInfoMidi(bool capture, bool input) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if((input && tli->_inputProcessed) || (!input && tli->_processed)) return *tli; float route_worst_latency = tli->_inputLatency; MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; const bool passthru = canPassThruLatencyMidi(capture); if(passthru || input) { // Now that we know the worst-case latency of the connected branches, // adjust each of the conveniently stored temporary latency values // in the routes according to whether they can dominate... RouteList* rl = AudioTrack::inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::TRACK_ROUTE || !ir->track || ir->track->isMidiTrack()) continue; Track* track = ir->track; // Default to zero. ir->audioLatencyOut = 0.0f; if(!off() && !track->off()) { const TrackLatencyInfo& li = track->getLatencyInfo(false); const bool participate = li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency; if(participate) { // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. ir->audioLatencyOut = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)ir->audioLatencyOut < 0) ir->audioLatencyOut = 0.0f; } } } const int port = midiPort(); if(!capture/*Tim*/ && port >= 0 && port < MusECore::MIDI_PORTS) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const MidiTrackList& tl = *MusEGlobal::song->midis(); const MidiTrackList::size_type tl_sz = tl.size(); for(MidiTrackList::size_type it = 0; it < tl_sz; ++it) { MidiTrack* track = static_cast(tl[it]); if(track->outPort() != port) continue; // TODO: FIXME: Where to store? We have no route to store it in. // Default to zero. //ir->audioLatencyOut = 0.0f; if(!off() && (openFlags() & (/*capture ? 2 :*/ 1)) && !track->off()) { TrackLatencyInfo& li = track->getLatencyInfo(false); const bool participate = li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency; if(participate) { // TODO: FIXME: Where to store? We have no route to store it in. // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. // ir->audioLatencyOut = route_worst_latency - ir->audioLatencyOut; // // Should not happen, but just in case. // if((long int)ir->audioLatencyOut < 0) // ir->audioLatencyOut = 0.0f; // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet // because we don't have multiple Midi Track outputs yet, only a single output port. // So we must store this information here just for Midi Tracks. li._latencyOutMidiTrack = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)li._latencyOutMidiTrack < 0) li._latencyOutMidiTrack = 0.0f; } } } #else for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { if(ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; Track* track = ir->track; // Default to zero. ir->audioLatencyOut = 0.0f; if(!off() && (openFlags() & (/*capture ? 2 :*/ 1)) && !track->off()) { TrackLatencyInfo& li = track->getLatencyInfo(false); const bool participate = li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency; if(participate) { // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. ir->audioLatencyOut = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)ir->audioLatencyOut < 0) ir->audioLatencyOut = 0.0f; } } } break; default: break; } } #endif // Special for the built-in metronome. //if(!capture) //{ // TODO: FIXME: Where to store? We have no route to store it in. // Default to zero. //ir->audioLatencyOut = 0.0f; if((openFlags() & 1 /*write*/) && !MusECore::metronome->off() && // sendMetronome() && metro_settings->midiClickFlag && metro_settings->clickPort == port) { TrackLatencyInfo& li = MusECore::metronome->getLatencyInfoMidi(capture, false); const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // TODO: FIXME: Where to store? We have no route to store it in. // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. // ir->audioLatencyOut = route_worst_latency - ir->audioLatencyOut; // // Should not happen, but just in case. // if((long int)ir->audioLatencyOut < 0) // ir->audioLatencyOut = 0.0f; // // Special for Midi Tracks: We don't have Midi Track to Midi Port routes yet // // because we don't have multiple Midi Track outputs yet, only a single output port. // // So we must store this information here just for Midi Tracks. // li._latencyOutMidiTrack = route_worst_latency - li._outputLatency; // // Should not happen, but just in case. // if((long int)li._latencyOutMidiTrack < 0) // li._latencyOutMidiTrack = 0.0f; // Special for metronome: We don't have metronome routes yet. // So we must store this information here just for the metronome. li._latencyOutMetronome = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)li._latencyOutMetronome < 0) li._latencyOutMetronome = 0.0f; } } //} } } if(input) tli->_inputProcessed = true; else tli->_processed = true; return *tli; } inline unsigned long SynthI::latencyCompWriteOffsetMidi(bool capture) const { const TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; return tli->_compensatorWriteOffset; } void SynthI::setLatencyCompWriteOffsetMidi(float worstCase, bool capture) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // If independent branches are NOT to affect project latency, // then there should be no need for any extra delay in the branch. if(!MusEGlobal::config.commonProjectLatency) { tli->_compensatorWriteOffset = 0; //fprintf(stderr, "SynthI::setLatencyCompWriteOffset() name:%s capture:%d worstCase:%f _outputLatency:%f _compensatorWriteOffset:%lu\n", // name().toLatin1().constData(), capture, worstCase, tli->_outputLatency, tli->_compensatorWriteOffset); return; } if(tli->_canDominateOutputLatency) { const long unsigned int wc = worstCase; const long unsigned int ol = tli->_outputLatency; if(ol > wc) tli->_compensatorWriteOffset = 0; else tli->_compensatorWriteOffset = wc - ol; } else { // if(tli->_outputLatency < 0) tli->_compensatorWriteOffset = 0; // else // tli->_compensatorWriteOffset = tli->_outputLatency; } //fprintf(stderr, "SynthI::setLatencyCompWriteOffset() name:%s capture:%d worstCase:%f" // " _outputLatency:%f _canDominateOutputLatency:%d _compensatorWriteOffset:%lu\n", // name().toLatin1().constData(), capture, worstCase, tli->_outputLatency, tli->_canDominateOutputLatency, tli->_compensatorWriteOffset); } //================================================ // END Latency correction/compensation routines. //================================================ //--------------------------------------------------------- // preProcessAlways //--------------------------------------------------------- void SynthI::preProcessAlways() { AudioTrack::preProcessAlways(); if(_sif) _sif->preProcessAlways(); // TODO: p4.0.15 Tim. Erasure of already-played events was moved from Audio::processMidi() // to each of the midi devices - ALSA, Jack, or Synth in SynthI::getData() below. // If a synth track is 'off', AudioTrack::copyData() does not call our getData(). // So there is no processing of midi play events, or putEvent FIFOs. // Hence the play events list and putEvent FIFOs will then accumulate events, sometimes // thousands. Only when the Synth track is turned on again, are all these events // processed. Whether or not we want this is a question. // // If we DON'T want the events to accumulate, we NEED this following piece of code. // Without this code: When a song is loaded, if a Synth track is off, various controller init events // can remain queued up so that when the Synth track is turned on, those initializations // will be processed. Otherwise we, or the user, will have to init every time the track is turned on. // Con: Thousands of events can accumulate. For example selecting "midi -> Reset Instr." sends a flood // of 2048 note-off events, one for each note in each channel! Each time, the 2048, 4096, 8192 etc. // events remain in the list. // Variation: Maybe allow certain types, or groups, of events through, especially bulk init or note offs. if(off()) { // Eat up any buffer events. _playbackEventBuffers->clearRead(); //_userEventBuffers->clearRead(); } } void MessSynthIF::preProcessAlways() { if(_mess) _mess->processMessages(); } //--------------------------------------------------------- // getData //--------------------------------------------------------- bool SynthI::getData(unsigned pos, int ports, unsigned n, float** buffer) { for (int k = 0; k < ports; ++k) memset(buffer[k], 0, n * sizeof(float)); if(!_sif) return false; int p = midiPort(); MidiPort* mp = (p != -1) ? &MusEGlobal::midiPorts[p] : 0; _sif->getData(mp, pos, ports, n, buffer); return true; } bool MessSynthIF::getData(MidiPort* /*mp*/, unsigned pos, int /*ports*/, unsigned n, float** buffer) { const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); unsigned int curPos = 0; unsigned int frame = 0; // Get the state of the stop flag. const bool do_stop = synti->stopFlag(); MidiPlayEvent buf_ev; // Transfer the user lock-free buffer events to the user sorted multi-set. // False = don't use the size snapshot, but update it. const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); for(unsigned int i = 0; i < usr_buf_sz; ++i) { if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) synti->_outUserEvents.insert(buf_ev); } // Transfer the playback lock-free buffer events to the playback sorted multi-set. const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); for(unsigned int i = 0; i < pb_buf_sz; ++i) { // Are we stopping? Just remove the item. if(do_stop) synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); // Otherwise get the item. else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) synti->_outPlaybackEvents.insert(buf_ev); } // Are we stopping? if(do_stop) { // Transport has stopped, purge ALL further scheduled playback events now. synti->_outPlaybackEvents.clear(); // Reset the flag. synti->setStopFlag(false); } iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); iMPEvent impe_us = synti->_outUserEvents.begin(); bool using_pb; while(1) { if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) using_pb = *impe_pb < *impe_us; else if(impe_pb != synti->_outPlaybackEvents.end()) using_pb = true; else if(impe_us != synti->_outUserEvents.end()) using_pb = false; else break; const MidiPlayEvent& ev = using_pb ? *impe_pb : *impe_us; const unsigned int evTime = ev.time(); if(evTime < syncFrame) { if(evTime != 0) fprintf(stderr, "MessSynthIF::getData() evTime:%u < syncFrame:%u!! curPos=%d\n", evTime, syncFrame, curPos); frame = 0; } else frame = evTime - syncFrame; // Event is for future? if(frame >= n) { DEBUG_SYNTH(stderr, "MessSynthIF::getData(): Event for future, breaking loop: frame:%u n:%d evTime:%u syncFrame:%u curPos:%d\n", frame, n, evTime, syncFrame, curPos); //continue; break; } if(frame > curPos) { if (!_mess) fprintf(stderr, "MessSynthIF::getData() should not happen - no _mess\n"); else _mess->process(pos, buffer, curPos, frame - curPos); curPos = frame; } // If putEvent fails, although we would like to not miss events by keeping them // until next cycle and trying again, that can lead to a large backup of events // over a long time. So we'll just... miss them. //putEvent(ev); //synti->putEvent(ev); processEvent(ev); // Done with ring buffer event. Remove it from FIFO. // C++11. if(using_pb) impe_pb = synti->_outPlaybackEvents.erase(impe_pb); else impe_us = synti->_outUserEvents.erase(impe_us); } if(curPos < n) { if (!_mess) fprintf(stderr, "MessSynthIF::getData() should not happen - no _mess\n"); else _mess->process(pos, buffer, curPos, n - curPos); } return true; } //--------------------------------------------------------- // putEvent // return true on error (busy) //--------------------------------------------------------- bool MessSynthIF::processEvent(const MidiPlayEvent& ev) { if (!_mess) return true; if (MusEGlobal::midiOutputTrace) { fprintf(stderr, "MidiOut: MESS: <%s>: ", synti->name().toLatin1().constData()); dumpMPEvent(&ev); } int chn = ev.channel(); int a = ev.dataA(); int b = ev.dataB(); switch(ev.type()) { // Special for program, hi bank, and lo bank: Virtually all synths encapsulate banks and program together // call rather than breaking them out into three separate controllers. Therefore we need to 'compose' a // CTRL_PROGRAM which supports the full complement of hi/lo bank and program. The synths should therefore NEVER // be allowed to receive ME_PROGRAM or CTRL_HBANK or CTRL_LBANK alone (it also saves them the management trouble)... // TODO: Try to move this into the individual synths and since we must not talk directly to them, rely on feedback // from them (in midi.cpp) to update our HOST current program absolutely when they change their own program ! case ME_PROGRAM: { int hb; int lb; synti->currentProg(chn, NULL, &lb, &hb); synti->setCurrentProg(chn, a & 0xff, lb, hb); // Only if there's something to change... //if(hb < 128 || lb < 128 || a < 128) //{ if(hb > 127) // Map "dont care" to 0 hb = 0; if(lb > 127) lb = 0; if(a > 127) a = 0; const int full_prog = (hb << 16) | (lb << 8) | a; return _mess->processEvent(MidiPlayEvent(ev.time(), ev.port(), chn, ME_CONTROLLER, CTRL_PROGRAM, full_prog)); //} //return false; } break; case ME_CONTROLLER: { // Our internal hwCtrl controllers support the 'unknown' value. // Don't send 'unknown' values to the driver. Ignore and return no error. if(b == CTRL_VAL_UNKNOWN) return false; if(a == CTRL_PROGRAM) { int hb = (b >> 16) & 0xff; int lb = (b >> 8) & 0xff; int pr = b & 0xff; synti->setCurrentProg(chn, pr, lb, hb); // Only if there's something to change... //if(hb < 128 || lb < 128 || pr < 128) //{ if(hb > 127) hb = 0; if(lb > 127) lb = 0; if(pr > 127) pr = 0; const int full_prog = (hb << 16) | (lb << 8) | pr; return _mess->processEvent(MidiPlayEvent(ev.time(), ev.port(), chn, ME_CONTROLLER, CTRL_PROGRAM, full_prog)); //} //return false; } if(a == CTRL_HBANK) { int lb; int pr; synti->currentProg(chn, &pr, &lb, NULL); synti->setCurrentProg(chn, pr, lb, b & 0xff); // Only if there's something to change... //if(b < 128 || lb < 128 || pr < 128) //{ if(b > 127) b = 0; if(lb > 127) lb = 0; if(pr > 127) pr = 0; const int full_prog = (b << 16) | (lb << 8) | pr; return _mess->processEvent(MidiPlayEvent(ev.time(), ev.port(), chn, ME_CONTROLLER, CTRL_PROGRAM, full_prog)); //} //return false; } if(a == CTRL_LBANK) { int hb; int pr; synti->currentProg(chn, &pr, NULL, &hb); synti->setCurrentProg(chn, pr, b & 0xff, hb); // Only if there's something to change... //if(hb < 128 || b < 128 || pr < 128) //{ if(hb > 127) hb = 0; if(b > 127) b = 0; if(pr > 127) pr = 0; const int full_prog = (hb << 16) | (b << 8) | pr; return _mess->processEvent(MidiPlayEvent(ev.time(), ev.port(), chn, ME_CONTROLLER, CTRL_PROGRAM, full_prog)); //} //return false; } } break; default: break; } return _mess->processEvent(ev); } int MessSynthIF::oldMidiStateHeader(const unsigned char** data) const { return _mess ? _mess->oldMidiStateHeader(data) : 0; } } // namespace MusECore muse-4.0.0/src/muse/synth.h000066400000000000000000000537371404105311000155460ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: synth.h,v 1.22.2.12 2009/12/06 10:05:00 terminator356 Exp $ // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SYNTH_H__ #define __SYNTH_H__ #include #include #include #include "globaldefs.h" #include "instruments/minstrument.h" #include "mididev.h" #include "track.h" #include "stringparam.h" #include "plugin.h" #include // Current version of saved midistate data. #define SYNTH_MIDI_STATE_SAVE_VERSION 2 // Forward declarations: class Mess; struct MESS; namespace MusEGui { class PopupMenu; } namespace MusECore { class MidiPort; class SynthI; class SynthIF; class Xml; //--------------------------------------------------------- // Synth // software synthesizer //--------------------------------------------------------- class Synth { protected: QFileInfo info; // Universal resource identifier, for plugins that use it (LV2). // If this exists, it should be used INSTEAD of file info, for comparison etc. // Never rely on the file info if a uri exists. QString _uri; int _instances; QString _name; QString _description; QString _maker; QString _version; PluginFeatures_t _requiredFeatures; public: enum Type { METRO_SYNTH=0, MESS_SYNTH, DSSI_SYNTH, VST_SYNTH, VST_NATIVE_SYNTH, VST_NATIVE_EFFECT, LV2_SYNTH, LV2_EFFECT, SYNTH_TYPE_END }; Synth(const QFileInfo& fi, const QString& uri, QString label, QString descr, QString maker, QString ver, PluginFeatures_t reqFeatures = PluginNoFeatures); virtual ~Synth() {} virtual Type synthType() const = 0; inline virtual PluginFeatures_t requiredFeatures() const { return _requiredFeatures; } inline int instances() const { return _instances; } inline virtual void incInstances(int val) { _instances += val; } inline QString uri() const { return _uri; } inline QString completeBaseName() { return info.completeBaseName(); } // ddskrjo inline QString baseName() { return info.baseName(); } // ddskrjo inline QString name() const { return _name; } inline QString absolutePath() const { return info.absolutePath(); } inline QString path() const { return info.path(); } inline QString filePath() const { return info.filePath(); } inline QString fileName() const { return info.fileName(); } inline QString description() const { return _description; } inline QString version() const { return _version; } inline QString maker() const { return _maker; } virtual SynthIF* createSIF(SynthI*) = 0; }; //--------------------------------------------------------- // MessSynth //--------------------------------------------------------- class MessSynth : public Synth { const MESS* _descr; public: MessSynth(const QFileInfo& fi, QString uri, QString label, QString descr, QString maker, QString ver) : Synth(fi, uri, label, descr, maker, ver) { _descr = 0; } virtual ~MessSynth() {} inline virtual Type synthType() const { return MESS_SYNTH; } virtual void* instantiate(const QString&); virtual SynthIF* createSIF(SynthI*); }; //--------------------------------------------------------- // SynthIF // synth instance interface // NOTICE: If implementing sysex support, be sure to make a unique ID and use // it to filter out unrecognized sysexes. Headers should be constructed as: // MUSE_SYNTH_SYSEX_MFG_ID The MusE SoftSynth Manufacturer ID byte (0x7C) found in midi.h // 0xNN The synth's unique ID byte //--------------------------------------------------------- class SynthIF : public PluginIBase { protected: SynthI* synti; public: SynthIF(SynthI* s) { synti = s; } virtual ~SynthIF() {} // Provide proper outside access instead of relying on being a 'friend' of SynthI. inline SynthI* synthI() { return synti; } inline const SynthI* synthI_const() const { return synti; } // This is only a kludge required to support old songs' midistates. Do not use in any new synth. inline virtual int oldMidiStateHeader(const unsigned char** /*data*/) const { return 0; } virtual void guiHeartBeat() = 0; virtual void showGui(bool v) { if(synti && hasGui()) PluginIBase::showGui(v); } virtual bool hasGui() const = 0; virtual bool hasNativeGui() const = 0; virtual void preProcessAlways() { } virtual bool getData(MidiPort*, unsigned pos, int ports, unsigned n, float** buffer) = 0; virtual MidiPlayEvent receiveEvent() = 0; virtual int eventsPending() const = 0; virtual int channels() const = 0; virtual int totalOutChannels() const = 0; virtual int totalInChannels() const = 0; virtual void deactivate3() = 0; virtual QString getPatchName(int, int, bool) const = 0; virtual void populatePatchPopup(MusEGui::PopupMenu*, int /*channel*/, bool /*drum*/) = 0; virtual void write(int level, Xml& xml) const = 0; virtual double getParameter(unsigned long idx) const = 0; virtual void setParameter(unsigned long idx, double value) = 0; virtual int getControllerInfo(int id, QString* name, int* ctrl, int* min, int* max, int* initval) = 0; // Returns true if a note name list is found for the given patch. // If true, name either contains the note name, or is blank if no note name was found. // drum = Want percussion names, not melodic. virtual bool getNoteSampleName( bool /*drum*/, int /*channel*/, int /*patch*/, int /*note*/, QString* /*name*/) const { return false; } //------------------------- // Methods for PluginIBase: //------------------------- virtual PluginFeatures_t requiredFeatures() const; virtual bool hasBypass() const; virtual bool on() const; virtual void setOn(bool val); virtual unsigned long pluginID(); virtual int id(); virtual QString pluginLabel() const; virtual QString name() const; virtual QString lib() const; virtual QString uri() const; virtual QString dirPath() const; virtual QString fileName() const; virtual QString titlePrefix() const; virtual AudioTrack* track(); virtual void enableController(unsigned long i, bool v = true); virtual bool controllerEnabled(unsigned long i) const; virtual void enableAllControllers(bool v = true); virtual void updateControllers(); virtual void activate(); virtual void deactivate(); virtual void writeConfiguration(int level, Xml& xml); virtual bool readConfiguration(Xml& xml, bool readPreset=false); virtual unsigned long parameters() const; virtual unsigned long parametersOut() const; virtual void setParam(unsigned long i, double val); virtual double param(unsigned long i) const; virtual double paramOut(unsigned long i) const; virtual const char* paramName(unsigned long i); virtual const char* paramOutName(unsigned long i); // FIXME TODO: Either find a way to agnosticize these two ranges, or change them from ladspa ranges to a new MusE range class. virtual LADSPA_PortRangeHint range(unsigned long i); virtual LADSPA_PortRangeHint rangeOut(unsigned long i); virtual bool hasLatencyOutPort() const; virtual unsigned long latencyOutPortIndex() const; virtual float latency() const; virtual CtrlValueType ctrlValueType(unsigned long i) const; virtual CtrlList::Mode ctrlMode(unsigned long i) const; // // Returns true if the transport source is connected to any of the // // track's midi input ports (ex. synth ports not muse midi ports). // // If midiport is -1, returns true if ANY port is connected. // virtual bool transportSourceConnected(unsigned int /*midiport*/ = -1) const { return false; } // Returns true if the transport source is connected to any of the // track's midi input ports (ex. synth ports not muse midi ports). virtual bool usesTransportSource() const { return false; } }; //--------------------------------------------------------- // SynthConfiguration //--------------------------------------------------------- struct SynthConfiguration { Synth::Type _type; QString _class; QString _uri; QString _label; QRect _geometry; QRect _nativeGeometry; bool _guiVisible; bool _nativeGuiVisible; // List of initial floating point parameters, for synths which use them. // Used once upon song reload, then discarded. std::vector initParams; //custom params in xml song file , synth tag, that will be passed to new SynthIF:setCustomData(Xml &) method //now only lv2host uses them, others simply ignore std::vector accumulatedCustomParams; // Initial, and running, string parameters for synths which use them, like dssi. StringParamMap _stringParamMap; SynthConfiguration() { _type = Synth::Type::METRO_SYNTH; _guiVisible = false; _nativeGuiVisible = false; } }; //--------------------------------------------------------- // SynthI // software synthesizer instance // Track // MidiDevice // MidiInstrument //--------------------------------------------------------- class SynthI : public AudioTrack, public MidiDevice, public MidiInstrument { static bool _isVisible; SynthIF* _sif; protected: Synth* synthesizer; MPEventList _outPlaybackEvents; MPEventList _outUserEvents; // Holds initial controller values, parameters, sysex, custom data etc. for synths which use them. SynthConfiguration _initConfig; bool getData(unsigned a, int b, unsigned c, float** data); // Returns the number of frames to shift forward output event scheduling times when putting events // into the eventFifos. virtual unsigned int pbForwardShiftFrames() const; virtual QString open(); virtual void close(); public: friend class SynthIF; friend class MessSynthIF; friend class DssiSynthIF; friend class LV2SynthIF; friend class VstSynthIF; friend class VstNativeSynthIF; friend class MetronomeSynthIF; SynthI(); SynthI(const SynthI& si, int flags); virtual ~SynthI(); SynthI* clone(int flags) const { return new SynthI(*this, flags); } virtual inline MidiDeviceType deviceType() const { return SYNTH_MIDI; } // Virtual so that inheriters (synths etc) can return whatever they want. virtual inline NoteOffMode noteOffMode() const { return NoteOffAll; } SynthIF* sif() const { return _sif; } bool initInstance(Synth* s, const QString& instanceName); inline virtual float selfLatencyAudio(int channel) const { return (_sif ? _sif->latency() : 0) + AudioTrack::selfLatencyAudio(channel); } void read(Xml&); virtual void write(int, Xml&) const; void setName(const QString& s); inline QString name() const { return AudioTrack::name(); } inline QString uri() const { return synthesizer ? synthesizer->uri() : QString(); } inline const SynthConfiguration& initConfig() const { return _initConfig; } inline Synth* synth() const { return synthesizer; } inline virtual bool isSynti() const { return true; } // Event time and tick must be set by caller beforehand. // Overridden here because input from synths may need to be treated specially. virtual void recordEvent(MidiRecordEvent&); inline virtual PluginFeatures_t pluginFeatures() const { return _sif->requiredFeatures(); } // Number of routable inputs/outputs for each Route::RouteType. virtual RouteCapabilitiesStruct routeCapabilities() const; virtual QString getPatchName(int ch, int prog, bool dr, bool /*includeDefault*/ = true) const { return _sif ? _sif->getPatchName(ch, prog, dr) : QString(); } // Returns a map item with members filled from either the original or working map item, // depending on which Field flags are set. The returned map includes any requested // WorkingDrumMapEntry::OverrideType instrument overrides. Channel can be -1 meaning default. virtual void getMapItem(int channel, int patch, int index, DrumMap& dest_map, int overrideType = WorkingDrumMapEntry::AllOverrides) const; virtual void populatePatchPopup(MusEGui::PopupMenu* m, int i, bool d) { if(!_sif) return; else _sif->populatePatchPopup(m, i, d); } void currentProg(int chan, int *prog, int *bankL, int *bankH) { _curOutParamNums[chan].currentProg(prog, bankL, bankH); } void setCurrentProg(int chan, int prog, int bankL, int bankH) { _curOutParamNums[chan].setCurrentProg(prog, bankL, bankH); } void guiHeartBeat() { if(!_sif) return; else _sif->guiHeartBeat(); } bool guiVisible() const { if(!_sif) return false; else return _sif->guiVisible(); } void showGui(bool v) { if(!_sif) return; else _sif->showGui(v); } bool hasGui() const { if(!_sif) return false; else return _sif->hasGui(); } bool nativeGuiVisible() const { if(!_sif) return false; else return _sif->nativeGuiVisible(); } void showNativeGui(bool v) { if(!_sif) return; else _sif->showNativeGui(v); } bool hasNativeGui() const { if(!_sif) return false; else return _sif->hasNativeGui(); } void getGeometry(int* x, int* y, int* w, int* h) const { if(_sif) _sif->getGeometry(x, y, w, h); else { if(x) *x = 0; if(y) *y = 0; if(w) *w = 0; if(h) *h = 0; } } void setGeometry(int x, int y, int w, int h) { if(!_sif) return; else _sif->setGeometry(x, y, w, h); } void getNativeGeometry(int* x, int* y, int* w, int* h) const { if(_sif) _sif->getNativeGeometry(x, y, w, h); else { if(x) *x = 0; if(y) *y = 0; if(w) *w = 0; if(h) *h = 0; } } void setNativeGeometry(int x, int y, int w, int h) { if(!_sif) return; else _sif->setNativeGeometry(x, y, w, h); } virtual void processMidi(unsigned int /*curFrame*/ = 0); void preProcessAlways(); // // Returns true if the transport source is connected to any of the // // track's midi input ports (ex. synth ports not muse midi ports). // // If midiport is -1, returns true if ANY port is connected. // virtual bool transportSourceConnected(int midiport = -1) const // { if(_sif) return _sif->transportSourceConnected(midiport); return false; } // Returns true if the transport source is connected to any of the // track's midi input ports (ex. synth ports not muse midi ports). virtual bool usesTransportSource() const { if(_sif) return _sif->usesTransportSource(); return false; } virtual bool transportAffectsAudioLatency() const { if(_sif) return usesTransportSource() && _sif->cquirks()._transportAffectsAudioLatency; return false; } // Synth devices can never dominate latency, only physical/hardware midi devices can. inline virtual bool canDominateOutputLatencyMidi(bool /*capture*/) const { return false; } inline virtual bool canDominateEndPointLatencyMidi(bool /*capture*/) const { return false; } inline virtual bool canCorrectOutputLatencyMidi() const { return false; } virtual bool isLatencyInputTerminalMidi(bool capture); virtual bool isLatencyOutputTerminalMidi(bool capture); virtual TrackLatencyInfo& getDominanceInfoMidi(bool capture, bool input); virtual TrackLatencyInfo& getDominanceLatencyInfoMidi(bool capture, bool input); virtual TrackLatencyInfo& setCorrectionLatencyInfoMidi(bool capture, bool input, float finalWorstLatency, float callerBranchLatency = 0.0f); virtual TrackLatencyInfo& getLatencyInfoMidi(bool capture, bool input); virtual unsigned long latencyCompWriteOffsetMidi(bool capture) const; virtual void setLatencyCompWriteOffsetMidi(float worstCase, bool capture); // The cached worst latency of all the channels in the track's effect rack plus any synthesizer latency if applicable. virtual float getWorstPluginLatencyAudio(); // Synth devices can never dominate latency, only physical/hardware midi devices can. inline virtual bool canDominateOutputLatency() const { return false; } inline virtual bool canDominateEndPointLatency() const { return false; } inline virtual bool canCorrectOutputLatency() const { return false; } virtual bool isLatencyInputTerminal(); virtual bool isLatencyOutputTerminal(); virtual TrackLatencyInfo& getDominanceInfo(bool input); virtual TrackLatencyInfo& getDominanceLatencyInfo(bool input); virtual TrackLatencyInfo& setCorrectionLatencyInfo(bool input, float finalWorstLatency, float callerBranchLatency = 0.0f); virtual TrackLatencyInfo& getLatencyInfo(bool input); inline MidiPlayEvent receiveEvent() { if(!_sif) return MidiPlayEvent(); else return _sif->receiveEvent(); } inline int eventsPending() const { if(!_sif) return 0; else return _sif->eventsPending(); } void deactivate2(); void deactivate3(); inline bool isActivated() const { return synthesizer && _sif; } inline virtual bool hasAuxSend() const { return true; } static void setVisible(bool t) { _isVisible = t; } virtual int height() const; static bool visible() { return _isVisible; } }; //--------------------------------------------------------- // MessSynthIF // mess synthesizer instance // NOTICE: If implementing sysex support, be sure to make a unique ID and use // it to filter out unrecognized sysexes. Headers should be constructed as: // MUSE_SYNTH_SYSEX_MFG_ID The MusE SoftSynth Manufacturer ID byte (0x7C) found in midi.h // 0xNN The synth's unique ID byte //--------------------------------------------------------- class MessSynthIF : public SynthIF { Mess* _mess; bool processEvent(const MidiPlayEvent& ev); public: MessSynthIF(SynthI* s) : SynthIF(s) { _mess = 0; } virtual ~MessSynthIF() { } // This is only a kludge required to support old songs' midistates. Do not use in any new synth. virtual int oldMidiStateHeader(const unsigned char** data) const; virtual void guiHeartBeat(); inline virtual bool guiVisible() const { return false; } inline virtual bool hasGui() const { return false; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool v); virtual bool hasNativeGui() const; virtual void getNativeGeometry(int*, int*, int*, int*) const; virtual void setNativeGeometry(int, int, int, int); virtual void preProcessAlways(); virtual bool getData(MidiPort*, unsigned pos, int ports, unsigned n, float** buffer); virtual MidiPlayEvent receiveEvent(); virtual int eventsPending() const; bool init(Synth* s, SynthI* si); virtual int channels() const; virtual int totalOutChannels() const; virtual int totalInChannels() const; virtual void deactivate3(); virtual QString getPatchName(int, int, bool) const; virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool); virtual void write(int level, Xml& xml) const; inline virtual double getParameter(unsigned long) const { return 0.0; } virtual void setParameter(unsigned long, double) {} virtual int getControllerInfo(int id, QString* name, int* ctrl, int* min, int* max, int* initval); // Returns true if a note name list is found for the given patch. // If true, name either contains the note name, or is blank if no note name was found. // drum = Want percussion names, not melodic. virtual bool getNoteSampleName( bool drum, int channel, int patch, int note, QString* name) const; }; // NOTE: Moved here from track.h to avoid circular dependency. typedef tracklist::iterator iSynthI; typedef tracklist::const_iterator ciSynthI; typedef tracklist SynthIList; typedef std::vector::iterator iSynthList; typedef std::vector::const_iterator ciSynthList; class SynthList : public std::vector { public: SynthList() { } // Each argument optional, can be empty. // If uri is not empty, the search is based solely on it, the other arguments are ignored. Synth* find(const QString& fileCompleteBaseName, const QString& pluginUri, const QString& pluginName) const; }; extern QString synthType2String(Synth::Type); extern Synth::Type string2SynthType(const QString&); } // namespace MusECore namespace MusEGlobal { extern MusECore::SynthList synthis; // array of available synthis } #endif muse-4.0.0/src/muse/tempo.cpp000066400000000000000000000573331404105311000160540ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tempo.cpp,v 1.7.2.7 2008/05/21 00:28:52 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "muse_math.h" #include "tempo.h" #include "globals.h" #include "gconfig.h" #include "xml.h" #include namespace MusEGlobal { MusECore::TempoList tempomap; MusECore::TempoRecList tempo_rec_list; } namespace MusECore { //--------------------------------------------------------- // TempoList //--------------------------------------------------------- TempoList::TempoList() { _tempo = 500000; insert(std::pair (MAX_TICK+1, new TEvent(_tempo, 0))); _tempoSN = 1; _globalTempo = 100; useList = true; } TempoList::~TempoList() { for (iTEvent i = begin(); i != end(); ++i) delete i->second; } //--------------------------------------------------------- // copy //--------------------------------------------------------- void TempoList::copy(const TempoList& src) { // Clear and delete the existing destination list. for (iTEvent i = begin(); i != end(); ++i) delete i->second; TEMPOLIST::clear(); for (ciTEvent i = src.cbegin(); i != src.cend(); ++i) { TEvent* new_e = new TEvent(*i->second); std::pair res = insert(std::pair (i->first, new_e)); if(!res.second) { fprintf(stderr, "TempoList::copy insert failed: tempolist:%p tempo:%p %d tick:%d\n", this, new_e, new_e->tempo, new_e->tick); } } } //--------------------------------------------------------- // add //--------------------------------------------------------- void TempoList::add(unsigned tick, int tempo, bool do_normalize) { if (tick > MAX_TICK) tick = MAX_TICK; iTEvent e = upper_bound(tick); if (tick == e->second->tick) e->second->tempo = tempo; else { TEvent* ne = e->second; TEvent* ev = new TEvent(ne->tempo, ne->tick); ne->tempo = tempo; ne->tick = tick; insert(std::pair (tick, ev)); } if(do_normalize) normalize(); } void TempoList::add(unsigned tick, TEvent* e, bool do_normalize) { int tempo = e->tempo; std::pair res = insert(std::pair (tick, e)); if(!res.second) { fprintf(stderr, "TempoList::add insert failed: tempolist:%p tempo:%p %d tick:%d\n", this, e, tempo, e->tick); } else { iTEvent ine = res.first; ++ine; // There is always a 'next' tempo event - there is always one at index MAX_TICK + 1. TEvent* ne = ine->second; // Swap the values. (This is how the tempo list works.) e->tempo = ne->tempo; e->tick = ne->tick; ne->tempo = tempo; ne->tick = tick; if(do_normalize) normalize(); } } //--------------------------------------------------------- // TempoList::normalize //--------------------------------------------------------- void TempoList::normalize() { unsigned frame = 0; const uint64_t numer = (uint64_t)MusEGlobal::sampleRate; const uint64_t denom = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL; for (iTEvent e = begin(); e != end(); ++e) { e->second->frame = frame; // Tick resolution is less than frame resolution. // Round up so that the reciprocal function (frame to tick) matches value for value. frame += muse_multiply_64_div_64_to_64( numer * (uint64_t)e->second->tempo, e->first - e->second->tick, denom, LargeIntRoundUp); } // Invalidate all cached frame or tick values used in the program, such as in class Pos. // On the very next call of tick2frame() or frame2tick(), serial numbers are compared // and if they are the same a cached value is returned. // Otherwise if the serial numbers are not the same the value is recalculated. ++_tempoSN; } //--------------------------------------------------------- // TempoList::dump //--------------------------------------------------------- void TempoList::dump() const { printf("\nTempoList:\n"); for (ciTEvent i = begin(); i != end(); ++i) { printf("%6d %06d Tempo %6d Frame %d\n", i->first, i->second->tick, i->second->tempo, i->second->frame); } } //--------------------------------------------------------- // clear //--------------------------------------------------------- void TempoList::clear() { for (iTEvent i = begin(); i != end(); ++i) delete i->second; TEMPOLIST::clear(); insert(std::pair (MAX_TICK+1, new TEvent(500000, 0))); ++_tempoSN; } //--------------------------------------------------------- // eraseRange //--------------------------------------------------------- void TempoList::eraseRange(unsigned stick, unsigned etick) { if(stick >= etick || stick > MAX_TICK) return; if(etick > MAX_TICK) etick = MAX_TICK; iTEvent se = MusEGlobal::tempomap.upper_bound(stick); if(se == end() || (se->first == MAX_TICK+1)) return; iTEvent ee = MusEGlobal::tempomap.upper_bound(etick); ee->second->tempo = se->second->tempo; ee->second->tick = se->second->tick; for(iTEvent ite = se; ite != ee; ++ite) delete ite->second; erase(se, ee); // Erase range does NOT include the last element. normalize(); } //--------------------------------------------------------- // tempo //--------------------------------------------------------- int TempoList::tempo(unsigned tick) const { if (useList) { ciTEvent i = upper_bound(tick); if (i == end()) { printf("no TEMPO at tick %d,0x%x\n", tick, tick); return 1000; } return i->second->tempo; } else return _tempo; } float TempoList::bpm(unsigned tick) const { return (float)globalTempo() * 600000.0f / (float)tempo(tick); } float TempoList::bpmAt(unsigned tick) const { return (float)globalTempo() * 600000.0f / (float)tempoAt(tick); } //--------------------------------------------------------- // tempoAt // Bypass the useList flag and read from the list //--------------------------------------------------------- int TempoList::tempoAt(unsigned tick) const { ciTEvent i = upper_bound(tick); if (i == end()) { printf("tempoAt: no TEMPO at tick %d,0x%x\n", tick, tick); return 1000; } return i->second->tempo; } //--------------------------------------------------------- // del //--------------------------------------------------------- void TempoList::del(unsigned tick, bool do_normalize) { iTEvent e = find(tick); if (e == end()) { printf("TempoList::del(%d): not found\n", tick); return; } del(e, do_normalize); } void TempoList::del(iTEvent e, bool do_normalize) { iTEvent ne = e; ++ne; if (ne == end()) { printf("TempoList::del() HALLO\n"); return; } ne->second->tempo = e->second->tempo; ne->second->tick = e->second->tick; erase(e); if(do_normalize) normalize(); } //--------------------------------------------------------- // setTempo // called from transport window // & slave mode tempo changes //--------------------------------------------------------- void TempoList::setTempo(unsigned tick, int newTempo) { if (useList) add(tick, newTempo, true); else { setStaticTempo(newTempo); } } //--------------------------------------------------------- // setGlobalTempo //--------------------------------------------------------- void TempoList::setGlobalTempo(int val) { _globalTempo = val; normalize(); } //--------------------------------------------------------- // addTempo //--------------------------------------------------------- void TempoList::addTempo(unsigned t, int tempo, bool do_normalize) { add(t, tempo, do_normalize); } //--------------------------------------------------------- // delTempo //--------------------------------------------------------- void TempoList::delTempo(unsigned tick, bool do_normalize) { del(tick, do_normalize); } //--------------------------------------------------------- // setStaticTempo //--------------------------------------------------------- void TempoList::setStaticTempo(int newTempo) { _tempo = newTempo; ++_tempoSN; } //--------------------------------------------------------- // setMasterFlag //--------------------------------------------------------- bool TempoList::setMasterFlag(unsigned /*tick*/, bool val) { if (useList != val) { useList = val; ++_tempoSN; return true; } return false; } //--------------------------------------------------------- // ticks2frames //--------------------------------------------------------- unsigned TempoList::ticks2frames(unsigned ticks, unsigned tempoTick, LargeIntRoundMode round_mode) const { // Tick resolution is less than frame resolution. // Round up so that the reciprocal function (frame to tick) matches value for value. return muse_multiply_64_div_64_to_64( (uint64_t)MusEGlobal::sampleRate * (uint64_t)tempo(tempoTick), ticks, (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL, round_mode); } // TODO // //--------------------------------------------------------- // // frames2ticks // //--------------------------------------------------------- // // unsigned TempoList::frames2ticks(unsigned frames, unsigned tempoTick) const // { // const uint64_t numer = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL; // const uint64_t denom = (uint64_t)MusEGlobal::sampleRate; // return muse_multiply_64_div_64_to_64(numer, frames, denom * (uint64_t)tempo(tempoTick)); // } //--------------------------------------------------------- // tick2frame //--------------------------------------------------------- unsigned TempoList::tick2frame(unsigned tick, unsigned frame, int* sn, LargeIntRoundMode round_mode) const { return (*sn == _tempoSN) ? frame : tick2frame(tick, sn, round_mode); } //--------------------------------------------------------- // tick2frame //--------------------------------------------------------- unsigned TempoList::tick2frame(unsigned tick, int* sn, LargeIntRoundMode round_mode) const { unsigned f; const uint64_t numer = (uint64_t)MusEGlobal::sampleRate; const uint64_t denom = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL; if (useList) { ciTEvent i = upper_bound(tick); if (i == end()) { printf("tick2frame(%d,0x%x): not found\n", tick, tick); return 0; } // Tick resolution is less than frame resolution. // Round up so that the reciprocal function (frame to tick) matches value for value. f = i->second->frame + muse_multiply_64_div_64_to_64( numer * (uint64_t)i->second->tempo, tick - i->second->tick, denom, round_mode); } else { // Tick resolution is less than frame resolution. // Round up so that the reciprocal function (frame to tick) matches value for value. f = muse_multiply_64_div_64_to_64(numer * (uint64_t)_tempo, tick, denom, round_mode); } if (sn) *sn = _tempoSN; return f; } //--------------------------------------------------------- // frame2tick // return cached value t if list did not change //--------------------------------------------------------- unsigned TempoList::frame2tick(unsigned frame, unsigned t, int* sn, LargeIntRoundMode round_mode) const { return (*sn == _tempoSN) ? t : frame2tick(frame, sn, round_mode); } //--------------------------------------------------------- // frame2tick //--------------------------------------------------------- unsigned TempoList::frame2tick(unsigned frame, int* sn, LargeIntRoundMode round_mode) const { unsigned tick; const uint64_t numer = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL; const uint64_t denom = (uint64_t)MusEGlobal::sampleRate; if (useList) { ciTEvent e; for (e = begin(); e != end();) { ciTEvent ee = e; ++ee; if (ee == end()) break; if (frame < ee->second->frame) break; e = ee; } // Normally do not round up here since (audio) frame resolution is higher than tick resolution. tick = e->second->tick + muse_multiply_64_div_64_to_64( numer, frame - e->second->frame, denom * (uint64_t)e->second->tempo, round_mode); } else // Normally do not round up here since (audio) frame resolution is higher than tick resolution. tick = muse_multiply_64_div_64_to_64(numer, frame, denom * (uint64_t)_tempo, round_mode); if (sn) *sn = _tempoSN; return tick; } //--------------------------------------------------------- // deltaTick2frame //--------------------------------------------------------- unsigned TempoList::deltaTick2frame(unsigned tick1, unsigned tick2, int* sn, LargeIntRoundMode round_mode) const { unsigned int f1, f2; const uint64_t numer = (uint64_t)MusEGlobal::sampleRate; const uint64_t denom = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL; if (useList) { ciTEvent i = upper_bound(tick1); if (i == end()) { printf("TempoList::deltaTick2frame: tick1:%d not found\n", tick1); // abort(); return 0; } // Tick resolution is less than frame resolution. // Round up so that the reciprocal function (frame to tick) matches value for value. f1 = i->second->frame + muse_multiply_64_div_64_to_64( numer * (uint64_t)i->second->tempo, tick1 - i->second->tick, denom, round_mode); i = upper_bound(tick2); if (i == end()) { return 0; } // Tick resolution is less than frame resolution. // Round up so that the reciprocal function (frame to tick) matches value for value. f2 = i->second->frame + muse_multiply_64_div_64_to_64( numer * (uint64_t)i->second->tempo, tick2 - i->second->tick, denom, round_mode); } else { // Tick resolution is less than frame resolution. // Round up so that the reciprocal function (frame to tick) matches value for value. f1 = muse_multiply_64_div_64_to_64(numer * (uint64_t)_tempo, tick1, denom, round_mode); // Tick resolution is less than frame resolution. // Round up so that the reciprocal function (frame to tick) matches value for value. f2 = muse_multiply_64_div_64_to_64(numer * (uint64_t)_tempo, tick2, denom, round_mode); } if (sn) *sn = _tempoSN; // FIXME: Caution: This should be rounded off properly somehow, but how to do that? // But it seems to work so far. return f2 - f1; } //--------------------------------------------------------- // deltaFrame2tick //--------------------------------------------------------- unsigned TempoList::deltaFrame2tick(unsigned frame1, unsigned frame2, int* sn, LargeIntRoundMode round_mode) const { unsigned tick1, tick2; const uint64_t numer = (uint64_t)MusEGlobal::config.division * (uint64_t)_globalTempo * 10000UL; const uint64_t denom = (uint64_t)MusEGlobal::sampleRate; if (useList) { ciTEvent e; for (e = begin(); e != end();) { ciTEvent ee = e; ++ee; if (ee == end()) break; if (frame1 < ee->second->frame) break; e = ee; } // Normally do not round up here since (audio) frame resolution is higher than tick resolution. tick1 = e->second->tick + muse_multiply_64_div_64_to_64( numer, frame1 - e->second->frame, denom * (uint64_t)e->second->tempo, round_mode); for (e = begin(); e != end();) { ciTEvent ee = e; ++ee; if (ee == end()) break; if (frame2 < ee->second->frame) break; e = ee; } // Normally do not round up here since (audio) frame resolution is higher than tick resolution. tick2 = e->second->tick + muse_multiply_64_div_64_to_64( numer, frame2 - e->second->frame, denom * (uint64_t)e->second->tempo, round_mode); } else { // Normally do not round up here since (audio) frame resolution is higher than tick resolution. tick1 = muse_multiply_64_div_64_to_64(numer, frame1, denom * (uint64_t)_tempo, round_mode); tick2 = muse_multiply_64_div_64_to_64(numer, frame2, denom * (uint64_t)_tempo, round_mode); } if (sn) *sn = _tempoSN; // FIXME: Caution: This should be rounded off properly somehow, but how to do that? // But it seems to work so far. return tick2 - tick1; } //--------------------------------------------------------- // TempoList::write //--------------------------------------------------------- void TempoList::write(int level, Xml& xml) const { xml.put(level++, "", _tempo); if (_globalTempo != 100) xml.intTag(level, "globalTempo", _globalTempo); for (ciTEvent i = begin(); i != end(); ++i) i->second->write(level, xml, i->first); xml.tag(level, "/tempolist"); } //--------------------------------------------------------- // TempoList::read //--------------------------------------------------------- void TempoList::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return; case Xml::TagStart: if (tag == "tempo") { TEvent* t = new TEvent(); unsigned tick = t->read(xml); iTEvent pos = find(tick); if (pos != end()) erase(pos); insert(std::pair (tick, t)); } else if (tag == "globalTempo") _globalTempo = xml.parseInt(); else xml.unknown("TempoList"); break; case Xml::Attribut: if (tag == "fix") _tempo = xml.s2().toInt(); break; case Xml::TagEnd: if (tag == "tempolist") { normalize(); return; } default: break; } } } //--------------------------------------------------------- // TEvent::write //--------------------------------------------------------- void TEvent::write(int level, Xml& xml, int at) const { xml.tag(level++, "tempo at=\"%d\"", at); xml.intTag(level, "tick", tick); xml.intTag(level, "val", tempo); xml.tag(level, "/tempo"); } //--------------------------------------------------------- // TEvent::read //--------------------------------------------------------- int TEvent::read(Xml& xml) { int at = 0; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: return 0; case Xml::TagStart: if (tag == "tick") tick = xml.parseInt(); else if (tag == "val") tempo = xml.parseInt(); else xml.unknown("TEvent"); break; case Xml::Attribut: if (tag == "at") at = xml.s2().toInt(); break; case Xml::TagEnd: if (tag == "tempo") { return at; } default: break; } } return 0; } //--------------------------------------------------------- // put // return true on fifo overflow //--------------------------------------------------------- bool TempoFifo::put(const TempoRecEvent& event) { if (size < TEMPO_FIFO_SIZE) { fifo[wIndex] = event; wIndex = (wIndex + 1) % TEMPO_FIFO_SIZE; // q_atomic_increment(&size); ++size; return false; } return true; } //--------------------------------------------------------- // get //--------------------------------------------------------- TempoRecEvent TempoFifo::get() { TempoRecEvent event(fifo[rIndex]); rIndex = (rIndex + 1) % TEMPO_FIFO_SIZE; --size; return event; } //--------------------------------------------------------- // peek //--------------------------------------------------------- const TempoRecEvent& TempoFifo::peek(int n) { int idx = (rIndex + n) % TEMPO_FIFO_SIZE; return fifo[idx]; } //--------------------------------------------------------- // remove //--------------------------------------------------------- void TempoFifo::remove() { rIndex = (rIndex + 1) % TEMPO_FIFO_SIZE; --size; } } // namespace MusECore muse-4.0.0/src/muse/tempo.h000066400000000000000000000206651404105311000155170ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tempo.h,v 1.2.2.1 2006/09/19 19:07:09 spamatica Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TEMPO_H__ #define __TEMPO_H__ #include #include #include "large_int.h" #ifndef MAX_TICK #define MAX_TICK (0x7fffffff/100) #endif // Tempo ring buffer size #define TEMPO_FIFO_SIZE 1024 namespace MusECore { class Xml; //--------------------------------------------------------- // Tempo Event //--------------------------------------------------------- struct TEvent { int tempo; unsigned tick; // new tempo at tick unsigned frame; // precomputed time for tick in sec int read(Xml&); void write(int, Xml&, int) const; TEvent() { } TEvent(unsigned t, unsigned tk) { tempo = t; tick = tk; frame = 0; } }; //--------------------------------------------------------- // TempoList //--------------------------------------------------------- typedef std::map > TEMPOLIST; typedef TEMPOLIST::iterator iTEvent; typedef TEMPOLIST::const_iterator ciTEvent; typedef TEMPOLIST::reverse_iterator riTEvent; typedef TEMPOLIST::const_reverse_iterator criTEvent; class TempoList : public TEMPOLIST { friend struct PendingOperationItem; int _tempoSN; // serial no to track tempo changes bool useList; int _tempo; // tempo if not using tempo list int _globalTempo; // %percent 50-200% void add(unsigned tick, int tempo, bool do_normalize = true); void add(unsigned tick, TEvent* e, bool do_normalize = true); void del(iTEvent, bool do_normalize = true); void del(unsigned tick, bool do_normalize = true); public: TempoList(); ~TempoList(); // Makes a copy of the source list including all allocated items. // This clears and deletes existing items in the destination list. void copy(const TempoList& src); void normalize(); void clear(); void eraseRange(unsigned stick, unsigned etick); void read(Xml&); void write(int, Xml&) const; void dump() const; // Returns the actual Beats Per Minute at given tick or the static tempo value if master (useList) is off. float bpm(unsigned tick) const; // Bypass the master (useList) flag and return the actual Beats Per Minute at the given tick. float bpmAt(unsigned tick) const; // Returns a tempo value from the list at the given tick if master (useList) is on, or else the static tempo value. // This is not in the same units as BPM. int tempo(unsigned tick) const; // Bypass the master (useList) flag and return a tempo value directly from the list at the given tick. // This is not in the same units as BPM. int tempoAt(unsigned tick) const; //------------------------------------------------------------------------------------------------------- // Normally do not round these tick to frame methods down since (audio) frame resolution is higher than tick // resolution. Other rounding methods are provided for convenience, since tick to frame is a 'broad' question. //------------------------------------------------------------------------------------------------------- // Returns the number of frames contained in the given ticks, // at the tempo at the tick given by tempoTick. Honours useList. unsigned ticks2frames(unsigned ticks, unsigned tempoTick, LargeIntRoundMode round_mode = LargeIntRoundUp) const; // TODO // // Returns the number of ticks contained in the given frames, // // at the tempo at the tick given by tempoTick. Honours useList. // unsigned frames2ticks(unsigned frames, unsigned tempoTick) const; unsigned tick2frame(unsigned tick, unsigned frame, int* sn, LargeIntRoundMode round_mode = LargeIntRoundUp) const; unsigned tick2frame(unsigned tick, int* sn = 0, LargeIntRoundMode round_mode = LargeIntRoundUp) const; unsigned deltaTick2frame(unsigned tick1, unsigned tick2, int* sn = 0, LargeIntRoundMode round_mode = LargeIntRoundUp) const; //------------------------------------------------------------------------------------------------------- // Normally do not round these frame to tick methods up since (audio) frame resolution is higher than tick // resolution. Other rounding methods are provided for convenience, since frame to tick is a 'broad' question. // Only round up or to nearest if you know what you are doing. It is OK to do that for example // to VISUALLY 'snap' the frame to a tick from either side of the tick (the MTScale time scale widgets). // But for most other uses especially timing, leave it alone at rounding down. Timing ticks need to change // to the next value ONLY when there are enough frames to count as one whole tick. Otherwise tick-based // events such as midi notes might be processed slightly too soon (or too late in some circumstances). //------------------------------------------------------------------------------------------------------- unsigned frame2tick(unsigned frame, int* sn = 0, LargeIntRoundMode round_mode = LargeIntRoundDown) const; unsigned frame2tick(unsigned frame, unsigned tick, int* sn, LargeIntRoundMode round_mode = LargeIntRoundDown) const; unsigned deltaFrame2tick(unsigned frame1, unsigned frame2, int* sn = 0, LargeIntRoundMode round_mode = LargeIntRoundDown) const; int tempoSN() const { return _tempoSN; } // Sets the tempo value in the list if master is on, or else the static tempo value. void setTempo(unsigned tick, int newTempo); // Returns the static tempo value (the one used when the master is off). int staticTempo() const { return _tempo; } // Sets the static tempo value (the one used when the master is off). void setStaticTempo(int newTempo); void addTempo(unsigned t, int tempo, bool do_normalize = true); void delTempo(unsigned tick, bool do_normalize = true); bool masterFlag() const { return useList; } bool setMasterFlag(unsigned tick, bool val); int globalTempo() const { return _globalTempo; } void setGlobalTempo(int val); }; //--------------------------------------------------------- // Tempo Record Event //--------------------------------------------------------- struct TempoRecEvent { int tempo; unsigned tick; TempoRecEvent() { } TempoRecEvent(unsigned tk, unsigned t) { tick = tk; tempo = t; } }; class TempoRecList : public std::vector { public: void addTempo(int tick, int tempo) { push_back(TempoRecEvent(tick, tempo)); } void addTempo(const TempoRecEvent& e) { push_back(e); } }; //--------------------------------------------------------- // TempoFifo //--------------------------------------------------------- class TempoFifo { TempoRecEvent fifo[TEMPO_FIFO_SIZE]; volatile int size; int wIndex; int rIndex; public: TempoFifo() { clear(); } bool put(const TempoRecEvent& event); // returns true on fifo overflow TempoRecEvent get(); const TempoRecEvent& peek(int = 0); void remove(); bool isEmpty() const { return size == 0; } void clear() { size = 0, wIndex = 0, rIndex = 0; } int getSize() const { return size; } }; } // namespace MusECore namespace MusEGlobal { extern MusECore::TempoList tempomap; extern MusECore::TempoRecList tempo_rec_list; } #endif muse-4.0.0/src/muse/thread.cpp000066400000000000000000000317771404105311000162030ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: thread.cpp,v 1.4.2.5 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "thread.h" #include #include #include #ifdef _WIN32 #include "poll.h" #include "poll_win.c" #else #include #include #endif #include #include "globals.h" #include "errno.h" #ifdef _WIN32 #define pipe(fds) _pipe(fds, 4096, _O_BINARY) #endif namespace MusECore { //--------------------------------------------------------- // Thread //--------------------------------------------------------- Thread::~Thread() { } //--------------------------------------------------------- // serverloop //--------------------------------------------------------- static void* loop(void* mops) { Thread* t = (Thread*) mops; t->loop(); return 0; } //--------------------------------------------------------- // start //--------------------------------------------------------- void Thread::start(int prio, void* ptr) { userPtr = ptr; pthread_attr_t* attributes = 0; _realTimePriority = prio; /* DELETETHIS 14 attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); pthread_attr_init(attributes); */ // pthread_mutexattr_t mutexattr; // pthread_mutexattr_init(&mutexattr); // pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_TIMED_NP); // pthread_mutex_init(&lock, &mutexattr); // pthread_cond_init(&ready, 0); // pthread_mutex_lock(&lock); //if (_realTimePriority) { if (MusEGlobal::realTimeScheduling && _realTimePriority > 0) { // p4.0.16 attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); pthread_attr_init(attributes); if (pthread_attr_setschedpolicy(attributes, SCHED_FIFO)) { printf("cannot set FIFO scheduling class for RT thread\n"); } if (pthread_attr_setscope (attributes, PTHREAD_SCOPE_SYSTEM)) { printf("Cannot set scheduling scope for RT thread\n"); } if (pthread_attr_setinheritsched(attributes, PTHREAD_EXPLICIT_SCHED)) { printf("Cannot set setinheritsched for RT thread\n"); } struct sched_param rt_param; memset(&rt_param, 0, sizeof(rt_param)); rt_param.sched_priority = _realTimePriority; if (pthread_attr_setschedparam (attributes, &rt_param)) { printf("Cannot set scheduling priority %d for RT thread (%s)\n", _realTimePriority, strerror(errno)); } } /* DELETETHIS 8 if (pthread_create(&thread, attributes, MusECore::loop, this)) perror("creating thread failed:"); // else // { // pthread_cond_wait(&ready, &lock); // } // pthread_mutex_unlock(&lock); */ int rv = pthread_create(&thread, attributes, MusECore::loop, this); if(rv) { // p4.0.16: MusEGlobal::realTimeScheduling is unreliable. It is true even in some clearly non-RT cases. // I cannot seem to find a reliable answer to the question of "are we RT or not". // MusE was failing with a stock kernel because of PTHREAD_EXPLICIT_SCHED. // So we'll just have to try again without attributes. if (MusEGlobal::realTimeScheduling && _realTimePriority > 0) rv = pthread_create(&thread, NULL, MusECore::loop, this); } if(rv) fprintf(stderr, "creating thread <%s> failed: %s\n", _name, strerror(rv)); if (attributes) // p4.0.16 { pthread_attr_destroy(attributes); free(attributes); } } //--------------------------------------------------------- // stop //--------------------------------------------------------- void Thread::stop(bool force) { if (thread == 0) return; if (force) { pthread_cancel(thread); threadStop(); } _running = false; if (thread) { if (pthread_join(thread, 0)) { // perror("Failed to join sequencer thread"); DELETETHIS and the if around? } } } //--------------------------------------------------------- // Thread // prio = 0 no realtime scheduling //--------------------------------------------------------- Thread::Thread(const char* s) { userPtr = 0; _name = s; _realTimePriority = 0; pfd = 0; npfd = 0; maxpfd = 0; _running = false; _pollWait = -1; thread = 0; // create message channels int filedes[2]; // 0 - reading 1 - writing if (pipe(filedes) == -1) { perror("thread:creating pipe"); exit(-1); } toThreadFdr = filedes[0]; toThreadFdw = filedes[1]; if (pipe(filedes) == -1) { perror("thread: creating pipe"); exit(-1); } fromThreadFdr = filedes[0]; fromThreadFdw = filedes[1]; // pthread_mutexattr_t mutexattr; DELETETHIS 5 // pthread_mutexattr_init(&mutexattr); // pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_TIMED_NP); // pthread_mutex_init(&lock, &mutexattr); // pthread_cond_init(&ready, 0); } //--------------------------------------------------------- // addPollFd //--------------------------------------------------------- void Thread::addPollFd(int fd, int action, void (*handler)(void*,void*), void* p, void* q) { if (fd == -1) return; for (iPoll i = plist.begin(); i != plist.end(); ++i) { if ((i->fd == fd) && (i->action == action)) return; } plist.push_back(Poll(fd, action, handler, p, q)); if (npfd == maxpfd) { int n = (maxpfd == 0) ? 4 : maxpfd * 2; //TODO: delete old pfd pfd = new struct pollfd[n]; maxpfd = n; } ++npfd; int idx = 0; for (iPoll i = plist.begin(); i != plist.end(); ++i, ++idx) { pfd[idx].fd = i->fd; pfd[idx].events = i->action; } } //--------------------------------------------------------- // removePollFd //--------------------------------------------------------- void Thread::removePollFd(int fd, int action) { for (iPoll i = plist.begin(); i != plist.end(); ++i) { if (i->fd == fd && i->action == action) { plist.erase(i); --npfd; break; } } int idx = 0; for (iPoll i = plist.begin(); i != plist.end(); ++i, ++idx) { pfd[idx].fd = i->fd; pfd[idx].events = i->action; } } //--------------------------------------------------------- // loop //--------------------------------------------------------- void Thread::loop() { #ifndef _WIN32 if (!MusEGlobal::debugMode) { if (mlockall(MCL_CURRENT | MCL_FUTURE)) perror("WARNING: Cannot lock memory:"); } #endif #ifdef __APPLE__ #define BIG_ENOUGH_STACK (1024*256*1) #else #define BIG_ENOUGH_STACK (1024*1024*1) #endif char buf[BIG_ENOUGH_STACK]; for (int i = 0; i < BIG_ENOUGH_STACK; i++) buf[i] = i; #undef BIG_ENOUGH_STACK pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, 0); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, 0); int policy = buf[0]; // Initialize using buf[0] to keep the compiler from complaining about unused buf. policy = 0; // Now set the true desired initial value. if ((policy = sched_getscheduler (0)) < 0) { printf("Thread: Cannot get current client scheduler: %s\n", strerror(errno)); } if (MusEGlobal::debugMsg) printf("Thread <%s, id %p> has %s priority %d\n", _name, (void *)pthread_self(), policy == SCHED_FIFO ? "SCHED_FIFO" : "SCHED_OTHER", policy == SCHED_FIFO ? _realTimePriority : 0); // pthread_mutex_lock(&lock); DELETETHIS and below _running = true; // pthread_cond_signal(&ready); // pthread_mutex_unlock(&lock); threadStart(userPtr); while (_running) { if (MusEGlobal::debugMode) // DEBUG _pollWait = 10; // ms else _pollWait = -1; int n = poll(pfd, npfd, _pollWait); if (n < 0) { if (errno == EINTR) continue; fprintf(stderr, "poll failed: %s\n", strerror(errno)); exit(-1); } if (n == 0) { // timeout defaultTick(); continue; } struct pollfd* p = &pfd[0]; int i = 0; for (iPoll ip = plist.begin(); ip != plist.end(); ++ip, ++p, ++i) { if (ip->action & p->revents) { (ip->handler)(ip->param1, ip->param2); break; } } } threadStop(); } //--------------------------------------------------------- // send // send request from gui to thread // wait until request is processed //--------------------------------------------------------- bool Thread::sendMsg(const ThreadMsg* m) { if (_running) { int rv = write(toThreadFdw, &m, sizeof(ThreadMsg*)); if (rv != sizeof(ThreadMsg*)) { perror("Thread::sendMessage(): write pipe failed"); return true; } // wait for sequencer to finish operation char c; rv = read(fromThreadFdr, &c, 1); if (rv != 1) { perror("Thread::sendMessage(): read pipe failed"); return true; } //int c; DELETETHIS 6 //rv = read(fromThreadFdr, &c, sizeof(c)); //if (rv != sizeof(c)) { // perror("Thread::sendMessage(): read pipe failed"); // return true; // } } else { // if thread is not running (during initialization) // process commands directly: processMsg(m); } return false; } //--------------------------------------------------------- // send // send request from gui to thread // do __not__ wait until request is processed //--------------------------------------------------------- bool Thread::sendMsg1(const void* m, int n) { int rv = write(toThreadFdw, m, n); if (rv != n) { perror("Thread::sendMessage1(): write pipe failed"); return true; } return false; } //--------------------------------------------------------- // readMsg //--------------------------------------------------------- void Thread::readMsg() { ThreadMsg* p; if (read(toThreadFdr, &p, sizeof(p)) != sizeof(p)) { perror("Thread::readMessage(): read pipe failed"); exit(-1); } processMsg(p); char c = 'x'; int rv = write(fromThreadFdw, &c, 1); if (rv != 1) perror("Thread::readMessage(): write pipe failed"); //int c = p->serialNo; DELETETHIS 4 //int rv = write(fromThreadFdw, &c, sizeof(c)); //if (rv != sizeof(c)) // perror("Thread::readMsg(): write pipe failed"); } //--------------------------------------------------------- // readMsg // sequencer reads one gui message //--------------------------------------------------------- void Thread::readMsg1(int size) { char buffer[size]; int n = read(toThreadFdr, buffer, size); if (n != size) { fprintf(stderr, "Thread::readMsg1(): read pipe failed, get %d, expected %d: %s\n", n, size, strerror(errno)); exit(-1); } processMsg1(buffer); } } // namespace MusECore muse-4.0.0/src/muse/thread.h000066400000000000000000000067571404105311000156500ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: thread.h,v 1.1.1.1.2.2 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __THREAD_H__ #define __THREAD_H__ #include #include struct pollfd; namespace MusECore { //--------------------------------------------------------- // Poll //--------------------------------------------------------- struct Poll { int fd; int action; void (*handler)(void*,void*); void* param1; void* param2; Poll(int _fd, int _action, void(*_handler)(void*,void*), void* p, void* q) { fd = _fd; action = _action; handler = _handler; param1 = p; param2 = q; } }; typedef std::list PollList; typedef std::list::iterator iPoll; //--------------------------------------------------------- // ThreadMsg //--------------------------------------------------------- struct ThreadMsg { int id; }; //--------------------------------------------------------- // Thread //--------------------------------------------------------- class Thread { const char* _name; volatile bool _running; int _pollWait; // poll timeout in msec (-1 = infinite) pthread_t thread; int toThreadFdw; // message to thread (app write) PollList plist; void* userPtr; protected: int _realTimePriority; int fromThreadFdr; // message from thread (seq read) int fromThreadFdw; // message from thread (app write) int toThreadFdr; // message to thread (seq read) struct pollfd* pfd; // poll file descriptors int npfd; int maxpfd; virtual void processMsg(const ThreadMsg*) {} virtual void processMsg1(const void *) {} virtual void defaultTick() {} public: Thread(const char* name); virtual ~Thread(); const char* name() const { return _name; } virtual void start(int priority, void* ptr=0); void stop(bool); void clearPollFd() { plist.clear(); npfd = 0; } void addPollFd(int fd, int action, void (*handler)(void*,void*), void*, void*); void removePollFd(int fd, int action); void loop(); void readMsg(); void readMsg1(int size); bool sendMsg1(const void* m, int n); bool sendMsg(const ThreadMsg* m); bool isRunning() const { return _running; } void setPollWait(int val) { _pollWait = val; } virtual void threadStart(void*){ } // called from loop virtual void threadStop() { } // called from loop before leaving }; } // namespace MusECore #endif muse-4.0.0/src/muse/ticksynth.cpp000066400000000000000000000702011404105311000167350ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ticksynth.cpp,v 1.8.2.7 2009/12/20 05:00:35 terminator356 Exp $ // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "audio.h" #include "ticksynth.h" #include "default_click.h" #include "midiport.h" #include "midi_consts.h" #include "popupmenu.h" #include "gconfig.h" #include "wave.h" #include "operations.h" #include "metronome_class.h" #include "globals.h" #include "song.h" #include "track.h" // If sysex support is ever added, make sure this number is unique among all the // MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. // 127 is reserved for special MusE system messages. //#define METRONOME_UNIQUE_ID 7 // // Undefine if and when multiple output routes are added to midi tracks. // #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ //#define METRONOME_DEBUG // For debugging output: Uncomment the fprintf section. #define DEBUG_TICKSYNTH(dev, format, args...) //fprintf(dev, format, ##args); namespace MusECore { MetronomeSynthI* metronome = 0; class MetronomeSynth; static MetronomeSynth* metronomeSynth = 0; //--------------------------------------------------------- // MetronomeSynth //--------------------------------------------------------- class MetronomeSynth : public Synth { public: MetronomeSynth(const QFileInfo& fi, const QString& uri) : Synth(fi, uri, QString("Metronome"), QString("Metronome"), QString(), QString()) {} virtual ~MetronomeSynth() {} inline virtual Type synthType() const { return METRO_SYNTH; } inline virtual void incInstances(int) {} virtual void* instantiate(); virtual SynthIF* createSIF(SynthI*); }; //--------------------------------------------------------- // instantiate //--------------------------------------------------------- void* MetronomeSynth::instantiate() { return 0; } //--------------------------------------------------------- // MetronomeSynthIF //--------------------------------------------------------- class MetronomeSynthIF : public SynthIF { const float* data; int pos; int len; float volume; void process(float** buffer, int offset, int n); void initSamples(); float *measSamples; int measLen; float *beatSamples; int beatLen; float *accent1Samples; int accent1Len; float *accent2Samples; int accent2Len; float *defaultClickEmphasisConverted; int defaultClickEmphasisLengthConverted; float *defaultClickConverted; int defaultClickLengthConverted; bool processEvent(const MidiPlayEvent& ev); public: MetronomeSynthIF(SynthI* s) : SynthIF(s) { data = 0; beatLen = 0; measLen = 0; accent1Len = 0; accent2Len = 0; measSamples = nullptr; beatSamples = nullptr; accent1Samples = nullptr; accent2Samples = nullptr; defaultClickEmphasisConverted = nullptr; defaultClickEmphasisLengthConverted = 0; defaultClickConverted = nullptr; defaultClickLengthConverted = 0; initSamples(); } virtual ~MetronomeSynthIF(); inline virtual void guiHeartBeat() { } inline virtual bool guiVisible() const { return false; } inline virtual bool hasGui() const { return false; } inline virtual bool nativeGuiVisible() const { return false; } inline virtual void showNativeGui(bool) { } inline virtual bool hasNativeGui() const { return false; } inline virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } inline virtual void setNativeGeometry(int, int, int, int) {} virtual bool getData(MidiPort*, unsigned pos, int ports, unsigned n, float** buffer); virtual MidiPlayEvent receiveEvent() { return MidiPlayEvent(); } virtual int eventsPending() const { return 0; } inline virtual int channels() const { return 1; } inline virtual int totalOutChannels() const { return 1; } inline virtual int totalInChannels() const { return 0; } inline virtual void deactivate3() {} inline virtual QString getPatchName(int, int, bool) const { return ""; } inline virtual void populatePatchPopup(MusEGui::PopupMenu*, int, bool) {} inline virtual void write(int, Xml&) const {} inline virtual double getParameter(unsigned long) const { return 0.0; } inline virtual void setParameter(unsigned long, double) {} inline virtual int getControllerInfo(int, QString*, int*, int*, int*, int*) { return 0; } void initSamplesOperation(MusECore::PendingOperationList&); //------------------------- // Methods for PluginIBase: //------------------------- virtual bool addScheduledControlEvent(unsigned long /*i*/, double /*val*/, unsigned /*frame*/) { return true; } // returns true if event cannot be delivered }; MetronomeSynthIF::~MetronomeSynthIF() { if (beatSamples) delete [] beatSamples; if (measSamples) delete [] measSamples; if (accent1Samples) delete [] accent1Samples; if (accent2Samples) delete [] accent2Samples; if (defaultClickEmphasisConverted) delete [] defaultClickEmphasisConverted; if (defaultClickConverted) delete [] defaultClickConverted; } //--------------------------------------------------------- // getData //--------------------------------------------------------- bool MetronomeSynthIF::getData(MidiPort*, unsigned /*pos*/, int/*ports*/, unsigned n, float** buffer) { #ifdef METRONOME_DEBUG fprintf(stderr, "MusE: MetronomeSynthIF::getData\n"); #endif const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); unsigned int curPos = 0; unsigned int frame = 0; // Get the state of the stop flag. const bool do_stop = synti->stopFlag(); MidiPlayEvent buf_ev; // Transfer the user lock-free buffer events to the user sorted multi-set. // False = don't use the size snapshot, but update it. const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); for(unsigned int i = 0; i < usr_buf_sz; ++i) { if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) synti->_outUserEvents.insert(buf_ev); } // Transfer the playback lock-free buffer events to the playback sorted multi-set. const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); for(unsigned int i = 0; i < pb_buf_sz; ++i) { // Are we stopping? Just remove the item. if(do_stop) synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); // Otherwise get the item. else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) synti->_outPlaybackEvents.insert(buf_ev); } // Are we stopping? if(do_stop) { // Transport has stopped, purge ALL further scheduled playback events now. synti->_outPlaybackEvents.clear(); // Reset the flag. synti->setStopFlag(false); } iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); iMPEvent impe_us = synti->_outUserEvents.begin(); bool using_pb; while(1) { if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) using_pb = *impe_pb < *impe_us; else if(impe_pb != synti->_outPlaybackEvents.end()) using_pb = true; else if(impe_us != synti->_outUserEvents.end()) using_pb = false; else break; const MidiPlayEvent& ev = using_pb ? *impe_pb : *impe_us; const unsigned int evTime = ev.time(); if(evTime < syncFrame) { fprintf(stderr, "MetronomeSynthIF::getData() evTime:%u < syncFrame:%u!! curPos=%d\n", evTime, syncFrame, curPos); frame = 0; } else frame = evTime - syncFrame; // Event is for future? if(frame >= n) { DEBUG_TICKSYNTH(stderr, "MetronomeSynthIF::getData(): Event for future, breaking loop: frame:%u n:%d evTime:%u syncFrame:%u curPos:%d\n", frame, n, evTime, syncFrame, curPos); //continue; break; } if(frame > curPos) { process(buffer, curPos, frame - curPos); curPos = frame; } // If putEvent fails, although we would like to not miss events by keeping them // until next cycle and trying again, that can lead to a large backup of events // over a long time. So we'll just... miss them. //putEvent(ev); //synti->putEvent(ev); processEvent(ev); // Done with ring buffer event. Remove it from FIFO. // C++11. if(using_pb) impe_pb = synti->_outPlaybackEvents.erase(impe_pb); else impe_us = synti->_outUserEvents.erase(impe_us); } if(curPos < n) process(buffer, curPos, n - curPos); return true; } //--------------------------------------------------------- // initSamples //--------------------------------------------------------- void MetronomeSynthIF::initSamples() { if (beatSamples) delete beatSamples; if (measSamples) delete measSamples; if (accent1Samples) delete accent1Samples; if (accent2Samples) delete accent2Samples; beatLen = 0; measLen = 0; accent1Len = 0; accent2Len = 0; beatSamples = nullptr; measSamples = nullptr; accent1Samples = nullptr; accent2Samples = nullptr; MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; { SndFile beat(MusEGlobal::museGlobalShare + "/metronome/" + metro_settings->beatSample, true, true); if (!beat.openRead(false)) { beatLen = beat.samplesConverted(); beatSamples = new float[beatLen]; beat.readConverted(0 /* pos */, 1 /* channels */, &beatSamples, beatLen); } } { SndFile meas(MusEGlobal::museGlobalShare + "/metronome/" + metro_settings->measSample, true, true); if (!meas.openRead(false)) { measLen = meas.samplesConverted(); measSamples = new float[measLen]; meas.readConverted(0, 1, &measSamples, measLen); } } { SndFile accent1(MusEGlobal::museGlobalShare + "/metronome/" + metro_settings->accent1Sample, true, true); if (!accent1.openRead(false)) { accent1Len = accent1.samplesConverted(); accent1Samples = new float[accent1Len]; accent1.readConverted(0, 1, &accent1Samples, accent1Len); } } { SndFile accent2(MusEGlobal::museGlobalShare + "/metronome/" + metro_settings->accent2Sample, true, true); if (!accent2.openRead(false)) { accent2Len = accent2.samplesConverted(); accent2Samples = new float[accent2Len]; accent2.readConverted(0, 1, &accent2Samples, accent2Len); } } { SndFile defClickEmphasis((void*)defaultClickEmphasis, sizeof(defaultClickEmphasis), true, true); defClickEmphasis.setFormat(SF_FORMAT_RAW | SF_FORMAT_FLOAT, 1, 44100, defaultClickEmphasisLength); if (!defClickEmphasis.openRead(false)) { defaultClickEmphasisLengthConverted = defClickEmphasis.samplesConverted(); defaultClickEmphasisConverted = new float[defaultClickEmphasisLengthConverted]; defClickEmphasis.readConverted(0, 1, &defaultClickEmphasisConverted, defaultClickEmphasisLengthConverted); } } { SndFile defClick((void*)defaultClick, sizeof(defaultClick), true, true); defClick.setFormat(SF_FORMAT_RAW | SF_FORMAT_FLOAT, 1, 44100, defaultClickLength); if (!defClick.openRead(false)) { defaultClickLengthConverted = defClick.samplesConverted(); defaultClickConverted = new float[defaultClickLengthConverted]; defClick.readConverted(0, 1, &defaultClickConverted, defaultClickLengthConverted); } } } //--------------------------------------------------------- // initSamplesOperation //--------------------------------------------------------- void MetronomeSynthIF::initSamplesOperation(MusECore::PendingOperationList& operations) { MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; SndFile beat(MusEGlobal::museGlobalShare + "/metronome/" + metro_settings->beatSample, true, true); if (!beat.openRead(false)) { const sf_count_t newBeatLen = beat.samplesConverted(); if(newBeatLen != 0) { float* newBeatSamples = new float[newBeatLen]; beat.readConverted(0, 1, &newBeatSamples, newBeatLen); operations.add(PendingOperationItem(&beatSamples, newBeatSamples, &beatLen, newBeatLen, PendingOperationItem::ModifyAudioSamples)); } } SndFile meas(MusEGlobal::museGlobalShare + "/metronome/" + metro_settings->measSample, true, true); if (!meas.openRead(false)) { const sf_count_t newMeasLen = meas.samplesConverted(); if(newMeasLen != 0) { float* newMeasSamples = new float[newMeasLen]; meas.readConverted(0, 1, &newMeasSamples, newMeasLen); operations.add(PendingOperationItem(&measSamples, newMeasSamples, &measLen, newMeasLen, PendingOperationItem::ModifyAudioSamples)); } } SndFile accent1(MusEGlobal::museGlobalShare + "/metronome/" + metro_settings->accent1Sample, true, true); if (!accent1.openRead(false)) { const sf_count_t newAccent1Len = accent1.samplesConverted(); if(newAccent1Len != 0) { float* newAccent1Samples = new float[newAccent1Len]; accent1.readConverted(0, 1, &newAccent1Samples, newAccent1Len); operations.add(PendingOperationItem(&accent1Samples, newAccent1Samples, &accent1Len, newAccent1Len, PendingOperationItem::ModifyAudioSamples)); } } SndFile accent2(MusEGlobal::museGlobalShare + "/metronome/" + metro_settings->accent2Sample, true, true); if (!accent2.openRead(false)) { const sf_count_t newAccent2Len = accent2.samplesConverted(); if(newAccent2Len != 0) { float* newAccent2Samples = new float[newAccent2Len]; accent2.readConverted(0, 1, &newAccent2Samples, newAccent2Len); operations.add(PendingOperationItem(&accent2Samples, newAccent2Samples, &accent2Len, newAccent2Len, PendingOperationItem::ModifyAudioSamples)); } } } //--------------------------------------------------------- // putEvent //--------------------------------------------------------- bool MetronomeSynthIF::processEvent(const MidiPlayEvent& ev) { if(ev.type() != MusECore::ME_NOTEON) return false; MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if (ev.dataA() == MusECore::measureSound) { if (metro_settings->clickSamples == MetronomeSettings::origSamples) { data = defaultClickEmphasisConverted; len = defaultClickEmphasisLengthConverted; } else { data = measSamples; len = measLen; } volume = metro_settings->measClickVolume; } else if (ev.dataA() == MusECore::beatSound) { if (metro_settings->clickSamples == MetronomeSettings::origSamples) { data = defaultClickConverted; len = defaultClickLengthConverted; } else { data = beatSamples; len = beatLen; } volume = metro_settings->beatClickVolume; } else if (ev.dataA() == MusECore::accent1Sound) { data = accent1Samples; len = accent1Len; volume = metro_settings->accent1ClickVolume; if (metro_settings->clickSamples == MetronomeSettings::origSamples) { volume=0.0; } } else if (ev.dataA() == MusECore::accent2Sound) { data = accent2Samples; len = accent2Len; volume = metro_settings->accent2ClickVolume; if (metro_settings->clickSamples == MetronomeSettings::origSamples) { volume=0.0; } } pos = 0; return false; } //--------------------------------------------------------- // createSIF //--------------------------------------------------------- SynthIF* MetronomeSynth::createSIF(SynthI* s) { return new MetronomeSynthIF(s); } //--------------------------------------------------------- // process // synthesize n samples into buffer+offset //--------------------------------------------------------- void MetronomeSynthIF::process(float** buffer, int offset, int n) { #ifdef METRONOME_DEBUG printf("MusE: MetronomeSynthIF::process data:%p offset:%d n:%d\n", data, offset, n); #endif if (data == 0) return; MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; const float* s = data + pos; float* d = *buffer + offset; int l = std::min(n, len); for (int i = 0; i < l; ++i) *d++ += *s++ * metro_settings->audioClickVolume * volume; pos += l; len -= l; if (len <= 0) data = 0; } //--------------------------------------------------------- // MetronomeSynthI //--------------------------------------------------------- void MetronomeSynthI::initSamplesOperation(PendingOperationList& operations) { if(sif()) dynamic_cast(sif())->initSamplesOperation(operations); } //================================================ // BEGIN Latency correction/compensation routines. //================================================ //--------------------------------------------------------- // isLatencyInputTerminal //--------------------------------------------------------- bool MetronomeSynthI::isLatencyInputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyInputTerminalProcessed) return _latencyInfo._isLatencyInputTerminal; // Ultimately if the track is off there is no audio or midi processing, so it's a terminal. if(off() /*|| !MusEGlobal::song->click()*/) { _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if(metro_settings->audioClickFlag) { const ciAudioOutput tl_end = MusEGlobal::song->outputs()->cend(); for(ciAudioOutput it = MusEGlobal::song->outputs()->cbegin(); it != tl_end; ++it) { const AudioOutput* ao = *it; if(ao->off()) continue; if(ao->sendMetronome()) { _latencyInfo._isLatencyInputTerminal = false; _latencyInfo._isLatencyInputTerminalProcessed = true; return false; } } } if(metro_settings->midiClickFlag /*&& !precount_mute_metronome*/) { const int port = metro_settings->clickPort; if((openFlags() & 2 /*read*/) && port >= 0 && port < MusECore::MIDI_PORTS) { const MidiPort* mp = &MusEGlobal::midiPorts[port]; const MidiDevice* md = mp->device(); // TODO Make a new SynthI::isReadable and isWritable to replace this and MANY others in the app, and add to some places // where it's not even used. Prevent sending to the synth, maybe finally get rid of the flushing in SynthI::preProcessAlways() if(md && ((md->openFlags() & 1 /*write*/) && (!md->isSynti() || !static_cast(md)->off()))) { _latencyInfo._isLatencyInputTerminal = false; _latencyInfo._isLatencyInputTerminalProcessed = true; return false; } } } _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } //--------------------------------------------------------- // isLatencyOutputTerminal //--------------------------------------------------------- bool MetronomeSynthI::isLatencyOutputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyOutputTerminalProcessed) return _latencyInfo._isLatencyOutputTerminal; MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if(metro_settings->audioClickFlag) { const ciAudioOutput tl_end = MusEGlobal::song->outputs()->cend(); for(ciAudioOutput it = MusEGlobal::song->outputs()->cbegin(); it != tl_end; ++it) { const AudioOutput* ao = *it; if(ao->off()) continue; if(ao->sendMetronome()) { _latencyInfo._isLatencyOutputTerminal = false; _latencyInfo._isLatencyOutputTerminalProcessed = true; return false; } } } if(metro_settings->midiClickFlag /*&& !precount_mute_metronome*/) { const int port = metro_settings->clickPort; if((openFlags() & 2 /*read*/) && port >= 0 && port < MusECore::MIDI_PORTS) { const MidiPort* mp = &MusEGlobal::midiPorts[port]; const MidiDevice* md = mp->device(); // TODO Make a new SynthI::isReadable and isWritable to replace this and MANY others in the app, and add to some places // where it's not even used. Prevent sending to the synth, maybe finally get rid of the flushing in SynthI::preProcessAlways() if(md && ((md->openFlags() & 1 /*write*/) && (!md->isSynti() || !static_cast(md)->off()))) { _latencyInfo._isLatencyOutputTerminal = false; _latencyInfo._isLatencyOutputTerminalProcessed = true; return false; } } } _latencyInfo._isLatencyOutputTerminal = true; _latencyInfo._isLatencyOutputTerminalProcessed = true; return true; } bool MetronomeSynthI::isLatencyInputTerminalMidi(bool capture) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if(tli->_isLatencyInputTerminalProcessed) return tli->_isLatencyInputTerminal; // Ultimately if the track is off there is no audio or midi processing, so it's a terminal. if(off() /*|| !MusEGlobal::song->click()*/) { tli->_isLatencyInputTerminal = true; tli->_isLatencyInputTerminalProcessed = true; return true; } MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if(metro_settings->audioClickFlag) { const ciAudioOutput tl_end = MusEGlobal::song->outputs()->cend(); for(ciAudioOutput it = MusEGlobal::song->outputs()->cbegin(); it != tl_end; ++it) { const AudioOutput* ao = *it; if(ao->off()) continue; if(ao->sendMetronome()) { tli->_isLatencyInputTerminal = false; tli->_isLatencyInputTerminalProcessed = true; return false; } } } if(capture/*Tim*/ && metro_settings->midiClickFlag /*&& !precount_mute_metronome*/) { const int port = metro_settings->clickPort; if((openFlags() & 2 /*read*/) && port >= 0 && port < MusECore::MIDI_PORTS) { const MidiPort* mp = &MusEGlobal::midiPorts[port]; const MidiDevice* md = mp->device(); // TODO Make a new SynthI::isReadable and isWritable to replace this and MANY others in the app, and add to some places // where it's not even used. Prevent sending to the synth, maybe finally get rid of the flushing in SynthI::preProcessAlways() if(md && ((md->openFlags() & 1 /*write*/) && (!md->isSynti() || !static_cast(md)->off()))) { tli->_isLatencyInputTerminal = false; tli->_isLatencyInputTerminalProcessed = true; return false; } } } tli->_isLatencyInputTerminal = true; tli->_isLatencyInputTerminalProcessed = true; return true; } bool MetronomeSynthI::isLatencyOutputTerminalMidi(bool capture) { TrackLatencyInfo* tli = capture ? &_captureLatencyInfo : &_playbackLatencyInfo; // Have we been here before during this scan? // Just return the cached value. if(tli->_isLatencyOutputTerminalProcessed) return tli->_isLatencyOutputTerminal; MusECore::MetronomeSettings* metro_settings = MusEGlobal::metroUseSongSettings ? &MusEGlobal::metroSongSettings : &MusEGlobal::metroGlobalSettings; if(metro_settings->audioClickFlag) { const ciAudioOutput tl_end = MusEGlobal::song->outputs()->cend(); for(ciAudioOutput it = MusEGlobal::song->outputs()->cbegin(); it != tl_end; ++it) { const AudioOutput* ao = *it; if(ao->off()) continue; if(ao->sendMetronome()) { tli->_isLatencyOutputTerminal = false; tli->_isLatencyOutputTerminalProcessed = true; return false; } } } if(capture/*Tim*/ && metro_settings->midiClickFlag /*&& !precount_mute_metronome*/) { const int port = metro_settings->clickPort; if((openFlags() & 2 /*read*/) && port >= 0 && port < MusECore::MIDI_PORTS) { const MidiPort* mp = &MusEGlobal::midiPorts[port]; const MidiDevice* md = mp->device(); // TODO Make a new SynthI::isReadable and isWritable to replace this and MANY others in the app, and add to some places // where it's not even used. Prevent sending to the synth, maybe finally get rid of the flushing in SynthI::preProcessAlways() if(md && ((md->openFlags() & 1 /*write*/) && (!md->isSynti() || !static_cast(md)->off()))) { tli->_isLatencyOutputTerminal = false; tli->_isLatencyOutputTerminalProcessed = true; return false; } } } tli->_isLatencyOutputTerminal = true; tli->_isLatencyOutputTerminalProcessed = true; return true; } //================================================ // END Latency correction/compensation routines. //================================================ //--------------------------------------------------------- // initMetronome //--------------------------------------------------------- void initMetronome() { QFileInfo fi; metronomeSynth = new MetronomeSynth(fi, QString()); metronome = new MetronomeSynthI(); QString name("metronome"); metronome->initInstance(metronomeSynth, name); } //--------------------------------------------------------- // exitMetronome //--------------------------------------------------------- void exitMetronome() { if(metronome) delete metronome; metronome = 0; if(metronomeSynth) delete metronomeSynth; metronomeSynth = 0; } } // namespace MusECore muse-4.0.0/src/muse/ticksynth.h000066400000000000000000000060771404105311000164140ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ticksynth.h,v 1.1.2.3 2009/12/06 10:05:00 terminator356 Exp $ // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TICKSYNTH_H__ #define __TICKSYNTH_H__ #include "synth.h" namespace MusECore { class PendingOperationList; extern void initMetronome(); extern void exitMetronome(); class MetronomeSynthI : public SynthI { virtual bool hasAuxSend() const { return false; } public: void initSamplesOperation(PendingOperationList& operations); // The built-in metronome synth is special: It can correct latency by itself, and is // not driven by a midi track which can correct latency. inline bool canCorrectOutputLatencyMidi() const { return true; } bool isLatencyInputTerminalMidi(bool capture); bool isLatencyOutputTerminalMidi(bool capture); // The built-in metronome synth is special: It can correct latency by itself, and is // not driven by a midi track which can correct latency. inline bool canCorrectOutputLatency() const { return true; } // The built-in metronome synth is special: It cannot pass thru latency. inline bool canPassThruLatency() const { return false; } bool isLatencyInputTerminal(); bool isLatencyOutputTerminal(); //------------------------------------------------------------------------ // The metronome synth is special - it cannot be routed like other tracks, // and thus cannot be muted, soloed, recorded, or monitored. //------------------------------------------------------------------------ inline virtual void setMute(bool) { } inline virtual void setOff(bool) { } inline virtual void setSolo(bool) { } inline virtual bool isMute() const { return false; } inline virtual unsigned int internalSolo() const { return 0; } inline virtual bool soloMode() const { return false; } inline virtual bool solo() const { return false; } inline virtual bool mute() const { return false; } inline virtual bool off() const { return false; } inline virtual bool recordFlag() const { return false; } inline virtual void setRecMonitor(bool) { } inline virtual bool recMonitor() const { return false; } }; extern MetronomeSynthI* metronome; } // namespace MusECore #endif muse-4.0.0/src/muse/track.cpp000066400000000000000000003047051404105311000160320ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: track.cpp,v 1.34.2.11 2009/11/30 05:05:49 terminator356 Exp $ // // (C) Copyright 2000-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011, 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "track.h" #include "event.h" #include "midi_consts.h" #include "mididev.h" #include "song.h" #include "audio.h" #include "midictrl.h" #include "helper.h" #include "limits.h" #include "dssihost.h" #include "gconfig.h" #include "operations.h" #include "icons.h" #include "drum_ordering.h" #include "globals.h" #include "config.h" #include // Forwards from header: #include "midiport.h" #include "plugin.h" #include "xml.h" #include "midiedit/drummap.h" #include "minstrument.h" #include "latency_compensator.h" // Undefine if and when multiple output routes are added to midi tracks. #define _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ namespace MusECore { int Track::_snGen=0; unsigned int Track::_soloRefCnt = 0; Track* Track::_tmpSoloChainTrack = 0; bool Track::_tmpSoloChainDoIns = false; bool Track::_tmpSoloChainNoDec = false; int Track::_selectionOrderCounter = 0; const char* Track::_cname[] = { "Midi", "Drum", "Wave", "AudioOut", "AudioIn", "AudioGroup", "AudioAux", "AudioSynth" }; bool MidiTrack::_isVisible=true; //--------------------------------------------------------- // addPortCtrlEvents //--------------------------------------------------------- void addPortCtrlEvents(MidiTrack* t, bool drum_ctls, bool non_drum_ctls) { if(!drum_ctls && !non_drum_ctls) return; bool is_drum_ctl; const PartList* pl = t->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* part = ip->second; const EventList& el = part->events(); unsigned len = part->lenTick(); for(ciEvent ie = el.begin(); ie != el.end(); ++ie) { const Event& ev = ie->second; // Do not add events which are past the end of the part. #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)ev.tick() >= (int)len) #else if(ev.tick() >= len) #endif break; #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)ev.tick() < 0) continue; #endif if(ev.type() == Controller) { int tick = ev.tick() + part->tick(); int cntrl = ev.dataA(); int val = ev.dataB(); // Is it a drum controller event, according to the track port's instrument? MidiPort* mp; int ch; is_drum_ctl = t->mappedPortChanCtrl(&cntrl, nullptr, &mp, &ch); if((is_drum_ctl && drum_ctls) || (!is_drum_ctl && non_drum_ctls)) mp->setControllerVal(ch, tick, cntrl, val, part); } } } } //--------------------------------------------------------- // removePortCtrlEvents //--------------------------------------------------------- void removePortCtrlEvents(MidiTrack* t, bool drum_ctls, bool non_drum_ctls) { if(!drum_ctls && !non_drum_ctls) return; bool is_drum_ctl; const PartList* pl = t->cparts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* part = ip->second; const EventList& el = part->events(); for(ciEvent ie = el.begin(); ie != el.end(); ++ie) { const Event& ev = ie->second; if(ev.type() == Controller) { int tick = ev.tick() + part->tick(); int cntrl = ev.dataA(); int val = ev.dataB(); // Is it a drum controller event, according to the track port's instrument? MidiPort* mp; int ch; is_drum_ctl = t->mappedPortChanCtrl(&cntrl, nullptr, &mp, &ch); if((is_drum_ctl && drum_ctls) || (!is_drum_ctl && non_drum_ctls)) mp->deleteController(ch, tick, cntrl, val, part); } } } } //--------------------------------------------------------- // isVisible //--------------------------------------------------------- bool Track::isVisible() { switch (type()) { case Track::AUDIO_AUX: return AudioAux::visible(); case Track::AUDIO_GROUP: return AudioGroup::visible(); case Track::AUDIO_INPUT: return AudioInput::visible(); case Track::AUDIO_OUTPUT: return AudioOutput::visible(); case Track::WAVE: return WaveTrack::visible(); case Track::MIDI: case Track::DRUM: return MidiTrack::visible(); case Track::AUDIO_SOFTSYNTH: return SynthI::visible(); default: break; } return false; } //--------------------------------------------------------- // y //--------------------------------------------------------- int Track::y() const { TrackList* tl = MusEGlobal::song->tracks(); int yy = 0; for (ciTrack it = tl->begin(); it != tl->end(); ++it) { if (this == *it) return yy; yy += (*it)->height(); } // FIXME Get this when loading a song with automation graphs showing. Benign. Likely song not fully loaded yet. p4.0.32 if(MusEGlobal::debugMsg) printf("Track::y(%s): track not in tracklist\n", name().toLatin1().constData()); return -1; } //--------------------------------------------------------- // Track::init //--------------------------------------------------------- void Track::init(int channels) { _auxRouteCount = 0; _nodeTraversed = false; _activity = 0; _lastActivity = 0; _recordFlag = false; _mute = false; _solo = false; _internalSolo = 0; _off = false; _channels = channels; // 1 - mono, 2 - stereo _selected = false; _selectionOrder = 0; _height = MusEGlobal::config.trackHeight; _locked = false; _recMonitor = false; for (int i = 0; i < MusECore::MAX_CHANNELS; ++i) { _meter[i] = 0.0; _peak[i] = 0.0; _isClipped[i] = false; } } Track::Track(Track::TrackType t, int channels) { _type = t; _sn = newSn(); init(channels); } Track::Track(const Track& t, int flags) { _type = t.type(); // This should be reset for a copy of a track. _sn = newSn(); init(t._channels); // moved setting the unique name to Song::duplicateTracks() // we'll see if there is any draw back to that. // See comments in the header about naming copied tracks, // and why we don't automatically pick a unique name. _name = t.name(); internal_assign(t, flags | ASSIGN_PROPERTIES); } Track::~Track() { _parts.clearDelete(); } //--------------------------------------------------------- // assign //--------------------------------------------------------- void Track::assign(const Track& t, int flags) { internal_assign(t, flags); } void Track::internal_assign(const Track& t, int flags) { if(flags & ASSIGN_PROPERTIES) { // Leave at init value. Should be set by adding routes later. //_auxRouteCount = t._auxRouteCount; // Leave at init value. Set by anti-circular routing mechanism later. //_nodeTraversed = t._nodeTraversed; // Leave at init value. (How can there be 'activity' yet on a new track?) //_activity = t._activity; //_lastActivity = t._lastActivity; // Leave at init value. Likely we don't want the new track soloed, // although there may be cases where we want that (such as modifying a // track copy and then requesting the operations system switch tracks - // in that case we likely want the solo to be the same). For now, caller // can set afterwards. TODO Decide whether to copy it. //_solo = t._solo; // Leave at init value. Set by soloing and routing mechanisms later. //_internalSolo = t._internalSolo; // Leave at init value. Set by init(). //_channels = t._channels; // Special for selected. Make sure this is called. // NOTE This makes this track the most recent selected. TODO Try to leave // the original track as the most recent selected. (More involved - the // '_selected' members of all tracks would need to be re-indexed). setSelected(t.selected()); // Leave at init value. Set by setSelected(), above. //_selectionOrder = t.selectionOrder(); // For now, copy this. It may be set later. _y = t._y; // For now, copy this. The caller may set it to some other value after. _height = t._height; _off = t._off; _recordFlag = t._recordFlag; _mute = t._mute; _comment = t.comment(); _locked = t.locked(); _recMonitor = t._recMonitor; } } //--------------------------------------------------------- // trackTypeIcon // Static //--------------------------------------------------------- QIcon* Track::trackTypeIcon(TrackType type) { switch(type) { case MusECore::Track::MIDI: return MusEGui::pianorollSVGIcon; case MusECore::Track::DRUM: return MusEGui::drumeditSVGIcon; case MusECore::Track::WAVE: return MusEGui::waveeditorSVGIcon; case MusECore::Track::AUDIO_OUTPUT: return MusEGui::trackOutputSVGIcon; case MusECore::Track::AUDIO_INPUT: return MusEGui::trackInputSVGIcon; case MusECore::Track::AUDIO_GROUP: return MusEGui::trackGroupVGIcon; case MusECore::Track::AUDIO_AUX: return MusEGui::trackAuxSVGIcon; case MusECore::Track::AUDIO_SOFTSYNTH: return MusEGui::synthSVGIcon; } return nullptr; } //--------------------------------------------------------- // trackTypeColor // Static //--------------------------------------------------------- QColor Track::trackTypeColor(TrackType type) { switch(type) { case MusECore::Track::MIDI: return MusEGlobal::config.midiTrackBg; case MusECore::Track::DRUM: return MusEGlobal::config.newDrumTrackBg; case MusECore::Track::WAVE: return MusEGlobal::config.waveTrackBg; case MusECore::Track::AUDIO_OUTPUT: return MusEGlobal::config.outputTrackBg; case MusECore::Track::AUDIO_INPUT: return MusEGlobal::config.inputTrackBg; case MusECore::Track::AUDIO_GROUP: return MusEGlobal::config.groupTrackBg; case MusECore::Track::AUDIO_AUX: return MusEGlobal::config.auxTrackBg; case MusECore::Track::AUDIO_SOFTSYNTH: return MusEGlobal::config.synthTrackBg; default: break; } return QColor(); } //--------------------------------------------------------- // trackTypeLabelColor // Static //--------------------------------------------------------- QColor Track::trackTypeLabelColor(TrackType type) { switch(type) { case MusECore::Track::MIDI: return MusEGlobal::config.midiTrackLabelBg; case MusECore::Track::DRUM: return MusEGlobal::config.newDrumTrackLabelBg; case MusECore::Track::WAVE: return MusEGlobal::config.waveTrackLabelBg; case MusECore::Track::AUDIO_OUTPUT: return MusEGlobal::config.outputTrackLabelBg; case MusECore::Track::AUDIO_INPUT: return MusEGlobal::config.inputTrackLabelBg; case MusECore::Track::AUDIO_GROUP: return MusEGlobal::config.groupTrackLabelBg; case MusECore::Track::AUDIO_AUX: return MusEGlobal::config.auxTrackLabelBg; case MusECore::Track::AUDIO_SOFTSYNTH: return MusEGlobal::config.synthTrackLabelBg; default: break; } return QColor(); } //--------------------------------------------------------- // displayName //--------------------------------------------------------- QString Track::displayName() const { const int idx = MusEGlobal::song->tracks()->index(this); return QString("%1:%2").arg(idx + 1).arg(_name); } //--------------------------------------------------------- // clearRecAutomation //--------------------------------------------------------- void Track::clearRecAutomation(bool clearList) { if(isMidiTrack()) return; AudioTrack *t = static_cast(this); // Re-enable all track and plugin controllers, and synth controllers if applicable. t->enableAllControllers(); if(clearList) t->recEvents()->clear(); } //--------------------------------------------------------- // setSelected //--------------------------------------------------------- void Track::setSelected(bool f) { if(f && !_selected) _selectionOrder = _selectionOrderCounter++; _selected = f; } //--------------------------------------------------------- // dump //--------------------------------------------------------- void Track::dump() const { printf("Track <%s>: typ %d, parts %zd sel %d sel order%d\n", _name.toLatin1().constData(), _type, _parts.size(), _selected, _selectionOrder); } //--------------------------------------------------------- // updateAuxRoute // Internal use. Update all the Aux ref counts of tracks dst is connected to. // If dst is valid, start traversal from there, not from this track. //--------------------------------------------------------- void Track::updateAuxRoute(int refInc, Track* dst) { if(isMidiTrack()) return; if(dst) { _nodeTraversed = true; dst->updateAuxRoute(refInc, NULL); _nodeTraversed = false; return; } if(_type == AUDIO_AUX) return; if(_nodeTraversed) { fprintf(stderr, "Track::updateAuxRoute %s _auxRouteCount:%d refInc:%d :\n", name().toLatin1().constData(), _auxRouteCount, refInc); if(refInc >= 0) fprintf(stderr, " MusE Warning: Please check your routes: Circular path found!\n"); else fprintf(stderr, " MusE: Circular path removed.\n"); return; } _nodeTraversed = true; _auxRouteCount += refInc; if(_auxRouteCount < 0) { fprintf(stderr, "Track::updateAuxRoute Ref underflow! %s _auxRouteCount:%d refInc:%d\n", name().toLatin1().constData(), _auxRouteCount, refInc); } for (iRoute i = _outRoutes.begin(); i != _outRoutes.end(); ++i) { if( !(*i).isValid() || (*i).type != Route::TRACK_ROUTE ) continue; Track* t = (*i).track; t->updateAuxRoute(refInc, NULL); } _nodeTraversed = false; } //--------------------------------------------------------- // isCircularRoute // If dst is valid, start traversal from there, not from this track. // Returns true if circular. //--------------------------------------------------------- bool Track::isCircularRoute(Track* dst) { bool rv = false; if(dst) { _nodeTraversed = true; rv = dst->isCircularRoute(NULL); _nodeTraversed = false; return rv; } if(_nodeTraversed) return true; _nodeTraversed = true; for (iRoute i = _outRoutes.begin(); i != _outRoutes.end(); ++i) { if( !(*i).isValid() || (*i).type != Route::TRACK_ROUTE ) continue; Track* t = (*i).track; rv = t->isCircularRoute(NULL); if(rv) break; } _nodeTraversed = false; return rv; } RouteCapabilitiesStruct Track::routeCapabilities() const { RouteCapabilitiesStruct s; s._trackChannels._inChannels = s._trackChannels._outChannels = _channels; s._trackChannels._inRoutable = s._trackChannels._outRoutable = (_channels != 0); return s; } inline bool Track::canDominateOutputLatency() const { // Return true only if the track is on and the user wants unterminated branches counted. return !off() && MusEGlobal::config.correctUnterminatedOutBranchLatency; } inline bool Track::canDominateInputLatency() const { return false; } bool Track::canPassThruLatency() const { return !off() && (!canRecordMonitor() || (MusEGlobal::config.monitoringAffectsLatency && isRecMonitored())); // || recordFlag(); } void Track::prepareLatencyScan() { _latencyInfo.initialize(); _transportSource.prepareLatencyScan(transportAffectsAudioLatency()); } float Track::getWorstPluginLatencyAudio() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._worstPluginLatencyProcessed) return _latencyInfo._worstPluginLatency; _latencyInfo._worstPluginLatency = 0.0f; _latencyInfo._worstPluginLatencyProcessed = true; return _latencyInfo._worstPluginLatency; } float Track::getWorstPortLatencyAudio() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._worstPortLatencyProcessed) return _latencyInfo._worstPortLatency; _latencyInfo._worstPortLatency = 0.0f; _latencyInfo._worstPortLatencyProcessed = true; return _latencyInfo._worstPortLatency; } float Track::getWorstSelfLatencyAudio() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._worstSelfLatencyProcessed) return _latencyInfo._worstSelfLatency; _latencyInfo._worstSelfLatency = 0.0f; _latencyInfo._worstSelfLatencyProcessed = true; return _latencyInfo._worstSelfLatency; } //--------------------------------------------------------- // MidiTrack //--------------------------------------------------------- MidiTrack::MidiTrack() : Track(MIDI) { init(); _drummap=new DrumMap[128]; _workingDrumMapPatchList = new WorkingDrumMapPatchList(); init_drummap(true /* write drummap ordering information as well */); } MidiTrack::MidiTrack(const MidiTrack& mt, int flags) : Track(mt, flags) { init(); _drummap=new DrumMap[128]; _workingDrumMapPatchList = new WorkingDrumMapPatchList(); internal_assign(mt, flags | Track::ASSIGN_PROPERTIES); } void MidiTrack::internal_assign(const Track& t, int flags) { if(!t.isMidiTrack()) return; const MidiTrack& mt = (const MidiTrack&)t; if(flags & ASSIGN_PROPERTIES) { _outPort = mt.outPort(); _outChannel = mt.outChannel(); transposition = mt.transposition; velocity = mt.velocity; delay = mt.delay; len = mt.len; compression = mt.compression; clefType = mt.clefType; _curDrumPatchNumber = mt._curDrumPatchNumber; } if(flags & ASSIGN_ROUTES) { for(ciRoute ir = mt._inRoutes.begin(); ir != mt._inRoutes.end(); ++ir) // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. _inRoutes.push_back(*ir); for(ciRoute ir = mt._outRoutes.begin(); ir != mt._outRoutes.end(); ++ir) // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. _outRoutes.push_back(*ir); } else if(flags & ASSIGN_DEFAULT_ROUTES) { // Add default track <-> midiport routes. int c; bool defOutFound = false; /// TODO: Remove this if and when multiple output routes supported. const int chmask = (1 << MusECore::MUSE_MIDI_CHANNELS) - 1; for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { MidiPort* mp = &MusEGlobal::midiPorts[i]; if(mp->device()) // Only if device is valid. { c = mp->defaultInChannels(); if(c) { // Don't call msgAddRoute. Caller later calls msgAddTrack which 'mirrors' this routing node. // All channels set or Omni? Use an Omni route: if(c == -1 || c == chmask) _inRoutes.push_back(Route(i)); else // Add individual channels: for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if(c & (1 << ch)) _inRoutes.push_back(Route(i, ch)); } } } if(!defOutFound) { c = mp->defaultOutChannels(); if(c) { #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ if(c == -1) c = 1; // Just to be safe, shouldn't happen, default to channel 0. for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if(c & (1 << ch)) { defOutFound = true; _outPort = i; // Leave drum tracks at channel 10. TODO: Want this? //if(type() != Track::NEW_DRUM) _outChannel = ch; break; } } #else // All channels set or Omni? Use an Omni route: if(c == -1 || c == chmask) _outRoutes.push_back(Route(i)); else // Add individual channels: for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { if(c & (1 << ch)) _outRoutes.push_back(Route(i, ch)); } #endif } } } } if (flags & ASSIGN_DRUMLIST) { // Safety in case assignment called on an existing track which is already in global_drum_ordering. remove_ourselves_from_drum_ordering(); for (MusEGlobal::global_drum_ordering_t::iterator it=MusEGlobal::global_drum_ordering.begin(); it!=MusEGlobal::global_drum_ordering.end(); ++it) { if (it->first == &mt) { // duplicates the entry at it, set it to the first entry of both it=MusEGlobal::global_drum_ordering.insert(it, *it); // make it point to the second entry ++it; it->first=this; } } for (int i=0;i<128;i++) // no memcpy allowed here. dunno exactly why, _drummap[i]=mt._drummap[i]; // seems QString-related. update_drum_in_map(); _drummap_ordering_tied_to_patch=mt._drummap_ordering_tied_to_patch; // TODO FINDMICH "assign" ordering as well if(mt._workingDrumMapPatchList) *_workingDrumMapPatchList = *mt._workingDrumMapPatchList; } else { init_drummap(true /* write drummap ordering information as well */); } const bool dup = flags & ASSIGN_DUPLICATE_PARTS; const bool cpy = flags & ASSIGN_COPY_PARTS; const bool cln = flags & ASSIGN_CLONE_PARTS; if(dup || cpy || cln) { const PartList* pl = t.cparts(); for (ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* spart = ip->second; Part* dpart = 0; if(dup) dpart = spart->hasClones() ? spart->createNewClone() : spart->duplicate(); else if(cpy) dpart = spart->duplicate(); else if(cln) dpart = spart->createNewClone(); if(dpart) { dpart->setTrack(this); parts()->add(dpart); } } } } void MidiTrack::assign(const Track& t, int flags) { Track::assign(t, flags); internal_assign(t, flags); } MidiTrack::~MidiTrack() { if(_workingDrumMapPatchList) delete _workingDrumMapPatchList; delete [] _drummap; remove_ourselves_from_drum_ordering(); } bool MidiTrack::setRecordFlag2AndCheckMonitor(bool f) { if(canRecord()) _recordFlag = f; if(MusEGlobal::config.monitorOnRecord && canRecordMonitor()) { if(f != _recMonitor) { _recMonitor = f; return true; } } return false; } void MidiTrack::convertToType(TrackType trackType) { if(trackType == MusECore::Track::MIDI || trackType == MusECore::Track::DRUM) { // // Drum -> Midi // MusECore::PartList* pl = parts(); for (MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) { for (MusECore::ciEvent ie = ip->second->events().begin(); ie != ip->second->events().end(); ++ie) { MusECore::Event ev = ie->second; if(ev.type() == MusECore::Note) { int pitch = ev.pitch(); pitch = MusEGlobal::drumMap[pitch].enote; ev.setPitch(pitch); } else if(ev.type() == MusECore::Controller) { int ctl = ev.dataA(); // Is it a drum controller event, according to the track port's instrument? MusECore::MidiController *mc = MusEGlobal::midiPorts[outPort()].drumController(ctl); if(mc) // Change the controller event's index into the drum map to an instrument note. ev.setA((ctl & ~0xff) | MusEGlobal::drumMap[ctl & 0x7f].enote); } } } setType(trackType); } } void MidiTrack::remove_ourselves_from_drum_ordering() { for (MusEGlobal::global_drum_ordering_t::iterator it=MusEGlobal::global_drum_ordering.begin(); it!=MusEGlobal::global_drum_ordering.end();) if (it->first == this) it=MusEGlobal::global_drum_ordering.erase(it); else it++; } //--------------------------------------------------------- // init //--------------------------------------------------------- void MidiTrack::init() { _outPort = 0; // let's set the port to the last instantiated device // if midi-channel defaults are set in the configuration it // will override this setting for (int i = MusECore::MIDI_PORTS - 1; i > -1; i--) { if (MusEGlobal::midiPorts[i].device() != NULL) { _outPort = i; break; } } _outChannel = (type()==DRUM) ? 9 : 0; clefType=trebleClef; _curDrumPatchNumber = CTRL_VAL_UNKNOWN; transposition = 0; velocity = 0; delay = 0; len = 100; // percent compression = 100; // percent } void MidiTrack::init_drum_ordering() { // first display entries with non-empty names, then with empty names. remove_ourselves_from_drum_ordering(); for (int i=0;i<128;i++) if (_drummap[i].name!="" && _drummap[i].name!="?") // non-empty name? MusEGlobal::global_drum_ordering.push_back(std::pair(this,i)); for (int i=0;i<128;i++) if (!(_drummap[i].name!="" && _drummap[i].name!="?")) // empty name? MusEGlobal::global_drum_ordering.push_back(std::pair(this,i)); } void MidiTrack::init_drummap(bool write_ordering) { for (int i=0;i<128;i++) _drummap[i]=iNewDrumMap[i]; if (write_ordering) init_drum_ordering(); update_drum_in_map(); _drummap_ordering_tied_to_patch=true; } void MidiTrack::update_drum_in_map() { for (int i = 0; i < 128; ++i) drum_in_map[(int)_drummap[i].enote] = i; } //--------------------------------------------------------- // height //--------------------------------------------------------- int MidiTrack::height() const { if (_isVisible) return _height; return 0; } //--------------------------------------------------------- // routeCapabilities //--------------------------------------------------------- RouteCapabilitiesStruct MidiTrack::routeCapabilities() const { RouteCapabilitiesStruct s; s._midiPortChannels._inRoutable = true; s._midiPortChannels._inChannels = MusECore::MUSE_MIDI_CHANNELS; s._trackChannels._outRoutable = true; // Support Midi Track to Audio Input Track routes (for soloing chain). #ifndef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ s._midiPortChannels._outChannels = MusECore::MUSE_MIDI_CHANNELS; #endif return s; } //--------------------------------------------------------- // canDominateOutputLatency //--------------------------------------------------------- inline bool MidiTrack::canDominateOutputLatency() const { // The midi track's own midi file contributions can never dominate latency. return false; } inline bool MidiTrack::canCorrectOutputLatency() const { return true; } bool MidiTrack::isLatencyInputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyInputTerminalProcessed) return _latencyInfo._isLatencyInputTerminal; // If we're asking for the view from the record side, check if we're // passing the signal through the track via monitoring. if(!canPassThruLatency()) { _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const int port = outPort(); if(port >= 0 && port < MusECore::MIDI_PORTS) { MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiDevice* md = mp->device(); if(md && (md->openFlags() & 1 /*write*/)) { // If it's a non-synth device, or a synth that is not off. if(!md->isSynti() || !static_cast(md)->off()) { _latencyInfo._isLatencyInputTerminal = false; _latencyInfo._isLatencyInputTerminalProcessed = true; return false; } } } #else const RouteList* rl = outRoutes(); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::MIDI_PORT_ROUTE: { const int port = ir->midiPort; const int ch = ir->channel; if(port < 0 || port >= MusECore::MIDI_PORTS || ch < -1 || ch >= MusECore::MUSE_MIDI_CHANNELS) continue; MidiPort* mp = &MusEGlobal::midiPorts[ir->midiPort]; MidiDevice* md = mp->device(); if(!md) continue; if(md->openFlags() & 1 /*write*/) { _latencyInfo._isLatencyInputTerminal = false; _latencyInfo._isLatencyInputTerminalProcessed = true; return false; } } break; case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO } break; default: break; } } #endif _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } bool MidiTrack::isLatencyOutputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyOutputTerminalProcessed) return _latencyInfo._isLatencyOutputTerminal; #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ const int port = outPort(); //if((openFlags() & (capture ? 2 : 1)) && port >= 0 && port < MusECore::MIDI_PORTS) //if((openFlags() & 1 /*write*/) && port >= 0 && port < MusECore::MIDI_PORTS) if(port >= 0 && port < MusECore::MIDI_PORTS) { MidiPort* mp = &MusEGlobal::midiPorts[port]; MidiDevice* md = mp->device(); if(md && (md->openFlags() & 1 /*write*/)) { // If it's a non-synth device, or a synth that is not off. if(!md->isSynti() || !static_cast(md)->off()) { _latencyInfo._isLatencyOutputTerminal = false; _latencyInfo._isLatencyOutputTerminalProcessed = true; return false; } } } #else const RouteList* rl = outRoutes(); for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::MIDI_PORT_ROUTE: { const int port = ir->midiPort; const int ch = ir->channel; if(port < 0 || port >= MusECore::MIDI_PORTS || ch < -1 || ch >= MusECore::MUSE_MIDI_CHANNELS) continue; MidiPort* mp = &MusEGlobal::midiPorts[ir->midiPort]; MidiDevice* md = mp->device(); if(!md) continue; if(md->openFlags() & 1 /*write*/) { _latencyInfo._isLatencyOutputTerminal = false; _latencyInfo._isLatencyOutputTerminalProcessed = true; return false; } } break; case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO } break; default: break; } } #endif _latencyInfo._isLatencyOutputTerminal = true; _latencyInfo._isLatencyOutputTerminalProcessed = true; return true; } //--------------------------------------------------------- // getDominanceInfo //--------------------------------------------------------- TrackLatencyInfo& MidiTrack::getDominanceInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._canDominateInputProcessed) || (!input && _latencyInfo._canDominateProcessed)) return _latencyInfo; // Get the default domination for this track type. bool can_dominate_lat = input ? canDominateInputLatency() : canDominateOutputLatency(); bool can_correct_lat = canCorrectOutputLatency(); const bool passthru = canPassThruLatency(); // Gather latency info from all connected input branches, // but ONLY if the track is not off. bool item_found = false; if(!off() && (passthru || input)) { RouteList* rl = inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::MIDI_PORT_ROUTE: { const int port = ir->midiPort; const int ch = ir->channel; if(port < 0 || port >= MusECore::MIDI_PORTS || ch < -1 || ch >= MusECore::MUSE_MIDI_CHANNELS) continue; MidiPort* mp = &MusEGlobal::midiPorts[ir->midiPort]; MidiDevice* md = mp->device(); if(!md) continue; //if(!off() && (md->openFlags() & 2 /*read*/) && (passthru || input)) if(md->openFlags() & 2 /*read*/) { const TrackLatencyInfo& li = md->getDominanceInfoMidi(true /*capture*/, false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) can_dominate_lat = true; if(li._canCorrectOutputLatency) can_correct_lat = true; } else { item_found = true; // Override the defaults with this first item's values. //route_worst_out_corr = li._outputAvailableCorrection; can_dominate_lat = li._canDominateOutputLatency; can_correct_lat = li._canCorrectOutputLatency; } } } } break; case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO } break; default: break; } } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off()) { if(input) { _latencyInfo._canDominateInputLatency = can_dominate_lat; } else { _latencyInfo._canDominateOutputLatency = can_dominate_lat; // If any of the branches can dominate, then this node cannot correct. _latencyInfo._canCorrectOutputLatency = can_correct_lat && !can_dominate_lat; } } if(input) _latencyInfo._canDominateInputProcessed = true; else _latencyInfo._canDominateProcessed = true; return _latencyInfo; } //--------------------------------------------------------- // getDominanceLatencyInfo //--------------------------------------------------------- TrackLatencyInfo& MidiTrack::getDominanceLatencyInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._dominanceInputProcessed) || (!input && _latencyInfo._dominanceProcessed)) return _latencyInfo; float route_worst_latency = 0.0f; const bool passthru = canPassThruLatency(); bool item_found = false; float worst_self_latency = 0.0f; if(!input && !off()) worst_self_latency = getWorstSelfLatencyAudio(); if(!off() && (passthru || input)) { RouteList* rl = inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::MIDI_PORT_ROUTE: { const int port = ir->midiPort; const int ch = ir->channel; if(port < 0 || port >= MusECore::MIDI_PORTS || ch < -1 || ch >= MusECore::MUSE_MIDI_CHANNELS) continue; MidiPort* mp = &MusEGlobal::midiPorts[ir->midiPort]; MidiDevice* md = mp->device(); if(!md) continue; //if(!off() && (md->openFlags() & 2 /*read*/) && (passthru || input)) if(md->openFlags() & 2 /*read*/) { const TrackLatencyInfo& li = md->getDominanceLatencyInfoMidi(true /*capture*/, false); // Whether the branch can dominate or correct latency or if we // want to allow unterminated input branches to // participate in worst branch latency calculations. const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Is it the first found item? if(item_found) { // If any one of the branches can dominate the latency, // that overrides any which cannot. if(li._canDominateOutputLatency) { // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. //if(li._outputLatency > route_worst_latency) // route_worst_latency = li._outputLatency; } // Override the current worst value if the latency is greater, // but ONLY if the branch can dominate. if(li._outputLatency > route_worst_latency) route_worst_latency = li._outputLatency; } else { item_found = true; // Override the default worst value, but ONLY if the branch can dominate. //if(li._canDominateOutputLatency) route_worst_latency = li._outputLatency; } } } } break; case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO } break; default: break; } } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off()) { if(input) { _latencyInfo._inputLatency = route_worst_latency; } else { if(passthru) { _latencyInfo._outputLatency = worst_self_latency + route_worst_latency; _latencyInfo._inputLatency = route_worst_latency; } else { _latencyInfo._outputLatency = worst_self_latency + _latencyInfo._sourceCorrectionValue; } } } if(input) _latencyInfo._dominanceInputProcessed = true; else _latencyInfo._dominanceProcessed = true; return _latencyInfo; } //--------------------------------------------------------- // setCorrectionLatencyInfo //--------------------------------------------------------- TrackLatencyInfo& MidiTrack::setCorrectionLatencyInfo(bool input, float finalWorstLatency, float callerBranchLatency) { const bool passthru = canPassThruLatency(); float worst_self_latency = 0.0f; if(!input && !off()) worst_self_latency = getWorstSelfLatencyAudio(); // The _trackLatency should already be calculated in the dominance scan. const float branch_lat = callerBranchLatency + worst_self_latency; if(!off() && (passthru || input)) { RouteList* rl = inRoutes(); for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::MIDI_PORT_ROUTE: { if(ir->midiPort < 0 || ir->midiPort >= MusECore::MIDI_PORTS || ir->channel < -1 || ir->channel >= MusECore::MUSE_MIDI_CHANNELS) continue; const MidiPort* mp = &MusEGlobal::midiPorts[ir->midiPort]; MidiDevice* md = mp->device(); //if(!off() && md && (md->openFlags() & 2 /*read*/) && (passthru || input)) if(md && (md->openFlags() & 2 /*read*/)) md->setCorrectionLatencyInfoMidi(true /*capture*/, false, finalWorstLatency, branch_lat); } break; default: break; } } } // Set the correction of all connected input branches, // but ONLY if the track is not off. if(!off()) { if(input) { } else { if(canCorrectOutputLatency() && _latencyInfo._canCorrectOutputLatency) { float corr = 0.0f; if(MusEGlobal::config.commonProjectLatency) corr -= finalWorstLatency; corr -= branch_lat; // The _sourceCorrectionValue is initialized to zero. // Whichever calling branch needs the most correction gets it. if(corr < _latencyInfo._sourceCorrectionValue) _latencyInfo._sourceCorrectionValue = corr; } } //fprintf(stderr, "MidiTrack::setCorrectionLatencyInfo() name:%s finalWorstLatency:%f branch_lat:%f corr:%f _sourceCorrectionValue:%f\n", // name().toLatin1().constData(), finalWorstLatency, branch_lat, corr, _latencyInfo._sourceCorrectionValue); } return _latencyInfo; } //--------------------------------------------------------- // getLatencyInfo //--------------------------------------------------------- TrackLatencyInfo& MidiTrack::getLatencyInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._inputProcessed) || (!input && _latencyInfo._processed)) return _latencyInfo; float route_worst_latency = _latencyInfo._inputLatency; const bool passthru = canPassThruLatency(); RouteList* rl = inRoutes(); // Now that we know the worst-case latency of the connected branches, // adjust each of the conveniently stored temporary latency values // in the routes according to whether they can dominate... for (iRoute ir = rl->begin(); ir != rl->end(); ++ir) { switch(ir->type) { case Route::MIDI_PORT_ROUTE: { const int port = ir->midiPort; const int ch = ir->channel; if(port < 0 || port >= MusECore::MIDI_PORTS || ch < -1 || ch >= MusECore::MUSE_MIDI_CHANNELS) continue; MidiPort* mp = &MusEGlobal::midiPorts[ir->midiPort]; MidiDevice* md = mp->device(); if(!md) continue; if(passthru || input) { // Default to zero. ir->audioLatencyOut = 0.0f; if(!off() && (md->openFlags() & 2 /*read*/)) { const TrackLatencyInfo& li = md->getLatencyInfoMidi(true /*capture*/, false); const bool participate = (li._canCorrectOutputLatency || li._canDominateOutputLatency || MusEGlobal::config.correctUnterminatedInBranchLatency); if(participate) { // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. ir->audioLatencyOut = route_worst_latency - li._outputLatency; // Should not happen, but just in case. if((long int)ir->audioLatencyOut < 0) ir->audioLatencyOut = 0.0f; } } } } break; case Route::TRACK_ROUTE: if(!ir->track) continue; if(ir->track->isMidiTrack()) { // TODO } break; default: break; } } if(input) _latencyInfo._inputProcessed = true; else _latencyInfo._processed = true; return _latencyInfo; } void MidiTrack::setLatencyCompWriteOffset(float worstCase) { // If independent branches are NOT to affect project latency, // then there should be no need for any extra delay in the branch. if(!MusEGlobal::config.commonProjectLatency) { _latencyInfo._compensatorWriteOffset = 0; //fprintf(stderr, "MidiTrack::setLatencyCompWriteOffset() name:%s worstCase:%f _outputLatency:%f _compensatorWriteOffset:%lu\n", // name().toLatin1().constData(), worstCase, _latencyInfo._outputLatency, _latencyInfo._compensatorWriteOffset); return; } if(_latencyInfo._canDominateOutputLatency) { const long unsigned int wc = worstCase; const long unsigned int ol = _latencyInfo._outputLatency; if(ol > wc) _latencyInfo._compensatorWriteOffset = 0; else _latencyInfo._compensatorWriteOffset = wc - ol; } else { // if(_latencyInfo._outputLatency < 0) _latencyInfo._compensatorWriteOffset = 0; // else // _latencyInfo._compensatorWriteOffset = _latencyInfo._outputLatency; } //fprintf(stderr, "MidiTrack::setLatencyCompWriteOffset() name:%s worstCase:%f _outputLatency:%f _canDominateOutputLatency:%d _compensatorWriteOffset:%lu\n", // name().toLatin1().constData(), worstCase, _latencyInfo._outputLatency, _latencyInfo._canDominateOutputLatency, _latencyInfo._compensatorWriteOffset); } //--------------------------------------------------------- // noOutRoute //--------------------------------------------------------- bool MidiTrack::noOutRoute() const { return _outRoutes.empty() #ifdef _USE_MIDI_TRACK_SINGLE_OUT_PORT_CHAN_ && (outChannel() < 0 || outPort() < 0 || !MusEGlobal::midiPorts[outPort()].device()) #endif ; } //--------------------------------------------------------- // mappedPortChanCtrl //--------------------------------------------------------- bool MidiTrack::mappedPortChanCtrl(int *ctrl, int *port, MidiPort** mport, int *channel) const { bool res = false; int t_ctrl = *ctrl; int t_port = outPort(); int t_ch = outChannel(); MidiPort* mp = &MusEGlobal::midiPorts[t_port]; const MidiController* mc = mp->drumController(t_ctrl); if(mc) { res = true; const int note = t_ctrl & 0x7f; // Is it a drum controller old_event, according to the track port's instrument? if(type() == Track::DRUM) { // Default to track port if -1 and track channel if -1. const int ch = drummap()[note].channel; if(ch != -1) t_ch = ch; const int p = drummap()[note].port; if(p != -1) t_port = p; t_ctrl &= ~0xff; t_ctrl |= drummap()[note].anote; } } *ctrl = t_ctrl; if(port) (*port) = t_port; if(mport) (*mport) = &MusEGlobal::midiPorts[t_port]; if(channel) (*channel) = t_ch; return res; } //--------------------------------------------------------- // setOutChannel //--------------------------------------------------------- MidiTrack::ChangedType_t MidiTrack::setOutChannel(int i, bool doSignal) { if(_outChannel == i) return NothingChanged; _outChannel = i; ChangedType_t res = ChannelChanged; if(updateDrummap(doSignal)) res |= DrumMapChanged; return res; } //--------------------------------------------------------- // setOutPort //--------------------------------------------------------- MidiTrack::ChangedType_t MidiTrack::setOutPort(int i, bool doSignal) { if(_outPort == i) return NothingChanged; _outPort = i; ChangedType_t res = PortChanged; if(updateDrummap(doSignal)) res |= DrumMapChanged; return res; } //--------------------------------------------------------- // setOutChanAndUpdate //--------------------------------------------------------- MidiTrack::ChangedType_t MidiTrack::setOutChanAndUpdate(int i, bool doSignal) { if(_outChannel == i) return NothingChanged; removePortCtrlEvents(this); _outChannel = i; ChangedType_t res = ChannelChanged; if(updateDrummap(doSignal)) res |= DrumMapChanged; addPortCtrlEvents(this); return res; } //--------------------------------------------------------- // setOutPortAndUpdate //--------------------------------------------------------- MidiTrack::ChangedType_t MidiTrack::setOutPortAndUpdate(int i, bool doSignal) { if(_outPort == i) return NothingChanged; removePortCtrlEvents(this); _outPort = i; ChangedType_t res = PortChanged; if(updateDrummap(doSignal)) res |= DrumMapChanged; addPortCtrlEvents(this); return res; } //--------------------------------------------------------- // setOutPortAndChannelAndUpdate //--------------------------------------------------------- MidiTrack::ChangedType_t MidiTrack::setOutPortAndChannelAndUpdate(int port, int ch, bool doSignal) { if(_outPort == port && _outChannel == ch) return NothingChanged; removePortCtrlEvents(this); _outPort = port; _outChannel = ch; ChangedType_t res = PortChanged | ChannelChanged; if(updateDrummap(doSignal)) res |= DrumMapChanged; addPortCtrlEvents(this); return res; } //--------------------------------------------------------- // setInPortAndChannelMask // For old song files with port mask (max 32 ports) and channel mask (16 channels), // before midi routing was added (the iR button). //--------------------------------------------------------- void MidiTrack::setInPortAndChannelMask(unsigned int portmask, int chanmask) { //bool changed = false; PendingOperationList operations; for(int port = 0; port < 32; ++port) // 32 is the old maximum number of ports. { // If the port was not used in the song file to begin with, just ignore it. // This saves from having all of the first 32 ports' channels connected. if(!MusEGlobal::midiPorts[port].foundInSongFile()) continue; const int allch = (1 << MusECore::MUSE_MIDI_CHANNELS) - 1; // Check if Omni route will do... if(chanmask == allch) { // Route wanted? if(portmask & (1 << port)) operations.add(MusECore::PendingOperationItem(MusECore::Route(port), MusECore::Route(this), MusECore::PendingOperationItem::AddRoute)); else operations.add(MusECore::PendingOperationItem(MusECore::Route(port), MusECore::Route(this), MusECore::PendingOperationItem::DeleteRoute)); } else // Add individual channels: for(int ch = 0; ch < MusECore::MUSE_MIDI_CHANNELS; ++ch) { // Route wanted? if(portmask & (1 << port) && (chanmask & (1 << ch))) operations.add(MusECore::PendingOperationItem(MusECore::Route(port, ch), MusECore::Route(this, ch), MusECore::PendingOperationItem::AddRoute)); else operations.add(MusECore::PendingOperationItem(MusECore::Route(port, ch), MusECore::Route(this, ch), MusECore::PendingOperationItem::DeleteRoute)); } } // if(changed) // { // MusEGlobal::audio->msgUpdateSoloStates(); // MusEGlobal::song->update(SC_ROUTE); // } if(!operations.empty()) { MusEGlobal::audio->msgExecutePendingOperations(operations, true); // MusEGlobal::song->update(SC_ROUTE); } } //--------------------------------------------------------- // newPart //--------------------------------------------------------- Part* MidiTrack::newPart(Part*p, bool clone) { MidiPart* part; if(!p) { part = new MidiPart(this); } else { if (clone) { part = (MidiPart*)p->createNewClone(); part->setTrack(this); } else { part = (MidiPart*)p->duplicate(); part->setTrack(this); } } return part; } //--------------------------------------------------------- // addStuckNote //--------------------------------------------------------- bool MidiTrack::addStuckNote(const MidiPlayEvent& ev) { stuckNotes.add(ev); return true; } //--------------------------------------------------------- // addStuckLiveNote // Return true if note was added. //--------------------------------------------------------- bool MidiTrack::addStuckLiveNote(int port, int chan, int note, int vel) { // for(ciMPEvent k = stuckLiveNotes.begin(); k != stuckLiveNotes.end(); ++k) // { // // We're looking for port, channel, and note. Time and velocity are not relevant. // if((*k).port() == port && // (*k).channel() == chan && // (*k).dataA() == note) // return false; // } stuckLiveNotes.add(MidiPlayEvent(0, port, chan, ME_NOTEOFF, note, vel)); // Mark for immediate playback return true; } //--------------------------------------------------------- // removeStuckLiveNote // Return true if note was removed. //--------------------------------------------------------- bool MidiTrack::removeStuckLiveNote(int port, int chan, int note) { for(ciMPEvent k = stuckLiveNotes.begin(); k != stuckLiveNotes.end(); ++k) { // We're looking for port, channel, and note. Time and velocity are not relevant. if((*k).port() == port && (*k).channel() == chan && (*k).dataA() == note) { stuckLiveNotes.erase(k); return true; } } return false; } //--------------------------------------------------------- // stuckLiveNoteExists // Return true if note exists. //--------------------------------------------------------- bool MidiTrack::stuckLiveNoteExists(int port, int chan, int note) { for(ciMPEvent k = stuckLiveNotes.begin(); k != stuckLiveNotes.end(); ++k) { // We're looking for port, channel, and note. Time and velocity are not relevant. if((*k).port() == port && (*k).channel() == chan && (*k).dataA() == note) return true; } return false; } //--------------------------------------------------------- // automationType //--------------------------------------------------------- AutomationType MidiTrack::automationType() const { MidiPort* port = &MusEGlobal::midiPorts[outPort()]; return port->automationType(outChannel()); } //--------------------------------------------------------- // setAutomationType //--------------------------------------------------------- void MidiTrack::setAutomationType(AutomationType t) { MidiPort* port = &MusEGlobal::midiPorts[outPort()]; port->setAutomationType(outChannel(), t); } //--------------------------------------------------------- // MidiTrack::write //--------------------------------------------------------- void MidiTrack::write(int level, Xml& xml) const { const char* tag; if (type() == MIDI) tag = "miditrack"; else if (type() == DRUM) tag = "newdrumtrack"; else { printf("THIS SHOULD NEVER HAPPEN: non-midi-type in MidiTrack::write()\n"); tag=""; } xml.tag(level++, tag); Track::writeProperties(level, xml); xml.intTag(level, "device", outPort()); xml.intTag(level, "channel", outChannel()); xml.intTag(level, "locked", _locked); xml.intTag(level, "transposition", transposition); xml.intTag(level, "velocity", velocity); xml.intTag(level, "delay", delay); xml.intTag(level, "len", len); xml.intTag(level, "compression", compression); xml.intTag(level, "automation", int(automationType())); xml.intTag(level, "clef", int(clefType)); const PartList* pl = cparts(); for (ciPart p = pl->begin(); p != pl->end(); ++p) p->second->write(level, xml); writeOurDrumSettings(level, xml); xml.etag(level, tag); } void MidiTrack::writeOurDrumSettings(int level, Xml& xml) const { xml.tag(level++, "our_drum_settings"); _workingDrumMapPatchList->write(level, xml); xml.intTag(level, "ordering_tied", _drummap_ordering_tied_to_patch); xml.etag(level, "our_drum_settings"); } void MidiTrack::dumpMap() { if(type() != DRUM) return; const int port = outPort(); if(port < 0 || port >= MusECore::MIDI_PORTS) return; MidiPort* mp = &MusEGlobal::midiPorts[port]; const int chan = outChannel(); const int patch = mp->hwCtrlState(chan, MusECore::CTRL_PROGRAM); fprintf(stderr, "Drum map for patch:%d\n\n", patch); fprintf(stderr, "name\t\tvol\tqnt\tlen\tchn\tprt\tlv1\tlv2\tlv3\tlv4\tenote\t\tanote\\ttmute\thide\n"); DrumMap all_dm, #ifdef _USE_INSTRUMENT_OVERRIDES_ instr_dm, instrdef_dm, #endif track_dm, trackdef_dm; for(int index = 0; index < 128; ++index) { getMapItem(patch, index, all_dm, WorkingDrumMapEntry::AllOverrides); getMapItem(patch, index, track_dm, WorkingDrumMapEntry::TrackOverride); getMapItem(patch, index, trackdef_dm, WorkingDrumMapEntry::TrackDefaultOverride); #ifdef _USE_INSTRUMENT_OVERRIDES_ getMapItem(patch, index, instr_dm, WorkingDrumMapEntry::InstrumentOverride); getMapItem(patch, index, instrdef_dm, WorkingDrumMapEntry::InstrumentDefaultOverride); #endif fprintf(stderr, "Index:%d ", index); fprintf(stderr, "All overrides:\n"); all_dm.dump(); #ifdef _USE_INSTRUMENT_OVERRIDES_ fprintf(stderr, "Instrument override:\n"); instr_dm.dump(); fprintf(stderr, "Instrument default override:\n"); instrdef_dm.dump(); #endif fprintf(stderr, "Track override:\n"); track_dm.dump(); fprintf(stderr, "Track default override:\n"); trackdef_dm.dump(); fprintf(stderr, "\n"); } } //--------------------------------------------------------- // MidiTrack::read //--------------------------------------------------------- void MidiTrack::read(Xml& xml) { unsigned int portmask = 0; int chanmask = 0; bool portmask_found = false; bool chanmask_found = false; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto out_of_MidiTrackRead_forloop; case Xml::TagStart: if (tag == "transposition") transposition = xml.parseInt(); else if (tag == "velocity") velocity = xml.parseInt(); else if (tag == "delay") delay = xml.parseInt(); else if (tag == "len") len = xml.parseInt(); else if (tag == "compression") compression = xml.parseInt(); else if (tag == "part") { Part* p = Part::readFromXml(xml, this); if(p) parts()->add(p); } // TODO: These two device and channel sections will need to change // if and when multiple output routes are supported. // For now just look for the first default (there's only one)... else if (tag == "device") { int port = xml.parseInt(); if(port == -1) { for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { if(MusEGlobal::midiPorts[i].defaultOutChannels()) { port = i; break; } } } if(port == -1) port = 0; setOutPort(port); } else if (tag == "channel") { int chan = xml.parseInt(); if(chan == -1) { for(int i = 0; i < MusECore::MIDI_PORTS; ++i) { int defchans = MusEGlobal::midiPorts[i].defaultOutChannels(); for(int c = 0; c < MusECore::MUSE_MIDI_CHANNELS; ++c) { if(defchans & (1 << c)) { chan = c; break; } } if(chan != -1) break; } } if(chan == -1) chan = 0; setOutChannel(chan); } else if (tag == "inportMap") { portmask = xml.parseUInt(); // Obsolete but support old files. portmask_found = true; } else if (tag == "inchannelMap") { chanmask = xml.parseInt(); // Obsolete but support old files. chanmask_found = true; } else if (tag == "locked") _locked = xml.parseInt(); else if (tag == "echo") // Obsolete but support old files. setRecMonitor(xml.parseInt()); else if (tag == "automation") setAutomationType(AutomationType(xml.parseInt())); else if (tag == "clef") clefType = (clefTypes)xml.parseInt(); else if (tag == "our_drum_settings") readOurDrumSettings(xml); else if (Track::readProperties(xml, tag)) { // version 1.0 compatibility: if (tag == "track" && xml.majorVersion() == 1 && xml.minorVersion() == 0) break; xml.unknown("MidiTrack"); } break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "miditrack" || tag == "drumtrack" || tag == "newdrumtrack") { if(portmask_found && chanmask_found) setInPortAndChannelMask(portmask, chanmask); // Support old files. goto out_of_MidiTrackRead_forloop; } default: break; } } out_of_MidiTrackRead_forloop: chainTrackParts(this); } void MidiTrack::readOurDrumSettings(Xml& xml) { bool doUpdateDrummap = false; for (;;) { Xml::Token token = xml.parse(); if (token == Xml::Error || token == Xml::End) break; const QString& tag = xml.s1(); switch (token) { case Xml::TagStart: if (tag == "tied") xml.parseInt(); // Obsolete. else if (tag == "ordering_tied") _drummap_ordering_tied_to_patch = xml.parseInt(); else if (tag == "our_drummap" || // OBSOLETE. Support old files. tag == "drummap" || // OBSOLETE. Support old files. tag == "drumMapPatch") { // false = Do not fill in unused items. _workingDrumMapPatchList->read(xml, false); doUpdateDrummap = true; } else xml.unknown("our_drum_settings"); break; case Xml::TagEnd: if (tag == "our_drum_settings") { if(doUpdateDrummap) { // We must ensure that there are NO duplicate enote fields, // since the instrument map may have changed by now. //normalizeWorkingDrumMapPatchList(); updateDrummap(false); } return; } default: break; } } } //--------------------------------------------------------- // addPart //--------------------------------------------------------- iPart Track::addPart(Part* p) { p->setTrack(this); return _parts.add(p); } //--------------------------------------------------------- // findPart //--------------------------------------------------------- Part* Track::findPart(unsigned tick) { for (iPart i = _parts.begin(); i != _parts.end(); ++i) { Part* part = i->second; if (tick >= part->tick() && tick < (part->tick()+part->lenTick())) return part; } return 0; } bool Track::selectEvents(bool select, unsigned long t0, unsigned long t1) { bool ret = false; PartList* pl = parts(); for(iPart ip = pl->begin(); ip != pl->end(); ++ip) { if(ip->second->selectEvents(select, t0, t1)) ret = true; } return ret; } //--------------------------------------------------------- // Track::writeProperties //--------------------------------------------------------- void Track::writeProperties(int level, Xml& xml) const { xml.strTag(level, "name", _name); if (!_comment.isEmpty()) xml.strTag(level, "comment", _comment); xml.intTag(level, "record", _recordFlag); xml.intTag(level, "mute", mute()); xml.intTag(level, "solo", solo()); xml.intTag(level, "off", off()); xml.intTag(level, "channels", _channels); xml.intTag(level, "height", _height); xml.intTag(level, "locked", _locked); xml.intTag(level, "recMonitor", _recMonitor); if (_selected) { xml.intTag(level, "selected", _selected); xml.intTag(level, "selectionOrder", _selectionOrder); } if (m_color.isValid()) xml.strTag(level, "color", m_color.name()); } //--------------------------------------------------------- // Track::readProperties //--------------------------------------------------------- bool Track::readProperties(Xml& xml, const QString& tag) { if (tag == "name") _name = xml.parse1(); else if (tag == "comment") _comment = xml.parse1(); else if (tag == "record") { bool recordFlag = xml.parseInt(); if (type() == TrackType::AUDIO_OUTPUT) recordFlag = false; setRecordFlag1(recordFlag); setRecordFlag2(recordFlag); } else if (tag == "mute") _mute = xml.parseInt(); else if (tag == "solo") _solo = xml.parseInt(); else if (tag == "off") _off = xml.parseInt(); else if (tag == "height") _height = xml.parseInt(); else if (tag == "channels") { setChannels(xml.parseInt()); } else if (tag == "locked") _locked = xml.parseInt(); else if (tag == "recMonitor") setRecMonitor(xml.parseInt()); else if (tag == "selected") _selected = xml.parseInt(); else if (tag == "selectionOrder") _selectionOrder = xml.parseInt(); else if (tag == "color") { QString c = xml.parse1(); if (QColor::isValidColor(c)) m_color.setNamedColor(c); } else return true; return false; } //--------------------------------------------------------- // writeRouting //--------------------------------------------------------- void Track::writeRouting(int level, Xml& xml) const { QString s; if (type() == Track::AUDIO_INPUT) { const RouteList* rl = &_inRoutes; for (ciRoute r = rl->begin(); r != rl->end(); ++r) { if((r->type == Route::TRACK_ROUTE && r->track) || (r->type != Route::TRACK_ROUTE && !r->name().isEmpty())) { s = "Route"; if(r->channel != -1) s += QString(" channel=\"%1\"").arg(r->channel); xml.tag(level++, s.toLatin1().constData()); // New routing scheme. s = "source"; if(r->type == Route::TRACK_ROUTE) s += QString(" track=\"%1\"/").arg(MusEGlobal::song->tracks()->index(r->track)); else s += QString(" type=\"%1\" name=\"%2\"/").arg(r->type).arg(Xml::xmlString(r->name())); xml.tag(level, s.toLatin1().constData()); xml.tag(level, "dest track=\"%d\"/", MusEGlobal::song->tracks()->index(this)); xml.etag(level--, "Route"); } } } const RouteList* rl = &_outRoutes; for (ciRoute r = rl->begin(); r != rl->end(); ++r) { // Ignore Audio Output to Audio Input routes. // They are taken care of by Audio Input in the section above. if((r->type == Route::TRACK_ROUTE && r->track && r->track->type() != Track::AUDIO_INPUT) || (r->type != Route::TRACK_ROUTE && (!r->name().isEmpty() || r->midiPort != -1))) { s = "Route"; if(r->channel != -1) s += QString(" channel=\"%1\"").arg(r->channel); if(r->channels != -1) s += QString(" channels=\"%1\"").arg(r->channels); if(r->remoteChannel != -1) s += QString(" remch=\"%1\"").arg(r->remoteChannel); xml.tag(level++, s.toLatin1().constData()); // Allow for a regular mono or stereo track to feed a multi-channel synti. xml.tag(level, "source track=\"%d\"/", MusEGlobal::song->tracks()->index(this)); s = "dest"; if(r->type != Route::TRACK_ROUTE && r->type != Route::MIDI_PORT_ROUTE) s += QString(" type=\"%1\"").arg(r->type); if(r->type == Route::MIDI_PORT_ROUTE) s += QString(" mport=\"%1\"/").arg(r->midiPort); else if(r->type == Route::TRACK_ROUTE) s += QString(" track=\"%1\"/").arg(MusEGlobal::song->tracks()->index(r->track)); else s += QString(" name=\"%1\"/").arg(Xml::xmlString(r->name())); xml.tag(level, s.toLatin1().constData()); xml.etag(level--, "Route"); } } } int MidiTrack::getFirstControllerValue(int ctrl, int def) { int val=def; unsigned tick=-1; // maximum integer for (iPart pit=parts()->begin(); pit!=parts()->end(); pit++) { Part* part=pit->second; if (part->tick() > tick) break; // ignore this and the rest. we won't find anything new. for (ciEvent eit=part->events().begin(); eit!=part->events().end(); eit++) { if (eit->first+part->tick() >= tick) break; if (eit->first > part->lenTick()) break; // ignore events past the end of the part // else if (eit->first+part->tick() < tick) and if (eit->second.type()==Controller && eit->second.dataA()==ctrl) { val = eit->second.dataB(); tick = eit->first+part->tick(); break; } } } return val; } int MidiTrack::getControllerChangeAtTick(unsigned tick, int ctrl, int def) { for (iPart pit=parts()->begin(); pit!=parts()->end(); pit++) { Part* part=pit->second; if (part->tick() > tick) break; // ignore this and the rest. we'd find nothing any more if (part->endTick() < tick) continue; // ignore only this. for (ciEvent eit=part->events().begin(); eit!=part->events().end(); eit++) { if (eit->first+part->tick() > tick) break; // we won't find anything in this part from now on. if (eit->first > part->lenTick()) break; // ignore events past the end of the part if (eit->first+part->tick() < tick) continue; // ignore only this // else if (eit->first+part->tick() == tick) and if (eit->second.type()==Controller && eit->second.dataA()==ctrl) return eit->second.dataB(); } } return def; } // returns the tick where this CC gets overridden by a new one // returns UINT_MAX for "never" unsigned MidiTrack::getControllerValueLifetime(unsigned tick, int ctrl) { unsigned result=UINT_MAX; for (iPart pit=parts()->begin(); pit!=parts()->end(); pit++) { Part* part=pit->second; if (part->tick() > result) break; // ignore this and the rest. we won't find anything new. if (part->endTick() < tick) continue; // ignore only this part, we won't find anything there. for (ciEvent eit=part->events().begin(); eit!=part->events().end(); eit++) { if (eit->first+part->tick() >= result) break; if (eit->first > part->lenTick()) break; // ignore events past the end of the part // else if (eit->first+part->tick() < result) and if (eit->first+part->tick() > tick && eit->second.type()==Controller && eit->second.dataA()==ctrl) { result = eit->first+part->tick(); break; } } } return result; } //--------------------------------------------------------- // updateDrummap // If audio is running (and not idle) this should only be called by the rt audio thread. // Returns true if map was changed. //--------------------------------------------------------- bool MidiTrack::updateDrummap(int doSignal) { if(type() != Track::DRUM || _outPort < 0 || _outPort >= MusECore::MIDI_PORTS) return false; MidiPort* mp = &MusEGlobal::midiPorts[_outPort]; const int patch = mp->hwCtrlState(_outChannel, CTRL_PROGRAM); bool map_changed; DrumMap ndm; map_changed = false; for(int i = 0; i < 128; i++) { getMapItem(patch, i, ndm, WorkingDrumMapEntry::AllOverrides); DrumMap& tdm = _drummap[i]; if(ndm != tdm) { tdm = ndm; map_changed = true; } // Be sure to update the drum input note map. Probably wise (and easy) to do it always. drum_in_map[(int)tdm.enote] = i; } // Ensure there are NO duplicate enote fields. Returns true if somethng was changed. if(normalizeDrumMap(patch)) map_changed = true; if(map_changed) { // Update the drum in (enote) map. update_drum_in_map(); // TODO Move this to gui thread where it's safe to do so - this is only gui stuff. if(drummap_ordering_tied_to_patch()) // TODO This is not exactly rt friendly since it may de/allocate. init_drum_ordering(); } // TODO Do this outside since we may be called as part of multiple tracks operations. if(map_changed && doSignal) { // It is possible we are being called from gui thread already, in audio idle mode. // Will this still work, and not conflict with audio sending the same message? // Are we are not supposed to write to an fd from different threads? if(!MusEGlobal::audio || MusEGlobal::audio->isIdle()) // Directly emit SC_DRUMMAP song changed signal. MusEGlobal::song->update(SC_DRUMMAP); else // Tell the gui to emit SC_DRUMMAP song changed signal. MusEGlobal::audio->sendMsgToGui('D'); // Drum map changed. return true; } return map_changed; } void MidiTrack::set_drummap_ordering_tied_to_patch(bool val) { _drummap_ordering_tied_to_patch=val; if (val) init_drum_ordering(); } void MidiTrack::modifyWorkingDrumMap(WorkingDrumMapList& list, bool isReset, bool includeDefault, bool #ifdef _USE_INSTRUMENT_OVERRIDES_ isInstrumentMod #endif , bool doWholeMap) { //if(!isDrumTrack()) if(type() != DRUM) return; const int port = outPort(); if(port < 0 || port >= MusECore::MIDI_PORTS) return; MidiPort* mp = &MusEGlobal::midiPorts[port]; const int chan = outChannel(); const int patch = mp->hwCtrlState(chan, MusECore::CTRL_PROGRAM); int index; int idx_end; int other_index; int fields; int cur_enote; int new_enote; // DrumMap orig_dm; DrumMap other_dm; WorkingDrumMapEntry other_wdme; #ifdef _USE_INSTRUMENT_OVERRIDES_ MidiInstrument* instr = mp->instrument(); #endif for(iWorkingDrumMapPatch_t iwdp = list.begin(); iwdp != list.end(); ++iwdp) { index = doWholeMap ? 0 : iwdp->first; idx_end = doWholeMap ? 128 : index + 1; for( ; index < idx_end; ++index) { DrumMap& dm = _drummap[index]; WorkingDrumMapEntry& wdme = iwdp->second; fields = wdme._fields; #ifdef _USE_INSTRUMENT_OVERRIDES_ if(isInstrumentMod) { if(instr) instr->setWorkingDrumMapItem(patch, index, wdme, isReset); } else #endif // FIXME Possible non realtime-friendly allocation. There will be adding new list and copying of 'name' QString here. if(isReset) { // cur_enote = dm.enote; _workingDrumMapPatchList->remove(patch, index, wdme._fields, includeDefault); getMapItem(patch, index, dm, WorkingDrumMapEntry::AllOverrides); // REMOVE Tim. newdrums. Removed. // new_enote = dm.enote; // other_index = drum_in_map[new_enote]; // // if(fields & WorkingDrumMapEntry::ENoteField && other_index != index) // { // // In doWholeMap mode, a previous index iteration may have already cleared the other ENote field. // // So do this only if there is a track override on the ENote field. // if(//doWholeMap && // (isWorkingMapItem(other_index, WorkingDrumMapEntry::ENoteField, patch) & // (WorkingDrumMapEntry::TrackOverride | WorkingDrumMapEntry::TrackDefaultOverride))) // { // // Here we need to see the original map item value /before/ any overrides, so that we can // // tell whether this other_index brute-force 'reset' value is still technically an // // override, and either remove or add (modify) the list appropriately. // getMapItem(patch, other_index, other_dm, WorkingDrumMapEntry::NoOverride); // if(other_dm.enote == cur_enote) // { // // The values are equal. This is technically no longer a track override and we may remove it. // _workingDrumMapPatchList->remove(patch, other_index, WorkingDrumMapEntry::ENoteField, includeDefault); // } // else // { // // The values are not equal. This is technically still a track override, so add (modify) it. // other_dm.enote = cur_enote; // WorkingDrumMapEntry other_wdme(other_dm, WorkingDrumMapEntry::ENoteField); // _workingDrumMapPatchList->add(patch, other_index, other_wdme); // } // // _drummap[other_index].enote = cur_enote; // drum_in_map[cur_enote] = other_index; // } // drum_in_map[new_enote] = index; // } } else { cur_enote = dm.enote; if(includeDefault) { // We are 'promoting' the fields to default patch list... other_wdme._fields = fields; other_wdme._mapItem = dm; // Add the item to the default patch drum list. _workingDrumMapPatchList->add(CTRL_PROGRAM_VAL_DONT_CARE, index, other_wdme); // Now remove the item from the non-default patch drum list. if(patch != CTRL_PROGRAM_VAL_DONT_CARE) _workingDrumMapPatchList->remove(patch, index, WorkingDrumMapEntry::AllFields, false); // Do not include defaults. } else { if(doWholeMap) { if(fields == WorkingDrumMapEntry::AllFields) { other_wdme._fields = fields; other_wdme._mapItem = dm; _workingDrumMapPatchList->add(patch, index, other_wdme); } else _workingDrumMapPatchList->add(patch, index, wdme); } else { _workingDrumMapPatchList->add(patch, index, wdme); getMapItem(patch, index, dm, WorkingDrumMapEntry::AllOverrides); } } if(!doWholeMap && (fields & WorkingDrumMapEntry::ENoteField)) { new_enote = dm.enote; other_index = drum_in_map[new_enote]; // If there is already another track override on the other index we must change it. if(isWorkingMapItem(other_index, WorkingDrumMapEntry::ENoteField, patch) != WorkingDrumMapEntry::NoOverride) { other_dm.enote = cur_enote; //WorkingDrumMapEntry other_wdme(other_dm, WorkingDrumMapEntry::ENoteField); other_wdme._mapItem = other_dm; other_wdme._fields = WorkingDrumMapEntry::ENoteField; if(includeDefault) { _workingDrumMapPatchList->add(CTRL_PROGRAM_VAL_DONT_CARE, other_index, other_wdme); // Now remove the item from the non-default patch drum list. if(patch != CTRL_PROGRAM_VAL_DONT_CARE) _workingDrumMapPatchList->remove(patch, other_index, WorkingDrumMapEntry::ENoteField, false); // Do not include defaults. } else _workingDrumMapPatchList->add(patch, other_index, other_wdme); //_drummap[other_index].enote = cur_enote; //drum_in_map[cur_enote] = other_index; //drum_in_map[new_enote] = index; } } } } } // Ensure there are NO duplicate enote fields. //if(normalizeDrumMap(patch)) // If anything changed, update the drum in map. // update_drum_in_map(); updateDrummap(false); // No signal. } void MidiTrack::setWorkingDrumMap(WorkingDrumMapPatchList* list, bool #ifdef _USE_INSTRUMENT_OVERRIDES_ isInstrumentMod #endif ) { //if(!isDrumTrack()) if(type() != DRUM) return; #ifdef _USE_INSTRUMENT_OVERRIDES_ if(isInstrumentMod) { // TODO // const int port = outPort(); // if(port < 0 || port >= MIDI_PORTS) // return; // MidiPort* mp = &MusEGlobal::midiPorts[port]; // MidiInstrument* instr = mp->instrument(); // instr->setWorkingDrumMap(); return; } #endif _workingDrumMapPatchList = list; // We must ensure that there are NO duplicate enote fields, // since the instrument map may have changed by now. //normalizeWorkingDrumMapPatchList(); updateDrummap(false); // No signal. update_drum_in_map(); } void MidiTrack::getMapItemAt(int tick, int index, DrumMap& dest_map, int overrideType) const { //if(!isDrumTrack()) if(type() != DRUM) { dest_map = iNewDrumMap[index]; return; } const int port = outPort(); if(port < 0 || port >= MusECore::MIDI_PORTS) { dest_map = iNewDrumMap[index]; return; } const MidiPort* mp = &MusEGlobal::midiPorts[port]; const int track_chan = outChannel(); // Get the patch number at tick, contributed by any part, // ignoring values outside of their parts. We must include // muted or off parts or tracks in the search since this is an // operation that must not be affected by mute or off. const int track_patch = mp->getVisibleCtrl(track_chan, tick, MusECore::CTRL_PROGRAM, true, true, true); // Get the instrument's map item, and include any requested overrides. getMapItem(track_patch, index, dest_map, overrideType); } void MidiTrack::getMapItem(int patch, int index, DrumMap& dest_map, int overrideType) const { //if(!isDrumTrack()) if(type() != DRUM) { dest_map = iNewDrumMap[index]; return; } const int port = outPort(); if(port < 0 || port >= MusECore::MIDI_PORTS) { dest_map = iNewDrumMap[index]; return; } const MidiPort* mp = &MusEGlobal::midiPorts[port]; const MidiInstrument* midi_instr = mp->instrument(); if(!midi_instr) { dest_map = iNewDrumMap[index]; return; } // Get the instrument's map item, and include any requested overrides. const int channel = outChannel(); midi_instr->getMapItem(channel, patch, index, dest_map, overrideType); // Did we request to include any track default patch overrides? if(overrideType & WorkingDrumMapEntry::TrackDefaultOverride) { // Get any track default patch overrides. const WorkingDrumMapEntry* def_wdm = _workingDrumMapPatchList->find(CTRL_PROGRAM_VAL_DONT_CARE, index, false); // No default. if(def_wdm) { if(def_wdm->_fields & WorkingDrumMapEntry::NameField) dest_map.name = def_wdm->_mapItem.name; if(def_wdm->_fields & WorkingDrumMapEntry::VolField) dest_map.vol = def_wdm->_mapItem.vol; if(def_wdm->_fields & WorkingDrumMapEntry::QuantField) dest_map.quant = def_wdm->_mapItem.quant; if(def_wdm->_fields & WorkingDrumMapEntry::LenField) dest_map.len = def_wdm->_mapItem.len; if(def_wdm->_fields & WorkingDrumMapEntry::ChanField) dest_map.channel = def_wdm->_mapItem.channel; if(def_wdm->_fields & WorkingDrumMapEntry::PortField) dest_map.port = def_wdm->_mapItem.port; if(def_wdm->_fields & WorkingDrumMapEntry::Lv1Field) dest_map.lv1 = def_wdm->_mapItem.lv1; if(def_wdm->_fields & WorkingDrumMapEntry::Lv2Field) dest_map.lv2 = def_wdm->_mapItem.lv2; if(def_wdm->_fields & WorkingDrumMapEntry::Lv3Field) dest_map.lv3 = def_wdm->_mapItem.lv3; if(def_wdm->_fields & WorkingDrumMapEntry::Lv4Field) dest_map.lv4 = def_wdm->_mapItem.lv4; if(def_wdm->_fields & WorkingDrumMapEntry::ENoteField) dest_map.enote = def_wdm->_mapItem.enote; if(def_wdm->_fields & WorkingDrumMapEntry::ANoteField) dest_map.anote = def_wdm->_mapItem.anote; if(def_wdm->_fields & WorkingDrumMapEntry::MuteField) dest_map.mute = def_wdm->_mapItem.mute; if(def_wdm->_fields & WorkingDrumMapEntry::HideField) dest_map.hide = def_wdm->_mapItem.hide; } } // Did we request to include any track overrides? if(!(overrideType & WorkingDrumMapEntry::TrackOverride)) return; // Get any track overrides. const WorkingDrumMapEntry* wdm = _workingDrumMapPatchList->find(patch, index, false); // No default. if(!wdm) return; if(wdm->_fields & WorkingDrumMapEntry::NameField) dest_map.name = wdm->_mapItem.name; if(wdm->_fields & WorkingDrumMapEntry::VolField) dest_map.vol = wdm->_mapItem.vol; if(wdm->_fields & WorkingDrumMapEntry::QuantField) dest_map.quant = wdm->_mapItem.quant; if(wdm->_fields & WorkingDrumMapEntry::LenField) dest_map.len = wdm->_mapItem.len; if(wdm->_fields & WorkingDrumMapEntry::ChanField) dest_map.channel = wdm->_mapItem.channel; if(wdm->_fields & WorkingDrumMapEntry::PortField) dest_map.port = wdm->_mapItem.port; if(wdm->_fields & WorkingDrumMapEntry::Lv1Field) dest_map.lv1 = wdm->_mapItem.lv1; if(wdm->_fields & WorkingDrumMapEntry::Lv2Field) dest_map.lv2 = wdm->_mapItem.lv2; if(wdm->_fields & WorkingDrumMapEntry::Lv3Field) dest_map.lv3 = wdm->_mapItem.lv3; if(wdm->_fields & WorkingDrumMapEntry::Lv4Field) dest_map.lv4 = wdm->_mapItem.lv4; if(wdm->_fields & WorkingDrumMapEntry::ENoteField) dest_map.enote = wdm->_mapItem.enote; if(wdm->_fields & WorkingDrumMapEntry::ANoteField) dest_map.anote = wdm->_mapItem.anote; if(wdm->_fields & WorkingDrumMapEntry::MuteField) dest_map.mute = wdm->_mapItem.mute; if(wdm->_fields & WorkingDrumMapEntry::HideField) dest_map.hide = wdm->_mapItem.hide; } int MidiTrack::isWorkingMapItem(int index, int fields, int patch) const { int ret = WorkingDrumMapEntry::NoOverride; if(type() != DRUM) return ret; // Is there an instrument override for this drum map item? const int port = outPort(); if(port >= 0 && port < MusECore::MIDI_PORTS) { const MidiPort* mp = &MusEGlobal::midiPorts[port]; // Grab the patch number while we are here, if we asked for it. if(patch == -1) { const int chan = outChannel(); patch = mp->hwCtrlState(chan, CTRL_PROGRAM); } #ifdef _USE_INSTRUMENT_OVERRIDES_ const MidiInstrument* midi_instr = mp->instrument(); if(midi_instr) ret |= midi_instr->isWorkingMapItem(patch, index, fields); #endif } // Is there a local track default patch override for this drum map item? const WorkingDrumMapEntry* def_wdm = _workingDrumMapPatchList->find(CTRL_PROGRAM_VAL_DONT_CARE, index, false); // No default. if(def_wdm && (def_wdm->_fields & fields)) ret |= WorkingDrumMapEntry::TrackDefaultOverride; if(patch != -1) { // Is there a local track override for this drum map item? const WorkingDrumMapEntry* wdm = _workingDrumMapPatchList->find(patch, index, false); // No default. if(wdm && (wdm->_fields & fields)) ret |= WorkingDrumMapEntry::TrackOverride; } return ret; } bool MidiTrack::normalizeDrumMap(int patch) { if(type() != DRUM) return false; //WorkingDrumMapList* wdml = _workingDrumMapPatchList->find(patch, true); WorkingDrumMapList* wdml = _workingDrumMapPatchList->find(patch, false); WorkingDrumMapList* def_wdml = 0; if(patch != CTRL_PROGRAM_VAL_DONT_CARE) def_wdml = _workingDrumMapPatchList->find(CTRL_PROGRAM_VAL_DONT_CARE, false); int index = 0; DrumMap dm; char enote; bool changed = false; bool used_index[128]; int used_enotes[128]; for(int i = 0; i < 128; ++i) { used_index[i] = false; used_enotes[i] = 0; } char unused_enotes[128]; int unused_enotes_sz = 0; char unused_index[128]; int unused_index_sz = 0; int unused_enotes_cnt = 0; // Find all the used enote fields and their indexes in the working list. if(wdml) { for(iWorkingDrumMapPatch_t iwdml = wdml->begin(); iwdml != wdml->end(); ++iwdml) { WorkingDrumMapEntry& wdme = iwdml->second; if(wdme._fields & WorkingDrumMapEntry::ENoteField) { used_index[iwdml->first] = true; //++used_enotes[(unsigned char)wdme._mapItem.enote]; } } } // Add all the used enote fields and their indexes in the default patch working list. if(def_wdml) { for(iWorkingDrumMapPatch_t iwdml = def_wdml->begin(); iwdml != def_wdml->end(); ++iwdml) { WorkingDrumMapEntry& wdme = iwdml->second; if(wdme._fields & WorkingDrumMapEntry::ENoteField) { used_index[iwdml->first] = true; //++used_enotes[(unsigned char)wdme._mapItem.enote]; } } } // Find all the used enote fields and their indexes in the working list. if(wdml) { for(iWorkingDrumMapPatch_t iwdml = wdml->begin(); iwdml != wdml->end(); ++iwdml) { WorkingDrumMapEntry& wdme = iwdml->second; if(wdme._fields & WorkingDrumMapEntry::ENoteField) { //used_index[iwdml->first] = true; ++used_enotes[(unsigned char)wdme._mapItem.enote]; } } } // Find all the unused indexes and enotes so far in the working list. unused_index_sz = 0; unused_enotes_sz = 0; for(int i = 0; i < 128; ++i) { if(!used_index[i]) unused_index[unused_index_sz++] = i; if(used_enotes[i] == 0) unused_enotes[unused_enotes_sz++] = i; } // Ensure there are NO duplicate enotes in the existing working list items so far. unused_enotes_cnt = 0; if(wdml) { for(iWorkingDrumMapPatch_t iwdml = wdml->begin(); iwdml != wdml->end(); ++iwdml) { WorkingDrumMapEntry& wdme = iwdml->second; if(wdme._fields & WorkingDrumMapEntry::ENoteField) { // More than 1 (this) usage? if(used_enotes[(unsigned char)wdme._mapItem.enote] > 1) { fprintf(stderr, "MidiTrack::normalizeWorkingDrumMap: Warning: Duplicate enote:%d found. Overriding it.\n", wdme._mapItem.enote); if(unused_enotes_cnt >= unused_enotes_sz) { fprintf(stderr, "MidiTrack::normalizeWorkingDrumMap: Error: unused_enotes_cnt >= unused_enotes_sz:%d\n", unused_enotes_sz); break; } --used_enotes[(unsigned char)wdme._mapItem.enote]; //wdme._mapItem.enote = unused_enotes[unused_enotes_cnt++]; // Get the instrument item. index = iwdml->first; // Modify the enote field. enote = unused_enotes[unused_enotes_cnt++]; _drummap[index].enote = enote; ++used_enotes[(unsigned char)enote]; changed = true; } } } } // Find all the used enote fields and their indexes in the default patch working list. if(def_wdml) { for(iWorkingDrumMapPatch_t iwdml = def_wdml->begin(); iwdml != def_wdml->end(); ++iwdml) { WorkingDrumMapEntry& wdme = iwdml->second; if(wdme._fields & WorkingDrumMapEntry::ENoteField) { //used_index[iwdml->first] = true; // If there is already a non-default patch enote override for this index, // do not increment used_notes, the non-default one takes priority over this default one. if(wdml) { ciWorkingDrumMapPatch_t def_iwdml = wdml->find(iwdml->first); if(def_iwdml != wdml->end()) { const WorkingDrumMapEntry& def_wdme = def_iwdml->second; if(def_wdme._fields & WorkingDrumMapEntry::ENoteField) continue; } } ++used_enotes[(unsigned char)wdme._mapItem.enote]; } } } // Find all the unused indexes and enotes so far in the working list. unused_enotes_sz = 0; for(int i = 0; i < 128; ++i) { if(used_enotes[i] == 0) unused_enotes[unused_enotes_sz++] = i; } // Ensure there are NO duplicate enotes in the existing default patch working list items so far. unused_enotes_cnt = 0; if(def_wdml) { for(iWorkingDrumMapPatch_t iwdml = def_wdml->begin(); iwdml != def_wdml->end(); ++iwdml) { WorkingDrumMapEntry& wdme = iwdml->second; if(wdme._fields & WorkingDrumMapEntry::ENoteField) { // If there is already a non-default patch enote override for this index, // skip this one, the non-default one takes priority over this default one. if(wdml) { ciWorkingDrumMapPatch_t def_iwdml = wdml->find(iwdml->first); if(def_iwdml != wdml->end()) { const WorkingDrumMapEntry& def_wdme = def_iwdml->second; if(def_wdme._fields & WorkingDrumMapEntry::ENoteField) continue; } } // More than 1 (this) usage? if(used_enotes[(unsigned char)wdme._mapItem.enote] > 1) { fprintf(stderr, "MidiTrack::normalizeWorkingDrumMap: Warning: Duplicate default enote:%d found. Overriding it.\n", wdme._mapItem.enote); if(unused_enotes_cnt >= unused_enotes_sz) { fprintf(stderr, "MidiTrack::normalizeWorkingDrumMap: Error: Default unused_enotes_cnt >= unused_enotes_sz:%d\n", unused_enotes_sz); break; } --used_enotes[(unsigned char)wdme._mapItem.enote]; //wdme._mapItem.enote = unused_enotes[unused_enotes_cnt++]; // Get the instrument item. index = iwdml->first; // Modify the enote field. enote = unused_enotes[unused_enotes_cnt++]; _drummap[index].enote = enote; ++used_enotes[(unsigned char)enote]; changed = true; } } } } // Add all used enotes in the unused enote indexes (the instrument fields). for(int i = 0; i < unused_index_sz; ++i) ++used_enotes[(unsigned char)_drummap[(unsigned char)unused_index[i]].enote]; // Find all the unused enotes. unused_enotes_sz = 0; for(int i = 0; i < 128; ++i) { if(used_enotes[i] == 0) unused_enotes[unused_enotes_sz++] = i; } // Ensure there are NO duplicate enotes in the unused enote map fields (the instrument fields). unused_enotes_cnt = 0; for(int i = 0; i < unused_index_sz; ++i) { // Get the instrument item. index = unused_index[i]; enote = _drummap[index].enote; // More than 1 (this) usage? if(used_enotes[(unsigned char)enote] > 1) { if(unused_enotes_cnt >= unused_enotes_sz) { fprintf(stderr, "MidiTrack::normalizeWorkingDrumMap: Error filling background items: unused_enotes_cnt >= unused_enotes_sz:%d\n", unused_enotes_sz); break; } --used_enotes[(unsigned char)enote]; // Modify the enote field. _drummap[index].enote = unused_enotes[unused_enotes_cnt++]; ++used_enotes[(unsigned char)_drummap[index].enote]; changed = true; } } return changed; } bool MidiTrack::normalizeDrumMap() { if(type() != DRUM) return false; const int port = outPort(); if(port < 0 || port >= MusECore::MIDI_PORTS) return false; const int chan = outChannel(); const int patch = MusEGlobal::midiPorts[port].hwCtrlState(chan, MusECore::CTRL_PROGRAM); return normalizeDrumMap(patch); } } // namespace MusECore muse-4.0.0/src/muse/track.h000066400000000000000000001627501404105311000155010ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: track.h,v 1.39.2.17 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2011-2013, 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TRACK_H__ #define __TRACK_H__ #include #include #include #include #include #include "wave.h" // for SndFileR #include "part.h" #include "mpevent.h" #include "key.h" #include "audio_fifo.h" #include "route.h" #include "ctrl.h" #include "globaldefs.h" #include "cleftypes.h" #include "controlfifo.h" #include "latency_info.h" #include "transport_obj.h" #include "plugin.h" #include "latency_compensator.h" // Forward declarations: namespace MusECore { class Pipeline; class PluginI; class MidiPort; class Xml; struct DrumMap; class WorkingDrumMapList; class WorkingDrumMapPatchList; class LatencyCompensator; typedef std::vector AuxSendValueList; typedef std::vector::iterator iAuxSendValue; //--------------------------------------------------------- // Track //--------------------------------------------------------- class Track { public: enum TrackType { MIDI=0, DRUM, WAVE, AUDIO_OUTPUT, AUDIO_INPUT, AUDIO_GROUP, AUDIO_AUX, AUDIO_SOFTSYNTH }; // NOTE: ASSIGN_DUPLICATE_PARTS ASSIGN_COPY_PARTS and ASSIGN_CLONE_PARTS are not allowed together - choose one. // (Safe, but it will choose one action over the other.) enum AssignFlags { ASSIGN_NONE=0, ASSIGN_PROPERTIES=1, ASSIGN_DUPLICATE_PARTS=2, ASSIGN_COPY_PARTS=4, ASSIGN_CLONE_PARTS=8, ASSIGN_PLUGINS=16, ASSIGN_STD_CTRLS=32, ASSIGN_PLUGIN_CTRLS=64, ASSIGN_ROUTES=128, ASSIGN_DEFAULT_ROUTES=256, ASSIGN_DRUMLIST=512 }; private: TrackType _type; // Serial number generator. static int _snGen; QString _comment; PartList _parts; QColor m_color; void init(int channels = 0); void internal_assign(const Track&, int flags); protected: static unsigned int _soloRefCnt; static Track* _tmpSoloChainTrack; static bool _tmpSoloChainDoIns; static bool _tmpSoloChainNoDec; // Every time a track is selected, the track's _selectionOrder is set to this value, // and then this value is incremented. The value is reset to zero occasionally, // for example whenever Song::selectAllTracks(false) is called. static int _selectionOrderCounter; RouteList _inRoutes; RouteList _outRoutes; bool _nodeTraversed; // Internal anti circular route traversal flag. int _auxRouteCount; // Number of aux paths feeding this track. // Serial number. int _sn; QString _name; bool _recordFlag; bool _recMonitor; // For midi and audio. Whether to pass the input through to output. bool _mute; bool _solo; unsigned int _internalSolo; bool _off; int _channels; // 1 - mono, 2 - stereo int _activity; int _lastActivity; double _meter[MusECore::MAX_CHANNELS]; double _peak[MusECore::MAX_CHANNELS]; bool _isClipped[MusECore::MAX_CHANNELS]; //used in audio mixer strip. Persistent. int _y; int _height; // visual height in arranger bool _locked; bool _selected; // The selection order of this track, compared to other selected tracks. // The selected track with the highest selected order is the most recent selected. int _selectionOrder; // Holds latency computations each cycle. TrackLatencyInfo _latencyInfo; // Holds a special extra 'source': The transport stream. // Tracks or plugins that request/receive transport info use this. TransportSource _transportSource; int newSn() { return _snGen++; } bool readProperties(Xml& xml, const QString& tag); void writeProperties(int level, Xml& xml) const; public: Track(TrackType, int channels = 0); // Copy constructor. // The various AssignFlags (flags) determine what is copied. // For now, the new track's name is set to the original name. // The caller SHOULD set a proper unique name afterwards, // otherwise identical track names will appear in the song. // It is possible for us to automatically to choose a unique name // here, but that may not be wise in some cases (such as modifying a // track copy and then requesting the operations system switch tracks - // in that case we want the names to be the same). // Also, it is possible the name choosing routine(s) may fail for // whatever reason, and we cannot abort creation here. // For that reason, it is best for the caller to try and pick a // unique name successfully BEFORE calling this constructor. Track(const Track&, int flags); virtual ~Track(); virtual void assign(const Track&, int flags); inline int serial() const { return _sn; } static const char* _cname[]; static QIcon *trackTypeIcon(TrackType); static QColor trackTypeColor(TrackType); static QColor trackTypeLabelColor(TrackType); QIcon* icon() const { return trackTypeIcon(type()); } QColor color() const { return m_color.isValid() ? m_color : trackTypeColor(type()); } void setColor(const QColor c) { m_color = c; } void resetColor() { m_color = QColor(); } QColor labelColor() const { return trackTypeLabelColor(type()); } QString comment() const { return _comment; } void setComment(const QString& s) { _comment = s; } int y() const; void setY(int n) { _y = n; } virtual int height() const = 0; void setHeight(int n) { _height = n; } bool selected() const { return _selected; } // Try to always call this instead of setting _selected, because it also sets _selectionOrder. void setSelected(bool f); // The order of selection of this track, compared to other selected tracks. // The selected track with the highest selected order is the most recent selected. int selectionOrder() const { return _selectionOrder; } // Resets the static selection counter. Optional. (Range is huge, unlikely would have to call). // Called for example whenever Song::selectAllTracks(false) is called. static void clearSelectionOrderCounter(){ _selectionOrderCounter = 0; } bool locked() const { return _locked; } void setLocked(bool b) { _locked = b; } void clearRecAutomation(bool clearList); const QString& name() const { return _name; } // setName can be overloaded to do other things like setting port names, while setNameText just sets the text. virtual void setName(const QString& s) { _name = s; } // setNameText just sets the text, while setName can be overloaded to do other things like setting port names. void setNameText(const QString& s) { _name = s; } // Returns a name suitable for display like "1:Track 5" where the number is the track's index in the track list. // This is useful because in the case of importing a midi file we allow duplicate, often blank, names. // This display string will help identify them. Like "1:", "2:" etc. QString displayName() const; TrackType type() const { return _type; } void setType(TrackType t) { _type = t; } QString cname() const { int t = type(); return QString(_cname[t]); } // routing RouteList* inRoutes() { return &_inRoutes; } RouteList* outRoutes() { return &_outRoutes; } const RouteList* inRoutes() const { return &_inRoutes; } const RouteList* outRoutes() const { return &_outRoutes; } virtual bool noInRoute() const { return _inRoutes.empty(); } virtual bool noOutRoute() const { return _outRoutes.empty(); } void writeRouting(int, Xml&) const; bool isCircularRoute(Track* dst); int auxRefCount() const { return _auxRouteCount; } // Number of Aux Tracks with routing paths to this track. void updateAuxRoute(int refInc, Track* dst); // Internal use. // Number of routable inputs/outputs for each Route::RouteType. virtual RouteCapabilitiesStruct routeCapabilities() const; PartList* parts() { return &_parts; } const PartList* cparts() const { return &_parts; } Part* findPart(unsigned tick); iPart addPart(Part* p); // Select or unselect a range of events. If t0 == t1, ALL events will be affected. // The t0 and t1 can be ticks or frames depending on the type of events. Unused for now. // Returns true if anything changed. bool selectEvents(bool select, unsigned long t0 = 0, unsigned long t1 = 0); virtual void write(int, Xml&) const = 0; virtual Track* clone(int flags) const = 0; // Returns true if any event in any part was opened. Does not operate on the part's clones, if any. virtual bool openAllParts() { return false; }; // Returns true if any event in any part was closed. Does not operate on the part's clones, if any. virtual bool closeAllParts() { return false; }; // Called from gui thread only. virtual bool setRecordFlag1(bool) = 0; // Called from audio thread only. virtual void setRecordFlag2(bool) = 0; // Same as setRecordFlag2 except it can automatically set the monitor flag // depending on the global config.monitorOnRecord. Called from audio thread only. // Returns whether the monitor was changed. virtual bool setRecordFlag2AndCheckMonitor(bool r) = 0; virtual Part* newPart(Part*p=0, bool clone = false) = 0; void dump() const; virtual void setMute(bool val) { _mute = val; } virtual void setOff(bool val) { _off = val; } void setInternalSolo(unsigned int val); virtual void setSolo(bool val) = 0; // Returns true if playback ultimately is muted, depending on // other factors such as soloing. virtual bool isMute() const { if(_solo || (_internalSolo && !_mute)) return false; if(_soloRefCnt) return true; return _mute; } // Returns true if playback ultimately is monitored, depending on // other factors such as soloing. virtual bool isRecMonitored() const { if(_off || !_recMonitor) return false; if(_solo || _internalSolo) return true; return _soloRefCnt == 0; } // Returns true (>= 1) if proxy-soloed. virtual unsigned int internalSolo() const { return _internalSolo; } // Returns true if proxy-muted. virtual bool soloMode() const { return _soloRefCnt; } // Returns true if soloed. virtual bool solo() const { return _solo; } // Returns true if muted. virtual bool mute() const { return _mute; } // Returns true if track is off. virtual bool off() const { return _off; } // Returns true if rec-armed. virtual bool recordFlag() const { return _recordFlag; } // Sets monitor. virtual void setRecMonitor(bool b) { if(canRecordMonitor()) _recMonitor = b; } // Returns true if monitored. virtual bool recMonitor() const { return _recMonitor; } virtual void preProcessAlways() { } TransportSource& transportSource() { return _transportSource; } // // Returns true if the transport source is connected to any of the // // track's midi input ports (ex. synth ports not muse midi ports). // // If midiport is -1, returns true if ANY port is connected. // virtual bool transportSourceConnected(int /*midiport*/ = -1) const { return false; } // Returns true if the transport source is connected to any of the // track's midi input ports (ex. synth ports not muse midi ports). virtual bool usesTransportSource() const { return false; } virtual bool transportAffectsAudioLatency() const { return false; } // Initializes this track's latency information in preparation for a latency scan. virtual void prepareLatencyScan(); // The contribution to latency from the track's own members (audio effect rack, etc). inline virtual float selfLatencyAudio(int /*channel*/) const { return 0.0f; } // The cached worst latency of all the channels in the track's effect rack plus any synthesizer latency if applicable. virtual float getWorstPluginLatencyAudio(); // The cached worst contribution to latency by any ports (for ex. Jack ports of audio input/output tracks). virtual float getWorstPortLatencyAudio(); // The cached worst latency of all the contributions from the track's own members (audio effect rack, etc) // plus any port latency if applicable. virtual float getWorstSelfLatencyAudio(); // Whether this track (and the branch it is in) can force other parallel branches to // increase their latency compensation to match this one. // If false, this branch will NOT disturb other parallel branches' compensation, // intead only allowing compensation UP TO the worst case in other branches. virtual bool canDominateOutputLatency() const; virtual bool canDominateInputLatency() const; // Whether this track (and the branch it is in) can force other parallel branches to // increase their latency compensation to match this one - IF this track is an end-point // and the branch allows domination. // If false, this branch will NOT disturb other parallel branches' compensation, // intead only allowing compensation UP TO the worst case in other branches. inline virtual bool canDominateEndPointLatency() const { return false; } // Whether this track and its branch can correct for latency, not just compensate. inline virtual bool canCorrectOutputLatency() const { return false; } // Whether the track can pass latency values through, the SAME as if record monitor is // supported and on BUT does not require record monitor support. // This is for example in the metronome MetronomeSynthI, since it is unique in that it // can correct its own latency unlike other synths, but it does not 'pass through' // the latency values to what drives it like other synths. virtual bool canPassThruLatency() const; // Whether any of the connected output routes are effectively connected. // That means track is not off, track is monitored where applicable, etc, // ie. signal can actually flow. // For Wave Tracks for example, asks whether the track is an end-point from the view of the input side. virtual bool isLatencyInputTerminal() = 0; // Whether any of the connected output routes are effectively connected. // That means track is not off, track is monitored where applicable, etc, // ie. signal can actually flow. // For Wave Tracks for example, asks whether the track is an end-point from the view of the playback side. virtual bool isLatencyOutputTerminal() = 0; virtual TrackLatencyInfo& getDominanceInfo(bool input) = 0; virtual TrackLatencyInfo& getDominanceLatencyInfo(bool input) = 0; // The finalWorstLatency is the grand final worst-case latency, of any output track or open branch, // determined in the complete getDominanceLatencyInfo() scan. // The callerBranchLatency is the inherent branch latency of the calling track, or zero if calling from // the very top outside of the branch heads (outside of output tracks or open branches). // The callerBranchLatency is accumulated as setCorrectionLatencyInfo() is called on each track // in a branch of the graph. virtual TrackLatencyInfo& setCorrectionLatencyInfo(bool input, float finalWorstLatency, float callerBranchLatency = 0.0f) = 0; // Argument 'input': Whether we want the input side of the track. For example un-monitored wave tracks // are considered two separate paths with a recording input side and a playback output side. virtual TrackLatencyInfo& getLatencyInfo(bool input) = 0; // Used during latency compensation processing. When analyzing in 'reverse' this mechansim is // needed only to equalize the timing of all the AudioOutput tracks. // It is applied as a direct offset in the latency delay compensator in getData(). virtual unsigned long latencyCompWriteOffset() const { return _latencyInfo._compensatorWriteOffset; } virtual void setLatencyCompWriteOffset(float /*worstCase*/) { } // Internal use... static void clearSoloRefCounts(); void updateSoloState(); virtual void updateSoloStates(bool noDec) = 0; virtual void updateInternalSoloStates(); int activity() { return _activity; } void setActivity(int v) { _activity = v; } int lastActivity() { return _lastActivity; } void setLastActivity(int v) { _lastActivity = v; } void addActivity(int v) { _activity += v; } void resetPeaks(); static void resetAllMeter(); double meter(int ch) const { return _meter[ch]; } double peak(int ch) const { return _peak[ch]; } void resetMeter(); bool readProperty(Xml& xml, const QString& tag); int channels() const { return _channels; } virtual void setChannels(int n); bool isMidiTrack() const { return type() == MIDI || type() == DRUM; } bool isDrumTrack() const { return type() == DRUM; } bool isSynthTrack() const { return type() == AUDIO_SOFTSYNTH; } virtual bool canRecord() const { return false; } virtual bool canRecordMonitor() const { return false; } virtual AutomationType automationType() const = 0; virtual void setAutomationType(AutomationType t) = 0; static void setVisible(bool) { } bool isVisible(); inline bool isClipped(int ch) const { if(ch >= MusECore::MAX_CHANNELS) return false; return _isClipped[ch]; } void resetClipper() { for(int ch = 0; ch < MusECore::MAX_CHANNELS; ++ch) _isClipped[ch] = false; } }; //--------------------------------------------------------- // MidiTrack //--------------------------------------------------------- class MidiTrack : public Track { int _outPort; int _outChannel; private: static bool _isVisible; clefTypes clefType; DrumMap* _drummap; // _drummap[foo].anote is always equal to foo // A list of user-altered drum map items. WorkingDrumMapPatchList* _workingDrumMapPatchList; bool _drummap_ordering_tied_to_patch; //if true, changing patch also changes drummap-ordering int drum_in_map[128]; int _curDrumPatchNumber; // Can be CTRL_VAL_UNKNOWN. void init(); void internal_assign(const Track&, int flags); void init_drummap(bool write_ordering); // function without argument in public void remove_ourselves_from_drum_ordering(); void writeOurDrumSettings(int level, Xml& xml) const; void readOurDrumSettings(Xml& xml); public: EventList events; // tmp Events during midi import MPEventList mpevents; // tmp Events druring recording MPEventList stuckLiveNotes; // Live (rec): Currently sounding note-ons that we don't know the note-off time yet. Event times = 0. MPEventList stuckNotes; // Playback: Currently sounding note-ons contributed by track - not sent directly to device MidiTrack(); MidiTrack(const MidiTrack&, int flags); virtual ~MidiTrack(); // FIXME This public assign() method doesn't really 'assign' routes - // if routes are assigned in flags, it does not clear existing routes ! virtual void assign(const Track&, int flags); virtual void convertToType(TrackType trackType); virtual AutomationType automationType() const; virtual void setAutomationType(AutomationType); // play parameter int transposition; int velocity; int delay; int len; int compression; // Called from gui thread only. virtual bool setRecordFlag1(bool) { return canRecord(); } // Called from audio thread only. virtual void setRecordFlag2(bool f) { if(canRecord()) _recordFlag = f; } // Same as setRecordFlag2 except it can automatically set the monitor flag // depending on the global config.monitorOnRecord. Called from audio thread only. // Returns whether the monitor was changed. virtual bool setRecordFlag2AndCheckMonitor(bool); virtual void read(Xml&); virtual void write(int, Xml&) const; virtual int height() const; virtual MidiTrack* clone(int flags) const { return new MidiTrack(*this, flags); } virtual Part* newPart(Part*p=0, bool clone=false); // Number of routable inputs/outputs for each Route::RouteType. virtual RouteCapabilitiesStruct routeCapabilities() const; virtual bool canDominateOutputLatency() const; virtual bool canCorrectOutputLatency() const; virtual bool isLatencyInputTerminal(); virtual bool isLatencyOutputTerminal(); virtual TrackLatencyInfo& getDominanceLatencyInfo(bool input); virtual TrackLatencyInfo& getDominanceInfo(bool input); // The finalWorstLatency is the grand final worst-case latency, of any output track or open branch, // determined in the complete getDominanceLatencyInfo() scan. // The callerBranchLatency is the inherent branch latency of the calling track, or zero if calling from // the very top outside of the branch heads (outside of output tracks or open branches). // The callerBranchLatency is accumulated as setCorrectionLatencyInfo() is called on each track // in a branch of the graph. virtual TrackLatencyInfo& setCorrectionLatencyInfo(bool input, float finalWorstLatency, float callerBranchLatency = 0.0f); virtual TrackLatencyInfo& getLatencyInfo(bool input); // Used during latency compensation processing. When analyzing in 'reverse' this mechansim is // needed only to equalize the timing of all the AudioOutput tracks. // It is applied as a direct offset in the latency delay compensator in getData(). virtual void setLatencyCompWriteOffset(float worstCase); // This enum describes what has changed in the following port/channel methods. enum ChangedType { NothingChanged = 0x0, PortChanged = 0x1, ChannelChanged = 0x2, DrumMapChanged = 0x4 }; // OR'd ChangedType flags. typedef int ChangedType_t; // Sets the output port, and for a drum track updates any drum map. Returns true if anything changed. // If doSignal is true, automatically emits SC_DRUM_MAP or sends audio message if audio is running and not idle. ChangedType_t setOutPort(int i, bool doSignal = false); // Sets the output channel, and for a drum track updates any drum map. Returns true if anything changed. // If doSignal is true, automatically emits SC_DRUM_MAP or sends audio message if audio is running and not idle. ChangedType_t setOutChannel(int i, bool doSignal = false); // Same as setOutPort, but also transfers controller data to the new selected port. ChangedType_t setOutPortAndUpdate(int port, bool doSignal = false); // Same as setOutChannel, but also transfers controller data to the new selected channel. ChangedType_t setOutChanAndUpdate(int chan, bool doSignal = false); // Combines both setOutChannel and setOutPort operations, and transfers controller data to the new selected port/channel. ChangedType_t setOutPortAndChannelAndUpdate(int port, int chan, bool doSignal = false); // Backward compatibility: For reading old songs. void setInPortAndChannelMask(unsigned int portmask, int chanmask); // Overridden for special midi output behaviour. virtual bool noOutRoute() const; int outPort() const { return _outPort; } int outChannel() const { return _outChannel; } // Given ctrl, if ctrl is a drum (per-note) controller, fills the other parameters with the // mapped track ctrl, port, and channel from the drum map, and returns true. // If ctrl is not a drum controller, port and channel are filled with the track's // port and channel (same as outPort() and outChannel()), and ctrl is not changed, and returns false. // Port, mport, and channel can be null. Ctrl is the fully qualified number if any (low byte is note) // and if ctrl is a drum controller ctrl is filled the note mapped to the drum map 'anote'. bool mappedPortChanCtrl(int *ctrl, int *port, MidiPort** mport, int *channel) const; virtual void setSolo(bool val); virtual void updateSoloStates(bool noDec); virtual void updateInternalSoloStates(); virtual bool addStuckNote(const MidiPlayEvent& ev); // These are only for 'live' (rec) notes for which we don't have a note-off time yet. Even times = 0. virtual bool addStuckLiveNote(int port, int chan, int note, int vel = 64); virtual bool removeStuckLiveNote(int port, int chan, int note); virtual bool stuckLiveNoteExists(int port, int chan, int note); virtual bool canRecord() const { return true; } virtual bool canRecordMonitor() const { return true; } static void setVisible(bool t) { _isVisible = t; } static bool visible() { return _isVisible; } int getFirstControllerValue(int ctrl, int def=-1); int getControllerChangeAtTick(unsigned tick, int ctrl, int def=-1); unsigned getControllerValueLifetime(unsigned tick, int ctrl); // returns the tick where this CC gets overridden by a new one // returns UINT_MAX for "never" void setClef(clefTypes i) { clefType = i; } clefTypes getClef() { return clefType; } DrumMap* drummap() { return _drummap; } const DrumMap* drummap() const { return _drummap; } int map_drum_in(int enote) { return drum_in_map[enote]; } void update_drum_in_map(); void init_drum_ordering(); void init_drummap() { init_drummap(false); } // function with argument in private // For drum tracks, updates the drum map and returns true if anything changed. // If doSignal is true, automatically emits SC_DRUM_MAP or sends audio message if audio is running and not idle. bool updateDrummap(int doSignal); //void workingDrumMapOperation(int index, bool updateDruminmap, const WorkingDrumMapEntry& item, PendingOperationList& ops); WorkingDrumMapPatchList* workingDrumMap() const { return _workingDrumMapPatchList; } void setWorkingDrumMap(WorkingDrumMapPatchList* list, bool isInstrumentMod); void modifyWorkingDrumMap(WorkingDrumMapList& list, bool isReset, bool includeDefault, bool isInstrumentMod, bool doWholeMap); //void modifyWorkingDrumMap(WorkingDrumMapPatchList& list, bool clear, bool isReset, bool isInstrumentMod); // Returns a map item with members filled from either the original or working map item, // depending on which Field flags are set. The returned map includes any requested // WorkingDrumMapEntry::OverrideType track or instrument overrides. void getMapItem(int patch, int index, DrumMap& dest_map, int overrideType) const; // Returns a map item with members filled from either the original or working map item, // depending on which Field flags are set. The returned map includes any requested // WorkingDrumMapEntry::OverrideType track or instrument overrides. // Same as getMapItem(), but determines patch at supplied tick. void getMapItemAt(int tick, int index, DrumMap& dest_map, int overrideType) const; // Returns OR'd WorkingDrumMapEntry::OverrideType flags indicating whether a map item's members, // given by 'fields' (OR'd WorkingDrumMapEntry::Fields), are either the original or working map item. // Here in MidiTrack the flags can be NoOverride, InstrumentOverride, and TrackOverride. See corresponding // function in MidiInstrument. If patch is -1 it uses the track's current patch (midi controller hwCtrlVal). int isWorkingMapItem(int index, int fields, int patch = -1) const; // Ensures there are NO duplicate enote fields in the final drum map array. // Returns true if anything changed. bool normalizeDrumMap(); // Ensures there are NO duplicate enote fields in the final drum map array for the given patch. // Returns true if anything changed. bool normalizeDrumMap(int patch); void set_drummap_ordering_tied_to_patch(bool); bool drummap_ordering_tied_to_patch() { return _drummap_ordering_tied_to_patch; } // Prints a handy debug table of drum map values and overrides etc. void dumpMap(); }; //--------------------------------------------------------- // AudioTrack // this track can hold audio automation data and can // hold tracktypes WAVE, AUDIO_GROUP, AUDIO_OUTPUT, // AUDIO_INPUT, AUDIO_SOFTSYNTH, AUDIO_AUX //--------------------------------------------------------- class AudioTrack : public Track { bool _haveData; // Whether we have data from a previous process call during current cycle. CtrlListList _controller; // Holds all controllers including internal, plugin and synth. ControlFifo _controlFifo; // For internal controllers like volume and pan. Plugins/synths have their own. CtrlRecList _recEvents; // recorded automation events unsigned long _controlPorts; Port* _controls; // For internal controllers like volume and pan. Plugins/synths have their own. double _curVolume; double _curVol1; double _curVol2; bool _prefader; // prefader metering AuxSendValueList _auxSend; void readAuxSend(Xml& xml); int recFileNumber; bool _sendMetronome; AutomationType _automationType; double _gain; void initBuffers(); void internal_assign(const Track&, int flags); void processTrackCtrls(unsigned pos, int trackChans, unsigned nframes, float** buffer); protected: // Cached audio data for all channels. If prefader is not on, the first two channels // have volume and pan applied if track is stereo, or the first channel has just // volume applied if track is mono. float** outBuffers; // Extra cached audio data. float** outBuffersExtraMix; // Just all zeros all the time, so we don't have to clear for silence. float* audioInSilenceBuf; // Just a place to connect all unused audio outputs. float* audioOutDummyBuf; // Internal temporary buffers for getData(). float** _dataBuffers; // Audio latency compensator. LatencyCompensator* _latencyComp; // These two are not the same as the number of track channels which is always either 1 (mono) or 2 (stereo): // Total number of output channels. int _totalOutChannels; // Total number of input channels. int _totalInChannels; Pipeline* _efxPipe; virtual bool getData(unsigned, int, unsigned, float**); SndFileR _recFile; // Exclusively for the recFile during bounce operations. long int _recFilePos; float _previousLatency; Fifo fifo; // fifo -> _recFile bool _processed; public: AudioTrack(TrackType t, int channels = 2); AudioTrack(const AudioTrack&, int flags); virtual ~AudioTrack(); // FIXME This public assign() method doesn't really 'assign' routes - // if routes are assigned in flags, it does not clear existing routes ! virtual void assign(const Track&, int flags); virtual AudioTrack* clone(int flags) const = 0; virtual Part* newPart(Part*p=0, bool clone=false); // Called from gui thread only. virtual bool setRecordFlag1(bool); // Called from audio thread only. virtual void setRecordFlag2(bool); bool prepareRecording(); // Same as setRecordFlag2 except it can automatically set the monitor flag // depending on the global config.monitorOnRecord. Called from audio thread only. // Returns whether the monitor was changed. virtual bool setRecordFlag2AndCheckMonitor(bool); bool processed() { return _processed; } void addController(CtrlList*); void removeController(int id); void swapControllerIDX(int idx1, int idx2); bool readProperties(Xml&, const QString&); void writeProperties(int, Xml&) const; void mapRackPluginsToControllers(); void showPendingPluginNativeGuis(); SndFileR recFile() const { return _recFile; } void setRecFile(SndFileR sf) { _recFile = sf; } CtrlListList* controller() { return &_controller; } // For setting/getting the _controls 'port' values. unsigned long parameters() const { return _controlPorts; } void setParam(unsigned long i, double val); double param(unsigned long i) const; virtual void setChannels(int n); virtual void setTotalOutChannels(int num); virtual int totalOutChannels() const { return _totalOutChannels; } virtual void setTotalInChannels(int num); virtual int totalInChannels() const { return _totalInChannels; } // Number of routable inputs/outputs for each Route::RouteType. virtual RouteCapabilitiesStruct routeCapabilities() const; // Number of required processing buffers. virtual int totalProcessBuffers() const { return (channels() == 1) ? 1 : totalOutChannels(); } virtual void setSolo(bool val); virtual void updateSoloStates(bool noDec); virtual void updateInternalSoloStates(); // Puts to the recording fifo. // This also performs adjustments for latency compensation before putting to the fifo. // Returns true on success. bool putFifo(int channels, unsigned long n, float** bp); // Transfers the recording fifo to _recFile. void record(); // Returns the recording fifo current count. int recordFifoCount() { return fifo.getCount(); } virtual void setMute(bool val); virtual void setOff(bool val); void setSendMetronome(bool val) { _sendMetronome = val; } bool sendMetronome() const { return _sendMetronome; } double volume() const; void setVolume(double val); double pan() const; void setPan(double val); double gain() const; void setGain(double val); bool prefader() const { return _prefader; } double auxSend(int idx) const; void setAuxSend(int idx, double v); void addAuxSend(int n); void setPrefader(bool val); Pipeline* efxPipe() { return _efxPipe; } void deleteAllEfxGuis(); void clearEfxList(); // Removes any existing plugin and inserts plugin into effects rack, and calls setupPlugin. void addPlugin(PluginI* plugin, int idx); // Assigns valid ID and track to plugin, and creates controllers for plugin. void setupPlugin(PluginI* plugin, int idx); double pluginCtrlVal(int ctlID) const; void setPluginCtrlVal(int param, double val); void readVolume(Xml& xml); virtual void preProcessAlways(); // Gathers this track's audio data and either copies or adds it to a supplied destination buffer. // If the per-channel 'addArray' is supplied, whether to copy or add each channel is given in the array, // otherwise it is given by the bulk 'add' flag. // The range of buffers in 'dstBuffer' given by 'dstStartChan' and 'availDstChannels' are filled with data. // If 'availDstChannels' is greater than 'requestedDstChannels', the excess buffers are filled with silence. // If 'requestedDstChannels' is greater than 'availDstChannels', copyData() acts AS IF 'requestedDstChannels' // was the real availDstChannels except it only fills up to 'availDstChannels'. This is to allow copyData() // to mix properly even when some routes are not available (ex. switching a track from stereo to mono, // which has an existing one-channel route on the right channel). // The 'srcStartChan' and 'srcChannels' give the range of channels to copy or add from this track. // If 'srcStartChan' is -1 it will be set to zero. If 'srcChannels' is -1`it will be set to this track's output channels. // The 'dstStartChan' can also be -1, but 'requestedDstChannels' and availDstChannels cannot. virtual void copyData(unsigned samplePos, int dstStartChan, int requestedDstChannels, int availDstChannels, int srcStartChan, int srcChannels, unsigned frames, float** dstBuffer, bool add = false, const bool* addArray = 0); virtual bool hasAuxSend() const { return false; } // Whether to use latency correction/compensation at all. // Simply depends on _latencyComp existence AND global configuration enableLatencyCorrection flag. bool useLatencyCorrection() const; // The contribution to latency by the track's own members (audio effect rack, etc). virtual float selfLatencyAudio(int channel) const; // The cached worst latency of all the channels in the track's effect rack. virtual float getWorstPluginLatencyAudio(); // The cached worst latency of all the contributions from the track's own members (audio effect rack, etc) // plus any port latency if applicable. virtual float getWorstSelfLatencyAudio(); virtual TrackLatencyInfo& getDominanceInfo(bool input); // Returns latency computations during each cycle. If the computations have already been done // this cycle, cached values are returned, otherwise they are computed, cached, then returned. virtual TrackLatencyInfo& getDominanceLatencyInfo(bool input); // The finalWorstLatency is the grand final worst-case latency, of any output track or open branch, // determined in the complete getDominanceLatencyInfo() scan. // The callerBranchLatency is the inherent branch latency of the calling track, or zero if calling from // the very top outside of the branch heads (outside of output tracks or open branches). // The callerBranchLatency is accumulated as setCorrectionLatencyInfo() is called on each track // in a branch of the graph. virtual TrackLatencyInfo& setCorrectionLatencyInfo(bool input, float finalWorstLatency, float callerBranchLatency = 0.0f); virtual TrackLatencyInfo& getLatencyInfo(bool input); // Used during latency compensation processing. When analyzing in 'reverse' this mechansim is // needed only to equalize the timing of all the AudioOutput tracks. // It is applied as a direct offset in the latency delay compensator in getData(). virtual void setLatencyCompWriteOffset(float worstCase); virtual bool isLatencyInputTerminal(); virtual bool isLatencyOutputTerminal(); // automation virtual AutomationType automationType() const { return _automationType; } virtual void setAutomationType(AutomationType t); CtrlRecList* recEvents() { return &_recEvents; } bool addScheduledControlEvent(int track_ctrl_id, double val, unsigned frame); // return true if event cannot be delivered void enableController(int track_ctrl_id, bool en); bool controllerEnabled(int track_ctrl_id) const; // Enable all track and plugin controllers, and synth controllers if applicable. void enableAllControllers(); void recordAutomation(int n, double v); void startAutoRecord(int, double); void stopAutoRecord(int, double); void setControllerMode(int, CtrlList::Mode m); void clearControllerEvents(int); void seekPrevACEvent(int); void seekNextACEvent(int); void eraseACEvent(int, int); void eraseRangeACEvents(int, int, int); void addACEvent(int, int, double); void changeACEvent(int id, int frame, int newframe, double newval); AuxSendValueList *getAuxSendValueList() { return &_auxSend; } }; //--------------------------------------------------------- // AudioInput //--------------------------------------------------------- class AudioInput : public AudioTrack { void* jackPorts[MAX_CHANNELS]; bool getData(unsigned, int, unsigned, float**); static bool _isVisible; void internal_assign(const Track& t, int flags); public: AudioInput(); AudioInput(const AudioInput&, int flags); virtual ~AudioInput(); float selfLatencyAudio(int channel) const; // The cached worst contribution to latency by any ports (for ex. Jack ports of audio input/output tracks). float getWorstPortLatencyAudio(); // Audio Input tracks have no correction available. They ALWAYS dominate any parallel branches, if they are not 'off'. bool canDominateOutputLatency() const; // FIXME This public assign() method doesn't really 'assign' routes - // if routes are assigned in flags, it does not clear existing routes ! // For input/output tracks we need to disconnect the routes from Jack. void assign(const Track&, int flags); AudioInput* clone(int flags) const { return new AudioInput(*this, flags); } void read(Xml&); void write(int, Xml&) const; // Register one or all input ports. If idx = -1 it registers all ports. // Returns true if ANY of the port(s) were successfully registered. bool registerPorts(int idx = -1); void setName(const QString& s); void* jackPort(int channel) { return jackPorts[channel]; } void setJackPort(int channel, void*p) { jackPorts[channel] = p; } bool hasAuxSend() const { return true; } // Number of routable inputs/outputs for each Route::RouteType. RouteCapabilitiesStruct routeCapabilities() const; static void setVisible(bool t) { _isVisible = t; } int height() const; static bool visible() { return _isVisible; } }; //--------------------------------------------------------- // AudioOutput //--------------------------------------------------------- class AudioOutput : public AudioTrack { void* jackPorts[MusECore::MAX_CHANNELS]; float* buffer[MusECore::MAX_CHANNELS]; float* buffer1[MusECore::MAX_CHANNELS]; unsigned long _nframes; // Audio latency compensator, for compensating output signals // according to any differing channel port latencies. LatencyCompensator* _outputLatencyComp; static bool _isVisible; void internal_assign(const Track& t, int flags); public: AudioOutput(); AudioOutput(const AudioOutput&, int flags); virtual ~AudioOutput(); float selfLatencyAudio(int channel) const; void setChannels(int n); // The cached worst contribution to latency by any ports (for ex. Jack ports of audio input/output tracks). float getWorstPortLatencyAudio(); // Audio output tracks can allow a branch to dominate if they are an end-point and the branch can dominate. inline bool canDominateEndPointLatency() const { return true; } // Audio Output is considered a termination point. bool isLatencyInputTerminal(); bool isLatencyOutputTerminal(); void applyOutputLatencyComp(unsigned nframes); // FIXME This public assign() method doesn't really 'assign' routes - // if routes are assigned in flags, it does not clear existing routes ! // For input/output tracks we need to disconnect the routes from Jack. void assign(const Track&, int flags); AudioOutput* clone(int flags) const { return new AudioOutput(*this, flags); } void read(Xml&); void write(int, Xml&) const; // Register one or all output ports. If idx = -1 it registers all ports. // Returns true if ANY of the port(s) were successfully registered. bool registerPorts(int idx = -1); void setName(const QString& s); void* jackPort(int channel) { return jackPorts[channel]; } void setJackPort(int channel, void*p) { jackPorts[channel] = p; } // Number of routable inputs/outputs for each Route::RouteType. RouteCapabilitiesStruct routeCapabilities() const; void processInit(unsigned); void process(unsigned pos, unsigned offset, unsigned); void processWrite(); void silence(unsigned); bool canRecord() const { return true; } static void setVisible(bool t) { _isVisible = t; } static bool visible() { return _isVisible; } int height() const; }; //--------------------------------------------------------- // AudioGroup //--------------------------------------------------------- class AudioGroup : public AudioTrack { static bool _isVisible; public: AudioGroup() : AudioTrack(AUDIO_GROUP) { } AudioGroup(const AudioGroup& t, int flags) : AudioTrack(t, flags) { } AudioGroup* clone(int flags) const { return new AudioGroup(*this, flags); } virtual void read(Xml&); virtual void write(int, Xml&) const; virtual bool hasAuxSend() const { return true; } static void setVisible(bool t) { _isVisible = t; } virtual int height() const; static bool visible() { return _isVisible; } }; //--------------------------------------------------------- // AudioAux //--------------------------------------------------------- class AudioAux : public AudioTrack { float* buffer[MusECore::MAX_CHANNELS]; static bool _isVisible; int _index; public: AudioAux(); AudioAux(const AudioAux& t, int flags); AudioAux* clone(int flags) const { return new AudioAux(*this, flags); } ~AudioAux(); virtual void read(Xml&); virtual void write(int, Xml&) const; virtual bool getData(unsigned, int, unsigned, float**); virtual void setChannels(int n); // Number of routable inputs/outputs for each Route::RouteType. virtual RouteCapabilitiesStruct routeCapabilities() const { RouteCapabilitiesStruct s = AudioTrack::routeCapabilities(); s._trackChannels._inRoutable = false; s._trackChannels._inChannels = 0; return s; } float** sendBuffer() { return buffer; } static void setVisible(bool t) { _isVisible = t; } virtual int height() const; static bool visible() { return _isVisible; } virtual int index() { return _index; } }; //--------------------------------------------------------- // WaveTrack //--------------------------------------------------------- class WaveTrack : public AudioTrack { Fifo _prefetchFifo; // prefetch Fifo // Each wavetrack has a separate prefetch position stamp // so that consumers can retard or advance the stream and // the prefetch can pump as much buffers as required while // keeping track of the last buffer position stamp. unsigned _prefetchWritePos; static bool _isVisible; void internal_assign(const Track&, int flags); // Writes data from connected input routes to the track's latency compensator. // It uses buffer for temporary storage. bool getInputData(unsigned pos, int channels, unsigned nframes, bool* usedInChannelArray, float** buffer); // Return false if no data or error. bool getPrefetchData(sf_count_t framePos, int dstChannels, sf_count_t nframe, float** bp, bool do_overwrite); public: WaveTrack(); WaveTrack(const WaveTrack& wt, int flags); // FIXME This public assign() method doesn't really 'assign' routes - // if routes are assigned in flags, it does not clear existing routes ! virtual void assign(const Track&, int flags); virtual WaveTrack* clone(int flags) const { return new WaveTrack(*this, flags); } virtual Part* newPart(Part*p=0, bool clone=false); // Returns true if any event in any part was opened. Does not operate on the part's clones, if any. bool openAllParts(); // Returns true if any event in any part was closed. Does not operate on the part's clones, if any. bool closeAllParts(); virtual void read(Xml&); virtual void write(int, Xml&) const; // Called from prefetch thread: // If overwrite is true, copies the data. If false, adds the data. virtual void fetchData(unsigned pos, unsigned frames, float** bp, bool doSeek, bool overwrite, int latency_correction = 0); virtual void seekData(sf_count_t pos); virtual bool getData(unsigned, int ch, unsigned, float** bp); // Depending on the Monitor setting, Wave Tracks can have available correction. // If unmonitored, they will never dominate parallel branches. bool canDominateOutputLatency() const; bool canCorrectOutputLatency() const; void clearPrefetchFifo(); Fifo* prefetchFifo() { return &_prefetchFifo; } virtual void prefetchAudio(sf_count_t writePos, sf_count_t frames); // For prefetch thread use only. inline unsigned prefetchWritePos() const { return _prefetchWritePos; } inline void setPrefetchWritePos(unsigned p) { _prefetchWritePos = p; } virtual void setChannels(int n); virtual bool hasAuxSend() const { return true; } bool canEnableRecord() const; virtual bool canRecord() const { return true; } virtual bool canRecordMonitor() const { return true; } static void setVisible(bool t) { _isVisible = t; } virtual int height() const; static bool visible() { return _isVisible; } }; //--------------------------------------------------------- // TrackList //--------------------------------------------------------- template class tracklist : public std::vector { typedef std::vector vlist; public: class iterator : public vlist::iterator { public: iterator() : vlist::iterator() {} iterator(vlist::iterator i) : vlist::iterator(i) {} T operator*() { return (T)(**((vlist::iterator*)this)); } iterator operator++(int) { return iterator ((*(vlist::iterator*)this).operator++(0)); } iterator& operator++() { return (iterator&) ((*(vlist::iterator*)this).operator++()); } }; class const_iterator : public vlist::const_iterator { public: const_iterator() : vlist::const_iterator() {} const_iterator(vlist::const_iterator i) : vlist::const_iterator(i) {} const_iterator(vlist::iterator i) : vlist::const_iterator(i) {} const T operator*() const { return (T)(**((vlist::const_iterator*)this)); } }; class reverse_iterator : public vlist::reverse_iterator { public: reverse_iterator() : vlist::reverse_iterator() {} reverse_iterator(vlist::reverse_iterator i) : vlist::reverse_iterator(i) {} T operator*() { return (T)(**((vlist::reverse_iterator*)this)); } }; tracklist() : vlist() {} virtual ~tracklist() {} void push_back(T v) { vlist::push_back(v); } iterator begin() { return vlist::begin(); } iterator end() { return vlist::end(); } const_iterator begin() const { return vlist::cbegin(); } const_iterator end() const { return vlist::cend(); } const_iterator cbegin() const { return vlist::cbegin(); } const_iterator cend() const { return vlist::cend(); } reverse_iterator rbegin() { return vlist::rbegin(); } reverse_iterator rend() { return vlist::rend(); } T& back() const { return (T&)(vlist::back()); } T& front() const { return (T&)(vlist::front()); } iterator find(const Track* t) { return std::find(begin(), end(), t); } const_iterator find(const Track* t) const { return std::find(cbegin(), cend(), t); } bool contains(const Track* t) const { return std::find(cbegin(), cend(), t) != cend(); } int index(const Track* t) const { int n = 0; for (vlist::const_iterator i = cbegin(); i != cend(); ++i, ++n) { if (*i == t) return n; } return -1; } T index(int k) const { if (k < 0 || k >= (int)size()) return nullptr; return (*this)[k]; } T findSerial(int sn) const { if (sn < 0) return nullptr; for (vlist::const_iterator i = cbegin(); i != cend(); ++i) { if ((*i)->serial() == sn) { return *i; } } return nullptr; } int indexOfSerial(int sn) const { if (sn < 0) return -1; int n = 0; for (vlist::const_iterator i = cbegin(); i != cend(); ++i, ++n) { if ((*i)->serial() == sn) { return n; } } return -1; } iterator index2iterator(int k) { if (k < 0 || k >= (int)size()) return end(); return begin() + k; } void erase(Track* t) { vlist::erase(find(t)); } void clearDelete() { for (vlist::iterator i = begin(); i != end(); ++i) delete *i; vlist::clear(); } void erase(vlist::const_iterator i) { vlist::erase(i); } void replace(Track* ot, Track* nt) { for (vlist::iterator i = begin(); i != end(); ++i) { if (*i == ot) { *i = nt; return; } } } // Returns the number of selected tracks in this list. int countSelected() const { int c = 0; for (vlist::const_iterator i = cbegin(); i != cend(); ++i) { if ((*i)->selected()) { ++c; } } return c; } // Returns the current (most recent) selected track, or null if none. // It returns the track with the highest _selectionOrder. // This helps with multi-selection common-property editing. T currentSelection() const { T cur = 0; int c = 0; int so; for (vlist::const_iterator i = cbegin(); i != cend(); ++i) { T t = *i; so = t->selectionOrder(); if (t->selected() && so >= c) { cur = t; c = so; } } return cur; } // Selects or unselects all tracks in this list. void selectAll(bool select) { for (vlist::iterator i = begin(); i != end(); ++i) { (*i)->setSelected(select); } } }; typedef tracklist TrackList; typedef TrackList::iterator iTrack; typedef TrackList::const_iterator ciTrack; typedef tracklist::iterator iMidiTrack; typedef tracklist::const_iterator ciMidiTrack; typedef tracklist MidiTrackList; typedef tracklist::iterator iWaveTrack; typedef tracklist::const_iterator ciWaveTrack; typedef tracklist WaveTrackList; typedef tracklist::iterator iAudioInput; typedef tracklist::const_iterator ciAudioInput; typedef tracklist InputList; typedef tracklist::iterator iAudioOutput; typedef tracklist::const_iterator ciAudioOutput; typedef tracklist OutputList; typedef tracklist::iterator iAudioGroup; typedef tracklist::const_iterator ciAudioGroup; typedef tracklist GroupList; typedef tracklist::iterator iAudioAux; typedef tracklist::const_iterator ciAudioAux; typedef tracklist AuxList; // NOTE: There is also a tracklist for SynthI* found in synth.h extern void addPortCtrlEvents(MidiTrack* t, bool drum_ctls = true, bool non_drum_ctls = true); extern void removePortCtrlEvents(MidiTrack* t, bool drum_ctls = true, bool non_drum_ctls = true); } // namespace MusECore #endif muse-4.0.0/src/muse/transport.cpp000066400000000000000000001057231404105311000167610ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: transport.cpp,v 1.8.2.3 2009/07/01 10:39:42 spamatica Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "song.h" #include "transport.h" #include "globals.h" #include "icons.h" #include "sync.h" #include "shortcuts.h" #include "gconfig.h" #include "app.h" #include "audio.h" #include "globaldefs.h" #include "operations.h" #include "tempo.h" #include "audiodev.h" #include "undo.h" // Forwards from header: #include #include #include #include #include #include #include #include "sig.h" #include "pos.h" #include "posedit.h" #include "doublelabel.h" #include "siglabel.h" #include "pixmap_button.h" #include "tempolabel.h" #include "sigedit.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_TRANSPORT(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { //--------------------------------------------------------- // toolButton //--------------------------------------------------------- // Unused since switch to svg. // static QToolButton* newButton(const QPixmap* pm, const QString& tt, // bool toggle=false, QWidget* parent=0) // { // QToolButton* button = new QToolButton(parent); // button->setFixedHeight(25); // button->setIcon(QIcon(*pm)); // button->setCheckable(toggle); // button->setToolTip(tt); // button->setFocusPolicy(Qt::NoFocus); // return button; // } static QToolButton* newButton(const QIcon* icon, const QString& tt, bool toggle=false, QWidget* parent=nullptr) { QToolButton* button = new QToolButton(parent); button->setFixedHeight(25); button->setIcon(*icon); button->setCheckable(toggle); button->setToolTip(tt); button->setFocusPolicy(Qt::NoFocus); return button; } //--------------------------------------------------------- // Handle // allows moving a root-window with the mouse //--------------------------------------------------------- Handle::Handle(QWidget* r, QWidget* parent) : QWidget(parent) { rootWin = r; setFixedWidth(20); setCursor(Qt::PointingHandCursor); this->setStyleSheet("background-color:" + MusEGlobal::config.transportHandleColor.name()); } //--------------------------------------------------------- // mouseMoveEvent //--------------------------------------------------------- void Handle::mouseMoveEvent(QMouseEvent* ev) { rootWin->move(ev->globalX()-dx, ev->globalY() - dy); } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void Handle::mousePressEvent(QMouseEvent* ev) { rootWin->raise(); dx = ev->globalX() - rootWin->x(); dy = ev->globalY() - rootWin->y(); } //--------------------------------------------------------- // TempoSig // Widget for Tempo + Signature //--------------------------------------------------------- TempoSig::TempoSig(QWidget* parent) : QWidget(parent) { //_curVal = 0.0; // _curVal = -1.0; // _extern = false; QBoxLayout* vb1 = new QVBoxLayout; vb1->setContentsMargins(0, 0, 0, 0); vb1->setSpacing(0); QBoxLayout* vb2 = new QVBoxLayout; vb2->setContentsMargins(0, 0, 0, 0); vb2->setSpacing(0); QBoxLayout* hb1 = new QHBoxLayout; hb1->setContentsMargins(0, 0, 0, 0); hb1->setSpacing(0); QFrame* f = new QFrame; f->setFrameStyle(QFrame::Panel | QFrame::Sunken); f->setLineWidth(1); _masterButton = new IconButton(masterTrackOnSVGIcon, masterTrackOffSVGIcon, 0, 0, false, true); _masterButton->setContentsMargins(0, 0, 0, 0); _masterButton->setCheckable(true); _masterButton->setToolTip(tr("Use mastertrack tempo")); _masterButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); _masterButton->setFocusPolicy(Qt::NoFocus); connect(_masterButton, SIGNAL(toggled(bool)), SLOT(masterToggled(bool))); hb1->addWidget(_masterButton); l3 = new QLabel(tr("Tempo/Sig")); // l3->setFont(MusEGlobal::config.fonts[2]); vb2->addWidget(l3); l1 = new TempoEdit(); l1->setContentsMargins(0, 0, 0, 0); l1->setFocusPolicy(Qt::StrongFocus); l1->setToolTip(tr("Mastertrack tempo at current position, or fixed tempo")); hb1->addWidget(l1); vb2->addLayout(hb1); l2 = new SigEdit(this); l2->setContentsMargins(0, 0, 0, 0); l2->setFocusPolicy(Qt::StrongFocus); l2->setToolTip(tr("Time signature at current position")); vb2->addWidget(l2); f->setLayout(vb2); vb1->addWidget(f); l1->setAlignment(Qt::AlignCenter); l1->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); l2->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); l3->setAlignment(Qt::AlignCenter); l3->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); connect(l1, SIGNAL(tempoChanged(double)), SLOT(newTempo(double))); connect(l2, SIGNAL(valueChanged(const MusECore::TimeSignature&)), SIGNAL(sigChanged(const MusECore::TimeSignature&))); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); connect(l1, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(l1, SIGNAL(escapePressed()), SIGNAL(escapePressed())); connect(l2, SIGNAL(returnPressed()), SIGNAL(returnPressed())); connect(l2, SIGNAL(escapePressed()), SIGNAL(escapePressed())); this->setLayout(vb1); } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void TempoSig::configChanged() { // l3->setFont(MusEGlobal::config.fonts[2]); } //--------------------------------------------------------- // masterToggled //--------------------------------------------------------- void TempoSig::masterToggled(bool val) { emit masterTrackChanged(val); } bool TempoSig::masterTrack() const { return _masterButton->isChecked(); } void TempoSig::setMasterTrack(bool on) { _masterButton->blockSignals(true); _masterButton->setChecked(on); _masterButton->blockSignals(false); } //--------------------------------------------------------- // setExternalMode //--------------------------------------------------------- void TempoSig::setExternalMode(bool on) { l1->setExternalMode(on); } //--------------------------------------------------------- // newTempo //--------------------------------------------------------- void TempoSig::newTempo(double t) { emit tempoChanged(int ((1000000.0 * 60.0) / t)); } //--------------------------------------------------------- // setTempo //--------------------------------------------------------- void TempoSig::setTempo(int tempo) { l1->setValue((1000000.0 * 60.0)/tempo); } //--------------------------------------------------------- // setTimesig //--------------------------------------------------------- void TempoSig::setTimesig(int a, int b) { l2->setValue(MusECore::TimeSignature(a, b)); } //--------------------------------------------------------- // Transport //--------------------------------------------------------- Transport::Transport(QWidget* parent, const char* name) : QWidget(parent, Qt::Window | Qt::FramelessWindowHint ) // Possibly also Qt::X11BypassWindowManagerHint { setObjectName(name); setWindowFlags(Qt::Window | Qt::FramelessWindowHint); setWindowTitle(QString("Muse: Transport")); setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); if (MusEGlobal::config.keepTransportWindowOnTop) setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); QHBoxLayout* hbox = new QHBoxLayout; hbox->setContentsMargins(2, 2, 2, 2); lefthandle = new Handle(this); hbox->addWidget(lefthandle); //----------------------------------------------------- // Record & Cycle Mode //----------------------------------------------------- QVBoxLayout *box1 = new QVBoxLayout; recMode = new QComboBox; recMode->setFocusPolicy(Qt::NoFocus); recMode->insertItem(MusECore::Song::REC_OVERDUP, tr("Overdub")); recMode->insertItem(MusECore::Song::REC_REPLACE, tr("Replace")); recMode->setCurrentIndex(MusEGlobal::song->recMode()); box1->addWidget(recMode); l2 = new QLabel(tr("Rec Mode")); // l2->setFont(MusEGlobal::config.fonts[2]); l2->setAlignment(Qt::AlignCenter); connect(recMode, SIGNAL(activated(int)), SLOT(setRecMode(int))); connect(MusEGlobal::song, SIGNAL(recModeChanged(int)), SLOT(setRecMode(int))); box1->addWidget(l2); cycleMode = new QComboBox; cycleMode->setFocusPolicy(Qt::NoFocus); cycleMode->insertItem(MusECore::Song::CYCLE_NORMAL, tr("Normal")); cycleMode->insertItem(MusECore::Song::CYCLE_MIX, tr("Mix")); cycleMode->insertItem(MusECore::Song::CYCLE_REPLACE, tr("Replace")); cycleMode->setCurrentIndex(MusEGlobal::song->cycleMode()); box1->addWidget(cycleMode); l3 = new QLabel(tr("Cycle Rec")); // l3->setFont(MusEGlobal::config.fonts[2]); l3->setAlignment(Qt::AlignCenter); connect(cycleMode, SIGNAL(activated(int)), SLOT(setCycleMode(int))); connect(MusEGlobal::song, SIGNAL(cycleModeChanged(int)), SLOT(setCycleMode(int))); box1->addWidget(l3); box1->setSpacing(0); hbox->addLayout(box1); //----------------------------------------------------- // loop flags //----------------------------------------------------- QVBoxLayout *button2 = new QVBoxLayout; button2->setSpacing(0); QToolButton* b1 = newButton(punchinSVGIcon, tr("Punch in"), true); QToolButton* b2 = newButton(loopSVGIcon, tr("Loop"), true); b2->setShortcut(shortcuts[SHRT_TOGGLE_LOOP].key); QToolButton* b3 = newButton(punchoutSVGIcon, tr("Punch out"), true); button2->addWidget(b1); button2->addWidget(b2); button2->addWidget(b3); b1->setToolTip(tr("Punch in")); b2->setToolTip(tr("Loop")); b3->setToolTip(tr("Punch out")); b1->setWhatsThis(tr("Punch in")); b2->setWhatsThis(tr("Loop")); b3->setWhatsThis(tr("Punch out")); connect(b1, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setPunchin(bool))); connect(b2, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setLoop(bool))); connect(b3, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setPunchout(bool))); b1->setChecked(MusEGlobal::song->punchin()); b2->setChecked(MusEGlobal::song->loop()); b3->setChecked(MusEGlobal::song->punchout()); connect(MusEGlobal::song, SIGNAL(punchinChanged(bool)), b1, SLOT(setChecked(bool))); connect(MusEGlobal::song, SIGNAL(punchoutChanged(bool)), b3, SLOT(setChecked(bool))); connect(MusEGlobal::song, SIGNAL(loopChanged(bool)), b2, SLOT(setChecked(bool))); hbox->addLayout(button2); //----------------------------------------------------- // left right mark //----------------------------------------------------- QVBoxLayout *marken = new QVBoxLayout; marken->setSpacing(0); marken->setContentsMargins(0, 0, 0, 0); tl1 = new PosEdit(nullptr); tl1->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); tl1->setFocusPolicy(Qt::NoFocus); marken->addWidget(tl1); l5 = new QLabel(tr("Left Mark")); // l5->setFont(MusEGlobal::config.fonts[2]); l5->setAlignment(Qt::AlignCenter); marken->addWidget(l5); tl2 = new PosEdit(nullptr); tl2->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); marken->addWidget(tl2); tl2->setFocusPolicy(Qt::NoFocus); l6 = new QLabel(tr("Right Mark")); // l6->setFont(MusEGlobal::config.fonts[2]); l6->setAlignment(Qt::AlignCenter); marken->addWidget(l6); hbox->addLayout(marken); //----------------------------------------------------- // Transport Buttons //----------------------------------------------------- QVBoxLayout *box4 = new QVBoxLayout; box4->setSpacing(0); box4->setContentsMargins(0, 0, 0, 0); QHBoxLayout *hbox1 = new QHBoxLayout; hbox1->setContentsMargins(0, 0, 0, 0); time1 = new PosEdit(nullptr); time2 = new PosEdit(nullptr); time2->setSmpte(true); time1->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); time2->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); time1->setFocusPolicy(Qt::NoFocus); time2->setFocusPolicy(Qt::NoFocus); hbox1->addWidget(time1); hbox1->addWidget(time2); box4->addLayout(hbox1); slider = new QSlider; slider->setMinimum(0); slider->setMaximum(200000); slider->setPageStep(1000); slider->setValue(0); slider->setOrientation(Qt::Horizontal); slider->setFocusPolicy(Qt::NoFocus); box4->addWidget(slider); tb = new QHBoxLayout; tb->setSpacing(0); buttons[0] = newButton(rewindToStartSVGIcon, tr("Rewind to Start")); buttons[0]->setWhatsThis(tr("Click this button to rewind to start position")); buttons[1] = newButton(rewindSVGIcon, tr("Rewind")); buttons[1]->setAutoRepeat(true); buttons[1]->setWhatsThis(tr("Click this button to rewind")); buttons[2] = newButton(fastForwardSVGIcon, tr("Forward")); buttons[2]->setAutoRepeat(true); buttons[2]->setWhatsThis(tr("Click this button to forward current play position")); buttons[3] = newButton(stopSVGIcon, tr("Stop"), true); buttons[3]->setChecked(true); // set STOP buttons[3]->setWhatsThis(tr("Click this button to stop playback")); buttons[4] = newButton(playSVGIcon, tr("Play"), true); buttons[4]->setWhatsThis(tr("Click this button to start playback")); buttons[5] = newButton(recMasterSVGIcon, tr("Record"), true); buttons[5]->setWhatsThis(tr("Click this button to enable recording")); for (int i = 0; i < 6; ++i) { buttons[i]->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); tb->addWidget(buttons[i]); } connect(buttons[3], SIGNAL(toggled(bool)), SLOT(stopToggled(bool))); connect(buttons[4], SIGNAL(toggled(bool)), SLOT(playToggled(bool))); connect(buttons[5], SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setRecord(bool))); connect(MusEGlobal::song, SIGNAL(recordChanged(bool)), SLOT(setRecord(bool))); connect(buttons[0], SIGNAL(clicked()), MusEGlobal::song, SLOT(rewindStart())); connect(buttons[1], SIGNAL(clicked()), MusEGlobal::song, SLOT(rewind())); connect(buttons[2], SIGNAL(clicked()), MusEGlobal::song, SLOT(forward())); box4->addLayout(tb); hbox->addLayout(box4); //----------------------------------------------------- // AQ - Click - Sync //----------------------------------------------------- QVBoxLayout *button1 = new QVBoxLayout; button1->setContentsMargins(0, 0, 0, 0); button1->setSpacing(0); clickButton = new IconButton(metronomeOnSVGIcon, metronomeOffSVGIcon, 0, 0, false, true); clickButton->setContentsMargins(0, 0, 0, 0); clickButton->setCheckable(true); clickButton->setToolTip(tr("Metronome on/off")); clickButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); syncButton = new IconButton(externSyncOnSVGIcon, externSyncOffSVGIcon, 0, 0, false, true); syncButton->setContentsMargins(0, 0, 0, 0); syncButton->setCheckable(true); syncButton->setToolTip(tr("External sync on/off")); syncButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); jackTransportButton = new IconButton(jackTransportOnSVGIcon, jackTransportOffSVGIcon, 0, 0, false, true); jackTransportButton->setContentsMargins(0, 0, 0, 0); jackTransportButton->setCheckable(true); jackTransportButton->setToolTip(tr("Jack Transport on/off")); jackTransportButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); timebaseMasterButton = new IconButton(timebaseMasterOnSVGIcon, timebaseMasterOffSVGIcon, 0, 0, false, true); timebaseMasterButton->setContentsMargins(0, 0, 0, 0); timebaseMasterButton->setCheckable(true); timebaseMasterButton->setToolTip( tr("On: Timebase master\nOff: Not master\nFlash: Waiting. Another client is master. Click to force.")); timebaseMasterButton->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); clickButton->setChecked(MusEGlobal::song->click()); syncButton->setChecked(MusEGlobal::extSyncFlag); jackTransportButton->setChecked(MusEGlobal::config.useJackTransport); timebaseMasterButton->setChecked(MusEGlobal::timebaseMasterState); clickButton->setFocusPolicy(Qt::NoFocus); syncButton->setFocusPolicy(Qt::NoFocus); jackTransportButton->setFocusPolicy(Qt::NoFocus); timebaseMasterButton->setFocusPolicy(Qt::NoFocus); // NOTE: Can't use this. Audio device does not exist at this point. //jackTransportButton->setEnabled(MusEGlobal::audioDevice && MusEGlobal::audioDevice->hasOwnTransport()); //timebaseMasterButton->setEnabled(MusEGlobal::audioDevice && // MusEGlobal::audioDevice->hasOwnTransport() && // MusEGlobal::audioDevice->hasTimebaseMaster() && // MusEGlobal::useJackTransport); button1->addStretch(); button1->addWidget(clickButton); button1->addWidget(syncButton); button1->addWidget(jackTransportButton); button1->addWidget(timebaseMasterButton); button1->addStretch(); connect(clickButton, SIGNAL(toggled(bool)), MusEGlobal::song, SLOT(setClick(bool))); connect(syncButton, SIGNAL(toggled(bool)), SLOT(extSyncClicked(bool))); connect(jackTransportButton, SIGNAL(toggled(bool)), SLOT(useJackTransportClicked(bool))); connect(timebaseMasterButton, &IconButton::toggled, [this](bool v) { timebaseMasterClicked(v); } ); _timebaseMasterBlinkConnection = connect(MusEGlobal::muse, &MusE::blinkTimerToggled, [this](bool v) { timebaseMasterButton->setBlinkPhase(v); } ); connect(MusEGlobal::song, SIGNAL(clickChanged(bool)), this, SLOT(setClickFlag(bool))); hbox->addLayout(button1); //----------------------------------------------------- // Tempo/Sig //----------------------------------------------------- QVBoxLayout *box5 = new QVBoxLayout; box5->setSpacing(0); box5->setContentsMargins(0, 0, 0, 0); tempo = new TempoSig; tempo->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); tempo->setFocusPolicy(Qt::NoFocus); box5->addStretch(); box5->addWidget(tempo); box5->addStretch(); hbox->addLayout(box5); //----------------------------------------------------- connect(tl1, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(lposChanged(const MusECore::Pos&))); connect(tl2, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(rposChanged(const MusECore::Pos&))); connect(time1, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(cposChanged(const MusECore::Pos&))); connect(time2, SIGNAL(valueChanged(const MusECore::Pos&)), SLOT(cposChanged(const MusECore::Pos&))); connect(slider,SIGNAL(valueChanged(int)), SLOT(cposChanged(int))); connect(MusEGlobal::song, SIGNAL(posChanged(int, unsigned, bool)), SLOT(setPos(int, unsigned, bool))); connect(tempo, SIGNAL(tempoChanged(int)), MusEGlobal::song, SLOT(setTempo(int))); connect(tempo, SIGNAL(sigChanged(const MusECore::TimeSignature&)), SLOT(sigChange(const MusECore::TimeSignature&))); connect(tempo, SIGNAL(masterTrackChanged(bool)), MusEGlobal::song, SLOT(setMasterFlag(bool))); connect(tempo, SIGNAL(escapePressed()), SLOT(setFocus())); connect(tempo, SIGNAL(returnPressed()), SLOT(setFocus())); connect(MusEGlobal::song, SIGNAL(playChanged(bool)), SLOT(setPlay(bool))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), this, SLOT(songChanged(MusECore::SongChangedStruct_t))); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); this->setLayout(hbox); righthandle = new Handle(this); hbox->addWidget(righthandle); songChanged(SC_EVERYTHING); syncChanged(MusEGlobal::extSyncFlag); } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void Transport::configChanged() { // l2->setFont(MusEGlobal::config.fonts[2]); // l3->setFont(MusEGlobal::config.fonts[2]); // l5->setFont(MusEGlobal::config.fonts[2]); // l6->setFont(MusEGlobal::config.fonts[2]); QPalette pal; pal.setColor(lefthandle->backgroundRole(), MusEGlobal::config.transportHandleColor); lefthandle->setPalette(pal); righthandle->setPalette(pal); const bool has_master = MusEGlobal::audioDevice && MusEGlobal::audioDevice->hasTimebaseMaster(); jackTransportButton->setEnabled(has_master); timebaseMasterButton->setEnabled(has_master && MusEGlobal::config.useJackTransport); } //--------------------------------------------------------- // setTempo //--------------------------------------------------------- void Transport::setTempo(int t) { tempo->setTempo(t); blockSignals(true); // Make sure positional controls are updated unsigned v = MusEGlobal::song->cpos(); time2->setValue(v); // time2 is SMPTE, it only need tempo updates. blockSignals(false); } //--------------------------------------------------------- // setHandleColor //--------------------------------------------------------- void Transport::setHandleColor(QColor c) { QPalette pal; pal.setColor(lefthandle->backgroundRole(), c); lefthandle->setPalette(pal); righthandle->setPalette(pal); } //--------------------------------------------------------- // setTimesig //--------------------------------------------------------- void Transport::setTimesig(int z, int n) { blockSignals(true); tempo->setTimesig(z, n); // Make sure positional controls are updated unsigned v = MusEGlobal::song->cpos(); time1->setValue(v); // time2 is SMPTE. It only need tempo updates. v = MusEGlobal::song->lpos(); tl1->setValue(v); v = MusEGlobal::song->rpos(); tl2->setValue(v); blockSignals(false); } //--------------------------------------------------------- // setPos //--------------------------------------------------------- void Transport::setPos(int idx, unsigned v, bool) { switch (idx) { case 0: time1->setValue(v); time2->setValue(v); if((unsigned) slider->value() != v) { slider->blockSignals(true); slider->setValue(v); slider->blockSignals(false); } if (!MusEGlobal::extSyncFlag) setTempo(MusEGlobal::tempomap.tempo(v)); { int z, n; MusEGlobal::sigmap.timesig(v, z, n); setTimesig(z, n); } break; case 1: tl1->setValue(v); break; case 2: tl2->setValue(v); break; } } //--------------------------------------------------------- // cposChanged //--------------------------------------------------------- void Transport::cposChanged(int tick) { MusEGlobal::song->setPos(MusECore::Song::CPOS, tick); } //--------------------------------------------------------- // cposChanged //--------------------------------------------------------- void Transport::cposChanged(const MusECore::Pos& pos) { MusEGlobal::song->setPos(MusECore::Song::CPOS, pos.tick()); } //--------------------------------------------------------- // lposChanged //--------------------------------------------------------- void Transport::lposChanged(const MusECore::Pos& pos) { MusEGlobal::song->setPos(MusECore::Song::LPOS, pos.tick()); } //--------------------------------------------------------- // rposChanged //--------------------------------------------------------- void Transport::rposChanged(const MusECore::Pos& pos) { MusEGlobal::song->setPos(MusECore::Song::RPOS, pos.tick()); } //--------------------------------------------------------- // setRecord //--------------------------------------------------------- void Transport::setRecord(bool flag) { buttons[5]->blockSignals(true); buttons[5]->setChecked(flag); buttons[5]->blockSignals(false); } //--------------------------------------------------------- // setPlay //--------------------------------------------------------- void Transport::setPlay(bool f) { buttons[3]->blockSignals(true); buttons[4]->blockSignals(true); buttons[3]->setChecked(!f); buttons[4]->setChecked(f); buttons[3]->blockSignals(false); buttons[4]->blockSignals(false); } //--------------------------------------------------------- // setMasterFlag //--------------------------------------------------------- void Transport::setMasterFlag(bool f) { tempo->setMasterTrack(f); } //--------------------------------------------------------- // setClickFlag //--------------------------------------------------------- void Transport::setClickFlag(bool f) { clickButton->blockSignals(true); clickButton->setChecked(f); clickButton->blockSignals(false); } //--------------------------------------------------------- // setSyncFlag //--------------------------------------------------------- void Transport::setSyncFlag(bool f) { syncButton->blockSignals(true); syncButton->setChecked(f); syncButton->blockSignals(false); } //--------------------------------------------------------- // toggleRecMode //--------------------------------------------------------- void Transport::setRecMode(int id) { if (MusEGlobal::song->recMode() != id) MusEGlobal::song->setRecMode(id); if (recMode->currentIndex() != id) recMode->setCurrentIndex(id); } //--------------------------------------------------------- // toggleCycleMode //--------------------------------------------------------- void Transport::setCycleMode(int id) { if (MusEGlobal::song->cycleMode() != id) MusEGlobal::song->setCycleMode(id); if (cycleMode->currentIndex() != id) cycleMode->setCurrentIndex(id); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void Transport::songChanged(MusECore::SongChangedStruct_t flags) { slider->setRange(0, MusEGlobal::song->len()); int cpos = MusEGlobal::song->cpos(); if (flags & (SC_MASTER | SC_TEMPO)) { if(!MusEGlobal::extSyncFlag) setTempo(MusEGlobal::tempomap.tempo(cpos)); } if (flags & SC_SIG) { int z, n; MusEGlobal::sigmap.timesig(cpos, z, n); setTimesig(z, n); } if (flags & SC_MASTER) { tempo->setMasterTrack(MusEGlobal::tempomap.masterFlag()); } if (flags & SC_EXTERNAL_MIDI_SYNC) { syncChanged(MusEGlobal::extSyncFlag); } if (flags & SC_USE_JACK_TRANSPORT) { jackSyncChanged(MusEGlobal::config.useJackTransport); } if (flags & SC_TIMEBASE_MASTER) { timebaseMasterChanged(MusEGlobal::timebaseMasterState); } } //--------------------------------------------------------- // syncChanged //--------------------------------------------------------- void Transport::syncChanged(bool flag) { syncButton->blockSignals(true); syncButton->setChecked(flag); syncButton->blockSignals(false); buttons[0]->setEnabled(!flag); // goto start buttons[1]->setEnabled(!flag); // rewind buttons[2]->setEnabled(!flag); // forward buttons[3]->setEnabled(!flag); // stop buttons[4]->setEnabled(!flag); // play slider->setEnabled(!flag); tempo->setExternalMode(flag); if(!flag) // Update or initialize the value - this might be the first time setting it. tempo->setTempo(MusEGlobal::tempomap.tempo(MusEGlobal::song->cpos())); MusEGlobal::playAction->setEnabled(!flag); MusEGlobal::startAction->setEnabled(!flag); MusEGlobal::stopAction->setEnabled(!flag); MusEGlobal::rewindAction->setEnabled(!flag); MusEGlobal::forwardAction->setEnabled(!flag); } void Transport::jackSyncChanged(bool flag) { jackTransportButton->blockSignals(true); timebaseMasterButton->blockSignals(true); jackTransportButton->setChecked(flag); jackTransportButton->setEnabled(MusEGlobal::audioDevice && MusEGlobal::audioDevice->hasOwnTransport()); timebaseMasterButton->setEnabled(MusEGlobal::audioDevice && MusEGlobal::audioDevice->hasOwnTransport() && MusEGlobal::audioDevice->hasTimebaseMaster() && flag); jackTransportButton->blockSignals(false); timebaseMasterButton->blockSignals(false); } void Transport::timebaseMasterChanged(bool flag) { DEBUG_TRANSPORT(stderr, "timebaseMasterChanged() flag:%d timebaseMaster:%d\n", flag, MusEGlobal::config.timebaseMaster); timebaseMasterButton->blockSignals(true); const bool has_master = MusEGlobal::audioDevice && MusEGlobal::audioDevice->hasTimebaseMaster(); if(has_master && flag) { DEBUG_TRANSPORT(stderr, "timebaseMasterChanged() We're master.\n"); // Reset to no blink. timebaseMasterButton->setBlinking(false); timebaseMasterButton->setChecked(true); } else if(has_master && MusEGlobal::config.timebaseMaster) { DEBUG_TRANSPORT(stderr, "timebaseMasterChanged() Before connection:%d\n", (bool)_timebaseMasterBlinkConnection); timebaseMasterButton->setChecked(false); // Set to blink. timebaseMasterButton->setBlinking(true); } else { DEBUG_TRANSPORT(stderr, "timebaseMasterChanged() Not master, don't wanna be either.\n"); // Reset to no blink. timebaseMasterButton->setBlinking(false); timebaseMasterButton->setChecked(false); } timebaseMasterButton->blockSignals(false); } //--------------------------------------------------------- // stopToggled //--------------------------------------------------------- void Transport::stopToggled(bool val) { if (val) MusEGlobal::song->setStop(true); else { buttons[3]->blockSignals(true); buttons[3]->setChecked(true); buttons[3]->blockSignals(false); } } //--------------------------------------------------------- // playToggled //--------------------------------------------------------- void Transport::playToggled(bool val) { if (val) MusEGlobal::song->setPlay(true); else { buttons[4]->blockSignals(true); buttons[4]->setChecked(true); buttons[4]->blockSignals(false); } } void Transport::sigChange(const MusECore::TimeSignature& sig) { // Add will replace if found. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::AddSig, MusEGlobal::song->cPos().tick(), sig.z, sig.n)); } //--------------------------------------------------------- // extSyncClicked //--------------------------------------------------------- void Transport::extSyncClicked(bool v) { MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(&MusEGlobal::extSyncFlag, v, MusECore::PendingOperationItem::SetExternalSyncFlag)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // useJackTransportClicked //--------------------------------------------------------- void Transport::useJackTransportClicked(bool v) { if(!v && MusEGlobal::timebaseMasterState && MusEGlobal::audioDevice) { // Let the operation do this. //MusEGlobal::config.timebaseMaster = v; // Force it. MusEGlobal::audioDevice->setMaster(v, false); } MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(&MusEGlobal::config.useJackTransport, v, MusECore::PendingOperationItem::SetUseJackTransport)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // timebaseMasterClicked //--------------------------------------------------------- void Transport::timebaseMasterClicked(bool v) { // MusECore::PendingOperationList operations; // operations.add(MusECore::PendingOperationItem(&MusEGlobal::useJackTransport, v, MusECore::PendingOperationItem::SetUseJackTransport)); // MusEGlobal::audio->msgExecutePendingOperations(operations, true); if(!MusEGlobal::audioDevice) return; if(MusEGlobal::config.useJackTransport) { //if(MusEGlobal::timebaseMasterState != MusEGlobal::config.timebaseMaster) MusEGlobal::config.timebaseMaster = v; // Force it. MusEGlobal::audioDevice->setMaster(v, true); } } void Transport::keyPressEvent(QKeyEvent* ev) { switch (ev->key()) { case Qt::Key_Escape: ev->accept(); // Yield the focus to the transport window. setFocus(); return; break; default: break; } // Let some other higher up window handle it if needed. ev->ignore(); QWidget::keyPressEvent(ev); } } // namespace MusEGui muse-4.0.0/src/muse/transport.h000066400000000000000000000112371404105311000164220ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: transport.h,v 1.4 2004/06/28 21:13:16 wschweer Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TRANSPORT_H__ #define __TRANSPORT_H__ #include #include #include #include "type_defs.h" // Forward declarations: class QComboBox; class QHBoxLayout; class QLabel; class QSlider; class QMouseEvent; class QToolButton; class QKeyEvent; namespace MusECore { class Pos; struct TimeSignature; } namespace MusEGui { class PosEdit; class DoubleLabel; class SigLabel; class IconButton; class TempoEdit; class SigEdit; //--------------------------------------------------------- // TempoSig //--------------------------------------------------------- class TempoSig : public QWidget { Q_OBJECT IconButton* _masterButton; TempoEdit* l1; SigEdit* l2; QLabel* l3; private slots: void configChanged(); void masterToggled(bool); public slots: void newTempo(double); void setTempo(int tempo); signals: void tempoChanged(int); void sigChanged(const MusECore::TimeSignature&); void masterTrackChanged(bool); void returnPressed(); void escapePressed(); public: TempoSig(QWidget* parent=0); void setTimesig(int a, int b); void setExternalMode(bool on); bool masterTrack() const; void setMasterTrack(bool); }; //--------------------------------------------------------- // Handle //--------------------------------------------------------- class Handle : public QWidget { QWidget* rootWin; int dx, dy; void mouseMoveEvent(QMouseEvent* ev); void mousePressEvent(QMouseEvent* ev); public: Handle(QWidget* r, QWidget* parent=0); }; //--------------------------------------------------------- // Transport //--------------------------------------------------------- class Transport : public QWidget { Q_OBJECT PosEdit* tl1; // left mark PosEdit* tl2; // right mark PosEdit* time1; // tick time PosEdit* time2; // SMPTE QSlider* slider; TempoSig* tempo; QHBoxLayout* tb; QComboBox* recMode; QComboBox* cycleMode; IconButton* clickButton; IconButton* syncButton; IconButton* jackTransportButton; IconButton* timebaseMasterButton; QToolButton* buttons[6]; // transport buttons QLabel* l2; QLabel* l3; QLabel* l5; QLabel* l6; Handle *lefthandle, *righthandle; QMetaObject::Connection _timebaseMasterBlinkConnection; protected: virtual void keyPressEvent(QKeyEvent *); private slots: void cposChanged(const MusECore::Pos&); void cposChanged(int); void lposChanged(const MusECore::Pos&); void rposChanged(const MusECore::Pos&); void songChanged(MusECore::SongChangedStruct_t); void syncChanged(bool); void timebaseMasterChanged(bool); void jackSyncChanged(bool); void setRecord(bool flag); void stopToggled(bool); void playToggled(bool); void configChanged(); void sigChange(const MusECore::TimeSignature&); void extSyncClicked(bool v); void useJackTransportClicked(bool v); void timebaseMasterClicked(bool v); public slots: void setTempo(int tempo); void setTimesig(int a, int b); void setPos(int,unsigned, bool); void setMasterFlag(bool); void setClickFlag(bool); void setSyncFlag(bool); void setPlay(bool f); void setHandleColor(QColor); void setRecMode(int); void setCycleMode(int); public: Transport(QWidget* parent, const char* name = 0); QColor getHandleColor() const { return lefthandle->palette().color(QPalette::Window); } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/transport_obj.cpp000066400000000000000000000226341404105311000176120ustar00rootroot00000000000000//=================================================================== // MusE // Linux Music Editor // // latency_info.cpp // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //=================================================================== #include "transport_obj.h" namespace MusECore { void TransportSource::prepareLatencyScan(bool can_correct_latency) { _latencyInfo.initialize(); _transportLatencyOut = 0.0f; _canCorrect = can_correct_latency; } bool TransportSource::isLatencyInputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyInputTerminalProcessed) return _latencyInfo._isLatencyInputTerminal; // If we're asking for the view from the record side, check if we're // passing the signal through the track via monitoring. if(!canPassThruLatency()) { _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } // const RouteList* rl = outRoutes(); // for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { // switch(ir->type) // { // case Route::TRACK_ROUTE: // if(!ir->track) // continue; // if(ir->track->isMidiTrack()) // { // // TODO // } // else // { // Track* track = ir->track; // if(track->off()) // || // //(atrack->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !atrack->isRecMonitored()))) // //&& atrack->canRecord() && !atrack->recordFlag())) // continue; // // _latencyInfo._isLatencyInputTerminal = false; // _latencyInfo._isLatencyInputTerminalProcessed = true; // return false; // } // break; // // default: // break; // } // } _latencyInfo._isLatencyInputTerminal = true; _latencyInfo._isLatencyInputTerminalProcessed = true; return true; } bool TransportSource::isLatencyOutputTerminal() { // Have we been here before during this scan? // Just return the cached value. if(_latencyInfo._isLatencyOutputTerminalProcessed) return _latencyInfo._isLatencyOutputTerminal; // const RouteList* rl = outRoutes(); // for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { // switch(ir->type) // { // case Route::TRACK_ROUTE: // if(!ir->track) // continue; // if(ir->track->isMidiTrack()) // { // // TODO // } // else // { // Track* track = ir->track; // if(track->off()) // || // //(atrack->canRecordMonitor() && (MusEGlobal::config.monitoringAffectsLatency || !atrack->isRecMonitored()))) // //&& atrack->canRecord() && !atrack->recordFlag())) // continue; // // _latencyInfo._isLatencyOutputTerminal = false; // _latencyInfo._isLatencyOutputTerminalProcessed = true; // return false; // } // break; // // default: // break; // } // } // // _latencyInfo._isLatencyOutputTerminal = true; // _latencyInfo._isLatencyOutputTerminalProcessed = true; // TODO: Ask whether this transport source is 'connected' (being used). // For now, assume it is. _latencyInfo._isLatencyOutputTerminal = false; _latencyInfo._isLatencyOutputTerminalProcessed = true; return true; } TrackLatencyInfo& TransportSource::getDominanceInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._canDominateInputProcessed) || (!input && _latencyInfo._canDominateProcessed)) return _latencyInfo; // Get the default domination for this track type. const bool can_dominate_lat = input ? canDominateInputLatency() : canDominateOutputLatency(); const bool can_correct_lat = canCorrectOutputLatency() && _canCorrect; // Set the correction of all connected input branches, // but ONLY if the track is not off. if(input) { _latencyInfo._canDominateInputLatency = can_dominate_lat; } else { _latencyInfo._canDominateOutputLatency = can_dominate_lat; // If any of the branches can dominate, then this node cannot correct. // REMOVE Tim. lv2. Added. // // Special for the transport source: We use the correction values // // but we do NOT report to the rest of the branch that we can correct. // // So we ignore _canCorrectOutputLatency. // // Yes, it can correct its own latency, but without further mechanisms we cannot know whether // // the transport information affects the actual sound, or just an arpeggiator for example. // // Example: // // The LV2 Example Metronome depends on the transport info to directly produce the sound. // // That sound will have latency so correcting the transport values will correct it. // // In that case we would want to return 'true' here - ie. this node can indeed 'correct' // // the actual sound. // // However, the Helm LV2 synth uses the transport info for the arpeggiator. // // Correcting the transport info only corrects the arpeggiator, NOT the actual sound. // // To correct the actual sound the midi input is corrected instead. // // Therefore we cannot return 'true' for certain here. // // By returning 'false' here, we will NOT inform the rest of the branch that we can correct, // // but we WILL use the correction value anyway. If it corrects the sound, great, but we // // will not report that to the branch. OK? ;-) _latencyInfo._canCorrectOutputLatency = can_correct_lat && !can_dominate_lat; // //_latencyInfo._canCorrectOutputLatency = false; } if(input) _latencyInfo._canDominateInputProcessed = true; else _latencyInfo._canDominateProcessed = true; return _latencyInfo; } TrackLatencyInfo& TransportSource::getDominanceLatencyInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._dominanceInputProcessed) || (!input && _latencyInfo._dominanceProcessed)) return _latencyInfo; float route_worst_latency = 0.0f; const bool passthru = canPassThruLatency(); float worst_self_latency = 0.0f; // Set the correction of all connected input branches, // but ONLY if the track is not off. if(input) { _latencyInfo._inputLatency = route_worst_latency; } else { if(passthru) { _latencyInfo._outputLatency = worst_self_latency + route_worst_latency; _latencyInfo._inputLatency = route_worst_latency; } else { _latencyInfo._outputLatency = worst_self_latency + _latencyInfo._sourceCorrectionValue; } } if(input) _latencyInfo._dominanceInputProcessed = true; else _latencyInfo._dominanceProcessed = true; return _latencyInfo; } TrackLatencyInfo& TransportSource::setCorrectionLatencyInfo( bool input, float finalWorstLatency, float callerBranchLatency, bool commonProjectLatency) { float worst_self_latency = 0.0f; // The _trackLatency should already be calculated in the dominance scan. const float branch_lat = callerBranchLatency + worst_self_latency; // Set the correction of all connected input branches, // but ONLY if the track is not off. if(input) { } else { // REMOVE Tim. lv2. Added. // // Special for the transport source: We use the correction values // // but we do NOT report to the rest of the branch that we can correct. // // So we ignore _canCorrectOutputLatency. See getDominanceInfo() for explanation. if(canCorrectOutputLatency() /*&& _latencyInfo._canCorrectOutputLatency*/) { float corr = 0.0f; //if(MusEGlobal::config.commonProjectLatency) if(commonProjectLatency) corr -= finalWorstLatency; corr -= branch_lat; // The _sourceCorrectionValue is initialized to zero. // Whichever calling branch needs the most correction gets it. if(corr < _latencyInfo._sourceCorrectionValue) _latencyInfo._sourceCorrectionValue = corr; } } //fprintf(stderr, "TransportSource::setCorrectionLatencyInfo() name:%s finalWorstLatency:%f branch_lat:%f corr:%f _sourceCorrectionValue:%f\n", // name().toLatin1().constData(), finalWorstLatency, branch_lat, corr, _latencyInfo._sourceCorrectionValue); return _latencyInfo; } TrackLatencyInfo& TransportSource::getLatencyInfo(bool input) { // Have we been here before during this scan? // Just return the cached value. if((input && _latencyInfo._inputProcessed) || (!input && _latencyInfo._processed)) return _latencyInfo; if(input) _latencyInfo._inputProcessed = true; else _latencyInfo._processed = true; return _latencyInfo; } } // namespace MusECore muse-4.0.0/src/muse/transport_obj.h000066400000000000000000000116461404105311000172600ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // latency_info.h // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TRANSPORT_OBJ_H__ #define __TRANSPORT_OBJ_H__ #include "latency_info.h" namespace MusECore { class TransportSource { private: TrackLatencyInfo _latencyInfo; float _transportLatencyOut; bool _canCorrect; public: TransportSource() { _latencyInfo.initialize(); _transportLatencyOut = 0.0f; _canCorrect = false; } // Initializes this track's latency information in preparation for a latency scan. void prepareLatencyScan(bool can_correct_latency = false); // Whether this track (and the branch it is in) can force other parallel branches to // increase their latency compensation to match this one. // If false, this branch will NOT disturb other parallel branches' compensation, // intead only allowing compensation UP TO the worst case in other branches. bool canDominateOutputLatency() const { return false; } bool canDominateInputLatency() const { return false; } // Whether this track (and the branch it is in) can force other parallel branches to // increase their latency compensation to match this one - IF this track is an end-point // and the branch allows domination. // If false, this branch will NOT disturb other parallel branches' compensation, // intead only allowing compensation UP TO the worst case in other branches. bool canDominateEndPointLatency() const { return false; } // Whether this track and its branch can correct for latency, not just compensate. // Special for transport source: Yes, it can correct its own latency, but without // further mechanisms we cannot know whether the transport information affects the // actual sound, or just an arpeggiator for example. See getDominanceInfo() for info. bool canCorrectOutputLatency() const { return true; } // Whether the track can pass latency values through, the SAME as if record monitor is // supported and on BUT does not require record monitor support. // This is for example in the metronome MetronomeSynthI, since it is unique in that it // can correct its own latency unlike other synths, but it does not 'pass through' // the latency values to what drives it like other synths. bool canPassThruLatency() const { return false; } // Whether any of the connected output routes are effectively connected. // That means track is not off, track is monitored where applicable, etc, // ie. signal can actually flow. // For Wave Tracks for example, asks whether the track is an end-point from the view of the input side. bool isLatencyInputTerminal(); // Whether any of the connected output routes are effectively connected. // That means track is not off, track is monitored where applicable, etc, // ie. signal can actually flow. // For Wave Tracks for example, asks whether the track is an end-point from the view of the playback side. bool isLatencyOutputTerminal(); TrackLatencyInfo& getDominanceInfo(bool input); TrackLatencyInfo& getDominanceLatencyInfo(bool input); // The finalWorstLatency is the grand final worst-case latency, of any output track or open branch, // determined in the complete getDominanceLatencyInfo() scan. // The callerBranchLatency is the inherent branch latency of the calling track, or zero if calling from // the very top outside of the branch heads (outside of output tracks or open branches). // The callerBranchLatency is accumulated as setCorrectionLatencyInfo() is called on each track // in a branch of the graph. TrackLatencyInfo& setCorrectionLatencyInfo( bool input, float finalWorstLatency, float callerBranchLatency = 0.0f, bool commonProjectLatency = false); // Argument 'input': Whether we want the input side of the track. For example un-monitored wave tracks // are considered two separate paths with a recording input side and a playback output side. TrackLatencyInfo& getLatencyInfo(bool input); float transportLatencyOut() const { return _transportLatencyOut; } void setTransportLatencyOut(float f) { _transportLatencyOut = f; } }; } // namespace MusECore #endif muse-4.0.0/src/muse/type_defs.h000066400000000000000000000271671404105311000163610ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // type_defs.h // Copyright (C) 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TYPE_DEFS_H__ #define __TYPE_DEFS_H__ #include namespace MusECore { // REMOVE Tim. Added. Moved here from event.h. // NOTICE: The values 3 and 4 (PAfter and CAfter) are reserved for the support of those two obsolete // channel and key pressure events in old files. They are converted to controllers upon load. enum EventType { Note=0, Controller=1, Sysex=2, /*PAfter=3,*/ /*CAfter=4,*/ Meta=5, Wave=6 }; typedef int64_t SongChangedFlags_t; struct SongChangedStruct_t { private: // // 128-bit combination of 128-bit SC_XX flags: // // Normal lower 64 bits of 128-bit flags. SongChangedFlags_t _flagsLo; // Upper 64 bits of 128-bit flags. SongChangedFlags_t _flagsHi; public: // An optional pointer to the object which initiated this song change. // The object's own songChanged() slot (if present) can use this to // ignore self-generated songChanged signals. This is the only practical // mechanism available for objects needing to do so. There's really // no other easy way to ignore such signals. void* _sender; public: inline SongChangedStruct_t(SongChangedFlags_t flagsLo = 0, SongChangedFlags_t flagsHi = 0, void* sender = 0) : _flagsLo(flagsLo), _flagsHi(flagsHi), _sender(sender) { }; SongChangedFlags_t flagsLo() const { return _flagsLo; } SongChangedFlags_t flagsHi() const { return _flagsHi; } // C++11: Avoid necessity of using the "Safe Bool Idiom". explicit inline operator bool() const { return _flagsLo || _flagsHi; } inline SongChangedStruct_t operator~() const { SongChangedStruct_t r(~_flagsLo, ~_flagsHi); return r; } inline bool operator==(const SongChangedStruct_t& f) const { return _flagsLo == f._flagsLo && _flagsHi == f._flagsHi; } inline bool operator!=(const SongChangedStruct_t& f) const { return _flagsLo != f._flagsLo || _flagsHi != f._flagsHi; } inline SongChangedStruct_t& operator|=(const SongChangedStruct_t& f) { _flagsLo |= f._flagsLo; _flagsHi |= f._flagsHi; return *this; } inline SongChangedStruct_t& operator&=(const SongChangedStruct_t& f) { _flagsLo &= f._flagsLo; _flagsHi &= f._flagsHi; return *this; } inline friend SongChangedStruct_t operator|(const SongChangedStruct_t& a, const SongChangedStruct_t& b) { SongChangedStruct_t r(a); r |= b; return r; } inline friend SongChangedStruct_t operator&(const SongChangedStruct_t& a, const SongChangedStruct_t& b) { SongChangedStruct_t r(a); r &= b; return r; } }; // Song changed flags: // These are flags, usually passed by connecting to the songChanged() signal, // which inform that various things have changed and appropriate action should // be taken (redraw, refill lists etc.) upon the signal's reception. // NOTE: Use the SongChangedStruct_t typedef to support all the bits. #define SC_NOTHING MusECore::SongChangedStruct_t(0, 0) #define SC_TRACK_INSERTED MusECore::SongChangedStruct_t(1) #define SC_TRACK_REMOVED MusECore::SongChangedStruct_t(2) #define SC_TRACK_MODIFIED MusECore::SongChangedStruct_t(4) #define SC_PART_INSERTED MusECore::SongChangedStruct_t(8) #define SC_PART_REMOVED MusECore::SongChangedStruct_t(0x10) #define SC_PART_MODIFIED MusECore::SongChangedStruct_t(0x20) #define SC_EVENT_INSERTED MusECore::SongChangedStruct_t(0x40) #define SC_EVENT_REMOVED MusECore::SongChangedStruct_t(0x80) #define SC_EVENT_MODIFIED MusECore::SongChangedStruct_t(0x100) #define SC_SIG MusECore::SongChangedStruct_t(0x200) // timing signature #define SC_TEMPO MusECore::SongChangedStruct_t(0x400) // tempo map changed #define SC_MASTER MusECore::SongChangedStruct_t(0x800) // master flag changed #define SC_SELECTION MusECore::SongChangedStruct_t(0x1000) // event selection. part and track selection have their own. #define SC_MUTE MusECore::SongChangedStruct_t(0x2000) // A track's mute or off state changed. #define SC_SOLO MusECore::SongChangedStruct_t(0x4000) #define SC_RECFLAG MusECore::SongChangedStruct_t(0x8000) #define SC_ROUTE MusECore::SongChangedStruct_t(0x10000) // A route was added, changed, or deleted. Or a midi track's out channel/port was changed. #define SC_CHANNELS MusECore::SongChangedStruct_t(0x20000) #define SC_CONFIG MusECore::SongChangedStruct_t(0x40000) // midiPort-midiDevice #define SC_DRUMMAP MusECore::SongChangedStruct_t(0x80000) // must update drumeditor #define SC_MIDI_INSTRUMENT MusECore::SongChangedStruct_t(0x100000) // A midi port or device's instrument has changed #define SC_AUDIO_CONTROLLER MusECore::SongChangedStruct_t(0x200000) // An audio controller value was added deleted or modified. #define SC_AUTOMATION MusECore::SongChangedStruct_t(0x400000) // A track's automation mode setting changed (off, read, touch, write etc). #define SC_AUX MusECore::SongChangedStruct_t(0x800000) // A mixer aux was added or deleted. Not adjusted. #define SC_RACK MusECore::SongChangedStruct_t(0x1000000) // mixer rack changed #define SC_CLIP_MODIFIED MusECore::SongChangedStruct_t(0x2000000) #define SC_MIDI_CONTROLLER_ADD MusECore::SongChangedStruct_t(0x4000000) // a hardware midi controller was added or deleted // SC_MIDI_TRACK_PROP: A midi track's properties changed (name, thru etc). // For fairly 'static' properties, not frequently changing transp del compr velo or len, // nor output channel/port (use SC_ROUTE). #define SC_MIDI_TRACK_PROP MusECore::SongChangedStruct_t(0x8000000) #define SC_PART_SELECTION MusECore::SongChangedStruct_t(0x10000000) // part selection changed #define SC_KEY MusECore::SongChangedStruct_t(0x20000000) // key map changed #define SC_TRACK_SELECTION MusECore::SongChangedStruct_t(0x40000000) // track selection changed #define SC_PORT_ALIAS_PREFERENCE MusECore::SongChangedStruct_t(0x80000000) // (Jack) port alias viewing preference has changed #define SC_ROUTER_CHANNEL_GROUPING MusECore::SongChangedStruct_t(0x100000000) // Router channel grouping changed #define SC_AUDIO_CONTROLLER_LIST MusECore::SongChangedStruct_t(0x200000000) // An audio controller list was added deleted or modified. #define SC_PIANO_SELECTION MusECore::SongChangedStruct_t(0x400000000) // Piano keyboard selected note changed. #define SC_DRUM_SELECTION MusECore::SongChangedStruct_t(0x800000000) // Drum list selected note changed. #define SC_TRACK_REC_MONITOR MusECore::SongChangedStruct_t(0x1000000000) // Audio or midi track's record monitor changed. #define SC_TRACK_MOVED MusECore::SongChangedStruct_t(0x2000000000) // Audio or midi track's position in track list or mixer changed. #define SC_TRACK_RESIZED MusECore::SongChangedStruct_t(0x4000000000) // Audio or midi track was resized in the arranger. #define SC_METRONOME MusECore::SongChangedStruct_t(0x8000000000) // Metronome lists settings such as accents changed. #define SC_EXTERNAL_MIDI_SYNC MusECore::SongChangedStruct_t(0x10000000000) // External midi sync flag changed. #define SC_USE_JACK_TRANSPORT MusECore::SongChangedStruct_t(0x20000000000) // UseJackTransport flag changed. #define SC_TIMEBASE_MASTER MusECore::SongChangedStruct_t(0x40000000000) // Timebase master state changed. #define SC_AUDIO_CONVERTER MusECore::SongChangedStruct_t(0x80000000000) // Audio converters settings or value lists changed. #define SC_AUDIO_STRETCH MusECore::SongChangedStruct_t(0x100000000000) // Audio converters stretch/pitch ratios changed. #define SC_MARKER_INSERTED MusECore::SongChangedStruct_t(0x200000000000) #define SC_MARKER_REMOVED MusECore::SongChangedStruct_t(0x400000000000) #define SC_MARKER_MODIFIED MusECore::SongChangedStruct_t(0x800000000000) // The marker list was rebuilt as a result of tempo changes. NOTE: Currently signals/slots are used for add/remove/modify etc. #define SC_MARKERS_REBUILT MusECore::SongChangedStruct_t(0x1000000000000) // The midi division changed. Re-normalization of tempo and signature lists will have already occurred. #define SC_DIVISION_CHANGED MusECore::SongChangedStruct_t(0x2000000000000) #define SC_EVERYTHING MusECore::SongChangedStruct_t(-1, -1) // global update typedef int64_t EventID_t; #define MUSE_INVALID_EVENT_ID -1 #define MUSE_INVALID_POSITION INT_MAX enum class ResizeDirection { RESIZE_TO_THE_LEFT, RESIZE_TO_THE_RIGHT }; enum RelevantSelectedEvents { NoEventsRelevant = 0x00, NotesRelevant = 0x01, ControllersRelevant = 0x02, SysexRelevant = 0x04, MetaRelevant = 0x08, WaveRelevant = 0x10, AllEventsRelevant = NotesRelevant | ControllersRelevant | SysexRelevant | MetaRelevant | WaveRelevant}; typedef int RelevantSelectedEvents_t; enum FunctionOptions { FunctionNoOptions = 0x00, // For pasting. Whether to cut the given items before pasting. // Don't call cut_items() AND then set this flag on paste_at(). // Here, cutting is usually reserved for direct pasting // (calling paste_items_at() with an EventTagList*). FunctionCutItems = 0x01, // Always paste into a new part. FunctionPasteAlwaysNewPart = 0x02, // Never paste into a new part. FunctionPasteNeverNewPart = 0x04, // Erase existing target controller items first. FunctionEraseItems = 0x08, // If FunctionEraseItems is set: How to handle the last item in any 'cluster' of controller events. FunctionEraseItemsWysiwyg = 0x10, // If FunctionEraseItems is set: Erase existing target items in empty source space between 'clusters'. FunctionEraseItemsInclusive = 0x20, FunctionEraseItemsDefault = FunctionEraseItems | FunctionEraseItemsWysiwyg, FunctionAllOptions = FunctionCutItems | FunctionPasteAlwaysNewPart | FunctionPasteNeverNewPart | FunctionEraseItems | FunctionEraseItemsWysiwyg | FunctionEraseItemsInclusive }; typedef int FunctionOptions_t; struct FunctionOptionsStruct { FunctionOptions_t _flags; FunctionOptionsStruct(const FunctionOptions_t& flags = FunctionEraseItemsDefault) : _flags(flags) { } void clear() { _flags = FunctionNoOptions; } void setFlags(const FunctionOptions_t& flags) { _flags = flags; } void appendFlags(const FunctionOptions_t& flags) { _flags |= flags; } void removeFlags(const FunctionOptions_t& flags) { _flags &= ~flags; } }; } // namespace MusECore #endif muse-4.0.0/src/muse/undo.cpp000066400000000000000000005376001404105311000156750ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: undo.cpp,v 1.12.2.9 2009/05/24 21:43:44 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "assert.h" #include "sig.h" #include "keyevent.h" #include "undo.h" #include "song.h" #include "globals.h" #include "audio.h" #include "midiport.h" #include "operations.h" #include "tempo.h" #include "audiodev.h" #include "wave_helper.h" #include "gconfig.h" #include "al/al.h" #include #include #include // Forwards from header: #include "track.h" #include "part.h" #include "ctrl.h" // Enable for debugging: //#define _UNDO_DEBUG_ namespace MusECore { // iundo points to last Undo() in Undo-list static bool undoMode = false; // for debugging std::list temporaryWavFiles; //--------------------------------------------------------- // typeName //--------------------------------------------------------- const char* UndoOp::typeName() { static const char* name[] = { "AddRoute", "DeleteRoute", "AddTrack", "DeleteTrack", "AddPart", "DeletePart", "MovePart", "ModifyPartStart", "ModifyPartLength", "ModifyPartName", "SelectPart", "AddEvent", "DeleteEvent", "ModifyEvent", "SelectEvent", "AddAudioCtrlVal", "DeleteAudioCtrlVal", "ModifyAudioCtrlVal", "ModifyAudioCtrlValList", "AddTempo", "DeleteTempo", "ModifyTempo", "SetTempo", "SetStaticTempo", "SetGlobalTempo", "EnableMasterTrack", "AddSig", "DeleteSig", "ModifySig", "AddKey", "DeleteKey", "ModifyKey", "ModifyTrackName", "ModifyTrackChannel", "SetTrackRecord", "SetTrackMute", "SetTrackSolo", "SetTrackRecMonitor", "SetTrackOff", "MoveTrack", "ModifyClip", "AddMarker", "DeleteMarker", "ModifyMarker", "SetMarkerPos", "ModifySongLen", "SetInstrument", "DoNothing", "ModifyMidiDivision", "EnableAllAudioControllers", "GlobalSelectAllEvents", "NormalizeMidiDivision" }; return name[type]; } //--------------------------------------------------------- // dump //--------------------------------------------------------- void UndoOp::dump() { printf("UndoOp: %s\n ", typeName()); switch(type) { case AddTrack: case DeleteTrack: printf("%d %s\n", trackno, track->name().toLatin1().constData()); break; case AddEvent: case DeleteEvent: printf("old event:\n"); oEvent.dump(5); printf(" new event:\n"); nEvent.dump(5); printf(" Part:\n"); if (part) part->dump(5); break; case ModifyTrackName: printf("<%s>-<%s>\n", _oldName->toLocal8Bit().data(), _newName->toLocal8Bit().data()); break; case ModifyTrackChannel: printf("%s <%d>-<%d>\n", track->name().toLatin1().constData(), _oldPropValue, _newPropValue); break; case SetTrackRecord: printf("%s %d\n", track->name().toLatin1().constData(), a); break; case SetTrackMute: printf("%s %d\n", track->name().toLatin1().constData(), a); break; case SetTrackSolo: printf("%s %d\n", track->name().toLatin1().constData(), a); break; case SetTrackRecMonitor: printf("%s %d\n", track->name().toLatin1().constData(), a); break; case SetTrackOff: printf("%s %d\n", track->name().toLatin1().constData(), a); break; default: break; } } //--------------------------------------------------------- // clearDelete //--------------------------------------------------------- void UndoList::clearDelete() { if(!empty()) { if (this->isUndo) { for(iUndo iu = begin(); iu != end(); ++iu) { Undo& u = *iu; for(iUndoOp i = u.begin(); i != u.end(); ++i) { switch(i->type) { case UndoOp::DeleteTrack: if(i->track) delete const_cast(i->track); break; case UndoOp::DeletePart: delete const_cast(i->part); break; case UndoOp::ModifyMarker: case UndoOp::SetMarkerPos: case UndoOp::AddMarker: case UndoOp::DeleteMarker: if (i->oldMarker) delete i->oldMarker; if (i->newMarker) delete i->newMarker; break; case UndoOp::ModifyPartName: case UndoOp::ModifyTrackName: if (i->_oldName) delete i->_oldName; if (i->_newName) delete i->_newName; break; case UndoOp::ModifyAudioCtrlValList: if (i->_eraseCtrlList) delete i->_eraseCtrlList; if (i->_addCtrlList) delete i->_addCtrlList; break; default: break; } } u.clear(); } } else { for(riUndo iu = rbegin(); iu != rend(); ++iu) { Undo& u = *iu; for(riUndoOp i = u.rbegin(); i != u.rend(); ++i) { switch(i->type) { case UndoOp::AddTrack: delete i->track; break; case UndoOp::AddPart: delete i->part; break; case UndoOp::ModifyMarker: case UndoOp::SetMarkerPos: case UndoOp::AddMarker: case UndoOp::DeleteMarker: if (i->oldMarker) delete i->oldMarker; if (i->newMarker) delete i->newMarker; break; case UndoOp::ModifyPartName: case UndoOp::ModifyTrackName: if (i->_oldName) delete i->_oldName; if (i->_newName) delete i->_newName; break; case UndoOp::ModifyAudioCtrlValList: if (i->_eraseCtrlList) delete i->_eraseCtrlList; if (i->_addCtrlList) delete i->_addCtrlList; break; default: break; } } u.clear(); } } } clear(); } //--------------------------------------------------------- // startUndo //--------------------------------------------------------- void Song::startUndo(void* sender) { redoList->clearDelete(); // redo must be invalidated when a new undo is started MusEGlobal::redoAction->setEnabled(false); setUndoRedoText(); undoList->push_back(Undo()); updateFlags = SongChangedStruct_t(0, 0, sender); undoMode = true; } //--------------------------------------------------------- // endUndo //--------------------------------------------------------- void Song::endUndo(SongChangedStruct_t flags) { // It is possible the current list may be empty after our optimizations during appending // of given operations to the current list. (Or if no operations were pushed between startUndo and endUndo). // Get rid of an empty current list now. if(undoList->back().empty()) undoList->pop_back(); else { riUndo prev_undo = undoList->rbegin(); prev_undo++; if (prev_undo!=undoList->rend()) { // try to merge the current Undo with the last one if (prev_undo->merge_combo(undoList->back())) undoList->pop_back(); } } // Even if the current list was empty, or emptied during appending of given operations to the current list, // the given operations were executed so we still need to inform that something may have changed. updateFlags |= flags; endMsgCmd(); undoMode = false; } //--------------------------------------------------------- // setUndoRedoText //--------------------------------------------------------- void Song::setUndoRedoText() { if(MusEGlobal::undoAction) { QString s = tr("Und&o"); if(MusEGlobal::undoAction->isEnabled()) { if(!undoList->empty() && !undoList->back().empty()) { int sz = undoList->back().size(); //if(sz >= 2) // s += QString(" (%1)").arg(sz); s += QString(" ") + undoList->back().front().typeName(); if(sz >= 2) s += ", .."; // Hm, the tooltip will not show three dots "..." } } MusEGlobal::undoAction->setText(s); } if(MusEGlobal::redoAction) { QString s = tr("Re&do"); if(MusEGlobal::redoAction->isEnabled()) { if(!redoList->empty() && !redoList->back().empty()) { int sz = redoList->back().size(); //if(sz >= 2) // s += QString(" (%1)").arg(sz); s += QString(" ") + redoList->back().front().typeName(); if(sz >= 2) s += ", .."; } } MusEGlobal::redoAction->setText(s); } } void Undo::push_front(const UndoOp& op) { insert(begin(), op); } void Undo::push_back(const UndoOp& op) { insert(end(), op); } void Undo::insert(Undo::iterator position, Undo::const_iterator first, Undo::const_iterator last) { for(Undo::const_iterator iuo = first; iuo != last; ++iuo) insert(position, *iuo); } void Undo::insert(Undo::iterator position, Undo::size_type n, const UndoOp& op) { for(Undo::size_type i = 0; i != n; ++i) insert(position, op); } void Undo::insert(Undo::iterator position, const UndoOp& op) { UndoOp n_op = op; #ifdef _UNDO_DEBUG_ switch(n_op.type) { case UndoOp::AddRoute: fprintf(stderr, "Undo::insert: AddRoute\n"); break; case UndoOp::DeleteRoute: fprintf(stderr, "Undo::insert: DeleteRoute\n"); break; case UndoOp::AddTrack: fprintf(stderr, "Undo::insert: AddTrack\n"); break; case UndoOp::DeleteTrack: fprintf(stderr, "Undo::insert: DeleteTrack\n"); break; case UndoOp::MoveTrack: fprintf(stderr, "Undo::insert: MoveTrack\n"); break; case UndoOp::ModifyTrackName: fprintf(stderr, "Undo::insert: ModifyTrackName\n"); break; case UndoOp::ModifyTrackChannel: fprintf(stderr, "Undo::insert: ModifyTrackChannel\n"); break; case UndoOp::SetTrackRecord: fprintf(stderr, "Undo::insert: SetTrackRecord\n"); break; case UndoOp::SetTrackMute: fprintf(stderr, "Undo::insert: SetTrackMute\n"); break; case UndoOp::SetTrackSolo: fprintf(stderr, "Undo::insert: SetTrackSolo\n"); break; case UndoOp::SetTrackRecMonitor: fprintf(stderr, "Undo::insert: SetTrackRecMonitor\n"); break; case UndoOp::SetTrackOff: fprintf(stderr, "Undo::insert: SetTrackOff\n"); break; case UndoOp::AddPart: fprintf(stderr, "Undo::insert: AddPart\n"); break; case UndoOp::DeletePart: fprintf(stderr, "Undo::insert: DeletePart\n"); break; case UndoOp::MovePart: fprintf(stderr, "Undo::insert: MovePart\n"); break; case UndoOp::SelectPart: fprintf(stderr, "Undo::insert: SelectPart\n"); break; case UndoOp::ModifyPartName: fprintf(stderr, "Undo::insert: ModifyPartName\n"); break; case UndoOp::ModifyPartStart: fprintf(stderr, "Undo::insert: ModifyPartStart\n"); break; case UndoOp::ModifyPartLength: fprintf(stderr, "Undo::insert: ModifyPartLength\n"); break; case UndoOp::AddEvent: fprintf(stderr, "Undo::insert: AddEvent\n"); break; case UndoOp::DeleteEvent: fprintf(stderr, "Undo::insert: DeleteEvent\n"); break; case UndoOp::ModifyEvent: fprintf(stderr, "Undo::insert: ModifyEvent\n"); break; case UndoOp::SelectEvent: fprintf(stderr, "Undo::insert: SelectEvent\n"); break; case UndoOp::AddAudioCtrlVal: fprintf(stderr, "Undo::insert: AddAudioCtrlVal\n"); break; case UndoOp::DeleteAudioCtrlVal: fprintf(stderr, "Undo::insert: DeleteAudioCtrlVal\n"); break; case UndoOp::ModifyAudioCtrlVal: fprintf(stderr, "Undo::insert: ModifyAudioCtrlVal\n"); break; case UndoOp::ModifyAudioCtrlValList: fprintf(stderr, "Undo::insert: ModifyAudioCtrlValList\n"); break; case UndoOp::AddTempo: fprintf(stderr, "Undo::insert: AddTempo tempo:%d tick:%d\n", n_op.b, n_op.a); break; case UndoOp::DeleteTempo: fprintf(stderr, "Undo::insert: DeleteTempo old val:%d tick:%d\n", n_op.b, n_op.a); break; case UndoOp::ModifyTempo: fprintf(stderr, "Undo::insert: ModifyTempo old:%d new:%d tick:%d\n", n_op.b, n_op.c, n_op.a); break; case UndoOp::SetTempo: fprintf(stderr, "Undo::insert: SetTempo tempo:%d tick:%d\n", n_op.b, n_op.a); break; case UndoOp::SetStaticTempo: fprintf(stderr, "Undo::insert: SetStaticTempo\n"); break; case UndoOp::SetGlobalTempo: fprintf(stderr, "Undo::insert: SetGlobalTempo\n"); break; case UndoOp::EnableMasterTrack: fprintf(stderr, "Undo::insert: EnableMasterTrack\n"); break; case UndoOp::AddSig: fprintf(stderr, "Undo::insert: AddSig\n"); break; case UndoOp::DeleteSig: fprintf(stderr, "Undo::insert: DeleteSig\n"); break; case UndoOp::ModifySig: fprintf(stderr, "Undo::insert: ModifySig\n"); break; case UndoOp::AddKey: fprintf(stderr, "Undo::insert: AddKey\n"); break; case UndoOp::DeleteKey: fprintf(stderr, "Undo::insert: DeleteKey\n"); break; case UndoOp::ModifyKey: fprintf(stderr, "Undo::insert: ModifyKey\n"); break; case UndoOp::ModifyClip: fprintf(stderr, "Undo::insert: ModifyClip\n"); break; case UndoOp::AddMarker: fprintf(stderr, "Undo::insert: AddMarker\n"); break; case UndoOp::DeleteMarker: fprintf(stderr, "Undo::insert: DeleteMarker\n"); break; case UndoOp::ModifyMarker: fprintf(stderr, "Undo::insert: ModifyMarker\n"); break; case UndoOp::SetMarkerPos: fprintf(stderr, "Undo::insert: SetMarkerPos\n"); break; case UndoOp::ModifySongLen: fprintf(stderr, "Undo::insert: ModifySongLen\n"); break; case UndoOp::SetInstrument: fprintf(stderr, "Undo::insert: SetInstrument\n"); break; case UndoOp::DoNothing: fprintf(stderr, "Undo::insert: DoNothing\n"); break; case UndoOp::ModifyMidiDivision: fprintf(stderr, "Undo::insert: ModifyMidiDivision\n"); break; case UndoOp::EnableAllAudioControllers: fprintf(stderr, "Undo::insert: EnableAllAudioControllers\n"); break; case UndoOp::GlobalSelectAllEvents: fprintf(stderr, "Undo::insert: GlobalSelectAllEvents\n"); break; case UndoOp::NormalizeMidiDivision: fprintf(stderr, "Undo::insert: NormalizeMidiDivision\n"); break; default: break; } #endif // (NOTE: Use this handy speed-up 'if' line to exclude unhandled operation types) if(n_op.type != UndoOp::ModifyTrackChannel && n_op.type != UndoOp::ModifyClip && n_op.type != UndoOp::DoNothing) { // TODO FIXME: Must look beyond position and optimize in that direction too ! //for(Undo::iterator iuo = begin(); iuo != position; ++iuo) iterator iuo = position; while(iuo != begin()) { --iuo; UndoOp& uo = *iuo; switch(n_op.type) { case UndoOp::AddRoute: if(uo.type == UndoOp::AddRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo) { fprintf(stderr, "MusE error: Undo::insert(): Double AddRoute. Ignoring.\n"); return; } else if(uo.type == UndoOp::DeleteRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo) { // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. erase(iuo); return; } break; case UndoOp::DeleteRoute: if(uo.type == UndoOp::DeleteRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo) { fprintf(stderr, "MusE error: Undo::insert(): Double DeleteRoute. Ignoring.\n"); return; } else if(uo.type == UndoOp::AddRoute && uo.routeFrom == n_op.routeFrom && uo.routeTo == n_op.routeTo) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(iuo); return; } break; case UndoOp::ModifyTrackName: if(uo.type == UndoOp::ModifyTrackName && uo.track == n_op.track) { fprintf(stderr, "MusE error: Undo::insert(): Double ModifyTrackName. Ignoring.\n"); return; } break; case UndoOp::MoveTrack: if(uo.type == UndoOp::MoveTrack && uo.a == n_op.a) { // Simply replace the 'to track' value. uo.b = n_op.b; return; } break; case UndoOp::SetTrackRecord: if(uo.type == UndoOp::SetTrackRecord && uo.track == n_op.track) { if(uo.a == n_op.a) { fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackRecord. Ignoring.\n"); return; } else { // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. erase(iuo); return; } } break; case UndoOp::SetTrackMute: if(uo.type == UndoOp::SetTrackMute && uo.track == n_op.track) { if(uo.a == n_op.a) { fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackMute. Ignoring.\n"); return; } else { // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. erase(iuo); return; } } break; case UndoOp::SetTrackSolo: if(uo.type == UndoOp::SetTrackSolo && uo.track == n_op.track) { if(uo.a == n_op.a) { fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackSolo. Ignoring.\n"); return; } else { // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. erase(iuo); return; } } break; case UndoOp::SetTrackRecMonitor: if(uo.type == UndoOp::SetTrackRecMonitor && uo.track == n_op.track) { if(uo.a == n_op.a) { fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackRecMonitor. Ignoring.\n"); return; } else { // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. erase(iuo); return; } } break; case UndoOp::SetTrackOff: if(uo.type == UndoOp::SetTrackOff && uo.track == n_op.track) { if(uo.a == n_op.a) { fprintf(stderr, "MusE error: Undo::insert(): Double SetTrackOff. Ignoring.\n"); return; } else { // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command. erase(iuo); return; } } break; case UndoOp::ModifyPartName: if(uo.type == UndoOp::ModifyPartName && uo.part == n_op.part) { fprintf(stderr, "MusE error: Undo::insert(): Double ModifyPartName. Ignoring.\n"); return; } break; case UndoOp::ModifyPartStart: // TODO: events_offset is a difference requiring accumulation not simple replacement, // and events_offset_time_type might be different requiring conversion. // if(uo.type == UndoOp::ModifyPartStart) // { // if(uo.part == n_op.part) // { // // Simply replace the new values. // uo.new_partlen_or_pos = n_op.new_partlen_or_pos; // uo.new_partlen = n_op.new_partlen; // uo.events_offset = n_op.events_offset; // uo.events_offset_time_type = n_op.events_offset_time_type; // return; // } // } break; case UndoOp::ModifyPartLength: // TODO: events_offset is a difference requiring accumulation not simple replacement, // and events_offset_time_type might be different requiring conversion. // if(uo.type == UndoOp::ModifyPartLength) // { // if(uo.part == n_op.part) // { // // Simply replace the new values. // uo.new_partlen_or_pos = n_op.new_partlen_or_pos; // uo.events_offset = n_op.events_offset; // uo.events_offset_time_type = n_op.events_offset_time_type; // return; // } // } break; case UndoOp::MovePart: if(uo.type == UndoOp::MovePart && uo.part == n_op.part) { // Simply replace the new value and new track. uo.new_partlen_or_pos = n_op.new_partlen_or_pos; uo.track = n_op.track; return; } break; case UndoOp::AddPart: if(uo.type == UndoOp::AddPart && uo.part == n_op.part) { fprintf(stderr, "MusE error: Undo::insert(): Double AddPart. Ignoring.\n"); return; } else if(uo.type == UndoOp::DeletePart && uo.part == n_op.part) { // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. erase(iuo); return; } break; case UndoOp::DeletePart: if(uo.type == UndoOp::DeletePart && uo.part == n_op.part) { fprintf(stderr, "MusE error: Undo::insert(): Double DeletePart. Ignoring.\n"); return; } else if(uo.type == UndoOp::AddPart && uo.part == n_op.part) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(iuo); return; } break; case UndoOp::AddEvent: if(uo.type == UndoOp::AddEvent && uo.nEvent == n_op.nEvent && uo.part == n_op.part) { fprintf(stderr, "MusE error: Undo::insert(): Double AddEvent. Ignoring.\n"); return; } else if(uo.type == UndoOp::DeleteEvent && uo.part == n_op.part) { if(uo.nEvent == n_op.nEvent) { // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command. erase(iuo); return; } else // To allow for easy DeleteEvent + AddEvent of a given controller number at a given time, // instead of demanding ModifyEvent. Automatically transform the operations. if(uo.nEvent.type() == Controller && n_op.nEvent.type() == Controller && uo.nEvent.dataA() == n_op.nEvent.dataA() && uo.nEvent.posValue() == n_op.nEvent.posValue()) { // Transform the DeleteEvent operation into a ModifyEvent operation. uo.type = UndoOp::ModifyEvent; uo.oEvent = uo.nEvent; uo.nEvent = n_op.nEvent; return; } } else if(uo.type == UndoOp::ModifyEvent && uo.part == n_op.part) { if(uo.nEvent == n_op.nEvent) { // Modify followed by adding of the modify's new event, is equivalent to just modifying with the added event. fprintf(stderr, "MusE error: Undo::insert(): ModifyEvent, then AddEvent same new event (double AddEvent). Ignoring.\n"); return; } else if(uo.oEvent == n_op.nEvent) { // Modify followed by adding of the modify's old event, is equivalent to just adding the event. // Transform the ModifyEvent operation into an AddEvent. uo.type = UndoOp::AddEvent; uo.nEvent = uo.oEvent; return; } } break; case UndoOp::DeleteEvent: if(uo.type == UndoOp::DeleteEvent && uo.nEvent == n_op.nEvent && uo.part == n_op.part) { fprintf(stderr, "MusE error: Undo::insert(): Double DeleteEvent. Ignoring.\n"); return; } else if(uo.type == UndoOp::AddEvent && uo.nEvent == n_op.nEvent && uo.part == n_op.part) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(iuo); return; } else if(uo.type == UndoOp::ModifyEvent && uo.part == n_op.part) { if(uo.oEvent == n_op.nEvent) { // Modify followed by delete of the modify's old event, is an error - two deletes of the same event. fprintf(stderr, "MusE error: Undo::insert(): ModifyEvent, then DeleteEvent same old event (double DeleteEvent). Ignoring.\n"); return; } else if(uo.nEvent == n_op.nEvent) { // Modify followed by delete of the modify's new event, is equivalent to just deleting the old event. // Transform the operation into a DeleteEvent. uo.type = UndoOp::DeleteEvent; uo.nEvent = uo.oEvent; return; } } break; case UndoOp::ModifyEvent: if(n_op.oEvent == n_op.nEvent) { // Equivalent to deleting then adding the same event - useless, cancels out. return; } else if(uo.type == UndoOp::ModifyEvent && uo.part == n_op.part) { // For testing... //fprintf(stderr, "MusE: DIAGNOSTIC: Undo::insert(): Double ModifyEvent... checking for errors...\n"); if(uo.oEvent == n_op.oEvent) { if(uo.nEvent == n_op.nEvent) { fprintf(stderr, "MusE error: Undo::insert(): Double ModifyEvent. Ignoring.\n"); return; } else { // For testing... //fprintf(stderr, "MusE: Undo::insert(): Double ModifyEvent. Same old events. Merging.\n"); // Two modify commands with old events the same is equivalent to just one modify command. // Replace the existing ModifyEvent command's new event with the requested ModifyEvent command's new event. uo.nEvent = n_op.nEvent; return; } } // REMOVE Tim. citem. Added. Remove. I think we CAN replace two different events with the same event. //else if(uo.nEvent == n_op.nEvent) //{ // // Cannot replace two different events with the same event. // fprintf(stderr, "MusE error: Undo::insert(): Double ModifyEvent: different old events but same new event. Ignoring.\n"); // return; //} // Are inner new/old pair the same event? else if(uo.nEvent == n_op.oEvent) { // Are outer old/new pair the same event? if(uo.oEvent == n_op.nEvent) { // First ModifyEvent old event and second ModifyEvent new event are both the same, equivalent to doing nothing. // Cancel out the two ModifyEvent operations by erasing the existing ModifyEvent command. erase(iuo); return; } else { // For testing... //fprintf(stderr, "MusE: Undo::insert(): Double ModifyEvent. Inner new/old pair same, outer old/new pair not same. Merging to one ModifyEvent.\n"); // Inner new/old pair are the same event and outer old/new pair are not the same event. // A modify command with new event followed by a modify command with old event the same // is equivalent to just one modify command. Replace the existing ModifyEvent command's // new event with the requested ModifyEvent command's new event. uo.nEvent = n_op.nEvent; return; } } // Inner new/old pair are not the same event. Are outer old/new pair the same event? else if(uo.oEvent == n_op.nEvent) { // For testing... //fprintf(stderr, "MusE: Undo::insert(): Double ModifyEvent. Inner new/old pair not same," // " outer old/new pair same. Transforming to Add and Delete.\n"); // Transform the existing ModifyEvent operation into an AddEvent. uo.type = UndoOp::AddEvent; // Transform the requested ModifyEvent operation into a DeleteEvent. n_op.type = UndoOp::DeleteEvent; n_op.nEvent = n_op.oEvent; // Allow it to add... } } else if(uo.type == UndoOp::AddEvent && uo.part == n_op.part) { // For testing... //fprintf(stderr, "MusE: Undo::insert(): AddEvent then ModifyEvent...\n"); if(uo.nEvent == n_op.oEvent) { // For testing... //fprintf(stderr, "MusE: Undo::insert(): AddEvent then ModifyEvent. Same event. Merging to AddEvent.\n"); // Add followed by modify with old event same as added event, is equivalent to just adding modify's new event. // Replace the existing AddEvent command's event with the requested ModifyEvent command's new event. uo.nEvent = n_op.nEvent; return; } if(uo.nEvent == n_op.nEvent) { // Add followed by modify with new event same as added event, is a caller error. fprintf(stderr, "MusE error: Undo::insert(): AddEvent, then ModifyEvent same new event (double AddEvent). Ignoring.\n"); return; } } if(uo.type == UndoOp::DeleteEvent && uo.part == n_op.part) { if(uo.nEvent == n_op.oEvent) { // Delete followed by modify with old event same as deleted event, is an error. fprintf(stderr, "MusE error: Undo::insert(): DeleteEvent, then ModifyEvent same old event (double DeleteEvent). Ignoring.\n"); return; } if(uo.nEvent == n_op.nEvent) { // For testing... //fprintf(stderr, "MusE: Undo::insert(): DeleteEvent then ModifyEvent. Same event. Merging to DeleteEvent.\n"); // Delete followed by modify with new event same as deleted event, is equivalent to just deleting modify's old event. // Replace the existing DeleteEvent command's event with the requested ModifyEvent command's old event. uo.nEvent = n_op.oEvent; } } break; case UndoOp::AddAudioCtrlVal: if(uo.type == UndoOp::AddAudioCtrlVal && uo.track == n_op.track && uo._audioCtrlID == n_op._audioCtrlID && uo._audioCtrlFrame == n_op._audioCtrlFrame) { // Simply replace the original value and frame. uo._audioCtrlVal = n_op._audioCtrlVal; return; } // TODO If possible. // else if(uo.type == UndoOp::DeleteAudioCtrlVal && uo.track == n_op.track && // uo._audioCtrlID == n_op._audioCtrlID && // uo._audioCtrlFrame == n_op._audioCtrlFrame) // { // // Delete followed by add, at the same frame. Transform the delete into a modify. // uo.type = UndoOp::ModifyAudioCtrlVal; // uo._audioCtrlVal = n_op._audioCtrlVal; // uo._audioNewCtrlFrame = // return; // } break; case UndoOp::DeleteAudioCtrlVal: if(uo.type == UndoOp::DeleteAudioCtrlVal && uo.track == n_op.track && uo._audioCtrlID == n_op._audioCtrlID && uo._audioCtrlFrame == n_op._audioCtrlFrame) { fprintf(stderr, "MusE error: Undo::insert(): Double DeleteAudioCtrlVal. Ignoring.\n"); return; } else if(uo.type == UndoOp::AddAudioCtrlVal && uo.track == n_op.track && uo._audioCtrlID == n_op._audioCtrlID && uo._audioCtrlFrame == n_op._audioCtrlFrame) { // Add followed by delete, at the same frame, is useless. Cancel out the add + delete by erasing the add command. erase(iuo); return; } break; case UndoOp::ModifyAudioCtrlVal: if(uo.type == UndoOp::ModifyAudioCtrlVal && uo.track == n_op.track && uo._audioCtrlID == n_op._audioCtrlID && uo._audioNewCtrlFrame == n_op._audioCtrlFrame) { // Simply replace the original new value and new frame. uo._audioNewCtrlVal = n_op._audioNewCtrlVal; uo._audioNewCtrlFrame = n_op._audioNewCtrlFrame; return; } break; case UndoOp::ModifyAudioCtrlValList: // Check the sanity of the requested op. if(n_op._eraseCtrlList == n_op._addCtrlList) { fprintf(stderr, "MusE error: Undo::insert(): ModifyAudioCtrlValList: Erase and add lists are the same. Ignoring.\n"); return; } if(uo.type == UndoOp::ModifyAudioCtrlValList) { if(uo._ctrlListList == n_op._ctrlListList) { if(uo._addCtrlList == n_op._addCtrlList && uo._eraseCtrlList == n_op._eraseCtrlList) { fprintf(stderr, "MusE error: Undo::insert(): Double ModifyAudioCtrlValList. Ignoring.\n"); return; } else if(uo._addCtrlList == n_op._eraseCtrlList) { // Delete the existing ModifyAudioCtrlValList command's _addCtrlList and replace it // with the requested ModifyAudioCtrlValList command's _addCtrlList. if(uo._addCtrlList) delete uo._addCtrlList; uo._addCtrlList = n_op._addCtrlList; return; } } // Seems possible... remove? But maybe dangerous to have two undo ops pointing to the same lists - they will be self-deleted. else { if(uo._addCtrlList == n_op._addCtrlList) { fprintf(stderr, "MusE error: Undo::insert(): ModifyAudioCtrlValList: Attempting to add same list to different containers. Ignoring.\n"); return; } else if(uo._eraseCtrlList == n_op._eraseCtrlList) { fprintf(stderr, "MusE error: Undo::insert(): ModifyAudioCtrlValList: Attempting to erase same list from different containers. Ignoring.\n"); return; } } } break; case UndoOp::SetInstrument: // Check the sanity of the requested op. if(n_op._oldMidiInstrument == n_op._newMidiInstrument) { fprintf(stderr, "MusE error: Undo::insert(): SetInstrument: Old and new instruments are the same. Ignoring.\n"); return; } if(uo.type == UndoOp::SetInstrument) { if(uo._midiPort == n_op._midiPort) { if(uo._oldMidiInstrument == n_op._oldMidiInstrument && uo._newMidiInstrument == n_op._newMidiInstrument) { fprintf(stderr, "MusE error: Undo::insert(): Double SetInstrument. Ignoring.\n"); return; } else if(uo._newMidiInstrument == n_op._oldMidiInstrument) { // Replace the existing SetInstrument command's _newMidiInstrument // with the requested ModifyAudioCtrlValList command's _newMidiInstrument. uo._newMidiInstrument = n_op._newMidiInstrument; return; } } } break; case UndoOp::AddTempo: if(uo.type == UndoOp::AddTempo && uo.a == n_op.a) { // Simply replace the value. uo.b = n_op.b; return; } else if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a) { // Delete followed by add. Transform the existing DeleteTempo operation into a ModifyTempo. uo.type = UndoOp::ModifyTempo; // a is already the tick, b is already the existing value from DeleteTempo, c is the new value. uo.c = n_op.b; return; } else if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a) { // Modify followed by add. Simply replace the value. // a is already the tick, b is already the existing value from ModifyTempo, c is the new value. uo.c = n_op.b; return; } // else if(uo.type == UndoOp::SetTempo && uo.a == n_op.a) // { // // Only if the master is on. // if(MusEGlobal::tempomap.masterFlag()) // { // // Simply replace the value. // uo.b = n_op.b; // return; // } // } break; case UndoOp::DeleteTempo: if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a) { fprintf(stderr, "MusE error: Undo::insert(): Double DeleteTempo. Ignoring.\n"); return; } else if(uo.type == UndoOp::AddTempo && uo.a == n_op.a) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(iuo); return; } else if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a) { // Modify followed by delete. Equivalent to delete. Transform existing ModifyTempo operation into a DeleteTempo. uo.type = UndoOp::DeleteTempo; // a is already the tick, b is already the existing old value from ModifyTempo. return; } // else if(uo.type == UndoOp::SetTempo && uo.a == n_op.a) // { // // Only if the master is on. // if(MusEGlobal::tempomap.masterFlag()) // { // // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. // erase(iuo); // return; // } // } break; case UndoOp::ModifyTempo: if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a) { // Simply replace c with the new value. uo.c = n_op.c; return; } else if(uo.type == UndoOp::AddTempo && uo.a == n_op.a) { // Add followed by modify. Simply replace the add value. uo.b = n_op.c; return; } else if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a) { // Delete followed by modify. Equivalent to modify. Transform existing DeleteTempo operation into a ModifyTempo. uo.type = UndoOp::ModifyTempo; // a is already the tick, b is already the existing value from DeleteTempo. c is the new value from ModifyTempo. uo.c = n_op.c; return; } // else if(uo.type == UndoOp::SetTempo && uo.a == n_op.a) // { // // Only if the master is on. // if(MusEGlobal::tempomap.masterFlag()) // { // // Add followed by modify. Simply replace the add value. // uo.b = n_op.c; // return; // } // } break; // case UndoOp::SetTempo: // if(uo.type == UndoOp::SetTempo && uo.a == n_op.a) // { // // Simply replace the value. // uo.b = n_op.b; // return; // } // else if(uo.type == UndoOp::AddTempo && uo.a == n_op.a) // { // // Simply replace the value. // uo.b = n_op.b; // return; // } // else if(uo.type == UndoOp::DeleteTempo && uo.a == n_op.a) // { // // Only if the master is on. // if(MusEGlobal::tempomap.masterFlag()) // { // // Delete followed by add. Transform the existing DeleteTempo operation into a ModifyTempo. // uo.type = UndoOp::ModifyTempo; // // a is already the tick, b is already the existing value from DeleteTempo, c is the new value. // uo.c = n_op.b; // return; // } // } // else if(uo.type == UndoOp::ModifyTempo && uo.a == n_op.a) // { // // Only if the master is on. // if(MusEGlobal::tempomap.masterFlag()) // { // // Modify followed by add. Simply replace the value. // // a is already the tick, b is already the existing value from ModifyTempo, c is the new value. // uo.c = n_op.b; // return; // } // } // else if(uo.type == UndoOp::SetStaticTempo && uo.a == n_op.a) // { // // Only if the master is not on. // if(!MusEGlobal::tempomap.masterFlag()) // { // // Simply replace the value. // uo.b = n_op.b; // return; // } // } // break; case UndoOp::SetStaticTempo: if(uo.type == UndoOp::SetStaticTempo) { // Simply replace a with the new value. uo.a = n_op.a; return; } break; case UndoOp::SetGlobalTempo: if(uo.type == UndoOp::SetGlobalTempo) { // Simply replace a with the new value. uo.a = n_op.a; return; } break; case UndoOp::EnableMasterTrack: if(uo.type == UndoOp::EnableMasterTrack) { if(uo.a == n_op.a) { fprintf(stderr, "MusE error: Undo::insert(): Double EnableMasterTrack. Ignoring.\n"); return; } else { // Toggling is useless. Cancel out the enable or disable + disable or enable by erasing the disable or enable command. erase(iuo); return; } } break; case UndoOp::AddSig: if(uo.type == UndoOp::AddSig && uo.a == n_op.a) { // Simply replace the value. uo.b = n_op.b; uo.c = n_op.c; return; } else if(uo.type == UndoOp::DeleteSig && uo.a == n_op.a) { // Delete followed by add. Transform the existing DeleteSig operation into a ModifySig. uo.type = UndoOp::ModifySig; // a is already the tick, b + c is already the existing value from DeleteSig, d + e is the new value. uo.d = n_op.b; uo.e = n_op.c; return; } else if(uo.type == UndoOp::ModifySig && uo.a == n_op.a) { // Modify followed by add. Simply replace the value. // a is already the tick, b + c is already the existing value from ModifySig, d + e is the new value. uo.d = n_op.b; uo.e = n_op.c; return; } break; case UndoOp::DeleteSig: if(uo.type == UndoOp::DeleteSig && uo.a == n_op.a) { fprintf(stderr, "MusE error: Undo::insert(): Double DeleteSig. Ignoring.\n"); return; } else if(uo.type == UndoOp::AddSig && uo.a == n_op.a) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(iuo); return; } else if(uo.type == UndoOp::ModifySig && uo.a == n_op.a) { // Modify followed by delete. Equivalent to delete. Transform existing ModifySig operation into a DeleteSig. uo.type = UndoOp::DeleteSig; // a is already the tick, b + c is already the existing old value from ModifySig. return; } break; case UndoOp::ModifySig: if(uo.type == UndoOp::ModifySig && uo.a == n_op.a) { // Simply replace d + e with the new value. uo.d = n_op.d; uo.e = n_op.e; return; } else if(uo.type == UndoOp::AddSig && uo.a == n_op.a) { // Add followed by modify. Simply replace the add value. uo.b = n_op.d; uo.c = n_op.e; return; } else if(uo.type == UndoOp::DeleteSig && uo.a == n_op.a) { // Delete followed by modify. Equivalent to modify. Transform existing DeleteSig operation into a ModifySig. uo.type = UndoOp::ModifySig; // a is already the tick, b + c is already the existing value from DeleteSig. d + e is the new value from ModifySig. uo.d = n_op.d; uo.e = n_op.e; return; } break; case UndoOp::AddKey: if(uo.type == UndoOp::AddKey && uo.a == n_op.a) { // Simply replace the value. uo.b = n_op.b; uo.c = n_op.c; return; } else if(uo.type == UndoOp::DeleteKey && uo.a == n_op.a) { // Delete followed by add. Transform the existing DeleteKey operation into a ModifyKey. uo.type = UndoOp::ModifyKey; // a is already the tick, b + c is already the existing value from DeleteKey, d + e is the new value. uo.d = n_op.b; uo.e = n_op.c; return; } else if(uo.type == UndoOp::ModifyKey && uo.a == n_op.a) { // Modify followed by add. Simply replace the value. // a is already the tick, b + c is already the existing value from ModifyKey, d + e is the new value. uo.d = n_op.b; uo.e = n_op.c; return; } break; case UndoOp::DeleteKey: if(uo.type == UndoOp::DeleteKey && uo.a == n_op.a) { fprintf(stderr, "MusE error: Undo::insert(): Double DeleteKey. Ignoring.\n"); return; } else if(uo.type == UndoOp::AddKey && uo.a == n_op.a) { // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(iuo); return; } else if(uo.type == UndoOp::ModifyKey && uo.a == n_op.a) { // Modify followed by delete. Equivalent to delete. Transform existing ModifyKey operation into a DeleteKey. uo.type = UndoOp::DeleteKey; // a is already the tick, b + c is already the existing old value from ModifyKey. return; } break; case UndoOp::ModifyKey: if(uo.type == UndoOp::ModifyKey && uo.a == n_op.a) { // Simply replace d + e with the new value. uo.d = n_op.d; uo.e = n_op.e; return; } else if(uo.type == UndoOp::AddKey && uo.a == n_op.a) { // Add followed by modify. Simply replace the add value. uo.b = n_op.d; uo.c = n_op.e; return; } else if(uo.type == UndoOp::DeleteKey && uo.a == n_op.a) { // Delete followed by modify. Equivalent to modify. Transform existing DeleteSig operation into a ModifySig. uo.type = UndoOp::ModifyKey; // a is already the tick, b + c is already the existing value from DeleteKey. d + e is the new value from ModifyKey. uo.d = n_op.d; uo.e = n_op.e; return; } break; case UndoOp::ModifySongLen: if(uo.type == UndoOp::ModifySongLen) { // Simply replace a with the new value. uo.a = n_op.a; return; } break; case UndoOp::ModifyMidiDivision: if(uo.type == UndoOp::ModifyMidiDivision) { // Simply replace a with the new value. uo.a = n_op.a; return; } break; case UndoOp::EnableAllAudioControllers: if(uo.type == UndoOp::EnableAllAudioControllers) { fprintf(stderr, "MusE error: Undo::insert(): Double EnableAllAudioControllers. Ignoring.\n"); return; } break; case UndoOp::NormalizeMidiDivision: if(uo.type == UndoOp::NormalizeMidiDivision) { fprintf(stderr, "MusE error: Undo::insert(): Double NormalizeMidiDivision. Ignoring.\n"); return; } break; case UndoOp::GlobalSelectAllEvents: if(uo.type == UndoOp::GlobalSelectAllEvents) { if(uo.a == n_op.a) { fprintf(stderr, "MusE error: Undo::insert(): Double GlobalSelectAllEvents. Ignoring.\n"); return; } else { // Special: Do not 'cancel' out this one. The selecions may need to affect all events. // Simply replace a with the new value. uo.a = n_op.a; return; } } break; case UndoOp::AddMarker: if(uo.type == UndoOp::AddMarker && uo.newMarker->id() == n_op.newMarker->id()) { // Done with older operation marker. Be sure to delete it. delete uo.newMarker; // Simply replace the existing new marker with the newer marker. uo.newMarker = n_op.newMarker; return; } else if(uo.type == UndoOp::DeleteMarker && uo.oldMarker->id() == n_op.newMarker->id()) { // Delete followed by add. Transform the existing DeleteMarker operation into a ModifyMarker. uo.type = UndoOp::ModifyMarker; // Move the new marker into the ModifyMarker command's new marker. // Keep the existing DeleteMarker command's oldMarker. uo.newMarker = n_op.newMarker; return; } break; case UndoOp::DeleteMarker: if(uo.type == UndoOp::DeleteMarker && uo.oldMarker->id() == n_op.oldMarker->id()) { // Done with older operation marker. Be sure to delete it. delete uo.oldMarker; // Simply replace the existing new marker with the newer marker. uo.oldMarker = n_op.oldMarker; return; } else if(uo.type == UndoOp::AddMarker && uo.newMarker->id() == n_op.oldMarker->id()) { // Done with operation markers. Be sure to delete them. delete uo.oldMarker; delete n_op.newMarker; // Add followed by delete is useless. Cancel out the add + delete by erasing the add command. erase(iuo); return; } break; case UndoOp::ModifyMarker: if(uo.type == UndoOp::ModifyMarker && uo.oldMarker->id() == n_op.oldMarker->id()) { // Done with these operation markers. Be sure to delete them. delete uo.newMarker; delete n_op.oldMarker; // Simply replace the older operation marker with the newer one. uo.newMarker = n_op.newMarker; return; } break; case UndoOp::SetMarkerPos: if(uo.type == UndoOp::SetMarkerPos && uo.oldMarker->id() == n_op.oldMarker->id()) { // Done with these operation markers. Be sure to delete them. delete uo.newMarker; delete n_op.oldMarker; // Simply replace the older operation marker with the newer one. uo.newMarker = n_op.newMarker; return; } break; // NOTE Some other undo op types may need treatment as well ! default: break; } } } std::list::insert(position, n_op); } bool Undo::merge_combo(const Undo& other) { if (other.combobreaker) return false; int has_other=0x01; int has_select_event=0x02; int has_select_part=0x04; int has_modify_aud_ctrl_val=0x08; int has_set_marker_pos=0x10; int has = 0; for (ciUndoOp op=this->begin(); op!=this->end(); op++) switch(op->type) { case UndoOp::DoNothing: break; case UndoOp::SelectEvent: has |= has_select_event; break; case UndoOp::SelectPart: has |= has_select_part; break; case UndoOp::ModifyAudioCtrlVal: has |= has_modify_aud_ctrl_val; break; case UndoOp::SetMarkerPos: has |= has_set_marker_pos; break; default: has |= has_other; break; } for (ciUndoOp op=other.begin(); op!=other.end(); op++) switch(op->type) { case UndoOp::DoNothing: break; case UndoOp::SelectEvent: has |= has_select_event; break; case UndoOp::SelectPart: has |= has_select_part; break; case UndoOp::ModifyAudioCtrlVal: has |= has_modify_aud_ctrl_val; break; case UndoOp::SetMarkerPos: has |= has_set_marker_pos; break; default: has |= has_other; break; } bool mergeable = (has == has_select_event || has == has_select_part || has == has_modify_aud_ctrl_val || has == has_set_marker_pos); if (mergeable) this->insert(this->end(), other.begin(), other.end()); return mergeable; } bool Song::applyOperation(const UndoOp& op, OperationType type, void* sender) { Undo operations; operations.push_back(op); return applyOperationGroup(operations, type, sender); } bool Song::applyOperationGroup(Undo& group, OperationType type, void* sender) { bool ret = false; if (!group.empty()) { // We don't use this here in applyOperationGroup or its call sequence. undoMode = false; switch(type) { case OperationExecute: case OperationUndoable: break; case OperationExecuteUpdate: case OperationUndoableUpdate: case OperationUndoMode: // Clear the updateFlags and set sender. updateFlags = SongChangedStruct_t(0, 0, sender); break; } // Execute the given operations. This can add or remove operations in the group. MusEGlobal::audio->msgExecuteOperationGroup(group); // Check whether there are actually any undoable operations in the group. // There shouldn't be any non-undoables left in the list, they are removed at execution, // but we'll double check here which also checks list emptiness. bool has_undoables = false; for(ciUndoOp iu = group.cbegin(); iu != group.cend(); ++iu) { if(!iu->_noUndo) { has_undoables = true; break; } } switch(type) { case OperationExecute: case OperationExecuteUpdate: break; case OperationUndoMode: // NOTE: If there are only non-undoables, there is NOTHING to redo (or undo). // Prevent one-time non-undoable operations from wiping out the redo list! if(has_undoables) { // The following does the same as startUndo but without clearing the updateFlags: // redo must be invalidated when a new undo is started redoList->clearDelete(); MusEGlobal::redoAction->setEnabled(false); setUndoRedoText(); undoList->push_back(Undo()); } // FALLTHROUGH case OperationUndoable: case OperationUndoableUpdate: // append all elements from "group" to the end of undoList->back(). // Only if there are undoable items. if(has_undoables && !undoList->empty()) { Undo& curUndo = undoList->back(); curUndo.insert(curUndo.end(), group.begin(), group.end()); if (group.combobreaker) curUndo.combobreaker=true; } break; } switch(type) { case OperationExecute: case OperationUndoable: break; case OperationExecuteUpdate: case OperationUndoableUpdate: emit songChanged(updateFlags); break; case OperationUndoMode: if(has_undoables) { // Also emits songChanged and resets undoMode. endUndo(0); ret = true; } else { emit songChanged(updateFlags); } break; } } return ret; } //--------------------------------------------------------- // revertOperationGroup2 // real time part //--------------------------------------------------------- void Song::revertOperationGroup2(Undo& /*operations*/) { pendingOperations.executeRTStage(); // Special for tempo: Need to normalize the tempo list, and resync audio. // To save time this is done here, not item by item. // Normalize is not needed for SC_MASTER. if(updateFlags & (SC_TEMPO | SC_DIVISION_CHANGED)) MusEGlobal::tempomap.normalize(); if(updateFlags & (SC_TEMPO | SC_MASTER | SC_DIVISION_CHANGED)) { MusEGlobal::audio->reSyncAudio(); // Must rebuild the marker list in case any markers are 'locked'. if(marker()->rebuild()) updateFlags |= SC_MARKERS_REBUILT; } // Special for sig: Need to normalize the signature list. // To save time this is done here, not item by item. if(updateFlags & (SC_SIG | SC_DIVISION_CHANGED)) MusEGlobal::sigmap.normalize(); // Special for track inserted: If it's an aux track, need to add missing aux sends to all tracks, // else if it's another audio track need to add aux sends to it. // To save from complexity this is done here, after all the operations. if(updateFlags & SC_TRACK_INSERTED) { int n = _auxs.size(); for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i) { if((*i)->isMidiTrack()) continue; MusECore::AudioTrack* at = static_cast(*i); if(at->hasAuxSend()) at->addAuxSend(n); } } } //--------------------------------------------------------- // Song::executeOperationGroup2 //--------------------------------------------------------- void Song::executeOperationGroup2(Undo& /*operations*/) { pendingOperations.executeRTStage(); // Special for tempo if altered: Need to normalize the tempo list, and resync audio. // To save time this is done here, not item by item. // Normalize is not needed for SC_MASTER. if(updateFlags & (SC_TEMPO | SC_DIVISION_CHANGED)) MusEGlobal::tempomap.normalize(); if(updateFlags & (SC_TEMPO | SC_MASTER | SC_DIVISION_CHANGED)) { MusEGlobal::audio->reSyncAudio(); // Must rebuild the marker list in case any markers are 'locked'. if(marker()->rebuild()) updateFlags |= SC_MARKERS_REBUILT; } // Special for sig: Need to normalize the signature list. // To save time this is done here, not item by item. if(updateFlags & (SC_SIG | SC_DIVISION_CHANGED)) MusEGlobal::sigmap.normalize(); // Special for track inserted: If it's an aux track, need to add missing aux sends to all tracks, // else if it's another audio track need to add aux sends to it. // To save from complexity this is done here, after all the operations. if(updateFlags & SC_TRACK_INSERTED) { int n = _auxs.size(); for(iTrack i = _tracks.begin(); i != _tracks.end(); ++i) { if((*i)->isMidiTrack()) continue; MusECore::AudioTrack* at = static_cast(*i); if(at->hasAuxSend()) at->addAuxSend(n); } } } UndoOp::UndoOp() { type=UndoOp::DoNothing; _noUndo = true; } UndoOp::UndoOp(UndoType type_, int a_, int b_, int c_, bool noUndo) { assert(type_==AddKey || type_==DeleteKey || type_== ModifyKey || type_==AddTempo || type_==DeleteTempo || type_==ModifyTempo || type_==SetTempo || type_==SetStaticTempo || type_==SetGlobalTempo || type_==EnableMasterTrack || type_==AddSig || type_==DeleteSig || type_==ModifySongLen || type_==MoveTrack || type_==GlobalSelectAllEvents || type_==ModifyMidiDivision); type = type_; a = a_; b = b_; c = c_; _noUndo = noUndo; switch(type) { case UndoOp::SetGlobalTempo: // a is already the new tempo, b is the existing tempo. b = MusEGlobal::tempomap.globalTempo(); break; case UndoOp::EnableMasterTrack: // a is already the new master flag, b is the existing master flag. b = MusEGlobal::tempomap.masterFlag(); break; case UndoOp::ModifyMidiDivision: // a is already the new division, b is the existing division. b = MusEGlobal::config.division; break; // For these operations, we must check if a value already exists and transform them into modify operations... case UndoOp::AddTempo: { int t = a; if(t > MAX_TICK) t = MAX_TICK; iTEvent ite = MusEGlobal::tempomap.upper_bound(t); if((int)ite->second->tick == t) { // Transform the AddTempo operation into a ModifyTempo. // a is already the tick, b is the existing value, c is the new value. type = UndoOp::ModifyTempo; c = b; b = ite->second->tempo; } } break; case UndoOp::SetTempo: { // Only if the master is on. if(MusEGlobal::tempomap.masterFlag()) { int t = a; if(t > MAX_TICK) t = MAX_TICK; iTEvent ite = MusEGlobal::tempomap.upper_bound(t); if((int)ite->second->tick == t) { // Transform the SetTempo operation into a ModifyTempo. // a is already the tick, b is the existing value, c is the new value. type = UndoOp::ModifyTempo; c = b; b = ite->second->tempo; } else { // Transform the SetTempo operation into an AddTempo. type = UndoOp::AddTempo; } } else { // a is the new tempo, b is the existing tempo. a = b; b = MusEGlobal::tempomap.staticTempo(); // Transform the SetTempo operation into a SetStaticTempo. type = UndoOp::SetStaticTempo; } } break; case UndoOp::SetStaticTempo: // a is already the new tempo, b is the existing tempo. b = MusEGlobal::tempomap.staticTempo(); break; case UndoOp::AddSig: { //if(t > MAX_TICK) // t = MAX_TICK; // Must rasterize the tick value HERE instead of in SigMap::addOperation(), // so that the rasterized value is recorded in the undo item. a = MusEGlobal::sigmap.raster1(a, 0); MusECore::iSigEvent ise = MusEGlobal::sigmap.upper_bound(a); if((int)ise->second->tick == a) { // Transform the AddSig operation into a ModifySig. // a is already the tick, b + c is the existing value, d + e is the new value. type = UndoOp::ModifySig; d = b; e = c; b = ise->second->sig.z; c = ise->second->sig.n; } } break; case UndoOp::AddKey: { int t = a; if(t > MAX_TICK) t = MAX_TICK; iKeyEvent ike = MusEGlobal::keymap.upper_bound(t); if((int)ike->second.tick == t) { // Transform the AddKey operation into a ModifyKey. // a is already the tick, b + c is the existing value, d + e is the new value. type = UndoOp::ModifyKey; d = b; e = c; b = ike->second.key; c = ike->second.minor; } } break; default: break; } } UndoOp::UndoOp(UndoType type_, int tick, const MusECore::TimeSignature old_sig, const MusECore::TimeSignature new_sig, bool noUndo) { assert(type_==ModifySig); type = type_; a = tick; b = old_sig.z; c = old_sig.n; d = new_sig.z; e = new_sig.n; _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, int n, const Track* track_, bool noUndo) { assert(type_==AddTrack || type_==DeleteTrack); assert(track_); type = type_; trackno = n; track = track_; _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, const Track* track_, bool value, bool noUndo) { assert(type_ == SetTrackRecord || type_ == SetTrackMute || type_ == SetTrackSolo || type_ == SetTrackRecMonitor || type_ == SetTrackOff); assert(track_); type = type_; track = track_; a = value; _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, const Part* part_, bool noUndo) { assert(type_==AddPart || type_==DeletePart); assert(part_); type = type_; part = part_; _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, const Part* part_, bool selected_, bool sel_old_, bool noUndo) { assert(type_==SelectPart); assert(part_); type=type_; part = part_; selected=selected_; selected_old=sel_old_; _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, const Part* part_, unsigned int old_len_or_pos, unsigned int new_len_or_pos, Pos::TType new_time_type_, const Track* oTrack, const Track* nTrack, bool noUndo) { assert(type_== MovePart); assert(part_); type = type_; part = part_; _noUndo = noUndo; track = nTrack; oldTrack = oTrack; // Make sure both tracks exist. if(!track && !oldTrack) track = oldTrack = part->track(); else if(!oldTrack) oldTrack = track; else if(!track) track = oldTrack; assert(oldTrack); assert(track); old_partlen_or_pos = old_len_or_pos; new_partlen_or_pos = new_len_or_pos; switch(part->type()) { case Pos::FRAMES: switch(new_time_type_) { case Pos::FRAMES: break; case Pos::TICKS: new_partlen_or_pos = MusEGlobal::tempomap.tick2frame(new_partlen_or_pos); break; } break; case Pos::TICKS: switch(new_time_type_) { case Pos::FRAMES: new_partlen_or_pos = MusEGlobal::tempomap.frame2tick(new_partlen_or_pos); break; case Pos::TICKS: break; } break; } } UndoOp::UndoOp(UndoType type_, const Part* part_, unsigned int old_pos, unsigned int new_pos, unsigned int old_len, unsigned int new_len, int64_t events_offset_, Pos::TType new_time_type_, bool noUndo) { assert(type_ == ModifyPartStart); assert(part_); type = type_; part = part_; _noUndo = noUndo; events_offset = events_offset_; events_offset_time_type = new_time_type_; old_partlen_or_pos = old_pos; new_partlen_or_pos = new_pos; old_partlen = old_len; new_partlen = new_len; } UndoOp::UndoOp(UndoType type_, const Part* part_, unsigned int old_len, unsigned int new_len, int64_t events_offset_, Pos::TType new_time_type_, bool noUndo) { assert(type_== ModifyPartLength); assert(part_); type = type_; part = part_; _noUndo = noUndo; events_offset = events_offset_; events_offset_time_type = new_time_type_; old_partlen_or_pos = old_len; new_partlen_or_pos = new_len; } UndoOp::UndoOp(UndoType type_, const Event& nev, const Event& oev, const Part* part_, bool doCtrls_, bool doClones_, bool noUndo) { assert(type_==ModifyEvent); assert(part_); type = type_; nEvent = nev; oEvent = oev; part = part_; doCtrls = doCtrls_; doClones = doClones_; _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, const Event& nev, const Part* part_, bool a_, bool b_, bool noUndo) { assert(type_==DeleteEvent || type_==AddEvent || type_==SelectEvent); assert(part_); type = type_; nEvent = nev; part = part_; _noUndo = noUndo; if(type_==SelectEvent) { selected = a_; selected_old = b_; } else { doCtrls = a_; doClones = b_; } } UndoOp::UndoOp(UndoType type_, const Marker& oldMarker_, const Marker& newMarker_, bool noUndo) { assert(type_==ModifyMarker); type = type_; oldMarker = new Marker(oldMarker_); newMarker = new Marker(newMarker_); _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, const Marker& marker_, bool noUndo) { assert(type_==AddMarker || type_==DeleteMarker); type = type_; oldMarker = newMarker = nullptr; Marker** mp = nullptr; if(type_== AddMarker) mp = &newMarker; else mp = &oldMarker; *mp = new Marker(marker_); _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, const Marker& marker_, unsigned int new_pos, Pos::TType new_time_type, bool noUndo) { assert(type_==SetMarkerPos); type = type_; oldMarker = new Marker(marker_); newMarker = new Marker(marker_); newMarker->setPosValue(new_pos, new_time_type); _noUndo = noUndo; } // UndoOp::UndoOp(UndoType type_, MarkerList** oldMarkerList_, MarkerList* newMarkerList_, bool noUndo) // { // assert(type_==ModifyMarkerList); // assert(oldMarkerList); // assert(newMarkerList); // type = type_; // oldMarkerList = oldMarkerList_; // newMarkerList = newMarkerList_; // _noUndo = noUndo; // } UndoOp::UndoOp(UndoType type_, const Event& changedEvent, const QString& changeData, int startframe_, int endframe_, bool noUndo) { assert(type_==ModifyClip); type = type_; _noUndo = noUndo; //filename = new QString(changedFile); nEvent = changedEvent; tmpwavfile = new QString(changeData); startframe = startframe_; endframe = endframe_; } UndoOp::UndoOp(UndoOp::UndoType type_, const Part* part_, const QString& old_name, const QString& new_name, bool noUndo) { assert(type_==ModifyPartName); assert(part_); type=type_; part=part_; _noUndo = noUndo; _oldName = new QString(old_name); _newName = new QString(new_name); } UndoOp::UndoOp(UndoOp::UndoType type_, const Track* track_, const QString& old_name, const QString& new_name, bool noUndo) { assert(type_==ModifyTrackName); assert(track_); type = type_; track = track_; _noUndo = noUndo; _oldName = new QString(old_name); _newName = new QString(new_name); } UndoOp::UndoOp(UndoOp::UndoType type_, const Track* track_, int oldChanOrCtrlID, int newChanOrCtrlFrame, bool noUndo) { assert(type_ == ModifyTrackChannel || type_ == DeleteAudioCtrlVal); assert(track_); type = type_; track = track_; if(type_ == ModifyTrackChannel) { _propertyTrack = track_; _oldPropValue = oldChanOrCtrlID; _newPropValue = newChanOrCtrlFrame; } else { _audioCtrlID = oldChanOrCtrlID; _audioCtrlFrame = newChanOrCtrlFrame; } _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, const Track* track_, int ctrlID, int frame, double value, bool noUndo) { assert(type_== AddAudioCtrlVal); assert(track_); type = type_; track = track_; _audioCtrlID = ctrlID; _audioCtrlFrame = frame; _audioCtrlVal = value; _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, const Track* track_, int ctrlID, int oldFrame, int newFrame, double oldValue, double newValue, bool noUndo) { assert(type_== ModifyAudioCtrlVal); assert(track_); type = type_; track = track_; _audioCtrlID = ctrlID; _audioCtrlFrame = oldFrame; _audioNewCtrlFrame = newFrame; _audioCtrlVal = oldValue; _audioNewCtrlVal = newValue; _noUndo = noUndo; } UndoOp::UndoOp(UndoOp::UndoType type_, CtrlListList* ctrl_ll, CtrlList* eraseCtrlList, CtrlList* addCtrlList, bool noUndo) { assert(type_== ModifyAudioCtrlValList); assert(ctrl_ll); //assert(eraseCtrlList); //assert(addCtrlList); assert(eraseCtrlList || addCtrlList); type = type_; _ctrlListList = ctrl_ll; _eraseCtrlList = eraseCtrlList; _addCtrlList = addCtrlList; _noUndo = noUndo; } UndoOp::UndoOp(UndoType type_, MidiPort* mp, MidiInstrument* instr, bool noUndo) { assert(type_== SetInstrument); assert(mp); assert(instr); type = type_; _midiPort = mp; _oldMidiInstrument = _midiPort->instrument(); _newMidiInstrument = instr; _noUndo = noUndo; } UndoOp::UndoOp(UndoOp::UndoType type_) { assert(type_== EnableAllAudioControllers || type_ == NormalizeMidiDivision); type = type_; // Cannot be undone. 'One-time' operation only, removed after execution. _noUndo = true; } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" UndoOp::UndoOp(UndoOp::UndoType type_, const Route& route_from_, const Route& route_to_, bool noUndo) { assert(type_ == AddRoute || type_ == DeleteRoute); _noUndo = noUndo; routeFrom = route_from_; routeTo = route_to_; } #pragma GCC diagnostic pop //--------------------------------------------------------- // addUndo //--------------------------------------------------------- void Song::addUndo(UndoOp i) { if (!undoMode) { printf("internal error: undoOp without startUndo()\n"); return; } undoList->back().push_back(i); emit sigDirty(); } //--------------------------------------------------------- // revertOperationGroup1 // non realtime context // return true if nothing to do //--------------------------------------------------------- void Song::revertOperationGroup1(Undo& operations) { MarkerList* new_marker_list = nullptr; TempoList* new_tempo_list = nullptr; SigList* new_sig_list = nullptr; KeyList* new_key_list = nullptr; for (riUndoOp i = operations.rbegin(); i != operations.rend(); ++i) { Track* editable_track = const_cast(i->track); Track* editable_property_track = const_cast(i->_propertyTrack); Part* editable_part = const_cast(i->part); switch(i->type) { case UndoOp::SelectPart: pendingOperations.add(PendingOperationItem(editable_part, i->selected_old, PendingOperationItem::SelectPart)); updateFlags |= SC_PART_SELECTION; break; case UndoOp::SelectEvent: pendingOperations.add(PendingOperationItem(editable_part, i->nEvent, i->selected_old, PendingOperationItem::SelectEvent)); updateFlags |= SC_SELECTION; break; case UndoOp::AddTrack: switch(editable_track->type()) { case Track::AUDIO_SOFTSYNTH: { SynthI* si = (SynthI*)editable_track; if(si->hasGui()) si->showGui(false); if(si->hasNativeGui()) si->showNativeGui(false); }// Fall through. case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_INPUT: case Track::AUDIO_GROUP: case Track::AUDIO_AUX: ((AudioTrack*)editable_track)->deleteAllEfxGuis(); updateFlags |= SC_RACK; break; default: break; } switch(editable_track->type()) { case Track::AUDIO_OUTPUT: { AudioOutput* ao = (AudioOutput*)editable_track; for(int ch = 0; ch < ao->channels(); ++ch) { MusEGlobal::audioDevice->unregisterPort(ao->jackPort(ch)); //ao->setJackPort(ch, 0); // Done in RT stage. updateFlags |= SC_ROUTE; } } break; case Track::AUDIO_INPUT: { AudioOutput* ai = (AudioOutput*)editable_track; for(int ch = 0; ch < ai->channels(); ++ch) { MusEGlobal::audioDevice->unregisterPort(ai->jackPort(ch)); //ai->setJackPort(ch, 0); // Done in RT stage. updateFlags |= SC_ROUTE; } } break; case Track::AUDIO_AUX: updateFlags |= SC_AUX; break; default: break; } removeTrackOperation(editable_track, pendingOperations); updateFlags |= SC_TRACK_REMOVED; break; case UndoOp::DeleteTrack: switch(editable_track->type()) { case Track::AUDIO_SOFTSYNTH: { SynthI* s = (SynthI*)editable_track; Synth* sy = s->synth(); if(!s->isActivated()) s->initInstance(sy, s->name()); // FIXME TODO: We want to restore any ports using this instrument via the undo // system but ATM a few other things can set the instrument without an undo // operation so the undo sequence would not be correct. So we don't have much // choice but to just reset inside the PendingOperation::DeleteTrack operation // for now when deleting a synth track. // Still, everything else is in place for undoable setting of instrument... } break; case Track::AUDIO_OUTPUT: { AudioOutput* ao = (AudioOutput*)editable_track; if(MusEGlobal::checkAudioDevice()) { for(int ch = 0; ch < ao->channels(); ++ch) { // This should be OK since the track has not yet been added in the realtime stage. if(ao->registerPorts(ch)) updateFlags |= SC_ROUTE; // Set the route Jack ports now to relieve our graph callback handler from having to do it. RouteList* rl = ao->outRoutes(); for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) if(ir->type == Route::JACK_ROUTE && ir->channel == ch) { ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName); updateFlags |= SC_ROUTE; } } } } break; case Track::AUDIO_INPUT: { AudioInput* ai = (AudioInput*)editable_track; if(MusEGlobal::checkAudioDevice()) { for(int ch = 0; ch < ai->channels(); ++ch) { // This should be OK since the track has not yet been added in the realtime stage. if(ai->registerPorts(ch)) updateFlags |= SC_ROUTE; // Set the route Jack ports now to relieve our graph callback handler from having to do it. RouteList* rl = ai->inRoutes(); for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) if(ir->type == Route::JACK_ROUTE && ir->channel == ch) { ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName); updateFlags |= SC_ROUTE; } } } } break; case Track::AUDIO_AUX: updateFlags |= SC_AUX; break; default: break; } // Ensure that wave event sndfile file handles are opened. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. editable_track->openAllParts(); insertTrackOperation(editable_track, i->trackno, pendingOperations); updateFlags |= SC_TRACK_INSERTED; break; case UndoOp::ModifyClip: sndFileApplyUndoFile(i->nEvent, i->tmpwavfile, i->startframe, i->endframe); updateFlags |= SC_CLIP_MODIFIED; break; case UndoOp::ModifyTrackChannel: if (editable_property_track->isMidiTrack()) { MusECore::MidiTrack* mt = static_cast(editable_property_track); if (i->_oldPropValue != mt->outChannel()) { MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; MusEGlobal::audio->msgIdle(true); changed |= mt->setOutChanAndUpdate(i->_oldPropValue, false); MusEGlobal::audio->msgIdle(false); MusEGlobal::audio->msgUpdateSoloStates(); updateFlags |= (SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); } } else { if(editable_property_track->type() != MusECore::Track::AUDIO_SOFTSYNTH) { MusECore::AudioTrack* at = static_cast(editable_property_track); if (i->_oldPropValue != at->channels()) { MusEGlobal::audio->msgSetChannels(at, i->_oldPropValue); updateFlags |= SC_CHANNELS; } } } break; case UndoOp::SetTrackRecord: if(!editable_track->setRecordFlag1(!i->a)) break; pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackRecord)); // FIXME: No choice but to include monitor flag. Really should try to merge pending ops flags // with undo flags after executing the pending ops in revertOperationGroup3... updateFlags |= (SC_RECFLAG | SC_TRACK_REC_MONITOR); break; case UndoOp::SetTrackMute: pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackMute)); updateFlags |= SC_MUTE; break; case UndoOp::SetTrackSolo: pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackSolo)); updateFlags |= SC_SOLO; break; case UndoOp::SetTrackRecMonitor: pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackRecMonitor)); updateFlags |= SC_TRACK_REC_MONITOR; break; case UndoOp::SetTrackOff: pendingOperations.add(PendingOperationItem(editable_track, !i->a, PendingOperationItem::SetTrackOff)); updateFlags |= SC_MUTE; break; case UndoOp::AddRoute: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:AddRoute\n"); #endif pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::DeleteRoute)); updateFlags |= SC_ROUTE; break; case UndoOp::DeleteRoute: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:DeleteRoute\n"); #endif pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::AddRoute)); updateFlags |= SC_ROUTE; break; case UndoOp::ModifyTrackName: pendingOperations.add(PendingOperationItem(editable_track, i->_oldName, PendingOperationItem::ModifyTrackName)); updateFlags |= (SC_TRACK_MODIFIED | SC_MIDI_TRACK_PROP); // If it's an aux track, notify aux UI controls to reload, or change their names etc. if(editable_track->type() == Track::AUDIO_AUX) updateFlags |= SC_AUX; break; case UndoOp::MoveTrack: pendingOperations.add(PendingOperationItem(&_tracks, i->b, i->a, PendingOperationItem::MoveTrack)); updateFlags |= SC_TRACK_MOVED; break; case UndoOp::ModifyPartName: pendingOperations.add(PendingOperationItem(editable_part, i->_oldName, PendingOperationItem::ModifyPartName)); updateFlags |= SC_PART_MODIFIED; break; case UndoOp::ModifyPartLength: { pendingOperations.modifyPartLengthOperation( editable_part, i->old_partlen_or_pos, -i->events_offset, i->events_offset_time_type); updateFlags |= SC_PART_MODIFIED; // If the part had events, then treat it as if they were added/removed with separate Add/DeleteEvent operations. // Even if they will be added/deleted later in this operations group with actual separate Add/DeleteEvent operations, // that's an SC_EVENT_ADDED/REMOVED anyway, so hopefully no harm. if(i->events_offset != 0 && !editable_part->events().empty()) updateFlags |= (SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED); } break; case UndoOp::ModifyPartStart: { pendingOperations.modifyPartStartOperation( editable_part, i->old_partlen_or_pos, i->old_partlen, -i->events_offset, i->events_offset_time_type); updateFlags |= SC_PART_MODIFIED; // If the part had events, then treat it as if they were added/removed with separate Add/DeleteEvent operations. // Even if they will be added/deleted later in this operations group with actual separate Add/DeleteEvent operations, // that's an SC_EVENT_ADDED/REMOVED anyway, so hopefully no harm. if(i->events_offset != 0 && !editable_part->events().empty()) updateFlags |= (SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED); } break; case UndoOp::MovePart: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:MovePart ** calling parts->movePartOperation\n"); #endif pendingOperations.movePartOperation(editable_part->track()->parts(), editable_part, i->old_partlen_or_pos, const_cast(i->oldTrack)); if(const_cast(i->oldTrack)) updateFlags |= SC_PART_INSERTED | SC_PART_REMOVED; updateFlags |= SC_PART_MODIFIED; break; case UndoOp::AddPart: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:AddPart ** calling parts->delOperation\n"); #endif pendingOperations.delPartOperation(editable_part->track()->parts(), editable_part); updateFlags |= SC_PART_REMOVED; // If the part had events, then treat it as if they were removed with separate DeleteEvent operations. // Even if they will be deleted later in this operations group with actual separate DeleteEvent operations, // that's an SC_EVENT_REMOVED anyway, so hopefully no harm. This fixes a problem with midi controller canvas // not updating after such a 'delete part with events, no separate AddEvents were used when creating the part'. if(!editable_part->events().empty()) updateFlags |= SC_EVENT_REMOVED; break; case UndoOp::DeletePart: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:DeletePart ** calling parts->addOperation\n"); #endif // Ensure that wave event sndfile file handles are opened. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. editable_part->openAllEvents(); pendingOperations.addPartOperation(editable_part->track()->parts(), editable_part); updateFlags |= SC_PART_INSERTED; // If the part has events, then treat it as if they were inserted with separate AddEvent operations. // Even if some will be inserted later in this operations group with actual separate AddEvent operations, // that's an SC_EVENT_INSERTED anyway, so should be no harm. if(!editable_part->events().empty()) updateFlags |= SC_EVENT_INSERTED; break; case UndoOp::AddEvent: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:AddEvent ** calling deleteEvent\n"); #endif deleteEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones); updateFlags |= SC_EVENT_REMOVED; break; case UndoOp::DeleteEvent: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:DeleteEvent ** calling addEvent\n"); #endif if(!i->nEvent.empty()) { SndFileR f = i->nEvent.sndFile(); // Ensure that wave event sndfile file handle is opened. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. if(!f.isNull() && !f.isOpen()) f->openRead(); } addEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones); updateFlags |= SC_EVENT_INSERTED; } break; case UndoOp::ModifyEvent: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:ModifyEvent ** calling changeEvent\n"); #endif changeEventOperation(i->nEvent, i->oEvent, editable_part, i->doCtrls, i->doClones); updateFlags |= SC_EVENT_MODIFIED; break; case UndoOp::AddAudioCtrlVal: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:AddAudioCtrlVal\n"); #endif CtrlListList* cll = static_cast(editable_track)->controller(); iCtrlList icl = cll->find(i->_audioCtrlID); if(icl != cll->end()) { CtrlList* cl = icl->second; iCtrl ic = cl->find(i->_audioCtrlFrame); if(ic != cl->end()) { pendingOperations.add(PendingOperationItem(cl, ic, PendingOperationItem::DeleteAudioCtrlVal)); updateFlags |= SC_AUDIO_CONTROLLER; } } } break; case UndoOp::DeleteAudioCtrlVal: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:DeleteAudioCtrlVal\n"); #endif CtrlListList* cll = static_cast(editable_track)->controller(); iCtrlList icl = cll->find(i->_audioCtrlID); if(icl != cll->end()) { //CtrlList* cl = icl->second; //iCtrl ic = cl->find(i->_audioCtrlFrame); //if(ic != cl->end()) // // An existing value was found (really shouldn't happen!). Replace it with the old value. // pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioCtrlVal, PendingOperationItem::ModifyAudioCtrlVal)); //else // Restore the old value. pendingOperations.add(PendingOperationItem(icl->second, i->_audioCtrlFrame, i->_audioCtrlVal, PendingOperationItem::AddAudioCtrlVal)); updateFlags |= SC_AUDIO_CONTROLLER; } } break; case UndoOp::ModifyAudioCtrlVal: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:ModifyAudioCtrlVal\n"); #endif CtrlListList* cll = static_cast(editable_track)->controller(); iCtrlList icl = cll->find(i->_audioCtrlID); if(icl != cll->end()) { CtrlList* cl = icl->second; iCtrl ic = cl->find(i->_audioNewCtrlFrame); if(ic != cl->end()) { // Restore the old value. pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioCtrlFrame, i->_audioCtrlVal, PendingOperationItem::ModifyAudioCtrlVal)); updateFlags |= SC_AUDIO_CONTROLLER; } } } break; case UndoOp::ModifyAudioCtrlValList: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:ModifyAudioCtrlValList\n"); #endif // Take either id. At least one list must be valid. const int id = i->_eraseCtrlList ? i->_eraseCtrlList->id() : i->_addCtrlList->id(); iCtrlList icl = i->_ctrlListList->find(id); if(icl != i->_ctrlListList->end()) { // Make a complete copy of the controller list. The list will be quickly switched in the realtime stage. // The Pending Operations system will take 'ownership' of this and delete it at the appropriate time. CtrlList* new_list = new CtrlList(*icl->second, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); // Erase any items in the add list that were added... //if(i->_addCtrlList) if(i->_addCtrlList && !i->_addCtrlList->empty()) { //const std::size_t sz = i->_addCtrlList->size(); //if(sz != 0) //{ //const CtrlList& cl_r = *i->_addCtrlList; // Both of these should be valid. //ciCtrl n_s = new_list->find(cl_r[0].frame); // The first item to be erased. //ciCtrl n_e = new_list->find(cl_r[sz - 1].frame); // The last item to be erased. iCtrl n_s = new_list->find(i->_addCtrlList->begin()->second.frame); // The first item to be erased. ciCtrl e_e = i->_addCtrlList->end(); --e_e; //ciCtrl n_e = new_list->find((--i->_eraseCtrlList->end())->second.frame); // The last item to be erased. iCtrl n_e = new_list->find(e_e->second.frame); // The last item to be erased. if(n_s != new_list->end() && n_e != new_list->end()) { // Since std range does NOT include the last iterator, increment n_e so erase will get all items. ++n_e; new_list->erase(n_s, n_e); } //} } // Re-add any items in the erase list that were erased... if(i->_eraseCtrlList && !i->_eraseCtrlList->empty()) new_list->insert(i->_eraseCtrlList->begin(), i->_eraseCtrlList->end()); // The operation will quickly switch the list in the RT stage then the delete the old list in the non-RT stage. pendingOperations.add(PendingOperationItem(icl, new_list, PendingOperationItem::ModifyAudioCtrlValList)); updateFlags |= SC_AUDIO_CONTROLLER_LIST; } } break; case UndoOp::SetInstrument: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:SetInstrument\n"); #endif // Restore the old value. pendingOperations.add(PendingOperationItem( i->_midiPort, i->_oldMidiInstrument, PendingOperationItem::SetInstrument)); updateFlags |= SC_MIDI_INSTRUMENT; } break; case UndoOp::DeleteTempo: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:DeleteTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_tempo_list) { new_tempo_list = new TempoList(); new_tempo_list->copy(MusEGlobal::tempomap); } // Defer normalize until end of stage 2. new_tempo_list->addTempo(i->a, i->b, false); updateFlags |= SC_TEMPO; break; case UndoOp::AddTempo: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:AddTempo ** calling tempomap.delOperation tick:%d\n", i->a); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_tempo_list) { new_tempo_list = new TempoList(); new_tempo_list->copy(MusEGlobal::tempomap); } // Defer normalize until end of stage 2. new_tempo_list->delTempo(i->a, false); updateFlags |= SC_TEMPO; break; case UndoOp::ModifyTempo: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:ModifyTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_tempo_list) { new_tempo_list = new TempoList(); new_tempo_list->copy(MusEGlobal::tempomap); } // Defer normalize until end of stage 2. new_tempo_list->addTempo(i->a, i->b, false); updateFlags |= SC_TEMPO; break; // case UndoOp::SetTempo: // // Only if the master is on. // if(MusEGlobal::tempomap.masterFlag()) // { // #ifdef _UNDO_DEBUG_ // fprintf(stderr, "Song::revertOperationGroup1:SetTempo ** calling tempomap.delOperation tick:%d\n", i->a); // #endif // MusEGlobal::tempomap.delOperation(i->a, pendingOperations); // } // else // pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->b, PendingOperationItem::SetStaticTempo)); // updateFlags |= SC_TEMPO; // break; case UndoOp::SetStaticTempo: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:SetStaticTempo ** adding SetStaticTempo operation\n"); #endif pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->b, PendingOperationItem::SetStaticTempo)); updateFlags |= SC_TEMPO; break; case UndoOp::SetGlobalTempo: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:SetGlobalTempo ** adding SetGlobalTempo operation\n"); #endif pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->b, PendingOperationItem::SetGlobalTempo)); updateFlags |= SC_TEMPO; break; case UndoOp::EnableMasterTrack: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:EnableMasterTrack ** adding EnableMasterTrack operation\n"); #endif pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, (bool)i->b, PendingOperationItem::SetUseMasterTrack)); updateFlags |= SC_MASTER; break; case UndoOp::DeleteSig: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:DeleteSig ** calling MusEGlobal::sigmap.addOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_sig_list) { new_sig_list = new SigList(); new_sig_list->copy(MusEGlobal::sigmap); } // Defer normalize until end of stage 2. new_sig_list->add(i->a, MusECore::TimeSignature(i->b, i->c), false); updateFlags |= SC_SIG; break; case UndoOp::AddSig: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:AddSig ** calling MusEGlobal::sigmap.delOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_sig_list) { new_sig_list = new SigList(); new_sig_list->copy(MusEGlobal::sigmap); } // Defer normalize until end of stage 2. new_sig_list->del(i->a, false); updateFlags |= SC_SIG; break; case UndoOp::ModifySig: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:ModifySig ** calling MusEGlobal::sigmap.addOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_sig_list) { new_sig_list = new SigList(); new_sig_list->copy(MusEGlobal::sigmap); } // Defer normalize until end of stage 2. // TODO: Hm should that be ->d and ->e like in executeOperationGroup1? new_sig_list->add(i->a, MusECore::TimeSignature(i->b, i->c), false); updateFlags |= SC_SIG; break; case UndoOp::DeleteKey: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:DeleteKey ** calling keymap.addOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_key_list) { new_key_list = new KeyList(); new_key_list->copy(MusEGlobal::keymap); } new_key_list->addKey(i->a, key_enum(i->b), i->c); updateFlags |= SC_KEY; break; case UndoOp::AddKey: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:AddKey ** calling keymap.delOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_key_list) { new_key_list = new KeyList(); new_key_list->copy(MusEGlobal::keymap); } new_key_list->delKey(i->a); updateFlags |= SC_KEY; break; case UndoOp::ModifyKey: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:ModifyKey ** calling keymap.addOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_key_list) { new_key_list = new KeyList(); new_key_list->copy(MusEGlobal::keymap); } // TODO: Hm should that be ->d and ->e like in executeOperationGroup1? new_key_list->addKey(i->a, key_enum(i->b), i->c); updateFlags |= SC_KEY; break; case UndoOp::ModifySongLen: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:ModifySongLen ** adding ModifySongLen operation\n"); #endif pendingOperations.add(PendingOperationItem(i->b, PendingOperationItem::ModifySongLength)); updateFlags |= SC_EVERYTHING; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. //updateFlags |= SC_SONG_LEN; break; case UndoOp::ModifyMidiDivision: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup1:ModifyMidiDivision\n"); #endif MusEGlobal::config.division = i->b; // Make sure the AL namespace variable mirrors our variable. AL::division = MusEGlobal::config.division; // Defer normalize until end of stage 2. updateFlags |= SC_DIVISION_CHANGED; break; case UndoOp::AddMarker: // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_marker_list) new_marker_list = new MarkerList(*marker()); if(i->newMarker) new_marker_list->remove(*i->newMarker); updateFlags |= SC_MARKER_REMOVED; break; case UndoOp::DeleteMarker: // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_marker_list) new_marker_list = new MarkerList(*marker()); if(i->oldMarker) new_marker_list->add(*i->oldMarker); updateFlags |= SC_MARKER_INSERTED; break; case UndoOp::ModifyMarker: case UndoOp::SetMarkerPos: // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_marker_list) new_marker_list = new MarkerList(*marker()); if(i->newMarker) new_marker_list->remove(*i->newMarker); if(i->oldMarker) new_marker_list->add(*i->oldMarker); updateFlags |= SC_MARKER_MODIFIED; break; default: break; } } if(new_tempo_list) pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, new_tempo_list, PendingOperationItem::ModifyTempoList)); if(new_sig_list) pendingOperations.add(PendingOperationItem(&MusEGlobal::sigmap, new_sig_list, PendingOperationItem::ModifySigList)); if(new_key_list) pendingOperations.add(PendingOperationItem(&MusEGlobal::keymap, new_key_list, PendingOperationItem::ModifyKeyList)); if(new_marker_list) pendingOperations.add(PendingOperationItem(&_markerList, new_marker_list, PendingOperationItem::ModifyMarkerList)); } //--------------------------------------------------------- // revertOperationGroup3 // non realtime context //--------------------------------------------------------- void Song::revertOperationGroup3(Undo& operations) { pendingOperations.executeNonRTStage(); #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::revertOperationGroup3 *** Calling pendingOperations.clear()\n"); #endif pendingOperations.clear(); for (riUndoOp i = operations.rbegin(); i != operations.rend(); ++i) { Track* editable_track = const_cast(i->track); // uncomment if needed Track* editable_property_track = const_cast(i->_propertyTrack); Part* editable_part = const_cast(i->part); // uncomment if needed switch(i->type) { case UndoOp::AddTrack: // Ensure that wave event sndfile file handles are closed. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. editable_track->closeAllParts(); break; case UndoOp::DeleteTrack: switch(editable_track->type()) { case Track::AUDIO_OUTPUT: // Connect audio output ports to Jack ports... if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning()) { AudioOutput* ao = (AudioOutput*)editable_track; for(int ch = 0; ch < ao->channels(); ++ch) { void* our_port = ao->jackPort(ch); if(!our_port) continue; const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); if(!our_port_name) continue; RouteList* rl = ao->outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::JACK_ROUTE || ir->channel != ch) continue; const char* route_name = ir->persistentJackPortName; //if(ir->jackPort) if(!MusEGlobal::audioDevice->findPort(route_name)) continue; //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) MusEGlobal::audioDevice->connect(our_port_name, route_name); updateFlags |= SC_ROUTE; } } } break; case Track::AUDIO_INPUT: // Connect Jack ports to audio input ports... if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning()) { AudioInput* ai = (AudioInput*)editable_track; for(int ch = 0; ch < ai->channels(); ++ch) { void* our_port = ai->jackPort(ch); if(!our_port) continue; const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); if(!our_port_name) continue; RouteList* rl = ai->inRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::JACK_ROUTE || ir->channel != ch) continue; const char* route_name = ir->persistentJackPortName; //if(ir->jackPort) if(!MusEGlobal::audioDevice->findPort(route_name)) continue; //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) MusEGlobal::audioDevice->connect(route_name, our_port_name); updateFlags |= SC_ROUTE; } } } break; default: break; } break; case UndoOp::AddPart: // Ensure that wave event sndfile file handles are closed. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. editable_part->closeAllEvents(); break; case UndoOp::AddEvent: { if(!i->nEvent.empty()) { SndFileR f = i->nEvent.sndFile(); // Ensure that wave event sndfile file handle is closed. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. if(!f.isNull() && f.isOpen()) f->close(); } } break; case UndoOp::ModifyMidiDivision: // This also tells all connected models to begin/end reset. MusEGlobal::globalRasterizer->setDivision(i->b); break; default: break; } } if(!operations.empty()) emit sigDirty(); } //--------------------------------------------------------- // executeOperationGroup1 // non realtime context // return true if nothing to do //--------------------------------------------------------- void Song::executeOperationGroup1(Undo& operations) { unsigned song_len = len(); MarkerList* new_marker_list = nullptr; TempoList* new_tempo_list = nullptr; SigList* new_sig_list = nullptr; KeyList* new_key_list = nullptr; for (iUndoOp i = operations.begin(); i != operations.end(); ++i) { Track* editable_track = const_cast(i->track); Track* editable_property_track = const_cast(i->_propertyTrack); Part* editable_part = const_cast(i->part); switch(i->type) { case UndoOp::SelectPart: pendingOperations.add(PendingOperationItem(editable_part, i->selected, PendingOperationItem::SelectPart)); updateFlags |= SC_PART_SELECTION; break; case UndoOp::SelectEvent: pendingOperations.add(PendingOperationItem(editable_part, i->nEvent, i->selected, PendingOperationItem::SelectEvent)); updateFlags |= SC_SELECTION; break; case UndoOp::AddTrack: switch(editable_track->type()) { case Track::AUDIO_SOFTSYNTH: { SynthI* s = (SynthI*)editable_track; Synth* sy = s->synth(); if(!s->isActivated()) s->initInstance(sy, s->name()); } break; case Track::AUDIO_OUTPUT: { AudioOutput* ao = (AudioOutput*)editable_track; if(MusEGlobal::checkAudioDevice()) { for(int ch = 0; ch < ao->channels(); ++ch) { // This should be OK since the track has not yet been added in the realtime stage. if(ao->registerPorts(ch)) updateFlags |= SC_ROUTE; // Set the route Jack ports now to relieve our graph callback handler from having to do it. RouteList* rl = ao->outRoutes(); for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) if(ir->type == Route::JACK_ROUTE && ir->channel == ch) { ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName); updateFlags |= SC_ROUTE; } } } } break; case Track::AUDIO_INPUT: { AudioInput* ai = (AudioInput*)editable_track; if(MusEGlobal::checkAudioDevice()) { for(int ch = 0; ch < ai->channels(); ++ch) { // This should be OK since the track has not yet been added in the realtime stage. if(ai->registerPorts(ch)) updateFlags |= SC_ROUTE; // Set the route Jack ports now to relieve our graph callback handler from having to do it. RouteList* rl = ai->inRoutes(); for(iRoute ir = rl->begin(); ir != rl->end(); ++ir) if(ir->type == Route::JACK_ROUTE && ir->channel == ch) { ir->jackPort = MusEGlobal::audioDevice->findPort(ir->persistentJackPortName); updateFlags |= SC_ROUTE; } } } } break; case Track::AUDIO_AUX: updateFlags |= SC_AUX; break; default: break; } // Ensure that wave event sndfile file handles are opened. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. editable_track->openAllParts(); insertTrackOperation(editable_track, i->trackno, pendingOperations); updateFlags |= SC_TRACK_INSERTED; break; case UndoOp::DeleteTrack: switch(editable_track->type()) { case Track::AUDIO_SOFTSYNTH: { SynthI* si = (SynthI*)editable_track; if(si->hasGui()) si->showGui(false); if(si->hasNativeGui()) si->showNativeGui(false); // FIXME TODO: We want to clear any ports using this instrument AND make it // undoable but ATM a few other things can set the instrument without an undo // operation so the undo sequence would not be correct. So we don't have much // choice but to just reset inside the PendingOperation::DeleteTrack operation for now. // Still, everything else is in place for undoable setting of instrument... }// Fall through. case Track::WAVE: case Track::AUDIO_OUTPUT: case Track::AUDIO_INPUT: case Track::AUDIO_GROUP: case Track::AUDIO_AUX: ((AudioTrack*)editable_track)->deleteAllEfxGuis(); updateFlags |= SC_RACK; break; default: break; } switch(editable_track->type()) { case Track::AUDIO_OUTPUT: { AudioOutput* ao = (AudioOutput*)editable_track; for(int ch = 0; ch < ao->channels(); ++ch) { MusEGlobal::audioDevice->unregisterPort(ao->jackPort(ch)); //ao->setJackPort(ch, 0); // Done in RT stage. updateFlags |= SC_ROUTE; } } break; case Track::AUDIO_INPUT: { AudioOutput* ai = (AudioOutput*)editable_track; for(int ch = 0; ch < ai->channels(); ++ch) { MusEGlobal::audioDevice->unregisterPort(ai->jackPort(ch)); //ai->setJackPort(ch, 0); // Done in RT stage. updateFlags |= SC_ROUTE; } } break; case Track::AUDIO_AUX: updateFlags |= SC_AUX; break; default: break; } removeTrackOperation(editable_track, pendingOperations); updateFlags |= SC_TRACK_REMOVED; break; case UndoOp::ModifyClip: sndFileApplyUndoFile(i->nEvent, i->tmpwavfile, i->startframe, i->endframe); updateFlags |= SC_CLIP_MODIFIED; break; case UndoOp::ModifyTrackChannel: if (editable_property_track->isMidiTrack()) { MusECore::MidiTrack* mt = static_cast(editable_property_track); if (i->_newPropValue != mt->outChannel()) { MusECore::MidiTrack::ChangedType_t changed = MusECore::MidiTrack::NothingChanged; MusEGlobal::audio->msgIdle(true); changed |= mt->setOutChanAndUpdate(i->_newPropValue, false); MusEGlobal::audio->msgIdle(false); MusEGlobal::audio->msgUpdateSoloStates(); updateFlags |= (SC_ROUTE | ((changed & MusECore::MidiTrack::DrumMapChanged) ? SC_DRUMMAP : 0)); } } else { if(editable_property_track->type() != MusECore::Track::AUDIO_SOFTSYNTH) { MusECore::AudioTrack* at = static_cast(editable_property_track); if (i->_newPropValue != at->channels()) { MusEGlobal::audio->msgSetChannels(at, i->_newPropValue); updateFlags |= SC_CHANNELS; } } } break; case UndoOp::SetTrackRecord: if(!editable_track->setRecordFlag1(i->a)) break; pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackRecord)); // FIXME: No choice but to include monitor flag. Really should try to merge pending ops flags // with undo flags after executing the pending ops in executeOperationGroup3... updateFlags |= (SC_RECFLAG | SC_TRACK_REC_MONITOR); break; case UndoOp::SetTrackMute: pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackMute)); updateFlags |= SC_MUTE; break; case UndoOp::SetTrackSolo: pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackSolo)); updateFlags |= SC_SOLO; break; case UndoOp::SetTrackRecMonitor: pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackRecMonitor)); updateFlags |= SC_TRACK_REC_MONITOR; break; case UndoOp::SetTrackOff: pendingOperations.add(PendingOperationItem(editable_track, i->a, PendingOperationItem::SetTrackOff)); updateFlags |= SC_MUTE; break; case UndoOp::AddRoute: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:AddRoute\n"); #endif pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::AddRoute)); updateFlags |= SC_ROUTE; break; case UndoOp::DeleteRoute: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:DeleteRoute\n"); #endif pendingOperations.add(PendingOperationItem(i->routeFrom, i->routeTo, PendingOperationItem::DeleteRoute)); updateFlags |= SC_ROUTE; break; case UndoOp::ModifyTrackName: pendingOperations.add(PendingOperationItem(editable_track, i->_newName, PendingOperationItem::ModifyTrackName)); updateFlags |= (SC_TRACK_MODIFIED | SC_MIDI_TRACK_PROP); // If it's an aux track, notify aux UI controls to reload, or change their names etc. if(editable_track->type() == Track::AUDIO_AUX) updateFlags |= SC_AUX; break; case UndoOp::MoveTrack: pendingOperations.add(PendingOperationItem(&_tracks, i->a, i->b, PendingOperationItem::MoveTrack)); updateFlags |= SC_TRACK_MOVED; break; case UndoOp::ModifyPartName: pendingOperations.add(PendingOperationItem(editable_part, i->_newName, PendingOperationItem::ModifyPartName)); updateFlags |= SC_PART_MODIFIED; break; case UndoOp::ModifyPartStart: { pendingOperations.modifyPartStartOperation( editable_part, i->new_partlen_or_pos, i->new_partlen, i->events_offset, i->events_offset_time_type); updateFlags |= SC_PART_MODIFIED; // If the part had events, then treat it as if they were added/removed with separate Add/DeleteEvent operations. // Even if they will be added/deleted later in this operations group with actual separate Add/DeleteEvent operations, // that's an SC_EVENT_ADDED/REMOVED anyway, so hopefully no harm. if(i->events_offset != 0 && !editable_part->events().empty()) updateFlags |= (SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED); } break; case UndoOp::ModifyPartLength: { unsigned p = Pos::convert(editable_part->posValue() + i->new_partlen_or_pos, editable_part->type(), Pos::TICKS); // >= for good luck, since this (simpler) comparison is in the TICKS domain. if(p >= song_len) { song_len = p + 1; // Insert a ModifySongLen operation BEFORE this one. If insert finds an existing ModifySongLen, // possibly long before this one, it REPLACES that one's values. operations.insert(i, UndoOp(UndoOp::ModifySongLen, song_len, len())); // Since the ModifySongLen above will not be iterated now, act like the operation had just been iterated. // The same REPLACEMENT rules apply here. pendingOperations.add(PendingOperationItem(song_len, PendingOperationItem::ModifySongLength)); updateFlags |= SC_EVERYTHING; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. //updateFlags |= SC_SONG_LEN; } pendingOperations.modifyPartLengthOperation( editable_part, i->new_partlen_or_pos, i->events_offset, i->events_offset_time_type); updateFlags |= SC_PART_MODIFIED; // If the part had events, then treat it as if they were added/removed with separate Add/DeleteEvent operations. // Even if they will be added/deleted later in this operations group with actual separate Add/DeleteEvent operations, // that's an SC_EVENT_ADDED/REMOVED anyway, so hopefully no harm. if(i->events_offset != 0 && !editable_part->events().empty()) updateFlags |= (SC_EVENT_INSERTED | SC_EVENT_REMOVED | SC_EVENT_MODIFIED); } break; case UndoOp::MovePart: { unsigned p = Pos::convert(editable_part->lenValue() + i->new_partlen_or_pos, editable_part->type(), Pos::TICKS); // >= for good luck, since this (simpler) comparison is in the TICKS domain. if(p >= song_len) { song_len = p + 1; // Insert a ModifySongLen operation BEFORE this one. If insert finds an existing ModifySongLen, // possibly long before this one, it REPLACES that one's values. operations.insert(i, UndoOp(UndoOp::ModifySongLen, song_len, len())); // Since the ModifySongLen above will not be iterated now, act like the operation had just been iterated. // The same REPLACEMENT rules apply here. pendingOperations.add(PendingOperationItem(song_len, PendingOperationItem::ModifySongLength)); updateFlags |= SC_EVERYTHING; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. //updateFlags |= SC_SONG_LEN; } #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:MovePart ** calling parts->movePartOperation\n"); #endif pendingOperations.movePartOperation(editable_part->track()->parts(), editable_part, i->new_partlen_or_pos, editable_track); if(editable_track) updateFlags |= SC_PART_INSERTED | SC_PART_REMOVED; updateFlags |= SC_PART_MODIFIED; } break; case UndoOp::AddPart: { unsigned p = Pos::convert(editable_part->lenValue() + editable_part->posValue(), editable_part->type(), Pos::TICKS); // >= for good luck, since this (simpler) comparison is in the TICKS domain. if(p >= song_len) { song_len = p + 1; // Insert a ModifySongLen operation BEFORE this one. If insert finds an existing ModifySongLen, // possibly long before this one, it REPLACES that one's values. operations.insert(i, UndoOp(UndoOp::ModifySongLen, song_len, len())); // Since the ModifySongLen above will not be iterated now, act like the operation had just been iterated. // The same REPLACEMENT rules apply here. pendingOperations.add(PendingOperationItem(song_len, PendingOperationItem::ModifySongLength)); updateFlags |= SC_EVERYTHING; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. //updateFlags |= SC_SONG_LEN; } #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:addPart ** calling parts->addOperation\n"); #endif // Ensure that wave event sndfile file handles are opened. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. editable_part->openAllEvents(); pendingOperations.addPartOperation(editable_part->track()->parts(), editable_part); updateFlags |= SC_PART_INSERTED; // If the part has events, then treat it as if they were inserted with separate AddEvent operations. // Even if some will be inserted later in this operations group with actual separate AddEvent operations, // that's an SC_EVENT_INSERTED anyway, so should be no harm. if(!editable_part->events().empty()) updateFlags |= SC_EVENT_INSERTED; } break; case UndoOp::DeletePart: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:deletePart ** calling parts->delOperation\n"); #endif pendingOperations.delPartOperation(editable_part->track()->parts(), editable_part); updateFlags |= SC_PART_REMOVED; // If the part had events, then treat it as if they were removed with separate DeleteEvent operations. // Even if they will be deleted later in this operations group with actual separate DeleteEvent operations, // that's an SC_EVENT_REMOVED anyway, so hopefully no harm. This fixes a problem with midi controller canvas // not updating after such a 'delete part with events, no separate AddEvents were used when creating the part'. if(!editable_part->events().empty()) updateFlags |= SC_EVENT_REMOVED; break; case UndoOp::AddEvent: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:AddEvent ** calling addEvent\n"); #endif if(!i->nEvent.empty()) { SndFileR f = i->nEvent.sndFile(); // Ensure that wave event sndfile file handle is opened. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. if(!f.isNull() && !f.isOpen()) f.openRead(); } addEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones); updateFlags |= SC_EVENT_INSERTED; } break; case UndoOp::DeleteEvent: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:DeleteEvent ** calling deleteEvent\n"); #endif // Special: Replace the undo item's event with the real actual event found in the event lists. // This way even a modified event can be passed in to the DeleteEvent operation constructor, // and as long as the ID AND position values match it will find and use the ORIGINAL event. // (It's safe, the = operator quickly returns if the two events have the same base pointer.) i->nEvent = deleteEventOperation(i->nEvent, editable_part, i->doCtrls, i->doClones); updateFlags |= SC_EVENT_REMOVED; } break; case UndoOp::ModifyEvent: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:ModifyEvent ** calling changeEvent\n"); #endif // Special: Replace the undo item's old event with the real actual event found in the event lists. // This way even a modified old event can be passed in to the ModifyEvent operation constructor, // and as long as the ID AND position values match it will find and use the ORIGINAL event. // (It's safe, the = operator quickly returns if the two events have the same base pointer.) i->oEvent = changeEventOperation(i->oEvent, i->nEvent, editable_part, i->doCtrls, i->doClones); updateFlags |= SC_EVENT_MODIFIED; break; case UndoOp::AddAudioCtrlVal: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:AddAudioCtrlVal\n"); #endif CtrlListList* cll = static_cast(editable_track)->controller(); iCtrlList icl = cll->find(i->_audioCtrlID); if(icl != cll->end()) { //CtrlList* cl = icl->second; //iCtrl ic = cl->find(i->_audioCtrlFrame); //if(ic != cl->end()) // // An existing value was found. Replace it with the new value. // pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioCtrlVal, PendingOperationItem::ModifyAudioCtrlVal)); //else // Add the new value. pendingOperations.add(PendingOperationItem(icl->second, i->_audioCtrlFrame, i->_audioCtrlVal, PendingOperationItem::AddAudioCtrlVal)); updateFlags |= SC_AUDIO_CONTROLLER; } } break; case UndoOp::DeleteAudioCtrlVal: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:DeleteAudioCtrlVal\n"); #endif CtrlListList* cll = static_cast(editable_track)->controller(); iCtrlList icl = cll->find(i->_audioCtrlID); if(icl != cll->end()) { CtrlList* cl = icl->second; iCtrl ic = cl->find(i->_audioCtrlFrame); if(ic != cl->end()) { i->_audioCtrlVal = ic->second.val; // Store the existing value so it can be restored. pendingOperations.add(PendingOperationItem(cl, ic, PendingOperationItem::DeleteAudioCtrlVal)); updateFlags |= SC_AUDIO_CONTROLLER; } } } break; case UndoOp::ModifyAudioCtrlVal: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:ModifyAudioCtrlVal\n"); #endif CtrlListList* cll = static_cast(editable_track)->controller(); iCtrlList icl = cll->find(i->_audioCtrlID); if(icl != cll->end()) { CtrlList* cl = icl->second; iCtrl ic = cl->find(i->_audioCtrlFrame); if(ic != cl->end()) { i->_audioCtrlVal = ic->second.val; // Store the existing value so it can be restored. pendingOperations.add(PendingOperationItem(icl->second, ic, i->_audioNewCtrlFrame, i->_audioNewCtrlVal, PendingOperationItem::ModifyAudioCtrlVal)); updateFlags |= SC_AUDIO_CONTROLLER; } } } break; case UndoOp::ModifyAudioCtrlValList: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:ModifyAudioCtrlValList\n"); #endif // Take either id. At least one list must be valid. const int id = i->_eraseCtrlList ? i->_eraseCtrlList->id() : i->_addCtrlList->id(); iCtrlList icl = i->_ctrlListList->find(id); if(icl != i->_ctrlListList->end()) { // Make a complete copy of the controller list. The list will be quickly switched in the realtime stage. // The Pending Operations system will take 'ownership' of this and delete it at the appropriate time. CtrlList* new_list = new CtrlList(*icl->second, CtrlList::ASSIGN_PROPERTIES | CtrlList::ASSIGN_VALUES); if(i->_eraseCtrlList && !i->_eraseCtrlList->empty()) { iCtrl n_s = new_list->find(i->_eraseCtrlList->begin()->second.frame); // The first item to be erased. ciCtrl e_e = i->_eraseCtrlList->end(); --e_e; iCtrl n_e = new_list->find(e_e->second.frame); // The last item to be erased. if(n_s != new_list->end() && n_e != new_list->end()) { // Since std range does NOT include the last iterator, increment n_e so erase will get all items. ++n_e; new_list->erase(n_s, n_e); } } // Add any items in the add list... if(i->_addCtrlList && !i->_addCtrlList->empty()) new_list->insert(i->_addCtrlList->begin(), i->_addCtrlList->end()); // The operation will quickly switch the list in the RT stage then the delete the old list in the non-RT stage. pendingOperations.add(PendingOperationItem(icl, new_list, PendingOperationItem::ModifyAudioCtrlValList)); updateFlags |= SC_AUDIO_CONTROLLER_LIST; } } break; case UndoOp::SetInstrument: { #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:SetInstrument\n"); #endif // Set the new value. pendingOperations.add(PendingOperationItem( i->_midiPort, i->_newMidiInstrument, PendingOperationItem::SetInstrument)); updateFlags |= SC_MIDI_INSTRUMENT; } break; case UndoOp::AddTempo: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:AddTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_tempo_list) { new_tempo_list = new TempoList(); new_tempo_list->copy(MusEGlobal::tempomap); } // Defer normalize until end of stage 2. new_tempo_list->addTempo(i->a, i->b, false); updateFlags |= SC_TEMPO; break; case UndoOp::DeleteTempo: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:DeleteTempo ** calling tempomap.delOperation tick:%d\n", i->a); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_tempo_list) { new_tempo_list = new TempoList(); new_tempo_list->copy(MusEGlobal::tempomap); } // Defer normalize until end of stage 2. new_tempo_list->delTempo(i->a, false); updateFlags |= SC_TEMPO; break; case UndoOp::ModifyTempo: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:ModifyTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->c); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_tempo_list) { new_tempo_list = new TempoList(); new_tempo_list->copy(MusEGlobal::tempomap); } // Defer normalize until end of stage 2. new_tempo_list->addTempo(i->a, i->c, false); updateFlags |= SC_TEMPO; break; // case UndoOp::SetTempo: // // Only if the master is on. // if(MusEGlobal::tempomap.masterFlag()) // { // #ifdef _UNDO_DEBUG_ // fprintf(stderr, "Song::executeOperationGroup1:SetTempo ** calling tempomap.addOperation tick:%d tempo:%d\n", i->a, i->b); // #endif // MusEGlobal::tempomap.addOperation(i->a, i->b, pendingOperations); // } // else // pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->a, PendingOperationItem::SetStaticTempo)); // updateFlags |= SC_TEMPO; // break; case UndoOp::SetStaticTempo: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:SetStaticTempo ** adding SetStaticTempo operation\n"); #endif pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->a, PendingOperationItem::SetStaticTempo)); updateFlags |= SC_TEMPO; break; case UndoOp::SetGlobalTempo: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:SetGlobalTempo ** adding SetGlobalTempo operation\n"); #endif pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, i->a, PendingOperationItem::SetGlobalTempo)); updateFlags |= SC_TEMPO; break; case UndoOp::EnableMasterTrack: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:EnableMasterTrack ** adding EnableMasterTrack operation\n"); #endif pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, (bool)i->a, PendingOperationItem::SetUseMasterTrack)); updateFlags |= SC_MASTER; break; case UndoOp::AddSig: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:AddSig ** calling MusEGlobal::sigmap.addOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_sig_list) { new_sig_list = new SigList(); new_sig_list->copy(MusEGlobal::sigmap); } // Defer normalize until end of stage 2. new_sig_list->add(i->a, MusECore::TimeSignature(i->b, i->c), false); updateFlags |= SC_SIG; break; case UndoOp::DeleteSig: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:DeleteSig ** calling MusEGlobal::sigmap.delOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_sig_list) { new_sig_list = new SigList(); new_sig_list->copy(MusEGlobal::sigmap); } // Defer normalize until end of stage 2. new_sig_list->del(i->a, false); updateFlags |= SC_SIG; break; case UndoOp::ModifySig: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:ModifySig ** calling MusEGlobal::sigmap.addOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_sig_list) { new_sig_list = new SigList(); new_sig_list->copy(MusEGlobal::sigmap); } // Defer normalize until end of stage 2. new_sig_list->add(i->a, MusECore::TimeSignature(i->d, i->e), false); updateFlags |= SC_SIG; break; case UndoOp::AddKey: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:AddKey ** calling keymap.addOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_key_list) { new_key_list = new KeyList(); new_key_list->copy(MusEGlobal::keymap); } new_key_list->addKey(i->a, key_enum(i->b), i->c); updateFlags |= SC_KEY; break; case UndoOp::DeleteKey: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:DeleteKey ** calling keymap.delOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_key_list) { new_key_list = new KeyList(); new_key_list->copy(MusEGlobal::keymap); } new_key_list->delKey(i->a); updateFlags |= SC_KEY; break; case UndoOp::ModifyKey: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:ModifyKey ** calling keymap.addOperation\n"); #endif // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_key_list) { new_key_list = new KeyList(); new_key_list->copy(MusEGlobal::keymap); } new_key_list->addKey(i->a, key_enum(i->d), i->e); updateFlags |= SC_KEY; break; case UndoOp::ModifySongLen: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:ModifySongLen ** adding ModifySongLen operation\n"); #endif pendingOperations.add(PendingOperationItem(i->a, PendingOperationItem::ModifySongLength)); updateFlags |= SC_EVERYTHING; // set all flags // TODO Refine this! Too many flags. // REMOVE Tim. //updateFlags |= SC_SONG_LEN; break; case UndoOp::ModifyMidiDivision: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:ModifyMidiDivision\n"); #endif MusEGlobal::config.division = i->a; // Make sure the AL namespace variable mirrors our variable. AL::division = MusEGlobal::config.division; // Defer normalize until end of stage 2. updateFlags |= SC_DIVISION_CHANGED; break; case UndoOp::EnableAllAudioControllers: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:EnableAllAudioControllers\n"); #endif pendingOperations.add(PendingOperationItem(PendingOperationItem::EnableAllAudioControllers)); updateFlags |= SC_AUDIO_CONTROLLER; break; case UndoOp::NormalizeMidiDivision: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:NormalizeMidiDivision\n"); #endif // Nothing to do here. // Defer normalize until end of stage 2. updateFlags |= SC_DIVISION_CHANGED; break; case UndoOp::GlobalSelectAllEvents: #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup1:GlobalSelectAllEvents\n"); #endif pendingOperations.add(PendingOperationItem(tracks(), i->a, 0, 0, PendingOperationItem::GlobalSelectAllEvents)); updateFlags |= SC_SELECTION; break; case UndoOp::AddMarker: // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_marker_list) new_marker_list = new MarkerList(*marker()); if(i->newMarker) new_marker_list->add(*i->newMarker); updateFlags |= SC_MARKER_INSERTED; break; case UndoOp::DeleteMarker: // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_marker_list) new_marker_list = new MarkerList(*marker()); if(i->oldMarker) new_marker_list->remove(*i->oldMarker); updateFlags |= SC_MARKER_REMOVED; break; case UndoOp::ModifyMarker: case UndoOp::SetMarkerPos: // Create the new list if it doesn't already exist. // Make a copy of the original list. if(!new_marker_list) new_marker_list = new MarkerList(*marker()); if(i->oldMarker) new_marker_list->remove(*i->oldMarker); if(i->newMarker) new_marker_list->add(*i->newMarker); updateFlags |= SC_MARKER_MODIFIED; break; default: break; } } if(new_tempo_list) pendingOperations.add(PendingOperationItem(&MusEGlobal::tempomap, new_tempo_list, PendingOperationItem::ModifyTempoList)); if(new_sig_list) pendingOperations.add(PendingOperationItem(&MusEGlobal::sigmap, new_sig_list, PendingOperationItem::ModifySigList)); if(new_key_list) pendingOperations.add(PendingOperationItem(&MusEGlobal::keymap, new_key_list, PendingOperationItem::ModifyKeyList)); if(new_marker_list) pendingOperations.add(PendingOperationItem(&_markerList, new_marker_list, PendingOperationItem::ModifyMarkerList)); } //--------------------------------------------------------- // executeOperationGroup3 // non realtime context //--------------------------------------------------------- void Song::executeOperationGroup3(Undo& operations) { pendingOperations.executeNonRTStage(); #ifdef _UNDO_DEBUG_ fprintf(stderr, "Song::executeOperationGroup3 *** Calling pendingOperations.clear()\n"); #endif pendingOperations.clear(); //bool song_has_changed = !operations.empty(); for (iUndoOp i = operations.begin(); i != operations.end(); ) { Track* editable_track = const_cast(i->track); // uncomment if needed Track* editable_property_track = const_cast(i->_propertyTrack); Part* editable_part = const_cast(i->part); // uncomment if needed switch(i->type) { case UndoOp::AddTrack: switch(editable_track->type()) { case Track::AUDIO_OUTPUT: // Connect audio output ports to Jack ports... if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning()) { AudioOutput* ao = (AudioOutput*)editable_track; for(int ch = 0; ch < ao->channels(); ++ch) { void* our_port = ao->jackPort(ch); if(!our_port) continue; const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); if(!our_port_name) continue; RouteList* rl = ao->outRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::JACK_ROUTE || ir->channel != ch) continue; const char* route_name = ir->persistentJackPortName; //if(ir->jackPort) if(!MusEGlobal::audioDevice->findPort(route_name)) continue; //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) MusEGlobal::audioDevice->connect(our_port_name, route_name); updateFlags |= SC_ROUTE; } } } break; case Track::AUDIO_INPUT: // Connect Jack ports to audio input ports... if(MusEGlobal::checkAudioDevice() && MusEGlobal::audio->isRunning()) { AudioInput* ai = (AudioInput*)editable_track; for(int ch = 0; ch < ai->channels(); ++ch) { void* our_port = ai->jackPort(ch); if(!our_port) continue; const char* our_port_name = MusEGlobal::audioDevice->canonicalPortName(our_port); if(!our_port_name) continue; RouteList* rl = ai->inRoutes(); for(ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::JACK_ROUTE || ir->channel != ch) continue; const char* route_name = ir->persistentJackPortName; //if(ir->jackPort) if(!MusEGlobal::audioDevice->findPort(route_name)) continue; //if(!MusEGlobal::audioDevice->portConnectedTo(our_port, route_name)) MusEGlobal::audioDevice->connect(route_name, our_port_name); updateFlags |= SC_ROUTE; } } } break; default: break; } break; case UndoOp::DeleteTrack: // Ensure that wave event sndfile file handles are closed. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. editable_track->closeAllParts(); break; case UndoOp::DeletePart: // Ensure that wave event sndfile file handles are closed. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. editable_part->closeAllEvents(); break; case UndoOp::DeleteEvent: { if(!i->nEvent.empty()) { SndFileR f = i->nEvent.sndFile(); // Ensure that wave event sndfile file handle is closed. // It should not be the job of the pending operations list to do this. // TODO Coordinate close/open with part mute and/or track off. if(!f.isNull() && f.isOpen()) f.close(); } } break; case UndoOp::ModifyMidiDivision: // This also tells all connected models to begin/end reset. MusEGlobal::globalRasterizer->setDivision(i->a); break; default: break; } // Is the operation marked as non-undoable? Remove it from the list. if(i->_noUndo) i = operations.erase(i); else ++i; } // If some operations marked as non-undoable were removed, it is OK, // because we only want dirty if an undoable operation was executed, right? if(!operations.empty()) // Hm, no. ANY operation actually changes things, so yes, the song is dirty. //if(song_has_changed) emit sigDirty(); } bool Undo::empty() const { if (std::list::empty()) return true; for (const_iterator it=begin(); it!=end(); it++) if (it->type!=UndoOp::DoNothing) return false; return true; } } // namespace MusECore muse-4.0.0/src/muse/undo.h000066400000000000000000000246401404105311000153350ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: undo.h,v 1.6.2.5 2009/05/24 21:43:44 terminator356 Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __UNDO_H__ #define __UNDO_H__ #include #include #include "event.h" #include "route.h" #include "sig.h" #include "pos.h" #include namespace MusECore { // Forward declarations: class Marker; class MidiPort; class MidiInstrument; class Track; class Part; class CtrlListList; class CtrlList; struct CtrlVal; extern std::list temporaryWavFiles; //!< Used for storing all tmp-files, for cleanup on shutdown //--------------------------------------------------------- // UndoOp //--------------------------------------------------------- struct UndoOp { enum UndoType { AddRoute, DeleteRoute, AddTrack, DeleteTrack, AddPart, DeletePart, MovePart, ModifyPartStart, ModifyPartLength, ModifyPartName, SelectPart, AddEvent, DeleteEvent, ModifyEvent, SelectEvent, AddAudioCtrlVal, DeleteAudioCtrlVal, ModifyAudioCtrlVal, ModifyAudioCtrlValList, // Add, delete and modify operate directly on the list. // setTempo does only if master is set, otherwise it operates on the static tempo value. AddTempo, DeleteTempo, ModifyTempo, SetTempo, SetStaticTempo, SetGlobalTempo, EnableMasterTrack, AddSig, DeleteSig, ModifySig, AddKey, DeleteKey, ModifyKey, ModifyTrackName, ModifyTrackChannel, SetTrackRecord, SetTrackMute, SetTrackSolo, SetTrackRecMonitor, SetTrackOff, MoveTrack, ModifyClip, AddMarker, DeleteMarker, ModifyMarker, // This one is provided separately for optimizing repeated adjustments. It is 'combo breaker' -aware. SetMarkerPos, //// For wholesale changes to the list. Preferred if multiple additions or deletions are required. //ModifyMarkerList, ModifySongLen, // a = new len, b = old len SetInstrument, DoNothing, ModifyMidiDivision, // These operations cannot be undone. They are 'one time' operations, removed after execution. EnableAllAudioControllers, GlobalSelectAllEvents, NormalizeMidiDivision }; UndoType type; union { //U() { memset( this, 0, sizeof( U ) ); } struct { int a; int b; int c; int d; int e; }; struct { const Part* part; unsigned old_partlen_or_pos; // FIXME FINDMICHJETZT XTicks!! unsigned new_partlen_or_pos; unsigned old_partlen; unsigned new_partlen; int64_t events_offset; Pos::TType events_offset_time_type; }; struct { int channel; int ctrl; int oVal; int nVal; }; struct { int startframe; //!< Start frame of changed data int endframe; //!< End frame of changed data QString* tmpwavfile; //!< The file with the changed data }; struct { Marker* oldMarker; Marker* newMarker; }; struct { const Track* _propertyTrack; int _oldPropValue; int _newPropValue; }; struct { CtrlListList* _ctrlListList; CtrlList* _eraseCtrlList; CtrlList* _addCtrlList; }; struct { int _audioCtrlID; int _audioCtrlFrame; int _audioNewCtrlFrame; double _audioCtrlVal; double _audioNewCtrlVal; }; struct { MidiPort* _midiPort; MidiInstrument* _oldMidiInstrument; MidiInstrument* _newMidiInstrument; }; }; QString* _oldName; QString* _newName; Event oEvent; Event nEvent; bool selected; bool selected_old; bool doCtrls; bool doClones; const Track* track; const Track* oldTrack; int trackno; Route routeFrom; Route routeTo; // If _noUndo is set, the operation cannot be undone. It is a 'one time' operation, removed after execution. // It allows mixed undoable and non-undoable operations in one list, all executed in one RT cycle. bool _noUndo; const char* typeName(); void dump(); UndoOp(); // NOTE: In these constructors, if noUndo is set, the operation cannot be undone. It is a 'one time' operation, removed after execution. // It allows mixed undoable and non-undoable operations in one list, all executed in one RT cycle. UndoOp(UndoType type, int a, int b, int c=0, bool noUndo = false); UndoOp(UndoType type, int n, const Track* track, bool noUndo = false); UndoOp(UndoType type, const Part* part, bool noUndo = false); UndoOp(UndoType type, const Part* part, const QString& old_name, const QString& new_name, bool noUndo = false); UndoOp(UndoType type, const Part* part, bool selected, bool selected_old, bool noUndo = false); UndoOp(UndoType type, const Part* part, unsigned int old_len_or_pos, unsigned int new_len_or_pos, Pos::TType new_time_type = Pos::TICKS, const Track* oTrack = 0, const Track* nTrack = 0, bool noUndo = false); UndoOp(UndoType type, const Part* part, unsigned int old_pos, unsigned int new_pos, unsigned int old_len, unsigned int new_len, int64_t events_offset, Pos::TType new_time_type = Pos::TICKS, bool noUndo = false); UndoOp(UndoType type, const Part* part, unsigned int old_len, unsigned int new_len, int64_t events_offset, Pos::TType new_time_type = Pos::TICKS, bool noUndo = false); UndoOp(UndoType type, const Event& nev, const Event& oev, const Part* part, bool doCtrls, bool doClones, bool noUndo = false); UndoOp(UndoType type, const Event& nev, const Part* part, bool, bool, bool noUndo = false); UndoOp(UndoType type, const Event& changedEvent, const QString& changeData, int startframe, int endframe, bool noUndo = false); UndoOp(UndoType type, const Marker& oldMarker, const Marker& newMarker, bool noUndo = false); UndoOp(UndoType type, const Marker& marker, bool noUndo = false); UndoOp(UndoType type, const Marker& marker, unsigned int new_pos, Pos::TType new_time_type, bool noUndo = false); // Takes ownership of the old list (deletes it). //UndoOp(UndoType type, MarkerList** oldMarkerList, MarkerList* newMarkerList, bool noUndo = false); UndoOp(UndoType type, const Track* track, const QString& old_name, const QString& new_name, bool noUndo = false); UndoOp(UndoType type, const Track* track, int old_chan, int new_chan, bool noUndo = false); //UndoOp(UndoType type, const Track* track, int ctrlID, int frame, bool noUndo = false); // Same as above. UndoOp(UndoType type, const Track* track, int ctrlID, int oldFrame, int newFrame, double oldValue, double newValue, bool noUndo = false); UndoOp(UndoType type, const Track* track, int ctrlID, int frame, double value, bool noUndo = false); UndoOp(UndoType type, const Track* track, bool value, bool noUndo = false); UndoOp(UndoType type, CtrlListList* ctrl_ll, CtrlList* eraseCtrlList, CtrlList* addCtrlList, bool noUndo = false); UndoOp(UndoType type, int tick, const MusECore::TimeSignature old_sig, const MusECore::TimeSignature new_sig, bool noUndo = false); UndoOp(UndoType type, const Route& route_from, const Route& route_to, bool noUndo = false); UndoOp(UndoType type, MidiPort* mp, MidiInstrument* instr, bool noUndo = false); UndoOp(UndoType type); }; class Undo : public std::list { public: Undo() : std::list() { combobreaker=false; } Undo(const Undo& other) : std::list(other) { this->combobreaker=other.combobreaker; } Undo& operator=(const Undo& other) { std::list::operator=(other); this->combobreaker=other.combobreaker; return *this;} bool empty() const; /** if set, forbid merging (below). * Defaults to false */ bool combobreaker; /** is possible, merges itself and other by appending * all contents of other at this->end(). * returns true if merged, false otherwise. * in case of success, the caller has to ensure that * other is deleted from the UndoList. */ bool merge_combo(const Undo& other); void push_front(const UndoOp& op); void push_back(const UndoOp& op); void insert(iterator position, const_iterator first, const_iterator last); void insert(iterator position, const UndoOp& op); void insert (iterator position, size_type n, const UndoOp& op); }; typedef Undo::iterator iUndoOp; typedef Undo::reverse_iterator riUndoOp; typedef Undo::const_iterator ciUndoOp; typedef Undo::const_reverse_iterator criUndoOp; class UndoList : public std::list { protected: bool isUndo; public: void clearDelete(); UndoList(bool _isUndo) : std::list() { isUndo=_isUndo; } }; typedef UndoList::iterator iUndo; typedef UndoList::reverse_iterator riUndo; } // namespace MusECore #endif // __UNDO_H__ muse-4.0.0/src/muse/value.cpp000066400000000000000000000043361404105311000160370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: value.cpp,v 1.2 2004/02/28 14:58:21 wschweer Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "value.h" #include "xml.h" namespace MusECore { IValue::IValue(QObject* parent, const char* name) : QObject(parent) { setObjectName(name); } BValue::BValue(QObject* parent, const char* name) : QObject(parent) { setObjectName(name); } //--------------------------------------------------------- // save //--------------------------------------------------------- void BValue::save(int level, Xml& xml) { xml.intTag(level, objectName().toLatin1().constData(), val); } //--------------------------------------------------------- // save //--------------------------------------------------------- void IValue::save(int level, Xml& xml) { xml.intTag(level, objectName().toLatin1().constData(), val); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void BValue::setValue(bool v) { if (val != v) { val = v; emit valueChanged(val); emit valueChanged(int(val)); } } void IValue::setValue(int v) { if (val != v) { val = v; emit valueChanged(val); } } } // namespace MusECore muse-4.0.0/src/muse/value.h000066400000000000000000000040731404105311000155020ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: value.h,v 1.1.1.1 2003/10/27 18:51:53 wschweer Exp $ // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __VALUE_H__ #define __VALUE_H__ #include namespace MusECore { class Xml; //--------------------------------------------------------- // IValue //--------------------------------------------------------- class IValue : public QObject { Q_OBJECT int val; signals: void valueChanged(int); public slots: void setValue(int v); public: IValue(QObject* parent=0, const char* name=0); int value() const { return val; } void save(int level, Xml& xml); }; //--------------------------------------------------------- // BValue //--------------------------------------------------------- class BValue : public QObject { Q_OBJECT bool val; signals: void valueChanged(bool); void valueChanged(int); public slots: void setValue(bool v); void setValue(int v) { setValue(bool(v)); } public: BValue(QObject* parent=0, const char* name=0); bool value() const { return val; } void save(int level, Xml& xml); }; } // namespace MusECore #endif muse-4.0.0/src/muse/vst.cpp000066400000000000000000000504161404105311000155370ustar00rootroot00000000000000//=================================================================== // MusE // Linux Music Editor // $Id: vst.cpp,v 1.5.2.6 2009/12/06 10:05:00 terminator356 Exp $ // // This code is based on jack_fst: // Copyright (C) 2004 Paul Davis // Torben Hohn // // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //=================================================================== #include "config.h" #ifdef VST_SUPPORT #include #include #include "muse_math.h" #include #include #include #include "vst.h" #include "globals.h" #include "synth.h" #include "jackaudio.h" #include "midi_consts.h" #include "xml.h" namespace MusECore { extern "C" void fst_error(const char *fmt, ...); extern long vstHostCallback (AEffect*, long, long, long, void*, float); extern JackAudioDevice* jackAudio; //--------------------------------------------------------- // vstHostCallback //--------------------------------------------------------- long vstHostCallback(AEffect* effect, long opcode, long index, long value, void* ptr, float opt) { static VstTimeInfo _timeInfo; jack_position_t jack_pos; jack_transport_state_t tstate; switch (opcode) { case audioMasterAutomate: // index, value, returns 0 effect->setParameter (effect, index, opt); return 0; case audioMasterVersion: // vst version, currently 2 (0 for older) return 2; case audioMasterCurrentId: // returns the unique id of a plug that's currently // loading return 0; case audioMasterIdle: // call application idle routine (this will // call effEditIdle for all open editors too) effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); return 0; case audioMasterPinConnected: // inquire if an input or output is being connected; // index enumerates input or output counting from zero: // value is 0 for input and != 0 otherwise. note: the // return value is 0 for such that older versions // will always return true. return 1; case audioMasterWantMidi: // is a filter which is currently ignored return 0; case audioMasterGetTime: // returns const VstTimeInfo* (or 0 if not supported) // should contain a mask indicating which fields are required // (see valid masks above), as some items may require extensive // conversions memset(&_timeInfo, 0, sizeof(_timeInfo)); if (effect) { tstate = jackAudio->transportQuery(&jack_pos); _timeInfo.samplePos = jack_pos.frame; _timeInfo.sampleRate = jack_pos.frame_rate; _timeInfo.flags = 0; if ((value & (kVstBarsValid|kVstTempoValid)) && (jack_pos.valid & JackPositionBBT)) { _timeInfo.tempo = jack_pos.beats_per_minute; _timeInfo.timeSigNumerator = (long) floor (jack_pos.beats_per_bar); _timeInfo.timeSigDenominator = (long) floor (jack_pos.beat_type); _timeInfo.flags |= (kVstBarsValid|kVstTempoValid); } if (tstate == JackTransportRolling) { _timeInfo.flags |= kVstTransportPlaying; } } else { _timeInfo.samplePos = 0; _timeInfo.sampleRate = sampleRate; } return (long)&_timeInfo; case audioMasterProcessEvents: // VstEvents* in return 0; case audioMasterSetTime: // VstTimenfo* in , filter in , not supported case audioMasterTempoAt: // returns tempo (in bpm * 10000) at sample frame location passed in return 0; case audioMasterGetNumAutomatableParameters: return 0; case audioMasterGetParameterQuantization: // returns the integer value for +1.0 representation, // or 1 if full single float precision is maintained // in automation. parameter index in (-1: all, any) return 0; case audioMasterIOChanged: // numInputs and/or numOutputs has changed return 0; case audioMasterNeedIdle: // plug needs idle calls (outside its editor window) return 0; case audioMasterSizeWindow: // index: width, value: height return 0; case audioMasterGetSampleRate: return 0; case audioMasterGetBlockSize: return 0; case audioMasterGetInputLatency: return 0; case audioMasterGetOutputLatency: return 0; case audioMasterGetPreviousPlug: // input pin in (-1: first to come), returns cEffect* return 0; case audioMasterGetNextPlug: // output pin in (-1: first to come), returns cEffect* case audioMasterWillReplaceOrAccumulate: // returns: 0: not supported, 1: replace, 2: accumulate return 0; case audioMasterGetCurrentProcessLevel: // returns: 0: not supported, // 1: currently in user thread (gui) // 2: currently in audio thread (where process is called) // 3: currently in 'sequencer' thread (midi, timer etc) // 4: currently offline processing and thus in user thread // other: not defined, but probably pre-empting user thread. return 0; case audioMasterGetAutomationState: // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write // offline return 0; case audioMasterOfflineStart: case audioMasterOfflineRead: // ptr points to offline structure, see below. return 0: error, 1 ok return 0; case audioMasterOfflineWrite: // same as read return 0; case audioMasterOfflineGetCurrentPass: case audioMasterOfflineGetCurrentMetaPass: return 0; case audioMasterSetOutputSampleRate: // for variable i/o, sample rate in return 0; case audioMasterGetSpeakerArrangement: // (long)input in , output in return 0; case audioMasterGetVendorString: // fills with a string identifying the vendor (max 64 char) strcpy ((char*) ptr, "LAD"); return 0; case audioMasterGetProductString: // fills with a string with product name (max 64 char) strcpy ((char*) ptr, "FreeST"); case audioMasterGetVendorVersion: // returns vendor-specific version return 1000; case audioMasterVendorSpecific: // no definition, vendor specific handling return 0; case audioMasterSetIcon: // void* in , format not defined yet return 0; case audioMasterCanDo: // string in ptr, see below return 0; case audioMasterGetLanguage: // see enum return 0; case audioMasterOpenWindow: // returns platform specific ptr return 0; case audioMasterCloseWindow: // close window, platform specific handle in return 0; case audioMasterGetDirectory: // get plug directory, FSSpec on MAC, else char* return 0; case audioMasterUpdateDisplay: // something has changed, update 'multi-fx' display effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f); return 0; case audioMasterBeginEdit: // begin of automation session (when mouse down), parameter index in return 0; case audioMasterEndEdit: // end of automation session (when mouse up), parameter index in return 0; case audioMasterOpenFileSelector: // open a fileselector window with VstFileSelect* in return 0; default: break; } return 0; } //--------------------------------------------------------- // scanVstDir //--------------------------------------------------------- static void scanVstDir(const QString& s) { if (debugMsg) printf("scan vst plugin dir <%s>\n", s.toLatin1()); QDir pluginDir(s, QString("*.dll"), QDir::Files); if (pluginDir.exists()) { const QFileInfoList* list = pluginDir.entryInfoList(); QFileInfoListIterator it(*list); QFileInfo* fi; while((fi = it.current())) { char* path = strdup(fi->filePath().toLatin1()); FSTInfo* info = fst_get_info(path); if (info) { if (info->numInputs == 0 && info->numOutputs) synthis.push_back(new VstSynth(*fi, fi->baseName(), QString(), QString(), QString())); fst_free_info(info); } free(path); ++it; } } } //--------------------------------------------------------- // fstSignalHandler //--------------------------------------------------------- static void fstSignalHandler(int sig, siginfo_t* /*info*/, void* /*context*/) { fst_error("fst signal handler %d, thread = 0x%x", sig, pthread_self ()); if (sig == SIGSEGV || sig == SIGABRT) { char*p = 0; *p = 0; } exit(-1); } void jfst_reserve_mem (int bufsize) { char buf [bufsize]; int i; fprintf (stderr, "Reserving memory: base=%p, size=%d, end=%p\n", buf, sizeof(buf), buf+sizeof(buf)); for (i=0; iplugin->flags & effFlagsHasEditor; } //--------------------------------------------------------- // incInstances //--------------------------------------------------------- void VstSynth::incInstances(int val) { _instances += val; if (_instances == 0 && fstHandle) { fst_unload(fstHandle); fstHandle = 0; } } //--------------------------------------------------------- // instantiate //--------------------------------------------------------- void* VstSynth::instantiate() { ++_instances; QString n; n.setNum(_instances); QString instanceName = baseName() + "-" + n; doSetuid(); QByteArray ba = info.filePath().toLatin1(); const char* path = ba.constData(); fstHandle = fst_load(path); if (fstHandle == 0) { printf("Synth::instantiate: cannot load vst plugin %s\n", path); undoSetuid(); return 0; } FST* fst = fst_instantiate(fstHandle, vstHostCallback, 0); if (fst == 0) { printf("Synth::instantiate:: cannot instantiate plugin %s\n", path); undoSetuid(); return 0; } AEffect* plugin = fst->plugin; plugin->dispatcher (plugin, effMainsChanged, 0, 1, 0, 0.0f); /* set program to zero */ plugin->dispatcher (plugin, effSetProgram, 0, 0, NULL, 0.0f); if (fst_run_editor(fst)) { printf("Synth::instantiate: cannot create gui"); undoSetuid(); return 0; } undoSetuid(); return fst; } //--------------------------------------------------------- // init //--------------------------------------------------------- bool VstSynthIF::init(Synth* s) { _fst = (FST*)((VstSynth*)s)->instantiate(); return (_fst == 0); } //--------------------------------------------------------- // channels //--------------------------------------------------------- int VstSynthIF::channels() const { AEffect* plugin = _fst->plugin; return plugin->numOutputs; } int VstSynthIF::totalOutChannels() const { AEffect* plugin = _fst->plugin; return plugin->numOutputs; } int VstSynthIF::totalInChannels() const { AEffect* plugin = _fst->plugin; return plugin->numInputs; } //--------------------------------------------------------- // createSIF //--------------------------------------------------------- SynthIF* VstSynth::createSIF(SynthI* s) { VstSynthIF* sif = new VstSynthIF(s); sif->init(this, s); return sif; } //--------------------------------------------------------- // deactivate3 //--------------------------------------------------------- void VstSynthIF::deactivate3() { if (_fst) { if (_guiVisible) fst_destroy_editor(_fst); fst_close(_fst); _fst = 0; } } //--------------------------------------------------------- // getParameter //--------------------------------------------------------- double VstSynthIF::getParameter(unsigned long idx) const { return _fst->plugin->getParameter(_fst->plugin, idx); } //--------------------------------------------------------- // setParameter //--------------------------------------------------------- void VstSynthIF::setParameter(unsigned long idx, double value) { _fst->plugin->setParameter(_fst->plugin, idx, value); } //--------------------------------------------------------- // write //--------------------------------------------------------- void VstSynthIF::write(int level, Xml& xml) const { //--------------------------------------------- // dump current state of synth //--------------------------------------------- int len = 0; const unsigned char* p; AEffect* plugin = _fst->plugin; int params = plugin->numParams; for (int i = 0; i < params; ++i) { xml.doubleTag(level, "param", plugin->getParameter(plugin, i)); } } //--------------------------------------------------------- // getData //--------------------------------------------------------- iMPEvent VstSynthIF::getData(MidiPort* mp, MPEventList* el, iMPEvent i, unsigned pos, int ports, unsigned n, float** buffer) { AEffect* plugin = _fst->plugin; for (; i != el->end(); ++i) { if (mp) mp->sendEvent(*i); else { if (putEvent(*i)) break; } } if (plugin->flags & effFlagsCanReplacing) { plugin->processReplacing(plugin, 0, buffer, n); } else { plugin->process(plugin, 0, buffer, n); } return el->end(); } //--------------------------------------------------------- // putEvent //--------------------------------------------------------- bool VstSynthIF::putEvent(const MidiPlayEvent& ev) { if (midiOutputTrace) ev.dump(); AEffect* plugin = _fst->plugin; static struct VstEvents events; static struct VstMidiEvent event; events.numEvents = 1; events.reserved = 0; events.events[0] = (VstEvent*)(&event); event.type = kVstMidiType; event.byteSize = 24; event.deltaFrames = 0; event.flags = 0; event.detune = 0; event.noteLength = 0; event.noteOffset = 0; event.reserved1 = 0; event.reserved2 = 0; event.noteOffVelocity = 0; switch (ev.type()) { case ME_PITCHBEND: { int a = ev.dataA() + 8192; int b = a >> 7; event.midiData[0] = (ev.type() | ev.channel()) & 0xff; event.midiData[1] = a & 0x7f; event.midiData[2] = b & 0x7f; event.midiData[3] = 0; } break; case ME_CONTROLLER: case ME_NOTEON: default: event.midiData[0] = (ev.type() | ev.channel()) & 0xff; event.midiData[1] = ev.dataA() & 0xff; event.midiData[2] = ev.dataB() & 0xff; event.midiData[3] = 0; break; } int rv = plugin->dispatcher(plugin, effProcessEvents, 0, 0, &events, 0.0f); return false; } } // namespace MusECore #else namespace MusECore { void initVST() {} } // namespace MusECore #endif muse-4.0.0/src/muse/vst.h000066400000000000000000000074511404105311000152050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: vst.h,v 1.11.2.3 2009/11/25 09:09:44 terminator356 Exp $ // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __VST_H__ #define __VST_H__ #include "synth.h" //class QMenu; namespace MusEGui { class PopupMenu; } namespace MusECore { struct _FSTHandle; struct _FST; //--------------------------------------------------------- // VstSynth //--------------------------------------------------------- class VstSynth : public Synth { _FSTHandle* fstHandle; public: VstSynth(const QFileInfo& fi) : Synth(fi, fi->baseName()) { fstHandle = 0; } virtual ~VstSynth() {} virtual Type synthType() const { return VST_SYNTH; } virtual void incInstances(int val); virtual void* instantiate(); virtual SynthIF* createSIF(SynthI*) const; }; //--------------------------------------------------------- // VstSynthIF // VSTi synthesizer instance //--------------------------------------------------------- class VstSynthIF : public SynthIF { _FST* _fst; bool _guiVisible; public: VstSynthIF(SynthI* s) : SynthIF(s) { _fst = 0; _guiVisible = false; } virtual void guiHeartBeat() { } virtual bool guiVisible() const { return false; } virtual void showGui(bool) { } virtual bool hasGui() const { return false; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool v); virtual bool hasNativeGui() const; virtual void getGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } virtual void setGeometry(int, int, int, int) {} virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const { *x=0;*y=0;*w=0;*h=0; } virtual void setNativeGeometry(int, int, int, int) {} virtual iMPEvent getData(MidiPort*, MPEventList*, iMPEvent, unsigned pos, int ports, unsigned n, float** buffer) ; virtual bool putEvent(const MidiPlayEvent& ev); virtual MidiPlayEvent receiveEvent(); virtual int eventsPending() const { return 0; } virtual bool init(Synth*); virtual int channels() const; virtual int totalOutChannels() const; virtual int totalInChannels() const; virtual void deactivate3(); virtual QString getPatchName(int, int, bool) const { return ""; } virtual void populatePatchPopup(PopupMenu*, int, bool) {}; virtual void write(int level, Xml& xml) const; virtual double getParameter(unsigned long idx) const; virtual void setParameter(unsigned long idx, double value); virtual int getControllerInfo(int, QString*, int*, int*, int*, int*) { return 0; } //------------------------- // Methods for PluginIBase: //------------------------- virtual bool addScheduledControlEvent(unsigned long /*i*/, double /*val*/, unsigned /*frame*/) { return true; } // returns true if event cannot be delivered }; } // namespace MusECore #endif muse-4.0.0/src/muse/vst_native.cpp000066400000000000000000003527521404105311000171150ustar00rootroot00000000000000//=================================================================== // MusE // Linux Music Editor // // vst_native.cpp // (C) Copyright 2012-2013 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //=================================================================== #include "config.h" #ifdef VST_NATIVE_SUPPORT #include #include #include #include #include #include #include "muse_math.h" #include #include #include #include #include "globals.h" #include "gconfig.h" #include "audio.h" #include "synth.h" #include "jackaudio.h" #include "midi_consts.h" #include "xml.h" #include "plugin.h" #include "popupmenu.h" #include "pos.h" #include "tempo.h" #include "sync.h" #include "sig.h" #include "minstrument.h" #include "vst_native.h" #include "pluglist.h" #define OLD_PLUGIN_ENTRY_POINT "main" #define NEW_PLUGIN_ENTRY_POINT "VSTPluginMain" // Enable debugging messages //#define VST_NATIVE_DEBUG //#define VST_NATIVE_DEBUG_PROCESS namespace MusECore { extern JackAudioDevice* jackAudio; static VstIntPtr currentPluginId = 0; static sem_t _vstIdLock; //----------------------------------------------------------------------------------------- // vstHostCallback // This must be a function, it cannot be a class method so we dispatch to various objects from here. //----------------------------------------------------------------------------------------- VstIntPtr VSTCALLBACK vstNativeHostCallback(AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) { // Is this callback for an actual instance? Hand-off to the instance if so. //VSTPlugin* plugin; if(effect && effect->user) { VstNativeSynthOrPlugin *userData = (VstNativeSynthOrPlugin*)(effect->user); //return ((VstNativeSynthIF*)plugin)->hostCallback(opcode, index, value, ptr, opt); return VstNativeSynth::pluginHostCallback(userData, opcode, index, value, ptr, opt); } // No instance found. So we are just scanning for plugins... #ifdef VST_NATIVE_DEBUG fprintf(stderr, "vstNativeHostCallback eff:%p opcode:%ld\n", effect, opcode); #endif switch (opcode) { case audioMasterAutomate: // index, value, returns 0 return 0; case audioMasterVersion: // vst version, currently 2 (0 for older) return 2300; case audioMasterCurrentId: // returns the unique id of a plug that's currently // loading return currentPluginId; case audioMasterIdle: // call application idle routine (this will // call effEditIdle for all open editors too) return 0; case audioMasterGetTime: // returns const VstTimeInfo* (or 0 if not supported) // should contain a mask indicating which fields are required // (see valid masks above), as some items may require extensive // conversions return 0; case audioMasterProcessEvents: // VstEvents* in return 0; case audioMasterIOChanged: // numInputs and/or numOutputs has changed return 0; case audioMasterSizeWindow: // index: width, value: height return 0; case audioMasterGetSampleRate: return MusEGlobal::sampleRate; case audioMasterGetBlockSize: return MusEGlobal::segmentSize; case audioMasterGetInputLatency: return 0; case audioMasterGetOutputLatency: return 0; case audioMasterGetCurrentProcessLevel: // returns: 0: not supported, // 1: currently in user thread (gui) // 2: currently in audio thread (where process is called) // 3: currently in 'sequencer' thread (midi, timer etc) // 4: currently offline processing and thus in user thread // other: not defined, but probably pre-empting user thread. return 0; case audioMasterGetAutomationState: // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write // offline return 0; case audioMasterOfflineStart: case audioMasterOfflineRead: // ptr points to offline structure, see below. return 0: error, 1 ok return 0; case audioMasterOfflineWrite: // same as read return 0; case audioMasterOfflineGetCurrentPass: case audioMasterOfflineGetCurrentMetaPass: return 0; case audioMasterGetVendorString: // fills with a string identifying the vendor (max 64 char) strcpy ((char*) ptr, "MusE"); return 1; case audioMasterGetProductString: // fills with a string with product name (max 64 char) strcpy ((char*) ptr, "NativeVST"); return 1; case audioMasterGetVendorVersion: // returns vendor-specific version return 2000; case audioMasterVendorSpecific: // no definition, vendor specific handling return 0; case audioMasterCanDo: // string in ptr, see below return 0; case audioMasterGetLanguage: // see enum return kVstLangEnglish; case audioMasterGetDirectory: // get plug directory, FSSpec on MAC, else char* return 0; case audioMasterUpdateDisplay: // something has changed, update 'multi-fx' display return 0; case audioMasterBeginEdit: // begin of automation session (when mouse down), parameter index in return 0; case audioMasterEndEdit: // end of automation session (when mouse up), parameter index in return 0; case audioMasterOpenFileSelector: // open a fileselector window with VstFileSelect* in return 0; case audioMasterCloseFileSelector: return 0; #ifdef VST_FORCE_DEPRECATED #ifndef VST_2_4_EXTENSIONS // deprecated in 2.4 case audioMasterGetSpeakerArrangement: // (long)input in , output in return 0; case audioMasterPinConnected: // inquire if an input or output is being connected; // index enumerates input or output counting from zero: // value is 0 for input and != 0 otherwise. note: the // return value is 0 for such that older versions // will always return true. //return 1; return 0; // VST 2.0 opcodes... case audioMasterWantMidi: // is a filter which is currently ignored return 0; case audioMasterSetTime: // VstTimenfo* in , filter in , not supported return 0; case audioMasterTempoAt: // returns tempo (in bpm * 10000) at sample frame location passed in return 0; // TODO: case audioMasterGetNumAutomatableParameters: return 0; case audioMasterGetParameterQuantization: // returns the integer value for +1.0 representation, // or 1 if full single float precision is maintained // in automation. parameter index in (-1: all, any) //return 0; return 1; case audioMasterNeedIdle: // plug needs idle calls (outside its editor window) return 0; case audioMasterGetPreviousPlug: // input pin in (-1: first to come), returns cEffect* return 0; case audioMasterGetNextPlug: // output pin in (-1: first to come), returns cEffect* return 0; case audioMasterWillReplaceOrAccumulate: // returns: 0: not supported, 1: replace, 2: accumulate //return 0; return 1; case audioMasterSetOutputSampleRate: // for variable i/o, sample rate in return 0; case audioMasterSetIcon: // void* in , format not defined yet return 0; case audioMasterOpenWindow: // returns platform specific ptr return 0; case audioMasterCloseWindow: // close window, platform specific handle in return 0; #endif #endif default: break; } if(MusEGlobal::debugMsg) fprintf(stderr, " unknown opcode\n"); return 0; } //--------------------------------------------------------- // initVST_Native //--------------------------------------------------------- void initVST_Native() { #ifdef VST_NATIVE_SUPPORT #ifdef VST_VESTIGE_SUPPORT printf("Initializing Native VST support. Using VESTIGE compatibility implementation.\n"); #else printf("Initializing Native VST support. Using Steinberg VSTSDK.\n"); #endif #endif sem_init(&_vstIdLock, 0, 1); const char* message = "scanVstNativeLib: "; const MusEPlugin::PluginScanList& scan_list = MusEPlugin::pluginList; for(MusEPlugin::ciPluginScanList isl = scan_list.begin(); isl != scan_list.end(); ++isl) { const MusEPlugin::PluginScanInfoRef inforef = *isl; const MusEPlugin::PluginScanInfoStruct& info = inforef->info(); switch(info._type) { case MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST: { #ifdef VST_NATIVE_SUPPORT if(MusEGlobal::loadNativeVST) { const QString inf_cbname = PLUGIN_GET_QSTRING(info._completeBaseName); const QString inf_name = PLUGIN_GET_QSTRING(info._name); const QString inf_uri = PLUGIN_GET_QSTRING(info._uri); const Plugin* plug_found = MusEGlobal::plugins.find( inf_cbname, inf_uri, inf_name); const Synth* synth_found = MusEGlobal::synthis.find( inf_cbname, inf_uri, inf_name); if(plug_found) { fprintf(stderr, "Ignoring LinuxVST effect name:%s uri:%s path:%s duplicate of path:%s\n", PLUGIN_GET_CSTRING(info._name), PLUGIN_GET_CSTRING(info._uri), PLUGIN_GET_CSTRING(info.filePath()), plug_found->filePath().toLatin1().constData()); } if(synth_found) { fprintf(stderr, "Ignoring LinuxVST synth name:%s uri:%s path:%s duplicate of path:%s\n", PLUGIN_GET_CSTRING(info._name), PLUGIN_GET_CSTRING(info._uri), PLUGIN_GET_CSTRING(info.filePath()), synth_found->filePath().toLatin1().constData()); } const bool is_effect = info._class & MusEPlugin::PluginScanInfoStruct::PluginClassEffect; const bool is_synth = info._class & MusEPlugin::PluginScanInfoStruct::PluginClassInstrument; const bool add_plug = (is_effect || is_synth) && info._inports > 0 && info._outports > 0 && !plug_found; // For now we allow effects as a synth track. Until we allow programs (and midi) in the effect rack. const bool add_synth = (is_synth || is_effect) && !synth_found; if(add_plug || add_synth) { VstNativeSynth* new_synth = new VstNativeSynth(info); if(add_synth) { if(MusEGlobal::debugMsg) fprintf(stderr, "scanVstNativeLib: adding vst synth plugin:%s name:%s effectName:%s vendorString:%s productString:%s vstver:%d\n", PLUGIN_GET_CSTRING(info.filePath()), PLUGIN_GET_CSTRING(info._completeBaseName), PLUGIN_GET_CSTRING(info._name), PLUGIN_GET_CSTRING(info._maker), PLUGIN_GET_CSTRING(info._description), info._apiVersionMajor ); MusEGlobal::synthis.push_back(new_synth); } if(add_plug) { if(MusEGlobal::debugMsg) info.dump(message); MusEGlobal::plugins.push_back( new VstNativePluginWrapper(new_synth, info._requiredFeatures) ); } } } #endif } break; case MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA: case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI: case MusEPlugin::PluginScanInfoStruct::PluginTypeVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeLV2: case MusEPlugin::PluginScanInfoStruct::PluginTypeMESS: case MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown: case MusEPlugin::PluginScanInfoStruct::PluginTypeNone: case MusEPlugin::PluginScanInfoStruct::PluginTypeAll: break; } } } //--------------------------------------------------------- // VstNativeSynth //--------------------------------------------------------- VstNativeSynth::VstNativeSynth(const QFileInfo& fi, const QString& uri, AEffect* plugin, const QString& label, const QString& desc, const QString& maker, const QString& ver, VstIntPtr id, void *dlHandle, bool isSynth, PluginFeatures_t reqFeatures) : Synth(fi, uri, label, desc, maker, ver, reqFeatures) { _handle = dlHandle; _id = id; _hasGui = plugin->flags & effFlagsHasEditor; _inports = plugin->numInputs; _outports = plugin->numOutputs; _controlInPorts = plugin->numParams; //#ifndef VST_VESTIGE_SUPPORT _hasChunks = plugin->flags & 32 /*effFlagsProgramChunks*/; //#else // _hasChunks = false; //#endif _flags = 0; _vst_version = 0; _vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f); // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far. if(_vst_version >= 2) { if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstEvents", 0.0f) > 0) _flags |= canReceiveVstEvents; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstEvents", 0.0f) > 0) _flags |= canSendVstEvents; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstMidiEvent", 0.0f) > 0) _flags |= canSendVstMidiEvents; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"sendVstTimeInfo", 0.0f) > 0) _flags |= canSendVstTimeInfo; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstMidiEvent", 0.0f) > 0) _flags |= canReceiveVstMidiEvents; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"receiveVstTimeInfo", 0.0f) > 0) _flags |= canReceiveVstTimeInfo; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"offline", 0.0f) > 0) _flags |=canProcessVstOffline; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"plugAsChannelInsert", 0.0f) > 0) _flags |= canUseVstAsInsert; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"plugAsSend", 0.0f) > 0) _flags |= canUseVstAsSend; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"mixDryWet", 0.0f) > 0) _flags |= canMixVstDryWet; if(plugin->dispatcher(plugin, effCanDo, 0, 0, (void*)"midiProgramNames", 0.0f) > 0) _flags |= canVstMidiProgramNames; } _isSynth = isSynth; } VstNativeSynth::VstNativeSynth(const MusEPlugin::PluginScanInfoStruct& info) : Synth(PLUGIN_GET_QSTRING(info.filePath()), PLUGIN_GET_QSTRING(info._uri), PLUGIN_GET_QSTRING(info._label), PLUGIN_GET_QSTRING(info._description), PLUGIN_GET_QSTRING(info._maker), PLUGIN_GET_QSTRING(info._version), info._requiredFeatures) { // _handle = dlHandle; _handle = NULL; _id = info._subID; _hasGui = info._pluginFlags & MusEPlugin::PluginScanInfoStruct::HasGui; _inports = info._inports; _outports = info._outports; _controlInPorts = info._controlInPorts; //#ifndef VST_VESTIGE_SUPPORT _hasChunks = info._pluginFlags & MusEPlugin::PluginScanInfoStruct::HasChunks; //#else // _hasChunks = false; //#endif _vst_version = info._apiVersionMajor; _flags = info._vstPluginFlags; _isSynth = info._class & MusEPlugin::PluginScanInfoStruct::PluginClassInstrument; } //--------------------------------------------------------- // incInstances //--------------------------------------------------------- void VstNativeSynth::incInstances(int val) { _instances += val; if(_instances == 0) { if(_handle && _id == 0) { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynth::incInstances no more instances, closing library\n"); #endif dlclose(_handle); _handle = NULL; } iIdx.clear(); oIdx.clear(); rpIdx.clear(); midiCtl2PortMap.clear(); port2MidiCtlMap.clear(); } } //--------------------------------------------------------- // instantiate //--------------------------------------------------------- AEffect* VstNativeSynth::instantiate(void* userData) { int inst_num = _instances; inst_num++; QString n; n.setNum(inst_num); QString instanceName = baseName() + "-" + n; QByteArray ba = info.filePath().toLatin1(); const char* path = ba.constData(); void* hnd = _handle; //int vst_version; if(hnd == NULL) { hnd = dlopen(path, RTLD_NOW); if(hnd == NULL) { fprintf(stderr, "dlopen(%s) failed: %s\n", path, dlerror()); return NULL; } } AEffect *(*getInstance)(audioMasterCallback); getInstance = (AEffect*(*)(audioMasterCallback))dlsym(hnd, NEW_PLUGIN_ENTRY_POINT); if(!getInstance) { if(MusEGlobal::debugMsg) { fprintf(stderr, "VST 2.4 entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" not found in library %s, looking for \"" OLD_PLUGIN_ENTRY_POINT "\"\n", path); } getInstance = (AEffect*(*)(audioMasterCallback))dlsym(hnd, OLD_PLUGIN_ENTRY_POINT); if(!getInstance) { fprintf(stderr, "ERROR: VST entrypoints \"" NEW_PLUGIN_ENTRY_POINT "\" or \"" OLD_PLUGIN_ENTRY_POINT "\" not found in library\n"); dlclose(hnd); return NULL; } else if(MusEGlobal::debugMsg) { fprintf(stderr, "VST entrypoint \"" OLD_PLUGIN_ENTRY_POINT "\" found\n"); } } else if(MusEGlobal::debugMsg) { fprintf(stderr, "VST entrypoint \"" NEW_PLUGIN_ENTRY_POINT "\" found\n"); } sem_wait(&_vstIdLock); currentPluginId = _id; AEffect *plugin = getInstance(vstNativeHostCallback); sem_post(&_vstIdLock); if(!plugin) { fprintf(stderr, "ERROR: Failed to instantiate plugin in VST library \"%s\"\n", path); if(_id == 0) dlclose(hnd); return NULL; } else if(MusEGlobal::debugMsg) fprintf(stderr, "plugin instantiated\n"); if(plugin->magic != kEffectMagic) { fprintf(stderr, "Not a VST plugin in library \"%s\"\n", path); if(_id == 0) dlclose(hnd); return NULL; } else if(MusEGlobal::debugMsg) fprintf(stderr, "plugin is a VST\n"); if(!(plugin->flags & effFlagsHasEditor)) { if(MusEGlobal::debugMsg) fprintf(stderr, "Plugin has no GUI\n"); } else if(MusEGlobal::debugMsg) fprintf(stderr, "Plugin has a GUI\n"); if(!(plugin->flags & effFlagsCanReplacing)) fprintf(stderr, "Plugin does not support processReplacing\n"); else if(MusEGlobal::debugMsg) fprintf(stderr, "Plugin supports processReplacing\n"); plugin->user = userData; // "2 = VST2.x, older versions return 0". Observed 2400 on all the ones tested so far. //vst_version = plugin->dispatcher(plugin, effGetVstVersion, 0, 0, NULL, 0.0f); /*if(!((plugin->flags & effFlagsIsSynth) || (vst_version >= 2 && plugin->dispatcher(plugin, effCanDo, 0, 0,(void*) "receiveVstEvents", 0.0f) > 0))) { if(MusEGlobal::debugMsg) fprintf(stderr, "Plugin is not a synth\n"); goto _error; }*/ ++_instances; _handle = hnd; return plugin; /* _error: //plugin->dispatcher(plugin, effMainsChanged, 0, 0, NULL, 0); plugin->dispatcher(plugin, effClose, 0, 0, NULL, 0); if(_id == 0) dlclose(hnd); return NULL; */ } //--------------------------------------------------------- // openPlugin // static //--------------------------------------------------------- bool VstNativeSynth::openPlugin(AEffect* plugin) { plugin->dispatcher(plugin, effOpen, 0, 0, NULL, 0); // work around to get airwave to work (author contacted so maybe another solution will // reveal itself) plugin->dispatcher(plugin, effSetSampleRate, 0, 0, NULL, MusEGlobal::sampleRate); plugin->dispatcher(plugin, effSetBlockSize, 0, MusEGlobal::segmentSize, NULL, 0.0f); plugin->dispatcher(plugin, effMainsChanged, 0, 1, NULL, 0.0f); //plugin->dispatcher(plugin, effSetProgram, 0, 0, NULL, 0.0f); // REMOVE Tim. Or keep? return true; } //--------------------------------------------------------- // createSIF //--------------------------------------------------------- SynthIF* VstNativeSynth::createSIF(SynthI* s) { VstNativeSynthIF* sif = new VstNativeSynthIF(s); sif->init(this); return sif; } //--------------------------------------------------------- // VstNativeSynthIF //--------------------------------------------------------- VstNativeSynthIF::VstNativeSynthIF(SynthI* s) : SynthIF(s) { _guiVisible = false; _gw = NULL; _synth = NULL; _plugin = NULL; _active = false; _editor = NULL; _inProcess = false; _controls = NULL; // controlsOut = 0; _audioInBuffers = NULL; _audioInSilenceBuf = NULL; _audioOutBuffers = NULL; userData.pstate = 0; userData.sif = this; } VstNativeSynthIF::~VstNativeSynthIF() { // Just in case it wasn't removed or deactivate3 wasn't called etc... if(_plugin) fprintf(stderr, "ERROR: ~VstNativeSynthIF: _plugin is not NULL!\n"); if(_audioOutBuffers) { unsigned long op = _synth->outPorts(); for(unsigned long i = 0; i < op; ++i) { if(_audioOutBuffers[i]) free(_audioOutBuffers[i]); } delete[] _audioOutBuffers; } if(_audioInBuffers) { unsigned long ip = _synth->inPorts(); for(unsigned long i = 0; i < ip; ++i) { if(_audioInBuffers[i]) free(_audioInBuffers[i]); } delete[] _audioInBuffers; } if(_audioInSilenceBuf) free(_audioInSilenceBuf); if(_controls) delete[] _controls; if(_gw) delete[] _gw; } //--------------------------------------------------------- // init //--------------------------------------------------------- bool VstNativeSynthIF::init(Synth* s) { _synth = (VstNativeSynth*)s; _plugin = _synth->instantiate(&userData); if(!_plugin) return false; if(!_synth->openPlugin(_plugin)) return false; queryPrograms(); unsigned long outports = _synth->outPorts(); if(outports != 0) { _audioOutBuffers = new float*[outports]; for(unsigned long k = 0; k < outports; ++k) { #ifdef _WIN32 _audioOutBuffers[k] = (float *) _aligned_malloc(16, sizeof(float *) * MusEGlobal::segmentSize); if(_audioOutBuffers[k] == NULL) { fprintf(stderr, "ERROR: VstNativeSynthIF::init: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)&_audioOutBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: VstNativeSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) _audioOutBuffers[k][q] = MusEGlobal::denormalBias; } else memset(_audioOutBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); } } unsigned long inports = _synth->inPorts(); if(inports != 0) { _audioInBuffers = new float*[inports]; for(unsigned long k = 0; k < inports; ++k) { #ifdef _WIN32 _audioInBuffers[k] = (float *) _aligned_malloc(16, sizeof(float *) * MusEGlobal::segmentSize); if(_audioInBuffers[k] == NULL) { fprintf(stderr, "ERROR: VstNativeSynthIF::init: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)&_audioInBuffers[k], 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: VstNativeSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) _audioInBuffers[k][q] = MusEGlobal::denormalBias; } else memset(_audioInBuffers[k], 0, sizeof(float) * MusEGlobal::segmentSize); } #ifdef _WIN32 _audioInSilenceBuf = (float *) _aligned_malloc(16, sizeof(float *) * MusEGlobal::segmentSize); if(_audioInSilenceBuf == NULL) { fprintf(stderr, "ERROR: VstNativeSynthIF::init: _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void**)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: VstNativeSynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(MusEGlobal::config.useDenormalBias) { for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) _audioInSilenceBuf[q] = MusEGlobal::denormalBias; } else memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); } _controls = NULL; _gw = NULL; unsigned long controlPorts = _synth->inControls(); if(controlPorts != 0) { _controls = new Port[controlPorts]; _gw = new VstNativeGuiWidgets[controlPorts]; } for(unsigned long i = 0; i < controlPorts; ++i) { _gw[i].pressed = false; _controls[i].idx = i; //float val; // TODO //ladspaDefaultValue(ld, k, &val); // FIXME TODO float val = _plugin->getParameter(_plugin, i); // TODO _controls[i].val = val; _controls[i].tmpVal = val; _controls[i].enCtrl = true; // Support a special block for synth ladspa controllers. // Put the ID at a special block after plugins (far after). int id = genACnum(MusECore::MAX_PLUGINS, i); const char* param_name = paramName(i); // TODO FIXME! ///float min, max; ///ladspaControlRange(ld, k, &min, &max); float min = 0.0, max = 1.0; CtrlList* cl; CtrlListList* cll = track()->controller(); iCtrlList icl = cll->find(id); if (icl == cll->end()) { cl = new CtrlList(id); cll->add(cl); cl->setCurVal(_controls[i].val); //cl->setCurVal(_plugin->getParameter(_plugin, i)); } else { cl = icl->second; _controls[i].val = cl->curVal(); if(dispatch(26 /*effCanBeAutomated*/, i, 0, NULL, 0.0f) == 1) { double v = cl->curVal(); if(v != _plugin->getParameter(_plugin, i)) _plugin->setParameter(_plugin, i, v); } #ifdef VST_NATIVE_DEBUG else fprintf(stderr, "VstNativeSynthIF::init %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), i); #endif } cl->setRange(min, max); cl->setName(QString(param_name)); //cl->setValueType(ladspaCtrlValueType(ld, k)); cl->setValueType(ctrlValueType(i)); //cl->setMode(ladspaCtrlMode(ld, k)); cl->setMode(ctrlMode(i)); } activate(); return true; } //--------------------------------------------------------- // resizeEditor //--------------------------------------------------------- bool VstNativeSynth::resizeEditor(MusEGui::VstNativeEditor *editor, int w, int h) { if(!editor || w <= 0 || h <= 0) return false; if (editor->fixScaling() && editor->devicePixelRatio() >= 1.0) { w = qRound((qreal)w / editor->devicePixelRatio()); h = qRound((qreal)h / editor->devicePixelRatio()); } editor->setFixedSize(w, h); return true; } //--------------------------------------------------------- // vstconfWrite //--------------------------------------------------------- void VstNativeSynth::vstconfWrite(AEffect *plugin, const QString& name, int level, Xml &xml) { if(hasChunks()) { //--------------------------------------------- // dump current state of synth //--------------------------------------------- fprintf(stderr, "%s: commencing chunk data dump, plugin api version=%d\n", name.toLatin1().constData(), vstVersion()); unsigned long len = 0; void* p = 0; len = plugin->dispatcher(plugin, 23 /* effGetChunk */, 0, 0, &p, 0.0); if (len) { QByteArray arrOut = QByteArray::fromRawData((char *)p, len); // Weee! Compression! QByteArray outEnc64 = qCompress(arrOut).toBase64(); QString customData(outEnc64); for (int pos=0; pos < customData.size(); pos+=150) { customData.insert(pos++,'\n'); // add newlines for readability } xml.strTag(level, "customData", customData); } } } //--------------------------------------------------------- // vstconfSet //--------------------------------------------------------- void VstNativeSynth::vstconfSet(AEffect *plugin, const std::vector &customParams) { if(customParams.size() == 0) return; if(!hasChunks()) { return; } for(size_t i = 0; i < customParams.size(); i++) { QString param = customParams [i]; param.remove('\n'); // remove all linebreaks that may have been added to prettyprint the songs file QByteArray paramIn; paramIn.append(param.toUtf8()); // Try to uncompress the data. QByteArray dec64 = qUncompress(QByteArray::fromBase64(paramIn)); // Failed? Try uncompressed. if(dec64.isEmpty()) dec64 = QByteArray::fromBase64(paramIn); plugin->dispatcher(plugin, 24 /* effSetChunk */, 0, dec64.size(), (void*)dec64.data(), 0.0); // index 0: is bank 1: is program break; //one customData tag includes all data in base64 } } //--------------------------------------------------------- // hostCallback //--------------------------------------------------------- VstIntPtr VstNativeSynth::pluginHostCallback(VstNativeSynthOrPlugin *userData, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) { static VstTimeInfo _timeInfo; #ifdef VST_NATIVE_DEBUG if(opcode != audioMasterGetTime) fprintf(stderr, "VstNativeSynthIF::hostCallback %s opcode:%ld\n", name().toLatin1().constData(), opcode); #endif switch (opcode) { case audioMasterAutomate: // index, value, returns 0 ///_plugin->setParameter (_plugin, index, opt); VstNativeSynth::guiControlChanged(userData, index, opt); return 0; case audioMasterVersion: // vst version, currently 2 (0 for older) return 2300; case audioMasterCurrentId: { // returns the unique id of a plug that's currently // loading ///return 0; AEffect *vstPlug = 0; if(userData->sif) vstPlug = userData->sif->_plugin; else if(userData->pstate) vstPlug = userData->pstate->plugin; return vstPlug->uniqueID; } case audioMasterIdle: // call application idle routine (this will // call effEditIdle for all open editors too) //_plugin->updateParamValues(false); //_plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); ///idleEditor(); // REMOVE Tim. Or keep. return 0; case audioMasterGetTime: { // returns const VstTimeInfo* (or 0 if not supported) // should contain a mask indicating which fields are required // (see valid masks above), as some items may require extensive // conversions // FIXME TODO: Optimizations: This may be called many times in one process call // due to our multi-run slices. Some of the (costly) info will be redundant. // So try to add some flag to try to only call some or all of this once per cycle. #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::hostCallback master time: valid: nanos:%d ppqpos:%d tempo:%d bars:%d cyclepos:%d sig:%d smpte:%d clock:%d\n", (bool)(value & kVstNanosValid), (bool)(value & kVstPpqPosValid), (bool)(value & kVstTempoValid), (bool)(value & kVstBarsValid), (bool)(value & kVstCyclePosValid), (bool)(value & kVstTimeSigValid), (bool)(value & kVstSmpteValid), (bool)(value & kVstClockValid)); #endif memset(&_timeInfo, 0, sizeof(_timeInfo)); unsigned int curr_frame = MusEGlobal::audio->pos().frame(); _timeInfo.samplePos = (double)curr_frame; _timeInfo.sampleRate = (double)MusEGlobal::sampleRate; _timeInfo.flags = 0; Pos p(MusEGlobal::extSyncFlag ? MusEGlobal::audio->tickPos() : curr_frame, MusEGlobal::extSyncFlag ? true : false); if(value & kVstBarsValid) { int p_bar, p_beat, p_tick; p.mbt(&p_bar, &p_beat, &p_tick); _timeInfo.barStartPos = (double)Pos(p_bar, 0, 0).tick() / (double)MusEGlobal::config.division; _timeInfo.flags |= kVstBarsValid; } if(value & kVstTimeSigValid) { int z, n; MusEGlobal::sigmap.timesig(p.tick(), z, n); #ifndef VST_VESTIGE_SUPPORT _timeInfo.timeSigNumerator = (long)z; _timeInfo.timeSigDenominator = (long)n; #else _timeInfo.timeSigNumerator = z; _timeInfo.timeSigDenominator = n; #endif _timeInfo.flags |= kVstTimeSigValid; } if(value & kVstPpqPosValid) { _timeInfo.ppqPos = (double)MusEGlobal::audio->tickPos() / (double)MusEGlobal::config.division; _timeInfo.flags |= kVstPpqPosValid; } if(value & kVstTempoValid) { double tempo = MusEGlobal::tempomap.tempo(p.tick()); _timeInfo.tempo = ((double)MusEGlobal::tempomap.globalTempo() * 600000.0) / tempo; _timeInfo.flags |= kVstTempoValid; } #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::hostCallback master time: sample pos:%f samplerate:%f sig num:%ld den:%ld tempo:%f\n", _timeInfo.samplePos, _timeInfo.sampleRate, _timeInfo.timeSigNumerator, _timeInfo.timeSigDenominator, _timeInfo.tempo); #endif if(MusEGlobal::audio->isPlaying()) _timeInfo.flags |= (kVstTransportPlaying | kVstTransportChanged); // TODO //if(MusEGlobal::audio->isRecording()) // _timeInfo.flags |= (kVstTransportRecording | kVstTransportChanged); #ifdef _WIN32 return *((long*)(&_timeInfo)); #else return (long)&_timeInfo; #endif } case audioMasterProcessEvents: { // VstEvents* in VstEvents* ve = (VstEvents*)ptr; int num_ev = ve->numEvents; #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::hostCallback audioMasterProcessEvents: numEvents:%d\n", num_ev); #endif for(int i = 0; i < num_ev; ++i) { // Due to incomplete vestige midi type support, cast as VstMidiEvent first in order to get the type. VstMidiEvent* vme = (VstMidiEvent*)ve->events[i]; switch(vme->type) { // Is it really a midi event? case kVstMidiType: { #ifdef VST_NATIVE_DEBUG fprintf(stderr, " kVstMidiType: deltaFrames:%d midiData[0]:%u [1]:%u [2]:%u\n", vme->deltaFrames, (unsigned char)vme->midiData[0], (unsigned char)vme->midiData[1], (unsigned char)vme->midiData[2]); #endif if(userData->sif) userData->sif->eventReceived(vme); //else if(userData->pstate) // TODO Plugin midi // vstPlug = userData->pstate->plugin; } break; #ifndef VST_VESTIGE_SUPPORT case kVstSysExType: break; #endif default: #ifdef VST_NATIVE_DEBUG fprintf(stderr, " unknown event type:%d\n", vme->type); #endif break; } } //return 0; // TODO: return 1; // Supported and processed. } case audioMasterIOChanged: // numInputs and/or numOutputs has changed return 0; case audioMasterSizeWindow: { // index: width, value: height MusEGui::VstNativeEditor *editor = userData->sif ? userData->sif->_editor : userData->pstate->editor; if(VstNativeSynth::resizeEditor(editor, int(index), int(value))) return 1; // supported. return 0; } case audioMasterGetSampleRate: //return 0; return MusEGlobal::sampleRate; case audioMasterGetBlockSize: //return 0; return MusEGlobal::segmentSize; case audioMasterGetInputLatency: return 0; case audioMasterGetOutputLatency: return 0; case audioMasterGetCurrentProcessLevel: { // returns: 0: not supported, // 1: currently in user thread (gui) // 2: currently in audio thread (where process is called) // 3: currently in 'sequencer' thread (midi, timer etc) // 4: currently offline processing and thus in user thread // other: not defined, but probably pre-empting user thread. bool inProcessNow = userData->sif ? userData->sif->_inProcess : userData->pstate->inProcess; if(inProcessNow) return 2; else return 1; } case audioMasterGetAutomationState: // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write // offline return 1; // TODO: case audioMasterOfflineStart: case audioMasterOfflineRead: // ptr points to offline structure, see below. return 0: error, 1 ok return 0; case audioMasterOfflineWrite: // same as read return 0; case audioMasterOfflineGetCurrentPass: case audioMasterOfflineGetCurrentMetaPass: return 0; case audioMasterGetVendorString: // fills with a string identifying the vendor (max 64 char) strcpy ((char*) ptr, "MusE"); return 1; case audioMasterGetProductString: // fills with a string with product name (max 64 char) strcpy ((char*) ptr, "MusE Sequencer"); return 1; case audioMasterGetVendorVersion: // returns vendor-specific version return 2000; case audioMasterVendorSpecific: // no definition, vendor specific handling return 0; case audioMasterCanDo: // string in ptr, see below if(!strcmp((char*)ptr, "sendVstEvents") || !strcmp((char*)ptr, "receiveVstMidiEvent") || !strcmp((char*)ptr, "sendVstMidiEvent") || !strcmp((char*)ptr, "sendVstTimeInfo") || !strcmp((char*)ptr, "sizeWindow") || !strcmp((char*)ptr, "supplyIdle")) return 1; #if 0 //ifndef VST_VESTIGE_SUPPORT else if(!strcmp((char*)ptr, "openFileSelector") || !strcmp((char*)ptr, "closeFileSelector")) return 1; #endif return 0; case audioMasterGetLanguage: // see enum //return 0; return kVstLangEnglish; case audioMasterGetDirectory: // get plug directory, FSSpec on MAC, else char* return 0; case audioMasterUpdateDisplay: { // something has changed, update 'multi-fx' display //_plugin->updateParamValues(false); //QApplication::processEvents(); // REMOVE Tim. Or keep. Commented in QTractor. AEffect *vstPlug = 0; if(userData->sif) vstPlug = userData->sif->_plugin; else if(userData->pstate) vstPlug = userData->pstate->plugin; vstPlug->dispatcher(vstPlug, effEditIdle, 0, 0, NULL, 0.0f); // ? return 0; } case audioMasterBeginEdit: // begin of automation session (when mouse down), parameter index in VstNativeSynth::guiAutomationBegin(userData, index); return 1; case audioMasterEndEdit: // end of automation session (when mouse up), parameter index in VstNativeSynth::guiAutomationEnd(userData, index); return 1; #if 0 //ifndef VST_VESTIGE_SUPPORT case audioMasterOpenFileSelector: // open a fileselector window with VstFileSelect* in return 0; case audioMasterCloseFileSelector: return 0; #endif #ifdef VST_FORCE_DEPRECATED #ifndef VST_2_4_EXTENSIONS // deprecated in 2.4 case audioMasterGetSpeakerArrangement: // (long)input in , output in return 0; case audioMasterPinConnected: // inquire if an input or output is being connected; // index enumerates input or output counting from zero: // value is 0 for input and != 0 otherwise. note: the // return value is 0 for such that older versions // will always return true. //return 1; return 0; // VST 2.0 opcodes... case audioMasterWantMidi: // is a filter which is currently ignored return 0; case audioMasterSetTime: // VstTimenfo* in , filter in , not supported return 0; case audioMasterTempoAt: // returns tempo (in bpm * 10000) at sample frame location passed in return 0; // TODO: case audioMasterGetNumAutomatableParameters: return 0; case audioMasterGetParameterQuantization: // returns the integer value for +1.0 representation, // or 1 if full single float precision is maintained // in automation. parameter index in (-1: all, any) //return 0; return 1; case audioMasterNeedIdle: // plug needs idle calls (outside its editor window) return 0; case audioMasterGetPreviousPlug: // input pin in (-1: first to come), returns cEffect* return 0; case audioMasterGetNextPlug: // output pin in (-1: first to come), returns cEffect* return 0; case audioMasterWillReplaceOrAccumulate: // returns: 0: not supported, 1: replace, 2: accumulate //return 0; return 1; case audioMasterSetOutputSampleRate: // for variable i/o, sample rate in return 0; case audioMasterSetIcon: // void* in , format not defined yet return 0; case audioMasterOpenWindow: // returns platform specific ptr return 0; case audioMasterCloseWindow: // close window, platform specific handle in return 0; #endif #endif default: break; } return 0; } //--------------------------------------------------------- // idleEditor //--------------------------------------------------------- void VstNativeSynthIF::idleEditor() { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::idleEditor %p\n", this); #endif // REMOVE Tim. Or keep. //_plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); //if(_editor) // _editor->update(); } //--------------------------------------------------------- // guiHeartBeat //--------------------------------------------------------- void VstNativeSynthIF::guiHeartBeat() { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::guiHeartBeat %p\n", this); #endif // REMOVE Tim. Or keep. if(_plugin && _active) { //#ifdef VST_FORCE_DEPRECATED // REMOVE Tim. Or keep //_plugin->dispatcher(_plugin, effIdle, 0, 0, NULL, 0.0f); //#endif if(_guiVisible) { _plugin->dispatcher(_plugin, effEditIdle, 0, 0, NULL, 0.0f); if(_editor) _editor->update(); } } } //--------------------------------------------------------- // nativeGuiVisible //--------------------------------------------------------- bool VstNativeSynthIF::nativeGuiVisible() const { return _guiVisible; } //--------------------------------------------------------- // showGui //--------------------------------------------------------- void VstNativeSynthIF::showNativeGui(bool v) { if(!(_plugin->flags & effFlagsHasEditor)) // || v == nativeGuiVisible()) return; if(v) { if(_editor) { if(!_editor->isVisible()) _editor->show(); _editor->raise(); _editor->activateWindow(); } else { Qt::WindowFlags wflags(Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint); _editor = new MusEGui::VstNativeEditor(nullptr, wflags); _editor->open(this, nullptr); } } else { if(_editor) { _editor->close(); //_editor = NULL; // No - done in editorDeleted. } } _guiVisible = v; } //--------------------------------------------------------- // editorOpened //--------------------------------------------------------- void VstNativeSynthIF::getNativeGeometry(int*x, int*y, int*w, int*h) const { if(!_editor) { *x=0;*y=0;*w=0;*h=0; return; } *x = _editor->x(); *y = _editor->y(); *w = _editor->width(); *h = _editor->height(); } //--------------------------------------------------------- // editorOpened //--------------------------------------------------------- void VstNativeSynthIF::setNativeGeometry(int x, int y, int w, int h) { if(!_editor) return; _editor->setGeometry(x, y, w, h); } //--------------------------------------------------------- // editorOpened //--------------------------------------------------------- void VstNativeSynthIF::editorOpened() { _guiVisible = true; } //--------------------------------------------------------- // editorClosed //--------------------------------------------------------- void VstNativeSynthIF::editorClosed() { _guiVisible = false; } //--------------------------------------------------------- // editorDeleted //--------------------------------------------------------- void VstNativeSynthIF::editorDeleted() { _editor = NULL; } //--------------------------------------------------------- // receiveEvent //--------------------------------------------------------- MidiPlayEvent VstNativeSynthIF::receiveEvent() { return MidiPlayEvent(); } //--------------------------------------------------------- // eventReceived //--------------------------------------------------------- void VstNativeSynthIF::eventReceived(VstMidiEvent* ev) { const int port = synti->midiPort(); MidiRecordEvent event; event.setB(0); //event.setPort(_port); event.setPort(port); // NOTE: From muse_qt4_evolution. Not done here in Muse-2 (yet). // move all events 2*MusEGlobal::segmentSize into the future to get // jitterfree playback // // cycle n-1 n n+1 // -+----------+----------+----------+- // ^ ^ ^ // catch process play // // These Jack events arrived in the previous period, and it may not have been at the audio position before this one (after a seek). // This is how our ALSA driver works, events there are timestamped asynchronous of any process, referenced to the CURRENT audio // position, so that by the time of the NEXT process, THOSE events have also occurred in the previous period. // So, technically this is correct. What MATTERS is how we adjust the times for storage, and/or simultaneous playback in THIS period, // and TEST: we'll need to make sure any non-contiguous previous period is handled correctly by process - will it work OK as is? // If ALSA works OK than this should too... event.setTime(MusEGlobal::audio->pos().frame() + ev->deltaFrames); event.setTick(MusEGlobal::lastExtMidiSyncTick); // event.setChannel(*(ev->buffer) & 0xf); // int type = *(ev->buffer) & 0xf0; // int a = *(ev->buffer + 1) & 0x7f; // int b = *(ev->buffer + 2) & 0x7f; event.setChannel(ev->midiData[0] & 0xf); int type = ev->midiData[0] & 0xf0; int a = ev->midiData[1] & 0x7f; int b = ev->midiData[2] & 0x7f; event.setType(type); switch(type) { case ME_NOTEON: // REMOVE Tim. Noteoff. Added. // Convert zero-velocity note ons to note offs as per midi spec. if(b == 0) event.setType(ME_NOTEOFF); // Fall through. case ME_NOTEOFF: case ME_CONTROLLER: case ME_POLYAFTER: //event.setA(*(ev->buffer + 1)); //event.setB(*(ev->buffer + 2)); event.setA(ev->midiData[1]); event.setB(ev->midiData[2]); break; case ME_PROGRAM: case ME_AFTERTOUCH: //event.setA(*(ev->buffer + 1)); event.setA(ev->midiData[1]); break; case ME_PITCHBEND: event.setA(((b << 7) + a) - 8192); break; case ME_SYSEX: { //int type = *(ev->buffer) & 0xff; int type = ev->midiData[0] & 0xff; switch(type) { // TODO: Sysex NOT supported with Vestige ! // case ME_SYSEX: // // // TODO: Deal with large sysex, which are broken up into chunks! // // For now, do not accept if the last byte is not EOX, meaning it's a chunk with more chunks to follow. // if(*(((unsigned char*)ev->buffer) + ev->size - 1) != ME_SYSEX_END) // { // if(MusEGlobal::debugMsg) // printf("VstNativeSynthIF::eventReceived sysex chunks not supported!\n"); // return; // } // // //event.setTime(0); // mark as used // event.setType(ME_SYSEX); // event.setData((unsigned char*)(ev->buffer + 1), ev->size - 2); // break; case ME_MTC_QUARTER: //if(_port != -1) if(port != -1) { //MusEGlobal::midiSyncContainer.mtcInputQuarter(_port, *(ev->buffer + 1)); MusEGlobal::midiSyncContainer.mtcInputQuarter(port, ev->midiData[1]); } return; case ME_SONGPOS: //if(_port != -1) if(port != -1) { //MusEGlobal::midiSyncContainer.setSongPosition(_port, *(ev->buffer + 1) | (*(ev->buffer + 2) << 7 )); // LSB then MSB MusEGlobal::midiSyncContainer.setSongPosition(port, ev->midiData[1] | (ev->midiData[2] << 7 )); // LSB then MSB } return; //case ME_SONGSEL: //case ME_TUNE_REQ: //case ME_SENSE: // return; // TODO: Hm, need the last frame time... Isn't that the same as audio->pos().frame() like above? // case ME_CLOCK: // const jack_nframes_t abs_ft = jack_last_frame_time(jc) - MusEGlobal::segmentSize + ev->time; // midiClockInput(abs_ft); // return; // case ME_TICK: // case ME_START: // case ME_CONTINUE: // case ME_STOP: // { // if(MusEGlobal::audioDevice && MusEGlobal::audioDevice->deviceType() == JACK_MIDI && _port != -1) // { // MusECore::JackAudioDevice* jad = static_cast(MusEGlobal::audioDevice); // jack_client_t* jc = jad->jackClient(); // if(jc) // { // jack_nframes_t abs_ft = jack_last_frame_time(jc) + ev->time; // double abs_ev_t = double(jack_frames_to_time(jc, abs_ft)) / 1000000.0; // MusEGlobal::midiSyncContainer.realtimeSystemInput(_port, type, abs_ev_t); // } // } // return; // } //case ME_SYSEX_END: //break; // return; default: if(MusEGlobal::debugMsg) printf("VstNativeSynthIF::eventReceived unsupported system event 0x%02x\n", type); return; } } //return; break; default: if(MusEGlobal::debugMsg) printf("VstNativeSynthIF::eventReceived unknown event 0x%02x\n", type); //printf("VstNativeSynthIF::eventReceived unknown event 0x%02x size:%d buf:0x%02x 0x%02x 0x%02x ...0x%02x\n", type, ev->size, *(ev->buffer), *(ev->buffer + 1), *(ev->buffer + 2), *(ev->buffer + (ev->size - 1))); return; } #ifdef VST_NATIVE_DEBUG printf("VstNativeSynthIF::eventReceived time:%d type:%d ch:%d A:%d B:%d\n", event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); #endif // Let recordEvent handle it from here, with timestamps, filtering, gui triggering etc. synti->recordEvent(event); } //--------------------------------------------------------- // hasGui //--------------------------------------------------------- bool VstNativeSynthIF::hasNativeGui() const { return _plugin->flags & effFlagsHasEditor; } //--------------------------------------------------------- // channels //--------------------------------------------------------- int VstNativeSynthIF::channels() const { return _plugin->numOutputs > MusECore::MAX_CHANNELS ? MusECore::MAX_CHANNELS : _plugin->numOutputs ; } int VstNativeSynthIF::totalOutChannels() const { return _plugin->numOutputs; } int VstNativeSynthIF::totalInChannels() const { return _plugin->numInputs; } //--------------------------------------------------------- // deactivate3 //--------------------------------------------------------- void VstNativeSynthIF::deactivate3() { if(_editor) { delete _editor; _editor = NULL; _guiVisible = false; } deactivate(); if (_plugin) { _plugin->dispatcher (_plugin, effClose, 0, 0, NULL, 0); _plugin = NULL; } } //--------------------------------------------------------- // queryPrograms //--------------------------------------------------------- void VstNativeSynthIF::queryPrograms() { char buf[256]; programs.clear(); int num_progs = _plugin->numPrograms; int iOldIndex = dispatch(effGetProgram, 0, 0, NULL, 0.0f); bool need_restore = false; for(int prog = 0; prog < num_progs; ++prog) { buf[0] = 0; //#ifndef VST_VESTIGE_SUPPORT // value = category. -1 = regular linear. if(dispatch(effGetProgramNameIndexed, prog, -1, buf, 0.0f) == 0) { //#endif dispatch(effSetProgram, 0, prog, NULL, 0.0f); dispatch(effGetProgramName, 0, 0, buf, 0.0f); need_restore = true; //#ifndef VST_VESTIGE_SUPPORT } //#endif int bankH = (prog >> 14) & 0x7f; int bankL = (prog >> 7) & 0x7f; int patch = prog & 0x7f; VST_Program p; p.name = QString(buf); //p.program = prog & 0x7f; //p.bank = prog << 7; p.program = (bankH << 16) | (bankL << 8) | patch; programs.push_back(p); } // Restore current program. if(need_restore) // && num_progs > 0) { dispatch(effSetProgram, 0, iOldIndex, NULL, 0.0f); fprintf(stderr, "FIXME: VstNativeSynthIF::queryPrograms(): effGetProgramNameIndexed returned 0. Used ugly effSetProgram/effGetProgramName instead\n"); } } //--------------------------------------------------------- // doSelectProgram //--------------------------------------------------------- void VstNativeSynthIF::doSelectProgram(int bankH, int bankL, int prog) { if(!_plugin) return; #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::doSelectProgram bankH:%d bankL:%d prog:%d\n", bankH, bankL, prog); #endif // // Only if there's something to change... // if(bankH >= 128 && bankL >= 128 && prog >= 128) // return; if(bankH > 127) // Map "dont care" to 0 bankH = 0; if(bankL > 127) bankL = 0; if(prog > 127) prog = 0; int p = (bankH << 14) | (bankL << 7) | prog; if(p >= _plugin->numPrograms) { fprintf(stderr, "VstNativeSynthIF::doSelectProgram program:%d out of range\n", p); return; } //for (unsigned short i = 0; i < instances(); ++i) //{ // "host calls this before a new program (effSetProgram) is loaded" #ifndef VST_VESTIGE_SUPPORT //if(dispatch(effBeginSetProgram, 0, 0, NULL, 0.0f) == 1) // TESTED: Usually it did not acknowledge. So IGNORE it. dispatch(effBeginSetProgram, 0, 0, NULL, 0.0f); //{ #endif dispatch(effSetProgram, 0, p, NULL, 0.0f); //dispatch(effSetProgram, 0, prog, NULL, 0.0f); // "host calls this after the new program (effSetProgram) has been loaded" #ifndef VST_VESTIGE_SUPPORT dispatch(effEndSetProgram, 0, 0, NULL, 0.0f); //} //else // fprintf(stderr, "VstNativeSynthIF::doSelectProgram bankH:%d bankL:%d prog:%d Effect did not acknowledge effBeginSetProgram\n", bankH, bankL, prog); #endif //} // TODO: Is this true of VSTs? See the similar section in dssihost.cpp // REMOVE Tim. // "A plugin is permitted to re-write the values of its input control ports when select_program is called. // The host should re-read the input control port values and update its own records appropriately. // (This is the only circumstance in which a DSSI plugin is allowed to modify its own input ports.)" From dssi.h // Need to update the automation value, otherwise it overwrites later with the last automation value. if(id() != -1) { const unsigned long sic = _synth->inControls(); for(unsigned long k = 0; k < sic; ++k) { // We're in the audio thread context: no need to send a message, just modify directly. //synti->setPluginCtrlVal(genACnum(id(), k), _controls[k].val); //synti->setPluginCtrlVal(genACnum(id(), k), _plugin->getParameter(_plugin, k)); const float v = _plugin->getParameter(_plugin, k); _controls[k].val = v; synti->setPluginCtrlVal(genACnum(id(), k), v); } } // // Reset parameters default value... // TODO ? // AEffect *pVstEffect = vst_effect(0); // if (pVstEffect) { // const qtractorPlugin::Params& params = qtractorPlugin::params(); // qtractorPlugin::Params::ConstIterator param = params.constBegin(); // for ( ; param != params.constEnd(); ++param) { // qtractorPluginParam *pParam = param.value(); // float *pfValue = pParam->subject()->data(); // *pfValue = pVstEffect->getParameter(pVstEffect, pParam->index()); // pParam->setDefaultValue(*pfValue); // } // } } //--------------------------------------------------------- // getPatchName //--------------------------------------------------------- QString VstNativeSynthIF::getPatchName(int /*chan*/, int prog, bool /*drum*/) const { unsigned long program = prog & 0xff; unsigned long lbank = (prog >> 8) & 0xff; unsigned long hbank = (prog >> 16) & 0xff; if (program > 127) // Map "dont care" to 0 program = 0; if (lbank > 127) lbank = 0; if (hbank > 127) hbank = 0; unsigned long p = (hbank << 16) | (lbank << 8) | program; unsigned long vp = (hbank << 14) | (lbank << 7) | program; //if((int)vp < _plugin->numPrograms) if(vp < programs.size()) { for(std::vector::const_iterator i = programs.begin(); i != programs.end(); ++i) { if(i->program == p) return i->name; } } return "?"; } //--------------------------------------------------------- // populatePatchPopup //--------------------------------------------------------- void VstNativeSynthIF::populatePatchPopup(MusEGui::PopupMenu* menu, int /*chan*/, bool /*drum*/) { // The plugin can change the programs, patches etc. // So make sure we're up to date by calling queryPrograms. queryPrograms(); menu->clear(); for (std::vector::const_iterator i = programs.begin(); i != programs.end(); ++i) { //int bank = i->bank; int prog = i->program; //int id = (bank << 7) + prog; const int hb = (prog >> 16) & 0xff; const int lb = (prog >> 8) & 0xff; const int pr = prog & 0xff; const bool vhb = hb != 0xff; const bool vlb = lb != 0xff; const bool vpr = pr != 0xff; QString astr; if(vhb || vlb || vpr) { if(vhb) astr += QString::number(hb + 1) + QString(":"); if(vlb) astr += QString::number(lb + 1) + QString(":"); else if(vhb) astr += QString("--:"); if(vpr) astr += QString::number(pr + 1); else if(vhb && vlb) astr += QString("--"); astr += QString(" "); } astr += i->name; QAction *act = menu->addAction(astr); //act->setData(id); act->setData(prog); } } //--------------------------------------------------------- // getParameter //--------------------------------------------------------- double VstNativeSynthIF::getParameter(unsigned long idx) const { if(idx >= _synth->inControls()) { fprintf(stderr, "VstNativeSynthIF::getParameter param number %lu out of range of ports:%lu\n", idx, _synth->inControls()); return 0.0; } return _plugin->getParameter(_plugin, idx); } //--------------------------------------------------------- // setParameter //--------------------------------------------------------- void VstNativeSynthIF::setParameter(unsigned long idx, double value) { addScheduledControlEvent(idx, value, MusEGlobal::audio->curFrame()); } //--------------------------------------------------------- // guiAutomationBegin //--------------------------------------------------------- void VstNativeSynth::guiAutomationBegin(VstNativeSynthOrPlugin *userData, unsigned long param_idx) { //_gw[param_idx].pressed = true; //not used AudioTrack* t = userData->sif ? userData->sif->track() : userData->pstate->pluginI->track(); int plug_id = userData->sif ? userData->sif->id() : userData->pstate->pluginI->id(); if(t && plug_id != -1) { plug_id = genACnum(plug_id, param_idx); //if(params[param].type == GuiParam::GUI_SLIDER) //{ //double val = ((Slider*)params[param].actuator)->value(); float val = userData->sif ? userData->sif->param(param_idx) : userData->pstate->pluginI->param(param_idx); // FIXME TODO: //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) // val = pow(10.0, val/20.0); //else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) // val = rint(val); //plugin->setParam(param, val); //((DoubleLabel*)params[param].label)->setValue(val); //if(t) //{ t->startAutoRecord(plug_id, val); t->setPluginCtrlVal(plug_id, val); //} //} // else if(params[param].type == GuiParam::GUI_SWITCH) // { // float val = (float)((CheckBox*)params[param].actuator)->isChecked(); // plugin->setParam(param, val); // // //if(t) // //{ // t->startAutoRecord(plug_id, val); // t->setPluginCtrlVal(plug_id, val); // //} // } } if(userData->sif) { userData->sif->enableController(param_idx, false); } else { userData->pstate->pluginI->enableController(param_idx, false); } } //--------------------------------------------------------- // guiAutomationEnd //--------------------------------------------------------- void VstNativeSynth::guiAutomationEnd(VstNativeSynthOrPlugin *userData, unsigned long param_idx) { AutomationType at = AUTO_OFF; AudioTrack* t = userData->sif ? userData->sif->track() : userData->pstate->pluginI->track(); int plug_id = userData->sif ? userData->sif->id() : userData->pstate->pluginI->id(); if(t) at = t->automationType(); if(t && plug_id != -1) { plug_id = genACnum(plug_id, param_idx); //if(params[param].type == GuiParam::GUI_SLIDER) //{ //double val = ((Slider*)params[param].actuator)->value(); float val = userData->sif ? userData->sif->param(param_idx) : userData->pstate->pluginI->param(param_idx); // FIXME TODO: //if (LADSPA_IS_HINT_LOGARITHMIC(params[param].hint)) // val = pow(10.0, val/20.0); //else if (LADSPA_IS_HINT_INTEGER(params[param].hint)) // val = rint(val); t->stopAutoRecord(plug_id, val); //} } // Special for switch - don't enable controller until transport stopped. if ((at == AUTO_OFF) || (at == AUTO_TOUCH)) // && (params[param].type != GuiParam::GUI_SWITCH || // FIXME TODO // !MusEGlobal::audio->isPlaying()) ) ) { if(userData->sif) { userData->sif->enableController(param_idx, true); } else { userData->pstate->pluginI->enableController(param_idx, true); } } //_gw[param_idx].pressed = false; //not used } //--------------------------------------------------------- // guiControlChanged //--------------------------------------------------------- int VstNativeSynth::guiControlChanged(VstNativeSynthOrPlugin *userData, unsigned long param_idx, float value) { VstNativeSynth *synth = userData->sif ? userData->sif->_synth : userData->pstate->pluginWrapper->_synth; #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynth::guiControlChanged received oscControl port:%lu val:%f\n", param_idx, value); #endif if(param_idx >= synth->inControls()) { fprintf(stderr, "VstNativeSynth::guiControlChanged: port number:%lu is out of range of index list size:%lu\n", param_idx, synth->inControls()); return 0; } // Record automation: // Take care of this immediately rather than in the fifo processing. int plugId = userData->sif ? userData->sif->id() : userData->pstate->pluginI->id(); if(plugId != -1) { unsigned long pid = genACnum(plugId, param_idx); if(userData->sif) { userData->sif->synti->recordAutomation(pid, value); } else { userData->pstate->pluginI->track()->recordAutomation(pid, value); } } // Schedules a timed control change: ControlEvent ce; ce.unique = false; // Not used for native vst. ce.fromGui = true; // It came from the plugin's own GUI. ce.idx = param_idx; ce.value = value; // Don't use timestamp(), because it's circular, which is making it impossible to deal // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. ce.frame = MusEGlobal::audio->curFrame(); ControlFifo &cfifo = userData->sif ? userData->sif->_controlFifo : userData->pstate->pluginI->_controlFifo; if(cfifo.put(ce)) fprintf(stderr, "VstNativeSynthIF::guiControlChanged: fifo overflow: in control number:%lu\n", param_idx); if(userData->sif) { userData->sif->enableController(param_idx, false); } else { userData->pstate->pluginI->enableController(param_idx, false); } return 0; } //--------------------------------------------------------- // write //--------------------------------------------------------- void VstNativeSynthIF::write(int level, Xml& xml) const { //#ifndef VST_VESTIGE_SUPPORT _synth->vstconfWrite(_plugin, name(), level, xml); //#else // fprintf(stderr, "support for vst chunks not compiled in!\n"); //#endif //--------------------------------------------- // dump current state of synth //--------------------------------------------- int params = _plugin->numParams; for (int i = 0; i < params; ++i) xml.doubleTag(level, "param", _plugin->getParameter(_plugin, i)); } //--------------------------------------------------------- // setCustomData //--------------------------------------------------------- void VstNativeSynthIF::setCustomData(const std::vector< QString > &customParams) { _synth->vstconfSet(_plugin, customParams); } //--------------------------------------------------------- // getData //--------------------------------------------------------- void VstNativeSynthIF::setVstEvent(VstMidiEvent* event, int a, int b, int c, int d) { event->type = kVstMidiType; event->byteSize = 24; event->deltaFrames = 0; event->flags = 0; event->detune = 0; event->noteLength = 0; event->noteOffset = 0; event->reserved1 = 0; event->reserved2 = 0; event->noteOffVelocity = 0; event->midiData[0] = a; event->midiData[1] = b; event->midiData[2] = c; event->midiData[3] = d; } //--------------------------------------------------------- // getData //--------------------------------------------------------- bool VstNativeSynthIF::processEvent(const MidiPlayEvent& e, VstMidiEvent* event) { int type = e.type(); int chn = e.channel(); int a = e.dataA(); int b = e.dataB(); #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event type:%d chn:%d a:%d b:%d\n", type, chn, a, b); #endif // REMOVE Tim. Noteoff. Added. const MidiInstrument::NoteOffMode nom = synti->noteOffMode(); switch(type) { case ME_NOTEON: #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_NOTEON\n"); #endif // REMOVE Tim. Noteoff. Added. if(b == 0) { // Handle zero-velocity note ons. Technically this is an error because internal midi paths // are now all 'note-off' without zero-vel note ons - they're converted to note offs. // Nothing should be setting a Note type Event's on velocity to zero. // But just in case... If we get this warning, it means there is still code to change. fprintf(stderr, "VstNativeSynthIF::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", e.time(), e.type(), chn, a, b); switch(nom) { // Instrument uses note offs. Convert to zero-vel note off. case MidiInstrument::NoteOffAll: //if(MusEGlobal::midiOutputTrace) // fprintf(stderr, "MidiOut: VST_Native: Following event will be converted to zero-velocity note off:\n"); setVstEvent(event, (ME_NOTEOFF | chn) & 0xff, a & 0x7f, 0); break; // Instrument uses no note offs at all. Send as-is. case MidiInstrument::NoteOffNone: // Instrument converts all note offs to zero-vel note ons. Send as-is. case MidiInstrument::NoteOffConvertToZVNoteOn: setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); break; } } else setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); break; case ME_NOTEOFF: #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_NOTEOFF\n"); #endif // REMOVE Tim. Noteoff. Changed. // setVstEvent(event, (type | chn) & 0xff, a & 0x7f, 0); switch(nom) { // Instrument uses note offs. Send as-is. case MidiInstrument::NoteOffAll: setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b); break; // Instrument uses no note offs at all. Send nothing. Eat up the event - return false. case MidiInstrument::NoteOffNone: return false; // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. case MidiInstrument::NoteOffConvertToZVNoteOn: //if(MusEGlobal::midiOutputTrace) // fprintf(stderr, "MidiOut: VST_Native: Following event will be converted to zero-velocity note on:\n"); setVstEvent(event, (ME_NOTEON | chn) & 0xff, a & 0x7f, 0); break; } break; // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore. case ME_PROGRAM: { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_PROGRAM\n"); #endif int hb, lb; synti->currentProg(chn, NULL, &lb, &hb); synti->setCurrentProg(chn, a & 0xff, lb, hb); doSelectProgram(hb, lb, a); return false; // Event pointer not filled. Return false. } break; case ME_CONTROLLER: { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER\n"); #endif // Our internal hwCtrl controllers support the 'unknown' value. // Don't send 'unknown' values to the driver. Ignore and return no error. if(b == CTRL_VAL_UNKNOWN) return false; if(a == CTRL_PROGRAM) { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PROGRAM\n"); #endif int bankH = (b >> 16) & 0xff; int bankL = (b >> 8) & 0xff; int prog = b & 0xff; synti->setCurrentProg(chn, prog, bankL, bankH); doSelectProgram(bankH, bankL, prog); return false; // Event pointer not filled. Return false. } if(a == CTRL_HBANK) { int lb, pr; synti->currentProg(chn, &pr, &lb, NULL); synti->setCurrentProg(chn, pr, lb, b & 0xff); doSelectProgram(b, lb, pr); // Event pointer not filled. Return false. return false; } if(a == CTRL_LBANK) { int hb, pr; synti->currentProg(chn, &pr, NULL, &hb); synti->setCurrentProg(chn, pr, b & 0xff, hb); doSelectProgram(hb, b, pr); // Event pointer not filled. Return false. return false; } if(a == CTRL_PITCH) { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PITCH\n"); #endif int v = b + 8192; setVstEvent(event, (ME_PITCHBEND | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f); return true; } if(a == CTRL_AFTERTOUCH) { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_AFTERTOUCH\n"); #endif setVstEvent(event, (ME_AFTERTOUCH | chn) & 0xff, b & 0x7f); return true; } if((a | 0xff) == CTRL_POLYAFTER) { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_POLYAFTER\n"); #endif setVstEvent(event, (ME_POLYAFTER | chn) & 0xff, a & 0x7f, b & 0x7f); return true; } #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_CONTROLLER, dataA is:%d\n", a); #endif // Regular controller. Pass it on. setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); return true; // REMOVE Tim. Or keep. TODO For native vsts? Or not... // // const LADSPA_Descriptor* ld = dssi->LADSPA_Plugin; // // ciMidiCtl2LadspaPort ip = synth->midiCtl2PortMap.find(a); // // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)? // // NOTE: There's no way to tell which of these controllers is supported by the plugin. // // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port. // if(ip == synth->midiCtl2PortMap.end()) // { // int ctlnum = a; // if(midiControllerType(a) != MidiController::Controller7) // return false; // Event pointer not filled. Return false. // else // { // #ifdef VST_NATIVE_DEBUG // fprintf(stderr, "VstNativeSynthIF::processEvent non-ladspa midi event is Controller7. Current dataA:%d\n", a); // #endif // a &= 0x7f; // ctlnum = DSSI_CC_NUMBER(ctlnum); // } // // // Fill the event. // #ifdef VST_NATIVE_DEBUG // fprintf(stderr, "VstNativeSynthIF::processEvent non-ladspa filling midi event chn:%d dataA:%d dataB:%d\n", chn, a, b); // #endif // snd_seq_ev_clear(event); // event->queue = SND_SEQ_QUEUE_DIRECT; // snd_seq_ev_set_controller(event, chn, a, b); // return true; // } // // unsigned long k = ip->second; // unsigned long i = controls[k].idx; // int ctlnum = DSSI_NONE; // if(dssi->get_midi_controller_for_port) // ctlnum = dssi->get_midi_controller_for_port(handle, i); // // // No midi controller for the ladspa port? Send to ladspa control. // if(ctlnum == DSSI_NONE) // { // // Sanity check. // if(k > synth->_controlInPorts) // return false; // // // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. // ctlnum = k + (CTRL_NRPN14_OFFSET + 0x2000); // } // else // { // #ifdef VST_NATIVE_DEBUG // fprintf(stderr, "VstNativeSynthIF::processEvent plugin requests DSSI-style ctlnum:%x(h) %d(d) be mapped to control port:%lu...\n", ctlnum, ctlnum, i); // #endif // // int c = ctlnum; // // Can be both CC and NRPN! Prefer CC over NRPN. // if(DSSI_IS_CC(ctlnum)) // { // ctlnum = DSSI_CC_NUMBER(c); // // #ifdef VST_NATIVE_DEBUG // fprintf(stderr, "VstNativeSynthIF::processEvent is CC ctlnum:%d\n", ctlnum); // #endif // // #ifdef VST_NATIVE_DEBUG // if(DSSI_IS_NRPN(ctlnum)) // fprintf(stderr, "VstNativeSynthIF::processEvent is also NRPN control. Using CC.\n"); // #endif // } // else // if(DSSI_IS_NRPN(ctlnum)) // { // ctlnum = DSSI_NRPN_NUMBER(c) + CTRL_NRPN14_OFFSET; // // #ifdef VST_NATIVE_DEBUG // fprintf(stderr, "VstNativeSynthIF::processEvent is NRPN ctlnum:%x(h) %d(d)\n", ctlnum, ctlnum); // #endif // } // // } // // float val = midi2LadspaValue(ld, i, ctlnum, b); // // #ifdef VST_NATIVE_DEBUG // fprintf(stderr, "VstNativeSynthIF::processEvent control port:%lu port:%lu dataA:%d Converting val from:%d to ladspa:%f\n", i, k, a, b, val); // #endif // // // Set the ladspa port value. // controls[k].val = val; // // // Need to update the automation value, otherwise it overwrites later with the last automation value. // if(id() != -1) // // We're in the audio thread context: no need to send a message, just modify directly. // synti->setPluginCtrlVal(genACnum(id(), k), val); // // // Since we absorbed the message as a ladspa control change, return false - the event is not filled. // return false; } break; case ME_PITCHBEND: { int v = a + 8192; setVstEvent(event, (type | chn) & 0xff, v & 0x7f, (v >> 7) & 0x7f); } break; case ME_AFTERTOUCH: setVstEvent(event, (type | chn) & 0xff, a & 0x7f); break; case ME_POLYAFTER: setVstEvent(event, (type | chn) & 0xff, a & 0x7f, b & 0x7f); break; case ME_SYSEX: { #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::processEvent midi event is ME_SYSEX\n"); #endif const unsigned char* data = e.constData(); if(e.len() >= 2) { if(data[0] == MUSE_SYNTH_SYSEX_MFG_ID) { if(data[1] == VST_NATIVE_SYNTH_UNIQUE_ID) { //if(e.len() >= 9) if(e.len() >= 10) { //--------------------------------------------------------------------- // NOTICE: Obsolete. Replaced by customData block. Keep for old songs! // We should never arrive here with a newer song now. //--------------------------------------------------------------------- if (QString((const char*)(data + 2)).startsWith("VSTSAVE")) { if(_synth->hasChunks()) { //#ifndef VST_VESTIGE_SUPPORT int chunk_flags = data[9]; if(chunk_flags & VST_NATIVE_CHUNK_FLAG_COMPRESSED) fprintf(stderr, "chunk flags:%x compressed chunks not supported yet.\n", chunk_flags); else { fprintf(stderr, "%s: loading chunk from sysex!\n", name().toLatin1().constData()); // 10 = 2 bytes header + "VSTSAVE" + 1 byte flags (compression etc) dispatch(24 /* effSetChunk */, 0, e.len()-10, (void*)(data+10), 0.0); // index 0: is bank 1: is program } //#else // fprintf(stderr, "support for vst chunks not compiled in!\n"); //#endif } // Event not filled. return false; } } } } } //else { // FIXME TODO: Sysex support. return false; } } break; default: if(MusEGlobal::debugMsg) fprintf(stderr, "VstNativeSynthIF::processEvent midi event unknown type:%d\n", e.type()); // Event not filled. return false; break; } return true; } //--------------------------------------------------------- // getData // If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- bool VstNativeSynthIF::getData(MidiPort* /*mp*/, unsigned pos, int ports, unsigned nframes, float** buffer) { const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); #ifdef VST_NATIVE_DEBUG_PROCESS fprintf(stderr, "VstNativeSynthIF::getData: pos:%u ports:%d nframes:%u syncFrame:%lu\n", pos, ports, nframes, syncFrame); #endif // All ports must be connected to something! const unsigned long in_ports = _synth->inPorts(); const unsigned long out_ports = _synth->outPorts(); const unsigned long nop = ((unsigned long) ports) > out_ports ? out_ports : ((unsigned long) ports); unsigned long sample = 0; // FIXME Better support for PluginPowerOf2BlockSize, by quantizing the control period times. // For now we treat it like fixed size. const bool usefixedrate = (requiredFeatures() & (PluginFixedBlockSize | PluginPowerOf2BlockSize | PluginCoarseBlockSize)); // For now, the fixed size is clamped to the audio buffer size. // TODO: We could later add slower processing over several cycles - // so that users can select a small audio period but a larger control period. const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; const unsigned long min_per_mask = min_per-1; // min_per must be power of 2 AudioTrack* atrack = track(); const AutomationType at = atrack->automationType(); const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; const unsigned long in_ctrls = _synth->inControls(); CtrlListList* cll = atrack->controller(); ciCtrlList icl_first; const int plug_id = id(); if(plug_id != -1 && ports != 0) // Don't bother if not 'running'. icl_first = cll->lower_bound(genACnum(plug_id, 0)); // Inform the host callback we are in the audio thread. _inProcess = true; #ifdef VST_NATIVE_DEBUG_PROCESS fprintf(stderr, "VstNativeSynthIF::getData: Handling inputs...\n"); #endif bool used_in_chan_array[in_ports]; // Don't bother initializing if not 'running'. // Don't bother if not 'running'. if(ports != 0) { // Initialize the array. for(unsigned long i = 0; i < in_ports; ++i) used_in_chan_array[i] = false; if(!atrack->noInRoute()) { RouteList *irl = atrack->inRoutes(); for(ciRoute i = irl->begin(); i != irl->end(); ++i) { if(i->track->isMidiTrack()) continue; // Only this synth knows how many destination channels there are, // while only the track knows how many source channels there are. // So take care of the destination channels here, and let the track handle the source channels. const int dst_ch = i->channel <= -1 ? 0 : i->channel; if((unsigned long)dst_ch >= in_ports) continue; const int dst_chs = i->channels <= -1 ? in_ports : i->channels; //const int total_ins = atrack->totalRoutableInputs(Route::TRACK_ROUTE); const int src_ch = i->remoteChannel <= -1 ? 0 : i->remoteChannel; const int src_chs = i->channels; int fin_dst_chs = dst_chs; if((unsigned long)(dst_ch + fin_dst_chs) > in_ports) fin_dst_chs = in_ports - dst_ch; static_cast(i->track)->copyData(pos, dst_ch, dst_chs, fin_dst_chs, src_ch, src_chs, nframes, &_audioInBuffers[0], false, used_in_chan_array); const int nxt_ch = dst_ch + fin_dst_chs; for(int ch = dst_ch; ch < nxt_ch; ++ch) used_in_chan_array[ch] = true; } } } #ifdef VST_NATIVE_DEBUG_PROCESS fprintf(stderr, "VstNativeSynthIF::getData: Processing automation control values...\n"); #endif int cur_slice = 0; while(sample < nframes) { unsigned long nsamp = nframes - sample; const unsigned long slice_frame = pos + sample; // // Process automation control values, while also determining the maximum acceptable // size of this run. Further processing, from FIFOs for example, can lower the size // from there, but this section determines where the next highest maximum frame // absolutely needs to be for smooth playback of the controller value stream... // if(ports != 0) // Don't bother if not 'running'. { ciCtrlList icl = icl_first; for(unsigned long k = 0; k < in_ctrls; ++k) { CtrlList* cl = (cll && plug_id != -1 && icl != cll->end()) ? icl->second : NULL; CtrlInterpolate& ci = _controls[k].interp; // Always refresh the interpolate struct at first, since things may have changed. // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && (slice_frame < (unsigned long)ci.sFrame || (ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame)) ) ) { if(cl && plug_id != -1 && (unsigned long)cl->id() == genACnum(plug_id, k)) { cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); if(icl != cll->end()) ++icl; } else { // No matching controller, or end. Just copy the current value into the interpolator. // Keep the current icl iterator, because since they are sorted by frames, // if the IDs didn't match it means we can just let k catch up with icl. ci.sFrame = 0; ci.eFrame = 0; ci.eFrameValid = false; ci.sVal = _controls[k].val; ci.eVal = ci.sVal; ci.doInterp = false; ci.eStop = false; } } else { if(ci.eStop && ci.eFrameValid && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. { // Clear the stop condition and set up the interp struct appropriately as an endless value. ci.sFrame = 0; //ci->eFrame; ci.eFrame = 0; ci.eFrameValid = false; ci.sVal = ci.eVal; ci.doInterp = false; ci.eStop = false; } if(cl && cll && icl != cll->end()) ++icl; } if(!usefixedrate && MusEGlobal::audio->isPlaying()) { unsigned long samps = nsamp; if(ci.eFrameValid) samps = (unsigned long)ci.eFrame - slice_frame; if(!ci.doInterp && samps > min_per) { samps &= ~min_per_mask; if((samps & min_per_mask) != 0) samps += min_per; } else samps = min_per; if(samps < nsamp) nsamp = samps; } float new_val; if(ci.doInterp && cl) new_val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); else new_val = ci.sVal; if(_controls[k].val != new_val) { _controls[k].val = new_val; if(dispatch(26 /*effCanBeAutomated*/, k, 0, NULL, 0.0f) == 1) { if(_plugin->getParameter(_plugin, k) != new_val) _plugin->setParameter(_plugin, k, new_val); } #ifdef VST_NATIVE_DEBUG else fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), k); #endif } #ifdef VST_NATIVE_DEBUG_PROCESS fprintf(stderr, "VstNativeSynthIF::getData k:%lu sample:%lu frame:%lu ci.eFrame:%d nsamp:%lu \n", k, sample, frame, ci.eFrame, nsamp); #endif } } #ifdef VST_NATIVE_DEBUG_PROCESS fprintf(stderr, "VstNativeSynthIF::getData sample:%lu nsamp:%lu\n", sample, nsamp); #endif bool found = false; unsigned long frame = 0; unsigned long index = 0; unsigned long evframe; // Get all control ring buffer items valid for this time period... while(!_controlFifo.isEmpty()) { const ControlEvent& v = _controlFifo.peek(); // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", found, evframe, frame, v.frame, v.idx, v.value, v.unique); #endif // Protection. Observed this condition. Why? Supposed to be linear timestamps. if(found && evframe < frame) { fprintf(stderr, "VstNativeSynthIF::getData *** Error: Event out of order: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d syncFrame:%u nframes:%u v.frame:%lu\n", evframe, frame, v.idx, v.value, v.unique, syncFrame, nframes, v.frame); // No choice but to ignore it. _controlFifo.remove(); // Done with the ring buffer's item. Remove it. continue; } if(evframe >= nframes // Next events are for a later period. || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. || (usefixedrate && found && v.unique && v.idx == index)) // Fixed rate and must reply to all. break; if(v.idx >= in_ctrls) // Sanity check. { _controlFifo.remove(); // Done with the ring buffer's item. Remove it. break; } found = true; frame = evframe; index = v.idx; if(ports == 0) // Don't bother if not 'running'. { _controls[v.idx].val = v.value; // Might as well at least update these. // #ifndef VST_VESTIGE_SUPPORT // if(dispatch(effCanBeAutomated, v.idx, 0, NULL, 0.0f) == 1) // { // #endif // if(v.value != _plugin->getParameter(_plugin, v.idx)) // _plugin->setParameter(_plugin, v.idx, v.value); // #ifndef VST_VESTIGE_SUPPORT // } // #ifdef VST_NATIVE_DEBUG // else // fprintf(stderr, "VstNativeSynthIF::getData %s parameter:%lu cannot be automated\n", name().toLatin1().constData(), v.idx); // #endif // #endif } else { CtrlInterpolate* ci = &_controls[v.idx].interp; // Tell it to stop the current ramp at this frame, when it does stop, set this value: ci->eFrame = frame; ci->eFrameValid = true; ci->eVal = v.value; ci->eStop = true; } // Need to update the automation value, otherwise it overwrites later with the last automation value. if(plug_id != -1) synti->setPluginCtrlVal(genACnum(plug_id, v.idx), v.value); _controlFifo.remove(); // Done with the ring buffer's item. Remove it. } if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. nsamp = frame - sample; if(sample + nsamp > nframes) // Safety check. nsamp = nframes - sample; // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. // Note this means it is still possible to get stuck in the top loop (at least for a while). if(nsamp != 0) { unsigned long nevents = 0; // Get the state of the stop flag. const bool do_stop = synti->stopFlag(); MidiPlayEvent buf_ev; // Transfer the user lock-free buffer events to the user sorted multi-set. // False = don't use the size snapshot, but update it. const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); for(unsigned int i = 0; i < usr_buf_sz; ++i) { if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) synti->_outUserEvents.insert(buf_ev); } // Transfer the playback lock-free buffer events to the playback sorted multi-set. const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); for(unsigned int i = 0; i < pb_buf_sz; ++i) { // Are we stopping? Just remove the item. if(do_stop) synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); // Otherwise get the item. else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) synti->_outPlaybackEvents.insert(buf_ev); } // Are we stopping? if(do_stop) { // Transport has stopped, purge ALL further scheduled playback events now. synti->_outPlaybackEvents.clear(); // Reset the flag. synti->setStopFlag(false); } // Count how many events we need. for(ciMPEvent impe = synti->_outPlaybackEvents.begin(); impe != synti->_outPlaybackEvents.end(); ++impe) { const MidiPlayEvent& e = *impe; if(e.time() >= (syncFrame + sample + nsamp)) break; ++nevents; } for(ciMPEvent impe = synti->_outUserEvents.begin(); impe != synti->_outUserEvents.end(); ++impe) { const MidiPlayEvent& e = *impe; if(e.time() >= (syncFrame + sample + nsamp)) break; ++nevents; } VstMidiEvent events[nevents]; char evbuf[sizeof(VstMidiEvent*) * nevents + sizeof(VstEvents)]; VstEvents *vst_events = (VstEvents*)evbuf; vst_events->numEvents = 0; vst_events->reserved = 0; iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); iMPEvent impe_us = synti->_outUserEvents.begin(); bool using_pb; unsigned long event_counter = 0; while(1) { if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) using_pb = *impe_pb < *impe_us; else if(impe_pb != synti->_outPlaybackEvents.end()) using_pb = true; else if(impe_us != synti->_outUserEvents.end()) using_pb = false; else break; const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us; #ifdef VST_NATIVE_DEBUG fprintf(stderr, "VstNativeSynthIF::getData eventFifos event time:%d\n", e.time()); #endif // Event is for future? if(e.time() >= (sample + nsamp + syncFrame)) break; if(ports != 0) // Don't bother if not 'running'. { // Returns false if the event was not filled. It was handled, but some other way. if(processEvent(e, &events[event_counter])) { // Time-stamp the event. unsigned int ft = (e.time() < syncFrame) ? 0 : e.time() - syncFrame; ft = (ft < sample) ? 0 : ft - sample; if(ft >= nsamp) { fprintf(stderr, "VstNativeSynthIF::getData: eventFifos event time:%d out of range. pos:%d syncFrame:%u ft:%u sample:%lu nsamp:%lu\n", e.time(), pos, syncFrame, ft, sample, nsamp); ft = nsamp - 1; } vst_events->events[event_counter] = (VstEvent*)&events[event_counter]; events[event_counter].deltaFrames = ft; ++event_counter; } } // Done with ring buffer's event. Remove it. // C++11. if(using_pb) impe_pb = synti->_outPlaybackEvents.erase(impe_pb); else impe_us = synti->_outUserEvents.erase(impe_us); } if(event_counter < nevents) nevents = event_counter; #ifdef VST_NATIVE_DEBUG_PROCESS fprintf(stderr, "VstNativeSynthIF::getData: Connecting and running. sample:%lu nsamp:%lu nevents:%lu\n", sample, nsamp, nevents); #endif if(ports != 0) // Don't bother if not 'running'. { // Set the events pointer. if(nevents > 0) { vst_events->numEvents = nevents; dispatch(effProcessEvents, 0, 0, vst_events, 0.0f); } float* in_bufs[in_ports]; float* out_bufs[out_ports]; // Connect the given buffers directly to the ports, up to a max of synth ports. for(unsigned long k = 0; k < nop; ++k) out_bufs[k] = buffer[k] + sample; // Connect the remaining ports to some local buffers (not used yet). for(unsigned long k = nop; k < out_ports; ++k) out_bufs[k] = _audioOutBuffers[k] + sample; // Connect all inputs either to some local buffers, or a silence buffer. for(unsigned long k = 0; k < in_ports; ++k) { if(used_in_chan_array[k]) in_bufs[k] = _audioInBuffers[k] + sample; else in_bufs[k] = _audioInSilenceBuf + sample; } // Run the synth for a period of time. This processes events and gets/fills our local buffers... if((_plugin->flags & effFlagsCanReplacing) && _plugin->processReplacing) { _plugin->processReplacing(_plugin, in_bufs, out_bufs, nsamp); } } sample += nsamp; } ++cur_slice; // Slice is done. Moving on to any next slice now... } // Inform the host callback we will be no longer in the audio thread. _inProcess = false; return true; } //-------------------------------- // Methods for PluginIBase: //-------------------------------- unsigned long VstNativeSynthIF::pluginID() { return (_plugin) ? _plugin->uniqueID : 0; } int VstNativeSynthIF::id() { return MusECore::MAX_PLUGINS; } // Set for special block reserved for synth. QString VstNativeSynthIF::pluginLabel() const { return _synth ? QString(_synth->name()) : QString(); } // FIXME Maybe wrong QString VstNativeSynthIF::lib() const { return _synth ? _synth->completeBaseName() : QString(); } QString VstNativeSynthIF::uri() const { return _synth ? _synth->uri() : QString(); } QString VstNativeSynthIF::dirPath() const { return _synth ? _synth->absolutePath() : QString(); } QString VstNativeSynthIF::fileName() const { return _synth ? _synth->fileName() : QString(); } void VstNativeSynthIF::enableController(unsigned long i, bool v) { _controls[i].enCtrl = v; } bool VstNativeSynthIF::controllerEnabled(unsigned long i) const { return _controls[i].enCtrl;} void VstNativeSynthIF::enableAllControllers(bool v) { if(!_synth) return; const unsigned long sic = _synth->inControls(); for(unsigned long i = 0; i < sic; ++i) _controls[i].enCtrl = v; } void VstNativeSynthIF::updateControllers() { } void VstNativeSynthIF::activate() { // Set some default properties dispatch(effSetSampleRate, 0, 0, NULL, MusEGlobal::sampleRate); dispatch(effSetBlockSize, 0, MusEGlobal::segmentSize, NULL, 0.0f); //for (unsigned short i = 0; i < instances(); ++i) { // dispatch(i, effMainsChanged, 0, 1, NULL, 0.0f); dispatch(effMainsChanged, 0, 1, NULL, 0.0f); #ifndef VST_VESTIGE_SUPPORT //dispatch(i, effStartProcess, 0, 0, NULL, 0.0f); dispatch(effStartProcess, 0, 0, NULL, 0.0f); #endif //} // REMOVE Tim. Or keep? From PluginI::activate(). // if (initControlValues) { // for (unsigned long i = 0; i < controlPorts; ++i) { // controls[i].val = controls[i].tmpVal; // } // } // else { // // get initial control values from plugin // for (unsigned long i = 0; i < controlPorts; ++i) { // controls[i].tmpVal = controls[i].val; // } // } _active = true; } void VstNativeSynthIF::deactivate() { _active = false; //for (unsigned short i = 0; i < instances(); ++i) { #ifndef VST_VESTIGE_SUPPORT //dispatch(i, effStopProcess, 0, 0, NULL, 0.0f); dispatch(effStopProcess, 0, 0, NULL, 0.0f); #endif //dispatch(i, effMainsChanged, 0, 0, NULL, 0.0f); dispatch(effMainsChanged, 0, 0, NULL, 0.0f); //} } unsigned long VstNativeSynthIF::parameters() const { return _synth ? _synth->inControls() : 0; } unsigned long VstNativeSynthIF::parametersOut() const { return 0; } void VstNativeSynthIF::setParam(unsigned long i, double val) { setParameter(i, val); } double VstNativeSynthIF::param(unsigned long i) const { return getParameter(i); } double VstNativeSynthIF::paramOut(unsigned long) const { return 0.0; } const char* VstNativeSynthIF::paramName(unsigned long i) { if(!_plugin) return 0; static char buf[256]; buf[0] = 0; dispatch(effGetParamName, i, 0, buf, 0); return buf; } const char* VstNativeSynthIF::paramOutName(unsigned long) { return 0; } LADSPA_PortRangeHint VstNativeSynthIF::range(unsigned long /*i*/) { LADSPA_PortRangeHint h; // FIXME TODO: h.HintDescriptor = 0; h.LowerBound = 0.0; h.UpperBound = 1.0; return h; } LADSPA_PortRangeHint VstNativeSynthIF::rangeOut(unsigned long) { // There are no output controls. LADSPA_PortRangeHint h; h.HintDescriptor = 0; h.LowerBound = 0.0; h.UpperBound = 1.0; return h; } // FIXME TODO: CtrlValueType VstNativeSynthIF::ctrlValueType(unsigned long /*i*/) const { return VAL_LINEAR; } CtrlList::Mode VstNativeSynthIF::ctrlMode(unsigned long /*i*/) const { return CtrlList::INTERPOLATE; } VstNativePluginWrapper::VstNativePluginWrapper(VstNativeSynth *s, PluginFeatures_t reqFeatures) { _synth = s; _requiredFeatures = reqFeatures; _fakeLd.Label = strdup(_synth->name().toUtf8().constData()); _fakeLd.Name = strdup(_synth->name().toUtf8().constData()); _fakeLd.UniqueID = _synth->_id; _fakeLd.Maker = strdup(_synth->maker().toUtf8().constData()); _fakeLd.Copyright = strdup(_synth->version().toUtf8().constData()); _isVstNativePlugin = true; _isVstNativeSynth = s->isSynth(); int numPorts = _synth->inPorts() + _synth->outPorts() + _synth->inControls(); _fakeLd.PortCount = numPorts; _fakePds = new LADSPA_PortDescriptor [numPorts]; memset(_fakePds, 0, sizeof(int) * numPorts); for(size_t i = 0; i < _synth->inPorts(); i++) { _fakePds [i] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; } for(size_t i = 0; i < _synth->outPorts(); i++) { _fakePds [i + _synth->inPorts()] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; } for(size_t i = 0; i < _synth->inControls(); i++) { _fakePds [i + _synth->inPorts() + _synth->outPorts()] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; } _fakeLd.PortNames = NULL; _fakeLd.PortRangeHints = NULL; _fakeLd.PortDescriptors = _fakePds; _fakeLd.Properties = 0; plugin = &_fakeLd; _isDssi = false; _isDssiSynth = false; _isLV2Plugin = false; _isLV2Synth = false; #ifdef DSSI_SUPPORT dssi_descr = NULL; #endif fi = _synth->info; _uri = _synth->uri(); ladspa = NULL; _handle = 0; _references = 0; _instNo = 0; _label = _synth->name(); _name = _synth->description(); _uniqueID = plugin->UniqueID; _maker = _synth->maker(); _copyright = _synth->version(); _portCount = plugin->PortCount; _inports = 0; _outports = 0; _controlInPorts = 0; _controlOutPorts = 0; for(unsigned long k = 0; k < _portCount; ++k) { LADSPA_PortDescriptor pd = plugin->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) { if(pd & LADSPA_PORT_INPUT) { ++_inports; } else if(pd & LADSPA_PORT_OUTPUT) { ++_outports; } } else if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) { ++_controlInPorts; } else if(pd & LADSPA_PORT_OUTPUT) { ++_controlOutPorts; } } } } VstNativePluginWrapper::~VstNativePluginWrapper() { free((void*)_fakeLd.Label); free((void*)_fakeLd.Name); free((void*)_fakeLd.Maker); free((void*)_fakeLd.Copyright); delete [] _fakePds; } LADSPA_Handle VstNativePluginWrapper::instantiate(PluginI *pluginI) { VstNativePluginWrapper_State *state = new VstNativePluginWrapper_State; if(!state) { abort(); } state->plugin = _synth->instantiate(&state->userData); if(!state->plugin) { delete state; return 0; } if(!_synth->openPlugin(state->plugin)) { delete state; return 0; } state->pluginI = pluginI; state->pluginWrapper = this; state->inPorts.resize(_inports); state->outPorts.resize(_outports); state->inControlPorts.resize(_controlInPorts); state->inControlLastValues.resize(_controlInPorts); bool refillDefCtrls = false; if(inControlDefaults.size() == 0) { refillDefCtrls = true; inControlDefaults.resize(_controlInPorts); portNames.resize(_inports + _outports + _controlInPorts); } memset(&state->inPorts [0], 0, _inports * sizeof(float *)); memset(&state->outPorts [0], 0, _outports * sizeof(float *)); memset(&state->inControlPorts [0], 0, _controlInPorts * sizeof(float *)); if(refillDefCtrls) { for(size_t i = 0; i < _controlInPorts; i++) { if(state->plugin->getParameter) { state->inControlLastValues [i] = inControlDefaults [i] = state->plugin->getParameter(state->plugin, i); } } for(size_t i = 0; i < portNames.size(); i++) { if(i < _inports) { std::stringstream ss; ss << "input" << i; portNames [i] = ss.str(); } else if(i < _inports + _outports) { std::stringstream ss; ss << "output" << (i - _inports); portNames [i] = ss.str(); } else if(i < _inports + _outports + _controlInPorts) { char buf[256]; memset(buf, 0, sizeof(buf)); dispatch(state, effGetParamName, i - _inports - _outports, 0, buf, 0); if(strlen(buf) > 0) { portNames [i] = buf; } else { std::stringstream ss; ss << "control" << (i - _inports - _outports); portNames [i] = ss.str(); } } } } QObject::connect(MusEGlobal::heartBeatTimer, SIGNAL(timeout()), state, SLOT(heartBeat())); return(LADSPA_Handle)state; } int VstNativePluginWrapper::incReferences(int ref) { _synth->incInstances(ref); return _synth->instances(); } void VstNativePluginWrapper::activate(LADSPA_Handle handle) { VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; // Set some default properties dispatch(state, effSetSampleRate, 0, 0, NULL, MusEGlobal::sampleRate); dispatch(state, effSetBlockSize, 0, MusEGlobal::segmentSize, NULL, 0.0f); //for (unsigned short i = 0; i < instances(); ++i) { // dispatch(i, effMainsChanged, 0, 1, NULL, 0.0f); dispatch(state, effMainsChanged, 0, 1, NULL, 0.0f); dispatch(state, 71 /*effStartProcess*/, 0, 0, NULL, 0.0f); if(state->plugin->getParameter) { for(size_t i = 0; i < _controlInPorts; i++) { state->pluginI->controls [i].val = state->pluginI->controls [i].tmpVal = inControlDefaults [i]; } } state->active = true; } void VstNativePluginWrapper::deactivate(LADSPA_Handle handle) { VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; if(!state) { return; } state->active = false; dispatch(state, 72 /*effStopProcess*/, 0, 0, NULL, 0.0f); dispatch(state, effMainsChanged, 0, 0, NULL, 0.0f); } void VstNativePluginWrapper::cleanup(LADSPA_Handle handle) { VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; if(!state) { return; } if(state->editor) { state->editor->close(); state->editor = NULL; state->guiVisible = false; } if (state->plugin) { dispatch(state, effClose, 0, 0, NULL, 0); state->plugin = 0; } delete state; } void VstNativePluginWrapper::connectPort(LADSPA_Handle handle, unsigned long port, float *value) { VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; if(port < _inports) { state->inPorts [port] = value; } else if(port < _inports + _outports) { state->outPorts [port - _inports] = value; } else if(port < _inports + _outports + _controlInPorts) { state->inControlPorts [port - _inports - _outports] = value; } } void VstNativePluginWrapper::apply(LADSPA_Handle handle, unsigned long n, float /*latency_corr*/) { VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; state->inProcess = true; if(state->pluginI->controls) { for(size_t i = 0; i < _controlInPorts; i++) { if(state->pluginI->controls [i].val == state->inControlLastValues [i]) { continue; } state->inControlLastValues [i] = state->pluginI->controls [i].val; if(dispatch(state, 26 /*effCanBeAutomated*/, i, 0, NULL, 0.0f) == 1) { if(state->plugin->getParameter && state->plugin->setParameter) { if(state->plugin->getParameter(state->plugin, i) != state->inControlLastValues [i]) state->plugin->setParameter(state->plugin, i, state->inControlLastValues [i]); } } } } if((state->plugin->flags & effFlagsCanReplacing) && state->plugin->processReplacing) { state->plugin->processReplacing(state->plugin, &state->inPorts [0], &state->outPorts [0], n); } else if(state->plugin->process) { state->plugin->process(state->plugin, &state->inPorts [0], &state->outPorts [0], n); } state->inProcess = false; } LADSPA_PortDescriptor VstNativePluginWrapper::portd(unsigned long k) const { return _fakeLd.PortDescriptors[k]; } LADSPA_PortRangeHint VstNativePluginWrapper::range(unsigned long) { LADSPA_PortRangeHint hint; hint.HintDescriptor = 0; hint.LowerBound = 0.0f; hint.UpperBound = 1.0f; hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; return hint; } void VstNativePluginWrapper::range(unsigned long, float *min, float *max) const { *min = 0.0f; *max = 1.0f; } double VstNativePluginWrapper::defaultValue(unsigned long port) const { return inControlDefaults [port]; } const char *VstNativePluginWrapper::portName(unsigned long port) { return portNames [port].c_str(); } CtrlValueType VstNativePluginWrapper::ctrlValueType(unsigned long) const { return VAL_LINEAR; } CtrlList::Mode VstNativePluginWrapper::ctrlMode(unsigned long) const { return CtrlList::INTERPOLATE; } bool VstNativePluginWrapper::hasNativeGui() const { return _synth->_hasGui; } void VstNativePluginWrapper::showNativeGui(PluginI *p, bool bShow) { assert(p->instances > 0); VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)p->handle [0]; if(!hasNativeGui()) return; if(bShow) { if(state->editor) { if(!state->editor->isVisible()) state->editor->show(); state->editor->raise(); state->editor->activateWindow(); } else { Qt::WindowFlags wflags(Qt::Window | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint); state->editor = new MusEGui::VstNativeEditor(NULL, wflags); state->editor->open(0, state); } } else { if(state->editor) { state->editor->close(); //_editor = NULL; // No - done in editorDeleted. } } state->guiVisible = bShow; } bool VstNativePluginWrapper::nativeGuiVisible(const PluginI *p) const { assert(p->instances > 0); VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)p->handle [0]; return state->guiVisible; } void VstNativePluginWrapper::writeConfiguration(LADSPA_Handle handle, int level, Xml &xml) { VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; _synth->vstconfWrite(state->plugin, name(), level, xml); // index 0: is bank 1: is program } void VstNativePluginWrapper::setCustomData(LADSPA_Handle handle, const std::vector &customParams) { VstNativePluginWrapper_State *state = (VstNativePluginWrapper_State *)handle; _synth->vstconfSet(state->plugin, customParams); } void VstNativePluginWrapper_State::heartBeat() { if(plugin && active) { if(guiVisible) { plugin->dispatcher(plugin, effEditIdle, 0, 0, NULL, 0.0f); if(editor) editor->update(); } } } } // namespace MusECore #else // VST_NATIVE_SUPPORT namespace MusECore { void initVST_Native() {} } // namespace MusECore #endif muse-4.0.0/src/muse/vst_native.h000066400000000000000000000315031404105311000165460ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // vst_native.h // (C) Copyright 2012-2013 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __VST_NATIVE_H__ #define __VST_NATIVE_H__ #include "config.h" // Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. // 127 is reserved for special MusE system messages. #define VST_NATIVE_SYNTH_UNIQUE_ID 9 // Midistate sysex initialization command. #define VST_NATIVE_INIT_DATA_CMD 1 #define VST_NATIVE_PARAMSAVE_VERSION_MAJOR 0 #define VST_NATIVE_PARAMSAVE_VERSION_MINOR 1 #define VST_NATIVE_CHUNK_FLAG_COMPRESSED 1 #ifdef VST_NATIVE_SUPPORT #include "plugin_list.h" #include "globaldefs.h" class VstNativeSynthIF; typedef class VstNativeSynthIF VSTPlugin; #ifdef VST_SDK_QUIRK #define __cdecl #endif #include "aeffectx.h" #ifdef VST_VESTIGE_SUPPORT #ifndef effGetProgramNameIndexed #define effGetProgramNameIndexed 29 #endif #endif #ifndef VST_2_4_EXTENSIONS #ifndef VST_VESTIGE_SUPPORT typedef long VstInt32; typedef long VstIntPtr; #else typedef int32_t VstInt32; typedef intptr_t VstIntPtr; #define VSTCALLBACK #endif #endif #include "vst_native_editor.h" #include "synth.h" #include "plugin.h" #include "midictrl.h" #include #include #include #include #endif // VST_NATIVE_SUPPORT namespace MusEGui { class PopupMenu; } namespace MusECore { #ifdef VST_NATIVE_SUPPORT struct VstRect{ short top; short left; short bottom; short right; }; struct VST_Program { //unsigned long bank; unsigned long program; QString name; }; //--------------------------------------------------------- // VstNativeSynth //--------------------------------------------------------- class VstNativePluginWrapper; class VstNativePluginWrapper_State; class VstNativeSynthIF; struct VstNativeSynthOrPlugin { VstNativeSynthIF *sif; VstNativePluginWrapper_State *pstate; }; class VstNativeSynth : public Synth { friend class VstNativePluginWrapper; void* _handle; int _vst_version; VstPluginFlags_t _flags; VstIntPtr _id; bool _isSynth; unsigned long /*_portCount,*/ _inports, _outports, _controlInPorts; //, _controlOutPorts; std::vector iIdx; // Audio input index to port number. std::vector oIdx; // Audio output index to port number. std::vector rpIdx; // Port number to control input index. Item is -1 if it's not a control input. MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to vst port numbers. MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps vst port numbers to midi controller numbers. bool _hasGui; bool _hasChunks; public: VstNativeSynth(const QFileInfo& fi, const QString& uri, AEffect* plugin, const QString& label, const QString& desc, const QString& maker, const QString& ver, VstIntPtr id, void *dlHandle, bool isSynth, PluginFeatures_t reqFeatures = PluginNoFeatures); VstNativeSynth(const MusEPlugin::PluginScanInfoStruct& info); virtual ~VstNativeSynth() {} virtual Type synthType() const { return _isSynth ? VST_NATIVE_SYNTH : VST_NATIVE_EFFECT; } virtual void incInstances(int val); virtual AEffect* instantiate(void *userData); // Opens a plugin instance, after instantiation. static bool openPlugin(AEffect* plugin); virtual SynthIF* createSIF(SynthI*); unsigned long inPorts() const { return _inports; } unsigned long outPorts() const { return _outports; } unsigned long inControls() const { return _controlInPorts; } //unsigned long outControls() const { return _controlOutPorts; } int vstVersion() const { return _vst_version; } bool hasChunks() const { return _hasChunks; } const std::vector* getRpIdx() { return &rpIdx; } bool isSynth() { return _isSynth; } static VstIntPtr pluginHostCallback(VstNativeSynthOrPlugin *userData, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); static int guiControlChanged(VstNativeSynthOrPlugin *userData, unsigned long param_idx, float value); static void guiAutomationBegin(VstNativeSynthOrPlugin *userData, unsigned long param_idx); static void guiAutomationEnd(VstNativeSynthOrPlugin *userData, unsigned long param_idx); static bool resizeEditor(MusEGui::VstNativeEditor *editor, int w, int h); void vstconfWrite(AEffect *plugin, const QString& name, int level, Xml &xml); void vstconfSet(AEffect *plugin, const std::vector & customParams); }; //--------------------------------------------------------- // VstNativeGuiWidgets //--------------------------------------------------------- struct VstNativeGuiWidgets { // TODO: Remove Tim. Or keep. //enum { // SLIDER, DOUBLE_LABEL, QCHECKBOX, QCOMBOBOX // }; //QWidget* widget; //int type; //unsigned long param; bool pressed; }; //--------------------------------------------------------- // VstNativeSynthIF // VSTi synthesizer instance //--------------------------------------------------------- class VstNativeSynthIF : public SynthIF { friend class VstNativeSynth; friend class MusEGui::VstNativeEditor; VstNativeSynth* _synth; AEffect* _plugin; bool _active; // Whether it's safe to call effIdle or effEditIdle. MusEGui::VstNativeEditor* _editor; bool _guiVisible; bool _inProcess; // To inform the callback of the 'process level' - are we in the audio thread? // Struct array to keep track of pressed flags and so on. // TODO: Not used yet. REMOVE Tim. Or keep. VstNativeGuiWidgets* _gw; Port* _controls; float** _audioOutBuffers; float** _audioInBuffers; float* _audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. VstNativeSynthOrPlugin userData; std::vector programs; void queryPrograms(); void doSelectProgram(int bankH, int bankL, int prog); bool processEvent(const MidiPlayEvent&, VstMidiEvent*); void setVstEvent(VstMidiEvent* event, int a = 0, int b = 0, int c = 0, int d = 0); void editorDeleted(); void editorOpened(); void editorClosed(); void eventReceived(VstMidiEvent*); public: VstNativeSynthIF(SynthI* s); virtual ~VstNativeSynthIF(); virtual bool init(Synth*); AEffect* plugin() const { return _plugin; } VstIntPtr dispatch(VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) const { if(_plugin) {return _plugin->dispatcher(_plugin, opcode, index, value, ptr, opt); } return 0; } void idleEditor(); virtual void guiHeartBeat(); virtual bool hasGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool v); virtual bool hasNativeGui() const; virtual void getNativeGeometry(int*x, int*y, int*w, int*h) const ; virtual void setNativeGeometry(int, int, int, int); virtual bool getData(MidiPort*, unsigned pos, int ports, unsigned nframes, float** buffer) ; virtual MidiPlayEvent receiveEvent(); virtual int eventsPending() const { return 0; } virtual int channels() const; virtual int totalOutChannels() const; virtual int totalInChannels() const; virtual void deactivate3(); virtual QString getPatchName(int chan, int prog, bool drum) const; virtual void populatePatchPopup(MusEGui::PopupMenu* menu, int chan, bool drum); virtual void write(int level, Xml& xml) const; virtual double getParameter(unsigned long idx) const; virtual void setParameter(unsigned long idx, double value); virtual int getControllerInfo(int, QString*, int*, int*, int*, int*) { return 0; } //------------------------- // Methods for PluginIBase: //------------------------- unsigned long pluginID(); int id(); QString pluginLabel() const; QString lib() const; QString uri() const; QString dirPath() const; QString fileName() const; void enableController(unsigned long i, bool v = true); bool controllerEnabled(unsigned long i) const; void enableAllControllers(bool v = true); void updateControllers(); void activate(); void deactivate(); unsigned long parameters() const; unsigned long parametersOut() const; void setParam(unsigned long i, double val); double param(unsigned long i) const; double paramOut(unsigned long i) const; const char* paramName(unsigned long i); const char* paramOutName(unsigned long i); LADSPA_PortRangeHint range(unsigned long i); LADSPA_PortRangeHint rangeOut(unsigned long i); CtrlValueType ctrlValueType(unsigned long i) const; CtrlList::Mode ctrlMode(unsigned long i) const; void setCustomData ( const std::vector & ); }; class VstNativePluginWrapper_State : public QObject { Q_OBJECT public: AEffect* plugin; VstNativePluginWrapper *pluginWrapper; PluginI *pluginI; std::vector inPorts; std::vector outPorts; std::vector inControlPorts; std::vector inControlLastValues; MusEGui::VstNativeEditor* editor; VstNativeSynthOrPlugin userData; bool guiVisible; bool inProcess; bool active; VstNativePluginWrapper_State() { plugin = 0; pluginWrapper = 0; pluginI = 0; editor = 0; guiVisible = false; userData.sif = 0; userData.pstate = this; inProcess = false; active = false; } virtual ~VstNativePluginWrapper_State() {} void editorDeleted() { editor = 0; } void editorOpened() { guiVisible = true; } void editorClosed() { guiVisible = false; } protected slots: virtual void heartBeat(); }; class VstNativePluginWrapper: public Plugin { friend class MusEGui::VstNativeEditor; friend class VstNativeSynth; private: VstNativeSynth *_synth; LADSPA_Descriptor _fakeLd; LADSPA_PortDescriptor *_fakePds; std::vector inControlDefaults; std::vector portNames; public: VstNativePluginWrapper ( VstNativeSynth *s, PluginFeatures_t reqFeatures = PluginNoFeatures ); VstNativeSynth *synth() { return _synth; } virtual ~VstNativePluginWrapper(); virtual LADSPA_Handle instantiate ( PluginI * ); virtual int incReferences ( int ref ); virtual void activate ( LADSPA_Handle handle ); virtual void deactivate ( LADSPA_Handle handle ); virtual void cleanup ( LADSPA_Handle handle ); virtual void connectPort ( LADSPA_Handle handle, unsigned long port, float *value ); virtual void apply ( LADSPA_Handle handle, unsigned long n, float latency_corr = 0.0f ); virtual LADSPA_PortDescriptor portd ( unsigned long k ) const; virtual LADSPA_PortRangeHint range ( unsigned long i ); virtual void range (unsigned long, float *min, float *max ) const; virtual double defaultValue ( unsigned long port ) const; virtual const char *portName (unsigned long port ); virtual CtrlValueType ctrlValueType ( unsigned long ) const; virtual CtrlList::Mode ctrlMode ( unsigned long ) const; virtual bool hasNativeGui() const; virtual void showNativeGui ( PluginI *p, bool bShow ); virtual bool nativeGuiVisible (const PluginI *p ) const; virtual void writeConfiguration(LADSPA_Handle handle, int level, Xml& xml); virtual void setCustomData (LADSPA_Handle handle, const std::vector & customParams); VstIntPtr dispatch(VstNativePluginWrapper_State *state, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) const { if(state->plugin) return state->plugin->dispatcher(state->plugin, opcode, index, value, ptr, opt); else return 0; } }; #endif // VST_NATIVE_SUPPORT extern void initVST_Native(); } // namespace MusECore #endif muse-4.0.0/src/muse/wave_helper.cpp000066400000000000000000000251001404105311000172140ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // (C) Copyright 2003-2020 Werner Schweer (ws@seh.de) and others // // wave_helper.cpp // // 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; 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; 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 "wave_helper.h" #include "globals.h" #include "gconfig.h" #include "song.h" #include "part.h" #include "track.h" #include "audio.h" namespace MusECore { bool sndFileCheckCopyOnWrite(const SndFileR sndFile) { if(sndFile.isNull()) return false; QString path_this = sndFile.canonicalPath(); if(path_this.isEmpty()) return false; bool fwrite = sndFile.isFileWritable(); // No exceptions: Even if this wave event is a clone, if it ain't writeable we gotta copy the wave. if(!fwrite) return true; // Count the number of unique part wave events (including possibly this one) using this file. // Not much choice but to search all active wave events - the sndfile ref count is not the solution for this... int use_count = 0; EventID_t id = MUSE_INVALID_EVENT_ID; Part* part = NULL; WaveTrackList* wtl = MusEGlobal::song->waves(); for(ciTrack it = wtl->begin(); it != wtl->end(); ++it) { PartList* pl = (*it)->parts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* p = ip->second; const EventList& el = p->events(); // const EventList& el = ip->second->events(); // // We are looking for active independent non-clone parts // if(ip->second->hasClones()) // continue; for(ciEvent ie = el.begin(); ie != el.end(); ++ie) { if(ie->second.type() != Wave) continue; const Event& ev = ie->second; if(ev.empty() || ev.id() == MUSE_INVALID_EVENT_ID) continue; const SndFileR sf = ev.sndFile(); if(sf.isNull()) continue; QString path = sf.canonicalPath(); if(path.isEmpty()) continue; if(path == path_this) { // Ignore clones of an already found event. if(ev.id() == id) { // Double check. if(part && !p->isCloneOf(part)) { fprintf(stderr, "sndFileCheckCopyOnWrite() Error: Two event ids are the same:%d but their parts:%p, %p are not clones!\n", (int)id, p, part); } continue; } part = p; id = ev.id(); ++use_count; } // If more than one unique part wave event is using the file, signify that the caller should make a copy of it. if(use_count > 1) return true; } } } return false; } void sndFileApplyUndoFile(const Event& original, const QString* tmpfile, unsigned startframe, unsigned endframe) { // This one is called on both undo and redo of a wavfile // For redo to be called, undo must have been called first, and we don't store both the original data and the modified data in separate // files. Thus, each time this function is called the data in the "original"-file will be written to the tmpfile, after the data // from the tmpfile has been applied. // // F.ex. if mute has been made on part of a wavfile, the unmuted data is stored in the tmpfile when // the undo operation occurs. The unmuted data is then written back to the original file, and the mute data will be // put in the tmpfile, and when redo is eventually called the data is switched again (causing the muted data to be written to the "original" // file. The data is merely switched. if (original.empty()) { fprintf(stderr, "sndFileApplyUndoFile: Internal error: original event is empty - Aborting\n"); return; } SndFileR orig = original.sndFile(); if (orig.isNull()) { fprintf(stderr, "sndFileApplyUndoFile: Internal error: original sound file is NULL - Aborting\n"); return; } if (orig.canonicalPath().isEmpty()) { fprintf(stderr, "sndFileApplyUndoFile: Error: Original sound file name is empty - Aborting\n"); return; } if (!orig.isOpen()) { if (orig.openRead()) { fprintf(stderr, "sndFileApplyUndoFile: Cannot open original file %s for reading - cannot undo! Aborting\n", orig.canonicalPath().toLocal8Bit().constData()); return; } } SndFile tmp = SndFile(*tmpfile); if (!tmp.isOpen()) { if (tmp.openRead()) { fprintf(stderr, "sndFileApplyUndoFile: Could not open temporary file %s for writing - cannot undo! Aborting\n", tmpfile->toLocal8Bit().constData()); return; } } MusEGlobal::audio->msgIdle(true); tmp.setFormat(orig.format(), orig.channels(), orig.samplerate()); // Read data in original file to memory before applying tmpfile to original unsigned file_channels = orig.channels(); unsigned tmpdatalen = endframe - startframe; float* data2beoverwritten[file_channels]; for (unsigned i=0; imsgIdle(false); return; } tmp.seek(0, 0); tmp.write(file_channels, data2beoverwritten, tmpdatalen, MusEGlobal::config.liveWaveUpdate); tmp.close(); // Delete dataholder for replaced original file for (unsigned i=0; imsgIdle(false); } //--------------------------------------------------------- // sndFileGetWave // If audioConverterSettings and stretchList are given, they are assigned. //--------------------------------------------------------- SndFileR sndFileGetWave(const QString& inName, bool readOnlyFlag, bool openFlag, bool showErrorBox, const AudioConverterSettingsGroup* audioConverterSettings, const StretchList* stretchList) { QString name = inName; if (QFileInfo(name).isRelative()) { name = MusEGlobal::museProject + QString("/") + name; } else { if (!QFile::exists(name)) { if (QFile::exists(MusEGlobal::museProject + QString("/") + name)) { name = MusEGlobal::museProject + QString("/") + name; } } } // Allow multiple instances. SndFile* f = nullptr; if (!QFile::exists(name)) { fprintf(stderr, "wave file <%s> not found\n", name.toLocal8Bit().constData()); return nullptr; } f = new SndFile(name); // Assign audio converter settings if given. if(audioConverterSettings) f->audioConverterSettings()->assign(*audioConverterSettings); // Assign stretch list if given. if(stretchList) *f->stretchList() = *stretchList; if(openFlag) { bool error; if (readOnlyFlag) error = f->openRead(); else { error = f->openWrite(); // if peak cache is older than wave file we reaquire the cache QFileInfo wavinfo(name); QString cacheName = wavinfo.absolutePath() + QString("/") + wavinfo.completeBaseName() + QString(".wca"); QFileInfo wcainfo(cacheName); if (!wcainfo.exists() || wcainfo.lastModified() < wavinfo.lastModified()) { QFile(cacheName).remove(); f->readCache(cacheName,true); } } if (error) { fprintf(stderr, "open wave file(%s) for %s failed: %s\n", name.toLocal8Bit().constData(), readOnlyFlag ? "writing" : "reading", f->strerror().toLocal8Bit().constData()); if(showErrorBox) QMessageBox::critical(nullptr, QObject::tr("MusE import error."), QObject::tr("MusE failed to import the file.\n" "Possibly this wasn't a sound file?\n" "If it was check the permissions, MusE\n" "sometimes requires write access to the file."), QMessageBox::Ok, QMessageBox::Ok); delete f; f = nullptr; } } return f; } } // namespace MusECore muse-4.0.0/src/muse/wave_helper.h000066400000000000000000000032231404105311000166630ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // (C) Copyright 2003-2020 Werner Schweer (ws@seh.de) and others // // wave_helper.h // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __WAVE_HELPER_H__ #define __WAVE_HELPER_H__ #include "event.h" #include "wave.h" #include "time_stretch.h" #include "audio_convert/audio_converter_settings_group.h" namespace MusECore { extern bool sndFileCheckCopyOnWrite(const SndFileR sndFile); extern void sndFileApplyUndoFile(const Event& original, const QString* tmpfile, unsigned startframe, unsigned endframe); // If audioConverterSettings and stretchList are given, they are assigned. extern SndFileR sndFileGetWave(const QString& name, bool readOnlyFlag, bool openFlag = true, bool showErrorBox = true, const AudioConverterSettingsGroup* audioConverterSettings = nullptr, const StretchList* stretchList = nullptr); } #endif muse-4.0.0/src/muse/waveedit/000077500000000000000000000000001404105311000160215ustar00rootroot00000000000000muse-4.0.0/src/muse/waveedit/CMakeLists.txt000066400000000000000000000042101404105311000205560ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( waveedit_mocs editgain.h waveedit.h wavecanvas.h ) ## ## UI files ## file (GLOB waveedit_ui_files editgainbase.ui ) QT5_WRAP_UI (waveedit_ui_headers ${waveedit_ui_files} ) ## ## List of source files to compile ## file (GLOB waveedit_source_files editgain.cpp waveedit.cpp wavecanvas.cpp ) ## ## Define target ## add_library ( waveedit ${MODULES_BUILD} ${waveedit_source_files} ${waveedit_ui_headers} ${waveedit_mocs} ) add_dependencies(waveedit components) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${waveedit_source_files} ${waveedit_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( waveedit PROPERTIES OUTPUT_NAME muse_waveedit ) ## ## Linkage ## target_link_libraries( waveedit ${QT_LIBRARIES} widgets ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS waveedit DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/waveedit/editgain.cpp000066400000000000000000000055221404105311000203150ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/waveedit/editgain.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // Author: Mathias Lundgren , (C) 2005 // // Copyright: See COPYING file that comes with this distribution // // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // // C++ Implementation: editgain // // Description: // // #include #include "editgain.h" namespace MusEGui { EditGain::EditGain(QWidget* parent, int initGainValue) : QDialog(parent) { setupUi(this); sliderGain->setValue(sliderGain->maximum() - initGainValue); connect(buttonReset, SIGNAL(pressed()), this, SLOT(resetPressed())); connect(buttonApply, SIGNAL(pressed()), this, SLOT(applyPressed())); connect(buttonCancel,SIGNAL(pressed()), this, SLOT(cancelPressed())); connect(sliderGain, SIGNAL(valueChanged(int)), this, SLOT(gainChanged(int))); if (sliderGain->value() != 100) buttonReset->setEnabled(true); } EditGain::~EditGain() { } /*! \fn EditGain::resetPressed */ void EditGain::resetPressed() { sliderGain->blockSignals(true); sliderGain->setValue(100); sliderGain->blockSignals(false); buttonReset->setEnabled(false); buttonApply->setEnabled(false); } /*! \fn EditGain::applyPressed() */ void EditGain::applyPressed() { done(QDialog::Accepted); } /*! \fn EditGain::cancelPressed() */ void EditGain::cancelPressed() { done(QDialog::Rejected); } /*! \fn EditGain::gainChanged(int value) */ void EditGain::gainChanged(int value) { gain = value; if (sliderGain->value() != 100) { buttonReset->setEnabled(true); buttonApply->setEnabled(true); } else { buttonReset->setEnabled(false); buttonApply->setEnabled(false); } } /*! \fn EditGain::getGain() */ int EditGain::getGain() { return gain; } } // namespace MusEGui muse-4.0.0/src/muse/waveedit/editgain.h000066400000000000000000000031541404105311000177610ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/waveedit/editgain.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // Author: Mathias Lundgren , (C) 2005 // // Copyright: See COPYING file that comes with this distribution // // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // // C++ Interface: editgain // // Description: // // #ifndef EDITGAIN_H #define EDITGAIN_H #include "ui_editgainbase.h" class QDialog; namespace MusEGui { class EditGain : public QDialog, public Ui::EditGainBase { Q_OBJECT public: EditGain(QWidget* parent = 0, int initGainValue=100); ~EditGain(); int getGain(); private: int gain; private slots: void resetPressed(); void applyPressed(); void cancelPressed(); void gainChanged(int value); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/waveedit/editgainbase.ui000066400000000000000000000161601404105311000210030ustar00rootroot00000000000000 EditGainBase 0 0 284 367 MusE: Modify gain Gain Qt::Horizontal QSizePolicy::Expanding 90 41 40 150 200 100 Qt::Vertical QSlider::TicksBothSides 10 200% false Qt::Vertical QSizePolicy::Expanding 30 109 100% false Qt::Vertical QSizePolicy::Expanding 30 108 0% false Qt::Horizontal QSizePolicy::Expanding 51 31 Qt::Horizontal QSizePolicy::Expanding 70 31 false &Reset Alt+R Qt::Horizontal QSizePolicy::Expanding 71 31 false &Apply Alt+A Qt::Horizontal QSizePolicy::Expanding 61 31 true &Cancel Alt+C muse-4.0.0/src/muse/waveedit/wavecanvas.cpp000066400000000000000000004021331404105311000206660ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // wavecanvas.cpp // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // Based on WaveView.cpp and PianoCanvas.cpp // (C) Copyright 2000 Werner Schweer (ws@seh.de) // and others. // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "muse_math.h" #include #include "app.h" #include "icons.h" #include "xml.h" #include "wavecanvas.h" #include "event.h" #include "globals.h" #include "cmd.h" #include "song.h" #include "audio.h" #include "functions.h" #include "gconfig.h" #include "shortcuts.h" #include "editgain.h" #include "wave.h" #include "waveedit.h" #include "fastlog.h" #include "utils.h" #include "tools.h" #include "copy_on_write.h" #include "helper.h" #include "sig.h" #include "wave_helper.h" #include "config.h" #include "menutitleitem.h" #include "audio_converter_settings.h" #include "audio_convert/audio_converter_plugin.h" #include "audio_convert/audio_converter_settings_group.h" #include "sndfile.h" #include "operations.h" // Forwards from header: #include #include #include #include #include #include #include #include #include "part.h" #include "track.h" #define ABS(x) (abs(x)) #define FABS(x) (fabs(x)) // For debugging output: Uncomment the fprintf section. #define ERROR_WAVECANVAS(dev, format, args...) fprintf(dev, format, ##args) #define INFO_WAVECANVAS(dev, format, args...) // fprintf(dev, format, ##args) #define DEBUG_WAVECANVAS(dev, format, args...) // fprintf(dev, format, ##args) namespace MusEGui { const int WaveCanvas::_stretchAutomationPointDetectDist = 4; const int WaveCanvas::_stretchAutomationPointWidthUnsel = 2; const int WaveCanvas::_stretchAutomationPointWidthSel = 3; //--------------------------------------------------------- // WEvent //--------------------------------------------------------- WEvent::WEvent(const MusECore::Event& e, MusECore::Part* p, int height) : EItem(e, p) { unsigned frame = e.frame() + p->frame(); setPos(QPoint(frame, 0)); unsigned len = e.lenFrame(); if(e.frame() + e.lenFrame() >= p->lenFrame()) len = p->lenFrame() - e.frame(); setBBox(QRect(frame, 0, len, height)); // Give the moving point an initial value. setMp(pos()); } //--------------------------------------------------------- // addItem //--------------------------------------------------------- CItem* WaveCanvas::addItem(MusECore::Part* part, const MusECore::Event& event) { WEvent* ev = new WEvent(event, part, height()); items.add(ev); return ev; } //--------------------------------------------------------- // WaveCanvas //--------------------------------------------------------- WaveCanvas::WaveCanvas(MidiEditor* pr, QWidget* parent, int sx, int sy) : EventCanvas(pr, parent, sx, 1) { setObjectName("WaveCanvas"); setStatusTip(tr("Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help.")); colorMode = 0; button = 0; supportsResizeToTheLeft = true; editor = pr; setVirt(true); setBg(QColor()); pos[0] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->cpos()); pos[1] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->lpos()); pos[2] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->rpos()); yScale = sy; mode = NORMAL; selectionStart = 0; selectionStop = 0; lastGainvalue = 100; songChanged(SC_TRACK_INSERTED); } WaveCanvas::~WaveCanvas() { //delete steprec; } //--------------------------------------------------------- // updateItems //--------------------------------------------------------- void WaveCanvas::updateItems() { bool curItemNeedsRestore=false; MusECore::Event storedEvent; int partSn = 0; if (curItem) { curItemNeedsRestore=true; storedEvent=curItem->event(); partSn=curItem->part()->sn(); } curItem=NULL; items.clearDelete(); startSample = INT_MAX; endSample = 0; curPart = 0; for (MusECore::iPart p = editor->parts()->begin(); p != editor->parts()->end(); ++p) { MusECore::WavePart* part = (MusECore::WavePart*)(p->second); if (part->sn() == curPartId) curPart = part; unsigned ssample = part->frame(); unsigned len = part->lenFrame(); unsigned esample = ssample + len; if (ssample < startSample) startSample = ssample; if (esample > endSample) endSample = esample; for (MusECore::ciEvent i = part->events().begin(); i != part->events().end(); ++i) { const MusECore::Event& e = i->second; // Do not add events which are past the end of the part. #ifdef ALLOW_LEFT_HIDDEN_EVENTS if((int)e.frame() + (int)e.lenFrame() < 0) continue; if((int)e.frame() >= (int)len) break; #else if(e.frame() > len) break; #endif if (e.type() == MusECore::Wave) { CItem* temp = addItem(part, e); if (temp && curItemNeedsRestore && e==storedEvent && part->sn()==partSn) { if (curItem!=NULL) printf("THIS SHOULD NEVER HAPPEN: curItemNeedsRestore=true, event fits, but there was already a fitting event!?\n"); curItem=temp; } } } } } //--------------------------------------------------------- // songChanged(type) //--------------------------------------------------------- void WaveCanvas::songChanged(MusECore::SongChangedStruct_t flags) { if (flags & ~(SC_SELECTION | SC_PART_SELECTION | SC_TRACK_SELECTION)) { // TODO FIXME: don't we actually only want SC_PART_*, and maybe SC_TRACK_DELETED? // (same in waveview.cpp) updateItems(); } MusECore::Event event; MusECore::WavePart* part = 0; int x = 0; CItem* nevent = 0; int n = 0; // count selections for (iCItem k = items.begin(); k != items.end(); ++k) { if (k->second->event().selected()) { ++n; if (!nevent) { nevent = k->second; } } } if (flags & SC_AUDIO_STRETCH) { for(iStretchSelectedItem is = _stretchAutomation._stretchSelectedList.begin(); is != _stretchAutomation._stretchSelectedList.end(); ) { MusECore::MuseFrame_t frame = is->first; StretchSelectedItem& ssi = is->second; MusECore::StretchList* sl = ssi._sndFile.stretchList(); if(!sl) continue; MusEGui::ciCItem i; for(i = items.begin(); i != items.end(); ++i) { WEvent* we = static_cast(i->second); MusECore::Event e = we->event(); if(MusECore::StretchList* e_sl = e.sndFile().stretchList()) { if(e_sl == sl && e_sl->find(frame) != e_sl->end()) break; } } if(i == items.end()) { iStretchSelectedItem is_save = is; _stretchAutomation._stretchSelectedList.erase(is); is = is_save; } else ++is; } } if (flags & SC_CLIP_MODIFIED) { redraw(); // Boring, but the only thing possible to do } if (flags & SC_TEMPO) { setPos(0, MusEGlobal::song->cpos(), false); setPos(1, MusEGlobal::song->lpos(), false); setPos(2, MusEGlobal::song->rpos(), false); } if (n >= 1) { x = nevent->x(); event = nevent->event(); part = (MusECore::WavePart*)nevent->part(); if (_setCurPartIfOnlyOneEventIsSelected && n == 1 && curPart != part) { curPart = part; curPartId = curPart->sn(); curPartChanged(); } } if(flags & (SC_SELECTION)) { // Prevent race condition: Ignore if the change was ultimately sent by the canvas itself. if(flags._sender != this) updateItemSelections(); } bool f1 = static_cast(flags & (SC_EVENT_INSERTED | SC_EVENT_MODIFIED | SC_EVENT_REMOVED | SC_PART_INSERTED | SC_PART_MODIFIED | SC_PART_REMOVED | SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_TRACK_MODIFIED | SC_SIG | SC_TEMPO | SC_KEY | SC_MASTER | SC_CONFIG | SC_DRUMMAP)); bool f2 = static_cast(flags & SC_SELECTION); if(f1 || f2) // Try to avoid all unnecessary emissions. emit selectionChanged(x, event, part, !f1); if (curPart == 0) curPart = (MusECore::WavePart*)(editor->parts()->begin()->second); redraw(); } //--------------------------------------------------------- // selectAtTick //--------------------------------------------------------- void WaveCanvas::selectAtTick(unsigned int tick) { selectAtFrame(MusEGlobal::tempomap.tick2frame(tick)); } //--------------------------------------------------------- // selectAtFrame //--------------------------------------------------------- void WaveCanvas::selectAtFrame(unsigned int frame) { //Select event nearest frame, if none selected and there are any if (!items.empty() && selectionSize() == 0) { iCItem i = items.begin(); CItem* nearest = i->second; while (i != items.end()) { CItem* cur=i->second; unsigned int curf=abs(cur->x() + (int)cur->part()->frame() - (int)frame); unsigned int nearf=abs(nearest->x() + (int)nearest->part()->frame() - (int)frame); if (curf < nearf) { nearest=cur; } i++; } if (!nearest->isSelected()) { selectItem(nearest, true); songChanged(SC_SELECTION); } } } ////--------------------------------------------------------- //// getCaption ////--------------------------------------------------------- //QString WaveCanvas::getCaption() const // { // int bar1, bar2, xx; // unsigned x; // MusEGlobal::sigmap.tickValues(curPart->tick(), &bar1, &xx, &x); // MusEGlobal::sigmap.tickValues(curPart->tick() + curPart->lenTick(), &bar2, &xx, &x); // return QString("Part <") + curPart->name() // + QString("> %1-%2").arg(bar1+1).arg(bar2+1); // } //--------------------------------------------------------- // track //--------------------------------------------------------- MusECore::WaveTrack* WaveCanvas::track() const { return ((MusECore::WavePart*)curPart)->track(); } //--------------------------------------------------------- // keyPress //--------------------------------------------------------- void WaveCanvas::keyPress(QKeyEvent* event) { int key = event->key(); if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) key += Qt::SHIFT; if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) key += Qt::ALT; if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) key+= Qt::CTRL; if (key == shortcuts[SHRT_DELETE].key) { switch (_tool) { case StretchTool: case SamplerateTool: { MusECore::PendingOperationList operations; StretchSelectedList_t& ssl = _stretchAutomation._stretchSelectedList; for(iStretchSelectedItem isi = ssl.begin(); isi != ssl.end(); ++isi) { StretchSelectedItem& ssi = isi->second; // ssi._sndFile.delAtStretchListOperation(ssi._type, isi->first, operations); MusEGlobal::song->delAtStretchListOperation(ssi._sndFile, ssi._type, isi->first, operations); } ssl.clear(); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } break; default: break; } return; } // TODO: New WaveCanvas: Convert these to frames, and remove unneeded functions. // Select items by key (PianoRoll & DrumEditor) else if (key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key) { rciCItem i; if (items.empty()) return; for (i = items.rbegin(); i != items.rend(); ++i) if (i->second->isSelected()) break; if(i == items.rend()) i = items.rbegin(); if(i != items.rbegin()) --i; if(i->second) { if (key != shortcuts[SHRT_SEL_RIGHT_ADD].key) deselectAll(); CItem* sel = i->second; sel->setSelected(true); redraw(); if (sel->x() + sel->width() > mapxDev(width())) { int mx = rmapx(sel->x()); int newx = mx + rmapx(sel->width()) - width(); // Leave a bit of room for the specially-drawn drum notes. But good for piano too. emit horizontalScroll( (newx > mx ? mx - 10: newx + 10) - rmapx(xorg) ); } } } //Select items by key: (PianoRoll & DrumEditor) else if (key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key) { ciCItem i; if (items.empty()) return; for (i = items.begin(); i != items.end(); ++i) if (i->second->isSelected()) break; if(i == items.end()) i = items.begin(); if(i != items.begin()) --i; if(i->second) { if (key != shortcuts[SHRT_SEL_LEFT_ADD].key) deselectAll(); CItem* sel = i->second; sel->setSelected(true); redraw(); if (sel->x() <= mapxDev(0)) emit horizontalScroll(rmapx(sel->x() - xorg) - 10); // Leave a bit of room. } } //else if (key == shortcuts[SHRT_INC_PITCH].key) { // modifySelected(NoteInfo::VAL_PITCH, 1); // } //else if (key == shortcuts[SHRT_DEC_PITCH].key) { // modifySelected(NoteInfo::VAL_PITCH, -1); // } else if (key == shortcuts[SHRT_INC_POS].key) { // TODO: Check boundaries modifySelected(NoteInfo::VAL_TIME, editor->raster()); } else if (key == shortcuts[SHRT_DEC_POS].key) { // TODO: Check boundaries modifySelected(NoteInfo::VAL_TIME, 0 - editor->raster()); } else if (key == shortcuts[SHRT_INCREASE_LEN].key) { // TODO: Check boundaries modifySelected(NoteInfo::VAL_LEN, editor->raster()); } else if (key == shortcuts[SHRT_DECREASE_LEN].key) { // TODO: Check boundaries modifySelected(NoteInfo::VAL_LEN, 0 - editor->raster()); } else event->ignore(); } //--------------------------------------------------------- // keyRelease //--------------------------------------------------------- void WaveCanvas::keyRelease(QKeyEvent* event) { const int key = event->key(); // We do not want auto-repeat events. // It does press and release repeatedly. Wait till the last release comes. if(!event->isAutoRepeat()) { // Select part to the right if(key == shortcuts[SHRT_SEL_RIGHT].key || key == shortcuts[SHRT_SEL_RIGHT_ADD].key || // Select part to the left key == shortcuts[SHRT_SEL_LEFT].key || key == shortcuts[SHRT_SEL_LEFT_ADD].key) { itemSelectionsChanged(); } return; } EventCanvas::keyRelease(event); } //--------------------------------------------------------- // setPos // set one of three markers // idx - 0-cpos 1-lpos 2-rpos // flag - emit followEvent() //--------------------------------------------------------- void WaveCanvas::setPos(int idx, unsigned val, bool adjustScrollbar) { val = MusEGlobal::tempomap.tick2frame(val); if (pos[idx] == val) return; int opos = mapx(pos[idx]); int npos = mapx(val); if (adjustScrollbar && idx == 0) { switch (MusEGlobal::song->follow()) { case MusECore::Song::NO: break; case MusECore::Song::JUMP: if (npos >= width()) { int ppos = val - xorg - rmapxDev(width()/4); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } else if (npos < 0) { int ppos = val - xorg - rmapxDev(width()*3/4); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } break; case MusECore::Song::CONTINUOUS: if (npos > (width()*5)/8) { int ppos = pos[idx] - xorg - rmapxDev(width()*5/8); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } else if (npos < (width()*3)/8) { int ppos = pos[idx] - xorg - rmapxDev(width()*3/8); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } break; } } int x; int w = 1; if (opos > npos) { w += opos - npos; x = npos; } else { w += npos - opos; x = opos; } pos[idx] = val; redraw(QRect(x-1, 0, w+2, height())); // From Canvas::draw (is otherwise identical). Fix for corruption. (TEST: New WaveCanvas: Still true?) } //--------------------------------------------------------- // setYScale //--------------------------------------------------------- void WaveCanvas::setYScale(int val) { yScale = val; redraw(); } //--------------------------------------------------------- // drawMarkers //--------------------------------------------------------- void WaveCanvas::drawMarkers(QPainter& p, const QRect& mr, const QRegion&) { const int mx = mr.x(); const int my = mr.y(); const int mw = mr.width(); const int mh = mr.height(); const int my_2 = my + mh; const ViewXCoordinate vx(mx, true); const ViewWCoordinate vw(mw, true); const ViewXCoordinate vx_2(mx + mw, true); QPen pen; pen.setCosmetic(true); MusECore::MarkerList* marker = MusEGlobal::song->marker(); pen.setColor(MusEGlobal::config.markerColor); p.setPen(pen); for (MusECore::iMarker m = marker->begin(); m != marker->end(); ++m) { const ViewXCoordinate xp(MusEGlobal::tempomap.tick2frame(m->second.tick()), false); if (isXInRange(xp, vx, vx_2)) { const int mxp = asMapped(xp)._value; p.drawLine(mxp, my, mxp, my_2); } } } //--------------------------------------------------------- // drawWaveParts //--------------------------------------------------------- void WaveCanvas::drawParts(QPainter& p, bool do_cur_part, const QRect& mr, const QRegion&) { bool wmtxen = p.worldMatrixEnabled(); p.setWorldMatrixEnabled(false); if(do_cur_part) { // Draw current part: if(curPart) { QRect mwpr = map(QRect(curPart->frame(), 0, curPart->lenFrame(), height())); QRect mpbgr = mr & mwpr; if(!mpbgr.isNull()) { QColor c; switch(colorMode) { default: case 0: if (curPart->colorIndex() == 0 && MusEGlobal::config.useTrackColorForParts) c = curPart->track()->color(); else c = MusEGlobal::config.partColors[curPart->colorIndex()]; break; case 1: c = Qt::lightGray; break; } c.setAlpha(MusEGlobal::config.globalAlphaBlend); QBrush part_bg_brush(MusECore::gGradientFromQColor(c, mwpr.topLeft(), mwpr.bottomLeft())); p.fillRect(mpbgr, part_bg_brush); } } } else { // Draw non-current parts: for (MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { MusECore::WavePart* wp = (MusECore::WavePart*)(ip->second); if(wp == curPart) continue; QRect mwpr = map(QRect(wp->frame(), 0, wp->lenFrame(), height())); QRect mpbgr = mr & mwpr; if(!mpbgr.isNull()) { QColor c(MusEGlobal::config.waveNonselectedPart); c.setAlpha(MusEGlobal::config.globalAlphaBlend); QBrush part_bg_brush(MusECore::gGradientFromQColor(c, mwpr.topLeft(), mwpr.bottomLeft())); p.fillRect(mpbgr, part_bg_brush); } } } p.setWorldMatrixEnabled(wmtxen); } // TODO: Overridden because we're in units of frames. // After BBT/frame mode is added to Canvas, remove this override and let Canvas do it. //--------------------------------------------------------- // raster //--------------------------------------------------------- QPoint WaveCanvas::raster(const QPoint& p) const { int x = p.x(); if (x < 0) x = 0; // Normally frame to tick methods round down. But here we need it to 'snap' // the frame from either side of a tick to the tick. So round to nearest. x = MusEGlobal::tempomap.tick2frame(editor->rasterVal(MusEGlobal::tempomap.frame2tick(x, 0, MusECore::LargeIntRoundNearest))); int pitch = y2pitch(p.y()); int y = pitch2y(pitch); return QPoint(x, y); } #define WHEEL_STEPSIZE 50 #define WHEEL_DELTA 120 //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void WaveCanvas::wheelEvent(QWheelEvent* ev) { int keyState = ev->modifiers(); bool shift = keyState & Qt::ShiftModifier; bool ctrl = keyState & Qt::ControlModifier; const QPoint pixelDelta = ev->pixelDelta(); const QPoint angleDegrees = ev->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; if (shift) { // scroll horizontally int d = -delta / WHEEL_DELTA; int xpixelscale = 5*MusECore::fast_log10(rmapxDev(1)); if (xpixelscale <= 0) xpixelscale = 1; int scrollstep = WHEEL_STEPSIZE * ( d ); scrollstep = scrollstep / 10; int newXpos = xpos + xpixelscale * scrollstep; if (newXpos < 0) newXpos = 0; emit horizontalScroll((unsigned)newXpos); } else if (ctrl) { // zoom horizontally #if QT_VERSION >= 0x050e00 emit horizontalZoom(delta>0, ev->globalPosition().toPoint()); #else emit horizontalZoom(delta>0, ev->globalPos()); #endif } else { // scroll horizontally emit mouseWheelMoved(delta / 10); } } //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- bool WaveCanvas::mousePress(QMouseEvent* event) { // if (event->modifiers() & Qt::ControlModifier) { // return true; // } const bool ctl = event->modifiers() & Qt::ControlModifier; button = event->button(); QPoint pt = event->pos(); unsigned x = event->x(); switch (_tool) { default: break; case RangeTool: if (ctl) return true; switch (button) { case Qt::LeftButton: if (mode == NORMAL) { // redraw and reset: if (selectionStart != selectionStop) { selectionStart = selectionStop = 0; redraw(); } mode = DRAG; dragstartx = x; selectionStart = selectionStop = x; drag = DRAG_LASSO_START; Canvas::start = pt; return false; } break; case Qt::MidButton: case Qt::RightButton: default: break; } break; case StretchTool: case SamplerateTool: { if(button != Qt::LeftButton) return true; StretchSelectedList_t& ssl = _stretchAutomation._stretchSelectedList; //if(!ctl) //{ // ssl.clear(); // update(); //} // TODO Look properly through the whole list instead of just current one. //if(!curItem) // break; //WEvent* wevent = static_cast(curItem); CItem* item = items.find(pt); if(!item) break; WEvent* wevent = static_cast(item); const MusECore::Event event = wevent->event(); if(event.type() != MusECore::Wave) break; MusECore::SndFileR sf = event.sndFile(); if(sf.isNull()) break; MusECore::StretchList* sl = sf.stretchList(); if(!sl) break; const double sf_sr_ratio = sf->sampleRateRatio(); MusECore::StretchListItem::StretchEventType type; if(_tool == StretchTool) type = MusECore::StretchListItem::StretchEvent; else //if(_tool == SamplerateTool) type = MusECore::StretchListItem::SamplerateEvent; MusECore::iStretchListItem isli_hit_test = stretchListHitTest(type, pt, wevent); if(isli_hit_test == sl->end()) { if(!ctl) { ssl.clear(); update(); } double newframe = sl->unSquish(sf_sr_ratio * double(x - wevent->x())); MusECore::PendingOperationList operations; MusEGlobal::song->addAtStretchListOperation(sf, type, newframe, sl->ratioAt(type, newframe), operations); MusEGlobal::audio->msgExecutePendingOperations(operations, true); ssl.insert(StretchSelectedItemInsertPair_t(newframe, StretchSelectedItem(type, sf))); _stretchAutomation._startMovePoint = pt; _stretchAutomation._controllerState = stretchStartMove; QWidget::setCursor(Qt::SizeHorCursor); break; } iStretchSelectedItemPair res = ssl.equal_range(isli_hit_test->first); iStretchSelectedItem isi; for(isi = res.first; isi != res.second; ++isi) if(isi->second._sndFile.stretchList() == sl && isi->second._type) break; if(isi != res.second) { if(ctl) { ssl.erase(isi); //setCursor(); update(); } else { _stretchAutomation._startMovePoint = pt; _stretchAutomation._controllerState = stretchStartMove; QWidget::setCursor(Qt::SizeHorCursor); } } else { if(!ctl) ssl.clear(); ssl.insert(std::pair(isli_hit_test->first, StretchSelectedItem(type, sf))); _stretchAutomation._startMovePoint = pt; _stretchAutomation._controllerState = stretchStartMove; QWidget::setCursor(Qt::SizeHorCursor); update(); } } break; } return true; } //--------------------------------------------------------- // viewMouseReleaseEvent //--------------------------------------------------------- void WaveCanvas::mouseRelease(QMouseEvent* ev) { QPoint pt = ev->pos(); const bool ctl = ev->modifiers() & Qt::ControlModifier; switch(_tool) { case StretchTool: case SamplerateTool: { if(button != Qt::LeftButton) { _stretchAutomation._controllerState = stretchDoNothing; setStretchAutomationCursor(pt); return; } StretchSelectedList_t& ssl = _stretchAutomation._stretchSelectedList; switch(_stretchAutomation._controllerState) { case stretchMovingController: case stretchAddNewController: //setCursor(); break; case stretchDoNothing: case stretchStartMove: if(!ctl) { ssl.clear(); update(); } // TODO Look properly through the whole list instead of just current one. //if(!curItem) // break; //WEvent* wevent = static_cast(curItem); CItem* item = items.find(pt); if(!item) break; WEvent* wevent = static_cast(item); const MusECore::Event event = wevent->event(); if(event.type() != MusECore::Wave) break; const MusECore::SndFileR sf = event.sndFile(); if(sf.isNull()) break; MusECore::StretchList* sl = sf.stretchList(); if(!sl) break; MusECore::StretchListItem::StretchEventType type; if(_tool == StretchTool) type = MusECore::StretchListItem::StretchEvent; else // if(_tool == SamplerateTool) type = MusECore::StretchListItem::SamplerateEvent; MusECore::iStretchListItem isli_hit_test = stretchListHitTest(type, pt, wevent); if(isli_hit_test == sl->end()) break; iStretchSelectedItemPair res = ssl.equal_range(isli_hit_test->first); iStretchSelectedItem isi; for(isi = res.first; isi != res.second; ++isi) if(isi->second._sndFile.stretchList() == sl && isi->second._type) break; if(isi == res.second) { ssl.insert(std::pair(isli_hit_test->first, StretchSelectedItem(type, sf))); update(); } break; } } break; default: break; } _stretchAutomation._controllerState = stretchDoNothing; button = Qt::NoButton; if(mode == DRAG) mode = NORMAL; setStretchAutomationCursor(pt); } //--------------------------------------------------------- // viewMousevent //--------------------------------------------------------- void WaveCanvas::mouseMove(QMouseEvent* event) { QPoint pt = event->pos(); int x = pt.x(); if (x < 0) x = 0; // Special for wave canvas: Normally in the base class EventCanvas we would rasterize the tick before emitting, // but since these units are in frames we handle displaying the position specially in the WaveEditor. // One reason is that we may need the precise frame, such as for the editing 'functions' and the stretching below. // Snapping those things, even to the minimum 1 tick ('off'), might be undesirable when we're editing at wave level. emit timeChanged(x); switch(_tool) { case StretchTool: case SamplerateTool: { event->accept(); //bool slowMotion = event->modifiers() & Qt::ShiftModifier; //processStretchAutomationMovements(event->pos(), slowMotion); //if(button != Qt::LeftButton) //{ // _stretchAutomation._controllerState = stretchDoNothing; // setCursor(); // return; //} switch(_stretchAutomation._controllerState) { case stretchDoNothing: case stretchAddNewController: setStretchAutomationCursor(pt); break; case stretchStartMove: _stretchAutomation._controllerState = stretchMovingController; // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case stretchMovingController: { if(button != Qt::LeftButton) { _stretchAutomation._controllerState = stretchDoNothing; //setCursor(); break;; } QPoint delta_pt = QPoint(pt.x() - _stretchAutomation._startMovePoint.x(), pt.y() - _stretchAutomation._startMovePoint.y()); if(delta_pt.x() == 0) break; double prevNewVal = 0, thisNewVal = 0; double prevFrame, thisFrame, nextFrame, delta_fr, next_delta_fr; MusECore::PendingOperationList operations; StretchSelectedList_t& ssl = _stretchAutomation._stretchSelectedList; for(ciStretchSelectedItem iss = ssl.begin(); iss != ssl.end(); ++iss) { const StretchSelectedItem& ssi = iss->second; MusECore::SndFileR sf = ssi._sndFile; if(sf.isNull()) continue; MusECore::StretchList* sl = sf.stretchList(); if(!sl) continue; MusECore::iStretchListItem isli_typed = sl->findEvent(ssi._type, iss->first); if(isli_typed == sl->end()) continue; const double wave_sr_ratio = sf.sampleRateRatio(); thisFrame = double(isli_typed->first); const MusECore::StretchListItem& sli_typed = isli_typed->second; MusECore::iStretchListItem prev_isli_typed = sl->previousEvent(ssi._type, isli_typed); if(prev_isli_typed == sl->end()) continue; prevFrame = double(prev_isli_typed->first); const MusECore::StretchListItem& prev_sli_typed = prev_isli_typed->second; MusECore::iStretchListItem next_isli_typed = sl->nextEvent(ssi._type, isli_typed); if(next_isli_typed == sl->end()) nextFrame = double(sf.samples()); else nextFrame = double(next_isli_typed->first); next_delta_fr = nextFrame - thisFrame; // FIXME: Comparing double with zero. if(next_delta_fr <= 0) continue; delta_fr = thisFrame - prevFrame; // FIXME: Comparing double with zero. if(delta_fr <= 0) continue; const double minStretchRatio = sf.minStretchRatio(); const double maxStretchRatio = sf.maxStretchRatio(); const double minSamplerateRatio = sf.minSamplerateRatio(); const double maxSamplerateRatio = sf.maxSamplerateRatio(); switch(ssi._type) { case MusECore::StretchListItem::StretchEvent: { const double left_prev_smpx = prev_sli_typed._samplerateSquishedFrame; const double left_this_smpx = sl->squish(thisFrame, MusECore::StretchListItem::SamplerateEvent); const double left_dsmpx = left_this_smpx - left_prev_smpx; const double left_effective_sr = left_dsmpx / delta_fr; const double right_this_smpx = sli_typed._samplerateSquishedFrame; const double right_next_smpx = sl->squish(nextFrame, MusECore::StretchListItem::SamplerateEvent); const double right_dsmpx = right_next_smpx - right_this_smpx; const double right_effective_sr = right_dsmpx / next_delta_fr; INFO_WAVECANVAS(stderr, "WaveCanvas::mouseMove StretchEvent delta_pt.x:%d\n", delta_pt.x()); double left_min_stretch_delta_x = (minStretchRatio - prev_sli_typed._stretchRatio) * left_effective_sr * delta_fr; if(left_min_stretch_delta_x > 0.0) left_min_stretch_delta_x = 0.0; if((double)delta_pt.x() < left_min_stretch_delta_x) delta_pt.setX(left_min_stretch_delta_x); double right_min_stretch_delta_x = (minStretchRatio - sli_typed._stretchRatio) * right_effective_sr * next_delta_fr; if(right_min_stretch_delta_x > 0.0) right_min_stretch_delta_x = 0.0; if(-(double)delta_pt.x() < right_min_stretch_delta_x) delta_pt.setX(right_min_stretch_delta_x); INFO_WAVECANVAS(stderr, " left_min_stretch_delta_x:%f right_min_stretch_delta_x:%f\n", left_min_stretch_delta_x, right_min_stretch_delta_x); if(maxStretchRatio > 0.0) { double left_max_stretch_delta_x = (maxStretchRatio - prev_sli_typed._stretchRatio) * left_effective_sr * delta_fr; if(left_max_stretch_delta_x < 0.0) left_max_stretch_delta_x = 0.0; if((double)delta_pt.x() > left_max_stretch_delta_x) delta_pt.setX(left_max_stretch_delta_x); double right_max_stretch_delta_x = (maxStretchRatio - sli_typed._stretchRatio) * right_effective_sr * next_delta_fr; if(right_max_stretch_delta_x < 0.0) right_max_stretch_delta_x = 0.0; if((double)delta_pt.x() > right_max_stretch_delta_x) delta_pt.setX(right_max_stretch_delta_x); } const double left_effective_dx = wave_sr_ratio * (double)delta_pt.x() / left_effective_sr; prevNewVal = prev_sli_typed._stretchRatio + (left_effective_dx / delta_fr); const double right_effective_dx = wave_sr_ratio * (double)delta_pt.x() / right_effective_sr; thisNewVal = sli_typed._stretchRatio - (right_effective_dx / next_delta_fr); } break; case MusECore::StretchListItem::SamplerateEvent: { const double left_prev_strx = prev_sli_typed._stretchSquishedFrame; const double left_this_strx = sl->squish(thisFrame, MusECore::StretchListItem::StretchEvent); const double left_dstrx = left_this_strx - left_prev_strx; const double left_effective_str = left_dstrx / delta_fr; const double right_this_strx = sli_typed._stretchSquishedFrame; const double right_next_strx = sl->squish(nextFrame, MusECore::StretchListItem::StretchEvent); const double right_dstrx = right_next_strx - right_this_strx; const double right_effective_str = right_dstrx / next_delta_fr; INFO_WAVECANVAS(stderr, "WaveCanvas::mouseMove SamplerateEvent delta_pt.x:%d\n", delta_pt.x()); double left_min_samplerate_delta_x = (minSamplerateRatio - prev_sli_typed._samplerateRatio) * left_effective_str * delta_fr; if(left_min_samplerate_delta_x > 0.0) left_min_samplerate_delta_x = 0.0; if((double)delta_pt.x() < left_min_samplerate_delta_x) delta_pt.setX(left_min_samplerate_delta_x); double right_min_samplerate_delta_x = (minSamplerateRatio - sli_typed._samplerateRatio) * right_effective_str * next_delta_fr; if(right_min_samplerate_delta_x > 0.0) right_min_samplerate_delta_x = 0.0; if(-(double)delta_pt.x() < right_min_samplerate_delta_x) delta_pt.setX(right_min_samplerate_delta_x); INFO_WAVECANVAS(stderr, " left_min_samplerate_delta_x:%f right_min_samplerate_delta_x:%f\n", left_min_samplerate_delta_x, right_min_samplerate_delta_x); if(maxSamplerateRatio > 0.0) { double left_max_samplerate_delta_x = (maxSamplerateRatio - prev_sli_typed._samplerateRatio) * left_effective_str * delta_fr; if(left_max_samplerate_delta_x < 0.0) left_max_samplerate_delta_x = 0.0; if((double)delta_pt.x() > left_max_samplerate_delta_x) delta_pt.setX(left_max_samplerate_delta_x); double right_max_samplerate_delta_x = (maxSamplerateRatio - sli_typed._samplerateRatio) * right_effective_str * next_delta_fr; if(right_max_samplerate_delta_x < 0.0) right_max_samplerate_delta_x = 0.0; if((double)delta_pt.x() > right_max_samplerate_delta_x) delta_pt.setX(right_max_samplerate_delta_x); } const double left_effective_dx = wave_sr_ratio * (double)delta_pt.x() / left_effective_str; prevNewVal = 1.0 / ((1.0 / prev_sli_typed._samplerateRatio) + (left_effective_dx / delta_fr)); const double right_effective_dx = wave_sr_ratio * (double)delta_pt.x() / right_effective_str; thisNewVal = 1.0 / ((1.0 / sli_typed._samplerateRatio) - (right_effective_dx / next_delta_fr)); } break; case MusECore::StretchListItem::PitchEvent: prevNewVal = prev_sli_typed._pitchRatio; // TODO break; } MusEGlobal::song->modifyAtStretchListOperation(sf, ssi._type, prevFrame, prevNewVal, operations); MusEGlobal::song->modifyAtStretchListOperation(sf, ssi._type, thisFrame, thisNewVal, operations); } MusEGlobal::audio->msgExecutePendingOperations(operations, true); _stretchAutomation._startMovePoint = pt; } break; } } break; default: { event->ignore(); switch (button) { case Qt::LeftButton: if (mode == DRAG) { int mx = mapx(x); int mstart = mapx(selectionStart); int mstop = mapx(selectionStop); //int mdstart = mapx(dragstartx); QRect r(0, 0, 0, height()); if (x < dragstartx) { if(x < selectionStart) { r.setLeft(mx); r.setWidth((selectionStop >= dragstartx ? mstop : mstart) - mx); } else { r.setLeft(mstart); r.setWidth(mx - mstart); } selectionStart = x; selectionStop = dragstartx; } else { if(x >= selectionStop) { r.setLeft(selectionStart < dragstartx ? mstart : mstop); r.setWidth(mx - (selectionStart < dragstartx ? mstart : mstop)); } else { r.setLeft(mx); r.setWidth(mstop - mx); } selectionStart = dragstartx; selectionStop = x; } update(r); } break; case Qt::MidButton: break; case Qt::RightButton: break; default: return; } } break; } } //--------------------------------------------------------- // pitch2y //--------------------------------------------------------- int WaveCanvas::pitch2y(int) const { return 0; } //--------------------------------------------------------- // y2pitch //--------------------------------------------------------- int WaveCanvas::y2pitch(int) const { return 0; } //--------------------------------------------------------- // drawItem // draws a wave //--------------------------------------------------------- void WaveCanvas::drawItem(QPainter& p, const CItem* item, const QRect& mr, const QRegion&) { MusECore::WavePart* wp = (MusECore::WavePart*)(item->part()); if(!wp || !wp->track()) return; MusECore::Event event = item->event(); if(event.empty()) return; //QRect rr = p.transform().mapRect(rect); // Gives inconsistent positions. Source shows wrong operation for our needs. const QRect ur = mapDev(mr); // Use our own map instead. const int ux = ur.x(); const int uw = ur.width(); const int ux_2 = ux + uw; QRect uwpr = QRect(wp->frame(), 0, wp->lenFrame(), height()); const QRect ubbr = item->bbox(); const QRect ubbr_exp = item->bbox().adjusted(0, 0, rmapxDev(1), 0); const QRect mbbr = map(ubbr); const int ubbx = ubbr.x(); const int ubbx_2 = ubbr.x() + ubbr.width(); const int mbbx = mbbr.x(); const int mbbx_2 = mapx(ubbr.x() + ubbr.width()); const QRect ubr = ur & ubbr; const QRect ubr_exp = ur & ubbr_exp; const int uby_exp = ubr_exp.y(); const int uby_2exp = ubr_exp.y() + ubr_exp.height(); const int mby_exp = mapy(uby_exp); const int mby_2exp = mapy(uby_2exp); const QRect ubrwp = ubr & uwpr; const QRect mbrwp = map(ubrwp); QPen pen; pen.setCosmetic(true); const QColor left_ch_color(0, 170, 255); const QColor right_ch_color(Qt::red); int x1 = mapx(ubrwp.x()); int x2 = mapx(ubrwp.x() + ubrwp.width()); if (x1 < 0) x1 = 0; if (x2 > width()) x2 = width(); int hh = height(); int y1 = mapy(ubrwp.y()); int y2 = mapy(ubrwp.y() + ubrwp.height()); int xScale = xmag; if (xScale < 0) xScale = -xScale; int px = wp->frame(); bool wmtxen = p.worldMatrixEnabled(); p.setWorldMatrixEnabled(false); int sx, ex; // Changed. Possible BUG ? Why the half nudge? // sx = event.frame() + px + xScale/2; sx = event.frame() + px; ex = sx + event.lenFrame(); sx = sx / xScale - xpos - xorg; ex = ex / xScale - xpos - xorg; if(sx >= x2 || ex < x1) return; if (sx < x1) sx = x1; if (ex > x2) ex = x2; const int ev_spos = event.spos(); int pos = (xpos + xorg + sx) * xScale - event.frame() - px; // fprintf(stderr, "\nWaveCanvas::drawItem:\nmr:\nx:%8d\t\ty:%8d\t\tw:%8d\t\th:%8d\n\n", // mr.x(), mr.y(), mr.width(), mr.height()); // fprintf(stderr, "\nur:\nx:%8d\t\ty:%8d\t\tw:%8d\t\th:%8d\n\n", // ur.x(), ur.y(), ur.width(), ur.height()); // fprintf(stderr, "\nubbr:\nx:%8d\t\ty:%8d\t\tw:%8d\t\th:%8d\n\n", // ubbr.x(), ubbr.y(), ubbr.width(), ubbr.height()); // fprintf(stderr, "\nmbbr:\nx:%8d\t\ty:%8d\t\tw:%8d\t\th:%8d\n\n", // mbbr.x(), mbbr.y(), mbbr.width(), mbbr.height()); QBrush brush; if (item->isMoving()) { QColor c(Qt::gray); c.setAlpha(MusEGlobal::config.globalAlphaBlend); QLinearGradient gradient(ubbr.topLeft(), ubbr.bottomLeft()); gradient.setColorAt(0, c); gradient.setColorAt(1, c.darker()); brush = QBrush(gradient); p.fillRect(sx, y1, ex - sx + 1, y2, brush); } else if (item->isSelected()) { QColor c(Qt::black); c.setAlpha(MusEGlobal::config.globalAlphaBlend); QLinearGradient gradient(ubbr.topLeft(), ubbr.bottomLeft()); // Use a colour only about 20% lighter than black, rather than the 50% we use in MusECore::gGradientFromQColor // and is used in darker()/lighter(), so that it is distinguished a bit better from grey non-part tracks. gradient.setColorAt(0, QColor(51, 51, 51, MusEGlobal::config.globalAlphaBlend)); gradient.setColorAt(1, c); brush = QBrush(gradient); p.fillRect(sx, y1, ex - sx + 1, y2, brush); } int ev_channels = 0; int wav_sx = 0; int wav_ex = 0; int wsx = 0; int wex = 0; bool wave_visible = false; MusECore::SndFileR f = event.sndFile(); if(!f.isNull()) { ev_channels = f.channels(); if (ev_channels > 0) { int h = hh / (ev_channels * 2); int cc = hh % (ev_channels * 2) ? 0 : 1; unsigned peoffset = px + event.frame() - ev_spos; const sf_count_t smps = f.samples(); if(-ev_spos < smps && ev_spos <= smps) { wave_visible = true; wav_sx = -ev_spos; wav_ex = smps - ev_spos; if(wav_sx < 0) wav_sx = 0; wav_sx += event.frame() + wp->frame(); wav_ex = f.unConvertPosition(wav_ex); if(wav_ex >= (int)event.lenFrame()) { wav_ex = event.lenFrame(); if(wav_ex > 0) --wav_ex; } wav_ex += event.frame() + wp->frame(); wav_sx = wav_sx / xScale - xpos - xorg; wav_ex = wav_ex / xScale - xpos - xorg; wsx = wav_sx < x1 ? x1 : wav_sx; wex = wav_ex > x2 ? x2 : wav_ex; } // fprintf(stderr, "WaveCanvas::drawItem: rect x:%d w:%d rr x:%d w:%d mr x:%d w:%d pos:%d sx:%d ex:%d\n", // rect.x(), rect.width(), // rr.x(), rr.width(), // mr.x(), mr.width(), // pos, // sx, ex); for (int i = sx; i < ex; i++) { int y = h; MusECore::SampleV sa[f.channels()]; if((ev_spos + f.convertPosition(pos)) > smps) break; // Seek the file only once, not with every read! if(i == sx) { if(f.seekUIConverted(pos, SEEK_SET | SFM_READ, ev_spos) == -1) break; } f.readConverted(sa, xScale, pos, ev_spos); pos += xScale; if (pos < 0) continue; int selectionStartPos = selectionStart - peoffset; // Offset transformed to event coords int selectionStopPos = selectionStop - peoffset; for (int k = 0; k < ev_channels; ++k) { int kk = k % f.channels(); int peak = (sa[kk].peak * (h - 1)) / yScale; int rms = (sa[kk].rms * (h - 1)) / yScale; if (peak > h) peak = h; if (rms > h) rms = h; QColor peak_color = MusEGlobal::config.wavePeakColor; QColor rms_color = MusEGlobal::config.waveRmsColor; if ((ev_spos + pos) > selectionStartPos && (ev_spos + pos) <= selectionStopPos) { peak_color = MusEGlobal::config.wavePeakColorSelected; rms_color = MusEGlobal::config.waveRmsColorSelected; QLine l_inv = clipQLine(i, y - h + cc, i, y + h - cc, mbrwp); if(!l_inv.isNull()) { // Draw inverted pen.setColor(QColor(Qt::black)); p.setPen(pen); p.drawLine(l_inv); } } QLine l_peak = clipQLine(i, y - peak - cc, i, y + peak, mbrwp); if(!l_peak.isNull()) { pen.setColor(peak_color); p.setPen(pen); p.drawLine(l_peak); } QLine l_rms = clipQLine(i, y - rms - cc, i, y + rms, mbrwp); if(!l_rms.isNull()) { pen.setColor(rms_color); p.setPen(pen); p.drawLine(l_rms); } y += 2 * h; } } // Only if there's something to draw. if(wave_visible && wsx <= wex && wsx < x2 && wex >= x1) { const int hn = hh / ev_channels; const int hhn = hn / 2; for (int i = 0; i < ev_channels; ++i) { const int h2 = hn * i; const int center = hhn + h2; if(center >= y1 && center < y2) { pen.setColor(QColor(i & 1 ? right_ch_color : left_ch_color)); p.setPen(pen); p.drawLine(wsx, center, wex, center); } } } } } const int h2 = hh / 2; if(h2 >= y1 && h2 < y2) { pen.setColor(QColor(Qt::black)); p.setPen(pen); // Draw the complete line only if there are an even number of channels (space for the line in the middle). // Ensure a complete line is drawn even if there is no sound file or channels. if((ev_channels & 1) == 0) p.drawLine(sx, h2, ex, h2); else { // Draw only the required two segments of the line. if(wave_visible) { if(sx < wsx) p.drawLine(sx, h2, wsx - 1, h2); if(wex < ex) p.drawLine(wex + 1, h2, ex, h2); } } } // // Draw custom dashed borders around the wave event // QColor color(item->isSelected() ? Qt::white : Qt::black); QPen penH(color); QPen penV(color); penH.setCosmetic(true); penV.setCosmetic(true); QVector customDashPattern; customDashPattern << 4.0 << 6.0; penH.setDashPattern(customDashPattern); penV.setDashPattern(customDashPattern); penV.setDashOffset(2.0); // FIXME: Some shifting still going on. Values likely not quite right here. //int xdiff = sx - r.x(); int xdiff = sx - mbbx; if(xdiff > 0) { int doff = xdiff % 10; penH.setDashOffset(doff); } // Tested OK. Each segment drawn only when necessary. if(y1 <= 0) { p.setPen(penH); p.drawLine(sx, 0, ex, 0); } if(y2 >= hh - 1) { p.setPen(penH); p.drawLine(sx, hh - 1, ex, hh - 1); } //fprintf(stderr, "...Checking left edge: ubbx:%d ux:%d ux_2:%d\n", ubbx, ux, ux_2); if(ubbx >= ux && ubbx < ux_2) { //fprintf(stderr, "...Drawing left edge at mbbx:%d mby_exp:%d mby_2exp:%d\n", mbbx, mby_exp, mby_2exp); p.setPen(penV); p.drawLine(mbbx, mby_exp, mbbx, mby_2exp); } //fprintf(stderr, "...Checking right edge: ubbx_2:%d ux:%d ux_2:%d\n", ubbx_2, ux, ux_2); if(ubbx_2 >= ux && ubbx_2 < ux_2) { //fprintf(stderr, "...Drawing right edge at mbbx_2:%d mby_exp:%d mby_2exp:%d\n", mbbx_2, mby_exp, mby_2exp); p.setPen(penV); p.drawLine(mbbx_2, mby_exp, mbbx_2, mby_2exp); } // Done. Restore and return. p.setWorldMatrixEnabled(wmtxen); } //--------------------------------------------------------- // drawTopItem //--------------------------------------------------------- void WaveCanvas::drawTopItem(QPainter& p, const QRect& rect, const QRegion&) { // TODO TODO: Convert this routine to new drawing system pulled from master? 20190121 QRect mr = map(rect); DEBUG_WAVECANVAS(stderr, "WaveCanvas::drawTopItem: rect.x:%d rect.w:%d mr.x:%d mr.w:%d\n", rect.x(), rect.width(), mr.x(), mr.width()); p.save(); p.setWorldMatrixEnabled(false); for(MusEGui::ciCItem i = items.begin(); i != items.end(); ++i) { //if(!(i->second->isSelected())) // continue; WEvent* e = static_cast(i->second); drawStretchAutomation(p, mr, e); } p.restore(); } //--------------------------------------------------------- // drawStretchAutomation //--------------------------------------------------------- void WaveCanvas::drawStretchAutomation(QPainter& p, const QRect& rr, WEvent* item) const { const MusECore::Event event = item->event(); if(event.type() != MusECore::Wave) return; const MusECore::SndFileR sf = event.sndFile(); if(sf.isNull()) return; const MusECore::StretchList* sl = sf.stretchList(); if(!sl) return; //const bool wave_sr_differs = sf.sampleRateDiffers(); const double wave_sr_ratio = sf.sampleRateRatio(); p.setBrush(Qt::NoBrush); QColor c; QPen pen; int xpixel; QVector pattern; pattern << 4 << 4; const StretchSelectedList_t& ssl = _stretchAutomation._stretchSelectedList; ciStretchSelectedItemPair res; for(MusECore::ciStretchListItem is = sl->begin(); is != sl->end(); ++is) { // Do not recognize or draw the item at zeroth frame. if(is->first == 0) continue; const MusECore::StretchListItem& sli = is->second; //if((_tool == StretchTool && (sli._type & MusECore::StretchListItem::StretchEvent)) || // (_tool == SamplerateTool && (sli._type & MusECore::StretchListItem::SamplerateEvent))) { xpixel = mapx(sl->squish((double)is->first) / wave_sr_ratio + item->x()); DEBUG_WAVECANVAS(stderr, "drawStretchAutomation: rr.x:%d rr.w:%d xpixel:%d\n", rr.x(), rr.width(), xpixel); if(sli._type & MusECore::StretchListItem::StretchEvent) { //if(_tool == StretchTool) c = Qt::magenta; //else // c = Qt::darkMagenta; res = ssl.equal_range(is->first); // FIXME Calls non-constant version? Want constant version. for(ciStretchSelectedItem ise = res.first; ise != res.second; ++ise) { if(ise->first == is->first && ise->second._sndFile.stretchList() == sl && ise->second._type == MusECore::StretchListItem::StretchEvent) { c = Qt::white; break; } } //c.setAlpha(200); pen.setColor(c); pen.setDashPattern(pattern); p.setPen(pen); p.drawLine(xpixel, rr.top() - 2, xpixel, rr.bottom() - 2); } if(sli._type & MusECore::StretchListItem::SamplerateEvent) { //if(_tool == SamplerateTool) c = Qt::cyan; //else // c = Qt::darkCyan; res = ssl.equal_range(is->first); // FIXME Calls non-constant version? Want constant version. for(ciStretchSelectedItem ise = res.first; ise != res.second; ++ise) { if(ise->first == is->first && ise->second._sndFile.stretchList() == sl && ise->second._type == MusECore::StretchListItem::SamplerateEvent) { c = Qt::white; break; } } //c.setAlpha(200); pen.setColor(c); pen.setDashPattern(pattern); // Offset to help distinguish from stretch lines. pen.setDashOffset(4.0); p.setPen(pen); // Draw reverse direction to help distinguish from stretch lines. p.drawLine(xpixel, rr.bottom() - 2, xpixel, rr.top() - 2); } } } } MusECore::iStretchListItem WaveCanvas::stretchListHitTest(int types, QPoint pt, WEvent* wevent) { const MusECore::Event event = wevent->event(); if(event.type() != MusECore::Wave) return MusECore::iStretchListItem(); const MusECore::SndFileR sf = event.sndFile(); if(sf.isNull()) return MusECore::iStretchListItem(); MusECore::StretchList* stretchList = sf.stretchList(); if(!stretchList) return MusECore::iStretchListItem(); //const bool wave_sr_differs = sf.sampleRateDiffers(); const double wave_sr_ratio = sf.sampleRateRatio(); const int pt_x = pt.x(); const int wevent_x = wevent->x(); int closest_dist = _stretchAutomationPointDetectDist; MusECore::iStretchListItem closest_ev = stretchList->end(); for(MusECore::iStretchListItem is = stretchList->begin(); is != stretchList->end(); ++is) { // Do not recognize or draw the item at zeroth frame. if(is->first == 0) continue; const MusECore::StretchListItem& se = is->second; if(!(se._type & types)) continue; const double newSqFrame = se._finSquishedFrame / wave_sr_ratio; const int xpixel = mapx(newSqFrame + wevent_x); const int pt_pixel = mapx(pt_x); const int x_diff = (xpixel > pt_pixel) ? (xpixel - pt_pixel) : (pt_pixel - xpixel); if(x_diff <= closest_dist) { closest_dist = x_diff; closest_ev = is; } } return closest_ev; } void WaveCanvas::setStretchAutomationCursor(QPoint pt) { if(_tool != StretchTool && _tool != SamplerateTool) return; CItem* item = items.find(pt); if(!item) { setCursor(); return; } WEvent* wevent = static_cast(item); const MusECore::Event event = wevent->event(); if(event.type() != MusECore::Wave) { setCursor(); return; } MusECore::SndFileR sf = event.sndFile(); if(sf.isNull()) { setCursor(); return; } MusECore::StretchList* sl = sf.stretchList(); if(!sl) { setCursor(); return; } MusECore::StretchListItem::StretchEventType type; if(_tool == StretchTool) type = MusECore::StretchListItem::StretchEvent; else // if(_tool == SamplerateTool) type = MusECore::StretchListItem::SamplerateEvent; MusECore::iStretchListItem isli_hit_test = stretchListHitTest(type, pt, wevent); if(isli_hit_test == sl->end()) setCursor(); else QWidget::setCursor(Qt::SizeHorCursor); } //--------------------------------------------------------- // drawMoving // draws moving items //--------------------------------------------------------- void WaveCanvas::drawMoving(QPainter& p, const CItem* item, const QRect& mr, const QRegion&) { const QRect ur = mapDev(mr); QRect ur_item = QRect(item->mp().x(), item->mp().y(), item->width(), item->height()); ur_item = ur_item.intersected(ur); if(!ur_item.isValid()) return; QPen pen; pen.setCosmetic(true); pen.setColor(Qt::black); p.setPen(pen); p.setBrush(QColor(0, 128, 0, 128)); // TODO: Pick a better colour, or use part colours, or grey? p.drawRect(ur_item); } //--------------------------------------------------------- // viewMouseDoubleClickEvent //--------------------------------------------------------- void WaveCanvas::viewMouseDoubleClickEvent(QMouseEvent* event) { if ((_tool != PointerTool) && (event->button() != Qt::LeftButton)) { mousePress(event); return; } } //--------------------------------------------------------- // moveCanvasItems //--------------------------------------------------------- MusECore::Undo WaveCanvas::moveCanvasItems(CItemMap& items, int /*dp*/, int dx, DragType dtype, bool rasterize) { if(editor->parts()->empty()) return MusECore::Undo(); //return empty list MusECore::PartsToChangeMap parts2change; MusECore::Undo operations; for(MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { MusECore::Part* part = ip->second; if(!part) continue; int npartoffset = 0; for(iCItem ici = items.begin(); ici != items.end(); ++ici) { CItem* ci = ici->second; ci->setMoving(false); if(ci->part() != part) continue; int x = ci->pos().x() + dx; int y = 0; QPoint newpos = QPoint(x, y); if(rasterize) newpos = raster(newpos); // Test moving the item... WEvent* wevent = (WEvent*) ci; MusECore::Event event = wevent->event(); x = newpos.x(); if(x < 0) x = 0; // Normally frame to tick methods round down. But here we need it to 'snap' // the frame from either side of a tick to the tick. So round to nearest. int nframe = (rasterize ? MusEGlobal::tempomap.tick2frame( editor->rasterVal(MusEGlobal::tempomap.frame2tick(x, 0, MusECore::LargeIntRoundNearest))) : x) - part->frame(); if(nframe < 0) nframe = 0; int diff = nframe + event.lenFrame() - part->lenFrame(); // If moving the item would require a new part size... if(diff > npartoffset) npartoffset = diff; } if(npartoffset > 0) { MusECore::iPartToChange ip2c = parts2change.find(part); if(ip2c == parts2change.end()) { MusECore::PartToChange p2c = {0, npartoffset}; parts2change.insert(std::pair (part, p2c)); } else ip2c->second.xdiff = npartoffset; } } bool forbidden=false; for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c) { MusECore::Part* opart = ip2c->first; if (opart->hasHiddenEvents() & MusECore::Part::RightEventsHidden) { forbidden=true; break; } } if (!forbidden) { std::vector< CItem* > doneList; typedef std::vector< CItem* >::iterator iDoneList; for(iCItem ici = items.begin(); ici != items.end(); ++ici) { CItem* ci = ici->second; int x = ci->pos().x(); int nx = x + dx; int ny = 0; QPoint newpos = QPoint(nx, ny); if(rasterize) newpos = raster(newpos); selectItem(ci, true); iDoneList idl; for(idl = doneList.begin(); idl != doneList.end(); ++idl) // This compares EventBase pointers to see if they're the same... if((*idl)->event() == ci->event()) break; // Do not process if the event has already been processed (meaning it's an event in a clone part)... if (idl == doneList.end()) { moveItem(operations, ci, newpos, dtype, rasterize); // always returns true. if not, change is necessary here! doneList.push_back(ci); } ci->move(newpos); if(moving.size() == 1) itemReleased(curItem, newpos); if(dtype == MOVE_COPY || dtype == MOVE_CLONE) selectItem(ci, false); } for(MusECore::iPartToChange ip2c = parts2change.begin(); ip2c != parts2change.end(); ++ip2c) { MusECore::Part* opart = ip2c->first; int diff = ip2c->second.xdiff; //schedule_resize_all_same_len_clone_parts(opart, opart->lenTick() + diff, operations); schedule_resize_all_same_len_clone_parts(opart, opart->lenFrame() + diff, operations); } return operations; } else { return MusECore::Undo(); //return empty list } } //--------------------------------------------------------- // moveItem // called after moving an object //--------------------------------------------------------- bool WaveCanvas::moveItem(MusECore::Undo& operations, CItem* item, const QPoint& pos, DragType dtype, bool rasterize) { WEvent* wevent = (WEvent*) item; MusECore::Event event = wevent->event(); MusECore::Event newEvent = event.clone(); int x = pos.x(); if (x < 0) x = 0; MusECore::Part* part = wevent->part(); // Normally frame to tick methods round down. But here we need it to 'snap' // the frame from either side of a tick to the tick. So round to nearest. int nframe = (rasterize ? MusEGlobal::tempomap.tick2frame( editor->rasterVal(MusEGlobal::tempomap.frame2tick(x, 0, MusECore::LargeIntRoundNearest))) : x) - part->frame(); if (nframe < 0) nframe = 0; newEvent.setFrame(nframe); newEvent.setLenFrame(event.lenFrame()); // don't check, whether the new event is within the part // at this place. with operation groups, the part isn't // resized yet. (flo93) if (dtype == MOVE_COPY || dtype == MOVE_CLONE) operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent, newEvent, part, false, false)); else operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); return true; } //--------------------------------------------------------- // newItem(p, state) //--------------------------------------------------------- CItem* WaveCanvas::newItem(const QPoint& p, int key_modifiers) { int frame = p.x(); if(frame < 0) frame = 0; // Normally frame to tick methods round down. But here we need it to 'snap' // the frame from either side of a tick to the tick. So round to nearest. if(!(key_modifiers & Qt::ShiftModifier)) frame = MusEGlobal::tempomap.tick2frame( editor->rasterVal1(MusEGlobal::tempomap.frame2tick(frame, 0, MusECore::LargeIntRoundNearest))); int len = p.x() - frame; frame -= curPart->frame(); if (frame < 0) return 0; MusECore::Event e = MusECore::Event(MusECore::Wave); e.setFrame(frame); e.setLenFrame(len); WEvent* we = new WEvent(e, curPart, height()); return we; } void WaveCanvas::newItem(CItem* item, bool noSnap) { WEvent* wevent = (WEvent*) item; MusECore::Event event = wevent->event(); MusECore::Part* part = wevent->part(); int pframe = part->frame(); int x = item->x(); if (xwidth(); if (!noSnap) { // Normally frame to tick methods round down. But here we need it to 'snap' // the frame from either side of a tick to the tick. So round to nearest. x = MusEGlobal::tempomap.tick2frame( editor->rasterVal1(MusEGlobal::tempomap.frame2tick(x, 0, MusECore::LargeIntRoundNearest))); w = MusEGlobal::tempomap.tick2frame( editor->rasterVal(MusEGlobal::tempomap.frame2tick(x + w, 0, MusECore::LargeIntRoundNearest))) - x; if (w == 0) w = MusEGlobal::tempomap.tick2frame(editor->raster()); } if (xlenFrame(); if (! ((diff > 0) && (part->hasHiddenEvents() & MusECore::Part::RightEventsHidden)) ) //operation is allowed { operations.push_back(MusECore::UndoOp(MusECore::UndoOp::AddEvent,event, part, false, false)); if (diff > 0)// part must be extended? { schedule_resize_all_same_len_clone_parts(part, event.endFrame(), operations); printf("newItem: extending\n"); } MusEGlobal::song->applyOperationGroup(operations); } else // forbid action by not applying it songChanged(SC_EVENT_INSERTED); //this forces an update of the itemlist, which is necessary //to remove "forbidden" events from the list again } //--------------------------------------------------------- // resizeItem //--------------------------------------------------------- void WaveCanvas::resizeItem(CItem* item, bool noSnap, bool) // experimental changes to try dynamically extending parts { WEvent* wevent = (WEvent*) item; MusECore::Event event = wevent->event(); MusECore::Event newEvent = event.clone(); int len; MusECore::Part* part = wevent->part(); if (noSnap) len = wevent->width(); else { unsigned frame = event.frame() + part->frame(); // Normally frame to tick methods round down. But here we need it to 'snap' // the frame from either side of a tick to the tick. So round to nearest. len = MusEGlobal::tempomap.tick2frame( editor->rasterVal(MusEGlobal::tempomap.frame2tick( frame + wevent->width(), 0, MusECore::LargeIntRoundNearest))) - frame; if (len <= 0) len = MusEGlobal::tempomap.tick2frame(editor->raster()); } MusECore::Undo operations; int diff = event.frame() + len - part->lenFrame(); if (resizeDirection == MusECore::ResizeDirection::RESIZE_TO_THE_LEFT) { int x = qMax(0, wevent->x()); int nframe = qMax(0u, x - part->frame()); newEvent.setFrame(nframe); } if (! ((diff > 0) && (part->hasHiddenEvents() & MusECore::Part::RightEventsHidden)) ) //operation is allowed { newEvent.setLenFrame(len); operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent,newEvent, event, wevent->part(), false, false)); if (diff > 0)// part must be extended? { //schedule_resize_all_same_len_clone_parts(part, event.tick()+len, operations); schedule_resize_all_same_len_clone_parts(part, event.frame() + len, operations); printf("resizeItem: extending\n"); } } //else forbid action by not performing it MusEGlobal::song->applyOperationGroup(operations); songChanged(SC_EVENT_MODIFIED); //this forces an update of the itemlist, which is necessary //to remove "forbidden" events from the list again } //--------------------------------------------------------- // deleteItem //--------------------------------------------------------- bool WaveCanvas::deleteItem(CItem* item) { WEvent* wevent = (WEvent*) item; if (wevent->part() == curPart) { MusECore::Event ev = wevent->event(); // Indicate do undo, and do not do port controller values and clone parts. MusEGlobal::song->applyOperation(MusECore::UndoOp(MusECore::UndoOp::DeleteEvent, ev, curPart, false, false)); return true; } return false; } //--------------------------------------------------------- // adjustWaveOffset //--------------------------------------------------------- void WaveCanvas::adjustWaveOffset() { bool have_selected = false; int init_offset = 0; for (iCItem k = items.begin(); k != items.end(); ++k) { if (k->second->isSelected()) { have_selected = true; init_offset = k->second->event().spos(); break; } } if(!have_selected) { QMessageBox::information(this, QString("MusE"), QWidget::tr("No wave events selected.")); return; } bool ok = false; int offset = QInputDialog::getInt(this, tr("Adjust Wave Offset"), tr("Wave offset (frames)"), init_offset, 0, 2147483647, 1, &ok); if(!ok) return; MusECore::Undo operations; // FIXME: Respect clones! If operating on two selected clones of the same part, an extra event is created! // Check - Is it really this code's problem? Seems so, other operations like moving an event seem OK. for(iCItem ici = items.begin(); ici != items.end(); ++ici) { if(ici->second->isSelected()) { MusECore::Event oldEvent = ici->second->event(); if(oldEvent.spos() != offset) { MusECore::Part* part = ici->second->part(); MusECore::Event newEvent = oldEvent.clone(); newEvent.setSpos(offset); // Do not do port controller values and clone parts. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, oldEvent, part, false, false)); } } } MusEGlobal::song->applyOperationGroup(operations); redraw(); } //--------------------------------------------------------- // draw //--------------------------------------------------------- void WaveCanvas::drawCanvas(QPainter& p, const QRect& rect, const QRegion& rg) { if (MusEGlobal::config.canvasShowGrid) { //--------------------------------------------------- // vertical lines //--------------------------------------------------- drawTickRaster(p, rect, rg, editor->raster(), true, false, false, MusEGlobal::config.midiCanvasBeatColor, // color sequence slightly done by trial and error.. MusEGlobal::config.midiCanvasBeatColor, MusEGlobal::config.midiCanvasFineColor, MusEGlobal::config.midiCanvasBarColor ); } } //--------------------------------------------------------- // waveCmd //--------------------------------------------------------- void WaveCanvas::waveCmd(int cmd) { // TODO: New WaveCanvas: Convert this routine to frames. switch(cmd) { case CMD_LEFT: { int spos = pos[0]; if(spos > 0) { spos -= 1; // Nudge by -1, then snap down with raster1. spos = MusEGlobal::sigmap.raster1(spos, editor->rasterStep(pos[0])); } if(spos < 0) spos = 0; MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); } break; case CMD_RIGHT: { int spos = MusEGlobal::sigmap.raster2(pos[0] + 1, editor->rasterStep(pos[0])); // Nudge by +1, then snap up with raster2. MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); } break; case CMD_LEFT_NOSNAP: { int spos = pos[0] - editor->rasterStep(pos[0]); if (spos < 0) spos = 0; MusECore::Pos p(spos,true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW } break; case CMD_RIGHT_NOSNAP: { MusECore::Pos p(pos[0] + editor->rasterStep(pos[0]), true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, true, true); //CDW } break; case CMD_INSERT: { if (pos[0] < start() || pos[0] >= end()) break; MusECore::MidiPart* part = (MusECore::MidiPart*)curPart; if (part == 0) break; const MusECore::EventList& el = part->events(); MusECore::Undo operations; std::list elist; for (MusECore::ciEvent e = el.lower_bound(pos[0] - part->tick()); e != el.end(); ++e) elist.push_back((MusECore::Event)e->second); for (std::list::iterator i = elist.begin(); i != elist.end(); ++i) { MusECore::Event event = *i; MusECore::Event newEvent = event.clone(); newEvent.setTick(event.tick() + editor->raster());// - part->tick()); DELETETHIS // Do not do port controller values and clone parts. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); } MusEGlobal::song->applyOperationGroup(operations); MusECore::Pos p(editor->rasterVal(pos[0] + editor->rasterStep(pos[0])), true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, false, true); } return; case CMD_BACKSPACE: if (pos[0] < start() || pos[0] >= end()) break; { MusECore::MidiPart* part = (MusECore::MidiPart*)curPart; if (part == 0) break; MusECore::Undo operations; const MusECore::EventList& el = part->events(); std::list elist; for (MusECore::ciEvent e = el.lower_bound(pos[0]); e != el.end(); ++e) elist.push_back((MusECore::Event)e->second); for (std::list::iterator i = elist.begin(); i != elist.end(); ++i) { MusECore::Event event = *i; MusECore::Event newEvent = event.clone(); newEvent.setTick(event.tick() - editor->raster() - part->tick()); // Do not do port controller values and clone parts. operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); } MusEGlobal::song->applyOperationGroup(operations); MusECore::Pos p(editor->rasterVal(pos[0] - editor->rasterStep(pos[0])), true); MusEGlobal::song->setPos(MusECore::Song::CPOS, p, true, false, true); } break; } } //--------------------------------------------------------- // cmd // pulldown menu commands //--------------------------------------------------------- void WaveCanvas::cmd(int cmd) { int modifyoperation = -1; double paramA = 0.0; switch (cmd) { case CMD_SELECT_ALL: // select all if (tool() == RangeTool) { if (!editor->parts()->empty()) { MusECore::iPart iBeg = editor->parts()->begin(); MusECore::iPart iEnd = editor->parts()->end(); iEnd--; MusECore::WavePart* beg = (MusECore::WavePart*) iBeg->second; MusECore::WavePart* end = (MusECore::WavePart*) iEnd->second; selectionStart = beg->frame(); selectionStop = end->frame() + end->lenFrame(); redraw(); } } for (iCItem k = items.begin(); k != items.end(); ++k) { if (!k->second->isSelected()) selectItem(k->second, true); } break; case CMD_SELECT_NONE: // select none selectionStart = selectionStop = 0; deselectAll(); break; case CMD_SELECT_INVERT: // invert selection for (iCItem k = items.begin(); k != items.end(); ++k) { selectItem(k->second, !k->second->isSelected()); } break; case CMD_SELECT_ILOOP: // select inside loop for (iCItem k = items.begin(); k != items.end(); ++k) { WEvent* wevent = (WEvent*)(k->second); MusECore::Part* part = wevent->part(); MusECore::Event event = wevent->event(); unsigned tick = event.tick() + part->tick(); if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos()) selectItem(k->second, false); else selectItem(k->second, true); } break; case CMD_SELECT_OLOOP: // select outside loop for (iCItem k = items.begin(); k != items.end(); ++k) { WEvent* wevent = (WEvent*)(k->second); MusECore::Part* part = wevent->part(); MusECore::Event event = wevent->event(); unsigned tick = event.tick() + part->tick(); if (tick < MusEGlobal::song->lpos() || tick >= MusEGlobal::song->rpos()) selectItem(k->second, true); else selectItem(k->second, false); } break; case CMD_RANGE_TO_SELECTION: setRangeToSelection(); break; case CMD_SELECT_PREV_PART: // select previous part { MusECore::Part* pt = editor->curCanvasPart(); MusECore::Part* newpt = pt; MusECore::PartList* pl = editor->parts(); for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) if(ip->second == pt) { if(ip == pl->begin()) ip = pl->end(); --ip; newpt = ip->second; break; } if(newpt != pt) editor->setCurCanvasPart(newpt); } break; case CMD_SELECT_NEXT_PART: // select next part { MusECore::Part* pt = editor->curCanvasPart(); MusECore::Part* newpt = pt; MusECore::PartList* pl = editor->parts(); for(MusECore::iPart ip = pl->begin(); ip != pl->end(); ++ip) if(ip->second == pt) { ++ip; if(ip == pl->end()) ip = pl->begin(); newpt = ip->second; break; } if(newpt != pt) editor->setCurCanvasPart(newpt); } break; case CMD_ADJUST_WAVE_OFFSET: adjustWaveOffset(); break; case CMD_EDIT_EXTERNAL: modifyoperation = EDIT_EXTERNAL; break; case CMD_EDIT_COPY: modifyoperation = COPY; break; case CMD_EDIT_CUT: modifyoperation = CUT; break; case CMD_EDIT_PASTE: modifyoperation = PASTE; break; case CMD_MUTE: modifyoperation = MUTE; break; case CMD_NORMALIZE: modifyoperation = NORMALIZE; break; case CMD_FADE_IN: modifyoperation = FADE_IN; break; case CMD_FADE_OUT: modifyoperation = FADE_OUT; break; case CMD_REVERSE: modifyoperation = REVERSE; break; case CMD_GAIN_FREE: { EditGain* editGain = new EditGain(this, lastGainvalue); if (editGain->exec() == QDialog::Accepted) { lastGainvalue = editGain->getGain(); modifyoperation = GAIN; paramA = (double)lastGainvalue / 100.0; } delete editGain; } break; case CMD_GAIN_200: modifyoperation = GAIN; paramA = 2.0; break; case CMD_GAIN_150: modifyoperation = GAIN; paramA = 1.5; break; case CMD_GAIN_75: modifyoperation = GAIN; paramA = 0.75; break; case CMD_GAIN_50: modifyoperation = GAIN; paramA = 0.5; break; case CMD_GAIN_25: modifyoperation = GAIN; paramA = 0.25; break; case CMD_CREATE_PART_REGION: { // create a new part and put in the copy buffer MusECore::Part* pt = editor->curCanvasPart(); if (pt == 0 || pt->track()->type() != MusECore::Track::WAVE) return; MusECore::WavePart *origPart = (MusECore::WavePart*)pt; if (MusEGlobal::song->lpos() < origPart->tick() || MusEGlobal::song->rpos() > origPart->endTick()) { QMessageBox::warning(this, tr("Part creation failed"), tr("Left and right position markers must be placed inside the current part."), QMessageBox::Ok, QMessageBox::Ok); return; } MusECore::WavePart *tempPart = new MusECore::WavePart(origPart->track()); unsigned origFrame = origPart->frame(); unsigned frameDistance = MusEGlobal::song->lPos().frame() - origFrame; tempPart->setPos(MusEGlobal::song->lpos()); tempPart->setLenTick(MusEGlobal::song->rpos() - MusEGlobal::song->lpos()); // loop through the events and set them accordingly for (MusECore::ciEvent iWaveEvent = origPart->events().begin(); iWaveEvent != origPart->events().end(); iWaveEvent++) { // TODO: handle multiple events correctly, // the math for subsequent events isn't correct const MusECore::Event& ev = iWaveEvent->second; MusECore::Event *newEvent = new MusECore::Event(ev.clone()); newEvent->setSpos(ev.spos() + frameDistance); newEvent->setLenTick(MusEGlobal::song->rpos() - MusEGlobal::song->lpos()); tempPart->addEvent(*newEvent); } std::set partList; partList.insert(tempPart); QMimeData *mimeData = MusECore::parts_to_mime(partList); QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); QMessageBox::information(this, tr("Part created"), tr("The selected region has been copied to the clipboard and can be pasted in the arranger."), QMessageBox::Ok, QMessageBox::Ok); } break; case CMD_ERASE_MEASURE: case CMD_DELETE_MEASURE: case CMD_CREATE_MEASURE: break; default: // printf("unknown ecanvas cmd %d\n", cmd); break; } if (modifyoperation != -1) { if (selectionStart == selectionStop && modifyoperation!=PASTE) { printf("No selection. Ignoring\n"); //@!TODO: Disable menu options when no selection QMessageBox::information(this, QString("MusE"), QWidget::tr("No selection. Ignoring")); return; } //if(!modifyWarnedYet) //{ // modifyWarnedYet = true; // if(QMessageBox::warning(this, QString("Muse"), // tr("Warning! Muse currently operates directly on the sound file.\n" // "Undo is supported, but NOT after exit, WITH OR WITHOUT A SAVE!"), tr("&Ok"), tr("&Cancel"), // QString::null, 0, 1 ) != 0) // return; //} modifySelection(modifyoperation, selectionStart, selectionStop, paramA); } itemSelectionsChanged(); redraw(); } //--------------------------------------------------------- // getSelection //--------------------------------------------------------- MusECore::WaveSelectionList WaveCanvas::getSelection(unsigned startpos, unsigned stoppos) { MusECore::WaveSelectionList selection; for (MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { MusECore::WavePart* wp = (MusECore::WavePart*)(ip->second); unsigned part_offset = wp->frame(); const MusECore::EventList& el = wp->events(); for (MusECore::ciEvent e = el.begin(); e != el.end(); ++e) { MusECore::Event event = e->second; if (event.empty()) continue; MusECore::SndFileR file = event.sndFile(); if (file.isNull()) continue; // Respect part end: Don't modify stuff outside of part boundary. unsigned elen = event.lenFrame(); if(event.frame() + event.lenFrame() >= wp->lenFrame()) { // Adjust apparent operation length: if(event.frame() > wp->lenFrame()) elen = 0; else elen = wp->lenFrame() - event.frame(); } unsigned event_offset = event.frame() + part_offset; unsigned event_startpos = event.spos(); unsigned event_length = elen + event.spos(); unsigned event_end = event_offset + event_length; //printf("startpos=%d stoppos=%d part_offset=%d event_offset=%d event_startpos=%d event_length=%d event_end=%d\n", // startpos, stoppos, part_offset, event_offset, event_startpos, event_length, event_end); if (!(event_end <= startpos || event_offset > stoppos)) { int tmp_sx = startpos - event_offset + event_startpos; int tmp_ex = stoppos - event_offset + event_startpos; unsigned sx; unsigned ex; tmp_sx < (int)event_startpos ? sx = event_startpos : sx = tmp_sx; tmp_ex > (int)event_length ? ex = event_length : ex = tmp_ex; //printf("Event data affected: %d->%d filename:%s\n", sx, ex, file.name().toLatin1().constData()); MusECore::WaveEventSelection s; s.event = event; s.startframe = sx; s.endframe = ex+1; //printf("sx=%d ex=%d\n",sx,ex); selection.push_back(s); } } } return selection; } //--------------------------------------------------------- // modifySelection //--------------------------------------------------------- void WaveCanvas::modifySelection(int operation, unsigned startpos, unsigned stoppos, double paramA) { if (operation == PASTE) { // we need to redefine startpos and stoppos if (copiedPart =="") return; MusECore::SndFile pasteFile(copiedPart); pasteFile.openRead(); startpos = pos[0]; stoppos = startpos+ pasteFile.samples(); // possibly this is wrong if there are tempo changes pasteFile.close(); pos[0]=stoppos; } // // Copy on Write: Check if some files need to be copied, either because they are not // writable, or more than one independent (non-clone) wave event shares a wave file. // MusECore::WaveSelectionList selection = getSelection(startpos, stoppos); std::vector copy_files_proj_dir; for(MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++) { MusECore::WaveEventSelection w = *i; if(w.event.empty()) continue; MusECore::SndFileR file = w.event.sndFile(); if(file.isNull()) continue; if(sndFileCheckCopyOnWrite(file)) { std::vector::iterator i = copy_files_proj_dir.begin(); for( ; i != copy_files_proj_dir.end(); ++i) { if(i->canonicalPath() == file.canonicalPath()) break; } if(i == copy_files_proj_dir.end()) copy_files_proj_dir.push_back(file); } } if(!copy_files_proj_dir.empty()) { CopyOnWriteDialog* dlg = new CopyOnWriteDialog(); for(std::vector::iterator i = copy_files_proj_dir.begin(); i != copy_files_proj_dir.end(); ++i) { qint64 sz = QFile(i->canonicalPath()).size(); QString s; if(sz > 1048576) s += QString::number(sz / 1048576) + "MB "; else if(sz > 1024) s += QString::number(sz / 1024) + "KB "; else s += QString::number(sz) + "B "; s += i->canonicalPath(); dlg->addProjDirFile(s); } int rv = dlg->exec(); delete dlg; if(rv != QDialog::Accepted) return; // Has a project been created yet? if(MusEGlobal::museProject == MusEGlobal::museProjectInitPath) // && MusEGlobal::config.useProjectSaveDialog { // No project, we need to create one. if(!MusEGlobal::muse->saveAs()) return; // No project, don't want to copy without one. //setFocus(); // For some reason focus is given away to Arranger } for(MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++) { MusECore::WaveEventSelection w = *i; if(w.event.empty()) continue; MusECore::SndFileR file = w.event.sndFile(); if(file.isNull() || !sndFileCheckCopyOnWrite(file)) // Make sure to re-check continue; QString filePath = MusEGlobal::museProject + QString("/") + file.name(); QString newFilePath; if(MusECore::getUniqueFileName(filePath, newFilePath)) { { QFile qf(file.canonicalPath()); if(!qf.copy(newFilePath)) // Copy the file { printf("MusE Error: Could not copy to new sound file (file exists?): %s\n", newFilePath.toLatin1().constData()); continue; // Let's not overwrite an existing file } } QFile nqf(newFilePath); // Need to make sure some permissions are set... QFile::Permissions pm = nqf.permissions(); if(!(pm & QFile::ReadOwner)) { pm |= QFile::ReadOwner; if(!nqf.setPermissions(pm)) { printf("MusE Error: Could not set read owner permissions on new sound file: %s\n", newFilePath.toLatin1().constData()); continue; } } if(!(pm & QFile::WriteOwner)) { pm |= QFile::WriteOwner; if(!nqf.setPermissions(pm)) { printf("MusE Error: Could not set write owner permissions on new sound file: %s\n", newFilePath.toLatin1().constData()); continue; } } if(!(pm & QFile::ReadUser)) { pm |= QFile::ReadUser; if(!nqf.setPermissions(pm)) { printf("MusE Error: Could not set read user permissions on new sound file: %s\n", newFilePath.toLatin1().constData()); continue; } } if(!(pm & QFile::WriteUser)) { pm |= QFile::WriteUser; if(!nqf.setPermissions(pm)) { printf("MusE Error: Could not set write user permissions on new sound file: %s\n", newFilePath.toLatin1().constData()); continue; } } MusECore::SndFile* newSF = new MusECore::SndFile(newFilePath); MusECore::SndFileR newSFR(newSF); // Create a sndFileR for the new file if(newSFR.openRead()) { printf("MusE Error: Could not open new sound file: %s\n", newSFR.canonicalPath().toLatin1().constData()); continue; // newSF will be deleted when newSFR goes out of scope and is deleted } MusEGlobal::audio->msgIdle(true); w.event.sndFile().close(); // Close the old file. // NOTE: For now, don't bother providing undo for this. Reason: If the user undoes // and then modifies again, it will prompt to create new copies each time. There is // no mechanism ("touched"?) to tell if an existing copy would be suitable to just 'drop in'. // It would help if we deleted the wave file copies upon undo, but not too crazy about that. // So since the copy has already been created and "there it is", we might as well use it. // It means that events and even undo items BEFORE this operation will point to this // NEW wave file (as if they always did). It also means the user CANNOT change back // to the old file... Oh well, this IS Copy On Write. // FIXME: Find a conceptual way to make undo work with or without deleting the copies. w.event.setSndFile(newSFR); // Set the new file. MusEGlobal::audio->msgIdle(false); } } } MusEGlobal::song->startUndo(); for (MusECore::iWaveSelection i = selection.begin(); i != selection.end(); i++) { MusECore::WaveEventSelection w = *i; if(w.event.empty()) continue; MusECore::SndFileR file = w.event.sndFile(); if(file.isNull()) continue; unsigned sx = w.startframe; unsigned ex = w.endframe; unsigned file_channels = file.channels(); QString tmpWavFile; if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", tmpWavFile)) { break; } MusEGlobal::audio->msgIdle(true); // Not good with playback during operations MusECore::SndFile tmpFile(tmpWavFile); tmpFile.setFormat(file.format(), file_channels, file.samplerate()); if (tmpFile.openWrite()) { MusEGlobal::audio->msgIdle(false); printf("Could not open temporary file...\n"); break; } // // Write out data that will be changed to temp file // unsigned tmpdatalen = ex - sx; off_t tmpdataoffset = sx; float* tmpdata[file_channels]; for (unsigned i=0; icmdChangeWave(w.event, tmpWavFile, sx, ex); MusEGlobal::audio->msgIdle(false); // Not good with playback during operations } MusEGlobal::song->endUndo(SC_CLIP_MODIFIED); redraw(); } //--------------------------------------------------------- // copySelection //--------------------------------------------------------- void WaveCanvas::copySelection(unsigned file_channels, float** tmpdata, unsigned length, bool blankData, unsigned format, unsigned sampleRate) { if (copiedPart!="") { QFile::remove(copiedPart); } if (!MusEGlobal::getUniqueTmpfileName("tmp_musewav",".wav", copiedPart)) { return; } MusECore::SndFile tmpFile(copiedPart); tmpFile.setFormat(format, file_channels, sampleRate); tmpFile.openWrite(); tmpFile.write(file_channels, tmpdata, length, MusEGlobal::config.liveWaveUpdate); tmpFile.close(); if (blankData) { // Set everything to 0! for (unsigned i=0; i loudest) loudest = data[i][j]; } } double scale = 0.99 / (double)loudest; for (unsigned i=0; iAudio:External Waveditor\nis set to a valid editor.")); } if(proc.exitStatus() != QProcess::NormalExit) { std::fprintf(stderr, "\nError: Launch external wave editor: Exit status: %d File: %s\n", proc.exitStatus(), MusEGlobal::config.externalWavEditor.toLatin1().constData()); } if(proc.exitCode() != 0) { std::fprintf(stderr, "\nError: Launch external wave editor: Exit code: %d File: %s\n", proc.exitCode(), MusEGlobal::config.externalWavEditor.toLatin1().constData()); } if (exttmpFile.openRead()) { printf("Could not reopen temporary file!\n"); } else { // Re-read file again exttmpFile.seek(0, 0); size_t sz = exttmpFile.readWithHeap(file_channels, tmpdata, tmpdatalen); if (sz != tmpdatalen) { // File must have been shrunken - not good. Alert user. QMessageBox::critical(this, tr("MusE - file size changed"), tr("When editing in external editor - you should not change the filesize\nsince it must fit the selected region.\n\nMissing data is muted")); for (unsigned i=0; iparts()), 1); if (md) { // "Note that setMimeData() assigns ownership of the QMimeData object to the QDrag object. // The QDrag must be constructed on the heap with a parent QWidget to ensure that Qt can // clean up after the drag and drop operation has been completed. " QDrag* drag = new QDrag(this); drag->setMimeData(md); if (t == MOVE_COPY || t == MOVE_CLONE) drag->exec(Qt::CopyAction); else drag->exec(Qt::MoveAction); } } //--------------------------------------------------------- // dragEnterEvent //--------------------------------------------------------- void WaveCanvas::dragEnterEvent(QDragEnterEvent* event) { //event->accept(Q3TextDrag::canDecode(event)); event->acceptProposedAction(); // TODO CHECK Tim. } //--------------------------------------------------------- // dragMoveEvent //--------------------------------------------------------- void WaveCanvas::dragMoveEvent(QDragMoveEvent*) { //printf("drag move %x\n", this); DELETETHIS (whole function?) //event->acceptProposedAction(); } //--------------------------------------------------------- // dragLeaveEvent //--------------------------------------------------------- void WaveCanvas::dragLeaveEvent(QDragLeaveEvent*) { //printf("drag leave\n"); DELETETHIS (whole function?) //event->acceptProposedAction(); } //--------------------------------------------------------- // curPartChanged //--------------------------------------------------------- void WaveCanvas::curPartChanged() { EventCanvas::curPartChanged(); editor->setWindowTitle(getCaption()); } //--------------------------------------------------------- // modifySelected //--------------------------------------------------------- void WaveCanvas::modifySelected(NoteInfo::ValType type, int val, bool delta_mode) { // TODO: New WaveCanvas: Convert this routine to frames and remove unneeded operations. QList< QPair > already_done; MusECore::Undo operations; for (iCItem i = items.begin(); i != items.end(); ++i) { if (!(i->second->isSelected())) continue; WEvent* e = (WEvent*)(i->second); MusECore::Event event = e->event(); if (event.type() != MusECore::Note) continue; MusECore::WavePart* part = (MusECore::WavePart*)(e->part()); if (already_done.contains(QPair(part->clonemaster_sn(), event))) continue; MusECore::Event newEvent = event.clone(); switch (type) { case NoteInfo::VAL_TIME: { int newTime = val; if(delta_mode) newTime += event.tick(); else newTime -= part->tick(); if (newTime < 0) newTime = 0; newEvent.setTick(newTime); } break; case NoteInfo::VAL_LEN: { int len = val; if(delta_mode) len += event.lenTick(); if (len < 1) len = 1; newEvent.setLenTick(len); } break; case NoteInfo::VAL_VELON: { int velo = val; if(delta_mode) velo += event.velo(); if (velo > 127) velo = 127; else if (velo < 0) // REMOVE Tim. Noteoff. Changed. Zero note on vel is not allowed now. // velo = 0; velo = 1; newEvent.setVelo(velo); } break; case NoteInfo::VAL_VELOFF: { int velo = val; if(delta_mode) velo += event.veloOff(); if (velo > 127) velo = 127; else if (velo < 0) velo = 0; newEvent.setVeloOff(velo); } break; case NoteInfo::VAL_PITCH: { int pitch = val; if(delta_mode) pitch += event.pitch(); if (pitch > 127) pitch = 127; else if (pitch < 0) pitch = 0; newEvent.setPitch(pitch); } break; } operations.push_back(MusECore::UndoOp(MusECore::UndoOp::ModifyEvent, newEvent, event, part, false, false)); already_done.append(QPair(part->clonemaster_sn(), event)); } MusEGlobal::song->applyOperationGroup(operations); } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void WaveCanvas::resizeEvent(QResizeEvent* ev) { // Readjust all wave canvas item heights bool do_redraw = false; for (iCItem k = items.begin(); k != items.end(); ++k) { if(k->second->height() != ev->size().height()) { k->second->setHeight(ev->size().height()); do_redraw = true; } } if (ev->size().width() != ev->oldSize().width()) emit newWidth(ev->size().width()); EventCanvas::resizeEvent(ev); if(do_redraw) redraw(); } //--------------------------------------------------------- // genItemPopup //--------------------------------------------------------- QMenu* WaveCanvas::genItemPopup(CItem* item) { QMenu* eventPopup = new QMenu(this); eventPopup->addAction(new MenuTitleItem(tr("Wave event:"), eventPopup)); eventPopup->addSeparator(); QAction *act_settings = eventPopup->addAction(tr("Converter settings")); act_settings->setData(0); act_settings->setEnabled(item && !item->event().sndFile().isNull()); genCanvasPopup(eventPopup); return eventPopup; } //--------------------------------------------------------- // itemPopup //--------------------------------------------------------- void WaveCanvas::itemPopup(CItem* /*item*/, int n, const QPoint& /*pt*/) { if(n >= TOOLS_ID_BASE) { canvasPopup(n); return; } switch(n) { case 0: // Settings if(curItem && !curItem->event().sndFile().isNull()) { if(MusECore::AudioConverterSettingsGroup* settings = curItem->event().sndFile().audioConverterSettings()) { MusECore::AudioConverterSettingsGroup* wrk_set = new MusECore::AudioConverterSettingsGroup(true); // Local settings. wrk_set->assign(*settings); AudioConverterSettingsDialog dialog(this, &MusEGlobal::audioConverterPluginList, wrk_set, true); // Local settings. if(dialog.exec() == QDialog::Accepted) { MusECore::PendingOperationList operations; MusEGlobal::song->modifyAudioConverterSettingsOperation( curItem->event().sndFile(), wrk_set, MusEGlobal::defaultAudioConverterSettings, true, // Local settings. operations); if(operations.empty()) { delete wrk_set; } else { MusEGlobal::audio->msgExecutePendingOperations(operations, true); //MusEGlobal::song->update(SC_); } } else { delete wrk_set; } } } break; default: printf("unknown action %d\n", n); break; } } void WaveCanvas::setRangeToSelection() { if (selectionStop > selectionStart) { int tick_min = MusEGlobal::tempomap.frame2tick(selectionStart, 0, MusECore::LargeIntRoundNearest); int tick_max = MusEGlobal::tempomap.frame2tick(selectionStop, 0, MusECore::LargeIntRoundNearest); MusECore::Pos p1(tick_min, true); MusECore::Pos p2(tick_max, true); if (p1 < MusEGlobal::song->lPos()) { MusEGlobal::song->setPos(MusECore::Song::LPOS, p1); MusEGlobal::song->setPos(MusECore::Song::RPOS, p2); } else { MusEGlobal::song->setPos(MusECore::Song::RPOS, p2); MusEGlobal::song->setPos(MusECore::Song::LPOS, p1); } } } } // namespace MusEGui muse-4.0.0/src/muse/waveedit/wavecanvas.h000066400000000000000000000237241404105311000203400ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // wavecanvas.h // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // Based on WaveView.cpp and PianoCanvas.cpp // (C) Copyright 2000 Werner Schweer (ws@seh.de) // and others. // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __WAVECANVAS_H__ #define __WAVECANVAS_H__ #include "type_defs.h" #include "ecanvas.h" #include #include #include #include #include #include "muse_time.h" #include "time_stretch.h" #include "wave.h" // Forward declarations: class QDragEnterEvent; class QDropEvent; class QMouseEvent; class QDragMoveEvent; class QDragLeaveEvent; class QWheelEvent; class QResizeEvent; class QPainter; namespace MusECore { class WavePart; class WaveTrack; struct WaveEventSelection { Event event; unsigned startframe; unsigned endframe; }; typedef std::list WaveSelectionList; typedef std::list::iterator iWaveSelection; } namespace MusEGui { //--------------------------------------------------------- // WEvent // ''visual'' Wave Event //--------------------------------------------------------- class WEvent : public EItem { public: WEvent(const MusECore::Event& e, MusECore::Part* p, int height); }; struct StretchSelectedItem { MusECore::StretchListItem::StretchEventType _type; MusECore::SndFileR _sndFile; StretchSelectedItem(MusECore::StretchListItem::StretchEventType type, MusECore::SndFileR sndFile = MusECore::SndFileR()) { _type = type; _sndFile = sndFile; } }; typedef std::multimap > StretchSelectedList_t; typedef StretchSelectedList_t::iterator iStretchSelectedItem; typedef StretchSelectedList_t::const_iterator ciStretchSelectedItem; typedef StretchSelectedList_t::reverse_iterator riStretchSelectedItem; typedef StretchSelectedList_t::const_reverse_iterator criStretchSelectedItem; typedef std::pair iStretchSelectedItemPair; typedef std::pair ciStretchSelectedItemPair; typedef std::pair StretchSelectedItemInsertPair_t; enum StretchControllerVals { stretchDoNothing, stretchStartMove, stretchMovingController, stretchAddNewController }; struct StretchAutomationObject { StretchSelectedList_t _stretchSelectedList; StretchControllerVals _controllerState; bool _moveController; QPoint _startMovePoint; // QPoint startMovePoint; // QList currentCtrlFrameList; // bool currentCtrlValid; // //MusECore::CtrlList *currentCtrlList; // MusECore::StretchList* currentCtrlList; // MusECore::Track *currentTrack; // bool moveController; // StretchControllerVals controllerState; // QString currentText; // bool breakUndoCombo; //QRect currentTextRect; //QRect currentVertexRect; //int currentTick; //int currentYNorm; StretchAutomationObject() { _controllerState = stretchDoNothing; _moveController = false; } }; //--------------------------------------------------------- // WaveCanvas //--------------------------------------------------------- class WaveCanvas : public EventCanvas { Q_OBJECT enum { NORMAL, DRAG } mode; enum { MUTE = 0, NORMALIZE, FADE_IN, FADE_OUT, REVERSE, GAIN, EDIT_EXTERNAL, CUT, COPY, PASTE }; //!< Modify operations static const int _stretchAutomationPointDetectDist; static const int _stretchAutomationPointWidthUnsel; static const int _stretchAutomationPointWidthSel; int yScale; int button; unsigned startSample; unsigned endSample; int colorMode; int selectionStart, selectionStop, dragstartx; int lastGainvalue; //!< Stores the last used gainvalue when specifying gain value in the editgain dialog QString copiedPart; StretchAutomationObject _stretchAutomation; //bool getUniqueTmpfileName(QString& newFilename); //!< Generates unique filename for temporary SndFile MusECore::WaveSelectionList getSelection(unsigned startpos, unsigned stoppos); void modifySelection(int operation, unsigned startpos, unsigned stoppos, double paramA); //!< Modifies selection void muteSelection(unsigned channels, float** data, unsigned length); //!< Mutes selection void normalizeSelection(unsigned channels, float** data, unsigned length); //!< Normalizes selection void fadeInSelection(unsigned channels, float** data, unsigned length); //!< Linear fade in of selection void fadeOutSelection(unsigned channels, float** data, unsigned length); //!< Linear fade out of selection void reverseSelection(unsigned channels, float** data, unsigned length); //!< Reverse selection void applyGain(unsigned channels, float** data, unsigned length, double gain); //!< Apply gain to selection void copySelection(unsigned file_channels, float** tmpdata, unsigned tmpdatalen, bool blankData, unsigned format, unsigned sampleRate); void editExternal(unsigned file_format, unsigned file_samplerate, unsigned channels, float** data, unsigned length); //void applyLadspa(unsigned channels, float** data, unsigned length); //!< Apply LADSPA plugin on selection void drawStretchAutomation(QPainter& p, const QRect& r, WEvent* wevent) const; MusECore::iStretchListItem stretchListHitTest(int types, QPoint pt, WEvent* wevent); void setStretchAutomationCursor(QPoint pt); void setRangeToSelection() override; protected: virtual QPoint raster(const QPoint&) const; void drawParts(QPainter&, bool /*do_cur_part*/, const QRect&, const QRegion& = QRegion()); virtual void viewMouseDoubleClickEvent(QMouseEvent*); virtual void wheelEvent(QWheelEvent*); virtual bool mousePress(QMouseEvent*); virtual void mouseMove(QMouseEvent* event); virtual void mouseRelease(QMouseEvent*); virtual void drawItem(QPainter&, const CItem*, const QRect&, const QRegion& = QRegion()); void drawMarkers(QPainter& p, const QRect& mr, const QRegion& mrg = QRegion()); void drawTopItem(QPainter& p, const QRect& rect, const QRegion& = QRegion()); virtual void drawMoving(QPainter&, const CItem*, const QRect&, const QRegion& = QRegion()); virtual MusECore::Undo moveCanvasItems(CItemMap&, int, int, DragType, bool rasterize = true); virtual bool moveItem(MusECore::Undo&, CItem*, const QPoint&, DragType, bool rasterize = true); virtual CItem* newItem(const QPoint&, int); virtual void resizeItem(CItem*, bool noSnap, bool); virtual void newItem(CItem*, bool noSnap); virtual bool deleteItem(CItem*); virtual void startDrag(CItem* item, DragType); virtual void dragEnterEvent(QDragEnterEvent* event); virtual void dragMoveEvent(QDragMoveEvent*); virtual void dragLeaveEvent(QDragLeaveEvent*); virtual CItem* addItem(MusECore::Part*, const MusECore::Event&); int y2pitch(int) const; int pitch2y(int) const; inline int y2height(int) const { return height(); } inline int yItemOffset() const { return 0; } virtual void drawCanvas(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void curPartChanged(); virtual void resizeEvent(QResizeEvent*); void adjustWaveOffset(); virtual QMenu* genItemPopup(CItem*); virtual void itemPopup(CItem*, int, const QPoint&); private slots: void setPos(int idx, unsigned val, bool adjustScrollbar); signals: void quantChanged(int); void rasterChanged(int); void newWidth(int); void mouseWheelMoved(int); public slots: void setYScale(int); void waveCmd(int); public: enum { CMD_MUTE=0, CMD_NORMALIZE, CMD_FADE_IN, CMD_FADE_OUT, CMD_REVERSE, CMD_GAIN_FREE, CMD_GAIN_200, CMD_GAIN_150, CMD_GAIN_75, CMD_GAIN_50, CMD_GAIN_25, CMD_EDIT_COPY, CMD_EDIT_CUT, CMD_EDIT_PASTE, CMD_PASTE_DIALOG, CMD_DEL, CMD_CREATE_PART_REGION, CMD_EDIT_EXTERNAL, CMD_QUANTIZE, CMD_SELECT_ALL, CMD_SELECT_NONE, CMD_SELECT_INVERT, CMD_SELECT_ILOOP, CMD_SELECT_OLOOP, CMD_SELECT_PREV_PART, CMD_SELECT_NEXT_PART, CMD_ERASE_MEASURE, CMD_DELETE_MEASURE, CMD_CREATE_MEASURE, CMD_ADJUST_WAVE_OFFSET , CMD_RANGE_TO_SELECTION }; WaveCanvas(MidiEditor*, QWidget*, int, int); virtual ~WaveCanvas(); MusECore::WaveTrack* track() const; void cmd(int cmd); void setColorMode(int mode) { colorMode = mode; redraw(); } // QString getCaption() const; void songChanged(MusECore::SongChangedStruct_t); void range(int* s, int* e) const { *s = startSample; *e = endSample; } void selectAtTick(unsigned int tick); void selectAtFrame(unsigned int frame); void modifySelected(NoteInfo::ValType type, int val, bool delta_mode = true); void keyPress(QKeyEvent*); void keyRelease(QKeyEvent* event); void updateItems(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/waveedit/waveedit.cpp000066400000000000000000001233421404105311000203420ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: waveedit.cpp,v 1.5.2.12 2009/04/06 01:24:54 terminator356 Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; 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 #include #include #include #include "app.h" #include "waveedit.h" #include "mtscale.h" #include "wavecanvas.h" #include "globals.h" #include "globaldefs.h" #include "audio.h" #include "utils.h" #include "song.h" #include "gconfig.h" #include "icons.h" #include "shortcuts.h" #include "cmd.h" #include "operations.h" #include "trackinfo_layout.h" // Forwards from header: #include #include #include #include #include #include #include #include #include #include #include "part.h" #include "xml.h" #include "splitter.h" #include "poslabel.h" #include "scrollscale.h" #include "cobject.h" #include "tools.h" #include "raster_widgets.h" // For debugging output: Uncomment the fprintf section. #define ERROR_WAVEEDIT(dev, format, args...) fprintf(dev, format, ##args) #define DEBUG_WAVEEDIT(dev, format, args...) // fprintf(dev, format, ##args) namespace MusECore { extern QColor readColor(MusECore::Xml& xml); } namespace MusEGui { static int waveEditTools = PointerTool | PencilTool | RubberTool | // CutTool | // unused RangeTool | PanTool | ZoomTool | StretchTool | SamplerateTool; int WaveEdit::_rasterInit = 96; int WaveEdit::_trackInfoWidthInit = 50; int WaveEdit::_canvasWidthInit = 300; int WaveEdit::colorModeInit = 0; //--------------------------------------------------------- // closeEvent //--------------------------------------------------------- void WaveEdit::closeEvent(QCloseEvent* e) { _isDeleting = true; // Set flag so certain signals like songChanged, which may cause crash during delete, can be ignored. storeSettings(); emit isDeleting(static_cast(this)); e->accept(); } void WaveEdit::storeSettings() { QSettings settings; //settings.setValue("Waveedit/geometry", saveGeometry()); settings.setValue("Waveedit/windowState", saveState()); //Store values of the horizontal splitter QList sizes = hsplitter->sizes(); QList::iterator it = sizes.begin(); _trackInfoWidthInit = *it; //There are only 2 values stored in the sizelist, size of trackinfo widget and canvas widget it++; _canvasWidthInit = *it; } //--------------------------------------------------------- // WaveEdit //--------------------------------------------------------- WaveEdit::WaveEdit(MusECore::PartList* pl, QWidget* parent, const char* name) : MidiEditor(TopWin::WAVE, _rasterInit, pl, parent, name) { setFocusPolicy(Qt::NoFocus); colorMode = colorModeInit; // Request to set the raster, but be sure to use the one it chooses, // which may be different than the one requested. _rasterInit = _rasterizerModel->checkRaster(_rasterInit); _raster = _rasterInit; _canvasXOrigin = DefaultCanvasXOrigin; //_minXMag = -32768; _minXMag = -16384; _maxXMag = -1; QAction* act; QMenu* menuEdit = menuBar()->addMenu(tr("&Edit")); cutAction = menuEdit->addAction(*cutSVGIcon, tr("C&ut")); connect(cutAction, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_EDIT_CUT); } ); copyAction = menuEdit->addAction(*copySVGIcon, tr("&Copy")); connect(copyAction, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_EDIT_COPY); } ); pasteAction = menuEdit->addAction(*pasteSVGIcon, tr("&Paste")); connect(pasteAction, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_EDIT_PASTE); } ); menuEdit->addSeparator(); copyPartRegionAction = menuEdit->addAction(tr("Create Part &from Range")); connect(copyPartRegionAction, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_CREATE_PART_REGION); } ); act = menuEdit->addAction(tr("Edit in E&xternal Editor...")); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_EDIT_EXTERNAL); } ); menuEdit->addSeparator(); // select = menuEdit->addMenu(QIcon(*selectIcon), tr("Select")); selectAllAction = menuEdit->addAction(*selectAllSVGIcon, tr("Select &All")); connect(selectAllAction, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_SELECT_ALL); } ); selectNoneAction = menuEdit->addAction(*deselectAllSVGIcon, tr("&Deselect All")); connect(selectNoneAction, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_SELECT_NONE); } ); menuEdit->addSeparator(); selectRangeToSelectionAction = menuEdit->addAction(*rangeToSelectionSVGIcon, tr("Set &Range to Selection")); connect(selectRangeToSelectionAction, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_RANGE_TO_SELECTION); } ); if (parts()->size() > 1) { menuEdit->addSeparator(); selectNextPartAction = menuEdit->addAction(*nextPartSVGIcon, tr("&Next Part")); selectPrevPartAction = menuEdit->addAction(*lastPartSVGIcon, tr("&Previous Part")); connect(selectNextPartAction, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_SELECT_NEXT_PART); } ); connect(selectPrevPartAction, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_SELECT_PREV_PART); } ); } else { selectPrevPartAction = nullptr; selectNextPartAction = nullptr; } menuFunctions = menuBar()->addMenu(tr("Func&tions")); menuGain = menuFunctions->addMenu(tr("&Gain")); act = menuGain->addAction("200%"); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_GAIN_200); } ); act = menuGain->addAction("150%"); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_GAIN_150); } ); act = menuGain->addAction("75%"); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_GAIN_75); } ); act = menuGain->addAction("50%"); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_GAIN_50); } ); act = menuGain->addAction("25%"); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_GAIN_25); } ); act = menuGain->addAction(tr("Other")); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_GAIN_FREE); } ); menuFunctions->addSeparator(); // REMOVE Tim. Also remove CMD_ADJUST_WAVE_OFFSET and so on... // adjustWaveOffsetAction = menuEdit->addAction(tr("Adjust wave offset...")); // mapper->setMapping(adjustWaveOffsetAction, WaveCanvas::CMD_ADJUST_WAVE_OFFSET); // connect(adjustWaveOffsetAction, SIGNAL(triggered()), mapper, SLOT(map())); act = menuFunctions->addAction(tr("Mute Selection")); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_MUTE); } ); act = menuFunctions->addAction(tr("Normalize Selection")); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_NORMALIZE); } ); act = menuFunctions->addAction(tr("Fade-in Selection")); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_FADE_IN); } ); act = menuFunctions->addAction(tr("Fade-out Selection")); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_FADE_OUT); } ); act = menuFunctions->addAction(tr("Reverse Selection")); connect(act, &QAction::triggered, [this]() { cmd(WaveCanvas::CMD_REVERSE); } ); QMenu* settingsMenu = menuBar()->addMenu(tr("&Display")); settingsMenu->menuAction()->setStatusTip(tr("Display menu: Display options specific to current editor.")); settingsMenu->addAction(subwinAction); // settingsMenu->addAction(shareAction); settingsMenu->addAction(fullscreenAction); settingsMenu->addSeparator(); eventColor = settingsMenu->addMenu(tr("&Event Color")); eventColor->setObjectName("CheckmarkOnly"); QActionGroup* actgrp = new QActionGroup(this); actgrp->setExclusive(true); evColorNormalAction = actgrp->addAction(tr("&Part Colors")); evColorNormalAction->setCheckable(true); connect(evColorNormalAction, &QAction::triggered, [this]() { eventColorModeChanged(0); } ); evColorPartsAction = actgrp->addAction(tr("&Gray")); evColorPartsAction->setCheckable(true); connect(evColorPartsAction, &QAction::triggered, [this]() { eventColorModeChanged(1); } ); eventColor->addActions(actgrp->actions()); connect(MusEGlobal::muse, SIGNAL(configChanged()), SLOT(configChanged())); //-------------------------------------------------- // ToolBar: Solo Cursor1 Cursor2 // NOTICE: Please ensure that any tool bar object names here match the names assigned // to identical or similar toolbars in class MusE or other TopWin classes. // This allows MusE::setCurrentMenuSharingTopwin() to do some magic // to retain the original toolbar layout. If it finds an existing // toolbar with the same object name, it /replaces/ it using insertToolBar(), // instead of /appending/ with addToolBar(). addToolBarBreak(); // Already has an object name. tools2 = new MusEGui::EditToolBar(this, waveEditTools); addToolBar(tools2); tb1 = addToolBar(tr("WaveEdit tools")); tb1->setObjectName("Wave Pos/Snap/Solo-tools"); solo = new QToolButton(); solo->setText(tr("Solo")); solo->setCheckable(true); solo->setFocusPolicy(Qt::NoFocus); tb1->addWidget(solo); connect(solo, SIGNAL(toggled(bool)), SLOT(soloChanged(bool))); QLabel* label = new QLabel(tr("Cursor")); tb1->addWidget(label); label->setAlignment(Qt::AlignRight|Qt::AlignVCenter); label->setIndent(3); pos1 = new PosLabel(nullptr); pos1->setObjectName("PosLabel"); pos1->setFixedHeight(22); tb1->addWidget(pos1); pos2 = new PosLabel(nullptr); pos2->setObjectName("PosLabel"); pos2->setFixedHeight(22); pos2->setSmpte(true); tb1->addWidget(pos2); gridOnButton = new QToolButton(); gridOnButton->setIcon(*gridOnSVGIcon); gridOnButton->setFocusPolicy(Qt::NoFocus); gridOnButton->setCheckable(true); gridOnButton->setToolTip(tr("Show grid")); gridOnButton->setWhatsThis(tr("Show grid")); tb1->addWidget(gridOnButton); connect(gridOnButton, &QToolButton::toggled, [this](bool v) { gridOnChanged(v); } ); rasterLabel = new RasterLabelCombo(RasterLabelCombo::TableView, _rasterizerModel, this, "RasterLabelCombo"); rasterLabel->setFocusPolicy(Qt::TabFocus); tb1->addWidget(rasterLabel); //--------------------------------------------------- // Rest //--------------------------------------------------- int yscale = 256; int xscale; if (!parts()->empty()) { // Roughly match total size of part MusECore::Part* firstPart = parts()->begin()->second; xscale = 0 - firstPart->lenFrame()/_widthInit[_type]; } else { xscale = -8000; } hscroll = new ScrollScale( _minXMag, _maxXMag, xscale, 10000, Qt::Horizontal, mainw, 0, false, 10000.0); canvas = new WaveCanvas(this, mainw, xscale, yscale); QSizeGrip* corner = new QSizeGrip(mainw); ymag = new QSlider(Qt::Vertical, mainw); ymag->setMinimum(1); ymag->setMaximum(256); ymag->setPageStep(256); ymag->setValue(yscale); ymag->setFocusPolicy(Qt::NoFocus); //--------------------------------------------------- // split //--------------------------------------------------- hsplitter = new MusEGui::Splitter(Qt::Horizontal, mainw, "hsplitter"); hsplitter->setChildrenCollapsible(true); //hsplitter->setHandleWidth(4); trackInfoWidget = new TrackInfoWidget(hsplitter); genTrackInfo(trackInfoWidget); time = new MTScale(_raster, mainw, xscale, true); //QWidget* split1 = new QWidget(splitter); QWidget* split1 = new QWidget(); split1->setObjectName("split1"); QGridLayout* gridS1 = new QGridLayout(split1); gridS1->setContentsMargins(0, 0, 0, 0); gridS1->setSpacing(0); gridS1->setRowStretch(2, 100); gridS1->setColumnStretch(1, 100); gridS1->addWidget(time, 0, 0, 1, 2); gridS1->addWidget(MusECore::hLine(mainw), 1, 0, 1, 2); gridS1->addWidget(canvas, 2, 0); gridS1->addWidget(ymag, 2, 1); QWidget* gridS2_w = new QWidget(); gridS2_w->setObjectName("gridS2_w"); gridS2_w->setContentsMargins(0, 0, 0, 0); QGridLayout* gridS2 = new QGridLayout(gridS2_w); gridS2->setContentsMargins(0, 0, 0, 0); gridS2->setSpacing(0); gridS2->setRowStretch(0, 100); //gridS2->setColumnStretch(1, 100); //gridS2->addWidget(ctrl, 0, 0); gridS2->addWidget(hscroll, 0, 0); gridS2->addWidget(corner, 0, 1, Qt::AlignBottom|Qt::AlignRight); gridS2_w->setMaximumHeight(hscroll->sizeHint().height()); gridS2_w->setMinimumHeight(hscroll->sizeHint().height()); QWidget* splitter_w = new QWidget(); splitter_w->setObjectName("splitter_w"); splitter_w->setContentsMargins(0, 0, 0, 0); QVBoxLayout* splitter_vbox = new QVBoxLayout(splitter_w); splitter_vbox->setContentsMargins(0, 0, 0, 0); splitter_vbox->setSpacing(0); //splitter_vbox->addWidget(splitter); splitter_vbox->addWidget(split1); splitter_vbox->addWidget(gridS2_w); hsplitter->addWidget(splitter_w); hsplitter->setStretchFactor(hsplitter->indexOf(trackInfoWidget), 0); QSizePolicy tipolicy = QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding); tipolicy.setHorizontalStretch(0); tipolicy.setVerticalStretch(100); trackInfoWidget->setSizePolicy(tipolicy); hsplitter->setStretchFactor(hsplitter->indexOf(splitter_w), 1); QSizePolicy epolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); epolicy.setHorizontalStretch(255); epolicy.setVerticalStretch(100); //splitter->setSizePolicy(epolicy); ymag->setFixedWidth(16); connect(canvas, SIGNAL(mouseWheelMoved(int)), this, SLOT(moveVerticalSlider(int))); connect(ymag, SIGNAL(valueChanged(int)), canvas, SLOT(setYScale(int))); connect(canvas, SIGNAL(horizontalZoom(bool, const QPoint&)), SLOT(horizontalZoom(bool, const QPoint&))); connect(canvas, SIGNAL(horizontalZoom(int, const QPoint&)), SLOT(horizontalZoom(int, const QPoint&))); canvas->setOrigin(_canvasXOrigin, 0); time->setOrigin(_canvasXOrigin, 0); changeRaster(_raster); mainGrid->addWidget(hsplitter, 0, 0, 1, 1); canvas->setFocus(); connect(canvas, SIGNAL(toolChanged(int)), tools2, SLOT(set(int))); connect(tools2, SIGNAL(toolChanged(int)), canvas, SLOT(setTool(int))); connect(MusEGlobal::muse, &MusE::configChanged, tools2, &EditToolBar::configChanged); connect(hscroll, SIGNAL(scrollChanged(int)), canvas, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), canvas, SLOT(setXMag(int))); setWindowTitle(canvas->getCaption()); connect(canvas, SIGNAL(followEvent(int)), hscroll, SLOT(setOffset(int))); connect(hscroll, SIGNAL(scrollChanged(int)), time, SLOT(setXPos(int))); connect(hscroll, SIGNAL(scaleChanged(int)), time, SLOT(setXMag(int))); connect(time, SIGNAL(timeChanged(unsigned)), SLOT(timeChanged(unsigned))); connect(canvas, SIGNAL(timeChanged(unsigned)), SLOT(setTime(unsigned))); connect(canvas, SIGNAL(horizontalScroll(unsigned)),hscroll, SLOT(setPos(unsigned))); connect(canvas, SIGNAL(horizontalScrollNoLimit(unsigned)),hscroll, SLOT(setPosNoLimit(unsigned))); connect(canvas, SIGNAL(curPartHasChanged(MusECore::Part*)), SLOT(updateTrackInfo())); connect(hscroll, SIGNAL(scaleChanged(int)), SLOT(updateHScrollRange())); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged1(MusECore::SongChangedStruct_t))); connect(rasterLabel, &RasterLabelCombo::rasterChanged, [this](int raster) { _setRaster(raster); } ); // For the wave editor, let's start with the operation range selection tool. canvas->setTool(MusEGui::RangeTool); tools2->set(MusEGui::RangeTool); setEventColorMode(colorMode); initShortcuts(); updateHScrollRange(); configChanged(); if(!parts()->empty()) { MusECore::WavePart* part = (MusECore::WavePart*)(parts()->begin()->second); solo->setChecked(part->track()->solo()); } QList mops; mops.append(_trackInfoWidthInit); mops.append(_canvasWidthInit); hsplitter->setSizes(mops); if(canvas->track()) { updateTrackInfo(); //toolbar->setSolo(canvas->track()->solo()); } initTopwinState(); finalizeInit(); } void WaveEdit::initShortcuts() { cutAction->setShortcut(shortcuts[SHRT_CUT].key); copyAction->setShortcut(shortcuts[SHRT_COPY].key); pasteAction->setShortcut(shortcuts[SHRT_PASTE].key); selectAllAction->setShortcut(shortcuts[SHRT_SELECT_ALL].key); selectNoneAction->setShortcut(shortcuts[SHRT_SELECT_NONE].key); //selectInvertAction->setShortcut(shortcuts[SHRT_SELECT_INVERT].key); //selectInsideLoopAction->setShortcut(shortcuts[SHRT_SELECT_ILOOP].key); //selectOutsideLoopAction->setShortcut(shortcuts[SHRT_SELECT_OLOOP].key); if (selectPrevPartAction && selectNextPartAction) { selectPrevPartAction->setShortcut(shortcuts[SHRT_SELECT_PREV_PART].key); selectNextPartAction->setShortcut(shortcuts[SHRT_SELECT_NEXT_PART].key); } selectRangeToSelectionAction->setShortcut(shortcuts[SHRT_LOCATORS_TO_SELECTION].key); // eventColor->menuAction()->setShortcut(shortcuts[SHRT_EVENT_COLOR].key); //evColorNormalAction->setShortcut(shortcuts[ ].key); //evColorPartsAction->setShortcut(shortcuts[ ].key); } //--------------------------------------------------------- // configChanged //--------------------------------------------------------- void WaveEdit::configChanged() { if (MusEGlobal::config.canvasBgPixmap.isEmpty()) { canvas->setBg(MusEGlobal::config.waveEditBackgroundColor); canvas->setBg(QPixmap()); } else { canvas->setBg(QPixmap(MusEGlobal::config.canvasBgPixmap)); } gridOnButton->blockSignals(true); gridOnButton->setChecked(MusEGlobal::config.canvasShowGrid); gridOnButton->blockSignals(false); initShortcuts(); canvas->redraw(); } //--------------------------------------------------------- // updateHScrollRange //--------------------------------------------------------- void WaveEdit::updateHScrollRange() { int s, e; canvas->range(&s, &e); // Range in frames unsigned tm = MusEGlobal::sigmap.ticksMeasure(MusEGlobal::tempomap.frame2tick(e)); // Show one more measure, and show another quarter measure due to imprecise drawing at canvas end point. //e += MusEGlobal::tempomap.tick2frame(tm + tm / 4); // TODO: Try changing scrollbar to use units of frames? e += (tm + tm / 4); // Compensate for the vscroll width. //e += wview->rmapxDev(-vscroll->width()); int s1, e1; hscroll->range(&s1, &e1); // ... if(s != s1 || e != e1) hscroll->setRange(s, e); // ... } //--------------------------------------------------------- // timeChanged //--------------------------------------------------------- void WaveEdit::timeChanged(unsigned t) { if(t == INT_MAX) { // Let the PosLabels disable themselves with INT_MAX. pos1->setValue(t); pos2->setValue(t); return; } unsigned frame = MusEGlobal::tempomap.tick2frame(t); pos1->setValue(t); pos2->setValue(frame); time->setPos(3, t, false); } //--------------------------------------------------------- // setTime //--------------------------------------------------------- void WaveEdit::setTime(unsigned samplepos) { if(samplepos == INT_MAX) { // Let the PosLabels disable themselves with INT_MAX. pos1->setValue(samplepos); pos2->setValue(samplepos); return; } // Normally frame to tick methods round down. But here we need it to 'snap' // the frame from either side of a tick to the tick. So round to nearest. unsigned tick = MusEGlobal::tempomap.frame2tick(samplepos, 0, MusECore::LargeIntRoundNearest); // Rasterize the tick. tick = MusEGlobal::sigmap.raster(tick, _raster); // To rasterize the pos2 frame, we must convert the rasterized tick back to frames. samplepos = MusEGlobal::tempomap.tick2frame(tick); pos1->setValue(tick); pos2->setValue(samplepos); time->setPos(3, tick, false); } //--------------------------------------------------------- // ~WaveEdit //--------------------------------------------------------- WaveEdit::~WaveEdit() { DEBUG_WAVEEDIT(stderr, "WaveEdit dtor\n"); } //--------------------------------------------------------- // cmd //--------------------------------------------------------- void WaveEdit::cmd(int n) { // Don't process if user is dragging or has clicked on the events. // Causes crashes later in Canvas::viewMouseMoveEvent and viewMouseReleaseEvent. if(canvas->getCurrentDrag()) return; ((WaveCanvas*)canvas)->cmd(n); } //--------------------------------------------------------- // loadConfiguration //--------------------------------------------------------- void WaveEdit::readConfiguration(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "bgcolor") MusEGlobal::config.waveEditBackgroundColor = readColor(xml); else if (tag == "raster") _rasterInit = xml.parseInt(); else if (tag == "trackinfowidth") _trackInfoWidthInit = xml.parseInt(); else if (tag == "canvaswidth") _canvasWidthInit = xml.parseInt(); else if (tag == "colormode") colorModeInit = xml.parseInt(); else if (tag == "topwin") TopWin::readConfiguration(WAVE, xml); else xml.unknown("WaveEdit"); break; case MusECore::Xml::TagEnd: if (tag == "waveedit") return; default: break; case MusECore::Xml::Error: case MusECore::Xml::End: return; } } } //--------------------------------------------------------- // saveConfiguration //--------------------------------------------------------- void WaveEdit::writeConfiguration(int level, MusECore::Xml& xml) { xml.tag(level++, "waveedit"); xml.colorTag(level, "bgcolor", MusEGlobal::config.waveEditBackgroundColor); xml.intTag(level, "raster", _rasterInit); xml.intTag(level, "trackinfowidth", _trackInfoWidthInit); xml.intTag(level, "canvaswidth", _canvasWidthInit); xml.intTag(level, "colormode", colorModeInit); TopWin::writeConfiguration(WAVE, level,xml); xml.tag(level, "/waveedit"); } //--------------------------------------------------------- // writeStatus //--------------------------------------------------------- void WaveEdit::writeStatus(int level, MusECore::Xml& xml) const { writePartList(level, xml); xml.tag(level++, "waveedit"); MidiEditor::writeStatus(level, xml); xml.intTag(level, "tool", int(canvas->tool())); xml.intTag(level, "xmag", hscroll->mag()); xml.intTag(level, "xpos", hscroll->pos()); xml.intTag(level, "ymag", ymag->value()); xml.tag(level, "/waveedit"); } //--------------------------------------------------------- // readStatus //--------------------------------------------------------- void WaveEdit::readStatus(MusECore::Xml& xml) { for (;;) { MusECore::Xml::Token token = xml.parse(); if (token == MusECore::Xml::Error || token == MusECore::Xml::End) break; QString tag = xml.s1(); switch (token) { case MusECore::Xml::TagStart: if (tag == "midieditor") MidiEditor::readStatus(xml); else if (tag == "tool") { int tool = xml.parseInt(); canvas->setTool(tool); tools2->set(tool); } else if (tag == "xmag") hscroll->setMag(xml.parseInt()); else if (tag == "ymag") ymag->setValue(xml.parseInt()); else if (tag == "xpos") hscroll->setPos(xml.parseInt()); else xml.unknown("WaveEdit"); break; case MusECore::Xml::TagEnd: if (tag == "waveedit") { changeRaster(_raster); return; } default: break; } } } //--------------------------------------------------------- // songChanged1 // signal from "song" //--------------------------------------------------------- void WaveEdit::songChanged1(MusECore::SongChangedStruct_t bits) { if(_isDeleting) // Ignore while while deleting to prevent crash. return; // We must catch this first and be sure to update the strips. if(bits & SC_TRACK_REMOVED) checkTrackInfoTrack(); if (bits & SC_DIVISION_CHANGED) { // The division has changed. The raster table and raster model will have been // cleared and re-filled, so any views on the model will no longer have a // current item and our current raster value will be invalid. They WILL NOT // emit an activated signal. So we must manually select a current item and // raster value here. We could do something fancy to try to keep the current // index - for example stay on quarter note - by taking the ratio of the new // division to old division and apply that to the old raster value and try // to select that index, but the division has already changed. // So instead, simply try to select the current raster value. The index in the box may change. // Be sure to use what it chooses. changeRaster(_raster); // Now set a reasonable zoom (mag) range. setupHZoomRange(); } if (bits & SC_SOLO) { MusECore::WavePart* part = (MusECore::WavePart*)(parts()->begin()->second); solo->blockSignals(true); solo->setChecked(part->track()->solo()); solo->blockSignals(false); } songChanged(bits); // We'll receive SC_SELECTION if a different part is selected. // Addition - also need to respond here to moving part to another track. (Tim) if (bits & (SC_PART_INSERTED | SC_PART_REMOVED)) updateTrackInfo(); // We must marshall song changed instead of connecting to the strip's song changed // otherwise it crashes when loading another song because track is no longer valid // and the strip's songChanged() seems to be called before Pianoroll songChanged() // gets called and has a chance to stop the crash. // Also, calling updateTrackInfo() from here is too heavy, it destroys and recreates // the strips each time no matter what the flags are ! else trackInfoSongChange(bits); } //--------------------------------------------------------- // soloChanged // signal from solo button //--------------------------------------------------------- void WaveEdit::soloChanged(bool flag) { MusECore::WavePart* part = (MusECore::WavePart*)(parts()->begin()->second); // This is a minor operation easily manually undoable. Let's not clog the undo list with it. MusECore::PendingOperationList operations; operations.add(MusECore::PendingOperationItem(part->track(), flag, MusECore::PendingOperationItem::SetTrackSolo)); MusEGlobal::audio->msgExecutePendingOperations(operations, true); } //--------------------------------------------------------- // gridOnChanged //--------------------------------------------------------- void WaveEdit::gridOnChanged(bool v) { MusEGlobal::config.canvasShowGrid = v; // We want the simple version, don't set the style or stylesheet yet. MusEGlobal::muse->changeConfig(true); } //--------------------------------------------------------- // viewKeyPressEvent //--------------------------------------------------------- void WaveEdit::keyPressEvent(QKeyEvent* event) { RasterizerModel::RasterPick rast_pick = RasterizerModel::NoPick; const int cur_rast = raster(); WaveCanvas* wc = (WaveCanvas*)canvas; int key = event->key(); if (((QInputEvent*)event)->modifiers() & Qt::ShiftModifier) key += Qt::SHIFT; if (((QInputEvent*)event)->modifiers() & Qt::AltModifier) key += Qt::ALT; if (((QInputEvent*)event)->modifiers() & Qt::ControlModifier) key+= Qt::CTRL; if (key == Qt::Key_Escape) { close(); return; } else if (key == shortcuts[SHRT_POS_INC].key) { wc->waveCmd(CMD_RIGHT); return; } else if (key == shortcuts[SHRT_POS_DEC].key) { wc->waveCmd(CMD_LEFT); return; } else if (key == shortcuts[SHRT_POS_INC_NOSNAP].key) { wc->waveCmd(CMD_RIGHT_NOSNAP); return; } else if (key == shortcuts[SHRT_POS_DEC_NOSNAP].key) { wc->waveCmd(CMD_LEFT_NOSNAP); return; } else if (key == shortcuts[SHRT_INSERT_AT_LOCATION].key) { wc->waveCmd(CMD_INSERT); return; } else if (key == Qt::Key_Backspace) { wc->waveCmd(CMD_BACKSPACE); return; } else if (key == shortcuts[SHRT_TOOL_POINTER].key) { tools2->set(MusEGui::PointerTool); return; } else if (key == shortcuts[SHRT_TOOL_PENCIL].key) { tools2->set(MusEGui::PencilTool); return; } else if (key == shortcuts[SHRT_TOOL_RUBBER].key) { tools2->set(MusEGui::RubberTool); return; } else if (key == shortcuts[SHRT_TOOL_SCISSORS].key) { tools2->set(MusEGui::CutTool); return; } else if (key == shortcuts[SHRT_TOOL_PAN].key) { tools2->set(MusEGui::PanTool); return; } else if (key == shortcuts[SHRT_TOOL_ZOOM].key) { tools2->set(MusEGui::ZoomTool); return; } else if (key == shortcuts[SHRT_TOOL_RANGE].key) { tools2->set(MusEGui::RangeTool); return; } else if (key == shortcuts[SHRT_TOOL_STRETCH].key) { tools2->set(MusEGui::StretchTool); return; } else if (key == shortcuts[SHRT_TOOL_SAMPLERATE].key) { tools2->set(MusEGui::SamplerateTool); return; } else if (key == shortcuts[SHRT_EVENT_COLOR].key) { if (colorMode == 0) colorMode = 1; else if (colorMode == 1) colorMode = 0; setEventColorMode(colorMode); return; } // TODO: New WaveCanvas: Convert some of these to use frames. else if (key == shortcuts[SHRT_ZOOM_IN].key) { horizontalZoom(true, QCursor::pos()); return; } else if (key == shortcuts[SHRT_ZOOM_OUT].key) { horizontalZoom(false, QCursor::pos()); return; } else if (key == shortcuts[SHRT_GOTO_CPOS].key) { MusECore::PartList* p = this->parts(); MusECore::Part* first = p->begin()->second; hscroll->setPos(MusEGlobal::song->cpos() - first->tick() ); return; } else if (key == shortcuts[SHRT_SCROLL_LEFT].key) { int pos = hscroll->pos() - MusEGlobal::config.division; if (pos < 0) pos = 0; hscroll->setPos(pos); return; } else if (key == shortcuts[SHRT_SCROLL_RIGHT].key) { int pos = hscroll->pos() + MusEGlobal::config.division; hscroll->setPos(pos); return; } else if (key == shortcuts[SHRT_SET_QUANT_BAR].key) rast_pick = RasterizerModel::GotoBar; else if (key == shortcuts[SHRT_SET_QUANT_OFF].key) //this hack has the downside that the next shortcut will use triols, but it's better than not having it, I think... rast_pick = RasterizerModel::GotoOff; else if (key == shortcuts[SHRT_SET_QUANT_1].key) rast_pick = RasterizerModel::Goto1; else if (key == shortcuts[SHRT_SET_QUANT_2].key) rast_pick = RasterizerModel::Goto2; else if (key == shortcuts[SHRT_SET_QUANT_3].key) rast_pick = RasterizerModel::Goto4; else if (key == shortcuts[SHRT_SET_QUANT_4].key) rast_pick = RasterizerModel::Goto8; else if (key == shortcuts[SHRT_SET_QUANT_5].key) rast_pick = RasterizerModel::Goto16; else if (key == shortcuts[SHRT_SET_QUANT_6].key) rast_pick = RasterizerModel::Goto32; else if (key == shortcuts[SHRT_SET_QUANT_7].key) rast_pick = RasterizerModel::Goto64; else if (key == shortcuts[SHRT_TOGGLE_TRIOL].key) rast_pick = RasterizerModel::ToggleTriple; else if (key == shortcuts[SHRT_TOGGLE_PUNCT].key) rast_pick = RasterizerModel::ToggleDotted; else if (key == shortcuts[SHRT_TOGGLE_PUNCT2].key) rast_pick = RasterizerModel::ToggleHigherDotted; else { //Default: event->ignore(); return; } if(rast_pick != RasterizerModel::NoPick) { const int new_rast = _rasterizerModel->pickRaster(cur_rast, rast_pick); if(new_rast != cur_rast) { setRaster(new_rast); //toolbar->setRaster(_raster); const QModelIndex mdl_idx = _rasterizerModel->modelIndexOfRaster(_raster); if(mdl_idx.isValid()) rasterLabel->setCurrentModelIndex(mdl_idx); else fprintf(stderr, "WaveEdit::keyPressEvent: _raster %d not found in box!\n", _raster); } } } //--------------------------------------------------------- // moveVerticalSlider //--------------------------------------------------------- void WaveEdit::moveVerticalSlider(int val) { ymag->setValue(ymag->value() + val); } void WaveEdit::horizontalZoom(bool zoom_in, const QPoint& glob_pos) { int mag = hscroll->mag(); int zoomlvl = ScrollScale::getQuickZoomLevel(mag); if(zoom_in) { if (zoomlvl < MusEGui::ScrollScale::zoomLevels-1) zoomlvl++; } else { if (zoomlvl > 1) zoomlvl--; } int newmag = ScrollScale::convertQuickZoomLevelToMag(zoomlvl); QPoint cp = canvas->mapFromGlobal(glob_pos); QPoint sp = mainw->mapFromGlobal(glob_pos); if(cp.x() >= 0 && cp.x() < canvas->width() && sp.y() >= 0 && sp.y() < mainw->height()) hscroll->setMag(newmag, cp.x()); } void WaveEdit::horizontalZoom(int mag, const QPoint& glob_pos) { QPoint cp = canvas->mapFromGlobal(glob_pos); QPoint sp = mainw->mapFromGlobal(glob_pos); if(cp.x() >= 0 && cp.x() < canvas->width() && sp.y() >= 0 && sp.y() < mainw->height()) hscroll->setMag(hscroll->mag() + mag, cp.x()); } //--------------------------------------------------------- // focusCanvas //--------------------------------------------------------- void WaveEdit::focusCanvas() { if(MusEGlobal::config.smartFocus) { canvas->setFocus(); canvas->activateWindow(); } } //--------------------------------------------------------- // _setRaster //--------------------------------------------------------- // void WaveEdit::_setRaster(int index) // { // const int rast = rasterLabel->itemData(index, RasterizerModel::RasterValueRole).toInt(); // MidiEditor::setRaster(rast); // _rasterInit = _raster; // time->setRaster(_raster); // canvas->redrawGrid(); // for (auto it : ctrlEditList) // it->redrawCanvas(); // // focusCanvas(); // } void WaveEdit::_setRaster(int raster) { MidiEditor::setRaster(raster); _rasterInit = _raster; time->setRaster(_raster); canvas->redrawGrid(); for (auto it : ctrlEditList) it->redrawCanvas(); focusCanvas(); } //--------------------------------------------------------- // changeRaster //--------------------------------------------------------- int WaveEdit::changeRaster(int val) { // _raster = _rasterizerModel->checkRaster(_raster); // _rasterInit = _raster; //const int idx = _rasterizerModel->indexOfRaster(_raster); // const int idx = rasterLabel->findData(_raster, MusECore::RasterizerModel::RasterValueRole); // if(idx >= 0) // rasterLabel->setCurrentIndex(idx); // const int lb_val = _rlistModel->checkRaster(val); // const int idx = _rlistModel->indexOfRaster(lb_val); // if(idx >= 0) // raster->setCurrentIndex(idx); // return lb_val; // const int lb_val = _rasterizerModel->checkRaster(val); const RasterizerModel* rast_mdl = rasterLabel->rasterizerModel(); MidiEditor::setRaster(rast_mdl->checkRaster(val)); _rasterInit = _raster; time->setRaster(_raster); // const int idx = rasterLabel->findData(_raster, RasterizerModel::RasterValueRole); // const int idx = rast_mdl->indexOfRaster(_raster); // if(idx >= 0) // rasterLabel->setCurrentIndex(idx); // else // fprintf(stderr, "WaveEdit::changeRaster: _raster %d not found in box!\n", _raster); const QModelIndex mdl_idx = rast_mdl->modelIndexOfRaster(_raster); if(mdl_idx.isValid()) rasterLabel->setCurrentModelIndex(mdl_idx); else fprintf(stderr, "WaveEdit::changeRaster: _raster %d not found in box!\n", _raster); canvas->redrawGrid(); for (auto it : ctrlEditList) it->redrawCanvas(); return _raster; } //--------------------------------------------------------- // eventColorModeChanged //--------------------------------------------------------- void WaveEdit::eventColorModeChanged(int mode) { colorMode = mode; colorModeInit = colorMode; ((WaveCanvas*)(canvas))->setColorMode(colorMode); } //--------------------------------------------------------- // setEventColorMode //--------------------------------------------------------- void WaveEdit::setEventColorMode(int mode) { colorMode = mode; colorModeInit = colorMode; evColorNormalAction->setChecked(mode == 0); evColorPartsAction->setChecked(mode == 1); ((WaveCanvas*)(canvas))->setColorMode(colorMode); } //--------------------------------------------------------- // setupHZoomRange //--------------------------------------------------------- void WaveEdit::setupHZoomRange() { //const int min = (_minXMag * MusEGlobal::config.division) / 384; //hscroll->setScaleRange(min, _maxXMag); hscroll->setScaleRange(_minXMag, _maxXMag); } } // namespace MusEGui muse-4.0.0/src/muse/waveedit/waveedit.h000066400000000000000000000103021404105311000177760ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: waveedit.h,v 1.3.2.8 2008/01/26 07:23:21 terminator356 Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __WAVE_EDIT_H__ #define __WAVE_EDIT_H__ #include "type_defs.h" #include "midieditor.h" // Forward declarations: class QMenu; class QWidget; class QKeyEvent; class QCloseEvent; class QAction; class QSlider; class QToolButton; class QPoint; class QToolBar; class QModelIndex; namespace MusECore { class PartList; } namespace MusEGui { class TopWin; class Splitter; class PosLabel; class EditToolBar; class RasterLabelCombo; //--------------------------------------------------------- // WaveEdit //--------------------------------------------------------- class WaveEdit : public MidiEditor { Q_OBJECT QSlider* ymag; QToolBar* tb1; QToolButton* solo; QToolButton* gridOnButton; MusEGui::PosLabel* pos1; MusEGui::PosLabel* pos2; QAction* selectAllAction; QAction* selectNoneAction; QAction* cutAction; QAction* copyAction; QAction* copyPartRegionAction; QAction* pasteAction; QAction* selectPrevPartAction; QAction* selectNextPartAction; QAction* selectRangeToSelectionAction; QAction* adjustWaveOffsetAction; QAction* evColorNormalAction; QAction* evColorPartsAction; MusEGui::EditToolBar* tools2; QMenu* menuFunctions, *menuGain, *eventColor; //*select,; int colorMode; MusEGui::Splitter* hsplitter; RasterLabelCombo* rasterLabel; static int _rasterInit; static int _trackInfoWidthInit; static int _canvasWidthInit; static int colorModeInit; void closeEvent(QCloseEvent*) override; void keyPressEvent(QKeyEvent*) override; void initShortcuts(); void setEventColorMode(int); // Sets up a reasonable zoom minimum and/or maximum based on // the current global midi division (ticks per quarter note) // which has a very wide range (48 - 12288). // Also sets the canvas and time scale offsets accordingly. void setupHZoomRange(); private slots: void cmd(int); void timeChanged(unsigned t); void setTime(unsigned t); void songChanged1(MusECore::SongChangedStruct_t); void soloChanged(bool flag); void gridOnChanged(bool flag); void moveVerticalSlider(int val); void eventColorModeChanged(int); void _setRaster(int raster); public slots: void configChanged(); virtual void updateHScrollRange() override; void horizontalZoom(bool zoom_in, const QPoint& glob_pos); void horizontalZoom(int mag, const QPoint& glob_pos); void focusCanvas() override; signals: void isDeleting(MusEGui::TopWin*); public: WaveEdit(MusECore::PartList*, QWidget* parent = nullptr, const char* name = nullptr); virtual ~WaveEdit() override; virtual void readStatus(MusECore::Xml&) override; virtual void writeStatus(int, MusECore::Xml&) const override; static void readConfiguration(MusECore::Xml&); static void writeConfiguration(int, MusECore::Xml&); // Same as setRaster() but returns the actual value used. int changeRaster(int val); void storeSettings() override; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/waveedit/waveview.cpp000066400000000000000000000431431404105311000203670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: waveview.cpp,v 1.10.2.16 2009/11/14 03:37:48 terminator356 Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include #include "editgain.h" #include "globals.h" #include "wave.h" #include "waveview.h" #include "song.h" #include "event.h" #include "waveedit.h" #include "audio.h" #include "gconfig.h" #include "fastlog.h" namespace MusEGui { bool modifyWarnedYet = false; //--------------------------------------------------------- // WaveView //--------------------------------------------------------- WaveView::WaveView(MidiEditor* pr, QWidget* parent, int xscale, int yscale) : View(parent, xscale, 1) { editor = pr; setVirt(true); pos[0] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->cpos()); pos[1] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->lpos()); pos[2] = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->rpos()); yScale = yscale; mode = NORMAL; selectionStart = 0; selectionStop = 0; lastGainvalue = 100; setFocusPolicy(Qt::StrongFocus); // Tim. setMouseTracking(true); setBg(QColor(192, 208, 255)); if (editor->parts()->empty()) { curPart = 0; curPartId = -1; } else { curPart = (MusECore::WavePart*)(editor->parts()->begin()->second); curPartId = curPart->sn(); } connect(MusEGlobal::song, SIGNAL(posChanged(int,unsigned,bool)), SLOT(setPos(int,unsigned,bool))); connect(MusEGlobal::song, SIGNAL(songChanged(MusECore::SongChangedStruct_t)), SLOT(songChanged(MusECore::SongChangedStruct_t))); songChanged(SC_SELECTION); } //--------------------------------------------------------- // setYScale //--------------------------------------------------------- void WaveView::setYScale(int val) { yScale = val; redraw(); } //--------------------------------------------------------- // draw //--------------------------------------------------------- void WaveView::pdraw(QPainter& p, const QRect& rr, const QRegion&) { int x1 = rr.x(); int x2 = rr.right() + 1; if (x1 < 0) x1 = 0; if (x2 > width()) x2 = width(); int hh = height(); int h = hh/2; int y = rr.y() + h; // Added by T356. int xScale = xmag; if (xScale < 0) xScale = -xScale; for (MusECore::iPart ip = editor->parts()->begin(); ip != editor->parts()->end(); ++ip) { MusECore::WavePart* wp = (MusECore::WavePart*)(ip->second); int channels = wp->track()->channels(); int px = wp->frame(); for (MusECore::ciEvent e = wp->events().begin(); e != wp->events().end(); ++e) { const MusECore::Event event& = e->second; if (event.empty()) continue; MusECore::SndFileR f = event.sndFile(); if(f.isNull()) continue; unsigned peoffset = px + event.frame() - event.spos(); int sx, ex; sx = event.frame() + px + xScale/2; ex = sx + event.lenFrame(); sx = sx / xScale - xpos; ex = ex / xScale - xpos; if (sx < x1) sx = x1; if (ex > x2) ex = x2; int pos = (xpos + sx) * xScale + event.spos() - event.frame() - px; //printf("pos=%d xpos=%d sx=%d ex=%d xScale=%d event.spos=%d event.frame=%d px=%d\n", // pos, xpos, sx, ex, xScale, event.spos(), event.frame(), px); h = hh / (channels * 2); int cc = hh % (channels * 2) ? 0 : 1; for (int i = sx; i < ex; i++) { y = rr.y() + h; MusECore::SampleV sa[f.channels()]; f.read(sa, xScale, pos); pos += xScale; if (pos < event.spos()) continue; int selectionStartPos = selectionStart - peoffset; // Offset transformed to event coords int selectionStopPos = selectionStop - peoffset; for (int k = 0; k < channels; ++k) { int kk = k % f.channels(); int peak = (sa[kk].peak * (h - 1)) / yScale; int rms = (sa[kk].rms * (h - 1)) / yScale; if (peak > h) peak = h; if (rms > h) rms = h; QColor peak_color = QColor(Qt::darkGray); QColor rms_color = QColor(Qt::black); // Changed by T356. Reduces (but not eliminates) drawing artifacts. //if (pos > selectionStartPos && pos < selectionStopPos) { if (pos > selectionStartPos && pos <= selectionStopPos) { peak_color = QColor(Qt::lightGray); rms_color = QColor(Qt::white); // Draw inverted p.setPen(QColor(Qt::black)); p.drawLine(i, y - h + cc, i, y + h - cc ); } p.setPen(peak_color); p.drawLine(i, y - peak - cc, i, y + peak); p.setPen(rms_color); p.drawLine(i, y - rms - cc, i, y + rms); y += 2 * h; } } } } View::pdraw(p, rr); } //--------------------------------------------------------- // draw //--------------------------------------------------------- void WaveView::draw(QPainter& p, const QRect& r, const QRegion&) { unsigned x = r.x() < 0 ? 0 : r.x(); unsigned y = r.y() < 0 ? 0 : r.y(); int w = r.width(); int h = r.height(); unsigned x2 = x + w; unsigned y2 = y + h; // // draw marker & centerline // p.setPen(Qt::blue); if (pos[1] >= x && pos[1] < x2) { p.drawLine(pos[1], y, pos[1], y2); } if (pos[2] >= x && pos[2] < x2) p.drawLine(pos[2], y, pos[2], y2); p.setPen(Qt::red); if (pos[0] >= x && pos[0] < x2) { p.drawLine(pos[0], y, pos[0], y2); } int n = 1; if(curPart) n = curPart->track()->channels(); int hn = h / n; int hh = hn / 2; for (int i = 0; i < n; ++i) { int h2 = hn * i; int center = hh + h2; p.setPen(QColor(i & i ? Qt::red : Qt::blue)); p.drawLine(x, center, x2, center); p.setPen(QColor(Qt::black)); p.drawLine(x, h2, x2, h2); } } //--------------------------------------------------------- // getCaption //--------------------------------------------------------- QString WaveView::getCaption() const { if(curPart) return QString("Part ") + curPart->name(); else return QString("Part "); } //--------------------------------------------------------- // songChanged //--------------------------------------------------------- void WaveView::songChanged(MusECore::SongChangedStruct_t flags) { if (flags._flags & ~(SC_SELECTION | SC_PART_SELECTION | SC_TRACK_SELECTION)) { // TODO FIXME: don't we actually only want SC_PART_*, and maybe SC_TRACK_DELETED? // (same in ecanvas.cpp) startSample = INT_MAX; endSample = 0; curPart = 0; for (MusECore::iPart p = editor->parts()->begin(); p != editor->parts()->end(); ++p) { MusECore::WavePart* part = (MusECore::WavePart*)(p->second); if (part->sn() == curPartId) curPart = part; int ssample = part->frame(); int esample = ssample + part->lenFrame(); if (ssample < startSample) { startSample = ssample; //printf("startSample = %d\n", startSample); } if (esample > endSample) { endSample = esample; //printf("endSample = %d\n", endSample); } } } if (flags._flags & SC_CLIP_MODIFIED) { redraw(); // Boring, but the only thing possible to do } if (flags._flags & SC_TEMPO) { setPos(0, MusEGlobal::song->cpos(), false); setPos(1, MusEGlobal::song->lpos(), false); setPos(2, MusEGlobal::song->rpos(), false); } redraw(); } //--------------------------------------------------------- // setPos // set one of three markers // idx - 0-cpos 1-lpos 2-rpos // flag - emit followEvent() //--------------------------------------------------------- void WaveView::setPos(int idx, unsigned val, bool adjustScrollbar) { val = MusEGlobal::tempomap.tick2frame(val); if (pos[idx] == val) return; int opos = mapx(pos[idx]); int npos = mapx(val); if (adjustScrollbar && idx == 0) { switch (MusEGlobal::song->follow()) { case MusECore::Song::NO: break; case MusECore::Song::JUMP: if (npos >= width()) { int ppos = val - xorg - rmapxDev(width()/4); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } else if (npos < 0) { int ppos = val - xorg - rmapxDev(width()*3/4); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } break; case MusECore::Song::CONTINUOUS: if (npos > (width()*5)/8) { int ppos = pos[idx] - xorg - rmapxDev(width()*5/8); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } else if (npos < (width()*3)/8) { int ppos = pos[idx] - xorg - rmapxDev(width()*3/8); if (ppos < 0) ppos = 0; emit followEvent(ppos); opos = mapx(pos[idx]); npos = mapx(val); } break; } } int x; int w = 1; if (opos > npos) { w += opos - npos; x = npos; } else { w += npos - opos; x = opos; } pos[idx] = val; //redraw(QRect(x, 0, w, height())); redraw(QRect(x-1, 0, w+2, height())); // p4.0.28 From Canvas::draw (is otherwise identical). Fix for corruption. } //--------------------------------------------------------- // viewMousePressEvent //--------------------------------------------------------- void WaveView::viewMousePressEvent(QMouseEvent* event) { button = event->button(); unsigned x = event->x(); switch (button) { case Qt::LeftButton: if (mode == NORMAL) { // redraw and reset: if (selectionStart != selectionStop) { selectionStart = selectionStop = 0; redraw(); } mode = DRAG; dragstartx = x; selectionStart = selectionStop = x; } break; case Qt::MidButton: case Qt::RightButton: default: break; } viewMouseMoveEvent(event); } #define WHEEL_STEPSIZE 50 #define WHEEL_DELTA 120 //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void WaveView::wheelEvent(QWheelEvent* ev) { int keyState = ev->modifiers(); bool shift = keyState & Qt::ShiftModifier; bool ctrl = keyState & Qt::ControlModifier; if (shift) { // scroll vertically int delta = -ev->delta() / WHEEL_DELTA; int xpixelscale = 5*MusECore::fast_log10(rmapxDev(1)); if (xpixelscale <= 0) xpixelscale = 1; int scrollstep = WHEEL_STEPSIZE * (delta); ///if (ev->state() == Qt::ShiftModifier) // if (((QInputEvent*)ev)->modifiers() == Qt::ShiftModifier) scrollstep = scrollstep / 10; int newXpos = xpos + xpixelscale * scrollstep; if (newXpos < 0) newXpos = 0; //setYPos(newYpos); emit horizontalScroll((unsigned)newXpos); } else if (ctrl) { // zoom horizontally if (ev->delta()>0) emit horizontalZoomIn(); else emit horizontalZoomOut(); } else { // scroll horizontally emit mouseWheelMoved(ev->delta() / 10); } } //--------------------------------------------------------- // viewMouseReleaseEvent //--------------------------------------------------------- void WaveView::viewMouseReleaseEvent(QMouseEvent* /*event*/) { button = Qt::NoButton; if (mode == DRAG) { mode = NORMAL; //printf("selectionStart=%d selectionStop=%d\n", selectionStart, selectionStop); } } //--------------------------------------------------------- // viewMouseMoveEvent //--------------------------------------------------------- void WaveView::viewMouseMoveEvent(QMouseEvent* event) { unsigned x = event->x(); emit timeChanged(x); int i; switch (button) { case Qt::LeftButton: i = 0; if (mode == DRAG) { if (x < dragstartx) { selectionStart = x; selectionStop = dragstartx; } else { selectionStart = dragstartx; selectionStop = x; } } break; case Qt::MidButton: i = 1; break; case Qt::RightButton: if ((MusEGlobal::config.rangeMarkerWithoutMMB) && (event->modifiers() & Qt::ControlModifier)) i = 1; else i = 2; break; default: return; } MusECore::Pos p(MusEGlobal::tempomap.frame2tick(x), true); MusEGlobal::song->setPos(i, p); } //--------------------------------------------------------- // range // returns range in samples //--------------------------------------------------------- void WaveView::range(int* s, int *e) { MusECore::PartList* lst = editor->parts(); if(lst->empty()) { *s = 0; *e = MusEGlobal::tempomap.tick2frame(MusEGlobal::song->len()); return; } int ps = MusEGlobal::song->len(), pe = 0; int tps, tpe; for(MusECore::iPart ip = lst->begin(); ip != lst->end(); ++ip) { tps = ip->second->tick(); if(tps < ps) ps = tps; tpe = tps + ip->second->lenTick(); if(tpe > pe) pe = tpe; } *s = MusEGlobal::tempomap.tick2frame(ps); *e = MusEGlobal::tempomap.tick2frame(pe); } } // namespace MusECore muse-4.0.0/src/muse/waveedit/waveview.h000066400000000000000000000066241404105311000200370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: waveview.h,v 1.3.2.6 2009/02/02 21:38:01 terminator356 Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef WAVE_VIEW_H #define WAVE_VIEW_H #include "type_defs.h" #include "view.h" #include #include #include "wave.h" class QPainter; class QRect; namespace MusECore { class PartList; class SndFileR; class WavePart; struct WaveEventSelection { SndFileR file; unsigned startframe; unsigned endframe; }; typedef std::list WaveSelectionList; typedef std::list::iterator iWaveSelection; } // namespace MusECore namespace MusEGui { class MidiEditor; //--------------------------------------------------------- // WaveView //--------------------------------------------------------- class WaveView : public View { Q_OBJECT MidiEditor* editor; unsigned pos[3]; int yScale; int button; int startSample; int endSample; MusECore::WavePart* curPart; QString copiedPart; int curPartId; enum { NORMAL, DRAG } mode; enum { MUTE = 0, NORMALIZE, FADE_IN, FADE_OUT, REVERSE, GAIN, EDIT_EXTERNAL, CUT, COPY, PASTE }; //!< Modify operations unsigned selectionStart, selectionStop, dragstartx; virtual void pdraw(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void draw(QPainter&, const QRect&, const QRegion& = QRegion()); virtual void viewMousePressEvent(QMouseEvent*); virtual void viewMouseMoveEvent(QMouseEvent*); virtual void viewMouseReleaseEvent(QMouseEvent*); virtual void wheelEvent(QWheelEvent*); //bool getUniqueTmpfileName(QString& newFilename); //!< Generates unique filename for temporary SndFile MusECore::WaveSelectionList getSelection(unsigned startpos, unsigned stoppos); int lastGainvalue; //!< Stores the last used gainvalue when specifying gain value in the editgain dialog private slots: void setPos(int idx, unsigned val, bool adjustScrollbar); public slots: void setYScale(int); void songChanged(MusECore::SongChangedStruct_t type); signals: void followEvent(int); void timeChanged(unsigned); void mouseWheelMoved(int); void horizontalScroll(unsigned); void horizontalZoomIn(); void horizontalZoomOut(); public: WaveView(MidiEditor*, QWidget* parent, int xscale, int yscale); QString getCaption() const; void range(int*, int*); MusECore::WavePart* part() { return curPart; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/waveevent.cpp000066400000000000000000000242731404105311000167310ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: waveevent.cpp,v 1.9.2.6 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2000-2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "audio_convert/audio_converter_settings_group.h" #include "audio_convert/audio_converter_plugin.h" #include "gconfig.h" #include "time_stretch.h" #include "globals.h" #include "waveevent.h" #include "xml.h" #include "wave.h" #include "part.h" #include "wave_helper.h" #include "audio_fifo.h" #include #include "muse_math.h" // For debugging output: Uncomment the fprintf section. #define WAVEEVENT_DEBUG(dev, format, args...) // fprintf(dev, format, ##args) #define WAVEEVENT_DEBUG_PRC(dev, format, args...) // fprintf(dev, format, ##args) namespace MusECore { //--------------------------------------------------------- // WaveEvent //--------------------------------------------------------- WaveEventBase::WaveEventBase(EventType t) : EventBase(t) { _spos = 0; _prefetchFifo = new Fifo(); _prefetchWritePos = ~0; _lastSeekPos = ~0; } WaveEventBase::WaveEventBase(const WaveEventBase& ev, bool duplicate_not_clone) : EventBase(ev, duplicate_not_clone) { _name = ev._name; _spos = ev._spos; // Make a new Fifo. _prefetchFifo = new Fifo(); _prefetchWritePos = ~0; _lastSeekPos = ~0; //*_audioConverterSettings = *ev._audioConverterSettings; // TODO? Or just keep the existing settings? // NOTE: It is necessary to create copies always. Unlike midi events, no shared data is allowed for // wave events because sndfile handles and audio stretchers etc. ABSOLUTELY need separate instances always. // So duplicate_not_clone is not used here. if(!ev.f.isNull() && !ev.f.canonicalPath().isEmpty()) { // Don't show error box, and assign the audio converter settings and stretch list. f = sndFileGetWave(ev.f.canonicalPath(), !ev.f.isWritable(), ev.f.isOpen(), false, ev.f.audioConverterSettings(), ev.f.stretchList()); } } WaveEventBase::~WaveEventBase() { delete _prefetchFifo; } //--------------------------------------------------------- // assign //--------------------------------------------------------- void WaveEventBase::assign(const EventBase& ev) { if(ev.type() != type()) return; EventBase::assign(ev); _name = ev.name(); _spos = ev.spos(); SndFileR sf = ev.sndFile(); setSndFile(sf); _prefetchWritePos = ~0; _lastSeekPos = ~0; } bool WaveEventBase::isSimilarTo(const EventBase& other_) const { const WaveEventBase* other = dynamic_cast(&other_); if (other==NULL) // dynamic cast hsa failed: "other_" is not of type WaveEventBase. return false; return f.dirPath()==other->f.dirPath() && _spos==other->_spos && this->PosLen::operator==(*other); } //--------------------------------------------------------- // WaveEvent::mid //--------------------------------------------------------- EventBase* WaveEventBase::mid(unsigned b, unsigned e) const { WaveEventBase* ev = new WaveEventBase(*this); unsigned fr = frame(); unsigned start = fr - b; if(b > fr) { start = 0; ev->setSpos(spos() + b - fr); } unsigned end = endFrame(); if (e < end) end = e; ev->setFrame(start); ev->setLenFrame(end - b - start); return ev; } //--------------------------------------------------------- // dump //--------------------------------------------------------- void WaveEventBase::dump(int n) const { EventBase::dump(n); } //--------------------------------------------------------- // WaveEventBase::read //--------------------------------------------------------- void WaveEventBase::read(Xml& xml) { StretchList sl; AudioConverterSettingsGroup settings(true); // Local non-default settings. settings.populate(&MusEGlobal::audioConverterPluginList, true); // Local non-default settings. QString filename; for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: case Xml::Attribut: return; case Xml::TagStart: if (tag == "poslen") PosLen::read(xml, "poslen"); else if (tag == "frame") _spos = xml.parseInt(); else if (tag == "file") { filename = xml.parse1(); } else if (tag == "stretchlist") { sl.read(xml); } else if (tag == "audioConverterSettingsGroup") { settings.read(xml, &MusEGlobal::audioConverterPluginList); } else xml.unknown("Event"); break; case Xml::TagEnd: if (tag == "event") { Pos::setType(FRAMES); // DEBUG if(!filename.isEmpty()) { SndFileR wf = sndFileGetWave(filename, true, true, true, &settings, &sl); if(wf) setSndFile(wf); } return; } default: break; } } } //--------------------------------------------------------- // write //--------------------------------------------------------- void WaveEventBase::write(int level, Xml& xml, const Pos& offset, bool forcePath) const { if (f.isNull()) return; xml.tag(level++, "event"); PosLen wpos(*this); wpos += offset; wpos.write(level, xml, "poslen"); xml.intTag(level, "frame", _spos); // offset in wave file // // waves in the project directory are stored // with relative path name, others with absolute path // QString path = f.dirPath(); if (!forcePath && path.contains(MusEGlobal::museProject)) { // extract MusEGlobal::museProject. QString newName = f.path().remove(MusEGlobal::museProject+"/"); xml.strTag(level, "file", newName); } else xml.strTag(level, "file", f.path()); if(f.stretchList()) f.stretchList()->write(level, xml); if(f.audioConverterSettings()) f.audioConverterSettings()->write(level, xml, &MusEGlobal::audioConverterPluginList); xml.etag(level, "event"); } //--------------------------------------------------------- // seekAudio //--------------------------------------------------------- void WaveEventBase::seekAudio(sf_count_t frame) { WAVEEVENT_DEBUG_PRC(stderr, "WaveEventBase::seekAudio frame:%lu\n", frame); if(!f.isNull()) { f.seekConverted(frame, SEEK_SET, _spos); } } void WaveEventBase::readAudio(unsigned frame, float** buffer, int channel, int n, bool /*doSeek*/, bool overwrite) { WAVEEVENT_DEBUG_PRC(stderr, "WaveEventBase::readAudio frame:%u channel:%d n:%d overwrite:%d\n", frame, channel, n, overwrite); if(!f.isNull()) { f.readConverted(frame, channel, buffer, n, overwrite); } return; } //--------------------------------------------------------- // prefetchAudio //--------------------------------------------------------- void WaveEventBase::prefetchAudio(Part* part, sf_count_t frames) { Fifo* fifo = audioPrefetchFifo(); if(!fifo) return; SndFileR sf = sndFile(); if(sf.isNull()) return; const sf_count_t p_spos = part->frame(); const sf_count_t p_epos = p_spos + part->lenFrame(); const sf_count_t e_spos = frame() + p_spos; sf_count_t nn = lenFrame(); const sf_count_t e_epos = e_spos + nn; if(_prefetchWritePos + frames >= p_spos && _prefetchWritePos < p_epos && _prefetchWritePos + frames >= e_spos && _prefetchWritePos < e_epos) { const sf_count_t offset = e_spos - _prefetchWritePos; if(offset > 0) { nn = frames - offset; } else { nn += offset; if(nn > frames) nn = frames; } const int chans = sf.channels(); const sf_count_t samples = chans * frames; float* bp; // Here we allocate ONE interleaved buffer which is fed with direct interleaved soundfile data. if(fifo->getWriteBuffer(1, samples, &bp, _prefetchWritePos)) return; // Clear the buffer. TODO: Optimize this by only clearing what's required, and merge with the denormal code below. memset(bp, 0, samples * sizeof(float)); sf.readDirect(bp, nn); // Add denormal bias to outdata. if(MusEGlobal::config.useDenormalBias) { for(sf_count_t i = 0; i < samples; ++i) bp[i] += MusEGlobal::denormalBias; } // Increment the prefetch buffer to a new position. fifo->add(); _prefetchWritePos += nn; } } } // namespace MusECore muse-4.0.0/src/muse/waveevent.h000066400000000000000000000064651404105311000164010ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: waveevent.h,v 1.6.2.4 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __WAVE_EVENT_H__ #define __WAVE_EVENT_H__ #include #include "eventbase.h" #include "muse_time.h" namespace MusECore { //--------------------------------------------------------- // WaveEvent //--------------------------------------------------------- class WaveEventBase : public EventBase { QString _name; SndFileR f; int _spos; // start sample position in WaveFile Fifo* _prefetchFifo; // Prefetch Fifo MuseCount_t _prefetchWritePos; // Current fifo write position. MuseCount_t _lastSeekPos; // Remember last seek to optimize seeks. // Creates a non-shared clone (copies event base), including the same 'group' id. virtual EventBase* clone() const { return new WaveEventBase(*this); } // Creates a copy of the event base, excluding the 'group' _id. virtual EventBase* duplicate() const { return new WaveEventBase(*this, true); } public: WaveEventBase(EventType t); // Creates a non-shared clone with same id, or duplicate with unique id, and 0 ref count and invalid Pos sn. WaveEventBase(const WaveEventBase& ev, bool duplicate_not_clone = false); virtual ~WaveEventBase(); virtual void assign(const EventBase& ev); // Assigns to this event, excluding the _id. virtual bool isSimilarTo(const EventBase& other) const; virtual void read(Xml&); virtual void write(int, Xml&, const Pos& offset, bool forcePath = false) const; virtual EventBase* mid(unsigned, unsigned) const; virtual void dump(int n = 0) const; virtual const QString name() const { return _name; } virtual void setName(const QString& s) { _name = s; } virtual int spos() const { return _spos; } virtual void setSpos(int s) { _spos = s; } virtual SndFileR sndFile() const { return f; } virtual void setSndFile(SndFileR& sf) { f = sf; } virtual void readAudio(unsigned frame, float** bpp, int channels, int nn, bool doSeek, bool overwrite); virtual void seekAudio(sf_count_t frame); virtual Fifo* audioPrefetchFifo() { return _prefetchFifo; } virtual void prefetchAudio(Part* part, sf_count_t frames); }; } // namespace MusECore #endif muse-4.0.0/src/muse/wavepreview.cpp000066400000000000000000000136651404105311000172740ustar00rootroot00000000000000#include "wavepreview.h" #include namespace MusEGlobal { MusECore::WavePreview *wavePreview = nullptr; } namespace MusECore { WavePreview::WavePreview(int segmentSize): sf(0), src(0), isPlaying(false), sem(1) { segSize = segmentSize * 10; tmpbuffer = new float [segSize]; srcbuffer = new float [segSize]; } WavePreview::~WavePreview() { stop(); delete[] tmpbuffer; delete[] srcbuffer; } long WavePreview::static_srcCallback (void *cb_data, float **data) { MusECore::WavePreview *wp = (MusECore::WavePreview *)cb_data; wp->nread = sf_readf_float(wp->sf, wp->tmpbuffer, wp->segSize / wp->sfi.channels); *data = wp->tmpbuffer; return wp->nread; } void WavePreview::play(QString path, int systemSampleRate) { stop(); memset(&sfi, 0, sizeof(sfi)); sf = sf_open(path.toUtf8().data(), SFM_READ, &sfi); if(sf) { int err = 0; //src = src_new(SRC_SINC_BEST_QUALITY, sfi.channels, &err); src = src_callback_new(static_srcCallback, SRC_SINC_MEDIUM_QUALITY, sfi.channels, &err, this); if(src) { p1 = tmpbuffer; p2 = srcbuffer; f1 = 0; f2 = 0; nread = 0; sd.src_ratio = ((double)systemSampleRate) / (double)sfi.samplerate; isPlaying = true; } else { sf_close(sf); sf = 0; } } } void WavePreview::stop() { isPlaying = false; sem.acquire(); if(sf) { sf_close(sf); sf = 0; } if(src) { src_delete(src); src = 0; } sem.release(); } void WavePreview::addData(int channels, int nframes, float *buffer[]) { if(sf && isPlaying) { sem.acquire(); if(!isPlaying) { sem.release(); return; } memset(srcbuffer, 0, sizeof(segSize) * sizeof(float)); /*p2 = srcbuffer; f2 = 0; while(true) { if(nread <= 0) { f1 = 0; p1 = tmpbuffer; nread = sf_readf_float(sf, tmpbuffer, nframes); if(nread <= 0) { isPlaying = false; return; } } sd.data_in = p1; sd.data_out = p2; sd.end_of_input = (nread == nframes) ? false : true; sd.input_frames = nread; sd.output_frames = nframes - f2; sd.input_frames_used = sd.output_frames_gen = 0; int err = src_process(src, &sd); if(err != 0) { break; } p1 += sd.input_frames_used * sfi.channels; p2 += sd.output_frames_gen * sfi.channels; f1 += sd.input_frames_used; f2 += sd.output_frames_gen; nread -= sd.input_frames_used; if((f2 >= nframes) || sd.end_of_input) { break; } }*/ int rd = src_callback_read(src, sd.src_ratio, nframes, srcbuffer); if(rd < nframes) { isPlaying = false; } if(rd == 0) { sem.release(); return; } int chns = std::min(channels, sfi.channels); for(int i = 0; i < chns; i++) { if(!buffer[i]) continue; for(int k = 0; k < nframes; k++) { buffer [i] [k] += srcbuffer [k * sfi.channels + i]; if((channels > 1) && (sfi.channels == 1)) { buffer [1] [k] += srcbuffer [k * sfi.channels + i]; } } } sem.release(); } } void initWavePreview(int segmentSize) { if(!MusEGlobal::wavePreview) MusEGlobal::wavePreview = new WavePreview(segmentSize); } void exitWavePreview() { if(MusEGlobal::wavePreview) { delete MusEGlobal::wavePreview; MusEGlobal::wavePreview = nullptr; } } void AudioPreviewDialog::urlChanged(const QString &str) { QFileInfo fi(str); if(fi.isDir()){ return; } if(chAutoPlay->isChecked()) { MusEGlobal::wavePreview->play(str, _systemSampleRate); } } void AudioPreviewDialog::startStopWave() { if(MusEGlobal::wavePreview->getIsPlaying()) { MusEGlobal::wavePreview->stop(); } else { QStringList files = selectedFiles(); if(files.size() > 0) { QString file = files [0]; QFileInfo fi(file); if(fi.isFile()) { MusEGlobal::wavePreview->play(file, _systemSampleRate); } } } } int AudioPreviewDialog::exec() { int r = QFileDialog::exec(); MusEGlobal::wavePreview->stop(); return r; } AudioPreviewDialog::AudioPreviewDialog(QWidget *parent, int systemSampleRate) :QFileDialog(parent), lastIsPlaying(false), _systemSampleRate(systemSampleRate) { setOption(QFileDialog::DontUseNativeDialog); setNameFilter(QString("Samples *.wav *.ogg *.flac (*.wav *.WAV *.ogg *.flac);;All files (*)")); //cb = new QComboBox; //cb->setEditable(false); //cb->addItems(list_ports()); //cb->setCurrentIndex(cb->count() - 1); chAutoPlay = new QCheckBox(this); chAutoPlay->setText(tr("Auto play")); chAutoPlay->setChecked(true); btnStop = new QPushButton(tr("Stop")); connect(btnStop, SIGNAL(clicked()), this, SLOT(startStopWave())); //QListView *v = this->findChild("listView", Qt::FindChildrenRecursively); QObject::connect(this, SIGNAL(currentChanged(const QString&)), this, SLOT(urlChanged(const QString&))); //this->layout()->addWidget(new QLabel("Midi device: ")); //this->layout()->addWidget(cb); this->layout()->addWidget(chAutoPlay); this->layout()->addWidget(btnStop); startTimer(30); } AudioPreviewDialog::~AudioPreviewDialog() { MusEGlobal::wavePreview->stop(); } void AudioPreviewDialog::timerEvent(QTimerEvent *) { if(lastIsPlaying != MusEGlobal::wavePreview->getIsPlaying()) { lastIsPlaying = MusEGlobal::wavePreview->getIsPlaying(); btnStop->setText(lastIsPlaying ? tr("Stop") : tr("Play")); } } } muse-4.0.0/src/muse/wavepreview.h000066400000000000000000000026241404105311000167320ustar00rootroot00000000000000#ifndef WAVEPREVIEW_H #define WAVEPREVIEW_H #include #include #include #include #include #include #include #include #include namespace MusECore { class WavePreview { private: SNDFILE *sf; SF_INFO sfi; SRC_STATE *src; bool isPlaying; float *tmpbuffer; float *srcbuffer; int segSize; float *p1; float *p2; int f1; int f2; SRC_DATA sd; sf_count_t nread; QSemaphore sem; static long static_srcCallback (void *cb_data, float **data); public: WavePreview(int segmentSize); virtual ~WavePreview(); void play(QString path, int systemSampleRate); void stop(); void addData(int channels, int nframes, float *buffer []); bool getIsPlaying() { return isPlaying; } }; class AudioPreviewDialog : public QFileDialog{ Q_OBJECT private: QCheckBox *chAutoPlay; QPushButton *btnStop; bool lastIsPlaying; int _systemSampleRate; private slots: void urlChanged(const QString &str); void startStopWave(); public slots: virtual int exec(); public: AudioPreviewDialog(QWidget *parent, int systemSampleRate); ~AudioPreviewDialog(); void timerEvent(QTimerEvent *); }; extern void initWavePreview(int segmentSize); extern void exitWavePreview(); } namespace MusEGlobal { extern MusECore::WavePreview *wavePreview; } #endif muse-4.0.0/src/muse/wavetrack.cpp000066400000000000000000000655151404105311000167200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: wavetrack.cpp,v 1.15.2.12 2009/12/20 05:00:35 terminator356 Exp $ // // (C) Copyright 2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include "track.h" #include "event.h" #include "audio.h" #include "wave.h" #include "xml.h" #include "song.h" #include "globals.h" #include "gconfig.h" #include "al/dsp.h" #include "audioprefetch.h" #include "latency_compensator.h" #include "config.h" // Turn on some cool terminal 'peak' meters for debugging // presence of actual audio at various places // #define NODE_DEBUG_TERMINAL_PEAK_METERS // For debugging output: Uncomment the fprintf section. #define WAVETRACK_DEBUG(dev, format, args...) // fprintf(dev, format, ##args) // For debugging transport timing: Uncomment the fprintf section. #define WAVETRACK_DEBUG_TRANSPORT_SYNC(dev, format, args...) // fprintf(dev, format, ##args); namespace MusECore { //--------------------------------------------------------- // WaveTrack //--------------------------------------------------------- // Default 1 channel for wave tracks. WaveTrack::WaveTrack() : AudioTrack(Track::WAVE, 1) { _prefetchWritePos = ~0; } WaveTrack::WaveTrack(const WaveTrack& wt, int flags) : AudioTrack(wt, flags) { _prefetchWritePos = ~0; internal_assign(wt, flags | Track::ASSIGN_PROPERTIES); } void WaveTrack::internal_assign(const Track& t, int flags) { if(t.type() != WAVE) return; //const WaveTrack& wt = (const WaveTrack&)t; const bool dup = flags & ASSIGN_DUPLICATE_PARTS; const bool cpy = flags & ASSIGN_COPY_PARTS; const bool cln = flags & ASSIGN_CLONE_PARTS; if(dup || cpy || cln) { const PartList* pl = t.cparts(); for (ciPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* spart = ip->second; Part* dpart = 0; if(dup) dpart = spart->hasClones() ? spart->createNewClone() : spart->duplicate(); else if(cpy) dpart = spart->duplicate(); else if(cln) dpart = spart->createNewClone(); if(dpart) { dpart->setTrack(this); parts()->add(dpart); } } } } void WaveTrack::assign(const Track& t, int flags) { AudioTrack::assign(t, flags); internal_assign(t, flags); } //--------------------------------------------------------- // seekData // called from prefetch thread //--------------------------------------------------------- void WaveTrack::seekData(sf_count_t pos) { WAVETRACK_DEBUG(stderr, "WaveTrack::seekData %s pos:%ld\n", name().toLatin1().constData(), pos); PartList* pl = parts(); for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { WavePart* part = (WavePart*)(ip->second); unsigned p_spos = part->frame(); EventList& el = part->nonconst_events(); for (iEvent ie = el.begin(); ie != el.end(); ++ie) { Event& event = ie->second; unsigned e_spos = event.frame() + p_spos; sf_count_t offset = 0; #ifdef ALLOW_LEFT_HIDDEN_EVENTS const sf_count_t e_pos_diff = (sf_count_t)(int)event.frame(); if(pos < (sf_count_t)(int)p_spos) { if(e_pos_diff < 0) offset = -e_pos_diff; } else { offset = pos - (sf_count_t)(int)e_spos; } #else offset = pos - e_spos; #endif if(offset < 0) offset = 0; event.seekAudio(offset); } } } //--------------------------------------------------------- // fetchData // called from prefetch thread //--------------------------------------------------------- void WaveTrack::fetchData(unsigned pos, unsigned samples, float** bp, bool doSeek, bool overwrite, int latency_correction) { WAVETRACK_DEBUG(stderr, "WaveTrack::fetchData %s samples:%u pos:%u overwrite:%d\n", name().toLatin1().constData(), samples, pos, overwrite); // reset buffer to zero if(overwrite) for (int i = 0; i < channels(); ++i) memset(bp[i], 0, samples * sizeof(float)); // Process only if track is not off. if(!off()) { const bool use_latency_corr = useLatencyCorrection(); bool do_overwrite = overwrite; PartList* pl = parts(); unsigned n = samples; for (iPart ip = pl->begin(); ip != pl->end(); ++ip) { WavePart* part = (WavePart*)(ip->second); if (part->mute()) continue; unsigned p_spos = part->frame(); unsigned p_epos = p_spos + part->lenFrame(); if (pos + n < p_spos) break; if (pos >= p_epos) continue; EventList& el = part->nonconst_events(); for (iEvent ie = el.begin(); ie != el.end(); ++ie) { Event& event = ie->second; unsigned e_spos = event.frame() + p_spos; unsigned nn = event.lenFrame(); unsigned e_epos = e_spos + nn; #ifdef ALLOW_LEFT_HIDDEN_EVENTS if ((int64_t)(pos + n) < (int64_t)(int)e_spos) break; if ((int64_t)pos >= (int64_t)(int)e_epos) continue; #else if (pos + n < e_spos) break; if (pos >= e_epos) continue; #endif int offset = e_spos - pos; unsigned srcOffset, dstOffset; if (offset > 0) { nn = n - offset; srcOffset = 0; dstOffset = offset; } else { srcOffset = -offset; dstOffset = 0; nn += offset; if (nn > n) nn = n; } if(use_latency_corr) { // Don't bother trying to read anything that comes before sample zero, // or limiting to zero which would just repeat the same beginning section over. // REMOVE Tim. latency. Added. Comment. // TODO: Change this: Insert blanks and use what we can from the buffer! if(latency_correction > 0 && (unsigned int)latency_correction > srcOffset) continue; // Move the source FORWARD by an amount necessary for latency correction. // i_correction will be negative for correction. srcOffset -= latency_correction; } float* bpp[channels()]; for (int i = 0; i < channels(); ++i) bpp[i] = bp[i] + dstOffset; event.readAudio(srcOffset, bpp, channels(), nn, doSeek, do_overwrite); do_overwrite = false; } } } if(overwrite && MusEGlobal::config.useDenormalBias) { // add denormal bias to outdata for (int i = 0; i < channels(); ++i) for (unsigned int j = 0; j < samples; ++j) bp[i][j] +=MusEGlobal::denormalBias; } _prefetchFifo.add(); } //--------------------------------------------------------- // write //--------------------------------------------------------- void WaveTrack::write(int level, Xml& xml) const { xml.tag(level++, "wavetrack"); AudioTrack::writeProperties(level, xml); const PartList* pl = cparts(); for (ciPart p = pl->begin(); p != pl->end(); ++p) p->second->write(level, xml); xml.etag(level, "wavetrack"); } //--------------------------------------------------------- // read //--------------------------------------------------------- void WaveTrack::read(Xml& xml) { for (;;) { Xml::Token token = xml.parse(); const QString& tag = xml.s1(); switch (token) { case Xml::Error: case Xml::End: goto out_of_WaveTrackRead_forloop; case Xml::TagStart: if (tag == "part") { Part* p = 0; p = Part::readFromXml(xml, this); if(p) parts()->add(p); } else if (AudioTrack::readProperties(xml, tag)) xml.unknown("WaveTrack"); break; case Xml::Attribut: break; case Xml::TagEnd: if (tag == "wavetrack") { mapRackPluginsToControllers(); goto out_of_WaveTrackRead_forloop; } default: break; } } out_of_WaveTrackRead_forloop: chainTrackParts(this); } //--------------------------------------------------------- // newPart //--------------------------------------------------------- Part* WaveTrack::newPart(Part*p, bool clone) { WavePart* part; if(!p) { part = new WavePart(this); } else { part = clone ? (WavePart*)p->createNewClone() : (WavePart*)p->duplicate(); part->setTrack(this); } return part; } bool WaveTrack::openAllParts() { bool opened = false; const PartList* pl = parts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { if(ip->second->openAllEvents()) opened = true; } return opened; } bool WaveTrack::closeAllParts() { bool closed = false; const PartList* pl = parts(); for(ciPart ip = pl->begin(); ip != pl->end(); ++ip) { if(ip->second->closeAllEvents()) closed = true; } return closed; } bool WaveTrack::getPrefetchData( sf_count_t framePos, int dstChannels, sf_count_t nframe, float** bp, bool do_overwrite) { const bool use_latency_corr = useLatencyCorrection(); float* pf_buf[dstChannels]; int i_correction = 0; if(use_latency_corr) { const TrackLatencyInfo& li = getLatencyInfo(false); const float correction = li._sourceCorrectionValue; i_correction = correction; } // First, fetch any track-wide pre-mixed data, such as straight unmodified data // or from prefetch-based samplerate converters, but NOT stretchers or pitch shifters - // those belong POST-prefetch, below, so they can have fast response to changes. if(MusEGlobal::audio->freewheel()) { // when freewheeling, read data direct from file: if(isMute()) { // We are muted. We need to let the fetching progress, but discard the data. for(int i = 0; i < dstChannels; ++i) // Set to the audio dummy buffer. pf_buf[i] = audioOutDummyBuf; // Indicate do not seek file before each read. fetchData(framePos, nframe, pf_buf, false, do_overwrite, i_correction); return false; } else { // Not muted. Fetch the data into the given buffers. // Indicate do not seek file before each read. fetchData(framePos, nframe, bp, false, do_overwrite, i_correction); // We have data. return true; } } else { bool ret_val = false; MuseCount_t pos; if(_prefetchFifo.peek(dstChannels, nframe, pf_buf, &pos)) { fprintf(stderr, "WaveTrack::getPrefetchData(%s) (prefetch peek A) fifo underrun\n", name().toLocal8Bit().constData()); return false; } //fprintf(stderr, "WaveTrack::getData(%s) (prefetch peek A) pos:%d\n", name().toLocal8Bit().constData(), pos); const int64_t frame_pos = framePos; const int64_t corr_frame_pos = framePos - i_correction; const int64_t corr_frame_end_pos = framePos - i_correction + nframe; WAVETRACK_DEBUG_TRANSPORT_SYNC(stderr, "WaveTrack::getPrefetchData: framePos:%ld i_correction:%d nframe:%ld corr_frame_pos:%ld corr_frame_end_pos:%ld pos:%ld\n", framePos, i_correction, nframe, corr_frame_pos, corr_frame_end_pos, pos); // Do we need to RETARD, or ADVANCE, the stream? if(corr_frame_end_pos <= pos) { WAVETRACK_DEBUG_TRANSPORT_SYNC(stderr, " RETARDING: corr_frame_end_pos <= pos\n"); // Allow the stream to RETARD. (That is, let our requested frame catch up to the stream.) return false; } else { // Allow the stream to ADVANCE if necessary. (That is, let the stream catch up to our requested frame.) while(corr_frame_pos >= pos + nframe) { WAVETRACK_DEBUG_TRANSPORT_SYNC(stderr, " ADVANCING: corr_frame_pos >= pos + nframe\n"); // Done with buffer, remove it. _prefetchFifo.remove(); if(_prefetchFifo.peek(dstChannels, nframe, pf_buf, &pos)) { fprintf(stderr, "WaveTrack::getPrefetchData(%s) (prefetch peek B) fifo underrun\n", name().toLocal8Bit().constData()); return false; } if(corr_frame_end_pos <= pos) { if(MusEGlobal::debugMsg) fprintf(stderr, "fifo get(%s) (A) error expected %ld, got %ld\n", name().toLocal8Bit().constData(), frame_pos, pos); return false; } } } if(corr_frame_pos <= pos) { if(!isMute()) { const unsigned blanks = pos - corr_frame_pos; const unsigned buf2_frames = nframe - blanks; if(do_overwrite) { if(blanks != 0) { for(int i = 0; i < dstChannels; ++i) AL::dsp->clear(bp[i], blanks, MusEGlobal::config.useDenormalBias); } for(int i = 0; i < dstChannels; ++i) AL::dsp->cpy(bp[i] + blanks, pf_buf[i], buf2_frames, MusEGlobal::config.useDenormalBias); } else { for(int i = 0; i < dstChannels; ++i) AL::dsp->mix(bp[i] + blanks, pf_buf[i], buf2_frames); } // We have data. ret_val = true; } // If the entire buffer was used, we are done with it. if(corr_frame_pos == pos) { // Done with buffer, remove it. _prefetchFifo.remove(); } } else { // This will always be at least 1, ie. corr_frame_pos > pos. const unsigned buf1_pos = corr_frame_pos - pos; const unsigned buf1_frames = nframe - buf1_pos; const unsigned buf2_pos = buf1_frames; const unsigned buf2_frames = buf1_pos; if(!isMute()) { if(do_overwrite) { for(int i = 0; i < dstChannels; ++i) AL::dsp->cpy(bp[i], pf_buf[i] + buf1_pos, buf1_frames, MusEGlobal::config.useDenormalBias); } else { for(int i = 0; i < dstChannels; ++i) AL::dsp->mix(bp[i], pf_buf[i] + buf1_pos, buf1_frames); } } // Done with buffer, remove it. _prefetchFifo.remove(); // We are expecting the next buffer. const MuseCount_t expect_nextpos = pos + nframe; // Peek the next buffer but do not remove it, // since the rest of it will be required next cycle. if(_prefetchFifo.peek(dstChannels, nframe, pf_buf, &pos)) { fprintf(stderr, "WaveTrack::getPrefetchData(%s) (prefetch peek C) fifo underrun\n", name().toLocal8Bit().constData()); return false; } if(pos != expect_nextpos) { if(MusEGlobal::debugMsg) fprintf(stderr, "fifo get(%s) (B) error expected %ld, got %ld\n", name().toLocal8Bit().constData(), expect_nextpos, pos); return false; } if(!isMute()) { if(do_overwrite) { for(int i = 0; i < dstChannels; ++i) AL::dsp->cpy(bp[i] + buf2_pos, pf_buf[i], buf2_frames, MusEGlobal::config.useDenormalBias); } else { for(int i = 0; i < dstChannels; ++i) AL::dsp->mix(bp[i] + buf2_pos, pf_buf[i], buf2_frames); } // We have data. ret_val = true; } } return ret_val; } } //--------------------------------------------------------- // getDataPrivate // return false if no data available //--------------------------------------------------------- bool WaveTrack::getInputData(unsigned pos, int channels, unsigned nframes, bool* usedInChannelArray, float** buffer) { // use supplied buffers const RouteList* rl = inRoutes(); const bool use_latency_corr = useLatencyCorrection(); #ifdef NODE_DEBUG_PROCESS fprintf(stderr, "AudioTrack::getData name:%s channels:%d inRoutes:%d\n", name().toLatin1().constData(), channels, int(rl->size())); #endif int dst_ch, dst_chs, src_ch, src_chs, fin_dst_chs, next_chan, i; unsigned long int l; bool have_data = false; for (ciRoute ir = rl->begin(); ir != rl->end(); ++ir) { if(ir->type != Route::TRACK_ROUTE || !ir->track || ir->track->isMidiTrack()) continue; // Only this track knows how many destination channels there are, // while only the route track knows how many source channels there are. // So take care of the destination channels here, and let the route track handle the source channels. dst_ch = ir->channel <= -1 ? 0 : ir->channel; if(dst_ch >= channels) continue; dst_chs = ir->channels <= -1 ? channels : ir->channels; src_ch = ir->remoteChannel <= -1 ? 0 : ir->remoteChannel; src_chs = ir->channels; fin_dst_chs = dst_chs; if(dst_ch + fin_dst_chs > channels) fin_dst_chs = channels - dst_ch; #ifdef NODE_DEBUG_PROCESS fprintf(stderr, " calling copy/addData on %s dst_ch:%d dst_chs:%d fin_dst_chs:%d src_ch:%d src_chs:%d ...\n", ir->track->name().toLatin1().constData(), dst_ch, dst_chs, fin_dst_chs, src_ch, src_chs); #endif static_cast(ir->track)->copyData(pos, dst_ch, dst_chs, fin_dst_chs, src_ch, src_chs, nframes, buffer, false, use_latency_corr ? nullptr : usedInChannelArray); #ifdef NODE_DEBUG_TERMINAL_PEAK_METERS if(MusEGlobal::audio->isPlaying()) { fprintf(stderr, "WaveTrack::getInputData() name:%s ir->latency:%lu latencyCompWriteOffset:%lu total:%lu\n", name().toLatin1().constData(), l, latencyCompWriteOffset(), l + latencyCompWriteOffset()); for(int ch = 0; ch < channels; ++ch) { fprintf(stderr, "channel:%d peak:", ch); float val; float peak = 0.0f; const float* buf = buffer[ch]; for(unsigned int smp = 0; smp < nframes; ++smp) { val = buf[smp]; if(val > peak) peak = val; } const int dots = peak * 20; for(int d = 0; d < dots; ++d) fprintf(stderr, "*"); fprintf(stderr, "\n"); } } #endif // Prepare the latency value to be passed to the compensator's writer, // by adjusting each route latency value. ie. the route with the worst-case // latency will get ZERO delay, while routes having smaller latency will get // MORE delay, to match all the signal timings together. // The route's audioLatencyOut should have already been calculated and // conveniently stored in the route. if((long int)ir->audioLatencyOut < 0) l = 0; else l = ir->audioLatencyOut; next_chan = dst_ch + fin_dst_chs; for(i = dst_ch; i < next_chan; ++i) { if(use_latency_corr) { // Write the buffers to the latency compensator. // By now, each copied channel should have the same latency. _latencyComp->write(i, nframes, l + latencyCompWriteOffset(), buffer[i]); } usedInChannelArray[i] = true; } have_data = true; } return have_data; } //--------------------------------------------------------- // getData //--------------------------------------------------------- bool WaveTrack::getData(unsigned framePos, int dstChannels, unsigned nframe, float** bp) { bool have_data = false; const bool track_rec_flag = recordFlag(); const bool track_rec_monitor = recMonitor(); // Separate monitor and record functions. const bool is_playing = MusEGlobal::audio->isPlaying(); const bool use_latency_corr = useLatencyCorrection(); //--------------------------------------------- // Note that the supplied buffers (bp) are at first // used as temporary storage but are later written // with final data. The reading and writing of fifo // file data wants linear memory whereas the latency // compensator uses wrap-around memory. //--------------------------------------------- //--------------------------------------------- // Contributions to data from input sources: //--------------------------------------------- // Gather input data from connected routes. if((MusEGlobal::song->bounceTrack != this) && !noInRoute()) { bool used_in_chan_array[dstChannels]; for(int i = 0; i < dstChannels; ++i) used_in_chan_array[i] = false; // The data retrieved by this will already be latency compensated. have_data = getInputData(framePos, dstChannels, nframe, used_in_chan_array, bp); // Do we want to record the incoming data? if(have_data && track_rec_flag && (MusEGlobal::audio->isRecording() || (MusEGlobal::song->record() && MusEGlobal::extSyncFlag && MusEGlobal::midiSyncContainer.isPlaying())) && recFile()) { if(MusEGlobal::audio->freewheel()) { } else { for(int i = 0; i < dstChannels; ++i) { if(used_in_chan_array[i]) { // Read back the latency compensated signals, using the buffers in-place. if(use_latency_corr) _latencyComp->peek(i, nframe, bp[i]); } else { // Fill unused channels with silence. // Channel is unused. Zero the supplied buffer. // REMOVE Tim. latency. Added. Maybe not required. The latency compensator already automatically clears to zero. AL::dsp->clear(bp[i], nframe, MusEGlobal::denormalBias); } } //fprintf(stderr, "WaveTrack::getData: name:%s RECORD: Putting to fifo: framePos:%d audio pos frame:%d\n", // name().toLatin1().constData(), // framePos, MusEGlobal::audio->pos().frame()); // This will adjust for the latency before putting. putFifo(dstChannels, nframe, bp); } } // Advance any peeked compensator channels now. if(use_latency_corr) _latencyComp->advance(nframe); } //--------------------------------------------- // Contributions to data from playback sources: //--------------------------------------------- if(!is_playing) { if(!have_data || (track_rec_monitor && have_data)) return have_data; return false; } // If there is no input source data or we do not want to monitor it, // overwrite the supplied buffers rather than mixing with them. const bool do_overwrite = !have_data || !track_rec_monitor; // Set the return value. // We only "have data" if we want to monitor it. have_data = track_rec_monitor && have_data; const bool have_pf_data = getPrefetchData(framePos, dstChannels, nframe, bp, do_overwrite); return have_data || have_pf_data; } inline bool WaveTrack::canDominateOutputLatency() const { // The wave track's own wave file contributions can never dominate latency. return false; } inline bool WaveTrack::canCorrectOutputLatency() const { return true; } //--------------------------------------------------------- // setChannels //--------------------------------------------------------- void WaveTrack::setChannels(int n) { AudioTrack::setChannels(n); SndFileR sf = recFile(); if (sf) { if (sf->samples() == 0) { sf->remove(); sf->setFormat(sf->format(), channels(), sf->samplerate()); sf->openWrite(); } } } void WaveTrack::clearPrefetchFifo() { _prefetchFifo.clear(); PartList* pl = parts(); for(iPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* part = ip->second; EventList& el = part->nonconst_events(); for(iEvent ie = el.begin(); ie != el.end(); ++ie) { Event& e = ie->second; if(e.audioPrefetchFifo()) e.audioPrefetchFifo()->clear(); } } } void WaveTrack::prefetchAudio(sf_count_t /*writePos*/, sf_count_t frames) { if(off()) return; PartList* pl = parts(); for(iPart ip = pl->begin(); ip != pl->end(); ++ip) { Part* part = ip->second; if(part->mute()) continue; EventList& el = part->nonconst_events(); for(iEvent ie = el.begin(); ie != el.end(); ++ie) { Event& e = ie->second; if(!e.audioPrefetchFifo()) continue; e.prefetchAudio(part, frames); } } } } // namespace MusECore muse-4.0.0/src/muse/widgets/000077500000000000000000000000001404105311000156575ustar00rootroot00000000000000muse-4.0.0/src/muse/widgets/CMakeLists.txt000066400000000000000000000210631404105311000204210ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= #============================================================================= ## NOTICE: ## The 'widgets' folder and library is meant to be kept as ## self-contained and independent as possible, since it might ## be shared with other libraries, for example our SimpleDrums ## MESS synth, which is a plugin and must strive be independent. ## Not all our 'widgets' could be stripped down like this, ## so the 'components' folder and library contains more complex ## controls which are too dependent on other MusE modules or ## controls and dialogs which are really only useful to the ## main app or could never really be publicly exposed. ## ## As more of the 'components' might be stripped down and moved ## into the 'widgets' folder, the original structure of the ## complete widgets cmake file is preserved below for convenience ## and history and comparison. #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP (widget_mocs # aboutbox_impl.h # arrangercolumns.h # action.h # bigtime.h # canvas.h checkbox.h # choose_sysex.h clipper_label.h colorframe.h comboboxpi.h comboQuant.h # combobox.h # comment.h # compact_knob.h # compact_patch_edit.h # compact_slider.h # copy_on_write.h # cpu_toolbar.h ctrlcombo.h # custom_widget_actions.h # dentry.h # didyouknow.h # doublelabel.h doublespinbox.h # editevent.h # elided_label.h # filedialog.h # genset.h # mdisettings.h # header.h # hitscale.h # intlabel.h knob.h knob_and_meter.h # lcd_widgets.h lcombo.h line_edit.h menutitleitem.h menutitlewidgets.h meter.h # meter_slider.h # metronome.h # midi_audio_control.h # midisyncimpl.h # midi_warn_init_pending_impl.h # mixdowndialog.h mlabel.h musemdiarea.h # mtscale.h # mtscale_flo.h # mtrackinfo.h # nentry.h # noteinfo.h # pastedialog.h # pasteeventsdialog.h # pitchedit.h # pitchlabel.h pixmap_button.h # plugindialog.h popup_double_spinbox.h # popupmenu.h # posedit.h # poslabel.h # projectcreateimpl.h # routepopup.h # scroll_area.h scrollbar.h # scrollscale.h # shortcutcapturedialog.h # shortcutconfig.h # sigedit.h # siglabel.h # sigscale.h # sig_tempo_toolbar.h # slider.h sliderbase.h # songinfo.h # songpos_toolbar.h spinbox.h spinboxFP.h # splitter.h swidget.h # tb1.h tempolabel.h text_edit.h # tools.h # trackinfo_layout.h # tracks_duplicate.h # ttoolbar.h # ttoolbutton.h # unusedwavefiles.h # view.h vscale.h # visibletracks.h # vst_native_editor.h # warn_bad_timing.h widget_stack.h ) ## ## UI files ## # file (GLOB widgets_ui_files # aboutbox.ui # arrangercolumnsbase.ui # appearancebase.ui # choose_sysex_base.ui # cliplisteditorbase.ui # commentbase.ui # configmidifilebase.ui # copy_on_write_base.ui # didyouknow.ui # editctrlbase.ui # editnotedialogbase.ui # editsysexdialogbase.ui # fdialogbuttons.ui # gensetbase.ui # mdisettings_base.ui # itransformbase.ui # metronomebase.ui # midisync.ui # midi_warn_init_pending.ui # midi_audio_control_base.ui # mittransposebase.ui # mixdowndialogbase.ui # # mtrackinfobase.ui # pastedialogbase.ui # pasteeventsdialogbase.ui # plugindialogbase.ui # projectcreate.ui # shortcutcapturedialogbase.ui # shortcutconfigbase.ui # songinfo.ui # synthconfigbase.ui # tracks_duplicate_base.ui # transformbase.ui # unusedwavefiles.ui # warn_bad_timing.ui # ) # QT5_WRAP_UI (widget_ui_headers ${widgets_ui_files}) ## ## List of source files to compile ## file (GLOB widgets_source_files # aboutbox_impl.cpp # arrangercolumns.cpp # background_painter.cpp # bigtime.cpp # canvas.cpp checkbox.cpp # choose_sysex.cpp # citem.cpp clipper_label.cpp colorframe.cpp comboQuant.cpp comboboxpi.cpp # combobox.cpp # comment.cpp # compact_knob.cpp # compact_patch_edit.cpp # compact_slider.cpp # copy_on_write.cpp # cpu_toolbar.cpp ctrlcombo.cpp # custom_widget_actions.cpp # dentry.cpp dimap.cpp # doublelabel.cpp doublespinbox.cpp drange.cpp # editevent.cpp # elided_label.cpp # filedialog.cpp # genset.cpp # mdisettings.cpp # header.cpp # hitscale.cpp # intlabel.cpp knob.cpp knob_and_meter.cpp # lcd_widgets.cpp lcombo.cpp line_edit.cpp menutitleitem.cpp menutitlewidgets.cpp meter.cpp # meter_slider.cpp # metronome.cpp # midi_audio_control.cpp # midisyncimpl.cpp # midi_warn_init_pending_impl.cpp # mixdowndialog.cpp mlabel.cpp mmath.cpp musemdiarea.cpp # mtrackinfo.cpp # mtscale.cpp # mtscale_flo.cpp # nentry.cpp # noteinfo.cpp # pastedialog.cpp # pasteeventsdialog.cpp # pitchedit.cpp # pitchlabel.cpp pixmap_button.cpp # plugindialog.cpp popup_double_spinbox.cpp # popupmenu.cpp # posedit.cpp # poslabel.cpp # projectcreateimpl.cpp # routepopup.cpp scldiv.cpp scldraw.cpp sclif.cpp scroll_area.cpp scrollbar.cpp # scrollscale.cpp # shortcutcapturedialog.cpp # shortcutconfig.cpp # sigedit.cpp # siglabel.cpp # sigscale.cpp # sig_tempo_toolbar.cpp # slider.cpp sliderbase.cpp # songpos_toolbar.cpp spinbox.cpp spinboxFP.cpp # splitter.cpp swidget.cpp # tb1.cpp tempolabel.cpp text_edit.cpp # tools.cpp # trackinfo_layout.cpp # tracks_duplicate.cpp # ttoolbar.cpp # ttoolbutton.cpp # unusedwavefiles.cpp # utils.cpp # velocity.cpp # ??? # view.cpp vscale.cpp # visibletracks.cpp # vst_native_editor.cpp # warn_bad_timing.cpp widget_stack.cpp #zita_resampler_settings.cpp ) ## ## Define target ## add_library ( widgets ${MODULES_BUILD} # ${widget_ui_headers} ${widget_mocs} ${widgets_source_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${widgets_source_files} # ${widgets_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( widgets # PROPERTIES COMPILE_FLAGS "${MUSECXXFLAGS} -I../ -I${PROJECT_SOURCE_DIR}/synti " PROPERTIES COMPILE_FLAGS "${VST_SDK_QUIRK} ${MUSECXXFLAGS} -I../ -I${PROJECT_SOURCE_DIR}/synti" OUTPUT_NAME muse_widgets ) ## ## Linkage ## target_link_libraries ( widgets ${QT_LIBRARIES} ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS widgets DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/muse/widgets/checkbox.cpp000066400000000000000000000042671404105311000201620ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: checkbox.cpp,v 1.2.2.2 2006/10/29 07:54:52 terminator356 Exp $ // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "checkbox.h" #include namespace MusEGui { //--------------------------------------------------------- // CheckBox //--------------------------------------------------------- CheckBox::CheckBox(QWidget* parent, int i, const char* name) : QCheckBox(parent) { setObjectName(name); _id = i; connect(this, SIGNAL(toggled(bool)), SLOT(hasToggled(bool))); } void CheckBox::hasToggled(bool val) { emit toggleChanged(val, _id); } //------------------------------------------------------------ // mousePressEvent //------------------------------------------------------------ void CheckBox::mousePressEvent(QMouseEvent *e) { if(e->button() == Qt::RightButton) emit checkboxRightClicked(e->globalPos(), _id); else { if(isChecked()) setChecked(false); else setChecked(true); emit checkboxPressed(_id); } } //------------------------------------------------------------ // mouseReleaseEvent //------------------------------------------------------------ void CheckBox::mouseReleaseEvent(QMouseEvent *e) { if(e->button() == Qt::RightButton) return; emit checkboxReleased(_id); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/checkbox.h000066400000000000000000000035211404105311000176170ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: checkbox.h,v 1.2.2.2 2006/10/29 07:54:52 terminator356 Exp $ // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CHECKBOX_H__ #define __CHECKBOX_H__ #include namespace MusEGui { //--------------------------------------------------------- // CheckBox //--------------------------------------------------------- class CheckBox : public QCheckBox { Q_OBJECT Q_PROPERTY( int id READ id WRITE setId ) int _id; protected: void mousePressEvent(QMouseEvent *e); void mouseReleaseEvent(QMouseEvent *e); private slots: void hasToggled(bool val); signals: void toggleChanged(bool, int); void checkboxPressed(int); void checkboxReleased(int); void checkboxRightClicked(const QPoint &, int); public: CheckBox(QWidget* parent, int i, const char* name = 0); int id() const { return _id; } void setId(int i) { _id = i; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/clipper_label.cpp000066400000000000000000000053661404105311000211720ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "fastlog.h" #include "clipper_label.h" namespace MusEGui { ClipperLabel::ClipperLabel(QWidget *parent): QFrame(parent), _isClipped(false), _value(0.0) { setObjectName("ClipperLabel"); // Background is drawn by us. setBackgroundRole(QPalette::NoRole); setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_StaticContents); setAttribute(Qt::WA_OpaquePaintEvent); //setFrameStyle(QFrame::Box | QFrame::Sunken); setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); //setLineWidth(1); // Not really required - StyledPanel always has 1 width. //setMidLineWidth(0); const int fw = frameWidth(); setContentsMargins(fw, fw, fw, fw); // setProperty("clipped", "false"); const QColor fc(255, 75, 75); const QColor fcd = fc.darker(150); _onGradient.setColorAt(0.0, fcd); _onGradient.setColorAt(0.5, fc); _onGradient.setColorAt(1.0, fcd); setVal(_value, true); } QSize ClipperLabel::sizeHint() const { const int fw = frameWidth(); const QSize sz = fontMetrics().boundingRect("-88.8.").size(); return QSize(sz.width() + 2 * fw, sz.height() + 2 * fw); } void ClipperLabel::paintEvent(QPaintEvent *e) { const QRect& r = frameRect(); QPainter p; p.begin(this); if(_isClipped) p.fillRect(r, _onGradient); else p.fillRect(r, palette().window()); p.end(); QFrame::paintEvent(e); p.begin(this); if(_isClipped) p.setPen(Qt::white); //p.drawText(e->rect(), Qt::AlignCenter, _text); // Drawing artifacts? p.drawText(contentsRect(), Qt::AlignCenter, _text); p.end(); } void ClipperLabel::resizeEvent(QResizeEvent *) { _onGradient.setStart(0, frameRect().y()); _onGradient.setFinalStop(0, frameRect().y() + frameRect().height() - 1); } void ClipperLabel::mousePressEvent(QMouseEvent *ev) { ev->accept(); emit clicked(); } void ClipperLabel::mouseReleaseEvent(QMouseEvent *ev) { ev->accept(); } void ClipperLabel::mouseMoveEvent(QMouseEvent *ev) { ev->accept(); } void ClipperLabel::contextMenuEvent(QContextMenuEvent * ev) { ev->accept(); } void ClipperLabel::setClipped(bool b) { if(b != _isClipped) { _isClipped = b; setVal(_value, true); update(); } } void ClipperLabel::setVal(double v, bool force) { if((v == _value) && !force) { return; } _value = v; v = MusECore::fast_log10(v) * 20.0; if(v >= -60.0f) { _text = locale().toString(v, 'f', 1); } else { _text = QString("-"); _text += QChar(0x221e); // The infinty character } update(); } } muse-4.0.0/src/muse/widgets/clipper_label.h000066400000000000000000000017471404105311000206360ustar00rootroot00000000000000#ifndef _CLIPPER_LABEL_H_ #define _CLIPPER_LABEL_H_ #include #include #include #include class QPaintEvent; class QResizeEvent; class QMouseEvent; class QContextMenuEvent; namespace MusEGui { class ClipperLabel : public QFrame { Q_OBJECT Q_PROPERTY( bool clipped READ clipped WRITE setClipped ) private: bool _isClipped; double _value; QString _text; QLinearGradient _onGradient; public: ClipperLabel(QWidget *parent = 0); virtual QSize sizeHint() const; bool clipped() const { return _isClipped; } void setClipped(bool b); void setVal(double v, bool force = false); protected: virtual void paintEvent(QPaintEvent *); virtual void resizeEvent(QResizeEvent *e); virtual void mousePressEvent(QMouseEvent *); virtual void mouseReleaseEvent(QMouseEvent *); virtual void mouseMoveEvent(QMouseEvent *); virtual void contextMenuEvent(QContextMenuEvent*); signals: void clicked(); }; } #endif muse-4.0.0/src/muse/widgets/colorframe.cpp000066400000000000000000000005301404105311000205120ustar00rootroot00000000000000#include #include #include "colorframe.h" ColorFrame::ColorFrame(QWidget *parent) : QWidget(parent) { } void ColorFrame::paintEvent(QPaintEvent *e) { QRect r(e->rect()); QPainter p(this); p.fillRect(r, _color); } void ColorFrame::mousePressEvent(QMouseEvent* e) { e->accept(); emit clicked(); } muse-4.0.0/src/muse/widgets/colorframe.h000066400000000000000000000007321404105311000201630ustar00rootroot00000000000000#ifndef COLORFRAME_H #define COLORFRAME_H #include class ColorFrame : public QWidget { Q_OBJECT protected: virtual void paintEvent(QPaintEvent*); virtual void mousePressEvent(QMouseEvent*); public: explicit ColorFrame(QWidget *parent = 0); QColor color() const { return _color; } void setColor(QColor c) {_color = c; update();} signals: void clicked(); public slots: private: QColor _color; }; #endif // COLORFRAME_H muse-4.0.0/src/muse/widgets/comboQuant.cpp000066400000000000000000000075601404105311000205030ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: comboQuant.cpp,v 1.1.1.1 2003/10/27 18:54:52 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "comboQuant.h" namespace MusEGui { static int quantTable[] = { 1, 16, 32, 64, 128, 256, 512, 1024, 1, 24, 48, 96, 192, 384, 768, 1536, 1, 36, 72, 144, 288, 576, 1152, 2304 }; static const char* quantStrings[] = { QT_TRANSLATE_NOOP("MusEGui::ComboQuant", "Off"), "64T", "32T", "16T", "8T", "4T", "2T", "1T", QT_TRANSLATE_NOOP("MusEGui::ComboQuant", "Off"), "64", "32", "16", "8", "4", "2", "1", QT_TRANSLATE_NOOP("MusEGui::ComboQuant", "Off"), "64.", "32.", "16.", "8.", "4.", "2.", "1." }; //--------------------------------------------------------- // ComboQuant //--------------------------------------------------------- ComboQuant::ComboQuant(QWidget* parent) : QComboBox(parent) { ///Q3ListBox* qlist = new Q3ListBox(this); ///qlist->setMinimumWidth(95); //setListBox(qlist); ddskrjo ///qlist->setColumnMode(3); qlist = new QTableWidget(8, 3); qlist->verticalHeader()->setDefaultSectionSize(22); qlist->horizontalHeader()->setDefaultSectionSize(32); qlist->setSelectionMode(QAbstractItemView::SingleSelection); qlist->verticalHeader()->hide(); qlist->horizontalHeader()->hide(); qlist->setMinimumWidth(96); setView(qlist); ///for (int i = 0; i < 24; i++) /// qlist->insertItem(tr(quantStrings[i]), i); for (int j = 0; j < 3; j++) for (int i = 0; i < 8; i++) qlist->setItem(i, j, new QTableWidgetItem(tr(quantStrings[i + j * 8]))); connect(this, SIGNAL(activated(int)), SLOT(activated(int))); } //--------------------------------------------------------- // activated //--------------------------------------------------------- void ComboQuant::activated(int /*index*/) { ///emit valueChanged(quantTable[index]); emit valueChanged(quantTable[qlist->currentRow() + qlist->currentColumn() * 8]); } //--------------------------------------------------------- // setQuant //--------------------------------------------------------- void ComboQuant::setValue(int val) { for (int i = 0; i < 24; i++) { if (val == quantTable[i]) { setCurrentIndex(i); return; } } for (unsigned i = 0; i < sizeof(quantTable)/sizeof(*quantTable); i++) { if (val == quantTable[i]) { setCurrentIndex(i); return; } } printf("ComboQuant::setValue(%d) not defined\n", val); setCurrentIndex(0); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/comboQuant.h000066400000000000000000000030721404105311000201420ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: comboQuant.h,v 1.1.1.1 2003/10/27 18:54:30 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __COMBOQUANT_H__ #define __COMBOQUANT_H__ #include //#include //#include class QWidget; class QTableWidget; namespace MusEGui { //--------------------------------------------------------- // ComboQuant //--------------------------------------------------------- class ComboQuant : public QComboBox { Q_OBJECT QTableWidget* qlist; private slots: void activated(int); signals: void valueChanged(int); public: ComboQuant(QWidget* parent = 0); void setValue(int val); }; } #endif muse-4.0.0/src/muse/widgets/comboboxpi.cpp000066400000000000000000000011211404105311000205170ustar00rootroot00000000000000#include "comboboxpi.h" #include namespace MusEGui { ComboBoxPI::ComboBoxPI(QWidget* parent, int i, const char* name) : QComboBox(parent) { setObjectName(name); pid = i; } //------------------------------------------------------------ // mousePressEvent //------------------------------------------------------------ void ComboBoxPI::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::RightButton) { e->accept(); emit rightClicked(e->globalPos(), pid); return; } e->ignore(); QComboBox::mousePressEvent(e); } } muse-4.0.0/src/muse/widgets/comboboxpi.h000066400000000000000000000007631404105311000201770ustar00rootroot00000000000000#ifndef COMBOBOXPI_H #define COMBOBOXPI_H #include namespace MusEGui { class ComboBoxPI : public QComboBox { Q_OBJECT Q_PROPERTY( int id READ id WRITE setId ) int pid; protected: void mousePressEvent(QMouseEvent *e); signals: void rightClicked(const QPoint &, int); public: ComboBoxPI(QWidget* parent, int i, const char* name = 0); int id() const { return pid; }; void setId(int i) { pid = i; }; }; } #endif // COMBOBOXPI_H muse-4.0.0/src/muse/widgets/ctrlcombo.cpp000066400000000000000000000071361404105311000203560ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ctrlcombo.cpp,v 1.1.1.1 2003/10/27 18:55:02 wschweer Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "ctrlcombo.h" namespace MusEGui { //--------------------------------------------------------- // CtrlComboBox //--------------------------------------------------------- CtrlComboBox::CtrlComboBox(QWidget* parent) : QComboBox(parent) { const char* ctxt[] = { "No Ctrl", "BankSelMSB", "Modulation", "BreathCtrl", "Control 3", "Foot Ctrl", "Porta Time", "DataEntMSB", "MainVolume", "Balance", "Control 9", "Pan", "Expression", "Control 12", "Control 13", "Control 14", "Control 15", "Gen.Purp.1", "Gen.Purp.2", "Gen.Purp.3", "Gen.Purp.4", "Control 20", "Control 21", "Control 22", "Control 23", "Control 24", "Control 25", "Control 26", "Control 27", "Control 28", "Control 29", "Control 30", "Control 31", "BankSelLSB", "Modul. LSB", "BrthCt.LSB", "Control 35", "FootCt.LSB", "Port.T LSB", "DataEntLSB", "MainVolLSB", "BalanceLSB", "Control 41", "Pan LSB", "Expr. LSB", "Control 44", "Control 45", "Control 46", "Control 47", "Gen.P.1LSB", "Gen.P.2LSB", "Gen.P.3LSB", "Gen.P.4LSB", "Control 52", "Control 53", "Control 54", "Control 55", "Control 56", "Control 57", "Control 58", "Control 59", "Control 60", "Control 61", "Control 62", "Control 63", "Sustain", "Porta Ped", "Sostenuto", "Soft Pedal", "Control 68", "Hold 2", "Control 70", "HarmonicCo", "ReleaseTime", "Attack Time", "Brightness", "Control 75", "Control 76", "Control 77", "Control 78", "Control 79", "Gen.Purp.5", "Gen.Purp.6", "Gen.Purp.7", "Gen.Purp.8", "Porta Ctrl", "Control 85", "Control 86", "Control 87", "Control 88", "Control 89", "Control 90", "Effect1Dep", "Effect2Dep", "Effect3Dep", "Effect4Dep", "Phaser Dep", "Data Incr", "Data Decr", "NRPN LSB", "NRPN MSB", "RPN LSB", "RPN MSB", "Control102", "Control103", "Control104", "Control105", "Control106", "Control107", "Control108", "Control109", "Control110", "Control111", "Control112", "Control113", "Control114", "Control115", "Control116", "Control117", "Control118", "Control119", "AllSndOff", "Reset Ctrl", "Local Ctrl", "AllNoteOff", "OmniModOff", "OmniModeOn", "MonoModeOn", "PolyModeOn" }; for (unsigned int i = 0; i < sizeof(ctxt)/sizeof(*ctxt); ++i) insertItem(i, QString(ctxt[i])); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/ctrlcombo.h000066400000000000000000000023271404105311000200200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ctrlcombo.h,v 1.1.1.1 2003/10/27 18:54:30 wschweer Exp $ // // (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __CTRLGRP_H__ #define __CTRLGRP_H__ #include namespace MusEGui { class CtrlComboBox : public QComboBox { Q_OBJECT public: CtrlComboBox(QWidget* parent); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/dimap.cpp000066400000000000000000000220441404105311000174570ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dimap.cpp,v 1.1.1.1 2003/10/27 18:55:11 wschweer Exp $ // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include "dimap.h" #include "mmath.h" namespace MusEGui { const double DiMap::LogMin = 1.0e-150; const double DiMap::LogMax = 1.0e150; // DiMap - Map a double interval into an integer interval // // The DiMap class maps an interval of type double into an interval of // type integer. It consists // of two intervals D = [d1, d2] (double) and I = [i1, i2] (int), which are // specified with the @DiMap::setDblRange@ and @DiMap::setIntRange@ // members. The point d1 is mapped to the point i1, and d2 is mapped to i2. // Any point inside or outside D can be mapped to a point inside or outside // I using @DiMap::transform@ or @DiMap::limTransform@ or vice versa // using @QwtPlot::invTransform@. D can be scaled linearly or // logarithmically, as specified with @DiMap::setDblRange@. //------------------------------------------------------------ //.F DiMap::DiMap (1) // Construct a DiMap instance. // //.u Syntax //.f DiMap::DiMap() // //.u Description // The double and integer intervals are both set to [0,1]. //------------------------------------------------------------ DiMap::DiMap() { d_x1 = 0.0; d_x2 = 1.0; d_y1 = 0; d_y2 = 1; d_cnv = 1.0; } //------------------------------------------------------------ //.F DiMap::DiMap (2) // Construct a DiMap instance with initial integer // and double intervals // //.u Syntax //.f DiMap::DiMap(int i1, int i2, double d1, double d2, bool logarithmic) // //.u Parameters //.p int i1 -- first border of integer interval // int i2 -- second border of integer interval // double d1 -- first border of double interval // double d2 -- second border of double interval // bool logarithmic -- logarithmic mapping, TRUE or FALSE. Defaults // to FALSE. //------------------------------------------------------------ DiMap::DiMap(int i1, int i2, double d1, double d2, bool logarithmic) { d_log = logarithmic; setIntRange(i1,i2); setDblRange(d1, d2); } //------------------------------------------------------------ //.F DiMap::~DiMap // Destroy a DiMap instance. // //.u Syntax //.f DiMap::~DiMap() //------------------------------------------------------------ DiMap::~DiMap() { } //------------------------------------------------------------ //.F DiMap::contains (1) // Returns TRUE if a value x lies inside or at the border of the // map's double range. // //.u Syntax //.f bool DiMap::contains(double x) // //.u Parameters //.p double x -- value //------------------------------------------------------------ bool DiMap::contains(double x) const { return ( (x >= MusECore::qwtMin(d_x1, d_x1)) && (x <= MusECore::qwtMax(d_x1, d_x2))); } //------------------------------------------------------------ //.F DiMap::contains (2) // Returns TRUE if a value x lies inside or at the border of the // map's integer range // //.u Syntax //.f bool DiMap::contains(int x) // //.u Parameters //.p int x -- value //------------------------------------------------------------ bool DiMap::contains(int x) const { return ( (x >= MusECore::qwtMin(d_y1, d_y1)) && (x <= MusECore::qwtMax(d_y1, d_y2))); } //------------------------------------------------------------ //.F DiMap::setDblRange // Specify the borders of the double interval // //.u Syntax //.f void DiMap::setDblRange(double d1, double d2, bool lg = FALSE) // //.u Parameters //.p double d1 -- first border // double d2 -- second border // bool lg -- logarithmic (TRUE) or linear (FALSE) // scaling. Defaults to FALSE. //------------------------------------------------------------ void DiMap::setDblRange(double d1, double d2, bool lg) { if (lg) { d_log = true; if (d1 < LogMin) d1 = LogMin; else if (d1 > LogMax) d1 = LogMax; if (d2 < LogMin) d2 = LogMin; else if (d2 > LogMax) d2 = LogMax; d_x1 = log(d1); d_x2 = log(d2); } else { d_log = false; d_x1 = d1; d_x2 = d2; } newFactor(); } //------------------------------------------------------------ //.F DiMap::setIntRange // Specify the borders of the integer interval // //.u Syntax //.f void DiMap::setIntRange(int i1, int i2) // //.u Parameters //.p int i1 -- first border // int i2 -- second border //------------------------------------------------------------ void DiMap::setIntRange(int i1, int i2) { d_y1 = i1; d_y2 = i2; newFactor(); } //------------------------------------------------------------ //.F DiMap::transform // Transform a point in double interval into an point in the // integer interval // //.u Syntax //.f int DiMap::transform(double x) // //.u Parameters //.p double x // //.u Return Value //.t // linear mapping: -- rint(i1 + (i2 - i1) / (d2 - d1) * (x - d1)) // logarithmic mapping: -- rint(i1 + (i2 - i1) / log(d2 / d1) * log(x / d1)) // //.u Note // The specified point is allowed to lie outside the intervals. If you // want to limit the returned value, use @DiMap::limTransform@. //------------------------------------------------------------ int DiMap::transform(double x) const { if (d_log) return (d_y1 + int(rint( (log(x) - d_x1) * d_cnv ))); else return (d_y1 + int(rint( (x - d_x1) * d_cnv ))); } //------------------------------------------------------------ //.F DiMap::invTransform // Transform an integer value into a double value // //.u Syntax //.f double DiMap::invTransform(int y) // //.u Parameters //.p int y -- integer value to be transformed // //.u Return Value //.t // linear mapping: -- d1 + (d2 - d1) / (i2 - i1) * (y - i1) // logarithmic mapping: -- d1 + (d2 - d1) / log(i2 / i1) * log(y / i1) //------------------------------------------------------------ double DiMap::invTransform(int y) const { if (d_cnv == 0.0) return 0.0; else { if (d_log) return exp(d_x1 + double(y - d_y1) / d_cnv ); else return ( d_x1 + double(y - d_y1) / d_cnv ); } } //------------------------------------------------------------ //.F DiMap::limTransform // Transform and limit // //.u Syntax //.f int DiMap::limTransform(double x) // //.u Parameters //.p double x // //.u Return Value // transformed value // //.u Description // The function is similar to @DiMap::transform@, but limits the input value // to the nearest border of the map's double interval if it lies outside // that interval. //------------------------------------------------------------ int DiMap::limTransform(double x) const { if ( x > MusECore::qwtMax(d_x1, d_x2) ) x = MusECore::qwtMax(d_x1, d_x2); else if ( x < MusECore::qwtMin(d_x1, d_x2)) x = MusECore::qwtMin(d_x1, d_x2); return transform(x); } //------------------------------------------------------------ //.F DiMap::xTransform // Exact transformation // //.u Syntax //.f double DiMap::dTransform(double x) // //.u Parameters //.p double x -- value to be transformed // //.u Return Value //.t // linear mapping: -- i1 + (i2 - i1) / (d2 - d1) * (x - d1) // logarithmic mapping: -- i1 + (i2 - i1) / log(d2 / d1) * log(x / d1) // //.u Description // This function is similar to @DiMap::transform@, but // makes the integer interval appear to be double. //------------------------------------------------------------ double DiMap::xTransform(double x) const { double rv; if (d_log) rv = double(d_y1) + (log(x) - d_x1) * d_cnv; else rv = double(d_y1) + (x - d_x1) * d_cnv; return rv; } //------------------------------------------------------------ //.F DiMap::newFactor // Re-calculate the conversion factor. //------------------------------------------------------------ void DiMap::newFactor() { if (d_x2 != d_x1) d_cnv = double(d_y2 - d_y1) / (d_x2 - d_x1); else d_cnv = 0.0; } } // namespace MusEGui muse-4.0.0/src/muse/widgets/dimap.h000066400000000000000000000041521404105311000171240ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: dimap.h,v 1.1.1.1 2003/10/27 18:54:28 wschweer Exp $ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DIMAP_H__ #define __DIMAP_H__ #include namespace MusEGui { class DiMap { double d_x1, d_x2; // double interval boundaries int d_y1,d_y2; // integer interval boundaries double d_cnv; // conversion factor bool d_log; // logarithmic scale? void newFactor(); public: static const double LogMin; static const double LogMax; DiMap(); DiMap(int, int, double, double, bool lg = false); ~DiMap(); bool contains(double x) const; bool contains(int x) const; void setIntRange(int i1, int i2); void setDblRange(double d1, double d2, bool lg = false); int transform(double x) const; double invTransform(int i) const; int limTransform(double x) const; double xTransform(double x) const; double d1() const { return d_x1;} double d2() const { return d_x2;} int i1() const { return d_y1;} int i2() const { return d_y2;} bool logarithmic() const { return d_log;} }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/doublespinbox.cpp000066400000000000000000000062671404105311000212530ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // doublespinbox.cpp (C) Copyright 2012 Tim E. Real (terminator356 at users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "doublespinbox.h" namespace MusEGui { void DoubleSpinBoxLineEdit::mouseDoubleClickEvent(QMouseEvent* e) { QLineEdit::mouseDoubleClickEvent(e); emit doubleClicked(); if((e->buttons() & Qt::LeftButton) && (e->modifiers() & Qt::ControlModifier)) emit ctrlDoubleClicked(); } //void DoubleSpinBoxLineEdit::mousePressEvent(QMouseEvent* e) //{ // QLineEdit::mousePressEvent(e); //selectAll(); // if((e->buttons() & Qt::LeftButton) && (e->modifiers() & Qt::ControlModifier)) // emit ctrlClicked(); //} //--------------------------------------------------------- // DoubleSpinBox //--------------------------------------------------------- DoubleSpinBox::DoubleSpinBox(QWidget* parent) : QDoubleSpinBox(parent) { DoubleSpinBoxLineEdit* le = new DoubleSpinBoxLineEdit(this); setLineEdit(le); setKeyboardTracking(false); connect(le, SIGNAL(doubleClicked()), this, SIGNAL(doubleClicked())); connect(le, SIGNAL(ctrlDoubleClicked()), this, SIGNAL(ctrlDoubleClicked())); //connect(le, SIGNAL(ctrlClicked()), this, SIGNAL(ctrlClicked())); } DoubleSpinBox::DoubleSpinBox(double minValue, double maxValue, double step, QWidget* parent) : QDoubleSpinBox(parent) { DoubleSpinBoxLineEdit* le = new DoubleSpinBoxLineEdit(this); setLineEdit(le); setRange(minValue, maxValue); setSingleStep(step); setKeyboardTracking(false); connect(le, SIGNAL(doubleClicked()), this, SIGNAL(doubleClicked())); connect(le, SIGNAL(ctrlDoubleClicked()), this, SIGNAL(ctrlDoubleClicked())); //connect(le, SIGNAL(ctrlClicked()), this, SIGNAL(ctrlClicked())); } void DoubleSpinBox::keyPressEvent(QKeyEvent* ev) { switch (ev->key()) { case Qt::Key_Return: QDoubleSpinBox::keyPressEvent(ev); emit returnPressed(); return; break; case Qt::Key_Escape: emit escapePressed(); return; break; default: break; } QDoubleSpinBox::keyPressEvent(ev); } void DoubleSpinBox::wheelEvent(QWheelEvent* e) { QDoubleSpinBox::wheelEvent(e); // Need this because Qt doesn't deselect the text if not focused. if(!hasFocus() && lineEdit()) lineEdit()->deselect(); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/doublespinbox.h000066400000000000000000000041071404105311000207070ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // doublespinbox.h (C) Copyright 2012 Tim E. Real (terminator356 at users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DOUBLESPINBOX_H__ #define __DOUBLESPINBOX_H__ #include #include namespace MusEGui { class DoubleSpinBoxLineEdit : public QLineEdit { Q_OBJECT protected: virtual void mouseDoubleClickEvent(QMouseEvent* e); //virtual void mousePressEvent(QMouseEvent* e); signals: void doubleClicked(); void ctrlDoubleClicked(); //void ctrlClicked(); public: DoubleSpinBoxLineEdit(QWidget* parent = 0) : QLineEdit(parent) {}; }; //--------------------------------------------------------- // DoubleSpinBox //--------------------------------------------------------- class DoubleSpinBox : public QDoubleSpinBox { Q_OBJECT protected: virtual void keyPressEvent(QKeyEvent*); virtual void wheelEvent(QWheelEvent*); signals: void doubleClicked(); void ctrlDoubleClicked(); //void ctrlClicked(); void returnPressed(); void escapePressed(); public: DoubleSpinBox(QWidget* parent=0); DoubleSpinBox(double minValue, double maxValue, double step = 1.0, QWidget* parent=0); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/drange.cpp000066400000000000000000000310451404105311000176260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: drange.cpp,v 1.2.2.1 2009/03/09 02:05:18 terminator356 Exp $ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include "mmath.h" #include "drange.h" #include "fastlog.h" #include "muse_math.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_DRANGE(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { const double DoubleRange::MinRelStep = 1.0e-10; const double DoubleRange::DefaultRelStep = 1.0e-2; const double DoubleRange::MinEps = 1.0e-10; //----------------------------------------------------------- // This class is useful as a base class or a member for sliders. // It represents an interval of type double within which a value can // be moved. The value can be either an arbitrary point inside // the interval (see @DoubleRange::setValue@), or it can be fitted // into a step raster (see @DoubleRange::fitValue@ and // @DoubleRange::incValue@). // // As a special case, a DoubleRange can be periodic, which means that // a value outside the interval will be mapped to a value inside the // interval when @DoubleRange::setValue@, @DoubleRange::fitValue@, // @DoubleRange::incValue@ or @DoubleRange::incPages@ are called. //------------------------------------------------------------ //--------------------------------------------------------- // doubleRange //--------------------------------------------------------- DoubleRange::DoubleRange() { d_minValue = 0; d_maxValue = 100.0; d_exactPrevValue = 0.0; d_exactValue = 0.0; d_value = 0.0; d_step = 0.1; d_periodic = false; d_log = false; d_integer = false; } //--------------------------------------------------------- // convertTo //--------------------------------------------------------- double DoubleRange::convertTo(double x, ConversionMode mode) const { switch(mode) { case ConvertNone: return x; case ConvertDefault: if(d_log) return muse_db2val(x); if(d_integer) return rint(x); return x; break; case ConvertInt: return rint(x); break; case ConvertLog: return muse_db2val(x); break; } return x; } //--------------------------------------------------------- // convertTo //--------------------------------------------------------- double DoubleRange::convertFrom(double x, ConversionMode mode) const { switch(mode) { case ConvertNone: return x; case ConvertDefault: if(d_log) { if(x == 0.0f) return d_minValue; else return MusECore::fast_log10(x) * 20.0f; } else if(d_integer) return rint(x); else return x; break; case ConvertInt: return rint(x); break; case ConvertLog: if(x == 0.0f) x = d_minValue; else x = MusECore::fast_log10(x) * 20.0f; break; } return x; } //--------------------------------------------------------- // setNewValue //--------------------------------------------------------- void DoubleRange::setNewValue(double x, bool align) { DEBUG_DRANGE(stderr, "DoubleRange::setNewValue TOP val:%.20f d_value:%.20f\n", x, d_value); if(x == d_value) return; const double vmin = MusECore::qwtMin(d_minValue, d_maxValue); const double vmax = MusECore::qwtMax(d_minValue, d_maxValue); const double prevValue = d_value; // Range check if (x < vmin) { if ((d_periodic) && (vmin != vmax)) d_value = x + ceil((vmin - x) / (vmax - vmin)) * (vmax - vmin); else d_value = vmin; } else if (x > vmax) { if ((d_periodic) && (vmin != vmax)) d_value = x - ceil( ( x - vmax) / (vmax - vmin )) * (vmax - vmin); else d_value = vmax; } else d_value = x; d_exactPrevValue = d_exactValue; d_exactValue = d_value; // align to grid if (align) { if (d_step != 0.0) d_value = d_minValue + rint((d_value - d_minValue) / d_step ) * d_step; else d_value = d_minValue; // correct rounding error at the border if (fabs(d_value - d_maxValue) < MinEps * MusECore::qwtAbs(d_step)) d_value = d_maxValue; // correct rounding error if value = 0 if (fabs(d_value) < MinEps * MusECore::qwtAbs(d_step)) d_value = 0.0; } DEBUG_DRANGE(stderr, " BOTTOM val:%.20f d_value:%.20f\n", x, d_value); if (prevValue != d_value) { DEBUG_DRANGE(stderr, " not equal, calling valueChange\n"); valueChange(); } } //--------------------------------------------------------- // fitValue // Adjust the value to the closest point in the step // raster. // The value is clipped when it lies outside the range. // When the range is @DoubleRange::periodic@, it will // be mapped to a point in the interval such that //--------------------------------------------------------- void DoubleRange::fitValue(double x, ConversionMode mode) { setNewValue(convertFrom(x, mode), true); } //--------------------------------------------------------- // setValue // Set a new value without adjusting to the step raster // The value is clipped when it lies outside the range. // When the range is @DoubleRange::periodic@, it will // be mapped to a point in the interval such that // // new value := x + n * (max. value - min. value) // // with an integer number n. //--------------------------------------------------------- void DoubleRange::setValue(double x, ConversionMode mode) { setNewValue(convertFrom(x, mode), false); } //--------------------------------------------------------- // setRange // Specify range and step size // - A change of the range changes the value if it lies outside the // new range. The current value // will *not* be adjusted to the new step raster. // - vmax < vmin is allowed. // - If the step size is left out or set to zero, it will be // set to 1/100 of the interval length. // - If the step size has an absurd value, it will be corrected // to a better one. //--------------------------------------------------------- void DoubleRange::setRange(double vmin, double vmax, double vstep, int pageSize, ConversionMode mode) { vmin = convertFrom(vmin, mode); vmax = convertFrom(vmax, mode); bool rchg = ((d_maxValue != vmax) || (d_minValue != vmin)); if(!rchg && vstep == d_step && pageSize == d_pageSize) // p4.0.45 return; if (rchg) { d_minValue = vmin; d_maxValue = vmax; } // // look if the step width has an acceptable // value or otherwise change it. // setStep(vstep); // // limit page size // d_pageSize = MusECore::qwtLim(pageSize,0, int(MusECore::qwtAbs((d_maxValue - d_minValue) / d_step))); // // If the value lies out of the range, it // will be changed. Note that it will not be adjusted to // the new step width. setNewValue(d_value, false); // call notifier after the step width has been // adjusted. if (rchg) rangeChange(); } //--------------------------------------------------------- // setStep // Change the step raster // // The value will *not* be adjusted to the new step raster. //--------------------------------------------------------- void DoubleRange::setStep(double vstep) { double newStep,intv; intv = d_maxValue - d_minValue; if (vstep == 0.0) newStep = intv * DefaultRelStep; else { if (((intv > 0) && (vstep < 0)) || ((intv < 0) && (vstep > 0))) newStep = -vstep; else newStep = vstep; if ( fabs(newStep) < fabs(MinRelStep * intv) ) newStep = MinRelStep * intv; } if (newStep != d_step) { d_step = newStep; DEBUG_DRANGE(stderr, "DoubleRange::setStep vstep:%.20f d_step:%.20f\n", vstep, d_step); stepChange(); } } //--------------------------------------------------------- // setPeriodic // Make the range periodic // // When the range is periodic, the value will be set to a point // inside the interval such that // // point = value + n * width // // if the user tries to set a new value which is outside the range. // If the range is nonperiodic (the default), values outside the // range will be clipped. //--------------------------------------------------------- void DoubleRange::setPeriodic(bool tf) { d_periodic = tf; } //------------------------------------------------------------ // incValue // Increment the value by a specified number of steps // // As a result of this operation, the new value will always be // adjusted to the step raster. //------------------------------------------------------------ void DoubleRange::incValue(int nSteps) { setNewValue(d_value + double(nSteps) * d_step, true); } //--------------------------------------------------------- // incPages // Increment the value by a specified number of pages //--------------------------------------------------------- void DoubleRange::incPages(int nPages) { setNewValue(d_value + double(nPages) * double(d_pageSize) * d_step, true); } //--------------------------------------------------------- // step //--------------------------------------------------------- double DoubleRange::step() const { return MusECore::qwtAbs(d_step); } //--------------------------------------------------------- // value //--------------------------------------------------------- double DoubleRange::value(ConversionMode mode) const { return convertTo(d_value, mode); } //--------------------------------------------------------- // minLogValue //--------------------------------------------------------- //double AbstractSlider::minValue() const { // return _log ? pow(10.0, _minValue*0.05f) : _minValue; //} //--------------------------------------------------------- // setMinLogValue //--------------------------------------------------------- void DoubleRange::setMinLogValue(double val) { if (d_log) { if (val == 0.0f) d_minValue = -100; else d_minValue = MusECore::fast_log10(val) * 20.0f; } else d_minValue = val; } //--------------------------------------------------------- // maxLogValue //--------------------------------------------------------- //double AbstractSlider::maxValue() const { // return _log ? pow(10.0, _maxValue*0.05f) : _maxValue; //} //--------------------------------------------------------- // setMaxLogValue //--------------------------------------------------------- void DoubleRange::setMaxLogValue(double val) { if (d_log) { d_maxValue = MusECore::fast_log10(val) * 20.0f; } else d_maxValue = val; } void DoubleRange::setLogRange(double a, double b, double vstep, int pagesize) { // setMinLogValue(a); // setMaxLogValue(b); double mn = 0, mx = 0; if(d_log) { if(a == 0.0f) d_minValue = -100; else mn = MusECore::fast_log10(a) * 20.0f; } else mn = a; if(d_log) mx = MusECore::fast_log10(b) * 20.0f; else mx = b; setRange(mn, mx, vstep, pagesize); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/drange.h000066400000000000000000000071101404105311000172670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: drange.h,v 1.1.1.1.2.1 2007/01/27 14:52:43 spamatica Exp $ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DOUBLE_RANGE_H__ #define __DOUBLE_RANGE_H__ namespace MusEGui { //--------------------------------------------------------- // DoubleRange //--------------------------------------------------------- class DoubleRange { static const double MinRelStep; static const double DefaultRelStep; static const double MinEps; double d_minValue; double d_maxValue; double d_step; int d_pageSize; double d_value; double d_exactValue; double d_exactPrevValue; bool d_periodic; bool d_log; bool d_integer; void setNewValue(double x, bool align = false); public: enum ConversionMode { ConvertNone, ConvertDefault, ConvertInt, ConvertLog }; protected: double convertFrom(double x, ConversionMode mode = ConvertDefault) const; double convertTo(double x, ConversionMode mode = ConvertDefault) const; double exactValue(ConversionMode mode = ConvertDefault) const { return convertTo(d_exactValue, mode); } double exactPrevValue(ConversionMode mode = ConvertDefault) const { return convertTo(d_exactPrevValue, mode); } virtual void valueChange() {} virtual void stepChange() {} virtual void rangeChange() {} public: DoubleRange(); virtual ~DoubleRange(){} double value(ConversionMode mode = ConvertDefault) const; virtual void setValue(double x, ConversionMode mode = ConvertDefault); virtual void fitValue(double x, ConversionMode mode = ConvertDefault); virtual void incValue(int); virtual void incPages(int); void setPeriodic(bool tf); void setRange (double vmin, double vmax, double vstep = 0.0, int pagesize = 1, ConversionMode mode = ConvertDefault); void setLogRange(double vmin, double vmax, double vstep = 0.0, int pagesize = 1); void setStep(double); double maxValue(ConversionMode mode = ConvertDefault) const { return convertTo(d_maxValue, mode); } void setMaxLogValue(double v); double minValue(ConversionMode mode = ConvertDefault) const { return convertTo(d_minValue, mode); } void setMinLogValue(double v); bool periodic() const { return d_periodic; } int pageSize() const { return d_pageSize; } double step() const; bool log() const { return d_log; } void setLog(bool v) { d_log = v; } bool integer() const { return d_integer; } void setInteger(bool v) { d_integer = v; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/knob.cpp000066400000000000000000000463651404105311000173320ustar00rootroot00000000000000//====================================================================== // MusE // Linux Music Editor // $Id: knob.cpp,v 1.3.2.3 2009/03/09 02:05:18 terminator356 Exp $ // // Adapted from Qwt Lib: // Copyright (C) 1997 Josef Wilgen // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) completely redesigned. // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "knob.h" #include "muse_math.h" #include "mmath.h" #include #include #include #include // For debugging output: Uncomment the fprintf section. #define DEBUG_KNOB(dev, format, args...) //fprintf(dev, format, ##args); namespace MusEGui { //--------------------------------------------------------- // The QwtKnob widget imitates look and behaviour of a volume knob on a radio. // It contains // a scale around the knob which is set up automatically or can // be configured manually (see @^QwtScaleIf@). // Automatic scrolling is enabled when the user presses a mouse // button on the scale. For a description of signals, slots and other // members, see QwtSliderBase@. //--------------------------------------------------------- //--------------------------------------------------------- // Knob //--------------------------------------------------------- Knob::Knob(QWidget* parent, const char* name) : SliderBase(parent, name) { hasScale = false; d_borderWidth = 4; d_shineWidth = 3; d_totalAngle = 270.0; d_scaleDist = 1; d_symbol = Line; d_maxScaleTicks = 11; d_knobWidth = 30; _faceColSel = false; d_faceColor = palette().color(QPalette::Window); d_rimColor = palette().mid().color(); d_shinyColor = palette().mid().color(); d_curFaceColor = d_faceColor; d_altFaceColor = d_faceColor; d_markerColor = palette().dark().color().darker(125); d_dotWidth = 8; l_slope = 0; l_const = 100; setMinimumSize(30,30); setUpdateTime(50); } //------------------------------------------------------------ // QwtKnob::setTotalAngle // Set the total angle by which the knob can be turned // // Syntax // void QwtKnob::setTotalAngle(double angle) // // Parameters // double angle -- angle in degrees. // // Description // The default angle is 270 degrees. It is possible to specify // an angle of more than 360 degrees so that the knob can be // turned several times around its axis. //------------------------------------------------------------ void Knob::setTotalAngle (double angle) { if (angle < 10.0) d_totalAngle = 10.0; else d_totalAngle = angle; d_scale.setAngleRange( -0.5 * d_totalAngle, 0.5 * d_totalAngle); } //------------------------------------------------------------ // Knob::setRange // Set the range and step size of the knob // // Sets the parameters that define the shininess of the ring // surrounding the knob and then proceeds by passing the // parameters to the parent class' setRange() function. //------------------------------------------------------------ void Knob::setRange(double vmin, double vmax, double vstep, int pagesize) { // divide by zero protection. probably too cautious if (! (vmin == vmax || qMax(-vmin, vmax) == 0)) { if (vmin * vmax < 0) l_slope = 80.0 / qMax(-vmin, vmax); else { l_slope = 80.0 / (vmax - vmin); l_const = 100 - l_slope * vmin; } } SliderBase::setRange(vmin, vmax, vstep, pagesize); } //------------------------------------------------------------ // QwtKnob::drawKnob // const QRect &r -- borders of the knob //------------------------------------------------------------ void Knob::drawKnob(QPainter* p, const QRect& r) { const QPalette& pal = palette(); QRect aRect; aRect.setRect(r.x() + d_borderWidth, r.y() + d_borderWidth, r.width() - 2*d_borderWidth, r.height() - 2*d_borderWidth); int width = r.width(); int height = r.height(); int size = qMin(width, height); p->setRenderHint(QPainter::Antialiasing, true); // // draw the rim // QLinearGradient linearg(QPoint(r.x(),r.y()), QPoint(size, size)); linearg.setColorAt(1 - M_PI_4, d_faceColor.lighter(125)); linearg.setColorAt(M_PI_4, d_faceColor.darker(175)); p->setBrush(linearg); p->setPen(Qt::NoPen); p->drawEllipse(r.x(),r.y(),size,size); // // draw shiny surrounding // QPen pn; pn.setCapStyle(Qt::FlatCap); pn.setColor(d_shinyColor.lighter(l_const + fabs(value() * l_slope))); pn.setWidth(d_shineWidth * 2); p->setPen(pn); p->drawArc(aRect, 0, 360 * 16); // // draw button face // QRadialGradient gradient(size/2, size/2, size-d_borderWidth, size/2-d_borderWidth, size/2-d_borderWidth); gradient.setColorAt(0, d_curFaceColor.lighter(150)); gradient.setColorAt(1, d_curFaceColor.darker(150)); p->setBrush(gradient); p->setPen(Qt::NoPen); p->drawEllipse(aRect); // // draw marker // //drawMarker(p, d_angle, isEnabled() ? d_markerColor : Qt::gray); drawMarker(p, d_angle, pal.currentColorGroup() == QPalette::Disabled ? pal.color(QPalette::Disabled, QPalette::WindowText) : d_markerColor); } //------------------------------------------------------------ //.F QwtSliderBase::valueChange // Notify change of value // //.u Parameters // double x -- new value // //.u Description // Sets the slider's value to the nearest multiple // of the step size. //------------------------------------------------------------ void Knob::valueChange() { recalcAngle(); d_newVal++; repaint(kRect); // HACK // In direct mode let the inherited classes (this) call these in their valueChange() methods, // so that they may be called BEFORE valueChanged signal is emitted by the setPosition() call above. // ScrDirect mode only happens once upon press with a modifier. After that, another mode is set. // Hack: Since valueChange() is NOT called if nothing changed, in that case these are called for us by the SliderBase. if(d_scrollMode == ScrDirect) { processSliderPressed(id()); emit sliderPressed(value(), id()); } // Emits valueChanged if tracking enabled. SliderBase::valueChange(); } //------------------------------------------------------------ //.F QwtKnob::getValue // Determine the value corresponding to a specified position // //.u Parameters: // const QPoint &p -- point // //.u Description: // Called by QwtSliderBase //------------------------------------------------------------ double Knob::getValue(const QPoint &p) { double newValue; double oneTurn; double eqValue; double arc; const QRect& r = rect(); double dx = double((r.x() + r.width() / 2) - p.x() ); double dy = double((r.y() + r.height() / 2) - p.y() ); arc = atan2(-dx,dy) * 180.0 / M_PI; newValue = 0.5 * (minValue() + maxValue()) + (arc + d_nTurns * 360.0) * (maxValue() - minValue()) / d_totalAngle; oneTurn = fabs(maxValue() - minValue()) * 360.0 / d_totalAngle; eqValue = value() + d_mouseOffset; if (fabs(newValue - eqValue) > 0.5 * oneTurn) { if (newValue < eqValue) newValue += oneTurn; else newValue -= oneTurn; } return newValue; } //------------------------------------------------------------ // //.F Knob::moveValue // Determine the value corresponding to a specified mouse movement. // //.u Syntax //.f void SliderBase::moveValue(const QPoint &deltaP, bool fineMode) // //.u Parameters //.p const QPoint &deltaP -- Change in position //.p bool fineMode -- Fine mode if true, coarse mode if false. // //.u Description // Called by SliderBase // Coarse mode (the normal mode) maps pixels to values depending on range and width, // such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. //------------------------------------------------------------ double Knob::moveValue(const QPoint &deltaP, bool /*fineMode*/) { // FIXME: To make fine mode workable, we need a way to make the adjustments 'multi-turn'. double oneTurn; double eqValue; const QRect& r = rect(); const QPoint new_p = _lastMousePos + deltaP; const int cx = r.x() + r.width() / 2; const int cy = r.y() + r.height() / 2; const double last_dx = double(cx - _lastMousePos.x()); const double last_dy = double(cy - _lastMousePos.y()); const double last_arc = atan2(-last_dx, last_dy) * 180.0 / M_PI; const double dx = double(cx - new_p.x()); const double dy = double(cy - new_p.y()); const double arc = atan2(-dx, dy) * 180.0 / M_PI; const double val = value(ConvertNone); // if((fineMode || borderlessMouse()) && d_scrollMode != ScrDirect) // { // const double arc_diff = arc - last_arc; // const double dval_diff = arc_diff * step(); // const double new_val = val + dval_diff; // d_valAccum = new_val; // Reset. // return d_valAccum; // } const double min = minValue(ConvertNone); const double max = maxValue(ConvertNone); const double drange = max - min; const double last_val = 0.5 * (min + max) + (last_arc + d_nTurns * 360.0) * drange / d_totalAngle; const double new_val = 0.5 * (min + max) + (arc + d_nTurns * 360.0) * drange / d_totalAngle; double dval_diff = new_val - last_val; //if(fineMode) // dval_diff /= 10.0; d_valAccum += dval_diff; DEBUG_KNOB(stderr, "Knob::moveValue value:%.20f last_val:%.20f new_val:%.20f p dx:%d dy:%d drange:%.20f step:%.20f dval_diff:%.20f d_valAccum:%.20f\n", val, last_val, new_val, deltaP.x(), deltaP.y(), drange, step(), dval_diff, d_valAccum); oneTurn = fabs(drange) * 360.0 / d_totalAngle; eqValue = val + d_mouseOffset; DEBUG_KNOB(stderr, " oneTurn:%.20f eqValue:%.20f\n", oneTurn, eqValue); if(fabs(d_valAccum - eqValue) > 0.5 * oneTurn) { if (d_valAccum < eqValue) { d_valAccum += oneTurn; DEBUG_KNOB(stderr, " added one turn, new d_valAccum:%.20f\n", d_valAccum); } else { d_valAccum -= oneTurn; DEBUG_KNOB(stderr, " subtracted one turn, new d_valAccum:%.20f\n", d_valAccum); } } return d_valAccum; } //------------------------------------------------------------ //.- //.F QwtKnob::setScrollMode // Determine the scrolling mode and direction // corresponding to a specified position // //.u Parameters // const QPoint &p -- point in question // //.u Description // Called by QwtSliderBase //------------------------------------------------------------ void Knob::getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction) { // If modifier or button is held, jump directly to the position at first. // After handling it, the caller can change to SrcMouse scroll mode. if(modifiers & Qt::ControlModifier || button == Qt::MidButton) { scrollMode = ScrDirect; direction = 0; return; } int dx, dy, r; double arc; /*Qt::ButtonState but= button ;*/ // prevent compiler warning : unsused variable r = kRect.width() / 2; dx = kRect.x() + r - p.x(); dy = kRect.y() + r - p.y(); if ( (dx * dx) + (dy * dy) <= (r * r)) // point is inside the knob { scrollMode = ScrMouse; direction = 0; } else // point lies outside { scrollMode = ScrTimer; arc = atan2(double(-dx),double(dy)) * 180.0 / M_PI; if ( arc < d_angle) direction = -1; else if (arc > d_angle) direction = 1; else direction = 0; } return; } //------------------------------------------------------------ //.F QwtKnob::rangeChange // Notify a change of the range // //.u Description // Called by QwtSliderBase //------------------------------------------------------------ void Knob::rangeChange() { if (!hasUserScale()) { d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor); } recalcAngle(); resize(size()); repaint(); } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void Knob::resizeEvent(QResizeEvent* ev) { SliderBase::resizeEvent(ev); int width, width_2; const QRect& r = rect(); // printf("resize %d %d %d\n", r.height(), r.width(), d_knobWidth); // width = MusECore::qwtMin(MusECore::qwtMin(r.height(), r.width()), d_knobWidth); width = MusECore::qwtMin(r.height(), r.width()); width_2 = width / 2; int x = r.x() + r.width() / 2 - width_2; int y = r.y() + r.height() / 2 - width_2; kRect.setRect(x, y, width, width); x = kRect.x() - d_scaleDist; y = kRect.y() - d_scaleDist; int w = width + 2 * d_scaleDist; d_scale.setGeometry(x, y, w, ScaleDraw::Round); } //------------------------------------------------------------ // paintEvent //------------------------------------------------------------ void Knob::paintEvent(QPaintEvent*) { /* QPainter p(this); const QRect &r = e->rect(); if ((r == kRect) && d_newVal ) { // event from valueChange() if (d_newVal > 1) // lost paintEvents()? drawKnob(&p, kRect); else { drawMarker(&p, d_oldAngle, d_curFaceColor); drawMarker(&p, d_angle, d_markerColor); } } else { p.eraseRect(rect()); if (hasScale) d_scale.draw(&p); drawKnob(&p, kRect); } d_newVal = 0; */ QPainter p(this); p.setRenderHint(QPainter::Antialiasing, true); if(hasScale) d_scale.draw(&p, palette()); drawKnob(&p, kRect); //drawMarker(&p, d_oldAngle, d_curFaceColor); //drawMarker(&p, d_angle, d_markerColor); d_newVal = 0; } //------------------------------------------------------------ //.- //.F QwtKnob::drawMarker // Draw the marker at the knob's front // //.u Parameters //.p QPainter *p -- painter // double arc -- angle of the marker // const QColor &c -- marker color // //.u Syntax // void QwtKnob::drawMarker(QPainter *p) // //------------------------------------------------------------ void Knob::drawMarker(QPainter *p, double arc, const QColor &c) { QPen pn; int radius; double rb,re; double rarc; rarc = arc * M_PI / 180.0; double ca = cos(rarc); double sa = - sin(rarc); radius = kRect.width() / 2 - d_borderWidth + d_shineWidth; if (radius < 3) radius = 3; int ym = kRect.y() + radius + d_borderWidth - d_shineWidth; int xm = kRect.x() + radius + d_borderWidth - d_shineWidth; switch (d_symbol) { case Dot: p->setBrush(c); p->setPen(Qt::NoPen); rb = double(MusECore::qwtMax(radius - 4 - d_dotWidth / 2, 0)); p->drawEllipse(xm - int(rint(sa * rb)) - d_dotWidth / 2, ym - int(rint(ca * rb)) - d_dotWidth / 2, d_dotWidth, d_dotWidth); break; case Line: pn.setColor(c); pn.setWidth(2); p->setPen(pn); rb = MusECore::qwtMax(double((radius - 1) / 3.0), 0.0); re = MusECore::qwtMax(double(radius - 1), 0.0); p->setRenderHint(QPainter::Antialiasing, true); p->drawLine( xm, ym, xm - int(rint(sa * re)), ym - int(rint(ca * re))); break; } } //------------------------------------------------------------ // //.F QwtKnob::setKnobWidth // Change the knob's width. // //.u Syntax //.f void QwtKnob::setKnobWidth(int w) // //.u Parameters //.p int w -- new width // //.u Description // The specified width must be >= 5, or it will be clipped. // //------------------------------------------------------------ void Knob::setKnobWidth(int w) { d_knobWidth = MusECore::qwtMax(w,5); resize(size()); repaint(); } //------------------------------------------------------------ // //.F QwtKnob::setBorderWidth // Set the knob's border width // //.u Syntax //.f void QwtKnob::setBorderWidth(int bw) // //.u Parameters //.p int bw -- new border width // //------------------------------------------------------------ void Knob::setBorderWidth(int bw) { d_borderWidth = MusECore::qwtMax(bw, 0); resize(size()); repaint(); } //------------------------------------------------------------ //.- //.F QwtKnob::recalcAngle // Recalculate the marker angle corresponding to the // current value // //.u Syntax //.f void QwtKnob::recalcAngle() // //------------------------------------------------------------ void Knob::recalcAngle() { d_oldAngle = d_angle; // // calculate the angle corresponding to the value // if (maxValue() == minValue()) { d_angle = 0; d_nTurns = 0; } else { d_angle = (value() - 0.5 * (minValue() + maxValue())) / (maxValue() - minValue()) * d_totalAngle; d_nTurns = floor((d_angle + 180.0) / 360.0); d_angle = d_angle - d_nTurns * 360.0; } } //------------------------------------------------------------ // setFaceColor //------------------------------------------------------------ void Knob::setFaceColor(const QColor c) { d_faceColor = c; if(!_faceColSel) //update(FALSE); repaint(); } //------------------------------------------------------------ // setAltFaceColor //------------------------------------------------------------ void Knob::setAltFaceColor(const QColor c) { d_altFaceColor = c; if(_faceColSel) //update(FALSE); repaint(); } //------------------------------------------------------------ // selectFaceColor //------------------------------------------------------------ void Knob::selectFaceColor(bool alt) { _faceColSel = alt; if(alt) d_curFaceColor = d_altFaceColor; else d_curFaceColor = d_faceColor; //update(FALSE); repaint(); } //------------------------------------------------------------ // setMarkerColor //------------------------------------------------------------ void Knob::setMarkerColor(const QColor c) { d_markerColor = c; //update(FALSE); repaint(); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/knob.h000066400000000000000000000067641404105311000167760ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/knob.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) completely redesigned. // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef QWT_KNOB_H #define QWT_KNOB_H #include "sliderbase.h" #include "sclif.h" #include #include #include namespace MusEGui { //--------------------------------------------------------- // Knob //--------------------------------------------------------- class Knob : public SliderBase, public ScaleIf { Q_OBJECT public: enum Symbol { Line, Dot }; enum KnobType { panType, auxType, gainType, }; protected: bool hasScale; int d_borderWidth; int d_shineWidth; int d_scaleDist; int d_maxScaleTicks; int d_newVal; int d_knobWidth; int d_dotWidth; Symbol d_symbol; double d_angle; double d_oldAngle; double d_totalAngle; double d_nTurns; double l_const; double l_slope; QRect kRect; bool _faceColSel; QColor d_faceColor; QColor d_shinyColor; QColor d_rimColor; QColor d_curFaceColor; QColor d_altFaceColor; QColor d_markerColor; void recalcAngle(); void valueChange(); void rangeChange(); virtual void drawKnob(QPainter *p, const QRect &r); void drawMarker(QPainter *p, double arc, const QColor &c); virtual void paintEvent(QPaintEvent *); virtual void resizeEvent(QResizeEvent *e); double getValue(const QPoint &p); // Determine the value corresponding to a specified mouse movement. double moveValue(const QPoint& /*deltaP*/, bool /*fineMode*/ = false); void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction ); void scaleChange() { repaint(); } void fontChange(const QFont &) { repaint(); } public: Knob(QWidget* parent = 0, const char *name = 0); ~Knob() {} void setRange(double vmin, double vmax, double vstep = 0.0, int pagesize = 1); void setKnobWidth(int w); void setTotalAngle (double angle); void setBorderWidth(int bw); void selectFaceColor(bool alt); bool selectedFaceColor() { return _faceColSel; } QColor faceColor() { return d_faceColor; } void setFaceColor(const QColor c); QColor altFaceColor() { return d_altFaceColor; } void setAltFaceColor(const QColor c); QColor markerColor() { return d_markerColor; } void setMarkerColor(const QColor c); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/knob_and_meter.cpp000066400000000000000000000421541404105311000213400ustar00rootroot00000000000000//====================================================================== // MusE // Linux Music Editor // knob_and_meter.cpp // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "knob_and_meter.h" #include "muse_math.h" #include "mmath.h" #include #include #include #include #include namespace MusEGui { //--------------------------------------------------------- // The QwtKnob widget imitates look and behaviour of a volume knob on a radio. // It contains // a scale around the knob which is set up automatically or can // be configured manually (see @^QwtScaleIf@). // Automatic scrolling is enabled when the user presses a mouse // button on the scale. For a description of signals, slots and other // members, see QwtSliderBase@. //--------------------------------------------------------- //--------------------------------------------------------- // KnobWithMeter //--------------------------------------------------------- KnobWithMeter::KnobWithMeter(QWidget* parent, const char* name) : Knob(parent, name) { // hasScale = false; // // d_borderWidth = 4; // d_shineWidth = 3; // d_totalAngle = 270.0; // d_scaleDist = 1; // d_symbol = Line; // d_maxScaleTicks = 11; // d_knobWidth = 30; // _faceColSel = FALSE; // d_faceColor = palette().color(QPalette::Window); // d_rimColor = palette().mid().color(); // d_shinyColor = palette().mid().color(); // d_curFaceColor = d_faceColor; // d_altFaceColor = d_faceColor; // d_markerColor = palette().dark().color().darker(125); // d_dotWidth = 8; // // l_slope = 0; // l_const = 100; // // setMinimumSize(30,30); // setUpdateTime(50); } //------------------------------------------------------------ // QwtKnob::setTotalAngle // Set the total angle by which the knob can be turned // // Syntax // void QwtKnob::setTotalAngle(double angle) // // Parameters // double angle -- angle in degrees. // // Description // The default angle is 270 degrees. It is possible to specify // an angle of more than 360 degrees so that the knob can be // turned several times around its axis. //------------------------------------------------------------ // void Knob::setTotalAngle (double angle) // { // if (angle < 10.0) // d_totalAngle = 10.0; // else // d_totalAngle = angle; // d_scale.setAngleRange( -0.5 * d_totalAngle, 0.5 * d_totalAngle); // } // //------------------------------------------------------------ // Knob::setRange // Set the range and step size of the knob // // Sets the parameters that define the shininess of the ring // surrounding the knob and then proceeds by passing the // parameters to the parent class' setRange() function. //------------------------------------------------------------ // void Knob::setRange(double vmin, double vmax, double vstep, int pagesize) // { // //if(vmin == d_minValue && vmax == d_maxValue && vstep == d_step && pageSize == d_pageSize) // p4.0.45 // // return; // // // divide by zero protection. probably too cautious // if (! (vmin == vmax || qMax(-vmin, vmax) == 0)) // { // if (vmin * vmax < 0) // l_slope = 80.0 / qMax(-vmin, vmax); // else // { // l_slope = 80.0 / (vmax - vmin); // l_const = 100 - l_slope * vmin; // } // } // SliderBase::setRange(vmin, vmax, vstep, pagesize); // } // //------------------------------------------------------------ // QwtKnob::drawKnob // const QRect &r -- borders of the knob //------------------------------------------------------------ void KnobWithMeter::drawKnob(QPainter* p, const QRect& r) { const QPalette& pal = palette(); QRect aRect; aRect.setRect(kRect.x() + d_borderWidth, kRect.y() + d_borderWidth, kRect.width() - 2*d_borderWidth, kRect.height() - 2*d_borderWidth); int width = kRect.width(); int height = kRect.height(); int size = qMin(width, height); p->setRenderHint(QPainter::Antialiasing, true); QPainterPath drawingPath, updatePath, finalPath, cornerPath; // // draw the rim // QLinearGradient linearg(QPoint(r.x(),r.y()), QPoint(size, size)); linearg.setColorAt(1 - M_PI_4, d_faceColor.lighter(125)); linearg.setColorAt(M_PI_4, d_faceColor.darker(175)); p->setBrush(linearg); p->setPen(Qt::NoPen); p->drawEllipse(r.x(),r.y(),size,size); // // draw shiny surrounding // QPen pn; pn.setCapStyle(Qt::FlatCap); pn.setColor(d_shinyColor.lighter(l_const + fabs(value() * l_slope))); pn.setWidth(d_shineWidth * 2); p->setPen(pn); p->drawArc(aRect, 0, 360 * 16); // // draw button face // QRadialGradient gradient(size/2, size/2, size-d_borderWidth, size/2-d_borderWidth, size/2-d_borderWidth); gradient.setColorAt(0, d_curFaceColor.lighter(150)); gradient.setColorAt(1, d_curFaceColor.darker(150)); p->setBrush(gradient); p->setPen(Qt::NoPen); p->drawEllipse(aRect); // // draw marker // //drawMarker(p, d_angle, isEnabled() ? d_markerColor : Qt::gray); drawMarker(p, d_angle, pal.currentColorGroup() == QPalette::Disabled ? pal.color(QPalette::Disabled, QPalette::WindowText) : d_markerColor); } //------------------------------------------------------------ //.F QwtSliderBase::valueChange // Notify change of value // //.u Parameters // double x -- new value // //.u Description // Sets the slider's value to the nearest multiple // of the step size. //------------------------------------------------------------ // void Knob::valueChange() // { // recalcAngle(); // d_newVal++; // repaint(kRect); // SliderBase::valueChange(); // } // //------------------------------------------------------------ //.F QwtKnob::getValue // Determine the value corresponding to a specified position // //.u Parameters: // const QPoint &p -- point // //.u Description: // Called by QwtSliderBase //------------------------------------------------------------ // double Knob::getValue(const QPoint &p) // { // double newValue; // double oneTurn; // double eqValue; // double arc; // // const QRect& r = rect(); // // double dx = double((r.x() + r.width() / 2) - p.x() ); // double dy = double((r.y() + r.height() / 2) - p.y() ); // // arc = atan2(-dx,dy) * 180.0 / M_PI; // // newValue = 0.5 * (minValue() + maxValue()) // + (arc + d_nTurns * 360.0) * (maxValue() - minValue()) // / d_totalAngle; // // oneTurn = fabs(maxValue() - minValue()) * 360.0 / d_totalAngle; // eqValue = value() + d_mouseOffset; // // if (fabs(newValue - eqValue) > 0.5 * oneTurn) // { // if (newValue < eqValue) // newValue += oneTurn; // else // newValue -= oneTurn; // } // // return newValue; // // } // //------------------------------------------------------------ //.- //.F QwtKnob::setScrollMode // Determine the scrolling mode and direction // corresponding to a specified position // //.u Parameters // const QPoint &p -- point in question // //.u Description // Called by QwtSliderBase //------------------------------------------------------------ // void Knob::getScrollMode( QPoint &p, const Qt::MouseButton &/*button*/, const Qt::KeyboardModifiers& /*modifiers*/, int &scrollMode, int &direction)// prevent compiler warning : unsused parameter // { // int dx, dy, r; // double arc; // // /*Qt::ButtonState but= button ;*/ // prevent compiler warning : unsused variable // r = kRect.width() / 2; // // dx = kRect.x() + r - p.x(); // dy = kRect.y() + r - p.y(); // // if ( (dx * dx) + (dy * dy) <= (r * r)) // point is inside the knob // { // scrollMode = ScrMouse; // direction = 0; // } // else // point lies outside // { // scrollMode = ScrTimer; // arc = atan2(double(-dx),double(dy)) * 180.0 / M_PI; // if ( arc < d_angle) // direction = -1; // else if (arc > d_angle) // direction = 1; // else // direction = 0; // } // return; // } // //------------------------------------------------------------ //.F QwtKnob::rangeChange // Notify a change of the range // //.u Description // Called by QwtSliderBase //------------------------------------------------------------ // void Knob::rangeChange() // { // if (!hasUserScale()) // { // d_scale.setScale(minValue(), maxValue(), // d_maxMajor, d_maxMinor); // } // recalcAngle(); // resize(size()); // repaint(); // } void KnobWithMeter::mousePressEvent(QMouseEvent *e) { // if (e->button() == Qt::MidButton || e->modifiers() & Qt::ControlModifier) { // int xpos = e->x() - width() /2; // double v = float(e->y()) / height() * 1.2; // // double halfRange = (maxValue() - minValue())/2; // double midValue = minValue() + halfRange; // // apply to range // if (xpos < 0) { // left values // v = -v; // } // setValue(v * halfRange + midValue); // SliderBase::valueChange(); // emit sliderMoved(value(),id()); // sliderMoved is used by auxChanged // // // fake a left-click to make the knob still "stick" to // // the mouse. // QMouseEvent temp(e->type(), e->pos(), Qt::LeftButton, e->buttons(), e->modifiers()); // SliderBase::mousePressEvent(&temp); // return; // } Knob::mousePressEvent(e); } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void KnobWithMeter::resizeEvent(QResizeEvent* ev) { Knob::resizeEvent(ev); // int width, width_2; // // const QRect& r = rect(); // // // printf("resize %d %d %d\n", r.height(), r.width(), d_knobWidth); // // // width = MusECore::qwtMin(MusECore::qwtMin(r.height(), r.width()), d_knobWidth); // width = MusECore::qwtMin(r.height(), r.width()); // width_2 = width / 2; // // int x = r.x() + r.width() / 2 - width_2; // int y = r.y() + r.height() / 2 - width_2; // // kRect.setRect(x, y, width, width); // // x = kRect.x() - d_scaleDist; // y = kRect.y() - d_scaleDist; // int w = width + 2 * d_scaleDist; // // d_scale.setGeometry(x, y, w, ScaleDraw::Round); } //------------------------------------------------------------ // paintEvent //------------------------------------------------------------ void KnobWithMeter::paintEvent(QPaintEvent* e) { /* QPainter p(this); const QRect &r = e->rect(); if ((r == kRect) && d_newVal ) { // event from valueChange() if (d_newVal > 1) // lost paintEvents()? drawKnob(&p, kRect); else { drawMarker(&p, d_oldAngle, d_curFaceColor); drawMarker(&p, d_angle, d_markerColor); } } else { p.eraseRect(rect()); if (hasScale) d_scale.draw(&p); drawKnob(&p, kRect); } d_newVal = 0; */ const QRect &r = e->rect(); QPainter p(this); p.setRenderHint(QPainter::Antialiasing, true); if(hasScale) d_scale.draw(&p, palette()); ///drawKnob(&p, kRect); drawKnob(&p, r); //drawMarker(&p, d_oldAngle, d_curFaceColor); //drawMarker(&p, d_angle, d_markerColor); d_newVal = 0; } //------------------------------------------------------------ //.- //.F QwtKnob::drawMarker // Draw the marker at the knob's front // //.u Parameters //.p QPainter *p -- painter // double arc -- angle of the marker // const QColor &c -- marker color // //.u Syntax // void QwtKnob::drawMarker(QPainter *p) // //------------------------------------------------------------ // void Knob::drawMarker(QPainter *p, double arc, const QColor &c) // { // // QPen pn; // int radius; // double rb,re; // double rarc; // // rarc = arc * M_PI / 180.0; // double ca = cos(rarc); // double sa = - sin(rarc); // // radius = kRect.width() / 2 - d_borderWidth + d_shineWidth; // if (radius < 3) radius = 3; // int ym = kRect.y() + radius + d_borderWidth - d_shineWidth; // int xm = kRect.x() + radius + d_borderWidth - d_shineWidth; // // switch (d_symbol) // { // case Dot: // // p->setBrush(c); // p->setPen(Qt::NoPen); // rb = double(MusECore::qwtMax(radius - 4 - d_dotWidth / 2, 0)); // p->drawEllipse(xm - int(rint(sa * rb)) - d_dotWidth / 2, // ym - int(rint(ca * rb)) - d_dotWidth / 2, // d_dotWidth, d_dotWidth); // // break; // // case Line: // // pn.setColor(c); // pn.setWidth(2); // p->setPen(pn); // // rb = MusECore::qwtMax(double((radius - 1) / 3.0), 0.0); // re = MusECore::qwtMax(double(radius - 1), 0.0); // // p->setRenderHint(QPainter::Antialiasing, true); // p->drawLine( xm, // ym, // xm - int(rint(sa * re)), // ym - int(rint(ca * re))); // // break; // } // // // } // //------------------------------------------------------------ // //.F QwtKnob::setKnobWidth // Change the knob's width. // //.u Syntax //.f void QwtKnob::setKnobWidth(int w) // //.u Parameters //.p int w -- new width // //.u Description // The specified width must be >= 5, or it will be clipped. // //------------------------------------------------------------ // void Knob::setKnobWidth(int w) // { // d_knobWidth = MusECore::qwtMax(w,5); // resize(size()); // repaint(); // } // //------------------------------------------------------------ // //.F QwtKnob::setBorderWidth // Set the knob's border width // //.u Syntax //.f void QwtKnob::setBorderWidth(int bw) // //.u Parameters //.p int bw -- new border width // //------------------------------------------------------------ // void Knob::setBorderWidth(int bw) // { // d_borderWidth = MusECore::qwtMax(bw, 0); // resize(size()); // repaint(); // } //------------------------------------------------------------ //.- //.F QwtKnob::recalcAngle // Recalculate the marker angle corresponding to the // current value // //.u Syntax //.f void QwtKnob::recalcAngle() // //------------------------------------------------------------ // void Knob::recalcAngle() // { // d_oldAngle = d_angle; // // // // // calculate the angle corresponding to the value // // // if (maxValue() == minValue()) // { // d_angle = 0; // d_nTurns = 0; // } // else // { // d_angle = (value() - 0.5 * (minValue() + maxValue())) // / (maxValue() - minValue()) * d_totalAngle; // d_nTurns = floor((d_angle + 180.0) / 360.0); // d_angle = d_angle - d_nTurns * 360.0; // // } // // } // //------------------------------------------------------------ // setFaceColor //------------------------------------------------------------ // void Knob::setFaceColor(const QColor c) // { // d_faceColor = c; // if(!_faceColSel) // //update(FALSE); // repaint(); // } //------------------------------------------------------------ // setAltFaceColor //------------------------------------------------------------ // void Knob::setAltFaceColor(const QColor c) // { // d_altFaceColor = c; // if(_faceColSel) // //update(FALSE); // repaint(); // } //------------------------------------------------------------ // selectFaceColor //------------------------------------------------------------ // void Knob::selectFaceColor(bool alt) // { // _faceColSel = alt; // if(alt) // d_curFaceColor = d_altFaceColor; // else // d_curFaceColor = d_faceColor; // //update(FALSE); // repaint(); // } //------------------------------------------------------------ // setMarkerColor //------------------------------------------------------------ // void Knob::setMarkerColor(const QColor c) // { // d_markerColor = c; // //update(FALSE); // repaint(); // } } // namespace MusEGui muse-4.0.0/src/muse/widgets/knob_and_meter.h000066400000000000000000000070171404105311000210040ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // knob_and_meter.h // (C) Copyright 2012 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __KNOB_AND_METER_H__ #define __KNOB_AND_METER_H__ #include "knob.h" #include "sclif.h" #include #include #include namespace MusEGui { //--------------------------------------------------------- // KnobWithMeter //--------------------------------------------------------- class KnobWithMeter : public Knob { Q_OBJECT // public: // enum Symbol { Line, Dot }; // // enum KnobType { // panType, // auxType, // gainType, // }; // // private: // bool hasScale; // // int d_borderWidth; // int d_shineWidth; // int d_scaleDist; // int d_maxScaleTicks; // int d_newVal; // int d_knobWidth; // int d_dotWidth; // // Symbol d_symbol; // double d_angle; // double d_oldAngle; // double d_totalAngle; // double d_nTurns; // // double l_const; // double l_slope; // // QRect kRect; // bool _faceColSel; // QColor d_faceColor; // QColor d_shinyColor; // QColor d_rimColor; // QColor d_curFaceColor; // QColor d_altFaceColor; // QColor d_markerColor; // // void recalcAngle(); // void valueChange(); // void rangeChange(); void drawKnob(QPainter *p, const QRect &r); // void drawMarker(QPainter *p, double arc, const QColor &c); virtual void paintEvent(QPaintEvent *e); virtual void resizeEvent(QResizeEvent *e); virtual void mousePressEvent(QMouseEvent *e); // double getValue(const QPoint &p); // void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction ); // void scaleChange() { repaint(); } // void fontChange(const QFont &) { repaint(); } public: KnobWithMeter(QWidget* parent = 0, const char *name = 0); ~KnobWithMeter() {} // void setRange(double vmin, double vmax, double vstep = 0.0, // int pagesize = 1); // void setKnobWidth(int w); // void setTotalAngle (double angle); // void setBorderWidth(int bw); // void selectFaceColor(bool alt); // bool selectedFaceColor() { return _faceColSel; } // QColor faceColor() { return d_faceColor; } // void setFaceColor(const QColor c); // QColor altFaceColor() { return d_altFaceColor; } // void setAltFaceColor(const QColor c); // QColor markerColor() { return d_markerColor; } // void setMarkerColor(const QColor c); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/lcombo.cpp000066400000000000000000000121561404105311000176430ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: lcombo.cpp,v 1.1.1.1.2.3 2009/07/01 22:14:56 spamatica Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "lcombo.h" #include #include #include #include #include namespace MusEGui { //--------------------------------------------------------- // LabelCombo //--------------------------------------------------------- LabelCombo::LabelCombo(const QString& txt, QWidget* parent, const char* name) : QWidget(parent) { setObjectName(name); QHBoxLayout* layout = new QHBoxLayout(this); QLabel* label = new QLabel(txt, this); //label->setContentsMargins(0,0,0,0); // REMOVE Tim. Or keep. box = new QComboBox(this); //box->setContentsMargins(0,0,0,0); // // Ignored was only solution, others were too tall. The label takes priority then. // //box->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Ignored); // box->setEditable(false); layout->addSpacing(2); layout->addWidget(label); // layout->addSpacing(2); layout->addWidget(box); // layout->addSpacing(2); layout->setContentsMargins(0, 0, 0, 0); connect(box, QOverload::of(&QComboBox::activated), [=](int index) { box_activated(index); } ); } void LabelCombo::addItem(const QString& txt, const QVariant &userData) { box->addItem(txt, userData); } void LabelCombo::insertItem(int index, const QString& txt, const QVariant &userData) { box->insertItem(index, txt, userData); } QAbstractItemView *LabelCombo::view() const { return box->view(); } void LabelCombo::setView(QAbstractItemView* v) { box->setModel(v->model()); box->setView(v); } void LabelCombo::setFocusPolicy (Qt::FocusPolicy fp) { box->setFocusPolicy(fp); } void LabelCombo::box_activated(int idx) { // HACK: Force the thing to show the right item. This hack is required because // if we are trying to show a table view in a combo box it normally wants // to show a single given column using a list view. const QAbstractItemView* iv = view(); if(!iv) return; const QModelIndex mdl_idx = iv->currentIndex(); if(!mdl_idx.isValid()) return; const int row = mdl_idx.row(); const int col = mdl_idx.column(); blockSignals(true); if(box->modelColumn() != col) box->setModelColumn(col); if(box->currentIndex() != row) box->setCurrentIndex(row); blockSignals(false); emit activated(idx); emit activated(mdl_idx); } void LabelCombo::clearFocus() { box->clearFocus(); } QVariant LabelCombo::itemData(int index, int role) const { return box->itemData(index, role); } int LabelCombo::findData(const QVariant &data, int role, Qt::MatchFlags flags) const { return box->findData(data, role, flags); } int LabelCombo::maxVisibleItems() const { return box->maxVisibleItems(); } void LabelCombo::setMaxVisibleItems(int maxItems) { box->setMaxVisibleItems(maxItems); } QComboBox::SizeAdjustPolicy LabelCombo::sizeAdjustPolicy() const { return box->sizeAdjustPolicy(); } void LabelCombo::setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy policy) { box->setSizeAdjustPolicy(policy); } int LabelCombo::currentIndex() const { return box->currentIndex(); } QModelIndex LabelCombo::currentModelIndex() const { return view()->currentIndex(); } void LabelCombo::setCurrentIndex(int i) { // HACK: Force the thing to show the right item. This hack is required because // if we are trying to show a table view in a combo box it normally wants // to show a single given column using a list view. int rc = box->model()->rowCount(); if(rc == 0) return; int r = i % rc; int c = i / rc; if(c >= box->model()->columnCount()) return; if(box->modelColumn() != c) box->setModelColumn(c); if(box->currentIndex() != r) box->setCurrentIndex(r); } void LabelCombo::setCurrentModelIndex(const QModelIndex& mdl_idx) { const int row = mdl_idx.row(); const int col = mdl_idx.column(); if(col >= box->model()->columnCount()) return; if(box->modelColumn() != col) box->setModelColumn(col); if(box->currentIndex() != row) box->setCurrentIndex(row); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/lcombo.h000066400000000000000000000051531404105311000173070ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: lcombo.h,v 1.1.1.1.2.3 2009/07/01 22:14:56 spamatica Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LCOMBO_H__ #define __LCOMBO_H__ #include #include #include class QAbstractItemView; class QString; class QModelIndex; namespace MusEGui { //--------------------------------------------------------- // LabelCombo //--------------------------------------------------------- class LabelCombo : public QWidget { Q_OBJECT QComboBox* box; signals: void activated(int); void activated(const QModelIndex&); private slots: void box_activated(int idx); public slots: void clearFocus(); void setCurrentIndex(int i); void setCurrentModelIndex(const QModelIndex& mdl_idx); public: LabelCombo(const QString& label, QWidget* parent, const char* name=0); void addItem(const QString& txt, const QVariant & userData = QVariant()); void insertItem(int index, const QString& txt, const QVariant & userData = QVariant()); QAbstractItemView *view() const; void setView(QAbstractItemView* v); void setFocusPolicy ( Qt::FocusPolicy fp ); QVariant itemData(int index, int role = Qt::UserRole) const; int findData( const QVariant &data, int role = Qt::UserRole, Qt::MatchFlags flags = static_cast(Qt::MatchExactly|Qt::MatchCaseSensitive)) const; int maxVisibleItems() const; void setMaxVisibleItems(int maxItems); QComboBox::SizeAdjustPolicy sizeAdjustPolicy() const; void setSizeAdjustPolicy(QComboBox::SizeAdjustPolicy policy); int currentIndex() const; QModelIndex currentModelIndex() const; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/line_edit.cpp000066400000000000000000000176321404105311000203300ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // line_edit.cpp // (C) Copyright 2017 Tim E. Real (terminator356 on sourceforge) // // 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; 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; 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 #include #include #include #include //#include //#include #include "line_edit.h" namespace MusEGui { //--------------------------------------------------------- // LineEdit //--------------------------------------------------------- LineEdit::LineEdit(QWidget* parent, const char* name) : QLineEdit(parent) { setObjectName(name); //setAutoFillBackground(true); _enableStyleHack = true; } LineEdit::LineEdit(const QString& contents, QWidget* parent, const char* name) : QLineEdit(contents, parent) { setObjectName(name); //setAutoFillBackground(true); _enableStyleHack = true; } void LineEdit::setEnableStyleHack(bool v) { _enableStyleHack = v; update(); } void LineEdit::paintEvent(QPaintEvent* ev) { QLineEdit::paintEvent(ev); ev->accept(); if(!_enableStyleHack) return; // -------------------------------------------- // Special hack to force the frame to be drawn: // -------------------------------------------- if(const QStyle* st = style()) { st = st->proxy(); QPainter p(this); QStyleOptionFrame o; initStyleOption(&o); // KDE BUG: Breeze and Oxygen IGNORE the drawFrame setting if the text font is // high enough such that it thinks the text is too big. // It draws nothing but a flat, square panel. // We end up with NO mouse over or focus rectangles at all ! // Force a small font size to fool the line edit panel into drawing a frame. QFont fnt(font()); fnt.setPointSize(1); // Zero not allowed. o.fontMetrics = QFontMetrics(fnt); QRect s_r(rect()); s_r.adjust(4, 4, -4, -4); QPainterPath inner_rect_path; inner_rect_path.addRect(s_r); QPainterPath fin_rect_path; fin_rect_path.addRect(rect()); fin_rect_path -= inner_rect_path; p.setClipPath(fin_rect_path); p.fillRect(rect(), palette().window()); st->drawPrimitive(QStyle::PE_PanelLineEdit, &o, &p); } } //====================================================== // TODO: Work in progress... // Since KDE Breeze and Oxygen are unlikely to change // the ignore-frame-if-too-small behaviour, muse really // needs its own line edit widget once and for all. // But it's a very complex and difficult control to make. //====================================================== /* //--------------------------------------------------------- // LineEdit //--------------------------------------------------------- LineEdit::LineEdit(QWidget* parent, const char* name) : QWidget(parent) { setObjectName(name); setAutoFillBackground(true); _drawFrame = true; _readOnly = false; _textLayout = new QTextLayout(); } LineEdit::~LineEdit() { delete _textLayout; } void LineEdit::initStyleOption(QStyleOptionFrame *option) const { if(!option) return; option->initFrom(this); option->rect = contentsRect(); option->lineWidth = _drawFrame ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth, option, this) : 0; option->midLineWidth = 0; option->state |= QStyle::State_Sunken; if(isReadOnly()) option->state |= QStyle::State_ReadOnly; #ifdef QT_KEYPAD_NAVIGATION if(hasEditFocus()) option->state |= QStyle::State_HasEditFocus; #endif option->features = QStyleOptionFrame::None; } void LineEdit::setFrame(bool v) { if(v == _drawFrame) return; _drawFrame = v; update(); } void LineEdit::setReadOnly(bool v) { if(v == _readOnly) return; _readOnly = v; update(); } void LineEdit::setText(const QString& s) { if(s == _text) return; _text = s; _textLayout->setText(_text); redoTextLayout(); update(); } int LineEdit::redoTextLayout() const { _textLayout->clearLayout(); _textLayout->beginLayout(); QTextLine l = _textLayout->createLine(); _textLayout->endLayout(); return qRound(l.ascent()); } void LineEdit::draw(QPainter *painter, const QPoint &offset, const QRect &clip, int flags) { QVector selections; if (flags & DrawSelections) { QTextLayout::FormatRange o; if (m_selstart < m_selend) { o.start = m_selstart; o.length = m_selend - m_selstart; o.format.setBackground(m_palette.brush(QPalette::Highlight)); o.format.setForeground(m_palette.brush(QPalette::HighlightedText)); } else { // mask selection if (m_blinkStatus){ o.start = m_cursor; o.length = 1; o.format.setBackground(m_palette.brush(QPalette::Text)); o.format.setForeground(m_palette.brush(QPalette::Window)); } } selections.append(o); } if (flags & DrawText) textLayout()->draw(painter, offset, selections, clip); if (flags & DrawCursor){ int cursor = m_cursor; if (m_preeditCursor != -1) cursor += m_preeditCursor; if (!m_hideCursor && m_blinkStatus) textLayout()->drawCursor(painter, offset, cursor, m_cursorWidth); } } void LineEdit::paintEvent(QPaintEvent* ev) { ev->accept(); //fprintf(stderr, "LineEdit::paintEvent x:%d y:%d w:%d h:%d \n", ev->rect().x(), ev->rect().y(), ev->rect().width(), ev->rect().height()); QPainter p(this); if(const QStyle* st = style()) { st = st->proxy(); QStyleOptionFrame o; initStyleOption(&o); // Qt BUG: Breeze and Oxygen IGNORE the drawFrame setting if the text font is // high enough such that it thinks the text is too big. // It draws nothing but a flat, square panel. // We end up with NO mouse over or focus rectangles at all ! // Force a small font size to fool the line edit panel into drawing a frame. QFont fnt(font()); fnt.setPointSize(1); // Zero not allowed. o.fontMetrics = QFontMetrics(fnt); st->drawPrimitive(QStyle::PE_PanelLineEdit, &o, &p); QRect s_r = st->subElementRect(QStyle::SE_LineEditContents, &o, this); //int ml, mt, mr, mb; //getTextMargins(&ml, &mt, &mr, &mb); //s_r.adjust(ml, mt, -mr, -mb); o.fontMetrics = fontMetrics(); // Force no frame. o.features = QStyleOptionFrame::None; o.frameShape = QFrame::NoFrame; o.lineWidth = 0; o.midLineWidth = 0; QRect b_r = st->subElementRect(QStyle::SE_LineEditContents, &o, this); //b_r.adjust(ml, mt, -mr, -mb); int x_offset = s_r.x() - b_r.x(); int y_offset = s_r.y() - b_r.y(); QRect clip_r(b_r); clip_r.adjust(-x_offset, -x_offset, -y_offset, -y_offset); QRect paint_r(o.rect); //paint_r.adjust(-x_offset, -x_offset, -y_offset, -y_offset); //paint_r.adjust(x_offset, -x_offset, y_offset, -y_offset); paint_r.adjust(6, -6, 6, -6); //QRect paint_r(s_r); //p.setClipRect(clip_r); // Done with this painter. //~p(); QPaintEvent paint_ev(paint_r); QLineEdit::paintEvent(&paint_ev); } } */ } // namespace MusEGui muse-4.0.0/src/muse/widgets/line_edit.h000066400000000000000000000063061404105311000177710ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // line_edit.h // (C) Copyright 2017 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __LINE_EDIT_H__ #define __LINE_EDIT_H__ //#include #include class QPaintEvent; // class QStyleOptionFrame; class QString; // class QTextLayout; namespace MusEGui { //--------------------------------------------------------- // LineEdit //--------------------------------------------------------- class LineEdit : public QLineEdit { Q_OBJECT bool _enableStyleHack; protected: virtual void paintEvent(QPaintEvent*); // virtual void keyPressEvent(QKeyEvent*); public: LineEdit(QWidget* parent = 0, const char* name = 0); LineEdit(const QString& contents, QWidget* parent = 0, const char* name = 0); bool enableStyleHack() const { return _enableStyleHack; } void setEnableStyleHack(bool); }; //====================================================== // TODO: Work in progress... // Since KDE Breeze and Oxygen are unlikely to change // the ignore-frame-if-too-small behaviour, muse really // needs its own line edit widget once and for all. // But it's a very complex and difficult control to make. //====================================================== /* //--------------------------------------------------------- // LineEdit //--------------------------------------------------------- class LineEdit : public QWidget { Q_OBJECT public: enum DrawFlags { DrawText = 0x01, DrawSelections = 0x02, DrawCursor = 0x04, DrawAll = DrawText | DrawSelections | DrawCursor }; private: bool _drawFrame; bool _readOnly; QString _text; QTextLayout* _textLayout; // Updates the internal text layout. Returns the ascent of the // created QTextLine. int redoTextLayout() const; void draw(QPainter* painter, const QPoint& offset, const QRect &clip, int flags); virtual void paintEvent(QPaintEvent*); protected: void initStyleOption(QStyleOptionFrame*) const; public: LineEdit(QWidget*, const char* name=0); virtual ~LineEdit(); bool hasFrame() const { return _drawFrame; } void setFrame(bool); bool isReadOnly() const { return _readOnly; } void setReadOnly(bool); QString text() const { return _text; } QString setText(); }; */ } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/menutitleitem.cpp000066400000000000000000000031631404105311000212530ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // (C) Copyright 1999-2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //============================================================================= #include "menutitleitem.h" #include "menutitlewidgets.h" namespace MusEGui { //--------------------------------------------------------- // MenuTitleItem //--------------------------------------------------------- MenuTitleItem::MenuTitleItem(const QString& ss, QWidget* parent) : QWidgetAction(parent) { setObjectName("menuTitleItem"); s = ss; // Don't allow to click on it. setEnabled(false); // Just to be safe, set to -1 instead of default 0. setData(-1); } QWidget* MenuTitleItem::createWidget(QWidget *parent) { MenuTitleLabel* l = new MenuTitleLabel(s, parent); return l; } } // namespace MusEGui muse-4.0.0/src/muse/widgets/menutitleitem.h000066400000000000000000000030171404105311000207160ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: menutitleitem.h,v 1.1.2.1 2009/06/10 00:34:59 terminator356 Exp $ // (C) Copyright 1999-2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MENU_TITLE_ITEM_H__ #define __MENU_TITLE_ITEM_H__ #include #include #include namespace MusEGui { //--------------------------------------------------------- // MenuTitleItem //--------------------------------------------------------- class MenuTitleItem : public QWidgetAction { Q_OBJECT private: QString s; public: MenuTitleItem(const QString&, QWidget* /*parent*/); QWidget* createWidget(QWidget* /*parent*/); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/menutitlewidgets.cpp000066400000000000000000000041641404105311000217650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // menutitlewidgets.cpp // (C) Copyright 2019 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "menutitlewidgets.h" namespace MusEGui { MenuTitleLabel::MenuTitleLabel(const QString &text, QWidget *parent, Qt::WindowFlags f) : QLabel(text, parent, f) { setAlignment(Qt::AlignCenter); setAutoFillBackground(true); // FIXME This doesn't play well with stylesheets. If a stylesheet is set it MUST set this colour // because this code has no effect when a sheet is set. // Instead, if we set a stylesheet here, then we cannot override it from a master sheet. // What to do...? // const QColor c = palette().color(backgroundRole()).darker(120); // QPalette palette; // palette.setColor(backgroundRole(), c); // setPalette(palette); setBackgroundRole(QPalette::Mid); // setForegroundRole(QPalette::Light); // setStyleSheet(QString("background-color: rgb(%1,%2,%3);").arg(c.red()).arg(c.green()).arg(c.blue())); } MenuTitleLabel::MenuTitleLabel(QWidget *parent, Qt::WindowFlags f) : QLabel(parent, f) { setAlignment(Qt::AlignCenter); setAutoFillBackground(true); setBackgroundRole(QPalette::Mid); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/menutitlewidgets.h000066400000000000000000000031111404105311000214210ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // Copyright (C) 1999-2011 by Werner Schweer and others // // menutitlewidgets.h // (C) Copyright 2019 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MENU_TITLE_WIDGETS_H__ #define __MENU_TITLE_WIDGETS_H__ #include #include #include namespace MusEGui { //--------------------------------------------------------- // MenuTitleLabel //--------------------------------------------------------- class MenuTitleLabel : public QLabel { Q_OBJECT public: MenuTitleLabel(const QString &text, QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); MenuTitleLabel(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/meter.cpp000066400000000000000000001250621404105311000175050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: meter.cpp,v 1.4.2.2 2009/05/03 04:14:00 terminator356 Exp $ // redesigned by oget on 2011/08/15 // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) // (C) Copyright 2011-2016 Tim E. Real (terminator356 on users DOT sourceforge DOT net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include #include #include #include "meter.h" // #include "utils.h" #include "fastlog.h" #include "muse_math.h" // Just an experiment. Some undesirable effects, see below... //#define _USE_CLIPPER 1 namespace MusEGui { //--------------------------------------------------------- // MeterLayout //--------------------------------------------------------- MeterLayout::MeterLayout(int endsMargin, QWidget* parent) : QVBoxLayout(parent), _endsMargin(endsMargin) { _spacer1 = new QSpacerItem(0, _endsMargin, QSizePolicy::Maximum, QSizePolicy::Maximum); _spacer2 = new QSpacerItem(0, _endsMargin, QSizePolicy::Maximum, QSizePolicy::Maximum); _hlayout = new QHBoxLayout(); addSpacerItem(_spacer1); addLayout(_hlayout); addSpacerItem(_spacer2); } int MeterLayout::meterEndsMargin() const { return _endsMargin; } void MeterLayout::setMeterEndsMargin(int m) { _endsMargin = m; _spacer1->changeSize(0, _endsMargin, QSizePolicy::Maximum, QSizePolicy::Maximum); _spacer2->changeSize(0, _endsMargin, QSizePolicy::Maximum, QSizePolicy::Maximum); invalidate(); } QHBoxLayout* MeterLayout::hlayout() { return _hlayout; } //--------------------------------------------------------- // Meter //--------------------------------------------------------- Meter::Meter(QWidget* parent, MeterType type, Qt::Orientation orient, double scaleMin, double scaleMax, ScalePos scalePos, const QColor& primaryColor, ScaleDraw::TextHighlightMode textHighlightMode, int refreshRate) : QFrame(parent), _primaryColor(primaryColor), _scalePos(scalePos), _refreshRate(refreshRate) //Qt::WNoAutoErase { setBackgroundRole(QPalette::NoRole); setAttribute(Qt::WA_NoSystemBackground); setAttribute(Qt::WA_StaticContents); // This is absolutely required for speed! Otherwise painfully slow because of full background // filling, even when requesting small udpdates! Background is drawn by us. (Just small corners.) setAttribute(Qt::WA_OpaquePaintEvent); //setFrameStyle(QFrame::Raised | QFrame::StyledPanel); // QFont fnt; // fnt.setFamily("Sans"); // fnt.setPixelSize(9); // //fnt.setStyleStrategy(QFont::PreferBitmap); // fnt.setStyleStrategy(QFont::NoAntialias); // fnt.setHintingPreference(QFont::PreferVerticalHinting); // setFont(fnt); // // setStyleSheet("font: 9px \"Sans\"; "); // setStyleSheet(MusECore::font2StyleSheet(fnt)); mtype = type; _orient = orient; d_scale.setTextHighlightMode(textHighlightMode); _scaleDist = 0; // Leftover from class Slider. Maybe use later? _showText = false; overflow = false; cur_pixv = -1; // Flag as -1 to initialize in paint. last_pixv = 0; cur_pixmax = 0; last_pixmax = 0; val = 0.0; targetVal = 0.0; targetValStep = 0.0; maxVal = 0.0; targetMaxVal = 0.0; minScale = scaleMin; maxScale = scaleMax; yellowScale = -10; redScale = 0; setLineWidth(0); setMidLineWidth(0); _radius = 4; _vu3d = true; _frame = false; _frameColor = Qt::darkGray; ensurePolished(); if (_vu3d) { dark_red_end = QColor(0x8e0000); dark_red_begin = QColor(0x8e3800); darkGradRed.setColorAt(1, dark_red_begin); darkGradRed.setColorAt(0, dark_red_end); dark_yellow_end = QColor(0x8e6800); dark_yellow_center = QColor(0x8e8e00); dark_yellow_begin = QColor(0x6a8400); darkGradYellow.setColorAt(1, dark_yellow_begin); darkGradYellow.setColorAt(0.5, dark_yellow_center); darkGradYellow.setColorAt(0, dark_yellow_end); mask_center = QColor(225, 225, 225, 64); mask_edge = QColor(30, 30, 30, 64); maskGrad.setColorAt(0, mask_edge); maskGrad.setColorAt(0.5, mask_center); maskGrad.setColorAt(1, mask_edge); } light_red_end = QColor(0xff0000); light_red_begin = QColor(0xdd8800); light_yellow_end = QColor(0xddcc00); light_yellow_center = QColor(0xffff00); light_yellow_begin = QColor(0xddff00); lightGradYellow.setColorAt(1, light_yellow_begin); lightGradYellow.setColorAt(0.5, light_yellow_center); lightGradYellow.setColorAt(0, light_yellow_end); lightGradRed.setColorAt(1, light_red_begin); lightGradRed.setColorAt(0, light_red_end); separator_color = QColor(0x666666); peak_color = QColor(0xeeeeee); connect(&fallingTimer, SIGNAL(timeout()), this, SLOT(updateTargetMeterValue())); setPrimaryColor(_primaryColor); // updateText(targetVal); } //------------------------------------------------------------ //.- //.F Slider::scaleChange // Notify changed scale // //.u Syntax //.f void Slider::scaleChange() // //.u Description // Called by QwtScaledWidget // //------------------------------------------------------------ void Meter::scaleChange() { if (!hasUserScale()) d_scale.setScale(maxScale, minScale, d_maxMajor, d_maxMinor); update(); } QSize Meter::sizeHint() const { int w = 40; int h = 40; const QFontMetrics fm = fontMetrics(); int msWidth = 0, msHeight = 0; if(_scalePos != None) { msWidth = d_scale.maxWidth(fm, false); msHeight = d_scale.maxHeight(fm); switch(_orient) { case Qt::Vertical: { const int smw = msWidth + _scaleDist; switch(_scalePos) { case Left: case Right: //w = 2*d_xMargin + d_thumbWidth + smw + 2; w = smw + 2; break; case InsideVertical: { //const int aw = smw > d_thumbWidth ? smw : d_thumbWidth; //w = 2*d_xMargin + aw + 2; w = smw + 2; } break; case Top: case Bottom: case InsideHorizontal: case None: break; } } break; case Qt::Horizontal: { const int smh = msHeight + _scaleDist; switch(_scalePos) { case Top: case Bottom: //h = 2*d_yMargin + d_thumbWidth + smh; h = smh; break; case InsideHorizontal: { //const int ah = smh > d_thumbWidth ? smh : d_thumbWidth; //h = 2*d_yMargin + ah; h = smh; } break; case Left: case Right: case InsideVertical: case None: break; } } break; } } else { // no scale switch(_orient) { case Qt::Vertical: w = 16; break; case Qt::Horizontal: h = 16; break; } } //fprintf(stderr, "Meter::sizeHint w:%d, h:%d\n", w, h); return QSize(w, h); } //--------------------------------------------------------- // updateText //--------------------------------------------------------- void Meter::updateText(double val) { if(val >= -60.0f) _text = locale().toString(val, 'f', 1); else { _text = QString("-"); _text += QChar(0x221e); // The infinty character } const QFontMetrics fm = fontMetrics(); //const QFontMetrics fm(_textFont); //// Rotate 90 deg. //_textSize = fm.boundingRect(txt).size().transposed(); const QSize sz = fm.boundingRect(_text).size(); const int txtw = sz.width(); const int txth = sz.height(); // if(_textPM.isNull() || _textPM.size().width() < w || _textPM.size().height() < h) // _textPM = QPixmap(w, h); // QPainter p; // p.begin(&_textPM); // // p.eraseRect(0, 0, w, h); // p.fillRect(0, 0, w, h, Qt::darkYellow); // p.rotate(90); // p.setPen(Qt::cyan); // // Rotate 90 deg. // //p.drawText(0, 0, h, w, Qt::AlignLeft | Qt::AlignTop, txt); // p.drawText(0, 0, txt); // p.end(); // Set the text size, incrementally expanding. Ensure that paint will erase the last largest size. // Rotate 90 degrees. const int fw = frameWidth(); const int w = width() - 2*fw; const int txtYOff = fw + (w > txth ? (w - txth) / 2 : 0); _textRect.setX(fw); _textRect.setY(txtYOff); if(txtw > _textRect.width()) _textRect.setWidth(txtw); if(txth > _textRect.height()) _textRect.setHeight(txth); const QRect rr(_textRect.y(), _textRect.x(), _textRect.height(), _textRect.width()); // Rotate -90 degrees. update(rr); //QRect ur(_textRect.y(), _textRect.x(), _textRect.height(), _textRect.width()); //update(ur); } //--------------------------------------------------------- // setVal //--------------------------------------------------------- void Meter::setVal(double v, double max, bool ovl) { overflow = ovl; bool ud = false; if(mtype == DBMeter) { double minScaleLin = muse_db2val(minScale); if((v >= minScaleLin && targetVal != v) || targetVal >= minScaleLin) { targetVal = v; ud = true; } } else { if(targetVal != v) { targetVal = v; ud = true; } } if(ud || (maxVal != max)) { targetMaxVal = max; if(!fallingTimer.isActive()) { fallingTimer.start(1000/std::max(30, _refreshRate)); } } } void Meter::updateTargetMeterValue() { double range = maxScale - minScale; int fw = frameWidth(); int w = width() - 2*fw; int h = height() - 2*fw; QRect udRect; bool udPeak = false; bool ud = false; if(targetVal > val) { val = targetVal; targetValStep = 0; ud = true; } else if(targetVal < val) { targetValStep = (val - targetVal) / ((double)(1000 / std::max(30, _refreshRate + 1)) / 7.0f); val -= targetValStep; if(val < targetVal) { val = targetVal; } ud = true; } const double transl_val = val - minScale; if(maxVal != targetMaxVal) { maxVal = targetMaxVal; const double v = (mtype == DBMeter) ? (MusECore::fast_log10(maxVal) * 20.0) : maxVal; if(_orient == Qt::Vertical) { cur_pixmax = maxVal == 0 ? fw : int(((maxScale - v) * h)/range); if(_showText) updateText(v); if(cur_pixmax > h) cur_pixmax = h; // Not using regions. Just lump them together. udRect = QRect(fw, last_pixmax, w, 1) | QRect(fw, cur_pixmax, w, 1); } else { //cur_pixmax = maxVal == 0 ? fw : int(((maxScale - v) * w)/range); cur_pixmax = maxVal == 0 ? w - fw : int((v * w)/range); if(_showText) updateText(v); if(cur_pixmax > w) cur_pixmax = w; // Not using regions. Just lump them together. udRect = QRect(last_pixmax, fw, 1, h) | QRect(cur_pixmax, fw, 1, w); } //printf("Meter::setVal peak cur_ymax:%d last_ymax:%d\n", cur_ymax, last_ymax); last_pixmax = cur_pixmax; ud = true; udPeak = true; } if(ud) { if(_orient == Qt::Vertical) { if(cur_pixv > h) cur_pixv = h; if(mtype == DBMeter) cur_pixv = val == 0 ? h : int(((maxScale - (MusECore::fast_log10(val) * 20.0)) * h)/range); //cur_pixv = transl_val <= 0.0 ? h : int(((maxScale - (MusECore::fast_log10(val) * 20.0)) * h)/range); // TODO else cur_pixv = val == 0 ? h : int(((maxScale - val) * h)/range); //cur_pixv = int(((maxScale - transl_val) * h)/range); // TODO //printf("Meter::setVal cur_yv:%d last_yv:%d\n", cur_yv, last_yv); int y1, y2; if(last_pixv < cur_pixv) { y1 = last_pixv; y2 = cur_pixv; } else { y1 = cur_pixv; y2 = last_pixv; } last_pixv = cur_pixv; if(udPeak) update(udRect | QRect(fw, y1, w, y2 - y1 + 1)); //repaint(udRect | QRect(fw, y1, w, y2 - y1 + 1)); else update(QRect(fw, y1, w, y2 - y1 + 1)); //repaint(QRect(fw, y1, w, y2 - y1 + 1)); } else { if(cur_pixv > w) cur_pixv = w; if(mtype == DBMeter) //cur_pixv = val == 0 ? 0 : int((MusECore::fast_log10(val) * 20.0 * w)/range); cur_pixv = transl_val <= 0.0 ? 0 : int((MusECore::fast_log10(transl_val) * 20.0 * w)/range); // FIXME: Comparison correct? else //cur_pixv = int((val * w)/range); cur_pixv = int((transl_val * w)/range); //printf("Meter::setVal cur_yv:%d last_yv:%d\n", cur_yv, last_yv); int x1, x2; if(last_pixv < cur_pixv) { x1 = last_pixv; x2 = cur_pixv; } else { x1 = cur_pixv; x2 = last_pixv; } last_pixv = cur_pixv; if(udPeak) update(udRect | QRect(x1, fw, x2 - x1 + 1, h)); //repaint(udRect | QRect(x1, fw, x2 - x1 + 1, h)); else update(QRect(x1, fw, x2 - x1 + 1, h)); //repaint(QRect(x1, fw, x2 - x1 + 1, h)); } } if(!ud) { fallingTimer.stop(); } } //--------------------------------------------------------- // resetPeaks // reset peak and overflow indicator //--------------------------------------------------------- void Meter::resetPeaks() { maxVal = val; overflow = val > 0.0; cur_pixv = -1; // Force re-initialization. update(); } //--------------------------------------------------------- // setRange //--------------------------------------------------------- void Meter::setRange(double min, double max) { if(min == minScale && max == maxScale) // p4.0.45 return; minScale = min; maxScale = max; cur_pixv = -1; // Force re-initialization. if (!hasUserScale()) d_scale.setScale(minScale, maxScale, d_maxMajor, d_maxMinor); update(); } //--------------------------------------------------------- // setRefreshRate //--------------------------------------------------------- void Meter::setRefreshRate(int rate) { _refreshRate = rate; } void Meter::setPrimaryColor(const QColor& color, const QColor &bgColor) { _primaryColor = color; int r = 0; if (_vu3d) { dark_green_begin = _primaryColor.darker(200); dark_green_end = dark_green_begin; r = dark_green_end.red() + 0x46; if(r > 255) r = 255; dark_green_end.setRed(r); darkGradGreen.setColorAt(1, dark_green_begin); darkGradGreen.setColorAt(0, dark_green_end); } else _bgColor = bgColor; light_green_begin = _primaryColor; light_green_end = light_green_begin; r = light_green_end.red() + 0x88; if(r > 255) r = 255; light_green_end.setRed(r); lightGradGreen.setColorAt(1, light_green_begin); lightGradGreen.setColorAt(0, light_green_end); update(); } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void Meter::paintEvent(QPaintEvent* ev) { // For some reason upon resizing we get double calls here and in resizeEvent. QPainter p(this); const int fw = frameWidth(); const int w = width() - 2*fw; const int h = height() - 2*fw; p.setRenderHint(QPainter::Antialiasing); //p.fillRect(0, 0, width(), height(), QColor(50, 50, 50)); const double range = maxScale - minScale; const double transl_val = val - minScale; bool textDrawn = false; // Rects() is obsolete. Qt >= 5.8 use begin()/end(). #if QT_VERSION >= 0x050800 QRegion::const_iterator ireg_end = ev->region().cend(); for(QRegion::const_iterator ireg = ev->region().cbegin(); ireg != ireg_end; ++ireg) { const QRect& rect = *ireg; #else const int rectCount = ev->region().rectCount(); QVector rects = ev->region().rects(); for(int ri = 0; ri < rectCount; ++ri) { const QRect& rect = rects.at(ri); #endif // Tested OK! Small non-overlapping rectangles. //fprintf(stderr, "Meter::paintEvent rcount:%d ridx:%d rx:%d ry:%d rw:%d rh:%d w:%d h:%d\n", // rectCount, ri, rect.x(), rect.y(), rect.width(), rect.height(), w, h); QPainterPath drawingPath, updatePath, finalPath, cornerPath; //bool updFull = false; // Initialize. Can't do in ctor, must be done after layouts have been done. Most reliable to do it here. if(cur_pixv == -1) { if(_orient == Qt::Vertical) { if(mtype == DBMeter) { cur_pixv = val == 0 ? h : int(((maxScale - (MusECore::fast_log10(val) * 20.0)) * h)/range); cur_pixmax = maxVal == 0 ? fw : int(((maxScale - (MusECore::fast_log10(maxVal) * 20.0)) * h)/range); } else { cur_pixv = val == 0 ? h : int(((maxScale - val) * h)/range); cur_pixmax = maxVal == 0 ? fw : int(((maxScale - maxVal) * h)/range); } if(cur_pixv > h) cur_pixv = h; last_pixv = cur_pixv; if(cur_pixmax > h) cur_pixmax = h; last_pixmax = cur_pixmax; //updFull = true; updatePath.addRect(fw, fw, w, h); // Update the whole thing } else { if(mtype == DBMeter) { cur_pixv = transl_val <= 0.0 ? 0 : int(((MusECore::fast_log10(transl_val) * 20.0) * w)/range); cur_pixmax = maxVal <= 0.0 ? w - fw : int(((MusECore::fast_log10(maxVal) * 20.0) * w)/range); } else { cur_pixv = int((transl_val * w)/range); cur_pixmax = maxVal <= 0.0 ? w - fw : int((maxVal * w)/range); } if(cur_pixv > w) cur_pixv = w; last_pixv = cur_pixv; if(cur_pixmax > w) cur_pixmax = w; last_pixmax = cur_pixmax; //updFull = true; updatePath.addRect(fw, fw, w, h); // Update the whole thing } } else updatePath.addRect(rect.x(), rect.y(), rect.width(), rect.height()); // Update only the requested rectangle drawingPath.addRoundedRect(fw, fw, w, h, _radius, _radius); // The actual desired shape of the meter finalPath = drawingPath & updatePath; // Draw corners as normal background colour. cornerPath = updatePath - finalPath; // Elegantly simple. Path subtraction! Wee... if(!cornerPath.isEmpty()) p.fillPath(cornerPath, palette().window()); #ifdef _USE_CLIPPER p.setClipPath(finalPath); // Meh, nice but not so good. Clips at edge so antialising has no effect! Can it be done ? #endif // Draw the red, green, and yellow sections. drawVU(p, rect, finalPath, cur_pixv); // Draw the peak white line. //if(updFull || (cur_ymax >= rect.y() && cur_ymax < rect.height())) { p.setRenderHint(QPainter::Antialiasing, false); // No antialiasing. Makes the line fuzzy, double height, or not visible at all. //p.setPen(peak_color); //p.drawLine(fw, cur_ymax, w, cur_ymax); // Undesirable. Draws outside the top rounded corners. // //QPainterPath path; path.moveTo(fw, cur_ymax); path.lineTo(w, cur_ymax); // ? Didn't work. No line at all. //p.drawPath(path & finalPath); QPainterPath path; if(_orient == Qt::Vertical) path.addRect(fw, cur_pixmax + cur_pixmax % 2 + 1, w, 1); else path.addRect(cur_pixmax + cur_pixmax % 2 + 1, fw, 1, h); path &= finalPath; if(!path.isEmpty()) p.fillPath(path, QBrush(peak_color)); } if (_vu3d) { // Draw the transparent layer on top of everything to give a 3d look p.setRenderHint(QPainter::Antialiasing); maskGrad.setStart(QPointF(fw, fw)); if(_orient == Qt::Vertical) maskGrad.setFinalStop(QPointF(w, fw)); else maskGrad.setFinalStop(QPointF(fw, h)); #ifdef _USE_CLIPPER p.fillRect(rect, QBrush(maskGrad)); #else //QPainterPath path; path.addRect(fw, fw, w); //p.fillPath(finalPath & path, QBrush(maskGrad)); p.fillPath(finalPath, QBrush(maskGrad)); #endif } if (_frame) { p.setPen(_frameColor); QPainterPath framePath; framePath.addRoundedRect(fw, fw, w, h, _radius, _radius); p.setClipPath(finalPath); p.drawPath(framePath); p.setClipping(false); } if(_showText) { const QRect rr(rect.y(), rect.x(), rect.height(), rect.width()); // Rotate 90 degrees. if(!textDrawn && rr.intersects(_textRect)) { textDrawn = true; //fprintf(stderr, " Drawing text:%s\n", _text.toLatin1().constData()); //p.setFont(_textFont); p.setPen(Qt::white); if(_orient == Qt::Vertical) { p.rotate(90); p.translate(0, -frameGeometry().width()); } //p.drawText(txtXOff, txtYOff, _textSize.width(), _textSize.height(), Qt::AlignLeft | Qt::AlignVCenter, _text); p.drawText(_textRect, Qt::AlignLeft | Qt::AlignVCenter, _text); //p.drawPixmap(fw, fw, _textPM); if(_orient == Qt::Vertical) { // Restore. p.translate(0, frameGeometry().width()); p.rotate(-90); } } } } if(_scalePos != None) { // p.fillRect(rect(), palette().window()); p.setRenderHint(QPainter::Antialiasing, false); d_scale.draw(&p, palette()); } } //--------------------------------------------------------- // drawVU //--------------------------------------------------------- void Meter::drawVU(QPainter& p, const QRect& rect, const QPainterPath& drawPath, int pixv) { int fw = frameWidth(); int w = width() - 2*fw; int h = height() - 2*fw; // Test OK. We are passed small rectangles on small value changes. //printf("Meter::drawVU rx:%d ry:%d rw:%d rh:%d w:%d h:%d\n", rect.x(), rect.y(), rect.width(), rect.height(), w, h); if(_orient == Qt::Vertical) { //QRect pr(0, 0, w, 0); if(mtype == DBMeter) // Meter type is dB... { double range = maxScale - minScale; int y1 = int((maxScale - redScale) * h / range); int y2 = int((maxScale - yellowScale) * h / range); if (_vu3d) { darkGradGreen.setStart(QPointF(fw, y2)); darkGradGreen.setFinalStop(QPointF(fw, h)); darkGradYellow.setStart(QPointF(fw, y1)); darkGradYellow.setFinalStop(QPointF(fw, y2)); darkGradRed.setStart(QPointF(fw, fw)); darkGradRed.setFinalStop(QPointF(fw, y1)); } lightGradGreen.setStart(QPointF(fw, y2)); lightGradGreen.setFinalStop(QPointF(fw, h)); lightGradYellow.setStart(QPointF(fw, y1)); lightGradYellow.setFinalStop(QPointF(fw, y2)); lightGradRed.setStart(QPointF(fw, fw)); lightGradRed.setFinalStop(QPointF(fw, y1)); #ifdef _USE_CLIPPER if(yv < y1) { // Red section: pr.setTop(fw); pr.setHeight(yv); p.fillRect(pr, QBrush(darkGradRed)); // dark red pr.setTop(yv); pr.setHeight(y1-yv); p.fillRect(pr & rect, QBrush(lightGradRed)); // light red // Yellow section: pr.setTop(y1); pr.setHeight(y2-y1); p.fillRect(pr & rect, QBrush(lightGradYellow)); // light yellow // Green section: pr.setTop(y2); pr.setHeight(h-y2); p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } else if(yv < y2) { // Red section: pr.setTop(fw); pr.setHeight(y1); p.fillRect(pr & rect, QBrush(darkGradRed)); // dark red // Yellow section: pr.setTop(y1); pr.setHeight(yv-y1); p.fillRect(pr & rect, QBrush(darkGradYellow)); // dark yellow pr.setTop(yv); pr.setHeight(y2-yv); p.fillRect(pr & rect, QBrush(lightGradYellow)); // light yellow // Green section: pr.setTop(y2); pr.setHeight(h-y2); p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } else //if(yv <= y3) { // Red section: pr.setTop(fw); pr.setHeight(y1); p.fillRect(pr & rect, QBrush(darkGradRed)); // dark red // Yellow section: pr.setTop(y1); pr.setHeight(y2-y1); p.fillRect(pr & rect, QBrush(darkGradYellow)); // dark yellow // Green section: pr.setTop(y2); pr.setHeight(yv-y2); p.fillRect(pr & rect, QBrush(darkGradGreen)); // dark green pr.setTop(yv); pr.setHeight(h-yv); p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } } else // Meter type is linear... { pr.setTop(fw); pr.setHeight(yv); p.fillRect(pr & rect, QBrush(darkGradGreen)); // dark green pr.setTop(yv); pr.setHeight(h-yv); p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } #else // NOT _USE_CLIPPER if(pixv < y1) { // Red section: { QPainterPath path; path.addRect(fw, fw, w, pixv); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradRed) : _bgColor); // dark red } { QPainterPath path; path.addRect(fw, pixv, w, y1-pixv); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradRed)); // light red } // Yellow section: { QPainterPath path; path.addRect(fw, y1, w, y2-y1); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradYellow)); // light yellow } // Green section: { QPainterPath path; path.addRect(fw, y2, w, h-y2); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradGreen)); // light green } } else if(pixv < y2) { // Red section: { QPainterPath path; path.addRect(fw, fw, w, y1); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradRed) : _bgColor); // dark red } // Yellow section: { QPainterPath path; path.addRect(fw, y1, w, pixv-y1); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradYellow) : _bgColor); // dark yellow } { QPainterPath path; path.addRect(fw, pixv, w, y2-pixv); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradYellow)); // light yellow } // Green section: { QPainterPath path; path.addRect(fw, y2, w, h-y2); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradGreen)); // light green } } else //if(yv <= y3) { // Red section: { QPainterPath path; path.addRect(fw, fw, w, y1); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradRed) : _bgColor); // dark red } // Yellow section: { QPainterPath path; path.addRect(fw, y1, w, y2-y1); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradYellow) : _bgColor); // dark yellow } // Green section: { QPainterPath path; path.addRect(fw, y2, w, pixv-y2); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradGreen) : _bgColor); // dark green } { QPainterPath path; path.addRect(fw, pixv, w, h-pixv); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradGreen)); // light green } } // Separators: { QRect r(0, y1, w, 1); r &= rect; if(!r.isNull()) p.fillRect(r, separator_color); } { QRect r(0, y2, w, 1); r &= rect; if(!r.isNull()) p.fillRect(r, separator_color); } } else // Meter type is linear... { if (_vu3d) { darkGradGreen.setStart(QPointF(fw, fw)); darkGradGreen.setFinalStop(QPointF(fw, h)); } lightGradGreen.setStart(QPointF(fw, fw)); lightGradGreen.setFinalStop(QPointF(fw, h)); { QPainterPath path; path.addRect(fw, fw, w, pixv); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradGreen) : _bgColor); // dark green } { QPainterPath path; path.addRect(fw, pixv, w, h-pixv); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradGreen)); // light green } } #endif // NOT _USE_CLIPPER } else // Horizontal meter { //QRect pr(0, 0, w, 0); if(mtype == DBMeter) // Meter type is dB... { double range = maxScale - minScale; int x1 = int(redScale * w / range); int x2 = int(yellowScale * w / range); if (_vu3d) { darkGradGreen.setStart(QPointF(x2, fw)); darkGradGreen.setFinalStop(QPointF(w, fw)); darkGradYellow.setStart(QPointF(x1, fw)); darkGradYellow.setFinalStop(QPointF(x2, fw)); darkGradRed.setStart(QPointF(fw, fw)); darkGradRed.setFinalStop(QPointF(x1, fw)); } lightGradGreen.setStart(QPointF(x2, fw)); lightGradGreen.setFinalStop(QPointF(w, fw)); lightGradYellow.setStart(QPointF(x1, fw)); lightGradYellow.setFinalStop(QPointF(x2, fw)); lightGradRed.setStart(QPointF(fw, fw)); lightGradRed.setFinalStop(QPointF(x1, fw)); #ifdef _USE_CLIPPER if(yv < y1) { // Red section: pr.setTop(fw); pr.setHeight(yv); p.fillRect(pr, QBrush(darkGradRed)); // dark red pr.setTop(yv); pr.setHeight(y1-yv); p.fillRect(pr & rect, QBrush(lightGradRed)); // light red // Yellow section: pr.setTop(y1); pr.setHeight(y2-y1); p.fillRect(pr & rect, QBrush(lightGradYellow)); // light yellow // Green section: pr.setTop(y2); pr.setHeight(h-y2); p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } else if(yv < y2) { // Red section: pr.setTop(fw); pr.setHeight(y1); p.fillRect(pr & rect, QBrush(darkGradRed)); // dark red // Yellow section: pr.setTop(y1); pr.setHeight(yv-y1); p.fillRect(pr & rect, QBrush(darkGradYellow)); // dark yellow pr.setTop(yv); pr.setHeight(y2-yv); p.fillRect(pr & rect, QBrush(lightGradYellow)); // light yellow // Green section: pr.setTop(y2); pr.setHeight(h-y2); p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } else //if(yv <= y3) { // Red section: pr.setTop(fw); pr.setHeight(y1); p.fillRect(pr & rect, QBrush(darkGradRed)); // dark red // Yellow section: pr.setTop(y1); pr.setHeight(y2-y1); p.fillRect(pr & rect, QBrush(darkGradYellow)); // dark yellow // Green section: pr.setTop(y2); pr.setHeight(yv-y2); p.fillRect(pr & rect, QBrush(darkGradGreen)); // dark green pr.setTop(yv); pr.setHeight(h-yv); p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } } else // Meter type is linear... { pr.setTop(fw); pr.setHeight(yv); p.fillRect(pr & rect, QBrush(darkGradGreen)); // dark green pr.setTop(yv); pr.setHeight(h-yv); p.fillRect(pr & rect, QBrush(lightGradGreen)); // light green } #else // NOT _USE_CLIPPER if(pixv < x1) { // Red section: { QPainterPath path; path.addRect(fw, fw, pixv, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradRed) : _bgColor); // dark red } { QPainterPath path; path.addRect(pixv, fw, x1-pixv, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradRed)); // light red } // Yellow section: { QPainterPath path; path.addRect(x1, fw, x2-x1, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradYellow)); // light yellow } // Green section: { QPainterPath path; path.addRect(x2, fw, w-x2, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradGreen)); // light green } } else if(pixv < x2) { // Red section: { QPainterPath path; path.addRect(fw, fw, x1, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradRed) : _bgColor); // dark red } // Yellow section: { QPainterPath path; path.addRect(x1, fw, pixv-x1, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradYellow) : _bgColor); // dark yellow } { QPainterPath path; path.addRect(pixv, fw, x2-pixv, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradYellow)); // light yellow } // Green section: { QPainterPath path; path.addRect(x2, fw, w-x2, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradGreen)); // light green } } else //if(yv <= y3) { // Red section: { QPainterPath path; path.addRect(fw, fw, x1, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradRed) : _bgColor); // dark red } // Yellow section: { QPainterPath path; path.addRect(x1, fw, x2-x1, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradYellow) : _bgColor); // dark yellow } // Green section: { QPainterPath path; path.addRect(x2, fw, pixv-x2, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradGreen) : _bgColor); // dark green } { QPainterPath path; path.addRect(pixv, fw, w-pixv, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradGreen)); // light green } } // Separators: { QRect r(x1, 0, 1, h); r &= rect; if(!r.isNull()) p.fillRect(r, separator_color); } { QRect r(x2, 0, 1, h); r &= rect; if(!r.isNull()) p.fillRect(r, separator_color); } } else // Meter type is linear... { if (_vu3d) { darkGradGreen.setStart(QPointF(fw, fw)); darkGradGreen.setFinalStop(QPointF(w, fw)); } lightGradGreen.setStart(QPointF(fw, fw)); lightGradGreen.setFinalStop(QPointF(w, fw)); { QPainterPath path; path.addRect(fw, fw, pixv, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, QBrush(lightGradGreen)); // light green } { QPainterPath path; path.addRect(pixv, fw, w, h); path &= drawPath; if(!path.isEmpty()) p.fillPath(path, _vu3d ? QBrush(darkGradGreen) : _bgColor); // dark green } } #endif // NOT _USE_CLIPPER } } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void Meter::resizeEvent(QResizeEvent* ev) { // For some reason upon resizing we get double calls here and in paintEvent. //printf("Meter::resizeEvent w:%d h:%d\n", ev->size().width(), ev->size().height()); cur_pixv = -1; // Force re-initialization. QFrame::resizeEvent(ev); //update(); //according to docs, update will be called automatically QSize s = ev->size(); const QFontMetrics fm = fontMetrics(); // reposition slider if(_orient == Qt::Horizontal) { switch(_scalePos) { case Top: d_scale.setGeometry(this->rect().x(), this->rect().y() + s.height() - 1 - _scaleDist, s.width(), ScaleDraw::Top); break; case Bottom: d_scale.setGeometry(this->rect().x(), this->rect().y() + s.height() + _scaleDist, s.width(), ScaleDraw::Bottom); break; case InsideHorizontal: d_scale.setGeometry(this->rect().x(), this->rect().y() + d_scale.maxHeight(fm) + _scaleDist, s.width(), ScaleDraw::InsideHorizontal); break; default: break; } } else // d_orient == Qt::Vertical { switch(_scalePos) { case Left: d_scale.setGeometry(this->rect().x() - _scaleDist, this->rect().y(), s.height(), ScaleDraw::Left); break; case Right: d_scale.setGeometry(this->rect().x() + width() + _scaleDist, this->rect().y(), s.height(), ScaleDraw::Right); break; case InsideVertical: { const int mxlw = d_scale.maxLabelWidth(fm, false); const int sclw = d_scale.scaleWidth(); d_scale.setGeometry(this->rect().x() + mxlw + sclw + _scaleDist, this->rect().y(), s.height(), ScaleDraw::InsideVertical); } break; default: break; } } adjustScale(); } void Meter::adjustScale() { // d_maxMinor = maxMin; // if(hasUserScale()) // d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor, mstep, log()); // else // d_scale.setScale(minValue(), maxValue(), d_maxMajor, d_maxMinor, log()); // update(); // const double range = maxScale() - minScale(); // if(range == 0.0) // return; // // int maxMaj = 5; // int maxMin = 3; // double mstep = scaleStep(); // // QFontMetrics fm = fontMetrics(); // if(_orient == Qt::Horizontal) // { // int unit_w = fm.width("888.8888"); // if(unit_w == 0) // unit_w = 20; // // if(hasUserScale()) // { // if(d_sliderRect.width() != 0) // { // const int fact = (int)(3.0 * range / (double)(d_sliderRect.width())) + 1; // mstep *= fact; // } // } // else // { // maxMaj = (int)((double)(d_sliderRect.width()) / (1.5 * ((double)unit_w))); // if(maxMaj < 1) // maxMaj = 1; // if(maxMaj > 5) // maxMaj = 5; // } // maxMin = (int)((double)(d_sliderRect.width()) / (1.5 * ((double)unit_w))); // if(maxMin < 1) // maxMin = 1; // if(maxMin > 5) // maxMin = 5; // } // else // { // int unit_h = fm.height(); // if(unit_h == 0) // unit_h = 20; // // if(hasUserScale()) // { // if(d_sliderRect.height() != 0) // { // const int fact = (int)(3.0 * range / (double)(d_sliderRect.height())) + 1; // mstep *= fact; // } // } // else // { // maxMaj = (int)((double)(d_sliderRect.height()) / (1.5 * ((double)unit_h))); // if(maxMaj < 1) // maxMaj = 1; // if(maxMaj > 5) // maxMaj = 5; // } // maxMin = (int)((double)(d_sliderRect.height()) / (1.5 * ((double)unit_h))); // if(maxMin < 1) // maxMin = 1; // if(maxMin > 5) // maxMin = 5; // } // // //fprintf(stderr, "Slider::adjustScale: maxMaj:%d maxMin:%d scaleStep:%f\n", maxMaj, maxMin, mstep); // d_maxMajor = maxMaj; } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void Meter::mousePressEvent(QMouseEvent*) { emit mousePress(); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/meter.h000066400000000000000000000136671404105311000171610ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: meter.h,v 1.1.1.1.2.2 2009/05/03 04:14:00 terminator356 Exp $ // redesigned by oget on 2011/08/15 // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2011 Orcan Ogetbil (ogetbilo at sf.net) // (C) Copyright 2011-2016 Tim E. Real (terminator356 on users DOT sourceforge DOT net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __METER_H__ #define __METER_H__ #include #include #include #include #include #include #include #include #include #include #include "sclif.h" #include "scldraw.h" namespace MusEGui { // ----------------------------------------------- // MeterLayout: // Convenience class that can align the ends of meters // with class Slider scale end points, for example. // ----------------------------------------------- class MeterLayout : public QVBoxLayout { Q_OBJECT Q_PROPERTY(int endsMargin READ meterEndsMargin WRITE setMeterEndsMargin) int _endsMargin; QHBoxLayout* _hlayout; QSpacerItem* _spacer1; QSpacerItem* _spacer2; public: MeterLayout(int endsMargin = 0, QWidget* parent = nullptr); int meterEndsMargin() const; void setMeterEndsMargin(int m); // This is the horizontal layout where meters can be added. QHBoxLayout* hlayout(); }; // ----------------------------------------------- // Meter: // Convenience class that can align the ends of meters // with class Slider scale end points, for example. // ----------------------------------------------- class Meter : public QFrame, public ScaleIf { Q_OBJECT Q_PROPERTY(int radius READ radius WRITE setRadius) Q_PROPERTY(bool vu3d READ vu3d WRITE setVu3d) int _radius; int _vu3d; public: enum MeterType {DBMeter, LinMeter}; enum ScalePos { None, Left, Right, Top, Bottom, InsideHorizontal, InsideVertical }; private: QColor _primaryColor; QColor _bgColor; bool _frame; QColor _frameColor; protected: QLinearGradient darkGradRed; QColor dark_red_end; QColor dark_red_begin; QLinearGradient darkGradYellow; QColor dark_yellow_end; QColor dark_yellow_center; QColor dark_yellow_begin; QLinearGradient darkGradGreen; QColor dark_green_end; QColor dark_green_begin; QLinearGradient lightGradRed; QColor light_red_end; QColor light_red_begin; QLinearGradient lightGradYellow; QColor light_yellow_end; QColor light_yellow_center; QColor light_yellow_begin; QLinearGradient lightGradGreen; QColor light_green_end; QColor light_green_begin; QLinearGradient maskGrad; QColor mask_center; QColor mask_edge; QColor separator_color; QColor peak_color; // int xrad, yrad; virtual void resizeEvent(QResizeEvent*); virtual void paintEvent(QPaintEvent*); virtual void mousePressEvent(QMouseEvent*); // Adjust scale so marks are not too close together. void adjustScale(); private: MeterType mtype; Qt::Orientation _orient; ScalePos _scalePos; int _refreshRate; int _scaleDist; bool overflow; double val; double targetVal; double targetValStep; double maxVal; double targetMaxVal; double minScale, maxScale; int yellowScale, redScale; int cur_pixv, last_pixv, cur_pixmax, last_pixmax; bool _showText; QString _text; QRect _textRect; void updateText(double val); void drawVU(QPainter& p, const QRect&, const QPainterPath&, int); void scaleChange(); QTimer fallingTimer; public slots: void resetPeaks(); void setVal(double, double, bool); void updateTargetMeterValue(); signals: void mousePress(); public: Meter(QWidget* parent, MeterType type = DBMeter, Qt::Orientation orient = Qt::Vertical, double scaleMin = -60.0, double scaleMax = 10.0, ScalePos scalePos = None, const QColor& primaryColor = QColor(0, 255, 0), ScaleDraw::TextHighlightMode textHighlightMode = ScaleDraw::TextHighlightNone, int refreshRate = 20); // QColor primaryColor() const { return _primaryColor; } void setPrimaryColor(const QColor& color, const QColor& bgColor = Qt::black); void setRange(double min, double max); void setRefreshRate(int rate); bool showText() const { return _showText; } void setShowText(bool v) { _showText = v; update(); } Qt::Orientation orientation() const { return _orient; } void setOrientation(Qt::Orientation o) { _orient = o; update(); } virtual QSize sizeHint() const; int radius() const { return _radius; } void setRadius(int radius) { _radius = radius; } int vu3d() const { return _vu3d; } void setVu3d(int vu3d) { _vu3d = vu3d; } void setFrame(bool frame, const QColor& color) { _frame = frame; _frameColor = color; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/mlabel.cpp000066400000000000000000000021771404105311000176260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mlabel.cpp,v 1.1.1.1 2003/10/27 18:55:03 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "mlabel.h" namespace MusEGui { void MLabel::mousePressEvent(QMouseEvent*) { emit mousePressed(); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/mlabel.h000066400000000000000000000032751404105311000172730ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mlabel.h,v 1.1.1.1 2003/10/27 18:55:03 wschweer Exp $ // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MLABEL_H__ #define __MLABEL_H__ #include namespace MusEGui { //--------------------------------------------------------- // MLabel // label widged which sends signal mousePressed // on mousePressEvent //--------------------------------------------------------- class MLabel : public QLabel { Q_OBJECT protected: virtual void mousePressEvent(QMouseEvent*); signals: void mousePressed(); public: MLabel(const QString& txt, QWidget* parent, const char* name = 0) : QLabel(txt, parent) {setObjectName(name);}; MLabel(QWidget* parent, const char* name = 0) : QLabel(parent) {setObjectName(name);}; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/mmath.cpp000066400000000000000000000167341404105311000175040ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/mmath.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "muse_math.h" #include "mmath.h" // QwtMath - a set of mathematical routines // // qwtGetMin -- Find the smallest value in an array // qwtGetMax -- Find the largest value in an array // qwtTwistArray -- invert the order of an array // qwtFloor125 -- Find the largest value fitting in a 1-2-5 pattern // qwtCeil125 -- Find the smallest value fitting in a 1-2-5 pattern // qwtChkMono -- Check for monotony // qwtLinSpace -- construct an array of equally spaced values // qwtLogSpace -- construct an array of logarithmically equally spaced values // qwtMax -- Return the largest of two values // qwtMin -- Return the smallest of two values // qwtAbs -- return the absolute value // qwtSign -- Return the sign of a number // qwtSqr -- Return the square of a number // qwtCopyArray -- Copy an array into another // qwtShiftArray -- Shift an array // qwtSwap -- Swap two values // qwtSort (1) -- Sort two values // qwtSort (2) -- Sort two values // qwtInt -- Return nearest integer // qwtLim -- Limit a values namespace MusECore { //------------------------------------------------------------ //.F qwtGetMin // Find the smallest value in an array // //.u Syntax //.f double qwtGetMin(double *array, int size) // //.u Parameters //.p double *array, int size // //------------------------------------------------------------ double qwtGetMin(double *array, int size) { double rv; int i; if (size > 0) { rv = array[0]; for (i=1; i< size; i++) rv = qwtMin(rv, array[i]); return rv; } else return 0.0; } //------------------------------------------------------------ // //.F qwtGetMax // Find the largest value in an array // //.u Syntax //.f double qwtGetMax(double *array, int size) // //.u Parameters //.p double *array, int size // //------------------------------------------------------------ double qwtGetMax(double *array, int size) { double rv; int i; if (size > 0) { rv = array[0]; for (i=1; i< size; i++) rv = qwtMax(rv, array[i]); return rv; } else return 0.0; } //------------------------------------------------------------ // //.F qwtCeil125 // Find the smallest value out of {1,2,5}*10^n with an integer number n // which is greater than or equal to x // //.u Syntax //.f double qwtCeil125(double x) // //.u Parameters //.p double x // //------------------------------------------------------------ double qwtCeil125( double x) { double lx, rv; double p10, fr; double sign = ( x > 0) ? 1.0 : -1.0; if (x == 0.0) return 0.0; lx = log10(fabs(x)); p10 = floor(lx); fr = exp10(lx - p10); if (fr <=1.0) fr = 1.0; else if (fr <= 2.0) fr = 2.0; else if (fr <= 5.0) fr = 5.0; else fr = 10.0; rv = fr * exp10(p10); return sign * rv; } //------------------------------------------------------------ // //.F qwtFloor125 // Find the largest value out of {1,2,5}*10^n with an integer number n // which is smaller than or equal to x // //.u Syntax //.f double qwtFloor125(double x) // //.u Parameters //.p double x // //------------------------------------------------------------ double qwtFloor125( double x) { double lx, rv; double p10, fr; double sign = ( x > 0) ? 1.0 : -1.0; if (x == 0.0) return 0.0; lx = log10(fabs(x)); p10 = floor(lx); fr = exp10(lx - p10); if (fr >= 10.0) fr = 10.0; else if (fr >= 5.0) fr = 5.0; else if (fr >= 2.0) fr = 2.0; else fr = 1.0; rv = fr * exp10(p10); return sign * rv; } //------------------------------------------------------------ // //.F qwtChkMono // Checks if an array is a strictly monotonic sequence // //.u Syntax //.f int qwtChkMono(double *array, int size) // //.u Parameters //.p double *array -- pointer to a double array // int size -- size of the array // //.u Return Value //.t 0 -- sequence is not strictly monotonic // 1 -- sequence is strictly monotonically increasing // -1 -- sequence is strictly monotonically decreasing // //------------------------------------------------------------ int qwtChkMono(double *array, int size) { int rv, i; if (size < 2) return 0; rv = qwtSign(array[1] - array[0]); for (i=1;i 0) { array[0] = xmin; array[imax] = xmax; step = (xmax - xmin) / double(imax); for (i=1;i inline int qwtSign(const T& x) { if (x > T(0)) return 1; else if (x < T(0)) return (-1); else return 0; } inline int qwtInt(double x) { return int(rint(x)); } template inline T qwtAbs (const T& x) { return( x > T(0) ? x : -x ); } template inline const T& qwtMax (const T& x, const T& y) { return ( x > y ? x : y ); } template inline const T& qwtMin ( const T& x, const T& y) { return ( x < y ? x : y ); } template T qwtLim(const T& x, const T& x1, const T& x2) { T rv; T xmin, xmax; xmin = qwtMin(x1, x2); xmax = qwtMax(x1, x2); if ( x < xmin ) rv = xmin; else if ( x > xmax ) rv = xmax; else rv = x; return rv; } } #endif muse-4.0.0/src/muse/widgets/moc_ttoolbar.cpp000066400000000000000000000017301404105311000210500ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./muse/widgets/moc_ttoolbar.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= muse-4.0.0/src/muse/widgets/musemdiarea.cpp000066400000000000000000000003011404105311000206510ustar00rootroot00000000000000#include #include "musemdiarea.h" MuseMdiArea::MuseMdiArea(QWidget *parent) : QMdiArea(parent) { } void MuseMdiArea::keyPressEvent(QKeyEvent *event) { event->ignore(); } muse-4.0.0/src/muse/widgets/musemdiarea.h000066400000000000000000000003431404105311000203240ustar00rootroot00000000000000#ifndef MUSEMDIAREA_H #define MUSEMDIAREA_H #include class MuseMdiArea : public QMdiArea { Q_OBJECT public: MuseMdiArea(QWidget *); virtual void keyPressEvent(QKeyEvent *); }; #endif // MUSEMDIAREA_H muse-4.0.0/src/muse/widgets/musewidgetsplug.cpp000066400000000000000000000665721404105311000216330ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: musewidgetsplug.cpp,v 1.9.2.9 2009/12/01 03:52:40 terminator356 Exp $ // (C) Copyright 2001-2003 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // this file makes some of the MusE widgets available // to QT-Designer // #include #include // p4.0.2 #include // #include #include #include "poslabel.h" #include "pitchedit.h" #include "pitchlabel.h" #include "sig.h" #include "tempo.h" #include "tempolabel.h" #include "sigedit.h" #include "slider.h" #include "doublelabel.h" #include "checkbox.h" #include "combobox.h" #include "gconfig.h" int sampleRate = 44100; // some dummy values to get things compiled/linked int division = 384; int MusEGlobal::mtcType = 0; bool hIsB = false; static const char* vall[] = { "c","c#","d","d#","e","f","f#","g","g#","a","a#","h" }; static const char* valu[] = { "C","C#","D","D#","E","F","F#","G","G#","A","A#","H" }; MusEGlobal::GlobalConfigValues config = { QStringList(), // pluginLadspaPathList QStringList(), // pluginDssiPathList QStringList(), // pluginVstPathList QStringList(), // pluginLinuxVstPathList QStringList(), // pluginLv2PathList 170, // globalAlphaBlend { QColor(0xff, 0xff, 0xff), // palette QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff), QColor(0xff, 0xff, 0xff) }, { QColor(255, 232, 140), // part colors QColor(0xff, 0x00, 0x00), QColor(0x00, 0xff, 0x00), QColor(0x00, 0x00, 0xff), QColor(0xff, 0xff, 0x00), QColor(0x00, 0xff, 0xff), QColor(0xff, 0x00, 0xff), QColor(0x9f, 0xc7, 0xef), QColor(0x00, 0xff, 0x7f), QColor(0x7f, 0x00, 0x00), QColor(0x00, 0x7f, 0x00), QColor(0x00, 0x00, 0x7f), QColor(0x7f, 0x7f, 0x3f), QColor(0x00, 0x7f, 0x7f), QColor(0x7f, 0x00, 0x7f), QColor(0x00, 0x7f, 0xff), QColor(0x00, 0x3f, 0x3f) }, { QString("Default"), // Default part color names QString("Refrain"), QString("Bridge"), QString("Intro"), QString("Coda"), QString("Chorus"), QString("Solo"), QString("Brass"), QString("Percussion"), QString("Drums"), QString("Guitar"), QString("Bass"), QString("Flute"), QString("Strings"), QString("Keyboard"), QString("Piano"), QString("Saxophone") }, QColor(0, 0, 255), // transportHandleColor; QColor(255, 0, 0), // bigTimeForegroundColor; QColor(0, 0, 0), // bigTimeBackgroundColor; QColor(200, 200, 200), // waveEditBackgroundColor; { QFont(QString("arial"), 10, QFont::Normal), QFont(QString("arial"), 7, QFont::Normal), // Mixer strips and midi track info panel QFont(QString("arial"), 10, QFont::Normal), QFont(QString("arial"), 10, QFont::Bold), QFont(QString("arial"), 8, QFont::Normal), // Small numbers: Timescale and markers, part name overlay QFont(QString("arial"), 8, QFont::Bold), // Small bold numbers such as marker text QFont(QString("arial"), 8, QFont::Bold, true) // Mixer strip labels. Looks and fits better with bold + italic than bold alone, // at the price of only few more pixels than Normal mode. }, QColor(0xff, 0xff, 0xff), // trackBg; QColor(0x80, 0xff, 0x80), // selected track Bg; QColor(0x00, 0x00, 0x00), // selected track Fg; Qt::gray, // trackSectionDividerColor; QColor(0, 160, 255), // midiTrackLabelBg; // Med blue QColor(150, 177, 189), // drumTrackLabelBg; // Pale pastel blue QColor(116, 232, 242), // newDrumTrackLabelBg; // Light blue Qt::magenta, // waveTrackLabelBg; Qt::green, // outputTrackLabelBg; Qt::red, // inputTrackLabelBg; Qt::yellow, // groupTrackLabelBg; QColor(142, 157, 6), // auxTrackLabelBg; // Med olive QColor(255, 130, 0), // synthTrackLabelBg; // Med orange QColor(220, 220, 220), // midiTrackBg; QColor(220, 220, 220), // drumTrackBg; QColor(220, 220, 220), // newDrumTrackBg; QColor(220, 220, 220), // waveTrackBg; QColor(189, 220, 193), // outputTrackBg; QColor(189, 220, 193), // inputTrackBg; QColor(220, 220, 220), // groupTrackBg; QColor(220, 220, 220), // auxTrackBg; QColor(220, 220, 220), // synthTrackBg; QColor(98, 124, 168), // part canvas bg QColor(255, 170, 0), // ctrlGraphFg; Medium orange QColor(0, 0, 0), // mixerBg; QColor(0xe0, 0xe0, 0xe0), // Ruler background QColor(0, 0, 0), // Ruler text QColor(255, 255, 255), // Midi editor canvas QColor(255, 255, 255), // midiControllerViewBg QColor(255, 255, 255), // drumListBg QColor(255, 255, 255), // rulerCurrent Qt::gray, // midiCanvasBeatColor Qt::black, // midiCanvasBarColor Qt::lightGray, // waveNonselectedPart Qt::darkGray, // wavePeakColor Qt::black, // waveRmsColor Qt::lightGray, // wavePeakColorSelected Qt::white, // waveRmsColorSelected Qt::darkGray, // partWaveColorPeak QColor(20,20,20), // partWaveColorRms QColor(54,54,54), // partMidiDarkEventColor QColor(200,200,200), // partMidiLightEventColor QColor(0,181,241 ), // sliderBarDefaultColor QColor(228,203,36 ), // sliderDefaultColor QColor(78,172,35 ), // panSliderColor QColor(209,86,86 ), // gainSliderColor QColor(190,190,39 ), // auxSliderColor QColor(154,135,124), // audioVolumeSliderColor QColor(153,156,124), // midiVolumeSliderColor QColor(37,121,255 ), // audioControllerSliderDefaultColor QColor(220,77,255 ), // audioPropertySliderDefaultColor QColor(37,121,255 ), // midiControllerSliderDefaultColor QColor(220,77,255 ), // midiPropertySliderDefaultColor QColor(100,255,255), // midiPatchReadoutColor QColor(0,221,255 ), // audioMeterPrimaryColor QColor(0,221,255 ), // midiMeterPrimaryColor QColor(208,145,49 ), // rackItemBackgroundColor MusEGlobal::WaveOutLine, // waveDrawing false, // fixFrozenMDISubWindows Turn on a fix for frozen MDIs in Breeze/Oxygen themes. // maxAliasedPointSize At what point size to switch from aliased text to non-aliased text. // Zero means always use anti-aliasing. For certain widgets that use it. May be more later. 8, false, // enableAlsaMidiDriver Whether to enable the ALSA midi driver 384, // division; 1024, // rtcTicks 0, // curMidiSyncInPort The currently selected midi sync input port. true, // midiSendInit Send instrument initialization sequences true, // warnInitPending Warn instrument initialization sequences pending false, // midiSendCtlDefaults Send instrument controller defaults at position 0 if none in song false, // midiSendNullParameters Send null parameters after each (N)RPN event false, // midiOptimizeControllers Don't send redundant H/L parameters or H/L values true, // warnIfBadTiming Warn if timer res not good false, // velocityPerNote Whether to show per-note or all velocities -60, // int minMeter; -60.0, // double minSlider; false, // use Jack freewheel 20, // int guiRefresh; QString(""), // userInstrumentsDir // Obsolete. Must keep for compatibility. //QString(""), // helpBrowser // Obsolete true, // extendedMidi 384, // division for smf export QString(""), // copyright string for smf export 1, // smf export file format false, // midi export file 2 byte timesigs instead of 4 true, // optimize midi export file note offs true, // expRunningStatus; Save space by using running status true, // Split imported tracks into multiple parts. // Obsolete. There is only 'New' drum tracks now. // true, // importMidiNewStyleDrum true, // importDevNameMetas Import Prefer Device Name metas over port number metas if both exist. true, // importInstrNameMetas Import Prefer Instrument Name metas over Mode sysexes if both exist. MusEGlobal::PORT_NUM_META | MusEGlobal::DEVICE_NAME_META, // exportPortsDevices. Or'd ExportPortsDevices_t flags. Export port number metas and/or device name metas. true, // exportPortDeviceSMF0 Export a port and/or device meta even for SMF0. MusEGlobal::MODE_SYSEX | MusEGlobal::INSTRUMENT_NAME_META, // exportModeInstr. Or'd ExportModeInstr_t flags. Export mode sysexes and/or instrument name metas. QString("GM"), // importMidiDefaultInstr Default to this instrument not Generic, if no match found true, // exportDrumMapOverrides Apply Port, Channel, and ANote drum map overrides to export true, // exportChannelOverridesToNewTrack Drum map Channel overrides go to a separate track 1, // startMode QString(""), // start song path false, // startSongLoadConfig 384, // gui division QRect(0, 0, 400, 300), // GeometryMain; QRect(0, 0, 200, 100), // GeometryTransport; QRect(0, 0, 600, 200), // GeometryBigTime; { QString("Mixer A"), QStringList(), QRect(0, 0, 300, 500), // Mixer1 true, true, true, true, true, true, true, true, true, MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW, QList() }, { QString("Mixer B"), QStringList(), QRect(200, 200, 300, 500), // Mixer2 true, true, true, true, true, true, true, true, true, MusEGlobal::MixerConfig::STRIPS_TRADITIONAL_VIEW, QList() }, true, // TransportVisible; false, // BigTimeVisible; false, // mixer1Visible; false, // mixer2Visible; false, // markerVisible; true, // arrangerVisible; true, // showSplashScreen 1, // canvasShowPartType 1 - names, 2 events 5, // canvasShowPartEvent true, // canvasShowGrid; false, // canvasShowGridHorizontalAlways false, // canvasShowGridBeatsAlways QString(""), // canvasBgPixmap; QStringList(), // canvasCustomBgList QString(""), // default styleSheetFile - For built-in set to ":/style.qss" QString(""), // style QString("sweep"), // externalWavEditor false, // useOldStyleStopShortCut false, // moveArmedCheckBox true, // useDenormalBias false, // useOutputLimiter true, // showDidYouKnow false, // vstInPlace Enable VST in-place processing 44100, // Dummy audio preferred sample rate 512, // Dummy audio buffer size QString("./"), // projectBaseFolder true, // projectStoreInFolder true, // useProjectSaveDialog 256, // minControlProcessPeriod false, // popupsDefaultStayOpen false, // leftMouseButtonCanDecrease false, // rangeMarkerWithoutMMB MusECore::DONT_REC_MUTED_OR_HIDDEN, true, // addHiddenTracks true, // unhideTracks // Obsolete. There is only 'New' drum tracks now. // MusEGlobal::PREFER_NEW, // drumTrackPreference true, // smartFocus 20, // trackHeight true, // borderlessMouse false, // autoSave false, // scrollableSubMenus true, // liveWaveUpdate true, // warnOnFileVersions Warn if file version different than current MusEGlobal::CONF_LV2_UI_USE_FIRST, //lv2UiBehavior true, // preferKnobsVsSliders Whether to prefer the use of knobs over sliders, esp in mixer. true, // showControlValues Whether to show the value along with label in small controls, esp in mixer. true, // monitorOnRecord Whether to automatically monitor on record arm. QString("klick1.wav"), // measSample QString("klick2.wav"), // beatSample QString("klick3.wav"), // accent1Sample QString("klick4.wav"), // accent2Sample MusEGlobal::RoutePreferCanonicalName, // preferredRouteNameOrAlias true, // routerExpandVertically 2 // routerGroupingChannels }; //--------------------------------------------------------- // pitch2string //--------------------------------------------------------- QString pitch2string(int v) { if (v < 0 || v > 127) return QString("----"); int octave = (v / 12) - 2; QString o = QString::number(octave); int i = v % 12; QString s(octave < 0 ? valu[i] : vall[i]); if (hIsB) { if (s == "h") s = "b"; else if (s == "H") s = "B"; } return s + o; } /* XPM */ static const char* slider_pixmap[]={ "22 22 50 1", ". c None", "f c #004005", "g c #004007", "h c #004107", "m c #004108", "j c #00430a", "E c #00501f", "s c #005021", "e c #014006", "l c #024006", "F c #095e34", "D c #0b572a", "k c #0b582b", "n c #0f5328", "u c #12562d", "o c #155a35", "p c #165c38", "q c #165c39", "i c #17501a", "I c #175522", "r c #18603f", "N c #18795e", "t c #187a60", "R c #1e5a29", "y c #22633d", "O c #307755", "B c #408262", "v c #439191", "G c #468667", "d c #4c7a51", "H c #4d8a6c", "J c #569174", "C c #599276", "P c #5e967a", "A c #63b1c2", "V c #659477", "Q c #659b80", "S c #6da087", "w c #70b2bc", "x c #72b5c0", "z c #74b7c3", "K c #79a891", "a c #7ea48a", "T c #8cb4a0", "L c #a3c3b3", "M c #b7d8d1", "U c #bedcd5", "c c #c3d2c3", "b c #f1f5f1", "# c #ffffff", "......................", "......................", "......................", "......................", "......................", "......###a............", "......#bcad...........", "......#bcad...........", "......#bcad...........", "..efgh#bcaihhhhhhhjk..", ".lmnop#bcaippppppqrst.", ".huvwx#bcayxxxxxxzABC.", ".DEFGH#bcaIHHHHHHJKLM.", "..NOPQ#bcaRQQQQQQSTU..", "......#bcad...........", "......#bcad...........", "......#bcad...........", "......VVVVd...........", ".......dddd...........", "......................", "......................", "......................"}; static const char *posedit_pixmap[] = { "22 22 8 1", " c Gray100", ". c Gray97", "X c #4f504f", "o c #00007f", "O c Gray0", "+ c none", "@ c Gray0", "# c Gray0", "++++++++++++++++++++++", "++++++++++++++++++++++", "++++++++++++++++++++++", "++++++++++++++++++++++", "+OOOOOOOOOOOOOOOOOOOO+", "OOXXXXXXXXXXXXXXXXXXOO", "OXX. OO OO O", "OX. oo O O", "OX. oo O .O", "OX ooo oooo O O", "OX oo oo oo O O", "OX oooo oo oo O O", "OX oo oo oo oo O O", "OX oo oo oo oo O O", "OX oooo oooo O O", "OX OO OO O", "OO..................OO", "+OOOOOOOOOOOOOOOOOOOO+", "++++++++++++++++++++++", "++++++++++++++++++++++", "++++++++++++++++++++++", "++++++++++++++++++++++" }; static const char *pitchedit_pixmap[] = { "22 22 8 1", " c Gray100", ". c Gray97", "X c #4f504f", "o c #00007f", "O c Gray0", "+ c none", "@ c Gray0", "# c Gray0", "++++++++++++++++++++++", "++++++++++++++++++++++", "++++++++++++++++++++++", "++++++++++++++++++++++", "+OOOOOOOOOOOOOOOOOOOO+", "OOXXXXXXXXXXXXXXXXXXOO", "OXX. OO OO O", "OX. o O O", "OX. oo O .O", "OX o o O O", "OX o O O", "OX o o O O", "OX oooo O O", "OX o O O", "OX O O", "OX OO OO O", "OO..................OO", "+OOOOOOOOOOOOOOOOOOOO+", "++++++++++++++++++++++", "++++++++++++++++++++++", "++++++++++++++++++++++", "++++++++++++++++++++++" }; //--------------------------------------------------------- // MusEPlugin //--------------------------------------------------------- class MusEPlugin : public QWidgetPlugin { public: MusEPlugin() {} ~MusEPlugin() {} QStringList keys() const; QWidget* create(const QString& key, QWidget* parent=0, const char* name = 0); QString group(const QString& key) const; QIcon iconSet(const QString& key) const; QString includeFile(const QString& key) const; QString toolTip(const QString& key) const; QString whatsThis(const QString& key) const; bool isContainer(const QString& key) const; }; //--------------------------------------------------------- // keys //--------------------------------------------------------- QStringList MusEPlugin::keys() const { QStringList list; list << QString("PosEdit") << QString("PitchEdit") << QString("PosLabel") << QString("PitchLabel") << QString("TempoLabel") << QString("TempoEdit") << QString("SigEdit") << QString("Slider") << QString("DoubleLabel") << QString("CheckBox") << QString("ComboBox") ; return list; } //--------------------------------------------------------- // create //--------------------------------------------------------- QWidget* MusEPlugin::create(const QString& key, QWidget* parent, const char* name) { if (key == QString("PosEdit")) return new PosEdit(parent, name); else if (key == QString("PitchEdit")) return new PitchEdit(parent, name); else if (key == QString("PitchLabel")) return new PitchLabel(parent, name); else if (key == QString("PosLabel")) return new PosLabel(parent, name); else if (key == QString("TempoLabel")) return new TempoLabel(parent, name); else if (key == QString("TempoEdit")) return new TempoEdit(parent, name); else if (key == QString("SigEdit")) return new SigEdit(parent, name); else if (key == QString("Slider")) return new Slider(parent, name); else if (key == QString("DoubleLabel")) return new DoubleLabel(parent, name); else if (key == QString("CheckBox")) return new CheckBox(parent, -1, name); else if (key == QString("ComboBox")) return new ComboBox(parent, name); return 0; } //--------------------------------------------------------- // group //--------------------------------------------------------- QString MusEPlugin::group(const QString& /*key*/) const { return QString("MusE"); } //--------------------------------------------------------- // iconSet //--------------------------------------------------------- QIcon MusEPlugin::iconSet(const QString& key) const { if (key == "PosEdit" || key == "PosLabel") return QIcon(QPixmap(posedit_pixmap)); else if (key == "PitchEdit" || key == "PitchLabel") return QIcon(QPixmap(pitchedit_pixmap)); else if (key == "TempoEdit" || key == "TempoLabel") return QIcon(QPixmap(pitchedit_pixmap)); else if (key == "SigEdit") return QIcon(QPixmap(pitchedit_pixmap)); else if (key == QString("Slider")) return QIcon(QPixmap(slider_pixmap)); // else if (key == QString("CheckBox")) // return QIconSet(QPixmap(slider_pixmap)); // else if (key == QString("ComboBox")) // return QIconSet(QPixmap(slider_pixmap)); return QIcon(); } //--------------------------------------------------------- // includeFile //--------------------------------------------------------- QString MusEPlugin::includeFile(const QString& key) const { if (key == QString("PosEdit")) return QString("posedit.h"); else if (key == QString("PitchEdit")) return QString("pitchedit.h"); else if (key == QString("PitchLabel")) return QString("pitchlabel.h"); else if (key == QString("PosLabel")) return QString("poslabel.h"); else if (key == QString("TempoLabel")) return QString("tempolabel.h"); else if (key == QString("TempoEdit")) return QString("tempolabel.h"); else if (key == QString("SigEdit")) return QString("sigedit.h"); else if (key == QString("Slider")) return QString("slider.h"); else if (key == QString("DoubleLabel")) return QString("dentry.h"); else if (key == QString("CheckBox")) return QString("checkbox.h"); else if (key == QString("ComboBox")) return QString("combobox.h"); return QString::null; } //--------------------------------------------------------- // toolTip //--------------------------------------------------------- QString MusEPlugin::toolTip (const QString& key) const { if (key == QString("PosEdit")) return QString("midi time position editor"); else if (key == QString("PitchEdit")) return QString("midi pitch spinbox"); else if (key == QString("PitchLabel")) return QString("midi pitch label"); else if (key == QString("PosLabel")) return QString("midi time position label"); else if (key == QString("TempoLabel")) return QString("midi tempo label"); else if (key == QString("TempoEdit")) return QString("midi tempo spinbox"); else if (key == QString("SigEdit")) return QString("midi signature spinbox"); else if (key == QString("Slider")) return QString("slider for double values"); else if (key == QString("DoubleLabel")) return QString("entry/label for double values"); else if (key == QString("CheckBox")) return QString("checkbox with id"); else if (key == QString("ComboBox")) return QString("combobox with id"); return QString::null; } //--------------------------------------------------------- // whatsThis //--------------------------------------------------------- QString MusEPlugin::whatsThis (const QString& key) const { if (key == QString("PosEdit")) return QString("midi time position editor"); else if (key == QString("PitchEdit")) return QString("midi pitch spinbox"); else if (key == QString("PitchLabel")) return QString("midi pitch label"); else if (key == QString("PosLabel")) return QString("midi time position label"); else if (key == QString("TempoLabel")) return QString("midi tempo label"); else if (key == QString("TempoEdit")) return QString("midi tempo spinbox"); else if (key == QString("SigEdit")) return QString("midi signature spinbox"); else if (key == QString("Slider")) return QString("slider for double values"); else if (key == QString("DoubleLabel")) return QString("entry/label for double values"); else if (key == QString("CheckBox")) return QString("checkbox with id"); else if (key == QString("ComboBox")) return QString("combobox with id"); return QString::null; } //--------------------------------------------------------- // isContainer //--------------------------------------------------------- bool MusEPlugin::isContainer (const QString& /*key*/) const { return false; } Q_EXPORT_PLUGIN(MusEPlugin) muse-4.0.0/src/muse/widgets/pixmap_button.cpp000066400000000000000000000257641404105311000212720ustar00rootroot00000000000000//============================================================================= // MusE // Linux Music Editor // pixmap_button.cpp // (C) Copyright 2011 Tim E. Real (terminator356 on users.sourceforge.net) // // 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; 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; 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 #include #include "pixmap_button.h" namespace MusEGui { ////------------------------------------------ //// PixmapButton ////------------------------------------------ //PixmapButton::PixmapButton(QWidget* parent) // : QWidget(parent) //{ // _onPixmap = 0; // _offPixmap = 0; // _margin = 0; // _checked = false; // _checkable = false; // QFont fnt = font(); // fnt.setPointSize(8); // setFont(fnt); //} //PixmapButton::PixmapButton(QPixmap* on_pixmap, QPixmap* off_pixmap, int margin, QWidget* parent, const QString& text) // : QWidget(parent) //{ // _text = text; // _onPixmap = on_pixmap; // _offPixmap = off_pixmap; // _margin = margin; // _checked = false; // _checkable = false; // if(_offPixmap) // setMinimumSize(_offPixmap->size().width() + 2*_margin, _offPixmap->size().height() + 2*_margin); // else // setMinimumSize(10 + 2*_margin, 10 + 2*_margin); // //font().s // QFont fnt = font(); // fnt.setPointSize(8); // setFont(fnt); //} //QSize PixmapButton::minimumSizeHint () const //{ // return QSize(10, 10); //} //void PixmapButton::setMargin(int v) //{ // _margin = v; // if(_offPixmap) // setMinimumSize(_offPixmap->size().width() + 2*_margin, _offPixmap->size().height() + 2*_margin); // update(); //} //void PixmapButton::setOffPixmap(QPixmap* pm) //{ // _offPixmap = pm; // if(_offPixmap) // setMinimumSize(_offPixmap->size().width() + 2*_margin, _offPixmap->size().height() + 2*_margin); // else // setMinimumSize(10 + 2*_margin, 10 + 2*_margin); // update(); //} //void PixmapButton::setOnPixmap(QPixmap* pm) //{ // _onPixmap = pm; // update(); //} //void PixmapButton::setCheckable(bool v) //{ // _checkable = v; // if(!_checkable) // _checked = false; // update(); //} //void PixmapButton::setChecked(bool v) //{ // if(!_checkable) // return; // if(_checked == v) // return; // _checked = v; // update(); // emit toggled(_checked); //} //void PixmapButton::setDown(bool v) //{ // if(!_checkable) // return; // if(_checked == v) // return; // _checked = v; // update(); //} //void PixmapButton::paintEvent(QPaintEvent* e) //{ // QWidget::paintEvent(e); // QPainter p(this); // int w2 = width() / 2; // int h2 = height() / 2; // int mw = _offPixmap->size().width(); // int mh = _offPixmap->size().height(); // int mw2 = mw / 2; // int mh2 = mh / 2; // if(!_text.isEmpty()) // //p.drawText(w2 - mw2, h2 - mh2, mw, mh, *pm); // p.drawText(_margin, height() - _margin, _text); // else // { // QPixmap* pm = _checked ? _onPixmap : _offPixmap; // if(pm) // p.drawPixmap(w2 - mw2, h2 - mh2, mw, mh, *pm); // } //} //void PixmapButton::mousePressEvent(QMouseEvent* e) //{ // if(_checkable) // _checked = !_checked; // update(); // emit pressed(); // if(_checkable) // emit toggled(_checked); // //e->setAccepted(true); // This makes menu not close when mouse is released. May be desirable with many small buttons... // QWidget::mousePressEvent(e); // Hm, need this so menus can close. //} //void PixmapButton::mouseReleaseEvent(QMouseEvent* e) //{ // emit clicked(_checked); // //e->setAccepted(true); // This makes menu not close when mouse is released. May be desirable with many small buttons... // QWidget::mouseReleaseEvent(e); // Hm, need this so menus can close. //} //void PixmapButton::contextMenuEvent(QContextMenuEvent * e) //{ // e->accept(); //} //------------------------------------------ // IconButton //------------------------------------------ IconButton::IconButton(QWidget* parent, const char* name) : QWidget(parent) { setObjectName(name); _blinking = false; _blinkPhase = false; _iconSetB = false; _iconSize = QSize(16, 16); _onIcon = 0; _offIcon = 0; _onIconB = 0; _offIconB = 0; _margin = 0; _checked = false; _checkable = false; } IconButton::IconButton(QIcon* on_icon, QIcon* off_icon, QIcon* on_iconB, QIcon* off_iconB, bool hasFixedIconSize, bool drawFlat, const QString& text, int margin, QWidget* parent, const char* name) : QWidget(parent), _onIcon(on_icon), _offIcon(off_icon), _onIconB(on_iconB), _offIconB(off_iconB), _hasFixedIconSize(hasFixedIconSize), _drawFlat(drawFlat), _text(text), _margin(margin) { setObjectName(name); _blinking = false; _blinkPhase = false; _iconSetB = false; _checked = false; _checkable = false; _iconSize = QSize(16, 16); } QSize IconButton::sizeHint() const { // TODO Ask style for margins. const QSize isz = iconSize(); const int fmh = fontMetrics().lineSpacing() + 5; const int iw = isz.width() + 2; const int ih = isz.height() + 2; const int h = (_hasFixedIconSize && ih > fmh) ? ih : fmh; const int w = (_hasFixedIconSize && iw > h) ? iw : h + 2; return QSize(w, h); } QSize IconButton::minimumSizeHint () const { return QSize(10, 10); } void IconButton::setText(QString txt) { _text = txt; updateGeometry(); } void IconButton::setIconSize(const QSize sz) { _iconSize = sz; updateGeometry(); } void IconButton::setMargin(int v) { _margin = v; update(); } void IconButton::setOffIcon(QIcon* pm) { _offIcon = pm; update(); } void IconButton::setOnIcon(QIcon* pm) { _onIcon = pm; update(); } void IconButton::setOffIconB(QIcon* pm) { _offIconB = pm; update(); } void IconButton::setOnIconB(QIcon* pm) { _onIconB = pm; update(); } void IconButton::setIconSetB(bool v) { _iconSetB = v; update(); } void IconButton::setCheckable(bool v) { _checkable = v; if(!_checkable) _checked = false; update(); } void IconButton::setChecked(bool v) { if(!_checkable) return; if(_checked == v) return; _checked = v; update(); emit toggled(_checked); } void IconButton::setDown(bool v) { if(!_checkable) return; if(_checked == v) return; _checked = v; update(); } void IconButton::paintEvent(QPaintEvent* ev) { ev->accept(); // if(!_drawFlat) // QToolButton::paintEvent(ev); QIcon::Mode mode; if(isEnabled()) mode = hasFocus() ? QIcon::Selected : QIcon::Normal; else mode = QIcon::Disabled; const bool is_on = ((_blinking || isChecked()) && (!_blinking || !_blinkPhase || !isEnabled())) ? true : false; QIcon* ico = nullptr; QPainter p(this); if(!_text.isEmpty()) //p.drawText(w2 - mw2, h2 - mh2, mw, mh, *pm); p.drawText(_margin, height() - _margin, _text); else { if(_iconSetB) ico = is_on ? _onIconB : _offIconB; else ico = is_on ? _onIcon : _offIcon; if(ico) ico->paint(&p, rect(), Qt::AlignCenter, mode, QIcon::On); } // TODO Bah! Just want a mouse-over rectangle for flat mode but some styles do this or that but not the other thing. // if(const QStyle* st = style()) // { // st = st->proxy(); // // QStyleOptionToolButton o; // // initStyleOption(&o); // // o.rect = rect(); // // //o.state |= QStyle::State_MouseOver; // // o.state = QStyle::State_Active | // // QStyle::State_Enabled | // // QStyle::State_AutoRaise | // This is required to get rid of the panel. // // QStyle::State_MouseOver; // // st->drawPrimitive(QStyle::PE_PanelButtonTool, &o, &p); // // // QStyleOptionFrame o; // // //initStyleOption(&o); // // o.rect = rect(); // // o.features = QStyleOptionFrame::Rounded; // // o.frameShape = QFrame::Box; // // o.lineWidth = 2; // // o.midLineWidth = 4; // // o.state |= QStyle::State_MouseOver; // // st->drawPrimitive(QStyle::PE_Frame, &o, &p); // // // QStyleOptionFocusRect o; // //o.QStyleOption::operator=(option); // //o.rect = st->subElementRect(QStyle::SE_ItemViewItemFocusRect, &option); // o.rect = rect(); // o.state |= QStyle::State_KeyboardFocusChange; // o.state |= QStyle::State_Item | // QStyle::State_Active | // QStyle::State_Enabled | // QStyle::State_HasFocus | // // //QStyle::State_Raised | // QStyle::State_Sunken | // // QStyle::State_Off | // //QStyle::State_On | // // QStyle::State_Selected | // // //QStyle::State_AutoRaise | // This is required to get rid of the panel. // // QStyle::State_MouseOver; // // // QPalette::ColorGroup cg = // // (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; // // o.backgroundColor = option.palette.color(cg, // // (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Window); // st->drawPrimitive(QStyle::PE_FrameFocusRect, &o, &p); // // } } void IconButton::mousePressEvent(QMouseEvent* e) { e->accept(); if(_checkable) _checked = !_checked; update(); emit pressed(); if(_checkable) emit toggled(_checked); // //e->setAccepted(true); // This makes menu not close when mouse is released. May be desirable with many small buttons... // QWidget::mousePressEvent(e); // Hm, need this so menus can close. } void IconButton::mouseReleaseEvent(QMouseEvent* e) { e->accept(); emit clicked(_checked); // //e->setAccepted(true); // This makes menu not close when mouse is released. May be desirable with many small buttons... // QWidget::mouseReleaseEvent(e); // Hm, need this so menus can close. } void IconButton::mouseMoveEvent(QMouseEvent* ev) { ev->accept(); } void IconButton::contextMenuEvent(QContextMenuEvent * e) { e->accept(); } void IconButton::setHasFixedIconSize(bool v) { _hasFixedIconSize = v; updateGeometry(); } void IconButton::setDrawFlat(bool v) { _drawFlat = v; update(); } void IconButton::setBlinking(bool v) { _blinking = v; if(!_blinking) setBlinkPhase(false); } void IconButton::setBlinkPhase(bool v) { if(_blinkPhase == v) return; _blinkPhase = v; if(isEnabled()) update(); } } // MusEGui muse-4.0.0/src/muse/widgets/pixmap_button.h000066400000000000000000000144421404105311000207260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // pixmap_button.h // (C) Copyright 2011 Tim E. Real (terminator356 on users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __PIXMAP_BUTTON_H__ #define __PIXMAP_BUTTON_H__ #include #include #include #include #include #include #include #include class QMouseEvent; class QContextMenuEvent; class QPaintEvent; //class QPixmap; class QIcon; namespace MusEGui { ////--------------------------------------------------------- //// PixmapButton ////--------------------------------------------------------- //class PixmapButton : public QWidget //{ // Q_OBJECT // Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable) // Q_PROPERTY(bool checked READ isChecked WRITE setChecked) // //Q_PROPERTY(QPixmap* offPixmap READ offPixmap WRITE setOffPixmap) // //Q_PROPERTY(QPixmap* onPixmap READ onPixmap WRITE setOnPixmap) // Q_PROPERTY(int margin READ margin WRITE setMargin) // private: // QString _text; // bool _checkable; // bool _checked; // int _margin; // QPixmap* _onPixmap; // QPixmap* _offPixmap; // protected: // virtual void paintEvent(QPaintEvent* ); // virtual void mousePressEvent(QMouseEvent* ); // virtual void mouseReleaseEvent(QMouseEvent* ); // void contextMenuEvent(QContextMenuEvent*); // signals: // void clicked(bool checked = false); // void toggled(bool checked = false); // void pressed(); // public: // PixmapButton(QWidget* parent = 0); // PixmapButton(QPixmap* on_pixmap, QPixmap* off_pixmap, int margin, QWidget* parent = 0, const QString& text = QString()); // virtual QSize minimumSizeHint () const; // virtual bool margin() const { return _margin; } // virtual void setMargin(int v); // virtual bool isChecked() const { return _checked; } // virtual void setChecked(bool); // virtual bool isDown() const { return _checked; } // virtual void setDown(bool); // virtual bool isCheckable() const { return _checkable; } // virtual void setCheckable(bool); // virtual QPixmap* offPixmap() const { return _offPixmap; } // virtual void setOffPixmap(QPixmap*); // virtual QPixmap* onPixmap() const { return _onPixmap; } // virtual void setOnPixmap(QPixmap*); //}; //--------------------------------------------------------- // IconButton //--------------------------------------------------------- class IconButton : public QWidget { Q_OBJECT Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable) Q_PROPERTY(bool checked READ isChecked WRITE setChecked) //Q_PROPERTY(QPixmap* offPixmap READ offPixmap WRITE setOffPixmap) //Q_PROPERTY(QPixmap* onPixmap READ onPixmap WRITE setOnPixmap) Q_PROPERTY(int margin READ margin WRITE setMargin) private: QIcon* _onIcon; QIcon* _offIcon; QIcon* _onIconB; QIcon* _offIconB; bool _hasFixedIconSize; bool _drawFlat; QString _text; int _margin; QSize _iconSize; bool _checkable; bool _checked; bool _iconSetB; bool _blinkPhase; bool _blinking; protected: virtual void paintEvent(QPaintEvent* ); virtual void mousePressEvent(QMouseEvent* ); virtual void mouseReleaseEvent(QMouseEvent* ); virtual void mouseMoveEvent(QMouseEvent* ); virtual void contextMenuEvent(QContextMenuEvent*); signals: void clicked(bool checked = false); void toggled(bool checked = false); void pressed(); public: IconButton(QWidget* parent = 0, const char* name = 0); IconButton(QIcon* on_icon, QIcon* off_icon, QIcon* on_iconB, QIcon* off_iconB, bool hasFixedIconSize = true, bool drawFlat = false, const QString& text = QString(), int margin = 0, QWidget* parent = 0, const char* name = 0); virtual QSize minimumSizeHint () const; virtual bool margin() const { return _margin; } virtual void setMargin(int v); virtual bool isChecked() const { return _checked; } virtual void setChecked(bool); virtual bool isDown() const { return _checked; } virtual void setDown(bool); virtual bool isCheckable() const { return _checkable; } virtual void setCheckable(bool); virtual QIcon* offIcon() const { return _offIcon; } virtual void setOffIcon(QIcon*); virtual QIcon* onIcon() const { return _onIcon; } virtual void setOnIcon(QIcon*); virtual QIcon* offIconB() const { return _offIconB; } virtual void setOffIconB(QIcon*); virtual QIcon* onIconB() const { return _onIconB; } virtual void setOnIconB(QIcon*); virtual bool iconSetB() const { return _iconSetB; } virtual void setIconSetB(bool v); QString text() const { return _text; } void setText(QString txt); bool hasFixedIconSize() const { return _hasFixedIconSize; } void setHasFixedIconSize(bool v); QSize iconSize() const { return _iconSize; } void setIconSize(const QSize sz); bool drawFlat() const { return _drawFlat; } void setDrawFlat(bool v); // If _hasFixedIconSize is true, this relies on iconSize(). Be sure to set iconSize to the desired value. virtual QSize sizeHint() const; bool blinking() const { return _blinking; } void setBlinking(bool); bool blinkPhase() const { return _blinkPhase; } void setBlinkPhase(bool v); }; } // MusEGui #endif // __PIXMAP_BUTTON_H__ muse-4.0.0/src/muse/widgets/popup_double_spinbox.cpp000066400000000000000000000113211404105311000226200ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // popup_double_spinbox.cpp // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "popup_double_spinbox.h" #include #include // For debugging output: Uncomment the fprintf section. #define DEBUG_POPUP_DOUBLE_SPINBOX(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { //--------------------------------------------------------- // PopupDoubleSpinBox //--------------------------------------------------------- PopupDoubleSpinBox::PopupDoubleSpinBox(QWidget* parent) : QDoubleSpinBox(parent) { // Reset these since our parent will typically turn them on for speed. setAutoFillBackground(true); setAttribute(Qt::WA_NoSystemBackground, false); setAttribute(Qt::WA_StaticContents, false); setAttribute(Qt::WA_OpaquePaintEvent, false); setAlignment(Qt::AlignRight | Qt::AlignVCenter); setContentsMargins(0, 0, 0, 0); setFrame(false); _closePending = false; // DoubleSpinBoxLineEdit* le = new DoubleSpinBoxLineEdit(this); // setLineEdit(le); // setKeyboardTracking(false); // // connect(le, SIGNAL(doubleClicked()), this, SIGNAL(doubleClicked())); // connect(le, SIGNAL(ctrlDoubleClicked()), this, SIGNAL(ctrlDoubleClicked())); // //connect(le, SIGNAL(ctrlClicked()), this, SIGNAL(ctrlClicked())); } bool PopupDoubleSpinBox::event(QEvent* e) { switch(e->type()) { case QEvent::KeyPress: { QKeyEvent* ke = static_cast(e); switch(ke->key()) { // For return, we want to close the editor but don't want the // parent to receive the event which will just open the box again. case Qt::Key_Return: case Qt::Key_Enter: //e->ignore(); // QDoubleSpinBox::event(e); e->accept(); //emit editingFinished(); if(!_closePending) { _closePending = true; emit returnPressed(); } // // Will emit editingFinished. // deleteLater(); return true; break; case Qt::Key_Escape: { e->accept(); //emit editingFinished(); if(!_closePending) { _closePending = true; emit escapePressed(); } // // Will emit editingFinished. // deleteLater(); return true; } break; default: break; } } break; case QEvent::NonClientAreaMouseButtonPress: // FIXME: Doesn't work. DEBUG_POPUP_DOUBLE_SPINBOX(stderr, "PopupDoubleSpinBox::event NonClientAreaMouseButtonPress\n"); case QEvent::FocusOut: e->accept(); if(!_closePending) { _closePending = true; emit returnPressed(); } return true; break; default: break; } // Do not pass ANY events on to the parent. QDoubleSpinBox::event(e); e->accept(); return true; } // void PopupDoubleSpinBox::keyPressEvent(QKeyEvent* e) // { // // switch (e->key()) { // // // For return, we want to close the editor but don't want the // // // parent to receive the event which will just open the box again. // // case Qt::Key_Return: // // case Qt::Key_Escape: // // e->accept(); // // //emit editingFinished(); // Already emitted // // return; // // break; // // default: // // break; // // } // // e->ignore(); // // // Do not pass ANY events on to the parent. // e->accept(); // QDoubleSpinBox::keyPressEvent(e); // } // void PopupDoubleSpinBox::wheelEvent(QWheelEvent* e) // { // QDoubleSpinBox::wheelEvent(e); // // Need this because Qt doesn't deselect the text if not focused. // if(!hasFocus() && lineEdit()) // lineEdit()->deselect(); // } // void PopupDoubleSpinBox::focusOutEvent(QFocusEvent*) // { // emit editingFinished(); // } } // namespace MusEGui muse-4.0.0/src/muse/widgets/popup_double_spinbox.h000066400000000000000000000030231404105311000222650ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // popup_double_spinbox.h // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __POPUP_DOUBLE_SPINBOX_H__ #define __POPUP_DOUBLE_SPINBOX_H__ #include class QEvent; namespace MusEGui { //--------------------------------------------------------- // PopupDoubleSpinBox //--------------------------------------------------------- class PopupDoubleSpinBox : public QDoubleSpinBox { Q_OBJECT private: bool _closePending; protected: virtual bool event(QEvent*); signals: void returnPressed(); void escapePressed(); public: PopupDoubleSpinBox(QWidget* parent=0); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/posedit_OLD.cpp000066400000000000000000000634561404105311000205460ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: posedit.cpp,v 1.3.2.2 2008/05/21 00:28:54 terminator356 Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include #include #include #include #include "posedit.h" #include "sig.h" #include "spinbox.h" extern int MusEGlobal::mtcType; namespace MusEGui { //--------------------------------------------------------- // QNumberSection //--------------------------------------------------------- class QNumberSection { int selstart; int selend; public: QNumberSection(int selStart = 0, int selEnd = 0) : selstart(selStart), selend(selEnd ) {} int selectionStart() const { return selstart; } void setSelectionStart(int s) { selstart = s; } int selectionEnd() const { return selend; } void setSelectionEnd( int s ) { selend = s; } int width() const { return selend - selstart; } }; //--------------------------------------------------------- // PosEditor //--------------------------------------------------------- class PosEditor : public QLineEdit { PosEdit* cw; bool frm; QPixmap *pm; int focusSec; QList sections; QString sep; int offset; int section(const QPoint&); protected: void init(); virtual bool event(QEvent *e); virtual void resizeEvent(QResizeEvent*); virtual void paintEvent(QPaintEvent*); virtual void mousePressEvent(QMouseEvent *e); virtual void keyPressEvent(QKeyEvent * event ); void applyFocusSelection() {} public: PosEditor(PosEdit* Q_PARENT, const char * Q_NAME ); ~PosEditor(); void setControlWidget(PosEdit * widget); PosEdit* controlWidget() const; void setSeparator(const QString& s) { sep = s; } QString separator() const { return sep; } int focusSection() const { return focusSec; } bool setFocusSection(int s); void appendSection(const QNumberSection& sec); void clearSections(); void setSectionSelection(int sec, int selstart, int selend); }; //--------------------------------------------------------- // section //--------------------------------------------------------- int PosEditor::section(const QPoint& pt) { if (pm->isNull()) return -1; QPainter p(pm); int fw = frm ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0; int x = 2 + fw; int y = 0; int w = width(); int h = height(); for (int i = 0; i < sections.count(); ++i) { QString s = cw->sectionFormattedText(i); QRect bb = p.boundingRect(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, s); int nx = bb.x() + bb.width(); if (pt.x() >= x && pt.x() < nx) return i; x = nx; if (i < sections.count()-1) { QString s = sep; p.drawText(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, s, -1, &bb); x = bb.x() + bb.width(); } } return -1; } //--------------------------------------------------------- // PosEditor //--------------------------------------------------------- PosEditor::PosEditor(PosEdit* parent, const char* name) : QLineEdit(parent), sep(".") { setObjectName(name); cw = parent; frm = true; focusSec = 0; pm = new QPixmap; offset = 0; init(); } //--------------------------------------------------------- // ~PosEditor //--------------------------------------------------------- PosEditor::~PosEditor() { delete pm; } //--------------------------------------------------------- // init //--------------------------------------------------------- void PosEditor::init() { setBackgroundMode(Qt::PaletteBase); setFocusSection(-1); setKeyCompression(true); setFocusPolicy(Qt::WheelFocus); } //--------------------------------------------------------- // event //--------------------------------------------------------- bool PosEditor::event(QEvent *e) { if (e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut) { repaint( rect(), false); } else if (e->type() == QEvent::ShortcutOverride) { QKeyEvent* ke = (QKeyEvent*) e; switch (ke->key()) { case Qt::Key_Delete: case Qt::Key_Backspace: case Qt::Key_Up: case Qt::Key_Down: case Qt::Key_Left: case Qt::Key_Right: ke->accept(); default: break; } } return QWidget::event(e); } void PosEditor::resizeEvent(QResizeEvent *e) { pm->resize(e->size()); QWidget::resizeEvent(e); } //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void PosEditor::paintEvent(QPaintEvent *) { if (pm->isNull()) return; const QColorGroup & cg = colorGroup(); QPainter p(pm); p.setPen(colorGroup().text()); QBrush bg = cg.brush(QColorGroup::Base); int fw = frm ? style()->pixelMetric(QStyle::PM_DefaultFrameWidth) : 0; int x = 2 + fw; int y = 0; int w = width(); int h = height(); p.fillRect(0, 0, w, h, bg); for (int i = 0; i < sections.count(); ++i) { QRect bb; QString s = cw->sectionFormattedText(i); if (hasFocus() && (int(i) == focusSec)) { QBrush bg = cg.brush(QColorGroup::Highlight); QRect r = p.boundingRect(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, s, -1); p.setPen(colorGroup().highlightedText()); p.fillRect(r, bg); } else p.setPen(colorGroup().text()); p.drawText(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, s, -1, &bb); x = bb.x() + bb.width(); if (i < sections.count()-1) { QString s = sep; p.drawText(x, y, w, h, Qt::AlignVCenter|Qt::AlignLeft, s, -1, &bb); x = bb.x() + bb.width(); } } p.end(); bitBlt(this, 0, 0, pm); } //--------------------------------------------------------- // mousePressEvent //--------------------------------------------------------- void PosEditor::mousePressEvent(QMouseEvent *e) { QPoint p(e->pos().x(), 0); int sec = section(p); if (sec != -1) { cw->setFocusSection(sec); repaint(rect(), false); } } //--------------------------------------------------------- // keyPressEvent //--------------------------------------------------------- void PosEditor::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Right: if (unsigned(focusSec) <= sections.count()) { if (cw->setFocusSection(focusSec+1)) repaint(rect(), false); } case Qt::Key_Left: if (focusSec > 0 ) { if (cw->setFocusSection(focusSec-1)) repaint(rect(), false); } case Qt::Key_Up: cw->stepUp(); case Qt::Key_Down: cw->stepDown(); case Qt::Key_Backspace: case Qt::Key_Delete: cw->removeLastNumber(focusSec); case Qt::Key_Return: cw->enterPressed(); default: QString txt = e->text(); if (!txt.isEmpty() && !sep.isEmpty() && txt[0] == sep[0]) { // do the same thing as KEY_RIGHT when the user presses the separator key if (unsigned(focusSec) < sections.count()) { if (cw->setFocusSection(focusSec+1)) repaint(rect(), false); } } int num = txt[0].digitValue(); if (num != -1) { cw->addNumber(focusSec, num); } } } void PosEditor::appendSection(const QNumberSection& sec) { sections.append(sec); } void PosEditor::clearSections() { sections.clear(); } //--------------------------------------------------------- // setSectionSelection //--------------------------------------------------------- void PosEditor::setSectionSelection(int secNo, int selstart, int selend) { if (secNo < 0 || secNo > (int)sections.count()) return; sections[secNo].setSelectionStart(selstart); sections[secNo].setSelectionEnd(selend); } //--------------------------------------------------------- // setFocusSection //--------------------------------------------------------- bool PosEditor::setFocusSection(int idx) { if (idx > (int)sections.count()-1 || idx < 0) return false; if (idx != focusSec) { focusSec = idx; applyFocusSelection(); return true; } return false; } //--------------------------------------------------------- // PosEdit //--------------------------------------------------------- PosEdit::PosEdit(QWidget* parent, const char* name) : QWidget(parent) { setObjectName(name); init(); updateButtons(); } PosEdit::PosEdit(const Pos& time, QWidget* parent, const char* name) : QWidget(parent, name) { init(); setValue(time); updateButtons(); } PosEdit::~PosEdit() { } //--------------------------------------------------------- // init //--------------------------------------------------------- void PosEdit::init() { ed = new PosEditor(this, "pos editor"); controls = new SpinBox(this); controls->setEditor(ed); setFocusProxy(ed); connect(controls, SIGNAL(stepUpPressed()), SLOT(stepUp())); connect(controls, SIGNAL(stepDownPressed()), SLOT(stepDown())); connect(this, SIGNAL(valueChanged(const Pos&)),SLOT(updateButtons())); overwrite = false; timerId = 0; typing = false; min = Pos(0); max = Pos(MAX_TICK); changed = false; adv = false; static Section s_midiSections[3] = { // measure, beat, tick { 0, 4, 1, 0 }, { 5, 2, 1, 0 }, { 8, 3, 0, 0 } }; static Section s_smpteSections[4] = { // minute second frame subframe { 0, 3, 0, 0 }, { 4, 2, 0, 0 }, { 7, 2, 0, 0 }, { 10, 2, 0, 0 } }; memcpy(midiSections, s_midiSections, sizeof(s_midiSections)); memcpy(smpteSections, s_smpteSections, sizeof(s_smpteSections)); _smpte = false; // show position in smpte format sec = midiSections; setSections(); setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); } //--------------------------------------------------------- // setSetions //--------------------------------------------------------- void PosEdit::setSections() { ed->clearSections(); ed->appendSection(QNumberSection(0,0)); ed->appendSection(QNumberSection(0,0)); ed->appendSection(QNumberSection(0,0)); if (_smpte) { ed->appendSection(QNumberSection(0,0)); ed->setSeparator(QString(":")); } else { ed->setSeparator(QString(".")); } } //--------------------------------------------------------- // smpte //--------------------------------------------------------- bool PosEdit::smpte() const { return _smpte; } //--------------------------------------------------------- // setSmpte //--------------------------------------------------------- void PosEdit::setSmpte(bool f) { _smpte = f; sec = f ? smpteSections : midiSections; setSections(); ed->repaint(ed->rect(), false); } //--------------------------------------------------------- // minValue //--------------------------------------------------------- Pos PosEdit::minValue() const { return min; } //--------------------------------------------------------- // maxValue //--------------------------------------------------------- Pos PosEdit::maxValue() const { return max; } //--------------------------------------------------------- // setRange //--------------------------------------------------------- void PosEdit::setRange(const Pos& _min, const Pos& _max) { if (min.isValid()) min = _min; if (max.isValid()) max = _max; } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void PosEdit::setValue(const Pos& time) { if (time > maxValue() || time < minValue()) return; if (_smpte) time.msf(&(sec[0].val), &(sec[1].val), &(sec[2].val), &(sec[3].val)); else time.mbt(&(sec[0].val), &(sec[1].val), &(sec[2].val)); changed = false; updateButtons(); ed->repaint(ed->rect(), false); } void PosEdit::setValue(const QString& s) { Pos time(s); setValue(time); } void PosEdit::setValue(int t) { Pos time(t); setValue(time); } Pos PosEdit::pos() const { if (_smpte) { if (Pos::isValid(sec[0].val, sec[1].val, sec[2].val, sec[3].val)) return Pos(sec[0].val, sec[1].val, sec[2].val, sec[3].val); } else { if (Pos::isValid(sec[0].val, sec[1].val, sec[2].val)) return Pos(sec[0].val, sec[1].val, sec[2].val); } return Pos(); } void PosEdit::setSeparator(const QString& s) { ed->setSeparator(s); } QString PosEdit::separator() const { return ed->separator(); } bool PosEdit::event(QEvent *e) { if (e->type() == QEvent::FocusOut) { typing = false; if (changed) { emit valueChanged(pos() ); changed = false; } } return QWidget::event(e); } void PosEdit::timerEvent(QTimerEvent *) { overwrite = true; } //--------------------------------------------------------- // stepUp //--------------------------------------------------------- void PosEdit::stepUp() { int secNo = ed->focusSection(); bool accepted = false; if (!outOfRange(secNo, sec[secNo].val+1)) { accepted = true; setSec(secNo, sec[secNo].val+1); } if (accepted) { changed = true; Pos p = pos(); emit valueChanged(p); } ed->repaint(ed->rect(), false); } //--------------------------------------------------------- // stepDown //--------------------------------------------------------- void PosEdit::stepDown() { int secNo = ed->focusSection(); bool accepted = false; if (!outOfRange(secNo, sec[secNo].val-1)) { accepted = true; setSec(secNo, sec[secNo].val-1); } if (accepted) { changed = true; emit valueChanged(pos()); } ed->repaint(ed->rect(), false); } //--------------------------------------------------------- // sectionFormattedText // Returns the formatted number for section sec. //--------------------------------------------------------- QString PosEdit::sectionFormattedText(int secNo) { QString txt = sectionText(secNo); int so = sec[secNo].offset; int len = sec[secNo].len; int eo = so + len; if (typing && secNo == ed->focusSection()) ed->setSectionSelection(secNo, eo - txt.length(), eo); else ed->setSectionSelection(secNo, so, eo); txt = txt.rightJustify(len, '0'); return txt; } //--------------------------------------------------------- // setFocusSection //--------------------------------------------------------- bool PosEdit::setFocusSection(int s) { if (s != ed->focusSection()) { killTimer(timerId); overwrite = true; typing = false; int so = sec[s].offset; int eo = so + sec[s].len; ed->setSectionSelection(s, so, eo); if (changed) { emit valueChanged(pos()); changed = false; } } return ed->setFocusSection(s); } //--------------------------------------------------------- // setSec //--------------------------------------------------------- void PosEdit::setSec(int secNo, int val) { if (val < 0) val = 0; if (_smpte) { switch(secNo) { case 0: break; case 1: if (val > 59) val = 59; break; case 2: switch(MusEGlobal::mtcType) { case 0: // 24 frames sec if (val > 23) val = 23; break; case 1: if (val > 24) val = 24; break; case 2: // 30 drop frame case 3: // 30 non drop frame if (val > 29) val = 29; break; } break; case 3: if (val > 99) val = 99; } } else { switch(secNo) { case 0: break; case 1: { int z, n; int tick = MusEGlobal::sigmap.bar2tick(sec[0].val, val, sec[2].val); MusEGlobal::sigmap.timesig(tick, z, n); if (val >= n) val = n-1; } break; case 2: { int tick = MusEGlobal::sigmap.bar2tick(sec[0].val, sec[1].val, val); int tb = MusEGlobal::sigmap.ticksBeat(tick); if (val >= tb) val = tb-1; } break; } } sec[secNo].val = val; } //--------------------------------------------------------- // sectionText // Returns the text of section \a sec. //--------------------------------------------------------- QString PosEdit::sectionText(int secNo) { return QString::number(sec[secNo].val + sec[secNo].voff); } //--------------------------------------------------------- // outOfRange // return true if out of range //--------------------------------------------------------- bool PosEdit::outOfRange(int secNo, int val) const { if (val < 0) return true; int limit = MAXINT; if (_smpte) { switch(secNo) { case 0: break; case 1: limit = 59; break; case 2: switch(MusEGlobal::mtcType) { case 0: // 24 frames sec limit = 23; break; case 1: limit = 24; break; case 2: // 30 drop frame case 3: // 30 non drop frame limit = 29; break; } break; case 3: limit = 99; break; } } else { switch(secNo) { case 0: break; case 1: { int z; int tick = MusEGlobal::sigmap.bar2tick(sec[0].val, val, sec[2].val); MusEGlobal::sigmap.timesig(tick, z, limit); limit -= 1; } break; case 2: int tick = MusEGlobal::sigmap.bar2tick(sec[0].val, sec[1].val, val); limit = MusEGlobal::sigmap.ticksBeat(tick) - 1; break; } } return val > limit; } //--------------------------------------------------------- // addNumber //--------------------------------------------------------- void PosEdit::addNumber(int secNo, int num) { if (secNo == -1) return; killTimer(timerId); bool accepted = false; typing = true; int voff = sec[secNo].voff; QString txt = sectionText(secNo); if ((unsigned) txt.length() == sec[secNo].len) { if (!outOfRange(secNo, num - voff)) { accepted = true; sec[secNo].val = num - voff; } } else { txt += QString::number(num); int temp = txt.toInt() - voff; if (outOfRange(secNo, temp)) txt = sectionText(secNo); else { accepted = true; sec[secNo].val = temp; } if (adv && ((unsigned) txt.length() == sec[secNo].len)) { setFocusSection(ed->focusSection() + 1); } } changed = accepted; if (accepted) emit valueChanged(pos()); timerId = startTimer(qApp->doubleClickInterval()*4); ed->repaint(ed->rect(), false); } //--------------------------------------------------------- // removeLastNumber //--------------------------------------------------------- void PosEdit::removeLastNumber(int secNo) { if (secNo == -1) return; QString txt = QString::number(sec[secNo].val); txt = txt.mid(0, txt.length() - 1); sec[secNo].val = txt.toInt() - sec[secNo].voff; ed->repaint(ed->rect(), false); } //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void PosEdit::resizeEvent(QResizeEvent* ev) { QWidget::resizeEvent(ev); controls->resize(width(), height()); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize PosEdit::sizeHint() const { QFontMetrics fm(font()); int fw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth,0, this); // ddskrjo 0 int h = fm.height() + fw * 2; int w = 4 + controls->arrowWidth() + fw * 4; if (_smpte) w += fm.width('9') * 10 + fm.width(ed->separator()) * 3; else w += fm.width('9') * 10 + fm.width(ed->separator()) * 2; return QSize(w, h).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // updateButtons //--------------------------------------------------------- void PosEdit::updateButtons() { bool upEnabled = isEnabled() && (pos() < maxValue()); bool downEnabled = isEnabled() && (pos() > minValue()); //printf("PosEdit::updateButtons smpte:%d upEnabled:%d downEnabled:%d\n", smpte(), upEnabled, downEnabled); controls->setStepEnabled(upEnabled, downEnabled); } //--------------------------------------------------------- // enterPressed //--------------------------------------------------------- void PosEdit::enterPressed() { emit returnPressed(); } //--------------------------------------------------------- // setEnabled //--------------------------------------------------------- void PosEdit::setEnabled(bool v) { QWidget::setEnabled(v); updateButtons(); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/posedit_OLD.h000066400000000000000000000066151404105311000202050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: posedit.h,v 1.1.1.1.2.1 2004/12/27 19:47:25 lunar_shuttle Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __POSEDIT_H__ #define __POSEDIT_H__ #include #include "pos.h" #include "section.h" class QResizeEvent; class QTimerEvent; class PosEditor; class SpinBox; namespace MusEGui { //--------------------------------------------------------- // PosEdit //--------------------------------------------------------- class PosEdit : public QWidget { Q_OBJECT Q_PROPERTY(QString separator READ separator WRITE setSeparator) Q_PROPERTY(bool smpte READ smpte WRITE setSmpte) void init(); void setSections(); QString sectionText(int sec); Section midiSections[3]; Section smpteSections[4]; Section* sec; bool _smpte; bool adv; bool overwrite; int timerId; bool typing; Pos min; Pos max; bool changed; PosEditor *ed; SpinBox* controls; private slots: void stepUp(); void stepDown(); signals: void valueChanged(const Pos&); void returnPressed(); protected: virtual bool event(QEvent *e ); void timerEvent(QTimerEvent* e); virtual void resizeEvent(QResizeEvent*); QString sectionFormattedText(int sec); void addNumber(int sec, int num); void removeLastNumber(int sec); bool setFocusSection(int s); virtual bool outOfRange(int, int) const; virtual void setSec(int, int); friend class PosEditor; protected slots: void updateButtons(); public slots: virtual void setValue(const Pos& time); void setValue(int t); void setValue(const QString& s); // Added p3.3.43 virtual void setEnabled(bool); public: PosEdit(QWidget* = 0, const char* = 0); PosEdit(const Pos& time, QWidget*, const char* = 0); ~PosEdit(); QSize sizeHint() const; Pos pos() const; virtual void setAutoAdvance(bool advance) { adv = advance; } bool autoAdvance() const { return adv; } virtual void setMinValue(const Pos& d) { setRange(d, maxValue()); } Pos minValue() const; virtual void setMaxValue( const Pos& d ) { setRange(minValue(), d ); } Pos maxValue() const; virtual void setRange(const Pos& min, const Pos& max); QString separator() const; virtual void setSeparator(const QString& s); void setSmpte(bool); bool smpte() const; void enterPressed(); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/scldiv.cpp000066400000000000000000000441161404105311000176550ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: scldiv.cpp,v 1.1.1.1 2003/10/27 18:54:32 wschweer Exp $ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include "scldiv.h" #include "mmath.h" namespace MusEGui { // ScaleDiv - A class for building scale divisions // // The ScaleDiv class can build // linear and logarithmic scale divisions for specified // intervals. It uses an adjustable algorithm to // generate the major and minor step widths automatically. // A scale division has a minimum value, a maximum value, // a vector of major marks, and a vector of minor marks. // // ScaleDiv uses implicit sharing for the mark vectors. // // Build a logarithmic scale division from 0.01 to 1000 // and print out the major and minor marks. //.c // #include // #include // // main() // { // int i,k; // ScaleDiv sd; // // sd.rebuild(0.01, 100, 10, 10, TRUE, 0.0); // // k=0; // for (i=0;i vmax) { if (val > vmax + delta_max) rv = false; val = vmax; } return rv; } //------------------------------------------------------------ //.F ScaleDiv::ScaleDiv // Construct a ScaleDiv instance. // //.u Syntax //.f ScaleDiv::ScaleDiv() //------------------------------------------------------------ ScaleDiv::ScaleDiv() { d_lBound = 0.0; d_hBound = 0.0; d_majStep = 0.0; d_log = false; } //------------------------------------------------------------ //.F ScaleDiv::~ScaleDiv // Destroy a ScaleDiv instance. // //.u Syntax //.f ScaleDiv::~ScaleDiv() //------------------------------------------------------------ ScaleDiv::~ScaleDiv() { } //------------------------------------------------------------ //.F ScaleDiv::ScaleDiv // Copy Constructor // //.u Syntax //.f ScaleDiv::ScaleDiv(const ScaleDiv &s) // //.u Parameters //.p const ScaleDiv &s -- scale division to be copied //------------------------------------------------------------ ScaleDiv::ScaleDiv(const ScaleDiv &s) { copy(s); } //------------------------------------------------------------ //.F ScaleDiv::operator= // Assignment operator // //.u Syntax //.f ScaleDiv & ScaleDiv::operator=(const ScaleDiv &s) // //.u Parameters //.p const ScaleDiv &s -- scale division to be assigned //------------------------------------------------------------ ScaleDiv& ScaleDiv::operator=(const ScaleDiv &s) { copy(s); return *this; } //------------------------------------------------------------ //.F ScaleDiv::copy // Copy member data from another ScaleDiv instance. // //.u Syntax //.f void ScaleDiv::copy(const ScaleDiv &s) // //.u Parameters //.p const ScaleDiv &s //------------------------------------------------------------ void ScaleDiv::copy(const ScaleDiv &s) { d_lBound = s.d_lBound; d_hBound = s.d_hBound; d_log = s.d_log; d_majStep = s.d_majStep; d_minMarks = s.d_minMarks; d_majMarks = s.d_majMarks; } //------------------------------------------------------------ //.F ScaleDiv::rebuild // Build a scale width major and minor divisions // //.p // double x1 -- first boundary value // double x2 -- second boundary value // int maxMajSteps -- max. number of major step intervals // int maxMinSteps -- max. number of minor step intervals // bool log -- logarithmic division (TRUE/FALSE) // double step -- fixed major step width. Defaults to 0.0. // bool ascend -- if TRUE, sort in ascending order from min(x1, x2) // to max(x1, x2). If FALSE, sort in the direction // from x1 to x2. Defaults to TRUE. // //.u Return Value // True if the arrays have been allocated successfully. // //.u Description // If no fixed step width is specified or if it is set to 0, the // major step width will be calculated automatically according to the // the value of maxMajSteps. The maxMajSteps parameter has no effect // if a fixed step size is specified. The minor step width is always // calculated automatically. // If the step width is to be calculated automatically, the algorithm // tries to find reasonable values fitting into the scheme {1,2,5}*10^n // with an integer number n for linear scales. // For logarithmic scales, there are three different cases: //.i // -- If the major step width is one decade, the minor marks // will fit into one of the schemes {1,2,...9}, {2,4,6,8}, {2,5} or {5}, // depending on the maxMinSteps parameter. // -- If the major step size spans // more than one decade, the minor step size will be {1,2,5}*10^n decades // with a natural number n. // -- If the whole range is less than one decade, a linear scale // division will be built // //.u Note // For logarithmic scales, the step width is measured in decades. //------------------------------------------------------------ bool ScaleDiv::rebuild(double x1, double x2, int maxMajSteps, int maxMinSteps, bool log, double step, bool ascend) { int rv; d_lBound = MusECore::qwtMin(x1, x2); d_hBound = MusECore::qwtMax(x1, x2); d_log = log; // printf("*** MajSteps %d / MinSteps %d / step %f / x1 %f / x2 %f \n", // maxMajSteps, maxMinSteps, step, x1, x2); if (step > (d_hBound - d_lBound)) step = 0.0; if (d_log) rv = buildLogDiv(maxMajSteps,maxMinSteps,step); else rv = buildLinDiv(maxMajSteps, maxMinSteps, step); if ((!ascend) && (x2 < x1)) { d_lBound = x1; d_hBound = x2; MusECore::qwtTwistArray(d_majMarks.data(), d_majMarks.size()); MusECore::qwtTwistArray(d_minMarks.data(), d_minMarks.size()); } return rv; } //------------------------------------------------------------ //.F ScaleDiv::buildLinDiv // Build a linear scale division in ascending order // //.u Syntax //.f bool ScaleDiv::buildLinDiv(int majSteps, int minSteps, double step) // //.u Parameters //.p int maxSteps -- max. number of step intervals // double step -- fixed step width // //.u Return Value // TRUE if array has been successfully resized // //.u Description // If the 'step' parameter is set to 0.0, this function // cal[culates the step width automatically according to // the value of 'maxSteps'. MaxSteps must be greater than or // equal to 2. It will be guessed if an invalid value is specified. // The maximum possible number of steps is limited to 10000. // The maxSteps parameter has no effect if a fixed step width is // specified. // //.u Note // This function uses the data members d_lBound and d_hBound and assumes // that d_hBound > d_lBound. //------------------------------------------------------------ bool ScaleDiv::buildLinDiv(int maxMajSteps, int maxMinSteps, double step) { int nMaj, nMin, minSize, i0,i,k; double val, mval; double firstTick, lastTick; double minStep; QVector buffer; bool rv = true; // parameter range check maxMajSteps = MusECore::qwtMax(1, maxMajSteps); maxMinSteps = MusECore::qwtMax(0, maxMinSteps); step = MusECore::qwtAbs(step); // reset vectors d_minMarks.resize(0); d_majMarks.resize(0); if (d_lBound == d_hBound) return true; // // Set up major divisions // if (step == 0.0) d_majStep = MusECore::qwtCeil125(MusECore::qwtAbs(d_hBound - d_lBound) * 0.999999 / double(maxMajSteps)); else d_majStep = step; if (d_majStep == 0.0) return true; firstTick = ceil( (d_lBound - step_eps * d_majStep) / d_majStep) * d_majStep; lastTick = floor( (d_hBound + step_eps * d_majStep) / d_majStep) * d_majStep; nMaj = MusECore::qwtMin(10000, int(rint((lastTick - firstTick) / d_majStep)) + 1); d_majMarks.resize(nMaj); MusECore::qwtLinSpace(d_majMarks.data(), d_majMarks.size(), firstTick, lastTick); // // Set up minor divisions // if (maxMinSteps < 1) // no minor divs return true; minStep = MusECore::qwtCeil125( d_majStep / double(maxMinSteps) ); if (minStep == 0.0) return true; nMin = MusECore::qwtAbs(int(rint(d_majStep / minStep))) - 1; // # minor steps per interval // Do the minor steps fit into the interval? if ( MusECore::qwtAbs(double(nMin + 1) * minStep - d_majStep) > step_eps * d_majStep) { nMin = 1; minStep = d_majStep * 0.5; } // Are there minor ticks below the first major tick? if (!d_majMarks.empty() && d_majMarks[0] > d_lBound ) i0 = -1; else i0 = 0; // resize buffer to the maximum possible number of minor ticks buffer.resize(nMin * (nMaj + 1)); // calculate minor ticks if (rv) { minSize = 0; for (i = i0; i < (int)d_majMarks.size(); i++) { if (i >= 0) val = d_majMarks[i]; else val = d_majMarks[0] - d_majStep; for (k=0; k< nMin; k++) { mval = (val += minStep); if (limRange(mval, d_lBound, d_hBound, border_eps)) { buffer[minSize] = mval; minSize++; } } } //d_minMarks.duplicate(buffer.data(), minSize); d_minMarks.resize(minSize); std::copy(buffer.data(), buffer.data() + minSize, d_minMarks.begin()); } return rv; } //------------------------------------------------------------ //.F ScaleDiv::buildLogDiv // Build a logarithmic scale division // //.u Syntax //.f bool ScaleDiv::buildLogDiv(int maxMajSteps, int maxMinSteps, int majStep) // //.u Parameters //.p int maxMajSteps, int maxMinSteps, int majStep // //.u Return Value // True if memory has been successfully allocated // //.u Note // This function uses the data members d_lBound and d_hBound and assumes // that d_hBound > d_lBound. //------------------------------------------------------------ bool ScaleDiv::buildLogDiv(int maxMajSteps, int maxMinSteps, double majStep) { double firstTick, lastTick; double lFirst, lLast; double val, sval, minStep, minFactor; int nMaj, nMin, minSize, i, k, k0, kstep, kmax, i0; bool rv = true; double width; QVector buffer; // Parameter range check maxMajSteps = MusECore::qwtMax(1, MusECore::qwtAbs(maxMajSteps)); maxMinSteps = MusECore::qwtMax(0, MusECore::qwtAbs(maxMinSteps)); majStep = MusECore::qwtAbs(majStep); // boundary check limRange(d_hBound, LOG_MIN, LOG_MAX); limRange(d_lBound, LOG_MIN, LOG_MAX); // reset vectors d_minMarks.resize(0); d_majMarks.resize(0); if (d_lBound == d_hBound) return true; // scale width in decades width = log10(d_hBound) - log10(d_lBound); // scale width is less than one decade -> build linear scale if (width < 1.0) { rv = buildLinDiv(maxMajSteps, maxMinSteps, 0.0); // convert step width to decades if (d_majStep > 0) d_majStep = log10(d_majStep); return rv; } // // Set up major scale divisions // if (majStep == 0.0) d_majStep = MusECore::qwtCeil125( width * 0.999999 / double(maxMajSteps)); else d_majStep = majStep; // major step must be >= 1 decade d_majStep = MusECore::qwtMax(d_majStep, 1.0); lFirst = ceil((log10(d_lBound) - step_eps * d_majStep) / d_majStep) * d_majStep; lLast = floor((log10(d_hBound) + step_eps * d_majStep) / d_majStep) * d_majStep; firstTick = exp10(lFirst); lastTick = exp10(lLast); nMaj = MusECore::qwtMin(10000, int(rint(MusECore::qwtAbs(lLast - lFirst) / d_majStep)) + 1); d_majMarks.resize(nMaj); MusECore::qwtLogSpace(d_majMarks.data(), d_majMarks.size(), firstTick, lastTick); // // Set up minor scale divisions // if ((d_majMarks.size() < 1) || (maxMinSteps < 1)) return true; // no minor marks if (d_majStep < 1.1) // major step width is one decade { if (maxMinSteps >= 8) { k0 = 2; kmax = 9; kstep = 1; minSize = (d_majMarks.size() + 1) * 8; } else if (maxMinSteps >= 4) { k0 = 2; kmax = 8; kstep = 2; minSize = (d_majMarks.size() + 1) * 4; } else if (maxMinSteps >= 2) { k0 = 2; kmax = 5; kstep = 3; minSize = (d_majMarks.size() + 1) * 2; } else { k0 = 5; kmax = 5; kstep = 1; minSize = (d_majMarks.size() + 1); } // resize buffer to the max. possible number of minor marks buffer.resize(minSize); // Are there minor ticks below the first major tick? if ( d_lBound < firstTick ) i0 = -1; else i0 = 0; minSize = 0; for (i = i0; i< (int)d_majMarks.size(); i++) { if (i >= 0) val = d_majMarks[i]; else val = d_majMarks[0] / exp10(d_majStep); for (k=k0; k<= kmax; k+=kstep) { sval = val * double(k); if (limRange(sval, d_lBound, d_hBound, border_eps)) { buffer[minSize] = sval; minSize++; } } } // copy values into the minMarks array //d_minMarks.duplicate(buffer.data(), minSize); d_minMarks.resize(minSize); std::copy(buffer.data(), buffer.data() + minSize, d_minMarks.begin()); } else // major step > one decade { // substep width in decades, at least one decade minStep = MusECore::qwtCeil125( (d_majStep - step_eps * (d_majStep / double(maxMinSteps))) / double(maxMinSteps) ); minStep = MusECore::qwtMax(1.0, minStep); // # subticks per interval nMin = int(rint(d_majStep / minStep)) - 1; // Do the minor steps fit into the interval? if ( MusECore::qwtAbs( double(nMin + 1) * minStep - d_majStep) > step_eps * d_majStep) nMin = 0; if (nMin < 1) return true; // no subticks // resize buffer to max. possible number of subticks buffer.resize((d_majMarks.size() + 1) * nMin ); // substep factor = 10^substeps minFactor = MusECore::qwtMax(exp10(minStep), 10.0); // Are there minor ticks below the first major tick? if ( d_lBound < firstTick ) i0 = -1; else i0 = 0; minSize = 0; for (i = i0; i< (int)d_majMarks.size(); i++) { if (i >= 0) val = d_majMarks[i]; else val = firstTick / exp10(d_majStep); for (k=0; k< nMin; k++) { sval = (val *= minFactor); if (limRange(sval, d_lBound, d_hBound, border_eps)) { buffer[minSize] = sval; minSize++; } } } //d_minMarks.duplicate(buffer.data(), minSize); d_minMarks.resize(minSize); std::copy(buffer.data(), buffer.data() + minSize, d_minMarks.begin()); } return rv; } //------------------------------------------------------------ //.F ScaleDiv::operator== // Equality operator // //.u Syntax //.f int ScaleDiv::operator==(const ScaleDiv &s) // //.u Parameters //.p const ScaleDiv &s // //.u Return Value // TRUE if this instance is equal to s //------------------------------------------------------------ int ScaleDiv::operator==(const ScaleDiv &s) const { if (d_lBound != s.d_lBound) return 0; if (d_hBound != s.d_hBound) return 0; if (d_log != s.d_log) return 0; if (d_majStep != s.d_majStep) return 0; if (d_majMarks != s.d_majMarks) return 0; return (d_minMarks == s.d_minMarks); } //------------------------------------------------------------ //.F ScaleDiv::operator!= // Inequality // //.u Syntax //.f int ScaleDiv::operator!=(const ScaleDiv &s) // //.u Parameters //.p const ScaleDiv &s // //.u Return Value // TRUE if this instance is not equal to s //------------------------------------------------------------ int ScaleDiv::operator!=(const ScaleDiv &s) const { return (!(*this == s)); } //------------------------------------------------------------ //.F ScaleDiv::reset // Detach the shared data and set everything to zero. // //.u Syntax //.f void ScaleDiv::reset() //------------------------------------------------------------ void ScaleDiv::reset() { // reset vectors d_minMarks.resize(0); d_majMarks.resize(0); d_lBound = 0.0; d_hBound = 0.0; d_majStep = 0.0; d_log = false; } } // namespace MusEGui muse-4.0.0/src/muse/widgets/scldiv.h000066400000000000000000000044551404105311000173240ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: scldiv.h,v 1.1.1.1 2003/10/27 18:54:43 wschweer Exp $ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SCLDIV_H__ #define __SCLDIV_H__ #include namespace MusEGui { class ScaleDiv { double d_lBound; double d_hBound; double d_majStep; bool d_log; QVector d_majMarks; QVector d_minMarks; void copy(const ScaleDiv &s); bool buildLinDiv(int maxMajMark, int maxMinMark, double step = 0.0); bool buildLogDiv(int maxMajMark, int maxMinMark, double step = 0.0); public: ScaleDiv (); virtual ~ScaleDiv(); ScaleDiv(const ScaleDiv& s); ScaleDiv& operator= (const ScaleDiv &s); int operator== (const ScaleDiv &s) const; int operator!= (const ScaleDiv &s) const; double lBound() const { return d_lBound; } double hBound() const { return d_hBound; } int minCnt() const { return d_minMarks.size(); } int majCnt() const { return d_majMarks.size(); } bool logScale() const { return d_log; } double majMark(int i) const { return d_majMarks[i]; } double minMark(int i) const { return d_minMarks[i]; } double majStep() const { return d_majStep; } void reset(); bool rebuild(double lBound, double hBound, int maxMaj, int maxMin, bool log, double step = 0.0, bool ascend = true); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/scldraw.cpp000066400000000000000000000701441404105311000200300ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: scldraw.cpp,v 1.1.1.1 2003/10/27 18:54:36 wschweer Exp $ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "muse_math.h" #include "mmath.h" #include "scldraw.h" namespace MusEGui { int const ScaleDraw::minLen = 10; const double step_eps = 1.0e-6; static const double WorstCase = -8.8888888888888888888888e-88; //------------------------------------------------------------ //.H ScaleDraw | 3 | 30/08/97 | Qwt Widget Library | Qwt Programmer's Manual //.I scldraw Different Scales //.U NAME // ScaleDraw - A class for drawing scales // //.U SYNOPSIS // #include // //.U DESCRIPTION // ScaleDraw can be used to draw linear or logarithmic scales. // A scale has an origin, // an orientation and a length, which all can be specified with // @ScaleDraw::setGeometry@. // After a scale division has been specified as a @^QwtScaleDiv@ object // using @ScaleDraw::setScale (1)@ // or determined internally using @ScaleDraw::setScale (2)@, // the scale can be drawn with the @QwtScaleDiv::draw@ member. // //.U INHERITED CLASSES // @QwtDiMap@ // //.U PUBLIC MEMBERS //.R // ScaleDraw::ScaleDraw -- constructor // ScaleDraw::setScale (1) -- set scale using QwtScaleDiv // ScaleDraw::setScale (2) -- set scale directly // ScaleDraw::setGeometry -- specify geometry // ScaleDraw::setAngleRange -- specify angle range for round scales // ScaleDraw::setLabelFormat -- set number format // ScaleDraw::scalediv -- return scale division // ScaleDraw::orientation -- return orientation // ScaleDraw::maxBoundingRect -- return maximum bounding rectangle // ScaleDraw::maxWidth -- return maximum width // ScaleDraw::maxHeight -- return maximum height // ScaleDraw::maxLabelWidth -- return maximum width of the number labels // ScaleDraw::draw -- draw the scale // //.U STATIC DATA MEMBERS //.t // enum ScaleDraw::Orientation { Left, Right, Top, Bottom, Round } -- // Scale orientation //------------------------------------------------------------ //------------------------------------------------------------ //.U MEMBER FUNCTION DESCRIPTION //------------------------------------------------------------ //------------------------------------------------------------ // //.F ScaleDraw::ScaleDraw // // Constructor //.u Description // The range of the scale is initialized to [0, 100], // the angle range is set to [-135, 135], the geometry // is initialized such that the origin is at (0,0), the // length is 100, and the orientation is ScaleDraw::Bottom. // //------------------------------------------------------------ ScaleDraw::ScaleDraw() { d_hpad = 3; d_vpad = 1; d_majLen = 4; d_medLen = 3; d_minLen = 2; d_minAngle = -135 * 16; d_maxAngle = 135 * 16; d_fmt = 'M'; // Metric suffix G M K. d_prec = 4; d_drawBackBone = true; d_textHighlightMode = TextHighlightNone; // initialize scale and geometry setGeometry(0,0,100,Bottom); setScale(0,100,0,0,10); } //------------------------------------------------------------ // //.F ScaleDraw::setScale (1) // Adjust the range of the scale // //.u Syntax //.f void ScaleDraw::setScale(double x1, double x2, double step, int logscale) // //.u Parameters //.p double x1 -- value at the left/low endpoint of the scale // double x2 -- value at the right/high endpoint of the scale // double step -- step size (default : 0.0) // int logscale -- logarithmic scale (default : 0) // //.u Description // If step == 0.0, the step width is calculated automatically // dependent on the maximal number of scale ticks. // //------------------------------------------------------------ void ScaleDraw::setScale(double x1, double x2, int maxMajIntv, int maxMinIntv, double step, int logscale) { d_scldiv.rebuild( x1, x2, maxMajIntv, maxMinIntv, logscale, step, false ); setDblRange( d_scldiv.lBound(), d_scldiv.hBound(), d_scldiv.logScale()); } //------------------------------------------------------------ // //.F ScaleDraw::setScale (2) // Change the scale division // //.u Syntax //.f void ScaleDraw::setScale(QwtAutoScale &as) // //.u Parameters //.p const QwtScaleDiv& sd -- new scale division // //------------------------------------------------------------ void ScaleDraw::setScale(const ScaleDiv &s) { d_scldiv = s; setDblRange(d_scldiv.lBound(),d_scldiv.hBound(),d_scldiv.logScale()); } QString ScaleDraw::composeLabelText(double val, char fmt, int prec) const { if(fmt == 'M') { if(val > 1000000000.0) return QString("%L1").arg(val / 1000000000.0, 0, 'g', prec) + "G"; else if(val > 1000000.0) return QString("%L1").arg(val / 1000000.0, 0, 'g', prec) + "M"; else if(val > 1000.0) return QString("%L1").arg(val / 1000.0, 0, 'g', prec) + "K"; else return QString("%L1").arg(val, 0, 'g', prec); } return QString("%L1").arg(val, 0, fmt, prec); } //------------------------------------------------------------ //.F ScaleDraw::draw // Draw the scale //.u Parameters //.p QPainter *p -- the painter //------------------------------------------------------------ void ScaleDraw::draw(QPainter *p, const QPalette& palette, double curValue)// const { double val,hval,majTick; int i,k,kmax; p->setPen(palette.text().color()); const int majCnt = d_scldiv.majCnt(); const int minCnt = d_scldiv.minCnt(); for (i=0; i< majCnt; i++) { val = d_scldiv.majMark(i); drawTick(p, palette, curValue, val, d_majLen); } for (i=0; i< majCnt; i++) { val = d_scldiv.majMark(i); drawLabel(p, palette, curValue, val, i == 0); } if (d_scldiv.logScale()) { for (i=0; i< minCnt; i++) { drawTick(p,palette,curValue,d_scldiv.minMark(i),d_minLen); } } else { k = 0; kmax = majCnt - 1; if (kmax > 0) { majTick = d_scldiv.majMark(0); hval = majTick - 0.5 * d_scldiv.majStep(); for (i=0; i< minCnt; i++) { val = d_scldiv.minMark(i); if (val > majTick) { if (k < kmax) { k++; majTick = d_scldiv.majMark(k); } else { majTick += d_scldiv.majMark(kmax) + d_scldiv.majStep(); } hval = majTick - 0.5 * d_scldiv.majStep(); } if (MusECore::qwtAbs(val-hval) < step_eps * d_scldiv.majStep()) drawTick(p, palette, curValue, val, d_medLen); else drawTick(p, palette, curValue, val, d_minLen); } } } // // draw backbone // if (d_drawBackBone) drawBackbone(p, palette, curValue); } //------------------------------------------------------------ //.F ScaleDraw::drawTick // Draws a singls scale tick // //.u Parameters //.p QPainter *p, double val, int len //------------------------------------------------------------ void ScaleDraw::drawTick(QPainter *p, const QPalette& /*palette*/, double /*curValue*/, double val, int len) const { int tval = transform(val); double arc; int x1, x2, y1, y2; switch(d_orient) { case Right: p->drawLine(d_xorg, tval, d_xorg + len, tval); break; case Bottom: p->drawLine(tval, d_yorg, tval, d_yorg + len); break; case InsideHorizontal: p->drawLine(tval, d_vpad + d_majLen - len, tval, d_vpad + d_majLen); break; case Left: case InsideVertical: p->drawLine(d_xorg, tval, d_xorg - len, tval); break; case Round: if ((tval <= d_minAngle + 359 * 16) || (tval >= d_minAngle - 359 * 16)) { arc = double(tval) / 16.0 * M_PI / 180.0; x1 = MusECore::qwtInt(d_xCenter + sin(arc) * d_radius); x2 = MusECore::qwtInt(d_xCenter + sin(arc) * (d_radius + double(len))); y1 = MusECore::qwtInt(d_yCenter - cos(arc) * d_radius); y2 = MusECore::qwtInt(d_yCenter - cos(arc) * (d_radius + double(len))); p->drawLine(x1, y1, x2, y2); } break; case Top: default: p->drawLine(tval, d_yorg, tval, d_yorg - len); break; } } //------------------------------------------------------------ //.- //.F ScaleDraw::drawLabel // Draws the number label for a major scale tick // //.u Parameters //.p QPainter *p, double val // //------------------------------------------------------------ void ScaleDraw::drawLabel(QPainter *p, const QPalette& palette, double curValue, double val, bool isSpecialText) const { static QString label; static double pi_4 = M_PI * 0.25; static double pi_75 = M_PI * 0.75; double arc; int xpos, ypos, x0, y0; int tval; QFontMetrics fm = p->fontMetrics(); tval = transform(val); // correct rounding errors if val = 0 if ((!d_scldiv.logScale()) && (MusECore::qwtAbs(val) < MusECore::qwtAbs(step_eps * d_scldiv.majStep()))) val = 0.0; if(isSpecialText && !_specialText.isEmpty()) label = _specialText; else label = composeLabelText(val, d_fmt, d_prec); switch(d_orient) { case Right: x0 = d_xorg + d_majLen + d_hpad; y0 = tval + (fm.ascent()-1) / 2; break; case Left: case InsideVertical: // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 x0 = d_xorg - d_majLen - d_hpad - fm.horizontalAdvance(label); #else x0 = d_xorg - d_majLen - d_hpad - fm.width(label); #endif y0 = tval + (fm.ascent() -1) / 2; break; case Bottom: // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 x0 = tval - (fm.horizontalAdvance(label)-1) / 2; #else x0 = tval - (fm.width(label)-1) / 2; #endif y0 = d_yorg + d_majLen + d_vpad + fm.ascent(); break; case InsideHorizontal: // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 x0 = tval - (fm.horizontalAdvance(label)-1) / 2; #else x0 = tval - (fm.width(label)-1) / 2; #endif //y0 = d_yorg + d_majLen + d_vpad + fm.ascent(); y0 = d_majLen + d_vpad + fm.ascent(); break; case Round: if ((tval > d_minAngle + 359 * 16) || (tval < d_minAngle - 359 * 16)) // break; return; arc = double(tval) / 16.0 * M_PI / 180.0; // Map arc into the interval -pi <= arc <= pi if ((arc < -M_PI) || (arc > M_PI)) arc -= floor((arc + M_PI) / M_PI * 0.5) * 2.0 * M_PI; xpos = 1 + MusECore::qwtInt(d_xCenter + (d_radius + double(d_majLen + d_vpad)) * sin(arc)); ypos = MusECore::qwtInt(d_yCenter - (d_radius + double(d_majLen + d_vpad)) * cos(arc)); if (arc < -pi_75) { // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 x0 = xpos - MusECore::qwtInt(double(fm.horizontalAdvance(label)) #else x0 = xpos - MusECore::qwtInt(double(fm.width(label)) #endif * (1.0 + (arc + pi_75) * M_2_PI)); y0 = ypos + fm.ascent() - 1; } else if (arc < -M_PI_4) { // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 x0 = xpos - fm.horizontalAdvance(label); #else x0 = xpos - fm.width(label); #endif y0 = ypos - MusECore::qwtInt(double(fm.ascent() - 1) * (arc + M_PI_4) * M_2_PI); } else if (arc < pi_4) { // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 x0 = xpos + MusECore::qwtInt(double(fm.horizontalAdvance(label)) #else x0 = xpos + MusECore::qwtInt(double(fm.width(label)) #endif * ( arc - M_PI_4 ) * M_2_PI ); y0 = ypos; } else if (arc < pi_75) { x0 = xpos; y0 = ypos + MusECore::qwtInt(double(fm.ascent() - 1) * (arc - M_PI_4) * M_2_PI); } else { // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 x0 = xpos - MusECore::qwtInt(double(fm.horizontalAdvance(label)) #else x0 = xpos - MusECore::qwtInt(double(fm.width(label)) #endif * ( arc - pi_75) * M_2_PI ); y0 = ypos + fm.ascent() - 1; } break; case Top: default: // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 x0 = tval - (fm.horizontalAdvance(label)-1) / 2; #else x0 = tval - (fm.width(label)-1) / 2; #endif y0 = d_yorg - d_majLen - d_vpad; break; } p->save(); switch(d_textHighlightMode) { case TextHighlightNone: p->setPen(palette.text().color()); p->drawText(x0, y0, label); break; case TextHighlightAlways: p->setPen(palette.brightText().color()); p->drawText(x0, y0, label); break; case TextHighlightSplit: if(val > curValue) { p->setPen(palette.text().color()); p->drawText(x0, y0, label); } else { p->setPen(palette.brightText().color()); p->drawText(x0, y0, label); } break; case TextHighlightShadow: // Text shadow: p->setPen(Qt::black); p->drawText(x0 + 1, y0 + 1, label); // Text: p->setPen(QColor(Qt::white).darker(120)); // Meh, not quite so bright, p->drawText(x0, y0, label); break; case TextHighlightSplitAndShadow: //fprintf(stderr, "ScaleDraw::drawLabel val:%.20f curValue:%.20f\n", val, curValue); if(val > curValue) { //fprintf(stderr, " drawing normal\n"); p->setPen(palette.text().color()); p->drawText(x0, y0, label); } else { // Text shadow: p->setPen(Qt::black); p->drawText(x0 + 1, y0 + 1, label); // Text: //p->setPen(palette.brightText().color().darker(120)); // Meh, not quite so bright, p->setPen(QColor(Qt::white).darker(120)); // Meh, not quite so bright, p->drawText(x0, y0, label); } break; } p->restore(); } //------------------------------------------------------------ //.- //.F ScaleDraw::drawBackbone // Draws the baseline of the scale // // //.u Parameters //.p QPainter *p // //------------------------------------------------------------ void ScaleDraw::drawBackbone(QPainter *p, const QPalette& /*palette*/, double /*curValue*/) const { int bw2; int a1, a2; bw2 = p->pen().width() / 2; switch(d_orient) { case Left: case InsideVertical: p->drawLine(d_xorg - bw2, d_yorg, d_xorg - bw2, d_yorg + d_len - 1); break; case Right: p->drawLine(d_xorg + bw2, d_yorg, d_xorg + bw2, d_yorg + d_len - 1); break; case Round: a1 = MusECore::qwtMin(i1(), i2()) - 90 * 16; a2 = MusECore::qwtMax(i1(), i2()) - 90 * 16; p->drawArc(d_xorg, d_yorg, d_len, d_len, -a2, a2 - a1 + 1); // counterclockwise break; case Top: p->drawLine(d_xorg, d_yorg - bw2, d_xorg + d_len - 1, d_yorg-bw2); break; case Bottom: p->drawLine(d_xorg, d_yorg+bw2, d_xorg + d_len - 1, d_yorg+bw2); break; case InsideHorizontal: p->drawLine(d_xorg, d_majLen + d_vpad, d_xorg + d_len - 1, d_majLen + d_vpad); break; default: p->drawLine(d_xorg, d_yorg, d_xorg + d_len - 1, d_yorg); break; } } //------------------------------------------------------------ // //.F ScaleDraw::setGeometry // Specify the geometry of the scale // // //.u Parameters //.p int xorigin -- x coordinate of the origin // int yorigin -- y coordinate of the origin // int length -- length or diameter of the scale // Orientation o -- The orientation // //.u Description // // The parameters xorigin, yorigin and length have different meanings, // dependent on the // orientation: //.t // ScaleDraw::Left -- The origin is the topmost point of the // baseline. The baseline is a vertical line with the // specified length. Scale marks and labels are drawn // at the left of the baseline. // // ScaleDraw::Right -- The origin is the topmost point of the // baseline. The baseline is a vertical line with the // specified length. Scale marks and labels are drawn // at the right of the baseline. // // ScaleDraw::Top -- The origin is the leftmost point of the // baseline. The baseline is a horizontal line with the // specified length. Scale marks and labels are drawn // above the baseline. // // ScaleDraw::Bottom -- The origin is the leftmost point of the // baseline. The baseline is a horizontal line with the // specified length. Scale marks and labels are drawn // below the baseline. // // ScaleDraw::Round -- The origin is the top left corner of the // bounding rectangle of the baseline circle. The baseline // is the segment of a circle with a diameter of the specified length. // Scale marks and labels are drawn outside the baseline // circle. // //------------------------------------------------------------ void ScaleDraw::setGeometry(int xorigin, int yorigin, int length, OrientationX o) { d_xorg = xorigin; d_yorg = yorigin; d_radius = double(length) * 0.5; d_xCenter = double(xorigin) + double(length) * 0.5; d_yCenter = double(yorigin) + double(length) * 0.5; if (length > minLen) d_len = length; else d_len = minLen; d_orient = o; switch(d_orient) { case Left: case Right: case InsideVertical: setIntRange(d_yorg + d_len - 1, d_yorg); break; case Round: setIntRange(d_minAngle, d_maxAngle); break; case Top: case Bottom: case InsideHorizontal: setIntRange(d_xorg, d_xorg + d_len - 1); break; } } //------------------------------------------------------------ // //.F ScaleDraw::maxWidth // Return the maximum width of the scale for a specified QPainter // //.u Syntax //.f int ScaleDraw::maxWidth(QPainter *p, int penWidth) // //.u Parameters //.p const QFontMetrics& fm -- font metrics used for calculations // bool worst -- if TRUE, assume the worst possible case. If FALSE, // calculate the real maximum width, which is more // CPU intensive. // int penWidth -- the width of the pen that will be used to draw the scale // //------------------------------------------------------------ int ScaleDraw::maxWidth(const QFontMetrics& fm, bool worst, int penWidth) const { return maxLabelWidth(fm,worst) + scaleWidth(penWidth); } //------------------------------------------------------------ // //.F ScaleDraw::maxHeight // Return the maximum height of the scale for the // specified painter // //.u Syntax //.f int ScaleDraw::maxHeight(QPainter *p) // //.u Parameters //.p const QFontMetrics& fm -- font metrics used for calculations // int penWidth -- the width of the pen that will be used to draw the scale // //------------------------------------------------------------ int ScaleDraw::maxHeight(const QFontMetrics& fm, int /*penWidth*/) const { int rv = 0; switch (d_orient) { case Top: case Bottom: case Round: case InsideHorizontal: //rv = penWidth + d_vpad + d_majLen + fm.height(); rv = 2 * d_vpad + d_majLen + fm.ascent(); break; case Left: case Right: case InsideVertical: //rv = d_len + ((fm.height() + 1) / 2); rv = d_len + ((fm.ascent() + 1) / 2); break; } return rv; } //------------------------------------------------------------ // //.F ScaleDraw:maxBoundingRect // Return the maximum bounding rectangle of the scale // for a specified painter // //.u Parameters //.p const QFontMetrics& fm -- font metrics used for calculations // //.u Description // The bounding rectangle is not very exact for round scales // with strange angle ranges. // //------------------------------------------------------------ QRect ScaleDraw::maxBoundingRect(const QFontMetrics& fm) const { int i, wl; //,wmax; int a, ar, amin, amax; double arc; QRect r; wl = maxLabelWidth(fm, true); switch(d_orient) { case Left: r = QRect( d_xorg - d_hpad - d_majLen - wl, d_yorg - fm.ascent(), d_majLen + d_hpad + wl, d_len + fm.height()); break; case Right: r = QRect( d_xorg, d_yorg - fm.ascent(), d_majLen + d_hpad + wl, d_len + fm.height()); break; case Top: r = QRect ( d_xorg - wl / 2, d_yorg - d_majLen - fm.ascent(), d_len + wl, d_majLen + d_vpad + fm.ascent()); break; case Bottom: r = QRect ( d_xorg - wl / 2, d_yorg, d_len + wl, d_majLen + d_vpad + fm.height()); break; case Round: amin = 2880; amax = 0; ar = 0; for (i=0; i< d_scldiv.majCnt(); i++) { a = transform(d_scldiv.majMark(i)); while (a > 2880) a -= 5760; while (a < - 2880) a += 5760; ar = MusECore::qwtAbs(a); if (ar < amin) amin = ar; if (ar > amax) amax = ar; } for (i=0; i< d_scldiv.minCnt(); i++) { a = transform(d_scldiv.majMark(i)); while (a > 2880) a -= 5760; while (a < - 2880) a += 5760; ar = MusECore::qwtAbs(a); if (ar < amin) amin = ar; if (ar > amax) amax = ar; } arc = double(amin) / 16.0 * M_PI / 180.0; r.setTop(MusECore::qwtInt(d_yCenter - (d_radius + double(d_majLen + d_vpad)) * cos(arc)) + fm.ascent() ); arc = double(amax) / 16.0 * M_PI / 180.0; r.setBottom(MusECore::qwtInt(d_yCenter - (d_radius + double(d_majLen + d_vpad)) * cos(arc)) + fm.height() ); //wmax = d_len + d_majLen + d_hpad + wl; DELETETHIS r.setLeft(d_xorg - d_majLen - d_hpad - wl); r.setWidth(d_len + 2*(d_majLen + d_hpad + wl)); break; case InsideHorizontal: case InsideVertical: return r; break; } return r; } //------------------------------------------------------------ // //.F ScaleDraw::setAngleRange // Adjust the baseline circle segment for round scales. // //.u Syntax //.f void ScaleDraw::setAngleRange(double angle1, double angle2) // //.u Parameters //.p double angle1, double angle2 // boundaries of the angle interval in degrees. // //.u Description // The baseline will be drawn from min(angle1,angle2) to max(angle1, angle2). // The settings have no effect if the scale orientation is not set to // ScaleDraw::Round. The default setting is [ -135, 135 ]. // An angle of 0 degrees corresponds to the 12 o'clock position, // and positive angles count in a clockwise direction. // //.u Note //.i // -- The angle range is limited to [-360, 360] degrees. Angles exceeding // this range will be clipped. // -- For angles more than 359 degrees above or below min(angle1, angle2), // scale marks will not be drawn. // -- If you need a counterclockwise scale, use @QwtScaleDiv::setRange (1)@ // or @QwtScaleDiv::setRange (2)@. //------------------------------------------------------------ void ScaleDraw::setAngleRange(double angle1, double angle2) { int amin, amax; angle1 = MusECore::qwtLim(angle1, -360.0, 360.0); angle2 = MusECore::qwtLim(angle2, -360.0, 360.0); amin = int(rint(MusECore::qwtMin(angle1, angle2) * 16.0)); amax = int(rint(MusECore::qwtMax(angle1, angle2) * 16.0)); if (amin == amax) { amin -= 1; amax += 1; } d_minAngle = amin; d_maxAngle = amax; setIntRange(d_minAngle, d_maxAngle); } //------------------------------------------------------------ // //.F ScaleDraw::setLabelFormat // Set the number format for the major scale labels // //.u Syntax //.f void ScaleDraw::setLabelFormat(char f, int prec) // //.u Parameters //.p char f -- format character // int prec -- precision // //.u Description // Format character and precision have the same meaning as for the // QString class, with one exception: Special format 'M' (Metric suffix G M K) // // //.u See also // QString::setNum in the Qt manual // //------------------------------------------------------------ void ScaleDraw::setLabelFormat(char f, int prec) { d_fmt = f; d_prec = prec; } //------------------------------------------------------------ // //.F ScaleDraw::maxLabelWidth // Return the maximum width of a label // //.u Syntax //.f int ScaleDraw::maxLabelWidth(QPainter *p, int worst) // //.u Parameters //.p const QFontMetrics& fm -- font metrics used for calculations // int worst -- If TRUE, take the worst case. If FALSE, take // the actual width of the largest label. // //------------------------------------------------------------ int ScaleDraw::maxLabelWidth(const QFontMetrics& fm, bool worst) const { int i,rv = 0; double val; QString s; if (worst) // worst case { s = composeLabelText(WorstCase, d_fmt, d_prec); // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 rv = fm.horizontalAdvance(s); #else rv = fm.width(s); #endif } else // actual width { for (i=0;i= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 rv = MusECore::qwtMax(rv,fm.horizontalAdvance(s)); #else rv = MusECore::qwtMax(rv,fm.width(s)); #endif } } return rv; } //------------------------------------------------------------ // //.F ScaleDraw::scaleWidth // Return the maximum width of the scale (minus the labels) // //.u Syntax //.f int ScaleDraw::scaleWidth(int penWidth) // //.u Parameters // int penWidth -- the width of the pen that will be used to draw the scale //------------------------------------------------------------ int ScaleDraw::scaleWidth(int penWidth) const { switch (d_orient) { case Left: case Right: case InsideVertical: return penWidth + d_hpad + d_majLen; break; case Round: return penWidth + d_vpad + d_majLen; break; case Top: case Bottom: case InsideHorizontal: return d_len; break; } return d_len; } //------------------------------------------------------------ // //.F ScaleDraw::scaleDiv // Return the scale division // //.u Syntax //.f const QwtScaleDiv & ScaleDraw::scaleDiv() const // //.u See also // @^QwtScaleDiv@ //------------------------------------------------------------ //------------------------------------------------------------ // //.F ScaleDraw::orientation // Return the orientation // //.u Syntax //.f int ScaleDraw::orientation() const // //.u See also // @ScaleDraw::setGeometry@ // //------------------------------------------------------------ } // namespace MusEGui muse-4.0.0/src/muse/widgets/scldraw.h000066400000000000000000000100711404105311000174660ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: scldraw.h,v 1.1.1.1 2003/10/27 18:55:08 wschweer Exp $ // // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SCLDRAW_H__ #define __SCLDRAW_H__ #include #include #include "dimap.h" #include "scldiv.h" class QPalette; class QFontMetrics; class QPainter; namespace MusEGui { class ScaleDraw : public DiMap { public: enum OrientationX { Bottom, Top, Left, Right, InsideHorizontal, InsideVertical, Round }; enum TextHighlightMode { TextHighlightNone, TextHighlightAlways, TextHighlightSplit, TextHighlightShadow, TextHighlightSplitAndShadow }; private: ScaleDiv d_scldiv; static const int minLen; OrientationX d_orient; TextHighlightMode d_textHighlightMode; QString _specialText; // Text to show if value = min int d_xorg; int d_yorg; int d_len; int d_hpad; int d_vpad; int d_medLen; int d_majLen; int d_minLen; int d_minAngle; int d_maxAngle; double d_xCenter; double d_yCenter; double d_radius; char d_fmt; int d_prec; bool d_drawBackBone; // Like QString::number except it allows special 'M' format (Metric suffix G, M, K). QString composeLabelText(double val, char fmt, int prec) const; void drawTick(QPainter *p, const QPalette& palette, double curValue, double val, int len) const; void drawBackbone(QPainter *p, const QPalette& palette, double curValue) const; void drawLabel(QPainter *p, const QPalette& palette, double curValue, double val, bool isSpecialText = false) const; public: ScaleDraw(); void setScale(const ScaleDiv &s); void setScale(double vmin, double vmax, int maxMajIntv, int maxMinIntv, double step = 0.0, int logarithmic = 0); void setGeometry(int xorigin, int yorigin, int length, OrientationX o); void setAngleRange(double angle1, double angle2); // Special 'M' format (Metric suffix G, M, K) supported. void setLabelFormat(char f, int prec); void setBackBone(bool v) { d_drawBackBone = v; } const ScaleDiv& scaleDiv() const { return d_scldiv; } OrientationX orientation() const { return d_orient; } TextHighlightMode textHighlightMode() const { return d_textHighlightMode; } void setTextHighlightMode(TextHighlightMode mode) { d_textHighlightMode = mode; } QString specialText() const { return _specialText; } void setSpecialText(const QString& s) { _specialText = s; } QRect maxBoundingRect(const QFontMetrics& fm) const; int maxWidth(const QFontMetrics& fm, bool worst = true, int penWidth = 1) const; int maxHeight(const QFontMetrics& fm, int penWidth = 1) const; int maxLabelWidth(const QFontMetrics& fm, bool worst = true) const; int scaleWidth(int penWidth = 1) const; void draw(QPainter *p, const QPalette& palette, double curValue = 0.0); // const; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/sclif.cpp000066400000000000000000000161541404105311000174720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sclif.cpp,v 1.1.1.1 2003/10/27 18:55:10 wschweer Exp $ // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "sclif.h" namespace MusEGui { // ScaleIf - An interface class for widgets containing a scale // // This interface class is used to provide classes // with a protected ScaleDraw member and a public // interface to access that scale. // // The primary purpose of this class is to define // a common interface for classes which are supposed to // contain a ScaleDraw class. It provides a protected // ScaleDraw member // called d_scale and a couple of public member functions // which allow direct but restricted access // to this scale by the user. // Widgets derived from this class have // to implement the member function scaleChange(), // which is called to notify changes of the // scale parameters and usually requires repainting or // resizing respectively. // In general, a class derived from ScaleIf is // expected to manage the division and the position of its scale internally // when no user-defined scale is set. It should take the d_maxMinor // and d_maxMajor members into account, which can be set by the user. // An implementation can check if a user-defined scale is set by calling the // @ScaleIf::hasUserScale@ member. //------------------------------------------------------------ // ScaleIf::ScaleIf // Construct a ScaleIf instance // // Syntax // ScaleIf::ScaleIf() //------------------------------------------------------------ ScaleIf::ScaleIf() { d_userScale = false; d_maxMajor = 5; d_maxMinor = 3; d_scaleStep = 0.0; d_scale.setScale(0.0,100.0,d_maxMajor, d_maxMinor); } //------------------------------------------------------------ // ScaleIf::setScale (1) // Specify a user-defined scale. // // Syntax // void ScaleIf::setScale(double vmin, double vmax, int logarithmic) // // Parameters // double vmin, double vmax -- boundary values // int logarithmic -- If != 0, Build a logarithmic scale // // Description // By default, the widget is supposed to control the range of its scale // automatically, but sometimes it is desirable to have a user-defined // scale which is not in sync with // the widget's range, e.g. if a logarithmic scale is needed // (sliders don't support that) or if the scale is required // to have a fixed range (say 0...100%), independent of the // widget's range. // // See also // @ScaleIf::autoScale@ //------------------------------------------------------------ void ScaleIf::setScale(double vmin, double vmax, int logarithmic) { setScale(vmin,vmax,0.0,logarithmic); } //------------------------------------------------------------ // ScaleIf::setScale (2) // Specify a user-defined scale. // // Syntax // void ScaleIf::setScale(double vmin, double vmax, int logarithmic) // // Parameters // double vmin, double vmax -- interval boundaries // int step -- major step size // int logarithmic -- If != 0, build a logarithmic scale // // Description // By default, the widget is supposed to control the range of its scale // automatically, but sometimes it is desirable to have a user-defined // scale which is not in sync with // the widget's range, e.g. if a logarithmic scale is needed // (sliders don't support that) or if the scale is required // to have a fixed range (say 0...100%), independent of the // widget's range. //------------------------------------------------------------ void ScaleIf::setScale(double vmin, double vmax, double step, int logarithmic) { d_scaleStep = step; ScaleDiv oldscl(d_scale.scaleDiv()); d_scale.setScale(vmin, vmax, d_maxMajor, d_maxMinor, step, logarithmic); d_userScale = true; if (oldscl != d_scale.scaleDiv()) scaleChange(); } //------------------------------------------------------------ // Scale::setScale // Assign a user-defined scale division // // Syntax // void Scale::setScale(const ScaleDiv &s) // // Parameters // const ScaleDiv &s -- scale division //------------------------------------------------------------ void ScaleIf::setScale(const ScaleDiv &s) { d_scale.setScale(s); scaleChange(); } //------------------------------------------------------------ // ScaleIf::autoScale // Advise the widget to control the scale range // internally. // Syntax // void ScaleIf::autoScale // // Description // Autoscaling is on by default. //------------------------------------------------------------ void ScaleIf::autoScale() { if (!d_userScale) { d_userScale = false; scaleChange(); } } //------------------------------------------------------------ // ScaleIf::setScaleMaxMajor // Set the maximum number of major tick intervals. // // Syntax // void ScaleIf::setScaleMaxMajor(int ticks) // // Parameters // int ticks -- maximal number of major ticks. // // Description // The scale's major ticks are calculated automatically such that // the number of major intervals does not exceed . // The default value is 5. //------------------------------------------------------------ void ScaleIf::setScaleMaxMajor(int ticks) { if (ticks != d_maxMajor) { d_maxMajor = ticks; d_scale.setScale(d_scale.scaleDiv().lBound(), d_scale.scaleDiv().hBound(), d_maxMajor, d_maxMinor, 0.0,d_scale.scaleDiv().logScale()); scaleChange(); } } //------------------------------------------------------------ // ScaleIf::setScaleMaxMinor // Set the maximum number of minor tick intervals // // Syntax // void ScaleIf::setScaleMaxMinor(int ticks) // // Parameters // int ticks // // Description // The scale's minor ticks are calculated automatically such that // the number of minor intervals does not exceed . // The default value is 3. //------------------------------------------------------------ void ScaleIf::setScaleMaxMinor(int ticks) { if ( ticks != d_maxMinor) { d_maxMinor = ticks; d_scale.setScale(d_scale.scaleDiv().lBound(), d_scale.scaleDiv().hBound(), d_maxMajor, d_maxMinor, 0.0, d_scale.scaleDiv().logScale()); scaleChange(); } } } // namespace MusEGui muse-4.0.0/src/muse/widgets/sclif.h000066400000000000000000000042471404105311000171370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sclif.h,v 1.1.1.1 2003/10/27 18:54:33 wschweer Exp $ // Copyright (C) 1997 Josef Wilgen // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SCALE_IF_H__ #define __SCALE_IF_H__ #include "scldraw.h" namespace MusEGui { //--------------------------------------------------------- // ScaleIf //--------------------------------------------------------- class ScaleIf { bool d_userScale; protected: ScaleDraw d_scale; int d_maxMajor; int d_maxMinor; double d_scaleStep; bool hasUserScale() {return d_userScale;} virtual void scaleChange() = 0; public: ScaleIf(); virtual ~ScaleIf() {}; void setScale (double vmin, double vmax, int logarithmic = 0); void setScale (double vmin, double vmax, double step, int logarithmic = 0); void setScale(const ScaleDiv &s); void setScaleMaxMajor( int ticks); void setScaleMaxMinor( int ticks); void setScaleBackBone(bool v) { d_scale.setBackBone(v); } QString specialText() const { return d_scale.specialText(); } void setSpecialText(const QString& s) { d_scale.setSpecialText(s); } void autoScale(); int scaleMaxMinor() const {return d_maxMinor;} int scaleMaxMajor() const {return d_maxMinor;} double scaleStep() const { return d_scaleStep; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/scrollbar.cpp000066400000000000000000000027431404105311000203540ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // scrollbar.cpp // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "scrollbar.h" #include #include namespace MusEGui { ScrollBar::ScrollBar(Qt::Orientation orientation, bool autoPageStep, QWidget* parent) : QScrollBar(orientation, parent), _autoPageStep(autoPageStep) { }; void ScrollBar::redirectedWheelEvent(QWheelEvent* e) { if(isVisible()) wheelEvent(e); } void ScrollBar::resizeEvent(QResizeEvent* e) { if(_autoPageStep) setPageStep(e->size().height()); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/scrollbar.h000066400000000000000000000033771404105311000200250ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // scrollbar.h // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SCROLLBAR_H__ #define __SCROLLBAR_H__ #include class QWheelEvent; class QResizeEvent; namespace MusEGui { //--------------------------------------------------------- // ScrollBar //--------------------------------------------------------- class ScrollBar : public QScrollBar { Q_OBJECT private: bool _autoPageStep; public slots: void redirectedWheelEvent(QWheelEvent*); protected: virtual void resizeEvent(QResizeEvent*); public: ScrollBar(Qt::Orientation orientation, bool autoPageStep = false, QWidget* parent = 0); bool autoPageStep() const { return _autoPageStep; } void setAutoPageStep(bool v) { _autoPageStep = v; update(); } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/section.h000066400000000000000000000022501404105311000174730ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: section.h,v 1.1.1.1 2003/10/27 18:54:27 wschweer Exp $ // (C) Copyright 2002 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SECTION_H__ #define __SECTION_H__ namespace MusEGui { struct Section { int offset; unsigned len; int voff; int val; }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/sliderbase.cpp000066400000000000000000001131771404105311000205120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sliderbase.cpp,v 1.4.2.4 2007/01/27 14:52:43 spamatica Exp $ // Copyright (C) 1997 Josef Wilgen // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include "sliderbase.h" #include "mmath.h" #include #include #include #include #include #include #include #include #include // For debugging output: Uncomment the fprintf section. #define DEBUG_SLIDER_BASE(dev, format, args...) //fprintf(dev, format, ##args); namespace MusEGui { // DESCRIPTION // SliderBase is a base class for // slider widgets. QwtSliderBase handles the mouse events // and updates the slider's value accordingly. Derived classes // only have to implement the @QwtSliderBase::getValue@ and // @QwtSliderBase::getScrollMode@ members, and should react to a // @QwtSliderbase::valueChange@, which normally requires repainting. //------------------------------------------------------------ //.F SliderBase::SliderBase // Constructor // //.u Syntax //.f SliderBase::SliderBase(QWidget *parent, const char *name) // //.u Parameters //.p QWidget *parent, const char *name // //------------------------------------------------------------ SliderBase::SliderBase(QWidget *parent, const char *name) : QWidget(parent) { setObjectName(name); _id = -1; _cursorHoming = false; // NOTE: Avoid using borderlessMouse for now because it may jump when clicking from another active window: _borderlessMouse = false; // REMOVE Tim Trackinfo. // // [ Audio strip: Press and move: When switching from another active window... ] // // --------------------------------------------------------------------------- // // // // AudioComponentRack::controllerPressed id:1 // // val:0.03000000000000002665 calling enableController(false) // // // // SliderBase::mouseMoveEvent firstMouseMoveAfterPress // // e->globalPos() x:1042 y:602 _lastGlobalMousePos x:1041 y:602 calling setPosition(delta x:1, y:0) // // SliderBase::setPosition calling fitValue(delta x:1, y:0) d_mouseOffset:0.00000000000000000000 // // CompactSlider::getValue value:0.03000000000000002665 p x:1 y:0 step:0.01000000000000000021 x change:0.01000000000000000021 // // DoubleRange::setNewValue TOP val:0.04000000000000002859 d_prevValue:0.02000000000000001776 d_value:0.03000000000000002665 // // BOTTOM val:0.04000000000000002859 d_prevValue:0.03000000000000002665 d_value:0.04000000000000003553 // // not equal, calling valueChange // // AudioComponentRack::controllerChanged id:1 val:0.04000000000000003553 // // AudioComponentRack::controllerMoved id:1 val:0.04000000000000003553 // // // // -------------------------------------------------------------------------------------- // // [[ ignoreMouseMove is now set to true, and the cursor is now told to move to screen centre ... ]] // // -------------------------------------------------------------------------------------- // // // // SliderBase::mouseMoveEvent ignoring mouse move // // // // -------------------------------------------------------------------------------------- // // [[ Here's the trouble: The cursor has not yet moved to screen centre !!! It's fine if MusE is already active, see below... ]] // // -------------------------------------------------------------------------------------- // // // // SliderBase::mouseMoveEvent not firstMouseMoveAfterPress // // e->globalPos() x:1044 y:601 scrn_cntr x:683 y:384 calling setPosition(delta x:361, y:217) // // SliderBase::setPosition calling fitValue(delta x:361, y:217) d_mouseOffset:0.00000000000000000000 // // CompactSlider::getValue value:0.04000000000000003553 p x:361 y:217 step:0.01000000000000000021 x change:3.60999999999999987566 // // DoubleRange::setNewValue TOP val:3.64999999999999991118 d_prevValue:0.03000000000000002665 d_value:0.04000000000000003553 // // BOTTOM val:3.64999999999999991118 d_prevValue:0.04000000000000003553 d_value:1.00000000000000000000 // // not equal, calling valueChange // // AudioComponentRack::controllerChanged id:1 val:1.00000000000000000000 // // AudioComponentRack::controllerMoved id:1 val:1.00000000000000000000 // // // // SliderBase::mouseMoveEvent ignoring mouse move // // // // SliderBase::mouseMoveEvent not firstMouseMoveAfterPress // // e->globalPos() x:683 y:384 scrn_cntr x:683 y:384 calling setPosition(delta x:0, y:0) // // SliderBase::setPosition calling fitValue(delta x:0, y:0) d_mouseOffset:0.00000000000000000000 // // CompactSlider::getValue value:1.00000000000000000000 p x:0 y:0 step:0.01000000000000000021 x change:0.00000000000000000000 // // DoubleRange::setNewValue TOP val:1.00000000000000000000 d_prevValue:0.04000000000000003553 d_value:1.00000000000000000000 // // BOTTOM val:1.00000000000000000000 d_prevValue:1.00000000000000000000 d_value:1.00000000000000000000 _ignoreMouseMove = false; _mouseGrabbed = false; _pressed = false; _pagingButtons = Qt::RightButton; _firstMouseMoveAfterPress = false; _cursorOverrideCount = 0; d_tmrID = 0; d_updTime = 150; d_mass = 0.0; d_tracking = true; d_trackingTempDisable = false; d_mouseOffset = 0.0; d_valAccum = 0.0; d_enableValueToolTips = false; d_showValueToolTipsOnHover = false; d_valueAtPress = 0.0; d_scrollMode = ScrNone; setRange(0.0, 1.0, 0.1); } //------------------------------------------------------------ //.F SliderBase::~SliderBase // Destructor // //.u Syntax //.f SliderBase::~SliderBase() //------------------------------------------------------------ SliderBase::~SliderBase() { // Just in case the ref count is not 0. This is our last chance to clear // our contribution to QApplication::setOverrideCursor references. showCursor(); if (d_tmrID) killTimer(d_tmrID); } void SliderBase::showCursor(bool show) { if(_cursorOverrideCount > 1) fprintf(stderr, "MusE Warning: _cursorOverrideCount > 1 in SliderBase::showCursor(%d)\n", show); if(show) { while(_cursorOverrideCount > 0) { QApplication::restoreOverrideCursor(); _cursorOverrideCount--; } } else { _cursorOverrideCount++; QApplication::setOverrideCursor(Qt::BlankCursor); } } void SliderBase::setMouseGrab(bool grabbed) { if(grabbed && !_mouseGrabbed) { _mouseGrabbed = true; grabMouse(); // CAUTION } else if(!grabbed && _mouseGrabbed) { releaseMouse(); _mouseGrabbed = false; } } //------------------------------------------------------------ //.F void SliderBase::wheelEvent(QWheelEvent *e) // Add wheel event handling // //.u Syntax //.f void SliderBase::wheelEvent(QWheelEvent *e) //------------------------------------------------------------ void SliderBase::wheelEvent(QWheelEvent *e) { e->accept(); // Do not allow setting value from the external while mouse is pressed. if(_pressed) return; // this leads to incomprehensible values for the user, why not just use the step? (kybos) // float inc = (maxValue(ConvertNone) - minValue(ConvertNone)) / 40; // if (e->modifiers() == Qt::ShiftModifier) // inc = inc / 10; // if(inc < step()) // inc = step(); float inc = step(); if (e->modifiers() == Qt::ShiftModifier) inc /= 5; // this at least works well with the default audio slider (kybos) const QPoint pixelDelta = e->pixelDelta(); const QPoint angleDegrees = e->angleDelta() / 8; int delta = 0; if(!pixelDelta.isNull()) delta = pixelDelta.y(); else if(!angleDegrees.isNull()) delta = angleDegrees.y() / 15; else return; if (delta > 0) setValue(value(ConvertNone)+inc, ConvertNone); else setValue(value(ConvertNone)-inc, ConvertNone); // Show a handy tooltip value box. if(d_enableValueToolTips) #if QT_VERSION >= 0x050e00 showValueToolTip(e->globalPosition().toPoint()); #else showValueToolTip(e->globalPos()); #endif emit sliderMoved(value(), _id); emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); } //------------------------------------------------------------ //.F SliderBase::stopMoving // Stop updating if automatic scrolling is active // //.u Syntax //.f void SliderBase::stopMoving() //------------------------------------------------------------ void SliderBase::stopMoving() { if(d_tmrID) { killTimer(d_tmrID); d_tmrID = 0; } } //------------------------------------------------------------ //.F SliderBase::setUpdateTime // Specify the update interval for automatic scrolling // //.u Syntax //.f void SliderBase::setUpdateTime(int t) // //.u Parameters //.p int t -- update interval in milliseconds // //.u See also // @SliderBase::getScrollMode@ //------------------------------------------------------------ void SliderBase::setUpdateTime(int t) { if (t < 50) t = 50; d_updTime = t; } //------------------------------------------------------------ //.F SliderBase::mousePressEvent // Mouse press event handler // //.u Syntax //.f void SliderBase::mousePressEvent(QMouseEvent *e) // //.u Parameters //.p QMouseEvent *e -- Qt Mouse press event //------------------------------------------------------------ void SliderBase::mousePressEvent(QMouseEvent *e) { e->accept(); QPoint p = e->pos(); const Qt::MouseButton button = e->button(); const Qt::MouseButtons buttons = e->buttons(); const bool shift = e->modifiers() & Qt::ShiftModifier; //const bool ctrl = e->modifiers() & Qt::ControlModifier; const bool meta = e->modifiers() & Qt::MetaModifier; const bool clicked = (button == Qt::LeftButton || button == Qt::MidButton); d_timerTick = 0; _pressed = true; _mouseDeltaAccum = QPoint(); // Reset. _lastGlobalMousePos = e->globalPos(); d_valueAtPress = value(ConvertNone); d_valAccum = d_valueAtPress; // Reset. d_trackingTempDisable = meta; // Generate automation graph recording straight lines if modifier held. getScrollMode(p, button, e->modifiers(), d_scrollMode, d_direction); _lastMousePos = p; stopMoving(); showCursor(); DEBUG_SLIDER_BASE(stderr, "SliderBase::mousePressEvent x:%d, y:%d d_mouseOffset:%.20f d_valueAtPress:%.20f d_scrollMode:%d\n", p.x(), p.y(), d_mouseOffset, d_valueAtPress, d_scrollMode); // Only one mouse button at a time! Otherwise bad things happen. if(buttons ^ button) { // Clear everything. setMouseGrab(false); d_scrollMode = ScrNone; d_direction = 0; _pressed = false; return; } switch(d_scrollMode) { case ScrPage: case ScrTimer: d_mouseOffset = 0; DoubleRange::incPages(d_direction); // Show a handy tooltip value box. if(d_enableValueToolTips) showValueToolTip(e->globalPos()); emit sliderMoved(value(), _id); emit sliderMoved(value(), _id, shift); d_tmrID = startTimer(MusECore::qwtMax(250, 2 * d_updTime)); break; case ScrMouse: case ScrDirect: d_speed = 0; //if(!pagingButtons().testFlag(Qt::RightButton) && button == Qt::RightButton) if(button == Qt::RightButton) { // Clear everything. setMouseGrab(false); d_scrollMode = ScrNone; d_direction = 0; _pressed = false; DEBUG_SLIDER_BASE(stderr, "SliderBase::mousePressEvent _pressed:%d\n", _pressed); emit sliderRightClicked(e->globalPos(), _id); break; } if(d_scrollMode != ScrDirect) d_time.start(); if(_cursorHoming && clicked && d_scrollMode != ScrDirect) { _ignoreMouseMove = true; // Avoid recursion. d_mouseOffset = 0.0; } else if(_borderlessMouse && clicked) { d_mouseOffset = 0.0; // _lastGlobalMousePos = e->globalPos(); // "It is almost never necessary to grab the mouse when using Qt, as Qt grabs // and releases it sensibly. In particular, Qt grabs the mouse when a mouse // button is pressed and keeps it until the last button is released." // // Apparently not. For some reason this was necessary. When the cursor is dragged // outside the window, holding left then pressing right mouse button COMPLETELY // bypasses us, leaving the app's default right-click handler to popup, and leaving // us in a really BAD state: mouse is grabbed (and hidden) and no way out ! // // That is likely just how QWidget works, but here using global cursor overrides // it is disastrous. TESTED: Yes, that is how other controls work. Hitting another // button while the mouse has been dragged outside causes it to bypass us ! setMouseGrab(true); // CAUTION showCursor(false); // CAUTION _firstMouseMoveAfterPress = true; // Prepare for the first mouse move event after this press. // The problem with this approach of moving the cursor on click is that // we can't get a double click signal (ex. to open an editor). //_ignoreMouseMove = true; // Avoid recursion. //const QRect r = QApplication::desktop()->screenGeometry(); //const QPoint pt(r.width()/2, r.height()/2); //QCursor::setPos(pt); } else d_mouseOffset = getValue(p) - d_valueAtPress; // If direct mode, jump to the pressed location, which also calls valueChange() if anything changed. if(d_scrollMode == ScrDirect) { d_mouseOffset = 0.0; //setPosition(p); // No, it subtracts d_mouseOffset which leaves net zero in case of last line above. DoubleRange::fitValue(getValue(p)); // Must set this so that mouseReleaseEvent reads the right value. d_valAccum = value(ConvertNone); } // HACK // In direct mode let the inherited classes call these in their valueChange() methods, // so that they may be called BEFORE valueChanged signal is emitted by the setPosition() call above. // ScrDirect mode only happens once upon press with a modifier. After that, another mode is set. // Hack: Since valueChange() is NOT called if nothing changed, call these in that case. if(d_scrollMode != ScrDirect || value(ConvertNone) == d_valueAtPress) { processSliderPressed(_id); emit sliderPressed(value(), _id); } // Show a handy tooltip value box. if(d_enableValueToolTips) showValueToolTip(e->globalPos()); // If direct mode, now set the mode to a regular mouse mode. if(d_scrollMode == ScrDirect) d_scrollMode = ScrMouse; break; default: d_mouseOffset = 0; d_direction = 0; break; } } //------------------------------------------------------------ //.- //.F SliderBase::buttonRelease // Emit a valueChanged() signal if necessary // //.u Syntax //.f void SliderBase::buttonReleased() // //------------------------------------------------------------ void SliderBase::buttonReleased() { if (!trackingIsActive() && valueHasChangedAtRelease()) { emit valueChanged(value(), _id); emit valueChanged(value(), _id, d_scrollMode); } } //------------------------------------------------------------ // //.F SliderBase::mouseReleaseEvent // Mouse Release Event handler // //.u Syntax //.f void SliderBase::mouseReleaseEvent(QMouseEvent *e) // //.u Parameters //.p QMouseEvent *e -- Qt Mouse Event // //------------------------------------------------------------ void SliderBase::mouseReleaseEvent(QMouseEvent *e) { int ms = 0; _ignoreMouseMove = false; const Qt::MouseButton button = e->button(); const bool clicked = (button == Qt::LeftButton || button == Qt::MidButton); const bool shift = e->modifiers() & Qt::ShiftModifier; _pressed = e->buttons() != Qt::NoButton; DEBUG_SLIDER_BASE(stderr, "SliderBase::mouseReleaseEvent pos x:%d y:%d last x:%d y:%d e->buttons():%d button:%d _pressed:%d val:%.20f d_valueAtPress:%.20f\n", e->pos().x(), e->pos().y(), _lastMousePos.x(), _lastMousePos.y(), int(e->buttons()), button, _pressed, value(), d_valueAtPress); e->accept(); switch(d_scrollMode) { case ScrMouse: if(button == Qt::RightButton) { d_scrollMode = ScrNone; break; } if(_borderlessMouse && clicked) { d_scrollMode = ScrNone; if(!_firstMouseMoveAfterPress) { _ignoreMouseMove = true; // Avoid recursion. QCursor::setPos(_lastGlobalMousePos); //_ignoreMouseMove = false; } // showCursor(); } else { // setPosition(e->pos()); movePosition(e->pos() - _lastMousePos, shift); d_direction = 0; d_mouseOffset = 0; if (d_mass > 0.0) { ms = d_time.elapsed(); if ((fabs(d_speed) > 0.0) && (ms < 50)) d_tmrID = startTimer(d_updTime); } else { d_scrollMode = ScrNone; buttonReleased(); } } DEBUG_SLIDER_BASE(stderr, " Calling processSliderReleased val:%.20f d_valueAtPress:%.20f\n", value(), d_valueAtPress); processSliderReleased(_id); emit sliderReleased(value(), _id); break; case ScrDirect: // setPosition(e->pos()); movePosition(e->pos() - _lastMousePos, shift); d_direction = 0; d_mouseOffset = 0; d_scrollMode = ScrNone; buttonReleased(); break; case ScrPage: stopMoving(); d_timerTick = 0; buttonReleased(); d_scrollMode = ScrNone; break; case ScrTimer: stopMoving(); d_timerTick = 0; buttonReleased(); d_scrollMode = ScrNone; break; default: d_scrollMode = ScrNone; buttonReleased(); } // Make sure this is done. See mousePressEvent. showCursor(); setMouseGrab(false); d_trackingTempDisable = false; // Reset generating automation graph recording straight lines if ctrl held. } //------------------------------------------------------------ // //.F SliderBase::setPosition // Move the slider to a specified point, adjust the value // and emit signals if necessary // //.u Syntax //.f void SliderBase::setPosition(const QPoint &p) // //.u Parameters //.p const QPoint &p // //------------------------------------------------------------ void SliderBase::setPosition(const QPoint &p) { DEBUG_SLIDER_BASE(stderr, "SliderBase::setPosition calling fitValue(x:%d, y:%d) d_mouseOffset:%.20f\n", p.x(), p.y(), d_mouseOffset); DoubleRange::fitValue(getValue(p) - d_mouseOffset); } //------------------------------------------------------------ // //.F SliderBase::movePosition // Move the slider to a specified point, adjust the value // and emit signals if necessary // //.u Syntax //.f void SliderBase::movePosition(const QPoint &deltaP, bool fineMode) // //.u Parameters //.p const QPoint &deltaP -- Change in position //.p bool fineMode -- Fine mode if true, coarse mode if false. // //.u Description // Coarse mode (the normal mode) maps pixels to values depending on range and width, // such that the slider follows the mouse cursor. Fine mode maps one step() value per pixel. //------------------------------------------------------------ void SliderBase::movePosition(const QPoint &deltaP, bool fineMode) { DEBUG_SLIDER_BASE(stderr, "SliderBase::movePosition calling fitValue(delta x:%d, y:%d) fineMode:%d\n", deltaP.x(), deltaP.y(), fineMode); DoubleRange::fitValue(moveValue(deltaP, fineMode)); } //------------------------------------------------------------ // //.F SliderBase::setTracking // // Enables or disables tracking. // //.u Syntax //.f void SliderBase::setTracking(bool enable) // //.u Parameters //.p bool enable -- enable (TRUE) or disable (FALSE) tracking // //.u Description // // If tracking is enabled, the slider emits a // valueChanged() signal whenever its value // changes (the default behaviour). If tracking // is disabled, the value changed() signal will only // be emitted if //.i -- the user releases the mouse // button and the value has changed or // -- at the end of automatic scrolling. //.P // Tracking is enabled by default. //------------------------------------------------------------ void SliderBase::setTracking(bool enable) { d_tracking = enable; } //------------------------------------------------------------ //.- //.F SliderBase::mouseMoveEvent // Mouse Move Event handler // //.u Syntax //.f void SliderBase::mouseMoveEvent(QMouseEvent *e) // //.u Parameters //.p QMouseEvent *e -- Qt Mouse Move Event // //------------------------------------------------------------ void SliderBase::mouseMoveEvent(QMouseEvent *e) { DEBUG_SLIDER_BASE(stderr, "SliderBase::mouseMoveEvent _ignoreMouseMove:%d\n", _ignoreMouseMove); e->accept(); const bool shift = e->modifiers() & Qt::ShiftModifier; //const bool ctrl = e->modifiers() & Qt::ControlModifier; const bool meta = e->modifiers() & Qt::MetaModifier; //d_trackingTempDisable = ctrl; // Generate automation graph recording straight lines if modifier held. if(_ignoreMouseMove) { DEBUG_SLIDER_BASE(stderr, "SliderBase::mouseMoveEvent ignoring mouse move\n"); _ignoreMouseMove = false; return; } const double prevValue = value(ConvertNone); if (d_scrollMode == ScrMouse ) { d_trackingTempDisable = meta; // Generate automation graph recording straight lines if modifier held. if(borderlessMouse()) { const QRect r = QApplication::primaryScreen()->geometry(); const QPoint scrn_cntr(r.width()/2, r.height()/2); QPoint delta; if(_firstMouseMoveAfterPress) { _firstMouseMoveAfterPress = false; delta = e->globalPos() - _lastGlobalMousePos; DEBUG_SLIDER_BASE(stderr, "SliderBase::mouseMoveEvent firstMouseMoveAfterPress\n e->globalPos() x:%d y:%d _lastGlobalMousePos x:%d y:%d calling setPosition(delta x:%d, y:%d)\n", e->globalPos().x(), e->globalPos().y(), _lastGlobalMousePos.x(), _lastGlobalMousePos.y(), delta.x(), delta.y()); } else { delta = e->globalPos() - scrn_cntr; DEBUG_SLIDER_BASE(stderr, "SliderBase::mouseMoveEvent not firstMouseMoveAfterPress\n e->globalPos() x:%d y:%d scrn_cntr x:%d y:%d calling setPosition(delta x:%d, y:%d)\n", e->globalPos().x(), e->globalPos().y(), scrn_cntr.x(), scrn_cntr.y(), delta.x(), delta.y()); } setPosition(delta); _ignoreMouseMove = true; QCursor::setPos(scrn_cntr); //_ignoreMouseMove = false; } else { //if(shift) // movePosition(e->pos() - _lastMousePos, true); //else // setPosition(e->pos()); movePosition(e->pos() - _lastMousePos, shift); } _mouseDeltaAccum += (e->pos() - _lastMousePos); _lastMousePos = e->pos(); _lastGlobalMousePos = e->globalPos(); if (d_mass > 0.0) { double ms = double(d_time.elapsed()); if (ms < 1.0) ms = 1.0; d_speed = (exactValue(ConvertNone) - exactPrevValue(ConvertNone)) / ms; d_time.start(); } //const bool valch = (valueHasChangedAtRelease()); const bool valch = value(ConvertNone) != prevValue; // Show a handy tooltip value box. if(d_enableValueToolTips && valch) //showValueToolTip(mapToGlobal(pos())); showValueToolTip(e->globalPos()); if(valch) { emit sliderMoved(value(), _id); emit sliderMoved(value(), _id, shift); } } else if(d_scrollMode == ScrNone) { // Show a handy tooltip value box. if(d_enableValueToolTips && d_showValueToolTipsOnHover) //showValueToolTip(mapToGlobal(pos())); showValueToolTip(e->globalPos()); } } void SliderBase::mouseDoubleClickEvent(QMouseEvent* e) { DEBUG_SLIDER_BASE(stderr, "mouseDoubleClickEvent::mouseDoubleClickEvent\n"); emit sliderDoubleClicked(e->pos(), _id, e->buttons(), e->modifiers()); e->ignore(); QWidget::mouseDoubleClickEvent(e); } void SliderBase::keyPressEvent(QKeyEvent* e) { int val = 0; switch (e->key()) { case Qt::Key_Up: val = 1; break; case Qt::Key_Down: val = -1; break; default: // Let ancestor handle it. e->ignore(); QWidget::keyPressEvent(e); return; break; } if(e->modifiers() & (Qt::AltModifier | Qt::MetaModifier | Qt::ControlModifier)) { // Let ancestor handle it. e->ignore(); QWidget::keyPressEvent(e); return; } e->accept(); // Do not allow setting value from the external while mouse is pressed. if(_pressed) return; if(e->modifiers() == Qt::ShiftModifier) //incPages(val); incValue(val * 5); else incValue(val); // Show a handy tooltip value box. //if(d_enableValueToolTips) // showValueToolTip(e->globalPos()); emit sliderMoved(value(), _id); emit sliderMoved(value(), _id, (bool)(e->modifiers() & Qt::ShiftModifier)); } void SliderBase::focusOutEvent(QFocusEvent* e) { DEBUG_SLIDER_BASE(stderr, "SliderBase::focusOutEvent _pressed:%d\n", _pressed); e->ignore(); QWidget::focusOutEvent(e); // Was a mouse button already pressed before focus was lost? // We will NOT get a mouseReleaseEvent! Take care of it here. // Typically this happens when popping up a menu in response to a click. if(_pressed) { // Clear everything. _ignoreMouseMove = false; d_scrollMode = ScrNone; d_direction = 0; _pressed = false; // Make sure this is done. See mousePressEvent. showCursor(); setMouseGrab(false); } } //------------------------------------------------------------ // //.F SliderBase::timerEvent // Timer event handler // //.u Syntax //.f void SliderBase::timerEvent(QTimerEvent *e) // //.u Parameters //.p QTimerEvent *e -- Qt timer event // //------------------------------------------------------------ void SliderBase::timerEvent(QTimerEvent*) { const double prevValue = value(ConvertNone); double newval; double inc = step(); switch (d_scrollMode) { case ScrMouse: if (d_mass > 0.0) { d_speed *= exp( - double(d_updTime) * 0.001 / d_mass ); newval = exactValue(ConvertNone) + d_speed * double(d_updTime); DoubleRange::fitValue(newval); // stop if d_speed < one step per second if (fabs(d_speed) < 0.001 * fabs(step())) { d_speed = 0; stopMoving(); buttonReleased(); } } else stopMoving(); break; case ScrPage: DoubleRange::incPages(d_direction); if(value(ConvertNone) != prevValue) { // Show a handy tooltip value box. if(d_enableValueToolTips) showValueToolTip(cursor().pos()); emit sliderMoved(value(), _id); emit sliderMoved(value(), _id, false); } if (!d_timerTick) { killTimer(d_tmrID); d_tmrID = startTimer(d_updTime); } break; case ScrTimer: DoubleRange::fitValue(value(ConvertNone) + double(d_direction) * inc); if(value(ConvertNone) != prevValue) { // Show a handy tooltip value box. if(d_enableValueToolTips) showValueToolTip(cursor().pos()); emit sliderMoved(value(), _id); emit sliderMoved(value(), _id, false); } if (!d_timerTick) { killTimer(d_tmrID); d_tmrID = startTimer(d_updTime); } break; default: stopMoving(); break; } d_timerTick = 1; } //------------------------------------------------------------ // //.F SliderBase::valueChange // Notify change of value // //.u Syntax //.f void SliderBase::valueChange() // //.u Parameters //.p double x -- new value // //.u Description // This function can be reimplemented by derived classes // in order to keep track of changes, i.e. repaint the widget. // The default implementation emits a valueChanged() signal // if tracking is enabled. // //------------------------------------------------------------ void SliderBase::valueChange() { if (trackingIsActive()) { emit valueChanged(value(), _id); emit valueChanged(value(), _id, d_scrollMode); } } //------------------------------------------------------------ // //.F SliderBase::setMass // Set the slider's mass for flywheel effect. // //.u Syntax //.f void SliderBase::setMass(double val) // //.u Parameters //.p double val -- new mass in kg // //.u Description // // If the slider's mass is greater then 0, it will continue // to move after the mouse button has been released. Its speed // decreases with time at a rate depending on the slider's mass. // A large mass means that it will continue to move for a // long time. // // Limits: If the mass is smaller than 1g, it is set to zero. // The maximal mass is limited to 100kg. // // Derived widgets may overload this function to make it public. // //------------------------------------------------------------ void SliderBase::setMass(double val) { if (val < 0.001) d_mass = 0.0; else if (val > 100.0) d_mass = 100.0; else d_mass = val; } //------------------------------------------------------------ // //.F SliderBase::setValue // Move the slider to a specified value // //.u Syntax //.f void SliderBase::setValue(double val) // //.u Parameters //.p double val -- new value // //.u Description // This function can be used to move the slider to a value // which is not an integer multiple of the step size. // //.u See also // @SliderBase::fitValue@ //------------------------------------------------------------ void SliderBase::setValue(double val, ConversionMode mode) { // Do not allow setting value from the external while mouse is pressed. if(_pressed) return; if (d_scrollMode == ScrMouse) stopMoving(); DoubleRange::setValue(val, mode); } //------------------------------------------------------------ // //.F QSlider::fitValue // Set the slider's value to the nearest integer multiple // of the step size. // //.u Syntax //.f void SliderBase::fitValue(double val) // //.u See also: // @SliderBase::setValue@ //------------------------------------------------------------ void SliderBase::fitValue(double val, ConversionMode mode) { // Do not allow setting value from the external while mouse is pressed. if(_pressed) return; if (d_scrollMode == ScrMouse) stopMoving(); DoubleRange::fitValue(val, mode); } //------------------------------------------------------------ // //.F SliderBase::incValue // Increment the value by a specified number of steps // //.u Syntax //.f void SliderBase::incValue(int steps) // //.u Parameters //.p int steps -- number of steps // //------------------------------------------------------------ void SliderBase::incValue(int steps) { // Do not allow setting value from the external while mouse is pressed. if(_pressed) return; if (d_scrollMode == ScrMouse) stopMoving(); DoubleRange::incValue(steps); } //------------------------------------------------------------ // //.F SliderBase::stepPage // Increment the value by a specified number of steps // //.u Syntax //.f void SliderBase::stepPages(int pages) // //.u Parameters //.p int pages -- +/- number of pages // //.u Description // Steps the control as if pager was clicked. // Designed to be called from outside (like from a buddy label), rather than from // the control itself. Calls DoubleRange::incPages, which normally causes valueChange() // (and emits valueChanged), but also emits sliderMoved. // //------------------------------------------------------------ void SliderBase::stepPages(int pages) { // Do not allow setting value from the external while mouse is pressed. if(_pressed) return; DoubleRange::incPages(pages); emit sliderMoved(value(), _id); emit sliderMoved(value(), _id, false); } //------------------------------------------------------------ // //.F SliderBase::getValue // Determine the value corresponding to a specified poind // //.u Syntax //.f void SliderBase::getValue(const QPoint &p) // //.u Parameters //.p const QPoint &p -- point // //.u Description // This is an abstract virtual function which is called when // the user presses or releases a mouse button or moves the // mouse. It has to be implemented by the derived class. // //------------------------------------------------------------ //------------------------------------------------------------ // //.F SliderBase::getScrollMode // Determine what to do when the user presses a mouse button. // //.u Syntax //.f void SliderBase::getScrollMode(const QPoint &p, int &scrollMode, int &direction) // //.u Input Parameters //.p const QPoint &p -- point where the mouse was pressed // //.u Output parameters // int &scrollMode -- The scrolling mode // int &direction -- direction: 1, 0, or -1. // //.u Description // This function is abstract and has to be implemented by derived classes. // It is called on a mousePress event. The derived class can determine // what should happen next in dependence of the position where the mouse // was pressed by returning scrolling mode and direction. SliderBase // knows the following modes: //.t // SliderBase::ScrNone -- Scrolling switched off. Don't change the value. // SliderBase::ScrMouse -- Change the value while the user keeps the // button pressed and moves the mouse. // SliderBase::ScrTimer -- Automatic scrolling. Increment the value // in the specified direction as long as // the user keeps the button pressed. // SliderBase::ScrPage -- Automatic scrolling. Same as ScrTimer, but // increment by page size. // // //------------------------------------------------------------ //------------------------------------------------------------ // //.F SliderBase::valueChanged // Notify a change of value. // //.u Syntax //.f void SliderBase::valueChanged(double value, int id) // //.u Parameters //.p double value -- new value // //.u Description // In the default setting // (tracking enabled), this signal will be emitted every // time the value changes ( see setTracking() ). //------------------------------------------------------------ //------------------------------------------------------------ // //.F SliderBase::sliderPressed // This signal is emitted when the user presses the // movable part of the slider (start ScrMouse Mode). // //.u Syntax //.f void SliderBase::sliderPressed() // //------------------------------------------------------------ //------------------------------------------------------------ // //.F SliderBase::SliderReleased // This signal is emitted when the user releases the // movable part of the slider. // //.u Syntax //.f void QwtSliderbase::SliderReleased() // //------------------------------------------------------------ //------------------------------------------------------------ // //.F SliderBase::sliderMoved // This signal is emitted when the user moves the // slider with the mouse. // //.u Syntax //.f void SliderBase::sliderMoved(double value, int _id [, bool shift]) // //.u Parameters //.p double value -- new value // //------------------------------------------------------------ } // namespace MusEGui muse-4.0.0/src/muse/widgets/sliderbase.h000066400000000000000000000162511404105311000201520ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: sliderbase.h,v 1.4.2.3 2006/11/14 06:28:37 terminator356 Exp $ // Copyright (C) 1997 Josef Wilgen // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SLIDERBASE_H__ #define __SLIDERBASE_H__ #include "drange.h" #include #include #include class QFocusEvent; class QTimerEvent; class QWheelEvent; class QMouseEvent; namespace MusEGui { //--------------------------------------------------------- // SliderBase //--------------------------------------------------------- class SliderBase : public QWidget, public DoubleRange { Q_OBJECT Q_PROPERTY( int id READ id WRITE setId ) Q_PROPERTY( double minValue READ minValue WRITE setMinValue ) Q_PROPERTY( double maxValue READ maxValue WRITE setMaxValue ) Q_PROPERTY( double value READ value WRITE setValue ) public: enum SliderBaseScrollMode { ScrNone, ScrMouse, ScrTimer, ScrDirect, ScrPage }; int _id; int d_tmrID; int d_updTime; int d_timerTick; QElapsedTimer d_time; double d_speed; double d_mass; bool _cursorHoming; bool _borderlessMouse; double d_valAccum; QPoint _mouseDeltaAccum; QPoint _lastMousePos; QPoint _lastGlobalMousePos; Qt::MouseButtons _pagingButtons; // Internal flags. bool _ignoreMouseMove; bool _firstMouseMoveAfterPress; // Whether we have grabbed the mouse. bool _mouseGrabbed; // The number of times we have called QApplication::setOverrideCursor(). // This should always be one or zero, anything else is an error, but unforeseen // events might cause us to miss a decrement with QApplication::restoreOverrideCursor(). int _cursorOverrideCount; // If show is true, calls QApplication::restoreOverrideCursor() until _cursorOverrideCount-- is <= 0. // If show is false, calls QApplication::setOverrideCursor with a blank cursor. void showCursor(bool show = true); // Sets or resets the _mouseGrabbed flag and grabs or releases the mouse. void setMouseGrab(bool grabbed = false); void buttonReleased(); protected: bool d_enableValueToolTips; bool d_showValueToolTipsOnHover; int d_scrollMode; double d_mouseOffset; int d_direction; int d_tracking; bool _pressed; bool d_trackingTempDisable; double d_valueAtPress; bool valueHasChangedAtRelease() const { return value(ConvertNone) != d_valueAtPress; } virtual void setMass(double val); void setPosition(const QPoint &p); void movePosition(const QPoint &deltaP, bool fineMode); virtual void valueChange(); virtual double mass() const { return d_mass; } void wheelEvent(QWheelEvent *e); void timerEvent(QTimerEvent *e); void mousePressEvent(QMouseEvent *e); void mouseReleaseEvent(QMouseEvent *e); virtual void mouseMoveEvent(QMouseEvent *e); virtual void mouseDoubleClickEvent(QMouseEvent *e); virtual void keyPressEvent(QKeyEvent*); // Required because if focus is lost while a mouse button is pressed, we do not get the mouseReleaseEvent. virtual void focusOutEvent(QFocusEvent*); // Determine the value corresponding to a specified mouse location. // If borderless mouse is enabled p is a delta value not absolute, so can be negative. virtual double getValue(const QPoint & p) = 0; // Determine the value corresponding to a specified mouse movement. virtual double moveValue(const QPoint &deltaP, bool fineMode = false) = 0; // Determine scrolling mode and direction. virtual void getScrollMode( QPoint &p, const Qt::MouseButton &button, const Qt::KeyboardModifiers& modifiers, int &scrollMode, int &direction) = 0; // Show a handy tooltip value box. virtual void showValueToolTip(QPoint) { } // Same as sliderPressed signal, except it's not a signal and is called before sliderPressed is emitted. virtual void processSliderPressed(int) { } // Same as sliderReleased signal, except it's not a signal and is called before sliderReleased is emitted. virtual void processSliderReleased(int) { } public slots: void setValue(double val, ConversionMode mode = ConvertDefault); void fitValue(double val, ConversionMode mode = ConvertDefault); void incValue(int steps); signals: void valueChanged(double value, int id); void valueChanged(double value, int id, int scrollMode); void sliderPressed(double value, int id); void sliderReleased(double value, int id); void sliderMoved(double value, int id); void sliderMoved(double value, int id, bool shift); void sliderRightClicked(QPoint p, int id); void sliderDoubleClicked(QPoint p, int id, Qt::MouseButtons buttons, Qt::KeyboardModifiers keys); public: SliderBase( QWidget *parent = 0, const char *name = 0 ); virtual ~SliderBase(); // Useful for signal mappers where we can't pass scroll mode. int scrollMode() const { return d_scrollMode; } bool mouseGrabbed() const { return _mouseGrabbed; } bool isPressed() const { return _pressed; } bool cursorHoming() const { return _cursorHoming; } void setCursorHoming(bool b) { _cursorHoming = b; } bool borderlessMouse() const { return _borderlessMouse; } void setBorderlessMouse(bool v) { _borderlessMouse = v; update(); } // The allowed mouse buttons which will cause a page step. Qt::MouseButtons pagingButtons() const { return _pagingButtons; } // Set the allowed mouse buttons which will cause a page step. void setPagingButtons(Qt::MouseButtons buttons) { _pagingButtons = buttons; } bool enableValueToolTips() const { return d_enableValueToolTips; } void setEnableValueToolTips(bool enable) { d_enableValueToolTips = enable; } bool showValueToolTipsOnHover() const { return d_showValueToolTipsOnHover; } void setShowValueToolTipsOnHover(bool enable) { d_showValueToolTipsOnHover = enable; } void setUpdateTime(int t); void stopMoving(); bool tracking() const { return d_tracking; } void setTracking(bool enable); bool trackingIsActive() const { return d_tracking && !d_trackingTempDisable; } void stepPages(int pages); void setMinValue(double v, ConversionMode mode = ConvertDefault) { DoubleRange::setRange(v, maxValue(mode), 0.0, 1, mode); } void setMaxValue(double v, ConversionMode mode = ConvertDefault) { DoubleRange::setRange(minValue(mode), v, 0.0, 1, mode); } int id() const { return _id; } void setId(int i) { _id = i; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/spinbox.cpp000066400000000000000000000065521404105311000200550ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: spinbox.cpp,v 1.1.2.3 2009/07/09 18:27:11 terminator356 Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "spinbox.h" namespace MusEGui { void SpinBoxLineEdit::mouseDoubleClickEvent(QMouseEvent* e) { QLineEdit::mouseDoubleClickEvent(e); emit doubleClicked(); if((e->buttons() & Qt::LeftButton) && (e->modifiers() & Qt::ControlModifier)) emit ctrlDoubleClicked(); } //void SpinBoxLineEdit::mousePressEvent(QMouseEvent* e) //{ // QLineEdit::mousePressEvent(e); //selectAll(); // if((e->buttons() & Qt::LeftButton) && (e->modifiers() & Qt::ControlModifier)) // emit ctrlClicked(); //} //--------------------------------------------------------- // SpinBox //--------------------------------------------------------- SpinBox::SpinBox(QWidget* parent) : QSpinBox(parent) { _returnMode = false; SpinBoxLineEdit* le = new SpinBoxLineEdit(this); setLineEdit(le); setKeyboardTracking(false); connect(le, SIGNAL(doubleClicked()), this, SIGNAL(doubleClicked())); connect(le, SIGNAL(ctrlDoubleClicked()), this, SIGNAL(ctrlDoubleClicked())); //connect(le, SIGNAL(ctrlClicked()), this, SIGNAL(ctrlClicked())); } SpinBox::SpinBox(int minValue, int maxValue, int step, QWidget* parent) : QSpinBox(parent) { _returnMode = false; SpinBoxLineEdit* le = new SpinBoxLineEdit(this); setLineEdit(le); setRange(minValue, maxValue); setSingleStep(step); setKeyboardTracking(false); connect(le, SIGNAL(doubleClicked()), this, SIGNAL(doubleClicked())); connect(le, SIGNAL(ctrlDoubleClicked()), this, SIGNAL(ctrlDoubleClicked())); //connect(le, SIGNAL(ctrlClicked()), this, SIGNAL(ctrlClicked())); } void SpinBox::keyPressEvent(QKeyEvent* ev) { switch (ev->key()) { case Qt::Key_Return: { bool mod = lineEdit()->isModified(); QSpinBox::keyPressEvent(ev); if(_returnMode && !mod) // Force valueChanged if return mode set, even if not modified. emit valueChanged(value()); emit returnPressed(); } return; break; case Qt::Key_Escape: emit escapePressed(); return; break; default: break; } QSpinBox::keyPressEvent(ev); } void SpinBox::wheelEvent(QWheelEvent* e) { QSpinBox::wheelEvent(e); // Need this because Qt doesn't deselect the text if not focused. if(!hasFocus() && lineEdit()) lineEdit()->deselect(); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/spinbox.h000066400000000000000000000067611404105311000175240ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: spinbox.h,v 1.1.2.2 2009/02/02 21:38:01 terminator356 Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // SpinBox: // Click up/down, or mousewheel, or hit enter with un-modified text (which means enter TWICE for modified text), // and the control will give up focus, thereby allowing you to use global shortcut keys afterwards. // Up/down keys still keep the focus. // // < Old. That was the SpinBox behaviour in MusE1 (Qt3). They are still desirable goals for MusE2 (Qt4). // < Flaw: Calling clearFocus() means nothing has focus, not canvases, not even the active top level window. We want canvases to have it. // < That requires (here in MusE2) setting top win focus proxies and using Application::focusChanged() to redirect focus to the // < active window's proxy. // < Very ugly. And with MDI, even more complicated to give focus back to current sub-window. Tried, was crash-prone. // < Also, toolbars can be floated, so calling clearFocus() from a SpinBox on a floating toolbar means nothing has focus but the // < toolbar itself is the active window, which requires setting a focus proxy on the toolbar so that Application::focusChanged() // < can figure out who to give the focus to! // < It seems we will have to use signals/slots instead of clearFocus()... // < Flaw: Clearing focus when up/down clicked (when stepBy() is called), auto-repeat might not work because the control has lost focus. #ifndef __SPINBOX_H__ #define __SPINBOX_H__ #include #include namespace MusEGui { class SpinBoxLineEdit : public QLineEdit { Q_OBJECT protected: virtual void mouseDoubleClickEvent(QMouseEvent* e); //virtual void mousePressEvent(QMouseEvent* e); signals: void doubleClicked(); void ctrlDoubleClicked(); //void ctrlClicked(); public: SpinBoxLineEdit(QWidget* parent = 0) : QLineEdit(parent) {}; }; //--------------------------------------------------------- // SpinBox //--------------------------------------------------------- class SpinBox : public QSpinBox { Q_OBJECT bool _returnMode; protected: virtual void keyPressEvent(QKeyEvent*); virtual void wheelEvent(QWheelEvent*); signals: void doubleClicked(); void ctrlDoubleClicked(); //void ctrlClicked(); void returnPressed(); void escapePressed(); public: SpinBox(QWidget* parent=0); SpinBox(int minValue, int maxValue, int step = 1, QWidget* parent=0); void setReturnMode(bool v) { _returnMode = v; } bool returnMode() const { return _returnMode; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/spinboxFP.cpp000066400000000000000000000136571404105311000203070ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: spinboxFP.cpp,v 1.1.1.1 2003/10/27 18:55:03 wschweer Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include //#include //#include //#include #include "muse_math.h" #include "spinboxFP.h" namespace MusEGui { //--------------------------------------------------------- // SpinBoxFP //--------------------------------------------------------- SpinBoxFP::SpinBoxFP(QWidget* parent) //: QSpinBox(parent) : QDoubleSpinBox(parent) { //validator = new QDoubleValidator(this); //lineEdit()->setValidator(validator = new QDoubleValidator(this)); //validator->setNotation(QDoubleValidator::StandardNotation); //_decimals = 0; setDecimals(0); connect(this, SIGNAL(valueChanged(double)), SLOT(valueChange(double))); } SpinBoxFP::SpinBoxFP(int minValue, int maxValue, int step, QWidget* parent) //SpinBoxFP::SpinBoxFP(double minValue, double maxValue, double step, QWidget* parent) //: QSpinBox(parent) : QDoubleSpinBox(parent) { //validator = new QDoubleValidator(this); //lineEdit()->setValidator(validator = new QDoubleValidator(this)); //validator->setNotation(QDoubleValidator::StandardNotation); //_decimals = 0; QDoubleSpinBox::setDecimals(0); setRange(minValue, maxValue); setSingleStep(step); connect(this, SIGNAL(valueChanged(double)), SLOT(valueChange(double))); } //--------------------------------------------------------- // valueChange //--------------------------------------------------------- void SpinBoxFP::valueChange(double) { double div = exp10(decimals()); emit valueChanged(int(value() * div)); } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void SpinBoxFP::setValue(int val) { double div = exp10(decimals()); QDoubleSpinBox::setValue(double(val) / div ); } //--------------------------------------------------------- // intValue //--------------------------------------------------------- int SpinBoxFP::intValue() { double div = exp10(decimals()); return int(value() * div); } //--------------------------------------------------------- // setDecimals //--------------------------------------------------------- void SpinBoxFP::setDecimals(int val) { //_decimals = val; //updateDisplay(); //interpretText(); // TODO: Check - is this what we need? Will send out signals? //setValue(value()); // Try this. "setValue() will emit valueChanged() if the new value is different from the old one." QDoubleSpinBox::setDecimals(val); double step = 1.0 / exp10(val); setSingleStep(step); } /* //--------------------------------------------------------- // validate //--------------------------------------------------------- QValidator::State SpinBoxFP::validate(QString& input, int& pos) const { // Must set these dynamically as settings may have changed. validator->setRange(minimum(), maximum(), _decimals); QValidator::State s = validator->validate(input, pos); return s; } //--------------------------------------------------------- // mapValueToText //--------------------------------------------------------- QString SpinBoxFP::textFromValue(int value) const { if (_decimals) { QString s; int div = int(exp10(_decimals)); // printf("val %d, prec %d, div %d\n", value, _precision, div); s.sprintf("%d.%0*d", value/div, _decimals, value%div); //s.sprintf("%0*f", value, _decimals); return s; } return QSpinBox::textFromValue(value); } //--------------------------------------------------------- // mapTextToValue //--------------------------------------------------------- int SpinBoxFP::valueFromText(const QString& text) const { //QString qs = cleanText(); if (_decimals) { //const char* s = qs.toLatin1(); //const char* s = cleanText().toLatin1().data(); //int a, b; bool ok; double f = text.toDouble(&ok); //int n = sscanf(s, "%d.%d", &a, &b); //int n = sscanf(s, "%f", &f); //if (n != 2) { //if (n != 1) { if (!ok) { // *ok = false; //return 0; // TODO: Check - Hmm, no OK parameter. Why return 0? Let's try: // Keep returning the current value until something valid comes in... return value(); } //int div = int(exp10(_decimals)); double div = int(exp10(_decimals)); //return a * div + b; return (f * div); } return QSpinBox::valueFromText(text); } */ } // namespace MusEGui muse-4.0.0/src/muse/widgets/spinboxFP.h000066400000000000000000000043001404105311000177350ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: spinboxFP.h,v 1.1.1.1.2.1 2008/05/21 00:28:54 terminator356 Exp $ // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SPINBOXFP_H__ #define __SPINBOXFP_H__ //#include #include //class QValidator; //class QDoubleValidator; namespace MusEGui { //--------------------------------------------------------- // SpinBoxFP //--------------------------------------------------------- //class SpinBoxFP : public QSpinBox { class SpinBoxFP : public QDoubleSpinBox { Q_OBJECT //Q_PROPERTY( int decimals READ decimals WRITE setDecimals ) //int _decimals; //QDoubleValidator* validator; signals: void valueChanged(int); private slots: void valueChange(double); protected: //virtual QString textFromValue(int) const; //virtual int valueFromText(const QString&) const; //virtual QValidator::State validate(QString&, int&) const; public: SpinBoxFP(QWidget* parent=0); SpinBoxFP(int minValue, int maxValue, int step = 1, QWidget* parent=0); //SpinBoxFP(double minValue, double maxValue, double step = 1.0, QWidget* parent=0); void setValue(int val); int intValue(); void setDecimals(int); //int decimals() const { return _decimals; } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/swidget.cpp000066400000000000000000000025361404105311000200370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: swidget.cpp,v 1.1.1.1 2003/10/27 18:54:27 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "swidget.h" #include namespace MusEGui { //--------------------------------------------------------- // resizeEvent //--------------------------------------------------------- void SWidget::resizeEvent(QResizeEvent* ev) { QWidget::resizeEvent(ev); emit heightChanged(ev->size().height()); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/swidget.h000066400000000000000000000030261404105311000174770ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: swidget.h,v 1.1.1.1 2003/10/27 18:54:49 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SWIDGET_H__ #define __SWIDGET_H__ #include namespace MusEGui { //--------------------------------------------------------- // SWidget // a simple widget which emits a heighChanged signal // on received ResizeEvents //--------------------------------------------------------- class SWidget : public QWidget { Q_OBJECT virtual void resizeEvent(QResizeEvent*); signals: void heightChanged(int); public: SWidget(QWidget* parent) : QWidget(parent) {} }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/tempolabel.cpp000066400000000000000000000130401404105311000205050ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tempolabel.cpp,v 1.1.1.1 2003/10/27 18:54:29 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include "tempolabel.h" #include "globaldefs.h" namespace MusEGui { //--------------------------------------------------------- // TempoLabel //--------------------------------------------------------- TempoLabel::TempoLabel(QWidget* parent, const char* name) : QLabel(parent) { setObjectName(name); setFrameStyle(WinPanel | Sunken); setLineWidth(2); setMidLineWidth(3); _value = 1.0; setValue(0.0); setIndent(3); setMinimumSize(sizeHint()); } //--------------------------------------------------------- // setVal //--------------------------------------------------------- void TempoLabel::setValue(int val) { setValue(double(val/1000.0)); } void TempoLabel::setValue(double val) { if (val == _value) return; _value = val; QString s = QString("%1").arg(val, 3, 'f', 2); setText(s); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize TempoLabel::sizeHint() const { QFontMetrics fm(font()); int fw = 4; int h = fm.height() + fw * 2; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = 6 + fm.horizontalAdvance(QString("000.00")) + fw * 2; // 6=indent #else int w = 6 + fm.width(QString("000.00")) + fw * 2; // 6=indent #endif return QSize(w, h).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // TempoSpinBox //--------------------------------------------------------- TempoEdit::TempoEdit(QWidget* parent) : DoubleSpinBox(parent) { setSingleStep(1.0); _extern = false; // Be consistent with other tempo boxes such as the one in transport. setDecimals(2); setRange(MusECore::MIN_TEMPO_VAL, MusECore::MAX_TEMPO_VAL); curVal = -1.0; connect(this, SIGNAL(valueChanged(double)), SLOT(newValue(double))); } //--------------------------------------------------------- // sizeHint //--------------------------------------------------------- QSize TempoEdit::sizeHint() const { if(const QStyle* st = style()) { st = st->proxy(); QStyleOptionSpinBox option; option.initFrom(this); option.rect = rect(); option.state = QStyle::State_Active | QStyle::State_Enabled; const QRect b_rect = st->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxUp); QFontMetrics fm(font()); const int fw = st->pixelMetric(QStyle::PM_SpinBoxFrameWidth); int h = fm.height() + fw * 2; // Width() is obsolete. Qt >= 5.11 use horizontalAdvance(). #if QT_VERSION >= 0x050b00 int w = fw * 2 + b_rect.width() + fm.horizontalAdvance(QString("000.00")); #else int w = fw * 2 + b_rect.width() + fm.width(QString("000.00")); #endif return QSize(w, h).expandedTo(QApplication::globalStrut()); } return QSize(20, 20).expandedTo(QApplication::globalStrut()); } //--------------------------------------------------------- // tempoChanged //--------------------------------------------------------- void TempoEdit::newValue(double val) { if(_extern) return; if (val != curVal) { curVal = val; emit tempoChanged(curVal); } } //--------------------------------------------------------- // setValue //--------------------------------------------------------- void TempoEdit::setValue(double val) { if (val != curVal) { curVal = val; if(!_extern) { blockSignals(true); QDoubleSpinBox::setValue(val); blockSignals(false); } } } //--------------------------------------------------------- // setExternalMode //--------------------------------------------------------- void TempoEdit::setExternalMode(bool on) { if(_extern == on) return; _extern = on; if(_extern) { setEnabled(false); // Set the special text. setSpecialValueText(QString("extern")); // Force to minimum. blockSignals(true); QDoubleSpinBox::setValue(minimum()); blockSignals(false); } else { // Reset the special text. setSpecialValueText(QString()); // Restore. blockSignals(true); QDoubleSpinBox::setValue(curVal); blockSignals(false); setEnabled(true); } } } // namespace MusEGui muse-4.0.0/src/muse/widgets/tempolabel.h000066400000000000000000000041351404105311000201570ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: tempolabel.h,v 1.1.1.1 2003/10/27 18:55:05 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TEMPOLABEL_H__ #define __TEMPOLABEL_H__ #include #include "doublespinbox.h" namespace MusEGui { //--------------------------------------------------------- // TempoLabel //--------------------------------------------------------- class TempoLabel : public QLabel { Q_OBJECT double _value; protected: QSize sizeHint() const; public slots: void setValue(int); void setValue(double); public: TempoLabel(QWidget*, const char* name = 0); }; //--------------------------------------------------------- // TempoEdit //--------------------------------------------------------- class TempoEdit : public DoubleSpinBox { Q_OBJECT double curVal; // External sync mode. bool _extern; protected: QSize sizeHint() const; private slots: void newValue(double); public slots: void setValue(double); signals: void tempoChanged(double); public: TempoEdit(QWidget* parent = 0); //int tempo() const; void setExternalMode(bool on); }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/text_edit.cpp000066400000000000000000000043411404105311000203560ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // text_edit.cpp // (C) Copyright 2017 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "text_edit.h" // For debugging output: Uncomment the fprintf section. #define DEBUG_TEXT_EDIT(dev, format, args...) // fprintf(dev, format, ##args); namespace MusEGui { TextEdit::TextEdit(QWidget* parent, const char* name) : QPlainTextEdit(parent) { setObjectName(name); } TextEdit::TextEdit(const QString& txt, QWidget* parent, const char* name) : QPlainTextEdit(txt, parent) { setObjectName(name); } void TextEdit::mouseDoubleClickEvent(QMouseEvent* ev) { if(!hasFocus()) { // ev->accept(); setFocus(); // return; } ev->ignore(); QPlainTextEdit::mouseDoubleClickEvent(ev); } QSize TextEdit::sizeHint() const { const int w = QPlainTextEdit::sizeHint().width(); const int lines = document()->lineCount(); const int fh = fontMetrics().lineSpacing(); const int marg = contentsMargins().bottom() + contentsMargins().top() + //viewportMargins().bottom() + viewportMargins().top() + // Only in Qt 5.5 viewport()->contentsMargins().bottom() + viewport()->contentsMargins().top(); const int h = fh * lines + marg + 6; // Extra for some kind of voodoo added deep down, can't find it. return QSize(w, h); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/text_edit.h000066400000000000000000000035641404105311000200310ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // text_edit.h // (C) Copyright 2017 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __TEXT_EDIT_H__ #define __TEXT_EDIT_H__ #include class QMouseEvent; namespace MusEGui { //--------------------------------------------------------- // TextEdit //--------------------------------------------------------- class TextEdit : public QPlainTextEdit { Q_OBJECT private: bool _doubleClickFocus; protected: virtual void mouseDoubleClickEvent(QMouseEvent*); public: TextEdit(QWidget* parent = 0, const char* name = 0); TextEdit(const QString& txt, QWidget* parent = 0, const char* name = 0); bool doubleClickFocus() const { return _doubleClickFocus; } void setDoubleClickFocus(bool v) { _doubleClickFocus = v; } QString text() const { return toPlainText(); } virtual void setText(const QString& s) { setPlainText(s); } virtual QSize sizeHint() const; virtual QSize minimumSizeHint() const { return sizeHint(); } }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/vscale.cpp000066400000000000000000000027531404105311000176470ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: vscale.cpp,v 1.1.1.1 2003/10/27 18:54:41 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "vscale.h" #include #include namespace MusEGui { //--------------------------------------------------------- // paintEvent //--------------------------------------------------------- void VScale::paintEvent(QPaintEvent*) { int h = height(); int w = width(); QPainter p; p.begin(this); p.drawLine(w/2, h/4, w, h/4); p.drawLine(0, h/2, w, h/2); p.drawLine(w/2, (3*h)/4, w, (3*h)/4); p.end(); } } // namespace MusEGui muse-4.0.0/src/muse/widgets/vscale.h000066400000000000000000000026601404105311000173110ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: vscale.h,v 1.1.1.1.2.1 2008/01/19 13:33:47 wschweer Exp $ // (C) Copyright 1999 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __VSCALE_H__ #define __VSCALE_H__ #include class QPaintEvent; namespace MusEGui { //--------------------------------------------------------- // VScale //--------------------------------------------------------- class VScale : public QWidget { Q_OBJECT virtual void paintEvent(QPaintEvent*); public: VScale(QWidget* parent=0) : QWidget(parent) {setFixedWidth(18);} }; } // namespace MusEGui #endif muse-4.0.0/src/muse/widgets/widget_stack.cpp000066400000000000000000000121071404105311000210340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // widget_stack.cpp // (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "widget_stack.h" #include "scrollbar.h" #include #include namespace MusEGui { //--------------------------------------------------------- // WidgetStack //--------------------------------------------------------- WidgetStack::WidgetStack(QWidget* parent, const char* name, SizeHintMode sizeHintMode) : QWidget(parent), _sizeHintMode(sizeHintMode) { setObjectName(name); top = -1; } //--------------------------------------------------------- // raiseWidget //--------------------------------------------------------- void WidgetStack::raiseWidget(int idx) { if (top != -1) { if (stack[top]) stack[top]->hide(); } top = idx; if (idx == -1) return; int n = stack.size(); if (idx >= n) return; if (stack[idx]) { resizeStack(size()); stack[idx]->show(); } } //--------------------------------------------------------- // addWidget //--------------------------------------------------------- void WidgetStack::addWidget(QWidget* w, unsigned int n) { if (w) w->hide(); if (stack.size() <= n ) stack.push_back(w); else { stack[n] = w; resizeStack(size()); } } QWidget* WidgetStack::getWidget(unsigned int n) { if (stack.size() <= n ) return 0; return stack[n]; } //--------------------------------------------------------- // visibleWidget //--------------------------------------------------------- QWidget* WidgetStack::visibleWidget() const { if (top != -1) return stack[top]; return 0; } //--------------------------------------------------------- // minimumSizeHint //--------------------------------------------------------- QSize WidgetStack::minimumSizeHint() const { if (top == -1) return (QSize(0, 0)); QSize s(0,0); // Check if we want only the visible widget... if(sizeHintMode() == VisibleHint && stack[top]) { QSize ss = stack[top]->minimumSizeHint(); if (!ss.isValid()) { // fprintf(stderr, "WidgetStack::minimumSizeHint: minimumSizeHint invalid, getting minimumSize\n"); ss = stack[top]->minimumSize(); } // fprintf(stderr, "WidgetStack::minimumSizeHint w:%d h:%d\n", ss.width(), ss.height()); return ss; } for (unsigned int i = 0; i < stack.size(); ++i) { if (stack[i]) { QSize ss = stack[i]->minimumSizeHint(); if (!ss.isValid()) ss = stack[i]->minimumSize(); s = s.expandedTo(ss); } } return s; } //--------------------------------------------------------- // wheelEvent //--------------------------------------------------------- void WidgetStack::wheelEvent(QWheelEvent* ev) { emit redirectWheelEvent(ev); } void WidgetStack::resizeStack(const QSize& newSize) { if(QWidget* widget = visibleWidget()) { QSize wsz = widget->minimumSizeHint(); if(!wsz.isValid()) wsz = widget->minimumSize(); QSize sz(newSize); if(sz.width() < wsz.width()) sz.setWidth(wsz.width()); if(sz.height() < wsz.height()) sz.setHeight(wsz.height()); widget->resize(sz); } } void WidgetStack::resizeEvent(QResizeEvent* e) { e->ignore(); QWidget::resizeEvent(e); resizeStack(e->size()); } QSize WidgetStack::sizeHint() const { QSize s(0,0); // Check if we want only the visible widget... if(sizeHintMode() == VisibleHint) { if(top == -1 || !stack[top]) return s; QSize ss = stack[top]->sizeHint(); if(ss.isValid()) return ss; else return s; } for(unsigned int i = 0; i < stack.size(); ++i) { if(stack[i]) { QSize ss = stack[i]->sizeHint(); if(ss.isValid()) s = s.expandedTo(ss); } } return s; } } // namespace MusEGui muse-4.0.0/src/muse/widgets/widget_stack.h000066400000000000000000000047271404105311000205120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // widget_stack.h // (C) Copyright 1999 Werner Schweer (ws@seh.de) // (C) Copyright 2016 Tim E. Real (terminator356 on sourceforge) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __WIDGET_STACK_H__ #define __WIDGET_STACK_H__ #include class QWheelEvent; class QResizeEvent; namespace MusEGui { //--------------------------------------------------------- // WidgetStack //--------------------------------------------------------- class WidgetStack : public QWidget { Q_OBJECT public: // StackHint: Use the maximum of all the stack widgets. // VisibleHint: Use only the current visible widget. // If it is invalid, use the StackHint. enum SizeHintMode { StackHint=0, VisibleHint=1 }; private: SizeHintMode _sizeHintMode; std::vector stack; int top; void resizeStack(const QSize&); protected: virtual void wheelEvent(QWheelEvent* e); virtual void resizeEvent(QResizeEvent* e); signals: void redirectWheelEvent(QWheelEvent*); public: WidgetStack(QWidget* parent = 0, const char* name = 0, SizeHintMode sizeHintMode = VisibleHint); void raiseWidget(int idx); void addWidget(QWidget* w, unsigned int idx); QWidget* getWidget(unsigned int idx); QWidget* visibleWidget() const; int curIdx() const { return top; } virtual QSize minimumSizeHint() const; SizeHintMode sizeHintMode() const { return _sizeHintMode; } void setSizeHintMode(SizeHintMode mode) { _sizeHintMode = mode; update(); } QSize sizeHint() const; }; } // namespace MusEGui #endif muse-4.0.0/src/packaging/000077500000000000000000000000001404105311000151645ustar00rootroot00000000000000muse-4.0.0/src/packaging/CMakeLists.txt000066400000000000000000000033701404105311000177270ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= # # produce the .desktop file # configure_file ( org.musesequencer.Muse4.desktop.in ${PROJECT_BINARY_DIR}/packaging/org.musesequencer.Muse4.desktop ) # # install the appdata, desktop and mime files # install( FILES muse64x64.png DESTINATION ${SHARE_INSTALL_PREFIX}/icons/hicolor/64x64/apps/ RENAME muse.png ) install( FILES muse128x128.png DESTINATION ${SHARE_INSTALL_PREFIX}/icons/hicolor/128x128/apps/ RENAME muse.png ) install( FILES ${PROJECT_BINARY_DIR}/packaging/org.musesequencer.Muse4.desktop DESTINATION ${SHARE_INSTALL_PREFIX}/applications/ ) install( FILES muse.xml DESTINATION ${SHARE_INSTALL_PREFIX}/mime/packages/ ) install( FILES org.musesequencer.Muse4.appdata.xml DESTINATION ${SHARE_INSTALL_PREFIX}/metainfo/ ) muse-4.0.0/src/packaging/muse.xml000066400000000000000000000005551404105311000166640ustar00rootroot00000000000000 MusE file muse-4.0.0/src/packaging/muse128x128.png000066400000000000000000000604461404105311000175330ustar00rootroot00000000000000PNG  IHDR<q pHYsod IDATxԽٯ,I~ȽSu{l(CR2M%`[~_ CÔ!L4pzzz\bCV͓7{{8DeZ;wƱ(08kV;Voyy߸ެYnټ~S]m.۞~R[V-"V`*k;9ZARJ(){6^\'{zݦc Z}m׽}M3Ƽ6ϵM65./bcܯyH)[M3Y6Pw{smcƘmDuڞlKF6yW5^oc0O6dJ)Ճkc1F 7QYOsy9{7;u|s`fסLruzsNX&mn>}}I+w/䶆}k-Z4 XM.2P:@Vo[r&PMm| Vk@bnߞR?^'"uO[ 64Xe\J]VԱ7kA1oos666ۨUs-w6n*M0ߛ,KaMQ  `9z}\={k7&]}ƌ$_6KnhkHq,A`knLzg7'mްzׁ"Dƹ X zQs2ËXˍo{~=n2Ma]N$;rBOgfls^c@+l HApn)\'Ym :%mB}f<cFMŔ"-E2IRo[SPoN׉H wPfM@;Uϭd4&Ͽ\<ê yK>;junb9i_LNSv]RqDzsع(flrzW?~u 0Tz[oXNmۈFsCcVdw)ޥT vdzz?..[XmZ^a!6!}z;ot|XM-U`q7eۜo kȦd aM9?/9=]qcz:l>G]1xm vmޚ:yrlRs#_!P?@"-34! @l?.1x% \u_JYu|oBo+iua _ 9" jNbv㗟qCCjctw ڤ59`EyVp皓yXB`ª8ŋ:#k IV\?OLJSi qwD(4VɽAJ\ܵm,,.: ȶKnrWd/!A2 PX½CXͰ(զ9tflgR5 MR" A‚ŜtqO8~_+.rVYHf O?ޓBHYꮶ5kkk5` QRGj(Q|mqUi6X|>gz|x+SHiQ%5&S,R81=GD2AY\M>c}|߿%֩Y ,.q;d (ZbuF:?gr9>9zH-%ï?'cz#g ܻk qmMpc_M/`mwxS`hʮ?=˿b4b/9 ?)lPYs!VSsL iҚ2Ap4ZթX}*%%dz )izXzOy_rv8; 5|_5qrtG&BJ<(V]j&C)ut]olMKj7WW=M6TN-iBH7ߡt *']Ϯ&WR xBy @ T 䊰0t%6xRsESVn<&,A_W($.3MлXQRցX`\\TXcZG,&_{ z;H/XF,&G/>s2 |NpL/ [-Ҍ0Z*Mq}v7cڱm)%aVyNo<袔tXam G'?_-j9er5^7Dtb QӚМq%3R\5{ ;@QJUAZ>ՒЃ$m0BEIqF39'q9#ogg_  E ֊TLM`M?h]sEJYUgu.9V4Gm6`oWМ Nh mjj֚]ՂbQ0֔¸VX1v=onD0d]l(ԊteH/Vad:h*%ǽ>uZߚ@M5e_.5TUX V|/ɳ%ŧ?/^0$"@ٌ0-d*Tp2n =I ( RiJX#63b/ xa 2"|pssF_ ^l:azqVuY X. (*Ͱi*r߱:ꄡ(j,L(.r| 5vV3V tNJT9 -HO =0 (I:?%N$-Q#|W|sga܋ctFlw2~}A_z 6QZhM`,6&Y\}}azVU.._2|Oe]>Ewx0Qt16[ <#Sބ %R44:+lCMU'Φ]k;] pKP-/%Ӌ#W/Y̮Y,+4'rhcZ0 €'6.?G1?_},>0O> !gsJK/4tjV$؉o1|K Ȉ:>9YaY9aeפiFb0 Zr hkѱ%PRyl J1?ϲ~x'>D$m=~Khkr$%lnn>q}}pC1>#3iQs"aD+MwI$-\Ŕ^mցyS0XRsՄk.Ϟs'qq1e &Ybȓ8s%ۤib MS瑥ӫK)2)Sn [Lkmp$k@x^/m,Zvޥ7'?`vu/j5CC9R!nk-[[[}<' "q\. g\"A`Kd,6kˠ àXg[m&6AVtב}R 3ɳ%F2ʎƖJr!~jZ˷m~2NFkmRq}}֠L܎Y;sC}v0o0FHAgs.Nϙ\ϙ3V!=7F$Ң 1>\\\e,PJU!aX. )`\0[ ˨x dڢt[EJ(!Zm7MNJe kՅ-z.Hsx$N":ZR)ǏkhmRlmmX,PJ>Nw=KruY]^\dw.w3` Î(\J Kɴ1x9ڤdyfUYU:G%eb,,WcX aOؿCf*Q/;Uu!$J)ZCCeїeDe c ٬zjXr$RH euh#`^Ͻ{Ju-axCFH*Cmd׷]׼<|BKd3_j U( A#['jbJcf\=# C|9*%QIu]  ]gNr1iQ> \8ɲnx|#Vl~[Zem|FiT6v~kh$s6SdʸX<>U#I)Z0KW8.cդ!Q1ɲ(}$$I~1d|^OEv. <5*/ OmO3<<)ЦH. Am7tLQI߅5 pmuo{UeҰH Zv,ll|6J)%.4*^WgU'A@BW-T:dYPx&gkj0Zθ~TN'>ȵm{9u&jT߿Zm&NnR7LJjhyq -bL:З:!шvwwg, vvv(jU%AY`=eSBv8ѱ0 + qEQ,$o;ǰNA /0 ;݀~%Ep8Zd2!Ʉ~O$UKT+D;Ty^vp-fQw΁=R0ך^}Ύ/x_\矞q=_Q+"2U3u\qJE3g6.27 M^]T-c`=8"x|Y:f{qְK."y*Ҫ?=^@):؄(eQZ s3\c,+^kY\2/l&zpK:st^,@Ju#G]0BH!X,L&v9==%svvvBTh'Sճ8___3*j(XgPomiǬҔlb;/@3EJ/wNw5ZI5WGCbeBguޛ2o|1g43Я _4thR@^|IBd=_T|^QVh%rR)ggg<~T墁7,&"vwwB0Nd2a41 ūW̧ꊞ7ﱘ) TP0[(rM*X$Av#%]TLł`@ELӊnmmUtHkA ] IDAT84` <t(l"]-QEV)VXbdMvMm@6!y (NrV2[ H"/J{D>Q֬s-)U$IU\Tyzʀ, 1@X("aPF#0d6UGQsGbf&bB^d(ʠ)X- Ea`h*U2JI >+/Z#]PoƺMmĠMw׽[)ev5RЍ=_"X睘_6ږi''Rٌ4M^d2ZK۽!kAPntI~t{{{haF<$I*jvX^YΧL./錻xq@\2V 4(EU2ܬV1as4Ck;ֻn⻎<6W̚-Z ҃nKv1^sj Ð{iepq'q\Ǣ|'2|ߧ0tfYy Ê r5łҕTv3g iihVk"Ï%KeB^SDrRe–QZB ^{rBXܦ̛۸F8m+וN@KI6xBx@2>Z $s9ߧ( iE 9qWαxRJB{"MS!YU%: i Q'1 qc%YD̸ѶTdV2CkSmvxy M4)X_\V~r52>RfBrEJI$RC5ł~_Q&Dci.=":ł( 5[[[iZeZaӡ\.ZW21`jU1]a7IxgD-MƍhP(4y(/Ybh Z^3 d%WMo;um>))$}YڱD}R_1^TVx"Ky`2T YXvl6drR-%ϓt$ 8="-}TgoOspk`=2s#άNVSn4nRcmM@E6r+}jH ~PR8! %6/_C$0MS.Qy%߻w,8??goot{{$I*3K<`P7L&dYNHtJD.Ȧ=!H<, *0`Y /T((z>͚MYǰ>ysy6{<ʻ%!=:ȭ5āG-!} iAT$1lmm*C^* I8|$IrxRJPJU!4U.Kdz~g\'}ز\,K1[UhMٖ$/3]{=|(TZ5hn!D"ׂ~9vuf}o:0!C `-?a2đJ|6`ԫx<rQZ* qYVxXTp` _}lWgөXh4""x%ݞdg9P(Ujڬef#zX~gDhn`4]L&КEhYxoۮkKX\6rcK9#<t%Z6}}G1NL&9ܻw)%^(@ٟ1_`@ EQP(CW)ЇEx!cDU+}c!۶ڨԦE:3T&Ҷ9VMU`txW٩+K]'C@6a(ܠ&ϋ5߶w[>MӊzYk+紓 2 3N\]]Un1Gɜ=˱Bv75G,xt@[)@XZ,@{Ƞ6ߔЦF\&UM!mo aڻwg}*: vsu"H[iuR+Ϯ.Y./mabJʫ-F-2]=}d8hU-TS[|z]<@U?0Mz3 Mlߧ;+?a?GI(xgW.1"866xv9ժHpWWW}kkTɯg6+? lO~Cze |[T*pUwsKjm :cxQ+J6MD\mjt&9bwcRdH2y|JϬzgx)c52쬊ǬV+NOOo1m.//Zc& Ʉ윝l_ş7FkbaY R"G2+TQȶi 7S6M L;u"H7Vmuu۵Xx1ѽAQJ$Acvq1畓fT]BTYnu?9t:( >}1ϟW 䖋*\,-coo<˸8;#CBCy0awi"_g۔@î= n֩T]+*(QڨUݼ7:ujZcڤmߦݽO]jB0ڧ@ ayru~/?GWrit݊rug^pȥiq;99|^ ڲ4{;-*/$e2\%5Cl(빁 شdWu@Jһ Mv@?7!Hz _R t>a28oOȳo:kν^mX,$I`0}NOO4<ϙL&|W9bru:c+&e*TJXr]h>Cѭ2U'v5mUMj TMGtթU嶱fi]}u>U`H|n0"ϖryzLo0Wy{fkx-ɲ^W$cABT ,r!6`r}EZ!Mc-Wuֵxu jdVz]&-u'R5Y`Pw wc݆6?_MyC|=΀PXeQEn>өL:v,x)ysxxjb0S^x!ٌ5!p%?[;]..$ UW$_'LC1R T:jj䪶Z5#&{5yhn{Fv?%NXD4zf8$=/>g~u]7 ðBT !8==%2=zD\pr|%LJz2Ek B.?E[*ȲٔFM6viˣuJ\ 톶R6zx&֯1[;< Unjq 9ch|ys~~1c)88jlxa6oUH%t-G$hUنդFS6& Mmuv׵}7Htх&gA3\Gۿ&]-;[qROzz=={l6#"O>B4VLKgGtQ`؇"-ЙA ʬ )zY6"/ 4eeYT[E6?N rRsK R@-J6ws<@[R(C 'X-Wz k[`$I9P.:=MS*cy\2Ld'AR*Y 0)#p_]Ll~_w4;5qo;unUw |M6te]_&Bl{[-?ZF# (W/9?zⴲI)KGuCU#gggUd2R0r~)9|8|QN,X-V ת-"HHhc+b7eܴ +m9V\U7(.ԅ .c3;w9.uVݽ㭳&S:4Pxo>h7 f:[ ~ӗ?x>JZ䗿='_2[ য়?<`c0\zNw~nEe+rN/2y|?avu| kbU'm9u`m#7XE'e)Ը[_~==߁iBB}fLJ0J%\K&?&㝇DIXMdYۋ960+3'56`ZKбҮ>4 R>7? VJTx wHv1uLY^XnۢArTQŽ 6ñjhK^&l@?ls;ryQ~`|M:\C VpB.wCtT3tAZUNjS.1F>..O8zG/{oisSj|R)V՗2\2G[hT nLl.N1z~~VivIN^}WQ@$N7ޣlXB` Hkyj2vD/Ql DQ\1% O?xoG kM}(=Y:HX6좭^=)PcGfX2Y9PmܛNY2w.)N 2l G|/`>A#`'12L^8E9ȳ%'? *A7=tr>e4V>zQsBão#$Yʏ"I!l#425a5Ք_[B3 \?~5Qk,T[½v&c:Ct=ۿOx?F>yVxl?M~CCls҄ڰzuʧǗx<}a#Y\;]*a5*K,6j흽$ER"Ŷ(bmEV؆3Pb  A  6)ΟHcƐEDIqzCv7%%4nsN{:UEtal"5H>#簓J$pȤuw <:Jf~l4P$+fТEL_ccF/S475.Z:p8]zTRyD> X$@*#Γ,h. ݍ X /&Xd3dPJYjfDA$o|92 )W;#tu:!vlr,\XbizkOQ*$ͭ8vہh@ ,(ERTb "xdZ'o$3vb6%/JY0Td%5q-eE[vO~QAfaUm+CPܺ؊ IDATa%vq-vT+oZKhN{CM#1(9ztݛ6 2C(2Q榮8;AO:ǖLp,4nm]u=Do7Y}\#7qQm}۱ S7\R1GyBdt;7,YZLyJ/-ԽXb0[AcdUhQ7KAo5FkQfLb4rYT0c*.TR۹1 sFIbذrٗq:4Bu} 4m_"'Ƶ.XHM)g'(\M|yt| Eit[\lmĜ93ARU&R՟ fɊ}戅|- yjj(6ʹӿLR\.-XÉ j ɷI9ܠ)Ҥqݵ~ ,wn0;< QⰐcwemEؕ(zATP`2s1ʠVAPB<ⰒfVspձl 9m<#2O*&8NP:z|c%ppxqx:sqr)n`g}iҩg_g I"MѻHcS P3nRy"($H2S};0<9Z;fc^Ń3mrAtN[&.F6@< V܊<4 6nN?P/46vDIR$67J>wwRy|&C. ZԚhi{+" ܹ\*E!W /t#U|DT"_Kd%Rsi;1{ O;X2*i9>y?5Ί9a.3 Skx-*ʪĉC-ܾK3#DWfﮠT|.G.&ؼ׌'ą e+S:[w#9b<˙<\xH<_$U()Ap{Vj42K=՟A^P׎|˕ ΄"l CBD2#)P*%7R3̦$1 ǹ9>m468 Ke 's̥ ,g rEElroskS52c&L̦2,V4$p>䅶_j`&4f(5жCx!\Rs> %\ ]e&m,`SV_ʴ3P/$ܹ 4Bɱ2u&R)4GDŕ4p5V`ڞmXivJv7vr]_CCC1`IM)6['3:ucBD_mHa+ZJTscb@c3.= s,& drE t4xNS,8|>?PnRnÁM@"2>UZ_Fs-% V_ץ 10+?5N'--ʓԩS|ɧ,ŧ) 8 픷/܌rVr{Ceڶ|jk5J?Q/ft6%LM^ Xy(k=f[{W9yzijj2R`vʛuc PقR5XUVK Eft_I< $-Pyhb1o,t#>\؝E2#;L&Cf,5yO %odzjn+~;.UhnWW㪧:c/!pՈ.Iul-<#CA7[`lZGJ]Yk W&B ^:Su5e6+"eEH1ⷼ]D$jP^vRD 52-dz{cdkU~%Gf4#Ľc.¨V3]P23r9N UR+HZb4uENzBT&[M,ZUж*ZV5c"՛P\qUƪf˱,u*2jL.uݱn5f*? XLUzUF= $ +%K+9=JbY%dGuU0CouM.sȕkIz\!>T[2j%" G1}nxķ`՘!{VWN+3X]xBF]e$ՌgUӨxvV][Jdq1 !3cbtT֘Cw^l8HM3ckVDW#Ģ1B9UQȻ&3,TIzOT~-۩V*k-FS贈yV-hfD_dr]ٶR ŷ*$bVC4hJ%ԪZcܪ7+G{eDQn,HUjb͘LUIZ-t`MnQی("xz3\݅^5k[,*s&&1L mK䘙lr fr~ }U'@VVLt]痿%DQRH_lڴ MӸx"1F@x<fΝڵNX;wt.]bjj%hvǃ#anD<+FTqO^"*2>YT^eDQ(ccc\xQ'MKl6cP(DSSׯ'yfn݊ᨸ ,FFF8{,XbH4eeeFGGʕ+\zɤ@ PN7֭[ܼy]vDx … |'\r nzinnׯs v֭[YnDz*JCT5' ~\*!mrS-eb///d2ttt/;pyR*#Fxd2ܼyh4޽{uH X-_7Vr@T[ʥF_ v'O_Qx|P/ϳ===ttt`kjS$ N:E,#L766⹛I)sѐL&C,#Jd… E֯_O(W_,..icr*: 8S0LH$(R)Ν;<رP*dnjf+3նE2sj[dqxq:3C^Yv AhB4%R(cffA__9cYV癚2=B۷o駟fʹ  b& fff>`xx14Mcii>`0H … Rp8?̑#Gشi|>Cd2gxxK.144D:6;>>~3N8AkkUz߲Od gUGW'yN>ȈO!YDNz{{ٲe .\`pp .bUE68pCoDss3` `RqF6o޽{裏|2hK.qm"q+UiU"1333@Y5vww{noec\rx d'h,͛y嗙X,r5_n>m6nѣGwK{{7U7ttt000@$i(q-no:t ]vDػw/[nqUJa.dZ&3Z]X,惀M6o}gy8[QB!c˖->}x0lQc@J]]]<|  k39>#9rIN:eʕ+tvv߿*Ԋ@5ekxX,fw8444H$*N']k?~ '9sw}7|iCZ9N^*%&"\@*?ҥK?~~*Rcߦ裏 fQ7`$ V\7cXV-w8_b+7 pS@aSIoE 1z UrBhYC-SW~:?@4z{{D"AnP(Ǚ4e>3hkk8Bf~:o|~^3h===< \hU!Akk!n5 @ss3F!zrV\^*|>cXV5c{ׯ344DXdSz-b--- z}O&L{n4MϐALa`xx(Kⅅ.^.\@kk+^s |T*0/^dyy٨cnu>PhL;裏cJ5}ܲe ^1FGG52:2R ^/===ڵݻwW ] гrZܠz*eV \wOs177G"jn' ~z{1=s=g1ijj"p 8s W^e|| L nZP"6l;سg;w4Vpy&''f B$ bXveԒb\|D"avO$UOoS2\gϞevvd2墭Çjg.Ν;ϳH*"V /thnn&ى6eY&''e~~ޠa=b%Ngg>"t:]Ӡ6P+[,{|>hiiYfK?|xc+OIDAT-v\ tuBSU,jU@}ڃzhk X=[Ur{ڍMA6VʬV}f}-5kյzXc r5fu~p[٬^ y{Vo.k%v͠= WjZp z7vDdq2]LIENDB`muse-4.0.0/src/packaging/muse64x64.png000066400000000000000000000165051404105311000173660ustar00rootroot00000000000000PNG  IHDR@@iqbKGDC pHYs  ~tIME9]IDATxݛiWyroojՒ}_,ْ,[^}mLp`C Ilx ! cH  -ْe[^n[nU}>ld#-Y_ߺu_{:4io(6gο#~wFy+6`e[F}.7w'Xc( +%GvsWc%l_]ߊ ͮ{m㵛^v#xc3Вp:v\ GF29>yՏ|_ޜ62⎟~:[:e/yu>]#l<`;ֿ+[ M'j%W|n {Ol9:jYp rAl^+iG߻-\wĚr?FU$$ X!6&NZOa~¸vkV{m#sH-2#_rY/ܐ/u&1Dq}uc,%M EB+|A|j] jC zm>m6%R $BLw01Ι4gB踚i{hɞGɘDum!gƒ n//%{m8_μY,La0*8;7r(khHK!! =bV!0 $r=rÁC6FM8HJ lC͒߹nni!u<3n@աNt^W;O'&6B@@y0ek;7((C0C,A:W@n,fvfqtڌ?G 8jũ M6NK+{ տ^d[c}(cߥhZP8c vmuxOU* t91 3l9`JVp!XXA2@:EuMh3 ѹs )GdG[[~6<-ߺy3XHS'cN,CQ]owRx ^Ydqֵ2W0`yسmsWQ 0sRB<1lM?Ovu mhjt%6<+zS3h 7qb{/-/R }Z*Jk⥷\vI:>DTP>2ܫm-i[g76џi/~`cS=ǷBXSyBU=B)sSWvupo`o*%UVj: x])$OROqE |Ӏkm!He{ɀsiq>%(A;0[ }pfv_QHo,撺 E)"Ŵ70U|Xɾ%]b04J`֮]vJKS&k(BB$56/hc#p֩Bss(z T(r۶OSSMBa`ܹt:=ѫ]WWwCssooU4P1qEg0H"[4~ʠj]W)n}}=emlmma玷5W}druM $"HLihcg`zcSk(8(!ȥg8W~> `vjB(J; R)eӭH$WIZ\~ϞVPSSsoQZl ta.3RV.nY%&Q$*\A( 0Ͽo5,V2I(01Y=00:H\b<[244&2f*-2tg1e=f/<|>, ]Dbt\.7G)X,^[-˚y\X )ɩu RpIb40TQȧ\<#M)bT*=foD"qs>O)U<W =Jn]ŖT*AL9~xEJN4Z6-Vf V8!Ja :j(9ߧA~95G¶ ˲FYD2m d] @K]Bqi8p@H)YCC V*XuJeoMMԦ .>rHcն cfҥp{Puq1=QƘh"T*)HRTQM/ߌr1>rT4@dYֈ:fHb1FkM -rr&YH& T J)w (ZhQR[ZZ]]]G}*?T*oZ`Aȑ#fآ:'J5|~Z:mz54 Jfa)BWwSe_;8mz_,Y#ssɢem`[\6픟xJ tcJZk1*ClM]9r|_OOOɑRy%(z41*B} ۶AMs`m$ª8<"(*G 2Z+QuA ),ORT*U_8aW8,91BA;maYQȆaH*Nk0u]B ᜷iWs/#B,'(J N'NB<IԼIxL" PBEQ-ggR+F]m AwO0L? Zs)\uc8ϤR1oض=ӟL&sl^,\ ZB5RB;#et$Mݴ-L^JU5J)Ƙ10cl_kL @c\JbE  U q!8Wo @Ji(!@S! ͨ/?Q6ߪ[ R/bX,v뺿,NBv۶'ٶ!*c6qf*;; +D\EJCxmu[#`iWJBs!0cJ5L'O,2ƞ۪sTk={ݲaqgcleY9k!W !ɿX *F i.|BU7lshm+e|u* c(#+NwOk}\)u ,Z(mNcc1樔ra}JcD`' 8'Jk"DOs+۽}{RiQ) !9M{Wwdr1&d0YqrQq&hrm۞ 0cبYL)]PCI fSo=sZJh(5S8_5qE)sT:Hܠv(zsRUJMVk !WqiYRʧǹV|uEsԐ0@  `lgWxc~/RnN!ugPSJkK)oR>L&*cqf;RS:>F U|RuZCUh!z[ {PF5JWRB"p((t]w1Aptc۶]!Rǹ&J=V̤|q PF#:-ƶ~ٙnw^ yn*DAqi(cEǥ4#JqJM5!fBeY"WN9B3Cߧ7ȑjć1O)!Ҝ n\kr;c0tj(V(6'IƘ,\o{s@u(sBs?RǞ}r„ ,zD"~ʔ)u+ї]>C]ڈ\Bk0Ç܆>뗽tN,9ѐ%6PD[c@kFN?{n^1XgφBFE;:::u'[3vbV<Z1m 8+\@Ju+۽9-rhm;n6D6Y!¥T-jGOi^6gF=&O\gm6֌1NB#7>L߶uN`17-ڔ^K-RpLJj&`-P$'%-߻ck|ȎrP2J2tz'O7TZ v3{Mis%PB ,Ka[a]±fV4}ن6hOPهΎ+brO|= Σl"$F`` `Y܊ڶD1yzg hhB܃ûc=CE?uaΣ8y;DR@èԒhthY|e_Fӻo(xw[oo&xSU^`ZJ ~0Δ6Zò,Jg:q턐ݻw z>0c7`+^#:AĚIENDB`muse-4.0.0/src/packaging/muse_mdk.spec000077500000000000000000000205441404105311000176540ustar00rootroot00000000000000%define name muse %define museversion 0.7.0pre3 %define release 1thac %define alsa_version 0.9.8 %define jackit_version 0.93.11 %define libjack0_version 0.93.11 %define libsndfile1_version 1.0.5 %define ladspa_version 1.12 %define glib2_0_version 2.2.3 %define qtversion 3.2 %define dssslver 1.78 %define musever 0.7.0pre3 %define musedir 0.7.0pre3 %define capver 1.0 %define fluidsynth_version 1.0.3 %define graphviz_version 1.9.0 %define jade_version 1.3.1 %define doxy_version 1.2.17 %define ladccaver 0.4.0 %define major 0 %define libname %mklibname %name %major Name: %{name} Summary: MusE is a MIDI/Audio sequencer with recording and editing capabilities. Version: %{museversion} Release: %{release} URL: http://muse.sourceforge.net/ Source0: %{name}-%{musever}.tar.bz2 Source1: %{name}-ardour-tutorial.tar.bz2 Group: Sound BuildRoot: %{_tmppath}/%{name}-buildroot License: GPL Requires: libqt3 >= %{qtversion} Requires: libsndfile1 >= %{libsndfile1_version} Requires: libalsa2 >= %{alsa_version} Requires: jackit >= %{jackit_version} Requires: libjack0 >= %{libjack0_version} BuildRequires: graphviz >= %{graphviz_version} BuildRequires: libgraphviz7 >= %{graphviz_version} BuildRequires: openjade >= %{jade_version} BuildRequires: doxygen >= %{doxy_version} BuildRequires: libfluidsynth1 >= %{fluidsynth_version} BuildRequires: libfluidsynth1-devel >= %{fluidsynth_version} BuildRequires: fluidsynth >= %{fluidsynth_version} BuildRequires: libalsa2-devel >= %{alsa_version} BuildRequires: docbook-style-dsssl >= %{dssslver} BuildRequires: libjack0-devel >= %{libjack0_version} BuildRequires: libsndfile1-devel >= %{libsndfile1_version} BuildRequires: libgraphviz7-devel >= %{graphviz_version} BuildRequires: libcap1-devel >= %{capver} BuildRequires: ladcca >= %{ladccaver} BuildRequires: docbook-dtd41-sgml BuildRequires: libext2fs2-devel %description MusE is a MIDI/Audio sequencer with recording and editing capabilities. Some Highlights: * standard midifile (smf) import-/export * organizes songs in tracks and parts which you can arrange with the part editor * midi editors: pianoroll, drum, list, controller * score editor with high quality postscript printer output * realtime: editing while playing * unlimited number of open editors * unlimited undo/redo * realtime and step-recording * multiple midi devices * unlimited number of tracks * audio playback/recording * Sync to external devices: MTC/MMC, Midi Clock, Master/Slave (currently only partial implemented) * LADSPA host * ALSA and JACK audio driver * uses raw midi devices (ALSA, OSS & serial ports) * XML project file * project file contains complete app state (session data) * Application spanning Cut/Paste Drag/Drop * uses C++, QT2 GUI Library, STL * GPL Licenced %package -n %libname Summary: Main libraries for %name. Group: System/Libraries %description -n %{libname} This package contains the dynamic library of plugins from %name. %package -n %{libname}-static Summary: Static libraries for %name plugins. Group: System/Libraries %description -n %{libname}-static This package contains the dynamic library of plugins from %name. Group: Development/C++ %package -n givertcap Summary: Give applications real-time capabilities Version: %{capver} Group: Sound URL: http://www.tml.hut.fi/~tilmonen/givertcap/ %description -n givertcap Givertcap is a small Linux application that is used to give other application real-time capabilities. With the aid of givertcap you can run real-time applications (audio and video -processing apps for example) with high priority without running the application as root. %prep rm -rf $RPM_BUILD_ROOT %setup -n %{name}-%{musever} -a 1 #perl -p -i -e 's|version="3.2.3"|version="3.1"||g' widgets/*.ui %build export QTDIR=/usr/lib/qt3 export KDEDIR=%_prefix export LD_LIBRARY_PATH=$QTDIR/lib:$KDEDIR/lib:$LD_LIBRARY_PATH export PATH=$QTDIR/bin:$KDEDIR/bin:$PATH %configure2_5x --prefix=%{_prefix} --datadir=%{_datadir} --libdir=%{_libdir} --with-jack \ --disable-suid-install --disable-suid-build --enable-patchbay --disable-rtcap \ --with-docbook-stylesheets=/usr/share/sgml/docbook/dsssl-stylesheets-%{dssslver} \ --enable-arch=i586 --enable-laddca # --disable-static --enable-shared --disable-qttest %make %install rm -rf $RPM_BUILD_ROOT %makeinstall mkdir -p $RPM_BUILD_ROOT%_menudir # (mandrake) menu support cat << EOF > %{buildroot}%{_menudir}/%{name} ?package(%{name}): longtitle="MusE audio editor" \ command="/usr/bin/muse" title="MusE audio editor" needs="x11" \ section="Multimedia/Sound" \ icon="sound_section.png" EOF %post %update_menus /sbin/ldconfig %postun %clean_menus /sbin/ldconfig %post -n %{libname} -p /sbin/ldconfig %postun -n %{libname} -p /sbin/ldconfig %clean rm -rf $RPM_BUILD_ROOT %files %defattr(-,root,root,0755) %doc AUTHORS COPYING INSTALL ChangeLog NEWS README* SECURITY %doc html %{_bindir}/muse %{_bindir}/grepmidi %{_libdir}/%name %{_datadir}/%name %{_menudir}/* %files -n %{libname} %defattr(-,root,root) %{_libdir}/%name/plugins/*.so* %{_libdir}/%name/synthi/*.so* %files -n %{libname}-static %defattr(-,root,root) %{_libdir}/%name/plugins/*.la %{_libdir}/%name/plugins/*.a %{_libdir}/%name/synthi/*.a %{_libdir}/%name/synthi/*.la #%files -n givertcap #%attr(4755, root, root) #%{_bindir}/givertcap %changelog * Thu May 27 2004 Torbjorn Turpeinen 0.7.0pre3-1thac - Updated to latest version * Wed May 05 2004 Torbjorn Turpeinen 0.7.0pre2-1thac - Updated to latest version * Tue Apr 20 2004 Torbjorn Turpeinen 0.7.0pre1-1thac - Updated to latest version * Tue Apr 20 2004 Torbjorn Turpeinen 0.6.3-2thac - Rebuilt against Mandrake 10.0 official * Mon Jan 12 2004 Torbjorn Turpeinen 0.6.3-1thac - Updated to latest version * Mon Nov 17 2003 Torbjorn Turpeinen 0.6.2-2thac - Rebuilt for jackit-0.9.0 * Fri Nov 07 2003 Torbjorn Turpeinen 0.6.2-1thac - Rebuilt for Mandrake 9.2 * Thu Oct 30 2003 Torbjorn Turpeinen 0.6.1-1thac - Rebuilt for Mandrake 9.2 * Tue Sep 09 2003 Torbjorn Turpeinen 0.6.1-1thac - Added muse-softsynth-fix on muse 0.6.1, it results in a dead lock when muse starts a softsynth without RT. the patch is an adhoc fix for this problem. - Added jackit 0.80.0 type patch - Changed naming to differ from Mandrake cooker * Fri Jul 25 2003 Torbjorn Turpeinen 0.6.1-1mdk - Updated to 0.6.1 final built against fluidsynth-1.0.2 - Built with qt-3.1.2 patch * Sat May 17 2003 Torbjorn Turpeinen 0.6.0-1mdk - Updated to 0.6.0 final built against fluidsynth-1.0.1 * Sat May 03 2003 Torbjorn Turpeinen 0.6.0pre8.3mdk - Rebuilt with rpmmacro patch that removes nvidia dependency. * Tue Apr 22 2003 Torbjorn Turpeinen 0.6.0pre8.2mdk - Rebuilt against latest version of jackit 0.67.2 * Fri Apr 04 2003 Torbjorn Turpeinen 0.6.0pre8.1mdk - Updated to latest version * Wed Mar 26 2003 Torbjorn Turpeinen 0.6.0pre7.2mdk - Rebuilt for Mandrake 9.1 * Sun Mar 23 2003 Torbjorn Turpeinen 0.6.0pre7.2mdk - Updated to latest version - Compiled against jackit-0.62.0 and iiwusynth-0.2.5 * Sun Feb 16 2003 Torbjorn Turpeinen 0.6.0pre5.4mdk - Recompiled against jackit-0.51.0. * Fri Feb 14 2003 Torbjorn Turpeinen 0.6.0pre5.3mdk - Recompiled against libsndfile-1.0.4. * Sat Feb 08 2003 Torbjorn Turpeinen 0.6.0pre5.2mdk - Recompiled against jack-0.44,graphviz and i586 optimized. * Wed Dec 11 2002 Torbjorn Turpeinen 0.6.0pre5.1mdk - Removed jack dependency again * Mon Dec 09 2002 Torbjorn Turpeinen 0.6.0pre5mdk - Rebuilt for Mandrake 9.0 * Fri Nov 15 2002 Lenny Cartier 0.6.0-0.pre3.2mdk - remove jack dependency * Wed Nov 13 2002 Lenny Cartier 0.6.0-0.pre3.1mdk - 0.6.0pre3 - clean filelist - disable-suid-build for now - from James Gregory : - fixed file locations to use macros rather than absolute paths * Thu Oct 3 2002 James Gregory 0.6.0-0.pre2.2mdk - First version to build. muse-4.0.0/src/packaging/org.musesequencer.Muse4.appdata.xml000066400000000000000000000036171404105311000237740ustar00rootroot00000000000000 org.musesequencer.Muse4 CC0-1.0 GPL-2.0+ MusE Digital Audio Workstation MusE none

MusE is a MIDI/Audio sequencer with recording and editing capabilities. It can perform audio effects like chorus/flanger in real-time via LASH and it supports Jack and ALSA interfaces. MusE aims to be a complete multitrack virtual studio for Linux.

org.musesequencer.Muse4.desktop https://muse-sequencer.github.io/img/arranger_new.png https://muse-sequencer.github.io/img/midi.png https://muse-sequencer.github.io/img/drum.png https://muse-sequencer.github.io/img/wave.png https://muse-sequencer.github.io/img/mixer.png https://muse-sequencer.github.io/ muse4

Apart from the usual bunch of bug fixes and improvements the most prominent new feature is the new tabbed UI with docks! By default now with a dark theme.

muse-4.0.0/src/packaging/org.musesequencer.Muse4.desktop.in000066400000000000000000000005551404105311000236370ustar00rootroot00000000000000[Desktop Entry] Name=MusE GenericName=Digital Audio Workstation GenericName[fr]=Séquenceur audio/MIDI Comment=A music editor for Linux Comment[fr]=Éditeur MIDI de musique Icon=muse Type=Application Exec=${MusE_EXEC_NAME} Terminal=false Categories=Sequencer;Midi;X-Jack;X-Sequencers;X-MIDI;Audio;AudioVideo; MimeType=audio/midi;application/x-muse;application/xml; muse-4.0.0/src/packaging/ubuntu/000077500000000000000000000000001404105311000165065ustar00rootroot00000000000000muse-4.0.0/src/packaging/ubuntu/DEBIAN/000077500000000000000000000000001404105311000174305ustar00rootroot00000000000000muse-4.0.0/src/packaging/ubuntu/DEBIAN/control000066400000000000000000000014011404105311000210270ustar00rootroot00000000000000Source: muse3master Maintainer: MusE Project Version: 3.1master Architecture: amd64 Build-Depends: cmake, debhelper-compat (= 12), dssi-dev, libasound2-dev, libdssialsacompat-dev [!linux-any], libfluidsynth-dev, libjack-dev, liblilv-dev (>= 0.22), liblo-dev, libqt5svg5-dev, libsamplerate0-dev, libsndfile1-dev, libsord-dev (>= 0.14), lv2-dev (>= 1.12), qtbase5-dev, qttools5-dev Homepage: https://github.com/muse-sequencer/muse Package: muse3master Depends: python3 Description: Qt-based audio/MIDI sequencer MusE is a MIDI/audio sequencer with recording and editing capabilities. This is the muse-4.0.0/src/packaging/ubuntu/DEBIAN/copyright000066400000000000000000000000061404105311000213570ustar00rootroot00000000000000GPLv2 muse-4.0.0/src/packaging/ubuntu/build_ubuntu_debian_package.sh000066400000000000000000000020251404105311000245170ustar00rootroot00000000000000#!/bin/sh PACKAGE_NAME=muse_master_amd64 echo building MusE package $PACKAGE_NAME # Script to build a ubuntu .deb package of the current checkout. # Step 1. build MusE mkdir build cd build cmake -DCMAKE_BUILD_TYPE=release -DCMAKE_INSTALL_PREFIX=/opt/muse-sequencer.github.io .. make # Step 2. install a temporary installation (requires write access to opt) # TODO: install to a chroot environment make install # Step 3. Setup package environment mkdir -p $PACKAGE_NAME/opt/muse-sequencer.github.io mkdir -p $PACKAGE_NAME/usr/bin mkdir -p $PACKAGE_NAME/usr/applications mkdir -p $PACKAGE_NAME/usr/icons ln -s /opt/muse-sequencer.github.io/bin/muse3 $PACKAGE_NAME/usr/bin/muse3 cp -r ../packaging/ubuntu/DEBIAN $PACKAGE_NAME mkdir -p $PACKAGE_NAME/opt/muse-sequencer.github.io cp -r /opt/muse-sequencer.github.io $PACKAGE_NAME/opt cp packaging/org.musesequencer.Muse3.desktop $PACKAGE_NAME/usr/applications/ cp ../packaging/org.musesequencer.Muse3.png $PACKAGE_NAME/usr/icons/ # Step 4. create package dpkg-deb --build $PACKAGE_NAME muse-4.0.0/src/plugins/000077500000000000000000000000001404105311000147215ustar00rootroot00000000000000muse-4.0.0/src/plugins/CMakeLists.txt000066400000000000000000000020531404105311000174610ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ADD_SUBDIRECTORY( freeverb ) ADD_SUBDIRECTORY( doublechorus ) ADD_SUBDIRECTORY( pandelay ) muse-4.0.0/src/plugins/doublechorus/000077500000000000000000000000001404105311000174175ustar00rootroot00000000000000muse-4.0.0/src/plugins/doublechorus/CMakeLists.txt000066400000000000000000000030411404105311000221550ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB doublechorus_source_files doublechorus.cpp doublechorusmodel.cpp simplechorusmodel.cpp ) ## ## Define target ## add_library ( doublechorus SHARED ${doublechorus_source_files} ) ## ## Compilation flags and target name ## # tell cmake to name the target doublechorus.so instead of # libdoublechorus.so # set_target_properties (doublechorus PROPERTIES PREFIX "" #COMPILE_FLAGS "-O3" ) ## ## Install location ## install( TARGETS doublechorus DESTINATION ${MusE_PLUGINS_DIR} ) muse-4.0.0/src/plugins/doublechorus/doublechorus.cpp000066400000000000000000000133171404105311000226260ustar00rootroot00000000000000//========================================================= // DoubleChorus for MusE // // (C) Copyright 2006 Nil Geisweiller // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "doublechorusmodel.h" #include //--------------------------------------------------------- // instantiate doublechorus // Construct a new plugin instance. //--------------------------------------------------------- LADSPA_Handle instantiate(const LADSPA_Descriptor* /*Descriptor*/, unsigned long samplerate) { return new DoubleChorusModel(samplerate); } //--------------------------------------------------------- // connect PortTo doublechorus // Connect a port to a data location. //--------------------------------------------------------- void connect(LADSPA_Handle Instance, unsigned long port, LADSPA_Data* data) { ((DoubleChorusModel *)Instance)->port[port] = data; } //--------------------------------------------------------- // activate //--------------------------------------------------------- void activate(LADSPA_Handle instance) { ((DoubleChorusModel *)instance)->activate(); } //--------------------------------------------------------- // deactivate //--------------------------------------------------------- void deactivate(LADSPA_Handle /*Instance*/) { } //--------------------------------------------------------- // run doublechorus //--------------------------------------------------------- void run(LADSPA_Handle Instance, unsigned long n) { ((DoubleChorusModel *)Instance)->processReplace(n); } //--------------------------------------------------------- // runAdding doublechorus // *ADD* the output to the output buffer. //--------------------------------------------------------- void runAdding(LADSPA_Handle Instance, unsigned long n) { ((DoubleChorusModel *)Instance)->processMix(n); } //--------------------------------------------------------- // set doublechorus RunAddingGain //--------------------------------------------------------- void setGain(LADSPA_Handle /*Instance*/, LADSPA_Data /*Gain*/) { printf("TEST setGain\n"); // ((DoubleChorusModel *)Instance)->m_fRunAddingGain = Gain; } //--------------------------------------------------------- // cleanup doublechorus //--------------------------------------------------------- void cleanup(LADSPA_Handle Instance) { delete (DoubleChorusModel *)Instance; } static const char* portNames[] = { "Input (Left)", "Input (Right)", "Output (Left)", "Output (Right)", "Pan 1", "LFOFreq 1", "Depth 1", "Pan 2", "LFOFreq 2", "Depth 2", "Dry/Wet" }; LADSPA_PortDescriptor portDescriptors[] = { LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL }; LADSPA_PortRangeHint portRangeHints[] = { { 0, 0.0, 0.0 }, { 0, 0.0, 0.0 }, { 0, 0.0, 0.0 }, { 0, 0.0, 0.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_HIGH, 0.0, 1.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_LOGARITHMIC | LADSPA_HINT_DEFAULT_HIGH, MINFREQ, MAXFREQ }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_LOW, 0.0, 1.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_LOW, 0.0, 1.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_LOGARITHMIC | LADSPA_HINT_DEFAULT_MIDDLE, MINFREQ, MAXFREQ }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_LOW, 0.0, 1.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_MIDDLE, 0.0, 1.0 }, }; LADSPA_Descriptor descriptor = { 1051, "doublechorus1", LADSPA_PROPERTY_HARD_RT_CAPABLE, "DoubleChorus", "Nil Geisweiller", "GPL", NBRPARAM + 4, portDescriptors, portNames, portRangeHints, 0, // impl. data instantiate, connect, activate, run, runAdding, setGain, deactivate, cleanup }; //--------------------------------------------------------- // _init // called automatically when the plugin library is first // loaded. //--------------------------------------------------------- void _init() { } //--------------------------------------------------------- // _fini // called automatically when the library is unloaded. //--------------------------------------------------------- void _fini() { } //--------------------------------------------------------- // ladspa_descriptor // Return a descriptor of the requested plugin type. //--------------------------------------------------------- const LADSPA_Descriptor* ladspa_descriptor(unsigned long i) { return (i == 0) ? &descriptor : 0; } muse-4.0.0/src/plugins/doublechorus/doublechorusmodel.cpp000066400000000000000000000136201404105311000236440ustar00rootroot00000000000000//=========================================================================== // // doublechorusmodel // // Version 0.0.1 // // // // // Copyright (c) 2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #include "doublechorusmodel.h" //--------------------------------------------------------- // DoubleChorusModel //--------------------------------------------------------- DoubleChorusModel::DoubleChorusModel(unsigned long samplerate) { _simpleChorus1 = new SimpleChorusModel((float)samplerate); _simpleChorus2 = new SimpleChorusModel((float)samplerate); param[0] = getPan1(); param[1] = getLFOFreq1(); param[2] = getDepth1(); param[3] = getPan2(); param[4] = getLFOFreq2(); param[5] = getDepth2(); param[6] = getDryWet(); } DoubleChorusModel::~DoubleChorusModel() { delete(_simpleChorus1); delete(_simpleChorus2); } //--------------------------------------------------------- // activate //--------------------------------------------------------- void DoubleChorusModel::activate() { *port[4] = param[0]; *port[5] = param[1]; *port[6] = param[2]; *port[7] = param[3]; *port[8] = param[4]; *port[9] = param[5]; *port[10] = param[6]; } //--------------------------------------------------------- // processReplace //--------------------------------------------------------- void DoubleChorusModel::processReplace(long n) { float tmpLeftOutput1; float tmpRightOutput1; float tmpLeftOutput2; float tmpRightOutput2; //update parameters if (param[0] != *port[4]) { param[0] = *port[4]; setPan1(param[0]); } if (param[1] != *port[5]) { param[1] = *port[5]; setLFOFreq1(param[1]); } if (param[2] != *port[6]) { param[2] = *port[6]; setDepth1(param[2]); } if (param[3] != *port[7]) { param[3] = *port[7]; setPan2(param[3]); } if (param[4] != *port[8]) { param[4] = *port[8]; setLFOFreq2(param[4]); } if (param[5] != *port[9]) { param[5] = *port[9]; setDepth2(param[5]); } if (param[6] != *port[10]) { param[6] = *port[10]; setDryWet(param[6]); } //process the effect for (int i = 0; i < n; ++i) { _simpleChorus1->process_chorus(port[0][i], port[1][i], &tmpLeftOutput1, &tmpRightOutput1); _simpleChorus2->process_chorus(port[0][i], port[1][i], &tmpLeftOutput2, &tmpRightOutput2); port[2][i] = _dryWet * (tmpLeftOutput1 + tmpLeftOutput2) + (1.0 - _dryWet) * port[0][i]; port[3][i] = _dryWet * (tmpRightOutput1 + tmpRightOutput2) + (1.0 - _dryWet) * port[1][i]; } } void DoubleChorusModel::processMix(long n) { float tmpLeftOutput1; float tmpRightOutput1; float tmpLeftOutput2; float tmpRightOutput2; //update parameters if (param[0] != *port[4]) { param[0] = *port[4]; setPan1(param[0]); } if (param[1] != *port[5]) { param[1] = *port[5]; setLFOFreq1(param[1]); } if (param[2] != *port[6]) { param[2] = *port[6]; setDepth1(param[2]); } if (param[3] != *port[7]) { param[3] = *port[7]; setPan2(param[3]); } if (param[4] != *port[8]) { param[4] = *port[8]; setLFOFreq2(param[4]); } if (param[5] != *port[9]) { param[5] = *port[9]; setDepth2(param[5]); } if (param[6] != *port[10]) { param[6] = *port[10]; setDryWet(param[6]); } //process the effect for (int i = 0; i < n; ++i) { _simpleChorus1->process_chorus(port[0][i], port[1][i], &tmpLeftOutput1, &tmpRightOutput1); _simpleChorus2->process_chorus(port[0][i], port[1][i], &tmpLeftOutput2, &tmpRightOutput2); port[2][i] += _dryWet * (tmpLeftOutput1 + tmpLeftOutput2) + (1.0 - _dryWet) * port[0][i]; port[3][i] += _dryWet * (tmpRightOutput1 + tmpRightOutput2) + (1.0 - _dryWet) * port[1][i]; } } //------------------------------------------------------------------ // set parameters //------------------------------------------------------------------ void DoubleChorusModel::setPan1(float value) { _simpleChorus1->setPan(value); } void DoubleChorusModel::setLFOFreq1(float value) { _simpleChorus1->setLFOFreq(value); } void DoubleChorusModel::setDepth1(float value) { _simpleChorus1->setDepth(value); } void DoubleChorusModel::setPan2(float value) { _simpleChorus2->setPan(value); } void DoubleChorusModel::setLFOFreq2(float value) { _simpleChorus2->setLFOFreq(value); } void DoubleChorusModel::setDepth2(float value) { _simpleChorus2->setDepth(value); } void DoubleChorusModel::setDryWet(float value) { _dryWet = value; } //---------------------------------------------------------------- // get parameters //---------------------------------------------------------------- float DoubleChorusModel::getPan1() { return _simpleChorus1->getPan(); } float DoubleChorusModel::getLFOFreq1() { return _simpleChorus1->getLFOFreq(); } float DoubleChorusModel::getDepth1() { return _simpleChorus1->getDepth(); } float DoubleChorusModel::getPan2() { return _simpleChorus2->getPan(); } float DoubleChorusModel::getLFOFreq2() { return _simpleChorus2->getLFOFreq(); } float DoubleChorusModel::getDepth2() { return _simpleChorus2->getDepth(); } float DoubleChorusModel::getDryWet() { return _dryWet; } muse-4.0.0/src/plugins/doublechorus/doublechorusmodel.h000066400000000000000000000036411404105311000233130ustar00rootroot00000000000000//=========================================================================== // // doublechorusmodel // // Version 0.0.1 // // // // // Copyright (c) 2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #ifndef __DOUBLECHORUSMODEL_H #define __DOUBLECHORUSMODEL_H #include "simplechorusmodel.h" #include #define NBRPARAM 7 class SimpleChorusModel; class DoubleChorusModel { SimpleChorusModel* _simpleChorus1; SimpleChorusModel* _simpleChorus2; float _dryWet; //0.0 : dry, 1.0 : wet public: LADSPA_Data* port[NBRPARAM + 4]; float param[NBRPARAM]; DoubleChorusModel(unsigned long samplerate); ~DoubleChorusModel(); void processMix(long numsamples); void processReplace(long numsamples); void setPan1(float value); void setLFOFreq1(float value); void setDepth1(float value); void setPan2(float value); void setLFOFreq2(float value); void setDepth2(float value); void setDryWet(float value); float getPan1(); float getLFOFreq1(); float getDepth1(); float getPan2(); float getLFOFreq2(); float getDepth2(); float getDryWet(); void activate(); }; #endif muse-4.0.0/src/plugins/doublechorus/simplechorusmodel.cpp000066400000000000000000000106561404105311000236710ustar00rootroot00000000000000//=========================================================================== // // simplechorus // // Version 0.0.1 // // // // // Copyright (c) 2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #include "simplechorusmodel.h" #include "muse_math.h" #include #define ABS(x) (x>=0?x:-x) // Linearly interpolate [ = a * (1 - f) + b * f] inline float lin_interp(float f, float a, float b) { return a + f * (b - a); } // Cubic interpolation function inline float cube_interp(const float fr, const float inm1, const float in, const float inp1, const float inp2) { return in + 0.5f * fr * (inp1 - inm1 + fr * (4.0f * inp1 + 2.0f * inm1 - 5.0f * in - inp2 + fr * (3.0f * (in - inp1) - inm1 + inp2))); } float SimpleChorusModel::sinus[MAXSINUSRESOLUTION]; int SimpleChorusModel::useCount = 0; SimpleChorusModel::SimpleChorusModel(float samplerate) { _sampleRate = samplerate; //sinus if (useCount++ == 0) for(int i = 0; i < MAXSINUSRESOLUTION; i++) sinus[i] = (float)(sin(((double)i * 2.0 * M_PI) / (double)MAXSINUSRESOLUTION)); _index = 0.0; //init buffer for(int i = 0; i < MAXBUFFERLENGTH; i++) { _leftBuffer[i] = 0.0; _rightBuffer[i] = 0.0; } _position = 0; //initial parameters _pan = 0.5; _LFOFreq = 1.0; _depth = 0.5; setChorus(); } SimpleChorusModel::~SimpleChorusModel() { } void SimpleChorusModel::process_chorus(float leftInput, float rightInput, float* leftOutput, float* rightOutput) { float ocsDiff; _ocsDistance = _depthAmp * sinus[(int)_index]; ocsDiff = _ocsDistance - floorf(_ocsDistance); _past_position_left = MAXBUFFERLENGTH //to be sure that _past_position_left>0 + _position - _leftMidDistance + (int)_ocsDistance; _past_position_right = MAXBUFFERLENGTH + _position - _rightMidDistance + (int)_ocsDistance; *leftOutput = _leftAmp * lin_interp(ocsDiff, _leftBuffer[_past_position_left%MAXBUFFERLENGTH], _leftBuffer[(_past_position_left+1)%MAXBUFFERLENGTH]); *rightOutput = _rightAmp * lin_interp(ocsDiff, _rightBuffer[_past_position_right%MAXBUFFERLENGTH], _rightBuffer[(_past_position_right+1)%MAXBUFFERLENGTH]); _leftBuffer[_position] = leftInput; _rightBuffer[_position] = rightInput; _position++; _position %= MAXBUFFERLENGTH; _index += _inct; _index = (_index= bufsize) bufidx = 0; // bufidx = ++bufidx % bufsize; return output; } void mute() { for (int i=0; i= bufsize) bufidx = 0; // bufidx = ++bufidx % bufsize; return output; } void mute() { for (int i=0; iport[port] = data; } //--------------------------------------------------------- // activate //--------------------------------------------------------- void activate(LADSPA_Handle instance) { ((Revmodel *)instance)->activate(); } //--------------------------------------------------------- // deactivate //--------------------------------------------------------- void deactivate(LADSPA_Handle /*Instance*/) { } //--------------------------------------------------------- // runFreeverb //--------------------------------------------------------- void run(LADSPA_Handle Instance, unsigned long n) { ((Revmodel*)Instance)->processreplace(n); } //--------------------------------------------------------- // runAddingFreeverb // *ADD* the output to the output buffer. //--------------------------------------------------------- void runAdding(LADSPA_Handle Instance, unsigned long n) { ((Revmodel*)Instance)->processmix(n); } //--------------------------------------------------------- // setFreeverbRunAddingGain //--------------------------------------------------------- void setGain(LADSPA_Handle /*Instance*/, LADSPA_Data /*Gain*/) { // ((Freeverb *)Instance)->m_fRunAddingGain = Gain; } //--------------------------------------------------------- // cleanupFreeverb //--------------------------------------------------------- void cleanup(LADSPA_Handle Instance) { delete (Revmodel *)Instance; } static const char* portNames[] = { "Input (Left)", "Input (Right)", "Output (Left)", "Output (Right)", "Room Size", "Damping", "Wet Level", }; LADSPA_PortDescriptor portDescriptors[] = { LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, }; LADSPA_PortRangeHint portRangeHints[] = { { 0, 0.0, 0.0 }, { 0, 0.0, 0.0 }, { 0, 0.0, 0.0 }, { 0, 0.0, 0.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW, 0.0, 1.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_LOGARITHMIC, 0.0, 1.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_LOGARITHMIC, 0.0, 1.0 }, }; LADSPA_Descriptor descriptor = { 1050, "freeverb1", LADSPA_PROPERTY_HARD_RT_CAPABLE, "Freeverb", "Werner Schweer", "None", 7, portDescriptors, portNames, portRangeHints, 0, // impl. data instantiate, connect, activate, run, runAdding, setGain, deactivate, cleanup }; //--------------------------------------------------------- // _init // called automatically when the plugin library is first // loaded. //--------------------------------------------------------- void _init() { } //--------------------------------------------------------- // _fini // called automatically when the library is unloaded. //--------------------------------------------------------- void _fini() { } //--------------------------------------------------------- // ladspa_descriptor // Return a descriptor of the requested plugin type. //--------------------------------------------------------- const LADSPA_Descriptor* ladspa_descriptor(unsigned long i) { return (i == 0) ? &descriptor : 0; } muse-4.0.0/src/plugins/freeverb/readme.txt000066400000000000000000000157271404105311000205330ustar00rootroot00000000000000readme from original freeverb-source: ============================================== Freeverb - Free, studio-quality reverb SOURCE CODE in the public domain ----------------------------------------------------------------------- Written by Jezar at Dreampoint - http://www.dreampoint.co.uk Introduction ------------ Hello. I'll try to keep this "readme" reasonably small. There are few things in the world that I hate more than long "readme" files. Except "coding conventions" - but more on that later... In this zip file you will find two folders of C++ source code: "Components" - Contains files that should clean-compile ON ANY TYPE OF COMPUTER OR SYSTEM WHATSOEVER. It should not be necessary to make ANY changes to these files to get them to compile, except to make up for inadequacies of certain compilers. These files create three classes - a comb filter, an allpass filter, and a reverb model made up of a number of instances of the filters, with some features to control the filters at a macro level. You will need to link these classes into another program that interfaces with them. The files in the components drawer are completely independent, and can be built without dependencies on anything else. Because of the simple interface, it should be possible to interface these files to any system - VST, DirectX, anything - without changing them AT ALL. "FreeverbVST" - Contains a Steinberg VST implementation of this version of Freeverb, using the components in (surprise) the components folder. It was built on a PC but may compile properly for the Macintosh with no problems. I don't know - I don't have a Macintosh. If you've figured out how to compile the examples in the Steinberg VST Development Kit, then you should easily figure out how to bring the files into a project and get it working in a few minutes. It should be very simple. Note that this version of Freeverb doesn't contain predelay, or any EQ. I thought that might make it difficult to understand the "reverb" part of the code. Once you figure out how Freeverb works, you should find it trivial to add such features with little CPU overhead. Also, the code in this version of Freeverb has been optimised. This has changed the sound *slightly*, but not significantly compared to how much processing power it saves. Finally, note that there is also a built copy of this version of Freeverb called "Freeverb3.dll" - this is a VST plugin for the PC. If you want a version for the Mac or anything else, then you'll need to build it yourself from the code. Technical Explanation --------------------- Freeverb is a simple implementation of the standard Schroeder/Moorer reverb model. I guess the only reason why it sounds better than other reverbs, is simply because I spent a long while doing listening tests in order to create the values found in "tuning.h". It uses 8 comb filters on both the left and right channels), and you might possibly be able to get away with less if CPU power is a serious constraint for you. It then feeds the result of the reverb through 4 allpass filters on both the left and right channels. These "smooth" the sound. Adding more than four allpasses doesn't seem to add anything significant to the sound, and if you use less, the sound gets a bit "grainy". The filters on the right channel are slightly detuned compared to the left channel in order to create a stereo effect. Hopefully, you should find the code in the components drawer a model of brevity and clarity. Notice that I don't use any "coding conventions". Personally, I think that coding conventions suck. They are meant to make the code "clearer", but they inevitably do the complete opposite, making the code completely unfathomable. Anyone whose done Windows programming with its - frankly stupid - "Hungarian notation" will know exactly what I mean. Coding conventions typically promote issues that are irrelevant up to the status of appearing supremely important. It may have helped back people in the days when compilers where somewhat feeble in their type-safety, but not in the new millennium with advanced C++ compilers. Imagine if we rewrote the English language to conform to coding conventions. After all, The arguments should be just as valid for the English language as they are for a computer language. For example, we could put a lower-case "n" in front of every noun, a lower-case "p" in front of a persons name, a lower-case "v" in front of every verb, and a lower-case "a" in front of every adjective. Can you imagine what the English language would look like? All in the name of "clarity". It's just as stupid to do this for computer code as it would be to do it for the English language. I hope that the code for Freeverb in the components drawer demonstrates this, and helps start a movement back towards sanity in coding practices. Background ---------- Why is the Freeverb code now public domain? Simple. I only intended to create Freeverb to provide me and my friends with studio-quality reverb for free. I never intended to make any money out of it. However, I simply do not have the time to develop it any further. I'm working on a "concept album" at the moment, and I'll never finish it if I spend any more time programming. In any case, I make more far money as a contract programmer - making Mobile Internet products - than I ever could writing plugins, so it simply doesn't make financial sense for me to spend any more time on it. Rather than give Freeverb to any particular individual or organisation to profit from it, I've decided to give it away to the internet community at large, so that quality, FREE (or at the very least, low-cost) reverbs can be developed for all platforms. Feel free to use the source code for Freeverb in any of your own products, whether they are also available for free, or even if they are commercial - I really don't mind. You may do with the code whatever you wish. If you use it in a product (whether commercial or not), it would be very nice of you, if you were to send me a copy of your product - although I appreciate that this isn't always possible in all circumstances. HOWEVER, please don't bug me with questions about how to use this code. I gave away Freeverb because I don't have time to maintain it. That means I *certainly* don't have time to answer questions about the source code, so please don't email questions to me. I *will* ignore them. If you can't figure the code for Freeverb out - then find somebody who can. I hope that either way, you enjoy experimenting with it. Disclaimer ---------- This software and source code is given away for free, without any warranties of any kind. It has been given away to the internet community as a free gift, so please treat it in the same spirit. I hope this code is useful and interesting to you all! I hope you have lots of fun experimenting with it and make good products! Very best regards, Jezar. Technology Consultant Dreampoint Design and Engineering http://www.dreampoint.co.uk //ends muse-4.0.0/src/plugins/freeverb/revmodel.cpp000066400000000000000000000144041404105311000210450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./plugins/freeverb/revmodel.cpp $ // // Written by Jezar at Dreampoint, June 2000 // http://www.dreampoint.co.uk // This code is public domain // //========================================================= // Reverb model implementation // #include #include "revmodel.h" //--------------------------------------------------------- // Revmodel //--------------------------------------------------------- Revmodel::Revmodel() { // Tie the components to their buffers combL[0].setbuffer(bufcombL1,combtuningL1); combR[0].setbuffer(bufcombR1,combtuningR1); combL[1].setbuffer(bufcombL2,combtuningL2); combR[1].setbuffer(bufcombR2,combtuningR2); combL[2].setbuffer(bufcombL3,combtuningL3); combR[2].setbuffer(bufcombR3,combtuningR3); combL[3].setbuffer(bufcombL4,combtuningL4); combR[3].setbuffer(bufcombR4,combtuningR4); combL[4].setbuffer(bufcombL5,combtuningL5); combR[4].setbuffer(bufcombR5,combtuningR5); combL[5].setbuffer(bufcombL6,combtuningL6); combR[5].setbuffer(bufcombR6,combtuningR6); combL[6].setbuffer(bufcombL7,combtuningL7); combR[6].setbuffer(bufcombR7,combtuningR7); combL[7].setbuffer(bufcombL8,combtuningL8); combR[7].setbuffer(bufcombR8,combtuningR8); allpassL[0].setbuffer(bufallpassL1,allpasstuningL1); allpassR[0].setbuffer(bufallpassR1,allpasstuningR1); allpassL[1].setbuffer(bufallpassL2,allpasstuningL2); allpassR[1].setbuffer(bufallpassR2,allpasstuningR2); allpassL[2].setbuffer(bufallpassL3,allpasstuningL3); allpassR[2].setbuffer(bufallpassR3,allpasstuningR3); allpassL[3].setbuffer(bufallpassL4,allpasstuningL4); allpassR[3].setbuffer(bufallpassR4,allpasstuningR4); // Set default values allpassL[0].setfeedback(0.5f); allpassR[0].setfeedback(0.5f); allpassL[1].setfeedback(0.5f); allpassR[1].setfeedback(0.5f); allpassL[2].setfeedback(0.5f); allpassR[2].setfeedback(0.5f); allpassL[3].setfeedback(0.5f); allpassR[3].setfeedback(0.5f); param[0] = initialroom; param[1] = initialdamp; param[2] = initialwet; setroomsize(initialroom); setdamp(initialdamp); setwidth(initialwidth); setmode(initialmode); // Buffer will be full of rubbish - so we MUST mute them for (int i = 0; i < numcombs; i++) { combL[i].mute(); combR[i].mute(); } for (int i=0;i= freezemode) { roomsize1 = 1; damp1 = 0; gain = muted; } else { roomsize1 = roomsize; damp1 = damp; gain = fixedgain; } for (int i = 0; i < numcombs; i++) { combL[i].setfeedback(roomsize1); combR[i].setfeedback(roomsize1); } for (int i = 0; i < numcombs; i++) { combL[i].setdamp(damp1); combR[i].setdamp(damp1); } } // The following get/set functions are not inlined, because // speed is never an issue when calling them, and also // because as you develop the reverb model, you may // wish to take dynamic action when they are called. void Revmodel::setroomsize(float value) { roomsize = (value*scaleroom) + offsetroom; update(); } float Revmodel::getroomsize() { return (roomsize-offsetroom)/scaleroom; } void Revmodel::setdamp(float value) { damp = value*scaledamp; update(); } void Revmodel::setwidth(float value) { width = value; update(); } void Revmodel::setmode(float value) { mode = value; update(); } float Revmodel::getmode() { return (mode >= freezemode) ? 1 : 0; } muse-4.0.0/src/plugins/freeverb/revmodel.h000066400000000000000000000043451404105311000205150ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./plugins/freeverb/revmodel.h $ // // // Written by Jezar at Dreampoint, June 2000 // http://www.dreampoint.co.uk // This code is public domain // //========================================================= // Reverb model declaration #ifndef _revmodel_ #define _revmodel_ #include "comb.h" #include "allpass.h" #include "tuning.h" #include //--------------------------------------------------------- // Revmodel //--------------------------------------------------------- class Revmodel { float gain; float roomsize,roomsize1; float damp,damp1; float width; float mode; // Comb filters comb combL[numcombs]; comb combR[numcombs]; // Allpass filters allpass allpassL[numallpasses]; allpass allpassR[numallpasses]; // Buffers for the combs float bufcombL1[combtuningL1]; float bufcombR1[combtuningR1]; float bufcombL2[combtuningL2]; float bufcombR2[combtuningR2]; float bufcombL3[combtuningL3]; float bufcombR3[combtuningR3]; float bufcombL4[combtuningL4]; float bufcombR4[combtuningR4]; float bufcombL5[combtuningL5]; float bufcombR5[combtuningR5]; float bufcombL6[combtuningL6]; float bufcombR6[combtuningR6]; float bufcombL7[combtuningL7]; float bufcombR7[combtuningR7]; float bufcombL8[combtuningL8]; float bufcombR8[combtuningR8]; // Buffers for the allpasses float bufallpassL1[allpasstuningL1]; float bufallpassR1[allpasstuningR1]; float bufallpassL2[allpasstuningL2]; float bufallpassR2[allpasstuningR2]; float bufallpassL3[allpasstuningL3]; float bufallpassR3[allpasstuningR3]; float bufallpassL4[allpasstuningL4]; float bufallpassR4[allpasstuningR4]; void update(); public: LADSPA_Data* port[7]; float param[3]; Revmodel(); void processmix(long numsamples); void processreplace(long numsamples); void setroomsize(float value); float getroomsize(); void setdamp(float value); void setwidth(float value); void setmode(float value); float getmode(); void activate(); }; #endif muse-4.0.0/src/plugins/freeverb/tuning.h000066400000000000000000000040741404105311000202030ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./plugins/freeverb/tuning.h $ // // Reverb model tuning values // // Written by Jezar at Dreampoint, June 2000 // http://www.dreampoint.co.uk // This code is public domain // //========================================================= #ifndef _tuning_ #define _tuning_ const int numcombs = 8; const int numallpasses = 4; const float muted = 0; const float fixedgain = 0.015f; const float scalewet = 3; const float scaledry = 2; const float scaledamp = 0.4f; const float scaleroom = 0.28f; const float offsetroom = 0.7f; const float initialroom = 0.5f; const float initialdamp = 0.5f; const float initialwet = 1/scalewet; const float initialdry = 0; const float initialwidth = 1; const float initialmode = 0; const float freezemode = 0.5f; const int stereospread = 23; // These values assume 44.1KHz sample rate // they will probably be OK for 48KHz sample rate // but would need scaling for 96KHz (or other) sample rates. // The values were obtained by listening tests. const int combtuningL1 = 1116; const int combtuningR1 = 1116+stereospread; const int combtuningL2 = 1188; const int combtuningR2 = 1188+stereospread; const int combtuningL3 = 1277; const int combtuningR3 = 1277+stereospread; const int combtuningL4 = 1356; const int combtuningR4 = 1356+stereospread; const int combtuningL5 = 1422; const int combtuningR5 = 1422+stereospread; const int combtuningL6 = 1491; const int combtuningR6 = 1491+stereospread; const int combtuningL7 = 1557; const int combtuningR7 = 1557+stereospread; const int combtuningL8 = 1617; const int combtuningR8 = 1617+stereospread; const int allpasstuningL1 = 556; const int allpasstuningR1 = 556+stereospread; const int allpasstuningL2 = 441; const int allpasstuningR2 = 441+stereospread; const int allpasstuningL3 = 341; const int allpasstuningR3 = 341+stereospread; const int allpasstuningL4 = 225; const int allpasstuningR4 = 225+stereospread; #endif//_tuning_ //ends muse-4.0.0/src/plugins/pandelay/000077500000000000000000000000001404105311000165165ustar00rootroot00000000000000muse-4.0.0/src/plugins/pandelay/CMakeLists.txt000066400000000000000000000027711404105311000212650ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB pandelay_source_files ladspapandelay.cpp pandelay.cpp pandelaymodel.cpp ) ## ## Define target ## add_library ( pandelay SHARED ${pandelay_source_files} ) ## ## Compilation flags and target name ## # tell cmake to name the target pandelay.so instead of # libpandelay.so # set_target_properties (pandelay PROPERTIES PREFIX "" #COMPILE_FLAGS "-O3" ) ## ## Install location ## install( TARGETS pandelay DESTINATION ${MusE_PLUGINS_DIR} ) muse-4.0.0/src/plugins/pandelay/ladspapandelay.cpp000066400000000000000000000053311404105311000222060ustar00rootroot00000000000000//=========================================================================== // // ladspapandelay // // Version 0.0.1 // // // // // Copyright (c) 2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #include "ladspapandelay.h" //--------------------------------------------------------- // PanDelay //--------------------------------------------------------- LADSPAPanDelay::LADSPAPanDelay(unsigned long samplerate) : PanDelayModel(samplerate) { //TODO init param } LADSPAPanDelay::~LADSPAPanDelay() { } //--------------------------------------------------------- // activate //--------------------------------------------------------- void LADSPAPanDelay::activate() { *port[4] = param[0]; *port[5] = param[1]; *port[6] = param[2]; *port[7] = param[3]; *port[8] = param[4]; *port[9] = param[5]; } void LADSPAPanDelay::updateParameters() { if (param[0] != *port[4]) { param[0] = *port[4]; setBPM(param[0]); } if (param[1] != *port[5]) { param[1] = *port[5]; setBeatRatio(param[1]); } if (param[2] != *port[6]) { param[2] = *port[6]; setFeedback(param[2]); } if (param[3] != *port[7]) { param[3] = *port[7]; setPanLFOFreq(param[3]); } if (param[4] != *port[8]) { param[4] = *port[8]; setPanLFODepth(param[4]); } if (param[5] != *port[9]) { param[5] = *port[9]; setDryWet(param[5]); } } //--------------------------------------------------------- // processReplace //--------------------------------------------------------- void LADSPAPanDelay::processReplace(long n) { updateParameters(); PanDelayModel::processReplace(port[0], port[1], port[2], port[3], n); } //--------------------------------------------------------- // processMix //--------------------------------------------------------- void LADSPAPanDelay::processMix(long n) { updateParameters(); PanDelayModel::processMix(port[0], port[1], port[2], port[3], n); } muse-4.0.0/src/plugins/pandelay/ladspapandelay.h000066400000000000000000000027641404105311000216620ustar00rootroot00000000000000//=========================================================================== // // ladspapandelay // // Version 0.0.1 // // // // // Copyright (c) 2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #ifndef __LADSPAPANDELAY_H #define __LADSPAPANDELAY_H #include "pandelaymodel.h" #include #ifdef NBRPARAM #undef NBRPARAM #endif #define NBRPARAM 6 class LADSPAPanDelay : public PanDelayModel { private: public: LADSPAPanDelay(unsigned long samplerate); ~LADSPAPanDelay(); LADSPA_Data* port[NBRPARAM + 4]; float param[NBRPARAM]; void updateParameters(); void processMix(long numsamples); void processReplace(long numsamples); void activate(); }; #endif muse-4.0.0/src/plugins/pandelay/pandelay.cpp000066400000000000000000000127561404105311000210320ustar00rootroot00000000000000//========================================================= // PanDelay for MusE // // (C) Copyright 2006 Nil Geisweiller // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "ladspapandelay.h" #include //--------------------------------------------------------- // instantiate pandelay // Construct a new plugin instance. //--------------------------------------------------------- LADSPA_Handle instantiate(const LADSPA_Descriptor* /*Descriptor*/, unsigned long samplerate) { return new LADSPAPanDelay(samplerate); } //--------------------------------------------------------- // connect PortTo pandelay // Connect a port to a data location. //--------------------------------------------------------- void connect(LADSPA_Handle Instance, unsigned long port, LADSPA_Data* data) { ((LADSPAPanDelay*)Instance)->port[port] = data; } //--------------------------------------------------------- // activate //--------------------------------------------------------- void activate(LADSPA_Handle instance) { ((LADSPAPanDelay*)instance)->activate(); } //--------------------------------------------------------- // deactivate //--------------------------------------------------------- void deactivate(LADSPA_Handle /*Instance*/) { } //--------------------------------------------------------- // run pandelay //--------------------------------------------------------- void run(LADSPA_Handle Instance, unsigned long n) { ((LADSPAPanDelay*)Instance)->processReplace(n); } //--------------------------------------------------------- // runAdding pandelay // *ADD* the output to the output buffer. //--------------------------------------------------------- void runAdding(LADSPA_Handle Instance, unsigned long n) { ((LADSPAPanDelay*)Instance)->processMix(n); } //--------------------------------------------------------- // set pandelay RunAddingGain //--------------------------------------------------------- void setGain(LADSPA_Handle /*Instance*/, LADSPA_Data /*Gain*/) { printf("TEST setGain\n"); // ((LADSPAPanDelay*)Instance)->m_fRunAddingGain = Gain; } //--------------------------------------------------------- // cleanup pandelay //--------------------------------------------------------- void cleanup(LADSPA_Handle Instance) { delete (LADSPAPanDelay*)Instance; } static const char* portNames[] = { "Input (Left)", "Input (Right)", "Output (Left)", "Output (Right)", "BMP", "Beat Ratio", "Feedback", "Pan LFO Freq", "Pan LFO Depth", "Dry/Wet" }; LADSPA_PortDescriptor portDescriptors[] = { LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL }; LADSPA_PortRangeHint portRangeHints[] = { { 0, 0.0, 0.0 }, { 0, 0.0, 0.0 }, { 0, 0.0, 0.0 }, { 0, 0.0, 0.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_100, MINBPM, MAXBPM }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_1, MINBEATRATIO, MAXBEATRATIO }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_HIGH, -1.0, 1.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_LOGARITHMIC | LADSPA_HINT_DEFAULT_HIGH, MINFREQ, MAXFREQ }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_HIGH, 0.0, 1.0 }, { LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_DEFAULT_MIDDLE, 0.0, 1.0 } }; LADSPA_Descriptor descriptor = { 1052, "pandelay", LADSPA_PROPERTY_HARD_RT_CAPABLE, "PanDelay", "Nil Geisweiller", "GPL", NBRPARAM + 4, portDescriptors, portNames, portRangeHints, 0, // impl. data instantiate, connect, activate, run, runAdding, setGain, deactivate, cleanup }; //--------------------------------------------------------- // _init // called automatically when the plugin library is first // loaded. //--------------------------------------------------------- void _init() { } //--------------------------------------------------------- // _fini // called automatically when the library is unloaded. //--------------------------------------------------------- void _fini() { } //--------------------------------------------------------- // ladspa_descriptor // Return a descriptor of the requested plugin type. //--------------------------------------------------------- const LADSPA_Descriptor* ladspa_descriptor(unsigned long i) { return (i == 0) ? &descriptor : 0; } muse-4.0.0/src/plugins/pandelay/pandelaymodel.cpp000066400000000000000000000104251404105311000220420ustar00rootroot00000000000000//=========================================================================== // // PanDelay, panoramic rotating delay // // version 0.0.1 // // pandelaymodel.cpp // // // Copyright (c) 2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #include "pandelaymodel.h" #include PanDelayModel::PanDelayModel(int samplerate) { for(int i = 0; i < MAXBUFFERLENGTH; i++) { _leftBuffer[i] = 0.0; _rightBuffer[i] = 0.0; } _bufferPointer = 0; _inc = 0.0; _l = 1.0; _r = 1.0; _samplerate = samplerate; _beatRatio = 4; setBPM(120); setPanDelay(); } PanDelayModel::~PanDelayModel() { } void PanDelayModel::setSamplerate(int sr) { _samplerate = sr; setPanDelay(); } void PanDelayModel::setBPM(float bpm) { _BPM = bpm; _delayTime = _beatRatio * 60.0 / _BPM; setPanDelay(); } void PanDelayModel::setBeatRatio(float br) { _beatRatio = br; _delayTime = _beatRatio * 60.0 / _BPM; setPanDelay(); } void PanDelayModel::setDelayTime(float dt) { if(dt < MINDELAYTIME) _delayTime = MINDELAYTIME; else if(dt > MAXDELAYTIME) _delayTime = MAXDELAYTIME; else _delayTime = dt; setPanDelay(); } void PanDelayModel::setFeedback(float fb) { _feedback = fb; setPanDelay(); } void PanDelayModel::setPanLFOFreq(float pf) { _panLFOFreq = pf; setPanDelay(); } void PanDelayModel::setPanLFODepth(float pd) { _panLFODepth = pd; setPanDelay(); } void PanDelayModel::setDryWet(float dw) { _dryWet = dw; } void PanDelayModel::setPanDelay() { float numLFOSample = (1.0/_panLFOFreq) * (float)_samplerate; _inc = 2.0 / numLFOSample; _delaySampleSize = (int)(_delayTime * (float)_samplerate); _lBound = 1.0 - _panLFODepth; _rBound = 1.0 + _panLFODepth; } void PanDelayModel::processMix(float* leftSamplesIn, float* rightSamplesIn, float* leftSamplesOut, float* rightSamplesOut, unsigned n) { float ls, rs, p; p = 1.0 - _dryWet; for(unsigned i = 0; i < n; i++) { //read buffer ls = _leftBuffer[_bufferPointer]; rs = _rightBuffer[_bufferPointer]; //write buffer _leftBuffer[_bufferPointer] *= _feedback; _leftBuffer[_bufferPointer] += leftSamplesIn[i]; _rightBuffer[_bufferPointer] *= _feedback; _rightBuffer[_bufferPointer] += rightSamplesIn[i]; //write out leftSamplesOut[i] += _l * _dryWet * ls + p * leftSamplesIn[i]; rightSamplesOut[i] += _r * _dryWet * rs + p * rightSamplesIn[i]; //update _bufferPointer _bufferPointer++; _bufferPointer%=_delaySampleSize; //update _l _r _r += _inc; _l -= _inc; //update _inc if(_r > _rBound || _r < _lBound) _inc = -_inc; } } void PanDelayModel::processReplace(float* leftSamplesIn, float* rightSamplesIn, float* leftSamplesOut, float* rightSamplesOut, unsigned n) { float ls, rs, p; p = 1.0 - _dryWet; for(unsigned i = 0; i < n; i++) { //read buffer ls = _leftBuffer[_bufferPointer]; rs = _rightBuffer[_bufferPointer]; //write buffer _leftBuffer[_bufferPointer] *= _feedback; _leftBuffer[_bufferPointer] += leftSamplesIn[i]; _rightBuffer[_bufferPointer] *= _feedback; _rightBuffer[_bufferPointer] += rightSamplesIn[i]; //write out leftSamplesOut[i] = _l * _dryWet * ls + p * leftSamplesIn[i]; rightSamplesOut[i] = _r * _dryWet * rs + p * rightSamplesIn[i]; //update _bufferPointer _bufferPointer++; _bufferPointer%=_delaySampleSize; //update _l _r _r += _inc; _l -= _inc; //update _inc if(_r > _rBound || _r < _lBound) _inc = -_inc; } } muse-4.0.0/src/plugins/pandelay/pandelaymodel.h000066400000000000000000000051211404105311000215040ustar00rootroot00000000000000//=========================================================================== // // PanDelay, panoramic rotating delay // // version 0.0.1 // // pandelaymodel.h // // // Copyright (c) 2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02110-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #ifndef __PANDELAYMODEL_H #define __PANDELAYMODEL_H #include "muse_math.h" #define MAXBUFFERLENGTH 192000 #define MINFREQ 0.1 //in Hz #define MAXFREQ 10.0 //in Hz #define MINBPM 60.0 #define MAXBPM 255.0 #define MINBEATRATIO 0.125 #define MAXBEATRATIO 2.0 #define MINDELAYTIME 0.01 //in second #define MAXDELAYTIME 2.0 //in second #ifdef NBRPARAM #undef NBRPARAM #endif #define NBRPARAM 5 class PanDelayModel { private: int _samplerate; //bool _beatFraction; //if true then the delay is calculated in beat fraction float _BPM; float _beatRatio; float _delayTime; //delay is calculated according to BMP and ratioBMP float _feedback; float _panLFOFreq; float _panLFODepth; float _dryWet; //0.0 : dry, 1.0 : wet int _delaySampleSize; float _lBound; float _rBound; float _inc; float _l; float _r; float _leftBuffer[MAXBUFFERLENGTH]; float _rightBuffer[MAXBUFFERLENGTH]; int _bufferPointer; public: PanDelayModel(int samplerate); ~PanDelayModel(); void setSamplerate(int sr); void setBeatRatio(float br); void setBPM(float bpm); void setDelayTime(float dt); void setFeedback(float dt); void setPanLFOFreq(float pf); void setPanLFODepth(float pd); void setDryWet(float dw); void setPanDelay(); void processMix(float* leftInSamples, float* rightInSamples, float* leftOutSamples, float* rightOutSamples, unsigned n); void processReplace(float* leftInSamples, float* rightInSamples, float* leftOutSamples, float* rightOutSamples, unsigned n); }; #endif /* __PANDELAYMODEL_H */ muse-4.0.0/src/qss/000077500000000000000000000000001404105311000140465ustar00rootroot00000000000000muse-4.0.0/src/qss/scrollbar_small_vertical.qss000066400000000000000000000015251404105311000216450ustar00rootroot00000000000000QScrollBar:vertical { border: 1px solid grey; background: darkgrey; width: 11px; margin: 6px 0 6px 0; } QScrollBar::handle:vertical { background: lightgrey; min-height: 4px; border: none; border-radius: 0; } QScrollBar::add-line:vertical { background: darkgrey; height: 6px; subcontrol-position: bottom; subcontrol-origin: margin; } QScrollBar::sub-line:vertical { background: darkgrey; height: 6px; subcontrol-position: top; subcontrol-origin: margin; } QScrollBar::up-arrow:vertical { width: 10px; height: 4px; image: url(:/svg/arrow_up.svg); } QScrollBar::down-arrow:vertical { width: 10px; height: 4px; image: url(:/svg/arrow_down.svg); } QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: none; } muse-4.0.0/src/resources.qrc000066400000000000000000000214221404105311000157620ustar00rootroot00000000000000 svg/routing_input.svg svg/routing_output.svg svg/routing_input_unconnected.svg svg/routing_output_unconnected.svg svg/headphones_off.svg svg/headphones_on.svg svg/mute_off.svg svg/mute_on.svg svg/mute_on_X.svg svg/mute_proxy_on.svg svg/mute_and_proxy_on.svg svg/track_off.svg svg/track_on.svg svg/stereo_off.svg svg/stereo_on.svg svg/pre_fader_off.svg svg/pre_fader_on.svg svg/monitor_off_default_col.svg svg/monitor_off.svg svg/monitor_on.svg svg/rec_arm_off.svg svg/rec_arm_off_default_col.svg svg/rec_arm_on.svg svg/solo_spotlight_off.svg svg/solo_spotlight_on.svg svg/solo_spotlight_on_alone.svg svg/solo_proxy_spotlight_on.svg svg/solo_proxy_spotlight_on_alone.svg svg/solo_and_proxy_spotlight_on.svg svg/stop.svg svg/play_off.svg svg/play_on.svg svg/fast_forward.svg svg/rewind.svg svg/rewind_to_start.svg svg/extern_sync_off.svg svg/extern_sync_on.svg svg/master_track_off.svg svg/master_track_on.svg svg/jack_transport_off.svg svg/jack_transport_on.svg svg/timebase_master_off.svg svg/timebase_master_on.svg svg/metronome_off.svg svg/metronome_on.svg svg/paste_ctrl.svg svg/paste_ctrl_erase.svg svg/paste_ctrl_erase_inclusive.svg svg/paste_ctrl_erase_wysiwyg.svg svg/transport_affects_latency_off.svg svg/transport_affects_latency_on.svg svg/override_latency_off.svg svg/override_latency_on.svg svg/speed_off.svg svg/speed_on.svg svg/zoom.svg svg/glue.svg svg/pencil.svg svg/eraser.svg svg/cutter.svg svg/draw.svg svg/pointer.svg svg/mute_parts.svg svg/hand.svg svg/cursor.svg svg/zoomAt.svg svg/magnet.svg svg/cursor_move.svg svg/pencil_move_4_way.svg svg/pencil_move_horiz.svg svg/pencil_move_vert.svg svg/closed_hand.svg svg/panic.svg svg/loop.svg svg/punchin.svg svg/punchout.svg svg/redo.svg svg/undo.svg svg/midiin.svg svg/speaker.svg svg/speaker_single_note.svg svg/speaker_chords.svg svg/steprec.svg svg/filesave.svg svg/filenew.svg svg/fileopen.svg svg/filesaveas.svg svg/fileclose.svg svg/appexit.svg svg/whatsthis.svg svg/exit.svg svg/filefromtemplate.svg svg/ctrl.svg svg/meta.svg svg/note.svg svg/sysex.svg svg/tracktype.svg svg/drop_down_triangle.svg svg/expand_left_right.svg svg/down_vee.svg svg/up_vee.svg svg/arrow_up.svg svg/arrow_down.svg qss/scrollbar_small_vertical.qss svg/audio_stretch.svg svg/audio_resample.svg svg/velocity_per_note.svg svg/velocity_all_notes.svg svg/midi_controller_new.svg svg/midi_controller_select.svg svg/midi_controller_remove.svg svg/down_arrow.svg svg/up_arrow.svg xpm/viewdetailed-128.png xpm/viewlist-128.png xpm/left-128.png xpm/newdirectory-128.png xpm/right-128.png xpm/up-128.png svg/pianoroll.svg svg/arranger.svg svg/waveeditor.svg svg/drumedit.svg svg/masteredit.svg svg/scoreedit.svg svg/listedit.svg svg/anker.svg svg/settings.svg svg/mixer.svg svg/bigtime.svg svg/transport.svg svg/minus.svg svg/plus.svg svg/key.svg svg/track_aux.svg svg/track_group.svg svg/track_output.svg svg/track_input.svg svg/synth.svg svg/info.svg svg/show_fields.svg svg/route_auto_adjust.svg svg/route_sel_source.svg svg/route_destinations.svg svg/route_sel_dest.svg svg/route_sources.svg svg/grid_on.svg svg/delete_sel_tracks.svg svg/duplicate_sel_track.svg svg/duplicate_sel_tracks.svg svg/filesave_revision.svg svg/filesave_project.svg svg/filesave_template.svg svg/range_to_selection.svg svg/quantize.svg svg/scroll_down.svg svg/scroll_left.svg svg/scroll_right.svg svg/scroll_up.svg svg/clear.svg svg/downmix_off.svg svg/downmix_on.svg svg/downmix_track.svg svg/restart.svg svg/snapshot.svg svg/empty_bar.svg svg/midi_init.svg svg/midi_local_off.svg svg/midi_reset.svg svg/midi_input_filter.svg svg/midi_input_remote.svg svg/midi_input_transform.svg svg/midi_input_transpose.svg svg/midi_transform.svg svg/lock.svg svg/midi_import_export.svg svg/midi_sync.svg svg/paste.svg svg/copy.svg svg/cut.svg svg/cpu.svg svg/paste_dialog.svg svg/paste_clone.svg svg/copy_range.svg svg/paste_clone_selected_track.svg svg/paste_selected_track.svg svg/delete.svg svg/eye_grey.svg svg/eye_crossed.svg svg/eye.svg svg/next_part.svg svg/last_part.svg svg/select_inside_loop.svg svg/select_outside_loop.svg svg/deselect_all.svg svg/select_all.svg svg/select_invert.svg svg/select_all_track.svg svg/led_green_dark.svg svg/led_off.svg svg/led_blue.svg svg/led_yellow.svg svg/led_red.svg svg/led_green.svg svg/route_in_midi.svg svg/route_out.svg svg/route_in.svg svg/route_out_midi.svg svg/midi_port.svg svg/router.svg svg/edit_instrument.svg svg/appearance.svg svg/delta.svg svg/synth_gui.svg muse-4.0.0/src/sandbox/000077500000000000000000000000001404105311000146765ustar00rootroot00000000000000muse-4.0.0/src/sandbox/CMakeLists.txt000066400000000000000000000026361404105311000174450ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB plugin_scan_source_files muse_plugin_scan.cpp ) ## ## Define target ## add_executable ( muse_plugin_scan ${plugin_scan_source_files} ) target_link_libraries(muse_plugin_scan plugin_scan_module plugin_cache_writer_module dl ${QT_LIBRARIES} ) ## ## Install location ## install(TARGETS muse_plugin_scan DESTINATION ${CMAKE_INSTALL_PREFIX}/bin ) muse-4.0.0/src/sandbox/muse_plugin_scan.cpp000066400000000000000000000227431404105311000207450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // // muse_plugin_scan.cpp // (C) Copyright 2018 Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include #include #include "config.h" #include "globaldefs.h" #include "plugin_cache_writer.h" #include "xml.h" #include #include "synti/libsynti/mess.h" #ifdef DSSI_SUPPORT #include #endif #ifdef VST_NATIVE_SUPPORT #ifdef VST_SDK_QUIRK #define __cdecl #endif #include "aeffectx.h" //#include //#include #endif // VST_NATIVE_SUPPORT // For debugging output: Uncomment the fprintf section. #define DEBUG_PLUGIN_SCAN(dev, format, args...) // std::fprintf(dev, format, ##args); // #include // static sem_t _vstIdLock; // #include // static QSemaphore _vstIdLock; namespace MusEPluginScan { //--------------------------------------------------------- // loadPluginLib // Returns true on success //--------------------------------------------------------- static bool loadPluginLib(MusEPlugin::PluginScanInfoStruct::PluginType_t types, const char* filename, const char* outfilename, bool do_ports) { DEBUG_PLUGIN_SCAN(stderr, "loadPluginLib: filename:%s\n", filename); //#ifdef VST_NATIVE_SUPPORT // TODO: Copied from vst_native.cpp shell support code (below). // Since it would be executed all the time if VST_NATIVE_SUPPORT was enabled - // we don't yet know what type the lib is! - we'll just try this for all plugins... // Tested with QSemaphore (not sem_wait): This caused it to freeze. Conflicts with QProcess? // sem_wait(&_vstIdLock); //_vstIdLock.acquire(); //#endif // VST_NATIVE_SUPPORT bool found = false; // Open the output file... QFile outfile(outfilename); if(!outfile.exists()) { std::fprintf(stderr, "muse_plugin_scan: the output file does not exist: %s for filename: %s\n", outfilename, filename); return false; } if(!outfile.open(QIODevice::WriteOnly /*| QIODevice::Text*/)) { std::fprintf(stderr, "muse_plugin_scan: failed to open outfilename: %s for filename: %s\n", outfilename, filename); return false; } // Open the library... void* handle = dlopen(filename, RTLD_NOW); if (handle == 0) { std::fprintf(stderr, "muse_plugin_scan: dlopen(%s) failed: %s\n", filename, dlerror()); } else { // Check if it's a DSSI plugin first... #ifdef DSSI_SUPPORT if(!found) { DSSI_Descriptor_Function dssi = NULL; if(types & MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI) { dssi = (DSSI_Descriptor_Function)dlsym(handle, "dssi_descriptor"); if(dssi) { DEBUG_PLUGIN_SCAN(stderr, "loadPluginLib: Is a DSSI library\n"); MusECore::Xml xml(&outfile); xml.header(); int level = 0; level = xml.putFileVersion(level); MusEPlugin::writeDssiInfo(filename, dssi, do_ports, level, xml); xml.tag(1, "/muse"); found = true; } else { DEBUG_PLUGIN_SCAN(stderr, "loadPluginLib: Not a DSSI library...\n"); } } } #endif // Check if it's a MESS plugin... if(!found) { MESS_Descriptor_Function msynth = NULL; if(types & MusEPlugin::PluginScanInfoStruct::PluginTypeMESS) { msynth = (MESS_Descriptor_Function)dlsym(handle, "mess_descriptor"); if(msynth) { DEBUG_PLUGIN_SCAN(stderr, "loadPluginLib: Is a MESS library\n"); MusECore::Xml xml(&outfile); xml.header(); int level = 0; level = xml.putFileVersion(level); MusEPlugin::writeMessInfo(filename, msynth, do_ports, level, xml); xml.tag(1, "/muse"); found = true; } else { DEBUG_PLUGIN_SCAN(stderr, "loadPluginLib: Not a MESS library...\n"); } } } // Check if it's a LADSPA plugin... if(!found) { LADSPA_Descriptor_Function ladspa = NULL; if(types & MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA) { ladspa = (LADSPA_Descriptor_Function)dlsym(handle, "ladspa_descriptor"); if(ladspa) { DEBUG_PLUGIN_SCAN(stderr, "loadPluginLib: Is a LADSPA library\n"); MusECore::Xml xml(&outfile); xml.header(); int level = 0; level = xml.putFileVersion(level); MusEPlugin::writeLadspaInfo(filename, ladspa, do_ports, level, xml); xml.tag(1, "/muse"); found = true; } else { DEBUG_PLUGIN_SCAN(stderr, "loadPluginLib: Not a LADSPA library...\n"); } } } // Check if it's a LinuxVST plugin... #ifdef VST_NATIVE_SUPPORT if(!found) { LinuxVST_Instance_Function getInstance = NULL; if(types & MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST) { getInstance = (LinuxVST_Instance_Function)dlsym(handle, MusEPlugin::VST_NEW_PLUGIN_ENTRY_POINT); if(getInstance) { DEBUG_PLUGIN_SCAN(stderr, "loadPluginLib: Is a LinuxVST library: New entrypoint found:%s\n", MusEPlugin::VST_NEW_PLUGIN_ENTRY_POINT); } else { getInstance = (LinuxVST_Instance_Function)dlsym(handle, MusEPlugin::VST_OLD_PLUGIN_ENTRY_POINT); if(getInstance) { DEBUG_PLUGIN_SCAN(stderr, "loadPluginLib: Is a LinuxVST library: Old entrypoint found:%s\n", MusEPlugin::VST_OLD_PLUGIN_ENTRY_POINT); } } if(getInstance) { // sem_wait(&_vstIdLock); // _vstIdLock.acquire(); // currentPluginId = 0; // bool bDontDlCLose = false; MusECore::Xml xml(&outfile); xml.header(); int level = 0; level = xml.putFileVersion(level); bool success = MusEPlugin::writeLinuxVstInfo(filename, getInstance, do_ports, level, xml); xml.tag(1, "/muse"); if (success) { found = true; } else { std::fprintf(stderr, "muse_plugin_scan: writeLinuxVstInfo(%s) failed\n", filename); found = false; outfile.seek(0); } } else { DEBUG_PLUGIN_SCAN(stderr, "loadPluginLib: Not a LinuxVST library: Entrypoints \"%s\" or \"%s\" not found...\n", MusEPlugin::VST_NEW_PLUGIN_ENTRY_POINT, MusEPlugin::VST_OLD_PLUGIN_ENTRY_POINT); } } } #endif // VST_NATIVE_SUPPORT } if(!found) { MusECore::Xml xml(&outfile); xml.header(); int level = 0; level = xml.putFileVersion(level); MusEPlugin::writeUnknownPluginInfo(filename, level, xml); xml.tag(1, "/muse"); found = true; } //_end: // Flush and close the output file. outfile.close(); // Close the library for now. It will be opened // again when an instance is created. if(handle) dlclose(handle); //#ifdef VST_NATIVE_SUPPORT // sem_post(&_vstIdLock); //_vstIdLock.release(); //#endif // VST_NATIVE_SUPPORT return found; } } // namespace MusEPluginScan //--------------------------------------------------------- // main //--------------------------------------------------------- int main(int argc, char* argv[]) { bool do_ports = false; const char* filename = 0; const char* outfilename = 0; MusEPlugin::PluginScanInfoStruct::PluginType_t types = MusEPlugin::PluginScanInfoStruct::PluginTypeAll; int c; while ((c = getopt(argc, argv, "f:t:o:p")) != EOF) { switch (c) { case 'f': filename = optarg; break; case 'o': outfilename = optarg; break; case 't': types = MusEPlugin::PluginScanInfoStruct::PluginType_t(atoi(optarg)); break; case 'p': do_ports = true; break; default: std::fprintf(stderr, "%s: -t -f -o -p (scan plugin ports)\n", argv[0]); return -1; } } if(!filename || filename[0] == 0) { std::fprintf(stderr, "Error: No filename given\n"); return -1; } if(!outfilename || outfilename[0] == 0) { std::fprintf(stderr, "Error: No output filename given\n"); return -1; } const bool res = MusEPluginScan::loadPluginLib(types, filename, outfilename, do_ports); if(!res) { std::fprintf(stderr, "Error loading plugin: <%s>\n", filename); return 1; } return 0; } muse-4.0.0/src/share/000077500000000000000000000000001404105311000143425ustar00rootroot00000000000000muse-4.0.0/src/share/CMakeLists.txt000066400000000000000000000027311404105311000171050ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= # NOTE: locale/ directory needs to be at the end so that the translations # are scanned before coming to share/locale ADD_SUBDIRECTORY(drummaps) ADD_SUBDIRECTORY(instruments) ADD_SUBDIRECTORY(plugins) ADD_SUBDIRECTORY(pybridge) ADD_SUBDIRECTORY(scripts) ADD_SUBDIRECTORY(templates) ADD_SUBDIRECTORY(wallpapers) ADD_SUBDIRECTORY(scoreglyphs) ADD_SUBDIRECTORY(locale) ADD_SUBDIRECTORY(themes) ADD_SUBDIRECTORY(metronome) ADD_SUBDIRECTORY(rdf) install (FILES didyouknow.txt splash.jpg DESTINATION ${MusE_SHARE_DIR} ) muse-4.0.0/src/share/didyouknow.txt000066400000000000000000000043541404105311000173050ustar00rootroot00000000000000To get started with MusE why don't you visit the
tutorials MusE can add effects in realtime for audio tracks,
check the effect rack on the mixer strip For routing and selection dialogs that allow multiple selections
use ctrl when clicking to keep the dialog open If you regularly make the same type of connections you can make a
template-project with all of your basic settings already prewired.
Just store the song as a template using the project dialog. It's possible to create midi filters or generators in any
language, see instructions and examples under
src/share/scripts in the source. Click on the automation column in the track list to make
automation lines visible for a specific parameter. Adding effects will add more automatable parameters to the
automation column. Most functions in MusE are reachable from the keyboard.
See 'Keyboard Shortcuts...' under 'Settings'.
For instance: A - pointer, D - draw, R - erase, S - cut.
A full list of all shortcuts, formatted for printing (sort of)
is available from the Shortcut dialog. Midi parts can be cloned. All clone parts contain the same data,
change one and all will be changed. Parts can be copied and deleted using the standard cut/copy/paste
but it is also possible to click and drag parts to duplicate.
ctrl+click+drag to duplicate parts
ctrl+alt+click+drag to create clones If it's hard to be precise while moving automation points with the mouse,
trying holding the SHIFT key, the movement will be slower. During recording it is often useful to restart the recording since the previous
take wasn't perfect or you simply want several takes. While recording press
shift+space (default shortcut), a new track is created and the recording is
restarted from the initial position.
If recording was done on multiple tracks, all the armed tracks will be duplicated. MusE 4.x defaults to connecting to Pulse Audio instead of Jack to ease startup
for new users. To connect to Jack change the default sound output on the
Audio configuration tab in Global Settings.
Or use the command line overrides to start the specific driver you wish to use. muse-4.0.0/src/share/drummaps/000077500000000000000000000000001404105311000161725ustar00rootroot00000000000000muse-4.0.0/src/share/drummaps/AKAI_SG01v_Bank_dr1.map000066400000000000000000000510451404105311000220240ustar00rootroot00000000000000 # Drum Set List for AKAI SG01v Bank dr1 # generated by babarosa # please report changes and errors # to muse mailing list BLIP#1 100 16 32 -1 -1 70 90 110 127 27 27 PLATE- 100 16 32 -1 -1 70 90 110 127 28 28 BLIP#2 100 16 32 -1 -1 70 90 110 127 29 29 SCRATCH- 100 16 32 -1 -1 70 90 110 127 30 30 SYN. RIM 100 16 32 -1 -1 70 90 110 127 31 31 MM CLIC C2 100 16 32 -1 -1 70 90 110 127 32 32 MM CLIK C5 100 16 32 -1 -1 70 90 110 127 33 33 BLIP#2 100 16 32 -1 -1 70 90 110 127 34 34 SYN. KICK 100 16 32 -1 -1 70 90 110 127 35 35 SDS5 KICK3 100 16 32 -1 -1 70 90 110 127 36 36 SIDE STICK 100 16 32 -1 -1 70 90 110 127 37 37 SYN. SNRE 2 100 16 32 -1 -1 70 90 110 127 38 38 SYN. CLAPS 100 16 32 -1 -1 70 90 110 127 39 39 SIM. SNRE 100 16 32 -1 -1 70 90 110 127 40 40 MID AC TOM 100 16 32 -1 -1 70 90 110 127 41 41 SYN. CL-HAT 100 16 32 -1 -1 70 90 110 127 42 42 MID AC TOM 100 16 32 -1 -1 70 90 110 127 43 43 SYN. HLF-HAT 100 16 32 -1 -1 70 90 110 127 44 44 MID AC TOM 100 16 32 -1 -1 70 90 110 127 45 45 SYN. OP-HAT 100 16 32 -1 -1 70 90 110 127 46 46 MID AC TOM 100 16 32 -1 -1 70 90 110 127 47 47 MID AC TOM 100 16 32 -1 -1 70 90 110 127 48 48 CRASH 100 16 32 -1 -1 70 90 110 127 49 49 MID AC TOM 100 16 32 -1 -1 70 90 110 127 50 50 RIDE SYMBAL 100 16 32 -1 -1 70 90 110 127 51 51 CRASH 100 16 32 -1 -1 70 90 110 127 52 52 RIDE SYMBAL 100 16 32 -1 -1 70 90 110 127 53 53 TAMBOURINE 100 16 32 -1 -1 70 90 110 127 54 54 CRASH 100 16 32 -1 -1 70 90 110 127 55 55 COWBELL 100 16 32 -1 -1 70 90 110 127 56 56 CRASH 100 16 32 -1 -1 70 90 110 127 57 57 FINGER SNAPS 100 16 32 -1 -1 70 90 110 127 58 58 RIDE SYMBAL 100 16 32 -1 -1 70 90 110 127 59 59 HI CONGA SL 100 16 32 -1 -1 70 90 110 127 60 60 HI CONGA SL 100 16 32 -1 -1 70 90 110 127 61 61 LO CONGA SL 100 16 32 -1 -1 70 90 110 127 62 62 HI CONGA OP 100 16 32 -1 -1 70 90 110 127 63 63 LOW CONGA OP 100 16 32 -1 -1 70 90 110 127 64 64 TIMBAL 100 16 32 -1 -1 70 90 110 127 65 65 TIMBAL 100 16 32 -1 -1 70 90 110 127 66 66 AGOGO 1 100 16 32 -1 -1 70 90 110 127 67 67 AGOGO 1 100 16 32 -1 -1 70 90 110 127 68 68 SYN. MARACAS 100 16 32 -1 -1 70 90 110 127 69 69 MARACAS 100 16 32 -1 -1 70 90 110 127 70 70 100 16 32 -1 -1 70 90 110 127 71 71 POWER KICK 100 16 32 -1 -1 70 90 110 127 72 72 SYN. RIM 100 16 32 -1 -1 70 90 110 127 73 73 POWER SNRE 100 16 32 -1 -1 70 90 110 127 74 74 BIG CLAP 100 16 32 -1 -1 70 90 110 127 75 75 SDS5 SNARE9 100 16 32 -1 -1 70 90 110 127 76 76 MID E.TOM 100 16 32 -1 -1 70 90 110 127 77 77 CL HAT 100 16 32 -1 -1 70 90 110 127 78 78 MID E.TOM 100 16 32 -1 -1 70 90 110 127 79 79 HLF HAT 100 16 32 -1 -1 70 90 110 127 80 80 MID E.TOM 100 16 32 -1 -1 70 90 110 127 81 81 OP HAT 100 16 32 -1 -1 70 90 110 127 82 82 MID E.TOM 100 16 32 -1 -1 70 90 110 127 83 83 MID E.TOM 100 16 32 -1 -1 70 90 110 127 84 84 CRASH 100 16 32 -1 -1 70 90 110 127 85 85 MID E.TOM 100 16 32 -1 -1 70 90 110 127 86 86 RIDE SYMBAL 100 16 32 -1 -1 70 90 110 127 87 87 WHITE NOISE 100 16 32 -1 -1 70 90 110 127 88 88 100 16 32 -1 -1 70 90 110 127 89 89 BLIP#2 100 16 32 -1 -1 70 90 110 127 90 90 SYN. LO-CONGA 100 16 32 -1 -1 70 90 110 127 91 91 SYN. COWBELL 100 16 32 -1 -1 70 90 110 127 92 92 SYN. LO-CONGA 100 16 32 -1 -1 70 90 110 127 93 93 SYN. CLAVES 100 16 32 -1 -1 70 90 110 127 94 94 SYN. LO-CONGA 100 16 32 -1 -1 70 90 110 127 95 95 muse-4.0.0/src/share/drummaps/AKAI_SG01v_Bank_dr2.map000066400000000000000000000510411404105311000220210ustar00rootroot00000000000000 # Drum Set List for AKAI SG01v Bank dr2 # generated by babarosa # please report changes and errors # to muse mailing list BLIP#1 100 16 32 -1 -1 70 90 110 127 27 27 PLATE- 100 16 32 -1 -1 70 90 110 127 28 28 BLIP#2 100 16 32 -1 -1 70 90 110 127 29 29 SCRATCH- 100 16 32 -1 -1 70 90 110 127 30 30 SYN. RIM 100 16 32 -1 -1 70 90 110 127 31 31 MM CLIC C2 100 16 32 -1 -1 70 90 110 127 32 32 MM CLIK C5 100 16 32 -1 -1 70 90 110 127 33 33 BLIP#2 100 16 32 -1 -1 70 90 110 127 34 34 SYN. KICK 100 16 32 -1 -1 70 90 110 127 35 35 SDS5 KICK3 100 16 32 -1 -1 70 90 110 127 36 36 SIDE STICK 100 16 32 -1 -1 70 90 110 127 37 37 SYN. SNRE 2 100 16 32 -1 -1 70 90 110 127 38 38 SYN. CLAPS 100 16 32 -1 -1 70 90 110 127 39 39 SDS5 SNARE9 100 16 32 -1 -1 70 90 110 127 40 40 SIM. SNRE 100 16 32 -1 -1 70 90 110 127 41 41 SYN. CL-HAT 100 16 32 -1 -1 70 90 110 127 42 42 SIM. SNRE 100 16 32 -1 -1 70 90 110 127 43 43 SYN. HLF-HAT 100 16 32 -1 -1 70 90 110 127 44 44 SIM. SNRE 100 16 32 -1 -1 70 90 110 127 45 45 SYN. OP-HAT 100 16 32 -1 -1 70 90 110 127 46 46 SIM. SNRE 100 16 32 -1 -1 70 90 110 127 47 47 SIM. SNRE 100 16 32 -1 -1 70 90 110 127 48 48 CRASH 100 16 32 -1 -1 70 90 110 127 49 49 SIM. SNRE 100 16 32 -1 -1 70 90 110 127 50 50 RIDE SYMBAL 100 16 32 -1 -1 70 90 110 127 51 51 CRASH 100 16 32 -1 -1 70 90 110 127 52 52 RIDE SYMBAL 100 16 32 -1 -1 70 90 110 127 53 53 TAMBOURINE 100 16 32 -1 -1 70 90 110 127 54 54 CRASH 100 16 32 -1 -1 70 90 110 127 55 55 COWBELL 100 16 32 -1 -1 70 90 110 127 56 56 CRASH 100 16 32 -1 -1 70 90 110 127 57 57 FINGER SNAPS 100 16 32 -1 -1 70 90 110 127 58 58 RIDE SYMBAL 100 16 32 -1 -1 70 90 110 127 59 59 HI CONGA SL 100 16 32 -1 -1 70 90 110 127 60 60 HI CONGA SL 100 16 32 -1 -1 70 90 110 127 61 61 LO CONGA SL 100 16 32 -1 -1 70 90 110 127 62 62 HI CONGA OP 100 16 32 -1 -1 70 90 110 127 63 63 LOW CONGA OP 100 16 32 -1 -1 70 90 110 127 64 64 TIMBAL 100 16 32 -1 -1 70 90 110 127 65 65 TIMBAL 100 16 32 -1 -1 70 90 110 127 66 66 AGOGO 1 100 16 32 -1 -1 70 90 110 127 67 67 AGOGO 1 100 16 32 -1 -1 70 90 110 127 68 68 SYN. MARACAS 100 16 32 -1 -1 70 90 110 127 69 69 MARACAS 100 16 32 -1 -1 70 90 110 127 70 70 100 16 32 -1 -1 70 90 110 127 71 71 POWER KICK 100 16 32 -1 -1 70 90 110 127 72 72 SYN. RIM 100 16 32 -1 -1 70 90 110 127 73 73 POWER SNRE 100 16 32 -1 -1 70 90 110 127 74 74 BIG CLAP 100 16 32 -1 -1 70 90 110 127 75 75 SDS5 SNARE9 100 16 32 -1 -1 70 90 110 127 76 76 MID E.TOM 100 16 32 -1 -1 70 90 110 127 77 77 CL HAT 100 16 32 -1 -1 70 90 110 127 78 78 MID E.TOM 100 16 32 -1 -1 70 90 110 127 79 79 HLF HAT 100 16 32 -1 -1 70 90 110 127 80 80 MID E.TOM 100 16 32 -1 -1 70 90 110 127 81 81 OP HAT 100 16 32 -1 -1 70 90 110 127 82 82 MID E.TOM 100 16 32 -1 -1 70 90 110 127 83 83 MID E.TOM 100 16 32 -1 -1 70 90 110 127 84 84 CRASH 100 16 32 -1 -1 70 90 110 127 85 85 MID E.TOM 100 16 32 -1 -1 70 90 110 127 86 86 RIDE SYMBAL 100 16 32 -1 -1 70 90 110 127 87 87 WHITE NOISE 100 16 32 -1 -1 70 90 110 127 88 88 100 16 32 -1 -1 70 90 110 127 89 89 BLIP#2 100 16 32 -1 -1 70 90 110 127 90 90 SYN. LO-CONGA 100 16 32 -1 -1 70 90 110 127 91 91 SYN. COWBELL 100 16 32 -1 -1 70 90 110 127 92 92 SYN. LO-CONGA 100 16 32 -1 -1 70 90 110 127 93 93 SYN. CLAVES 100 16 32 -1 -1 70 90 110 127 94 94 SYN. LO-CONGA 100 16 32 -1 -1 70 90 110 127 95 95 muse-4.0.0/src/share/drummaps/CMakeLists.txt000066400000000000000000000021101404105311000207240ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB drummaps_files *.map) install( FILES ${drummaps_files} DESTINATION ${MusE_SHARE_DIR}/drummaps ) muse-4.0.0/src/share/drummaps/Drumgizmo_CrocellKit.map000066400000000000000000000056251404105311000227710ustar00rootroot00000000000000 Kick L Kick R Snare Rim Snare Snare Rest Snare Rimshot Tom 4 HH Closed Tom 3 HH Pedal 1 1 HH Open Tom 2 Tom 1 Crash L 1 1 Ride China R Ride Bell China L Splash L Crash L Choke Crash R Splash R Crash R 2 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 HH Pedal + Hit 1 Crash R Choke 1 1 HH X-Hat 1 HH Semi-Open 1 muse-4.0.0/src/share/drummaps/KURZWEIL_SP2X_Drum_Map_GM_ReMap.map000066400000000000000000000727731404105311000242340ustar00rootroot00000000000000 # Drum Set List for KURZWEIL SP2X Drum Map GM ReMap # generated by babarosa # please report changes and errors # to muse mailing list 100 16 32 -1 -1 70 90 110 127 27 27 100 16 32 -1 -1 70 90 110 127 28 28 100 16 32 -1 -1 70 90 110 127 29 29 100 16 32 -1 -1 70 90 110 127 30 30 100 16 32 -1 -1 70 90 110 127 31 31 100 16 32 -1 -1 70 90 110 127 32 32 100 16 32 -1 -1 70 90 110 127 33 33 100 16 32 -1 -1 70 90 110 127 34 34 100 16 32 -1 -1 70 90 110 127 35 35 100 16 32 -1 -1 70 90 110 127 36 36 100 16 32 -1 -1 70 90 110 127 37 37 100 16 32 -1 -1 70 90 110 127 38 38 100 16 32 -1 -1 70 90 110 127 39 39 100 16 32 -1 -1 70 90 110 127 40 40 100 16 32 -1 -1 70 90 110 127 41 41 100 16 32 -1 -1 70 90 110 127 42 42 100 16 32 -1 -1 70 90 110 127 43 43 100 16 32 -1 -1 70 90 110 127 44 44 Metronome Click 100 16 32 -1 -1 70 90 110 127 45 45 100 16 32 -1 -1 70 90 110 127 46 46 Kick Drum 2 100 16 32 -1 -1 70 90 110 127 47 47 Kick Drum 1 100 16 32 -1 -1 70 90 110 127 48 48 Side Stick 100 16 32 -1 -1 70 90 110 127 49 49 Snare Drum 1 100 16 32 -1 -1 70 90 110 127 50 50 Hand Clap 100 16 32 -1 -1 70 90 110 127 51 51 Snare Drum 2 100 16 32 -1 -1 70 90 110 127 52 52 Lo Tom 2 100 16 32 -1 -1 70 90 110 127 53 53 Closed Hi Hat 100 16 32 -1 -1 70 90 110 127 54 54 Lo Tom 1 100 16 32 -1 -1 70 90 110 127 55 55 Pedal Hi Hat 100 16 32 -1 -1 70 90 110 127 56 56 Mid Tom 2 100 16 32 -1 -1 70 90 110 127 57 57 Open Hi Hat 100 16 32 -1 -1 70 90 110 127 58 58 Mid Tom 1 100 16 32 -1 -1 70 90 110 127 59 59 Hi Tom 2 100 16 32 -1 -1 70 90 110 127 60 60 Crash Cymbal 1 100 16 32 -1 -1 70 90 110 127 61 61 Hi Tom 1 100 16 32 -1 -1 70 90 110 127 62 62 Ride Cymbal 1 100 16 32 -1 -1 70 90 110 127 63 63 100 16 32 -1 -1 70 90 110 127 64 64 Ride Bell 100 16 32 -1 -1 70 90 110 127 65 65 Tambourine 100 16 32 -1 -1 70 90 110 127 66 66 Splash Cymbal 100 16 32 -1 -1 70 90 110 127 67 67 Cowbell 100 16 32 -1 -1 70 90 110 127 68 68 Crash Cymbal 2 100 16 32 -1 -1 70 90 110 127 69 69 100 16 32 -1 -1 70 90 110 127 70 70 Ride Cymbal 2 100 16 32 -1 -1 70 90 110 127 71 71 Hi Bongo 100 16 32 -1 -1 70 90 110 127 72 72 Lo Bongo 100 16 32 -1 -1 70 90 110 127 73 73 Mute Conga 100 16 32 -1 -1 70 90 110 127 74 74 Hi Conga 100 16 32 -1 -1 70 90 110 127 75 75 Lo Conga 100 16 32 -1 -1 70 90 110 127 76 76 Hi Timbale 100 16 32 -1 -1 70 90 110 127 77 77 Lo Timbale 100 16 32 -1 -1 70 90 110 127 78 78 Hi Agogo 100 16 32 -1 -1 70 90 110 127 79 79 Lo Agogo 100 16 32 -1 -1 70 90 110 127 80 80 Cabasa 100 16 32 -1 -1 70 90 110 127 81 81 Maracas 100 16 32 -1 -1 70 90 110 127 82 82 100 16 32 -1 -1 70 90 110 127 83 83 100 16 32 -1 -1 70 90 110 127 84 84 100 16 32 -1 -1 70 90 110 127 85 85 100 16 32 -1 -1 70 90 110 127 86 86 Clave 100 16 32 -1 -1 70 90 110 127 87 87 100 16 32 -1 -1 70 90 110 127 88 88 100 16 32 -1 -1 70 90 110 127 89 89 100 16 32 -1 -1 70 90 110 127 90 90 100 16 32 -1 -1 70 90 110 127 91 91 Triangle Mute 100 16 32 -1 -1 70 90 110 127 92 92 Triangle Open 100 16 32 -1 -1 70 90 110 127 93 93 Shaker 100 16 32 -1 -1 70 90 110 127 94 94 100 16 32 -1 -1 70 90 110 127 95 95 100 16 32 -1 -1 70 90 110 127 96 96 100 16 32 -1 -1 70 90 110 127 97 97 100 16 32 -1 -1 70 90 110 127 98 98 100 16 32 -1 -1 70 90 110 127 99 99 100 16 32 -1 -1 70 90 110 127 100 100 100 16 32 -1 -1 70 90 110 127 101 101 100 16 32 -1 -1 70 90 110 127 102 102 100 16 32 -1 -1 70 90 110 127 103 103 100 16 32 -1 -1 70 90 110 127 104 104 100 16 32 -1 -1 70 90 110 127 105 105 100 16 32 -1 -1 70 90 110 127 106 106 100 16 32 -1 -1 70 90 110 127 107 107 100 16 32 -1 -1 70 90 110 127 108 108 100 16 32 -1 -1 70 90 110 127 109 109 100 16 32 -1 -1 70 90 110 127 110 110 100 16 32 -1 -1 70 90 110 127 111 111 100 16 32 -1 -1 70 90 110 127 112 112 100 16 32 -1 -1 70 90 110 127 113 113 100 16 32 -1 -1 70 90 110 127 114 114 100 16 32 -1 -1 70 90 110 127 115 115 100 16 32 -1 -1 70 90 110 127 116 116 100 16 32 -1 -1 70 90 110 127 117 117 100 16 32 -1 -1 70 90 110 127 118 118 100 16 32 -1 -1 70 90 110 127 119 119 100 16 32 -1 -1 70 90 110 127 120 120 100 16 32 -1 -1 70 90 110 127 121 121 100 16 32 -1 -1 70 90 110 127 122 122 100 16 32 -1 -1 70 90 110 127 123 123 100 16 32 -1 -1 70 90 110 127 124 124 100 16 32 -1 -1 70 90 110 127 125 125 100 16 32 -1 -1 70 90 110 127 126 126 100 16 32 -1 -1 70 90 110 127 127 127 muse-4.0.0/src/share/drummaps/KURZWEIL_SP2X_Drum_Map_Normal.map000066400000000000000000000733661404105311000240740ustar00rootroot00000000000000 100 16 32 -1 -1 70 90 110 127 27 27 100 16 32 -1 -1 70 90 110 127 28 28 100 16 32 -1 -1 70 90 110 127 29 29 100 16 32 -1 -1 70 90 110 127 30 30 100 16 32 -1 -1 70 90 110 127 31 31 100 16 32 -1 -1 70 90 110 127 32 32 100 16 32 -1 -1 70 90 110 127 33 33 100 16 32 -1 -1 70 90 110 127 34 34 100 16 32 -1 -1 70 90 110 127 35 35 100 16 32 -1 -1 70 90 110 127 36 36 100 16 32 -1 -1 70 90 110 127 37 37 100 16 32 -1 -1 70 90 110 127 38 38 100 16 32 -1 -1 70 90 110 127 39 39 100 16 32 -1 -1 70 90 110 127 40 40 100 16 32 -1 -1 70 90 110 127 41 41 Hard Snare 1 100 16 32 -1 -1 70 90 110 127 42 42 Kick 3 100 16 32 -1 -1 70 90 110 127 43 43 Kick 3 100 16 32 -1 -1 70 90 110 127 44 44 Snare 4 100 16 32 -1 -1 70 90 110 127 45 45 Snare 4 100 16 32 -1 -1 70 90 110 127 46 46 Crash Cymbal 100 16 32 -1 -1 70 90 110 127 47 47 Floor Tom 100 16 32 -1 -1 70 90 110 127 48 48 Floor Tom 100 16 32 -1 -1 70 90 110 127 49 49 Lo Mid Tom 100 16 32 -1 -1 70 90 110 127 50 50 Lo Mid Tom 100 16 32 -1 -1 70 90 110 127 51 51 Mid Tom 100 16 32 -1 -1 70 90 110 127 52 52 Mid Tom 100 16 32 -1 -1 70 90 110 127 53 53 Mid Tom 100 16 32 -1 -1 70 90 110 127 54 54 Hi Tom 100 16 32 -1 -1 70 90 110 127 55 55 Hi Tom 100 16 32 -1 -1 70 90 110 127 56 56 Hi Tom 100 16 32 -1 -1 70 90 110 127 57 57 Hi Tom 100 16 32 -1 -1 70 90 110 127 58 58 Kick 1 100 16 32 -1 -1 70 90 110 127 59 59 Kick 1 100 16 32 -1 -1 70 90 110 127 60 60 Kick 1 100 16 32 -1 -1 70 90 110 127 61 61 Kick 2 100 16 32 -1 -1 70 90 110 127 62 62 Kick 2 100 16 32 -1 -1 70 90 110 127 63 63 Cross Stick Ambient 100 16 32 -1 -1 70 90 110 127 64 64 Cross Stick Ambient 100 16 32 -1 -1 70 90 110 127 65 65 Cross Stick 100 16 32 -1 -1 70 90 110 127 66 66 Snare 3 100 16 32 -1 -1 70 90 110 127 67 67 Snare 3 100 16 32 -1 -1 70 90 110 127 68 68 Snare 2 100 16 32 -1 -1 70 90 110 127 69 69 Snare 2 100 16 32 -1 -1 70 90 110 127 70 70 Soft Snare 1 100 16 32 -1 -1 70 90 110 127 71 71 Dual Snare 1 100 16 32 -1 -1 70 90 110 127 72 72 Dual Snare 1 100 16 32 -1 -1 70 90 110 127 73 73 Closed Hi-hat 100 16 32 -1 -1 70 90 110 127 74 74 Closed Hi-hat 100 16 32 -1 -1 70 90 110 127 75 75 Closed Hi-hat 100 16 32 -1 -1 70 90 110 127 76 76 Slightly Open Hi-hat 100 16 32 -1 -1 70 90 110 127 77 77 Slightly Open Hi-hat 100 16 32 -1 -1 70 90 110 127 78 78 Slightly Open Hi-hat 100 16 32 -1 -1 70 90 110 127 79 79 Open Hi-hat 100 16 32 -1 -1 70 90 110 127 80 80 Open Hi-hat 100 16 32 -1 -1 70 90 110 127 81 81 Open\Closed Hi-hat 100 16 32 -1 -1 70 90 110 127 82 82 Open\Closed Hi-hat 100 16 32 -1 -1 70 90 110 127 83 83 Foot Hi-hat 100 16 32 -1 -1 70 90 110 127 84 84 Crash Cymbal 100 16 32 -1 -1 70 90 110 127 85 85 Crash Cymbal 100 16 32 -1 -1 70 90 110 127 86 86 Crash Cymbal 100 16 32 -1 -1 70 90 110 127 87 87 Crash Cymbal 100 16 32 -1 -1 70 90 110 127 88 88 Crash Cymbal 100 16 32 -1 -1 70 90 110 127 89 89 Crash Cymbal 100 16 32 -1 -1 70 90 110 127 90 90 Splash Cymbal 100 16 32 -1 -1 70 90 110 127 91 91 Ride Cymbal 100 16 32 -1 -1 70 90 110 127 92 92 Ride Cymbal Rim 100 16 32 -1 -1 70 90 110 127 93 93 Ride Cymbal Rim\Bell 100 16 32 -1 -1 70 90 110 127 94 94 Ride Cymbal Bell 100 16 32 -1 -1 70 90 110 127 95 95 Ride Cymbal Bell 100 16 32 -1 -1 70 90 110 127 96 96 Cowbell 100 16 32 -1 -1 70 90 110 127 97 97 Hand Clap 100 16 32 -1 -1 70 90 110 127 98 98 Lo Timbale\Hi Timbale 100 16 32 -1 -1 70 90 110 127 99 99 Timbale Shell 100 16 32 -1 -1 70 90 110 127 100 100 Conga 100 16 32 -1 -1 70 90 110 127 101 101 Hi Tumba 100 16 32 -1 -1 70 90 110 127 102 102 Conga 100 16 32 -1 -1 70 90 110 127 103 103 Lo Tumba 100 16 32 -1 -1 70 90 110 127 104 104 Clave 100 16 32 -1 -1 70 90 110 127 105 105 Shakers 100 16 32 -1 -1 70 90 110 127 106 106 Tambourine 100 16 32 -1 -1 70 90 110 127 107 107 Tambourine 100 16 32 -1 -1 70 90 110 127 108 108 Shakers 100 16 32 -1 -1 70 90 110 127 109 109 Tambourine 100 16 32 -1 -1 70 90 110 127 110 110 Maracas 100 16 32 -1 -1 70 90 110 127 111 111 Maracas 100 16 32 -1 -1 70 90 110 127 112 112 Lo Agogo 100 16 32 -1 -1 70 90 110 127 113 113 Hi Agogo 100 16 32 -1 -1 70 90 110 127 114 114 Lo Bongo 100 16 32 -1 -1 70 90 110 127 115 115 Bongo Slap 100 16 32 -1 -1 70 90 110 127 116 116 Hi Bongo 100 16 32 -1 -1 70 90 110 127 117 117 Finger Snap 100 16 32 -1 -1 70 90 110 127 118 118 Muted Triangle 100 16 32 -1 -1 70 90 110 127 119 119 Open Triangle 100 16 32 -1 -1 70 90 110 127 120 120 100 16 32 -1 -1 70 90 110 127 121 121 100 16 32 -1 -1 70 90 110 127 122 122 100 16 32 -1 -1 70 90 110 127 123 123 100 16 32 -1 -1 70 90 110 127 124 124 100 16 32 -1 -1 70 90 110 127 125 125 100 16 32 -1 -1 70 90 110 127 126 126 100 16 32 -1 -1 70 90 110 127 127 127 muse-4.0.0/src/share/drummaps/NoteNumbers.map000066400000000000000000000162141404105311000211360ustar00rootroot00000000000000 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 muse-4.0.0/src/share/drummaps/addictive_drums.map000066400000000000000000001117141404105311000220440ustar00rootroot00000000000000 100 16 32 -1 -1 70 90 110 127 35 35 Bass Drum 1 100 16 32 -1 -1 70 90 110 127 36 36 Snare1 100 16 32 -1 -1 70 90 110 127 37 37 Snare2 100 16 32 -1 -1 70 90 110 127 38 38 Snare3 100 16 32 -1 -1 70 90 110 127 39 39 Snare4 100 16 32 -1 -1 70 90 110 127 40 40 Snare5H 100 16 32 -1 -1 70 90 110 127 41 41 Snare6 100 16 32 -1 -1 70 90 110 127 43 43 SideStick 100 16 32 -1 -1 70 90 110 127 42 42 SideStick2 100 16 32 -1 -1 70 90 110 127 44 44 Cymbal1 100 16 32 -1 -1 70 90 110 127 46 46 Xtra 100 16 32 -1 -1 70 90 110 127 47 47 HighHatStomp 100 16 32 -1 -1 70 90 110 127 48 48 HighHat1 100 16 32 -1 -1 70 90 110 127 49 49 HighHat2 100 16 32 -1 -1 70 90 110 127 50 50 HighHat3 100 16 32 -1 -1 70 90 110 127 51 51 HighHat4 100 16 32 -1 -1 70 90 110 127 52 52 HighHatBell 100 16 32 -1 -1 70 90 110 127 53 53 HighHatOpen1 100 16 32 -1 -1 70 90 110 127 54 54 HighHatOpen2 100 16 32 -1 -1 70 90 110 127 55 55 HighHatOpen3 100 16 32 -1 -1 70 90 110 127 56 56 HighHatOpen4 100 16 32 -1 -1 70 90 110 127 57 57 HighHatOpen5 100 16 32 -1 -1 70 90 110 127 58 58 HighHatOpenStomp 100 16 32 -1 -1 70 90 110 127 59 59 Ride 100 16 32 -1 -1 70 90 110 127 45 45 Ride2 100 16 32 -1 -1 70 90 110 127 60 60 Ride3 100 16 32 -1 -1 70 90 110 127 61 61 Ride4 100 16 32 -1 -1 70 90 110 127 62 62 RideHold 100 16 32 -1 -1 70 90 110 127 63 63 Tom4_1 100 16 32 -1 -1 70 90 110 127 65 65 Tom4_2 100 16 32 -1 -1 70 90 110 127 66 66 Tom3_1 100 16 32 -1 -1 70 90 110 127 67 67 Tom3_2 100 16 32 -1 -1 70 90 110 127 68 68 Tom2_1 100 16 32 -1 -1 70 90 110 127 69 69 Tom2_2 100 16 32 -1 -1 70 90 110 127 70 70 Tom1_1 100 16 32 -1 -1 70 90 110 127 71 71 Tom1_2 100 16 32 -1 -1 70 90 110 127 72 72 Sticks 100 16 32 -1 -1 70 90 110 127 75 75 Cymbal1 100 16 32 -1 -1 70 90 110 127 77 77 Cymbal1Grab 100 16 32 -1 -1 70 90 110 127 78 78 Cymbal2 100 16 32 -1 -1 70 90 110 127 79 79 Cymbal2Grab 100 16 32 -1 -1 70 90 110 127 80 80 Cymbal3 100 16 32 -1 -1 70 90 110 127 81 81 Cymbal3Grab 100 16 32 -1 -1 70 90 110 127 82 82 100 16 32 -1 -1 70 90 110 127 76 76 100 16 32 -1 -1 70 90 110 127 74 74 100 16 32 -1 -1 70 90 110 127 64 64 100 16 32 -1 -1 70 90 110 127 73 73 100 16 32 -1 -1 70 90 110 127 83 83 100 16 32 -1 -1 70 90 110 127 84 84 100 16 32 -1 -1 70 90 110 127 85 85 100 16 32 -1 -1 70 90 110 127 86 86 100 16 32 -1 -1 70 90 110 127 87 87 100 16 32 -1 -1 70 90 110 127 88 88 100 16 32 -1 -1 70 90 110 127 89 89 100 16 32 -1 -1 70 90 110 127 90 90 100 16 32 -1 -1 70 90 110 127 91 91 100 16 32 -1 -1 70 90 110 127 92 92 100 16 32 -1 -1 70 90 110 127 93 93 100 16 32 -1 -1 70 90 110 127 94 94 100 16 32 -1 -1 70 90 110 127 95 95 100 16 32 -1 -1 70 90 110 127 96 96 100 16 32 -1 -1 70 90 110 127 97 97 100 16 32 -1 -1 70 90 110 127 98 98 100 16 32 -1 -1 70 90 110 127 99 99 100 16 32 -1 -1 70 90 110 127 100 100 100 16 32 -1 -1 70 90 110 127 101 101 100 16 32 -1 -1 70 90 110 127 102 102 100 16 32 -1 -1 70 90 110 127 103 103 100 16 32 -1 -1 70 90 110 127 104 104 100 16 32 -1 -1 70 90 110 127 105 105 100 16 32 -1 -1 70 90 110 127 106 106 100 16 32 -1 -1 70 90 110 127 107 107 100 16 32 -1 -1 70 90 110 127 108 108 100 16 32 -1 -1 70 90 110 127 109 109 100 16 32 -1 -1 70 90 110 127 110 110 100 16 32 -1 -1 70 90 110 127 111 111 100 16 32 -1 -1 70 90 110 127 112 112 100 16 32 -1 -1 70 90 110 127 113 113 100 16 32 -1 -1 70 90 110 127 114 114 100 16 32 -1 -1 70 90 110 127 115 115 100 16 32 -1 -1 70 90 110 127 116 116 100 16 32 -1 -1 70 90 110 127 117 117 100 16 32 -1 -1 70 90 110 127 118 118 100 16 32 -1 -1 70 90 110 127 119 119 100 16 32 -1 -1 70 90 110 127 120 120 100 16 32 -1 -1 70 90 110 127 121 121 100 16 32 -1 -1 70 90 110 127 122 122 100 16 32 -1 -1 70 90 110 127 123 123 100 16 32 -1 -1 70 90 110 127 124 124 100 16 32 -1 -1 70 90 110 127 125 125 100 16 32 -1 -1 70 90 110 127 126 126 100 16 32 -1 -1 70 90 110 127 127 127 100 16 32 -1 -1 70 90 110 127 0 0 100 16 32 -1 -1 70 90 110 127 1 1 100 16 32 -1 -1 70 90 110 127 2 2 100 16 32 -1 -1 70 90 110 127 3 3 100 16 32 -1 -1 70 90 110 127 4 4 100 16 32 -1 -1 70 90 110 127 5 5 100 16 32 -1 -1 70 90 110 127 6 6 100 16 32 -1 -1 70 90 110 127 7 7 100 16 32 -1 -1 70 90 110 127 8 8 100 16 32 -1 -1 70 90 110 127 9 9 100 16 32 -1 -1 70 90 110 127 10 10 100 16 32 -1 -1 70 90 110 127 11 11 100 16 32 -1 -1 70 90 110 127 12 12 100 16 32 -1 -1 70 90 110 127 13 13 100 16 32 -1 -1 70 90 110 127 14 14 100 16 32 -1 -1 70 90 110 127 15 15 100 16 32 -1 -1 70 90 110 127 16 16 100 16 32 -1 -1 70 90 110 127 17 17 100 16 32 -1 -1 70 90 110 127 18 18 100 16 32 -1 -1 70 90 110 127 19 19 100 16 32 -1 -1 70 90 110 127 20 20 100 16 32 -1 -1 70 90 110 127 21 21 100 16 32 -1 -1 70 90 110 127 22 22 100 16 32 -1 -1 70 90 110 127 23 23 100 16 32 -1 -1 70 90 110 127 24 24 100 16 32 -1 -1 70 90 110 127 25 25 100 16 32 -1 -1 70 90 110 127 26 26 100 16 32 -1 -1 70 90 110 127 27 27 100 16 32 -1 -1 70 90 110 127 28 28 100 16 32 -1 -1 70 90 110 127 29 29 100 16 32 -1 -1 70 90 110 127 30 30 100 16 32 -1 -1 70 90 110 127 31 31 100 16 32 -1 -1 70 90 110 127 32 32 100 16 32 -1 -1 70 90 110 127 33 33 100 16 32 -1 -1 70 90 110 127 34 34 muse-4.0.0/src/share/drummaps/dx200_drum.map000066400000000000000000000261561404105311000205670ustar00rootroot00000000000000 PulseBass C100 PulseBass C#111 PulseBass D122 PulseBass D#133 PulseBass E144 PulseBass F155 PulseBass F#166 PulseBass G177 PulseBass G#188 PulseBass A199 PulseBass A#11010 PulseBass B11111 SineBass C11212 SineBass C#11313 SineBass D11414 SineBass D#11515 SineBass E11616 SineBass F11717 SineBass F#11818 SineBass G11919 SineBass G#12020 SineBass A12121 SineBass A#12222 SineBass B12323 PickBass C12424 PickBass C#12525 PickBass D12626 PickBass D#12727 PickBass E12828 PickBass F12929 PickBass F#13030 PickBass G13131 PickBass G#13232 PickBass A13333 PickBass A#13434 PickBass B13535 BD Analog 80813636 BD R&B 113737 BD R&B 213838 BD Lo-Fi13939 BD Jungle14040 BD Hip 114141 BD Hip 214242 BD Tech14343 BD Dist 114444 BD Dist 214545 BD Human 114646 BD Human 214747 BD Elec 114848 BD Elec 2 90914949 BD Elec 3 90915050 SD Live15151 SD R&B 115252 SD R&B 215353 SD Analog 80815454 SD Hip 115555 SD Hip 2 Lo-Fi15656 SD Hip 315757 SD Cut Jungle15858 SD Dodge15959 SD Timbra16060 SD D&B16161 SD Dist 90916262 SD Elec 1 90916363 SD Elec 2 90916464 SD Rim 116565 SD Rim 216666 HH D&B Cls16767 HH D&B Opn16868 HH Ana Cls 1 90916969 HH Ana Opn 1 80817070 HH Syn Cls17171 HH Syn Opn17272 HH Ana Cls 2 90917373 HH Ana Opn 2 90917474 Tom Dist 90917575 Tom Ana 117676 Tom Ana 2 90917777 Tom Synth17878 Tom Sine17979 Crush Sym18080 Ride Sym18181 Ride Bell18282 Tambourine18383 Tabla Open18484 Taba Mute18585 Tabla Nah18686 Udu Low18787 Udu High18888 Udu Finger18989 Clave 80819090 Maracas19191 Shaker19292 Clap 90919393 Scratch 119494 Scratch 219595 Scratch 319696 Scratch 419797 Ripper19898 Zap 119999 Zap 21100100 Rev Low1101101 Synth Vibra1102102 Metal1103103 Click1104104 Gt Attack1105105 Gt Power1106106 Stab Organ1107107 SlowBass1108108 FingerBass1109109 SynthBass 11110110 SynthBass 21111111 SynthBass 31112112 SynthBass 41113113 Digi Wave 11114114 Digi Wave 21115115 Digi Wave 31116116 Digi Wave 41117117 Digi Wave 51118118 Digi Wave 61119119 Digi Wave 71120120 muse-4.0.0/src/share/drummaps/sc88-pc1.map000066400000000000000000000153771404105311000201540ustar00rootroot00000000000000 # Drum Set List SC-88 Drum Set 1 # # "Program Change 1" # "Standard Set 1" # Snare Roll 25 25 Finger Snare 26 26 High Q 27 27 Slap 28 28 Scratch Push 29 29 Scratch Pull 30 30 Sticks 31 31 Square Click 32 32 Metronome Click 33 33 Metronome Bell 34 34 Standard 1 Kick 2 35 35 Standard 1 Kick 1 36 36 Side Stick 37 37 Standard 1 Snare 1 38 38 Hand Clap 39 39 Standard 1 Snare 2 40 40 Low Tom 2 41 41 Closed Hi-hat 1 42 42 Low Tom 1 43 43 Pedal Hi-hat 44 44 Mid Tom 2 45 45 Open Hi-hat 2 46 46 Mid Tom 1 47 47 High Tom 2 48 48 Crash Cymbal 1 49 49 High Tom 1 50 50 Ride Cymbal 1 51 51 Chinese Cymbal 52 52 Ride Bell 53 53 Tambourine 54 54 Splash Cymbal 55 55 Cowbell 56 56 Crash Cymbal 2 57 57 Vibra Slap 58 58 Ride Cymbal 2 59 59 High Bongo 60 60 Low Bongo 61 61 Mute High Conga 62 62 Open High Conga 63 63 Low Conga 64 64 High Timbale 65 65 Low Timbale 66 66 High Agogo 67 67 Low Agogo 68 68 Cabasa 69 69 Maracas 70 70 Short Hi Whistle 71 71 Long Low Whistle 72 72 Short Guiro 73 73 Long Guiro 74 74 Claves 75 75 High Wood Block 76 76 Low Wood Block 77 77 Mute Cuica 78 78 Open Cuica 79 79 Mute Triangle 80 80 Open Triangle 81 81 Shaker 82 82 Jingle Bell 83 83 Bell Tree 84 84 Castanets 85 85 Mute Surdo 86 86 Open Surdo 87 87 muse-4.0.0/src/share/instruments/000077500000000000000000000000001404105311000167355ustar00rootroot00000000000000muse-4.0.0/src/share/instruments/Access_Virus.idf000066400000000000000000001673021404105311000220230ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Akai-SG01v.idf000066400000000000000000000421771404105311000211370ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Alesis-QS-78R.idf000066400000000000000000001237161404105311000215520ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/AlesisQS6.idf000066400000000000000000001205021404105311000211730ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/CMakeLists.txt000066400000000000000000000021211404105311000214710ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB instruments_files *.idf) install( FILES ${instruments_files} DESTINATION ${MusE_SHARE_DIR}/instruments ) muse-4.0.0/src/share/instruments/Classic_cantabile_sp-250.idf000066400000000000000000002560101404105311000240160ustar00rootroot00000000000000 High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Acoustic Bass Drum Bass Drum 1 Side Stick Acoustic Snare Hand Clap Electric Snare Low Floor Tom Closed Hi-hat High Floor Tom Pedal Hi-hat Low Tom Open Hi-hat Low-Mid Tom High Mid Tom High Tom 1 China Cymbal Ride Bell Tambourine Cowbell Vibra-slap High Bongo Low Bongo Low Conga Low Timbale High Agogo Low Agogo Cabasa Maracas Short Guiro Long Guiro Claves LowWood Block Mute Cuica Open Cuica Shaker Jingle Bell Bell Tree Castanets Mute Surdo Open Surdo Brush Snare 1 Brush Snare 2 Brush Swirl l Snare Roll Castanets Room Snare 1 Sticks Kick Pop Snare High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Room Kick 2 Room Kick 1 Side Stick Room Snare 1 Hand Clap Room Snare 2 Room LowTom2 Closed Hi-hat Room LowTom1 Pedal Hi-hat Room MidTom2 Open Hi-hat Room MidTom1 Room Hi Tom2 Room Hi Tom1 China Cymbal Ride Bell Tambourine Cowbell Vibra-slap High Bongo Low Bongo Low Conga Low Timbale High Agogo Low Agogo Cabasa Maracas Short Guiro Long Guiro Claves LowWood Block Mute Cuica Open Cuica Shaker Jingle Bell Bell Tree Castanets Mute Surdo Open Surdo Brush Snare 1 Brush Snare 2 Brush Swirl l Snare Roll Castanets Room Snare 1 Sticks Kick Pop Snare High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Power Kick Drum Side Stick Power Snare Drum Hand Clap Electric Snare PowerLowTom2 Closed Hi-hat PowerLowTom1 Pedal Hi-hat PowerMidTom2 Open Hi-hat PowerMidTom1 Power HiTom2 Power HiTom1 China Cymbal Ride Bell Tambourine Cowbell Vibra-slap High Bongo Low Bongo Low Conga Low Timbale High Agogo Low Agogo Cabasa Maracas Short Guiro Long Guiro Claves LowWood Block Mute Cuica Open Cuica Shaker Jingle Bell Bell Tree Castanets Mute Surdo Open Surdo Brush Snare 1 Brush Snare 2 Brush Swirl l Snare Roll Castanets Room Snare 1 Sticks Kick Pop Snare High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Electric Bass Drum 2 Electric Bass Drum Side Stick Electric Snare 1 Hand Clap Electric Snare 2 Electric Low Tom 2 Closed Hi-hat Electric Low Tom 1 Pedal Hi-hat Electric Mid Tom 2 Open Hi-hat Electric Mid Tom 1 Electric Hi Tom 2 High Tom 1 ReverseCymbal Ride Bell Tambourine Cowbell Vibra-slap High Bongo Low Bongo Low Conga Low Timbale High Agogo Low Agogo Cabasa Maracas Short Guiro Long Guiro Claves LowWood Block Mute Cuica Open Cuica Shaker Jingle Bell Bell Tree Castanets Mute Surdo Open Surdo Brush Snare 1 Brush Snare 2 Brush Swirl l Snare Roll Castanets Room Snare 1 Sticks Kick Pop Snare High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Analog Bass Drum2 Analog Bass Drum Analog Rim Shot Analog Snare 1 Analog Clap Analog Snare 2 Analog Low Tom2 Analog Closed Hi-hat Analog Low Tom1 Analog ClosedHi-hat Analog Mid Tom2 Analog Closed Hi-hat Analog Mid Tom1 Analog Hi Tom2 Analog Cymbal Analog Hi Tom1 China Cymbal Ride Bell Tambourine Analog Cowbell Vibra-slap High Bongo Low Bongo Analog Hi Conga Analog Mid Conga Analog Low Conga Low Timbale High Agogo Low Agogo Cabasa Analog Maracas Short Guiro Long Guiro Analog Claves LowWood Block Mute Cuica Open Cuica Shaker Jingle Bell Bell Tree Castanets Mute Surdo Open Surdo Brush Snare 1 Brush Snare 2 Brush Swirl l Snare Roll Castanets Room Snare 1 Sticks Kick Pop Snare High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Jazz Kick 2 Jazz Kick 1 Side Stick Jazz Snare 1 Hand Clap Jazz Snare 2 Low Floor Tom Closed Hi-hat High Floor Tom Pedal Hi-hat Low Tom Open Hi-hat Low-Mid Tom High Mid Tom High Tom 1 China Cymbal Ride Bell Tambourine Cowbell Vibra-slap High Bongo Low Bongo Low Conga Low Timbale High Agogo Low Agogo Cabasa Maracas Short Guiro Long Guiro Claves LowWood Block Mute Cuica Open Cuica Shaker Jingle Bell Bell Tree Castanets Mute Surdo Open Surdo Brush Snare 1 Brush Snare 2 Brush Swirl l Snare Roll Castanets Room Snare 1 Sticks Kick Pop Snare High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Jazz Kick 2 Jazz Kick 1 Side Stick Brush Tap Hand Clap Brush Slap Brush Low Tom2 Closed Hi-hat Brush Low Tom1 Pedal Hi-hat Brush Mid Tom2 Open Hi-hat Brush Mid Tom1 Brush Hi Tom2 Brush Hi Tom1 China Cymbal Ride Bell Tambourine Cowbell Vibra-slap High Bongo Low Bongo Low Conga Low Timbale High Agogo Low Agogo Cabasa Maracas Short Guiro Long Guiro Claves LowWood Block Mute Cuica Open Cuica Shaker Jingle Bell Bell Tree Castanets Mute Surdo Open Surdo Brush Snare 1 Brush Snare 2 Brush Swirl l Snare Roll Castanets Room Snare 1 Sticks Kick Pop Snare Closed Hi-hat 2 Pedal Hi-hat Open Hi-hat 2 Ride Cymbal 1 Sticks Square Click Metronome Click Metronome Bell Concert BD 2 Concert BD 1 Side Stick Concert SD Castanets Concert SD Timpani F Timpani F# Timpani G Timpani G# Timpani A Timpani A# Timpani B Timpani c Timpani c# Timpani d Timpani d# Timpani e Timpani f Tambourine Cowbell Concert Cymbal 2 Vibra-slap Concert Cymbal 1 High Bongo Low Bongo Low Conga Low Timbale High Agogo Low Agogo Cabasa Maracas Short Guiro Long Guiro Claves LowWood Block Mute Cuica Open Cuica Shaker Jingle Bell Bell Tree Castanets Mute Surdo Open Surdo Applause Brush Snare 2 Brush Swirl l Snare Roll Castanets Room Snare 1 Sticks Kick Pop Snare High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Guitar Fret Noise Guitar Cutting Noise Up Guitar Cutting Noise Down String Slap Of Double Bass Fl.Key Click Laughing Scream Punch Heart Beat Footsteps 1 Footsteps 2 Applause Door Creaking Door Scratch Wind Chimes Car-Engine Car-Stop Car-Pass Car-Crash Siren Train Jetplane Helicopter Starship Gun Shot Machine Gun Lasergun Explosion Dog Horse-Gallop Birds Rain Thunder Wind Seashore Stream Bubble Guitar fx 1 Guitar fx 2 Guitar fx 3 Guitar fx 4 Guitar fx 5 muse-4.0.0/src/share/instruments/Drumgizmo - CrocellKit.idf000066400000000000000000000074521404105311000235370ustar00rootroot00000000000000 Kick L Kick R Snare Rim Snare Snare Rest Snare Rimshot Tom 4 HH Closed Tom 3 HH Pedal HH OpenL Tom 2 Tom 1 Crash L Ride R China R Ride R Bell China L Splash L Crash L Stop Crash R Splash R Crash R Extra 1 1 1 1 1 1 1 1 1 1 1 1 1 HH Pedal Hit 1 Crash R Stop 1 1 HH Cl No PedalL 1 HH Semi-Open 1 muse-4.0.0/src/share/instruments/Emu-4mbgsgmmt-sf.idf000066400000000000000000000621211404105311000224560ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Hammond_XB-1.idf000066400000000000000000000104011404105311000215270ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/KORG microSTATION combinations.idf000077500000000000000000000434361404105311000250020ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/KORG microSTATION.idf000066400000000000000000001274371404105311000223350ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Korg Krome (Bank Map GM2).idf000066400000000000000000015470431404105311000234200ustar00rootroot00000000000000 Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Closed Hi-Hat-Whispy Hi-Hat Dance 4 Open Timbales-Paila / Shaker Hi-Hat-Crisp Open/Chili/ Crisp Open Hi-Hat-Vintage Hi-Hat 2-Pedal Open/Closed/Pedal BD-Dance 04 L/R BD-Dance 02 BD-Dance 04 Dance 07 BD-Dance 10 Orch.SD Roll 1 Orch.SD Roll 2 Orch.SD Roll 1 Orch.SD Roll 2 SD-Sidestick Hi Cymbal Reverse SD-Reverse IFX1 BD-01a-dry BD-03a-dry BD-03a-dry BD-02b-dry BD-02a-dry SD-06-dry SD-05-dry SD-04-dry SD-03-dry SD-02-dry SD-07-dry BD-01b-dry BD-01a-dry Sidestick-dry SD-01a-dry Hand Claps 1 SD-01b-dry Tom Lo-dry HiHat-Closed-dry Tom Lo-dry HiHat-Pedal1-dry Tom Mid-dry HiHat-Open-dry Tom Mid-dry Tom Hi-dry Crash 01-dry Tom Hi-dry Ride 01-dry China-dry Ride 02-dry Tambourine 3/1-Push/1-Pull Splash-dry Cowbell 2 Crash 02-dry Vibraslap Ride Bongo 2 Hi-Open f/mf/f Bongo 2 Lo-Muffled f/mp/Lo-Closed Conga 2 Hi-ClosedSlap/Hi-Closed Conga 2 Hi-OpenSlap/mf/f/mp Conga 2 Lo-OpenSlap/mf/f Timbales Hi-Rim 1/2/Hi-Edge Timbales Lo-Rim/Open Agogo Bell IFX5 Off Agogo Bell IFX5 Off Cabasa-Up/Down Maracas-Pull/Push Samba Whistle 2 Samba Whistle 2 Guiro-Short Guiro-Long Claves Woodblock 1 Woodblock 1 Cuica 2-Hi Cuica 2-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause BD-Dance 13-EXs5 BD-Dance 12-EXs5 BD-Mad Analog 03-EXs5 BD-HipHop 07-EXs5 BD-Dance 15-EXs5 SD-Mad Analog 11-EXs5 SD-Granular 05-EXs5-R SD-Mad Analog 05-EXs5 SD-Dance 12-EXs5 SD-Mad Analog 13-EXs5 BD-Mad Analog 06-EXs5 BD-Mad Analog 07-EXs5 BD-Dance 11 BD-Dance 03 BD-Dance 08 BD-Dance 01 BD-Dance 07/Tom-Dance SD-Dance 4 (Old) SD-Dance 5 Noise-White/SD-Sidestick Ambi SD-Rap 2 Dance Claps 3/Click SD-Dance 6 BD-Dance 06 (R&B) BD-Dance 07 Rimshot-Dance 2 SD-Dance 3 (New) Dance Claps 1 SD-Dance 2/SD-Sidestick Dry Tom-Dance Hi-Hat Dance 3 Closed Tom-Dance Hi-Hat Dance 3 Open Tom-Dance Hi-Hat Dance 3 Open Dance Perc 8 Maraca Hi-Hat Dance 4 Closed Crash-Dance 3 Hi-Hat Dance 4 Open Ride-Dance 1 Crash-Dance 2 DDD1 Ride-Dance 2 DDD1 Tambourine 3/2 Noise-VPM Mod Dance Perc 7 Cowbell Crash-Dance 1 Cricket Spectrum/Tambourine 3 Perc-Mad Analog 06-EXs5 Zap 1 Zap 2 Dance Perc 6 Clave/BD-Dance 05 (Rap) Dance Perc 5 Conga Dance Perc 5 Conga HiHat Mad Analog 01-EXs5 Triangle-Mute/BD-Dance 05 (Rap) HiHat Mad Analog 16-EXs5 Triangle-Open/Tom-Brush Hi Hi-Hat Dance 5 Closed/DJ-SD Rub Hi-Hat Dance 5 Open Rimshot-Dance 1 PC Vox-Cks/BD-Woofer Hi-Hat Dance 1 Closed/BD-Dry 1 Noise-White/SD-Amb.Piccolo 1 Hi-Hat Dance 2 Open/DJ-Record Stop PC Vox-Sis/SD-Paper PC Vox-Dunc/Vocal SD Hi-Hat-Alpo Closed PC Vox-For/Tom-Brush Hi Hi-Hat-Amb Crackle/Ride-Dance 1 PC Vox-Zee/Tom-Jazz Hi Rim Hi-Hat-Crisp Open/Ride-Dance 1 PC Vox-Pa/Swish Terra PC Vox-Tu/BD-Jazz 2 Crash Cymbal 2 China Cymbal Ride Cymbal 4 Edge Conga 1 Hi-Slap 1/Lo-Open Conga 1 Hi-Open/Lo-MuteSlap Ride Cymbal Cup 2 Conga 1 Hi-Slap 2/Hi-Mute Cowbell Chacha Bell Cowbell 1 Guiro-Short/BD-Dry 1 Guiro-Tap Metal Hit/Finger Cymbal Dance Perc 1 Hip/BD-Dry 1 Swish Terra Cyber Drum Tambourine 1-Push Air Vortex Tubular Telephone Ring Footstep 2 Triangle-Mute Tribe Dance Perc 1 Hip/BD-Dry 1 BD-amb 1 mf -L Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Open Hi-Hat-Chili Hi-Hat-Old Open Timbales-Paila Hi-Hat-Crisp Open Hi-Hat-Vintage 1 Hi-Hat 3-Pedal Open BD-Dance 01-EXs5 BD-Dance 14-EXs5 BD-TekClub 12-EXs5 BD-HipHop 08b/a-EXs5 BD-Hip 2 SD-Dance 09-EXs5 SD-TekClub 04-EXs5 SD-HipHop 07-EXs5 SD-Mad Analog 15-EXs5 SD-TekClub 16-EXs5 Cymbal Reverse SD-Ringy BD-Dance 15-EXs5 BD-Dance 10-EXs5 BD-TekClub 03-EXs5 BD-Dance 08-EXs5 BD-Dance 11-EXs5 SD-Dance 14-EXs5 SD-Dance 19-EXs5 SD-Dance 12-EXs5 SD-Dance 20-EXs5 SD-Dance 21-EXs5 SD-Dance 18/17-EXs5 BD-Dance 12-EXs5 BD-Dance 13-EXs5 SD-Dance 13-EXs5 SD-Dance 15-EXs5 SD-Dance 16-EXs5 SD-Dance 14-EXs5 Tom-Dance d-EXs5 Hi-Hat Dance 06-EXs5 Tom-Dance d-EXs5 Hi-Hat Dance 07-EXs5 Tom-Dance e-EXs5 Hi-Hat Dance 11-EXs5 Tom-Dance f-EXs5 Tom-Dance g-EXs5 Crash Dance 1-EXs5 Tom-Dance h-EXs5 Crash Dance 4-EXs5 Crash Dance 3-EXs5 Ride HipHop 2-EXs5 Tambourine 3/1-EXs5 Crash Dance 2-EXs5 Perc-Dance 01-EXs5 Crash HipHop 1-EXs5-R Perc-Dance 09-EXs5 Ride HipHop 1-EXs5 Perc-Dance 02-EXs5 Perc-Dance 03-EXs5 Perc-Dance 12-EXs5 Perc-Dance 05-EXs5 Perc-Dance 04-EXs5 Hi-Hat Dance 09-EXs5 Hi-Hat Dance 10-EXs5 Perc-Mad Analog 19-EXs5 Perc-Mad Analog 19-EXs5 Shaker 4-EXs5 Shaker 5-EXs5 Orchestral BD Orchestral BD Zap-EXs5 Perc-Dance 07-EXs5 Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Closed Hi-Hat-Whispy Hi-Hat Dance 4 Open Timbales-Paila / Shaker Hi-Hat-Crisp Open/Chili/ Crisp Open Hi-Hat-Vintage Hi-Hat 2-Pedal Open/Closed/Pedal BD-Dance 04 L/R BD-Dance 02 BD-Dance 04 Dance 07 BD-Dance 10 Orch.SD Roll 1 Orch.SD Roll 2 Orch.SD Roll 1 Orch.SD Roll 2 SD-Sidestick Hi Cymbal Reverse SD-Reverse IFX1 BD-01a-dry BD-03a-dry BD-03a-dry BD-02b-dry BD-02a-dry SD-06-dry SD-05-dry SD-04-dry SD-03-dry SD-02-dry SD-07-dry BD-01b-dry BD-01a-dry Sidestick-dry SD-01a-dry Hand Claps 1 SD-01b-dry Tom Lo-dry HiHat-Closed-dry Tom Lo-dry HiHat-Pedal1-dry Tom Mid-dry HiHat-Open-dry Tom Mid-dry Tom Hi-dry Crash 01-dry Tom Hi-dry Ride 01-dry China-dry Ride 02-dry Tambourine 3/1-Push/1-Pull Splash-dry Cowbell 2 Crash 02-dry Vibraslap Ride Bongo 2 Hi-Open f/mf/f Bongo 2 Lo-Muffled f/mp/Lo-Closed Conga 2 Hi-ClosedSlap/Hi-Closed Conga 2 Hi-OpenSlap/mf/f/mp Conga 2 Lo-OpenSlap/mf/f Timbales Hi-Rim 1/2/Hi-Edge Timbales Lo-Rim/Open Agogo Bell IFX5 Off Agogo Bell IFX5 Off Cabasa-Up/Down Maracas-Pull/Push Samba Whistle 2 Samba Whistle 2 Guiro-Short Guiro-Long Claves Woodblock 1 Woodblock 1 Cuica 2-Hi Cuica 2-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause HHat-Dance5-amb1 HHat-Dance7-amb1 HHat-Whispy-amb1 HHat-Dance4/1-amb1 TimbalesH-Paila-a1 / Shaker 1-amb1 H.Hat-Crisp/Chili-amb1 H.Hat-Old-amb1 HHat-Pedal3/2-amb1 BD-amb-01 BD-amb-02 BD-amb-03 BD-amb-04 BD-amb-05 SD-Orch Roll-amb1 SD-Orch Roll-amb1 SD-Orch Roll-amb1 SD-Orch Roll-amb1 Stick Hit-amb Reverse Cymbal-amb SD Reverse-amb BD-01a-amb1 BD-03b-amb1 BD-03a-amb1 BD-02b-amb1 BD-02a-amb1 SD-06-amb1 SD-05-amb1 SD-04-amb1 SD-03-amb1 SD-02-amb1 SD-07-amb1 BD-01b-amb1 BD-01a-amb1 Sidestick-amb1 SD-01a-amb1 Claps-Dance-amb1 SD-01b-amb1 Tom Lo-amb1 HiHat-Cl-amb1 Tom Lo-amb1 HHat-Pdl1-amb1 Tom Mid-amb1 HiHat-Op-amb1 Tom Mid-amb1 Tom Hi-amb1 Crash 01-amb1 Tom Hi-amb1 Ride 01-amb1 China-amb1 Ride 02-amb1 Tambourine 3/1/2-amb1 Splash-amb1 Cowbell 2-amb1 Crash 02-amb1 Vibra-Slap-amb1 Ride 03-amb1 Bongo H-Op f-amb1 Bongo L-Muf1/L-Muf2/Lo-Cl-amb1 Conga H-Slp2/Hi-Op1-amb1 Conga H-Slp1/Hi-Op2/Hi-Op3/Hi-Op1-amb1 Conga L-Slap/Lo-Op1/Lo-Op2-amb1 Timbales H-Rim2/H-Rim1/H-Edge-a1 Timbales L-Ege1/L-Op-a1 Agogo Lo-amb1 Agogo Hi-amb1 Cabasa 1/3-amb1 Maracas-Pull1-amb1 Whistle 3-amb1 Whistle 3-amb1 Guiro-Short a-amb1 Guiro-Long a-amb1 Claves b-amb1 Woodblock 1/2-amb1 Woodblock 3/4-amb1 Cuica Hi c-amb1 Cuica Lo-amb1 Triangle-Mute1/2-a1 Triangle-Mute1/2-a1 Shaker 1/2-amb1 Marc Tree-amb1 BD-Dance-amb-01 HHat Dance8-amb1 SD-Dance-amb1 HHat Dance7-amb1 Crash-Dance-amb1 Perc-Dance1-amb1 Claps-Dance-amb1 Perc-Dance2-amb1 SD-Orch Roll-amb1 SD-Orch-amb1 Bell Tree-amb1 Stadium-amb1 Applause-amb1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute1/3 Baya-Mute5/4 Tabla-Open Tabla-Tin/Mute4 Drumstick Hit/BD-Dry 1 Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry BD-Dry 1 BD-Dry 4 BD-Soft BD-Soft Room BD-Pillow SD-Full Room SD-Classic Room SD-Amb.Piccolo 1 SD-Paper SD-Dry 4 SD-Dry 2 BD-Dry 3 BD-Dry 2 SD-Sidestick Dry SD-Amb.Piccolo 2 Hand Claps 2 SD-Big Rock Tom 3-Floor Hi-Hat 2-Closed 2 Tom 3-Floor Hi-Hat 2-Pedal Open Tom 3-Lo Hi-Hat 2-Open 1/2 Tom 3-Lo Tom 3-Hi Crash Cymbal 3/BD-Dry 1 Tom 3-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Tambourine 2 Splash Cymbal Cowbell 1/BD-Dry 1 Crash Cymbal 2/BD-Dry 1 Vibraslap/BD-Dry 1 Ride Cymbal 5 Edge Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/Toe Conga 1 Hi-Open Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Agogo Bell/BD-Dry 1 Agogo Bell/BD-Dry 1 Cabasa-Up/Down Maracas-Push Samba Whistle 1/Chinese Gong Samba Whistle 1/Chinese Gong Guiro-Short/Tap Guiro-Long/BD-Dry 1 Claves/BD-Dry 1 Woodblock 1/BD-Dry 1 Woodblock 1/BD-Dry 1 Cuica/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) / 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute/3 Baya-Mute5/4 Tabla-Open Tabla-Tin/Mute 4 Drumstick Hit/BD-Dry 1 Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-Tubby BD-Gated BD-Chili BD-Hip 4 BD-PhatJaz SD-Chili/SD-Jazz Ring SD-Ambi Hit 2/SD-Classic Room SD-Brasser/SD-Paper SD-Vintage 4/SD-Brush Tap 1 SD-Hip 5 SD-GhostSet roll BD-Dry 6 BD-Dry 5 SD-Sidestick Ambi SD-GhostSet f/p2 Hand Claps 2 SD-Klanger/GhostSet p1 Tom 2-Floor Hi-Hat 3-Closed 2/1 Tom 2-Floor Hi-Hat 3-Pedal Open / Pedal Tom 2-Lo Hi-Hat 3-Sizzle/Open 1 Tom 2-Lo Tom 2-Hi Crash Cymbal 3/BD-Dry 1 Tom 2-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Tambourine 2 / 1-Push Splash Cymbal Cowbell 1/BD-Dry 1 Crash Cymbal 2/BD-Dry 1 Vibraslap/BD-Dry 1 Ride Cymbal 5 Edge Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open/Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Agogo Bell/BD-Dry 1 Agogo Bell/BD-Dry 1 Cabasa-Up/Down Maracas-Push Samba Whistle 1/Chinese Gong Samba Whistle 1/Chinese Gong Guiro-Short/Tap Guiro-Long/BD-Dry 1 Claves/BD-Dry 1 Woodblock 1/BD-Dry 1 Woodblock 1/BD-Dry 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open/BD-Dry 1 Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) / 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap?Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Mute 4 Drumstick Hit Cymbal Reverse/SD-Reverse SD-Reverse BD-Tight/BD-amb 2 mf -L BD-Squash/BD-amb 2 f -L BD-Gated/BD-amb 1 f -L BD-Tubby/BD-amb 1 mf -L BD-Tight/BD-amb 2 mf -L SD-Full Room/SD-dry 1 f -L SD-Dry 2/SD-dry 1 mp -L SD-Amb.Piccolo 2/SD-dry 1 fff -L SD-Dry 3/SD-dry 1 mp -L SD-GhostSet f/SD-dry 1 f -L SD-Yowie/SD-dry 1 f -L BD-Dry 3/BD-amb 2 mf -L BD-Dry 2/BD-amb 2 f -L SD-Sidestick Ambi/SD-Sidestick mf -L SD-Sidestick Ambi/SD-dry 1 f -L Hand Claps 2/1 -L SD-GhostSet f/SD-dry 1 p -L Tom 2-Floor/Tom 1-Lo f -L Hi-Hat 1-Closed ff/mf Tom 2-Floor/Tom 1-MidLo f -L Hi-Hat 1-Pedal ff/1-Pedal mf Tom 2-Floor/Tom 1-MidHi f -L Hi-Hat 1-Open ff/mf Tom 2-Floor/Tom 1-MidHi f -L Tom 3-Floor/Tom 1-Hi mf -L Crash Cymbal 3/1 mf -L Tom 3-Floor/Tom 1-Hi mf -L Ride Cymbal 4 Edge / 1 -L China Cymbal Ride Cymbal Cup 2/Cup 1 -L Tambourine 2/1-Push Splash Cymbal/Crash Cymbal 1 mf -L Cowbell 1/BD-Dry 1 Crash Cymbal 2/Crash Cymbal 1 mf -L Vibraslap/BD-Dry 1 Crash Cymbal 4/Ride Cymbal 1 -L Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Tom-Processed Timbales Hi-Rim 1/Hi-Edge Tom-Processed Timbales Lo-Open Tom-Processed Maracas-Push Tom-Processed Tom-Processed Cabasa-Tap/Up Tom-Processed Claves Woodblock 1 Woodblock 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) / 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Mute 4 Drumstick Hit Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-Tight BD-Squash BD-Gated BD-Tubby BD-Woofer SD-Full Room SD-Cracker Room SD-Amb.Piccolo 2 SD-Processed 1 SD-Yowie SD-Atomic BD-Terminator BD-Mondo Kill SD-Sidestick Ambi SD-Big Rock Hand Claps 2 SD-Processed 2 Tom 4-Floor Hi-Hat 3-Closed 2/1 Tom 4-Lo Hi-Hat 3-Pedal Open/Pedal Tom 4-Lo Hi-Hat 3-Open 1/2 Tom 4-Lo Tom 4-Hi Crash Cymbal 3/BD-Dry 1 Tom 4-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Tambourine 2/1-Push Splash Cymbal Cowbell 1/BD-Dry 1 Crash Cymbal 2/BD-Dry 1 Vibraslap/BD-Dry 1 Ride Cymbal 5 Edge Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Tom-Processed Timbales Hi-Rim 1/Hi-Edge Tom-Processed Timbales Lo-Open Tom-Processed Maracas-Push Tom-Processed Tom-Processed Cabasa-Tap/Up Tom-Processed Claves Woodblock 1 Woodblock 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) / 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca Orch.SD Roll 2/1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Mute 4 Drumstick Hit Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-Tight/BD-amb 2 mf -L BD-Squash/BD-amb 2 mf -L BD-Gated/BD-amb 1 f -L BD-Tubby/BD-amb 1 mf -L BD-Woofer/BD-amb 1 mf -L SD-Full Room/SD-dry 1 mp -L SD-Cracker/SD-dry 1 ff -L SD-Amb.Piccolo 2/SD-dry 1 ff -L SD-Processed 1/SD-dry 1 ff -L SD-Yowie/SD-dry 1 fff -L SD-Atomic/SD-dry 1 fff -L BD-Terminator/BD-amb 2 mf -L BD-Mondo Kill/BD-amb 2 f -L SD-Sidestick Ambi/SD-Sidestick mf -L SD-Big Rock/SD-dry 1 fff -L Hand Claps 2/1 -L SD-Processed 2/SD-dry 1 mf -L Tom 3-Floor/Tom 1-Lo f -L Hi-Hat 1-Closed ff/mf Tom 3-Lo/Tom 1-MidLo f -L Hi-Hat 1-Pedal ff/mf Tom 3-Lo/Tom 1-MidHi f -L Hi-Hat 1-Open ff/mf Tom 3-Lo/Tom 1-MidHi f -L Tom 3-Hi/Tom 1-Hi mf -L Crash Cymbal 3/1 mf -L Tom 3-Hi/Tom 1-Hi mf -L Ride Cymbal 4 Edge/Ride Cymbal 1 -L China Cymbal Ride Cymbal Cup 2/Cup 1 -L Tambourine 2/1-Push Splash Cymbal/Crash Cymbal 1 mf -L Cowbell 1/BD-Dry 1 Crash Cymbal 2/1 mf -L Vibraslap/BD-Dry 1 Cymbal 4 Edge/Ride Cymbal 2 -L Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/Conga 1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Tom-Processed Timbales Hi-Rim 1/Hi-Edge Tom-Processed Timbales Lo-Open Tom-Processed Maracas-Push Tom-Processed Tom-Processed Cabasa-Tap/Up Tom-Processed Claves Woodblock 1 Woodblock 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) / 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Tom 2-Floor Tsuzumi Tom 2-Lo Taiko-Rim/Open Tom 2-Hi Tom 2-Hi Chacha Bell/BD-Dry 1 Finger Snap Hand Claps 2 Drumstick Hit/BD-Dry 1 BD-Tubby BD-Dry 3 BD-Woofer BD-Dry 4 BD-Soft BD-Jazz 1 BD-Jazz 2 SD-Paper SD-Dry 3 SD-Dry 2 SD-Amb.Piccolo 1 SD-Jazz Ring SD-Brush Hit/BD-Dry 1 BD-Jazz 2 BD-Pillow SD-Sidestick Dry SD-Brush Tap 2/BD-Dry 1 SD-Brush Tap 1/BD-Dry 1 SD-Brush Swirl/BD-Dry 1 Tom-Brush Floor Hi-Hat 2-Closed 2/1 Tom-Brush Hi Hi-Hat 2-Pedal Tom-Brush Hi Hi-Hat 2-Open 1/2 Tom-Jazz Floor Tom-Jazz Hi Rim/Hi Crash Cymbal 2/BD-Dry 1 Tom-Jazz Hi Rim/Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Tambourine 2/1-Push Splash Cymbal Cowbell 1 Crash Cymbal 4 Vibraslap/BD-Dry 1 Ride Cymbal 5 Edge Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/Toe Conga 1 Hi-Open Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Agogo Bell/BD-Dry 1 Agogo Bell/BD-Dry 1 Cabasa-Up/Down Maracas-Push/BD-Dry 1 Samba Whistle 1/Chinese Gong Samba Whistle 1/Chinese Gong Guiro-Short/Tap Guiro-Long/BD-Dry 1 Claves/BD-Dry 1 Woodblock 1/BD-Dry 1 Woodblock 1/BD-Dry 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open/BD-Dry 1 Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Closed Hi-Hat-Whispy Hi-Hat Dance 4 Open Timbales-Paila / Shaker Hi-Hat-Crisp Open/Chili/ Crisp Open Hi-Hat-Vintage Hi-Hat 2-Pedal Open/Closed/Pedal BD-Dance 04 L/R BD-Dance 02 BD-Dance 04 Dance 07 BD-Dance 10 Orch.SD Roll 1 Orch.SD Roll 2 Orch.SD Roll 1 Orch.SD Roll 2 SD-Sidestick Hi Cymbal Reverse SD-Reverse IFX1 BD-01a-dry BD-03a-dry BD-03a-dry BD-02b-dry BD-02a-dry SD-06-dry SD-05-dry SD-04-dry SD-03-dry SD-02-dry SD-07-dry BD-01b-dry BD-01a-dry Sidestick-dry SD-01a-dry Hand Claps 1 SD-01b-dry Tom Lo-dry HiHat-Closed-dry Tom Lo-dry HiHat-Pedal1-dry Tom Mid-dry HiHat-Open-dry Tom Mid-dry Tom Hi-dry Crash 01-dry Tom Hi-dry Ride 01-dry China-dry Ride 02-dry Tambourine 3/1-Push/1-Pull Splash-dry Cowbell 2 Crash 02-dry Vibraslap Ride Bongo 2 Hi-Open f/mf/f Bongo 2 Lo-Muffled f/mp/Lo-Closed Conga 2 Hi-ClosedSlap/Hi-Closed Conga 2 Hi-OpenSlap/mf/f/mp Conga 2 Lo-OpenSlap/mf/f Timbales Hi-Rim 1/2/Hi-Edge Timbales Lo-Rim/Open Agogo Bell IFX5 Off Agogo Bell IFX5 Off Cabasa-Up/Down Maracas-Pull/Push Samba Whistle 2 Samba Whistle 2 Guiro-Short Guiro-Long Claves Woodblock 1 Woodblock 1 Cuica 2-Hi Cuica 2-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Closed Hi-Hat-Whispy Hi-Hat Dance 4 Open Timbales-Paila / Shaker Hi-Hat-Crisp Open/Chili/ Crisp Open Hi-Hat-Vintage Hi-Hat 2-Pedal Open/Closed/Pedal BD-Dance 04 L/R BD-Dance 02 BD-Dance 04 Dance 07 BD-Dance 10 Orch.SD Roll 1 Orch.SD Roll 2 Orch.SD Roll 1 Orch.SD Roll 2 SD-Sidestick Hi Cymbal Reverse SD-Reverse IFX1 BD-01a-dry BD-03a-dry BD-03a-dry BD-02b-dry BD-02a-dry SD-06-dry SD-05-dry SD-04-dry SD-03-dry SD-02-dry SD-07-dry BD-01b-dry BD-01a-dry Sidestick-dry SD-01a-dry Hand Claps 1 SD-01b-dry Tom Lo-dry HiHat-Closed-dry Tom Lo-dry HiHat-Pedal1-dry Tom Mid-dry HiHat-Open-dry Tom Mid-dry Tom Hi-dry Crash 01-dry Tom Hi-dry Ride 01-dry China-dry Ride 02-dry Tambourine 3/1-Push/1-Pull Splash-dry Cowbell 2 Crash 02-dry Vibraslap Ride Bongo 2 Hi-Open f/mf/f Bongo 2 Lo-Muffled f/mp/Lo-Closed Conga 2 Hi-ClosedSlap/Hi-Closed Conga 2 Hi-OpenSlap/mf/f/mp Conga 2 Lo-OpenSlap/mf/f Timbales Hi-Rim 1/2/Hi-Edge Timbales Lo-Rim/Open Agogo Bell IFX5 Off Agogo Bell IFX5 Off Cabasa-Up/Down Maracas-Pull/Push Samba Whistle 2 Samba Whistle 2 Guiro-Short Guiro-Long Claves Woodblock 1 Woodblock 1 Cuica 2-Hi Cuica 2-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause Hi-Hat 3-Closed 1 Hi-Hat 1-Closed mf Hi-Hat 1-Open mf Hi-Hat-Whispy Hi-Hat 1-Open ff Hi-Hat-Chili Hi-Hat-Alpo Closed DJ Vinyl Sliced 04 BD-Dry 3 DJ Vinyl Sliced 13 BD-Dance 03 BD-Dance 06 (R&B) BD-Ambi SD-Brasser "kkk" Voice 20 SD-Ambi Pop SD-Dry 2 Cymbal Reverse Finger Snap BD-Dance 06-EXs5 BD-Dance 05-EXs5 BD-Dance 03-EXs5 BD-Dance 06-EXs5 BD-Dance 09-EXs5 SD-Dance 05-EXs5 SD-Dance 10-EXs5 SD-Dance 07-EXs5 SD-Dance 09-EXs5 SD-Dance 02-EXs5 SD-Dance 04-EXs5 BD-Dance 04-EXs5 BD-Dance 08-EXs5 SD-Dance 06-EXs5 SD-Dance 03-EXs5 SD-Dance 01-EXs5 SD-Dance 07-EXs5 Tom-Dance i-EXs5 Hi-Hat Dance Tom-Dance j-EXs5 Hi-Hat Dance 02-EXs5 Tom-Dance k-EXs5 Hi-Hat Dance 04-EXs5 Tom-Dance a-EXs5 Tom-Dance b-EXs5 Crash Dance 1-EXs5 Tom-Dance c-EXs5 Ride Dance-EXs5 Crash Dance 2-EXs5 Orch Cymbal-Open Tambourine 1-EXs5 Cymbal Reverse 1-EXs5 Perc-Dance 01-EXs5 Crash HipHop 1-EXs5-L Sleigh Bell Crash Dance 4-EXs5 Perc-Dance 02-EXs5 Perc-Dance 03-EXs5 Perc-Dance 12-EXs5 Perc-Dance 05-EXs5 Perc-Dance 04-EXs5 Hi-Hat Dance 03-EXs5 Hi-Hat Dance 05-EXs5 Tambourine 2-EXs5 Tambourine 3-EXs5 Shaker 1-EXs5 Shaker 3-EXs5 DJ-Scratch 2-EXs5 DJ-Horn-EXs5 DJ-Scratch 1-EXs5 DJ-Big Cannon-EXs5 Perc-Mad Analog 37-EXs5 DJ-Vinyl Sliced 09/03/11/05-EXs5 DJ-Vinyl Sliced 12/09/12/02-EXs5 DJ Vinyl Sliced 05/04/07/06-EXs5 DJ-Record Stop-EXs5 Triangle-Mute-EXs5 Triangle Open 1-EXs5 Shaker 2-EXs5 Marc Tree Rev-EXs5 BD-Dance 01-EXs5 Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Voice 12 Voice 09 Bell Tree Stadium Applause Orchestral SD Voice 21 Voice 23 Voice 24 Voice 25 Voice 26 GunShot BD-amb 1 mf -L Hi-Hat Dance 3 Closed Hi-Hat Dance 3 Ope Hi-Hat-Old Closed 2 Hi-Hat Dance 4 Closed Timbales-Paila/Shaker 4-EXs5 Hi-Hat 3-Closed 1/Hi-Hat-Chili/Hi-Hat Dance 5 Closed Hi-Hat-Vintage 1/Hi-Hat-Crisp Closed 2 Hi-Hat 2-Pedal Open / 2-Closed 1 / 2-Pedal BD-HipHop 01b/02a-EXs5 BD-HipHop 11-EXs5 BD-HipHop 10/09-EXs5 BD-HipHop 14b/a-EXs5 BD-HipHop 04-EXs5 SD-HipHop 09-EXs5-L SD-HipHop 02-EXs5 SD-HipHop 04-EXs5 SD-HipHop 06a-EXs5 SD-HipHop 10-EXs5 Crash-Dance 1 SD-Yowie BD-HipHop 11-EXs5/BD-Dance 11-EXs5 BD-HipHop 06-EXs5 BD-HipHop 12/03-EXs5 BD-HipHop 02b/a-EXs5 BD-HipHop 06/12-EXs5 SD-HipHop 07-EXs5/SD-HipHop 11-EXs5-L SD-HipHop 08-EXs5 SD-HipHop 03-EXs5-L SD-HipHop 13-EXs5/SD-dry 1 ff -L/mp -L/p -L SD-HipHop 12-EXs5-L SD-HipHop 07-EXs5 BD-HipHop 13b/a-EXs5-L BD-HipHop 07/08b/08a-EXs5 Sidestick-HipHop-EXs5-L SD-HipHop 15d/c/b/a-EXs5-L Clap HipHop 1/2-EXs5 SD-HipHop 15a-EXs5-L/Perc-Granular 21-EXs5 Tom-HipHop Lo 3/2/1-EXs5 Hi-Hat HipHop 03/02/01-EXs5 Tom-HipHop Mid Lo 4/3/2/1-EXs5 Hi-Hat HipHop 06/07-EXs5 Tom-HipHop Mid Hi 4/3/2/1-EXs5 Hi-Hat HipHop 05/04-EXs5 Tom-HipHop Mid Hi 4/3/2/1-EXs5 Tom-HipHop Hi 4/3/2/1-EXs5 Crash HipHop 1-EXs5-L Tom-HipHop Hi 4/3/2/1-EXs5 Ride Cymbal 4 Edge Hi-Hat Dance 5 Open Ride HipHop 2-EXs5 Perc-Granular 36/39/37-EXs5 Crash-Dance 3 Perc-Mad Analog 19-EXs5 Crash HipHop 2-EXs5 Wind Ride HipHop 1-EXs5 Perc-Mad Analog 32-EXs5 Perc-Mad Analog 34-EXs5 Bongo 1 Lo-Stick Conga 1 Hi-Open Conga 1 Hi-Slap 1 Perc-Mad Analog 22/21/20-EXs5 Perc-Mad Analog 24-EXs5 Perc-Mad Analog 28-EXs5 Perc-Mad Analog 29-EXs5 Perc-Mad Analog 23-EXs5 Perc-Granular 36-EXs5 Perc-Mad Analog 06-EXs5 Perc-Mad Analog 05-EXs5 Perc-Mad Analog 12-EXs5 Perc-Mad Analog 13-EXs5 Perc-Mad Analog 37-EXs5 Perc-Mad Analog 30-EXs5 Perc-Mad Analog 30-EXs5 Perc-TekClub 18-EXs5 Perc-TekClub 18-EXs5 Perc-Granular 46-EXs5 Perc-Granular 46-EXs5 Perc-Mad Analog 35-EXs5 Marc Tree BD-HipHop 03-EXs5 Hi-Hat Dance 3 Closed SD-HipHop 02-EXs5 Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Tabla-Mute 4 Drumstick Hit/BD-Dry 1 Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-PhatJaz/BD-Dry 1 BD-Hip 1/BD-Dry 4 BD-Dance 04/BD-Dance 04 BD-AmbiRocker BD-Dance 13/BD-Pillow SD-Vintage 5/SD-Full Room SD-Ambi Hit 1/SD-Classic Room SD-AmbiCrackle 2/SD-Amb.Piccolo 1 SD-Vintage 2/SD-Paper SD-Hip 3/SD-Dry 4 SD-Ambi House/SD-Dry 2 BD-Dance 07/BD-Dry 3 BD-Ambi/BD-Dry 2 SD-Sidestick Dry SD-Whopper/SD-Amb.Piccolo 2 Hand Claps 4 Happy/2 SD-Vintage 1/SD-Big Rock Tom-Dirty Funk Hi-Hat-Amb Crackle/Hi-Hat 2-Closed 2 Tom-Dirty Funk Hi-Hat-Old Closed 2/Hi-Hat 2-Pedal Tom-Dirty Funk Hi-Hat-Crisp Open / 2-Open 2 Tom-Old R&B Tom-Old R&B Crash Cymbal 3 Tom-Old R&B Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Tambourine 2 / 1-Push Splash Cymbal Cowbell 1/BD-Dry 1 Crash Cymbal 2/BD-Dry 1 Vibraslap/BD-Dry 1 Ride Cymbal 5 Edge Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Agogo Bell/BD-Dry 1 Agogo Bell/BD-Dry 1 Cabasa-Up/Down Maracas-Push Samba Whistle 1/Chinese Gong Samba Whistle 1/Chinese Gong Guiro-Short/Guiro-Tap Guiro-Long/BD-Dry 1 Claves/BD-Dry 1 Woodblock 1/BD-Dry 1 Woodblock 1/BD-Dry 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open/BD-Dry 1 Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06/05 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 DJ Vinyl Sliced 15 DJ Vinyl Sliced 13 Voice Perc-Kick 2b-EXs5 BD-Mad Analog 08-EXs5 BD-Mad Analog 12-EXs5 SD-HipHop 12-EXs5-R Sidestick-HipHop-EXs5-L Voice Perc-Snare 3-EXs5 SD-Mad Analog 12-EXs5 SD-HipHop 03-EXs5-R Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-Dance Soft/BD-Dry 1 DJ-BD Rub BD-Dance 08/Zap 1 BD-Dance 03/Tom-VPM BD-Dance 06 (R&B)/BD-Pillow DJ-SD Rub/SD-Dry 2 Dance Claps 3/Scream SD-AmbiCrackle 2/SD-Amb.Piccolo 1 SD-Dance 2/SD-Sidestick Dry SD-Sidestick mf -L/Dance Claps 2 SD-Vintage 5/SD-Full Room BD-Dance 12/Orchestral BD BD-Dance 13/Taiko-Open Rimshot-Dance 1 Dance Claps 3/SD-Sidestick mf -L Dance Claps 1/Hand Claps 1 -L SD-Hip 4/SD-Dance 7 BD-Hip 1/Explosion Hi-Hat Dance 3 Closed / 2-Pedal Taiko-Open/Explosion Hi-Hat Dance 3 Open / 2-Closed 2 BD-Hip 1/Explosion Hi-Hat Dance 4 Open / 2-Open 2 Vocal Cowbell/Tom-Old R&B Vocal Cowbell/Tom-Old R&B Crash Cymbal 3/BD-Dry 1 Claves/Tom-Old R&B Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Dance Perc 2b/2a Tribe/Chinese Gong Tambura/Gamelan Orch Cymbal-Open/Timpani Applause/Stadium Ride Cymbal 1 -L / 4 Edge Bongo 1 Hi-Slap/Hi-Open Bongo 1 Lo-Slap/Lo-Open Conga 1 Hi-Mute/1-Toe Conga 1 Hi-MtSlap/Hi-Open Conga 1 Lo-Slap/Lo-Open Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Open Tambourine 3 Tambourine 2 Cabasa-Up/Down Maracas-Push DJ-BD Rub DJ-SD Rub DJ Vinyl Sliced 11/12 DJ Vinyl Sliced 09/10 DJ-Reverse Woodblock 1/BD-Dry 1 Woodblock 1/BD-Dry 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open/BD-Dry 1 Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B)/05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Tabla-Mute 4 Drumstick Hit Cymbal Reverse SD-Reverse BD-Jazz 2 BD-Dance 06 (R&B)/BD-Dry 5 BD-Pillow/DJ-Record Stop BD-Ringy/Dance Perc 2b BD-Ambi Crackle/DJ-Old Record SD-AmbiCrackle 3/DJ-Old Record Orchestral SD SD-Brush Swirl/SD-Brush Tap 1 SD-Vintage 2/SD-Brush Hit SD-Hip 3/SD-GhostSet f SD-Jazz Ring/SD-Brush Tap 2 BD-Hip 4 BD-Dance Soft SD-Sidestick mf -R SD-Brush Hit/Tom 2-Hi Dance Claps 1/Hand Claps 1 -L SD-Hip 5/SD-Sidestick Dry Tom-Old R&B Hi-Hat-Crisp Closed 1 / Hi-Hat 1-Closed mf /BD-Dry 1 Tom-Old R&B Hi-Hat-Crisp Closed 1 / Hi-Hat 1-Pedal mf Tom-Old R&B L/R Off Hi-Hat-Old Open/Hi-Hat 1-Open mf Tom-Dance Tom-Dance Crash Cymbal 1 mf -L Tom-Dance Ride Cymbal 1 -L / 4 Edge Crash-Dance 2 DDD1 / China Cymbal Ride Cymbal Cup 1 -R / Ride-Dance 1 Tambourine 2/1-Push Crash-Dance 1 Finger Snap Crash Cymbal 2 Finger Cymbal/BD-Dry 1 Ride-Dance 1 Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Agogo Bell L/R Off Agogo Bell/BD-Dry 1 Cabasa-Up/Down Maracas-Push/Pull Uhh Solo Yea Solo Guiro-Short/Tap Guiro-Long Claves Woodblock 1 Woodblock 1 Cuica 1-Hi Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open Shaker 1 Marc Tree Bell Tree Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-amb 1 mf -L Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Open/Closed Hi-Hat Dance 03-EXs5 Hi-Hat Dance 3 Closed Timbales-Paila/Shaker 1 Hi-Hat-Crisp Open/Chili/Whispy Hi-Hat-Vintage 1 Hi-Hat 2-Pedal Open/Closed 1/Pedal BD-TekClub 03-EXs5 BD-TekClub 11-EXs5 BD-TekClub 12-EXs5 BD-TekClub 06-EXs5 BD-TekClub 04-EXs5/BD-Mad Analog 14-EXs5 SD-TekClub 03-EXs5 SD-TekClub 06/07-EXs5 SD-TekClub 08-EXs5-L SD-TekClub 11-EXs5 SD-TekClub 17-EXs5 Cymbal Reverse SD-Processed BD-TekClub BD-TekClub BD-TekClub BD-TekClub 10-EXs5 BD-TekClub 08/07/06-EXs5 SD-TekClub 05/04-EXs5-L SD-TekClub 01-EXs5 SD-TekClub 12-EXs5 SD-TekClub 15/07-EXs5 Crash-Granular-EXs5-L/SD-HipHop 08-EXs5 SD-TekClub-EXs5 BD-TekClub 05-EXs5 BD-TekClub 04-EXs5 SD-Mad Analog 02-EXs5 SD-TekClub 08-EXs5-L SD-TekClub 10-EXs5-L SD-TekClub 14-EXs5 Tom-TekClub-EXs5-L Hat-TekClub 05-EXs5-L Tom-TekClub-EXs5-L Hat-TekClub 10-EXs5/01-EXs5-R Tom-TekClub-EXs5-L Hat-TekClub 11-EXs5/03-EXs5-L Tom-Granular 6-EXs5 Tom-Granular 5-EXs5 Crash HipHop 1-EXs5-L Tom-Granular 4-EXs5 Ride HipHop 2-EXs5 China Cymbal Perc-TekClub 14-EXs5 Tambourine 2/1-Push/1-Pull Crash-Granular-EXs5-R Perc-TekClub 10-EXs5 Crash HipHop 2-EXs5 HiHat Granular 06-EXs5-R Perc-TekClub 02-EXs5 Perc-TekClub 08-EXs5 Perc-TekClub 08-EXs5 Perc-Mad Analog 32-EXs5 Perc-Mad Analog 34-EXs5 Perc-Mad Analog 33-EXs5 Perc-TekClub 10-EXs5 Perc-TekClub 10-EXs5 Hi-Hat Dance 5 Open Hi-Hat Dance 5 Open Perc-Mad Analog 23-EXs5 Maracas-Pull Perc-Granular 35/34-EXs5 Voice 35 "Aow"/Voice 39 "Uuh" Hat-TekClub 10-EXs5 Hat-TekClub 11-EXs5 Perc-TekClub 01-EXs5 Perc-TekClub 06/05-EXs5 Perc-TekClub 12/13-EXs5 Perc-TekClub 04-EXs5 Perc-TekClub 17-EXs5-R/16-EXs5 Triangle-Mute Triangle-Roll Dance Perc 8 Maraca Marc Tree BD-Mad Analog 04-EXs5 HiHat Mad Analog 02-EXs5 SD-Mad Analog 10-EXs5 HiHat Mad Analog 03-EXs5 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause BD-HipHopf 03-EXs5 BD-TekClub 07-EXs5 BD-Granular01-EXs5-L/BD-Dance 01 BD-TekClub 04-EXs5 BD-Mad Analog 01-EXs5 SD-TekClub 08-EXs5-L SD-HipHop 04-EXs5 SD-Dance 12-EXs5 SD-Dance 05-EXs5 SD-Dance 02-EXs5 BD-Mad Analog 04-EXs5 BD-Dance 01 BD-Dance 11 BD-Dance 04 BD-Dance 08 BD-Squash BD-Dance 06 (R&B) SD-Dance 3 (New) SD-Dance 5 SD-Hip 4 SD-Sizzle SD-Noise/SD-Vintage 4 SD-Hip 1/SD-Chili BD-Dance 12 BD-Dance 09 Rimshot-Dance 2 SD-Dance 4 (Old) Dance Claps 1 SD-Dance 8 Tom-Dance Hi-Hat Dance 1 Closed Tom-Dance Hi-Hat Dance 1 Open Tom-Dance Hi-Hat Dance 1 Open Hi-Hat-Whispy/BD-Dry 1 Hi-Hat Dance 3 Closed Crash-Dance 3 Hi-Hat Dance 3 Open Ride-Dance 1 Crash Cymbal 3/China Cymbal Ride-Dance 2 DDD1 Tambourine 3/2 Splash Cymbal Dance Perc 7 Cowbell Crash-Dance 1 Zap 3 Cymbal Reverse Zap 1 Zap 2 Dance Perc 6 Clave/BD-Dance 05 (Rap) Dance Perc 5 Conga Dance Perc 5 Conga Metal Hit/DJ-Reverse Hi-Hat-Chili/BD-Dry 1 Industry Loop 2/DJ-Old Record Hi-Hat 3-Pedal/DJ-Record Stop Dance Perc 4 House/DJ-SD Rub Dance Perc 8 Maraca/SD-Noise Vocal SD Car Crash/BD-Woofer Triangle-Mute/BD-Dance 05 (Rap) Noise-White/SD-Amb.Piccolo 1 Triangle-Open/Tom-Brush Hi E.Guitar Cut Noise 1/SD-Paper Hi-Hat-Hip 2/Vocal SD Hi-Hat-Alpo Closed String Slap/Tom-Brush Hi Hi-Hat-Amb Crackle/Ride-Dance 1 Dist.Slide 1/Tom-Jazz Hi Rim Hi-Hat-Crisp Open/Ride-Dance 1 Swish Terra Swish Terra/BD-Jazz 2 Crash Cymbal 2 China Cymbal Ride Cymbal 4 Edge Conga 1 Hi-Slap 1/Lo-Open Conga 1 Hi-Open/Lo-MuteSlap Ride Cymbal Cup 2 Conga 1 Hi-Slap 2/Hi-Mute Cowbell 1 Chacha Bell Cowbell 1 Guiro-Short/BD-Dry 1 Guiro-Tap Metal Hit/Finger Cymbal Dance Perc 1 Hip/BD-Dry 1 Swish Terra Cyber Drum Tambourine 1-Push Air Vortex Tubular Telephone Ring Footstep 2 Triangle-Mute Tribe Dance Perc 1 Hip/BD-Dry 1 BD-Mad Analog 10-EXs5 BD-HipHop 08b-EXs5 BD-Granular 02-EXs5-R/L BD-TekClub 02-EXs5 BD-Granular 20-EXs5-L/R SD-Dance 09-EXs5 SD-dry 1 p -R SD-Granular 20-EXs5-L SD-TekClub 13-EXs5 SD-Dance 02-EXs5 BD-Dance 10 BD-Dance 01 BD-Dance 03 BD-Dance 01 BD-Squash BD-Dance 13/Hi-Hat 3-Closed 1 BD-Dance 01/Zap 1 SD-Dance 3 (New) SD-Dance 5 SD-Dance 2 IFX1 Dance Claps 3 SD-Dance 2/Hand Claps 2 SD-Dance 8 BD-Dance 10/Hi-Hat Dance 3 Closed BD-Dance 11/Zap 1 Rimshot-Dance 1/2 SD-Dance 7 Dance Claps 2 SD-Dance 6 Tom-Dance/Tom 4-Floor Hi-Hat Dance 1 Closed Tom-Dance/Tom 4-Lo Hi-Hat Dance 2 Closed Tom-Dance/Tom 4-Hi Hi-Hat Dance 2 Open Dance Perc 2b/2a Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Open Hi-Hat Dance 3 Open Ride-Dance 1 Crash-Dance 1 Ride-Dance 2 DDD1 Tambourine 3/2 Splash Cymbal Dance Perc 7 Cowbell Crash-Dance 1 PC Vox-One Cymbal Reverse Zap 1 Zap 2 Dance Perc 6 Clave/BD-Dance 05 (Rap) Dance Perc 5 Conga Dance Perc 5 Conga Tabla-Na/DJ-Reverse Hi-Hat Dance 3 Closed/BD-Dry 1 Baya-Mute 2/DJ-Old Record Hi-Hat Dance 3 Open/DJ-Record Stop Baya-Open/DJ-SD Rub Dance Perc 8 Maraca/SD-Noise CompuVoice-Beep/Vocal SD Dance Claps 3/BD-Woofer Triangle-Mute/BD-Dance 05 (Rap) Hand Claps 1 -L/SD-Amb.Piccolo 1 Triangle-Open/Tom-Brush Hi Finger Snap/SD-Paper Hi-Hat-Old Closed 3/Vocal SD Hi-Hat-Alpo Closed E.Guitar Pick 1/Tom-Brush Hi Hi-Hat-Amb Crackle/Ride-Dance 1 Dist.Slide 1/Tom-Jazz Hi Rim Hi-Hat-Crisp Open/Ride-Dance 1 Dist.Slide 2/Swish Terra Guitar Scratch/BD-Jazz 2 Crash Cymbal 2/Crash Cymbal 2 China Cymbal Ride Cymbal 4 Edge Conga 1 Hi-Slap 1/Bongo 1 Lo-Open Conga 1 Hi-Open/Lo-MuteSlap Ride Cymbal Cup 2 Conga 1 Hi-Slap 2/Hi-Mute Cowbell 1 Chacha Bell Cowbell 1 Guiro-Short/BD-Dry 1 Guiro-Tap Metal Hit/Finger Cymbal Dance Perc 1 Hip/BD-Dry 1 Swish Terra Cyber Drum Tambourine 1-Push Air Vortex Tubular Telephone Ring Footstep 2 Triangle-Mute Tribe Dance Perc 1 Hip/BD-Dry 1 BD-Mad Analog 04-EXs5 BD-Dance 06-EXs5 BD-Mad Analog 05-EXs5 BD-HipHop 03-EXs5 BD-Dance 14-EXs5 SD-Mad Analog 13-EXs5 SD-TekClub 04-EXs5 SD-TekClub 13-EXs5 SD-Dance 12-EXs5/DJ-SD Rub SD-Dance 03-EXs5 BD-TekClub 01-EXs5 BD-Dance 01-EXs5/BD-Mad Analog 13-EXs5 BD-Dance 13 BD-Dance 04/Dance Perc 4 House BD-Dance 08/BD-Jungle BD-Mad Analog 06-EXs5/07-EXs5/08-EXs5 HiHat Mad Analog 16-EXs5/BD-Dance 06 (R&B) SD-Dance 5 SD-Dance 3 (New) SD-Hip 4 SD-Rap 2/Dance Claps 1 SD-Noise/Dance Claps 3/SD-Noise SD-Dance 2/7 BD-Dance 06 (R&B)/BD-amb 1 f -R BD-Dance 13 Rimshot-Dance 1 SD-Dance 2/Dance Claps 2 Dance Claps 1/Dance Claps 3 SD-Dance 5 Tom-Dance Hi-Hat/Dance Perc 2a Tom-Dance Hi-Hat Dance 3 Open/Dance Perc 2b Tom-Dance Hi-Hat Dance 3/4 Open Hi-Hat Dance 1 Closed Dance Perc 8 Maraca Crash-Dance 3 Hi-Hat Dance 2 Open Ride-Dance 1 Crash-Dance 1/China Cymbal Ride-Dance 2 DDD1 Dance Perc 2b/2a Industry Loop 2 HiHat Mad Analog 09-EXs5 Crash-Dance 1 568:HiHat Mad Analog 10-EXs5 Perc-Mad Analog 07-EXs5 Zap 1 Zap 2 Dance Perc 6 Clave Dance Perc 5 Conga Dance Perc 5 Conga Noise-VPM Mod/DJ-Reverse Hi-Hat-Chili Noise-White Hi-Hat 3-Pedal/DJ-Record Stop Dance Perc 3 House Hi-Hat 2-Open 2 Vocal Cowbell GunShot Triangle-Mute Noise-VPM Mod/SD-Amb.Piccolo 1 Triangle-Open/Tom-Brush Hi HiHat Granular 19-EXs5-L/R Hi-Hat-Ambi Hi-Hat-Alpo Closed CompuVoice-When/Tom-Brush Hi Hi-Hat-Amb Crackle/Ride-Dance 1 CompuVoice-Do it/Tom-Jazz Hi Rim Hi-Hat-Crisp Open/Ride-Dance 1 CompuVoice-Ch Year solo/BD-Jazz 2 Crash Cymbal 2 China Cymbal Ride Cymbal 4 Edge Conga 1 Hi-Slap 1/Bongo 1 Lo-Open Conga 1 Hi-Open/Lo-MuteSlap Ride Cymbal Cup 2 Conga 1 Hi-Slap 2/Hi-Mute Cowbell 1 Chacha Bell Cowbell 1 Guiro-Short/BD-Dry 1 Guiro-Tap Metal Hit/Finger Cymbal Dance Perc 1 Hip/BD-Dry 1 Swish Terra Cyber Drum Tambourine 1-Push Air Vortex Tubular Telephone Ring Footstep 2 Triangle-Mute Tribe Dance Perc 1 Hip/BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Mute 4 Drumstick Hit/BD-Dry 1 Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-PhatJaz BD-Tubby/BD-Dry 4 BD-Dry 6 BD-Klanger/BD-Hip 1 BD-Pillow SD-Ambi Hit 3 SD-Hip 3 SD-Klanger/SD-Amb.Piccolo 1 SD-Ringy SD-Chili SD-Dry 2 BD-Ringy/BD-Dance 08 BD-Ambi Crackle/BD-Ambi SD-Sidestick Dry/Rimshot-Dance 2 SD-Chili Hand Claps 4 Happy/3 Hip SD-AmbiCrackle 3 Tom-Old R&B Hi-Hat-Crisp Closed 2 Tom-Old R&B Hi-Hat 2-Closed 2 Tom-Old R&B Hi-Hat-Crisp Open / 2-Open 2 Hi-Hat-Vintage 1 Hi-Hat-Amb Crackle Crash Cymbal 3/BD-Dry 1 Hi-Hat-Old Open Ride Cymbal 4 Edge Noise-VPM Mod/Cricket Spectrum Ride Cymbal Cup 2 Tambourine 2/1-Push Tambourine 3/1-Pull Hi-Hat-Hip 2/BD-Dry 1 Crash Cymbal 2/BD-Dry 1 MachineGun Ride-Dance 2 DDD1 Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Dance Perc 1 Hip/BD-Dry 1 Dance Perc 1 Hip/BD-Dry 1 Cabasa-Up/Down Maracas-Push SD-GhostSet f SD-GhostSet roll Train/Chinese Gong Space Lore Noise-VPM Mod/BD-Dry 1 Air Vortex/BD-Dry 1 Cricket Spectrum Car Engine/BD-Dry 1 Door Slam/BD-Dry 1 Triangle-Mute Triangle-Open/BD-Dry 1 Shaker 1 Bell Tree/BD-Dry 1 BD-Dance 06/05 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dance 06 (R&B)/Perc-Granular 16-EXs5-L BD-Granular 28-EXs5 BD-Dance 05 (Rap)/BD-Hip 4 BD-Mad Analog 04-EXs5 BD-Pop SD-HipHop 03-EXs5-L SD-HipHop 15d-EXs5-L SD-Granular 06-EXs5/SD-dry 1 f -R SD-Granular 14-EXs5-L/SD-HipHop 06b-EXs5 SD-Sizzle/SD-HipHop 04-EXs5 Cymbal Reverse SD-Reverse BD-Terminator/BD-Dance 13 BD-Dance 13/BD-Dry 3 BD-Ringy/BD-Dance 13 BD-HipHop 14a-EXs5 BD-Dance 13/BD-Dance 08-EXs5 SD-TekClub 05-EXs5-L/SD-Dance 2 SD-Jungle SD-Dance 15-EXs5 SD-Hip 5/SD-Sidestick Ambi SD-Jungle SD-Big Rock BD-Tight/BD-Dance 08 BD-Dance 13/BD-Terminator SD-Sidestick Dry/SD-Sizzle SD-Dance 2/Dance Claps 1 Dance Claps 2/Hand Claps 3 Hip SD-Dance 2/SD-Ambi Hit 3 Tom-Old R&B Hi-Hat-Vintage 1 Tom-Old R&B Hi-Hat-Amb Crackle Tom-Old R&B Splash Cymbal/Noise-White Hi-Hat 1-Closed mf/Noise-White Hi-Hat 1-Closed ff/Noise-White Crash-Dance 1/BD-Dry 1 Hi-Hat-Old Open Ride Cymbal 4 Edge China Cymbal/Stadium Ride Cymbal Cup 2 HiHat Granular 03-EXs5-L/R Crash-Jungle Dance Perc 2a Crash Cymbal 2/Crash-Jungle Dance Perc 2b Ride-Dance 2 DDD1 Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Hi-Hat Dance 06-EXs5 Hi-Hat Dance 10-EXs5 Hat-TekClub 12-EXs5-L Hat-TekClub 12-EXs5-R Tambourine 2 Tambourine 3 SD-GhostSet f SD-GhostSet roll Zap 2 Zap-EXs5 Noise-VPM Mod Noise-VPM Mod Tambura/Cricket Spectrum Voice 21 Marc Tree Triangle-Mute Triangle-Open Shaker 1 Bell Tree BD-Dance 06/05 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applausef Voice Perc-Kick 1-EXs5 Voice Perc-Kick 2a-EXs5 Voice Perc-Kick 2b-EXs5 BD-HipHop 14a-EXs5/BD-Jungle BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Ambi Soft/"Tuunn" BD-Mad Analog 03-EXs5 DJ Vinyl Sliced 15/Perc-Mad Analog 17-EXs5 BD-Mad Analog 08-EXs5/CompuVoice-Do it Perc-Mad Analog 25-EXs5 SD-Mad Analog 05-EXs5 Clap Mad Analog 5-EXs5/Voice Perc-Snare 2-EXs5 Perc-Granular 32-EXs5 SD-Mad Analog 07-EXs5 SD-Mad Analog 09-EXs5 BD-Granular 10-EXs5-R/L BD-Mad Analog 06/07-EXs5 BD-Dance 13/Perc-Granular 20-EXs5 BD-Dance 09/Click DJ-Old Record/BD-Dance 08 BD-Dry 5/BD-Dance 07 BD-Dance 07/Hi-Hat Dance 4 Open SD-Dry 3/Timbales Hi-Edge SD-Dance 3 (New) SD-Jungle/Hand Claps 4 Happy SD-Ambi Hit 1 Dance Claps 3/1 Dance Perc 8 Maraca/SD-Sidestick Dry Noise-White/Cyber Drum Hart Beat/Zap 2 CompuVoice-Beep Noise-White Noise-White/Applause SD-Dance 6/SD-Air Explosion Tom-Synth/Noise-White Noise-White/Click Tom-Synth/Noise-White Noise-White/Click Tom-Synth/Noise-White Noise-White/Click Tom-Synth/Noise-White Hi-Hat Dance 4 Closed Noise-VPM Mod/Noise-White Hi-Hat Dance 4 Open Ride-Dance 2 DDD1/Telephone Ring Crash-Dance 3/China Cymbal Ride-Dance 2 DDD1 Cabasa-Up/Down Splash Cymbal Shaker 2/1 Crash-Dance 2 DDD1 Dance Perc 6 Clave Cymbal Reverse Dance Perc 5 Conga/Bongo 1 Hi-Open Dance Perc 5 Conga/Bongo 1 Lo-Open Conga 1 Hi-MtSlap/Conga 1-Toe Dance Perc 5 Conga/Conga 1 Hi-Open Dance Perc 5 Conga/Conga 1 Lo-Open Tambourine 1-Push/1-Pull Tambourine 3/2 Dance Perc 7 Cowbell/BD-Dry 1 Rimshot-Dance 2 Cabasa-Up/Down Maracas-Push Dance Perc 8 Maraca/Chinese Gong SD-Metal Striker/Chinese Gong Car Engine/Guiro-Tap Industry Loop 2 Industry Loop 2 Perc-Mad Analog 19-EXs5 Tambourine 3/BD-Dry 1 Perc-Granular 39-EXs5 String Slap/BD-Dry 1 Perc-Mad Analog 06-EXs5 Zap 1/BD-Dry 1 Perc-Granular 28-EXs5-L Car Crash/BD-Dry 1 BD-Dance 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 4 (Old) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Jungle Dance Perc 6 Clave Wind/BD-Dry 1 Stream/BD-Dry 1 "Two..." Solo/"One..." Solo "Four..." Solo/"Three..." Solo Bell Tree/BD-Dry 1 Space Lore/BD-Dry 1 Hart Beat/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-amb 1 mf -L Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Open/Closed Hi-Hat Dance 03-EXs5 Hi-Hat Dance 3 Closed Timbales-Paila/Shaker 1 Hi-Hat-Crisp Open/Hi-Hat-Chili/Hi-Hat-Whispy Hi-Hat-Vintage 1 Hi-Hat 2-Pedal Open/2-Closed 1/Hi-Hat 2-Pedal BD-Mad Analog 03-EXs5 BD-Mad Analog 15-EXs5 BD-Mad Analog 12-EXs5 BD-Mad Analog 13/14-EXs5 BD-Mad Analog 09-EXs5 SD-Mad Analog 01-EXs5 SD-Mad Analog 04-EXs5 SD-Mad Analog 11-EXs5 SD-Mad Analog 07-EXs5/SD-Granular 22-EXs5-L Cymbal Reverse SD-Mad Analog 01-EXs5 BD-Mad Analog 01-EXs5 BD-Mad Analog 05-EXs5 Perc-Granular 18-EXs5 BD-Mad Analog 02-EXs5/BD-TekClub 10-EXs5 BD-Mad Analog 11-EXs5/BD-Mad Analog 10-EXs5 SD-Mad Analog 14-EXs5/Perc-Granular 44-EXs5 SD-TekClub 04-EXs5 SD-Mad Analog 15-EXs5 SD-Mad Analog 12-EXs5/Hi-Hat Granular 01-EXs5 SD-Mad Analog 13-EXs5 SD-Mad Analog 02-EXs5 BD-Mad Analog 06/07/08-EXs5 BD-Mad Analog 04-EXs5 Sidestick-Mad Ana 2-EXs5/SD-Mad Analog 11-EXs5 SD-Mad Analog 10-EXs5 SD-Granular 21-EXs5-L/Clap Mad Analog 2-EXs5 SD-Mad Analog 13-EXs5/SD-Mad Analog 12-EXs5 Tom-Mad Analog 3-EXs5 HiHat Mad Analog 01-EXs5 Tom-Mad Analog 2-EXs5 HiHat Mad Analog 10/09-EXs5 Tom-Mad Analog HiHat Mad Analog 16/15/14-EXs5 Tom-Mad Analog 5b/5a-EXs5 Tom-Mad Analog 6b/6a-EXs5 Hi-Hat-Clangy Open/Industry Loop 2 Tom-Mad Analog 4b/4a-EXs5 Perc-Mad Analog 08-EXs5 Splash Cymbal Ride Cymbal Cup 1 -L Dance Perc 2a Crash-Granular-EXs5-L Perc-Granular 22-EXs5 Crash HipHop 2-EXs5 Perc-Mad Analog 03-EXs5 HiHat Granular 18-EXs5-R Perc-TekClub 08-EXs5 Perc-TekClub 08-EXs5 Perc-Mad Analog 32-EXs5/Conga 2 Hi-Closed Conga 2 Hi-OpenSlap/Perc-Mad Analog 34-EXs5 Conga 2 Lo-OpenSlap/Perc-Mad Analog 33-EXs5 Perc-Mad Analog 12-EXs5 Perc-Mad Analog 15-EXs5 Perc-Granular 23-EXs5 Perc-Granular 23-EXs5 Perc-Mad Analog 23-EXs5 Perc-Mad Analog 09-EXs5 Perc-Mad Analog 05-EXs5 Perc-Mad Analog 04-EXs5 HiHat Mad Analog 08-EXs5 HiHat Mad Analog 07-EXs5 Perc-Mad Analog 12/11-EXs5 Perc-Mad Analog 15/14/13-EXs5 Perc-Mad Analog 18/17/16-EXs5 Perc-Mad Analog 31-EXs5 Perc-Mad Analog 31-EXs5 Triangle-Mute Triangle-Open Shaker 1/2 Marc Tree BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbel Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause BD-amb 1 mf -L L/R Off Hi-Hat Dance 4 Closed IFX4 Off 534:Hi-Hat Dance 4 Open/Closed Hi-Hat Dance 03-EXs5f Hi-Hat Dance 3 Closedf Timbales-Paila/Shaker 1 Hi-Hat-Crisp Open/Chili/Whispy Hi-Hat-Vintage 1 Hi-Hat 2-Pedal Open/Closed 1/Pedal BD-Granular 06-EXs5 BD-Granular 13-EXs5 BD-Granular 11-EXs5-L BD-Granular 17-EXs5 BD-Granular 20-EXs5-L SD-Granular 01-EXs5 SD-Granular 02-EXs5 SD-Granular 20-EXs5-L SD-Granular 19-EXs5-L SD-Granular 16-EXs5-L Cymbal Reverse L/R Off SD-Processed BD-Granular 10-EXs5-L BD-Granular 21/23-EXs5 BD-Granular 09/08-EXs5 BD-Granular 28-EXs5 BD-Granular 27/26-EXs5-L SD-Granular 26-EXs5 SD-Granular 08-EXs5-L/R SD-Granular 03-EXs5 SD-Granular 24/23-EXs5-L SD-Granular 18-EXs5-L SD-Granular 15-EXs5-L BD-Granular 22-EXs5-L BD-Granular 18-EXs5-L Perc-Granular 23-EXs5/Sidestick-Mad Ana 1-EXs5 SD-Granular 07-EXs5-L SD-Granular 14-EXs5-L SD-Granular 05/04-EXs5-L Tom-Granular 2-EXs5-L HiHat Granular 21/20-EXs5 Tom-Granular 3-EXs5-L HiHat Granular 18/17-EXs5-L Tom-Granular 1-EXs5-L HiHat Granular 18-EXs5-L Tom-Granular 6-EXs5 Tom-Granular 5-EXs5 Crash-Granular-EXs5-L Tom-Granular 4-EXs5 HiHat Granular 19/18-EXs5-L Splash Cymbal Ride Cymbal Perc-Granular 36/37/38-EXs5 Perc-Granular 12-EXs5 Perc-Granular 04-EXs5 Crash HipHop 2-EXs5 Perc-Granular 32-EXs5 Perc-TekClub 09-EXs5-L Perc-Granular 07-EXs5 Perc-Granular 08-EXs5 Perc-Granular 05-EXs5 Tsuzumi Tsuzumi Perc-Granular 29/04-EXs5 Perc-Granular 29/04-EXs5 Perc-Granular 06-EXs5 Perc-Granular 06-EXs5 Dance Perc 8 Maraca Shaker 1/2 Perc-Granular 03-EXs5 Perc-Granular 03-EXs5 SD-Granular 16-EXs5-L SD-Granular 17-EXs5-L Perc-Granular 20/19-EXs5 Perc-Granular 33-EXs5 Perc-Granular 45-EXs5 Perc-Granular 12-EXs5 Perc-Granular 21-EXs5 Triangle-Mute Triangle-Roll Perc-Granular 26-EXs5 Marc Tree BD-Granular 07-EXs5 HiHat Granular 21-EXs5 SD-Granular 13-EXs5 HiHat Granular 22-EXs5 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause BD-Dry 1 BD-HipHop 07-EXs5 BD-TekClub 06-EXs5 BD-Dance 10 BD-Dance 05 (Rap) BD-Dance 13-EXs5 SD-HipHop 09-EXs5-L SD-Mad Analog 01-EXs5 SD-HipHop 06b-EXs5 SD-Mad Analog 13-EXs5 SD-Dance 04-EXs5 Cymbal Reverse SD-Reverse BD-Dance 06 (R&B) BD-Dark/BD-Dance 13 BD-Hip 1/2 BD-Dance 13 BD-Dance Soft/08 SD-AmbiHop SD-Ambi Hit 1/SD-Classic Room SD-Brasser/SD-Amb.Piccolo 1 SD-Ambi Pop/SD-AmbiCrackle 1 SD-Dry Present/SD-Big Rock SD-Vintage 6 BD-Dry 6/BD-Dark BD-Pop/BD-Dance 08 SD-Sidestick Ambi/Dry SD-Vintage 1 Hand Claps 3 Hip/4 Happy SD-Hip 6/SD-Dry 4 Tom-Dance Hi-Hat-Tight 1/2 Tom-Dance Hi-Hat-Old Closed 2/Hi-Hat-Hip 1 Tom-Dance Hi-Hat-Clangy Open/Hi-Hat-Crisp Open Tom-Old R&B Tom-Old R&B Noise-VPM Mod/BD-Dry 1 Tom-Old R&B Ride Cymbal 5 Edge Crash-Dance 3/China Cymbal Ride Cymbal Cup 2 Tambourine 2/1-Push Explosion/BD-Dry 1 Cowbell 1/BD-Dry 1 Crash-Dance 2 DDD1/Splash Cymbal Church Bell/BD-Dry 1 Crickets 2 Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Hat-TekClub 02-EXs5-R/L Hi-Hat Dance 1 Open/Closed Tambourine 3-EXs5 Tambourine 1-EXs5 Cabasa-Up/Down Maracas-Push Maraca/Chinese Gong SD-Metal Striker/Chinese Gong Car Engine/Guiro-Tap Industry Loop 2/Guiro-Tap Industry Loop 2/BD-Dry 1 Car Stop/BD-Dry 1 Tambourine 3/BD-Dry 1 Industry Loop 2/BD-Dry 1 String Slap/BD-Dry 1 Dance Perc 1 Hip/Triangle-Mute Zap 1/BD-Dry 1 Bubble/Shaker 1 Car Crash/BD-Dry 1 BD-Dance 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 4 (Old) Hi-Hat Dance 3 Open/BD-Dry 1 Chinese Gong/Crash-Dance 3 Dance Perc 6 Clave SD-Dance 01-EXs5 Stream/BD-Dry 1 "Two..." Solo/"One..." Solo "Four..." Solo/"Three..." Solo Bell Tree/BD-Dry 1 Space Lore/BD-Dry 1 Hart Beat/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Granular 11-EXs5-R BD-HipHop 10-EXs5 BD-Granular 25-EXs5 BD-Granular 26-EXs5-L BD-Granular 28-EXs5 SD-Sizzle SD-Granular 06-EXs5 SD-Rap 2 SD-HipHop 03-EXs5-L SD-Granular 07-EXs5-L Cymbal Reverse SD-Reverse BD-Squash BD-Jazz 2 Zap 1 Footstep 1/2 Cyber Drum Tabla-Na/Open Orchestral SD SD-Rap 2 SD-Dance 1 SD-Amb.Piccolo 2/SD-Off Center SD-Atomic Djembe-Open BD-Dance 02 Timbales Hi-Rim 1 SD-Jazz Ring Zap 3 SD-Dance 4 (Old) Tom 3-Lo Hi-Hat Dance 1 Closed Tom-Brush Floor Hi-Hat Dance 3 Open Tom-Synth Triangle-Open/Hi-Hat Dance 1 Open Tom-Synth Tom-Jazz Hi Orch Cymbal-Open Tom 4-Lo/BD-Dry 1 Ride Cymbal-Jazz Air Vortex Ride Cymbal Cup 1 -R Tambourine 3/2 Splash Cymbal Dist.Slide 1/2 Ride Cymbal 2 -L Bird 1 Sleigh Bell Bongo 2 Hi-Open f/mf Bongo 2 Lo-Muffled f/mp Conga 2 Hi-ClosedSlap/Hi-Closed Conga 2 Hi-OpenSlap/Hi-Open mf Conga 2 Lo-OpenSlap/Lo-Open mf Perc-Mad Analog 35-EXs5 Perc-Mad Analog 36-EXs5 Perc-Granular 39/38-EXs5 Tambourine 2 / 1-Pull Shaker 3/2-EXs5 Dance Perc 8 Maraca Finger Cymbal Guiro-Long Vocal SD Taiko-Open/Gamelan Perc-Granular 28-EXs5-L Telephone Ring Footstep 1/2 Zap 1 Cricket Spectrum Noise-VPM Mod Noise-White Noise-VPM Mod Agogo Bell GunShot Finger Cymbal/Marc Tree Samba Whistle 1 HiHat Granular 18-EXs5-R/L DJ-BD Rub Industry Dist.Slide 2/1 Harp-Glissando/Rainstick Castanet-Double 1/Vibraslap DJ-Record Stop/DJ-Reverse DJ-Scratch 2/1 Tribe Dance Perc 7 Cowbell BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-amb 1 mf -L BD-Dance 10 DJ Vinyl Sliced 13 BD-Dance 07 BD-Dance 08 SD-HipHop 04-EXs5 SD-Vintage 2 Dance Perc 3 House/BD-Dry 5 Voice 20 SD-Jazz Ring SD-Full Room Cymbal Reverse SD-Whopper BD-Hip 2 BD-Klanger BD-PhatJaz BD-Dance 09 Zap 1 SD-Brasser DJ Vinyl Sliced 16 SD-Ambi Hit 1 SD-AmbiHop SD-Rap 2 SD-Dance 1 Voice Perc-Kick 1/3-EXs5 Voice Perc-Kick 2a/2b-EXs5 Voice Perc-Snare 4-EXs5 Voice Perc-Snare 1-EXs5 Voice Perc-Snare 3-EXs5 Voice Perc-Snare 2-EXs5 Voice Perc-Tom-Lo-EXs5 VoicePerc-HH-Close1-EXs5 Voice Perc-Tom-Lo-EXs5 VoicePerc-HH-Close2-EXs5 Voice Perc-Tom-Mid-EXs5 Voice Perc-Cymbal-EXs5 Voice Perc-Tom-Mid-EXs5 Voice Perc-Tom-Hi-EXs5 Voice Perc-Cymbal-EXs5 Voice Perc-Tom-Hi-EXs5 Ride HipHop 2-EXs5 Car Stop IFX5 Off Voice Perc-"Pa"-EXs5 Industry Loop 1 Perc-Granular 32-EXs5 Voice Perc-Cowbell-EXs5 Voice Perc-Cymbal-EXs5 Voice Perc-Shaker-EXs5 Perc-Granular 27-EXs5 Zap 2 DJ Vinyl Sliced 18 DJ Vinyl Sliced 17 DJ-Vinyl Sliced 08-EXs5 DJ Vinyl Sliced 05 Voice 23 DJ-Reverse DJ Vinyl Sliced 03 Voice 21 DJ-Scratch 3b Voice 26 DJ-Hit Rub Voice 09 DJ Vinyl Sliced 13 DJ-Scratch 1-EXs5 DJ-Scratch 2-EXs5 DJ-Record Stop-EXs5 DJ Vinyl Sliced 08 DJ Vinyl Sliced 04 DJ Vinyl Sliced 05 Triangle-Mute Triangle-Open DJ-Vocal Rub 1 DJ Vinyl Sliced 02 DJ Vinyl Sliced 06 DJ Vinyl Sliced 07 DJ-Scratch 2-EXs5 DJ Vinyl Sliced 09 DJ Vinyl Sliced 10 DJ-Vocal Rub 2 DJ Vinyl Sliced 11 DJ Vinyl Sliced 12 DJ-Vocal Rub 2 Voice 07 PC Vox-Cks DJ-Scratch 1 DJ Vinyl Sliced 18 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Tabla-Mute 4 Tabla-Mute 2/3 Tabla-Na/Tabla-Mute 1 Tabla-Mute 1/2 Orchestral BD/BD-Dry 1 Tribe/BD-Dry 1 Finger Snap/BD-Dry 1 Hand Claps 2/BD-Dry 1 Triangle-Mute/BD-Dry 1 Triangle-Open/BD-Dry 1 Castanet-Double 2/BD-Dry 1 Castanet-Single/BD-Dry 1 Castanet-Double 1/BD-Dry 1 Guiro-Long/BD-Dry 1 Guiro-Short Guiro-Tap/BD-Dry 1 Vibraslap/BD-Dry 1 Claves/BD-Dry 1 Dance Perc 6 Clave/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Cuica 1-Hi/BD-Dry 1 Timbales Lo-Open/BD-Dry 1 Woodblock 1/BD-Dry 1 Timbales Lo-Rim/BD-Dry 1 Woodblock 1/BD-Dry 1 Timbales Lo-Mute/BD-Dry 1 Vocal Woodblock/BD-Dry 1 Timbales Hi-Edge/BD-Dry 1 Timbales Hi-Rim 2/BD-Dry 1 Chacha Bell/BD-Dry 1 Timbales Hi-Rim 1/BD-Dry 1 Mambo Bell/BD-Dry 1 Timbales-Paila/BD-Dry 1 Bongo 1 Lo-Open/BD-Dry 1 Cowbell 1/BD-Dry 1 Bongo 1 Lo-Slap/BD-Dry 1 Cowbell 2/BD-Dry 1 Bongo 1 Hi-Open/BD-Dry 1 Vocal Cowbell/BD-Dry 1 Bongo 1 Hi-Slap Conga 1 Lo-Open Conga 1 Lo-Slap/BD-Dry 1 Conga 1 Lo-MuteSlap/BD-Dry 1 Conga 1 Hi-MtSlap/BD-Dry 1 Conga 1 Hi-Open Conga 1 Hi-Mute/BD-Dry 1 Conga 1-Heel/BD-Dry 1 Conga 1 Hi-Slap 1/BD-Dry 1 Conga 1-Toe/BD-Dry 1 Conga 1 Hi-Slap 2/BD-Dry 1 Agogo Bell/BD-Dry 1 Agogo Bell/BD-Dry 1 Maracas-Pull/Push Shaker 1 Shaker 2 Cabasa-Tap/Down Cabasa-Tap/Up Caxixi-Hard/Caxixi-Soft Tambourine 1-Push/BD-Dry 1 Tambourine 2 Tambourine 1-Pull/BD-Dry 1 Tambourine 3 Sleigh Bell Samba Whistle 1/BD-Dry 1 Samba Whistle 1/BD-Dry 1 Bell Tree/BD-Dry 1 Samba Whistle 1/BD-Dry 1 Marc Tree/BD-Dry 1 Samba Whistle 1/BD-Dry 1 Rap Sleigh bell Rainstick/BD-Dry 1 Chinese Gong/BD-Dry 1 Bird 1/BD-Dry 1 Bird 2/BD-Dry 1 Bird 1/BD-Dry 1 Cricket Spectrum/BD-Dry 1 Marc Tree/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 BD-Dry 1 Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestral BD Orch.SD Roll 1 Orchestral SD Orch.SD Roll 2 Orchestral SD Orch Cymbal-Open Orch Cymbal-Closed Taiko-Open Orch.SD Roll 1 Orchestral SD Orch.SD Roll 2 Orchestral SD Triangle-Mute Triangle-Roll Triangle-Open Chinese Gong Tambourine 3 Woodblock 1 Castanet-Double 1 Woodblock 1 Castanet-Double 2 Woodblock 1 Castanet-Single Woodblock 1 Woodblock 1 Timpani Timpani Timpani Timpani Djembe-Open Djembe-Mute Djembe-Slap Caxixi-Hard/Soft Sleigh Bell Cabasa-Up/Down Finger Cymbal Baya-Ghe Baya-Open Baya-Mute 1 Baya-Mute 2 Baya-Mute 3 Baya-Mute 4 Baya-Mute 5 Tabla-Na Tabla-Open Tabla-Tin Tabla-Mute 1 Tabla-Mute 2 Tabla-Mute 3 Tabla-Mute 4 Taiko-Open Tsuzumi/Vibraslap Taiko-Rim Conga 1 Lo-Open/BD-Dry 1 Conga 1 Lo-MuteSlap / Hi-Slap 2 Conga 1 Hi-Mute / Bongo 1 Hi-Slap Conga 1 Hi-Slap 1 / BD-Dry 1 Cabasa-Up Rainstick Harp-Glissando Rainstick BD-Dry 1 BD-Dry 1 Gamelan Gamelan Bird 1 Cricket Spectrum/BD-Dry 1 Tribe BD-Dry 1 Gamelan Gamelan/Tambura Gamelan Gamelan BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-amb 1 mf -L BD-amb 1 f -L BD-amb 2 mf -L BD-amb 2 f -L BD-Dry 1 BD-Dry 2 BD-Dry 3 BD-Dry 4 BD-Dry 5 BD-Soft BD-Soft Room BD-Jazz 1 BD-Jazz 2 BD-Pillow BD-Woofer BD-Mondo Kill BD-Terminator BD-Tubby BD-Gated BD-Tight BD-Squash BD-Dance 03 BD-Dark BD-Ringy BD-Dance Soft BD-PhatJaz Orchestral BD SD-Dry 2 SD-Dry 3 SD-Dry 4 SD-Normal SD-GhostSet roll SD-GhostSet p2 SD-GhostSet p1 SD-GhostSet f SD-Full Room SD-Off Center SD-Jazz Ring SD-Amb.Piccolo 1 SD-Amb.Piccolo 2 SD-Paper SD-Classic Room SD-Brush Hit SD-Brush Tap 1 SD-Brush Tap 2 SD-Brush Swirl SD-Atomic SD-Big Rock SD-Yowie SD-Processed 1 SD-Processed 2 SD-Cracker Room Orch.SD Roll 1 SD-Sidestick Dry Orch.SD Roll 2 Crash Cymbal 2 Orchestral SD SD-Reverse Hi-Hat 2-Closed 1 SD-Hip 5 Hi-Hat 2-Pedal SD-AmbiHop Hi-Hat 2-Open 1 SD-Whopper SD-Ambi Pop SD-dry 1 p -L SD-dry 1 mp -L SD-dry 1 mf -L SD-dry 1 f -L SD-dry 1 ff -L SD-dry 1 ff -L SD-dry 1 fff -L BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 6 BD-Dance 01 BD-Dance 02 BD-Dance 11 BD-Dance 04 BD-Hip 1 BD-Hip 2 BD-Hip 3 BD-Hip 4 BD-Pop BD-Dance 08 BD-Dance 14 BD-Ambi BD-Ambi Crackle BD-Ambi Soft BD-AmbiRocker BD-Chili BD-Dance 13 BD-Dance 07 BD-Cannon BD-Klanger BD-Dance 05 (Rap) BD-Dance 06 (R&B) BD-Dance 09 BD-Dance 10 BD-Dance 12 SD-Ambi House SD-Dance 1 SD-Rap 1 SD-Rap 2 SD-Noise SD-Sizzle SD-Hip 1 SD-Hip 2 SD-Hip 3 SD-Hip 4 SD-Hip 6 SD-Ringy SD-Tiny SD-Vintage 1 SD-Vintage 2 SD-Vintage 3 SD-Vintage 4 SD-Vintage 5 SD-Vintage 6 SD-AmbiCrackle 1 SD-AmbiCrackle 2 SD-AmbiCrackle 3 SD-Brasser SD-Chili SD-Klanger SD-Dry Present SD-Ambi Hit 1 SD-Ambi Hit 2 Hi-Hat Dance 3 Closed SD-Ambi Hit 3 Hi-Hat Dance 1 Closed SD-Dance 3 (New) Hi-Hat Dance 1 Open SD-Dance 4 (Old) SD-Dance 5 SD-Dance 8 Vocal SD BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Maracas-Push Maracas-Pull Caxixi-Hard Dance Perc 1 Hip Tambourine 1-Push Tambourine 1-Pull Tambourine 2 Tambourine 3 BD-Hip 3 Hi-Hat 3-Closed 1 SD-Chili Hi-Hat 3-Closed 2 Hi-Hat 2-Closed 1 Hi-Hat 2-Closed 2 Hi-Hat 3-Open 2 Hi-Hat 2-Open 2 Hi-Hat 3-Open 1 Hi-Hat 2-Open 1 Hi-Hat 3-Sizzle Hi-Hat 2-Sizzle Hi-Hat 2-Pedal Hi-Hat 3-Pedal Hi-Hat 2-Pedal Open Hi-Hat 3-Pedal Open Hi-Hat-Chili Hi-Hat-Crisp Closed 1 Hi-Hat-Vintage 1 Hi-Hat-Crisp Closed 2 Hi-Hat-Vintage 2 Hi-Hat-Crisp Open Hi-Hat-Tight 1 Hi-Hat-Tight 2 Hi-Hat-Old Open Hi-Hat-Old Closed 1 Hi-Hat-Clangy Open Hi-Hat-Old Closed 2 Hi-Hat-Old Closed 3 Hi-Hat-Alpo Closed Hi-Hat-Amb Crackle Hi-Hat-Whispy Hi-Hat-Ambi Hi-Hat-Hip 2 Hi-Hat-Hip 1 Hi-Hat Dance 1 Closed Sleigh Bell Hi-Hat Dance 1 Open Rap Sleigh bell Hi-Hat Dance 3 Closed Hi-Hat Dance 3 Open Crash Cymbal 4 Crash-Dance 3 Crash Cymbal 2 China Cymbal Crash Cymbal 3 Splash Cymbal Ride-Dance 1 Crash-Dance 1 Ride-Dance 2 DDD1 Crash-Dance 2 DDD1 Ride Cymbal 4 Edge Ride Cymbal 5 Edge Ride Cymbal-Jazz Ride Cymbal Cup 2 Cymbal Reverse Splash Cymbal Crash-Dance 1 Ride-Dance 1 Ride Cymbal Cup 2 Hi-Hat Dance 1 Open Hi-Hat-Old Closed 2 Hi-Hat-Old Closed 3 Hi-Hat-Crisp Closed 2 Hi-Hat Dance 1 Closed Hi-Hat 3-Closed 1 Hi-Hat Dance 3 Closed Click Click Click Click Click Click Click Click Click Click Click Click Click Click Click Click Click BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Tight BD-Dry 3 SD-Sidestick Ambi SD-Full Room Dance Claps 1 SD-Full Room Tom 3-Floor Hi-Hat 2-Open 2 Tom 3-Floor Hi-Hat 3-Pedal Tom 3-Lo Hi-Hat 3-Open 2 Tom 3-Lo Tom 3-Hi Crash Cymbal 3 Tom 3-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Dry 2 BD-Dry 3 SD-Sidestick Ambi SD-Yowie Dance Claps 1 SD-Yowie Tom 4-Floor Hi-Hat 2-Open 2 Tom 4-Floor Hi-Hat 3-Pedal Tom 4-Lo Hi-Hat 3-Open 2 Tom 4-Lo Tom 4-Hi Crash Cymbal 3 Tom 4-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Gated BD-Mondo Kill SD-Sidestick Ambi SD-Big Rock Dance Claps 1 SD-Big Rock Tom 4-Floor Hi-Hat 2-Open 2 Tom 4-Floor Hi-Hat 3-Pedal Tom 4-Lo Hi-Hat 3-Open 2 Tom 4-Lo Tom 4-Hi Crash Cymbal 3 Tom 4-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Hip 1 BD-Dance 14 SD-Sidestick Ambi Tom-Synth Dance Claps 1 SD-Ambi Hit 1 Tom-Synth Hi-Hat 3-Sizzle Tom-Synth Hi-Hat 3-Pedal Tom-Synth Hi-Hat 3-Sizzle Tom-Synth Tom-Synth Crash Cymbal 3 Tom-Synth Ride Cymbal 4 Edge Crash Cymbal 2 Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Dance 06 (R&B) BD-Dance 09 Rimshot-Dance 2 SD-Dance 3 (New) Dance Claps 1 SD-Dance 4 (Old) Tom-Dance Hi-Hat Dance 3 Open Tom-Dance Hi-Hat Dance 3 Open Tom-Dance Hi-Hat Dance 3 Open Tom-Dance Tom-Dance Ride-Dance 1 Tom-Dance Crash-Dance 3 China Cymbal Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Dry 4 BD-Jazz 2 SD-Sidestick Ambi SD-Paper Dance Claps 1 SD-Jazz Ring Tom-Jazz Floor Hi-Hat 2-Closed 1 Tom-Jazz Floor Hi-Hat 2-Pedal Tom-Jazz Hi Hi-Hat 2-Open 1 Tom-Jazz Hi Tom-Jazz Hi Crash Cymbal 2 Tom-Jazz Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Dry 4 L/R Off BD-Jazz 2 L/R Off SD-Sidestick Ambi L/R Off SD-Brush Tap 1 L/R Off SD-Brush Hit L/R Off SD-Brush Swirl L/R Off Tom-Jazz Floor L/R Off Hi-Hat 2-Closed 1 L/R 1 Tom-Jazz Floor L/R Off Hi-Hat 2-Pedal L/R 1 Tom-Jazz Hi L/R Off Hi-Hat 2-Open 1 L/R 1 Tom-Jazz Hi L/R Off Tom-Jazz Hi L/R Off Crash Cymbal 2 L/R Off Tom-Jazz Hi L/R Off Ride Cymbal 4 Edge L/R Off China Cymbal L/R Off Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Hi-Hat 2-Closed 1 Hi-Hat 2-Pedal Hi-Hat 2-Open 1 Ride Cymbal 4 Edge Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Soft Room Orchestral BD SD-Sidestick Ambi Orchestral SD Conga 2 Hi-Open mf Orchestral SD Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Baya-Openf China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Orch Cymbal-Open Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Tight BD-Dry 3 SD-Sidestick Ambi SD-Full Room Perc-Granular 17-EXs5-L CompuVoice-Toi Perc-Granular 21-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Finger Snap "Cap" Crickets 1 Door Slam Rainstick Door Creak Tribe Finger Snap "Tehh" "Kaahh" CompuVoice-Do it CompuVoice-Test "Ti" "Pan" "Thuum" "Four..." Solo "Three..." Solo Perc-Granular 22-EXs5 Tambourine 2-EXs5 "Two..." Solo "One..." Solo PC Vox-Sis PC Vox-Dunc CompuVoice-Chi PC Vox-For Dog PC Vox-One Footstep 2 PC Vox-If PC Vox-Zee PC Vox-Ae PC Vox-Pa "Haaa" CompuVoice-Beep "Chhi" L/R Off CompuVoice-Ti PC Vox-O PC Vox-Tu Dog Stadium Applause Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" When received this message, transmits INQUlRY MESSAGE REPLY [ F0,7E,nn,06,01,F7 ] 3rd byte nn : Channel = 0 - F : Global Channel = 7F : Any Channel F0 7E 7F 06 01 F7 Received when in Sequencer mode [ F0,7E,nn,09,01,F7 ] 3rd byte nn : Channel = 0 - F : Global Channel = 7F : Any Channel F0 7E 7F 09 01 F7 Master Volume [ F0,7F,0g,04,01,vv,mm,F7 ] 3rd byte g : Global Channel 6th byte vv : Value(LSB) 7th byte mm : Value(MSB) mm,vv = 00,00 - 7F,7F : Min - Max F0 7F 00 04 01 00 00 F7 Master Balance [ F0,7F,0g,04,02,vv,mm,F7 ] 3rd byte g : Global Channel 6th byte vv : Value(LSB) 7th byte mm : Value(MSB) mm,vv = 00,00:Left, 40,00:Center, 7F,7F:Right F0 7F 00 04 02 00 00 F7 Control Master Tune(cent) in Global [ F0,7F,0g,04,03,vv,mm,F7 ] 3rd byte g : Global Channel 6th byte vv : Value(LSB) 7th byte mm : Value(MSB) mm,vv = 20,00:-50, 40,00:+00, 60,00:+50 F0 7F 00 04 03 00 00 F7 Control Transpose (chromatic step) in Global [ F0,7F,0g,04,04,vv,mm,F7 ] 3rd byte g : Global Channel 6th byte vv : Value(LSB) 7th byte mm : Value(MSB) mm,vv = 34,00:-12, 40,00:+00, 4C,00:+12 F0 7F 00 04 04 00 00 F7 MODE REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 12 Function F7 End of Excl (Receives this message, and transmits Func=42 message) F0 42 30 00 01 15 12 F7 MODE DATA F0, 42, 3g, 00, 01, 15 Excl Header 42 Function 0000 0mmm Mode (*2) 0000 0000 reserved 0sss ssss data1 0ddd dddd data2 00 Reserved F7 End of Excl (Receives FUNC=12 message, and transmits this message & data) ss : bit 0,1 = 0 : Note Receive is EVEN, = 1 : ODD, = 2 : ALL bit 3,4 = 0 : Seq Clock is internal, = 1 : External(MIDI), = 2 : External(USB) dd : bit 0 = 0 : Programs Memory is not protected, = 1 : protected bit 1 = 0 : Combinations Memory is not protected, = 1 : protected bit 2 = 0 : Sequence Memory is not protected, = 1 : protected bit 3 = 0 : Drum Kits Memory is not protected, = 1 : protected bit 4 = 0 : Arp Patterns Memory is not protected, = 1 : protected F0 42 30 00 01 15 42 00 00 00 00 00 F7 MODE CHANGE F0, 42, 3g, 00, 01, 15 Excl Header 4E Function 0000 0mmm Mode (*2) F7 End of Excl (Receives this message & data, changes the Mode, and transmits Func=24 message) When the Mode is changed by SW, transmits this message & data. F0 42 30 00 01 15 4E 00 F7 PARAMETER CHANGE F0, 42, 3g, 00, 01, 15 Excl Header 41 Function 0000 0mmm Mode (*2) 00 Parameter ID (MSB) 0ppp pppp Parameter ID (LSB) (TABLE 1,2,5,10,11) 00 Parameter SUB ID (MSB) 0qqq qqqq Parameter SUB ID (LSB) (TABLE 1,2,5,10,11) 0000 vvvv Value (Value bit28-31) 0vvv vvvv Value (Value bit21-27) 0vvv vvvv Value (Value bit14-20) 0vvv vvvv Value (Value bit7-13) 0vvv vvvv Value (Value bit0-6) F7 End of Excl (Even if it receives this message, a reply does not return) When the Parameter No. is changed by SW, transmits this message & data. F0 42 30 00 01 15 41 00 00 00 00 00 00 00 00 00 00 F7 DRUM KIT PARAMETER CHANGE F0, 42, 3g, 00, 01, 15 Excl Header 53 Function 0sss ssss Index No. (s = 00-7F ( : C-1-G9)) 0bbb bbbb Drum Kit Bank (*3) 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) 00 Parameter No.(MSB) 0ppp pppp Parameter No.(LSB) (TABLE 6) 0000 vvvv Value (Value bit28-31) 0vvv vvvv Value (Value bit21-27) 0vvv vvvv Value (Value bit14-20) 0vvv vvvv Value (Value bit7-13) 0vvv vvvv Value (Value bit0-6) F7 End of Excl (Even if it receives this message, a reply does not return) F0 42 30 00 01 15 53 00 00 00 00 00 00 00 00 00 00 F7 ARPEGGIO PATTERN PARAMETER CHANGE F0, 42, 3g, 00, 01, 15 Excl Header 54 Function 0000 000b Arpeggio AorB b = 0 : Arpeggio A, 1 : Arpeggio B 0000 aaaa Pattern No. MSB (bit10-7) 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) 0sss ssss Step No. ss = 00-2F ( : 00-47) 0ttt tttt Tone No. tt = 00-0B ( : 00-11) 00 Parameter No. (MSB) 0ppp pppp Parameter No. (LSB) (TABLE 4) 0000 vvvv Value (Value bit28-31) 0vvv vvvv Value (Value bit21-27) 0vvv vvvv Value (Value bit14-20) 0vvv vvvv Value (Value bit7-13) 0vvv vvvv Value (Value bit0-6) F7 End of Excl (Even if it receives this message, a reply does not return) F0 42 30 00 01 15 54 00 00 00 00 00 00 00 00 00 00 00 00 F7 PROGRAM PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 00 Object(Program) 0bbb bbbb Program Bank (*3) 00 Reserved 0ppp pppp Program No. F7 End of Excl (Receives this message, and transmits Func=73/Object=00 or Func=24 message) F0 42 30 00 01 15 72 00 00 00 00 F7 COMBINATION PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 01 Object(Combination) 0bbb bbbb Combination Bank (*3) 00 Reserved 0ccc cccc Combination No. F7 End of Excl (Receives this message, and transmits Func=73/Object=01 or Func=24 message) F0 42 30 00 01 15 72 01 00 00 00 F7 SONG TIMBRE SET DATA DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 02 Object(Song Timbre Set) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) F7 End of Excl (Receives this message, and transmits Func=73/Object=02 or Func=24 message) F0 42 30 00 01 15 72 02 00 00 00 F7 GLOBAL DATA DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 03 Object(Global) 00 Reserved 00 Reserved 00 Reserved F7 End of Excl (Receives this message, and transmits Func=73/Object=03 or Func=24 message) F0 42 30 00 01 15 72 03 00 00 00 F7 DRUM KIT PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 04 Object(Drum Kit) 0bbb bbbb Drum Kit Bank (*3) 00 Reserved 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) F7 End of Excl (Receives this message, and transmits Func=73/Object=04 or Func=24 message) F0 42 30 00 01 15 72 04 00 00 00 F7 SONG CONTROL DATA DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 08 Object(Song Control) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) F7 End of Excl (Receives this message, and transmits Func=73/Object=08 or Func=24 message) F0 42 30 00 01 15 72 08 00 00 00 F7 SONG EVENT DATA DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 09 Object(Song Event) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) F7 End of Excl (Receives this message, and transmits Func=73/Object=09 or Func=24 message) F0 42 30 00 01 15 72 09 00 00 00 F7 DRUM TRACK PATTERN DATA DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 0B Object(Drum Track Pattern) 00 Reserved 0000 0kkk Pattern No. MSB 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) F7 End of Excl (Receives this message, and transmits Func=73/Object=0B&0C or Func=24 message) F0 42 30 00 01 15 72 0B 00 00 00 F7 PROGRAM BANK PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 0F Object(Program Bank) 0bbb bbbb Program Bank (*3) 00 Reserved 00 Reserved F7 End of Excl (Receives this message, and transmits Func=73/Object=0F or Func=24 message) F0 42 30 00 01 15 72 0F 00 00 00 F7 COMBINATION BANK PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 11 Object(Combination Bank) 0bbb bbbb Combination Bank (*3) 00 Reserved 00 Reserved F7 End of Excl (Receives this message, and transmits Func=73/Object=11 or Func=24 message) F0 42 30 00 01 15 72 11 00 00 00 F7 DRUM KIT BANK PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 12 Object(Drum Kit Bank) 0bbb bbbb Drum Kit Bank (*3) 00 Reserved 00 Reserved F7 End of Excl (Receives this message, and transmits Func=73/Object=12 or Func=24 message) F0 42 30 00 01 15 72 12 00 00 00 F7 ARPEGGIO PATTERN PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 16 Object(Arpeggio Pattern) 00 Reserved 0000 aaaa Pattern No. MSB (bit10-7) 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) F7 End of Excl (Receives this message, and transmits Func=73/Object=16 or Func=24 message) F0 42 30 00 01 15 72 16 00 00 00 F7 ARPEGGIO PATTERN ALL PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 17 Object(Arpeggio Pattern All) 00 Reserved 00 Reserved 00 Reserved F7 End of Excl (Receives this message, and transmits Func=73/Object=17 or Func=24 message) F0 42 30 00 01 15 72 17 00 00 00 F7 PROGRAM PARAMETER (IN INTERNAL MEMORY) DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 00 Object(Program) 0bbb bbbb Program Bank (*3) 00 Reserved 0ppp pppp Program No. 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 1) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=00 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 00 00 00 00 00 00 F7 COMBINATION PARAMETER (IN INTERNAL MEMORY) DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 01 Object(Combination) 0bbb bbbb Combination Bank (*3) 00 Reserved 0ccc cccc Combination No. 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 2) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=01 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 01 00 00 00 00 00 F7 SONG TIMBRE SET DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 02 Object(Song Timbre Set) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 10) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=02 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 02 00 00 00 00 00 F7 GLOBAL DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 03 Object(Global) 00 Reserved 00 Reserved 00 Reserved 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 3) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=03 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 03 00 00 00 00 00 F7 DRUM KIT PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 04 Object(Drum Kit) 0bbb bbbb Drum Kit Bank (*3) 00 Reserved 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 6) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=04 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 04 00 00 00 00 00 F7 SONG CONTROL DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 08 Object(Song Control) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 11) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=08 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 08 00 00 00 00 00 F7 SONG EVENT DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 09 Object(Song Event) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 12) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=09 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 09 00 00 00 00 00 F7 DRUM TRACK PATTERN DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 0B Object(Drum Track Pattern) 00 Reserved 0000 0kkk Pattern No. MSB 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 7) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=0B message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 0B 00 00 00 00 00 F7 DRUM TRACK PATTERN EVENT DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 0C Object(Drum Track Pattern Event) 000n nnnn Drum Track Pattern Event Num Packet (0-26) 0000 0kkk Pattern No. MSB 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 8) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=0B message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 0C 00 00 00 00 00 F7 CUELIST DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 0D Object(CueList) 00 Reserved 00 Reserved 000c cccc Cuelist No. c = 0-13 ( : 0-19) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 9) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 0D 00 00 00 00 00 F7 PROGRAM BANK PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 0F Object(Program Bank) 0bbb bbbb Program Bank (*3) 00 Reserved 00 Reserved 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 1) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=0F message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 0F 00 00 00 00 00 F7 COMBINATION BANK PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 11 Object(Combination Bank) 0bbb bbbb Combination Bank (*3) 00 Reserved 00 Reserved 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 2) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=11 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 11 00 00 00 00 00 F7 DRUM KIT BANK PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 12 Object(Drum Kit Bank) 0bbb bbbb Drum Kit Bank (*3) 00 Reserved 00 Reserved 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 6) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=12 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 12 00 00 00 00 00 F7 ARPEGGIO PATTERN PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 16 Object(Arpeggio Pattern) 00 Reserved 0000 aaaa Pattern No. MSB (bit10-7) 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 4) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=16 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 16 00 00 00 00 00 F7 ARPEGGIO PATTERN ALL PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 17 Object(Arpeggio Pattern All) 00 Reserved 00 Reserved 00 Reserved 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 4) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=17 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 17 00 00 00 00 00 F7 CURRENT OBJECT PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 74 Function 0000 oooo Object (o = 0:Program/1:Combination/2:Song Timbre Set) F7 End of Excl (Receives this message, and transmits Func=75/Object=00(/01/02) or Func=24 message) F0 42 30 00 01 15 74 00 F7 CURRENT PROGRAM PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 75 Function 00 Object(Program) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 1) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=74/Object=00 message, and transmits this message & data. F0 42 30 00 01 15 75 00 00 00 F7 CURRENT COMBINATION PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 75 Function 01 Object(Combination) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 2) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=74/Object=01 message, and transmits this message & data. F0 42 30 00 01 15 75 01 00 00 F7 CURRENT SONG TIMBRE SET DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 75 Function 02 Object(Current Song Timbre) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 10) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=74/Object=02 message, and transmits this message & data. F0 42 30 00 01 15 75 02 00 00 F7 STORE OBJECT PROGRAM REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 00 Object(Program) 0bbb bbbb Bank No. (*3) F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 00 00 F7 STORE OBJECT COMBINATION REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 01 Object(Combination) 0bbb bbbb Bank No. (*3) F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 01 00 F7 STORE OBJECT CURRENT SONG TIMBRE SET REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 02 Object(Song Timbre Set) 00 Reserved F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 02 00 F7 STORE OBJECT DRUM KIT REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 04 Object(Drum Kit) 00 Reserved F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 04 00 F7 STORE OBJECT PROGRAM BANK REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 0F Object(Program Bank) 0bbb bbbb Bank No. (*3) F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 0F 00 F7 STORE OBJECT COMBINATION BANK REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 11 Object(Combination Bank) 0bbb bbbb Bank No. (*3) F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 11 00 F7 STORE OBJECT DRUM KIT BANK REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 12 Object(Drum Kit Bank) 00 Reserved F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 12 00 F7 STORE OBJECT ARPEGGIO PATTERN REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 16 Object(Arpeggio Pattern) 00 Reserved F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 16 00 F7 STORE OBJECT ARPEGGIO PATTERN ALL REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 17 Object(Arpeggio Pattern All) 00 Reserved F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 17 00 F7 OBJECT WRITE REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 77 Function 0000 oooo Object (o = 0:Program/1:Combination) 0bbb bbbb Bank (*3) 00 Reserved 0iii iiii Index No. (i = Program or Combination) F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 77 00 00 00 00 F7 RESULT REPORT F0, 42, 3g, 00, 01, 15 Excl Header 24 Function 0ccc cccc Result Code F7 End of Excl (Transmits this message when there is a result in the MIDI IN message) cc = 0 : Result OK 1 : Received Data Length is wrong 2 : Dest Memory is protected 3 : Dest Bank/Prog/Param is not exist 4 : The mode is wrong 5 : Memory over flow 40 : Another type error F0 42 30 00 01 15 24 00 F7 WAVEFORM CHANGE F0, 42, 3g, 00, 01, 15 Excl Header 7F Function 00 Object(CHANGE) 0000 cccc MIDI Channel c = 0-15 0000 000o OSC Select o = 0 or 1 0000 0bbb Bank Select b = 0 (Mono) ~ 1 (Stereo) 0nnn nnnn Number LSB 0000 nnnn Number MSB 0000 ssss Start Offset s = 0 (Off) ~ 8 (8ch) 0000 000t OSC Switch t = 0 (Off) or 1 (On) F7 End of Excl n = 0 ~ 1076 (In case of "Bank:Mono") n = 0 ~ 6 (In case of "Bank:Stereo") F0 42 30 00 01 15 7F 00 00 00 00 00 00 00 00 F7 WAVEFORM RESET F0, 42, 3g, 00, 01, 15 Excl Header 7F Function 01 Object(RESET) 0000 cccc MIDI Channel c = 0-15 F7 End of Excl F0 42 30 00 01 15 7F 01 00 F7 muse-4.0.0/src/share/instruments/Korg Krome (Bank Map KORG).idf000066400000000000000000015455071404105311000236000ustar00rootroot00000000000000 Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Closed Hi-Hat-Whispy Hi-Hat Dance 4 Open Timbales-Paila / Shaker Hi-Hat-Crisp Open/Chili/ Crisp Open Hi-Hat-Vintage Hi-Hat 2-Pedal Open/Closed/Pedal BD-Dance 04 L/R BD-Dance 02 BD-Dance 04 Dance 07 BD-Dance 10 Orch.SD Roll 1 Orch.SD Roll 2 Orch.SD Roll 1 Orch.SD Roll 2 SD-Sidestick Hi Cymbal Reverse SD-Reverse IFX1 BD-01a-dry BD-03a-dry BD-03a-dry BD-02b-dry BD-02a-dry SD-06-dry SD-05-dry SD-04-dry SD-03-dry SD-02-dry SD-07-dry BD-01b-dry BD-01a-dry Sidestick-dry SD-01a-dry Hand Claps 1 SD-01b-dry Tom Lo-dry HiHat-Closed-dry Tom Lo-dry HiHat-Pedal1-dry Tom Mid-dry HiHat-Open-dry Tom Mid-dry Tom Hi-dry Crash 01-dry Tom Hi-dry Ride 01-dry China-dry Ride 02-dry Tambourine 3/1-Push/1-Pull Splash-dry Cowbell 2 Crash 02-dry Vibraslap Ride Bongo 2 Hi-Open f/mf/f Bongo 2 Lo-Muffled f/mp/Lo-Closed Conga 2 Hi-ClosedSlap/Hi-Closed Conga 2 Hi-OpenSlap/mf/f/mp Conga 2 Lo-OpenSlap/mf/f Timbales Hi-Rim 1/2/Hi-Edge Timbales Lo-Rim/Open Agogo Bell IFX5 Off Agogo Bell IFX5 Off Cabasa-Up/Down Maracas-Pull/Push Samba Whistle 2 Samba Whistle 2 Guiro-Short Guiro-Long Claves Woodblock 1 Woodblock 1 Cuica 2-Hi Cuica 2-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause BD-Dance 13-EXs5 BD-Dance 12-EXs5 BD-Mad Analog 03-EXs5 BD-HipHop 07-EXs5 BD-Dance 15-EXs5 SD-Mad Analog 11-EXs5 SD-Granular 05-EXs5-R SD-Mad Analog 05-EXs5 SD-Dance 12-EXs5 SD-Mad Analog 13-EXs5 BD-Mad Analog 06-EXs5 BD-Mad Analog 07-EXs5 BD-Dance 11 BD-Dance 03 BD-Dance 08 BD-Dance 01 BD-Dance 07/Tom-Dance SD-Dance 4 (Old) SD-Dance 5 Noise-White/SD-Sidestick Ambi SD-Rap 2 Dance Claps 3/Click SD-Dance 6 BD-Dance 06 (R&B) BD-Dance 07 Rimshot-Dance 2 SD-Dance 3 (New) Dance Claps 1 SD-Dance 2/SD-Sidestick Dry Tom-Dance Hi-Hat Dance 3 Closed Tom-Dance Hi-Hat Dance 3 Open Tom-Dance Hi-Hat Dance 3 Open Dance Perc 8 Maraca Hi-Hat Dance 4 Closed Crash-Dance 3 Hi-Hat Dance 4 Open Ride-Dance 1 Crash-Dance 2 DDD1 Ride-Dance 2 DDD1 Tambourine 3/2 Noise-VPM Mod Dance Perc 7 Cowbell Crash-Dance 1 Cricket Spectrum/Tambourine 3 Perc-Mad Analog 06-EXs5 Zap 1 Zap 2 Dance Perc 6 Clave/BD-Dance 05 (Rap) Dance Perc 5 Conga Dance Perc 5 Conga HiHat Mad Analog 01-EXs5 Triangle-Mute/BD-Dance 05 (Rap) HiHat Mad Analog 16-EXs5 Triangle-Open/Tom-Brush Hi Hi-Hat Dance 5 Closed/DJ-SD Rub Hi-Hat Dance 5 Open Rimshot-Dance 1 PC Vox-Cks/BD-Woofer Hi-Hat Dance 1 Closed/BD-Dry 1 Noise-White/SD-Amb.Piccolo 1 Hi-Hat Dance 2 Open/DJ-Record Stop PC Vox-Sis/SD-Paper PC Vox-Dunc/Vocal SD Hi-Hat-Alpo Closed PC Vox-For/Tom-Brush Hi Hi-Hat-Amb Crackle/Ride-Dance 1 PC Vox-Zee/Tom-Jazz Hi Rim Hi-Hat-Crisp Open/Ride-Dance 1 PC Vox-Pa/Swish Terra PC Vox-Tu/BD-Jazz 2 Crash Cymbal 2 China Cymbal Ride Cymbal 4 Edge Conga 1 Hi-Slap 1/Lo-Open Conga 1 Hi-Open/Lo-MuteSlap Ride Cymbal Cup 2 Conga 1 Hi-Slap 2/Hi-Mute Cowbell Chacha Bell Cowbell 1 Guiro-Short/BD-Dry 1 Guiro-Tap Metal Hit/Finger Cymbal Dance Perc 1 Hip/BD-Dry 1 Swish Terra Cyber Drum Tambourine 1-Push Air Vortex Tubular Telephone Ring Footstep 2 Triangle-Mute Tribe Dance Perc 1 Hip/BD-Dry 1 BD-amb 1 mf -L Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Open Hi-Hat-Chili Hi-Hat-Old Open Timbales-Paila Hi-Hat-Crisp Open Hi-Hat-Vintage 1 Hi-Hat 3-Pedal Open BD-Dance 01-EXs5 BD-Dance 14-EXs5 BD-TekClub 12-EXs5 BD-HipHop 08b/a-EXs5 BD-Hip 2 SD-Dance 09-EXs5 SD-TekClub 04-EXs5 SD-HipHop 07-EXs5 SD-Mad Analog 15-EXs5 SD-TekClub 16-EXs5 Cymbal Reverse SD-Ringy BD-Dance 15-EXs5 BD-Dance 10-EXs5 BD-TekClub 03-EXs5 BD-Dance 08-EXs5 BD-Dance 11-EXs5 SD-Dance 14-EXs5 SD-Dance 19-EXs5 SD-Dance 12-EXs5 SD-Dance 20-EXs5 SD-Dance 21-EXs5 SD-Dance 18/17-EXs5 BD-Dance 12-EXs5 BD-Dance 13-EXs5 SD-Dance 13-EXs5 SD-Dance 15-EXs5 SD-Dance 16-EXs5 SD-Dance 14-EXs5 Tom-Dance d-EXs5 Hi-Hat Dance 06-EXs5 Tom-Dance d-EXs5 Hi-Hat Dance 07-EXs5 Tom-Dance e-EXs5 Hi-Hat Dance 11-EXs5 Tom-Dance f-EXs5 Tom-Dance g-EXs5 Crash Dance 1-EXs5 Tom-Dance h-EXs5 Crash Dance 4-EXs5 Crash Dance 3-EXs5 Ride HipHop 2-EXs5 Tambourine 3/1-EXs5 Crash Dance 2-EXs5 Perc-Dance 01-EXs5 Crash HipHop 1-EXs5-R Perc-Dance 09-EXs5 Ride HipHop 1-EXs5 Perc-Dance 02-EXs5 Perc-Dance 03-EXs5 Perc-Dance 12-EXs5 Perc-Dance 05-EXs5 Perc-Dance 04-EXs5 Hi-Hat Dance 09-EXs5 Hi-Hat Dance 10-EXs5 Perc-Mad Analog 19-EXs5 Perc-Mad Analog 19-EXs5 Shaker 4-EXs5 Shaker 5-EXs5 Orchestral BD Orchestral BD Zap-EXs5 Perc-Dance 07-EXs5 Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Closed Hi-Hat-Whispy Hi-Hat Dance 4 Open Timbales-Paila / Shaker Hi-Hat-Crisp Open/Chili/ Crisp Open Hi-Hat-Vintage Hi-Hat 2-Pedal Open/Closed/Pedal BD-Dance 04 L/R BD-Dance 02 BD-Dance 04 Dance 07 BD-Dance 10 Orch.SD Roll 1 Orch.SD Roll 2 Orch.SD Roll 1 Orch.SD Roll 2 SD-Sidestick Hi Cymbal Reverse SD-Reverse IFX1 BD-01a-dry BD-03a-dry BD-03a-dry BD-02b-dry BD-02a-dry SD-06-dry SD-05-dry SD-04-dry SD-03-dry SD-02-dry SD-07-dry BD-01b-dry BD-01a-dry Sidestick-dry SD-01a-dry Hand Claps 1 SD-01b-dry Tom Lo-dry HiHat-Closed-dry Tom Lo-dry HiHat-Pedal1-dry Tom Mid-dry HiHat-Open-dry Tom Mid-dry Tom Hi-dry Crash 01-dry Tom Hi-dry Ride 01-dry China-dry Ride 02-dry Tambourine 3/1-Push/1-Pull Splash-dry Cowbell 2 Crash 02-dry Vibraslap Ride Bongo 2 Hi-Open f/mf/f Bongo 2 Lo-Muffled f/mp/Lo-Closed Conga 2 Hi-ClosedSlap/Hi-Closed Conga 2 Hi-OpenSlap/mf/f/mp Conga 2 Lo-OpenSlap/mf/f Timbales Hi-Rim 1/2/Hi-Edge Timbales Lo-Rim/Open Agogo Bell IFX5 Off Agogo Bell IFX5 Off Cabasa-Up/Down Maracas-Pull/Push Samba Whistle 2 Samba Whistle 2 Guiro-Short Guiro-Long Claves Woodblock 1 Woodblock 1 Cuica 2-Hi Cuica 2-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause HHat-Dance5-amb1 HHat-Dance7-amb1 HHat-Whispy-amb1 HHat-Dance4/1-amb1 TimbalesH-Paila-a1 / Shaker 1-amb1 H.Hat-Crisp/Chili-amb1 H.Hat-Old-amb1 HHat-Pedal3/2-amb1 BD-amb-01 BD-amb-02 BD-amb-03 BD-amb-04 BD-amb-05 SD-Orch Roll-amb1 SD-Orch Roll-amb1 SD-Orch Roll-amb1 SD-Orch Roll-amb1 Stick Hit-amb Reverse Cymbal-amb SD Reverse-amb BD-01a-amb1 BD-03b-amb1 BD-03a-amb1 BD-02b-amb1 BD-02a-amb1 SD-06-amb1 SD-05-amb1 SD-04-amb1 SD-03-amb1 SD-02-amb1 SD-07-amb1 BD-01b-amb1 BD-01a-amb1 Sidestick-amb1 SD-01a-amb1 Claps-Dance-amb1 SD-01b-amb1 Tom Lo-amb1 HiHat-Cl-amb1 Tom Lo-amb1 HHat-Pdl1-amb1 Tom Mid-amb1 HiHat-Op-amb1 Tom Mid-amb1 Tom Hi-amb1 Crash 01-amb1 Tom Hi-amb1 Ride 01-amb1 China-amb1 Ride 02-amb1 Tambourine 3/1/2-amb1 Splash-amb1 Cowbell 2-amb1 Crash 02-amb1 Vibra-Slap-amb1 Ride 03-amb1 Bongo H-Op f-amb1 Bongo L-Muf1/L-Muf2/Lo-Cl-amb1 Conga H-Slp2/Hi-Op1-amb1 Conga H-Slp1/Hi-Op2/Hi-Op3/Hi-Op1-amb1 Conga L-Slap/Lo-Op1/Lo-Op2-amb1 Timbales H-Rim2/H-Rim1/H-Edge-a1 Timbales L-Ege1/L-Op-a1 Agogo Lo-amb1 Agogo Hi-amb1 Cabasa 1/3-amb1 Maracas-Pull1-amb1 Whistle 3-amb1 Whistle 3-amb1 Guiro-Short a-amb1 Guiro-Long a-amb1 Claves b-amb1 Woodblock 1/2-amb1 Woodblock 3/4-amb1 Cuica Hi c-amb1 Cuica Lo-amb1 Triangle-Mute1/2-a1 Triangle-Mute1/2-a1 Shaker 1/2-amb1 Marc Tree-amb1 BD-Dance-amb-01 HHat Dance8-amb1 SD-Dance-amb1 HHat Dance7-amb1 Crash-Dance-amb1 Perc-Dance1-amb1 Claps-Dance-amb1 Perc-Dance2-amb1 SD-Orch Roll-amb1 SD-Orch-amb1 Bell Tree-amb1 Stadium-amb1 Applause-amb1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute1/3 Baya-Mute5/4 Tabla-Open Tabla-Tin/Mute4 Drumstick Hit/BD-Dry 1 Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry BD-Dry 1 BD-Dry 4 BD-Soft BD-Soft Room BD-Pillow SD-Full Room SD-Classic Room SD-Amb.Piccolo 1 SD-Paper SD-Dry 4 SD-Dry 2 BD-Dry 3 BD-Dry 2 SD-Sidestick Dry SD-Amb.Piccolo 2 Hand Claps 2 SD-Big Rock Tom 3-Floor Hi-Hat 2-Closed 2 Tom 3-Floor Hi-Hat 2-Pedal Open Tom 3-Lo Hi-Hat 2-Open 1/2 Tom 3-Lo Tom 3-Hi Crash Cymbal 3/BD-Dry 1 Tom 3-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Tambourine 2 Splash Cymbal Cowbell 1/BD-Dry 1 Crash Cymbal 2/BD-Dry 1 Vibraslap/BD-Dry 1 Ride Cymbal 5 Edge Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/Toe Conga 1 Hi-Open Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Agogo Bell/BD-Dry 1 Agogo Bell/BD-Dry 1 Cabasa-Up/Down Maracas-Push Samba Whistle 1/Chinese Gong Samba Whistle 1/Chinese Gong Guiro-Short/Tap Guiro-Long/BD-Dry 1 Claves/BD-Dry 1 Woodblock 1/BD-Dry 1 Woodblock 1/BD-Dry 1 Cuica/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) / 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute/3 Baya-Mute5/4 Tabla-Open Tabla-Tin/Mute 4 Drumstick Hit/BD-Dry 1 Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-Tubby BD-Gated BD-Chili BD-Hip 4 BD-PhatJaz SD-Chili/SD-Jazz Ring SD-Ambi Hit 2/SD-Classic Room SD-Brasser/SD-Paper SD-Vintage 4/SD-Brush Tap 1 SD-Hip 5 SD-GhostSet roll BD-Dry 6 BD-Dry 5 SD-Sidestick Ambi SD-GhostSet f/p2 Hand Claps 2 SD-Klanger/GhostSet p1 Tom 2-Floor Hi-Hat 3-Closed 2/1 Tom 2-Floor Hi-Hat 3-Pedal Open / Pedal Tom 2-Lo Hi-Hat 3-Sizzle/Open 1 Tom 2-Lo Tom 2-Hi Crash Cymbal 3/BD-Dry 1 Tom 2-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Tambourine 2 / 1-Push Splash Cymbal Cowbell 1/BD-Dry 1 Crash Cymbal 2/BD-Dry 1 Vibraslap/BD-Dry 1 Ride Cymbal 5 Edge Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open/Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Agogo Bell/BD-Dry 1 Agogo Bell/BD-Dry 1 Cabasa-Up/Down Maracas-Push Samba Whistle 1/Chinese Gong Samba Whistle 1/Chinese Gong Guiro-Short/Tap Guiro-Long/BD-Dry 1 Claves/BD-Dry 1 Woodblock 1/BD-Dry 1 Woodblock 1/BD-Dry 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open/BD-Dry 1 Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) / 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap?Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Mute 4 Drumstick Hit Cymbal Reverse/SD-Reverse SD-Reverse BD-Tight/BD-amb 2 mf -L BD-Squash/BD-amb 2 f -L BD-Gated/BD-amb 1 f -L BD-Tubby/BD-amb 1 mf -L BD-Tight/BD-amb 2 mf -L SD-Full Room/SD-dry 1 f -L SD-Dry 2/SD-dry 1 mp -L SD-Amb.Piccolo 2/SD-dry 1 fff -L SD-Dry 3/SD-dry 1 mp -L SD-GhostSet f/SD-dry 1 f -L SD-Yowie/SD-dry 1 f -L BD-Dry 3/BD-amb 2 mf -L BD-Dry 2/BD-amb 2 f -L SD-Sidestick Ambi/SD-Sidestick mf -L SD-Sidestick Ambi/SD-dry 1 f -L Hand Claps 2/1 -L SD-GhostSet f/SD-dry 1 p -L Tom 2-Floor/Tom 1-Lo f -L Hi-Hat 1-Closed ff/mf Tom 2-Floor/Tom 1-MidLo f -L Hi-Hat 1-Pedal ff/1-Pedal mf Tom 2-Floor/Tom 1-MidHi f -L Hi-Hat 1-Open ff/mf Tom 2-Floor/Tom 1-MidHi f -L Tom 3-Floor/Tom 1-Hi mf -L Crash Cymbal 3/1 mf -L Tom 3-Floor/Tom 1-Hi mf -L Ride Cymbal 4 Edge / 1 -L China Cymbal Ride Cymbal Cup 2/Cup 1 -L Tambourine 2/1-Push Splash Cymbal/Crash Cymbal 1 mf -L Cowbell 1/BD-Dry 1 Crash Cymbal 2/Crash Cymbal 1 mf -L Vibraslap/BD-Dry 1 Crash Cymbal 4/Ride Cymbal 1 -L Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Tom-Processed Timbales Hi-Rim 1/Hi-Edge Tom-Processed Timbales Lo-Open Tom-Processed Maracas-Push Tom-Processed Tom-Processed Cabasa-Tap/Up Tom-Processed Claves Woodblock 1 Woodblock 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) / 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Mute 4 Drumstick Hit Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-Tight BD-Squash BD-Gated BD-Tubby BD-Woofer SD-Full Room SD-Cracker Room SD-Amb.Piccolo 2 SD-Processed 1 SD-Yowie SD-Atomic BD-Terminator BD-Mondo Kill SD-Sidestick Ambi SD-Big Rock Hand Claps 2 SD-Processed 2 Tom 4-Floor Hi-Hat 3-Closed 2/1 Tom 4-Lo Hi-Hat 3-Pedal Open/Pedal Tom 4-Lo Hi-Hat 3-Open 1/2 Tom 4-Lo Tom 4-Hi Crash Cymbal 3/BD-Dry 1 Tom 4-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Tambourine 2/1-Push Splash Cymbal Cowbell 1/BD-Dry 1 Crash Cymbal 2/BD-Dry 1 Vibraslap/BD-Dry 1 Ride Cymbal 5 Edge Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Tom-Processed Timbales Hi-Rim 1/Hi-Edge Tom-Processed Timbales Lo-Open Tom-Processed Maracas-Push Tom-Processed Tom-Processed Cabasa-Tap/Up Tom-Processed Claves Woodblock 1 Woodblock 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) / 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca Orch.SD Roll 2/1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Mute 4 Drumstick Hit Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-Tight/BD-amb 2 mf -L BD-Squash/BD-amb 2 mf -L BD-Gated/BD-amb 1 f -L BD-Tubby/BD-amb 1 mf -L BD-Woofer/BD-amb 1 mf -L SD-Full Room/SD-dry 1 mp -L SD-Cracker/SD-dry 1 ff -L SD-Amb.Piccolo 2/SD-dry 1 ff -L SD-Processed 1/SD-dry 1 ff -L SD-Yowie/SD-dry 1 fff -L SD-Atomic/SD-dry 1 fff -L BD-Terminator/BD-amb 2 mf -L BD-Mondo Kill/BD-amb 2 f -L SD-Sidestick Ambi/SD-Sidestick mf -L SD-Big Rock/SD-dry 1 fff -L Hand Claps 2/1 -L SD-Processed 2/SD-dry 1 mf -L Tom 3-Floor/Tom 1-Lo f -L Hi-Hat 1-Closed ff/mf Tom 3-Lo/Tom 1-MidLo f -L Hi-Hat 1-Pedal ff/mf Tom 3-Lo/Tom 1-MidHi f -L Hi-Hat 1-Open ff/mf Tom 3-Lo/Tom 1-MidHi f -L Tom 3-Hi/Tom 1-Hi mf -L Crash Cymbal 3/1 mf -L Tom 3-Hi/Tom 1-Hi mf -L Ride Cymbal 4 Edge/Ride Cymbal 1 -L China Cymbal Ride Cymbal Cup 2/Cup 1 -L Tambourine 2/1-Push Splash Cymbal/Crash Cymbal 1 mf -L Cowbell 1/BD-Dry 1 Crash Cymbal 2/1 mf -L Vibraslap/BD-Dry 1 Cymbal 4 Edge/Ride Cymbal 2 -L Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/Conga 1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Tom-Processed Timbales Hi-Rim 1/Hi-Edge Tom-Processed Timbales Lo-Open Tom-Processed Maracas-Push Tom-Processed Tom-Processed Cabasa-Tap/Up Tom-Processed Claves Woodblock 1 Woodblock 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) / 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Tom 2-Floor Tsuzumi Tom 2-Lo Taiko-Rim/Open Tom 2-Hi Tom 2-Hi Chacha Bell/BD-Dry 1 Finger Snap Hand Claps 2 Drumstick Hit/BD-Dry 1 BD-Tubby BD-Dry 3 BD-Woofer BD-Dry 4 BD-Soft BD-Jazz 1 BD-Jazz 2 SD-Paper SD-Dry 3 SD-Dry 2 SD-Amb.Piccolo 1 SD-Jazz Ring SD-Brush Hit/BD-Dry 1 BD-Jazz 2 BD-Pillow SD-Sidestick Dry SD-Brush Tap 2/BD-Dry 1 SD-Brush Tap 1/BD-Dry 1 SD-Brush Swirl/BD-Dry 1 Tom-Brush Floor Hi-Hat 2-Closed 2/1 Tom-Brush Hi Hi-Hat 2-Pedal Tom-Brush Hi Hi-Hat 2-Open 1/2 Tom-Jazz Floor Tom-Jazz Hi Rim/Hi Crash Cymbal 2/BD-Dry 1 Tom-Jazz Hi Rim/Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Tambourine 2/1-Push Splash Cymbal Cowbell 1 Crash Cymbal 4 Vibraslap/BD-Dry 1 Ride Cymbal 5 Edge Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/Toe Conga 1 Hi-Open Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Agogo Bell/BD-Dry 1 Agogo Bell/BD-Dry 1 Cabasa-Up/Down Maracas-Push/BD-Dry 1 Samba Whistle 1/Chinese Gong Samba Whistle 1/Chinese Gong Guiro-Short/Tap Guiro-Long/BD-Dry 1 Claves/BD-Dry 1 Woodblock 1/BD-Dry 1 Woodblock 1/BD-Dry 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open/BD-Dry 1 Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Closed Hi-Hat-Whispy Hi-Hat Dance 4 Open Timbales-Paila / Shaker Hi-Hat-Crisp Open/Chili/ Crisp Open Hi-Hat-Vintage Hi-Hat 2-Pedal Open/Closed/Pedal BD-Dance 04 L/R BD-Dance 02 BD-Dance 04 Dance 07 BD-Dance 10 Orch.SD Roll 1 Orch.SD Roll 2 Orch.SD Roll 1 Orch.SD Roll 2 SD-Sidestick Hi Cymbal Reverse SD-Reverse IFX1 BD-01a-dry BD-03a-dry BD-03a-dry BD-02b-dry BD-02a-dry SD-06-dry SD-05-dry SD-04-dry SD-03-dry SD-02-dry SD-07-dry BD-01b-dry BD-01a-dry Sidestick-dry SD-01a-dry Hand Claps 1 SD-01b-dry Tom Lo-dry HiHat-Closed-dry Tom Lo-dry HiHat-Pedal1-dry Tom Mid-dry HiHat-Open-dry Tom Mid-dry Tom Hi-dry Crash 01-dry Tom Hi-dry Ride 01-dry China-dry Ride 02-dry Tambourine 3/1-Push/1-Pull Splash-dry Cowbell 2 Crash 02-dry Vibraslap Ride Bongo 2 Hi-Open f/mf/f Bongo 2 Lo-Muffled f/mp/Lo-Closed Conga 2 Hi-ClosedSlap/Hi-Closed Conga 2 Hi-OpenSlap/mf/f/mp Conga 2 Lo-OpenSlap/mf/f Timbales Hi-Rim 1/2/Hi-Edge Timbales Lo-Rim/Open Agogo Bell IFX5 Off Agogo Bell IFX5 Off Cabasa-Up/Down Maracas-Pull/Push Samba Whistle 2 Samba Whistle 2 Guiro-Short Guiro-Long Claves Woodblock 1 Woodblock 1 Cuica 2-Hi Cuica 2-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Closed Hi-Hat-Whispy Hi-Hat Dance 4 Open Timbales-Paila / Shaker Hi-Hat-Crisp Open/Chili/ Crisp Open Hi-Hat-Vintage Hi-Hat 2-Pedal Open/Closed/Pedal BD-Dance 04 L/R BD-Dance 02 BD-Dance 04 Dance 07 BD-Dance 10 Orch.SD Roll 1 Orch.SD Roll 2 Orch.SD Roll 1 Orch.SD Roll 2 SD-Sidestick Hi Cymbal Reverse SD-Reverse IFX1 BD-01a-dry BD-03a-dry BD-03a-dry BD-02b-dry BD-02a-dry SD-06-dry SD-05-dry SD-04-dry SD-03-dry SD-02-dry SD-07-dry BD-01b-dry BD-01a-dry Sidestick-dry SD-01a-dry Hand Claps 1 SD-01b-dry Tom Lo-dry HiHat-Closed-dry Tom Lo-dry HiHat-Pedal1-dry Tom Mid-dry HiHat-Open-dry Tom Mid-dry Tom Hi-dry Crash 01-dry Tom Hi-dry Ride 01-dry China-dry Ride 02-dry Tambourine 3/1-Push/1-Pull Splash-dry Cowbell 2 Crash 02-dry Vibraslap Ride Bongo 2 Hi-Open f/mf/f Bongo 2 Lo-Muffled f/mp/Lo-Closed Conga 2 Hi-ClosedSlap/Hi-Closed Conga 2 Hi-OpenSlap/mf/f/mp Conga 2 Lo-OpenSlap/mf/f Timbales Hi-Rim 1/2/Hi-Edge Timbales Lo-Rim/Open Agogo Bell IFX5 Off Agogo Bell IFX5 Off Cabasa-Up/Down Maracas-Pull/Push Samba Whistle 2 Samba Whistle 2 Guiro-Short Guiro-Long Claves Woodblock 1 Woodblock 1 Cuica 2-Hi Cuica 2-Lo Triangle-Mute Triangle-Open Shaker 1 Marc Tree BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause Hi-Hat 3-Closed 1 Hi-Hat 1-Closed mf Hi-Hat 1-Open mf Hi-Hat-Whispy Hi-Hat 1-Open ff Hi-Hat-Chili Hi-Hat-Alpo Closed DJ Vinyl Sliced 04 BD-Dry 3 DJ Vinyl Sliced 13 BD-Dance 03 BD-Dance 06 (R&B) BD-Ambi SD-Brasser "kkk" Voice 20 SD-Ambi Pop SD-Dry 2 Cymbal Reverse Finger Snap BD-Dance 06-EXs5 BD-Dance 05-EXs5 BD-Dance 03-EXs5 BD-Dance 06-EXs5 BD-Dance 09-EXs5 SD-Dance 05-EXs5 SD-Dance 10-EXs5 SD-Dance 07-EXs5 SD-Dance 09-EXs5 SD-Dance 02-EXs5 SD-Dance 04-EXs5 BD-Dance 04-EXs5 BD-Dance 08-EXs5 SD-Dance 06-EXs5 SD-Dance 03-EXs5 SD-Dance 01-EXs5 SD-Dance 07-EXs5 Tom-Dance i-EXs5 Hi-Hat Dance Tom-Dance j-EXs5 Hi-Hat Dance 02-EXs5 Tom-Dance k-EXs5 Hi-Hat Dance 04-EXs5 Tom-Dance a-EXs5 Tom-Dance b-EXs5 Crash Dance 1-EXs5 Tom-Dance c-EXs5 Ride Dance-EXs5 Crash Dance 2-EXs5 Orch Cymbal-Open Tambourine 1-EXs5 Cymbal Reverse 1-EXs5 Perc-Dance 01-EXs5 Crash HipHop 1-EXs5-L Sleigh Bell Crash Dance 4-EXs5 Perc-Dance 02-EXs5 Perc-Dance 03-EXs5 Perc-Dance 12-EXs5 Perc-Dance 05-EXs5 Perc-Dance 04-EXs5 Hi-Hat Dance 03-EXs5 Hi-Hat Dance 05-EXs5 Tambourine 2-EXs5 Tambourine 3-EXs5 Shaker 1-EXs5 Shaker 3-EXs5 DJ-Scratch 2-EXs5 DJ-Horn-EXs5 DJ-Scratch 1-EXs5 DJ-Big Cannon-EXs5 Perc-Mad Analog 37-EXs5 DJ-Vinyl Sliced 09/03/11/05-EXs5 DJ-Vinyl Sliced 12/09/12/02-EXs5 DJ Vinyl Sliced 05/04/07/06-EXs5 DJ-Record Stop-EXs5 Triangle-Mute-EXs5 Triangle Open 1-EXs5 Shaker 2-EXs5 Marc Tree Rev-EXs5 BD-Dance 01-EXs5 Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Voice 12 Voice 09 Bell Tree Stadium Applause Orchestral SD Voice 21 Voice 23 Voice 24 Voice 25 Voice 26 GunShot BD-amb 1 mf -L Hi-Hat Dance 3 Closed Hi-Hat Dance 3 Ope Hi-Hat-Old Closed 2 Hi-Hat Dance 4 Closed Timbales-Paila/Shaker 4-EXs5 Hi-Hat 3-Closed 1/Hi-Hat-Chili/Hi-Hat Dance 5 Closed Hi-Hat-Vintage 1/Hi-Hat-Crisp Closed 2 Hi-Hat 2-Pedal Open / 2-Closed 1 / 2-Pedal BD-HipHop 01b/02a-EXs5 BD-HipHop 11-EXs5 BD-HipHop 10/09-EXs5 BD-HipHop 14b/a-EXs5 BD-HipHop 04-EXs5 SD-HipHop 09-EXs5-L SD-HipHop 02-EXs5 SD-HipHop 04-EXs5 SD-HipHop 06a-EXs5 SD-HipHop 10-EXs5 Crash-Dance 1 SD-Yowie BD-HipHop 11-EXs5/BD-Dance 11-EXs5 BD-HipHop 06-EXs5 BD-HipHop 12/03-EXs5 BD-HipHop 02b/a-EXs5 BD-HipHop 06/12-EXs5 SD-HipHop 07-EXs5/SD-HipHop 11-EXs5-L SD-HipHop 08-EXs5 SD-HipHop 03-EXs5-L SD-HipHop 13-EXs5/SD-dry 1 ff -L/mp -L/p -L SD-HipHop 12-EXs5-L SD-HipHop 07-EXs5 BD-HipHop 13b/a-EXs5-L BD-HipHop 07/08b/08a-EXs5 Sidestick-HipHop-EXs5-L SD-HipHop 15d/c/b/a-EXs5-L Clap HipHop 1/2-EXs5 SD-HipHop 15a-EXs5-L/Perc-Granular 21-EXs5 Tom-HipHop Lo 3/2/1-EXs5 Hi-Hat HipHop 03/02/01-EXs5 Tom-HipHop Mid Lo 4/3/2/1-EXs5 Hi-Hat HipHop 06/07-EXs5 Tom-HipHop Mid Hi 4/3/2/1-EXs5 Hi-Hat HipHop 05/04-EXs5 Tom-HipHop Mid Hi 4/3/2/1-EXs5 Tom-HipHop Hi 4/3/2/1-EXs5 Crash HipHop 1-EXs5-L Tom-HipHop Hi 4/3/2/1-EXs5 Ride Cymbal 4 Edge Hi-Hat Dance 5 Open Ride HipHop 2-EXs5 Perc-Granular 36/39/37-EXs5 Crash-Dance 3 Perc-Mad Analog 19-EXs5 Crash HipHop 2-EXs5 Wind Ride HipHop 1-EXs5 Perc-Mad Analog 32-EXs5 Perc-Mad Analog 34-EXs5 Bongo 1 Lo-Stick Conga 1 Hi-Open Conga 1 Hi-Slap 1 Perc-Mad Analog 22/21/20-EXs5 Perc-Mad Analog 24-EXs5 Perc-Mad Analog 28-EXs5 Perc-Mad Analog 29-EXs5 Perc-Mad Analog 23-EXs5 Perc-Granular 36-EXs5 Perc-Mad Analog 06-EXs5 Perc-Mad Analog 05-EXs5 Perc-Mad Analog 12-EXs5 Perc-Mad Analog 13-EXs5 Perc-Mad Analog 37-EXs5 Perc-Mad Analog 30-EXs5 Perc-Mad Analog 30-EXs5 Perc-TekClub 18-EXs5 Perc-TekClub 18-EXs5 Perc-Granular 46-EXs5 Perc-Granular 46-EXs5 Perc-Mad Analog 35-EXs5 Marc Tree BD-HipHop 03-EXs5 Hi-Hat Dance 3 Closed SD-HipHop 02-EXs5 Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Tabla-Mute 4 Drumstick Hit/BD-Dry 1 Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-PhatJaz/BD-Dry 1 BD-Hip 1/BD-Dry 4 BD-Dance 04/BD-Dance 04 BD-AmbiRocker BD-Dance 13/BD-Pillow SD-Vintage 5/SD-Full Room SD-Ambi Hit 1/SD-Classic Room SD-AmbiCrackle 2/SD-Amb.Piccolo 1 SD-Vintage 2/SD-Paper SD-Hip 3/SD-Dry 4 SD-Ambi House/SD-Dry 2 BD-Dance 07/BD-Dry 3 BD-Ambi/BD-Dry 2 SD-Sidestick Dry SD-Whopper/SD-Amb.Piccolo 2 Hand Claps 4 Happy/2 SD-Vintage 1/SD-Big Rock Tom-Dirty Funk Hi-Hat-Amb Crackle/Hi-Hat 2-Closed 2 Tom-Dirty Funk Hi-Hat-Old Closed 2/Hi-Hat 2-Pedal Tom-Dirty Funk Hi-Hat-Crisp Open / 2-Open 2 Tom-Old R&B Tom-Old R&B Crash Cymbal 3 Tom-Old R&B Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Tambourine 2 / 1-Push Splash Cymbal Cowbell 1/BD-Dry 1 Crash Cymbal 2/BD-Dry 1 Vibraslap/BD-Dry 1 Ride Cymbal 5 Edge Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Agogo Bell/BD-Dry 1 Agogo Bell/BD-Dry 1 Cabasa-Up/Down Maracas-Push Samba Whistle 1/Chinese Gong Samba Whistle 1/Chinese Gong Guiro-Short/Guiro-Tap Guiro-Long/BD-Dry 1 Claves/BD-Dry 1 Woodblock 1/BD-Dry 1 Woodblock 1/BD-Dry 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open/BD-Dry 1 Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06/05 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 DJ Vinyl Sliced 15 DJ Vinyl Sliced 13 Voice Perc-Kick 2b-EXs5 BD-Mad Analog 08-EXs5 BD-Mad Analog 12-EXs5 SD-HipHop 12-EXs5-R Sidestick-HipHop-EXs5-L Voice Perc-Snare 3-EXs5 SD-Mad Analog 12-EXs5 SD-HipHop 03-EXs5-R Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-Dance Soft/BD-Dry 1 DJ-BD Rub BD-Dance 08/Zap 1 BD-Dance 03/Tom-VPM BD-Dance 06 (R&B)/BD-Pillow DJ-SD Rub/SD-Dry 2 Dance Claps 3/Scream SD-AmbiCrackle 2/SD-Amb.Piccolo 1 SD-Dance 2/SD-Sidestick Dry SD-Sidestick mf -L/Dance Claps 2 SD-Vintage 5/SD-Full Room BD-Dance 12/Orchestral BD BD-Dance 13/Taiko-Open Rimshot-Dance 1 Dance Claps 3/SD-Sidestick mf -L Dance Claps 1/Hand Claps 1 -L SD-Hip 4/SD-Dance 7 BD-Hip 1/Explosion Hi-Hat Dance 3 Closed / 2-Pedal Taiko-Open/Explosion Hi-Hat Dance 3 Open / 2-Closed 2 BD-Hip 1/Explosion Hi-Hat Dance 4 Open / 2-Open 2 Vocal Cowbell/Tom-Old R&B Vocal Cowbell/Tom-Old R&B Crash Cymbal 3/BD-Dry 1 Claves/Tom-Old R&B Ride Cymbal 4 Edge China Cymbal Ride Cymbal Cup 2 Dance Perc 2b/2a Tribe/Chinese Gong Tambura/Gamelan Orch Cymbal-Open/Timpani Applause/Stadium Ride Cymbal 1 -L / 4 Edge Bongo 1 Hi-Slap/Hi-Open Bongo 1 Lo-Slap/Lo-Open Conga 1 Hi-Mute/1-Toe Conga 1 Hi-MtSlap/Hi-Open Conga 1 Lo-Slap/Lo-Open Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Open Tambourine 3 Tambourine 2 Cabasa-Up/Down Maracas-Push DJ-BD Rub DJ-SD Rub DJ Vinyl Sliced 11/12 DJ Vinyl Sliced 09/10 DJ-Reverse Woodblock 1/BD-Dry 1 Woodblock 1/BD-Dry 1 Cuica 1-Hi/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open/BD-Dry 1 Shaker 1 Marc Tree/BD-Dry 1 BD-Dance 06 (R&B)/05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Tabla-Mute 4 Drumstick Hit Cymbal Reverse SD-Reverse BD-Jazz 2 BD-Dance 06 (R&B)/BD-Dry 5 BD-Pillow/DJ-Record Stop BD-Ringy/Dance Perc 2b BD-Ambi Crackle/DJ-Old Record SD-AmbiCrackle 3/DJ-Old Record Orchestral SD SD-Brush Swirl/SD-Brush Tap 1 SD-Vintage 2/SD-Brush Hit SD-Hip 3/SD-GhostSet f SD-Jazz Ring/SD-Brush Tap 2 BD-Hip 4 BD-Dance Soft SD-Sidestick mf -R SD-Brush Hit/Tom 2-Hi Dance Claps 1/Hand Claps 1 -L SD-Hip 5/SD-Sidestick Dry Tom-Old R&B Hi-Hat-Crisp Closed 1 / Hi-Hat 1-Closed mf /BD-Dry 1 Tom-Old R&B Hi-Hat-Crisp Closed 1 / Hi-Hat 1-Pedal mf Tom-Old R&B L/R Off Hi-Hat-Old Open/Hi-Hat 1-Open mf Tom-Dance Tom-Dance Crash Cymbal 1 mf -L Tom-Dance Ride Cymbal 1 -L / 4 Edge Crash-Dance 2 DDD1 / China Cymbal Ride Cymbal Cup 1 -R / Ride-Dance 1 Tambourine 2/1-Push Crash-Dance 1 Finger Snap Crash Cymbal 2 Finger Cymbal/BD-Dry 1 Ride-Dance 1 Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Agogo Bell L/R Off Agogo Bell/BD-Dry 1 Cabasa-Up/Down Maracas-Push/Pull Uhh Solo Yea Solo Guiro-Short/Tap Guiro-Long Claves Woodblock 1 Woodblock 1 Cuica 1-Hi Cuica 1-Lo/BD-Dry 1 Triangle-Mute Triangle-Open Shaker 1 Marc Tree Bell Tree Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-amb 1 mf -L Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Open/Closed Hi-Hat Dance 03-EXs5 Hi-Hat Dance 3 Closed Timbales-Paila/Shaker 1 Hi-Hat-Crisp Open/Chili/Whispy Hi-Hat-Vintage 1 Hi-Hat 2-Pedal Open/Closed 1/Pedal BD-TekClub 03-EXs5 BD-TekClub 11-EXs5 BD-TekClub 12-EXs5 BD-TekClub 06-EXs5 BD-TekClub 04-EXs5/BD-Mad Analog 14-EXs5 SD-TekClub 03-EXs5 SD-TekClub 06/07-EXs5 SD-TekClub 08-EXs5-L SD-TekClub 11-EXs5 SD-TekClub 17-EXs5 Cymbal Reverse SD-Processed BD-TekClub BD-TekClub BD-TekClub BD-TekClub 10-EXs5 BD-TekClub 08/07/06-EXs5 SD-TekClub 05/04-EXs5-L SD-TekClub 01-EXs5 SD-TekClub 12-EXs5 SD-TekClub 15/07-EXs5 Crash-Granular-EXs5-L/SD-HipHop 08-EXs5 SD-TekClub-EXs5 BD-TekClub 05-EXs5 BD-TekClub 04-EXs5 SD-Mad Analog 02-EXs5 SD-TekClub 08-EXs5-L SD-TekClub 10-EXs5-L SD-TekClub 14-EXs5 Tom-TekClub-EXs5-L Hat-TekClub 05-EXs5-L Tom-TekClub-EXs5-L Hat-TekClub 10-EXs5/01-EXs5-R Tom-TekClub-EXs5-L Hat-TekClub 11-EXs5/03-EXs5-L Tom-Granular 6-EXs5 Tom-Granular 5-EXs5 Crash HipHop 1-EXs5-L Tom-Granular 4-EXs5 Ride HipHop 2-EXs5 China Cymbal Perc-TekClub 14-EXs5 Tambourine 2/1-Push/1-Pull Crash-Granular-EXs5-R Perc-TekClub 10-EXs5 Crash HipHop 2-EXs5 HiHat Granular 06-EXs5-R Perc-TekClub 02-EXs5 Perc-TekClub 08-EXs5 Perc-TekClub 08-EXs5 Perc-Mad Analog 32-EXs5 Perc-Mad Analog 34-EXs5 Perc-Mad Analog 33-EXs5 Perc-TekClub 10-EXs5 Perc-TekClub 10-EXs5 Hi-Hat Dance 5 Open Hi-Hat Dance 5 Open Perc-Mad Analog 23-EXs5 Maracas-Pull Perc-Granular 35/34-EXs5 Voice 35 "Aow"/Voice 39 "Uuh" Hat-TekClub 10-EXs5 Hat-TekClub 11-EXs5 Perc-TekClub 01-EXs5 Perc-TekClub 06/05-EXs5 Perc-TekClub 12/13-EXs5 Perc-TekClub 04-EXs5 Perc-TekClub 17-EXs5-R/16-EXs5 Triangle-Mute Triangle-Roll Dance Perc 8 Maraca Marc Tree BD-Mad Analog 04-EXs5 HiHat Mad Analog 02-EXs5 SD-Mad Analog 10-EXs5 HiHat Mad Analog 03-EXs5 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause BD-HipHopf 03-EXs5 BD-TekClub 07-EXs5 BD-Granular01-EXs5-L/BD-Dance 01 BD-TekClub 04-EXs5 BD-Mad Analog 01-EXs5 SD-TekClub 08-EXs5-L SD-HipHop 04-EXs5 SD-Dance 12-EXs5 SD-Dance 05-EXs5 SD-Dance 02-EXs5 BD-Mad Analog 04-EXs5 BD-Dance 01 BD-Dance 11 BD-Dance 04 BD-Dance 08 BD-Squash BD-Dance 06 (R&B) SD-Dance 3 (New) SD-Dance 5 SD-Hip 4 SD-Sizzle SD-Noise/SD-Vintage 4 SD-Hip 1/SD-Chili BD-Dance 12 BD-Dance 09 Rimshot-Dance 2 SD-Dance 4 (Old) Dance Claps 1 SD-Dance 8 Tom-Dance Hi-Hat Dance 1 Closed Tom-Dance Hi-Hat Dance 1 Open Tom-Dance Hi-Hat Dance 1 Open Hi-Hat-Whispy/BD-Dry 1 Hi-Hat Dance 3 Closed Crash-Dance 3 Hi-Hat Dance 3 Open Ride-Dance 1 Crash Cymbal 3/China Cymbal Ride-Dance 2 DDD1 Tambourine 3/2 Splash Cymbal Dance Perc 7 Cowbell Crash-Dance 1 Zap 3 Cymbal Reverse Zap 1 Zap 2 Dance Perc 6 Clave/BD-Dance 05 (Rap) Dance Perc 5 Conga Dance Perc 5 Conga Metal Hit/DJ-Reverse Hi-Hat-Chili/BD-Dry 1 Industry Loop 2/DJ-Old Record Hi-Hat 3-Pedal/DJ-Record Stop Dance Perc 4 House/DJ-SD Rub Dance Perc 8 Maraca/SD-Noise Vocal SD Car Crash/BD-Woofer Triangle-Mute/BD-Dance 05 (Rap) Noise-White/SD-Amb.Piccolo 1 Triangle-Open/Tom-Brush Hi E.Guitar Cut Noise 1/SD-Paper Hi-Hat-Hip 2/Vocal SD Hi-Hat-Alpo Closed String Slap/Tom-Brush Hi Hi-Hat-Amb Crackle/Ride-Dance 1 Dist.Slide 1/Tom-Jazz Hi Rim Hi-Hat-Crisp Open/Ride-Dance 1 Swish Terra Swish Terra/BD-Jazz 2 Crash Cymbal 2 China Cymbal Ride Cymbal 4 Edge Conga 1 Hi-Slap 1/Lo-Open Conga 1 Hi-Open/Lo-MuteSlap Ride Cymbal Cup 2 Conga 1 Hi-Slap 2/Hi-Mute Cowbell 1 Chacha Bell Cowbell 1 Guiro-Short/BD-Dry 1 Guiro-Tap Metal Hit/Finger Cymbal Dance Perc 1 Hip/BD-Dry 1 Swish Terra Cyber Drum Tambourine 1-Push Air Vortex Tubular Telephone Ring Footstep 2 Triangle-Mute Tribe Dance Perc 1 Hip/BD-Dry 1 BD-Mad Analog 10-EXs5 BD-HipHop 08b-EXs5 BD-Granular 02-EXs5-R/L BD-TekClub 02-EXs5 BD-Granular 20-EXs5-L/R SD-Dance 09-EXs5 SD-dry 1 p -R SD-Granular 20-EXs5-L SD-TekClub 13-EXs5 SD-Dance 02-EXs5 BD-Dance 10 BD-Dance 01 BD-Dance 03 BD-Dance 01 BD-Squash BD-Dance 13/Hi-Hat 3-Closed 1 BD-Dance 01/Zap 1 SD-Dance 3 (New) SD-Dance 5 SD-Dance 2 IFX1 Dance Claps 3 SD-Dance 2/Hand Claps 2 SD-Dance 8 BD-Dance 10/Hi-Hat Dance 3 Closed BD-Dance 11/Zap 1 Rimshot-Dance 1/2 SD-Dance 7 Dance Claps 2 SD-Dance 6 Tom-Dance/Tom 4-Floor Hi-Hat Dance 1 Closed Tom-Dance/Tom 4-Lo Hi-Hat Dance 2 Closed Tom-Dance/Tom 4-Hi Hi-Hat Dance 2 Open Dance Perc 2b/2a Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Open Hi-Hat Dance 3 Open Ride-Dance 1 Crash-Dance 1 Ride-Dance 2 DDD1 Tambourine 3/2 Splash Cymbal Dance Perc 7 Cowbell Crash-Dance 1 PC Vox-One Cymbal Reverse Zap 1 Zap 2 Dance Perc 6 Clave/BD-Dance 05 (Rap) Dance Perc 5 Conga Dance Perc 5 Conga Tabla-Na/DJ-Reverse Hi-Hat Dance 3 Closed/BD-Dry 1 Baya-Mute 2/DJ-Old Record Hi-Hat Dance 3 Open/DJ-Record Stop Baya-Open/DJ-SD Rub Dance Perc 8 Maraca/SD-Noise CompuVoice-Beep/Vocal SD Dance Claps 3/BD-Woofer Triangle-Mute/BD-Dance 05 (Rap) Hand Claps 1 -L/SD-Amb.Piccolo 1 Triangle-Open/Tom-Brush Hi Finger Snap/SD-Paper Hi-Hat-Old Closed 3/Vocal SD Hi-Hat-Alpo Closed E.Guitar Pick 1/Tom-Brush Hi Hi-Hat-Amb Crackle/Ride-Dance 1 Dist.Slide 1/Tom-Jazz Hi Rim Hi-Hat-Crisp Open/Ride-Dance 1 Dist.Slide 2/Swish Terra Guitar Scratch/BD-Jazz 2 Crash Cymbal 2/Crash Cymbal 2 China Cymbal Ride Cymbal 4 Edge Conga 1 Hi-Slap 1/Bongo 1 Lo-Open Conga 1 Hi-Open/Lo-MuteSlap Ride Cymbal Cup 2 Conga 1 Hi-Slap 2/Hi-Mute Cowbell 1 Chacha Bell Cowbell 1 Guiro-Short/BD-Dry 1 Guiro-Tap Metal Hit/Finger Cymbal Dance Perc 1 Hip/BD-Dry 1 Swish Terra Cyber Drum Tambourine 1-Push Air Vortex Tubular Telephone Ring Footstep 2 Triangle-Mute Tribe Dance Perc 1 Hip/BD-Dry 1 BD-Mad Analog 04-EXs5 BD-Dance 06-EXs5 BD-Mad Analog 05-EXs5 BD-HipHop 03-EXs5 BD-Dance 14-EXs5 SD-Mad Analog 13-EXs5 SD-TekClub 04-EXs5 SD-TekClub 13-EXs5 SD-Dance 12-EXs5/DJ-SD Rub SD-Dance 03-EXs5 BD-TekClub 01-EXs5 BD-Dance 01-EXs5/BD-Mad Analog 13-EXs5 BD-Dance 13 BD-Dance 04/Dance Perc 4 House BD-Dance 08/BD-Jungle BD-Mad Analog 06-EXs5/07-EXs5/08-EXs5 HiHat Mad Analog 16-EXs5/BD-Dance 06 (R&B) SD-Dance 5 SD-Dance 3 (New) SD-Hip 4 SD-Rap 2/Dance Claps 1 SD-Noise/Dance Claps 3/SD-Noise SD-Dance 2/7 BD-Dance 06 (R&B)/BD-amb 1 f -R BD-Dance 13 Rimshot-Dance 1 SD-Dance 2/Dance Claps 2 Dance Claps 1/Dance Claps 3 SD-Dance 5 Tom-Dance Hi-Hat/Dance Perc 2a Tom-Dance Hi-Hat Dance 3 Open/Dance Perc 2b Tom-Dance Hi-Hat Dance 3/4 Open Hi-Hat Dance 1 Closed Dance Perc 8 Maraca Crash-Dance 3 Hi-Hat Dance 2 Open Ride-Dance 1 Crash-Dance 1/China Cymbal Ride-Dance 2 DDD1 Dance Perc 2b/2a Industry Loop 2 HiHat Mad Analog 09-EXs5 Crash-Dance 1 568:HiHat Mad Analog 10-EXs5 Perc-Mad Analog 07-EXs5 Zap 1 Zap 2 Dance Perc 6 Clave Dance Perc 5 Conga Dance Perc 5 Conga Noise-VPM Mod/DJ-Reverse Hi-Hat-Chili Noise-White Hi-Hat 3-Pedal/DJ-Record Stop Dance Perc 3 House Hi-Hat 2-Open 2 Vocal Cowbell GunShot Triangle-Mute Noise-VPM Mod/SD-Amb.Piccolo 1 Triangle-Open/Tom-Brush Hi HiHat Granular 19-EXs5-L/R Hi-Hat-Ambi Hi-Hat-Alpo Closed CompuVoice-When/Tom-Brush Hi Hi-Hat-Amb Crackle/Ride-Dance 1 CompuVoice-Do it/Tom-Jazz Hi Rim Hi-Hat-Crisp Open/Ride-Dance 1 CompuVoice-Ch Year solo/BD-Jazz 2 Crash Cymbal 2 China Cymbal Ride Cymbal 4 Edge Conga 1 Hi-Slap 1/Bongo 1 Lo-Open Conga 1 Hi-Open/Lo-MuteSlap Ride Cymbal Cup 2 Conga 1 Hi-Slap 2/Hi-Mute Cowbell 1 Chacha Bell Cowbell 1 Guiro-Short/BD-Dry 1 Guiro-Tap Metal Hit/Finger Cymbal Dance Perc 1 Hip/BD-Dry 1 Swish Terra Cyber Drum Tambourine 1-Push Air Vortex Tubular Telephone Ring Footstep 2 Triangle-Mute Tribe Dance Perc 1 Hip/BD-Dry 1 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Mute 4 Drumstick Hit/BD-Dry 1 Cymbal Reverse/BD-Dry 1 SD-Reverse/BD-Dry 1 BD-PhatJaz BD-Tubby/BD-Dry 4 BD-Dry 6 BD-Klanger/BD-Hip 1 BD-Pillow SD-Ambi Hit 3 SD-Hip 3 SD-Klanger/SD-Amb.Piccolo 1 SD-Ringy SD-Chili SD-Dry 2 BD-Ringy/BD-Dance 08 BD-Ambi Crackle/BD-Ambi SD-Sidestick Dry/Rimshot-Dance 2 SD-Chili Hand Claps 4 Happy/3 Hip SD-AmbiCrackle 3 Tom-Old R&B Hi-Hat-Crisp Closed 2 Tom-Old R&B Hi-Hat 2-Closed 2 Tom-Old R&B Hi-Hat-Crisp Open / 2-Open 2 Hi-Hat-Vintage 1 Hi-Hat-Amb Crackle Crash Cymbal 3/BD-Dry 1 Hi-Hat-Old Open Ride Cymbal 4 Edge Noise-VPM Mod/Cricket Spectrum Ride Cymbal Cup 2 Tambourine 2/1-Push Tambourine 3/1-Pull Hi-Hat-Hip 2/BD-Dry 1 Crash Cymbal 2/BD-Dry 1 MachineGun Ride-Dance 2 DDD1 Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Timbales Hi-Rim 1/Hi-Edge Timbales Lo-Open Dance Perc 1 Hip/BD-Dry 1 Dance Perc 1 Hip/BD-Dry 1 Cabasa-Up/Down Maracas-Push SD-GhostSet f SD-GhostSet roll Train/Chinese Gong Space Lore Noise-VPM Mod/BD-Dry 1 Air Vortex/BD-Dry 1 Cricket Spectrum Car Engine/BD-Dry 1 Door Slam/BD-Dry 1 Triangle-Mute Triangle-Open/BD-Dry 1 Shaker 1 Bell Tree/BD-Dry 1 BD-Dance 06/05 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1/BD-Dry 1 Dance Perc 8 Maraca/BD-Dry 1 Orch.SD Roll 2/BD-Dry 1 Orchestral SD Bell Tree/BD-Dry 1 Stadium/BD-Dry 1 Applause/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dance 06 (R&B)/Perc-Granular 16-EXs5-L BD-Granular 28-EXs5 BD-Dance 05 (Rap)/BD-Hip 4 BD-Mad Analog 04-EXs5 BD-Pop SD-HipHop 03-EXs5-L SD-HipHop 15d-EXs5-L SD-Granular 06-EXs5/SD-dry 1 f -R SD-Granular 14-EXs5-L/SD-HipHop 06b-EXs5 SD-Sizzle/SD-HipHop 04-EXs5 Cymbal Reverse SD-Reverse BD-Terminator/BD-Dance 13 BD-Dance 13/BD-Dry 3 BD-Ringy/BD-Dance 13 BD-HipHop 14a-EXs5 BD-Dance 13/BD-Dance 08-EXs5 SD-TekClub 05-EXs5-L/SD-Dance 2 SD-Jungle SD-Dance 15-EXs5 SD-Hip 5/SD-Sidestick Ambi SD-Jungle SD-Big Rock BD-Tight/BD-Dance 08 BD-Dance 13/BD-Terminator SD-Sidestick Dry/SD-Sizzle SD-Dance 2/Dance Claps 1 Dance Claps 2/Hand Claps 3 Hip SD-Dance 2/SD-Ambi Hit 3 Tom-Old R&B Hi-Hat-Vintage 1 Tom-Old R&B Hi-Hat-Amb Crackle Tom-Old R&B Splash Cymbal/Noise-White Hi-Hat 1-Closed mf/Noise-White Hi-Hat 1-Closed ff/Noise-White Crash-Dance 1/BD-Dry 1 Hi-Hat-Old Open Ride Cymbal 4 Edge China Cymbal/Stadium Ride Cymbal Cup 2 HiHat Granular 03-EXs5-L/R Crash-Jungle Dance Perc 2a Crash Cymbal 2/Crash-Jungle Dance Perc 2b Ride-Dance 2 DDD1 Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Hi-Hat Dance 06-EXs5 Hi-Hat Dance 10-EXs5 Hat-TekClub 12-EXs5-L Hat-TekClub 12-EXs5-R Tambourine 2 Tambourine 3 SD-GhostSet f SD-GhostSet roll Zap 2 Zap-EXs5 Noise-VPM Mod Noise-VPM Mod Tambura/Cricket Spectrum Voice 21 Marc Tree Triangle-Mute Triangle-Open Shaker 1 Bell Tree BD-Dance 06/05 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applausef Voice Perc-Kick 1-EXs5 Voice Perc-Kick 2a-EXs5 Voice Perc-Kick 2b-EXs5 BD-HipHop 14a-EXs5/BD-Jungle BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Ambi Soft/"Tuunn" BD-Mad Analog 03-EXs5 DJ Vinyl Sliced 15/Perc-Mad Analog 17-EXs5 BD-Mad Analog 08-EXs5/CompuVoice-Do it Perc-Mad Analog 25-EXs5 SD-Mad Analog 05-EXs5 Clap Mad Analog 5-EXs5/Voice Perc-Snare 2-EXs5 Perc-Granular 32-EXs5 SD-Mad Analog 07-EXs5 SD-Mad Analog 09-EXs5 BD-Granular 10-EXs5-R/L BD-Mad Analog 06/07-EXs5 BD-Dance 13/Perc-Granular 20-EXs5 BD-Dance 09/Click DJ-Old Record/BD-Dance 08 BD-Dry 5/BD-Dance 07 BD-Dance 07/Hi-Hat Dance 4 Open SD-Dry 3/Timbales Hi-Edge SD-Dance 3 (New) SD-Jungle/Hand Claps 4 Happy SD-Ambi Hit 1 Dance Claps 3/1 Dance Perc 8 Maraca/SD-Sidestick Dry Noise-White/Cyber Drum Hart Beat/Zap 2 CompuVoice-Beep Noise-White Noise-White/Applause SD-Dance 6/SD-Air Explosion Tom-Synth/Noise-White Noise-White/Click Tom-Synth/Noise-White Noise-White/Click Tom-Synth/Noise-White Noise-White/Click Tom-Synth/Noise-White Hi-Hat Dance 4 Closed Noise-VPM Mod/Noise-White Hi-Hat Dance 4 Open Ride-Dance 2 DDD1/Telephone Ring Crash-Dance 3/China Cymbal Ride-Dance 2 DDD1 Cabasa-Up/Down Splash Cymbal Shaker 2/1 Crash-Dance 2 DDD1 Dance Perc 6 Clave Cymbal Reverse Dance Perc 5 Conga/Bongo 1 Hi-Open Dance Perc 5 Conga/Bongo 1 Lo-Open Conga 1 Hi-MtSlap/Conga 1-Toe Dance Perc 5 Conga/Conga 1 Hi-Open Dance Perc 5 Conga/Conga 1 Lo-Open Tambourine 1-Push/1-Pull Tambourine 3/2 Dance Perc 7 Cowbell/BD-Dry 1 Rimshot-Dance 2 Cabasa-Up/Down Maracas-Push Dance Perc 8 Maraca/Chinese Gong SD-Metal Striker/Chinese Gong Car Engine/Guiro-Tap Industry Loop 2 Industry Loop 2 Perc-Mad Analog 19-EXs5 Tambourine 3/BD-Dry 1 Perc-Granular 39-EXs5 String Slap/BD-Dry 1 Perc-Mad Analog 06-EXs5 Zap 1/BD-Dry 1 Perc-Granular 28-EXs5-L Car Crash/BD-Dry 1 BD-Dance 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 4 (Old) Hi-Hat Dance 3 Open/BD-Dry 1 Crash-Jungle Dance Perc 6 Clave Wind/BD-Dry 1 Stream/BD-Dry 1 "Two..." Solo/"One..." Solo "Four..." Solo/"Three..." Solo Bell Tree/BD-Dry 1 Space Lore/BD-Dry 1 Hart Beat/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-amb 1 mf -L Hi-Hat Dance 4 Closed Hi-Hat Dance 4 Open/Closed Hi-Hat Dance 03-EXs5 Hi-Hat Dance 3 Closed Timbales-Paila/Shaker 1 Hi-Hat-Crisp Open/Hi-Hat-Chili/Hi-Hat-Whispy Hi-Hat-Vintage 1 Hi-Hat 2-Pedal Open/2-Closed 1/Hi-Hat 2-Pedal BD-Mad Analog 03-EXs5 BD-Mad Analog 15-EXs5 BD-Mad Analog 12-EXs5 BD-Mad Analog 13/14-EXs5 BD-Mad Analog 09-EXs5 SD-Mad Analog 01-EXs5 SD-Mad Analog 04-EXs5 SD-Mad Analog 11-EXs5 SD-Mad Analog 07-EXs5/SD-Granular 22-EXs5-L Cymbal Reverse SD-Mad Analog 01-EXs5 BD-Mad Analog 01-EXs5 BD-Mad Analog 05-EXs5 Perc-Granular 18-EXs5 BD-Mad Analog 02-EXs5/BD-TekClub 10-EXs5 BD-Mad Analog 11-EXs5/BD-Mad Analog 10-EXs5 SD-Mad Analog 14-EXs5/Perc-Granular 44-EXs5 SD-TekClub 04-EXs5 SD-Mad Analog 15-EXs5 SD-Mad Analog 12-EXs5/Hi-Hat Granular 01-EXs5 SD-Mad Analog 13-EXs5 SD-Mad Analog 02-EXs5 BD-Mad Analog 06/07/08-EXs5 BD-Mad Analog 04-EXs5 Sidestick-Mad Ana 2-EXs5/SD-Mad Analog 11-EXs5 SD-Mad Analog 10-EXs5 SD-Granular 21-EXs5-L/Clap Mad Analog 2-EXs5 SD-Mad Analog 13-EXs5/SD-Mad Analog 12-EXs5 Tom-Mad Analog 3-EXs5 HiHat Mad Analog 01-EXs5 Tom-Mad Analog 2-EXs5 HiHat Mad Analog 10/09-EXs5 Tom-Mad Analog HiHat Mad Analog 16/15/14-EXs5 Tom-Mad Analog 5b/5a-EXs5 Tom-Mad Analog 6b/6a-EXs5 Hi-Hat-Clangy Open/Industry Loop 2 Tom-Mad Analog 4b/4a-EXs5 Perc-Mad Analog 08-EXs5 Splash Cymbal Ride Cymbal Cup 1 -L Dance Perc 2a Crash-Granular-EXs5-L Perc-Granular 22-EXs5 Crash HipHop 2-EXs5 Perc-Mad Analog 03-EXs5 HiHat Granular 18-EXs5-R Perc-TekClub 08-EXs5 Perc-TekClub 08-EXs5 Perc-Mad Analog 32-EXs5/Conga 2 Hi-Closed Conga 2 Hi-OpenSlap/Perc-Mad Analog 34-EXs5 Conga 2 Lo-OpenSlap/Perc-Mad Analog 33-EXs5 Perc-Mad Analog 12-EXs5 Perc-Mad Analog 15-EXs5 Perc-Granular 23-EXs5 Perc-Granular 23-EXs5 Perc-Mad Analog 23-EXs5 Perc-Mad Analog 09-EXs5 Perc-Mad Analog 05-EXs5 Perc-Mad Analog 04-EXs5 HiHat Mad Analog 08-EXs5 HiHat Mad Analog 07-EXs5 Perc-Mad Analog 12/11-EXs5 Perc-Mad Analog 15/14/13-EXs5 Perc-Mad Analog 18/17/16-EXs5 Perc-Mad Analog 31-EXs5 Perc-Mad Analog 31-EXs5 Triangle-Mute Triangle-Open Shaker 1/2 Marc Tree BD-Dance 06 (R&B) Hi-Hat Dance 3 Closed SD-Dance 3 (New) Hi-Hat Dance 3 Open Crash-Dance 3 Dance Perc 7 Cowbel Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause BD-amb 1 mf -L L/R Off Hi-Hat Dance 4 Closed IFX4 Off 534:Hi-Hat Dance 4 Open/Closed Hi-Hat Dance 03-EXs5f Hi-Hat Dance 3 Closedf Timbales-Paila/Shaker 1 Hi-Hat-Crisp Open/Chili/Whispy Hi-Hat-Vintage 1 Hi-Hat 2-Pedal Open/Closed 1/Pedal BD-Granular 06-EXs5 BD-Granular 13-EXs5 BD-Granular 11-EXs5-L BD-Granular 17-EXs5 BD-Granular 20-EXs5-L SD-Granular 01-EXs5 SD-Granular 02-EXs5 SD-Granular 20-EXs5-L SD-Granular 19-EXs5-L SD-Granular 16-EXs5-L Cymbal Reverse L/R Off SD-Processed BD-Granular 10-EXs5-L BD-Granular 21/23-EXs5 BD-Granular 09/08-EXs5 BD-Granular 28-EXs5 BD-Granular 27/26-EXs5-L SD-Granular 26-EXs5 SD-Granular 08-EXs5-L/R SD-Granular 03-EXs5 SD-Granular 24/23-EXs5-L SD-Granular 18-EXs5-L SD-Granular 15-EXs5-L BD-Granular 22-EXs5-L BD-Granular 18-EXs5-L Perc-Granular 23-EXs5/Sidestick-Mad Ana 1-EXs5 SD-Granular 07-EXs5-L SD-Granular 14-EXs5-L SD-Granular 05/04-EXs5-L Tom-Granular 2-EXs5-L HiHat Granular 21/20-EXs5 Tom-Granular 3-EXs5-L HiHat Granular 18/17-EXs5-L Tom-Granular 1-EXs5-L HiHat Granular 18-EXs5-L Tom-Granular 6-EXs5 Tom-Granular 5-EXs5 Crash-Granular-EXs5-L Tom-Granular 4-EXs5 HiHat Granular 19/18-EXs5-L Splash Cymbal Ride Cymbal Perc-Granular 36/37/38-EXs5 Perc-Granular 12-EXs5 Perc-Granular 04-EXs5 Crash HipHop 2-EXs5 Perc-Granular 32-EXs5 Perc-TekClub 09-EXs5-L Perc-Granular 07-EXs5 Perc-Granular 08-EXs5 Perc-Granular 05-EXs5 Tsuzumi Tsuzumi Perc-Granular 29/04-EXs5 Perc-Granular 29/04-EXs5 Perc-Granular 06-EXs5 Perc-Granular 06-EXs5 Dance Perc 8 Maraca Shaker 1/2 Perc-Granular 03-EXs5 Perc-Granular 03-EXs5 SD-Granular 16-EXs5-L SD-Granular 17-EXs5-L Perc-Granular 20/19-EXs5 Perc-Granular 33-EXs5 Perc-Granular 45-EXs5 Perc-Granular 12-EXs5 Perc-Granular 21-EXs5 Triangle-Mute Triangle-Roll Perc-Granular 26-EXs5 Marc Tree BD-Granular 07-EXs5 HiHat Granular 21-EXs5 SD-Granular 13-EXs5 HiHat Granular 22-EXs5 Crash-Dance 3 Dance Perc 7 Cowbell Dance Claps 1 Dance Perc 8 Maraca Orch.SD Roll 2 Orchestral SD Bell Tree Stadium Applause BD-Dry 1 BD-HipHop 07-EXs5 BD-TekClub 06-EXs5 BD-Dance 10 BD-Dance 05 (Rap) BD-Dance 13-EXs5 SD-HipHop 09-EXs5-L SD-Mad Analog 01-EXs5 SD-HipHop 06b-EXs5 SD-Mad Analog 13-EXs5 SD-Dance 04-EXs5 Cymbal Reverse SD-Reverse BD-Dance 06 (R&B) BD-Dark/BD-Dance 13 BD-Hip 1/2 BD-Dance 13 BD-Dance Soft/08 SD-AmbiHop SD-Ambi Hit 1/SD-Classic Room SD-Brasser/SD-Amb.Piccolo 1 SD-Ambi Pop/SD-AmbiCrackle 1 SD-Dry Present/SD-Big Rock SD-Vintage 6 BD-Dry 6/BD-Dark BD-Pop/BD-Dance 08 SD-Sidestick Ambi/Dry SD-Vintage 1 Hand Claps 3 Hip/4 Happy SD-Hip 6/SD-Dry 4 Tom-Dance Hi-Hat-Tight 1/2 Tom-Dance Hi-Hat-Old Closed 2/Hi-Hat-Hip 1 Tom-Dance Hi-Hat-Clangy Open/Hi-Hat-Crisp Open Tom-Old R&B Tom-Old R&B Noise-VPM Mod/BD-Dry 1 Tom-Old R&B Ride Cymbal 5 Edge Crash-Dance 3/China Cymbal Ride Cymbal Cup 2 Tambourine 2/1-Push Explosion/BD-Dry 1 Cowbell 1/BD-Dry 1 Crash-Dance 2 DDD1/Splash Cymbal Church Bell/BD-Dry 1 Crickets 2 Bongo 1 Hi-Open Bongo 1 Lo-Open Conga 1 Hi-MtSlap/1-Toe Conga 1 Hi-Open Conga 1 Lo-Open Hat-TekClub 02-EXs5-R/L Hi-Hat Dance 1 Open/Closed Tambourine 3-EXs5 Tambourine 1-EXs5 Cabasa-Up/Down Maracas-Push Maraca/Chinese Gong SD-Metal Striker/Chinese Gong Car Engine/Guiro-Tap Industry Loop 2/Guiro-Tap Industry Loop 2/BD-Dry 1 Car Stop/BD-Dry 1 Tambourine 3/BD-Dry 1 Industry Loop 2/BD-Dry 1 String Slap/BD-Dry 1 Dance Perc 1 Hip/Triangle-Mute Zap 1/BD-Dry 1 Bubble/Shaker 1 Car Crash/BD-Dry 1 BD-Dance 05 (Rap) Hi-Hat Dance 3 Closed SD-Dance 4 (Old) Hi-Hat Dance 3 Open/BD-Dry 1 Chinese Gong/Crash-Dance 3 Dance Perc 6 Clave SD-Dance 01-EXs5 Stream/BD-Dry 1 "Two..." Solo/"One..." Solo "Four..." Solo/"Three..." Solo Bell Tree/BD-Dry 1 Space Lore/BD-Dry 1 Hart Beat/BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Granular 11-EXs5-R BD-HipHop 10-EXs5 BD-Granular 25-EXs5 BD-Granular 26-EXs5-L BD-Granular 28-EXs5 SD-Sizzle SD-Granular 06-EXs5 SD-Rap 2 SD-HipHop 03-EXs5-L SD-Granular 07-EXs5-L Cymbal Reverse SD-Reverse BD-Squash BD-Jazz 2 Zap 1 Footstep 1/2 Cyber Drum Tabla-Na/Open Orchestral SD SD-Rap 2 SD-Dance 1 SD-Amb.Piccolo 2/SD-Off Center SD-Atomic Djembe-Open BD-Dance 02 Timbales Hi-Rim 1 SD-Jazz Ring Zap 3 SD-Dance 4 (Old) Tom 3-Lo Hi-Hat Dance 1 Closed Tom-Brush Floor Hi-Hat Dance 3 Open Tom-Synth Triangle-Open/Hi-Hat Dance 1 Open Tom-Synth Tom-Jazz Hi Orch Cymbal-Open Tom 4-Lo/BD-Dry 1 Ride Cymbal-Jazz Air Vortex Ride Cymbal Cup 1 -R Tambourine 3/2 Splash Cymbal Dist.Slide 1/2 Ride Cymbal 2 -L Bird 1 Sleigh Bell Bongo 2 Hi-Open f/mf Bongo 2 Lo-Muffled f/mp Conga 2 Hi-ClosedSlap/Hi-Closed Conga 2 Hi-OpenSlap/Hi-Open mf Conga 2 Lo-OpenSlap/Lo-Open mf Perc-Mad Analog 35-EXs5 Perc-Mad Analog 36-EXs5 Perc-Granular 39/38-EXs5 Tambourine 2 / 1-Pull Shaker 3/2-EXs5 Dance Perc 8 Maraca Finger Cymbal Guiro-Long Vocal SD Taiko-Open/Gamelan Perc-Granular 28-EXs5-L Telephone Ring Footstep 1/2 Zap 1 Cricket Spectrum Noise-VPM Mod Noise-White Noise-VPM Mod Agogo Bell GunShot Finger Cymbal/Marc Tree Samba Whistle 1 HiHat Granular 18-EXs5-R/L DJ-BD Rub Industry Dist.Slide 2/1 Harp-Glissando/Rainstick Castanet-Double 1/Vibraslap DJ-Record Stop/DJ-Reverse DJ-Scratch 2/1 Tribe Dance Perc 7 Cowbell BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-amb 1 mf -L BD-Dance 10 DJ Vinyl Sliced 13 BD-Dance 07 BD-Dance 08 SD-HipHop 04-EXs5 SD-Vintage 2 Dance Perc 3 House/BD-Dry 5 Voice 20 SD-Jazz Ring SD-Full Room Cymbal Reverse SD-Whopper BD-Hip 2 BD-Klanger BD-PhatJaz BD-Dance 09 Zap 1 SD-Brasser DJ Vinyl Sliced 16 SD-Ambi Hit 1 SD-AmbiHop SD-Rap 2 SD-Dance 1 Voice Perc-Kick 1/3-EXs5 Voice Perc-Kick 2a/2b-EXs5 Voice Perc-Snare 4-EXs5 Voice Perc-Snare 1-EXs5 Voice Perc-Snare 3-EXs5 Voice Perc-Snare 2-EXs5 Voice Perc-Tom-Lo-EXs5 VoicePerc-HH-Close1-EXs5 Voice Perc-Tom-Lo-EXs5 VoicePerc-HH-Close2-EXs5 Voice Perc-Tom-Mid-EXs5 Voice Perc-Cymbal-EXs5 Voice Perc-Tom-Mid-EXs5 Voice Perc-Tom-Hi-EXs5 Voice Perc-Cymbal-EXs5 Voice Perc-Tom-Hi-EXs5 Ride HipHop 2-EXs5 Car Stop IFX5 Off Voice Perc-"Pa"-EXs5 Industry Loop 1 Perc-Granular 32-EXs5 Voice Perc-Cowbell-EXs5 Voice Perc-Cymbal-EXs5 Voice Perc-Shaker-EXs5 Perc-Granular 27-EXs5 Zap 2 DJ Vinyl Sliced 18 DJ Vinyl Sliced 17 DJ-Vinyl Sliced 08-EXs5 DJ Vinyl Sliced 05 Voice 23 DJ-Reverse DJ Vinyl Sliced 03 Voice 21 DJ-Scratch 3b Voice 26 DJ-Hit Rub Voice 09 DJ Vinyl Sliced 13 DJ-Scratch 1-EXs5 DJ-Scratch 2-EXs5 DJ-Record Stop-EXs5 DJ Vinyl Sliced 08 DJ Vinyl Sliced 04 DJ Vinyl Sliced 05 Triangle-Mute Triangle-Open DJ-Vocal Rub 1 DJ Vinyl Sliced 02 DJ Vinyl Sliced 06 DJ Vinyl Sliced 07 DJ-Scratch 2-EXs5 DJ Vinyl Sliced 09 DJ Vinyl Sliced 10 DJ-Vocal Rub 2 DJ Vinyl Sliced 11 DJ Vinyl Sliced 12 DJ-Vocal Rub 2 Voice 07 PC Vox-Cks DJ-Scratch 1 DJ Vinyl Sliced 18 Taiko-Rim/Open Tsuzumi Djembe-Open Djembe-Slap/Mute Baya-Ghe/Open Baya-Mute 1/3 Baya-Mute 5/4 Tabla-Open Tabla-Tin/Tabla-Mute 4 Tabla-Mute 2/3 Tabla-Na/Tabla-Mute 1 Tabla-Mute 1/2 Orchestral BD/BD-Dry 1 Tribe/BD-Dry 1 Finger Snap/BD-Dry 1 Hand Claps 2/BD-Dry 1 Triangle-Mute/BD-Dry 1 Triangle-Open/BD-Dry 1 Castanet-Double 2/BD-Dry 1 Castanet-Single/BD-Dry 1 Castanet-Double 1/BD-Dry 1 Guiro-Long/BD-Dry 1 Guiro-Short Guiro-Tap/BD-Dry 1 Vibraslap/BD-Dry 1 Claves/BD-Dry 1 Dance Perc 6 Clave/BD-Dry 1 Cuica 1-Lo/BD-Dry 1 Cuica 1-Hi/BD-Dry 1 Timbales Lo-Open/BD-Dry 1 Woodblock 1/BD-Dry 1 Timbales Lo-Rim/BD-Dry 1 Woodblock 1/BD-Dry 1 Timbales Lo-Mute/BD-Dry 1 Vocal Woodblock/BD-Dry 1 Timbales Hi-Edge/BD-Dry 1 Timbales Hi-Rim 2/BD-Dry 1 Chacha Bell/BD-Dry 1 Timbales Hi-Rim 1/BD-Dry 1 Mambo Bell/BD-Dry 1 Timbales-Paila/BD-Dry 1 Bongo 1 Lo-Open/BD-Dry 1 Cowbell 1/BD-Dry 1 Bongo 1 Lo-Slap/BD-Dry 1 Cowbell 2/BD-Dry 1 Bongo 1 Hi-Open/BD-Dry 1 Vocal Cowbell/BD-Dry 1 Bongo 1 Hi-Slap Conga 1 Lo-Open Conga 1 Lo-Slap/BD-Dry 1 Conga 1 Lo-MuteSlap/BD-Dry 1 Conga 1 Hi-MtSlap/BD-Dry 1 Conga 1 Hi-Open Conga 1 Hi-Mute/BD-Dry 1 Conga 1-Heel/BD-Dry 1 Conga 1 Hi-Slap 1/BD-Dry 1 Conga 1-Toe/BD-Dry 1 Conga 1 Hi-Slap 2/BD-Dry 1 Agogo Bell/BD-Dry 1 Agogo Bell/BD-Dry 1 Maracas-Pull/Push Shaker 1 Shaker 2 Cabasa-Tap/Down Cabasa-Tap/Up Caxixi-Hard/Caxixi-Soft Tambourine 1-Push/BD-Dry 1 Tambourine 2 Tambourine 1-Pull/BD-Dry 1 Tambourine 3 Sleigh Bell Samba Whistle 1/BD-Dry 1 Samba Whistle 1/BD-Dry 1 Bell Tree/BD-Dry 1 Samba Whistle 1/BD-Dry 1 Marc Tree/BD-Dry 1 Samba Whistle 1/BD-Dry 1 Rap Sleigh bell Rainstick/BD-Dry 1 Chinese Gong/BD-Dry 1 Bird 1/BD-Dry 1 Bird 2/BD-Dry 1 Bird 1/BD-Dry 1 Cricket Spectrum/BD-Dry 1 Marc Tree/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 Click/BD-Dry 1 BD-Dry 1 Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestra Hit Orchestral BD Orch.SD Roll 1 Orchestral SD Orch.SD Roll 2 Orchestral SD Orch Cymbal-Open Orch Cymbal-Closed Taiko-Open Orch.SD Roll 1 Orchestral SD Orch.SD Roll 2 Orchestral SD Triangle-Mute Triangle-Roll Triangle-Open Chinese Gong Tambourine 3 Woodblock 1 Castanet-Double 1 Woodblock 1 Castanet-Double 2 Woodblock 1 Castanet-Single Woodblock 1 Woodblock 1 Timpani Timpani Timpani Timpani Djembe-Open Djembe-Mute Djembe-Slap Caxixi-Hard/Soft Sleigh Bell Cabasa-Up/Down Finger Cymbal Baya-Ghe Baya-Open Baya-Mute 1 Baya-Mute 2 Baya-Mute 3 Baya-Mute 4 Baya-Mute 5 Tabla-Na Tabla-Open Tabla-Tin Tabla-Mute 1 Tabla-Mute 2 Tabla-Mute 3 Tabla-Mute 4 Taiko-Open Tsuzumi/Vibraslap Taiko-Rim Conga 1 Lo-Open/BD-Dry 1 Conga 1 Lo-MuteSlap / Hi-Slap 2 Conga 1 Hi-Mute / Bongo 1 Hi-Slap Conga 1 Hi-Slap 1 / BD-Dry 1 Cabasa-Up Rainstick Harp-Glissando Rainstick BD-Dry 1 BD-Dry 1 Gamelan Gamelan Bird 1 Cricket Spectrum/BD-Dry 1 Tribe BD-Dry 1 Gamelan Gamelan/Tambura Gamelan Gamelan BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-amb 1 mf -L BD-amb 1 f -L BD-amb 2 mf -L BD-amb 2 f -L BD-Dry 1 BD-Dry 2 BD-Dry 3 BD-Dry 4 BD-Dry 5 BD-Soft BD-Soft Room BD-Jazz 1 BD-Jazz 2 BD-Pillow BD-Woofer BD-Mondo Kill BD-Terminator BD-Tubby BD-Gated BD-Tight BD-Squash BD-Dance 03 BD-Dark BD-Ringy BD-Dance Soft BD-PhatJaz Orchestral BD SD-Dry 2 SD-Dry 3 SD-Dry 4 SD-Normal SD-GhostSet roll SD-GhostSet p2 SD-GhostSet p1 SD-GhostSet f SD-Full Room SD-Off Center SD-Jazz Ring SD-Amb.Piccolo 1 SD-Amb.Piccolo 2 SD-Paper SD-Classic Room SD-Brush Hit SD-Brush Tap 1 SD-Brush Tap 2 SD-Brush Swirl SD-Atomic SD-Big Rock SD-Yowie SD-Processed 1 SD-Processed 2 SD-Cracker Room Orch.SD Roll 1 SD-Sidestick Dry Orch.SD Roll 2 Crash Cymbal 2 Orchestral SD SD-Reverse Hi-Hat 2-Closed 1 SD-Hip 5 Hi-Hat 2-Pedal SD-AmbiHop Hi-Hat 2-Open 1 SD-Whopper SD-Ambi Pop SD-dry 1 p -L SD-dry 1 mp -L SD-dry 1 mf -L SD-dry 1 f -L SD-dry 1 ff -L SD-dry 1 ff -L SD-dry 1 fff -L BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 6 BD-Dance 01 BD-Dance 02 BD-Dance 11 BD-Dance 04 BD-Hip 1 BD-Hip 2 BD-Hip 3 BD-Hip 4 BD-Pop BD-Dance 08 BD-Dance 14 BD-Ambi BD-Ambi Crackle BD-Ambi Soft BD-AmbiRocker BD-Chili BD-Dance 13 BD-Dance 07 BD-Cannon BD-Klanger BD-Dance 05 (Rap) BD-Dance 06 (R&B) BD-Dance 09 BD-Dance 10 BD-Dance 12 SD-Ambi House SD-Dance 1 SD-Rap 1 SD-Rap 2 SD-Noise SD-Sizzle SD-Hip 1 SD-Hip 2 SD-Hip 3 SD-Hip 4 SD-Hip 6 SD-Ringy SD-Tiny SD-Vintage 1 SD-Vintage 2 SD-Vintage 3 SD-Vintage 4 SD-Vintage 5 SD-Vintage 6 SD-AmbiCrackle 1 SD-AmbiCrackle 2 SD-AmbiCrackle 3 SD-Brasser SD-Chili SD-Klanger SD-Dry Present SD-Ambi Hit 1 SD-Ambi Hit 2 Hi-Hat Dance 3 Closed SD-Ambi Hit 3 Hi-Hat Dance 1 Closed SD-Dance 3 (New) Hi-Hat Dance 1 Open SD-Dance 4 (Old) SD-Dance 5 SD-Dance 8 Vocal SD BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Maracas-Push Maracas-Pull Caxixi-Hard Dance Perc 1 Hip Tambourine 1-Push Tambourine 1-Pull Tambourine 2 Tambourine 3 BD-Hip 3 Hi-Hat 3-Closed 1 SD-Chili Hi-Hat 3-Closed 2 Hi-Hat 2-Closed 1 Hi-Hat 2-Closed 2 Hi-Hat 3-Open 2 Hi-Hat 2-Open 2 Hi-Hat 3-Open 1 Hi-Hat 2-Open 1 Hi-Hat 3-Sizzle Hi-Hat 2-Sizzle Hi-Hat 2-Pedal Hi-Hat 3-Pedal Hi-Hat 2-Pedal Open Hi-Hat 3-Pedal Open Hi-Hat-Chili Hi-Hat-Crisp Closed 1 Hi-Hat-Vintage 1 Hi-Hat-Crisp Closed 2 Hi-Hat-Vintage 2 Hi-Hat-Crisp Open Hi-Hat-Tight 1 Hi-Hat-Tight 2 Hi-Hat-Old Open Hi-Hat-Old Closed 1 Hi-Hat-Clangy Open Hi-Hat-Old Closed 2 Hi-Hat-Old Closed 3 Hi-Hat-Alpo Closed Hi-Hat-Amb Crackle Hi-Hat-Whispy Hi-Hat-Ambi Hi-Hat-Hip 2 Hi-Hat-Hip 1 Hi-Hat Dance 1 Closed Sleigh Bell Hi-Hat Dance 1 Open Rap Sleigh bell Hi-Hat Dance 3 Closed Hi-Hat Dance 3 Open Crash Cymbal 4 Crash-Dance 3 Crash Cymbal 2 China Cymbal Crash Cymbal 3 Splash Cymbal Ride-Dance 1 Crash-Dance 1 Ride-Dance 2 DDD1 Crash-Dance 2 DDD1 Ride Cymbal 4 Edge Ride Cymbal 5 Edge Ride Cymbal-Jazz Ride Cymbal Cup 2 Cymbal Reverse Splash Cymbal Crash-Dance 1 Ride-Dance 1 Ride Cymbal Cup 2 Hi-Hat Dance 1 Open Hi-Hat-Old Closed 2 Hi-Hat-Old Closed 3 Hi-Hat-Crisp Closed 2 Hi-Hat Dance 1 Closed Hi-Hat 3-Closed 1 Hi-Hat Dance 3 Closed Click Click Click Click Click Click Click Click Click Click Click Click Click Click Click Click Click BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Tight BD-Dry 3 SD-Sidestick Ambi SD-Full Room Dance Claps 1 SD-Full Room Tom 3-Floor Hi-Hat 2-Open 2 Tom 3-Floor Hi-Hat 3-Pedal Tom 3-Lo Hi-Hat 3-Open 2 Tom 3-Lo Tom 3-Hi Crash Cymbal 3 Tom 3-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Dry 2 BD-Dry 3 SD-Sidestick Ambi SD-Yowie Dance Claps 1 SD-Yowie Tom 4-Floor Hi-Hat 2-Open 2 Tom 4-Floor Hi-Hat 3-Pedal Tom 4-Lo Hi-Hat 3-Open 2 Tom 4-Lo Tom 4-Hi Crash Cymbal 3 Tom 4-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Gated BD-Mondo Kill SD-Sidestick Ambi SD-Big Rock Dance Claps 1 SD-Big Rock Tom 4-Floor Hi-Hat 2-Open 2 Tom 4-Floor Hi-Hat 3-Pedal Tom 4-Lo Hi-Hat 3-Open 2 Tom 4-Lo Tom 4-Hi Crash Cymbal 3 Tom 4-Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Hip 1 BD-Dance 14 SD-Sidestick Ambi Tom-Synth Dance Claps 1 SD-Ambi Hit 1 Tom-Synth Hi-Hat 3-Sizzle Tom-Synth Hi-Hat 3-Pedal Tom-Synth Hi-Hat 3-Sizzle Tom-Synth Tom-Synth Crash Cymbal 3 Tom-Synth Ride Cymbal 4 Edge Crash Cymbal 2 Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Dance 06 (R&B) BD-Dance 09 Rimshot-Dance 2 SD-Dance 3 (New) Dance Claps 1 SD-Dance 4 (Old) Tom-Dance Hi-Hat Dance 3 Open Tom-Dance Hi-Hat Dance 3 Open Tom-Dance Hi-Hat Dance 3 Open Tom-Dance Tom-Dance Ride-Dance 1 Tom-Dance Crash-Dance 3 China Cymbal Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Dry 4 BD-Jazz 2 SD-Sidestick Ambi SD-Paper Dance Claps 1 SD-Jazz Ring Tom-Jazz Floor Hi-Hat 2-Closed 1 Tom-Jazz Floor Hi-Hat 2-Pedal Tom-Jazz Hi Hi-Hat 2-Open 1 Tom-Jazz Hi Tom-Jazz Hi Crash Cymbal 2 Tom-Jazz Hi Ride Cymbal 4 Edge China Cymbal Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Dry 4 L/R Off BD-Jazz 2 L/R Off SD-Sidestick Ambi L/R Off SD-Brush Tap 1 L/R Off SD-Brush Hit L/R Off SD-Brush Swirl L/R Off Tom-Jazz Floor L/R Off Hi-Hat 2-Closed 1 L/R 1 Tom-Jazz Floor L/R Off Hi-Hat 2-Pedal L/R 1 Tom-Jazz Hi L/R Off Hi-Hat 2-Open 1 L/R 1 Tom-Jazz Hi L/R Off Tom-Jazz Hi L/R Off Crash Cymbal 2 L/R Off Tom-Jazz Hi L/R Off Ride Cymbal 4 Edge L/R Off China Cymbal L/R Off Ride Cymbal-Jazz Baya-Open China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Ride-Dance 2 DDD1 Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Hi-Hat 2-Closed 1 Hi-Hat 2-Pedal Hi-Hat 2-Open 1 Ride Cymbal 4 Edge Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Soft Room Orchestral BD SD-Sidestick Ambi Orchestral SD Conga 2 Hi-Open mf Orchestral SD Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Timpani Baya-Openf China Cymbal Timbales-Paila Crash Cymbal 3 Bongo 2 Hi-Fingernail Orch Cymbal-Open Conga 1 Hi-Mute Conga 2 Lo-Toe Conga 2 Lo-OpenSlapFlam Conga 2 Lo-Open f Conga 1 Lo-Open Guiro-Long Djembe-Mute Timbales Hi-Rim 1 Timbales Hi-Rim 1 Woodblock 2 Conga 2 Hi-Heel Triangle-Mute-EXs5 Triangle-Mute-EXs5 Conga 2 Hi-Closed Conga 2 Hi-Muffled Bongo 2 Hi-Cuptone Bongo 2 Hi-Slap Bongo 2 Hi-Slap Tabla-Mute 1 Tabla-Mute 2 Tabla-Na Baya-Mute 3 Timbales Lo-Open Tambourine 2 Tambourine 2-EXs5 Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 BD-Dry 1 Orchestral SD Orch.SD Roll 2 Finger Snap Perc-Granular 17-EXs5-L Dog Perc-Granular 22-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Perc-Granular 16-EXs5R "Cap" Baya-Mute 3 BD-Tight BD-Dry 3 SD-Sidestick Ambi SD-Full Room Perc-Granular 17-EXs5-L CompuVoice-Toi Perc-Granular 21-EXs5 Perc-Granular 22-EXs5 Drumstick Hit Finger Snap "Cap" Crickets 1 Door Slam Rainstick Door Creak Tribe Finger Snap "Tehh" "Kaahh" CompuVoice-Do it CompuVoice-Test "Ti" "Pan" "Thuum" "Four..." Solo "Three..." Solo Perc-Granular 22-EXs5 Tambourine 2-EXs5 "Two..." Solo "One..." Solo PC Vox-Sis PC Vox-Dunc CompuVoice-Chi PC Vox-For Dog PC Vox-One Footstep 2 PC Vox-If PC Vox-Zee PC Vox-Ae PC Vox-Pa "Haaa" CompuVoice-Beep "Chhi" L/R Off CompuVoice-Ti PC Vox-O PC Vox-Tu Dog Stadium Applause Conga 2 Hi-Open mf Djembe-Slap Djembe-Mute "Pehh" When received this message, transmits INQUlRY MESSAGE REPLY [ F0,7E,nn,06,01,F7 ] 3rd byte nn : Channel = 0 - F : Global Channel = 7F : Any Channel F0 7E 7F 06 01 F7 Received when in Sequencer mode [ F0,7E,nn,09,01,F7 ] 3rd byte nn : Channel = 0 - F : Global Channel = 7F : Any Channel F0 7E 7F 09 01 F7 Master Volume [ F0,7F,0g,04,01,vv,mm,F7 ] 3rd byte g : Global Channel 6th byte vv : Value(LSB) 7th byte mm : Value(MSB) mm,vv = 00,00 - 7F,7F : Min - Max F0 7F 00 04 01 00 00 F7 Master Balance [ F0,7F,0g,04,02,vv,mm,F7 ] 3rd byte g : Global Channel 6th byte vv : Value(LSB) 7th byte mm : Value(MSB) mm,vv = 00,00:Left, 40,00:Center, 7F,7F:Right F0 7F 00 04 02 00 00 F7 Control Master Tune(cent) in Global [ F0,7F,0g,04,03,vv,mm,F7 ] 3rd byte g : Global Channel 6th byte vv : Value(LSB) 7th byte mm : Value(MSB) mm,vv = 20,00:-50, 40,00:+00, 60,00:+50 F0 7F 00 04 03 00 00 F7 Control Transpose (chromatic step) in Global [ F0,7F,0g,04,04,vv,mm,F7 ] 3rd byte g : Global Channel 6th byte vv : Value(LSB) 7th byte mm : Value(MSB) mm,vv = 34,00:-12, 40,00:+00, 4C,00:+12 F0 7F 00 04 04 00 00 F7 MODE REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 12 Function F7 End of Excl (Receives this message, and transmits Func=42 message) F0 42 30 00 01 15 12 F7 MODE DATA F0, 42, 3g, 00, 01, 15 Excl Header 42 Function 0000 0mmm Mode (*2) 0000 0000 reserved 0sss ssss data1 0ddd dddd data2 00 Reserved F7 End of Excl (Receives FUNC=12 message, and transmits this message & data) ss : bit 0,1 = 0 : Note Receive is EVEN, = 1 : ODD, = 2 : ALL bit 3,4 = 0 : Seq Clock is internal, = 1 : External(MIDI), = 2 : External(USB) dd : bit 0 = 0 : Programs Memory is not protected, = 1 : protected bit 1 = 0 : Combinations Memory is not protected, = 1 : protected bit 2 = 0 : Sequence Memory is not protected, = 1 : protected bit 3 = 0 : Drum Kits Memory is not protected, = 1 : protected bit 4 = 0 : Arp Patterns Memory is not protected, = 1 : protected F0 42 30 00 01 15 42 00 00 00 00 00 F7 MODE CHANGE F0, 42, 3g, 00, 01, 15 Excl Header 4E Function 0000 0mmm Mode (*2) F7 End of Excl (Receives this message & data, changes the Mode, and transmits Func=24 message) When the Mode is changed by SW, transmits this message & data. F0 42 30 00 01 15 4E 00 F7 PARAMETER CHANGE F0, 42, 3g, 00, 01, 15 Excl Header 41 Function 0000 0mmm Mode (*2) 00 Parameter ID (MSB) 0ppp pppp Parameter ID (LSB) (TABLE 1,2,5,10,11) 00 Parameter SUB ID (MSB) 0qqq qqqq Parameter SUB ID (LSB) (TABLE 1,2,5,10,11) 0000 vvvv Value (Value bit28-31) 0vvv vvvv Value (Value bit21-27) 0vvv vvvv Value (Value bit14-20) 0vvv vvvv Value (Value bit7-13) 0vvv vvvv Value (Value bit0-6) F7 End of Excl (Even if it receives this message, a reply does not return) When the Parameter No. is changed by SW, transmits this message & data. F0 42 30 00 01 15 41 00 00 00 00 00 00 00 00 00 00 F7 DRUM KIT PARAMETER CHANGE F0, 42, 3g, 00, 01, 15 Excl Header 53 Function 0sss ssss Index No. (s = 00-7F ( : C-1-G9)) 0bbb bbbb Drum Kit Bank (*3) 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) 00 Parameter No.(MSB) 0ppp pppp Parameter No.(LSB) (TABLE 6) 0000 vvvv Value (Value bit28-31) 0vvv vvvv Value (Value bit21-27) 0vvv vvvv Value (Value bit14-20) 0vvv vvvv Value (Value bit7-13) 0vvv vvvv Value (Value bit0-6) F7 End of Excl (Even if it receives this message, a reply does not return) F0 42 30 00 01 15 53 00 00 00 00 00 00 00 00 00 00 F7 ARPEGGIO PATTERN PARAMETER CHANGE F0, 42, 3g, 00, 01, 15 Excl Header 54 Function 0000 000b Arpeggio AorB b = 0 : Arpeggio A, 1 : Arpeggio B 0000 aaaa Pattern No. MSB (bit10-7) 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) 0sss ssss Step No. ss = 00-2F ( : 00-47) 0ttt tttt Tone No. tt = 00-0B ( : 00-11) 00 Parameter No. (MSB) 0ppp pppp Parameter No. (LSB) (TABLE 4) 0000 vvvv Value (Value bit28-31) 0vvv vvvv Value (Value bit21-27) 0vvv vvvv Value (Value bit14-20) 0vvv vvvv Value (Value bit7-13) 0vvv vvvv Value (Value bit0-6) F7 End of Excl (Even if it receives this message, a reply does not return) F0 42 30 00 01 15 54 00 00 00 00 00 00 00 00 00 00 00 00 F7 PROGRAM PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 00 Object(Program) 0bbb bbbb Program Bank (*3) 00 Reserved 0ppp pppp Program No. F7 End of Excl (Receives this message, and transmits Func=73/Object=00 or Func=24 message) F0 42 30 00 01 15 72 00 00 00 00 F7 COMBINATION PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 01 Object(Combination) 0bbb bbbb Combination Bank (*3) 00 Reserved 0ccc cccc Combination No. F7 End of Excl (Receives this message, and transmits Func=73/Object=01 or Func=24 message) F0 42 30 00 01 15 72 01 00 00 00 F7 SONG TIMBRE SET DATA DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 02 Object(Song Timbre Set) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) F7 End of Excl (Receives this message, and transmits Func=73/Object=02 or Func=24 message) F0 42 30 00 01 15 72 02 00 00 00 F7 GLOBAL DATA DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 03 Object(Global) 00 Reserved 00 Reserved 00 Reserved F7 End of Excl (Receives this message, and transmits Func=73/Object=03 or Func=24 message) F0 42 30 00 01 15 72 03 00 00 00 F7 DRUM KIT PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 04 Object(Drum Kit) 0bbb bbbb Drum Kit Bank (*3) 00 Reserved 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) F7 End of Excl (Receives this message, and transmits Func=73/Object=04 or Func=24 message) F0 42 30 00 01 15 72 04 00 00 00 F7 SONG CONTROL DATA DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 08 Object(Song Control) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) F7 End of Excl (Receives this message, and transmits Func=73/Object=08 or Func=24 message) F0 42 30 00 01 15 72 08 00 00 00 F7 SONG EVENT DATA DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 09 Object(Song Event) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) F7 End of Excl (Receives this message, and transmits Func=73/Object=09 or Func=24 message) F0 42 30 00 01 15 72 09 00 00 00 F7 DRUM TRACK PATTERN DATA DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 0B Object(Drum Track Pattern) 00 Reserved 0000 0kkk Pattern No. MSB 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) F7 End of Excl (Receives this message, and transmits Func=73/Object=0B&0C or Func=24 message) F0 42 30 00 01 15 72 0B 00 00 00 F7 PROGRAM BANK PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 0F Object(Program Bank) 0bbb bbbb Program Bank (*3) 00 Reserved 00 Reserved F7 End of Excl (Receives this message, and transmits Func=73/Object=0F or Func=24 message) F0 42 30 00 01 15 72 0F 00 00 00 F7 COMBINATION BANK PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 11 Object(Combination Bank) 0bbb bbbb Combination Bank (*3) 00 Reserved 00 Reserved F7 End of Excl (Receives this message, and transmits Func=73/Object=11 or Func=24 message) F0 42 30 00 01 15 72 11 00 00 00 F7 DRUM KIT BANK PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 12 Object(Drum Kit Bank) 0bbb bbbb Drum Kit Bank (*3) 00 Reserved 00 Reserved F7 End of Excl (Receives this message, and transmits Func=73/Object=12 or Func=24 message) F0 42 30 00 01 15 72 12 00 00 00 F7 ARPEGGIO PATTERN PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 16 Object(Arpeggio Pattern) 00 Reserved 0000 aaaa Pattern No. MSB (bit10-7) 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) F7 End of Excl (Receives this message, and transmits Func=73/Object=16 or Func=24 message) F0 42 30 00 01 15 72 16 00 00 00 F7 ARPEGGIO PATTERN ALL PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 72 Function 17 Object(Arpeggio Pattern All) 00 Reserved 00 Reserved 00 Reserved F7 End of Excl (Receives this message, and transmits Func=73/Object=17 or Func=24 message) F0 42 30 00 01 15 72 17 00 00 00 F7 PROGRAM PARAMETER (IN INTERNAL MEMORY) DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 00 Object(Program) 0bbb bbbb Program Bank (*3) 00 Reserved 0ppp pppp Program No. 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 1) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=00 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 00 00 00 00 00 00 F7 COMBINATION PARAMETER (IN INTERNAL MEMORY) DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 01 Object(Combination) 0bbb bbbb Combination Bank (*3) 00 Reserved 0ccc cccc Combination No. 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 2) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=01 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 01 00 00 00 00 00 F7 SONG TIMBRE SET DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 02 Object(Song Timbre Set) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 10) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=02 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 02 00 00 00 00 00 F7 GLOBAL DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 03 Object(Global) 00 Reserved 00 Reserved 00 Reserved 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 3) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=03 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 03 00 00 00 00 00 F7 DRUM KIT PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 04 Object(Drum Kit) 0bbb bbbb Drum Kit Bank (*3) 00 Reserved 000k kkkk Drum Kit No. (k = 00-0F(INT), 00-1F(USER)) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 6) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=04 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 04 00 00 00 00 00 F7 SONG CONTROL DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 08 Object(Song Control) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 11) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=08 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 08 00 00 00 00 00 F7 SONG EVENT DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 09 Object(Song Event) 00 Reserved 00 Reserved 0ccc cccc Song No. cc = 00-7F ( : 0-127) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 12) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=09 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 09 00 00 00 00 00 F7 DRUM TRACK PATTERN DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 0B Object(Drum Track Pattern) 00 Reserved 0000 0kkk Pattern No. MSB 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 7) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=0B message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 0B 00 00 00 00 00 F7 DRUM TRACK PATTERN EVENT DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 0C Object(Drum Track Pattern Event) 000n nnnn Drum Track Pattern Event Num Packet (0-26) 0000 0kkk Pattern No. MSB 0kkk kkkk Pattern No. LSB k = 0-3E7 ( : 0-999) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 8) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=0B message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 0C 00 00 00 00 00 F7 CUELIST DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 0D Object(CueList) 00 Reserved 00 Reserved 000c cccc Cuelist No. c = 0-13 ( : 0-19) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 9) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 0D 00 00 00 00 00 F7 PROGRAM BANK PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 0F Object(Program Bank) 0bbb bbbb Program Bank (*3) 00 Reserved 00 Reserved 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 1) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=0F message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 0F 00 00 00 00 00 F7 COMBINATION BANK PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 11 Object(Combination Bank) 0bbb bbbb Combination Bank (*3) 00 Reserved 00 Reserved 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 2) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=11 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 11 00 00 00 00 00 F7 DRUM KIT BANK PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 12 Object(Drum Kit Bank) 0bbb bbbb Drum Kit Bank (*3) 00 Reserved 00 Reserved 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 6) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=12 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 12 00 00 00 00 00 F7 ARPEGGIO PATTERN PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 16 Object(Arpeggio Pattern) 00 Reserved 0000 aaaa Pattern No. MSB (bit10-7) 0aaa aaaa Pattern No. LSB (bit 6-0) a = 000-403 ( : 0-1027) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 4) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=16 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 16 00 00 00 00 00 F7 ARPEGGIO PATTERN ALL PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 73 Function 17 Object(Arpeggio Pattern All) 00 Reserved 00 Reserved 00 Reserved 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 4) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=72/Object=17 message, and transmits this message & data. Transmits this message & data when DATA DUMP is executed. F0 42 30 00 01 15 73 17 00 00 00 00 00 F7 CURRENT OBJECT PARAMETER DUMP REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 74 Function 0000 oooo Object (o = 0:Program/1:Combination/2:Song Timbre Set) F7 End of Excl (Receives this message, and transmits Func=75/Object=00(/01/02) or Func=24 message) F0 42 30 00 01 15 74 00 F7 CURRENT PROGRAM PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 75 Function 00 Object(Program) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 1) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=74/Object=00 message, and transmits this message & data. F0 42 30 00 01 15 75 00 00 00 F7 CURRENT COMBINATION PARAMETER DUMP F0, 42, 3g, 00, 01, 15 Excl Header 75 Function 01 Object(Combination) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 2) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=74/Object=01 message, and transmits this message & data. F0 42 30 00 01 15 75 01 00 00 F7 CURRENT SONG TIMBRE SET DATA DUMP F0, 42, 3g, 00, 01, 15 Excl Header 75 Function 02 Object(Current Song Timbre) 0ooo oooo Object Version (*4) 0ddd dddd Data (*1, TABLE 10) : F7 End of Excl (Receives this message & data, and transmits Func=24 message) Receives Func=74/Object=02 message, and transmits this message & data. F0 42 30 00 01 15 75 02 00 00 F7 STORE OBJECT PROGRAM REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 00 Object(Program) 0bbb bbbb Bank No. (*3) F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 00 00 F7 STORE OBJECT COMBINATION REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 01 Object(Combination) 0bbb bbbb Bank No. (*3) F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 01 00 F7 STORE OBJECT CURRENT SONG TIMBRE SET REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 02 Object(Song Timbre Set) 00 Reserved F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 02 00 F7 STORE OBJECT DRUM KIT REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 04 Object(Drum Kit) 00 Reserved F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 04 00 F7 STORE OBJECT PROGRAM BANK REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 0F Object(Program Bank) 0bbb bbbb Bank No. (*3) F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 0F 00 F7 STORE OBJECT COMBINATION BANK REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 11 Object(Combination Bank) 0bbb bbbb Bank No. (*3) F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 11 00 F7 STORE OBJECT DRUM KIT BANK REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 12 Object(Drum Kit Bank) 00 Reserved F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 12 00 F7 STORE OBJECT ARPEGGIO PATTERN REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 16 Object(Arpeggio Pattern) 00 Reserved F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 16 00 F7 STORE OBJECT ARPEGGIO PATTERN ALL REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 76 Function 17 Object(Arpeggio Pattern All) 00 Reserved F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 76 17 00 F7 OBJECT WRITE REQUEST F0, 42, 3g, 00, 01, 15 Excl Header 77 Function 0000 oooo Object (o = 0:Program/1:Combination) 0bbb bbbb Bank (*3) 00 Reserved 0iii iiii Index No. (i = Program or Combination) F7 End of Excl (Receives this message, write the data and transmits Func=24 message) F0 42 30 00 01 15 77 00 00 00 00 F7 RESULT REPORT F0, 42, 3g, 00, 01, 15 Excl Header 24 Function 0ccc cccc Result Code F7 End of Excl (Transmits this message when there is a result in the MIDI IN message) cc = 0 : Result OK 1 : Received Data Length is wrong 2 : Dest Memory is protected 3 : Dest Bank/Prog/Param is not exist 4 : The mode is wrong 5 : Memory over flow 40 : Another type error F0 42 30 00 01 15 24 00 F7 WAVEFORM CHANGE F0, 42, 3g, 00, 01, 15 Excl Header 7F Function 00 Object(CHANGE) 0000 cccc MIDI Channel c = 0-15 0000 000o OSC Select o = 0 or 1 0000 0bbb Bank Select b = 0 (Mono) ~ 1 (Stereo) 0nnn nnnn Number LSB 0000 nnnn Number MSB 0000 ssss Start Offset s = 0 (Off) ~ 8 (8ch) 0000 000t OSC Switch t = 0 (Off) or 1 (On) F7 End of Excl n = 0 ~ 1076 (In case of "Bank:Mono") n = 0 ~ 6 (In case of "Bank:Stereo") F0 42 30 00 01 15 7F 00 00 00 00 00 00 00 00 F7 WAVEFORM RESET F0, 42, 3g, 00, 01, 15 Excl Header 7F Function 01 Object(RESET) 0000 cccc MIDI Channel c = 0-15 F7 End of Excl F0 42 30 00 01 15 7F 01 00 F7 muse-4.0.0/src/share/instruments/Korg Wavestation EX.idf000066400000000000000000000476211404105311000231170ustar00rootroot00000000000000 Kick AmbiKick Crack Snar Crack Snar Low Snare Snare High Sidestick Sidestick High Tenny Hit Tenny Hit High Tom Low Tom Lo-Mid Tom Mid Tom High HiHat Closed HiHat Closed Low HiHat Open HiHat Open Low Conga Low Conga Mid Conga High Claves Claves High Tick Hit Pot Hit Hammer Thong Noise Vibe Piano Hit "Tuunn" "Pehh" "Thuum" "Kaahh" "Tchh" "Pan" "Ti" "Cap" "Chhi" "Tinn" "Haaa" Crickets Noise 2 Mid Noise 2 Low Noise 2 High Noise 2 High Noise 2 High Noise 2 High Noise 2 High Noise 2 High Noise 2 High Noise 2 High Noise 2 High Noise 2 High Noise 2 High Noise 2 High Noise 2 High muse-4.0.0/src/share/instruments/Korg-MS2000R.idf000066400000000000000000001027501404105311000212710ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Korg-X50.idf000066400000000000000000002154531404105311000207070ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Korg-X5DR-PresetA.idf000066400000000000000000000544221404105311000224130ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Korg-X5DR-PresetB.idf000066400000000000000000000543551404105311000224210ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Kurzweil-SP2X.idf000066400000000000000000000147151404105311000217770ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Lexicon-MX200.idf000066400000000000000000000443331404105311000215750ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/MC303.idf000066400000000000000000000764071404105311000201640ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/MC505.idf000066400000000000000000001061701404105311000201570ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Roland SD-50.idf000066400000000000000000006134051404105311000213620ustar00rootroot00000000000000 7e 7f 09 03 7e 7f 09 03 High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Kick Drum 2 Kick Drum 1 Aco.Snare Elec.Snare Low Tom 2 ClosedHi-hat [EXC 1] Low Tom 1 Pedal Hi-hat [EXC 1] Mid Tom 2 Open Hi-hat [EXC 1] Mid Tom 1 High Tom 2 CrashCymbal1 High Tom 1 Room Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Hi WoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Kick Drum 2 Kick Drum 1 Aco.Snare Elec.Snare Room LowTom2 ClosedHi-hat [EXC 1] Room LowTom1 Pedal Hi-hat [EXC 1] Room MidTom2 Open Hi-hat [EXC 1] Room MidTom1 Room HiTom2 CrashCymbal1 Hi Tom1 Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Kick Drum 2 Power Kick PowerSnare Elec.Snare PowerLowTom2 ClosedHi-hat [EXC 1] PowerLowTom1 Pedal Hi-hat [EXC 1] PowerMidTom2 Open Hi-hat [EXC 1] PowerMidTom1 Power HiTom2 CrashCymbal1 Power HiTom1 Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Kick Drum 2 Elec.Kick E.SnareDrum1 E.SnareDrum2 E.Low Tom 2 ClosedHi-hat [EXC 1] E.Low Tom 1 Pedal Hi-hat [EXC 1] E.Mid Tom 2 Open Hi-hat [EXC 1] E.Mid Tom 1 E.Hi Tom 2 CrashCymbal1 E.Hi Tom 1 Ride Cymbal1 Reverse Cym. SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Kick Drum 2 Ana.Kick Ana.Rim Sho Ana.Snare Hand Clap Elec.Snare Ana.Low Tom2 Ana.ClosedHH [EXC 1] Ana.Low Tom1 Ana.ClosedHH [EXC 1] Ana.Mid Tom2 Ana.Open HH [EXC 1] Ana.Mid Tom1 Ana.Hi Tom2 Ana.Cymbal Ana.Hi Tom1 Ride Cymbal1 China Cymbal Ride Bell Tambourine SplashCymbal Ana.Cowbell CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo Low Bongo Ana.Hi Conga Ana.MidConga Ana.LowConga High Timbale Low Timbale High Agogo Low Agogo Cabasa Ana.Maracas ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Ana.Claves HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Jazz Kick 2 Jazz Kick 1 Aco.Snare Elec.Snare Low Tom 2 ClosedHi-hat [EXC 1] Low Tom 1 Pedal Hi-hat [EXC 1] Mid Tom 2 Open Hi-hat [EXC 1] Mid Tom 1 High Tom 2 CrashCymbal1 High Tom 1 Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Hi WoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] High Q Slap Pedal Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Jazz Kick 2 Jazz Kick 1 Brush Tap Brush Slap Brush Swirl Low Tom 2 ClosedHi-hat [EXC 1] Low Tom 1 Pedal Hi-hat [EXC 1] Mid Tom 2 Open Hi-hat [EXC 1] Mid Tom 1 High Tom 2 CrashCymbal1 High Tom 1 Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] ClosedHi-hat [EXC 1] Hi-hat [EXC 1] Open Hi-hat [EXC 1] Ride Cymbal1 Sticks – Square Click Metron Click Metron Bell Concert BD 2 Concert BD 1 Concert SD Castanets Concert Timpani F Timpani F# Timpani G Timpani G# Timpani A Timpani A# Timpani B Timpani c Timpani c# Timpani d Timpani d# Timpani e Timpani f SplashCymbal Concert Cym2 Vibra-slap Concert Cym1 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause High Q SD Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell GtFret Noise Cut Noise Up Cut Noise Dw Slap St.Bass Fl.Key Click Laughing Scream Punch Heart Beat Footsteps 1 Footsteps 2 Applause Door Creak Door Scratch Wind Chimes Car Engine Car Stop Car Pass Car Crash Siren Train Jetplane Helicopter Starship Gun Shot Machine Gun Lasergun Explosion Dog Horse Gallop Birds Rain Thunder Wind Seashore Stream Bubble 7e 7f 09 02 7e 7f 09 02 MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Kick Drum 2 Kick Drum 1 Reg.Snr 2 Reg.Snr 1 Reg.F.Tom ClosedHi-hat [EXC 1] Reg.L.Tom Pedal Hi-hat [EXC 1] Reg.M.Tom Open Hi-hat [EXC 1] Reg.M.Tom Reg.H.Tom CrashCymbal1 Reg.H.Tom Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Hi WoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Kick Drum 2 Kick Drum 1 Snare 1 Snare 2 Low Tom 2 ClosedHi-hat [EXC 1] Low Tom 1 Pedal Hi-hat [EXC 1] Mid Tom 2 Open Hi-hat [EXC 1] Mid Tom 1 High Tom 2 CrashCymbal1 High Tom 1 Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Kick Drum 2 Kick Drum 1 Snare 1 Snare 2 Low Tom 2 ClosedHi-hat [EXC 1] Low Tom 1 Pedal Hi-hat [EXC 1] Mid Tom 2 Open Hi-hat [EXC 1] Mid Tom 1 High Tom 2 CrashCymbal1 High Tom 1 Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Power Kick2 Power Kick1 Power Snare1 Power Snare2 Low Tom1 ClosedHi-hat [EXC 1] Low Tom2 Pedal Hi-hat [EXC 1] Mid Tom 1 Open Hi-hat [EXC 1] Mid Tom2 High Tom1 CrashCymbal1 High Tom2 Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Jazz Kick 2 Jazz Kick 1 Side Stick Jazz Snare1 Hand Clap Jazz Snare2 Low Tom 2 ClosedHi-hat [EXC 1] Low Tom 1 Pedal Hi-hat [EXC 1] Mid Tom 2 Open Hi-hat [EXC 1] Mid Tom 1 High Tom 2 CrashCymbal1 High Tom 1 Ride Cymbal1 China Cymbal Ride Bell Tambourine SplashCymbal Cowbell CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo Low Bongo MuteHi Conga OpenHi Conga Low Conga High Timbale Low Timbale High Agogo Low Agogo Cabasa Maracas ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Claves HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Jazz Kick 2 Jazz Kick 1 Brush Tap Brush Slap Brush Swirl Low Tom 2 ClosedHi-hat [EXC 1] Low Tom 1 Pedal Hi-hat [EXC 1] Mid Tom 2 Open Hi-hat [EXC 1] Mid Tom 1 High Tom 2 CrashCymbal1 High Tom 1 Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Hi WoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Ana.Kick 1 Ana.Kick 2 Ana.Rim Shot Ana.Snare Elec.Snare Ana.Low Tom2 Ana.Cl HH 1 [EXC 1] Ana.Low Tom1 Ana.Cl HH 2 [EXC 1] Ana.Mid Tom2 Ana.Open HH [EXC 1] Ana.Mid Tom1 Ana.Hi Tom2 Ride Cymbal1 Ana.Hi Tom1 Ana.Cymbal China Cymbal SplashCymbal Ana.Cowbell CrashCymbal Vibra-slap Ride Cymbal2 High Bongo Ana.Hi Conga Ana.MidConga Ana.LowConga Ana.Maracas ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Ana.Claves HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Ana.Kick 1 Ana.Kick 2 Ana.Rim Shot Ana.Snare Elec.Snare Ana.Low Tom2 Ana.Cl HH 1 [EXC 1] Ana.Low Tom1 Ana.Cl HH 2 [EXC 1] Ana.Mid Tom2 Ana.Open HH [EXC 1] Ana.Mid Tom1 Ana.Hi Tom2 Ride Cymbal1 Ana.Hi Tom1 Ana.Cymbal China Cymbal SplashCymbal Ana.Cowbell CrashCymbal Vibra-slap Ride Cymbal2 High Bongo Ana.Hi Conga Ana.MidConga Ana.LowConga Ana.Maracas ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Ana.Claves HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q Slap Scratch Push [EXC 7] Scratch Pull [EXC 7] Sticks Square Click Metron Click Metron Bell Kick 2 Kick 1 Snare 1 Snare 2 Reg.F.Tom ClosedHi-hat [EXC 1] Reg.L.Tom Pedal Hi-hat [EXC 1] Reg.M.Tom Open Hi-hat [EXC 1] Reg.M.Tom Reg.H.Tom CrashCymbal1 Reg.H.Tom Ride Cymbal1 China Cymbal SplashCymbal CrashCymbal2 Vibra-slap Ride Cymbal2 High Bongo MuteHi Conga OpenHi Conga ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q Mix Kick1 Analog Kick1 Mix Snare4 Analog Kick2 Mix Snare3 Mix Kick2 HH Shaker [EXC 1] Mix Kick4 Mix Kick3 TR808 Rim Mix Snare1 TR808 Clap Mix Snare2 Tom1 TR808 ClHH [EXC 1] Tom2 Noise ClHH [EXC 1] Tom3 TR808 OpHH [EXC 1] Tom4 Tom5 TR909 Cym Tom6 TR808 Cym China Cymbal Rock Rd Edge Mix Perc Mix Crash1 Ana.Cowbell Mix Crash2 Roll FX Ride Cym High Bongo Low Bongo MuteHi Conga OpenHi Conga Low Conga High Timbale Low Timbale High Agogo Low Agogo Cabasa Maracas ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Claves HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q TR808 Kick HH Kick Mix Snare2 Mix Kick4 Short Snare2 Mix Kick3 TR808 Cl HH [EXC 1] Mix Kick1 Mix Kick2 Soft Stick Short Snare1 Hand Clap Mix Snare1 TR808 Tom1 Cl Hihat1 [EXC 1] TR808 Tom2 Cl Hihat2 [EXC 1] TR808 Tom3 Op Hihat [EXC 1] TR808 Tom4 TR808 Tom5 TR909 Cym TR808 Tom6 TR808 Cym China Cymbal Rock Rd Edge Mix Hat Mix Crash1 Ana.Cowbell Mix Crash2 Vibra-slap Ride Cym High Bongo MuteHi Conga OpenHi Conga Cabasa ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Hi WoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q TR909 Kick SH32 Kick1 AnalogSnare Analog Kick TR808 Snare SH32 Kick2 Syn ClHH1 [EXC 1] Mix Kick2 Mix Kick1 Mix Rim Mix Snare1 TR808 Clap Mix Snare2 Mix Tom1 Mix ClHH1 [EXC 1] Mix Tom2 Mix ClHH2 [EXC 1] Mix Tom3 Op Hihat [EXC 1] Mix Tom4 Mix Tom5 Mix Crash Mix Tom6 Deep Rock Rd Edge China Cymbal Ride Cymbal Tambourine Syn Splash Cowbell Concert Cym Vibraslap TR808 Cym High Bongo MuteHi Conga OpenHi Conga Cabasa ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q SH32 Kick1 TR909 Kick1 AnalogSnare1 Analog Kick1 TR808 Snare SH32 Kick2 Pedal Hihat [EXC 1] TR909 Kick2 Analog Kick2 Synth Rim DistNz Snare TR808 Clap DistNz Snare Deep Tom1 Syn ClHH1 [EXC 1] Deep Tom2 Syn ClHH2 [EXC 1] Deep Tom3 Syn OpHH [EXC 1] Deep Tom4 Deep Tom5 TR808 OpHH Tom6 Mix Wide Syn Cym TR808 Cym1 Ride Cym1 Castanet TR808 Cym2 TR808Cowbell Ride Cym2 Syn Cowbell Ride Cym3 High Bongo MuteHi Conga OpenHi Conga Cabasa ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz MC500 Beep1 MC500 Beep2 Concert SD Snare Roll Snap High Q TR808 Kick Mix Kick1 TR909 Snare1 Mix Kick2 TR909 Snare2 Mix Kick3 Thin ClHH [EXC 1] TR909 Kick Analog Kick Mix Rim Mix Snare2 Mix Clap Mix Snare1 Mix Tom1 Mix ClHH1 [EXC 1] Mix Tom2 Mix ClHH2 [EXC 1] Mix Tom3 Op Hihat [EXC 1] Mix Tom4 Mix Tom5 Mix Crash Tom6 Rock Rd Edge China Cymbal Ride Cymbal Tambourine Splash Cym Cowbell Concert Cym Vibraslap Syn China High Bongo Low Bongo MuteHi Conga OpenHi Conga Low Conga High Timbale Low Timbale High Agogo Low Agogo Cabasa Maracas ShortWhistle [EXC 2] Long Whistle [EXC 2] Short Guiro [EXC 3] Long Guiro [EXC 3] Claves HiWoodBlock LowWoodBlock Mute Cuica [EXC 4] Open Cuica [EXC 4] MuteTriangle [EXC 5] OpenTriangle [EXC 5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC 6] Open Surdo [EXC 6] Applause SnareGhost1 SnareGhost2 Hand Clap1 Hand Clap2 Scratch1 Scratch2 Scratch3 Hit1 Hit2 Hit3 Hit4 Hit5 Low Tom1 Low Tom2 Cajon1 Cajon2 Syn Burst Nz Sweep Down Laser Syn Back Nz 1PS muse-4.0.0/src/share/instruments/Roland-E28.idf000066400000000000000000000436101404105311000212000ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Roland-JV90.idf000066400000000000000000001554541404105311000213440ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Roland-MT32.idf000066400000000000000000000325551404105311000213350ustar00rootroot00000000000000 Bass Drum Bass Drum Side Stick Accoustic Snare Hand Clap Electric Snare Low Floor Tom Closed Hi-Hat Low Tom Pedal Hi-Hat Mid Tom Open Hi-Hat Mid Tom High Tom Crash Cymbal High Tom Ride Cymbal Tambourine Cowbell High Bongo Low Bongo Mute High Conga Open HighConga Low Conga High Timbales Low Timbales High Agogo Low Agogo Cabasa Maracas Short Whistle Long Whistle Guiro Claves Byte 3 = Unit Number F0 41 00 16 11 10 00 00 00 00 17 59 F7 Byte 3 = Unit Number F0 41 00 16 11 05 00 00 03 00 00 78 F7 Byte 3 = Unit Number F0 41 00 16 11 08 00 00 02 00 00 76 F7 muse-4.0.0/src/share/instruments/Roland-SC88.idf000066400000000000000000000655561404105311000213440ustar00rootroot00000000000000 41 10 42 12 40 00 7f 00 41 41 10 42 12 40 00 7f 00 41 muse-4.0.0/src/share/instruments/Roland-SCD70.idf000066400000000000000000002527471404105311000214370ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Roland-XP30.idf000066400000000000000000003127511404105311000213410ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Roland_FantomXR.idf000077500000000000000000002642101404105311000224260ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Roland_SRX-02.idf000077500000000000000000000071061404105311000216220ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Roland_SRX-09.idf000077500000000000000000000711321404105311000216310ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Waldorf-Q.idf000066400000000000000000000753011404105311000212230ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Waldorf-microQ-Factory2000.idf000066400000000000000000001013151404105311000241570ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Waldorf-microQ-Factory2001.idf000066400000000000000000001012651404105311000241640ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Waldorf-microQ-Phoenix.idf000066400000000000000000001012271404105311000236620ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Waldorf-microQ.idf000066400000000000000000001012161404105311000222500ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Waldorf_Microwave-I.idf000066400000000000000000000174161404105311000232320ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Yamaha-01v.idf000066400000000000000000000257741404105311000212440ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Yamaha-9000pro.idf000066400000000000000000007705441404105311000217510ustar00rootroot00000000000000 43 10 4c 00 00 7e 00 Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Brush Tap Swirl Snare Roll Castanet Snare Soft Sticks Kick Soft Open Rim Shot Kick Tight Kick Side Stick Snare Hand Clap Snare Tight Floor Tom L Hi-Hat Closed Floor Tom H Hi-Hat Pedal Low Tom Hi-Hat Open Mid Tom L Mid Tom H Crash Cymbal 1 High Tom Ride Cymbal 1 Chinese Cymbal Ride Cymbal Cup Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Brush Tap Swirl Snare Roll Castanet Snare Soft 2 Sticks Kick Soft Open Rim Shot H Short Kick Tight Kick Short Side Stick Light Snare Short Hand Clap Snare Tight H Floor Tom L Hi-Hat Closed Floor Tom H Hi-Hat Pedal Low Tom Hi-Hat Open Mid Tom L Mid Tom H Crash Cymbal 1 High Tom Ride Cymbal 1 Chinese Cymbal Ride Cymbal Cup Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Brush Tap Swirl Snare Roll Castanet Snare Electro Sticks Kick Tight L Snare Pitched Kick Wet Kick Tight H Stick Ambient Snare Ambient Hand Clap Snare Tight 2 Hybrid Tom 1 Hi-Hat Closed 2 Hybrid Tom 2 Hi-Hat Pedal 2 Hybrid Tom 3 Hi-Hat Open 2 Hybrid Tom 4 Hybrid Tom 5 Crash Cymbal 1 Hybrid Tom 6 Ride Cymbal 1 Chinese Cymbal Ride Cymbal Cup Tambourine Light Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Brush Tap Swirl Snare Roll Castanet Snare Soft Sticks Kick Soft Open Rim Shot Kick Tight Kick Side Stick Snare SNappy Hand Clap Snare Tight Snappy Tom Room 1 Hi-Hat Closed Tom Room 2 Hi-Hat Pedal Tom Room 3 Hi-Hat Open Tom Room 4 Tom Room 5 Crash Cymbal 1 Tom Room 6 Ride Cymbal 1 Chinese Cymbal Ride Cymbal Cup Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Brush Tap Swirl Snare Roll Castanet Snare Noisy Sticks Kick Soft Open Rim Shot Kick 2 Kick Gate Side Stick Snare Rock Hand Clap Snare Rock Tight Tom Rock 1 Hi-Hat Closed Tom Rock 2 Hi-Hat Pedal Tom Rock 3 Hi-Hat Open Tom Rock 4 Tom Rock 5 Crash Cymbal 1 Tom Rock 6 Ride Cymbal 1 Chinese Cymbal Ride Cymbal Cup Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Reverse Cymbal Snare Roll Hi Q 2 Snare Snappy Electro Sticks Kick 3 Open Rim Shot Kick Gate Kick Gate Heavy Side Stick Snare Noisy 2 Hand Clap Snare Noisy 3 Tom Electro 1 Hi-Hat Closed Tom Electro 2 Hi-Hat Pedal Tom Electro 3 Hi-Hat Open Tom Electro 4 Tom Electro 5 Crash Cymbal 1 Tom Electro 6 Ride Cymbal 1 Chinese Cymbal Ride Cymbal Cup Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Scratch H2 Scratch L2 Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Reverse Cymbal Snare Roll Hi Q 2 Snare Noisy 4 Sticks Kick 3 Open Rim Shot Kick Analog Short Kick Analog Side Stick Analog Snare Analog Hand Clap Snare Analog 2 Tom Analog 1 Hi-Hat Closed Analog Floor Analog 2 Hi-Hat Closed Analog 2 Low Analog 3 Hi-Hat Open Analog Tom Analog 4 Tom Analog 5 Crash Analog Tom Analog 6 Ride Cymbal 1 Chinese Cymbal Ride Cymbal Cup Tambourine Splash Cymbal Cowbell Analog Crash Cymbal 2 Vibraslap Ride Cymbal 2 Bongo H Bongo L Conga Analog H Conga Analog M Conga Analog L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas 2 Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves 2 Wood Block H Wood Block L Scratch H2 Scratch L2 Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Reverse Cymbal Snare Roll Hi Q 2 Snare Techno Sticks Kick Techno Q Rim Gate Kick Techno L Kick Techno Side Stick Analog Snare Clap Hand Clap Snare Dry Tom Analog 1 Hi-Hat Closed 3 Floor Analog 2 Hi-Hat Closed Analog 3 Low Analog 3 Hi-Hat Open 3 Tom Analog 4 Tom Analog 5 Crash Analog Tom Analog 6 Ride Cymbal 1 Chinese Cymbal Ride Cymbal Cup Tambourine Splash Cymbal Cowbell Analog Crash Cymbal 2 Vibraslap Ride Cymbal 2 Bongo H Bongo L Conga Analog H Conga Analog M Conga Analog L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas 2 Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves 2 Wood Block H Wood Block L Scratch H2 Scratch L2 Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Brush Tap Swirl Snare Roll Castanet Snare Jazz H Sticks Kick Soft Open Rim Shot Kick Tight Kick Jazz Side Stick Light Snare Jazz L Hand Clap Snare Jazz M Floor Tom L Hi-Hat Closed Floor Tom H Hi-Hat Pedal Low Tom Hi-Hat Open Mid Tom L Mid Tom H Crash Cymbal 1 High Tom Ride Cymbal 1 Chinese Cymbal Ride Cymbal Cup Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Brush Tap Swirl Snare Roll Castanet Brush Slap 2 Sticks Kick Soft Open Rim Shot Light Kick Tight Kick Jazz Side Stick Light Brush Slap 3 Hand Clap Brush Tap 2 Tom Brush 1 Hi-Hat Closed Tom Brush 2 Hi-Hat Pedal Tom Brush 3 Hi-Hat Open Tom Brush 4 Tom Brush 5 Crash Cymbal 1 Tom Brush 6 Ride Cymbal 1 Chinese Cymbal Ride Cymbal Cup Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl Brush Slap Brush Tap Swirl Snare Roll Castanet Snare Soft Sticks Kick Soft 2 Open Rim Shot Gran Cassa Gran Cassa Mute Side Stick Band Snare Hand Clap Band Snare 2 Floor Tom L Hi-Hat Closed Floor Tom H Hi-Hat Pedal Low Tom Hi-Hat Open Mid Tom L Mid Tom H Hand Cymbal High Tom Hand Cymbal Short Chinese Cymbal Ride Cymbal Cup Tambourine Splash Cymbal Cowbell Hand Cymbal 2 Vibraslap Handy Cymbal 2 Short Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Stereo Brush Swirl Stereo Brush Slap Stereo Brush Tap Swirl Stereo Snare Roll Stereo Castanet Snare L Stereo Sticks Kick Soft Stereo Open Rim Shot Stereo Kick Light Stereo Kick Std Stereo Side Stick Stereo Snare M Stereo Hand Clap Snare H Stereo Floor Tom L Stereo Hi-Hat Closed Stereo Floor Tom H Stereo Hi-Hat Pedal Stereo Low Tom Stereo Hi-Hat Open Stereo Mid Tom L Stereo Mid Tom H Stereo Crash Cymbal 1 Stereo High Tom Stereo Ride Cymbal 1 Stereo Chinese Cymbal Stereo Ride Cymbal Cup Stereo Tambourine Splash Cymbal Stereo Cowbell Crash Cymbal 2 Stereo Vibraslap Ride Cymbal 2 Stereo Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Stereo Brush Swirl Stereo Brush Slap Stereo Brush Tap Swirl Stereo Snare Roll Stereo Castanet Snare L Stereo Sticks Kick Soft Stereo Open Rim Shot Stereo Kick Light Stereo Kick Std Stereo Side Stick Stereo Snare M Stereo Hand Clap Snare H Stereo Floor Tom L Stereo Hi-Hat Closed Stereo Floor Tom H Stereo Hi-Hat Pedal Stereo Low Tom Stereo Hi-Hat Open Stereo Mid Tom L Stereo Mid Tom H Stereo Crash Cymbal 1 Stereo High Tom Stereo Ride Cymbal 1 Stereo Chinese Cymbal Stereo Ride Cymbal Cup Stereo Tambourine Splash Cymbal Stereo Cowbell Crash Cymbal 2 Stereo Vibraslap Ride Cymbal 2 Stereo Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Stereo Brush Swirl Stereo Brush Slap Stereo Brush Tap Swirl Stereo Snare Roll Stereo Castanet Snare Funk L Stereo Sticks Kick Soft Stereo Open Rim Shot Stereo Kick Std Stereo Kick Funk Stereo Side Stick Stereo Snare Funk M Stereo Hand Clap Snare Funk H Stereo Floor Tom L Stereo Hi-Hat Closed Stereo Floor Tom H Stereo Hi-Hat Pedal Stereo Low Tom Stereo Hi-Hat Open Stereo Mid Tom L Stereo Mid Tom H Stereo Crash Cymbal 1 Stereo High Tom Stereo Ride Cymbal 1 Stereo Chinese Cymbal Stereo Ride Cymbal Cup Stereo Tambourine Splash Cymbal Stereo Cowbell Crash Cymbal 2 Stereo Vibraslap Ride Cymbal 2 Stereo Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Stereo Brush Swirl Stereo Brush Slap Stereo Brush Tap Swirl Stereo Snare Roll Stereo Castanet Snare Funk L Stereo Sticks Kick Soft Stereo Open Rim Shot Stereo Kick Std Stereo Kick Funk Stereo Side Stick Stereo Snare Funk M Stereo Hand Clap Snare Funk H Stereo Floor Tom L Stereo Hi-Hat Closed Stereo Floor Tom H Stereo Hi-Hat Pedal Stereo Low Tom Stereo Hi-Hat Open Stereo Mid Tom L Stereo Mid Tom H Stereo Crash Cymbal 1 Stereo High Tom Stereo Ride Cymbal 1 Stereo Chinese Cymbal Stereo Ride Cymbal Cup Stereo Tambourine Splash Cymbal Stereo Cowbell Crash Cymbal 2 Stereo Vibraslap Ride Cymbal 2 Stereo Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Stereo Brush Swirl Stereo Brush Slap Stereo Brush Tap Swirl Stereo Snare Roll Stereo Castanet Brush Slap 2 Stereo Sticks Kick Soft Stereo Open Rim Shot Stereo Kick Std Stereo Kick Funk Stereo Side Stick Stereo Snare Brush M Stereo Hand Clap Snare Brush H Stereo Brush Floor Tom L Stereo Hi-Hat Closed Stereo Brush Floor Tom H Stereo Hi-Hat Pedal Stereo Brush Low Tom Stereo Hi-Hat Open Stereo Brush Mid Tom L Stereo Brush Mid Tom H Stereo Brush Crash Cymbal 1 Stereo Brush High Tom Stereo Brush Ride Cymbal 1 Stereo Chinese Cymbal Stereo Brush Ride Cymbal Cup Stereo Tambourine Splash Cymbal Stereo Cowbell Brush Crash Cymbal 2 Stereo Vibraslap Brush Ride Cymbal 2 Stereo Bongo H Bongo L Conga H Mute Conga H Open Conga L Timbale H Timbale L Agogo H Agogo L Cabasa Maracas Samba Whistle H Samba Whistle L Guiro Short Guiro Long Claves Wood Block H Wood Block L Cuica Mute Cuica Open Triangle Mute Triangle Open Shaker Jingle Bells Bell Tree Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Stereo Brush Swirl Stereo Brush Slap Stereo Brush Tap Swirl Stereo Snare Roll Stereo Castanet Snare L Stereo Sticks Kick Soft Stereo Open Rim Shot Stereo Kick Light Stereo Kick Std Stereo Side Stick Stereo Snare M Stereo Hand Clap Stereo Snare H Stereo Floor Tom L Stereo Hi-Hat Closed Stereo Floor Tom H Stereo Hi-Hat Pedal Stereo Low Tom Stereo Hi-Hat Open Stereo Mid Tom L Stereo Mid Tom H Stereo Crash Cymbal 1 Stereo High Tom Stereo Ride Cymbal 1 Stereo Chinese Cymbal Stereo Ride Cymbal Cup Stereo Tambourine Stereo Splash Cymbal Stereo Cowbell Stereo Crash Cymbal 2 Stereo Vibraslap Ride Cymbal 2 Stereo Bongo H Stereo Bongo L Stereo Conga H Mute Stereo Conga H Open Stereo Conga L Stereo Timbale H Stereo Timbale L Stereo Agogo H Agogo L Cabasa Stereo Maracas Stereo Samba Whistle H Samba Whistle L Guiro Short Stereo Guiro Long Stereo Claves Wood Block H Wood Block L Cuica Mute Stereo Cuica Open Stereo Triangle Mute Stereo Triangle Open Stereo Shaker Stereo Jingle Bells Wind Chime Stereo Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Stereo Brush Swirl Stereo Brush Slap Stereo Brush Tap Swirl Stereo Snare Roll Stereo Castanet Snare L Stereo Sticks Kick Soft Stereo Open Rim Shot Stereo Kick Light Stereo Kick Std Stereo Side Stick Stereo Snare M Stereo Hand Clap Stereo Snare H Stereo Floor Tom L Stereo Hi-Hat Closed Stereo Floor Tom H Stereo Hi-Hat Pedal Stereo Low Tom Stereo Hi-Hat Open Stereo Mid Tom L Stereo Mid Tom H Stereo Crash Cymbal 1 Stereo High Tom Stereo Ride Cymbal 1 Stereo Chinese Cymbal Stereo Ride Cymbal Cup Stereo Tambourine Stereo Splash Cymbal Stereo Cowbell Stereo Crash Cymbal 2 Stereo Vibraslap Ride Cymbal 2 Stereo Bongo H Stereo Bongo L Stereo Conga H Mute Stereo Conga H Open Stereo Conga L Stereo Timbale H Stereo Timbale L Stereo Agogo H Agogo L Cabasa Stereo Maracas Stereo Samba Whistle H Samba Whistle L Guiro Short Stereo Guiro Long Stereo Claves Wood Block H Wood Block L Cuica Mute Stereo Cuica Open Stereo Triangle Mute Stereo Triangle Open Stereo Shaker Stereo Jingle Bells Wind Chime Stereo Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Stereo Brush Swirl Stereo Brush Slap Stereo Brush Tap Swirl Stereo Snare Roll Stereo Castanet Snare L Stereo Sticks Kick Soft Stereo Open Rim Shot Stereo Kick Std Stereo Kick Funk Stereo Side Stick Stereo Snare Funk M Stereo Hand Clap Stereo Snare Funk H Stereo Floor Tom L Stereo Hi-Hat Closed Stereo Floor Tom H Stereo Hi-Hat Pedal Stereo Low Tom Stereo Hi-Hat Open Stereo Mid Tom L Stereo Mid Tom H Stereo Crash Cymbal 1 Stereo High Tom Stereo Ride Cymbal 1 Stereo Chinese Cymbal Stereo Ride Cymbal Cup Stereo Tambourine Stereo Splash Cymbal Stereo Cowbell Stereo Crash Cymbal 2 Stereo Vibraslap Ride Cymbal 2 Stereo Bongo H Stereo Bongo L Stereo Conga H Mute Stereo Conga H Open Stereo Conga L Stereo Timbale H Stereo Timbale L Stereo Agogo H Agogo L Cabasa Stereo Maracas Stereo Samba Whistle H Samba Whistle L Guiro Short Stereo Guiro Long Stereo Claves Wood Block H Wood Block L Cuica Mute Stereo Cuica Open Stereo Triangle Mute Stereo Triangle Open Stereo Shaker Stereo Jingle Bells Wind Chime Stereo Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Stereo Brush Swirl Stereo Brush Slap Stereo Brush Tap Swirl Stereo Snare Roll Stereo Castanet Snare L Stereo Sticks Kick Soft Stereo Open Rim Shot Stereo Kick Std Stereo Kick Funk Stereo Side Stick Stereo Snare Funk M Stereo Hand Clap Stereo Snare Funk H Stereo Floor Tom L Stereo Hi-Hat Closed Stereo Floor Tom H Stereo Hi-Hat Pedal Stereo Low Tom Stereo Hi-Hat Open Stereo Mid Tom L Stereo Mid Tom H Stereo Crash Cymbal 1 Stereo High Tom Stereo Ride Cymbal 1 Stereo Chinese Cymbal Stereo Ride Cymbal Cup Stereo Tambourine Stereo Splash Cymbal Stereo Cowbell Stereo Crash Cymbal 2 Stereo Vibraslap Ride Cymbal 2 Stereo Bongo H Stereo Bongo L Stereo Conga H Mute Stereo Conga H Open Stereo Conga L Stereo Timbale H Stereo Timbale L Stereo Agogo H Agogo L Cabasa Stereo Maracas Stereo Samba Whistle H Samba Whistle L Guiro Short Stereo Guiro Long Stereo Claves Wood Block H Wood Block L Cuica Mute Stereo Cuica Open Stereo Triangle Mute Stereo Triangle Open Stereo Shaker Stereo Jingle Bells Wind Chime Stereo Surdo Mute Surdo Open Hi Q Whip SLap Scratch H Scratch L Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Stereo Brush Swirl Stereo Brush Slap Stereo Brush Tap Swirl Stereo Snare Roll Stereo Castanet Brush Slap 2 Stereo Sticks Kick Soft Stereo Open Rim Shot Stereo Kick Std Stereo Kick Funk Stereo Side Stick Stereo Snare Brush M Stereo Hand Clap Stereo Snare Brush H Stereo Brush Floor Tom L Stereo Hi-Hat Closed Stereo Brush Floor Tom H Stereo Hi-Hat Pedal Stereo Brush Low Tom Stereo Hi-Hat Open Stereo Brush Mid Tom L Stereo Brush Mid Tom H Stereo Brush Crash Cymbal 1 Stereo Brush High Tom Stereo Brush Ride Cymbal 1 Stereo Chinese Cymbal Stereo Brush Ride Cymbal Cup Stereo Tambourine Stereo Splash Cymbal Stereo Cowbell Stereo Brush Crash Cymbal 2 Stereo Vibraslap Brush Ride Cymbal 2 Stereo Bongo H Stereo Bongo L Stereo Conga H Mute Stereo Conga H Open Stereo Conga L Stereo Timbale H Stereo Timbale L Stereo Agogo H Agogo L Cabasa Stereo Maracas Stereo Samba Whistle H Samba Whistle L Guiro Short Stereo Guiro Long Stereo Claves Wood Block H Wood Block L Cuica Mute Stereo Cuica Open Stereo Triangle Mute Stereo Triangle Open Stereo Shaker Stereo Jingle Bells Wind Chime Stereo Nakarazan Dom Cabasa Nakarazan Edge Hager Dom Hager Edge Bongo H Bongo L Conga H Mute Conga H Open Conga L Zagrouda H Zagrouda L Kick Soft Side Stick Snare Soft Arabic Hand Clap Snare Floor Tom L Hi-Hat Closed Floor Tom H Hi-Hat Pedal Low Tom Hi-Hat Open Mid Tom L Mid Tom H Crash Cymbal 1 High Tom Ride Cymbal 1 Crash Cymbal 2 Duhulla Dom Tambourine Duhulla Tak Cowbell Duhulla Sak Claves Doff Dom Katem Dom Katem Tak Katem Sak Katem Tak Doff Tak Tabla Dom Tabla Tak1 Tabla Tik Tabla Tak2 Tabla Sak Tabla Roll of Edge Tabla Flam Sagat 1 Tabel Dom Sagat 3 Tabel Tak Sagat 2 Rik Dom Rik Tak 2 Rik Finger 1 Rik Tak 1 Rik Finger 2 Rik Brass Tremolo Rik Sak Rik Tik Cutting Noise Cutting Noise 2 String Slap Flute Key Click Shower Thunder Wind Stream Bubble Feed Dog Horse Bird Tweet 2 Ghost Maou Phone Call Door Squeak Door Slam Scratch Cut Scratch H 3 Wind Chime Telephone Ring 2 Car Engine Ignition Car Tires Squeal Car Passing Car Crash Siren Train Jet Plane Starship Burst Roller Coaster Submarine Laugh Scream Punch Heart Beat Foot Steps Machine Gun Laser Gun Explosion Firework Conga H Tip Stereo Conga H Heel Stereo Conga H Open Stereo Conga H Mute Stereo Conga H Slap Open Stereo Conga H Slap Stereo Conga H Slap Mute Stereo Conga L Tip Stereo Conga L Heel Stereo Conga L Open Stereo Conga L Mute Stereo Conga L Slap Open Stereo Conga L Slap Stereo Conga L Slide Stereo Bongo H Open 1 Finger Stereo Bongo H Open 3 Finger Stereo Bongo H Rim Stereo Bongo H Tip Stereo Bongo H Heel Stereo Bongo H Slap Stereo Bongo L Open 1 Finger Stereo Bongo L Open 3 Finger Stereo Bongo L Rim Stereo Bongo L Tip Stereo Bongo L Heel Stereo Bongo L Slap Stereo Timbale L Open Stereo Timbale H Open Stereo Cowbell Top Stereo Guiro Short Stereo Guiro Long Stereo Tambourine Stereo Maracas Stereo Shaker Stereo Cabasa Stereo Hand Clap Stereo Conga H Tip Stereo Conga H Heel Stereo Conga H Open Stereo Conga H Mute Stereo Conga H Slap Open Stereo Conga H Slap Stereo Conga H Slap Mute Stereo Conga L Tip Stereo Conga L Heel Stereo Conga L Open Stereo Conga L Mute Stereo Conga L Slap Open Stereo Conga L Slap Stereo Conga L Slide Stereo Bongo H Open 1 Finger Stereo Bongo H Open 3 Finger Stereo Bongo H Rim Stereo Bongo H Tip Stereo Bongo H Heel Stereo Bongo H Slap Stereo Bongo L Open 1 Finger Stereo Bongo L Open 3 Finger Stereo Bongo L Rim Stereo Bongo L Tip Stereo Bongo L Heel Stereo Bongo L Slap Stereo Timbale L Open Stereo Timbale H Open Stereo Cowbell Top Stereo Guiro Short Stereo Guiro Long Stereo Tambourine Stereo Maracas Stereo Shaker Stereo Cabasa Stereo Cuica Mute Stereo Cuica Open Stereo Triangle Mute Stereo Triangle Open Stereo Wind Chime Stereo Switch General Midi mode on: Ignored by the 9000pro f0 7e 7f 09 01 f7 Switch XG mode on: Good practice to send though ignored by the 9000pro f0 43 10 4c 00 00 7e 00 f7 Change Style Section 0xF0 0x43 0x7E 0x00 ss dd 0xF7 ss = Style Section 0x00-0x01: INTRO II 0x02-0x03: INTRO III 0x04-0x07: INTRO I 0x08: MAIN A 0x09: MAIN B 0x0A: MAIN C 0x0B-0x0F: MAIN D 0x10: FILL IN A 0x11: FILL IN B 0x12: FILL IN C 0x13-0x17: FILL IN D 0x18: BREAK FILL A 0x19: BREAK FILL B 0x1A BREAK FILL C 0x1B-0x1F: BREAK FILL D 0x20-0x21: ENDING II 0x22-0x23: ENDING III 0x24-0x27: ENDING I dd = Switch On/Off On/Off 0x00 Off 0x7H On f0 43 7e 00 00 00 f7 Tempo setting of internal sequencer or style accompaniment? 0xF0 0x43 0x7E 0x01 t4 t3 t2 t1 0xF7 t4: Tempo 4 t3: Tempo 3 t2: Tempo 2 t1: Tempo 4 f0 43 7e 01 00 00 00 00 f7 Played chord by style accompaniment 0xF0 0x43 0x7E 0x02 cr ct bn bt 0xF7 cr = Chord Root First Nibble: Flat, Natural or Major 0x0? - 0x6? = bbb, bb, b, natural, #, ##, ### Second Nibble: Root Note 0x?1 - 0x?7 = C, D, E, F, G, A, B ct = Chord Type: 0x00 - 0x22 Maj, Maj6, Maj7, Maj7(#11), Maj(9), Maj7(9), Maj6(9), aug, min, min6, min7, min7b5, min(9), min7(9), min7(11), minMaj7, minMaj7(9), dim, dim7, 7th, 7sus4, 7b5, 7(9), 7(#11), 7(13), 7(b9), 7(b13), 7(#9), Maj7aug, 7aug, 1+8, 1+5, sus4, 1+2+5, Chord Cancel bn = Bass Note Same as Chord Root 0x7F: No Bass Chord bt = Bass Type Same as Chord Type 0x7F: No Bass Chord f0 43 7e 02 31 00 31 00 f7 Played chord by style accompaniment 0xF0 0x43 0x7E 03 note1 note2 note3 ... note10 0xF7 f0 43 7e 03 00 00 00 00 00 00 00 00 00 00 f7 Overall volume of all parts 0xF0 0x7F 0x7F 0x04 0x01 ll mm 0xF7 ll: Volume LSB (Ignored) mm: Volume MSB f0 7f 7f 04 01 00 7f f7 Fine control of standard XG parameters (Manual p. 235ff) 0xF0 0x43 0x0? 0x4C ah am al dd ... 0xF7 0x0?: Device Number (Transmit: 0x00, Receive 0x00 - 0x0F) ah: Address High am: Address Mid al: Address Low dd: Parameter data (variable length) f0 43 00 4c 00 00 00 f7 Bulk dump of panel configuration? 0xF0 0x43 0x1? 0x4C bh bl ah am al dd ... cc 0xF7 0x1?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) bh: Byte Count MSB bl: Byte Count LSB ah: Address High am: Address Mid al: Address Low dd: Parameter data (variable length) cc: Checksum f0 43 10 4c 00 00 00 00 00 00 f7 Request parameter value 0xF0 0x43 0x3? 0x4C ah am al 0xF7 0x3?: Device Number ah: Address High am: Address Mid al: Address Low f0 43 30 4c 00 00 00 f7 Request bulk dump 0xF0 0x43 0x2? 0x4C ah am al 0xF7 0x2?: Device Number ah: Address High am: Address Mid al: Address Low f0 43 20 4c 00 00 00 f7 Internal clock status? (Clavinova compliance) f0 43 73 01 02 f7 External clock status? (Clavinova compliance) f0 43 73 01 03 f7 Organ Flute register settings. Use in conjunction with the Organ Flute program change. 0xF0 0x43 0x73 0x01 0x06 0x0B 0x00 0x00 0x01 0x06 0x0? (21 Data Bytes) Checksum 0xF7 0x01: Model ID (Clavinova Common ID) 0x06: Bulk ID 0x0B: Bulk Number (Organ Flutes) 0x00 0x00 0x01 0x06: Data Length = 16 Bytes ?? 0x0?: MIDI Channel 21 Data Bytes: 1' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 1 1/3' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 1 3/5' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 2' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 2 2/3' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 4' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 5 1/3' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 8' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 16' Register: 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 Attack 2': 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 Attack 2 2/3': 0x00 - 0x07 = Off, -12, -9, -6, -4.5, -3, -1.5, 0 Attack Length: 0x00 - 0x07 = ? Response: 0x00 - 0x07 = ? Attack Mode: 0x00 = Each, 0x01 = First Wave Variation: 0x00 = Sine, 0x01 = Tone Wheel Volume: 0x00 - 0x07 Aux 4: Always 0x00 Aux 5: Always 0x00 Aux 6: Always 0x00 Aux 7: Always 0x00 Checksum: 0-Sum(Data Bytes) f0 43 73 01 06 0B 00 00 01 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f7 Special MIDI receive mode with the following channel assignments (when On): 1 - 10: Melody 15: Rhythm Part 16: Control (incl. SysEx) f0 43 73 01 14 f7 Special MIDI receive mode with the following channel assignments (when On): 1 - 10: Melody 15: Rhythm Part 16: Control (incl. SysEx) f0 43 73 01 13 f7 f0 43 73 01 61 f7 f0 43 73 01 62 f7 Enable of disable realtime control of volume, expression and pan for a single channel 0xF0 0x43 0x73 0x01 0x11 0x0? 0x45 dd 0xF7 0x0?: MIDI Channel dd: 0x00 = Realtime on, 0x7F = Realtime off f0 43 73 01 11 00 45 00 f7 Master tuning of the whole instrument 0xF0 0x43 0x1? 0x27 0x30 0x00 0x00 mm ll cc 0xF7 0x1?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) mm: Master Tuning MSB ll: Master Tuning LSB cc: Ignored f0 43 10 27 30 00 00 00 00 00 f7 Some weird flag for the internal sequencer? 0xF0 0x43 0x73 0x01 0x11 0x0? 0x54 dd 0xF7 0x0?: Sequencer Track Number dd: Ignored f0 43 73 01 11 00 54 00 f7 0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x00 value 0xF7 Value = Pitch to Note switch 0x00: Off 0x01: On f0 43 73 01 11 00 50 00 00 f7 0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x01 value 0xF7 Value = Pitch to Note part 0x00: Right1 0x01: Right2 0x02: Left 0x03: Lead 0x04: Upper f0 43 73 01 11 00 50 01 00 f7 0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x10 value 0xF7 Value = Harmony Part 0x00: Off 0x01: Upper 0x02: Lower f0 43 73 01 11 00 50 10 00 f7 0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x11 value 0xF7 Value = Reverb depth: 0x00 - 0x7F f0 43 73 01 11 00 50 11 00 f7 0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x12 value 0xF7 Value = Chorus depth: 0x00 - 0x7F f0 43 73 01 11 00 50 12 00 f7 0xF0 0x43 0x73 0x01 0x11 0x00 0x50 0x13 value 0xF7 Value = VH LED switch 0x00: LED Off 0x7F: LEF On f0 43 73 01 11 00 50 13 00 f7 muse-4.0.0/src/share/instruments/Yamaha-CS1x.idf000066400000000000000000003615071404105311000214110ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Yamaha-MX49_MX61.idf000066400000000000000000015226471404105311000221140ustar00rootroot00000000000000 Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd PowerD 1St Stick1 Bd PowerB 2L Sd PowerD 5St Bd PowerA 2L Bd Jazz2L Sd PowerB SL Sd PowerC 1-5St HandClap L Sd PowerD 1-5St Tom Power05 2L HH Power ClTip 1-4R Tom Power05 2L HH Power ClPedal R Tom Power04 2L HH Power Open EdgeR Tom Power03 2R Tom Power03 2R Crash2 L Tom Power01 2R Ride1 L China L Ride Cup1 L Tambourine1 Sw Splash R Cowbell2 Crash2 L Vibraslap Ride2 L Bongo Hi 1Finger 1-2 Bongo Lo 1Finger 1-2 Conga Hi SlapMute1-2 Conga Hi Open 1-2 Conga Lo Open 1-2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1/3 Sleigh Bell Wind Chime Sd Bld St3-4 Sd Blues 3St Sd Hip St1-2 Sd Funk St2-3 Sd Soul St3-4 Sd LdwHMono Sd Heavy Sd Tight St3-4 Sd BluesHeart St2-3 Bd Snap R2 Bd Tight R2 Bd Funk 2 R Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd PowerA 1St Stick1 Bd PowerB 2L Sd PowerA 5St Bd PowerA 2L Bd Jazz2L Sd PowerB SL Sd PowerB 1-5St HandClap L Sd PowerA 1-5St Tom Power05 2L HH Power ClEgde1-4R Tom Power05 2L HH Power ClPedal R Tom Power04 2L HH Power Open EdgeR Tom Power03 2R Tom Power03 2R Crash2 L Tom Power01 2R Ride1 L China L Ride Cup1 L Tambourine1 Sw Splash R Cowbell2 Crash2 L Vibraslap Ride2 L Bongo Hi 1Finger 1-2 Bongo Lo 1Finger 1-2 Conga Hi SlapMute1-2 Conga Hi Open 1-2 Conga Lo Open 1-2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1/3 Sleigh Bell Wind Chime Sd Bld St3-4 Sd Blues 3St Sd Hip St1-2 Sd Funk St2-3 Sd Soul St3-4 Sd LdwHMono Sd Heavy Sd Tight St3-4 Sd BluesHeart St2-3 Bd Snap R2 Bd Tight R2 Bd Funk 2 R Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Normal 3St Stick1 Bd Funk Son L Sd Basic St2-3 Bd Snap R2 Bd H Son L Sd Closed Rim St Sd SonA St3-4 Clap T9 Sd Tight St3-4 Tom Floor L HH Closed Foot R Tom Floor L HH Half Open2 R Tom Low L HH Open2 R Tom Low L Tom Mid R Crash2 L Tom Hi R Ride1 L China L Ride Cup1 L Tambourine1 Sw Splash R Cowbell2 Crash2 L Vibraslap Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Bld St3-4 Sd Blues St2-3 Sd BluesHeart 3R Sd Hip St1-2 Sd Normal St3-4 Sd LdwHMono Sd Soul St3 Sd Heavy Sd Funk 2St Bd Normal 2L Bd Funk Son L Bd Snap R2 Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Normal 3St Stick1 Bd Funk 2 R Sd Basic St2-3 Bd High L Bd Normal 2L Sd Closed Rim St Sd Normal St3-4 Clap T9 Sd Basic 2St Tom Floor L HH Closed 1-4R Tom Floor L HH Pedal 3 R Tom Low L HH Open 2R Tom Low L Tom Mid R Crash2 L Tom Hi R Ride1 L China L Ride Cup1 L Tambourine1 Sw Splash R Cowbell2 Crash2 L Vibraslap Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Slp Open Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Bld St3-4 Sd Blues 3L Sd Hip St1-2 Sd Funk St2-3 Sd Soul St3-4 Sd LdwHMono Sd Heavy Sd Tight St3-4 Sd BluesHeart St2-3 Bd Snap R2 Bd Tight R2 Bd Funk 2 R Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Rock Open1 R Stick1 Bd Room1 Sd Strong St4 Bd Jazz2L Bd Room3 Sd Rock Stick2 R Sd Strong St3-4 HipHop clap6 Sd PowerA 1-5St Tom Floor L HH Closed Rock Sw R Tom Low L HH Half Open Rock R Tom Low L HH Open Rock R Tom Mid R Tom Mid R Crash1 L Tom Hi R Ride2 L China L Ride Cup2 L Tambourine1 Sw Splash R Cowbell2 Crash1 L Vibraslap Ride Cup1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Blues St2-3 Sd Tight St3-4 Sd BluesHeart 3R Sd Tight 4R Bd Low L Sd PowerC 4St Bd R&B1 Sd PowerB 1-5St Bd Room1 Sd PowerA 1-5St Bd Funk Son L Bd Room2 Analog Click Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd RockRoll St Castanet Sd Rock Open2 R Stick1 Bd Room3 Sd Rock Rim1 R Bd Rock2 L Bd Rock1 L Sd Rock Stick1 R Sd Rock Open1 R HipHop clap3 Sd Rock Mute1 R Tom Rock Low L HH Closed Rock Sw R Tom Rock Low L HH Pedal Rock Sw R Tom Rock Mid R HH Open Rock Sw R Tom Rock Mid R Tom Rock Hi R Crash1 L Tom Rock Hi R Ride2 L China L Ride Cup2 L Tambourine RX5 Splash R Cowbell1 Crash1 L Vibraslap Ride2 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Rock Flam R Sd Rock Open2 R Sd Rock Rim2 R Sd Rock Mute2 R Bd Funk 2 R Sd RockRollD St Bd H Son L Sd Tight St3-4 Bd Funk Son L Sd SonA 3St Bd Funk 2 R Bd H Son L Analog Click Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd RockRoll St Castanet Sd Rock Open2 R Stick1 Bd Room1 Sd Rock Rim2 R Bd Ambience R2 Bd Rock2 L Sd Rock Stick2 R Sd Rock Open2 R HandClap L Sd Rock Mute2 R Tom Rock Low L HH Closed Rock 2R Tom Rock Low L HH Half Open Rock R Tom Rock Mid R HH Open Rock R Tom Rock Mid R Tom Rock Hi R Crash2 L Tom Rock Hi R Ride2 L China L Ride Cup2 L Tambourine RX5 Splash R Cowbell1 Crash2 L Vibraslap Ride2 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Rock Flam R Sd Rock Open1 R Sd Rock Rim1 R Sd Rock Mute1 R Bd Rock1 L Sd RockRollD St Bd Funk 2 R Sd Tight St3-4 Bd High L Sd SonA 4St Bd L Son L Bd Rock1 L Bd HipHop2 Cabasa2 Sd HipHop9 Scratch Lo R Scratch Vo F Sd RockRollD St Clave Sd HipHop5 Sd HipHop Rim3 Bd HipHop8 Sd HipHop Rim6 Bd HipHop9 Bd HipHop5 Sd HipHop Rim2 Sd HipHop11 HipHop clap2 Sd HipHop2 Tom Floor L HH Closed Hip Tom Low L HH Pedal Hip Tom Mid R HH Open Hip Tom Hi R Tom Hi R Crash4 Tom Hi R Ride2 L Crash2 L Ride Mono Tambourine1 Hit Hand Cymbal Open R Mambo Body China L Handbell L Ride Cup Mono2 Conga Hi Slp Open Conga Lo Open 1 Conga Lo Tip Conga Lo SlapOpen Conga Lo Open 2 Bd HipHop4 HipHop clap3 Agogo Hi Agogo Lo Shaker 2 Shaker 1 Bd HipHop10 Bd HipHop6 Sd HipHop Rim4 Sd HipHop6 HipHop Snap1 Bd HipHop10 Sd HipHop7 HipHop clap1 Paila L Triangle Mute Triangle Open Tambourine1 Sw Shaker 2 Bd HipHop8 Clap T9 Sd T8-1 Sd T8-1 Sd T8-4 Tom T8 HH Closed T8-2 Tom T8 HH Closed T8-2 Tom T8 HH Open T8-2 Tom T8 Crash T8 Bd HipHop3 Hi Q2 Scratch Hip2 Scratch Sd R Scratch Vo R Sd Break Roll Sd T9Rim Sd HipHop2 Sd T8 Rim Bd HipHop9 Sd HipHop Rim1 Bd HipHop6 Bd HipHop5 Sd HipHop Rim2 Sd HipHop1 HipHop clap6 Sd HipHop9 Tom T9 Lo HH Closed Hip Tom T9 Lo HH Pedal Hip Tom T9 Hi HH Open Hip Tom T9 Hi Tom T8 Crash3 Tom T8 Ride T9 Ride T9 Crash Brush Cup L Tambourine RX5 Crash Syn Cowbell AnCR Crash T9 MetalGuiro Long Ride Mono Cajon Tip 1-2 Bongo Lo Tip Tmbourim Finger Tumbo 2 Tumba Heel Paila H Paila L Cowbell T8 Cowbell AnCR Shekere Up Caxixi1 Grain2 R Grain2 R Cajon Tip 1 HipHop clap7 Clave Wood Block Sd HipHop Rim4 HipHop Snap1 HipHop Snap2 Electric Triangle Triangle Open Shaker 1 MetalGuiro Long Wind Chime Sd HipHop Rim4 Sd T8-1 Sd T8-2 Sd T8-4 Tom T8 HH Closed T8-2 Tom T8 HH Closed T8-2 Tom T8 HH Open T8-2 Tom T8 HipHop flex1 Sd Hip Stick2 Vox Hit Rev TurnTable Hit Brass Scratch Sd R Sd LiveRoll R Clave T8 Sd Hip 2R Sd Hip Stick6 R Bd HipHop9 Sd Garg L Bd T8-3 Bd T9-1 Sd HipHop Rim3 Sd HipHop10 HipHop clap6 Sd HipHop1 Tom T8 HH Closed T8-1 Tom T8 HH Open T8-1 Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash4 Tom T8 Ride Brush1 L Crash T8 Ride T9 Tambourine RX5 Splash R Mambo Body Crash T9 Vibraslap Ride T9 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Hip Stick8 Sd Jungle3 HandClap L Sd Hip Rim5 Scratch H Scratch L Scratch Down Scratch Hi R Scratch Bd F Scratch Combi Scratch Vo R Bd Break2 Bd T8-3 Sd T8 Rim Sd Hip1 Clap T9 Sd Syn Sd LiveRoll R Sd Hip Stick4 Sd HipHop2 Sd HipHop Rim6 Bd Hip Deep Sd R&B1 Rim Bd Break Heavy Bd Hip Deep Sd HipHop Rim2 Sd Hip3 HandClap L Sd Lo-Fi Tom Floor R HH Closed Lo-Fi Tom Low L HH Closed Break Tom Low L HH Open Lo-Fi Tom Low L Tom Hi L Crash3 Tom Hi L Ride T9 China2 Ride Cup Mono Tambourine RX5 Crash3 Cowbell RX11 Crash2 R Cowbell T8 Ride T9 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Radio Noise Radio Noise Agogo Lo Agogo Lo Cabasa1 MaracasT8 FxGun2 FxGun1 Scratch Spin Scratch Stop Clave T8 Door Squeak Hi Q2 Scratch Bd F Scratch Bd R Triangle Mute Triangle Open Analog Shaker Tambourine1 Hit Wind Chime Sd Human2 Sd Break Roll Stab Hard Sd Jungle1 Bd Rock1 L Scratch Down Wataiko HH Hit Stab Organ2 Sonar1 TurnTable Police Sd T8 Rim Vox Hit Rev TurnTable Stab Hard Scratch Vo R Sd Roll Mono Clave T8 Sd Hip 1R Sd Hip Stick4 Bd AnCR Sd Hip Rim3 Bd T9-3 Bd Hip Deep Sd Hip Stick5 Sd Hip Gate HipHop clap6 Sd Lo-Fi Tom T8 HH Closed Hip Tom T8 HH Pedal Hip Tom T8 HH Open Hip Tom T8 Tom T8 Crash4 Tom T8 Ride2 L Crash T8 Ride T9 Tambourine RX5 Splash R Mambo Body Crash T9 Vibraslap Ride T9 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Shaker Hip1 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Hip Stick1 Sd Jungle1 HandClap L Sd Break Heavy Scratch L Scratch Down Scratch Hi F Scratch Lo F Scratch Lo R Scratch Bd F Scratch Bd R Bd Break Heavy Sd Hip Stick1 Hi Q1 TurnTable Hit Uhh Scratch Stop Sd LiveRoll L Clave T8 Sd Cut Sd Rock Stick1 R Bd T9-2 Sd Hip Rim1 Bd R&B2 Bd Break Deep Sd Closed Rim3 Sd Hip Rim2 HipHop clap4 Sd R&B1 Conga T8 HH Closed T8-1 Conga T8 HH Closed T8-2 Conga T8 HH Open T8-2 Conga T8 Conga T8 Crash4 Conga T8 Ride2 L Crash T8 Ride T9 Tambourine RX5 Splash R Mambo Body Crash T9 Vibraslap Ride T9 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Closed Rim R Sd AnSm Rim HandClap L Sd Hip3 Scratch Sd R Scratch Hip Scratch Bd R Scratch Vo R Scratch Hip2 Scratch Combi2 Scratch Combi4 Bd Hip Deep Sd Hip Rim4 Hi Q1 TurnTable Hit Orchestra1 Scratch Bd R Sd LiveRoll R Analog Click Sd Hip Rim3 Sd AnCR Rim Bd HipHop9 Sd HipHop Rim6 Bd T8-3 Bd T8-2 Sd T8 Rim Sd Hip Stick4 HandClap L Sd Hip Stick3 Tom T8 HH Closed T8-1 Tom T8 HH Open T8-1 Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash4 Tom T8 Ride2 L Crash T8 Ride T9 Tambourine RX5 Splash R Mambo Body Crash T9 Vibraslap Ride T9 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd AnSm Sd Closed Rim3 Analog Click Sd Closed Rim2 Scratch H Scratch L Scratch Down Scratch Hi R Scratch Bd F Scratch Combi Scratch Vo R Bd Jungle2 Sd Hip Rim3 Hi Q2 Scratch H Scratch H Scratch Vo R Sd Roll Mono Analog Click Sd Hip Rim4 Sd T8 Rim Bd HipHop9 Sd Hip Stick4 Bd Hard Long Bd ElecClick Sd Hip Stick1 Sd Hip Stick2 HipHop clap8 Sd Hip Stick6 R Tom T9 Lo HH Closed T8-1 Tom T9 Lo HH Open T8-1 Tom T9 Lo HH Open T8-1 Tom T9 Lo Tom T9 Lo Crash T9 Tom T9 Lo Ride1 L Crash T8 Ride Cup Mono2 Electric Tambourine2 Splash R Cowbell T8 Crash T9 Vibraslap Crash Syn Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd T8 Rim Sd Hip Stick1 Sd Hip Stick2 Sd Closed Rim2 Scratch Down Scratch Hi F Scratch Lo F Scratch Lo R Scratch Bd R Scratch Combi Scratch Stop Bd HipHop8 Bd T9 Hd3 Sd AnCR Sd AnSm Rim Sd ClapAna Sd T9Gate Sd Roll Mono Sd Timbr Sd FM Rim Sd JungleFX Bd T9-4 Sd Hip Rim5 Bd T9-3 Bd Elec2 Sd T8 Rim Sd Lo-Fi HipHop clap2 Sd R&B1 Bd T8-3 HH Closed T9 Bd T8-3 HH Pedal Hip Bd T8-3 HH Open T9 Bd T8-3 Bd T8-3 Crash1 L Bd T8-3 Ride2 L China2 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash1 L Cowbell T8 Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Hi Q1 Hi Q1 Agogo Lo Agogo Lo Analog Shaker Maracas Slur Vox Drum Vox Drum Hi Q1 Hi Q2 Clave T8 Hi Q1 Scratch Spin Scratch Hi R Scratch Hi F Triangle Mute Triangle Open Shaker 2 Tambourine1 Hit Wind Chime Sd Break Roll Noise Burst Vox Bell Sd R&B1 VoxAlk Udu High Trance Choir Swarm Bd Pop Sd R&B1 Rim Bd R&B2 Crash3 Bd T9-1 Sd AnCR Rim Sd Hip Gate Sd Break Roll Sd T9-2 Sd LiveRoll St HipHop Snap2 Sd HipHop9 Sd HipHop Rim2 Bd HipHop5 Sd Hip Rim4 Bd T9-1 Bd T8-2 Sd HipHop Rim1 HipHop Snap2 HipHop clap8 Sd T8-5 Tom T8 HH Closed T8-1 Tom T8 HH Open T8-1 Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash1 L Tom T8 Ride2 L China2 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash1 L Cowbell T8 Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Hi Q1 Hi Q1 Agogo Lo Agogo Lo Analog Shaker Maracas Slur Vox Drum Vox Drum Hi Q1 Hi Q2 Clave T8 Hi Q1 Scratch Spin Scratch Hi R Scratch Hi F Triangle Mute Triangle Open Shaker 2 Tambourine1 Hit Wind Chime Sd Break Roll Noise Burst Vox Bell Sd R&B1 VoxAlk Udu High Trance Choir Swarm Bd Pop Sd R&B1 Rim Bd R&B2 Crash3 Bd T9 Hd2 Sd AnCR Sd Break2 Sd Break Roll Sd T9Gate Sd Roll Mono HipHop Snap2 Sd HipHop2 Sd HipHop Rim3 Bd T9-2 Sd Hip Rim3 Bd T9-1 Bd T8-2 Sd HipHop Rim3 Sd R&B1 HipHop clap6 Sd Hip Gate Tom T9 Lo HH Closed T9 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Lo Tom T9 Hi Crash1 L Bd T8-3 Ride2 L China2 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash1 L Cowbell T8 Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Hi Q1 Hi Q1 Agogo Lo Agogo Lo Analog Shaker Maracas Slur Vox Drum Vox Drum Hi Q1 Hi Q2 Clave T8 Hi Q1 Scratch Spin Scratch Hi R Scratch Hi F Triangle Mute Triangle Open Shaker 2 Tambourine1 Hit Wind Chime Sd Break Roll Noise Burst Vox Bell Sd R&B1 VoxAlk Udu High Trance Choir Swarm Bd Pop Sd R&B1 Rim Bd R&B2 Crash3 Bd T9-2 Sd AnSm Rim Sd Elec9 Clap AnSm Sd Elec7 Sd Elec11 Sd Elec7 Sd T8-1 Sd T9-5 Bd T9 Hd2 Sd Elec12 Bd T9 Hd3 Bd Hard Long Sd T9Rim Sd Elec5 Clap T9 Sd T9-2 Sd Syn HH Closed T9 Sd Syn HH Open T9 Sd Syn HH Open T9 Sd Syn Sd Syn Crash T9 Sd Syn Ride T9 Crash Syn Ride Cup Mono Tambourine1 Hit Crash T8 Cowbell T8 China L Cowbell T8 Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Slap Conga Hi Slp Open Conga Hi Open 2 Tom Syn1 Tom Syn1 Analog Click Noise Burst Cabasa2 Maracas Slur Neo Seq Em-3 Neo Seq2 Em-5 Electric Perc1 Shaker Hip1 Clave T8 Tambourine RX5 Cowbell RX11 Shaker Hip2 Electric Perc10 Triangle Mute Triangle Open Shaker Hip4 Zap2 Bd HipHop8 Bd T9 Hd1 Bd ElecComp3 Bd T9-1 Bd T9 Hd3 Bd Hard Long HH Closed T8-1 Bd ElecComp2 HH Open T8-2 Bd T9 Hd1 HH Open T8-1 Bd T9 Hd2 Bd ElecComp3 LFO Seq Trance Choir Tibetan Bowl Scratch Stop Fx Female Voice L Stab Organ2 Door Squeak Radio Noise Door Squeak Acoustic Bass FX06 Electric Bass FX 02 Acoustic Bass FX06 Bd BlpHd Bd Blip Sd Hip Gate Sd SonA St3-4 Noise Burst Tom Dist Lo HH Closed T9 Tom Dist Hi HH Closed 2R Tom T9 Hi HH Open T9 Tom T9 Hi Tom T9 Hi China L Tom T9 Hi Splash R Crash T9 Ride T9 Pandeiro Tip Electric Tambourine1 Hi Q2 Bd Gate Hi Q2 Acoustic Bass FX03 Acoustic Bass FX01 Mini Blip6 Mini Blip1 Mini Blip2 Quinto1 Slap Tabla Bya Djembe Edge Tabla Nah Barasim Tak Electric Perc8 Electric Perc13 Hop FX2 Motion Beam Zap2 Digi Voice Mod Human Whistle Digital Wind Analog Click HH Closed T8-1 Sd FM HH Half Open2 R Clave T8 HH Open T8-1 Reverse Cymbal Bd Analog Tight Bd ElecComp3 Bd T9 Hd3 Bd ZapHard Bd Slimy Bd Break Hard Sd BreakRim Sd Soul St3 Sd Ambience Sd Rock Mute1 R Sd Cut Sd D&B3 Sd FX1 Bd T8-1 Sd T8 Rim Sd T8-2 Electric Clap1 Sd Elec11 Sd Elec7 Sd T8-4 Bd Elec2 Sd T8-1 Bd HipHop8 Sd T8-3 Bd HipHop9 Bd HipHop8 Sd AnSm Rim Sd HipHop5 Clap AnSm Sd HipHop6 Tom T8 HH Closed T8-1 Tom T8 HH Open T8-1 Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash3 Tom T8 Ride T9 Splash R Ride Cup Mono Tambourine1 Hit Crash T8 Cowbell T8 China L Cowbell T8 Ride1 L Conga T8 Conga T8 Conga T8 Conga T8 Conga T8 Conga Hi Slap Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1 Cabasa2 Maracas Slur Fx Female Voice L Portamento Lead Seq Electric Perc9 Shaker Hip1 Clave T8 Hi Q1 Hi Q2 Shaker Hip2 Electric Perc15 Triangle Mute Triangle Open Shaker Hip4 Ripper Bd Elec4 Sd Hip1 Sd Elec9 Sd Elec6 Sd HipHop2 Bd ElecGate HH Closed T8-2 Bd ThickSkin HH Open T8-2 Bd Break Heavy HH Open T8-2 Bd Break Deep Bd R&B2 Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Brush Soft St Stick1 Bd Funk 2 R Sd Basic 3St Bd Jazz2L Bd FK Jazz R Sd Closed Rim4 St Sd Brush Med St HandClap L Sd Brush vari St Tom Brush Floor L HH Power ClTip 1-4R Tom Brush Low L HH Power ClPedal R Tom Brush Low L HH Power Open EdgeR Tom Brush Mid R Tom Brush Mid R Crash Brush1 L Tom Brush Hi R Ride Brush1 L China L Crash Brush Cup L Tambourine1 Sw Splash R Cowbell2 Crash Brush2 R Vibraslap Ride Brush2 L Bongo Hi 1Finger 1-2 Bongo Lo 1Finger 1-2 Conga Hi SlapMute1-2 Conga Hi Open 1-2 Conga Lo Open 1-2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1/3 Sleigh Bell Wind Chime Sd Bld St3-4 Sd Blues 3St Sd Hip St1-2 Sd Funk St2-3 Sd Soul St3-4 Sd LdwHMono Sd Heavy Sd Tight St3-4 Sd BluesHeart St2-3 Bd Snap R2 Bd Tight R2 Bd Funk 2 R Bd Jazz2L Sd Brush Soft R Sd Brush SwirAtt St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Hip 1St Stick1 Bd Funk 2 R Sd Tight 3St Bd FK Jazz R Bd Funk 2 R Sd Closed Rim St Sd Tight 1St Sd RockRoll St Sd BluesHeart St2-3 Tom Floor L HH Power ClTip 1-4R Tom Floor L HH Power ClPedal R Tom Low L HH Power Open EdgeR Tom Mid R Tom Mid R Crash2 L Tom Hi R Ride2 L China L Ride Cup Mono2 Tambourine1 Hit Splash R Cowbell1 Crash2 L Vibraslap Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1 Timbale Hi1 Agogo Lo Agogo Lo Cabasa1 Maracas Slur Sd Brush Soft R Sd Brush Swir St Guiro Short Guiro Long Clave Wood Block Wood Block Timbale Paila H Timbale Paila L Triangle Mute Triangle Open Shaker 2 Sleigh Bell Wind Chime Sd Blues 3L Sd Funk Ay L Sd BluesHeart 3R Sd Blues 2St Bd Low L Sd LdwHMono Bd Dry2 Sd Brush SwirAtt St Bd Hip Deep Sd Roll Mono Bd Normal 2L Crash Brush2 R Bd T9-2 Sd AnCR Sd T9-5 Clap AnSm Sd T9Gate Sd RockRoll St Sd T9-3 Sd T9-4 Sd T9Gate Bd T9-4 Sd T9-5 Bd T9-1 Bd T9-3 Sd T9Rim Sd T9-1 Clap T9 Sd T9-2 Tom T9 Lo HH Closed T9 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Hi Tom T9 Hi Crash T9 Tom T9 Hi Ride T9 China2 Ride Cup Mono Tambourine RX5 Crash3 Cowbell1 Crash4 Cowbell T8 Ride2 L Conga T8 Conga T8 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Analog Click Conga T8 Cabasa1-2 Maracas Slur FxGun2 FxGun1 Scratch H Scratch Down Hi Q1 Hi Q1 Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd Piccolo Sd T8-5 Sd RockRollD St Sd Brush Med St Bd BlpHd Sd Jungle1 Bd Sustain Sd D&B1 Bd Break2 Sd Dist Bd TekPower Bd Distortion RM Bd T8-1 Sd AnCR Sd T8-4 Sd ClapAna Sd T8-3 Tom T8 Sd T8-5 Bd T8-3 Sd T8-4 Bd T8-2 Sd T8-3 T8 Kick Bass Bd T8-2 Sd T8 Rim Sd T8-2 Clap T9 Sd T8-1 Tom T8 HH Closed T8-2 Tom T8 HH Open T8-1 Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash T8 Tom T8 Ride T9 China2 Ride Cup Mono Tambourine RX5 Splash R Cowbell T8 Crash4 Vibraslap Ride2 L Conga T8 Conga T8 Conga T8 Conga T8 Conga T8 Timbale Hi1 Timbale Lo1 Glass Glass Cabasa1-2 MaracasT8 FxGun2 FxGun1 Analog Shaker Analog Shaker Clave T8 Hi Q1 Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd Hip1 Sd Hip2 Sd Hip Gate Sd Break1 Bd Blip Sd FX1 Bd FxHammer Sd Hammer Bd ZapHard Sd Garg L Bd TekPower Bd Slimy Bd Slimy Sd T9Rim Sd T8-5 Clap T9 Sd T9-3 Sd Human1 Sd Hip3 Sd T9-4 Sd Hip2 Bd ThickSkin Sd T9-2 Bd Distortion4 Bd ZapMid Sd T8 Rim Sd T8-5 Clap T9 Sd Syn Tom T8 HH Closed Lo-Fi Tom T8 HH Open Lo-Fi Tom T8 HH Open Lo-Fi Tom T8 Tom T8 Crash T8 Tom T8 Ride T9 Crash3 Ride Cup Mono Tambourine AnCR Crash2 L Cowbell T8 Crash T9 Cowbell T8 Crash Syn Conga T8 Conga T8 Conga T8 Conga T8 Conga T8 Timbale Hi1-3 Timbale Lo1-2 Glass Glass Cabasa1 MaracasT8 FxGun2 FxGun1 Scratch H Scratch Down Hi Q1 Hi Q1 Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Analog Shaker Sleigh Bell Bd Elec4 Bd ElecComp3 Bd ElecFX2 Bd ElecClick Bd ElecPower1 Bd ElecPower2 Bd Elec3 Bd Elec1 Sd Elec6 Sd Elec7 Sd Elec8 Sd Elec9 Sd Elec12 Bd T9-4 Sd T8 Rim Sd T8-5 HandClap L Sd Garg L Sd LiveRoll St Sd T9-3 Sd T8-1 Sd T9-5 Bd T9-1 Sd T9Gate Bd T9-2 Bd T9-2 Sd T9Rim Sd T9-1 Clap T9 Sd T9-2 Tom T9 Lo HH Closed T8-1 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Hi Tom T9 Hi Crash T9 Tom T9 Hi Ride T9 Crash3 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 L Cowbell T8 Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1 Timbale Hi1 Agogo Lo Agogo Lo Cabasa1 Maracas Slur Vox Drum Vox Drum Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd Break Roll Noise Burst Vox Bell Sd R&B1 VoxAlk Udu High Trance Choir Swarm Bd Pop Sd R&B1 Rim Bd R&B2 Crash3 Bd T9-2 Sd T8 Rim Dohol High Clap T9 Sd T9-2 Sd T9Gate Sd Human2 Sd FX1 Sd Lo-Fi Bd T9 Hd2 Sd T9Rim Bd T9-3 Bd T9 Hd1 Sd T9Rim Sd T9-2 Noise Burst Sd Strong L3 Sd Syn HH Closed T9 Sd Syn HH Open T9 Sd Syn HH Open T9 Sd Syn Sd Syn Crash T9 Sd Syn Ride T9 Crash Syn Ride Cup Mono Tambourine1 Hit Crash T8 Cowbell1 Crash3 Cowbell T8 Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Heel Conga Hi Slp Open Conga Hi Open 2 Tom Syn1 Tom Syn1 Analog Click Sd Rim SE Cabasa1 Maracas Slur Vox Drum Vox Drum Tambourine AnCR Analog Shaker Clave T8 Tambourine RX5 Cowbell RX11 Analog Shaker Scratch Hi F Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Tabla Bya Tabla Nah Tabla Mute Vox Bell Bd Pop Udu High Bd FX Gate Noise Burst Bd T9-1 Sd Break Roll Crash3 TurnTable Bd T9-4 Sd T8 Rim Sd T8-5 HandClap L Sd Garg L Sd LiveRoll St Sd T9-3 Sd T8-1 Sd T9-5 Bd T9-1 Sd T9Gate Bd T9-2 Bd T9-3 Sd T9Rim Sd T9-1 Clap T9 Sd T9-2 Tom T9 Lo HH Closed T8-1 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Hi Tom T9 Hi Crash T9 Tom T9 Hi Ride T9 Crash3 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 L Cowbell T8 Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1 Timbale Hi1 Agogo Lo Agogo Lo Cabasa1 Maracas Slur Vox Drum Vox Drum Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd Break Roll Noise Burst Vox Bell Sd R&B1 VoxAlk Udu High Trance Choir Swarm Bd Pop Sd R&B1 Rim Bd R&B2 Crash3 Bd FX Gate Bongo Lo 3Finger 1-2 Sd GM HandClap L Sd Gate2 Sd RockRoll St Sd Rock Rim1 R Sd Piccolo Sd Rock Flam R Bd Room2 Sd Rock Stick1 R Bd Room3 Bd Gate Sd Mid Rim Sd Gate1 Clap AnSm Sd Ambience Tom Ambience Lo HH Closed D&B Tom Ambience Lo HH Pedal D&B Tom Ambience Lo HH Open D&B Tom Ambience Hi Tom Ambience Hi Crash2 L Tom Ambience Hi Ride1 L China L Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 L Vibraslap Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Bd +HH Bd +HH Agogo Lo Agogo Lo Cabasa1 Maracas Slur FxGun2 FxGun1 Scratch H Scratch Down Clave Wood Block Wood Block Scratch L Scratch L Triangle Mute Triangle Open Shaker 2 Sleigh Bell Wind Chime Sd Dist Sd FM Sd FM Rim Sd Clappy Bd Room3 Sd Timbr Bd Rock1 L Sd Rim SE Bd Rock2 L Sd Break Roll Bd D&B1 Bd Room2 Bd Break Deep Sd Hip 1St Sd Lo-Fi Sd Clappy Sd LdwHMono Sd RockRollD St Sd Gate1 Sd Mid Sd BreakRim Bd Break Heavy Sd Hip Rim4 Bd Break2 Bd Break1 Sd Hip Rim1 Sd Break3 Sd Break1 Sd Break2 Tom Low L HH Closed Rock S R Tom Low L HH Pedal Rock R Tom Low L HH Half Open Rock R Tom Hi R Tom Hi R Crash2 L Tom Hi R Ride2 L China L Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 R Cowbell RX11 Ride Mono Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Open 2 Agogo Lo Cabasa1 Maracas Slur Timbale Hi1 Timbale Lo1 Scratch H Scratch Down Clave Wood Block Wood Block Scratch L Scratch L Triangle Mute Triangle Open Shaker 2 Sleigh Bell TurnTable Sd Break Roll FM Bass5 Mind Bell Sd FX1 Bd Rock2 L LFO Seq Ripper Noise Burst Police Fairwave Swarm Radio Noise Bd Hip Deep Bongo Lo 3Finger 1-2 Sd R&B2 Sd Timbr Sd D&B3 Sd Hammer Glass Bd D&B2 HH Pedal 3 R Bd Hip Deep HH Open2 R Bd Break Hard Bd D&B1 Sd Mid Rim Sd D&B1 Noise Burst Sd D&B2 Tom Ambience Lo HH Closed D&B Tom Ambience Lo HH Pedal D&B Tom Ambience Lo HH Open D&B Tom Ambience Hi Tom Ambience Hi Crash2 L Tom Ambience Hi Ride1 L China2 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 L Vibraslap Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Sd D&B4 HH Reverse D&B Agogo Lo Agogo Lo Cabasa1 Maracas Slur FxGun2 FxGun1 Guiro Short Guiro Long Clave Electric Perc8 Electric Perc2 P50-1 Electric Perc13 TurnTable Triangle Open Shaker 2 Sleigh Bell Wind Chime Sd Dist Sd FM Sd FM Rim Sd Clappy Bd D&B1 Sd Timbr Bd Distortion5 Sd Rim SE Bd Distortion4 Sd Hip3 Bd Slimy Bd +HH Bd Analog Tight Sd T8 Rim Sd Jungle3 HandClap L Sd T8-1 Tom Syn1 Sd BluesHeart 3L Sd BluesHeart 3R Sd SonA 3St Bd Funk 2 R Sd Basic St2-3 Bd Blip Bd T9-4 Sd T9Rim Sd FM Clap T9 Sd AnCR Tom T9 Lo HH Closed Tek Tom T9 Lo HH Closed Tek Tom T9 Lo HH Open Tek Tom T9 Hi Tom T9 Hi Crash T9 Tom T9 Hi Ride T9 Crash3 Ride Cup2 L Tambourine1 Hit Crash3 Cowbell1 Splash R Cowbell T8 Ride Mono Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1 Timbale Hi1 Agogo Hi Agogo Lo Cabasa1 Maracas Slur FxGun2 FxGun1 Scratch H Scratch Down Hi Q1 Hi Q1 Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd T9-5 Sd T9-5 Sd T9-5 Sd T9-5 Bd Human1 Sd D&B1 Acoustic Bass FX03 DigiOsc4 DigiOsc3 Sd Break1 Bd Jungle1 Bd Break Heavy Bd Jungle2 Sd Mid Rim Sd Jungle3 Sd Jungle3 Sd Piccolo Rim1 Sd Roll Mono Sd Wood Sd R&B2 Sd D&B2 Bd Jungle2 Sd JungleFX Bd T8-1 Bd Jungle1 Sd Hip Rim4 Sd Jungle2 Sd JungleFX Sd Jungle3 Tom Floor L HH Closed Rock Sw R Tom Low L HH Pedal Rock Sw R Tom Mid R HH Open Rock Sw R Tom Mid R Tom Hi R Crash2 L Tom Hi R Ride2 L China2 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 L Vibraslap Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Tabla Nah Tabla Bya Agogo Lo Agogo Lo Cabasa1 Maracas Slur Timbale Hi1 Timbale Lo1 Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 2 Sleigh Bell Wind Chime HH Reverse D&B RainLoop Scratch H Scratch L Bd BlpHd Sd Syn IndusTom Pandeiro Open Ripper TelRing Scratch Lo R Tape Rewind Bd AnCR Bd Blip Sd T8-5 Clap AnSm Sd Hip Rim2 Sd Roll Mono Sd T8-1 Sd AnCR Sd T8-2 Bd T8-1 Sd T8-4 Bd Blip Bd T8-2 Sd T8 Rim Sd T8-3 Clap T9 Clap AnSm Tom Syn2 HH Closed T8-2 Tom Syn2 HH Open T8-1 Tom Syn2 HH Open T8-2 Tom Syn2 Tom Syn2 Crash T9 Tom Syn2 Ride T9 Crash3 Crash Syn Crash T8 Crash3 Sd Rim SE Crash2 L Cowbell T8 Crash Syn Tom T8 Tom T8 Tom T8 Tom T8 Tom T8 Ooh Feedback Door Squeak Heart Beat Bomb HH Open Lo-Fi Shaker 1 Zap2 Zap1 Scratch H Scratch L Hi Q1 Hi Q1 Hi Q2 Scratch L Hit Orchestra2 HH Closed FX HH Open FX Analog Shaker Analog Shaker TurnTable Sd Break Roll FM Bass5 Mind Bell Sd Break2 Bd Rock2 L Radio Noise Ripper Noise Burst Scratch Spin Bell Choir Hi-Lo Radio Noise Bd Human1 Scratch Vo F Hit Uhh Scratch Vo R Sd JungleFX Sd RockRoll St Sd Break1 Sd AnCR Sd T9-1 Bd Sustain Sd Hip Stick1 Bd Human2 Bd Human2 Sd Wood Sd Human1 Sd Garg H Sd Human2 Bd Human1 HH Open Human Bd Human1 HH Open Human Bd Human1 HH Open Human Bd Human1 Bd Human1 Noise Burst Bd Human1 Crash T8 China2 Ride Cup Mono Scratch Sd R Splash R Vox Drum Crash4 Electric Tambourine2 Stab Aah Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Agogo Lo Agogo Lo Cabasa1 Maracas Slur FxGun2 Bomb Scratch H Scratch Down Scratch Lo F Scratch Lo R Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Shaker 2 Sleigh Bell TurnTable Sd Rock Rim1 R Helicopter Stab Hard Sd Gate1 Bd Rock1 L Scratch Down Bd AnSm Sonar1 Wataiko Wataiko HH Hit Stab Organ2 Bd Distortion3 Sd Wood Sd Gate1 HandClap L Sd Gate2 Sd Roll Mono Sd ClapAna Sd Hip3 Ripper Bd Distortion RM Sd T8 Rim Bd Distortion2 Bd Distortion1 Sd Mid Rim Sd Dist Noise Burst Sd FX2 Tom Dist Lo HH Closed T9 Tom Dist Lo HH Open T9 Tom Dist Lo HH Open T9 Tom Dist Hi Tom Dist Hi Crash T9 Tom Dist Hi Ride T9 China2 Ride Cup Mono Tambourine AnCR Crash3 Cowbell T8 Crash2 L Vibraslap Ride1 L Sd Timbr Sd Timbr Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Bd +HH Bd +HH Glass Glass Cabasa1 Maracas Slur FxGun2 FxGun1 Scratch H Scratch Down Hi Q1 Hi Q1 Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd Dist Sd FM Sd FM Rim Sd Clappy Bd FxHammer IndusTom Bd D&B1 IndusTom Bd Room3 IndusTom Bd T9-4 Sd Hammer Analog Click Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Normal 3St Stick1 Bd Rock2 L Sd Tight 1St Bd Break1 Bd Rock2 L Sd Rock Stick2 R Sd PowerA 3L HandClap L Sd Piccolo Rim1 Tom Ambience Lo HH Closed Rock 2R Tom Ambience Lo HH Pedal Rock R Tom Ambience Lo HH Open Rock R Tom Ambience Hi Tom Ambience Hi Crash2 L Tom Ambience Hi Ride2 L China L Ride Cup2 L Tambourine1 Sw Splash R Mambo Bl2 Crash2 L Vibraslap Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Scratch Combi2 Scratch Hi R Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Piccolo Sd Heavy Sd ClapAna Sd Piccolo Rim1 Bd Room2 Sd Mid Bd Room3 Sd Mid Rim Bd Gate Sd Gate1 Bd FX Gate Bd D&B1 Bd Blip Hi Q2 Sd Piccolo Rim2 Hi Q1 Sd AnCR Tom Syn1 HH Closed Syn Tom Syn1 HH Closed FX Tom Syn2 HH Open Syn Bd AnCR Bd T9-1 Sd Hip Stick3 Sd Jungle1 Clap T9 Sd Jungle1 Tom T8 HH Closed T8-2 Tom T8 HH Open T8-1 Tom T8 HH Open T8-2 Tom T8 Tom T8 Crash Syn Tom T8 Ride T9 Crash3 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell T8 Crash2 L Vibraslap Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Glass Glass Cabasa1 MaracasT8 Bird Engine Room L Electric Perc14 Gunshot Clave T8 Wood Block Wood Block Tambourine AnCR Cowbell AnCR Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd T8-5 Sonar1 Sd T8-5 Sonar1 Bubble Sd T8-5 Bubble Sd T8-5 Eery Sd T8-5 FX Chime FX Chime Bd Hard Long Bd T9-1 Bd Slimy Bd AnaHard Bd Distortion4 Bd ElecComp3 Bd Hip Deep Bd FxHammer Bd T9-3 Bd T8-3 Bd Sustain Bd R&B2 Bd T9 Hd1 Bd Slimy Sd T8-1 Sd T8 Rim Sd Clappy Sd Blues 3St HH Open 2R HH Closed T8-2 HH Open 2R HH Open T8-2 HH Open 2R HH Open T8-2 Clap AnSm Crash2 St Crash T9 Ride1 L Ride T9 Analog Click Clave T8 Clap AnSm Clap T9 Analog Shaker Conga T8 Conga T8 Sd AnCR Sd RockRoll St Agogo Hi China L Hi Q2 Hi Q1 Zap2 Zap1 Zap2 Door Squeak Scratch Combi3 AcousticGtr FX27 R Distortion FX Scratch Hi F Wurli EP Med+ Wurli EP Med+ Wurli EP Med+ Resonance X-Fade Sonar1 Vox Drum Vox Drum Vox Drum HH Open Syn HH Closed Syn Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Bd HipHop8 Bd T9-1 Bd Blip Bd Break Hard Bd ElecFX1 Bd ElecGate Bd ElecFX2 Bd Distortion2 Bd ThickSkin Bd ElecComp2 Bd Elec2 Bd ElecClick Bd ElecClick Sd T9Rim Sd Elec12 Clap T9 Sd Elec8 Tom T9 Lo HH Closed T9 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Hi Tom T9 Hi Crash T9 Tom T9 Hi Ride T9 Crash T9 Ride T9 Electric Tambourine2 Crash T9 Electric Cowbell Crash T9 Electric Tambourine1 Ride T9 Electric Perc5 Electric Perc4 Electric Perc12 Electric Perc6 Electric Perc1 Bell FX1 Bell FX2 Scratch Attack Scratch R&B Cabasa1-2 Maracas Slur Electric Perc14 Electric Perc15 Shaker Hip2 Electric Perc3 Electric Perc7 Electric Perc8 Electric Perc9 Electric Perc10 Electric Perc13 Triangle Mute Triangle Open Electric Maracas Electric Triangle Electric Perc11 Sd Elec1 Sd Elec9 Sd Elec10 Sd Elec11 Sd Elec2 Sd Elec3 Sd Elec4 Sd Elec5 Sd Elec6 Sd Elec7 Electric Clap1 Electric Clap2 Analog Click Sd Brush Med L Sd Brush Swir R Sd Brush Med L Sd Brush SwirAtt R Sd Orch Roll St Castanet Sd Orch1 S L Stick1 Bd Gran Casa Mute L Sd Orch1 H L Bd Gran Casa Mute L Bd Gran Casa Open L Sd Closed Rim R Sd Orch1 H L HandClap L Sd Orch2 H L Timpani Hard St HH Closed 1-4R Timpani Hard St HH Pedal 3 R Timpani Hard St HH Open 2R Timpani Hard St Timpani Hard St Hand Cymbal Open R Timpani Hard St Ride1 L China L Ride Cup1 L Tambourine1 Hit Hand Cymbal Mute R Cowbell2 Crash1 L Vibraslap Ride2 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Bd Hard Long Bd TekPower Bd Distortion5 Bd Distortion3 Bd Distortion1 Bd D&B1 Bd Blip Bd AnSm Bd T8-2 Bd T8-3 Bd T9 Hd3 Bd T9-2 Bd T9-3 Sd T9Rim Sd T9-1 Clap T9 Sd T9-4 Tom T9 Lo HH Closed T9 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Lo Tom T9 Lo Crash T9 Conga T8 Ride T9 Conga T8 Analog Click Clave T8 MaracasT8 Tambourine AnCR Analog Shaker Cowbell T8 Cowbell AnCR Sd T8-1 Sd T8-2 Sd T8-3 Sd AnCR Sd Jungle1 Sd D&B1 Sd Hip1 Sd R&B1 Sd R&B2 Sd Hip1 Sd Wood Sd Timbr HH Closed T8-1 HH Open T8-1 HH Closed T8-2 HH Open T8-2 HH Pedal 3 R HH Open 2R HH Open 2R HH Closed Lo-Fi HH Open Lo-Fi HH Closed Syn HH Open Syn Analog Shaker Tambourine RX5 Tambourine RX5 Electric Cowbell Conga T8 Electric Triangle Clave T8 Analog Shaker Electric Clap1 Electric Clap2 Tambourine AnCR Cowbell T8 Analog Click Cowbell RX11 Bd Normal 2L Sd Rock Stick1 R Sd Rock Open2 R Sd RockRoll St Sd Rock Open1 R Tom Low L HH Closed Foot R Tom Low L HH Pedal 3 R Tom Mid R HH Open 2R Tom Hi R Bd Rock2 L Sd Rock Stick1 R Sd Rock Open2 R HandClap L Sd Rock Mute2 R Tom Rock Low L HH Closed Rock Sw R Tom Rock Low L HH Pedal Rock Sw R Tom Rock Mid R HH Open Rock Sw R Tom Rock Mid R Tom Rock Hi R Crash2 L Tom Rock Hi R Ride1 L China L Ride Cup1 L Tambourine1 Sw Splash R Cowbell1 Crash2 L Shaker 1 Ride1 L Bd Rock1 L Sd Rock Stick2 R Sd Rock Open1 R Clap T9 Sd Rock Mute1 R Tom Rock Low L HH Closed Rock Sw R Tom Rock Low L HH Pedal Rock R Tom Rock Mid R HH Half Open Rock R Tom Rock Mid R Tom Rock Hi R Crash2 L Tom Rock Hi R Ride2 L China L Ride Cup1 L Tambourine1 Hit Splash R Mambo Bl2 Crash2 L Shekere Down Ride2 L Bd Funk 2 R Sd Rock Stick1 R Sd Rock Rim1 R Sd Rock Flam R Sd Rock Rim2 R Tom Floor L HH Closed 4R Tom Low L HH Pedal 3 R Tom Mid R HH Open2 R Tom Mid R Tom Hi R Bd T8-3 Sd T8 Rim Sd HipHop11 Clap T9 Sd HipHop9 Bd T8-3 HH Closed Tek Bd T8-3 HH Open Tek Bd T8-3 HH Open Tek Bd T8-3 Bd Hip Deep Sd Hip Stick4 Sd Hip Rim1 HipHop clap1 Sd Hip2 Tom T8 HH Closed T8-2 Tom T8 HH Open AnCR Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash T9 Tom T8 Crash Syn Crash Syn Crash T8 Tambourine RX5 Splash R Cowbell T8 Crash T9 Cowbell AnCR Ride T9 Bd Hip Deep Sd Hip Stick5 Sd Hip Rim2 HipHop clap2 Sd Hip1 Tom T9 Lo HH Closed RX5 Tom T9 Lo HH Closed AnCR Tom T9 Lo HH Open T8-2 Tom T9 Lo Tom T9 Hi Crash2 R Tom T9 Hi Crash T8 Crash Syn Crash T8 Tambourine1 Hit China L Cowbell RX11 Crash2 L Cowbell1 Ride2 L Bd Hip Deep Sd HipHop Rim4 Sd HipHop2 HipHop clap2 Sd Hip Rim5 Tom Dist Lo HH Closed Hip Tom Dist Lo HH Pedal Hip Tom Dist Hi HH Open Hip Tom Dist Hi Tom Dist Hi Bd T9-1 Bd T9-1 Bd T9-2 Bd T9-3 Bd T9-3 Bd T9-4 Bd T9 Hd1 Bd T9 Hd2 Bd T9 Hd3 Bd T9 Hd3 Bd Hard Long Bd AnCR Bd T9-1 Bd T9-2 Bd T9-3 Bd T9-4 Bd T9 Hd1 Bd T9 Hd2 Bd T9 Hd3 Bd T8-1 Bd T8-2 Bd T8-3 Bd Elec1 Bd AnCR Bd Human1 Bd Elec1 Bd Blip Bd BlpHd Bd Elec3 Bd ElecFX1 Bd ElecFX2 Bd ElecGate Bd ElecClick Bd ElecPower1 Bd R&B2 Bd Hip Deep Bd Break Deep Bd Break1 Bd ElecPower2 Bd Break Heavy Bd Break Hard Bd Distortion1 Bd Distortion2 Bd Distortion3 Bd Distortion4 Bd Distortion5 Bd Elec2 Bd ElecComp1 Bd FxHammer Bd ZapHard Bd ZapMid Bd Sustain Bd TekPower Bd Hard Long Bd Analog Tight Bd AnaHard Bd ElecComp2 Bd ElecComp3 Bd Slimy Bd Gate Bd FX Gate Bd HipHop1 Bd HipHop2 Bd HipHop3 Bd HipHop4 Bd HipHop5 Bd HipHop6 Bd HipHop7 Bd HipHop8 Bd HipHop9 Bd HipHop10 Bd Hard Long Bd Analog Tight Wind Chime Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Normal 3St Stick1 Bd T9-2 Sd Basic St2-3 Bd High L Bd Normal 2L Sd Funk St2-3 Sd Bld St3-4 HandClap L Sd Soul St3-4 Tom Rock Low L HH Closed 1-4R Tom Rock Mid R HH Pedal 3 R Tom Floor L HH Open 2R Tom Low L Tom Mid R Crash2 St Tom Hi R HH Open 2R Reverse Cymbal Timbale Lo1-2 Conga Hi Open 2 Timbale Hi1-3 Conga Hi Slp Open Tambourine1 Sw Tumba Open Cowbell1 Clave Bongo Hi 1Finger 1-2 Wood Block Bongo Lo 3Finger 1-2 Wood Block Dohol Low Cabasa1-2 Dohol High Shaker 1 Castanet Maracas Sw Castanet Twaisat3 Twaisat Twaisat2 Twaisat4 Twaisat4 Small Tabla Dom Barasim Dom Small Tabla Snap Barasim Sak Small Tabla Sak Barasim Tak Small Tabla Tak Big Tabla Dom Segal Dom Big Tabla Tak Segal Tak Big Tabla Sak Tabla Dom Tabla Tak1 Tabla Tremolo Tabla Tak2 Tabla Tik1 Tabla Sak1 Tabla Tik3 Tabla Tik2 Tabla Tik2 Wataiko Djembe Edge Djembe Mute Djembe Open Djembe Combi Tabla Bya Tabla Nah Tabla Mute Tabla Combi Small Tabla Dom Small Tabla Dom Bd Hard Long Bd T9-1 Sd T9-2 Electric Clap1 Sd Strong L4 Tom Dist Lo HH Closed T9 Tom Dist Hi HH Closed T8-1 Tom Syn1 HH Open T9 Tom Syn2 Sd Hip1 Crash4 Sd D&B4 HH Closed Break Reverse Cymbal Timbale Lo1-2 Conga Hi Open 2 Timbale Hi1-3 Conga Hi Slp Open Tambourine1 Sw Tumba Open Cowbell1 Clave Bongo Hi 1Finger 1-2 Wood Block Bongo Lo 3Finger 1-2 Wood Block Dohol Low Cabasa1-2 Dohol High Shaker 1 Castanet Maracas Sw Castanet Twaisat3 Twaisat Twaisat2 Twaisat4 Twaisat4 Small Tabla Dom Barasim Dom Small Tabla Snap Barasim Sak Small Tabla Sak Barasim Tak Small Tabla Tak Big Tabla Dom Segal Dom Big Tabla Tak Segal Tak Big Tabla Sak Tabla Dom Tabla Tak1 Tabla Tremolo Tabla Tak2 Tabla Tik1 Tabla Sak1 Tabla Tik3 Tabla Tik2 Sd FX1 Sd FX2 Sd FX3 Sd FX4 Sd Dist Sd Cut Sd Garg H Sd Garg L Sd FM Sd Syn Sd Hammer Sd Lo-Fi Sd T9-1 Sd T9-2 Sd T9-3 Sd T9-4 Sd T9-5 Sd T9Gate Sd T8-1 Sd T8-2 Sd T8-3 Sd T8-4 Sd T8-5 Sd AnSm Sd AnCR Sd Human1 Sd Jungle1 Sd Jungle2 Sd Jungle3 Sd JungleFX Sd D&B1 Sd D&B2 Sd D&B3 Sd D&B4 Sd R&B1 Sd R&B1 Rim Sd R&B2 Sd Hip1 Sd Hip2 Sd Hip3 Sd Hip Gate Sd Hip Rim1 Sd Hip Rim2 Sd Hip Rim3 Sd Hip Rim4 Sd Hip Rim5 Sd Break2 Sd Break Heavy Sd Elec1 Sd Elec2 Sd Elec3 Sd Elec4 Sd Elec5 Sd Elec6 Sd Elec7 Sd Elec8 Sd Elec9 Sd Elec10 Sd Elec11 Sd Elec12 Sd Elec1 Sd T8-2 Sd Elec3 Sd Elec4 Sd Elec5 Sd Elec6 Sd Elec7 Sd Elec1 Sd Elec8 Sd Elec10 Sd Elec11 Sd Elec12 Sd T9-3 Bd Elec2 Scratch Stop Sd Brush Swir L Sd Brush Med R Sd Brush Swir St Sd Roll Mono FM Bass6 Seashore Electric Perc4 Bd ElecComp3 Sd Strong R3 Bd Elec2 Bd Slimy Timbale Paila H Sd Piccolo Clap AnSm Sd Elec10 Mg Bass2 Hi Q2 Mg Bass2 Electric Perc7 Tom T8 HH Open T8-2 Tom T8 Tom T8 Crash3 Tom T8 Ride Cup Mono Ride Cup Mono Ride Cup Mono Scratch Sd F Ride Cup1 L Electric Perc7 Crash3 Hi-Lo China2 Ring Mod Electric Perc8 Ring Mod Electric Perc7 Electric Perc11 Paila H Paila H Hop FX2 Hop FX2 Scratch H Scratch Hi F Zap2 LFO Seq LFO Seq Mini Bass3a Quiek Digi Breath DigiHeli Electric Perc7 FM Organ1 Tibetan Bowl Tibetan Bowl Segal Dom Engine Room L Electric Perc8 Steel Mute L Mini Blip2 Spectrum2 Mini Blip5 Noise Bd D&B1 Spectrum2 SabreTone Electric Perc14 Electric Perc8 DigiHeli Wind Chime Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Normal 3St Stick1 Bd Funk 2 R Sd Basic St2-3 Bd High L Bd Normal 2L Sd Closed Rim St Sd Normal St3-4 Clap T9 Sd Basic St2-3 Tom Floor L HH Closed 1-4R Tom Low L HH Pedal 3 R Tom Low L HH Open 2R Tom Mid R Tom Mid R Crash2 L Tom Hi R Ride1 L China L Ride Cup1 L Tambourine1 Sw Splash R Cowbell2 Crash2 L Vibraslap Ride1 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Blues 3L Sd Tight St3-4 Sd BluesHeart 3R Sd Hip St1-2 Bd Low L Sd LdwHMono Bd R&B1 Sd Heavy Bd Room1 Sd Ambience Bd Funk Son L Bd Room2 Clave Cajon Kick Cajon Slap Cajon Tip 1-2 Clave Wood Block HandClap L Ride2 L HipHop Snap2 Castanet Conga Hi Tip Conga Hi Heel Conga Hi Open 1-2 Conga Hi Mute Conga Hi Slp Open Conga Hi Slap Conga Hi SlapMute1-2 Conga Lo Tip Conga Lo Heel Conga Lo Open 1-2 Conga Lo Mute Conga Lo SlapOpen Conga Lo SlapOpen Conga Lo Open 1 Bongo Hi 1Finger 1-2 Bongo Hi 3Finger 1-2 Bongo Hi Rim Bongo Hi Tip Bongo Hi Heel Bongo Hi Slap Bongo Lo 1Finger 1-2 Bongo Lo 3Finger 1-2 Bongo Lo Rim Bongo Lo Tip Bongo Lo 3Finger 1-2 Bongo Hi Slap Timbale Lo1-2 Quinto1 Slap Quinto1 Open Conga Hi Tip Tumba Open Paila L Timbale Hi1-3 Pandeiro Open Pandeiro Thump Pandeiro Tip Pandeiro Heel Paila H Cowbell2 Cowbell2 Agogo Hi Agogo Hi Guiro Short Guiro Long MetalGuiro Short1-2 MetalGuiro Long Tambourine1 Sw Tmbourim Open Tmbourim Mute Tmbourim Finger Maracas Sw Shaker 1/3 Cabasa1-2 Cuica Lo Cuica Hi Cowbell1 Mambo Body Shekere Down Shekere Sap Triangle Mute Triangle Open Whistle Wind Chime Wataiko Udu High Djembe Open Quinto1 Open Udu Low Udu Finger Udu High Tabla Bya Tmbourim Open Tabla Mute Tabla Nah Tabla Mute Timpani Hard St Hit Uhh Bongo Hi 1Finger 1-2 Clap T9 Triangle Mute Triangle Open Castanet Castanet Castanet Guiro Long Guiro Swp Guiro Short Vibraslap Clave Clave T8 Cuica Lo Cuica Hi Timbale Lo1-2 Wood Block Timbale Lo2 Wood Block Timbale Hi1-3 ChaCha Timbale Hi2 Timbale Paila L Timbale Paila L Timbale Paila H Mambo Bl1 Bongo Lo 3Finger 1-2 Bongo Lo 3Finger 1-2 Handbell L Bongo Hi 1Finger 1-2 Handbell H Bongo Hi 1Finger 1-2 Agogo Lo Agogo Hi Conga Hi Heel Conga Hi Tip Tumba Slap Maracas Slur Tumba Open Conga Hi Heel Conga Hi Tip Conga Hi Slp Open Shaker 1 Conga Hi Open 2 Cabasa1 Cabasa1-2 Conga Hi Heel Conga Hi Tip Quinto1 Slap Shaker 1 Quinto2 Open Caxixi1-2 Tambourine1 Shake Tambourine1 Hit Tambourine1 Sw Tambourine1 Hit Sleigh Bell Whistle Whistle Bd L Son L Sd Rock Stick2 R Sd Funk 3St Crash2 St Splash R Tom Low L HH Closed 1-4R Tom Low L HH Pedal 3 R Tom Mid R HH Open 2R Tom Hi R Conga Hi Tip Conga Hi Heel Tumba Slap Clave Tumba Open Conga Hi Tip Conga Hi Heel Conga Hi Slp Open Handbell L Conga Hi Open 2 Handbell H Mambo Bl1 Conga Hi Tip Conga Hi Heel Quinto1 Slap ChaCha Quinto1 Open Bongo Lo 3Finger 1-2 Bongo Hi 1Finger 1-2 Bongo Hi 3Finger 1-2 Timbale Lo1 Timbale Paila L Timbale Hi1 Timbale Paila H Wood Block Wood Block Shekere Down Shekere Up Shekere Sap Maracas Slur Maracas Staccato Guiro Long Guiro Swp Guiro Short Surdo Mute Surdo Open Pandeiro Tip Pandeiro Heel Pandeiro Thump Pandeiro Open Tmbourim Finger Tmbourim Open Tmbourim Mute Agogo Lo Tap Agogo Lo Agogo Hi Triangle Open Triangle Mute Shaker 2 Shaker 1 Shaker 3 Cabasa1 Cabasa2 Caxixi1 Caxixi2 Cuica Lo Cuica Hi Whistle Whistle Vibraslap Wind Chime Electric Bass FX 01 Zap2 HH Pedal D&B HandClap L Scratch Vo F IndusTom Sd Wood IndusTom Sd ClapAna IndusTom Sd FM Rim Bd FX Gate Bd FxHammer Conga Hi Slp Open Sd FM HandClap L Sd Timbr Tom Syn2 HH Closed Syn Tom Syn2 HH Open Syn Tom Syn2 HH Open Syn Tom Syn2 Tom Syn2 HH Open Mono Sonar1 HH Open T8-1 HH Open Mono Ride Cup2 L Sleigh Bell HH Open T8-2 Resonance X-Fade Crash2 L RainLoop Ride Cup2 L AirHose L AirHose L Udu High Mg Bass2 Mg Bass3 Acoustic Bass FX06 Acoustic Bass FX06 Agogo Lo Agogo Lo HH Open Human Vox Hit Rev Hi Q1 Hi Q1 LFO Seq LFO Seq BellAir HH Hit HH Hit Ooh Feedback Ooh Feedback Mind Bell Glass Swarm FxGun2 Lektronica Distortion FX Gunshot Police Ripper Distortion FX Tape Rewind FxGun1 Bomb Zap2 Noise Burst Stab Giant Stab Orchestra Clave T8 Sd Hip Stick6 R Guiro Long Sd Hip Stick6 R Guiro Long AcousticGtr FX13 R Castanet Electric Bass FX 11 Stick1 Electric Bass FX 08 Electric Bass FX 07 Tom T9 Hi Electric Bass FX 17 Sd Closed Rim3 Stick2 Guiro Long Stick2 Surdo Open Sd AnSm Rim Surdo Open Clap AnSm Surdo Open Vibraslap Surdo Open Surdo Open Seashore Surdo Open Sd Rock Stick2 R Applause Sd Rock Stick1 R Tambourine1 Shake Seashore Wood Block Applause Helicopter Sd Rock Stick2 R Conga Hi Slp Open Conga Hi Slp Open Castanet Castanet Wood Block Timbale Paila L Timbale Paila L Stick1 Stick2 Caxixi1-2 Caxixi2 Door Squeak Door Squeak Guiro Short Guiro Long Clave Wood Block Wood Block Door Squeak Door Squeak Wood Block Clave Door Squeak HandClap L ChaCha Sd Rock Open2 R Bd Jungle3 Quinto1 Slap Bd Room2 Bd H Son L Sd Heavy Surdo Open Sd Rock Stick1 R Bd Slimy Sd Gate1 Bd Funk Son L Bd Gate Cowbell T8 HH Closed 4R Ride Cup2 L HH Open Syn Ride Cup2 L Ride Cup1 L FM Metal2 Ride1 L Electric Perc7 Cowbell2 HH Closed 1R ChaCha Cowbell2 HH Closed Syn HH Open Tek HH Closed T8-2 HH Closed RX5 Cowbell2 HH Open T8-1 Cowbell2 HH Closed Syn Cowbell2 HH Open Syn Cowbell2 Cowbell2 HH Open FX Cowbell2 Ride Cup1 L HH Open AnCR Ride Cup1 L Tambourine1 Sw HH Open FX Mambo Bl2 HH Open FX Sleigh Bell Ride Mono Brite Piano Brite Piano Tubular Bell Bell Brass Bell Timbale Paila H Timbale Paila L Bell Bell HH Closed 1R HH Open AnCR BellAir FX Chime HH Open Rock R Lektronica Agogo Lo Cowbell1 Cowbell2 Hi-Lo Hi-Lo Triangle Mute Triangle Open FX Chime Sleigh Bell Wind Chime Small Bell Small Bell Brass Bell Hibashi Brite Piano Sd LdwHMono Brite Piano Sd BreakRim Gamelan2 Udu Finger Finger Cymbal Bell Tabla Mute Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St AcousticGtr FX29 R Stick2 Pandeiro Thump Tabla Mute Electric Bass FX 16 AcousticGtr FX17 R Tabla Combi Wataiko Quinto1 Slap Sd Brush Med St HandClap L Sd Funk Ay L Udu Low Tabla Mute Udu Low Tabla Bya Udu High Tabla Nah Udu High Udu Finger Ride2 L Udu Finger Ride Cup1 L Ride Cup Mono2 Ride Cup1 L Tambourine1 Hit Ride Cup1 L Cowbell1 Ride Mono Vibraslap Ride2 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Tmbourim Finger Tmbourim Finger HandClap L HandClap L Guiro Long Guiro Long Udu Finger Tabla Bya Conga Hi Tip Udu High Udu High Udu Finger Finger Cymbal Tabla Mute Sleigh Bell Wind Chime Conga Hi Slp Open Tumba Tip Tumba Tip Electric Bass FX 08 Tumba Tip Electric Bass FX 01 Tumba Open Electric Bass FX 06 AcousticGtr FX04 R Udu Low Djembe Combi Tumba Slap Scratch H Scratch L Scratch Down Scratch Hi F Scratch Hi R Scratch Lo F Scratch Lo R Scratch Bd F Scratch Bd R Scratch Sd F Scratch Sd R Scratch Vo F Scratch H Scratch L Scratch Down Scratch Hi F Scratch Hi R Scratch Lo F Scratch Lo R Scratch Bd F Scratch Bd R Scratch Sd F Scratch Sd R Scratch Vo F Scratch Vo R Scratch Stop Scratch Spin Scratch Hip Scratch Hip2 Scratch Combi Scratch Combi2 Scratch Combi3 Scratch Combi4 Scratch H Scratch L Scratch Down Scratch Hi F Scratch Hi R Scratch Lo F Scratch Lo R Scratch Bd F Scratch Bd R Scratch Sd F Scratch Sd R Scratch Vo F Scratch Vo R Scratch Stop Scratch Spin Scratch Hip Scratch Hip2 Scratch Combi Scratch Combi2 Scratch Combi3 Scratch Combi4 Scratch H Scratch L Scratch Down Scratch Hi F Scratch Hi R Scratch Lo F Scratch Lo R Scratch Bd F Scratch Bd R Scratch Sd F Scratch Sd R Scratch Vo F Scratch Vo R Scratch Stop Scratch Spin Scratch Hip Scratch Hip2 Scratch Combi Scratch Combi2 Mini Blip2 Mini Blip1 Mini Blip3 Mini Blip4 Mini Blip5 Mini Blip6 Bell FX1 Bell FX2 Electric Perc4 Electric Perc6 Electric Perc15 Electric Perc5 Electric Perc2 Electric Perc3 Electric Perc7 Electric Perc8 Electric Perc9 Electric Perc10 Electric Perc14 Electric Perc13 Neo Seq Em-3 Spectrum1 Spectrum3 Spectrum2 Whisper Seq Bb Digi Voice Mod Moving Chime L Engine Room L Raspberry DigiHeli Hop FX2 Willow L Anemone R Magnitude R Radio Noise Quiek Stab Giant HH Hit Stab Hard Stab Organ2 Stab Aah Hit Uhh Hit Orchestra2 Hit Orchestra1 Hit Brass Orchestra Hit1 R Orchestra Hit2 L Brass Sect Fall Dist/Overdrive FX Distortion FX ElectricGtr FX23 Acoustic Bass FX04 Acoustic Bass FX05 Acoustic Bass FX01 Scratch H Scratch L Scratch Down Scratch Hi F Scratch Stop Tape Rewind Ripper Noise Burst Zap1 Zap2 Hi Q1 Hi Q2 Bomb FxGun1 Motion Beam Birch L Tri Wave FM Bass2 FM Metal2 Fat Saw Mn Scratch Attack Spectrum2 3o3 Saw2 TurnTable Sd Roll Mono FM Bass6 Tape Rewind Electric Perc5 Bd ElecComp2 Sd Normal St3-4 Bd Slimy Bd Elec2 Mini Bass3a Sd BluesHeart St2-3 3o3 Square Sd SonA 3L Tom Dist Hi DigiHeli Tom Dist Hi Electric Perc2 Tom Dist Hi HH Open T8-2 Tom Dist Hi Tom Dist Hi Crash3 Tom Dist Hi Ride Cup1 L Ride2 L Ride Cup Mono2 Scratch Hi F Splash R Mini Bass1 Crash1 L Engine Room L Ride Cup1 L Sync LFO P5 SawDown 0 dg OB Strings Sd Normal 3St Electric Perc11 PWM Slow LFO Seq BigSyn BigSyn Hit Orchestra2 Twaisat4 Digi 03 Digi 03 Digi 01 Digi 01 Sonar1 Stab Hard Engine Room L Chatter Hoo Mondira Mondira Rotor Raspberry DigiHeli 3o3 Square Hop FX2 DigiBell3 Vox Hit Rev Hi Q1 Tape Rewind FxGun2 FxGun1 Thunder Police Bomb Bomb Switch General Midi mode on: Ignored by the MX49/MX61 f0 7e 7f 09 01 f7 Request Device Number Code from the instrument. The instrument answers with a "Identity Reply" and returns the Device ID 0x47 0x06. 0xF0 0x7E 0x0? 0x06 0x01 0xF7 0x0?: Device number (any number) f0 7e 00 06 01 f7 Reply message to an "Identity Request" 0xF0 0x7E 0x7F 0x06 0x02 0x43 0x00 0x41 dd dd 0x00 0x00 0x00 0x7F 0xF7 dd dd: Device Number Code (always 0x47 0x06) F0 7E 7F 06 02 43 00 41 47 06 00 00 00 7F F7 Overall volume of all parts 0xF0 0x7F 0x7F 0x04 0x01 ll mm 0xF7 ll: Volume LSB (Ignored) mm: Volume MSB f0 7f 7f 04 01 00 7f f7 Fine control of all voice or performance parameters 0xF0 0x43 0x1? 0x7F 0x17 ah am al dd ... 0xF7 0x1?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) ah: Address High am: Address Mid al: Address Low dd: Parameter data (variable length) f0 43 10 f7 17 00 00 00 00 f7 Bulk dump of all settings (Data List p. 57ff) 0xF0 0x43 0x0? 0x7F 0x17 bh bl ah am al dd ... cc 0xF7 0x0?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) bh: Byte Count MSB bl: Byte Count LSB ah: Address High am: Address Mid al: Address Low dd: Parameter data (variable length) cc: Checksum f0 43 00 7f 17 00 00 00 00 00 00 f7 Request bulk dump (Data List p. 57ff) 0xF0 0x43 0x2? 0x7F 0x17 ah am al 0xF7 0x2?: Device Number ah: Address High am: Address Mid al: Address Low f0 43 20 7f 17 00 00 00 f7 Request parameter value (Data List p. 57 left side) 0xF0 0x43 0x3? 0x7F 0x17 ah am al 0xF7 0x3?: Device Number ah: Address High am: Address Mid al: Address Low f0 43 30 7f 17 00 00 00 f7 muse-4.0.0/src/share/instruments/Yamaha-Motif XS.idf000066400000000000000000020741311404105311000222200ustar00rootroot00000000000000 Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Normal 1St Stick1 Bd Funk St1-2 Sd Basic St2-3 Bd High St Bd Normal St1-2 Sd Closed Rim St Sd Normal St1-4 Clap T9 Sd Basic St1-2 Tom Floor St HH Closed 1-4St Tom Low St HH Pedal 3 St Tom Low St HH Open 2St Tom Mid St Tom Mid St Crash2 St Tom Hi St Ride1 St China St Ride Cup1 St Tambourine1 Sw Splash St Cowbell2 Crash2 RL Vibraslap Ride1 RL Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Blues L1-3 Sd Tight St1-4 Sd BluesHeart R1-3 Sd Hip St1-2 Bd Low St Sd LdwHMono Bd R&B1 Sd Heavy Bd Room1 Sd Ambience Bd Funk Son St Bd Room2 Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd PowerD 1St Stick1 Bd PowerB 1-2St Sd PowerD 5St Bd PowerA 1-2St Bd Jazz1-2St Sd PowerB SSt Sd PowerC 1-5St HandClap St Sd PowerD 1-5St Tom Power05 1-2St HH Power ClTip 1-4St Tom Power05 1-2St HH Power ClPedal St Tom Power04 1-2St HH Power Open EdgeSt Tom Power03 1-2St Tom Power03 1-2St Crash2 St Tom Power01 1-2St Ride1 St China St Ride Cup1 St Tambourine1 Sw Splash St Cowbell2 Crash2 RL Vibraslap Ride2 St Bongo Hi 1Finger 1-2 Bongo Lo 1Finger 1-2 Conga Hi SlapMute1-2 Conga Hi Open 1-2 Conga Lo Open 1-2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1/3 Sleigh Bell Wind Chime Sd Bld St1-4 Sd Blues 3St Sd Hip St1-2 Sd Funk St1-3 Sd Soul St1-4 Sd LdwHMono Sd Heavy Sd Tight St1-4 Sd BluesHeart St1-3 Bd Snap 1-2 Bd Tight 1-2 Bd Funk St1-2 Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd PowerA 1St Stick1 Bd PowerB 1-2St Sd PowerA 5St Bd PowerA 1-2St Bd Jazz1-2St Sd PowerB SSt Sd PowerB 1-5St HandClap St Sd PowerA 1-5St Tom Power05 1-2St HH Power ClEgde1-4St Tom Power05 1-2St HH Power ClPedal St Tom Power04 1-2St HH Power Open EdgeSt Tom Power03 1-2St Tom Power03 1-2St Crash2 St Tom Power01 1-2St Ride1 St China St Ride Cup1 St Tambourine1 Sw Splash St Cowbell2 Crash2 RL Vibraslap Ride2 St Bongo Hi 1Finger 1-2 Bongo Lo 1Finger 1-2 Conga Hi SlapMute1-2 Conga Hi Open 1-2 Conga Lo Open 1-2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1/3 Sleigh Bell Wind Chime Sd Bld St1-4 Sd Blues 3St Sd Hip St1-2 Sd Funk St1-3 Sd Soul St1-4 Sd LdwHMono Sd Heavy Sd Tight St1-4 Sd BluesHeart St1-3 Bd Snap 1-2 Bd Tight 1-2 Bd Funk St1-2 Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Normal 1St Stick1 Bd Funk Son St Sd Basic St1-3 Bd Snap 1 Bd H Son St Sd Closed Rim St Sd SonA St1-4 Clap T9 Sd Tight St1-4 Tom Floor St HH Closed Foot St Tom Floor St HH Half Open2 St Tom Low St HH Open2 St Tom Low St Tom Mid St Crash2 RL Tom Hi St Ride1 St China St Ride Cup1 St Tambourine1 Sw Splash St Cowbell2 Crash2 RL Vibraslap Ride1 RL Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Bld St1-4 Sd Blues St1-3 Sd BluesHeart R1-3 Sd Hip St1-2 Sd Normal St1-4 Sd LdwHMono Sd Soul St1-3 Sd Heavy Sd Funk 1St Bd Normal 1St Bd Funk Son St Bd Snap 1-2 Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Normal 1St Stick1 Bd Funk St1-2 Sd Basic St2-3 Bd High St Bd Normal St1-2 Sd Closed Rim St Sd Normal St1-4 Clap T9 Sd Basic St1-2 Tom Floor St HH Closed 1-4St Tom Floor St HH Pedal 3 St Tom Low St HH Open 1-2St Tom Low St Tom Mid St Crash2 RL Tom Hi St Ride1 St China St Ride Cup1 St Tambourine1 Sw Splash St Cowbell2 Crash2 RL Vibraslap Ride1 RL Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Slp Open Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Bld St1-4 Sd Blues L1-3 Sd Hip St1-2 Sd Funk St1-3 Sd Soul St2-4 Sd LdwHMono Sd Heavy Sd Tight St1-4 Sd BluesHeart St1/3 Bd Snap 1-2 Bd Tight 1-2 Bd Funk St1-2 Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Rock Open1 R Stick1 Bd Room1 Sd Rng St4 Bd Jazz2St Bd Room3 Sd Rock Stick2 St Sd Strong St1-4 HipHop clap6 Sd PowerA 1-5St Tom Floor St HH Closed Rock Sw St Tom Low St HH Half Open Rock St Tom Low St HH Open Rock St Tom Mid St Tom Mid St Crash1 RL Tom Hi St Ride2 St China St Ride Cup2 St Tambourine1 Sw Splash St Cowbell2 Crash1 St Vibraslap Ride Cup1 RL Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Blues St1/3 Sd Tight St1-4 Sd BluesHeart R1-3 Sd Tight R1-4 Bd Low St Sd PowerC 4St Bd R&B1 Sd PowerB 1-5St Bd Room1 Sd PowerA 1-5St Bd Funk Son St Bd Room2 Analog Click Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd RockRoll St Castanet Sd Rock Open2 St Stick1 Bd Room3 Sd Rock Rim1 St Bd Rock2 St Bd Rock1 St Sd Rock Stick1 St Sd Rock Open1 St HipHop clap3 Sd Rock Mute1 St Tom Rock Low St HH Closed Rock Sw St Tom Rock Low St HH Pedal Rock Sw St Tom Rock Mid St HH Open Rock Sw St Tom Rock Mid St Tom Rock Hi St Crash1 RL Tom Rock Hi St Ride2 St China St Ride Cup2 St Tambourine RX5 Splash St Cowbell1 Crash1 St Vibraslap Ride2 RL Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Rock Flam St Sd Rock Open2 St Sd Rock Rim2 St Sd Rock Mute2 St Bd Funk 1St Sd RockRollD St Bd H Son St Sd Tight St1-4 Bd Funk Son St Sd SonA 3St Bd Funk R1-2 Bd H Son St Analog Click Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd RockRoll St Castanet Sd Rock Open2 St Stick1 Bd Room1 Sd Rock Rim2 St Bd Ambience L1-2 Bd Rock2 St Sd Rock Stick2 St Sd Rock Open2 St HandClap St Sd Rock Mute2 St Tom Rock Low St HH Closed Rock 2St Tom Rock Low St HH Half Open Rock St Tom Rock Mid St HH Open Rock St Tom Rock Mid St Tom Rock Hi St Crash2 St Tom Rock Hi St Ride2 St China St Ride Cup2 St Tambourine RX5 Splash St Cowbell1 Crash2 RL Vibraslap Ride2 RL Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Rock Flam St Sd Rock Open1 St Sd Rock Rim1 St Sd Rock Mute1 St Bd Rock1 St Sd RockRollD St Bd Funk 2St Sd Tight St1-4 Bd High St Sd SonA 4St Bd L Son St Bd Rock1 St Bd HipHop2 Cabasa2 Sd HipHop9 Scratch Lo R Scratch Vo F Sd RockRollD St Clave Sd HipHop5 Sd HipHop Rim3 Bd HipHop8 Sd HipHop Rim6 Bd HipHop9 Bd HipHop5 Sd HipHop Rim2 Sd HipHop11 HipHop clap2 Sd HipHop2 Tom Floor St HH Closed Hip Tom Low St HH Pedal Hip Tom Mid St HH Open Hip Tom Hi St Tom Hi St Crash4 Tom Hi St Ride2 L Crash2 St Ride Mono Tambourine1 Hit Hand Cymbal Open St Mambo Body China R Handbell L Ride Cup Mono2 Conga Hi Slp Open Conga Lo Open 1 Conga Lo Tip Conga Lo SlapOpen Conga Lo Open 2 Bd HipHop4 HipHop clap3 Agogo Hi Agogo Lo Shaker 2 Shaker 1 Bd HipHop10 Bd HipHop6 Sd HipHop Rim4 Sd HipHop6 HipHop Snap1 Bd HipHop10 Sd HipHop7 HipHop clap1 Paila L Triangle Mute Triangle Open Tambourine1 Sw Shaker 2 Bd HipHop8 Clap T9 Sd T8-1 Sd T8-1 Sd T8-4 Tom T8 HH Closed T8-2 Tom T8 HH Closed T8-2 Tom T8 HH Open T8-2 Tom T8 Crash T8 Bd HipHop3 Hi Q2 Scratch Hip2 Scratch Sd R Scratch Vo R Sd Break Roll Sd T9Rim Sd HipHop2 Sd T8 Rim Bd HipHop9 Sd HipHop Rim1 Bd HipHop6 Bd HipHop5 Sd HipHop Rim2 Sd HipHop1 HipHop clap6 Sd HipHop9 Tom T9 Lo HH Closed Hip Tom T9 Lo HH Pedal Hip Tom T9 Hi HH Open Hip Tom T9 Hi Tom T8 Crash3 Tom T8 Ride T9 Ride T9 Crash Brush Cup St Tambourine RX5 Crash Syn Cowbell AnCR Crash T9 MetalGuiro Long Ride Mono Cajon Tip 1-2 Bongo Lo Tip Tmbourim Finger Tumbo 2 Tumba Heel Paila H Paila L Cowbell T8 Cowbell AnCR Shekere Up Caxixi1 Grain2 St Grain2 L Cajon Tip 1 HipHop clap7 Clave Wood Block Sd HipHop Rim4 HipHop Snap1 HipHop Snap2 Electric Triangle Triangle Open Shaker 1 MetalGuiro Long Wind Chime Sd HipHop Rim4 Sd T8-1 Sd T8-2 Sd T8-4 Tom T8 HH Closed T8-2 Tom T8 HH Closed T8-2 Tom T8 HH Open T8-2 Tom T8 HipHop flex1 Sd Hip Stick2 Vox Hit Rev TurnTable Hit Brass Scratch Sd R Sd LiveRoll R Clave T8 Sd Hip 2R Sd Hip Stick6 R Bd HipHop9 Sd Garg L Bd T8-3 Bd T9-1 Sd HipHop Rim3 Sd HipHop10 HipHop clap6 Sd HipHop1 Tom T8 HH Closed T8-1 Tom T8 HH Open T8-1 Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash4 Tom T8 Ride Brush1 R Crash T8 Ride T9 Tambourine RX5 Splash R Mambo Body Crash T9 Vibraslap Ride T9 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Hip Stick8 Sd Jungle3 HandClap R Sd Hip Rim5 Scratch H Scratch L Scratch Down Scratch Hi R Scratch Bd F Scratch Combi Scratch Vo R Bd Break2 Bd T8-3 Sd T8 Rim Sd Hip1 Clap T9 Sd Syn Sd LiveRoll R Sd Hip Stick4 Sd HipHop2 Sd HipHop Rim6 Bd Hip Deep Sd R&B1 Rim Bd Break Heavy Bd Hip Deep Sd HipHop Rim2 Sd Hip3 HandClap St Sd Lo-Fi Tom Floor R HH Closed Lo-Fi Tom Low L HH Closed Break Tom Low L HH Open Lo-Fi Tom Low L Tom Hi L Crash3 Tom Hi L Ride T9 China2 Ride Cup Mono Tambourine RX5 Crash3 Cowbell RX11 Crash2 R Cowbell T8 Ride T9 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Radio Noise Radio Noise Agogo Lo Agogo Lo Cabasa1 MaracasT8 FxGun2 FxGun1 Scratch Spin Scratch Stop Clave T8 Door Squeak Hi Q2 Scratch Bd F Scratch Bd R Triangle Mute Triangle Open Analog Shaker Tambourine1 Hit Wind Chime Sd Human2 Sd Break Roll Stab Hard Sd Jungle1 Bd Rock1 St Scratch Down Wataiko HH Hit Stab Organ2 Sonar1 TurnTable Police Sd T8 Rim Vox Hit Rev TurnTable Stab Hard Scratch Vo R Sd Roll Mono Clave T8 Sd Hip 1R Sd Hip Stick4 Bd AnCR Sd Hip Rim3 Bd T9-3 Bd Hip Deep Sd Hip Stick5 Sd Hip Gate HipHop clap6 Sd Lo-Fi Tom T8 HH Closed Hip Tom T8 HH Pedal Hip Tom T8 HH Open Hip Tom T8 Tom T8 Crash4 Tom T8 Ride2 R Crash T8 Ride T9 Tambourine RX5 Splash R Mambo Body Crash T9 Vibraslap Ride T9 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Shaker Hip1 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Hip Stick1 Sd Jungle1 HandClap R Sd Break Heavy Scratch L Scratch Down Scratch Hi F Scratch Lo F Scratch Lo R Scratch Bd F Scratch Bd R Bd Break Heavy Sd Hip Stick1 Hi Q1 TurnTable Hit Uhh Scratch Stop Sd LiveRoll L Clave T8 Sd Cut Sd Rock Stick1 R Bd T9-2 Sd Hip Rim1 Bd R&B2 Bd Break Deep Sd Closed Rim3 Sd Hip Rim2 HipHop clap4 Sd R&B1 Conga T8 HH Closed T8-1 Conga T8 HH Closed T8-2 Conga T8 HH Open T8-2 Conga T8 Conga T8 Crash4 Conga T8 Ride2 R Crash T8 Ride T9 Tambourine RX5 Splash R Mambo Body Crash T9 Vibraslap Ride T9 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Closed Rim R Sd AnSm Rim HandClap St Sd Hip3 Scratch Sd R Scratch Hip Scratch Bd R Scratch Vo R Scratch Hip2 Scratch Combi2 Scratch Combi4 Bd Hip Deep Sd Hip Rim4 Hi Q1 TurnTable Hit Orchestra1 Scratch Bd R Sd LiveRoll R Analog Click Sd Hip Rim3 Sd AnCR Rim Bd HipHop9 Sd HipHop Rim6 Bd T8-3 Bd T8-2 Sd T8 Rim Sd Hip Stick4 HandClap R Sd Hip Stick3 Tom T8 HH Closed T8-1 Tom T8 HH Open T8-1 Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash4 Tom T8 Ride2 R Crash T8 Ride T9 Tambourine RX5 Splash R Mambo Body Crash T9 Vibraslap Ride T9 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd AnSm Sd Closed Rim3 Analog Click Sd Closed Rim2 Scratch H Scratch L Scratch Down Scratch Hi R Scratch Bd F Scratch Combi Scratch Vo R Bd Jungle2 Sd Hip Rim3 Hi Q2 Scratch H Scratch H Scratch Vo R Sd Roll Mono Analog Click Sd Hip Rim4 Sd T8 Rim Bd HipHop9 Sd Hip Stick4 Bd Hard Long Bd ElecClick Sd Hip Stick1 Sd Hip Stick2 HipHop clap8 Sd Hip Stick6 St Tom T9 Lo HH Closed T8-1 Tom T9 Lo HH Open T8-1 Tom T9 Lo HH Open T8-1 Tom T9 Lo Tom T9 Lo Crash T9 Tom T9 Lo Ride1 RL Crash T8 Ride Cup Mono2 Electric Tambourine2 Splash R Cowbell T8 Crash T9 Vibraslap Crash Syn Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd T8 Rim Sd Hip Stick1 Sd Hip Stick2 Sd Closed Rim2 Scratch Down Scratch Hi F Scratch Lo F Scratch Lo R Scratch Bd R Scratch Combi Scratch Stop Bd HipHop8 Bd T9 Hd3 Sd AnCR Sd AnSm Rim Sd ClapAna Sd T9Gate Sd Roll Mono Sd Timbr Sd FM Rim Sd JungleFX Bd T9-4 Sd Hip Rim5 Bd T9-3 Bd Elec2 Sd T8 Rim Sd Lo-Fi HipHop clap2 Sd R&B1 Bd T8-3 HH Closed T9 Bd T8-3 HH Pedal Hip Bd T8-3 HH Open T9 Bd T8-3 Bd T8-3 Crash1 RL Bd T8-3 Ride2 St China2 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash1 St Cowbell T8 Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Hi Q1 Hi Q1 Agogo Lo Agogo Lo Analog Shaker Maracas Slur Vox Drum Vox Drum Hi Q1 Hi Q2 Clave T8 Hi Q1 Scratch Spin Scratch Hi R Scratch Hi F Triangle Mute Triangle Open Shaker 2 Tambourine1 Hit Wind Chime Sd Break Roll Noise Burst Vox Bell Sd R&B1 VoxAlk Udo High Trance Choir Swarm Bd Pop Sd R&B1 Rim Bd R&B2 Crash3 Bd T9-1 Sd AnCR Rim Sd Hip Gate Sd Break Roll Sd T9-2 Sd LiveRoll St HipHop Snap2 Sd HipHop9 Sd HipHop Rim2 Bd HipHop5 Sd Hip Rim4 Bd T9-1 Bd T8-2 Sd HipHop Rim1 HipHop Snap2 HipHop clap8 Sd T8-5 Tom T8 HH Closed T8-1 Tom T8 HH Open T8-1 Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash1 RL Tom T8 Ride2 St China2 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash1 St Cowbell T8 Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Hi Q1 Hi Q1 Agogo Lo Agogo Lo Analog Shaker Maracas Slur Vox Drum Vox Drum Hi Q1 Hi Q2 Clave T8 Hi Q1 Scratch Spin Scratch Hi R Scratch Hi F Triangle Mute Triangle Open Shaker 2 Tambourine1 Hit Wind Chime Sd Break Roll Noise Burst Vox Bell Sd R&B1 VoxAlk Udo High Trance Choir Swarm Bd Pop Sd R&B1 Rim Bd R&B2 Crash3 Bd T9 Hd2 Sd AnCR Sd Break2 Sd Break Roll Sd T9Gate Sd Roll Mono HipHop Snap2 Sd HipHop2 Sd HipHop Rim3 Bd T9-2 Sd Hip Rim3 Bd T9-1 Bd T8-2 Sd HipHop Rim3 Sd R&B1 HipHop clap6 Sd Hip Gate Tom T9 Lo HH Closed T9 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Lo Tom T9 Hi Crash1 RL Bd T8-3 Ride2 St China2 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash1 St Cowbell T8 Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Hi Q1 Hi Q1 Agogo Lo Agogo Lo Analog Shaker Maracas Slur Vox Drum Vox Drum Hi Q1 Hi Q2 Clave T8 Hi Q1 Scratch Spin Scratch Hi R Scratch Hi F Triangle Mute Triangle Open Shaker 2 Tambourine1 Hit Wind Chime Sd Break Roll Noise Burst Vox Bell Sd R&B1 VoxAlk Udo High Trance Choir Swarm Bd Pop Sd R&B1 Rim Bd R&B2 Crash3 Bd T9-2 Sd AnSm Rim Sd Elec9 Clap AnSm Sd Elec7 Sd Elec11 Sd Elec7 Sd T8-1 Sd T9-5 Bd T9 Hd2 Sd Elec12 Bd T9 Hd3 Bd Hard Long Sd T9Rim Sd Elec5 Clap T9 Sd T9-2 Sd Syn HH Closed T9 Sd Syn HH Open T9 Sd Syn HH Open T9 Sd Syn Sd Syn Crash T9 Sd Syn Ride T9 Crash Syn Ride Cup Mono Tambourine1 Hit Crash T8 Cowbell T8 China St Cowbell T8 Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Slap Conga Hi Slp Open Conga Hi Open 2 Tom Syn1 Tom Syn1 Analog Click Noise Burst Cabasa2 Maracas Slur Neo Seq Em-3 Neo Seq2 Em-5 Electric Perc1 Shaker Hip1 Clave T8 Tambourine RX5 Cowbell RX11 Shaker Hip2 Electric Perc10 Triangle Mute Triangle Open Shaker Hip4 Zap2 Bd HipHop8 Bd T9 Hd1 Bd ElecComp3 Bd T9-1 Bd T9 Hd3 Bd Hard Long HH Closed T8-1 Bd ElecComp2 HH Open T8-2 Bd T9 Hd1 HH Open T8-1 Bd T9 Hd2 Bd ElecComp3 LFO Seq Trance Choir Tibetan Bowl Scratch Stop Fx Female Voice St Stab Organ2 Door Squeak Radio Noise Door Squeak Acoustic Bass FX06 Electric Bass FX 02 Acoustic Bass FX06 Bd BlpHd Bd Blip Sd Hip Gate Sd SonA St1-4 Noise Burst Tom Dist Lo HH Closed T9 Tom Dist Hi HH Closed 2St Tom T9 Hi HH Open T9 Tom T9 Hi Tom T9 Hi China St Tom T9 Hi Splash St Crash T9 Ride T9 Pandeiro Tip Electric Tambourine1 Hi Q2 Bd Gate Hi Q2 Acoustic Bass FX03 Acoustic Bass FX01 Mini Blip6 Mini Blip1 Mini Blip2 Quinto1 Slap Tabla Bya Djembe Edge Tabla Nah Barasim Tak Electric Perc8 Electric Perc13 Hop FX1 Motion Beam Zap2 Digi Voice Mod Human Whistle Digital Wind Analog Click HH Closed T8-1 Sd FM HH Half Open2 RL Clave T8 HH Open T8-1 Reverse Cymbal Bd Analog Tight Bd ElecComp3 Bd T9 Hd3 Bd ZapHard Bd Slimy Bd Break Hard Sd BreakRim Sd Soul St1-3 Sd Ambience Sd Rock Mute1 R Sd Cut Sd D&B3 Sd FX1 Bd T8-1 Sd T8 Rim Sd T8-2 Electric Clap1 Sd Elec11 Sd Elec7 Sd T8-4 Bd Elec2 Sd T8-1 Bd HipHop8 Sd T8-3 Bd HipHop9 Bd HipHop8 Sd AnSm Rim Sd HipHop5 Clap AnSm Sd HipHop6 Tom T8 HH Closed T8-1 Tom T8 HH Open T8-1 Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash3 Tom T8 Ride T9 Splash St Ride Cup Mono Tambourine1 Hit Crash T8 Cowbell T8 China St Cowbell T8 Ride1 St Conga T8 Conga T8 Conga T8 Conga T8 Conga T8 Conga Hi Slap Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1 Cabasa2 Maracas Slur Fx Female Voice St Portamento Lead Seq Electric Perc9 Shaker Hip1 Clave T8 Hi Q1 Hi Q2 Shaker Hip2 Electric Perc15 Triangle Mute Triangle Open Shaker Hip4 Ripper Bd Elec4 Sd Hip1 Sd Elec9 Sd Elec6 Sd HipHop2 Bd ElecGate HH Closed T8-2 Bd ThickSkin HH Open T8-2 Bd Break Heavy HH Open T8-2 Bd Break Deep Bd R&B2 Clave Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Brush Soft St Stick1 Bd Funk St1-2 Sd Basic 3St Bd Jazz1-2St Bd FK Jazz St Sd Closed Rim4 St Sd Brush Med St HandClap St Sd Brush vari St Tom Brush floor St HH Power ClTip 1-4St Tom Brush Low St HH Power ClPedal St Tom Brush Low St HH Power Open EdgeSt Tom Brush Mid St Tom Brush Mid St Crash Brush1 St Tom Brush Hi St Ride Brush1 St China St Crash Brush Cup St Tambourine1 Sw Splash St Cowbell2 Crash Brush2 St Vibraslap Ride Brush2 St Bongo Hi 1Finger 1-2 Bongo Lo 1Finger 1-2 Conga Hi SlapMute1-2 Conga Hi Open 1-2 Conga Lo Open 1-2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1/3 Sleigh Bell Wind Chime Sd Bld St1-4 Sd Blues 3St Sd Hip St1-2 Sd Funk St1-3 Sd Soul St1-4 Sd LdwHMono Sd Heavy Sd Tight St1-4 Sd BluesHeart St1-3 Bd Snap 1-2 Bd Tight 1-2 Bd Funk St1-2 Bd Jazz1-2St Sd Brush Soft R Sd Brush SwirAtt St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Hip 1St Stick1 Bd Funk 1 L Sd Tight 3St Bd FK Jazz St Bd Funk St1-2 Sd Closed Rim St Sd Tight 1St Sd RockRoll St Sd BluesHeart St1-3 Tom Floor St HH Power ClTip 1-4St Tom Floor St HH Power ClPedal St Tom Low St HH Power Open EdgeSt Tom Mid St Tom Mid St Crash2 St Tom Hi St Ride2 St China St Ride Cup Mono2 Tambourine1 Hit Splash St Cowbell1 Crash2 St Vibraslap Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1 Timbale Hi1 Agogo Lo Agogo Lo Cabasa1 Maracas Slur Sd Brush Soft R Sd Brush Swir St Guiro Short Guiro Long Clave Wood Block Wood Block Timbale Paila H Timbale Paila L Triangle Mute Triangle Open Shaker 2 Sleigh Bell Wind Chime Sd Blues L1-3 Sd Funk Ay St Sd BluesHeart R1-3 Sd Blues St1-2 Bd Low L Sd LdwHMono Bd Dry2 Sd Brush SwirAtt St Bd Hip Deep Sd Roll Mono Bd Normal 2R Crash Brush2 St Clave Cajon Kick Cajon Slap Cajon Tip 1-2 Clave Wood Block HandClap St Ride2 L HipHop Snap2 Castanet Conga Hi Tip Conga Hi Heel Conga Hi Open 1-2 Conga Hi Mute Conga Hi Slp Open Conga Hi Slap Conga Hi SlapMute1-2 Conga Lo Tip Conga Lo Heel Conga Lo Open 1-2 Conga Lo Mute Conga Lo SlapOpen Conga Lo SlapOpen Conga Lo Open 1 Bongo Hi 1Finger 1-2 Bongo Hi 3Finger 1-2 Bongo Hi Rim Bongo Hi Tip Bongo Hi Heel Bongo Hi Slap Bongo Lo 1Finger 1-2 Bongo Lo 3Finger 1-2 Bongo Lo Rim Bongo Lo Tip Bongo Lo 3Finger 1-2 Bongo Hi Slap Timbale Lo1-2 Quinto1 Slap Quinto1 Open Conga Hi Tip Tumba Open Paila L Timbale Hi1-3 Pandeiro Open Pandeiro Thump Pandeiro Tip Pandeiro Heel Paila H Cowbell2 Cowbell2 Agogo Hi Agogo Hi Guiro Short Guiro Long MetalGuiro Short1-2 MetalGuiro Long Tambourine1 Sw Tmbourim Open Tmbourim Mute Tmbourim Finger Maracas Sw Shaker 1/3 Cabasa1-2 Cuica Lo Cuica Hi Cowbell1 Mambo Body Shekere Down Shekere Sap Triangle Mute Triangle Open Whistle Wind Chime Bd T9-2 Sd AnCR Sd T9-5 Clap AnSm Sd T9Gate Sd RockRoll St Sd T9-3 Sd T9-4 Sd T9Gate Bd T9-4 Sd T9-5 Bd T9-1 Bd T9-3 Sd T9Rim Sd T9-1 Clap T9 Sd T9-2 Tom T9 Lo HH Closed T9 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Hi Tom T9 Hi Crash T9 Tom T9 Hi Ride T9 China2 Ride Cup Mono Tambourine RX5 Crash3 Cowbell1 Crash4 Cowbell T8 Ride2 R Conga T8 Conga T8 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Analog Click Conga T8 Cabasa1-2 Maracas Slur FxGun2 FxGun1 Scratch H Scratch Down Hi Q1 Hi Q1 Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd Piccolo Sd T8-5 Sd RockRollD St Sd Brush Med St Bd BlpHd Sd Jungle1 Bd Sustain Sd D&B1 Bd Break2 Sd Dist Bd TekPower Bd Distortion RM Bd T8-1 Sd AnCR Sd T8-4 Sd ClapAna Sd T8-3 Tom T8 Sd T8-5 Bd T8-3 Sd T8-4 Bd T8-2 Sd T8-3 T8 Kick Bass Bd T8-2 Sd T8 Rim Sd T8-2 Clap T9 Sd T8-1 Tom T8 HH Closed T8-2 Tom T8 HH Open T8-1 Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash T8 Tom T8 Ride T9 China2 Ride Cup Mono Tambourine RX5 Splash R Cowbell T8 Crash4 Vibraslap Ride2 R Conga T8 Conga T8 Conga T8 Conga T8 Conga T8 Timbale Hi1 Timbale Lo1 Glass Glass Cabasa1-2 MaracasT8 FxGun2 FxGun1 Analog Shaker Analog Shaker Clave T8 Hi Q1 Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd Hip1 Sd Hip2 Sd Hip Gate Sd Break1 Bd Blip Sd FX1 Bd FxHammer Sd Hammer Bd ZapHard Sd Garg L Bd TekPower Bd Slimy Bd Slimy Sd T9Rim Sd T8-5 Clap T9 Sd T9-3 Sd Human1 Sd Hip3 Sd T9-4 Sd Hip2 Bd ThickSkin Sd T9-2 Bd Distortion4 Bd ZapMid Sd T8 Rim Sd T8-5 Clap T9 Sd Syn Tom T8 HH Closed Lo-Fi Tom T8 HH Open Lo-Fi Tom T8 HH Open Lo-Fi Tom T8 Tom T8 Crash T8 Tom T8 Ride T9 Crash3 Ride Cup Mono Tambourine AnCR Crash2 L Cowbell T8 Crash T9 Cowbell T8 Crash Syn Conga T8 Conga T8 Conga T8 Conga T8 Conga T8 Timbale Hi1-3 Timbale Lo1-2 Glass Glass Cabasa1 MaracasT8 FxGun2 FxGun1 Scratch H Scratch Down Hi Q1 Hi Q1 Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Analog Shaker Sleigh Bell Bd Elec4 Bd ElecComp3 Bd ElecFX2 Bd ElecClick Bd ElecPower1 Bd ElecPower2 Bd Elec3 Bd Elec1 Sd Elec6 Sd Elec7 Sd Elec8 Sd Elec9 Sd Elec12 Bd T9-4 Sd T8 Rim Sd T8-5 HandClap St Sd Garg L Sd LiveRoll St Sd T9-3 Sd T8-1 Sd T9-5 Bd T9-1 Sd T9Gate Bd T9-2 Bd T9-2 Sd T9Rim Sd T9-1 Clap T9 Sd T9-2 Tom T9 Lo HH Closed T8-1 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Hi Tom T9 Hi Crash T9 Tom T9 Hi Ride T9 Crash3 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 St Cowbell T8 Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1 Timbale Hi1 Agogo Lo Agogo Lo Cabasa1 Maracas Slur Vox Drum Vox Drum Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd Break Roll Noise Burst Vox Bell Sd R&B1 VoxAlk Udo High Trance Choir Swarm Bd Pop Sd R&B1 Rim Bd R&B2 Crash3 Bd T9-2 Sd T8 Rim Brite Piano Clap T9 Sd T9-2 Sd T9Gate Sd Human2 Sd FX1 Sd Lo-Fi Bd T9 Hd2 Sd T9Rim Bd T9-3 Bd T9 Hd1 Sd T9Rim Sd T9-2 Noise Burst Sd Strong L1-4 Sd Syn HH Closed T9 Sd Syn HH Open T9 Sd Syn HH Open T9 Sd Syn Sd Syn Crash T9 Sd Syn Ride T9 Crash Syn Ride Cup Mono Tambourine1 Hit Crash T8 Cowbell1 Crash3 Cowbell T8 Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Heel Conga Hi Slp Open Conga Hi Open 2 Tom Syn1 Tom Syn1 Analog Click Sd Rim SE Cabasa1 Maracas Slur Vox Drum Vox Drum Tambourine AnCR Analog Shaker Clave T8 Tambourine RX5 Cowbell RX11 Analog Shaker Scratch Hi F Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Tabla Bya Tabla Nah Tabla Mute Vox Bell Bd Pop Udo High Bd FX Gate Noise Burst Bd T9-1 Sd Break Roll Crash3 TurnTable Bd T9-4 Sd T8 Rim Sd T8-5 HandClap St Sd Garg L Sd LiveRoll St Sd T9-3 Sd T8-1 Sd T9-5 Bd T9-1 Sd T9Gate Bd T9-2 Bd T9-3 Sd T9Rim Sd T9-1 Clap T9 Sd T9-2 Tom T9 Lo HH Closed T8-1 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Hi Tom T9 Hi Crash T9 Tom T9 Hi Ride T9 Crash3 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 St Cowbell T8 Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1 Timbale Hi1 Agogo Lo Agogo Lo Cabasa1 Maracas Slur Vox Drum Vox Drum Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd Break Roll Noise Burst Vox Bell Sd R&B1 VoxAlk Udo High Trance Choir Swarm Bd Pop Sd R&B1 Rim Bd R&B2 Crash3 Bd FX Gate Bongo Lo 3Finger 1-2 Sd GM HandClap St Sd Gate2 Sd RockRoll St Sd Rock Rim1 St Sd Piccolo Sd Rock Flam St Bd Room2 Sd Rock Stick1 St Bd Room3 Bd Gate Sd Mid Rim Sd Gate1 Clap AnSm Sd Ambience Tom Ambience Lo HH Closed D&B Tom Ambience Lo HH Pedal D&B Tom Ambience Lo HH Open D&B Tom Ambience Hi Tom Ambience Hi Crash2 St Tom Ambience Hi Ride1 St China St Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 St Vibraslap Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Bd +HH Bd +HH Agogo Lo Agogo Lo Cabasa1 Maracas Slur FxGun2 FxGun1 Scratch H Scratch Down Clave Wood Block Wood Block Scratch L Scratch L Triangle Mute Triangle Open Shaker 2 Sleigh Bell Wind Chime Sd Dist Sd FM Sd FM Rim Sd Clappy Bd Room3 Sd Timbr Bd Rock1 St Sd Rim SE Bd Rock2 St Sd Break Roll Bd D&B1 Bd Room2 Bd Break Deep Sd Hip 1St Sd Lo-Fi Sd Clappy Sd LdwHMono Sd RockRollD St Sd Gate1 Sd Mid Sd BreakRim Bd Break Heavy Sd Hip Rim4 Bd Break2 Bd Break1 Sd Hip Rim1 Sd Break3 Sd Break1 Sd Break2 Tom Low St HH Closed Rock S R Tom Low St HH Pedal Rock R Tom Low St HH Half Open Rock R Tom Hi St Tom Hi St Crash2 L Tom Hi St Ride2 L China L Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 R Cowbell RX11 Ride Mono Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Open 2 Agogo Lo Cabasa1 Maracas Slur Timbale Hi1 Timbale Lo1 Scratch H Scratch Down Clave Wood Block Wood Block Scratch L Scratch L Triangle Mute Triangle Open Shaker 2 Sleigh Bell TurnTable Sd Break Roll FM Bass5 Mind Bell Sd FX1 Bd Rock2 St LFO Seq Ripper Noise Burst Police Fairwave Swarm Radio Noise Bd Hip Deep Bongo Lo 3Finger 1-2 Sd R&B2 Sd Timbr Sd D&B3 Sd Hammer Glass Bd D&B2 HH Pedal 3 St Bd Hip Deep HH Open2 St Bd Break Hard Bd D&B1 Sd Mid Rim Sd D&B1 Noise Burst Sd D&B2 Tom Ambience Lo HH Closed D&B Tom Ambience Lo HH Pedal D&B Tom Ambience Lo HH Open D&B Tom Ambience Hi Tom Ambience Hi Crash2 L Tom Ambience Hi Ride1 St China2 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 St Vibraslap Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Sd D&B4 HH Reverse D&B Agogo Lo Agogo Lo Cabasa1 Maracas Slur FxGun2 FxGun1 Guiro Short Guiro Long Clave Electric Perc8 Electric Perc2 P50-1 Electric Perc13 TurnTable Triangle Open Shaker 2 Sleigh Bell Wind Chime Sd Dist Sd FM Sd FM Rim Sd Clappy Bd D&B1 Sd Timbr Bd Distortion5 Sd Rim SE Bd Distortion4 Sd Hip3 Bd Slimy Bd +HH Bd Analog Tight Sd T8 Rim Sd Jungle3 HandClap St Sd T8-1 Tom Syn1 Sd BluesHeart L1-3 Sd BluesHeart 3R Sd SonA St1-3 Bd Funk St1-2 Sd Basic St1-3 Bd Blip Bd T9-4 Sd T9Rim Sd FM Clap T9 Sd AnCR Tom T9 Lo HH Closed Tek Tom T9 Lo HH Closed Tek Tom T9 Lo HH Open Tek Tom T9 Hi Tom T9 Hi Crash T9 Tom T9 Hi Ride T9 Crash3 Ride Cup2 L Tambourine1 Hit Crash3 Cowbell1 Splash St Cowbell T8 Ride Mono Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1 Timbale Hi1 Agogo Hi Agogo Lo Cabasa1 Maracas Slur FxGun2 FxGun1 Scratch H Scratch Down Hi Q1 Hi Q1 Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd T9-5 Sd T9-5 Sd T9-5 Sd T9-5 Bd Human1 Sd D&B1 Acoustic Bass FX03 DigiOsc4 DigiOsc3 Sd Break1 Bd Jungle1 Bd Break Heavy Bd Jungle2 Sd Mid Rim Sd Jungle3 Sd Jungle3 Sd Piccolo Rim1 Sd Roll Mono Sd Wood Sd R&B2 Sd D&B2 Bd Jungle2 Sd JungleFX Bd T8-1 Bd Jungle1 Sd Hip Rim4 Sd Jungle2 Sd JungleFX Sd Jungle3 Tom Floor L HH Closed Rock Sw R Tom Low L HH Pedal Rock Sw R Tom Mid R HH Open Rock Sw R Tom Mid R Tom Hi R Crash2 L Tom Hi R Ride2 R China2 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell1 Crash2 St Vibraslap Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Tabla Nah Tabla Bya Agogo Lo Agogo Lo Cabasa1 Maracas Slur Timbale Hi1 Timbale Lo1 Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 2 Sleigh Bell Wind Chime HH Reverse D&B RainLoop Scratch H Scratch L Bd BlpHd Sd Syn IndusTom Pandeiro Open Ripper TelRing Scratch Lo R Tape Rewind Bd AnCR Bd Blip Sd T8-5 Clap AnSm Sd Hip Rim2 Sd Roll Mono Sd T8-1 Sd AnCR Sd T8-2 Bd T8-1 Sd T8-4 Bd Blip Bd T8-2 Sd T8 Rim Sd T8-3 Clap T9 Clap AnSm Tom Syn2 HH Closed T8-2 Tom Syn2 HH Open T8-1 Tom Syn2 HH Open T8-2 Tom Syn2 Tom Syn2 Crash T9 Tom Syn2 Ride T9 Crash3 Crash Syn Crash T8 Crash3 Sd Rim SE Crash2 St Cowbell T8 Crash Syn Tom T8 Tom T8 Tom T8 Tom T8 Tom T8 Applause Door Squeak Heart Beat Bomb HH Open Lo-Fi Shaker 1 Zap2 Zap1 Scratch H Scratch L Hi Q1 Hi Q1 Hi Q2 Scratch L Hit Orchestra2 HH Closed FX HH Open FX Analog Shaker Analog Shaker TurnTable Sd Break Roll FM Bass5 Mind Bell Sd Break2 Bd Rock2 St Radio Noise Ripper Noise Burst Scratch Spin Bell Choir Hi-Lo Radio Noise Bd Human1 Scratch Vo F Hit Uhh Scratch Vo R Sd JungleFX Sd RockRoll St Sd Break1 Sd AnCR Sd T9-1 Bd Sustain Sd Hip Stick1 Bd Human2 Bd Human2 Sd Wood Sd Human1 Sd Garg H Sd Human2 Bd Human1 HH Open Human Bd Human1 HH Open Human Bd Human1 HH Open Human Bd Human1 Bd Human1 Noise Burst Bd Human1 Crash T8 China2 Ride Cup Mono Scratch Sd R Splash St Vox Drum Crash4 Electric Tambourine2 Stab Aah Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Agogo Lo Agogo Lo Cabasa1 Maracas Slur FxGun2 Bomb Scratch H Scratch Down Scratch Lo F Scratch Lo R Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Shaker 2 Sleigh Bell TurnTable Sd Rock Rim1 R Helicopter Stab Hard Sd Gate1 Bd Rock1 St Scratch Down Bd AnSm Sonar1 Wataiko Wataiko HH Hit Stab Organ2 Bd Distortion3 Sd Wood Sd Gate1 HandClap St Sd Gate2 Sd Roll Mono Sd ClapAna Sd Hip3 Ripper Bd Distortion RM Sd T8 Rim Bd Distortion2 Bd Distortion1 Sd Mid Rim Sd Dist Noise Burst Sd FX2 Tom Dist Lo HH Closed T9 Tom Dist Lo HH Open T9 Tom Dist Lo HH Open T9 Tom Dist Hi Tom Dist Hi Crash T9 Tom Dist Hi Ride T9 China2 Ride Cup Mono Tambourine AnCR Crash3 Cowbell T8 Crash2 St Vibraslap Ride1 St Sd Timbr Sd Timbr Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Bd +HH Bd +HH Glass Glass Cabasa1 Maracas Slur FxGun2 FxGun1 Scratch H Scratch Down Hi Q1 Hi Q1 Hi Q2 Scratch L Scratch L Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd Dist Sd FM Sd FM Rim Sd Clappy Bd FxHammer IndusTom Bd D&B1 IndusTom Bd Room3 IndusTom Bd T9-4 Sd Hammer Analog Click Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Normal 1St Stick1 Bd Rock2 R Sd Tight 1St Bd Break1 Bd Rock2 R Sd Rock Stick2 R Sd SonA 1L HandClap L Sd Piccolo Rim1 Tom Ambience Lo HH Closed Rock 2R Tom Ambience Lo HH Pedal Rock R Tom Ambience Lo HH Open Rock R Tom Ambience Hi Tom Ambience Hi Crash2 L Tom Ambience Hi Ride2 R China L Ride Cup2 R Tambourine1 Sw Splash R Mambo Bl2 Crash2 L Vibraslap Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Scratch Combi2 Scratch Hi R Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Sd Piccolo Sd Heavy Sd ClapAna Sd Piccolo Rim1 Bd Room2 Sd Mid Bd Room3 Sd Mid Rim Bd Gate Sd Gate1 Bd FX Gate Bd D&B1 Bd Blip Hi Q2 Sd Piccolo Rim2 Hi Q1 Sd AnCR Tom Syn1 HH Closed Syn Tom Syn1 HH Closed FX Tom Syn2 HH Open Syn Bd AnCR Bd T9-1 Sd Hip Stick3 Sd Jungle1 Clap T9 Sd Jungle1 Tom T8 HH Closed T8-2 Tom T8 HH Open T8-1 Tom T8 HH Open T8-2 Tom T8 Tom T8 Crash Syn Tom T8 Ride T9 Crash3 Ride Cup Mono Tambourine1 Hit Crash3 Cowbell T8 Crash2 St Vibraslap Ride1 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Glass Glass Cabasa1 MaracasT8 Bird Engine Room St Electric Perc14 Gunshot Clave T8 Wood Block Wood Block Tambourine AnCR Cowbell AnCR Triangle Mute Triangle Open Analog Shaker Sleigh Bell Wind Chime Sd T8-5 Sonar1 Sd T8-5 Sonar1 Bubble Sd T8-5 Bubble Sd T8-5 Eery Sd T8-5 FX Chime FX Chime Bd Hard Long Bd T9-1 Bd Slimy Bd AnaHard Bd Distortion4 Bd ElecComp3 Bd Hip Deep Bd FxHammer Bd T9-3 Bd T8-3 Bd Sustain Bd R&B2 Bd T9 Hd1 Bd Slimy Sd T8-1 Sd T8 Rim Sd Clappy Sd Blues 3St HH Open 2St HH Closed T8-2 HH Open 2St HH Open T8-2 HH Open 2St HH Open T8-2 Clap AnSm Crash2 St Crash T9 Ride1 St Ride T9 Analog Click Clave T8 Clap AnSm Clap T9 Analog Shaker Conga T8 Conga T8 Sd AnCR Sd RockRoll St Agogo Hi China RL Hi Q2 Hi Q1 Zap2 Zap1 Zap2 Door Squeak Scratch Combi3 AcousticGtr FX27 St Play FX Mix Scratch Hi F Brite Piano Brite Piano Brite Piano Resonance X-Fade Sonar1 Vox Drum Vox Drum Vox Drum HH Open Syn HH Closed Syn Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Tri Wave Bd HipHop8 Bd T9-1 Bd Blip Bd Break Hard Bd ElecFX1 Bd ElecGate Bd ElecFX2 Bd Distortion2 Bd ThickSkin Bd ElecComp2 Bd Elec2 Bd ElecClick Bd ElecClick Sd T9Rim Sd Elec12 Clap T9 Sd Elec8 Tom T9 Lo HH Closed T9 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Hi Tom T9 Hi Crash T9 Tom T9 Hi Ride T9 Crash T9 Ride T9 Electric Tambourine2 Crash T9 Electric Cowbell Crash T9 Electric Tambourine1 Ride T9 Electric Perc5 Electric Perc4 Electric Perc12 Electric Perc6 Electric Perc1 Bell FX1 Bell FX2 Scratch Attack Scratch R&B Cabasa1-2 Maracas Slur Electric Perc14 Electric Perc15 Shaker Hip2 Electric Perc3 Electric Perc7 Electric Perc8 Electric Perc9 Electric Perc10 Electric Perc13 Triangle Mute Triangle Open Electric Maracas Electric Triangle Electric Perc11 Sd Elec1 Sd Elec9 Sd Elec10 Sd Elec11 Sd Elec2 Sd Elec3 Sd Elec4 Sd Elec5 Sd Elec6 Sd Elec7 Electric Clap1 Electric Clap2 Analog Click Sd Brush Med L Sd Brush Swir R Sd Brush Med L Sd Brush SwirAtt R Sd Orch Roll St Castanet Sd Orch1 S St Stick1 Bd Gran Casa Mute St Sd Orch1 H L Bd Gran Casa Mute St Bd Gran Casa Open St Sd Closed Rim R Sd Orch1 H St HandClap St Sd Orch2 H St Timpani Sw St HH Closed 1-4St Timpani Sw St HH Pedal 1-3 R Timpani Sw St HH Open 1-2St Timpani Sw St Timpani Sw St Hand Cymbal Open St Timpani Sw St Ride1 St China St Ride Cup1 St Tambourine1 Hit Hand Cymbal Mute St Cowbell2 Crash1 St Vibraslap Ride2 St Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Open 2 Tumba Open Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Cabasa1-2 Maracas Slur Whistle Whistle Guiro Short Guiro Long Clave Wood Block Wood Block Cuica Hi Cuica Lo Triangle Mute Triangle Open Shaker 1 Sleigh Bell Wind Chime Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Wood Block Bd Hard Long Bd TekPower Bd Distortion5 Bd Distortion3 Bd Distortion1 Bd D&B1 Bd Blip Bd AnSm Bd T8-2 Bd T8-3 Bd T9 Hd3 Bd T9-2 Bd T9-3 Sd T9Rim Sd T9-1 Clap T9 Sd T9-4 Tom T9 Lo HH Closed T9 Tom T9 Lo HH Open T9 Tom T9 Lo HH Open T9 Tom T9 Lo Tom T9 Lo Crash T9 Conga T8 Ride T9 Conga T8 Analog Click Clave T8 MaracasT8 Tambourine AnCR Analog Shaker Cowbell T8 Cowbell AnCR Sd T8-1 Sd T8-2 Sd T8-3 Sd AnCR Sd Jungle1 Sd D&B1 Sd Hip1 Sd R&B1 Sd R&B2 Sd Hip1 Sd Wood Sd Timbr HH Closed T8-1 HH Open T8-1 HH Closed T8-2 HH Open T8-2 HH Pedal 3 St HH Open 2St HH Open 2St HH Closed Lo-Fi HH Open Lo-Fi HH Closed Syn HH Open Syn Analog Shaker Tambourine RX5 Tambourine RX5 Electric Cowbell Conga T8 Electric Triangle Clave T8 Analog Shaker Electric Clap1 Electric Clap2 Tambourine AnCR Cowbell T8 Analog Click Cowbell RX11 Bd Normal St1-2 Sd Rock Stick1 St Sd Rock Open2 St Sd RockRoll St Sd Rock Open1 St Tom Low St HH Closed Foot St Tom Low St HH Pedal 3 St Tom Mid St HH Open 2St Tom Hi St Bd Rock2 St Sd Rock Stick1 St Sd Rock Open2 St HandClap St Sd Rock Mute2 St Tom Rock Low St HH Closed Rock Sw St Tom Rock Low St HH Pedal Rock Sw St Tom Rock Mid St HH Open Rock Sw St Tom Rock Mid St Tom Rock Hi St Crash2 St Tom Rock Hi St Ride1 St China St Ride Cup1 St Tambourine1 Sw Splash St Cowbell1 Crash2 St Shaker 1 Ride1 St Bd Rock1 St Sd Rock Stick2 St Sd Rock Open1 St Clap T9 Sd Rock Mute1 St Tom Rock Low St HH Closed Rock Sw St Tom Rock Low St HH Pedal Rock St Tom Rock Mid St HH Half Open Rock St Tom Rock Mid St Tom Rock Hi St Crash2 St Tom Rock Hi St Ride2 L China St Ride Cup1 St Tambourine1 Hit Splash St Mambo Bl2 Crash2 St Shekere Down Ride2 R Bd Funk St1-2 Sd Rock Stick1 St Sd Rock Rim1 St Sd Rock Flam St Sd Rock Rim2 St Tom Floor St HH Closed 4St Tom Low St HH Pedal 3 St Tom Mid St HH Open2 St Tom Mid St Tom Hi St Bd T8-3 Sd T8 Rim Sd HipHop11 Clap T9 Sd HipHop9 Bd T8-3 HH Closed Tek Bd T8-3 HH Open Tek Bd T8-3 HH Open Tek Bd T8-3 Bd Hip Deep Sd Hip Stick4 Sd Hip Rim1 HipHop clap1 Sd Hip2 Tom T8 HH Closed T8-2 Tom T8 HH Open AnCR Tom T8 HH Open T8-1 Tom T8 Tom T8 Crash T9 Tom T8 Crash Syn Crash Syn Crash T8 Tambourine RX5 Splash R Cowbell T8 Crash T9 Cowbell AnCR Ride T9 Bd Hip Deep Sd Hip Stick5 Sd Hip Rim2 HipHop clap2 Sd Hip1 Tom T9 Lo HH Closed RX5 Tom T9 Lo HH Closed AnCR Tom T9 Lo HH Open T8-2 Tom T9 Lo Tom T9 Hi Crash2 R Tom T9 Hi Crash T8 Crash Syn Crash T8 Tambourine1 Hit China R Cowbell RX11 Crash2 L Cowbell1 Ride2 R Bd Hip Deep Sd HipHop Rim4 Sd HipHop2 HipHop clap2 Sd Hip Rim5 Tom Dist Lo HH Closed Hip Tom Dist Lo HH Pedal Hip Tom Dist Hi HH Open Hip Tom Dist Hi Tom Dist Hi Wataiko Udo High Djembe Open Quinto1 Open Udo Low Udo Finger Udo High Tabla Bya Tmbourim Open Tabla Mute Tabla Nah Tabla Mute Timpani Sw St Hit Uhh Bongo Hi 1Finger 1-2 Clap T9 Triangle Mute Triangle Open Castanet Castanet Castanet Guiro Long Guiro Swp Guiro Short Vibraslap Clave Clave T8 Cuica Lo Cuica Hi Timbale Lo1-2 Wood Block Timbale Lo2 Wood Block Timbale Hi1-3 ChaCha Timbale Hi2 Timbale Paila L Timbale Paila L Timbale Paila H Mambo Bl1 Bongo Lo 3Finger 1-2 Bongo Lo 3Finger 1-2 Handbell L Bongo Hi 1Finger 1-2 Handbell H Bongo Hi 1Finger 1-2 Agogo Lo Agogo Hi Conga Hi Heel Conga Hi Tip Tumba Slap Maracas Slur Tumba Open Conga Hi Heel Conga Hi Tip Conga Hi Slp Open Shaker 1 Conga Hi Open 2 Cabasa1 Cabasa1-2 Conga Hi Heel Conga Hi Tip Quinto1 Slap Shaker 1 Quinto2 Open Caxixi1-2 Tambourine1 Shake Tambourine1 Hit Tambourine1 Sw Tambourine1 Hit Sleigh Bell Whistle Whistle Bd L Son St Sd Rock Stick2 St Sd Funk St1/3 Crash2 St Splash St Tom Low St HH Closed 1-4St Tom Low St HH Pedal 3 St Tom Mid St HH Open 2St Tom Hi St Conga Hi Tip Conga Hi Heel Tumba Slap Clave Tumba Open Conga Hi Tip Conga Hi Heel Conga Hi Slp Open Handbell L Conga Hi Open 2 Handbell H Mambo Bl1 Conga Hi Tip Conga Hi Heel Quinto1 Slap ChaCha Quinto1 Open Bongo Lo 3Finger 1-2 Bongo Hi 1Finger 1-2 Bongo Hi 3Finger 1-2 Timbale Lo1 Timbale Paila L Timbale Hi1 Timbale Paila H Wood Block Wood Block Shekere Down Shekere Up Shekere Sap Maracas Slur Maracas Staccato Guiro Long Guiro Swp Guiro Short Surdo Mute Surdo Open Pandeiro Tip Pandeiro Heel Pandeiro Thump Pandeiro Open Tmbourim Finger Tmbourim Open Tmbourim Mute Agogo Lo Tap Agogo Lo Agogo Hi Triangle Open Triangle Mute Shaker 2 Shaker 1 Shaker 3 Cabasa1 Cabasa2 Caxixi1 Caxixi2 Cuica Lo Cuica Hi Whistle Whistle Vibraslap Wind Chime Acoustic Bass FX06 Acoustic Bass FX06 Play FX Mix Power Chord Mono Power Chord Mono Acoustic Bass FX01 AcousticGtr FX02 St Electric Bass FX 05 Electric Bass FX 16 Electric Bass FX 01 ElectricGtr FX06 Electric Bass FX 17 Electric Bass FX 04 Electric Bass FX 10 Play FX Mix Electric Bass FX 11 Play FX Mix Electric Bass FX 18 Electric Bass FX 16 Electric Bass FX 18 Electric Bass FX 17 Electric Bass FX 18 ElectricGtr FX13 Electric Bass FX 18 Electric Bass FX 18 AcousticGtr FX29 St Electric Bass FX 18 Electric Bass FX 11 AcousticGtr FX29 St Electric Bass FX 11 Electric Bass FX 10 AcousticGtr FX29 St Play FX Mix AcousticGtr FX29 St Electric Bass FX 13 Electric Bass FX 11 Electric Bass FX 10 Electric Bass FX 10 Electric Bass FX 02 Electric Bass FX 08 Electric Bass FX 18 Acoustic Bass FX09 Acoustic Bass FX09 Acoustic Bass FX07 Acoustic Bass FX07 Acoustic Bass FX06 Acoustic Bass FX09 Play FX Mix Play FX Mix Electric Bass FX 22 AcousticGtr FX06 St Electric Bass FX 16 Electric Bass FX 16 Electric Bass FX 03 Play FX Mix Play FX Mix Electric Bass FX 11 Electric Bass FX 17 AcousticGtr FX02 St Electric Bass FX 14 ElectricGtr FX21 ElectricGtr FX20 ElectricGtr FX19 Play FX Mix Play FX Mix Play FX Mix Play FX Mix Electric Bass FX Electric Bass FX 22 Electric Bass FX 05 Electric Bass FX 15 Electric Bass FX Electric Bass FX 19 Electric Bass FX 01 Zap2 HH Pedal D&B HandClap L Scratch Vo F IndusTom Sd Wood IndusTom Sd ClapAna IndusTom Sd FM Rim Bd FX Gate Bd FxHammer Conga Hi Slp Open Sd FM HandClap L Sd Timbr Tom Syn2 HH Closed Syn Tom Syn2 HH Open Syn Tom Syn2 HH Open Syn Tom Syn2 Tom Syn2 HH Open Mono Sonar1 HH Open T8-1 HH Open Mono Ride Cup2 L Sleigh Bell HH Open T8-2 Resonance X-Fade Crash2 L RainLoop Ride Cup2 R AirHose L AirHose L Udo High Mg Bass2 Mg Bass3 Acoustic Bass FX06 Acoustic Bass FX06 Agogo Lo Agogo Lo HH Open Human Vox Hit Rev Hi Q1 Hi Q1 LFO Seq LFO Seq BellAir HH Hit HH Hit Ooh Feedback Ooh Feedback Mind Bell Glass Swarm FxGun2 Lektronica Play FX Mix Gunshot Police Ripper Play FX Mix Tape Rewind FxGun1 Bomb Zap2 Noise Burst Stab Giant Stab Orchestra Clave T8 Sd Hip Stick6 L Guiro Long Sd Hip Stick6 R Guiro Long AcousticGtr FX13 St Castanet Electric Bass FX 11 Stick1 Electric Bass FX 08 Electric Bass FX 07 Tom T9 Hi Electric Bass FX 17 Sd Closed Rim3 Stick2 Guiro Long Stick2 Surdo Open Sd AnSm Rim Surdo Open Clap AnSm Surdo Open Vibraslap Surdo Open Surdo Open Seashore Surdo Open Sd Rock Stick2 St Applause Sd Rock Stick1 L Tambourine1 Shake Seashore Wood Block Applause Helicopter Sd Rock Stick2 R Conga Hi Slp Open Conga Hi Slp Open Castanet Castanet Wood Block Timbale Paila L Timbale Paila L Stick1 Stick2 Caxixi1-2 Caxixi2 Door Squeak Door Squeak Guiro Short Guiro Long Clave Wood Block Wood Block Door Squeak Door Squeak Wood Block Clave Door Squeak HandClap L ChaCha Sd Rock Open2 St Bd Jungle3 Quinto1 Slap Bd Room2 Bd H Son St Sd Heavy Surdo Open Sd Rock Stick1 St Bd Slimy Sd Gate1 Bd Funk Son St Bd Gate Cowbell T8 HH Closed 4R Ride Cup2 R HH Open Syn Ride Cup2 L Ride Cup1 St FM Metal2 Ride1 St Electric Perc7 Cowbell2 HH Closed 1St ChaCha Cowbell2 HH Closed Syn HH Open Tek HH Closed T8-2 HH Closed RX5 Cowbell2 HH Open T8-1 Cowbell2 HH Closed Syn Cowbell2 HH Open Syn Cowbell2 Cowbell2 HH Open FX Cowbell2 Ride Cup1 St HH Open AnCR Ride Cup1 St Tambourine1 Sw HH Open FX Mambo Bl2 HH Open FX Sleigh Bell Ride Mono Brite Piano Brite Piano Tubular Bell Bell Brass Bell Timbale Paila H Timbale Paila L Bell Bell HH Closed 1St HH Open AnCR BellAir FX Chime HH Open Rock L Lektronica Agogo Lo Cowbell1 Cowbell2 Hi-Lo Hi-Lo Triangle Mute Triangle Open FX Chime Sleigh Bell Wind Chime Small Bell Small Bell Brass Bell Hibashi Brite Piano Sd LdwHMono Brite Piano Sd BreakRim Gamelan2 Udo Finger Finger Cymbal Bell Tabla Mute Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St AcousticGtr FX29 St Stick2 Pandeiro Thump Tabla Mute Electric Bass FX 16 AcousticGtr FX17 St Tabla Combi Wataiko Quinto1 Slap Sd Brush Med St HandClap St Sd Funk Ay St Udo Low Tabla Mute Udo Low Tabla Bya Udo High Tabla Nah Udo High Udo Finger Ride2 R Udo Finger Ride Cup1 St Ride Cup Mono2 Ride Cup1 St Tambourine1 Hit Ride Cup1 St Cowbell1 Ride Mono Vibraslap Ride2 L Bongo Hi 1Finger 1-2 Bongo Lo 3Finger 1-2 Conga Hi Tip Conga Hi Slp Open Conga Hi Open 2 Timbale Hi1-3 Timbale Lo1-2 Agogo Hi Agogo Lo Tmbourim Finger Tmbourim Finger HandClap L HandClap L Guiro Long Guiro Long Udo Finger Tabla Bya Conga Hi Tip Udo High Udo High Udo Finger Finger Cymbal Tabla Mute Sleigh Bell Wind Chime Conga Hi Slp Open Tumba Tip Tumba Tip Electric Bass FX 08 Tumba Tip Electric Bass FX 01 Tumba Open Electric Bass FX 06 AcousticGtr FX04 St Udo Low Djembe Combi Tumba Slap Scratch H Scratch L Scratch Down Scratch Hi F Scratch Hi R Scratch Lo F Scratch Lo R Scratch Bd F Scratch Bd R Scratch Sd F Scratch Sd R Scratch Vo F Scratch H Scratch L Scratch Down Scratch Hi F Scratch Hi R Scratch Lo F Scratch Lo R Scratch Bd F Scratch Bd R Scratch Sd F Scratch Sd R Scratch Vo F Scratch Vo R Scratch Stop Scratch Spin Scratch Hip Scratch Hip2 Scratch Combi Scratch Combi2 Scratch Combi3 Scratch Combi4 Scratch H Scratch L Scratch Down Scratch Hi F Scratch Hi R Scratch Lo F Scratch Lo R Scratch Bd F Scratch Bd R Scratch Sd F Scratch Sd R Scratch Vo F Scratch Vo R Scratch Stop Scratch Spin Scratch Hip Scratch Hip2 Scratch Combi Scratch Combi2 Scratch Combi3 Scratch Combi4 Scratch H Scratch L Scratch Down Scratch Hi F Scratch Hi R Scratch Lo F Scratch Lo R Scratch Bd F Scratch Bd R Scratch Sd F Scratch Sd R Scratch Vo F Scratch Vo R Scratch Stop Scratch Spin Scratch Hip Scratch Hip2 Scratch Combi Scratch Combi2 Sd Normal 1St Sd Normal 2St Sd Normal 3St Sd Normal 4St Sd Funk 1St Sd Funk 2St Sd Funk 3St Sd Funk Ay St Sd Basic 1St Sd Basic 2St Sd Basic 3St Sd SonA 1St Sd SonA 2St Sd SonA 3St Sd SonA 4St Sd Blues 1St Sd Blues 2St Sd Blues 3St Sd BluesHeart 1St Sd BluesHeart 2St Sd BluesHeart 3St Sd Tight 1St Sd Tight 2St Sd Tight 3St Sd Tight 4St Sd Hip 1St Sd Hip 2St Sd Rock Open1 St Sd Rock Mute1 St Sd Rock Rim1 St Sd Rock Open2 St Sd Rock Mute2 St Sd Rock Rim2 St Sd Rock Flam St Sd Rock Open1 St Sd Rock Mute1 St Sd Rock Rim1 St Sd Rock Open2 St Sd Rock Mute2 St Sd Rock Rim2 St Sd Rock Flam St Sd Normal 3St Sd Funk Ay St Sd Basic 3St Sd SonA 3St Sd BluesHeart 3St Sd Tight 3St Sd Tight 3R Sd Bld St1-4 Sd Soul St1-4 Sd Strong St1-4 Sd Rng St1-4 Sd Mid Rim Sd Ambience Sd Gate1 Sd LdwHMono Sd BreakRim Sd Dry Sd Heavy Sd GM Sd PowerB 1-5St Sd PowerC 1-5St Sd PowerD 1-4St Sd GM Sd Brush Soft St Sd Brush Med St Sd Brush vari St Sd Brush SwirAtt St Sd Orch1 S St Sd Orch2 SwSt Sd Orch Roll St Sd Orch1 H St Sd Brush Med St Sd Normal St1-4 Sd Normal St1-3 Sd Normal St2-4 Sd Normal St1-2 Sd Normal St2-3 Sd Normal St3-4 Sd Normal 1St Sd Normal 2St Sd Normal 3St Sd Normal 4St Sd Funk St1-3 Sd Funk St1-2 Sd Funk St2-3 Sd Funk St1/3 Sd Funk 1St Sd Funk 2St Sd Funk 3St Sd Funk Ay St Sd Basic St1-3 Sd Basic St1-2 Sd Basic St2-3 Sd Basic St1/3 Sd Basic 1St Sd Basic 2St Sd Basic 3St Sd SonA St1-4 Sd SonA St1-3 Sd SonA St2-4 Sd SonA St1-2 Sd SonA St2-3 Sd SonA St3-4 Sd SonA 1St Sd SonA 2St Sd SonA 3St Sd SonA 4St Sd Blues St1-3 Sd Blues St1-2 Sd Blues St2-3 Sd Blues St1/3 Sd Blues 1St Sd Blues 2St Sd Blues 3St Sd BluesHeart St1-3 Sd BluesHeart St1-2 Sd BluesHeart St2-3 Sd BluesHeart St1/3 Sd BluesHeart 1St Sd BluesHeart 2St Sd BluesHeart 3St Sd Tight St1-4 Sd Tight St1-3 Sd Tight St2-4 Sd Tight St1-2 Sd Tight St2-3 Sd Tight St3-4 Sd Tight 1St Sd Tight 2St Sd Tight 3St Sd Tight 4St Sd PowerA 1St Sd PowerA 2St Sd PowerA 3St Sd PowerA 4St Sd PowerA 5St Sd PowerB 1St Sd PowerB 2St Sd PowerB 3St Sd PowerB 4St Sd PowerB 5St Sd PowerC 1-5St Sd PowerD 1-4St Sd Blues St1-3 Sd BluesHeart St1-3 Bd Normal St1-2 Bd Snap 1-2 Bd Tight 1 Bd Normal L1-2 Bd Funk St1-2 Bd Ambience 1-2 Bd Funk 2St Bd High St Bd Low St Bd H Son St Bd L Son St Bd Funk Son St Bd Rock1 St Bd Rock2 St Bd Dry1 Bd Dry2 Bd Pop Bd Var1 Bd Var2 Bd Room1 Bd Room2 Bd Room3 Bd Gate Bd FX Gate Bd PowerA 1-2St Bd PowerB 1-2St Bd FK Jazz St Bd T9-4 Bd T9 Hd1 Bd T9 Hd2 Bd T9 Hd3 Bd T8-1 Bd T8-2 Bd T8-3 Bd AnSm Bd AnCR Bd Human1 Bd Human2 Bd Blip Bd BlpHd Bd Jungle1 Bd Jungle2 Bd Jungle3 Bd D&B1 Bd D&B2 Bd R&B1 Bd R&B2 Bd Hip Deep Bd Break Deep Bd Break1 Bd Break2 Bd Break Heavy Bd Break Hard Bd Distortion1 Bd Distortion2 Bd Distortion3 Bd Distortion4 Bd Distortion5 Bd Distortion RM Bd +HH Bd FxHammer Bd ZapHard Bd ZapMid Bd Sustain Bd TekPower Bd Hard Long Bd Analog Tight Bd AnaHard Bd Lo-Fi Bd ThickSkin Bd Slimy Bd HipHop8 Bd HipHop10 Bd T9-1 Bd T9-1 Bd T9-2 Bd T9-3 Bd T9-3 Bd T9-4 Bd T9 Hd1 Bd T9 Hd2 Bd T9 Hd3 Bd T9 Hd3 Bd Hard Long Bd AnCR Bd T9-1 Bd T9-2 Bd T9-3 Bd T9-4 Bd T9 Hd1 Bd T9 Hd2 Bd T9 Hd3 Bd T8-1 Bd T8-2 Bd T8-3 Bd Elec1 Bd AnCR Bd Human1 Bd Elec1 Bd Blip Bd BlpHd Bd Elec3 Bd ElecFX1 Bd ElecFX2 Bd ElecGate Bd ElecClick Bd ElecPower1 Bd R&B2 Bd Hip Deep Bd Break Deep Bd Break1 Bd ElecPower2 Bd Break Heavy Bd Break Hard Bd Distortion1 Bd Distortion2 Bd Distortion3 Bd Distortion4 Bd Distortion5 Bd Elec2 Bd ElecComp1 Bd FxHammer Bd ZapHard Bd ZapMid Bd Sustain Bd TekPower Bd Hard Long Bd Analog Tight Bd AnaHard Bd ElecComp2 Bd ElecComp3 Bd Slimy Bd Gate Bd FX Gate Bd HipHop1 Bd HipHop2 Bd HipHop3 Bd HipHop4 Bd HipHop5 Bd HipHop6 Bd HipHop7 Bd HipHop8 Bd HipHop9 Bd HipHop10 Bd Hard Long Bd Analog Tight Wind Chime Sd Brush Soft St Sd Brush Swir St Sd Brush Med St Sd Brush SwirAtt St Sd LiveRoll St Castanet Sd Normal 1St Stick1 Bd T9-2 Sd Basic St1-3 Bd High St Bd Normal St1-2 Sd Funk St1-3 Sd Bld St1-4 HandClap St Sd Soul St1-4 Tom Rock Low St HH Closed 1-4St Tom Rock Mid St HH Pedal 1-3St Tom Floor St HH Open 1-2St Tom Low St Tom Mid St Crash2 St Tom Hi St HH Open 1-2St Reverse Cymbal Timbale Lo1-2 Conga Hi Open 2 Timbale Hi1-3 Conga Hi Slp Open Tambourine1 Sw Tumba Open Cowbell1 Clave Bongo Hi 1Finger 1-2 Wood Block Bongo Lo 3Finger 1-2 Wood Block Dohol Low Cabasa1-2 Dohol High Shaker 1 Castanet Maracas Sw Castanet Twaisat3 Twaisat Twaisat2 Twaisat4 Twaisat4 Small Tabla Dom Barasim Dom Small Tabla Snap Barasim Sak Small Tabla Sak Barasim Tak Small Tabla Tak Big Tabla Dom Segal Dom Big Tabla Tak Segal Tak Big Tabla Sak Tabla Dom Tabla Tak1 Tabla Tremoro Tabla Tak2 Tabla Tik1 Tabla Sak1 Tabla Tik3 Tabla Tik2 Tabla Tik2 Wataiko Djembe Edge Djembe Mute Djembe Open Djembe Combi Tabla Bya Tabla Nah Tabla Mute Tabla Combi Small Tabla Dom Small Tabla Dom Bd Hard Long Bd T9-1 Sd T9-2 Electric Clap1 Sd Strong L1-4 Tom Dist Lo HH Closed T9 Tom Dist Hi HH Closed T8-1 Tom Syn1 HH Open T9 Tom Syn2 Sd Hip1 Crash4 Sd D&B4 HH Closed Break Reverse Cymbal Timbale Lo1-2 Conga Hi Open 2 Timbale Hi1-3 Conga Hi Slp Open Tambourine1 Sw Tumba Open Cowbell1 Clave Bongo Hi 1Finger 1-2 Wood Block Bongo Lo 3Finger 1-2 Wood Block Dohol Low Cabasa1-2 Dohol High Shaker 1 Castanet Maracas Sw Castanet Twaisat3 Twaisat Twaisat2 Twaisat4 Twaisat4 Small Tabla Dom Barasim Dom Small Tabla Snap Barasim Sak Small Tabla Sak Barasim Tak Small Tabla Tak Big Tabla Dom Segal Dom Big Tabla Tak Segal Tak Big Tabla Sak Tabla Dom Tabla Tak1 Tabla Tremoro Tabla Tak2 Tabla Tik1 Tabla Sak1 Tabla Tik3 Tabla Tik2 Sd FX1 Sd FX2 Sd FX3 Sd FX4 Sd Dist Sd Cut Sd Garg H Sd Garg L Sd FM Sd Syn Sd Hammer Sd Lo-Fi Sd T9-1 Sd T9-2 Sd T9-3 Sd T9-4 Sd T9-5 Sd T9Gate Sd T8-1 Sd T8-2 Sd T8-3 Sd T8-4 Sd T8-5 Sd AnSm Sd AnCR Sd Human1 Sd Jungle1 Sd Jungle2 Sd Jungle3 Sd JungleFX Sd D&B1 Sd D&B2 Sd D&B3 Sd D&B4 Sd R&B1 Sd R&B1 Rim Sd R&B2 Sd Hip1 Sd Hip2 Sd Hip3 Sd Hip Gate Sd Hip Rim1 Sd Hip Rim2 Sd Hip Rim3 Sd Hip Rim4 Sd Hip Rim5 Sd Break2 Sd Break Heavy Sd Elec1 Sd Elec2 Sd Elec3 Sd Elec4 Sd Elec5 Sd Elec6 Sd Elec7 Sd Elec8 Sd Elec9 Sd Elec10 Sd Elec11 Sd Elec12 Sd Elec1 Sd T8-2 Sd Elec3 Sd Elec4 Sd Elec5 Sd Elec6 Sd Elec7 Sd Elec1 Sd Elec8 Sd Elec10 Sd Elec11 Sd Elec12 Sd T9-3 Mini Blip2 Mini Blip1 Mini Blip3 Mini Blip4 Mini Blip5 Mini Blip6 Bell FX1 Bell FX2 Electric Perc4 Electric Perc6 Electric Perc15 Electric Perc5 Electric Perc2 Electric Perc3 Electric Perc7 Electric Perc8 Electric Perc9 Electric Perc10 Electric Perc14 Electric Perc13 Neo Seq Em-3 Spectrum1 Spectrum3 Spectrum2 Whisper Seq Bb Digi Voice Mod Moving Chime St Engine Room St Raspberry DigiHeli Hop FX1 Willow St Anemone St Magnitude St Radio Noise Quiek Stab Giant HH Hit Stab Hard Stab Organ2 Stab Aah Hit Uhh Hit Orchestra2 Hit Orchestra1 Hit Brass Orchestra Hit1 St Orchestra Hit2 St Brass Sect Fall Play FX Mix Play FX Mix ElectricGtr FX23 Acoustic Bass FX04 Acoustic Bass FX05 Acoustic Bass FX01 Scratch H Scratch L Scratch Down Scratch Hi F Scratch Stop Tape Rewind Ripper Noise Burst Zap1 Zap2 Hi Q1 Hi Q2 Bomb FxGun1 Motion Beam Birch St Tri Wave FM Bass2 FM Metal2 Bd Elec2 Scratch Stop Sd Brush Swir L Sd Brush Med R Sd Brush Swir St Sd Roll Mono FM Bass6 Seashore Electric Perc4 Bd ElecComp3 Sd Strong R2 Bd Elec2 Bd Slimy Timbale Paila H Sd Piccolo Clap AnSm Sd Elec10 Mg Bass2 Hi Q2 Mg Bass2 Electric Perc7 Tom T8 HH Open T8-2 Tom T8 Tom T8 Crash3 Tom T8 Ride Cup Mono Ride Cup Mono Ride Cup Mono Scratch Sd F Ride Cup1 RL Electric Perc7 Crash3 Hi-Lo China2 Ring Mod Electric Perc8 Ring Mod Electric Perc7 Electric Perc11 Paila H Paila H Hop FX1 Hop FX2 Scratch H Scratch Hi F Zap2 LFO Seq LFO Seq Mini Bass3a Quiek Digi Breath DigiHeli Electric Perc7 FM Organ1 Tibetan Bowl Tibetan Bowl Segal Dom Engine Room R Electric Perc8 Steel Slide St Mini Blip2 Spectrum2 Mini Blip5 Noise Bd D&B1 Spectrum2 SabreTone Electric Perc14 Electric Perc8 DigiHeli Wind Chime Fat Saw Mn Scratch Attack Spectrum2 3o3 Saw2 TurnTable Sd Roll Mono FM Bass6 Tape Rewind Electric Perc5 Bd ElecComp2 Sd Normal St1-4 Bd Slimy Bd Elec2 Mini Bass3a Sd BluesHeart St1-3 3o3 Square Sd SonA 3L Tom Dist Hi DigiHeli Tom Dist Hi Electric Perc2 Tom Dist Hi HH Open T8-2 Tom Dist Hi Tom Dist Hi Crash3 Tom Dist Hi Ride Cup1 R Ride2 RL Ride Cup Mono2 Scratch Hi F Splash L Mini Bass1 Crash1 St Engine Room St Ride Cup1 RL Sync LFO P5 SawDown 0 dg OB Strings Sd Normal St1-3 Electric Perc11 PWM Slow LFO Seq BigSyn BigSyn Hit Orchestra2 Small Tabla Dom Digi 03 Digi 03 Digi 01 Digi 01 Sonar1 Stab Hard Engine Room St Chatter Hoo Mondira Mondira Rotor Raspberry DigiHeli 3o3 Square Hop FX2 DigiBell3 Vox Hit Rev Hi Q1 Tape Rewind FxGun2 FxGun1 Thunder Police Bomb Bomb Switch General Midi mode on: Ignored by Motif XS f0 7e 7f 09 01 f7 Request Device Number Code from the instrument. The instrument answers with a "Identity Reply" and returns the Device ID 0x35 0x06 for XS6, 0x36 x06 for XS7 or 0x37 x06 for XS8. 0xF0 0x7E 0x0? 0x06 0x01 0xF7 0x0?: Device number (any number) f0 7e 00 06 01 f7 Reply message to an "Identity Request" 0xF0 0x7E 0x7F 0x06 0x02 0x43 0x00 0x41 dd dd 0x00 0x00 0x00 0x7F 0xF7 dd dd: Device Number 0x35 x06 = Motif XS6 0x36 x06 = Motif XS7 0x37 x06 = Motif XS8 F0 7E 7F 06 02 43 00 41 35 06 00 00 00 7F F7 Reply message to an "Identity Request" 0xF0 0x7E 0x7F 0x06 0x02 0x43 0x00 0x41 dd dd 0x00 0x00 0x00 0x7F 0xF7 dd dd: Device Number 0x35 x06 = Motif XS6 0x36 x06 = Motif XS7 0x37 x06 = Motif XS8 F0 7E 7F 06 02 43 00 41 36 06 00 00 00 7F F7 Reply message to an "Identity Request" 0xF0 0x7E 0x7F 0x06 0x02 0x43 0x00 0x41 dd dd 0x00 0x00 0x00 0x7F 0xF7 dd dd: Device Number 0x35 x06 = Motif XS6 0x36 x06 = Motif XS7 0x37 x06 = Motif XS8 F0 7E 7F 06 02 43 00 41 37 06 00 00 00 7F F7 Overall volume of all parts 0xF0 0x7F 0x7F 0x04 0x01 ll mm 0xF7 ll: Volume LSB (Ignored) mm: Volume MSB f0 7f 7f 04 01 00 7f f7 Fine control of all voice or performance parameters (Data List p. 58ff) 0xF0 0x43 0x1? 0x7F 0x03 ah am al dd ... 0xF7 0x1?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) ah: Address High am: Address Mid al: Address Low dd: Parameter data (variable length) f0 43 10 7f 03 00 00 00 00 f7 Bulk dump of all settings (Data List p. 57ff) 0xF0 0x43 0x0? 0x7F 0x03 bh bl ah am al dd ... cc 0xF7 0x0?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) bh: Byte Count MSB bl: Byte Count LSB ah: Address High am: Address Mid al: Address Low dd: Parameter data (variable length) cc: Checksum f0 43 00 7f 03 00 00 00 00 00 00 f7 Request bulk dump (Data List p. 57ff) 0xF0 0x43 0x2? 0x7F 0x03 ah am al 0xF7 0x2?: Device Number ah: Address High am: Address Mid al: Address Low f0 43 20 7f 03 00 00 00 f7 Request parameter value (Data List p. 56) 0xF0 0x43 0x3? 0x7F 0x03 ah am al 0xF7 0x3?: Device Number ah: Address High am: Address Mid al: Address Low f0 43 30 7f 03 00 00 00 f7 muse-4.0.0/src/share/instruments/Yamaha-Motif-Rack.idf000066400000000000000000002127601404105311000225630ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Yamaha-Motif.idf000066400000000000000000001315311404105311000217010ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Yamaha-P100.idf000066400000000000000000000043601404105311000212420ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Yamaha-P50m.idf000066400000000000000000000062261404105311000213460ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Yamaha-PSR275.idf000077500000000000000000001123441404105311000215310ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Yamaha-PSR530.idf000066400000000000000000001376431404105311000215310ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Yamaha-Rex50.idf000066400000000000000000000151571404105311000215330ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/Yamaha-S30_S80.idf000066400000000000000000003654071404105311000216350ustar00rootroot00000000000000 7e 7f 09 01 Dr BdVar5 Dr SdPcBH Dr SdWdH Dr SdVar1 Dr BdVar3 Dr SdRoll Dr SdBldH Dr SdDry Dr Stick Dr BdVar4 Dr SdPcBH Dr BdVar1 Dr BdPop Dr C.Rim2 Dr SdHvy Pc Clap Dr SdGM Dr Tom2L Dr HHCl5 Dr Tom2L Dr HHPdl2 Dr Tom2L Dr HHOpn Dr Tom2H Dr Tom2H Dr Crash Dr Tom2H Dr Ride Dr China Dr Cup Pc Tmborn Dr Crash2 Pc Cowbel Dr Crash2 Pc VbSlp Dr Cup2 Pc BongoH Pc BongoL Pc CngSlp Pc CongaH Pc CongaL Pc TmblsH Pc TmblsL Pc AgogoL Pc AgogoL Pc Cabasa Pc Maracs Dr China Dr Crash2 Pc TrangL Cp FngCym Pc Claves Pc Wdblck Pc Wdblck Cp Timpni Cp Timpni Pc TrangL Pc TrangL Pc Shaker Pc JnglBl Dr BdStd Dr SdRim1 Dr SdBldH Dr SdHvy Dr SdVar1 Dr SdVar2 Dr SdAmb1 Dr SdFsnH Dr SdAmb3 Dr SdPclH Dr BdHby Dr BdHby Dr BdLoJz Dr Crash Dr BdLoJz Pc CONG Dr SdBldH Pc CONG Dr EuroBD Dr T8BdBm Dr T9BD2 Dr T9BD3 Dr T9Rim Dr T9SD2 Dr T8Rmst Dr T9SD5 Dr T9TomL Dr T9HHC1 Dr T9TomM Dr T9HHC2 Dr T9TomH Dr T9HHO1 Dr T9TomH Dr T9HHO2 Dr T9Crsh Dr T9Ride Dr T9Clap Pc AnvlPp Dr T9BD1 Dr SdPcBH Pc WdBlk Pc AnvlPp Pc CONG Pc AnvlHt Pc StlClp Pc Ripper Pc CONG Dr T8CngH Dr T8CngH Dr T8CngH Dr T8CngH Dr T8Crsh Dr T8HHCl Dr T8HHOp Pc TrangL Dr T8Mrcs Pc TrangL Pc Shaker Pc AnvlHt Pc AnvlHt Dr T8Clve Pc VoxDrm Dr DstKik Dr T8Crsh Pc JnglBl Pc Tmborn Pc Tmborn Fx BigSyn Fx BigSyn Fx Melow Fx VxAlk Fx VxAlk Fx VxAlk Dr T8SD1 Pc Whistl Dr SdPclH Dr T8BdBm Dr DstKik Dr BdVar5 Dr BdVar4 Dr T8BdBm Dr T9BD1 Dr SdBldH Dr SdPclH Dr SdPrmH Dr T8SD1 Dr SdBkBt Dr SdBrsL Dr T8BdBm Dr BdAna Dr C.Rim2 Dr SdRim1 Dr T8Clap Dr SdPcBH Dr TomF Dr HHCL2 Dr TomF Dr T8HHCl Dr TomL Dr HHOpn Dr TomM Dr TomHL Dr Crash Dr TomHH Dr Ride2 Dr China Dr Cup2 Pc Tmborn Dr Crash2 Pc CONG Dr Crash2 Pc VbSlp Dr Ride Fx AHoseL Fx AHoseR Pc CngSlp Pc CongaH Pc CongaL Pc TmblsH Pc TmblsL Pc AnvlPp Pc AnvlPp Pc Shaker Pc Maracs Pc Scrch1 Dr T8Bd Pc StlClp Dr SdFsnH Dr T9Clap Dr SdVar2 Pc JnglBl Dr T9HHC1 Pc TrangL Dr T9HHC2 Pc TrangL Dr T9HHO1 Fx HiLo Fx Eery Dr T9Crsh Dr China Fx VoxBel Fx Melow Wv MogBs1 Pc Tmborn Pc Scrch2 Dr T9Ride Fx Eery FX HH Hit Fx TrnCho Fx TrnCho Dr T8BdBm Dr T8CngM Dr T8Bd Dr T8CngH Dr T9BD2 Dr T9BD2 Pc CONG Dr T9SD1 Pc CONG Dr EuroBD Dr EuroBD Dr T9BD2 Dr T9BD3 Dr T9Rim Dr T9SD1 Dr DstKik Dr T9SD4 Dr T9TomL Dr T9HHC1 Dr T9TomM Dr T9HHC2 Dr T9TomH Dr T9HHO1 Dr T9TomH Dr T9HHO2 Dr T9Crsh Dr T9Ride Dr T9Clap Pc AnvlPp Dr T9BD1 Dr SdPcBH Pc WdBlk Pc AnvlPp Pc Ripper Pc AnvlHt Pc StlClp Dr T8BdBm Pc CONG Dr T8CngH Dr T8CngH Dr T8CngH Dr T8CngH Dr T8Crsh Dr T8HHCl Dr T8HHOp Pc TrangL Dr T8Mrcs Pc TrangL Fx WndChm Fx BelAir Fx BigSyn Fx Electr FX HH Hit Fx VxAlk Fx VxAlk Fx HiLo Fx HiLo Fx VoxBel Fx VoxBel Fx Eery Wv FMBs1 Wv FMBs1 Wv FMBs1 Wv FMBs1 Wv FMBs1 Wv FMBs1 Wv FMBs1 Wv FMBs1 Wv FMBs1 Wv FMBs1 Wv FMBs1 Wv FMBs1 Wv FMBs1 Dr T8Bd Dr BdVar4 Pc CongaL Dr BdAna Dr T9BD3 Dr SdAmb2 Dr SdRim2 Dr T8SD1 Dr T8SD3 Dr SdBrsH Dr SdAmb3 Dr EuroBD Dr T9BD3 Fx HiLo Dr T9SD4 Dr T8Clap Pc WdBlk Wv Noise Dr HHCl5 Wv Noise Dr T9HHC1 Wv Noise Dr T9HHO1 Dr Tom2L Dr Tom2H Dr T9Crsh Dr Tom2H Fx Eery Dr China Dr Cup2 Pc Tmborn Dr Crash Wv MogBs1 Dr Crash2 Pc VbSlp Dr Ride Fx VxAlk FX HH Hit Pc TmblsH Pc CongaH Pc CongaL Fx BelAir Fx Electr Pc AnvlPp Pc AnvlPp Pc Shaker Pc Maracs Pc Bltree Dr T8Bd Fx VoxBel Dr SdAmb2 Dr T9Clap Dr SdRim1 Fx MltPno Dr T8HHCl Pc AnvlHt Dr T9HHC2 Pc AnvlHt Dr T8HHOp Fx Eery Fx Eery Dr T9Crsh Dr China Fx VoxBel Dr T8Crsh En VxNoiz Pc Tmborn Pc Ripper Dr T9Ride Dr T8CngL Dr T8CngM Dr TmJzH Dr T8HHOp Pc Claves Dr SdBrsL Dr SdSwep Dr SdBrsH Dr SdSwep Dr SdRoll Pc Castnt Dr SdBrsL Dr Stick Dr BdVar3 Dr SdBrsH Dr BdVar4 Dr BdLoJz Dr SdBrsH Dr SdBrsL Pc Clap Dr SdBrsS Dr TmJzL Dr HHCl4 Dr TmJzL Dr HHPdl Dr TmJzL Dr HHOpn Dr TmJzM Dr TmJzH Dr Crash Dr TmJzH Dr Ride Dr Ride2 Dr Cup Pc Tmborn Dr Crash2 Pc Cowbel Dr Crash2 Dr Ride Dr Cup2 Pc BongoH Pc BongoL Pc CngSlp Pc CongaH Pc CongaL Pc TmblsH Pc TmblsL Pc AgogoL Pc AgogoL Pc Cabasa Pc Maracs Pc Whistl Pc Whistl Pc GiroCl Pc GuiroL Pc Claves Pc Wdblck Pc Wdblck Pc CuicaL Pc CuicaH Pc TrangL Pc TrangL Pc Shaker Pc JnglBl Pc Bltree Pc Claves Dr SdBrsL Dr SdSwep Dr SdBrsH Dr SdSwep Dr SdRoll Pc Castnt Dr SdDry Dr Stick Dr BdVar3 Dr SdPcBH Dr BdVar4 Dr BdHby Dr C.Rim1 Dr SdGM Pc Clap Dr SdGM Dr TomL Dr HHCl5 Dr TomL Dr HHPdl2 Dr TomM Dr HHOpn2 Dr TomM Dr TomHL Dr Crash Dr TomHH Dr Ride Dr China Dr Cup Pc Tmborn Dr Crash2 Pc Cowbel Dr Crash2 Pc VbSlp Dr Cup2 Pc BongoH Pc BongoL Pc CngSlp Pc CongaH Pc CongaL Pc TmblsH Pc TmblsL Pc AgogoL Pc AgogoL Pc Cabasa Pc Maracs Pc Whistl Pc Whistl Pc GiroCl Pc GuiroL Pc Claves Pc Wdblck Pc Wdblck Pc CuicaL Pc CuicaH Pc TrangL Pc TrangL Pc Shaker Pc JnglBl Pc Bltree Dr SdBkBt Dr SdBldS Dr SdVar1 Dr SdSonD Dr SdRim1 Dr SdWdH Dr SdBldH Dr SdPcBH Dr SdLdwH Dr BdPop Dr BdHby Dr BdVar5 Pc Claves Dr SdBrsL Dr SdSwep Dr SdBrsH Dr SdSwep Dr SdRoll Pc Castnt Dr T8SD4 Dr T8Rmst Dr BdAna Dr T8SD3 Dr T8BdBm Dr T8Bd Dr T8Side Dr T8SD1 Dr T8Clap Dr T8SD2 Dr T8Tom Dr T8HHCl Dr T8Tom Dr T8HHOp Dr T8Tom Dr T8HHOp Dr T8Tom Dr T8Tom Dr T8Crsh Dr T8Tom Dr T8Crsh Dr China Dr Cup Pc Tmborn Dr Crash2 Dr T8Cwbl Dr Crash2 Pc VbSlp Dr Ride2 Pc BongoH Pc BongoL Dr T8CngH Dr T8CngM Dr T8CngL Pc TmblsH Pc TmblsL Pc AgogoL Pc AgogoL Pc Cabasa Dr T8Mrcs Pc Whistl Pc Whistl Pc GiroCl Pc GuiroL Dr T8Clve Pc Wdblck Pc Wdblck Pc CuicaL Pc CuicaH Pc TrangL Pc TrangL Pc Shaker Pc JnglBl Pc Bltree Dr T8BdBm Dr T8SD2 Dr T8SD1 Dr T8SD3 Dr T8SD4 Dr T9SD4 FX HH Hit Dr T9SD2 Dr T8Rmst Dr T8Bd Dr T9SD5 Dr T9BD3 Dr T9BD1 Dr C.Rim1 Dr T9SD1 Dr T9Clap Dr T9SD3 Dr T9TomL Dr T9HHC2 Dr T9TomL Dr T9HHO2 Dr T9TomM Dr T9HHO1 Dr T9TomM Dr T9TomH Dr T9Crsh Dr T9TomH Dr T9Ride Dr T8Crsh Dr T9Ride Pc Tmborn Dr T8Crsh Dr T8Cwbl Dr T9Crsh Dr DstKik Dr T8Crsh Dr T8CngH Dr T8CngL Dr T8CngM Wv MogBs1 Wv Late-S Dr SdBkBt Dr SdBkBt Dr T8Cwbl Pc AgogoL Pc Cabasa Dr T8Mrcs Fx HiLo Fx HiLo Pc AnvlHt Pc Ripper Dr T8Clve Dr T8Clve Pc Castnt Pc Scrch1 Pc Scrch2 Pc TrangL Pc TrangL Pc Shaker Pc JnglBl Pc Bltree Dr SdRim1 Dr SdPclH Pc Clap Dr SdPrmH Dr BdAna Dr T8HHCl Dr T9BD2 Dr HHCl5 Dr BdVar1 Dr T8HHOp Dr EuroBD Dr BdHby Dr BdVar5 Dr SdBrsL Dr SdSwep Dr SdBrsH Dr SdSwep Dr SdRoll Wv Noise Dr SdSonA Dr Stick Dr BdVar5 Dr SdPcBH Dr BdVar2 Dr BdLoJz Dr C.Rim2 Dr SdGM Pc Clap Dr SdWdH Dr TomF Dr HHCl1 Dr TomL Dr HHPdl Dr TomL Dr HHOpn Dr TomM Dr TomHL Dr Crash Dr TomHH Dr Ride Dr China Dr Cup Pc Tmborn Dr Crash2 Dr T8Cwbl Dr Crash2 Pc VbSlp Dr T8Crsh Pc BongoH Pc BongoL Pc CngSlp Pc CongaH Pc CongaL Pc TmblsH Pc TmblsL Pc AgogoL Pc AgogoL Pc Cabasa Pc Maracs Pc Whistl Pc Whistl Pc GiroCl Pc GuiroL Pc Claves Pc Wdblck Pc Wdblck Pc CuicaL Pc CuicaH Pc TrangL Pc TrangL Pc Shaker Pc JnglBl Pc Bltree Dr DstKik Dr DstKik Dr DstKik Dr DstKik Dr DstKik Dr DstKik Dr DstKik Dr DstKik Dr DstKik Dr DstKik Dr DstKik Dr DstKik Switch General Midi mode on Received only when SYSTEM Receive GM/XG on is set to on in PERFORMANCE MODE. The Part values will be reset according to the SYSTEM Internal Part settings. f0 7e 7f 09 01 f7 Switch XG mode on Received only when SYSTEM Receive GM/XG on is set to on in PERFORMANCE MODE. The Part values will be reset according to the SYSTEM Internal Part settings. Takes about 170ms to execute. f0 43 10 4c 00 00 7e 00 f7 Request Device Number Code from the instrument. The instrument answers with a "Identity Reply" and returns the Device ID 0x23 0x04. 0xF0 0x7E 0x0? 0x06 0x01 0xF7 0x0?: Device number (any number) f0 7e 00 06 01 f7 Reply message to an "Identity Request" 0xF0 0x7E 0x7F 0x06 0x02 0x43 0x00 0x41 dd dd 0x00 0x00 0x00 0x7F 0xF7 dd dd: Device Number Code (always 0x23 0x04) f0 7e 7f 06 02 43 00 41 23 04 00 00 00 7f f7 Overall volume of all parts 0xF0 0x7F 0x7F 0x04 0x01 ll mm 0xF7 ll: Volume LSB (Ignored) mm: Volume MSB f0 7f 7f 04 01 00 7f f7 Remotely activate or deactivate panel buttons 0xF0 0x43 0x1? 0x64 ah am al dd ... 0xF7 0x1?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) ah: Address High am: Address Mid al: Address Low dd: Parameter data (variable length) f0 43 10 64 00 00 00 00 f7 Bulk dump of all settings (Data List p. 29ff) 0xF0 0x43 0x0? 0x64 bh bl ah am al dd ... cc 0xF7 0x0?: Device Number (Transmit: 0x10, Receive 0x10 - 0x1F) bh: Byte Count MSB bl: Byte Count LSB ah: Address High am: Address Mid al: Address Low dd: Parameter data (variable length) cc: Checksum f0 43 00 64 00 00 00 00 00 00 00 f7 Request bulk dump (Data List p. 29ff) 0xF0 0x43 0x2? 0x64 ah am al 0xF7 0x2?: Device Number ah: Address High am: Address Mid al: Address Low f0 43 20 64 00 00 00 f7 Request parameter value (Data List p. 29ff) 0xF0 0x43 0x3? 0x64 ah am al 0xF7 0x3?: Device Number ah: Address High am: Address Mid al: Address Low f0 43 30 64 00 00 00 f7 muse-4.0.0/src/share/instruments/Yamaha-S90.idf000066400000000000000000001225701404105311000212010ustar00rootroot00000000000000 Master number 0 select 0x43 0x10 0x6b 0xa 0x0 0x0 0x0 Switch to SeqPlay Mode 0x43 0x10 0x6b 0xa 0x0 0x1 0x3 Local Off 0x43 0x10 0x6b 0x0 0x0 0x9 0x0 0x43 0x10 0x6b 0xa 0x0 0x0 0x0 0x43 0x10 0x6b 0xa 0x0 0x1 0x3 0x43 0x10 0x6b 0x0 0x0 0x9 0x0 muse-4.0.0/src/share/instruments/ZynAdd-1_4.idf000066400000000000000000000017421404105311000211770ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/emuproteus2000.idf000066400000000000000000002020071404105311000221340ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/gm.idf000066400000000000000000000264041404105311000200320ustar00rootroot00000000000000 Switch General Midi mode on 7e 7f 09 01 7e 7f 09 01 Acoustic Bass Drum Bass Drum 1 Side Stick Acoustic Snare Hand Clap Electric Snare Low Floor Tom Closed Hi-Hat High Floor Tom Pedal Hi-Hat Low Tom Open Hi-Hat Low-Mid Tom Hi-Mid Tom Crash Cymbal 1 High Tom Ride Cymbal 1 Chinese Cymbal Ride Bell Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Hi Bongo Low Bongo Mute Hi Conga Open Hi Conga Low Conga High Timbale Low Timbale High Agogo Low Agogo Cabasa Maracas Short Whistle Long Whistle Short Guiro Long Guiro Claves Hi Wood Block Low Wood Block Mute Cuica Open Cuica Mute Triangle Open Triangle muse-4.0.0/src/share/instruments/gm2.idf000066400000000000000000001034241404105311000201120ustar00rootroot00000000000000 Switch General Midi Level 2 mode on 7e 7f 09 03 7e 7f 09 03 High Q Slap Scratch Push [EXC7] Scratch Pull [EXC7] Sticks Square Click Metronome Click Metronome Bell Acoustic Bass Drum Bass Drum 1 Side Stick Acoustic Snare Hand Clap Electric Snare Low Floor Tom Closed Hi-hat [EXC1] High Floor Tom Pedal Hi-hat [EXC1] Low Tom Open Hi-hat [EXC1] Low-Mid Tom High Mid Tom Crash Cymbal 1 High Tom Ride Cymbal 1 Chinese Cymbal Ride Bell Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibra-slap Ride Cymbal 2 High Bongo Low Bongo Mute Hi Conga Open Hi Conga Low Conga High Timbale Low Timbale High Agogo Low Agogo Cabasa Maracas Short Whistle [EXC2] Long Whistle [EXC2] Short Guiro [EXC3] Long Guiro [EXC3] Claves Hi Wood Block Low Wood Block Mute Cuica [EXC4] Open Cuica [EXC4] Mute Triangle [EXC5] Open Triangle [EXC5] Shaker Jingle Bell Bell Tree Castanets Mute Surdo [EXC6] Open Surdo [EXC6] Room Low Tom 2 Room Low Tom 1 Room Mid Tom 2 Room Mid Tom 1 Room Hi Tom 2 Room Hi Tom 1 Power Kick Drum Power Snare Drum Power Low Tom 2 Power Low Tom 1 Power Mid Tom 2 Power Mid Tom 1 Power Hi Tom 2 Power Hi Tom 1 Electric Bass Drum Electric Snare 1 Electric Snare 2 Electric Low Tom 2 Electric Low Tom 1 Electric Mid Tom 2 Electric Mid Tom 1 Electric Hi Tom 2 Electric Hi Tom 1 Reverse Cymbal Analog Bass Drum Analog Rim Shot Analog Snare 1 Analog Low Tom 2 Analog CHH 1 [EXC1] Analog Low Tom 1 Analog CHH 2 [EXC1] Analog Mid Tom 2 Analog OHH [EXC1] Analog Mid Tom 1 Analog Hi Tom 2 Analog Cymbal Analog Hi Tom 1 Analog Cowbell Analog High Conga Analog Mid Conga Analog Low Conga Analog Maracas Analog Claves Jazz Kick 2 Jazz Kick 1 JazzKick 2 JazzKick 1 Brush Tap Brush Slap Brush Swirl Closed Hi-hat 2 [EXC1] Pedal Hi-hat [EXC1] Open Hi-hat 2 [EXC1] Ride Cymbal 1 Concert BD 2 Concert BD 1 Concert SD Castanets Concert SD Timpani F Timpani F# Timpani G Timpani G# Timpani A Timpani A# Timpani B Timpani c Timpani c# Timpani d Timpani d# Timpani e Timpani f Concert Cymbal 2 Concert Cymbal 1 Applause High Q Slap Scratch Push [EXC7] Scratch Pull [EXC7] Sticks Square Click Metronome Click Metronome Bell Guitar Fret Guitar Cutting Noise Up Guitar Cutting Noise Down String Slap of Double Bass Fl. Key Click Laughing Scream Punch Heart Beat Footsteps 1 Footsteps 2 Applause Door Creaking Door Scratch Wind Chimes Car-Engine Car-Stop Car-Pass Car-Crash Siren Train Jetplane Helicopter Starship Gun Shot Machine Gun Lasergun Explosion Dog Horse-Gallop Birds Rain Thunder Wind Seashore Stream Bubble muse-4.0.0/src/share/instruments/gs.idf000066400000000000000000001004251404105311000200340ustar00rootroot00000000000000 Switch General Midi mode on 7e 7f 09 01 Switch GS mode on 0x41 0x10 0x42 0x12 0x40 0x00 0x7f 0x00 0x41 7e 7f 09 01 41 10 42 12 40 00 7f 00 41 High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Kick Drum 2 Kick Drum 1 Snare Drum 1 Snare Drum 2 Low Tom 2 Low Tom 1 Mid Tom 2 Mid Tom 1 High Tom 2 High Tom 1 Shaker Jingle Bell Belltree Castanets Mute Surdo Open Surdo High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Kick Drum 2 Kick Drum 1 Snare Drum 1 Snare Drum 2 Room Low Tom 2 Room Low Tom 1 Room Mid Tom 2 Room Mid Tom 1 Room High Tom 2 Room High Tom 1 Shaker Jingle Bell Belltree Castanets Mute Surdo Open Surdo High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Kick Drum 2 Mondo Kick Gated Snare Snare Drum 2 Room Low Tom 2 Room Low Tom 1 Room Mid Tom 2 Room Mid Tom 1 Room High Tom 2 Room High Tom 1 Shaker Jingle Bell Belltree Castanets Mute Surdo Open Surdo High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Kick Drum 2 Elec. Bass Drum Elec. Snare Gated Snare Elec. Low Tom 2 Elec. Low Tom 1 Elec. Mid Tom 2 Elec. Mid Tom 1 Elec. High Tom 2 Elec. High Tom 1 Reverse Cymbal Shaker Jingle Bell Belltree Castanets Mute Surdo Open Surdo High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Kick Drum 2 808 Bass Drum 808 Rim Shot 808 Snare Drum Snare Drum 2 808 Low Tom 2 808 Closed Hi-Hat 808 Low Tom 1 808 Pedal Hi-Hat 808 Tom 2 808 Open Hi-Hat 808 Mid Tom 1 808 High Tom 2 808 Cymbal 808 High Tom 1 Reverse Cymbal 808 Cowbell 808 High Conga 808 Mid Conga 808 Low Conga 808 Maracas Shaker Jingle Bell Belltree Castanets Mute Surdo Open Surdo High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Jazz Bass Drum 2 Jazz Bass Drum 1 Snare Drum 1 Snare Drum 2 Low Tom 2 Low Tom 1 Mid Tom 2 Mid Tom 1 High Tom 2 High Tom 1 Shaker Jingle Bell Belltree Castanets Mute Surdo Open Surdo High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Jazz Bass Drum 2 Jazz Bass Drum 1 Brush Tap Brush Slap Brush Swirl Low Tom 2 Low Tom 1 Mid Tom 2 Mid Tom 1 High Tom 2 High Tom 1 Shaker Jingle Bell Belltree Castanets Mute Surdo Open Surdo Closed Hi-Hat Pedal Hi-Hat Open Hi-Hat Ride Cymbal Sticks Square Click Metronome Click Metronome Bell Concert Bass Drum 2 Concert Bass Drum 1 Concert Snare Drum Castanets Concert Snare Drum Timpani F Timpani F# Timpani G Timpani G# Timpani A Timpani A# Timpani B Timpani C Timpani C# Timpani D Timpani D# Timpani E Timpani F Concert Cymbal 2 Concert Cymbal 1 Shaker Jingle Bell Belltree Castanets Mute Surdo Open Surdo High Q Slap Scratch Push Scratch Pull Sticks Square Click Metronome Click Metronome Bell Guitar Fret Noise Guitar Cut Noise Up Guitar Cut Noise Down Double Bass String Slap Flute Key Click Laughing Screaming Punch Heartbeat Footsteps 1 Footsteps 2 Applause Door Creaking Door Closing Scratch Wind Chimes Car Engine Car Brakes Car Passing Car Crash Siren Train Jet Plane Helicopter Starship Gun Shot Machine Gun Laser Gun Explosion Dog Bark Horse Gallop Birds Tweet Rain Thunder Wind Seashore Stream Bubble Quijada Laughing Screaming Punch Heartbeat Footsteps 1 Footsteps 2 Applause Door Creaking Door Closing Scratch Wind Chimes Car Engine Car Brakes Car Passing Car Crash Siren Train Jet Plane Helicopter Starship Gun Shot Machine Gun Laser Gun Explosion Dog Bark Horse Gallop Birds Tweet Rain Thunder Wind Seashore Stream Bubble muse-4.0.0/src/share/instruments/ns5r.idf000066400000000000000000001136171404105311000203210ustar00rootroot00000000000000 muse-4.0.0/src/share/instruments/xg.idf000066400000000000000000001764561404105311000200620ustar00rootroot00000000000000 Switch General Midi mode on 7e 7f 09 01 Switch XG mode on 0x43 0x10 0x4c 0x00 0x00 0x7e 0x00 7e 7f 09 01 43 10 4c 00 00 7e 00 Mute Surdo Open Surdo High Q Whip Slap Scratch Push Scratch Pull Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl L Brush Slap Brush Swirl H Snare Roll Castanet Snare L Sticks Bass Drum L Open Rim Shot Bass Drum M Bass Drum H Snare M Snare H Shaker Jingle Bell Belltree Mute Surdo Open Surdo High Q Whip Slap Scratch Push Scratch Pull Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl L Brush Slap Brush Swirl H Snare Roll 2 Castanet Snare L 2 Sticks Bass Drum L Open Rim Shot 2 Bass Drum M 2 Bass Drum H 2 Snare M 2 Snare H 2 Shaker Jingle Bell Belltree Mute Surdo Open Surdo High Q Whip Slap Scratch Push Scratch Pull Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl L Brush Slap Brush Swirl H Snare Roll Castanet Snare L Sticks Bass Drum L Open Rim Shot Bass Drum M Room Bass Drum Room Snare L Room Snare H Room Tom 1 Room Tom 2 Room Tom 3 Room Tom 4 Room Tom 5 Room Tom 6 Shaker Jingle Bell Belltree Mute Surdo Open Surdo High Q Whip Slap Scratch Push Scratch Pull Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl L Brush Slap Brush Swirl H Snare Roll Castanet Rock Snare M Sticks Rock Bass Drum M Open Rim Shot Bass Drum H 3 Rock Bass Drum Rock Snare L Rock Snare Rim Rock Tom 1 Rock Tom 2 Rock Tom 3 Rock Tom 4 Rock Tom 5 Rock Tom 6 Shaker Jingle Bell Belltree Mute Surdo Open Surdo High Q Whip Slap Scratch Push Scratch Pull Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl L Brush Slap Reverse Cymbal Snare Roll High Q Snare M Sticks Bass Drum H 4 Open Rim Shot Rock Bass Drum Gated Bass Drum Rock Snare L Rock Snare H Elec. Tom 1 Elec. Tom 2 Elec. Tom 3 Elec. Tom 4 Elec. Tom 5 Elec. Tom 6 Scratch Push Scratch Pull Shaker Jingle Bell Belltree Mute Surdo Open Surdo High Q Whip Slap Scratch Push Scratch Pull Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl L Brush Slap Reverse Cymbal Snare Roll High Q Rock Snare H Sticks Bass Drum M Open Rim Shot Analog Bass Drum L Analog Bass Drum H Analog Side Stick Analog Snare L Analog Snare H Analog Tom 1 Analog Hi-Hat Closed 1 Analog Tom 2 Analog Hi-Hat Closed 2 Analog Tom 3 Analog Hi-Hat Open Analog Tom 4 Analog Tom 5 Analog Cymbal Analog Tom 6 Analog Cowbell Analog Conga H Analog Conga M Analog Conga L Analog Maracas Analog Claves Scratch Push Scratch Pull Shaker Jingle Bell Belltree Mute Surdo Open Surdo High Q Whip Slap Scratch Push Scratch Pull Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl L Brush Slap Brush Swirl H Snare Roll Castanet Snare L Sticks Bass Drum L Open Rim Shot Bass Drum M Jazz Bass Drum Snare M Snare H Jazz Tom 1 Jazz Tom 2 Jazz Tom 3 Jazz Tom 4 Jazz Tom 5 Jazz Tom 6 Shaker Jingle Bell Belltree Mute Surdo Open Surdo High Q Whip Slap Scratch Push Scratch Pull Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl L Brush Slap Brush Swirl H Snare Roll Castanet Brush Slap L Sticks Bass Drum L Open Rim Shot Bass Drum M Soft Bass Drum Brush Slap Brush Tap Brush Tom 1 Brush Tom 2 Brush Tom 3 Brush Tom 4 Brush Tom 5 Brush Tom 6 Shaker Jingle Bell Belltree Mute Surdo Open Surdo High Q Whip Slap Scratch Push Scratch Pull Finger Snap Click Noise Metronome Click Metronome Bell Seq Click L Seq Click H Brush Tap Brush Swirl L Brush Slap Brush Swirl H Snare Roll Castanet Snare L Sticks Bass Drum L 2 Open Rim Shot Gran Cassa Gran Cassa Mute Marching Snare H Marching Snare M Jazz Tom 1 Jazz Tom 2 Jazz Tom 3 Jazz Tom 4 Jazz Tom 5 Hand Cym. Open L Jazz Tom 6 Hand Cym. Closed L Hand Cym. Open H Hand Cym. Closed H Shaker Jingle Bell Belltree Guitar Cutting Noise Guitar Cutting Noise 2 Dist. Cut Noise String Slap Bass Slide Pick Scrape FL.Key Click Rain Thunder Wind Stream Bubble Feed Dog Horse Gallop Bird 2 Kitty Growl Haunted Ghost Maou Dial Tone Door Creaking Door Slam Scratch Scratch 2 Windchime Telephone Ring 2 Engine Start Tire Screech Car Passing Crash Siren Train Jetplane Starship Burst Noise Coaster SbMarine Laughing Screaming Punch Heartbeat Footsteps Applaus2 Machine Gun Laser Gun Explosion Firework muse-4.0.0/src/share/instruments/yam_mo6_v4.idf000066400000000000000000002540651404105311000214150ustar00rootroot00000000000000 muse-4.0.0/src/share/locale/000077500000000000000000000000001404105311000156015ustar00rootroot00000000000000muse-4.0.0/src/share/locale/CMakeLists.txt000066400000000000000000000040051404105311000203400ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB TRANSLATIONS_FILES *.ts) if (UPDATE_TRANSLATIONS) qt5_create_translation(QM_FILES ${FILES_TO_TRANSLATE} ${TRANSLATIONS_FILES}) else (UPDATE_TRANSLATIONS) QT5_ADD_TRANSLATION(QM_FILES ${TRANSLATIONS_FILES}) endif (UPDATE_TRANSLATIONS) add_custom_target (translations ALL DEPENDS ${QM_FILES}) # If we are updating the translations, make sure that these targets # are scanned first if (UPDATE_TRANSLATIONS) add_dependencies(translations muse arranger cliplist ctrl driver instruments liste marker master midiedit mixer mplugins remote waveedit widgets awl deicsonze fluid fluidsynth libsynti organ s1 simpledrums2 vam components ) endif (UPDATE_TRANSLATIONS) install(FILES ${QM_FILES} DESTINATION ${MusE_SHARE_DIR}/locale ) muse-4.0.0/src/share/locale/muse_cs.ts000066400000000000000000034474221404105311000176270ustar00rootroot00000000000000 @default Add Midi Track Vytvořit stopu MIDI Add Drum Track Vytvořit stopu bicích Add Old Style Drum Track Vytvořit stopu bicích ve starém stylu Add New Style Drum Track Vytvořit stopu bicích v novém stylu Add Wave Track Vytvořit stopu Wave Add Audio Output Vytvořit výstup zvuku Add Audio Group Vytvořit skupinu zvuku Add Audio Input Vytvořit vstup zvuku Add Aux Send Vytvořit Aux Send Add Synth Přidat syntetizátor Select project directory Vybrat projektový adresář Warning: No output devices! Varování: Žádná výstupní zařízení! Open midi config... Otevřít nastavení MIDI... Output port/device Výstupní přípojka/zařízení default Výchozí Empty ports Prázdné přípojky <none> <Žádný> Route Tok signálu channel="%1" kanál="%1" channelMask="%1" KanalMaske="%1" dest Cíl name="%1"/ Název="%1" Bad timing Špatné načasování Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. Also please check console output for any further error messages. Kmitočet zdroje časování je %1hz, což je pod doporučeným minimem: 500hz! To by u MIDI mohlo vést až ke slyšitelným problémům s časováním. Podívejte se, prosím, na často kladené otázky na stránkách http://muse-sequencer.org na řešení. Také se, prosím, podívejte na výstup konzole kvůli jakýmkoli dalším chybovým hláškám. Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. Also please check console output for any further error messages Kmitočet zdroje časování je %1hz, což je pod doporučeným minimem: 500hz! To by u MIDI mohlo vést až ke slyšitelným problémům s časováním. Podívejte se, prosím, na často kladené otázky na stránkách http://muse-sequencer.org na řešení. Také se, prosím, podívejte na výstup konzole kvůli jakýmkoli dalším chybovým hláškám AboutBox AboutBox O programu Version 2 pre-alpha Verze 2 (C) Copyright 1999-2010 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Autorské právo 1999-2010 Werner Schweer a další. Podívejte se na stránky http://www.muse-sequencer.org na nové verze a kvůli více informacím. Zveřejněno pod GNU Public License Version 2 Verze 2 (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Autorské právo 1999-2012 Werner Schweer a další. Podívejte se na stránky http://www.muse-sequencer.org na nové verze a zde hledejte další informace. Zveřejněno pod GNU Public License (C) Copyright 1999-2014 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Autorské právo 1999-2014 Werner Schweer a další. Podívejte se na stránky http://www.muse-sequencer.org na nové verze a zde hledejte další informace. Zveřejněno pod GNU Public License Version info (replaced programmatically) Informace o verzi (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Autorské právo 1999-2017 Werner Schweer a další. Podívejte se na stránky http://www.muse-sequencer.org na nové verze a zde hledejte další informace. Zveřejněno pod GNU Public License {1999-2015 ?} System information: Informace o systému: TextLabel Textový štítek MusE MusE (C) Copyright 1999-2020 the MusE development team. See https://muse-sequencer.github.io for new versions and more information. Published under the GNU Public License. &Keep On Rocking! &Zůstávejte ve spojení! Alt+K Alt+K Internals System information (useful for debugging): AppearanceDialogBase MusE: Appearance settings MusE: Nastavení vzhledu Arranger Aranžér Parts Části show names Ukázat názvy show events Ukázat události show Cakewalk Style Ukázat styl Cakewalk y-stretch Úsek y Events Události note on Nota zapnuta poly pressure Vícenásobné stisknutí tlačítka controller Ovladač aftertouch Dodělávka po pitch bend Ohyb výšky tónu program change Změna programu special Zvláštní Wave Drawing in Parts O&nly outline Background picture Obrázek pozadí add Přidat remove Odstranit clear Smazat show snap grid Ukázat magnetickou mřížku Colors Barvy Items Položky Palette Paleta add to palette Přidat do palety B B S S H H V V G G R R Color name: Název barvy: Global opacity Celková neprůhlednost Style/Fonts Styl a písmo QT Theme Podoba Qt Windows Windows MusE MusE Metal Kov Norwegian Wood Norské dřevo Platinum Platina CDE CDE Motif Motif Motif Plus Motif Plus May require restarting MusE for best results Může kvůli nejlepšímu výsledku vyžadovat opětovné spuštění MusE Style Sheet: Styl: ... ... MusE color scheme Schéma barev MusE current settings Nynější nastavení Change Změnit Fonts Písma Family Rodina písma Size Velikost Font 1 Písmo 1 Font 2 Písmo 2 Font 3 Písmo 3 Font 0 Písmo 0 Bold Tučné Italic Kurzíva Icon size Custom cursor size Distribute current system font family to all other fonts System Font sizes for fonts 1-6 are determined at every program start, to optimally match the currently used system font. Disable if you want to set fixed sizes in the font settings below. Default scaling factors: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% Current theme If a user style sheet for one of the MusE color schemes exists and is selected, it will be merged with the default style sheet for this scheme. The user style sheet takes precedence and overwrites identical settings in the default style sheet. Cascade default and user theme style sheets Show icons in menus Use native standard dialogs Calculate and apply relative font sizes dynamically at program start Maximum aliased size, 0 = no alias Insert Vložit Font 4 Písmo 4 Font 5 Písmo 5 Show names Ukázat názvy Show e&vents Show Ca&kewalk style RMS/PEAK (&traditional) Add Přidat Remove Odstranit Clear Smazat Show snap grid Choose color... Add to palette Save all... Load all... Themes Motivy Font 6 Písmo 6 Size (pt) At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. Fill in optimal font sizes in relation to the system font Help Apply Použít Ok OK Cancel Zrušit ArrangerColumnsBase Configure arranger columns Nastavit sloupce aranžéru Columns: Sloupce: Name: Název: Controller type: Typ ovladače: Midi controller type Typ ovladače MIDI Affect CCs at Control7 Ovladač 7 Control14 Ovladač 14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Výška tónu Program Program H-Ctrl H-Ctrl Midi controller number high byte Vysoké bajtové číslo ovladače MIDI L-Ctrl L-Ctrl Midi controller number low byte Nízké bajtové číslo ovladače MIDI * wild card * affect CCs at Ovlivnit CCs při begin of song Začátek písně current position Nynější poloha &Add &Přidat &Delete S&mazat Done Hotovo AudioConverterSettingsBase Audio Converter Settings Available audio converters Offline settings... Realtime settings... Gui settings... Use default preferences (see Global Settings) Preferences When only samplerate conversion is required, use: When time stretching or pitch shifting is required, use: OK OK Cancel Zrušit Awl::MidiVolEntry off Vypnuto db dB Awl::VolEntry off Vypnuto ChooseSysexBase Dialog Dialog Hex: Šestnáctkový: TextLabel Textový štítek Comment: Poznámka: &OK &OK &Cancel Z&rušit ClipListEditorBase MusE: ClipList MusE: Seznam ukázek Name Název Refs Odkazy Samplerate Vzorkovací kmitočet Len Délka Data Data Status Clip Properties Vlastnosti ukázek Pos: Poloha: Len: Délka: CommentBase Form1 Formulář 1 Track Comment Poznámka ke stopě Track 1 Stopa 1 ConfigMidiFileBase MusE: Config Midi File Import/Export MusE: Nastavení zavedení/vyvedení souboru MIDI Import: Zavést: Default instrument: Výchozí nástroj: Device Name metas trump Port metas if both exist Názvy zařízení přetrumfnou Přípojky, pokud jsou oba Instrument Name metas trump Mode sysexes if both exist Názvy nástrojů přetrumfnou Režimy SysEx, pokud jsou oba Split tracks into parts, or one single part Rozdělit stopy na části, nebo jednu jedinou část Split tracks into &parts Rozdělit stopy na čá&sti Alt+P Alt+P Use new-style drum tracks Použít stopy bicích v novém stylu Use old-style drum tracks Použít stopy bicích ve starém stylu Export: Vyvést: 96 96 192 192 384 384 Enable extended smf format (currently not implemented) Povolit rozšířený formát smf (v současnosti neprovedeno) Running Status saves space by not repeating event status bytes. If this is off every event will have a status byte, increasing file size. Use Running &Status To turn a note off some devices can use note-offs or zero-velocity note-ons or both. When used with Running Status this setting saves space. It is safe to leave this off. Replace note-offs with &zero velocity note-ons Use &2-byte time signatures instead of standard 4 Použít &2-bajtová taktová označení namísto obvyklých 4 Alt+2 Alt+2 If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum note controllers. If disabled, drum maps are ignored during export. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. See "What's This ?" for more info. If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum controllers. Any Port overrides cause a separate track to be created (requires format 1). Any Channel overrides will still appear in the same track in the exported file, but upon re-import into an app such as MusE, MusE will treat that as a new track and create one. If disabled, drum maps are ignored. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. Upon re-importing into another app, it is up to the other app to redirect the particular drum ports, channels and notes to make the song sound like it was intended. Drum map overrides are unique to MusE. If re-importing into MusE, for faithful reproduction disable this setting and apply a drum map and some overrides later. If re-importing or opening in another midi player, enable this setting so that the player can have a chance at faithful reproduction. Some user adjustment may be required (choice of instruments, ports, etc). Apply drum map Port, Channel, ANote overrides If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. See "What's This ?" for more info. If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. The extra track is automatically created. In this mode all events on a track in the midi file are on the same channel. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. In this mode the track events can be on different channels. But if the file is re-imported into an app such as MusE, MusE will automatically create the extra track. Drum map Channel overrides go to a separate track Export instrument or mode: Mode sysexes Režim SysEx Instrument name metas Název nástroje Both Oba Port metas Přípojka Device name metas Název zařízení Export a Port/Device meta for format 0 Vyvedení a přípojka/zařízení pro formát 0 Copyright: Autorské právo: Format: Formát: Note: Format 0 uses the FIRST midi track's name/comment in the arranger Poznámka: Formát 0 používá název/poznámku PRVNÍ stopy MIDI v aranžéru Division: Rozdělení: Save space by replacing note-offs with &zero velocity note-ons Ušetřit místo nahrazením vypnutí not zapnutím not o &nulové síle tónu Alt+Z Alt+Z 0 (single track) 0 (jedna stopa) Import Export 1 (multiple tracks) 1 (více stop) Export port or device metas: &OK &OK &Cancel Z&rušit CopyOnWriteDialogBase Copy Wave Files Kopírovat soubor WAV Some sound files will be copied to the Project Directory, either because they are not writable or because more than one independent Wave Event shares them. (If you prefer instead that the Wave Events are inter-dependent, try using Clone Parts.) Multiple copies will be made in some cases. If no Project has been created yet, you will be asked to, giving another chance to cancel. Tyto soubory budou zkopírovány do adresáře s projektem, buď protože nejsou zapisovatelné nebo protože je sdílí více než jedna událost Wave Event. (Pokud místo toho dáváte přednost, aby byly události Wave mezi-závislé, vyzkoušejte klonování částí.) V některých případech bude uděláno více kopií. Pokud ještě nebyl vytvořen žádný projekt, budete dotázán, dostanete další možnost k zrušení. These files will be copied to the Project Directory: Tyto soubory budou zkopírovány do adresáře s projektem: CrescendoBase MusE: Crescendo/Decrescendo MusE: Crescendo/Decrescendo Range Rozsah Looped Events Události uvnitř smyčky &Looped Events Selected Looped Vybrané události uvnitř smyčky Parts Části Selected parts or c&urrent part All parts &in editor Values Hodnoty Start velocity Počáteční síla tónu % % End velocity Koncová síla tónu &Absolute Rela&tive Absolute Absolutní Relative Poměrná OK OK Cancel Zrušit DeicsOnzeGui Save configuration Uložit nastavení Critical Error Vážná chyba Cannot open file %1 Nelze otevřít soubor %1 Parsing error for file %1 Chyba při zpracování u souboru %1 Load category dialog Nahrát dialog skupiny Load set dialog Nahrát dialog nastavení Save set dialog Uložit dialog nastavení New category Nová skupina Delete category Smazat skupinu Load category Nahrát skupinu Save category Uložit skupinu Load set Nahrát nastavení Save set Uložit nastavení Delete set Smazat nastavení New subcategory Nová podskupina Delete subcategory Smazat podskupinu Load subcategory Nahrát podskupinu Save subcategory Uložit podskupinu New preset Nové přednastavení Delete preset Smazat přednastavení Load preset Nahrát přednastavení Save preset Uložit přednastavení No more category supported Žádná další skupina není podporována You can not add more categories Nelze přidat více skupin Do you really want to delete %1 ? Opravdu chcete smazat %1? &Yes &Ano &No &Ne No category selected Nevybrána žádná skupina You must first select a category. Nejprve musíte vybrat skupinu. Replace or add Nahradit nebo přidat %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? Předpokládá se, že %1 bude ovlivněn k vysoké bance číslo %2, ale na tomto místě už jedna je. Opravdu to chcete nahradit nebo to přidat do dalšího volného místa? &Replace &Nahradit &Add &Přidat Download error Chyba při stahování There is no more free category slot. Není žádné další volné místo pro skupinu. Save category dialog Uložit dialog skupiny No more subcategory supported Žádná další podskupina není podporována You can not add more subcategories Nelze přidat více podskupin No subcategory selected Nevybrána žádná podskupina You must first select a subcategory. Nejprve musíte vybrat podskupinu. Load subcategory dialog Nahrát dialog podskupiny %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? Předpokládá se, že %1 bude ovlivněn k nízké bance číslo %2, ale na tomto místě už jedna je. Opravdu to chcete nahradit nebo to přidat do dalšího volného místa? There is no more free subcategory slot. Není žádné další volné místo pro podskupinu. Save subcategory dialog Uložit dialog podskupiny No more preset supported Žádné další přednastavení není podporováno You can not add more presets Nelze přidat více přednastavení No preset selected Nevybráno žádné přednastavení You must first select a preset. Nejprve musíte vybrat přednastavení. Load preset dialog Nahrát dialog přednastavení %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? Předpokládá se, že %1 bude ovlivněn k programu číslo %2, ale na tomto místě už jedna je. Opravdu to chcete nahradit nebo to přidat do dalšího volného místa? There is no more free preset slot. Není žádné další volné místo pro přednastavení. Save preset dialog Uložit dialog přednastavení Browse set dialog Procházet dialog nastavení Browse image dialog Procházet dialog obrázku DeicsOnzeGuiBase DeicsOnze DeicsOnze &Preset &Přednastavení Program numerous Číslo programu INITVOICE INITVOICE LBank Nízká banka Subcategory Podskupina Bank numerous Číslo banky NONE ŽÁDNÁ HBank Vysoká banka Category Skupina Prog Program Preset Přednastavení DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. DeicsOnze v0.5.5 Autorské právo (c) 2004-2006 Nil Geisweiller. Zveřejněno pod GPL licencí. &Global &Celkové Pitch Envelope Obálka výšky tónu PL3 PL3 PL2 PL2 PL1 PL1 PR1 PR1 PR2 PR2 PR3 PR3 Pitch modulation depth Hloubka modulace výšky tónu LFO NKO LFO Sync NKO Sync Pitch modulation sensitivity Citlivost modulace výšky tónu Pitch Modulation Sensitivity Citlivost modulace výšky tónu LFO Delay Zpoždění NKO LFO delay Zpoždění NKO LFO speed Rychlost NKO Amplitude modulation depth Hloubka modulace rozkmitu Amplitude modulation sensitivity Citlivost modulace rozkmitu Amplitude Modulation Sensitivity Citlivost modulace rozkmitu AMS CMR LFO Waveform Časový průběh vlny NKO Pitch Modulation Depth Hloubka modulace výšky tónu PMD HMVT LFO Speed Rychlost NKO AMD HMR Speed Rychlost Delay Zpoždění PMS CMVT Modulation Matrix Základ modulace <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> <b>Algoritmus 1</b> : <i>Op 1</i> modulováno podle <i>Op 2</i> modulováno podle <i>Op 3</i> modulováno podle <i>Op 4</i><br> <b>Algoritmus 2</b> : <i>Op 1</i> modulováno podle <i>Op 2</i> modulováno podle jak <i>Op 3</i> tak <i>Op 4</i><br> <b>Algoritmus 3</b> : <i>Op 1</i>modulováno podle jak <i>Op 4</i> tak <i>Op 2</i> modulováno podle <i>Op 3</i><br> <b>Algoritmus 4</b> : <i>Op 1</i> modulováno podle jak <i>Op 2</i> tak <i>Op 3</i> modulováno podle <i>Op 4</i><br> <b>Algoritmus 5</b> : (<i>Op 1</i> modulováno podley <i>Op 2</i>) přidat do (<i>Op 3</i> modulováno podle <i>Op 4</i>) <br> <b>Algoritmus 6</b> : přidání tří <i>Op 1, 2, 3</i> vše modulováno podle <i>Op 4</i><br> <b>Algoritmus 7</b> : přidání tří <i>Op 1, 2, 3</i> s <i>Op 3</i> modulováno podle <i>Op 4</i><br> <b>Algoritmus 8</b> : přidání čtyř <i>Op 1, 2, 3, 4</i> Op4 Feedback Zpětná vazba Op4 Feedback level of the operator 4 Úroveň zpětné vazby operátora 4 Transpose Převedení Op &1 Operátor &1 Scaling 1 Změna velikosti 1 LS1 ZVÚ RS1 ZVR1 Rate Scaling Změna velikosti rychlosti Attack Rate of the operator 1 Rychlost náběhu operátora 1 Level Scaling Změna velikosti úrovně Amplitude Envelope 1 Obálka rozkmitu 1 RR1 RU1 D1R1 R1Z1 D1L1 Ú1Z1 D2R1 R1Z2 Release Rate Rychlost uvolnění 2° Decay Rate 2° Rychlost zpoždění 1° Decay Level 1° Úroveň zpoždění 1° Decay Rate 1° Rychlost zpoždění Attack Rate Rychlost náběhu AR1 RN1 Detune, OSCWave, EGShift 1 Rozladění, OSCWave, Posun EG 1 DET1 ROZ1 Detune Rozladění EG Shift Posun EG 96dB 96 dB 48dB 48 dB 24dB 24 dB 12dB 12 dB Wave form Tvar vlny Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Tvar vlny 1 = <i>sin(<b>t</b>)</i><br> Tvar vlny 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Tvar vlny 3 = <i>if <b>t</b>&#060 pi pak sin(<b>t</b>) else 0</i><br> Tvar vlny 4 = <i>if <b>t</b>&#060 pi pak sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Tvar vlny 5 = <i>if <b>t</b>&#060 pi pak sin(2*<b>t</b>) else 0</i><br> Tvar vlny 6 = <i>if <b>t</b>&#060 pi pak sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Tvar vlny 7 = <i>if <b>t</b>&#060 pi pak abs(sin(2*<b>t</b>)) else 0</i><br> Tvar vlny 8 = <i>if <b>t</b>&#060 pi pak sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Sensitivity 1 Citlivost 1 KVS1 CRK1 Amplitude Modulation Enable Povolení modulace rozkmitu AME1 PMR1 Keyboard Velocity Sensitivity Citlivost síly tónu klávesnice Key Velocity Sensitivity Citlivost síly tónu not EBS1 CZEG1 EG Bias Sensitivity Citlivost zkreslení EG Eg Bias Sensitivity Citlivost zkreslení EG Frequency 1 Kmitočet 1 Coarse 1 Hrubý 1 Coarse Ratio Hrubý poměr Fine 1 Jemný 1 Fine Ratio Jemný poměr Freq 1 Kmit 1 Fixed Frequency Pevný kmitočet Toggle Fix Frequency Přepnout pevný kmitočet FIX PEV OUT 1 VÝS 1 Output Volume Hlasitost výstupu Volume Hlasitost Op &2 Operátor &2 Amplitude Envelope 2 Obálka rozkmitu 2 D1R2 R2Z1 D1L2 Ú2Z1 D2R2 R2Z2 RR2 RU2 AR2 RN2 Frequency 2 Kmitočet 2 Coarse 2 Hrubý 2 Fine 2 Jemný 2 Freq 2 Kmit 2 Scaling 2 Změna velikosti 2 LS2 ZVÚ2 RS2 ZVR2 OUT 2 VÝS 2 Detune, OSCWave, EGShift 2 Rozladění, OSCWave, Posun EG 2 DET2 ROZ2 Sensitivity 2 Citlivost 2 EBS2 CZEG2 KVS2 CRK2 AME2 PMR2 Op &3 Operátor &3 Amplitude Envelope 3 Obálka rozkmitu 3 D1R3 R3Z1 D1L3 Ú3Z1 D2R3 R3Z2 RR3 RU3 AR3 RN3 Scaling 3 Změna velikosti 3 LS3 ZVÚ3 RS3 ZVR3 OUT 3 VÝS 3 Frequency 3 Kmitočet 3 Coarse 3 Hrubý 3 Fine 3 Jemný 3 Freq 3 Kmit 3 Detune, OSCWave, EGShift 3 Rozladění, OSCWave, Posun EG 3 DET3 ROZ3 Sensitivity 3 Citlivost 3 EBS3 CZEG3 KVS3 CRK3 AME3 PMR3 Op &4 Operátor &4 amplitude Envelope 4 Obálka rozkmitu 4 AR4 RN4 D1R4 R4Z1 D1L4 Ú4Z1 D2R4 R4Z2 RR4 RU4 Frequency 4 Kmitočet 4 Coarse 4 Hrubý 4 Fine 4 Jemný 4 Freq 4 Kmit 4 Scaling 4 Změna velikosti 4 LS4 ZVÚ4 RS4 ZVR4 OUT 4 VÝS 4 Detune, OSCWave, EGShift 4 Rozladění, OSCWave, Posun EG 4 DET4 ROZ4 Sensitivity 4 Citlivost 4 EBS4 CZEG4 KVS4 CRK4 AME4 PMR4 &Func &Funk Delay Pan Depth Hloubka vyvážení zpoždění Delay Pan LFO Freq Kmitočet NKO vyvážení zpoždění Delay Ch Send Level Úroveň poslání zpoždění kanálu Channel Chorus Sbor kanálu Delay Feedback Zpětná vazba zpoždění Delay On/Off, Return Level Zpoždění zapnuto/vypnuto, úroveň navrácení On Zapnuto Delay Beat Ratio Poměr úderu zpoždění Delay BPM ÚZM zpoždění Foot Control Ovládání stopy Pitch Bend Range Rozsah výšky tónu Pitch Výška tónu Amplitude Rozkmit Modulation Wheel Kolečko modulace Breath Control Ovládání dechu Pitch Bias Zkreslení výšky tónu Envelope Bias Zkreslení obálky After Touch Dodělávka po Phony Mode Předstíraný režim POLY POLY MONO MONO Potamento Portamento Portamento Mode Režim portamento FINGER PRST FULL PLNÝ PT ČP Portamento Time Čas portamenta C&horus &Sbor Chorus Parameters Parametry sboru Channel send level Úroveň poslání kanálu On/Off and Return level Zapnuto/Vypnuto a úroveň navrácení Select LADSPA plugin Vybrat přídavný modul LADSPA Change plugin Změnit přídavný modul &Reverb &Dozvuk Reverb Parameters Parametry dozvuku &Config &Nastavení Font Size Velikost písma Quality Kvalita High Vysoká Middle Střední Low Nízká Ultra low Velice nízká Filter Filtr Save Mode (into the song) Uložit režim (do písně) Save only the used presets Uložit pouze použitá přednastavení Save the entire set Uložit celé nastavení Save the configuration Uložit nastavení Configuration File Soubor s nastavením Save... Uložit... Load... Nahrát... Save as default Uložit jako výchozí Colors Barvy Text Text Background Pozadí Edit Text Upravit text Edit Background Upravit pozadí Red Červená Blue Modrá Green Zelená Set Path Nastavit cestu Image in the background : Obrázek na pozadí: Browse... Procházet... Load the set at the initialization : Nahrát nastavení při zapnutí: Set Brightness, Detune, Attack and Release of the current channel to default Nastavit jas, rozladění, náběh a uvolnění nynějšího kanálu na výchozí Res. Ctrl Res. Ctrl Cut all notes off Vyjmout všechny noty Panic! Nouzové zastavení! Number of Voices Počet hlasů Number of voices Počet hlasů Enable Povolit Channel Kanál Vol Hlasitost Channel Ctrl Kanál Ctrl Release Uvolnění Attack Náběh Brightness Jas Modulation Modulace Pan Vyvážení DelOverlapsBase MusE: Delete Overlaps MusE: Smazat překrytí Range Rozsah All Events Všechny události Selected Events Vybrané události Looped Events Události uvnitř smyčky Selected Looped Vybrané události uvnitř smyčky Parts Části Selected parts or c&urrent part All parts &in editor OK OK Cancel Zrušit DidYouKnow Did you know? Víte, že? Don't show on startup Neukazovat při spuštění Next tip Další rada Close Zavřít DuplicateTracksBase Duplicate tracks Zdvojit stopy Number of copies Počet kopií Copy all routes Kopírovat všechny signálové toky Default routing Výchozí signálový tok No routes Žádné toky signálu Duplicate all parts Make independent copies of all parts Make clones of all parts No parts Copy parts Kopírovat části Copy drumlist Kopírovat seznam bicích Copy standard (vol, pan) and synth controllers Kopírovat standardní (hlasitost, vyvážení) a syntetizátorové ovladače Copy standard controllers (vol, pan) Kopírovat standardní ovladače (hlasitost, vyvážení) Copy effects rack plugins Kopírovat přídavné moduly přihrádky efektů Copy plugin controllers Kopírovat ovladače přídavných modulů Ok OK Cancel Zrušit EditCtrlBase MusE: Edit Controller Event MusE: Upravit událost ovladače Time Position Časová poloha Available Controller: Dostupný ovladač: Create New Controller Vytvořit nový ovladač textLabel3 Popis 3 Value Hodnota Controller Ovladač Note Nota H-Bank Vysoká banka L-Bank Nízká banka Program Program off Vypnuto pushButton4 Tlačítko 4 &OK &OK &Cancel Z&rušit EditGainBase MusE: Modify gain MusE: Změnit zesílení Gain Zesílení 200% 200% 100% 100% 0% 0% &Reset &Nastavit znovu Alt+R Alt+R &Apply &Použít Alt+A Alt+A &Cancel Z&rušit Alt+C Alt+C EditInstrumentBase MusE: Instrument Editor MusE: Editor nástrojů Instrument Name: Název nástroje: Selected instrument name. Vybraný název nástroje. List of defined instruments. Seznam s vymezenými nástroji. Note off mode: Note off mode Selects how to handle note off events. The instrument can use note off events, or not at all, or convert them to zero-velocity note on events which helps save midi interface bandwidth. Refer to the instrument manufacturer's midi implementation chart for details. Pa&tches &Záplaty List of groups and patches. Seznam skupin a záplat. Group/Patch Skupina/Záplata Properties Vlastnosti Name: Název: Group or patch name Název skupiny nebo záplaty High Bank: Vysoká banka: Patch high bank number Záplatovat číslo vysoké banky Patch high bank number. --- means don't care. Záplatovat číslo vysoké banky. --- znamená, nestarat se. --- dont care --- Low Bank: Nízká banka: Patch low bank number Záplatovat číslo nízké banky Patch low bank number. --- means don't care. Záplatovat číslo nízké banky. --- znamená, nestarat se. --- --- Program: Program: Patch program number Záplatovat číslo programu Drum patch Záplata bicích If set, the patch is available only for drum channels. Je-li nastaveno, je záplata dostupná pouze pro kanály bicích. Drum Bicí GM patch Záplata GM If set, the patch is available in a 'GM' or 'NO' midi song type. Je-li nastaveno, je záplata dostupná v typu písně MIDI 'GM' nebo 'NO'. GM GM GS patch Záplata GS If set, the patch is available in a 'GS' or 'NO' midi song type. Je-li nastaveno, je záplata dostupná v typu písně MIDI 'GS' nebo 'NO'. GS GS XG patch Záplata XG If set, the patch is available in an 'XG' or 'NO' midi song type. Je-li nastaveno, je záplata dostupná v typu písně MIDI 'XG' nebo 'NO'. XG XG Show in tracks: Ukázat ve stopách: Midi MIDI &Drum Delete group or patch Smazat skupinu nebo záplatu &Delete S&mazat Alt+D Alt+D New patch Nová záplata New &Patch Nová &záplata Alt+P Alt+P New group Nová skupina New &Group Nová &skupina Alt+G Alt+G Contro&ller &Ovladač Common: Obecné: This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. Toto je seznam často používaných ovladačů MIDI. MusE ovšem zachází se všemi ovladači jako jsou "výška tónu" a "změny programů" jako s normálními ovladači. List of defined controllers Seznam vymezených ovladačů List of defined controllers. Seznam vymezených ovladačů. Name Název Type Typ H-Ctrl H-Ctrl L-Ctrl L-Ctrl Min Nejméně Max Nejvíce Def Vymezení Midi controller name Název ovladače MIDI Type: Typ: Midi controller type Typ ovladače MIDI Control7 Ovladač 7 Control14 Ovladač 14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Výška tónu Program Program Patch program number. --- means don't care. Patch Name: Dr def Midi controller number high byte Vysoké bajtové číslo ovladače MIDI Midi controller number low byte (* means drum controller) Nízké bajtové číslo ovladače MIDI (* znamená ovladač bicích) Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and GS/XG type songs and instruments. Allows controllers for each instrument in Muse's drum map. The low byte will be replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. Nízké bajtové číslo ovladače MIDI. Pokud je nízký bajt *, potom ovladač je 'ovladač bicích'. Pro stopy bicích a písně a nástroje typu GS/XG. Povoluje ovladače pro každý nástroj v rozložení bicích Muse. Nízký bajt bude v rozložení bicích nahrazen 'Notou A'. Příklady: Nástroje GS a XG Ovladače bících. * wild card * Range: Rozsah: Min Nejméně Minimum value. If negative, auto-translate. Nejmenší hodnota. Pokud je záporná, automatická proměna. Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. Useful for controllers which should be displayed with zero bias. For example, 'Pan': Minimum: -64 Maximum: 63 True range: Min: 0 Max: 127 (bias = 64) 'CoarseTuning': Min: -24 Max: 23 True range: Min: 40 Max: 87 (bias = 64) Bias is determined from controller type: 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 Type 'Pitch' is the exception. It is biased at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) Nejmenší hodnota. Pokud je nejmenší hodnota záporná, bude rozsah automaticky proměněn na kladný. To je užitečné u ovladačů, které by měly být zobrazeny s nulovým zkreslením. Například: 'Vyvážení': Minimum: -64 Maximum: 63 Opravdový rozsah: Min: 0 Max: 127 (bias = 64) 'Hrubé ladění': Min: -24 Max: 23 Opravdový rozsah: Min: 40 Max: 87 (bias = 64) Zkreslení je určeno z typu ovladače: 7-bit ovladač 7 / RPN: Zkreslení = 64 14-bit ovladač 14 / RPN14: Zkreslení = 8192 Typ 'výška tónu' je výjimka. Je zkreslen na nulu, i se záporným minimem: 'Výška tónu': Min: -8192 Max: 8191 Opravdový rozsah: Min: -8192 Max: 8191 (zkreslení 0) Max Nejvíce Maximum value Největší hodnota Default: Výchozí: L-Bank Nízká banka off Vypnuto Progr. Progr. ??? ??? H-Bank Vysoká banka Drum&maps &Rozložení bicích Patch program number. --- means don't care. High Bank Low Bank Default value. Off: No default. Výchozí hodnota. Vypnuto: Žádná výchozí. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). off dont care Vypnuto Add common controller Přidat obecný ovladač &Add &Přidat Alt+A Alt+A Delete controller Smazat ovladač Create a new controller Vytvořit nový ovladač New &Controller Nový &ovladač Alt+C Alt+C Null Param Hi: Nulový parametr vysoký: Null parameter number High byte Nulový parametr vysoký bajt If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events from corrupting the RPN/NRPN controller. Typically, set to 127/127, or an unused RPN/NRPN controller number. Je-li nastaveno, budou tato čísla 'null' parametru poslána po každé události RPN/NRPN. Toto brání následným událostem 'data' v poškození ovladače RPN/NRPN. Typicky nastavit na 127/127, nebo nepoužívané číslo ovladače RPN/NRPN. Lo: Nízký: Null parameter number Low byte Číslo nulového parametru nízký bajt S&ysEx S&ysEx SysEx List: Seznam SysEx: Hex Entry: Záznam Hex: New SysE&x Nový SysE&x Alt+X Alt+X Drummaps Rozložení bicích Patch Collections: Sbírky záplat: &Copy &Kopírovat &Remove &Odstranit &Up &Nahoru &Down &Dolů Patch: Záplata: from od to do Bank Hi: Vysoká banka: Bank Lo: Nízká banka: Contro&llers &Ovladače Null Parameters: Hi: Nulový parametr: vysoký: &Add Common... &Přidat obecný... W W Drum default: Comment: Poznámka: Note: The enclosing F0 ... F7 are not required &Initialization &Inicializace Instrument initialization sequence: Inicializační sekvence nástroje: &Add... &Přidat... &Change... &Změnit... Tools Nástroje &File &Soubor &Help &Nápověda &New &Nový New Nový Ctrl+N Ctrl+N &Open... &Otevřít... Open Otevřít Ctrl+O Ctrl+O &Save &Uložit Save Uložit Ctrl+S Ctrl+S Save &As... Uložit &jako... Save As Uložit jako &Close &Zavřít Close Zavřít Info Informace Additional information E&xit &Ukončit Exit Ukončit new item Nová položka What's this? Co je to? EditNoteDialogBase MusE: Enter Note MusE: Zadat notu OK OK Cancel Zrušit Length: Délka: Time Position: Časová poloha: Pitch: Výška tónu: Velocity On: Dynamika zapnuta: Velocity Off: Dynamika vypnuta: EditSysexDialogBase MusE: Enter SysEx MusE: Zadat SysEx TimePosition: Časová poloha: Hex: Note: The enclosing F0 ... F7 are not required Name: Název: Comment: Poznámka: &Select... &Vybrat... &OK &OK &Cancel Z&rušit OK OK Cancel Zrušit FLUIDGui FLUID: open Soundfile FLUID: Otevřít zvukové písmo FLUIDGuiBase Form1 Formulář 1 Soundfont Zvukové písmo Load Nahrát FLUIDSynthGuiBase FLUID Synth FLUID Synth Load Nahrát Delete Smazat Dump Info Vypsat informace ID ID Fontname Název písma Chnl Kanál Soundfont Zvuková banka Drum Chnl Kanál bicích Level Úroveň Width Šířka Damping Tlumení Room Size Velikost prostoru Reverb Dozvuk CHANNEL SETUP NASTAVENÍ KANÁLU Sine Sinus Triangle Trojúhelník Type Typ Number Počet Speed Rychlost Depth Hloubka Chorus Sbor Gain Zesílení LOADED SOUNDFONTS NAHRANÉ ZVUKOVÉ BANKY FileDialogButtons fdialogbuttons TlačítkaProSouborovýDialog Home Domovská složka Global Celkové User Uživatel Project Projekt read Midi Port Configuration Přečíst nastavení přípojky MIDI write window states Zapisovat stavy oken FluidSynthGui Choose soundfont Vybrat zvukovou banku GateTimeBase MusE: Modify Note Length MusE: Změnit délku noty Range Rozsah All Events Všechny události Selected Events Vybrané události Looped Events Události uvnitř smyčky Selected Looped Vybráno a uvnitř smyčky Parts Části Selected parts or c&urrent part All parts &in editor Values Hodnoty Rate: Rychlost: Offset: Posun času: % % lenNew = (lenOld * rate) + offset délkaNová = (délkaStará * rychlost) + posun OK OK Cancel Zrušit GlobalSettingsDialogBase MusE: Global Settings MusE: Celková nastavení Application Program Project directory Projektový adresář Projects: Projekty: ... ... start &with last song Začít s &poslední písní start with &template Začít s &předlohou sta&rt with song Začít s &určitou písní Views Pohledy y-pos Poloha y show Ukázat x-pos Poloha x height Výška width Šířka Mixer A Směšovač A Mixer B Směšovač B Big Time Velký ukazatel času Main Window Hlavní okno Transport Přehrávání set current values Nastavit nynější hodnoty Cur Nyní Start Muse Spustit MusE Choose start song or template Vybrat začáteční píseň nebo předlohu Reset to default Nastavit znovu na výchozí Start song Začáteční píseň start with last song Začít s poslední písní start with template Začít s předlohou start with song Začít s určitou písní On Launch Při spuštění show splash screen Ukázat úvodní obrazovku show "Did you know?" dialog Ukázat dialog Víte, že? Start template or song: Spustit předlohu nebo píseň: Read MIDI Ports configuration from file, or else automatically configure Číst nastavení přípojek MIDI ze souboru, nebo nastavit automaticky Read MIDI Ports configuration Přečíst nastavení přípojek MIDI Warn if opening file versions different than current version Upozornit, pokud se verze otevřených souborů liší od nynější verze Auto save (every 5 minutes if not playing/recording) Uložit automaticky (každých 5 minut, pokud se nepřehrává/nenahrává) Audio Zvuk Mixer Směšovač dB dB min. Meter Value Nejmenší hodnota regulátoru hladin min. Slider Val Nejmenší hodnota posuvníku Try to use Jack Freewheel Zkuste použít volnoběh Jack Speeds bounce operations Urychlí operace odmíchání Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Použít volnoběh Jack, je-li to možné. Tím se dramaticky zrychlí operace odmíchání. Enable denormal protection Povolit ochranu denormál Enable output limiter Povolit omezovač výstupu VST in-place VST v místě Enable VST in-place processing (restart required) Povolit zpracování VST v místě (je vyžadováno nové spuštění) Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. Povolit zpracování VST v místě. Toto vypněte, pokud přídavné moduly přihrádky efektů VST Ladspa nepracují nebo vrací zpět nahlas, i když se předpokládá, že mají být schopné v místě. Nastavení vyžaduje opětovné spuštění. Minimum control period Nejmenší kontrolní perioda Minimum audio controller process period (samples). Nejmenší perioda procesu ovladače zvuku (vzorky). Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. Nejmenší perioda procesu ovladače zvuku (vzorky). Ovlivňuje dobu odpovědi od ovladačů zvuku a grafů ovladačů. Nastavte nízkou hodnotu pro rychlé, jemné ovládání. Pokud to způsobí potíže s rychlostí, nastavte vyšší hodnotu. 1 1 2 2 4 4 8 8 16 16 Editors Open editors in tabs (else in separate windows) Piano roll Váleček Drum editor Editor bicích Wave editor Editor Wave Score editor Editor notového zápisu Master track editor Editor hlavní stopy DSSI VST Directories where dssi-vst (if installed) looks for Wine vst plugins (*.dll files) 32 32 64 64 128 128 256 256 512 512 1024 1024 2048 2048 External Waveditor Vnější editor Wave External Waveditor command Příkaz pro vnější editor Wave Note: External editor opened from the internal editor. Poznámka: Vnější editor otevřený z vnitřního editoru. Dummy Audio Driver (settings require restart) Falešný ovladač zvuku (nastavení vyžaduje nové spuštění) Sample rate Vzorkovací kmitočet Hz Hz Period size (Frames per period): Velikost periody (snímků za periodu): Shorter periods give better midi playback resolution. Kratší periody dávají lepší rozlišení přehrávání MIDI. Midi MIDI &Record all instruments &Nahrávat všechny nástroje &Don't record hidden instruments &Nenahrávat skryté nástroje Don'&t record muted instruments N&enahrávat ztlumené nástroje Don't record &hidden or muted instruments Nenahrávat s&kryté nebo ztlumené nástroje Instrument initialization Inicializace nástroje Send instrument initialization sequences Poslat inicializační sekvenci nástroje Warn if instrument initialization sequences pending Upozornit, pokud inicializační sekvence nástroje čeká na vyřízení Send instrument controller default values if none in song, at init or rewind Poslat výchozí hodnoty ovladače nástroje, pokud není žádná hodnota v písní, v init nebo převíjení zpět Ticks Tiky RTC Resolution (Ticks/Sec) Rozlišení RTC (RealTimeClock) (Tiky/Sekundy) 4096 4096 8192 8192 16384 16384 32768 32768 Midi Resolution (Ticks/Quarternote) Rozlišení MIDI (Tiky/Čtvrťová nota) &Only offer old-style drumtracks &Pouze po stopách bicích ve starém stylu Only offer new-style &drumtracks Pouze po stopách bicích v &novém stylu &Prefer old-style drumtracks &Upřednostňovat stopy bicích ve starém stylu Prefer &new-style drumtracks Upřednostňovat stopy bicích v no&vém stylu 48 48 Start MusE Start with &last song Start with &template Sta&rt with song On launch Show splash screen Show "Did you know?" dialog Plugin Paths Ordered by priority. Changes take effect on restart. The plugin cache is used for safety and speed. It automatically rescans when any of the given directories change, for example by installing a new plugin. Click this button to force the cache to recreate upon next restart. &Force plugin rescan and cache recreation on next restart LADSPA DSSI Linux VST Directories containing Linux vst plugins (*.so files) LV2 LV2 NOTE: Ensure the directory containing core LV2 bundles is included (typically /usr/lib/lv2). Otherwise all plugins may not load properly. Move up Move down Add... Edit... Remove Odstranit Audio driver settings (require restart) Sample rate: Audio backend: Shorter periods give better midi playback resolution. Note that this cannot be set with Jack devices! Period size (frames per period): Note that samplerate cannot be set with Jack devices! Minimum meter value Minimum slider value Sample rate conversion, time stretching and pitch shifting Audio converter settings... 96 96 192 192 384 384 768 768 1536 1536 3072 3072 6144 6114 12288 12288 Displayed Resolution (Ticks/Quarternote) Zobrazované rozlišení (Tiky/Čtvrťová nota) Midi controller behaviour Send Null parameters after an (N)RPN value If set, 'null' parameter numbers will be sent after each RPN/NRPN event. (A 'null' parameter number is 127.) This prevents subsequent rogue or accidental 'data' events (without parameters) from corrupting the current RPN/NRPN controller. Normally it is OK to leave this unset, but use it if there is a possibility of such 'data' events. If this is set, the 'Optimize controllers' setting does not affect these controllers' parameters, only the H/L values. Send Null parameters Don't send redundant parameters or values If set, redundant H/L parameters or H/L values are not sent. They are sent only if they changed. This can save midi interface bandwidth. But it is possible the receiving device might miss the previous changes (for example if the device is turned on after they were sent). The 'Panic' button will force them to be resent. Certain operations will also force them to be resent, such as clicking a GUI midi control (but not while adjusting it). Optimize controllers Record new style drum tracks Nahrávat stopy bicích v novém stylu Record all instruments Nahrávat všechny nástroje Don't record hidden instruments Nenahrávat skryté nástroje Don't record muted instruments Nenahrávat ztlumené nástroje Don't record hidden or muted instruments Nenahrávat skryté nebo ztlumené nástroje Warn if timer frequency is inadequate Upozornit, pokud je frekvence časovače nepřiměřená Record drum tracks Record a&ll instruments Latency Whether to enable latency correction/compensation: Enable latency correction/compensation Project latency Whether completely independent branches share a common project-wide latency: Project-wide latency Unterminated branches Whether unterminated branches participate in latency correction/compensation: Unterminated input branches Unterminated output branches Track monitoring Whether track monitor buttons affect latency correction/compensation: Monitoring affects latency GUI Behaviour Chování rozhraní Behavior Chování Revert native GUI scaling for plugins in HiDPI Live update wave parts while recording Whether to show knob and slider values in certain places, for example mixer strips. Turn off to reduce clutter. Revert native UI window HiDPI scaling (some plugins ignore this setting) Show knob and slider values Whether to show knobs or sliders in certain places, for example mixer strips Prefer midi volume as decibels instead of 0-127 Style hack: Force line edit widgets to draw a frame Prefer knobs instead of sliders GUI refresh rate Use first available Ask once Ask always Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. This hack forces a frame to be drawn, but may interfere with other styles. Use old-style stop shortcut Track height Výška stopy GUI Refresh Rate Rychlost obnovování rozhraní /sec /s Use old-style stop shortcut: Použít klávesovou zkratku pro zastavení ve starém stylu: Move single armed track with selection Přesunout jednu odjištěnou stopu s výběrem Use project save dialog Použít dialog pro uložení projektu Some popup menus stay open (else hold Ctrl) Některé vyskakovací nabídky zůstanou otevřeny (držet Ctrl) Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. Umožní, aby některé vyskakovací nabídky zůstanou otevřeny. Je pro to, aby zůstaly otevřeny, potřeba podržet klávesu Ctrl. In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. V některých oblastech snižuje prostřední tlačítko myši hodnoty, zatímco pravé tlačítko je zvyšuje. Uživatelé bez prostředního tlačítka myši mohou tuto volbu vybrat, aby se v takových oblastech chovalo stejně jako prostřední tlačítko myši tlačítko levé. Use left mouse button for decreasing values Použít levé tlačítko myši na zmenšení hodnot Shift + Right click sets left range marker Shift + klepnutí pravým tlačítkem myši nastaví značku pro levý rozsah Allow adding hidden tracks in track list menu Povolit přidání skrytých stop v nabídce seznamu stop Unhide tracks when adding hidden tracks Zrušit skrytí stop při přidávání skrytých stop Monitor on record-arm automatically LV2 UI open behavior GUI Rozhraní Set range markers (left / right) Middle mouse button / Right mouse button Ctrl + Right mouse button / Right mouse button Ctrl + Left mouse button / Ctrl + Right mouse button Options Show status bar Show note names on notes in pianoroll Show note tooltips in midi editors GUI Tweaks Tweaks Keep transport window on top (requires restart) Smart focus Chytré zaměření After editing, controls will return focus to their respective canvas Po dokončení úprav vrátí ovladače zaměření na jim příslušné plátno Show newly created midi velocity graphs per-note Ukázat nově vytvořené grafy síly tónu MIDI na notu px px Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. Povolit myš bez okraje. Pro určité funkce, jako je zvětšení/vyvážení. Zakázat užití náhradního běžného postupu. Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. Povolit myš bez okraje. Pro určité funkce, jako je zvětšení. Zakázat užití náhradního běžného postupu. Borderless zoom/pan mouse (else use alternate method) Myš bez okraje při zvětšení/vyvážení (jinde použít náhradní postup) Scrollable submenus Pohyblivé podnabídky Whether record-arming a track automatically activates monitoring. Drum tracks Stopy bicích Only offer old-style drumtracks Pouze po stopách bicích ve starém stylu Only offer new-style drumtracks Pouze po stopách bicích v novém stylu Prefer old-style drumtracks Upřednostňovat stopy bicích ve starém stylu Prefer new-style drumtracks Upřednostňovat stopy bicích v novém stylu GUI Style Styl rozhraní MDI-subwindowness and sharing menus Podokna a sdílení nabídek Presets: Přednastavení: traditional MusE SDI Tradiční MusE SDI Cakewalk-like MDI MDI na způsob Cakewalku Borland-/Mac-like MDI MDI na způsob Borland/Mac &Apply &Použít &Ok &OK &Cancel Z&rušit LegatoBase MusE: Legato MusE: Legato Range Rozsah All Events Všechny události Selected Events Vybrané události Looped Events Události uvnitř smyčky Selected Looped Vybrané události uvnitř smyčky Parts Části Selected parts or c&urrent part All parts &in editor Settings Nastavení ticks tiky Minimum Length Nejmenší délka Allow shortening notes Dovolit zkracování not OK OK Cancel Zrušit MITTransposeBase MusE: Midi Input Plugin: Transpose MusE: Přídavný modul pro vstup MIDI: Převedení On Zapnuto TriggerKey Spouštěcí klávesa Transpose: Převést: +0 +0 MRConfigBase MusE: Midi Input Plugin: Remote Control MusE: Přídavný modul pro vstup MIDI: Vzdálené ovládání Activate Zapnout On Zapnuto Actions Činnosti Stop Zastavit Record Nahrávat Goto Left Mark Jít na levou značku Play Přehrát Insert rest (step rec) Vložit přestávku (StepRec) MdiSettingsBase Form Formulář GroupBox SkupinovýBox MDI subwin Podokno MDI Shares menu when subwin Sdílí nabídku, když je podoknem Shares menu when free Sdílí nabídku, když je okno volné MetronomeConfigBase MusE: Metronome Config MusE: Nastavení metronomu Metronome Metronom Audio Beep Vnitřní reproduktor počítače Choose outputs... Vybrat výstupy... 50 50 % Audio volume Hlasitost zvuku % MIDI Click Klapnutí MIDI Midi Channel Kanál MIDI Measure Note Nota MIDI na začátku taktu Measure Velocity Síla tónu noty MIDI na začátku taktu Beat Velocity Síla tónu na začátku taktu Beat Note Nota na začátku doby Midi Port Přípojka MIDI Disabled since jack does not support it Zakázat, protože to JACK nepodporuje Precount Předpočítat enable Povolit Bars Takty From Mastertrack Z hlavní stopy / / Signature Taktové označení Prerecord Přednahrát Preroll Přetáčení vpřed Hint: Enable metronome in Transportpanel Rada: Povolte metronom v ovládacím panelu pro přehrávání Two samples (old samples) Dva vzorky (staré vzorky) Four samples (new samples, with accents) Čtyři vzorky (nové vzorky, s přízvuky) Volume Hlasitost Audio master Řízení zvuku Meas Takt Beat Doba Accent1 Důraz 1 Accent2 Důraz 2 Sample Vzorek Preferences Apply the settings globally or to the current song: Use global metronome Use song-specific metronome Enable accents for the audio and midi metronomes: &Two samples (old samples) Four samples (&new samples, with accents) Audio Zvuk MIDI Accent 2 Velocity Accent 1 Velocity Accent 1 Note Accent 2 Note Off Vypnuto Accents Accent sequence beats Current accent 1 sequence: (Click to change) Current accent 2 sequence: Factory User Uživatel Preset sequences Add current accents to presets Make a new preset from the current accents. + Delete preset Smazat přednastavení Delete the selected preset. Only user presets can be deleted. - Use preset Copy the preset to the current accents. -> Reset to Factory or User defaults Resets all current accent sequences to default Factory or User presets. ! Enable the precount Enable Povolit Number of bars Use Mastertrack time signature Beats per bar Beat note type Enable precount when playing Precount on play Mute the metronome after precount Mute metronome Not supported yet &Apply &Použít Alt+A Alt+A &OK &OK Alt+O Alt+O &Cancel Z&rušit Alt+C Alt+C MidiAudioControlBase Midi control Ovládání MIDI Port: Přípojka: Channel: Kanál: Control type: Typ ovládání: &Learn Na&učit Control7 Ovladač 7 Control14 Ovladač 14 RPN RPN NRPN NRPN RPN14 RPN 14 NRPN14 NRPN 14 Pitch Výška tónu Program Program Hi: Vysoký: Lo: Nízký: Learn Naučit se MidiFilterConfigBase MusE: Midi Input Filter MusE: Vstupní filtr MIDI Record Filter Filtr nahrávání Note On Nota zapnuta Poly Pressure Vícenásobné stisknutí tlačítka Controller Ovladač Program Change Změna programu After Touch Dodělávka po Pitch Bend Ohyb výšky tónu Sysex Sysex Thru Filter Thru filtr Controller Filter Filtr ovladače Channel Filter Filtr kanálu 14 14 10 10 6 6 12 12 4 4 2 2 9 9 8 8 3 3 13 13 15 15 16 16 7 7 11 11 5 5 1 1 MidiInputTransformDialogBase MusE: Midi Input Transformator MusE: Měnič vstupu MIDI Filter Filtr All Vše Equal Stejný Unequal Nestejný Note Poznámka Poly Pressure Vícenásobné stisknutí tlačítka Control Change Změna ovládání Aftertouch Dodělávka po Pitch Bend Ohyb výšky tónu NRPN NRPN RPN RPN Program Program Value 2 Hodnota 2 Value 1 Hodnota 1 Event Type Typ události Ignore Přehlížet Higher Vyšší Lower Nižší Inside Uvnitř Outside Vně Channel Kanál Port Přípojka Processing Zpracování Keep Zachovat Fix Pevný Plus Plus Minus Minus Multiply Násobit Divide Dělit Invert Obrátit ScaleMap Rozložení not Flip Obrátit Dyn Dyn Random Náhodný Toggle Přepnout Modules Moduly 1 1 2 2 3 3 4 4 Enable module 1 Enable module 2 Enable module 3 Enable module 4 enable modul 1 Povolit modul 1 enable modul 2 Povolit modul 2 enable modul 3 Povolit modul 3 enable modul 4 Povolit modul 4 Preset Přednastavení Name: Název: Comment: Poznámka: Function Funkce create new preset Vytvořit nové přednastavení &New &Nový delete preset Smazat přednastavení &Delete S&mazat &Dismiss &Zahodit PresetList Seznam přednastavení MidiSyncConfigBase MusE: Midi Sync MusE: Seřízení MIDI Apply Použít Ok OK Cancel Zrušit MTC MTC Type: Typ: 24 24 25 25 30D 30D 30N 30N Offset: Posun: hour hodina h h minute Minuta m min second Sekunda s s frame Snímek f sn subframe Podsnímek Sync receiving and sending Seřídit přijímání a posílání Send and receive Jack transport Poslat a přijmout přehrávání JACK Send and receive Jack transport information, including stop, start and position. Poslat a přijmout informace o přehrávání JACK, včetně zastavení, spuštění a polohy. Use Jack transport Použít přehrávání JACK Make MusE the Jack transport Timebase Master Udělat MusE pánem nad řízením času přehrávání JACK Make MusE the Jack transport Timebase Master. Allows Jack to show time as MusE Bars, Beats, and Ticks. MusE will try to become master, but other Jack clients can also take over later. You can always click here again for Master. Udělat MusE pánem nad řízením času přehrávání JACK. Dovolí JACKovi, aby ukazoval čas jako takty, doby, tiky MusE. MusE se pokusí stát hlavním, ale jiní klienti JACK to mohou později také převzít. Vždy můžete klepnout sem a nastavit MusE jako hlavní. Jack transport Timebase Master Řízení času pro přehrávání JACK Control MusE timing by external midi clock or MTC sync Ovládat časování MusE přes vnější hodiny MIDI nebo MTC sync When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). V režimu Slave je tempo řízeno vnějškově. MusE může seřídit k hodinám midi nebo MTC quarter frame sync. zapnuté vstupy v seznamu budou mít účinek (RMC, RMMC, RMTC). Slave to external sync Spojit s vnějším sync Averaging applied to recorded external tempo changes. Zprůměrování použito na nahrané změny vnějšího tempa. External midi clock can be very jittery. Tempo is derived from it and recorded. It is usually desirable to average it and limit the number of recorded changes. Tiny: 2 section 4/4 = 8 stages. 1/8T note averaging, may produce jitter. Small: 3 section 12/8/4 = 24 stages. 1/4 note averaging, may still produce jitter. Medium: 3 section 28/12/8 = 48 stages. 1/2 note averaging. Less jitter. Large: 4 section 48/48/48/48 = 192 stages. Use this if the song has only one tempo. Very low quantization values can be used. Large pre-detect: 4 section 8/48/48/48 = 152 stages + first stage large step pre-detector. Use this if you expect sudden large tempo steps. None: Use only if high accuracy is needed for audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. Vnější hodiny MIDI mohou být velmi neklidné. Tempo je odvozeno z nich a nahráno. Obvykle je žádoucí jeho zprůměrování a omezení počtu nahraných změn. Maličké: 2 části 4/4 = 8 úseků. 1/8 zprůměrování not, může tvořit neklid. Malé: 3 části 12/8/4 = 24 úseků. 1/4 zprůměrování not, pořád může tvořit neklid. Střední: 3 části 28/12/8 = 48 úseků. 1/2 zprůměrování not. Méně neklidu. Velké: 4 části 48/48/48/48 = 192 úseků. Použijte, když má píseň jedno tempo. Lze použít velmi nízké hodnoty kvantizace. Large pre-detect: 4 section 8/48/48/48 = 152 úseků + před-detektor velkého kroku prvního úseku. Použijte, očekáváteli náhlé velké kroky v tempu. Žádné: Použijte, jen když je při přehrávání potřeba vysoká přesnost vyrovnání zvuku. Pozor: Nahrává tisíce změn tempa za minutu. MusE může zpomalit a soubor s písní bude velký. Tempo record averaging Zprůměrování při nahrávání tempa bpm ÚZM Tempo record quantization Kvantizace při nahrávání tempa Send start to first clock delay Přestávka mezi posláním "začátku" až po poslání prvních hodin This button and other transport related buttons are found on the Transport Panel Allows 'slow sync' devices time to synchronize to MusE. This value is the delay from sending start to sending the first clock. Dá zařízením "slow sync" čas na seřízení s MusE. Tato hodnota je přestávkou mezi posláním "začátku" až po poslání prvních hodin. ms ms Send sync delay Přestávka pro poslání sync Note: Sync delay and MTC sync currently not fully implemented Poznámka: Sync delay a MTC sync nejsou v současnosti zcela udělány MidiTrackInfoBase MusE: TrackInfo MusE: Informace o stopě output port Výstupní přípojka output channel Výstupní kanál all midi events are sent to this output channel Všechny události MIDI jsou poslány do tohoto výstupního kanálu Out ch Výstupní kanál input routing Vstupní signálový tok input detect Zjištění vstupu Input detect indicator. Detects all note on-off, controller, aftertouch, program change, and pitchbend (but not sysex or realtime) events on the selected channels, on the selected midi ports. Údaj o zjištění vstupu. Zjistí události všechny noty zapnuto/vypnuto, ovladač, dodělávka po, změna programu a ohyb výšky tónu (ale ne Sysex nebo skutečný čas)- na vybraných kanálech na vybraných přípojkách MIDI. W W Midi thru MIDI thru Pass input events through ('thru') to output. Vést vstupní události skrz ('thru') do výstupu. output routing Výstupní signálový tok off Vypnuto Transp. Přesun Delay Zpoždění % % Length Délka Velocity Síla tónu Compr. Kompr. Channel Info Informace o kanálu Select instrument patch Vybrat záplatu nástroje <unknown> <neznámý> Rec: Nahr.: Add all settings to song Přidat všechna nastavení do písně All Vše Bank Select MSB. Ctrl-double-click on/off. Výběr banky MSB. Ctrl-dvojité klepnutí zapnuto/vypnuto. Change note length in percent of actual length Změnit délku noty v procentech skutečné délky Offset playback of notes before or after actual note Posun přehrávání not před nebo po skutečné notě Transpose notes up or down Transponovat noty nahoru nebo dolů H-Bank Vysoká banka Bank Select LSB. Ctrl-double-click on/off. Výběr banky LSB. Ctrl-dvojité klepnutí zapnuto/vypnuto. <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> <html><head/><body><p>Přidat nebo odebrat na síle tónu not ve stopě.</p><p><span style=" font-style:italic;">Jelikož je rozsah not MIDI 0-127, <br/>může to znamenat, že noty nedosáhnout <br/>spojené síly tónu, nota + síla tónu.</span></p></body></html> Compress the notes velocity range, in percent of actual velocity Stlačit rozsah dynamiky not (procenta ze skutečné síly tónu) L-Bank Nízká banka Program. Ctrl-double-click on/off. Program. Dvojité klepnutí pro zapnuto/vypnuto. Add bank + prog settings to song Přidat banku + nastavení programu do písně Prog Program Volume. Ctrl-double-click on/off. Hlasitost. Dvojité klepnutí pro zapnuto/vypnuto. Add vol setting to song Přidat nastavení hlasitosti do písně Vol Hlasitost Change stereo position. Ctrl-double-click on/off. Změnit polohu sterea. Ctrl-dvojité klepnutí zapnuto/vypnuto. Add pan setting to song Přidat nastavení vyvážení do písně Pan Vyvážení Select instrument Vybrat nástroj MidiTransformDialogBase MusE: Midi Transformator MusE: Měnič MIDI &New &Nový &Delete S&mazat &Apply &Použít &OK &OK &Cancel Z&rušit PresetList Seznam přednastavení Processing Zpracování Event Type Typ události Keep Zachovat Fix Pevný Note Poznámka Poly Pressure Vícenásobné stisknutí tlačítka Control Change Změna ovládání Aftertouch Dodělávka po Pitch Bend Ohyb výšky tónu NRPN NRPN RPN RPN Plus Plus Minus Minus Multiply Násobit Divide Dělit Value 2 Hodnota 2 Invert Obrátit ScaleMap Rozložení not Flip Obrátit Dyn Dyn Random Náhodný Value 1 Hodnota 1 Length Délka Position Poloha Randomize Filter Filtr All Vše Equal Stejný Unequal Nestejný Ignore Přehlížet Higher Vyšší Lower Nižší Inside Uvnitř Outside Vně Bar Range Oblast taktu Preset Přednastavení Name: Název: Comment: Poznámka: Range Rozsah process all events Zpracovat všechny události selected tracks Vybrané stopy inside loop Uvnitř smyčky Function Funkce Select Vybrat Quantize Kvantizovat Delete Smazat Transform Proměnit Insert Vložit Copy Kopírovat Extract Vytáhnout Quantize Value Kvantizovat hodnotu MixdownFileDialogBase MusE: Set Mixdown Wavefile MusE: Nastavit finální namíchání souboru Wave &OK &OK &Cancel Z&rušit File Path Cesta k souboru Channel Kanál Stereo Stereo Mono Mono 5.1 5.1 wav,16 Bit wav, 16 Bit wav, 24 Bit wav, 24 Bit wav, 32 Bit (float) wav, 32 Bit (float) Format Formát MoveBase MusE: Move Notes MusE: Přesunout noty Range Rozsah All Events Všechny události Selected Events Vybrané události Looped Events Události uvnitř smyčky Selected Looped Vybrané události uvnitř smyčky Parts Části Selected parts or c&urrent part All parts &in editor Value Hodnota Move by Přesunout podle ticks tiky OK OK Cancel Zrušit MusECore::AudioPreviewDialog Auto play Přehrát automaticky Stop Zastavit Play Přehrát MusECore::RasterizerModel Off Vypnuto Bar Takt MusECore::Scripts Do you want to process ALL or only selected events? Chcete zpracovat VŠECHNY nebo pouze vybrané události? &Selected &Vybráno &All &Vše MusE - external script failed MusE: Vnější skript selhal MusE was unable to launch the script, error message: %1 MusE se skript nepodařilo spustit. Chybová zpráva: %1 Re-read script names from disc MusECore::Song Jack shutdown! Vypnutí JACK! Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: - a performance issue with your particular setup. - a bug in MusE (or possibly in another connected software). - a random hiccup which might never occur again. - jack was voluntary stopped by you or someone else - jack crashed If there is a persisting problem you are much welcome to discuss it on the MusE mailinglist. (there is information about joining the mailinglist on the MusE homepage which is available through the help menu) To proceed check the status of Jack and try to restart it and then . click on the Restart button. Jack zjistil potíže s rychlostí, které vedly k tomu, že MusE bylo odděleno od Jack. To se mohlo stát z různých důvodů: - potíže s rychlostí ve vašem nastavení - chyba v MusE nebo jiném připojeném programu - náhodný zádrhel, který by se už nikdy neopakoval - Jack byl vámi nebo někým jiným dobrovolně zastaven - Jack spadl Pokud se potíže vyskytnou znovu, budeme velmi rádi, když si s vámi o tom budeme moci promluvit v poštovním seznamu MusE. (informace o tom, jak se člověk přidá k poštovním seznamu, se nacházejí na domovských stránkách MusE, které jsou dosažitelné přes hlavní nabídku Nápověda) Pro pokračování, prosím, přezkoušejte stav Jacka, zkuste Jack znovu spustit a klepněte na tlačítko "Spustit znovu". Automation: Automatizace: previous event Předchozí událost next event Další událost set event Nastavit událost add event Přidat událost erase event Vymazat událost erase range Vymazat rozsah clear automation Smazat automatizaci Automation Automatizace Previous event Next event Set event Add event Erase event Erase range Clear automation Midi control Ovládání MIDI Assign Přiřadit Clear Smazat Clear all controller events? Smazat všechny události ovladače? &Ok &OK &Cancel Z&rušit Controller Ovladač Bypass MusE: Tempo list MusE: Seznam tempa External tempo changes were recorded. Transfer them to master tempo list? Byly nahrány vnější změny tempa. Mají se převzít do hlavního seznamu tempa? Do you want to process ALL or only selected events? Chcete zpracovat VŠECHNY nebo pouze vybrané události? &Selected &Vybráno &All &Vše MusE - external script failed MusE: Vnější skript selhal MusE was unable to launch the script, error message: %1 MusE se skript nepodařilo spustit. Chybová zpráva: %1 Und&o &Zpět Re&do Z&novu MusEGui select Pointer Tool: with the pointer tool you can: select parts move parts copy parts Nástroj ukazovátka: Tento nástroj umožňuje: Vybrat části Přesunout části Kopírovat části select Pencil Tool: with the pencil tool you can: create new parts modify length of parts Nástroj tužky": Tento nástroj umožňuje: Vytvořit nové části Změnit délku jednotlivých částí select Delete Tool: with the delete tool you can delete parts Nástroj gumy": Smazat části select Cut Tool: with the cut tool you can split a part Nástroj pro stříhání: S tímto nástrojem se části rozdělují select Glue Tool: with the glue tool you can glue two parts Nástroj pro přilepení: S tímto nástrojem se dvě jednotlivé části spojují v jednu select Score Tool: Nástroj pro notový zápis: select Quantize Tool: insert display quantize event Nástroj pro kvantizaci: S tímto nástrojem se vkládají události kvantizace select Drawing Tool Nástroj tužky na kreslení select Muting Tool: click on part to mute/unmute Nástroj pro ztlumení: Klepněte na část pro zapnutí/vypnutí ztlumení Manipulate automation Pracovat na automatizaci Cursor tool Nástroj ukazovátka pointer Ukazovátko pencil Tužka eraser Guma cutter Střihadlo score Notový zápis glue Lepidlo quantize Kvantizace draw Tužka na kreslení mute parts Ztlumit části edit automation Upravit automatizaci cursor Ukazovátko MusEGui::Appearance Main application font, and default font for any controls not defined here. Hlavní písmo programu a výchozí písmo pro všechny ovládací prvky, které tu nejsou stanoveny. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Proužky směšovače a přihrádky s efekty. Informační panel ke stopám MIDI. Zadávací pole pro panel ovládání MIDI. Transport controls. Ovládání přehrávání. Time scale upper, and time signature. Controller graph and S/X buttons. Časové měřítko nahoru a taktové označení. Graf ovladače a tlačítka S/X. Time scale lower, and arranger part name overlay. Časové měřítko dolů a aranžér: překrytí názvu části. Main application font, and default font for any controls not defined here. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Arranger part name overlay. Time scale lower. Tempo scale, and markers. Měřítko tempa a značky. Mixer labels. Auto-font-sizing up to chosen font size. Word-breaking but only with spaces. Popisky u směšovače. Velikost písma bude automaticky přizpůsobena nahoru zvolené velikosti. Zalomení řádků ale jen u mezer. Maximum mixer label auto-font-sizing font size. Největší velikost písma u automatické velikosti písma popisků směšovače. Global opacity (opposite of transparency). Celková neprůhlednost (protějšek průhlednosti). Standard Obvyklý Custom Vlastní Keep Qt system style Zachovat styl systému Qt Do you really want to reset colors to theme default? Opravdu chcete barvy nastavit znovu na výchozí vzhled? Style was changed. The program must be restarted for changes to take place. Restart now? Revert changes Revert all... Do you really want to reset all colors? &Ok &OK &Cancel Z&rušit MusE: load image MusE: Nahrát obrázek No current color item Select Color: %1 Select style sheet Vybrat styl Qt style sheets (*.qss) Styly Qt (*.qss) MusEGui::Arranger Enable Recording Povolit nahrávání Mute/Off Indicator Ztlumit/Ukazatel vypnuto Solo Indicator Ukazatel "Solo" Track Type Typ stopy Track Name Název stopy Midi output channel number or audio channels Číslo kanálu výstupu MIDI nebo zvukové kanály Midi output port or synth midi port Přípojka výstupu MIDI nebo přípojka MIDI syntetizátoru Time Lock Zámek času Automation parameter selection Výběr parametru automatizace Notation clef Notový klíč Enable recording. Click to toggle. Zapnout nahrávání. Klepněte pro přepnutí. Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. On/Off is not! Ukazatel ztlumení. Klepněte pro přepnutí. Klepnutí pravým tlačítkem myši pro přepnutí stopy na zapnuto/vypnuto. Ztlumení je navrženo pro rychlou, opakovanou činnost. Zapnuto/Vypnuto není! Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed, indicated by a dark square. Ukazatel sóla. Klepněte pro přepnutí. Připojené stopy jsou také domněle (phantom) zesólovány, ukázáno tmavým čtverečkem. Track type. Right-click to change midi and drum track types. Typ stopy. Klepnutí pravým tlačítkem myši pro změnu typů stopy MIDI a bicí. Track name. Double-click to edit. Right-click for more options. Typ stopy. Dvojité klepnutí tlačítkem myši pro úpravy. Klepnutí pravým tlačítkem myši pro více voleb. Midi/drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Stopa MIDI/bicí. Číslo výstupního kanálu. Zvuková stopa: Kanály. Klepnutí středovým kolečkem/pravým tlačítkem myši pro změnu. Midi/drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Stopa MIDI/bicí. Výstupní přípojka. Syntezátorová stopa: Přiřazená přípojka MIDI. Klepnutí levým tlačítkem myši pro změnu. Klepnutí pravým tlačítkem myši pro ukázání rozhraní. Time lock Zámek času Notation clef. Select this tracks notation clef. Notový klíč. Vybere notový klíč této stopy. Track index. Click to select track, CTRL+LMB to add/remove, SHIFT+LMB for range select, dblclick to select all of the same type. Enable/Disable input monitor. Passes input through to output (see also Settings: Automatically monitor on record arm). Enable/Disable recording. Mute indicator. Left-click to mute track (for quick action), right-click to toggle track on/off (for more permanent action). Track type. Click to switch between midi and drum track types. Track name. Double-click to edit. Right-click for context menu. Midi/Drum: Output channel number. Audio: Channels. Mid/right-click to change. Midi/Drum: Output port. Synth: Assigned midi port. Left-click to change, right-click to show synth GUI. Notation clef. Click to select this tracks notation clef. Arranger Aranžér Cursor Poloha ukazovátka Cursor position Bars Takty Off Vypnuto Bar Takt Track index Enable input monitor Enable recording Mute/Off indicator Solo indicator Track type Track name Enable input monitor. Click to toggle. Passes input through to output for monitoring. See also Settings: Automatically Monitor On Record Arm. Enable recording. Click to toggle. See also Settings: Automatically Monitor On Record Arm. Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed. Midi/Drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi/Drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Solo indicator. Connected tracks are also 'phantom' soloed. Snap Magnet Len Délka song length - bars délka písně - takty Type Typ NO Žádný GM GM GS GS XG XG midi song type Typ písně MIDI Pitch Výška tónu midi pitch Změna výšky tónu MIDI global midi pitch shift Celková změna výšky tónu MIDI Tempo Tempo midi tempo Tempo MIDI Song length - bars Midi pitch Global midi pitch shift Midi tempo N N TrackInfo Informace o stopě I Input monitor Vstup R Record L M Mute M S Solo S C Class (track type) C Track Stopa Port Přípojka Ch Channel K T Time lock T Automation Automatizace Clef Klíč MusEGui::ArrangerColumns Control7 Ovladač 7 Control14 Ovladač 14 RPN RPN NPRN NPRN RPN14 RPN 14 NRPN14 NRPN 14 Pitch Výška tónu Program Program Aftertouch Dodělávka po MusEGui::ArrangerView MusE: Arranger MusE: Aranžér Arranger Aranžér D&elete S&mazat C&ut Vyj&mout &Copy &Kopírovat Copy in range Kopírovat v oblasti &Paste &Vložit Paste (show dialog) Vložit (ukázat dialog) Paste c&lone Vložit k&lon Paste clone (show dialog) Vložit klon (ukázat dialog) &Insert Empty Measure Vložit prázdný &takt Delete Selected Tracks Smazat vybrané stopy Duplicate Selected Tracks Zdvojit vybrané stopy Shrink selected parts Srazit vybrané části Expand selected parts Roztáhnout vybrané části Clean selected parts Uklidit vybrané části Paste to selected &track Vložit do vybrané &stopy Paste clone to selected trac&k Vložit klon do vybrané s&topy Paste (show dialo&g) Vložit (ukázat &dialog) Purge hidden events from selected parts Zbavit skryté události vybraných částí Copy in Range Paste C&lone Paste to Selected &Track Paste Clone to Selected Trac&k Paste (Show Dialo&g)... Shrink Selected Parts Expand Selected Parts Purge Hidden Events from Selected Parts Add Track Přidat stopu Insert Track Vložit stopu Select Vybrat Select &All Vybrat &vše &Deselect All &Zrušit výběr všeho Invert &Selection Obrátit &výběr &Inside Loop &Uvnitř smyčky &Outside Loop &Vně smyčky All &Parts on Track Všechny čá&sti na stopě Score Notový zápis All Tracks in One Staff One Staff per Track New Score Window... Pianoroll... Drums... Event List... Wave... Open in New Window Always open the part(s) in a new editor window Open Synth Plugin GUI... Midi &Transform... Global Cut - Selected Tracks Global Insert - Selected Tracks Global Split - Selected Tracks Change Note &Length Change Note &Velocity &Scripts Scripts for midi processing. Applied to selected midi parts (or else tracks). User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. &Display Display menu: View-specific display options. Toggle &Mixer Strip Configure &Custom Columns... New... Nothing to edit Není co upravovat all tracks in one staff Všechny stopy v jedné osnově one staff per track Jedna osnova na stopu New score window Nové okno s notovým zápisem Pianoroll Editor válečku Drums Editor bicích List Editor seznamu Wave Editor Wave Mastertrack Hlavní stopa Graphic Grafický editor Midi &Transform &Proměnit MIDI Global Cut Celkové vyjmutí Global Insert Celkové vložení Global Split Celkové rozdělení Global Cut - selected tracks Celkové vyjmutí - vybrané stopy Global Insert - selected tracks Celkové vložení - vybrané stopy Global Split - selected tracks Celkové rozdělení - vybrané stopy &Edit Úp&ravy Fu&nctions &Funkce &Structure &Stavba Functions Funkce &Quantize Notes &Kvantizovat noty Change note &length Změnit &délku noty Change note &velocity Změnit &sílu tónu noty Crescendo/Decrescendo Crescendo/Decrescendo Transpose Převedení Erase Events (Not Parts) Smazat události (nikoli části) Move Events (Not Parts) Přesunout události (nikoli části) Set Fixed Note Length Nastavit pevnou délku noty Delete Overlapping Notes Smazat překrývající se noty Legato Legato Window &Config &Aranžér Configure &custom columns Nastavit v&lastní sloupce Remove track(s) Odstranit stopu(y) Are you sure you want to remove this track(s)? Opravdu chcete odstranit stopu(y)? New Nový Changed Settings Nastavení změněna Unfortunately, the changed arranger column settings cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. (we'll try to fix that) Naneštěstí nelze změněná nastavení sloupce aranžéru použít, dokud MusE běží. Aby se změny použily, spusťte, prosím, MusE znovu. Promiňte (pokoušíme se to opravit) MusEGui::AudioComponentRack Aux send level (dB) Vol Hlasitost Pan Vyvážení Mute Volume/gain Panorama/Balance Gain Zesílení Calibration gain MusEGui::AudioMixerApp &Create &Vytvořit &View &Pohled Strips Routing Tok signálu Show Midi Tracks Ukázat stopy MIDI Show Drum Tracks Ukázat stopy bicích Show New Style Drum Tracks Ukázat stopy bicích v novém stylu Show Wave Tracks Ukázat stopy Wave Show Inputs Ukázat vstupy Show Outputs Ukázat výstupy Show Groups Ukázat skupiny Show Auxs Ukázat Aux Show Synthesizers Ukázat syntetizátory Traditional order Arranger order User order Show all hidden strips Unhide strip: (no hidden strips) MusEGui::AudioStrip panorama Vyvážení aux send level Úroveň poslání Aux Pan Vyvážení calibration gain Kalibrace zesílení 1/2 channel 1/2 kanály Pre Před pre fader - post fader Předprolínač - poprolínač dB dB record Nahrávat mute Ztlumit record downmix Nahrávat smíchání solo mode Režim sóla off Vypnuto input routing Vstupní signálový tok output routing Výstupní signálový tok Pre Fader Listening (PFL) Record downmix Record arm Mute or proxy mute Solo or proxy solo Track off Input monitor Input routing. Press F1 for help. Output routing. Press F1 for help. Volume/Gain Pass input through to output Off Vypnuto Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Auto off Read Číst Touch Dotknout se Write Zapsat Automation type Automation type: Off, Read, Touch or Write. Press F1 for help. L meter peak/clip R meter peak/clip automation type Typ automatizace MusEGui::BigTime format display Zobrazení formátu bar Takt beat Doba tick Tik minute Minuta second Sekunda frame Snímek subframe Podsnímek Format display Bar Takt Beat Doba Tick Minute Second Druhá Frame Subframe MusE: Bigtime MusE: Velký ukazatel času MusEGui::Canvas Tools: Nástroje: Tools Nástroje MusEGui::ClipListEdit MusE: Clip List Editor MusE: Editor seznamu ukázek Window &Config O&kno MusEGui::ComboQuant Off Vypnuto MusEGui::CompactKnob off Vypnuto MusEGui::CompactPatchEdit Patch name MusEGui::CompactSlider off Vypnuto MusEGui::CpuStatusBar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) CPU load averaged over each GUI update period. DSP load read from JACK. Number of xruns. Double-click to reset. MusEGui::CpuToolbar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) MusEGui::CtrlCanvas Make the current part's track match the selected drumlist entry Udělat to tak, aby stopa nynější části odpovídala vybranému záznamu v seznamu bicích Drawing hint: Hold Ctrl to affect only existing events Rada ke kreslení: Podržte klávesu Ctrl pro ovlivnění pouze stávající události Use pencil or line tool to draw new events Použijte tužku nebo nástroj na čáry pro nakreslení nových událostí Control canvas: Use Pencil tool to edit events and Draw tool to adjust them gradually. Hold Ctrl to affect only existing events. Merge options Erase target Erase target events between source events Erase target WYSIWYG Include last source item width when erasing Erase target inclusive Include entire source range when erasing Merge actions Merge Merge the dragged items Merge a copy Merge a copy of the dragged items Cancel drag Cancel dragging the items Use shift + pencil or line tool to draw new events Použijte klávesu Shift + tužku nebo nástroj na čáry pro nakreslení nových událostí MusEGui::CtrlPanel S Select controller S Select controller Remove panel Manual adjust (Ctrl-double-click on/off) All/Per-note velocity mode select controller Vybrat ovladač X Remove panel (destroy button) X remove panel Zavřít pohled s ovladačem manual adjust Ruční upravení ctrl-double-click on/off Ctrl-dvojité klepnutí pro zapnuto/vypnuto off Vypnuto all/per-note velocity mode Režim síly tónu vše/na notu Velocity Síla nárazu add new ... Přidat nový ovladač... Instrument-defined Vymezeno nástrojově Add ... Přidat... Others Jiné Edit instrument ... Upravit nástroj... Common Controls Obecné ovládání MusEGui::DList hide this instrument Skrýt tento nástroj show this instrument Ukázat tento nástroj this turns a grayed out eye into a blue eye Změní šedé oko na modré oko Hide this instrument This turns a blue eye into a crossed eye Show this instrument This turns a crossed eye into a blue eye Set field Sets a field Set row Sets a row Set column Sets a whole column to the field Set list Sets the whole list Reset field Resets a field in a row to default patch or instrument value Reset row Resets a row to the instrument values Reset column Resets a whole column to the instrument values Reset list Resets the whole list to the instrument values Reset track's drum list Resets all lists on all patches to the instrument values Promote field to default patch Promotes a field in a row to the default patch Promote row to default patch Promotes a row to the default patch Promote column to default patch Promotes a column to the default patch Promote list to default patch Promotes the whole list to the default patch Reset instrument field Reset instrument row Reset instrument list Set instrument field Set instrument row Set instrument list Drum list: Edit instrument-specific settings. MusEGui::DrumCanvas Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help. Moving items failed Přesun položek se nezdařil The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. Výběr se nepodařilo přesunout, protože by alespoň jedna nota byla přesunuta do stopy, která je jiná než jak původní stopa, tak stopa nynější části. Může pomoci nynější část změnit pomocí Alt+šipka vlevo/vpravo. Creating event failed Vytvoření události se nezdařilo Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Událost se nepodařilo vytvořit, protože nyní vybraná část není stejnou stopou, a vybraný nástroj by mohl být buď na žádné nebo na více částech, což je nejednoznačné. Vyberte cílovou část, pak to zkuste znovu. Recording event failed Nahrání události se nezdařilo Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Událost se nepodařilo nahrát, protože nyní vybraná část není stejnou stopou, a nástroj, který se má nahrát, by mohl být buď na žádné nebo na více částech, což je nejednoznačné. Vyberte cílovou část, pak to zkuste znovu. Internal error Vnitřní chyba Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. Vyskytla se, i když by jeden řekl, že je to nemožné, jedna opravdu ošklivá vnitřní chyba. Prověřte výstup konzole. Nic nebylo nahráno. Drum map Rozložení bicích Reset the track's drum map with instrument defaults? Nastavit rozložení bicích stopy znovu s výchozími nástroji? Note: Velocity: Start (bar): Start (time): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete LMB: Delete Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete. MusEGui::DrumEdit mute instrument Ztlumit nástroj sound name Název zvuku volume percent Procento hlasitosti quantisation Kvantizace this input note triggers the sound Tato vstupní nota spustí zvuk note length Délka noty this is the note which is played Toto je nota, která se přehrává output channel (hold ctl to affect all rows) Výstupní kanál (držet klávesu Ctrl pro ovlivnění všech řádků) output port (hold ctl to affect all rows) Výstupní přípojka (držet klávesu Ctrl pro ovlivnění všech řádků) shift + control key: draw velocity level 1 Klávesy Shift + Ctrl: Kreslit úroveň síly nárazu (dynamiky) 1 control key: draw velocity level 2 Klávesa Ctrl: Kreslit úroveň síly nárazu (dynamiky) 2 shift key: draw velocity level 3 Klávesa Shift: Kreslit úroveň síly nárazu (dynamiky) 3 draw velocity level 4 Kreslit úroveň síly nárazu (dynamiky) 4 output channel (ctl: affect all rows) Výstupní kanál (držet klávesu Ctrl pro ovlivnění všech řádků) output port (ctl: affect all rows) Výstupní přípojka (držet klávesu Ctrl pro ovlivnění všech řádků) &File &Soubor Load Map Nahrát rozložení bicích Save Map Uložit rozložení bicích Reset GM Map Nastavit rozložení GM znovu &Edit Úp&ravy Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Note which triggers the sound Note which is played Override track output channel (hold CTRL to affect all rows). Override track output port (hold CTRL to affect all rows). CTRL + META keys: Draw velocity level 1 META key: Draw velocity level 2 META + ALT keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) Cut Vyjmout Copy Kopírovat Copy events in range Kopírovat události v rozsahu Paste Vložit Paste to current part Paste (with Dialog) Vložit (s dialogem) Delete Events Smazat události &Select &Vybrat Select All Vybrat vše Select None Nevybrat nic Invert Obrátit Inside Loop Uvnitř smyčky Outside Loop Vně smyčky Previous Part Předchozí část Next Part Další část Event List... Fu&nctions &Funkce &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Cursor step Cursor tools Add controller view Re-order list Přeuspořádat seznam Set Fixed Length Nastavit pevnou délku Modify Velocity Změnit sílu tónu Crescendo/Decrescendo Crescendo/Decrescendo Quantize Kvantizovat Erase Event Smazat událost Move Notes Přesunout noty Delete Overlaps Smazat překrytí &Plugins &Moduly Window &Config O&kno Drum tools Nástroje pro bicí Load Drummap Nahrát rozložení bicích hide instrument Skrýt nástroj override track output channel (hold ctl to affect all rows) Potlačit výstupní kanál stopy (držet klávesu Ctrl pro ovlivnění všech řádků) override track output port (hold ctl to affect all rows) Potlačit výstupní kanál přípojky (držet klávesu Ctrl pro ovlivnění všech řádků) control + meta keys: draw velocity level 1 Klávesa Ctrl + meta klávesy: Kreslit úroveň síly tónu (dynamiky) 1 meta key: draw velocity level 2 Meta klávesa: Kreslit úroveň síly tónu (dynamiky) 3 draw default velocity level 3 Kreslit výchozí úroveň síly tónu (dynamiky) 3 meta + alt keys: draw velocity level 4 Meta + Alt klávesy: Kreslit úroveň síly tónu (dynamiky) 4 override track output channel (ctl: affect all rows) Potlačit výstupní kanál stopy (klávesa Ctrl: ovlivní všechny řádky) override track output port (ctl: affect all rows) Potlačit výstupní přípojku stopy (klávesa Ctrl: ovlivní všechny řádky) Re-order map Přeuspořádat rozložení &Display Display menu: View-specific display options. Group Seskupit Don't group Neseskupovat Group by channel Seskupit podle kanálu Group maximally Seskupit maximálně Show/Hide Ukázat/Skrýt Also show hidden instruments Ukázat i skryté nástroje Show all instruments Ukázat všechny nástroje Hide all instruments Skrýt všechny nástroje Only show used instruments Ukázat pouze použité nástroje Only show instruments with non-empty name or used instruments Ukázat pouze nástroje s vyplněným názvem nebo použité nástroje Step record Midi input Play events Drum map tools Nástroje rozložení bicích Store Drummap Uložit rozložení bicích Step Record Nahrávat po taktu Midi Input Vstup MIDI Play Events Přehrát události cursor tools Nástroje ukazovátka Cursor step: Krok ukazovátka: Set step size for cursor edit Nastavit délku kroku pro režim ukazovátka ctrl Ctrl Add Controller View Přidat pohled ovladače H hide H M mute M Sound Zvuk Vol Hlasitost QNT QNT E-Note Nota E Len Délka A-Note Nota A Ch K Port Přípojka LV1 LV1 LV2 LV2 LV3 LV3 LV4 LV4 Muse: Load Drum Map MusE: Nahrát rozložení bicích MusE: Store Drum Map MusE: Uložit rozložení bicích Drum map Rozložení bicích Reset the drum map with GM defaults? Nastavit rozložení bicích znovu s výchozím GM? Not all parts are displayed Ne všechny části jsou zobrazeny You selected both old-style-drumtracks and others (that is: new-style or midi tracks), but they cannot displayed in the same drum edit. I'll only display the old-style drumtracks in this editor, dropping the others. Vybral jste jak stopy bicích ve starém stylu a jiné (to je nový styl nebo stopy MIDI), ale tyto nelze zobrazit ve stejné úpravě bicích. V tomto editoru budou zobrazeny stopy bicích ve starém stylu, ostatní budou zahozeny. MusEGui::EditCAfterDialog MusE: Enter Channel Aftertouch MusE: Zadat dodělávku kanálu Time Position Časová poloha Pressure Tlak úhozu MusEGui::EditEventDialog Ok OK Cancel Zrušit MusEGui::EditInstrument Use note offs No note offs Convert to 0-vel note ons Control7 Ovladač 7 Control14 Ovladač 14 RPN RPN NPRN NPRN RPN14 RPN 14 NRPN14 NRPN 14 Pitch Výška tónu Program Program PolyAftertouch Vícenásobná dodělávka po Aftertouch Dodělávka po H hide H M mute M Name Název Vol Hlasitost Quant Kvantizace E-Note Nota E Len Délka A-Note Nota A Ch K Port Přípojka LV1 LV1 LV2 LV2 LV3 LV3 LV4 LV4 Tick Tik Data Data Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) MusE: Create file failed MusE: Vytvoření souboru se nezdařilo MusE: Write File failed MusE: Zápis souboru se nezdařil MusE: Save Instrument Definition MusE: Uložit vymezení nástroje Instrument Definition (*.idf) Vymezení nástroje (*.idf) MusE: Save instrument as MusE: Uložit nástroj jako Enter a new unique instrument name: Zadat nový jedinečný název nástroje: The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? Uživatelský nástroj '%1' již existuje. Toto přepíše jeho .idf soubor s nástrojem. Jste si jistý? MusE: Bad instrument name MusE: Špatný název nástroje Please choose a unique instrument name. (The name might be used by a hidden instrument.) Vyberte, prosím, jedinečný název nástroje. (Název může být použit skrytým nástrojem.) MusE: Bad patch name MusE: Špatný název záplaty Please choose a unique patch name Vyberte, prosím, jedinečný název pro záplatu MusE: Bad patchgroup name MusE: Špatný název skupiny záplat Please choose a unique patchgroup name Vyberte, prosím, jedinečný název pro skupinu záplat MusE: Bad controller name MusE: Špatný název ovladače Please choose a unique controller name Vyberte, prosím, jedinečný název pro ovladač New controller: Error Nový ovladač: Chyba Error! All control numbers are taken up! Clean up the instrument! Chyba! Všechna čísla ovládání jsou zabrána! Ukliďte nástroj! MusE: Cannot add common controller MusE: Nelze přidat obecný ovladač A controller named '%1' already exists. Ovladač s názvem '%1' již existuje. A controller number %1 already exists. Číslo ovladače '%1' již existuje. MusE MusE The current Instrument contains unsaved data Save Current Instrument? Nynější nástroj obsahuje neuložená data. Uložit nynější nástroj? &Save &Uložit &Nosave &Neukládat &Abort &Zrušit MusEGui::EditMetaDialog MusE: Enter Meta Event MusE: Zadání metaudálosti Time Position Časová poloha Meta Type Meta typ Enter Hex Zadat Hex MusEGui::EditPAfterDialog MusE: Enter Poly Aftertouch MusE: Zadat vícedodělávku Time Position Časová poloha Pitch Výška tónu Pressure Tlak úhozu MusEGui::EditToolBar select Pointer Tool: with the pointer tool you can: select parts move parts copy parts Nástroj ukazovátka: Tento nástroj umožňuje: Vybrat části Přesunout části Kopírovat části select Pencil Tool: with the pencil tool you can: create new parts modify length of parts Nástroj tužky": Tento nástroj umožňuje: Vytvořit nové části Změnit délku jednotlivých částí select Delete Tool: with the delete tool you can delete parts Nástroj gumy": Smazat části select Cut Tool: with the cut tool you can split a part Nástroj pro stříhání: S tímto nástrojem se části rozdělují select Glue Tool: with the glue tool you can glue two parts Nástroj pro přilepení: S tímto nástrojem se dvě jednotlivé části spojují v jednu select Score Tool: Nástroj pro notový zápis: select Quantize Tool: insert display quantize event Nástroj pro kvantizaci: S tímto nástrojem se vkládají události kvantizace select Drawing Tool Nástroj tužky na kreslení select Muting Tool: click on part to mute/unmute Nástroj pro ztlumení: Klepněte na část pro zapnutí/vypnutí ztlumení Select Pointer tool: With the pointer tool you can: select parts move parts copy parts Select Pencil tool: With the pencil tool you can: create new parts modify length of parts Select Delete tool: With the delete tool you can delete parts Select Cut tool: With the cut tool you can split a part Select Glue tool: With the glue tool you can glue two parts Select Score tool: Select Quantize tool: Insert display quantize event Select Drawing tool Select Muting tool: Click on part to mute/unmute Manipulate automation Pracovat na automatizaci Select Cursor (tracker mode) tool: With the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Select Range tool Select Panning tool Select Zoom tool Select Stretch tool Select Sample rate tool Pointer Pencil Eraser Cutter Glue Range Rozsah Pan Vyvážení Zoom Score Notový zápis Quantize Kvantizovat Draw Mute parts Edit automation Cursor Poloha ukazovátka Stretch Sample rate Vzorkovací kmitočet Cursor tool Nástroj ukazovátka pointer Ukazovátko pencil Tužka eraser Guma cutter Střihadlo score Notový zápis glue Lepidlo select Cursor (tracker mode) tool: with the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Vyberte nástroj ukazovátka (stopový režim): Pomocí nástroj ukazovátka můžete: pohybovat se pomocí kláves šipek k umísťování not používat VBNM měnit krok pomocí 0 a 9 select Range Tool Vybrat nástroj pro rozsahy select Panning Tool Vybrat nástroj pro vyvážení select Zoom Tool Vybrat nástroj pro zvětšení range Rozsah pan Vyvážení zoom Zvětšení quantize Kvantizovat draw Kreslit mute parts Ztlumit části edit automation Upravit automatizaci cursor Ukazovátko Edit Tools Upravit nástroje MusEGui::EffectRack effect rack Přihrádka s efekty new Nový change Změnit move up Posunout nahoru move down Posunout dolů remove Odstranit bypass Obejít show gui Ukázat uživatelské rozhraní show native gui Ukázat původní rozhraní save preset Uložit přednastavení Effect rack: Double-click a slot to insert/edit effect. RMB to open context menu. Press F1 for help. Effect rack Double-click a slot to insert FX New Nový Change Změnit Move up Move down Remove Odstranit Bypass Show gui Show native gui Save preset Uložit přednastavení Presets Přednastavení MusE: Save Preset MusE: Uložit přednastavení Replace effect Nahradit efekt Do you really want to replace the effect %1? Opravdu chcete nahradit efekt %1? MusEGui::GlobalSettingsConfig MusE: Choose start template or song MusE: Vybrat začáteční píseň nebo předlohu MusEGui::Header Track Info Columns Sloupce s informacemi o stopě Custom column MusEGui::LCDPatchEdit High bank: Low bank: Program (Ctrl-double-click on/off) off Vypnuto High bank Low bank Program Program MusEGui::LMaster MusE: Mastertrack MusE: Hlavní stopa &Edit Ú&&ravy Insert Tempo Vložit tempo Insert Signature Vložit taktové označení Insert Key Vložit tóninu Edit Positon Upravit polohu Edit Value Upravit hodnotu Delete Event Smazat událost Window &Config O&kno Edit tools Upravit nástroje Tempo Tempo Timesig Taktové označení Key Tónina new tempo Nové tempo new signature Nové taktové označení new key Nová tónina Meter Metrum Time Čas Type Typ Value Hodnota Signature Taktové označení Position Poloha Delete Smazat Insert tempo change Insert time signature change Insert key change Edit position Edit value Delete event Reposition of the initial tempo and signature events is not allowed Změna počátečního tempa a taktového označení není dovolena MusE: List Editor MusE: Editor seznamu Input error, conversion not OK or value out of range Chyba na vstupu. Převod není v pořádku, anebo je hodnota mimo rozsah Reposition of tempo and signature events to start position is not allowed! Změna událostí tempa a taktového označení na počáteční polohu není dovolena! MusEGui::ListEdit insert Note Vložit notu insert SysEx Vložit SysEx insert Ctrl Vložit Ctrl insert Meta Vložit Meta insert Channel Aftertouch Vložit dodělávku kanálu insert Poly Aftertouch Vložit více dodělávku kanálu &Edit Úp&ravy Cut Vyjmout Copy Kopírovat Paste Vložit Delete Events Smazat události Increase Tick Zvětšit tiky Decrease Tick Zmenšit tiky Window &Config O&kno Insert Note Vložit notu Insert SysEx Vložit SysEx Insert Ctrl Vložit Ctrl Insert Meta Vložit Meta Note SysEx Ctrl Meta Tick+ Tick- Delete Smazat Insert tools Vložit nástroje Tick Tiky Bar Takt Type Typ Ch K Val A Hodnota A Val B Hodnota B Val C Hodnota C Len Délka Comment Poznámka Insert note event Insert system exclusive event Insert controller event Insert meta event Increase tick value Decrease tick value Delete event MusE: List Editor MusE: Editor seznamu MusEGui::MPConfig Default input connections Výchozí vstupní spojení Are you sure you want to apply to all existing midi tracks now? Jste si jistý, že chcete použít na všechny existující stopy MIDI nyní? Default output connections Výchozí výstupní spojení Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? Nastavení se použije na nové stopy MIDI. Chcete použít na všechny existující stopy MIDI nyní? MusE: bad device name MusE: Špatný název zařízení please choose a unique device name Vyberte, prosím, jedinečný název pro zařízení in Vstup out Výstup Show first aliases Ukázat první přezdívku Show second aliases Ukázat druhou přezdívku Toggle all Přepnout vše Change all tracks now Změnit všechny stopy nyní Remove Odstranit Create Jack device Vytvořit zařízení Jack Port Number Číslo přípojky Enable gui Povolit rozhraní Enable reading Povolit čtení Enable writing Povolit zápis Port instrument Nástroj přípojky Midi device name. Click to edit (Jack) Název zařízení MIDI. Klepnout pro úpravu (Jack) Connections from Jack Midi outputs Spojení z výstupů MIDI Jack Connections to Jack Midi inputs Spojení do výstupů MIDI Jack Auto-connect these channels to new midi tracks Automaticky tyto kanály připojit do nových stop MIDI Auto-connect new midi tracks to these channels Automaticky připojit nové stopy MIDI do těchto kanálů Auto-connect new midi tracks to this channel Automaticky připojit nové stopy MIDI do tohoto kanálu Device state Stav zařízení Enable gui for device Povolit rozhraní pro zařízení Enable reading from device Povolit čtení ze zařízení Enable writing to device Povolit zápis na zařízení Name of the midi device associated with this port number. Click to edit Jack midi name. Název zařízení MIDI spojeného s tímto číslem přípojky. Klepněte pro změnu názvu MIDI Jack. Instrument connected to port Nástroj spojen s přípojkou Connections from Jack Midi output ports Spojení z výstupních přípojek MIDI Jack Connections to Jack Midi input ports Spojení k vstupním přípojkám MIDI Jack Auto-connect these channels, on this port, to new midi tracks. Automaticky připojit tyto kanály, na této přípojce, do nových stop MIDI. Connect new midi tracks to these channels, on this port. Připojit nové stopy MIDI do těchto kanálů, na této přípojce. Connect new midi tracks to this channel, on this port. Připojit nové stopy MIDI do tohoto kanálu, na této přípojce. State: result of opening the device Stav: Událost otevření zařízení Port Přípojka GUI Rozhraní I Vstup O Výstup Instrument Nástroj Device Name Název zařízení Please choose a unique device name Port number Midi device name Název zařízení MIDI Midi device type Typ zařízení MIDI Connections from Jack Midi Spojení od JACK MIDI Connections to Jack Midi Spojení do JACK MIDI Result of opening the device: OK: Assigned to a port and in use Closed: Unassigned to a port, or closed R/W Error: Unable to open for read or write Unavailable: USB midi unplugged, or external application not running, or synth plugin not installed etc. (Jack Midi devices have 'unavailable ports' in the routes columns.) Unavailable devices or ports can be purged with 'Remove' or with the advanced router. Enable Graphical User Interface for device Povolit pro zařízení grafické uživatelské rozhraní Connections from Jack Midi ports Spojení od přípojek JACK MIDI Connections to Jack Midi ports Spojení do přípojek JACK MIDI In routes Tok vstupního signálu Out routes Tok výstupního signálu Def in ch Výchozí vstupní kanál Def out ch Výchozí výstupní kanál State Stav Type Typ In Vstup Out Výstup <unknown> <neznámý> <none> <Žádný> MusEGui::MTScale bar scale Měřítko taktu Bar scale Bar scale: Use mouse buttons to set position and range markers. Hold Shift to set (LMB) or delete (RMB) custom markers. MusEGui::MTScaleFlo bar scale Měřítko taktu Bar scale MusEGui::MarkerView MusE: Marker MusE: Značka add marker Přidat značku delete marker Smazat značku &Edit Úp&ravy Window &Config O&kno edit tools Nástroje pro úpravy Add marker Delete marker Edit tools Upravit nástroje Bar:Beat:Tick Takt:Doba:Tik Hr:Mn:Sc:Fr:Sf Hod:Min:Sek:Sn:SR Lock Zámek Description Popis Edit tick Edit SMPTE timecode Toggle timecode editing Edit marker description <Description> Text Text Marker Properties Vlastnosti značky MusEGui::Master Tools Nástroje MusEGui::MasterEdit MusE: Mastertrack MusE: Hlavní stopa Window &Config O&kno Enable master Povolit hlavní Enable Povolit Enable usage of master track Povolit použití hlavní stopy Info Informace Cursor Poloha ukazovátka time at cursor position Čas na nynější značce polohy tempo at cursor position Tempo na nynější značce polohy Mastertrack Hlavní stopa &Display Display menu: View-specific display options. Time at cursor position Tempo at cursor position Off Vypnuto Bar Takt Snap Magnet CurPos Nynější Poloha tempo at current position Čas na nynější značce polohy time signature at current position Taktové označení na nynější značce polohy MusEGui::MetronomeConfig Reset accents: Resets all accents to the defaults (first in list) of the current preset category (Factory or User). Proceed? MusEGui::MidiAudioControl Control7 Ovladač 7 Control14 Ovladač 14 RPN RPN NPRN NPRN RPN14 RPN 14 NRPN14 NRPN 14 Pitch Výška tónu Program Program Aftertouch Dodělávka po MusEGui::MidiComponentRack Pro Var Var Rev Ozv Cho Sbo Pan Vyvážení Program Program VariationSend VariacePoslání ReverbSend OzvěnaPoslání ChorusSend SborPoslání Pan/Balance Vyvážení/Pan Controller Ovladač (Ctrl-double-click on/off) <unknown> <neznámý> Instrument Nástroj Transpose Převedení Transpose notes up or down Transponovat noty nahoru nebo dolů Delay Zpoždění Offset playback of notes before or after actual note Posun přehrávání not před nebo po skutečné notě Length Délka Change note length in percent of actual length Změnit délku noty v procentech skutečné délky Velocity <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> Compress Compress the notes velocity range, in percent of actual velocity Stlačit rozsah dynamiky not (procenta ze skutečné síly tónu) <Patch> MusEGui::MidiInputTransformDialog New Nový MusEGui::MidiStrip B Palette B B off Vypnuto Record arm Mute or proxy mute Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Track off Input monitor Intput routing. Press F1 for help. Output routing. Press F1 for help. Inst Inst Prop Instruments and controllers Properties Vlastnosti Volume/Gain (Ctrl-double-click on/off) Pass input through to output Input monitor: Pass input through to output. ctrl-double-click on/off Ctrl-dvojité klepnutí pro zapnuto/vypnuto VariationSend VariacePoslání Var Var ReverbSend OzvěnaPoslání Rev Ozv ChorusSend SborPoslání Cho Sbo dB dB Pan/Balance Vyvážení/Pan Pan Vyvážení record Nahrávat mute Ztlumit solo mode Režim sóla input routing Vstupní signálový tok output routing Výstupní signálový tok MusEGui::MidiSyncConfig Port Number Číslo přípojky Name of the midi device associated with this port number Název zařízení MIDI spojeného s tímto číslem přípojky Sync to this device. Click to select. Midi clock input detected Zjištěn vstup hodin MIDI Midi tick input detected Zjištěn vstup tiku MIDI Midi real time input detected Zjištěn vstup MIDI ve skutečném čase (RT) MMC input detected Zjištěn vstup MMC MTC input detected Zjištěn vstup MTC Detected SMPTE format Zjištěn formát SMPTE Receive id number. 127 = Global. Double click to edit. Přijmout číslo ID. 127 = Celkové. Dvojité klepnutí pro úpravy. Accept midi clock input Přijmout vstup hodin MIDI Accept midi real time input Přijmout vstup MIDI ve skutečném čase (RT) Accept MMC input Přijmout vstup MMC Accept MTC input Přijmout vstup MTC Receive start rewinds before playing Přijmout začátek přetočí před přehráváním Transmit id number. 127 = Global. Double click to edit. Odeslat číslo ID. 127 = Celkové. Dvojité klepnutí pro úpravy. Send midi clock output Poslat výstup hodin MIDI Send midi realtime output Poslat výstup MIDI ve skutečném čase (RT) Send MMC output Poslat výstup MMC Send MTC output Poslat výstup MTC Type Typ Midi clock input detected. Current port actually used is red. Click to force a port to be used. Zjištěn vstup hodin MIDI. Nynější skutečně používaná přípojka je červená. Klepněte pro vynucení přípojky, která se má používat. Midi realtime input detected, including start/stop/continue, and song position. Zjištěn vstup MIDI ve skutečném čase (RT), včetně spustit/zastavit/pokračovat a polohy písně. MMC input detected, including stop/play/deferred play, and locate. Zjištěn vstup MMC, včetně zastavit/přehrát/odložené přehrávání a určit polohu. MTC input detected, including forward quarter-frame sync and full-frame locate. Current port actually used is red. Click to force a port to be current. Zjištěn vstup MTC, včetně seřízení dopředu-čtvrťový-snímek a rozpoznání polohy celého-snímku. Nyní používaná přípojka je červená. Klepněte pro vynucení jiné připojky. Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detects format of MTC quarter and full frame, and MMC locate. Zjištěn formát SMPTE: 24fps, 25fps, 30fps drop snímek, nebo 30fps ne-drop Zjišťuje formát MTC čtvrťového a celého snímku, a rozpoznání polohy MMC. Receive id number. 127 = global receive all, even if not global. Přijmout číslo ID. 127 = celkové přijmout vše, i když není celkové. Accept midi clock input. Only one input is used for clock. Auto-acquire: If two or more port realtime inputs are enabled, the first clock detected is used, until clock is lost, then another can take over. Best if each turns off its clock at stop, so MusE can re-acquire the clock from another port. Click on detect indicator to force another. Přijmout vstup hodin MIDI. Pro hodiny se používá jen jeden vstup. Automaticky použít: Pokud je povoleno více než jeden vstup ve skutečném čase, použijí se první rozpoznané hodiny, dokud nejsou ztraceny. Pak se převezmou jiné. Nejlepší je, když každý při zastavení své hodiny vypne, takže MusE může používat hodiny z jiné přípojky. Klepněte na ukazatel rozpoznání pro vynucení jiné. Accept midi realtime input, including start/stop/continue, and song position. Non-clock events (start,stop etc) are accepted by ALL enabled ports. This means you may have several master devices connected, and muse will accept input from them. Přijmout vstupy ve skutečném čase MIDI, včetně Spustit/Zastavit/Pokračovat a Poloha písně. Ne-hodinové události (Spustit/Zastavit atd.) jsou přijaty VŠEMY zapnutými přípojkami. To znamená, že můžete mít připojeno více hlavních zařízení, a MusE přijme jejich data. Accept MMC input, including stop/play/deferred play, and locate. Přijmout vstup MMC včetně zastavit/přehrát/pozdržené přehrávání a poloha. Accept MTC input, including forward quarter-frame sync and full-frame locate. See 'rc' column for more help. Přijmout vstup MTC včetně seřízení dopředu-čtvrťový-snímek a rozpoznání polohy celého-snímku. Podívejte se na sloupec "rc" pro další nápovědu. When start is received, rewind before playing. Note: It may be impossible to rewind fast enough to synchronize with the external device. Když je přijato spuštění, přetočí se před přehráváním zpět. Poznámka: Může být nemožné provést přetočení zpět dostatečně rychle tak, aby se zůstalo v seřízeném stavu s vnějším zařízením. Sync to this device. Click to select. MusE will synchronize to this device's clock or MTC quarter-frame, if 'slave to external sync' is on. MTC input detected, including forward quarter-frame sync and full-frame locate Accept midi clock input. Only one port can be used for clock sync. Accept MTC input, including forward quarter-frame sync and full-frame locate. Only one port can be used for sync. When start is received, rewind before playing. Note: In some cases, such as having many project audio tracks, it may be impossible to rewind fast enough to synchronize with the external device. Manually rewinding the device before playing is recommended. Transmit id number. 127 = global transmit to all. Odeslat číslo ID. 127 = celkové odeslat všem. Send midi clock output. If 'Slave to External Sync' is chosen, muse can re-transmit clock to any other chosen ports. Poslat výstup hodin MIDI. Když je vybráno "Spojit s vnějším sync", může MusE signál hodin předat jakýmkoli dalším vybraným přípojkám. Send midi realtime output, including start/stop/continue, and song position. If 'Slave to external sync' is chosen, muse can re-transmit midi realtime input messages to any other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. Poslat výstup ve skutečném čase MIDI, včetně Spustit/Zastavit/Pokračovat a Poloha písně. Když je vybráno "Spojit s vnějším sync", může MusE zprávy vstupu ve skutečném čase MIDI znovu předat jakýmkoli dalším vybraným přípojkám. To znamená, že můžete mít připojeno více "otrockých"zařízení a MusE může zprávy ve skutečném čase předat/poslat znovu jednomu nebo všem. Port Přípojka Device Name Název zařízení s s c c k k r r m m t t type Typ rid rid rc rc rr rr rm rm rt rt rw rw tid tid tc tc tr tr tm tm tt tt None Žádný Tiny Drobná Small Malá Large Velká Large with pre-detect Velká s předzjištěním MusE MusE Settings have changed Apply sync settings? Nastavení se změnila. Použít nastavení pro seřízení? &Apply &Použít &No &Ne &Abort &Zrušit <none> <Žádný> MusEGui::MidiTrackInfo <unknown> <neznámý> MusEGui::MidiTransformerDialog New Nový MusEGui::MixdownFileDialog Wave Files (*.wav);;All Files (*) Soubory Wave (*.wav);;Všechny soubory (*) MusEGui::MusE Failed to start audio! Nepodařilo se spustit zvuk! Was not able to start audio, check if jack is running. Nepodařilo se spustit zvuk. Ověřte, že JACK běží. Timeout waiting for audio to run. Check if jack is running. Překročení času při čekání na zvuk. Ověřte, že JACK běží. Und&o &Zpět Re&do Z&novu undo last change to song Vrátit poslední změnu na písni redo last undo Udělat poslední vrácení o krok zpět Loop Smyčka loop between left mark and right mark Smyčka mezi levou značkou a pravou značkou Punchin Začít nahrávání na levé značce record starts at left mark Nahrávání začne na levé značce Punchout Ukončit nahrávání na pravé značce record stops at right mark Nahrávání skončí na pravé značce Start Spustit rewind to start position Skočit na začátek Rewind O jeden takt zpět rewind current position Skočit o jeden takt zpět Forward O jeden takt vpřed move current position Skočit o jeden takt vpřed Stop Zastavit stop sequencer Zastavit přehrávání Play Přehrát start sequencer play Spustit přehrávání Record Nahrávat to record press record and then play Pro nahrávání klepněte nejprve na tlačítko "Nahrávat" a potom na "Přehrávat" Panic Nouzové zastavení send note off to all midi channels Poslat příkaz "Nota vypnuta" všem kanálům MIDI turn on/off metronome Zapnout/Vypnout metronom &New &Nový Create New Song Vytvořit novou píseň &Open &Otevřít píseň Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. Klepněte na toto tlačítko pro otevření <em>Nové písně</em>.<br> Také můžete v nabídce Soubor vybrat příkaz <b>Nová píseň</b>. Open &Recent Otevřít &poslední píseň &Save &Uložit Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. Klepněte na toto tlačítko pro uložení vámi právě upravované písně s automaticky položeným dotazem na název souboru. Také můžete v nabídce Soubor vybrat příkaz Uložit. Save &As Uložit &jako Import Midifile Zavést soubor MIDI Export Midifile Vyvést soubor MIDI Import Part Zavést část Import Audio File Zavést soubor Audio Find unused wave files Najít nepoužívané soubory Wave &Quit &Ukončit Song Info Informace o písni Transport Panel Ovládací panel přehrávání Bigtime Window Velký ukazatel času Mixer A Směšovač A Mixer B Směšovač B Cliplist Seznam ukázek Marker View Pohled se značkami Arranger View Pohled s aranžérem Fullscreen Celá obrazovka &Plugins &Přídavné moduly Edit Instrument Upravit nástroj Input Plugins Vstupní přídavné moduly Transpose Převedení Midi Input Transform Proměnit vstup MIDI Midi Input Filter Vstupní filtr MIDI Midi Remote Control Vzdálené ovladání MIDI Rhythm Generator Generátor rytmu Reset Instr. Nastavit nástroj znovu Init Instr. Zapnout nástroj Local Off Místní vypnuto Bounce to Track Odmíchat na stopu Bounce to File Odmíchat do souboru Restart Audio Spustit zvuk znovu Mixer Automation Automatizace míchacího pultu Take Snapshot Udělat snímek Clear Automation Data Smazat data automatizace Cascade Překrývat Tile Klást vedle sebe In rows V řádcích In columns Ve sloupcích Global Settings Celková nastavení Configure Shortcuts Nastavit klávesové zkratky Follow Song Sledovat píseň Don't Follow Song Nesledovat píseň Follow Page Sledovat píseň na stranách Follow Continuous Sledovat píseň stále Set left/right marker for bounce range MusE: Song: MusE: Píseň: Metronome Metronom Midi Sync Seřízení MIDI Midi File Import/Export Zavedení/Vyvedení souboru MIDI Appearance Settings Nastavení vzhledu Midi Ports / Soft Synth Přípojky MIDI/Softwarové syntetizátory &Manual &Příručka &MusE Homepage Stránky &MusE &Report Bug... &Nahlásit chybu... &About MusE &O programu MusE Song Position Poloha písně Tempo Tempo Signature Taktové označení File Buttons Tlačítka pro soubor Undo/Redo Zpět/Znovu Transport Přehrávání Cpu load Vytížení procesoru Measured CPU load Měřené vytížení procesoru No CPU load data Žádné údaje o vytížení procesoru &File &Soubor &View &Pohled &Midi &MIDI &Audio &Zvuk A&utomation A&utomatizace &Windows &Okna Se&ttings Nas&tavení &Help &Nápověda About &Qt O &Qt Cannot read template Nelze přečíst předlohu File open error Chyba při otevírání souboru File read error Chyba při čtení souboru Unknown File Format: %1 Neznámý formát souboru: %1 MusE: Song: %1 MusE: Píseň: %1 MusE: load project MusE: Nahrát projekt Timeout waiting for audio to run. Check if jack is running or try another driver. Was not able to start audio, check if jack is running or try another driver. Undo last change to song Redo last undo Loop between left mark and right mark Punch in Record starts at left mark Recording starts at left mark Punch out Record stops at right mark Recording stops at right mark Rewind to start position Rewind current position Move current position Stop sequencer Restart rec Start sequencer play To record press record and then play Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help. Turn on/off metronome Metronome on/off. Press F1 for help. Create new song New from &Template... Create new song from template &Open... &Otevřít... Open song from file Click this button to open an existing song. Save current song Click this button to save the song you are editing. You will be prompted for a file name. Save &As... Uložit &jako... &Close &Zavřít Import Midifile... Export Midifile... Import Part... Import Audio File... Find Unused Wave Files... Song Info... Mastertrack Hlavní stopa Edit Instrument... Transpose... Midi Input Transform... Midi Input Filter... Midi Remote Control... Reset Instrument Init Instrument Send 'local-off' command to all midi channels. Bounce to File... Global Settings... Appearance... Rewind on Stop Metronome... Midi Sync... Midi File Import/Export... &MusE Homepage... &Did You Know? &About MusE... Snooper (developer tool)... About &Qt... The project has no project sample rate (added 2011). Please enter a rate. The current system rate (%1Hz) is suggested, and cancelling uses it: The project has audio waves, but no project sample rate (added 2011). Please enter a rate. The most common wave rate found is suggested, the project was probably made with it. Cancelling uses the current system rate (%1Hz): Project sample rate MusE: load template MusE: Nahrát předlohu MusE: Write File failed MusE: Zápis souboru se nezdařil The current Project contains unsaved data Save Current Project? Nynější projekt obsahuje neuložená data. Uložit nynější projekt? S&kip &Přeskočit Load configuration colors &Cancel Z&rušit Color settings will immediately be replaced with any found in the file. Are you sure you want to proceed? &Ok &OK Save configuration colors MusE: Save As MusE: Uložit jako Nothing to edit Není co upravovat MusE: Bounce to Track MusE: Odmíchat na stopu No wave tracks found Nebyly nalezeny žádné stopy Wave No audio output tracks found Nebyly nalezeny žádné zvukové výstupní stopy Select one audio output track, and one target wave track Vyberte jednu zvukovou výstupní stopu a jednu cílovou stopu Wave Select one target wave track Vyberte jednu cílovou stopu Wave Select one target wave track, and one audio output track Vyberte jednu cílovou stopu Wave a jednu zvukovou výstupní stopu MusE: Bounce to File MusE: Odmíchat do souboru Select one audio output track Vyberte jednu zvukovou výstupní stopu MusE: Bounce MusE: Odmíchat set left/right marker for bounce range Nastavit levou/pravou značku pro oblast odmíchání The current Project contains unsaved data Load overwrites current Project: Save Current Project? Nynější projekt obsahuje neuložená data. Nahrání přepíše nynější projekt: Uložit nynější projekt? &Abort &Zrušit This will clear all automation data on all audio tracks! Proceed? Toto smaže všechna data automatizace u všech zvukových stop! Pokračovat? This takes an automation snapshot of all controllers on all audio tracks, at the current position. Proceed? Toto udělá v nynější poloze snímek automatizace všech ovladačů na všech zvukových stopách. Pokračovat? MusE: Warning MusE: Varování The song uses multiple ports but export format 0 (single track) is set. The first track's port will be used. Playback will likely be wrong unless the channels used in one port are different from all other ports. Canceling and setting a different export format would be better. Continue? MusE: Export Midi MusE: Vyvést MIDI no help found at: Nebyla nalezena žádná nápověda: MusE: Open Help MusE: Otevřít nápovědu Unable to launch help Nelze spustit nápovědu For some reason MusE has to launch the default browser on your machine. Z určitého důvodu musí MusE na vašem stroji spustit výchozí prohlížeč. MusE: Import Midi MusE: Zavést MIDI Add midi file to current project? Přidat soubor MIDI do nynějšího projektu? &Add to Project &Přidat do projektu &Replace &Nahradit reading midifile Čte se soubor MIDI Reading midifile failed: Nepodařilo se: Import part is only valid for midi and wave tracks! Zavedení části je platné pouze pro stopy MIDI a Wave! MusE: load part MusE: Nahrát část No track selected for import Pro zavedení nebyla vybrána žádná stopa %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. Nepodařilo se zavést %n část z %1. Pravděpodobně má vybraná stopa nesprávný typ. Nepodařilo se zavést %n části z %1. Pravděpodobně má vybraná stopa nesprávný typ. Nepodařilo se zavést %n částí z %1. Pravděpodobně má vybraná stopa nesprávný typ. %n part(s) could not be imported. Likely the selected track is the wrong type. Nepodařilo se zavést %n část. Pravděpodobně má vybraná stopa nesprávný typ. Nepodařilo se zavést %n části. Pravděpodobně má vybraná stopa nesprávný typ. Nepodařilo se zavést %n částí. Pravděpodobně má vybraná stopa nesprávný typ. to import an audio file you have first to selecta wave track pro zavedení zvukového souboru nejprve musíte vybrat stopu Wave To import an audio file you have first to select a wave track Import Wavefile Zavést soubor Wave This wave file has a samplerate of %1, as opposed to current setting %2. File will be resampled from %1 to %2 Hz. Do you still want to import it? Tento soubor Wave má vzorkovací kmitočet %1, jako protiklad k nynějšímu nastavení %2. Soubor bude převzorkován z %1 na %2 Hz Stále ještě jej chcete zavést? Wave import error Chyba při zavedení WAVE There are too many wave files of the same base name as imported wave file Can not continue. Je tu příliš mnoho souborů WAVE se stejným základním názvem, jako má zavedený soubor WAVE. Nelze pokračovat. Can't create new wav file in project folder! Nelze vytvořit nový soubor WAVE ve složce s projektem! Failed to initialize sample rate converter! Nepodařilo se spustit měnič vzorkovacího kmitočtu! Cancel Zrušit Show Docks Toggle display of currently visible dock windows. Display MusE main window in full screen mode. Mastertrack Graphic... Send 'note-off' command to all midi channels. Send initialization messages as found in instrument definition. Take Automation Snapshot Keyboard Shortcuts... Midi Ports / Soft Synths... &Manual (wiki)... File buttons Timeline Recording Sync Seřízení Position Poloha The current project contains unsaved data. Save current project? No valid parts selected The current project contains unsaved data. Load overwrites current project. Save current project? This wave file has a samplerate of %1 Hz, as opposed to current setting %2 Hz. A live, real-time samplerate converter can be used on this file. Or, a copy of the file can be resampled now from %1 Hz to %2 Hz. Do you want to use a converter or resample the file now? Use live converter Resample now Resampling wave file "%1" from %2 to %3 Hz... Převzorkovává se soubor WAVE "%1" z %2 na %3 Hz... Output has clipped Resampling again and normalizing wave file "%1" Try %2 of %3... Výstup byl zastřižen. Znovu se převzorkovává a normalizuje soubor WAVE "%1" Zkuste %2 z %3... This wave file has a samplerate of %1, as opposed to current setting %2. Do you still want to import it? Tento soubor Wave má vzorkovací kmitočet %1, jako protiklad k nynějšímu nastavení %2. Stále ještě jej chcete zavést? &Yes &Ano &No &Ne File version is %1.%2 Current version is %3.%4 Conversions may be applied if file is saved! Verze souboru je %1.%2 Nynější verze je %3.%4 Převedení je možné použít, pokud je soubor uložen! Opening file Otevírá se soubor Do not warn again Nevarovat znovu Unable to launch browser Error launching default browser MusEGui::NoteInfo Note Info Informace o notě delta/absolute mode Delta/Absolutní režim Delta/Absolute mode Start Spustit Len Délka Pitch Výška tónu Velo on Velo off Velo On Síla nárazu zapnuto Velo Off Síla nárazu vypnuto MusEGui::PartCanvas Cannot copy/move/clone to different Track-Type Nelze kopírovat/přesunovat/klonovat na jiný typ stopy Part: Část: C&ut Vyj&mout &Copy &Kopírovat s&elect &Vybrat clones Klony rename Přejmenovat color Barva delete Smazat split Rozdělit glue Lepidlo super glue (merge selection) Vynikající lepidlo (sloučit výběr) de-clone Zrušit klonování save part to disk Uložit část na disk wave edit Editor Wave file info Informace o souboru Part S&elect Rename Přejmenovat Color Delete Smazat Split Glue Super glue (merge selection) De-clone Save part to disk... Wave edit... File info... MusE: Save part Automation Automatizace Normalize Normalizovat MusE: save part MusE: Uložit část Part name: %1 Files: Název části: %1 Soubory: Automation: Automatizace: Part canvas: Use Pencil tool to draw parts. Double-click to create a new MIDI/drum part between the range markers (set with MMB + RMB). Press F1 for help. Remove selected Odstranit vybrané LMB: Select/Move/Dblclick to edit | CTRL+LMB: Multi select/Move&Copy | CTRL+ALT+LMB: Dblclick to edit in new window | SHIFT+LMB: Select track | MMB: Delete LMB: Draw to resize | MMB: Delete | CTRL+RMB: Trim length LMB: Delete | CTRL+RMB: Trim length LMB: Cut part in two LMB: Merge with following part LMB: Mute selected part LMB: Edit automation events in audio parts %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. Nepodařilo se vložit %n část z %1. Pravděpodobně má vybraná stopa nesprávný typ. Nepodařilo se vložit %n části z %1. Pravděpodobně má vybraná stopa nesprávný typ. Nepodařilo se vložit %n částí z %1. Pravděpodobně má vybraná stopa nesprávný typ. %n part(s) could not be pasted. Likely the selected track is the wrong type. Nepodařilo se vložit %n část. Pravděpodobně má vybraná stopa nesprávný typ. Nepodařilo se vložit %n části. Pravděpodobně má vybraná stopa nesprávný typ. Nepodařilo se vložit %n částí. Pravděpodobně má vybraná stopa nesprávný typ. Cannot paste: multiple tracks selected Nelze vložit: vybráno více stop Cannot paste: no track selected Nelze vložit: nevybrána žádná stopa Can only paste to midi/drum track Vložení možné jen do stopy MIDI/Bicí Can only paste to wave track Vložení možné jen do stopy Wave Can only paste to midi or wave track Vložení možné jen do stopy MIDI nebo Wave Cannot paste: wrong data type Nelze vložit: Nesprávný datový typ MusEGui::PasteDialog %n quarter(s) %n čtvrťová nota %n čtvrťové noty %n čtvrťových not %1 quarter for floating-point arguments like 1.5 %1 čtvrťová nota %1 quarters for floating-point arguments like 1.5 %1 čtvrťové noty MusEGui::PasteEventsDialog %n quarter(s) %n čtvrťová nota@@ %n čtvrťové noty %n čtvrťových not %1 quarter for floating-point arguments like 1.5 %1 čtvrťová nota %1 quarters for floating-point arguments like 1.5 %1 čtvrťové noty MusEGui::Piano Piano: Press key to play or enter events in step record mode (SHIFT for chords). RMB: Set cursor for polyphonic control events. CTRL+Mousewheel to zoom view vertically. Left click: Play Right click: Select key MusEGui::PianoCanvas Pianoroll canvas: Use Pencil tool to draw and edit MIDI events, Pointer tool to select and edit. Press F1 for help. Note: Velocity: Start (bar): Start (time): Length (ticks): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete | CTRL+RMB: Trim length LMB: Resize | CTRL+LMB: Multi select | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length LMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length MusEGui::PianoRoll &Edit Úp&ravy C&ut Vyj&mout &Copy &Kopírovat Copy events in range Kopírovat události v rozsahu &Paste &Vložit Paste to current part Paste (with dialog) Vložit (s dialogem) Delete &Events Smazat &události &Select &Vybrat Select &All Vybrat &vše &Deselect All &Zrušit výběr všeho Invert &Selection Obrátit &výběr &Inside Loop &Uvnitř smyčky &Outside Loop &Vně smyčky &Previous Part &Předchozí část &Next Part &Další část Event List... Fu&nctions &Funkce Quantize Kvantizovat Modify Note Length Změnit délku noty Modify Velocity Změnit sílu tónu Crescendo/Decrescendo Crescendo/Decrescendo Transpose Převedení Erase Events Smazat události Move Notes Přesunout noty Set Fixed Length Nastavit pevnou délku Delete Overlaps Smazat překrytí Legato Legato &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Play single note Play chords &Plugins &Moduly &Display Display menu: View-specific display options. Add controller view Step record Midi input Play events Window &Config O&kno &Event Color Barva &události &Blue &Modrá &Pitch colors Barvy pro &výšku tónu &Velocity colors Barvy pro &sílu tónu Pianoroll tools Nástroje pro váleček Step Record Nahrávat po taktu Midi Input Vstup MIDI Play Events Přehrát události ctrl Ctrl Add Controller View Přidat pohled ovladače MusEGui::PluginDialog MusE: select plugin MusE: Vybrat přídavný modul MusE: Select Plugin Plugin categories. Right-click on tabs to manage. Right-click on plugins to add/remove from a category. Skupiny přídavných modulů. Klepnutí pravým tlačítkem myši pro spravování. Klepnutí pravým tlačítkem myši na přídavné moduly pro přidání/odstranění ze skupiny. Type Typ Lib Lib Label Štítek Name Název AI Al AO AO CI Cl CO CO IP IP FB 2B 8' {2B?} id ID Maker Výrobce Copyright Autorské právo Audio inputs Vstupy zvuku Audio outputs Výstupy zvuku Control inputs Vstupy ovládání Control outputs Výstupy ovládání In-place capable Schopen v místě Requires fixed block size Requires power-of-2 block size ID number Číslo ID &Create new group &Delete currently selected group Re&name currently selected group New group Nová skupina &create new group &Vytvořit novou skupinu &delete currently selected group &Smazat nyní vybranou skupinu re&name currently selected group &Přejmenovat nyní vybranou skupinu Associated categories Sdružené skupiny You need to define some categories first. Nejprve musíte stanovit nějaké skupiny. new group Nová skupina Enter the new group name Zadat nový název skupiny Wine VST Wine VST Ok OK Cancel Zrušit Show plugs: Ukázat přídavné moduly: Mono and Stereo Mono a stereo Stereo Stereo Mono Mono Show All Ukázat vše Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. Vyberte, které typy přídavných modulů mají být v seznamu viditelné.<br>Uvědomte si, že použití přídavných modulů monona stopy stereo není problém, dva budou použity souběžně.<br>Dejte si pozor na to, že alternativa 'vše' zahrnuje přídavné moduly, které v přihrádce s efekty nemusí být užitečné. Search in 'Label' and 'Name': Hledat ve 'Štítek' a 'Název': dssi synth syntetizátor dssi dssi effect efekt dssi LV2 synth Syntetizátor LV2 LV2 effect Efekt LV2 VST synth VST effect ladspa ladspa MusEGui::PluginGui File Buttons Tlačítka pro soubor Load Preset Nahrát přednastavení Save Preset Uložit přednastavení Bypass plugin Plugin settings MusE: Save preset Plugin Settings bypass plugin Přeskočit přídavný modul pro tok signálu MusE: load preset MusE: Nahrát přednastavení Error reading preset. Might not be right type for this plugin Chyba při čtení přednastavení. Nemusí to být správný typ pro tento přídavný modul MusE: save preset MusE: Uložit přednastavení MusEGui::PluginSettings Global setting: On Zapnuto Off Vypnuto MusEGui::PopupMenu <More...> %1 <Více...> %1 MusEGui::PosToolbar Left marker Left marker position Right marker Right marker position Current position Current position in bars/beats SMPTE position Current position in SMPTE time Show/Hide position in ticks and audio frames Show/Hide additional output fields displaying the current position in ticks and audio frames Current position in ticks Current position in audio frames Current position slider MusEGui::ProjectCreateImpl Select directory Vybrat adresář MusEGui::RasterLabelCombo Snap Magnet MusEGui::RecToolbar Record mode Record mode: Overdub to add new events, Replace to replace overlapping events. Overdub Přepsat Replace Nahradit Cycle record mode Cycle record mode: Normal to replace range when loop is finished, Mix to add new events, Replace to replace range on first MIDI input. Normal Normální Mix MusEGui::RouteDialog Normal Normální Alias 1 Přezdívka 1 Alias 2 Přezdívka 2 Source Zdroj Destination Cíl <none> <Žádný> MusEGui::RoutePopupMenu Channel Kanál <none> <Žádný> Soloing chain Řetěz sóla Channel grouping: Seskupení kanálů: Mono Mono Stereo Stereo Channels Kanály Midi ports/devices Přípojky/Zařízení MIDI Omni Omni Show aliases: Ukázat přezdívky: First První Second Druhá Show names Ukázat názvy Show first aliases Ukázat první přezdívku Show second aliases Ukázat druhou přezdívku Jack ports Přípojky JACK Connect Spojit Unavailable Nedostupné Open advanced router... Otevřít pokročilý směrovač... Warning: No output devices! Varování: Žádná výstupní zařízení! Output routes: Výstupní signálový tok: Input routes: Vstupní signálový tok: Audio returns Zpáteční kanály zvuku Midi sends Odeslání MIDI Sources: Zdroje: Warning: No input devices! Varování: Žádná vstupní zařízení! ALSA devices JACK devices Synth devices Open midi config... Otevřít nastavení MIDI... Toggle all Přepnout vše More... Více... Tracks Stopy Destinations: Cíle: Audio sends Zvuk posílá Midi port sends Přípojka MIDI posílá MusEGui::ScoreCanvas Treble Houslový klíč Bass Basový klíč Grand Staff Oba klíče Remove staff Odstranit osnovu Ambiguous part Nejasná část There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. Jsou dvě nebo tři možné části, ke kterým se dá nota přidat, ale žádná není vybrána. Vyberte, prosím, cílovou část klepnutím na jakoukoli notu k ní patřící, a zkuste to znovu, nebo přidejte novou notovou osnovu, která bude obsahovat jen cílovou část. No part Žádná část There are no parts you could add the note to. Nejsou tu žádné části, do kterých byste mohl přidat notu. Tools Nástroje MusEGui::ScoreEdit Step recording tools Nástroje pro nahrávání po taktu Step Record Nahrávat po taktu Note settings Nastavení noty Note length: Délka noty: last poslední Apply to new notes: Použít na nové noty: Apply to selected notes: Použít na vybrané noty: Velocity: Síla tónu: Off-Velocity: Síla tónu vypnuto: Quantisation settings Nastavení kvantizace Step record Quantisation: Kvantizace: Pixels per whole: Pixelů na celou notu: &Edit Úp&ravy C&ut Vyj&mout &Copy &Kopírovat Copy events in range Kopírovat události v rozsahu Quantisation Pixels per whole Note length Velocity Apply to selected notes, or new notes if none is selected Off-Velocity Copy Events in Range &Paste &Vložit Paste (with dialog) Vložit (s dialogem) Delete &Events Smazat &události &Select &Vybrat Select &All Vybrat &vše &Deselect All &Zrušit výběr všeho Invert &Selection Obrátit &výběr &Inside Loop &Uvnitř smyčky &Outside Loop &Vně smyčky Fu&nctions &Funkce &Quantize &Kvantizovat Change Note &Length Change Note &Velocity Display menu: View-specific display options. Note Head &Colors &Default Set Up &Preamble Display &Key Signature Display &Time Signature Set Score &Name Change note &length Změnit &délku noty Change note &velocity Změnit &sílu tónu noty Crescendo/Decrescendo Crescendo/Decrescendo Transpose Převedení Erase Events Smazat události Move Notes Přesunout noty Set Fixed Length Nastavit pevnou délku Delete Overlaps Smazat překrytí Legato Legato &Display Window &Config O&kno Note head &colors &Barvy pro hlavičky not &Black Če&rná &Velocity &Síla tónu &Part Čá&st Set up &preamble Nastavit úv&od Display &key signature Zobrazit &předznamenání Display &time signature Zobrazit &taktové označení Set Score &name Nastavit &název notového zápisu Enter the new score title Zadat nový název notového zápisu Error Chyba Changing score title failed: the selected title is not unique Změna názvu notového zápisu se nezdařila: zvolený název není jedinečný MusEGui::ScrollScale next page Další strana previous page Předchozí strana current page number Číslo nynější strany MusEGui::ShortcutCaptureDialog Ok OK Cancel Zrušit Shortcut conflicts with %1 Klávesová zkratka se střetává s %1 Undefined Nestanoveno MusEGui::ShortcutConfig Save printable text file Uložit tisknutelný soubor s textem Text files (*.txt);;All files (*) Textové soubory (*.txt);;Všechny soubory (*) Error Chyba Error opening file for saving Chyba při otevírání souboru pro uložení Shortcuts for selected category: Zkratky pro vybranou skupinu: Legend: Vysvětlivky: An error occurred while saving Při ukládání se vyskytla chyba MusEGui::SigScale signature scale Měřítko taktového označení Signature scale MusEGui::SigToolbar Time signature at current position Signature: Taktové označení: MusEGui::SigToolbarWidget time signature at current position Taktové označení na nynější značce polohy Signature: Taktové označení: MusEGui::Strip Remove track? Odstranit stopu? Name Název Enter track name: MusE: bad trackname MusE: Špatný název stopy Please choose a unique track name Configuration Prefer knobs, not sliders Show values in controls Prefer midi volume as decibels Monitor on record-arm automatically Visible audio effects Actions Činnosti Change track name Hide strip MusEGui::SyncToolbar External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::TList Track list: Use context menu to create tracks. Click track ID to select, CTRL to add, SHIFT for range, dblclick to select all of same type. Press F1 for help. SYNTH IS UNAVAILABLE! <none> <Žádný> visible Viditelný no clef Žádný klíč Treble Houslový klíč Bass Basový klíč Grand Oba klíče off Vypnuto <unknown> <neznámý> MusE: bad trackname MusE: Špatný název stopy Please choose a unique track name Show gui Show native gui Clear automation please choose a unique track name Vyberte, prosím, jedinečný název pro stopu Unused Devices Nepoužívaná zařízení Update drummap? Obnovit rozložení bicích? Do you want to use same port for all instruments in the drummap? Chcete pro všechny nástroje v rozložení bicích použít stejnou přípojku? &Yes &Ano &No &Ne show gui Ukázat uživatelské rozhraní show native gui Ukázat původní rozhraní Presets Přednastavení Clear all controller events? Smazat všechny události ovladače? &Ok &OK &Cancel Z&rušit Change color Změnit barvu Midi control Ovládání MIDI Assign Přiřadit Clear Smazat Other Jiné clear automation Smazat automatizaci Treble clef Houslový klíč Bass clef Basový klíč Grand Staff Oba klíče Viewable automation Viditelná automatizace Internal Vnitřní Synth Syntetizátor Delete Track Smazat stopu Delete Selected Tracks Smazat vybrané stopy Track Comment Poznámka ke stopě Save track's drumlist Uložit seznam bicích stopy Save track's drumlist differences to initial state Uložit rozdíly seznamu bicích stopy do počátečního stavu Load track's drumlist Nahrát seznam bicích stopy Reset track's drumlist Nastavit seznam bicích stopy znovu Reset track's drumlist-ordering Nastavit znovu pořadí seznamu bicích stopy Copy track's drumlist to all selected tracks Kopírovat seznam bicích stopy do všech vybraných stop Copy track's drumlist's differences to all selected tracks Kopírovat rozdíly seznamu bicích stopy do všech vybraných stop Insert Track Vložit stopu Drum map Rozložení bicích Reset the track's drum map with instrument defaults? Nastavit rozložení bicích stopy znovu s výchozími nástroji? Reset the track's drum map ordering? Nastavit znovu pořadí rozložení bicích stopy? Muse: Load Track's Drum Map MusE: Nahrát rozložení bicích stopy Drummap Rozložení bicích This drummap was created with a previous version of MusE, it is being read but the format has changed slightly so some adjustments may be necessary. Tato mapa bicích byla vytvořena v předchozí verzi MusE. Dá se přečíst, ale formát se trochu změnil, takže budou potřeba nějaké úpravy. MusE: Store Track's Drum Map MusE: Uložit rozložení bicích stopy Midi MIDI Drum Bicí New style drum Bicí v novém stylu Do you want to use same port and channel for all instruments in the drummap? Chcete pro všechny nástroje v rozložení bicích použít stejnou přípojku a týž kanál? MusEGui::TempoSig Use mastertrack tempo Tempo/Sig Tempo/Druh taktu Mastertrack tempo at current position, or fixed tempo Time signature at current position MusEGui::TempoToolbar Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Tap Tap this button to set tempo Set the tempo to the time difference of two successive clicks. Times out after two seconds. Tempo: Tempo: MusEGui::TempoToolbarWidget tempo at current position Čas na nynější značce polohy Tempo: Tempo: MusEGui::Toolbar1 Off Vypnuto Solo Sólo Cursor Poloha ukazovátka Snap Magnet MusEGui::TopWin As subwindow Jako podokno Shares tools and menu Sdílí nástroje a nabídku Tabbed/Floating Display editor in a tab or in a separate window (preset in Global Settings->Editors). Fullscreen Celá obrazovka Undo/Redo tools Nástroje Zpět/Znovu Panic Nouzové zastavení Metronome Metronom Timeline Transport Přehrávání Recording Sync Seřízení Song Position Poloha písně Tempo Tempo Signature Taktové označení Position Poloha Piano roll Váleček List editor Editor seznamu Drum editor Editor bicích Master track editor Editor hlavní stopy Master track list editor Editor seznamu hlavní stopy Wave editor Editor Wave Clip list Seznam ukázek Marker view Pohled se značkami Score editor Editor notového zápisu Arranger Aranžér <unknown toplevel type> <Neznámý typ nejvyšší úrovně> MusEGui::TrackComment MusE: Track Comment MusE: Poznámka ke stopě Track Comment: Poznámka ke stopě: MusEGui::Transport Overdub Přepsat Replace Nahradit Rec Mode Režim nahrávání Normal Normální Mix Smíchat Cycle Rec Nahrávat ve smyčce Punch in Punch out Rewind to Start Rewind O jeden takt zpět Forward O jeden takt vpřed Stop Zastavit Play Přehrát Record Nahrávat Metronome on/off External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. punchin Začít nahrávání na levé značce loop Smyčka punchout Ukončit nahrávání na pravé značce Punch In Začít nahrávání na levé značce Loop Smyčka Punch Out Ukončit nahrávání na pravé značce Left Mark Levá značka Right Mark Pravá značka rewind to start Přetočit zpět na začátek Click this button to rewind to start position Klepněte na toto tlačítko pro návrat na začátek rewind O jeden takt zpět Click this button to rewind Klepněte na toto tlačítko pro skok o jeden takt zpět forward O jeden takt vpřed Click this button to forward current play position Klepněte na toto tlačítko pro skok o jeden takt vpřed stop Zastavit Click this button to stop playback Klepněte na toto tlačítko pro zastavení přehrávání play Přehrát Click this button to start playback Klepněte na toto tlačítko pro spuštění přehrávání record Nahrávat Click this button to enable recording Klepnout na toto tlačítko pro povolení režimu nahrávání AC AC quantize during record Kvantizovat během nahrávání Click Klepnout metronom click on/off Ťukání metronomu zapnuto/vypnuto Sync Seřízení external sync on/off Vnější seřízení zapnuto/vypnuto Jack JACK Jack transport sync on/off Seřízení přehrávání JACK zapnuto/vypnuto Master Hlavní use master track Použít hlavní stopu MusEGui::VisibleTracks Show wave tracks Ukázat stopy Wave Show group tracks Ukázat stopy skupin Show aux tracks Ukázat stopy Aux Show input tracks Ukázat vstupní stopy Show output tracks Ukázat výstupní stopy Show midi tracks Ukázat stopy MIDI Show synth tracks Ukázat stopy syntetizátorů Visible track types Viditelné typy stop MusEGui::WaveCanvas Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help. Adjust Wave Offset Upravit posun WAVE Wave offset (frames) Posun WAVE (snímky) Part creation failed Vytvoření části se nezdařilo Left and right position markers must be placed inside the current part. Značky pro levou a pravou polohu musí být umístěny uvnitř nynější části. Part created Část vytvořena The selected region has been copied to the clipboard and can be pasted in the arranger. Vybraná oblast byla zkopírována do schránky a je možné ji vložit do aranžéru. MusE - external editor failed MusE: Vnější editor selhal MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE se nepodařilo spustit vnější editor. Prověřte, zda je nastavení editoru v Celková nastavení->Zvuk: Vnější editor Wave nastaven na platný editor. MusE - file size changed MusE: Velikost souboru změněna When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Když provádíte úpravy ve vnějším editoru, neměli byste měnit velikost souboru, protože tato se musí vejít do vybrané oblasti. Chybějící data jsou ztlumena Wave event: Converter settings MusEGui::WaveEdit &Edit Úp&ravy Func&tions &Funkce &Gain &Zesílení Other Jiné &Copy &Kopírovat &Create Part from Region &Vytvořit část z oblasti C&ut Vyj&mout &Paste &Vložit Edit in E&xternal Editor Upravit ve &vnějším editoru Edit in E&xternal Editor... Mute Selection Ztlumit výběr Normalize Selection Normalizovat výběr Fade In Selection Postupné zesílení signálu ve výběru Fade Out Selection Postupné zeslabení signálu ve výběru Reverse Selection Obrátit výběr Select Vybrat Select &All Vybrat &vše &Deselect All &Zrušit výběr všeho &Previous Part &Předchozí část &Next Part &Další část &Display Display menu: View-specific display options. Window &Config O&kno &Event Color Barva &události &Part colors Barvy čá&stí &Gray Š&edá WaveEdit tools Nástroje pro úpravy Wave Solo Sólo Cursor Poloha ukazovátka MusEGui::WaveView MusE - external editor failed MusE: Vnější editor selhal MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE se nepodařilo spustit vnější editor. Prověřte, zda je nastavení editoru v Celková nastavení->Zvuk: Vnější editor Wave nastaven na platný editor. MusE - file size changed MusE: Velikost souboru změněna When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Když provádíte úpravy ve vnějším editoru, neměli byste měnit velikost souboru, protože tato se musí vejít do vybrané oblasti. Chybějící data jsou ztlumena OrganGuiBase MusE: Organ MusE: Varhany Drawbars Táhla 16' 16' 4' 4' 2 2/3' 2 2/3' 2' 2' 5 1/3' 5 1/3' 8' 8' Envelope Hi Obalová křivka vysoká Release Uvolnění Sustain Držení tónu Decay Pokles Attack Náběh ms ms cB cB Envelope Lo Obalová křivka nízká O-1 Osc-1 Oscillator Oscilátor Brass Žesťové nástroje Reed Plátkové nástroje Flute Flétna Velocity Síla tónu PasteDialogBase MusE: Paste Parts MusE: Vložit části Number and raster Počet a rastr insert Vložit times krát raster rastr Insert Vložit Raster Rastr ticks tiky Move, Merge, Clone Přesunout, sloučit, klonovat Move everything to the right Přesunout vše napravo Move only affected parts to the right Přesunout ovlivněné části napravo Put everything into a single track Dát vše do jedné stopy Merge with existing parts Sloučit se stávajícími částmi Insert as clones (where possible) Vložit jako klony (tam, kde je to možné) OK OK Cancel Zrušit PasteEventsDialogBase MusE: Paste Events MusE: Vložit události Number and raster Počet a rastr insert Vložit times krát raster rastr ticks tiky Paste options Volby pro vložení &Always into existing parts &Never into existing parts &Into existing parts if part has not to be expanded by more than Always into existing parts Vždy do stávajících částí Never into existing parts Nikdy do stávajících částí Into existing parts if part has not to be expanded by more than Do stávajících částí, pokud část nemá být rozšířena o víc jak Insert Vložit Raster Rastr Put everything into the (selected) part Dát vše do (vybrané) části Paste midi controller target options Keep e&xisting Erase existin&g Erase WYSIWYG Erase inclusive OK OK Cancel Zrušit PluginDialogBase Dialog Dialog Ports: Přípojky: Mono Mono Mono + Stereo Mono + Stereo Stereo Stereo All Vše Plugin type: Druh přídavného modulu: Filter: Filtr: &OK &OK &Cancel Z&rušit PluginSettings Dialog Dialog Options Fixed speed Transport affects audio latency Override reported audio latency Reported audio latency override value Revert HiDPI scaling of native GUI Set in Global settings->GUI tweaks Global setting Additional information Follow global setting On Zapnuto Off Vypnuto ProjectCreate Create Project Vytvořit projekt Projects folder: Složka s projektem: ... ... Project Name: Název projektu: Project is a Template Projekt je předloha Write window state Zapsat stav okna Project song file type: Typ souboru s písní projektu: Project Path to song file: Cesta projektu k souboru s písní: &Create project folder (recommended for audio projects) &Vytvořit složku s projektem (doporučeno pro projekty se zvukem) Create project folder (recommended for audio projects) Vytvořit složku s projektem (doporučeno pro projekty se zvukem) Song information: Informace o písni: QMenu Select gui type Vybrat typ rozhraní QObject Error Chyba Please first select the range for crescendo with the loop markers. Nejprve, prosím, zvolte rozsah crescenda se značkami pro smyčku. Controller ! Ovladač! Other ! Jiné! Select gui type Vybrat typ rozhraní Preset actions Činnosti přednastavení Save preset... Uložit přednastavení... Update list Obnovit seznam Saved presets Uložená přednastavení No presets found Nenalezeno žádné přednastavení Enter new preset name Zadat nový název přednastavení Midi programs Programy MIDI Presets Přednastavení Switch on General Midi Level 1 mode Zapnout režim Obecné MIDI úroveň 1 Switch on General Midi Level 2 mode Zapnout režim Obecné MIDI úroveň 2 Switch off General Midi Level 1 or 2 Vypnout režim Obecné MIDI úroveň 1 nebo 2 Switch on Roland GS mode Zapnout režim Roland GS Switch on Yamaha XG mode Zapnout režim Yamaha XG Tracks: Stopy: Midi ports: Přípojky MIDI: Midi devices: Zařízení MIDI: Jack: JACK. Jack midi: MIDI JACK: Instruments Current device Others Jiné Files Synthesizers Open Otevřít Closed <Properties> <Base Property> <Property> Input routing Warning: No input routes! Click to connect... Output routing Warning: No output routes! Click to connect... TRACK TAKE MusE import error. MusE failed to import the file. Possibly this wasn't a sound file? If it was check the permissions, MusE sometimes requires write access to the file. FX slot QWidget Cannot convert sysex string Nelze převést řetězec sysex Hex String too long (2048 bytes limit) Šestnáctkový řetězec je příliš dlouhý (hranice je 2048 bytů) generic midi Obecné MIDI new Nový None Žádný create peakfile for Vytvořit soubor s vrcholem hladiny pro No wave events selected. Nevybrány žádné události vlny. No selection. Ignoring Žádný výběr. Přehlíží se MusE: get file name MusE: Předat název souboru The directory %1 does not exist. Create it? Adresář %1 neexistuje. Vytvořit jej? MusE: create directory MusE: Vytvořit adresář creating dir failed Vytvoření adresáře se nezdařilo Creating dir failed File %1 exists. Overwrite? Soubor %1 existuje. Přepsat? MusE: write MusE: Zapsat Open File %1 failed: %2 Soubor %1 se nepodařilo otevřít: %2 MusE: Open File MusE: Otevřít soubor Instrument-defined Vymezeno nástrojově Generic midi Edit instrument ... Upravit nástroj... Add Přidat Others Jiné Common Controls Obecné ovládání Velocity Síla tónu Create peakfile for New Nový QuantBase MusE: Quantize MusE: Kvantizovat Range Rozsah All Events Všechny události Selected Events Vybrané události Looped Events Události uvnitř smyčky Selected Looped Vybrané události uvnitř smyčky Parts Části Selected parts or c&urrent part All parts &in editor Values Hodnoty Strength: Síla: % % Threshold (ticks): Práh (tiků): Quantize Len Délka kvantizace Raster Rastr Whole Celá Half Půlová 4th Čtvrťová 4th Triplet Triola čtvrťová 8th Osminová 8th Triplet Triola osminová 16th Šestnáctinová 16th Triplet Triola šestnáctinová 32th Dvaatřicetinová 32th Triplet Triola dvaatřicetinová Swing: Swing: If the proposed change in tick or length is smaller than threshold, nothing is done. If swing=0, this is normal If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. Pokud je změna polohy nebo délky menší než práh, neudělá se nic. Pokud je swing=0, bude se kvantizovat normálně Pokud je swing 33, dostanete rytmus 2:1. Pokud je -33, dostanete rytmus 1:2. OK OK Cancel Zrušit RemoveBase MusE: Erase Notes MusE: Vymazat noty Range Rozsah All Events Všechny události Selected Events Vybrané události Looped Events Události uvnitř smyčky Selected Looped Vybrané události uvnitř smyčky All Eve&nts Selec&ted Events &Looped Events Selected L&ooped Parts Části Selected parts or c&urrent part All parts &in editor Thresholds Prahy ticks tiky Velocity Síla tónu Length Délka <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pokud není zaškrtnuto nic, je odstraněno vše.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pokud je zaškrtnuta síla tónu (dynamika), jsou odstraněny pouze noty se silou tónu (dynamiky) &lt; práh.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Pokud je zaškrtnuto obojí, jsou odstraněny noty se silou tónu (dynamiky) &lt; práh NEBO noty s délkou &lt; práh.</p></body></html> OK OK Cancel Zrušit RouteDialogBase MusE: Routing MusE: Tok signálu List of available sources. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Connections View window. Shows all current connections. Thick lines are Omni Routes. Thin lines are Channel Routes. Okno s pohledem na spojení. Ukáže všechna nynější spojení. Tlusté čáry jsou cesty Omni. Tenké čáry jsou cesty kanálů. List of available destinations. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Itemized list of current connections. Položkový seznam nynějších spojení. Show only selected sources Ukázat pouze vybrané zdroje Show only destination routes for selected source Ukázat pouze cílové cesty pro vybraný zdroj Show only selected destinations Ukázat pouze vybrané cíle Show only source routes for selected destination Ukázat pouze zdrojové cesty pro vybraný cíl Show all Midi Ports Ukázat všechny přípojky MIDI Auto adjust column size Automaticky přizpůsobit velikost sloupce Automatically adjusts the source and destination tree widths when the splitters are adjusted. This also turns on text word wrap, which may cause slower response with larger lists. Preferred route name or alias Upřednostňovaný název cesty nebo přezdívka Connect source to destination Spojit zdroj s cílem Remove selected route Odstranit vybraný tok signálu Add Route Přidat tok signálu Source: Zdroj: Destination: Cíl: connect source to destination Spojit zdroj s cílem Connect Spojit Current Routes Nynější toky signálu Source Zdroj Destination Cíl remove selected route Odstranit vybraný tok signálu Remove Odstranit RubberbandSettingsBase Rubberband Settings Rubberband support is not enabled Use default settings Presets: Přednastavení: Default Percussion Maximum Stretch: Elastic Precise Window: Standard Obvyklý Short Long Smoothing: Off Vypnuto On Zapnuto Channels: Apart Together Transients: Crisp Mixed Smooth Detector: Compound Percussive Soft Phase: Laminar Independent Formant: Shifted Preserved Pitch: Výška tónu: High speed High quality High consistency OK OK Cancel Zrušit SRCResamplerSettingsBase SRC Resampler Settings Use default settings Converter Type: SINC Best quality SINC Medium quality SINC Fastest Zero Order Hold Linear OK OK Cancel Zrušit SS_PluginChooserBase SimpleDrums - Ladspa Plugin Chooser Jednoduché bubny - volič přídavného modulu LADSPA Name Název Label Štítek Inports Vstupní přípojky Outports Výstupní přípojky Creator Tvůrce &Cancel Z&rušit Alt+C Alt+C &OK &OK Alt+O Alt+O SS_PluginFront Clear and unload effect Smazat a vyložit efekt Load effect Nahrát efekt Toggle display of effect parameters Přepnout zobrazení parametrů efektů Turn effect on/off Zapnout/Vypnout efekt SetlenBase MusE: Set Note Length MusE: Nastavit délku noty Range Rozsah All Events Všechny události Selected Events Vybrané události Looped Events Události uvnitř smyčky Selected Looped Vybrané události uvnitř smyčky Parts Části Selected parts or c&urrent part All parts &in editor Value Hodnota New length Nová délka ticks tiky OK OK Cancel Zrušit ShortcutCaptureDialogBase Enter shortcut sequence Zadat klávesovou zkratku Press keys to enter shortcut sequence! Tisknout klávesy pro zadání posloupnosti klávesové zkratky! Old shortcut: Stará klávesová zkratka: Undefined Nestanoveno New shortcut: Nová klávesová zkratka: OK OK Cancel Zrušit ShortcutConfigBase Configure Keyboard Shortcuts Nastavit klávesové zkratky Shortcut Category Skupina klávesové zkratky Shortcut Klávesová zkratka Description Popis Filter: Filtr: &Reset all &Clear &Smazat Alt+C Alt+C &Define &Vymezit Alt+D Alt+D &Apply &Použít Alt+A Alt+A &Printable file... &Tisknutelný soubor... Alt+P Alt+P &Ok &OK Alt+O Alt+O SimpleDrumsGuiBase DrumSynth 0.1 Syntetizátor bicích 0.1 Audio file assignments. Within parantesis: note number and traditional midi drum name SimplePluginChooserBase SimpleDrums - Ladspa Plugin Chooser Jednoduché bubny - volič přídavného modulu LADSPA Name Název Label Štítek Inports Vstupní přípojky Outports Výstupní přípojky Creator Tvůrce &Cancel Z&rušit Alt+C Alt+C &OK &OK Alt+O Alt+O SimpleSynthGui Mix Směs Chn Kan Channel routing Směrování kanálu &Load setup &Nahrát nastavení &Save setup &Uložit nastavení Load sample dialog Dialog pro nahrání vzorku SnooperDialogBase Snooper Mode Auto-hide Separate parented top levels Update tree Mouse clicks Key presses Flash timer s Reset Filters MusEGui QWidget Properties Vlastnosti Search: Class name: Object name: About This tool helps developers and designers discover objects. Click on anything anywhere or press a key to highlight an item. It will NOT auto-update when new objects are created. Click 'Update tree'. When open, may slow program slightly. This dialog is NOT shown. Class name::Object name Property Type Typ Value Hodnota Event type SongInfo Song Information Informace o písni Show on song load Ukázat při nahrávání písně &Cancel Z&rušit Alt+C Alt+C &Ok &OK Alt+O Alt+O SynthConfigBase Midi Port and Soft Synth Configuration Nastavení přípojky MIDI a softwarového syntetizátoru Instances Instance Name Název Type Typ Midi Port Přípojka MIDI Remove Instance Odstranit instanci Midi connections Spojení MIDI Soft Synthesizer Softwarový syntetizátor Add Instance Přidat instanci Configure midi devices, midi ports, and synthesizers Nastavit zařízení MIDI, přípojky MIDI a softwarové syntetizátory Available soft synthesizers Dostupné softwarové syntetizátory list of available software synthesizers Seznam dostupných softwarových syntetizátorů List of available software synthesizers File Soubor Inst Inst Version Verze Description Popis Useable devices Použitelná zařízení Add: Přidat: Synth Syntetizátor ALSA ALSA JACK JACK Rename Přejmenovat Remove Odstranit Device port assignments Přiřazení přípojek zařízením &Apply &Použít &OK &OK TransposeBase MusE: Transpose MusE: Převést Range Rozsah All Events Všechny události Selected Events Vybrané události Looped Events Události uvnitř smyčky Selected Looped Vybrané události uvnitř smyčky Parts Části Selected parts or c&urrent part All parts &in editor Value Hodnota Halftone-steps Kroky po půltónech OK OK Cancel Zrušit UnusedWaveFiles Dialog Dialog List of unused audio files in current project directory: Seznam nepoužívaných zvukových souborů v adresáři s nynějším projektem: Current project Nynější projekt All .med files in current directory Všechny soubory .med v adresáři s nynějším projektem Move files to 'unused' subdir Přesunout soubory do adresáře 'nepoužívané' Cancel Zrušit VAMGui MusE: Load VAM Presets MusE: Nahrát přednastavení VAM MusE: Save VAM Presets MusE: Uložit přednastavení VAM VAMGuiBase Virtual Analogue for MusE Virtuální analog pro MusE LFO NKO Freq Kmit Waveform Tvar vlny Sine Sinus Pulse Pravoúhelník Saw Pila Triangle Trojúhelník Filter Filtr EnvMod RežObál Attack Náběh Decay Pokles Sustain Držení tónu Release Uvolnění Cutoff Filtr kmitočtu Resonance Zvučnost Invert Obrátit KeyTrack KlíčStopa Presets Přednastavení Set Nastavit load preset list Nahrát seznam přednastavení save preset list Uložit seznam přednastavení save preset list to a new file Uložit seznam přednastavení do nového souboru delete preset Smazat přednastavení DCO 1 DCO 1 Pitch Výška tónu Detune Rozladění PWM PWM FM FM PW PW DCO 2 DCO 2 On Zapnuto VAM 1.0beta3 Virtual Analog for MusE Released under GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VAM 1.0beta3 Virtual Analog pro MusE Vydáno pod GPL. Autorské právo (C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Autorské právo (C) 2005 Robert Jonsson (rj@spamatica.se) VelocityBase MusE: Modify Velocity MusE: Změnit sílu tónu Range Rozsah All Events Všechny události Selected Events Vybrané události Looped Events Události uvnitř smyčky Selected Looped Vybrané události uvnitř smyčky Parts Části Selected parts or c&urrent part All parts &in editor Values Hodnoty Rate: Rychlost: % % Offset: Posun: veloNew = (veloOld * rate) + offset dynNová = (dynStará * rychlost) + posun OK OK Cancel Zrušit ZitaResamplerSettingsBase Zita Resampler Settings Zita Resampler support is not enabled Use default settings Options: OK OK Cancel Zrušit file_patterns Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) All Files (*) Všechny soubory (*) Midi (*.mid) Midi (*.mid) Karaoke (*.kar) Karaoke (*.kar) all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) Všechny známé soubory (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) med Files (*.med *.med.gz *.med.bz2) Soubory med (*.med *.med.gz *.med.bz2) Uncompressed med Files (*.med) Nestlačené soubory med (*.med) gzip compressed med Files (*.med.gz) Soubory med stlačené gzip (*.med.gz) bzip2 compressed med Files (*.med.bz2) Soubory med stlačené bzip2 (*.med.bz2) mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) Soubory mid (*.mid *.midi *.kar *.MID *.MIDI *.KAR) (*.jpg *.gif *.png) (*.jpg *.gif *.png) (*.jpg) (*.jpg) (*.gif) (*.gif) (*.png) (*.png) part Files (*.mpt *.mpt.gz *.mpt.bz2) Soubory part (*.mpt *.mpt.gz *.mpt.bz2) part Files (*.mpt) Soubory part (*.mpt) gzip compressed part Files (*.mpt.gz) Soubory part stlačené gzip (*.mpt.gz) bzip2 compressed part Files (*.mpt.bz2) Soubory part stlačené bzip2 (*.mpt.bz2) Presets (*.pre *.pre.gz *.pre.bz2) Přednastavení (*.pre *.pre.gz *.pre.bz2) Presets (*.pre) Přednastavení (*.pre) gzip compressed presets (*.pre.gz) Přednastavení stlačená gzip (*.pre.gz) bzip2 compressed presets (*.pre.bz2) Přednastavení stlačená bzip2 (*.pre.bz2) Presets (*.map *.map.gz *.map.bz2) Přednastavení (*.map *.map.gz *.map.bz2) Presets (*.map) Přednastavení (*.map) gzip compressed presets (*.map.gz) Přednastavení stlačená gzip (*.map.gz) bzip2 compressed presets (*.map.bz2) Přednastavení stlačená bzip2 (*.map.bz2) Wave/Binary (*.wav *.ogg *.flac *.bin) WAV/Binární (*.wav *.ogg *.flac *.bin) Wave (*.wav *.ogg *.flac) WAV (*.wav *.ogg *.flac) Color configuration files (*.cfc) Qt style sheets (*.qss) Styly Qt (*.qss) Wave/Binary (*.wav *.ogg *.bin) Wave/Binární (*.wav *.ogg *.bin) Wave (*.wav *.ogg) Wave (*.wav *.ogg) Binary (*.bin) Binární (*.bin) freeverb FreeVerb FreeVerb Room Size Velikost prostoru Damping Vyklopení Wet Level Síla účinku Tap-Reverberator Tap-Reverberator Decay [ms] Doba poklesu [ms] dB dB Dry Level [dB] Síla hlasitosti nezměněného signálu [dB] Wet Level [dB] Síla hlasitosti změněného signálu [dB] Preset: Přednastavení: AfterBurn AfterBurn AfterBurn (Long) AfterBurn (dlouhý) Ambience Atmosféra Ambience (Thick) Atmosféra (silná) Ambience (Thick) - HD Atmosféra (silná) - HD Cathedral Katedrála Cathedral - HD Katedrála - HD Drum Chamber Síň bicích Garage Garáž Garage (Bright) Garáž (jasná) Gymnasium Sportovní hala Gymnasium (Bright) Sportovní hala (jasná) Gymnasium (Bright) - HD Sportovní hala (jasná) - HD Hall (Small) Hala (malá) Hall (Medium) Hala (střední) Hall (Large) Hala (velká) Hall (Large) - HD Hala (velká) - HD Plate (Small) Deska (malá) Plate (Medium) Deska (střední) Plate (Large) Deska (velká) Plate (Large) - HD Deska (velká) - HD Pulse Chamber Síň rytmu Pulse Chamber (Reverse) Síň rytmu (obrácená) Resonator (96 ms) Rezonátor (96 ms) Resonator (152 ms) Rezonátor (152 ms) Resonator (208 ms) Rezonátor (208 ms) Room (Small) Pokoj (malý) Room (Medium) Pokoj (střední) Room (Large) Pokoj (velký) Room (Large) - HD Pokoj (velký) - HD Slap Chamber Síň plácnutí Slap Chamber - HD Síň plácnutí - HD Slap Chamber (Bright) Síň plácnutí (jasná) Slap Chamber (Bright) HD Síň plácnutí (jasná) HD Smooth Hall (Small) Klidná hala (malá) Smooth Hall (Medium) Klidná hala (střední) Smooth Hall (Large) Klidná hala (velká) Smooth Hall (Large) - HD Klidná hala (velká) - HD Vocal Plate Hlasitá deska Vocal Plate - HD Hlasitá deska - HD Warble Chamber Síň cvrlikání Warehoouse Skladiště Warehouse - HD Skladiště - HD Comb Filters Sdružené filtry Allpass Filters Všeprůchozí filtry Bandpass Filters Pásmové filtry Enhanced Stereo Rozšířené stereo main File to open Alsa midi only (using dummy audio driver) Use RtAudio driver Use JAckAudio driver to connect to Jack audio server Do not try to auto-start the Jack audio server Do not auto-populate midi ports with midi devices found, at startup Force inclusion of ALSA midi even if using Jack Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) Force midi real time priority to n (default: audio driver prio -1) Force plugin cache re-creation. (Automatic if any plugin path directories changed.) Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care. Don't load LADSPA plugins Don't load MESS plugins Don't load VST plugins Don't load LinuxVST plugins Don't load DSSI plugins Don't load LV2 plugins Don't use LASH Force locale to the given language/country code (xx = Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins. Debug mode: no threads, no RT Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively! Debug mode: trace midi Input Debug mode: trace midi Output Debug mode: trace sync Enable Python control support Pyro nameserver host name Pyro nameserver host port Pyro daemon host name Pyro daemon host port Pyro communication timeout in seconds midiWarnInitPendingBase Instrument initialization Inicializace nástroje MusE should now send some Instrument Initialization Sequences. The sequences (usually System Exclusive messages) are defined by the selected instruments in the Settings -> Midi Ports dialog, such as the GM (default), GS, or XG instruments. Typically you should answer yes here. You can always do it manually from the Midi menu. Continue? Don't ask me again Neptat se znovu shortcuts Transport: Start playback from current location Přehrávání: Začít přehrávání od nynější polohy Transport: Toggle metronome Přehrávání: Zapnout/Vypnout metronom Transport: Stop Playback Přehrávání: Zastavit přehrávání Transport: Goto Start Přehrávání: Jít na začátek Transport: Goto End Transport: Play, Stop, Rewind Přehrávání: Přehrát, Zastavit, Přetočit Transport: Restart recording Přehrávání: Začít nahrávání znovu Transport: Restart recording (multi take) Transport: Goto left marker Přehrávání: Jít na levou značku Transport: Goto right marker Přehrávání: Jít na pravou značku Transport: Toggle Loop section Přehrávání: Přepnout smyčku Transport: Toggle Record Přehrávání: Zapnout/Vypnout nahrávání Transport: Arm current track Transport: Clear all rec enabled tracks Přehrávání: Vrátit zpět všechny stopy označené pro nahrávání Toggle fullscreen Přepnout zobrazení na celou obrazovku Toggle tabbed/floating editor window Toggle docks display Toggle rewind on stop Show Arranger window Edit: Copy Úpravy: Kopírovat Edit: Copy in range Úpravy: Kopírovat v rozsahu Edit: Undo Úpravy: Zpět Edit: Redo Úpravy: Znovu Edit: Cut Úpravy: Vyjmout Edit: Paste Úpravy: Vložit Edit: Paste to current part Edit: Paste to selected track Úpravy: Vložit do vybrané stopy Edit: Paste clone Úpravy: Vložit klona Edit: Paste clone to selected track Úpravy: Vložit klona do vybrané stopy Edit: Paste (with dialog) Úpravy: Vložit (s dialogem) Edit: Delete Úpravy: Smazat File: New project Soubor: Nový projekt File: New project from template File: Open from disk Soubor: Otevřít File: Save project Soubor: Uložit projekt File: Open recent file Soubor: Otevřít poslední soubor File: Save as Soubor: Uložit jako File: Load template Soubor: Nahrát předlohu File: Import midi file Soubor: Zavést soubor MIDI File: Export midi file Soubor: Vyvést soubor MIDI File: Import midi part Soubor: Zavést MIDI part File: Import audio file Soubor: Zavést zvukový soubor File: Quit MusE Soubor: Ukončit MusE Edit: Rename part Edit: Select parts on track Úpravy: Vybrat části na stopě Open pianoroll Otevřít váleček Open drumeditor Otevřít editor bicích Open listeditor Otevřít editor seznamu Open waveeditor Otevřít editor Wave Open graphical mastertrack editor Otevřít obrazový editor hlavní stopy Open list mastertrack editor Otevřít editor seznamu hlavní stopy Open midi transformer Otevřít měnič MIDI Open track Synth GUI Open pianoroll in new window Open drumeditor in new window Open listeditor in new window Open waveeditor in new window Add midi track Přidat stopu MIDI Add drum track Přidat stopu bicích Add new style drum track Přidat stopu bicích v novém stylu Add wave track Přidat stopu Wave Add audio output Přidat výstup zvuku Add audio group Přidat skupinu zvuku Add audio input Přidat vstup zvuku Add audio aux Přidat aux zvuku Insert midi track Insert drum track Insert wave track Insert audio output Insert audio group Insert audio input Insert audio aux Structure: Global cut Stavba: Celkové vyjmutí Structure: Global insert Stavba: Celkové vložení Structure: Global split Stavba: Celkové rozdělení Structure: Cut events Stavba: Vyjmout události View: Open mixer #1 window Pohled: Otevřít okno se směšovačem 1 View: Open mixer #2 window Pohled: Otevřít okno se směšovačem 2 View: Toggle transport window Pohled: Přepnout okno pro přehrávání View: Toggle bigtime window Pohled: Přepnout velký ukazatel času View: Open marker window Pohled: Otevřít okno se značkou View: Toggle mixer strip display Settings: Follow song by page Nastavení: Sledovat píseň na několika stranách Settings: Follow song off Nastavení: Nesledovat píseň Settings: Follow song continuous Nastavení: Sledovat píseň stále Settings: Global configuration Nastavení: Celková nastavení Settings: Configure shortcuts Nastavení: Nastavit klávesové zkratky Settings: Configure metronome Nastavení: Nastavit metronom Settings: Midi sync configuration Nastavení: Nastavení seřízení MIDI Settings: Midi file import/export configuration Nastavení: Nastavení zavedení/vyvedení souboru MIDI Settings: Appearance settings Nastavení: Nastavení vzhledu Settings: Midi ports / Soft Synth Nastavení: Přípojky MIDI/Softwarové syntetizátory Settings: Audio subsystem configuration Nastavení: Nastavení zvukového podsystému Midi: Edit midi instruments Midi: Upravit nástroje MIDI Midi: Open midi input transform Midi: Otevřít proměnu vstupu MIDI Midi: Open midi input filter Midi: Otevřít filtr vstupu MIDI Midi: Midi input transpose Midi: Převedení vstupu MIDI Midi: Midi remote control Midi: Vzdálené ovladání MIDI Midi: Random rhythm generator Midi: Generátor náhodného rytmu Midi: Reset midi Midi: Nastavit MIDI znovu Midi: Init midi Midi: Inicializovat MIDI Midi: Midi local off Midi: Místní vypnuto Audio: Bounce audio to track Zvuk: Odmíchat zvuk na stopu Audio: Bounce audio to file Zvuk: Odmíchat zvuk do souboru Audio: Restart audio Zvuk: Spustit zvuk znovu Automation: Mixer automation Automatizace: Automatizace míchacího pultu Automation: Take mixer snapshot Automatizace: Udělat snímek směšovače Automation: Clear mixer automation Automatizace: Smazat automatizaci směšovače Help: Open Manual Nápověda: Otevřít příručku Help: Toggle whatsthis mode Nápověda: Přepnout režim "Co je to?" Edit: Edit selected part Úpravy: Upravit vybranou část Edit: Select nearest part on track above Úpravy: Vybrat nejbližší část na předchozí stopě Edit: Add nearest part on track above Úpravy: Přidat nejbližší část do předchozí stopy Edit: Select nearest part on track below Úpravy: Vybrat nejbližší část na další stopě Edit: Add nearest part on track below Úpravy: Přidat nejbližší část do další stopy Edit: Insert empty measure Úpravy: Vložit prázdný takt Edit: Paste as clones Úpravy: Vložit jako klony Edit: Paste as clones (with dialog) Úpravy: Vložit jako klony (s dialogem) Edit: Duplicate track Úpravy: Zdvojit stopu Select track above Vybrat předchozí stopu Select track below Vybrat další stopu Edit selected track name Upravit název vybrané stopy Mute all currently selected tracks Solo all currently selected tracks Midi: Transpose Midi: Převést Edit: Select all Úpravy: Vybrat vše Edit: Select none Úpravy: Nevybrat nic Edit: Invert Selection Úpravy: Obrátit výběr Edit: Select events/parts inside locators Úpravy: Vybrat všechny události/části v oblasti Edit: Select events/parts outside locators Úpravy: Vybrat všechny události/části vně oblasti Edit: Select previous part Úpravy: Vybrat předchozí část Edit: Select next part Úpravy: Vybrat další část Edit: Select nearest part/event to the left or move cursor Úpravy: Vybrat nejbližší část/nejbližší událost nalevo anebo přesunout ukazovátko Edit: Add nearest part/event to the left to selection Úpravy: Přidat nejbližší část/nejbližší událost nalevo do výběru Edit: Select nearest part/event to the right or move cursor Úpravy: Vybrat nejbližší část/nejbližší událost napravo anebo přesunout ukazovátko Edit: Add nearest part/event to the right to selection Úpravy: Přidat nejbližší část/nejbližší událost napravo do výběru Edit: Set locators to selection Úpravy: Nastavit oblast z výběru Edit: Increase pitch Úpravy: Zvýšit výšku tónu Edit: Decrease pitch Úpravy: Snížit výšku tónu Edit: Increase event position Úpravy: Zvýšit polohu události Edit: Decrease event position Úpravy: Snížit polohu události View: Zoom in Pohled: Přiblížit View: Zoom out Pohled: Oddálit View: Goto Current Position Pohled: Jít na nynější polohu View: Scroll left Pohled: Projíždět doleva Transport: Step record Přehrávání: Krokové nahrávání Transport: Midi input Přehrávání: Vstup MIDI Transport: Play events Přehrávání: Přehrát události Edit: Increase velocity Úpravy: Zvýšit sílu tónu Edit: Decrease velocity Úpravy: Snížit sílu tónu Edit: Set Fixed Length on Midi Events Úpravy: Nastavit pevnou délku pro události MIDI Quantize Kvantizovat Modify Note Length Změnit délku noty Modify Velocity Změnit sílu tónu Edit: Crescendo Úpravy: Crescendo Edit: Thin Out Úpravy: Prostřihávat Edit: Erase Event Úpravy: Smazat událost Edit: Delete Overlaps Úpravy: Smazat překrývající se noty Edit: Note Shift Úpravy: Posunout noty Edit: Move Clock Úpravy: Posunout hodiny Edit: Copy Measure Úpravy: Kopírovat takt Edit: Erase Measure Úpravy: Vymazat takt Edit: Delete Measure Úpravy: Smazat takt Edit: Create Measure Úpravy: Vytvořit takt Edit: Change Event Color Úpravy: Změnit barvu události Move: Move to selected note Posun: Posun do vybrané noty Tool: Pointer Nástroj: Ukazatel Tool: Pencil Nástroj: Tužka Tool: Eraser Nástroj: Guma Tool: Pan Nástroj: Vyvážení Tool: Zoom Nástroj: Zvětšení Tool: Line Draw Nástroj: Čára Tool: Cursor Nástroj: Ukazovátko Add note velocity 1 Přidat notu se silou tónu (dynamika) 1 Add note velocity 2 Přidat notu se silou tónu (dynamika) 2 Add note velocity 3 Přidat notu se silou tónu (dynamika) 3 Add note velocity 4 Přidat notu se silou tónu (dynamika) 4 Cursor step size: larger Velikost kroku ukazovátka: větší Cursor step size: smaller Velikost kroku ukazovátka: menší Instrument/Cursor up Nástroj/Ukazovátko nahoru Instrument/Cursor down Nástroj/Ukazovátko dolů Tool: Range Nástroj: Rozsah Tool: Stretch Tool: Sample rate Tool: Scissor Nástroj: Nůžky Tool: Glue Nástroj: Lepidlo Tool: Mute Nástroj: Ztlumit Transport: Increase current position Přehrávání: Zvýšit nynější polohu Transport: Decrease current position Přehrávání: Snížit nynější polohu Transport: Increase current position, no snap Přehrávání: Zvýšit nynější polohu, žádné zapadnutí Transport: Decrease current position, no snap Přehrávání: Snížit nynější polohu, žádné zapadnutí Quantize: Set quantize to off Quantize: Set quantize to 1/1 note Kvantizovat: Nastavit kvantizaci na celou notu Quantize: Set quantize to 1/2 note Kvantizovat: Nastavit kvantizaci na půlovou notu Quantize: Set quantize to 1/4 note Kvantizovat: Nastavit kvantizaci na čtvrťovou notu Quantize: Set quantize to 1/8 note Kvantizovat: Nastavit kvantizaci na osminovou notu Quantize: Set quantize to 1/16 note Kvantizovat: Nastavit kvantizaci na šestnáctinovou notu Quantize: Set quantize to 1/32 note Kvantizovat: Nastavit kvantizaci na dvaatřicetinovou notu Quantize: Set quantize to 1/64 note Kvantizovat: Nastavit kvantizaci na čtyřiašedesátinovou notu Quantize: Toggle triol quantization Kvantizovat: Přepnout triolovou kvantizaci Quantize: Toggle punctuation quantization Kvantizovat: Přepnout tečkovanou kvantizaci Quantize: Toggle punctuation quantization (2) Kvantizovat: Přepnout tečkovanou kvantizaci (2) Edit: Insert at location Úpravy: Vložit v poloze Edit: Increase length Úpravy: Zvětšit délku Edit: Decrease length Úpravy: Zmenšit délku Insert Note Vložit notu Insert SysEx Vložit SysEx Insert Ctrl Vložit Ctrl Insert Meta Vložit Meta Select strip to the left Select strip to the right Mixer strip volume down Mixer strip volume up Mixer strip pan left Mixer strip pan right Mixer strip volume down page Mixer strip volume up page Mixer strip pan left page Mixer strip pan right page Insert Channel Aftertouch Vložit dodělávku kanálu Insert Key Aftertouch Vložit dodělávku tóniny Insert Tempo Vložit tempo Insert Signature Vložit taktové označení Change Event Position Změnit polohu události Edit Event Value Upravit hodnotu události Insert Key Vložit tóninu Settings: Midi ports / Soft synths Goto Next Marker Jít na další značku Goto Prev Marker Jít na předchozí značku Normalize Normalizovat warnBadTimingBase Bad timing Špatné načasování Message here Zpráva zde Don't warn me again Nevarovat znovu muse-4.0.0/src/share/locale/muse_de.ts000066400000000000000000026652121404105311000176100ustar00rootroot00000000000000 @default Add Midi Track Midispur hinzufügen Add Drum Track Schlagzeugspur hinzufügen Add Wave Track Wavespur hinzufügen Add Audio Output Audioausgang hinzufügen Add Audio Group Audiogruppe hinzufügen Add Audio Input Audioeingang hinzufügen Add Aux Send Aux Send hinzufügen Select project directory Projektverzeichnis auswählen Add Synth Synthesizer hinzufügen Route Signalfluss dest Ziel name="%1"/ Name="%1" Warning: No output devices! Warnung: Keine Ausgangsgeräte! Open midi config... Öffne MIDI-Konfig... Empty ports Leere Ports <none> <kein> channelMask="%1" KanalMaske="%1" Bad timing Schlechter Taktgeber Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. Also please check console output for any further error messages. Die Frequenz des Taktgebers ist %1Hz; das ist unter dem empfohlenen Minimum von 500Hz! Dies könnte zu hörbaren Timingproblemen für MIDI führen. Bitte sehen sie in den FAQ unter http://muse-sequencer.org nach Lösungen. Bitte überprüfen sie außerdem die Konsolenmeldungen auf weitere Fehlermeldungen. Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. Also please check console output for any further error messages Die Frequenz des Taktgebers ist %1Hz; das ist unter dem empfohlenen Minimum von 500Hz! Dies könnte zu hörbaren Timingproblemen für MIDI führen. Bitte sehen sie in den FAQ unter http://muse-sequencer.org nach Lösungen. Bitte überprüfen sie außerdem die Konsolenmeldungen auf weitere Fehlermeldungen. AboutBox AboutBox Projektinformation Version 2 pre-alpha Version 2 pre-alpha (C) Copyright 1999-2010 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2010 Werner Schweer und andere. Siehe http://www.muse-sequencer.org für neue Versionen und mehr Informationen. Veröffentlicht unter der GNU Public License Version 2 Version 2 (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer und andere. Siehe http://www.muse-sequencer.org für neue Versionen und mehr Informationen. Veröffentlicht unter der GNU Public License (C) Copyright 1999-2014 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer und andere. Siehe http://www.muse-sequencer.org für neue Versionen und mehr Informationen. Veröffentlicht unter der GNU Public License {1999-2014 ?} (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer und andere. Siehe http://www.muse-sequencer.org für neue Versionen und mehr Informationen. Veröffentlicht unter der GNU Public License {1999-2014 ?} {1999-2015 ?} MusE MusE (C) Copyright 1999-2020 the MusE development team. See https://muse-sequencer.github.io for new versions and more information. Published under the GNU Public License. &Keep On Rocking! Weiterroc&ken! Alt+K Alt+K Internals Intern System information (useful for debugging): Systeminformation (nützlich für Debugging): AppearanceDialogBase Apply Anwenden Ok Bestätigen Cancel Abbrechen Arranger Arrangierer MusE: Appearance settings MusE: Erscheinungsbild einstellen Parts Parts show names Namen anzeigen show events Ereignisse anzeigen show Cakewalk Style Cakewalk Stil y-stretch strecke y Events Ereignisse note on Note An poly pressure Poly Tastendruck controller Kontroller aftertouch Aftertouch pitch bend Pitch bend program change Programmwechsel special Spezial Wave Drawing in Parts Zeichnen von Waves in Parts RMS/PEAK (&traditional) RMS/PEAK (&traditionell) Background picture Hintergrundbild Help Hilfe show snap grid Magnetisches Gitter anzeigen Colors Farben Items Items Choose color... Wähle Farbe... Color name: Farbname: Qt system style Qt System Style Current settings Aktuelle Einstellungen Style sheet Stylesheet Icon size Ikonengröße Custom cursor size Custom-Cursor Größe Distribute current system font family to all other fonts Aktuelle Fontfamilie in alle anderen Fonts verteilen System System Font sizes for fonts 1-6 are determined at every program start, to optimally match the currently used system font. Disable if you want to set fixed sizes in the font settings below. Default scaling factors: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% Fontgrößen für Fonts 1-6 werden bei jedem Programmstart ermittelt, damit sie optimal zum aktuellen Systemfont passen. Kann ausgeschaltet werden, um feste Fontgrößen unten in den Einstellugen zu setzen. Voreingestellte Skalierungsfaktoren: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% Current theme Aktuelles Design If a user style sheet for one of the MusE color schemes exists and is selected, it will be merged with the default style sheet for this scheme. The user style sheet takes precedence and overwrites identical settings in the default style sheet. Cascade default and user theme style sheets Default und Benutzerstylesheet kaskadieren Show icons in menus Ikonen in Menüs anzeigen Use native standard dialogs Native Standarddialoge verwenden Calculate and apply relative font sizes dynamically at program start Relative Fontgrößen beim Programmstart berechnen und anwenden Maximum aliased size, 0 = no alias Maximale Größe für Aliasing, 0 = kein Aliasing Insert Einfügen Size (pt) Größe (pt) Fill in optimal font sizes in relation to the system font Mit optimalen Fontgrößen im Verhältnis zum Systemfont befüllen Maximum aliased size, 0 = no alias: Maximale Größe für Aliasing, 0 = kein Aliasing At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. Global opacity Globale Durchsichtigkeit Show names Namen anzeigen Show e&vents &Ereignisse anzeigen Show Ca&kewalk style &Cakewalk Stil O&nly outline Nur &Kontur RMS/PEAK (&Traditional) RMS/PEAK (&Traditionell) Add Hinzufügen Remove Entfernen Clear Löschen Show snap grid Magnetisches Gitter anzeigen Add to palette Zur Palette hinzufügen Style/Fonts Stil und Schriftart QT Theme Qt Thema Windows Fenster MusE MusE Metal Metall Norwegian Wood Norwegisches Holz Platinum Platinum CDE CDE Motif Motif Motif Plus Motif Plus May require restarting MusE for best results Neustart von MusE könnte erforderlich sein Use theme icons if available. Requires restarting MusE. Wenn verfügbar, Ikonen aus Theme verwenden. Erfordert Neustart. MusE color scheme MusE Farbenschema Change Ändern Fonts Schriftarten Family Familie Size Größe Font 1 Schriftart 1 Themes Themen Font 2 Schriftart 2 Font 3 Schriftart 3 Font 0 Schriftart 0 Bold Fett Italic Kursiv ... ... Font 4 Schriftart 4 Font 5 Schriftart 5 Palette Palette add hinzufügen remove entfernen add to palette Zur Palette hinzufügen B B S S H H V V G G R R clear löschen Save all... Alles Speichern... Load all... Alles Laden... Style Sheet: Formatvorlage: Font 6 Schriftart 6 ArrangerColumnsBase Configure arranger columns Arranger-Spalten konfigurieren Columns: Spalten: Name: Name: Controller type: Controller-Typ: Midi controller type Typ des Midi-Controllers Affect CCs at Beeinflusse CCs bei Control7 Controller7 Control14 Controller14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Program Programm H-Ctrl H-Ctrl Midi controller number high byte Midi Controllernummer höchstwertiges Byte L-Ctrl L-Ctrl Midi controller number low byte Midi Kontrollernummer niederwertigstes Byte * wild card * affect CCs at beeinflusse CCs bei begin of song Liedanfang current position aktuelle Position &Add &Hinzufügen &Delete &Löschen Done Fertig AudioConverterSettingsBase Audio Converter Settings Audiokonverter Einstellungen Available audio converters: Verfügbare Audiokonverter: Available audio converters Verfügbare Audiokonverter Offline settings... Offline-Einstellugen... Realtime settings... Realtime-Einstellungen... Gui settings... Gui-Einstellungen... Use default preferences (see Global Settings) Voreinstellungen verwenden (siehe Globale Einstellugen) Preferences Voreinstellungen Preferences: Einstellungen When only samplerate conversion is required, use: Wenn nur die Samplerate gebraucht wird: When time stretching or pitch shifting is required, use: Wenn Timestretching oder Pitchshifting gebraucht wird: OK Bestätigen Cancel Abbrechen Awl::MidiVolEntry off aus db dB Awl::VolEntry off aus ChooseSysexBase Dialog Dialog Hex: Hex: Comment: Kommentar: &OK &Bestätigen &Cancel &Abbrechen ClipListEditorBase MusE: ClipList MusE: Klipp Liste Name Name Refs Refs Samplerate Sample-Rate Len Länge Data Daten Status Status Clip Properties Klippeigenschaften Pos: Pos: Len: Länge: CommentBase Form1 Form1 Track Comment Spur Kommentar Track 1 Spur 1 ConfigMidiFileBase &OK &Bestätigen &Cancel &Abbrechen 0 (single track) 0 (einzelne Spur) 1 (multiple tracks) 1 (mehrere Spuren) Format: Format: Export Export 96 96 192 192 384 384 Division: Unterteilung: Copyright: Copyright: MusE: Config Midi File Import/Export MusE: Konfiguriere MIDI-Datei Import/Export Import: Import: Split tracks into &parts Spuren in &Parts teilen Alt+P Alt+P Split tracks into parts, or one single part Spuren in Parts teilen, oder einzelnen Part erstellen Import Import Default instrument: Instrument-Voreinstellung Device Name metas trump Port metas if both exist Gerätenamen-Metadaten haben Priorität über Port-Metadaten Instrument Name metas trump Mode sysexes if both exist Instrumentenamen-Metadaten haben Priorität über Mode-SysEx-Daten Export: Export: Enable extended smf format (currently not implemented) Aktiviere erweitertes SMF-Format (momentan nicht implementiert) Running Status saves space by not repeating event status bytes. If this is off every event will have a status byte, increasing file size. Use Running &Status &Laufstatus verwenden To turn a note off some devices can use note-offs or zero-velocity note-ons or both. When used with Running Status this setting saves space. It is safe to leave this off. Replace note-offs with &zero velocity note-ons Note-offs durch Note-ons mit Velocity &0 ersetzen Use &2-byte time signatures instead of standard 4 Benutze &2-byte-Zeitsignaturen statt standardmäßig 4-byte Alt+2 Alt+2 If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum note controllers. If disabled, drum maps are ignored during export. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. See "What's This ?" for more info. If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum controllers. Any Port overrides cause a separate track to be created (requires format 1). Any Channel overrides will still appear in the same track in the exported file, but upon re-import into an app such as MusE, MusE will treat that as a new track and create one. If disabled, drum maps are ignored. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. Upon re-importing into another app, it is up to the other app to redirect the particular drum ports, channels and notes to make the song sound like it was intended. Drum map overrides are unique to MusE. If re-importing into MusE, for faithful reproduction disable this setting and apply a drum map and some overrides later. If re-importing or opening in another midi player, enable this setting so that the player can have a chance at faithful reproduction. Some user adjustment may be required (choice of instruments, ports, etc). Apply drum map Port, Channel, ANote overrides Port, Kanal und ANote aus Schagzeugkanal übersteuern If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. See "What's This ?" for more info. If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. The extra track is automatically created. In this mode all events on a track in the midi file are on the same channel. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. In this mode the track events can be on different channels. But if the file is re-imported into an app such as MusE, MusE will automatically create the extra track. Drum map Channel overrides go to a separate track Schagzeugbelegungskanal-Übersteuerung in separatem Track Export instrument or mode: Instrument und Mode exportieren: Mode sysexes Mode-SysEx Daten Export port or device metas: Port- und Gerätemetadaten exportieren: Instrument name metas Instrumentnamen-Metadaten Port metas Port-Metadaten Device name metas Gerätenamen-Metadaten Export a Port/Device meta for format 0 Port/Geräte-Metadaten für Format 0 exportieren Note: Format 0 uses the FIRST midi track's name/comment in the arranger #Hinweis: Format 0 nutzt den Namen/Kommentar der ERSTEN Midispur im Arranger Save space by replacing note-offs with &zero velocity note-ons Platz sparen, indem Note-offs durch Note-ons mit Anschlagsdynamik &0 ersetzt werden Alt+Z Alt+Z CopyOnWriteDialogBase Copy Wave Files Wave-Dateien kopieren Some sound files will be copied to the Project Directory, either because they are not writable or because more than one independent Wave Event shares them. (If you prefer instead that the Wave Events are inter-dependent, try using Clone Parts.) Multiple copies will be made in some cases. If no Project has been created yet, you will be asked to, giving another chance to cancel. These files will be copied to the Project Directory: Diese Dateien werden ins Projektverzeichnis kopiert: CrescendoBase MusE: Crescendo/Decrescendo MusE: Crescendo/Decrescendo Range Bereich Looped Events Ereignisse innerhalb Schleife &Looped Events Ereignisse innerhalb &Schleife Selected Looped Ausgewählt und innerhalb der Schleife Parts Parts Selected parts or c&urrent part Aus&gewählte Parts oder aktueller Part All parts &in editor &Alle Parts im Editor Values Werte Start velocity Anfangsdynamik % % End velocity Enddynamik &Absolute A&bsolut Rela&tive &Relativ Absolute Absolut Relative Relativ OK Bestätigen Cancel Abbrechen DeicsOnzeGui Save configuration Konfiguration speichern Critical Error Kritischer Fehler Cannot open file %1 Kann Datei "%1" nicht öffnen Parsing error for file %1 Fehler beim Parsen von Datei "%1" Load category dialog Kategorie laden Load set dialog Set laden Save set dialog Set speichern New category Neue Kategorie Delete category Kategorie löschen Load category Kategorie laden Save category Kategorie speichern Load set Set laden Save set Set speichern Delete set Set löschen New subcategory Neue Unterkategorie Delete subcategory Unterkategorie löschen Load subcategory Unterkategorie laden Save subcategory Unterkategorie speichern New preset Neue Vorlage Delete preset Vorlade löschen Load preset Vorlage laden Save preset Vorlage speichern No more category supported Keine weiteren Kategorien unterstützt You can not add more categories Sie können keine weiteren Kategorien hinzufügen Do you really want to delete %1 ? Möchten Sie wirklich %1 löschen? &Yes &Ja &No &Nein No category selected Keine Kategorie ausgewählt You must first select a category. Sie müssen zuerst eine Kategorie auswählen. Replace or add Ersetzen oder Hinzufügen %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 soll mit der hbank-Nummer %2 beeinflusst werden, aber es gibt bereits eine auf diesem Slot. Wollen Sie sie ersetzen oder in den nächsten freien Slot hinzufügen? &Replace Erset&zen &Add &Hinzufügen Download error Download-Fehler There is no more free category slot. Es gibt keinen weiteren freien Kategorie-Slot. Save category dialog Kategorie speichern No more subcategory supported Keine weiteren Unterkategorien unterstützt You can not add more subcategories Sie können keine weiteren Unterkateogien hinzufügen No subcategory selected Keine Unterkategorie ausgewählt You must first select a subcategory. Sie müssen erst eine Unterkategorie auswählen. Load subcategory dialog Unterkategorie-Laden-Dialog %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 soll mit der lbank-Nummer %2 beeinflusst werden, aber es gibt bereits eine auf diesem Slot. Wollen Sie sie ersetzen oder in den nächsten freien Slot hinzufügen? There is no more free subcategory slot. Es gibt keinen weiteren freien Unterkategorie-Slot. Save subcategory dialog Unterkategorie-Speichern-Dialog No more preset supported Keine weiteren Voreinstellungen unterstützt You can not add more presets Sie können keine weiteren Voreinstellungen hinzufügen No preset selected Keine Voreinstellung ausgewählt You must first select a preset. Sie müssen erst eine Voreinstellung auswählen. Load preset dialog Voreinstellung-Laden-Dialog %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 soll mit der Programm-Nummer %2 beeinflusst werden, aber es gibt bereits eine auf diesem Slot. Wollen Sie sie ersetzen oder in den nächsten freien Slot hinzufügen? There is no more free preset slot. Es gibt keinen weiteren freien Voreinstellung-Slot. Save preset dialog Voreinstellung-Speichern-Dialog Browse set dialog Set-Suchen-Dialog Browse image dialog Bild-Suchen-Dialog DeicsOnzeGuiBase DeicsOnze DeicsOnze &Preset &Vorlage Program numerous Programmnummer INITVOICE INITVOICE LBank L-Bank Subcategory Unterkategorie Bank numerous Banknummer NONE KEIN HBank H-Bank Category Kategorie Prog Prog Preset Vorlage DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Veröffentlicht unter der GPL Lizenz. &Global &Global Pitch Envelope Tonhöhen-Hüllkurve PL3 PL3 PL2 PL2 PL1 PL1 PR1 PR1 PR2 PR2 PR3 PR3 Pitch modulation depth Vibrato-Stärke LFO LFO LFO Sync LFO Sync Pitch modulation sensitivity Vibrato-Sensitivität Pitch Modulation Sensitivity Vibrato-Sensitivität LFO Delay LFO Verzögerung LFO delay LFO Verzögerung LFO speed LFO Geschwindigkeit Amplitude modulation depth Tremolostärke Amplitude modulation sensitivity Tremolo-Sensitivität Amplitude Modulation Sensitivity Tremolo-Sensitivität AMS AMS LFO Waveform LFO Wellenform Pitch Modulation Depth Vibrato-Stärke PMD PMD LFO Speed LFO Geschwindigkeit AMD AMD Speed Geschwindigkeit Delay Verzögerung PMS PMS Modulation Matrix Modulationsmatrix <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> <b>Algorithmus 1</b> : <i>Op 1</i> moduliert durch <i>Op 2</i> moduliert durch <i>Op 3</i> moduliert durch <i>Op 4</i><br> <b>Algorithmus 2</b> : <i>Op 1</i> moduliert durch <i>Op 2</i> moduliert durch sowohl <i>Op 3</i> als auch <i>Op 4</i><br> <b>Algorithmus 3</b> : <i>Op 1</i> moduliert durch sowohl<i>Op 4</i> als auch <i>Op 2</i> moduliert durch <i>Op 3</i><br> <b>Algorithmus 4</b> : <i>Op 1</i> moduliert durch sowohl<i>Op 2</i> als auch <i>Op 3</i> moduliert durch <i>Op 4</i><br> <b>Algorithmus 5</b> : (<i>Op 1</i> moduliert durch <i>Op 2</i>) addiert zu (<i>Op 3</i> moduliert durch <i>Op 4</i>) <br> <b>Algorithmus 6</b> : Summe der drei <i>Op 1, 2, 3</i> alle moduliert durch <i>Op 4</i><br> <b>Algorithmus 7</b> : Summe der drei <i>Op 1, 2, 3</i> mit <i>Op 3</i> moduliert durch <i>Op 4</i><br> <b>Algorithmus 8</b> : Summe der vier <i>Op 1, 2, 3, 4</i> Op4 Feedback Op4 Rückspeisung Feedback level of the operator 4 Rückspeise ("Feedback")-level des Operators 4 Transpose Transponieren Op &1 Op &1 Scaling 1 Skalierung 1 LS1 LS1 RS1 RS1 Rate Scaling Ratenskalierung Attack Rate of the operator 1 Attackrate des Operators 1 Level Scaling Levelskalierung Amplitude Envelope 1 Amplitudenhüllkurve 1 RR1 RR1 D1R1 D1R1 D1L1 D1L1 D2R1 D2R1 Release Rate Release Rate 2° Decay Rate 2° Decay Rate 1° Decay Level 1° Decay Level 1° Decay Rate 1° Decay Rate Attack Rate Attack Rate AR1 AR1 Detune, OSCWave, EGShift 1 Verstimmung, OSCWave, EGShift 1 DET1 DET1 Detune Verstimmung EG Shift EG Shift 96dB 96dB 48dB 48dB 24dB 24dB 12dB 12dB Wave form Wellenform Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Wellenform 1 = <i>sin(<b>t</b>)</i><br> Wellenform 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wellenform 3 = <i>wenn <b>t</b>&#060 pi dann sin(<b>t</b>) sonst 0</i><br> Wellenform 4 = <i>wenn <b>t</b>&#060 pi dann sin(<b>t</b>)*abs(sin(<b>t</b>)) sonst 0</i><br> Wellenform 5 = <i>wenn <b>t</b>&#060 pi dann sin(2*<b>t</b>) sonst 0</i><br> Wellenform 6 = <i>wenn <b>t</b>&#060 pi dann sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) sonst 0</i><br> Wellenform 7 = <i>wenn <b>t</b>&#060 pi dann abs(sin(2*<b>t</b>)) sonst 0</i><br> Wellenform 8 = <i>wenn <b>t</b>&#060 pi dann sin(2*<b>t</b>)*sin(2*<b>t</b>) sonst 0</i> Sensitivity 1 Sensitivität 1 KVS1 KVS1 Amplitude Modulation Enable Amplitudenmodulation aktivieren AME1 AME1 Keyboard Velocity Sensitivity Keyboard Anschlagsdynamik-Sensitivität Key Velocity Sensitivity Noten-Anschlagsdynamik-Sensitivität EBS1 EBS1 EG Bias Sensitivity EG Bias Sensitivität Eg Bias Sensitivity EG Bias Sensitivität Frequency 1 Frequenz 1 Coarse 1 Grob 1 Coarse Ratio Grob Verhältnis Fine 1 Fein 1 Fine Ratio Fein Verhältnis Freq 1 Freq 1 Fixed Frequency Feste Frequenz Toggle Fix Frequency "Feste Frequenz" umschalten FIX FIX OUT 1 OUT1 Output Volume Ausgangslautstärke Volume Lautstärke Op &2 Op &2 Amplitude Envelope 2 Amplitudenhüllkurve 2 D1R2 D1R2 D1L2 D1L2 D2R2 D2R2 RR2 RR2 AR2 AR2 Frequency 2 Frequenz 2 Coarse 2 Grob 2 Fine 2 Fein 2 Freq 2 Freq 2 Scaling 2 Skalierung 2 LS2 LS2 RS2 RS2 OUT 2 OUT 2 Detune, OSCWave, EGShift 2 Verstimmung, OSCWave, EGShift 2 DET2 DET2 Sensitivity 2 Sensitivität 2 EBS2 EBS2 KVS2 KVS2 AME2 AME2 Op &3 Op &3 Amplitude Envelope 3 Amplitudenhüllkurve 3 D1R3 D1R3 D1L3 D1L3 D2R3 D2R3 RR3 RR3 AR3 AR3 Scaling 3 Skalierung 3 LS3 LS3 RS3 RS3 OUT 3 OUT 3 Frequency 3 Frequenz 3 Coarse 3 Grob 3 Fine 3 Fein 3 Freq 3 Freq 3 Detune, OSCWave, EGShift 3 Verstimmung, OSCWave, EGShift 3 DET3 DET3 Sensitivity 3 Sensitivität 3 EBS3 EBS3 KVS3 KVS3 AME3 AME3 Op &4 Op &4 amplitude Envelope 4 Amplitudenhüllkurve 4 AR4 AR4 D1R4 D1R4 D1L4 D1L4 D2R4 D2R4 RR4 RR4 Frequency 4 Frequenz 4 Coarse 4 Grob 4 Fine 4 Fein 4 Freq 4 Freq 4 Scaling 4 Skalierung 4 LS4 LS4 RS4 RS4 OUT 4 OUT 4 Detune, OSCWave, EGShift 4 Verstimmung, OSCWave, EGShift 4 DET4 DET4 Sensitivity 4 Sensitivität 4 EBS4 EBS4 KVS4 KVS4 AME4 AME4 &Func &Func Delay Pan Depth Delay Pan LFO Freq Delay Ch Send Level Channel Chorus Kanal-Chorus Delay Feedback Delay On/Off, Return Level On An Delay Beat Ratio Delay BPM Foot Control Fußschalter Pitch Bend Range Pitchbend-Bereich Pitch Tonhöhe Amplitude Amplitude Modulation Wheel Modulations-Rad Breath Control Anblaskontrolle Pitch Bias Tonhöhen-Bias Envelope Bias Hüllkurven-Bias After Touch Aftertouch Phony Mode POLY POLY MONO MONO Potamento Portamento Portamento Mode Portamento-Modus FINGER FINGER FULL VOLL PT PT Portamento Time Portamento-Dauer C&horus C&horus Chorus Parameters Chorus-Parameter Channel send level On/Off and Return level Select LADSPA plugin LADSPA-Plugin auswählen Change plugin Plugin ändern &Reverb &Reverb Reverb Parameters Nachhallparameter &Config &Einstellen Font Size Schriftgröße Quality Qualität High Hoch Middle Mittel Low Gering Ultra low Sehr Gering Filter Filter Save Mode (into the song) Speichermodus (in das Lied) Save only the used presets Nur die benutzten Vorlagen speichern Save the entire set Das gesamte Set speichern Save the configuration Die Konfiguration speichern Configuration File Konfigurationsdatei Save... Speichern... Load... Laden... Save as default Als Standard speichern Colors Farben Text Text Background Hintergrund Edit Text Text bearbeiten Edit Background Hintergrund bearbeiten Red Rot Blue Blau Green Grün Set Path Pfad setzen Image in the background : Hintergrundbild: Browse... Durchsuchen... Load the set at the initialization : Das Set bei der Initialisierung laden: Set Brightness, Detune, Attack and Release of the current channel to default Helligkeit, Verstimmung, Attack und Release des momentanen Kanals auf den Standard setzen Res. Ctrl Cut all notes off Alle Noten abschneiden Panic! Panik! Number of Voices Anzahl der Stimmen Number of voices Anzahl der Stimmen Enable Einschalten Channel Kanal Vol Vol Channel Ctrl Kanal Ctrl Release Release Attack Attack Brightness Helligkeit Modulation Modulation Pan Pan DelOverlapsBase MusE: Delete Overlaps MusE: Überlappungen entfernen Range Bereich All Events Alle Ereignisse Selected Events Ausgewählte Ereignisse Looped Events Ereignisse innerhalb Schleife Selected Looped Ausgewählt und innerhalb der Schleife Parts Parts Selected parts or c&urrent part Aus&gewählte Parts oder aktueller Part All parts &in editor &Alle Parts im Editor OK Bestätigen Cancel Abbrechen DidYouKnow Did you know? Wussten Sie? Don't show on startup Nicht beim Starten zeigen Next tip Nächster Tipp Close Schließen DuplicateTracksBase Duplicate tracks Spuren duplizieren Number of copies Anzahl der Kopien Copy all routes Alle Routen kopieren Default routing Standard-Routen No routes Keine Routen Duplicate all parts Alle Spuren duplizieren Make independent copies of all parts Unabhängige Kopien von allen Parts erzeugen Make clones of all parts Klone von allen Parts erzeugen No parts Keine Parts Copy parts Parts kopieren Copy drumlist Schlagzeugliste kopieren Copy standard (vol, pan) and synth controllers Synth und Standard-Controller (Vol, Pan) kopieren Copy standard controllers (vol, pan) Standard-Controller (Vol,Pan) kopieren Copy effects rack plugins Effekt-Rack-Plugins kopieren Copy plugin controllers Plugin-Controller kopieren Ok Bestätigen Cancel Abbrechen EditCtrlBase MusE: Edit Controller Event MusE: Kontroller Ereignis bearbeiten Time Position Position Available Controller: Verfügbare Kontroller: Create New Controller Neuen Kontroller erstellen textLabel3 Beschriftung3 Value Wert Controller Kontroller Note Note H-Bank H-Bank L-Bank L-Bank Program Programm off aus pushButton4 Schaltfläche4 &OK &Bestätigen &Cancel &Abbrechen EditGainBase MusE: Modify gain MusE: Gain verändern Gain Gain 200% 200% 100% 100% 0% 0% &Reset Zu&rücksetzen Alt+R Alt+R &Apply &Anwenden Alt+A Alt+A &Cancel &Abbrechen Alt+C Alt+C EditInstrumentBase MusE: Instrument Editor MusE: Instrumenten Editor High Bank: High Bank: Low Bank: Low Bank: Program: Programm: Patch program number. --- means don't care. &Delete &Löschen Alt+D Alt+D High Bank Low Bank Patch program number. --- means don't care. Patch Name: Dr def Drum Schlagzeug GM GM GS GS XG XG This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. Dies ist eine Liste häufig verwendeter Midi Kontroller. MusE behandelt allerdings Kontroller wie "pitch" und "program changes" wie normale Kontroller. Properties Eigenschaften Control7 Kontroller7 Control14 Kontroller14 RPN RPN NRPN NRPN Pitch Tonhöhe Program Programm H-Ctrl H-Ctrl L-Ctrl L-Ctrl Min Min Max Max Name Name Drum&maps Schlagzeug&belegung Type Typ Hex Entry: Hex Eintrag: &File &Datei Tools Werkzeuge New Neu &New &Neu Ctrl+N Ctrl+N Open Öffnen &Open... &Öffnen ... Ctrl+O Ctrl+O Save Sichern &Save &Sichern Ctrl+S Ctrl+S Save As Sichern Unter Save &As... Sichern &Unter... Exit Beenden E&xit &Beenden Instrument Name: Name des Instruments: Selected instrument name. Name des ausgewählten Instruments. List of defined instruments. Liste aller definierten Instrumente. Note off mode: Note off mode Selects how to handle note off events. The instrument can use note off events, or not at all, or convert them to zero-velocity note on events which helps save midi interface bandwidth. Refer to the instrument manufacturer's midi implementation chart for details. Pa&tches Pa&tches List of groups and patches. Liste der Gruppen und Patches. Group/Patch Gruppe/Patch Name: Name: Group or patch name Gruppen- oder Patchname Patch high bank number Hohe Banknummer für den Patch Patch high bank number. --- means don't care. Hohe Banknummer für den Patch. --- i.d.R. uninteressant. --- dont care --- Patch low bank number Niedrige Banknummer für den Patch Patch low bank number. --- means don't care. Hohe Banknummer für den Patch. --- i.d.R. uninteressant. --- --- Patch program number Programmnummer für den Patch Drum patch Schlagzeug-Patch If set, the patch is available only for drum channels. Wenn aktiviert, ist der Patch nur für Schlagzeugkanäle verfügbar. GM patch GM Patch If set, the patch is available in a 'GM' or 'NO' midi song type. Wenn aktiviert, ist der Patch nur in einem "GM" oder "NO" Songtypen verfügbar. GS patch GS Patch If set, the patch is available in a 'GS' or 'NO' midi song type. Wenn aktiviert, ist der Patch nur in einem "GS" oder "NO" Songtypen verfügbar. XG patch XG Patch If set, the patch is available in an 'XG' or 'NO' midi song type. Wenn aktiviert, ist der Patch nur in einem "XG" oder "NO" Songtypen verfügbar. Show in tracks: In Tracks zeigen: Midi Midi &Drum Schlagzeug Delete group or patch Lösche Gruppe oder Patch New patch Neuer Patch New &Patch Neuer &Patch Alt+P Alt+P New group Neue Gruppe New &Group Neue &Gruppe Alt+G Alt+G Contro&ller Contro&ller Common: Gemeinsam: List of defined controllers Liste von definierten Controllern List of defined controllers. Liste von definierten Controllern. Min Min Max Max Def Def Midi controller name Name des Midi-Controllers Type: Typ: Midi controller type Typ des Midi-Controllers RPN14 RPN14 NRPN14 NRPN14 Midi controller number high byte Midi Controllernummer höchstwertiges Byte Midi controller number low byte (* means drum controller) Midi Controllernummer niederwertiges Byte (* bedeutet Schlagzeugcontroller) Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and GS/XG type songs and instruments. Allows controllers for each instrument in Muse's drum map. The low byte will be replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. Niederwertiges Byte der MIDI-Controller- nummer. Wenn es "*" ist, ist der Controller ein Schlagzeugcontroller. Für Schlagzeug- spuren und GS/XG-Songs und -Instrumente. Ermöglicht Controller für jedes Instrument in der Schlagzeug-Liste. Das niederwertige Byte wird durch die "ANote" der Liste ersetzt. Beispiele: Die Schlagzeugcontroller der GS- und XG-Instrumente. * wild card * Range: Bereich: Minimum value. If negative, auto-translate. Minimalwert. Wenn negativ, automatisch umsetzen. Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. Useful for controllers which should be displayed with zero bias. For example, 'Pan': Minimum: -64 Maximum: 63 True range: Min: 0 Max: 127 (bias = 64) 'CoarseTuning': Min: -24 Max: 23 True range: Min: 40 Max: 87 (bias = 64) Bias is determined from controller type: 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 Type 'Pitch' is the exception. It is biased at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) Minimalwert. Wenn er negativ ist, wird der Bereich automatisch in einen positiven umgesetzt. Nützlich für Controller, die ohne Bias dargestellt werden sollen. Bespielsweise "Panorama": Minimum -64, Maximum 63. Wirklicher Bereich: 0 bis 127 (Bias ist also 64) "Grobes Stimmen": -24 bis 23 In Wirklichkeit: 40 bis 87, Bias=64. Das Bias wird durch den Controller-Typen bestimmt: für 7-bittige Controller: Bias=64 für 14-bittige Controller: Bias= 8192 Der Typ "Pitch" ist die Ausnahme: Er hat kein Bias, obwohl er ein negatives Minimum besitzt: Er geht von -8192 bis 8191 Maximum value Maximalwert Default: Standard: L-Bank L-Bank off aus Progr. Progr. ??? ??? H-Bank H-Bank Default value. Off: No default. Standardwert. "Aus": Kein Standardwert. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). Standard- (Initial-)Wert. Aus bedeutet "kein Standard". Wenn ein Standardwert ausgewählt ist, wird der Wert zu dem Controller gesendet, wenn er zum Lied hinzu- gefügt wird (in der Pianoroll oder dem Eventeditor). Wenn das Lied neu geladen wird, wird der Wert erneut gesendet. Ansonsten bleibt der Controller auf seinem vorherigen Wert. Controller werden auch automatisch zu einem Lied hinzugefügt, sobald ein solches MIDI- Controller-Event empfangen wurde. Achtung! Passen Sie bei Controllern wie "Sustain" und "ResetAllController" auf Standardwerte auf. Sie sollten wahrscheinlich den Standardwert auf "aus" setzen (in der Pianoroll oder dem Schlagzeug- editor und dem Instrumenteneditor). off dont care aus Add common controller Füge gemeinsamen Controller hinzu &Add &Hinzufügen Alt+A Alt+A Delete controller Controller löschen Create a new controller Neuen controller erstellen New &Controller Neuer &Controller Alt+C Alt+C Null Param Hi: Null Param Hi: Null parameter number High byte Null Parameternummer höchstwertiges Byte If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events from corrupting the RPN/NRPN controller. Typically, set to 127/127, or an unused RPN/NRPN controller number. Wenn gesetzt, werden diese "null" Parameter- nummern gesendet nach jedem RPN/NRPN- Event. Das verhindert, dass nachfolgende Datenevents die RPN/NRPN-Controller zerstören. Normalerweise auf 127/127 oder eine unbenutzte RPN/NRPN-Controllernummer gesetzt. Lo: Lo: Null parameter number Low byte Null Parameternummer niederwertiges Byte S&ysEx S&ysEx SysEx List: SysEx-Liste: New SysE&x Neuer SysE&x Alt+X Alt+X Patch Collections: Patch-Sammlungen: &Copy &Kopieren &Remove &Entfernen &Up Nach &oben &Down Nach &unten Contro&llers &Controller &Add Common... &Gemeinsame hinzufügen... W W Drum default: Schlagzeugdefault: Comment: Kommentar: Note: The enclosing F0 ... F7 are not required Hinwies: Die umgebenden F0 ... F7 sind nicht erforderlich &Initialization &Initialisierung Instrument initialization sequence: Instrument-Initialisierungssequenz: &Add... &Hinzufügen... &Change... &Ändern... &Help &Hilfe &Close &Schließen Close Schließen new item neuer Eintrag What's this? Was ist das? Info Info Additional information EditNoteDialogBase MusE: Enter Note MusE: Note eingeben OK Bestätigen Cancel Abbrechen Length: Länge: Time Position: Position: Pitch: Tonhöhe: Velocity On: Velocity An: Velocity Off: Velocity Aus: EditSysexDialogBase MusE: Enter SysEx MusE: SysEx eingeben TimePosition: Position: Hex: Note: The enclosing F0 ... F7 are not required Hinwies: Die umgebenden F0 ... F7 sind nicht erforderlich Name: Name: Comment: Kommentar: &Select... &Auswählen &OK &Bestätigen &Cancel &Abbrechen OK Bestätigen Cancel Abbrechen FLUIDGui FLUID: open Soundfile FLUID: Sounddatei öffnen FLUIDGuiBase Form1 Form1 Soundfont Soundfont Load Laden FLUIDSynthGuiBase FLUID Synth FLUID Synth Load Laden Delete Löschen Dump Info Dump-Info ID ID Fontname Name Chnl Kanal Soundfont Soundfont Drum Chnl Drumkanal Level Level Width Breite Damping Dämpfung Room Size Raumgröße Reverb Reverb CHANNEL SETUP KANAL SETUP Sine Sinus Triangle Dreieck Type Typ Number Anzahl Speed Geschwindigkeit Depth Stärke Chorus Chorus Gain Verstärkung LOADED SOUNDFONTS GELADENE SOUNDFONTS FileDialogButtons Home Home Global Global User Benutzer Project Projekt read Midi Port Configuration lese Midi-Port- Konfiguration write window states schreibe Fenster fdialogbuttons fdialogbuttons FluidSynthGui Choose soundfont Soundfont auswählen GateTimeBase Range Bereich All Events Alle Ereignisse Selected Events Ausgewählte Ereignisse Looped Events Ereignisse innerhalb Schleife MusE: Modify Note Length MusE: Notenlänge verändern Selected Looped Ausgewählt und innerhalb der Schleife Parts Parts Selected parts or c&urrent part Aus&gewählte Parts oder aktueller Part All parts &in editor &Alle Parts im Editor Values Werte Rate: Rate: Offset: Zeitversatz: % % lenNew = (lenOld * rate) + offset lenNeu = (lenAlt * rate) + offset OK Bestätigen Cancel Abbrechen GlobalSettingsDialogBase MusE: Global Settings MusE: Globale Einstellungen Audio Audio Mixer Mischpult dB dB min. Meter Value min. Pegelreglerwerte Midi Midi Ticks Ticks 1024 1024 2048 2048 4096 4096 Displayed Resolution (Ticks/Quarternote) Angezeigte Auflösung (Ticks/Viertelnote) 48 48 Sample rate conversion, time stretching and pitch shifting Sampleratenkonversion, Timestretching und Pitchschifting Editors Editoren Open editors in tabs (else in separate windows) Editoren in Tabs öffnen (sonst in separaten Fenstern) Piano roll Pianoroll Drum editor Schlagzeugeditor Wave editor Wave-Editor Score editor Partitureditor Master track editor Masterspur-Editor DSSI VST Directories where dssi-vst (if installed) looks for Wine vst plugins (*.dll files) Directories containing Linux vst plugins (*.so files) 96 96 192 192 384 384 768 768 1536 1536 3072 3072 6144 61144 12288 12288 RTC Resolution (Ticks/Sec) RTC (RealTimeClock) Auflösung (Ticks/Sekunde) /sec /sek GUI Refresh Rate GUI Aktualisierungsrate Warn if timer frequency is inadequate Warnen falls Timerfrequenz nicht adäquat Track height Trackhöhe Use project save dialog Benutze Projekt-Speichern-Dialog Some popup menus stay open (else hold Ctrl) Manche Popup-Menüs bleiben offen (ansonsten Strg halten) Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. Erlaubt es, dass einige Popup-Menüs offen bleiben. Ansonsten können Sie auch Strg drücken, um ein solches Menü offen zu halten. In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. In manchen Bereichen vermindert die mittlere Maustaste Werte, während die rechte erhöht. Benutzer ohne mittlere Maustaste können diese Option nutzen, damit sich in solchen Bereichen die linke Maustaste wie die mittlere verhält. Use left mouse button for decreasing values Benutze linke Maustaste, um Werte zu erniedrigen Shift + Right click sets left range marker Shift + Rechtsklick setzt den linken Bereichs-Marker Allow adding hidden tracks in track list menu Erlaube es, verborgene Spurtypen hinzuzufügen in der Spurliste Unhide tracks when adding hidden tracks Verborgene Spurentypen anzeigen, wenn ein solcher Typ hinzugefügt wird Smart focus Intelligenter Fokus After editing, controls will return focus to their respective canvas Nach dem Editieren werden GUI-Elemente den Fokus zur jeweilgen Canvas zurückgeben Show newly created midi velocity graphs per-note Neu angelegte Midi-Velocity-Graphen pro Note anzeigen px px Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. Borderless zoom/pan mouse (else use alternate method) Randlose Zoom/Pan-Mausfunktion (sonst alternative Methode verwenden) start with &last song Zuletzt &bearbeitetes Lied Midi controller behaviour Verhalten des Midi-Controllers Send Null parameters after an (N)RPN value If set, 'null' parameter numbers will be sent after each RPN/NRPN event. (A 'null' parameter number is 127.) This prevents subsequent rogue or accidental 'data' events (without parameters) from corrupting the current RPN/NRPN controller. Normally it is OK to leave this unset, but use it if there is a possibility of such 'data' events. If this is set, the 'Optimize controllers' setting does not affect these controllers' parameters, only the H/L values. Send Null parameters Null-Parameter senden Don't send redundant parameters or values Keine redundanten Parameter der Werte senden If set, redundant H/L parameters or H/L values are not sent. They are sent only if they changed. This can save midi interface bandwidth. But it is possible the receiving device might miss the previous changes (for example if the device is turned on after they were sent). The 'Panic' button will force them to be resent. Certain operations will also force them to be resent, such as clicking a GUI midi control (but not while adjusting it). Optimize controllers Midi-Controller optimieren Record drum tracks Schlagzeugspuren aufnehmen Record a&ll instruments Alle Instrumente au&fnehmen Latency Latenz Whether to enable latency correction/compensation: Latenzkorrektur/-kompensation soll aktiviert werden: Enable latency correction/compensation Latenzkorrektur/-kompensation aktivieren Project latency Projektlatenz Whether completely independent branches share a common project-wide latency: Vollständig unabhängige Zweige sollen die projektweite Latenz gemeinsam nutzen: Project-wide latency Projektweite Latenz Unterminated branches Nicht terminierte Zweige Whether unterminated branches participate in latency correction/compensation: Nicht terminierte Zweige sollen an Latenzkorrektur/-kompensation teilnehmen: Unterminated input branches Nicht terminierte Input-Zweige Unterminated output branches Nicht terminierte Output-Zweige Track monitoring Track Monitoring Whether track monitor buttons affect latency correction/compensation: Track-Monitor-Schaltflächen sollen die Latenzkorrektur/-kompensation beeinflussen: Monitoring affects latency Monitoring beeinflusst die Latenz GUI GUI GUI refresh rate GUI Aktualisierungsrate LV2 UI open behavior Verhalten beim Öffnen von LV2 UIs Set range markers (left / right) Bereichsmarker setzen (links / rechts) Middle mouse button / Right mouse button Mittlere Maustaste / Rechte Maustaste Ctrl + Right mouse button / Right mouse button Ctrl + rechte Maustaste / Rechte Maustaste Ctrl + Left mouse button / Ctrl + Right mouse button Ctrl + linke Maustaste / Ctrl + rechte Maustaste Options Optionen Show status bar Statusleiste anzeigen Show note tooltips in midi editors Noten-Tooltips in Midieditoren anzeigen GUI Tweaks GUI Tweaks Tweaks Tweaks Keep transport window on top (requires restart) Transportfenster immer oben (erfordert Neustart) Use old-style stop shortcut Benutze altes Stopp-Kürzel Show note names on notes in pianoroll Notennamen auf Events in Pianoroll anzeigen Traditional MusE SDI Klassisches SDI Monitor on record-arm automatically Automatisches Monitoring bei Aufnahme-Scharfschaltung Style hack: Force line edit widgets to draw a frame Stil-Hack: Ein Frame bei LineEdit-Widgets erzwingen Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. This hack forces a frame to be drawn, but may interfere with other styles. Whether to show knobs or sliders in certain places, for example mixer strips Whether record-arming a track automatically activates monitoring. Prefer knobs instead of sliders Drehregler statt Schieberegler bevorzugen Live update wave parts while recording Waveparts während Aufnahme aktualisieren Scrollable submenus Scrollbare Submenus Ctrl + Right click sets left range marker Ctrl + Rechtsklick setzt den linken Bereichs-Marker Use first available Zuerst verfügbar Ask once Einmal fragen Ask always Immer fragen Bug fix: Fix frozen MDI windows Bug-Fix: Nicht reagierende MDI-Windows korrigieren Prefer midi volume as decibels instead of 0-127 Dezibell statt 0-127 für Midilautstärke bevorzugen Show knob and slider values Dreh- und Schiebereglerwerte anzeigen Whether to show knob and slider values in certain places, for example mixer strips. Turn off to reduce clutter. LV2 UI Open behavior LV2 UI Verhalten beim Öffnen Start MusE MusE starten Start with &last song Zuletzt &bearbeitetes Lied Start with &template Mit &Vorlage starten Sta&rt with song Mit &Lied starten On launch Beim Start Show splash screen Begrüßungsbild anzeigen Show "Did you know?" dialog Zeige "Wussten Sie?"-Dialog Audio driver settings (require restart) Audio-Driver Einstellungen (erfordert Neustart) Period size (frames per period): Periodengröße (Frames pro Periode): Minimum meter value Minimaler Pegelreglerwert Minimum slider value Minimaler Schiebereglerwert Sample rate conversion, time stretching and pitch shifting: Sampleratenkonversion, Timestretching und Pitchschifting Audio converter settings... Einstellungen für den Audiokonverter... Revert native GUI scaling for plugins in HiDPI HiDPI Skalierung von nativen Plugin-GUIs zurücksetzen Revert native UI window HiDPI scaling (some plugins ignore this setting) HiDPI Skalierung von nativen Plugin-GUIs zurücksetzen (einige Plugins ignorieren diese Einstellung) Open MDI subwindows maximized MDI-Subwindows maximiert öffnen Drum tracks Schlagzeugspuren Only offer old-s&tyle drumtracks Nur old-style Schlagzeugspuren anbieten Prefer old-style drumtrac&ks Old-style Schlagzeugspuren bevorzugen GUI Style GUI-Stil MDI-subwindowness and sharing menus MDI-Unterfenster und gemeinsame Nutzung von Menüs Presets: Voreinstellungen: traditional MusE SDI traditionelles MusE SDI Cakewalk-like MDI MDI (wie Cakewalk) Borland-/Mac-like MDI MDI (wie Borland/Mac) &Apply &Anwenden &Ok &Bestätigen &Cancel Abb&rechen Application Anwendung Start Muse MusE starten start with last song Mit letztem Lied starten start with song Mit bestimmtem Lied starten Views Ansichten y-pos y-pos show zeigen x-pos x-pos height Höhe width Breite Big Time Zeitanzeige groß Transport Transport Cur Jetzt set current values Jetzige Werte übernehmen show splash screen Begrüßungsbild anzeigen Mixer A Mixer A Mixer B Mixer B show "Did you know?" dialog Zeige "Wussten Sie?"-Dialog Start song Beim Starten geöffnetes Lied start with &template &Vorlage sta&rt with song &Lied min. Slider Val min. Schiebereglerwert Enable denormal protection Denormal-Schutz aktivieren Enable output limiter Aktiviere Ausgangs-Limiter External Waveditor Externer Wave-Editor External Waveditor command Befehl für externen Wave-Editor Note: External editor opened from the internal editor. Hinweis: der externe Editor, der vom internen aus geöffnet wird. Dummy Audio Driver (settings require restart) Dummy Audiotreiber (erfordert Neustart) Hz Hz Period size (Frames per period): Periodengröße (Frames pro Periode): 16 16 Project directory Projektverzeichnis Projects: Projekte: ... ... Main Window Hauptfenster Choose start song or template Wählen Sie ein Lied oder eine Vorlage für den Start Reset to default Auf Standardwerte zurücksetzen start with template mit Vorlage starten Start template or song: Mit Vorlage oder Lied starten: Read MIDI Ports configuration from file, or else automatically configure Lese MIDI-Port-Konfiguration von der Datei, oder konfiguriere sonst automatisch Read MIDI Ports configuration Lese MIDI-Port-Konfiguration Warn if opening file versions different than current version Warnung beim Öffnen von Dateien mit anderer Version Auto save (every 5 minutes if not playing/recording) Autosave (alle 5 Minuten, nicht während Wiedergabe/Aufnahme) Show Anzeigen Plugin Paths Plugin-Pfade Ordered by priority. Changes take effect on restart. Nach Priorität sortieren (Änderungen werden nach Neustart aktiv). The plugin cache is used for safety and speed. It automatically rescans when any of the given directories change, for example by installing a new plugin. Click this button to force the cache to recreate upon next restart. &Force plugin rescan and cache recreation on next restart &Full-Plugin-Rescan und Cache-Auffrischung beim Neustart erzwingen LADSPA LADSPA DSSI DSSI VST VST Linux VST Linux VST LV2 LV2 NOTE: Ensure the directory containing core LV2 bundles is included (typically /usr/lib/lv2). Otherwise all plugins may not load properly. HINWEIS: Verzeichnis mit Core-LV2-Bundles muss enthalten sein (typischerweise /usr/lib/lv2). Sonst können eventuell nicht alle Plugins korrekt geladen werden. Move up Nach oben bewegen Move down Nach unten bewegen Add... Hinzufügen ... Edit... Bearbeiten... Remove Entfernen Audio Driver settings (require restart) Audio-Driver Einstellungen (erfordert Neustart) Sample rate: Sampling-Rate: Audio backend: Audio Backend: Shorter periods give better midi playback resolution. Note that this cannot be set with Jack devices! Note that samplerate cannot be set with Jack devices! Try to use Jack Freewheel Versuche, Jack-Freewheel zu nutzen Speeds bounce operations Beschleunigt Abmisch-Operationen Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Benutzt Jack's Freewheel-Modus, wenn möglich. Das beschleunigt Abmisch-Operationen dramatisch. VST in-place VST in-place Enable VST in-place processing (restart required) Aktiviere VST-in-place-verarbeitung (Neustart erforderlich) Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. Aktiviere VST-in-place-verarbeitung: Schalten Sie das aus, wenn VST Ladspa Effekt-Rack-Plugins nicht funktionieren oder laut rückspeisten, selbst wenn sie in-place-fähig sein sollten. Einstellung erfordert Neustart. Minimum control period Minimale Kontroll-Periode Minimum audio controller process period (samples). Minimale Audiocontroller-Prozessperiode (Samples). Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. Minimale Audiocontroller-Periode (Samples). Beeinflusst die Reaktionszeit von Audiocontrols und Controller-Graphen. Setzen Sie einen niedrigen Wert für schnelle, weiche Kontrolle. Wenn das Geschwindigkeitsprobleme verursacht, benutzen Sie einen höheren Wert. 1 1 2 2 4 4 8 8 32 32 64 64 128 128 256 256 512 512 Sample rate Sampling-Rate Shorter periods give better midi playback resolution. Kürzere Perioden führen zu einer besseren Midi-Abspiel-Auflösung. &Don't record hidden instruments &Ausgeblendete Instrumente nicht aufnehmen Don'&t record muted instruments &Stummgeschaltete Instrumente nicht aufnehmen Don't record &hidden or muted instruments &Stummgeschaltete oder ausgeblendete Instrumente nicht aufnehmen Instrument initialization Instrument-Initialisierung Send instrument initialization sequences Instrument-Initialisierungssequenzen senden Warn if instrument initialization sequences pending Warnen falls Instrument-Initialisierungssequenzen anstehen Send instrument controller default values if none in song, at init or rewind Standardwerte für Instrumentencontroller senden (bei Init/Rewind) 8192 8192 16384 16384 32768 32768 Midi Resolution (Ticks/Quarternote) Midi Auflösung (Ticks/Viertelnote) Only offer new-style &drumtracks Nur &new-style Schlagzeugspuren anbieten Prefer &new-style drumtracks Ne&w-style Schlagzeugspuren bevorzugen Record new style drum tracks New-style Schlagzeugspuren aufnehmen GUI Behaviour GUI-Verhalten Use old-style stop shortcut: Benutze altes Stopp-Kürzel: Move single armed track with selection Bewege "für Aufnahme scharfschalten" mit Auswahl On Launch Beim Start Behavior Verhalten LegatoBase MusE: Legato MusE: Legato Range Bereich All Events Alle Ereignisse Selected Events Ausgewählte Ereignisse Looped Events Ereignisse innerhalb Schleife Selected Looped Ausgewählt und innerhalb der Schleife Parts Parts Selected parts or c&urrent part Aus&gewählte Parts oder aktueller Part All parts &in editor &Alle Parts im Editor Settings Einstellungen ticks Ticks Minimum Length Minimallänge Allow shortening notes Erlaube, Noten zu verkürzen OK Bestätigen Cancel Abbrechen MITTransposeBase MusE: Midi Input Plugin: Transpose MusE: Midi Eingang PlugIn: Transponieren On An TriggerKey Auslösertaste Transpose: Transponieren: +0 +0 MRConfigBase MusE: Midi Input Plugin: Remote Control MusE: Midi Eingang PlugIn: Fernbedienung Activate Einschalten On An Actions Funktionen Stop Stopp Record Aufnahme Goto Left Mark Zum linken Marker bewegen Play Wiedergabe Insert rest (step rec) Pause einfügen (StepRec) MdiSettingsBase MDI subwin MDI-Unterfenster Shares menu when subwin Teilt das Menü, wenn Unterfenster Shares menu when free Teilt das Menü, wenn freies Fenster MetronomeConfigBase MusE: Metronome Config MusE: Metronom Konfiguration Metronome Metronom Audio Beep Interner PC Lautsprecher Volume Lautstärke Audio master Audio-Master Meas Meas Beat Schlag Accent1 Akzent 1 Accent2 Akzent 2 Sample Sample Preferences Voreinstellungen Apply the settings globally or to the current song: Einstellungen global oder im aktuellen Lied anwenden: Use global metronome Globales Metronom Use song-specific metronome Liedspezifisches Metronom Enable accents for the audio and midi metronomes: Akzente für Audio- und Midi-Metronom aktivieren: &Two samples (old samples) Zwei Samples (&alte Samples) Four samples (&new samples, with accents) Vier Samples (&neue Samples, mit Akzenten) Audio Audio MIDI Midi MIDI Click MIDI Klick Midi Channel Midi Kanal Accent 2 Velocity Akzent 2 Velocity Accent 1 Velocity Akzent 1 Velocity Accent 1 Note Akzent 1 Note Measure Note Midi Note Taktbeginn Measure Velocity Midi Note Taktschlag Beat Velocity Taktbeginn Anschlagstärke Beat Note Taktschlag Anschlagstärke Midi Port Midi Anschluss Precount Einzählen enable einschalten Bars Takte From Mastertrack Von der Masterspur / / Signature Taktmaß Prerecord Aufnahme ab Einzählen Preroll Vorlauf &Apply &Anwenden &OK &Bestätigen &Cancel Abb&rechen Choose outputs... Wähle Ausgänge... 50 50 % Audio volume % Audiolautstärke Hint: Enable metronome in Transportpanel Hinweis: Aktivieren Sie das Metronom im Transportpanel Accent 2 Note Akzent 2 Note Off Aus Accents Akzente Accent sequence beats Taktschläge für Akzentsequenz Current accent 1 sequence: (Click to change) Aktuelle Sequenz für Akzent 1 (Zum Ändern klicken) Current accent 2 sequence: Aktuelle Sequenz für Akzent 2: Factory Factory User Benutzer Preset sequences Preset-Sequenzen Add current accents to presets Aktuelle Akzente zu Presets hinzufügen Make a new preset from the current accents. Neuen Preset aus aktuellen Akzenten erstellen + + Delete preset Preset löschen Delete the selected preset. Only user presets can be deleted. Ausgewählten Preset lsöchen. Nur benutzerdefinierte Presets können gelöscht werden. - - Use preset Preset verwenden Copy the preset to the current accents. Preset in aktuelle Akzente kopieren. -> -> Reset to Factory or User defaults Auf Factory- oder Benutzervoreinstellungen zurücksetzen Resets all current accent sequences to default Factory or User presets. Alle aktuellen Akzente auf Factory- oder Benutzervoreinstellungen zurücksetzen. ! ! Enable the precount Einzählen einschalten Enable Einschalten Number of bars Taktanzahl Use Mastertrack time signature Takt vom Mastertrack verwenden Beats per bar Taktschläge Beat note type Takttyp Enable precount when playing Einzählen beim Abspielen einschalten Precount on play Einzählen beim Abspielen Mute the metronome after precount Nach Einzählen Metronom stummschalten Mute metronome Metronom stummschalten Not supported yet Noch nicht unterstützt Alt+A Alt+A Alt+O Alt+O Alt+C Alt+C MidiAudioControlBase Midi control MIDI-Steuerung Port: Port: Channel: Channel: Control type: Controller-Typ: &Learn &Lernen Control7 Controller7 Control14 Controller14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Pitch-Bend Program Programm Hi: Lo: Learn Lernen MidiFilterConfigBase MusE: Midi Input Filter MusE: Midi Eingangsfilter Record Filter Aufnahme Filter Note On Note an Poly Pressure Poly Anschlagsdruck Controller Kontroller Program Change Programmwechsel After Touch Aftertouch Pitch Bend Pitch bend Sysex Sysex Thru Filter Thru Filter Controller Filter Kontroller Filter Channel Filter Kanal Filter 14 14 10 10 6 6 12 12 4 4 2 2 9 9 8 8 3 3 13 13 15 15 16 16 7 7 11 11 5 5 1 1 MidiInputTransformDialogBase MusE: Midi Input Transformator MusE: Midi Eingang Transformator Filter Filter All alle Equal gleich Unequal ungleich Note Note Poly Pressure Poly Anschlagsdruck Control Change Kontrollerwechsel Aftertouch Aftertouch Pitch Bend Pitch bend NRPN NRPN RPN RPN Program Programm Value 2 Wert 2 Value 1 Wert 1 Event Type Ereignistyp Ignore ignorieren Higher größer Lower kleiner Inside innerhalb Outside außerhalb Channel Kanal Port Anschluss Processing Bearbeitung Keep behalten Fix fest Plus plus Minus minus Multiply multiplizieren Divide dividieren Invert invertieren ScaleMap Notenbelegung Flip umdrehen Dyn Dyn Random zufällig Toggle Umschalten Modules Module 1 1 2 2 3 3 4 4 enable modul 1 Modul 1 einschalten enable modul 2 Modul 2 einschalten enable modul 3 Modul 3 einschalten enable modul 4 Modul 4 einschalten Preset Vorlage Name: Name: Comment: Kommentar: Function Funktion &New &Neu create new preset Neue Vorlage erzeugen &Delete &Löschen delete preset Vorlage löschen Enable module 1 Enable module 2 Enable module 3 Enable module 4 &Dismiss &Verwerfen PresetList Liste Vorlagen MidiSyncConfigBase MusE: Midi Sync MusE: Midi Sync Apply Anwenden Ok Bestätigen Cancel Abbrechen hour Stunde h Std minute Minute m min second Sekunde s Sek frame Rahmen f R subframe Subrahmen 24 24 25 25 30D 30D 30N 30N MTC MTC Type: Typ: Offset: Zeitversatz: Sync receiving and sending Synchronisiere Empfangen und Senden Send and receive Jack transport Sende und empfange Jack Transport Send and receive Jack transport information, including stop, start and position. Sende und empfange Jack Transport Information, einschließlich Stopp, Start und Position. Use Jack transport Benutze Jack-Transport Make MusE the Jack transport Timebase Master Mache MusE zum Jack-Transport Timebase-Master Make MusE the Jack transport Timebase Master. Allows Jack to show time as MusE Bars, Beats, and Ticks. MusE will try to become master, but other Jack clients can also take over later. You can always click here again for Master. Mache MusE zum Jack-Transport Timebase-Master Erlaubt es Jack, die Zeit als MusE-Takte, Schäge und Ticks zu zeigen. MusE wird versuchen, Master zu werden, aber andere Jack-Clients können später übernehmen. Sie können immer hier klicken, um Master wieder zu aktivieren. Jack transport Timebase Master Jack Transport Timebase Master Control MusE timing by external midi clock or MTC sync Steuere MusE-Timing via externe Midi-Clock oder MTC sync When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). Wenn im Slave-Modus, ist das Tempo extern gesteuert. MusE kann zur Midi-Clock oder MTC quarter frame sync synchronisieren. Aktivierte Eingänge in der Liste werden Effekt haben (RMC, RMMC, RMTC). Slave to external sync Binde an externe sync Averaging applied to recorded external tempo changes. External midi clock can be very jittery. Tempo is derived from it and recorded. It is usually desirable to average it and limit the number of recorded changes. Tiny: 2 section 4/4 = 8 stages. 1/8T note averaging, may produce jitter. Small: 3 section 12/8/4 = 24 stages. 1/4 note averaging, may still produce jitter. Medium: 3 section 28/12/8 = 48 stages. 1/2 note averaging. Less jitter. Large: 4 section 48/48/48/48 = 192 stages. Use this if the song has only one tempo. Very low quantization values can be used. Large pre-detect: 4 section 8/48/48/48 = 152 stages + first stage large step pre-detector. Use this if you expect sudden large tempo steps. None: Use only if high accuracy is needed for audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. Tempo record averaging Tempoaufname-Averaging bpm bpm Tempo record quantization Tempoaufname-Quantisierung Send start to first clock delay Pause zwischen dem Senden von "Start" bis zum ersten Clock This button and other transport related buttons are found on the Transport Panel Diese Schaltfläche und Ähnliche befinden sich auf dem Transportpanel Allows 'slow sync' devices time to synchronize to MusE. This value is the delay from sending start to sending the first clock. Gibt "Slow-Sync"-Geräten Zeit, um sich mit MusE zu synchronisieren. Dieser Wert ist die Pause zwischen dem Senden von "Start" bis zum Senden des ersten Clocks. ms ms Send sync delay Pause für das Senden von Sync Note: Sync delay and MTC sync currently not fully implemented Hinweis: Sync delay und MTC sync sind momentan nicht komplett implementiert MidiTrackInfoBase MusE: TrackInfo MusE: Spurinfo output channel Ausgangskanal % % output port Ausgangsanschluss off aus Transp. Transp. Channel Info Kanalinfo Rec: Rec: Prog Prog Vol Vol Pan Pan Delay Delay Bank Select MSB. Ctrl-double-click on/off. Bank MSB. Ctrl+Doppelklick für an/aus. H-Bank H-Bank Compr. Kompr. Bank Select LSB. Ctrl-double-click on/off. Bank LSB. Ctrl+Doppelklick für an/aus. L-Bank L-Bank Velocity Velocity Length Länge all midi events are sent to this output channel alle MIDI-Events werden zu diesem Ausgangskanal gesendet Out ch Aus.Kanal input routing Eingangs-Signalfluss output routing Ausgangs-Signalfluss input detect Eingangserkennung Input detect indicator. Detects all note on-off, controller, aftertouch, program change, and pitchbend (but not sysex or realtime) events on the selected channels, on the selected midi ports. Eingangserkennungs-Anzeige. Erkennt alle Note an/aus, Controller, Aftertouch, Programmänderung und Pitchbend (aber nicht Sysex oder Echtzeit)- Ereignisse auf den ausgewählten Kanälen auf den gewählten MIDI-Ports. W W Midi thru MIDI thru Pass input events through ('thru') to output. Schleife Eingangsevents durch zum Ausgang. Select instrument patch Instrumentenpatch auswählen <unknown> <unbekannt> Add all settings to song Alle Einstellungen zum Lied hinzufügen All alle Program. Ctrl-double-click on/off. Programm. Ctrl+Doppelklick für an/aus. Volume. Ctrl-double-click on/off. Lautstärke. Ctrl+Doppelklick für an/aus. Change stereo position. Ctrl-double-click on/off. Stereo-Position. Ctrl+Doppelklick für an/aus. Add bank + prog settings to song Füge Bank und Prog Einstellungen zum Lied hinzu Add vol setting to song Füge Lautstärke Einstellungen zum Lied hinzu Add pan setting to song Füge Pan-Einstellung zum Song hinzu MidiTransformDialogBase MusE: Midi Transformator MusE: Midi Transformator &New &Neu &Delete &Löschen &Apply &Anwenden &OK &Bestätigen &Cancel Abb&rechen PresetList Vorlagenliste Processing Bearbeitung Event Type Ereignistyp Keep behalten Fix fest Note Note Poly Pressure Poly Andruckstärke Control Change Kontrollerwechsel Aftertouch Aftertouch Pitch Bend Pitch bend NRPN NRPN RPN RPN Plus plus Minus minus Multiply multiplizieren Divide dividieren Value 2 Wert 2 Invert umkehren ScaleMap Notenbelegung Flip umdrehen Dyn Dyn Random zufällig Value 1 Wert 1 Length Länge Position Zeitposition Randomize Zufallswerte Filter Filter All alle Equal gleich Unequal ungleich Ignore ignorieren Higher größer Lower kleiner Inside innerhalb Outside außerhalb Bar Range Taktbereich Preset Vorlage Name: Name: Comment: Kommentar: Range Bereich process all events Alle Ereignisse verarbeiten selected tracks Ausgewählte Spuren inside loop Innerhalb der Schleife Function Funktion Select Auswählen Quantize Quantisieren Delete Löschen Transform Transformieren Insert Einfügen Copy Kopieren Extract Entnehmen Quantize Value Wert quantisieren MixdownFileDialogBase MusE: Set Mixdown Wavefile MusE: Wave Datei Abmischung einstellen &OK &Bestätigen &Cancel Abb&rechen File Path Dateipfad Channel Kanal Stereo Stereo Mono Mono 5.1 5.1 wav,16 Bit wav, 16 Bit wav, 24 Bit wav, 24 Bit wav, 32 Bit (float) wav, 32 Bit (float) Format Format MoveBase MusE: Move Notes MusE: Verschiebe Noten Range Bereich All Events Alle Ereignisse Selected Events Ausgewählte Ereignisse Looped Events Ereignisse innerhalb Schleife Selected Looped Ausgewählt und innerhalb der Schleife Parts Parts Selected parts or c&urrent part Aus&gewählte Parts oder aktueller Part All parts &in editor &Alle Parts im Editor Value Wert Move by Bewege um ticks Ticks OK Bestätigen Cancel Abbrechen MusECore::AudioPreviewDialog Stop Stopp Play Wiedergabe MusECore::RasterizerModel Off Aus Bar Takt MusECore::Scripts Do you want to process ALL or only selected events? Alle oder nur ausgewählte Events bearbeiten? &Selected Aus&gewählt &All &Alle MusE - external script failed MusE - externes Skript fehlgeschlagen MusE was unable to launch the script, error message: %1 MusE konnte das Skript nicht starten, Fehlermeldung: %1 Re-read script names from disc Skriptnamen vom Disk einlesen MusECore::Song Jack shutdown! Jack heruntergefahren! Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: - a performance issue with your particular setup. - a bug in MusE (or possibly in another connected software). - a random hiccup which might never occur again. - jack was voluntary stopped by you or someone else - jack crashed If there is a persisting problem you are much welcome to discuss it on the MusE mailinglist. (there is information about joining the mailinglist on the MusE homepage which is available through the help menu) To proceed check the status of Jack and try to restart it and then . click on the Restart button. Jack hat ein Geschwindigkeitsproblem festgestellt, welches dazu geführt hat, dass MusE von Jack getrennt wurde. Dies kann aus verschiedenen Gründen passieren: - ein Geschwindigkeitsproblem in Ihrer Einrichtung - ein Fehler in MusE oder einer anderen verbundenen Software - ein zufälliges Verschlucken, das nie wieder passieren könnte - Jack wurde absichtlich gestoppt - Jack ist abgestürzt Wenn das Problem erneut auftritt, sind Sie sehr willkommen, in der MusE Mailingliste eine Frage zu stellen. (Informationen darüber, wie man der Mailingliste beitritt finden sich auf der MusE-Homepage, die über das Hilfe-Menü erreichbar ist) Zum Fortsetzen überprüfen Sie bitten den Zustand von Jack, versuchen, Jack neu zu starten und klicken dann auf den "Neustart"- Knopf. Automation: Automatisierung: previous event vorheriges Ereignis next event nächstes Ereignis set event setze Ereignis add event füge Ereignis hinzu erase event lösche Ereignis erase range lösche Bereich clear automation lösche Automatisierung Automation Automatisierung Previous event Vorheriges Ereignis Next event Nächstes Ereignis Set event Setze Ereignis Add event Füge Ereignis hinzu Erase event Lösche Ereignis Erase range Lösche Bereich Clear automation Lösche Automatisierung Midi control MIDI-Steuerung Assign Zuweisen Clear Löschen Clear all controller events? Alle Controller-Ereignisse löschen? &Ok &Bestätigen &Cancel &Abbrechen Controller Kontroller Controller: Controller: Bypass Übergehen MusE: Tempo list MusE: Tempo-Liste External tempo changes were recorded. Transfer them to master tempo list? Es wurden externe Tempoänderungen aufgezeichnet. Sollen sie in die Master-Tempoliste übernommen werden? Do you want to process ALL or only selected events? Alle oder nur ausgewählte Events bearbeiten? &Selected Aus&gewählt &All &Alle MusE - external script failed MusE - externes Skript fehlgeschlagen MusE was unable to launch the script, error message: %1 MusE konnte das Skript nicht starten, Fehlermeldung: %1 Und&o &Rückgängig Re&do &Wiederholen MusEGui select Pointer Tool: with the pointer tool you can: select parts move parts copy parts Werkzeug "Zeiger": Dieses Werkzeug kann: Parts auswählen Parts verschieben Parts kopieren select Pencil Tool: with the pencil tool you can: create new parts modify length of parts Werkzeug "Zeichenstift": Dieses Werkzeug kann: Neue Parts erzeugen Die Länge einzelner Parts ändern select Delete Tool: with the delete tool you can delete parts Werkzeug "Radierer": mit diesem Werkzeug werden Parts gelöscht select Cut Tool: with the cut tool you can split a part Werkzeug "Schnitt": mit diesem Werkzeug werden Parts geteilt select Glue Tool: with the glue tool you can glue two parts Werkzeug "Verbinder": mit diesem Werkzeug werden zwei einzelne Parts zu einem verbunden select Score Tool: Werkzeug "Notensatz": select Quantize Tool: insert display quantize event Werkzeug "Quantisierung": mit diesem Werkzeug werden quantisierte Ereignisse eingefügt select Drawing Tool Werkzeug "Zeichenstift" select Muting Tool: click on part to mute/unmute Werkzeug "Stille": Klicken Sie auf einen Part um diesen stumm bzw. laut zu schalten Manipulate automation Automatisierung manipulieren Cursor tool Zeiger-Werkzeug pointer Zeiger pencil Zeichenstift eraser Radierer cutter Schnitt score Notensatz glue Verbinder quantize Quantisierung draw Zeichenstift mute parts Schalte Parts stumm edit automation Automatisierung bearbeiten cursor Zeiger MusEGui::Appearance Main application font, and default font for any controls not defined here. Hauptschriftart, und Standard für alles, was hier nicht definiert ist. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Mixer-Strips und Effekteinschübe. Midispur-Info-Panel. MIDI-Control-Panel Eintrag Box. Transport controls. Transport-Bedienelemente. Time scale upper, and time signature. Controller graph and S/X buttons. Zeitskala oben und Taktsignatur. Controllergraph und S/X-Knöpfe. Time scale lower, and arranger part name overlay. Zeitskala unten und Arranger: Partnamen-Overlay. Main application font, and default font for any controls not defined here. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Arranger part name overlay. Time scale lower. Tempo scale, and markers. Temposkala und Marker. Mixer labels. Auto-font-sizing up to chosen font size. Word-breaking but only with spaces. Mixer-Beschriftungen. Schriftgröße wird automatisch nach oben zur gewählten Größe hin angepasst. Zeilenumbruch aber nur bei Leerzeichen. Maximum mixer label auto-font-sizing font size. Maximale Mixer-Schriftgröße. Global opacity (opposite of transparency). Globale Durchsichtigkeit. Standard Standard Custom Benutzerdefiniert Open custom style sheet Custom-Stylesheet öffnen Remove custom style sheet Custom-Stylesheet entfernen Keep Qt system style Beim Qt-Systemstil belassen Do you really want to reset colors to theme default? Farben wirklich auf Theme-Voreinstellung zurücksetzen? Style was changed. The program must be restarted for changes to take place. Restart now? Stil wurde geändert. Programm muß neu gestartet werden. Jetzt neu starten? Revert changes Änderungen zurücknehmen Revert all... Alles zurücknehmen... Do you really want to reset all colors? Wirklich alle Farben zurücksetzen? &Ok &Bestätigen MusE: load image MusE: Bild laden No current color item Keine aktuelle Farbe Select Color: %1 Farbe Auswählen: %1 Select style sheet Formatvorlage auswählen Qt style sheets (*.qss) QT-Formatvorlagen (*.qss) MusEGui::Arranger Enable Recording Aufnahme einschalten Mute/Off Indicator Mute/Off Anzeige Solo Indicator Solo-Anzeige Track Type Spurtyp Track Name Spurname Midi output channel number or audio channels Midi-Ausgangskanalnummer oder Audiokanäle Midi output port or synth midi port Midi-Ausgangsport oder Synth-Midi-Port Time Lock Sperre "Zeit" Automation parameter selection Automatisierungsparameterauswahl Notation clef Notenschlüssel Enable recording. Click to toggle. Aufnahme einschalten. Klicken zum umschalten. Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. On/Off is not! Mute-Anzeige. Klicken zum umschalten. Rechtsklick um Track an/aus umzuschalten. "Mute" ist für schnelles, wiederholtes Umschalten. An/aus nicht! Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed, indicated by a dark square. Solo-Anzeige. Klicken zum umschalten. Verbundene Spuren sind auch "Phantom-"Solo-geschalten, angezeigt durch ein dunkles Quadrat. Track type. Right-click to change midi and drum track types. Spurtyp. Rechtsklick, um Midi und Schlagzeug Spurtypen umzuschalten. Track name. Double-click to edit. Right-click for more options. Spurname. Doppelklick zum bearbeiten. Rechtsklick für mehr Optionen. Midi/drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi-/Schlagzeugspur: Ausgangskanalnummer. Audiospur: Kanäle. Mittel/Rechtsklick zum Ändern. Midi/drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Midi/Schlagzeugspur: Ausgangsport. Synth-Spur: Zugewiesener MIDI-Port. Linksklick zum ändern. Rechtsklick, um die GUI zu zeigen. Time lock Zeitsperre Notation clef. Select this tracks notation clef. Notenschlüssel. Wählt den Schlüssel dieser Spur aus. Track index. Click to select track, CTRL+LMB to add/remove, SHIFT+LMB for range select, dblclick to select all of the same type. Enable/Disable input monitor. Passes input through to output (see also Settings: Automatically monitor on record arm). Enable/Disable recording. Mute indicator. Left-click to mute track (for quick action), right-click to toggle track on/off (for more permanent action). Track type. Click to switch between midi and drum track types. Track name. Double-click to edit. Right-click for context menu. Midi/Drum: Output channel number. Audio: Channels. Mid/right-click to change. Midi/Drum: Output port. Synth: Assigned midi port. Left-click to change, right-click to show synth GUI. Notation clef. Click to select this tracks notation clef. Arranger Arrangierer Cursor Zeiger Cursor position Cursorposition Bars Takte Off Aus Bar Takt Track index Track Index Enable input monitor Input-Monitor aktivieren Enable recording Aufnahme einschalten Mute/Off indicator Mute/Off Anzeige Solo indicator Solo-Anzeige Track type Spurtyp Track name Spurname Enable input monitor. Click to toggle. Passes input through to output for monitoring. See also Settings: Automatically Monitor On Record Arm. Enable recording. Click to toggle. See also Settings: Automatically Monitor On Record Arm. Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed. Midi/Drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi/Drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Solo indicator. Connected tracks are also 'phantom' soloed. Snap Magnet Len Länge Show mixer strip for current track Mixerstrip für aktuelle Spur anzeigen Hide mixer strip for current track Mixerstrip für aktuelle Spur ausblenden song length - bars Liedlänge - Takte Type Typ NO Kein GM GM GS GS XG XG midi song type Midi Lied Typ Pitch Tonhöhe midi pitch Midi Tonhöhenänderung global midi pitch shift Globale Midi Tonhöhenänderung Tempo Tempo midi tempo Midi Tempo Song length - bars Liedlänge - Takte Midi pitch Midi Tonhöhe Global midi pitch shift Globale Midi-Tonhöhenänderung Midi tempo Midi Tempo N N TrackInfo Spurinfo Display mixer strip for current track Mixerstrip für aktuellen Track anzeigen Alt Alt # # I Input monitor E R Record R M Mute M S Solo S C Class (track type) C Track Spur Port Port Ch Channel K T Time lock T Automation Automatisierung Clef Schlüssel MusEGui::ArrangerColumns Control7 Controller7 Control14 Controller14 RPN RPN NPRN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Tonhöhe Program Programm Aftertouch Aftertouch MusEGui::ArrangerView MusE: Arranger MusE: Arrangierer Arranger Arrangierer D&elete &Löschen C&ut &Ausschneiden &Copy &Kopieren Copy in range Im Bereich kopieren &Paste &Einfügen Paste (show dialog) Einfügen (Dialog zeigen) Paste c&lone K&lon Einfügen Paste clone (show dialog) Klon einfügen (Dialog zeigen) &Insert Empty Measure Leeren &Takt einfügen Delete Selected Tracks Ausgewählte Spuren löschen Duplicate Selected Tracks Ausgewählte Spuren duplizieren Shrink selected parts Ausgewählte Parts schrumpfen Expand selected parts Ausgewählte Parts erweitern Clean selected parts Ausgewählte Parts säubern Copy in Range Im Bereich kopieren Paste C&lone K&lon Einfügen Paste to Selected &Track In &ausgewählte Spur einfügen Paste Clone to Selected Trac&k &Klon in ausgewählte Spur einfügen Paste (Show Dialo&g)... Einfügen (Dialog &anzeigen)... Shrink Selected Parts Ausgewählte Parts schrumpfen Expand Selected Parts Ausgewählte Parts erweitern Purge Hidden Events from Selected Parts Versteckte Events aus selektierten Parts löschen Add Track Spur hinzufügen Insert Track Spur einfügen Select Auswählen Select &All Alles a&uswählen &Deselect All Alles ab&wählen Invert &Selection Auswa&hl umkehren &Inside Loop Bereich &innerhalb der Schleife &Outside Loop Bereich außerhalb der Schlei&fe All &Parts on Track Alle &Parts der Spur Score Partitur All Tracks in One Staff Alle Spuren in einem System One Staff per Track Ein System pro Spur Event List... Eventliste Nothing to edit Es gibt nichts zu editieren All Tracks in One Staff... Alle Spuren in einem System... One Staff per Track... Ein System pro Spur... New Score Window... Neues Partiturfenster... Pianoroll... Pianoroll... Drums... Schlagzeug... List... Liste... Wave... Wave... Open in New Window Im neuen Fenster öffnen Always open the part(s) in a new editor window Open Synth Plugin GUI... Synth-Plugin GUI öffnen... Midi &Transform... Midi &transformieren... Global Cut - Selected Tracks Globaler Schnitt - markierte Spuren Global Insert - Selected Tracks Globales Einfügen - markierte Spuren Global Split - Selected Tracks Globales Aufteilen - markierte Spuren Change Note &Length Noten&länge verändern Change Note &Velocity &Anschlagsdynamik verändern &Scripts &Skripte Scripts for midi processing. Applied to selected midi parts (or else tracks). User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. &Display &Anzeige Display menu: View-specific display options. Toggle &Mixer Strip Mixerstrip umschalten Configure &Custom Columns... Benutzerdefinierte &Spalten konfigurieren... New... Neu... all tracks in one staff alle Spuren in ein System one staff per track ein System pro Spur New score window Neues Partiturfenster Pianoroll Pianorollen Editor Drums Schlagzeug Editor List Listen Editor Wave Wave-Editor Mastertrack Masterspur Graphic Graphischer Editor Midi &Transform Midi &transformieren Global Cut Globaler Schnitt Global Insert Globales Einfügen Global Split Globales Aufteilen Global Cut - selected tracks Globaler Schnitt - markierte Spuren Global Insert - selected tracks Globales Einfügen - markierte Spuren Global Split - selected tracks Globales Aufteilen - markierte Spuren &Edit &Bearbeiten Fu&nctions &Funktionen &Structure &Struktur Functions Funktionen &Quantize Notes Noten &quantisieren Change note &length Noten&länge verändern Change note &velocity &Anschlagsdynamik verändern Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transponieren Erase Events (Not Parts) Events (nicht Parts) löschen Move Events (Not Parts) Events (nicht Parts) verschieben Set Fixed Note Length Feste Notenlänge setzen Delete Overlapping Notes Überlappende Noten entfernen Legato Legato Window &Config Fenster&konfiguration Configure &custom columns Benutzerdefinierte &Spalten konfigurieren Remove track(s) Spur(en) entfernen Are you sure you want to remove this track(s)? Möchten Sie wirklich diese Spur entfernen? New Neu Changed Settings Einstellungen verändert Unfortunately, the changed arranger column settings cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. (we'll try to fix that) Leider können die veränderten Arranger-Spalten nicht übernommen werden, während MusE noch läuft. Um Sie zu übernehmen, starten Sie MusE bitte neu. (Entschuldigung, wir arbeiten daran) MusEGui::AudioComponentRack Aux send level (dB) Vol Vol Pan Pan Mute Volume/gain Lautstärke/Verstärkung Panorama/Balance Gain Calibration gain MusEGui::AudioMixerApp &Create &Erzeugen &View An&zeige Strips Strips Routing Signalfluss Show Midi Tracks Zeige Midi-Spuren Show Drum Tracks Zeige Schlagzeug-Spuren Show New Style Drum Tracks Zeige Schlagzeug-Spuren (neuer Stil) Show Wave Tracks Zeige Wave-Spuren Show Inputs Zeige Eingänge Show Outputs Zeige Ausgänge Show Groups Zeige Gruppen Show Auxs Zeige Aux Show Synthesizers Zeige Synthesizer Traditional order Klassische Reihenfolge Arranger order Arranger-Reihenfolge User order Benutzer-Reihenfolge Show all hidden strips Ausgeblendete Strips anzeigen Unhide strip: Ausgeblendeten Strip anzeigen: (no hidden strips) (keine ausgeblendeten Strips) MusEGui::AudioStrip panorama Panorama aux send level Aux Send Pegel Pan Pan 1/2 channel 1/2 Kanäle Pre Pre pre fader - post fader Vor Regler - nach Regler dB dB record Aufnahme mute stumm record downmix Abmischung aufnehmen solo mode Solo Modus off Aus input routing Eingangs-Signalfluss output routing Ausgangs-Signalfluss Pre Fader Listening (PFL) Volume/gain Lautstärke/Verstärkung Record downmix Downmix aufnehmen Record arm Aufnahme scharfschalten Mute or proxy mute Stummschalten oder Proxy-Stummschalten Solo or proxy solo Solo oder Proxe-Solo Track off Track aus Input monitor Input Monitor Input routing. Press F1 for help. Output routing. Press F1 for help. Volume/Gain Pass input through to output Schleife Eingangsevents durch zum Ausgang Off Aus Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Auto off Read Lesen Touch Berühren Write Schreiben Automation type Automatisierungstyp Automation type: Off, Read, Touch or Write. Press F1 for help. L meter peak/clip R meter peak/clip automation type Automatisierungstyp MusEGui::BigTime format display Formatanzeige bar Takt beat Schlag tick Tick minute Minute second Sekunde frame Frame subframe Subframe Format display Formatanzeige Bar Takt Beat Schlag Tick Tick Minute Minute Second Sekunde Frame Frame Subframe Subframe MusE: Bigtime MusE: Große Zeitanzeige MusEGui::Canvas Tools: Werkzeuge: Tools Werkzeuge MusEGui::ClipListEdit MusE: Clip List Editor MusE: Clip-Listen-Editor &Display &Anzeige Window &Config Fenster&konfiguration MusEGui::ComboQuant Off Aus MusEGui::CompactKnob off aus MusEGui::CompactPatchEdit Patch name Patchname MusEGui::CompactSlider off aus MusEGui::CpuStatusBar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) CPU load averaged over each GUI update period. DSP load read from JACK. Number of xruns. Double-click to reset. MusEGui::CpuToolbar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) MusEGui::CtrlCanvas Drawing hint: Hold Ctrl to affect only existing events Hinweis: Halten Sie STRG, um nur existierende Events zu beeinflussen Use pencil or line tool to draw new events Benutzen Sie Stift- oder Linienwerkzeug, um neue Events zu zeichnen Control canvas: Use Pencil tool to edit events and Draw tool to adjust them gradually. Hold Ctrl to affect only existing events. Merge options Merge-Optionen Erase target Ziel löschen Erase target events between source events Zielevents zwischen Quellevents löschen Erase target WYSIWYG Ziel löschen (WYSIWYG) Include last source item width when erasing Breite des letzten Quellevents beim Löschen einbeziehen Erase target inclusive Ziel löschen inklusiv Include entire source range when erasing Ganzen Quellbereich beim Löschen einbeziehen Merge actions Merge-Aktionen Merge Merge Merge the dragged items Gezogene Items mergen Merge a copy Kopie mergen Merge a copy of the dragged items Kopie der gezogenen Items mergen Cancel drag Drag abbrechen Cancel dragging the items Item-Drag abbrechen Use shift + pencil or line tool to draw new events Benutzen Sie SHIFT+Stift- oder Linienwerkzeug, um neue Events zu zeichnen MusEGui::CtrlPanel S Select controller S Select controller Controller wählen Remove panel Panel entfernen Manual adjust (Ctrl-double-click on/off) Manuelle Anpassung (Ctrl-Doppelklick ein/aus) All/Per-note velocity mode Gesamt/Pro-Note Velocity Modus select controller Controller wählen X Remove panel (destroy button) X remove panel Kontrolleransicht schließen manual adjust Manuelles Einstellen ctrl-double-click on/off Ctrl+Doppelklick für an/aus off aus Velocity Anschlagsdynamik add new ... Neuen Kontroller hinzufügen ... Instrument-defined Instrumenten-definiert Add ... Hinzufügen ... Others Andere Edit instrument ... Instrument bearbeiten ... Common Controls Gemeinsame Controller MusEGui::DList Hide this instrument Instrument ausblenden This turns a blue eye into a crossed eye Show this instrument Instrument anzeigen This turns a crossed eye into a blue eye Set field Feld setzen Sets a field Set row Reihe setzen Sets a row Set column Spalte setzen Sets a whole column to the field Set list Liste setzen Sets the whole list Reset field Feld zurücksetzen Resets a field in a row to default patch or instrument value Reset row Reihe zurücksetzen Resets a row to the instrument values Reset column Spalte zurücksetzen Resets a whole column to the instrument values Reset list Liste zurücksetzen Resets the whole list to the instrument values Reset track's drum list Resets all lists on all patches to the instrument values Promote field to default patch Promotes a field in a row to the default patch Promote row to default patch Promotes a row to the default patch Promote column to default patch Promotes a column to the default patch Promote list to default patch Promotes the whole list to the default patch Reset instrument field Instrumentfeld zurücksetzen Reset instrument row Instrumentreihe zurücksetzen Reset instrument list Instrumentliste zurücksetzen Set instrument field Instrumentfeld setzen Set instrument row Instrumentreihe setzen Set instrument list Instrumentliste setzen Drum list: Edit instrument-specific settings. MusEGui::DrumCanvas Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help. Moving items failed Verzeichnis erzeugen schlug fehl The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. Creating event failed Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Recording event failed Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Internal error Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. Drum map Schlagzeugbelegung Reset the track's drum map with instrument defaults? Die Schlagzeugbelegung auf Standardwerte zurücksetzen? Note: Velocity: Start (bar): Start (time): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete LMB: Delete Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete. MusEGui::DrumEdit mute instrument Instrument stummschalten sound name Klangname volume percent Lautstärke Prozent quantisation Quantisierung this input note triggers the sound Diese Eingangsnote löst den Klang aus note length Notenlänge this is the note which is played Das ist die Note, die gespielt wird output channel (hold ctl to affect all rows) Ausgangskanal (halten Sie CTRL, um alle Reihen zu beeinflussen) output port (hold ctl to affect all rows) Ausgangsport (halten Sie CTRL, um alle Reihen zu beeinflussen) shift + control key: draw velocity level 1 Shift + Ctrl: Zeichne Dynamiklevel 1 control key: draw velocity level 2 Ctrl: Zeichne Dynamiklevel 2 shift key: draw velocity level 3 Shift: Zeichne Dynamiklevel 3 draw velocity level 4 Zeichne Dynamiklevel 4 output channel (ctl: affect all rows) Ausgangskanal (CTRL, um alle Reihen zu beeinflussen) output port (ctl: affect all rows) Ausgangskanal (CTRL, um alle Reihen zu beeinflussen) &File &Datei Load Map Schlagzeugbelegung laden Save Map Schlagzeugbelegung Sichern Reset GM Map Auf GM-Belegung zurücksetzen &Edit B&earbeiten Hide instrument Instrument ausblenden Mute instrument Instrument stummschalten Sound name Klangname Volume percent Lautstärke Prozent Quantisation Quantisierung This input note triggers the sound Diese Eingangsnote löst den Klang aus Note length Notenlänge This is the note which is played Das ist die Note, die gespielt wird Override track output channel (hold ctl to affect all rows) Ausgangskanal übersteuern (halten Sie CTRL, um alle Reihen zu beeinflussen) Override track output port (hold ctl to affect all rows) Ausgangsport übersteuern (halten Sie CTRL, um alle Reihen zu beeinflussen) Control + meta keys: Draw velocity level 1 Control + Metakeys: Zeichne Dynamiklevel 1 Meta key: Draw velocity level 2 Metakey: Zeichne Dynamiklevel 2 Draw default velocity level 3 Zeichne Dynamiklevel 3 Meta + alt keys: Draw velocity level 4 Meta + Alt: Zeichne Dynamiklevel 4 Note which triggers the sound Note which is played Override track output channel (hold CTRL to affect all rows). Override track output port (hold CTRL to affect all rows). CTRL + META keys: Draw velocity level 1 META key: Draw velocity level 2 META + ALT keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Ausgangskanal übersteuern (halten Sie CTRL, um alle Reihen zu beeinflussen) Override track output port (ctl: affect all rows) Ausgangskanal übersteuern (halten Sie CTRL, um alle Reihen zu beeinflussen) Cut Ausschneiden Copy Kopieren Copy events in range Kopiere Events im Bereich Paste Einfügen Paste to current part In aktuellen Part einfügen Paste (with Dialog) Einfügen (Dialog zeigen) Delete Events Events entfernen &Select &Auswählen Select All Alles auswählen Select None Nichts auswählen Invert Auswahl umkehren Inside Loop Innerhalb der Schleife Outside Loop Ausserhalb der Schleife Previous Part Vorheriger Part Next Part Nächster Part Event List... Eventliste... Fu&nctions Fu&nktionen &Scripts &Skripte Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Cursor step Cursor-Schritt Load drummap Schlagzeugbelegung laden Store drummap Schlagzeugbelegung speichern Cursor tools Cursor-Werkzeuge Add controller view Controlleransicht hinzufügen Re-order list Liste neu sortieren Set Fixed Length Länge festlegen Modify Velocity Velocity ändern Crescendo/Decrescendo Crescendo/Decrescendo Quantize Quantisieren Erase Event Events löschen Move Notes Noten verschieben Delete Overlaps Überlappungen entfernen &Plugins &Plugins Window &Config Fenster&konfiguration Drum tools Schlagzeug-Werkzeuge Load Drummap Schlagzeugbelegung laden Re-order map Belegung neu sortieren &Display &Anzeige Display menu: View-specific display options. Group Gruppieren Don't group Nicht gruppieren Group by channel Nach Kanal gruppieren Group maximally Maximal gruppieren Show/Hide Anzeigen/Ausblenden Also show hidden instruments Auch ausgeblendete Instrumente anzeigen Show all instruments Alle Instrumente anzeigen Hide all instruments Alle Instrumente ausblenden Only show used instruments Nur verwendete Instrumente anzeigen Only show instruments with non-empty name or used instruments Nur Instrumente mit Namen oder Verwendung anzeigen Step record Step-Aufnahme Midi input Midi Eingabe Play events Ereignisse abspielen Drum map tools Schlagzeug-Werkzeuge Store Drummap Schlagzeugbelegung sichern Step Record Aufnahme taktschlagweise Midi Input Midi Eingang Play Events Ereignisse abspielen cursor tools Cursor-Werkzeug Cursor step: Cursor-Schritt: Set step size for cursor edit Schrittweite für Cursor-Modus einstellen ctrl ctrl Add Controller View Controlleransicht hinzufügen H hide H M mute M Sound Klang Vol Vol QNT QNT E-Note E-Note Len Länge A-Note A-Note Ch K Port Port LV1 LV1 LV2 LV2 LV3 LV3 LV4 LV4 Muse: Load Drum Map MusE: Schlagzeugbelegung laden MusE: Store Drum Map MusE: Schlagzeugbelegung speichern Drum map Schlagzeugbelegung Reset the drum map with GM defaults? Die Schlagzeugbelegung auf die GM-Standardwerte zurücksetzen? Not all parts are displayed Nicht alle Parts werden angezeigt MusEGui::EditCAfterDialog MusE: Enter Channel Aftertouch MusE: Channel Aftertouch festlegen Time Position Position Pressure Anschlagsdruck MusEGui::EditEventDialog Ok Bestätigen Cancel Abbrechen MusEGui::EditInstrument Use note offs Note-off verwenden No note offs Keine Note-off verwenden Convert to 0-vel note ons Control7 Controller7 Control14 RPN RPN NPRN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Tonhöhe Program Programm PolyAftertouch Aftertouch Aftertouch H hide H M mute M Name Name Vol Vol Quant E-Note E-Note Len Länge A-Note A-Note Ch K Port LV1 LV1 LV2 LV2 LV3 LV3 LV4 LV4 Tick Tick Data Daten Hide instrument Instrument ausblenden Mute instrument Instrument stummschalten Sound name Klangname Volume percent Lautstärke Prozent Quantisation Quantisierung This input note triggers the sound Diese Eingangsnote löst den Klang aus Note length Notenlänge This is the note which is played Das ist die Note, die gespielt wird Override track output channel (hold ctl to affect all rows) Ausgangskanal übersteuern (halten Sie CTRL, um alle Reihen zu beeinflussen) Override track output port (hold ctl to affect all rows) Ausgangsport übersteuern (halten Sie CTRL, um alle Reihen zu beeinflussen) Control + meta keys: Draw velocity level 1 Control + Metakeys: Zeichne Dynamiklevel 1 Meta key: Draw velocity level 2 Metakey: Zeichne Dynamiklevel 2 Draw default velocity level 3 Zeichne Dynamiklevel 3 Meta + alt keys: Draw velocity level 4 Meta + Alt: Zeichne Dynamiklevel 4 Override track output channel (ctl: affect all rows) Ausgangskanal übersteuern (halten Sie CTRL, um alle Reihen zu beeinflussen) Override track output port (ctl: affect all rows) Ausgangskanal übersteuern (halten Sie CTRL, um alle Reihen zu beeinflussen) MusE: Create file failed MusE: Erzeugen der Datei schlug fehl MusE: Write File failed MusE: Datei schreiben schlug fehl MusE: Save Instrument Definition MusE: Instrumentendefinition speichern Instrument Definition (*.idf) Instrumentendefinition (*.idf) MusE: Save instrument as MusE: Instrument speichern unter Enter a new unique instrument name: Geben Sie einen neuen einzigartigen Instrumentennamen ein: The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? Das Benutzerinstrument "%1" existiert bereits. Dies wird seine .idf Instrumentendatei überschreiben. Sind Sie sicher? MusE: Bad instrument name MusE: Ungeeigneter Instrumentenname Please choose a unique instrument name. (The name might be used by a hidden instrument.) Bitte wählen Sie einen einzigartigen Instrumentennamen. (Der Name darf von einem versteckten Instrument benutzt werden.) MusE: Bad patch name MusE: Ungeeigneter Patch-Name Please choose a unique patch name Bitte eindeutigen Patchnamen wählen MusE: Bad patchgroup name MusE: Ungeeigneter Patchgruppen-Name Please choose a unique patchgroup name Bitte eindeutigen Patchgruppennamen wählen MusE: Bad controller name MusE: Ungeeigneter Controllername Please choose a unique controller name Bitte eindeutigen Controllernamen wählen New controller: Error Error! All control numbers are taken up! Clean up the instrument! MusE: Cannot add common controller MusE: Kann gemeinsamen Controller nicht hinzufügen A controller named '%1' already exists. Ein Controller namens "%1" existiert bereits. A controller number %1 already exists. Die Controllernummer %1 existiert bereits. MusE MusE The current Instrument contains unsaved data Save Current Instrument? Das momentane Instrument enthält ungespeicherte Daten. Instrument speichern? &Save &Speichern &Nosave &Nicht speichern &Abort &Abbrechen MusEGui::EditMetaDialog MusE: Enter Meta Event MusE: Meta-Event eingeben Time Position Position Meta Type Meta Typ Enter Hex Hex eingeben MusEGui::EditPAfterDialog MusE: Enter Poly Aftertouch MusE: Poly Aftertouch eingeben Time Position Position Pitch Tonhöhe Pressure Anschlagsdruck MusEGui::EditToolBar select Pointer Tool: with the pointer tool you can: select parts move parts copy parts Werkzeug "Zeiger": Dieses Werkzeug kann: Parts auswählen Parts verschieben Parts kopieren select Pencil Tool: with the pencil tool you can: create new parts modify length of parts Werkzeug "Zeichenstift": Dieses Werkzeug kann: Neue Parts erzeugen Die Länge einzelner Parts ändern select Delete Tool: with the delete tool you can delete parts Werkzeug "Radierer": mit diesem Werkzeug werden Parts gelöscht select Cut Tool: with the cut tool you can split a part Werkzeug "Schnitt": mit diesem Werkzeug werden Parts geteilt select Glue Tool: with the glue tool you can glue two parts Werkzeug "Verbinder": mit diesem Werkzeug werden zwei einzelne Parts zu einem verbunden select Score Tool: Werkzeug "Notensatz": select Quantize Tool: insert display quantize event Werkzeug "Quantisierung": mit diesem Werkzeug werden quantisierte Ereignisse eingefügt select Drawing Tool Werkzeug "Zeichenstift" select Muting Tool: click on part to mute/unmute Werkzeug "Stille": Klicken Sie auf einen Part um diesen stumm bzw. laut zu schalten Select Pointer tool: With the pointer tool you can: select parts move parts copy parts Select Pencil tool: With the pencil tool you can: create new parts modify length of parts Select Delete tool: With the delete tool you can delete parts Select Cut tool: With the cut tool you can split a part Select Glue tool: With the glue tool you can glue two parts Select Score tool: Select Quantize tool: Insert display quantize event Select Drawing tool Select Muting tool: Click on part to mute/unmute Manipulate automation Automatisierung manipulieren Select Cursor (tracker mode) tool: With the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Select Range tool Select Panning tool Select Zoom tool Select Stretch tool Select Sample rate tool Pointer Zeiger Pencil Zeichenstift Eraser Radierer Cutter Schnitt Glue Verbinder Range Bereich Pan Pan Zoom Zoom Score Partitur Quantize Quantisieren Draw Zeichenstift Mute parts Parts stummschalten Edit automation Automatisierung bearbeiten Cursor Cursor Stretch Strecken Sample rate Sampling-Rate Cursor tool Zeiger-Werkzeug pointer Zeiger pencil Zeichenstift eraser Radierer cutter Schnitt score Notensatz glue Verbinder quantize Quantisierung draw Zeichenstift mute parts Schalte Parts stumm edit automation Automatisierung bearbeiten cursor Zeiger Edit Tools Werkzeuge bearbeiten MusEGui::EffectRack effect rack Effekteinschub new Neu change ändern move up Nach oben bewegen move down Nach unten bewegen remove entfernen bypass übergehen show gui GUI anzeigen show native gui Native GUI anzeigen save preset Preset speichern Effect rack Effekteinschub Effect rack: Double-click a slot to insert/edit effect. RMB to open context menu. Press F1 for help. Effect rack Double-click a slot to insert FX New Neu Change Ändern Move up Nach oben bewegen Move down Nach unten bewegen Remove Entfernen Bypass Übergehen Show gui GUI anzeigen Show native gui Native GUI anzeigen Save preset Preset speichern Presets Presets MusE: Save Preset MusE: Preset speichern Replace effect Effekt ersetzen Do you really want to replace the effect %1? Möchten Sie wirklich den Effekt %1 ersetzen? MusEGui::GlobalSettingsConfig MusE: Choose start template or song MusE: Lied oder eine Vorlage für den Start wählen MusEGui::Header Track Info Columns Spur-Info Spalten Custom column MusEGui::LCDPatchEdit High bank: Low bank: Program (Ctrl-double-click on/off) off aus High bank Low bank Program Programm MusEGui::LMaster MusE: Mastertrack MusE: Masterspur &Edit &Bearbeiten Insert Tempo Tempo einfügen Insert Signature Taktmaß einfügen Insert Key Tonart einfügen Edit Positon Position bearbeiten Edit Value Wert bearbeiten Delete Event Event löschen Window &Config Fenster&konfiguration Edit tools Tempo Tempo Timesig Taktmaß Key Tonart new tempo Neues Tempo new signature Neues Taktmaß new key Neue Tonart Edit Position Position bearbeiten &Display &Anzeige New tempo Neues Tempo New signature Neues Taktmaß New key Neue Tonart Meter Taktposition Time Zeit Type Typ Value Wert Signature Taktmaß Position Zeitposition Delete Löschen Insert tempo change Insert time signature change Insert key change Edit position Edit value Delete event Reposition of the initial tempo and signature events is not allowed Das Verschieben der initialen Tempo- und Taktmaß-Events ist nicht erlaubt MusE: List Editor MusE: Listeneditor Input error, conversion not OK or value out of range Eingabefehler, Umwandlung nicht in Ordnung oder Wert außerhalb des zulässigen Bereichs Reposition of tempo and signature events to start position is not allowed! Das Verschieben von Tempo- und Taktmaß-Events an die Startposition ist nicht erlaubt! MusEGui::ListEdit insert Note Note einfügen insert SysEx SysEx einfügen insert Ctrl Ctrl einfügen insert Meta Meta einfügen insert Channel Aftertouch Channel Aftertouch einfügen insert Poly Aftertouch Poly Aftertouch einfügen &Edit B&earbeiten Cut Ausschneiden Copy Kopieren Paste Einfügen Delete Events Events löschen Increase Tick Tick erhöhen Decrease Tick Tick verringern Window &Config Fenster&konfiguration Insert Note Note einfügen Insert SysEx SysEx einfügen Insert Ctrl Ctrl einfügen Insert Meta Meta einfügen &Display &Anzeige Note Note SysEx Ctrl Meta Tick+ Tick- Delete Löschen Insert tools Werkzeuge "Einfügen" Tick Tick Bar Takt Type Typ Ch K Val A Wert A Val B Wert B Val C Wert C Len Länge Comment Kommentar Insert note event Notenevent einfügen Insert system exclusive event Insert controller event Insert meta event Increase tick value Decrease tick value Delete event Event löschen MusE: List Editor MusE: Listeneditor MusEGui::MPConfig Default input connections Standard-Eingangsverbindungen Are you sure you want to apply to all existing midi tracks now? Sind Sie sicher, dass Sie das auf alle existierenden MIDI-Spuren anwenden wollen? Default output connections Standard-Ausgangsverbindungen Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? Die Einstellung wird auf neue MIDI-Spuren angewendet werden. Möchten Sie sie jetzt auf alle existierende MIDI-Spuren anwenden? MusE: bad device name MusE: ungeeigneter Gerätename please choose a unique device name Bitte wählen Sie einen einzigartigen Gerätenamen in ein out aus Show first aliases Zeige erste Aliase Show second aliases Zeige zweite Aliase Toggle all Alle umschalten Change all tracks now Alle Spuren jetzt ändern Remove Entfernen Create Jack device Jack-Gerät erstellen Port Number Anschlussnummer Enable gui GUI aktivieren Enable reading Lesen aktivieren Enable writing Schreiben aktivieren Port instrument Anschluss-Instrument Midi device name. Click to edit (Jack) Midigerätename. Klicken zum bearbeiten (Jack) Connections from Jack Midi outputs Verbindungen von Jack Midi Ausgängen Connections to Jack Midi inputs Verbindungen zu Jack Midi Eingängen Auto-connect these channels to new midi tracks Verbinde diese Kanäle automatisch mit neuen Midispuren Auto-connect new midi tracks to these channels Verbinde neue Midispuren automatisch mit diesen Kanälen Auto-connect new midi tracks to this channel Verbinde neue Midispuren automatisch mit diesem Kanal Device state Gerätestatus Enable gui for device GUI für Gerät aktivieren Enable reading from device Lesen vom Gerät aktivieren Enable writing to device Schreiben zum Gerät aktivieren Name of the midi device associated with this port number. Click to edit Jack midi name. Name des MIDI-Gerätes, welches mit dieser Anschlussnummer verknüpft ist. Klicken Sie, um den Jack Midi Namen zu ändern. Instrument connected to port Instrument mit Anschluss verbunden Connections from Jack Midi output ports Verbindungen von Jack Midi Ausgangsports Connections to Jack Midi input ports Verbindungen zu Jack Midi Eingangsports Auto-connect these channels, on this port, to new midi tracks. Verbinde diese Kanäle auf diesem Port automatisch mit neuen MIDI-Spuren. Connect new midi tracks to these channels, on this port. Verbinde neue MIDI-Spuren mit diesen Kanälen auf diesem Port. Connect new midi tracks to this channel, on this port. Verbinde neue MIDI-Spuren mit diesem Kanal auf diesem Port. State: result of opening the device Status: Ergebnis vom Öffnen des Gerätes Port Anschluss GUI GUI I E O A Instrument Instrument Device Name Gerätename Please choose a unique device name Bitte wählen Sie einen eindeutigen Gerätenamen Port number Portnummer Midi device name Midigerätname Midi device type Midigerättyp Connections from Jack Midi Verbindungen von Jack Midi Ausgängen Connections to Jack Midi Verbindungen zu Jack Midi Eingängen Result of opening the device: OK: Assigned to a port and in use Closed: Unassigned to a port, or closed R/W Error: Unable to open for read or write Unavailable: USB midi unplugged, or external application not running, or synth plugin not installed etc. (Jack Midi devices have 'unavailable ports' in the routes columns.) Unavailable devices or ports can be purged with 'Remove' or with the advanced router. Enable Graphical User Interface for device GUI für Gerät aktivieren Connections from Jack Midi ports Verbindungen von Jack Midi Ausgängen Connections to Jack Midi ports Verbindungen zu Jack Midi Eingängen In routes Eingangssignalfluss Out routes Ausgangssignalfluss Def in ch Standard Ein-Kanal Def out ch Standard Aus-Kanal State Status Type Typ In In Out Out <unknown> <unbekannt> <none> <kein> MusEGui::MTScale bar scale Taktskala Bar scale Taktskala Bar scale: Use mouse buttons to set position and range markers. Hold Shift to set (LMB) or delete (RMB) custom markers. MusEGui::MTScaleFlo bar scale Taktskala Bar scale Taktskala MusEGui::MarkerView MusE: Marker MusE: Marker add marker Marker hinzufügen delete marker Marker löschen &Edit B&earbeiten Window &Config Fenster&konfiguration edit tools Werkzeuge "Bearbeiten" Add marker Marker hinzufügen Delete marker Marker löschen &Display &Anzeige Edit tools Bearbeitungswerkzeuge Bar:Beat:Tick Takt:Schlag:Tick Hr:Mn:Sc:Fr:Sf Std:Min:Sek:Fr:SFr Lock Sperre Description Beschreibung Edit tick Edit SMPTE timecode Toggle timecode editing Edit marker description <Description> Text Text Marker Properties Marker Eigenschaften MusEGui::Master Tools Werkzeuge MusEGui::MasterEdit MusE: Mastertrack MusE: Masterspur Window &Config Fenster&konfiguration Enable master Master einschalten Enable Einschalten Enable usage of master track Verwendung der Masterspur einschalten Info Info Cursor Positionsmarker time at cursor position Zeit an aktuellem Positionsmarker tempo at cursor position Tempo an aktuellem Positionsmarker Mastertrack Masterspur &Display &Anzeige Display menu: View-specific display options. Time at cursor position Zeit an aktuellem Positionsmarker Tempo at cursor position Tempo an aktuellem Positionsmarker Off Aus Bar Takt Snap Magnet CurPos AktPos tempo at current position Tempo an aktuellem Positionsmarker time signature at current position Taktmaß an aktuellem Positionsmarker MusEGui::MetronomeConfig Reset accents: Betonungen zurücksetzen: Resets all accents to the defaults (first in list) of the current preset category (Factory or User). Proceed? MusEGui::MidiAudioControl Control7 Controller7 Control14 RPN RPN NPRN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Tonhöhe Program Programm Aftertouch Aftertouch MusEGui::MidiComponentRack Pro Var Var Rev Rev Cho Cho Pan Pan Program Programm VariationSend VariationSend ReverbSend ReverbSend ChorusSend ChorusSend Pan/Balance Pan/Balance Controller Kontroller (Ctrl-double-click on/off) <unknown> <unbekannt> Instrument Instrument Transpose Transponieren Transpose notes up or down Delay Offset playback of notes before or after actual note Length Länge Change note length in percent of actual length Velocity <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> Compress Compress the notes velocity range, in percent of actual velocity <Patch> MusEGui::MidiInputTransformDialog New Neu MusEGui::MidiStrip Palette A Palette A Palette B Palette B A Palette A A B Palette B B off aus Volume/gain (Ctrl-double-click on/off) Lautstärke/Verstärkung (Ctrl+Doppelklick für an/aus) Record arm Aufnahme scharfschalten Mute or proxy mute Stummschalten oder Proxy-Stummschalten Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo Solo oder Proxe-Solo Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Track off Track aus Input monitor Input Monitor Intput routing. Press F1 for help. Output routing. Press F1 for help. Inst Inst Prop Instruments and controllers Properties Eigenschaften Volume/Gain (Ctrl-double-click on/off) Pass input through to output Schleife Eingangsevents durch zum Ausgang Input monitor: Pass input through to output. ctrl-double-click on/off Ctrl+Doppelklick für an/aus VariationSend VariationSend Var Var ReverbSend ReverbSend Rev Rev ChorusSend ChorusSend Cho Cho dB dB Pan/Balance Pan/Balance Pan Pan record Aufnahme mute Stumm solo mode Solo Modus input routing Eingangs-Signalfluss output routing Ausgangs-Signalfluss MusEGui::MidiSyncConfig Port Number Portnummer Name of the midi device associated with this port number Name des Midi Gerätes an dieser Portnummer Sync to this device. Click to select. Zu diesem Gerät synchronisieren. Auswahl durch klicken. Midi clock input detected Midi-Uhr Eingang erkannt Midi tick input detected Midi-Tick Eingang erkannt Midi real time input detected Midi Echtzeiteingang erkannt MMC input detected MMC Eingang erkannt MTC input detected MTC Eingang erkannt Detected SMPTE format SMPTE-Format erkannt Receive id number. 127 = Global. Double click to edit. Accept midi clock input Akzeptiere Midi-Uhr-Eingang Accept midi real time input Akzeptiere Midi-Echtzeit-Eingang Accept MMC input Akzeptiere MMC-Eingang Accept MTC input Akzeptiere MTC-Eingang Receive start rewinds before playing Das Empfangen von "Start" spult vor dem Abspielen zurück Transmit id number. 127 = Global. Double click to edit. Send midi clock output Send midi realtime output Send MMC output Send MTC output Type Typ Midi clock input detected. Current port actually used is red. Click to force a port to be used. Midi-Uhr-Eingang erkannt. Der momentan verwendete Anschluss ist rot. Klicken Sie, um einen anderen Anschluss zu erzwingen. Midi realtime input detected, including start/stop/continue, and song position. Midi-Echtzeit-Eingang erkannt, einschließlich Start/Stopp/Fortsetzen und Liedposition. MMC input detected, including stop/play/deferred play, and locate. MMC-Eingang erkannt, einschließlich Stopp/Play/Verzögertes Play und Position. MTC input detected, including forward quarter-frame sync and full-frame locate. Current port actually used is red. Click to force a port to be current. MTC-Eingang erkannt, einschließlich Vorwärts-Viertel-Frame-Synchronisation und Voll-Frame-Positionserkennung. Der momentan verwendete Anschluss ist rot. Klicken Sie, um einen anderen Anschluss zu erzwingen. Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detects format of MTC quarter and full frame, and MMC locate. Receive id number. 127 = global receive all, even if not global. Accept midi clock input. Only one input is used for clock. Auto-acquire: If two or more port realtime inputs are enabled, the first clock detected is used, until clock is lost, then another can take over. Best if each turns off its clock at stop, so MusE can re-acquire the clock from another port. Click on detect indicator to force another. Midi-Uhr-Eingang akzeptieren. Nur ein Eingang wird für die Uhr benutzt. Auto-Benutzen: Wenn mehr als ein Echtzeiteingang aktiviert ist, wird die erste erkannte Uhr genutzt, bis sie verloren geht. Dann übernimmt eine andere. Es ist am besten, wenn jeder seine Uhr beim Stopp ausschaltet, damit MusE die Uhr von einem anderen Anschluss benutzen kann. Klicken Sie auf den Erkennungs- Indikator, um eine andere zu erzwingen. Accept midi realtime input, including start/stop/continue, and song position. Non-clock events (start,stop etc) are accepted by ALL enabled ports. This means you may have several master devices connected, and muse will accept input from them. Akzeptiere MIDI-Echtzeit-Eingänge, einschließlich Start/Stopp/Fortsetzen und Liedposition. Nicht-Uhr-Ereignisse (Start/Stopp etc.) werden von ALLEN aktivierten Anschlüssen angenommen. Das bedeutet, dass Sie mehrere Master-Geräte verbinden können, und MusE wird deren Daten annehmen. Accept MMC input, including stop/play/deferred play, and locate. Akzeptiere MMC-Eingang, einschließlich Stopp/Play/Verzögertes Play und Position. Accept MTC input, including forward quarter-frame sync and full-frame locate. See 'rc' column for more help. MTC-Eingang erkannt, einschließlich Vorwärts-Viertel-Frame-Synchronisation und Voll-Frame-Positionserkennung. Siehe die "rc"-Spalte für mehr Hilfe. When start is received, rewind before playing. Note: It may be impossible to rewind fast enough to synchronize with the external device. Wenn Start empfangen wird, wird vor dem Abspielen zurückgespult. Hinweis: Es könnte unmöglich sein, schnell genug zurückzuspulen, um mit dem externen Gerät synchron zu bleiben. Sync to this device. Click to select. MusE will synchronize to this device's clock or MTC quarter-frame, if 'slave to external sync' is on. MTC input detected, including forward quarter-frame sync and full-frame locate Accept midi clock input. Only one port can be used for clock sync. Accept MTC input, including forward quarter-frame sync and full-frame locate. Only one port can be used for sync. When start is received, rewind before playing. Note: In some cases, such as having many project audio tracks, it may be impossible to rewind fast enough to synchronize with the external device. Manually rewinding the device before playing is recommended. Transmit id number. 127 = global transmit to all. Send midi clock output. If 'Slave to External Sync' is chosen, muse can re-transmit clock to any other chosen ports. Sende Midi-Uhr-Ausgang. Wenn "Binde an Externe Sync" gewählt ist, kann MusE das Uhr-Signal an andere ausgewählte Anschlüsse weiterleiten. Send midi realtime output, including start/stop/continue, and song position. If 'Slave to external sync' is chosen, muse can re-transmit midi realtime input messages to any other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. Sende Midi-Echtzeit Ausgang, einschließlich Start/Stopp/ Fortsetzen und Liedposition. Wenn "Binde an externe Sync." aktiviert ist, kann MusE Midi Echtzeit-Eingangsnachrichten an andere ausgewählte Anschlüsse weiterleiten. Das bedeutet, dass Sie mehrere "Sklaven"-Geräte verbinden können, und MusE Echtzeitnachrichten an einige oder alle weiterleiten kann. Port Anschluss Device Name Gerätename s Sek c c k k r r m m t t type Typ rid rid rc rc rr rr rm rm rt rt rw rw tid tid tc tc tr tr tm tm tt tt None Kein Tiny Gering Small Klein Large Groß Large with pre-detect Groß mit Pre-Detect MusE MusE Settings have changed Apply sync settings? Einstellungen wurden geändert. Sync-Einstellungen anwenden? &Apply &Anwenden &No &Nein &Abort A&bbrechen <none> <kein> MusEGui::MidiTrackInfo <unknown> <unbekannt> MusEGui::MidiTransformerDialog New Neu MusEGui::MixdownFileDialog Wave Files (*.wav);;All Files (*) Wave Dateien (*.wav);;Alle Dateien (*) MusEGui::MusE Failed to start audio! Audio konnte nicht gestartet werden! Was not able to start audio, check if jack is running. Audio konnte nicht gestartet werden, überprüfen Sie, ob Jack läuft. Timeout waiting for audio to run. Check if jack is running. Timeout beim Warten auf Audio. Überprüfen Sie, ob Jack läuft. Und&o &Rückgängig Re&do &Wiederholen undo last change to song Letzte Änderung am Lied rückgängig machen redo last undo Letztes Rückgängigmachen wiederholen Loop Schleife loop between left mark and right mark Schleife zwischen linkem und rechtem Marker Punchin Aufnahme am linken Marker beginnen record starts at left mark Aufnahme am linken Marker beginnen Punchout Aufnahme am rechten Marker beenden record stops at right mark Aufnahme am rechten Marker beenden Start Start rewind to start position Zur Startposition springen Rewind Einen Taktschlag rückwärts rewind current position Einen Taktschlag rückwärts Forward Einen Taktschlag vorwärts move current position Einen Taktschlag vorwärts Stop Stopp stop sequencer Wiedergabe beenden Play Wiedergabe start sequencer play Wiedergabe beginnen Record Aufnahme to record press record and then play Zur Aufnahme erst die Schaltfläche "Aufnahme" und dann "Wiedergabe" klicken Panic Panik send note off to all midi channels Panik - "Note aus" Befehl an alle Midikanäle senden &New &Neu Create New Song Neues Lied erzeugen &Open Lied &öffnen Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. Auf diese Schaltfläche klicken um ein <em>Neues Lied</em> zu öffnen.<br> Alternativ den Befehl <b>Neues Lied</b> des Menüs "Datei" auswählen. Open &Recent &Letztes Lied öffnen &Save &Speichern Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. Auf diese Schaltfläche klicken um das gerade bearbeitete Lied zu sichern mit der automatischen Aufforderung einen Dateinamen anzugeben. Alternativ das Lied mit dem Befehl "Sichern" im Menü "Datei" sichern. Save &As Speichern &unter Import Midifile Mididatei importieren Export Midifile Mididatei exportieren Import Part Part importieren Import Audio File Audiodatei importieren Find unused wave files Finde ungenutzte Wave-Dateien &Quit &Beenden Song Info Liedinfo Transport Panel Transportkonsole Bigtime Window Große Zeitanzeige Mixer A Mixer A Mixer B Mixer B Cliplist Cliplist Marker View Marker-Fenster Arranger View Arranger-Fenster Fullscreen Vollbild &Plugins &Plugins Edit Instrument Instrument bearbeiten Input Plugins Eingang-PlugIns Transpose Transponieren Midi Input Transform Midi Eingang transformieren Midi Input Filter Midi Eingangsfilter Midi Remote Control Midi Fernbedienung Rhythm Generator Rhythmusgenerator Reset Instr. Instrument zurücksetzen Init Instr. Instrument initialisieren Local Off Lokal aus Bounce to Track Auf Spur abmischen Bounce to File In Datei abmischen Restart Audio Audio erneut starten Mixer Automation Mischpult Automatisierung Take Snapshot Schnappschuss aufnehmen Clear Automation Data Automatisierungsdaten löschen Cascade Staffeln Tile Kacheln In rows In Reihen In columns In Spalten Global Settings Globale Einstellungen Configure Shortcuts Tastenkürzel einstellen Follow Song Folge dem Lied Don't Follow Song Folge dem Lied nicht Follow Page Folge dem Lied seitenweise Follow Continuous Folge dem Lied ständig Set left/right marker for bounce range Linken/rechten Marker für Abmischbereich einstellen MusE: Song: MusE: Lied: Metronome Metronom Midi Sync Midi Sync Midi File Import/Export Midi-Datei Import/Export Appearance Settings Erscheinungsbild einstellen Midi Ports / Soft Synth Midi Anschlüsse / Software Synthesizer &Manual &Handbuch &MusE Homepage &MusE Webseite &Report Bug... &Fehler melden ... &About MusE &Über MusE Song Position Song-Position Tempo Tempo Signature Taktmaß File Buttons Datei-Knöpfe Undo/Redo Rückgängig/Wiederholen Transport Transport Cpu load CPU-Auslastung Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help. &File &Datei &View An&sicht &Midi &Midi &Audio &Audio A&utomation A&utomatisierung &Windows F&enster Se&ttings Ei&nstellungen &Help &Hilfe About &Qt Über &Qt Cannot read template Vorlage nicht lesbar File open error Fehler beim Datei öffnen File read error Fehler beim Datei lesen Unknown File Format: %1 Unbekanntes Dateiformat: %1 MusE: Song: %1 MusE: Lied: %1 MusE: load project Muse: Projekt laden Timeout waiting for audio to run. Check if jack is running or try another driver. Was not able to start audio, check if jack is running or try another driver. Undo last change to song Letzte Änderung am Lied rückgängig machen Redo last undo Letztes Rückgängigmachen wiederholen Loop between left mark and right mark Schleife zwischen linkem und rechtem Marker Punch in Aufnahme am linken Marker beginnen Record starts at left mark Aufnahme am linken Marker beginnen Recording starts at left mark Punch out Aufnahme am rechten Marker beenden Record stops at right mark Aufnahme am rechten Marker beenden Recording stops at right mark Rewind to start position Zur Startposition spulen Rewind current position Aktuelle Position zurückspulen Move current position Aktuelle Position bewegen Stop sequencer Wiedergabe beenden Restart rec Aufnahme erneut starten Start sequencer play Wiedergabe beginnen To record press record and then play Zur Aufnahme erst die Schaltfläche "Aufnahme" und dann "Wiedergabe" klicken Send note off to all midi channels "Note aus" Befehl an alle Midikanäle senden Turn on/off metronome Metronom an/aus Metronome on/off. Press F1 for help. Create new song Neues Lied anlegen New from &Template... Neu aus &Vorlage... Create new song from template Neues Lied aus Vorlage anlegen &Open... &Öffnen... Open song from file Lied aus Datei öffnen Click this button to open an existing song. Klicken Sie auf diese Schaltfläche, um ein vorhandenes Lied zu öffnen. Save current song Aktuelles Lied speichern Click this button to save the song you are editing. You will be prompted for a file name. Klicken Sie auf diese Schaltfläche, um das aktuelle Lied zu speichern. Es folgt eine Aufforderung zur Dateinameneingabe. Save &As... Speichern &unter... &Close &Schließen Import Midifile... Mididatei importieren... Export Midifile... Mididatei exportieren... Import Part... Part importieren... Import Audio File... Audiodatei importieren... Find Unused Wave Files... Ungenutzte Wave-Dateien finden... Song Info... Liedinfo... Transport Panel... Transportkonsole... Bigtime Window... Große Zeitanzeige... Mixer A... Mixer A... Mixer B... Mixer B... Cliplist... Cliplist... Marker View... Markeransicht... Mastertrack Masterspur Graphic... Graphisch... List... Liste... Edit Instrument... Instrument bearbeiten ... Transpose... Transponieren... Midi Input Transform... Midi Eingang transformieren... Midi Input Filter... Midi Eingangsfilter... Midi Remote Control... Midi Fernbedienung... Reset Instrument Instrument zurücksetzen Init Instrument Instrument initialisieren Send 'local-off' command to all midi channels. Bounce to File... In Datei abmischen... In Rows In Reihen In Columns In Spalten Global Settings... Globale Einstellungen... Appearance... Erscheinungsbild... Configure Shortcuts... Tastenkürzel konfigurieren... Rewind on Stop Zum Start zurück Metronome... Metronom... Midi Sync... Midi Sync... Midi File Import/Export... Midi-Datei Import/Export... Appearance Settings... Erscheinungsbild... Midi Ports / Soft Synth... Midi Anschlüsse / Software Synthesizer... &Manual... &Handbuch... &MusE Homepage... &MusE Webseite... &Did You Know? &Wussten Sie? &About MusE... &Über MusE... Snooper (developer tool)... Snooper (Entwicklertool)... About &Qt... Über &Qt... The project has no project sample rate (added 2011). Please enter a rate. The current system rate (%1Hz) is suggested, and cancelling uses it: The project has audio waves, but no project sample rate (added 2011). Please enter a rate. The most common wave rate found is suggested, the project was probably made with it. Cancelling uses the current system rate (%1Hz): Project sample rate Projekt-Samplingrate MusE: load template Muse: Vorlage laden MusE: Write File failed MusE: Datei schreiben schlug fehl The current Project contains unsaved data Save Current Project? Das aktuelle Projekt enthält nicht gespeicherte Daten Aktuelles Projekt speichern? S&kip &Überspringen Load configuration colors Farbenkonfiguration laden &Cancel &Abbrechen Color settings will immediately be replaced with any found in the file. Are you sure you want to proceed? Farbeinstellungen werden durch Werte aus der Datei ersetzt. Wollen Sie fortsetzen? &Ok &Bestätigen Save configuration colors Farbenkonfiguration speichern File exists. Do you want to overwrite it? Datei vorhanden. Möchten Sie sie überschreiben? MusE: Save As MusE: Speichern unter Nothing to edit Es gibt nichts zu editieren MusE: Bounce to Track MusE: Auf Spur abmischen No wave tracks found Keine Wave-Tracks gefunden No audio output tracks found Keine Audio-Tracks gefunden Select one audio output track, and one target wave track Wählen Sie eine Audio-Ausgangsspur und eine Ziel-Wave-Spur Select one target wave track Wählen Sie eine Ziel-Wave-Spur Select one target wave track, and one audio output track Wählen Sie eine Ziel-Wave-Spur und eine Audio-Ausgangs-Spur MusE: Bounce to File MusE: In Datei abmischen Select one audio output track Wählen Sie eine Audio-Ausgangs-Spur MusE: Bounce MusE: Abmischen set left/right marker for bounce range Linken/rechten Marker für Abmischbereich einstellen The current Project contains unsaved data Load overwrites current Project: Save Current Project? Das aktuelle Projekt enthält ungesicherte Daten Neues Lied laden überschreibt das aktuelle Projekt: Aktuelles Projekt sichern? &Abort &Abbrechen This will clear all automation data on all audio tracks! Proceed? Dies wird alle Automatisierungsdaten von allen Audiospuren löschen! Fortsetzen? This takes an automation snapshot of all controllers on all audio tracks, at the current position. Proceed? Dies nimmt an der momentanen Position einen Automatisierungs-Schnappschuss aller Controller von allen Audiospuren auf. Fortsetzen? MusE: Warning MusE: Warnung The song uses multiple ports but export format 0 (single track) is set. The first track's port will be used. Playback will likely be wrong unless the channels used in one port are different from all other ports. Canceling and setting a different export format would be better. Continue? MusE: Export Midi MusE: Midi exportieren no help found at: Hilfe wurde nicht gefunden: No help found at: Hilfe wurde nicht gefunden: MusE: Open Help MusE: Hilfe öffnen Unable to launch help Konnte Hilfe nicht starten For some reason MusE has to launch the default browser on your machine. Aus irgendeinem Grund muss MusE den Standardbrowser auf Ihrem System starten. MusE: Import Midi MusE: Midi importieren Add midi file to current project? Mididatei zu aktuellem Lied hinzufügen? &Add to Project Zu Lied hin&zufügen &Replace Erset&zen reading midifile Lese Mididatei Reading midifile Lese Mididatei failed: schlug fehl: Import part is only valid for midi and wave tracks! Part importieren ist nur möglich für MIDI und WAVE-Spuren! MusE: load part MusE: Part laden No track selected for import Keine Spur ist für den Import ausgewählt %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. %n Part von insgesamt %1 konnte nicht eingefügt werden. Wahrscheinlich hat die ausgewählte Spur den falschen Typ. %n Parts von insgesamt %1 konnten nicht eingefügt werden. Wahrscheinlich hat die ausgewählte Spur den falschen Typ. %n part(s) could not be imported. Likely the selected track is the wrong type. %n Part konnte nicht eingefügt werden. Wahrscheinlich hat die ausgewählte Spur den falschen Typ. %n Parts konnten nicht eingefügt werden. Wahrscheinlich hat die ausgewählte Spur den falschen Typ. to import an audio file you have first to selecta wave track um eine Audio-Datei zu importieren müssen Sie erst ein Wave-Spur auswählen To import an audio file you have first to select a wave track Um eine Audio-Datei zu importieren müssen Sie erst eine Wave-Spur auswählen Import Wavefile Wavedatei importieren This wave file has a samplerate of %1, as opposed to current setting %2. File will be resampled from %1 to %2 Hz. Do you still want to import it? Diese Wavedatei hat eine Samplerate von %1, im Gegensatz zur momentanen Einstellung %2. Möchten Sie sie dennoch importieren? Wave import error Fehler beim Wave-Import There are too many wave files of the same base name as imported wave file Can not continue. Can't create new wav file in project folder! Wavedatei kann im Projektordner nicht angelegt werden! Failed to initialize sample rate converter! Cancel Abbrechen Show Docks Docks anzeigen Toggle display of currently visible dock windows. Anzeige von angedockten Fenstern umschalten. Display MusE main window in full screen mode. Mastertrack Graphic... Masterspur-Editor graphisch... Send 'note-off' command to all midi channels. Send initialization messages as found in instrument definition. Take Automation Snapshot Automatisierung-Schnappschuss aufnehmen Keyboard Shortcuts... Tastenkürzel... Midi Ports / Soft Synths... &Manual (wiki)... &Handbuch (Wiki)... File buttons Datei-Schaltflächen Timeline Timeline Recording Aufnahme Sync Sync Position Zeitposition The current project contains unsaved data. Save current project? No valid parts selected Keine gültigen Parts ausgewählt The current project contains unsaved data. Load overwrites current project. Save current project? This wave file has a samplerate of %1 Hz, as opposed to current setting %2 Hz. A live, real-time samplerate converter can be used on this file. Or, a copy of the file can be resampled now from %1 Hz to %2 Hz. Do you want to use a converter or resample the file now? Wollen Sie einen Konverter nutzen oder Datei jetzt resampeln? Use live converter Live-Konverter benutzen Resample now Jetzt resampeln Resampling wave file "%1" from %2 to %3 Hz... Output has clipped Resampling again and normalizing wave file "%1" Try %2 of %3... This wave file has a samplerate of %1, as opposed to current setting %2. Do you still want to import it? Diese Wavedatei hat eine samplerate von %1, im Gegensatz zur momentanen Einstellung %2. Möchten Sie sie dennoch importieren? &Yes &Ja &No &Nein File version is %1.%2 Current version is %3.%4 Conversions may be applied if file is saved! Opening file Öffne Datei Do not warn again Nicht mehr warnen Unable to launch browser Browser konnte nicht gestartet werden Error launching default browser Fehler beim Starten des Default-Browsers MusEGui::NoteInfo Note Info Noteninformation delta/absolute mode Delta-/Absolutmodus Delta/Absolute mode Delta-/Absolutmodus Start Start Len Länge Pitch Tonhöhe Velo on Velo off Velo On Velo an Velo Off Velo aus MusEGui::PartCanvas Cannot copy/move/clone to different Track-Type Kopieren/verschieben/klonen auf anderen Spurtyp nicht möglich Part: Part: C&ut &Ausschneiden &Copy &Kopieren s&elect &wähle clones Klone rename Umbenennen color Farbe delete löschen split Schnitt glue Verbinder super glue (merge selection) Super-Kleber (Auswahl vereinigen) de-clone Entklonen save part to disk Speichere Part auf Festplatte wave edit Wave Editor file info Dateiinfo Part S&elect &Wählen Rename Umbenennen Color Farbe Delete Löschen Split Aufteilen Glue Verbinden Super glue (merge selection) Super-Kleber (Auswahl vereinigen) De-clone Entklonen Save part to disk... Speichere Part auf Festplatte... Wave edit... File info... MusE: Save part Automation Automatisierung Wave edit Wave Editor Save part to disk Speichere Part auf Festplatte File info Dateiinfo Normalize Normalisieren MusE: save part MusE: Speichere Part Part name: %1 Files: Part Name: %1 Dateien: Automation: Automatisierung: Part canvas: Use Pencil tool to draw parts. Double-click to create a new MIDI/drum part between the range markers (set with MMB + RMB). Press F1 for help. Remove selected Löschen LMB: Select/Move/Dblclick to edit | CTRL+LMB: Multi select/Move&Copy | CTRL+ALT+LMB: Dblclick to edit in new window | SHIFT+LMB: Select track | MMB: Delete LMB: Draw to resize | MMB: Delete | CTRL+RMB: Trim length LMB: Delete | CTRL+RMB: Trim length LMB: Cut part in two LMB: Merge with following part LMB: Mute selected part LMB: Edit automation events in audio parts %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. %n Part von insgesamt %1 konnte nicht eingefügt werden. Wahrscheinlich hat die ausgewählte Spur den falschen Typ. %n Parts von insgesamt %1 konnten nicht eingefügt werden. Wahrscheinlich hat die ausgewählte Spur den falschen Typ. %n part(s) could not be pasted. Likely the selected track is the wrong type. %n Part konnte nicht eingefügt werden. Wahrscheinlich hat die ausgewählte Spur den falschen Typ. %n Parts konnten nicht eingefügt werden. Wahrscheinlich hat die ausgewählte Spur den falschen Typ. Cannot paste: multiple tracks selected Einfügen nicht möglich: mehrere Spuren ausgewählt Cannot paste: no track selected Einfügen nicht möglich: keine Spur ausgewählt Can only paste to midi/drum track Einfügen möglich nur in Midi/Drum Spur Can only paste to wave track Einfügen möglich nur in Wave Spur Can only paste to midi or wave track Einfügen möglich nur in Midi oder Wave Spur Cannot paste: wrong data type Einfügen nicht möglich: falscher Datentyp MusEGui::PasteDialog %n quarter(s) %n Viertelnote %n Viertelnoten %1 quarter for floating-point arguments like 1.5 %1 Viertelnote %1 quarters for floating-point arguments like 1.5 %1 Viertelnoten MusEGui::PasteEventsDialog %n quarter(s) %n Viertelnote %n Viertelnoten %1 quarter for floating-point arguments like 1.5 %1 Viertelnote %1 quarters for floating-point arguments like 1.5 %1 Viertelnoten MusEGui::Piano Piano: Press key to play or enter events in step record mode (SHIFT for chords). RMB: Set cursor for polyphonic control events. CTRL+Mousewheel to zoom view vertically. Left click: Play Right click: Select key MusEGui::PianoCanvas Pianoroll canvas: Use Pencil tool to draw and edit MIDI events, Pointer tool to select and edit. Press F1 for help. Note: Velocity: Start (bar): Start (time): Length (ticks): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete | CTRL+RMB: Trim length LMB: Resize | CTRL+LMB: Multi select | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length LMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length MusEGui::PianoRoll &Edit &Bearbeiten C&ut &Ausschneiden &Copy &Kopieren Copy events in range Kopiere Events im Bereich &Paste &Einfügen Paste to current part In aktuellen Part einfügen Paste (with dialog) Einfügen (Dialog zeigen) Delete &Events &Events löschen &Select &Auswählen Select &All Alles a&uswählen &Deselect All Alles ab&wählen Invert &Selection Auswa&hl umkehren &Inside Loop Bereich &innerhalb der Schleife &Outside Loop Bereich außerhalb der Schlei&fe &Previous Part Vorheriger &Part &Next Part &Nächster Part Event List... Eventliste... Fu&nctions &Funktionen Quantize Quantisieren Modify Note Length Notenlänge verändern Modify Velocity Anschlagsdynamik ändern Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transponieren Erase Events Events löschen Move Notes Noten verschieben Set Fixed Length Länge festlegen Delete Overlaps Überlappungen entfernen Legato Legato &Scripts &Skripte Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Play single note Note abspielen Play chords Akkorde abspielen &Plugins &Plugins &Display &Anzeige Display menu: View-specific display options. Add controller view Controlleransicht hinzufügen Step record Step-Aufnahme Midi input Midi Eingang Play events Ereignisse abspielen Window &Config Fenster&konfiguration &Event Color &Eventfarbe &Blue &Blau &Pitch colors Farbe nach &Tonhöhe &Velocity colors Farbe nach &Anschlagsdynamik Pianoroll tools Werkzeug "Pianorollen Editor" Step Record Aufnahme taktschlagweise Midi Input Midi Eingang Play Events Ereignisse abspielen ctrl Ctrl Add Controller View Controlleransicht hinzufügen MusEGui::PluginDialog MusE: select plugin MusE: PlugIn wählen MusE: Select Plugin MusE: Plugin auswählen Plugin categories. Right-click on tabs to manage. Right-click on plugins to add/remove from a category. Type Typ Lib Lib Label Beschriftung Name Name AI Al AO AO CI Cl CO CO IP IP FB 2B 8' {2B?} id id Maker Erzeuger Copyright Copyright Audio inputs Audio-Eingänge Audio outputs Audio-Ausgänge Control inputs Steuerungs-Eingänge Control outputs Steuerungs-Ausgänge In-place capable In-Place-fähig Requires fixed block size Requires power-of-2 block size ID number ID-Nummer &Create new group Neue &Gruppe erzeugen &Delete currently selected group Ausgewählte Gruppe &löschen Re&name currently selected group Ausgewählte Gruppe &umbenennen New group Neue Gruppe Ok Bestätigen Cancel Abbrechen Show plugs: Zeige Plugins: Mono and Stereo Mono und Stereo Stereo Stereo Mono Mono Show All Zeige alle Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. Wählen Sie aus, welche Plugintypen in der Liste sichtbar sein sollen.<br>Es ist kein Problem, Mono-Plugins auf Stereospuren zu verwenden; dann werden zwei parallel verwendet.<br>Beachten Sie, dass die "Alle"-Alternative Plugings beinhaltet, die nicht in einem Effekteinschub nützlich sein könnten. Associated categories Verw You need to define some categories first. Enter the new group name Namen für neue Gruppe eingeben Wine VST Wine VST dssi synth DSSI-Synth dssi effect DSSI-Effekt LV2 synth LV2 Synth LV2 effect LV2 Effekt VST synth VST Synth VST effect VST Effekt ladspa LADSPA Search in 'Label' and 'Name': Suche in "Beschriftung" und "Name": MusEGui::PluginGui File Buttons Datei-Knöpfe Load Preset Vorlage laden Save Preset Speicher Preset Bypass plugin Plugin überspringen Plugin settings Plugin-Einstellungen MusE: Save preset Plugin Settings Plugin-Einstellungen Quirks: Quirks: Fixed speed Feste Geschwindigkeit Transport affects audio latency Transport beeinflusst die Latenz Revert native UI HiDPI scaling: Follow global setting HiDPI Skalierung von nativen UIs zurücksetzen: Globale Einstellung berücksichtigen Revert native UI HiDPI scaling: On HiDPI Skalierung von nativen UIs zurücksetzen: Ein Revert native UI HiDPI scaling: Off HiDPI Skalierung von nativen UIs zurücksetzen: Aus bypass plugin Signalfluss PlugIn überspringen MusE: load preset MusE: Vorlage laden Error reading preset. Might not be right type for this plugin Fehler beim Lesen der Vorlage. Könnte der falsche Typ für dieses Plugin sein MusE: save preset MusE: Vorlage sichern MusEGui::PluginSettings Global setting: Globale Einstellung: On An Off Aus MusEGui::PopupMenu <More...> %1 <Mehr...> %1 MusEGui::PosToolbar Left marker Linker Marker Left marker position Right marker Rechter Marker Right marker position Current position Aktuelle Position Current position in bars/beats Aktuelle Position in Takten/Schlägen SMPTE position SMPTE Position Current position in SMPTE time Show/Hide position in ticks and audio frames Position in Ticks und Audioframes anzeigen/verstecken Show/Hide additional output fields displaying the current position in ticks and audio frames Current position in ticks Aktuelle Position in Ticks Current position in audio frames Aktuelle Position in Audioframes Current position slider Schieberegler für aktuelle Position MusEGui::ProjectCreateImpl Select directory Verzeichnis auswählen MusEGui::RasterLabelCombo Snap Magnet MusEGui::RecToolbar Record mode Aufnahmemodus Record mode: Overdub to add new events, Replace to replace overlapping events. Overdub Überschreiben Replace Ersetzen Cycle record mode Cycle record mode: Normal to replace range when loop is finished, Mix to add new events, Replace to replace range on first MIDI input. Normal Normal Mix MusEGui::RouteDialog Normal Normal Alias 1 Alias 2 Source Quelle Destination Ziel <none> <kein> MusEGui::RoutePopupMenu Channel Kanal Soloing chain Solo-Kette Audio returns Audio-Rückkanäle Warning: No input devices! Warnung: Keine Eingangsgeräte! Channel grouping: Kanalgruppierung: Mono Mono Stereo Stereo Channels Kanäle Omni Omni Show aliases: Aliase anzeigen: First Erste Second Zweite Show names Namen anzeigen Show first aliases Zeige erste Aliase Show second aliases Zeige zweite Aliase Jack ports Jack-Ports Connect Verbinden Unavailable Nicht verfügbar Open advanced router... Advanced Router öffnen... Warning: No output devices! Warnung: Keine Ausgangsgeräte! Output routes: Ausgangssignalfluss: Input routes: Eingangssignalfluss: Open midi config... Öffne MIDI-Konfig... ALSA devices ALSA Geräte JACK devices JACK Geräte Synth devices Synth-Geräte <none> <kein> Midi sends Sources: Quellen: Toggle all Alles umwählen More... Mehr... Tracks Spuren Destinations: Ziele: Audio sends Audio-Sender Midi port sends Midiport-Sender MusEGui::ScoreCanvas Treble Violinschlüssel Bass Bassschlüssel Grand Staff Beide Schlüssel Remove staff Notenzeile entfernen Ambiguous part Unklarer Part There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. Es gibt zwei oder mehr mögliche Parts, zu denen man die Note hinzufügen könnte, aber keiner ist der ausgewählte. Bitte wählen Sie den Ziel-Part aus, indem Sie auf eine beliebige Note, die zu ihm gehört, klicken, und versuchen Sie es erneut, oder fügen Sie eine neue Notenzeile hinzu, die nur den Zielpart enthält. No part Kein Part There are no parts you could add the note to. Es gibt keine Parts, zu denen man die Note hinzufügen könnte. Tools Werkzeuge MusEGui::ScoreEdit Step recording tools "Taktschlagweise Aufnahme"-Werkzeuge Step Record Aufnahme taktschlagweise Note settings Noteneinstellungen Note length: Notenlänge: last letzte Apply to new notes: Für neue Noten: Apply to selected notes: Für markierte Noten: Velocity: Dynamik: Off-Velocity: Loslassdynamik: Quantisation settings Quantisierungseinstellungen Step record Step-Aufnahme Quantisation: Quantisierung: Pixels per whole: Pixel pro ganze Note: &Edit &Bearbeiten C&ut &Ausschneiden &Copy &Kopieren Copy events in range Kopiere Events im Bereich Quantisation Quantisierung Pixels per whole Pixels pro ganze Note Note length Notenlänge Velocity Apply to selected notes, or new notes if none is selected Off-Velocity Copy Events in Range &Paste &Einfügen Paste (with dialog) Einfügen (Dialog zeigen) Delete &Events &Events löschen &Select &Auswählen Select &All Alles a&uswählen &Deselect All Alles ab&wählen Invert &Selection Auswa&hl umkehren &Inside Loop Bereich &innerhalb der Schleife &Outside Loop Bereich außerhalb der Schlei&fe Fu&nctions Fu&nktionen &Quantize &Quantisieren Change Note &Length Noten&länge verändern Change Note &Velocity &Anschlagsdynamik verändern Display menu: View-specific display options. Note Head &Colors &Default Set Up &Preamble Display &Key Signature Display &Time Signature Set Score &Name Change note &length Noten&länge verändern Change note &velocity &Anschlagsdynamik verändern Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transponieren Erase Events Events löschen Move Notes Noten verschieben Set Fixed Length Feste Notenlänge setzen Delete Overlaps Überlappungen entfernen Legato Legato &Display &Anzeige Window &Config Fenster&konfiguration Note head &colors Notenkopf-&Farben &Black &Schwarz &Velocity Je nach &Dynamik &Part Je nach &Part Set up &preamble &Preambel einstellen Display &key signature &Vorzeichen anzeigen Display &time signature &Takt anzeigen Set Score &name Partitur&name setzen Enter the new score title Geben Sie den neuen Titel ein Error Fehler Changing score title failed: the selected title is not unique Das Ändern des Titels ist fehl- geschlagen: Der eingegebene Titel ist nicht einzigartig MusEGui::ScrollScale next page Nächste Seite previous page Vorherige Seite current page number Aktuelle Seitennummer MusEGui::ShortcutCaptureDialog Ok Bestätigen Cancel Abbrechen Shortcut conflicts with %1 Tastenkürzel überschneidet sich mit %1 Undefined Unbestimmt MusEGui::ShortcutConfig Save printable text file Druckbare Textdatei speichern Text files (*.txt);;All files (*) Error Fehler Error opening file for saving Fehler beim Datei öffnen zum Speichern Shortcuts for selected category: Shortcuts für ausgewählte Kategorie: Legend: Erklärung: An error occurred while saving Fehler beim Speichern MusEGui::SigScale signature scale Taktmaß-Skala Signature scale Taktmaß-Skala MusEGui::SigToolbar Time signature at current position Taktmaß an aktueller Position Signature: Taktmaß: MusEGui::SigToolbarWidget time signature at current position Taktmaß bei aktueller Position Signature: Taktmaß: MusEGui::Strip Remove track? Spur entfernen? Name Name Enter track name: Spurnamen eingeben: MusE: bad trackname MusE: ungeeigneter Spurname Please choose a unique track name Bitte eindeutigen Spurnamen wählen Configuration: Konfiguration: Configuration Prefer knobs, not sliders Drehregler statt Schieberegler bevorzugen Show values in controls Dreh- und Schiebereglerwerte anzeigen Prefer midi volume as decibels Dezibell für Midilautstärke bevorzugen Monitor on record-arm automatically Automatisches Monitoring bei Aufnahme-Scharfschaltung Visible audio effects Sichtbare Audioeffekte Actions Aktionen Actions: Aktionen: Change track name Spurnamen ändern Hide strip Strip ausblenden MusEGui::SyncToolbar External sync on/off Externer Sync an/aus Jack Transport on/off Jack-Transport an/aus On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::TList SYNTH IS UNAVAILABLE! <none> <kein> visible sichtbar no clef kein Notenschlüssel Treble Violinschlüssel Bass Bassschlüssel Grand Beide Schlüssel off aus <unknown> <unbekannt> MusE: bad trackname MusE: ungeeigneter Spurname Please choose a unique track name Bitte eindeutigen Spurnamen wählen Show gui GUI anzeigen Show native gui Native GUI anzeigen Clear automation Lösche Automatisierung please choose a unique track name Bitte eindeutigen Spurnamen wählen Update drummap? Drumbelegung aktualisieren? Do you want to use same port for all instruments in the drummap? Möchten Sie für alle Instrumente der Drumbelegung den selben Anschluss verwenden? &Yes &Ja &No &Nein show gui GUI anzeigen show native gui Native GUI anzeigen Presets Voreinstellungen Clear all controller events? Alle Controller-Ereignisse löschen? &Ok &Bestätigen &Cancel Change color Farbe ändern Midi control MIDI-Steuerung Assign Zuweisen Clear Löschen Other Andere clear automation lösche Automatisierung Track list: Use context menu to create tracks. Click track ID to select, CTRL to add, SHIFT for range, dblclick to select all of same type. Press F1 for help. Treble clef Violinschlüssel Bass clef Bassschlüssel Grand Staff Beide Schlüssel Viewable automation Sichtbare Automatisierung Internal Intern Synth Synth Delete Track Spur löschen Delete Selected Tracks Ausgewählte Spuren löschen Track Comment Spur Kommentar Save track's drumlist Schlagzeugliste einer Spur speichern Load track's drumlist Schlagzeugliste einer Spur laden Reset track's drumlist Schlagzeugliste einer Spur zurücksetzen Reset track's drumlist-ordering Reihenfolge der Schlagzeugliste einer Spur zurücksetzen Copy track's drumlist to all selected tracks Schlagzeugliste einer Spur in alle ausgewählten Spuren kopieren Insert Track Spur einfügen Drum map Schlagzeugbelegung Reset the track's drum map with instrument defaults? Die Schlagzeugbelegung auf Standardwerte zurücksetzen? Reset the track's drum map ordering? Reihenfolge der Schlagzeugbelegung einer Spur zurücksetzen? Muse: Load Track's Drum Map MusE: Spur-Schlagzeugbelegung laden MusE: Store Track's Drum Map MusE: Spur-Schlagzeugbelegung speichern Midi Midi Drum Schlagzeug Do you want to use same port and channel for all instruments in the drummap? Möchten Sie für alle Instrumente der Drumbelegung den selben Anschluss und Kanal verwenden? Unused Devices Ungenutzte Geräte MusEGui::TempoSig Use mastertrack tempo Masterspur-Tempo verwenden Tempo/Sig Tempo/Taktmaß Mastertrack tempo at current position, or fixed tempo Mastertrack-Tempo an aktueller Position, oder festes Tempo Time signature at current position Taktmaß an aktueller Position MusEGui::TempoToolbar Use mastertrack tempo Masterspur-Tempo verwenden Mastertrack tempo at current position, or fixed tempo Mastertrack-Tempo an aktueller Position, oder festes Tempo Tap Tap this button to set tempo Set the tempo to the time difference of two successive clicks. Times out after two seconds. Tempo: Tempo: MusEGui::TempoToolbarWidget tempo at current position Tempo bei aktueller Position Tempo: Tempo: MusEGui::Toolbar1 Off Aus Solo Solo Cursor Cursor Snap Magnet MusEGui::TopWin As subwindow Als Unterfenster Shares tools and menu Teilt sich Toolbar und Menü Tabbed/Floating Display editor in a tab or in a separate window (preset in Global Settings->Editors). Fullscreen Vollbild Undo/Redo tools Rückgängig/Wiederholen-Werkzeuge Panic Panik Metronome Metronom Timeline Timeline Transport Transport Recording Aufnahme Sync Sync Song Position Song-Position As Subwindow Als Unterfenster Shares Tools and Menu Teilt sich Toolbar und Menü Tempo Tempo Signature Taktmaß Position Zeitposition Piano roll Pianoroll List editor Listeneditor Drum editor Schlagzeugeditor Master track editor Masterspur-Editor Master track list editor Masterspur-Listeneditor Wave editor Wave-Editor Clip list Cliplist Marker view Marker-Fenster Score editor Partitureditor Arranger Arrangierer <unknown toplevel type> <unbekannter Fenstertyp> MusEGui::TrackComment MusE: Track Comment MusE: Spur-Kommentar Track Comment: Spur-Kommentar: MusEGui::Transport Overdub Überschreiben Replace Ersetzen Rec Mode Modus "Aufnahme" Normal Normal Mix Mischen Cycle Rec Aufnahme in Schleife Punch in Aufnahme am linken Marker beginnen Punch out Aufnahme am rechten Marker beenden Rewind to Start Zum Start zurück Rewind Zurückspulen Forward Vorwärtsspulen Stop Stopp Play Wiedergabe Record Aufnahme Metronome on/off Metronom an/aus External sync on/off Externer Sync an/aus Jack Transport on/off Jack-Transport an/aus On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. punchin Aufnahme am linken Marker beginnen loop Schleife punchout Aufnahme am rechten Marker beenden Punch In Aufnahme am linken Marker beginnen Loop Schleife Punch Out Aufnahme am rechten Marker beenden Left Mark Linker Marker Right Mark Rechter Marker rewind to start Zum Start zurück Click this button to rewind to start position Klicken Sie auf diese Schaltfläche, um zur Startposition zurückzukehren rewind Einen Taktschlag rückwärts Click this button to rewind Klicken Sie auf diese Schaltfläche, um einen Taktschlag rückwärts zu springen forward Einen Taktschlag vorwärts Click this button to forward current play position Klicken Sie auf diese Schaltfläche, um einen Taktschlag vorwärts zu springen stop Stopp Click this button to stop playback Klicken Sie auf diese Schaltfläche, um die Wiedergabe zu stoppen play Wiedergabe Click this button to start playback Klicken Sie auf diese Schaltfläche, um die Wiedergabe zu starten record Aufnahme Click this button to enable recording Klicken Sie auf diese Schaltfläche, um in den Modus "Aufnahme" zu gelangen AC AC quantize during record Während Aufnahme quantisieren Click Klick metronom click on/off Metronom Klick an/aus Sync Sync external sync on/off Externer Sync an/aus Jack Jack Jack transport sync on/off Jack-Transport-Synchronisation an/aus Master Master use master track Masterspur einschalten MusEGui::VisibleTracks Show wave tracks Zeige Wave-Spuren Show group tracks Zeige Gruppen-Spuren Show aux tracks Zeige Aux-Spuren Show input tracks Zeige Eingangs-Spuren Show output tracks Zeige Ausgangs-Spuren Show midi tracks Zeige Midi-Spuren Show synth tracks Zeige Synth-Spuren Visible track types Sichtbare Spurtypen MusEGui::WaveCanvas Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help. Adjust Wave Offset Wave-Offset anpassen Wave offset (frames) Part creation failed Left and right position markers must be placed inside the current part. Part created The selected region has been copied to the clipboard and can be pasted in the arranger. MusE - external editor failed MusE - externer Editor fehlgeschlagen MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE konnte den externen Editor nicht starten. Überprüfen Sie, ob die Editor-Einstellung in: Globale Einstellungen -> Audio:Externer Waveeditor stimmt. MusE - file size changed MusE - Dateigröße geändert When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Wenn Sie im externen Editor editieren, sollten Sie die Dateigröße nicht verändern, weil die es in den ausgewählten Bereich passen muss. Fehlende Daten werden stummgeschaltet Wave event: Converter settings Konverter-Einstellungen MusEGui::WaveEdit &Edit &Bearbeiten Func&tions &Funktionen &Gain &Verstärkung Other Andere &Copy &Kopieren &Create Part from Region &Part aus Bereich erzeugen C&ut &Ausschneiden &Paste &Einfügen Edit in E&xternal Editor In e&xternem Editor bearbeiten Edit in E&xternal Editor... Im e&xternen Editor bearbeiten... Mute Selection Auswahl stummschalten Normalize Selection Auswahl normalisieren Fade In Selection Auswahl einblenden Fade Out Selection Auswahl ausblenden Reverse Selection Auswahl umkehren Select Auswählen Select &All Alles a&uswählen &Deselect All Alles ab&wählen &Previous Part Vorheriger &Part &Next Part &Nächster Part &Display &Anzeige Display menu: View-specific display options. Window &Config Fenster&konfiguration &Event Color &Eventfarbe &Part colors &Gray &Grau WaveEdit tools Werkzeuge "Wave-Edit" Solo Solo Cursor Cursor MusEGui::WaveView MusE - external editor failed MusE - externer Editor fehlgeschlagen MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE konnte den externen Editor nicht starten. Überprüfen Sie, ob die Editor-Einstellung in: Globale Einstellungen -> Audio:Externer Waveeditor stimmt. MusE - file size changed MusE - Dateigröße geändert When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Wenn Sie im externen Editor editieren, sollten Sie die Dateigröße nicht verändern, weil die es in den ausgewählten Bereich passen muss. Fehlende Daten werden stummgeschaltet OrganGuiBase MusE: Organ MusE: Orgel Drawbars Register 16' 16' 4' 4' 2 2/3' 2 2/3' 2' 2' 5 1/3' 5 1/3' 8' 8' Envelope Hi Hüllkurve Hoch Release Release Sustain Sustain Decay Decay Attack Attack ms ms cB cB Envelope Lo Hüllkurve Niedrig O-1 O-1 Oscillator Oszillator Brass Blech Reed Holz Flute Flöte Velocity Dynamik PasteDialogBase MusE: Paste Parts MusE: Parts einfügen Number and raster Anzahl und Raster insert füge times mal ein raster Raster Insert Einfügen Raster Raster ticks Ticks Move, Merge, Clone Verschieben, Vereinigen, Klonen Move everything to the right Alles nach rechts verschieben Move only affected parts to the right Nur betroffene Parts nach rechts verschieben Put everything into a single track Alles in einzelne Spur einfügen Merge with existing parts Mit existierenden Parts vereinigen Insert as clones (where possible) Als Klone einfügen (wenn möglich) OK Bestätigen Cancel Abbrechen PasteEventsDialogBase MusE: Paste Events MusE: Events einfügen Number and raster Anzahl und Raster insert Einfügen times mal ein raster Raster ticks Ticks Paste options Optionen fürs Einfügen &Always into existing parts &Immer in existierende Parts &Never into existing parts &Nie in existierende Parts &Into existing parts if part has not to be expanded by more than In &existierende Parts, wenn der Part erweitert werden muss um nicht mehr als Always into existing parts Immer in existierende Parts Never into existing parts Nie in existierende Parts Into existing parts if part has not to be expanded by more than In existierende Parts, wenn der Part erweitert werden muss um nicht mehr als Insert Einfügen Raster Raster Put everything into the (selected) part Alles in den (ausgewählten) Part einfügen Paste midi controller target options Midi-Kontroller Zieloptionen einfügen Keep e&xisting E&xistierende behalten Erase existin&g Existierende &löschen Erase WYSIWYG Löschen WYSIWYG Erase inclusive Löschen inklusiv OK Bestätigen Cancel Abbrechen PluginDialogBase Dialog Dialog Ports: Ports: Mono Mono Mono + Stereo Mono + Stereo Stereo Stereo All Alle Plugin type: Plugintyp: Filter: Filter: &OK &Bestätigen &Cancel &Abbrechen PluginSettings Dialog Dialog Options Optionen Fixed speed Feste Geschwindigkeit Transport affects audio latency Transport beeinflusst die Latenz Override reported audio latency Reported audio latency override value Revert HiDPI scaling of native GUI Set in Global settings->GUI tweaks Global setting Additional information Follow global setting On An Off Aus ProjectCreate Create Project Projekt erzeugen Projects folder: Projektverzeichnis: ... ...... Project Name: Projekt-Name: Project is a Template Projekt ist eine Vorlage Write window state Fenster schreiben Project song file type: Projekt-Lieddatei-Typ: Project Path to song file: Projektpfad zur Lieddatei: &Create project folder (recommended for audio projects) Erzeuge Projektverzeichnis (empfohlen für Audioprojekte) Create project folder (recommended for audio projects) Erzeuge Projektverzeichnis (empfohlen für Audioprojekte) Song information: Lied-Information: QObject Error Fehler Please first select the range for crescendo with the loop markers. Bitte wählen Sie erst den Bereich für das Crescendo mit den Bereichsmarkern aus. Controller ! Kontroller ! Other ! Andere ! Presets Voreinstellungen Switch on General Midi Level 1 mode Switch on General Midi Level 2 mode Switch off General Midi Level 1 or 2 Switch on Roland GS mode Switch on Yamaha XG mode Tracks: Spuren: Midi ports: Midi ports: Midi devices: Midigeräte: Jack: Jack: Jack midi: Jack-Midi: Instruments: Instrumente: Instruments Current device Aktuelles Gerät Others Andere Files Dateien Synthesizers Synthesizer Open Offen Closed Geschlossen <Properties> <Eigenschaften> <Base Property> <Base-Eigenschaft> <Property> <Eigenschaft> Empty Leer Input routing Eingangs-Signalfluss Warning: No input routes! Click to connect... Warnung: Keine Eingangsgeräte! Zum Verbinden klicken... Output routing Ausgangs-Signalfluss Warning: No output routes! Click to connect... Warnung: Keine Ausgangsgeräte! Zum Verbinden klicken... TRACK TAKE MusE import error. MusE failed to import the file. Possibly this wasn't a sound file? If it was check the permissions, MusE sometimes requires write access to the file. FX slot QWidget Cannot convert sysex string Sysex Kette nicht umwandelbar Hex String too long (2048 bytes limit) Hex Kette ist zu lang (Grenze 2048 Bytes) new Neu create peakfile for Pegelspitzendatei erzeugen für MusE: get file name MusE: Dateinamen übermitteln The directory %1 does not exist. Create it? Das Verzeichnis %1 existiert nicht. Erzeugen? MusE: create directory MusE: Verzeichnis erzeugen creating dir failed Verzeichnis erzeugen schlug fehl File %1 exists. Overwrite? Die Datei %1 existiert. Überschreiben? Open File %1 failed: %2 Die Datei "%1" konnte nicht geöffnet werden: %2 MusE: write MusE: schreiben Creating dir failed Verzeichnis erzeugen schlug fehl MusE: Open File MusE: Datei öffnen None Kein generic midi General Midi No wave events selected. Keine Wave-Events ausgewählt. No selection. Ignoring Keine Auswahl. Ignoriert Instrument-defined Instrumenten-definiert Generic midi Generisches Midi Edit instrument ... Instrument bearbeiten ... Add Hinzufügen Others Andere Common Controls Gemeinsame Controller Velocity Velocity Create peakfile for Pegelspitzendatei erzeugen für New Neu QuantBase MusE: Quantize MusE: Quantisieren Range Bereich All Events Alle Ereignisse Selected Events Ausgewählte Ereignisse Looped Events Ereignisse innerhalb Schleife Selected Looped Ausgewählt und innerhalb der Schleife Parts Parts Selected parts or c&urrent part Aus&gewählte Parts oder aktueller Part All parts &in editor &Alle Parts im Editor Values Werte Strength: Stärke: % % Threshold (ticks): Schwellwert (Ticks): Quantize Len Länge quantisieren Raster Raster Whole Ganze Half Halbe 4th Viertel 4th Triplet Triolenviertel 8th Achtel 8th Triplet Triolenachtel 16th Sechzehntel 16th Triplet Triolensechzehntel 32th 32tel 32th Triplet Triolen-32tel Swing: Swing: If the proposed change in tick or length is smaller than threshold, nothing is done. If swing=0, this is normal If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. Wenn die Änderung der Position oder der Länge kleiner als Schwelle ist, wird nichts getan. Wenn Swing=0, wird normal quantisiert. Wenn Swing=33, erhalten Sie einen 2:1-Rhythmus. Wenn Swing=-33, erhalten Sie einen 1:2-Rhythmus. OK Bestätigen Cancel Abbrechen RemoveBase MusE: Erase Notes MusE: Noten löschen Range Bereich All Events Alle Ereignisse Selected Events Ausgewählte Ereignisse Looped Events Ereignisse innerhalb Schleife Selected Looped Ausgewählt und innerhalb der Schleife All Eve&nts &Alle Ereignisse Selec&ted Events Aus&gewählte Ereignisse &Looped Events Ereignisse innerhalb &Schleife Selected L&ooped Ausgewählt und &innerhalb der Schleife Parts Parts Selected parts or c&urrent part Ausgewählte &Parts oder aktueller Part All parts &in editor Alle Parts im &Editor Thresholds Schwellenwerte ticks Ticks Velocity Dynamik Length Länge <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wenn nichts ausgewählt ist, wird alles gelöscht.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wenn "Dynamik" ausgewählt ist, werden nur Noten mit Dynamik &lt; Schwelle gelöscht.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Wenn beide ausgewählt sind, werden Noten mit Dynamik &lt; Schwelle ODER Länge &lt; Schwelle gelöscht.</p></body></html> OK Bestätigen Cancel Abbrechen RhythmGenerator Rhythmusgenerator RouteDialogBase MusE: Routing MusE: Signalfluss List of available sources. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Connections View window. Shows all current connections. Thick lines are Omni Routes. Thin lines are Channel Routes. List of available destinations. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Itemized list of current connections. Show only selected sources Nur ausgewählte Quellen anzeigen Show only destination routes for selected source Show only selected destinations Nur ausgewählte Ziele anzeigen Show only source routes for selected destination Show all Midi Ports Alle Midi-Ports anzeigen Auto adjust column size Spaltenbreite automatisch anpassen Automatically adjusts the source and destination tree widths when the splitters are adjusted. This also turns on text word wrap, which may cause slower response with larger lists. Preferred route name or alias Bevorzugter Signalflussname oder Alias Connect source to destination Quelle mit Ziel verbinden Remove selected route Ausgewählten Signalfluss entfernen Add Route Signalfluss hinzufügen Source: Quelle: Destination: Ziel: Connect Verbinden connect source to destination Quelle mit Ziel verbinden Current Routes Aktueller Signalfluss Source Quelle Destination Ziel Remove Entfernen remove selected route Ausgewählten Signalfluss entfernen RubberbandSettingsBase Rubberband Settings Rubberband support is not enabled Use default settings Presets: Voreinstellungen: Default Percussion Maximum Stretch: Elastic Precise Window: Standard Standard Short Long Smoothing: Off Aus On An Channels: Apart Together Transients: Crisp Mixed Smooth Detector: Compound Percussive Soft Phase: Laminar Independent Formant: Shifted Preserved Pitch: Tonhöhe: High speed High quality High consistency OK Bestätigen Cancel Abbrechen SRCResamplerSettingsBase SRC Resampler Settings Use default settings Converter Type: SINC Best quality SINC Medium quality SINC Fastest Zero Order Hold Linear OK Bestätigen Cancel Abbrechen SS_PluginChooserBase SimpleDrums - Ladspa Plugin Chooser SimpleDrums - Ladspa Plugin Wähler Name Name Label Beschriftung Inports Eingangsports Outports Ausgangsports Creator Ersteller &Cancel &Abbrechen Alt+C Alt+C &OK &Bestätigen Alt+O Alt+O SS_PluginFront Clear and unload effect Lösche und entlade den Effekt Load effect Lade Effekt Toggle display of effect parameters Schalte Anzeige von Effektparametern um Turn effect on/off Schalte Effekt an/aus SetlenBase MusE: Set Note Length MusE: Feste Notenlänge setzen Range Bereich All Events Alle Ereignisse Selected Events Ausgewählte Ereignisse Looped Events Ereignisse innerhalb Schleife Selected Looped Ausgewählt und innerhalb der Schleife Parts Parts Selected parts or c&urrent part Aus&gewählte Parts oder aktueller Part All parts &in editor &Alle Parts im Editor Value Wert New length Neue Länge ticks Ticks OK Bestätigen Cancel Abbrechen ShortcutCaptureDialogBase Enter shortcut sequence Folge von Tastenkürzeln eingeben Press keys to enter shortcut sequence! Tasten drücken um Folge von Tastenkürzeln einzugeben! Old shortcut: Altes Tastenkürzel: Undefined unbestimmt New shortcut: Neues Tastenkürzel: OK Bestätigen Cancel Abbrechen ShortcutConfigBase Configure Keyboard Shortcuts Tastenkürzel einstellen Shortcut Category Kategorie Tastenkürzel Description Beschreibung Shortcut Tastenkürzel Filter: Filter: &Reset all &Clear &Löschen Alt+C Alt+C &Define &Bestimmen Alt+D Alt+D &Apply &Anwenden Alt+A Alt+A &Printable file... &Druckbare Datei... Alt+P Alt+P &Ok &Bestätigen Alt+O Alt+O SimpleDrumsGuiBase DrumSynth 0.1 DrumSynth 0.1 Audio file assignments. Within parantesis: note number and traditional midi drum name SimplePluginChooserBase SimpleDrums - Ladspa Plugin Chooser SimpleDrums - Ladspa Plugin Wähler Name Name Label Beschriftung Inports Eingangsports Outports Ausgangsports Creator Ersteller &Cancel &Abbrechen Alt+C Alt+C &OK &Bestätigen Alt+O Alt+O SimpleSynthGui Mix Mix Chn Chn Channel routing Kanal-Signalfluss &Load setup Setup &laden &Save setup Setup &speichern Load sample dialog Sample-Laden-Dialog SnooperDialogBase Snooper Mode Auto-hide Separate parented top levels Update tree Mouse clicks Key presses Flash timer s Reset Filters MusEGui QWidget Properties Eigenschaften Search: Class name: Object name: About This tool helps developers and designers discover objects. Click on anything anywhere or press a key to highlight an item. It will NOT auto-update when new objects are created. Click 'Update tree'. When open, may slow program slightly. This dialog is NOT shown. Class name::Object name Property Type Typ Value Wert Event type SongInfo Song Information Lied-Information Show on song load Zeige beim Laden &Cancel &Abbrechen Alt+C Alt+C &Ok &Bestätigen Alt+O Alt+O SynthConfigBase Soft Synthesizer Software-Synthesizer File Datei Instances Instanzen Name Name list of available software synthesizers Liste verfügbarer Software-Synthesizer Add Instance Instanz hinzufügen Remove Instance Instanz entfernen Midi Port Midi Anschluss Midi Port and Soft Synth Configuration MIDI-Port- und SoftSynth-Konfiguration Type Typ Midi connections Midi Verbindungen Configure midi devices, midi ports, and synthesizers MIDI-Port- und SoftSynth-Konfiguration Available soft synthesizers Liste verfügbarer Software-Synthesizer List of available software synthesizers Inst Inst Version Version Description Beschreibung Useable devices Verwendbare Geräte Add: Hinzufügen: Synth Synth ALSA JACK Rename Umbenennen Remove Entfernen Device port assignments Geräteports-Zuordnungen &Apply &Anwenden &OK &Bestätigen TransposeBase MusE: Transpose MusE: Transponieren Range Bereich All Events Alle Ereignisse Selected Events Ausgewählte Ereignisse Looped Events Ereignisse innerhalb Schleife Selected Looped Ausgewählt und innerhalb der Schleife Parts Parts Selected parts or c&urrent part Aus&gewählte Parts oder aktueller Part All parts &in editor &Alle Parts im Editor Value Wert Halftone-steps Halbtonschritte OK Bestätigen Cancel Abbrechen UnusedWaveFiles Dialog Dialog List of unused audio files in current project directory: Liste aller unbenutzen Audiodateien im Projektverzeichnis: Current project Momentanes Projekt All .med files in current directory Alle .med-Dateien im momentanen Verzeichnis Move files to 'unused' subdir Verschiebe Dateien ins "unused"-Unterverzeichnis Cancel Abbrechen VAMGui MusE: Load VAM Presets MusE: VAM-Voreinstellungen laden MusE: Save VAM Presets MusE: VAM-Voreinstellungen speichern VAMGuiBase Virtual Analogue for MusE Virtueller Analoger für MusE LFO LFO Freq Freq Waveform Wellenform Sine Sinus Pulse Rechteck Saw Sägezahn Triangle Dreieck Filter Filter EnvMod EnvMod Attack Attack Decay Decay Sustain Sustain Release Release Cutoff Filterfrequenz Resonance Resonanz Invert Invertieren KeyTrack Presets Voreinstellungen Set Set load preset list Voreinstellungsliste laden save preset list Voreinstellungsliste speichern save preset list to a new file Voreinstellungsliste in einer neuen Datei speichern delete preset Voreinstellung löschen DCO 1 DCO 1 Pitch Tonhöhe Detune Verstimmung PWM PWM FM FM PW PW DCO 2 DCO2 On An VAM 1.0beta3 Virtual Analog for MusE Released under GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VAM 1.0beta3 Virtual Analog for MusE Veröffentlicht unter der GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VelocityBase MusE: Modify Velocity MusE: Anschlagsdynamik ändern Range Bereich All Events Alle Ereignisse Selected Events Ausgewählte Ereignisse Looped Events Ereignisse innerhalb Schleife Parts Parts Selected parts or c&urrent part Aus&gewählte Parts oder aktueller Part All parts &in editor &Alle Parts im Editor Values Werte Rate: Rate: Offset: Offset: % % Selected Looped Ausgewählt und innerhalb der Schleife veloNew = (veloOld * rate) + offset dynNeu = (dynAlt * rate) + offset OK Bestätigen Cancel Abbrechen ZitaResamplerSettingsBase Zita Resampler Settings Zita Resampler support is not enabled Use default settings Options: OK Bestätigen Cancel Abbrechen file_patterns Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) All Files (*) Alle Dateien (*) Midi (*.mid) Midi (*.mid) Karaoke (*.kar) Karaoke (*.kar) all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) Alle bekannten Dateien (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) med Files (*.med *.med.gz *.med.bz2) med Files (*.med *.med.gz *.med.bz2) Uncompressed med Files (*.med) Unkomprimierte med-Dateien (*.med) gzip compressed med Files (*.med.gz) Mit gzip komprimierte med-Dateien (*.med.gz) bzip2 compressed med Files (*.med.bz2) Mit bzip2 komprimierte med-Dateien (*.med.bz2) mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) Midi-Dateien (*.mid *.midi *.kar *.MID *.MIDI *.KAR) (*.jpg *.gif *.png) (*.jpg *.gif *.png) (*.jpg) (*.jpg) (*.gif) (*.gif) (*.png) (*.png) part Files (*.mpt *.mpt.gz *.mpt.bz2) Part-Dateien (*.mpt *.mpt.gz *.mpt.bz2) part Files (*.mpt) Part-Dateien (*.mpt) gzip compressed part Files (*.mpt.gz) Mit gzip komprimierte Part-Dateien (*.mpt.gz) bzip2 compressed part Files (*.mpt.bz2) Mit bzip2 komprimierte Part-Dateien (*.mpt.bz2) Presets (*.pre *.pre.gz *.pre.bz2) Voreinstellungen (*.pre *.pre.gz *.pre.bz2) Presets (*.pre) Voreinstellungen (*.pre) gzip compressed presets (*.pre.gz) mit gzip komprimierte Voreinstellungen (*.pre.gz) bzip2 compressed presets (*.pre.bz2) mit bzip2 komprimierte Voreinstellungen (*.pre.bz2) Presets (*.map *.map.gz *.map.bz2) Schlagzeugbelegungen (*.map *.map.gz *.map.bz2) Presets (*.map) Schlagzeugbelegungen (*.map) gzip compressed presets (*.map.gz) mit gzip komprimierte Schlagzeugbelegungen (*.map.gz) bzip2 compressed presets (*.map.bz2) mit bzip2 komprimierte Schlagzeugbelegungen (*.map.bz2) Wave/Binary (*.wav *.ogg *.flac *.bin) Wave (*.wav *.ogg *.flac) Color configuration files (*.cfc) Farbenkonfigurationsdateien (*.cfc) Qt style sheets (*.qss) Qt-Stylesheets (*.qss) Wave/Binary (*.wav *.ogg *.bin) Wave/Binär (*.wav *.ogg *.bin) Wave (*.wav *.ogg) Wave (*.wav *.ogg) Binary (*.bin) Binär (*.bin) freeverb FreeVerb FreeVerb Room Size Raumgröße Damping Dämpfung Wet Level Effektstärke Tap-Reverberator Tap-Reverberator Decay [ms] Abschwellzeit [ms] dB dB Dry Level [dB] Lautstärke des unmodifizierten Signals (Dry Level) [dB] Wet Level [dB] Lautstärke des modifizierten Signals (Wet Level) [dB] Preset: Vorlage: AfterBurn AfterBurn AfterBurn (Long) AfterBurn (Lang) Ambience Ambience Ambience (Thick) Ambience (Fett) Ambience (Thick) - HD Ambience (Fett) - HD Cathedral Kathedrale Cathedral - HD Kathedrale - HD Drum Chamber Schlagzeugraum Garage Garage Garage (Bright) Garage (Hell) Gymnasium Sporthalle Gymnasium (Bright) Sporthalle (Hell) Gymnasium (Bright) - HD Sporthalle (Hell) -HD Hall (Small) Halle (Klein) Hall (Medium) Halle (Mittel) Hall (Large) Halle (Groß) Hall (Large) - HD Halle (Groß) - HD Plate (Small) Plate (Klein) Plate (Medium) Plate (Mittel) Plate (Large) Plate (Groß) Plate (Large) - HD Plate (Groß) - HD Pulse Chamber Pulse Chamber (Reverse) Resonator (96 ms) Resonator (152 ms) Resonator (208 ms) Room (Small) Raum (Klein) Room (Medium) Raum (Mittel) Room (Large) Raum (Groß) Room (Large) - HD Raum (Groß) - HD Slap Chamber Slap Chamber - HD Slap Chamber (Bright) Slap Chamber (Bright) HD Smooth Hall (Small) Smooth Hall (Medium) Smooth Hall (Large) Smooth Hall (Large) - HD Vocal Plate Vocal Plate - HD Warble Chamber Warehoouse Warehouse - HD Comb Filters Comb-Filter Allpass Filters Allpass-Filter Bandpass Filters Bandpass-Filter Enhanced Stereo Erweitertes Stereo main File to open Alsa midi only (using dummy audio driver) Use RtAudio driver Use JAckAudio driver to connect to Jack audio server Do not try to auto-start the Jack audio server Do not auto-populate midi ports with midi devices found, at startup Force inclusion of ALSA midi even if using Jack Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) Force midi real time priority to n (default: audio driver prio -1) Force plugin cache re-creation. (Automatic if any plugin path directories changed.) Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care. Don't load LADSPA plugins Don't load MESS plugins Don't load VST plugins Don't load LinuxVST plugins Don't load DSSI plugins Don't load LV2 plugins Don't use LASH Force locale to the given language/country code (xx = Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins. Debug mode: no threads, no RT Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively! Debug mode: trace midi Input Debug mode: trace midi Output Debug mode: trace sync Enable Python control support Pyro nameserver host name Pyro nameserver host port Pyro daemon host name Pyro daemon host port Pyro communication timeout in seconds midiWarnInitPendingBase Instrument initialization Instrument-Initialisierung MusE should now send some Instrument Initialization Sequences. The sequences (usually System Exclusive messages) are defined by the selected instruments in the Settings -> Midi Ports dialog, such as the GM (default), GS, or XG instruments. Typically you should answer yes here. You can always do it manually from the Midi menu. Continue? Don't ask me again Nicht mehr fragen shortcuts Transport: Start playback from current location Transport: Abspielen ab momentaner Position Transport: Toggle metronome Transport: Metronom ein-/ausschalten Transport: Stop Playback Transport: Stopp Transport: Goto Start Transport: Zum Anfang gehen Transport: Goto End Transport: Zum Ende gehen Transport: Play, Stop, Rewind Transport: Start, Stopp, Zurückspulen Transport: Restart recording Transport: Aufnahme neu starten Transport: Restart recording (multi take) Transport: Aufnahme neu starten (multi take) Transport: Goto left marker Transport: Zum linken Marker gehen Transport: Goto right marker Transport: zum rechten Marker gehen Transport: Toggle Loop section Transport: Schleife umschalten Transport: Toggle Record Transport: Aufnahme ein-/ausschalten Transport: Arm current track Transport: Aktuellen Track scharfschalten Transport: Clear all rec enabled tracks Transport: Alle Aufnahmemarkierungen zurücksetzen Toggle fullscreen Vollbild umschalten Toggle tabbed/floating editor window Toggle docks display Toggle rewind on stop Zurückspulen beim Stop umschalten Show Arranger window Edit: Copy Bearbeiten: Kopieren Edit: Copy in range Bearbeiten: Im Bereich kopieren Edit: Undo Bearbeiten: Rückgängig Edit: Redo Bearbeiten: Wiederholen Edit: Cut Bearbeiten: Ausschneiden Edit: Paste Bearbeiten: Einfügen Edit: Paste to current part Bearbeiten: Einfügen in aktuellen Part Edit: Paste to selected track Bearbeiten: Einfügen in ausgewählten Track Edit: Paste clone Bearbeiten: Als Klone einfügen Edit: Paste clone to selected track Bearbeiten: Als Klone in ausgewählten Track einfügen Edit: Paste (with dialog) Bearbeiten: Einfügen (Dialog zeigen) Edit: Delete Bearbeiten: Löschen File: New project Datei: Neues Projekt File: New project from template Datei: Neues Projekt aus Vorlage File: Open from disk Datei: Öffnen File: Save project Datei: Speichern File: Open recent file Datei: Letzt Datei öffnen File: Save as Datei: Speichern unter File: Load template Datei: als Vorlage laden File: Import midi file Datei: MIDI-Datei importieren File: Export midi file Datei: MIDI-Datei exportieren File: Import midi part Datei: Midipart importieren File: Import audio file Datei: Audiodatei importieren File: Quit MusE Datei: Beenden Edit: Rename part Bearbeiten: Part umbenennen Edit: Select parts on track Bearbeiten: Parts auf der Spur auswählen Open pianoroll Piano Roll öffnen Open drumeditor Schlagzeugeditor öffnen Open listeditor Listeneditor öffnen Open waveeditor Wave-Editor öffnen Open graphical mastertrack editor Graphischen Masterspureditor öffnen Open list mastertrack editor Listen-Masterspureditor öffnen Open midi transformer Midi-Transformer öffnen Open track Synth GUI Open pianoroll in new window Open drumeditor in new window Open listeditor in new window Open waveeditor in new window Add midi track Midispur hinzufügen Add drum track Schlagzeugspur hinzufügen Add new style drum track New-style Schlagzeugspur hinzufügen Add wave track Wavespur hinzufügen Add audio output Audioausgang hinzufügen Add audio group Audiogruppe hinzufügen Add audio input Audioeingang hinzufügen Add audio aux Audio Aux hinzufügen Insert midi track Insert drum track Insert wave track Insert audio output Insert audio group Insert audio input Insert audio aux Structure: Global cut Struktur: Globaler Schnitt Structure: Global insert Struktur: Globales Einfügen Structure: Global split Struktur: Globales Teilen Structure: Cut events Struktur: Events ausschneiden View: Open mixer #1 window Zeigen: Mixerfenster 1 anzeigen View: Open mixer #2 window Zeigen: Mixerfenster 2 anzeigen View: Toggle transport window Zeigen: Transportfenster umschalten View: Toggle bigtime window Zeigen: Große Zeitanzeige umschalten View: Open marker window Zeigen: Markerfenster umschalten View: Toggle mixer strip display Settings: Follow song by page Einstellungen: Dem Lied seitenweise folgen Settings: Follow song off Einstellungen: Dem Lied nicht folgen Settings: Follow song continuous Einstellungen: Dem Lied ständig folgen Settings: Global configuration Einstellungen: Globale Konfiguration Settings: Configure shortcuts Einstellungen: Tastenkürzel einstellen Settings: Configure metronome Einstellungen: Metronom einstellen Settings: Midi sync configuration Einstellungen: Midi Sync Konfiguration Settings: Midi file import/export configuration Einstellungen: Midi Import/Export-Konfiguration Settings: Appearance settings Einstellungen: Erscheinungsbild einstellen Settings: Midi ports / Soft Synth Einstellungen: Midianschlüsse / Software Synthesizer Settings: Audio subsystem configuration Einstellungen: Audiosystem einstellen Midi: Edit midi instruments Midi: Midiinstrumente bearbeiten Midi: Open midi input transform Midi: Midi-Eingangstransformation öffnen Midi: Open midi input filter Midi: Midi-Eingangsfilter öffnen Midi: Midi input transpose Midi: Midieingang transponieren Midi: Midi remote control Midi: Midi-Fernbedienung Midi: Random rhythm generator Midi: Zufälliger-Rhythmus-Generator Midi: Reset midi Midi: Zurücksetzen Midi: Init midi Midi: Initialisieren Midi: Midi local off Midi: Lokal aus Audio: Bounce audio to track Audio: Auf Spur abmischen Audio: Bounce audio to file Audio: In Datei abmischen Audio: Restart audio Audio: Neustarten Automation: Mixer automation Automatisierung: Mixerautomatisierung Automation: Take mixer snapshot Automatisierung: Mixer-Schnappschuss aufnehmen Automation: Clear mixer automation Automatisierung: Mixerautomatisierung löschen Help: Open Manual Hilfe: Handbuch öffnen Help: Toggle whatsthis mode Hilfe: "Was ist das?" Edit: Edit selected part Bearbeiten: Markierten Part bearbeiten Edit: Select nearest part on track above Bearbeiten: Nächsten Part auf vorheriger Spur markieren Edit: Add nearest part on track above Bearbeiten: Nächsten Part auf vorheriger Spur hinzufügen Edit: Select nearest part on track below Bearbeiten: Nächsten Part auf nächster Spur markieren Edit: Add nearest part on track below Bearbeiten: Nächsten Part auf nächster Spur hinzufügen Edit: Insert empty measure Bearbeiten: Leeren Takt einfügen Edit: Paste as clones Bearbeiten: Als Klone einfügen Edit: Paste as clones (with dialog) Bearbeiten: Als Klone einfügen (Dialog zeigen) Edit: Duplicate track Bearbeiten: Track duplizieren Select track above Vorherige Spur auswählen Select track below Nächste Spur auswählen Edit selected track name Namen des ausgewählten Tracks bearbeiten Mute all currently selected tracks Alle aktuell ausgewählten Tracks stummschalten Solo all currently selected tracks Für alle aktuell ausgewählten Tracks Solofunktion aktivieren Midi: Transpose Midi: Transponieren Edit: Select all Bearbeiten: Alles auswählen Edit: Select none Bearbeiten: Nichts auswählen Edit: Invert Selection Bearbeiten: Auswahl invertieren Edit: Select events/parts inside locators Bearbeiten: Alles im Bereich auswählen Edit: Select events/parts outside locators Bearbeiten: Alles außerhalb des Bereichs auswählen Edit: Select previous part Bearbeiten: Vorherigen Part auswählen Edit: Select next part Bearbeiten: Nächsten Part auswählen Edit: Select nearest part/event to the left or move cursor Bearbeiten: Nächsten Part / nächstes Event links auswählen, oder Cursor verschieben Edit: Add nearest part/event to the left to selection Bearbeiten: Nächsten Part / nächstes Event links zur Auswahl hinzufügen Edit: Select nearest part/event to the right or move cursor Bearbeiten: Nächsten Part / nächstes Event rechts auswählen, oder Cursor verschieben Edit: Add nearest part/event to the right to selection Bearbeiten: Nächsten Part / nächstes Event rechts zur Auswahl hinzufügen Edit: Set locators to selection Bearbeiten: Bereich von Auswahl setzen Edit: Increase pitch Bearbeiten: Tonhöhe erhöhen Edit: Decrease pitch Bearbeiten: Tonhöhe erniedrigen Edit: Increase event position Bearbeiten: Eventposition erhöhen Edit: Decrease event position Bearbeiten: Eventposition erniedrigen View: Zoom in Zeigen: Hereinzoomen View: Zoom out Zeigen: Herauszoomen View: Goto Current Position Zeigen: Zur momentanen Position gehen View: Scroll left Zeigen: Nach links scrollen Transport: Step record Transport: Step-Aufnahme Transport: Midi input Transport: Midi-Eingabe Transport: Play events Transport: Events wiedergeben Edit: Increase velocity Bearbeiten: Velocity erhöhen Edit: Decrease velocity Bearbeiten: Velocity reduzieren Edit: Set Fixed Length on Midi Events Bearbeiten: feste Länge für Midi-Events setzen Quantize Quantisieren Modify Note Length Notenlänge verändern Modify Velocity Anschlagsdynamik ändern Edit: Crescendo Bearbeiten: Crescendo Edit: Thin Out Bearbeiten: Ausdünnen Edit: Erase Event Bearbeiten: Event löschen Edit: Delete Overlaps Bearbeiten: Überlappende Noten entfernen Edit: Note Shift Bearbeiten: Noten verschieben Edit: Move Clock Bearbeiten: Uhr verschieben Edit: Copy Measure Bearbeiten: Takt kopieren Edit: Erase Measure Bearbeiten: Takt löschen Edit: Delete Measure Bearbeiten: Takt löschen Edit: Create Measure Bearbeiten: Takt erstellen Edit: Change Event Color Bearbeiten: Eventfarbe ändern Move: Move to selected note Bewegen: Zur ausgewählten Note bewegen Tool: Pointer Werkzeug: Zeiger Tool: Pencil Werkzeug: Stift Tool: Eraser Werkzeug: Radierer Tool: Pan Werkzeug: Schwenken Tool: Zoom Werkzeug: Zoom Tool: Line Draw Werkzeug: Linie Tool: Cursor Werkzeug: Cursor Add note velocity 1 Note mit Anschlagsdynamik 1 hinzufügen Add note velocity 2 Note mit Anschlagsdynamik 2 hinzufügen Add note velocity 3 Note mit Anschlagsdynamik 3 hinzufügen Add note velocity 4 Note mit Anschlagsdynamik 4 hinzufügen Cursor step size: larger Cursorschrittweite: größer Cursor step size: smaller Cursorschrittweite: kleiner Instrument/Cursor up Instrument/Cursor nach oben Instrument/Cursor down Instrument/Cursor nach unten Tool: Range Werkzeug: Bereich Tool: Stretch Werkzeug: Stretch Tool: Sample rate Werkzeug: Samplingrate Tool: Scissor Werkzeug: Schere Tool: Glue Werkzeug: Kleber Tool: Mute Werkzeug: Stummschalten Transport: Increase current position Transport: Momentane Position erhöhen Transport: Decrease current position Transport: Momentane Position erniedrigen Transport: Increase current position, no snap Transport: Momentane Position erhöhen, kein Einrasten Transport: Decrease current position, no snap Transport: Momentane Position erniedrigen, kein Einrasten Quantize: Set quantize to off Quantize: Set quantize to 1/1 note Quantisieren: Ganze Noten Quantize: Set quantize to 1/2 note Quantisieren: Halbe Noten Quantize: Set quantize to 1/4 note Quantisieren: Viertelnoten Quantize: Set quantize to 1/8 note Quantisieren: Achtelnoten Quantize: Set quantize to 1/16 note Quantisieren: 16telnoten Quantize: Set quantize to 1/32 note Quantisieren: 32telnoten Quantize: Set quantize to 1/64 note Quantisieren: 64telnoten Quantize: Toggle triol quantization Quantisieren: Triolenquantisierung umschalten Quantize: Toggle punctuation quantization Quantisieren: punktierte Quantisierung umschalten Quantize: Toggle punctuation quantization (2) Quantisieren: punktierte Quantisierung umschalten (2) Edit: Insert at location Bearbeiten: Bei Position einfügen Edit: Increase length Bearbeiten: Länge erhöhen Edit: Decrease length Bearbeiten: Länge erniedrigen Insert Note Note einfügen Insert SysEx SysEx einfügen Insert Ctrl Ctrl einfügen Insert Meta Meta einfügen Select strip to the left Select strip to the right Mixer strip volume down Mixer strip volume up Mixer strip pan left Mixer strip pan right Mixer strip volume down page Mixer strip volume up page Mixer strip pan left page Mixer strip pan right page Insert Channel Aftertouch Channel Aftertouch einfügen Insert Key Aftertouch Key Aftertouch einfügen Insert Tempo Tempo einfügen Insert Signature Taktmaß einfügen Change Event Position Eventposition verändern Edit Event Value Eventwert bearbeiten Insert Key Tonart einfügen Settings: Midi ports / Soft synths Goto Next Marker Zum nächsten Marker gehen Goto Prev Marker Zum vorherigen Marker gehen Normalize Normalisieren warnBadTimingBase Bad timing Schlechter Taktgeber Message here Don't warn me again Nicht mehr warnen muse-4.0.0/src/share/locale/muse_en.ts000066400000000000000000027724551404105311000176320ustar00rootroot00000000000000 @default <none> AboutBox AboutBox MusE (C) Copyright 1999-2020 the MusE development team. See https://muse-sequencer.github.io for new versions and more information. Published under the GNU Public License. Internals System information (useful for debugging): &Keep On Rocking! Alt+K AppearanceDialogBase MusE: Appearance settings Arranger Parts y-stretch Events note on poly pressure controller aftertouch pitch bend program change special RMS/PEAK (&traditional) Background picture Colors Items Palette B S H V G R Color name: Global opacity Show names Show e&vents Show Ca&kewalk style Wave Drawing in Parts O&nly outline Add Remove Clear Show snap grid Choose color... Add to palette Save all... Load all... ... Fonts Family Font 1 Font 2 Font 3 Bold Current theme If a user style sheet for one of the MusE color schemes exists and is selected, it will be merged with the default style sheet for this scheme. The user style sheet takes precedence and overwrites identical settings in the default style sheet. Cascade default and user theme style sheets Show icons in menus Use native standard dialogs Italic Font 4 Font 5 Themes Icon size Custom cursor size Distribute current system font family to all other fonts System Font sizes for fonts 1-6 are determined at every program start, to optimally match the currently used system font. Disable if you want to set fixed sizes in the font settings below. Default scaling factors: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% Calculate and apply relative font sizes dynamically at program start Font 6 Maximum aliased size, 0 = no alias Insert Size (pt) At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. Fill in optimal font sizes in relation to the system font Help Apply Ok Cancel ArrangerColumnsBase Configure arranger columns Columns: Name: Controller type: Midi controller type H-Ctrl Midi controller number high byte L-Ctrl Midi controller number low byte * wild card Affect CCs at begin of song current position &Add &Delete Done AudioConverterSettingsBase Audio Converter Settings Available audio converters Offline settings... Realtime settings... Gui settings... Use default preferences (see Global Settings) Preferences When only samplerate conversion is required, use: When time stretching or pitch shifting is required, use: OK Cancel Awl::MidiVolEntry off Awl::VolEntry off ChooseSysexBase Dialog Hex: Comment: &OK &Cancel ClipListEditorBase MusE: ClipList Name Refs Samplerate Len Data Status Clip Properties Pos: Len: CommentBase Form1 Track Comment Track 1 ConfigMidiFileBase MusE: Config Midi File Import/Export Split tracks into &parts Alt+P Split tracks into parts, or one single part Import Default instrument: Device Name metas trump Port metas if both exist Instrument Name metas trump Mode sysexes if both exist 96 192 384 Enable extended smf format (currently not implemented) Running Status saves space by not repeating event status bytes. If this is off every event will have a status byte, increasing file size. Use Running &Status To turn a note off some devices can use note-offs or zero-velocity note-ons or both. When used with Running Status this setting saves space. It is safe to leave this off. Replace note-offs with &zero velocity note-ons Use &2-byte time signatures instead of standard 4 If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum note controllers. If disabled, drum maps are ignored during export. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. See "What's This ?" for more info. If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum controllers. Any Port overrides cause a separate track to be created (requires format 1). Any Channel overrides will still appear in the same track in the exported file, but upon re-import into an app such as MusE, MusE will treat that as a new track and create one. If disabled, drum maps are ignored. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. Upon re-importing into another app, it is up to the other app to redirect the particular drum ports, channels and notes to make the song sound like it was intended. Drum map overrides are unique to MusE. If re-importing into MusE, for faithful reproduction disable this setting and apply a drum map and some overrides later. If re-importing or opening in another midi player, enable this setting so that the player can have a chance at faithful reproduction. Some user adjustment may be required (choice of instruments, ports, etc). Apply drum map Port, Channel, ANote overrides If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. See "What's This ?" for more info. If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. The extra track is automatically created. In this mode all events on a track in the midi file are on the same channel. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. In this mode the track events can be on different channels. But if the file is re-imported into an app such as MusE, MusE will automatically create the extra track. Drum map Channel overrides go to a separate track Export instrument or mode: Mode sysexes Instrument name metas Port metas Device name metas Export a Port/Device meta for format 0 Copyright: Format: Note: Format 0 uses the FIRST midi track's name/comment in the arranger Division: 0 (single track) Export 1 (multiple tracks) Export port or device metas: &OK &Cancel CopyOnWriteDialogBase Copy Wave Files Some sound files will be copied to the Project Directory, either because they are not writable or because more than one independent Wave Event shares them. (If you prefer instead that the Wave Events are inter-dependent, try using Clone Parts.) Multiple copies will be made in some cases. If no Project has been created yet, you will be asked to, giving another chance to cancel. These files will be copied to the Project Directory: CrescendoBase MusE: Crescendo/Decrescendo Range &Looped Events Selected Looped Parts Selected parts or c&urrent part All parts &in editor Values Start velocity % End velocity &Absolute Rela&tive OK Cancel DeicsOnzeGui Save configuration Critical Error Cannot open file %1 Parsing error for file %1 Load category dialog Load set dialog Save set dialog New category Delete category Load category Save category Load set Save set Delete set New subcategory Delete subcategory Load subcategory Save subcategory New preset Delete preset Load preset Save preset No more category supported You can not add more categories Do you really want to delete %1 ? &Yes &No No category selected You must first select a category. Replace or add %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? &Replace &Add Download error There is no more free category slot. Save category dialog No more subcategory supported You can not add more subcategories No subcategory selected You must first select a subcategory. Load subcategory dialog %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? There is no more free subcategory slot. Save subcategory dialog No more preset supported You can not add more presets No preset selected You must first select a preset. Load preset dialog %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? There is no more free preset slot. Save preset dialog Browse set dialog Browse image dialog DeicsOnzeGuiBase DeicsOnze &Preset Program numerous INITVOICE LBank Subcategory Bank numerous NONE HBank Category Prog Preset DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. &Global Pitch Envelope PL3 PL2 PL1 PR1 PR2 PR3 Pitch modulation depth LFO LFO Sync Pitch modulation sensitivity Pitch Modulation Sensitivity LFO Delay LFO delay LFO speed Amplitude modulation depth Amplitude modulation sensitivity Amplitude Modulation Sensitivity AMS LFO Waveform Pitch Modulation Depth PMD LFO Speed AMD Speed Delay PMS Modulation Matrix <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> Op4 Feedback Feedback level of the operator 4 Transpose Op &1 Scaling 1 LS1 RS1 Rate Scaling Attack Rate of the operator 1 Level Scaling Amplitude Envelope 1 RR1 D1R1 D1L1 D2R1 Release Rate 2° Decay Rate 1° Decay Level 1° Decay Rate Attack Rate AR1 Detune, OSCWave, EGShift 1 DET1 Detune EG Shift 96dB 48dB 24dB 12dB Wave form Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Sensitivity 1 KVS1 Amplitude Modulation Enable AME1 Keyboard Velocity Sensitivity Key Velocity Sensitivity EBS1 EG Bias Sensitivity Eg Bias Sensitivity Frequency 1 Coarse 1 Coarse Ratio Fine 1 Fine Ratio Freq 1 Fixed Frequency Toggle Fix Frequency FIX OUT 1 Output Volume Volume Op &2 Amplitude Envelope 2 D1R2 D1L2 D2R2 RR2 AR2 Frequency 2 Coarse 2 Fine 2 Freq 2 Scaling 2 LS2 RS2 OUT 2 Detune, OSCWave, EGShift 2 DET2 Sensitivity 2 EBS2 KVS2 AME2 Op &3 Amplitude Envelope 3 D1R3 D1L3 D2R3 RR3 AR3 Scaling 3 LS3 RS3 OUT 3 Frequency 3 Coarse 3 Fine 3 Freq 3 Detune, OSCWave, EGShift 3 DET3 Sensitivity 3 EBS3 KVS3 AME3 Op &4 amplitude Envelope 4 AR4 D1R4 D1L4 D2R4 RR4 Frequency 4 Coarse 4 Fine 4 Freq 4 Scaling 4 LS4 RS4 OUT 4 Detune, OSCWave, EGShift 4 DET4 Sensitivity 4 EBS4 KVS4 AME4 &Func Delay Pan Depth Delay Pan LFO Freq Delay Ch Send Level Channel Chorus Delay Feedback Delay On/Off, Return Level On Delay Beat Ratio Delay BPM Foot Control Pitch Bend Range Pitch Amplitude Modulation Wheel Breath Control Pitch Bias Envelope Bias After Touch Phony Mode POLY MONO Potamento Portamento Mode FINGER FULL PT Portamento Time C&horus Chorus Parameters Channel send level On/Off and Return level Select LADSPA plugin Change plugin &Reverb Reverb Parameters &Config Font Size Quality High Middle Low Ultra low Filter Save Mode (into the song) Save only the used presets Save the entire set Save the configuration Configuration File Save... Load... Save as default Colors Text Background Edit Text Edit Background Red Blue Green Set Path Image in the background : Browse... Load the set at the initialization : Set Brightness, Detune, Attack and Release of the current channel to default Res. Ctrl Cut all notes off Panic! Number of Voices Number of voices Enable Channel Vol Channel Ctrl Release Attack Brightness Modulation Pan DelOverlapsBase MusE: Delete Overlaps Range All Events Selected Events Looped Events Selected Looped Parts Selected parts or c&urrent part All parts &in editor OK Cancel DidYouKnow Did you know? Don't show on startup Next tip Close DuplicateTracksBase Duplicate tracks Number of copies Copy all routes Default routing No routes Duplicate all parts Make independent copies of all parts Make clones of all parts No parts Copy drumlist Copy standard (vol, pan) and synth controllers Copy effects rack plugins Copy plugin controllers Ok Cancel EditCtrlBase MusE: Edit Controller Event Time Position Available Controller: Create New Controller textLabel3 Value Controller Note H-Bank L-Bank Program off pushButton4 &OK &Cancel EditGainBase MusE: Modify gain Gain 200% 100% 0% &Reset Alt+R &Apply Alt+A &Cancel Alt+C EditInstrumentBase MusE: Instrument Editor Instrument Name: Selected instrument name. List of defined instruments. Note off mode: Note off mode Selects how to handle note off events. The instrument can use note off events, or not at all, or convert them to zero-velocity note on events which helps save midi interface bandwidth. Refer to the instrument manufacturer's midi implementation chart for details. Pa&tches List of groups and patches. Group/Patch Properties Name: Group or patch name High Bank: Patch high bank number Patch high bank number. --- means don't care. --- dont care Low Bank: Patch low bank number Patch low bank number. --- means don't care. --- Program: Patch program number Patch program number. --- means don't care. Drum Show in tracks: Midi &Drum Delete group or patch &Delete Alt+D New patch New &Patch Alt+P New group New &Group Alt+G High Bank Low Bank Program Patch program number. --- means don't care. Patch Name: List of defined controllers List of defined controllers. Name Type H-Ctrl L-Ctrl Min Max Def Dr def Midi controller name Type: Midi controller type Midi controller number high byte Midi controller number low byte (* means drum controller) Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and GS/XG type songs and instruments. Allows controllers for each instrument in Muse's drum map. The low byte will be replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. * wild card Range: Min Minimum value. If negative, auto-translate. Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. Useful for controllers which should be displayed with zero bias. For example, 'Pan': Minimum: -64 Maximum: 63 True range: Min: 0 Max: 127 (bias = 64) 'CoarseTuning': Min: -24 Max: 23 True range: Min: 40 Max: 87 (bias = 64) Bias is determined from controller type: 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 Type 'Pitch' is the exception. It is biased at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) Max Maximum value Default: Drum default: L-Bank Info Additional information off Progr. ??? H-Bank Drum&maps Default value. Off: No default. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). off dont care Add common controller &Add Alt+A Delete controller Create a new controller New &Controller Alt+C S&ysEx SysEx List: Hex Entry: New SysE&x Alt+X Patch Collections: &Copy &Remove &Up &Down Contro&llers &Add Common... W Comment: Note: The enclosing F0 ... F7 are not required &Initialization Instrument initialization sequence: &Add... &Change... Tools &File &Help &New New Ctrl+N &Open... Open Ctrl+O &Save Save Ctrl+S Save &As... Save As &Close Close new item What's this? EditNoteDialogBase MusE: Enter Note OK Cancel Length: Time Position: Pitch: Velocity On: Velocity Off: EditSysexDialogBase MusE: Enter SysEx TimePosition: Hex: Note: The enclosing F0 ... F7 are not required Name: Comment: &Select... &OK &Cancel FLUIDSynthGuiBase FLUID Synth Load Delete Dump Info ID Fontname Chnl Soundfont Drum Chnl Level Width Damping Room Size Reverb CHANNEL SETUP Sine Triangle Type Number Speed Depth Chorus Gain LOADED SOUNDFONTS FileDialogButtons fdialogbuttons Home Global User Project read Midi Port Configuration write window states FluidSynthGui Choose soundfont GateTimeBase MusE: Modify Note Length Range All Events Selected Events Looped Events Selected Looped Parts Selected parts or c&urrent part All parts &in editor Values Rate: Offset: % lenNew = (lenOld * rate) + offset OK Cancel GlobalSettingsDialogBase MusE: Global Settings Application Project directory ... Start song Choose start song or template Reset to default Start template or song: Read MIDI Ports configuration from file, or else automatically configure Read MIDI Ports configuration Warn if opening file versions different than current version Auto save (every 5 minutes if not playing/recording) Editors Open editors in tabs (else in separate windows) Piano roll Drum editor Wave editor Score editor Master track editor DSSI VST Directories where dssi-vst (if installed) looks for Wine vst plugins (*.dll files) Directories containing Linux vst plugins (*.so files) Audio Mixer dB Try to use Jack Freewheel Speeds bounce operations Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Enable denormal protection Enable output limiter VST in-place Enable VST in-place processing (restart required) Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. Minimum control period Minimum audio controller process period (samples). Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. 1 2 4 8 16 32 64 128 256 512 1024 2048 External Waveditor External Waveditor command Note: External editor opened from the internal editor. Midi &Don't record hidden instruments Don'&t record muted instruments Don't record &hidden or muted instruments Instrument initialization Send instrument initialization sequences Warn if instrument initialization sequences pending Send instrument controller default values if none in song, at init or rewind Ticks RTC Resolution (Ticks/Sec) 4096 8192 16384 32768 Midi Resolution (Ticks/Quarternote) 48 Start MusE Start with &last song Start with &template Sta&rt with song On launch Show splash screen Show "Did you know?" dialog Plugin Paths Ordered by priority. Changes take effect on restart. The plugin cache is used for safety and speed. It automatically rescans when any of the given directories change, for example by installing a new plugin. Click this button to force the cache to recreate upon next restart. &Force plugin rescan and cache recreation on next restart LADSPA DSSI Linux VST LV2 NOTE: Ensure the directory containing core LV2 bundles is included (typically /usr/lib/lv2). Otherwise all plugins may not load properly. Move up Move down Add... Edit... Remove Audio driver settings (require restart) Sample rate: Audio backend: Shorter periods give better midi playback resolution. Note that this cannot be set with Jack devices! Period size (frames per period): Note that samplerate cannot be set with Jack devices! Minimum meter value Minimum slider value Sample rate conversion, time stretching and pitch shifting Audio converter settings... 96 192 384 768 1536 3072 6144 12288 Midi controller behaviour Send Null parameters after an (N)RPN value If set, 'null' parameter numbers will be sent after each RPN/NRPN event. (A 'null' parameter number is 127.) This prevents subsequent rogue or accidental 'data' events (without parameters) from corrupting the current RPN/NRPN controller. Normally it is OK to leave this unset, but use it if there is a possibility of such 'data' events. If this is set, the 'Optimize controllers' setting does not affect these controllers' parameters, only the H/L values. Send Null parameters Don't send redundant parameters or values If set, redundant H/L parameters or H/L values are not sent. They are sent only if they changed. This can save midi interface bandwidth. But it is possible the receiving device might miss the previous changes (for example if the device is turned on after they were sent). The 'Panic' button will force them to be resent. Certain operations will also force them to be resent, such as clicking a GUI midi control (but not while adjusting it). Optimize controllers GUI LV2 UI open behavior Set range markers (left / right) Middle mouse button / Right mouse button Ctrl + Right mouse button / Right mouse button Ctrl + Left mouse button / Ctrl + Right mouse button Options Show status bar Show note tooltips in midi editors GUI Tweaks Tweaks Warn if timer frequency is inadequate Track height /sec Move single armed track with selection Use project save dialog Some popup menus stay open (else hold Ctrl) Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. Use left mouse button for decreasing values Allow adding hidden tracks in track list menu Unhide tracks when adding hidden tracks Smart focus After editing, controls will return focus to their respective canvas Show newly created midi velocity graphs per-note px Keep transport window on top (requires restart) Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. Borderless zoom/pan mouse (else use alternate method) Scrollable submenus Record drum tracks Record a&ll instruments Latency Whether to enable latency correction/compensation: Enable latency correction/compensation Project latency Whether completely independent branches share a common project-wide latency: Project-wide latency Unterminated branches Whether unterminated branches participate in latency correction/compensation: Unterminated input branches Unterminated output branches Track monitoring Whether track monitor buttons affect latency correction/compensation: Monitoring affects latency Revert native GUI scaling for plugins in HiDPI Live update wave parts while recording Whether to show knob and slider values in certain places, for example mixer strips. Turn off to reduce clutter. Revert native UI window HiDPI scaling (some plugins ignore this setting) Show knob and slider values Whether to show knobs or sliders in certain places, for example mixer strips Prefer midi volume as decibels instead of 0-127 Style hack: Force line edit widgets to draw a frame Prefer knobs instead of sliders GUI refresh rate Use first available Ask once Ask always Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. This hack forces a frame to be drawn, but may interfere with other styles. Use old-style stop shortcut Monitor on record-arm automatically Show note names on notes in pianoroll Whether record-arming a track automatically activates monitoring. &Apply &Ok &Cancel LegatoBase MusE: Legato Range All Events Selected Events Looped Events Selected Looped Parts Selected parts or c&urrent part All parts &in editor Settings ticks Minimum Length Allow shortening notes OK Cancel MITTransposeBase MusE: Midi Input Plugin: Transpose On TriggerKey Transpose: +0 MRConfigBase MusE: Midi Input Plugin: Remote Control On Actions Stop Record Goto Left Mark Play Insert rest (step rec) MetronomeConfigBase MusE: Metronome Config Audio Beep Choose outputs... 50 MIDI Click Midi Channel Measure Note Measure Velocity Beat Velocity Beat Note Midi Port Precount Bars From Mastertrack / Signature Prerecord Preroll Hint: Enable metronome in Transportpanel Volume Audio master Meas Beat Accent1 Accent2 Sample Preferences Apply the settings globally or to the current song: Use global metronome Use song-specific metronome Enable accents for the audio and midi metronomes: &Two samples (old samples) Four samples (&new samples, with accents) Audio MIDI Accent 2 Velocity Accent 1 Velocity Accent 1 Note Accent 2 Note Off Accents Accent sequence beats Current accent 1 sequence: (Click to change) Current accent 2 sequence: Factory User Preset sequences Add current accents to presets Make a new preset from the current accents. + Delete preset Delete the selected preset. Only user presets can be deleted. - Use preset Copy the preset to the current accents. -> Reset to Factory or User defaults Resets all current accent sequences to default Factory or User presets. ! Enable the precount Enable Number of bars Use Mastertrack time signature Beats per bar Beat note type Enable precount when playing Precount on play Mute the metronome after precount Mute metronome Not supported yet &Apply Alt+A &OK Alt+O &Cancel Alt+C MidiAudioControlBase Midi control Port: Channel: Control type: &Learn Hi: Lo: MidiFilterConfigBase MusE: Midi Input Filter Record Filter Note On Poly Pressure Controller Program Change After Touch Pitch Bend Sysex Thru Filter Controller Filter Channel Filter 14 10 6 12 4 2 9 8 3 13 15 16 7 11 5 1 MidiInputTransformDialogBase MusE: Midi Input Transformator Filter All Equal Unequal Note Poly Pressure Control Change Aftertouch Pitch Bend NRPN RPN Program Value 2 Value 1 Event Type Ignore Higher Lower Inside Outside Channel Port Processing Keep Fix Plus Minus Multiply Divide Invert ScaleMap Flip Dyn Random Toggle Modules 1 2 3 4 Enable module 1 Enable module 2 Enable module 3 Enable module 4 Preset Name: Comment: Function create new preset &New delete preset &Delete &Dismiss PresetList MidiSyncConfigBase MusE: Midi Sync Apply Ok Cancel MTC Type: 24 25 30D 30N Offset: hour h minute m second s frame f subframe Sync receiving and sending Control MusE timing by external midi clock or MTC sync When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). Slave to external sync Averaging applied to recorded external tempo changes. External midi clock can be very jittery. Tempo is derived from it and recorded. It is usually desirable to average it and limit the number of recorded changes. Tiny: 2 section 4/4 = 8 stages. 1/8T note averaging, may produce jitter. Small: 3 section 12/8/4 = 24 stages. 1/4 note averaging, may still produce jitter. Medium: 3 section 28/12/8 = 48 stages. 1/2 note averaging. Less jitter. Large: 4 section 48/48/48/48 = 192 stages. Use this if the song has only one tempo. Very low quantization values can be used. Large pre-detect: 4 section 8/48/48/48 = 152 stages + first stage large step pre-detector. Use this if you expect sudden large tempo steps. None: Use only if high accuracy is needed for audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. Tempo record averaging bpm Tempo record quantization Send start to first clock delay This button and other transport related buttons are found on the Transport Panel Allows 'slow sync' devices time to synchronize to MusE. This value is the delay from sending start to sending the first clock. ms Send sync delay Note: Sync delay and MTC sync currently not fully implemented MidiTransformDialogBase MusE: Midi Transformator &New &Delete &Apply &OK &Cancel PresetList Processing Event Type Keep Fix Note Poly Pressure Control Change Aftertouch Pitch Bend NRPN RPN Plus Minus Multiply Divide Value 2 Invert ScaleMap Flip Dyn Random Value 1 Length Position Randomize Filter All Equal Unequal Ignore Higher Lower Inside Outside Bar Range Preset Name: Comment: Range process all events selected tracks inside loop Function Select Quantize Delete Transform Insert Copy Extract Quantize Value MixdownFileDialogBase MusE: Set Mixdown Wavefile &OK &Cancel File Path Channel Stereo Mono 5.1 wav,16 Bit wav, 24 Bit wav, 32 Bit (float) Format MoveBase MusE: Move Notes Range All Events Selected Events Looped Events Selected Looped Parts Selected parts or c&urrent part All parts &in editor Value Move by ticks OK Cancel MusECore::RasterizerModel Off Bar MusECore::Scripts Do you want to process ALL or only selected events? &Selected &All MusE - external script failed MusE was unable to launch the script, error message: %1 Re-read script names from disc MusECore::Song Jack shutdown! Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: - a performance issue with your particular setup. - a bug in MusE (or possibly in another connected software). - a random hiccup which might never occur again. - jack was voluntary stopped by you or someone else - jack crashed If there is a persisting problem you are much welcome to discuss it on the MusE mailinglist. (there is information about joining the mailinglist on the MusE homepage which is available through the help menu) To proceed check the status of Jack and try to restart it and then . click on the Restart button. Automation Previous event Next event Set event Add event Erase event Erase range Clear automation Midi control Assign Clear Clear all controller events? &Ok &Cancel Controller Bypass MusE: Tempo list External tempo changes were recorded. Transfer them to master tempo list? Und&o Re&do MusEDialog::PasteDialog %n quarter(s) %n quarter %n quarters quarter quarter quarters quarters MusEDialog::PasteEventsDialog %n quarter(s) %n quarter %n quarters quarter quarter quarters quarters MusEGui::Appearance Time scale upper, and time signature. Controller graph and S/X buttons. Tempo scale, and markers. Mixer labels. Auto-font-sizing up to chosen font size. Word-breaking but only with spaces. Maximum mixer label auto-font-sizing font size. Global opacity (opposite of transparency). Standard Custom Main application font, and default font for any controls not defined here. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Arranger part name overlay. Time scale lower. Style was changed. The program must be restarted for changes to take place. Restart now? Revert changes Revert all... Do you really want to reset all colors? MusE: load image No current color item Select Color: %1 MusEGui::Arranger Midi output channel number or audio channels Midi output port or synth midi port Automation parameter selection Notation clef Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. On/Off is not! Track type. Right-click to change midi and drum track types. Track name. Double-click to edit. Right-click for more options. Time lock Notation clef. Select this tracks notation clef. Track index. Click to select track, CTRL+LMB to add/remove, SHIFT+LMB for range select, dblclick to select all of the same type. Enable/Disable input monitor. Passes input through to output (see also Settings: Automatically monitor on record arm). Enable/Disable recording. Mute indicator. Left-click to mute track (for quick action), right-click to toggle track on/off (for more permanent action). Track type. Click to switch between midi and drum track types. Track name. Double-click to edit. Right-click for context menu. Midi/Drum: Output channel number. Audio: Channels. Mid/right-click to change. Midi/Drum: Output port. Synth: Assigned midi port. Left-click to change, right-click to show synth GUI. Notation clef. Click to select this tracks notation clef. Arranger Cursor Cursor position Bars Track index Enable input monitor Enable recording Mute/Off indicator Solo indicator Track type Track name Enable input monitor. Click to toggle. Passes input through to output for monitoring. See also Settings: Automatically Monitor On Record Arm. Enable recording. Click to toggle. See also Settings: Automatically Monitor On Record Arm. Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed. Midi/Drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi/Drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Solo indicator. Connected tracks are also 'phantom' soloed. Pitch Tempo Song length - bars Midi pitch Global midi pitch shift Midi tempo N Track Port Ch Channel T Time lock Automation Clef MusEGui::ArrangerColumns Control7 Control14 RPN NPRN RPN14 NRPN14 Pitch Program Aftertouch MusEGui::ArrangerView MusE: Arranger D&elete C&ut &Copy &Paste &Insert Empty Measure Delete Selected Tracks Duplicate Selected Tracks Copy in Range Arranger Paste C&lone Paste to Selected &Track Paste Clone to Selected Trac&k Paste (Show Dialo&g)... Shrink Selected Parts Expand Selected Parts Purge Hidden Events from Selected Parts Add Track Insert Track Select Select &All &Deselect All Invert &Selection &Inside Loop &Outside Loop All &Parts on Track Score All Tracks in One Staff One Staff per Track New Score Window... Pianoroll... Drums... Event List... Wave... Open in New Window Always open the part(s) in a new editor window Open Synth Plugin GUI... Midi &Transform... Global Cut - Selected Tracks Global Insert - Selected Tracks Global Split - Selected Tracks Change Note &Length Change Note &Velocity &Scripts Scripts for midi processing. Applied to selected midi parts (or else tracks). User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. &Display Configure &Custom Columns... New... Nothing to edit Global Cut Global Insert Global Split &Edit Fu&nctions &Structure &Quantize Notes Crescendo/Decrescendo Transpose Erase Events (Not Parts) Move Events (Not Parts) Set Fixed Note Length Delete Overlapping Notes Legato Display menu: View-specific display options. Toggle &Mixer Strip Remove track(s) Are you sure you want to remove this track(s)? MusEGui::AudioComponentRack Aux send level (dB) Vol Pan Mute Volume/gain Panorama/Balance Gain Calibration gain MusEGui::AudioMixerApp &Create &View Strips Routing Show Midi Tracks Show Drum Tracks Show Wave Tracks Show Inputs Show Outputs Show Groups Show Auxs Show Synthesizers Traditional order Arranger order User order Show all hidden strips Unhide strip: (no hidden strips) MusEGui::AudioStrip 1/2 channel Pre Fader Listening (PFL) Record downmix Record arm Mute or proxy mute Solo or proxy solo Track off Input monitor Input routing. Press F1 for help. Output routing. Press F1 for help. Volume/Gain Pass input through to output Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Auto off Read Touch Write Automation type Automation type: Off, Read, Touch or Write. Press F1 for help. L meter peak/clip R meter peak/clip MusEGui::BigTime Format display Bar Beat Tick Minute Second Frame Subframe MusE: Bigtime MusEGui::Canvas Tools MusEGui::ComboQuant Off MusEGui::CompactKnob off MusEGui::CompactPatchEdit Patch name MusEGui::CompactSlider off MusEGui::CpuStatusBar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) CPU load averaged over each GUI update period. DSP load read from JACK. Number of xruns. Double-click to reset. MusEGui::CpuToolbar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) MusEGui::CtrlCanvas Control canvas: Use Pencil tool to edit events and Draw tool to adjust them gradually. Hold Ctrl to affect only existing events. Merge options Erase target Erase target events between source events Erase target WYSIWYG Include last source item width when erasing Erase target inclusive Include entire source range when erasing Merge actions Merge Merge the dragged items Merge a copy Merge a copy of the dragged items Cancel drag Cancel dragging the items MusEGui::CtrlPanel Select controller Remove panel Manual adjust (Ctrl-double-click on/off) All/Per-note velocity mode MusEGui::DList Hide this instrument This turns a blue eye into a crossed eye Show this instrument This turns a crossed eye into a blue eye Set field Sets a field Set row Sets a row Set column Sets a whole column to the field Set list Sets the whole list Reset field Resets a field in a row to default patch or instrument value Reset row Resets a row to the instrument values Reset column Resets a whole column to the instrument values Reset list Resets the whole list to the instrument values Reset track's drum list Resets all lists on all patches to the instrument values Promote field to default patch Promotes a field in a row to the default patch Promote row to default patch Promotes a row to the default patch Promote column to default patch Promotes a column to the default patch Promote list to default patch Promotes the whole list to the default patch Reset instrument field Reset instrument row Reset instrument list Set instrument field Set instrument row Set instrument list Drum list: Edit instrument-specific settings. MusEGui::DrumCanvas Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help. Moving items failed The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. Creating event failed Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Recording event failed Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Internal error Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. Drum map Reset the track's drum map with instrument defaults? Note: Velocity: Start (bar): Start (time): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete LMB: Delete Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete. MusEGui::DrumEdit &Edit Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Note which triggers the sound Note which is played Override track output channel (hold CTRL to affect all rows). Override track output port (hold CTRL to affect all rows). CTRL + META keys: Draw velocity level 1 META key: Draw velocity level 2 META + ALT keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) Cut Copy Copy events in range Paste Paste to current part Paste (with Dialog) Delete Events &Select Select All Select None Invert Inside Loop Outside Loop Previous Part Next Part Event List... Fu&nctions Set Fixed Length Modify Velocity Crescendo/Decrescendo Quantize Erase Event Move Notes Delete Overlaps &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Cursor step Cursor tools Add controller view Drum tools &Display Display menu: View-specific display options. Group Don't group Group by channel Group maximally Show/Hide Also show hidden instruments Show all instruments Hide all instruments Only show used instruments Only show instruments with non-empty name or used instruments Step record Midi input Play events Set step size for cursor edit H hide M mute Sound Vol QNT E-Note Len A-Note Ch Port LV1 LV2 LV3 LV4 Muse: Load Drum Map MusE: Store Drum Map Drum map Reset the drum map with GM defaults? MusEGui::EditEventDialog Ok Cancel MusEGui::EditInstrument Use note offs No note offs Convert to 0-vel note ons Control7 Control14 RPN NPRN RPN14 NRPN14 Pitch Program PolyAftertouch Aftertouch H hide M mute Name Vol Quant E-Note Len A-Note Ch Port LV1 LV2 LV3 LV4 Tick Data Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) MusE: Create file failed MusE: Write File failed MusE: Save Instrument Definition Instrument Definition (*.idf) MusE: Save instrument as Enter a new unique instrument name: The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? MusE: Bad instrument name Please choose a unique instrument name. (The name might be used by a hidden instrument.) MusE: Bad patch name Please choose a unique patch name MusE: Bad patchgroup name Please choose a unique patchgroup name MusE: Bad controller name Please choose a unique controller name New controller: Error Error! All control numbers are taken up! Clean up the instrument! MusE The current Instrument contains unsaved data Save Current Instrument? &Save &Nosave &Abort MusEGui::EditMetaDialog MusE: Enter Meta Event Time Position Meta Type Enter Hex MusEGui::EditToolBar Select Pointer tool: With the pointer tool you can: select parts move parts copy parts Select Pencil tool: With the pencil tool you can: create new parts modify length of parts Select Delete tool: With the delete tool you can delete parts Select Cut tool: With the cut tool you can split a part Select Glue tool: With the glue tool you can glue two parts Select Score tool: Select Quantize tool: Insert display quantize event Select Drawing tool Select Muting tool: Click on part to mute/unmute Manipulate automation Select Cursor (tracker mode) tool: With the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Select Range tool Select Panning tool Select Zoom tool Select Stretch tool Select Sample rate tool Pointer Pencil Eraser Cutter Glue Range Pan Zoom Score Quantize Draw Mute parts Edit automation Cursor Stretch Sample rate Edit Tools MusEGui::EffectRack Effect rack: Double-click a slot to insert/edit effect. RMB to open context menu. Press F1 for help. Effect rack Double-click a slot to insert FX New Change Move up Move down Remove Bypass Show gui Show native gui Save preset Presets MusE: Save Preset Replace effect Do you really want to replace the effect %1? MusEGui::GlobalSettingsConfig MusE: Choose start template or song MusEGui::Header Track Info Columns Custom column MusEGui::LCDPatchEdit High bank: Low bank: Program (Ctrl-double-click on/off) off High bank Low bank Program MusEGui::LMaster Edit tools Tempo Key Time Type Value Signature Position Delete Insert tempo change Insert time signature change Insert key change Edit position Edit value Delete event Reposition of the initial tempo and signature events is not allowed MusE: List Editor Input error, conversion not OK or value out of range Reposition of tempo and signature events to start position is not allowed! MusEGui::ListEdit Cut Copy Paste Note SysEx Ctrl Meta Tick+ Tick- Delete Insert tools Tick Bar Type Ch Val A Val B Val C Len Comment Insert note event Insert system exclusive event Insert controller event Insert meta event Increase tick value Decrease tick value Delete event MusEGui::MPConfig Default input connections Are you sure you want to apply to all existing midi tracks now? Default output connections MusE: bad device name in out Toggle all Change all tracks now Remove Create Jack device Port Number Enable gui Enable reading Enable writing Port instrument Midi device name. Click to edit (Jack) Connections from Jack Midi outputs Connections to Jack Midi inputs Auto-connect these channels to new midi tracks Auto-connect new midi tracks to these channels Auto-connect new midi tracks to this channel Device state Enable gui for device Enable reading from device Enable writing to device Name of the midi device associated with this port number. Click to edit Jack midi name. Instrument connected to port Connections from Jack Midi output ports Connections to Jack Midi input ports Auto-connect these channels, on this port, to new midi tracks. Connect new midi tracks to these channels, on this port. Connect new midi tracks to this channel, on this port. State: result of opening the device Port GUI I O Instrument Device Name Please choose a unique device name Port number Midi device name Midi device type Connections from Jack Midi Connections to Jack Midi Result of opening the device: OK: Assigned to a port and in use Closed: Unassigned to a port, or closed R/W Error: Unable to open for read or write Unavailable: USB midi unplugged, or external application not running, or synth plugin not installed etc. (Jack Midi devices have 'unavailable ports' in the routes columns.) Unavailable devices or ports can be purged with 'Remove' or with the advanced router. Enable Graphical User Interface for device Connections from Jack Midi ports Connections to Jack Midi ports In routes Out routes Def in ch Def out ch State Type In Out <unknown> <none> MusEGui::MTScale Bar scale Bar scale: Use mouse buttons to set position and range markers. Hold Shift to set (LMB) or delete (RMB) custom markers. MusEGui::MTScaleFlo Bar scale MusEGui::MarkerView Add marker Delete marker Edit tools Bar:Beat:Tick Hr:Mn:Sc:Fr:Sf Lock Description Edit tick Edit SMPTE timecode Toggle timecode editing Edit marker description <Description> MusEGui::Master Tools MusEGui::MasterEdit MusE: Mastertrack Mastertrack Display menu: View-specific display options. Info Cursor &Display Time at cursor position Tempo at cursor position MusEGui::MetronomeConfig Reset accents: Resets all accents to the defaults (first in list) of the current preset category (Factory or User). Proceed? MusEGui::MidiAudioControl Control7 Control14 RPN NPRN RPN14 NRPN14 Pitch Program Aftertouch MusEGui::MidiComponentRack Pro Var Rev Cho Pan Program VariationSend ReverbSend ChorusSend Pan/Balance Controller (Ctrl-double-click on/off) <unknown> Instrument Transpose Transpose notes up or down Delay Offset playback of notes before or after actual note Length Change note length in percent of actual length Velocity <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> Compress Compress the notes velocity range, in percent of actual velocity <Patch> MusEGui::MidiInputTransformDialog New MusEGui::MidiStrip off Record arm Mute or proxy mute Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Track off Input monitor Intput routing. Press F1 for help. Output routing. Press F1 for help. Inst Prop Instruments and controllers Properties Volume/Gain (Ctrl-double-click on/off) Pass input through to output Input monitor: Pass input through to output. MusEGui::MidiSyncConfig Port Number Name of the midi device associated with this port number Sync to this device. Click to select. Midi clock input detected Midi tick input detected Midi real time input detected MMC input detected MTC input detected Detected SMPTE format Receive id number. 127 = Global. Double click to edit. Accept midi clock input Accept midi real time input Accept MMC input Accept MTC input Receive start rewinds before playing Transmit id number. 127 = Global. Double click to edit. Send midi clock output Send midi realtime output Send MMC output Send MTC output Midi realtime input detected, including start/stop/continue, and song position. MMC input detected, including stop/play/deferred play, and locate. Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detects format of MTC quarter and full frame, and MMC locate. Receive id number. 127 = global receive all, even if not global. Accept midi realtime input, including start/stop/continue, and song position. Non-clock events (start,stop etc) are accepted by ALL enabled ports. This means you may have several master devices connected, and muse will accept input from them. Accept MMC input, including stop/play/deferred play, and locate. Type Sync to this device. Click to select. MusE will synchronize to this device's clock or MTC quarter-frame, if 'slave to external sync' is on. MTC input detected, including forward quarter-frame sync and full-frame locate Accept midi clock input. Only one port can be used for clock sync. Accept MTC input, including forward quarter-frame sync and full-frame locate. Only one port can be used for sync. When start is received, rewind before playing. Note: In some cases, such as having many project audio tracks, it may be impossible to rewind fast enough to synchronize with the external device. Manually rewinding the device before playing is recommended. Transmit id number. 127 = global transmit to all. Send midi clock output. If 'Slave to External Sync' is chosen, muse can re-transmit clock to any other chosen ports. Send midi realtime output, including start/stop/continue, and song position. If 'Slave to external sync' is chosen, muse can re-transmit midi realtime input messages to any other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. Port Device Name None Tiny Small Large Large with pre-detect MusE Settings have changed Apply sync settings? &Apply &No &Abort <none> MusEGui::MidiTransformerDialog New MusEGui::MixdownFileDialog Wave Files (*.wav);;All Files (*) MusEGui::MusE Failed to start audio! Und&o Re&do Loop Start Rewind Forward Stop Play Record Panic &New Open &Recent &Save Transport Panel Bigtime Window Mixer A Mixer B Show Docks Toggle display of currently visible dock windows. Display MusE main window in full screen mode. Mastertrack Graphic... Send 'note-off' command to all midi channels. Send initialization messages as found in instrument definition. Take Automation Snapshot Keyboard Shortcuts... Midi Ports / Soft Synths... The current project contains unsaved data. Save current project? No valid parts selected The current project contains unsaved data. Load overwrites current project. Save current project? Import Audio File &Quit Fullscreen Input Plugins Rhythm Generator Local Off Bounce to Track Restart Audio Clear Automation Data Follow Song Don't Follow Song Follow Page Follow Continuous Set left/right marker for bounce range MusE: Song: Metronome &Report Bug... Tempo Signature Undo/Redo Transport Cpu load &Manual (wiki)... File buttons Timeline Recording Sync Position &File &View &Midi &Audio &Windows Se&ttings &Help Cannot read template File open error File read error Unknown File Format: %1 MusE: Song: %1 MusE: load project Timeout waiting for audio to run. Check if jack is running or try another driver. Was not able to start audio, check if jack is running or try another driver. Undo last change to song Redo last undo Loop between left mark and right mark Punch in Record starts at left mark Recording starts at left mark Punch out Record stops at right mark Recording stops at right mark Rewind to start position Rewind current position Move current position Stop sequencer Restart rec Start sequencer play To record press record and then play Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help. Turn on/off metronome Metronome on/off. Press F1 for help. Create new song New from &Template... Create new song from template &Open... Open song from file Click this button to open an existing song. Save current song Click this button to save the song you are editing. You will be prompted for a file name. Save &As... &Close Import Midifile... Export Midifile... Import Part... Import Audio File... Find Unused Wave Files... Song Info... Edit Instrument... Transpose... Midi Input Transform... Midi Input Filter... Midi Remote Control... Reset Instrument Init Instrument Send 'local-off' command to all midi channels. Bounce to File... Global Settings... Appearance... Rewind on Stop Metronome... Midi Sync... Midi File Import/Export... &MusE Homepage... &Did You Know? &About MusE... Snooper (developer tool)... About &Qt... The project has no project sample rate (added 2011). Please enter a rate. The current system rate (%1Hz) is suggested, and cancelling uses it: The project has audio waves, but no project sample rate (added 2011). Please enter a rate. The most common wave rate found is suggested, the project was probably made with it. Cancelling uses the current system rate (%1Hz): Project sample rate MusE: load template MusE: Write File failed S&kip Load configuration colors &Cancel Color settings will immediately be replaced with any found in the file. Are you sure you want to proceed? &Ok Save configuration colors MusE: Save As Nothing to edit MusE: Bounce to Track No wave tracks found No audio output tracks found Select one audio output track, and one target wave track Select one target wave track Select one target wave track, and one audio output track MusE: Bounce to File Select one audio output track MusE: Bounce &Abort This will clear all automation data on all audio tracks! Proceed? This takes an automation snapshot of all controllers on all audio tracks, at the current position. Proceed? MusE: Warning The song uses multiple ports but export format 0 (single track) is set. The first track's port will be used. Playback will likely be wrong unless the channels used in one port are different from all other ports. Canceling and setting a different export format would be better. Continue? MusE: Export Midi MusE: Import Midi Add midi file to current project? &Add to Project &Replace Reading midifile failed: Import part is only valid for midi and wave tracks! MusE: load part No track selected for import %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. %n part out of %1 could not be imported. Likely the selected track is the wrong type. %n parts out of %1 could not be imported. Likely the selected track is the wrong type. %n part(s) could not be imported. Likely the selected track is the wrong type. %n part could not be imported. Likely the selected track is the wrong type. %n parts could not be imported. Likely the selected track is the wrong type. Import Wavefile Wave import error There are too many wave files of the same base name as imported wave file Can not continue. Can't create new wav file in project folder! Failed to initialize sample rate converter! Cancel This wave file has a samplerate of %1 Hz, as opposed to current setting %2 Hz. A live, real-time samplerate converter can be used on this file. Or, a copy of the file can be resampled now from %1 Hz to %2 Hz. Do you want to use a converter or resample the file now? Use live converter Resample now Resampling wave file "%1" from %2 to %3 Hz... Output has clipped Resampling again and normalizing wave file "%1" Try %2 of %3... To import an audio file you have first to select a wave track File version is %1.%2 Current version is %3.%4 Conversions may be applied if file is saved! Opening file Do not warn again Unable to launch browser Error launching default browser MusEGui::NoteInfo Note Info Delta/Absolute mode Start Len Pitch Velo on Velo off MusEGui::PartCanvas Cannot copy/move/clone to different Track-Type C&ut &Copy clones S&elect Part Rename Color Delete Split Glue Super glue (merge selection) De-clone Save part to disk... Wave edit... File info... MusE: Save part Automation Normalize Part canvas: Use Pencil tool to draw parts. Double-click to create a new MIDI/drum part between the range markers (set with MMB + RMB). Press F1 for help. Part name: %1 Files: Remove selected LMB: Select/Move/Dblclick to edit | CTRL+LMB: Multi select/Move&Copy | CTRL+ALT+LMB: Dblclick to edit in new window | SHIFT+LMB: Select track | MMB: Delete LMB: Draw to resize | MMB: Delete | CTRL+RMB: Trim length LMB: Delete | CTRL+RMB: Trim length LMB: Cut part in two LMB: Merge with following part LMB: Mute selected part LMB: Edit automation events in audio parts %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. %n part out of %1 could not be pasted. Likely the selected track is the wrong type. %n parts out of %1 could not be pasted. Likely the selected track is the wrong type. %n part(s) could not be pasted. Likely the selected track is the wrong type. %n part could not be pasted. Likely the selected track is the wrong type. %n parts could not be pasted. Likely the selected track is the wrong type. Cannot paste: multiple tracks selected Cannot paste: no track selected Can only paste to midi/drum track Can only paste to wave track Can only paste to midi or wave track Cannot paste: wrong data type MusEGui::PasteDialog %n quarter(s) %n quarter %n quarters %1 quarter for floating-point arguments like 1.5 %1 quarters for floating-point arguments like 1.5 MusEGui::PasteEventsDialog %n quarter(s) %n quarter %n quarters %1 quarter for floating-point arguments like 1.5 %1 quarters for floating-point arguments like 1.5 MusEGui::Piano Piano: Press key to play or enter events in step record mode (SHIFT for chords). RMB: Set cursor for polyphonic control events. CTRL+Mousewheel to zoom view vertically. Left click: Play Right click: Select key MusEGui::PianoCanvas Pianoroll canvas: Use Pencil tool to draw and edit MIDI events, Pointer tool to select and edit. Press F1 for help. Note: Velocity: Start (bar): Start (time): Length (ticks): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete | CTRL+RMB: Trim length LMB: Resize | CTRL+LMB: Multi select | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length LMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length MusEGui::PianoRoll &Edit C&ut &Copy Copy events in range &Paste Paste to current part Paste (with dialog) Delete &Events &Select Select &All &Deselect All Invert &Selection &Inside Loop &Outside Loop &Previous Part &Next Part Event List... Fu&nctions Quantize Modify Note Length Modify Velocity Crescendo/Decrescendo Transpose Erase Events Move Notes Set Fixed Length Delete Overlaps Legato &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Play single note Play chords &Display Step record Midi input Play events &Event Color Display menu: View-specific display options. &Blue &Pitch colors &Velocity colors Add controller view Pianoroll tools MusEGui::PluginDialog Plugin categories. Right-click on tabs to manage. Right-click on plugins to add/remove from a category. Type Lib Label Name AI AO CI CO IP FB 2B id Maker Copyright Audio inputs Audio outputs Control inputs Control outputs In-place capable Requires fixed block size Requires power-of-2 block size ID number &Create new group &Delete currently selected group Re&name currently selected group New group Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. MusE: Select Plugin Associated categories You need to define some categories first. Enter the new group name Wine VST dssi synth dssi effect LV2 synth LV2 effect VST synth VST effect ladspa MusEGui::PluginGui File Buttons Load Preset Save Preset Bypass plugin Plugin settings MusE: Save preset Plugin Settings MusE: load preset Error reading preset. Might not be right type for this plugin MusEGui::PluginSettings Global setting: On Off MusEGui::PopupMenu <More...> %1 MusEGui::PosToolbar Left marker Left marker position Right marker Right marker position Current position Current position in bars/beats SMPTE position Current position in SMPTE time Show/Hide position in ticks and audio frames Show/Hide additional output fields displaying the current position in ticks and audio frames Current position in ticks Current position in audio frames Current position slider MusEGui::ProjectCreateImpl Select directory MusEGui::RasterLabelCombo Snap MusEGui::RecToolbar Record mode Record mode: Overdub to add new events, Replace to replace overlapping events. Overdub Replace Cycle record mode Cycle record mode: Normal to replace range when loop is finished, Mix to add new events, Replace to replace range on first MIDI input. Normal Mix MusEGui::RouteDialog Normal Alias 1 Alias 2 Source Destination <none> MusEGui::RoutePopupMenu Channel Soloing chain Audio returns Warning: No input devices! Channel grouping: Mono Stereo Channels Omni Show aliases: First Second Show names Show first aliases Show second aliases Jack ports Connect Unavailable Open advanced router... Warning: No output devices! Output routes: Input routes: Open midi config... ALSA devices JACK devices Synth devices <none> Midi sends Sources: More... Tracks Destinations: Audio sends MusEGui::ScoreCanvas Treble Bass Grand Staff Remove staff Ambiguous part There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. No part There are no parts you could add the note to. Tools MusEGui::ScoreEdit Step recording tools Quantisation Pixels per whole Note settings last Quantisation settings Step record &Edit C&ut &Copy Note length Velocity Apply to selected notes, or new notes if none is selected Off-Velocity Copy Events in Range &Paste Paste (with dialog) Delete &Events &Select Select &All &Deselect All Invert &Selection &Inside Loop &Outside Loop Fu&nctions &Quantize Change Note &Length Change Note &Velocity Display menu: View-specific display options. Note Head &Colors &Default Set Up &Preamble Display &Key Signature Display &Time Signature Set Score &Name Crescendo/Decrescendo Transpose Erase Events Move Notes Set Fixed Length Delete Overlaps Legato &Display &Velocity &Part Enter the new score title Error Changing score title failed: the selected title is not unique MusEGui::ScrollScale next page previous page current page number MusEGui::ShortcutCaptureDialog Ok Cancel Shortcut conflicts with %1 Undefined MusEGui::ShortcutConfig Save printable text file Text files (*.txt);;All files (*) Error Error opening file for saving Shortcuts for selected category: Legend: An error occurred while saving MusEGui::SigScale Signature scale MusEGui::SigToolbar Time signature at current position MusEGui::Strip Name Enter track name: MusE: bad trackname Please choose a unique track name Configuration Prefer knobs, not sliders Show values in controls Prefer midi volume as decibels Monitor on record-arm automatically Visible audio effects Actions Change track name Hide strip MusEGui::SyncToolbar External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::TList SYNTH IS UNAVAILABLE! <none> visible no clef Treble Bass Grand off <unknown> MusE: bad trackname Please choose a unique track name Show gui Show native gui Clear automation Presets Track list: Use context menu to create tracks. Click track ID to select, CTRL to add, SHIFT for range, dblclick to select all of same type. Press F1 for help. Clear all controller events? &Ok &Cancel Change color Midi control Assign Clear Other Treble clef Bass clef Grand Staff Viewable automation Internal Synth Delete Track Delete Selected Tracks Track Comment Save track's drumlist Load track's drumlist Reset track's drumlist Reset track's drumlist-ordering Copy track's drumlist to all selected tracks Insert Track Drum map Reset the track's drum map with instrument defaults? Reset the track's drum map ordering? Muse: Load Track's Drum Map MusE: Store Track's Drum Map Midi Drum MusEGui::TempoSig Use mastertrack tempo Tempo/Sig Mastertrack tempo at current position, or fixed tempo Time signature at current position MusEGui::TempoToolbar Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Tap Tap this button to set tempo Set the tempo to the time difference of two successive clicks. Times out after two seconds. MusEGui::Toolbar1 Solo Cursor MusEGui::TopWin Tabbed/Floating Display editor in a tab or in a separate window (preset in Global Settings->Editors). Fullscreen Undo/Redo tools Panic Metronome Timeline Transport Recording Sync Tempo Signature Position Piano roll Drum editor Master track editor Wave editor Score editor Arranger <unknown toplevel type> MusEGui::TrackComment MusE: Track Comment Track Comment: MusEGui::Transport Overdub Replace Rec Mode Normal Mix Cycle Rec Punch in Punch out Rewind to Start Rewind Forward Stop Play Record Metronome on/off External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. Loop Left Mark Right Mark Click this button to rewind to start position Click this button to rewind Click this button to forward current play position Click this button to stop playback Click this button to start playback Click this button to enable recording MusEGui::VisibleTracks Show wave tracks Show group tracks Show aux tracks Show input tracks Show output tracks Show midi tracks Show synth tracks Visible track types MusEGui::WaveCanvas Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help. Adjust Wave Offset Wave offset (frames) Part creation failed Left and right position markers must be placed inside the current part. Part created The selected region has been copied to the clipboard and can be pasted in the arranger. MusE - external editor failed MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE - file size changed When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Wave event: Converter settings MusEGui::WaveEdit &Edit Func&tions &Gain Other &Copy &Create Part from Region C&ut &Paste Edit in E&xternal Editor... Mute Selection Normalize Selection Fade In Selection Fade Out Selection Reverse Selection Select Select &All &Deselect All &Previous Part &Next Part &Display Display menu: View-specific display options. &Event Color &Part colors &Gray WaveEdit tools Solo Cursor OrganGuiBase MusE: Organ Drawbars 16' 4' 2 2/3' 2' 5 1/3' 8' Envelope Hi Release Sustain Decay Attack ms cB Envelope Lo O-1 Oscillator Brass Reed Flute Velocity PasteDialog %n quarter(s) %n quarter %n quarters quarter quarter quarters quarters PasteDialogBase MusE: Paste Parts Number and raster times Insert Raster ticks Move, Merge, Clone Move everything to the right Move only affected parts to the right Put everything into a single track Merge with existing parts Insert as clones (where possible) OK Cancel PasteEventsDialogBase MusE: Paste Events Number and raster times Insert Raster ticks Paste options &Always into existing parts &Never into existing parts &Into existing parts if part has not to be expanded by more than Put everything into the (selected) part Paste midi controller target options Keep e&xisting Erase existin&g Erase WYSIWYG Erase inclusive OK Cancel PluginDialogBase Dialog Ports: Mono Mono + Stereo Stereo All Plugin type: Filter: &OK &Cancel PluginSettings Dialog Options Fixed speed Transport affects audio latency Override reported audio latency Reported audio latency override value Revert HiDPI scaling of native GUI Set in Global settings->GUI tweaks Global setting Additional information Follow global setting On Off ProjectCreate Create Project Projects folder: ... Project Name: Project is a Template Write window state Project song file type: Project Path to song file: &Create project folder (recommended for audio projects) Song information: QObject Error Please first select the range for crescendo with the loop markers. Controller ! Other ! Switch on General Midi Level 1 mode Switch on General Midi Level 2 mode Switch off General Midi Level 1 or 2 Switch on Roland GS mode Switch on Yamaha XG mode Tracks: Midi ports: Midi devices: Jack: Jack midi: Instruments Current device Others Files Synthesizers Open Closed <Properties> <Base Property> <Property> Input routing Warning: No input routes! Click to connect... Output routing Warning: No output routes! Click to connect... TRACK TAKE MusE import error. MusE failed to import the file. Possibly this wasn't a sound file? If it was check the permissions, MusE sometimes requires write access to the file. FX slot QWidget Cannot convert sysex string Hex String too long (2048 bytes limit) None No wave events selected. No selection. Ignoring MusE: get file name The directory %1 does not exist. Create it? MusE: create directory File %1 exists. Overwrite? Open File %1 failed: %2 MusE: write Creating dir failed MusE: Open File Instrument-defined Generic midi Edit instrument ... Add Others Common Controls Velocity Create peakfile for New QuantBase MusE: Quantize Range All Events Selected Events Looped Events Selected Looped Parts Selected parts or c&urrent part All parts &in editor Values Strength: % Threshold (ticks): Quantize Len Raster Whole Half 4th 4th Triplet 8th 8th Triplet 16th 16th Triplet 32th 32th Triplet Swing: If the proposed change in tick or length is smaller than threshold, nothing is done. If swing=0, this is normal If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. OK Cancel RemoveBase MusE: Erase Notes Range All Eve&nts Selec&ted Events &Looped Events Selected L&ooped Parts Selected parts or c&urrent part All parts &in editor Thresholds ticks Velocity Length <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> OK Cancel RouteDialogBase MusE: Routing List of available sources. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Connections View window. Shows all current connections. Thick lines are Omni Routes. Thin lines are Channel Routes. List of available destinations. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Itemized list of current connections. Show only selected sources Show only destination routes for selected source Show only selected destinations Show only source routes for selected destination Show all Midi Ports Auto adjust column size Automatically adjusts the source and destination tree widths when the splitters are adjusted. This also turns on text word wrap, which may cause slower response with larger lists. Preferred route name or alias Connect source to destination Remove selected route Connect Remove RubberbandSettingsBase Rubberband Settings Rubberband support is not enabled Use default settings Presets: Default Percussion Maximum Stretch: Elastic Precise Window: Standard Short Long Smoothing: Off On Channels: Apart Together Transients: Crisp Mixed Smooth Detector: Compound Percussive Soft Phase: Laminar Independent Formant: Shifted Preserved Pitch: High speed High quality High consistency OK Cancel SRCResamplerSettingsBase SRC Resampler Settings Use default settings Converter Type: SINC Best quality SINC Medium quality SINC Fastest Zero Order Hold Linear OK Cancel SS_PluginFront Clear and unload effect Load effect Toggle display of effect parameters Turn effect on/off SetlenBase MusE: Set Note Length Range All Events Selected Events Looped Events Selected Looped Parts Selected parts or c&urrent part All parts &in editor Value New length ticks OK Cancel ShortcutCaptureDialogBase Enter shortcut sequence Press keys to enter shortcut sequence! Old shortcut: Undefined New shortcut: OK Cancel ShortcutConfigBase Configure Keyboard Shortcuts Shortcut Category Shortcut Description Filter: &Reset all &Clear Alt+C &Define Alt+D &Apply Alt+A &Printable file... Alt+P &Ok Alt+O SimpleDrumsGuiBase DrumSynth 0.1 Audio file assignments. Within parantesis: note number and traditional midi drum name SimplePluginChooserBase SimpleDrums - Ladspa Plugin Chooser Name Label Inports Outports Creator &Cancel Alt+C &OK Alt+O SimpleSynthGui Mix Chn Channel routing &Load setup &Save setup Load sample dialog SnooperDialogBase Snooper Mode Auto-hide Separate parented top levels Update tree Mouse clicks Key presses Flash timer s Reset Filters MusEGui QWidget Properties Search: Class name: Object name: About This tool helps developers and designers discover objects. Click on anything anywhere or press a key to highlight an item. It will NOT auto-update when new objects are created. Click 'Update tree'. When open, may slow program slightly. This dialog is NOT shown. Class name::Object name Property Type Value Event type SongInfo Song Information Show on song load &Cancel Alt+C &Ok Alt+O SynthConfigBase Name Type Configure midi devices, midi ports, and synthesizers Available soft synthesizers List of available software synthesizers File Inst Version Description Useable devices Add: Synth ALSA JACK Rename Remove Device port assignments &Apply &OK TransposeBase MusE: Transpose Range All Events Selected Events Looped Events Selected Looped Parts Selected parts or c&urrent part All parts &in editor Value Halftone-steps OK Cancel UnusedWaveFiles Dialog List of unused audio files in current project directory: Current project All .med files in current directory Move files to 'unused' subdir Cancel VAMGui MusE: Load VAM Presets MusE: Save VAM Presets VAMGuiBase Virtual Analogue for MusE LFO Freq Waveform Sine Pulse Saw Triangle Filter EnvMod Attack Decay Sustain Release Cutoff Resonance Invert KeyTrack Presets Set load preset list save preset list save preset list to a new file delete preset DCO 1 Pitch Detune PWM FM PW DCO 2 On VAM 1.0beta3 Virtual Analog for MusE Released under GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VelocityBase MusE: Modify Velocity Range All Events Selected Events Looped Events Selected Looped Parts Selected parts or c&urrent part All parts &in editor Values Rate: % Offset: veloNew = (veloOld * rate) + offset OK Cancel ZitaResamplerSettingsBase Zita Resampler Settings Zita Resampler support is not enabled Use default settings Options: OK Cancel file_patterns Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) All Files (*) Midi (*.mid) Karaoke (*.kar) all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) med Files (*.med *.med.gz *.med.bz2) Uncompressed med Files (*.med) gzip compressed med Files (*.med.gz) bzip2 compressed med Files (*.med.bz2) mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) (*.jpg *.gif *.png) (*.jpg) (*.gif) (*.png) part Files (*.mpt *.mpt.gz *.mpt.bz2) part Files (*.mpt) gzip compressed part Files (*.mpt.gz) bzip2 compressed part Files (*.mpt.bz2) Presets (*.pre *.pre.gz *.pre.bz2) Presets (*.pre) gzip compressed presets (*.pre.gz) bzip2 compressed presets (*.pre.bz2) Presets (*.map *.map.gz *.map.bz2) Presets (*.map) gzip compressed presets (*.map.gz) bzip2 compressed presets (*.map.bz2) Wave/Binary (*.wav *.ogg *.flac *.bin) Wave (*.wav *.ogg *.flac) Binary (*.bin) Color configuration files (*.cfc) Qt style sheets (*.qss) freeverb FreeVerb Room Size Damping Wet Level Tap-Reverberator Decay [ms] dB Dry Level [dB] Wet Level [dB] Preset: AfterBurn AfterBurn (Long) Ambience Ambience (Thick) Ambience (Thick) - HD Cathedral Cathedral - HD Drum Chamber Garage Garage (Bright) Gymnasium Gymnasium (Bright) Gymnasium (Bright) - HD Hall (Small) Hall (Medium) Hall (Large) Hall (Large) - HD Plate (Small) Plate (Medium) Plate (Large) Plate (Large) - HD Pulse Chamber Pulse Chamber (Reverse) Resonator (96 ms) Resonator (152 ms) Resonator (208 ms) Room (Small) Room (Medium) Room (Large) Room (Large) - HD Slap Chamber Slap Chamber - HD Slap Chamber (Bright) Slap Chamber (Bright) HD Smooth Hall (Small) Smooth Hall (Medium) Smooth Hall (Large) Smooth Hall (Large) - HD Vocal Plate Vocal Plate - HD Warble Chamber Warehoouse Warehouse - HD Comb Filters Allpass Filters Bandpass Filters Enhanced Stereo main File to open Alsa midi only (using dummy audio driver) Use RtAudio driver Use JAckAudio driver to connect to Jack audio server Do not try to auto-start the Jack audio server Do not auto-populate midi ports with midi devices found, at startup Force inclusion of ALSA midi even if using Jack Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) Force midi real time priority to n (default: audio driver prio -1) Force plugin cache re-creation. (Automatic if any plugin path directories changed.) Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care. Don't load LADSPA plugins Don't load MESS plugins Don't load VST plugins Don't load LinuxVST plugins Don't load DSSI plugins Don't load LV2 plugins Don't use LASH Force locale to the given language/country code (xx = Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins. Debug mode: no threads, no RT Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively! Debug mode: trace midi Input Debug mode: trace midi Output Debug mode: trace sync Enable Python control support Pyro nameserver host name Pyro nameserver host port Pyro daemon host name Pyro daemon host port Pyro communication timeout in seconds midiWarnInitPendingBase Instrument initialization MusE should now send some Instrument Initialization Sequences. The sequences (usually System Exclusive messages) are defined by the selected instruments in the Settings -> Midi Ports dialog, such as the GM (default), GS, or XG instruments. Typically you should answer yes here. You can always do it manually from the Midi menu. Continue? Don't ask me again shortcuts Transport: Start playback from current location Transport: Toggle metronome Transport: Stop Playback Transport: Goto Start Transport: Play, Stop, Rewind Transport: Goto left marker Transport: Goto right marker Transport: Toggle Loop section Transport: Toggle Record Transport: Clear all rec enabled tracks Toggle fullscreen Toggle tabbed/floating editor window Toggle docks display Show Arranger window Edit: Copy Edit: Copy in range Edit: Undo Edit: Redo Edit: Cut Edit: Paste Edit: Paste to selected track Edit: Paste clone Edit: Paste clone to selected track Edit: Paste (with dialog) Edit: Delete File: New project File: Open from disk File: Save project File: Open recent file File: Save as File: Load template File: Import midi file File: Export midi file File: Import midi part File: Import audio file File: Quit MusE Edit: Select parts on track Open pianoroll Open drumeditor Open listeditor Open waveeditor Open graphical mastertrack editor Open list mastertrack editor Open midi transformer Open track Synth GUI Open pianoroll in new window Open drumeditor in new window Open listeditor in new window Open waveeditor in new window Add midi track Add drum track Add wave track Add audio output Add audio group Add audio input Add audio aux Insert midi track Insert drum track Insert wave track Insert audio output Insert audio group Insert audio input Insert audio aux Structure: Global cut Structure: Global insert Structure: Global split Structure: Cut events View: Open mixer #1 window View: Open mixer #2 window View: Toggle transport window View: Toggle bigtime window View: Open marker window View: Toggle mixer strip display Settings: Follow song by page Settings: Follow song off Settings: Follow song continuous Settings: Global configuration Settings: Configure shortcuts Transport: Goto End Transport: Restart recording Transport: Restart recording (multi take) Transport: Arm current track Toggle rewind on stop Edit: Paste to current part File: New project from template Edit: Rename part Settings: Configure metronome Settings: Midi sync configuration Settings: Midi file import/export configuration Settings: Appearance settings Settings: Audio subsystem configuration Midi: Edit midi instruments Midi: Open midi input transform Midi: Open midi input filter Midi: Midi input transpose Midi: Midi remote control Midi: Random rhythm generator Midi: Reset midi Midi: Init midi Midi: Midi local off Audio: Bounce audio to track Audio: Bounce audio to file Audio: Restart audio Automation: Take mixer snapshot Automation: Clear mixer automation Help: Open Manual Help: Toggle whatsthis mode Edit: Edit selected part Edit: Select nearest part on track above Edit: Add nearest part on track above Edit: Select nearest part on track below Edit: Add nearest part on track below Edit: Insert empty measure Edit: Duplicate track Select track above Select track below Edit selected track name Mute all currently selected tracks Solo all currently selected tracks Midi: Transpose Edit: Select all Edit: Select none Edit: Invert Selection Edit: Select events/parts inside locators Edit: Select events/parts outside locators Edit: Select previous part Edit: Select next part Edit: Select nearest part/event to the left or move cursor Edit: Add nearest part/event to the left to selection Edit: Select nearest part/event to the right or move cursor Edit: Add nearest part/event to the right to selection Edit: Set locators to selection Edit: Increase pitch Edit: Decrease pitch Edit: Increase event position Edit: Decrease event position View: Zoom in View: Zoom out View: Goto Current Position View: Scroll left Transport: Step record Transport: Midi input Transport: Play events Edit: Increase velocity Edit: Decrease velocity Edit: Set Fixed Length on Midi Events Quantize Modify Note Length Modify Velocity Edit: Crescendo Edit: Thin Out Edit: Erase Event Edit: Delete Overlaps Edit: Note Shift Edit: Move Clock Edit: Copy Measure Edit: Erase Measure Edit: Delete Measure Edit: Create Measure Edit: Change Event Color Move: Move to selected note Tool: Pointer Tool: Pencil Tool: Eraser Tool: Pan Tool: Zoom Tool: Line Draw Tool: Cursor Add note velocity 1 Add note velocity 2 Add note velocity 3 Add note velocity 4 Cursor step size: larger Cursor step size: smaller Instrument/Cursor up Instrument/Cursor down Tool: Range Tool: Stretch Tool: Sample rate Tool: Scissor Tool: Glue Tool: Mute Transport: Increase current position Transport: Decrease current position Transport: Increase current position, no snap Transport: Decrease current position, no snap Quantize: Set quantize to off Quantize: Set quantize to 1/1 note Quantize: Set quantize to 1/2 note Quantize: Set quantize to 1/4 note Quantize: Set quantize to 1/8 note Quantize: Set quantize to 1/16 note Quantize: Set quantize to 1/32 note Quantize: Set quantize to 1/64 note Quantize: Toggle triol quantization Quantize: Toggle punctuation quantization Quantize: Toggle punctuation quantization (2) Edit: Insert at location Edit: Increase length Edit: Decrease length Insert Note Insert SysEx Insert Ctrl Insert Meta Select strip to the left Select strip to the right Mixer strip volume down Mixer strip volume up Mixer strip pan left Mixer strip pan right Mixer strip volume down page Mixer strip volume up page Mixer strip pan left page Mixer strip pan right page Insert Tempo Insert Signature Change Event Position Edit Event Value Insert Key Settings: Midi ports / Soft synths Goto Next Marker Goto Prev Marker Normalize warnBadTimingBase Bad timing Message here Don't warn me again muse-4.0.0/src/share/locale/muse_es.ts000066400000000000000000025635511404105311000176320ustar00rootroot00000000000000 @default Add Midi Track Agregar pista MIDI Add Drum Track Agregar pista de percusión Add Wave Track Agregar pista de audio Add Audio Output Agregar salida de audio Add Audio Group Agregar grupo de audio Add Audio Input Agregar entrada de audio Add Aux Send Agregar envio auxiliar Add Synth Agregar sintetizador Route Ruta dest dest name="%1"/ nombre="%1"/ Warning: No output devices! Atencion: No hay salidas disponibles Open midi config... Configurar MIDI... Empty ports Puertos no asignados <none> <ninguno> channelMask="%1" channelMask="%1" AboutBox AboutBox Version 2 pre-alpha MusE 2.0 pre-alfa (C) Copyright 1999-2010 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Derechos de autor 1999-2010 Werner Schweer y otros. Ver http://www.muse-sequencer.org por nuevas versiones y mas información. (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Derechos de autor 1999-2010 Werner Schweer y otros. Ver http://www.muse-sequencer.org por nuevas versiones y mas información. {1999-2012 ?} (C) Copyright 1999-2014 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Derechos de autor 1999-2010 Werner Schweer y otros. Ver http://www.muse-sequencer.org por nuevas versiones y mas información. {1999-2012 ?} {1999-2014 ?} (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Derechos de autor 1999-2010 Werner Schweer y otros. Ver http://www.muse-sequencer.org por nuevas versiones y mas información. {1999-2012 ?} {1999-2014 ?} {1999-2015 ?} MusE MusE (C) Copyright 1999-2020 the MusE development team. See https://muse-sequencer.github.io for new versions and more information. Published under the GNU Public License. &Keep On Rocking! Volver a la sesión Alt+K Alt+K Internals System information (useful for debugging): AppearanceDialogBase Apply Aplicar Ok Aceptar Cancel Cancelar Arranger Arreglador Parts Regiones show names mostrar nombres show events mostrar eventos show Cakewalk Style mostrar al estilo de Cakewalk y-stretch Periodo Y Events Eventos note on nota on poly pressure presión polifónica controller controlador aftertouch aftertouch pitch bend pitch bend program change cambio de programa special especial Wave Drawing in Parts O&nly outline Background picture Imagen de fondo Fill in optimal font sizes in relation to the system font show snap grid muestra la rejilla de captura Colors Colores Items Objetos Style/Fonts Estilo/Tipografía QT Theme tema QT Windows Ventanas MusE MusE Metal Metal Norwegian Wood Madera Noruega Platinum Platino CDE CDE Motif Motif Motif Plus Motif Plus Fonts Tipografía Family Familia Size Tamaño Font 1 Tipografía 1 Themes Show names Show e&vents Show Ca&kewalk style RMS/PEAK (&traditional) Add Remove Eliminar Clear Show snap grid Choose color... Add to palette Save all... Load all... Font 2 Tipografía 2 Size (pt) At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. Help Font 3 Tipografía 3 Font 0 Tipografía 0 Bold Negrita Italic Cursiva ... ... Color name: Nombre de color Global opacity Opacidad global Icon size Custom cursor size Distribute current system font family to all other fonts System Font sizes for fonts 1-6 are determined at every program start, to optimally match the currently used system font. Disable if you want to set fixed sizes in the font settings below. Default scaling factors: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% Current theme If a user style sheet for one of the MusE color schemes exists and is selected, it will be merged with the default style sheet for this scheme. The user style sheet takes precedence and overwrites identical settings in the default style sheet. Cascade default and user theme style sheets Show icons in menus Use native standard dialogs Calculate and apply relative font sizes dynamically at program start Maximum aliased size, 0 = no alias Insert Insertar Font 4 Tipografía 4 Font 5 Tipografía 5 Palette Paleta MusE: Appearance settings MusE: Configuración de apariencia add Agregar remove quitar add to palette Agregar a la paleta B Azul S Saturación H Matíz V Brillo G Verde R Rojo clear Limpiar Style Sheet: Hoja de estilo Font 6 Tipografía 6 ArrangerColumnsBase Configure arranger columns Columns: Name: Nombre: Controller type: Midi controller type Tipo de controlador MIDI Affect CCs at Control7 Control7 Control14 Control14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Program Programa H-Ctrl Control-H Midi controller number high byte Controlar número alto del byte MIDI L-Ctrl Control-L Midi controller number low byte * wild card * begin of song current position &Add &Agregar &Delete &Borrar Done AudioConverterSettingsBase Audio Converter Settings Available audio converters Offline settings... Realtime settings... Gui settings... Use default preferences (see Global Settings) Preferences When only samplerate conversion is required, use: When time stretching or pitch shifting is required, use: OK Aceptar Cancel Cancelar Awl::MidiVolEntry off apagado db dB Awl::VolEntry off Apagar ChooseSysexBase Dialog Dialogo Hex: Comment: Comentario: &OK &Cancel &Cancelar ClipListEditorBase MusE: ClipList MusE: Lista de archivos de audio Name Nombre Refs Referencias Samplerate Len Duración Data Datos Status Clip Properties Propiedades del Clip Pos: Posición: Len: Duración: CommentBase Form1 Form1 Track Comment Comentario de la pista Track 1 Pista 1 ConfigMidiFileBase &OK &OK &Cancel &Cancelar 0 (single track) 0 (una sola pista) 1 (multiple tracks) 1 (Pistas multiples) Format: Formato: Export 96 96 192 192 384 384 Division: División: Copyright: Derecho de autor MusE: Config Midi File Import/Export MusE: Configuración de la importacion/exportación MIDI Import: Importado Split tracks into &parts Dividir en pistas y regiones Alt+P Alt+P Split tracks into parts, or one single part Dividir en pistas y regiones o una sola región. Import Default instrument: Device Name metas trump Port metas if both exist Instrument Name metas trump Mode sysexes if both exist Export: Exportado: Enable extended smf format (currently not implemented) Habilitar formato extendido smf (no implementado) Running Status saves space by not repeating event status bytes. If this is off every event will have a status byte, increasing file size. Use Running &Status To turn a note off some devices can use note-offs or zero-velocity note-ons or both. When used with Running Status this setting saves space. It is safe to leave this off. Replace note-offs with &zero velocity note-ons Use &2-byte time signatures instead of standard 4 Compás de 2 bytes Alt+2 Alt+2 If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum note controllers. If disabled, drum maps are ignored during export. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. See "What's This ?" for more info. If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum controllers. Any Port overrides cause a separate track to be created (requires format 1). Any Channel overrides will still appear in the same track in the exported file, but upon re-import into an app such as MusE, MusE will treat that as a new track and create one. If disabled, drum maps are ignored. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. Upon re-importing into another app, it is up to the other app to redirect the particular drum ports, channels and notes to make the song sound like it was intended. Drum map overrides are unique to MusE. If re-importing into MusE, for faithful reproduction disable this setting and apply a drum map and some overrides later. If re-importing or opening in another midi player, enable this setting so that the player can have a chance at faithful reproduction. Some user adjustment may be required (choice of instruments, ports, etc). Apply drum map Port, Channel, ANote overrides If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. See "What's This ?" for more info. If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. The extra track is automatically created. In this mode all events on a track in the midi file are on the same channel. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. In this mode the track events can be on different channels. But if the file is re-imported into an app such as MusE, MusE will automatically create the extra track. Drum map Channel overrides go to a separate track Export instrument or mode: Mode sysexes Export port or device metas: Instrument name metas Port metas Device name metas Export a Port/Device meta for format 0 Note: Format 0 uses the FIRST midi track's name/comment in the arranger Nota: Formato 0 utiliza el nombre de la primera pista midi como comentario en el arreglista Save space by replacing note-offs with &zero velocity note-ons Ahorro de espacio por la sustitución de "note-off" con cero y la velocidad de "note-on" Alt+Z Alt+Z CopyOnWriteDialogBase Copy Wave Files Some sound files will be copied to the Project Directory, either because they are not writable or because more than one independent Wave Event shares them. (If you prefer instead that the Wave Events are inter-dependent, try using Clone Parts.) Multiple copies will be made in some cases. If no Project has been created yet, you will be asked to, giving another chance to cancel. These files will be copied to the Project Directory: CrescendoBase MusE: Crescendo/Decrescendo MusE: Crescendo/Decrescendo Range Rango Looped Events Eventos en bucle &Looped Events Selected Looped Seleccionar bucles Parts Regiones Selected parts or c&urrent part All parts &in editor Values Valores Start velocity Velocidad al inicio % % End velocity Velocidad al final &Absolute Rela&tive Absolute Absoluto Relative Relativo OK Aceptar Cancel Cancelar DeicsOnzeGui Save configuration Guardar configuración Critical Error Error critico Cannot open file %1 No se puede abrir el archivo %1 Parsing error for file %1 Error de análisis del archivo %1 Load category dialog Cargar diálogo de la categoría Load set dialog Cargar grupo de diálogos Save set dialog Guardar grupo de diálogos New category Nueva categoria Delete category Borrar categoria Load category Cargar categoria Save category Guardar categoria Load set Cargar grupo Save set Guardar grupo Delete set Borrar grupo New subcategory Nueva subcategoria Delete subcategory Borrar sub categoria Load subcategory Crgar subcategoria Save subcategory Guardar subcategoria New preset Nuevo preseteo Delete preset Borrar preseteo Load preset Cargar preseteo Save preset Guardar preseteo No more category supported No se soportan mas categorías You can not add more categories No se pueden agregar mas categorias Do you really want to delete %1 ? ¿Seguro desea borrar %1 ? &Yes &Si &No &No No category selected No hay categoria seleccionada You must first select a category. Primero debe seleccionar una categoria Replace or add Reemplazar o agregar %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 Se supone que es el número de banco superior afectado %2, pero ya hay uno en esta ranura. ¿Quiere reemplazarlo o añadir una ranura libre después? &Replace &Reemplazar &Add &Agregar Download error Error de descarga There is no more free category slot. No hay mas lugar para categorías. Save category dialog Guardar dialogo de categoria No more subcategory supported No se soportan mas sub categorías You can not add more subcategories No se pueden agregar mas sub categorias No subcategory selected No hay subcategoria seleccionada You must first select a subcategory. Primero debe seleccionar una sub categoria Load subcategory dialog Cargar dialogo de subcategoria %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 Se supone que es el número de banco inferior afectado %2, pero ya hay uno en esta ranura. ¿Quiere reemplazarlo o añadir una ranura libre después? There is no more free subcategory slot. No hay mas lugar para sub categorías. Save subcategory dialog Guardar dialogo de subcategoria No more preset supported No se soportan mas preseteos You can not add more presets No se pueden agregar mas preseteos No preset selected No hay preseteos seleccionados You must first select a preset. Primero selecciones un preseteo. Load preset dialog Cargar dialogo de preseteo %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 Se supone que es afectada el número del programa %2,pero ya hay uno en esta ranura. ¿Quieres reemplazarlo o añadir una ranura libre después? There is no more free preset slot. No hay mas lugar para preseteos. Save preset dialog Guardar dialogo de preseteo Browse set dialog Buscar ajustes de diálogo Browse image dialog Buscar imagen de dialogo DeicsOnzeGuiBase DeicsOnze DeicsOnze &Preset &Preseteos Program numerous Numero de programa INITVOICE InitVoice LBank Banco inferior Subcategory Subcategoria Bank numerous Numero de banco NONE Ninguno HBank Banco superior Category Categoria Prog Programa Preset Preselección DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. DeicsOnze v0.5.5 Derechos Reservados(c) 2004-2006 Nil Geisweiller. Publicado bajo licencia GPL. &Global &Global Pitch Envelope Envolvente de afinación PL3 PL3 PL2 PL2 PL1 PL1 PR1 PR1 PR2 PR2 PR3 PR3 Pitch modulation depth Profundidad de modulación de tono LFO LFO LFO Sync LFO Sync Pitch modulation sensitivity Pitch modulation sensitivity Pitch Modulation Sensitivity Pitch Modulation Sensitivity LFO Delay LFO Delay LFO delay LFO delay LFO speed LFO speed Amplitude modulation depth Amplitude modulation depth Amplitude modulation sensitivity Amplitude modulation sensitivity Amplitude Modulation Sensitivity Amplitude Modulation Sensitivity AMS AMS LFO Waveform LFO Waveform Pitch Modulation Depth Pitch Modulation Depth PMD PMD LFO Speed LFO Speed AMD AMD Speed Velocidad Delay Delay PMS PMS Modulation Matrix Modulation Matrix <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> Op4 Feedback Op4 Feedback Feedback level of the operator 4 Feedback level of the operator 4 Transpose Transponer Op &1 Op &1 Scaling 1 Scaling 1 LS1 LS1 RS1 RS1 Rate Scaling Rate Scaling Attack Rate of the operator 1 Attack Rate of the operator 1 Level Scaling Level Scaling Amplitude Envelope 1 Amplitude Envelope 1 RR1 RR1 D1R1 D1R1 D1L1 D1L1 D2R1 D2R1 Release Rate Release Rate 2° Decay Rate 2° Decay Rate 1° Decay Level 1° Decay Level 1° Decay Rate 1° Decay Rate Attack Rate Attack Rate AR1 AR1 Detune, OSCWave, EGShift 1 Detune, OSCWave, EGShift 1 DET1 DET1 Detune Detune EG Shift EG Shift 96dB 96dB 48dB 48dB 24dB 24dB 12dB 12dB Wave form Wave form Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Sensitivity 1 Sensitivity 1 KVS1 KVS1 Amplitude Modulation Enable Amplitude Modulation Enable AME1 AME1 Keyboard Velocity Sensitivity Keyboard Velocity Sensitivity Key Velocity Sensitivity Key Velocity Sensitivity EBS1 EBS1 EG Bias Sensitivity EG Bias Sensitivity Eg Bias Sensitivity Eg Bias Sensitivity Frequency 1 Frequency 1 Coarse 1 Coarse 1 Coarse Ratio Coarse Ratio Fine 1 Fine 1 Fine Ratio Fine Ratio Freq 1 Freq 1 Fixed Frequency Fixed Frequency Toggle Fix Frequency Toggle Fix Frequency FIX FIX OUT 1 OUT 1 Output Volume Output Volume Volume Volume Op &2 Op &2 Amplitude Envelope 2 Amplitude Envelope 2 D1R2 D1R2 D1L2 D1L2 D2R2 D2R2 RR2 RR2 AR2 AR2 Frequency 2 Frequency 2 Coarse 2 Coarse 2 Fine 2 Fine 2 Freq 2 Freq 2 Scaling 2 Scaling 2 LS2 LS2 RS2 RS2 OUT 2 OUT 2 Detune, OSCWave, EGShift 2 Detune, OSCWave, EGShift 2 DET2 DET2 Sensitivity 2 Sensitivity 2 EBS2 EBS2 KVS2 KVS2 AME2 AME2 Op &3 Op &3 Amplitude Envelope 3 Amplitude Envelope 3 D1R3 D1R3 D1L3 D1L3 D2R3 D2R3 RR3 RR3 AR3 AR3 Scaling 3 Scaling 3 LS3 LS3 RS3 RS3 OUT 3 OUT 3 Frequency 3 Frequency 3 Coarse 3 Coarse 3 Fine 3 Fine 3 Freq 3 Freq 3 Detune, OSCWave, EGShift 3 Detune, OSCWave, EGShift 3 DET3 DET3 Sensitivity 3 Sensitivity 3 EBS3 EBS3 KVS3 KVS3 AME3 AME3 Op &4 Op &4 amplitude Envelope 4 amplitude Envelope 4 AR4 AR4 D1R4 D1R4 D1L4 D1L4 D2R4 D2R4 RR4 RR4 Frequency 4 Frequency 4 Coarse 4 Coarse 4 Fine 4 Fine 4 Freq 4 Freq 4 Scaling 4 Scaling 4 LS4 LS4 RS4 RS4 OUT 4 OUT 4 Detune, OSCWave, EGShift 4 Detune, OSCWave, EGShift 4 DET4 DET4 Sensitivity 4 Sensitivity 4 EBS4 EBS4 KVS4 KVS4 AME4 AME4 &Func &Func Delay Pan Depth Delay Pan Depth Delay Pan LFO Freq Delay Pan LFO Freq Delay Ch Send Level Delay Ch Send Level Channel Chorus Channel Chorus Delay Feedback Delay Feedback Delay On/Off, Return Level Delay On/Off, Return Level On On Delay Beat Ratio Delay Beat Ratio Delay BPM Delay BPM Foot Control Foot Control Pitch Bend Range Pitch Bend Range Pitch Pitch Amplitude Amplitude Modulation Wheel Modulation Wheel Breath Control Breath Control Pitch Bias Pitch Bias Envelope Bias Envelope Bias After Touch After Touch Phony Mode Phony Mode POLY POLY MONO MONO Potamento Portamento Portamento Mode Portamento Mode FINGER FINGER FULL FULL PT PT Portamento Time Portamento Time C&horus C&horus Chorus Parameters Chorus Parameters Channel send level Channel send level On/Off and Return level On/Off and Return level Select LADSPA plugin Select LADSPA plugin Change plugin Cambiar efecto &Reverb &Reverb Reverb Parameters &Config &Configuración Font Size Tamaño de fuente Quality Calidad High Alta Middle Media Low Baja Ultra low Muy baja Filter Filtro Save Mode (into the song) Guardar modo (en la sesión) Save only the used presets Guardar solo el preseteo Save the entire set Guardar todas las configuraciones Save the configuration Guardar configuración Configuration File Archivo de configuración Save... Guardar... Load... Abrir... Save as default Guardar por defecto Colors Colores Text Texto Background Fondo Edit Text Editar texro Edit Background Editar fondo Red Rojo Blue Azúl Green Verde Set Path Ajustes de la ruta Image in the background : Imagen de fondo: Browse... Buscar... Load the set at the initialization : Cargar al iniciar Set Brightness, Detune, Attack and Release of the current channel to default Ajustes de Brightness, Detune, Attack y Release del canal actual por defecto Res. Ctrl Reestablecer Cut all notes off Apaga todas las notas Panic! Pánico! Number of Voices Número de voces Number of voices Número de voces Enable Habilitado Channel Canal Vol Volumen Channel Ctrl Controlador de canal Release Release Attack Ataque Brightness Brillo Modulation Modulación Pan Panorámica DelOverlapsBase MusE: Delete Overlaps MusE: Borrar superpuestos Range Rango All Events Todos los eventos Selected Events Eventos seleccionados Looped Events Bucle de eventos Selected Looped Selecionar bucle Parts Regiones Selected parts or c&urrent part All parts &in editor OK Aceptar Cancel Cancelar DidYouKnow Did you know? ¿Sabia usted que? Don't show on startup No volver a mostrar Next tip Siguiente sugerencia Close Cerrar DuplicateTracksBase Duplicate tracks Number of copies Copy all routes Default routing No routes Duplicate all parts Make independent copies of all parts Make clones of all parts No parts Copy drumlist Copy standard (vol, pan) and synth controllers Copy effects rack plugins Copy plugin controllers Ok Aceptar Cancel Cancelar EditCtrlBase MusE: Edit Controller Event MusE:-Editar eventos de control Time Position Posición de tiempo Available Controller: Control disponible Create New Controller Crear nuevo control textLabel3 textLabel3 Value Valor Controller Controlador Note Nota H-Bank Banco-H L-Bank Banco-L Program Programa off apagado pushButton4 pushButton4 &OK &Aceptar &Cancel &Cancelar EditGainBase MusE: Modify gain MusE:-Modificar ganancia Gain Ganancia 200% 200% 100% 100% 0% 0% &Reset &Resetear Alt+R Alt+R &Apply &Aplicar Alt+A Alt+A &Cancel &Cancelar Alt+C Alt+C EditInstrumentBase MusE: Instrument Editor MusE: Editro de Instrumento High Bank: Banco superior: Low Bank: Banco inferior: Program: Programa: Patch program number. --- means don't care. &Delete &Borrar Alt+D Alt+D High Bank Low Bank Patch program number. --- means don't care. Patch Name: Dr def Drum Percusión GM GM GS GS XG XG This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. Lista de controladores MIDI de uso común Tenga en cuenta que en Muse cambios de tono y el programa se manejan como los controladores normales. Properties Propiedades Control7 Control7 Control14 Control14 RPN RPN NRPN NRPN Pitch Tono Program Programa H-Ctrl Control-H L-Ctrl Control-L Min Minimo Max Máximo Name Nombre Drum&maps Type Tipo Hex Entry: Hexadecimal: &File Archivo Tools Herramientas New Nuevo &New &Nuevo Ctrl+N Ctrl+N Open Abrir &Open... &Abrir... Ctrl+O Ctrl+O Save Guardar &Save &Guardar Ctrl+S Ctrl+S Save As Guardar como Save &As... Guardar &Como... Exit Salir E&xit &Salir Instrument Name: Nombre de instrumento: Selected instrument name. Seleccionar nombre de instrumento List of defined instruments. Lista de instrumentos definidos Note off mode: Note off mode Selects how to handle note off events. The instrument can use note off events, or not at all, or convert them to zero-velocity note on events which helps save midi interface bandwidth. Refer to the instrument manufacturer's midi implementation chart for details. Pa&tches Grupos List of groups and patches. Lista de grupos Group/Patch Grupos Name: Nombre: Group or patch name Nombre de grupo o sección Patch high bank number Número de grupo de banco superior Patch high bank number. --- means don't care. Número de grupo de banco superior. --- dont care no importa Patch low bank number Número de grupo de banco inferior Patch low bank number. --- means don't care. Número de grupo de banco inferior. --- Patch program number Número de parte de programa Drum patch Parte de percusiòn If set, the patch is available only for drum channels. Si se establece, el grupo estará disponible únicamente para los canales de percusión. GM patch Grupo GM If set, the patch is available in a 'GM' or 'NO' midi song type. Si se establece, estará disponible el grupo "GM" para la sesión. GS patch Grupo GS If set, the patch is available in a 'GS' or 'NO' midi song type. Si se establece, estará disponible el grupo "GS" para la sesión. XG patch Grupo XG If set, the patch is available in an 'XG' or 'NO' midi song type. Si se establece, estará disponible el grupo "XG" para la sesión. Show in tracks: Midi MIDI &Drum Delete group or patch Borrar grupo New patch Nueva grupo New &Patch Nueva gru&po Alt+P Alt+P New group Nuevo grupo New &Group Nuevo &Grupo Alt+G Alt+G Contro&ller Contro&lador Common: Comunes: List of defined controllers Lista de controladores definidos List of defined controllers. Lista de controladores definidos. Min Mínimo Max Máximo Def Def Midi controller name Nombre de controlador MIDI Type: Tipo: Midi controller type Tipo de controlador MIDI RPN14 RPN14 NRPN14 NRPN14 Midi controller number high byte Controlar número alto del byte MIDI Midi controller number low byte (* means drum controller) Controlador MIDI byte bajo (* significa que controla la percusión) Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and GS/XG type songs and instruments. Allows controllers for each instrument in Muse's drum map. The low byte will be replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. Controlador MIDI byte bajo. Byte bajo es * el controlador regulador de percusión,-para las pistas de batería e instrumentos GS / XG. * wild card * Range: Rango: Minimum value. If negative, auto-translate. Valor mínimo. Si es negativo,se traduce automaticamente. Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. Useful for controllers which should be displayed with zero bias. For example, 'Pan': Minimum: -64 Maximum: 63 True range: Min: 0 Max: 127 (bias = 64) 'CoarseTuning': Min: -24 Max: 23 True range: Min: 40 Max: 87 (bias = 64) Bias is determined from controller type: 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 Type 'Pitch' is the exception. It is biased at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) Valor mínimo. Si el valor mínimo es negativo, el rango automáticamente se traduce en un rango positivo. El valor es determinado por el tipo de controlador 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 'Pitch' es la excepción. Está fijada a cero, incluso con un mínimo negativo 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) Maximum value Valor máximo Default: Por defecto: L-Bank Banco-L off apagado Progr. Programa ??? ??? H-Bank Banco-H Default value. Off: No default. El valor por defecto. Apagado: No hay valor por defecto. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). Valor por defecto (inicial) . Apagado, no tiene valor predeterminado. Si se elije un valor por defecto , el valor será enviado cuando el controlador se añada en la sesión (en el editor de matríz o editor de lista). cuando si la sesión se vuelve a cargar, el valor se vuelve a enviar. De lo contrario el controlador permanece en su último valor. Los controladores también se añaden automáticamente a una sesión a la recepción de un evento de controlador midi. off dont care apagado Add common controller Agregar controlador común &Add &Agregar Alt+A Alt+A Delete controller Borrar controlador Create a new controller Crear un nuevo controlador New &Controller Nuevo Alt+C Alt+C Null Param Hi: Byte alto no válido: Null parameter number High byte El número de byte alto no es válido. If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events from corrupting the RPN/NRPN controller. Typically, set to 127/127, or an unused RPN/NRPN controller number. Si se establece, 'nulo' el número de parámetro será enviados después de cada evento RPN / NRPN. Esto evita que los acontecimientos posteriores de "datos" corrompan el controlador RPN / NRPN. Por lo general, establezca a 127/127, o usar el número de controlador RPN / NRPN. Lo: Inferior: Null parameter number Low byte El parametro inferior es nulo S&ysEx S&ysEx SysEx List: Lista se Sistema exclusivo (SysEx) New SysE&x Nuevo SysE&x Alt+X Alt+X Patch Collections: &Copy &Copiar &Remove &Up &Down Contro&llers &Add Common... W W Drum default: Comment: Comentario: Note: The enclosing F0 ... F7 are not required &Initialization Instrument initialization sequence: &Add... &Change... &Help A&yuda &Close Close Cerrar new item Nuevo ítem What's this? ¿Que es esto? Info Info Additional information EditNoteDialogBase MusE: Enter Note MusE: Escribe Nota OK Aceptar Cancel Cancelar Length: Duración: Time Position: Posición de tiempo: Pitch: Tono: Velocity On: Velocidad On: Velocity Off: Velocidad Off: EditSysexDialogBase MusE: Enter SysEx MusE: Introducir sistema exclusivo TimePosition: Posición de tiempo: Hex: Note: The enclosing F0 ... F7 are not required Name: Nombre: Comment: Comentario: &Select... &OK &Cancel &Cancelar OK Aceptar Cancel Cancelar FLUIDGui FLUID: open Soundfile FLUID: Archivo de sonido abierto FLUIDGuiBase Form1 Formulario1 Soundfont Fuente de sonido Load Cargar FLUIDSynthGuiBase FLUID Synth Fluid Synth Load Cargar Delete Borrar Dump Info Volcar información ID ID Fontname Nombre de fuente Chnl Canal Soundfont Fuente de sonido Drum Chnl Canal de percusión Level Nivel Width Anchura Damping Amortiguamiento Room Size Tamaño sala Reverb Reverberación CHANNEL SETUP CONFIGURACIÓN DE CANAL Sine Seno Triangle Triángulo Type Tipo Number Número Speed Velocidad Depth Profundidad Chorus Coro ;-) Gain Ganancia LOADED SOUNDFONTS SOUNDFONTS CARGADOS FileDialogButtons Home Global Global User Usuario Project Sesión read Midi Port Configuration write window states fdialogbuttons Dialogo de botones FluidSynthGui Choose soundfont Elegir soundfont GateTimeBase Range Rango All Events Todos los eventos Selected Events Eventos seleccionados Looped Events Eventos en bucle MusE: Modify Note Length MusE: Cambiar duración de notas Selected Looped Seleccionar bucles Parts Regiones Selected parts or c&urrent part All parts &in editor Values Valores Rate: Velocidad: Offset: Desplazamiento: % % lenNew = (lenOld * rate) + offset lenNew = (lenOld * rate) + offset OK Aceptar Cancel Cancelar GlobalSettingsDialogBase MusE: Global Settings MusE: Configuración global globales Audio Audio Mixer Mezclador dB dB min. Meter Value Valor mínimo del vúmetro Midi MIDI Ticks Tics 1024 1024 2048 2048 4096 4096 Displayed Resolution (Ticks/Quarternote) Resolución mostrada (tics/negra) 48 48 96 96 192 192 384 384 768 768 1536 1536 3072 3072 6144 6144 12288 12288 RTC Resolution (Ticks/Sec) Resolución del reloj (Tics/segundo) GUI GUI /sec /seg GUI Refresh Rate Velocidad de refresco de la interfáz Warn if timer frequency is inadequate Track height Use project save dialog Usar diálogo de guardado Some popup menus stay open (else hold Ctrl) Menús emergentes (si no mantenga presionado Ctrl) Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. Permite tener abiertos algunos menús emengentes. De lo contrario, mantenga la tecla Ctrl para mantenerlos abiertos. In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. En algunas zonas, el botón central del ratón disminuye el valor, mientras que el botón derecho lo aumenta. Los usuarios sin un botón central del ratón, puede seleccionar esta opción para hacer que el botón de la izquierda se comporte como el botón del medio. Use left mouse button for decreasing values Botón izquierdo del ratón reduce el valor Shift + Right click sets left range marker Ctrl + clic derecho establece marcador de la izquierda. Allow adding hidden tracks in track list menu Unhide tracks when adding hidden tracks Smart focus After editing, controls will return focus to their respective canvas Show newly created midi velocity graphs per-note px Keep transport window on top (requires restart) Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. Borderless zoom/pan mouse (else use alternate method) Scrollable submenus Sample rate conversion, time stretching and pitch shifting Editors Open editors in tabs (else in separate windows) Piano roll Editor de matríz Drum editor Editro de percusión Wave editor Editro de audio Score editor Editor de partituras Master track editor Editor de pista maestra DSSI VST Directories where dssi-vst (if installed) looks for Wine vst plugins (*.dll files) Directories containing Linux vst plugins (*.so files) Audio converter settings... Midi controller behaviour Send Null parameters after an (N)RPN value If set, 'null' parameter numbers will be sent after each RPN/NRPN event. (A 'null' parameter number is 127.) This prevents subsequent rogue or accidental 'data' events (without parameters) from corrupting the current RPN/NRPN controller. Normally it is OK to leave this unset, but use it if there is a possibility of such 'data' events. If this is set, the 'Optimize controllers' setting does not affect these controllers' parameters, only the H/L values. Send Null parameters Don't send redundant parameters or values If set, redundant H/L parameters or H/L values are not sent. They are sent only if they changed. This can save midi interface bandwidth. But it is possible the receiving device might miss the previous changes (for example if the device is turned on after they were sent). The 'Panic' button will force them to be resent. Certain operations will also force them to be resent, such as clicking a GUI midi control (but not while adjusting it). Optimize controllers Record drum tracks Record a&ll instruments Latency Whether to enable latency correction/compensation: Enable latency correction/compensation Project latency Whether completely independent branches share a common project-wide latency: Project-wide latency Unterminated branches Whether unterminated branches participate in latency correction/compensation: Unterminated input branches Unterminated output branches Track monitoring Whether track monitor buttons affect latency correction/compensation: Monitoring affects latency Revert native GUI scaling for plugins in HiDPI Live update wave parts while recording Whether to show knob and slider values in certain places, for example mixer strips. Turn off to reduce clutter. Revert native UI window HiDPI scaling (some plugins ignore this setting) Show knob and slider values Whether to show knobs or sliders in certain places, for example mixer strips Prefer midi volume as decibels instead of 0-127 Style hack: Force line edit widgets to draw a frame Prefer knobs instead of sliders GUI refresh rate Use first available Ask once Ask always Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. This hack forces a frame to be drawn, but may interfere with other styles. Use old-style stop shortcut Monitor on record-arm automatically Show note names on notes in pianoroll Whether record-arming a track automatically activates monitoring. LV2 UI open behavior Set range markers (left / right) Middle mouse button / Right mouse button Ctrl + Right mouse button / Right mouse button Ctrl + Left mouse button / Ctrl + Right mouse button Options Show status bar Show note tooltips in midi editors GUI Tweaks Tweaks MDI-subwindowness and sharing menus MDI Sub ventanas y posición de menús Presets: Preseteos: traditional MusE SDI Comportamiento tradicional de MusE Cakewalk-like MDI Comportamiento similar a Cakewalk Borland-/Mac-like MDI Comportamiento similar a Borland/Mac &Apply &Aplicar &Ok Ac&eptar &Cancel &Cancelar Application Aplicación Start Muse Inicio de MusE start with last song Abrir la última sesión start with song Abrir la sesión Views Vistas y-pos pos. Y show Ver x-pos Pos. X height Ancho width Altura Big Time Ventana de tiempo Transport Transporte Cur Actual set current values Establecer valores actuales show splash screen Mostrar pantalla de inicio Mixer A Mezcaldora A Mixer B Mezcladora B show "Did you know?" dialog Ver dialogo ¿Sabia usted que? Start song Iniciar Sesión min. Slider Val Valor mínimo del potensiómetro Enable denormal protection Habilitar proteccion Enable output limiter Habilitar limitación de salida External Waveditor Editor de audio externo External Waveditor command Editor de audio externo (comando) Note: External editor opened from the internal editor. Note: El editor externo se abre desde adentro Dummy Audio Driver (settings require restart) Controlador de audio en vacío (El cambio requiere reinicio) Hz Hz Period size (Frames per period): Tamaño del período (Cuadros por período) 16 16 Project directory ... ... Main Window Ventana principal Choose start song or template Start MusE Reset to default Start with &last song Start with &template Sta&rt with song On launch Show splash screen Show "Did you know?" dialog Start template or song: Read MIDI Ports configuration from file, or else automatically configure Read MIDI Ports configuration Warn if opening file versions different than current version Auto save (every 5 minutes if not playing/recording) Plugin Paths Ordered by priority. Changes take effect on restart. The plugin cache is used for safety and speed. It automatically rescans when any of the given directories change, for example by installing a new plugin. Click this button to force the cache to recreate upon next restart. &Force plugin rescan and cache recreation on next restart LADSPA DSSI Linux VST LV2 VL2 NOTE: Ensure the directory containing core LV2 bundles is included (typically /usr/lib/lv2). Otherwise all plugins may not load properly. Move up Move down Add... Edit... Remove Eliminar Audio driver settings (require restart) Sample rate: Audio backend: Shorter periods give better midi playback resolution. Note that this cannot be set with Jack devices! Period size (frames per period): Note that samplerate cannot be set with Jack devices! Minimum meter value Minimum slider value Try to use Jack Freewheel Habilitar Jack para juego libre Speeds bounce operations Velocidad de las operaciones de rebote Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Usar juego libre para Jack si es posible Reduce dramaticamente el tiempo de rebote. VST in-place VST emplazado Enable VST in-place processing (restart required) Activar VST en el prosesamiento (Requiere reiniciar) Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. Activar VST en el procesamiento. Desactive esta opción si Rack de efectos Ladspa VST no está instalado (Requiere reiniciar) Minimum control period Periodo mínimo de control Minimum audio controller process period (samples). Período mínimo del control de proceso de audio (muestras) Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. Período mínimo del control de proceso de audio (muestras) Ajusta la sensibilidad de los controles de audio y gráficos de control. Establecer un valor bajo para un control rápido y suave. Si le causa problemas de rendimiento, establezca un valor mayor. 1 1 2 2 4 4 8 8 32 32 64 64 128 128 256 256 512 512 Sample rate Frecuencia de muestreo Shorter periods give better midi playback resolution. Períodos más cortos dan una mejor resolución de la reproducción midi. &Don't record hidden instruments Don'&t record muted instruments Don't record &hidden or muted instruments Instrument initialization Send instrument initialization sequences Warn if instrument initialization sequences pending Send instrument controller default values if none in song, at init or rewind 8192 5 1/3' {8192?} 16384 5 1/3' {16384?} 32768 5 1/3' {32768?} Midi Resolution (Ticks/Quarternote) Resolución MIDI (Tics/Negra) Use old-style stop shortcut: Atajo antiguo para detener Move single armed track with selection Armar la pista con la selección (Grabar) On Launch Lanzar al inicio Behavior Comportamiento LegatoBase MusE: Legato MusE: Legato Range Rango All Events Todos los eventos Selected Events Eventos seleccionados Looped Events Eventos del bucle Selected Looped Bucle seleccionado Parts Regiones Selected parts or c&urrent part All parts &in editor Settings Configuración ticks Tics Minimum Length Duración minima: Allow shortening notes Permitir notas cortas OK Aceptar Cancel Cancelar MITTransposeBase MusE: Midi Input Plugin: Transpose MusE: Plugin de entrada MIDI : Transposición On Encendido TriggerKey Tecla de salto Transpose: Trasponer: +0 +0 MRConfigBase MusE: Midi Input Plugin: Remote Control MusE: Pluginde entrada MIDI : Control remoto Activate Activar On Encendido Actions Acciones Stop Parar Record Grabar Goto Left Mark Ir a marca izquierda Play Reproducir Insert rest (step rec) Insertar el resto MdiSettingsBase Form Forma GroupBox Grupos MDI subwin MDI sub ventana Shares menu when subwin Acciones de menú cuando es sub ventana Shares menu when free Acciones de menú cuando es ventana libre MetronomeConfigBase MusE: Metronome Config MusE: Configuración del metrónomo Metronome Metrónomo Audio Beep Bip de audio Volume Volume Audio master Meas Beat Accent1 Accent2 Sample Preferences Apply the settings globally or to the current song: Use global metronome Use song-specific metronome Enable accents for the audio and midi metronomes: &Two samples (old samples) Four samples (&new samples, with accents) Audio Audio MIDI MIDI Click Clic MIDI Midi Channel Canal MIDI Accent 2 Velocity Accent 1 Velocity Accent 1 Note Measure Note Nota de compás Measure Velocity Velocidad de compás Beat Velocity Velocidad de barra Beat Note Nota de barra Midi Port Puerto MIDI Precount Cuenta atrás enable habilitada Bars Compases From Mastertrack desde la pista Mestra / / Signature Compás Prerecord Pre-captura Preroll Pre-escucha &Apply &Aplicar &OK Ac&eptar &Cancel &Cancelar Choose outputs... Seleccionar salidas... 50 50 % Audio volume % De volúmen de audio Hint: Enable metronome in Transportpanel Habilitar metrónomo en el panel de transporte Accent 2 Note Off Apagado Accents Accent sequence beats Current accent 1 sequence: (Click to change) Current accent 2 sequence: Factory User Usuario Preset sequences Add current accents to presets Make a new preset from the current accents. + Delete preset Borrar preseteo Delete the selected preset. Only user presets can be deleted. - Use preset Copy the preset to the current accents. -> Reset to Factory or User defaults Resets all current accent sequences to default Factory or User presets. ! Enable the precount Enable Habilitado Number of bars Use Mastertrack time signature Beats per bar Beat note type Enable precount when playing Precount on play Mute the metronome after precount Mute metronome Not supported yet Alt+A Alt+A Alt+O Alt+O Alt+C Alt+C MidiAudioControlBase Midi control Port: Channel: Control type: &Learn Control7 Control7 Control14 Control14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Program Programa Hi: Lo: MidiFilterConfigBase MusE: Midi Input Filter MusE: Filtro de entrada MIDI Record Filter Filtro de captura Note On Nota on Poly Pressure Presión polifónica Controller Controlador Program Change Cambio de programa After Touch After Touch Pitch Bend Rueda de afinación Sysex Sistema exclusivo Thru Filter Filtro Thru Controller Filter Filtrar controlador Channel Filter Filtro de canal 14 14 10 10 6 6 12 12 4 4 2 2 9 9 8 8 3 3 13 13 15 15 16 16 7 7 11 11 5 5 1 1 MidiInputTransformDialogBase MusE: Midi Input Transformator MusE: Transformador de entrada MIDI Filter Filtro All Todo Equal Igual Unequal Diferente Note Nota Poly Pressure Presión polifóica Control Change Cambio de control Aftertouch AfterTouch Pitch Bend Rueda de modulación NRPN NRPN RPN RPN Program Programa Value 2 Valor 2 Value 1 Valor 1 Event Type Tipo de Evento Ignore Ignorar Higher Mayor Lower Menor Inside Entre Outside Fuera Channel Canal Port Puerto Processing Procesando Keep Mantener Fix Arreglar Plus Más Minus Menos Multiply Multiplicar Divide Dividir Invert Invertir ScaleMap Mapa de escalado Flip cambiar Dyn Dinámico Random Aleatorio Toggle Modules Módulos 1 1 2 2 3 3 4 4 enable modul 1 habilitar módulo 1 enable modul 2 habilitar módulo 2 enable modul 3 habilitar módulo 3 enable modul 4 habilitar módulo 4 Preset Preselección Name: Nombre: Comment: Comentario: Function Función &New &Nueva create new preset crea nueva preselección &Delete &Borrar delete preset borra la preselección Enable module 1 Enable module 2 Enable module 3 Enable module 4 &Dismiss &Ocultar PresetList Lista de Preselecciones MidiSyncConfigBase MusE: Midi Sync MusE: Sincronización MIDI Apply Aplicar Ok Aceptar Cancel Cancelar hour hora h h minute minuto m m second segundo s s frame frame f f subframe sub-frame 24 24 25 25 30D 30D 30N 30N MTC MTC Type: Tipo: Offset: Desplazamiento: Sync receiving and sending Sincronizar la recepción y envío Send and receive Jack transport Recepción y envío del transporte de Jack Send and receive Jack transport information, including stop, start and position. Información de la recepción y envío del transporte de Jack, incluye detener, iniciar y posición. Use Jack transport Usar transporte de Jack Make MusE the Jack transport Timebase Master Hacer de MusE el transporte de base de tiempo principal para Jack Make MusE the Jack transport Timebase Master. Allows Jack to show time as MusE Bars, Beats, and Ticks. MusE will try to become master, but other Jack clients can also take over later. You can always click here again for Master. Hacer de MusE el transporte de base de tiempo principal para Jack Permite a Jack para mostrar el tiempo como Compases de MusE , pulso, e inserciones. MusE tratará de convertirse en maestro, pero otros clientes de Jack también pueden hacerlo más adelante. Siempre puede hacer clic aquí de nuevo para definir como maestro. Jack transport Timebase Master Usar Jack como transporte maestro Control MusE timing by external midi clock or MTC sync Control de tiempo de MusE por el reloj MIDI externo o sincronización MTC When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). Cuando está en modo esclavo, el tempo es controlado externamente. Muse se sincroniza con el reloj midi, o sincronización MTC Entradas habilitadas en la lista que rigen (RMC, RMMC, CRFM). Slave to external sync Esclavo de sincronía externa Averaging applied to recorded external tempo changes. External midi clock can be very jittery. Tempo is derived from it and recorded. It is usually desirable to average it and limit the number of recorded changes. Tiny: 2 section 4/4 = 8 stages. 1/8T note averaging, may produce jitter. Small: 3 section 12/8/4 = 24 stages. 1/4 note averaging, may still produce jitter. Medium: 3 section 28/12/8 = 48 stages. 1/2 note averaging. Less jitter. Large: 4 section 48/48/48/48 = 192 stages. Use this if the song has only one tempo. Very low quantization values can be used. Large pre-detect: 4 section 8/48/48/48 = 152 stages + first stage large step pre-detector. Use this if you expect sudden large tempo steps. None: Use only if high accuracy is needed for audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. Tempo record averaging bpm Tempo record quantization Send start to first clock delay Retraso del envío de empezar el primer reloj. This button and other transport related buttons are found on the Transport Panel Allows 'slow sync' devices time to synchronize to MusE. This value is the delay from sending start to sending the first clock. Permite "sincronización lenta" para dispositivos sincronizar con Muse. Este valor es el retraso del envío de empezar el primer reloj. ms ms Send sync delay Enviar retraso de sincronización Note: Sync delay and MTC sync currently not fully implemented MidiTrackInfoBase MusE: TrackInfo MusE: Información de pista output channel canal de salida % % output port puerto de salida off apagado Transp. Transporte Channel Info Información de Canal Rec: Grabar Prog Programa Vol Volumen Pan Panorama Delay Retardo H-Bank Banco-superior Compr. Compresión L-Bank Banco-inferior Velocity Velocidad Length Duración all midi events are sent to this output channel Todos los eventos MIDI se envían a este canal de salida Out ch Canal de salida input routing Ruteo de entrada output routing ruteado de entrada input detect Detectar entrada Input detect indicator. Detects all note on-off, controller, aftertouch, program change, and pitchbend (but not sysex or realtime) events on the selected channels, on the selected midi ports. Indicador de entrada. Detecta todas las notas de encendido y apagado, eventos , de control, aftertouch, cambio de programa, y pitchbend en los canales seleccionados, en los puertos MIDI seleccionados. (No detecta envios SysEx o tiempo real) W W Midi thru MIDI thru Pass input events through ('thru') to output. Pasar los eventos de entrada a la salida (Thru). Select instrument patch Seleccione el instrumento <unknown> <desconocido> Add all settings to song Agregar todos los ajustes a la sesión All Todo Add bank + prog settings to song Agregar ajustes de banco y programa a la sesión Add vol setting to song Agregar ajustes de volumen a la sesión Add pan setting to song Agregar ajuste de panorámica a la sesión MidiTransformDialogBase MusE: Midi Transformator MusE: Transformador MIDI &New &Nueva &Delete &Borrar &Apply &Aplicar &OK Ac&eptar &Cancel &Cancelar PresetList Lista de preselecciones Processing Procesando Event Type Tipo de evento Keep Mantener Fix Arreglar Note Nota Poly Pressure Presión polifónica Control Change Cambio de control Aftertouch AfterTouch Pitch Bend Rueda de modulación NRPN NRPN RPN RPN Plus Más Minus Menos Multiply Multiplicar Divide Dividir Value 2 Valor 2 Invert Invertir ScaleMap Escalado de mapa Flip Cambiar Dyn Dyn Random Aleatorio Value 1 Valor 1 Length Duración Position Posición Randomize Filter Filtro All Todo Equal Igual Unequal Diferente Ignore Ignora Higher Mayor Lower Menor Inside Entre Outside Fuera Bar Range Rango de compases Preset Preselección Name: Nombre: Comment: Comentario: Range Rango process all events procesar todos los eventos selected tracks seleccionar pistas inside loop dentro de bucle Function Función Select Seleccionar Quantize Cuantizar Delete Borrar Transform Transformar Insert Insertar Copy Copiar Extract Extraer Quantize Value Cuantizar valor MixdownFileDialogBase MusE: Set Mixdown Wavefile MusE: Selecciona el archivo de Masterización &OK &Aceptar &Cancel &Cancelar File Path Directorio del archivo Channel Canal Stereo Estéreo Mono Mono 5.1 5.1 wav,16 Bit wav, 16 bits wav, 24 Bit wav, 24 bits wav, 32 Bit (float) wav, 32 bits Format Formato MoveBase MusE: Move Notes MusE: mover notas Range Rango All Events Todos los eventos Selected Events Eventos seleccionados Looped Events Eventos del bucle Selected Looped Bucle seleccionado Parts Regiones Selected parts or c&urrent part All parts &in editor Value Valor Move by Mover ticks Tics OK Aceptar Cancel Cancelar MusECore::AudioPreviewDialog Stop Parar Play Reproducir MusECore::RasterizerModel Off Apagado Bar MusECore::Scripts Do you want to process ALL or only selected events? &Selected &All MusE - external script failed MusE - fallo de orden externa MusE was unable to launch the script, error message: %1 Re-read script names from disc MusECore::Song Jack shutdown! Detener Jack Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: - a performance issue with your particular setup. - a bug in MusE (or possibly in another connected software). - a random hiccup which might never occur again. - jack was voluntary stopped by you or someone else - jack crashed If there is a persisting problem you are much welcome to discuss it on the MusE mailinglist. (there is information about joining the mailinglist on the MusE homepage which is available through the help menu) To proceed check the status of Jack and try to restart it and then . click on the Restart button. Jack ha detectado un problema de rendimiento y MusE lo ha desconectado Esto podría suceder debido a varias razones: - Un problema de rendimiento con su configuración particular. - Un fallo en Muse u otro software de conexión. --Un error al azar que tal vez nunca vuelva a ocurrir. - Jack fue detenido por usted u otra persona - Jack se estrelló Si el problema persiste estaremos dispuesto a analizarlo La lista de correos de MusE ee encuentra disponible a través del menú de ayuda. Para comprobar el estado de Jack y reiniciar el servidor. haga clic en el botón Reiniciar. Automation: Automatización previous event evento anterior next event evento siguiente add event agregar evento erase event borrar evento erase range borrar rango clear automation limpiar automatizacion Automation Automatizacion Previous event Next event Set event Add event Erase event Erase range Clear automation Midi control Assign Clear Clear all controller events? Limpiar todos los eventos de control &Ok Ac&eptar &Cancel &Cancelar Controller Controlador Bypass MusE: Tempo list External tempo changes were recorded. Transfer them to master tempo list? MusE - external script failed MusE - fallo de orden externa Und&o Deshacer Re&do &Rehacer MusEGui select Pointer Tool: with the pointer tool you can: select parts move parts copy parts Selecciona la herramienta Puntero: con la herramienta de puntero puede: seleccionar regiones mover regiones copiar regiones. select Pencil Tool: with the pencil tool you can: create new parts modify length of parts Selecciona la Herramienta de Lápiz: con la herramienta de Lápiz puede: crear nuevas regiones modificar la duración de las regiones. select Delete Tool: with the delete tool you can delete parts selecciona la Herramienta de Borrar: con la herramienta de Borrar puedes borrar regiones. select Cut Tool: with the cut tool you can split a part selecciona la Herramienta de Corte: con la herramienta de Corte puede dividir una región select Glue Tool: with the glue tool you can glue two parts selecciona la Herramienta de Unir: con la herramienta de Unir puedes unir dos regiones. select Score Tool: selecciona la Herramienta de Partitura: select Quantize Tool: insert display quantize event selecciona la Herramianta de Cuantización: inserta un evento de cuantización de visualización select Drawing Tool selecciona la Herramienta de Dibujo select Muting Tool: click on part to mute/unmute selecciona la Herramienta de Silenciar: haz click en una región para silenciar/des-silenciar Manipulate automation Manipular automatización Cursor tool Herramientas de cursor pointer puntero pencil lápiz eraser Borrador cutter cortador score partitura quantize cuantizador draw dibujar mute parts silenciar regiones edit automation Editar automatización cursor Cursor MusEGui::Appearance Main application font, and default font for any controls not defined here. Fuente principal de la aplicación y la fuente por defecto para los controles no estan definidas. Time scale upper, and time signature. Controller graph and S/X buttons. Main application font, and default font for any controls not defined here. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Arranger part name overlay. Time scale lower. Tempo scale, and markers. Mixer labels. Auto-font-sizing up to chosen font size. Word-breaking but only with spaces. Maximum mixer label auto-font-sizing font size. Tamaño máximo de la fuente para etiqueta del mezclador. Global opacity (opposite of transparency). Opacidad global Standard Estandar Custom Personalizado Style was changed. The program must be restarted for changes to take place. Restart now? Revert changes Revert all... Do you really want to reset all colors? &Cancel &Cancelar MusE: load image MusE: carga imagen No current color item Select Color: %1 Select style sheet Seleccionar hoja de estilo Qt style sheets (*.qss) Hoja de estilo QT (*.qss) MusEGui::Arranger Enable Recording Habilita captura Mute/Off Indicator Indicador de Desconexion/Apagado Solo Indicator Indicador de solo Track Type Tipo de pista Track Name Nombre de pista Midi output channel number or audio channels Numero de canal de salida MIDI o canales de audio Midi output port or synth midi port Puerto de salida MIDI o puerto de sintetizador Time Lock Bloqueo de tiempo Automation parameter selection Seleccion de parametros de automatizacion Notation clef Clave de la notación Enable recording. Click to toggle. Habilitar captura: Clic en el botón. Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. On/Off is not! Indicador de silenciado: click para silenciar Clic con el boton derecho del ratón para Apagar/Encender Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed, indicated by a dark square. Indicador de Solo, clic en el botón. Pistas "fantasmas" en solo, seran indicadas por un cuadrado negro. Track type. Right-click to change midi and drum track types. Tipo de pista: Clic derecho alterna entre tipo MIDI/Percusión Track name. Double-click to edit. Right-click for more options. Nombre de pista: Doble clic para editar Click derecho para mas opciones. Midi/drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Pista MIDI/Percusión: Numero de canal de salida Pista de audio: Canales. Clic con el botón del medio o el derecho para cambiar. Midi/drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Pista de MIDI/Percusión: Puerto de salida. Pista de sintetizador: Asigna el puerto MIDI Clic de ratón para cambiar Clic derecho para ver la interfaz Time lock Bloqueo de tiempo Notation clef. Select this tracks notation clef. Clave de la notación. Seleccione esta clave de la notación de las pistas. Track index. Click to select track, CTRL+LMB to add/remove, SHIFT+LMB for range select, dblclick to select all of the same type. Enable/Disable input monitor. Passes input through to output (see also Settings: Automatically monitor on record arm). Enable/Disable recording. Mute indicator. Left-click to mute track (for quick action), right-click to toggle track on/off (for more permanent action). Track type. Click to switch between midi and drum track types. Track name. Double-click to edit. Right-click for context menu. Midi/Drum: Output channel number. Audio: Channels. Mid/right-click to change. Midi/Drum: Output port. Synth: Assigned midi port. Left-click to change, right-click to show synth GUI. Notation clef. Click to select this tracks notation clef. Arranger Arreglador Cursor Cursor Cursor position Bars Compases Off Apagado Bar Compás Track index Enable input monitor Enable recording Mute/Off indicator Solo indicator Track type Track name Enable input monitor. Click to toggle. Passes input through to output for monitoring. See also Settings: Automatically Monitor On Record Arm. Enable recording. Click to toggle. See also Settings: Automatically Monitor On Record Arm. Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed. Midi/Drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi/Drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Solo indicator. Connected tracks are also 'phantom' soloed. Snap Snap Len Len song length - bars Duración de compás Type Tipo NO No GM GM GS GS XG XG midi song type tipo de canción midi Pitch Tono midi pitch tono midi global midi pitch shift desplazamiento de tono midi Tempo Tempo midi tempo tempo midi Song length - bars Midi pitch Global midi pitch shift Midi tempo N N TrackInfo Información de pista R Record R M Mute M S Solo S C Class (track type) C Track Pista Port Puerto Ch Channel T Time lock T Automation Automatizacion Clef Clave MusEGui::ArrangerColumns Control7 Control7 Control14 Control14 RPN RPN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Program Programa Aftertouch AfterTouch MusEGui::ArrangerView MusE: Arranger Ordenar Arranger Arreglador D&elete C&ut C&ortar &Copy &Copiar Copy in range Copiar interior del rango &Paste &Pegar Paste (show dialog) Pegar (ver dialogo) Paste c&lone Pegar c&lon Paste clone (show dialog) Pergar clon (ver dialogo) &Insert Empty Measure &Insertar compás vacío Delete Selected Tracks Borrar pistas seleccionadas Duplicate Selected Tracks Shrink selected parts Encojer regiones seleccionadas Expand selected parts Expander regiones seleccionadas Clean selected parts Limpiar regiones seleccionadas Copy in Range Paste C&lone Paste to Selected &Track Paste Clone to Selected Trac&k Paste (Show Dialo&g)... Shrink Selected Parts Expand Selected Parts Purge Hidden Events from Selected Parts Add Track Agregar pista Insert Track Insertar pista Select Seleccionar Select &All Seleccion&ar todo &Deselect All &Deseleccionar todo Invert &Selection Invertir &selección &Inside Loop &Interior del bucle &Outside Loop &Exterior del bucle All &Parts on Track Todas las regiones de la &pista Score Editor de partitura All Tracks in One Staff One Staff per Track New Score Window... Pianoroll... Drums... Event List... Wave... Open in New Window Always open the part(s) in a new editor window Open Synth Plugin GUI... Midi &Transform... Global Cut - Selected Tracks Global Insert - Selected Tracks Global Split - Selected Tracks Change Note &Length Change Note &Velocity &Scripts Scripts for midi processing. Applied to selected midi parts (or else tracks). User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. &Display Display menu: View-specific display options. Toggle &Mixer Strip Configure &Custom Columns... New... Nothing to edit Nada para editar New score window Nueva ventana de partitura Pianoroll Editor de matriz Drums Editor de percusión List Editor de lista Wave Editor de audio Mastertrack Pista maestra. Graphic Editor gráfico Midi &Transform &Transformación MIDI Global Cut Corte global Global Insert Inserción global Global Split División global &Edit &Editar Fu&nctions &Structure E&structura Functions Funciones &Quantize Notes Cuantizado de notas Change note &length Cambiar duración de notas Change note &velocity Cambiar velocidad de notas Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transponer Erase Events (Not Parts) Borrar eventos (no regiones) Move Events (Not Parts) Mover eventos (no regiones) Set Fixed Note Length Igualar duración de notas Delete Overlapping Notes Borrar notas superpuestas Legato Legato Window &Config Comportamiento de ventana Remove track(s) Are you sure you want to remove this track(s)? New Nuevo MusEGui::AudioComponentRack Aux send level (dB) Vol Pan Mute Volume/gain Panorama/Balance Gain Ganancia Calibration gain MusEGui::AudioMixerApp &Create &Crear &View &Ver Strips Routing Ruteado Show Midi Tracks Ver pistas MIDI Show Drum Tracks Ver pistas de percision Show Wave Tracks Ver pistas de audio Show Inputs Ver entradas de audio Show Outputs Ver salidas de audio Show Groups Ver grupos de audio Show Auxs Ver envios auxiliares Show Synthesizers Ver sintetizadores Traditional order Arranger order User order Show all hidden strips Unhide strip: (no hidden strips) MusEGui::AudioStrip panorama panorama aux send level volumen de envio auxiliar Pan Paneo 1/2 channel Canales 1/2 Pre Pre pre fader - post fader pre fader - port fader dB dB record grabar mute silencio record downmix downmix de captura solo mode Modo solo off apagado input routing Ruteado de entrada output routing ruteado de salida Pre Fader Listening (PFL) Record downmix Record arm Mute or proxy mute Solo or proxy solo Track off Input monitor Input routing. Press F1 for help. Output routing. Press F1 for help. Volume/Gain Pass input through to output Off Apagado Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Auto off Read Lectura Touch Tocar Write Escritura Automation type Automation type: Off, Read, Touch or Write. Press F1 for help. L meter peak/clip R meter peak/clip automation type Tipo de automatizacion MusEGui::BigTime format display Formato de vista bar compaa beat Pulso tick Tic minute minuto second segundo frame frame subframe sub-frame Format display Bar Beat Tick Tic Minute Second Frame Subframe MusE: Bigtime MusE: Big Time MusEGui::Canvas Tools Herramientas MusEGui::ClipListEdit MusE: Clip List Editor MusE: Editor de lista de archivos de audio Window &Config Comportamiento de ventana MusEGui::ComboQuant Off Apagado MusEGui::CompactKnob off MusEGui::CompactPatchEdit Patch name MusEGui::CompactSlider off MusEGui::CpuStatusBar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) CPU load averaged over each GUI update period. DSP load read from JACK. Number of xruns. Double-click to reset. MusEGui::CpuToolbar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) MusEGui::CtrlCanvas Control canvas: Use Pencil tool to edit events and Draw tool to adjust them gradually. Hold Ctrl to affect only existing events. Merge options Erase target Erase target events between source events Erase target WYSIWYG Include last source item width when erasing Erase target inclusive Include entire source range when erasing Merge actions Merge Merge the dragged items Merge a copy Merge a copy of the dragged items Cancel drag Cancel dragging the items MusEGui::CtrlPanel S Select controller S Select controller Remove panel Manual adjust (Ctrl-double-click on/off) All/Per-note velocity mode select controller Seleccionar controlador X Remove panel (destroy button) X remove panel Quitar el panel manual adjust Ajuste manual off apagado Velocity Velocidad add new ... Agregar nuevo... Instrument-defined Definición de instrumento Add ... Agregar... Others Otros Edit instrument ... Editar instrumento... Common Controls Controles comunes MusEGui::DList Hide this instrument This turns a blue eye into a crossed eye Show this instrument This turns a crossed eye into a blue eye Set field Sets a field Set row Sets a row Set column Sets a whole column to the field Set list Sets the whole list Reset field Resets a field in a row to default patch or instrument value Reset row Resets a row to the instrument values Reset column Resets a whole column to the instrument values Reset list Resets the whole list to the instrument values Reset track's drum list Resets all lists on all patches to the instrument values Promote field to default patch Promotes a field in a row to the default patch Promote row to default patch Promotes a row to the default patch Promote column to default patch Promotes a column to the default patch Promote list to default patch Promotes the whole list to the default patch Reset instrument field Reset instrument row Reset instrument list Set instrument field Set instrument row Set instrument list Drum list: Edit instrument-specific settings. MusEGui::DrumCanvas Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help. Moving items failed The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. Creating event failed Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Recording event failed Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Internal error Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. Drum map Mapa de percusiòn Reset the track's drum map with instrument defaults? Note: Velocity: Start (bar): Start (time): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete LMB: Delete Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete. MusEGui::DrumEdit mute instrument silenciar instrumento sound name nombre del sonido volume percent Porcentaje de volumen quantisation cuantización this input note triggers the sound ensta nota de entrada dispara el sonido note length Duración de nota this is the note which is played Esta es la nota que se toca output channel (hold ctl to affect all rows) canal de salida (mantener presionado Ctrl a afectar a todas las pistas) output port (hold ctl to affect all rows) Puerto de salida (Mantener presionado CTRL a afecta todas las pistas) shift + control key: draw velocity level 1 Shift + CTRL: Dibuja la velocidad 1 control key: draw velocity level 2 CTRL: Dibuja la velocidad 2 shift key: draw velocity level 3 Shift: Dibuja la velocidad 3 draw velocity level 4 Dibuja la velocidad 4 output channel (ctl: affect all rows) Canal de salida. (CTRL afecta todas las pistas) output port (ctl: affect all rows) Puerto de salida (CTRL afecta todas las pistas) &File Archivo Load Map Cargar Mapa Save Map Guarda Mapa Reset GM Map Resetear mapa GM &Edit &Editar Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Note which triggers the sound Note which is played Override track output channel (hold CTRL to affect all rows). Override track output port (hold CTRL to affect all rows). CTRL + META keys: Draw velocity level 1 META key: Draw velocity level 2 META + ALT keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) Cut Cortar Copy Copiar Copy events in range Copiar eventos del rango Paste Pegar Paste to current part Paste (with Dialog) Pegar (con dialogo) Delete Events Borrar eventos &Select &Seleccionar Select All Seleccionar todo Select None No seleccionar nada Invert Invertir Inside Loop Interior del bucle Outside Loop Exterior del bucle Previous Part Región anterior Next Part Región siguiente Event List... Fu&nctions Funciones &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Cursor step Cursor tools Add controller view Re-order list Reordenar la lista Set Fixed Length Fijar duración Modify Velocity Modificar la velocidad. Crescendo/Decrescendo Crescendo/Decrescendo Quantize Cuantizar Erase Event Borrar evento Move Notes Mover notas Delete Overlaps Eliminar superpuestos &Plugins Efectos Drum tools Herramientas de percusión Load Drummap Cargar mapa de percusión &Display Display menu: View-specific display options. Group Don't group Group by channel Group maximally Show/Hide Also show hidden instruments Show all instruments Hide all instruments Only show used instruments Only show instruments with non-empty name or used instruments Step record Midi input Play events Store Drummap Guardar mapa de percusión Step Record Grabación por pasos Midi Input Entrada MIDI Play Events Reproducir eventos cursor tools Herramientas de cursor Set step size for cursor edit Establecer el tamaño del paso para modificar el cursor ctrl control Add Controller View Agregar vista de controlador H hide Matíz M mute M Sound Sonido Vol Volumen QNT QNT E-Note Nota-E Len Duraciòn A-Note Nota-A Ch Canal Port Puerto LV1 VL1 LV2 VL2 LV3 VL3 LV4 VL4 Muse: Load Drum Map MusE: Cargar mapa de percusión MusE: Store Drum Map MusE: Guardar mapa de percusión Drum map Mapa de percusiòn Reset the drum map with GM defaults? ¿Resetear en mapa de percusión con el banco GM? MusEGui::EditCAfterDialog MusE: Enter Channel Aftertouch MusE: Introduzca el AfterTouch del canal Time Position Posición de tiempo Pressure Presión MusEGui::EditEventDialog Ok Aceptar Cancel Cancelar MusEGui::EditInstrument Use note offs No note offs Convert to 0-vel note ons Control7 Control7 Control14 Control14 RPN RPN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Program Programa PolyAftertouch Aftertouch AfterTouch H hide Matíz M mute M Name Nombre Vol Quant E-Note Nota-E Len A-Note Nota-A Ch Canal Port Puerto LV1 VL1 LV2 VL2 LV3 VL3 LV4 VL4 Tick Tic Data Datos Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) MusE: Create file failed MusE: Fallo la creacion del archivo MusE: Write File failed MusE: La captura del archivo ha fallado MusE: Save Instrument Definition MusE: Guardar definicion de instrumento Instrument Definition (*.idf) Definicion de instrumento (*.idf) MusE: Save instrument as MusE: Guardar instrumento como Enter a new unique instrument name: Ingrese un nuevo nombre unico de instrumento The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? MusE: Bad instrument name MusE: Nombre de instrumento invalido Please choose a unique instrument name. (The name might be used by a hidden instrument.) Selecciones un nombre único al instrumento (El nombre puede estar siendo utilizado por un instrumento oculto.) MusE: Bad patch name MusE: Nombre de programa invalido Please choose a unique patch name Por favor elija nombre unico de programa MusE: Bad patchgroup name MusE: Nombre de grupo erroneo. Please choose a unique patchgroup name Seleccione un nombre único al grupo MusE: Bad controller name MusE: Nombre del controlador no válido. Please choose a unique controller name El nombre del controlador ya existe New controller: Error Error! All control numbers are taken up! Clean up the instrument! MusE: Cannot add common controller MusE: No se puede agregar un controlador comun. A controller named '%1' already exists. El nombre de controlador '%1' ya existe. A controller number %1 already exists. El número de controlador %1 ya existe. MusE MusE The current Instrument contains unsaved data Save Current Instrument? El instrumento actual contiene datos sin guardar. ¿Desea guardarlos ahora? &Save &Guardar &Nosave &No Guardar &Abort &Abortar MusEGui::EditMetaDialog MusE: Enter Meta Event MusE: Introduzca evento Meta Time Position Posición de tiempo Meta Type Tipo de Meta Enter Hex Ingrese Hex MusEGui::EditPAfterDialog MusE: Enter Poly Aftertouch Muse: Escribe el aftertouch polifónico Time Position Posición de tiempo Pitch Tono Pressure Presión MusEGui::EditToolBar select Pointer Tool: with the pointer tool you can: select parts move parts copy parts Selecciona la herramienta Puntero: con la herramienta de puntero puede: seleccionar regiones mover regiones copiar regiones. select Pencil Tool: with the pencil tool you can: create new parts modify length of parts Selecciona la Herramienta de Lápiz: con la herramienta de Lápiz puede: crear nuevas regiones modificar la duración de las regiones. select Delete Tool: with the delete tool you can delete parts selecciona la Herramienta de Borrar: con la herramienta de Borrar puedes borrar regiones. select Cut Tool: with the cut tool you can split a part selecciona la Herramienta de Corte: con la herramienta de Corte puede dividir una región select Glue Tool: with the glue tool you can glue two parts selecciona la Herramienta de Unir: con la herramienta de Unir puedes unir dos regiones. select Score Tool: selecciona la Herramienta de Partitura: select Quantize Tool: insert display quantize event selecciona la Herramianta de Cuantización: inserta un evento de cuantización de visualización select Drawing Tool selecciona la Herramienta de Dibujo select Muting Tool: click on part to mute/unmute selecciona la Herramienta de Silenciar: haz click en una región para silenciar/des-silenciar Select Pointer tool: With the pointer tool you can: select parts move parts copy parts Select Pencil tool: With the pencil tool you can: create new parts modify length of parts Select Delete tool: With the delete tool you can delete parts Select Cut tool: With the cut tool you can split a part Select Glue tool: With the glue tool you can glue two parts Select Score tool: Select Quantize tool: Insert display quantize event Select Drawing tool Select Muting tool: Click on part to mute/unmute Manipulate automation Manipular automatización Select Cursor (tracker mode) tool: With the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Select Range tool Select Panning tool Select Zoom tool Select Stretch tool Select Sample rate tool Pointer Pencil Eraser Cutter Glue Range Rango Pan Zoom Score Editor de partitura Quantize Cuantizar Draw Mute parts Edit automation Cursor Cursor Stretch Sample rate Frecuencia de muestreo Cursor tool Herramientas de cursor pointer puntero pencil lápiz eraser Borrador cutter cortador score partitura glue Pegador quantize cuantizador draw dibujar mute parts silenciar regiones edit automation Editar automatización cursor Cursor Edit Tools Editar herramientas MusEGui::EffectRack effect rack rack de efectos new nuevo change cambiar move up subir move down bajar remove quitar bypass saltar show gui mostrar gui show native gui Ver interfaz nativa save preset Guardar preseteo Effect rack: Double-click a slot to insert/edit effect. RMB to open context menu. Press F1 for help. Effect rack Double-click a slot to insert FX New Nuevo Change Move up Move down Remove Eliminar Bypass Show gui Show native gui Save preset Guardar preseteo Presets Plantillas MusE: Save Preset MusE: Guardar preseteo Replace effect Reemplazar efecto Do you really want to replace the effect %1? ¿Desea reemplazar el efecto %1? MusEGui::GlobalSettingsConfig MusE: Choose start template or song MusEGui::Header Track Info Columns Colunna de informaciòn de pistas Custom column MusEGui::LCDPatchEdit High bank: Low bank: Program (Ctrl-double-click on/off) off High bank Low bank Program Programa MusEGui::LMaster MusE: Mastertrack MusE: pista maestra &Edit &Editar Insert Tempo Insertar tiempo Insert Signature Insertar signatura Insert Key Insertar clave Edit Positon Editar posición Edit Value Editar valor Delete Event Borrar evento Window &Config &Configuraciòn de la ventana Edit tools Editar herramientas Tempo Timpo Timesig compás Key Clave new tempo nuevo tempo new signature nuevo compás new key Nueva clave Meter Metrónomo Time Tiempo Type Tipo Value Valor Signature Compás Position Posición Delete Borrar Insert tempo change Insert time signature change Insert key change Edit position Edit value Delete event Reposition of the initial tempo and signature events is not allowed Reposición del tempo inicial y la firma de eventos, no está permitido MusE: List Editor MusE: Editor de lista Input error, conversion not OK or value out of range Error de entrada,la conversión no escorrecta o valor está fuera de rango. Reposition of tempo and signature events to start position is not allowed! Reposición de los eventos de tempo y la firma a la posición inicial no está permitida! MusEGui::ListEdit insert Note insertar Nota insert SysEx insertar sistema exclusivo insert Ctrl insertar controlador insert Meta insertar Meta insert Channel Aftertouch insertar AfterTouch de canal insert Poly Aftertouch insertar AfterTouch Polifónico &Edit &Editar Cut Cortar Copy Copiar Paste Pegar Delete Events Borrar eventos Window &Config &Configuraciòn de la ventana Insert Note Insertar nota Insert SysEx Insertar sistema exclusivo Insert Ctrl Insertar controlador Insert Meta Insertar Meta Note Nota SysEx Ctrl Meta Tick+ Tick- Delete Borrar Insert tools Insertar herramientas Tick Tic Bar Compàz Type Tipo Ch Val A Val A Val B Val B Val C Val C Len Comment Comentario Insert note event Insert system exclusive event Insert controller event Insert meta event Increase tick value Decrease tick value Delete event MusE: List Editor MusE: Editor de lista MusEGui::MPConfig Default input connections Conexión de entrada predeterminada Are you sure you want to apply to all existing midi tracks now? ¿Seguro que desea aplicar a todas las pistas MIDI existentes? Default output connections Conexión de salida predeterminada Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? El sjuste se aplicará a las nuevas pistas MIDI. ¿Usted desea aplicar a todas las pistas MIDI existentes? MusE: bad device name MusE: Nombre del dispositivo incorrecto please choose a unique device name El nombre del dispositivo ya existe in Entrada out Salida Show first aliases Mostrar primero los álias Show second aliases Mostrsr después los álias Toggle all Cambiar todos Change all tracks now Cambiar todas las pistas ahora Remove Eliminar Create Jack device Crear conexión Jack Port Number Número de puerto Enable gui Habilitar visualización Enable reading Habilitar lectura Enable writing Habilitar escritura Port instrument Puerto de instrumento Midi device name. Click to edit (Jack) Nombre del dispositivo MIDI, Clic para editar (Jack) Connections from Jack Midi outputs Conexiones de salidas MIDI de Jack Connections to Jack Midi inputs Conexiones de entrada MIDI de Jack Auto-connect these channels to new midi tracks Conectar automaticamente a estos canales las nuevas pistas midi Auto-connect new midi tracks to these channels Conectar nuevas pistas MIDI a estos canales Auto-connect new midi tracks to this channel Autoconectar las nuevas pistas MIDI a este canal Device state Estado del dispositivo Enable gui for device Habilitar visualización para el dispositivo Enable reading from device Habilitar la lectura desde el dispositivo Enable writing to device Habilitar la escritura en el dispositivo Name of the midi device associated with this port number. Click to edit Jack midi name. Nombre del dispositivo MIDI asociado con este puerto. Haga clic para cambiar el nombre del dispositivo MIDI de Jack. Instrument connected to port Instrumento conectado al puerto Connections from Jack Midi output ports Conexiones de los puertos de salida MIDI de Jack Connections to Jack Midi input ports Conexiones de los puertos de entrada MIDI de Jack Auto-connect these channels, on this port, to new midi tracks. Conexión automática de estos canales, en este puerto, a las nuevas pistas MIDI. Connect new midi tracks to these channels, on this port. Conecte las nuevas pistas MIDI a estos canales, en este puerto. Connect new midi tracks to this channel, on this port. Conecte las nuevas pistas MIDI a este canal, en este puerto. State: result of opening the device Estado: resultado de abrir el puerto Port Puerto GUI GUI I O Instrument Instrumento Device Name Nombre de dispositivo Please choose a unique device name Port number Midi device name Midi device type Connections from Jack Midi Connections to Jack Midi Result of opening the device: OK: Assigned to a port and in use Closed: Unassigned to a port, or closed R/W Error: Unable to open for read or write Unavailable: USB midi unplugged, or external application not running, or synth plugin not installed etc. (Jack Midi devices have 'unavailable ports' in the routes columns.) Unavailable devices or ports can be purged with 'Remove' or with the advanced router. Enable Graphical User Interface for device Connections from Jack Midi ports Connections to Jack Midi ports In routes Rutas de entrada Out routes Rutas de salida Def in ch Definir canales de entrada Def out ch Definir canales de salida State Estado Type Tipo In Out <unknown> <desconocido> <none> <ningúno> MusEGui::MTScale bar scale escala de compases Bar scale Bar scale: Use mouse buttons to set position and range markers. Hold Shift to set (LMB) or delete (RMB) custom markers. MusEGui::MTScaleFlo bar scale escala de compases Bar scale MusEGui::MarkerView MusE: Marker MusE: Marcador add marker Agregar marcador delete marker borrar marcador &Edit &Editar Window &Config &Comportamiento de ventana edit tools editar herramientas Add marker Delete marker Edit tools Editar herramientas Bar:Beat:Tick Compás:Golpe:Tic Hr:Mn:Sc:Fr:Sf Hr:Mn:Sg:Fr:Sf Lock Bloquear Description Descripción Edit tick Edit SMPTE timecode Toggle timecode editing Edit marker description <Description> Text Texto Marker Properties Propiedades del marcador MusEGui::Master Tools Herramientas MusEGui::MasterEdit MusE: Mastertrack MusE: Pista maestra Window &Config &Configuraciones de la ventana Enable master Habilitar maestro Enable Habilitado Enable usage of master track Habilitar el uso de la pista maestra Info Info Cursor Cursor time at cursor position tiempo en la posición del cursor tempo at cursor position tempo en la posición del cursor Mastertrack Pista maestra. &Display Display menu: View-specific display options. Time at cursor position Tempo at cursor position Off Apagado Bar Compás Snap Snap CurPos Posición del cursor tempo at current position tempo en la posición actual time signature at current position compás en la posición actual MusEGui::MetronomeConfig Reset accents: Resets all accents to the defaults (first in list) of the current preset category (Factory or User). Proceed? MusEGui::MidiAudioControl Control7 Control7 Control14 Control14 RPN RPN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Program Programa Aftertouch AfterTouch MusEGui::MidiComponentRack Pro Var Var Rev Rev Cho Cho Pan Program Programa VariationSend Variación del envío ReverbSend Envio de reverberancia ChorusSend Envio de corolizador Pan/Balance Panorama/Balance Controller Controlador (Ctrl-double-click on/off) <unknown> <desconocido> Instrument Instrumento Transpose Transponer Transpose notes up or down Delay Offset playback of notes before or after actual note Length Duración Change note length in percent of actual length Velocity <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> Compress Compress the notes velocity range, in percent of actual velocity <Patch> MusEGui::MidiInputTransformDialog New Nuevo MusEGui::MidiStrip B Palette B Azul off apagado Record arm Mute or proxy mute Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Track off Input monitor Intput routing. Press F1 for help. Output routing. Press F1 for help. Inst Instrumento Prop Instruments and controllers Properties Propiedades Volume/Gain (Ctrl-double-click on/off) Pass input through to output Input monitor: Pass input through to output. VariationSend Variación del envío Var Var ReverbSend Envio de reverberancia Rev Rev ChorusSend Envio de corolizador Cho Cho dB dB Pan/Balance Panorama/Balance Pan Panorama record grabar mute silencio solo mode modo solo input routing Ruteado de entrada output routing ruteado de entrada MusEGui::MidiSyncConfig Port Number Número de puerto Name of the midi device associated with this port number Nombre del dispositivo MIDI asociado con este número de puerto Sync to this device. Click to select. Midi clock input detected Midi clock input detectado Midi tick input detected Midi tick input detectado Midi real time input detected Midi real time input detectado MMC input detected MMC input detectado MTC input detected MTC input detectado Detected SMPTE format Formato SMPTE detectado Receive id number. 127 = Global. Double click to edit. Recibido id number, 127 = global, Doble clic para editar Accept midi clock input MIDI clock input aceptado Accept midi real time input MIDI real time input aceptado Accept MMC input MMC input aceptado Accept MTC input MTC input aceptado Receive start rewinds before playing Iniciando rebobinado antes de reproducir Transmit id number. 127 = Global. Double click to edit. Transmitiendo id number 127 = global, Doble clic para editar Send midi clock output Enviar MIDI clock output Send midi realtime output Enviar MIDI realtime output Send MMC output Enviar MMC output Send MTC output Enviar MTC output Type Tipo Midi clock input detected. Current port actually used is red. Click to force a port to be used. Entrada de MIDI clock input detectado. El puerto utilizado es de color rojo. Haga clic para obligar a un puerto a utilizarlo. Midi realtime input detected, including start/stop/continue, and song position. MIDI realtime input detectado, incluye Reproducir, Detener, Continuar y Posición en la canción. MMC input detected, including stop/play/deferred play, and locate. MMC input detectado, incluye Reproducir, Detener, Continuar y Posición en la canción. MTC input detected, including forward quarter-frame sync and full-frame locate. Current port actually used is red. Click to force a port to be current. De entrada MTC detectadas, así como avanzar de cuartos de la sincronía y localizar fotograma completo. El puerto actual utilizado es de color rojo. Haga clic para obligar a un puerto a actualizar. Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detects format of MTC quarter and full frame, and MMC locate. Formato SMPTE detectado: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detecta el formato de cuarto de MTC y de fotograma completo, y localiza MMC. Receive id number. 127 = global receive all, even if not global. Recibe número de identificación. 127 = Globales reciben de todo, incluso si no es global. Accept midi clock input. Only one input is used for clock. Auto-acquire: If two or more port realtime inputs are enabled, the first clock detected is used, until clock is lost, then another can take over. Best if each turns off its clock at stop, so MusE can re-acquire the clock from another port. Click on detect indicator to force another. Aceptar la entrada del reloj MIDI. Sólo una entrada se utiliza para el reloj. Auto-adquisición: Si dos o más entradas en tiempo real del puerto están habilitadas, el reloj detecta por primera vez se utiliza, hasta que el reloj se ha perdido, luego otro se haga cargo. Mejor si cada uno se apaga el reloj en la parada, por lo que Muse puede volver a adquirir el reloj de otro puerto. Haga clic en la detección de indicadores de forzar a otro. Accept midi realtime input, including start/stop/continue, and song position. Non-clock events (start,stop etc) are accepted by ALL enabled ports. This means you may have several master devices connected, and muse will accept input from them. Acepta la entrada de MIDI en tiempo real, incluyendo iniciar / detener / continuar, y posición de la canción. los eventos (start, stop, etc) son aceptado por todos los puertos habilitados. Esto significa que usted puede tener varios maestros con los dispositivos conectados, y MusE aceptará la voluntad de éstos. Accept MMC input, including stop/play/deferred play, and locate. Aceptar la entrada de MMC, incluida la parada / reproducción / reproducción diferida, y localización. Accept MTC input, including forward quarter-frame sync and full-frame locate. See 'rc' column for more help. Aceptar la entrada del MTC, incluso delante de cuartos de la sincronía y localizar de fotograma completo. Ver columna "rc" para obtener más ayuda. When start is received, rewind before playing. Note: It may be impossible to rewind fast enough to synchronize with the external device. Aceptar la entrada de MIDI en tiempo real, incluyendo recibir al inicio, rebobinar antes de reproducir. Nota: Puede ser imposible para un rebobinado rápido lo suficiente como para sincronizar con el dispositivo externo. Sync to this device. Click to select. MusE will synchronize to this device's clock or MTC quarter-frame, if 'slave to external sync' is on. MTC input detected, including forward quarter-frame sync and full-frame locate Accept midi clock input. Only one port can be used for clock sync. Accept MTC input, including forward quarter-frame sync and full-frame locate. Only one port can be used for sync. When start is received, rewind before playing. Note: In some cases, such as having many project audio tracks, it may be impossible to rewind fast enough to synchronize with the external device. Manually rewinding the device before playing is recommended. Transmit id number. 127 = global transmit to all. Transmitir id number. 127 = global transmitir a todos. Send midi clock output. If 'Slave to External Sync' is chosen, muse can re-transmit clock to any other chosen ports. Enviar la salida de reloj MIDI. al 'Esclavo de sincronización externa " que elija, MusE puede volver a transmitir el reloj a cualquier otro puerto elegido. Send midi realtime output, including start/stop/continue, and song position. If 'Slave to external sync' is chosen, muse can re-transmit midi realtime input messages to any other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. Enviar salida MIDI en tiempo real, incluyendo el arranque / parada / continuación, y de posición de canción. al 'Esclavo de sincronización externa "que se elija, MusE puede volver a transmitir mensajes MIDI de entrada en tiempo real a cualquier otros puertos elegidos. Esto significa que usted puede tener varios esclavos los dispositivos conectados, y MusE puede volver a enviar mensajes en tiempo real a cualquiera o todos ellos. Port Puerto Device Name Nombre de dispositivo s s c c k k r r m m t t type Tipo rid rid rc rc rr rr rm rm rt rt rw rw tid tid tc tc tr tr tm tm tt tt None Ningúno Tiny Small Large Large with pre-detect MusE MusE Settings have changed Apply sync settings? La configuración ha cambiado ¿Desea aplicar la nueva configuración de sincronización? &Apply &Aplicar &No Ca&ncelar &Abort &Abortar <none> <ninguno> MusEGui::MidiTrackInfo <unknown> <desconocido> MusEGui::MidiTransformerDialog New Nuevo MusEGui::MixdownFileDialog Wave Files (*.wav);;All Files (*) Archivos de onda (*.wav);;Todos los archivos(*) MusEGui::MusE Failed to start audio! Fallo al iniciar el audio Was not able to start audio, check if jack is running. No se puede iniciar el audio, verifique el estado de Jack Timeout waiting for audio to run. Check if jack is running. Sa agotó el tiempo de espera. Comprobar si Jack está en ejecución Und&o Deshacer Re&do &Rehacer undo last change to song deshace el ltimo cambio en la canción redo last undo rehace el ltimo deshacer Loop Bucle loop between left mark and right mark hace un bucle entre las marcas de izquierda y derecha Punchin Pinchar record starts at left mark la captura comienza en la marca izquierda Punchout Fin pinchado record stops at right mark la captura termina en la merca derecha Start Iniciar rewind to start position retrocede a la posición de inicio Rewind Retroceder rewind current position retrasa la posición actual Forward Avanzar move current position mueve la posición actual Stop Parar stop sequencer para el secuenciador Play Reproducir start sequencer play inicia el secuenciador Record Grabar to record press record and then play para grabar pulsa grabar y después reproducir Panic Pánico send note off to all midi channels envia un apagado de nota (note off) a todos los canales midi &New &Nueva sesión Create New Song Crea una nueva canción &Open &Abrir sesión Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. Pulsa este botón para abrir una <em>nueva canción</em>Puedes usar también el comando <b>Abrir</b> del me de archivo. Open &Recent Abrir &Recientes &Save &Guardar Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. Pulsa este botón para guardar la canción que está editando. Se te preguntará por un nombre de archivo. Puedes seleccionar también el comando Guardar del men de Archivo. Save &As Guardar c&omo Import Midifile Importar archivo MIDI Export Midifile Exportar archivo MIDI Import Part Importar región. Import Audio File Importar archivo de audio Find unused wave files Buscar archivos de audio sin uso &Quit &Salir Song Info Comentarios de la sesión Transport Panel Panel de transporte Bigtime Window Ventana de reloj Mixer A Mezcladora A Mixer B Mezcladora B Cliplist Lista de archivos de audio Marker View Editor de marcadores Arranger View Ordenar vistas Fullscreen Pantalla completa &Plugins Efectos Edit Instrument Editar instrumento Input Plugins Efectos de entrada Transpose Transponer Midi Input Transform Transformación de entrada MIDI Midi Input Filter Filtrado de entrada MIDI Midi Remote Control Control remoto MIDI Rhythm Generator Generador de ritmo Reset Instr. Resetear instrumento Init Instr. Inicializar instruemnto Local Off Apagado local Bounce to Track Rebotar a pista Bounce to File Rebotar a Archivo Restart Audio Reiniciar audio Mixer Automation Mezcla de automatización Take Snapshot Obtener instantanea Clear Automation Data Limpiar datos de automatización Cascade Cascada Tile Titulo In rows Filas In columns Columnas Global Settings Configuración global Configure Shortcuts Configurar atajos Follow Song Seguir canción Don't Follow Song No seguir canción Follow Page Seguir la página Follow Continuous Seguir contínuamente Set left/right marker for bounce range MusE: Song: Metronome Metrónomo Midi Sync Sincronización MIDI Midi File Import/Export Importar/Exportar archivo MIDI Appearance Settings Opciones de apariencia Midi Ports / Soft Synth Puertos MIDI / Sintetizadores &Manual &Manual &MusE Homepage Página del proyecto &MusE &Report Bug... &Reportar un fallo &About MusE &Acerca de MusE Tempo Signature Compás File Buttons Columna de botones Undo/Redo Deshacer/Rehacer Transport Transporte Cpu load &File Sesión &View &Ver &Midi &MIDI &Audio &Audio A&utomation A&utomatización &Windows Ventanas Se&ttings Configuraciones &Help A&yuda Cannot read template No se puede leer la plantilla File open error Error al abrir el archivo File read error Error al leer el archivo Unknown File Format: %1 Formato de archivo desconocido: %1 MusE: Song: %1 Sesión de MusE: %1 MusE: load project MusE: Carga sesión Timeout waiting for audio to run. Check if jack is running or try another driver. Was not able to start audio, check if jack is running or try another driver. Undo last change to song Redo last undo Loop between left mark and right mark Punch in Record starts at left mark Recording starts at left mark Punch out Record stops at right mark Recording stops at right mark Rewind to start position Rewind current position Move current position Stop sequencer Restart rec Start sequencer play To record press record and then play Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help. Turn on/off metronome Metronome on/off. Press F1 for help. Create new song New from &Template... Create new song from template &Open... &Abrir... Open song from file Click this button to open an existing song. Save current song Click this button to save the song you are editing. You will be prompted for a file name. Save &As... Guardar &Como... &Close Import Midifile... Export Midifile... Import Part... Import Audio File... Find Unused Wave Files... Song Info... Mastertrack Pista maestra. Edit Instrument... Transpose... Midi Input Transform... Midi Input Filter... Midi Remote Control... Reset Instrument Init Instrument Send 'local-off' command to all midi channels. Bounce to File... Global Settings... Appearance... Rewind on Stop Metronome... Midi Sync... Midi File Import/Export... &MusE Homepage... &Did You Know? &About MusE... Snooper (developer tool)... About &Qt... The project has no project sample rate (added 2011). Please enter a rate. The current system rate (%1Hz) is suggested, and cancelling uses it: The project has audio waves, but no project sample rate (added 2011). Please enter a rate. The most common wave rate found is suggested, the project was probably made with it. Cancelling uses the current system rate (%1Hz): Project sample rate MusE: load template MusE: cargar plantilla MusE: Write File failed MusE: La captura del archivo ha fallado The current Project contains unsaved data Save Current Project? La sesión actual contiene datos sin guardar Guardar la sesión actual? S&kip Ignorar Load configuration colors &Cancel &Cancelar Color settings will immediately be replaced with any found in the file. Are you sure you want to proceed? &Ok Save configuration colors MusE: Save As MusE: Guardar como Nothing to edit Nada para editar MusE: Bounce to Track MusE: Rebotar a la pista No wave tracks found No existe el archivo de audio No audio output tracks found No hay salidas de audio Select one audio output track, and one target wave track Seleccione una salida de salida de audio, y una pista de audio. Select one target wave track Seleccione una pista de audio de destino Select one target wave track, and one audio output track Selecciones una pista de audio, y una salida de audio. MusE: Bounce to File Rebotar a archivo Select one audio output track Seleccionar salida de audio MusE: Bounce MusE: Rebotar set left/right marker for bounce range Fije marcadores derecho e izquierdo para el rango de rebote. The current Project contains unsaved data Load overwrites current Project: Save Current Project? El proyecto actual contine datos no guardados Cargar sobreescribe el proyecto actual: Guardar el proyecto actual? &Abort &Abortar This will clear all automation data on all audio tracks! Proceed? This takes an automation snapshot of all controllers on all audio tracks, at the current position. Proceed? MusE: Warning The song uses multiple ports but export format 0 (single track) is set. The first track's port will be used. Playback will likely be wrong unless the channels used in one port are different from all other ports. Canceling and setting a different export format would be better. Continue? MusE: Export Midi MusE: Exportar MIDI no help found at: no se ha encontrado ayuda en: MusE: Open Help MusE: Abre ayuda Unable to launch help No es posible enlazar la ayuda. For some reason MusE has to launch the default browser on your machine. Por alguna razón, MusE tiene que lanzar el navegador por defecto MusE: Import Midi MusE: Importar MIDI Add midi file to current project? ¿Agregar archivo MIDI a la sesión? &Add to Project Agregar a la sesión &Replace &Reemplazar reading midifile leyendo archivo midi Reading midifile failed: falló: Import part is only valid for midi and wave tracks! Importar regiones es solo para pistas MIDI o de audio MusE: load part MusE: Cargar región. No track selected for import No hay pistas seleccionadas para la importaciòn %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. %n Región(es) fuera de %1 no puede ser importada. Probablemente la pista seleccionada es del tipo incorrecto. %n part(s) could not be imported. Likely the selected track is the wrong type. %n Región(es) no puede ser importada. Probablemente la pista seleccionada es del tipo incorrecto. to import an audio file you have first to selecta wave track Para importar un archivo de audio seleccione la pista de audio To import an audio file you have first to select a wave track Import Wavefile Importar archivo de audio Wave import error There are too many wave files of the same base name as imported wave file Can not continue. Can't create new wav file in project folder! Failed to initialize sample rate converter! Cancel Cancelar Show Docks Toggle display of currently visible dock windows. Display MusE main window in full screen mode. Mastertrack Graphic... Send 'note-off' command to all midi channels. Send initialization messages as found in instrument definition. Take Automation Snapshot Keyboard Shortcuts... Midi Ports / Soft Synths... &Manual (wiki)... File buttons Timeline Recording Sync Sinconización Position Posición The current project contains unsaved data. Save current project? No valid parts selected The current project contains unsaved data. Load overwrites current project. Save current project? This wave file has a samplerate of %1 Hz, as opposed to current setting %2 Hz. A live, real-time samplerate converter can be used on this file. Or, a copy of the file can be resampled now from %1 Hz to %2 Hz. Do you want to use a converter or resample the file now? Use live converter Resample now Resampling wave file "%1" from %2 to %3 Hz... Output has clipped Resampling again and normalizing wave file "%1" Try %2 of %3... This wave file has a samplerate of %1, as opposed to current setting %2. Do you still want to import it? Este archivo tiene una frecuencia de muestreo de %1, El valor actual de la sesión es %2. ¿Desea importarlo de todos modos? &Yes Aceptar &No Ca&ncelar File version is %1.%2 Current version is %3.%4 Conversions may be applied if file is saved! Opening file Do not warn again Unable to launch browser Error launching default browser MusEGui::NoteInfo Note Info Información de nota Delta/Absolute mode Start Iniciar Len Duración Pitch Tono Velo on Velo off Velo On Velocidad On Velo Off Velocidad Off MusEGui::PartCanvas Cannot copy/move/clone to different Track-Type No se puede copiar/mover/clonar a un tipo diferente de pista C&ut C&ortar &Copy &Copiar s&elect S&eleccionar clones Clonar rename renombrar color color delete borrar split partir glue Unir de-clone des-clonar save part to disk Guardar región en el disco. wave edit editar onda file info Información de archivo Part S&elect Rename Color Delete Borrar Split Glue Super glue (merge selection) De-clone Save part to disk... Wave edit... File info... MusE: Save part Automation Automatizacion Normalize MusE: save part MusE: Guardar región. Part name: %1 Files: Nombre de región: %1 Archivos: Automation: Automatización Part canvas: Use Pencil tool to draw parts. Double-click to create a new MIDI/drum part between the range markers (set with MMB + RMB). Press F1 for help. Remove selected LMB: Select/Move/Dblclick to edit | CTRL+LMB: Multi select/Move&Copy | CTRL+ALT+LMB: Dblclick to edit in new window | SHIFT+LMB: Select track | MMB: Delete LMB: Draw to resize | MMB: Delete | CTRL+RMB: Trim length LMB: Delete | CTRL+RMB: Trim length LMB: Cut part in two LMB: Merge with following part LMB: Mute selected part LMB: Edit automation events in audio parts %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. %n Región(es) fuera de %1 no pueden ser pegadas. Probablemente la pista seleccionada es del tipo incorrecto. %n part(s) could not be pasted. Likely the selected track is the wrong type. %n Región(es) no puede ser pegada. Probablemente la pista seleccionada es del tipo incorrecto. Cannot paste: multiple tracks selected No puedo pegar: mtilpes pistas seleccionadas Cannot paste: no track selected No puedo pegar: No hay pista seleccionada Can only paste to midi/drum track Solo se puede insertar pistas MIDI/Percusión Can only paste to wave track Solo se pueden insertar pistas de audio Can only paste to midi or wave track Solo se pueden insertar pistas de MIDI o audio Cannot paste: wrong data type No válido: Tipo de datos equivocado MusEGui::PasteDialog %n quarter(s) %n Negra(s) %1 quarter for floating-point arguments like 1.5 %1 Negra %1 quarters for floating-point arguments like 1.5 %1 Negras MusEGui::PasteEventsDialog %n quarter(s) %n Negra(s) %1 quarter for floating-point arguments like 1.5 %1 Negra %1 quarters for floating-point arguments like 1.5 %1 Negras MusEGui::Piano Piano: Press key to play or enter events in step record mode (SHIFT for chords). RMB: Set cursor for polyphonic control events. CTRL+Mousewheel to zoom view vertically. Left click: Play Right click: Select key MusEGui::PianoCanvas Pianoroll canvas: Use Pencil tool to draw and edit MIDI events, Pointer tool to select and edit. Press F1 for help. Note: Velocity: Start (bar): Start (time): Length (ticks): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete | CTRL+RMB: Trim length LMB: Resize | CTRL+LMB: Multi select | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length LMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length MusEGui::PianoRoll &Edit &Editar C&ut C&ortar &Copy &Copiar Copy events in range Copiar eventos del rango &Paste &Pegar Paste to current part Paste (with dialog) Pegar (con dialogo) Delete &Events Borrar eventos &Select &Seleccionar Select &All Seleccion&ar todo &Deselect All &Deseleccionar todo Invert &Selection Invertir &selección &Inside Loop &Interior del bucle &Outside Loop &Exterior del bucle &Previous Part Región previa &Next Part Siguiente región Event List... Fu&nctions Fu&nciones Quantize Cuantizar Modify Note Length Modificar duraciòn de la nota Modify Velocity Modificar la velocidad. Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transponer Erase Events Borrar eventos Move Notes Mover notas Set Fixed Length Fijar ajustes de duración Delete Overlaps Borrar superpuestos Legato Legato &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Play single note Play chords &Plugins Efectos &Display Display menu: View-specific display options. Add controller view Step record Midi input Play events Window &Config &Configuraciòn de ventanas &Event Color Color de eventos &Blue Azul &Pitch colors Color por afinación &Velocity colors Color por velocidad Pianoroll tools Herramientas del editor de matríz Step Record Grabación por pasos Midi Input Entrada MIDI Play Events Reproducir eventos ctrl control Add Controller View Agregar visor de controladores MusEGui::PluginDialog MusE: select plugin MusE: Selecciona el plugin MusE: Select Plugin Plugin categories. Right-click on tabs to manage. Right-click on plugins to add/remove from a category. Type Tipo Lib Lib Label Etiqueta Name Nombre AI AI AO AO CI CI CO CO IP IP FB 2B 5 1/3' {2B?} id id Maker Fabricante Copyright Copyright Audio inputs Audio outputs Control inputs Control outputs In-place capable Requires fixed block size Requires power-of-2 block size ID number &Create new group &Delete currently selected group Re&name currently selected group New group Nuevo grupo Ok Aceptar Cancel Cancelar Mono and Stereo Mono y estereo Stereo Estéreo Mono Mono Show All Ver todo Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. Associated categories You need to define some categories first. Enter the new group name Wine VST dssi synth dssi effect LV2 synth LV2 effect VST synth VST effect ladspa Search in 'Label' and 'Name': Buscar en nombre y etiqueta MusEGui::PluginGui File Buttons Botón de archivos Load Preset Cargar plantilla Save Preset Guardar plantilla Bypass plugin Plugin settings MusE: Save preset Plugin Settings bypass plugin saltar plugin MusE: load preset MusE: Cargar plantilla Error reading preset. Might not be right type for this plugin Preseteo dañado o no corresponde al efecto MusE: save preset MusE: Guardar plantilla MusEGui::PluginSettings Global setting: On Off Apagado MusEGui::PopupMenu <More...> %1 MusEGui::PosToolbar Left marker Left marker position Right marker Right marker position Current position Current position in bars/beats SMPTE position Current position in SMPTE time Show/Hide position in ticks and audio frames Show/Hide additional output fields displaying the current position in ticks and audio frames Current position in ticks Current position in audio frames Current position slider MusEGui::ProjectCreateImpl Select directory MusEGui::RasterLabelCombo Snap MusEGui::RecToolbar Record mode Record mode: Overdub to add new events, Replace to replace overlapping events. Overdub Agregar Replace Reemplazar Cycle record mode Cycle record mode: Normal to replace range when loop is finished, Mix to add new events, Replace to replace range on first MIDI input. Normal Normal Mix Mezcla MusEGui::RouteDialog Normal Normal Alias 1 Alias 2 Source Fuente Destination Destino <none> MusEGui::RoutePopupMenu Channel Canal Soloing chain Cadena de solos Audio returns Retornos de audio Warning: No input devices! Atención: no hay entradas Channel grouping: Mono Stereo Estéreo Channels Omni Show aliases: First Second Show names Show first aliases Mostrar primero los álias Show second aliases Mostrsr después los álias Jack ports Connect Conectar Unavailable Open advanced router... Warning: No output devices! Atencion: No hay salidas disponibles Output routes: Input routes: Open midi config... Abrir la configuraciòn MIDI ALSA devices JACK devices Synth devices <none> <ningúno> Midi sends Sources: Toggle all Cambiar todos More... Mas... Tracks Destinations: Audio sends Envios de audio Midi port sends Puertos de envio MIDI MusEGui::ScoreCanvas Treble Agudos Bass Bajos Grand Staff Doble Remove staff Remover partitura Ambiguous part Parte ambígua There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. Hay dos o más partes posibles que podría añadir la nota, pero ninguno coincide con la parte seleccionada. Por favor, seleccione la parte de destino haciendo clic en cualquier nota que le pertenecen y vuelva a intentarlo, evite agregar una nota nueva que existe la parte destino. No part No hay partitura There are no parts you could add the note to. No hay piezas en las que se pueda añadir la nota. Tools Herramientas MusEGui::ScoreEdit Step recording tools Herramienta de grabación por pasos Step Record Grabación por pasos Note settings Propiedades de la nota Note length: Duración de nota: last Pasado Apply to new notes: Se aplica a nuevas notas Apply to selected notes: Aplicar a las notas seleccionadas Velocity: Velocidad: Off-Velocity: Off-Velocity: Quantisation settings Ajustes del cuantizado Step record Quantisation: Cuantizado: Pixels per whole: Pixeles por entero: &Edit &Editar C&ut C&ortar &Copy &Copiar Copy events in range Copiar eventos del rango Quantisation Pixels per whole Note length Velocity Apply to selected notes, or new notes if none is selected Off-Velocity Copy Events in Range &Paste &Pegar Paste (with dialog) Pegar (con diálogo) Delete &Events Borrar &eventos &Select &Seleccionar Select &All Seleccion&ar todo &Deselect All &Deseleccionar todo Invert &Selection Invertir &selección &Inside Loop &Interior de bucle &Outside Loop &Exterior del bucle Fu&nctions Fu&nciones &Quantize Cuantizar Change Note &Length Change Note &Velocity Display menu: View-specific display options. Note Head &Colors &Default Set Up &Preamble Display &Key Signature Display &Time Signature Set Score &Name Change note &length Cambiar duración de notas Change note &velocity Cambiar velocidad de las notas Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transponer Erase Events Borrar eventos Move Notes Mover notas Set Fixed Length Cambiar duración de notas Delete Overlaps Borrar notas duplicadas Legato Legato &Display Window &Config Comportamiento de ventana Note head &colors Color de las notas &Black Negro &Velocity Velocidad &Part Región Set up &preamble Establecer encabezado Display &key signature Ver armadura de clave Display &time signature Ver signatura de tiempo Set Score &name Ajuste de nombre del pentagrama Enter the new score title Ingrese el título de la nueva partitura Error Error Changing score title failed: the selected title is not unique Error al cambiar el título a la partitura: El título seleccionado ya existe. MusEGui::ScrollScale next page página siguiente previous page página anterior current page number nuúmero de página actual MusEGui::ShortcutCaptureDialog Ok Aceptar Cancel Cancelar Shortcut conflicts with %1 Conflicto en el atajo con Undefined Sin definir MusEGui::ShortcutConfig Save printable text file Text files (*.txt);;All files (*) Error Error Error opening file for saving Shortcuts for selected category: Legend: An error occurred while saving MusEGui::SigScale signature scale Armadura de la escala Signature scale MusEGui::SigToolbar Time signature at current position MusEGui::SigToolbarWidget time signature at current position compás en la posición actual MusEGui::Strip Name Nombre Enter track name: MusE: bad trackname MusE: Nombre de pista erroneo Please choose a unique track name Configuration Prefer knobs, not sliders Show values in controls Prefer midi volume as decibels Monitor on record-arm automatically Visible audio effects Actions Acciones Change track name Hide strip MusEGui::SyncToolbar External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::TList SYNTH IS UNAVAILABLE! <none> <ningúno> visible no clef No hay clave Treble Agudo Bass Bajo Grand Doble off <unknown> <desconocido> MusE: bad trackname MusE: Nombre de pista erroneo Please choose a unique track name Show gui Show native gui Clear automation please choose a unique track name Error: El nombre de pista ya existe Update drummap? ¿Actualizár mapa de percusión? Do you want to use same port for all instruments in the drummap? ¿Desea utilizar el mismo puerto para todos los instrumentos en el mapa de percusión? &Yes Aceptar &No Cancelar show gui Ver interfáz show native gui Ver interfáz nativa Presets Plantillas Clear all controller events? Limpiar todos los eventos de control &Ok &Cancel &Cancelar Change color Midi control Assign Clear Other Otros clear automation limpiar automatizacion Track list: Use context menu to create tracks. Click track ID to select, CTRL to add, SHIFT for range, dblclick to select all of same type. Press F1 for help. Treble clef Partitura de agudos Bass clef Partitura de Bajos Grand Staff Partitura doble Viewable automation Automatización visible Internal Synth Delete Track Borrar pista Delete Selected Tracks Borrar pistas seleccionadas Track Comment Comentario de la pista Save track's drumlist Load track's drumlist Reset track's drumlist Reset track's drumlist-ordering Copy track's drumlist to all selected tracks Insert Track Insertar pista Drum map Mapa de percusiòn Reset the track's drum map with instrument defaults? Reset the track's drum map ordering? Muse: Load Track's Drum Map MusE: Store Track's Drum Map Midi MIDI Drum Percusión Do you want to use same port and channel for all instruments in the drummap? ¿Desea utilizar el mismo puerto y canal para todos los instrumentos del mapa de percusión? MusEGui::TempoSig Use mastertrack tempo Tempo/Sig tempo/firma Mastertrack tempo at current position, or fixed tempo Time signature at current position MusEGui::TempoToolbar Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Tap Tap this button to set tempo Set the tempo to the time difference of two successive clicks. Times out after two seconds. MusEGui::TempoToolbarWidget tempo at current position tempo en la posición actual MusEGui::Toolbar1 Off Apagado Solo Solo Cursor Cursor Snap Chasquido MusEGui::TopWin As subwindow Como sub ventana Shares tools and menu Herramientas y menús Tabbed/Floating Display editor in a tab or in a separate window (preset in Global Settings->Editors). Fullscreen Pantalla completa Undo/Redo tools Panic Pánico Metronome Metrónomo Timeline Transport Transporte Recording Sync Sinconización Tempo Signature Compás Position Posición Piano roll Editor de matríz List editor Editor de lista Drum editor Editro de percusión Master track editor Editor de pista maestra Master track list editor Editor de lista de pista maestra Wave editor Editro de audio Clip list Lista de audios Marker view Ver marcadores Score editor Editor de partituras Arranger Arreglador <unknown toplevel type> <Tipo de nivel superior desconocido> MusEGui::TrackComment MusE: Track Comment MusE: Comentario de la pista Track Comment: Comentario de la pista: MusEGui::Transport Overdub Agregar Replace Reemplazar Rec Mode Modo de captura Normal Normal Mix Mezcla Cycle Rec Ciclar captura Punch in Punch out Rewind to Start Rewind Retroceder Forward Avanzar Stop Parar Play Reproducir Record Grabar Metronome on/off External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. punchin pinchar loop bucle punchout fin pinchado Punch In Pinchar Loop Bucle Punch Out Fin pinchado Left Mark Marca izquierda Right Mark Marca derecha rewind to start retroceder al inicio Click this button to rewind to start position Pulsa este botón para regresar al principio rewind retroceder Click this button to rewind Pulsa este botón para ir hacia atrás forward avanzar Click this button to forward current play position Pulsa este botón para adelantar la posición de reproducción stop parar Click this button to stop playback Pulsa este botón para detener la reproducción play reproducir Click this button to start playback Pulsa este botón para iniciar la reproducción record grabar Click this button to enable recording Pulsa este botón para habilitar la captura AC AC quantize during record cuantizar durante la captura Click Click metronom click on/off Click del metrónomo encendido/apagado Sync Sinconización external sync on/off Sincronización externa encedida/apagada Jack Jack Jack transport sync on/off Sincronizar a Jack Encendido/Apagado Master Maestro use master track usar pista maestra MusEGui::VisibleTracks Show wave tracks Ver pistas de audio Show group tracks Ver grupos Show aux tracks Ver envios auxiliares Show input tracks Ver entradas de audio Show output tracks Ver salidas de audio Show midi tracks Ver pistas MIDI Show synth tracks Ver Sintetizadores Visible track types Ver tipo de pistas MusEGui::WaveCanvas Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help. Adjust Wave Offset Wave offset (frames) Part creation failed Left and right position markers must be placed inside the current part. Part created The selected region has been copied to the clipboard and can be pasted in the arranger. MusE - external editor failed MusE: Falla del editor de audio externo MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. Muse no pudo lanzar el editor externo Compruebe esta configuraciòn en: Configuración global-> Audio: Editor de audio externo que el comando del editor sea válido. MusE - file size changed MusE: Cambió el tamaño del archivo When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Al editar en el editor externo no se debe cambiar el tamaño del archivo ya que debe ajustarse a la región seleccionada. Los datos faltantes se silenciarán. Wave event: Converter settings MusEGui::WaveEdit &Edit &Editar Func&tions Funciones &Gain &Ganancia Other Otros &Copy &Copiar &Create Part from Region C&ut Cortar &Paste &Pegar Edit in E&xternal Editor Abrir con editor externo Edit in E&xternal Editor... Mute Selection Silenciar selección Normalize Selection Normalizar selección Fade In Selection Aparecer progresivamente Fade Out Selection Desvanecer progresivamente Reverse Selection Invertir selección Select Seleccionar Select &All Seleccion&ar todo &Deselect All &Deseleccionar todo &Previous Part Región previa &Next Part Siguiente región &Display Display menu: View-specific display options. Window &Config &Comportamiento de ventana &Event Color Color de eventos &Part colors &Gray WaveEdit tools Herramientas de edición de audio Solo Solo Cursor Cursor MusEGui::WaveView MusE - external editor failed MusE: Falla del editor de audio externo MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. Muse no pudo lanzar el editor externo Compruebe esta configuraciòn en: Configuración global-> Audio: Editor de audio externo que el comando del editor sea válido. MusE - file size changed MusE: Cambió el tamaño del archivo When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Al editar en el editor externo no se debe cambiar el tamaño del archivo ya que debe ajustarse a la región seleccionada. Los datos faltantes se silenciarán. OrganGuiBase MusE: Organ MusE: órgano O-1 0-1 Oscillator Oscillator Brass Brass Reed Reed Flute Flute Drawbars Drawbars 16' 16' 4' 4' 2 2/3' 2 2/3' 2' 2' 8' 8' 5 1/3' 5 1/3' Envelope Lo Envelope Lo Envelope Hi Envelope Hi Release Release Sustain Sustain Decay Decay Attack Attack ms ms cB cB Velocity Velocity PasteDialogBase MusE: Paste Parts MusE: Pegar regiones. Number and raster Cantidad y alineado insert Insertar times -Tiempos raster Trama Insert Insertar Raster Raster ticks Tics Move, Merge, Clone Mover, Mezclar, Clonar Move everything to the right Mover todo a la derecha. Move only affected parts to the right Mover sólo las regiones afectadas a la derecha. Put everything into a single track Poner todo en una sola pista. Merge with existing parts Mezclar con las regiones existentes. Insert as clones (where possible) Insertar como clones (si es posible) OK Aceptar Cancel Cancelar PasteEventsDialogBase MusE: Paste Events MusE: Pegar eventos Number and raster Cantidad y alineado insert Insertar times -Tiempos raster Alineado ticks -Tics Paste options Opciones del pegado &Always into existing parts &Never into existing parts &Into existing parts if part has not to be expanded by more than Always into existing parts Siempre en las regiones existentes. Never into existing parts Nunca en las regiones existentes. Into existing parts if part has not to be expanded by more than En las regiones existentes si mas de una región no tiene que ser ampliada . Insert Insertar Raster Raster Put everything into the (selected) part Poner todo lo seleccionado en una región. Paste midi controller target options Keep e&xisting Erase existin&g Erase WYSIWYG Erase inclusive OK Aceptar Cancel Cancelar PluginDialogBase Dialog Dialogo Ports: Mono Mono Mono + Stereo Stereo Estéreo All Todo Plugin type: Filter: &OK &Cancel &Cancelar PluginSettings Dialog Dialogo Options Fixed speed Transport affects audio latency Override reported audio latency Reported audio latency override value Revert HiDPI scaling of native GUI Set in Global settings->GUI tweaks Global setting Additional information Follow global setting On Off Apagado ProjectCreate Create Project Crear sesión Projects folder: ... ... Project Name: Nombre del la sesión: Project is a Template Write window state Project song file type: Project Path to song file: Ruta de la sesión: &Create project folder (recommended for audio projects) Create project folder (recommended for audio projects) Crear carpeta para la sesión (recomendado para sesiones de audio) Song information: Información de la sesión QObject Error Error Please first select the range for crescendo with the loop markers. Por favor, seleccione el rango de crescendo con los marcadores de bucle. Controller ! Other ! Presets Plantillas Switch on General Midi Level 1 mode Switch on General Midi Level 2 mode Switch off General Midi Level 1 or 2 Switch on Roland GS mode Switch on Yamaha XG mode Tracks: Midi ports: Midi devices: Jack: Jack midi: Instruments Current device Others Otros Files Synthesizers Open Abrir Closed <Properties> <Base Property> <Property> Input routing Warning: No input routes! Click to connect... Output routing Warning: No output routes! Click to connect... TRACK TAKE MusE import error. MusE failed to import the file. Possibly this wasn't a sound file? If it was check the permissions, MusE sometimes requires write access to the file. FX slot QWidget Cannot convert sysex string No se puede convertir el paquete de sistema exclusivo Hex String too long (2048 bytes limit) Cadena hexadecimal demasiado larga (límite de 2048 bytes) generic midi MIDI genérico new Nueva sesión create peakfile for Crear archivo de picos MusE: get file name MusE: Obtener el nombre de archivo The directory %1 does not exist. Create it? La carpeta %1 no existe. ¿Desea crearla? MusE: create directory MusE: Crear carpeta creating dir failed falló la creación de carpeta File %1 exists. Overwrite? El archivo %1 ya existe. ¿Desea sobreescribirlo? Open File %1 failed: %2 Abrir archivo %1 falló: %2 MusE: write MusE: Escribir Creating dir failed MusE: Open File MusE: Abrir archivo None Ningúno No wave events selected. No selection. Ignoring No hay nada seleccionado! Instrument-defined Definición de instrumento Generic midi Edit instrument ... Editar instrumento... Add Others Otros Common Controls Controles comunes Velocity Create peakfile for New Nuevo QuantBase MusE: Quantize MusE: Cuantizár Range Rango All Events Todos los eventos Selected Events Eventos seleccionados Looped Events Eventos del bucle Selected Looped Seleccionar bucle Parts Regiones Selected parts or c&urrent part All parts &in editor Values Valores Strength: Porcentaje: % % Threshold (ticks): Umbrál (Tics): Quantize Len Cuantizar duración Raster Raster Whole Totalidad Half Mitad 4th 4th 4th Triplet 4th Triplet 8th Octavos 8th Triplet Octavos con puntillo 16th 16th 16th Triplet 16th Triplet 32th 32th 32th Triplet 32th Triplet Swing: Oscilación: If the proposed change in tick or length is smaller than threshold, nothing is done. If swing=0, this is normal If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. Si en el cambio propuesto la duración es menor que el umbral, no se hace nada. Si la oscilación es = 0, esto es normal Si la oscilación es de 33, se obtiene un ritmo 2:1. Si la oscilación es de -33, se obtiene un ritmo1:2. OK Aceptar Cancel Cancelar RemoveBase MusE: Erase Notes MusE: Borrar notas Range Rango All Events Todos los eventos Selected Events Eventos seleccionados Looped Events Eventos del bucle Selected Looped Bucle seleccionado All Eve&nts Selec&ted Events &Looped Events Selected L&ooped Parts Regiones Selected parts or c&urrent part All parts &in editor Thresholds Umbrales ticks -Tics Velocity Velocidad Length Duración <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> OK Aceptar Cancel Cancelar RhythmGenerator RouteDialogBase MusE: Routing Muse: Ruteado List of available sources. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Connections View window. Shows all current connections. Thick lines are Omni Routes. Thin lines are Channel Routes. List of available destinations. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Itemized list of current connections. Show only selected sources Show only destination routes for selected source Show only selected destinations Show only source routes for selected destination Show all Midi Ports Auto adjust column size Automatically adjusts the source and destination tree widths when the splitters are adjusted. This also turns on text word wrap, which may cause slower response with larger lists. Preferred route name or alias Connect source to destination Remove selected route Add Route Agregar ruta Source: Fuente: Destination: Destino: Connect Conectar connect source to destination Conectar fuente a destino Current Routes Rutas actuales Source Fuente Destination Destino Remove Eliminar remove selected route Eliminar ruta seleccionada RubberbandSettingsBase Rubberband Settings Rubberband support is not enabled Use default settings Presets: Preseteos: Default Percussion Maximum Stretch: Elastic Precise Window: Standard Estandar Short Long Smoothing: Off Apagado On Channels: Apart Together Transients: Crisp Mixed Smooth Detector: Compound Percussive Soft Phase: Laminar Independent Formant: Shifted Preserved Pitch: Tono: High speed High quality High consistency OK Aceptar Cancel Cancelar SRCResamplerSettingsBase SRC Resampler Settings Use default settings Converter Type: SINC Best quality SINC Medium quality SINC Fastest Zero Order Hold Linear OK Aceptar Cancel Cancelar SS_PluginChooserBase SimpleDrums - Ladspa Plugin Chooser SimpleDrums - Selector de Efecto Ladspa Name Nombre Label Etiqueta Inports Puerto de entrada Outports Puerto de salida Creator Creador &Cancel &Cancelar Alt+C Alt+C &OK Aceptar Alt+O Alt+O SS_PluginFront Clear and unload effect Limpiar y descargar efecto Load effect Cargar efecto Toggle display of effect parameters Vista de los parámetros de efecto Turn effect on/off Encender/Apagar efecto SetlenBase MusE: Set Note Length MusE: Cambiar duración de notas Range Rango All Events Todos los eventos Selected Events Eventos seleccionados Looped Events Eventos en bucle Selected Looped Seleccionar bucle Parts Regiones Selected parts or c&urrent part All parts &in editor Value Valor New length Nueva duración ticks -Tics OK Aceptar Cancel Cancelar ShortcutCaptureDialogBase Enter shortcut sequence Ingresar atajo Press keys to enter shortcut sequence! Ingrese la secuencia de teclas Old shortcut: Atajo anterior Undefined Sin definir New shortcut: Nuevo atajo OK Aceptar Cancel Cancelar ShortcutConfigBase Configure Keyboard Shortcuts Configurar atajos de teclado Shortcut Category Categoría de accesos rápidos Description Descripción Shortcut Acceso rápido Filter: &Reset all &Clear Limpiar Alt+C Alt+C &Define &Definir Alt+D Alt+D &Apply &Aplicar Alt+A Alt+A &Printable file... Alt+P Alt+P &Ok Alt+O Alt+O SimpleDrumsGuiBase DrumSynth 0.1 DrumSynth 0.1 Audio file assignments. Within parantesis: note number and traditional midi drum name SimplePluginChooserBase SimpleDrums - Ladspa Plugin Chooser SimpleDrums - Selector de Efecto Ladspa Name Nombre Label Etiqueta Inports Puerto de entrada Outports Puerto de salida Creator Creador &Cancel &Cancelar Alt+C Alt+C &OK Alt+O Alt+O SimpleSynthGui Mix Mezcla Chn Channel routing &Load setup Cargar configuración &Save setup Guardar configuración Load sample dialog Cargar diálogo de muestra SnooperDialogBase Snooper Mode Auto-hide Separate parented top levels Update tree Mouse clicks Key presses Flash timer s Reset Filters MusEGui QWidget Properties Propiedades Search: Class name: Object name: About This tool helps developers and designers discover objects. Click on anything anywhere or press a key to highlight an item. It will NOT auto-update when new objects are created. Click 'Update tree'. When open, may slow program slightly. This dialog is NOT shown. Class name::Object name Property Type Tipo Value Valor Event type SongInfo Song Information Información de la sesión Show on song load Mostrar al cargar la sesión &Cancel &Cancelar Alt+C Alt+C &Ok Aceptar Alt+O Alt+O SynthConfigBase Soft Synthesizer Sintetizador Virtual File Archivo Instances Copias Name Nombre list of available software synthesizers lista de sintetizadores de software dispoibles Add Instance Agregar copia Remove Instance Quitar copia Midi Port Puerto MIDI Type Tipo Midi connections Conexiones de MIDI Configure midi devices, midi ports, and synthesizers Available soft synthesizers List of available software synthesizers Inst Instrumento Version Versión Description Descripción Useable devices Add: Synth ALSA JACK Rename Remove Eliminar Device port assignments &Apply &Aplicar &OK TransposeBase MusE: Transpose MusE:-Transponer Range Rango All Events Todos los eventos Selected Events Eventos seleccionados Looped Events Eventos del bucle Selected Looped Bucle seleccionadp Parts Regiones Selected parts or c&urrent part All parts &in editor Value Valor Halftone-steps Semitonos OK Aceptar Cancel Cancelar UnusedWaveFiles Dialog Dialogo List of unused audio files in current project directory: Listar archivos de audio sin uso en la sesión Current project Sesión actual All .med files in current directory Todas las sesiones en la carpeta actual Move files to 'unused' subdir Mover no usados a carpeta "unused" Cancel Cancelar VAMGui MusE: Load VAM Presets MusE: Cargar preseteos de VAM MusE: Save VAM Presets Guardar preseteos de VAM VAMGuiBase Virtual Analogue for MusE Analogico virtual para MusE LFO LFO Freq Freq Waveform Tipo Onda Sine Seno Pulse Pulso Saw Sierra Triangle Triángulo Filter Filtro EnvMod Modulación de envolvente Attack Ataque Decay Decaimiento Sustain Sostenido Release Relajación Cutoff Frecuencia de corte Resonance Resonancia Invert Invertir KeyTrack Seguir teclas Presets Plantillas Set Establecer load preset list cargar lista de plantillas save preset list guardar lista de plantillas save preset list to a new file guardar lista de plantillas en un archivo nuevo delete preset borrar plantilla DCO 1 DCO 1 Pitch Tono Detune Desafinación PWM PWM FM FM PW PW DCO 2 DCO 2 On Encendido VAM 1.0beta3 Virtual Analog for MusE Released under GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VAM 1.0beta3 Sintetizador análogo virtual para MusE Liberado bajo licencia GPL. Derechos Reservados(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Derechos Reservados(C) 2005 Robert Jonsson (rj@spamatica.se) VelocityBase MusE: Modify Velocity MusE: Modificar la velocidad. Range Rango All Events Todos los eventos Selected Events Eventos seleccionados Looped Events Eventos del bucle Parts Regiones Selected parts or c&urrent part All parts &in editor Values Valores Rate: Porcentajes: Offset: Desplazamiento: % % Selected Looped Selecionar bucles veloNew = (veloOld * rate) + offset OK Aceptar Cancel Cancelar ZitaResamplerSettingsBase Zita Resampler Settings Zita Resampler support is not enabled Use default settings Options: OK Aceptar Cancel Cancelar file_patterns Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) All Files (*) Todos los archivos (*) Midi (*.mid) Karaoke (*.kar) all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) med Files (*.med *.med.gz *.med.bz2) Uncompressed med Files (*.med) gzip compressed med Files (*.med.gz) bzip2 compressed med Files (*.med.bz2) mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) (*.jpg *.gif *.png) (*.jpg) (*.gif) (*.png) part Files (*.mpt *.mpt.gz *.mpt.bz2) part Files (*.mpt) gzip compressed part Files (*.mpt.gz) bzip2 compressed part Files (*.mpt.bz2) Presets (*.pre *.pre.gz *.pre.bz2) Presets (*.pre) gzip compressed presets (*.pre.gz) bzip2 compressed presets (*.pre.bz2) Presets (*.map *.map.gz *.map.bz2) Presets (*.map) gzip compressed presets (*.map.gz) bzip2 compressed presets (*.map.bz2) Wave/Binary (*.wav *.ogg *.flac *.bin) Wave (*.wav *.ogg *.flac) Binary (*.bin) Color configuration files (*.cfc) Qt style sheets (*.qss) Hoja de estilo QT (*.qss) freeverb FreeVerb FreeVerb Room Size Room Size Damping Damping Wet Level Wet Level Tap-Reverberator Tap-Reverb Decay [ms] Decay [ms] dB dB Dry Level [dB] Dry Level [dB] Wet Level [dB] Wet Level [dB] Preset: Preseteo: AfterBurn AfterBurn AfterBurn (Long) AfterBurn (Long) Ambience Ambience Ambience (Thick) Ambience (Thick) Ambience (Thick) - HD Ambience (Thick) - HD Cathedral Cathedral Cathedral - HD Cathedral - HD Drum Chamber Drum Chamber Garage Garage Garage (Bright) Garage (Bright) Gymnasium Gymnasium Gymnasium (Bright) Gymnasium (Bright) Gymnasium (Bright) - HD Gymnasium (Bright) - HD Hall (Small) Hall (Small) Hall (Medium) Hall (Medium) Hall (Large) Hall (Large) Hall (Large) - HD Hall (Large) - HD Plate (Small) Plate (Small) Plate (Medium) Plate (Medium) Plate (Large) Plate (Large) Plate (Large) - HD Plate (Large) - HD Pulse Chamber Pulse Chamber Pulse Chamber (Reverse) Pulse Chamber (Reverse) Resonator (96 ms) Resonator (96 ms) Resonator (152 ms) Resonator (152 ms) Resonator (208 ms) Resonator (208 ms) Room (Small) Room (Small) Room (Medium) Room (Medium) Room (Large) Room (Large) Room (Large) - HD Room (Large) - HD Slap Chamber Slap Chamber Slap Chamber - HD Slap Chamber - HD Slap Chamber (Bright) Slap Chamber (Bright) Slap Chamber (Bright) HD Slap Chamber (Bright) HD Smooth Hall (Small) Smooth Hall (Small) Smooth Hall (Medium) Smooth Hall (Medium) Smooth Hall (Large) Smooth Hall (Large) Smooth Hall (Large) - HD Smooth Hall (Large) - HD Vocal Plate Vocal Plate Vocal Plate - HD Vocal Plate - HD Warble Chamber Warble Chamber Warehoouse Warehoouse Warehouse - HD Warehouse - HD Comb Filters Filtros combinados Allpass Filters Sin filtros Bandpass Filters flitros de banda Enhanced Stereo Ampliar estéreo main File to open Alsa midi only (using dummy audio driver) Use RtAudio driver Use JAckAudio driver to connect to Jack audio server Do not try to auto-start the Jack audio server Do not auto-populate midi ports with midi devices found, at startup Force inclusion of ALSA midi even if using Jack Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) Force midi real time priority to n (default: audio driver prio -1) Force plugin cache re-creation. (Automatic if any plugin path directories changed.) Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care. Don't load LADSPA plugins Don't load MESS plugins Don't load VST plugins Don't load LinuxVST plugins Don't load DSSI plugins Don't load LV2 plugins Don't use LASH Force locale to the given language/country code (xx = Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins. Debug mode: no threads, no RT Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively! Debug mode: trace midi Input Debug mode: trace midi Output Debug mode: trace sync Enable Python control support Pyro nameserver host name Pyro nameserver host port Pyro daemon host name Pyro daemon host port Pyro communication timeout in seconds midiWarnInitPendingBase Instrument initialization MusE should now send some Instrument Initialization Sequences. The sequences (usually System Exclusive messages) are defined by the selected instruments in the Settings -> Midi Ports dialog, such as the GM (default), GS, or XG instruments. Typically you should answer yes here. You can always do it manually from the Midi menu. Continue? Don't ask me again shortcuts Transport: Start playback from current location Transporte: Iniciar reproducción desde la ubicación actual Transport: Toggle metronome Transporte: Alternar metrónomo Transport: Stop Playback Transporte : Detener reproducción Transport: Goto Start Transporte: Ir al comienzo Transport: Goto End Transport: Play, Stop, Rewind Transporte: Iniciar, Detener, Rebobinar Transport: Restart recording Transport: Restart recording (multi take) Transport: Goto left marker Transporte: Ir a la marca izquierda Transport: Goto right marker Transporte: Ir a la marca derecha Transport: Toggle Loop section Transporte: Alternar selección del bucle Transport: Toggle Record Transporte Alternar grabación Transport: Arm current track Transport: Clear all rec enabled tracks Transporte: deshabilitar el grabado en todas las pistas Toggle fullscreen Pantalla completa Toggle tabbed/floating editor window Toggle docks display Toggle rewind on stop Show Arranger window Edit: Copy Editar:Copiar Edit: Copy in range Editar: Copiar rango Edit: Undo Editar: Deshacer Edit: Redo Edit: Rehacer Edit: Cut Editar: Cortar Edit: Paste Editar: Pegar Edit: Paste to current part Edit: Paste to selected track Edit: Paste clone Edit: Paste clone to selected track Edit: Paste (with dialog) Editar: Pegar (con diálogo) Edit: Delete Editar: Borrar File: New project Archivo: Nueva sesión File: New project from template File: Open from disk Archivo: Abrir desde disco File: Save project Archivo: Guardar sesión File: Open recent file Archivo: Sesiones recientes File: Save as Archivo: Guardar como File: Load template Archivo: Abrir plantilla File: Import midi file Archivo: Importar archivo MIDI File: Export midi file Archivo: Exportar archivo MIDI File: Import midi part Archivo: Importar región MIDI File: Import audio file Archivo: Importar archivo de audio File: Quit MusE Archivo: Cerrar MusE Edit: Rename part Edit: Select parts on track Editar: Seleccionar regiones de la pista Open pianoroll Abrir editor de matríz Open drumeditor Editar: Editor de percusión Open listeditor Abrir editor de lista Open waveeditor Abrir editor de audio Open graphical mastertrack editor Vista de pista maestra Open list mastertrack editor Lista de pista maestra Open midi transformer Abrir transformador MIDI Open track Synth GUI Open pianoroll in new window Open drumeditor in new window Open listeditor in new window Open waveeditor in new window Add midi track Agregar pista MIDI Add drum track Agregar pista de percusión Add wave track Agregar pista de audio Add audio output Agregar salida de audio Add audio group Agregar grupo de audio Add audio input Agregar entrada de audio Add audio aux Agregar envio auxiliar Insert midi track Insert drum track Insert wave track Insert audio output Insert audio group Insert audio input Insert audio aux Structure: Global cut Estructura: Corte Globál Structure: Global insert Estructura: Inserción global Structure: Global split Estructura: División globál Structure: Cut events Estructura: Cortar eventos View: Open mixer #1 window Ver: Abrir mezclador #1 View: Open mixer #2 window Ver: Abrir mezclador #2 View: Toggle transport window Ver: Ventana alternativa del transporte View: Toggle bigtime window Ver: Ventana de reloj View: Open marker window Ver: Abrir ventana de marcadores View: Toggle mixer strip display Settings: Follow song by page Ajustes: Seguir página Settings: Follow song off Ajustes: No seguir canción Settings: Follow song continuous Ajustes: Seguir canción indefinidamente Settings: Global configuration Configuraciones: Configuración global Settings: Configure shortcuts Configuraciones: Atajos de teclado Settings: Configure metronome Configuraciones: Metrónomo Settings: Midi sync configuration Configuraciones: Sincronía MIDI Settings: Midi file import/export configuration Configuraciones: Configuracion de exportar/importar MIDI Settings: Appearance settings Configuraciones: Apariencia Settings: Midi ports / Soft Synth Configuraciones: Puertos MIDI/Sintetizadores virtuales Settings: Audio subsystem configuration Configuraciones: Configurar sub sistema de audio Midi: Edit midi instruments MIDI: Editar instrumentos Midi: Open midi input transform MIDI: Abrir el transformador de entrada MIDI Midi: Open midi input filter MIDI: Abrir el filtro de entrada MIDI Midi: Midi input transpose MIDI: Filtro de transporte de entrada Midi: Midi remote control MIDI: Control remoto MIDI Midi: Random rhythm generator MIDI: Generador de ritmo aleatorio Midi: Reset midi MIDI: Resetear MIDI Midi: Init midi MIDI: Inicializar MIDI Midi: Midi local off MIDI: MIDI local apagado Audio: Bounce audio to track Audio: Rebotar audio a pista Audio: Bounce audio to file Audio: Rebotar audio a archivo Audio: Restart audio Audio: Reiniciar audio Automation: Mixer automation Automatización: Mezcladora de automatización Automation: Take mixer snapshot Automatización: Tomar instantánea del mezclador Automation: Clear mixer automation Automatización: Limpiar la mezcladora de automatización Help: Open Manual Ayuda: Abrir manual Help: Toggle whatsthis mode Ayuda: Cambiar el modo ¿qué es esto? Edit: Edit selected part Editar: Seleccionar región Edit: Select nearest part on track above Editar: Seleccione la región más cercana por encima de la pista Edit: Add nearest part on track above Editar: Agregar la región más cercana por encima de la pista Edit: Select nearest part on track below Editar: Seleccione la región más cercana por debajo de la pista Edit: Add nearest part on track below Editar: Agregar la región más cercana por debajo de la pista Edit: Insert empty measure Editar: Insertar compás vacío Edit: Paste as clones Editar: Pegar como clon Edit: Paste as clones (with dialog) Editar: Pegar como clon (con diálogo) Edit: Duplicate track Select track above Seleccionar la pista anterior Select track below Seleccionar la pista siguiente Edit selected track name Mute all currently selected tracks Solo all currently selected tracks Midi: Transpose MIDI: Transportar Edit: Select all Editar: Seleccionar todo Edit: Select none Editar: Deseleccionar Edit: Invert Selection Editar: Invertir selección Edit: Select events/parts inside locators Editar: seleccionar eventos/regiones dentro de los marcadores Edit: Select events/parts outside locators Editar: seleccionar eventos/regiones fuera de los marcadores Edit: Select previous part Edición: Seleccionar región anterior Edit: Select next part Editar: Seleccionar región siguiente Edit: Select nearest part/event to the left or move cursor Editar: Seleccione región /evento mas cercano a la izquierda o mover el cursor Edit: Add nearest part/event to the left to selection Edit: Añadir región/evento mas cercano a la izquierda a la selección Edit: Select nearest part/event to the right or move cursor Editar: seleccione la región/evento mas cercano la derecha o mover el cursor Edit: Add nearest part/event to the right to selection Edit: Añadir la región/evento a la derecha a la selección Edit: Set locators to selection Editar: Conjunto localizadores a la selección Edit: Increase pitch Editar: Subir afinación Edit: Decrease pitch Editar: Bajar afinación Edit: Increase event position Editar: Aumenta la posición del evento Edit: Decrease event position Editar: Disminuye la posición del evento View: Zoom in Ver: Acercar View: Zoom out Ver: Alejar View: Goto Current Position Ver: Ir a la posición actual View: Scroll left Ver: Mover a la izquierda Transport: Step record Transport: Midi input Transport: Play events Edit: Increase velocity Edit: Decrease velocity Edit: Set Fixed Length on Midi Events Editar Igualar la duración de las notas MIDI Quantize Cuantizar Modify Note Length Modificar duraciòn Modify Velocity Modificar la velocidad Edit: Crescendo Editar: Crescendo Edit: Thin Out Editar: Truncar Edit: Erase Event Editar: Borrar evento Edit: Delete Overlaps Editar: Borrar notas duplicadas Edit: Note Shift Editar: Alterar nota Edit: Move Clock Editar: Mover reloj Edit: Copy Measure Editar: Copiar compás Edit: Erase Measure Editar: Vaciar compás Edit: Delete Measure Editar: Borrar compás Edit: Create Measure Editar: Crear compás Edit: Change Event Color Editar: Cambiar el color de evento Move: Move to selected note Tool: Pointer Herramientas: Apuntador Tool: Pencil Herramientas: Lápiz Tool: Eraser Herramientas: Borrador Tool: Pan Tool: Zoom Tool: Line Draw Herramientas: Linea de dibujo Tool: Cursor Herramientas: Cursor Add note velocity 1 Añadir velocidad de la nota 1 Add note velocity 2 Añadir velocidad de la nota 2 Add note velocity 3 Añadir velocidad de la nota 3 Add note velocity 4 Añadir velocidad de la nota 4 Cursor step size: larger Aumentar el paso de cursor Cursor step size: smaller Reducir el paso del cursor Instrument/Cursor up Instrumento, Incrementar valor Instrument/Cursor down Instrumento, Disminuir valor Tool: Range Tool: Stretch Tool: Sample rate Tool: Scissor Herramientas: Tijeta Tool: Glue Herramienta: Unir Tool: Mute Herramienta: Silenciar Transport: Increase current position Transporte: Incrementar la posición actual Transport: Decrease current position Transporte: retroceder de la posición actual Transport: Increase current position, no snap Transporte: Adelantar de la posición actual, no snap Transport: Decrease current position, no snap Transporte: retroceder de la posición actual, no snap Quantize: Set quantize to off Quantize: Set quantize to 1/1 note Cuantizar: Ajustar cuantizado a 1/1 note Quantize: Set quantize to 1/2 note Cuantizar: Ajustar cuantizado a 1/2 note Quantize: Set quantize to 1/4 note Cuantizar: Ajustar cuantizado a 1/4 note Quantize: Set quantize to 1/8 note Cuantizar: Ajustar cuantizado a 1/8 note Quantize: Set quantize to 1/16 note Cuantizar: Ajustar cuantizado a 1/16 note Quantize: Set quantize to 1/32 note Cuantizar: Ajustar cuantizado a 1/32 note Quantize: Set quantize to 1/64 note Cuantizar: Ajustar cuantizado a 1/64 note Quantize: Toggle triol quantization Cuantizado: Ajuste de cauntización triol. Quantize: Toggle punctuation quantization Cuantización: Cambiar-puntuación-del cuantizado Quantize: Toggle punctuation quantization (2) Cuantización: Cambiar-puntuación-del cuantizado (2) Edit: Insert at location Editar: Insertar en la posición Edit: Increase length Editar: Incrementar duración Edit: Decrease length Editar: Acortar duración Insert Note Insertar nota Insert SysEx Insertar sistema exclusivo Insert Ctrl Insertar controlador Insert Meta Insertar Meta Select strip to the left Select strip to the right Mixer strip volume down Mixer strip volume up Mixer strip pan left Mixer strip pan right Mixer strip volume down page Mixer strip volume up page Mixer strip pan left page Mixer strip pan right page Insert Channel Aftertouch Insertar AfterTouch de canal Insert Key Aftertouch Insertar AfterTouch de tecla Insert Tempo Insertar tempo Insert Signature Insertar armadura de clave Change Event Position Cambiar posición de evento Edit Event Value Editar valor del evento Insert Key Insertar clave Settings: Midi ports / Soft synths Goto Next Marker Ir a la siguiente marca Goto Prev Marker Ir a la marca anterior Normalize warnBadTimingBase Bad timing Message here Don't warn me again muse-4.0.0/src/share/locale/muse_fr.ts000066400000000000000000027500511404105311000176230ustar00rootroot00000000000000 @default Add Midi Track Ajouter une Piste Midi Add Drum Track Ajouter une Piste de Batterie Add Old Style Drum Track Ajouter une Piste de Batterie (Ancien Style) Add New Style Drum Track Ajouter une Piste de Batterie (Nouveau Style) Add Wave Track Ajouter une Piste Audio Add Audio Output Ajouter une Sortie Audio Add Audio Group Ajouter un Groupe Audio Add Audio Input Ajouter une Entrée Audio Add Aux Send Ajouter un Envoie Aux Add Synth Ajouter un Synthétiseur Select project directory Sélectionner le répertoire du projet Route Route dest dest name="%1"/ nom ="%1"/ Warning: No output devices! Attention: Pas de périphériques de sortie! Open midi config... Ouvrir la config midi... Output port/device Port/périphérique de sortie default défaut Empty ports Ports vides <none> <rien> channelMask="%1" masqueCanal="%1" Bad timing Mauvais timing Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. Also please check console output for any further error messages. Le timing de la fréquence source est de %1hz, ce qui est inférieur au minimum recommandé: 500hz! Ceci peut amener des problèmes de timing audibles pour le MIDI. Veuillez lire la FAQ à l'adresse http://muse-sequencer.org pour obtenir des solutions. Veuillez aussi vérifier les sorties dans la console pour plus de messages d'erreurs. AboutBox AboutBox Boite A Propos Version 2 Version 2 Version 2.1.1 Version 2.1.1 Version 2.2beta1 Version 2.2beta1 (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer et autres. Voir http://www.muse-sequencer.org pour de nouvelles versions et pour plus d'information. Publié sous la licence publique GNU Version 2.2beta2 Version 2.2beta2 {2.2b?} (C) Copyright 1999-2014 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer et autres. Voir http://www.muse-sequencer.org pour de nouvelles versions et pour plus d'information. Publié sous la licence publique GNU {1999-2014 ?} (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer et autres. Voir http://www.muse-sequencer.org pour de nouvelles versions et pour plus d'information. Publié sous la licence publique GNU {1999-2014 ?} {1999-2015 ?} MusE MusE (C) Copyright 1999-2020 the MusE development team. See https://muse-sequencer.github.io for new versions and more information. Published under the GNU Public License. &Keep On Rocking! &Keep On Rocking! Alt+K Internals System information (useful for debugging): AppearanceDialogBase Apply Appliquer Ok Ok Cancel Annuler Arranger Arrangeur Parts Parties show names afficher les noms show events afficher les évènements show Cakewalk Style afficher dans le style Cakewalk y-stretch y-étirement Events Evènements note on note on poly pressure pression poly controller contrôleurs aftertouch pression (aftertouch) pitch bend molette de ton (pitch) program change sélection de programme (prog. change) special spécial Wave Drawing in Parts O&nly outline Background picture Image de fond Fill in optimal font sizes in relation to the system font show snap grid voir la grille de positionnement Colors Couleurs Items Eléments Style/Fonts Styles/Polices QT Theme Thème QT Windows Windows MusE MusE Metal Métal Norwegian Wood Bois Norvégien Platinum Platine CDE CDE Motif Motif Motif Plus Motif Plus May require restarting MusE for best results Peut nécessiter un redémarrage de MusE pour de meilleurs résultats MusE color scheme Schéma de couleur MusE current settings réglages courants light theme (changing overrides all other settings) thème clair (le changement écrasera tous les autres réglages) dark theme (changing overrides all other settings) thème foncé (le changement écrasera tous les autres réglages) Fonts Polices Family Famille Size Taille Font 1 Police 1 Font 2 Police 2 Font 3 Police 3 Font 0 Police 0 Bold Gras Italic Italique ... ... Icon size Current theme If a user style sheet for one of the MusE color schemes exists and is selected, it will be merged with the default style sheet for this scheme. The user style sheet takes precedence and overwrites identical settings in the default style sheet. Cascade default and user theme style sheets Custom cursor size Show icons in menus Use native standard dialogs Distribute current system font family to all other fonts System Font sizes for fonts 1-6 are determined at every program start, to optimally match the currently used system font. Disable if you want to set fixed sizes in the font settings below. Default scaling factors: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% Calculate and apply relative font sizes dynamically at program start Maximum aliased size, 0 = no alias Insert Insérer Font 4 Police 4 Font 5 Police 5 clear nettoyer MusE: Appearance settings MusE: Réglages d'apparence add ajouter remove enlever Palette Palette add to palette ajouter à la palette B B S S H H V V G G R R Color name: Nom de couleur: Global opacity Opacité globale Show names Show e&vents Show Ca&kewalk style RMS/PEAK (&traditional) Add Ajouter Remove Supprimer Clear Nettoyer Show snap grid Choose color... Add to palette Save all... Load all... Themes Thèmes Help Style Sheet: Feuille de style: Change Changer Font 6 Police 6 Size (pt) At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. ArrangerColumnsBase Configure arranger columns Configurer les colonnes de l'arrangeur Columns: Colonnes: Name: Nom: Controller type: Type de contrôleur: Midi controller type Type de contrôleur midi Affect CCs at Control7 Control7 Control14 Control14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Ton Program Programme H-Ctrl H-Ctrl Midi controller number high byte Contrôleur midi - numéro de byte haut L-Ctrl L-Ctrl Midi controller number low byte Contrôleur midi - numéro de byte bas * wild card * affect CCs at affecte les CCs à begin of song début du morceau current position position courante &Add &Ajouter &Delete &Supprimer Done Fait AudioConverterSettingsBase Audio Converter Settings Available audio converters Offline settings... Realtime settings... Gui settings... Use default preferences (see Global Settings) Preferences When only samplerate conversion is required, use: When time stretching or pitch shifting is required, use: OK OK Cancel Annuler Awl::MidiVolEntry off off db db Awl::VolEntry off off ChooseSysexBase Dialog Dialogue Hex: Comment: &OK &Cancel ClipListEditorBase MusE: ClipList MusE: Liste de Clips Name Nom Refs Réfs Samplerate Fréquence d'échantillonnage Len Long Data Donnée Status Clip Properties Propriétés du Clip Pos: Pos: Len: Long: CommentBase Form1 Form1 Track Comment Commentaire pour la Piste Track 1 Piste 1 ConfigMidiFileBase MusE: Config Midi File Import/Export MusE: Configure l'Importation/Exportation des fichiers MIDI Import: Importer: Split tracks into &parts Diviser les pistes en &parties Alt+P Alt+P Split tracks into parts, or one single part Diviser les pistes en parties, ou une seule partie Import Default instrument: Instrument par défaut: Device Name metas trump Port metas if both exist Le Nom de Périphérique métas prend le dessus sur le Port métas si les deux existent Instrument Name metas trump Mode sysexes if both exist Le Nom d'Instrument métas prend le dessus sur le Mode sysexes si les deux existent Use new-style drum tracks Utiliser le nouveau style de piste batterie Use old-style drum tracks Utiliser l'ancien style de piste de batterie Export: Exporter: 96 96 192 192 384 384 Enable extended smf format (currently not implemented) Active le format smf étendu (pas encore implémenté) Running Status saves space by not repeating event status bytes. If this is off every event will have a status byte, increasing file size. Use Running &Status To turn a note off some devices can use note-offs or zero-velocity note-ons or both. When used with Running Status this setting saves space. It is safe to leave this off. Replace note-offs with &zero velocity note-ons Use &2-byte time signatures instead of standard 4 Utiliser la signature temporelle &2-byte au lieu du 4 standard Alt+2 Alt+2 If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum note controllers. If disabled, drum maps are ignored during export. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. See "What's This ?" for more info. If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum controllers. Any Port overrides cause a separate track to be created (requires format 1). Any Channel overrides will still appear in the same track in the exported file, but upon re-import into an app such as MusE, MusE will treat that as a new track and create one. If disabled, drum maps are ignored. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. Upon re-importing into another app, it is up to the other app to redirect the particular drum ports, channels and notes to make the song sound like it was intended. Drum map overrides are unique to MusE. If re-importing into MusE, for faithful reproduction disable this setting and apply a drum map and some overrides later. If re-importing or opening in another midi player, enable this setting so that the player can have a chance at faithful reproduction. Some user adjustment may be required (choice of instruments, ports, etc). Apply drum map Port, Channel, ANote overrides If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. See "What's This ?" for more info. If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. The extra track is automatically created. In this mode all events on a track in the midi file are on the same channel. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. In this mode the track events can be on different channels. But if the file is re-imported into an app such as MusE, MusE will automatically create the extra track. Drum map Channel overrides go to a separate track Export instrument or mode: Mode sysexes Mode sysexes Instrument name metas Nom d'instrument métas Both Les Deux Port metas Port métas Device name metas Nom de périphérique métas Export a Port/Device meta for format 0 Exporte un Port/Périphérique méta pour le format 0 Copyright: Copyright: Format: Format: Note: Format 0 uses the FIRST midi track's name/comment in the arranger Note: Le format 0 utilise le PREMIER nom/commentaire de la piste midi dans l'arrangeur Division: Division: Save space by replacing note-offs with &zero velocity note-ons Economise de l'espace en remplaçant les note-offs par des note-ons de vélocité &zéro Alt+Z Alt+Z 0 (single track) 0 (piste seule) Export 1 (multiple tracks) 1 (pistes multiples) Export port or device metas: &OK &OK &Cancel &Annuler CopyOnWriteDialogBase Copy Wave Files Copier les Fichiers Wave Some sound files will be copied to the Project Directory, either because they are not writable or because more than one independent Wave Event shares them. (If you prefer instead that the Wave Events are inter-dependent, try using Clone Parts.) Multiple copies will be made in some cases. If no Project has been created yet, you will be asked to, giving another chance to cancel. Certains fichiers son seront copiés dans le répertoire projet, soit parce qu'ils non pas le droit d'écriture ou parce que plus d'un évènement Wave indépendant les partages. (Si vous préférez qu'à la place les Evènements Wave soient inter-dépendents, essayez d'utiliser le clonage de parties.) Plusieurs copies seront effectuées dans certains cas. Si aucun projet n'a encore été créé, il vous sera donné une autre chance d'abandonner. These files will be copied to the Project Directory: Ces fichiers seront copiés dans le répertoire projet: CrescendoBase MusE: Crescendo/Decrescendo MusE: Crescendo/Decrescendo Range Plage Looped Events Evènements bouclés &Looped Events Selected Looped Sélectionné bouclé Parts Parties Selected parts or c&urrent part All parts &in editor Values Valeurs Start velocity Vélocité de départ % % End velocity Vélocité de fin &Absolute Rela&tive Absolute Absolue Relative Relative OK OK Cancel Annuler DeicsOnzeGui Save configuration Sauvegarder la configuration Critical Error Erreur critique Cannot open file %1 Ne peut pas ouvrir le fichier %1 Parsing error for file %1 Erreur d'analyse pour le fichier %1 Load category dialog Dialogue de chargement de catégorie Load set dialog Dialogue de chargement d'ensemble Save set dialog Dialogue de sauvegarde d'ensemble New category Nouvelle catégorie Delete category Supprime la catégorie Load category Charge la catégorie Save category Sauvegarde la catégorie Load set Charge l'ensemble Save set Sauve l'ensemble Delete set Supprime l'ensemble New subcategory Nouvelle sous-catégorie Delete subcategory Supprime la sous-catégorie Load subcategory Charge la sous-catégorie Save subcategory Sauvegarde la sous-catégorie New preset Nouveau pré-réglage Delete preset Supprime le pré-réglage Load preset Charge le pré-réglage Save preset Sauvegarde le pré-réglage No more category supported Plus de catégorie supportée You can not add more categories Vous ne pouvez pas ajouter plus de catégories Do you really want to delete %1 ? Voulez-vous réellement supprimer %1 ? &Yes &Oui &No &Non No category selected Pas de catégorie sélectionnée You must first select a category. Vous devez d'abord sélectionner une catégorie. Replace or add Remplace ou ajoute %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 est supposé être affecté à la hbank numéro %2, mais il y a en a déjà une dans cet emplacement. Voulez-vous le remplacer ou l'ajouter dans le prochain emplacement libre ? &Replace &Remplacer &Add &Ajouter Download error Erreur de téléchargement There is no more free category slot. Il n'y a plus d'emplacement de catégorie libre. Save category dialog Dialogue de sauvegarde de catégorie No more subcategory supported Plus de sous-catégorie supportée You can not add more subcategories Vous ne pouvez pas ajouter plus de sous-catégories No subcategory selected Pas de sous-catégorie sélectionnée You must first select a subcategory. Vous devez d'abord sélectionner une sous-catégorie. Load subcategory dialog Dialogue de chargement de sous-catégorie %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 est supposé être affecté à la lbank numéro %2, mais il y a en a déjà une dans cet emplacement. Voulez-vous le remplacer ou l'ajouter dans le prochain emplacement libre ? There is no more free subcategory slot. Il n'y a plus d'emplacement libre pour une sous-catégorie. Save subcategory dialog Dialogue de sauvegarde de sous-catégorie No more preset supported Plus de pré-réglage supporté You can not add more presets Vous ne pouvez pas ajouter plus de pré-réglages No preset selected Pas de pré-réglage sélectionné You must first select a preset. Vous devez d'abord sélectionner un pré-réglage. Load preset dialog Dialogue de chargement de pré-réglage %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 est supposé être affecté au prog numéro %2, mais il y a en a déjà un dans cet emplacement. Voulez-vous le remplacer ou l'ajouter dans le prochain emplacement libre ? There is no more free preset slot. Il n'y a plus d'emplacement libre pour un pré-réglage. Save preset dialog Dialogue de sauvegarde de pré-réglage Browse set dialog Dialogue de parcours d'ensemble Browse image dialog Dialogue de parcours d'image DeicsOnzeGuiBase DeicsOnze DeicsOnze &Preset &Pré-réglage Program numerous Program numéro INITVOICE INITVOICE LBank LBank Subcategory Sous-catégorie Bank numerous Banque numéro NONE AUCUN HBank HBank Category Catégorie Prog Prog Preset Pré-réglage DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Publié sous licence GPL. &Global &Globale Pitch Envelope Enveloppe de ton PL3 PL3 PL2 PL2 PL1 PL1 PR1 PR1 PR2 PR2 PR3 PR3 Pitch modulation depth Profondeur de modulation du ton LFO LFO LFO Sync Synchro LFO Pitch modulation sensitivity Sensibilité de modulation du ton Pitch Modulation Sensitivity Sensibilité de Modulation du Ton LFO Delay Délai LFO LFO delay Délai LFO LFO speed Vitesse LFO Amplitude modulation depth Profondeur de modulation d'amplitude Amplitude modulation sensitivity Sensibilité de modulation d'amplitude Amplitude Modulation Sensitivity Sensibilité de modulation d'amplitude AMS AMS LFO Waveform Forme d'onde LFO Pitch Modulation Depth Profondeur de Modulation du Ton PMD PMD LFO Speed Vitesse LFO AMD AMD Speed Vitesse Delay Délai PMS PMS Modulation Matrix Matrice de Modulation <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> <b>Algorithme 1</b> : <i>Op 1</i> modulé par <i>Op 2</i> modulé par <i>Op 3</i> modulé par <i>Op 4</i><br> <b>Algorithme 2</b> : <i>Op 1</i> modulé par <i>Op 2</i> modulé par <i>Op 3</i> et <i>Op 4</i><br> <b>Algorithme 3</b> : <i>Op 1</i> modulé par <i>Op 4</i> et <i>Op 2</i> modulé par <i>Op 3</i><br> <b>Algorithme 4</b> : <i>Op 1</i> modulé par <i>Op 2</i> et <i>Op 3</i> modulé par <i>Op 4</i><br> <b>Algorithme 5</b> : (<i>Op 1</i> modulé par <i>Op 2</i>) ajouté à (<i>Op 3</i> modulé par <i>Op 4</i>) <br> <b>Algorithme 6</b> : addition des trois <i>Op 1, 2, 3</i> tous modulés par <i>Op 4</i><br> <b>Algorithme 7</b> : addition des trois <i>Op 1, 2, 3</i> avec <i>Op 3</i> modulé par <i>Op 4</i><br> <b>Algorithme 8</b> : addition des quatres <i>Op 1, 2, 3, 4</i> Op4 Feedback Feedback Op4 Feedback level of the operator 4 Niveau de feedback de l'opérateur 4 Transpose Transposition Op &1 Op &1 Scaling 1 Mise à l'échelle 1 LS1 LS1 RS1 RS1 Rate Scaling Mise à l'échelle de la vitesse Attack Rate of the operator 1 Vitesse d'attaque de l'opérateur 1 Level Scaling Mise à l'échelle du niveau Amplitude Envelope 1 Amplitude de l'Enveloppe 1 RR1 RR1 D1R1 D1R1 D1L1 D1L1 D2R1 D2R1 Release Rate Vitesse de relachement 2° Decay Rate 2nd Vitesse de décroissance 1° Decay Level 1er niveau de décroissance 1° Decay Rate 1ère Vitesse de décroissance Attack Rate Vitesse d'Attaque AR1 AR1 Detune, OSCWave, EGShift 1 Désaccordage, OSCWave, EGShift 1 DET1 DET1 Detune Désaccordage EG Shift Décalage EG 96dB 96dB 48dB 48dB 24dB 24dB 12dB 12dB Wave form Forme d'onde Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Forme d'onde 1 = <i>sin(<b>t</b>)</i><br> Forme d'onde 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Forme d'onde 3 = <i>si <b>t</b>&#060 pi alors sin(<b>t</b>) sinon 0</i><br> Forme d'onde 4 = <i>si <b>t</b>&#060 pi alors sin(<b>t</b>)*abs(sin(<b>t</b>)) sinon 0</i><br> Forme d'onde 5 = <i>si <b>t</b>&#060 pi alors sin(2*<b>t</b>) sinon 0</i><br> Forme d'onde 6 = <i>si <b>t</b>&#060 pi alors sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) sinon 0</i><br> Forme d'onde 7 = <i>si <b>t</b>&#060 pi alors abs(sin(2*<b>t</b>)) sinon 0</i><br> Forme d'onde 8 = <i>si <b>t</b>&#060 pi alors sin(2*<b>t</b>)*sin(2*<b>t</b>) sinon 0</i> Sensitivity 1 Sensibilité 1 KVS1 KVS1 Amplitude Modulation Enable Activation de la Modulation d'Amplitude AME1 AME1 Keyboard Velocity Sensitivity Sensibilité de Vélocité du Clavier Key Velocity Sensitivity Sensibilité de Vélocité d'une Touche EBS1 EBS1 EG Bias Sensitivity Sensibilité du Biais EG Eg Bias Sensitivity Sensibilité du Biais Eg Frequency 1 Fréquence 1 Coarse 1 Grossier 1 Coarse Ratio Ratio Grossier Fine 1 Fin 1 Fine Ratio Ratio Fin Freq 1 Fréq 1 Fixed Frequency Fréquence fixée Toggle Fix Frequency Basculer en fréquence fixe FIX FIX OUT 1 SORTIE 1 Output Volume Volume de sortie Volume Volume Op &2 Op &2 Amplitude Envelope 2 Amplitude de l'Enveloppe 2 D1R2 D1R2 D1L2 D1L2 D2R2 D2R2 RR2 RR2 AR2 AR2 Frequency 2 Fréquence 2 Coarse 2 Grossier 2 Fine 2 Fin 2 Freq 2 Fréq 2 Scaling 2 Mise à l'échelle 2 LS2 LS2 RS2 RS2 OUT 2 SORTIE 2 Detune, OSCWave, EGShift 2 Désaccorde, OSCWave, EGShift 2 DET2 DET2 Sensitivity 2 Sensibilité 2 EBS2 EBS2 KVS2 KVS2 AME2 AME2 Op &3 Op &3 Amplitude Envelope 3 Amplitude de l'Enveloppe 3 D1R3 D1R3 D1L3 D1L3 D2R3 D2R3 RR3 RR3 AR3 AR3 Scaling 3 Mise à l'échelle 3 LS3 LS3 RS3 RS3 OUT 3 SORTIE 3 Frequency 3 Fréquence 3 Coarse 3 Grossier 3 Fine 3 Fin 3 Freq 3 Fréq 3 Detune, OSCWave, EGShift 3 Désaccorde, OSCWave, EGShift 3 DET3 DET3 Sensitivity 3 Sensibilité 3 EBS3 EBS3 KVS3 KVS3 AME3 AME3 Op &4 Op &4 amplitude Envelope 4 Amplitude de l'Enveloppe 4 AR4 AR4 D1R4 D1R4 D1L4 D1L4 D2R4 D2R4 RR4 RR4 Frequency 4 Fréquence 4 Coarse 4 Grossier 4 Fine 4 Fin 4 Freq 4 Fréq 4 Scaling 4 Mise à l'échelle 4 LS4 LS4 RS4 RS4 OUT 4 SORTIE 4 Detune, OSCWave, EGShift 4 Désaccorde, OSCWave, EGShift 4 DET4 DET4 Sensitivity 4 Sensibilité 4 EBS4 EBS4 KVS4 KVS4 AME4 AME4 &Func &Fonc Delay Pan Depth Retard de profondeur du panoramique Delay Pan LFO Freq Retard de la Fréq LFO du panoramique Delay Ch Send Level Retard du niveau d'envoie du canal Channel Chorus Canal de chorus Delay Feedback Retard de feedback Delay On/Off, Return Level Retard On/Off, Niveau de Retour On On Delay Beat Ratio Retard du facteur de pulsation Delay BPM Retard BPM Foot Control Pédale de Contrôle Pitch Bend Range Plage de Bend du Ton Pitch Ton Amplitude Amplitude Modulation Wheel Roue de modulation Breath Control Contrôle de respiration Pitch Bias Biais du ton Envelope Bias Biais de l'enveloppe After Touch Pression (After Touch) Phony Mode Mode bidon POLY POLY MONO MONO Potamento Portamento Portamento Mode Mode Portamento FINGER DOIGT FULL PLEIN PT PT Portamento Time Instant de Portamento C&horus C&horus Chorus Parameters Paramètres de chorus Channel send level Niveau d'envoi du canal On/Off and Return level On/Off et Niveau de retour Select LADSPA plugin Sélectionner le greffon LADSPA Change plugin Changer le greffon &Reverb &Réverb Reverb Parameters Paramétres de réverbération &Config &Configuration Font Size Taille de police Quality Qualité High Haute Middle Moyenne Low Basse Ultra low Ultra basse Filter Filtre Save Mode (into the song) Sauvegarder le mode (dans la chanson) Save only the used presets Sauvegarder uniquement les pré-réglages utilisés Save the entire set Sauvegarder l'ensemble complet Save the configuration Sauvegarder la configuration Configuration File Fichier de Configuration Save... Sauvegarder... Load... Charger... Save as default Sauvegarder par défaut Colors Couleurs Text Texte Background Fond Edit Text Editer le Texte Edit Background Editer le Fond Red Rouge Blue Bleu Green Vert Set Path Régler le chemin Image in the background : Image de fond : Browse... Parcourir... Load the set at the initialization : Charger l'ensemble à l'initialisation : Set Brightness, Detune, Attack and Release of the current channel to default Régler la Luminosité, le Désaccordage, l'Attaque et le Relâchement du canal courant aux valeurs par défaut Res. Ctrl Rés. Ctrl Cut all notes off Couper toutes les notes à off Panic! Panique! Number of Voices Nombre de Voix Number of voices Nombre de voix Enable Activer Channel Canal Vol Vol Channel Ctrl Canal Ctrl Release Relâchement Attack Attaque Brightness Luminosité Modulation Modulation Pan Pan DelOverlapsBase MusE: Delete Overlaps MusE: Supprimer les chevauchements Range Plage All Events Tous les Evènements Selected Events Evènements sélectionnés Looped Events Evènements bouclés Selected Looped Sélectionné bouclé Parts Parties Selected parts or c&urrent part All parts &in editor OK OK Cancel Annuler DidYouKnow Did you know? Le saviez vous? Don't show on startup Ne pas afficher au démarrage Next tip Truc suivant Close Fermer DuplicateTracksBase Duplicate tracks Dupliquer les pistes Number of copies Nombre de copies Copy all routes Copier toutes les routes Default routing Routage par défaut No routes Pas de routes Duplicate all parts Make independent copies of all parts Make clones of all parts No parts Copy parts Copier des parties Copy drumlist Copier des listes de batterie Copy standard (vol, pan) and synth controllers Copy standard controllers (vol, pan) Copier les controlleurs standards (vol, pan) Copy effects rack plugins Copier les racks de gréffons d'effets Copy plugin controllers Copier les contrôleurs de gréffon Ok Ok Cancel Annuler EditCtrlBase MusE: Edit Controller Event MusE: Editer les évènements du contrôleur Time Position Position Temporelle Available Controller: Contrôleur Disponible: Create New Controller Créer un Nouveau Contrôleur textLabel3 LabelText3 Value Valeur Controller Contrôleur Note Note H-Bank H-Bank L-Bank L-Bank Program Programme off off pushButton4 BoutonPoussoir4 &OK &OK &Cancel &Annuler EditGainBase MusE: Modify gain MusE: Modifier le gain Gain Gain 200% 200% 100% 100% 0% 0% &Reset &Réinitialiser Alt+R Alt+R &Apply &Appliquer Alt+A Alt+A &Cancel A&nnuler Alt+C Alt+N EditInstrumentBase MusE: Instrument Editor MusE: Editeur d'Instrument Instrument Name: Nom de l'Instrument: Selected instrument name. Nom d'instrument sélectionné. List of defined instruments. Liste des instruments définis. Note off mode: Note off mode Selects how to handle note off events. The instrument can use note off events, or not at all, or convert them to zero-velocity note on events which helps save midi interface bandwidth. Refer to the instrument manufacturer's midi implementation chart for details. Pa&tches Pa&tches List of groups and patches. Liste des groupes et des patches. Group/Patch Groupe/Patch Properties Propriétés Name: Nom: Group or patch name Groupe ou nom de patch High Bank: Banque High: Patch high bank number Numéro de patch de la banque high Patch high bank number. --- means don't care. Numéro de patch de la banque high. --- le sens importe peu. --- dont care --- Low Bank: Banque Low: Patch low bank number Numéro de patch de la banque low Patch low bank number. --- means don't care. Numéro de patch de la banque low. --- le sens importe peu. --- --- Program: Programme: Patch program number Numéro de programme de patch Drum patch Patch de batterie If set, the patch is available only for drum channels. Si activé, le patch n'est disponible que pour les canaux de batterie. Drum Batterie GM patch Patch GM If set, the patch is available in a 'GM' or 'NO' midi song type. Si activé, le patch est accessible dans une chanson midi de type 'GM' ou 'NO'. GM GM GS patch Patch GS If set, the patch is available in a 'GS' or 'NO' midi song type. Si activé, le patch est accessible dans une chanson midi de type 'GS' ou 'NO'. GS GS XG patch Patch XG If set, the patch is available in an 'XG' or 'NO' midi song type. Si activé, le patch est accessible pour les chansons midi de type 'XG' ou 'NO'. XG XG Delete group or patch Supprimer un groupe ou un patch &Delete &Supprimer Alt+D Alt+S New patch Nouveau patch New &Patch Nouveau &Patch Alt+P Alt+P New group Nouveau groupe New &Group Nouveau &Groupe Alt+G Alt+G &Initialization Instrument initialization sequence: &Add... &Change... &Close &Fermer Close Fermer Contro&ller Contrô&leur Common: Commun: This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. Ceci est une liste de contrôleurs communément utilisés. Notez que dans MusE le ton et les chamgements de programme sont gérés comme des contrôleurs normaux. List of defined controllers Liste de contrôleurs définis List of defined controllers. Liste de contrôleurs définis. Name Nom Type Type H-Ctrl H-Ctrl L-Ctrl L-Ctrl Min Min Max Max Def Déf Midi controller name Nom de contrôle midi Type: Type: Midi controller type Type de contrôle midi Control7 Control7 Control14 Control8 RPN RPN NRPN NRPN RPN14 RPM14 NRPN14 NRPN14 Pitch Ton Program Programme Midi controller number high byte Numéro de byte haut du contrôleur midi Midi controller number low byte (* means drum controller) Numéro de byte bas du contrôleur midi (* signifie contrôleur de batterie) Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and GS/XG type songs and instruments. Allows controllers for each instrument in Muse's drum map. The low byte will be replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. Numéro de byte bas du contrôleur midi. Si le byte bas est * alors le contrôleur est un 'contrôleur de batterie'. Pour des pistes de batterie et de type chanson GS/XG et instruments. Autoriser les contrôleurs pour chaque instrument dans la carte de batterie de Muse. Le byte bas sera remplacé par le 'ANote' dans la carte de batterie. Exemples: Les instruments GS et XG Contrôleurs de batterie. * wild card * Range: Plage: Min Min Minimum value. If negative, auto-translate. Valeur minimum. Si négatif, traduit automatiquement. Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. Useful for controllers which should be displayed with zero bias. For example, 'Pan': Minimum: -64 Maximum: 63 True range: Min: 0 Max: 127 (bias = 64) 'CoarseTuning': Min: -24 Max: 23 True range: Min: 40 Max: 87 (bias = 64) Bias is determined from controller type: 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 Type 'Pitch' is the exception. It is biased at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) Valeur minimum. Si la valeur minimum est négative, la plage sera automatiquement traduite en une plage positive. Utile pour les contrôleurs qui devraient être affiché avec un biais nul. Par exemple, 'Pan': Minimum: -64 Maximum: 63 Vraie plage: Min: 0 Max: 127 (biais = 64) 'Réglage grossier': Min: -24 Max: 23 Vraie plage: Min: 40 Max: 87 (biais = 64) Le biais est déterminé à partir du type de contrôleur: 7-bit Controller7 / RPN: Biais = 64 14-bit Controller14 / RPN14: Biais = 8192 Le type 'Ton' est l'exception. Il est biaisé à zéro, même avec une valeur minimum négative: 'Ton': Min: -8192 Max: 8191 Vraie plage: Min: -8192 Max: 8191 (biais 0) Max Max Maximum value Valeur maximum Default: Défaut: L-Bank L-Bank off off Progr. Progr. ??? ??? H-Bank H-Bank Drum&maps Cartes de &batterie Default value. Off: No default. Valeur par défaut. Off: Pas de valeur par défaut. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). Valeur (initiale) par défaut. Off signifie par de valeur par défaut. Si une valeur par défaut est choisie, la valeur sera envoyée au contrôleur quand le contrôleur est ajouté à la chanson (dans la vue piano ou dans l'éditeur d'évènements). Quand la chanson est rechargée, la valeur est envoyée à nouveau. Autrement, le contrôleur reste à sa dernière valeur. Les contrôleurs sont aussi automatiquement ajoutés à une chanson à la réception d'un évènement d'un contrôleur midi. Attention! Faites attention au contrôleurs tels que 'Sustain' et 'ResetAllController' avec des valeurs par défaut. Vous devriez probablement mettre sur 'off' leur valeur par défaut (dans la vue piano ou dans l'éditeur de batterie, et dans l'éditeur d'instrument). off dont care off Add common controller Ajout d'un contrôleur commun &Add &Ajouter Alt+A Alt+A Delete controller Supprimer le contrôleur Create a new controller Créer un nouveau contrôleur New &Controller Nouveau &Contrôleur Alt+C Alt+C Null Param Hi: Paramètre nul Hi: Null parameter number High byte Numéro de byte haut du paramètre nul If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events from corrupting the RPN/NRPN controller. Typically, set to 127/127, or an unused RPN/NRPN controller number. Si activé, ces numéros de paramètres 'null' seront envoyés après chaque évènement RPN/NRPN. Cela prévient les évènements 'data' qui suivent de corrompre le contrôleur RPN/NRPN. Typiquement, mettre sur 127/127, ou sur un numéro de contrôleur RPN/NRPN non utilisé. Lo: Lo: Null parameter number Low byte Numéro de byte bas du paramètre nul S&ysEx S&ysEx SysEx List: Liste SysEx: Hex Entry: Entrée Hexa: New SysE&x Nouveau SysE&x Alt+X Alt+X Drummaps Cartes de batterie Patch Collections: Collection de patches: &Copy &Copier &Remove Supp&rimer &Up Ha&ut &Down &Bas Patch: Patch: from de to à Bank Hi: Banque Hi: Bank Lo: Banque Lo: Contro&llers Contrô&leurs Null Parameters: Hi: Paramètres Null: Hi: Midi Midi &Add Common... &Ajouter Commun... W W Show in tracks: Afficher dans les pistes: Patch program number. --- means don't care. &Drum High Bank Low Bank Patch program number. --- means don't care. Patch Name: Dr def Drum default: Comment: Commentaire: Note: The enclosing F0 ... F7 are not required Tools Outils &File &Fichier &Help &Aide &New &Nouveau New Nouveau Ctrl+N Ctrl+N &Open... &Ouvrir... Open Ouvrir Ctrl+O Ctrl+O &Save &Sauvegarder Save Sauvegarder Ctrl+S Ctrl+S Save &As... Sauvegarder &sous... Save As Sauvegarder sous Info Info Additional information E&xit &Quitter Exit Quitter new item nouvel élément What's this? Qu'est-ce que c'est? EditNoteDialogBase MusE: Enter Note MusE: Entrer la Note OK OK Cancel Annuler Length: Longueur: Time Position: Position Temporelle: Pitch: Ton: Velocity On: Vélocité On: Velocity Off: Vélocité Off: EditSysexDialogBase MusE: Enter SysEx MusE: Entrer le Sysex TimePosition: Position Temporelle: Hex: Note: The enclosing F0 ... F7 are not required Name: Nom: Comment: Commentaire: &Select... &OK &Cancel OK OK Cancel Annuler FLUIDGui FLUID: open Soundfile FLUID: ouvrir un fichier son FLUIDGuiBase Form1 Form1 Soundfont Soundfont Load Charger FLUIDSynthGuiBase FLUID Synth FLUID Synth Gain Gain Room Size Dimension de la Chambre Damping Atténuation Level Niveau Dump Info Afficher Info ID ID Fontname Nom de police Chnl Canal Drum Chnl Canal Batterie Width Largeur CHANNEL SETUP REGLAGE DE CANAL Speed Vitesse Depth Profondeur LOADED SOUNDFONTS SOUNDFONTS CHARGEES Number Nombre Type Type Sine Sinusoïde Triangle Triangle Reverb Réverb Chorus Chorus Load Charger Delete Effacer Soundfont Soundfont FileDialogButtons Home Global Global User Utilisateur Project Projet read Midi Port Configuration Lire le port de configuration Midi write window states écrire les états de la fenêtre fdialogbuttons fdialogbuttons FluidSynthGui Choose soundfont Choisir la soundfont GateTimeBase Range Plage All Events Tous les Evènements Selected Events Evènements sélectionnés Looped Events Evènements bouclés MusE: Modify Note Length MusE: Modifier la Longueur de Note Selected Looped Sélectionné Bouclé Parts Parties Selected parts or c&urrent part All parts &in editor Values Valeurs Rate: Vitesse: Offset: Décalage: % % lenNew = (lenOld * rate) + offset NouvLong = (AncLong * vitesse) + décalage OK OK Cancel Annuler GlobalSettingsDialogBase MusE: Global Settings MusE: Paramètres Globaux Audio Audio Mixer Mixeur dB dB min. Meter Value Valeur min de la jauge Midi Midi Ticks Coches 1024 1024 2048 2048 4096 4096 Displayed Resolution (Ticks/Quarternote) Résolution affichée (Coches par 1/4 de note) 48 48 Start MusE Start with &last song Start with &template Sta&rt with song On launch Show splash screen Show "Did you know?" dialog 96 96 192 192 384 384 768 768 1536 1536 3072 3072 6144 6144 12288 12288 RTC Resolution (Ticks/Sec) Résolution RTC (Coches par sec.) /sec /sec GUI Refresh Rate Vitesse de rafraîchissement de l'interface Warn if timer frequency is inadequate Avertir si la fréquence du timer n'est pas adaptée Track height Hauteur de piste Use project save dialog Utiliser le dialogue de sauvegarde de projet Some popup menus stay open (else hold Ctrl) Certains menus contextuels restent ouvert (autrement appuyer sur Ctrl) Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. Autorise certains menus contextuels à rester ouvert. Autrement, appuyer sur Ctrl pour les laisser ouvert. Editors Open editors in tabs (else in separate windows) Piano roll Vue piano Drum editor Editeur de batterie Wave editor Editeur de forme d'onde Score editor Editeur de partition Master track editor Editeur de piste principale DSSI VST Directories where dssi-vst (if installed) looks for Wine vst plugins (*.dll files) Midi controller behaviour Send Null parameters after an (N)RPN value If set, 'null' parameter numbers will be sent after each RPN/NRPN event. (A 'null' parameter number is 127.) This prevents subsequent rogue or accidental 'data' events (without parameters) from corrupting the current RPN/NRPN controller. Normally it is OK to leave this unset, but use it if there is a possibility of such 'data' events. If this is set, the 'Optimize controllers' setting does not affect these controllers' parameters, only the H/L values. Send Null parameters Don't send redundant parameters or values If set, redundant H/L parameters or H/L values are not sent. They are sent only if they changed. This can save midi interface bandwidth. But it is possible the receiving device might miss the previous changes (for example if the device is turned on after they were sent). The 'Panic' button will force them to be resent. Certain operations will also force them to be resent, such as clicking a GUI midi control (but not while adjusting it). Optimize controllers Record drum tracks Record a&ll instruments Latency Whether to enable latency correction/compensation: Enable latency correction/compensation Project latency Whether completely independent branches share a common project-wide latency: Project-wide latency Unterminated branches Whether unterminated branches participate in latency correction/compensation: Unterminated input branches Unterminated output branches Track monitoring Whether track monitor buttons affect latency correction/compensation: Monitoring affects latency Revert native GUI scaling for plugins in HiDPI Live update wave parts while recording Whether to show knob and slider values in certain places, for example mixer strips. Turn off to reduce clutter. Revert native UI window HiDPI scaling (some plugins ignore this setting) Show knob and slider values Whether to show knobs or sliders in certain places, for example mixer strips Prefer midi volume as decibels instead of 0-127 Style hack: Force line edit widgets to draw a frame Prefer knobs instead of sliders GUI refresh rate Use first available Ask once Ask always In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. Dans certaines zones, le bouton du milieu décrémente la valeur, tandis que le bouton de droite l'incrémente. Les utilisateur sans un bouton du milieu peuvent sélectionner cette option pour rendre le comportement du bouton de gauche identique au bouton droit dans ces zones. Use left mouse button for decreasing values Utilisez le bouton de gauche pour décrémenter les valeurs Shift + Right click sets left range marker Shift + clic droit règle le marqueur gauche de la plage Allow adding hidden tracks in track list menu Autorise l'ajout de pistes cachées dans le menu liste des pistes Unhide tracks when adding hidden tracks Découvre les pistes quand on ajoute des pistes cachées Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. This hack forces a frame to be drawn, but may interfere with other styles. Use old-style stop shortcut Monitor on record-arm automatically Show note names on notes in pianoroll Smart focus Focus intelligent After editing, controls will return focus to their respective canvas Après édition, les contrôles rendront le focus à leur canvas respectifs Show newly created midi velocity graphs per-note Afficher les graphes nouvellement créés de vélocité midi par note px px Keep transport window on top (requires restart) Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. Active la souris sans bords. Pour certaines fonctions comme le zoom/panoramique. Désactive l'utilisation d'une méthode alternative standard. Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. Active la souris sans bords. Pour certaines fonctions comme le zoom. Désactive l'utilisation d'une méthode alternative standard. Borderless zoom/pan mouse (else use alternate method) Souris zoom/panoramique sans bord (sinon utilise une méthode alternative) Scrollable submenus Whether record-arming a track automatically activates monitoring. Drum tracks Pistes de batterie Only offer old-style drumtracks Propose seulement les pistes de batterie ancien style Only offer new-style drumtracks Propose seulement les pistes de batterie nouveau style Prefer old-style drumtracks Préfère les pistes de batterie ancien style Prefer new-style drumtracks Préfère les pistes de batterie nouveau style GUI Style Style du GUI MDI-subwindowness and sharing menus Sous-fenêtrage MDI et menus partagés Presets: Pré-réglages: traditional MusE SDI MusE traditionnel SDI Cakewalk-like MDI Similaire à Cakewalk MDI Borland-/Mac-like MDI Similaire à Borland/Mac MDI &Apply &Appliquer &Ok &Ok &Cancel &Annuler Application Application Views Vues y-pos y-pos show afficher x-pos x-pos height hauteur width largeur Mixer A Mixeur A Mixer B Mixeur B Big Time Grosse Horloge Transport Transport set current values Régler les valeurs courantes Cur Cour Start Muse Démarrer MusE show splash screen Afficher l'écran de démarrage show "Did you know?" dialog Afficher le dialogue "Le saviez-vous?" Start song Démarrer la chanson start with last song Démarrer avec la dernière chanson start with song Démarrer avec la chanson min. Slider Val Val min. de l'ascenceur Enable denormal protection Activer la protection contre la dénormalisation Enable output limiter Activer la limitation de sortie External Waveditor Editeur Wave Externe External Waveditor command Command pour l'Editeur Wave Externe Note: External editor opened from the internal editor. Note: Editeur externe ouvert à partir de l'éditeur interne. Dummy Audio Driver (settings require restart) Pilote Audio de Base (les réglages nécessient un redémarrage) Hz Hz Period size (Frames per period): Taille de la période (Trames par période): 16 16 Project directory Répertoire project Projects: Projets: ... ... Main Window Fenêtre Principale Choose start song or template Choisir une chanson de départ ou un modèle Reset to default Revenir aux valeurs par défaut start with template Démarrer avec un modèle Start template or song: Modèle ou chanson de démarrage: Read MIDI Ports configuration from file, or else automatically configure Lire la configuration des Ports MIDI à partir d'un fichier, ou alors configure automatiquement Read MIDI Ports configuration Lire la configuration des Ports MIDI Warn if opening file versions different than current version Auto save (every 5 minutes if not playing/recording) Sauvegarde automatique (toute les 5 minutes si pas de lecture/enregistrement) Plugin Paths Ordered by priority. Changes take effect on restart. The plugin cache is used for safety and speed. It automatically rescans when any of the given directories change, for example by installing a new plugin. Click this button to force the cache to recreate upon next restart. &Force plugin rescan and cache recreation on next restart LADSPA DSSI Linux VST Directories containing Linux vst plugins (*.so files) LV2 LV2 NOTE: Ensure the directory containing core LV2 bundles is included (typically /usr/lib/lv2). Otherwise all plugins may not load properly. Move up Move down Add... Edit... Remove Supprimer Audio driver settings (require restart) Sample rate: Audio backend: Shorter periods give better midi playback resolution. Note that this cannot be set with Jack devices! Period size (frames per period): Note that samplerate cannot be set with Jack devices! Minimum meter value Minimum slider value Try to use Jack Freewheel Essaye d'utiliser Jack FreeWheel Speeds bounce operations Accélère la vitesse des opérations de rebondissement (bounce) Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Utiliser le mode Jack FreeWheel si possible. Ceci accélère de façon importante la vitesse des opérations de rebondissement (bounce). VST in-place VST sur place Enable VST in-place processing (restart required) Active le traitement des VST sur place (redémarrage requis) Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. Active le traitement des VST sur place. L'éteindre si le rack d'effet VST Ladspa ne fonctionne pas ou si un effet de feedback est important, même s'ils sont supposés être traitable sur place. Ce réglage requiert un redémarrage. Minimum control period Période de contrôle minimum Minimum audio controller process period (samples). Période minimum du processeur de contrôle audio (échantillons). Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. Période minimum du processeur de contrôle audio (échantillons). Ajuste la bonne réaction des contrôles audio et des graphiques de contrôleur. Mettre à une valeur basse for des contrôles rapides et fluides. Si cela cause un problème de performances, mettre à une valeur élevée. 1 1 2 2 4 4 8 8 Sample rate conversion, time stretching and pitch shifting Audio converter settings... GUI GUI LV2 UI open behavior Set range markers (left / right) Middle mouse button / Right mouse button Ctrl + Right mouse button / Right mouse button Ctrl + Left mouse button / Ctrl + Right mouse button Options Show status bar Show note tooltips in midi editors GUI Tweaks Tweaks 32 32 64 64 128 128 256 256 512 512 Sample rate Période d'échantillonnage Shorter periods give better midi playback resolution. Des périodes plus courtes donne une meilleure résolution de rendu midi. &Don't record hidden instruments Don'&t record muted instruments Don't record &hidden or muted instruments Instrument initialization Initialisation de l'Instrument Send instrument initialization sequences Envoie les séquences d'initialisation de l'instrument Warn if instrument initialization sequences pending Avertir si les séquences d'initialisation d'instrument sont en attentes Send instrument controller default values if none in song, at init or rewind Envoie les valeurs par défaut des contrôleurs de l'instrument s'il n'y en a pas dans la chanson, à l'initialisation ou au rembobinage 8192 8192 16384 16384 32768 32768 Midi Resolution (Ticks/Quarternote) Résolution Midi (coches/Noire) Record new style drum tracks Enregistre des pistes de batterie dans le nouveau style Record all instruments Enregistre tous les instruments Don't record hidden instruments N'enregistre pas les instruments cachés Don't record muted instruments N'enregistre pas les instruments rendus muets Don't record hidden or muted instruments N'enregistre pas les instruments cachés ou rendus muets GUI Behaviour Comportement du GUI Use old-style stop shortcut: Utilise l'ancien style de raccourci d'arrêt: Move single armed track with selection Déplacer la piste armée avec la sélection On Launch Au Démarrage Behavior Comportement LegatoBase MusE: Legato MusE: Legato Range Plage All Events Tous les Evènements Selected Events Evènements sélectionnés Looped Events Evènements bouclés Selected Looped Sélectionné Bouclé Parts Parties Selected parts or c&urrent part All parts &in editor Settings Réglages ticks coches Minimum Length Longueur minimum Allow shortening notes Autoriser le raccourcissement des notes OK OK Cancel Annuler MITTransposeBase MusE: Midi Input Plugin: Transpose MusE: Plugin d'Entrée Midi: Transposition On On TriggerKey Note de Déclenchement Transpose: Transposition: +0 +0 MRConfigBase MusE: Midi Input Plugin: Remote Control MusE: Plugin d'Entrée Midi: Contrôle Distant Activate Activer On On Actions Actions Stop Stop Record Enregistre Goto Left Mark Aller à la Marque Gauche Play Jouer Insert rest (step rec) Insérer une pause (enregistrement pas à pas) MdiSettingsBase Form Form GroupBox GroupBox MDI subwin Sous-fenêtre MDI Shares menu when subwin Partager les menus quand sous-fenêtre Shares menu when free Partager les menus quand libres MetronomeConfigBase MusE: Metronome Config MusE: Configuration du Métronome Metronome Métronome Audio Beep Bip audio Two samples (old samples) Deux échantillons (anciens échantillons) Four samples (new samples, with accents) Quatre échantillons (nouveaux échantillons, avec accents) Audio volume Volume Audio Meas volume Volume Mesure Beat volume Volume Battement Accent1 volume Volume Accent 1 Accent2 volume Volume Accent 2 MIDI Click Clic MIDI Midi Channel Canal Midi Measure Note Note Mesure Measure Velocity Vélocité de la Mesure Beat Velocity Résolution (Beat) de la Vélocité Beat Note Note de Résolution (Beat) Midi Port Port MIDI Precount Pré-compte enable activer Bars Mesures From Mastertrack de la Piste Principale / / Signature Signature Prerecord Pré-enregistrement Preroll PreRoll &Apply &Appliquer Volume Volume Preferences Apply the settings globally or to the current song: Use global metronome Use song-specific metronome Enable accents for the audio and midi metronomes: &Two samples (old samples) Four samples (&new samples, with accents) Audio Audio Audio master Meas Beat Accent1 Accent2 Sample &OK &Ok &Cancel &Annuler Choose outputs... Choisir les sorties... 50 50 % Audio volume % de volume audio Hint: Enable metronome in Transportpanel Astuce: Activer le métronome dans le panneau de transport MIDI Accent 2 Velocity Accent 1 Velocity Accent 1 Note Accent 2 Note Off Off Accents Accent sequence beats Current accent 1 sequence: (Click to change) Current accent 2 sequence: Factory User Utilisateur Preset sequences Add current accents to presets Make a new preset from the current accents. + Delete preset Supprime le pré-réglage Delete the selected preset. Only user presets can be deleted. - Use preset Copy the preset to the current accents. -> Reset to Factory or User defaults Resets all current accent sequences to default Factory or User presets. ! Enable the precount Enable Activer Number of bars Use Mastertrack time signature Beats per bar Beat note type Enable precount when playing Precount on play Mute the metronome after precount Mute metronome Not supported yet Alt+A Alt+A Alt+O Alt+O Alt+C Alt+C MidiAudioControlBase Midi control Contrôle Midi Port: Port: Channel: Canal: Control type: Type de contrôle: &Learn &Apprendre Control7 Control7 Control14 Control8 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Ton Program Programme Hi: Hi: Lo: Lo: Learn Apprentissage MidiControllerEditDialogBase MusE: Define Midi Controller MusE: Définir le Contrôleur Midi &Add &Ajouter Alt+A Alt+A create new entry créer une nouvelle entrée pressing the New button you create a new entry in the MusE list of defined controllers en pressant le bouton Nouveau, vous créez une nouvelle entrée dans la liste MusE des contrôleurs définis &Delete &Supprimer delete selected entry supprimer l'entrée sélectionnée &OK &OK A&pply A&ppliquer &Cancel A&nnuler Predefined Controller: Contrôleur prédéfini: Name Nom Type Type H-Ctrl H-Ctrl L-Ctrl L-Ctrl Min Val Val Min Max Val Val Max list of defined controllers liste des contrôleurs définis This is the MusE list of defined controllers. Ceci est la liste MusE des contrôleurs définis. Managed Controller for Port Contrôleur géré pour le Port Channel Canal This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. Ceci est une liste de contrôleurs communément utilisés. Notez que dans MusE le ton et les changements de programme sont gérés comme des contrôleurs normaux. Properties Propriétés Name Nom Min Value Valeur Min Max Value Valeur Max Type Type Control7 Control7 Control14 Control14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Ton Program Programme Midi Controller Number High Byte Contrôleur Midi - Numéro de Byte Haut Midi Controller Number Low Byte Contrôleur Midi - Numéro de Byte Bas Range Plage MidiFilterConfigBase MusE: Midi Input Filter MusE: Filtre Entrée Midi Record Filter Filtre d'Enregistrement Note On Note On Poly Pressure Pression Poly Controller Contrôle Program Change Changement de Programme After Touch Pression (After Touch) Pitch Bend Molette de modulation - Ton Sysex SysEx Thru Filter Filtre Thru Controller Filter Filtre de contrôle Channel Filter Filtre Canal 14 14 10 10 6 6 12 12 4 4 2 2 9 9 8 8 3 3 13 13 15 15 16 16 7 7 11 11 5 5 1 1 MidiInputTransformDialogBase MusE: Midi Input Transformator MusE: Transformateur d'Entrée Midi Filter Filtre All Tous Equal Egal Unequal Différent de Note Note Poly Pressure Pression Poly Control Change Contrôle de Changement Aftertouch Pression Pitch Bend Molette de modulation NRPN NRPN RPN RPN Program Programme Value 2 Valeur 2 Value 1 Valeur 1 Event Type Type d'Evènement Ignore Ignore Higher Plus Haut Lower Plus Bas Inside A l'intérieur Outside A l'extérieur Channel Canal Port Port Processing Travail Keep Garder Fix Fixer Plus Plus Minus Moins Multiply Multiplier Divide Diviser Invert Inverser ScaleMap Echelle Flip Retourner Dyn Dyn Random Aléatoire Toggle Modules Modules 1 1 2 2 3 3 4 4 enable modul 1 Activer module 1 enable modul 2 Activer module 2 enable modul 3 Activer module 3 enable modul 4 Activer module 4 Preset Pré-Réglages Name: Nom: Comment: Commentaires: Function Fonction &New &Nouveau create new preset Crée un nouveau réglage &Delete &Supprimer delete preset supprime un réglage Enable module 1 Enable module 2 Enable module 3 Enable module 4 &Dismiss &Rejeter PresetList Liste de Pré-Réglage MidiSyncConfigBase MusE: Midi Sync MusE: Synchro Midi Apply Appliquer Ok Ok Cancel Annuler hour heure h h minute minute m m second seconde s s frame trame f f subframe sous-trame 24 24 25 25 30D 30D 30N 30N MTC MTC Type: Type: Offset: Décalage: Sync receiving and sending Synchro la réception et l'envoi Send and receive Jack transport Envoyer et recevoir le transport Jack Send and receive Jack transport information, including stop, start and position. Envoyer et recevoir les informations de transport Jack, incluant stop, démarre et position. Use Jack transport Utiliser le transport Jack Make MusE the Jack transport Timebase Master Faire de MusE le maitre Timebase du transport Jack Make MusE the Jack transport Timebase Master. Allows Jack to show time as MusE Bars, Beats, and Ticks. MusE will try to become master, but other Jack clients can also take over later. You can always click here again for Master. Faire de MusE the le maitre TimeBase du transport Jack. Autorise Jack à afficher le temps comme des Barres MusE, Battements, et Coches. MusE essayera de devenir le maitre, mais d'autres clients Jack peuvent aussi prendre la main plus tard. Vous pouvez toujours cliquer ici à nouveau pour devenir Maitre. Jack transport Timebase Master Maitre Timebase du transport Jack Control MusE timing by external midi clock or MTC sync Contrôler le minutage de MusE par une horloge midi externe ou une synchro MTC When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). Doutes sur cette traduction En mode esclave, le tempo est contrôlé de façon externe. MusE peut se synchroniser à une horloge midi, ou à un quart de trame de synchro MTC. Activer les entrées dans la liste les appliquera (RMC, RMMC, RMTC). Slave to external sync Esclave d'une synchro externe Averaging applied to recorded external tempo changes. Moyennage appliqué à l'enregistrement de changements de tempo externes. External midi clock can be very jittery. Tempo is derived from it and recorded. It is usually desirable to average it and limit the number of recorded changes. Tiny: 2 section 4/4 = 8 stages. 1/8T note averaging, may produce jitter. Small: 3 section 12/8/4 = 24 stages. 1/4 note averaging, may still produce jitter. Medium: 3 section 28/12/8 = 48 stages. 1/2 note averaging. Less jitter. Large: 4 section 48/48/48/48 = 192 stages. Use this if the song has only one tempo. Very low quantization values can be used. Large pre-detect: 4 section 8/48/48/48 = 152 stages + first stage large step pre-detector. Use this if you expect sudden large tempo steps. None: Use only if high accuracy is needed for audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. L'horloge midi externe peut varier beaucoup. Le tempo est dérivé de cette horloge et enregistré. Il est habituellement désirable de la moyenner et de limiter le nombre de changements enregistrés. Minuscule: 2 sections 4/4 = 8 étapes. Moyennage 1/8T de note, peut produire de la variation. Petit: 3 sections 12/8/4 = 24 étapes. Moyennage 1/4 de note, peut encore produire de la variation. Moyen: 3 sections 28/12/8 = 48 étapes. Moyennage 1/2 de note. Moins de variation. Grand: 4 sections 48/48/48/48 = 192 étapes. A utiliser si la chanson n'a qu'un tempo. Des valeurs très faibles de quantification peuvent être utilisées. Grande pré-détection: 4 sections 8/48/48/48 = 152 étapes + première étape avec une pré-détection à grand pas. A utiliser si vous avez des grands pas soudains de tempo. Aucun: A utiliser uniquement si une grande précision est requise pour l'alignement audio sur du playback. Attention: Enregistre des centaines de changement de tempo par minute. MusE peut ralentir et la chanson aura une grosse taille. Tempo record averaging Moyennage du tempo d'enregistrement bpm bpm Tempo record quantization Quantification du tempo d'enregistrement Send start to first clock delay Envoyer start au premier décalage d'horloge This button and other transport related buttons are found on the Transport Panel Allows 'slow sync' devices time to synchronize to MusE. This value is the delay from sending start to sending the first clock. Autorise les systèmes de temps à 'synchro lente' à se synchroniser à MusE. Cette valeur correspond au délai entre l'envoie du start et l'envoie de la première horloge. ms ms Send sync delay Envoie un délai de synchro Note: Sync delay and MTC sync currently not fully implemented Note: Délai de synchro et synchro MTC ne sont pas complètement implémentés MidiTrackInfoBase MusE: TrackInfo MusE: Info de Piste output channel canal de sortie % % output port port de sortie off off Transpose notes up or down Transposer les notes vers le haut ou le bas Transp. Transp. Offset playback of notes before or after actual note Décale le jeu des notes avant ou après la note actuelle Change note length in percent of actual length Change la longueur de note en pourcentage de la longueur actuelle <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> <html><head/><body><p>Ajoute ou soustrait la vélocité des notes sur la piste.</p><p><span style=" font-style:italic;">Puisque la plage de note midi est 0-127 ceci <br/>signifie que les notes n'atteindront pas <br/>la vélocité combinée, note + Vélocité.</span></p></body></html> Compress the notes velocity range, in percent of actual velocity Compresse la plage de vélocité des notes, en pourcentage de la vélocité actuelle Channel Info Info canal Rec: Enr: Bank Select MSB. Ctrl-double-click on/off. Sélection de banque MSB. Ctrl-double-clic on/off. Bank Select LSB. Ctrl-double-click on/off. Sélection de banque LSB. Ctrl-double-clic on/off. Program. Ctrl-double-click on/off. Programme. Ctrl-double-clic on/off. Prog Prog Volume. Ctrl-double-click on/off. Volume. Ctrl-double-clic on/off. Vol Vol Change stereo position. Ctrl-double-click on/off. Change la position stéréo. Ctrl-double-clic on/off. Pan Pan Delay Délai H-Bank H-Bank Compr. Compr. L-Bank L-Bank Velocity Vélocité Length Longueur all midi events are sent to this output channel tous les évènements midi sont envoyés vers ce canal de sortie Out ch Ch. sortie input routing routage d'entrée output routing routage de sortie input detect détection d'entrée Input detect indicator. Detects all note on-off, controller, aftertouch, program change, and pitchbend (but not sysex or realtime) events on the selected channels, on the selected midi ports. Indicateur de détection d'entrée. Détecte tous les évènements note on-off, contrôleurs, aftertouch, changement de programme, et les pitchbend (mais pas les sysex ou realtime) sur les canaux sélectionnés, sur les ports midi sélectionnés. W W Midi thru Midi thru Pass input events through ('thru') to output. Fait transiter les évènements d'entrée vers ('thru') la sortie. Select instrument patch Sélectionne le patch d'instrument <unknown> <inconnu> Add all settings to song Appliquer tous les réglages à la chanson All Tous Add bank + prog settings to song Applique les réglages de banque et de programme à la chanson Add vol setting to song Applique le réglage de volume à la chanson Add pan setting to song Applique le réglage de panoramique à la chanson MidiTransformDialogBase MusE: Midi Transformator MusE: Transformateur Midi &New &Nouveau &Delete &Effacer &Apply &Appliquer &OK &Ok &Cancel &Annuler PresetList List des Réglages Processing Transformations Event Type Type d'évènements Keep Garder Fix Fixer Note Note Poly Pressure Pression Poly Control Change Changement de Contrôle Aftertouch Pression Aftertouch Pitch Bend Molette de modulation NRPN NRPN RPN RPN Plus Addition Minus Soustraction Multiply Multiplication Divide Division Value 2 Valeur 2 Invert Inverser ScaleMap Echelle Flip Retourner Dyn Dyn Random Aléatoire Value 1 Valeur 1 Length Longueur Position Position Randomize Randomiser Filter Filtre All Tous Equal Egale Unequal Différent Ignore Ignore Higher Plus haut Lower Plus bas Inside A l'intérieur Outside A l'extérieur Bar Range Type de mesure Preset Réglages Name: Nom: Comment: Commentaire: Range Type process all events transformer tous les évènements selected tracks pistes sélectionnées inside loop dans la boucle Function fonction Select Sélectionner Quantize Quantifier Delete Supprimer Transform Transformer Insert Insérer Copy Copier Extract Extraire Quantize Value Valeur de Quantification MixdownFileDialogBase MusE: Set Mixdown Wavefile MusE: Fichier Wav pour mixage &OK &OK &Cancel &Annuler File Path Chemin du Fichier Channel Canal Stereo Stéréo Mono Mono 5.1 5.1 wav,16 Bit wav,16 Bit wav, 24 Bit wav, 24 Bit wav, 32 Bit (float) wav, 32 Bit (flottant) Format Format MoveBase MusE: Move Notes MusE: Déplacer des Notes Range Plage All Events Tous les Evènements Selected Events Evènements Sélectionnés Looped Events Evènements Bouclés Selected Looped Sélectionné Bouclé Parts Parties Selected parts or c&urrent part All parts &in editor Value Valeur Move by Déplacer de ticks coches OK OK Cancel Annuler MusECore::AudioPreviewDialog Stop Stop Play Jouer MusECore::RasterizerModel Off Off Bar Mesure MusECore::Scripts Do you want to process ALL or only selected events? &Selected &All MusE - external script failed MusE - échec d'un script externe MusE was unable to launch the script, error message: %1 MusE n'a pas été capable de lancer le script, message d'erreur: %1 Re-read script names from disc MusECore::Song Jack shutdown! Arrêt de Jack! Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: - a performance issue with your particular setup. - a bug in MusE (or possibly in another connected software). - a random hiccup which might never occur again. - jack was voluntary stopped by you or someone else - jack crashed If there is a persisting problem you are much welcome to discuss it on the MusE mailinglist. (there is information about joining the mailinglist on the MusE homepage which is available through the help menu) To proceed check the status of Jack and try to restart it and then . click on the Restart button. Jack a détecté un problème de performance qui a amené MusE à se déconnecter. Ceci peut arriver pour diverses raisons: - un problème de performance avec votre réglage particulier. - un bug dans MusE (ou dans un programme connecté à MusE). - un évènement aléatoire qui pourra ne plus jamais se reproduire. - jack a été stoppé de façon volontaire par vous ou quelqu'un d'autre - jack a planté Si le problème persiste, vous êtes invité à en discuter sur la liste de diffusion MusE. (il y a des informations indiquant comment joindre la liste de diffusion sur le site web MusE qui est accessible via le menu d'aide) Pour continuer, vérifiez le status de Jack et essayez de le redémarrer puis cliquez sur le bouton Redémarrer. Automation: Automation: previous event évènement précédent next event évènement suivant set event définir l'évènement add event ajouter un évènement erase event effacer un évènement erase range effacer une plage clear automation nettoyer l'automation Automation Automation Previous event Next event Set event Add event Erase event Erase range Clear automation Midi control Contrôle midi Assign Assigner Clear Nettoyer Clear all controller events? Nettoyer tous les contrôleurs d'évènements? &Ok &Ok &Cancel &Annuler Controller Bypass MusE: Tempo list MusE: Liste de tempo External tempo changes were recorded. Transfer them to master tempo list? Des changements de tempo externes ont été enregistrés. Les transférer vers la liste principale de tempo? MusE - external script failed MusE - échec d'un script externe MusE was unable to launch the script, error message: %1 MusE n'a pas été capable de lancer le script, message d'erreur: %1 Und&o &Annuler Re&do Re&faire MusEGui select Pointer Tool: with the pointer tool you can: select parts move parts copy parts Choisir l'Outil de Pointage: avec l'outil de pointage vous pouvez: choisir des pièces déplacer des pièces copier des pièces select Pencil Tool: with the pencil tool you can: create new parts modify length of parts Choisir l'Outil Crayon avec le crayon vous pouvez: créer de nouvelles pièces modifier la longueur des pièces select Delete Tool: with the delete tool you can delete parts choisir l'Outil Gomme: avec la gomme vous pouvez effacer les pièces select Cut Tool: with the cut tool you can split a part Choisir l'Outil Ciseaux avec les ciseaux vous pouver couper une pièce select Glue Tool: with the glue tool you can glue two parts Choisir l'Outil Colle: avec la colle vous pouvez coller deux pièces ensemble select Score Tool: Choisir l'Outil Partition select Quantize Tool: insert display quantize event Choisir l'Outil Quantisation: pour voir les marques de quantisation select Drawing Tool Choisir l'Outil de Dessin select Muting Tool: click on part to mute/unmute Choisir l'Outil Silence: cliquez sur une pièces pour la rendre muette ou pas. pointer pointeur pencil crayon cutter ciseaux score partition quantize quantisation draw dessin mute parts silence MusEGui::Appearance Main application font, and default font for any controls not defined here. Police principal de l'application, et police par défaut pour tous les contrôles non définis ici. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Bandes de mixeurs et racks d'effets. Panneau d'information de la piste midi. Boite d'entrée du panneau de contrôle midi. Transport controls. Contrôles de transport. Time scale upper, and time signature. Controller graph and S/X buttons. Echelle de temps haute, et signature temporelle. Graphique de contrôleur et boutons S/X. Time scale lower, and arranger part name overlay. Echelle de temps basse, et nom de couche de la partie arrangeur. Main application font, and default font for any controls not defined here. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Arranger part name overlay. Time scale lower. Tempo scale, and markers. Echelle de tempo, et marqueurs. Mixer labels. Auto-font-sizing up to chosen font size. Word-breaking but only with spaces. Labels du mixeur. Dimensionnement automatique de la police parmis les tailles choisies. Césure de mot mais uniquement avec des espaces. Maximum mixer label auto-font-sizing font size. Taille maximum du dimensionnement automatique de la police. Global opacity (opposite of transparency). Opacité globale (opposé de la transparence). Standard Standard Custom Personnalisé Keep Qt system style Conserver le style système Qt Do you really want to reset colors to theme default? Voulez-vous réellement réinitialiser les couleurs à celles du thème par défaut? Style was changed. The program must be restarted for changes to take place. Restart now? Revert changes Revert all... Do you really want to reset all colors? &Ok &Ok &Cancel &Annuler MusE: load image MusE: charger une image No current color item Select Color: %1 Select style sheet Sélectionner la feuille de style Qt style sheets (*.qss) Feuilles de style Qt (*.qss) MusEGui::Arranger Enable Recording Activer l'Enregistrement Mute/Off Indicator Indicateur Muet/Off Solo Indicator Indicateur de Solo Track Type Type de Piste Track Name Nom de Piste Midi output channel number or audio channels Numéro de canal de sortie midi ou canaux audio Midi output port or synth midi port Port de sortie midi ou port de synthé midi Time Lock Verrouillage d'Horloge Automation parameter selection Sélection de paramètre d'automation Notation clef Clef de notation Enable recording. Click to toggle. Active l'enregistrement. Cliquer pour basculer. Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. On/Off is not! Indicateur muet. Cliquer pour basculer. Clic-droit pour basculer la piste entre on et off. Muet est conçu pour une action rapide et répétée. On/Off ne l'est pas! Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed, indicated by a dark square. Indicateur de solo. Cliquer pour basculer. Les pistes connectées sont aussi en mode 'phantom' solo. indiqué par un carré noir. Track type. Right-click to change midi and drum track types. Type de piste. Clic-droit pour changer les types de piste midi et batterie. Track name. Double-click to edit. Right-click for more options. Nom de piste. Double-clic pour éditer. Clic-droit pour plus d'options. Midi/drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Piste midi/batterie: Numéro de canal de sortie. Piste audio: Canaux. Clic Milieu/Droit pour changer. Midi/drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Piste midi/batterie: Port de sortie. Piste synthé: Port midi assigné. Clic gauche pour changer. Clic droit pour afficher l'interface graphique. Time lock Verrouillage d'Horloge Notation clef. Select this tracks notation clef. Clef de notation. Sélectionner la clef de notation pour ces pistes. Track index. Click to select track, CTRL+LMB to add/remove, SHIFT+LMB for range select, dblclick to select all of the same type. Enable/Disable input monitor. Passes input through to output (see also Settings: Automatically monitor on record arm). Enable/Disable recording. Mute indicator. Left-click to mute track (for quick action), right-click to toggle track on/off (for more permanent action). Track type. Click to switch between midi and drum track types. Track name. Double-click to edit. Right-click for context menu. Midi/Drum: Output channel number. Audio: Channels. Mid/right-click to change. Midi/Drum: Output port. Synth: Assigned midi port. Left-click to change, right-click to show synth GUI. Notation clef. Click to select this tracks notation clef. Arranger Arrangeur Cursor Curseur Cursor position Bars Mesures Off Off Bar Mesure Track index Enable input monitor Enable recording Mute/Off indicator Solo indicator Track type Track name Enable input monitor. Click to toggle. Passes input through to output for monitoring. See also Settings: Automatically Monitor On Record Arm. Enable recording. Click to toggle. See also Settings: Automatically Monitor On Record Arm. Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed. Midi/Drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi/Drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Solo indicator. Connected tracks are also 'phantom' soloed. Snap Aligne Len Long song length - bars Longueur de la chansons - mesures Type Type NO NO GM GM GS GS XG XG midi song type type de fichier midi Pitch Ton midi pitch ton midi global midi pitch shift transposition midi globale Tempo Tempo midi tempo tempo midi Song length - bars Midi pitch Global midi pitch shift Midi tempo N N TrackInfo Info de Piste I Input monitor E R Record R M Mute M S Solo S C Class (track type) C Track Piste Port Port Ch Channel Can T Time lock T Automation Automation Clef Clef MusEGui::ArrangerColumns Control7 Control7 Control14 Control14 RPN RPN NPRN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Ton Program Programme Aftertouch Pression Aftertouch MusEGui::ArrangerView MusE: Arranger MusE: Arrangeur Arranger Arrangeur D&elete C&ut Co&uper &Copy &Copier Copy in range Copier dans la plage &Paste Co&ller Paste (show dialog) Coller (affiche le dialoge) Paste c&lone Co&ller clone Paste clone (show dialog) Coller clone (affiche le dialogue) Paste to selected &track Coller dans la pis&te sélectionnée Paste clone to selected trac&k Coller le c&lone dans la piste sélectionnée Paste (show dialo&g) Coller (afficher le dialo&gue) &Insert Empty Measure &Insérer une mesure vide Delete Selected Tracks Supprimer les pistes sélectionnées Duplicate Selected Tracks Dupliquer les pistes sélectionnées Shrink selected parts Réduire les parties sélectionnées Expand selected parts Etendre les parties sélectionnées Purge hidden events from selected parts Purger les évènements cachés des parties sélectionnées Copy in Range Paste C&lone Paste to Selected &Track Paste Clone to Selected Trac&k Paste (Show Dialo&g)... Shrink Selected Parts Expand Selected Parts Purge Hidden Events from Selected Parts Add Track Ajouter Piste Insert Track Insérer une Piste Select Sélectionner Select &All Sélectionner &Tout &Deselect All &Dé-selectionner tout Invert &Selection Inverser la &Sélection &Inside Loop Boucle &Interne &Outside Loop B&oucle Externe All &Parts on Track Tous les &parties sur la Piste Score Partition All Tracks in One Staff One Staff per Track New Score Window... Pianoroll... Drums... Event List... Wave... Open in New Window Always open the part(s) in a new editor window Open Synth Plugin GUI... Midi &Transform... Global Cut - Selected Tracks Global Insert - Selected Tracks Global Split - Selected Tracks Change Note &Length Change Note &Velocity &Scripts Scripts for midi processing. Applied to selected midi parts (or else tracks). User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. &Display Display menu: View-specific display options. Toggle &Mixer Strip Configure &Custom Columns... New... Nothing to edit Rien à éditer all tracks in one staff toutes les pistes sur une portée one staff per track une portée par piste New score window Nouvelle fenêtre de partition Pianoroll Vue-Piano Drums Batteries List Liste Wave Forme d'Onde Mastertrack Piste Principale Graphic Graphique Midi &Transform &Transformation MIDI Global Cut Couper global Global Insert Insérer global Global Split Diviser global Global Cut - selected tracks Couper global - pistes sélectionnées Global Insert - selected tracks Insérer global - pistes sélectionnées Global Split - selected tracks Diviser global - pistes sélectionnées &Edit &Editer Fu&nctions Fo&nctions &Structure &Structure Functions Fonctions &Quantize Notes &Quantifie les Notes Change note &length Changer la &longueur de la note Change note &velocity Changer la &vélocité de la note Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transposition Erase Events (Not Parts) Efface les Evènements (Pas les Parties) Move Events (Not Parts) Déplace les Evènements (Pas les Parties) Set Fixed Note Length Définit un Longueur de Note Fixée Delete Overlapping Notes Supprime les Notes qui se Chevauchent Legato Legato Window &Config &Configuration de Fenêtre Configure &custom columns Configure les &colonnes supplémentaires Remove track(s) Supprime le(s) piste(s) Are you sure you want to remove this track(s)? Etes-vous sur de vouloir supprimer cette/ces piste(s)? New Nouveau Changed Settings Paramètres Modifiés Unfortunately, the changed arranger column settings cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. (we'll try to fix that) Malheureusement, la modification des réglages des colonnes de l'arrangeur ne peut pas être appliquée tant que MusE s'exécute. Pour appliquer ces changements, veuillez redémarrer MusE. Désolé. (nous essayerons de réparer cela) MusEGui::AudioComponentRack Aux send level (dB) Vol Vol Pan Pan Mute Volume/gain Panorama/Balance Gain Gain Calibration gain MusEGui::AudioMixerApp &Create &Créer &View &Vue Strips Routing Routage Show Midi Tracks Afficher les Pistes Midi Show Drum Tracks Afficher les Piste de Batterie Show New Style Drum Tracks Afficher les Pistes dans le Nouveau Style de Batterie Show Wave Tracks Afficher les Pistes d'Onde Show Inputs Afficher les Entrées Show Outputs Afficher les Sorties Show Groups Afficher les Groupes Show Auxs Afficher les Auxs Show Synthesizers Afficher les Synthétiseurs Traditional order Arranger order User order Show all hidden strips Unhide strip: (no hidden strips) MusEGui::AudioStrip panorama panorama aux send level niveau d'envoie aux Pan Pan calibration gain gain de calibration 1/2 channel 1/2 canal Pre Pré pre fader - post fader pré fader - post fader dB dB record enregistre mute muet record downmix enregistrer downmix solo mode mode solo off off input routing routage d'entrée output routing routage de sortie Pre Fader Listening (PFL) Record downmix Record arm Mute or proxy mute Solo or proxy solo Track off Input monitor Input routing. Press F1 for help. Output routing. Press F1 for help. Volume/Gain Pass input through to output Off Off Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Auto off Read Lire Touch Touche Write Ecrire Automation type Automation type: Off, Read, Touch or Write. Press F1 for help. L meter peak/clip R meter peak/clip automation type type d'automation MusEGui::BigTime format display format d'affichage bar mesure beat battement tick coche minute minute second seconde frame trame subframe sous-trame Format display Bar Mesure Beat Tick Coche Minute Second Frame Subframe MusE: Bigtime MusE: Grande Horloge MusEGui::Canvas Tools: Outils: Tools Outils MusEGui::ClipListEdit MusE: Clip List Editor MusE: Editeur de la Liste de Clips Window &Config &Configuration de la Fenêtre MusEGui::ComboQuant Off Off MusEGui::CompactKnob off off MusEGui::CompactPatchEdit Patch name MusEGui::CompactSlider off off MusEGui::CpuStatusBar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) CPU load averaged over each GUI update period. DSP load read from JACK. Number of xruns. Double-click to reset. MusEGui::CpuToolbar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) MusEGui::CtrlCanvas Make the current part's track match the selected drumlist entry Faire que la partie courante de la piste corresponde à l'entrée sélectionnée de la liste de batterie Drawing hint: Hold Ctrl to affect only existing events Conseil de dessin: Maintenir Ctrl pour affecter uniquement les évènements existants Use pencil or line tool to draw new events Utiliser le stylo ou l'outil ligne pour dessiner de nouveaux évènements Control canvas: Use Pencil tool to edit events and Draw tool to adjust them gradually. Hold Ctrl to affect only existing events. Merge options Erase target Erase target events between source events Erase target WYSIWYG Include last source item width when erasing Erase target inclusive Include entire source range when erasing Merge actions Merge Merge the dragged items Merge a copy Merge a copy of the dragged items Cancel drag Cancel dragging the items MusEGui::CtrlPanel S Select controller S Select controller Remove panel Manual adjust (Ctrl-double-click on/off) All/Per-note velocity mode select controller Choisissez le contrôleur X Remove panel (destroy button) X remove panel supprimer le panneau manual adjust ajustement manuel ctrl-double-click on/off ctrl-double-clic on/off off off all/per-note velocity mode mode vélocité tout/par-note Velocity Vélocité add new ... ajouter un nouveau ... Instrument-defined Instrument-défini Add ... Ajouter ... Others Autres Edit instrument ... Editer l'instrument ... Common Controls Contrôles Communs MusEGui::DList hide this instrument cacher cet instrument show this instrument afficher cet instrument this turns a grayed out eye into a blue eye ceci transforme un oeil grisé en un oeil bleu Hide this instrument This turns a blue eye into a crossed eye Show this instrument This turns a crossed eye into a blue eye Set field Sets a field Set row Sets a row Set column Sets a whole column to the field Set list Sets the whole list Reset field Resets a field in a row to default patch or instrument value Reset row Resets a row to the instrument values Reset column Resets a whole column to the instrument values Reset list Resets the whole list to the instrument values Reset track's drum list Resets all lists on all patches to the instrument values Promote field to default patch Promotes a field in a row to the default patch Promote row to default patch Promotes a row to the default patch Promote column to default patch Promotes a column to the default patch Promote list to default patch Promotes the whole list to the default patch Reset instrument field Reset instrument row Reset instrument list Set instrument field Set instrument row Set instrument list Drum list: Edit instrument-specific settings. MusEGui::DrumCanvas Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help. Moving items failed Echec du déplacement des éléments The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. La sélection n'a pas pu être déplacé, parce qu'au moins une note aurait du être déplacé dans une piste qui est différente de la piste originel et de la partie de la piste courante. Changer la partie courante avec ALT+GAUCHE/DROITE peut aider. Creating event failed Echec de la création d'un évènement Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Impossible de créer l'évènement, parce que la partie courante sélectionné n'est pas la même piste, et l'instrument sélectionné peut être soit sur aucune ou sur plusieurs parties, ce qui est ambigu. Sélectionnez la partie de destination et essayez à nouveau. Recording event failed Echec de l'enregistrement d'un évènement Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Impossible d'enregistrer l'évènement, parce que la partie courante sélectionnée n'est pas la même piste, et l'instrument qui doit être enregistré peut être soit sur aucune soit sur plusieurs parties, ce qui est ambigu. Sélectionnez la partie de destination, et essayez à nouveau. Internal error Erreur interne Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. Attention, un erreur interne affreuse, qui ne peut normalement pas se produire, s'est produite. Vérifiez les messages de la console. Rien n'a été enregistré. Drum map Carte de batterie Reset the track's drum map with instrument defaults? Réinitialiser la carte de batterie de la piste avec les réglages par défaut de l'instrument? Note: Velocity: Start (bar): Start (time): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete LMB: Delete Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete. MusEGui::DrumEdit mute instrument rendre muet l'instrument sound name nom du son volume percent pourcentage de volume quantisation quantification this input note triggers the sound cette note en entrée déclenche le son note length longueur de note this is the note which is played c'est la note qui est jouée output channel (hold ctl to affect all rows) canal de sortie (maintenir ctrl pour affecter toutes les lignes) output port (hold ctl to affect all rows) port de sortie (maintenir ctrl pour affecter toutes les sorties) shift + control key: draw velocity level 1 touches shift + control: dessine une vélocité de niveau 1 control key: draw velocity level 2 touche control: dessine une vélocité de niveau 2 shift key: draw velocity level 3 touche shift: dessine une vélocité de niveau 3 draw velocity level 4 dessine une vélocité de niveau 4 output channel (ctl: affect all rows) canal de sortie (ctrl: affecte toutes les lignes) output port (ctl: affect all rows) port de sortie (ctrl: affecte toutes les sorties) &File &Fichier Load Map Charger la Carte Save Map Enregistrer la Carte Reset GM Map Réinitialiser la Carte GM &Edit &Editer Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Note which triggers the sound Note which is played Override track output channel (hold CTRL to affect all rows). Override track output port (hold CTRL to affect all rows). CTRL + META keys: Draw velocity level 1 META key: Draw velocity level 2 META + ALT keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) Cut Couper Copy Copier Copy events in range Copier les évènements dans la plage Paste Coller Paste to current part Paste (with Dialog) Coller (avec Dialogue) Delete Events Supprimer des Evènements &Select &Sélectionner Select All Tout Sélectionner Select None Ne rien Sélectionner Invert Inverser Inside Loop Boucle Interne Outside Loop Boucle Externe Previous Part Partie précédente Next Part Partie Suivante Event List... Fu&nctions Fo&nctions Set Fixed Length Activer la longueur fixée Modify Velocity Modifier la vélocité Crescendo/Decrescendo Crescendo/Decrescendo Quantize Quantifier Erase Event Effacer l'Evènement Move Notes Déplacer les Notes Delete Overlaps Supprimer les Recouvrements &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Cursor step &Plugins &Greffons Cursor tools Add controller view Window &Config &Configuration de la Fenêtre Drum tools Outils Batterie Load Drummap Charger la Carte de Batterie hide instrument cacher l'instrument override track output channel (hold ctl to affect all rows) écrase le canal de la piste de sortie (maintenir ctrl pour affecter toutes les lignes) override track output port (hold ctl to affect all rows) écrase le port de la piste de sortie (maintenir ctrl pour affecter toutes les lignes) control + meta keys: draw velocity level 1 contrôle + touche méta: dessine une vélocité de niveau 1 meta key: draw velocity level 2 touche méta: dessine une vélocité de niveau 2 draw default velocity level 3 dessine une vélocité par défaut de niveau 3 meta + alt keys: draw velocity level 4 méta + touche alt: dessine une vélocité de niveau 4 override track output channel (ctl: affect all rows) écrase le canal de la piste de sortie (ctrl: affecte toutes les lignes) override track output port (ctl: affect all rows) écrase le port de la piste de sortie (ctrl: affecte toutes les lignes) Re-order map Ré-organiser la carte &Display Display menu: View-specific display options. Group Grouper Don't group Ne pas grouper Group by channel Grouper par canal Group maximally Grouper au maximum Show/Hide Afficher/Cacher Also show hidden instruments Afficher aussi les instruments cachés Show all instruments Afficher tous les instruments Hide all instruments Cacher tous les instruments Only show used instruments Afficher seulement les instruments utilisés Only show instruments with non-empty name or used instruments Afficher seulement les instruments avec un nom ou les instruments utilisés Step record Midi input Play events Drum map tools Outils de carte de batterie Store Drummap Enregistrer la Carte de Batterie Step Record Enregistrement pas à pas Midi Input Entrée Midi Play Events Jouer les Evènements cursor tools outils curseur Cursor step: Incrément de curseur: Set step size for cursor edit Régler la taille du pas pour l'édition par curseur ctrl ctrl Add Controller View Ajouter une Vue Contrôleur H hide H M mute M Sound Son Vol Vol QNT QNT E-Note E-Note Len Long A-Note A-Note Ch Can Port Port LV1 LV1 LV2 LV2 LV3 LV3 LV4 LV4 Muse: Load Drum Map MusE: Charger la Carte de Batterie MusE: Store Drum Map MusE: Enregistrer la Carte de Batterie Drum map Carte de batterie Reset the drum map with GM defaults? Réinitialiser la carte de batterie avec les réglages GM par défaut? Not all parts are displayed Toutes les parties ne sont pas affichées You selected both old-style-drumtracks and others (that is: new-style or midi tracks), but they cannot displayed in the same drum edit. I'll only display the old-style drumtracks in this editor, dropping the others. Vous avez sélectionné des pistes de batterie ancien style et d'autres pistes (c'est à dire: nouveau style ou pistes midi), mais elles ne peuvent pas être affiché dans le même éditeur de batterie. Je n'afficherai que la piste de batterie ancien style dans cet éditeur, en oubliant les aures. MusEGui::EditCAfterDialog MusE: Enter Channel Aftertouch MusE: Entrez le Canal AfterTouch Time Position Position Temporelle Pressure Pression MusEGui::EditEventDialog Ok Ok Cancel Annuler MusEGui::EditInstrument Use note offs No note offs Convert to 0-vel note ons Control7 Control7 Control14 Control14 RPN RPN NPRN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Ton Program Programme PolyAftertouch PolyAftertouch Aftertouch Pression Aftertouch H hide H M mute M Name Nom Vol Vol Quant Quant E-Note E-Note Len Long A-Note A-Note Ch Can Port Port LV1 LV1 LV2 LV2 LV3 LV3 LV4 LV4 Tick Coche Data Donnée Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) MusE: Create file failed MusE: Echec de la création du fichier MusE: Write File failed MusE: Echec de l'enregistrement du fichier MusE: Save Instrument Definition MusE: Sauvegarder la définition de l'instrument Instrument Definition (*.idf) Définition de l'instrument (*.idf) MusE: Save instrument as MusE: Sauvegarder l'instrument sous Enter a new unique instrument name: Entrez un nouveau nom d'instrument unique: The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? L'instrument utilisateur '%1' existe déjà. Le fichier instrument idf sera écrasé. Etes-vous sur? MusE: Bad instrument name MusE: Mauvais nom d'instrument Please choose a unique instrument name. (The name might be used by a hidden instrument.) Veuillez choisir un nom d'instrument unique. (Le nom peut être utilisé par un instrument caché.) MusE: Bad patch name MusE: Mauvais nom de patch Please choose a unique patch name Veuillez choisir un nom de patch unique MusE: Bad patchgroup name MusE: Mauvais nom de groupe de patch Please choose a unique patchgroup name Veuillez choisir un nom de groupe de patch unique MusE: Bad controller name MusE: Mauvais nom de contrôleur Please choose a unique controller name Veuillez choisir un nom de contrôleur unique New controller: Error Nouveau contrôleur: Erreur Error! All control numbers are taken up! Clean up the instrument! Erreur! Tous les numéros de contrôle sont pris! Nettoyez l'instrument! MusE: Cannot add common controller MusE: Impossible dajouter un contrôleur commun A controller named '%1' already exists. Un contrôleur nommé '%1' existes déjà. A controller number %1 already exists. Un contrôleur numéro %1 existes déjà. MusE MusE The current Instrument contains unsaved data Save Current Instrument? L'instrument courant contient des données non sauvegardées Sauvegarder l'instrument courant? &Save &Sauvegarder &Nosave &Pas de sauvegarde &Abort &Annuler MusEGui::EditMetaDialog MusE: Enter Meta Event MusE: Entrez un Méta-évènement Time Position Position Temporelle Meta Type Type Méta Enter Hex Entrez Hexa MusEGui::EditPAfterDialog MusE: Enter Poly Aftertouch Muse: Entrer la Pression Aftertouch polyphonique Time Position Position Temporelle Pitch Ton Pressure Pression MusEGui::EditToolBar select Pointer Tool: with the pointer tool you can: select parts move parts copy parts Choisir l'Outil de Pointage: avec l'outil de pointage vous pouvez: choisir des parties déplacer des parties copier des parties select Pencil Tool: with the pencil tool you can: create new parts modify length of parts Choisir l'Outil Crayon avec le crayon vous pouvez: créer de nouvelles parties modifier la longueur des parties select Delete Tool: with the delete tool you can delete parts choisir l'Outil Gomme: avec la gomme vous pouvez effacer les parties select Cut Tool: with the cut tool you can split a part Choisir l'Outil Ciseaux avec les ciseaux vous pouver diviser une partie select Glue Tool: with the glue tool you can glue two parts Choisir l'Outil Colle: avec la colle vous pouvez coller deux parties ensemble select Score Tool: Choisir l'Outil Partition: select Quantize Tool: insert display quantize event Choisir l'Outil Quantification: pour voir les marques de quantification select Drawing Tool Choisir l'Outil de Dessin select Muting Tool: click on part to mute/unmute Choisir l'Outil Silence: cliquez sur une partie pour la rendre muette ou pas Select Pointer tool: With the pointer tool you can: select parts move parts copy parts Select Pencil tool: With the pencil tool you can: create new parts modify length of parts Select Delete tool: With the delete tool you can delete parts Select Cut tool: With the cut tool you can split a part Select Glue tool: With the glue tool you can glue two parts Select Score tool: Select Quantize tool: Insert display quantize event Select Drawing tool Select Muting tool: Click on part to mute/unmute Manipulate automation Manipuler l'automation Select Cursor (tracker mode) tool: With the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Select Range tool Select Panning tool Select Zoom tool Select Stretch tool Select Sample rate tool Pointer Pencil Eraser Cutter Glue Range Pan Pan Zoom Score Partition Quantize Quantifier Draw Mute parts Edit automation Cursor Curseur Stretch Sample rate Période d'échantillonnage Cursor tool Outil Curseur pointer pointeur pencil crayon eraser effaceur cutter ciseaux score partition glue colle select Cursor (tracker mode) tool: with the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 sélectionne l'outil Curseur (mode poursuite): avec l'outil curseur, vous pouvez: naviguer avec les flèches utiliser VBNM pour placer les notes changer le pas avec 0 et 9 select Range Tool sélectionne l'Outil Plage select Panning Tool sélectionne l'Outil Panoramique select Zoom Tool sélectionne l'Outil Zoom range plage pan panoramique zoom zoom quantize quantification draw dessin mute parts rend silencieuces des parties edit automation éditer l'automation cursor curseur Edit Tools Outils d'Edition MusEGui::EffectRack effect rack rack d'effets new nouveau change changement move up déplacer vers le haut move down déplacer vers le bas remove supprimer bypass direct show gui afficher l'interface show native gui afficher l'interface native save preset sauvegarder les pré-réglages Effect rack: Double-click a slot to insert/edit effect. RMB to open context menu. Press F1 for help. Effect rack Double-click a slot to insert FX New Nouveau Change Changer Move up Move down Remove Supprimer Bypass Show gui Show native gui Save preset Sauvegarde le pré-réglage Presets Pré-réglages MusE: Save Preset MusE: Sauvegarder les pré-réglages Replace effect Remplacer l'effet Do you really want to replace the effect %1? Voulez-vous réellement remplacer l'effet %1? MusEGui::GlobalSettingsConfig MusE: Choose start template or song MusE: Choisir le modèle de départ ou la chanson MusEGui::Header Track Info Columns Colonnes d'information de piste Custom column MusEGui::LCDPatchEdit High bank: Low bank: Program (Ctrl-double-click on/off) off off High bank Low bank Program Programme MusEGui::LMaster MusE: Mastertrack MusE: Piste Principale &Edit &Editer Insert Tempo Insérer le Tempo Insert Signature Insérer la Signature Insert Key Insérer la Clef Edit Positon Editer la Position Edit Value Editer la Valeur Delete Event Supprimer l'évènement Window &Config &Configuration de la Fenêtre Edit tools Outils d'Edition Tempo Tempo Timesig Signature Temporelle Key Clef new tempo nouveau tempo new signature nouvelle signature new key nouvelle clef Meter Mesure Time Temps Type Type Value Valeur Signature Signature Position Position Delete Insert tempo change Insert time signature change Insert key change Edit position Edit value Delete event Reposition of the initial tempo and signature events is not allowed Le repositionnement du tempo initial et de la signature des évènements n'est pas autorisée MusE: List Editor MusE: Editeur de Liste Input error, conversion not OK or value out of range Erreur d'entrée, la conversion n'est pas OK ou la valeur est en dehors de la plage Reposition of tempo and signature events to start position is not allowed! Le repositionnement du tempo initial et de la signature des évènements au début n'est pas autorisée! MusEGui::ListEdit insert Note insère une note insert SysEx insère une commande midi SysEx insert Ctrl insére un caractère de Contrôle insert Meta insére une balise Méta insert Channel Aftertouch insére un canal de Pression (AfterTouch) insert Poly Aftertouch insère une Pression (AfterTouch) polyphonique &Edit &Editer Cut Couper Copy Copier Paste Coller Delete Events Supprimer les évènements Increase Tick Incrémenter les coches Decrease Tick Décrémenter les coches Window &Config &Configuration de la fenêtre Insert Note Insérer Note Insert SysEx Insérer SysEx Insert Ctrl Insérer Ctrl Insert Meta Insérer Méta Note Note SysEx Ctrl Ctrl Meta Tick+ Tick- Delete Insert tools Outils d'Insertion Tick Coche Bar Mesure Type Type Ch Can Val A Val A Val B Val B Val C Val C Len Long Comment Commentaire Insert note event Insert system exclusive event Insert controller event Insert meta event Increase tick value Decrease tick value Delete event MusE: List Editor MusE: Editeur de Liste MusEGui::MPConfig Default input connections Connections d'entrée par défaut Are you sure you want to apply to all existing midi tracks now? Etes-vous sur de vouloir appliquer ces changements à toutes les pistes midi maintenant? Default output connections Connections de sortie par défaut Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? Les réglages s'appliqueront aux nouvelles pistes midi. Voulez-vous appliquer ces réglages à toutes les pistes midi existantes maintenant? MusE: bad device name MusE: mauvais nom de périphérique please choose a unique device name veuillez choisir un nom de périphérique unique in entrée out sortie Show first aliases Montrer les premiers alias Show second aliases Montrer les seconds alias Toggle all Basculer tout Change all tracks now Modifier toutes les pistes maintenant Remove Supprimer Create Jack device Créer un périphérique Jack Port Number Numéro de port Enable gui Activer le gui Enable reading Activer la lecture Enable writing Activer l'écriture Port instrument Port d'instrument Midi device name. Click to edit (Jack) Nom de périphérique midi. Cliquer pour éditer (Jack) Connections from Jack Midi outputs Connections à partir des sorties Midi Jack Connections to Jack Midi inputs Connections à des entrées Midi Jack Auto-connect these channels to new midi tracks Auto-connecter ces canaux aux nouvelles pistes midi Auto-connect new midi tracks to these channels Auto-connecter les nouvelles pistes midi à ces canaux Auto-connect new midi tracks to this channel Auto-connecter les nouvelles pistes midi à ce canal Device state Etat du périphérique Enable gui for device Activer le gui pour le périphérique Enable reading from device Activer la lecture à partir du périphérique Enable writing to device Activer l'écriture vers le périphérique Name of the midi device associated with this port number. Click to edit Jack midi name. Nom du périphérique midi associé à ce numéro de port. Cliquer pour éditer le nom midi Jack. Instrument connected to port Instrument connecté au port Connections from Jack Midi output ports Connections à partir des ports de sortie Midi Jack Connections to Jack Midi input ports Connections aux ports d'entrée Midi Jack Auto-connect these channels, on this port, to new midi tracks. Auto-connecter ces canaux, sur ce port, vers les nouvelles pistes midi. Connect new midi tracks to these channels, on this port. Connecter les nouvelles pistes midi vers ces canaux, sur ce port. Connect new midi tracks to this channel, on this port. Connecter les nouvelles pistes midi vers ce canal, sur ce port. State: result of opening the device Etat: résultat de l'ouverture du périphérique Port Port GUI GUI I E O S Instrument Instrument Device Name Nom du Périphérique Please choose a unique device name Port number Midi device name Midi device type Connections from Jack Midi Connections to Jack Midi Result of opening the device: OK: Assigned to a port and in use Closed: Unassigned to a port, or closed R/W Error: Unable to open for read or write Unavailable: USB midi unplugged, or external application not running, or synth plugin not installed etc. (Jack Midi devices have 'unavailable ports' in the routes columns.) Unavailable devices or ports can be purged with 'Remove' or with the advanced router. Enable Graphical User Interface for device Connections from Jack Midi ports Connections to Jack Midi ports In routes Routes d'entrée Out routes Routes de sortie Def in ch Déf can entr Def out ch Déf can sort State Etat Type Type In Out <unknown> <inconnu> <none> <rien> MusEGui::MTScale bar scale taille de la mesure Bar scale Bar scale: Use mouse buttons to set position and range markers. Hold Shift to set (LMB) or delete (RMB) custom markers. MusEGui::MTScaleFlo bar scale taille de la mesure Bar scale MusEGui::MarkerView MusE: Marker MusE: Marqueur add marker ajouter un marqueur delete marker supprimer un Marqueur &Edit &Editer Window &Config &Configuration de la Fenêtre edit tools outils d'édition Add marker Delete marker Edit tools Outils d'Edition Bar:Beat:Tick Mesure:Résolution(Beat):Coche Hr:Mn:Sc:Fr:Sf Hr:Mn:Sc:Fr:Sf Lock Verrouillage Description Description Edit tick Edit SMPTE timecode Toggle timecode editing Edit marker description <Description> Text Texte Marker Properties Propriétés du Marqueur MusEGui::Master Tools Outils MusEGui::MasterEdit MusE: Mastertrack MusE: Piste Principale Window &Config &Configuration de la Fenêtre Enable master Activer maître Enable Activer Enable usage of master track Activer l'utilisation de la piste principale Info Info Cursor Curseur time at cursor position temps à la position du curseur tempo at cursor position temps à la position du curseur Mastertrack Piste Principale &Display Display menu: View-specific display options. Time at cursor position Tempo at cursor position Off Off Bar Mesure Snap Aligne CurPos PosCur tempo at current position tempo à la position courante time signature at current position signature temporelle à la position courante MusEGui::MetronomeConfig Reset accents: Resets all accents to the defaults (first in list) of the current preset category (Factory or User). Proceed? MusEGui::MidiAudioControl Control7 Control7 Control14 Control14 RPN RPN NPRN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Ton Program Programme Aftertouch Pression Aftertouch MusEGui::MidiComponentRack Pro Var Var Rev Rév Cho Cho Pan Pan Program Programme VariationSend EnvoieVariation ReverbSend EnvoiRéverb ChorusSend EnvoiChorus Pan/Balance Pan/Balance Controller (Ctrl-double-click on/off) <unknown> <inconnu> Instrument Instrument Transpose Transpose notes up or down Transposer les notes vers le haut ou le bas Delay Délai Offset playback of notes before or after actual note Décale le jeu des notes avant ou après la note actuelle Length Longueur Change note length in percent of actual length Change la longueur de note en pourcentage de la longueur actuelle Velocity Vélocité <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> Compress Compress the notes velocity range, in percent of actual velocity Compresse la plage de vélocité des notes, en pourcentage de la vélocité actuelle <Patch> MusEGui::MidiInputTransformDialog New Nouveau MusEGui::MidiStrip B Palette B B off off Record arm Mute or proxy mute Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Track off Input monitor Intput routing. Press F1 for help. Output routing. Press F1 for help. Inst Inst Prop Instruments and controllers Properties Propriétés Volume/Gain (Ctrl-double-click on/off) Pass input through to output Input monitor: Pass input through to output. ctrl-double-click on/off ctrl-double-clic on/off VariationSend EnvoieVariation Var Var ReverbSend EnvoiRéverb Rev Rév ChorusSend EnvoiChorus Cho Cho dB dB Pan/Balance Pan/Balance Pan Pan record enregistrer mute muet solo mode mode solo input routing routage d'entrée output routing routage de sortie MusEGui::MidiSyncConfig Port Number Numéro de Port Name of the midi device associated with this port number Nom du périphérique midi associé avec ce numéro de port Sync to this device. Click to select. Midi clock input detected Horloge d'entrée midi détectée Midi tick input detected Coche d'entrée midi détectée Midi real time input detected Entrée midi temps réelle détectée MMC input detected Entrée MMC détectée MTC input detected Entrée MTC détectée Detected SMPTE format Format SMPTE détecté Receive id number. 127 = Global. Double click to edit. Numéro d'id reçu. 127 = Global. Double cliquer pour éditer. Accept midi clock input Accepter les horloges d'entrée midi Accept midi real time input Accepter les entrées midi temps réel Accept MMC input Accepter les entrées MMC Accept MTC input Accepter les entrées MTC Receive start rewinds before playing La réception de 'démarre' rembobine avant de jouer Transmit id number. 127 = Global. Double click to edit. Numéro d'id transmis. 127 = Global. Double cliquer pour éditer. Send midi clock output Envoie la sortie horloge midi Send midi realtime output Envoie la sortie midi temps réel Send MMC output Envoie la sortie MMC Send MTC output Envoie la sortie MTC Type Type Midi clock input detected. Current port actually used is red. Click to force a port to be used. Horloge d'entrée midi détectée. Le port courant actuellement utilisé est rouge. Cliquer pour forcer un port à être utilisé. Midi realtime input detected, including start/stop/continue, and song position. Entrée temps réelle midi détectée, incluant démarrer/stop/continuer, et la position dans la chanson. MMC input detected, including stop/play/deferred play, and locate. Entrée MMC détectée, incluant stop/jouer/jouer différé, et localise. MTC input detected, including forward quarter-frame sync and full-frame locate. Current port actually used is red. Click to force a port to be current. Entrée MTC détectée, incluant la synchro avant quart de trame et la localisation pleine-trame. Le port courant actuellement utilisé est rouge. Cliquer pour forcer le port à être le port courant. Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detects format of MTC quarter and full frame, and MMC locate. Format SMPTE détecté: 24tps, 25 tps, 30tps trame oubliée, ou 30tps trame non oubliée Détecte le format du MTC quart et pleine trame, et la localisation MMC. Receive id number. 127 = global receive all, even if not global. Numéro d'id reçu. 127 = global reçoit tout, même si non global. Accept midi clock input. Only one input is used for clock. Auto-acquire: If two or more port realtime inputs are enabled, the first clock detected is used, until clock is lost, then another can take over. Best if each turns off its clock at stop, so MusE can re-acquire the clock from another port. Click on detect indicator to force another. Accepte les entrées horloge midi. Seulement une entrée est utilisée pour l'horloge. Auto-aquisition: Si deux entrées temps réelles ou plus sont activées, la première horloge détectée est utilisée, jusqu'à ce que l'horloge soit perdue, alors une autre horloge prend le relai. Meilleurs si chaque entrée éteint son horloge à l'arrêt, de façon à ce que MusE puisse ré-aquérir l'horloge à partir d'un autre port. Cliquer sur l'indicateur de détection pour forcer l'utilisation d'une autre horloge. Accept midi realtime input, including start/stop/continue, and song position. Non-clock events (start,stop etc) are accepted by ALL enabled ports. This means you may have several master devices connected, and muse will accept input from them. Accepte les entrées midi temps réelles, incluant démarre/stop/continue, et la position dans la chanson. Les évènements hors horloge (démarre,stop etc) sont acceptés par TOUS les ports activés. Ceci veut dire que vous pouvez avoir plusieurs périphériques maîtres connectés, et MusE acceptera les entrées de leur part. Accept MMC input, including stop/play/deferred play, and locate. Accepte l'entrée MMC, incluant stop/jouer/jouer différé, et localiser. Accept MTC input, including forward quarter-frame sync and full-frame locate. See 'rc' column for more help. Accepte l'entrée MTC, incluant la synchro quart de trame avant et la localisation pleine trame. Voir la colonne 'rc' pour plus d'aide. When start is received, rewind before playing. Note: It may be impossible to rewind fast enough to synchronize with the external device. Quand 'démarre' est reçu, rembobine avant de jouer. Note: Il peut être possible de rembobiner suffisamment rapidement pour synchroniser avec un périphérique externe. Sync to this device. Click to select. MusE will synchronize to this device's clock or MTC quarter-frame, if 'slave to external sync' is on. MTC input detected, including forward quarter-frame sync and full-frame locate Accept midi clock input. Only one port can be used for clock sync. Accept MTC input, including forward quarter-frame sync and full-frame locate. Only one port can be used for sync. When start is received, rewind before playing. Note: In some cases, such as having many project audio tracks, it may be impossible to rewind fast enough to synchronize with the external device. Manually rewinding the device before playing is recommended. Transmit id number. 127 = global transmit to all. Numéro d'id transmit. 127 = global transmet à tous. Send midi clock output. If 'Slave to External Sync' is chosen, muse can re-transmit clock to any other chosen ports. Envoie la sortie horloge midi. Si 'Esclave à une synchro externe' est choisi, muse peut re-transmettre l'horloge à chacun des autres ports choisis. Send midi realtime output, including start/stop/continue, and song position. If 'Slave to external sync' is chosen, muse can re-transmit midi realtime input messages to any other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. Envoi la sortie midi temps réel, incluant démarre/stop/continue, et la position dans la chanson. Si 'Esclave d'une synchro externe' est choisi, MusE peut re-transmettre les messages de l'entrée temps réelle à chacun des autres ports choisis. Ceci veut dire que vous pouvez avoir plusieurs périphériques esclaves connectés, et muse peut re-transmettre les messages temps réels à chacun d'eux. Port Port Device Name Nom du Périphérique s s c c k k r r m m t t type type rid rid rc rc rr rr rm rm rt rt rw rw tid tid tc tc tr tr tm tm tt tt None Aucun Tiny Minuscule Small Petit Large Grand Large with pre-detect Grand avec pré-détection MusE MusE Settings have changed Apply sync settings? Les réglages ont changé Appliquer les réglages de synchro? &Apply &Appliquer &No &Non &Abort &Annuler <none> <aucun> MusEGui::MidiTrackInfo <unknown> <inconnu> MusEGui::MidiTransformerDialog New Nouveau MusEGui::MixdownFileDialog Wave Files (*.wav);;All Files (*) Fichiers Wave (*.wav);;Tous les Fichiers (*) MusEGui::MusE Failed to start audio! Echec du démarrage de l'audio! Was not able to start audio, check if jack is running. Impossible de démarrer l'audio, vérifiez si Jack s'exécute. Timeout waiting for audio to run. Check if jack is running. Timeout d'attente de démarrage de l'audio. Vérifiez que Jack s'exécute. Und&o &Annuler Re&do Re&faire undo last change to song annuler le dernier changement sur la chanson redo last undo refaire ce qui a été annulé en dernier Loop Boucle loop between left mark and right mark boucler entre les marqueurs gauche et droit Punchin Punch-In record starts at left mark l'enregistrement démarre au marqueur gauche Punchout Punch-Out record stops at right mark l'enregistrement s'arrête au marqueur droit Start Démarrer rewind to start position revenir à la position de départ Rewind Rembobiner rewind current position revenir à la position en cours Forward Avance move current position déplacer la position en cours Stop Stop stop sequencer arrêter le séquenceur Play Jouer start sequencer play démarrer la lecture du séquenceur Record Enregistrer to record press record and then play pour enregistrer, presser Enregistrement puis Lecture Panic Panique send note off to all midi channels envoyer 'note off' à tous les canaux midi turn on/off metronome activer / désactiver le métronome &New &Nouveau Create New Song Créer une Nouvelle Chanson &Open &Ouvrir Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. Cliquer sur ce bouton pour ouvrir une <em>nouvelle chanson</em>.<br>Vous pouvez aussi sélectionner la <b>commande Ouvrir</b> du menu fichier. Open &Recent Ouvrir &Récent &Save &Sauvegarder Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. Cliquer sur ce bouton pour sauvegarder la chanson que vous éditez. Il vous sera demandé un nom de fichier. Vous pouvez aussi sélectionner la commande Sauvegarder du menu fichier. Save &As Enregistrer &Sous Import Midifile Importer fichier MIDI Export Midifile Exporter fichier MIDI Import Part Importer une Partie Import Audio File Importer fichier Audio Find unused wave files Trouver les fichiers wave non utilisés &Quit &Quitter Song Info Info de la Chanson Transport Panel Panneau de Transport Bigtime Window Fenêtre Grande Horloge Mixer A Mixeur A Mixer B Mixeur B Cliplist ListClip Marker View Vue Marqueur Arranger View Vue Arrangeur Fullscreen Plein écran &Plugins &Greffons Edit Instrument Editer l'instrument Input Plugins Greffons d'Entrée Transpose Transpose Midi Input Transform Transformations Entrée MIDI Midi Input Filter Filtres d'Entrée MIDI Midi Remote Control Télécommande MIDI Rhythm Generator Générateur de Rythme Reset Instr. Ré-initialisation des Instruments. Init Instr. Init. Instr. Local Off Local Off Bounce to Track Rebondir sur la piste Bounce to File Rebondir sur Fichier Restart Audio Redémarrer l'Audio Mixer Automation Mixeur Automation Take Snapshot Prendre une Prise de Vue Clear Automation Data Nettoyer les Données d'Automation Cascade Cascade Tile Tuile In rows En lignes In columns En colonnes Global Settings Paramètres Généraux Configure Shortcuts Configure les raccourcis Follow Song Suivre la Chanson Don't Follow Song Ne pas Suivre la Chanson Follow Page Suivre la Page Follow Continuous Suivi Continu Set left/right marker for bounce range MusE: Song: MusE: Chanson: Metronome Métronome Midi Sync Synchro MIDI Midi File Import/Export Import/Export Fichier Midi Appearance Settings Réglages d'Apparence Midi Ports / Soft Synth Ports Midi / Synthé Soft &Manual &Manuel &MusE Homepage &Page d'Accueil MusE &Report Bug... &Rapporter un Bug... &About MusE &A propos de MusE Song Position Position de la Chanson Tempo Tempo Signature Signature File Buttons Boutons Fichier Undo/Redo Défaire/Refaire Transport Transport Cpu load &File &Fichier &View &Vue &Midi &MIDI &Audio &Audio A&utomation A&utomation &Windows &Fenêtres Se&ttings Régla&ges &Help &Aide About &Qt A propos de &Qt Cannot read template Impossible de lire le modèle File open error Erreur d'ouverture de fichier File read error Erreur de lecture de fichier Unknown File Format: %1 Format de fichier inconnu: %1 MusE: Song: %1 MusE: Chanson: %1 MusE: load project MusE: charger le projet Timeout waiting for audio to run. Check if jack is running or try another driver. Was not able to start audio, check if jack is running or try another driver. Undo last change to song Redo last undo Loop between left mark and right mark Punch in Record starts at left mark Recording starts at left mark Punch out Record stops at right mark Recording stops at right mark Rewind to start position Rewind current position Move current position Stop sequencer Restart rec Start sequencer play To record press record and then play Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help. Turn on/off metronome Metronome on/off. Press F1 for help. Create new song New from &Template... Create new song from template &Open... &Ouvrir... Open song from file Click this button to open an existing song. Save current song Click this button to save the song you are editing. You will be prompted for a file name. Save &As... Sauvegarder &sous... &Close &Fermer Import Midifile... Export Midifile... Import Part... Import Audio File... Find Unused Wave Files... Song Info... Mastertrack Piste Principale Edit Instrument... Transpose... Midi Input Transform... Midi Input Filter... Midi Remote Control... Reset Instrument Init Instrument Send 'local-off' command to all midi channels. Bounce to File... Global Settings... Appearance... Rewind on Stop Metronome... Midi Sync... Midi File Import/Export... &MusE Homepage... &Did You Know? &About MusE... Snooper (developer tool)... About &Qt... The project has no project sample rate (added 2011). Please enter a rate. The current system rate (%1Hz) is suggested, and cancelling uses it: The project has audio waves, but no project sample rate (added 2011). Please enter a rate. The most common wave rate found is suggested, the project was probably made with it. Cancelling uses the current system rate (%1Hz): Project sample rate MusE: load template MusE: charger le modèle MusE: Write File failed MusE: échec de l'écriture du fichier The current Project contains unsaved data Save Current Project? Le Projet en cours contient des données non encore sauvegardées Sauvegarder le Projet Courant? S&kip Sau&ter Load configuration colors &Cancel &Annuler Color settings will immediately be replaced with any found in the file. Are you sure you want to proceed? &Ok &Ok Save configuration colors MusE: Save As MusE: Enregistrer Sous Nothing to edit Rien à éditer MusE: Bounce to Track MusE: Rebondir sur la Piste No wave tracks found Pas de pistes wave trouvées No audio output tracks found Pas de pistes de sortie audio trouvées Select one audio output track, and one target wave track Sélectionnez une piste de sortie audio et une piste wave cible Select one target wave track Sélectionnez une piste wave cible Select one target wave track, and one audio output track Sélectionnez une piste wave cible, et une piste de sortie audio MusE: Bounce to File MusE: Rebondir sur Fichier Select one audio output track Sélectionnez une piste de sortie audio MusE: Bounce MusE: Rebondir set left/right marker for bounce range régler les marqueurs gauche/droit pour la plage de rebondissement The current Project contains unsaved data Load overwrites current Project: Save Current Project? Le Projet en cours contient des données non sauvegardées Le chargement va écraser le Projet courant: Sauvegarder le Projet Courant? &Abort &Annuler This will clear all automation data on all audio tracks! Proceed? Ceci va nettoyer toutes les données d'automation sur toutes les pistes audio! Continuer? This takes an automation snapshot of all controllers on all audio tracks, at the current position. Proceed? Ceci va faire une prise de vue des automations de tous les contrôleurs sur toutes les pistes audio, à la position courante. Continuer? MusE: Warning MusE: Attention The song uses multiple ports but export format 0 (single track) is set. The first track's port will be used. Playback will likely be wrong unless the channels used in one port are different from all other ports. Canceling and setting a different export format would be better. Continue? La chanson utilise plusieurs ports mais le format d'exportation vaut 0 (piste seule). Le port de la première piste sera utilisé. La lecture sera probablement mauvaise à moins que les canaux utilisés avec un port soient différents de tous les autres ports. Annuler et régler un format d'exportation différent serait mieux. Continuer? MusE: Export Midi MusE: Export Midi no help found at: pas d'aide trouvée ici: MusE: Open Help MusE: Ouvrir l'Aide Unable to launch help Impossible de charger l'aide For some reason MusE has to launch the default browser on your machine. Pour certaines raisons MusE doit charger le navigateur par défaut de votre machine. MusE: Import Midi MusE: Import Midi Add midi file to current project? Ajouter le fichier midi au projet courant? &Add to Project &Ajouter au Projet &Replace &Remplacer reading midifile lecture du fichier Midi Reading midifile failed: échec: Import part is only valid for midi and wave tracks! Importer une partie n'est valide que pour les pistes midi et wave! MusE: load part MusE: charger une partie No track selected for import Pas de piste sélectionnée pour l'importation %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. %n partie(s) sur %1 n'ont pas pu être importé. Probablement parce que la piste sélectionnée est du mauvais type. %n part(s) could not be imported. Likely the selected track is the wrong type. %n partie(s) n'ont pas pu être importé. Probablement parce que la piste sélectionnée est du mauvais type. to import an audio file you have first to selecta wave track pour importer un fichier audio, vous devez d'abord sélectionner une piste wave To import an audio file you have first to select a wave track Import Wavefile Importer un fichier Wave Wave import error There are too many wave files of the same base name as imported wave file Can not continue. Can't create new wav file in project folder! Failed to initialize sample rate converter! Cancel Annuler Show Docks Toggle display of currently visible dock windows. Display MusE main window in full screen mode. Mastertrack Graphic... Send 'note-off' command to all midi channels. Send initialization messages as found in instrument definition. Take Automation Snapshot Keyboard Shortcuts... Midi Ports / Soft Synths... &Manual (wiki)... File buttons Timeline Recording Sync Sync Position Position The current project contains unsaved data. Save current project? No valid parts selected The current project contains unsaved data. Load overwrites current project. Save current project? This wave file has a samplerate of %1 Hz, as opposed to current setting %2 Hz. A live, real-time samplerate converter can be used on this file. Or, a copy of the file can be resampled now from %1 Hz to %2 Hz. Do you want to use a converter or resample the file now? Use live converter Resample now Resampling wave file "%1" from %2 to %3 Hz... Output has clipped Resampling again and normalizing wave file "%1" Try %2 of %3... This wave file has a samplerate of %1, as opposed to current setting %2. Do you still want to import it? Ce fichier wave a une période d'échantillonnage de %1, contrairement au réglage courant %2. Voulez-vous quand même l'importer? &Yes &Oui &No &Non File version is %1.%2 Current version is %3.%4 Conversions may be applied if file is saved! Opening file Do not warn again Unable to launch browser Error launching default browser MusEGui::NoteInfo Note Info Info Note delta/absolute mode mode delta/absolu Delta/Absolute mode Start Début Len Long Pitch Ton Velo on Velo off Velo On Vélo On Velo Off Vélo Off MusEGui::PartCanvas Cannot copy/move/clone to different Track-Type Ne peut copier/déplacer vers une type de Piste différent Part: Partie: C&ut Co&uper &Copy &Copier s&elect Sél&ectionner clones clônes rename renommer color couleur delete supprimer split diviser glue colle super glue (merge selection) super colle (fusionner la sélection) de-clone de-clôner save part to disk sauvegarder la partie vers le disque wave edit édition wave file info information du fichier Part S&elect Rename Color Delete Split Glue Super glue (merge selection) De-clone Save part to disk... Wave edit... File info... MusE: Save part Automation Automation Normalize MusE: save part MusE: sauvegarder une partie Part name: %1 Files: Nom de partie: %1 Fichiers: Automation: Automation: Part canvas: Use Pencil tool to draw parts. Double-click to create a new MIDI/drum part between the range markers (set with MMB + RMB). Press F1 for help. Remove selected Supprimer les sélectionnés LMB: Select/Move/Dblclick to edit | CTRL+LMB: Multi select/Move&Copy | CTRL+ALT+LMB: Dblclick to edit in new window | SHIFT+LMB: Select track | MMB: Delete LMB: Draw to resize | MMB: Delete | CTRL+RMB: Trim length LMB: Delete | CTRL+RMB: Trim length LMB: Cut part in two LMB: Merge with following part LMB: Mute selected part LMB: Edit automation events in audio parts %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. %n partie(s) parmis %1 n'ont pas pu être collé. Probablement parce que la piste sélectionnée est du mauvais type. %n part(s) could not be pasted. Likely the selected track is the wrong type. %n partie(s) n'ont pas pu être collé. Probablement parce que la piste sélectionnée est du mauvais type. Cannot paste: multiple tracks selected Ne peut coller: plusieurs pistes sélectionnées Cannot paste: no track selected Ne peut pas coller: pas de piste sélectionnée Can only paste to midi/drum track Ne peut uniquement coller que sur une piste midi/batterie Can only paste to wave track Ne peut uniquement coller que sur une piste wave Can only paste to midi or wave track Ne peut uniquement coller que sur une piste midi ou wave Cannot paste: wrong data type Ne peut coller: mauvais type de donnée MusEGui::PasteDialog %n quarter(s) %n quart(s) %1 quarter for floating-point arguments like 1.5 %1 quart %1 quarters for floating-point arguments like 1.5 %1 quarts MusEGui::PasteEventsDialog %n quarter(s) %n quart(s) %1 quarter for floating-point arguments like 1.5 %1 quart %1 quarters for floating-point arguments like 1.5 %1 quarts MusEGui::Piano Piano: Press key to play or enter events in step record mode (SHIFT for chords). RMB: Set cursor for polyphonic control events. CTRL+Mousewheel to zoom view vertically. Left click: Play Right click: Select key MusEGui::PianoCanvas Pianoroll canvas: Use Pencil tool to draw and edit MIDI events, Pointer tool to select and edit. Press F1 for help. Note: Velocity: Start (bar): Start (time): Length (ticks): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete | CTRL+RMB: Trim length LMB: Resize | CTRL+LMB: Multi select | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length LMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length MusEGui::PianoRoll &Edit &Editer C&ut Co&uper &Copy &Copier Copy events in range Copier les évènements dans la plage &Paste C&oller Paste to current part Paste (with dialog) Coller (avec dialogue) Delete &Events Supprimer les Evèn&ements &Select &Sélectionner Select &All Selectionner &Tout &Deselect All &Dé-selectionner tout Invert &Selection Inverser la &Sélection &Inside Loop Boucle &Interne &Outside Loop B&oucle Externe &Previous Part &Partie Précédente &Next Part Partie Suiva&nte Event List... Fu&nctions Fo&nctions Quantize Quantifier Modify Note Length Modifier la Longueur de Note Modify Velocity Modifier la vélocité Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transposer Erase Events Effacer les Evènements Move Notes Déplacer les Notes Set Fixed Length Activer la longueur fixée Delete Overlaps Supprimer les Recouvrements Legato Legato &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Play single note Play chords &Plugins &Greffons &Display Display menu: View-specific display options. Add controller view Step record Midi input Play events Window &Config &Configuration de la Fenêtre &Event Color Couleur d'Evèn&ement &Blue &Bleu &Pitch colors Couleurs de &Ton &Velocity colors Couleurs de &Vélocité Pianoroll tools Outils vue piano Step Record Enregistrement pas à pas Midi Input Entrée Midi Play Events Jouer les Evènements ctrl ctrl Add Controller View Ajouter une Vue Contrôleur MusEGui::PluginDialog MusE: select plugin MusE: choisir le greffon MusE: Select Plugin Plugin categories. Right-click on tabs to manage. Right-click on plugins to add/remove from a category. Catégories de greffon. Clic-droit sur les onglets pour gérer. Clic-droit sur les gréffons pour ajouter/supprimer d'une catégorie. Type Type Lib Lib Label Label Name Nom AI AI AO AO CI CI CO CO IP IP FB 2B 5 1/3' {2B?} id id Maker Fabriquant Copyright Copyright Audio inputs Entrées audio Audio outputs Sorties audio Control inputs Entrées de contrôle Control outputs Sorties de contrôle In-place capable capable sur place Requires fixed block size Requires power-of-2 block size ID number Numéro d'ID &Create new group &Delete currently selected group Re&name currently selected group New group Nouveau groupe Ok Ok Cancel Annuler Show plugs: Afficher les prises: Mono and Stereo Mono et Stéréo Stereo Stéréo Mono Mono Show All Afficher Tout &create new group &créer un nouveau groupe &delete currently selected group &détruire le groupe actuellement sélectionné re&name currently selected group re&nommer le groupe actuellement sélectionné Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. Selectionnez les types de greffons qui devraient être visible dans la liste.<br>Notez qu'utiliser des greffons mono sur des pistes mono n'est pas un problème, deux seront utilisés en parallèle.<br>Attention aussi car l'alternative 'tout' inclues des greffons qui peuvent ne pas être utiles dans un rack d'effets. Associated categories Catégories associées You need to define some categories first. Vous devez d'abord définir des catégories. new group nouveau groupe Enter the new group name Entrer le nouveau nom de groupe Wine VST dssi synth synthé dssi dssi effect effet dssi LV2 synth LV2 effect VST synth VST effect ladspa ladspa Search in 'Label' and 'Name': Chercher dans 'Label' et 'Nom': MusEGui::PluginGui File Buttons Boutons Fichier Load Preset Charger le pré-réglage Save Preset Sauvegarder le pré-réglage Bypass plugin Plugin settings MusE: Save preset Plugin Settings bypass plugin Greffon bypassé MusE: load preset MusE: charger les pré-réglages Error reading preset. Might not be right type for this plugin Erreur lors de la lecture du pré-réglage. Peut ne pas être le bon type pour ce greffon MusE: save preset MusE: sauvegarder le pré-réglage MusEGui::PluginSettings Global setting: On On Off Off MusEGui::PopupMenu <More...> %1 <Plus...> %1 MusEGui::PosToolbar Left marker Left marker position Right marker Right marker position Current position Current position in bars/beats SMPTE position Current position in SMPTE time Show/Hide position in ticks and audio frames Show/Hide additional output fields displaying the current position in ticks and audio frames Current position in ticks Current position in audio frames Current position slider MusEGui::ProjectCreateImpl Select directory Sélectionnez le répertoire MusEGui::RasterLabelCombo Snap Aligne MusEGui::RecToolbar Record mode Record mode: Overdub to add new events, Replace to replace overlapping events. Overdub Sur-enregistrement (Overdub) Replace Remplace Cycle record mode Cycle record mode: Normal to replace range when loop is finished, Mix to add new events, Replace to replace range on first MIDI input. Normal Normal Mix Mix MusEGui::RouteDialog Normal Normal Alias 1 Alias 2 Source Source Destination Destination <none> MusEGui::RoutePopupMenu Channel Canal Soloing chain chaîne solo Audio returns Retours audio Warning: No input devices! Attention: pas de périphériques d'entrée! Channel grouping: Mono Stereo Stéréo Channels Omni Show aliases: First Second Show names Show first aliases Montrer les premiers alias Show second aliases Montrer les seconds alias Jack ports Connect Connecter Unavailable Open advanced router... Warning: No output devices! Attention: Pas de périphériques de sortie! Output routes: Input routes: Open midi config... Ouvrir la configuration midi... ALSA devices JACK devices Synth devices <none> <rien> Midi sends Sources: Toggle all Basculer tout More... Plus... Tracks Destinations: Audio sends Envoies audio Midi port sends Envoies de port midi MusEGui::ScoreCanvas Treble Aigus Bass Basse Grand Staff Grande portée Remove staff Supprimer la portée Ambiguous part Partie ambigue There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. Il y a deux parties possibles ou plus dans lesquelles vous pourriez ajouter la note, mais aucune ne correspond à la partie sélectionnée. Veuillez sélectionner la partie de destination en cliquant sur une des notes lui appartenant et essayez à nouveau, ou ajoutez une nouvelle portée ne contenant que la partie de destination. No part Pas de partie There are no parts you could add the note to. Il n'y a pas de parties dans lesquelles vous pourriez ajouter une note. Tools Outils MusEGui::ScoreEdit Step recording tools Outils d'enregistrement pas à pas Step Record Enregistrement pas à pas Note settings Réglages de note Note length: Longueur de note: last dernier Apply to new notes: Appliquer aux nouvelles notes: Apply to selected notes: Appliquer aux notes sélectionnées: Velocity: Vélocité: Off-Velocity: Vélocité Off: Quantisation settings Réglages de quantification Step record Quantisation: Quantification: Pixels per whole: Pixels pour le total: &Edit &Editer C&ut Co&uper &Copy &Copier Copy events in range Copier les évènements dans la plage Quantisation Pixels per whole Note length Velocity Vélocité Apply to selected notes, or new notes if none is selected Off-Velocity Copy Events in Range &Paste C&oller Paste (with dialog) Coller (avec dialogue) Delete &Events Supprimer les &Evènements &Select &Sélectionner Select &All Sélectionner &Tout &Deselect All &Dé-selectionner tout Invert &Selection Inverser la &Sélection &Inside Loop Boucle &Interne &Outside Loop Boucle E&xterne Fu&nctions Fo&nctions &Quantize &Quantifier Change Note &Length Change Note &Velocity Display menu: View-specific display options. Note Head &Colors &Default Set Up &Preamble Display &Key Signature Display &Time Signature Set Score &Name Change note &length Changer la &longueur de note Change note &velocity Changer la &vélocité de la note Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transposer Erase Events Effacer les Evènements Move Notes Déplacer les Notes Set Fixed Length Activer la longueur fixée Delete Overlaps Supprimer les Recouvrements Legato Legato &Display Window &Config &Configuration de la Fenêtre Note head &colors &Couleurs de la tête de note &Black &Noire &Velocity &Vélocité &Part &Partie Set up &preamble Régler le &préambule Display &key signature Afficher la si&gnature de la clef Display &time signature Afficher la signature &temporelle Set Score &name Régler le &nom de la partition Enter the new score title Entrer le nouveau nom de la partition Error Erreur Changing score title failed: the selected title is not unique Echec du changement du nom de partition: le titre sélectionné n'est pas unique MusEGui::ScrollScale next page page suivante previous page page précédente current page number numéro de page courante MusEGui::ShortcutCaptureDialog Ok Ok Cancel Annuler Shortcut conflicts with %1 Le raccourci est en conflit avec %1 Undefined Non-définie MusEGui::ShortcutConfig Save printable text file Sauvegarder sous la forme de texte imprimable Text files (*.txt);;All files (*) Fichiers texte (*.txt);;Tous les fichiers (*) Error Erreur Error opening file for saving Erreur lors de l'ouverture du fichier pour sauvegarde Shortcuts for selected category: Raccourcis pour la catégorie sélectionnée: Legend: Légende: An error occurred while saving Une erreur est survenue lors de la sauvegarde MusEGui::SigScale signature scale échelle de la signature Signature scale MusEGui::SigToolbar Time signature at current position Signature: Signature: MusEGui::SigToolbarWidget time signature at current position signature temporelle à la position en cours Signature: Signature: MusEGui::Strip Remove track? Supprimer la piste? Name Nom Enter track name: MusE: bad trackname MusE: mauvais nom de piste Please choose a unique track name Configuration Prefer knobs, not sliders Show values in controls Prefer midi volume as decibels Monitor on record-arm automatically Visible audio effects Actions Actions Change track name Hide strip MusEGui::SyncToolbar External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::TList Track list: Use context menu to create tracks. Click track ID to select, CTRL to add, SHIFT for range, dblclick to select all of same type. Press F1 for help. SYNTH IS UNAVAILABLE! <none> <rien> visible visible no clef pas de clef Treble Aigu Bass Basse Grand Grand off off <unknown> <inconnu> MusE: bad trackname MusE: mauvais nom de piste Please choose a unique track name Show gui Show native gui Clear automation please choose a unique track name veuillez choisir un nom unique de piste Unused Devices Périphériques non utilisés Update drummap? Mettre à jour la carte de batterie? Do you want to use same port for all instruments in the drummap? Voulez-vous utiliser le même port pour tous les instruments de cette carte de batterie? &Yes &Oui &No &Non show gui afficher l'interface show native gui afficher l'interface native Presets Pré-réglages Clear all controller events? Nettoyer tous les contrôleurs d'évènements? &Ok &Ok &Cancel &Annuler Change color Changer la couleur Midi control Contrôle Midi Assign Assigner Clear Nettoyer Other Autre clear automation nettoyer l'automation Treble clef Clef aigue Bass clef Clef basse Grand Staff Grande portée Viewable automation Automation affichable Internal Interne Synth Synthé Delete Track Supprimer la piste Delete Selected Tracks Supprimer les pistes sélectionnées Track Comment Commentaire pour la Piste Save track's drumlist Sauvegarder la liste de batterie de la piste Save track's drumlist differences to initial state Sauvegarder les différences par rapport à l'état initial de la liste de batterie Load track's drumlist Charger la liste de batterie de la piste Reset track's drumlist Réinitialiser la liste de batterie de la piste Reset track's drumlist-ordering Réinitialiser la liste de batterie de la piste-organiser Copy track's drumlist to all selected tracks Copier la liste de batterie de la piste vers toutes les pistes sélectionnées Copy track's drumlist's differences to all selected tracks Copier les différences de la liste de batterie de la piste vers toutes les pistes sélectionnées Insert Track Insérer une Piste Drum map Carte de batterie Reset the track's drum map with instrument defaults? Réinitialiser la carte de batterie de la piste avec les réglages par défaut de l'instrument? Reset the track's drum map ordering? Réinitialiser l'organisation de la carte de batterie de la piste? Muse: Load Track's Drum Map MusE: Charger la Carte de Batterie de la Piste Drummap Carte de batterie This drummap was created with a previous version of MusE, it is being read but the format has changed slightly so some adjustments may be necessary. Cette carte de batterie a été créée avec une version précédente de MusE, elle sera lue mais le format a légèrement changé donc quelques ajustement seront nécessaires. MusE: Store Track's Drum Map MusE: Stocker la Carte de Batterie de la Piste Midi Midi Drum Batterie New style drum Nouveau style de batterie Do you want to use same port and channel for all instruments in the drummap? Voulez-vous utiliser le même port et canal pour tous les instrument de la carte de batterie? MusEGui::TempoSig Use mastertrack tempo Tempo/Sig Tempo/Sig Mastertrack tempo at current position, or fixed tempo Time signature at current position MusEGui::TempoToolbar Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Tap Tap this button to set tempo Set the tempo to the time difference of two successive clicks. Times out after two seconds. Tempo: Tempo: MusEGui::TempoToolbarWidget tempo at current position tempo à la position courante Tempo: Tempo: MusEGui::Toolbar1 Off Off Solo Solo Cursor Curseur Snap Aligne MusEGui::TopWin As subwindow Comme sous-fenêtre Shares tools and menu Partager les outils et le menu Tabbed/Floating Display editor in a tab or in a separate window (preset in Global Settings->Editors). Fullscreen Plein écran Undo/Redo tools Outils Défaire/Refaire Panic Panique Metronome Métronome Timeline Transport Transport Recording Sync Sync Song Position Position de chanson Tempo Tempo Signature Signature Position Position Piano roll Vue piano List editor Editeur de liste Drum editor Editeur de batterie Master track editor Editeur de piste principale Master track list editor Editeur de liste de piste principale Wave editor Editeur de forme d'onde Clip list Liste de clip Marker view Vue des marqueurs Score editor Editeur de partition Arranger Arrangeur <unknown toplevel type> <type toplevel inconnu> MusEGui::TrackComment MusE: Track Comment Muse: Commentaire de Piste Track Comment: Commentaire de Piste: MusEGui::Transport Overdub Sur-enregistrement (Overdub) Replace Remplace Rec Mode Mode Enregistrement Normal Normal Mix Mix Cycle Rec Enr. Cycle Punch in Punch out Rewind to Start Rewind Rembobiner Forward Avance Stop Stop Play Jouer Record Metronome on/off External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. punchin Punch-In loop boucle punchout Punch-Out Punch In Punch-In Loop Boucle Punch Out Punch-Out Left Mark Marque Gauche Right Mark Marque Droite rewind to start rembobine au départ Click this button to rewind to start position Cliquez sur ce bouton pour revenir à la position de départ rewind rembobiner Click this button to rewind Cliquez sur ce bouton pour revenir en arrière forward avance Click this button to forward current play position Cliquez sur ce bouton pour avancer dans la lecture stop stop Click this button to stop playback Cliquez sur ce bouton pour arréter la lecture play jouer Click this button to start playback Cliquez sur ce bouton pour démarrer la lecture record enregistrer Click this button to enable recording Cliquez sur ce bouton pour activer l'enregistrement AC AC quantize during record quantifie pendant l'enregistrement Click Clic metronom click on/off clic du métronome on/off Sync Sync external sync on/off synchro externe on/off Jack Jack Jack transport sync on/off Synchro de transport Jack on/off Master Maître use master track Utiliser la piste principale MusEGui::VisibleTracks Show wave tracks Afficher les pistes wave Show group tracks Afficher les pistes groupe Show aux tracks Afficher les pistes aux Show input tracks Afficher les pistes d'entrée Show output tracks Afficher les pistes de sortie Show midi tracks Afficher les pistes midi Show synth tracks Afficher les pistes synthé Visible track types Types de piste visibles MusEGui::WTScale bar scale taille de la mesure MusEGui::WaveCanvas Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help. Adjust Wave Offset Ajuster le décalage Wave Wave offset (frames) Décalage Wave (trames) Part creation failed Echec de la création de la partie Left and right position markers must be placed inside the current part. La position des marqueurs gauche et droite doit être placé à l'intérieur de la partie courante. Part created Partie créée The selected region has been copied to the clipboard and can be pasted in the arranger. La région sélectionnée a été copiée dans le presse-papier et peut être collée dans l'arrangeur. MusE - external editor failed MusE - échec de lancement de l'éditeur externe MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE n'a pas été capable de lancer l'éditeur externe vérifier que le réglage de l'éditeur dans: Réglages Globaux->Audio:Editeur Wave Externe est réglé sur un éditeur valide. MusE - file size changed MusE - la taille du fichier a changé When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Quand on édite via l'éditeur externe - vous ne devriez pas changer la taille du fichier car il doit tenir dans la région sélectionnée. Les données manquante sont rendues muettes Wave event: Converter settings MusEGui::WaveEdit &Edit &Editer Func&tions Fonc&tions &Gain &Gain Other Autre &Copy &Copier &Create Part from Region &Créer une Partie à partir de la Région C&ut Co&uper &Paste Co&ller Edit in E&xternal Editor Editer dans un éditeur e&xterne Adjust wave offset... Ajuster le décalage wave... Edit in E&xternal Editor... Mute Selection Rendre la sélection muette Normalize Selection Normaliser la sélection Fade In Selection Faire un fade in sur la sélection Fade Out Selection Faire un fade out sur la sélection Reverse Selection Inverser la sélection Select Sélectionner Select &All Sélectionner &Tout &Deselect All &Dé-sélectionner tout &Previous Part &Partie Précédente &Next Part Partie Suiva&nte &Display Display menu: View-specific display options. Window &Config &Configuration de la Fenêtre &Event Color Couleur d'&Evènement &Part colors Couleurs de &partie &Gray &Gris WaveEdit tools Outils WaveEdit Solo Solo Cursor Curseur MusEGui::WaveView MusE - external editor failed MusE - échec de lancement de l'éditeur externe MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE n'a pas été capable de lancer l'éditeur externe vérifier que le réglage de l'éditeur dans: Réglages Globaux->Audio:Editeur Wave Externe est réglé sur un éditeur valide. MusE - file size changed MusE - la taille du fichier a changé When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Quand on édite via l'éditeur externe - vous ne devriez pas changer la taille du fichier car il doit tenir dans la région sélectionnée. Les données manquante sont rendues muettes OrganGuiBase MusE: Organ MusE: Orgue O-1 O-1 Oscillator Oscillateur Brass Cuivres Reed Anche Flute Flûte Drawbars Grands Tuyaux 16' 16' 4' 4' 2 2/3' 2 2/3' 2' 2' 8' 8' 5 1/3' 5 1/3' Envelope Lo Enveloppe basse (Lo) Envelope Hi Enveloppe Haute (Hi) Release Relâchement Sustain Tenue Decay Affaiblissement Attack Attaque ms ms cB cB Velocity Vélocité PasteDialogBase MusE: Paste Parts MusE: Coller des Parties Number and raster Nombre et rendu insert insérer times fois raster rendu Insert Insérer Raster Rendre ticks coches Move, Merge, Clone Déplacer, Fusionner, Clôner Move everything to the right Déplacer tout vers la droite Move only affected parts to the right Déplacer uniquement les parties affectées vers la droite Put everything into a single track Mettre tout dans une seule piste Merge with existing parts Fusionner avec des parties existantes Insert as clones (where possible) Insérer comme clônes (où c'est possible) OK OK Cancel Annuler PasteEventsDialogBase MusE: Paste Events MusE: Coller des Evènements Number and raster Nombre et rendu insert Insérer times fois raster rendu ticks coches Paste options Options de collage &Always into existing parts &Never into existing parts &Into existing parts if part has not to be expanded by more than Always into existing parts Toujours dans des parties existantes Never into existing parts Jamais dans des parties existantes Into existing parts if part has not to be expanded by more than Dans des parties existantes si la partie ne doit pas être étendue de plus de Insert Insérer Raster Rendre Put everything into the (selected) part Mettre tout dans la partie (sélectionnée) Paste midi controller target options Keep e&xisting Erase existin&g Erase WYSIWYG Erase inclusive OK OK Cancel Annuler PluginDialogBase Dialog Dialogue Ports: Mono Mono Mono + Stereo Stereo Stéréo All Tous Plugin type: Filter: &OK &Cancel PluginSettings Dialog Dialogue Options Fixed speed Transport affects audio latency Override reported audio latency Reported audio latency override value Revert HiDPI scaling of native GUI Set in Global settings->GUI tweaks Global setting Additional information Follow global setting On On Off Off ProjectCreate Create Project Créer un Projet Projects folder: Répertoire projets: ... ... Project Name: Nom de projet: Project is a Template Le projet est un modèle Write window state Ecrire l'état de la fenêtre Project song file type: Type de fichier project chanson: Project Path to song file: Chemin du projet vers le fichier chanson: &Create project folder (recommended for audio projects) &Créer un répertoire projet (recommandé pour les projets audio) Create project folder (recommended for audio projects) Créer le répertoire projet (recommandé pour les projets audio) Song information: Information de la chanson: QObject Error Erreur Please first select the range for crescendo with the loop markers. Veuillez sélectionner une plage pour le crescendo avec les marqueurs de boucle. Controller ! Other ! Presets Pré-réglages Switch on General Midi Level 1 mode Switch on General Midi Level 2 mode Switch off General Midi Level 1 or 2 Switch on Roland GS mode Switch on Yamaha XG mode Tracks: Midi ports: Midi devices: Jack: Jack midi: Instruments Current device Others Autres Files Synthesizers Open Ouvrir Closed <Properties> <Base Property> <Property> Input routing Warning: No input routes! Click to connect... Output routing Warning: No output routes! Click to connect... TRACK TAKE MusE import error. MusE failed to import the file. Possibly this wasn't a sound file? If it was check the permissions, MusE sometimes requires write access to the file. FX slot QWidget Cannot convert sysex string Ne peut convertir le code sysex Hex String too long (2048 bytes limit) Code Hexa trop long (limite: 2048 octets) generic midi MIDI générique new nouveau create peakfile for créer fichier de crêtes pour MusE: get file name MusE: avoir le nom du fichier The directory %1 does not exist. Create it? Le répertoire %1 n'existe pas. Le créer? MusE: create directory MusE: créer un répertoire creating dir failed échec de la création du répertoire File %1 exists. Overwrite? Le fichier %1 existe. L'écraser? Open File %1 failed: %2 L'ouverture du fichier %1 a échoué: %2 MusE: write MusE: écriture Creating dir failed MusE: Open File MusE: Ouvrir un Fichier None Rien No wave events selected. Pas d'évènements wave sélectionnés. No selection. Ignoring Pas de sélection. On ignore Instrument-defined Instrument-défini Generic midi Edit instrument ... Editer l'instrument ... Add Ajouter Others Autres Common Controls Contrôles Communs Velocity Vélocité Create peakfile for New Nouveau QuantBase MusE: Quantize MusE: Quantifier Range Plage All Events Tous les Evènements Selected Events Evènements sélectionnés Looped Events Evènements bouclés Selected Looped Sélectionné bouclé Parts Parties Selected parts or c&urrent part All parts &in editor Values Valeurs Strength: Force: % % Threshold (ticks): Seuil (coches): Quantize Len Quantifier Longueur Raster Rendre Whole Entièrement Half Moitié 4th Noire 4th Triplet Triolet de Noire 8th Croche 8th Triplet Triolet de croche 16th Double croche 16th Triplet Triolet de double croche 32th Triple croche 32th Triplet Triolet de triple croche Swing: Swing: If the proposed change in tick or length is smaller than threshold, nothing is done. If swing=0, this is normal If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. Si le changement proposé en coche ou longueur est plus petit que le seuil, rien n'est fait. Si swing=0, c'est normal Si swing vaut 33, vous obtenez un rythme 2:1. Si swing vaut -33, vous obtenez un rythme 1:2. OK OK Cancel Annuler RemoveBase MusE: Erase Notes MusE: Effaçage de Notes Range Plage All Events Tous les Evènements Selected Events Evènements choisis Looped Events Evènements bouclés Selected Looped Sélectionné Bouclé All Eve&nts Selec&ted Events &Looped Events Selected L&ooped Parts Parties Selected parts or c&urrent part All parts &in editor Thresholds Seuils ticks coches Velocity Vélocité Length Longueur <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si rien n'est coché, tout sera supprimé.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si la vélocité est cochée, seul les notes ayant velo &lt; seuil seront supprimées.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Si tous est coché, les notes ayant velo &lt; seuil OU ayant une longueur &lt; seuil seront supprimées.</p></body></html> OK OK Cancel Annuler RhythmBase MusE: Random Rhythm Generator MusE: Générateur de Rythme Aléatoire Instrument Properties Propriétés d'Instrument counts/bar décomptes/mesure steps/count pas/décompte # bars # mesures test teste contrib contribution randomize randomiser Group 1 Groupe 1 Group 2 Groupe 2 Group 3 Groupe 3 Group 4 Groupe 4 Group 5 Groupe 5 listen écouter Instrument Instrument Group Groupe create new entry créer une nouvelle entrée pressing the New button you create a new entry in the MusE list of defined controllers en pressant le bouton Nouveau, vous créez une nouvelle entrée dans la liste MusE des contrôleurs définis &New &Nouveau delete selected entry supprimer l'entrée sélectionnée &Delete &Supprimer Up Haut Down Bas list of defined controllers liste des contrôleurs définis This is the MusE list of defined controllers. Ceci est la liste MusE des contrôleurs définis. Instrument Instrument steps/count pas/décompte <b>Notice!</b><br> Random Rhythm Generator is not enabled yet! <b>Remarque!</b><br> Le Générateur de Rythme Aléatoire n'est pas encore activé! Tools Outils &File &Fichier &Edit &Editer &Help &Aide New Nouveau Ctrl+N Ctrl+N &Open... &Ouvrir... Open Ouvrir Ctrl+O Ctrl+O &Save &Sauvegarder Save Sauvegarder Ctrl+S Ctrl+S Save &As... Sauvegarder &sous... Save As Sauvegarder sous &Print... Im&primer... Print Imprimer Ctrl+P Ctrl+P E&xit &Quitter Exit Quitter &Undo Ann&uler Undo Annuler Ctrl+Z Ctrl+Z &Redo &Refaire Redo Refaire Ctrl+Y Ctrl+Y &Cut &Couper Cut Couper Ctrl+X Ctrl+X C&opy C&opier Copy Copier Ctrl+C Ctrl+C &Paste Co&ller Paste Coller Ctrl+V Ctrl+V &Find... C&hercher... Find Chercher Ctrl+F Ctrl+H &Contents... &Contenus... Contents Contenus &Index... &Index... Index Index &About... &A propos... About A propos RhythmGenerator Form3 Form3 Instrument Settings: Réglages d'instrument: Instrument Instrument Hi-Hat Hi-Hat add ajouter delete supprimer steps/count pas/décompte count/bar décompte/mesure # bars # mesures Group Settings: Réglages de Groupe: Group Groupe Group 1 Groupe 1 Group 2 Groupe 2 Group 3 Groupe 3 Group 4 Groupe 4 Group 5 Groupe 5 contrib contribution listen écouter Randomize Randomiser Rhythm Style: Style de Rythme: Clear Nettoyer Open... Ouvrir... Save Sauvegarder Save as... Sauvegarder sous... Generate Générer Close Fermer RouteDialogBase MusE: Routing MusE: Routage List of available sources. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Connections View window. Shows all current connections. Thick lines are Omni Routes. Thin lines are Channel Routes. List of available destinations. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Itemized list of current connections. Show only selected sources Show only destination routes for selected source Show only selected destinations Show only source routes for selected destination Show all Midi Ports Auto adjust column size Automatically adjusts the source and destination tree widths when the splitters are adjusted. This also turns on text word wrap, which may cause slower response with larger lists. Preferred route name or alias Connect source to destination Remove selected route Add Route Ajouter une route Source: Source: Destination: Destination: connect source to destination connecter la source à la destination Connect Connecter Current Routes Routes courantes Source Source Destination Destination remove selected route supprimer la route sélectionnée Remove Supprimer RubberbandSettingsBase Rubberband Settings Rubberband support is not enabled Use default settings Presets: Pré-réglages: Default Percussion Maximum Stretch: Elastic Precise Window: Standard Standard Short Long Smoothing: Off Off On On Channels: Apart Together Transients: Crisp Mixed Smooth Detector: Compound Percussive Soft Phase: Laminar Independent Formant: Shifted Preserved Pitch: Ton: High speed High quality High consistency OK OK Cancel Annuler SRCResamplerSettingsBase SRC Resampler Settings Use default settings Converter Type: SINC Best quality SINC Medium quality SINC Fastest Zero Order Hold Linear OK OK Cancel Annuler SS_PluginChooserBase SimpleDrums - Ladspa Plugin Chooser BatterieSimple - Sélecteur de Greffon Ladspa Name Nom Label Label Inports Ports d'entrée Outports Ports de sortie Creator Créateur &Cancel &Annuler Alt+C Alt+A &OK &OK Alt+O Alt+O SS_PluginFront Clear and unload effect Nettoyer et enlever l'effet Load effect Charger l'effet Toggle display of effect parameters Basculer l'affichage des paramètres de l'effet Turn effect on/off Basculer l'effet entre on/off SetlenBase MusE: Set Note Length MusE: Régler la Longueur de Note Range Plage All Events Tous les Evènements Selected Events Evènements Sélectionnés Looped Events Evènements Bouclés Selected Looped Sélectionné Bouclé Parts Parties Selected parts or c&urrent part All parts &in editor Value Valeur New length Nouvelle longueur ticks coches OK OK Cancel Annuler ShortcutCaptureDialogBase Enter shortcut sequence Entrez la séquence du raccourci Press keys to enter shortcut sequence! Appuyez sur les touches pour entrer la séquence de raccourci! Old shortcut: Ancien raccourci: Undefined Non-définie New shortcut: Nouveau raccourci: OK OK Cancel Annuler ShortcutConfigBase Configure Keyboard Shortcuts Configurer les Raccourcis Clavier Shortcut Category Catégorie du Raccourci Description Description Shortcut Raccourci Filter: &Reset all &Clear &Nettoyer Alt+C Alt+N &Define &Définir Alt+D Alt+D &Apply &Appliquer Alt+A Alt+A &Printable file... Fichier im&primable... Alt+P Alt+P &Ok &Ok Alt+O Alt+O SimpleDrumsGuiBase DrumSynth 0.1 DrumSynth 0.1 Audio file assignments. Within parantesis: note number and traditional midi drum name SimplePluginChooserBase SimpleDrums - Ladspa Plugin Chooser BatterieSimple - Sélecteur de Greffon Ladspa Name Nom Label Label Inports Ports d'entrée Outports Ports de sortie Creator Créateur &Cancel Alt+C &OK Alt+O Alt+O SimpleSynthGui Mix Mix Chn Channel routing &Load setup &Charger la configuration &Save setup &Sauvegarder la configuration Load sample dialog Dialogue de chargement d'échantillon SnooperDialogBase Snooper Mode Auto-hide Separate parented top levels Update tree Mouse clicks Key presses Flash timer s Reset Filters MusEGui QWidget Properties Propriétés Search: Class name: Object name: About A propos This tool helps developers and designers discover objects. Click on anything anywhere or press a key to highlight an item. It will NOT auto-update when new objects are created. Click 'Update tree'. When open, may slow program slightly. This dialog is NOT shown. Class name::Object name Property Type Type Value Valeur Event type SongInfo Song Information Information de la Chanson Show on song load Afficher lors du chargement de la chanson &Cancel &Annuler Alt+C Alt+A &Ok &Ok Alt+O Alt+O SynthConfigBase Soft Synthesizer Synthétiseur Soft File Fichier Instances Instances Name Nom list of available software synthesizers liste des synthétiseurs softs disponibles Add Instance Ajouter une Instance Remove Instance Retirer une Instance Midi Port Port MIDI Midi Port and Soft Synth Configuration Port Midi et Configuration du Synthé Soft Type Type Midi connections Connections midi Configure midi devices, midi ports, and synthesizers Available soft synthesizers List of available software synthesizers Inst Inst Version Version Description Description Useable devices Add: Synth Synthé ALSA JACK Rename Remove Supprimer Device port assignments &Apply &Appliquer &OK &OK TimeCanvas Ctrl Ctrl Add Controller View Ajouter une Vue Contrôleur TransposeBase MusE: Transpose MusE: Transpose Range Plage All Events Tous les Evènements Selected Events Evènements Sélectionnés Looped Events Evènements Bouclés Selected Looped Sélectionné Bouclé Parts Parties Selected parts or c&urrent part All parts &in editor Value Valeur Halftone-steps Pas de demi-tons OK OK Cancel Annuler UnusedWaveFiles Dialog Dialogue List of unused audio files in current project directory: Liste des fichiers audio non-utilisés dans le répertoire courant du projet: Current project Projet courant All .med files in current directory Tous les fichiers .med dans le répertoire courant Move files to 'unused' subdir Déplacer les fichiers vers le sous-répertoire 'non-utilisé' Cancel Annuler VAMGui MusE: Load VAM Presets MusE: Charger les pré-réglages VAM MusE: Save VAM Presets MusE: Sauvegarder les pré-réglages VAM VAMGuiBase Virtual Analogue for MusE Synthé Analogique Virtuel pour MusE LFO LFO Freq Fréq Waveform Forme d'Onde Sine Sinusoïde Pulse Impulsion Saw Dents-de-scie Triangle Triangle Filter Filtre EnvMod ModEnv Attack Attaque Decay Affaiblissement Sustain Maintien Release Relâchement Cutoff Coupure Resonance Résonance Invert Inverser KeyTrack PisteClef Presets Pré-réglages Set Set load preset list charger la liste des pré-réglages save preset list sauvegarder la liste des pré-réglages save preset list to a new file sauvegarder la liste des pré-réglages dans un nouveau fichier delete preset supprimer le pré-réglage DCO 1 DCO 1 Pitch Ton Detune Désaccordage PWM PWM FM FM PW PW DCO 2 DCO 2 On On VAM 1.0beta3 Virtual Analog for MusE Released under GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VAM 1.0beta3 Virtual Analog for MusE Distribué sous la licence GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VelocityBase MusE: Modify Velocity Muse: Modifier la Vélocité Range Plage All Events Tous les Evènements Selected Events Evènements Sélectionnés Looped Events Evènements Bouclés Parts Parties Selected parts or c&urrent part All parts &in editor Values Valeurs Rate: Vitesse: Offset: Décalage: % % Selected Looped Sélectionné Bouclé veloNew = (veloOld * rate) + offset NouvVélo = (AncVélo * Vitesse) + décalage OK OK Cancel Annuler ZitaResamplerSettingsBase Zita Resampler Settings Zita Resampler support is not enabled Use default settings Options: OK OK Cancel Annuler file_patterns Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) All Files (*) Tous les Fichiers (*) Midi (*.mid) Midi (*.mid) Karaoke (*.kar) Karaoke (*.kar) all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) tous les fichiers connus (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) med Files (*.med *.med.gz *.med.bz2) Fichiers med (*.med *.med.gz *.med.bz2) Uncompressed med Files (*.med) Fichiers med non-compressés (*.med) gzip compressed med Files (*.med.gz) Fichiers med compressés par gzip (*.med.gz) bzip2 compressed med Files (*.med.bz2) Fichiers med compressés par bzip2 (*.med.bz2) mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) Fichiers mid (*.mid *.midi *.kar *.MID *.MIDI *.KAR) (*.jpg *.gif *.png) (*.jpg *.gif *.png) (*.jpg) (*.jpg) (*.gif) (*.gif) (*.png) (*.png) part Files (*.mpt *.mpt.gz *.mpt.bz2) Fichiers parties (*.mpt *.mpt.gz *.mpt.bz2) part Files (*.mpt) Fichiers parties (*.mpt) gzip compressed part Files (*.mpt.gz) Fichiers parties compressés par gzip (*.mpt.gz) bzip2 compressed part Files (*.mpt.bz2) Fichiers parties compressés par bzip2 (*.mpt.bz2) Presets (*.pre *.pre.gz *.pre.bz2) Pré-réglages (*.pre *.pre.gz *.pre.bz2) Presets (*.pre) Pré-réglages (*.pre) gzip compressed presets (*.pre.gz) Pré-réglages compressés par gzip (*.pre.gz) bzip2 compressed presets (*.pre.bz2) Pré-réglages compressés par bzip2 (*.pre.bz2) Presets (*.map *.map.gz *.map.bz2) Pré-réglages (*.map *.map.gz *.map.bz2) Presets (*.map) Pré-réglages (*.map) gzip compressed presets (*.map.gz) Pré-réglages compressés par gzip (*.map.gz) bzip2 compressed presets (*.map.bz2) Pré-réglages compressés par bzip2 (*.map.bz2) Wave/Binary (*.wav *.ogg *.flac *.bin) Wave/Binaire (*.wav *.ogg *.flac *.bin) Wave (*.wav *.ogg *.flac) Wave (*.wav *.ogg *.flac) Color configuration files (*.cfc) Qt style sheets (*.qss) Feuilles de style Qt (*.qss) Wave/Binary (*.wav *.ogg *.bin) Wave/Binaire (*.wav *.ogg *.bin) Wave (*.wav *.ogg) Wave (*.wav *.ogg) Binary (*.bin) Binaire (*.bin) freeverb FreeVerb FreeVerb Room Size Dimension de la chambre Damping Atténuation Wet Level Niveau Wet Tap-Reverberator Tap-Reverberator Decay [ms] Décroissance [ms] dB dB Dry Level [dB] Niveau Dry [dB] Wet Level [dB] Niveau Wet [dB] Preset: Pré-réglage: AfterBurn PostCombustion AfterBurn (Long) PostCombustion (Long) Ambience Ambiance Ambience (Thick) Ambiance (Epais) Ambience (Thick) - HD Ambiance (Epais) - HD Cathedral Cathédrale Cathedral - HD Cathédrale - HD Drum Chamber Chambre de Batterie Garage Garage Garage (Bright) Garage (Clair) Gymnasium Gymnase Gymnasium (Bright) Gymnase (Clair) Gymnasium (Bright) - HD Gymnase (Clair) - HD Hall (Small) Hall (Petit) Hall (Medium) Hall (Moyen) Hall (Large) Hall (Grand) Hall (Large) - HD Hall (Grand) - HD Plate (Small) Plaque (Petite) Plate (Medium) Plaque (Moyenne) Plate (Large) Plaque (Grand) Plate (Large) - HD Plaque (Grande) - HD Pulse Chamber Chambre d'Impulsion Pulse Chamber (Reverse) Chambre d'Impulsion (Inversé) Resonator (96 ms) Résonateur (96 ms) Resonator (152 ms) Résonateur (152 ms) Resonator (208 ms) Résonateur (208 ms) Room (Small) Pièce (Petite) Room (Medium) Pièce (Moyenne) Room (Large) Pièce (Grande) Room (Large) - HD Pièce (Grande) - HD Slap Chamber Chambre Slap Slap Chamber - HD Chambre Slap - HD Slap Chamber (Bright) Chambre Slap (Claire) Slap Chamber (Bright) HD Chambre Slap (Claire) - HD Smooth Hall (Small) Hall Lisse (Petit) Smooth Hall (Medium) Hall Lisse (Moyen) Smooth Hall (Large) Hall Lisse (Grand) Smooth Hall (Large) - HD Hall Lisse (Grand) - HD Vocal Plate Plaque Vocale Vocal Plate - HD Plaque Vocale - HD Warble Chamber Chambre Warble Warehoouse Entrepôt Warehouse - HD Entrepôt - HD Comb Filters Filtre Comb Allpass Filters Filtres Passe-tout Bandpass Filters Filtres Passe-bande Enhanced Stereo Stéréo Améliorée main File to open Alsa midi only (using dummy audio driver) Use RtAudio driver Use JAckAudio driver to connect to Jack audio server Do not try to auto-start the Jack audio server Do not auto-populate midi ports with midi devices found, at startup Force inclusion of ALSA midi even if using Jack Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) Force midi real time priority to n (default: audio driver prio -1) Force plugin cache re-creation. (Automatic if any plugin path directories changed.) Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care. Don't load LADSPA plugins Don't load MESS plugins Don't load VST plugins Don't load LinuxVST plugins Don't load DSSI plugins Don't load LV2 plugins Don't use LASH Force locale to the given language/country code (xx = Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins. Debug mode: no threads, no RT Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively! Debug mode: trace midi Input Debug mode: trace midi Output Debug mode: trace sync Enable Python control support Pyro nameserver host name Pyro nameserver host port Pyro daemon host name Pyro daemon host port Pyro communication timeout in seconds midiWarnInitPendingBase Instrument initialization Initialisation de l'instrument MusE should now send some Instrument Initialization Sequences. The sequences (usually System Exclusive messages) are defined by the selected instruments in the Settings -> Midi Ports dialog, such as the GM (default), GS, or XG instruments. Typically you should answer yes here. You can always do it manually from the Midi menu. Continue? MusE devrait maintenant envoyer des Séquences d'Initialisation d'Instrument. Les séquences (habituellement des Messages Systèmes exclusifs) sont définis via les instruments sélectionnés dans Réglages -> Dialogue Ports Midi Ports, comme les instruments GM (défaut), GS, ou XG. Typiquement, vous devriez répondre oui ici. Vous pouvez toujours le faire manuellement à partir du menu Midi. Continuer? Don't ask me again Ne plus me redemander shortcuts Transport: Start playback from current location Transport: Démarrer la lecture à partir de la position courante Transport: Toggle metronome Transport: Basculer le métronome Transport: Stop Playback Transport: Arrêter la lecture Transport: Goto Start Transport: Aller au début Transport: Play, Stop, Rewind Transport: Jouer, Stop, Rembobiner Transport: Goto left marker Transport: Aller au marqueur gauche Transport: Goto right marker Transport: Aller au marqueur droit Transport: Toggle Loop section Transport: Basculer la section Boucle Transport: Toggle Record Transport: Basculer l'Enregistrement Transport: Clear all rec enabled tracks Transport: Nettoyer toutes les pistes ayant l'enregistrement activé Toggle fullscreen Basculer en plein écran Toggle tabbed/floating editor window Toggle docks display Show Arranger window Edit: Copy Editer: Copier Edit: Copy in range Editer: Copier dans la plage Edit: Undo Editer: Défaire Edit: Redo Editer: Refaire Edit: Cut Editer Couper Edit: Paste Editer: Coller Edit: Paste to selected track Editer: Coller dans la piste sélectionnée Edit: Paste clone Editer: Coller clône Edit: Paste clone to selected track Editer: Coller clône dans la piste sélectionnée Edit: Paste (with dialog) Editer: Coller (avec dialogue) Edit: Delete Editer: Supprimer File: New project Fichier: Nouveau projet File: Open from disk Fichier: Ouvrir à partir du disque File: Save project Fichier: Sauvegarder le projet File: Open recent file Fichier: Ouvrir un fichier récent File: Save as Fichier: Sauvegarder sous File: Load template Fichier: Charger un modèle File: Import midi file Fichier: Importer un fichier midi File: Export midi file Fichier: Exporter un fichier midi File: Import midi part Fichier: Importer une partie midi File: Import audio file Fichier: Importer un fichier audio File: Quit MusE Fichier: Quitter MusE Edit: Select parts on track Editer: Sélectionner des parties sur une piste Open pianoroll Ouvrir la vue piano Open drumeditor Ouvrir l'éditeur de batterie Open listeditor Ouvrir l'éditeur de liste Open waveeditor Ouvrir l'éditeur de forme d'onde Open graphical mastertrack editor Ouvrir l'éditeur graphique de la piste principale Open list mastertrack editor Ouvrir l'éditeur de liste de la piste principale Open midi transformer Ouvrir le transformateur midi Open track Synth GUI Open pianoroll in new window Open drumeditor in new window Open listeditor in new window Open waveeditor in new window Add midi track Ajouter une piste midi Add drum track Ajouter une piste de batterie Add new style drum track Ajouter une piste de batterie nouveau style Add wave track Ajouter une piste wave Add audio output Ajouter une sortie audio Add audio group Ajouter un groupe audio Add audio input Ajouter une entrée audio Add audio aux Ajouter un aux audio Insert midi track Insert drum track Insert wave track Insert audio output Insert audio group Insert audio input Insert audio aux Structure: Global cut Structure: Couper global Structure: Global insert Structure: Insertion globale Structure: Global split Structure: Diviser global Structure: Cut events Structure: Couper les évènements View: Open mixer #1 window Vue: Ouvrir la fenêtre mixeur #1 View: Open mixer #2 window Vue: Ouvrir la fenêtre mixeur #2 View: Toggle transport window Vue: Basculer la fenêtre transport View: Toggle bigtime window Vue: Basculer la fenêtre grosse horloge View: Open marker window Vue: Ouvrir la fenêtre marqueur View: Toggle mixer strip display Settings: Follow song by page Réglages: Suivre la chanson par page Settings: Follow song off Réglages: Suivre la chanson off Settings: Follow song continuous Réglages: Suivre la chanson continue Settings: Global configuration Réglages: Configuration globale Settings: Configure shortcuts Réglages: Configuration des raccourcis Transport: Goto End Transport: Restart recording Transport: Restart recording (multi take) Transport: Arm current track Toggle rewind on stop Edit: Paste to current part File: New project from template Edit: Rename part Settings: Configure metronome Réglages: Configuration du métronome Settings: Midi sync configuration Réglages: Configuration de la synchro midi Settings: Midi file import/export configuration Réglages: Configuration de l'import/export d'un fichier midi Settings: Appearance settings Réglages: Réglages de l'apparence Settings: Midi ports / Soft Synth Réglages: Ports midi / Synthé Soft Settings: Audio subsystem configuration Réglages: Configuration du sous-système audio Midi: Edit midi instruments Midi: Editer les instruments midi Midi: Open midi input transform Midi: Ouvrir la transformation de l'entrée midi Midi: Open midi input filter Midi: Ouvrir le filtre d'entrée midi Midi: Midi input transpose Midi: Transposition de l'entrée midi Midi: Midi remote control Midi: Télécommande midi Midi: Random rhythm generator Midi: Générateur de rythme aléatoire Midi: Reset midi Midi: Réinitialiser midi Midi: Init midi Midi: Initialiser midi Midi: Midi local off Midi: Midi local off Audio: Bounce audio to track Audio: Faire rebondir l'audio vers la piste Audio: Bounce audio to file Audio: Faire rebondir l'audio vers un fichier Audio: Restart audio Audio: Redémarrer l'audio Automation: Mixer automation Automation: Mixeur d'automation Automation: Take mixer snapshot Automation: Prendre un cliché du mixeur Automation: Clear mixer automation Automation: Nettoyer le mixeur d'automation Help: Open Manual Aide: Ouvrir le Manuel Help: Toggle whatsthis mode Aide: Basculer le mode qu'est-ce que c'est Edit: Edit selected part Editer: Editer la partie sélectionnée Edit: Select nearest part on track above Editer: Sélectionner la partie la plus proche sur la piste du dessus Edit: Add nearest part on track above Editer: Ajouter la partie la plus proche sur la piste du dessus Edit: Select nearest part on track below Editer: Sélectionner la partie la plus proche sur la piste du dessous Edit: Add nearest part on track below Editer: Ajouter la partie la plus proche sur la piste du dessous Edit: Insert empty measure Editer: Ajouter une mesure vide Edit: Duplicate track Select track above Sélectionner la piste du dessus Select track below Sélectionner la piste du dessous Edit selected track name Mute all currently selected tracks Solo all currently selected tracks Midi: Transpose Midi: Transposer Edit: Select all Editer: Sélectionner tout Edit: Select none Editer: Ne rien sélectionner Edit: Invert Selection Editer: Inverser la sélection Edit: Select events/parts inside locators Editer: Sélectionner les évènements/parties à l'intérieur des positionneurs Edit: Select events/parts outside locators Editer: Sélectionner les évènements/parties à l'extérieur des positionneurs Edit: Select previous part Editer: Sélectionner la partie précédente Edit: Select next part Editer: Sélectionner la partie suivante Edit: Select nearest part/event to the left or move cursor Editer: Sélectionner la partie/évènement la plus proche à gauche ou déplacer le curseur Edit: Add nearest part/event to the left to selection Editer: Sélectionner la partie/évènement la plus proche à gauche de la sélection Edit: Select nearest part/event to the right or move cursor Editer: Sélectionner la partie/évènement la plus proche à droite ou déplacer le curseur Edit: Add nearest part/event to the right to selection Editer: Sélectionner la partie/évènement la plus proche à droite de la sélection Edit: Set locators to selection Editer: Régler les positionneurs sur la sélection Edit: Increase pitch Editer: incrémenter le ton Edit: Decrease pitch Editer: Décrémenter le ton Edit: Increase event position Editer: Incrémenter la position de l'évènement Edit: Decrease event position Editer: Décrémenter la position de l'évènement View: Zoom in Vue: Zoomer à l'intérieur View: Zoom out Vue: Zoomer à l'extérieur View: Goto Current Position Vue: Aller à la position courante View: Scroll left Vue: Faire défiler à gauche Transport: Step record Transport: Midi input Transport: Play events Edit: Increase velocity Edit: Decrease velocity Edit: Set Fixed Length on Midi Events Editer: Régler des longueurs fixées sur des Evènements Midi Quantize Quantifier Modify Note Length Modifier la Longueur de Note Modify Velocity Modifier la Vélocité Edit: Crescendo Editer: Crescendo Edit: Thin Out Editer:Eclaircir Edit: Erase Event Editer: Supprimer l'Evènement Edit: Delete Overlaps Editer: Supprimer les Recouvrements Edit: Note Shift Editer: Décalage de Note Edit: Move Clock Editer: Déplacer l'Horloge Edit: Copy Measure Editer: Copier la Mesure Edit: Erase Measure Editer: Effacer la Mesure Edit: Delete Measure Editer: Supprimer la Mesure Edit: Create Measure Editer: Créer la Mesure Edit: Change Event Color Editer: Changer la Couleur de l'Evènement Move: Move to selected note Tool: Pointer Outil: Pointeur Tool: Pencil Outil: Crayon Tool: Eraser Outil: Effaceur Tool: Pan Outil: Panoramique Tool: Zoom Outil: Zoom Tool: Line Draw Outil: Dessine une Ligne Tool: Cursor Outil: Curseur Add note velocity 1 Ajouter la vélocité de note 1 Add note velocity 2 Ajouter la vélocité de note 2 Add note velocity 3 Ajouter la vélocité de note 3 Add note velocity 4 Ajouter la vélocité de note 4 Cursor step size: larger Taille du pas du curseur: plus large Cursor step size: smaller Taille du pas du curseur: plus petit Instrument/Cursor up Instrument/Curseur haut Instrument/Cursor down Instrument/Curseur bas Tool: Range Outil: Plage Tool: Stretch Tool: Sample rate Tool: Scissor Outil: Ciseaux Tool: Glue Outil: Colle Tool: Mute Outil: Muet Transport: Increase current position Transport: Incrémenter la position courante Transport: Decrease current position Transport Décrémenter la position courante Transport: Increase current position, no snap Transport: Incrémenter la position courante, pas d'alignement Transport: Decrease current position, no snap Transport: Décrémenter la position courante, pas d'alignement Quantize: Set quantize to off Quantize: Set quantize to 1/1 note Quantifier: Régler la quantification sur 1/1 de note Quantize: Set quantize to 1/2 note Quantifier: Régler la quantification sur 1/2 de note Quantize: Set quantize to 1/4 note Quantifier: Régler la quantification sur 1/4 de note Quantize: Set quantize to 1/8 note Quantifier: Régler la quantification sur 1/8 de note Quantize: Set quantize to 1/16 note Quantifier: Régler la quantification sur 1/16 de note Quantize: Set quantize to 1/32 note Quantifier: Régler la quantification sur 1/32 de note Quantize: Set quantize to 1/64 note Quantifier: Régler la quantification sur 1/64 de note Quantize: Toggle triol quantization Quantifier: Basculer la quantification des triolets Quantize: Toggle punctuation quantization Quantifier: Basculer la quantification de la ponctuation Quantize: Toggle punctuation quantization (2) Quantifier: Basculer la quantification de la ponctuation (2) Edit: Insert at location Editer: Insérer à la position Edit: Increase length Editer: Incrémenter la longueur Edit: Decrease length Editer: Décrémenter la longueur Insert Note Insérer Note Insert SysEx Insérer SysEx Insert Ctrl Insérer Ctrl Insert Meta Insérer Méta Select strip to the left Select strip to the right Mixer strip volume down Mixer strip volume up Mixer strip pan left Mixer strip pan right Mixer strip volume down page Mixer strip volume up page Mixer strip pan left page Mixer strip pan right page Insert Channel Aftertouch Insérer Canal de Pression Aftertouch Insert Key Aftertouch Insérer une note de Pression Aftertouch Insert Tempo Insérer le Tempo Insert Signature Insérer la Signature Change Event Position Changer la Position de l'Evènement Edit Event Value Editer la Valeur de l'Evènement Insert Key Insérer la Clef Settings: Midi ports / Soft synths Goto Next Marker Aller au Marqueur Suivant Goto Prev Marker Aller au Marqueur Précédent Normalize warnBadTimingBase Bad timing Mauvais timing Message here Message ici Don't warn me again Ne plus me prévenir muse-4.0.0/src/share/locale/muse_it.ts000066400000000000000000017107621404105311000176340ustar00rootroot00000000000000 @default Add Midi Track Aggiungi traccia Midi Add Drum Track Aggiungi traccia batteria Add Wave Track Aggiungi traccia audio Add Audio Output Aggiungi uscita audio Add Audio Group Aggiungi gruppo audio Add Audio Input Aggiungi ingresso audio Add Aux Send Aggiungi mandata aux Add Synth Aggiungi Synth Select project directory Scegli directory per il progetto dest dest Warning: No output devices! Avviso: Non ci sono dispositivi d'uscita! Open midi config... Apri configurazione Midi... Empty ports Porte vuote <none> <nulla> Bad timing Timing difettoso AboutBox AboutBox su MusE Version 2 Versione 2 (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer e altri. Visita http://www.muse-sequencer.org a proposito di nuove versioni e informationi aggiuntive. Published under the GNU Public License &Keep On Rocking! &Keep On Rocking! Alt+K Alt+K (C) Copyright 1999-2014 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer e altri. Visita http://www.muse-sequencer.org a proposito di nuove versioni e informationi aggiuntive. Published under the GNU Public License {1999-2014 ?} (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer e altri. Visita http://www.muse-sequencer.org a proposito di nuove versioni e informationi aggiuntive. Published under the GNU Public License {1999-2014 ?} {1999-2015 ?} MusE MusE Internals System information (useful for debugging): (C) Copyright 1999-2020 the MusE development team. See https://muse-sequencer.github.io for new versions and more information. Published under the GNU Public License. AppearanceDialogBase MusE: Appearance settings MusE: Impostazioni relative all'aspetto Arranger Arrangiatore Parts Parti show names mostra nomi show events mostra eventi show Cakewalk Style mostra stile Cakewalk y-stretch Events Eventi note on nota attiva poly pressure controller aftertouch aftertouch pitch bend program change special speciale Background picture Immagine di sfondo add aggiungi remove rimuovi clear cancella show snap grid mostra griglia di allineamento Colors Colori Items Oggetti Palette Tavolozza dei colori add to palette aggiungi a tavolozza B B S S H H V V G G R R Color name: Nome colore: Global opacity Opacità globale Style/Fonts Stile/Caratteri QT Theme Tema QT Windows Windows MusE MusE Metal Metallo Norwegian Wood Legno norvegese Platinum Platino CDE CDE Motif Motif Motif Plus Motif Plus May require restarting MusE for best results Per ottenere risultati ottimali, potrebbe richiedere il riavvio di MusE Style Sheet: Foglio di stile: ... ... Fonts Caratteri Family Famiglia Size Dimensione Font 1 Tipo di carattere 1 Font 2 Tipo di carattere 2 Font 3 Tipo di carattere 3 Font 0 Tipo di carattere 0 Bold grassetto Italic corsivo Font 4 Tipo di carattere 4 Font 5 Tipo di carattere 5 Font 6 Tipo di carattere 6 Apply Applica Ok OK Cancel Annulla Themes Show names Show e&vents Show Ca&kewalk style Wave Drawing in Parts O&nly outline Add Remove Clear Show snap grid Choose color... Add to palette Save all... Load all... Size (pt) At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. Icon size Custom cursor size Distribute current system font family to all other fonts System Font sizes for fonts 1-6 are determined at every program start, to optimally match the currently used system font. Disable if you want to set fixed sizes in the font settings below. Default scaling factors: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% Calculate and apply relative font sizes dynamically at program start Maximum aliased size, 0 = no alias Insert Fill in optimal font sizes in relation to the system font RMS/PEAK (&traditional) Current theme If a user style sheet for one of the MusE color schemes exists and is selected, it will be merged with the default style sheet for this scheme. The user style sheet takes precedence and overwrites identical settings in the default style sheet. Cascade default and user theme style sheets Show icons in menus Use native standard dialogs Help ArrangerColumnsBase Configure arranger columns Configura colonne arrangiatore Columns: Colonne: Name: Nome: Controller type: Tip controllo: Midi controller type Tipo controllo Midi Control7 Control7 Control14 Control14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Tono Program Programma H-Ctrl H-Ctrl Midi controller number high byte Numero controller Midi (byte alto) L-Ctrl L-Ctrl Midi controller number low byte Numero controller Midi (byte basso) * wild card * affect CCs at influenza CC begin of song all'inizio della canzone current position alla posizione corrente &Add &Aggiungi &Delete &Cancella Done Fatto Affect CCs at AudioConverterSettingsBase Audio Converter Settings Available audio converters Offline settings... Realtime settings... Gui settings... Use default preferences (see Global Settings) Preferences When only samplerate conversion is required, use: When time stretching or pitch shifting is required, use: OK OK Cancel Annulla Awl::MidiVolEntry off spento db db Awl::VolEntry off spento ChooseSysexBase Dialog Hex: Comment: &OK &Cancel A&nnulla ClipListEditorBase MusE: ClipList MusE: Lista clip Name Nome Refs Rif Samplerate Frequenza di campionamento Len Lun Data Dati Clip Properties Proprietà Clip Pos: Pos: Len: Lun: Status CommentBase Form1 Modulo1 Track Comment Commento traccia Track 1 Traccia 1 ConfigMidiFileBase MusE: Config Midi File Import/Export Import: Importa: Split tracks into &parts Dividi tracce in &parti Alt+P Alt+P Split tracks into parts, or one single part Dividi tracce in parti, o un'unica parte Export: Esporta: 96 96 192 192 384 384 Enable extended smf format (currently not implemented) Use &2-byte time signatures instead of standard 4 Alt+2 Alt+2 Copyright: Copyright: Format: Formato: Note: Format 0 uses the FIRST midi track's name/comment in the arranger Division: 0 (single track) 1 (multiple tracks) &OK &Cancel A&nnulla Default instrument: Device Name metas trump Port metas if both exist Instrument Name metas trump Mode sysexes if both exist Mode sysexes Instrument name metas Port metas Device name metas Export a Port/Device meta for format 0 Import Export Running Status saves space by not repeating event status bytes. If this is off every event will have a status byte, increasing file size. Use Running &Status To turn a note off some devices can use note-offs or zero-velocity note-ons or both. When used with Running Status this setting saves space. It is safe to leave this off. Replace note-offs with &zero velocity note-ons If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum note controllers. If disabled, drum maps are ignored during export. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. See "What's This ?" for more info. If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum controllers. Any Port overrides cause a separate track to be created (requires format 1). Any Channel overrides will still appear in the same track in the exported file, but upon re-import into an app such as MusE, MusE will treat that as a new track and create one. If disabled, drum maps are ignored. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. Upon re-importing into another app, it is up to the other app to redirect the particular drum ports, channels and notes to make the song sound like it was intended. Drum map overrides are unique to MusE. If re-importing into MusE, for faithful reproduction disable this setting and apply a drum map and some overrides later. If re-importing or opening in another midi player, enable this setting so that the player can have a chance at faithful reproduction. Some user adjustment may be required (choice of instruments, ports, etc). Apply drum map Port, Channel, ANote overrides If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. See "What's This ?" for more info. If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. The extra track is automatically created. In this mode all events on a track in the midi file are on the same channel. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. In this mode the track events can be on different channels. But if the file is re-imported into an app such as MusE, MusE will automatically create the extra track. Drum map Channel overrides go to a separate track Export instrument or mode: Export port or device metas: CopyOnWriteDialogBase Copy Wave Files Some sound files will be copied to the Project Directory, either because they are not writable or because more than one independent Wave Event shares them. (If you prefer instead that the Wave Events are inter-dependent, try using Clone Parts.) Multiple copies will be made in some cases. If no Project has been created yet, you will be asked to, giving another chance to cancel. These files will be copied to the Project Directory: CrescendoBase MusE: Crescendo/Decrescendo MusE: Crescendo/Decrescendo Range Intervallo Looped Events Eventi del loop Selected Looped Eventi del loop selezionati Values Valori Start velocity Velocity di partenza % % End velocity Velocity finale Absolute Assoluti Relative Relativi OK OK Cancel Annulla &Looped Events Parts Parti Selected parts or c&urrent part All parts &in editor &Absolute Rela&tive DeicsOnzeGui Save configuration Critical Error Cannot open file %1 Parsing error for file %1 Load category dialog Load set dialog Save set dialog New category Delete category Load category Save category Load set Save set Delete set New subcategory Delete subcategory Load subcategory Save subcategory New preset Delete preset Load preset Save preset No more category supported You can not add more categories Do you really want to delete %1 ? &Yes &No No category selected You must first select a category. Replace or add %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? &Replace &Add &Aggiungi Download error There is no more free category slot. Save category dialog No more subcategory supported You can not add more subcategories No subcategory selected You must first select a subcategory. Load subcategory dialog %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? There is no more free subcategory slot. Save subcategory dialog No more preset supported You can not add more presets No preset selected You must first select a preset. Load preset dialog %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? There is no more free preset slot. Save preset dialog Browse set dialog Browse image dialog DeicsOnzeGuiBase DeicsOnze &Preset Program numerous INITVOICE LBank Subcategory Bank numerous NONE HBank Category Prog Preset DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. &Global Pitch Envelope PL3 PL2 PL1 PR1 PR2 PR3 Pitch modulation depth LFO LFO Sync Pitch modulation sensitivity Pitch Modulation Sensitivity LFO Delay LFO delay LFO speed Amplitude modulation depth Amplitude modulation sensitivity Amplitude Modulation Sensitivity AMS LFO Waveform Pitch Modulation Depth PMD LFO Speed AMD Speed Delay PMS Modulation Matrix <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> Op4 Feedback Feedback level of the operator 4 Transpose Op &1 Scaling 1 LS1 RS1 Rate Scaling Attack Rate of the operator 1 Level Scaling Amplitude Envelope 1 RR1 D1R1 D1L1 D2R1 Release Rate 2° Decay Rate 1° Decay Level 1° Decay Rate Attack Rate AR1 Detune, OSCWave, EGShift 1 DET1 Detune EG Shift 96dB 48dB 24dB 12dB Wave form Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Sensitivity 1 KVS1 Amplitude Modulation Enable AME1 Keyboard Velocity Sensitivity Key Velocity Sensitivity EBS1 EG Bias Sensitivity Eg Bias Sensitivity Frequency 1 Coarse 1 Coarse Ratio Fine 1 Fine Ratio Freq 1 Fixed Frequency Toggle Fix Frequency FIX OUT 1 Output Volume Volume Op &2 Amplitude Envelope 2 D1R2 D1L2 D2R2 RR2 AR2 Frequency 2 Coarse 2 Fine 2 Freq 2 Scaling 2 LS2 RS2 OUT 2 Detune, OSCWave, EGShift 2 DET2 Sensitivity 2 EBS2 KVS2 AME2 Op &3 Amplitude Envelope 3 D1R3 D1L3 D2R3 RR3 AR3 Scaling 3 LS3 RS3 OUT 3 Frequency 3 Coarse 3 Fine 3 Freq 3 Detune, OSCWave, EGShift 3 DET3 Sensitivity 3 EBS3 KVS3 AME3 Op &4 amplitude Envelope 4 AR4 D1R4 D1L4 D2R4 RR4 Frequency 4 Coarse 4 Fine 4 Freq 4 Scaling 4 LS4 RS4 OUT 4 Detune, OSCWave, EGShift 4 DET4 Sensitivity 4 EBS4 KVS4 AME4 &Func Delay Pan Depth Delay Pan LFO Freq Delay Ch Send Level Channel Chorus Delay Feedback Delay On/Off, Return Level On acceso Delay Beat Ratio Delay BPM Foot Control Pitch Bend Range Pitch Tono Amplitude Modulation Wheel Breath Control Pitch Bias Envelope Bias After Touch Phony Mode POLY MONO Potamento Portamento Mode FINGER FULL PT Portamento Time C&horus Chorus Parameters Channel send level On/Off and Return level Select LADSPA plugin Change plugin &Reverb Reverb Parameters &Config Font Size Quality High Middle Low Ultra low Filter Save Mode (into the song) Save only the used presets Save the entire set Save the configuration Configuration File Save... Load... Save as default Colors Colori Text Background Edit Text Edit Background Red Blue Green Set Path Image in the background : Browse... Load the set at the initialization : Set Brightness, Detune, Attack and Release of the current channel to default Res. Ctrl Cut all notes off Panic! Number of Voices Number of voices Enable Channel Vol Vol Channel Ctrl Release Attack Brightness Modulation Pan DelOverlapsBase MusE: Delete Overlaps MusE: Cancella sovrapposizioni Range Intervallo All Events Tutti gli eventi Selected Events Eventi selezionati Looped Events Eventi del loop Selected Looped Eventi del loop selezionati OK OK Cancel Annulla Parts Parti Selected parts or c&urrent part All parts &in editor DidYouKnow Did you know? Sapevi che...? Don't show on startup Non mostrare all'avvio Next tip Suggerimento successivo Close Chiudi DuplicateTracksBase Duplicate tracks Number of copies Numero di copie Copy all routes Default routing No routes Copy effects rack plugins Copy plugin controllers Ok OK Cancel Annulla Copy drumlist Copy standard (vol, pan) and synth controllers Duplicate all parts Make independent copies of all parts Make clones of all parts No parts EditCtrlBase MusE: Edit Controller Event Time Position Available Controller: Create New Controller textLabel3 Value Valore Controller H-Bank L-Bank Program Programma off spento pushButton4 &OK &Cancel A&nnulla Note EditGainBase MusE: Modify gain Gain 200% 200% 100% 100% 0% 0% &Reset Alt+R &Apply &Applica Alt+A &Cancel A&nnulla Alt+C EditInstrumentBase MusE: Instrument Editor Instrument Name: Selected instrument name. List of defined instruments. Pa&tches List of groups and patches. Group/Patch Properties Name: Nome: Group or patch name High Bank: Patch high bank number Patch high bank number. --- means don't care. --- dont care Low Bank: Patch low bank number Patch low bank number. --- means don't care. --- Program: Patch program number Drum Delete group or patch &Delete &Cancella Alt+D New patch New &Patch Alt+P Alt+P New group New &Group Alt+G List of defined controllers List of defined controllers. Name Type H-Ctrl H-Ctrl L-Ctrl L-Ctrl Min Max Def Midi controller name Type: Midi controller type Tipo controllo Midi Midi controller number high byte Numero controller Midi (byte alto) Midi controller number low byte (* means drum controller) Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and GS/XG type songs and instruments. Allows controllers for each instrument in Muse's drum map. The low byte will be replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. * wild card * Range: Min Minimum value. If negative, auto-translate. Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. Useful for controllers which should be displayed with zero bias. For example, 'Pan': Minimum: -64 Maximum: 63 True range: Min: 0 Max: 127 (bias = 64) 'CoarseTuning': Min: -24 Max: 23 True range: Min: 40 Max: 87 (bias = 64) Bias is determined from controller type: 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 Type 'Pitch' is the exception. It is biased at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) Max Maximum value Default: L-Bank off spento Progr. ??? H-Bank Default value. Off: No default. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). off dont care spento Add common controller &Add &Aggiungi Alt+A Delete controller Create a new controller New &Controller Alt+C S&ysEx SysEx List: Hex Entry: New SysE&x Alt+X Tools &File &Help &New New Nuovo Ctrl+N &Open... Open Ctrl+O &Save Save Ctrl+S Save &As... Save As new item What's this? Show in tracks: Midi Midi Drum&maps Patch Collections: &Copy &Remove &Up &Down Contro&llers &Add Common... W Comment: &Initialization Instrument initialization sequence: &Add... &Change... &Close Close Chiudi Note off mode: Note off mode Selects how to handle note off events. The instrument can use note off events, or not at all, or convert them to zero-velocity note on events which helps save midi interface bandwidth. Refer to the instrument manufacturer's midi implementation chart for details. Patch program number. --- means don't care. &Drum High Bank Low Bank Program Programma Patch program number. --- means don't care. Patch Name: Dr def Drum default: Note: The enclosing F0 ... F7 are not required Info Additional information EditNoteDialogBase MusE: Enter Note MusE: immetti nota OK OK Cancel Annulla Length: Lunghezza: Time Position: Posizione nel tempo: Pitch: Tono: Velocity On: Velocity On: Velocity Off: Velocity Off: EditSysexDialogBase MusE: Enter SysEx TimePosition: Comment: Name: Nome: &Select... &OK &Cancel A&nnulla Hex: Note: The enclosing F0 ... F7 are not required FLUIDGui FLUID: open Soundfile FLUID: apri Soundfile FLUIDGuiBase Soundfont Soundfont FLUIDSynthGuiBase FLUID Synth Load Delete Dump Info ID Fontname Chnl Soundfont Soundfont Drum Chnl Level Width Damping Room Size Reverb CHANNEL SETUP Sine Triangle Type Number Speed Depth Chorus Gain LOADED SOUNDFONTS FileDialogButtons fdialogbuttons Global User Project read Midi Port Configuration write window states Home FluidSynthGui Choose soundfont Scegli un Soundfont GateTimeBase MusE: Modify Note Length MusE: modifica lunghezza note Range Intervallo All Events Tutti gli eventi Selected Events Eventi selezionati Looped Events Eventi del loop Selected Looped Eventi del loop selezionati Values Valori Rate: Rata: Offset: Offset: % % lenNew = (lenOld * rate) + offset lenNew = (lenOld * rate) + offset OK OK Cancel Annulla Parts Parti Selected parts or c&urrent part All parts &in editor GlobalSettingsDialogBase MusE: Global Settings MusE: Impostazioni globali Application Applicazione Project directory Directory progetti Projects: Progetti: ... ... Views Finestre y-pos pos-y show mostra x-pos pos-x height altezza width larghezza Mixer A Mixer A Mixer B Mixer B Big Time Timer gigante Main Window Finestra principale Transport Trasporto set current values imposta valori attuali Cur corrente Start Muse Avviamento di Muse Start song Canzone iniziale start with last song ultima canzone Choose start song or template Seleziona canzone iniziale o modello Reset to default Ripristina valori predefiniti start with template modello start with song canzone specifica On Launch All'avvio show splash screen mostra schermata iniziale show "Did you know?" dialog mostra finestra "Sapevi che...?" Start template or song: Modello o canzone di partenza: Read MIDI Ports configuration from file, or else automatically configure Reimposta configurazione porte Midi dal file, o altrimenti configura automaticamente Read MIDI Ports configuration Reimposta configurazione porte Midi Audio Audio Mixer Mixer dB dB min. Meter Value valore minimo dell'indicatore min. Slider Val valore minimo dello slider Try to use Jack Freewheel Tenta di utilizzare Jack Freewheel Speeds bounce operations Accelera operazioni di bounce Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Usa la modalità Jack Freewheel se possibile. Cio velocizza notevolmente operazioni di bounce. Enable denormal protection Abilita protezione da denormalizzazione Enable output limiter Attiva limitatore output VST in-place VST in-place Enable VST in-place processing (restart required) Attiva elaborazione VST in-place (richiede restart) Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. Minimum control period Periodo di controllo minimo Minimum audio controller process period (samples). Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. 1 1 2 2 4 4 8 8 16 16 32 32 64 64 128 128 256 256 512 512 1024 1024 2048 2048 External Waveditor Editor externo suoni External Waveditor command Comando per lanciare un editor esterno di suoni Note: External editor opened from the internal editor. Nota: l'editor esterno viene lanciato dall'editor interno. Dummy Audio Driver (settings require restart) Driver audio fittizio (modifiche a queste impostazioni richiedono il riavvio) Sample rate Frequenza di campionamento Hz Hz Period size (Frames per period): Dimensione periodo (fotogrammi per periodo): Shorter periods give better midi playback resolution. Periodi più brevi migliorano la risoluzione della riproduzione Midi. Midi Midi Ticks RTC Resolution (Ticks/Sec) 4096 4096 8192 8192 16384 16384 32768 32768 Midi Resolution (Ticks/Quarternote) 48 48 96 96 192 192 384 384 768 768 1536 1536 3072 3072 6144 6144 12288 12288 GUI Behaviour Comportamento interfaccia grafica Behavior Comportamento GUI Refresh Rate frequenza aggiornamento interfaccia grafica /sec /sec Use old-style stop shortcut: Utilizza tasto stop diretto classico: Move single armed track with selection Sposta traccia singola con selezione Use project save dialog Utilizza finestra dialogo per salvataggio progetto Some popup menus stay open (else hold Ctrl) Tieni aperti alcuni menu popup (in alternativa premi Ctrl) Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. Use left mouse button for decreasing values Utilizza pulsante mouse sinistro per decrementare i valori Shift + Right click sets left range marker Shift + click destro imposta il demarcatore di sinistra dell'intervallo Allow adding hidden tracks in track list menu Permetti l'aggiunta di trace nascoste nel menu lista tracce Unhide tracks when adding hidden tracks Scopri tracce quando aggiungi trace nascoste Smart focus Focus intelligente After editing, controls will return focus to their respective canvas Dopo le modifiche, i controlli ripassano lo stato attivo al loro canvas GUI Style Stile interfaccia grafica Presets: Impostazioni predefinite: traditional MusE SDI SDI classica MusE Cakewalk-like MDI MDI stile Cakewalk Borland-/Mac-like MDI MDI stile Borland/Mac &Apply &Applica &Ok &OK &Cancel A&nnulla Auto save (every 5 minutes if not playing/recording) Instrument initialization Send instrument initialization sequences Warn if instrument initialization sequences pending Send instrument controller default values if none in song, at init or rewind Warn if timer frequency is inadequate Track height Show newly created midi velocity graphs per-note px Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. Borderless zoom/pan mouse (else use alternate method) &Don't record hidden instruments Don'&t record muted instruments Don't record &hidden or muted instruments Scrollable submenus Warn if opening file versions different than current version Start MusE Start with &last song Start with &template Sta&rt with song On launch Show splash screen Show "Did you know?" dialog Plugin Paths Ordered by priority. Changes take effect on restart. The plugin cache is used for safety and speed. It automatically rescans when any of the given directories change, for example by installing a new plugin. Click this button to force the cache to recreate upon next restart. &Force plugin rescan and cache recreation on next restart LADSPA DSSI Linux VST LV2 LV2 NOTE: Ensure the directory containing core LV2 bundles is included (typically /usr/lib/lv2). Otherwise all plugins may not load properly. Move up Move down Add... Edit... Remove Audio driver settings (require restart) Sample rate: Audio backend: Shorter periods give better midi playback resolution. Note that this cannot be set with Jack devices! Period size (frames per period): Note that samplerate cannot be set with Jack devices! Minimum meter value Minimum slider value Midi controller behaviour Send Null parameters after an (N)RPN value If set, 'null' parameter numbers will be sent after each RPN/NRPN event. (A 'null' parameter number is 127.) This prevents subsequent rogue or accidental 'data' events (without parameters) from corrupting the current RPN/NRPN controller. Normally it is OK to leave this unset, but use it if there is a possibility of such 'data' events. If this is set, the 'Optimize controllers' setting does not affect these controllers' parameters, only the H/L values. Send Null parameters Don't send redundant parameters or values If set, redundant H/L parameters or H/L values are not sent. They are sent only if they changed. This can save midi interface bandwidth. But it is possible the receiving device might miss the previous changes (for example if the device is turned on after they were sent). The 'Panic' button will force them to be resent. Certain operations will also force them to be resent, such as clicking a GUI midi control (but not while adjusting it). Optimize controllers Record a&ll instruments Latency Whether to enable latency correction/compensation: Enable latency correction/compensation Project latency Whether completely independent branches share a common project-wide latency: Project-wide latency Unterminated branches Whether unterminated branches participate in latency correction/compensation: Unterminated input branches Unterminated output branches Track monitoring Whether track monitor buttons affect latency correction/compensation: Monitoring affects latency Revert native GUI scaling for plugins in HiDPI Live update wave parts while recording Whether to show knob and slider values in certain places, for example mixer strips. Turn off to reduce clutter. Revert native UI window HiDPI scaling (some plugins ignore this setting) Show knob and slider values Whether to show knobs or sliders in certain places, for example mixer strips Prefer midi volume as decibels instead of 0-127 Style hack: Force line edit widgets to draw a frame Prefer knobs instead of sliders GUI refresh rate Use first available Ask once Ask always Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. This hack forces a frame to be drawn, but may interfere with other styles. Use old-style stop shortcut Monitor on record-arm automatically Show note names on notes in pianoroll Whether record-arming a track automatically activates monitoring. Record drum tracks Sample rate conversion, time stretching and pitch shifting Audio converter settings... LV2 UI open behavior Set range markers (left / right) Middle mouse button / Right mouse button Ctrl + Right mouse button / Right mouse button Ctrl + Left mouse button / Ctrl + Right mouse button Options GUI Tweaks Tweaks Editors Open editors in tabs (else in separate windows) Piano roll Drum editor Wave editor Score editor Master track editor DSSI VST Directories where dssi-vst (if installed) looks for Wine vst plugins (*.dll files) Directories containing Linux vst plugins (*.so files) GUI Show status bar Show note tooltips in midi editors Keep transport window on top (requires restart) LegatoBase MusE: Legato Range Intervallo All Events Tutti gli eventi Selected Events Eventi selezionati Looped Events Eventi del loop Selected Looped Eventi del loop selezionati Settings ticks Minimum Length Allow shortening notes OK OK Cancel Annulla Parts Parti Selected parts or c&urrent part All parts &in editor MITTransposeBase MusE: Midi Input Plugin: Transpose On acceso TriggerKey Transpose: +0 MRConfigBase MusE: Midi Input Plugin: Remote Control MusE: Midi Input Plugin: Controllo Remoto Activate Attiva On acceso Actions Azioni Stop Stop Record Registra Goto Left Mark Vai al marcatore di sinistra Play Riproduci Insert rest (step rec) Inserisci pausa (step rec) MetronomeConfigBase MusE: Metronome Config Audio Beep Choose outputs... 50 50 MIDI Click Midi Channel Measure Note Measure Velocity Beat Velocity Beat Note Midi Port Precount Bars From Mastertrack / Signature Prerecord Preroll Hint: Enable metronome in Transportpanel &Apply &Applica Alt+A &OK Alt+O &Cancel A&nnulla Alt+C Volume Audio master Meas Beat Accent1 Accent2 Sample Preferences Apply the settings globally or to the current song: Use global metronome Use song-specific metronome Enable accents for the audio and midi metronomes: &Two samples (old samples) Four samples (&new samples, with accents) Audio Audio MIDI Accent 2 Velocity Accent 1 Velocity Accent 1 Note Accent 2 Note Off spento Accents Accent sequence beats Current accent 1 sequence: (Click to change) Current accent 2 sequence: Factory User Preset sequences Add current accents to presets Make a new preset from the current accents. + Delete preset Delete the selected preset. Only user presets can be deleted. - Use preset Copy the preset to the current accents. -> Reset to Factory or User defaults Resets all current accent sequences to default Factory or User presets. ! Enable the precount Enable Number of bars Use Mastertrack time signature Beats per bar Beat note type Enable precount when playing Precount on play Mute the metronome after precount Mute metronome Not supported yet MidiAudioControlBase Midi control Port: Channel: Control type: Hi: Lo: &Learn MidiFilterConfigBase MusE: Midi Input Filter Record Filter Note On Poly Pressure Controller Program Change After Touch Pitch Bend Sysex Thru Filter Controller Filter Channel Filter 14 14 10 10 6 6 12 12 4 4 2 2 9 9 8 8 3 3 13 13 15 15 16 16 7 7 11 11 5 5 1 1 MidiInputTransformDialogBase MusE: Midi Input Transformator Filter All Equal Unequal Note Poly Pressure Control Change Aftertouch Pitch Bend NRPN NRPN RPN RPN Value 2 Value 1 Event Type Ignore Higher Lower Inside Outside Channel Port Porta Processing Keep Fix Plus Minus Multiply Divide Invert Inverti selezione ScaleMap Flip Dyn Random Modules 1 1 2 2 3 3 4 4 Preset Name: Nome: Comment: Function create new preset &New delete preset &Delete &Cancella &Dismiss PresetList Program Programma Toggle Enable module 1 Enable module 2 Enable module 3 Enable module 4 MidiSyncConfigBase MusE: Midi Sync Apply Applica Ok OK Cancel Annulla MTC Type: 24 24 25 25 30D 30D 30N 30N Offset: Offset: hour h minute minuto m second secondo s frame fotogramma f subframe frazione di fotogramma Sync receiving and sending Control MusE timing by external midi clock or MTC sync When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). Slave to external sync Averaging applied to recorded external tempo changes. External midi clock can be very jittery. Tempo is derived from it and recorded. It is usually desirable to average it and limit the number of recorded changes. Tiny: 2 section 4/4 = 8 stages. 1/8T note averaging, may produce jitter. Small: 3 section 12/8/4 = 24 stages. 1/4 note averaging, may still produce jitter. Medium: 3 section 28/12/8 = 48 stages. 1/2 note averaging. Less jitter. Large: 4 section 48/48/48/48 = 192 stages. Use this if the song has only one tempo. Very low quantization values can be used. Large pre-detect: 4 section 8/48/48/48 = 152 stages + first stage large step pre-detector. Use this if you expect sudden large tempo steps. None: Use only if high accuracy is needed for audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. Tempo record averaging bpm Tempo record quantization Send start to first clock delay Allows 'slow sync' devices time to synchronize to MusE. This value is the delay from sending start to sending the first clock. ms Send sync delay Note: Sync delay and MTC sync currently not fully implemented This button and other transport related buttons are found on the Transport Panel MidiTrackInfoBase off spento % % <unknown> <sconosciuto> Vol Vol MidiTransformDialogBase MusE: Midi Transformator &New &Delete &Cancella &Apply &Applica &OK &Cancel A&nnulla PresetList Processing Event Type Keep Fix Note Poly Pressure Control Change Aftertouch Pitch Bend NRPN NRPN RPN RPN Plus Minus Multiply Divide Value 2 Invert Inverti selezione ScaleMap Flip Dyn Random Value 1 Length Position Filter All Equal Unequal Ignore Higher Lower Inside Outside Bar Range Preset Name: Nome: Comment: Range Intervallo process all events selected tracks inside loop Function Select Quantize Quantizza Delete Transform Insert Copy Copia Extract Quantize Value Randomize MixdownFileDialogBase MusE: Set Mixdown Wavefile &OK &Cancel A&nnulla File Path Channel Stereo Mono 5.1 5.1 wav,16 Bit wav, 24 Bit wav, 32 Bit (float) Format MoveBase MusE: Move Notes MusE: Sposta note Range Intervallo All Events Tutti gli eventi Selected Events Eventi selezionati Looped Events Eventi del loop Selected Looped Eventi del loop selezionati Value Valore Move by Sposta di ticks OK OK Cancel Annulla Parts Parti Selected parts or c&urrent part All parts &in editor MusECore::AudioPreviewDialog Stop Stop Play Riproduci MusECore::RasterizerModel Off spento Bar MusECore::Scripts Do you want to process ALL or only selected events? &Selected &All MusE - external script failed MusE was unable to launch the script, error message: %1 Re-read script names from disc MusECore::Song Jack shutdown! Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: - a performance issue with your particular setup. - a bug in MusE (or possibly in another connected software). - a random hiccup which might never occur again. - jack was voluntary stopped by you or someone else - jack crashed If there is a persisting problem you are much welcome to discuss it on the MusE mailinglist. (there is information about joining the mailinglist on the MusE homepage which is available through the help menu) To proceed check the status of Jack and try to restart it and then . click on the Restart button. Midi control Assign Clear Clear all controller events? &Ok &OK &Cancel A&nnulla MusE: Tempo list External tempo changes were recorded. Transfer them to master tempo list? Und&o Re&do Previous event Next event Set event Add event Erase event Erase range Clear automation Bypass Automation Controller MusEDialog::PasteDialog %n quarter(s) %n quarters quarter quarter quarters quarters MusEDialog::PasteEventsDialog %n quarter(s) %n quarters quarter quarter quarters quarters MusEGui::Appearance Time scale upper, and time signature. Controller graph and S/X buttons. Tempo scale, and markers. Mixer labels. Auto-font-sizing up to chosen font size. Word-breaking but only with spaces. Maximum mixer label auto-font-sizing font size. Global opacity (opposite of transparency). Standard Custom MusE: load image &Ok &OK &Cancel A&nnulla Style was changed. The program must be restarted for changes to take place. Restart now? Revert changes Revert all... Do you really want to reset all colors? No current color item Select Color: %1 Main application font, and default font for any controls not defined here. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Arranger part name overlay. Time scale lower. MusEGui::Arranger Track Type Tipo di traccia Track Name Nome traccia Midi output channel number or audio channels Numero canali Midi d'uscita o canali audio Midi output port or synth midi port Porta Midi d'uscita o porta Midi sintetizzatore Automation parameter selection Selezione parametri automazione Notation clef Chiave notazione Enable recording. Click to toggle. Abilita registrazione. Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. On/Off is not! Track type. Right-click to change midi and drum track types. Track name. Double-click to edit. Right-click for more options. Time lock Notation clef. Select this tracks notation clef. Arranger Arrangiatore Cursor Off spento Len Lun Pitch Tono Tempo N R Record R M Mute M S Solo S Track Port Porta Ch Channel Ch T Time lock Automation Clef Track index Enable input monitor Enable input monitor. Click to toggle. Passes input through to output for monitoring. See also Settings: Automatically Monitor On Record Arm. Enable recording. Click to toggle. See also Settings: Automatically Monitor On Record Arm. Song length - bars Midi pitch Global midi pitch shift Midi tempo Enable recording Mute/Off indicator Solo indicator Track type Track name Midi/Drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi/Drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed. Track index. Click to select track, CTRL+LMB to add/remove, SHIFT+LMB for range select, dblclick to select all of the same type. Enable/Disable input monitor. Passes input through to output (see also Settings: Automatically monitor on record arm). Enable/Disable recording. Mute indicator. Left-click to mute track (for quick action), right-click to toggle track on/off (for more permanent action). Solo indicator. Connected tracks are also 'phantom' soloed. Track type. Click to switch between midi and drum track types. Track name. Double-click to edit. Right-click for context menu. Midi/Drum: Output channel number. Audio: Channels. Mid/right-click to change. Midi/Drum: Output port. Synth: Assigned midi port. Left-click to change, right-click to show synth GUI. Notation clef. Click to select this tracks notation clef. Cursor position Bars MusEGui::ArrangerColumns Control7 Control7 Control14 Control14 RPN RPN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Tono Program Programma Aftertouch MusEGui::ArrangerView MusE: Arranger C&ut &Copy &Paste &Insert Empty Measure Delete Selected Tracks Duplicate Selected Tracks Add Track Select Select &All &Deselect All Invert &Selection &Inside Loop &Outside Loop All &Parts on Track Score Global Cut Global Insert Global Split &Edit &Modifica &Structure &Quantize Notes Crescendo/Decrescendo Crescendo/Decrescendo Transpose Erase Events (Not Parts) Move Events (Not Parts) Set Fixed Note Length Delete Overlapping Notes Legato Window &Config &Configura finestra New Nuovo D&elete Remove track(s) Are you sure you want to remove this track(s)? Copy in Range Paste C&lone Paste to Selected &Track Paste Clone to Selected Trac&k Paste (Show Dialo&g)... Shrink Selected Parts Expand Selected Parts Purge Hidden Events from Selected Parts All Tracks in One Staff One Staff per Track New Score Window... Pianoroll... Drums... Wave... Midi &Transform... Global Cut - Selected Tracks Global Insert - Selected Tracks Global Split - Selected Tracks Fu&nctions Fu&nzioni Change Note &Length Change Note &Velocity &Display Configure &Custom Columns... New... Arranger Arrangiatore Insert Track Event List... Open in New Window Always open the part(s) in a new editor window Open Synth Plugin GUI... &Scripts Scripts for midi processing. Applied to selected midi parts (or else tracks). User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Display menu: View-specific display options. Toggle &Mixer Strip Nothing to edit MusEGui::AudioComponentRack Aux send level (dB) Vol Vol Pan Mute Volume/gain Panorama/Balance Gain Calibration gain MusEGui::AudioMixerApp &Create &View Routing Show Midi Tracks Show Drum Tracks Show Wave Tracks Show Inputs Show Outputs Show Groups Show Auxs Show Synthesizers Strips Traditional order Arranger order User order Show all hidden strips Unhide strip: (no hidden strips) MusEGui::AudioStrip 1/2 channel dB dB off spento Off spento Read Touch Write Pre Fader Listening (PFL) Record downmix Record arm Mute or proxy mute Solo or proxy solo Track off Input monitor Pass input through to output Automation type L meter peak/clip R meter peak/clip Input routing. Press F1 for help. Output routing. Press F1 for help. Volume/Gain Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Auto off Automation type: Off, Read, Touch or Write. Press F1 for help. MusEGui::BigTime format display formato display bar misura beat battuta tick frazione minute minuto second secondo frame fotogramma subframe frazione di fotogramma MusE: Bigtime MusE: Timer gigante Format display Bar Beat Tick Minute Second Frame Subframe MusEGui::Canvas Tools MusEGui::ClipListEdit Window &Config &Configura finestra MusEGui::ComboQuant Off spento MusEGui::CompactKnob off spento MusEGui::CompactPatchEdit Patch name MusEGui::CompactSlider off spento MusEGui::CpuStatusBar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) CPU load averaged over each GUI update period. DSP load read from JACK. Number of xruns. Double-click to reset. MusEGui::CpuToolbar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) MusEGui::CtrlCanvas Merge options Erase target Erase target events between source events Erase target WYSIWYG Include last source item width when erasing Erase target inclusive Include entire source range when erasing Merge actions Merge Merge the dragged items Merge a copy Merge a copy of the dragged items Cancel drag Cancel dragging the items Control canvas: Use Pencil tool to edit events and Draw tool to adjust them gradually. Hold Ctrl to affect only existing events. MusEGui::CtrlPanel S Select controller S off spento Select controller Remove panel Manual adjust (Ctrl-double-click on/off) All/Per-note velocity mode MusEGui::DList Hide this instrument This turns a blue eye into a crossed eye Show this instrument This turns a crossed eye into a blue eye Set field Sets a field Set row Sets a row Set column Sets a whole column to the field Set list Sets the whole list Reset field Resets a field in a row to default patch or instrument value Reset row Resets a row to the instrument values Reset column Resets a whole column to the instrument values Reset list Resets the whole list to the instrument values Reset track's drum list Resets all lists on all patches to the instrument values Promote field to default patch Promotes a field in a row to the default patch Promote row to default patch Promotes a row to the default patch Promote column to default patch Promotes a column to the default patch Promote list to default patch Promotes the whole list to the default patch Reset instrument field Reset instrument row Reset instrument list Set instrument field Set instrument row Set instrument list Drum list: Edit instrument-specific settings. MusEGui::DrumCanvas Moving items failed The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. Creating event failed Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Recording event failed Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Internal error Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. Drum map Mappa batteria Reset the track's drum map with instrument defaults? Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help. Note: Velocity: Start (bar): Start (time): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete LMB: Delete Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete. MusEGui::DrumEdit mute instrument disattiva audio strumento sound name nome suono volume percent percentuale volume quantisation quantizzazione this input note triggers the sound questa nota d'ingresso attiva il suono note length lunghezza della nota this is the note which is played questa è la nota che viene suonata output channel (hold ctl to affect all rows) canale di output (premi ctl per modificare tutte le righe) output port (hold ctl to affect all rows) porta di output (premi ctl per modificare tutte le righe) Load Map Carica mappa Save Map Salva mappa Reset GM Map Reimposta mappa GM &Edit &Modifica Cut Taglia Copy Copia Copy events in range Copia eventi nell'intervallo Paste Incolla Paste (with Dialog) Incolla (con dialogo) Delete Events Cancella eventi &Select &Seleziona Select All Seleziona tutto Select None Annulla selezione Invert Inverti selezione Inside Loop All'interno del loop Outside Loop All'esterno del loop Previous Part Parte precedente Next Part Parte successiva Fu&nctions Fu&nzioni Re-order list Riordina lista Set Fixed Length Imposta lunghezza fissa Modify Velocity Modifica velocity Crescendo/Decrescendo Crescendo/Decrescendo Quantize Quantizza Erase Event Cancella evento Move Notes Sposta note Delete Overlaps Cancella sovrapposizioni &Plugins &Plugin Window &Config &Configura finestra Drum tools Strumenti batteria Load Drummap Carica mappa batteria Store Drummap Salva mappa batteria Step Record Registra passo per passo Midi Input Ingresso Midi cursor tools strumenti cursore Set step size for cursor edit Imposta dimensione passo per editing con il cursore ctrl ctrl Add Controller View Aggiungi visualizzazione controller M mute M Sound Suono Vol Vol QNT QNT E-Note E-Note Len Lun A-Note A-Note Ch Ch Port Porta LV1 LV1 LV2 LV2 LV3 LV3 LV4 LV4 Muse: Load Drum Map Muse: Carica mappa batteria MusE: Store Drum Map Muse: Salva mappa batteria Drum map Mappa batteria Reset the drum map with GM defaults? Reimposta mappa batteria predefinita GM? Group Don't group Group by channel Group maximally Show/Hide Also show hidden instruments Show all instruments Hide all instruments Only show used instruments Only show instruments with non-empty name or used instruments H hide H Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) Paste to current part &Display Step record Midi input Play events Cursor tools Add controller view Note which triggers the sound Note which is played Override track output channel (hold CTRL to affect all rows). Override track output port (hold CTRL to affect all rows). CTRL + META keys: Draw velocity level 1 META key: Draw velocity level 2 META + ALT keys: Draw velocity level 4 Event List... &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Display menu: View-specific display options. Cursor step MusEGui::EditEventDialog Ok OK Cancel Annulla MusEGui::EditInstrument MusE: Create file failed MusE: Write File failed MusE: Save Instrument Definition Instrument Definition (*.idf) MusE: Save instrument as Enter a new unique instrument name: The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? MusE: Bad instrument name Please choose a unique instrument name. (The name might be used by a hidden instrument.) MusE: Bad patch name Please choose a unique patch name MusE: Bad patchgroup name Please choose a unique patchgroup name MusE: Bad controller name Please choose a unique controller name MusE MusE The current Instrument contains unsaved data Save Current Instrument? &Save &Nosave &Abort Control7 Control7 Control14 Control14 RPN RPN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Tono Program Programma PolyAftertouch Aftertouch Name Nome Vol Vol Quant E-Note E-Note Len Lun A-Note A-Note LV1 LV1 LV2 LV2 LV3 LV3 LV4 LV4 Tick Data Dati New controller: Error Error! All control numbers are taken up! Clean up the instrument! Use note offs No note offs Convert to 0-vel note ons H hide H M mute M Ch Ch Port Porta Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) MusEGui::EditMetaDialog MusE: Enter Meta Event Time Position Meta Type Enter Hex MusEGui::EditToolBar Manipulate automation Edit Tools Select Pointer tool: With the pointer tool you can: select parts move parts copy parts Select Pencil tool: With the pencil tool you can: create new parts modify length of parts Select Delete tool: With the delete tool you can delete parts Select Cut tool: With the cut tool you can split a part Select Glue tool: With the glue tool you can glue two parts Select Score tool: Select Quantize tool: Insert display quantize event Select Drawing tool Select Muting tool: Click on part to mute/unmute Select Cursor (tracker mode) tool: With the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Select Range tool Select Panning tool Select Zoom tool Pointer Pencil Eraser Cutter Glue Range Intervallo Pan Zoom Score Quantize Quantizza Draw Mute parts Edit automation Cursor Select Stretch tool Select Sample rate tool Stretch Sample rate Frequenza di campionamento MusEGui::EffectRack remove rimuovi MusE: Save Preset Replace effect Do you really want to replace the effect %1? Presets New Nuovo Change Move up Move down Remove Bypass Show gui Show native gui Save preset Effect rack: Double-click a slot to insert/edit effect. RMB to open context menu. Press F1 for help. Effect rack Double-click a slot to insert FX MusEGui::GlobalSettingsConfig MusE: Choose start template or song MusE: Scegli un modello o una canzone di partenza MusEGui::Header Track Info Columns Colonne Info Tracce Custom column MusEGui::LCDPatchEdit High bank: Low bank: Program (Ctrl-double-click on/off) off spento High bank Low bank Program Programma MusEGui::LMaster &Edit &Modifica Window &Config &Configura finestra Edit tools Tempo Key Time Type Value Valore Reposition of the initial tempo and signature events is not allowed MusE: List Editor Input error, conversion not OK or value out of range Reposition of tempo and signature events to start position is not allowed! Signature Position Delete Insert tempo change Insert time signature change Insert key change Edit position Edit value Delete event MusEGui::ListEdit &Edit &Modifica Cut Taglia Copy Copia Paste Incolla Delete Events Cancella eventi Window &Config &Configura finestra Insert tools Tick Bar Type Ch Ch Val A Val B Val C Len Lun Comment Note SysEx Ctrl Meta Tick+ Tick- Delete Insert note event Insert system exclusive event Insert controller event Insert meta event Increase tick value Decrease tick value Delete event MusEGui::MPConfig Default input connections Are you sure you want to apply to all existing midi tracks now? Default output connections MusE: bad device name in out Toggle all Change all tracks now Create Jack device Port Number Enable gui Enable reading Enable writing Port instrument Midi device name. Click to edit (Jack) Connections from Jack Midi outputs Connections to Jack Midi inputs Auto-connect these channels to new midi tracks Auto-connect new midi tracks to these channels Auto-connect new midi tracks to this channel Device state Enable gui for device Enable reading from device Enable writing to device Name of the midi device associated with this port number. Click to edit Jack midi name. Instrument connected to port Connections from Jack Midi output ports Connections to Jack Midi input ports Auto-connect these channels, on this port, to new midi tracks. Connect new midi tracks to these channels, on this port. Connect new midi tracks to this channel, on this port. State: result of opening the device Port Porta GUI I O Instrument Device Name In routes Out routes Def in ch Def out ch State <unknown> <sconosciuto> <none> <nulla> Remove Midi device name Midi device type Connections from Jack Midi Connections to Jack Midi Result of opening the device: OK: Assigned to a port and in use Closed: Unassigned to a port, or closed R/W Error: Unable to open for read or write Unavailable: USB midi unplugged, or external application not running, or synth plugin not installed etc. (Jack Midi devices have 'unavailable ports' in the routes columns.) Unavailable devices or ports can be purged with 'Remove' or with the advanced router. Enable Graphical User Interface for device Connections from Jack Midi ports Connections to Jack Midi ports Type In Out Please choose a unique device name Port number MusEGui::MTScale bar scale scala barre Bar scale Bar scale: Use mouse buttons to set position and range markers. Hold Shift to set (LMB) or delete (RMB) custom markers. MusEGui::MTScaleFlo bar scale scala barre Bar scale MusEGui::MarkerView &Edit &Modifica Window &Config &Configura finestra Bar:Beat:Tick Hr:Mn:Sc:Fr:Sf Lock Add marker Delete marker Edit tools Description Edit tick Edit SMPTE timecode Toggle timecode editing Edit marker description <Description> MusEGui::Master Tools MusEGui::MasterEdit MusE: Mastertrack Window &Config &Configura finestra Info Cursor Off spento &Display Time at cursor position Tempo at cursor position Mastertrack Display menu: View-specific display options. MusEGui::MetronomeConfig Reset accents: Resets all accents to the defaults (first in list) of the current preset category (Factory or User). Proceed? MusEGui::MidiAudioControl Control7 Control7 Control14 Control14 RPN RPN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Tono Program Programma Aftertouch MusEGui::MidiComponentRack Pro Var Rev Cho Pan Program Programma VariationSend ReverbSend ChorusSend Pan/Balance Controller (Ctrl-double-click on/off) <unknown> <sconosciuto> Instrument Transpose Transpose notes up or down Delay Offset playback of notes before or after actual note Length Change note length in percent of actual length Velocity <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> Compress Compress the notes velocity range, in percent of actual velocity <Patch> MusEGui::MidiInputTransformDialog New Nuovo MusEGui::MidiStrip off spento dB dB B Palette B B Record arm Mute or proxy mute Solo or proxy solo Track off Input monitor Pass input through to output Intput routing. Press F1 for help. Output routing. Press F1 for help. Inst Prop Instruments and controllers Properties Volume/Gain (Ctrl-double-click on/off) Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. MusEGui::MidiSyncConfig Port Number Name of the midi device associated with this port number Midi clock input detected Midi tick input detected Midi real time input detected MMC input detected MTC input detected Detected SMPTE format Receive id number. 127 = Global. Double click to edit. Accept midi clock input Accept midi real time input Accept MMC input Accept MTC input Receive start rewinds before playing Transmit id number. 127 = Global. Double click to edit. Send midi clock output Send midi realtime output Send MMC output Send MTC output Midi realtime input detected, including start/stop/continue, and song position. MMC input detected, including stop/play/deferred play, and locate. Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detects format of MTC quarter and full frame, and MMC locate. Receive id number. 127 = global receive all, even if not global. Accept midi realtime input, including start/stop/continue, and song position. Non-clock events (start,stop etc) are accepted by ALL enabled ports. This means you may have several master devices connected, and muse will accept input from them. Accept MMC input, including stop/play/deferred play, and locate. Transmit id number. 127 = global transmit to all. Send midi clock output. If 'Slave to External Sync' is chosen, muse can re-transmit clock to any other chosen ports. Send midi realtime output, including start/stop/continue, and song position. If 'Slave to external sync' is chosen, muse can re-transmit midi realtime input messages to any other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. Port Porta Device Name None Tiny Small Large Large with pre-detect MusE MusE Settings have changed Apply sync settings? &Apply &Applica &No &Abort <none> <nulla> Sync to this device. Click to select. Sync to this device. Click to select. MusE will synchronize to this device's clock or MTC quarter-frame, if 'slave to external sync' is on. MTC input detected, including forward quarter-frame sync and full-frame locate Accept midi clock input. Only one port can be used for clock sync. Accept MTC input, including forward quarter-frame sync and full-frame locate. Only one port can be used for sync. When start is received, rewind before playing. Note: In some cases, such as having many project audio tracks, it may be impossible to rewind fast enough to synchronize with the external device. Manually rewinding the device before playing is recommended. Type MusEGui::MidiTrackInfo <unknown> <sconosciuto> MusEGui::MidiTransformerDialog New Nuovo MusEGui::MixdownFileDialog Wave Files (*.wav);;All Files (*) File Wave (*.wav);;Tutti i File (*) MusEGui::MusE Failed to start audio! Und&o Re&do Loop Start Rewind Forward Stop Stop Play Riproduci Record Registra Panic &New Open &Recent &Save Import Audio File &Quit Mixer A Mixer A Mixer B Mixer B Fullscreen &Plugins &Plugin Input Plugins Rhythm Generator Local Off Bounce to Track Restart Audio Clear Automation Data Follow Song Don't Follow Song Follow Page Follow Continuous Metronome &Report Bug... Tempo Signature File Buttons Pulsanti file Undo/Redo Transport Trasporto &File &View &Midi &Audio &Windows Se&ttings &Help Cannot read template File open error File read error Unknown File Format: %1 MusE: Song: %1 MusE: load project MusE: load template MusE: Write File failed S&kip &Cancel A&nnulla MusE: Save As Nothing to edit MusE: Bounce to Track No wave tracks found No audio output tracks found Select one audio output track, and one target wave track Select one target wave track Select one target wave track, and one audio output track MusE: Bounce to File Select one audio output track MusE: Bounce &Abort This will clear all automation data on all audio tracks! Proceed? This takes an automation snapshot of all controllers on all audio tracks, at the current position. Proceed? MusE: Export Midi MusE: Import Midi Add midi file to current project? &Add to Project &Replace failed: Import part is only valid for midi and wave tracks! MusE: load part No track selected for import %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. %n parts out of %1 could not be imported. Likely the selected track is the wrong type. %n part(s) could not be imported. Likely the selected track is the wrong type. %n parts could not be imported. Likely the selected track is the wrong type. Import Wavefile MusE: Song: MusE: Warning The song uses multiple ports but export format 0 (single track) is set. The first track's port will be used. Playback will likely be wrong unless the channels used in one port are different from all other ports. Canceling and setting a different export format would be better. Continue? Wave import error There are too many wave files of the same base name as imported wave file Can not continue. Can't create new wav file in project folder! Failed to initialize sample rate converter! Cancel Annulla Resampling wave file "%1" from %2 to %3 Hz... Output has clipped Resampling again and normalizing wave file "%1" Try %2 of %3... File version is %1.%2 Current version is %3.%4 Conversions may be applied if file is saved! Opening file Do not warn again Cpu load To import an audio file you have first to select a wave track Reading midifile Load configuration colors Color settings will immediately be replaced with any found in the file. Are you sure you want to proceed? &Ok &OK Save configuration colors Timeout waiting for audio to run. Check if jack is running or try another driver. Was not able to start audio, check if jack is running or try another driver. Undo last change to song Redo last undo Loop between left mark and right mark Punch in Record starts at left mark Punch out Record stops at right mark Rewind to start position Rewind current position Move current position Stop sequencer Restart rec Start sequencer play To record press record and then play Turn on/off metronome Create new song New from &Template... Create new song from template &Open... Open song from file Click this button to open an existing song. Save current song Click this button to save the song you are editing. You will be prompted for a file name. Save &As... &Close Import Midifile... Export Midifile... Import Part... Import Audio File... Find Unused Wave Files... Song Info... Edit Instrument... Transpose... Midi Input Transform... Midi Input Filter... Midi Remote Control... Reset Instrument Init Instrument Bounce to File... Global Settings... Appearance... Rewind on Stop Metronome... Midi Sync... Midi File Import/Export... &MusE Homepage... &Did You Know? &About MusE... Snooper (developer tool)... About &Qt... Set left/right marker for bounce range The project has no project sample rate (added 2011). Please enter a rate. The current system rate (%1Hz) is suggested, and cancelling uses it: The project has audio waves, but no project sample rate (added 2011). Please enter a rate. The most common wave rate found is suggested, the project was probably made with it. Cancelling uses the current system rate (%1Hz): Project sample rate This wave file has a samplerate of %1 Hz, as opposed to current setting %2 Hz. A live, real-time samplerate converter can be used on this file. Or, a copy of the file can be resampled now from %1 Hz to %2 Hz. Do you want to use a converter or resample the file now? Use live converter Resample now Unable to launch browser Error launching default browser Recording starts at left mark Recording stops at right mark Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help. Metronome on/off. Press F1 for help. Transport Panel Bigtime Window Show Docks Toggle display of currently visible dock windows. Display MusE main window in full screen mode. Mastertrack Graphic... Send 'note-off' command to all midi channels. Send initialization messages as found in instrument definition. Send 'local-off' command to all midi channels. Take Automation Snapshot Keyboard Shortcuts... Midi Ports / Soft Synths... &Manual (wiki)... File buttons Timeline Recording Sync Position The current project contains unsaved data. Save current project? No valid parts selected The current project contains unsaved data. Load overwrites current project. Save current project? MusEGui::NoteInfo Note Info Start Len Lun Pitch Tono Delta/Absolute mode Velo on Velo off MusEGui::PartCanvas Cannot copy/move/clone to different Track-Type C&ut &Copy clones Part name: %1 Files: Remove selected %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. %n parts out of %1 could not be pasted. Likely the selected track is the wrong type. %n part(s) could not be pasted. Likely the selected track is the wrong type. %n parts could not be pasted. Likely the selected track is the wrong type. Cannot paste: multiple tracks selected Cannot paste: no track selected Can only paste to midi/drum track Can only paste to wave track Can only paste to midi or wave track Cannot paste: wrong data type Normalize S&elect Rename Color Delete Split Glue Super glue (merge selection) De-clone Save part to disk... Part canvas: Use Pencil tool to draw parts. Double-click to create a new MIDI/drum part between the range markers (set with MMB + RMB). Press F1 for help. Part Wave edit... File info... MusE: Save part Automation LMB: Select/Move/Dblclick to edit | CTRL+LMB: Multi select/Move&Copy | CTRL+ALT+LMB: Dblclick to edit in new window | SHIFT+LMB: Select track | MMB: Delete LMB: Draw to resize | MMB: Delete | CTRL+RMB: Trim length LMB: Delete | CTRL+RMB: Trim length LMB: Cut part in two LMB: Merge with following part LMB: Mute selected part LMB: Edit automation events in audio parts MusEGui::PasteDialog %n quarter(s) %n quarters %1 quarter for floating-point arguments like 1.5 %1 quarters for floating-point arguments like 1.5 MusEGui::PasteEventsDialog %n quarter(s) %n quarters %1 quarter for floating-point arguments like 1.5 %1 quarters for floating-point arguments like 1.5 MusEGui::Piano Piano: Press key to play or enter events in step record mode (SHIFT for chords). RMB: Set cursor for polyphonic control events. CTRL+Mousewheel to zoom view vertically. Left click: Play Right click: Select key MusEGui::PianoCanvas Pianoroll canvas: Use Pencil tool to draw and edit MIDI events, Pointer tool to select and edit. Press F1 for help. Note: Velocity: Start (bar): Start (time): Length (ticks): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete | CTRL+RMB: Trim length LMB: Resize | CTRL+LMB: Multi select | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length LMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length MusEGui::PianoRoll &Edit &Modifica C&ut &Copy Copy events in range Copia eventi nell'intervallo &Paste Paste (with dialog) Delete &Events &Select &Seleziona Select &All &Deselect All Invert &Selection &Inside Loop &Outside Loop &Previous Part &Next Part Fu&nctions Fu&nzioni Quantize Quantizza Modify Note Length Modify Velocity Modifica velocity Crescendo/Decrescendo Crescendo/Decrescendo Transpose Erase Events Move Notes Sposta note Set Fixed Length Imposta lunghezza fissa Delete Overlaps Cancella sovrapposizioni Legato &Plugins &Plugin Window &Config &Configura finestra &Event Color &Blue &Pitch colors &Velocity colors Pianoroll tools Step Record Registra passo per passo Midi Input Ingresso Midi ctrl ctrl Add Controller View Aggiungi visualizzazione controller Paste to current part &Display Step record Midi input Play events Event List... &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Display menu: View-specific display options. Add controller view Play single note Play chords MusEGui::PluginDialog Type Lib Label Name Nome AI AO CI CO IP id Maker Copyright Audio inputs Audio outputs Control inputs Control outputs In-place capable ID number Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. dssi synth dssi effect ladspa Plugin categories. Right-click on tabs to manage. Right-click on plugins to add/remove from a category. Associated categories You need to define some categories first. Enter the new group name Wine VST LV2 synth LV2 effect FB 2B 2B Requires fixed block size Requires power-of-2 block size &Create new group &Delete currently selected group Re&name currently selected group New group VST synth VST effect MusE: Select Plugin MusEGui::PluginGui File Buttons Pulsanti file Load Preset Carica preimpostazioni Save Preset Salva preimpostazioni bypass plugin bypassa plugin MusE: load preset MusE: carica preimpostazioni Error reading preset. Might not be right type for this plugin Errore di lettura preset. Forse non è adatto a questo plugin MusE: save preset MusE: salva preimpostazioni Bypass plugin Plugin settings MusE: Save preset Plugin Settings MusEGui::PluginSettings Global setting: On acceso Off spento MusEGui::PopupMenu <More...> %1 MusEGui::PosToolbar Left marker Left marker position Right marker Right marker position Current position Current position in bars/beats SMPTE position Current position in SMPTE time Show/Hide position in ticks and audio frames Show/Hide additional output fields displaying the current position in ticks and audio frames Current position in ticks Current position in audio frames Current position slider MusEGui::ProjectCreateImpl Select directory Seleziona directory MusEGui::RasterLabelCombo Snap MusEGui::RecToolbar Record mode Record mode: Overdub to add new events, Replace to replace overlapping events. Overdub Replace Cycle record mode Cycle record mode: Normal to replace range when loop is finished, Mix to add new events, Replace to replace range on first MIDI input. Normal Mix MusEGui::RouteDialog Normal Alias 1 Alias 2 Source Destination <none> <nulla> MusEGui::RoutePopupMenu Channel Soloing chain Audio returns Warning: No input devices! Open midi config... Apri configurazione Midi... <none> <nulla> More... Audio sends Channel grouping: Mono Stereo Channels Omni Show aliases: First Second Show names Show first aliases Show second aliases Jack ports Connect Unavailable Open advanced router... Output routes: Input routes: Tracks Destinations: Midi sends Sources: ALSA devices JACK devices Synth devices Warning: No output devices! Avviso: Non ci sono dispositivi d'uscita! MusEGui::ScoreCanvas Treble Alti Bass Bassi Grand Staff Pentagramma doppio Remove staff Rimuovi pentagramma Ambiguous part Parte ambigua There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. Ci sono due o più parti a cui potresti aggiungere la nota, ma nessuna coincide con la parte scelta. Scegli la parte di destinazione clickando su una nota della parte e riprovaci di nuovo o aggiungi un nuovo pentagramma contente la parte di destinazione. No part Nessuna parte There are no parts you could add the note to. Non ci sono parti a cui aggiungere la nota. Tools MusEGui::ScoreEdit Step recording tools Step Record Registra passo per passo Note settings last Quantisation settings &Edit &Modifica C&ut &Copy Copy events in range Copia eventi nell'intervallo &Paste Paste (with dialog) Delete &Events &Select &Seleziona Select &All &Deselect All Invert &Selection &Inside Loop &Outside Loop Fu&nctions Fu&nzioni &Quantize Crescendo/Decrescendo Crescendo/Decrescendo Transpose Erase Events Move Notes Sposta note Set Fixed Length Imposta lunghezza fissa Delete Overlaps Cancella sovrapposizioni Legato Window &Config &Configura finestra &Velocity &Part Enter the new score title Error Changing score title failed: the selected title is not unique Step record &Display Quantisation Pixels per whole Note length Velocity Apply to selected notes, or new notes if none is selected Off-Velocity Copy Events in Range Change Note &Length Change Note &Velocity Display menu: View-specific display options. Note Head &Colors &Default Set Up &Preamble Display &Key Signature Display &Time Signature Set Score &Name MusEGui::ScrollScale next page previous page current page number MusEGui::ShortcutCaptureDialog Ok OK Cancel Annulla Shortcut conflicts with %1 Undefined Non definito MusEGui::ShortcutConfig Save printable text file Text files (*.txt);;All files (*) Error Error opening file for saving Shortcuts for selected category: Legend: An error occurred while saving MusEGui::SigScale Signature scale MusEGui::SigToolbar Time signature at current position MusEGui::Strip Remove track? Rimovi traccia? Name Nome Enter track name: MusE: bad trackname Please choose a unique track name Prefer knobs, not sliders Show values in controls Prefer midi volume as decibels Monitor on record-arm automatically Change track name Hide strip Visible audio effects Configuration Actions Azioni MusEGui::SyncToolbar External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::TList <none> <nulla> visible no clef Treble Alti Bass Bassi Grand off spento <unknown> <sconosciuto> MusE: bad trackname Midi control Assign Clear Treble clef Bass clef Grand Staff Pentagramma doppio Viewable automation Internal Synth Delete Track Track Comment Commento traccia Insert Track Midi Midi Drum Presets Clear all controller events? &Ok &OK &Cancel A&nnulla Change color Other Save track's drumlist Load track's drumlist Reset track's drumlist Reset track's drumlist-ordering Copy track's drumlist to all selected tracks Drum map Mappa batteria Reset the track's drum map with instrument defaults? Reset the track's drum map ordering? Muse: Load Track's Drum Map MusE: Store Track's Drum Map SYNTH IS UNAVAILABLE! Please choose a unique track name Show gui Show native gui Clear automation Delete Selected Tracks Track list: Use context menu to create tracks. Click track ID to select, CTRL to add, SHIFT for range, dblclick to select all of same type. Press F1 for help. MusEGui::TempoSig Tempo/Sig Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Time signature at current position MusEGui::TempoToolbar Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Tap Tap this button to set tempo Set the tempo to the time difference of two successive clicks. Times out after two seconds. MusEGui::Toolbar1 Off spento Solo Cursor MusEGui::TopWin Fullscreen Undo/Redo tools Panic Transport Trasporto Tempo Signature Piano roll Drum editor Master track editor Wave editor Marker view Vista marcatori Score editor Arranger Arrangiatore <unknown toplevel type> Metronome Tabbed/Floating Display editor in a tab or in a separate window (preset in Global Settings->Editors). Timeline Recording Sync Position MusEGui::TrackComment MusE: Track Comment Track Comment: MusEGui::Transport Overdub Replace Rec Mode Normal Mix Cycle Rec Loop Left Mark marcatore di sinistra Right Mark marcatore di destra Click this button to rewind to start position Click this button to rewind Click this button to forward current play position Click this button to stop playback Click this button to start playback Click this button to enable recording Punch in Punch out Rewind to Start Rewind Forward Stop Stop Play Riproduci Record Registra Metronome on/off External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::VisibleTracks Show wave tracks Mostra tracce audio Show group tracks Mostra tracce di gruppo Show aux tracks Mostra tracce aux Show input tracks Mostra tracce input Show output tracks Mostra tracce output Show midi tracks Mostra tracce Midi Show synth tracks Mostra tracce synth Visible track types Tipi di traccia visibili MusEGui::WaveCanvas Adjust Wave Offset Wave offset (frames) Part creation failed Left and right position markers must be placed inside the current part. Part created The selected region has been copied to the clipboard and can be pasted in the arranger. MusE - external editor failed MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE - file size changed When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Wave event: Converter settings Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help. MusEGui::WaveEdit &Edit &Modifica Func&tions &Gain Other &Copy C&ut &Paste Mute Selection Normalize Selection Fade In Selection Fade Out Selection Reverse Selection Select Select &All &Deselect All Window &Config &Configura finestra WaveEdit tools Solo Cursor &Create Part from Region &Previous Part &Next Part &Event Color &Part colors &Gray Edit in E&xternal Editor... &Display Display menu: View-specific display options. OrganGuiBase MusE: Organ Drawbars 16' 16' 4' 4' 2 2/3' 2 2/3' 2' 2' 5 1/3' 5 1/3' 8' 8' Envelope Hi Release Sustain Decay Attack ms cB Envelope Lo O-1 Oscillator Brass Reed Flute Velocity PasteDialog %n quarter(s) %n quarters quarter quarter quarters quarters PasteDialogBase MusE: Paste Parts Number and raster times ticks Move, Merge, Clone Move everything to the right Move only affected parts to the right Put everything into a single track Merge with existing parts Insert as clones (where possible) OK OK Cancel Annulla Insert Raster PasteEventsDialogBase MusE: Paste Events Number and raster times ticks Paste options Put everything into the (selected) part OK OK Cancel Annulla &Always into existing parts &Never into existing parts &Into existing parts if part has not to be expanded by more than Paste midi controller target options Keep e&xisting Erase existin&g Erase WYSIWYG Erase inclusive Insert Raster PluginDialogBase Dialog Ports: Mono Mono + Stereo Stereo All Plugin type: Filter: &OK &Cancel A&nnulla PluginSettings Dialog Options Fixed speed Transport affects audio latency Override reported audio latency Reported audio latency override value Revert HiDPI scaling of native GUI Set in Global settings->GUI tweaks Global setting Additional information Follow global setting On acceso Off spento ProjectCreate Create Project Projects folder: ... ... Project Name: Project is a Template Write window state Project song file type: Project Path to song file: Song information: &Create project folder (recommended for audio projects) QObject Error Please first select the range for crescendo with the loop markers. Controller ! Other ! Switch on General Midi Level 1 mode Switch on General Midi Level 2 mode Switch off General Midi Level 1 or 2 Switch on Roland GS mode Switch on Yamaha XG mode Tracks: Midi ports: Midi devices: Jack: Jack midi: Current device Others Files Synthesizers Open Closed <Properties> <Base Property> <Property> Input routing Warning: No input routes! Click to connect... Output routing Warning: No output routes! Click to connect... TRACK TAKE MusE import error. MusE failed to import the file. Possibly this wasn't a sound file? If it was check the permissions, MusE sometimes requires write access to the file. Instruments FX slot QWidget Cannot convert sysex string Hex String too long (2048 bytes limit) None No selection. Ignoring MusE: get file name The directory %1 does not exist. Create it? MusE: create directory File %1 exists. Overwrite? Open File %1 failed: %2 MusE: write MusE: Open File Instrument-defined Edit instrument ... Add Others Common Controls Velocity No wave events selected. Generic midi Creating dir failed Create peakfile for New Nuovo QuantBase MusE: Quantize Range Intervallo All Events Tutti gli eventi Selected Events Eventi selezionati Looped Events Eventi del loop Selected Looped Eventi del loop selezionati Values Valori Strength: % % Threshold (ticks): Quantize Len Raster Whole Half 4th 4th Triplet 8th 8th Triplet 16th 16th Triplet 32th 32th Triplet Swing: If the proposed change in tick or length is smaller than threshold, nothing is done. If swing=0, this is normal If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. OK OK Cancel Annulla Parts Parti Selected parts or c&urrent part All parts &in editor RemoveBase MusE: Erase Notes Range Intervallo All Events Tutti gli eventi Selected Events Eventi selezionati Looped Events Eventi del loop Selected Looped Eventi del loop selezionati Thresholds ticks Velocity Length <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> OK OK Cancel Annulla All Eve&nts Selec&ted Events &Looped Events Selected L&ooped Parts Parti Selected parts or c&urrent part All parts &in editor RouteDialogBase MusE: Routing Connect Remove List of available sources. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Connections View window. Shows all current connections. Thick lines are Omni Routes. Thin lines are Channel Routes. List of available destinations. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Itemized list of current connections. Show only selected sources Show only destination routes for selected source Show only selected destinations Show only source routes for selected destination Show all Midi Ports Auto adjust column size Automatically adjusts the source and destination tree widths when the splitters are adjusted. This also turns on text word wrap, which may cause slower response with larger lists. Preferred route name or alias Connect source to destination Remove selected route RubberbandSettingsBase Rubberband Settings Rubberband support is not enabled Use default settings Presets: Impostazioni predefinite: Default Percussion Maximum Stretch: Elastic Precise Window: Standard Short Long Smoothing: Off spento On acceso Channels: Apart Together Transients: Crisp Mixed Smooth Detector: Compound Percussive Soft Phase: Laminar Independent Formant: Shifted Preserved Pitch: Tono: High speed High quality High consistency OK OK Cancel Annulla SRCResamplerSettingsBase SRC Resampler Settings Use default settings Converter Type: SINC Best quality SINC Medium quality SINC Fastest Zero Order Hold Linear OK OK Cancel Annulla SS_PluginChooserBase Name Nome &Cancel A&nnulla SS_PluginFront Clear and unload effect Load effect Toggle display of effect parameters Turn effect on/off SetlenBase MusE: Set Note Length Range Intervallo All Events Tutti gli eventi Selected Events Eventi selezionati Looped Events Eventi del loop Selected Looped Eventi del loop selezionati Value Valore New length ticks OK OK Cancel Annulla Parts Parti Selected parts or c&urrent part All parts &in editor ShortcutCaptureDialogBase Enter shortcut sequence Immetti sequenza scelta rapida Press keys to enter shortcut sequence! Premi dei tasti per immettere la sequenza di scelta rapida! Old shortcut: Scelta rapida precedente: Undefined Non definito New shortcut: Nuova scelta rapida: OK OK Cancel Annulla ShortcutConfigBase Configure Keyboard Shortcuts Shortcut Category Shortcut Description &Clear Alt+C &Define Alt+D &Apply &Applica Alt+A &Printable file... Alt+P Alt+P &Ok &OK Alt+O Filter: &Reset all SimpleDrumsGuiBase DrumSynth 0.1 DrumSynth 0.1 Audio file assignments. Within parantesis: note number and traditional midi drum name SimplePluginChooserBase SimpleDrums - Ladspa Plugin Chooser Name Nome Label Inports Outports Creator &Cancel A&nnulla Alt+C &OK Alt+O SimpleSynthGui &Load setup &carica impostazioni &Save setup &salva impostazioni Load sample dialog Finestra caricamento campioni Mix Chn Channel routing SnooperDialogBase Snooper Mode Auto-hide Separate parented top levels Update tree Mouse clicks Key presses Flash timer s Reset Filters MusEGui QWidget Properties Search: Class name: Object name: About This tool helps developers and designers discover objects. Click on anything anywhere or press a key to highlight an item. It will NOT auto-update when new objects are created. Click 'Update tree'. When open, may slow program slightly. This dialog is NOT shown. Class name::Object name Property Type Value Valore Event type SongInfo Song Information Show on song load &Cancel A&nnulla Alt+C &Ok &OK Alt+O SynthConfigBase Name Nome Type File Inst Version Description &Apply &Applica &OK Configure midi devices, midi ports, and synthesizers Available soft synthesizers Useable devices Add: Synth ALSA JACK Rename Remove Device port assignments List of available software synthesizers TransposeBase MusE: Transpose Range Intervallo All Events Tutti gli eventi Selected Events Eventi selezionati Looped Events Eventi del loop Selected Looped Eventi del loop selezionati Value Valore Halftone-steps OK OK Cancel Annulla Parts Parti Selected parts or c&urrent part All parts &in editor UnusedWaveFiles Dialog List of unused audio files in current project directory: Current project All .med files in current directory Move files to 'unused' subdir Cancel Annulla VAMGui MusE: Load VAM Presets MusE: Save VAM Presets VAMGuiBase Virtual Analogue for MusE LFO Freq Waveform Sine Pulse Saw Triangle Filter EnvMod Attack Decay Sustain Release Cutoff Resonance Invert Inverti selezione KeyTrack Presets Set load preset list save preset list save preset list to a new file delete preset DCO 1 Pitch Tono Detune PWM FM PW DCO 2 On acceso VAM 1.0beta3 Virtual Analog for MusE Released under GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VelocityBase MusE: Modify Velocity Range Intervallo All Events Tutti gli eventi Selected Events Eventi selezionati Looped Events Eventi del loop Selected Looped Eventi del loop selezionati Values Valori Rate: Rata: % % Offset: Offset: veloNew = (veloOld * rate) + offset OK OK Cancel Annulla Parts Parti Selected parts or c&urrent part All parts &in editor ZitaResamplerSettingsBase Zita Resampler Settings Zita Resampler support is not enabled Use default settings Options: OK OK Cancel Annulla file_patterns Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) All Files (*) Midi (*.mid) Karaoke (*.kar) all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) med Files (*.med *.med.gz *.med.bz2) Uncompressed med Files (*.med) gzip compressed med Files (*.med.gz) bzip2 compressed med Files (*.med.bz2) mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) (*.jpg *.gif *.png) (*.jpg) (*.gif) (*.png) part Files (*.mpt *.mpt.gz *.mpt.bz2) part Files (*.mpt) gzip compressed part Files (*.mpt.gz) bzip2 compressed part Files (*.mpt.bz2) Presets (*.pre *.pre.gz *.pre.bz2) Presets (*.pre) gzip compressed presets (*.pre.gz) bzip2 compressed presets (*.pre.bz2) Presets (*.map *.map.gz *.map.bz2) Presets (*.map) gzip compressed presets (*.map.gz) bzip2 compressed presets (*.map.bz2) Binary (*.bin) Wave/Binary (*.wav *.ogg *.flac *.bin) Wave (*.wav *.ogg *.flac) Color configuration files (*.cfc) Qt style sheets (*.qss) freeverb FreeVerb Room Size Damping Wet Level Tap-Reverberator Decay [ms] dB dB Dry Level [dB] Wet Level [dB] Preset: AfterBurn AfterBurn (Long) Ambience Ambience (Thick) Ambience (Thick) - HD Cathedral Cathedral - HD Drum Chamber Garage Garage (Bright) Gymnasium Gymnasium (Bright) Gymnasium (Bright) - HD Hall (Small) Hall (Medium) Hall (Large) Hall (Large) - HD Plate (Small) Plate (Medium) Plate (Large) Plate (Large) - HD Pulse Chamber Pulse Chamber (Reverse) Resonator (96 ms) Resonator (152 ms) Resonator (208 ms) Room (Small) Room (Medium) Room (Large) Room (Large) - HD Slap Chamber Slap Chamber - HD Slap Chamber (Bright) Slap Chamber (Bright) HD Smooth Hall (Small) Smooth Hall (Medium) Smooth Hall (Large) Smooth Hall (Large) - HD Vocal Plate Vocal Plate - HD Warble Chamber Warehoouse Warehouse - HD Comb Filters Allpass Filters Bandpass Filters Enhanced Stereo main File to open Alsa midi only (using dummy audio driver) Use RtAudio driver Use JAckAudio driver to connect to Jack audio server Do not try to auto-start the Jack audio server Do not auto-populate midi ports with midi devices found, at startup Force inclusion of ALSA midi even if using Jack Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) Force midi real time priority to n (default: audio driver prio -1) Force plugin cache re-creation. (Automatic if any plugin path directories changed.) Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care. Don't load LADSPA plugins Don't load MESS plugins Don't load VST plugins Don't load DSSI plugins Don't load LV2 plugins Don't use LASH Force locale to the given language/country code (xx = Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins. Debug mode: no threads, no RT Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively! Debug mode: trace midi Input Debug mode: trace midi Output Debug mode: trace sync Enable Python control support Pyro nameserver host name Pyro nameserver host port Pyro daemon host name Pyro daemon host port Pyro communication timeout in seconds Don't load LinuxVST plugins midiWarnInitPendingBase Instrument initialization MusE should now send some Instrument Initialization Sequences. The sequences (usually System Exclusive messages) are defined by the selected instruments in the Settings -> Midi Ports dialog, such as the GM (default), GS, or XG instruments. Typically you should answer yes here. You can always do it manually from the Midi menu. Continue? Don't ask me again shortcuts Transport: Start playback from current location Trasporto: inizia riproduzione dalla posizione corrente Transport: Toggle metronome Trasporto: attiva/disattiva metronomo Transport: Stop Playback Trasporto: arresta riproduzione in corso Transport: Goto Start Trasporto: vai all'inizio Transport: Play, Stop, Rewind Trasporto: riproduzione, arresto, riavvolgimento Transport: Goto left marker Trasporto: vai al marcatore di sinistra Transport: Goto right marker Trasporto: vai al marcatore di destra Transport: Toggle Loop section Trasporto: attiva/disattiva selezione loop Transport: Toggle Record Trasporto: attiva/disattiva registrazione Transport: Clear all rec enabled tracks Trasporto: cancella tutte le tracce abilitate alla registrazione Toggle fullscreen attiva/disattiva visualizzazione a tutto schermo Edit: Copy Modifica: copia Edit: Copy in range Modifica: copia nell'intervallo Edit: Undo Modifica: annulla Edit: Redo Modifica: ripristina Edit: Cut Modifica: taglia Edit: Paste Modifica: incolla Edit: Paste (with dialog) Modifica: incolla (con dialogo) Edit: Delete Modifica: cancella File: New project File: nuovo progetto File: Open from disk File: apri da disco File: Save project File: salva progetto File: Open recent file File: apri file recente File: Save as File: salva come File: Load template File: carica modello File: Import midi file file: importa file Midi File: Export midi file File: esporta file Midi File: Import midi part File: importa parte Midi File: Import audio file File: importa file audio File: Quit MusE File: chiudi MusE Edit: Select parts on track Modifica: seleziona parti sulla traccia Open pianoroll Apri pianoroll Open drumeditor Apri editor batteria Open listeditor Apri editor liste Open waveeditor Apri editor audio Open graphical mastertrack editor Apri editor grafico traccia master Open list mastertrack editor Apri editor lista tracce master Open midi transformer Apri trasformatore Midi Add midi track Aggiungi traccia Midi Add drum track Aggiungi traccia batteria Add wave track Aggiungi traccia audio Add audio output Aggiungi uscita audio Add audio group Aggiungi gruppo audio Add audio input Aggiungi ingresso audio Add audio aux Aggiungi audio aux Structure: Global cut Struttura: taglio globale Structure: Global insert Struttura: inserimento globale Structure: Global split Struttura: divisione globale Structure: Cut events Struttura: taglio eventi View: Open mixer #1 window View: Open mixer #2 window View: Toggle transport window View: Toggle bigtime window View: Open marker window Settings: Follow song by page Settings: Follow song off Settings: Follow song continuous Settings: Global configuration Settings: Configure shortcuts Settings: Configure metronome Settings: Midi sync configuration Settings: Midi file import/export configuration Settings: Appearance settings Settings: Audio subsystem configuration Midi: Edit midi instruments Midi: Open midi input transform Midi: Open midi input filter Midi: Midi input transpose Midi: Midi remote control Midi: Random rhythm generator Midi: Reset midi Midi: Init midi Midi: Midi local off Audio: Bounce audio to track Audio: Bounce audio to file Audio: Restart audio Automation: Take mixer snapshot Automation: Clear mixer automation Help: Open Manual Help: Toggle whatsthis mode Edit: Edit selected part Edit: Select nearest part on track above Edit: Add nearest part on track above Edit: Select nearest part on track below Edit: Add nearest part on track below Edit: Insert empty measure Select track above Select track below Midi: Transpose Edit: Select all Edit: Select none Edit: Invert Selection Edit: Select events/parts inside locators Edit: Select events/parts outside locators Edit: Select previous part Edit: Select next part Edit: Select nearest part/event to the left or move cursor Edit: Add nearest part/event to the left to selection Edit: Select nearest part/event to the right or move cursor Edit: Add nearest part/event to the right to selection Edit: Set locators to selection Edit: Increase pitch Edit: Decrease pitch Edit: Increase event position Edit: Decrease event position View: Zoom in View: Zoom out View: Goto Current Position View: Scroll left Edit: Set Fixed Length on Midi Events Quantize Quantizza Modify Note Length Modify Velocity Modifica velocity Edit: Crescendo Edit: Thin Out Edit: Erase Event Edit: Delete Overlaps Edit: Note Shift Edit: Move Clock Edit: Copy Measure Edit: Erase Measure Edit: Delete Measure Edit: Create Measure Edit: Change Event Color Tool: Pointer Tool: Pencil Tool: Eraser Tool: Line Draw Tool: Cursor Add note velocity 1 Add note velocity 2 Add note velocity 3 Add note velocity 4 Cursor step size: larger Cursor step size: smaller Instrument/Cursor up Instrument/Cursor down Tool: Scissor Tool: Glue Tool: Mute Transport: Increase current position Transport: Decrease current position Transport: Increase current position, no snap Transport: Decrease current position, no snap Quantize: Set quantize to 1/1 note Quantize: Set quantize to 1/2 note Quantize: Set quantize to 1/4 note Quantize: Set quantize to 1/8 note Quantize: Set quantize to 1/16 note Quantize: Set quantize to 1/32 note Quantize: Set quantize to 1/64 note Quantize: Toggle triol quantization Quantize: Toggle punctuation quantization Quantize: Toggle punctuation quantization (2) Edit: Insert at location Edit: Increase length Edit: Decrease length Insert Note Insert SysEx Insert Ctrl Insert Meta Insert Tempo Insert Signature Change Event Position Edit Event Value Insert Key Goto Next Marker Goto Prev Marker Edit: Paste to selected track Edit: Paste clone Edit: Paste clone to selected track Transport: Step record Transport: Midi input Transport: Play events Edit: Increase velocity Edit: Decrease velocity Move: Move to selected note Tool: Pan Tool: Zoom Tool: Range Edit: Duplicate track Transport: Restart recording Edit selected track name Normalize Transport: Goto End Transport: Restart recording (multi take) Transport: Arm current track Toggle rewind on stop Edit: Paste to current part File: New project from template Edit: Rename part Mute all currently selected tracks Solo all currently selected tracks Tool: Stretch Tool: Sample rate Toggle tabbed/floating editor window Toggle docks display Show Arranger window Open track Synth GUI Open pianoroll in new window Open drumeditor in new window Open listeditor in new window Open waveeditor in new window Insert midi track Insert drum track Insert wave track Insert audio output Insert audio group Insert audio input Insert audio aux View: Toggle mixer strip display Settings: Midi ports / Soft synths Quantize: Set quantize to off Select strip to the left Select strip to the right Mixer strip volume down Mixer strip volume up Mixer strip pan left Mixer strip pan right Mixer strip volume down page Mixer strip volume up page Mixer strip pan left page Mixer strip pan right page warnBadTimingBase Bad timing Timing difettoso Message here Don't warn me again muse-4.0.0/src/share/locale/muse_pl.ts000066400000000000000000026732121404105311000176320ustar00rootroot00000000000000 @default Add Midi Track Dodaj Ślad Midi Add Drum Track Dodaj Ślad Perkusyjny Add Wave Track Dodaj Ślad Audio Add Audio Output Dodaj Wyjście Audio Add Audio Group Dodaj Grupę(Szynę) Audio Add Audio Input Dodaj Wejście Audio Add Aux Send Dodaj Wysyłkę Audio Route Połączenie channel="%1" kanał="%1" dest cel name="%1"/ nazwa="%1"/ <none> <brak> AboutBox AboutBox O programie MusE MusE (C) Copyright 1999-2020 the MusE development team. See https://muse-sequencer.github.io for new versions and more information. Published under the GNU Public License. Internals System information (useful for debugging): (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2015 Werner Schweer i inni. Nowe wersje oraz więcej informacji na stronie: http://www.muse-sequencer.org. Program wydany na licencji GNU Public License System information: Informacja o systemie: &Keep On Rocking! Alt+K AppearanceDialogBase Apply Zastosuj Ok Akceptuj Cancel Anuluj Arranger Główne Okno MusE: Appearance settings MusE: ustawienia wyglądu Parts Klocki show names Pokaż nazwy show events pokaż zdarzenia MIDI show Cakewalk Style pokaż w stylu Cakewalk y-stretch Rozciągnij w pionie Events Elementy MIDI note on Dźwięki poly pressure Poly pressure controller Kontrolery MIDI aftertouch Docisk (Aftertouch) pitch bend Odchylenie stroju (Pitch Bend) program change Zmiana programu (program change) special Specjalne Wave Drawing in Parts Wykres audio w klockach Only Outline Tylko obrys RMS/PEAK(Traditional) Tradycyjny (RMS/PEAK) Background picture Obrazek w tle show snap grid pokaż siatkę przyciągania Colors Kolory Items Składniki Color name: Nazwa koloru: Global opacity Globalna przeźroczystość Show names Pokaż nazwy Show e&vents Show Ca&kewalk style O&nly outline Add Dodaj Remove Usuń Clear Wyczyść Show snap grid Choose color... Add to palette Save all... Load all... Help Style/Fonts Style/Czcionki Icon size Custom cursor size Distribute current system font family to all other fonts System Font sizes for fonts 1-6 are determined at every program start, to optimally match the currently used system font. Disable if you want to set fixed sizes in the font settings below. Default scaling factors: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% RMS/PEAK (&traditional) Current theme If a user style sheet for one of the MusE color schemes exists and is selected, it will be merged with the default style sheet for this scheme. The user style sheet takes precedence and overwrites identical settings in the default style sheet. Cascade default and user theme style sheets Show icons in menus Use native standard dialogs Calculate and apply relative font sizes dynamically at program start Maximum aliased size, 0 = no alias Insert Wstaw Size (pt) Fill in optimal font sizes in relation to the system font At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. QT Theme Styl Qt Windows Windows MusE MusE Metal Metaliczny Norwegian Wood Norweskie drzewo Platinum Platyna CDE CDE Motif Motif Motif Plus Motif Plus May require restarting MusE for best results Zrestartuj MusE dla lepszego efektu MusE color scheme Zestaw kolorów MusE current settings Bieżące ustawienia Change Zmień Fonts Czcionki Family Rodzina Czcionek Size Rozmiar Font 1 Czcionka 1 Themes Style Font 2 Czcionka 2 Font 3 Czcionka 3 Font 0 Czcionka 0 Bold Pogrubienie Italic Kursywa ... ... Font 4 Czcionka 4 Font 5 Czcionka 5 Palette Paleta kolorów add dodaj show e&vents Pokaż &zdarzenia MIDI show Ca&kewalk Style Pokaż w stylu Ca&kewalk remove usuń add to palette dodaj do palety B B S S H H V V G G R R clear wyczyść Style Sheet: Arkusz stylów: Font 6 Czcionka 6 ArrangerColumnsBase Configure arranger columns Konfiguracja kolumn w oknie głównym Columns: Kolumny: Name: Nazwa: Controller type: Typ kontrolera MIDI: Midi controller type Rodzaj kontrolera MIDI Affect CCs at Control7 Kontroler7 Control14 Kontroler14 RPN RPN NRPN NRPN Pitch Transpozycja Program Program H-Ctrl H-Ctrl Midi controller number high byte Numer kontrolera MIDI: starszy bajt L-Ctrl L-Ctrl Midi controller number low byte Numer kontrolera MIDI: młodszy bajt * wild card affect CCs at wyślij komunikaty CC na begin of song początku utworu current position bieżącej pozycji &Add &Dodaj &Delete &Kasuj Done Gotowe AudioConverterSettingsBase Audio Converter Settings Available audio converters Offline settings... Realtime settings... Gui settings... Use default preferences (see Global Settings) Preferences When only samplerate conversion is required, use: When time stretching or pitch shifting is required, use: OK Akceptuj Cancel Anuluj Awl::MidiVolEntry off wył. db db Awl::VolEntry off wył. ChooseSysexBase Dialog Hex: Comment: Opis: &OK &Akceptuj &Cancel &Anuluj ClipListEditorBase MusE: ClipList MusE: Lista Klipów Name Nazwa Refs Odn. Samplerate Częst. próbkowania Len Długość Data Dane Status Clip Properties Właściwości klipu Pos: Poz.: Len: Dł.: CommentBase Form1 Form. 1 Track Comment Opis śladu Track 1 Ślad 1 ConfigMidiFileBase &OK &Akceptuj &Cancel &Anuluj 0 (single track) 0 (pojedynczy ślad) 1 (multiple tracks) 1 (wiele śladów) Format: Format: Export 96 96 192 192 384 384 Division: Rozdzielczość: Copyright: Prawa autorskie: MusE: Config Midi File Import/Export MusE: konfiguracja importu/eksportu plików MIDI Import: Import: Split tracks into &parts &Podziel ślady na klocki Alt+P Alt+P Split tracks into parts, or one single part Podziel ślady MIDI na poszczególne klocki Import Default instrument: Domyślny instrument: Device Name metas trump Port metas if both exist May be incorrect W przypadku konfliktu, meta-nazwy urządzenia przykrywają meta-nazwy portu Instrument Name metas trump Mode sysexes if both exist May be incorrect W przypadku konfliktu, meta-nazwy instrumentu przykrywają komunikaty Mode SysEx Use new-style drum tracks Ślady perkusyjne nowego typu Use old-style drum tracks Ślady perkusyjne starego typu Export: Eksport: Enable extended smf format (currently not implemented) Użyj rozszerzonego formatu SMF (nie zaimplementowano) Running Status saves space by not repeating event status bytes. If this is off every event will have a status byte, increasing file size. Tryb Running Status pozwala na oszczędzanie miejsca poprzez unikanie powtarzania bajtów statusu zdarzenia. Jeśli opcja jest wyłączona, każdemu zdarzeniu będzie towarzyszył bajt statusu, co spowoduje zwiększenie rozmiaru pliku. Use Running &Status Tryb Running &Status To turn a note off some devices can use note-offs or zero-velocity note-ons or both. When used with Running Status this setting saves space. It is safe to leave this off. By wyłączyć dźwięk, urządzenia mogą używać komunikatów Note Off, Note On z zerową wartością parametru velociy bądź obu. W przypadku korzystania z trybu Running Status ustawienie to pozwala na zredukowanie liczby przesyłanych komunikatów MIDI i oszczędność miejsca. Bezpieczniej jest pozostawić tę opcję wyłączoną. Replace note-offs with &zero velocity note-ons Zamieniaj Note Off na Note On z &zerowym velocity Use &2-byte time signatures instead of standard 4 Używaj &2-bajtowego metrum, zamiast standardowego 4-bajtowego If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum note controllers. If disabled, drum maps are ignored during export. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. See "What's This ?" for more info. If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum controllers. Any Port overrides cause a separate track to be created (requires format 1). Any Channel overrides will still appear in the same track in the exported file, but upon re-import into an app such as MusE, MusE will treat that as a new track and create one. If disabled, drum maps are ignored. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. Upon re-importing into another app, it is up to the other app to redirect the particular drum ports, channels and notes to make the song sound like it was intended. Drum map overrides are unique to MusE. If re-importing into MusE, for faithful reproduction disable this setting and apply a drum map and some overrides later. If re-importing or opening in another midi player, enable this setting so that the player can have a chance at faithful reproduction. Some user adjustment may be required (choice of instruments, ports, etc). Apply drum map Port, Channel, ANote overrides If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. See "What's This ?" for more info. If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. The extra track is automatically created. In this mode all events on a track in the midi file are on the same channel. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. In this mode the track events can be on different channels. But if the file is re-imported into an app such as MusE, MusE will automatically create the extra track. Drum map Channel overrides go to a separate track Export instrument or mode: Eksport instrumentu/trybu: Mode sysexes Mode SysEx Export port or device metas: Eksport meta portu lub urządzenia (Port/Device metas): Instrument name metas Meta-nazwy instrumentu Both Oba Port metas Meta-nazwy portu Device name metas Meta-nazwy urządzenia Export a Port/Device meta for format 0 Eksportuj Port/Device meta dla formatu 0 Note: Format 0 uses the FIRST midi track's name/comment in the arranger Uwaga: dla formatu 0 użyte zostaną nazwy i komentarze z pierwszego śladu MIDI Save space by replacing note-offs with &zero velocity note-ons Oszczędzanie miejsca poprzez zamianę zdarzeń note-off na zdarzenia note-on z &zerową wartością parametru velocity CopyOnWriteDialogBase Copy Wave Files Kopiowanie plików audio Some sound files will be copied to the Project Directory, either because they are not writable or because more than one independent Wave Event shares them. (If you prefer instead that the Wave Events are inter-dependent, try using Clone Parts.) Multiple copies will be made in some cases. If no Project has been created yet, you will be asked to, giving another chance to cancel. Niektóre pliki audio zostaną skopiowane do katalogu projektu, ponieważ są one niezapisywalne lub są dzielone pomiędzy kilka niezależnych zdarzeń audio (należy użyć klonowania klocków, jeśli zdarzenia mają być współzależne). W niektórych przypadkach pliki mogą być skopiowane wielokrotnie. Jeżeli żaden projekt nie został jeszcze utworzony, pojawi się prośba o jego stworzenie, co da kolejną szansę wycofania się. These files will be copied to the Project Directory: Następujące pliki zostaną skopiowane do katalogu projektu: CrescendoBase MusE: Crescendo/Decrescendo MusE: crescendo/decrescendo Range Zakres Looped Events Pomiędzy lokatorami &Looped Events Selected Looped Zaznaczone pomiędzy lokatorami Parts Klocki Selected parts or c&urrent part All parts &in editor Values Wartości Start velocity Początkowa velocity % End velocity Końcowa velocity &Absolute Rela&tive Absolute Bezwzględna Relative Względna OK Akceptuj Cancel Anuluj DeicsOnzeGui Save configuration Zapisz konfigurację Critical Error Błąd krytyczny Cannot open file %1 Nie można otworzyć pliku %1 Parsing error for file %1 Błąd parsowania pliku %1 Load category dialog Ładuj kategorię Load set dialog Ładuj zestaw Save set dialog Zapisz zestaw New category Nowa kategoria Delete category Kasuj kategorię Load category Ładuj kategorię Save category Zapisz kategorię Load set Ładuj zestaw Save set Zapisz zestaw Delete set Kasuj zestaw New subcategory Nowa podkategoria Delete subcategory Kasuj podkategorię Load subcategory Ładuj podkategorię Save subcategory Zapisz podkategorię New preset Nowe ustawienie Delete preset Kasuj ustawienie Load preset Ładuj ustawienie Save preset Zapisz ustawienie No more category supported Nie można użyć większej liczby kategorii You can not add more categories Nie można dodać większej liczby kategorii Do you really want to delete %1 ? Na pewno chcesz skasować %1? &Yes &Tak &No &Nie No category selected Nie wybrano żadnej kategorii You must first select a category. Musisz najpierw wybrać kategorię. Replace or add Zamień lub dodaj %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? Kategoria %1 ma zostać przyporządkowana do banku (HBank) nr %2, jednak jest on zajęty. Nadpisać, czy dodać do kolejnego wolnego slotu? &Replace &Zastąp &Add &Dodaj Download error Błąd pobierania There is no more free category slot. Brak wolnych miejsc na kategorię. Save category dialog Zapisz kategorię No more subcategory supported Nie można użyć większej liczby podkategorii You can not add more subcategories Nie można dodać większej liczby podkategorii No subcategory selected Nie wybrano żadnej podkategorii You must first select a subcategory. Musisz najpierw wybrać podkategorię. Load subcategory dialog Ładuj podkategorię %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? Podkategoria %1 ma zostać przyporządkowana do banku (LBank) nr %2, jednak jest on zajęty. Nadpisać, czy dodać do kolejnego wolnego slotu? There is no more free subcategory slot. Brak wolnych miejsc na podkategorię. Save subcategory dialog Zapisz podkategorię No more preset supported Nie można użyć większej liczby ustawień You can not add more presets Nie można dodać większej liczby ustawień No preset selected Nie wybrano żadnego ustawienia You must first select a preset. Musisz najpierw wybrać ustawienie. Load preset dialog Ładuj ustawienie %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? Ustawienie %1 ma zostać przyporządkowane do programu (Prog) nr %2, jednak jest on zajęty. Nadpisać, czy dodać do kolejnego wolnego slotu? There is no more free preset slot. Brak wolnych miejsc na ustawienia. Save preset dialog Zapisz ustawienie Browse set dialog Przeglądanie zestawów Browse image dialog Przeglądanie obrazów DeicsOnzeGuiBase DeicsOnze &Preset &Ustawienie Program numerous Numer programu INITVOICE LBank Subcategory Podkategoria Bank numerous Numer banku NONE BRAK HBank Category Kategoria Prog Preset Ustawienie DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Wydany na licencji GPL. &Global &Globalnie Pitch Envelope PL3 PL2 PL1 PR1 PR2 PR3 Pitch modulation depth LFO LFO Sync Pitch modulation sensitivity Pitch Modulation Sensitivity LFO Delay LFO delay LFO speed Amplitude modulation depth Amplitude modulation sensitivity Amplitude Modulation Sensitivity AMS LFO Waveform Pitch Modulation Depth PMD LFO Speed AMD Speed Delay Opóźnienie PMS Modulation Matrix Matryca modulacji <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> <b>Algorytm 1</b> : <i>Op 1</i> modulowany przez <i>Op 2</i> modulowany przez <i>Op 3</i> modulowany przez <i>Op 4</i><br> <b>Algorytm 2</b> : <i>Op 1</i> modulowany przez <i>Op 2</i> modulowany przez both <i>Op 3</i> i <i>Op 4</i><br> <b>Algorytm 3</b> : <i>Op 1</i> modulowany przez <i>Op 4</i> i <i>Op 2</i> modulowany przez <i>Op 3</i><br> <b>Algorytm 4</b> : <i>Op 1</i> modulowany przez <i>Op 2</i> i <i>Op 3</i> modulowany przez <i>Op 4</i><br> <b>Algorytm 5</b> : (<i>Op 1</i> modulowany przez <i>Op 2</i>) dodaj do (<i>Op 3</i> modulowany przez <i>Op 4</i>) <br> <b>Algorytm 6</b> : dodanie trzech <i>Op 1, 2, 3</i> wszystkie modulowane przez <i>Op 4</i><br> <b>Algorytm 7</b> : dodanie trzech <i>Op 1, 2, 3</i> z <i>Op 3</i> modulowane przez <i>Op 4</i><br> <b>Algorytm 8</b> : dodanie czterech <i>Op 1, 2, 3, 4</i> Op4 Feedback Feedback level of the operator 4 Transpose Transponuj Op &1 Scaling 1 LS1 RS1 Rate Scaling Attack Rate of the operator 1 Level Scaling Amplitude Envelope 1 RR1 D1R1 D1L1 D2R1 Release Rate 2° Decay Rate 1° Decay Level 1° Decay Rate Attack Rate AR1 Detune, OSCWave, EGShift 1 DET1 Detune EG Shift 96dB 48dB 24dB 12dB Wave form Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi wtedy sin(<b>t</b>) w przeciwnym razie 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi wtedy sin(<b>t</b>)*abs(sin(<b>t</b>)) w przeciwnym razie 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi wtedy sin(2*<b>t</b>) w przeciwnym razie 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi wtedy sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) w przeciwnym razie 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi wtedy abs(sin(2*<b>t</b>)) w przeciwnym razie 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi wtedy sin(2*<b>t</b>)*sin(2*<b>t</b>) w przeciwnym razie 0</i> Sensitivity 1 KVS1 Amplitude Modulation Enable AME1 Keyboard Velocity Sensitivity Key Velocity Sensitivity EBS1 EG Bias Sensitivity Eg Bias Sensitivity Frequency 1 Coarse 1 Coarse Ratio Fine 1 Fine Ratio Freq 1 Fixed Frequency Toggle Fix Frequency FIX OUT 1 Output Volume Volume Głośność Op &2 Amplitude Envelope 2 D1R2 D1L2 D2R2 RR2 AR2 Frequency 2 Coarse 2 Fine 2 Freq 2 Scaling 2 LS2 RS2 OUT 2 Detune, OSCWave, EGShift 2 DET2 Sensitivity 2 EBS2 KVS2 AME2 Op &3 Amplitude Envelope 3 D1R3 D1L3 D2R3 RR3 AR3 Scaling 3 LS3 RS3 OUT 3 Frequency 3 Coarse 3 Fine 3 Freq 3 Detune, OSCWave, EGShift 3 DET3 Sensitivity 3 EBS3 KVS3 AME3 Op &4 amplitude Envelope 4 AR4 D1R4 D1L4 D2R4 RR4 Frequency 4 Coarse 4 Fine 4 Freq 4 Scaling 4 LS4 RS4 OUT 4 Detune, OSCWave, EGShift 4 DET4 Sensitivity 4 EBS4 KVS4 AME4 &Func Delay Pan Depth Delay Pan LFO Freq Delay Ch Send Level Channel Chorus Delay Feedback Delay On/Off, Return Level On Delay Beat Ratio Delay BPM Foot Control Pitch Bend Range Pitch Amplitude Modulation Wheel Breath Control Pitch Bias Envelope Bias After Touch Docisk (Aftertouch) Phony Mode POLY MONO Potamento Portamento Mode FINGER FULL PT Portamento Time C&horus Chorus Parameters Channel send level On/Off and Return level Select LADSPA plugin Change plugin &Reverb Reverb Parameters &Config &Konfiguracja Font Size Rozmiar czcionki Quality Jakość High Wysoka Middle Średnia Low Niska Ultra low Bardzo niska Filter Filtr Save Mode (into the song) Tryb zapisywania (w utworze) Save only the used presets Zapisz tylko wykorzystane ustawienia Save the entire set Zapisz cały zestaw Save the configuration Zapisz konfigurację Configuration File Plik konfiguracji Save... Zapisz... Load... Ładuj... Save as default Zapisz jako domyślne Colors Kolory Text Tekst Background Tło Edit Text Element edytowany Edit Background Tło elementu edytowanego Red Czerwony Blue Niebieski Green Zielony Set Path Ustaw ścieżkę Image in the background : Obraz w tle: Browse... Przeglądaj... Load the set at the initialization : Ładuj zestaw podczas inicjalizacji: Set Brightness, Detune, Attack and Release of the current channel to default Ustaw domyślne wartości dla bieżącego kanału Res. Ctrl Res. kontr. Cut all notes off Wyłącz wszystkie dźwięki Panic! Panika! Number of Voices Liczba głosów Number of voices Liczba głosów Enable Włącz Channel Kanał Vol Gł. Channel Ctrl Kontr. kan. Release Attack Brightness Modulation Pan Panorama DelOverlapsBase MusE: Delete Overlaps MusE: kasowanie elementów nakładających się Range Zakres All Events Wszystkie elementy Selected Events Zaznaczone elementy Looped Events Pomiędzy lokatorami Selected Looped Zaznaczone pomiędzy lokatorami Parts Klocki Selected parts or c&urrent part All parts &in editor OK Akceptuj Cancel Anuluj DidYouKnow Did you know? Czy wiesz, że...? Don't show on startup Nie pokazuj przy starcie Next tip Następna porada Close Zamknij DuplicateTracksBase Duplicate tracks Kopiuj ślady Number of copies Liczba kopii Copy all routes Kopiuj wszystkie połączenia Default routing Domyślne połączenia No routes Brak połączeń Copy parts Kopiuj klocki Duplicate all parts Make independent copies of all parts Make clones of all parts No parts Copy drumlist Kopiuj listę instrumentów perkusyjnych Copy standard (vol, pan) and synth controllers Kopiuj standardowe kontrolery (gł., pan.) oraz kontrolery instrumentów Copy effects rack plugins Kopiuj wtyczki z racka efektów Copy plugin controllers Kopiuj kontrolery wtyczek Ok Akceptuj Cancel Anuluj EditCtrlBase MusE: Edit Controller Event MusE: edytuj zdarzenia kontrolera Time Position Pozycja Available Controller: Dostępne kontrolery: Create New Controller Ustaw nowy kontroler textLabel3 Etykieta3 Value Wartość Controller Kontroler Note Dźwięk H-Bank H-Bank L-Bank L-Bank Program Program off wył. pushButton4 naciśnij Przycisk 4 &OK &Akceptuj &Cancel &Anuluj EditGainBase MusE: Modify gain MusE: modyfikuj wzmocnienie Gain Wzmocnienie 200% 200% 100% 100% 0% 0% &Reset Alt+R &Apply &Zastosuj Alt+A Alt+Z &Cancel A&nuluj Alt+C Alt+N EditInstrumentBase MusE: Instrument Editor MusE: Edytor Instrumentów High Bank: Górny Bank: Low Bank: Dolny Bank: Program: Program: &Delete &Kasuj Alt+D Alt+K Drum Perkusja GM GM GS GS XG XG This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. To jest lista najczęściej używanych kontrolerów MIDI. Zauważ, że "pitch" i "zmiana programu (program change)" są traktowane w MuzA jako zwykłe kontrolery. Properties Właściwości Control7 Kontroler7 Control14 Kontroler14 RPN RPN NRPN NRPN Pitch Transpozycja Program Program H-Ctrl H-Ctrl L-Ctrl L-Ctrl Min Min. Max Maks. Name Nazwa Drum&maps &Zestaw perkusyjny Type Typ Hex Entry: Wartość Heks. &File &Plik Tools Narzędzia New Nowy &New &Nowy Ctrl+N Ctrl+N Open Otwórz &Open... &Otwórz... Ctrl+O Ctrl+O Save Zapisz &Save &Zapisz Ctrl+S Ctrl+S Save As Zapisz jako Save &As... Zapisz &Jako... Exit Zakończ E&xit &Zakończ Instrument Name: Nazwa instrumentu: Selected instrument name. Nazwa wybranego instrumentu. List of defined instruments. Lista zdefiniowanych instrumentów. Pa&tches &Brzmienia List of groups and patches. Lista brzmień w grupach. Group/Patch Grupa/Brzmienie Name: Nazwa: Group or patch name Nazwa grupy bądź brzmienia. Patch high bank number Numer górnego banku dla danej barwy Patch high bank number. --- means don't care. Numer górnego banku dla danej barwy, --- – bez znaczenia. --- dont care --- Patch low bank number Numer dolnego banku dla danej barwy Patch low bank number. --- means don't care. Numer dolnego banku dla danej barwy, --- – bez znaczenia. --- --- Patch program number Nr programu danego brzmienia Patch program number. --- means don't care. Show in tracks: Pokaż na ścieżkach: Midi MIDI &Drum Delete group or patch Kasuj grupę lub brzmienie New patch Nowe brzmienie New &Patch Nowe b&rzmienie Alt+P Alt+R New group Nowa grupa New &Group Nowa &grupa Alt+G High Bank Low Bank Patch program number. --- means don't care. Patch Name: List of defined controllers Lista zdefiniowanych kontrolerów List of defined controllers. Lista zdefiniowanych kontrolerów. Min Min. Max Maks. Def Domyślnie Dr def Midi controller name Nazwa kontrolera MIDI Type: Typ: Midi controller type Typ kontrolera MIDI Midi controller number high byte Numer kontrolera MIDI: starszy bajt Midi controller number low byte (* means drum controller) Numer kontrolera MIDI: młodszy bajt (* oznacza kontroler perkusyjny) Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and GS/XG type songs and instruments. Allows controllers for each instrument in Muse's drum map. The low byte will be replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. Młodszy bajt numeru kontrolera MIDI. Wartość * oznacza kontroler perkusyjny dla śladów perkusyjnych i instrumentów w standardzie GS/XG. Umożliwia przyporządkowanie kontrolerów dla każdego instrumentu w zestawie perkusyjnym MusE. Młodszy bajt zostanie zastąpiony przez „ANote” w przypadku zestawu perkusyjnego. Przykłady: instrumenty GS/XG, kontrolery perkusyjne. * wild card * Range: Zakres: Minimum value. If negative, auto-translate. Wartość minimalna. Jeśli ujemna, użyta zostanie konwersja automatyczna. Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. Useful for controllers which should be displayed with zero bias. For example, 'Pan': Minimum: -64 Maximum: 63 True range: Min: 0 Max: 127 (bias = 64) 'CoarseTuning': Min: -24 Max: 23 True range: Min: 40 Max: 87 (bias = 64) Bias is determined from controller type: 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 Type 'Pitch' is the exception. It is biased at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) Wartość minimalna. Jeżeli jest mniejsza od zera, zakres zostanie automatycznie przeliczony do wartości dodatnich. Jest to rozwiązanie przydatne dla kontrolerów, które prezentowane są z przesunięciem względem zera (zero bias). Dla przykładu: „Pan”: min.: -64, maks.: 63. Rzeczywisty zakres: min. 0, maks: 127 (bias = 64). „CoarseTuning”: min.: -24, maks.: 23. Rzeczywisty zakres: min.:40, maks.: 87 (bias = 64). Wartość „bias” określana jest na podstawie typu kontrolera: 7-bitowy Controller7/RPN: bias = 64, 14-bitowy Controller14/RPN14: bias = 8192. Typ „Pitch” (odchylenie stroju) jest wyjątkiem. Dla niego wartość przesunięcia wynosi 0, mimo iż zawiera wartości ujemne. „Pitch”: min.: -8192, maks.: 8191. Rzeczywisty zakres: min.: -8192, maks.: 8191 (bias 0). Maximum value Wartość maksymalna Default: Domyślnie: Drum default: L-Bank L-Bank Info Informacja Additional information off Progr. Nr programu ??? ??? H-Bank H-Bank Default value. Off: No default. Domyślna wartość. Wył.: brak wartości domyślnej. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). Domyślna (początkowa) wartość. Wył. oznacza brak wartości domyślnej. Jeżeli podano wartość domyślną, zostanie ona wysłana do danego kontrolera, w chwili jego dodania (w edytorze Pianoroll bądź w Edytorze Listy). Wartość zostanie wysłana ponownie po ponownym wczytaniu utworu. W każdym innym przypadku, kontroler pozostaje ustawiony zgodnie ze swoją ostatnią wartością. Kontrolery dodawane są także do utworu w momencie wystąpienia związanego z kontrolerem zdarzenia MIDI. Ważne! Należy zwrócić szczególną uwagę na domyślne wartości takich kontrolerów jak „Sustain”, czy „ResetAllControllers”. Najprawdopodobniej najlepszym rozwiązaniem jest ustawienie ich wartości domyślnych na „wył.” (w edytorze Pianoroll bądź Edytorze Listy). off dont care wył. Add common controller Dodaj typowy kontroler &Add &Dodaj Alt+A Alt+D Delete controller Kasuj kontroler Create a new controller Utwórz nowy kontroler New &Controller Nowy k&ontroler Alt+C Alt+O Null parameter number High byte Starszy bajt numeru pustego (null) kontrolera dodatkowego If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events from corrupting the RPN/NRPN controller. Typically, set to 127/127, or an unused RPN/NRPN controller number. Jeśli ustawiono, „puste” (null) numery kontrolerów dodatkowych zastaną wysłane po każdym zdarzeniu RPN/NRPN. Zapobiega to psuciu ustawionych wartości RPN/NRPN przez występujące w międzyczasie zdarzenia typu „data”. Zazwyczaj ustawiane na wartość 127/127 bądź numer nieużywanego kontrolera dodatkowego (RPN/NRPN). Lo: Mł.: Null parameter number Low byte Młodszy bajt numeru pustego (null) kontrolera dodatkowego S&ysEx SysEx List: Lista SysEx: New SysE&x Nowy SysE&x Alt+X Patch Collections: Zbiór brzmień: Note off mode: Tryb Note off: Note off mode Tryb Note off Selects how to handle note off events. The instrument can use note off events, or not at all, or convert them to zero-velocity note on events which helps save midi interface bandwidth. Refer to the instrument manufacturer's midi implementation chart for details. Pozwala na wybranie sposobu obsługi zdarzeń „note off”. Dany instrument może, lecz nie musi korzystać ze zdarzeń „note off”. Może też przedstawiać je, jako nuty o zerowej wartości parametru velocity (co pozwala na oszczędzanie przepustowości interfejsu MIDI). Szczegółowe informacje odnośnie konkretnego instrumentu, powinny być zawarte w tabeli implementacji MIDI dostarczanej przez producenta. &Copy &Kopiuj &Remove &Usuń &Up &Góra &Down D&ół Patch: Brzmienie: from od to do Bank Hi: Górny bank: Bank Lo: Dolny bank: Contro&llers Kontro&lery Null Parameters: Hi: Bajt kontrolera pustego (null): St.: &Add Common... &Dodaj typowy... W Comment: Opis: Note: The enclosing F0 ... F7 are not required &Initialization &Inicjalizacja Instrument initialization sequence: Sekwencja inicjalizacji instrumentu: &Add... &Dodaj... &Change... Z&mień... &Help P&omoc &Close Z&amknij Close Zamknij new item nowy What's this? Co to jest? EditNoteDialogBase MusE: Enter Note MusE: wstaw nutę OK Akceptuj Cancel Anuluj Length: Długość: Time Position: Pozycja: Pitch: Wysokość dźwięku: Velocity On: Prędkość uderzenia (velocity) wł.: Velocity Off: Prędkość uderzenia (velocity) wył.: EditSysexDialogBase MusE: Enter SysEx MusE: wstaw komunikat MIDI SysEx TimePosition: Pozycja: Hex: Note: The enclosing F0 ... F7 are not required Name: Nazwa: Comment: Opis: &Select... &Wybierz... &OK &Akceptuj &Cancel A&nuluj OK Akceptuj Cancel Anuluj FLUIDGuiBase Form1 Form1 FLUIDSynthGuiBase FLUID Synth Load Ładuj Delete Kasuj Dump Info Informacja ID Fontname Nazwa Chnl Kan. Soundfont Drum Chnl Kan. perk. Level Width Damping Room Size Rozmiar pomieszczenia Reverb CHANNEL SETUP KONFIGURACJA KANAŁU Sine Triangle Type Typ Number Numer Speed Depth Chorus Gain Wzmocnienie LOADED SOUNDFONTS ZAŁADOWANE SOUNDFONTY FileDialogButtons Home Katalog domowy Global Globalnie User Użytkownik Project Katalog projektu read Midi Port Configuration Ładuj konfigurację portów MIDI write window states Zapisuj pozycje okien fdialogbuttons FluidSynthGui Choose soundfont Wybierz soundfont GateTimeBase Range Zakres All Events Wszystkie elementy Selected Events Zaznaczone elementy Looped Events Pomiędzy lokatorami MusE: Modify Note Length MusE: modyfikuj długość nut Selected Looped Zaznaczone pomiędzy lokatorami Parts Klocki Selected parts or c&urrent part All parts &in editor Values Parametry Rate: Częstotliwość: Offset: Offset: % % lenNew = (lenOld * rate) + offset dłNowa = (dłStara * częst.) + offset OK Akceptuj Cancel Anuluj GlobalSettingsDialogBase MusE: Global Settings MusE: ustawienia globalne Audio Audio Mixer Mikser dB dB min. Meter Value Min. wartość miernika sygnału Midi MIDI Ticks Ticks 1024 1024 2048 2048 4096 4096 Displayed Resolution (Ticks/Quarternote) Wyświetlana rozdzielczość (Ticks/Ćwiartka) 48 48 96 96 192 192 384 384 768 768 1536 1536 3072 3072 6144 6144 12288 12288 RTC Resolution (Ticks/Sec) RTC (Rzeczywista Rozdzielczość Zegara) (Ticks/Sekunda) /sec /sek. GUI Refresh Rate Częstotliwość odświeżania interfejsu użytkownika Warn if timer frequency is inadequate Ostrzegaj, jeśli częstotliwość zegara jest nieodpowiednia Track height Wysokość śladu Use project save dialog Używaj okna dialogowego „Zapisz projekt” Some popup menus stay open (else hold Ctrl) Niektóre wyskakujące menu pozostają otwarte (jeśli nie, użyj Ctrl) Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. Pozwala, by niektóre wyskakujące menu pozostały otwarte. Jeżeli opcja jest nieaktywna, należy przytrzymać Ctrl, by menu nie było zamykane. In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. Przy niektórych elementach, środkowy klawisz myszy zwiększa wartości, a prawy je zmniejsza. Użytkownicy myszki bez środkowego przycisku mogą wybrać tę opcję, aby lewy klawisz zachowywał się jak klawisz środkowy. Use left mouse button for decreasing values Używaj lewego przycisku myszy do zmniejszania wartości Shift + Right click sets left range marker Shift + prawy klawisz ustawia lewy znacznik zakresu Allow adding hidden tracks in track list menu Pozwól na dodawanie ukrytych śladów w menu „lista śladów” Unhide tracks when adding hidden tracks Podczas dodawania ukrytych śladów, spraw by były widoczne Smart focus After editing, controls will return focus to their respective canvas Probably should be more informative Po edycji wartości, zostanie aktywowany poprzednio wybrany element Show newly created midi velocity graphs per-note Domyślnie pokazuj wykres velocity tylko dla nut o tej samej wysokości px Keep transport window on top (requires restart) Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. Włącz mysz nieograniczoną ramami ekranu. Działa dla takich funkcji, jak przesuwanie/powiększanie. Wyłączenie tej opcji implikuje użycie metody alternatywnej. Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. Włącz mysz nieograniczoną ramami ekranu. Działa dla takich funkcji, jak przesuwanie/powiększanie. Wyłączenie tej opcji implikuje użycie metody alternatywnej. Borderless zoom/pan mouse (else use alternate method) Mysz pozbawiona ograniczeń przy powiększaniu/przesuwaniu (w przeciwnym razie użyj metody alternatywnej) Scrollable submenus Przewijane podmenu Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. This hack forces a frame to be drawn, but may interfere with other styles. Use old-style stop shortcut Monitor on record-arm automatically Show note names on notes in pianoroll Whether record-arming a track automatically activates monitoring. Drum tracks Ślady perkusyjne GUI Style Styl GUI MDI-subwindowness and sharing menus Podokna MDI i współdzielone menu Presets: Ustawienia: traditional MusE SDI Tradycyjne MDI MusE Cakewalk-like MDI MDI w stylu Cakewalk Borland-/Mac-like MDI MDI w stylu Borland/Mac &Apply &Zastosuj &Ok &Akceptuj &Cancel A&nuluj Application Aplikacja Start Muse Uruchamianie MusE start with last song Otwórz ostatnio otwierany utwór start with song Ładuj przy uruchamianiu utwór Views Widoki y-pos poz.-y show Pokaż x-pos poz.-x height wysokość width szerokość Big Time Duży Zegar Transport Panel transportu Cur Aktualne set current values Ustaw aktualną wartość show splash screen Pokaż okno o programie przy starcie Mixer A Mikser A Mixer B Mikser B show "Did you know?" dialog Pokaż okno „Czy wiesz, że...?” Start song Utwór przy uruchamianiu start &with last song Ła&duj ostatni utwór start with &template Ładuj &szablon sta&rt with song Z&acznij nowy utwór min. Slider Val Min. wartość tłumika sygnału Enable denormal protection Włącz ochronę przed liczbami zdenormalizowanymi Enable output limiter Włącz limiter na wyjściu External Waveditor Zewnętrzny edytor audio External Waveditor command Wywołanie zewnętrznego edytora Note: External editor opened from the internal editor. Uwaga: zewnętrzny edytor otwiera się z poziomu edytora wewnętrznego. Dummy Audio Driver (settings require restart) Atrapa sterownika audio (ustawienia wymagają restartu) Period size (Frames per period): Not sure if it is a correct translation Rozmiar okresu (ramki na sekundę) 16 16 Project directory Katalog projektu Projects: Projekty: ... ... Main Window Główne okno Choose start song or template Wybierz utwór uruchamiany przy starcie bądź szablon Start MusE Reset to default Wróć do ustawień domyślnych Start with &last song Start with &template Sta&rt with song On launch Show splash screen Show "Did you know?" dialog Start template or song: Szablon bądź utwór uruchamiany przy starcie: Read MIDI Ports configuration from file, or else automatically configure Ładuj plik z konfiguracją portów MIDI; w przeciwnym razie konfiguruj automatycznie Read MIDI Ports configuration Ładuj konfigurację portów MIDI Warn if opening file versions different than current version Ostrzegaj, jeżeli otwierany jest plik zapisany w innej wersji programu Auto save (every 5 minutes if not playing/recording) Automatyczne zapisywanie (co 5 minut, jeśli nie jest uruchomione odtwarzanie/nagrywanie) Plugin Paths Ordered by priority. Changes take effect on restart. The plugin cache is used for safety and speed. It automatically rescans when any of the given directories change, for example by installing a new plugin. Click this button to force the cache to recreate upon next restart. &Force plugin rescan and cache recreation on next restart LADSPA DSSI Linux VST Directories containing Linux vst plugins (*.so files) LV2 PV2 NOTE: Ensure the directory containing core LV2 bundles is included (typically /usr/lib/lv2). Otherwise all plugins may not load properly. Move up Move down Add... Edit... Remove Usuń Audio driver settings (require restart) Sample rate: Audio backend: Shorter periods give better midi playback resolution. Note that this cannot be set with Jack devices! Period size (frames per period): Note that samplerate cannot be set with Jack devices! Minimum meter value Minimum slider value Try to use Jack Freewheel Staraj się używać Jack Freewheel Speeds bounce operations Przyśpiesza operacje zgrywania (bounce) Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Jeżli to możliwe używaj trybu Jack Freewheel, który znacząco przyśpiesza operacje zgrywania (bounce). VST in-place Enable VST in-place processing (restart required) Włącz tryb przetwarzania „in-place” dla VST (wymagany restart) Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. Włącz tryb przetwarzania „in-place” dla VST. Należy je wyłączyć, jeśli wtyczki VST Ladspa nie działają poprawnie bądź słyszalne jest głośne sprzężenie (nawet w przypadku, gdy wtyczki mają możliwość pracy w tym trybie). Zmiana tego ustawienia wymaga restartu. Minimum control period Not sure. Minimalny okres sterowania Minimum audio controller process period (samples). Not sure. Minimalny okres dla procesu sterowania audio (próbki). Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. Minimalny okres dla procesu sterowania audio (próbki). Wpływa na czułość nastaw audio i szczegółowość wykresów kontrolerów. Niska wartość zapewnia szybkie i płynne sterowanie. W przypadku problemów z wydajnością, należy zwiększyć tą wartość. 1 1 2 2 4 4 8 8 Sample rate conversion, time stretching and pitch shifting Audio converter settings... Record drum tracks GUI GUI LV2 UI open behavior Set range markers (left / right) Middle mouse button / Right mouse button Ctrl + Right mouse button / Right mouse button Ctrl + Left mouse button / Ctrl + Right mouse button Options Show status bar Show note tooltips in midi editors GUI Tweaks Tweaks 32 32 64 64 128 128 256 256 512 512 Sample rate Częstotliwość próbkowania Shorter periods give better midi playback resolution. Krótszy okres pozwala na wyższą rozdzielczość podczas odtwarzania. &Record all instruments Nag&rywaj wszystkie instrumenty &Don't record hidden instruments &Nie nagrywaj ukrytych instrumentów Don'&t record muted instruments Nie nagrywaj &wyciszonych instrumentów Don't record &hidden or muted instruments Nie nagrywaj &ukrytych oraz wyciszonych instrumentów Instrument initialization Inicjalizacja instrumentów Send instrument initialization sequences Wysyłaj sekwencje inicjalizacji instrumentów Warn if instrument initialization sequences pending Ostrzegaj, jeżeli inicjalizacja instrumentu nie została wykonana Send instrument controller default values if none in song, at init or rewind Podczas startu lub przewijania wysyłaj domyślne wartości kontrolerów, jeżeli nie występują w utworze 8192 8192 16384 16384 32768 32768 Midi Resolution (Ticks/Quarternote) Rozdzielczość MIDI (Ticks/Ćwiartka) Editors Open editors in tabs (else in separate windows) Piano roll Pianoroll Drum editor Edytor Perkusji Wave editor Edytor Audio Score editor Edytor Zapisu Nutowego Master track editor Edytor graficzny Tempo/Metrum DSSI VST Directories where dssi-vst (if installed) looks for Wine vst plugins (*.dll files) Midi controller behaviour Send Null parameters after an (N)RPN value If set, 'null' parameter numbers will be sent after each RPN/NRPN event. (A 'null' parameter number is 127.) This prevents subsequent rogue or accidental 'data' events (without parameters) from corrupting the current RPN/NRPN controller. Normally it is OK to leave this unset, but use it if there is a possibility of such 'data' events. If this is set, the 'Optimize controllers' setting does not affect these controllers' parameters, only the H/L values. Send Null parameters Don't send redundant parameters or values If set, redundant H/L parameters or H/L values are not sent. They are sent only if they changed. This can save midi interface bandwidth. But it is possible the receiving device might miss the previous changes (for example if the device is turned on after they were sent). The 'Panic' button will force them to be resent. Certain operations will also force them to be resent, such as clicking a GUI midi control (but not while adjusting it). Optimize controllers Record a&ll instruments Latency Whether to enable latency correction/compensation: Enable latency correction/compensation Project latency Whether completely independent branches share a common project-wide latency: Project-wide latency Unterminated branches Whether unterminated branches participate in latency correction/compensation: Unterminated input branches Unterminated output branches Track monitoring Whether track monitor buttons affect latency correction/compensation: Monitoring affects latency Revert native GUI scaling for plugins in HiDPI Live update wave parts while recording Odświeżanie klocków audio podczas nagrywania Whether to show knob and slider values in certain places, for example mixer strips. Turn off to reduce clutter. Revert native UI window HiDPI scaling (some plugins ignore this setting) Show knob and slider values Whether to show knobs or sliders in certain places, for example mixer strips Prefer midi volume as decibels instead of 0-127 Style hack: Force line edit widgets to draw a frame Prefer knobs instead of sliders GUI refresh rate LV2 UI Open behavior Sposób otwierania UI LV2 Use first available Wybierz pierwsze dostępne Ask once Zapytaj raz Ask always Pytaj zawsze &Only offer old-style drumtracks &Używaj wyłącznie śladów perkusyjnych starego typu Only offer new-style &drumtracks Używaj wyłącznie śla&dów perkusyjnych nowego typu &Prefer old-style drumtracks &Preferuj ślady perkusyjne starego typu Prefer &new-style drumtracks Preferuj ślady perkusyjne &nowego typu Record new style drum tracks Nagrywaj ślady perkusyjne nowego typu GUI Behaviour Zachowanie GUI Use old-style stop shortcut: Używaj starego skrótu dla funkcji stop Move single armed track with selection Przesuń status nagrywania na śladzie wraz z zaznaczeniem On Launch Przy starcie Behavior Zachowanie LegatoBase MusE: Legato Range Zakres All Events Wszystkie elementy Selected Events Zaznaczone elementy Looped Events Pomiędzy lokatorami Selected Looped Zaznaczone pomiędzy lokatorami Parts Klocki Selected parts or c&urrent part All parts &in editor Settings Ustawienia ticks ticks Minimum Length Długość minimalna Allow shortening notes Pozwalaj na skracanie nut OK Akceptuj Cancel Anuluj MITTransposeBase MusE: Midi Input Plugin: Transpose MusE: wtyczka wejścia MIDI: transponuj On Włącz. TriggerKey Klawisz uruchamiający Transpose: Transponuj: +0 MRConfigBase MusE: Midi Input Plugin: Remote Control MusE: wtyczka wejścia MIDI: zdalne sterowanie Activate Aktywuj On Wł. Actions Funkcje Stop Stop Record Nagrywanie Goto Left Mark Idź do lewego lokatora Play Odtwarzanie Insert rest (step rec) Wstaw pauzę (nagr. krok.) MdiSettingsBase MDI subwin MDI podokno Shares menu when subwin Współdzieli menu w podoknie Shares menu when free Współdzieli menu jeśli wolne MetronomeConfigBase MusE: Metronome Config MusE: konfiguracja metronomu Metronome Metronom Audio Beep Audio Beep Two samples (old samples) Dwie próbki (stare sample) Four samples (new samples, with accents) Cztery próbki (nowe sample z akcentami) Volume Głośność Audio master Meas Takt Beat Rytm Accent1 Akcent1 Accent2 Akcent2 Sample Sampel Preferences Apply the settings globally or to the current song: Use global metronome Use song-specific metronome Enable accents for the audio and midi metronomes: &Two samples (old samples) Four samples (&new samples, with accents) Audio Audio MIDI MIDI Click MIDI Klik Midi Channel Kanał MIDI Accent 2 Velocity Accent 1 Velocity Accent 1 Note Measure Note Dźwięk 1 uderzenie w takcie Measure Velocity Głośność 1 uderzenie w takcie Beat Velocity Głośność pozostałych uderzeń metronomu Beat Note Dźwięk pozostałych uderzeń metronomu Midi Port Numer portu MIDI Disabled since jack does not support it Wyłączone, ponieważ nie jest wspierane przez Jack Precount Nabicie tempa enable Włącz Bars Takty From Mastertrack Ze śladu tempo/metrum (Mastertrack) / / Signature Metrum Prerecord Prerecord Preroll Preroll &Apply &Zastosuj &OK &Akceptuj &Cancel A&nuluj Choose outputs... Wybierz wyjścia... 50 50 Hint: Enable metronome in Transportpanel Podpowiedź: włącz metronom na Panelu Transportu Accent 2 Note Off Wył. Accents Accent sequence beats Current accent 1 sequence: (Click to change) Current accent 2 sequence: Factory User Użytkownik Preset sequences Add current accents to presets Make a new preset from the current accents. + Delete preset Kasuj ustawienie Delete the selected preset. Only user presets can be deleted. - Use preset Copy the preset to the current accents. -> Reset to Factory or User defaults Resets all current accent sequences to default Factory or User presets. ! Enable the precount Enable Włącz Number of bars Use Mastertrack time signature Beats per bar Beat note type Enable precount when playing Precount on play Mute the metronome after precount Mute metronome Not supported yet Alt+A Alt+Z Alt+O Alt+A Alt+C Alt+N MidiAudioControlBase Midi control Sterowanie MIDI Port: Channel: Kanał: Control type: Typ kontrolera: &Learn &Ucz się Control7 Kontroler7 Control14 Kontroler14 RPN RPN NRPN NRPN Pitch Transpozycja Program Program Hi: St.: Lo: Mł.: MidiFilterConfigBase MusE: Midi Input Filter MusE: filtr wejścia MIDI Record Filter Filtr Komunikatów Przych. (MIDI In) Note On Dźwięki (Note On) Poly Pressure Polifoniczny nacisk po uderzeniu (Poly Pressure) Controller Kontrolery Program Change Zmiana programu (Program Change) After Touch Docisk (Aftertouch) Pitch Bend Odchylenie stroju (Pitch Bend) Sysex SysEx Thru Filter Filtr Komunikatów Wych. (Midi Thru) Controller Filter Filtruj Kontroler Channel Filter Filtruj Kanał 14 14 10 10 6 6 12 12 4 4 2 2 9 9 8 8 3 3 13 13 15 15 16 16 7 7 11 11 5 5 1 1 MidiInputTransformDialogBase MusE: Midi Input Transformator MusE: przekształcanie wejścia MIDI Filter Filtr All Wszystko Equal Równe Unequal Nierówne Note Dźwięk Poly Pressure Polifoniczny nacisk po uderzeniu (Poly Pressure) Control Change Control Change Aftertouch Docisk (Aftertouch) Pitch Bend Odchylenie stroju (Pitch Bend) NRPN NRPN RPN RPN Program Program Value 2 Wartość 2 Value 1 Wartość 1 Event Type Rodzaje komunikatów MIDI Ignore Ignoruj Higher Powyżej Lower Poniżej Inside Pomiędzy Outside Poza Channel Kanał Port Port Processing Przetwarzanie Keep Pozostaw Fix Ustal Plus Plus Minus Minus Multiply Przemnóż Divide Podziel Invert Odwróć ScaleMap Zakres skali Flip Odwróć Dyn Dyn Random Losowo Toggle Przełącz Modules Moduły 1 1 2 2 3 3 4 4 enable modul 1 aktywuj moduł 1 enable modul 2 aktywuj moduł 2 enable modul 3 aktywuj moduł 3 enable modul 4 aktywuj moduł 4 Preset Ustawienie Name: Nazwa: Comment: Opis: Function Funkcja &New &Nowy create new preset Twórz nowe ustawienie &Delete &Kasuj delete preset Kasuj ustawienie Enable module 1 Enable module 2 Enable module 3 Enable module 4 &Dismiss &Wyrzuć PresetList Lista ustawień MidiSyncConfigBase MusE: Midi Sync MusE: synchronizacja MIDI Apply Zastosuj Ok Akceptuj Cancel Anuluj hour godzina h godz. minute minuta m min. second sekunda s sek. frame ramka f r. subframe podramka This button and other transport related buttons are found on the Transport Panel When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). W trybie slave tempo jest kontrolowane zewnętrznie. MusE może się zsynchronizować do zegara MIDI lub do ćwiartki ramki synchronizacji MTC. Obowiązują aktywne wejścia z listy (RMC, RMMC, RMTC). 24 24 25 25 30D 30D 30N 30N MTC MTC Type: Typ: Offset: Offset: Sync receiving and sending Wysyłanie i odbiór komunikatów synchronizacji Send and receive Jack transport Wysyłaj i odbieraj transport Jack Send and receive Jack transport information, including stop, start and position. Wysyłaj i odbieraj transport Jack, włączając w to pozycje start i stop. Use Jack transport Używaj transportu Jack Make MusE the Jack transport Timebase Master Ustaw MusE jako Timebase Master dla transportu Jack Make MusE the Jack transport Timebase Master. Allows Jack to show time as MusE Bars, Beats, and Ticks. MusE will try to become master, but other Jack clients can also take over later. You can always click here again for Master. Ustaw MusE jako Timebase Master dla transportu Jack. Umożliwia to serwerowi Jack pokazywanie czasu w formacie MusE (takty, uderzenia i ticks). MusE spróbuje działać w trybie Master, jednak pozostali klienci Jack w każdej chwili mogą przejąć jego rolę. W takiej sytuacji, kliknięcie w tym miejscu pozwala na ponowne przejęcie roli Master. Jack transport Timebase Master Timebase Master dla transportu Jack Control MusE timing by external midi clock or MTC sync Kontroluj czas MusE zewnętrznym zegarem MIDI bądź za pomocą synchronizacji MTC When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). W trybie slave tempo jest kontrolowane zewnętrznie. MusE może się zsynchronizować do zegara MIDI lub do ćwiartki ramki synchronizacji MTC. Obowiązują aktywne wejścia z listy (RMC, RMMC, RMTC). W trybie slave tempo Slave to external sync Synchronizacja zewnętrzna (tryb slave) Averaging applied to recorded external tempo changes. Użyj uśredniania podczas nagrywania zmian tempa z zewnątrz. External midi clock can be very jittery. Tempo is derived from it and recorded. It is usually desirable to average it and limit the number of recorded changes. Tiny: 2 section 4/4 = 8 stages. 1/8T note averaging, may produce jitter. Small: 3 section 12/8/4 = 24 stages. 1/4 note averaging, may still produce jitter. Medium: 3 section 28/12/8 = 48 stages. 1/2 note averaging. Less jitter. Large: 4 section 48/48/48/48 = 192 stages. Use this if the song has only one tempo. Very low quantization values can be used. Large pre-detect: 4 section 8/48/48/48 = 152 stages + first stage large step pre-detector. Use this if you expect sudden large tempo steps. None: Use only if high accuracy is needed for audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. Zewnętrzny zegar MIDI może być bardzo niestabilny. Na jego podstawie wyznaczane i nagrywane jest tempo utworu. Wskazane jest więc jego uśrednianie i tym samym ograniczenie liczby zapisywanych zmian tempa. Najmniejsze: 2 sekcje 4/4 = 8 etapów. Uśrednianie do nuty 1/8T, może powodować niestabilność. Małe: 3 sekcje 12/8/4 = 24 etapy. Uśrednianie do nuty 1/4, nadal może powodować niestabilność. Średnie: 3 sekcje 28/12/8 = 48 etapów. Uśrednianie do nuty 1/2. Mniejsza niestabilność. Duże: 4 sekcje 48/48/48/48 = 192 etapy. Rekomendowane, gdy utwór ma tylko jedno tempo. Mogą być użyte bardzo niskie wartości kwantyzacji. Duże pre-detect: 4 sekcje 8/48/48/48 = 152 etapy + „large step pre-detector” w pierwszym etapie. Rekomendowane, jeżeli istnieje możliwość wystąpienia nagłych i znaczących zmian tempa. Brak: używane jedynie w przypadku konieczności synchronizacji odtwarzania audio. Uwaga: może zapisywać tysiące zmian tempa na minute. Może znacząco spowolnić MusE, rozmiar utworu może być bardzo duży. Tempo record averaging Uśrednianie tempa podczas nagrywania bpm Tempo record quantization Kwantyzacja tempa podczas nagrywania Send start to first clock delay Opóźnienie pomiędzy start, a pierwszym komunikatem zegara (sync delay) Allows 'slow sync' devices time to synchronize to MusE. This value is the delay from sending start to sending the first clock. Pozwala „powolnym” urządzeniom na zsynchronizowanie się do MusE. Wartość ta stanowi opóźnienie między wysłaniem start, a pierwszym komunikatem zegara (sync delay). ms Send sync delay Opóźnienie przy wysyłaniu synchronizacji Note: Sync delay and MTC sync currently not fully implemented Uwaga: opóźnienie synchronizacji i synchronizacja MTC nie są w tej chwili całkowicie zaimplementowane MidiTrackInfoBase MusE: TrackInfo Info o śladzie output channel kanał wyjściowy % % output port port wyjściowy off wył. Transp. Transpozycja Channel Info Info o kanale Rec: Nagr.: Change note length in percent of actual length Zmienia długość nut w stosunku procentowym do obecnej długości Offset playback of notes before or after actual note Opóźnienie/wyprzedzenie przy odtwarzaniu zapisanych na ścieżce dźwięków Transpose notes up or down Transpozycja nut (w górę bądź w dół) Bank Select MSB. Ctrl-double-click on/off. Wybór banku MSB (Bank Select MSB). Ctrl + podwójny klik włącza/wyłącza. Bank Select LSB. Ctrl-double-click on/off. Wybór banku LSB (Bank Select LSB). Ctrl + podwójny klik włącza/wyłącza. <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style=" font-style:italic;">Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> <html><head/><body><p>Dodaj lub odejmij prędkość uderzenia (velocity) do/od nut zapisanych na śladzie.</p><p><span style=" font-style:italic;">Zakres velocity nut to 0-127, może się więc <br />zdarzyć, że nie będzie możliwe <br />przyporządkowanie w całości tak obliczonej <br />wartości (note velocity + velocity), gdyż <br />znajdzie się ona poza dopuszczalnym <br />zakresem.</span></p></body></html> Compress the notes velocity range, in percent of actual velocity Kompresuj zakres prędkości uderzenia (Note Velocity), o procent w stosunku do wartości obecnej Program. Ctrl-double-click on/off. Program. Ctrl + podwójny klik włącza/wyłącza. Prog Program Volume. Ctrl-double-click on/off. Głośność. Ctrl + podwójny klik włącza/wyłącza. Vol Głośn. (vol.) Change stereo position. Ctrl-double-click on/off. Zmień pozycję w panoramie stereo. Ctrl + podwójny klik włącza/wyłącza. Pan Panorama Select instrument Wybierz instrument Delay Opóźnienie H-Bank H-Bank Compr. Kompr. L-Bank L-Bank Velocity Prędk. uderz. (vel.) Length Długość all midi events are sent to this output channel Wszystkie zdarzenia MIDI wysyłane są do tego kanału Out ch Kanał wyj. input routing Połączenia wejściowe output routing Połączenia wyjściowe input detect Zdarzenie wejściowe Input detect indicator. Detects all note on-off, controller, aftertouch, program change, and pitchbend (but not sysex or realtime) events on the selected channels, on the selected midi ports. Kontrolka zdarzeń wejściowych. Sygnalizuje następujące zdarzenia (na wybranych kanałach i wybranych portach MIDI): wszystkie zdarzenia note on-off, zdarzenia kontrolerów, aftertouch, program change, pitch bend. Nie sygnalizuje komunikatów SysEx i zdarzeń typu realtime. Midi thru MIDI thru Pass input events through ('thru') to output. Przekazuje zdarzenia wejściowe do wyjścia (through - „thru”). Select instrument patch Wybierz brzmienie (patch) instrumentu <unknown> <nieznany> Add all settings to song Dodaj do utworu wszystkie poniższe ustawienia All Wszystko Add bank + prog settings to song Dodaj do utworu ustawienia banków i programów Add vol setting to song Dodaj do utworu ustawienia głośności (volume) Add pan setting to song Dodaj do utworu ustawienia panoramy (pan) MidiTransformDialogBase MusE: Midi Transformator MusE: przekształcanie komunikatów MIDI &New &Nowy &Delete &Kasuj &Apply &Zastosuj &OK &Akceptuj &Cancel &Anuluj PresetList Lista Ustawień Processing Przetwarzanie Event Type Rodzaje komunikatów MIDI Keep Pozostaw Fix Ustal Note Dźwięki Poly Pressure Polifoniczny nacisk po uderzeniu Control Change Control Change Aftertouch Docisk (Aftertouch) Pitch Bend Odchylenie stroju (Pitch Bend) NRPN NRPN RPN RPN Plus Plus Minus Minus Multiply Przemnóż Divide Podziel Value 2 Wartość 2 Invert Odwróć ScaleMap Zakres skali Flip Odwróć Dyn Dyn. Random Losowo Value 1 Wartość 1 Length Długość: Position Pozycja Randomize Filter Filtruj All Wszystko Equal Równe Unequal Nierówne Ignore Ignoruj Higher Powyżej Lower Poniżej Inside Pomiędzy Outside Poza Bar Range Zakres Taktów Preset Ustawienie Name: Nazwa: Comment: Opis: Range Zakres process all events przetwórz wszystkie elementy selected tracks Zaznaczone ślady inside loop Pomiędzy lokatorami. Function Opcje Select Wybierz Quantize Kwantyzuj Delete Kasuj Transform przekształć Insert Wstaw Copy Kopiuj Extract Wydobądź Quantize Value Wartość kwantyzacji MixdownFileDialogBase MusE: Set Mixdown Wavefile MusE: ustaw parametry zgrania miksu do pliku audio &OK &Akceptuj &Cancel &Anuluj File Path Ścieżka do pliku Channel Ilość Kanałów Stereo Stereo Mono Mono 5.1 5.1 wav,16 Bit wav, 16 Bitów wav, 24 Bit wav, 24 Bity wav, 32 Bit (float) wav, 32 Bity (float) Format Format MoveBase MusE: Move Notes MusE: przesuń nuty Range Zakres All Events Wszystkie elementy Selected Events Zaznaczone elementy Looped Events Pomiędzy lokatorami Selected Looped Zaznaczone pomiędzy lokatorami Parts Klocki Selected parts or c&urrent part All parts &in editor Value Wartość Move by Przesuń o ticks OK Akceptuj Cancel Anuluj MusECore::AudioPreviewDialog Auto play Odtwarzanie automatycznie Stop Stop Play Odtwarzanie MusECore::RasterizerModel Off Wył. Bar MusECore::Scripts Do you want to process ALL or only selected events? Przetwarzać WSZYSTKIE, czy wybrane elementy? &Selected &Wybrane &All W&szystkie MusE - external script failed MusE - wystąpił błąd w skrypcie zewnętrznym MusE was unable to launch the script, error message: %1 MusE: nie można uruchomić skryptu, informacja o błędzie: %1 Re-read script names from disc MusECore::Song Jack shutdown! Wyłączenie serwera Jack! Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: - a performance issue with your particular setup. - a bug in MusE (or possibly in another connected software). - a random hiccup which might never occur again. - jack was voluntary stopped by you or someone else - jack crashed If there is a persisting problem you are much welcome to discuss it on the MusE mailinglist. (there is information about joining the mailinglist on the MusE homepage which is available through the help menu) To proceed check the status of Jack and try to restart it and then . click on the Restart button. Jack wykrył problem z wydajnością, co spowodowało odłączenie MusE. Mogło tak się zdarzyć z kilku powodów. Np.: - problem z wydajnością systemu, - błąd w MusE (lub innej podłączonej aplikacji), - losowy problem, który w przyszłości może się nie pojawić, - Jack został zatrzymany intencjonalnie, - Jack uległ uszkodzeniu i został zatrzymany. Jeżeli problem powtarza się, zapraszamy do przedstawienia go na liście mailowej MusE (informacja o subskrypcji znajduje się na stronie domowej MusE dostępnej w menu Pomoc). Aby kontynuować, należy sprawdzić status serwera Jack i spróbować go zrestartować. Następnie należy kliknąć na przycisk Restart. Automation: Automatyka: previous event poprzedni element next event następne zdarzenie set event ustaw zdarzenie add event dodaj zdarzenie erase event skasuj zdarzenie erase range skasuj zakres clear automation wyczyść automatykę Automation Automatyka Previous event Next event Set event Add event Erase event Erase range Clear automation Midi control Sterowanie MIDI Assign Przyporządkuj Clear Wyczyść Clear all controller events? Wyczyścić wszystkie zdarzenia kontrolerów? &Ok &Akceptuj &Cancel A&nuluj Controller Bypass MusE: Tempo list MusE: Lista Tempa External tempo changes were recorded. Transfer them to master tempo list? Zostały nagrane zewnętrzne zmiany tempa. Czy przenieść je do głównej listy tempa? Do you want to process ALL or only selected events? Przetwarzać WSZYSTKIE, czy wybrane elementy? &Selected &Wybrane &All W&szystkie MusE - external script failed MusE - wystąpił błąd w skrypcie zewnętrznym MusE was unable to launch the script, error message: %1 MusE: nie można uruchomić skryptu, informacja o błędzie: %1 Und&o &Cofnij Re&do &Przywróć MusEGui select Pointer Tool: with the pointer tool you can: select parts move parts copy parts Kliknij na strzałkę. Strzałką możesz zaznaczać, przesuwać i kopiować klocki. select Pencil Tool: with the pencil tool you can: create new parts modify length of parts Kliknij na ołówek. Ołówkiem możesz tworzyć klocki oraz zmieniać ich długość. select Delete Tool: with the delete tool you can delete parts Kliknij na Gumkę. Gumką można kasować klocki. select Cut Tool: with the cut tool you can split a part Kliknij na Nożyczki. Nożyczkami można ciąć klocki. select Glue Tool: with the glue tool you can glue two parts Kliknij na Klej. Klejem możesz sklejać dwa klocki select Score Tool: Wybierz Podgląd Nutowy. select Quantize Tool: insert display quantize event Ustaw Kwantyzator (ustawia kwantyzację wstawiania komunikatów MIDI). select Drawing Tool wybierz Wykres select Muting Tool: click on part to mute/unmute Kliknij na Wyłącz. Kliknij na pojedynczy klocek aby go wyłączyć z odtwarzania. pointer Strzałka pencil Ołówek eraser Gumka cutter Nożyczki score Pogląd nutowy quantize Kwantyzator draw Wykres mute parts Wyłącz klocki z odtwarzania MusEGui::Appearance Main application font, and default font for any controls not defined here. Główna czcionka aplikacji i domyślna czcionka dla elementów, które nie są tu wymienione. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Not sure what is 'control panel entry box'. Sekcje kanałów miksera i racki efektów. Panel informacyjny śladów MIDI. Okno dialogowe panelu kontrolnego MIDI. Transport controls. Tekst na panelu transportu. Time scale upper, and time signature. Controller graph and S/X buttons. Górna linijka czasu i metrum. Wykres kontrolera i przyciski S/X. Time scale lower, and arranger part name overlay. Dolna linijka czasu i nazwy klocków w oknie głównym. Main application font, and default font for any controls not defined here. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Arranger part name overlay. Time scale lower. Tempo scale, and markers. Linijka tempa i znaczniki. Mixer labels. Auto-font-sizing up to chosen font size. Word-breaking but only with spaces. Etykiety miksera. Elementy są automatycznie powiększkane do wybranego rozmiaru czcionki. Linie łamane są tylko w miejscu spacji. Maximum mixer label auto-font-sizing font size. Maksymalny rozmiar czcionki etykiet miksera. Global opacity (opposite of transparency). Globalna przeźroczystość (właściwie poziom nieprzeźroczystości). Standard Standardowe Custom Użytkownika Keep Qt system style Zachowaj systemowy temat Qt Do you really want to reset colors to theme default? Czy naprawdę chcesz powrócić do domyślnych kolorów tematu? Style was changed. The program must be restarted for changes to take place. Restart now? Revert changes Revert all... Do you really want to reset all colors? &Ok &Akceptuj &Cancel A&nuluj MusE: load image MusE: załaduj obrazek No current color item Select Color: %1 Select style sheet Wybierz arkusz stylów Qt style sheets (*.qss) Arkusze stylów Qt (*.qss) MusEGui::Arranger Enable Recording Aktywuj nagrywanie Mute/Off Indicator Kontrolka wyciszenia (mute) Solo Indicator Kontrolka trybu solo Track Type Typ śladu Track Name Nazwa śladu Midi output channel number or audio channels Numer wyjściowego kanału MIDI bądź kanałów audio Midi output port or synth midi port Port wyjściowy MIDI bądź port MIDI syntezatora programowego Time Lock Zablokuj ślad (Time Lock) Automation parameter selection Wybór parametrów automatyki Notation clef Klucz zapisu nutowego Enable recording. Click to toggle. Aktywuj nagrywanie. Kliknij, aby przełączyć. Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. On/Off is not! Can't uderstand the meaning of the last sentences. So, not translating it now. Kontrolka wyciszenia (mute). Kliknij, aby przełączyć. Prawy klawisz włącza/wyłącza ślad. Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed, indicated by a dark square. Kontrolka trybu solo. Kliknij, aby przełączyć. Ślady podłączone do modyfikowanego przejdą w „pośredni” tryb solo, co jest zaznaczone ciemnym kwadratem. Track type. Right-click to change midi and drum track types. Typ śladu. Prawy klawisz zmienia typ śladu perkusyjnego i MIDI. Track name. Double-click to edit. Right-click for more options. Nazwa śladu. Podwójny klik - edycja. Prawy klawisz pojazuje więcej opcji. Midi/drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Ślad MIDI/perkusyjny: numer kanału wyjściowego. Ślad audio: kanały. Środkowy/prawy klawisz zmienia wartość. Midi/drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Ślad MIDI/perkusyjny: port wyjściowy. Ślad syntezatora software'owego: przyporządkowane porty MIDI. Środkowy/prawy klawisz zmienia wartość. Prawy klawisz pokazuje GUI syntezatora. Time lock Zablokuj ślad (Time Lock) Notation clef. Select this tracks notation clef. Klucz zapisu nutowego. Pozwala na wybranie klucza zapisu nutowego. Track index. Click to select track, CTRL+LMB to add/remove, SHIFT+LMB for range select, dblclick to select all of the same type. Enable/Disable input monitor. Passes input through to output (see also Settings: Automatically monitor on record arm). Enable/Disable recording. Mute indicator. Left-click to mute track (for quick action), right-click to toggle track on/off (for more permanent action). Track type. Click to switch between midi and drum track types. Track name. Double-click to edit. Right-click for context menu. Midi/Drum: Output channel number. Audio: Channels. Mid/right-click to change. Midi/Drum: Output port. Synth: Assigned midi port. Left-click to change, right-click to show synth GUI. Notation clef. Click to select this tracks notation clef. Arranger Główne Okno Cursor Kursor Cursor position Bars Takty Off Wył. Bar Takt Track index Enable input monitor Enable recording Mute/Off indicator Solo indicator Track type Track name Enable input monitor. Click to toggle. Passes input through to output for monitoring. See also Settings: Automatically Monitor On Record Arm. Enable recording. Click to toggle. See also Settings: Automatically Monitor On Record Arm. Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed. Midi/Drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi/Drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Solo indicator. Connected tracks are also 'phantom' soloed. Snap Przyciąganie Len Długość song length - bars długość utworu - takty Type Typ NO Brak GM GM GS GS XG XG midi song type Utwór Midi Pitch Transpozycja midi pitch Transpozycja MIDI global midi pitch shift Globalna transpozycja MIDI Tempo Tempo midi tempo Tempo MIDI Song length - bars Midi pitch Global midi pitch shift Midi tempo N 100% TrackInfo Info o śladzie I Input monitor Wej. R Record R S Solo S C Class (track type) T Track Ślad Port Port Ch Channel Kan. T Time lock B Automation Automatyka Clef Klucz MusEGui::ArrangerColumns Control7 Kontroler7 Control14 Kontroler14 RPN RPN NPRN RPN14 NRPN14 Pitch Odchylenie (Pitch bend) Program Program Aftertouch Docisk (Aftertouch) MusEGui::ArrangerView MusE: Arranger MusE: Główne Okno Arranger Główne Okno D&elete &Kasuj C&ut &Wytnij &Copy K&opiuj Copy in range Kopiuj zakres &Paste &Wklej Paste c&lone Wklej klo&n Paste to selected &track Wkle&j do zaznaczonego śladu Paste clone to selected trac&k Wklej klon do zaz&naczonego śladu Paste (show dialo&g) Wklej (pokaż okno dialo&gowe) &Insert Empty Measure &Wstaw pusty takt Delete Selected Tracks Skasuj zaznaczone ślady Duplicate Selected Tracks Kopiuj zaznaczone ślady Shrink selected parts Skróć zaznaczone klocki Expand selected parts Rozszerz zaznaczone klocki Purge hidden events from selected parts Usuń ukryte elementy z zaznaczonych klocków Copy in Range Paste C&lone Paste to Selected &Track Paste Clone to Selected Trac&k Paste (Show Dialo&g)... Shrink Selected Parts Expand Selected Parts Purge Hidden Events from Selected Parts Add Track Dodaj ślad Insert Track Dodaj ślad Select Wybierz Select &All Zaznacz &wszystko &Deselect All &Odznacz wszystko Invert &Selection Odw&róć zaznaczenie &Inside Loop &Pomiędzy lokatorami &Outside Loop &Poza obrębem lokatorów All &Parts on Track Wszystkie &klocki na śladzie Score Zapis nutowy All Tracks in One Staff One Staff per Track New Score Window... Pianoroll... Drums... Event List... Wave... Open in New Window Always open the part(s) in a new editor window Open Synth Plugin GUI... Midi &Transform... Global Cut - Selected Tracks Global Insert - Selected Tracks Global Split - Selected Tracks Change Note &Length Change Note &Velocity &Scripts Scripts for midi processing. Applied to selected midi parts (or else tracks). User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. &Display Display menu: View-specific display options. Toggle &Mixer Strip Configure &Custom Columns... New... Nothing to edit Nie ma nic do edytowania all tracks in one staff Wszystkie ślady na jednej pięciolinii one staff per track Jedna pięciolinia na ślad New score window Nowe okno z zapisem nutowym Pianoroll Pianoroll Drums Edytor Perkusji List Edytor Lista Wave Edytor Audio Mastertrack Ślad Tempo/Metrum Graphic Edytor Graficzny Tempo/Metrum Midi &Transform &Przekształcanie MIDI Global Cut Globalne wycięcie Global Insert Globalne wstawienie Global Split Globalne podzielenie Global Cut - selected tracks Globalne wycięcie - zaznaczone ślady Global Insert - selected tracks Globalne wstawienie - zaznaczone ślady Global Split - selected tracks Globalne podzielenie - zaznaczone ślady &Edit &Edycja Fu&nctions &Structure &Globalne Functions Działania &Quantize Notes &Kwantyzuj nuty Change note &length Modyfikuj &długość nut Change note &velocity Modyfikuj &prędkość uderzenia (velocity) Crescendo/Decrescendo Crescendo/decrescendo Transpose Transponuj Erase Events (Not Parts) Kasuj zdarzenia (pozostaw klocki) Move Events (Not Parts) Przesuń nuty (pozostaw klocki) Set Fixed Note Length Ustaw stałą długość nut Delete Overlapping Notes Kasuj nakładające się nuty Legato Window &Config &Konfiguracja okna Configure &custom columns Konfiguruj &kolumny użytkownika Remove track(s) Usuń ślad(y) Are you sure you want to remove this track(s)? Czy na pewno usunąć te ślad(y)? New Nowy Changed Settings Zmienione ustawienia Unfortunately, the changed arranger column settings cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. (we'll try to fix that) Niestety, zmiana ustawień kolumn w Oknie Głównym nie może być zastosowana podczas działania MusE. Wymagany jest restart MusE. Przepraszamy. (spróbujemy poprawić tę niedogodność) MusEGui::AudioComponentRack Aux send level (dB) Vol Pan Panorama Mute Volume/gain Głośność/wzmocnienie Panorama/Balance Gain Calibration gain MusEGui::AudioMixerApp &Create &Utwórz &View &Przeglądaj Strips Sekcje kanałów Routing Konfiguracja połączeń Show Midi Tracks Pokaż ślady MIDI Show Drum Tracks Pokaż ślady perkusyjne Show New Style Drum Tracks Pokaż ślady perkusyjne nowego typu Show Wave Tracks Pokaż ślady audio Show Inputs Pokaż wejścia Show Outputs Pokaż wyjścia Show Groups Pokaż grupy Show Auxs Pokaż szyny (aux) Show Synthesizers Pokaż syntezatory Traditional order Kolejność tradycyjna Arranger order Kolejność z Głównego Okna User order Kolejność użytkownika Show all hidden strips Pokaż wszystkie ukryte kanały Unhide strip: Pokaż ukryty kanał: (no hidden strips) (brak ukrytych kanałów) MusEGui::AudioStrip panorama panorama aux send level poziom sygnału na wysyłce Pan Panorama calibration gain kalibracja wzmocnienia aux send level (dB) poziom sygnału na wysyłce (dB) 1/2 channel kanał m/s Pre Przed pre fader - post fader przed/za tłumikiem Gain Wzm. Volume/gain Głośność/wzmocnienie dB dB record nagrywaj mute wycisz record downmix zgraj miks solo mode tryb solo off wył. input routing połączenia wejściowe output routing połączenia wyjściowe Pre Fader Listening (PFL) Volume/Gain Input monitor: Pass input through to output. Record downmix Record arm Mute or proxy mute Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Track off Auto off Automation type: Off, Read, Touch or Write. Press F1 for help. Input monitor Input routing. Press F1 for help. Output routing. Press F1 for help. Pass input through to output Off Wył. Read Czytaj Touch Dotknij Write Zapisz Automation type automation type typ automatyki L meter peak/clip Lewy miernik poziomu szczytowego/przesterowania R meter peak/clip Prawy miernik poziomu szczytowego/przesterowania MusEGui::BigTime format display format wyświetlania bar takt beat uderzenie minute minuta second sekunda frame ramka subframe podramka Format display Bar Beat Rytm Tick Minute Second Drugi Frame Subframe MusE: Bigtime MusE: Duży Zegar MusEGui::Canvas Tools: Narzędzia: Tools Narzędzia MusEGui::ClipListEdit MusE: Clip List Editor MusE: Edytor Lista Klipów Window &Config Konfigura&cja okna MusEGui::ComboQuant Off Wył. MusEGui::CompactKnob off wył. MusEGui::CompactPatchEdit Patch name Nazwa brzmienia Patch high-bank number (Ctrl-double-click on/off) Numer górnego banku dla danej barwy (Ctrl + podwójny klik włącza/wyłącza) Patch low-bank number (Ctrl-double-click on/off) Numer dolnego banku dla danej barwy (Ctrl + podwójny klik włącza/wyłącza) Patch program (Ctrl-double-click on/off) Nr programu brzmienia (Ctrl + podwójny klik włącza/wyłącza) Patch high-bank number Numer górnego banku dla danej barwy Patch low-bank number Numer dolnego banku dla danej barwy Patch program Nr programu brzmienia MusEGui::CompactSlider off wył. MusEGui::CpuStatusBar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) CPU load averaged over each GUI update period. DSP load read from JACK. Number of xruns. Double-click to reset. MusEGui::CpuToolbar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) MusEGui::CtrlCanvas Make the current part's track match the selected drumlist entry Is it correct? Dopasuj ślad bieżącego klocka do wybranej pozycji listy instrumentów perkusyjnych Drawing hint: Hold Ctrl to affect only existing events Wskazówka odnośnie rysowania: przytrzymaj ctrl aby zmienić tylko istniejące elementy Use pencil or line tool to draw new events Użyj ołówka bądź narzędzia linii aby dodać nowe elementy Control canvas: Use Pencil tool to edit events and Draw tool to adjust them gradually. Hold Ctrl to affect only existing events. Merge options Erase target Erase target events between source events Erase target WYSIWYG Include last source item width when erasing Erase target inclusive Include entire source range when erasing Merge actions Merge Merge the dragged items Merge a copy Merge a copy of the dragged items Cancel drag Cancel dragging the items MusEGui::CtrlPanel S Select controller S Select controller Remove panel Manual adjust (Ctrl-double-click on/off) All/Per-note velocity mode select controller wybierz kontroler X Remove panel (destroy button) X remove panel usuń panel manual adjust ręczne dopasowywanie ctrl-double-click on/off ctrl + podwójny klik włącza/wyłącza off wył. all/per-note velocity mode tryb wyświetlania. velocity: wszystkie/per nuta Velocity Prędk. uderz. (vel) add new ... dodaj nowe ... MusEGui::DList hide this instrument ukryj ten instrument show this instrument pokaż ten instrument Hide this instrument This turns a blue eye into a crossed eye Show this instrument This turns a crossed eye into a blue eye Set field Sets a field Set row Sets a row Set column Sets a whole column to the field Set list Sets the whole list Reset field Resets a field in a row to default patch or instrument value Reset row Resets a row to the instrument values Reset column Resets a whole column to the instrument values Reset list Resets the whole list to the instrument values Reset track's drum list Resets all lists on all patches to the instrument values Promote field to default patch Promotes a field in a row to the default patch Promote row to default patch Promotes a row to the default patch Promote column to default patch Promotes a column to the default patch Promote list to default patch Promotes the whole list to the default patch Reset instrument field Reset instrument row Reset instrument list Set instrument field Set instrument row Set instrument list Drum list: Edit instrument-specific settings. MusEGui::DrumCanvas Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help. Moving items failed Przesunięcie elementu nie powiodło się The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. Wybrane elementy nie mogą zostać przeniesione, ponieważ przynajmniej jedna nuta zostanie przeniesiona na ślad, który jest inny, niż ślad oryginalny i bieżący ślad klocka. Pomóc może zmiana bieżacego klocka za pomocą klawiszy ALT+LEWO/PRAWO. Creating event failed Utworzenie elementu nie powiodło się Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Not sure I understand the text correctly. Probably should be fixed. Nie można utworzyć elementu, ponieważ aktualnie wybrany klocek nie jest na tym samym śladzie i wybrany instrument może być na żadnym bądź wielu śladach, co jest dwuznaczne. Wybierz klocek docelowy i spróbuj ponownie. Recording event failed Nagranie zdarzenia nie powiodło się Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Not sure I understand the text correctly. Probably should be fixed. Nie można nagrać zdarzenia, ponieważ aktualnie wybrany klocek nie jest na tym samym śladzie i wybrany instrument może być na żadnym bądź wielu śladach, co jest dwuznaczne. Wybierz klocek docelowy i spróbuj ponownie. Internal error Błąd wewnętrzny Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. WTF? Wystąpił dziwny błąd, który nie miał prawa wystąpić. Sprawdź informacje na konsoli. Nic nie zostało nagrane. Drum map Zestaw perkusyjny Reset the track's drum map with instrument defaults? Przywrócić zestaw perkusyjny śladu do wartości domyślnych? Note: Velocity: Start (bar): Start (time): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete LMB: Delete Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete. MusEGui::DrumEdit mute instrument wycisz instrument sound name nazwa brzmienia volume percent procent głośności quantisation kwantyzacja this input note triggers the sound ten dźwięk odgrywa brzmienie note length długość dźwięku this is the note which is played to jest dźwięk, który jest odgrywany &File &Plik Load Map Załaduj zestaw Save Map Zapisz zestaw Reset GM Map Resetuj zestaw GM &Edit &Edycja Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Note which triggers the sound Note which is played Override track output channel (hold CTRL to affect all rows). Override track output port (hold CTRL to affect all rows). CTRL + META keys: Draw velocity level 1 META key: Draw velocity level 2 META + ALT keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) Cut Wytnij Copy Kopiuj Copy events in range Kopiuj elementy z zakresu Paste Wklej Paste to current part Paste (with Dialog) Wklej (pokaż okno dialogowe) Delete Events Kasuj elementy &Select &Zaznacz Select All Zaznacz wszystko Select None Bez zaznaczenia Invert Odwróć Inside Loop Pomiędzy lokatorami Outside Loop Poza obrębem lokatorów Previous Part Poprzedni klocek Next Part Następny klocek Event List... Fu&nctions Działania Set Fixed Length Ustaw stałą długość nut Modify Velocity Modyfikuj prędkość uderzenia (velocity) Crescendo/Decrescendo Crescendo/decrescendo Quantize Kwantyzuj Erase Event Kasuj zdarzenia Move Notes Przesuń nuty Delete Overlaps Kasuj nakładające się nuty &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Cursor step &Plugins &Wtyczki Cursor tools Add controller view Window &Config &Konfiguracja okna Drum tools Narzędzia perkusyjne Load Drummap Załaduj zestaw perkusyjny hide instrument ukryj instrument override track output channel (hold ctl to affect all rows) nadpisz kanał wyjściowy śladu (wciśnij ctrl aby zmienić we wszystkich rzędach) override track output port (hold ctl to affect all rows) nadpisz wyjściowy port śladu (wciśnij ctrl aby zmienić we wszystkich rzędach) control + meta keys: draw velocity level 1 ctrl + klawisze meta: rysuj velocity poziom 1 meta key: draw velocity level 2 klawisz meta: rysuj velocity poziom 2 draw default velocity level 3 rysuj domyślne velocity poziom 3 meta + alt keys: draw velocity level 4 alt + klawisze meta: rysuj velocity poziom 4 override track output channel (ctl: affect all rows) nadpisz kanał wyjściowy śladu (wciśnij ctrl aby zmienić we wszystkich rzędach) override track output port (ctl: affect all rows) nadpisz wyjściowy port śladu (wciśnij ctrl aby zmienić we wszystkich rzędach) Re-order map Przesortuj zestaw perkusyjny &Display Display menu: View-specific display options. Group Grupuj Don't group Nie grupuj Group by channel Grupuj po kanale Group maximally Grupuj maksymalnie Show/Hide Pokaż/ukryj Also show hidden instruments Pokaż także ukryte instrumenty Show all instruments Pokaż wszystkie instrumenty Hide all instruments Ukryj wszystkie instrumenty Only show used instruments Pokaż tylko używane instrumenty Only show instruments with non-empty name or used instruments Pokaż tylko używane instrumenty bądź te, których nazwa nie jest pusta Step record Midi input Play events Drum map tools Narzędzia zestawu perkusyjnego Store Drummap Zapisz zestaw perkusyjny Step Record Nagrywanie krokowe Midi Input Wejście MIDI Play Events Odgrywaj nuty cursor tools narzędzia kursora Cursor step: Krok kursora: Set step size for cursor edit Ustaw rozmiar kroku dla edycji kursorem Add Controller View Dodaj widok kontrolera H hide H M mute Sound Brzmienie Vol Głośn. QNT Kwant. E-Note U-dźwięk Len Długość A-Note W-dźwięk Ch Kan. Port Port LV1 PV1 LV2 PV2 LV3 PV3 LV4 PV4 Muse: Load Drum Map MusE: załaduj zestaw perkusyjny MusE: Store Drum Map MusE: zapisz zestaw perkusyjny Drum map Zestaw perkusyjny Reset the drum map with GM defaults? Nadpisać zestaw perkusyjny instrumentami GM? Not all parts are displayed Nie wszystkie klocki są wyświetlane You selected both old-style-drumtracks and others (that is: new-style or midi tracks), but they cannot displayed in the same drum edit. I'll only display the old-style drumtracks in this editor, dropping the others. Zostały wybrane ślady perkusyjne starego typu, jak również inne ślady (czyli perkusja nowego typu lub ślady MIDI), jednak nie mogą być wyświetlone w tym samym oknie edycyjnym. Wyświetlam jedynie perkusję starego typu, pomijając resztę. MusEGui::EditCAfterDialog MusE: Enter Channel Aftertouch MuzA: Wstaw zmianę średniego nacisku pod uderzeniu (channel aftertouch) Time Position Pozycja: Pressure Nacisk MusEGui::EditEventDialog Ok Akceptuj Cancel Anuluj MusEGui::EditInstrument Use note offs Używaj Note Off No note offs Nie używaj Note Off Convert to 0-vel note ons Przekształć w Note On z Velocity 0 Control7 Kontroler7 Control14 Kontroler14 RPN RPN NPRN RPN14 NRPN14 Pitch Odchylenie od stroju (Pitch bend) Program Program PolyAftertouch Docisk polifoniczny (Poly Aftertouch) Aftertouch Docisk (Aftertouch) H hide H M mute Name Nazwa Vol Głośn. Quant Kwant. E-Note U-dźwięk Len Długość A-Note W-dźwięk Ch Kan. Port Port LV1 PV1 LV2 PV2 LV3 PV3 LV4 PV4 Tick Tick (impuls sygnału czasu) Data Dane Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) MusE: Create file failed MusE: utworzenie pliku nie powiodło się MusE: Write File failed MusE: zapis pliku nie powiódł się MusE: Save Instrument Definition MusE: zapis definicji instrumentu Instrument Definition (*.idf) Definicja instrumentu (*.idf) MusE: Save instrument as MusE: zapisz instrument jako Enter a new unique instrument name: Wpisz nową unikalną nazwę instrumentu The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? Instrument użytkownika „%1” już istnieje. Plik .idf zostanie nadpisany. Jesteś pewien? MusE: Bad instrument name MusE: niewłaściwa nazwa instrumentu Please choose a unique instrument name. (The name might be used by a hidden instrument.) Proszę wybrać unikalną nazwę dla instrumentu (Nazwa może być używana przez ukryty instrument.) MusE: Bad patch name MusE: niewłaściwa nazwa brzmienia Please choose a unique patch name Proszę wybrać unikalną nazwę brzmienia MusE: Bad patchgroup name MusE: zła nazwa grupy brzmień Please choose a unique patchgroup name Proszę wybrać unikalną nazwę grupy brzmień MusE: Bad controller name MusE: niewłaściwa nazwa kontrolera Please choose a unique controller name Proszę wybrać unikalną nazwę kontrolera New controller: Error Nowy kontroler: błąd Error! All control numbers are taken up! Clean up the instrument! Błąd! Wszystkie wartości kontrolerów zostały użyte. Proszę wyczyścić definicję instrumentu! MusE MusE The current Instrument contains unsaved data Save Current Instrument? Bieżący instrument zawiera niezapisane dane. Zapisać bieżący instrument? &Save &Zapisz &Nosave &Nie zapisuj &Abort &Anuluj MusEGui::EditMetaDialog MusE: Enter Meta Event MusE: wstaw meta-zdarzenie Time Position Pozycja Meta Type Meta typ Enter Hex Wstaw heks. MusEGui::EditPAfterDialog MusE: Enter Poly Aftertouch MuzA: Wstaw zmianę średniego nacisku pod uderzeniu (channel aftertouch) Time Position Pozycja: Pitch Transpozycja Pressure Nacisk MusEGui::EditToolBar select Pointer Tool: with the pointer tool you can: select parts move parts copy parts Kliknij na strzałkę. Strzałką możesz zaznaczać, przesuwać i kopiować klocki select Pencil Tool: with the pencil tool you can: create new parts modify length of parts Kliknij na ołówek. Ołówkiem możesz tworzyć klocki oraz zmieniać ich długość. select Delete Tool: with the delete tool you can delete parts Kliknij na gumkę. Gumką można kasować klocki. select Cut Tool: with the cut tool you can split a part Kliknij na nożyczki. Nożyczkami można ciąć klocki. select Glue Tool: with the glue tool you can glue two parts Kliknij na klej. Klejem możesz sklejać dwa klocki. select Score Tool: Wybierz zapis nutowy select Quantize Tool: insert display quantize event Ustaw kwantyzator. Ustawia kwantyzację wstawiania komunikatów MIDI. select Drawing Tool Wybierz wykres select Muting Tool: click on part to mute/unmute Kliknij na wyłącz. Kliknij na pojedynczy klocek aby go wyłączyć z odtwarzania. Select Pointer tool: With the pointer tool you can: select parts move parts copy parts Select Pencil tool: With the pencil tool you can: create new parts modify length of parts Select Delete tool: With the delete tool you can delete parts Select Cut tool: With the cut tool you can split a part Select Glue tool: With the glue tool you can glue two parts Select Score tool: Select Quantize tool: Insert display quantize event Select Drawing tool Select Muting tool: Click on part to mute/unmute Manipulate automation Modyfikuj automatykę Select Cursor (tracker mode) tool: With the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Select Range tool Select Panning tool Select Zoom tool Select Stretch tool Select Sample rate tool Pointer Pencil Eraser Cutter Glue Range Zakres Pan Panorama Zoom Score Zapis nutowy Quantize Kwantyzuj Draw Mute parts Edit automation Cursor Kursor Stretch Sample rate Częstotliwość próbkowania pointer strzałka pencil ołówek eraser gumka cutter nożyczki score zapis nutowy glue klej select Cursor (tracker mode) tool: with the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Wybierz kursor (tryb trackera). Z narzędziem kursora możesz: - poruszać za pomocą klawiszy kursora, - użyć klawiszy VBNM do rysowania nut, - zmienić krok w zakresie 0 - 9. select Range Tool Wybierz zakres select Panning Tool Wybierz przesuwanie select Zoom Tool Wybierz powiększenie range zakres pan przesuwanie zoom powiększanie quantize kwantyzacja draw wykres mute parts wyłączenie klocków z odtwarzania edit automation edycja automatyki cursor kursor Edit Tools Narzędzia edycyjne MusEGui::EffectRack effect rack rack efektów new nowy change zmień move up przesuń w górę move down przesuń w dół remove usuń bypass omiń (bypass) show gui pokaż interfejs użytkownika show native gui pokaż natywny interfejs użytkownika save preset zapisz ustawienie Effect rack: Double-click a slot to insert/edit effect. RMB to open context menu. Press F1 for help. Effect rack Double-click a slot to insert FX New Nowy Change Zmień Move up Move down Remove Usuń Bypass Show gui Show native gui Save preset Zapisz ustawienie Presets Ustawienia MusE: Save Preset MusE: zapisz ustawienie Replace effect Zamień efekt Do you really want to replace the effect %1? Czy naprawdę chcesz zamienić efekt %1? MusEGui::GlobalSettingsConfig MusE: Choose start template or song MusE: wybierz szablon początkowy bądź utwór MusEGui::Header Track Info Columns Kolumny informacji o śladzie Custom column MusEGui::LCDPatchEdit High bank: Low bank: Program (Ctrl-double-click on/off) off wył. High bank Low bank Program Program MusEGui::LMaster MusE: Mastertrack MusE: Ślad Tempo/Metrum &Edit &Edycja Insert Tempo Wstaw tempo Insert Signature Wstaw metrum Insert Key Wstaw tonację Edit Positon Edytuj pozycję Edit Value Edytuj wartość Delete Event Skasuj zdarzenie Window &Config &Konfiguracja okna Edit tools Narzędzia edycyjne Tempo Tempo Timesig Metrum Key Tonacja new tempo nowe tempo new signature nowe metrum new key nowa tonacja Meter Pozycja Time Czas Type Typ Value Wartość Signature Metrum Position Pozycja Delete Kasuj Insert tempo change Insert time signature change Insert key change Edit position Edit value Delete event Reposition of the initial tempo and signature events is not allowed Zmiana pozycji początkowego tempa i metrum jest niedozwolona MusE: List Editor MusE: Edytor Lista Input error, conversion not OK or value out of range Błąd wprowadzanej wartości: niewłaściwy format bądź wartość poza zakresem Reposition of tempo and signature events to start position is not allowed! Zmiana tempa i metrum do pozycji początkowej jest niedozwolona! MusEGui::ListEdit insert Note Wstaw nutę insert SysEx Wstaw SysEx insert Ctrl Wstaw kontroler insert Meta Wstaw meta insert Channel Aftertouch Wstaw zmianę średniego nacisku pod uderzeniu (channel aftertouch) insert Poly Aftertouch Wstaw zmianę średniego nacisku pod uderzeniu (Poly Aftertouch) &Edit &Edycja Cut Wytnij Copy Kopiuj Paste Wklej Delete Events Kasuj zdarzenia Increase Tick Zwiększ tick Decrease Tick Zmniejsz tick Window &Config &Konfiguracja okna Insert Note Wstaw nutę Insert SysEx Wstaw SysEx Insert Ctrl Wstaw kontroler Insert Meta Wstaw meta Note SysEx Ctrl Meta Tick+ Tick- Delete Kasuj Insert tools Narzędzia wstawiania Tick Tick Bar Pozycja Type Typ Ch Kan. Val A Wart. A Val B Wart. B Val C Wart. C Len Długość Comment Opis Insert note event Insert system exclusive event Insert controller event Insert meta event Increase tick value Decrease tick value Delete event MusE: List Editor MusE: Edytor Lista MusEGui::MPConfig Default input connections Domyślne połączenia wejściowe Are you sure you want to apply to all existing midi tracks now? Czy na pewno teraz chcesz zastosować dla wszystkich śladów MIDI? Default output connections Domyślne połączenia wyjściowe MusE: bad device name MusE: niewłaściwa nazwa urządzenia please choose a unique device name proszę wybrać unikalną nazwę urządzenia in wej. out wyj. Toggle all Przełącz wszystkie Change all tracks now Zmień na wszystkich śladach Remove Usuń Create Jack device Utwórz urządzenie Jack Port Number Numer portu Enable gui Włącz GUI Enable reading Włącz wejście Enable writing Włącz wyjście Port instrument Numer portu Midi device name. Click to edit (Jack) Nazwa urządzenia MIDI. Kliknij, aby edytować (Jack) Connections from Jack Midi outputs Połączenia z wyjść MIDI Jack Connections to Jack Midi inputs Połączenia do wejść MIDI Jack Auto-connect these channels to new midi tracks Automatycznie podłączaj te kanały do nowych śladów MIDI Auto-connect new midi tracks to these channels Automatycznie podłączaj nowe ślady MIDI do tych kanałów Auto-connect new midi tracks to this channel Automatycznie podłączaj nowe ślady MIDI do tego kanału Device state Status urządzenia Enable gui for device Włącz graficzny interfejs użytkownika (GUI) dla tego urządzenia Enable reading from device Włącz odczyt z tego urządzenia (wejście) Enable writing to device Włącz zapis do tego urządzenia (wyjście) Name of the midi device associated with this port number. Click to edit Jack midi name. Nazwa urządzenia MIDI powiązanego z tym numerem portu. Kliknij, aby edytować nazwę urządzenia Jack. Instrument connected to port Instrument połączony do portu Connections from Jack Midi output ports Połączenia z portów wyjściowych Jack MIDI Connections to Jack Midi input ports Połączenia do portów wejściowych Jack MIDI Auto-connect these channels, on this port, to new midi tracks. Automatycznie podłączaj te kanały (na tym porcie) do nowych śladów MIDI. Connect new midi tracks to these channels, on this port. Podłącz nowe ślady MIDI do tych kanałów (na tym porcie). Connect new midi tracks to this channel, on this port. Podłącz nowe ślady MIDI do tego kanału (na tym porcie). State: result of opening the device Status: status otwarcia urządzenia Port Port GUI GUI I Wej. O Wyj. Instrument Device Name Nazwa urządzenia Please choose a unique device name Port number Midi device name Nazwa urządzenia MIDI Midi device type Typ urządzenia MIDI Connections from Jack Midi Połączenia z Jack MIDI Connections to Jack Midi Połączenia do Jack MIDI Result of opening the device: OK: Assigned to a port and in use Closed: Unassigned to a port, or closed R/W Error: Unable to open for read or write Unavailable: USB midi unplugged, or external application not running, or synth plugin not installed etc. (Jack Midi devices have 'unavailable ports' in the routes columns.) Unavailable devices or ports can be purged with 'Remove' or with the advanced router. Status otwarcia urządzenia: OK: przyporządkowane, w użyciu, Closed: nieprzyporządkowane do portu bądź zamknięte, R/W Error: błąd otwarcia do odczytu lub zapisu, Unavailable: urządzenie MIDI USB odłączone, zewnętrzna aplikacja nie pracuje, wtyczka syntezatora nie zainstalowana etc., Urządzenia lub porty ze statusem „unavailable” mogą być skasowane za pomocą przycisku Usuń bądź w oknie Konfiguracja Połączeń. Enable Graphical User Interface for device Włącz graficzny interfejs użytkownika (GUI) dla urządzenia Connections from Jack Midi ports Połączenia z portów Jack MIDI Connections to Jack Midi ports Połączenia do portów Jack MIDI In routes Poł. wej. Out routes Poł. wyj. Def in ch Domyślny kan. wej. Def out ch Domyślny kan. wyj. State Status Type Typ In JWej. Out JWyj. <unknown> <nieznany> <none> <brak> MusEGui::MTScale bar scale linijka taktów Bar scale Bar scale: Use mouse buttons to set position and range markers. Hold Shift to set (LMB) or delete (RMB) custom markers. MusEGui::MTScaleFlo bar scale linijka taktów Bar scale MusEGui::MarkerView MusE: Marker MusE: Edytor Znaczników add marker Dodaj znacznik delete marker Kasuj znacznik &Edit &Edycja Window &Config &Konfiguracja okna edit tools Narzędzia Edycyjne Add marker Delete marker Edit tools Narzędzia edycyjne Bar:Beat:Tick Takt:Uderzenie:Tick Hr:Mn:Sc:Fr:Sf Godz.:Min.:Sek.:Kl.:SKl Lock Zablokuj Description Opis Edit tick Edit SMPTE timecode Toggle timecode editing Edit marker description <Description> Text Opis Marker Properties Właściwości znacznika MusEGui::Master Tools Narzędzia MusEGui::MasterEdit MusE: Mastertrack MusE: Edytor Graficzny Tempo/Metrum Window &Config &Konfiguracja okna Enable master Włącz Ślad tempo/Metrum Enable Włącz Enable usage of master track Aktywuj Ślad tempo/Metrum Info Informacja Cursor Kursor time at cursor position Wstaw metrum w aktualnej pozycji kursora tempo at cursor position Wstaw tempo w aktualnej pozycji kursora Mastertrack Ślad Tempo/Metrum &Display Display menu: View-specific display options. Time at cursor position Tempo at cursor position Off Wył. Bar Takt Snap Przyciąganie CurPos Akt. poz. kursora tempo at current position Wstaw tempo w aktualnej pozycji kursora time signature at current position Wstaw metrum w aktualnej pozycji kursora MusEGui::MetronomeConfig Reset accents: Resets all accents to the defaults (first in list) of the current preset category (Factory or User). Proceed? MusEGui::MidiAudioControl Control7 Kontroler7 Control14 Kontroler14 RPN RPN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Odchylenie (Pitch bend) Program Program Aftertouch Docisk (Aftertouch) MusEGui::MidiComponentRack Pro Prg. Var Var. Rev Pogł. Cho Chorus Pan Panorama Program Program VariationSend VariationSend ReverbSend Wysyłka pogłosu ChorusSend Wysyłka chorusa Pan/Balance Pan/Balans Controller (Ctrl-double-click on/off) <unknown> <nieznany> Instrument Instrument Transpose Transponuj Transpose notes up or down Transpozycja nut (w górę bądź w dół) Delay Opóźnienie Offset playback of notes before or after actual note Opóźnienie/wyprzedzenie przy odtwarzaniu zapisanych na ścieżce dźwięków Length Change note length in percent of actual length Zmienia długość nut w stosunku procentowym do obecnej długości Velocity <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> <html><head/><body><p>Dodaj lub odejmij prędkość uderzenia (velocity) do/od nut zapisanych na śladzie.</p><p><span style=" font-style:italic;">Zakres velocity nut to 0-127, może się więc <br />zdarzyć, że nie będzie możliwe <br />przyporządkowanie w całości tak obliczonej <br />wartości (note velocity + velocity), gdyż <br />znajdzie się ona poza dopuszczalnym <br />zakresem.</span></p></body></html> Compress Compress the notes velocity range, in percent of actual velocity Kompresuj zakres prędkości uderzenia (Note Velocity), o procent w stosunku do wartości obecnej <Patch> MusEGui::MidiInputTransformDialog New Nowy MusEGui::MidiStrip B Palette B B off wył. Input monitor: Pass input through to output. Record arm Mute or proxy mute Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Track off Input monitor Intput routing. Press F1 for help. Output routing. Press F1 for help. Inst Instr. Prop Instruments and controllers Properties Właściwości Volume/Gain (Ctrl-double-click on/off) Pass input through to output ctrl-double-click on/off ctrl + podwójny klik włącza/wyłącza VariationSend VariationSend Transpose notes up or down Transpozycja nut (w górę bądź w dół) Tran Tran. Offset playback of notes before or after actual note Opóźnienie/wyprzedzenie przy odtwarzaniu zapisanych na ścieżce dźwięków Dly Opóź. Change note length in percent of actual length Zmienia długość nut w stosunku procentowym do obecnej długości Len Dł. <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> <html><head/><body><p>Dodaj lub odejmij prędkość uderzenia (velocity) do/od nut zapisanych na śladzie.</p><p><span style=" font-style:italic;">Zakres velocity nut to 0-127, może się więc <br />zdarzyć, że nie będzie możliwe <br />przyporządkowanie w całości tak obliczonej <br />wartości (note velocity + velocity), gdyż <br />znajdzie się ona poza dopuszczalnym <br />zakresem.</span></p></body></html> Vel Vel. Compress the notes velocity range, in percent of actual velocity Kompresuj zakres prędkości uderzenia (Note Velocity), o procent w stosunku do wartości obecnej Cmp Kompr. Instrument Instrument <unknown> <nieznany> Program Program Pro Prg. VariationSend (Ctrl-double-click on/off) VariationSend (Ctrl + podwójny klik włącza/wyłącza) ReverbSend (Ctrl-double-click on/off) Wysyłka pogłosu (Ctrl + podwójny klik włącza/wyłącza) Volume/gain (Ctrl-double-click on/off) Głośność/wzmocnienie. (Ctrl + podwójny klik włącza/wyłącza) Prg Prg. Var Var. ReverbSend Wysyłka pogłosu Rev Pogł. ChorusSend Wysyłka chorusa Cho Chorus ChorusSend (Ctrl-double-click on/off) Wysyłka chorusa (Ctrl + podwójny klik włącza/wyłącza) dB dB Pan/Balance Pan/Balans Pan Panorama Pan/Balance (Ctrl-double-click on/off) Panorama/balans (Ctrl + podwójny klik włącza/wyłącza) record Nagrywanie mute Wycisz solo mode Tryb solo input routing Połączenia wejściowe output routing Połączenia wyjściowe midi thru MIDI thru Pass input events through ('thru') to output Przekazuje zdarzenia wejściowe do wyjścia (through - „thru”). MusEGui::MidiSyncConfig Port Number Numer portu Name of the midi device associated with this port number Nazwa urządzenia MIDI skojarzona z tym numerem portu Sync to this device. Click to select. Midi clock input detected Wykrycie na wejściu zegara MIDI Midi tick input detected Wykrycie na wejściu MIDI tick Midi real time input detected Wykrycie na wejściu komunikatów MIDI czasu rzeczywistego (realtime) MMC input detected Wykrycie na wejściu komunikatów MMC MTC input detected Wykrycie na wejściu komunikatów MTC Detected SMPTE format Wykryty format SMPTE Receive id number. 127 = Global. Double click to edit. Numer identyfikacyjny (odbiór). 127 = globalny. Podwójny klik - edycja. Accept midi clock input Odbieraj na wejściu zegar MIDI Accept midi real time input Odbieraj na wejściu komunikaty MIDI czasu rzeczywistego (realtime) Accept MMC input Odbieraj na wejściu komunikaty MMC Accept MTC input Odbieraj na wejściu komunikaty MTC Receive start rewinds before playing Odbieraj przewijanie do początku przed odtwarzaniem Transmit id number. 127 = Global. Double click to edit. Numer identyfikacyjny (nadawanie). 127 = globalny. Podwójny klik - edycja. Send midi clock output Wysyłaj komunikaty zegara MIDI Send midi realtime output Wysyłaj komunikaty MIDI czasu rzeczywistego (realtime) Send MMC output Wysyłaj komunikaty MMC Send MTC output Wysyłaj komunikaty MTC Type Typ Midi clock input detected. Current port actually used is red. Click to force a port to be used. Wykrycie na wejściu zegara MIDI. Aktualnie używany port zaznaczony jest na czerwono. Kliknij, aby wymusić używanie danego portu. Midi realtime input detected, including start/stop/continue, and song position. Wykrycie na wejściu komunikatów MIDI czasu rzeczywistego (realtime), w tym komunikatów: start/stop/continue oraz pozycja utworu. MMC input detected, including stop/play/deferred play, and locate. Wykrycie na wejściu komunikatów MMC, w tym komunikatów: stop/play/deferred play/locate. MTC input detected, including forward quarter-frame sync and full-frame locate. Current port actually used is red. Click to force a port to be current. Wykrycie na wejściu komunikatów MTC, w tym: forward quarter-frame sync, full-frame locate. Aktualnie używany port zaznaczony jest na czerwono. Kliknij, aby wymusić używanie danego portu. Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detects format of MTC quarter and full frame, and MMC locate. Wykrywane formaty SMPTE: 24fps, 25fps, 30fps drop frame, 30fps non-drop. Wykrywane są formaty MTC quarter i full frame, a także MMC locate. Receive id number. 127 = global receive all, even if not global. But what means: 'even if not global' in this context? Numer identyfikacyjny (odbiór). 127 = globalny odbiór wszystkiego. Accept midi clock input. Only one input is used for clock. Auto-acquire: If two or more port realtime inputs are enabled, the first clock detected is used, until clock is lost, then another can take over. Best if each turns off its clock at stop, so MusE can re-acquire the clock from another port. Click on detect indicator to force another. Odbieraj na wejściu zegar MIDI. Dla zegara używane jest tylko jedno wejście. Automatyczne przejmowanie: w przypadku, gdy wybrany jest więcej niż jeden port, zostaje użyty ten, na którym zegar pojawił się jako pierwszy. Jeśli zegar zniknie, wykorzystany zostanie następny port. Rozwiązanie to działa najlepiej, kiedy urządzenia zaprzestają wysyłania zegara przy zatrzymaniu odtwarzania. MusE może wtedy skorzystać z zegara odbieranego na innym porcie. Kliknij na kontrolce wejścia, aby wymusić używanie konkretnego portu. Accept midi realtime input, including start/stop/continue, and song position. Non-clock events (start,stop etc) are accepted by ALL enabled ports. This means you may have several master devices connected, and muse will accept input from them. Odbieraj komunikaty czasu rzeczywistego (realtime). W tym komunikaty: start/stop/continue oraz pozycję utworu. Zdarzenia nie związane z zegarem (start, stop etc.) są odbierane przez WSZYSTKIE aktywne porty. Oznacza to, że istnieć może kilka aktywnych urządzeń „master”. MusE odbierze komunikaty z każdego z nich. Accept MMC input, including stop/play/deferred play, and locate. Odbieraj komunikaty MMC, w tym komunikaty: stop/play/deferred play/locate. Accept MTC input, including forward quarter-frame sync and full-frame locate. See 'rc' column for more help. Odbieraj komunikaty MTC, w tym: forward quarter-frame sync, full-frame locate. Pomoc dla kolumny „rc” zawiera więcej informacji. When start is received, rewind before playing. Note: It may be impossible to rewind fast enough to synchronize with the external device. Jeżeli odebrano komunikat „start”, przewiń przed odtwarzaniem. Uwaga: szybkość przewijania może okazać się niewystarczająca do poprawnej synchronizacji z urządzeniem zewnętrznym. Sync to this device. Click to select. MusE will synchronize to this device's clock or MTC quarter-frame, if 'slave to external sync' is on. MTC input detected, including forward quarter-frame sync and full-frame locate Accept midi clock input. Only one port can be used for clock sync. Accept MTC input, including forward quarter-frame sync and full-frame locate. Only one port can be used for sync. When start is received, rewind before playing. Note: In some cases, such as having many project audio tracks, it may be impossible to rewind fast enough to synchronize with the external device. Manually rewinding the device before playing is recommended. Transmit id number. 127 = global transmit to all. Wysyłany numer identyfikacyjny. 127 = globalne wysyłanie do wszystkich. Send midi clock output. If 'Slave to External Sync' is chosen, muse can re-transmit clock to any other chosen ports. Wysyłaj zegar MIDI. Jeżeli wybrano opcję „Synchronizacja zewnętrzna (tryb slave)”, MusE może przekazywać odebrane komunikaty zegara do innego wybranego portu. Send midi realtime output, including start/stop/continue, and song position. If 'Slave to external sync' is chosen, muse can re-transmit midi realtime input messages to any other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. Wysyłaj komunikaty MIDI czasu rzeczywistego (realtime), w tym start/stop/continue oraz pozycję utworu. Jeżeli wybrano opcję „Synchronizacja zewnętrzna (tryb slave)”, MusE może przekazywać odebrane komunikaty realtime do innego wybranego portu. Oznacza to, że podłączonych może być kilka urządzeń „slave” i MusE będzie do każdego z nich przekazywać uprzednio odebrane komunikaty. Port Port Device Name Nazwa urządzenia s sek. c c k k r r m m t t type typ rid rid rc rc rr rr rm rm rt rt rw rw tid tid tc tc tr tr tm tm tt tt None Brak Tiny Najmniejsze Small Małe Large Duże Large with pre-detect Duże + pre-detect MusE MusE Settings have changed Apply sync settings? Ustawienia zmieniły się. Użyć nowych ustawień synchronizacji? &Apply &Zastosuj &No &Nie &Abort &Anuluj <none> <brak> MusEGui::MidiTrackInfo <unknown> <nieznany> MusEGui::MidiTransformerDialog New Nowy MusEGui::MixdownFileDialog Wave Files (*.wav);;All Files (*) Pliki wave (*.wav);;Wszystkie pliki (*) MusEGui::MusE Failed to start audio! Nie można uruchomić dźwięku! Was not able to start audio, check if jack is running. Nie można uruchomić dźwięku, sprawdź działanie serwera Jack. Timeout waiting for audio to run. Check if jack is running. Upłynął czas oczekiwania na uruchomienie dźwięku. Sprawdź działanie serwera Jack. Und&o Co&fnij Re&do &Przywróć undo last change to song cofnij ostatnią zmianę w utworze redo last undo przywróć ostatnio cofniętą operację Loop Pętla loop between left mark and right mark Zapętl pomiędzy lokatorami Punchin Włącz wcinki (punch In) record starts at left mark Nagrywaj od lewego lokatora. Punchout Wył. wcinki (punch out) record stops at right mark Nagrywaj od prawego lokatora. Start Start rewind to start position Przewiń do początku. Rewind Przewiń do tyłu rewind current position Przewiń kursor do tyłu. Forward Przewiń do przodu move current position Przewijaj kursor do przodu. Stop Stop stop sequencer Zatrzymaj sekwencer. Play Odtwarzanie Restart rec Ponów nagrywanie start sequencer play Uruchom odtwarzanie sekwencera. Record Nagrywanie to record press record and then play Aby nagrywać najpierw naciśnij nagrywanie, potem odtwarzanie. Panic Zatrzymaj wszystkie komunikaty MIDI! send note off to all midi channels Wyślij komunikaty Note Off do wszystkich kanałów. turn on/off metronome Włącz/wyłącz metronom. &New &Nowy Create New Song Twórz nowy utwór. Można też użyć kombinacji Ctrl-N. &Open &Otwórz Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. Kliknij ten przycisk, aby otworzyć <em>nowy utwór</em>.<br>Można też wybrać opcję <b>Otwórz</b> w menu Plik. Open &Recent Otwórz o&statnie &Save &Zapisz Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. Kliknij ten przycisk, aby zapisać edytowany utwór. Trzeba podać nazwę pliku. Można też wybrać opcję Zapisz w menu Plik lub użyć kombinacji Ctrl-S. Save &As Zapisz &jako Import Midifile &Import pliku MIDI Export Midifile &Eksport pliku MIDI Import Part Import klocka Import Audio File Import pliku audio Find unused wave files Znajdź nieużywane pliki audio &Quit &Zakończ Song Info Informacja o utworze Transport Panel Panel Transportu Bigtime Window Duży Zegar Mixer A Mikser A Mixer B Mikser B Cliplist Lista Klipów Marker View Edytor Znaczników Arranger View Główne Okno Fullscreen Tryb pełnoekranowy &Plugins &Wtyczki Edit Instrument Edytuj instrument Input Plugins Wtyczki wejścia Transpose Transponuj Midi Input Transform Przekształcanie wejścia MIDI Midi Input Filter Filtr wejścia MIDI Midi Remote Control Zdalne Sterowanie MIDI Rhythm Generator Generator rytmu Reset Instr. Reset instr. Init Instr. Inicjalizacja instr. Local Off Local Off Bounce to Track Zgrywanie śladu (bounce) Bounce to File Zgrywanie do pliku (bounce) Restart Audio Zrestartuj audio Mixer Automation Automatyka miksera Take Snapshot Zrzut ustawień miksera Clear Automation Data Wyczyść automatykę Cascade Kaskadowo Tile Sąsiadująco In rows W rzędach In columns W kolumnach Global Settings Ustawienia globalne Configure Shortcuts Konfiguracja skrótów Follow Song Podążanie kursora za utworem Don't Follow Song Nie podążaj za utworem Follow Page Kursor się przesuwa Follow Continuous Takty się przesuwają (kursor stoi) &Did you know? &Czy wiesz, że...? MusE: Song: MusE: utwór: Metronome Metronom Midi Sync Synchronizacja MIDI Midi File Import/Export Konfiguracja importu/eksportu plików MIDI Appearance Settings Ustawienia wyglądu Midi Ports / Soft Synth Porty MIDI i syntezatory programowe &Manual &Instrukcja obsługi &MusE Homepage Strona domowa &MusE &Report Bug... &Zgłoś błąd... &About MusE I&nformacje o programie Song Position Pozycja utworu Tempo Tempo Signature Metrum File Buttons Przyciski Menu Plik Undo/Redo Cofnij/Przywróć Transport Panel transportu Cpu load Obciążenie CPU Measured CPU load Aktualne obciążenie CPU No CPU load data Brak danych o obciążeniu CPU &File &Plik &View &Widok &Midi &MIDI &Audio &Audio A&utomation A&utomatyka &Windows &Okna Se&ttings Us&tawienia &Help Pomo&c About &Qt Informacje o &Qt Cannot read template Nie można odczytać pliku szablonu File open error Błąd odczytu pliku File read error Błąd odczytu pliku Unknown File Format: %1 Nieznany format pliku: %1 Set left/right marker for bounce range MusE: Song: %1 MusE: utwór: %1 MusE: load project MusE: ładuj utwór Timeout waiting for audio to run. Check if jack is running or try another driver. Was not able to start audio, check if jack is running or try another driver. Undo last change to song Redo last undo Loop between left mark and right mark Punch in Record starts at left mark Recording starts at left mark Punch out Record stops at right mark Recording stops at right mark Rewind to start position Rewind current position Move current position Stop sequencer Start sequencer play To record press record and then play Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help. Turn on/off metronome Metronome on/off. Press F1 for help. Create new song New from &Template... Create new song from template &Open... &Otwórz... Open song from file Click this button to open an existing song. Save current song Click this button to save the song you are editing. You will be prompted for a file name. Save &As... Zapisz &Jako... &Close Z&amknij Import Midifile... Export Midifile... Import Part... Import Audio File... Find Unused Wave Files... Song Info... Mastertrack Ślad Tempo/Metrum Edit Instrument... Transpose... Midi Input Transform... Midi Input Filter... Midi Remote Control... Reset Instrument Init Instrument Send 'local-off' command to all midi channels. Bounce to File... Global Settings... Appearance... Rewind on Stop Metronome... Midi Sync... Midi File Import/Export... &MusE Homepage... &Did You Know? &About MusE... Snooper (developer tool)... About &Qt... The project has no project sample rate (added 2011). Please enter a rate. The current system rate (%1Hz) is suggested, and cancelling uses it: The project has audio waves, but no project sample rate (added 2011). Please enter a rate. The most common wave rate found is suggested, the project was probably made with it. Cancelling uses the current system rate (%1Hz): Project sample rate MusE: load template MusE: ładuj szablon MusE: Write File failed MusE: zapis pliku nie powiódł się The current Project contains unsaved data Save Current Project? Bieżący utwór zawiera niezapisane dane. Zapisać otwarty utwór? S&kip &Pomiń Load configuration colors &Cancel &Anuluj Color settings will immediately be replaced with any found in the file. Are you sure you want to proceed? &Ok Save configuration colors MusE: Save As MusE: zapisz Jako Nothing to edit Nie ma nic do edytowania MusE: Bounce to Track MusE: zgrywanie (bounce) do śladu No wave tracks found Nie znaleziono śladów audio No audio output tracks found Nie znaleziono śladów wyjścia Select one audio output track, and one target wave track Wybierz jeden ślad wyjścia oraz jeden docelowy ślad audio Select one target wave track Wybierz jeden docelowy ślad audio Select one target wave track, and one audio output track Wybierz jeden docelowy ślad audio oraz jeden ślad wyjścia MusE: Bounce to File MusE: zgrywanie (bounce) do pliku Select one audio output track Wybierz jeden ślad wyjścia audio MusE: Bounce MusE: zgrywanie (bounce) set left/right marker for bounce range ustaw lokatory do zgrania (bounce) danego zakresu The current Project contains unsaved data Load overwrites current Project: Save Current Project? Bieżący projekt zawiera niezapisane dane. Wybrane działanie spowoduje jego nadpisanie. Zapisać bieżący projekt? &Abort &Anuluj This will clear all automation data on all audio tracks! Proceed? Dane automatyki dla wszystkich śladów zostaną wyczyszczone! Kontynuować? This takes an automation snapshot of all controllers on all audio tracks, at the current position. Proceed? Zostanie wykonany automatyczny zrzut wszystkich kontrolerów dla wszystkich śladów audio na bieżącej pozycji. Kontynuować? MusE: Warning MusE: ostrzeżenie The song uses multiple ports but export format 0 (single track) is set. The first track's port will be used. Playback will likely be wrong unless the channels used in one port are different from all other ports. Canceling and setting a different export format would be better. Continue? W utworze użyto wielu portów, jednak ustawiony jest eksport do formatu 0 (tylko jeden ślad). Zostanie więc wykorzystany jedynie port z pierwszego śladu, co sprawi, że utwór najprawdopodobniej będzie odtwarzany niepoprawnie. Lepszym rozwiązaniem będzie anulowanie tej czynności i zmiana formatu eksportowanego pliku. Kontynuować? MusE: Export Midi MusE: eksportuj do pliku MIDI no help found at: nie znaleziono pomocy przy: MusE: Open Help MusE: otwórz pomoc Unable to launch help Nie można uruchomić pomocy For some reason MusE has to launch the default browser on your machine. MusE musi otworzyć domyślną przeglądarkę na Twoim komputerze. MusE: Import Midi MusE: importuj plik MIDI Add midi file to current project? Dodać plik MIDI do bieżącego projektu? &Add to Project &Dodaj do projektu &Replace &Zastąp reading midifile czytanie pliku MIDI Reading midifile failed: niepowodzenie: Import part is only valid for midi and wave tracks! Importowanie klocków jest możliwe jedynie dla śladów MIDI i audio! MusE: load part MusE: importuj klocek No track selected for import Nie wybrano śladu do importu %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. %n klocek z %1 nie mogł zostać zaimportowany. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. %n klocki z %1 nie mogły zostać zaimportowane. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. %n klocków z %1 nie mogło zostać zaimportowanych. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. %n part(s) could not be imported. Likely the selected track is the wrong type. %n klocek nie mogł zostać zaimportowany. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. %n klocki nie mogły zostać zaimportowane. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. %n klocków nie mogło zostać zaimportowanych. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. to import an audio file you have first to selecta wave track aby zaimportować plik dźwiękowy musisz najpierw wybrać ślad audio Import Wavefile Import pliku audio This wave file has a samplerate of %1, as opposed to current setting %2. File will be resampled from %1 to %2 Hz. Do you still want to import it? Częstotliwość próbkowania tego pliku audio to %1, jednak bieżące ustawienie to %2. Plik zostanie przekonwertowany z częstotliwości %1 do %2 Hz. Czy nadal chcesz importować plik? Wave import error Błąd importu pliku audio There are too many wave files of the same base name as imported wave file Can not continue. Jest zbyt dużo plików audio o tej samej nazwie bazowej. Nie można kontynuować. Can't create new wav file in project folder! Nie można utworzyć nowego pliku audio w katalogu projektu! Failed to initialize sample rate converter! Błąd inicjalizacji konwertera częstotliwości próbkowania! Cancel Anuluj Show Docks Toggle display of currently visible dock windows. Display MusE main window in full screen mode. Mastertrack Graphic... Send 'note-off' command to all midi channels. Send initialization messages as found in instrument definition. Take Automation Snapshot Keyboard Shortcuts... Midi Ports / Soft Synths... &Manual (wiki)... File buttons Timeline Recording Sync Synch. Position Pozycja The current project contains unsaved data. Save current project? No valid parts selected The current project contains unsaved data. Load overwrites current project. Save current project? This wave file has a samplerate of %1 Hz, as opposed to current setting %2 Hz. A live, real-time samplerate converter can be used on this file. Or, a copy of the file can be resampled now from %1 Hz to %2 Hz. Do you want to use a converter or resample the file now? Use live converter Resample now Resampling wave file "%1" from %2 to %3 Hz... Konwersja pliku audio „%1” z %2 do %3 Hz... Output has clipped Resampling again and normalizing wave file "%1" Try %2 of %3... Plik wyjściowy jest przesterowany. Ponowna konwersja i normalizacja pliku audio „%1” Próba %2 z %3... &Yes &Tak To import an audio file you have first to select a wave track &No &Nie File version is %1.%2 Current version is %3.%4 Conversions may be applied if file is saved! Plik zapisany jest w wersji %1.%2. Bieżąca wersja to %3.%4. Przy zapisywaniu pliku może być wykonana konwersja! Opening file Otwieranie pliku Do not warn again Nie ostrzegaj ponownie Unable to launch browser Error launching default browser MusEGui::NoteInfo Note Info Informacja o nutach delta/absolute mode Tryb delta/bezwzględny Delta/Absolute mode Start Len Długość Pitch Wysokość Velo on Velo off Velo On Prędk. uderz. wł. Velo Off Prędk. uderz. wył. MusEGui::PartCanvas Cannot copy/move/clone to different Track-Type Nie można skopiować/przenieść/sklonować do śladu innego typu Part: Klocek: C&ut &Wytnij &Copy &Kopiuj s&elect W&ybierz clones klony rename Zmień nazwę color Kolor delete Kasuj split Podziel glue Klej super glue (merge selection) Super klej (scalanie wybranych klocków) de-clone Odklonuj save part to disk Zapisz klocek na dysk wave edit Edycja audio file info Informacja o plikach Part S&elect Rename Zmień nazwę Color Delete Kasuj Split Glue Super glue (merge selection) De-clone Save part to disk... Wave edit... File info... MusE: Save part Automation Automatyka Normalize Normalizacja MusE: save part MusE: zapisz klocek Part name: %1 Files: Nazwa klocka: %1 Pliki: Automation: Automatyka: Part canvas: Use Pencil tool to draw parts. Double-click to create a new MIDI/drum part between the range markers (set with MMB + RMB). Press F1 for help. Remove selected Usuń zaznaczone elementy LMB: Select/Move/Dblclick to edit | CTRL+LMB: Multi select/Move&Copy | CTRL+ALT+LMB: Dblclick to edit in new window | SHIFT+LMB: Select track | MMB: Delete LMB: Draw to resize | MMB: Delete | CTRL+RMB: Trim length LMB: Delete | CTRL+RMB: Trim length LMB: Cut part in two LMB: Merge with following part LMB: Mute selected part LMB: Edit automation events in audio parts %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. %n klocek z %1 nie mogł zostać wklejony. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. %n klocki z %1 nie mogły zostać wklejone. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. %n klocków z %1 nie mogło zostać wklejonych. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. %n part(s) could not be pasted. Likely the selected track is the wrong type. %n klocek nie mogł zostać wklejony. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. %n klocki nie mogły zostać wklejone. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. %n klocków nie mogło zostać wklejonych. Najprawdopodobniej wybrany ślad ma niewłaściwy typ. Cannot paste: multiple tracks selected Nie można wkleić: zaznaczono kilka śladów Cannot paste: no track selected Nie można wkleić: nie zaznaczono żadnych śladów Can only paste to midi/drum track Można wkleić tylko do śladu MIDI lub perkusji Can only paste to wave track Można wkleić tylko do śladu audio Can only paste to midi or wave track Można wkleić tylko do śladu MIDI lub audio Cannot paste: wrong data type Nie można wkleić: nieprawidłowy typ danych MusEGui::PasteDialog %n quarter(s) %n ćwiartka %n ćwiartki %n ćwiartek %1 quarter for floating-point arguments like 1.5 %1 ćwiartki %1 quarters for floating-point arguments like 1.5 %1 ćwiartki MusEGui::PasteEventsDialog %n quarter(s) %n ćwiartka %n ćwiartki %n ćwiartek %1 quarter for floating-point arguments like 1.5 %1 ćwiartki %1 quarters for floating-point arguments like 1.5 %1 ćwiartki MusEGui::Piano Piano: Press key to play or enter events in step record mode (SHIFT for chords). RMB: Set cursor for polyphonic control events. CTRL+Mousewheel to zoom view vertically. Left click: Play Right click: Select key MusEGui::PianoCanvas Pianoroll canvas: Use Pencil tool to draw and edit MIDI events, Pointer tool to select and edit. Press F1 for help. Note: Velocity: Start (bar): Start (time): Length (ticks): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete | CTRL+RMB: Trim length LMB: Resize | CTRL+LMB: Multi select | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length LMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length MusEGui::PianoRoll &Edit &Edycja C&ut &Wytnij &Copy &Kopiuj Copy events in range Kopiuj elementy z zakresu &Paste &Wklej Paste to current part Paste (with dialog) Wklej (pokaż okno dialogowe) Delete &Events Kasuj &elementy &Select &Zaznacz Select &All Zaznacz &wszystko &Deselect All &Odznacz wszystko Invert &Selection Odwróć &zaznaczenie &Inside Loop &Pomiędzy lokatorami &Outside Loop &Poza obrębem lokatorów &Previous Part &Poprzedni klocek &Next Part &Następny klocek Event List... Fu&nctions D&ziałania Quantize Kwantyzuj Modify Note Length Modyfikuj długość nut Modify Velocity Modyfikuj prędkość uderzenia (velocity) Crescendo/Decrescendo Crescendo/decrescendo Transpose Transponuj Erase Events Kasuj zdarzenia Move Notes Przesuń nuty Set Fixed Length Ustaw stałą długość nut Delete Overlaps Kasuj nakładające się nuty Legato Legato &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Play single note Play chords &Plugins &Wtyczki &Display Display menu: View-specific display options. Add controller view Step record Midi input Play events Window &Config &Konfiguracja okna &Event Color &Kolor nut &Blue &Niebieski &Pitch colors K&oloruj po wysokości &Velocity colors Koloruj &po prędkości uderzenia (velocity) Pianoroll tools Narzędzia Pianoroll Step Record Nagrywanie krokowe Midi Input Wejście MIDI Play Events Odgrywaj nuty ctrl kontr. Add Controller View Dodaj widok kontrolera MusEGui::PluginDialog MusE: select plugin MusE: wybierz wtyczkę MusE: Select Plugin Plugin categories. Right-click on tabs to manage. Right-click on plugins to add/remove from a category. Kategorie wtyczek. Kliknij prawym klawiszem na zakładkach, aby zarządzać. Kliknij prawym klawiszem na wtyczkach, aby dodać/usunąć je z kategorii. Type Typ Lib Bibl. Label Etykieta Name Nazwa AI AWej. AO AWyj. CI KWej. CO KWyj. IP IP FB 2B 2B id Ident. Maker Autor Copyright Prawa autorskie Audio inputs Wejścia audio Audio outputs Wyjścia audio Control inputs Wejścia kontrolne Control outputs Wyjścia kontrolne In-place capable Możliwość pracy w trybie „in-place” Requires fixed block size Requires power-of-2 block size ID number Numer identyfikacyjny &Create new group &Delete currently selected group Re&name currently selected group New group Nowa grupa &create new group &Twórz nową grupę &delete currently selected group &Usuń zaznaczoną grupę re&name currently selected group Zmień &nazwę zaznaczonej grupy Ok Akceptuj Cancel Anuluj Stereo Stereo Mono Mono Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. But where the All tab is? Zaznacz typy wtyczek, które powinny być widoczne na liście.<br>Zwróć uwagę, że dopuszczalne jest umieszczanie wtyczek mono na śladach stereofonicznych. W takim przypadku użyte zostaną równolegle dwie.<br>Zwróć też uwagę, że w zakładce grupującej wszystkie wtyczki, mogą znaleźć się też takie, które nie będą przydatne w racku efektów. Associated categories Przyporządkowane kategorie You need to define some categories first. Musisz najpierw zdefiniować jakieś kategorie. new group Nowa grupa Enter the new group name Wpisz nazwę nowej grupy Wine VST Wine VST dssi synth synt. DSSI dssi effect efekt DSSI LV2 synth synt. LV2 LV2 effect efekt LV2 VST synth synt.VST VST effect efekt VST ladspa ladspa MusEGui::PluginGui File Buttons Przyciski Menu Plik Load Preset Załaduj ustawienie Save Preset Zapisz ustawienie Bypass plugin Plugin settings MusE: Save preset Plugin Settings bypass plugin omiń wtyczkę (bypass) MusE: load preset MusE: załaduj ustawienie Error reading preset. Might not be right type for this plugin Błąd podczas czytania ustawień. Ich rodzaj może nie być właściwy dla tej wtyczki MusE: save preset MusE: zapisz ustawienie MusEGui::PluginSettings Global setting: On Off Wył. MusEGui::PopupMenu <More...> %1 <Więcej...> %1 MusEGui::PosToolbar Left marker Left marker position Right marker Right marker position Current position Current position in bars/beats SMPTE position Current position in SMPTE time Show/Hide position in ticks and audio frames Show/Hide additional output fields displaying the current position in ticks and audio frames Current position in ticks Current position in audio frames Current position slider MusEGui::ProjectCreateImpl Select directory Wybierz katalog MusEGui::RasterLabelCombo Snap Przyciąganie MusEGui::RecToolbar Record mode Record mode: Overdub to add new events, Replace to replace overlapping events. Overdub Dogrywanie Replace Zastępowanie Cycle record mode Cycle record mode: Normal to replace range when loop is finished, Mix to add new events, Replace to replace range on first MIDI input. Normal Normalny Mix Miks MusEGui::RouteDialog Normal Normalny Alias 1 Alias 1 Alias 2 Alias 2 Source Urz. źródłowe Destination Urz. docelowe <none> <brak> MusEGui::RoutePopupMenu Channel Kanał Soloing chain Łańcuch trybu solo Audio returns Powroty audio Warning: No input devices! Uwaga: brak urządzeń wejściowych! Channel grouping: Grupowanie kanałów: Mono Mono Stereo Stereo Channels Kanały Midi ports/devices Porty/urządzenia MIDI Omni Omni Show aliases: Pokaż aliasy: First Pierwszy Second Drugi Show names Pokaż nazwy Show first aliases Pokaż pierwsze aliasy Show second aliases Pokaż drugie aliasy Jack ports Porty Jack Connect Połącz Unavailable Niedostępny Open advanced router... Otwórz Konfigurację Połączeń... Warning: No output devices! Output routes: Połączenia wyjściowe: Input routes: Połączenia wejściowe: Open midi config... Otwórz konfigurację MIDI... ALSA devices JACK devices Synth devices <none> <brak> Midi sends Wysyłki MIDI Sources: Źródła: More... Więcej... Tracks Ślady Destinations: Urz. docelowe: Audio sends Wysyłki audio MusEGui::ScoreCanvas Treble Klucz wiolinowy Bass Klucz basowy Grand Staff Akolada Remove staff Usuń pięciolinię Ambiguous part Klocek jest niejednoznaczny There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. Istnieją klocki, do których można dodać nutę, jednak żaden z nich nie odpowiada tym wybranym. Proszę wybrać klocek docelowy klikając na dowolnej należącej do niego nucie, następnie spróbować ponownie. Można też dodać nową pięciolinię zawierającą jedynie klocek docelowy. No part Brak klocka There are no parts you could add the note to. Nie ma klocków, do których można dodać nutę. Tools Narzędzia MusEGui::ScoreEdit Step recording tools Narzędzia nagrywania krokowego Step Record Nagrywanie krokowe Note settings Ustawienia nut Note length: Długość nuty: last ost. Apply to new notes: Zastosuj dla nowych nut: Apply to selected notes: Zastosuj dla wybranych nut: Velocity: Prędk. uderz. (velocity): Off-Velocity: Predk. uderz. (velocity) wył.: Quantisation settings Ustawienia kwantyzacji Step record Quantisation: Kwantyzacja: Pixels per whole: :-/ Rozmiar: &Edit &Edycja C&ut &Wytnij &Copy &Kopiuj Copy events in range Kopiuj elementy z zakresu Quantisation Pixels per whole Note length Velocity Apply to selected notes, or new notes if none is selected Off-Velocity Copy Events in Range &Paste &Wklej Paste (with dialog) Wklej (pokaż okno dialogowe) Delete &Events Kasuj &elementy &Select &Zaznacz Select &All Zaznacz &wszystko &Deselect All &Odznacz wszystko Invert &Selection Odwróć &zaznaczenie &Inside Loop &Pomiędzy lokatorami &Outside Loop Poz&a obrębem lokatorów Fu&nctions D&ziałania &Quantize &Kwantyzuj Change Note &Length Change Note &Velocity Display menu: View-specific display options. Note Head &Colors &Default Set Up &Preamble Display &Key Signature Display &Time Signature Set Score &Name Change note &length Modyfikuj &długość nut Change note &velocity Zmień &prędkość uderzenia (velocity) Crescendo/Decrescendo Crescendo/decrescendo Transpose Transponuj Erase Events Kasuj zdarzenia Move Notes Przesuń nuty Set Fixed Length Ustaw stałą długość nut Delete Overlaps Kasuj nakładające się nuty Legato Legato &Display Window &Config &Konfiguracja okna Note head &colors Kolory &główki nuty &Black &Czarny &Velocity Koloruj &po prędkości uderzenia (velocity) &Part Koloruj po &klocku Set up &preamble Ko&nfiguracja notacji Display &key signature Wyświetlaj &tonację Display &time signature Wyświetlaj &metrum Set Score &name Ustaw nazwę arkusza nutowego Enter the new score title Wpisz nowy tytuł arkusza Error Błąd Changing score title failed: the selected title is not unique Zmiana nazwy arkusza nutowego nie powiodła się: podany tytuł nie jest unikalny MusEGui::ScrollScale next page następna strona previous page poprzednia strona current page number aktualny numer strony MusEGui::ShortcutCaptureDialog Ok Akceptuj Cancel Anuluj Shortcut conflicts with %1 Konflikt skrótu z %1 Undefined Niezdefiniowany MusEGui::ShortcutConfig Save printable text file Zapisz jako plik tekstowy Text files (*.txt);;All files (*) Pliki tekstowe (*.txt);;Wszystkie pliki (*) Error Błąd Error opening file for saving Nie można otworzyć pliku do zapisu Shortcuts for selected category: Skróty dla wybranej kategorii: Legend: Legenda: An error occurred while saving Wystąpił błąd podczas zapisu MusEGui::SigScale signature scale oś metrum Signature scale MusEGui::SigToolbar Time signature at current position Signature: Metrum: MusEGui::SigToolbarWidget time signature at current position Wstaw metrum w aktualnej pozycji kursora Signature: Metrum: MusEGui::Strip Remove track? Usunąć ślad? Remove track Usuń ślad Name Nazwa Enter track name: MusE: bad trackname MusE: błędna nazwa śladu Please choose a unique track name Configuration Prefer knobs, not sliders Show values in controls Prefer midi volume as decibels Monitor on record-arm automatically Visible audio effects Actions Funkcje Change track name Hide strip Ukryj kanał MusEGui::SyncToolbar External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::TList <none> <brak> visible widoczna no clef brak klucza Treble Kl. wiol. Bass Kl. bas. Grand Akolada off wył. <unknown> <nieznany> MusE: bad trackname MusE: błędna nazwa śladu please choose a unique track name Proszę wybrać unikalną nazwę śladu Unused Devices Nieużywane urządzenia Update drummap? Zaktualizować zestaw perkusyjny? Do you want to use same port for all instruments in the drummap? Czy chcesz używać jednego portu MIDI dla wszystkich instrumentów w zestawie perkusyjnym? &Yes &Tak &No &Nie show gui pokaż interfejs użytkownika show native gui pokaż natywny interfejs użytkownika Presets Ustawienia Clear all controller events? Wyczyścić wszystkie zdarzenia kontrolerów? &Ok &Akceptuj &Cancel &Anuluj Change color Zmień kolor Midi control Sterowanie MIDI Assign Przyporządkuj Clear Wyczyść Other Pozostałe clear automation wyczyść automatykę Track list: Use context menu to create tracks. Click track ID to select, CTRL to add, SHIFT for range, dblclick to select all of same type. Press F1 for help. SYNTH IS UNAVAILABLE! Please choose a unique track name Show gui Show native gui Clear automation Treble clef Klucz wiolinowy Bass clef Klucz basowy Grand Staff Akolada Viewable automation Widoczna automatyka Internal Wewnętrzne Synth Syntezator Delete Track Skasuj ślad Delete Selected Tracks Skasuj zaznaczone ślady Track Comment Opis śladu Save track's drumlist Zapisz zestaw perkusyjny śladu Save track's drumlist differences to initial state Unsure Zapisz różnice w zestawie perkusyjnym śladu w stosunku do stanu początkowego Load track's drumlist Ładuj zestaw perkusyjny śladu Reset track's drumlist Przywróć zestaw perkusyjny Reset track's drumlist-ordering Przywróć kolejność zestawu perkusyjnego Copy track's drumlist to all selected tracks Kopiuj zestaw perkusyjny śladu do zaznaczonych śladów Copy track's drumlist's differences to all selected tracks Kopiuj różnice w zestawie perkusyjnym śladu do zaznaczonych śladów Insert Track Dodaj ślad Drum map Zestaw perkusyjny Reset the track's drum map with instrument defaults? Przywrócić zestaw perkusyjny śladu do wartości domyślnych? Reset the track's drum map ordering? Przywrócić kolejność zestawu perkusyjnego śladu? Muse: Load Track's Drum Map MusE: załaduj zestaw perkusyjny Drummap Zestaw perkusyjny This drummap was created with a previous version of MusE, it is being read but the format has changed slightly so some adjustments may be necessary. Ten zestaw perkusyjny został stworzony za pomocą poprzedniej wersji MusE. Został przeczytany, jednak format pliku nieco się zmienił. Pewne zmiany mogą się okazać konieczne. MusE: Store Track's Drum Map MusE: zapisz zestaw perkusyjny Midi MIDI Drum Perkusja New style drum Perkusja nowego typu Do you want to use same port and channel for all instruments in the drummap? Czy chcesz używać jednego portu i jednego kanału dla wszystkich instrumentów w zestawie perkusyjnym? MusEGui::TempoSig Use mastertrack tempo Tempo/Sig Tempo/Metrum Mastertrack tempo at current position, or fixed tempo Time signature at current position MusEGui::TempoToolbar Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Tap Tap this button to set tempo Set the tempo to the time difference of two successive clicks. Times out after two seconds. Tempo: Tempo: TAP TAP MusEGui::TempoToolbarWidget tempo at current position Wstaw tempo w aktualnej pozycji kursora Tempo: Tempo: TAP TAP MusEGui::Toolbar1 Off Wył. Solo Solo Cursor Kursor Snap Przyciąganie MusEGui::TopWin As subwindow Jako podokno Shares tools and menu Współdzieli narzędzia oraz menu Tabbed/Floating Display editor in a tab or in a separate window (preset in Global Settings->Editors). Fullscreen Tryb pełnoekranowy Undo/Redo tools Narzędzia Cofnij/Przywróć Panic Zatrzymaj wszystkie komunikaty MIDI! Metronome Metronom Timeline Transport Panel transportu Recording Sync Synch. Song Position Pozycja utworu Tempo Tempo Signature Metrum Position Pozycja Piano roll Pianoroll List editor Edytor Lista Drum editor Edytor Perkusji Master track editor Edytor graficzny Tempo/Metrum Master track list editor Edytor Lista Tempo/Metrum Wave editor Edytor Audio Clip list Lista Klipów Marker view Edytor Znaczników Score editor Edytor Zapisu Nutowego Arranger Główne Okno <unknown toplevel type> <nieznany typ najwyższego poziomu> MusEGui::TrackComment MusE: Track Comment MusE: opis śladu Track Comment: Opis śladu: MusEGui::Transport Overdub Dogrywanie Replace Zastępowanie Rec Mode Tryb nagrywania Normal Normalny Mix Miks Cycle Rec Zapętl nagrywanie Punch in Punch out Rewind to Start Rewind Przewiń do tyłu Forward Przewiń do przodu Stop Stop Play Odtwarzanie Record Nagrywanie Metronome on/off External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. punchin włącz wcinki (punch In) loop pętla punchout wył. wcinki (punch out) Punch In Włącz wcinki (punch In) Loop Pętla Punch Out Wył. wcinki (punch out) Left Mark Lewy lokator Right Mark Prawy lokator rewind to start Przewijanie do początku Click this button to rewind to start position Wciśnij ten przycisk, aby przewinąć do początku rewind Przewijanie do tyłu Click this button to rewind Wciśnij ten przycisk, aby przewinąć do tyłu forward Przewijanie do przodu Click this button to forward current play position Wciśnij ten przycisk, aby przewinąć do początku stop Stop Click this button to stop playback Wciśnij ten przycisk, aby zatrzymać odtwarzanie play Odtwarzanie Click this button to start playback Kliknij ten przycisk, aby rozpocząć odtwarzanie record Nagrywanie Click this button to enable recording Naciśnij ten przycisk, aby pozwolić na nagrywanie AC Aut. kwant. quantize during record Kwantyzuj podczas nagrywania Click Metronom metronom click on/off Włącz/wyłącz metronom Sync Synch. external sync on/off Synchronizacja do zewnętrznego urządzenia (wł./wył.) Jack Jack Jack transport sync on/off Synchronizacja transportu Jack (wł./wył.) Master Master use master track Używaj śladu tempo/metrum (master track) MusEGui::VisibleTracks Show wave tracks Pokaż ślady audio Show group tracks Pokaż ślady grup Show aux tracks Pokaż szyny (aux) Show input tracks Pokaż ślady wejścia Show output tracks Pokaż ślady wyjścia Show midi tracks Pokaż ślady MIDI Show synth tracks Pokaż ślady syntezatorów Visible track types Pokazywane ślady MusEGui::WaveCanvas Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help. Adjust Wave Offset Dopasuj przesunięcie (offset) audio Wave offset (frames) Przesunięcie audio (ramki) Part creation failed Utworzenie klocka nie powiodło się Left and right position markers must be placed inside the current part. Lewy i prawy znacznik musi być umieszczony wewnątrz bieżącego klocka. Part created Utworzono klocek The selected region has been copied to the clipboard and can be pasted in the arranger. Wybrany zakres został skopiowany do schowka i może być wklejony w Głównym Oknie. MusE - external editor failed MusE - wystąpił błąd w zewnętrznym edytorze MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE nie mógł uruchomić zewnętrznego edytora. Sprawdź, czy w okienku: Ustawienia MusE->Ustawienia globalne-> Audio:Wywołanie zewnętrznego edytora znajduje się poprawne wywołanie edytora. MusE - file size changed MusE - zmienił się rozmiar pliku When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Podczas edycji w zewnętrznym edytorze nie powinno się zmieniać rozmiaru edytowanego pliku, ponieważ musi on pasować do wybranego zakresu. Brakujące fragmenty zostaną wyciszone. Wave event: Converter settings MusEGui::WaveEdit &Edit &Edycja Func&tions D&ziałania &Gain &Wzmocnienie Other Inne &Copy &Kopiuj &Create Part from Region &Utwórz klocek z zakresu C&ut &Wytnij &Paste &Wklej Edit in E&xternal Editor Edytuj w &zewnętrznym edytorze audio Edit in E&xternal Editor... Mute Selection Wycisz zaznaczenie Normalize Selection Normalizuj zaznaczenie Fade In Selection Zgłaśniaj zaznaczenie (fade in) Fade Out Selection Ściszaj zaznaczenie (fade out) Reverse Selection Odwróć zaznaczenie Select Wybierz Select &All Zaznacz &wszystko &Deselect All &Odznacz wszystko &Previous Part &Poprzedni klocek &Next Part &Następny klocek &Display Display menu: View-specific display options. Window &Config &Konfiguracja okna &Event Color &Kolor audio &Part colors &Kolory klocka &Gray &Szary WaveEdit tools Narzędzia edycyjne audio Solo Solo Cursor Kursor OrganGuiBase MusE: Organ Drawbars 16' 16' 4' 4' 2 2/3' 2 2/3' 2' 2' 5 1/3' 5 1/3' 8' 8' Envelope Hi Release Sustain Decay Attack ms cB Envelope Lo O-1 Oscillator Brass Reed Flute Velocity Prędk. uderz. (vel) PasteDialogBase MusE: Paste Parts MusE: wklej klocki Number and raster Liczba i raster insert wstaw times raz(y) raster raster Insert Wstaw Raster Raster ticks ticks Move, Merge, Clone Przesuwanie, scalanie, klonowanie Move everything to the right Przesuń wszystko do prawej Move only affected parts to the right Przesuń do prawej tylko zmodyfikowane klocki Put everything into a single track Umieść wszystko na jednym śladzie Merge with existing parts Scal z istniejącymi klockami Insert as clones (where possible) Wstaw jako klony (jeśli to możliwe) OK Akceptuj Cancel Anuluj PasteEventsDialogBase MusE: Paste Events MusE: wklej elementy Number and raster Liczba i raster insert wstaw times raz(y) raster raster ticks ticks Paste options Opcje wklejania &Always into existing parts &Never into existing parts &Into existing parts if part has not to be expanded by more than Always into existing parts Zawsze do istniejących klocków Never into existing parts Nigdy do istniejących klocków Into existing parts if part has not to be expanded by more than Do istniejących klocków, jeśli klocek nie zostanie rozszerzony bardziej, niż o: Insert Wstaw Raster Raster Put everything into the (selected) part Umieść wszystko w zaznaczonym śladzie Paste midi controller target options Keep e&xisting Erase existin&g Erase WYSIWYG Erase inclusive OK Akceptuj Cancel Anuluj PluginDialogBase Dialog Ports: Porty: Mono Mono Mono + Stereo Stereo Stereo All Wszystko Plugin type: Rodzaj wtyczki: Filter: Filtr: &OK &Akceptuj &Cancel A&nuluj PluginSettings Dialog Okno dialogowe Options Fixed speed Transport affects audio latency Override reported audio latency Reported audio latency override value Revert HiDPI scaling of native GUI Set in Global settings->GUI tweaks Global setting Additional information Follow global setting On Off Wył. ProjectCreate Create Project Twórz projekt Projects folder: Katalog projektu: ... ... Project Name: Nazwa projektu: Project is a Template Projekt jest szablonem Write window state Zapisuj pozycje okien Project song file type: Typ pliku utworu: Project Path to song file: Ścieżka do utworu: &Create project folder (recommended for audio projects) &Utwórz katalog projektu (rekomendowane w przypadku projektów z plikami audio) Song information: Informacja o utworze: QObject Error Błąd Please first select the range for crescendo with the loop markers. Proszę najpierw zaznaczyć zakres crescendo za pomocą lokatorów pętli. Controller ! Kontroler ! Other ! Inny ! Select gui type Wybierz typ GUI Preset actions Działania na ustawieniach Save preset... Zapisz ustawienie... Update list Odśwież listę Saved presets Zapisane ustawienia No presets found Nie znaleziono żadnego ustawienia Enter new preset name Wpisz nową nazwę ustawienia Midi programs Programy MIDI Presets Ustawienia Switch on General Midi Level 1 mode Włącz tryb General MIDI Level 1 Switch on General Midi Level 2 mode Włącz tryb General MIDI Level 2 Switch off General Midi Level 1 or 2 Włącz tryb General MIDI Level 1 lub 2 Switch on Roland GS mode Włącz tryb Roland GS Switch on Yamaha XG mode Włącz tryb Yamaha XG Tracks: Ślady: Midi ports: Porty MIDI: Midi devices: Urządzenia MIDI: Jack: Jack: Jack midi: Jack MIDI: Instruments Current device Others Inne Files Synthesizers Open Otwórz Closed <Properties> <Base Property> <Property> Input routing Warning: No input routes! Click to connect... Output routing Warning: No output routes! Click to connect... TRACK TAKE MusE import error. MusE failed to import the file. Possibly this wasn't a sound file? If it was check the permissions, MusE sometimes requires write access to the file. FX slot QWidget Cannot convert sysex string Nie można przekonwertować łańcucha SysEx Hex String too long (2048 bytes limit) Łańcuch hex za długi (limit 2048 bajtów) new nowy create peakfile for stwórz plik szczytu dla MusE: get file name MusE: pobierz nazwę pliku The directory %1 does not exist. Create it? Katalog %1 nie istnieje. Utworzyć? MusE: create directory MusE: utwórz katalog creating dir failed tworzenie katalogu nie powiodło się File %1 exists. Overwrite? Plik %1 istnieje. Nadpisać? Open File %1 failed: %2 Otwarcie pliku %1 nie powiodło się: %2 MusE: write MusE: zapis: Creating dir failed MusE: Open File MusE: otwórz plik None Brak generic midi Ogólny instrument MIDI No wave events selected. Nie wybrano zdarzeń audio No selection. Ignoring Niczego nie zaznaczono. Pomijam. Instrument-defined Zdefiniowane w instrumencie Generic midi Edit instrument ... Edytuj instrument... Add Dodaj Others Inne Common Controls Typowe kontrolery Velocity Prędk. uderz. (vel) Create peakfile for New Nowy QuantBase MusE: Quantize MusE: kwantyzacja Range Zakres All Events Wszystkie elementy Selected Events Zaznaczone elementy Looped Events Pomiędzy lokatorami Selected Looped Zaznaczone pomiędzy lokatorami Parts Klocki Selected parts or c&urrent part All parts &in editor Values Wartości Strength: Siła: % % Threshold (ticks): Próg (ticks): Quantize Len Kwantyzuj długość Raster Raster Whole Cała nuta (1) Half Półnuta (2) 4th Ćwierćnuta (4) 4th Triplet Triola ćwierćnutowa (4T) 8th Ósemka (8) 8th Triplet Triola ósemkowa (8T) 16th Szesnastka (16) 16th Triplet Triola szesnastkowa (16T) 32th Trzydziestodwójka (32) 32th Triplet Triola trzydziestodwójkowa (32T) Swing: Swing: If the proposed change in tick or length is smaller than threshold, nothing is done. If swing=0, this is normal If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. Jeśli zmiana pozycji bądź długości nut jest mniejsza od progu, nic nie zostanie zrobione. Jeżeli swing będzie ustawiony na 0, rytm się nie zmieni. jeżeli swing będzie ustawiony na 33, powstanie rytm 2:1. jeżeli swing będzie ustawiony na -33, powstanie rytm 1:2. OK Akceptuj Cancel Anuluj RemoveBase MusE: Erase Notes MusE: kasuj zdarzenia Range Zakres All Events Wszystkie elementy Selected Events Zaznaczone elementy Looped Events Pomiędzy lokatorami Selected Looped Zaznaczone pomiędzy lokatorami All Eve&nts Selec&ted Events &Looped Events Selected L&ooped Parts Klocki Selected parts or c&urrent part All parts &in editor Thresholds Progi ticks ticks Velocity Prędk. uderz. (vel) Length Długość <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Jeżeli nic nie zaznaczono, usuwane są wszystkie zdarzenia..</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Jeżeli zaznaczono „Prędk. uderz. (vel)”, usuwane są jedynie nuty z wartością velocity &lt; progu.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Jeżali zaznaczono obie opcje, usuwane są nuty z wartością velocity &lt; progu LUB nuty o długości &lt; od progu.</p></body></html> OK Akceptuj Cancel Anuluj RhythmGenerator RouteDialogBase MusE: Routing MusE: Konfiguracja Połączeń List of available sources. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Lista dostępnych urządzeń źródłowych. Połącz źródła sygnału z urządzeniami docelowymi. W przypadków pozycji z listą kanałów, połączenia można tworzyć w trybie Omni Mode (pozycja tekstowa) bądź w trybie Channel Mode (połączenia pomiędzy poszczególnymi kanałami). Connections View window. Shows all current connections. Thick lines are Omni Routes. Thin lines are Channel Routes. Okno z widokiem połączeń. Pokazuje bieżące połączenia. Grube linie oznaczają połączenia Omni Mode. Cienkie linie oznaczają połączenia Channel Mode. List of available destinations. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Lista dostępnych urządzeń docelowych. Połącz źródła z urządzeniami docelowymi. W przypadków pozycji z listą kanałów, połączenia można tworzyć w trybie Omni Mode (pozycja tekstowa) bądź w trybie Channel Mode (połączenia pomiędzy poszczególnymi kanałami). Itemized list of current connections. Lista bieżących połączeń. Show only selected sources Pokaż tylko wybrane urządzenia źródłowe Show only destination routes for selected source Pokaż tylko docelowe połączenia dla wybranego źródła Show only selected destinations Pokaż tylko wybrane urządzenia docelowe Show only source routes for selected destination Pokaż tylko źródłowe połączenia dla wybranego urządzenia docelowego Show all Midi Ports Pokaż wszystkie porty MIDI Auto adjust column size Automatycznie dopasuj rozmiary kolumn Automatically adjusts the source and destination tree widths when the splitters are adjusted. This also turns on text word wrap, which may cause slower response with larger lists. Automatycznie dopasowuje szerokość drzew urządzeń źródłowych i docelowych przy zmianie rozmiaru ich okien. Opcja ta włącza także zawijanie tekstu, co może spowolnić wyświetlanie przy dłuższych listach. Preferred route name or alias Preferowana nazwa połączenia bądź alias Connect source to destination Łączy urządzenie źródłowe z docelowym Remove selected route Usuwa wybrane połączenie Add Route Dodaj połączenie Source: Źródło: Destination: Odbiornik: Connect Połącz connect source to destination połącz źródło do odbiornika Current Routes Aktualne połączenia Source Źródło Destination Odbiornik Remove Usuń remove selected route Usuń wybrane połączenie RubberbandSettingsBase Rubberband Settings Rubberband support is not enabled Use default settings Presets: Ustawienia: Default Percussion Maximum Stretch: Elastic Precise Window: Standard Standardowe Short Long Smoothing: Off Wył. On Channels: Apart Together Transients: Crisp Mixed Smooth Detector: Compound Percussive Soft Phase: Laminar Independent Formant: Shifted Preserved Pitch: Wysokość dźwięku: High speed High quality High consistency OK Akceptuj Cancel Anuluj SRCResamplerSettingsBase SRC Resampler Settings Use default settings Converter Type: SINC Best quality SINC Medium quality SINC Fastest Zero Order Hold Linear OK Akceptuj Cancel Anuluj SS_PluginChooserBase SimpleDrums - Ladspa Plugin Chooser Simple Drums - lista wtyczek Ladspa Name Nazwa Label Etykieta Inports Porty wej. Outports Porty wyj. Creator Autor &Cancel A&nuluj Alt+C Alt+N &OK &Akceptuj Alt+O Alt+A SS_PluginFront Clear and unload effect Wyczyść i usuń efekt Load effect Ładuj efekt Toggle display of effect parameters Włącz/wyłącz wyświetlanie parametrów efektów Turn effect on/off Włącz/wyłącz efekt SetlenBase MusE: Set Note Length MusE: ustaw stałą długość nut Range Zakres All Events Wszystkie elementy Selected Events Zaznaczone elementy Looped Events Pomiędzy lokatorami Selected Looped Zaznaczone pomiędzy lokatorami Parts Klocki Selected parts or c&urrent part All parts &in editor Value Wartość New length Nowa długość ticks ticks OK Akceptuj Cancel Anuluj ShortcutCaptureDialogBase Enter shortcut sequence Wprowadź skrót - kombinację klawiszy Press keys to enter shortcut sequence! Naciśnij kombinację klawiszy by wprowadzić skrót! Old shortcut: Stary skrót Undefined Niezdefiniowany New shortcut: Nowy skrót OK Akceptuj Cancel Anuluj ShortcutConfigBase Configure Keyboard Shortcuts Konfiguruj skróty klawiaturowe Shortcut Category Kategoria skrótu Description Opis Shortcut Skrót Filter: Filtr: &Reset all &Clear &Wyczyść Alt+C Alt+W &Define Z&definiuj Alt+D Alt+D &Apply Z&astosuj Alt+A Alt+A &Printable file... &Plik tekstowy... Alt+P &Ok A&kceptuj Alt+O Alt+K SimpleDrumsGuiBase DrumSynth 0.1 Audio file assignments. Within parantesis: note number and traditional midi drum name SimplePluginChooserBase SimpleDrums - Ladspa Plugin Chooser Simple Drums - lista wtyczek Ladspa Name Nazwa Label Etykieta Inports Porty wej. Outports Porty wyj. Creator Autor &Cancel Alt+C &OK &Akceptuj Alt+O SimpleSynthGui Mix Miks Chn Kan. Channel routing Połączenia kanałów &Load setup &Załaduj konfigurację &Save setup Z&apisz konfigurację Load sample dialog Okno załaduj sample SnooperDialogBase Snooper Mode Auto-hide Separate parented top levels Update tree Mouse clicks Key presses Flash timer s Reset Filters MusEGui QWidget Properties Właściwości Search: Class name: Object name: About This tool helps developers and designers discover objects. Click on anything anywhere or press a key to highlight an item. It will NOT auto-update when new objects are created. Click 'Update tree'. When open, may slow program slightly. This dialog is NOT shown. Class name::Object name Property Type Typ Value Wartość Event type SongInfo Song Information Informacja o utworze Show on song load Pokaż po załadowaniu utworu &Cancel &Anuluj Alt+C Alt+A &Ok A&kceptuj Alt+O Alt+K SynthConfigBase Soft Synthesizer Syntezator Softowy File Plik Instances Urządzenia Name Nazwa list of available software synthesizers lista wszystkich dostępnych syntezatorów softowych Add Instance Dodaj urządzenie Remove Instance Usuń urządzenie Midi Port Port Midi Type Typ Midi connections Połączenia MIDI Configure midi devices, midi ports, and synthesizers Konfiguruj urządzenia MIDI, porty MIDI i syntezatory Available soft synthesizers Dostępne syntezatory programowe List of available software synthesizers Inst Instr. Version Wersja Description Opis Useable devices Gotowe urządzenia Add: Dodaj: Synth Synt. ALSA JACK Rename Zmień nazwę Remove Usuń Device port assignments Przyporządkowanie urządzeń do portów &Apply &Zastosuj &OK &Akceptuj TransposeBase MusE: Transpose MusE: transponuj Range Zakres All Events Wszystkie elementy Selected Events Zaznaczone elementy Looped Events Pomiędzy lokatorami Selected Looped Zaznaczone pomiędzy lokatorami Parts Klocki Selected parts or c&urrent part All parts &in editor Value Wartość Halftone-steps Liczba półtonów OK Akceptuj Cancel Anuluj UnusedWaveFiles Dialog Okno dialogowe List of unused audio files in current project directory: Lista nieużywanych plików audio w katalogu projektu: Current project Bieżący projekt All .med files in current directory Wszystkie pliki .med w bieżącym katalogu Move files to 'unused' subdir Przenieś pliki do podkatalogu „unused” Cancel Anuluj VAMGui MusE: Load VAM Presets MusE: ładuj ustawienia VAM MusE: Save VAM Presets MusE: zapisz ustawienia VAM VAMGuiBase Virtual Analogue for MusE LFO Freq Waveform Sine Pulse Saw Triangle Filter EnvMod Attack Decay Sustain Release Cutoff Resonance Invert Odwróć KeyTrack Presets Ustawienia Set load preset list save preset list save preset list to a new file delete preset kasuj ustawienie DCO 1 Pitch Detune PWM FM PW DCO 2 On VAM 1.0beta3 Virtual Analog for MusE Released under GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VelocityBase MusE: Modify Velocity MusE: modyfikuj prędkość uderzenia (velocity) Range Zakres All Events Wszystkie elementy Selected Events Zaznaczone elementy Looped Events Pomiędzy lokatorami Parts Klocki Selected parts or c&urrent part All parts &in editor Values Wartości Rate: Częstotliwość: Offset: Offset: % % Selected Looped Zaznaczone pomiędzy lokatorami veloNew = (veloOld * rate) + offset dłNowa = (dłStara * częst.) + offset OK Akceptuj Cancel Anuluj ZitaResamplerSettingsBase Zita Resampler Settings Zita Resampler support is not enabled Use default settings Options: OK Akceptuj Cancel Anuluj file_patterns Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) All Files (*) Wszystkie Pliki (*) Midi (*.mid) Karaoke (*.kar) all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) wszystkie znane pliki (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) med Files (*.med *.med.gz *.med.bz2) Pliki med (*.med *.med.gz *.med.bz2) Uncompressed med Files (*.med) Nieskompresowane pliki med (*.med) gzip compressed med Files (*.med.gz) Pliki med skompresowane gzip (*.med.gz) bzip2 compressed med Files (*.med.bz2) Pliki med skompresowane bzip2 (*.med.bz2) mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) Pliki mid (*.mid *.midi *.kar *.MID *.MIDI *.KAR) (*.jpg *.gif *.png) (*.jpg) (*.gif) (*.png) part Files (*.mpt *.mpt.gz *.mpt.bz2) Wszystkie pliki klocków (*.mpt *.mpt.gz *.mpt.bz2) part Files (*.mpt) Pliki klocków (*.mpt) gzip compressed part Files (*.mpt.gz) Pliki klocków skompresowane gzip (*.mpt.gz) bzip2 compressed part Files (*.mpt.bz2) Pliki klocków skompresowane bzip2 (*.mpt.bz2) Presets (*.pre *.pre.gz *.pre.bz2) Wszystkie ustawienia (*.pre *.pre.gz *.pre.bz2) Presets (*.pre) Ustawienia (*.pre) gzip compressed presets (*.pre.gz) Ustawienia skompresowane gzip (*.pre.gz) bzip2 compressed presets (*.pre.bz2) Ustawienia skompresowane bzip2 (*.pre.bz2) Presets (*.map *.map.gz *.map.bz2) Wszystkie ustawienia (*.map *.map.gz *.map.bz2) Presets (*.map) Ustawienia (*.map) gzip compressed presets (*.map.gz) Ustawienia skompresowane gzip (*.map.gz) bzip2 compressed presets (*.map.bz2) Ustawienia skompresowane bzip2 (*.map.bz2) Wave/Binary (*.wav *.ogg *.flac *.bin) Wave (*.wav *.ogg *.flac) Binary (*.bin) Color configuration files (*.cfc) Qt style sheets (*.qss) Arkusze stylów Qt (*.qss) freeverb FreeVerb Room Size Rozmiar pomieszczenia Damping Wet Level Tap-Reverberator Decay [ms] dB dB Dry Level [dB] Wet Level [dB] Preset: AfterBurn AfterBurn (Long) Ambience Ambience (Thick) Ambience (Thick) - HD Cathedral Cathedral - HD Drum Chamber Garage Garage (Bright) Gymnasium Gymnasium (Bright) Gymnasium (Bright) - HD Hall (Small) Hall (Medium) Hall (Large) Hall (Large) - HD Plate (Small) Plate (Medium) Plate (Large) Plate (Large) - HD Pulse Chamber Pulse Chamber (Reverse) Resonator (96 ms) Resonator (152 ms) Resonator (208 ms) Room (Small) Room (Medium) Room (Large) Room (Large) - HD Slap Chamber Slap Chamber - HD Slap Chamber (Bright) Slap Chamber (Bright) HD Smooth Hall (Small) Smooth Hall (Medium) Smooth Hall (Large) Smooth Hall (Large) - HD Vocal Plate Vocal Plate - HD Warble Chamber Warehoouse Warehouse - HD Comb Filters Allpass Filters Bandpass Filters Enhanced Stereo main File to open Alsa midi only (using dummy audio driver) Use RtAudio driver Use JAckAudio driver to connect to Jack audio server Do not try to auto-start the Jack audio server Do not auto-populate midi ports with midi devices found, at startup Force inclusion of ALSA midi even if using Jack Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) Force midi real time priority to n (default: audio driver prio -1) Force plugin cache re-creation. (Automatic if any plugin path directories changed.) Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care. Don't load LADSPA plugins Don't load MESS plugins Don't load VST plugins Don't load LinuxVST plugins Don't load DSSI plugins Don't load LV2 plugins Don't use LASH Force locale to the given language/country code (xx = Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins. Debug mode: no threads, no RT Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively! Debug mode: trace midi Input Debug mode: trace midi Output Debug mode: trace sync Enable Python control support Pyro nameserver host name Pyro nameserver host port Pyro daemon host name Pyro daemon host port Pyro communication timeout in seconds midiWarnInitPendingBase Instrument initialization Inicjalizacja instrumentów MusE should now send some Instrument Initialization Sequences. The sequences (usually System Exclusive messages) are defined by the selected instruments in the Settings -> Midi Ports dialog, such as the GM (default), GS, or XG instruments. Typically you should answer yes here. You can always do it manually from the Midi menu. Continue? MusE powinien w tej chwili wysłać pewne sekwencje inicjalizacji instrumentów. Sekwencje te (najczęściej komunikaty System Exclusive) przyporządkowane są na podstawie wybranego typu instrumentu w oknie: Ustawienia MusE -> Porty MIDI i syntezatory programowe Np. instrumenty GM (domyślne), GS lub XG. Zazwyczaj należy potwierdzić tę operację. W każdej chwili można wysłać sekwencje ręcznie korzystając z menu MIDI. Kontynuować? Don't ask me again Nie pytaj ponownie shortcuts Transport: Start playback from current location Transport: odtwarzaj od bieżącej pozycji Transport: Toggle metronome Transport: włącz/wyłącz metronom Transport: Stop Playback Transport: zatrzymaj odtwarzanie Transport: Goto Start Transport: wróć do początku Transport: Play, Stop, Rewind Transport: odtwarzaj, zatrzymaj, przewiń Transport: Goto left marker Transport: idź do lewego znacznika Transport: Goto right marker Transport: idź do prawego znacznika Transport: Toggle Loop section Transport: włącz/wyłącz pętlę Transport: Toggle Record Transport: włącz/wyłącz nagrywanie Transport: Clear all rec enabled tracks Transport: wyczyść tryb nagrywania dla wszystkich śladów Toggle fullscreen Włącz/wyłącz tryb pełnoekranowy Toggle tabbed/floating editor window Toggle docks display Show Arranger window Edit: Copy Edycja: kopiuj Edit: Copy in range Edycja: kopiuj zakres Edit: Undo Edycja: cofnij Edit: Redo Edycja: przywróć Edit: Cut Edycja: wytnij Edit: Paste Edycja: wklej Edit: Paste to selected track Edycja: wklej do zaznaczonego śladu Edit: Paste clone Edycja: wklej klon Edit: Paste clone to selected track Edycja: wklej klon do zaznaczonego śladu Edit: Paste (with dialog) Edycja: wklej (pokaż okno dialogowe) Edit: Delete Edycja: kasuj File: New project Plik: nowy File: Open from disk Plik: otwórz File: Save project Plik: zapisz File: Open recent file Plik: otwórz ostatnie File: Save as Plik: zapisz jako File: Load template Plik: ładuj szablon File: Import midi file Plik: import pliku MIDI File: Export midi file Plik: eksport pliku MIDI File: Import midi part Plik: import klocka File: Import audio file Plik: import pliku audio File: Quit MusE Plik: zakończ Edit: Select parts on track Edycja: wybierz wszystkie klocki na śladzie Open pianoroll Otwórz Pianoroll Open drumeditor Otwórz Edytor Perkusji Open listeditor Otwórz Edytor Lista Open waveeditor Otwórz Edytor Audio Open graphical mastertrack editor Otwórz Edytor Graficzny Tempo/Metrum Open list mastertrack editor Otwórz Edytor Lista Tempo/Metrum Open midi transformer Otwórz Przekształcanie wejścia MIDI Open track Synth GUI Open pianoroll in new window Open drumeditor in new window Open listeditor in new window Open waveeditor in new window Add midi track Dodaj ślad MIDI Add drum track Dodaj ślad perkusyjny Add new style drum track Dodaj ślad perkusyjny nowego typu Add wave track Dodaj ślad audio Add audio output Dodaj wyjście audio Add audio group Dodaj grupę audio Add audio input Dodaj wejście audio Add audio aux Dodaj szynę (aux) audio Insert midi track Insert drum track Insert wave track Insert audio output Insert audio group Insert audio input Insert audio aux Structure: Global cut Globalne: globalne wycięcie Structure: Global insert Globalne: globalne wstawienie Structure: Global split Globalne: globalne podzielenie Structure: Cut events Not implemented Globalne: wytnij elementy View: Open mixer #1 window Widok: mikser A View: Open mixer #2 window Widok: mikser B View: Toggle transport window Widok: włącz/wyłącz Panel Transportu View: Toggle bigtime window Widok: włącz/wyłącz Duży Zegar View: Open marker window Widok: otwórz Edytor Znaczników View: Toggle mixer strip display Settings: Follow song by page Ustawienia: kursor się przesuwa Settings: Follow song off Ustawienia: nie podążaj za utworem Settings: Follow song continuous Ustawienia: takty się przesuwają (kursor stoi) Settings: Global configuration Ustawienia: ustawienia globalne Settings: Configure shortcuts Ustawienia: konfiguruj skróty klawiaturowe Transport: Goto End Transport: Restart recording Transport: ponów nagrywanie Transport: Restart recording (multi take) Transport: ponów nagrywanie (tryb multi take) Transport: Arm current track Toggle rewind on stop Edit: Paste to current part File: New project from template Edit: Rename part Settings: Configure metronome Ustawienia: metronom Settings: Midi sync configuration Ustawienia: synchronizacja MIDI Settings: Midi file import/export configuration Ustawienia: konfiguracja importu/eksportu plików MIDI Settings: Appearance settings Ustawienia: ustawienia wyglądu Settings: Midi ports / Soft Synth Ustawienia: porty MIDI i syntezatory programowe Settings: Audio subsystem configuration Not implemented? Ustawienia: konfiguracja podsystemu audio Midi: Edit midi instruments MIDI: Edytor Instrumentów Midi: Open midi input transform MIDI: Otwórz przekształcanie wejścia MIDI Midi: Open midi input filter MIDI: otwórz filtr wejścia MIDI Midi: Midi input transpose MIDI: wtyczka wejścia MIDI: transponuj Midi: Midi remote control MIDI: wtyczka wejścia MIDI: zdalne sterowanie Midi: Random rhythm generator MIDI: generator losowego rytmu Midi: Reset midi MIDI: reset instr. Midi: Init midi MIDI: inicjalizacja instr. Midi: Midi local off MIDI: local off Audio: Bounce audio to track Audio: zgrywanie (bounce) do śladu Audio: Bounce audio to file Audio: zgrywanie (bounce) do pliku Audio: Restart audio Audio: zrestartuj audio Automation: Mixer automation Automatyka: automatyka miksera Automation: Take mixer snapshot Automatyka: zrzut ustawień miksera Automation: Clear mixer automation Automatyka: wyczyść automatykę Help: Open Manual Pomoc: instrukcja obsługi Help: Toggle whatsthis mode Pomoc: włącz/wyłącz tryb „Co to jest?” Edit: Edit selected part Edycja: edytuj zaznaczony klocek Edit: Select nearest part on track above Edycja: zaznacz najbliższy klocek na górnym śladzie Edit: Add nearest part on track above Edycja: dodaj do zaznaczenia najbliższy klocek na górnym śladzie Edit: Select nearest part on track below Edycja: zaznacz najbliższy klocek na dolnym śladzie Edit: Add nearest part on track below Edycja: dodaj do zaznaczenia najbliższy klocek na dolnym śladzie Edit: Insert empty measure Edycja: wstaw pusty takt Edit: Duplicate track Edycja: kopiuj ślad Select track above Zaznacz górny ślad Select track below Zaznacz dolny ślad Edit selected track name Edytuj nazwę wybranego śladu Mute all currently selected tracks Solo all currently selected tracks Midi: Transpose MIDI: transponuj Edit: Select all Edycja: zaznacz wszystko Edit: Select none Edycja: odznacz wszystko Edit: Invert Selection Edycja: odwróć zaznaczenie Edit: Select events/parts inside locators Edycja: zaznacz elementy/klocki pomiędzy lokatorami Edit: Select events/parts outside locators Edycja: zaznacz elementy/klocki poza lokatorami Edit: Select previous part Edycja: zaznacz poprzedni klocek Edit: Select next part Edycja: zaznacz następny klocek Edit: Select nearest part/event to the left or move cursor Edycja: zaznacz najbliższy element/klocek na lewo bądź przesuń kursor Edit: Add nearest part/event to the left to selection Edycja: dodaj do zaznaczenia najbliższy element/klocek na lewo Edit: Select nearest part/event to the right or move cursor Edycja: zaznacz najbliższy element/klocek na prawo bądź przesuń kursor Edit: Add nearest part/event to the right to selection Edycja: dodaj do zaznaczenia najbliższy element/klocek na prawo Edit: Set locators to selection Edycja: ustaw lokatory w miejscu zaznaczenia Edit: Increase pitch Edycja:zwiększ wysokość dźwięku Edit: Decrease pitch Edycja:zmniejsz wysokość dźwięku Edit: Increase event position Edycja: przesuń element w prawo Edit: Decrease event position Edycja: przesuń element w lewo View: Zoom in Widok: powiększ View: Zoom out Widok: pomniejsz View: Goto Current Position Widok: idź do bieżącej pozycji View: Scroll left Widok: przesuń w lewo Transport: Step record Transport: nagrywanie krokowe Transport: Midi input Transport: wejście MIDI Transport: Play events Transport: odgrywaj nuty Edit: Increase velocity Edycja: zwiększ prędkość uderzenia (velocity) Edit: Decrease velocity Edycja: zmniejsz prędkość uderzenia (velocity) Edit: Set Fixed Length on Midi Events Edycja: ustaw stałą długość nut Quantize Kwantyzuj Modify Note Length Modyfikuj długość nut Modify Velocity Modyfikuj prędkość uderzenia (velocity) Edit: Crescendo Edycja: crescendo/decrescendo Edit: Thin Out Not impemented? Edycja: thin out Edit: Erase Event Edycja: kasuj zdarzenie Edit: Delete Overlaps Edycja: kasuj elementy nakładające się Edit: Note Shift Edycja: przesuń nuty Edit: Move Clock Edycja: przesuń zegar Edit: Copy Measure Edycja: kopiuj takt Edit: Erase Measure Not implemented, ambiguous Edycja: wyczyść takt Edit: Delete Measure Not implemented, ambiguous Edycja: kasuj takt Edit: Create Measure Edycja: usuń takt Edit: Change Event Color Edycja: zmień kolor elementu Move: Move to selected note Przesuwanie: przesuń kursor do wybranej nuty Tool: Pointer Narzędzie: strzałka Tool: Pencil Narzędzie: ołówek Tool: Eraser Narzędzie: gumka Tool: Pan Narzędzie: przesuwanie Tool: Zoom Narzędzie: powiększanie Tool: Line Draw Narzędzie: wykres Tool: Cursor Narzędzie: kursor Add note velocity 1 Dodaj nutę z poziomem velocity 1 Add note velocity 2 Dodaj nutę z poziomem velocity 2 Add note velocity 3 Dodaj nutę z poziomem velocity 3 Add note velocity 4 Dodaj nutę z poziomem velocity 4 Cursor step size: larger Rozmiar kroku kursora: większy Cursor step size: smaller Rozmiar kroku kursora: mniejszy Instrument/Cursor up Instrument/kursor do góry Instrument/Cursor down Instrument/kursor do dołu Tool: Range Narzędzie: zakres Tool: Stretch Tool: Sample rate Tool: Scissor Narzędzie: nożyczki Tool: Glue Narzędzie: klej Tool: Mute Narzędzie: wycisz Transport: Increase current position Transport: przesuń kursor w prawo Transport: Decrease current position Transport: przesuń kursor w lewo Transport: Increase current position, no snap Transport: przesuń kursor w prawo (bez przyciągania) Transport: Decrease current position, no snap Transport: przesuń kursor w lewo (bez przyciągania) Quantize: Set quantize to off Quantize: Set quantize to 1/1 note Kwantyzacja: ustaw kwantyzację do 1/1 całej nuty Quantize: Set quantize to 1/2 note Kwantyzacja: ustaw kwantyzację do 1/2 całej nuty Quantize: Set quantize to 1/4 note Kwantyzacja: ustaw kwantyzację do 1/4 całej nuty Quantize: Set quantize to 1/8 note Kwantyzacja: ustaw kwantyzację do 1/8 całej nuty Quantize: Set quantize to 1/16 note Kwantyzacja: ustaw kwantyzację do 1/16 całej nuty Quantize: Set quantize to 1/32 note Kwantyzacja: ustaw kwantyzację do 1/32 całej nuty Quantize: Set quantize to 1/64 note Kwantyzacja: ustaw kwantyzację do 1/64 całej nuty Quantize: Toggle triol quantization Kwantyzacja: ustaw kwantyzację do trioli Quantize: Toggle punctuation quantization Kwantyzacja: ustaw kwantyzację do kropek Quantize: Toggle punctuation quantization (2) Kwantyzacja: ustaw kwantyzację do kropek (2) Edit: Insert at location Edycja: wstaw odstęp w pozycji kursora Edit: Increase length Edycja:zwiększ długość elementu Edit: Decrease length Edycja:zmniejsz długość elementu Insert Note Wstaw nutę Insert SysEx Wstaw SysEx Insert Ctrl Wstaw kontroler Insert Meta Wstaw meta Select strip to the left Select strip to the right Mixer strip volume down Mixer strip volume up Mixer strip pan left Mixer strip pan right Mixer strip volume down page Mixer strip volume up page Mixer strip pan left page Mixer strip pan right page Insert Channel Aftertouch Wstaw zmianę docisku kanałowego (Channel Aftertouch) Insert Key Aftertouch Wstaw zmianę docisku klawisza (Key Aftertouch) Insert Tempo Wstaw tempo Insert Signature Wstaw metrum Change Event Position Zmień pozycję elementu Edit Event Value Edytuj wartość elementu Insert Key Wstaw tonację Settings: Midi ports / Soft synths Goto Next Marker Idź do następnego znacznika Goto Prev Marker Idź do poprzedniego znacznika Normalize Normalizacja warnBadTimingBase Bad timing Zły timing Message here Don't warn me again Nie ostrzegaj ponownie muse-4.0.0/src/share/locale/muse_ru.ts000066400000000000000000023606101404105311000176400ustar00rootroot00000000000000 @default Add Midi Track Добавить Midi Трек Add Drum Track Добавить трек ударных Add Wave Track Добавить Аудио-трек Add Audio Output Добавить аудиовыход Add Audio Group Добавить аудиогруппу Add Audio Input Добавить аудиовход Add Aux Send Добавить Aux посыл Select project directory Выбрать папку проекта Add Synth Добавить синтезатор Route Маршрут dest назн name="%1"/ имя="%1"/ Warning: No output devices! Предупреждение: Нет устройства выхода! Open midi config... Открыть midi конфиг... Empty ports Свободные порты <none> <ничего> channelMask="%1" МаскаКанала="%1" Bad timing Плохой тайминг Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. Also please check console output for any further error messages. Частота равна %1гц, что ниже рекомендованного минимума:500гц! это может привести к слышимым проблемам при воспроизведении MIDI. Смотрите FAQ на http://muse-sequencer.org для исправления. Также проверьте вывод консоли для выявления сообщений об ошибках. Add Old Style Drum Track Добавить трек ударных в старом стиле Add New Style Drum Track Добавить трек ударных в новом стиле channel="%1" канал="%1" AboutBox AboutBox Дополнительная информация Version 2 pre-alpha Версия 2.0~rc2 (svn revision: 0) (C) Copyright 1999-2010 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2010 Werner Schweer и другие. На http://www.muse-sequencer.org смотрите новые версии и дополнительную информацию. Опубликовано на условиях GNU Public License &Keep On Rocking! &Продолжим рок! Alt+K Alt+K Version 2 Версия-2 (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2010 Werner Schweer и другие. На http://www.muse-sequencer.org смотрите новые версии и дополнительную информацию. Опубликовано на условиях GNU Public License {1999-2012 ?} (C) Copyright 1999-2014 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2010 Werner Schweer и другие. На http://www.muse-sequencer.org смотрите новые версии и дополнительную информацию. Опубликовано на условиях GNU Public License {1999-2012 ?} {1999-2014 ?} (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2010 Werner Schweer и другие. На http://www.muse-sequencer.org смотрите новые версии и дополнительную информацию. Опубликовано на условиях GNU Public License {1999-2012 ?} {1999-2014 ?} {1999-2015 ?} MusE MusE Internals System information (useful for debugging): (C) Copyright 1999-2020 the MusE development team. See https://muse-sequencer.github.io for new versions and more information. Published under the GNU Public License. AppearanceDialogBase Apply Применить Ok Ok Cancel Отмена Arranger Компоновщик MusE: Appearance settings MusE: Настройки внешнего вида Parts Партии show names показывать названия show events показывать события show Cakewalk Style показывать в стиле Cakewalk y-stretch сжатие по вертикали Events События note on нажатие ноты poly pressure полифоническое нажатие controller контроллер aftertouch посленажатие pitch bend глиссандо program change смена программы special специальные Background picture Фоновое изображение show snap grid показывать сетку привязки Colors Цвета Items Разделы Color name: Название цвета: Global opacity Общая непрозрачность Style/Fonts Стиль/Шрифты QT Theme Тема QT Windows Окна MusE MusE Metal Металл Norwegian Wood Норвежское дерево Platinum Платина CDE КДЕ Motif Motif Motif Plus Motif Plus Fonts Шрифты Family Семейство Size Размер Font 1 Шрифт 1 Font 2 Шрифт 2 Font 3 Шрифт 3 Font 0 Шрифт 0 Bold Жирный Italic Курсив ... ... Font 4 Шрифт 4 Font 5 Шрифт 5 Palette Палитра add добавить remove удалить add to palette Добавить в палитру B B S S H H V V G G R R clear очистить Style Sheet: Таблица Стилей: Font 6 Шрифт 6 May require restarting MusE for best results Может потребоваться перезапуск MusE Themes Темы MusE color scheme Цветовая схема MusE current settings текущие настройки Change Изменить Show names Show e&vents Show Ca&kewalk style Wave Drawing in Parts O&nly outline Add Добавить Remove Удалить Clear Очистить Show snap grid Choose color... Add to palette Save all... Load all... Size (pt) At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. Icon size Custom cursor size Distribute current system font family to all other fonts System Font sizes for fonts 1-6 are determined at every program start, to optimally match the currently used system font. Disable if you want to set fixed sizes in the font settings below. Default scaling factors: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% Calculate and apply relative font sizes dynamically at program start Maximum aliased size, 0 = no alias Insert Вставить Fill in optimal font sizes in relation to the system font RMS/PEAK (&traditional) Current theme If a user style sheet for one of the MusE color schemes exists and is selected, it will be merged with the default style sheet for this scheme. The user style sheet takes precedence and overwrites identical settings in the default style sheet. Cascade default and user theme style sheets Show icons in menus Use native standard dialogs Help ArrangerColumnsBase Configure arranger columns Настроить колонки компоновщика Columns: Колонки: Name: Имя: Controller type: Тип контроллера: Midi controller type Тип midi-контроллера Control7 Контрл7 Control14 Контрл14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Высота тона Program Программа H-Ctrl H-Ктрл Midi controller number high byte Старший байт midi-контроллера L-Ctrl L-Ктрл Midi controller number low byte Младший байт Midi контроллера * wild card * универсальный шаблон affect CCs at влияет на контроллеры begin of song начало сонга current position текущая позиция &Add &Добавить &Delete &Удалить Done Готово Affect CCs at AudioConverterSettingsBase Audio Converter Settings Available audio converters Offline settings... Realtime settings... Gui settings... Use default preferences (see Global Settings) Preferences When only samplerate conversion is required, use: When time stretching or pitch shifting is required, use: OK Cancel Отмена Awl::MidiVolEntry off выкл db дб Awl::VolEntry off выкл ChooseSysexBase Dialog Диалог Hex: Comment: Комментарий: &OK &Cancel ClipListEditorBase MusE: ClipList MusE: Список Клипов Name Имя Refs Ссыл (Refs) Len Дл Data Данные Clip Properties Свойства Клипа Pos: Поз: Len: Дл: Samplerate Частота дискретизации Status CommentBase Form1 Форма1 Track Comment Комментарий к треку Track 1 Трек 1 ConfigMidiFileBase &OK &ОК &Cancel &Отмена 0 (single track) 0 (один трек) 1 (multiple tracks) 1 (несколько треков) Format: Формат: 96 96 192 192 384 384 Division: Разрешение (тиков на четверть): Copyright: Copyright: MusE: Config Midi File Import/Export MusE: Настроить Импорт/Экспорт Midi-файла Import: Импорт: Split tracks into &parts Разделять треки на &партии Alt+P Alt+P Split tracks into parts, or one single part Разделить треки на партии, или все в одну партию Export: Экспорт: Enable extended smf format (currently not implemented) Разрешить расширенный smf формат (в настоящее время не осуществлено) Use &2-byte time signatures instead of standard 4 Использовать &2-байтные размеры в такте вместо стандартных 4 Alt+2 Alt+2 Note: Format 0 uses the FIRST midi track's name/comment in the arranger Примеч: Формат 0 использует имя/коментарий ПЕРВОГО midi трека в компоновщике Save space by replacing note-offs with &zero velocity note-ons Сохранить место, заменяя note-offs на &note-ons с нулевой скоростью нажатия Alt+Z Alt+Z Use new-style drum tracks Использовать треки ударных в новом стиле Use old-style drum tracks Использовать треки ударных в старом стиле Default instrument: Device Name metas trump Port metas if both exist Instrument Name metas trump Mode sysexes if both exist Mode sysexes Instrument name metas Port metas Device name metas Export a Port/Device meta for format 0 Import Export Running Status saves space by not repeating event status bytes. If this is off every event will have a status byte, increasing file size. Use Running &Status To turn a note off some devices can use note-offs or zero-velocity note-ons or both. When used with Running Status this setting saves space. It is safe to leave this off. Replace note-offs with &zero velocity note-ons If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum note controllers. If disabled, drum maps are ignored during export. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. See "What's This ?" for more info. If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum controllers. Any Port overrides cause a separate track to be created (requires format 1). Any Channel overrides will still appear in the same track in the exported file, but upon re-import into an app such as MusE, MusE will treat that as a new track and create one. If disabled, drum maps are ignored. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. Upon re-importing into another app, it is up to the other app to redirect the particular drum ports, channels and notes to make the song sound like it was intended. Drum map overrides are unique to MusE. If re-importing into MusE, for faithful reproduction disable this setting and apply a drum map and some overrides later. If re-importing or opening in another midi player, enable this setting so that the player can have a chance at faithful reproduction. Some user adjustment may be required (choice of instruments, ports, etc). Apply drum map Port, Channel, ANote overrides If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. See "What's This ?" for more info. If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. The extra track is automatically created. In this mode all events on a track in the midi file are on the same channel. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. In this mode the track events can be on different channels. But if the file is re-imported into an app such as MusE, MusE will automatically create the extra track. Drum map Channel overrides go to a separate track Export instrument or mode: Export port or device metas: CopyOnWriteDialogBase Copy Wave Files Some sound files will be copied to the Project Directory, either because they are not writable or because more than one independent Wave Event shares them. (If you prefer instead that the Wave Events are inter-dependent, try using Clone Parts.) Multiple copies will be made in some cases. If no Project has been created yet, you will be asked to, giving another chance to cancel. These files will be copied to the Project Directory: CrescendoBase MusE: Crescendo/Decrescendo MusE: Крещендо/Диминуэндо Range Диапазон Looped Events События в цикле Selected Looped Выбранные в цикле Values Значения Start velocity Начальная скор. нажатия % % End velocity Конечная скор. нажатия Absolute Абсолютная Relative Относительная OK OK Cancel Отмена &Looped Events Parts Партии Selected parts or c&urrent part All parts &in editor &Absolute Rela&tive DeicsOnzeGui Delete preset Удалить пресет Do you really want to delete %1 ? Вы действительно хотите удалить %1 ? &Yes &Да &No &Нет Save configuration Сохранить конфигурацию Critical Error Критическая ошибка Cannot open file %1 Не могу открыть файл %1 Parsing error for file %1 Синтаксическая ошибка в файле %1 Load category dialog Загрузить диалог категорий Load set dialog Загрузить диалог настройки Save set dialog Сохранить диалог настройки New category Новая категория Delete category Удалить категорию Load category Загрузить категорию Save category Сохранить категорию Load set Загрузить установку Save set Сохранить установку Delete set Удалить установку New subcategory Новая подкатегория Delete subcategory Удалить подкатегорию Load subcategory Загрузить подкатегорию Save subcategory Сохранить подкатегорию New preset Новый пресет Load preset Загрузить пресет Save preset Сохранить пресет No more category supported Большее количество категорий не поддерживается You can not add more categories Вы не можете больше добавлять категории No category selected Категория не выбрана You must first select a category. Сначала нужно выбрать категорию. Replace or add Заменить или добавить %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 должен изменить номер H-банка %2, но этот слот уже занят. Хотите заменить его или добавить в следующий слот ? &Replace &Заменить &Add &Добавить Download error Ошибка загрузки There is no more free category slot. Больше нет свободных слотов для категорий. Save category dialog Сохранить диалог категории No more subcategory supported Большее количество подкатегорий не поддерживается You can not add more subcategories Нельзя добавить больше подкатегорий No subcategory selected Подкатегория не выбрана You must first select a subcategory. Сначала нужно выбратьподкатегорию. Load subcategory dialog Загрузить диалог подкатегории %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 должен повлиять на номер L-банка %2, но этот слот уже занят. Хотите заменить его или добавить в следующий слот ? There is no more free subcategory slot. Больше нет свободных слотов для подкатегорий. Save subcategory dialog Сохранть диалог подкатегории No more preset supported Большее количество пресетов не поддерживается You can not add more presets Вы не можете больше добавлять пресеты No preset selected Пресет не выбран You must first select a preset. Сначала выберите пресет. Load preset dialog Загрузить диалог пресета %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 должен повлиять на номер программы %2, но этот слот уже занят. Хотите заменить её или добавить в следующий слот ? There is no more free preset slot. Больше нет свободных слотов для пресета. Save preset dialog Сохранить диалог пресета Browse set dialog Просмотр диалога установки Browse image dialog Просмотр диалога изображения DeicsOnzeGuiBase DeicsOnze DeicsOnze Subcategory Подкатегория NONE НИЧЕГО Category Категория Preset Пресет Bank numerous Несколько банков Prog Прогр Program numerous Несколько программ &Global &Общий Coarse Ratio Грубая пропорция POLY ПОЛИ MONO МОНО Pitch Bend Range Диапозон изменения высоты тона LFO LFO PMS PMS PMD PMD AMD AMD Speed Скорость Delay Задержка AMS AMS LFO Waveform Форма волны LFO LFO Sync Синхр. LFO Amplitude Modulation Sensitivity Чувствительность Амплитудной Модуляции Pitch Modulation Sensitivity Чувствительность Частотной Модуляции Pitch Modulation Depth Глубина Частотной Модуляции LFO Speed Скорость LFO LFO Delay Задержка LFO Transpose Транспонировать Modulation Matrix Матрица модуляции Volume Громкость Detune Расстройка &Preset &Пресет INITVOICE INITVOICE LBank LБанк HBank HБанк DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Опубликован под лицензией GPL. Pitch Envelope Развертка высоты тона PL3 PL3 PL2 PL2 PL1 PR1 PR1 PR2 PR3 PR3 Pitch modulation depth Глубина модуляции Pitch modulation sensitivity Чувствительность модуляции LFO delay Задержка LFO LFO speed Скорость LFO Amplitude modulation depth Amplitude modulation sensitivity <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> Op4 Feedback Feedback level of the operator 4 Op &1 Scaling 1 LS1 RS1 Rate Scaling Attack Rate of the operator 1 Level Scaling Amplitude Envelope 1 RR1 D1R1 D1L1 D2R1 Release Rate 2° Decay Rate 1° Decay Level 1° Decay Rate Attack Rate AR1 Detune, OSCWave, EGShift 1 DET1 EG Shift 96dB 48dB 24dB 12dB Wave form Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Sensitivity 1 KVS1 Amplitude Modulation Enable AME1 Keyboard Velocity Sensitivity Key Velocity Sensitivity EBS1 EG Bias Sensitivity Eg Bias Sensitivity Frequency 1 Coarse 1 Fine 1 Fine Ratio Freq 1 Fixed Frequency Toggle Fix Frequency FIX OUT 1 Output Volume Op &2 Amplitude Envelope 2 D1R2 D1L2 D2R2 RR2 AR2 Frequency 2 Coarse 2 Fine 2 Freq 2 Scaling 2 LS2 RS2 OUT 2 Detune, OSCWave, EGShift 2 DET2 Sensitivity 2 EBS2 KVS2 AME2 Op &3 Amplitude Envelope 3 D1R3 D1L3 D2R3 RR3 AR3 Scaling 3 LS3 RS3 OUT 3 Frequency 3 Coarse 3 Fine 3 Freq 3 Detune, OSCWave, EGShift 3 DET3 Sensitivity 3 EBS3 KVS3 AME3 Op &4 amplitude Envelope 4 AR4 D1R4 D1L4 D2R4 RR4 Frequency 4 Coarse 4 Fine 4 Freq 4 Scaling 4 LS4 RS4 OUT 4 Detune, OSCWave, EGShift 4 DET4 Sensitivity 4 EBS4 KVS4 AME4 &Func Delay Pan Depth Delay Pan LFO Freq Delay Ch Send Level Channel Chorus Delay Feedback Delay On/Off, Return Level On Вкл Delay Beat Ratio Delay BPM Foot Control Pitch Высота тона Amplitude Modulation Wheel Breath Control Pitch Bias Envelope Bias After Touch Посленажатие Phony Mode Potamento Portamento Mode FINGER FULL PT Portamento Time C&horus Chorus Parameters Channel send level On/Off and Return level Select LADSPA plugin Change plugin &Reverb Reverb Parameters &Config &Настроить Font Size Quality High Middle Low Ultra low Filter Фильтр Save Mode (into the song) Save only the used presets Save the entire set Save the configuration Configuration File Save... Load... Save as default Colors Цвета Text Текст Background Edit Text Edit Background Red Blue Green Set Path Image in the background : Browse... Load the set at the initialization : Set Brightness, Detune, Attack and Release of the current channel to default Res. Ctrl Cut all notes off Panic! Number of Voices Number of voices Enable Разрешить Channel Канал Vol Громк Channel Ctrl Release Послезвучие Attack Атака Brightness Modulation Pan Пан DelOverlapsBase MusE: Delete Overlaps Удалить Наложения Range Диапазон All Events Все События Selected Events Выбранные события Looped Events События в Цикле Selected Looped Выбранные в Цикле OK OK Cancel Отмена Parts Партии Selected parts or c&urrent part All parts &in editor DidYouKnow Did you know? Знаете ли вы? Don't show on startup Не показывать при старте программы Next tip Следующий совет Close Закрыть DuplicateTracksBase Duplicate tracks Дублировать треки Number of copies Количество копий Copy all routes Скопировать все маршруты Default routing Маршрут по умолчанию No routes Нет маршрутов Copy parts Скопировать партии Copy standard controllers (vol, pan) Копировать стандартные контроллеры (громк, пан) Copy effects rack plugins Копировать плагины рэка эффектов Copy plugin controllers Копировать контроллеры плагинов Ok ОК Cancel Отмена Copy drumlist Копировать список ударных Copy standard (vol, pan) and synth controllers Duplicate all parts Make independent copies of all parts Make clones of all parts No parts EditCtrlBase MusE: Edit Controller Event MusE: Редактировать контроллер Time Position Позиция по времени Available Controller: Доступные контроллеры: Create New Controller Создать новый контроллер textLabel3 текстМетка3 Value Значение Controller Контроллер H-Bank H-банк L-Bank L-банк Program Программа off выкл pushButton4 кнопка нажатия4 &OK &ОК &Cancel О&тмена Note Нота EditGainBase MusE: Modify gain MusE: Изменить усиление Gain Усиление 200% 200% 100% 100% 0% 0% &Reset &Сброс Alt+R Alt+R &Apply &Применить Alt+A Alt+A &Cancel &Отмена Alt+C Alt+C EditInstrumentBase MusE: Instrument Editor Редактор инструментов High Bank: H-банк: Low Bank: L-банк: Program: Программа: &Delete &Удалить Alt+D Alt+D Drum Ударные GM GM GS GS XG XG This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. Это список общеупотребительных midi-контроллеров. Обратите внимание, что изменение программ и звуковысотности в MusE, такое же, как и управление обычными контроллерами. Properties Свойства Control7 Контрл7 Control14 Контрл14 RPN RPN NRPN NRPN Pitch Высота тона Program Программа H-Ctrl H-Ктрл L-Ctrl L-Ктрл Min Мин Max Макс Name Имя Type Тип Hex Entry: Шестнадцатиричный Ввод: &File &Файл Tools Инструменты New Новый &New &Новый Ctrl+N Ctrl+N Open Открыть &Open... &Открыть... Ctrl+O Ctrl+O Save Сохранить &Save &Сохранить Ctrl+S Ctrl+S Save As Сохранить как Save &As... Сохранить &как... Exit Выйти E&xit В&ыйти Instrument Name: Имя Инструмента: Selected instrument name. Имя выбранного инструмента. List of defined instruments. Список назначенных инструментов. Pa&tches Па&тчи List of groups and patches. Список групп и патчей. Group/Patch Группа/Патч Name: Имя: Group or patch name Группа или имя патча Patch high bank number Номер H-банка патча Patch high bank number. --- means don't care. Номер H-банка патча. --- не важно какой. --- dont care --- не важно Patch low bank number Номер L-банка патча Patch low bank number. --- means don't care. Номер L-банка патча. --- не важно какой. --- --- Patch program number Програмный номер патча Drum patch Патч ударных If set, the patch is available only for drum channels. Если установлено, то этот патч может использоваться только в каналах ударных. GM patch GM патч If set, the patch is available in a 'GM' or 'NO' midi song type. Если установлено, то этот патч может использоваться только в типах midi-проектов "GM" или "NO". GS patch GS патч If set, the patch is available in a 'GS' or 'NO' midi song type. Если установлено, то этот патч может использоваться только в типах midi-проектов "GS" или "NO". XG patch XG патч If set, the patch is available in an 'XG' or 'NO' midi song type. Если установлено, то этот патч может использоваться только в типах midi-проектов "XG" или "NO". Delete group or patch Удалить группу или патч New patch Новый патч New &Patch Новый &Патч Alt+P Alt+P New group Новая группа New &Group Новая &Группа Alt+G Alt+G Contro&ller Контро&ллер Common: Обычный: List of defined controllers Список назначенных контроллеров List of defined controllers. Список назначенных контроллеров. Min Мин Max Макс Def Назн Midi controller name Имя midi-контроллера Type: Тип: Midi controller type Тип midi-контроллера RPN14 RPN14 NRPN14 NRPN14 Midi controller number high byte Старший байт midi-контроллера Midi controller number low byte (* means drum controller) Младший байт midi-контроллера (* означает контроллер ударных) Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and GS/XG type songs and instruments. Allows controllers for each instrument in Muse's drum map. The low byte will be replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. Младший байт midi-контроллера. Если младший байт - *, тогда контроллер является "контроллером ударных". Для треков ударных и GS/XG типов проектов и инструментов. Разрешает контроллеры для каждого инструмента в карте ударных MusE. Младший байт будет заменен "ANote" в карте ударных. Примеры: Контроллеры ударных для GS и XG инструментов. * wild card * универсальный шаблон Range: Диапазон: Minimum value. If negative, auto-translate. Минимальное значение. Если отрицательное, то авто-перевод. Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. Useful for controllers which should be displayed with zero bias. For example, 'Pan': Minimum: -64 Maximum: 63 True range: Min: 0 Max: 127 (bias = 64) 'CoarseTuning': Min: -24 Max: 23 True range: Min: 40 Max: 87 (bias = 64) Bias is determined from controller type: 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 Type 'Pitch' is the exception. It is biased at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) Минимальное значение. Если минимальное значение отрицательное, то диапазон автоматически переводится в положительный. Полезно для контроллеров отображаемых с нулевым смещением. Например, "Пан": Минимум: -64 Максимум: 63 Реальный диапазон: Мин: 0 Макс: 127 (смещение = 64) "Грубая настройка": Мин: -24 Макс: 23 Реальный диапазон: Мин: 40 Макс: 87 (смещение = 64) Контроллер "Высота тона" является исключением. Его смещение равно нулю, даже при отрицательных значениях. "Высота тона" Мин: -8192 Макс: 8191 Реальный диапазон: Мин: -8192 Макс:8191 (смещение = 0) Maximum value Максимальное значение Default: По умолчанию: L-Bank L-банк off выкл Progr. Прогр. ??? ??? H-Bank H-банк Default value. Off: No default. Значение по умолчанию. Выкл: Нет значения по умолчанию. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). Значение по умолчанию (начальное). Выкл означает, что значения по умолчанию нет. Если выбрано "по умолчанию", то значение будет послано контроллеру, когда он будет добавлен в проект (в пианоролле или редакторе списка). При перезагрузке проекта значение будет послано снова. Или же будет применено последнее значение контроллера. Контроллеры также автоматически добавляются в проект при получении midi-сообщений о контроллерах. Внимание! Будте осторожны с такими контроллерами как "Sustain" и "ResetAllController". Вероятнее всего, что вы назначите "выкл" для этих контроллеров (в пианоролле, редакторе ударных или редакторе инструментов). off dont care выкл Add common controller Добавить обычный контроллер &Add &Добавить Alt+A Alt+A Delete controller Удалить контроллер Create a new controller Добавить новый контроллер New &Controller Новый &Контроллер Alt+C Alt+C Null Param Hi: Нулевой Парам Ст: Null parameter number High byte Нулевой параметр значения Старшего байта If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events from corrupting the RPN/NRPN controller. Typically, set to 127/127, or an unused RPN/NRPN controller number. Если установлено, то это значение "нулевого" параметра будет послано после каждого события RPN/NRPN. Это предотвращает искажение "данных" последующих событий контроллером RPN/NRPN. Обычно, устанавливается 127/127, или "не использовать". Lo: Мл: Null parameter number Low byte Нулевой параметр Младшего байта S&ysEx S&ysEx SysEx List: Список SysEx: New SysE&x Новый SysE&x Alt+X Alt+X &Help &Помощь new item новый пункт What's this? Что это? Drummaps Карты ударных Patch Collections: Коллекции патчей: &Copy С&копировать &Remove &Удалить &Up &Вверх &Down &Вниз Patch: Патч: from из to в Bank Hi: Банк Hi: Bank Lo: Банк Lo: Show in tracks: Midi Midi Drum&maps Contro&llers &Add Common... W W Comment: Комментарий: &Initialization Instrument initialization sequence: &Add... &Change... &Close Close Закрыть Note off mode: Note off mode Selects how to handle note off events. The instrument can use note off events, or not at all, or convert them to zero-velocity note on events which helps save midi interface bandwidth. Refer to the instrument manufacturer's midi implementation chart for details. Patch program number. --- means don't care. &Drum High Bank Low Bank Patch program number. --- means don't care. Patch Name: Dr def Drum default: Note: The enclosing F0 ... F7 are not required Info Инфо Additional information EditNoteDialogBase MusE: Enter Note MusE: Ввести ноту OK ОК Cancel Отмена Length: Длина: Time Position: Позиция по времени: Pitch: Высота тона: Velocity On: Скорость наж: Velocity Off: Скорость снятия: EditSysexDialogBase MusE: Enter SysEx MusE: Ввести SysEx TimePosition: Время: Comment: Комментарий: OK ОК Cancel Отмена Name: Имя: &Select... &OK &Cancel Hex: Note: The enclosing F0 ... F7 are not required FLUIDGui FLUID: open Soundfile FLUID: открыть звуковой файл FLUIDGuiBase Form1 Форма1 Soundfont Soundfont Load Загрузить FLUIDSynthGuiBase FLUID Synth Синтезатор FLUID Gain Усиление Level Уровень Width Ширина Damping Демпфирование Room Size Размер помещения Reverb Реверб Delete Удалить Load Загрузить Sine Синусоида Triangle Треугольная Type Тип Number Число Speed Скорость Depth Глубина Chorus Хорус CHANNEL SETUP НАСТРОЙКА КАНАЛА ID ID Fontname Назв. SF-банка Chnl Канал Soundfont Банк LOADED SOUNDFONTS ЗАГРУЖЕННЫЕ БАНКИ Dump Info Дамп Инфо Drum Chnl Канал ударных FileDialogButtons Global Общий User Пользователь Project Проект read Midi Port Configuration читать конфигурацию midi порта fdialogbuttons КнопкиДиалогаФайлов write window states запоминать состояние окон Home FluidSynthGui Choose soundfont Выберите soundfont GateTimeBase Range Диапазон All Events Все события Selected Events Выбранные события Looped Events События в цикле MusE: Modify Note Length MusE: Изменить длину ноты Selected Looped Выбранные в цикле Values Значения Rate: Скорость: Offset: Смещение: % % lenNew = (lenOld * rate) + offset Нов.дл = (Ст.дл * скор.) + смещ OK ОК Cancel Отмена Parts Партии Selected parts or c&urrent part All parts &in editor GlobalSettingsDialogBase MusE: Global Settings MusE: Общие настройки Use project save dialog Использ. диалог сохр. проекта Some popup menus stay open (else hold Ctrl) Использовать клавишу Ctrl для удержания всплывающих меню Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. Разрешает некоторым всплыв. меню оставаться открытыми. Или удерживайте Ctrl. In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. В некоторых местах средняя кнопка мыши уменьшает значение, а правая увеличивает. Пользователи без средней кнопки могут выбрать эту опцию, чтобы левая кнопка мыши в таких местах вела себя как правая. Use left mouse button for decreasing values Использовать левую кнопку мыши для уменьшения Shift + Right click sets left range marker Shift + щелчок пр. кнопкой - устанавливает левый маркер Allow adding hidden tracks in track list menu Разрешить добавление скрытых треков в меню списка треков Unhide tracks when adding hidden tracks Показывать треки, когда добавляются скрытые GUI Style Стиль ГИП MDI-subwindowness and sharing menus Общие меню и подменю MDI Presets: Пресеты: traditional MusE SDI традиционное MusE SDI Cakewalk-like MDI MDI в стиле Cakewalk Borland-/Mac-like MDI MDI в стиле Borland-/Mac &Apply &Применить &Ok &ОК &Cancel &Отмена Audio Аудио Mixer Микшер dB Дб min. Meter Value мин. знач. индикатора Midi Midi Ticks Тики 1024 1024 2048 2048 4096 4096 Displayed Resolution (Ticks/Quarternote) Отображаемое разрешение (Тиков/на четверть) 48 48 96 96 192 192 384 384 768 768 1536 1536 3072 3072 6144 6144 12288 12288 RTC Resolution (Ticks/Sec) RTC-разрешение (тиков/в сек) GUI Refresh Rate Частота обновления ГИП /sec /сек Application Приложение Start Muse Запустить Muse start with last song с последним проектом start with song с проектом Views Настройки окон y-pos y-поз. show показать x-pos x-поз. height высота width ширина Big Time Хронометр Transport Транспорт Cur Тек set current values установить текущее значение show splash screen показывать заставку External Waveditor Внешний аудиоредактор External Waveditor command Команда вызова внешнего аудиоредактора Mixer A Микшер А Mixer B Микшер В show "Did you know?" dialog показывать окно "Знаете ли вы?" Start song Запустить проект min. Slider Val мин. значение слайдера Enable denormal protection Разрешить защиту от нарушений режима Enable output limiter Разрешить лимитер выхода Note: External editor opened from the internal editor. Примечание: Внешний редактор открывается из внутреннего редактора. Dummy Audio Driver (settings require restart) Фиктивный Аудио Драйвер (требует перезагрузки) Hz Гц Period size (Frames per period): Размер периода (Фреймов на период): 16 16 Project directory Папка проекта Projects: Проекты: ... ... Main Window Главное окно Try to use Jack Freewheel Использовать режим "Jack Freewheel" Speeds bounce operations Ускоряет операции сведения Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Используйте режим Jack freewheel, если возможно. Значительно ускоряет операции сведения. VST in-place Быстрая обработка для VST-плагинов Enable VST in-place processing (restart required) Разрешить быструю обработку для VST-плагинов. (Требуется перезагрузка) Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. Возможность быстрой обработки VST. Отключите, если плагины эффектов рэка "VST Ladspa" не работают, или звучат очень громко, хотя и не должны. Требуется перезагрузка. Minimum control period Минимальный размер буфера Minimum audio controller process period (samples). Минимальный период аудио обработки (сэмпл). Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. Минимальный период аудио обработки (сэмпл). Отрегулируйте чувствительность кнопок и линеек. Установите, сначала, небольшие значения. При возникновении проблем - увеличьте. 1 1 2 2 4 4 8 8 32 32 64 64 128 128 256 256 512 512 Sample rate Частота дискретизации Shorter periods give better midi playback resolution. Меньшие периоды дают лучшее разрешение проигрывания midi. 8192 8192 16384 16384 32768 32768 Midi Resolution (Ticks/Quarternote) Midi-Разрешение (Тиков/На четверть) GUI Behaviour Поведение ГИП Use old-style stop shortcut: Использовать старую "горячую клавишу" для остановки: Move single armed track with selection Назначать "для записи" выбранный трек On Launch При запуске Behavior Поведение Choose start song or template Начать с сонга или шаблона Reset to default Сбросить к исходным start with template с шаблоном Start template or song: запустить шаблон или сонг: Read MIDI Ports configuration from file, or else automatically configure Читать конфигурацию midi портов из файла, или настроить автоматически Read MIDI Ports configuration Читать конфигурацию midi-портов Smart focus Умное фокусирование After editing, controls will return focus to their respective canvas После редактирования регуляторы вернутся в соответствующие позиции Record new style drum tracks Запись треков ударных в новом стиле Record all instruments Записывать все инструменты Don't record hidden instruments Не записывать скрытые инструменты Don't record muted instruments Не записывать инструменты "без звука" Don't record hidden or muted instruments Не записывать инструменты "без звука" и скрытые Drum tracks Треки ударных Only offer old-style drumtracks Предлагать только треки ударных в старом стиле Only offer new-style drumtracks Предлагать только треки ударных в новом стиле Prefer old-style drumtracks Предпочитать старые Prefer new-style drumtracks Предпочитать новые Auto save (every 5 minutes if not playing/recording) Instrument initialization Send instrument initialization sequences Warn if instrument initialization sequences pending Send instrument controller default values if none in song, at init or rewind Warn if timer frequency is inadequate Track height Show newly created midi velocity graphs per-note px Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. Borderless zoom/pan mouse (else use alternate method) &Don't record hidden instruments Don'&t record muted instruments Don't record &hidden or muted instruments Scrollable submenus Прокручиваемые подменю Warn if opening file versions different than current version Start MusE Start with &last song Start with &template Sta&rt with song On launch Show splash screen Show "Did you know?" dialog Plugin Paths Ordered by priority. Changes take effect on restart. The plugin cache is used for safety and speed. It automatically rescans when any of the given directories change, for example by installing a new plugin. Click this button to force the cache to recreate upon next restart. &Force plugin rescan and cache recreation on next restart LADSPA DSSI Linux VST LV2 УР2 NOTE: Ensure the directory containing core LV2 bundles is included (typically /usr/lib/lv2). Otherwise all plugins may not load properly. Move up Move down Add... Edit... Remove Удалить Audio driver settings (require restart) Sample rate: Audio backend: Shorter periods give better midi playback resolution. Note that this cannot be set with Jack devices! Period size (frames per period): Note that samplerate cannot be set with Jack devices! Minimum meter value Minimum slider value Midi controller behaviour Send Null parameters after an (N)RPN value If set, 'null' parameter numbers will be sent after each RPN/NRPN event. (A 'null' parameter number is 127.) This prevents subsequent rogue or accidental 'data' events (without parameters) from corrupting the current RPN/NRPN controller. Normally it is OK to leave this unset, but use it if there is a possibility of such 'data' events. If this is set, the 'Optimize controllers' setting does not affect these controllers' parameters, only the H/L values. Send Null parameters Don't send redundant parameters or values If set, redundant H/L parameters or H/L values are not sent. They are sent only if they changed. This can save midi interface bandwidth. But it is possible the receiving device might miss the previous changes (for example if the device is turned on after they were sent). The 'Panic' button will force them to be resent. Certain operations will also force them to be resent, such as clicking a GUI midi control (but not while adjusting it). Optimize controllers Record a&ll instruments Latency Whether to enable latency correction/compensation: Enable latency correction/compensation Project latency Whether completely independent branches share a common project-wide latency: Project-wide latency Unterminated branches Whether unterminated branches participate in latency correction/compensation: Unterminated input branches Unterminated output branches Track monitoring Whether track monitor buttons affect latency correction/compensation: Monitoring affects latency Revert native GUI scaling for plugins in HiDPI Live update wave parts while recording Whether to show knob and slider values in certain places, for example mixer strips. Turn off to reduce clutter. Revert native UI window HiDPI scaling (some plugins ignore this setting) Show knob and slider values Whether to show knobs or sliders in certain places, for example mixer strips Prefer midi volume as decibels instead of 0-127 Style hack: Force line edit widgets to draw a frame Prefer knobs instead of sliders GUI refresh rate Use first available Ask once Ask always Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. This hack forces a frame to be drawn, but may interfere with other styles. Use old-style stop shortcut Monitor on record-arm automatically Show note names on notes in pianoroll Whether record-arming a track automatically activates monitoring. Record drum tracks Sample rate conversion, time stretching and pitch shifting Audio converter settings... LV2 UI open behavior Set range markers (left / right) Middle mouse button / Right mouse button Ctrl + Right mouse button / Right mouse button Ctrl + Left mouse button / Ctrl + Right mouse button Options GUI Tweaks Tweaks Editors Open editors in tabs (else in separate windows) Piano roll Пианоролл Drum editor Редактор ударных Wave editor Wave редактор Score editor Партитурный редактор Master track editor Редактор мастер-трека (график) DSSI VST Directories where dssi-vst (if installed) looks for Wine vst plugins (*.dll files) Directories containing Linux vst plugins (*.so files) GUI ГИП Show status bar Show note tooltips in midi editors Keep transport window on top (requires restart) LegatoBase MusE: Legato MusE: Легато Range Диапазон All Events Все события Selected Events Выбранные события Looped Events События в цикле Selected Looped Выбранные ы цикле Settings Настройки ticks тик(и) Minimum Length Минимальная Длина Allow shortening notes Разрешить сокращение нот OK OK Cancel Отмена Parts Партии Selected parts or c&urrent part All parts &in editor MITTransposeBase MusE: Midi Input Plugin: Transpose MusE: Midi-плагин на входе: Транспонирование On Вкл TriggerKey Кнопка запуска Transpose: Транспонировать: +0 +0 MRConfigBase MusE: Midi Input Plugin: Remote Control MusE: Входной MIDI-плагин: Дистанц. управление Activate Активировать On Вкл Actions Действия Stop Остановить Record Записать Goto Left Mark Перейти к левому маркеру Play Воспроизвести Insert rest (step rec) Вставить паузу (пошаг. зап) MdiSettingsBase Form Form GroupBox Окно Группы MDI subwin MDI под-окно Shares menu when subwin Общее меню в под-окне Shares menu when free Общее меню всегда MetronomeConfigBase MusE: Metronome Config MusE: Настройка метронома Metronome Метроном Audio Beep Аудио клик MIDI Click MIDI клик Midi Channel MIDI-канал Measure Note Нота на первую долю такта Measure Velocity Громкость первой доли такта Beat Velocity Громкость слабых долей такта Beat Note Нота на слабые доли такта Midi Port MIDI-порт Precount Предварительный отсчет enable разрешить Bars Такт(ов) From Mastertrack Из Мастер-трека / / Signature Размер Prerecord Предзапись Preroll Предвоспроизведение &Apply &Применить &OK &ОК &Cancel &Отмена Choose outputs... Выбрать выходы... 50 50 % Audio volume % Громкость аудио Hint: Enable metronome in Transportpanel Подсказка: Активируйте метроном на Транспортной панели Alt+A Alt+A Alt+O Alt+O Alt+C Alt+C Volume Громкость Audio master Meas Beat Accent1 Accent2 Sample Preferences Apply the settings globally or to the current song: Use global metronome Use song-specific metronome Enable accents for the audio and midi metronomes: &Two samples (old samples) Four samples (&new samples, with accents) Audio Аудио MIDI Accent 2 Velocity Accent 1 Velocity Accent 1 Note Accent 2 Note Off Выкл Accents Accent sequence beats Current accent 1 sequence: (Click to change) Current accent 2 sequence: Factory User Пользователь Preset sequences Add current accents to presets Make a new preset from the current accents. + Delete preset Удалить пресет Delete the selected preset. Only user presets can be deleted. - Use preset Copy the preset to the current accents. -> Reset to Factory or User defaults Resets all current accent sequences to default Factory or User presets. ! Enable the precount Enable Разрешить Number of bars Use Mastertrack time signature Beats per bar Beat note type Enable precount when playing Precount on play Mute the metronome after precount Mute metronome Not supported yet MidiAudioControlBase Midi control Midi контроль Port: Channel: Канал: Control type: Тип контроля: Control7 Контрл7 Control14 Контрл14 RPN RPN NRPN NRPN RPN14 RPN14 NRPN14 NRPN14 Pitch Высота тона Program Программа Hi: Hi: Lo: Lo: Learn Узнать &Learn MidiFilterConfigBase MusE: Midi Input Filter MusE: Фильтр входящих Midi-событий Record Filter Фильтр записи Note On Нажатие ноты Poly Pressure Полифоническое давление Controller Контроллер Program Change Смена программы After Touch Посленажатие Pitch Bend Изменение высоты тона Sysex SysEx Thru Filter Фильтр сквозного канала Controller Filter Фильтр контроллеров Channel Filter Фильтр каналов 14 14 10 10 6 6 12 12 4 4 2 2 9 9 8 8 3 3 13 13 15 15 16 16 7 7 11 11 5 5 1 1 MidiInputTransformDialogBase MusE: Midi Input Transformator MusE: Преобразователь входящих MIDI-событий Filter Фильтр All Все Equal Равно Unequal Не равно Note Нота Poly Pressure Полифоническое давление Control Change Смена контроллера Aftertouch Посленажатие Pitch Bend Изменение высоты тона NRPN NRPN RPN RPN Value 2 Значение 2 Value 1 Значение 1 Event Type Тип события Ignore Игнорировать Higher Выше Lower Ниже Inside Внутри Outside Вне Channel Канал Port Порт Processing Обработка Keep Сохранить Fix Исправить Plus Плюс Minus Минус Multiply Умножить Divide Разделить Invert Инвертировать ScaleMap Шкала Flip Обратить Dyn Дин Random Случайно Modules Модули 1 1 2 2 3 3 4 4 enable modul 1 разрешить модуль 1 enable modul 2 разрешить модуль 2 enable modul 3 разрешить модуль 3 enable modul 4 разрешить модуль 4 Preset Пресет Name: Имя: Comment: Комментарий: Function Функция &New &Новый create new preset создать новый пресет &Delete &Удалить delete preset удалить пресет &Dismiss &Отклонить PresetList Список пресетов Program Программа Toggle Enable module 1 Enable module 2 Enable module 3 Enable module 4 MidiSyncConfigBase MusE: Midi Sync MusE: Midi-синхронизация Apply Применить Ok ОК Cancel Отмена MTC MТC Type: Тип: 24 24 25 25 30D 30D 30N 30N Offset: Смещение: hour часов h ч minute минут(а) m м second секунд(а) s с frame фреймов f ф subframe субфреймов Sync receiving and sending Получение и отправление синхронизации Send and receive Jack transport Отсылать и получать транспорт "Jack" Send and receive Jack transport information, including stop, start and position. Отсылать и получать информацию с транспорта "Jack", стоп, старт, и позицию. Use Jack transport Использовать транспорт "Jack" Make MusE the Jack transport Timebase Master Сделать MusE Мастером Базы времени для транспорта "Jack" Make MusE the Jack transport Timebase Master. Allows Jack to show time as MusE Bars, Beats, and Ticks. MusE will try to become master, but other Jack clients can also take over later. You can always click here again for Master. Сделать MusE Мастером Базы времени для транспорта "Jack" Позволяет "Jack" показывать время как в "MusE", то есть Такты, Доли и Тики. MusE будет пытаться стать Мастером, но другие клиенты могут позже взять верх. Вы должны каждый раз щелкнуть здесь, чтобы MusE снова стал Мастером. Jack transport Timebase Master Мастер Базы времени транспорта "Jack" Control MusE timing by external midi clock or MTC sync Контролировать Muse через внешний midi-clock или MTC синхр When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). В режиме ведомого темп контролируется извне. MusE может синхр. с midi-clock, или с четверть-фреймовой синхр. MTC. Разрешенные входы в списке будут задействованы. (RMC, RMMC, RMTC). Slave to external sync Ведомый для внешней синхронизации Send start to first clock delay Посылать "старт" первой задержке "clock" Allows 'slow sync' devices time to synchronize to MusE. This value is the delay from sending start to sending the first clock. Позволяет "медленно синхр." устройствам синхронизироваться с MusE. Это значение является задержкой от посылки старта до посылки первого "clock". ms мс Send sync delay Посылать синхр. задержку Note: Sync delay and MTC sync currently not fully implemented Примечание: Задержка синхр. и MTC применена не полностью Averaging applied to recorded external tempo changes. Для записи изменений внешнего темпа применено усреднение. External midi clock can be very jittery. Tempo is derived from it and recorded. It is usually desirable to average it and limit the number of recorded changes. Tiny: 2 section 4/4 = 8 stages. 1/8T note averaging, may produce jitter. Small: 3 section 12/8/4 = 24 stages. 1/4 note averaging, may still produce jitter. Medium: 3 section 28/12/8 = 48 stages. 1/2 note averaging. Less jitter. Large: 4 section 48/48/48/48 = 192 stages. Use this if the song has only one tempo. Very low quantization values can be used. Large pre-detect: 4 section 8/48/48/48 = 152 stages + first stage large step pre-detector. Use this if you expect sudden large tempo steps. None: Use only if high accuracy is needed for audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. Внешнее midi время может быть очень неровным. Темп берется и записывается из него. Обычно нужно усреднять его и ограничивать количество записываемых изменений. Очень маленькое: 2 секции 4/4 = 8 ступеням. Усреднение до1/8T ноты, могут быть скачки. Маленькое: 3 секции 12/8/4 = 24 ступеням. Усреднение до1/4 ноты, но все равно могут быть скачки. Среднее: 3 секции 28/12/8 = 48 ступеням. Усреднение до1/2 ноты, меньше скачков. Большое: 4 секции 48/48/48/48 = 192 ступеням. Используйте его, если в сонге только один темп. Может быть использовано очень низкое значение квантования. Большое с пред-обнаружением: 4 секции 8/48/48/48 = 152 ступеням. + большой шаг первой ступени - пред-обнаружитель. Используйте если ожидаете большие резкие скачки темпа. Нет: Используйте , если нужна большая точность воспроизведения. Предупреждение: Записываются тысячи изменений темпа в минуту. Это может замедлить работу MusE и увеличить размер файла. Tempo record averaging Усреднение записи темпа bpm bpm Tempo record quantization Квантование записи темпа This button and other transport related buttons are found on the Transport Panel MidiTrackInfoBase MusE: TrackInfo MusE: Трек Инфо output channel канал выхода % % output port порт выхода off выкл Transp. Трансп. Channel Info Канал-инфо Rec: Зап: Prog Прог Vol Громк Pan Пан Delay Задержка H-Bank H-банк Compr. Компр. L-Bank L-банк Velocity Скорость нажатия Length Длина all midi events are sent to this output channel все midi-события посланы на этот канал выхода Out ch Вых кан input routing маршрут входа output routing маршрут выхода input detect активность входа Input detect indicator. Detects all note on-off, controller, aftertouch, program change, and pitchbend (but not sysex or realtime) events on the selected channels, on the selected midi ports. Индикатор активности входа. Обнаруживает ноты, контроллеры, смену программы, и изменение высоты тона (но не sysex или realtime) на выбранных каналах выбранных midi-портов. W W Midi thru Сквозной midi-канал Pass input events through ('thru') to output. Пропускать входные события через сквозной канал на выход. Select instrument patch Выбрать патч инструмента <unknown> <неизвестно> Add all settings to song Добавить все настройки в проект All Все Add bank + prog settings to song Добавить банк и программу в проект Add vol setting to song Добавить настройки громк. в проект Add pan setting to song Добавить настройки панорамы в проект Bank Select MSB. Ctrl-double-click on/off. Выбор банка MSB. Ctrl-двойной щелчок вкл/выкл. Bank Select LSB. Ctrl-double-click on/off. Выбор банка LSB. Ctrl-двойной щелчок вкл/выкл. Program. Ctrl-double-click on/off. Программа. Ctrl-двойной щелчок вкл/выкл. Volume. Ctrl-double-click on/off. Громкость. Ctrl-двойной щелчок вкл/выкл. Change stereo position. Ctrl-double-click on/off. Изменение стерео позиции. Ctrl-двойной щелчок вкл/выкл. MidiTransformDialogBase MusE: Midi Transformator MusE: Преобразователь MIDI-сообщений &New &Новая &Delete У&далить &Apply &Применить &OK &ОК &Cancel &Отмена PresetList Список пресетов Processing Обработка Event Type Тип события Keep Сохранить Fix Исправить Note Нота Poly Pressure Полифоническое нажатие Control Change Смена контроллера Aftertouch Посленажатие Pitch Bend Смена высоты тона NRPN NRPN RPN RPN Plus Плюс Minus Минус Multiply Умножить Divide Разделить Value 2 Значение 2 Invert Инвертировать ScaleMap Шкала Flip Обратить Dyn Дин Random Случайно Value 1 Значение 1 Length Длина Position Позиция Filter Фильтр All Все Equal Равные Unequal Не равные Ignore Игнорировать Higher Выше Lower Ниже Inside Внутри Outside Вне Bar Range Диапазон тактов Preset Пресет Name: Имя: Comment: Комментарий: Range Диапазон process all events обработать все события selected tracks выделенные треки inside loop Внутри цикла Function Функция Select Выделить Quantize Квантовать Delete Удалить Transform Преобразовать Insert Вставить Copy Скопировать Extract Извлечь Quantize Value Значение квантования Randomize MixdownFileDialogBase MusE: Set Mixdown Wavefile MusE: Настроить параметры Wave-файла &OK &OK &Cancel О&тменить File Path Путь к файлу Channel Канал Stereo Стерео Mono Моно 5.1 5.1 wav,16 Bit wav, 16 бит wav, 24 Bit wav, 24 бита wav, 32 Bit (float) wav, 32 бита (float) Format Формат MoveBase MusE: Move Notes MusE: Сдвинуть ноты Range Диапазон All Events Все события Selected Events Выбранные события Looped Events События в цикле Selected Looped Выбранные в цикле Value Значение Move by Сдвинуть на ticks тик(а, ов) OK OK Cancel Отмена Parts Партии Selected parts or c&urrent part All parts &in editor MusECore::AudioPreviewDialog Stop Остановить Play Воспроизвести MusECore::RasterizerModel Off Выкл Bar Такт MusECore::Scripts Do you want to process ALL or only selected events? &Selected &All MusE - external script failed MusE - ошибка запуска внешнего скрипта MusE was unable to launch the script, error message: %1 MusE не смог запустить скрипт, ошибка: %1 Re-read script names from disc MusECore::Song Jack shutdown! Jack прекратил работу! Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: - a performance issue with your particular setup. - a bug in MusE (or possibly in another connected software). - a random hiccup which might never occur again. - jack was voluntary stopped by you or someone else - jack crashed If there is a persisting problem you are much welcome to discuss it on the MusE mailinglist. (there is information about joining the mailinglist on the MusE homepage which is available through the help menu) To proceed check the status of Jack and try to restart it and then . click on the Restart button. Проблемы в работе "Jack", привели к его рассоединению с "MusE". Это могло произойти по разным причинам: -проблемы в работе, вызванные особенностью ваших настроек. -баг в "MusE" (или, возможно, в других подсоединенных программах). -случайное выключение, которое больше никогда не повторится. -"Jack" остановлен вами или кем-нибудь еще. -"Jack" рухнул Если это постоянная проблема мы приглашаем вас обсудить её в почтовой рассылке "MusE". (в меню "Помощь" есть информация о том, как присоединиться к почтовой рассылке "MusE") Чтобы продолжить работу, проверте состояние "Jack", постарайтесь перезапустить его, а затем щелкните по кнопке "Перезапустить". Automation: Автоматизация: previous event предыдущее событие next event следующее событие set event настроить событие add event добавить событие erase event стереть событие erase range стереть диапазон clear automation очистить автоматизацию Clear all controller events? Очистить все события контроллеров? &Ok &ОК &Cancel &Отмена MusE - external script failed MusE - ошибка запуска внешнего скрипта MusE was unable to launch the script, error message: %1 MusE не смог запустить скрипт, ошибка: %1 Midi control Midi контроль Assign Назначить Clear Очистить MusE: Tempo list MusE: Список темпа External tempo changes were recorded. Transfer them to master tempo list? Были записаны изменения темпа. Перенести их в список мастер-трека? Und&o &Отменить Re&do &Повторить Previous event Next event Set event Add event Erase event Erase range Clear automation Bypass Automation Автоматизация Controller Контроллер MusEGui select Pointer Tool: with the pointer tool you can: select parts move parts copy parts выбрать инструмент "Указатель": при помощи Указателя можно: выделять партии перемещать партии копировать партии select Pencil Tool: with the pencil tool you can: create new parts modify length of parts выбрать инструмент "Карандаш": инструментом Карандаш вы можете: создавать новые партии изменять длину партий select Delete Tool: with the delete tool you can delete parts выбрать инструмент "Ластик": инструментом "Ластик" вы можете удалять партии select Cut Tool: with the cut tool you can split a part выбрать инструмент "Ножницы": инструментом Ножницы вы можете резать партию select Glue Tool: with the glue tool you can glue two parts выбрать инструмент "Клей": инструментом Клей вы можете соединять партии select Score Tool: выбрать инструмент партитуры: select Quantize Tool: insert display quantize event выбрать инструмент "Квантование": квантует отображаемые события select Drawing Tool выбрать инструмент "Рисование" select Muting Tool: click on part to mute/unmute выбрать инструмент "Без звука": Щелкните по партии, чтоб переключать "Без зв./Звук" Manipulate automation Управлять автоматизацией Cursor tool Инструмент Курсор pointer указатель pencil карандаш eraser ластик cutter ножницы score партитура glue клей quantize квантовать draw рисовать mute parts заглушить партию edit automation править автоматизацию cursor курсор MusEGui::Appearance Main application font, and default font for any controls not defined here. Главный шрифт приложения, и шрифт по умолчанию для кнопок управления, назначаются не здесь. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Линейки микшера и рэки эффектов. Инфо-панель Midi-трека. Окно ввода контрольной панели Midi. Transport controls. Контроль транспорта. Time scale upper, and time signature. Controller graph and S/X buttons. Шкала времени вверху и тактовый размер. Графы контроллеров и кнопки S/X. Time scale lower, and arranger part name overlay. Шкала времени внизу и название партии. Tempo scale, and markers. Шкала темпа и маркеры. Mixer labels. Auto-font-sizing up to chosen font size. Word-breaking but only with spaces. Надписи в микшере. Авто-размер шрифта до выбр. размера. Разрыв слов - только пробелом. Maximum mixer label auto-font-sizing font size. Максимальный размер авто-шрифта для надписей в микшере. Global opacity (opposite of transparency). Общая непрозрачность (в противоположность прозрачности). Standard Стандартный Custom По выбору MusE: load image MusE: загрузить изображение Select style sheet Выберите стиль Qt style sheets (*.qss) Таблицы стилей Qt (*.qss) Keep Qt system style Удерживать системный стиль Qt &Ok &ОК &Cancel Отмена Style was changed. The program must be restarted for changes to take place. Restart now? Revert changes Revert all... Do you really want to reset all colors? No current color item Select Color: %1 Main application font, and default font for any controls not defined here. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Arranger part name overlay. Time scale lower. MusEGui::Arranger Enable Recording Разрешить запись Mute/Off Indicator Без звука Solo Indicator Соло Track Type Тип трека Track Name Имя трека Midi output channel number or audio channels Номер выходного Midi-канала или аудио каналов Midi output port or synth midi port Выходной midi-порт или midi-порт синтезатора Time Lock Блокировка времени Automation parameter selection Выбор параметра автоматизации Notation clef Ключ нотации Enable recording. Click to toggle. Разрешить запись. Щелкните для переключения. Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. On/Off is not! Индикатор "Без звука". Щелкните для переключения. Щелчок правой кнопкой мыши включает/выключает трек. Индикатор "Без звука" предназначен для быстрых, повторных действий. А Вкл/Выкл нет! Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed, indicated by a dark square. Индикатор Соло. Щелкните для переключения. Все треки, связанные с этим треком тоже перейдут режим "фантомного" соло, Они будут обозначены темными квадратами. Track type. Right-click to change midi and drum track types. Тип трека. Щелкните правой кнопкой мыши чтобы изменить тип трека (midi-трек или трек ударных). Track name. Double-click to edit. Right-click for more options. Имя трека. Редактирование - двойной щелчок. Большее количество опций - правый щелчок мыши. Midi/drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Трек Midi/Ударных: номер канала выхода. Аудио трек: Каналы. Чтобы изменить, щелкните Средней/Правой кнопкой мыши. Midi/drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Трек Midi/Ударных: Порт выхода. Трек синтезатора: Назначенный midi-порт. Щелчок левой кнопкой - изменить. Щелчок правой кнопкой - показать ГИП. Time lock Блокировка времени Notation clef. Select this tracks notation clef. Ключ нотации. Выберите ключ для этого трека. Arranger Компоновщик Cursor Курсор Off Выкл Bar Такт Snap Привязка Len Дл song length - bars длина проекта - в тактах Type Тип NO NO GM GM GS GS XG XG midi song type тип midi-проекта Pitch Высота тона midi pitch midi высотность global midi pitch shift общий сдвиг midi-высотности Tempo Темп midi tempo midi-темп N N TrackInfo ТрекИнфо R Record R M Mute M S Solo S C Class (track type) Т Track Трек Port Порт Ch Channel Кан T Time lock Т Automation Автоматизация Clef Ключ Track index Enable input monitor Enable input monitor. Click to toggle. Passes input through to output for monitoring. See also Settings: Automatically Monitor On Record Arm. Enable recording. Click to toggle. See also Settings: Automatically Monitor On Record Arm. Song length - bars Midi pitch Global midi pitch shift Midi tempo I Input monitor Вх Enable recording Mute/Off indicator Solo indicator Track type Track name Midi/Drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi/Drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Track index. Click to select track, CTRL+LMB to add/remove, SHIFT+LMB for range select, dblclick to select all of the same type. Enable/Disable input monitor. Passes input through to output (see also Settings: Automatically monitor on record arm). Enable/Disable recording. Mute indicator. Left-click to mute track (for quick action), right-click to toggle track on/off (for more permanent action). Track type. Click to switch between midi and drum track types. Track name. Double-click to edit. Right-click for context menu. Midi/Drum: Output channel number. Audio: Channels. Mid/right-click to change. Midi/Drum: Output port. Synth: Assigned midi port. Left-click to change, right-click to show synth GUI. Notation clef. Click to select this tracks notation clef. Cursor position Bars Такт(ов) Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed. Solo indicator. Connected tracks are also 'phantom' soloed. MusEGui::ArrangerColumns Control7 Контрл7 Control14 Контрл14 RPN RPN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Высота тона Program Программа Aftertouch Посленажатие MusEGui::ArrangerView MusE: Arranger MusE: Компоновщик C&ut &Вырезать &Copy С&копировать Copy in range Копировать диапазон &Paste &Вставить Paste (show dialog) Вставить (показать диалог) Paste c&lone Вставить к&лон Paste clone (show dialog) Вставить клон (показать диалог) &Insert Empty Measure &Вставить пустой такт Delete Selected Tracks Удалить выделенные треки Duplicate Selected Tracks Дублировать выбранные треки Shrink selected parts Сжать выбранные партии Expand selected parts Расширить выбранные партии Clean selected parts Очистить выбранные партии Add Track Добавить трек Select Выделить Select &All Выделить &Всё &Deselect All &Снять выделение Invert &Selection Инвертировать &Выделенное &Inside Loop &Внутри цикла &Outside Loop &Вне цикла All &Parts on Track Все &Партии в Треке Score Партитура all tracks in one staff все треки на один стан one staff per track один трек на стан New score window Окно новой партитуры Pianoroll Пианоролл Drums Редактор ударных List Редактор списка Wave Редактор Аудио Mastertrack Мастер-трек Graphic Графический редактор Midi &Transform &Трансформация MIDI Global Cut Общая вырезка Global Insert Общая вставка Global Split Общее разделение Global Cut - selected tracks Общая вырезка - выбранные треки Global Insert - selected tracks Общая вставка - выбранные треки Global Split - selected tracks Общее разделение - выбранные треки &Edit &Правка &Structure &Структура Functions Функции &Quantize Notes &Квантовать ноты Change note &length Изменить длину &ноты Change note &velocity Изменить скорость нажатия &ноты Crescendo/Decrescendo Крещендо/Диминуэндо Transpose Транспонировать Erase Events (Not Parts) Удалить события (не партии) Move Events (Not Parts) Передвинуть события (не партии) Set Fixed Note Length Установить длину ноты Delete Overlapping Notes Удалить наложения Legato Легато Window &Config &Конфигурация окна New Новый Configure &custom columns Настроить &колонки пользователя Changed Settings Измененные настройки Unfortunately, the changed arranger column settings cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. (we'll try to fix that) К сожалению изменение колонок не может быть применено во время работы MusE. Чтобы применить изменения, перезапустите MusE. (постараемся это исправить) Purge hidden events from selected parts Удалить скрытые события из выбранных партий Remove track(s) Удалить трек(и) Are you sure you want to remove this track(s)? Вы уверены, что хотите удалить трек(и)? D&elete Copy in Range Paste C&lone Paste to Selected &Track Paste Clone to Selected Trac&k Paste (Show Dialo&g)... Shrink Selected Parts Expand Selected Parts Purge Hidden Events from Selected Parts All Tracks in One Staff One Staff per Track New Score Window... Pianoroll... Drums... Wave... Midi &Transform... Global Cut - Selected Tracks Global Insert - Selected Tracks Global Split - Selected Tracks Fu&nctions &Функции Change Note &Length Change Note &Velocity &Display Configure &Custom Columns... New... Arranger Компоновщик Insert Track Добавить трек Event List... Open in New Window Always open the part(s) in a new editor window Open Synth Plugin GUI... Display menu: View-specific display options. Toggle &Mixer Strip &Scripts Scripts for midi processing. Applied to selected midi parts (or else tracks). User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Nothing to edit Нечего редактировать MusEGui::AudioComponentRack Aux send level (dB) Vol Громк Pan Пан Mute Volume/gain Panorama/Balance Gain Усиление Calibration gain MusEGui::AudioMixerApp &Create &Создать &View &Вид Routing Маршрутизация Show Midi Tracks Показать Midi-треки Show Drum Tracks Показать треки ударных Show Wave Tracks Показать Wave-треки Show Inputs Показать входы Show Outputs Показать выходы Show Groups Показать Группы Show Auxs Показать Auxs Show Synthesizers Показать синтезаторы Show New Style Drum Tracks Показывать треки ударных в новом стиле Strips Traditional order Arranger order User order Show all hidden strips Unhide strip: (no hidden strips) MusEGui::AudioStrip panorama панорама aux send level уровень aux посыла Pan Пан 1/2 channel стерео-моно Pre Пре pre fader - post fader пре федер - пост федер dB дБ record запись mute без звука record downmix записать микс solo mode соло off выкл input routing маршрут входа output routing маршрут выхода Off Выкл Read Читать Touch По касанию Write Писать automation type тип автоматизации Pre Fader Listening (PFL) Record downmix Record arm Mute or proxy mute Solo or proxy solo Track off Input monitor Pass input through to output Automation type L meter peak/clip R meter peak/clip Input routing. Press F1 for help. Output routing. Press F1 for help. Volume/Gain Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Auto off Automation type: Off, Read, Touch or Write. Press F1 for help. MusEGui::BigTime format display формат дисплея bar такт beat доля tick тик minute минут(а) second секунд(а) frame фреймов subframe субфреймов MusE: Bigtime MusE: Хронометр Format display Bar Такт Beat Tick Тик Minute Second Frame Subframe MusEGui::Canvas Tools Инструменты MusEGui::ClipListEdit MusE: Clip List Editor MusE: Редактор списка клипов Window &Config Конфигурация &окна MusEGui::ComboQuant Off Выкл MusEGui::CompactKnob off выкл MusEGui::CompactPatchEdit Patch name MusEGui::CompactSlider off выкл MusEGui::CpuStatusBar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) CPU load averaged over each GUI update period. DSP load read from JACK. Number of xruns. Double-click to reset. MusEGui::CpuToolbar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) MusEGui::CtrlCanvas Drawing hint: Hold Ctrl to affect only existing events Совет: Удерживайте Ctrl, чтобы править только существующие события Use shift + pencil or line tool to draw new events Используйте shift + карандаш или линию, чтобы рисовать новые события Make the current part's track match the selected drumlist entry Привести текущий трек в соответствие с вводом списка ударных Use pencil or line tool to draw new events Используйте карандаш или линию для для создания новых событий Merge options Erase target Erase target events between source events Erase target WYSIWYG Include last source item width when erasing Erase target inclusive Include entire source range when erasing Merge actions Merge Merge the dragged items Merge a copy Merge a copy of the dragged items Cancel drag Cancel dragging the items Control canvas: Use Pencil tool to edit events and Draw tool to adjust them gradually. Hold Ctrl to affect only existing events. MusEGui::CtrlPanel S Select controller S select controller выбрать контроллер X Remove panel (destroy button) X remove panel удалить панель manual adjust настройка вручную off выкл Velocity Скорость нажатия add new ... Добавить новый... Instrument-defined Определенный инструментом Add ... Добавить... Others Другие Edit instrument ... Редактировать инструмент... Common Controls Обычные кнопки ctrl-double-click on/off ctrl-двойной щелчок вкл/выкл Select controller Remove panel Manual adjust (Ctrl-double-click on/off) All/Per-note velocity mode MusEGui::DList hide this instrument скрыть этот инструмент show this instrument показать этот инструмент this turns a grayed out eye into a blue eye это превратит серый глаз в голубой Hide this instrument This turns a blue eye into a crossed eye Show this instrument This turns a crossed eye into a blue eye Set field Sets a field Set row Sets a row Set column Sets a whole column to the field Set list Sets the whole list Reset field Resets a field in a row to default patch or instrument value Reset row Resets a row to the instrument values Reset column Resets a whole column to the instrument values Reset list Resets the whole list to the instrument values Reset track's drum list Resets all lists on all patches to the instrument values Promote field to default patch Promotes a field in a row to the default patch Promote row to default patch Promotes a row to the default patch Promote column to default patch Promotes a column to the default patch Promote list to default patch Promotes the whole list to the default patch Reset instrument field Reset instrument row Reset instrument list Set instrument field Set instrument row Set instrument list Drum list: Edit instrument-specific settings. MusEGui::DrumCanvas Moving items failed Передвижение не удалось The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. Выбранное не может быть передвинуто, так как по крайней мере одна нота была бы передвинута в трек не соответствующий ни оригинальному ни текущему. Может помочь изменение данной партии с ALT+ВЛЕВО/ВПРАВО. Creating event failed Не удалось создать соытие Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Не могу создать событие, так как выбранная партия находится не в том же треке, и выбранный инструмент может быть или нигде, или во многих партиях, что сомнительно. Выберите партию назначения и попробуйте снова. Recording event failed Запись события не удалась Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Не могу записать событие, так как выбранная партия находится не в том же треке, и выбранный инструмент может быть или нигде, или во многих партиях, что сомнительно. Выберите партию назначения и попробуйте снова. Internal error Внутренняя ошибка Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. Бр-р, абсолютно невозможная внутренняя ошибка все же случилась. Проверьте вывод консоли. Ничего не записалось. Drum map Карта ударных Reset the track's drum map with instrument defaults? Заменить инструменты в карте инструментами по умолчанию? Note: Velocity: Start (bar): Start (time): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete LMB: Delete Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete. Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help. MusEGui::DrumEdit mute instrument заглушить инструмент sound name название звука volume percent громкость в процентах quantisation квантование this input note triggers the sound эта входящая нота включает звук note length длина ноты this is the note which is played нота, которая воспроизводится output channel (hold ctl to affect all rows) канал выхода (удерживайте ctrl, чтобы воздействовать на все ряды) output port (hold ctl to affect all rows) порт выхода (удерживайте ctrl, чтобы воздействовать на все ряды) shift + control key: draw velocity level 1 клавиши shift+control: рисовать уровень 1 скорости нажатия control key: draw velocity level 2 Клавиша control: рисовать уровень 2 скорости нажатия shift key: draw velocity level 3 Клавиша shift: рисовать уровень 3 скорости нажатия draw velocity level 4 рисовать уровень 4 скорости нажатия output channel (ctl: affect all rows) канал выхода (ctrl: действует на все ряды) output port (ctl: affect all rows) порт выхода (ctrl: действует на все ряды) &File &Файл Load Map Загрузить карту Save Map Сохранить карту Reset GM Map Настроить карту GM по умолчанию &Edit &Правка Cut Вырезать Copy Копировать Copy events in range Копировать события в диапазоне Paste Вставить Paste (with Dialog) Вставить (с диалогом) Delete Events Удалить события &Select &Выделить Select All Выделить всё Select None Снять выделение Invert Инвертировать Inside Loop Внутри цикла Outside Loop Вне цикла Previous Part Предыдущая партия Next Part Следующая партия Fu&nctions &Функции Re-order list Изменить порядок списка Set Fixed Length Установит Фиксир. Длину Modify Velocity Изменить Скорость наж Crescendo/Decrescendo Крещендо/Диминуэндо Quantize Квантовать Erase Event Удалить событие Move Notes Передвинуть ноты Delete Overlaps Удалить наложения &Plugins &Плагины Window &Config Конфигурация &окна Drum tools Инструменты ударных Load Drummap Загрузить карту ударных Store Drummap Сохранить карту ударных Step Record Пошаговая запись Midi Input MIDI-вход cursor tools инструменты курсора Set step size for cursor edit Установить размер шага для курсора ctrl ctrl Add Controller View Добавить панель M mute M Sound Звук Vol Громк QNT КВНТ E-Note E-Нота Len Дл A-Note A-Нота Ch Кан Port Порт LV1 УР1 LV2 УР2 LV3 УР3 LV4 УР4 Muse: Load Drum Map Muse: Загрузить карту ударных MusE: Store Drum Map Muse: Сохранить карту ударных Drum map Карта ударных Reset the drum map with GM defaults? Сбросить карту ударных на GM? hide instrument скрыть инструмент Re-order map Переписать карту Group Группировать Don't group Не группировать Group by channel группировать по каналам Group maximally Группировать максимально Show/Hide Показать/Скрыть Also show hidden instruments Также показать скрытые инструменты Show all instruments Показать все инструменты Hide all instruments Скрыть все инструменты Only show used instruments Показать только используемые инструменты Only show instruments with non-empty name or used instruments Показать только инструментыс не пустым именем или используемые Drum map tools Инструменты карты барабанов Cursor step: Шаг курсора: H hide H Not all parts are displayed Показаны не все партии You selected both old-style-drumtracks and others (that is: new-style or midi tracks), but they cannot displayed in the same drum edit. I'll only display the old-style drumtracks in this editor, dropping the others. Вы выбрали треки в старом стиле вместе с другими (в новом стиле или midi треки), но они не могут быть показаны в той же редакции ударных. Показываю только треки в старом стиле, остальные опускаю. Play Events Воспроизвести события Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) Paste to current part &Display Step record Midi input Play events Cursor tools Add controller view Event List... Display menu: View-specific display options. Cursor step Note which triggers the sound Note which is played Override track output channel (hold CTRL to affect all rows). Override track output port (hold CTRL to affect all rows). CTRL + META keys: Draw velocity level 1 META key: Draw velocity level 2 META + ALT keys: Draw velocity level 4 &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. MusEGui::EditCAfterDialog MusE: Enter Channel Aftertouch MusE: Ввести Channel Aftertouch Time Position Позиция по времени Pressure Давление MusEGui::EditEventDialog Ok Ok Cancel Отмена MusEGui::EditInstrument MusE: Create file failed MusE: Не удалось создать файл MusE: Write File failed MusE: Не удалось записать файл MusE: Save Instrument Definition MusE: Сохранить Определение Инструмента Instrument Definition (*.idf) Определение инструмента (*.idf) MusE: Save instrument as MusE: Сохранить инструмент как Enter a new unique instrument name: Введите неповторяющееся имя инструмента: The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? Инструмент пользователя '%1' уже существует. Файл инструмента .idf будет переписан. Вы уверены? MusE: Bad instrument name MusE: Неверное имя инструмента Please choose a unique instrument name. (The name might be used by a hidden instrument.) Пожалуйста, выберите неповторяющееся имя для инструмента. (Имя может использоваться в скрытых инструментах) MusE: Bad patch name MusE: Неверное имя патча Please choose a unique patch name Пожалуйста, выберите неповторяющееся имя патча MusE: Bad patchgroup name MusE: Неверное имя группы Please choose a unique patchgroup name Пожалуйста, выберите неповторяющееся имя группы MusE: Bad controller name MusE: Неверное имя контроллера Please choose a unique controller name Пожалуйста, выберите неповторяющееся имя для контроллера MusE: Cannot add common controller MusE: Невозможно добавить обычный контроллер A controller named '%1' already exists. Контроллер '%1' уже существует. A controller number %1 already exists. Контроллер номер '%1' уже существует. MusE MusE The current Instrument contains unsaved data Save Current Instrument? Текущий Инструмент содержит несохраненные данные Сохранить Инструмент? &Save &Сохранить &Nosave &Не сохранять &Abort &Отмена Name Имя Vol Громк Quant Квант E-Note E-Нота Len Дл A-Note A-Нота LV1 УР1 LV2 УР2 LV3 УР3 LV4 УР4 Control7 Контрл7 Control14 Контрл14 RPN RPN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Высота тона Program Программа PolyAftertouch Aftertouch Посленажатие Tick Тик Data Данные New controller: Error Error! All control numbers are taken up! Clean up the instrument! Use note offs No note offs Convert to 0-vel note ons H hide H M mute M Ch Кан Port Порт Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) MusEGui::EditMetaDialog MusE: Enter Meta Event MusE: Ввести метасобытие Time Position Позиция по времени Meta Type Метатип Enter Hex Ввести Hex MusEGui::EditPAfterDialog MusE: Enter Poly Aftertouch MusE: Ввести полиф. посленажатие Time Position Позиция по времени Pitch Высота тона Pressure Давление MusEGui::EditToolBar select Pointer Tool: with the pointer tool you can: select parts move parts copy parts выбрать инструмент "Указатель": при помощи Указателя можно: выделять партии перемещать партии копировать партии select Pencil Tool: with the pencil tool you can: create new parts modify length of parts выбрать инструмент "Карандаш": инструментом Карандаш вы можете: создавать новые партии изменять длину партий select Delete Tool: with the delete tool you can delete parts выбрать инструмент "Ластик": инструментом "Ластик" вы можете удалять партии select Cut Tool: with the cut tool you can split a part выбрать инструмент "Ножницы": инструментом Ножницы вы можете резать партию select Glue Tool: with the glue tool you can glue two parts выбрать инструмент "Клей": инструментом Клей вы можете соединять партии select Score Tool: выбрать инструмент партитуры: select Quantize Tool: insert display quantize event выбрать инструмент "Квантование": квантует отображаемые события select Drawing Tool выбрать инструмент "Рисование" select Muting Tool: click on part to mute/unmute выбрать инструмент "Без звука": Щелкните по партии, чтоб переключать "Без зв./Звук" Manipulate automation Управлять автоматизацией Cursor tool Инструмент Курсор pointer указатель pencil карандаш eraser ластик cutter ножницы score партитура glue клей quantize квантовать draw рисовать mute parts заглушить партию edit automation править автоматизацию cursor курсор Edit Tools Инструменты правки Select Pointer tool: With the pointer tool you can: select parts move parts copy parts Select Pencil tool: With the pencil tool you can: create new parts modify length of parts Select Delete tool: With the delete tool you can delete parts Select Cut tool: With the cut tool you can split a part Select Glue tool: With the glue tool you can glue two parts Select Score tool: Select Quantize tool: Insert display quantize event Select Drawing tool Select Muting tool: Click on part to mute/unmute Select Cursor (tracker mode) tool: With the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Select Range tool Select Panning tool Select Zoom tool Pointer Pencil Eraser Cutter Glue Range Диапазон Pan Пан Zoom Score Партитура Quantize Квантовать Draw Mute parts Edit automation Cursor Курсор Select Stretch tool Select Sample rate tool Stretch Sample rate Частота дискретизации MusEGui::EffectRack effect rack рэк эффектов new новый change изменить move up передвинуть выше move down передвинуть ниже remove удалить bypass обойти show gui показать ГИП show native gui показать родной ГИП save preset сохранить пресет MusE: Save Preset MusE: Сохранить Пресет Replace effect Заменить эффект Do you really want to replace the effect %1? Вы действительно хотите заменить эффект %1? Presets Пресеты New Новый Change Изменить Move up Move down Remove Удалить Bypass Show gui Show native gui Save preset Сохранить пресет Effect rack Double-click a slot to insert FX Effect rack: Double-click a slot to insert/edit effect. RMB to open context menu. Press F1 for help. MusEGui::GlobalSettingsConfig MusE: Choose start template or song MusE: Начать с шаблона или сонга MusEGui::Header Track Info Columns Трек-инфо Custom column MusEGui::LCDPatchEdit High bank: Low bank: Program (Ctrl-double-click on/off) off выкл High bank Low bank Program Программа MusEGui::LMaster MusE: Mastertrack MusE: Мастер-трек &Edit &Правка Insert Tempo Вставить темп Insert Signature Вставить размер Insert Key Вставить ключ Edit Positon Изменить позицию Edit Value Изменить значение Delete Event Удалить событие Window &Config Кофигурация &окна Edit tools Инструменты правки Tempo Темп Timesig Размер Key Ключ new tempo новый темп new signature новый размер new key новый ключ Meter Индикатор Time Время Type Тип Value Значение Reposition of the initial tempo and signature events is not allowed Изменение начального темпа и размера запрещено MusE: List Editor MusE: Редактор списка Input error, conversion not OK or value out of range Ошибка ввода, неверная конверсия или значение вне диапазона Reposition of tempo and signature events to start position is not allowed! Изменение темпа и размера запрещено! Signature Размер Position Позиция Delete Удалить Insert tempo change Insert time signature change Insert key change Edit position Edit value Delete event MusEGui::ListEdit insert Note вставить Ноту insert SysEx вставить SysEx insert Ctrl Вставить кнтрл insert Meta вставить Meta insert Channel Aftertouch вставить Channel посленажатие insert Poly Aftertouch вставить Полиф. Посленаж &Edit &Правка Cut Вырезать Copy Копировать Paste Вставить Delete Events Удалить события Window &Config Конфигурация &окна Insert tools Инструменты вставки Tick Тик Bar Такт Type Тип Ch Кан Val A Знач. А Val B Знач. B Val C Знач. С Len Дл Comment Комментарий Increase Tick Увеличить Тик Decrease Tick Уменьшить Тик MusE: List Editor MusE: Редактор списка Insert Note Вставить ноту Insert SysEx Вставить SysEx Insert Ctrl Вставить кнтрл Insert Meta Вставить Meta Note Нота SysEx Ctrl Meta Tick+ Tick- Delete Удалить Insert note event Insert system exclusive event Insert controller event Insert meta event Increase tick value Decrease tick value Delete event MusEGui::MPConfig Default input connections Вх. соединения по умолч Are you sure you want to apply to all existing midi tracks now? Уверены, что хотите применить ко всем существующим midi трекам? Default output connections Вых. соединения по умолч Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? Настройка будет применена к новым midi трекам. Хотите применить её ко всем уже существующим midi трекам? MusE: bad device name MusE: неверное имя устройства please choose a unique device name пожалуйста, выберите неповторяющееся имя для устройства in in out out Show first aliases Показать первые псевдонимы Show second aliases Показать вторые псевдонимы Toggle all Переключить все Change all tracks now Изменить все треки Create Jack device Создать Jack-устройство Port Number Номер порта Enable gui Разрешить ГИП Enable reading Разрешить чтение Enable writing Разрешить запись Port instrument Инструмент Midi device name. Click to edit (Jack) Имя устройства midi. Щелкните для правки (Jack) Connections from Jack Midi outputs Соединения с midi-выходами Jack Connections to Jack Midi inputs Соединения с midi-входами Jack Auto-connect these channels to new midi tracks Авто-соединение этих каналов с новыми midi-треками Auto-connect new midi tracks to these channels Авто-соединение новых midi-треков с этими каналами Auto-connect new midi tracks to this channel Авто-соединение новых midi-треков с этим каналом Device state Состояние устройства Enable gui for device Разрешить ГИП для устройства Enable reading from device Разрешить чтение с устройства Enable writing to device Разрешить запись на устройство Name of the midi device associated with this port number. Click to edit Jack midi name. Имя midi-устройства связано с этим номером порта "Jack". Щелкните для правки. Instrument connected to port Инструмент Connections from Jack Midi output ports Соединения с выходными midi-портами Jack Connections to Jack Midi input ports Соединения с входными midi-портами Jack Auto-connect these channels, on this port, to new midi tracks. Авто-соединение этих каналов, этого порта с новыми midi треками. Connect new midi tracks to these channels, on this port. Соединять новые midi треки с этими каналами, этого порта. Connect new midi tracks to this channel, on this port. Соединять новые midi треки с этим каналом, этого порта. State: result of opening the device Состояние: результат открытия устройства Port Порт GUI ГИП I Вх O Вых Instrument Инструмент Device Name Имя устройства In routes Вх. маршруты Out routes Вых. маршруты Def in ch вх кан по ум Def out ch вых кан по ум State Состояние <unknown> <неизвестно> <none> <ничего> Remove Удалить Midi device name Midi device type Connections from Jack Midi Connections to Jack Midi Result of opening the device: OK: Assigned to a port and in use Closed: Unassigned to a port, or closed R/W Error: Unable to open for read or write Unavailable: USB midi unplugged, or external application not running, or synth plugin not installed etc. (Jack Midi devices have 'unavailable ports' in the routes columns.) Unavailable devices or ports can be purged with 'Remove' or with the advanced router. Enable Graphical User Interface for device Connections from Jack Midi ports Connections to Jack Midi ports Type Тип In Out Please choose a unique device name Port number MusEGui::MTScale bar scale шкала тактов Bar scale Bar scale: Use mouse buttons to set position and range markers. Hold Shift to set (LMB) or delete (RMB) custom markers. MusEGui::MTScaleFlo bar scale шкала тактов Bar scale MusEGui::MarkerView MusE: Marker MusE: Маркер add marker добавить маркер delete marker удалить маркер &Edit &Правка Window &Config Конфигурация &окна edit tools инструменты правки Bar:Beat:Tick Такт:Доля:Тик Hr:Mn:Sc:Fr:Sf Чс:Мин:Сек:Фрм:Субфрм Lock Закрыть Text Текст Marker Properties Свойства маркера Add marker Delete marker Edit tools Инструменты правки Description Описание Edit tick Edit SMPTE timecode Toggle timecode editing Edit marker description <Description> MusEGui::Master Tools Инструменты MusEGui::MasterEdit MusE: Mastertrack MusE: Мастер-трек Window &Config Конфигурация &окна Enable master Разрешить мастер(а) Enable Разрешить Enable usage of master track Разрешить использование мастер-трека Info Инфо Cursor Курсор time at cursor position время в позиции курсора tempo at cursor position темп в позиции курсора Off Выкл Bar Такт Snap Привязка CurPos КурсПоз tempo at current position темп в текущей позиции time signature at current position размер в текущей позиции &Display Time at cursor position Tempo at cursor position Mastertrack Мастер-трек Display menu: View-specific display options. MusEGui::MetronomeConfig Reset accents: Resets all accents to the defaults (first in list) of the current preset category (Factory or User). Proceed? MusEGui::MidiAudioControl Control7 Контрл7 Control14 Контрл14 RPN RPN NPRN RPN14 RPN14 NRPN14 NRPN14 Pitch Высота тона Program Программа Aftertouch Посленажатие MusEGui::MidiComponentRack Pro Var Вар Rev Ревер Cho Хорус Pan Пан Program Программа VariationSend Вариационный посыл ReverbSend Посыл Реверберации ChorusSend Посыл Хоруса Pan/Balance Пан/Баланс Controller Контроллер (Ctrl-double-click on/off) <unknown> <неизвестно> Instrument Инструмент Transpose Транспонировать Transpose notes up or down Delay Задержка Offset playback of notes before or after actual note Length Длина Change note length in percent of actual length Velocity Скорость нажатия <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> Compress Compress the notes velocity range, in percent of actual velocity <Patch> MusEGui::MidiInputTransformDialog New Новый MusEGui::MidiStrip off выкл VariationSend Вариационный посыл Var Вар ReverbSend Посыл Реверберации Rev Ревер ChorusSend Посыл Хоруса Cho Хорус dB дБ Pan/Balance Пан/Баланс Pan Пан record запись mute без звука solo mode соло input routing маршрут входа output routing маршрут выхода ctrl-double-click on/off ctrl-двойной щелчок вкл/выкл B Palette B B Record arm Mute or proxy mute Solo or proxy solo Track off Input monitor Pass input through to output Intput routing. Press F1 for help. Output routing. Press F1 for help. Inst Инстр Prop Instruments and controllers Properties Свойства Volume/Gain (Ctrl-double-click on/off) Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. MusEGui::MidiSyncConfig Port Number Номер порта Name of the midi device associated with this port number Имя MIDI-устройства, ассоциированного с номером этого порта Midi clock input detected Обнаружен вход midi-clock Midi tick input detected Обнаружен вход midi-тика Midi real time input detected Обнаружен midi-вход реального времени MMC input detected Обнаружен вход MMC MTC input detected Обнаружен вход MTC Detected SMPTE format Обнаруженые SMPTE форматы Receive id number. 127 = Global. Double click to edit. Получить id номер. 127 = Общий. Двойной щелчок - правка. Accept midi clock input Принять вход Midi-clock Accept midi real time input Принять midi-вход реального времени Accept MMC input Принять вход MMC Accept MTC input Принять вход MTC Receive start rewinds before playing Получить перед воспроизведением сообщение о начале перемотки Transmit id number. 127 = Global. Double click to edit. Передать номер id. 127 = Общий. Двойной щелчок - правка. Send midi clock output Послать выход midi-clock Send midi realtime output Послать midi-выход реального времени Send MMC output Послать выход MMC Send MTC output Послать выход MTC Midi clock input detected. Current port actually used is red. Click to force a port to be used. Обнаружен вход midi-clock. Фактически используемый текущий порт - красный. Щелкните, чтобы принудительно использовать порт. Midi realtime input detected, including start/stop/continue, and song position. Обнаружен midi-вход реального времени, с "старт/стоп/продолжить" и позицией проекта. MMC input detected, including stop/play/deferred play, and locate. Обнаружен MMC-вход c "старт/стоп/отложенное воспроизведение" и локацией. MTC input detected, including forward quarter-frame sync and full-frame locate. Current port actually used is red. Click to force a port to be current. Обнаружен MTC-вход, с четверть-фреймовой синхронизацией и полно-фреймовой локацией. Фактически используемый текущий порт - красный. Щелкните, чтобы принудительно сделать порт текущим. Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detects format of MTC quarter and full frame, and MMC locate. Обнаруженые форматы SMPTE: 24fps, 25fps, 30fps с пропущенным фреймом, или 30fps без пропущенного фрейма Обнаруживает MTC-формат четверть и полно-фреймовый, и MMC локацию. Receive id number. 127 = global receive all, even if not global. Получить номер id. 127 =общий получает все, даже если не общий. Accept midi clock input. Only one input is used for clock. Auto-acquire: If two or more port realtime inputs are enabled, the first clock detected is used, until clock is lost, then another can take over. Best if each turns off its clock at stop, so MusE can re-acquire the clock from another port. Click on detect indicator to force another. Принять вход midi-clock. Только один вход используется для "clock". Авто-получение: если разрешены два или более входов реального времени на порт, то будут использован первый обнаруженный "clock", пока он не будет потерян, затем будет использован другой. Самое лучшее, если каждый отключит свой "clock" во время остановки, чтобы MusE снова получил "clock" с другого порта. Щелкните по индикатору обнаружения, чтобы сделать по другому. Accept midi realtime input, including start/stop/continue, and song position. Non-clock events (start,stop etc) are accepted by ALL enabled ports. This means you may have several master devices connected, and muse will accept input from them. Принять midi-вход реального времени с "старт/стоп/продолжить" и позицией проекта. События "Non-clock" (старт, стоп, и т. п.) принимаются ВСЕМИ разрешенными портами. Это означает, что вы можете иметь несколько подсоединенных мастер-устройств, и MusE примет вход от них. Accept MMC input, including stop/play/deferred play, and locate. Принять вход MMC, с "стоп/старт/отложенным воспроизведением" и локацией. Accept MTC input, including forward quarter-frame sync and full-frame locate. See 'rc' column for more help. Принять вход MTC, с четверть-фреймовой синхронизацией и полно-фреймовой локацией. См. колонку "rc" для получения помощи. When start is received, rewind before playing. Note: It may be impossible to rewind fast enough to synchronize with the external device. Когда сообщение "старт" получено, перемотать перед воспроизведением. Примечание: может быть так, что перемотка окажется недостаточно быстрой для синхронизации с внешним устройством. Transmit id number. 127 = global transmit to all. Передавать номер id. 127 = общая передача для всех. Send midi clock output. If 'Slave to External Sync' is chosen, muse can re-transmit clock to any other chosen ports. Посылать выход midi-clock. Если выбрано "Slave to External Sync", то MusE может снова передать "clock" на любой другой выбранный порт. Send midi realtime output, including start/stop/continue, and song position. If 'Slave to external sync' is chosen, muse can re-transmit midi realtime input messages to any other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. Послать midi-выход реального времени, с "старт/стоп/продолжить" и позицией проекта. Если выбрано "Slave to External Sync", то MusE может снова передать сообщения с midi-входа реального времени на любой другой выбранный порт. Это значит, что вы можете иметь несколько присоединенных ведомых устройств, и MusE может снова послать сообщения реального времени на любое из них или на все. Port Порт Device Name Имя устройства c c k k r r m m t t type тип rid rid rc rc rr rr rm rm rt rt rw rw tid tid tc tc tr tr tm tm tt tt MusE MusE Settings have changed Apply sync settings? Настройки изменились Применить настройки синхронизации? &Apply &Применить &No &Нет &Abort &Отмена <none> <ничего> None Ничего Tiny Очень маленький Small Маленький Large Большой Large with pre-detect Большой с пред-обнаружением Sync to this device. Click to select. Sync to this device. Click to select. MusE will synchronize to this device's clock or MTC quarter-frame, if 'slave to external sync' is on. MTC input detected, including forward quarter-frame sync and full-frame locate Accept midi clock input. Only one port can be used for clock sync. Accept MTC input, including forward quarter-frame sync and full-frame locate. Only one port can be used for sync. When start is received, rewind before playing. Note: In some cases, such as having many project audio tracks, it may be impossible to rewind fast enough to synchronize with the external device. Manually rewinding the device before playing is recommended. s с Type Тип MusEGui::MidiTrackInfo <unknown> <неизвестно> MusEGui::MidiTransformerDialog New Новый MusEGui::MixdownFileDialog Wave Files (*.wav);;All Files (*) Файлы Wave (*.wav);;Все файлы (*) MusEGui::MusE Failed to start audio! Не могу воспроизвести аудио! Was not able to start audio, check if jack is running. Не могу воспроизвести аудио, проверьте работает ли "Jack". Timeout waiting for audio to run. Check if jack is running. Ожидание запуска аудио. Проверьте, работает ли "Jack". Und&o &Отменить Re&do &Повторить undo last change to song отменить последнее изменение в проекте redo last undo повторить последнюю отмену Loop Цикл loop between left mark and right mark цикл между левым и правым маркерами Punchin Начало врезки record starts at left mark запись начинается с левого маркера Punchout Конец врезки record stops at right mark запись останавливается на правом маркере Start К началу rewind to start position перемотать к начальной позиции Rewind Перемотать назад rewind current position перемотать к текущей позиции Forward Перемотать вперёд move current position переместить текущую позицию Stop Остановить stop sequencer остановить секвенсер Play Воспроизвести start sequencer play начать воспроизведение в секвенсере Record Записать to record press record and then play чтобы начать запись нажмите "запись", а затем "воспроизвести" Panic Паника send note off to all midi channels посылает сообщение "note off" на все midi-каналы &New &Новый Create New Song Создать Новый Проект &Open &Открыть Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. Щелкните по этой кнопке для открытия другого проекта. Вы также можете выбрать пункт Открыть в меню "Файл". Open &Recent Открыть &Недавние &Save &Сохранить Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. Щелкните по этой кнопке для сохранения проекта. Будет предложено ввести имя файла. Вы также можете выбрать пункт "Сохранить" в меню "Файл". Save &As Сохранить &Как Import Midifile Импортировать MIDI-файл Export Midifile Экспортировать MIDI-файл Import Part Импортировать Партию Import Audio File Импортировать Аудио-файл Find unused wave files Найти не используемые аудио-файлы &Quit Вы&йти Song Info Информация о Проекте Transport Panel Панель транспорта Bigtime Window Окно хронометра Mixer A Микшер А Mixer B Микшер В Cliplist Список клипов Marker View Маркеры Arranger View Показывать Компоновщик Fullscreen Во весь экран &Plugins &Плагины Edit Instrument Редактировать Инструмент Input Plugins Плагины на входе Transpose Транспонировать Midi Input Transform Преобразовать входящие Midi-события Midi Input Filter Фильтр входящих Midi-событий Midi Remote Control Дистанционное управление Midi Rhythm Generator Генератор Ритма Reset Instr. Перезагрузить инстр. Init Instr. Запустить инстр. Local Off Отключить локальное управление Bounce to Track Свести в трек Bounce to File Свести в файл Restart Audio Перезапустить аудио Mixer Automation Автоматизация микшера Take Snapshot Сделать снимок Clear Automation Data Удалить Данные Автоматизации Cascade Каскадом Tile Черепицей In rows В ряд In columns Колонками Global Settings Общие настройки Configure Shortcuts Настроить горячие клавиши Follow Song Следовать за проектом Don't Follow Song не следовать за проектом Follow Page следовать постранично Follow Continuous следовать непрерывно Metronome Метроном Midi Sync Midi-Cинхр Midi File Import/Export Импорт/Экспорт midi-файла Appearance Settings Настройки внешнего вида Midi Ports / Soft Synth MIDI-порты/Прогр. синтезаторы &Manual &Руководство &MusE Homepage &Домашняя страница MusE &Report Bug... &Сообщить об ошибке... &About MusE &О программе MusE File Buttons Файловые кнопки Undo/Redo Отменить/Вернуть Transport Транспорт &File &Файл &View &Вид &Midi &Midi &Audio &Аудио A&utomation &Автоматизация &Windows &Окна Se&ttings Н&астройки &Help &Справка Cannot read template Не могу прочитать шаблон File open error Ошибка открытия файла File read error Ошибка прочтения файла Unknown File Format: %1 Неизвестный Формат Файла: %1 MusE: Song: %1 MusE: Проект: %1 MusE: load project MusE: Открыть проект MusE: load template MusE: открыть шаблон MusE: Write File failed MusE: Не удалось записать файл The current Project contains unsaved data Save Current Project? Текущий проект содержит не сохранённые данные. Сохранить текущий проект? S&kip Не &сохранять &Cancel &Отмена MusE: Save As MusE: Сохранить как Nothing to edit Нечего редактировать MusE: Bounce to Track MusE: Свести в Трек No wave tracks found wave-треки не найдены No audio output tracks found Выходные аудио треки не найдены Select one audio output track, and one target wave track Выберите один трек аудио выхода и один wave-трек, как цель Select one target wave track Выберите один wave-трек, как трек назначения Select one target wave track, and one audio output track Выберите один wave-трек, как цель, (трек назначения) и один трек аудио выхода MusE: Bounce to File MusE: Свести в файл Select one audio output track Выберите один трек аудио выхода MusE: Bounce MusE: Свести set left/right marker for bounce range настройте левый/правый маркеры для установки диапазона сведения The current Project contains unsaved data Load overwrites current Project: Save Current Project? Текущий проект содержит не сохранённые данные. Загрузка нового проекта приведёт к перезаписи данных текущего проекта. Сохранить текущий проект? &Abort &Отмена MusE: Export Midi MusE:Экспорт MIDI no help found at: справка не найдена в: MusE: Open Help MusE: Открыть справку Unable to launch help Невозможно открыть справку For some reason MusE has to launch the default browser on your machine. По некоторым причинам MusE вынужден запустить на вашей машине броузер по умолчанию. MusE: Import Midi MusE: Импорт MIDI Add midi file to current project? Добавить midi файл в текущий проект? &Add to Project &Добавить в Проект &Replace &Заменить reading midifile читается Midi-файл failed: не удалось: Import part is only valid for midi and wave tracks! Импортирование партий допустимо только для midi и wave треков! MusE: load part MusE: открыть партию No track selected for import Не выбран трек для импорта %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. %n парти(я, и, ий) из %1 не могут быть импортированы. Возможно выбранный трек неверного типа. %n part(s) could not be imported. Likely the selected track is the wrong type. %n парти(я, и, ий) могут быть импортированы. Возможно выбранный трек неверного типа. to import an audio file you have first to selecta wave track чтобы импортировать аудио файл, вы должны сначала выбрать wave-трек Import Wavefile Импортировать wave-файл This wave file has a samplerate of %1, as opposed to current setting %2. Do you still want to import it? Samplerate этого wave-файла %1, в то время как текущий Samplerate %2. Хотите вы всё же импортировать его? &Yes &Да &No &Нет Song Position Позиция Сонга Tempo Темп Signature Размер About &Qt О &Qt This will clear all automation data on all audio tracks! Proceed? Удалится вся автоматизация во всех аудио треках! Продолжить? This takes an automation snapshot of all controllers on all audio tracks, at the current position. Proceed? Будет сделан снимок автоматизации всех контроллеров во всех аудио треках в текущей позиции. Продолжить? MusE: Song: MusE: Warning The song uses multiple ports but export format 0 (single track) is set. The first track's port will be used. Playback will likely be wrong unless the channels used in one port are different from all other ports. Canceling and setting a different export format would be better. Continue? Wave import error There are too many wave files of the same base name as imported wave file Can not continue. Can't create new wav file in project folder! Failed to initialize sample rate converter! Cancel Отмена Resampling wave file "%1" from %2 to %3 Hz... Output has clipped Resampling again and normalizing wave file "%1" Try %2 of %3... File version is %1.%2 Current version is %3.%4 Conversions may be applied if file is saved! Opening file Do not warn again Cpu load Загрузка ЦП Measured CPU load Загрузка ЦП No CPU load data Нет данных о загрузке ЦП To import an audio file you have first to select a wave track Reading midifile Load configuration colors Color settings will immediately be replaced with any found in the file. Are you sure you want to proceed? &Ok &ОК Save configuration colors Timeout waiting for audio to run. Check if jack is running or try another driver. Was not able to start audio, check if jack is running or try another driver. Undo last change to song Redo last undo Loop between left mark and right mark Punch in Record starts at left mark Punch out Record stops at right mark Rewind to start position Rewind current position Move current position Stop sequencer Restart rec Start sequencer play To record press record and then play Turn on/off metronome Create new song New from &Template... Create new song from template &Open... &Открыть... Open song from file Click this button to open an existing song. Save current song Click this button to save the song you are editing. You will be prompted for a file name. Save &As... Сохранить &как... &Close Import Midifile... Export Midifile... Import Part... Import Audio File... Find Unused Wave Files... Song Info... Mastertrack Мастер-трек Edit Instrument... Transpose... Midi Input Transform... Midi Input Filter... Midi Remote Control... Reset Instrument Init Instrument Bounce to File... Global Settings... Appearance... Rewind on Stop Metronome... Midi Sync... Midi File Import/Export... &MusE Homepage... &Did You Know? &About MusE... Snooper (developer tool)... About &Qt... Set left/right marker for bounce range The project has no project sample rate (added 2011). Please enter a rate. The current system rate (%1Hz) is suggested, and cancelling uses it: The project has audio waves, but no project sample rate (added 2011). Please enter a rate. The most common wave rate found is suggested, the project was probably made with it. Cancelling uses the current system rate (%1Hz): Project sample rate This wave file has a samplerate of %1 Hz, as opposed to current setting %2 Hz. A live, real-time samplerate converter can be used on this file. Or, a copy of the file can be resampled now from %1 Hz to %2 Hz. Do you want to use a converter or resample the file now? Use live converter Resample now Unable to launch browser Error launching default browser Show Docks Toggle display of currently visible dock windows. Display MusE main window in full screen mode. Mastertrack Graphic... Send 'note-off' command to all midi channels. Send initialization messages as found in instrument definition. Take Automation Snapshot Keyboard Shortcuts... Midi Ports / Soft Synths... The current project contains unsaved data. Save current project? No valid parts selected The current project contains unsaved data. Load overwrites current project. Save current project? Recording starts at left mark Recording stops at right mark Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help. Metronome on/off. Press F1 for help. Send 'local-off' command to all midi channels. &Manual (wiki)... File buttons Timeline Recording Sync Синхр Position Позиция MusEGui::NoteInfo Note Info Информация о ноте Start Начало Len Дл Pitch Высота тона Velo On Скор. наж Velo Off Скор. снятия delta/absolute mode отн./абсолютный режим Delta/Absolute mode Velo on Velo off MusEGui::PartCanvas Cannot copy/move/clone to different Track-Type Невозможно копировать/перемещать/клонировать данные в разные типы дорожек C&ut &Вырезать &Copy С&копировать s&elect в&ыбрать clones клоны rename переименовать color цвет delete удалить split разделить glue клей super glue (merge selection) супер клей (смешать выделенное) de-clone де-клонировать save part to disk сохранить партию на диске wave edit правка сэмплов file info Информация о файле MusE: save part MusE: сохранить партию Part name: %1 Files: Имя партии:%1 Файлы: %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. %n парти(я)(и)(й) из %1 не могут быть вставлены. Неверный формат, выбранного трека. %n part(s) could not be pasted. Likely the selected track is the wrong type. %n парти(я)(и)(й) не могут быть вставлены. Неверный формат, выбранного трека. Cannot paste: multiple tracks selected Вставка невозможна: выбрано несколько треков Cannot paste: no track selected Вставка невозможна: нет выбранного трека Can only paste to midi/drum track Можно вставить только в midi/drum трек Can only paste to wave track Можно вставить только в wave-трек Can only paste to midi or wave track Можно вставить только в midi или wave треки Cannot paste: wrong data type Вставка невозможна: неверный тип данных Remove selected Удалить выбранное Automation: Автоматизация: Normalize Нормализовать S&elect Rename Color Delete Удалить Split Glue Super glue (merge selection) De-clone Save part to disk... Part Wave edit... File info... MusE: Save part Automation Автоматизация LMB: Select/Move/Dblclick to edit | CTRL+LMB: Multi select/Move&Copy | CTRL+ALT+LMB: Dblclick to edit in new window | SHIFT+LMB: Select track | MMB: Delete LMB: Draw to resize | MMB: Delete | CTRL+RMB: Trim length LMB: Delete | CTRL+RMB: Trim length LMB: Cut part in two LMB: Merge with following part LMB: Mute selected part LMB: Edit automation events in audio parts Part canvas: Use Pencil tool to draw parts. Double-click to create a new MIDI/drum part between the range markers (set with MMB + RMB). Press F1 for help. MusEGui::PasteDialog %n quarter(s) %n четверть(и) %1 quarter for floating-point arguments like 1.5 %1 четверть %1 quarters for floating-point arguments like 1.5 %1 четверти MusEGui::PasteEventsDialog %n quarter(s) %n четверть(и) %1 quarter for floating-point arguments like 1.5 %1 четверть %1 quarters for floating-point arguments like 1.5 %1 четверти MusEGui::Piano Left click: Play Right click: Select key Piano: Press key to play or enter events in step record mode (SHIFT for chords). RMB: Set cursor for polyphonic control events. CTRL+Mousewheel to zoom view vertically. MusEGui::PianoCanvas Note: Velocity: Start (bar): Start (time): Length (ticks): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete | CTRL+RMB: Trim length LMB: Resize | CTRL+LMB: Multi select | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length LMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length Pianoroll canvas: Use Pencil tool to draw and edit MIDI events, Pointer tool to select and edit. Press F1 for help. MusEGui::PianoRoll &Edit &Правка C&ut &Вырезать &Copy С&копировать Copy events in range Копировать события в диапазоне &Paste &Вставить Paste (with dialog) Вставить (с диалогом) Delete &Events Удалить &События &Select &Выделить Select &All Выделить &Всё &Deselect All &Снять выделение Invert &Selection Инвертировать &Выделенное &Inside Loop &Внутри цикла &Outside Loop &Вне цикла &Previous Part &Предыдущая партия &Next Part &Следующая партия Fu&nctions &Функции Quantize Квантовать Modify Note Length Изменить Длину Ноты Modify Velocity Изменить скорость наж Crescendo/Decrescendo Крещендо/Диминуэндо Transpose Транспонировать Erase Events Удалить События Move Notes Передвинуть Ноты Set Fixed Length Установить Фиксир. Длину Delete Overlaps Удалить наложения Legato Легато &Plugins &Плагины Window &Config Конфигурация &окна &Event Color &Цвет события &Blue &Синий &Pitch colors &Цвета высоты тона &Velocity colors &Цвета скорости нажатия Pianoroll tools Инструменты пианоролла Step Record Пошаговая запись Midi Input Midi-вход Play Events Воспроизвести события ctrl ctrl Add Controller View Добавить панель Paste to current part &Display Step record Midi input Play events Event List... Display menu: View-specific display options. Add controller view Play single note Play chords &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. MusEGui::PluginDialog MusE: select plugin MusE: выбрать плагин Lib Библ Label Метка Name Имя AI AI AO AO CI CI CO CO IP IP id id Maker Создатель Copyright Copyright Ok Ok Cancel Отмена Mono and Stereo Моно и Стерео Stereo Стерео Mono Моно Show All Показать Все Search in 'Label' and 'Name': Искать по "Меткам" и "Именам": Type Тип Audio inputs Аудио входы Audio outputs Аудио выходы Control inputs Контрольные входы Control outputs Контрольные выходы In-place capable In-place capable ID number Номер ID Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. Выбирает типы плагинов, которые будут показаны в списке.<br>Использование моно плагинов на стерео треках не проблема, два пойдут в параллель.<br>В категории "all" могут встретиться плагины не для рэка эффектов. dssi synth dssi синт dssi effect dssi эффект LV2 synth LV2 синт LV2 effect LV2 эффект ladspa ladspa Show plugs: Показать плагины: Plugin categories. Right-click on tabs to manage. Right-click on plugins to add/remove from a category. Associated categories You need to define some categories first. Enter the new group name Wine VST FB 2B 5 1/3' {2B?} Requires fixed block size Requires power-of-2 block size &Create new group &Delete currently selected group Re&name currently selected group New group Новая группа VST synth VST effect MusE: Select Plugin MusEGui::PluginGui File Buttons Файловые кнопки Load Preset Загрузить пресет Save Preset Сохранить пресет bypass plugin обойти плагин MusE: load preset MusE: загрузить пресет Error reading preset. Might not be right type for this plugin Ошибка прочтения пресета. Возможно неверный тип для этого плагина MusE: save preset MusE: сохранить пресет Bypass plugin Plugin settings MusE: Save preset Plugin Settings MusEGui::PluginSettings Global setting: On Вкл Off Выкл MusEGui::PopupMenu <More...> %1 MusEGui::PosToolbar Left marker Left marker position Right marker Right marker position Current position Current position in bars/beats SMPTE position Current position in SMPTE time Show/Hide position in ticks and audio frames Show/Hide additional output fields displaying the current position in ticks and audio frames Current position in ticks Current position in audio frames Current position slider MusEGui::ProjectCreateImpl Select directory Выбрать папку MusEGui::RasterLabelCombo Snap Привязка MusEGui::RecToolbar Record mode Record mode: Overdub to add new events, Replace to replace overlapping events. Overdub Наложить Replace Заменить Cycle record mode Cycle record mode: Normal to replace range when loop is finished, Mix to add new events, Replace to replace range on first MIDI input. Normal Нормальный Mix Смешать MusEGui::RouteDialog Normal Нормальный Alias 1 Alias 2 Source Источник Destination Назначение <none> <ничего> MusEGui::RoutePopupMenu Channel Канал Soloing chain Соло цепи Audio returns Аудио возвраты Warning: No input devices! Предупреждение: Нет входных устройств! Open midi config... Открыть midi конфиг... <none> <ничего> Toggle all Переключить все More... Ещё... Audio sends Аудио посылы Midi port sends Посылы midi порта Channel grouping: Mono Stereo Стерео Channels Omni Show aliases: First Second Show names Show first aliases Показать первые псевдонимы Show second aliases Показать вторые псевдонимы Jack ports Connect Соединить Unavailable Open advanced router... Output routes: Input routes: Tracks Destinations: Midi sends Sources: ALSA devices JACK devices Synth devices Warning: No output devices! Предупреждение: Нет устройства выхода! MusEGui::ScoreCanvas Treble Скрипичный Bass Басовый Grand Staff Большой стан Remove staff Удалить стан Ambiguous part Неопределенная партия There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. Существуют две или более партии, куда вы можете добавить ноту, но ни одна из них не совпадает с выбранной партией. Щелкните по любой ноте в партии, чтобы выбрать её как партию назначения и повторите попытку. Или добавьте нотный стан с партией назначения. No part Нет партии There are no parts you could add the note to. Нет партий, куда вы могли бы добавить ноту. Tools Инструменты MusEGui::ScoreEdit Step recording tools Инструменты пошаговой записи Step Record Пошаговая запись Note settings Настройки ноты Note length: Длина ноты: last Apply to new notes: Применить к новым нотам: Apply to selected notes: Применить к выбранным нотам: Velocity: Скорость наж: Off-Velocity: Скорость отп: Quantisation settings Настройки квантования Quantisation: Квантование: Pixels per whole: Пикселей на целую: &Edit &Правка C&ut &Вырезать &Copy С&копировать Copy events in range Копировать события в диапазоне &Paste &Вставить Paste (with dialog) Вставить (с диалогом) Delete &Events Удалить &События &Select &Выделить Select &All Выделить &Всё &Deselect All &Снять выделение Invert &Selection &Инвертировать выделенное &Inside Loop &Внутри цикла &Outside Loop &Вне цикла Fu&nctions &Функции &Quantize &Квантовать Change note &length Изменить длину &ноты Change note &velocity Изменить &скорость наж. ноты Crescendo/Decrescendo Крещендо/Диминуэндо Transpose Транспонировать Erase Events Удалить события Move Notes Передвинуть события Set Fixed Length Установить фикс. длину Delete Overlaps Удалить наложения Legato Легато Window &Config &Настройка окна Note head &colors &Цвет головки ноты &Black &Черный &Velocity &Скорость нажатия &Part &Партия Set up &preamble &Настроить преамбулу Display &key signature &Показывать знаки при ключе Display &time signature &Показывать тактовый размер Set Score &name Назн. имя &партитуры Enter the new score title Ввести название партитуры Error Ошибка Changing score title failed: the selected title is not unique Не удалось изменить название: Такое название уже существует Step record &Display Quantisation Pixels per whole Note length Velocity Скорость нажатия Apply to selected notes, or new notes if none is selected Off-Velocity Copy Events in Range Change Note &Length Change Note &Velocity Display menu: View-specific display options. Note Head &Colors &Default Set Up &Preamble Display &Key Signature Display &Time Signature Set Score &Name MusEGui::ScrollScale next page след. страница previous page предыд. страница current page number номер текущей страницы MusEGui::ShortcutCaptureDialog Ok Ok Cancel Отмена Shortcut conflicts with %1 "Горячая клавиша" конфликтует с %1 Undefined Не назначено MusEGui::ShortcutConfig Save printable text file Text files (*.txt);;All files (*) Error Ошибка Error opening file for saving Shortcuts for selected category: Legend: An error occurred while saving MusEGui::SigScale signature scale линейка ключей Signature scale MusEGui::SigToolbar Time signature at current position Signature: Размер: MusEGui::SigToolbarWidget time signature at current position размер в текущей позиции Signature: Размер: MusEGui::Strip Remove track? Удалить трек? Name Имя Enter track name: MusE: bad trackname MusE: неверное имя трека Please choose a unique track name Prefer knobs, not sliders Show values in controls Prefer midi volume as decibels Monitor on record-arm automatically Change track name Hide strip Visible audio effects Configuration Actions Действия MusEGui::SyncToolbar External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::TList <none> <ничего> visible видимые no clef без ключа Treble Скрипичный Bass Басовый Grand Большой стан MusE: bad trackname MusE: неверное имя трека please choose a unique track name Выберите неповторяющееся имя для трека Unused Devices Неиспользованные устройства Update drummap? Обновить карту ударных? Do you want to use same port for all instruments in the drummap? Вы хотите использовать один порт для всех инструментов в списке ударных? &Yes &Да &No &Нет show gui показать ГИП show native gui показать родной ГИП Treble clef Скрипичный Bass clef Басовый Grand Staff Большой стан Viewable automation Видимая автоматизация Delete Track Удалить трек Track Comment Комментарий к треку Insert Track Добавить трек Midi Midi Drum Ударные Do you want to use same port and channel for all instruments in the drummap? Вы хотите использовать тот же порт и канал для всех инструментов в списке ударных? off выкл <unknown> <неизвестно> Midi control Midi контроль Assign Назначить Clear Очистить Internal Внутренний Synth Синт Save track's drumlist Сохранить список ударных трека Save track's drumlist differences to initial state Сохранить изменения в списке ударных трека Load track's drumlist Загрузить список ударных трека Reset track's drumlist Сбросить список ударных трека в нач. состояние Reset track's drumlist-ordering Сбросить порядок ударных трека в нач. состояние Copy track's drumlist to all selected tracks Скопировать список ударных трека во все выбранные треки Copy track's drumlist's differences to all selected tracks Скопировать изменения в списке ударных трека во все выбранные треки Drum map Карта ударных Reset the track's drum map with instrument defaults? Заменить инструменты в карте инструментами по умолчанию? Reset the track's drum map ordering? Восстановить в карте ударных порядок по умолчанию? Muse: Load Track's Drum Map Muse: Загрузить карту ударных трека MusE: Store Track's Drum Map Muse: Сохранить карту ударных трека New style drum Барабан в новом стиле Presets Пресеты Clear all controller events? Очистить все события контроллеров? &Ok &ОК &Cancel Change color Other Другой clear automation очистить автоматизацию SYNTH IS UNAVAILABLE! Please choose a unique track name Show gui Show native gui Clear automation Delete Selected Tracks Удалить выделенные треки Track list: Use context menu to create tracks. Click track ID to select, CTRL to add, SHIFT for range, dblclick to select all of same type. Press F1 for help. MusEGui::TempoSig Tempo/Sig Темп/Размер Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Time signature at current position MusEGui::TempoToolbar Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Tempo: Темп: Tap Tap this button to set tempo Set the tempo to the time difference of two successive clicks. Times out after two seconds. MusEGui::TempoToolbarWidget tempo at current position темп в текущей позиции Tempo: Темп: MusEGui::Toolbar1 Off Выкл Solo Соло Cursor Курсор Snap Привязка MusEGui::TopWin As subwindow Как подокно Shares tools and menu Общие инструменты и меню Fullscreen Во весь экран Piano roll Пианоролл List editor Редактор списка Drum editor Редактор ударных Master track editor Редактор мастер-трека (график) Master track list editor Редактор мастер-трека (список) Wave editor Wave редактор Clip list Редактор списка клипов Marker view Редактор маркеров Score editor Партитурный редактор Arranger Компоновщик <unknown toplevel type> <неизвестный тип уровня> Undo/Redo tools Инструмент отменить/вернуть Panic Паника Transport Транспорт Song Position Позиция Сонга Tempo Темп Signature Размер Metronome Метроном Tabbed/Floating Display editor in a tab or in a separate window (preset in Global Settings->Editors). Timeline Recording Sync Синхр Position Позиция MusEGui::TrackComment MusE: Track Comment MusE: Комментарий к треку Track Comment: Комментарий к треку: MusEGui::Transport Overdub Наложить Replace Заменить Rec Mode Режим записи Normal Нормальный Mix Смешать Cycle Rec Циклич. запись punchin начало врезки loop цикл punchout конец врезки Punch In Начало врезки Loop Цикл Punch Out Конец врезки Left Mark Левый маркер Right Mark Правый маркер rewind to start перемотать к началу Click this button to rewind to start position Щелкните по этой кнопке для перемещения к началу rewind перемотать назад Click this button to rewind Щелкните по этой кнопке для перемотки назад forward перемотать вперёд Click this button to forward current play position Щелкните по этой кнопке для перемещения вперёд stop остановить Click this button to stop playback Щелкните по этой кнопке для остановки воспроизведения play воспроизвести Click this button to start playback Щелкните по этой кнопке, чтобы начать воспроизведение record запись Click this button to enable recording Щелкните по этой кнопке, чтобы разрешить запись AC AC quantize during record квантовать при записи Click Метроном metronom click on/off метроном вкл/выкл Sync Синхр external sync on/off Вкл/выкл внешн. синхр Jack Jack Jack transport sync on/off Синхр. транспорта "Jack" вкл/выкл Master Мастер use master track использовать мастер-трек Punch in Punch out Rewind to Start Rewind Перемотать назад Forward Перемотать вперёд Stop Остановить Play Воспроизвести Record Записать Metronome on/off External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::VisibleTracks Show wave tracks Показать аудио-треки Show group tracks Показать групповые треки Show aux tracks Показать aux треки Show input tracks Показать входные треки Show output tracks Показать выходные треки Show midi tracks Показать midi треки Show synth tracks Показать треки синтезаторов Visible track types Видимые типы треков MusEGui::WaveCanvas Adjust Wave Offset Wave offset (frames) Part creation failed Left and right position markers must be placed inside the current part. Part created The selected region has been copied to the clipboard and can be pasted in the arranger. MusE - external editor failed MusE - ошибка запуска внешнего редактора MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. "MusE" не может запустить внешний редактор. Проверьте, действительно ли в: Общие настройки->Аудио:Внешний редактор выбран правильный редактор. MusE - file size changed MusE - размер файла изменился When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted При редактировании во внешнем редакторе - не изменяйте размер файла так как он должен соответствовать выбранной области. Утерянные данные заглушены Wave event: Converter settings Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help. MusEGui::WaveEdit &Edit &Правка Func&tions Фу&нкции &Gain &Усиление Other Другой &Copy С&копировать C&ut &Вырезать &Paste &Вставить Edit in E&xternal Editor Редактировать во &внешнем редакторе Mute Selection Заглушить выделенное Normalize Selection Нормализовать выделенное Fade In Selection Нарастание звука в выделенном Fade Out Selection Затихание звука в выделенном Reverse Selection Перевернуть выделенное Select Выделить Select &All Выделить &Всё &Deselect All &Снять выделение Window &Config Конфигурация &окна WaveEdit tools Инструменты правки wave Solo Соло Cursor Курсор &Create Part from Region &Previous Part &Предыдущая партия &Next Part &Следующая партия &Event Color &Цвет события &Part colors &Gray Edit in E&xternal Editor... &Display Display menu: View-specific display options. MusEGui::WaveView MusE - external editor failed MusE - ошибка запуска внешнего редактора MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. "MusE" не может запустить внешний редактор. Проверьте, действительно ли в: Общие настройки->Аудио:Внешний редактор выбран правильный редактор. MusE - file size changed MusE - размер файла изменился When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted При редактировании во внешнем редакторе - не изменяйте размер файла так как он должен соответствовать выбранной области. Утерянные данные заглушены OrganGuiBase MusE: Organ MusE: Орган O-1 O-1 Oscillator Осциллятор Brass Медные духовые Reed Язычковые деревянные духовые Flute Флейта 16' 16' 4' 4' 2 2/3' 2 2/3' 2' 2' 8' 8' 5 1/3' 5 1/3' Release Послезвучие Sustain Стационарная часть звука Decay Спад огибающей Attack Атака ms мс Velocity Скорость нажатия Drawbars Слайдеры регистров Envelope Hi Верх огибающей cB cB Envelope Lo Низ огибающей PasteDialogBase MusE: Paste Parts MusE: Вставить партии Number and raster Номер и значение insert вставить times раз raster значение ticks тик(ов) Move, Merge, Clone Передвинуть, Смешать, Клонировать Move everything to the right Передвинуть всё вправо Move only affected parts to the right Передвинуть только задействованные партии вправо Put everything into a single track Поместить всё в один трек Merge with existing parts Смешать с существующими партиями Insert as clones (where possible) Вставить как клоны (где возможно) OK OK Cancel Отмена Insert Вставить Raster Значение PasteEventsDialogBase MusE: Paste Events MusE: Вставить события Number and raster Номер и значение insert вставить times раз raster значение ticks тик(ов) Paste options Опции вставки Always into existing parts Всегда в существующие партии Never into existing parts Никогда в существующие партии Into existing parts if part has not to be expanded by more than В существующие партии, если партия не будет расширена больше чем Put everything into the (selected) part Поместить всё в (выбранную) партию OK OK Cancel Отмена &Always into existing parts &Never into existing parts &Into existing parts if part has not to be expanded by more than Paste midi controller target options Keep e&xisting Erase existin&g Erase WYSIWYG Erase inclusive Insert Вставить Raster Значение PluginDialogBase Dialog Диалог Ports: Mono Моно Mono + Stereo Stereo Стерео All Все Plugin type: Filter: &OK &Cancel PluginSettings Dialog Диалог Options Fixed speed Transport affects audio latency Override reported audio latency Reported audio latency override value Revert HiDPI scaling of native GUI Set in Global settings->GUI tweaks Global setting Additional information Follow global setting On Вкл Off Выкл ProjectCreate Create Project Создать Проект Projects folder: Папка проекта: ... ... Project Name: Название проекта: Project is a Template Проект является Шаблоном Project song file type: Тип файла сонга в проекте: Project Path to song file: Путь к файлу сонга в проекте: Create project folder (recommended for audio projects) Создать папку пректа (рекомендуется для аудио проектов) Song information: Информация о сонге: Write window state Запоминать состояние окон &Create project folder (recommended for audio projects) QObject Error Ошибка Please first select the range for crescendo with the loop markers. Выберите сначала маркерами диапазон для крещендо. Controller ! Other ! No presets found Пресеты не найдены Switch on General Midi Level 1 mode Switch on General Midi Level 2 mode Switch off General Midi Level 1 or 2 Switch on Roland GS mode Switch on Yamaha XG mode Select gui type Выберите интерфейс Preset actions Действия над пресетами Save preset... Сохранить пресет... Update list Обновить список Saved presets Сохраненные пресеты Enter new preset name Введите имя нового пресета Midi programs Midi программы Presets Пресеты Tracks: Midi ports: Midi devices: Jack: Jack midi: Current device Others Другие Files Synthesizers Open Открыть Closed <Properties> <Base Property> <Property> Input routing Warning: No input routes! Click to connect... Output routing Warning: No output routes! Click to connect... TRACK TAKE MusE import error. MusE failed to import the file. Possibly this wasn't a sound file? If it was check the permissions, MusE sometimes requires write access to the file. Instruments FX slot QWidget Cannot convert sysex string Невозможно конвертировать строку SysEx Hex String too long (2048 bytes limit) Hex-строка слишком длинная (предел 2048 байтов) generic midi обычный MIDI new новый create peakfile for создать пиковый файл для MusE: get file name MusE: получить имя файла The directory %1 does not exist. Create it? Папка %1 не существует. Создать её? MusE: create directory MusE: создать папку creating dir failed создание папки не удалось File %1 exists. Overwrite? Файл %1 уже существует. Переписать? Open File %1 failed: %2 Открытие файла %1 не удалось: %2 MusE: write MusE: записать MusE: Open File MusE: Открыть файл None Ничего No selection. Ignoring Не выделено. Игнорируется Instrument-defined Определенный инструментом Edit instrument ... Редактировать инструмент... Add Добавить Others Другие Common Controls Обычные кнопки Velocity Скорость нажатия No wave events selected. Не выбрано wave-событий Generic midi Creating dir failed Create peakfile for New Новый QuantBase MusE: Quantize Квантование Range Диапазон All Events Все События Selected Events Выбранные События Looped Events События в Цикле Selected Looped Выбранные в цикле Values Значения Strength: Сила: % % Threshold (ticks): Порог (тики): Quantize Len Квантовать длину Raster Значение Whole Целая Half Половинная 4th 1/4 4th Triplet 1/4 Триоль 8th 1/8 8th Triplet 1/8 Триоль 16th 1/16 16th Triplet 1/16 Триоль 32th 1/32 32th Triplet 1/32 Триоль Swing: Свинг: If the proposed change in tick or length is smaller than threshold, nothing is done. If swing=0, this is normal If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. Если предложенные изменения длины (или тиков) меньше порогового значения, ничего не произойдет. если свинг=0, это норма если свинг = 33, это ритм (2:1). если свинг = -33, то это ритм (1:2). OK OK Cancel Отмена Parts Партии Selected parts or c&urrent part All parts &in editor RemoveBase MusE: Erase Notes MusE: Стереть ноты Range Диапазон All Events Все события Selected Events Выбранные события Looped Events События в цикле Selected Looped Выбранные в цикле Thresholds Порог ticks тик(ов) Velocity Скорость нажатия Length Длина <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">(new line) <html><head><meta name="qrichtext" content="1" /><style type="text/css">(new line) p, li { white-space: pre-wrap; }(new line) </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;">(new line) <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p>(new line) <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p>(new line) <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> OK OK Cancel Отмена All Eve&nts Selec&ted Events &Looped Events Selected L&ooped Parts Партии Selected parts or c&urrent part All parts &in editor RhythmGenerator RouteDialogBase MusE: Routing MusE: Маршруты Add Route Добавить маршрут Source: Источник: Destination: Назначение: Connect Соединить connect source to destination Соединить источник с назначением Current Routes Текущие маршруты Source Источник Destination Назначение Remove Удалить remove selected route Удалить выбранный маршрут List of available sources. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Connections View window. Shows all current connections. Thick lines are Omni Routes. Thin lines are Channel Routes. List of available destinations. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Itemized list of current connections. Show only selected sources Show only destination routes for selected source Show only selected destinations Show only source routes for selected destination Show all Midi Ports Auto adjust column size Automatically adjusts the source and destination tree widths when the splitters are adjusted. This also turns on text word wrap, which may cause slower response with larger lists. Preferred route name or alias Connect source to destination Remove selected route RubberbandSettingsBase Rubberband Settings Rubberband support is not enabled Use default settings Presets: Пресеты: Default Percussion Maximum Stretch: Elastic Precise Window: Standard Стандартный Short Long Smoothing: Off Выкл On Вкл Channels: Apart Together Transients: Crisp Mixed Smooth Detector: Compound Percussive Soft Phase: Laminar Independent Formant: Shifted Preserved Pitch: Высота тона: High speed High quality High consistency OK Cancel Отмена SRCResamplerSettingsBase SRC Resampler Settings Use default settings Converter Type: SINC Best quality SINC Medium quality SINC Fastest Zero Order Hold Linear OK Cancel Отмена SS_PluginChooserBase SimpleDrums - Ladspa Plugin Chooser SimpleDrums - Выбор LADSPA-плагина Name Имя Label Метка Inports Порты входа Outports Порты выхода Creator Создатель &Cancel &Отменить Alt+C Alt+C &OK &ОК Alt+O Alt+О SS_PluginFront Clear and unload effect Очистить и выгрузить эффект Load effect Загрузить эффект Toggle display of effect parameters Переключить дисплей параметров эффекта Turn effect on/off Эффект вкл/выкл SetlenBase MusE: Set Note Length MusE: Установить длину ноты Range Диапазон All Events Все события Selected Events Выбранные события Looped Events События в цикле Selected Looped Выбранные в цикле Value Значение New length Новая длина ticks тиков OK OK Cancel Отмена Parts Партии Selected parts or c&urrent part All parts &in editor ShortcutCaptureDialogBase Enter shortcut sequence Введите последовательность "горячих клавиш" Press keys to enter shortcut sequence! Нажмите клавиши для задания "горячих клавиш"! Old shortcut: Старое сочетание: Undefined Не назначено New shortcut: Новое сочетание: OK ОК Cancel Отмена ShortcutConfigBase Configure Keyboard Shortcuts Настроить "горячие клавиши" Shortcut Category Категория "горячих клавиш" Description Описание Shortcut "Горячая клавиша" &Clear &Очистить Alt+C Alt+C &Define Н&азначить Alt+D Alt+D &Apply &Применить Alt+A Alt+A &Printable file... Alt+P Alt+P &Ok &ОК Alt+O Filter: &Reset all SimpleDrumsGuiBase DrumSynth 0.1 DrumSynth 0.1 Audio file assignments. Within parantesis: note number and traditional midi drum name SimplePluginChooserBase SimpleDrums - Ladspa Plugin Chooser SimpleDrums - Выбор LADSPA-плагина Name Имя Label Метка Inports Порты входа Outports Порты выхода Creator Создатель &Cancel Alt+C Alt+C &OK Alt+O SimpleSynthGui &Load setup З&агрузить настройки &Save setup &Сохранить настройки Load sample dialog Загрузить диалог сэмплов Mix Смешать Chn Channel routing SnooperDialogBase Snooper Mode Auto-hide Separate parented top levels Update tree Mouse clicks Key presses Flash timer s Reset Filters MusEGui QWidget Properties Свойства Search: Class name: Object name: About This tool helps developers and designers discover objects. Click on anything anywhere or press a key to highlight an item. It will NOT auto-update when new objects are created. Click 'Update tree'. When open, may slow program slightly. This dialog is NOT shown. Class name::Object name Property Type Тип Value Значение Event type SongInfo Song Information Информация о проекте Show on song load Показывать при загрузке &Cancel &Отменить Alt+C Alt+C &Ok &ОК Alt+O Alt+O SynthConfigBase Soft Synthesizer Программный синтезатор File Файл Instances Выбранный Name Имя list of available software synthesizers список доступных программных синтезаторов Add Instance Добавить выбранный Remove Instance Удалить выбранный Midi connections MIDI-соединения Midi Port MIDI-порт Midi Port and Soft Synth Configuration Midi-порт и Конфигурация Синтезатора Type Тип Inst Инстр Version Версия Description Описание &Apply &Применить &OK Configure midi devices, midi ports, and synthesizers Available soft synthesizers Useable devices Add: Synth Синт ALSA JACK Rename Remove Удалить Device port assignments List of available software synthesizers TransposeBase MusE: Transpose MusE: Транспонировать Range Диапазон All Events Все События Selected Events Выбранные События Looped Events События в Цикле Selected Looped Выбранные в Цикле Value Значение Halftone-steps Полутоновые шаги OK OK Cancel Отмена Parts Партии Selected parts or c&urrent part All parts &in editor UnusedWaveFiles Dialog Диалог List of unused audio files in current project directory: Список неиспользуемых аудио файлов в папке проекта: Current project Текущий проект All .med files in current directory Все .med файлы в текущей папке Move files to 'unused' subdir Переместить файлы в подпапку 'unused' Cancel Отмена VAMGui MusE: Load VAM Presets MusE: загрузить пресеты VAM MusE: Save VAM Presets MusE: сохраненить пресеты VAM VAMGuiBase Virtual Analogue for MusE Виртуальный Аналог для MusE LFO LFO Freq Частота Waveform Форма волны Sine Синусоида Pulse Пульс Saw Пилообразная Triangle Треугольная Filter Фильтр EnvMod Режим Огибающей Attack Атака Decay Спад Sustain Стационарная часть Release Послезвучие Cutoff Отсечка Resonance Резонанс Invert Инвертировать KeyTrack Трекинг клавиатуры Presets Пресеты Set Установить load preset list Загрузить список пресетов save preset list Сохранить список пресетов save preset list to a new file Сохранить список пресетов в новый файл delete preset Удалить пресет DCO 1 DCO 1 Pitch Высота тона Detune Расстройка PWM PWM FM FM PW PW DCO 2 DCO 2 On Вкл VAM 1.0beta3 Virtual Analog for MusE Released under GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VAM 1.0бета3 Виртуальный Аналог для MusE Выпущен на условиях GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VelocityBase MusE: Modify Velocity MusE: Изменить скорость нажатия Range Диапазон All Events Все События Selected Events Выбранные События Looped Events События в Цикле Values Значения Rate: Частота: Offset: Смещение: % % Selected Looped Выбранные в Цикле veloNew = (veloOld * rate) + offset Скор.нажНовая = (скор.нажСтарая * частоту) + смещение OK Cancel Отмена Parts Партии Selected parts or c&urrent part All parts &in editor ZitaResamplerSettingsBase Zita Resampler Settings Zita Resampler support is not enabled Use default settings Options: OK Cancel Отмена file_patterns Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) Караоке (*.kar *.KAR *.kar.gz *.kar.bz2) All Files (*) Все файлы (*) Midi (*.mid) Midi (*.mid) Karaoke (*.kar) Караоке (*.kar) all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) все известные файлы (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) med Files (*.med *.med.gz *.med.bz2) Файлы med (*.med *.med.gz *.med.bz2) Uncompressed med Files (*.med) Несжатые файлы med (*.med) gzip compressed med Files (*.med.gz) Сжатые файлы med (*.med.gz) bzip2 compressed med Files (*.med.bz2) Сжатые файлы med (*.med.bz2) mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) Файлы mid (*.mid *.midi *.kar *.MID *.MIDI *.KAR) (*.jpg *.gif *.png) (*.jpg *.gif *.png) (*.jpg) (*.jpg) (*.gif) (*.gif) (*.png) (*.png) part Files (*.mpt *.mpt.gz *.mpt.bz2) Файлы партии (*.mpt *.mpt.gz *.mpt.bz2) part Files (*.mpt) Файлы партии (*.mpt) gzip compressed part Files (*.mpt.gz) Сжатые файлы партии (*.mpt.gz) bzip2 compressed part Files (*.mpt.bz2) Сжатые файлы партии (*.mpt.bz2) Presets (*.pre *.pre.gz *.pre.bz2) Пресеты (*.pre *.pre.gz *.pre.bz2) Presets (*.pre) Пресеты (*.pre) gzip compressed presets (*.pre.gz) Сжатые пресеты (*.pre.gz) bzip2 compressed presets (*.pre.bz2) Сжатые пресеты (*.pre.bz2) Presets (*.map *.map.gz *.map.bz2) Пресеты (*.map *.map.gz *.map.bz2) Presets (*.map) Пресеты (*.map) gzip compressed presets (*.map.gz) Сжатые пресеты (*.map.gz) bzip2 compressed presets (*.map.bz2) Сжатые пресеты (*.map.bz2) Wave/Binary (*.wav *.ogg *.bin) Wave/Бинарные (*.wav *.ogg *.bin) Wave (*.wav *.ogg) Wave (*.wav *.ogg) Binary (*.bin) Бинарные (*.bin) Wave/Binary (*.wav *.ogg *.flac *.bin) Wave (*.wav *.ogg *.flac) Color configuration files (*.cfc) Qt style sheets (*.qss) Таблицы стилей Qt (*.qss) freeverb FreeVerb FreeVerb Damping Демпфирование Wet Level Уровень "Влажного" сигнала Tap-Reverberator Пленочный Ревербератор Decay [ms] Спад(мсек) dB дБ Dry Level [dB] Уровень "Сухого" сигнала [дб] Wet Level [dB] Уровень "Влажного" сигнала [Дб] Preset: Пресет: AfterBurn Запаздывающее Нарастание AfterBurn (Long) Запаздывающее Нарастание (Длинное) Ambience Окружение Ambience (Thick) Окружение (Плотное) Ambience (Thick) - HD Окружение (Плотное) - HD Cathedral Кафедральный Собор Cathedral - HD Кафедральный Собор - HD Drum Chamber Комната для Ударных Garage Гараж Garage (Bright) Гараж (Яркий) Gymnasium Гимнастический зал Gymnasium (Bright) Гимнастический зал (Яркий) Gymnasium (Bright) - HD Гимнастический зал (Яркий) - HD Hall (Small) Зал (Небольшой) Hall (Medium) Зал (Средний) Hall (Large) Зал (Большой) Hall (Large) - HD Зал (Большой) - HD Plate (Small) Plate реверб (Маленькая) Plate (Medium) Plate реверб (Средняя) Room Size Размер помещения Plate (Large) Plate реверб (Большая) Plate (Large) - HD Plate реверб (Большая) - HD Pulse Chamber Пульсирующая комната Pulse Chamber (Reverse) Пульсирующая комната (Реверс) Resonator (96 ms) Резонатор (96 мсек) Resonator (152 ms) Резонатор (152 мсек) Resonator (208 ms) Резонатор (208 мсек) Room (Small) Помещение (Маленькое) Room (Medium) Помещение (Среднее) Room (Large) Помещение (Большое) Room (Large) - HD Помещение (Большое) - HD Slap Chamber Комната "slap" Slap Chamber - HD Комната "slap" - HD Slap Chamber (Bright) Комната "slap" (Яркая) Slap Chamber (Bright) HD Комната "slap" (Яркая) HD Smooth Hall (Small) Ровный зал (Небольшой) Smooth Hall (Medium) Ровный зал (Средний) Smooth Hall (Large) Ровный зал (Большой) Smooth Hall (Large) - HD Ровный зал (Большой) - HD Vocal Plate Вокальная Plate-реверб Vocal Plate - HD Вокальная Plate-реверб - HD Warble Chamber Журчащая комната Warehoouse Склад Warehouse - HD Склад - HD Comb Filters Причесывающие фильтры Allpass Filters Широкополосный фильтр Bandpass Filters Полосовой фильтр Enhanced Stereo Расширенное стерео main File to open Alsa midi only (using dummy audio driver) Use RtAudio driver Use JAckAudio driver to connect to Jack audio server Do not try to auto-start the Jack audio server Do not auto-populate midi ports with midi devices found, at startup Force inclusion of ALSA midi even if using Jack Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) Force midi real time priority to n (default: audio driver prio -1) Force plugin cache re-creation. (Automatic if any plugin path directories changed.) Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care. Don't load LADSPA plugins Don't load MESS plugins Don't load VST plugins Don't load DSSI plugins Don't load LV2 plugins Don't use LASH Force locale to the given language/country code (xx = Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins. Debug mode: no threads, no RT Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively! Debug mode: trace midi Input Debug mode: trace midi Output Debug mode: trace sync Enable Python control support Pyro nameserver host name Pyro nameserver host port Pyro daemon host name Pyro daemon host port Pyro communication timeout in seconds Don't load LinuxVST plugins midiWarnInitPendingBase Instrument initialization MusE should now send some Instrument Initialization Sequences. The sequences (usually System Exclusive messages) are defined by the selected instruments in the Settings -> Midi Ports dialog, such as the GM (default), GS, or XG instruments. Typically you should answer yes here. You can always do it manually from the Midi menu. Continue? Don't ask me again shortcuts Transport: Start playback from current location Транспорт: Начать проигрывание с текущей позиции Transport: Toggle metronome Транспорт: Метроном вкл/выкл Transport: Stop Playback Транспорт: Стоп Transport: Goto Start Транспорт:: Перейти к началу Transport: Play, Stop, Rewind Транспорт: Воспр., Стоп, Перемотка Transport: Goto left marker Транспорт: Перейти к левому маркеру Transport: Goto right marker Транспорт: Перейти к правому маркеру Transport: Toggle Loop section Транспорт: Цикл вкл/выкл Transport: Toggle Record Транспорт: Запись вкл/выкл Transport: Clear all rec enabled tracks Транспорт: Снять назначенную запись со всех треков Toggle fullscreen Полный экран вкл/выкл Edit: Copy Правка: Копировать Edit: Copy in range Правка: Копировать диапазон Edit: Undo Правка: Отменить Edit: Redo Правка: Вернуть Edit: Cut Правка: Вырезать Edit: Paste Правка: Вставить Edit: Paste (with dialog) Правка: Вставить (с диалогом) Edit: Delete Правка: Удалить File: New project Файл: Новый проект File: Open from disk Файл: Открыть с диска File: Save project Файл: Сохранить проект File: Open recent file Файл: Открыть недавние File: Save as Файл: Сохранить как File: Load template Файл: Загрузить шаблон File: Import midi file Файл: Импортировать midi файл File: Export midi file Файл: Экспортировать midi файл File: Import midi part Файл: Импортировать midi партию File: Import audio file Файл: Импортировать аудио файл File: Quit MusE Файл: Выйти из программы Edit: Select parts on track Правка: Выбрать партии в треке Open pianoroll Открыть пианоролл Open drumeditor Открыть редактор ударных Open listeditor Открыть редактор списка Open waveeditor Открыть wave-редактор Open graphical mastertrack editor Открыть редактор графика мастер-трека Open list mastertrack editor Открыть редактор списка мастер-трека Open midi transformer Открыть преобразователь midi-событий Add midi track Добавить midi-трек Add drum track Добавить трек ударных Add wave track Добавить аудио-трек Add audio output Добавить аудио выход Add audio group Добавить аудио-группу Add audio input Добавить аудио вход Add audio aux Добавить аудио aux Structure: Global cut Структура: Общая вырезка Structure: Global insert Структура: Общая вставка Structure: Global split Структура: Общее разделение Structure: Cut events Структура: Вырезать события View: Open mixer #1 window Вид: Открыть окно микшера #1 View: Open mixer #2 window Вид: Открыть окно микшера #2 View: Toggle transport window Вид: Откр/закр окно транспорта View: Toggle bigtime window Вид: Откр/закр окно хронометра View: Open marker window Вид: Открыть окно маркеров Settings: Follow song by page Настройки: Следовать за проектом постранично Settings: Follow song off Настройки: Не следовать за проектом Settings: Follow song continuous Настройки: Следовать за проектом непрерывно Settings: Global configuration Настройки: Общая конфигурация Settings: Configure shortcuts Настройки: Настроить "горячие клавиши" Settings: Configure metronome Настройки: Настроить метроном Settings: Midi sync configuration Настройки: Конфигурация midi-синхронизации Settings: Midi file import/export configuration Настройки: Настройка экспорта/импорта Midi-файла Settings: Appearance settings Настройки: Настройки внешнего вида Settings: Midi ports / Soft Synth Настройки: Midi-порты / Синтезаторы Settings: Audio subsystem configuration Настройки: Конфигурация аудио-подсистемы Midi: Edit midi instruments Midi: Редактировать midi-инструмент Midi: Open midi input transform Midi: Открыть преобразователь midi-входа Midi: Open midi input filter Midi: Открыть фильтр midi-входа Midi: Midi input transpose Midi: Транспозиция midi-входа Midi: Midi remote control Midi: Дистанционный midi-контроль Midi: Random rhythm generator Midi: Генератор случайного ритма Midi: Reset midi Midi: Сброс настроек midi Midi: Init midi Midi: Инициализация midi Midi: Midi local off Midi: Локальное управление отключено Audio: Bounce audio to track Аудио: Свести аудио в трек Audio: Bounce audio to file Аудио: Свести аудио в файл Audio: Restart audio Аудио: Перезапустить аудио Automation: Mixer automation Автоматизация: Автоматизация микшера Automation: Take mixer snapshot Automation: Сделать снимок микшера Automation: Clear mixer automation Automation: Очистить автоматизацию микшера Help: Open Manual Справка: Открыть руководство Help: Toggle whatsthis mode Справка: Что это? - Edit: Edit selected part Правка: Редактировать выбранную партию Edit: Select nearest part on track above Правка: Выбрать ближайшую партию треком выше Edit: Add nearest part on track above Правка: Добавить ближайшую партию треком выше Edit: Select nearest part on track below Правка: Выбрать ближайшую партию треком ниже Edit: Add nearest part on track below Правка: Добавить ближайшую партию треком ниже Edit: Insert empty measure Правка: Вставить пустой такт Edit: Paste as clones Правка: Вставить как клон Edit: Paste as clones (with dialog) Правка: Вставить как клон (с диалогом) Select track above Выбрать трек выше Select track below Выбрать трек ниже Midi: Transpose Midi: Транпонировать Edit: Select all Правка: Выбрать всё Edit: Select none Правка: Снять выделение Edit: Invert Selection Правка: Инвертировать выделенное Edit: Select events/parts inside locators Правка: Выбрать события/партии внутри локаторов Edit: Select events/parts outside locators Правка: Выбрать события/партии вне локаторов Edit: Select previous part Правка: Выбрать предыдущую партию Edit: Select next part Правка: Выбрать следующую партию Edit: Select nearest part/event to the left or move cursor Правка: Выбрать ближайшую партию/событие слева или передвинуть курсор Edit: Add nearest part/event to the left to selection Правка: Добавить к выделенному ближайшую партию/событие слева Edit: Select nearest part/event to the right or move cursor Правка: Выбрать ближайшую партию/событие справа или передвинуть курсор Edit: Add nearest part/event to the right to selection Правка: Добавить к выделенному ближайшую партию/событие справа Edit: Set locators to selection Правка: Поставить локаторы на выбранный диапазон Edit: Increase pitch Правка: Повысить высоту тона Edit: Decrease pitch Правка: Понизить высоту тона Edit: Increase event position Правка: Увеличить позицию события Edit: Decrease event position Правка: Уменьшить позицию события View: Zoom in Вид: Увеличить View: Zoom out Вид: Уменьшить View: Goto Current Position Вид: Перейти к текущей позиции View: Scroll left Вид: Прокрутить влево Edit: Set Fixed Length on Midi Events Правка: Установить фикс. длину midi-событий Quantize Квантовать Modify Note Length Изменить длительность ноты Modify Velocity Изменить скорость нажатия Edit: Crescendo Правка: Крещендо Edit: Thin Out Правка: Сделать выборку Edit: Erase Event Правка: Удалить событие Edit: Delete Overlaps Правка: Убрать наложения Edit: Note Shift Правка: Сдвиг ноты Edit: Move Clock Правка: Передвинуть время Edit: Copy Measure Правка: Скопировать такт Edit: Erase Measure Правка: Убрать такт Edit: Delete Measure Правка: Удалить такт Edit: Create Measure Правка: Создать такт Edit: Change Event Color Правка: Изменить цвет события Tool: Pointer Инструменты: Указатель Tool: Pencil Инструменты: Карандаш Tool: Eraser Инструменты: Ластик Tool: Line Draw Инструменты: Рисовать линию Tool: Cursor Инструменты: Курсор Add note velocity 1 Добавить скорость нажатия 1 Add note velocity 2 Добавить скорость нажатия 2 Add note velocity 3 Добавить скорость нажатия 3 Add note velocity 4 Добавить скорость нажатия 4 Cursor step size: larger Размер шага курсора: больше Cursor step size: smaller Размер шага курсора: меньше Instrument/Cursor up Инструмент/Курсор вверх Instrument/Cursor down Инструмент/Курсор вниз Tool: Scissor Tool: Ножницы Tool: Glue Tool: Клей Tool: Mute Tool: Без звука Transport: Increase current position Транспорт: Увеличить тек. позицию Transport: Decrease current position Транспорт: Уменьшить тек. позицию Transport: Increase current position, no snap Транспорт: Увеличить тек. позицию, без привязки Transport: Decrease current position, no snap Транспорт: Уменьшить тек. позицию, без привязки Quantize: Set quantize to 1/1 note Квантование: Квантовать целыми (1/1) Quantize: Set quantize to 1/2 note Квантование: Квантовать половинными (1/2) Quantize: Set quantize to 1/4 note Квантование: Квантовать четвертными (1/4) Quantize: Set quantize to 1/8 note Квантование: Квантовать восьмыми (1/8) Quantize: Set quantize to 1/16 note Квантование: Квантовать шестнадцатыми (1/16) Quantize: Set quantize to 1/32 note Квантование: Квантовать тридцатьвторыми (1/32) Quantize: Set quantize to 1/64 note Квантование: Квантовать шестьдесятчетвертыми (1/64) Quantize: Toggle triol quantization Квантование: Вкл/выкл триоль Quantize: Toggle punctuation quantization Квантование: Вкл/выкл точку Quantize: Toggle punctuation quantization (2) Квантование: Вкл/выкл двойную точку Edit: Insert at location Правка: Вставить сюда Edit: Increase length Правка: Увеличить длину Edit: Decrease length Правка: Уменьшить длину Insert Note Вставить ноту Insert SysEx Вставить SysEx Insert Ctrl Вставить кнтрл Insert Meta Вставить Meta Insert Channel Aftertouch Вставить Channel Aftertouch Insert Key Aftertouch Вставить Key Aftertouch Insert Tempo Вставить темп Insert Signature Вставить размер Change Event Position Изменить позицию события Edit Event Value Редактировать значение события Insert Key Вставить ключ Goto Next Marker Перейти к след. маркеру Goto Prev Marker Перейти к предыд. маркеру Add new style drum track Добавить трек ударных в новом стиле Edit: Paste to selected track Edit: Paste clone Edit: Paste clone to selected track Transport: Step record Transport: Midi input Transport: Play events Edit: Increase velocity Edit: Decrease velocity Move: Move to selected note Tool: Pan Tool: Zoom Tool: Range Edit: Duplicate track Transport: Restart recording Транспорт: Перезапустить запись Edit selected track name Редактировать имя выбранной дорожки Normalize Нормализовать Transport: Goto End Transport: Restart recording (multi take) Transport: Arm current track Toggle rewind on stop Edit: Paste to current part File: New project from template Edit: Rename part Mute all currently selected tracks Solo all currently selected tracks Tool: Stretch Tool: Sample rate Toggle tabbed/floating editor window Toggle docks display Show Arranger window Open track Synth GUI Open pianoroll in new window Open drumeditor in new window Open listeditor in new window Open waveeditor in new window Insert midi track Insert drum track Insert wave track Insert audio output Insert audio group Insert audio input Insert audio aux View: Toggle mixer strip display Settings: Midi ports / Soft synths Quantize: Set quantize to off Select strip to the left Select strip to the right Mixer strip volume down Mixer strip volume up Mixer strip pan left Mixer strip pan right Mixer strip volume down page Mixer strip volume up page Mixer strip pan left page Mixer strip pan right page warnBadTimingBase Bad timing Плохой тайминг Message here Don't warn me again muse-4.0.0/src/share/locale/muse_sv_SE.ts000066400000000000000000022104761404105311000202350ustar00rootroot00000000000000 @default Add Midi Track Lägg till Midispår Add Drum Track Lägg till Trumspår Add Wave Track Lägg till Wavespår Add Audio Output Lägg till Ljudutgång Add Audio Group Lägg till Ljudgrupp Add Audio Input Lägg till Ljudingång Add Aux Send Lägg till Aux-Send Add Synth Lägg till Synth Route Koppling dest mål name="%1"/ namn="%1"/ Warning: No output devices! Varning: Inga utgångsenheter! Open midi config... Öppna midikonfiguration... Empty ports Tomma portar <none> <ingen> channelMask="%1" kanalmask="%1" Select project directory Välj projektkatalog Bad timing Dålig timing Timing source frequency is %1hz, which is below the recommended minimum: 500hz! This could lead to audible timing problems for MIDI. Please see the FAQ on http://muse-sequencer.org for remedies. Also please check console output for any further error messages. Frekvensen på tidkällan är %1hz vilket är under det rekommenderade minsta värdet: 500hz! Det kan leda till hörbara timingproblem för MIDI. See FAQ på http://muse-sequencer.org för lösningsförslag. See också terminalutskrifter för eventuella fler felmeddelanden. Add Old Style Drum Track Lägg till trumspår av gamla typen Add New Style Drum Track Lägg till trumspår av nya typen AboutBox AboutBox Om &Keep On Rocking! &Rocka på! Alt+K (C) Copyright 1999-2012 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer och andra. Se http://www.muse-sequencer.org för nya versioner och mer information. Publicerad under GNU Public License (C) Copyright 1999-2014 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer och andra. Se http://www.muse-sequencer.org för nya versioner och mer information. Publicerad under GNU Public License {1999-2014 ?} (C) Copyright 1999-2015 Werner Schweer and others. See http://www.muse-sequencer.org for new versions and more information. Published under the GNU Public License (C) Copyright 1999-2012 Werner Schweer och andra. Se http://www.muse-sequencer.org för nya versioner och mer information. Publicerad under GNU Public License {1999-2014 ?} {1999-2015 ?} MusE MusE Internals System information (useful for debugging): (C) Copyright 1999-2020 the MusE development team. See https://muse-sequencer.github.io for new versions and more information. Published under the GNU Public License. AppearanceDialogBase Apply Verkställ Ok Cancel Avbryt Arranger Arrangerare Parts Parter show names visa namn show events visa händelser(events) show Cakewalk Style använd Cakewalkstil y-stretch y-sträck Events Händelser note on poly pressure controller kontroll aftertouch pitch bend program change special Background picture Bakgrundsbild show snap grid visa rutnät Colors Färger Items Style/Fonts Stil/Typsnitt QT Theme QT-tema Windows Fönster Metal Metall Norwegian Wood Norskt trä Platinum Platina Fonts Typsnitt Family Familj Size Storlek Font 1 Typsnitt 1 Font 2 Typsnitt 2 Font 3 Typsnitt 3 Font 0 Typsnitt 0 Bold Fet Italic Kursiv ... Color name: Färgnamn: Global opacity Global genomskinlighet Font 4 Typsnitt 4 Font 5 Typsnitt 5 Palette Palett MusE: Appearance settings MusE: Utseendeinställningar add lägg till remove ta bort add to palette lägg till i palett B S H V G R clear rensa Font 6 Typsnitt 6 1 1 May require restarting MusE for best results Kan kräva omstart av MusE för bästa resultat Themes Show names Show e&vents Show Ca&kewalk style Wave Drawing in Parts O&nly outline Add Remove Ta bort Clear Rensa Show snap grid Choose color... Add to palette Save all... Load all... Size (pt) At what point size to switch from aliased text to non-aliased text. Zero means never alias, always use anti-aliasing. For certain controls such as compact sliders. Aliased text is bright and sharp but may look poor in some cases. The font family is forced to 'Sans', which should give reasonable results at low point sizes. Icon size Custom cursor size Distribute current system font family to all other fonts System Font sizes for fonts 1-6 are determined at every program start, to optimally match the currently used system font. Disable if you want to set fixed sizes in the font settings below. Default scaling factors: Font 1: 70% Font 2: 100% Font 3: 100% Font 4: 80% Font 5: 80% Font 6: 80% Calculate and apply relative font sizes dynamically at program start Maximum aliased size, 0 = no alias Insert Sätt in Fill in optimal font sizes in relation to the system font RMS/PEAK (&traditional) Current theme If a user style sheet for one of the MusE color schemes exists and is selected, it will be merged with the default style sheet for this scheme. The user style sheet takes precedence and overwrites identical settings in the default style sheet. Cascade default and user theme style sheets Show icons in menus Use native standard dialogs Help ArrangerColumnsBase Configure arranger columns Konfigurera arrangerarkolumner Columns: Kolumner: Name: Namn: Controller type: Kontroltyp: Midi controller type Midi kontrolltyp Control7 Kontroll7 Control14 Kontroll14 Pitch Tonhöjd H-Ctrl Midi controller number high byte Midikontroll-nummer, hög byte L-Ctrl Midi controller number low byte Midikontrollnummer, låg byte * wild card affect CCs at påverkar CC vid begin of song början på sång current position nuvarande position &Add &Lägg till &Delete &Ta bort Done Färdig Affect CCs at AudioConverterSettingsBase Audio Converter Settings Available audio converters Offline settings... Realtime settings... Gui settings... Use default preferences (see Global Settings) Preferences When only samplerate conversion is required, use: When time stretching or pitch shifting is required, use: OK Ok Cancel Avbryt Awl::MidiVolEntry off av Awl::VolEntry off av ChooseSysexBase Dialog Hex: Comment: Kommentar: &OK &Ok &Cancel &Avbryt ClipListEditorBase MusE: ClipList MusE: Clip-lista Name Namn Refs Refs Start Start Len Längd Data Data Clip Properties Clip-egenskaper Pos: Pos: Len: Längd: Samplerate Samplingshastighet Status CommentBase Form1 Track Comment Spår-kommentar Track 1 Spår 1 ConfigMidiFileBase &OK &Ok &Cancel &Avbryt 0 (single track) 0 (ett spår) 1 (multiple tracks) 1 (flera spår) Format: Format: 96 96 192 192 384 384 Division: Division: Copyright: Copyright: MusE: Config Midi File Import/Export MusE: Ställ in midifil import/export Import: Importera: Split tracks into &parts Dela spår i &parter Alt+P Alt+P Split tracks into parts, or one single part Dela spår i parter, eller en enda part Export: Exportera: Enable extended smf format (currently not implemented) Slå på utbyggt smf-format (ej implementerat) Use &2-byte time signatures instead of standard 4 Använd &2-byte tidsignaturer istället för standard, 4 Alt+2 Alt+2 Note: Format 0 uses the FIRST midi track's name/comment in the arranger Notera: Format 0 använder det FÖRSTA midi spårets namn/kommentar i arrangeraren Save space by replacing note-offs with &zero velocity note-ons Spara utrymme genom att ersätta note-off med note-on med velocity &noll Alt+Z Alt+Z Use new-style drum tracks Använd nya typen trumspår Use old-style drum tracks Använd gamla typen trumspår Default instrument: Device Name metas trump Port metas if both exist Instrument Name metas trump Mode sysexes if both exist Mode sysexes Instrument name metas Port metas Device name metas Export a Port/Device meta for format 0 Import Export Running Status saves space by not repeating event status bytes. If this is off every event will have a status byte, increasing file size. Use Running &Status To turn a note off some devices can use note-offs or zero-velocity note-ons or both. When used with Running Status this setting saves space. It is safe to leave this off. Replace note-offs with &zero velocity note-ons If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum note controllers. If disabled, drum maps are ignored during export. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. See "What's This ?" for more info. If enabled, and a drum track has any drum map Port, Channel, and ANote overrides, they will be applied to drum notes and drum controllers. Any Port overrides cause a separate track to be created (requires format 1). Any Channel overrides will still appear in the same track in the exported file, but upon re-import into an app such as MusE, MusE will treat that as a new track and create one. If disabled, drum maps are ignored. All drum notes and drum controllers will be exported using the track's Port and Channel and the unmodified note. Upon re-importing into another app, it is up to the other app to redirect the particular drum ports, channels and notes to make the song sound like it was intended. Drum map overrides are unique to MusE. If re-importing into MusE, for faithful reproduction disable this setting and apply a drum map and some overrides later. If re-importing or opening in another midi player, enable this setting so that the player can have a chance at faithful reproduction. Some user adjustment may be required (choice of instruments, ports, etc). Apply drum map Port, Channel, ANote overrides If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. See "What's This ?" for more info. If enabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will go to a separate track in the midi file. The extra track is automatically created. In this mode all events on a track in the midi file are on the same channel. If disabled, any drum notes or drum controllers on an applied drum map item in which Channel is overridden will stay on that track in the midi file. In this mode the track events can be on different channels. But if the file is re-imported into an app such as MusE, MusE will automatically create the extra track. Drum map Channel overrides go to a separate track Export instrument or mode: Export port or device metas: CopyOnWriteDialogBase Copy Wave Files Some sound files will be copied to the Project Directory, either because they are not writable or because more than one independent Wave Event shares them. (If you prefer instead that the Wave Events are inter-dependent, try using Clone Parts.) Multiple copies will be made in some cases. If no Project has been created yet, you will be asked to, giving another chance to cancel. These files will be copied to the Project Directory: CrescendoBase MusE: Crescendo/Decrescendo MusE: Crescendo/Decrescendo Range Område Looped Events Loopade händelser Selected Looped Valda loopade Values Värden Start velocity Startvelocity % End velocity Slutvelocity Absolute Absolut Relative Relativ OK Cancel Avbryt &Looped Events Parts Parter Selected parts or c&urrent part All parts &in editor &Absolute Rela&tive DeicsOnzeGui Delete preset Ta bort preset Do you really want to delete %1 ? Vill du verkligen ta bort %1 ? &Yes &Ja &No &Nej Save configuration Spara konfiguration Critical Error Kritiskt fel Cannot open file %1 Kan inte öppna fil %1 Parsing error for file %1 Fel vid parsning av fil %1 Load category dialog Ladda kategoridialog Load set dialog Ladda set-dialog Save set dialog Spara set-dialog New category Ny kategori Delete category Ta bort kategori Load category Ladda kategori Save category Spara kategori Load set Ladda set Save set Spara set Delete set Ta bort set New subcategory Ny sub-kategori Delete subcategory Ta bort sub-kategori Load subcategory Ladda sub-kategori Save subcategory Spara sub-kategori New preset Ny preset Load preset Ladda preset Save preset Spara preset No more category supported Ingen fler kategori stöds You can not add more categories Kan inte lägga till fler kategorier No category selected Ingen kategori vald You must first select a category. Välj först en kategori. Replace or add Ersätt eller lägg till %1 is supposed to be affected to the hbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 påverkas av hbank nummer %2s, men det finns redan en i den positionen. Vill du ersätta den eller lägga till i nästa lediga position? &Replace &Ersätt &Add &Lägg till Download error Nerladdningsfel There is no more free category slot. Finns inga fler lediga kategori-positioner. Save category dialog Spara kategori dialog No more subcategory supported Inga fler sub-kategorier stöds You can not add more subcategories Du kan inte lägga till fler subkategorier No subcategory selected Ingen subkategori är vald You must first select a subcategory. Väl först en subkategori. Load subcategory dialog Ladda sub-kategori dialog %1 is supposed to be affected to the lbank number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 påverkas av lbank nummer %2s, men det finns redan en i den positionen. Vill du ersätta den eller lägga till i nästa lediga position? There is no more free subcategory slot. Finns inga fler lediga sub-kategoripositioner. Save subcategory dialog Spara sub-kategori dialog No more preset supported Inga fler presets stöds You can not add more presets Det går inte lägga till fler presets No preset selected Inget preset valt You must first select a preset. Välj först ett preset. Load preset dialog Ladda preset dialog %1 is supposed to be affected to the prog number %2, but there is already one on this slot. Do you want to replace it or to add it in the next free slot ? %1 påverkas av prog nummer %2s, men det finns redan en i den positionen. Vill du ersätta den eller lägga till i nästa lediga position? There is no more free preset slot. Finns inga fler lediga preset positioner. Save preset dialog Spara preset dialog Browse set dialog Bläddra set dialog Browse image dialog Bläddra bild dialog DeicsOnzeGuiBase Subcategory Underkategori Category Kategori Pitch Bend Range Pitchbend område Speed Hastighet LFO Waveform LFO Vågform Amplitude Modulation Sensitivity Amplitudmodulationskänslighet Pitch Modulation Sensitivity Pitchmodulationskänslighet Pitch Modulation Depth Pitchmodulationsdjup LFO Speed LFO Hastighet Transpose Transponera Modulation Matrix Modulationsmatris DeicsOnze &Preset &Preset Program numerous Programnummer INITVOICE LBank Bank numerous Banknummer NONE Ingen HBank Prog Preset DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Publicerad låg tillstånd GPL. &Global &Global Pitch Envelope Pitchenvelope PL3 PL2 PL1 PR1 PR2 PR3 Pitch modulation depth Pitchmodulationsdjup LFO LFO Sync LFO-synk Pitch modulation sensitivity Pitchmodulationskänslighet LFO Delay LFO delay LFO speed LFO Hastighet Amplitude modulation depth Amplitudmodulationskänslighet Amplitude modulation sensitivity Amplitudmodulationskänslighet AMS PMD AMD Delay PMS <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> Op4 Feedback Op4 återkoppling Feedback level of the operator 4 Återkopplingsmängd för operator 4 Op &1 Scaling 1 Skalning 1 LS1 RS1 Rate Scaling Skalningshastighet Attack Rate of the operator 1 Attackhastighet för operator 1 Level Scaling Skalningsnivå Amplitude Envelope 1 Amplitudeenvelope 1 RR1 D1R1 D1L1 D2R1 Release Rate Utgivningsdatum 2° Decay Rate 2° Decay-hastighet 1° Decay Level 1° Decay-nivå 1° Decay Rate 1° Decay-hastighet Attack Rate Attack-hastighet AR1 Detune, OSCWave, EGShift 1 DET1 Detune Snedstäm EG Shift 96dB 48dB 24dB 12dB Wave form Vågform Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> Sensitivity 1 Känslighet 1 KVS1 Amplitude Modulation Enable Aktivera amplitudmodulering AME1 Keyboard Velocity Sensitivity Keyboard velocitykänslighet Key Velocity Sensitivity Tangent-velocitykänslighet EBS1 EG Bias Sensitivity EG Bias-känslighet Eg Bias Sensitivity Eg Bias-känslighet Frequency 1 Frekvens 1 Coarse 1 Growkornig 1 Coarse Ratio Grovkornig kvot Fine 1 Fin 1 Fine Ratio Fin kvot Freq 1 Frek 1 Fixed Frequency Fast frekvens Toggle Fix Frequency Omkoppling fast frekvens FIX Fast OUT 1 Ut 1 Output Volume Utgångsvolym Volume Volym Op &2 Amplitude Envelope 2 Amplitudeenvelope 2 D1R2 D1L2 D2R2 RR2 AR2 Frequency 2 Frekvens 2 Coarse 2 Growkornig 2 Fine 2 Fin 2 Freq 2 Frek 2 Scaling 2 Skalning 2 LS2 RS2 OUT 2 Ut 2 Detune, OSCWave, EGShift 2 DET2 Sensitivity 2 Känslighet 2 EBS2 KVS2 AME2 Op &3 Amplitude Envelope 3 Amplitudenvelope 3 D1R3 D1L3 D2R3 RR3 AR3 Scaling 3 Skalning 3 LS3 RS3 OUT 3 Ut 3 Frequency 3 Frekvens 3 Coarse 3 Grovkornig 3 Fine 3 Fin 3 Freq 3 Frek 3 Detune, OSCWave, EGShift 3 DET3 Sensitivity 3 Känslighet 3 EBS3 KVS3 AME3 Op &4 amplitude Envelope 4 Amplitudenvelop 4 AR4 D1R4 D1L4 D2R4 RR4 Frequency 4 Frekvens 4 Coarse 4 Grovkornig 4 Fine 4 Fin 4 Freq 4 Frek 4 Scaling 4 Skalning 4 LS4 RS4 OUT 4 Ut 4 Detune, OSCWave, EGShift 4 DET4 Sensitivity 4 Känslighet 4 EBS4 KVS4 AME4 &Func &Funk Delay Pan Depth Delay-pandjup Delay Pan LFO Freq Delay-pan LFO-frek Delay Ch Send Level Delay Kanal Sändnivå Channel Chorus Kanalchorus Delay Feedback Delay-återkoppling Delay On/Off, Return Level Delay Av/På, Returnivå On Delay Beat Ratio Delay Taktkvot Delay BPM Foot Control Fotkontroll Pitch Tonhöjd Amplitude Amplitud Modulation Wheel Modulationshjul Breath Control Andningskontroll Pitch Bias Tonhöjdspåverkan Envelope Bias Enveloppåverkan After Touch Aftertouch Phony Mode POLY MONO Potamento Portamento Portamento Mode Portamentoläge FINGER FULL PT Portamento Time Portamentotid C&horus C&horus Chorus Parameters Chorusparametrar Channel send level Kanal-sändnivå On/Off and Return level Av/På och returnivå Select LADSPA plugin Välj LADSPA-plugin Change plugin Byt plugin &Reverb Reverb Parameters Reverbparametrar &Config &Inställningar Font Size Fontstorlek Quality Kvalitet High Hög Middle Mellan Low Låg Ultra low Extremlåg Filter Filter Save Mode (into the song) Spara läge (i sång) Save only the used presets Spara endast använda presets Save the entire set Spara hela gruppen Save the configuration Spara konfiguration Configuration File Konfigurationsfil Save... Spara... Load... Ladda... Save as default Spara som förvalt Colors Färger Text Text Background Bakgrund Edit Text Redigera text Edit Background Redigera bakgrund Red Blue Blå Green Grön Set Path Ställ in sökväg Image in the background : Bild i bakgrund: Browse... Bläddra... Load the set at the initialization : Ladda gruppen vid initiering : Set Brightness, Detune, Attack and Release of the current channel to default Ställ in klarhet, detune, attack och release för nuvarande kanal till standard Res. Ctrl Cut all notes off Alla noter av Panic! Panik! Number of Voices Antal röster Number of voices Antal röster Enable Aktivera Channel Kanal Vol Channel Ctrl Kanalkontroll Release Attack Brightness Klarhet Modulation Modulering Pan DelOverlapsBase MusE: Delete Overlaps MusE: Ta bort överlappande Range Område All Events Alla Händelser Selected Events Valda händelser Looped Events Loopade händelser Selected Looped Valda loopade OK Ok Cancel Avbryt Parts Parter Selected parts or c&urrent part All parts &in editor DidYouKnow Did you know? Visste du? Don't show on startup Visa inte vid start Next tip Nästa tips Close Stäng DuplicateTracksBase Duplicate tracks Duplicera spår Number of copies Antal kopior Copy all routes Kopiera alla kopplingar Default routing Förvald koppling No routes Inga kopplingar Copy parts Kopiera parter Copy standard controllers (vol, pan) Kopiera standardkontroller (vol, pan) Copy effects rack plugins Kopiera effektrack-instickseffekter Copy plugin controllers Kopiera instickseffekt-kontroller Ok Cancel Avbryt Copy drumlist Kopiera trumlista Copy standard (vol, pan) and synth controllers Duplicate all parts Make independent copies of all parts Make clones of all parts No parts EditCtrlBase MusE: Edit Controller Event MusE: Editera kontroll-event Time Position Tidsposition Available Controller: Tillgänglig kontroll: Create New Controller Skapa ny kontroll textLabel3 Value Värde Controller Kontroll H-Bank L-Bank Program off av pushButton4 &OK &Ok &Cancel &Avbryt Note Not EditGainBase MusE: Modify gain MusE: Ändra förstärkning Gain Förstärkning 200% 100% 0% &Reset &Reset Alt+R &Apply &Verkställ Alt+A Alt+V &Cancel &Avbryt Alt+C EditInstrumentBase MusE: Instrument Editor MusE: Instrumenteditor High Bank: Hög bank: Low Bank: Låg bank: Program: Program: &Delete &Ta bort Alt+D Alt+T Drum Trumma This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. Detta är en lista med vanligt använda midi-kontroller Notera att i MusE hanteras tonhöjd och program change som vanliga kontroller. Properties Egenskaper Control7 Kontroll7 Control14 Kontroll14 Pitch Tonhöjd H-Ctrl L-Ctrl Min Max Name Namn Type Typ Hex Entry: Hexinmatning: &File &Arkiv Tools Verktyg New Ny &New &Ny Ctrl+N Open Öppna &Open... &Öppna... Ctrl+O Ctrl+Ö Save Spara &Save &Spara Ctrl+S Save As Spara som Save &As... S&para som... Exit Stäng E&xit S&täng Instrument Name: Instrumentnamn: Selected instrument name. Valt instrumentnamn. List of defined instruments. Lista med definierade instrument. Pa&tches Pa&tchar List of groups and patches. Lista med grupper och patcher. Group/Patch Grupp/patch Name: Namn: Group or patch name Grupp eller patch-namn Patch high bank number Patch, högt banknummer Patch high bank number. --- means don't care. Patch, högt banknummer. -- betyder: ignorera. Patch low bank number Patch, lågt banknummer Patch low bank number. --- means don't care. Patch, lågt banknummer --- betyder: ignorera. --- Patch program number Patch programnummer Drum patch Trumpatch If set, the patch is available only for drum channels. Om satt så är patchen endast tillgänglig för trumkanaler. If set, the patch is available in a 'GM' or 'NO' midi song type. Om satt så är patchen endast tillgänglig för 'GM' eller NO' midisångtyper. If set, the patch is available in a 'GS' or 'NO' midi song type. Om satt så är patchen endast tillgänglig för 'GS' eller 'NO' midisångtyper. If set, the patch is available in an 'XG' or 'NO' midi song type. Om satt så är patchen endast tillgänglig för 'XG' eller NO' midisångtyper. Delete group or patch Ta bort grupp eller patch New patch Ny patch New &Patch Ny &patch Alt+P New group Ny grupp New &Group Ny &grupp Alt+G Contro&ller Kontro&ll Common: Vanlig: List of defined controllers Lista med definierade kontroller List of defined controllers. Lista med definierade kontroller. Min Max Def Midi controller name Midi kontrollnamn Type: Typ: Midi controller type Midi kontrolltyp Midi controller number high byte Midikontroll-nummer, hög byte Midi controller number low byte (* means drum controller) Midikontroll-nummer, låg byte (* trumkontroller) Midi controller number low byte. If low byte is * then the controller is a 'drum controller'. For drum tracks and GS/XG type songs and instruments. Allows controllers for each instrument in Muse's drum map. The low byte will be replaced by the 'ANote' in the drum map. Examples: The GS and XG instruments' Drum controllers. Range: Område: Minimum value. If negative, auto-translate. Minsta värde. Om negativt, översätt automatiskt. Minimum value. If the minimum value is negative, the range will automatically be translated to a positive range. Useful for controllers which should be displayed with zero bias. For example, 'Pan': Minimum: -64 Maximum: 63 True range: Min: 0 Max: 127 (bias = 64) 'CoarseTuning': Min: -24 Max: 23 True range: Min: 40 Max: 87 (bias = 64) Bias is determined from controller type: 7-bit Controller7 / RPN: Bias = 64 14-bit Controller14 / RPN14: Bias = 8192 Type 'Pitch' is the exception. It is biased at zero, even with a negative minimum: 'Pitch': Min: -8192 Max: 8191 True range: Min: -8192 Max: 8191 (bias 0) Minsta värde. Om det minsta värdet är negativt så kommer området automatiskt konverteras till ett positivt område Användbart för kontroller som bör visas balanserad runt noll. Till exempel 'Pan': Minimum: -64 Maximum: 63 Verkligt område: Min: 0 Max: 127 (förskjutning = 64) 'Grovstämning': Min: -24 Max: 23 Verkligt område: Min: 40 Max: 87 (förskjutning = 64) Förskjutningen bestäms av kontrolltyp 7-bit Kontroll7 / RPN: förskjutning = 64 14-bit Kontroll14 / RPN14: förskjutning = 8192 'Tonhöjd' är undantaget. Den är förskjuten till noll även med ett negativt minimum: 'Tonhöjd': Min: -8192 Max: 8191 Verkligt område: Min: -8192 Max: 8191 (bias 0) Maximum value Högsta värde Default: Förvalt: L-Bank off av Progr. Progr. ??? ??? H-Bank Default value. Off: No default. Förvalt värde. Av: Inget förvalt. Default (initial) value. Off means no default. If a default value is chosen, the value will be sent to the controller when the controller is added to the song (in piano roll or event editor). When the song is re-loaded, the value is sent again. Otherwise the controller remains at its last value. Controllers are also automatically added to a song upon reception of a midi controller event. Caution! Watch out for controllers such as 'Sustain' and 'ResetAllController' with default values. You should probably turn 'off' their default (in piano roll or drum edit, and instrument editor). Standard (initialt) värde. Av betyder standard. Om standardvärde är valt kommer värdet skickas till kontrollen när den läggs till i sången (i piano rulle eller händelseeditor). När sången laddas om sätts värdet igen. Annars skulle kontrollen stanna på sitt tidigare värde. Kontroller läggs också automatiskt till en sång när de tas emot via midi. Warning! Se upp för kontroller som 'Sustain' eller 'ResetAllController' med standard värden. De bör ha standard värde avstängt (i piano rulle, trum editor, instrument editor). Add common controller Lägg till vanlig kontroll &Add &Lägg till Alt+A Alt+L Delete controller Ta bort kontroll Create a new controller Skapa ny kontroll New &Controller Ny &kontroll Alt+C Alt+K Null Param Hi: Nullparam. hög: Null parameter number High byte Nullparameter hög-byte If set, these 'null' parameter numbers will be sent after each RPN/NRPN event. This prevents subsequent 'data' events from corrupting the RPN/NRPN controller. Typically, set to 127/127, or an unused RPN/NRPN controller number. Om den är inställd, "null" parameternummer kommer att skickas efter varje händelse RPN / NRPN. Detta förhindrar efterföljande händelser "data" korrumperande föraren RPN / NRPN. Vanligtvis inställd på 127/127, eller genom att använda controller RPN / NRPN. Lo: Låg: Null parameter number Low byte Nullparameter, låg byte S&ysEx S&ysEx SysEx List: SysEx-lista: New SysE&x Ny SysE&x Alt+X &Help &Hjälp new item ny enhet What's this? Vad är detta? --- dont care ignorera * wild card off dont care av Drummaps Trummappningar Patch Collections: Patch val: &Copy &Kopiera &Remove &Ta bort &Up &Upp &Down &Ner from från to till Bank Hi: Hög bank: Bank Lo: Låg bank: Show in tracks: Midi Midi Drum&maps Contro&llers &Add Common... W Comment: Kommentar: &Initialization Instrument initialization sequence: &Add... &Change... &Close Close Stäng Note off mode: Note off mode Selects how to handle note off events. The instrument can use note off events, or not at all, or convert them to zero-velocity note on events which helps save midi interface bandwidth. Refer to the instrument manufacturer's midi implementation chart for details. Patch program number. --- means don't care. &Drum High Bank Low Bank Program Patch program number. --- means don't care. Patch Name: Dr def Drum default: Note: The enclosing F0 ... F7 are not required Info Informationen Additional information EditNoteDialogBase MusE: Enter Note MusE: Mata in not OK Ok Cancel Avbryt Length: Längd: Time Position: Tidsposition: Pitch: Tonhöjd: Velocity On: Velocity på: Velocity Off: Velocity av: EditSysexDialogBase MusE: Enter SysEx MusE: Mata in SysEx TimePosition: Tidsposition: Comment: Kommentar: OK Ok Cancel Avbryt Name: Namn: &Select... &OK &Ok &Cancel &Avbryt Hex: Note: The enclosing F0 ... F7 are not required FLUIDGui FLUID: open Soundfile FLUID: öppna ljudbank FLUIDGuiBase Load Ladda FLUIDSynthGuiBase Gain Förstärkning Room Size Rumsstorlek Damping Dämpning Level Nivå Width Bredd Speed Hastighet Depth Djup Number Nummer Type Typ Sine Sinus Triangle Triangel Load Ladda Delete Ta bort Fontname Fontnamn Drum Chnl Trumkanal CHANNEL SETUP Kanalkonfiguration LOADED SOUNDFONTS Laddade soundfonts FLUID Synth Dump Info ID Chnl Soundfont Reverb Chorus FileDialogButtons Global User Användare Project Projekt fdialogbuttons read Midi Port Configuration läs midi-portkonfiguration write window states skriv fönsterinställningar Home FluidSynthGui Choose soundfont Välj soundfont GateTimeBase MusE: Modify Note Length MusE: Modifiera not-längd Range Område All Events Alla händelser Selected Events Valda händelser Looped Events Loopade händelser Selected Looped Valda loopade Values Värden Rate: Hastighet: Offset: Offset: % lenNew = (lenOld * rate) + offset OK Cancel Avbryt Parts Parter Selected parts or c&urrent part All parts &in editor GlobalSettingsDialogBase MusE: Global Settings MusE: Globala inställningar Use project save dialog Använd projektspara-dialog Some popup menus stay open (else hold Ctrl) Vissa popupmenyer behöver Ctrl+click för att stanna öppna Allows some popup menus to stay open. Otherwise, hold Ctrl to keep them open. Tillåter vissa popupmenyer att vara öppna. För vissa använd ctrl+click. In some areas, the middle mouse button decreases values, while the right button increases. Users without a middle mouse button can select this option to make the left button behave like the middle button in such areas. Ibland används mittersta musknappen för att minska och högra knappen för att minska värden. Användare utan mittersta musknappen kan använda denna option för att använda vänster musknapp i stället för mittersta. Use left mouse button for decreasing values Använd vänster musknapp för att minska värden Shift + Right click sets left range marker Shift+högerklick sätter vänster områdesmarkör Allow adding hidden tracks in track list menu Tillåt gömda spår i spårlistan Unhide tracks when adding hidden tracks Ta fram gömda spår när gömda spår läggs till GUI Style GUI-stil MDI-subwindowness and sharing menus MDI-subwindowness och delning av menyer Presets: Presets: traditional MusE SDI traditionell MusE SDI Cakewalk-like MDI Cakewalkliknande MDI Borland-/Mac-like MDI Borland/Macliknande MDI &Apply &Verkställ &Ok &Ok &Cancel &Avbryt Audio Ljud Mixer dB min. Meter Value min. Metervärde Midi Midi Ticks Tick 1024 2048 4096 Displayed Resolution (Ticks/Quarternote) Visad upplösning (Tick/Kvartsnot) 48 96 192 384 768 1536 3072 6144 12288 RTC Resolution (Ticks/Sec) RTC-Upplösning (Tick/Sek) GUI Refresh Rate GUI-uppdateringsfrekvens /sec /sek Application Applikation Start Muse Starta MusE start with last song starta med senaste sång start with song starta med sång start song: startsång: Views Vyer show visa height höjd width bredd Big Time Stor tidvisare set current values sätt nuvarande värden start with template: default.med börja med mall: default.med show splash screen visa splash External Waveditor Extern Ljudredigerare External Waveditor command Externt ljudredigerarkommando show "Did you know?" dialog visa "Visste du?" dialog Start song Startsång min. Slider Val min. Regelvärde Enable denormal protection Slå på "denormal"-skydd Enable output limiter Slå på utgångs-limiter Note: External editor opened from the internal editor. Notis: Extern redigerare öppnas från den interna redigeraren. Dummy Audio Driver (settings require restart) Låtsas ljuddrivrutin (kräver omstart vid ändring) Period size (Frames per period): Periodlängd (antalet ramar per period): 16 Project directory Projektkatalog Projects: Projekt: ... .... Main Window Huvudfönster Try to use Jack Freewheel Använd Jack Freewheel om tillgängligt Speeds bounce operations Snabbar upp ping-pong operationer Use Jack Freewheel mode if possible. This dramatically speeds bounce operations. Använd Jack Freewheel läge om möjligt. Snabbar upp ping-pong operationer dramatiskt. VST in-place VST på-plats Enable VST in-place processing (restart required) Möjliggör VST på-plats processering (kräver omstart) Enable VST in-place processing. Turn this off if VST Ladspa effect rack plugins do not work or feedback loudly, even if they are supposed to be in-place capable. Setting requires a restart. Slå på VST på-plats processering. Slå av detta om VST Ladspa effektrack instickseffekter inte fungerar eller ger högljud feedback även om de bör vara på-plats kapabla. Inställning kräver omstart. Minimum control period Minsta kontrollperiod Minimum audio controller process period (samples). Minsta ljudkontroll-processeringsperiod (i ljudramar). Minimum audio controller process period (samples). Adjusts responsiveness of audio controls and controller graphs. Set a low value for fast, smooth control. If it causes performance problems, set a higher value. Minsta ljudkontroll-processeringsperiod (i ljudramar). Justerar responstid för ljudkontroll och kontroll grafer. Låga värden ger snabb, mjuk kontroll. Om det ger prestandaproblem sätt ett högre värde. 1 2 4 8 32 64 128 256 512 Sample rate Samplingshastighet Shorter periods give better midi playback resolution. Kortare perioder ger bättre midiuppspelnings-upplösning. 8192 16384 32768 Midi Resolution (Ticks/Quarternote) Midiupplösning (Tick/kvartsnot) GUI Behaviour GUI-funktion Use old-style stop shortcut: Använd traditionellt stop kortkommando: Move single armed track with selection Flytta enskilt inspelningsspår med valt spår On Launch Vid start Behavior Uppförande Smart focus (restart required) Smartfokus (omstart krävs) Smart focus Smartfokus After editing, controls will return focus to their respective canvas Efter editering lämnar kontroll tillbaka fokus till respektive editor Choose start song or template Välj startsång eller mall Reset to default Återställ till förvalt start with template Starta med mall Start template or song: Startmall eller sång: Read MIDI Ports configuration from file, or else automatically configure Läs MIDI-port konfiguration från fil, eller i annatfall, konfigurera automatiskt Read MIDI Ports configuration läs midi-portkonfiguration Record new style drum tracks Spela in trumspår av nya typen Record all instruments Spela in alla instrument Don't record hidden instruments Spela inte in gömda instrument Don't record muted instruments Spela inte in tystade instrument Don't record hidden or muted instruments Spela inte in gömda eller tystade instrument Drum tracks Trumspår Only offer old-style drumtracks Föreslå bara gamla typen trumspår Only offer new-style drumtracks Föreslå bara nya typen trumspår Prefer old-style drumtracks Föredra gamla typen trumspår Prefer new-style drumtracks Föredra nya typen trumspår Auto save (every 5 minutes if not playing/recording) Instrument initialization Send instrument initialization sequences Warn if instrument initialization sequences pending Send instrument controller default values if none in song, at init or rewind Warn if timer frequency is inadequate Track height Show newly created midi velocity graphs per-note px Enable borderless mouse. For certain functions like zoom/pan. Disable to use an alternate standard method. Enable borderless mouse. For certain functions like zoom. Disable to use an alternate standard method. Borderless zoom/pan mouse (else use alternate method) &Don't record hidden instruments Don'&t record muted instruments Don't record &hidden or muted instruments Scrollable submenus Warn if opening file versions different than current version Start MusE Start with &last song Start with &template Sta&rt with song On launch Show splash screen Show "Did you know?" dialog Plugin Paths Ordered by priority. Changes take effect on restart. The plugin cache is used for safety and speed. It automatically rescans when any of the given directories change, for example by installing a new plugin. Click this button to force the cache to recreate upon next restart. &Force plugin rescan and cache recreation on next restart LADSPA DSSI Linux VST LV2 NOTE: Ensure the directory containing core LV2 bundles is included (typically /usr/lib/lv2). Otherwise all plugins may not load properly. Move up Move down Add... Edit... Remove Ta bort Audio driver settings (require restart) Sample rate: Audio backend: Shorter periods give better midi playback resolution. Note that this cannot be set with Jack devices! Period size (frames per period): Note that samplerate cannot be set with Jack devices! Minimum meter value Minimum slider value Midi controller behaviour Send Null parameters after an (N)RPN value If set, 'null' parameter numbers will be sent after each RPN/NRPN event. (A 'null' parameter number is 127.) This prevents subsequent rogue or accidental 'data' events (without parameters) from corrupting the current RPN/NRPN controller. Normally it is OK to leave this unset, but use it if there is a possibility of such 'data' events. If this is set, the 'Optimize controllers' setting does not affect these controllers' parameters, only the H/L values. Send Null parameters Don't send redundant parameters or values If set, redundant H/L parameters or H/L values are not sent. They are sent only if they changed. This can save midi interface bandwidth. But it is possible the receiving device might miss the previous changes (for example if the device is turned on after they were sent). The 'Panic' button will force them to be resent. Certain operations will also force them to be resent, such as clicking a GUI midi control (but not while adjusting it). Optimize controllers Record a&ll instruments Latency Whether to enable latency correction/compensation: Enable latency correction/compensation Project latency Whether completely independent branches share a common project-wide latency: Project-wide latency Unterminated branches Whether unterminated branches participate in latency correction/compensation: Unterminated input branches Unterminated output branches Track monitoring Whether track monitor buttons affect latency correction/compensation: Monitoring affects latency Revert native GUI scaling for plugins in HiDPI Live update wave parts while recording Whether to show knob and slider values in certain places, for example mixer strips. Turn off to reduce clutter. Revert native UI window HiDPI scaling (some plugins ignore this setting) Show knob and slider values Whether to show knobs or sliders in certain places, for example mixer strips Prefer midi volume as decibels instead of 0-127 Style hack: Force line edit widgets to draw a frame Prefer knobs instead of sliders GUI refresh rate Use first available Ask once Ask always Force line edit widgets to draw a frame at small sizes. Some styles refuse to draw the frame. This hack forces a frame to be drawn, but may interfere with other styles. Use old-style stop shortcut Monitor on record-arm automatically Show note names on notes in pianoroll Whether record-arming a track automatically activates monitoring. Record drum tracks Sample rate conversion, time stretching and pitch shifting Audio converter settings... LV2 UI open behavior Set range markers (left / right) Middle mouse button / Right mouse button Ctrl + Right mouse button / Right mouse button Ctrl + Left mouse button / Ctrl + Right mouse button Options GUI Tweaks Tweaks Editors Open editors in tabs (else in separate windows) Piano roll Pianorulle Drum editor Trumeditor Wave editor Ljud-redigerare Score editor Notredigerare Master track editor Master spår redigerare DSSI VST Directories where dssi-vst (if installed) looks for Wine vst plugins (*.dll files) Directories containing Linux vst plugins (*.so files) GUI Show status bar Show note tooltips in midi editors Keep transport window on top (requires restart) LegatoBase MusE: Legato Range Område All Events Alla Händelser Selected Events Valda Händelser Looped Events Loopade Händelser Selected Looped Valda loopade Settings Inställningar ticks Tick Minimum Length Minsta längd Allow shortening notes Tillåt förkortning av noter OK Cancel Avbryt Parts Parter Selected parts or c&urrent part All parts &in editor MITTransposeBase MusE: Midi Input Plugin: Transpose MusE: Midi Inputplugin: Transponering On TriggerKey TriggNot Transpose: Transponering: +0 MRConfigBase MusE: Midi Input Plugin: Remote Control MusE: Midiinputplugin: Fjärrkontroll Activate Aktivera On Actions Operationer Stop Stopp Record Spela in Goto Left Mark Gå till vänster markering Play Spela Insert rest (step rec) Lägg in paus (sluta spela in) MdiSettingsBase MDI subwin MDI underfönster Shares menu when subwin Dela meny med underfönster Shares menu when free Delar meny när ledigt MetronomeConfigBase MusE: Metronome Config MusE: Metronomkonfiguration Metronome Metronom Audio Beep Audio - Ton MIDI Click Midiklick Midi Channel Midikanal Measure Note Taktnot Measure Velocity Taktanslag Beat Velocity Slaganslag Beat Note Slagnot Midi Port Midiport Precount Inräkning enable Bars Takter From Mastertrack Från masterspår / Signature Signatur Prerecord Förinspelning Preroll Inräkning &Apply &Verställ &OK &Ok &Cancel &Avbryt Choose outputs... Välj utgångar... 50 % Audio volume % ljudvolym Hint: Enable metronome in Transportpanel Tips: Slå på metronom i transportpanelen Alt+A Alt+L Alt+O Alt+C Volume Volym Audio master Meas Beat Accent1 Accent2 Sample Preferences Apply the settings globally or to the current song: Use global metronome Use song-specific metronome Enable accents for the audio and midi metronomes: &Two samples (old samples) Four samples (&new samples, with accents) Audio Ljud MIDI Accent 2 Velocity Accent 1 Velocity Accent 1 Note Accent 2 Note Off Av Accents Accent sequence beats Current accent 1 sequence: (Click to change) Current accent 2 sequence: Factory User Användare Preset sequences Add current accents to presets Make a new preset from the current accents. + Delete preset Ta bort preset Delete the selected preset. Only user presets can be deleted. - Use preset Copy the preset to the current accents. -> Reset to Factory or User defaults Resets all current accent sequences to default Factory or User presets. ! Enable the precount Enable Aktivera Number of bars Use Mastertrack time signature Beats per bar Beat note type Enable precount when playing Precount on play Mute the metronome after precount Mute metronome Not supported yet MidiAudioControlBase Midi control Midikontroll Port: Channel: Kanal: Control type: Kontrolltyp: Control7 Kontroll7 Control14 Kontroll14 Pitch Tonhöjd Hi: Hög: Lo: Låg: Learn Lär &Learn MidiControllerEditDialogBase MusE: Define Midi Controller MusE: Definiera Midikontroll Name Namn Type Typ Min Val Min.värde Max Val Max.värde list of defined controllers lista med definierade kontroll This is the MusE list of defined controllers. Detta är MusEs lista med definierade kontroller. Properties Egenskaper Name Namn Max Value Max.värde Min Value Min.värde Type Typ create new entry skapa nytt inlägg pressing the New button you create a new entry in the MusE list of defined controllers tryck på Ny knappen för att skapa nytt inlägg i MusEs lista med definierade kontroller &Delete &Ta bort delete selected entry ta bort valt inlägg &OK &Ok &Cancel &Avbryt &Add &Lägg till Alt+A Alt+L Predefined Controller: Fördefinieradecontrollers: Managed Controller for Port Hanterad kontroll för port Channel Kanal This is a list of commonly used midi controllers. Note that in MusE pitch and program changes are handled like normal controllers. Detta är en lista med vanligt använda midikontroller. Notera att tonhöjd- och program-change hanteras som vanliga controllers i MusE. Pitch Tonhöjd Midi Controller Number High Byte Midikontrollnummer, hög byte Midi Controller Number Low Byte Midikontrollnummer, låg byte Range Område Control7 Kontroll7 Control14 Kontroll14 MidiFilterConfigBase MusE: Midi Input Filter MusE: Midi ingångsfilter Record Filter Inspelningsfilter Note On Noteon Poly Pressure Controller kontroll Program Change After Touch Pitch Bend Sysex Thru Filter Controller Filter Kontrollfilter Channel Filter Kanalfilter 14 10 6 12 4 2 9 8 3 13 15 16 7 11 5 1 MidiInputTransformDialogBase MusE: Midi Input Transformator MusE: Midi inputtransformator Filter Filter All Alla Equal Lika Unequal Ej lika Note Not Poly Pressure Control Change Aftertouch Pitch Bend NRPN RPN Value 2 Värde 2 Value 1 Värde 1 Event Type Händelsetyp Ignore Ignorera Higher Högre Lower Lägre Inside Innanför Outside Utanför Channel Kanal Port Processing Bearbetar Keep Behåll Fix Fäst Plus Pluss Minus Multiply Multiplicera Divide Dela Invert Invertera ScaleMap Skalmap Flip Vänd Dyn Random Slumpmässig Modules Moduler 1 2 3 4 enable modul 1 aktivera modul 1 enable modul 2 aktivera modul 2 enable modul 3 aktivera modul 3 enable modul 4 aktivera modul 4 Preset Name: Namn: Comment: Kommentar: Function Funktion &New &Ny create new preset skapa nytt preset &Delete &Ta bort delete preset ta bort preset &Dismiss &Stäng PresetList Presetlista Program Toggle Enable module 1 Enable module 2 Enable module 3 Enable module 4 MidiSyncConfigBase MusE: Midi Sync MusE: Midisync Apply Verkställ Ok Cancel Avbryt MTC Type: Typ: 24 25 30D 30N Offset: Offset: hour timma h minute minut m second sekund s frame ram f r subframe subram Sync receiving and sending Synkronisera inkommande och utgående Send and receive Jack transport Skicka och ta emot Jack-transport Send and receive Jack transport information, including stop, start and position. Skicka och ta emot Jack-transport information, inkluderar stop, start och position. Use Jack transport Använd Jack-transport Make MusE the Jack transport Timebase Master Låt MusE vara Jack-transport tidbas-master Make MusE the Jack transport Timebase Master. Allows Jack to show time as MusE Bars, Beats, and Ticks. MusE will try to become master, but other Jack clients can also take over later. You can always click here again for Master. Låt MusE vara Jack-transport tidbas-master. Låter Jack visa tid som MusE takter, taktdelar och tick. MusE försöker ta rollen som master men andra jack-klienter kan överta rollen senare. Du kan alltid trycka här igen för att låta MusE bli master. Jack transport Timebase Master Jack-transport tidbas-master Control MusE timing by external midi clock or MTC sync Kontrollera MusE timing med extern midi-klocka eller MTC synk When in slave mode, tempo is controlled externally. MusE can sync to midi clock, or MTC quarter frame sync. Enabled inputs in the list will be in effect (RMC, RMMC, RMTC). I slav-läge så kontrolleras tempo externt. MusE kan synkronisera mot midiklocka eller MTC kvartsram-synk. Aktiverade ingångar i listan används (RMC, RMMC, RMTC). Slave to external sync Slava till extern synkronisering Send start to first clock delay "Start till första klocka"-fördröjning Allows 'slow sync' devices time to synchronize to MusE. This value is the delay from sending start to sending the first clock. Ger 'långsama' enheter tid att synkronisera till MusE. Detta värde är en fördröjning från att start skickas till första klockan skickas. ms Send sync delay Skicka synkfördöjning 1 1 Note: Sync delay and MTC sync currently not fully implemented Notera: Synkdelay och MTC-synk, för tillfället inte helt implementerat Averaging applied to recorded external tempo changes. Utjämning applicerad på inspelade externa tempoförändringar External midi clock can be very jittery. Tempo is derived from it and recorded. It is usually desirable to average it and limit the number of recorded changes. Tiny: 2 section 4/4 = 8 stages. 1/8T note averaging, may produce jitter. Small: 3 section 12/8/4 = 24 stages. 1/4 note averaging, may still produce jitter. Medium: 3 section 28/12/8 = 48 stages. 1/2 note averaging. Less jitter. Large: 4 section 48/48/48/48 = 192 stages. Use this if the song has only one tempo. Very low quantization values can be used. Large pre-detect: 4 section 8/48/48/48 = 152 stages + first stage large step pre-detector. Use this if you expect sudden large tempo steps. None: Use only if high accuracy is needed for audio alignment on playback. Caution: Records thousands of tempo changes per minute. MusE may slow and the song file will be large. Tempo record averaging Utjämning av tempoinspelning bpm Tempo record quantization Kvantisering av tempoinspelning This button and other transport related buttons are found on the Transport Panel MidiTrackInfoBase MusE: TrackInfo MusE: Spårinfo output channel utgångskanal output port utgångsport off av Transp. Transp. Channel Info Kanalinfo Rec: Rec: Compr. Compr. Velocity Hastighet Length Längd all midi events are sent to this output channel Alla midihändelser skickas till denna utgångskanal Out ch Utkanal input routing ingångskoppling output routing utgångskoppling input detect in-detekt Input detect indicator. Detects all note on-off, controller, aftertouch, program change, and pitchbend (but not sysex or realtime) events on the selected channels, on the selected midi ports. Indikerar midi på ingång. Detekterar alla not on/off, kontroll-, aftertouch-, program change-, pitchbend-, men inte SysEX- eller realtids-event på de valda kanalerna på den valda midi-porten. Pass input events through ('thru') to output. Skicka inkommande midihändelser vidare till utgång. Select instrument patch Välj instrument-patch <unknown> <okänt> Add all settings to song Lägg till alla inställningar i sång All Alla Add bank + prog settings to song Lägg till bank + prog inställning i sång Add vol setting to song Lägg till volym inställning i sång Add pan setting to song Lägg till pan inställning i sång Bank Select MSB. Ctrl-click on/off. Bankval MSB. Ctrl-klick av/på. Bank Select LSB. Ctrl-click on/off. Bankval LSB. Ctrl-klick av/på. Program. Ctrl-click on/off. Program. Ctrl-klick av/på. Volume. Ctrl-click on/off. Volym. Ctrl-klick av/på. Change stereo position. Ctrl-click on/off. Byt stereo position. Ctrl-klick av/på. Bank Select MSB. Ctrl-double-click on/off. Bankval MSB. Ctrl-dubbelklick av/på. Bank Select LSB. Ctrl-double-click on/off. Bankval LSB. Ctrl-dubbelklick av/på. Program. Ctrl-double-click on/off. Program. Ctrl-dubbelklick av/på. Volume. Ctrl-double-click on/off. Volym. Ctrl-dubbelklick av/på. Change stereo position. Ctrl-double-click on/off. Byt stereo position. Ctrl-dubbelklick av/på. MidiTransformDialogBase MusE: Midi Transformator &New &Ny &Delete &Ta bort &Apply &Verkställ &OK &Ok &Cancel &Avbryt PresetList Presetlista Processing Bearbetar Event Type Händelsetyp Keep Behåll Fix Fäst Note Not Poly Pressure Control Change Aftertouch Pitch Bend NRPN RPN Plus Pluss Minus Multiply Multiplicera Divide Dela Value 2 Värde 2 Invert Invertera ScaleMap Skalmap Flip Vänd Dyn Random Slumpmässig Value 1 Värde 1 Length Längd Position Filter Filter All Alla Equal Lika Unequal Ej lika Ignore Ignorera Higher Högre Lower Lägre Inside Innanför Outside Utanför Bar Range Taktområde Preset Name: Namn: Comment: Kommentar: Range Område process all events bearbeta alla händelser selected tracks valda spår inside loop I loop Function Funktion Select Välj Quantize Kvantisera Delete Ta bort Transform Transformera Insert Sätt in Copy Kopiera Extract Extrahera Quantize Value Kvantisera värde Randomize Slumpa MixdownFileDialogBase MusE: Set Mixdown Wavefile MusE: Välj ljudfil för nermixning &OK &Ok &Cancel &Avbryt File Path Sökväg till fil Channel Kanal Stereo Mono 5.1 wav,16 Bit wav, 24 Bit wav, 32 Bit (float) Format MoveBase MusE: Move Notes MusE: Flytta noter Range Område All Events Alla Händelser Selected Events Valda Händelser Looped Events Loopade Händelser Selected Looped Valda loopade Value Värde Move by Flytta, steg ticks tick OK Cancel Avbryt Parts Parter Selected parts or c&urrent part All parts &in editor MusECore::AudioPreviewDialog Stop Stopp Play Spela MusECore::RasterizerModel Off Av Bar Takt MusECore::Scripts Do you want to process ALL or only selected events? &Selected &All MusE - external script failed MusE - externt skript misslyckades MusE was unable to launch the script, error message: %1 MusE kunde inte starta skript, felmeddelande: %1 Re-read script names from disc MusECore::Song Jack shutdown! Jack avstängd! Jack has detected a performance problem which has lead to MusE being disconnected. This could happen due to a number of reasons: - a performance issue with your particular setup. - a bug in MusE (or possibly in another connected software). - a random hiccup which might never occur again. - jack was voluntary stopped by you or someone else - jack crashed If there is a persisting problem you are much welcome to discuss it on the MusE mailinglist. (there is information about joining the mailinglist on the MusE homepage which is available through the help menu) To proceed check the status of Jack and try to restart it and then . click on the Restart button. Jack har detekterat ett prestandaproblem som har lett till att MusE blivit bortkopplad. Detta kan hända av en mängd anledningar: - ett prestanda problem med din specifika konfiguration. - en bugg i MusE (eller möjligen i en annan inkopplad mjukvara). - ett slumpmässigt problem som kanske aldrig händer igen. - Jack blev medvetet stoppad. - Jack krashade. Om problemet återkommer är du välkommen att diskutera det på MusE mailinglistan. (information om hur man kommer åt den finns på hjälpmenyn) För att fortsätta, säkerställ att Jack är igång och klicka på Omstart knappen. Automation: Automation: previous event föregående händelse next event nästa händelse set event ändra händelse add event lägg till händelse erase event ta bort händelse erase range ta bort händelseområde clear automation rensa automation Clear all controller events? Ta bort alla kontrollerhändelser? &Ok &Ok &Cancel &Avbryt MusE - external script failed MusE - externt skript misslyckades MusE was unable to launch the script, error message: %1 MusE kunde inte starta skript, felmeddelande: %1 Midi control Midikontroll Assign Tilldela Clear Rensa MusE: Tempo list MusE: Tempolista External tempo changes were recorded. Transfer them to master tempo list? Externa tempoändringar har spelats in. För över dem till master-tempolistan? Und&o &Ångra Re&do &Gör om Previous event Next event Set event Add event Erase event Erase range Clear automation Bypass Automation Controller MusEGui select Pointer Tool: with the pointer tool you can: select parts move parts copy parts välj pek-verktyg: med pekverktyget kan du: välja parter flytta parter kopiera parter select Pencil Tool: with the pencil tool you can: create new parts modify length of parts välj Pennverktyg: med pennverktyget kan du: skapa parter ändra längden på parter select Delete Tool: with the delete tool you can delete parts välj Suddverktyg: med suddverktyget kan du ta bort parter select Cut Tool: with the cut tool you can split a part välj Delningsverktyg: med delningsverktyget kan du dela på parter select Glue Tool: with the glue tool you can glue two parts välj Limverktyg: med limverktyget kan du limma ihop två parter select Score Tool: välj Partiturverktyg: select Quantize Tool: insert display quantize event välj Kvantiseringsverktyg: sätt in kvantiseringshändelser select Drawing Tool välj Ritverktyg select Muting Tool: click on part to mute/unmute välj Tystningsverktyg: klicka på en part för att Tysta/Avtysta Manipulate automation Hantera automation Cursor tool Markörverktyg pointer pekare pencil penna eraser sudd cutter delare score partitur glue lim quantize kvantisering draw rita mute parts tysta parter edit automation Redigera automatisering cursor markör MusEGui::Appearance Main application font, and default font for any controls not defined here. Huvudapplikationstypsnitt, även standard typsnitt för alla gui-kontroller ej definierade här. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Mixer-remsa och effektrack. Midi spårinfopanel. Midikontrollpanel-inmatningsbox. Transport controls. Transportkontroller. Time scale upper, and time signature. Controller graph and S/X buttons. Övre tidsskala och tidsignatur. Kontrollgraf och S/X knappar. Time scale lower, and arranger part name overlay. Lägre tidskala och partnamnöverlagring i redigerare. Tempo scale, and markers. Temposkala och markörer. Mixer labels. Auto-font-sizing up to chosen font size. Word-breaking but only with spaces. Mixeretiketter. Automatisktypsnittsstorlek upp till vald storlek. Ordbrytning men bara med mellanslag. Maximum mixer label auto-font-sizing font size. Max mixetikett automatisk typsnittsstorlek. Global opacity (opposite of transparency). Global opacitet (motsats till genomskinlighet). Standard Custom Anpassad MusE: load image MusE: ladda bild Select style sheet Välj stylesheet Keep Qt system style Behåll Qt systemstil &Ok &Ok &Cancel &Avbryt Style was changed. The program must be restarted for changes to take place. Restart now? Revert changes Revert all... Do you really want to reset all colors? No current color item Select Color: %1 Main application font, and default font for any controls not defined here. Mixer strips and effects racks. Midi track info panel. Midi control panel entry box. Arranger part name overlay. Time scale lower. MusEGui::Arranger Enable Recording Slå på inspelning Mute/Off Indicator Tysta/av indikator Solo Indicator Solo-indikator Track Type Spårtyp Track Name Spårnamn Midi output channel number or audio channels Midiutgångskanalnummer eller ljudkanal Midi output port or synth midi port Midiutgångsport eller syntmidiport Time Lock Tidlås Automation parameter selection Automation parameterval Notation clef Enable recording. Click to toggle. Slå på inspelning. Klicka för att växla. Mute indicator. Click to toggle. Right-click to toggle track on/off. Mute is designed for rapid, repeated action. On/Off is not! Mute indikator. Klicka för att växla. Högerklick för att växla spår av/på. Mute är gjort för snabb och återkommande användande. Av/På är det inte! Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed, indicated by a dark square. Soloindikator. Klicka för att växla. Kopplade spår får implicit solo, indikeras med en mörk fyrkant. Track type. Right-click to change midi and drum track types. Spårtyp. Högerklicka för att byta midi- och trum-spårtyper. Track name. Double-click to edit. Right-click for more options. Spårnamn. Dubbelklicka för att editera. Högerklick för fler alternativ. Midi/drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi/trumspår: Utgångskanalnummer. Ljudspår: Kanaler. Mitt/högerklick för att ändra. Midi/drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Midi/trumspår: Utport Syntspår: tilldelad midiport. Vänsterklick för att ändra. Högerklick för at visa GUI. Time lock Tidlås Notation clef. Select this tracks notation clef. Notationsklav. Välj spårets notationsklav. Arranger Arrangerare Cursor Markör Off Av Bar Takt Snap Fäst vid Len Längd song length - bars sånglängd - takter Type Typ midi song type typ av midisång Pitch Tonhöjd midi pitch miditonhöjd global midi pitch shift global midi-tonhöjdsjustering Tempo N TrackInfo Spårinfo Track Spår Port Ch Channel T Time lock Automation Clef Klav Track index Enable input monitor Enable input monitor. Click to toggle. Passes input through to output for monitoring. See also Settings: Automatically Monitor On Record Arm. Enable recording. Click to toggle. See also Settings: Automatically Monitor On Record Arm. Song length - bars Midi pitch Global midi pitch shift Midi tempo Enable recording Mute/Off indicator Solo indicator Track type Track name Midi/Drum track: Output channel number. Audio track: Channels. Mid/right-click to change. Midi/Drum track: Output port. Synth track: Assigned midi port. Left-click to change. Right-click to show GUI. Track index. Click to select track, CTRL+LMB to add/remove, SHIFT+LMB for range select, dblclick to select all of the same type. Enable/Disable input monitor. Passes input through to output (see also Settings: Automatically monitor on record arm). Enable/Disable recording. Mute indicator. Left-click to mute track (for quick action), right-click to toggle track on/off (for more permanent action). Track type. Click to switch between midi and drum track types. Track name. Double-click to edit. Right-click for context menu. Midi/Drum: Output channel number. Audio: Channels. Mid/right-click to change. Midi/Drum: Output port. Synth: Assigned midi port. Left-click to change, right-click to show synth GUI. Notation clef. Click to select this tracks notation clef. Cursor position Bars Takter Solo indicator. Click to toggle. Connected tracks are also 'phantom' soloed. Solo indicator. Connected tracks are also 'phantom' soloed. MusEGui::ArrangerColumns Control7 Kontroll7 Control14 Kontroll14 RPN NPRN RPN14 NRPN14 Pitch Tonhöjd Program Aftertouch MusEGui::ArrangerView MusE: Arranger MusE: Arrangerare Undo/Redo tools Ångra/Gör om verktyg panic panik C&ut Klipp &ut &Copy &Kopiera Copy in range Kopiera i område &Paste K&listra in Paste (show dialog) Klistra in (visa dialog) Paste c&lone Klistra in &klon Paste clone (show dialog) Klistra in klon (visa dialog) &Insert Empty Measure &Lägg in tom takt Delete Selected Tracks Ta bort valda spår Shrink selected parts Krymp valda parter Expand selected parts Expandera valda parter Clean selected parts Rensa valda parter Add Track Lägg Till Spår Select Välj Select &All Markera &Allt &Deselect All A&vmarkera Allt Invert &Selection Invertera &Markering &Inside Loop &I Loop &Outside Loop &Utanför Loop All &Parts on Track Alla &Parter i spår Score Partitur all tracks in one staff alla spår i en notlinje one staff per track en notlinje per spår New score window Nytt partiturfönster Pianoroll Pianorulle Drums Trummor List Lista Wave Wave Mastertrack Masterspår Graphic Grafisk Midi &Transform Midi &Transform Global Cut Globalt klipp Global Insert Global insättning Global Split Global delning Global Cut - selected tracks Globalt klipp - valda spår Global Insert - selected tracks Global insättning - valda spår Global Split - selected tracks Global delning - valda spår &Edit &Redigera &Structure &Struktur Functions Funktioner &Quantize Notes Kvantisera &noter Change note &length Ändra not l&ängd Change note &velocity Ändra not &anslag Crescendo/Decrescendo Transpose Transponera Erase Events (Not Parts) Ta bort händelser (ej parter) Move Events (Not Parts) Flytta händelser (ej parter) Set Fixed Note Length Sätt fast notlängd Delete Overlapping Notes Ta bort överlappande noter Legato Window &Config Fönster k&onfiguration New Ny Duplicate Selected Tracks Duplicera valt spår Configure &custom columns Konfigurera &anpassade kolumner Changed Settings Ändrade inställningar Unfortunately, the changed arranger column settings cannot be applied while MusE is running. To apply the changes, please restart MusE. Sorry. (we'll try to fix that) Tyvärr kan inte de ändrade kolumninställningarna i arrangeraren appliceras när MusE kör. För att aktivera starta om MusE. (Vi kommer att försöka fixa det senare) Purge hidden events from selected parts Ta bort gömda even från valda parter Remove track(s) Are you sure you want to remove this track(s)? D&elete Copy in Range Paste C&lone Paste to Selected &Track Paste Clone to Selected Trac&k Paste (Show Dialo&g)... Shrink Selected Parts Expand Selected Parts Purge Hidden Events from Selected Parts All Tracks in One Staff One Staff per Track New Score Window... Pianoroll... Drums... Wave... Midi &Transform... Global Cut - Selected Tracks Global Insert - Selected Tracks Global Split - Selected Tracks Fu&nctions Change Note &Length Change Note &Velocity &Display Configure &Custom Columns... New... Arranger Arrangerare Insert Track Lägg in spår Event List... Open in New Window Always open the part(s) in a new editor window Open Synth Plugin GUI... Display menu: View-specific display options. Toggle &Mixer Strip &Scripts Scripts for midi processing. Applied to selected midi parts (or else tracks). User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. Nothing to edit Inget att redigera MusEGui::AudioComponentRack Aux send level (dB) Vol Pan Översikt Mute Volume/gain Panorama/Balance Gain Förstärkning Calibration gain MusEGui::AudioMixerApp &Create &Skapa &View &Vy Routing Koppling Show Midi Tracks Visa midispår Show Drum Tracks Visa trumspår Show Wave Tracks Visa wavespår Show Inputs Visa ingångar Show Outputs Visa utgångar Show Groups Visa grupper Show Auxs Visa Auxspår Show Synthesizers Visa syntspår Show New Style Drum Tracks Visa nya typen trumspår Strips Traditional order Arranger order User order Show all hidden strips Unhide strip: (no hidden strips) MusEGui::AudioStrip aux send level aux send nivå 1/2 channel 1/2 kanal record inspelning mute tysta record downmix spela in nermixning solo mode sololäge off av input routing ingångskoppling output routing utgångskoppling Off Av Read Läs Touch Rör Write Skriv automation type automationstyp Pre Fader Listening (PFL) Record downmix Record arm Mute or proxy mute Solo or proxy solo Track off Input monitor Pass input through to output Automation type L meter peak/clip R meter peak/clip Input routing. Press F1 for help. Output routing. Press F1 for help. Volume/Gain Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. Auto off Automation type: Off, Read, Touch or Write. Press F1 for help. MusEGui::BigTime format display formatvisning bar takt beat taktdel tick tick minute minut second sekund frame ram subframe subram MusE: Bigtime Format display Bar Takt Beat Tick kryssa Minute Second Frame Subframe MusEGui::Canvas Tools Verktyg MusEGui::ClipListEdit MusE: Clip List Editor MusE: Clip-listredigerare Undo/Redo tools Ångra/Gör om verktyg panic panik Window &Config Fönster k&onfiguration MusEGui::ComboQuant Off Av MusEGui::CompactKnob off av MusEGui::CompactPatchEdit Patch name MusEGui::CompactSlider off av MusEGui::CpuStatusBar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) CPU load averaged over each GUI update period. DSP load read from JACK. Number of xruns. Double-click to reset. MusEGui::CpuToolbar CPU load averaged over each GUI update period DSP load read from JACK Number of xruns (click to reset) MusEGui::CtrlCanvas Drawing hint: Hold Ctrl to affect only existing events Rit-tips: Håll Ctrl för att bara ändra existerande händelser Use shift + pencil or line tool to draw new events Använd shift + penna- eller linjeverktyg för att rita nya händelser Make the current part's track match the selected drumlist entry Låt den nuvarande partens spårtyp matcha den valda trumspårsinställningen Use pencil or line tool to draw new events Använd penna eller linjeverktyg för att rita nya event Merge options Erase target Erase target events between source events Erase target WYSIWYG Include last source item width when erasing Erase target inclusive Include entire source range when erasing Merge actions Merge Merge the dragged items Merge a copy Merge a copy of the dragged items Cancel drag Cancel dragging the items Control canvas: Use Pencil tool to edit events and Draw tool to adjust them gradually. Hold Ctrl to affect only existing events. MusEGui::CtrlPanel select controller välj controller remove panel ta bort panel manual adjust manuell justering double click on/off dubbelklick av/på off av Velocity Hastighet add new ... lägg till ny... Instrument-defined Instrumentdefinierad Add ... Lägg till... Others Andra Edit instrument ... Redigera instrument... Common Controls Vanliga kontroller ctrl-double-click on/off kontroll dubbelklick av/på Select controller Remove panel Manual adjust (Ctrl-double-click on/off) All/Per-note velocity mode MusEGui::DList hide this instrument göm detta instrument show this instrument visa detta instrument this turns a grayed out eye into a blue eye denna ändrar ett utgråat öga till ett blått öga Hide this instrument This turns a blue eye into a crossed eye Show this instrument This turns a crossed eye into a blue eye Set field Sets a field Set row Sets a row Set column Sets a whole column to the field Set list Sets the whole list Reset field Resets a field in a row to default patch or instrument value Reset row Resets a row to the instrument values Reset column Resets a whole column to the instrument values Reset list Resets the whole list to the instrument values Reset track's drum list Resets all lists on all patches to the instrument values Promote field to default patch Promotes a field in a row to the default patch Promote row to default patch Promotes a row to the default patch Promote column to default patch Promotes a column to the default patch Promote list to default patch Promotes the whole list to the default patch Reset instrument field Reset instrument row Reset instrument list Set instrument field Set instrument row Set instrument list Drum list: Edit instrument-specific settings. MusEGui::DrumCanvas Moving items failed Flyttning misslyckades The selection couldn't be moved, because at least one note would be moved into a track which is different from both the original track and the current part's track. Changing the current part with ALT+LEFT/RIGHT may help. Urvalet kunde inte flyttas eftersom minst en not skulle ha blivit flyttad till ett spår som skiljer sig från både originalet och den nuvarande partens spår. Byta den valda parten med ALT+VÄNSTER/HÖGER kanske hjälper. Creating event failed Couldn't create the event, because the currently selected part isn't the same track, and the selected instrument could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Recording event failed Couldn't record the event, because the currently selected part isn't the same track, and the instrument to be recorded could be either on no or on multiple parts, which is ambiguous. Select the destination part, then try again. Internal error Wtf, some nasty internal error which is actually impossible occurred. Check console output. Nothing recorded. Drum map Karta över slagverk Reset the track's drum map with instrument defaults? Note: Velocity: Start (bar): Start (time): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete LMB: Select | CTRL+LMB: Multi select | SHIFT+LMB: Select pitch | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete LMB: Delete Arrow keys to move cursor, V,B,N,M keys to create events with increasing velocity, Del to delete. Drum canvas: Use Pencil tool to create and edit events, Pointer tool to select, Cursor tool for special keyboard entry mode (arrow keys, V, B, N, M, Del). Press F1 for help. MusEGui::DrumEdit mute instrument tysta instrument sound name ljudnamn volume percent volymprocent quantisation kvantisering this input note triggers the sound denna in-not triggar ljudet note length notlängd this is the note which is played det är denna not som visas output channel (hold ctl to affect all rows) Utgångskanal (håll Ctrl för att ändra alla rader) output port (hold ctl to affect all rows) Utgångsport (håll Ctrl för att ändra alla rader) shift + control key: draw velocity level 1 shift + kontrolltangent: rita anslag nivå 1 control key: draw velocity level 2 kontrolltangent: rita shift key: draw velocity level 3 Shift: Rita hastighet 3 draw velocity level 4 Rita hastighet 4 output channel (ctl: affect all rows) Utgångskanal. (CTRL påverkar alla spår) output port (ctl: affect all rows) Utgång (CTRL påverkar alla spår) &File &Arkiv Load Map Ladda mappning Save Map Spara Mappning Reset GM Map GM Återställ kartan &Edit &Redigera Cut Klipp ut Copy Kopiera Copy events in range Kopiera intervall händelser Paste Klistra in Paste (with Dialog) Klistra in (med dialog) Delete Events Ta bort evenemang &Select &Välj Select All Välj Allt Select None Välj Ingen Invert Invertera Inside Loop I loop Outside Loop Utanför loop Previous Part Region föregående Next Part Nästa Region Fu&nctions Fu&nktioner Re-order list Ordna om listan Set Fixed Length Sätt fast längd Modify Velocity Modifiera Velocity Crescendo/Decrescendo Crescendo/Decrescendo Quantize Kvantisera Erase Event Ta bort Händelse Move Notes Flytta noter Delete Overlaps Eliminera överlappande &Plugins &Insticksprogram Window &Config Fönster&inställningar Drum tools Slagverktyg Load Drummap Ladda trummappning Store Drummap Spara trummappning Step Record Stegvis inspelning Midi Input Entry MIDI cursor tools Markören verktyg Set step size for cursor edit Ställa stegstorleken för att ändra markören panic panik transport transport ctrl ctrl Add Controller View Lägg View Controller M mute M Sound Ljud Vol QNT E-Note Len Längd A-Note Ch Port LV1 LV2 LV3 LV4 Muse: Load Drum Map Muse: Ladda trummappning MusE: Store Drum Map MusE: Spara trummappning Drum map Karta över slagverk Reset the drum map with GM defaults? ¿Återställ kartan över slagverk i GM banken? Group Grupp Don't group Group by channel Group maximally Show/Hide Also show hidden instruments Show all instruments Hide all instruments Only show used instruments Only show instruments with non-empty name or used instruments H hide Play Events Spela händelser Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) Paste to current part &Display Step record Midi input Play events Cursor tools Add controller view Event List... Display menu: View-specific display options. Cursor step Note which triggers the sound Note which is played Override track output channel (hold CTRL to affect all rows). Override track output port (hold CTRL to affect all rows). CTRL + META keys: Draw velocity level 1 META key: Draw velocity level 2 META + ALT keys: Draw velocity level 4 &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. MusEGui::EditCAfterDialog MusE: Enter Channel Aftertouch Muse: Skriv in channel aftertouch Time Position Tidsposition Pressure Tryck MusEGui::EditEventDialog Ok Om Cancel Avbryt MusEGui::EditInstrument MusE: Create file failed Muse: Fel vid skapande av filen MusE: Write File failed MusE: Misslyckades med att skriva fil MusE: Save Instrument Definition Muse: Spara definition av instrument Instrument Definition (*.idf) Definition av instrument (*. IDF) MusE: Save instrument as Muse: Spara instrument som Enter a new unique instrument name: Ange ett nytt unikt namn på instrument: The user instrument '%1' already exists. This will overwrite its .idf instrument file. Are you sure? Användarinstrumentet '%1' finns redan. Detta kommer att skriva över dess.idf-fil. Är du säker? MusE: Bad instrument name Muse: Ogiltig namn på instrument Please choose a unique instrument name. (The name might be used by a hidden instrument.) Välj ett unikt namn till instrumentet (Namnet kanske använd av ett dolt instrument.) MusE: Bad patch name Muse: Ogiltigt programnamn Please choose a unique patch name Välj ett unikt programnamn MusE: Bad patchgroup name Muse: Felaktigt gruppnamn Please choose a unique patchgroup name Välj ett unikt namn i gruppen MusE: Bad controller name Muse: ogiltigt kontroll namn Please choose a unique controller name Välj ett unikt kontroll-namn MusE: Cannot add common controller Muse: Kan inte lägga till vanlig kontroll A controller named '%1' already exists. Kontroll med namn %1 finns redan. A controller number %1 already exists. Kontroll med nummer %1 finns redan. MusE MusE The current Instrument contains unsaved data Save Current Instrument? Det nuvarande instrumentet innehåller osparat data. Vill du spara nu? &Save &Spara &Nosave &Inte spara &Abort &Avbryt Name Namn Vol Quant E-Note Len Längd A-Note LV1 LV2 LV3 LV4 Control7 Kontroll7 Control14 Kontroll14 RPN NPRN RPN14 NRPN14 Pitch Tonhöjd Program PolyAftertouch Aftertouch Tick kryssa Data Data New controller: Error Error! All control numbers are taken up! Clean up the instrument! Use note offs No note offs Convert to 0-vel note ons H hide M mute M Ch kanal Port Port Hide instrument Mute instrument Sound name Volume percent Quantisation This input note triggers the sound Note length This is the note which is played Override track output channel (hold ctl to affect all rows) Override track output port (hold ctl to affect all rows) Control + meta keys: Draw velocity level 1 Meta key: Draw velocity level 2 Draw default velocity level 3 Meta + alt keys: Draw velocity level 4 Override track output channel (ctl: affect all rows) Override track output port (ctl: affect all rows) MusEGui::EditMetaDialog MusE: Enter Meta Event MusE: Mata in Meta händelse Time Position Tidsposition Meta Type Metatyp Enter Hex in hexadecimalt MusEGui::EditPAfterDialog MusE: Enter Poly Aftertouch MusE: Mata in Poly Aftertouch Time Position Tidsposition Pitch Tonhöjd Pressure Tryck MusEGui::EditToolBar select Pointer Tool: with the pointer tool you can: select parts move parts copy parts välj pek-verktyg: med pekverktyget kan du: välja parter flytta parter kopiera parter select Pencil Tool: with the pencil tool you can: create new parts modify length of parts välj Pennverktyg: med pennverktyget kan du: skapa parter ändra längden på parter select Delete Tool: with the delete tool you can delete parts välj Suddverktyg: med suddverktyget kan du ta bort parter select Cut Tool: with the cut tool you can split a part välj Delningsverktyg: med delningsverktyget kan du dela på parter select Glue Tool: with the glue tool you can glue two parts välj Limverktyg: med limverktyget kan du limma ihop två parter select Score Tool: välj Partiturverktyg: select Quantize Tool: insert display quantize event välj Kvantiseringsverktyg: sätt in kvantiseringshändelser select Drawing Tool välj Ritverktyg select Muting Tool: click on part to mute/unmute välj Tystningsverktyg: klicka på en part för att Tysta/Avtysta Manipulate automation Hantering automation Cursor tool Markören verktyg pointer pekare pencil penna eraser sudd cutter delare score partitur glue lim quantize kvantisering draw rita mute parts tysta parter edit automation Redigera automatisering cursor markör Edit Tools Redigeringsverktyg Select Pointer tool: With the pointer tool you can: select parts move parts copy parts Select Pencil tool: With the pencil tool you can: create new parts modify length of parts Select Delete tool: With the delete tool you can delete parts Select Cut tool: With the cut tool you can split a part Select Glue tool: With the glue tool you can glue two parts Select Score tool: Select Quantize tool: Insert display quantize event Select Drawing tool Select Muting tool: Click on part to mute/unmute Select Cursor (tracker mode) tool: With the cursor tool you can: navigate with arrow keys use VBNM to place notes change step with 0 and 9 Select Range tool Select Panning tool Select Zoom tool Pointer Pencil Eraser Cutter Glue Range Område Pan Översikt Zoom Score Partitur Quantize Kvantisera Draw Mute parts Edit automation Cursor Markör Select Stretch tool Select Sample rate tool Stretch Sample rate Samplingshastighet MusEGui::EffectRack effect rack effektrack new ny change ändra move up flytta upp move down flytta ner remove ta bort bypass hoppa show gui Visa gränssnitt show native gui Visa eget gränssnitt save preset Spara pre instÄllning MusE: Save Preset MusE:Spara pre instÄllning Replace effect Ersätta effekt Do you really want to replace the effect %1? Vill du verkligen ersätta effekten %1? Presets Preset New Ny Change Move up Move down Remove Ta bort Bypass Show gui Show native gui Save preset Spara preset Effect rack Double-click a slot to insert FX Effect rack: Double-click a slot to insert/edit effect. RMB to open context menu. Press F1 for help. MusEGui::GlobalSettingsConfig MusE: Choose start template or song MusE: Välj startsång eller mall MusEGui::Header Track Info Columns Spåra information kolonn Custom column MusEGui::LCDPatchEdit High bank: Low bank: Program (Ctrl-double-click on/off) off av High bank Low bank Program MusEGui::LMaster MusE: Mastertrack MusE: Masterspår &Edit &Redigera Insert Tempo Sätt in tempo Insert Signature Sätt in signatur Insert Key In nyckeln Edit Positon Editera position Edit Value Editera värde Delete Event Ta bort event Window &Config Fönster&inställningar Undo/Redo tools Verktyg Ångra / Gör om Edit tools Redigera-verktyg Tempo Tempo Timesig Compaz Key Nyckel new tempo nytt tempo new signature ny signatur new key Ny nyckel panic panik transport transport Meter metronom Time Tid Type Typ Value Värde Reposition of the initial tempo and signature events is not allowed Ompositionering av initalt tempo och/eller signaturevent är inte tillåtet MusE: List Editor Muse: Listeditor Input error, conversion not OK or value out of range Inmatningsfel, konvertering felaktig eller värde utanför område Reposition of tempo and signature events to start position is not allowed! Ompositionering av tempo och/eller signaturevent till startposition är inte tillåtet! Signature Signatur Position Delete Ta bort Insert tempo change Insert time signature change Insert key change Edit position Edit value Delete event MusEGui::ListEdit insert Note Mata in Not insert SysEx Mata in SysEx insert Ctrl Mata in Ctrl insert Meta Mata in Meta insert Channel Aftertouch Mata in Aftertouch för kanal insert Poly Aftertouch Mata in Poly Aftertouch &Edit &Redigera Cut Klipp ut Copy Kopiera Paste Klistra in Delete Events Ta bort evenemang Window &Config Fönster&inställningar Undo/Redo tools Verktyg Ångra / Gör om Insert tools Sätt verktyg panic panik transport transport Tick kryssa Bar Takt Type Typ Ch kanal Val A Värde A Val B Värde B Val C Värde C Len Längd Comment Kommentar Increase Tick Öka tick Decrease Tick Minska tick MusE: List Editor Muse: Listeditor Insert Note Mata in not Insert SysEx Mata in SysEx Insert Ctrl Mata in Ctrl Insert Meta Mata in Meta Note Not SysEx Ctrl Meta Tick+ Tick- Delete Ta bort Insert note event Insert system exclusive event Insert controller event Insert meta event Increase tick value Decrease tick value Delete event MusEGui::MPConfig Default input connections Standard inputs Are you sure you want to apply to all existing midi tracks now? Är du säker på att du vill använda för alla befintliga MIDI-spår? Default output connections Standard outputs Setting will apply to new midi tracks. Do you want to apply to all existing midi tracks now? Inställningen gäller för nya MIDI-spår. Vill du också använda för alla befintliga MIDI-spår? MusE: bad device name Muse: Felaktigt enhetsnamn please choose a unique device name Välj ett unikt enhetsnamn in in out ut Show first aliases Visa först alias Show second aliases Visa andra alias Toggle all Ändra alla Change all tracks now Ändra alla spår nu Create Jack device Skapa Jackanslutning Port Number Portnummer Enable gui Aktivera gui Enable reading Aktivera läsning Enable writing Aktivera skrivning Port instrument Instrument-port Midi device name. Click to edit (Jack) MIDI enhetsnamn, Klicka här för att redigera (Jack) Connections from Jack Midi outputs MIDI utgångar från Jack Connections to Jack Midi inputs MIDI ingångar från Jack Auto-connect these channels to new midi tracks Anslut automatiskt dessa kanaler till nya midi-spår Auto-connect new midi tracks to these channels Anslut automatiskt dessa midi spår till dessa kanaler Auto-connect new midi tracks to this channel Koppla automatiskt nya MIDI-spår till den här kanalen Device state Enhetsstatus Enable gui for device Aktivera gui för enheten Enable reading from device Aktivera läsning från enheten Enable writing to device Aktivera skrivning till enheten Name of the midi device associated with this port number. Click to edit Jack midi name. MIDI enhetsnamn associerat med detta portnummer. Klicka för att ändra MIDI enhetsnamnet i Jack. Instrument connected to port Instrument kopplat till denna port Connections from Jack Midi output ports Anslutningar från Jack MIDI utgångsportar Connections to Jack Midi input ports Anslutningar till Jack MIDI ingångsportar Auto-connect these channels, on this port, to new midi tracks. Anslut automatisks dessa kanaler, på denna port, till nya MIDI spår. Connect new midi tracks to these channels, on this port. Anslut nya MIDI-spår till dessa kanaler, för denna port. Connect new midi tracks to this channel, on this port. Anslut nya MIDI-spår till denna kanal, på den här porten. State: result of opening the device Status: resultat av enhetsöppning Port Port GUI I O Instrument Instrument Device Name Enhetsnamn In routes Ingångsväg Out routes Utgångsvägar Def in ch Std inkanal Def out ch Std utg kanal State Tillstånd <unknown> <okänd> <none> <Inget> Remove Ta bort Midi device name Midi device type Connections from Jack Midi Connections to Jack Midi Result of opening the device: OK: Assigned to a port and in use Closed: Unassigned to a port, or closed R/W Error: Unable to open for read or write Unavailable: USB midi unplugged, or external application not running, or synth plugin not installed etc. (Jack Midi devices have 'unavailable ports' in the routes columns.) Unavailable devices or ports can be purged with 'Remove' or with the advanced router. Enable Graphical User Interface for device Connections from Jack Midi ports Connections to Jack Midi ports Type Typ In Out Please choose a unique device name Port number MusEGui::MTScale bar scale Skalstrecken Bar scale Bar scale: Use mouse buttons to set position and range markers. Hold Shift to set (LMB) or delete (RMB) custom markers. MusEGui::MTScaleFlo bar scale Skalstrecken Bar scale MusEGui::MarkerView MusE: Marker MusE: Markör add marker lägg till markör delete marker ta bort markör &Edit &Redigera Window &Config Fönster&inställningar Undo/Redo tools Verktyg Ångra / Gör om edit tools redigeringsverktyg panic panik transport transport Bar:Beat:Tick Takt:Slag:Tick Hr:Mn:Sc:Fr:Sf Hr:Mn:Sc:Fr:Sf Lock Lås Text Text Marker Properties Markörinställningar Add marker Delete marker Edit tools Redigera-verktyg Description Beskrivning Edit tick Edit SMPTE timecode Toggle timecode editing Edit marker description <Description> MusEGui::Master Tools Verktyg MusEGui::MasterEdit MusE: Mastertrack MusE: Masterspår Window &Config Fönster&inställningar Undo/Redo tools Verktyg Ångra / Gör om panic panik transport transport Enable master Möjliggöra läraren Enable Aktivera Enable usage of master track Aktivera användning av masterspår Info Informationen Cursor Markör time at cursor position tid vid markörposition tempo at cursor position tempo vid markörposition Off Av Bar Takt Snap Fäst vid CurPos Placera markören tempo at current position tempo vid nuvarande position time signature at current position time signature vid nuvarande position &Display Time at cursor position Tempo at cursor position Mastertrack Masterspår Display menu: View-specific display options. MusEGui::MetronomeConfig Reset accents: Resets all accents to the defaults (first in list) of the current preset category (Factory or User). Proceed? MusEGui::MidiAudioControl Control7 Kontroll7 Control14 Kontroll14 RPN NPRN RPN14 NRPN14 Pitch Tonhöjd Program Aftertouch MusEGui::MidiComponentRack Pro Var Lev Rev Efterklang Cho Kören Pan Översikt Program VariationSend Leverans Varians ReverbSend Sända efterklang ChorusSend Skicka kören Pan/Balance Översikt / Balans Controller (Ctrl-double-click on/off) <unknown> Instrument Instrument Transpose Transponera Transpose notes up or down Delay Offset playback of notes before or after actual note Length Längd Change note length in percent of actual length Velocity <html><head/><body><p>Add or substract velocity to notes on track.</p><p><span style= font-style:italic;>Since the midi note range is 0-127 this <br/>might mean that the notes do not reach <br/>the combined velocity, note + Velocity.</span></p></body></html> Compress Compress the notes velocity range, in percent of actual velocity <Patch> MusEGui::MidiInputTransformDialog New Ny MusEGui::MidiStrip off av double click on/off Docle klicka på På / Av VariationSend Leverans Varians Var Lev ReverbSend Sända efterklang Rev Efterklang ChorusSend Skicka kören Cho Kören dB dB Pan/Balance Översikt / Balans Pan Översikt record Post mute tysta solo mode endast mode input routing dirigering av inträde output routing utgångsrouting ctrl-double-click on/off kontroll dubbelklick av/på Record arm Mute or proxy mute Solo or proxy solo Track off Input monitor Pass input through to output Intput routing. Press F1 for help. Output routing. Press F1 for help. Inst Prop Instruments and controllers Properties Egenskaper Volume/Gain (Ctrl-double-click on/off) Input monitor: Pass input through to output. Mute or proxy mute. Connected tracks are 'phantom' muted. Solo or proxy solo. Connected tracks are 'phantom' soloed. Press F1 for help. MusEGui::MidiSyncConfig Port Number Portnummer Name of the midi device associated with this port number Namn på midienhet associerad med detta portnummer Midi clock input detected Midi klock-indata upptäckt Midi tick input detected Midi tick-indata upptäckt Midi real time input detected Midi realtids-indata upptäckt MMC input detected MMC indata upptäckt MTC input detected MTC indata upptäckts Detected SMPTE format Upptäckt SMPTE-format Receive id number. 127 = Global. Double click to edit. Mottagar-id nummer: 127 = Global. Dubbelklicka för att editera. Accept midi clock input Acceptera midiklock-indata Accept midi real time input Acceptera midi realtidsindata Accept MMC input Acceptera MMC indata Accept MTC input Acceptera MTC indata Receive start rewinds before playing Mottagande av start spolar tillbaka före uppspelning Transmit id number. 127 = Global. Double click to edit. Skicka id nummer: 127 = Global. Dubbelklicka för att editera. Send midi clock output Skicka midi klock utdata Send midi realtime output Skicka midi realtidsutdata Send MMC output Skicka MMC utdata Send MTC output Skicka MTC utdata Midi clock input detected. Current port actually used is red. Click to force a port to be used. Midi klock-indata upptäckt. Den nubarande porten som faktiskt används är röd. Klicka för att tvinga en port att användas. Midi realtime input detected, including start/stop/continue, and song position. Midi realtidsindata upptäckt, inkluderar start/stopp/fortsätt och sång-position. MMC input detected, including stop/play/deferred play, and locate. MMC indata upptäckt, inkluderar stopp/spela/fördröjd uppspelning och lokalisering. MTC input detected, including forward quarter-frame sync and full-frame locate. Current port actually used is red. Click to force a port to be current. MTC indata hittat, inkluderar frammåt kvartsram-synk och full-ram lokalisering. Nuvarande port som används är röd. Klicka för att tvinga en port att användas. Detected SMPTE format: 24fps, 25fps, 30fps drop frame, or 30fps non-drop Detects format of MTC quarter and full frame, and MMC locate. Detektera SMPTE format: 24fps, 26fps, 30fps tappa rammar, eller 30fps inga tappade Detekterar format av MTC kvarts- och fullramar, och MMC lokalisering. Receive id number. 127 = global receive all, even if not global. Ta emot id nummer. 127 = global, ta emot alla, även om inte global. Accept midi clock input. Only one input is used for clock. Auto-acquire: If two or more port realtime inputs are enabled, the first clock detected is used, until clock is lost, then another can take over. Best if each turns off its clock at stop, so MusE can re-acquire the clock from another port. Click on detect indicator to force another. Acceptera midi-klock-indata. Bara en ingång används för klocka. Auto-välj: Om två eller flera realtids-indata-portar är aktiverade, den första klockan som hittats används tills klockan tappas, då kan en annan ta över. Det är bäst om alla slår av sin klocka vid stopp så MusE can återinitiera klockan från en annan port. Klicka på detekteringsindikatorn för att tvinga en annan. Accept midi realtime input, including start/stop/continue, and song position. Non-clock events (start,stop etc) are accepted by ALL enabled ports. This means you may have several master devices connected, and muse will accept input from them. Acceptera midi realtids-indata, inkluderar start/stop/fortsätt och sång-position. Icke-klock-händelser (start, stopp etc) accepteras av alla aktiverade portar. Detta betyder att det kan finnas flera kopplade master-enheter kopplade som MusE accepterar indata från. Accept MMC input, including stop/play/deferred play, and locate. Acceptera MMC indata, inkluderar stopp/spela/fördröjd uppspelning och lokalisering. Accept MTC input, including forward quarter-frame sync and full-frame locate. See 'rc' column for more help. Acceptera MTC indata, inkluderar frammåt kvartsram-synk och full-ram lokalisering. See 'rc' kolumn för mer hjälp. When start is received, rewind before playing. Note: It may be impossible to rewind fast enough to synchronize with the external device. När start är mottagen, spola tillbaka innan uppspelning. Notera: Det kan vara omöjligt att spola tillbaka tillräckligt snabbt för att synkronisera med en externt enhet. Transmit id number. 127 = global transmit to all. Sänd id-nummer. 127 = global sänd till alla. Send midi clock output. If 'Slave to External Sync' is chosen, muse can re-transmit clock to any other chosen ports. Skicka midiklock-utdata. Om 'Slava till externsynk' är vald, MusE can återsända klockan till andra valda portar. Send midi realtime output, including start/stop/continue, and song position. If 'Slave to external sync' is chosen, muse can re-transmit midi realtime input messages to any other chosen ports. This means you may have several slave devices connected, and muse can re-send realtime messages to any or all of them. Skicka midi realtids-utdata, inkluderar start/stopp/fortsätt och sång-position. Om 'slava till externsynk' är vald, MusE kan återsända midi realtids-indata meddelanden till andra valda portar. Detta betyder att MusE kan skicka vidare realtids meddelanden till flera kopplade slav-enheter. Port Port Device Name Enhetsnamn type typ MusE MusE Settings have changed Apply sync settings? Inställningar har ändrats Aktivera synkinställningar? &Apply &No &Nej &Abort &Avbryt <none> None Ingen Tiny Jätteliten Small Liten Large Stor Large with pre-detect Stor med fördetektering Sync to this device. Click to select. Sync to this device. Click to select. MusE will synchronize to this device's clock or MTC quarter-frame, if 'slave to external sync' is on. MTC input detected, including forward quarter-frame sync and full-frame locate Accept midi clock input. Only one port can be used for clock sync. Accept MTC input, including forward quarter-frame sync and full-frame locate. Only one port can be used for sync. When start is received, rewind before playing. Note: In some cases, such as having many project audio tracks, it may be impossible to rewind fast enough to synchronize with the external device. Manually rewinding the device before playing is recommended. Type Typ MusEGui::MidiTransformerDialog New Ny MusEGui::MixdownFileDialog Wave Files (*.wav);;All Files (*) Ljudfiler (*.wav);;Alla filer (*) MusEGui::MusE Failed to start audio! Gick inte att starta ljud! Was not able to start audio, check if jack is running. Kan inte starta ljud, kontrollera status på Jack. Timeout waiting for audio to run. Check if jack is running. Tid löpte ut i väntan på att ljud skulle starta. Kontrollera om Jack kör. Und&o &Ångra Re&do &Gör om undo last change to song ångra sista ändring i sång redo last undo gör om sista ångring Loop Loop loop between left mark and right mark loopa mellan vänster och höger markering Punchin Tryck in record starts at left mark inspelning börjar vid vänster markering Punchout Tryck ut record stops at right mark inspelning slutar vid höger markering Start Starta rewind to start position spola tillbaka till startposition Rewind Spola tillbaka rewind current position flytta markör bakåt Forward Framåt move current position flytta markör Stop Stopp stop sequencer stoppa sequencer Play Spela start sequencer play starta sequenceruppspelning Record Spela in to record press record and then play för att spela in, tryck på record och sedan play Panic Panik send note off to all midi channels skicka note-off till alla midi kanaler &New &Ny Create New Song Ny sång &Open &Öppna Click this button to open a <em>new song</em>.<br>You can also select the <b>Open command</b> from the File menu. Klicka här för att öppna en <em>ny sång</em>.<br>Du kan också välja menyvalet <b>Öppna</b> från Arkivmenyn. Open &Recent &Nyligen öppnade &Save &Spara Click this button to save the song you are editing. You will be prompted for a file name. You can also select the Save command from the File menu. Klicka här för att spara den sång du håller på att redigera. En förfrågan om filnamn kommer att ställas. Du kan också välja menyalternativet Spara från Arkivmenyn. Save &As S&para som Import Midifile Importera midifil Export Midifile Exportera midifil Import Part Importera Part Import Audio File Importera ljudfil Find unused wave files Hitta oanvända filer &Quit A&vsluta Song Info Synpunkter från sessionen Transport Panel Transportpanel Bigtime Window Bigtime fönster Mixer A Mixer A Mixer B Mixer B Cliplist Lista över ljudfiler Marker View Markörvy Arranger View Sortera visningar Fullscreen Helskärm &Plugins Insticks&program Edit Instrument Redigera instrument Input Plugins Effekter av Entry Transpose Transponera Midi Input Transform MIDI ingång omvandling Midi Input Filter MIDI Filtrering Midi Remote Control Midi-fjärrkontroll Rhythm Generator Rytm generatorn Reset Instr. Återställ instrumentet. Init Instr. Initiera instrumentet. Local Off lokal av Bounce to Track Bounce till Spår Bounce to File Bounce till Fil Restart Audio Starta om Audio Mixer Automation Mixerautomation Take Snapshot Ta ett snapshot Clear Automation Data Rensa automationsdata Cascade Kaskad Tile Rubricerade In rows I rader In columns I kolumner Global Settings Globala inställningar Configure Shortcuts Konfigurera kortkommandon Follow Song Följ sång Don't Follow Song Följ inte Sång Follow Page Följ sida Follow Continuous Följ kontinuerligt Metronome Metronom Midi Sync Midi synk Midi File Import/Export importera/Exportera MIDI-fil Appearance Settings Utseendeinställningar Midi Ports / Soft Synth Midiportar / Mjukvarusyntar &MusE Homepage M&usEs hemsida &Report Bug... &Rapportera fel... &About MusE &Om MusE File Buttons Arkivknappar Undo/Redo Ångra / Gör om Transport Transport &File &Arkiv &View &Visa &Midi &Audio L&jud A&utomation A&utomatisering &Windows &Fönster Se&ttings &konfiguration &Help &Hjälp Cannot read template Kan inte läsa mall File open error Fel vid öppning av fil File read error Fel vid läsning av fil Unknown File Format: %1 Okänt filformat:%1 MusE: Song: %1 Muse sång:%1 MusE: load project MusE: öppna projekt MusE: load template MusE: ladda mall MusE: Write File failed MusE: Misslyckades med att skriva fil The current Project contains unsaved data Save Current Project? Det nuvarande projektet innehåller osparade ändringar. Spara nuvarande projekt? S&kip &Hoppa över &Cancel &Avbryt MusE: Save As MusE: Spara Som Nothing to edit Inget att redigera MusE: Bounce to Track MusE: Bounce till spår No wave tracks found Det finns inget ljudspår No audio output tracks found Inget utgångs-spår Select one audio output track, and one target wave track Välj ett utgångs-ljudspår, och ett mål-ljudspår Select one target wave track Välj ett ljudspår mål Select one target wave track, and one audio output track Välj ett mål-ljudspår, och en ljudutgång MusE: Bounce to File MusE: Bounce till fil Select one audio output track Välj ljudutgång MusE: Bounce Muse: Bounce set left/right marker for bounce range sätt vänster och höger markör till bounce-område The current Project contains unsaved data Load overwrites current Project: Save Current Project? Det nuvarande projektet innehåller osparade ändringar Öppning skriver över nuvarande Projekt Spara nuvarande Projekt? &Abort &Avbryt MusE: Export Midi MusE: Exportera Midi no help found at: ingen h jälp hittad vid: MusE: Open Help Muse: Arkiv hjälp Unable to launch help Kan inte öppna hjälp For some reason MusE has to launch the default browser on your machine. Av någon anledning måste MusE starta standardwebbläsare på din maskin. MusE: Import Midi MusE: Importera Midi Add midi file to current project? Lägg midifil till nuvarande projekt? &Add to Project &Lägg till projekt &Replace &Ersätt reading midifile läser midifil failed: misslyckades: Import part is only valid for midi and wave tracks! Importera parter är bara möjligt med MIDI- eller ljud-spår! MusE: load part MusE: ladda part No track selected for import Inget spår valt för import to import an audio file you have first to selecta wave track Om du vill importera en ljudfil väljer ljudspår Import Wavefile Importera ljudfil This wave file has a samplerate of %1, as opposed to current setting %2. Do you still want to import it? Denna fil har en samplingsfrekvens på %1, Nuvärdet för sessionen är%2. Vill du importera den ändå? &Yes &Ja &No &Nej About &Qt om &Qt Song Position Sångposition Tempo Tempo Signature Signatur %n part(s) out of %1 could not be imported. Likely the selected track is the wrong type. %n parter av %1 kunde inte importeras. Antagligen var det valda spåret av fel typ. %n part(s) could not be imported. Likely the selected track is the wrong type. %n parter kunde inte importeras. Antagligen var det valda spåret av fel typ. This will clear all automation data on all audio tracks! Proceed? Detta rensar all automationsdata på alla ljudspår! Fortsätt? This takes an automation snapshot of all controllers on all audio tracks, at the current position. Proceed? Detta tar en ögonblicks bild av automationen för alla kontrollrar på alla ljudspår vid den nuvarande positionen. Försätt? MusE: Song: MusE: Warning The song uses multiple ports but export format 0 (single track) is set. The first track's port will be used. Playback will likely be wrong unless the channels used in one port are different from all other ports. Canceling and setting a different export format would be better. Continue? Wave import error There are too many wave files of the same base name as imported wave file Can not continue. Can't create new wav file in project folder! Failed to initialize sample rate converter! Cancel Avbryt Resampling wave file "%1" from %2 to %3 Hz... Output has clipped Resampling again and normalizing wave file "%1" Try %2 of %3... File version is %1.%2 Current version is %3.%4 Conversions may be applied if file is saved! Opening file Do not warn again Cpu load To import an audio file you have first to select a wave track Reading midifile Load configuration colors Color settings will immediately be replaced with any found in the file. Are you sure you want to proceed? &Ok &Ok Save configuration colors Timeout waiting for audio to run. Check if jack is running or try another driver. Was not able to start audio, check if jack is running or try another driver. Undo last change to song Redo last undo Loop between left mark and right mark Punch in Record starts at left mark Punch out Record stops at right mark Rewind to start position Rewind current position Move current position Stop sequencer Restart rec Start sequencer play To record press record and then play Turn on/off metronome Create new song New from &Template... Create new song from template &Open... &Öppna... Open song from file Click this button to open an existing song. Save current song Click this button to save the song you are editing. You will be prompted for a file name. Save &As... S&para som... &Close Import Midifile... Export Midifile... Import Part... Import Audio File... Find Unused Wave Files... Song Info... Mastertrack Masterspår Edit Instrument... Transpose... Midi Input Transform... Midi Input Filter... Midi Remote Control... Reset Instrument Init Instrument Bounce to File... Global Settings... Appearance... Rewind on Stop Metronome... Midi Sync... Midi File Import/Export... &MusE Homepage... &Did You Know? &About MusE... Snooper (developer tool)... About &Qt... Set left/right marker for bounce range The project has no project sample rate (added 2011). Please enter a rate. The current system rate (%1Hz) is suggested, and cancelling uses it: The project has audio waves, but no project sample rate (added 2011). Please enter a rate. The most common wave rate found is suggested, the project was probably made with it. Cancelling uses the current system rate (%1Hz): Project sample rate This wave file has a samplerate of %1 Hz, as opposed to current setting %2 Hz. A live, real-time samplerate converter can be used on this file. Or, a copy of the file can be resampled now from %1 Hz to %2 Hz. Do you want to use a converter or resample the file now? Use live converter Resample now Unable to launch browser Error launching default browser Show Docks Toggle display of currently visible dock windows. Display MusE main window in full screen mode. Mastertrack Graphic... Send 'note-off' command to all midi channels. Send initialization messages as found in instrument definition. Take Automation Snapshot Keyboard Shortcuts... Midi Ports / Soft Synths... The current project contains unsaved data. Save current project? No valid parts selected The current project contains unsaved data. Load overwrites current project. Save current project? Recording starts at left mark Recording stops at right mark Panic button: Send 'all sounds off' and 'reset all controls' to all midi channels. Press F1 for help. Metronome on/off. Press F1 for help. Send 'local-off' command to all midi channels. &Manual (wiki)... File buttons Timeline Recording Sync Synk Position MusEGui::NoteInfo Note Info Notinfo Start Starta Len Längd Pitch Tonhöjd Velo On på hastighet Velo Off maximivarvtal delta/absolute mode delta/absolut-läge Delta/Absolute mode Velo on Velo off MusEGui::PartCanvas Cannot copy/move/clone to different Track-Type Kan inte kopiera/flytta/klona till annan Spårtyp C&ut Klipp &ut &Copy &Kopiera s&elect &Välj clones kloner rename byt namn color färg delete ta bort split dela glue lim de-clone häv kloning save part to disk Spara part till disk wave edit wave redigering file info Filinformation MusE: save part MusE: spara part Part name: %1 Files: Part-namn: %1 Filer: Cannot paste: multiple tracks selected Kan inte klistra in: flera spår valda Cannot paste: no track selected Kan inte klistra in: inget spår valt Can only paste to midi/drum track Kan endast klistra in på midi/trummspår Can only paste to wave track Kan endast klistra till wavespår Can only paste to midi or wave track Du kan bara bädda MIDI eller ljud spår Cannot paste: wrong data type Kan inte klistra in: fel datatyp super glue (merge selection) superlim (sammanfoga val) %n part(s) out of %1 could not be pasted. Likely the selected track is the wrong type. %n parter av %1 kunde inte klistras in. Antagligen var det valda spåret av fel typ. %n part(s) could not be pasted. Likely the selected track is the wrong type. %n parter kunde inte klistras in. Antagligen var det valda spåret av fel typ. Remove selected Ta bort valt Automation: Automation: Normalize S&elect Rename Color Delete Ta bort Split Glue Super glue (merge selection) De-clone Save part to disk... Part Wave edit... File info... MusE: Save part Automation LMB: Select/Move/Dblclick to edit | CTRL+LMB: Multi select/Move&Copy | CTRL+ALT+LMB: Dblclick to edit in new window | SHIFT+LMB: Select track | MMB: Delete LMB: Draw to resize | MMB: Delete | CTRL+RMB: Trim length LMB: Delete | CTRL+RMB: Trim length LMB: Cut part in two LMB: Merge with following part LMB: Mute selected part LMB: Edit automation events in audio parts Part canvas: Use Pencil tool to draw parts. Double-click to create a new MIDI/drum part between the range markers (set with MMB + RMB). Press F1 for help. MusEGui::PasteDialog %1 quarter for floating-point arguments like 1.5 %1 kvartar %1 quarters for floating-point arguments like 1.5 %1 kvartar %n quarter(s) %1 kvartar MusEGui::PasteEventsDialog %1 quarter for floating-point arguments like 1.5 %1 kvartar %1 quarters for floating-point arguments like 1.5 %1 kvartar %n quarter(s) %1 kvartar MusEGui::Piano Left click: Play Right click: Select key Piano: Press key to play or enter events in step record mode (SHIFT for chords). RMB: Set cursor for polyphonic control events. CTRL+Mousewheel to zoom view vertically. MusEGui::PianoCanvas Note: Velocity: Start (bar): Start (time): Length (ticks): LMB: Select/Move | CTRL+LMB: Multi select/Move&copy | SHIFT+LMB: Select pitch | MMB: Delete | CTRL+RMB: Trim length LMB: Resize | CTRL+LMB: Multi select | CTRL+SHIFT+LMB: Multi pitch select | MMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length LMB: Delete | RMB: Select exclusive | CTRL+RMB: Trim length Pianoroll canvas: Use Pencil tool to draw and edit MIDI events, Pointer tool to select and edit. Press F1 for help. MusEGui::PianoRoll &Edit &Redigera C&ut Klipp &ut &Copy &Kopiera Copy events in range Kopiera händelser i intervall &Paste K&listra in Paste (with dialog) Klistra in (med dialog) Delete &Events Ta bort &händelser &Select &Välj Select &All Välj &allt &Deselect All A&vmarkera allt Invert &Selection Invertera &markering &Inside Loop &I loop &Outside Loop &Utanför loop &Previous Part &Föregående part &Next Part &Nästa part Fu&nctions F&unktioner Quantize Kvantisera Modify Note Length Modifiera notlängd Modify Velocity Modifiera anslag Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transponera Erase Events Ta bort händelse Move Notes Flytta noter Set Fixed Length Sätt fast längd Delete Overlaps Ta bort överlappande Legato &Plugins &Instickseffekter Window &Config Fönster &konfiguration &Event Color &Händelsefärg &Blue &Blå &Pitch colors &Tonhöjdsfärg &Velocity colors &Anslagsfärg Pianoroll tools Pianorulleverktyg Step Record Stegvis inspelning Midi Input Midi-inmatning Play Events Spela händelser panic panik transport transport ctrl ctrl Add Controller View Lägg till kontrollvy Paste to current part &Display Step record Midi input Play events Event List... Display menu: View-specific display options. Add controller view Play single note Play chords &Scripts Scripts are applied to all or selected events. User scripts can be added in '~/.config/MusE/MusE/scripts/'. See 'MIDI scripting' in MusE wiki. MusEGui::PluginDialog MusE: select plugin MusE: välj instickseffekt Lib Label Etikett Name Namn AI AO CI CO IP id Maker Upphovsman Copyright Ok Ok Cancel Avbryt Mono and Stereo Mono och stereo Show All Visa alla Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallell.<br>Also beware that the 'all' alternative includes plugins that probably not are usable by MusE. Välj vilka typer av instickseffekter som skall vara synliga i listan.<br>Notera att användning av mono effekter på ett stereospår inte är något problem, två instanser kommer<br> att att användas parallellt. Var också medveten om att 'alla' alternativet inkluderar instickseffekter som MusE antagligen inte kan använda. Search in 'Label' and 'Name': Sök i 'Etikett' och 'Namn': Type Typ Audio inputs Ljudingång Audio outputs Ljudutgång Control inputs Kontrollingångar Control outputs Kontrollutgångar In-place capable Påplats-kapabel ID number ID-nummer Select which types of plugins should be visible in the list.<br>Note that using mono plugins on stereo tracks is not a problem, two will be used in parallel.<br>Also beware that the 'all' alternative includes plugins that may not be useful in an effect rack. Välj vilka typer av instickseffekter som skall vara synliga i listan.<br>Notera att användning av mono effekter på ett stereospår inte är något problem, två instanser kommer<br> att att användas parallellt. Var också medveten om att 'alla' alternativet inkluderar instickseffekter kanske inte går använda i ett effektrack. dssi synth dssi effect dssi-effekt ladspa Show plugs: Visa instickseffekter: Plugin categories. Right-click on tabs to manage. Right-click on plugins to add/remove from a category. Associated categories You need to define some categories first. Enter the new group name Wine VST LV2 synth LV2 effect FB 2B 2B Requires fixed block size Requires power-of-2 block size &Create new group &Delete currently selected group Re&name currently selected group New group Ny grupp VST synth VST effect MusE: Select Plugin MusEGui::PluginGui File Buttons Arkivknappar Load Preset Öppna preset Save Preset Spara preset bypass plugin koppla ur instickseffekt MusE: load preset MusE: öppna preset Error reading preset. Might not be right type for this plugin Fel vid preset inläsning. Kanske inte rätt instickseffekt MusE: save preset MusE: spara preset Bypass plugin Plugin settings MusE: Save preset Plugin Settings MusEGui::PluginSettings Global setting: On Off Av MusEGui::PopupMenu <More...> %1 MusEGui::PosToolbar Left marker Left marker position Right marker Right marker position Current position Current position in bars/beats SMPTE position Current position in SMPTE time Show/Hide position in ticks and audio frames Show/Hide additional output fields displaying the current position in ticks and audio frames Current position in ticks Current position in audio frames Current position slider MusEGui::ProjectCreateImpl Select directory Välj katalog MusEGui::RasterLabelCombo Snap Fäst vid MusEGui::RecToolbar Record mode Record mode: Overdub to add new events, Replace to replace overlapping events. Overdub Överspelning Replace Ersätt Cycle record mode Cycle record mode: Normal to replace range when loop is finished, Mix to add new events, Replace to replace range on first MIDI input. Normal Mix MusEGui::RhythmGenerator Instrument Settings: Instrumentinställningar: Instrument Instrument add lägg till delete ta bort steps/count steg/räknare count/bar slag/takt # bars # takt Group Settings: Gruppinställningar: Group Grupp Group 1 Grupp 1 Group 2 Grupp 2 Group 3 Grupp 3 Group 4 Grupp 4 Group 5 Grupp 5 listen lyssna Randomize Slumpa Rhythm Style: Rytmstil: Clear Rensa Open... Öppna... Save Spara Save as... Spara som... Generate Generera Close Stäng MusEGui::RouteDialog Normal Alias 1 Alias 2 Source Källa Destination <none> MusEGui::RoutePopupMenu Channel Kanal Soloing chain Solokedja Audio returns Ljudreturer Warning: No input devices! Varning: Inga inmatningsenheter! Open midi config... Öppna midikonfiguration... <none> Toggle all Ändra alla More... Mer... Audio sends Ljud-sänd Midi port sends Midiport-sänd Channel grouping: Mono Stereo Channels Omni Show aliases: First Second Show names Show first aliases Visa först alias Show second aliases Visa andra alias Jack ports Connect Koppla Unavailable Open advanced router... Output routes: Input routes: Tracks Destinations: Midi sends Sources: ALSA devices JACK devices Synth devices Warning: No output devices! Varning: Inga utgångsenheter! MusEGui::ScoreCanvas Treble Diskant Bass Bas Grand Staff Notrad Remove staff Ta bort rad Ambiguous part Tvetydig del There are two or more possible parts you could add the note to, but none matches the selected part. Please select the destination part by clicking on any note belonging to it and try again, or add a new stave containing only the destination part. Det finns två eller fler möjliga parter du kan lägga noten till men ingen matchar den valda parten. Välj mål genom att klicka på någon not tillhörande rätt part och försök igen, eller lägg till en ny notrad som bara innehåller den nya parten. No part Ingen part There are no parts you could add the note to. Det finns ingen part att lägga noten till. Tools Verktyg MusEGui::ScoreEdit Undo/Redo tools Ångra/gör om-verktyg Step recording tools Stegvisinspelningsverktyg Step Record Stegvisinspelning panic panik transport transport Note settings Notinställningar Note length: Notlängd: last sist Apply to new notes: Använd på nya noter: Apply to selected notes: Använd på valda noter: Velocity: Anslag: Off-Velocity: Av-anslag: Quantisation settings Kvantiseringsinställningar Quantisation: Kvantisering: Pixels per whole: Pixels per hel: &Edit &Redigera C&ut Klipp &ut &Copy &Kopiera Copy events in range Kopiera händelser i intervall &Paste K&listra in Paste (with dialog) Klistra in (med dialog) Delete &Events Ta bort &händelser &Select &Välj Select &All Markera &allt &Deselect All A&vmarkera allt Invert &Selection Invertera &markering &Inside Loop &I loop &Outside Loop &Utanför loop Fu&nctions Fu&nktioner &Quantize &Kvantisera Change note &length Ändra notl&ängd Change note &velocity Ändra not&anslag Crescendo/Decrescendo Crescendo/Decrescendo Transpose Transponera Erase Events Ta bort händelser Move Notes Flytta noter Set Fixed Length Sätt fast längd Delete Overlaps Ta bort överlappande Legato Window &Config Fönster &konfiguration Note head &colors Nothuvud&färger &Black &Svart &Velocity &Anslag &Part Set up &preamble Konfigurera &upptakt Display &key signature Visa &tonart Display &time signature Visa &taktart Set Score &name Ange noternas &namn Enter the new score title Mata in det nya namnet på noterna Error Fel Changing score title failed: the selected title is not unique Byte av not-titel misslyckades: Den valda titeln är inte unik Step record &Display Quantisation Pixels per whole Note length Velocity Apply to selected notes, or new notes if none is selected Off-Velocity Copy Events in Range Change Note &Length Change Note &Velocity Display menu: View-specific display options. Note Head &Colors &Default Set Up &Preamble Display &Key Signature Display &Time Signature Set Score &Name MusEGui::ScrollScale next page nästa sida previous page förra sidan current page number nuvarande sidnummer MusEGui::ShortcutCaptureDialog Ok Ok Cancel Avbryt Shortcut conflicts with %1 Kortkommando krockar med %1 Undefined Odefinierad MusEGui::ShortcutConfig Save printable text file Text files (*.txt);;All files (*) Error Fel Error opening file for saving Shortcuts for selected category: Legend: An error occurred while saving MusEGui::SigScale signature scale signaturskala Signature scale MusEGui::SigToolbar Time signature at current position Signature: Signatur MusEGui::SigToolbarWidget time signature at current position tid-signatur vid nuvarande position Signature: Signatur MusEGui::Strip Remove track? Ta bort spår? Name Namn Enter track name: MusE: bad trackname MusE: dåligt spårnamn Please choose a unique track name Prefer knobs, not sliders Show values in controls Prefer midi volume as decibels Monitor on record-arm automatically Change track name Hide strip Visible audio effects Configuration Actions Operationer MusEGui::SyncToolbar External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::TList <none> visible synlig no clef ingen klav Treble Diskant Bass Bas Grand Noter MusE: bad trackname MusE: dåligt spårnamn please choose a unique track name vänligen välj ett unikt spårnamn Unused Devices Oanvända enheter Update drummap? Updatera trum-map? Do you want to use same port for all instruments in the drummap? Vill du använda samma port för alla instrument i trum-map:en? &Yes &Ja &No &Nej show gui visa gui show native gui Visa eget gränssnitt Treble clef Diskantklav Bass clef Basklav Grand Staff Noter Viewable automation Synlig automation Delete Track Ta bort spår Track Comment Spårkommentar Insert Track Lägg in spår Midi Midi Drum Trumma Do you want to use same port and channel for all instruments in the drummap? Vill du använda samma port och kanal för alla instrument i trummap:en? off av <unknown> <okänd> Midi control Midikontroll Assign Tilldela Clear Rensa Internal Intern Synth Synt Save track's drumlist Spara spårets trumlista Load track's drumlist Reset track's drumlist Reset track's drumlist-ordering Copy track's drumlist to all selected tracks Drum map Karta över slagverk Reset the track's drum map with instrument defaults? Reset the track's drum map ordering? Muse: Load Track's Drum Map MusE: Store Track's Drum Map Presets Preset Clear all controller events? Ta bort alla kontrollerhändelser? &Ok &Ok &Cancel &Avbryt Change color Other Andra clear automation rensa automation SYNTH IS UNAVAILABLE! Please choose a unique track name Show gui Show native gui Clear automation Delete Selected Tracks Ta bort valda spår Track list: Use context menu to create tracks. Click track ID to select, CTRL to add, SHIFT for range, dblclick to select all of same type. Press F1 for help. MusEGui::TempoSig Tempo/Sig Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Time signature at current position MusEGui::TempoToolbar Use mastertrack tempo Mastertrack tempo at current position, or fixed tempo Tap Tap this button to set tempo Set the tempo to the time difference of two successive clicks. Times out after two seconds. MusEGui::TempoToolbarWidget tempo at current position tempo vid nuvarande position MusEGui::Toolbar1 Off Av Solo Cursor Markör Snap Fäst vid MusEGui::TopWin As subwindow Som underfönster Shares tools and menu Delar verktyg och meny Fullscreen Helskärm Piano roll Pianorulle List editor Listredigerare Drum editor Trumeditor Master track editor Master spår redigerare Master track list editor Master spår list redigerare Wave editor Ljud-redigerare Clip list Klipp-lista Marker view Markörvy Score editor Notredigerare Arranger Arrangerare <unknown toplevel type> Undo/Redo tools Ångra/Gör om verktyg Panic Panik Transport Transport Song Position Sångposition Tempo Tempo Signature Signatur Metronome Metronom Tabbed/Floating Display editor in a tab or in a separate window (preset in Global Settings->Editors). Timeline Recording Sync Synk Position MusEGui::TrackComment MusE: Track Comment MusE: Spårkommentar Track Comment: Spårkommentar: MusEGui::Transport Overdub Överspelning Replace Ersätt Rec Mode Inspelningsläge Normal Mix Cycle Rec Cirkulärinspelning punchin Tryck på loop upprepning punchout tryck av Punch In Tryck in Loop Upprepning Punch Out Tryck av Left Mark Vänster markering Right Mark Höger markering rewind to start spola tillbaka till början Click this button to rewind to start position Klicka här för att spola tillbaks till början rewind spola tillbaka Click this button to rewind Klicka här för att spola tillbaka forward framåt Click this button to forward current play position Klicka här för att flytta markören framåt stop stopp Click this button to stop playback Klicka här för att stoppa uppspelning play spela Click this button to start playback Klicka här för att starta uppspelning record inspelning Click this button to enable recording Klicka här för att spela in quantize during record kvantisera under inspelning Click Klick metronom click on/off metronomklick av/på Sync Synk external sync on/off externsynk. av/på Jack transport sync on/off Jack transport synk av/på Master Master use master track använd master-spår Punch in Punch out Rewind to Start Rewind Spola tillbaka Forward Framåt Stop Stopp Play Spela Record Spela in Metronome on/off External sync on/off Jack Transport on/off On: Timebase master Off: Not master Flash: Waiting. Another client is master. Click to force. MusEGui::VisibleTracks Show wave tracks Visa ljudspår Show group tracks Visa gruppspår Show aux tracks Visa aux-spår Show input tracks Visa ingångs-spår Show output tracks Visa utgångs-spår Show midi tracks Visa midi-spår Show synth tracks Visa synth-spår Visible track types Synliga spårtyper MusEGui::WTScale bar scale taktskala MusEGui::WaveCanvas Adjust Wave Offset Wave offset (frames) Part creation failed Left and right position markers must be placed inside the current part. Part created The selected region has been copied to the clipboard and can be pasted in the arranger. MusE - external editor failed MusE - fel med extern redigerare MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE kunde inte starta extern redigerare kontrollera om inställningarna i: Globala inställningar->Ljud:Extern ljudredigerare är satt till en giltig redigerarbinär. MusE - file size changed MusE - filstorleken har förändrats When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Vid editering i extern editor - bör du inte ändra filstorleken eftersom den måste passa i regionen. Saknat data tystas Wave event: Converter settings Wave canvas: Use Pencil tool to edit wave events, Pointer tool to select and edit. Press F1 for help. MusEGui::WaveEdit &Edit &Redigera Func&tions Funk&tioner &Gain &Förstärkning Other Andra &Copy &Kopiera C&ut Klipp &ut &Paste K&listra in Edit in E&xternal Editor Editera i e&xtern redigerare Mute Selection Tysta markering Normalize Selection Normalisera markering Fade In Selection Tona in markering Fade Out Selection Tona ut Markering Reverse Selection Reversera markering Select Välj Select &All Markera &allt &Deselect All A&vmarkera allt Window &Config Fönster &konfigurering Wave edit tools Ljudredigeringsverktyg transport transport WaveEdit tools Ljudredigeringsverktyg Solo Cursor Markör &Create Part from Region &Previous Part &Föregående part &Next Part &Nästa part &Event Color &Händelsefärg &Part colors &Gray Edit in E&xternal Editor... &Display Display menu: View-specific display options. MusEGui::WaveView MusE - external editor failed MusE - fel med extern redigerare MusE was unable to launch the external editor check if the editor setting in: Global Settings->Audio:External Waveditor is set to a valid editor. MusE kunde inte starta extern redigerare kontrollera om inställningarna i: Globala inställningar->Ljud:Extern ljudredigerare är satt till en giltig redigerarbinär. MusE - file size changed MusE - filstorleken har förändrats When editing in external editor - you should not change the filesize since it must fit the selected region. Missing data is muted Vid editering i extern editor - bör du inte ändra filstorleken eftersom den måste passa i regionen. Saknat data tystas OrganGuiBase Velocity Anslag MusE: Organ Drawbars Koppel 16' 4' 2 2/3' 2' 5 1/3' 8' Envelope Hi Envelop Hög Release Avslut Sustain Decay Attack ms cB Envelope Lo Envelop Hög O-1 Oscillator Brass Reed Flute PasteDialogBase MusE: Paste Parts MusE: Klistra in parter Number and raster Nummer och rutnät insert lägg in times antal gånger raster rutnät ticks tick Move, Merge, Clone Flytta, Sammanfoga, Klona Move everything to the right Flytta allt till höger Move only affected parts to the right Flytta endast påverkade parter till höger Put everything into a single track Stoppa allt i ett enda spår Merge with existing parts Sammanfoga med existerande parter Insert as clones (where possible) Infoga som klon (när möjligt) OK Cancel Avbryt 1 quarter 1 kvart Insert Sätt in Raster Rutnät PasteEventsDialogBase MusE: Paste Events MusE: Klistra in händelser Number and raster Nummer och rutnät insert infoga times gånger raster rutnät ticks tick Paste options Inklistringsinställningar Always into existing parts Alltid i existerande parter Never into existing parts Aldrig i existerande parter Into existing parts if part has not to be expanded by more than I existerande parter om part inte behöver expandera mer än Put everything into the (selected) part Info allt i den valda parten OK Cancel Avbryt 1 quarter 1 kvart 1 quarter 1 kvart &Always into existing parts &Never into existing parts &Into existing parts if part has not to be expanded by more than Paste midi controller target options Keep e&xisting Erase existin&g Erase WYSIWYG Erase inclusive Insert Sätt in Raster Rutnät PluginDialogBase Dialog Ports: Mono Mono + Stereo Stereo All Alla Plugin type: Filter: &OK &Ok &Cancel &Avbryt PluginSettings Dialog Options Fixed speed Transport affects audio latency Override reported audio latency Reported audio latency override value Revert HiDPI scaling of native GUI Set in Global settings->GUI tweaks Global setting Additional information Follow global setting On Off Av ProjectCreate Create Project Skapa projekt Projects folder: Projektkatalog: ... ... Project Name: Projektnamn: Project is a Template Projekt är en mall Project song file type: Sång-filtyp för projekt: Project Path to song file: Projektsökväg till sångfil: Create project folder (recommended for audio projects) Skapa projektkatalog (rekommenderat för ljudprojekt) Song information: Sånginformation: Write window state Skriv fönsterinställningar &Create project folder (recommended for audio projects) QObject Error Fel Please first select the range for crescendo with the loop markers. Välj område för crescendo med loopmarkörerna. Controller ! Other ! Switch on General Midi Level 1 mode Switch on General Midi Level 2 mode Switch off General Midi Level 1 or 2 Switch on Roland GS mode Switch on Yamaha XG mode Presets Preset Tracks: Midi ports: Midi devices: Jack: Jack midi: Current device Others Andra Files Synthesizers Open Öppna Closed <Properties> <Base Property> <Property> Input routing Warning: No input routes! Click to connect... Output routing Warning: No output routes! Click to connect... TRACK TAKE MusE import error. MusE failed to import the file. Possibly this wasn't a sound file? If it was check the permissions, MusE sometimes requires write access to the file. Instruments FX slot QWidget Cannot convert sysex string Kan inte konvertera sysex-sträng Hex String too long (2048 bytes limit) Hex-sträng för lång (2048 bytes maxgräns) generic midi generell midi new ny create peakfile for skapa "peak"-fil MusE: get file name MusE: hämta filnamn The directory %1 does not exist. Create it? Katalogen %1 existerar inte. Skapa den? MusE: create directory MusE: skapa katalog creating dir failed misslyckades med att skapa katalog File %1 exists. Overwrite? Filen %1 existerar. Skrivöver? Open File %1 failed: %2 Öppning av fil %1 misslyckades: %2 MusE: write MusE: skriv MusE: Open File MusE: Öppna fil None Ingen No selection. Ignoring Inget valt, ignorerar Instrument-defined Instrumentdefinierad Edit instrument ... Redigera instrument... Add Others Andra Common Controls Vanliga kontroller Velocity No wave events selected. Generic midi Creating dir failed Create peakfile for New Ny QuantBase MusE: Quantize MusE: kvantisering Range Område All Events Alla händelser Selected Events Valda händelser Looped Events Loopade händelser Selected Looped Valda loopade Values Värden Strength: Styrka: % Threshold (ticks): Tröskelvärde (tick): Quantize Len Kvantiseringslängd Raster Rutnät Whole Hel Half Halv 4th Fjärdedel 4th Triplet Fjärdedel triol 8th Åttondel 8th Triplet Åttondel triol 16th Sextondel 16th Triplet Sextondel triol 32th 32:del 32th Triplet 32:del triol Swing: If the proposed change in tick or length is smaller than threshold, nothing is done. If swing=0, this is normal If swing is 33, you get a 2:1-rhythm. If swing is -33, you get a 1:2-rhythm. Om den föreslagna ändringen i tick eller längd är mindre än tröskelvärdet händer inget. Om swing=0 är detta normalt Om swing är 33 blir rytmen 2:1 Om swing är -33 blir 1:2. OK Ok Cancel Avbryt Parts Parter Selected parts or c&urrent part All parts &in editor RemoveBase MusE: Erase Notes MusE: Ta bort noter Range Område All Events Alla händelser Selected Events Valda händelser Looped Events Loopade händelser Selected Looped Valda loopade Thresholds Tröskelvärden ticks tick Velocity Anslag Length Längd <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If nothing is checked, everything is removed.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If velocity is checked, only notes with velo &lt; threshold are removed.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">If both are checked, notes with velo &lt; threshold OR with length &lt; threshold are removed.</p></body></html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Om inget är valt, allt blir borttaget.</p> <p style=" margin-top:0px; margin-bottom:7px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Om anslag är valt, bara noter med anslag &lt; tröskelvärdet tas bort.</p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Om båda är valda, noter med anslag &lt; tröskelvärde ELLER med längd &lt; tröskelvärdet tas bort.</p></body></html> OK Ok Cancel Avbryt All Eve&nts Selec&ted Events &Looped Events Selected L&ooped Parts Parter Selected parts or c&urrent part All parts &in editor RhythmBase MusE: Random Rhythm Generator MusE: Slumprytmgenerator Instrument Properties Instrumentinställningar counts/bar slag/takt # bars # takter randomize slumpa Group 1 Grupp 1 Group 2 Grupp 2 Group 3 Grupp 3 Group 4 Grupp 4 Group 5 Grupp 5 listen lyssna Instrument Instrument Group Grupp &New &Ny create new entry skapa nytt inlägg pressing the New button you create a new entry in the MusE list of defined controllers skapa nytt inlägg genom att trycka på Ny knappen i MusEs lista över definierade controllers &Delete &Ta bort delete selected entry ta bort valt inlägg Up Upp Down Ner list of defined controllers lista med definierade controllers This is the MusE list of defined controllers. Detta är MusEs lista med definierade controllers. &File &Arkiv &Edit &Redigera &Help &Hjälp Tools Verktyg New Ny Open Öppna &Open... &Öppna... Ctrl+O Ctrl+Ö Save Spara &Save &Spara Save As Spara som Save &As... S&para som... Print Skriv ut &Print... Skriv &ut... Exit Stäng E&xit S&täng Undo Ångra &Undo &Ångra Redo Gör om &Redo Gör &om Cut Klipp ut &Cut Klipp &ut Copy Kopiera C&opy &Öppna Paste Klistra in &Paste K&listra in Find Sök &Find... &Sök... Contents Innehåll &Contents... &Innehåll... About Om &About... &Om... steps/count steg/inräkning test test steps/count steg/inräkning <b>Notice!</b><br> Random Rhythm Generator is not enabled yet! <b>Notera!</b><br> Slumprytmgenerator är inte tillgänglig än! RhythmGenerator RouteDialogBase MusE: Routing MusE: Kopplingar Add Route Lägg till rutt Source: Källa: Connect Koppla connect source to destination koppla källa till destination Current Routes Nuvarande kopplingar Source Källa Remove Ta bort remove selected route Ta bort vald koppling List of available sources. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Connections View window. Shows all current connections. Thick lines are Omni Routes. Thin lines are Channel Routes. List of available destinations. Connect a source to a destination. For items having a channel bar, connections can be Omni Mode (the textual item) or Channel Mode (the Channel bar channels). Itemized list of current connections. Show only selected sources Show only destination routes for selected source Show only selected destinations Show only source routes for selected destination Show all Midi Ports Auto adjust column size Automatically adjusts the source and destination tree widths when the splitters are adjusted. This also turns on text word wrap, which may cause slower response with larger lists. Preferred route name or alias Connect source to destination Remove selected route RubberbandSettingsBase Rubberband Settings Rubberband support is not enabled Use default settings Presets: Presets: Default Percussion Maximum Stretch: Elastic Precise Window: Standard Short Long Smoothing: Off Av On Channels: Apart Together Transients: Crisp Mixed Smooth Detector: Compound Percussive Soft Phase: Laminar Independent Formant: Shifted Preserved Pitch: Tonhöjd: High speed High quality High consistency OK Ok Cancel Avbryt SRCResamplerSettingsBase SRC Resampler Settings Use default settings Converter Type: SINC Best quality SINC Medium quality SINC Fastest Zero Order Hold Linear OK Ok Cancel Avbryt SS_PluginChooserBase SimpleDrums - Ladspa Plugin Chooser SimpleDrums - Ladspa insticksprogramväljare Name Namn Label Etikett Inports Inportar Outports Utportar Creator Upphovsman &Cancel &Avbryt Alt+C Alt+A &OK &Ok SS_PluginFront Clear and unload effect Rensa och ladda ur effekt Load effect Ladda effekt Toggle display of effect parameters Slå av/på visning av effektparametrar Turn effect on/off Slå effekt av/på SetlenBase MusE: Set Note Length MusE: Sätt notlängd Range Område All Events Alla händelser Selected Events Valda händelser Looped Events Loopade händelser Selected Looped Valda loopade Value Värde New length Ny längd ticks tick OK Cancel Avbryt Parts Parter Selected parts or c&urrent part All parts &in editor ShortcutCaptureDialogBase Enter shortcut sequence Ange kortkommandosekvens Press keys to enter shortcut sequence! Tryck valda tangenter för att ange kortkommandosekvens! Old shortcut: Gammalt kortkommando: Undefined Odefinierad New shortcut: Nytt kortkommando: OK Cancel Avbryt ShortcutConfigBase Configure Keyboard Shortcuts Konfigurera tangentbordskortkommando Shortcut Category Kortkommandokategori Description Beskrivning Shortcut Kortkommando &Clear &Rensa Alt+C Alt+R &Define &Definiera Alt+D &Apply &Verkställ Alt+A Alt+L &Printable file... Alt+P Alt+P &Ok &Ok Alt+O Filter: &Reset all SimpleDrumsGuiBase DrumSynth 0.1 Trumsynth 0,1 Audio file assignments. Within parantesis: note number and traditional midi drum name SimplePluginChooserBase SimpleDrums - Ladspa Plugin Chooser SimpleDrums - Ladspa insticksprogramväljare Name Namn Label Etikett Inports Inportar Outports Utportar Creator Upphovsman &Cancel &Avbryt Alt+C &OK &Ok Alt+O SimpleSynthGui &Load setup &Ladda setup &Save setup &Spara setup Load sample dialog Mix Chn Channel routing SnooperDialogBase Snooper Mode Auto-hide Separate parented top levels Update tree Mouse clicks Key presses Flash timer s Reset Filters MusEGui QWidget Properties Egenskaper Search: Class name: Object name: About Om This tool helps developers and designers discover objects. Click on anything anywhere or press a key to highlight an item. It will NOT auto-update when new objects are created. Click 'Update tree'. When open, may slow program slightly. This dialog is NOT shown. Class name::Object name Property Type Typ Value Värde Event type SongInfo Song Information Sånginformation Show on song load Visa vid inladdning av sång &Cancel &Avbryt Alt+C Alt+R &Ok &Ok Alt+O SynthConfigBase Soft Synthesizer Mjukvarusynt File Arkiv Instances Instanser Name Namn list of available software synthesizers lista med tillgängliga mjukvarusyntar Add Instance Lägg till instans Remove Instance Ta bort instans Midi Port Midiport Midi Port and Soft Synth Configuration Midiport och mjukvarusynt-konfiguration Type Typ Midi connections Midikopplingar Inst Version Description Beskrivning &Apply &OK &Ok Configure midi devices, midi ports, and synthesizers Available soft synthesizers Useable devices Add: Synth Synt ALSA JACK Rename Remove Ta bort Device port assignments List of available software synthesizers TimeCanvas Add Controller View Lägg till kontrollvy TransposeBase MusE: Transpose MusE: Transponera Range Område All Events Alla händelser Selected Events Valda händelser Looped Events Loopade händelser Selected Looped Valda loopade Value Värde Halftone-steps Halvnots-steg OK Cancel Avbryt Parts Parter Selected parts or c&urrent part All parts &in editor UnusedWaveFiles Dialog List of unused audio files in current project directory: Oanvända ljudfiler i nuvarande projektkatalog: Current project Nuvarande projekt All .med files in current directory Alla .med filter i nuvarande katalog Move files to 'unused' subdir Flytta filer till 'unused' underkatalog Cancel Avbryt VAMGui MusE: Save VAM Presets MusE: Spara VAM presets MusE: Load VAM Presets MusE: Ladda VAM Presets VAMGuiBase Virtual Analogue for MusE Virtual Analogue för MusE Waveform Vågform Sine Sinus Pulse Puls Saw Sågtand Triangle Triangel Filter Filter Invert Invertera Presets Preset load preset list öppna presetlista save preset list spara presetlista save preset list to a new file spara presetlista till ny fil delete preset ta bort preset Pitch Tonhöjd On LFO Freq EnvMod Attack Decay Sustain Release Cutoff Resonance KeyTrack Set DCO 1 Detune Snedstäm PWM FM PW DCO 2 VAM 1.0beta3 Virtual Analog for MusE Released under GPL. Copyright(C) 2002 Jotsif Lindman Hörnlund ( jotsif@linux.nu ) Copyright(C) 2005 Robert Jonsson (rj@spamatica.se) VelocityBase MusE: Modify Velocity MusE: Modifiera anslag Range Område All Events Alla händelser Selected Events Valda händelser Looped Events Loopade händelser Selected Looped Valda loopade Values Värden Rate: Hastighet: % Offset: Kompensation: veloNew = (veloOld * rate) + offset OK Cancel Avbryt Parts Parter Selected parts or c&urrent part All parts &in editor ZitaResamplerSettingsBase Zita Resampler Settings Zita Resampler support is not enabled Use default settings Options: OK Ok Cancel Avbryt file_patterns Midi/Kar (*.mid *.MID *.kar *.KAR *.mid.gz *.mid.bz2) Midi (*.mid *.MID *.mid.gz *.mid.bz2) Karaoke (*.kar *.KAR *.kar.gz *.kar.bz2) All Files (*) Alla filer (*) Midi (*.mid) Karaoke (*.kar) all known files (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) Alla kända filer (*.med *.med.gz *.med.bz2 *.mid *.midi *.kar) med Files (*.med *.med.gz *.med.bz2) med-filer (*.med *.med.gz *.med.bz2) Uncompressed med Files (*.med) Okomprimerade med-filer (*.med) gzip compressed med Files (*.med.gz) gzip komprimerade med-filer (*.med.gz) bzip2 compressed med Files (*.med.bz2) bzip2 komprimerade med-filer (*.med.bz2) mid Files (*.mid *.midi *.kar *.MID *.MIDI *.KAR) mid-filer (*.mid *.midi *.kar *.MID *.MIDI *.KAR) (*.jpg *.gif *.png) (*.jpg) (*.gif) (*.png) part Files (*.mpt *.mpt.gz *.mpt.bz2) part-filer (*,mpt *.mpt.gz *.mpt.bz2) part Files (*.mpt) part-filer (*.mpt) gzip compressed part Files (*.mpt.gz) gzip-komprimerade part-filer (*.mpt.gz) bzip2 compressed part Files (*.mpt.bz2) bzip2-komprimerade part-filer (*.mpt.bz2) Presets (*.pre *.pre.gz *.pre.bz2) Presets (*.pre) gzip compressed presets (*.pre.gz) gzip-komprimerade presets (*.pre.gz) bzip2 compressed presets (*.pre.bz2) bzip2 komprimerade presets (*.pre.bz2) Presets (*.map *.map.gz *.map.bz2) Presets (*.map) gzip compressed presets (*.map.gz) gzip-komprimerade presets (*.map.gz) bzip2 compressed presets (*.map.bz2) bzip2-komprimerade presets (*.map.bz2) Binary (*.bin) Wave/Binary (*.wav *.ogg *.flac *.bin) Wave (*.wav *.ogg *.flac) Color configuration files (*.cfc) Qt style sheets (*.qss) freeverb Damping Dämpning FreeVerb Room Size Rumsstorlek Wet Level Effektnivå Tap-Reverberator Decay [ms] Utklingning [ms] dB dB Dry Level [dB] Mängd torrt ljud [dB] Wet Level [dB] Mängden effektljud [db] Preset: AfterBurn AfterBurn (Long) Ambience Ambience (Thick) Ambience (Thick) - HD Cathedral Cathedral - HD Drum Chamber Garage Garage (Bright) Gymnasium Gymnasium (Bright) Gymnasium (Bright) - HD Hall (Small) Hall (Medium) Hall (Large) Hall (Large) - HD Plate (Small) Plate (Medium) Plate (Large) Plate (Large) - HD Pulse Chamber Pulse Chamber (Reverse) Resonator (96 ms) Resonator (152 ms) Resonator (208 ms) Room (Small) Room (Medium) Room (Large) Room (Large) - HD Slap Chamber Slap Chamber - HD Slap Chamber (Bright) Slap Chamber (Bright) HD Smooth Hall (Small) Smooth Hall (Medium) Smooth Hall (Large) Smooth Hall (Large) - HD Vocal Plate Vocal Plate - HD Warble Chamber Warehoouse Warehouse - HD Comb Filters Allpass Filters Bandpass Filters Enhanced Stereo main File to open Alsa midi only (using dummy audio driver) Use RtAudio driver Use JAckAudio driver to connect to Jack audio server Do not try to auto-start the Jack audio server Do not auto-populate midi ports with midi devices found, at startup Force inclusion of ALSA midi even if using Jack Set audio driver real time priority to n (Dummy only, default 40. Else fixed by Jack.) Force midi real time priority to n (default: audio driver prio -1) Force plugin cache re-creation. (Automatic if any plugin path directories changed.) Do not re-create plugin cache. Avoids repeated re-creations in some circumstances. Use with care. Don't load LADSPA plugins Don't load MESS plugins Don't load VST plugins Don't load DSSI plugins Don't load LV2 plugins Don't use LASH Force locale to the given language/country code (xx = Ubuntu/unity workaround: don't allow sharing menus and mdi-subwins. Debug mode: no threads, no RT Debug mode: enable some debug messages specify twice for lots of debug messages this may slow down MusE massively! Debug mode: trace midi Input Debug mode: trace midi Output Debug mode: trace sync Enable Python control support Pyro nameserver host name Pyro nameserver host port Pyro daemon host name Pyro daemon host port Pyro communication timeout in seconds Don't load LinuxVST plugins midiWarnInitPendingBase Instrument initialization MusE should now send some Instrument Initialization Sequences. The sequences (usually System Exclusive messages) are defined by the selected instruments in the Settings -> Midi Ports dialog, such as the GM (default), GS, or XG instruments. Typically you should answer yes here. You can always do it manually from the Midi menu. Continue? Don't ask me again shortcuts Transport: Start playback from current location Transport: Starta uppspelning från nuvarande position Transport: Toggle metronome Transport: Slå av/på metronome Transport: Stop Playback Transport: Stoppa uppspelning Transport: Goto Start Transport: Gå till början Transport: Play, Stop, Rewind Transport: Spela, Stoppa, Spola tillbaka Transport: Goto left marker Transport: Gå till vänster markör Transport: Goto right marker Transport: Gå till höger markör Transport: Toggle Loop section Transport: Slå av/på loop Transport: Toggle Record Transport: Slå av/på inspelning Transport: Clear all rec enabled tracks Transport: Slå av alla inspelningsaktiverade spår Toggle fullscreen Av/på fullskärm Edit: Copy Redigera: Kopiera Edit: Copy in range Redigera: Kopiera område Edit: Undo Redigera: Ångra Edit: Redo Redigera: Gör om Edit: Cut Redigera: Klipp ut Edit: Paste Redigera: Klistra in Edit: Paste (with dialog) Redigera: Klistra in (med dialog) Edit: Delete Redigera:Ta bort File: New project Arkiv: Nytt projekt File: Open from disk Arkiv: Öppna från disk File: Save project Arkiv: Spara projekt File: Open recent file Arkiv: Öppna nyligen använd fil File: Save as Arkiv: Spara som File: Load template Arkiv: Ladda mall File: Import midi file Arkiv: Importera midi-fil File: Export midi file Arkiv: Exportera midi-fil File: Import midi part Arkiv: Importera midi-part File: Import audio file Arkiv: Importera ljud-fil File: Quit MusE Arkiv: Stäng av MusE Edit: Select parts on track Redigera: Välj parter på spår Open pianoroll Öppna pianorulle Open drumeditor Öppna trum-redigerare Open listeditor Öppna list-redigerare Open waveeditor Öppna ljud-redigerare Open graphical mastertrack editor Öppna grafisk master-spår redigerare Open list mastertrack editor Öppna lista-masterspår redigerare Open midi transformer Öppna midi-transformator Add midi track Lägg till midispår Add drum track Lägg till trumspår Add wave track Lägg till ljudspår Add audio output Lägg till ljudutgång Add audio group Lägg till ljudgrupp Add audio input Lägg till ljudingång Add audio aux Lägg till ljud-aux Structure: Global cut Struktur: Globalt klipp ut Structure: Global insert Struktur: Global lägg in Structure: Global split Struktur: Global delning Structure: Cut events Struktur, Klipp händelser View: Open mixer #1 window Vy: Öppna mixer #1 fönster View: Open mixer #2 window Vy: Öppna mixer #2 fönster View: Toggle transport window Vy Av/på transport-fönster View: Toggle bigtime window Vy: Av/på bigtime fönster View: Open marker window Vy: Öppna markör-fönster Settings: Follow song by page Inställningar: Följ sång sida för sida Settings: Follow song off Inställningar: Följ inte sång Settings: Follow song continuous Inställningar: Följ sång kontinuerligt Settings: Global configuration Inställningar: Global konfiguration Settings: Configure shortcuts Inställningar: Konfigurera kortkommandon Settings: Configure metronome Inställningar: Konfigurera metronom Settings: Midi sync configuration Inställningar: Midi synk-konfiguration Settings: Midi file import/export configuration Inställningar: Midi-fil import/export konfiguration Settings: Appearance settings Inställningar: Utseende inställningar Settings: Midi ports / Soft Synth Inställningar: Midi-port / mjukvarusynt Settings: Audio subsystem configuration Inställningar: Ljud-system konfiguration Midi: Edit midi instruments Midi: Redigera midi-instrument Midi: Open midi input transform Midi: Öppna midi ingångstransformator Midi: Open midi input filter Midi: Öppna midi ingångsfilter Midi: Midi input transpose Midi: Midi ingångs transponerare Midi: Midi remote control Midi: Midi fjärrkontroll Midi: Random rhythm generator Midi: Slumprytm-generator Midi: Reset midi Midi- Nollställ midi Midi: Init midi Midi: Initiera midi Midi: Midi local off Midi: Midi lokal av Audio: Bounce audio to track Ljud: Studsa ljud till spår Audio: Bounce audio to file Ljud: Studsa ljud till fil Audio: Restart audio Ljud: Starta om ljud Automation: Mixer automation Automatisering: Mixer automatisering Automation: Take mixer snapshot Automatisering: Ta en ögonblicksbild av mixer Automation: Clear mixer automation Automatisering: Rensa mixer automatisering Help: Open Manual Hjälp: Öppna manual Help: Toggle whatsthis mode Hjälp: Slå av/på "vad är det här"-funktion Edit: Edit selected part Redigera: Redigera vald part Edit: Select nearest part on track above Redigera: Välj närmaste part på spåret ovanför Edit: Add nearest part on track above Redigera: Lägg till närmaste part på spår ovan Edit: Select nearest part on track below Redigera: Välj närmaste part på spår under Edit: Add nearest part on track below Redigera: Lägg till närmaste part på spår under Edit: Insert empty measure Redigera: Lägg in tom takt Edit: Paste as clones Redigera: Klistra in som kloner Edit: Paste as clones (with dialog) Redigera: Klistra in som kloner (med dialog) Select track above Välj spår ovan Select track below Välj spår under Midi: Transpose Midi: Transponera Edit: Select all Redigera: Välj allt Edit: Select none Redigera: Välj inget Edit: Invert Selection Redigera: Invertera val Edit: Select events/parts inside locators Redigera: Välj händelser/parter innanför markörer Edit: Select events/parts outside locators Redigera: Välj händelser/parter utanför markörer Edit: Select previous part Redigera: Välj föregående part Edit: Select next part Redigera: Välj nästa part Edit: Select nearest part/event to the left or move cursor Redigera: Välj närmaste part/händelse till vänster eller flytta markör Edit: Add nearest part/event to the left to selection Redigera: Välj närmaste part/händelse till vänster om valda Edit: Select nearest part/event to the right or move cursor Redigera: Välj närmaste part/händelse till höger eller flytta markör Edit: Add nearest part/event to the right to selection Redigera: Lägg till närmaste part/händelse till höger om valda Edit: Set locators to selection Redigera: Sätt markörer till valda Edit: Increase pitch Redigera: Öka tonhöjd Edit: Decrease pitch Redigera Sänk tonhöjd Edit: Increase event position Redigera: Öka händelse-position Edit: Decrease event position Redigera: Minska händelse-position View: Zoom in Vy: Zooma in View: Zoom out Vy: Zooma ut View: Goto Current Position Vy: Gå till nuvarande position View: Scroll left Vy: Rulla vänster Edit: Set Fixed Length on Midi Events Vy: Sätt fast längd på midi-händelser Quantize Kvantisera Modify Note Length Modifiera notlängd Modify Velocity Modifiera anslag Edit: Crescendo Redigera: Crescendo Edit: Thin Out Redigera: Tunna ut Edit: Erase Event Redigera: Ta bort händelse Edit: Delete Overlaps Redigera: Ta bort överlappande Edit: Note Shift Redigera. Ändra not Edit: Move Clock Redigera: Flytta klocka Edit: Copy Measure Redigera: Kopiera takt Edit: Erase Measure Redigera: Ta bort takt Edit: Delete Measure Redigera: Ta bort takt Edit: Create Measure Redigera: Skapa takt Edit: Change Event Color Redigera: Ändra händelse-färg Tool: Pointer Verktyg: Pekare Tool: Pencil Verktyg: Penna Tool: Eraser Verktyg: Sudd Tool: Line Draw Verktyg: Rita linje Tool: Cursor Verktyg: Pekare Add note velocity 1 Lägg till not med anslag 1 Add note velocity 2 Läg till not med anslag 2 Add note velocity 3 Lägg till not med anslag 3 Add note velocity 4 Lägg till not med anslag 4 Cursor step size: larger Pekare steglängd: större Cursor step size: smaller Pekare steglängd: mindre Instrument/Cursor up Instrument/pekare up Instrument/Cursor down Instrument/Pekare nere Tool: Scissor Verktyg: Sax Tool: Glue Verktyg: Lim Tool: Mute Verktyg: Tysta Transport: Increase current position Transport: Öka nuvarande position Transport: Decrease current position Transport: Minska nuvarande position Transport: Increase current position, no snap Transport: Minska nuvarande position, utan att fästa Transport: Decrease current position, no snap Transport: Minska nuvarande position, utan att fästa Quantize: Set quantize to 1/1 note Kvantisera: Sätt kvantisering till 1/1 not Quantize: Set quantize to 1/2 note Kvantisera: Sätt kvantisering till 1/2 not Quantize: Set quantize to 1/4 note Kvantisera: Sätt kvantisering till 1/4 not Quantize: Set quantize to 1/8 note Kvantisera: Sätt kvantisering till 1/8 not Quantize: Set quantize to 1/16 note Kvantisera: Sätt kvantisering till 1/16 not Quantize: Set quantize to 1/32 note Kvantisera: Sätt kvantisering till 1/32 not Quantize: Set quantize to 1/64 note Kvantisera: Sätt kvantisering till 1/64 not Quantize: Toggle triol quantization Kvantisering: Slå av på triol-kvantisering Quantize: Toggle punctuation quantization Kvantisering: Slå av/på interpunktion-kvantisering Quantize: Toggle punctuation quantization (2) Kvantisering: Slå av/på interpunktion-kvantisering (2) Edit: Insert at location Redigera: Lägg till vid position Edit: Increase length Redigera: Öka längd Edit: Decrease length Redigera: Minska längd Insert Note Mata in not Insert SysEx Mata in SysEx Insert Ctrl Mata in Ctrl Insert Meta Mata in Meta Insert Channel Aftertouch Mata in Aftertouch för kanal Insert Key Aftertouch Mata in tangent Aftertouch Insert Tempo Sätt in tempo Insert Signature Sätt in signatur Change Event Position Ändra händelse-position Edit Event Value Redigera händelsevärde Insert Key Lägg till tonart Goto Next Marker Gå till nästa markör Goto Prev Marker Gå till föregående markör Edit: Paste to selected track Edit: Paste clone Edit: Paste clone to selected track Transport: Step record Transport: Midi input Transport: Play events Edit: Increase velocity Edit: Decrease velocity Move: Move to selected note Tool: Pan Tool: Zoom Tool: Range Edit: Duplicate track Transport: Restart recording Edit selected track name Normalize Transport: Goto End Transport: Restart recording (multi take) Transport: Arm current track Toggle rewind on stop Edit: Paste to current part File: New project from template Edit: Rename part Mute all currently selected tracks Solo all currently selected tracks Tool: Stretch Tool: Sample rate Toggle tabbed/floating editor window Toggle docks display Show Arranger window Open track Synth GUI Open pianoroll in new window Open drumeditor in new window Open listeditor in new window Open waveeditor in new window Insert midi track Insert drum track Insert wave track Insert audio output Insert audio group Insert audio input Insert audio aux View: Toggle mixer strip display Settings: Midi ports / Soft synths Quantize: Set quantize to off Select strip to the left Select strip to the right Mixer strip volume down Mixer strip volume up Mixer strip pan left Mixer strip pan right Mixer strip volume down page Mixer strip volume up page Mixer strip pan left page Mixer strip pan right page warnBadTimingBase Bad timing Dålig timing Message here Don't warn me again muse-4.0.0/src/share/metronome/000077500000000000000000000000001404105311000163475ustar00rootroot00000000000000muse-4.0.0/src/share/metronome/CMakeLists.txt000066400000000000000000000021051404105311000211050ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2014 by MusE dev team # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB metronome_files *.wav *.mdf) install( FILES ${metronome_files} DESTINATION ${MusE_SHARE_DIR}/metronome ) muse-4.0.0/src/share/metronome/factory_presets.mdf000066400000000000000000000017151404105311000222570ustar00rootroot00000000000000 3, 0, 3, 0 0, 3, 0, 3 0, 0, 3, 0 3, 0, 0, 0, 0 3, 0, 3, 0, 0 0, 0, 3, 0, 0 0, 0, 0, 0, 3 3, 0, 3, 0, 3, 0 3, 0, 0, 3, 0, 0 0, 0, 3, 0, 0, 3 0, 0, 3, 0, 0, 0 3, 0, 0, 0, 0, 0, 0 3, 0, 3, 0, 3, 0, 0 3, 0, 0, 0, 3, 0, 0 3, 0, 0, 0, 3, 0, 0, 0 0, 0, 3, 0, 0, 0, 3, 0 3, 0, 3, 0, 3, 0, 3, 0 0, 3, 0, 3, 0, 3, 0, 3 3, 0, 0, 3, 0, 0, 3, 0, 0 0, 0, 3, 0, 0, 3, 0, 0, 3 3, 0, 0, 0, 0, 0, 0, 0, 0 3, 0, 0, 0, 0, 0, 3, 0, 0 3, 0, 3, 0, 3, 0, 3, 0, 0 0, 3, 0, 3, 0, 3, 0, 3, 0 muse-4.0.0/src/share/metronome/klick1.wav000066400000000000000000000750521404105311000202550ustar00rootroot00000000000000RIFF"zWAVEfmt DXdatay$8\Ģ\#W;ݐ"uDDqDA2)p_"&(:&O -L@l} 0ߢ '7:&x YY/[Qncn?-J+KruޗD'/.< # Rkֽ 7 XG MC >(c i:hf#܁[=6. Xh [ Ci Www1S)C  UrBU"!\_4klT fW 4 5 `~~r)w:q"7` <? =&\pKmu} -  # qYQ>yA54 K 2 P v 4 RITxRI )ES +JK $ yK_%z1F4Ww9kd:W--ERA=[0[Ss!`&a|/ig4eSVs26A$hU~D`u8rFo`t@"BDvTH4//IKq1W+92t|}V~MOgA[0itY^Ze3G}5. I739@jW$b/Zl?HE2:;tF9  IdlY<#0P, N )rbe4/f!t'o (@fa&r}Yj*lZaY|(S%KsPx' ING1um=~2hUg:!P#Db.<1"8YgW&F7WlYF' 2o$x '?= , )f"(* t1yM;Y9!-17'6&!ju/FT5Gob0]J/hBPD*gHg[i AkNZP&)_7+E2g+^\y56ZTbkhmMP|&P7'T*)2K {h$6mZHb" w8`yLK. axE|zz Edy!0kT?eyve'+f*n6d.,8Y+?3OKH;5OznM S<I:\2;6MZ2nnwg \ZM%5qu N\+'4qE[WQQ[83`2K= [JyZ2(:Ty<y\V9}6.<|a`r8 ZK!,hx +@[Xu6Z<wU,wx#P x Ee0jD_l7l$YC C ?!t ~ n3Cc{M\}J4w i(8+7^t:;7`t(&}vvQ.a)"b",vX]B&~ ;YO[Fb1l]4/!#Vn)qDe,4_*(H%}N.7AGh]8 x * *e>6#1?t9 9M)QcFG=2d/LO9 *w0utIgDgbb&FHr+l>W_U]WqzM k1J_(j"}~9sDb3vf.cU}$VFNuf7j Ggk Fe=oYn[{20kACQ]UTR`pM4,Yb9PhtCz;>tr5*+3dwa^"mZtSUJ>5 TP$Qwm3(0a =?T&@N$H.U;7 52s"~[7hQx:Z/_!t'UX Pb\})Dn~&M_(wfgU#=0dagQ.YiRQ RvV'uSdTf=ZtK<khuGZu-}uS*gZ[U } qs)v =;9FO Q(H!fUYhvX 6<Ru =YN0q! Fy< rhG Z |~>=M_5H ).4khe'*LiuHy6|Q;*oow 9{NB$WpmmbCWB#890XG^|zX5R0lkOjjU 2}}O9?+L/4:bXj3^7?Bm} ]<]nRJJ+!mk F/^4]4B7vw@xHI\zTR'e.rw"`:2kB]z?+TM!C,B$DQ  !bl6>R&L"45_no.vG>J2$ekGs'0pDB BWsVg^:v | J>Q0U 8?#5 J@~RzIRsigqH69}{XLgW-!!BwC AUF*X$K;hd09tLs>@3%#2T<I|3 AG%uodwdi!a(6gV=9sBg95f:"G t mv( V+goz : \}?LhJ$.' g#m/-WhVI<eGo_ugn`%HJ\Y/[mIU_Q#YZFX:.+{fuFp+TU 28 Z0Y!QE "s]]eN'C9 #uPWZ[KPslB JuRcXOdRr*ziNn_ ~3 JAMI"=~Z ;mwmludsqT0kE})j V_>|JC<+4XspT["=CQC '`>Y2C0>  p0:EbDD &-mlb`C>+-0-!3fz|^VYi3TC*0wZ>f`iz48-'-/_FYxq:%E\<{CBrD(o4\2(A\i[KdO!/NA$WnzLPyRm-,QmBIU:yu Sqp{_~( 8 Vbt#7G5#0-*3 x/x-Dt~ U!+z3UH/,&f mV*2 v4z.)i #;Xy[7 1Sqh),>jQ76-ZqJzq-F\M{"Qly'f_(&5Zh&*=E}gs5DS2D0EH5M$ >YO<)y0<v>IA\`jlvVltjX ? .mR{cg$9Rz4j-(Mr=fXJZQIV-^B 0;_KGrIh19)r~hFs6x/ ^ ?[d0l@^jJ2!ibb7 *<>D#_f0&122:fNKx^3l^OMU">y;A, LG/*U1 @K@D4QQ.S\hA%<|]X# =Kx= (-@>jOJD^UpRj/:b<i!~E.>2f^f}RPJb,,jVD 0u_^L6nX|E:G9"1TE}E[%8e1[]b,I2:S-&HzQ!4MY0Mv11) 1! -zZ{XQYm{%utYp S}xo8< r 5K S] \b5  4V"/@>5'RL P=@ [8eX.8 1-f.} /a. osq *M.b_/Zm86c5 2*:_U - )!)XN f0y1*8* 69LL"49I`:wZ" ;TRMci19 2+(5Pu*t!u:a1`i7 \4SG(n86ALrs  &)-&1 , 0%h[6E3A>7 S|S%: ;E/n914 5?6 44/)!>;JU<%_6! >Z @ 91MDE)*$A+8%/ 5Q>Q%^< :44LGQ<B$3D @B5B2kwdVqq>W.*2Gg6{HY]0)HZ<]=M:+NF/>%&SF8|7pX@K6-PTJJwEgR&3(20&/ 1!UZE^7%%41C{B 97U#=5@G5 EQ:G(@%3BP -*+ 9*/ *C(0 +4E<+%TQ+--#;86,',V5U,== -($Fu : 5V1,5S.) 6@/+UP<BR'5)+s'  #H"#0:8" - 5 +n &$$    CB ;C!.): )$&  PU=  &#*'&1+(!,)*DI:h08 " #((1<7C'?Q'  +*  .=*;5O,*G$"=7# >! <Y? 'SF *K. IJ8 ' AKD$#4>+!%":G!&&"A]"--(% C= 8*3UM (MI2 '  TP@ %2 %VP )8,3E O:4@:</(.:0*1 N ).64AJ%*S= 8!0, ,3TC!%$? kP*GqH !: 5" M< .< 8$%-(505./@(R.< '%!$,MOZW84>'#=0" )"3!$ $ % $0- *DU## ^GCBB<FD, & O4::3^i7 $%%$/@SN5;xv/4 !WC 7  $2 HE Eb- %) '; <7 A@?+ *; *$6*9."&!$) 4  !( % $ 1B@ K$ 3 (>" (*    4(T/44C?5 B>$    VK  *$ $#3 =0 )8F@."86  "/ !'I**/ 0A8 + -=A+6D1:XA\i3'% *;6ON') :' '.SD="Nm72F@NQ!)  +$17X$F1#1?:'$ $**0"& (%)$ ( ): %'85 Hg"1 1"+HUG +::8C<IG# 23>\f> %0.-;%#4 ]8B=A, +!1<;A. -<(8E0]ZJ9,;L^;6'Ns>JF=) *975 ..CD )J;&HZI'M*=N!2B  , )  7(%0#   #1Q2&&,>?, *!"Ym,   4(  /0, )  <;98 ! (4%  ! !/.  ,0!0037 ' * </21"  . !(   $%595D/" 5D" ( 9 # =)  5*("!   4   44'  -<) 1 9GCA. #6!6Q?R?;)7 !7"0@*=+'3C5$$! 9(2EC57#  0CA=G  $3.:, <H11M ,&%10!.<='%" '%Ze!" ;74#!K7 $*%:$  * #0 @aL +)#(+!-)9;WFF7;_-@q=, + ,2  /?),, DB _= (   ( 8'9'!C:#!# AP1 0$'#1B[&  3Q,9:&F?+ 35''.0';;9% )  $) <;!')J?G, 4C0 3! . $  >-!) ,+S.0!$! 23[)")'-2"   OQB/"* % .-+ -!"'& @=#3%&*KV5%8/P  0,$#4F7558:5 /9 %UW" /MXC(#?+$HV 1,/&_R:5S@.. &/ ,*/ #2 !  -B1"!,8\:2" ;J& %51 KTBA,75*( , 5640,8'*<4 %C%0RV4$3,610  6/80   '%& Q, *((#%!120, "'&L; $8J!C65# #a5)',)%* "&5&%#( <"  $-7)9+. /!& 0?!7*? //$( 3')%7/' '  <H'D>-!A#$' )JH'< 2%9^@ ?@!##  # +/ 21*( 1;"TB  %1(98'9'ReX6"$9$&;A  %4%>H52/ # 5(((,- 1&=?!20 .-,>1  m=#!!3    " $& #   = A;`}E2*B  # (-Q!#% 62 -:))  .?\-, 2? )c_=\G6!?$ /1&% , 'F#0>:@4[[W$%! ,L/%DP^9*9$!@4#! 0  4wG"  )J&A/,Z[)(*, JZ8:2C"?"C3+/. #3 .- +<:RU,*.  1  6H%$6D ;(!$'%LZ,"%GR%  =25G! .#8JWC GY6$%(& A35VE$ /12E"& + 5#&,   "@ AH'  0 %#$J8  %*C H' 1,  &H( 4#-)  /"#  OS# " C '9:$ ,10 . #<>)() TV8 $ 7%#&6! -*:-  ( $2J %*&6lj20  $6ND 2UP *OR Q1^O!<;JN*&( ?$#76Cr?"5 9 %8&' 53%;J(BF 5R1/C##2!4 KN (ZO/<,!%%%$# +<3 '%"'4/04/ , U "C(*,?LJ"1' 8H1"A16( (- 2#<L<)  /.  &=,"$ '2!/_S% *1( #7* 1$##3 %GI   /(C88L&'&"!1 $01$IW# =tzd&  ';  ,+   +"- 10 #2.1 % 55#! +!>   ?$) 2%3*I+4D#43,H    7&5" A) ")& +) * %(7T3278   , !! ) *'&"+ A..B 4c?""&2AK[A!! $.+:9<'"* .:++  9LN .+/  $/<O//$- 9864.+  $EE &7O?;M ,$C@=3 / 9 M (]J!"( DD %  + %E4;= ;   ! B.!@"D;%+- +1B ,A@ 73()BK '$ 5J&*)!7(&A(**[.,>PA muse-4.0.0/src/share/metronome/klick2.wav000066400000000000000000000452661404105311000202620ustar00rootroot00000000000000RIFFJWAVEfmt DXdataJojWA!jQaYp\9CnH&&Y.'  (x{ ! TL W1U5EEW>t5j* v 4 j#": /u TDC }  Wc&= p %r?#(߈7 YhuMM} HHdu7^nNHWE_Tv,M:-їҜ\ڷԏρHyKfnT߱QܼwD#ٗ؆vyndHWe*x01ww`hL53fW [ExX<  V j J V +tf} hk \:.a E & m^  Y qk NX/ HPr 7!y >z5BeK;z+QvyAi $08HvUE.  x 7@ - t (j|- Y m *  nK*-@F6tO|1!$)c(kORj3 c6g!#19?|aRGuZa=5ryogy zDg> # c I   i  l # Y P  z' P a q>: * |" z ` W qm1"( *o!8: Y,  ^ C$( N *  d 4 <*! 1 e tp [ c R>xv<;5*HyDl!CFOBxo0SNu:0C[1mT/Y/r9mz)9DMp| eAA2eQ [akYBo7\]KON`7jG@<uEiAD!#sc`( M  )3}U M   z \ }PsT g 4 h n qW   is N LJjCD7 ~0Xv #9gt4S>$w5jFU#N01aYSjl*4[c] -ax)VuG$rk_NU_j#4esmw- "!4SD-)8\ 3\8p{; fQS>_-^Jrh0 qXI|rk3<.GNWNj~#i<ZHko/\7 X RyG9>8H@/p   my;K!t'HEy&)q1dthfE0~in'Ch1L%pVMa+I)>v<#WD]q^ u;pf=q]u8EX5PGA8I)Oc^`;U :yb  z&n#UBX [(6p(d`pQ q9Rwl:@g(m6sY  jy&~M*-2%[ f&7^[] ec10Ks|kwrx%s.  8#&CZ'tY;%l8< P ?H8d|-MIRQ |+gP(GxK% pRQ%@ASM5 -kp3|A%z=?g] _C}'cn+sD,C8-mgyX|ZMQn-f}"~$JPSP}BPI<-"H3Ynt=n#W*zr(A5pQ }|yyx`9 _{@~Ero5&OJuH"3}%6=Sg65~qnM/D+o7c5i] Rk2=>J]@UB13Js5 \OE3 |cr5U:7*?J< >xwTL%EertHx:a?  ;LhodNoK)R5bz h:r(:0~_JW s?y2?hg);M/DjZF4/8|(LLZz:!p+jas_Da)]_3 r2qs , 'cor\JQNH.W#u_V%Mj;D/X$%#KbH gI[JeSPw|q|x^x}tEW '.)3ZMoH +!f:ITx /A"cTRh lrvd,PY8iZSfp/@HBD(MiF @&I3hFBtI37HeDHLUbSjdUe}F83;DHR&  -]Yf '\=>:'O_Y, 7c\rDT&sZ hv~ ~T+xj~3uuT2+SxuL+ >^n}~?5kt/7 !GtJ (2:,$))!^;O'%oxs]D^R/8THF/'/Y}i P\%sUNx"zsg`dy$2q"OZ;XI9mF%gFnf5VH@z~j_XRg'g5VN'j0 EemL46h[u.2He^)F|@:uKex[=.'0~sG1">xA`DY""6D7-KEA3TjiV@0w|+GbyfG7C+&} A@ C6IWzM/-h1!<NHVY:-##"+&8y=xk7BLDNV;4tGEdq<!Kirw}u;3GG@5>4@:ZaR6$Aq[/)PPH[*0B5/K(33qCaT)=\9e<#pP1BwZ% :`d<)zVfuELk<3;5*/>EBlk. Y ZWNu3AbdG;+,8>J$ "">56/6CNV? D{dXfvgRHUp~y|xnWUNyiD.EUnYY>1)TnwnvqT15GdssuzqZN9!$_z[;iq8$75=*"A1d>St{%ej( 8r_D6CMbyyuxW0!)*1JSbcQKS\jkN;7?O:. 9]wa/ IrY=)f|g]`nnd?%4B_x~daLYSZDPQm{ U'&Q  "/' &,?B#'sjdlt~_JZomJTfeMCLRkZ1",V^9+Dbq> LjxnD  -84,sZE\zM!,>6>:825+& "192>-Ll|wb8+;ZxX01UNJwqj^I8?Qsc7 /O~k[MVpoF=1JcpxebL]pxTGBAMPsnO1*!4/+  '7:,4),  &-#  -KJ= )BV~vfO. '3)3PzvZWM`iz{jgskknz( ;D7;-HOeonbNCDD[]]MRV[hxtahqyxtpfig[Gg|sfowoeQU^jnw~tvtjfZVB5 BknS=57?NZ_U^UcqziU;*#02C65/. &32! 6QNP99&ENrxi>1 !!-1?9+  $.6D%$)-LKF16EME4,/Cc_cMA4>CUY\fRP@@RhmVBCbxhg{v|lVJ@B?L[{|{    ,3mfajnqb_^\cq~jkNT61$5Nvq^JXVXMD83;K`gifcjpnQ' %6]aXR@<:2)$ )4FC=    1>C5  +$7LT96"''#"134)<38/=>DI7($.71(7,8-9842 " &KYM2 &%  (866saawvy ~~y}~u}{qhjaypjurug{ 13&   "##   ",14$,'/# 0,+'0&)%(.("  $)$) #%       $&!  %'*"$  )!.&' '")   ((-56AB6='#-2FQYXS[WWOGLCRTQDGHSU\M>;8@LNXS]X^UXMO[\qij^G@=LW\hincbdaZ]V\`_aeaiQO?GMYk[W?JAQVL`W^cjig^SQVY]URBGTkrwxgc`eou~yz}|~w~twqutjngonrzwx{rz}z{xyy}k|m{||nyhuqounpvwxuz{qj`YXTS^qeliikkgeceirblWdQUJNNTUUPSaYaXNBLP__hkabQdSjfdWD3:KWmgi]TOVL]MUTZ\_lT_I^YdZXRQC9G<H,4&%,;LZlVW>N:MCBE>IBGPONCHFFHA96.)5)B@I>:704$%' '#(2(3,803<77/+2<6:?BDB?54,((! "))115?@NPZOD?31,-,78@>A:+.&047F>64+3<<BD0(,(-4715/,,+.'-'+)%#%" #&&,*.%!      ~}|zx~~}w{w{}{y     '*4/./.100- +*188<:<984-/.:;@>6:*/3D>641'+''0.,?4EEG=;3%,+545<6-+()&35;D;>5)#$/@COME9>78=4B7.,+-?ECH/*%'1;8E5+)(->?QE?465.)#'*$85D9?("#%5%5+1+1(0&'!(-!&*,-.,4*"!%!439:*,!%)-.-/$*('-&)%(2=AA<3"'&$,2$&*#! )+0<8IDOCKCDKIFGBCFDGJKDKLPRWS[LID:FA=BECLBNPV[ZaOZSNXW[TVJDCBMNKXPRPOVZdW^KPLSUV`X[Sc]igdpfndmafbh]ca__fged_`VWPL[_ile`bekksltrnmljrhde\]VUQXbceaNSKfhqjef]a_bmb[TBGEPUVUOMGKQUWXNTCBBIGUQZ[MDL?FBJJA?162A@LKJ=;2;HM]WME4:4:;EFJ;<558>+4,1/2>38**)*'/,5/(*",$836-0),.-)&''1&   '!  ("    ! %    &                $$# "'$& %!%#*&'!*muse-4.0.0/src/share/metronome/klick3.wav000066400000000000000000000540701404105311000202540ustar00rootroot00000000000000RIFF0XWAVEfmt DXdata XNE,-#2!闵8xf&X`)O~]A9n"@3F.7 d]{4   eEuQ Y^obI46L=|F=$%'^eE B8I/ Ҍ99ƥ#I5uc  0< BB4^/IXRD7rPy7)Ev!3j,$Ɲ/8Hb>T%T+2 49*q  W&$'_v'-*"{O֐<%4#6Ao+ #_ Z1iY>jSZ1fHu <)r. X k * sY*oإlݾPH"a $B()("M{_2^cw@^TcFP2k6fo~ y@6'>  "Sl*5< !Au frUm4th  `$/3,vx GA00cAv%1 <- 5"; d\LIAXn=Ճ9+0g y$ + H Tj $ Ut'0( J%&L,\@n Tjp; ljU bZ <wUVJ`j>7!b e9K3 N+R: q r?au Ea[NC\ m\bC H ? Y4 RjHu@/xOd@.H43  | V81# SM1 # )A+&'Aklh A &h; AUqX f a*L / { VjFP`?U;R JShb_yd)u )K 4*8 uq:u+3Fk/| 4{Gn%9{*9VRjq8K)sD N Y{NHO_Dm 1H ?u|Vh- eR8 gBxBJ DL!93 B9G_" ? 0 v=*`!7; ]#g&; fI3(e lnE|C_z1;uNiS] Q1&Ht o'VK( & ~=W_Sx1Tc_6D<8)Mj f opN]L-)3 b   ! ( Z %1u t&2 8"k&-Y~> n %tM 4 0K jMEXFR!n%]^Z?'%{[aaVQFf_I ~7 Hx;;OYTLbPB #> '{2+<R7i8]dRy$ ]sD;f (Q I  ppg.j:uN e8`Wv3ID+j]@ ;$ `2 v  =a$ D*bHrL?{+R{-!jm dPM, Hs"^v%JR 7JuH] JJ fap 'LBn~/m?|<I  ]1\Y,@o"  IsUd$IQ"coH u5 ^$yT-~Cnha M.0VYG3`_w }<(KcjC` '(S QPlLq`nG?  ih]~4OxI*~24{zt>FZU cDarGQ 34b*\wd(( U %g?+=v?u}YhTX:A/ 8kt3HHFbEZ +O^&\pG)Z:f4x> aSwRatwS1S ny.lPAz9-|<(QGlt1, <I R$NO%' `xG7 T*R!,-sj<teop6;/yK1uRtCgnX# eV~%z6S|Y_U6QK?PdIN8bL92^=3 W n-)+iEZm?w+t5_YfE#>up )o*2z#;mp]>KY*E;I[ CW*X3w/=Rfv:r|0yrg'W E~'z JU(J\A _dt`L'<~4} :Z  _A|q'to'{([mMG.Cy}/jLtm"kp}5VG  3L (Vj_?wI\W85{Z2\;+U*UB~/  g>p y;eD;*4mj &Y-q$iJe+cM$  Lo"P@}s}mr~[Rf4b7cGN{T 5 CX% 'W $^3^ E}iu&P #?[-V9N>34%iVe&F5ByIsKi]x:/d1c<-Xx-N)E0i-jBZj'Ioz)!<CRt z#_`q S88 v%Ywh_Nh<I=,r;VS>(['FWUrsdP>iX8@=24ZyU_.tz :9(PMVT)"2rz'!P8_Ml :? YStS&ZtDP&w(zyVL&JgT:; c>Elz4\{uiP(^uMbfLc5lP!/-ox 0RBznaS Z6\\ g;X%m#U7B: wZk YTboB.CmHk"Z++mYg >8 v@NbwOjyz$| < lbRYM*:,~ah^s* f6%X!cz=yqGSeQbrV&" 0c/o:!u@:VnI{ :)1`\U/Ea  y'Y:Nc1RLiKVL(T~|'^ fGn3ut$D.2(JMZPAB-\^GN"= X29R V9+fp 8< 1@ @=8m:/>=W}{kq:S?H`WK3-} \v,>@\_;H0 NWwO2bj}m=4WS7)lhkN'av1DTx;T)NM .P/{1h$#E9 /8Qp"w-rx]Lm8M2J5]1X]5&{EiF_&?JAIzSP 8:b3,=!<tWo[/vA`MG{dOcJ*VCf(^M^:%]x(m{zsYsczcnLVx z rAPa) K}*ksA-WkJ^+(0@u^z@e+3kJ Ate884 k1}BuN WKL$$atsJU5*AqG?ycA;gb>:$OAWL|H<"kEMnK O>cp$y) s:nemD5p=!PQ$M A]GJK"tA 4|Gt8I[WZ7!Z0w^8,9\ ^koz([,>mUvqyT>ik6M1xo,}}#*p&c)vudAP1F0:aN6k >vB1A3zPT"$RM"=ah7BtS$7U-0<t"/@c8". R2j@A6 elpzYv2b E-{UCzlw'5-tut6b_< #_d T|#!d/Qb5@E~3MO1_M0B^H0AA;do+;9,`q4@I87=$?tr@q&Gr$ t|"~Rc6+p J@ "P)JTCOQF,-vIY$=FT,:d5,r1#} *Xk9C!J2~F"iMh7Zn/apD,<F hu :H'I-%4~ %S-m2 HEJU5{Mjz4}z*T.5zGjCYm4S E Bwp(Ld>dc@,*DfZM=&.udG;L1Q9J^O*  s/ddK-o,zU$ Tj7|Rw3kh-\kkAMFk_,"R+eka1-sFqREN]AT9dDQ* >U}EkoD2DU/]VHElAG  Id JRPZG 74ILQ-XjamD'$cLb }`ExiK /W7?]UmXKi2 /2Hb G 'lf}v@ gh1,Ete(6Nh60&zP8qyRAh-0Fui570DW=$!M 3(<>[s {if]i6*F_N6UalolpT65\u5IlG*[*kBDee<To5*[ybFF^zA=(g0+sEv$sfQ>U~/W:57#rNzHnyx(\nO" h*r@+*cgT@L 3ZeA~^J H -{k*Z-, ((@!5by7i bm^~{ 2JPWVwjMB{zI$3byF;^8"  n=k'7%zk;'Es:_mW @ &&u*Mp9,CCTD/ 2N}hi{^[~jND "# #:- 0bXgjv )sj%#I;1iPX{r? ;ij}sdjC7V`Kwi7 lRbosn[UH7;;]}}{\O&&KLaw{nUDH7(~5qM1 +R$E7.* `ryl9XXTYx]e %,9 rPzkTa)ooK1>c{~wXJ?J[ssQO8%+:auv`WK' !%5. ? si\wF6?_n1Cyo}\.{kl#5n|iV_Q><KZfL2'=_nTC& 4j?jRLA.$c}i@T0 *pa' L\Ur 4D\qY<,AmkS(  ?uv.K,_L*)B8>;(  1B6% (RU<! wrUF=XC=G;BFEA7VT][tx  $iLKTgq{&BYE/--ESk`B5?] _H?EU\nP9)  zQ+$9IWtr`BSR+;-Oss}nkk}mU[-.6>an_LBD=&. 54)9, @hf80$'& + L]oouP)3ZklcV7>r`\b^L" =]\wrOYfjacz|uqsU:7DW@ 0QVagts^CK[F8AC: HnxtbKKI#!!-)2AhaI/0 --5HddegWsurzg\D& 0CM&   +% "(N@;AEA7.5QaU3 24 )@QM7:$"8Npbtvs]N2'5MnyiTC39HH8% '! %->&""4;OE:(w~~jgcdx ,*5" &%.7B34%$9FOJE,ahiZ\`aohdY`^E@9=Lcwxs_Q>@<JPXM3  uq "*%*6_eZO<2CKWSZSaVXpfY[YiMFD</9PZfZC9 $)(@=BK89'.BPYKNKD,.<[IJ/{nlzxys/<<AA=4)  -);J]]aa_brxvnrqw}~qeYTSYVB>1<(4;B13   - *(*(#/*33#$%%#  17980+=DJGKJRa`[D; % ("$)((&5:7>=>!'4>G:4$!2,0'3#C46  %  !-31<80802>8ACER\_KJ@/&%   %"!"896+;>6.'#  "$'!"/"$#"'/"0-*0223+'"" "&$+/5;;44<98:-'$"##      (  "(! !'"+)(!%1/!-)*%(+"    $$0)&!  '%        &!         # !"                                                   muse-4.0.0/src/share/metronome/klick4.wav000066400000000000000000000330001404105311000202430ustar00rootroot00000000000000RIFF5WAVEfmt DXdata5:9hh$l^!1ߟTo$Y14]Eر 3n{4 t^!B` QKqrrMQqN!;oX t:J% PbA:=k!7YVlJ e['y/Q GFpt<| +Kb0.# ;X_@>Mwz`/xR^3T~Vki)mdmo*m!g[KA0mHh,/b\OZ.[q ,^>tYy,1t09^bPfd[l > m{iPlWW iLZJl Sj!<ou( *g6a[n)q?sJM:Q hp3Zr+_CGZc(t)Uz Zi|F8o5' y[%oSGI:!D7 Q OD51t/S_s/G@6yOg'I#*:n"`=/9>JC#pG$\Rg2d0'j"83*v%|cYc$"/5_o/f\R ;+%ti4OdkT*fie FAs5x396N|g)$c]gF$ LFQCUI M<G@ E\_q5,KPG8fDIsS'STX YtNm2hT9`3b53-0 ~kC[ xh%;8E&$E|Gw*e \:Nt~R. ~BAd1lz(G)jczqn:i0;=-*'dfl%]+4/t"x4 XB60:pLr -SxsmFtt1v5TJgM/FLnJT%Rf_cvaq8IJmzVjovt`.'!$ vN0~XbJ?mL (?oVT{p.~v]]pKaP"z61Wk$+i*1!zE, dv-]N(+q*(3E"!4~1 0(S :*L8kip'$ xT98hofC+pN0tI"$fUH? [+39' $hd(q|]VRR&0'ce8k !,@n9U3I[{F ABcMCJc{?/b7 -sNdc:n+aDLK!qq9n6H.%d;$QPH%EVJ k>fsiR]EA{3fVde~~iD5*D377DnwbG&]!msR&,]`E}DVnz<DC>0Jo{J^de[<=V;Uca!:JHUh`:5H+#zp9oF "ax[L<OQmN-CtBL fdn.{<0 :=Ki"MbUC?&h|k"XTfm;F|:;M;rIUpv=Fis;1 $>Pv1 $}g'%bgg~D4at<`9<, 0%m PJuwt{qyKTY]flixQ[{(.gQ\%(Jfd?#J>#E^3uWvA^<'#^oB%9$CZL&>OECL("EP,1WuhW? $H8%)Xa+4+(# (F?=;sv">xaeN1 /_zqJO~}v]hPB5EM*;fD2HK8 8c% %@TVCC;&33q? ),K1/"'C3*0\Z>2he4 (dvT ?c{i6'Rl\LC0#!,  %EssJ+!",<, %(28A;(<IH? 9@N70'1Ch{ZBl#jV15>F/ (BV0]A68MM<% AE7, %=3&-UY83>" U^C .4&8A> 9MO/$+&"(4" *JYPH1*/:JL# '&1 1NWL)GSB?,&4C?0G[]^-# $WB=!8CX^dD;*) 'AL+" "#;^f;1 Is|hW!12@>DKMO31+;JU_B: 3Wpg/*;MMHR: !4%7`bR! ' $#CJ'7,# "zxy|       +9%.'"#)#!-& $FPN?3#'+$)#(74?/>.C)*6*>2 :Qi\H&/3,"#'4288-$%2/9(:<- $!!1=?@4& !'/&&#-(1(#)'"!2MDI*% 37852   -6;2. ,AGB<0*-,      " * !-( %+ "! ! & #(&  *&    !&#     !                                               muse-4.0.0/src/share/metronome/klick5.wav000066400000000000000000000027121404105311000202520ustar00rootroot00000000000000RIFFWAVEfmt DXdata$a NX)~&,f6O y'4ٰ&f\8lU,,,_%J(t$B#4#;hTU:c, 1m¾7c R66"*SOݣ?q a?)|'  aj_l+ov0B-l b"$4}vT} Nxij |:V/^WM O > %4 ,SN5@ d d ^ E 9m#&!1 X<n8 Z  A o,6 f +2JH?U9k 4 Xl [  |  3 7]v!4vBn-;G"?_zDy%0PmDo~YczYi%m53(&ZxKb  xB}uW*klH015gn'PRBO^8.+geoeP'm_s=AeSfls?"c0)-u>.~bRHs{LPI'5\8H>RP vf&.W2797-1"/ `=x y8Ɠҏ:t2ZnZx"% [ *M#m w  I# 3; " *e@pXJ?3s""y&0q) ^ x>! m!#J#M  9 ?F  mGsi !"WH + w*`^n,H*3\ti [p  Q  bX  %SW2ZWVS~Yz{'H5+L2lx93Qw T&wRE-Rj%l%^3w|s  4   `t ,`WwAABny3muse-4.0.0/src/share/plugins/000077500000000000000000000000001404105311000160235ustar00rootroot00000000000000muse-4.0.0/src/share/plugins/2142.ui000066400000000000000000000310431404105311000167530ustar00rootroot00000000000000 freeverb 0 0 512 205 0 0 400 200 Tap-Reverberator Decay [ms] false 0 0 true 0 0 0 10000 0 0 0 true 1 0 -70 -70 10 dB 2 Dry Level [dB] false 0 0 true 2 0 -70 -70 10 dB 2 Wet Level [dB] false 10 0 1 -70 10 10 0 2 -70 10 10 0 0 10000 0 0 Preset: false 0 0 AfterBurn AfterBurn (Long) Ambience Ambience (Thick) Ambience (Thick) - HD Cathedral Cathedral - HD Drum Chamber Garage Garage (Bright) Gymnasium Gymnasium (Bright) Gymnasium (Bright) - HD Hall (Small) Hall (Medium) Hall (Large) Hall (Large) - HD Plate (Small) Plate (Medium) Plate (Large) Plate (Large) - HD Pulse Chamber Pulse Chamber (Reverse) Resonator (96 ms) Resonator (152 ms) Resonator (208 ms) Room (Small) Room (Medium) Room (Large) Room (Large) - HD Slap Chamber Slap Chamber - HD Slap Chamber (Bright) Slap Chamber (Bright) HD Smooth Hall (Small) Smooth Hall (Medium) Smooth Hall (Large) Smooth Hall (Large) - HD Vocal Plate Vocal Plate - HD Warble Chamber Warehoouse Warehouse - HD Comb Filters Allpass Filters Bandpass Filters Enhanced Stereo MusEGui::DoubleLabel QWidget
doublelabel.h
MusEGui::Slider QWidget
slider.h
muse-4.0.0/src/share/plugins/CMakeLists.txt000066400000000000000000000022721404105311000205660ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB plugins_ui_files *.ui) set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${plugins_ui_files} CACHE INTERNAL "" ) install( FILES ${plugins_ui_files} DESTINATION ${MusE_SHARE_DIR}/plugins ) muse-4.0.0/src/share/pybridge/000077500000000000000000000000001404105311000161475ustar00rootroot00000000000000muse-4.0.0/src/share/pybridge/CMakeLists.txt000066400000000000000000000021071404105311000207070ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB pybridge_files *.py) install( FILES ${pybridge_files} DESTINATION ${MusE_SHARE_DIR}/pybridge ) muse-4.0.0/src/share/pybridge/examples/000077500000000000000000000000001404105311000177655ustar00rootroot00000000000000muse-4.0.0/src/share/pybridge/examples/addpartexample.py000066400000000000000000000026431404105311000233370ustar00rootroot00000000000000""" //========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //========================================================= """ import Pyro4.core muse=Pyro4.core.Proxy('PYRONAME:muse') # # Example on how to insert a new note, outcommented since I run the script several times and it inserts so many notes :-) # But it works! # rpos = muse.getRPos() lpos = muse.getLPos() event = {'data':[61,100,0], 'tick':0, # Relative offset of part - 0 = beginning of part 'type':"note", 'len':rpos - lpos} part = {'events': [event], 'tick': lpos} muse.createPart("Track 1", lpos, rpos - lpos, part) muse-4.0.0/src/share/pybridge/examples/addtrack.py000066400000000000000000000031361404105311000221170ustar00rootroot00000000000000""" //========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //========================================================= """ import Pyro4.core import time muse=Pyro4.core.Proxy("PYRONAME:muse") for j in range(0,5): for i in range(0,30): muse.addMidiTrack("amiditrack" + str(i)) for i in range(0,30): muse.deleteTrack("amiditrack" + str(i)) for i in range(0, 10): print (i) muse.addMidiTrack("amiditrack") muse.addWaveTrack("awavetrack") muse.addOutput("anoutput") muse.addInput("aninput") muse.setMute("aninput", False) muse.setAudioTrackVolume("aninput",1.0) muse.deleteTrack("amiditrack") muse.deleteTrack("awavetrack") muse.deleteTrack("anoutput") muse.deleteTrack("aninput") time.sleep(1) muse-4.0.0/src/share/pybridge/examples/ctrlexample.py000066400000000000000000000026161404105311000226640ustar00rootroot00000000000000""" //========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //========================================================= """ import Pyro4.core import time muse=Pyro4.core.Proxy('PYRONAME:muse') #for i in range(0,10): # print ("Ctrl no " + str(i) + " = " + str(muse.getMidiControllerValue("Track 1", i))) """ for i in range(0,127): muse.setMidiControllerValue("Track 1", 7, i) time.sleep(0.1) """ muse.setMidiControllerValue("Track 1", 7, 56) print (muse.getMidiControllerValue("Track 1", 7)) print (muse.getAudioTrackVolume("Out 1")) muse.setAudioTrackVolume("Out 1", 0.034) muse-4.0.0/src/share/pybridge/examples/effecttoggle.py000066400000000000000000000023511404105311000227760ustar00rootroot00000000000000""" //========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //========================================================= """ import Pyro4.core import time muse=Pyro4.core.Proxy('PYRONAME:muse') trackname = "wavtrack" fxs = muse.getTrackEffects(trackname) print (fxs) for i in range (0,10): muse.toggleTrackEffect(trackname,0, False) time.sleep(1) muse.toggleTrackEffect(trackname,0, True) time.sleep(1) muse-4.0.0/src/share/pybridge/examples/mute.py000066400000000000000000000023121404105311000213070ustar00rootroot00000000000000""" //========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //========================================================= """ import Pyro4.core import time muse=Pyro4.core.Proxy('PYRONAME:muse') for i in range(0,10): muse.setMute("Strings", False) muse.setMute("Lead1", True) time.sleep(1) muse.setMute("Strings", True) muse.setMute("Lead1", False) time.sleep(1) muse-4.0.0/src/share/pybridge/examples/repeatpart.py000066400000000000000000000044601404105311000225120ustar00rootroot00000000000000""" //========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //========================================================= """ import Pyro4.core import sys import time SLEEPIVAL=0.3 def advanceToNextSection(muse, newlpos, newrpos): print ("Advancing...") currpos = muse.getRPos() curlpos = muse.getLPos() curpos = muse.getCPos() muse.setLoop(False) while curpos < currpos: time.sleep(SLEEPIVAL) curpos = muse.getCPos() print ("Leaving current section...") muse.setRPos(newrpos) curpos = muse.getCPos() while curpos < newlpos: time.sleep(SLEEPIVAL) curpos = muse.getCPos() print ("Entered new section") muse.setLPos(newlpos) muse.setLoop(True) return muse=Pyro4.core.Proxy('PYRONAME:muse') muse.stopPlay() parts = muse.getParts("Track 1") muse.setLPos(parts[0]['tick']) muse.setRPos(parts[0]['tick'] + parts[0]['len']) muse.setCPos(0) time.sleep(0.2) # Hmmm, don't like it but it seems necessary to pause a short while before starting play muse.setLoop(True) muse.startPlay() for i in range(1, len(parts)): part = parts[i] tick = part['tick'] len = part['len'] print ("Press enter to advance to next section/part!") sys.stdin.read(1) advanceToNextSection(muse, tick, tick + len) print ("This is the final section. Disabling loop and leaving...") muse.setLoop(False) #print ("Press enter to leave final section") #sys.stdin.read(1) #muse.setLoop(False) muse-4.0.0/src/share/pybridge/examples/setpositionexample.py000066400000000000000000000033601404105311000242750ustar00rootroot00000000000000""" //========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //========================================================= """ import Pyro4.core import time muse=Pyro4.core.Proxy('PYRONAME:muse') parts = muse.getParts("Track 1") ptick = parts[0]['tick'] plen = parts[0]['len'] muse.setLPos(ptick) muse.setRPos(ptick + plen) # Using '/' here causes error in Python 3 which # treats it as float division. We want integer division '//' # which was introduced in Python 2.2. muse.setCPos(ptick + plen // 2) songlen = muse.getSongLen() #print "Song length: " + str(songlen) # # Copy first part to after current song length, thus increase song length with length of first part # newsonglen = songlen + parts[0]['len'] muse.setSongLen(newsonglen) muse.createPart("Track 1", songlen + 1, parts[0]['len'], parts[0]) time.sleep(1) lastpart = muse.getParts("Track 1").pop() print (lastpart['id']) muse.deletePart(lastpart['id']) print (muse.getDivision()) muse-4.0.0/src/share/pybridge/examples/tempoexample.py000066400000000000000000000021221404105311000230340ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import Pyro4.core muse=Pyro4.core.Proxy('PYRONAME:muse') print ("Tempo: " + str(muse.getTempo(0))) muse-4.0.0/src/share/pybridge/examples/trackparamchangeexample.py000066400000000000000000000025021404105311000252050ustar00rootroot00000000000000""" //========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //========================================================= """ import Pyro4.core import time muse=Pyro4.core.Proxy('PYRONAME:muse') muse.setMidiTrackParameter("Track 1", "velocity",10) muse.setMidiTrackParameter("Track 1", "compression",101) muse.setMidiTrackParameter("Track 1", "delay",2) muse.setMidiTrackParameter("Track 1", "transposition",1) for i in range(-127, 127): muse.setMidiTrackParameter("Track 1", "velocity",i) time.sleep(0.1) muse-4.0.0/src/share/pybridge/musepclient.py000066400000000000000000000026241404105311000210550ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= # # Example client for MusE Pyro bridge (Python Remote Object) # import Pyro4.core import time muse=Pyro4.core.Proxy('PYRONAME:muse') print ("Current position is: " + str(muse.getCPos())) muse.startPlay() time.sleep(1) # Sleep one second muse.stopPlay() print ("New position is: " + str(muse.getCPos())) muse.rewindStart() print ("Pos after rewind is: " + str(muse.getCPos())) print ("Lpos, Rpos: " + str(muse.getLPos()) + ":" + str(muse.getRPos())) muse-4.0.0/src/share/pybridge/museplauncher.py000066400000000000000000000362771404105311000214130ustar00rootroot00000000000000""" //========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //========================================================= This file is used by MusE for launching a Pyro name service and connecting a remote object to the global Python functions """ from __future__ import print_function import Pyro4.core import Pyro4.naming from Pyro4.errors import PyroError,NamingError import socket import select import time import threading # # Note: this module, 'muse' is activated from within MusE - thus it is not possible to execute the scripts without a running # MusE instance # import muse # # Class which implements the functionality that is used remotely. # In short just repeating the global functions in the muse-module # # TODO: It should be better to skip this class completely by implementing # functionality as a class in pyapi.cpp instead of global functions # that need to be wrapped like this # # Pyro >= 4.46, requires exposing now class MusE: def __init__(self, daemon): self.daemon = daemon # This method is to support a technique of shutting down the server # by calling this 'shutdown' from another thread or process. # From another process obviously tested OK. But it would be more desirable # from another thread. But the code to do so from another thread in the SAME # process can be complex (using sub-interpreters). # So our simpler loopCondition technique is the one used at this time of writing. # Still, we keep this around for possible usage since it does work. # # oneway in case call returns much later than daemon.shutdown @Pyro4.oneway @Pyro4.expose def shutdown(self): print('Shutting down Pyro daemon...') self.daemon.shutdown() #@staticmethod #@classmethod @Pyro4.expose def getCPos(self): # Get current position return muse.getCPos() @Pyro4.expose def startPlay(self): # Start playback return muse.startPlay() @Pyro4.expose def stopPlay(self): # Stop playback return muse.stopPlay() @Pyro4.expose def rewindStart(self): # Rewind current position to start return muse.rewindStart() @Pyro4.expose def getLPos(self): # Get position of left locator return muse.getLPos() @Pyro4.expose def getRPos(self): # Get position of right locator return muse.getRPos() @Pyro4.expose def getTempo(self, tick): #Get tempo at particular tick return muse.getTempo(tick) @Pyro4.expose def getTrackNames(self): # get track names return muse.getTrackNames() @Pyro4.expose def getParts(self, trackname): # get parts in a particular track return muse.getParts(trackname) @Pyro4.expose def createPart(self, trackname, starttick, lenticks, part): # create part in track return muse.createPart(trackname, starttick, lenticks, part) @Pyro4.expose def modifyPart(self, part): # modify a part (the part to be modified is specified by its id return muse.modifyPart((part)) @Pyro4.expose def deletePart(self, part): # delete a part return muse.deletePart((part)) @Pyro4.expose def getSelectedTrack(self): # get first selected track in arranger window return muse.getSelectedTrack() @Pyro4.expose def importPart(self, trackname, filename, tick): # import part file to a track at a given position return muse.importPart(trackname, filename, tick) @Pyro4.expose def setCPos(self, tick): # set current position return muse.setPos(0, tick) @Pyro4.expose def setLPos(self, tick): # set left locator return muse.setPos(1, tick) @Pyro4.expose def setRPos(self, tick): # set right locator return muse.setPos(2, tick) @Pyro4.expose def setSongLen(self, ticks): # set song length return muse.setSongLen(ticks) @Pyro4.expose def getSongLen(self): # get song length return muse.getSongLen() @Pyro4.expose def getDivision(self): # get division (ticks per 1/4, or per beat?) return muse.getDivision() @Pyro4.expose def setMidiTrackParameter(self, trackname, paramname, value): # set midi track parameter (velocity, compression, len, transpose) return muse.setMidiTrackParameter(trackname, paramname, value); @Pyro4.expose def getLoop(self): # get loop flag return muse.getLoop() @Pyro4.expose def setLoop(self, loopFlag): # set loop flag return muse.setLoop(loopFlag) @Pyro4.expose def getMute(self, trackname): # get track mute parameter return muse.getMute(trackname) @Pyro4.expose def setMute(self, trackname, enabled): # set track mute parameter return muse.setMute(trackname, enabled) @Pyro4.expose def setVolume(self, trackname, volume): # set mixer volume return muse.setVolume(trackname, volume) @Pyro4.expose def getMidiControllerValue(self, trackname, ctrlno): # get a particular midi controller value for a track return muse.getMidiControllerValue(trackname, ctrlno) @Pyro4.expose def setMidiControllerValue(self, trackname, ctrlno, value): # set a particular midi controller value for a track return muse.setMidiControllerValue(trackname, ctrlno, value) @Pyro4.expose def setAudioTrackVolume(self, trackname, dvol): # set volume for audio track return muse.setAudioTrackVolume(trackname, dvol) @Pyro4.expose def getAudioTrackVolume(self, trackname): # get volume for audio track return muse.getAudioTrackVolume(trackname) @Pyro4.expose def getTrackEffects(self, trackname): # get effect names for an audio track return muse.getTrackEffects(trackname) @Pyro4.expose def toggleTrackEffect(self, trackname, effectno, onoff): # toggle specific effect on/off return muse.toggleTrackEffect(trackname, effectno, onoff) def findNewTrack(self, oldtracknames): #internal function tracknames = muse.getTrackNames() for trackname in tracknames: if trackname in oldtracknames: continue return trackname @Pyro4.expose def changeTrackName(self, trackname, newname): #change track name return muse.changeTrackName(trackname, newname) def nameNewTrack(self, newname, oldtracknames):# Internal function, wait until new track shows up in tracknames, then rename it tmpname = None for i in range(0,100): tmpname = self.findNewTrack(oldtracknames) if tmpname == None: time.sleep(0.1) continue else: self.changeTrackName(tmpname, newname) time.sleep(0.1) # Ouch!! break @Pyro4.expose def addMidiTrack(self, trackname): # add midi track oldtracknames = muse.getTrackNames() if trackname in oldtracknames: return None muse.addMidiTrack() self.nameNewTrack(trackname, oldtracknames) @Pyro4.expose def addWaveTrack(self, trackname): # add wave track oldtracknames = muse.getTrackNames() if trackname in oldtracknames: return None muse.addWaveTrack() self.nameNewTrack(trackname, oldtracknames) @Pyro4.expose def addInput(self, trackname): # add audio input oldtracknames = muse.getTrackNames() if trackname in oldtracknames: return None muse.addInput() self.nameNewTrack(trackname, oldtracknames) @Pyro4.expose def addOutput(self, trackname): # add audio output oldtracknames = muse.getTrackNames() if trackname in oldtracknames: return None muse.addOutput() self.nameNewTrack(trackname, oldtracknames) @Pyro4.expose def addGroup(self, trackname): # add audio group oldtracknames = muse.getTrackNames() if trackname in oldtracknames: return None muse.addGroup() self.nameNewTrack(trackname, oldtracknames) @Pyro4.expose def deleteTrack(self, trackname): # delete a track tracknames = muse.getTrackNames() if trackname not in tracknames: return False muse.deleteTrack(trackname) # def getOutputRoute(self, trackname): # return muse.getOutputRoute(trackname) class NameServiceThread(threading.Thread): def __init__(self, host, port): threading.Thread.__init__(self) self.host = host self.port = port def run(self): hostname = socket.gethostname() print ("Starting Pyro nameserver") try: if self.port is None: Pyro4.naming.startNSloop(host=self.host) else: Pyro4.naming.startNSloop(host=self.host, port=self.port) except: print ("Unable to start Pyro nameserver at host=" + str(self.host) + " port=" + str(self.port)) print ("Pyro nameserver finished") # # main server program # def main(): print ("Inside museplauncher.py...") # TESTED: When running example scripts, too low a value caused problems with # 'not enough data' errors unless set to higher, around at least 3. But if set too high # it takes longer for it to check loopCondition for our shutdown flag (tested 50 seconds, # average shutdown wait was long). Be sure this value is LESS THAN the timeout value of # the parent QThread's wait(value) so that the thread does not timeout before the daemon does. # This is done automatically in our c++ code by setting the QThread wait time to a higher # value than our global MusEGlobal::pythonBridgePyroCommTimeout. # # We need to set either a socket communication timeout, # or use the select based server. Otherwise the daemon requestLoop # will block indefinitely and is never able to evaluate the loopCondition. # This is a floating point value. Pyro4.config.COMMTIMEOUT = muse.getConfigPyroCommTimeout() config_pyro_ns_hostname = muse.getConfigPyroNSHostname() config_pyro_ns_port = muse.getConfigPyroNSPort() config_pyro_daemon_hostname = muse.getConfigPyroDaemonHostname() config_pyro_daemon_port = muse.getConfigPyroDaemonPort() if config_pyro_ns_hostname: ns_hostname = config_pyro_ns_hostname else: #ns_hostname = socket.gethostname() ns_hostname = None if config_pyro_ns_port: ns_port = int(config_pyro_ns_port) else: ns_port = None if config_pyro_daemon_hostname: daemon_hostname = config_pyro_daemon_hostname else: #daemon_hostname = socket.gethostname() daemon_hostname = None if config_pyro_daemon_port: daemon_port = int(config_pyro_daemon_port) else: daemon_port = None print("Nameserver hostname:" + str(ns_hostname) + " Port:" + str(ns_port) + " Attempting to locate a Pyro nameserver...") try: ns = Pyro4.locateNS(host=ns_hostname, port=ns_port) except Pyro4.errors.NamingError: ns_running = False else: ns_running = True if ns_running: print("...Found Pyro nameserver") else: print("...Pyro nameserver not found. Starting a new one...") # Note: See the Pyro4 eventloop example for a cool custom loop that works, # but it requires the broadcast server to be running. This does not. nsthread = NameServiceThread(host=ns_hostname, port=ns_port) nsthread.start() for i in range(0,5): try: ns = Pyro4.locateNS(host=ns_hostname, port=ns_port) except Pyro4.errors.NamingError: if i == 4: print("...Pyro nameserver did not respond") return time.sleep(1) else: break print("Daemon hostname:" + str(daemon_hostname) + " Port:" + str(daemon_port)) try: if daemon_port is None: pyrodaemon = Pyro4.core.Daemon(host=daemon_hostname) else: pyrodaemon = Pyro4.core.Daemon(host=daemon_hostname, port=daemon_port) except: print("Could not start Pyro daemon") return print("Pyro daemon location string=%s" % pyrodaemon.locationStr) print("Pyro daemon sockets=%s" % pyrodaemon.sockets) # connect a new object implementation (first unregister previous one) try: ns.remove('muse') except Pyro4.errors.NamingError: pass muse_inst = MusE(pyrodaemon) # register a server object with the daemon serveruri = pyrodaemon.register(muse_inst, objectId="muse") print("Object registered with Pyro daemon. uri=%s" % serveruri) # register it with the embedded nameserver directly ns.register("muse", serveruri) print("Object registered with the Pyro nameserver") # Tested OK. pyrodaemon.requestLoop(lambda : muse.serverRunFlag()) print("Pyro daemon loop finished") # We only want execution if running the script. The value is set to __main__. # Otherwise if being imported by another module __name__ contains the name of the script. if __name__=="__main__": main() # FIXME What was this for? It caused double execution in cases # where the server didn't start (which makes sense) #main() muse-4.0.0/src/share/pybridge/musepstopserver.py000066400000000000000000000022621404105311000220110ustar00rootroot00000000000000""" //========================================================= // MusE // Linux Music Editor // (C) Copyright 2009 Mathias Gyllengahm (lunar_shuttle@users.sf.net) // (C) Copyright 2019 Tim E. Real (terminator356 on users dot sourceforge dot net) # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //========================================================= This file stops the muse Pyro server """ import Pyro4.core muse=Pyro4.core.Proxy('PYRONAME:muse') muse.shutdown() muse._pyroRelease() muse-4.0.0/src/share/pybridge/parter/000077500000000000000000000000001404105311000174445ustar00rootroot00000000000000muse-4.0.0/src/share/pybridge/parter/main.py000066400000000000000000000031421404105311000207420ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time from PyQt5.QtWidgets import QApplication from parter import ParterMainwidget import sys, os import Pyro4.core #import musemock #muse = musemock.MusEMock() muse=Pyro4.core.Proxy('PYRONAME:muse') """ strack = muse.getSelectedTrack() cpos = muse.getCPos() muse.importPart(strack, "/home/ddskmlg/.muse/parts/testpart2.mpt", cpos) sys.exit(0) """ if __name__ == '__main__': app = QApplication(sys.argv) partsdir = os.getenv("HOME") + "/.muse/parts" mainw = ParterMainwidget(None, muse, partsdir) mainw.show() #muse.importPart("Track 1","/home/ddskmlg/.muse/parts/testpart2.mpt",18432) sys.exit(app.exec_()) muse-4.0.0/src/share/pybridge/parter/parter.py000066400000000000000000000112611404105311000213140ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time,os from PyQt5.QtWidgets import QWidget, QFileDialog, QListView, QTreeView, QDirModel, QApplication from PyQt5.QtWidgets import QButtonGroup, QPushButton, QLabel, QGridLayout from PyQt5.QtCore import QFileInfo class ParterMainwidget(QWidget): def __init__(self, parent=None, muse=None, partsdir=None): QWidget.__init__(self, parent) self.muse = muse self.partsdir = partsdir self.lcurdir = QLabel(partsdir) moveupbutton = QPushButton("Parent dir") appendbutton = QPushButton("Append") putbutton = QPushButton("Put") blayout = QGridLayout() blayout.addWidget(self.lcurdir) blayout.addWidget(moveupbutton) blayout.addWidget(appendbutton) blayout.addWidget(putbutton) self.tree = QTreeView() self.dirmodel = QDirModel() self.tree.setModel(self.dirmodel) self.tree.setRootIndex(self.dirmodel.index(self.partsdir)) layout = QGridLayout() self.setLayout(layout) layout.addWidget(self.tree, 0, 0) layout.addLayout(blayout, 0, 1) moveupbutton.clicked.connect(self.parentDir) appendbutton.clicked.connect(self.appendPressed) putbutton.clicked.connect(self.putPressed) self.tree.doubleClicked.connect(self.activated) def parentDir(self): f = QFileInfo(self.partsdir) self.changeDir(f.canonicalPath()) def changeDir(self, newdir): self.partsdir = newdir self.tree.setRootIndex(self.dirmodel.index(self.partsdir)) self.lcurdir.setText(self.partsdir) def activated(self, s): fileInfo = self.dirmodel.fileInfo(s) if fileInfo.isDir(): self.changeDir(fileInfo.absoluteFilePath()) return fname = str(fileInfo.absoluteFilePath()) # if not str() around it crashes! self.putPart(fname) def putPart(self, fname): trackid = self.muse.getSelectedTrack() if trackid == None: return cpos = self.muse.getCPos() self.muse.importPart(trackid, fname, cpos) def getSelectedItem(self): selectionmodel = self.tree.selectionModel() for i in selectionmodel.selectedIndexes(): fileInfo = self.dirmodel.fileInfo(i) return str(fileInfo.absoluteFilePath()) return None def appendPressed(self): selected = self.getSelectedItem() if selected == None: return trackid = self.muse.getSelectedTrack() if trackid == None: return parts = self.muse.getParts(trackid) if parts == None: return pos = 0 if len(parts) > 0: part = parts[len(parts) - 1] pos = part['tick'] + part['len'] print ("Appending " + selected) self.muse.importPart(trackid, selected, pos) def putPressed(self): selected = self.getSelectedItem() if selected == None: return trackid = self.muse.getSelectedTrack() if trackid == None: return cpos = self.muse.getCPos() self.muse.importPart(trackid, selected, cpos) def testfunc2(self, index): print (str(index.row()) + " " + str(index.column())) print (index.data().toString()) if __name__ == '__main__': app = QApplication(sys.argv) mainw = ParterMainwidget() mainw.show() sys.exit(app.exec_()) muse-4.0.0/src/share/pybridge/robert.py000066400000000000000000000041401404105311000200150ustar00rootroot00000000000000# -*- coding: utf-8 -*- #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= # # Example client for MusE Pyro bridge (Python Remote Object) # import Pyro4.core import time muse=Pyro4.core.Proxy('PYRONAME:muse') print ("Current position is: " + str(muse.getCPos())) midiDevice=open("/dev/snd/midiC1D0") nextIsCommand=False while True: v=midiDevice.read(1) if nextIsCommand: print (" %d"%ord(v)) if ord(v) == 0: print ("set hh") muse.setMute("hh", False) muse.setMute("RIDE", True) if ord(v) == 1: muse.setMute("hh", True) muse.setMute("RIDE", False) print ("set ride") if ord(v) == 2: muse.setMute("ACCENT1", False) if ord(v) == 3: muse.setMute("ACCENT2", False) if ord(v) == 127: print ("mute all accents") muse.setMute("ACCENT1", True) muse.setMute("ACCENT2", True) nextIsCommand=False if ord(v) == 192: nextIsCommand=True ''' muse.startPlay() time.sleep(1) # Sleep one second muse.stopPlay() print ("New position is: " + str(muse.getCPos())) muse.rewindStart() print "Pos after rewind is: " + str(muse.getCPos()) print "Lpos, Rpos: " + str(muse.getLPos()) + ":" + str(muse.getRPos()) ''' muse-4.0.0/src/share/rdf/000077500000000000000000000000001404105311000151155ustar00rootroot00000000000000muse-4.0.0/src/share/rdf/CMakeLists.txt000066400000000000000000000021071404105311000176550ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB rdf_files *.rdfs *.rdf README) install( FILES ${rdf_files} DESTINATION ${MusE_SHARE_DIR}/rdf ) muse-4.0.0/src/share/rdf/README000066400000000000000000000004441404105311000157770ustar00rootroot00000000000000Any LADSPA rdf files placed here will OVERRIDE official versions that may come with their respective plugins. The rdf files installed here by the program are CORRECTED versions of the official versions of a few plugins. There were several errors in them. Authors were contacted, no reply. muse-4.0.0/src/share/rdf/blop.rdf000066400000000000000000003356131404105311000165610ustar00rootroot00000000000000 ]> Bandlimited Sawtooth Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Sawtooth Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Square Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Square Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Variable Width Pulse Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Variable Width Pulse Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Variable Width Pulse Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Variable Width Pulse Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Variable Slope Triangle Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Variable Slope Triangle Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Variable Slope Triangle Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Bandlimited Variable Slope Triangle Oscillator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later ADSR Envelope Generator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Mono Amplifier Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Mono Amplifier Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Frequency Modulator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Frequency Modulator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Frequency Modulator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Frequency Modulator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Control to Audio Interpolator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Random Wave Generator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Random Wave Generator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Random Wave Generator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Random Wave Generator Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Sum Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Sum Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Sum Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Product Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Product Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Product Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Difference Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Difference Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Difference Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Difference Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Ratio Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Ratio Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Ratio Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Ratio Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later 4 Pole Low Pass Filter with Resonance Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later 4 Pole Low Pass Filter with Resonance Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Branch Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Branch Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Analogue Style 64 Step Sequencer Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Analogue Style 32 Step Sequencer Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Analogue Style 16 Step Sequencer Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Clock Oscillator with Gate Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Clock Oscillator with Gate Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later ADSR Envelope Generator with Gate and Trigger Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later DAHDSR Envelope Generator with Gate and Trigger (Audio Rate Inputs) Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later DAHDSR Envelope Generator with Gate and Trigger (Control Inputs) Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later DAHDSR Envelope Generator with Control Gate and Trigger (Control Inputs) Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Clock Pulse Oscillator with Gate Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Clock Pulse Oscillator with Gate Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Tracker Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Signal Tracker Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Quantiser (20 Steps) Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Quantiser (50 Steps) Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later Quantiser (100 Steps) Mike Rawes <mike_rawes@yahoo.co.uk> GNU General Public Licence Version 2 or Later muse-4.0.0/src/share/rdf/swh-plugins.rdf000066400000000000000000005311651404105311000201050ustar00rootroot00000000000000 ]> Steve Harris <steve@plugin.org.uk> Aliasing Andy Wingo <wingo at pobox dot com> Allpass delay line, noninterpolating Andy Wingo <wingo at pobox dot com> Allpass delay line, linear interpolation Andy Wingo <wingo at pobox dot com> Allpass delay line, cubic spline interpolation Steve Harris <steve@plugin.org.uk> AM pitchshifter Steve Harris <steve@plugin.org.uk> Simple amplifier Steve Harris <steve@plugin.org.uk> Analogue Oscillator Alexander Ehlert <mag@glame.de> Glame Bandpass Analog Filter Alexander Ehlert <mag@glame.de> Glame Bandpass Filter Steve Harris <steve@plugin.org.uk> Bode frequency shifter Steve Harris <steve@plugin.org.uk> Bode frequency shifter (CV) Alexander Ehlert <mag@glame.de> Glame Butterworth X-over Filter Alexander Ehlert <mag@glame.de> GLAME Butterworth Lowpass Alexander Ehlert <mag@glame.de> GLAME Butterworth Highpass Steve Harris <steve@plugin.org.uk> Chebyshev distortion Steve Harris <steve@plugin.org.uk> Comb Filter Andy Wingo <wingo at pobox dot com> Comb delay line, noninterpolating Andy Wingo <wingo at pobox dot com> Comb delay line, linear interpolation Andy Wingo <wingo at pobox dot com> Comb delay line, cubic spline interpolation Steve Harris <steve@plugin.org.uk> Comb Splitter Steve Harris <steve@plugin.org.uk> Constant Signal Generator Steve Harris <steve@plugin.org.uk> Crossover distortion Steve Harris <steve@plugin.org.uk> DC Offset Remover Steve Harris <steve@plugin.org.uk> Debug Plugin Andy Wingo <wingo at pobox dot com> Exponential signal decay Steve Harris <steve@plugin.org.uk> Decimator Steve Harris <steve@plugin.org.uk> Declipper Andy Wingo <wingo at pobox dot com> Simple delay line, noninterpolating Andy Wingo <wingo at pobox dot com> Simple delay line, linear interpolation Andy Wingo <wingo at pobox dot com> Simple delay line, cubic spline interpolation Steve Harris <steve@plugin.org.uk> Delayorama Steve Harris <steve@plugin.org.uk> Diode Processor Steve Harris <steve@plugin.org.uk> Audio Divider (Suboctave Generator) Steve Harris <steve@plugin.org.uk> DJ EQ (mono) Steve Harris <steve@plugin.org.uk> DJ EQ Steve Harris <steve@plugin.org.uk> DJ flanger Steve Harris <steve@plugin.org.uk> Dyson compressor Steve Harris <steve@plugin.org.uk> Fractionally Addressed Delay Line Steve Harris <steve@plugin.org.uk> Fast Lookahead limiter Steve Harris <steve@plugin.org.uk> Flanger Steve Harris <steve@plugin.org.uk> FM Oscillator Steve Harris <steve@plugin.org.uk> Foldover distortion Steve Harris <steve@plugin.org.uk> Fast overdrive Steve Harris <steve@plugin.org.uk> Frequency tracker Steve Harris <steve@plugin.org.uk> Gate Steve Harris <steve@plugin.org.uk> Gate Steve Harris <steve@plugin.org.uk> Stereo Gate Steve Harris <steve@plugin.org.uk> Giant flange Steve Harris <steve@plugin.org.uk> Gong model Steve Harris <steve@plugin.org.uk> Gong beater Steve Harris <steve@plugin.org.uk> GSM simulator Juhana Sadeharju <kouhia at nic.funet.fi>, LADSPAification by Steve Harris <steve@plugin.org.uk> GVerb Marcus Andersson Hard Limiter Steve Harris <steve@plugin.org.uk> Harmonic generator Steve Harris <steve@plugin.org.uk> Hermes Filter Alexander Ehlert <mag@glame.de> Glame Highpass Filter Steve Harris <steve@plugin.org.uk> Hilbert transformer Steve Harris <steve@plugin.org.uk> Impulse convolver Andy Wingo <wingo at pobox dot com> Nonbandlimited single-sample impulses (Frequency: Control) Steve Harris <steve@plugin.org.uk> Inverter Steve Harris <steve@plugin.org.uk> Karaoke Steve Harris <steve@plugin.org.uk> Artificial latency Steve Harris <steve@plugin.org.uk> L/C/R Delay Steve Harris <steve@plugin.org.uk> Lookahead limiter Steve Harris <steve@plugin.org.uk> Lookahead limiter (fixed latency) Alexander Ehlert <mag@glame.de> Glame Lowpass Filter Steve Harris <steve@plugin.org.uk> LS Filter Steve Harris <steve@plugin.org.uk> Matrix: MS to Stereo Joern Nettingsmeier <nettings@folkwang-hochschule.de> Matrix Spatialiser Steve Harris <steve@plugin.org.uk> Matrix: Stereo to MS Steve Harris <steve@plugin.org.uk> Multiband EQ Steve Harris <steve@plugin.org.uk> Modulatable delay Steve Harris <steve@plugin.org.uk> Multivoice Chorus Alexander Ehlert <mag@glame.de> Mag's Notch Filter Steve Harris <steve@plugin.org.uk> LFO Phaser Steve Harris <steve@plugin.org.uk> 4 x 4 pole allpass Steve Harris <steve@plugin.org.uk> Auto phaser Steve Harris <steve@plugin.org.uk> Pitch Scaler Steve Harris <steve@plugin.org.uk> Higher Quality Pitch Scaler Steve Harris <steve@plugin.org.uk> Plate reverb Steve Harris <steve@plugin.org.uk> Pointer cast distortion Steve Harris <steve@plugin.org.uk> Rate shifter Steve Harris <steve@plugin.org.uk> Retro Flanger Jesse Chappell <jesse at essej dot net> Reverse Delay (5s max) Steve Harris <steve@plugin.org.uk> Ringmod with two inputs Steve Harris <steve@plugin.org.uk> Ringmod with LFO Steve Harris <steve@plugin.org.uk> Barry's Satan Maximiser Steve Harris <steve@plugin.org.uk> SC1 Steve Harris <steve@plugin.org.uk> SC2 Steve Harris <steve@plugin.org.uk> SC3 Steve Harris <steve@plugin.org.uk> SC4 Steve Harris <steve@plugin.org.uk> SC4 mono Steve Harris <steve@plugin.org.uk> SE4 Steve Harris <steve@plugin.org.uk> Wave shaper Steve Harris <steve@plugin.org.uk> Signal sifter Steve Harris <steve@plugin.org.uk> Sine + cosine oscillator Steve Harris <steve@plugin.org.uk> Single band parametric Steve Harris <steve@plugin.org.uk> Sinus wavewrapper Steve Harris <steve@plugin.org.uk> Smooth Decimator Frank Neumann <franky@users.sourceforge.net> Mono to Stereo splitter Steve Harris <steve@plugin.org.uk> Step Demuxer Steve Harris <steve@plugin.org.uk> Surround matrix encoder Steve Harris <steve@plugin.org.uk> State Variable Filter Steve Harris <steve@plugin.org.uk> Tape Delay Simulation Steve Harris <steve@plugin.org.uk> Transient mangler Steve Harris <steve@plugin.org.uk> Triple band parametric with shelves Steve Harris <steve@plugin.org.uk> Valve saturation Steve Harris <steve@plugin.org.uk> Valve rectifier Achim Settelmeier <settel-linux@sirlab.de> (adapted by Josh Green and Hexasoft) Vocoder Steve Harris <steve@plugin.org.uk> VyNil (Vinyl Effect) Steve Harris <steve@plugin.org.uk> Wave Terrain Oscillator Steve Harris <steve@plugin.org.uk> Crossfade Steve Harris <steve@plugin.org.uk> Crossfade (4 outs) Steve Harris <steve@plugin.org.uk> z-1 muse-4.0.0/src/share/rdf/swh-scales.rdf000066400000000000000000000214151404105311000176660ustar00rootroot00000000000000 ]> muse-4.0.0/src/share/scoreglyphs/000077500000000000000000000000001404105311000167045ustar00rootroot00000000000000muse-4.0.0/src/share/scoreglyphs/0.png000066400000000000000000000005731404105311000175560ustar00rootroot00000000000000PNG  IHDR .IsRGBbKGD pHYs  tIME"-ǡ<tEXtCommentCreated with GIMPWIDAT(ϕMA gO $J p ' N@Ei-,Qv\pj)eY;)+ QAo`-n>y7|I斘|_ 9|c:<+>Ӯp=RJVO}I )#FIENDB`muse-4.0.0/src/share/scoreglyphs/1.png000066400000000000000000000005411404105311000175520ustar00rootroot00000000000000PNG  IHDR .IsRGBbKGD pHYs  tIME"ItEXtCommentCreated with GIMPWIDAT(Ͻ1JAg-"1+ r K"u` 9م5jWya`ٙM )!zؠ׈XVr,ѫNhvXCS~<3%MM>}s[pҾ6%Mơڊjq w ׿ 7Q;X x?.߄8! A IENDB`muse-4.0.0/src/share/scoreglyphs/2.png000066400000000000000000000006631404105311000175600ustar00rootroot00000000000000PNG  IHDR .IsRGBbKGD pHYs  tIME"r tEXtCommentCreated with GIMPWIDAT(m[.QNc /A$ bB?Ļ2u~;ΩUkZ;2Ӡ"bKE&35+`D;4"1j"6}k]=ƫ_f%Dcs kˀ\afގ tj}|X:2Ҁk$w Cʴ`_<`#xxN#^Fl߈Fp??qfpӐ2grOu/XV)>t0.*ӐJIENDB`muse-4.0.0/src/share/scoreglyphs/3.png000066400000000000000000000006761404105311000175650ustar00rootroot00000000000000PNG  IHDR .IsRGBbKGD pHYs  tIME" EtEXtCommentCreated with GIMPWIDAT(mұNA\9Р!1< -% %H c)Cs! OL7;;;;]0..pkG2&%"k x?=ZKeA%]e,w$E]i;c7V5 u 8*x5t9[ExR9Qۯ (];[J1xXiz#"V/z*LyKgNIfh!N꤭/vi  ]VIPB 0f|oIENDB`muse-4.0.0/src/share/scoreglyphs/4.png000066400000000000000000000006401404105311000175550ustar00rootroot00000000000000PNG  IHDR .IsRGBbKGD pHYs  tIME" tEXtCommentCreated with GIMPWIDAT(υ=JDQ /ScikP[p 65.B܁vV""n@k}.InNH)Ɉ.]6԰T=%(ޯAIhV kة,a7:z8'7z78t ǩs:MGBqAr]ob-aHzMW /xC GN0#댈.=1km4XJd'1IENDB`muse-4.0.0/src/share/scoreglyphs/5.png000066400000000000000000000006571404105311000175660ustar00rootroot00000000000000PNG  IHDR .IsRGBbKGD pHYs  tIME"tEXtCommentCreated with GIMPW IDAT(υ=/Ag. B!ZRO+?($*R!JHVPj0} 71$9gfϙLI2e&" 61Tva+y}\fS]1놽.$>3?Q4X,  3:U 1_8\grE;35J=wSx,g@+"}տ2y\Z?cP8 -⽀wЮXpUvaZ*"iy~4tVDD cQ~.Gocz4IENDB`muse-4.0.0/src/share/scoreglyphs/6.png000066400000000000000000000006341404105311000175620ustar00rootroot00000000000000PNG  IHDR .IsRGBbKGD pHYs  tIME"tetEXtCommentCreated with GIMPWIDAT(ύIAgDCp5<g"(xW0/^ă"&`N{j^ޫsVc{Wx oFFhq!^6񎌯Bh4X #rX* %ůB-uDc>sh(KNe-ghH8EXSVq49 3.^⮿;{)&l.%lŊ<8(P_V m7MIENDB`muse-4.0.0/src/share/scoreglyphs/7.png000066400000000000000000000006341404105311000175630ustar00rootroot00000000000000PNG  IHDR .IsRGBbKGD pHYs  tIME"tEXtCommentCreated with GIMPWIDAT(υѱ.DanZ [(6"щBSB[hT:I<JdV a˟I&3sIkV8AETbzD*α|de-a'h5)Ʊ4k[t#%WXL ;!0C8ynXAD^ ^4f1n{u{C,1l""𰌯U'jG{uPFIENDB`muse-4.0.0/src/share/scoreglyphs/CMakeLists.txt000066400000000000000000000021171404105311000214450ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB scoreglyph_files *.png) install( FILES ${scoreglyph_files} DESTINATION ${MusE_SHARE_DIR}/scoreglyphs ) muse-4.0.0/src/share/scoreglyphs/LICENSE000066400000000000000000000450641404105311000177220ustar00rootroot00000000000000These images can be redistributed and/or modified under the terms of the GNU General Public License Version 2 as published by the Free Software Foundation. These images 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. If not, see . CREDITS: these images were taken from lilypond's feta font on 28. March 2011. they're licensed under the GPL2, as is the feta font (and the whole lilypond project they belong to), see ./feta-original/COPYING-lilypond the relevant original files from the feta font can be found in ./feta-original/ a copy of the GPL2, which applies to these images, is attached below: GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. muse-4.0.0/src/share/scoreglyphs/acc_b.png000066400000000000000000000005331404105311000204420ustar00rootroot00000000000000PNG  IHDR osRGBbKGD pHYs  tIME#utEXtCommentCreated with GIMPWIDAT(1jBQ/Rw J-܍A4هBP0 F5f\p5nxXc7[7k _e6mYIENDB`muse-4.0.0/src/share/scoreglyphs/acc_sharp.png000066400000000000000000000006231404105311000213360ustar00rootroot00000000000000PNG  IHDR`sRGBbKGD pHYs  tIME )2tEXtCommentCreated with GIMPWIDAT(ϕҿ.DA\JGK%Sx:QDDJ )6Q)l!*D)nnJ̜9e'" 'La%kƂ/V%AmRJh}߈\! 8T O0B/6+導 #s*L6rX@Rk)3, [X8pվ[v|f]l="SJuUN_?G9lt?8" IENDB`muse-4.0.0/src/share/scoreglyphs/clef_bass_big.png000066400000000000000000000011551404105311000221560ustar00rootroot00000000000000PNG  IHDR,>ksRGBbKGD pHYs  tIME tEXtCommentCreated with GIMPWIDATH=kQ, n"$⊊`c3lV[DQ6 J $XH j(2{-<.q&6ng{Μ6V߫H)-C+Rz(\Y"c[1;)׵Ĕ4^al ŏ{V@/CFM@>@4i ;q2v]):a 8 M|L~\,4tBr*` ?jھyW7Ňtq-»&ܮ|v&u*s8DcsP>f-O~7R3uqGfϗOW3i؋\-e՝׵W`{`nfɪ إh^y+` Vekc <G1U5WV3~<u7sp,4ޯ^㎞K)g7NLHIENDB`muse-4.0.0/src/share/scoreglyphs/clef_violin_big.png000066400000000000000000000021641404105311000225270ustar00rootroot00000000000000PNG  IHDRLsRGBbKGD pHYs  tIME 5:1tEXtCommentCreated with GIMPWIDATXMVU̼S \5H>bjHIAIHb%Hhf&Ң,ʘL\L0㌂1t} ڽ{><sw3kl٘F 4f`w/OKǷ8/0},e1b2m;` +1M75 ^wB'n`x`u7w? .4eyeY'޵ucӁ9b̮[4ו|YS%A@l()`Aޜf:->Y/ Es% )GMe[7X*S2 Z|~)^!p904Ь& @C\]lI!`@fU4,} lU[0K 9rDyCiVVO+&6`Q(X~zx . o 'X,+0dqi[fsx8n`5A6- [cg6.I)ҊLCcٽU ;OrQi{ أU4`xHƁE.3W~x 8 9YX mB%SLl8D* } pH;ӠCl1{: u_!; YƲ玙(_B)<\\jFf6jflkۇWQFW:$pGl1J:Fw C"(jv6[.<._nE: \. 77 pef럫zhxxSuT#YW8fDA:S:S:S7sfiIENDB`muse-4.0.0/src/share/scoreglyphs/dot.png000066400000000000000000000003321404105311000201760ustar00rootroot00000000000000PNG  IHDR~sRGBbKGD pHYs  tIME!xtEXtCommentCreated with GIMPW5IDAT%1 &wRw0 2X0n * IENDB`muse-4.0.0/src/share/scoreglyphs/feta-original/000077500000000000000000000000001404105311000214255ustar00rootroot00000000000000muse-4.0.0/src/share/scoreglyphs/feta-original/COPYING-lilypond000066400000000000000000000447111404105311000243170ustar00rootroot00000000000000If you want to redistribute LilyPond, you must comply with the GNU General Public License (reproduced below). This license applies to LilyPond with the following exceptions: - It does not apply to example input files (which are in the subdirectory input/) that explicitly specify another license. - If you create a document which uses fonts included in LilyPond, and embed this font or unaltered portions of this font into the document, then this font does not by itself cause the resulting document to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the document might be covered by the GNU General Public License. If you modify this font, you may extend this exception to your version of the font, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. muse-4.0.0/src/share/scoreglyphs/feta-original/GNUmakefile000066400000000000000000000133551404105311000235060ustar00rootroot00000000000000# mf/GNUmakefile depth = .. STEPMAKE_TEMPLATES = metafont \ install \ install-out LOCALSTEPMAKE_TEMPLATES = lilypond include $(depth)/make/stepmake.make EXTRA_DIST_FILES += README mf2pt1.mp # We don't use $(MF_FILES), because there's more .mf cruft here FETA_MF_FILES = $(call src-wildcard,feta[0-9]*.mf) \ $(call src-wildcard,feta-braces-[a-z].mf) \ $(call src-wildcard,feta-alphabet*[0-9].mf) \ $(call src-wildcard,parmesan[0-9]*.mf) STAFF_SIZES = 11 13 14 16 18 20 23 26 BRACES = a b c d e f g h i OTF_FILES = $(STAFF_SIZES:%=$(outdir)/emmentaler-%.otf) \ $(outdir)/aybabtu.otf PE_SCRIPTS = $(STAFF_SIZES:%=$(outdir)/emmentaler-%.pe) OTF_TABLES = $(STAFF_SIZES:%=$(outdir)/feta%.otf-table) \ $(BRACES:%=$(outdir)/feta-braces-%.otf-table) FETA_FONTS = $(FETA_MF_FILES:.mf=) SVG_FILES = $(OTF_FILES:%.otf=%.svg) FC_FIND = $(shell $(FCMATCH) --verbose 'Century Schoolbook L:style=$(1)' \ | grep 'file:' \ | sed 's/.*"\([^"]*\)".*/\1/g') NCSB_OTFS = $(addprefix $(outdir)/,CenturySchL-Ital.otf \ CenturySchL-BoldItal.otf \ CenturySchL-Roma.otf \ CenturySchL-Bold.otf) NCSB_INSTALL_DIR = $(local_lilypond_datadir)/fonts/otf LOG_FILES = $(FETA_MF_FILES:%.mf=$(outdir)/%.log) LISP_FILES = $(FETA_MF_FILES:%.mf=$(outdir)/%.lisp) ENC_FILES = $(FETA_MF_FILES:%.mf=$(outdir)/%.enc) TFM_FILES = $(FETA_MF_FILES:%.mf=$(outdir)/%.tfm) $(outdir)/aybabtu.otf-table: $(BRACES:%=$(outdir)/feta-braces-%.otf-table) cat $^ > $@ $(outdir)/aybabtu.otf-gtable: $(BRACES:%=$(outdir)/feta-braces-%.otf-gtable) echo '(design_size . 20)' > $@ # only for fonts which # # 1. are mentioned in font.scm # # 2. are not included with teTeX # $(outdir)/emmentaler-%.otf \ $(outdir)/emmentaler-%.svg: $(outdir)/emmentaler-%.pe \ $(outdir)/feta%.pfb \ $(outdir)/feta-alphabet%.pfb \ $(outdir)/parmesan%.pfb \ $(outdir)/feta%.otf-table \ $(outdir)/feta%.otf-gtable cd $(outdir) && $(FONTFORGE) -script $(notdir $(basename ,$@).pe) $(outdir)/aybabtu.otf \ $(outdir)/aybabtu.svg: $(outdir)/aybabtu.pe \ $(foreach s,$(BRACES),$(outdir)/feta-braces-$(s).pfb) \ $(outdir)/aybabtu.otf-table $(outdir)/aybabtu.otf-gtable cd $(outdir) && $(FONTFORGE) -script aybabtu.pe $(outdir)/%.pfb: $(outdir)/%.log $(outdir)/%.otf-table: $(outdir)/%.lisp cat $< $(if $(findstring brace,$<),,$(subst feta,parmesan,$<)) \ $(if $(findstring brace,$<),,$(subst feta,feta-alphabet,$<)) > $@ ## ugh -- we want this to prevent failing -j2 compiles. $(outdir)/feta26.otf-table: $(outdir)/feta26.lisp \ $(outdir)/parmesan26.lisp \ $(outdir)/feta-alphabet26.lisp $(outdir)/feta23.otf-table: $(outdir)/feta23.lisp \ $(outdir)/parmesan23.lisp \ $(outdir)/feta-alphabet23.lisp $(outdir)/feta20.otf-table: $(outdir)/feta20.lisp \ $(outdir)/parmesan20.lisp \ $(outdir)/feta-alphabet20.lisp $(outdir)/feta18.otf-table: $(outdir)/feta18.lisp \ $(outdir)/parmesan18.lisp \ $(outdir)/feta-alphabet18.lisp $(outdir)/feta16.otf-table: $(outdir)/feta16.lisp \ $(outdir)/parmesan16.lisp \ $(outdir)/feta-alphabet16.lisp $(outdir)/feta14.otf-table: $(outdir)/feta14.lisp \ $(outdir)/parmesan14.lisp \ $(outdir)/feta-alphabet14.lisp $(outdir)/feta13.otf-table: $(outdir)/feta13.lisp \ $(outdir)/parmesan13.lisp \ $(outdir)/feta-alphabet13.lisp $(outdir)/feta11.otf-table: $(outdir)/feta11.lisp \ $(outdir)/parmesan11.lisp \ $(outdir)/feta-alphabet11.lisp $(outdir)/aybabtu.otf: $(outdir)/aybabtu.subfonts \ $(outdir)/aybabtu.fontname \ $(outdir)/aybabtu.otf-table \ $(outdir)/aybabtu.otf-gtable \ $(outdir)/aybabtu.pe $(outdir)/aybabtu.otf \ $(outdir)/aybabtu.svg: $(BRACES:%=$(outdir)/feta-braces-%.pfb) $(outdir)/aybabtu.fontname: echo -n 'aybabtu' > $@ $(outdir)/aybabtu.subfonts: echo $(subst .mf,,$(call src-wildcard,feta-braces-[a-z].mf)) > $@ $(PE_SCRIPTS): $(buildscript-dir)/gen-emmentaler-scripts $< --dir=$(outdir) ALL_FONTS = $(FETA_FONTS) PFB_FILES = $(ALL_FONTS:%=$(outdir)/%.pfb) # Make tfm files first, log files last, # so that normally log files aren't made twice ALL_GEN_FILES = $(LOG_FILES) \ $(ENC_FILES) \ $(LISP_FILES) \ $(OTF_TABLES) \ $(NCSB_OTFS) \ $(PFB_FILES) \ $(OTF_FILES) \ $(SVG_FILES) # PRE_INSTALL=$(MAKE) "$(ALL_GEN_FILES)" INSTALLATION_DIR = $(local_lilypond_datadir)/fonts/source INSTALLATION_FILES = $(MF_FILES) INSTALLATION_OUT_SUFFIXES = 1 2 3 INSTALLATION_OUT_DIR1 = $(local_lilypond_datadir)/fonts/otf INSTALLATION_OUT_FILES1 = $(OTF_FILES) \ $(NCSB_OTFS) INSTALLATION_OUT_DIR2 = $(local_lilypond_datadir)/fonts/svg INSTALLATION_OUT_FILES2 = $(SVG_FILES) INSTALLATION_OUT_DIR3 = $(local_lilypond_datadir)/fonts/type1 INSTALLATION_OUT_FILES3 = $(PFB_FILES) export MFINPUTS := .:$(MFINPUTS) default: $(ALL_GEN_FILES) \ $(outdir)/emmentaler-20.otf \ tree-regen \ $(outdir)/fonts.conf .PHONY: tree-regen # FIXME: temporary hack: must regenerate after building fonts tree-regen: $(ALL_GEN_FILES) ${MAKE} -C $(top-build-dir) link-mf-tree ## ## todo: this also depends on .tfm, FIXME. $(outdir)/%.lisp \ $(outdir)/%.otf-gtable \ $(outdir)/%.enc \ $(outdir)/%.pe: $(outdir)/%.log $(buildscript-dir)/mf-to-table \ --global-lisp=$(outdir)/$('$(shell cd $(outdir); pwd)'' > $@ $(NCSB_OTFS): $(NCSB_SOURCE_FILES) \ $(auxscript-dir)/pfx2ttf.fontforge $(foreach i, $(basename $(NCSB_SOURCE_FILES)), \ $(FONTFORGE) -script $(auxscript-dir)/pfx2ttf.fontforge \ $(i).pfb $(i).afm $(outdir)/ && ) true # eof muse-4.0.0/src/share/scoreglyphs/feta-original/README000066400000000000000000000130501404105311000223040ustar00rootroot00000000000000 This is a font of music symbols. All MF sources are original. Most of the documentation is in comments in the MF code. Non-square pixels are not supported; with other words, the horizontal and vertical resolution of the output device must be the same. Currently, outline fonts are created by using `autotrace', but we are already in the process of converting the MF code directly to PostScript code with a tool called `mf2pt1', which in turn calls `FontForge' to postprocess the output (mainly to remove outline overlaps and to add hints). The recommended calling sequence of mf2pt1 is mf2pt1 --rounding=0.0001 You need mf2pt1 version 2.1 or newer. Here some guidelines to assure a clean conversion. . Never use `---'. Replace it with `--' together with explicit path directions (if necessary). . Don't use self-intersecting outlines in general since they can confuse mf2pt1's algorithm to get the orientation of a closed path. Note that MetaPost's implementation of the `turningnumber' primitive (which would immediately give the orientation of a path) is severely broken before version 1.0 of MetaPost, thus some hand-made code in mf2pt1.mp is used to work around this bug. . If outlines intersect, avoid grazing intersections. In case two outlines intersect in an explicitly defined point, include this point in both intersecting paths to avoid problems due to rounding errors. . Don't use `draw' with a polygonal pen but for straight lines (consisting of exactly two points). In most cases it is quite easy to emulate `draw' with an explicit definition of the outline or with `penstroke'. . Don't apply transformations after calling `fill' -- for example, don't mirror `currentpicture'. Instead, transform the path and call `fill' afterwards. This ensures that mf2pt1 gets the correct outline directions which is a necessary prerequisite for FontForge's algorithm to remove overlaps. Some glyph name rules: . Most glyph names have the form ., where is defined with the `fet_begingroup' command, and is given with `fet_beginchar' (within a `fet_begingroup' block). Example: `clefs.vaticana.fa'. . Sometimes it would be sensible to use negative numbers in glyph names. However, the `-' character shouldn't be used in a glyph name. Replace it with `M'. For example, write `rests.M3mensural' instead of `rests.-3mensural'. . Glyphs which exist in both an `up' and `down' version should start the part with either `u' or `d', respectively. Example: `flags.d3', `flags.u3'. Glyphs which are neutral w.r.t. the direction, and where members of the glyph group exist which have `up' and `down' versions, should start with an `s'. Example: `noteheads.s0re'. Some design rules: . Always use smooth curve transitions. Since this is difficult to see in MetaFont proof sheets (which don't show the tangents) I recommend to call mf2pt1 like this FONTFORGE=foo mf2pt1 ... (`foo' should be a non-existent program; this avoids the default postprocessing). Then call FontForge to inspect the outlines. . Use rounded corners. Hints for stem attachment: . Stem attachment of glyphs is controlled by two special variables called `charwx' and `charwy'. Stems can be regarded as (very oblonged) rectangles with slightly rounded corners. For stems pointing upwards the lower right corner of this rectangle is attached to the glyph at position (charwx, charwy). For stems pointing downwards it works analogously but with the upper left corner, where the position of the attachment point is additionally reflected horizontally about the center of the glyph -- this ensures that in most cases charwx and charwy can be set to the same values for up and down stems even though these are attached at the right/left end of the note, respectively. To make this more precise, the upper left corner of a down stem is attached at position (charwd/2 - charwx, charwy), where `charwd' is an internal metafont variable representing the glyph width as specified by the `set_char_box' command. . In case different stem attachments for upward and downward pointing stems are needed, two separate glyphs must be defined in the Metafont file; of course, this also applies if two entirely different shapes are needed. These have the same name but are prefixed by `u' and `d', respectively (for `up' and `down', obviously). In each of these glyphs the variables charwx and charwy must be set accordingly. If, on the other hand, the attachment point is the `same' for both directions (with the abovementioned horizontal reflection taken into account), then the prefix `s' (for `symmetric') should be used. See the existing files for examples. The numbers in the glyph names refer to the duration of the note; e.g., `s0cross' in feta-bolletjes.mf defines the notehead for a whole cross-shaped note (similarly, `s1cross' and `s2cross' are for half and quarter notes, respectively). Finally, some rules to assure that rasterization at low resolutions gives good results. Today, this is a minor issue, but in some cases it might show design flaws. . Use `define_whole_pixels' and friends where appropriate. . Use `hround' and `vround' consistently. A lot of auxiliary macros are defined in feta-macros.mf. . If a path element is duplicated or shifted, use an integer value for the offset. . Add `eps' for mirrored paths to assure correct MetaFont rasterization. See the comment and the variables at the end of `feta-params.mf' for details how vertical symmetry should be achieved. muse-4.0.0/src/share/scoreglyphs/feta-original/aybabtu.pe.in000066400000000000000000000043061404105311000240120ustar00rootroot00000000000000#!@FONTFORGE@ # all your brace are belong to us Open("feta-braces-b.pfb"); SelectAll(); Scale(200, 0, 0); Generate("feta-braces-b.scale.pfb"); Open("feta-braces-c.pfb"); SelectAll(); Scale(300, 0, 0); Generate("feta-braces-c.scale.pfb"); Open("feta-braces-d.pfb"); SelectAll(); Scale(400, 0, 0); Generate("feta-braces-d.scale.pfb"); Open("feta-braces-e.pfb"); SelectAll(); Scale(500, 0, 0); Generate("feta-braces-e.scale.pfb"); Open("feta-braces-f.pfb"); SelectAll(); Scale(600, 0, 0); Generate("feta-braces-f.scale.pfb"); Open("feta-braces-g.pfb"); SelectAll(); Scale(700, 0, 0); Generate("feta-braces-g.scale.pfb"); Open("feta-braces-h.pfb"); SelectAll(); Scale(800, 0, 0); Generate("feta-braces-h.scale.pfb"); Open("feta-braces-i.pfb"); SelectAll(); Scale(900, 0, 0); Generate("feta-braces-i.scale.pfb"); New() MergeFonts("feta-braces-a.pfb"); MergeFonts("feta-braces-b.scale.pfb"); MergeFonts("feta-braces-c.scale.pfb"); MergeFonts("feta-braces-d.scale.pfb"); MergeFonts("feta-braces-e.scale.pfb"); MergeFonts("feta-braces-f.scale.pfb"); MergeFonts("feta-braces-g.scale.pfb"); MergeFonts("feta-braces-h.scale.pfb"); MergeFonts("feta-braces-i.scale.pfb"); SetFontNames("Aybabtu-Regular", "Aybabtu", "Aybabtu Regular", "Regular", "GNU GPL", "@TOPLEVEL_VERSION@"); i = 0; while (i < CharCnt()) Select(i); # crashes fontforge, use PUA for now -- jcn # SetUnicodeValue(i + 0xF0000, 0); /* PRIVATE AREA In the BMP, the range 0xe000 to 0xf8ff will never be assigned to any characters by the standard and is reserved for private usage. For the Linux community, this private area has been subdivided further into the range 0xe000 to 0xefff which can be used individually by any end-user and the Linux zone in the range 0xf000 to 0xf8ff where extensions are coordinated among all Linux users. The registry of the characters assigned to the Linux zone is currently maintained by H. Peter Anvin . */ SetUnicodeValue(i + 0xE000, 0); ++i; endloop LoadTableFromFile("LILF", "aybabtu.subfonts") LoadTableFromFile("LILC", "aybabtu.otf-table") LoadTableFromFile("LILY", "aybabtu.otf-gtable") Generate("aybabtu.otf"); Generate("aybabtu.svg"); muse-4.0.0/src/share/scoreglyphs/feta-original/bigcheese.pe.in000066400000000000000000000025201404105311000242750ustar00rootroot00000000000000#!@FONTFORGE@ New(); # Separate Feta versioning? # Naming: * expose LilyPond20/LilyPond rather than bigcheese # * using 20 as Weight works for gnome-font-select widget: gfs SetFontNames("bigcheese20", "LilyPond", "LilyPond BigCheese 20", "20", "GNU GPL", "@TOPLEVEL_VERSION@"); MergeFonts("feta20.pfa"); MergeFonts("parmesan20.pfa"); # load nummer/din after setting PUA. i = 0; while (i < CharCnt()) Select(i); # crashes fontforge, use PUA for now -- jcn # SetUnicodeValue(i + 0xF0000, 0); /* PRIVATE AREA In the BMP, the range 0xe000 to 0xf8ff will never be assigned to any characters by the standard and is reserved for private usage. For the Linux community, this private area has been subdivided further into the range 0xe000 to 0xefff which can be used individually by any end-user and the Linux zone in the range 0xf000 to 0xf8ff where extensions are coordinated among all Linux users. The registry of the characters assigned to the Linux zone is currently maintained by H. Peter Anvin . */ SetUnicodeValue(i + 0xE000, 0); ++i; endloop MergeFonts("feta-nummer10.pfa"); MergeFonts("feta-din14.pfa"); MergeKern("feta-din14.tfm"); LoadTableFromFile("LILC", "feta20.otf-table") Generate("bigcheese20.otf"); Generate("bigcheese20.cff"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-accordion.mf000066400000000000000000000211161404105311000246300ustar00rootroot00000000000000% % feta-accordion.mf -- draw accordion symbols % % source file of the GNU LilyPond music typesetter % % (c) 1998--2009 Han-Wen Nienhuys fet_begingroup ("accordion"); % % These dimensions are the same for all register symbols. % The different symbols should calculate their other dimensions from them. % accreg_dot_size# := .5 staff_space#; accreg_linethickness# := 1.3 stafflinethickness#; accreg_lh# := 1.0 staff_space#; define_pixels (accreg_dot_size, accreg_linethickness, accreg_lh); fet_beginchar ("accDiscant", "accDiscant") save r, pat, lh, lt; path pat; r# = 3/2 accreg_lh#; define_pixels (r); set_char_box (r# + accreg_linethickness# / 2, r# + accreg_linethickness# / 2, 0, 2 r# + 0.7 accreg_linethickness#); lh = vround (2/3 r); lt = vround (0.7 accreg_linethickness); h := 3 lh + lt; b := w := (3 lh + hround accreg_linethickness) / 2; penpos1 (hround accreg_linethickness, 0); penpos2 (lt, 90); penpos3 (hround accreg_linethickness, 180); penpos4 (lt, 270); z1r = (w, h / 2); z2r = (0, h); z3r = (-b, h / 2); z4r = (0, 0); penlabels (1, 2, 3, 4); % mf doesn't handle pixel dropouts in outline objects, so we use % `draw' if not called by mpost if known miterlimit: fill z1r .. z2r .. z3r .. z4r .. cycle; unfill z1l .. z2l .. z3l .. z4l .. cycle; else: pickup pencircle xscaled accreg_linethickness yscaled lt; draw z1 .. z2 .. z3 .. z4 .. cycle; fi; pat := z4{right} .. z1{up} .. {left}z2; pickup penrazor scaled lt rotated 90; top z5 = pat intersectionpoint ((0, lh + lt) -- (w, lh + lt)); z6 = z5 xscaled -1; bot z7 = pat intersectionpoint ((0, 2 lh) -- (w, 2 lh)); z8 = z7 xscaled -1; labels (5, 6, 7, 8); draw z5 -- z6; draw z7 -- z8; fet_endchar; fet_beginchar ("accDot", "accDot") set_char_box (accreg_dot_size# / 2, accreg_dot_size# / 2, accreg_dot_size# / 2, accreg_dot_size# / 2); pickup pencircle scaled accreg_dot_size; rt x0 = hround (accreg_dot_size / 2); top y0 = vround (accreg_dot_size / 2); drawdot z0; fet_endchar; fet_beginchar ("accFreebase", "accFreebase") save r, lh, lt; r# = accreg_lh#; define_pixels (r); set_char_box (r# + accreg_linethickness# / 2, r# + accreg_linethickness# / 2, 0, 2 r# + 0.7 accreg_linethickness#); lh = vround r; lt = vround (0.7 accreg_linethickness); h := 2 lh + lt; b := w := (2 lh + hround accreg_linethickness) / 2; penpos1 (hround accreg_linethickness, 0); penpos2 (lt, 90); penpos3 (accreg_linethickness, 180); penpos4 (lt, 270); z1r = (w, h / 2); z2r = (0, h); z3r = (-b, h / 2); z4r = (0, 0); penlabels (1, 2, 3, 4); % mf doesn't handle pixel dropouts in outline objects, so we use % `draw' if not called by mpost if known miterlimit: fill z1r .. z2r .. z3r .. z4r .. cycle; unfill z1l .. z2l .. z3l .. z4l .. cycle; else: pickup pencircle xscaled accreg_linethickness yscaled lt; draw z1 .. z2 .. z3 .. z4 .. cycle; fi; pickup penrazor scaled lt rotated 90; draw z1 -- z3; fet_endchar; fet_beginchar ("accStdbase", "accStdbase") save r, p, lh, lt; path pat; r# = 2 accreg_lh#; define_pixels (r); set_char_box (r# + accreg_linethickness# / 2, r# + accreg_linethickness# / 2, 0, 2 r# + 0.7 accreg_linethickness#); lh = vround (1/2 r); lt = vround (0.7 accreg_linethickness); h := 4 lh + lt; b := w := (4 lh + hround accreg_linethickness) / 2; penpos1 (hround accreg_linethickness, 0); penpos2 (lt, 90); penpos3 (hround accreg_linethickness, 180); penpos4 (lt, 270); z1r = (w, h / 2); z2r = (0, h); z3r = (-b, h / 2); z4r = (0, 0); penlabels (1, 2, 3, 4); % mf doesn't handle pixel dropouts in outline objects, so we use % `draw' if not called by mpost if known miterlimit: fill z1r .. z2r .. z3r .. z4r .. cycle; unfill z1l .. z2l .. z3l .. z4l .. cycle; else: pickup pencircle xscaled accreg_linethickness yscaled lt; draw z1 .. z2 .. z3 .. z4 .. cycle; fi; pat := z4{right} .. z1{up} .. {left}z2; pickup penrazor scaled lt rotated 90; top z5 = pat intersectionpoint ((0, lh + lt) -- (w, lh + lt)); z6 = z5 xscaled -1; bot z7 = pat intersectionpoint ((0, 3 lh) -- (w, 3 lh)); z8 = z7 xscaled -1; labels (5, 6, 7, 8); draw z1 -- z3; draw z5 -- z6; draw z7 -- z8; fet_endchar; fet_beginchar ("accBayanbase", "accBayanbase") save lh, lt; lh = vround accreg_lh; lt = vround accreg_linethickness; set_char_box (accreg_lh# + accreg_linethickness# / 2, accreg_lh# + accreg_linethickness# / 2, 0, 3 accreg_lh# + accreg_linethickness#); h := 3 lh + lt; draw_rounded_block ((-w, 0), (-w + lt, h), lt); draw_rounded_block ((w - lt, 0), (w, h), lt); pickup penrazor scaled lt rotated 90; bot z1 = (-w + lt / 2, 0); bot z2 = (-w + lt / 2, lh); bot z3 = (-w + lt / 2, 2 lh); bot z4 = (-w + lt / 2, 3 lh); bot z5 = (w - lt / 2, 0); bot z6 = (w - lt / 2, lh); bot z7 = (w - lt / 2, 2 lh); bot z8 = (w - lt / 2, 3 lh); draw z1 -- z5; draw z2 -- z6; draw z3 -- z7; draw z4 -- z8; fet_endchar; def def_B (expr w, h) = pickup pencircle scaled 0.15 linethickness; penpos10 (thin, -90); penpos11 (thin, -90); penpos12 (thick, 0); penpos13 (thin, 90); penpos14 (thin, 90); penpos15 (thick, 180); penpos16 (thin, -90); penpos17 (thin, -90); penpos18 (thick, 0); penpos19 (thick, 0); z10 = (0, 0); z11 = (cOne * w, 0); z12 = (w, .5 mb * h); z13 = (cTwo * w, mb * h); z14 = (2 thick, mb * h); z15 = (.94 w, h - .5 mt * h); z16 = z13 + (0, mt * h); z17 = (0, h); z18 = (1.5 thick, 0); z19 = (1.5 thick, h); enddef; def def_S (expr w, h) = pickup pencircle scaled 0.03 linethickness; penpos1 (thin, 180); penpos2 (thin, -90); penpos3 (thick, 0); penpos4 (.5 thick, 90); penpos5 (thick, 0); penpos6 (thin, -90); penpos7 (thin, 180); penpos8 (thin, 180); penpos9 (thin, 0); z1 = (0, hs); z2 = (w / 2, 0); z3 = (w - .5 thick, .5 mb * h); z4 = (w / 2, mb * h); z5 = (.5 thick, h - .5 mt * h); z6 = (w / 2, h); z7 = (w, h - hs); z8 = (0, y2r); z9 = (w, y6l); path bue, bueoverst; bue := z2{left} .. z1{up}; t := xpart (bue intersectiontimes (z8l -- z7l)); bueoverst := z6{right} .. z7{down}; enddef; def def_some_vars = save hs, mb, mt, thin, thick, height, width, cOne, cTwo; save bx, hx; width = .8 (4 staff_space); height = 2.4 staff_space; % URG. smaller sizes should be wider and fatter % thin = 0.05 staff_space; % thick = 0.2 staff_space; 4 hx + bx = 1.15; 10 hx + bx = 1; fatten := designsize * hx + bx * 1.2; thick := 0.2 staff_space * fatten; % urg: mustn't ever go thinner than blot! thin# := blot_diameter#; define_pixels (thin); hs = 0.4 staff_space; mb = .53; mt = .47; cOne = 0.65; cTwo = 0.60; enddef; fet_beginchar ("accOldEE", "accOldEE") save r, pp, ir, lh, lt, stroke_width; r# = staff_space#; define_pixels (r); lr = .4 staff_space - linethickness; ir = .6 staff_space; stroke_width = .05 staff_space + .5 linethickness; set_char_box (r# + accreg_linethickness# / 2, r# + accreg_linethickness# / 2, 0, 2 r# + 0.7 accreg_linethickness#); z1 = (0, 0); z2 = (0, ir); z3 = (0, -ir); penpos1 (blot_diameter, 0); penpos2 (stroke_width + blot_diameter, 0); penpos3 (stroke_width + blot_diameter, 0); pickup pencircle scaled (lr + blot_diameter); for pp := 0 step 45 until 135: drawdot z2 rotated pp; drawdot z3 rotated pp; penstroke (z2e -- z1e -- z3e) rotated pp; endfor; pickup pencircle scaled lr; drawdot (0, 0); currentpicture := currentpicture shifted (0, h / 2); lh = vround (2 r); lt = vround (0.7 accreg_linethickness); h := lh + lt; b := w := (lh + hround accreg_linethickness) / 2; penpos10 (hround accreg_linethickness, 0); penpos11 (lt, 90); penpos12 (hround accreg_linethickness, 180); penpos13 (lt, 270); z10r = (w, h / 2); z11r = (0, h); z12r = (-b, h / 2); z13r = (0, 0); % penlabels (1, 2, 10, 11, 12, 13); % mf doesn't handle pixel dropouts in outline objects, so we use % `draw' if not called by mpost if known miterlimit: fill z10r .. z11r .. z12r .. z13r .. cycle; unfill z10l .. z11l .. z12l .. z13l .. cycle; else: pickup pencircle xscaled accreg_linethickness yscaled lt; draw z10 .. z11 .. z12 .. z13 .. cycle; fi; fet_endchar; fet_endgroup ("accordion"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-alphabet.mf000066400000000000000000000005251404105311000244500ustar00rootroot00000000000000test := 0; staffsize# := design_size * pt#; input feta-autometric; input feta-macros; input feta-params; fet_beginfont ("feta-alphabet", design_size, "fetaNumber"); mode_setup; number_design_size := design_size / 2; dynamic_design_size := 14 design_size / 20; input feta-nummer-code; input feta-din-code; fet_endfont ("feta-nummer"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-alphabet11.mf000066400000000000000000000001421404105311000246050ustar00rootroot00000000000000% part of LilyPond's pretty-but-neat music font design_size := 11.22; input feta-alphabet; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-alphabet13.mf000066400000000000000000000001421404105311000246070ustar00rootroot00000000000000% part of LilyPond's pretty-but-neat music font design_size := 12.60; input feta-alphabet; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-alphabet14.mf000066400000000000000000000001421404105311000246100ustar00rootroot00000000000000% part of LilyPond's pretty-but-neat music font design_size := 14.14; input feta-alphabet; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-alphabet16.mf000066400000000000000000000001421404105311000246120ustar00rootroot00000000000000% part of LilyPond's pretty-but-neat music font design_size := 15.87; input feta-alphabet; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-alphabet18.mf000066400000000000000000000001421404105311000246140ustar00rootroot00000000000000% part of LilyPond's pretty-but-neat music font design_size := 17.82; input feta-alphabet; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-alphabet20.mf000066400000000000000000000001641404105311000246110ustar00rootroot00000000000000% feta-alphabet20.mf % part of LilyPond's pretty-but-neat music font design_size := 20; input feta-alphabet; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-alphabet23.mf000066400000000000000000000001671404105311000246170ustar00rootroot00000000000000% feta-alphabet23.mf % part of LilyPond's pretty-but-neat music font design_size := 22.45; input feta-alphabet; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-alphabet26.mf000066400000000000000000000001421404105311000246130ustar00rootroot00000000000000% part of LilyPond's pretty-but-neat music font design_size := 25.20; input feta-alphabet; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-arrow.mf000066400000000000000000000104551404105311000240250ustar00rootroot00000000000000% % feta-arrow.mf -- draw arrow heads % % source file of the GNU LilyPond music typesetter % % (c) 2005--2009 Han-Wen Nienhuys fet_begingroup ("arrowheads"); % % To consider: we could put arrow heads at their real Unicode locations. % % % Set up paths for upper half of arrow head pointing right. % CLOSE_PATH is for a closed arrow, OPEN_PATH is for the open arrow. % OPEN_TENSION sets the tension for the part which forms the path in % OPEN_PATH. % def set_arrow_paths (expr width, height, open_tension, arrowlinethickness) = save pat, finalpath; save tip_angle_o, tip_angle_c, tip_dir_c, tip_dir_o; save indent; path pat, open_path, close_path; pair tip_dir_o, tip_dir_c; pickup pencircle scaled arrowlinethickness; indent = linethickness / 2; lft x1 = -width; x2 = 0; top y1 = height; y2 = 0; z4 = z3; y5 = 0; x5 = x4 + indent; pat := z1 ..tension open_tension.. z2{right}; penpos2 (arrowlinethickness, 90); tip_dir_o := direction 0.0 of pat; tip_angle_o := angle (tip_dir_o); penpos1 (arrowlinethickness, 90 + tip_angle_o); z3 = z1; penpos3 (arrowlinethickness, tip_angle_o); open_path := z1r{tip_dir_o} .. z2r{right} .. z2l{left} .. z1l{-tip_dir_o} .. z3l .. cycle; pat := z4 .. z5{down}; tip_dir_c := direction 0.0 of pat; tip_angle_c := angle (tip_dir_c); penpos4 (arrowlinethickness, 90+ tip_angle_c); penpos5 (arrowlinethickness, 0); z6 = z5; penpos6 (arrowlinethickness, 90); close_path := z4l{tip_dir_c} .. z5l{down} .. z6l{right} .. z2l{right} .. z2r{left} ..tension open_tension.. z1r{-tip_dir_o} .. z3l .. cycle; enddef; fet_beginchar ("open", "open.01"); set_char_box (staff_space#, 1.6 linethickness# / 2, 0.5 staff_space#, 0.5 staff_space#); set_arrow_paths (staff_space, 0.5 staff_space, 1.0, 1.6 linethickness); fill open_path; open_path := open_path yscaled -1; fill open_path; penlabels (1, 2, 3); penlabels (4, 5, 6); fet_endchar; fet_beginchar ("open", "open.0M1"); set_char_box (1.6 linethickness# / 2, staff_space#, 0.5 staff_space#, 0.5 staff_space#); set_arrow_paths (staff_space, 0.5 staff_space, 1.0, 1.6 linethickness); fill open_path; open_path := open_path yscaled -1; fill open_path; currentpicture := currentpicture xscaled -1; fet_endchar; fet_beginchar ("open", "open.11"); set_char_box (.5 staff_space#, .5 staff_space#, 1.0 staff_space#, 1.6 linethickness# / 2); set_arrow_paths (staff_space, 0.5 staff_space, 1.0, 1.6 linethickness); fill open_path; open_path := open_path yscaled -1; fill open_path; currentpicture := currentpicture rotated 90; fet_endchar; fet_beginchar ("open", "open.1M1"); set_char_box (.5 staff_space#, .5 staff_space#, 1.6 linethickness# / 2, 1.0 staff_space#); set_arrow_paths (staff_space, 0.5 staff_space, 1.0, 1.6 linethickness); fill open_path; open_path := open_path yscaled -1; fill open_path; currentpicture := currentpicture rotated 90; currentpicture := currentpicture yscaled -1; fet_endchar; fet_beginchar ("close", "close.01"); set_char_box (staff_space#, 0, 0.5 staff_space#, 0.5 staff_space#); set_arrow_paths (staff_space, 0.5 staff_space, 1.5, blot_diameter); fill close_path; close_path := close_path yscaled -1; fill close_path; fet_endchar; fet_beginchar ("close", "close.0M1"); set_char_box (0, staff_space#, 0.5 staff_space#, 0.5 staff_space#); set_arrow_paths (staff_space, 0.5 staff_space, 1.5, blot_diameter); fill close_path; close_path := close_path yscaled -1; fill close_path; currentpicture := currentpicture xscaled -1; fet_endchar; fet_beginchar ("close", "close.11"); set_char_box (.5 staff_space#, .5 staff_space#, 1.0 staff_space#, 0.0 staff_space#); set_arrow_paths (staff_space, 0.5 staff_space, 1.5, blot_diameter); fill close_path; close_path := close_path yscaled -1; fill close_path; currentpicture := currentpicture rotated 90; fet_endchar; fet_beginchar ("close", "close.1M1"); set_char_box (.5 staff_space#, .5 staff_space#, 0.0 staff_space#, 1.0 staff_space#); set_arrow_paths (staff_space, 0.5 staff_space, 1.5, blot_diameter); fill close_path; close_path := close_path yscaled -1; fill close_path; currentpicture := currentpicture rotated -90; fet_endchar; fet_endgroup ("arrowheads"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-autometric.mf000066400000000000000000000116251404105311000250470ustar00rootroot00000000000000% % autometric.mf -- administrative MF routines % % source file of the Feta (not an acronym for Font-En-Tja) % pretty-but-neat music font % % (c) 1997--2009 Han-Wen Nienhuys % Jan Nieuwenhuizen % % These macros help create ascii logging output % to automate generation of the lily tables. % The output should be parsed by the mf-to-table script. message "******************************************************"; message "Using feta Autometric macros."; message "order of messages: "; message " NAME/CODE/BP/WD/DP/HT/WX/WY"; message "******************************************************"; message ""; % font or database? def fet_beginfont (expr name, size, encod) = font_identifier := name & decimal size; font_size size; font_coding_scheme "asis"; message "@{font@:GNU@:LilyPond@:" & name & "@:" & decimal size & "@:" & encod & "@}"; message ""; enddef; def fet_endfont = message "@{tnof@}"; enddef; % group or table? def fet_begingroup (expr name) = begingroup; save feta_group; string feta_group; feta_group := name; message "@{group@:" & feta_group & "@}"; message ""; enddef; def fet_endgroup (expr name) = message "@{puorg@:" & name & "@}"; message ""; endgroup; enddef; def autometric_parameter (expr name, value) = message "@{parameter@:" & name & "@:" & decimal value & "@}"; enddef; def autometric_output_char = message "@{char@:" & charnamestr & "@:" & decimal charcode & "@:" & decimal charbp & "@:" & decimal charwd & "@:" & decimal chardp & "@:" & decimal charht & "@:" & decimal charwx & "@:" & decimal charwy & "@:" & idstr & "@}"; enddef; def hround_pixels (expr sharped) = hround (sharped * hppp) enddef; def vround_pixels (expr sharped) = vround (sharped * vppp) enddef; def tand (expr alpha) = (sind alpha / cosd alpha) enddef; def to_bp (expr num) = decimal (num * bp_per_pixel) enddef; % breapth, width, depth, height % breapth x-depth def set_char_box (expr b_sharp, w_sharp, d_sharp, h_sharp) = save scharbp, scharht, scharwd, schardp; % some paranoia if someone calls set_char_box (charwd, charbp, ...) scharbp := b_sharp; scharht := h_sharp; schardp := d_sharp; scharwd := w_sharp; charbp := scharbp; charht := scharht; chardp := schardp; charwd := scharwd; w := hround (w_sharp * hppp); b := hround (b_sharp * hppp); h := vround (h_sharp * vppp); d := vround (d_sharp * vppp); charwx := charwd; charwy := 0; % additions for mf2pt1 (`bbox' is called `glyph_dimensions' starting % with version 2.4.2) if known bp_per_pixel: special "% MF2PT1: bbox " & to_bp (-b) & " " & to_bp (-d) & " " & to_bp (w) & " " & to_bp (h); special "% MF2PT1: glyph_dimensions " & to_bp (-b) & " " & to_bp (-d) & " " & to_bp (w) & " " & to_bp (h); special "% MF2PT1: font_size " & decimal designsize; special "% MF2PT1: font_slant " & decimal font_slant_; for fvar = "font_identifier", "font_coding_scheme", "font_version", "font_comment", "font_family", "font_weight", "font_unique_id", "font_name": if known scantokens (fvar & "_"): special "% MF2PT1: " & fvar & " " & scantokens (fvar & "_"); fi; endfor; for fvar = "font_underline_position", "font_underline_thickness": if known scantokens (fvar & "_"): special "% MF2PT1: " & fvar & " " & scantokens ("decimal " & fvar & "_"); fi; endfor; special "% MF2PT1: font_fixed_pitch " & (if font_fixed_pitch_: "1" else: "0" fi); % this must come after the `font_size' special special "% MF2PT1: charwd " & decimal charwd; fi; enddef; def no_dimen_beginchar (expr c) = begingroup; charcode := if known c: byte c else: 0; fi; charic := 0; clearxy; clearit; clearpen; scantokens extra_beginchar; enddef; % % we leave the ctrl characters alone. % code := 32; % starts just as plain mf's beginchar: % charcode, % and then adds: % charname see below % id index in lily's table % The dimensions are uninitialised; you should use set_char_box manually. def fet_beginchar (expr name, id_lit) = save idstr, charnamestr; save charbp; save w, b, h, d; save charwx, charwy; string idstr, charnamestr; charnamestr := name; idstr := id_lit; % addition for mf2pt1 if known bp_per_pixel: if known feta_group: special "% MF2PT1: glyph_name " & feta_group & "." & idstr; else: special "% MF2PT1: glyph_name " & idstr; fi; fi; no_dimen_beginchar (incr code) name; enddef; def makebox_with_breapth (text r) = for y = -d, 0, h: r ((-b, y), (w, y)); endfor; for x = -b, 0, w: r ( (x, -d), (x, h)); endfor; enddef; % % override plain endchar. We want a different box. % def breapth_endchar = scantokens extra_endchar; if proofing > 0: makebox_with_breapth (proofrule); fi; chardx := (w + b); % what the heck is chardx shipit; endgroup; enddef; def fet_endchar = autometric_output_char; breapth_endchar; enddef; muse-4.0.0/src/share/scoreglyphs/feta-original/feta-banier.mf000066400000000000000000000357711404105311000241430ustar00rootroot00000000000000% % feta-banier.mf -- draw flags % % source file of the GNU LilyPond music typesetter % % (c) 1997--2009 Han-Wen Nienhuys fet_begingroup ("flags"); save outer_path; path outer_path; % % Flags pointing down overlap with the notehead (in x-direction), so % the down-flag can be bigger. % upflag_width# = .65 black_notehead_width# + stemthickness# / 2; downflag_width# = .833 black_notehead_width# + stemthickness# / 2; right_upflag_space# = .0 upflag_width#; right_downflag_space# = .0 downflag_width#; % % Flags pointing down cannot overlap with the notehead in y-direction, % so they have less slant. % % Because of optical illusion, the utmost flag (bottom for % down-pointing, top for up-pointing) should be smaller than the other % flags. Adobe Sonata doesn't do this correctly. (Instead they have % an extension flag, which looks less elegant.) % save hip_thickness, foot_thickness; hip_thickness# = 1.0 linethickness# + 0.069 staff_space#; foot_thickness# = 1.2055 linethickness# + 0.06 staff_space#; define_pixels (hip_thickness, foot_thickness); % % Inspired by Adobe Sonata and [Wanske]. % For example, see POSTSCRIPT Language -- program design, % page 119, and [Wanske], p 41, 42. % def draw_flag (expr center, flare, dims, hip_depth, foot_wid, hip_thickness, foot_thickness, show_labels) = save c; clearxy; c = 0.7; penpos1 (flare, 90); penpos2 (whatever, 0); x2r - x2l = hround (hip_thickness); penpos3 (foot_thickness, -20.0); z1r = center; z2r = center + (xpart (dims), -ypart (dims) * hip_depth); z3r = center + (xpart (dims) * foot_wid, -ypart (dims)); x2r := hround x2r; y2r := vround y2r; outer_path := z3r{curl c} .. z2r{up} .. {up}z1r; if show_labels = 1: penlabels (1, 2, 3); fi; fill z1l{curl 0} ..tension 1.1.. z2l{down} .. {curl c}simple_serif (z3l, z3r, 80) & outer_path & z1r -- cycle; enddef; % % TODO: calculate intersectpoint (see TeX book, p. 137) % TODO: calculate incision_depth % def add_flag (expr yoff, flare, hip_wid_multiplier, hip_dep_multiplier, intersectpoint, hip_thickness, foot_thickness) = begingroup save prev_center, prev_xextreme, prev_yextreme; save rel_foot, ip, center, incision_depth; save prev_hipwid, prev_footdep, prev_hipdep, wid, dep, hip_dep; save hip_dep_ratio, foot_wid_ratio; pair prev_center, center, foot, prev_xextreme, prev_yextreme; pair ip, rel_foot; incision_depth = 1.013; prev_center = point 2 of outer_path; prev_xextreme = point 1 of outer_path; prev_yextreme = point 0 of outer_path; prev_hipwid = xpart (prev_xextreme - prev_center); prev_footdep = -ypart (prev_yextreme - prev_center); prev_hipdep = -ypart (prev_xextreme - prev_center); ip = point intersectpoint of outer_path; wid = prev_hipwid * hip_wid_multiplier; hip_dep = prev_hipdep * hip_dep_multiplier; center = prev_center + (0, yoff); rel_foot = incision_depth [(wid, hip_dep), ip - center]; dep = -ypart (rel_foot); foot_wid_ratio = xpart (rel_foot) / wid; hip_dep_ratio = hip_dep / dep; draw_flag (center, flare, (wid, dep), hip_dep_ratio, foot_wid_ratio, hip_thickness, foot_thickness, 0); endgroup enddef; fet_beginchar ("8th Flag (up)", "u3"); save flare, hip_depth_ratio, hip_width, foot_depth, foot_width_ratio; flare = staff_space; hip_depth_ratio = .72; foot_width_ratio = .8; hip_width# = upflag_width# - hip_thickness# / 2; foot_depth# = 3 staff_space# - blot_diameter# / 2; define_pixels (hip_width, foot_depth); set_char_box (0, hip_width# + stemthickness# / 2 + right_upflag_space#, foot_depth# + foot_thickness# / 2, stemthickness# / 2); draw_flag ((0,0), flare, (hip_width, foot_depth), hip_depth_ratio, foot_width_ratio, hip_thickness, foot_thickness, 1); draw_square_block ((-0.5 stemthickness_rounded, -staff_space_rounded), (0, 0)); fet_endchar; fet_beginchar ("16th Flag (up)", "u4"); save flare, hip_depth_ratio, hip_width, foot_depth, foot_width_ratio; save flagspace, total_depth, flag_count; total_depth# = 3.5 staff_space# - blot_diameter# / 2; flag_count = 2; flare = .85 staff_space; flagspace# = .85 staff_space#; hip_depth_ratio = .72; hip_width# = upflag_width# - hip_thickness# / 2; flagspace# + foot_depth# = total_depth#; foot_width_ratio = .8; define_pixels (hip_width, foot_depth); define_whole_vertical_pixels (flagspace); set_char_box (0, hip_width# + stemthickness# / 2 + right_upflag_space#, total_depth# + foot_thickness# / 2, stemthickness# / 2); draw_flag ((0, -flagspace), flare, (hip_width, foot_depth), hip_depth_ratio, foot_width_ratio, hip_thickness, foot_thickness, 1); add_flag (flagspace, flare, .97, 1.00, 1.25, hip_thickness, foot_thickness); draw_square_block ((-0.5 stemthickness_rounded, 0), (0, -2 staff_space_rounded)); fet_endchar; fet_beginchar ("32nd Flag (up)", "u5"); save flare, hip_depth_ratio, hip_width, foot_depth, foot_width_ratio; save flagspace, total_depth, flag_count; flag_count = 3; total_depth# = 4.25 staff_space#; flare = .85 staff_space; flagspace# = .87 staff_space#; hip_depth_ratio = .72; hip_width# = upflag_width# - hip_thickness# / 2; foot_width_ratio = .8; (flag_count - 1) * flagspace# + foot_depth# = total_depth#; define_pixels (hip_width, foot_depth); define_whole_vertical_pixels (flagspace); set_char_box (0, hip_width# + right_upflag_space#, total_depth# + foot_thickness# / 2, stemthickness# / 2); draw_flag ((0, -2 flagspace), flare, (hip_width, foot_depth), hip_depth_ratio, foot_width_ratio, hip_thickness, foot_thickness, 1); add_flag (flagspace, flare, .97, 1.00, 1.25, hip_thickness, foot_thickness); add_flag (flagspace, flare, .95, 1.05, 1.25, hip_thickness, foot_thickness); draw_square_block ((-0.5 stemthickness_rounded, 0), (0, -3 staff_space_rounded)); fet_endchar; fet_beginchar ("64th Flag (up)", "u6"); save flare, hip_depth_ratio, hip_width, foot_depth, foot_width_ratio; save flagspace, total_depth, flag_count; flag_count = 4; flare = .85 staff_space; flagspace# = .9 staff_space#; hip_depth_ratio = .72; hip_width# = upflag_width# - hip_thickness# / 2; total_depth# = 5.25 staff_space#; foot_width_ratio = .8; (flag_count - 1) * flagspace# + foot_depth# = total_depth#; define_pixels (hip_width, foot_depth); define_whole_vertical_pixels (flagspace); set_char_box (0, hip_width# + right_upflag_space#, total_depth# + foot_thickness# / 2, stemthickness# / 2); draw_flag ((0, -(flag_count - 1) * flagspace), flare, (hip_width, foot_depth), hip_depth_ratio, foot_width_ratio, hip_thickness, foot_thickness, 1); add_flag (flagspace, flare, .97, 1.00, 1.3, hip_thickness, foot_thickness); add_flag (flagspace, flare, 1.00, 1.00, 1.25, hip_thickness, foot_thickness); add_flag (flagspace, flare, .95, 1.05, 1.25, hip_thickness, foot_thickness); draw_square_block ((-0.5 stemthickness_rounded, 0), (0, -4 staff_space_rounded)); fet_endchar; fet_beginchar ("128th Flag (up)", "u7"); save flare, hip_depth_ratio, hip_width, foot_depth, foot_width_ratio; save flagspace, total_depth, flag_count; flag_count = 5; flare = .85 staff_space; flagspace# = .93 staff_space#; hip_depth_ratio = .72; hip_width# = upflag_width# - hip_thickness# / 2; total_depth# = 6.25 staff_space#; foot_width_ratio = .8; (flag_count - 1) * flagspace# + foot_depth# = total_depth#; define_pixels (hip_width, foot_depth); define_whole_vertical_pixels (flagspace); set_char_box (0, hip_width# + right_upflag_space#, total_depth# + foot_thickness# / 2, stemthickness# / 2); draw_flag ((0, -(flag_count - 1) * flagspace), flare, (hip_width, foot_depth), hip_depth_ratio, foot_width_ratio, hip_thickness, foot_thickness, 1); add_flag (flagspace, flare, .97, 1.00, 1.3, hip_thickness, foot_thickness); add_flag (flagspace, flare, 1.00, 1.00, 1.25, hip_thickness, foot_thickness); add_flag (flagspace, flare, 1.00, 1.00, 1.25, hip_thickness, foot_thickness); add_flag (flagspace, flare, 0.95, 1.05, 1.25, hip_thickness, foot_thickness); draw_square_block ((-0.5 stemthickness_rounded, 0), (0, -5 staff_space_rounded)); fet_endchar; fet_beginchar ("8th (down)", "d3"); save flare, hip_depth_ratio, hip_width, foot_depth, foot_width_ratio; save flagspace, total_depth, flag_count; flag_count = 1; flare = staff_space; flagspace# = .9 staff_space#; hip_depth_ratio = .72; hip_width# = downflag_width# - hip_thickness# / 2; total_depth# = 2.85 staff_space#; foot_width_ratio = .8; (flag_count - 1) * flagspace# + foot_depth# = total_depth#; define_pixels (hip_width, flagspace, foot_depth); set_char_box (0, hip_width# + right_downflag_space#, total_depth# + foot_thickness# / 2, stemthickness# / 2) draw_flag ((0, -(flag_count - 1) * flagspace), flare, (hip_width, foot_depth), hip_depth_ratio, foot_width_ratio, hip_thickness, foot_thickness, 0); draw_square_block ((-0.5 stemthickness_rounded, 0), (0, -staff_space_rounded)); y_mirror_char; fet_endchar; %%%%%%%% % % Single Stroke for Short Appogiatura % fet_beginchar ("grace dash (up)", "ugrace"); save flare, hip_depth_ratio, hip_width, foot_depth; hip_depth_ratio = .72; flare# = staff_space#; hip_width# = upflag_width# - hip_thickness# / 2; foot_depth# = 3 staff_space#; define_pixels (hip_width, foot_depth); set_char_box (hip_width# * hip_depth_ratio, hip_width# + right_upflag_space#, foot_depth# * hip_depth_ratio, -flare#) pickup pencircle scaled 1.5 stemthickness; z1 = (-b, -d); z2 = (w, h); penpos1 (1.5 stemthickness, angle (z2 - z1) - 90); penpos2 (1.5 stemthickness, angle (z2 - z1) - 90); fill z1l -- z2l .. top z2 .. rt z2 .. z2r -- z1r .. bot z1 .. lft z1 .. cycle; penlabels (1, 2); fet_endchar; fet_beginchar ("grace dash (down)", "dgrace"); save flare, hip_depth_ratio, hip_width, foot_depth; save total_depth; hip_depth_ratio = .72 ; flare# = .99 staff_space#; hip_width# = downflag_width# - hip_thickness# / 2; total_depth# = 2.85 staff_space#; foot_depth# = total_depth#; foot_width_ratio = .8; define_pixels (hip_width, foot_depth); set_char_box (hip_width# * hip_depth_ratio, hip_width# + right_downflag_space#, foot_depth# * hip_depth_ratio, -flare#) pickup pencircle scaled 1.5 stemthickness; z1 = (-b, -d); z2 = (w, h); penpos1 (1.5 stemthickness, angle (z2 - z1) - 90); penpos2 (1.5 stemthickness, angle (z2 - z1) - 90); fill z1l -- z2l .. top z2 .. rt z2 .. z2r -- z1r .. bot z1 .. lft z1 .. cycle; y_mirror_char; fet_endchar; fet_beginchar ("16th (down)", "d4"); save flare, hip_depth_ratio, hip_width, foot_depth, foot_width_ratio; save flagspace, total_depth, flag_count; flag_count = 2; flare = .8 staff_space; flagspace# = .9 staff_space#; hip_depth_ratio = .85; hip_width# = downflag_width# - hip_thickness# / 2; total_depth# = 3.0 staff_space# - blot_diameter# / 2; foot_width_ratio = .95; (flag_count - 1) * flagspace# + foot_depth# = total_depth#; set_char_box (0, hip_width# + right_downflag_space#, total_depth# + foot_thickness# / 2, stemthickness# / 2); define_pixels (hip_width, foot_depth); define_whole_vertical_pixels (flagspace); draw_flag ((0, -(flag_count - 1) * flagspace), flare, (hip_width, foot_depth), hip_depth_ratio, foot_width_ratio, hip_thickness, foot_thickness, 0); add_flag (flagspace, flare, .95, 1.00, 1.25, hip_thickness, foot_thickness); draw_square_block ((-0.5 stemthickness_rounded, 0), (0, -2 staff_space_rounded)); y_mirror_char; fet_endchar; fet_beginchar ("32nd (down)", "d5"); save flare, hip_depth_ratio, hip_width, foot_depth, foot_width_ratio; save flagspace, total_depth, flag_count; flag_count = 3; flare = .84 staff_space; flagspace# = .9 staff_space#; hip_depth_ratio = .85; hip_width# = downflag_width# - hip_thickness# / 2; total_depth# = 3.85 staff_space#; foot_width_ratio = .95; (flag_count - 1) * flagspace# + foot_depth# = total_depth#; define_pixels (hip_width, foot_depth); define_whole_vertical_pixels (flagspace); set_char_box (0, hip_width# + right_downflag_space#, total_depth# + foot_thickness# / 2, stemthickness# / 2); draw_flag ((0, -(flag_count - 1) * flagspace), flare, (hip_width, foot_depth), hip_depth_ratio, foot_width_ratio, hip_thickness, foot_thickness, 0); add_flag (flagspace, flare, .97, 1.00, 1.25, hip_thickness, foot_thickness); add_flag (flagspace, flare, .95, 1.05, 1.25, hip_thickness, foot_thickness); draw_square_block ((-0.5 stemthickness_rounded, 0), (0, -3 staff_space_rounded)); y_mirror_char; fet_endchar; fet_beginchar ("64th (down)", "d6"); save flare, hip_depth_ratio, hip_width, foot_depth, foot_width_ratio; save flagspace, total_depth, flag_count; flag_count = 4; flare = .8 staff_space; flagspace# = .9 staff_space#; hip_depth_ratio = .85; hip_width# = downflag_width# - hip_thickness# / 2; total_depth# = 4.35 staff_space#; foot_width_ratio = .98; (flag_count - 1) * flagspace# + foot_depth# = total_depth#; define_pixels (hip_width, foot_depth); define_whole_vertical_pixels (flagspace); set_char_box (0, hip_width# + right_downflag_space#, total_depth# + foot_thickness# / 2, stemthickness# / 2); draw_flag ((0, -(flag_count - 1) * flagspace), flare, (hip_width, foot_depth), hip_depth_ratio, foot_width_ratio, hip_thickness, foot_thickness, 0); add_flag (flagspace, flare, .97, 1.20, 1.175, hip_thickness, foot_thickness); add_flag (flagspace, flare, .97, 1.10, 1.175, hip_thickness, foot_thickness); add_flag (.98 flagspace, flare, .91, 1.05, 1.2, hip_thickness, foot_thickness); draw_square_block ((-0.5 stemthickness_rounded, 0), (0, -4 staff_space_rounded)); y_mirror_char; fet_endchar; fet_beginchar ("128th (down)", "d7"); save flare, hip_depth_ratio, hip_width, foot_depth, foot_width_ratio; save flagspace, total_depth, flag_count; flag_count = 5; flare = .8 staff_space; flagspace# = .9 staff_space#; hip_depth_ratio = .85; hip_width# = downflag_width# - hip_thickness# / 2; total_depth# = 5.25 staff_space#; foot_width_ratio = .98; (flag_count - 1) * flagspace# + foot_depth# = total_depth#; define_pixels (hip_width, foot_depth); define_whole_vertical_pixels (flagspace); set_char_box (0, hip_width# + right_downflag_space#, total_depth# + foot_thickness# / 2, stemthickness# / 2); draw_flag ((0, -(flag_count - 1) * flagspace), flare, (hip_width, foot_depth), hip_depth_ratio, foot_width_ratio, hip_thickness, foot_thickness, 0); add_flag (flagspace, flare, .97, 1.20, 1.175, hip_thickness, foot_thickness); add_flag (flagspace, flare, .97, 1.10, 1.175, hip_thickness, foot_thickness); add_flag (.98 flagspace, flare, .91, 1.05, 1.2, hip_thickness, foot_thickness); add_flag (.98 flagspace, flare, .91, 1.05, 1.2, hip_thickness, foot_thickness); draw_square_block ((-0.5 stemthickness_rounded, 0), (0, -5 staff_space_rounded)); y_mirror_char; fet_endchar; fet_endgroup ("flags"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-beugel.mf000066400000000000000000000063531404105311000241400ustar00rootroot00000000000000% % feta-beugel.mf -- Staff braces % % source file of the Feta (not an acronym for Font-En-Tja) % pretty-but-neat music font % % (c) 1997--2009 Han-Wen Nienhuys % Jan Nieuwenhuizen % We have to reduce the pixel-per-point value to % support large brace glyphs in case we are using MetaPost % (which by default sets `hppp' to 49.80244, regardless of % the used `mag' value) if known miterlimit: bpppix_ := 0.2; % ten times larger than original numeric mm, pt, dd, bp, cm, pc, cc, in; mm * bpppix_ = 2.83464; pt * bpppix_ = 0.99626; dd * bpppix_ = 1.06601; bp * bpppix_ = 1; cm * bpppix_ = 28.34645; pc * bpppix_ = 11.95517; cc * bpppix_ = 12.79213; in * bpppix_ = 72; hppp := pt; vppp := pt; fi; input feta-autometric; input feta-macros; staffsize# := 20 pt#; %% arbitrary input feta-params; % % We must let the design increase for each font to make sure that mftrace % doesn't jack up the resolution too highly for the longer braces. % fet_beginfont ("feta-braces-" & char (97 + font_count), (font_count + 1) * 20, "fetaBraces"); mode_setup; save code, braces_per_font; code := 64; braces_per_font := 64; def draw_brace (expr height_sharp, width_sharp, slt_sharp, brace_number) = save pendir, height, width, thin, thick, slt, pat; save penangle; pair pendir; path pat; height# := height_sharp; width# := width_sharp; slt# := slt_sharp; fet_beginchar ("brace number " & decimal (brace_number), "brace" & decimal (brace_number)) set_char_box (width#, 0, height# / 2, height# / 2); define_pixels (height, width, slt); thin = 2 slt; thick = .5 width; z2 = .5 [z1, z3]; y3l = y1 + height / 2; x3 = x1 + width; y1 = 0; x1 = -b; y0 = y1; x0 = x1 - 2/6 thin; pendir = unitvector (x3 - x1, y3l / 6 - y1); penangle = angle pendir - 90; penpos3 (thin, penangle); penpos2 (thick, angle (z3 - z1) - 90); penpos1 (2/3 thin, penangle); penlabels (1, 2, 3); labels (0); pat := z2r .. simple_serif (z3r, z3l, 90) .. z2l .. z1l{dir (angle (z1r - z1l) - 90)} .. z0{down} .. z1r{-dir (angle (z1l - z1r) + 90)} .. cycle; pat := subpath (0, 5) of pat -- subpath (6, 7) of pat .. cycle; fill pat; fill pat yscaled -1; fet_endchar; enddef; save stafflinethickness; save increment; linethickness := 0.5 pt#; increment := 0.5 pt#; y := 10 pt#; for i := 0 step 1 until font_count: save number; number := braces_per_font * i; for j := 0 step 1 until (braces_per_font - 1): % message "l: "&decimal l; % note: define_pixels (x) multiplies x by hppp, % must never get bigger than infinity y := y + increment; if y > infinity / hppp: message "Resolution and/or magnification is too high"; message "HPPP: "& decimal hppp &" Y: " & decimal y; errmessage "please report to "; fi; % x should be about one staff space, taking brace to have % default height of 3 staffs, this yields height / 3 / 4 = 12 % but 15 looks better x := y / 15; increment := x / 10; linethickness := min (0.5 pt#, y / 150); if i = font_count: draw_brace (y, x, linethickness, number); fi; number := number + 1; endfor; endfor; fet_endfont ("feta-braces"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-bolletjes.mf000066400000000000000000000756271404105311000246720ustar00rootroot00000000000000% -*-Fundamental-*- % feta-bolletjes.mf -- implement noteheads % % source file of LilyPond's pretty-but-neat music font % % (c) 1997--2009 Jan Nieuwenhuizen % & Han-Wen Nienhuys % & Juergen Reuter % test_outlines := 0; save remember_pic; picture remember_pic; % Most beautiful noteheads are pronounced, not circular, % and not even symmetric. % These examples are inspired by [Wanske]; see literature list. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % NOTE HEAD VARIABLES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% save black_notehead_width, noteheight; save half_notehead_width, whole_notehead_width, slash_thick; save slash_slope, overdone_heads, solfa_noteheight; numeric noteheight; numeric slash_thick; numeric black_notehead_width; numeric whole_notehead_width; numeric half_notehead_width; fet_begingroup ("noteheads"); % Slope of slash. From scm/grob-description.scm. How to auto-copy? slash_slope := 1.7; % Thickness of slash lines. Quarter notes get 1.5slt width. slash_thick# := 2/3 * 0.48 staff_space#; % % Hand-engraved music often has balls extending above and below % the lines. If you like that, modify overdone heads (unit: % stafflinethickness). % overdone_heads = 0.0; noteheight# := staff_space# + (1 + overdone_heads) * stafflinethickness#; % % solfa heads should not overlap on chords. % solfa_noteheight# := staff_space# - stafflinethickness#; define_pixels (slash_thick); define_whole_vertical_pixels (noteheight); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % SLANT moves both extrema on the long axis (by SLANT * ELLIPTICITY, % so SLANT = -1, puts the extreme on the long axis next to the short % axis one). % def draw_outside_ellipse (expr ellipticity, tilt, superness, slant) = save attachment_y; save pat; path pat; pat := superellipse ((ellipticity, 0), (-slant * ellipticity, 1.0), (-ellipticity, 0), (slant * ellipticity, -1.0), superness); pat := pat rotated tilt; save top_point, right_point; pair top_point, right_point; top_point := directionpoint left of pat; right_point := directionpoint up of pat; save scaling, width; scaling# = noteheight# / (2 ypart (top_point)); width# := 2 xpart (right_point) * scaling#; define_pixels (scaling, width); set_char_box (0, width#, noteheight# / 2, noteheight# / 2); d := d - feta_space_shift; % attachment Y charwy := ypart (right_point) * scaling#; charwx := width#; pat := pat scaled scaling shifted (w / 2, .5 (h - d)); width := hround width; if test_outlines = 1: draw pat; else: fill pat; fi; enddef; def undraw_inside_ellipse (expr ellipticity, tilt, superness, clearance) = begingroup save pat; path pat; pat := superellipse ((ellipticity, 0), (0, 1.0), (-ellipticity, 0), (0, -1.0), superness); pat := pat rotated tilt; save top_point, right_point; pair top_point, right_point; top_point := directionpoint left of pat; right_point := directionpoint up of pat; save height, scaling; height# = staff_space# + stafflinethickness# - clearance; scaling# = height# / (2 ypart (top_point)); define_pixels (scaling); pat := pat scaled scaling shifted (w / 2, .5 (h - d)); if test_outlines = 1: draw pat; else: unfill pat; fi endgroup; enddef; % % dimensions aren't entirely right. % def draw_longa (expr up) = save stemthick, fudge; stemthick# = 2 stafflinethickness#; define_whole_blacker_pixels (stemthick); fudge = hround (blot_diameter / 2); draw_outside_ellipse (1.80, 0, 0.707, 0); undraw_inside_ellipse (1.30, 125, 0.68, 2 stafflinethickness#); pickup pencircle scaled stemthick; if up: bot y1 = -d; top y2 = h; rt x1 - fudge = 0; x1 = x2; fudge + lft x3 = w; x4 = x3; top y4 = h + 3.0 staff_space; y3 = y1; else: bot y1 = -d - 3.0 staff_space; top y2 = h; rt x1 - fudge = 0; x1 = x2; fudge + lft x3 = w; x4 = x3; y4 = y2; bot y3 = -d; fi; draw_gridline (z1, z2, stemthick); draw_gridline (z3, z4, stemthick); labels (1, 2, 3, 4); enddef; fet_beginchar ("Longa notehead", "uM2"); draw_longa (true); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("Longa notehead", "dM2"); draw_longa (false); draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Longa notehead", "uM2"); draw_longa (true); draw_staff (-2, 2, 0.5); fet_endchar; fet_beginchar ("Longa notehead", "dM2"); draw_longa (false); draw_staff (-2, 2, 0.5); fet_endchar; fi; % % dimensions aren't entirely right. % def draw_brevis = save stemthick, fudge; stemthick# = 2 stafflinethickness#; define_whole_blacker_pixels (stemthick); fudge = hround (blot_diameter / 2); draw_outside_ellipse (1.80, 0, 0.707, 0); undraw_inside_ellipse (1.30, 125, 0.68, 2 stafflinethickness#); pickup pencircle scaled stemthick; bot y1 = -d; top y2 = h; rt x1 - fudge = 0; x1 = x2; fudge + lft x3 = w; x4 = x3; y4 = y2; y3 = y1; draw_gridline (z1, z2, stemthick); draw_gridline (z3, z4, stemthick); enddef; fet_beginchar ("Brevis notehead", "sM1"); draw_brevis; draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Brevis notehead", "sM1"); draw_brevis; draw_staff (-2, 2, 0.5); fet_endchar; fi; fet_beginchar ("Whole notehead", "s0"); draw_outside_ellipse (1.80 - puff_up_factor / 3.0, 0, 0.707, 0); undraw_inside_ellipse (1.30, 125 - puff_up_factor * 10, 0.68, 2 stafflinethickness#); whole_notehead_width# := charwd; draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Whole notehead", "s0"); draw_outside_ellipse (1.80 - puff_up_factor / 3.0, 0, 0.707, 0); undraw_inside_ellipse (1.30, 125 - puff_up_factor * 10, 0.68, 2 stafflinethickness#); draw_staff (-2, 2, 0.5); fet_endchar; fi; fet_beginchar ("Half notehead", "s1"); draw_outside_ellipse (1.53 - puff_up_factor / 3.0, 34, 0.66, 0.17); undraw_inside_ellipse (3.25, 33, 0.81, 2.5 stafflinethickness#); half_notehead_width# := charwd; draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Half notehead", "s1"); draw_outside_ellipse (1.53 - puff_up_factor / 3.0, 34, 0.66, 0.17); undraw_inside_ellipse (3.25, 33, 0.81, 2.5 stafflinethickness#); draw_staff (-2, 2, 0.5); fet_endchar; fi; fet_beginchar ("Quart notehead", "s2"); % used to have 32. With 31, they are slightly bolder. draw_outside_ellipse (1.49 - puff_up_factor / 3.0, 31, 0.707, 0); black_notehead_width# := charwd; draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Quart notehead", "s2"); draw_outside_ellipse (1.49 - puff_up_factor / 3.0, 31, 0.707, 0); draw_staff (-2, 2, 0.5); fet_endchar; fi; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% fet_beginchar ("Whole diamondhead", "s0diamond"); draw_outside_ellipse (1.80, 0, 0.495, 0); undraw_inside_ellipse (1.30, 125, 0.6, .4 staff_space# + stafflinethickness#); draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Whole diamondhead", "s0diamond"); draw_outside_ellipse (1.80, 0, 0.495, 0); undraw_inside_ellipse (1.30, 125, 0.6, .4 staff_space# + stafflinethickness#); draw_staff (-2, 2, 0.5); fet_endchar; fi; fet_beginchar ("Half diamondhead", "s1diamond"); draw_outside_ellipse (1.50, 34, 0.49, 0.17); undraw_inside_ellipse (3.5, 33, 0.80, .3 staff_space# + 1.5 stafflinethickness#); draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Half diamondhead", "s1diamond"); draw_outside_ellipse (1.50, 34, 0.49, 0.17); undraw_inside_ellipse (3.5, 33, 0.80, .3 staff_space# + 1.5 stafflinethickness#); draw_staff (-2, 2, 0.5); fet_endchar; fi; fet_beginchar ("Quart diamondhead", "s2diamond"); draw_outside_ellipse (1.80, 35, 0.495, -0.25); draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Quart diamondhead", "s2diamond"); draw_outside_ellipse (1.80, 35, 0.495, -0.25); draw_staff (-2, 2, 0.5); fet_endchar; fi; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% vardef penposx@# (expr d) = begingroup; save pat; path pat; pat = top z@# .. lft z@# .. bot z@# .. rt z@# .. cycle; z@#l = pat intersectionpoint (z@# -- infinity * dir (d + 180)); z@#r = pat intersectionpoint (z@# -- infinity * dir (d)); endgroup enddef; % % UGH: xs not declared as argument. % def define_triangle_shape (expr stemdir) = save triangle_a, triangle_b, triangle_c; save triangle_out_a, triangle_out_b, triangle_out_c; save triangle_in, triangle_out; save width, depth, height; save origin, left_up_dir; save exact_left_point, exact_right_point, exact_down_point; path triangle_a, triangle_b, triangle_c; path triangle_out_a, triangle_out_b, triangle_out_c; path triangle_in, triangle_out; pair origin, left_up_dir; pair exact_down_point, exact_left_point, exact_right_point; save pen_thick; pen_thick# = stafflinethickness# + .1 staff_space#; define_pixels (llap); define_blacker_pixels (pen_thick); left_up_dir = llap# * dir (90 + tilt); xpart (left_up_dir) * xs - (pen_thick# * xs) / 2 + xpart origin = 0; ypart origin = 0; exact_left_point := origin + (left_up_dir xscaled xs); exact_down_point := origin + (left_up_dir rotated 120 xscaled xs); exact_right_point := origin + (left_up_dir rotated 240 xscaled xs); height# = ypart (exact_left_point + origin) + pen_thick# / 2; depth# = -ypart (exact_down_point + origin) + pen_thick# / 2; width# = xpart (exact_right_point - exact_left_point) + pen_thick# * xs; set_char_box (0, width#, depth#, height#); % Formerly, the shape has simply been drawn with an elliptical pen % (`scaled pen_thick xscaled xs'), but the envelope of such a curve % is of 6th degree. For the sake of mf2pt1, we approximate it. pickup pencircle scaled pen_thick xscaled xs; z0 = (hround_pixels (xpart origin), 0); z1 = z1' = z0 + llap * dir (90 + tilt) xscaled xs; z2 = z2' = z0 + llap * dir (90 + tilt + 120) xscaled xs; z3 = z3' = z0 + llap * dir (90 + tilt + 240) xscaled xs; z12 = caveness [.5[z1, z2], z3]; z23 = caveness [.5[z2, z3], z1]; z31 = caveness [.5[z3, z1], z2]; triangle_a = z1 .. z12 .. z2; triangle_b = z2 .. z23 .. z3; triangle_c = z3 .. z31 .. z1; penposx1 (angle (direction 0 of triangle_a) - 90); penposx2 (angle (direction 0 of triangle_b) - 90); penposx3 (angle (direction 0 of triangle_c) - 90); penposx1' (angle (direction infinity of triangle_c) + 90); penposx2' (angle (direction infinity of triangle_a) + 90); penposx3' (angle (direction infinity of triangle_b) + 90); penposx12 (angle (z12 - z0)); penposx23 (angle (z23 - z0)); penposx31 (angle (z31 - z0)); z10 = (z0 -- z1) intersectionpoint (z1l .. z12l .. z2'r); z20 = (z0 -- z2) intersectionpoint (z2l .. z23l .. z3'r); z30 = (z0 -- z3) intersectionpoint (z3l .. z31l .. z1'r); triangle_in = z10 .. z12l .. z20 & z20 .. z23l .. z30 & z30 .. z31l .. z10 & cycle; triangle_out_a = z1r .. z12r .. z2'l; triangle_out_b = z2r .. z23r .. z3'l; triangle_out_c = z3r .. z31r .. z1'l; triangle_out = top z1 .. lft z1 .. z1r{direction 0 of triangle_out_a} & triangle_out_a & {direction infinity of triangle_out_a}z2'l .. lft z2 .. bot z2 .. z2r{direction 0 of triangle_out_b} & triangle_out_b & {direction infinity of triangle_out_b}z3'l .. rt z3 .. top z3 .. z3r{direction 0 of triangle_out_c} & triangle_out_c & {direction infinity of triangle_out_c}z1'l .. cycle; labels (0, 10, 20, 30); penlabels (1, 1', 2, 2', 3, 3', 12, 23, 31); % attachment Y if stemdir = 1: charwy := ypart exact_right_point; charwx := xpart exact_right_point + .5 pen_thick# * xs; else: charwy := -ypart exact_down_point; charwx := width# - (xpart exact_down_point - .5 pen_thick# * xs); fi enddef; def draw_whole_triangle_head = save hei, xs; save llap; save tilt; tilt = 40; llap# = 3/4 noteheight#; xs = 1.5; caveness := 0.1; define_triangle_shape (1); fill triangle_out; unfill triangle_in; enddef; fet_beginchar ("Whole trianglehead", "s0triangle"); draw_whole_triangle_head; draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Whole trianglehead", "s0triangle"); draw_whole_triangle_head; draw_staff (-2, 2, 0.5); fet_endchar; fi; def draw_small_triangle_head (expr dir) = save hei, xs; save llap; save tilt; tilt = 40; llap# = 2/3 noteheight#; xs = 1.2; caveness := 0.1; define_triangle_shape (dir); pickup feta_fillpen; filldraw triangle_out; unfilldraw triangle_in; enddef; fet_beginchar ("Half trianglehead (downstem)", "d1triangle"); draw_small_triangle_head (-1); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("Half trianglehead (upstem)", "u1triangle"); draw_small_triangle_head (1); draw_staff (-2, 2, 0.5); fet_endchar; def draw_closed_triangle_head (expr dir) = save hei, xs; save llap; save tilt; tilt = 40; llap# = 2/3 noteheight#; xs = 1.0; caveness := 0.1; define_triangle_shape (dir); fill triangle_out; enddef; fet_beginchar ("Quart trianglehead (upstem)", "u2triangle"); draw_closed_triangle_head (1); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("Quart trianglehead (downstem)", "d2triangle"); draw_closed_triangle_head (-1); draw_staff (-2, 2, 0.5); fet_endchar; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Slash heads are for indicating improvisation. They are % twice as high as normal heads. % def draw_slash (expr hwid_hash) = save exact_height; save ne, nw_dist; pair ne, nw_dist; exact_height = staff_space# + stafflinethickness# / 2; set_char_box (0, 2 exact_height / slash_slope + hwid_hash, exact_height, exact_height); charwx := charwd; charwy := charht; clearxy; d := d - feta_shift; pickup pencircle scaled blot_diameter; bot y1 = -d; top y2 = h; lft x1 = 0; lft x2 = 2 h / slash_slope; rt x3 = w; y3 = y2; y4 = y1; x3 - x2 = x4 - x1; ne = unitvector (z3 - z4); nw_dist = (ne rotated 90) * 0.5 blot_diameter; fill bot z1{left} .. (z1 + nw_dist){ne} -- (z2 + nw_dist){ne} .. top z2{right} -- top z3{right} .. (z3 - nw_dist){-ne} -- (z4 - nw_dist){-ne} .. bot z4{left} -- cycle; if hwid_hash > 2 slash_thick#: save th; th = slash_thick - blot_diameter; y6 = y7; y5 = y8; y3 - y7 = th; y5 - y1 = th; z6 - z5 = whatever * ne; z8 - z7 = whatever * ne; z5 = z1 + whatever * ne + th * (ne rotated -90); z8 = z4 + whatever * ne + th * (ne rotated 90); unfill z5 -- z6 -- z7 -- z8 -- cycle; fi labels (range 1 thru 10); enddef; fet_beginchar ("Whole slashhead", "s0slash"); draw_slash (4 slash_thick# + 0.5 staff_space#); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("Half slashhead", "s1slash"); draw_slash (3.0 slash_thick# + 0.15 staff_space#); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("Quart slashhead", "s2slash"); draw_slash (1.5 slash_thick#); draw_staff (-2, 2, 0); fet_endchar; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % `thick' is the distance between the NE/SW parallel lines in the cross % (distance between centres of lines) in multiples of stafflinethickness % def draw_cross (expr thick) = save ne, nw; save ne_dist, nw_dist, rt_dist, up_dist; save crz_in, crz_out; save thickness; pair ne, nw; pair ne_dist, nw_dist, rt_dist, up_dist; path crz_in, crz_out; pen_thick# := 1.2 stafflinethickness#; thickness# := thick * stafflinethickness#; define_pixels (thickness); define_blacker_pixels (pen_thick); pickup pencircle scaled pen_thick; h := h - feta_shift; top y3 = h; ne = unitvector ((1, (2 h - pen_thick) / (w - pen_thick))); rt x4 = w / 2; y5 = 0; z4 - z5 = whatever * ne; x6 = 0; z6 - z3 = whatever * ne; z3 - z4 = whatever * (ne yscaled -1); z4 - z3 = whatever * (ne) + (ne rotated -90) * thickness; x1 = charwd / 2 - .5 pen_thick#; z1 = whatever * ne + thick / 2 * stafflinethickness# * (ne rotated -90); % labels (1, 2, 3, 4, 5, 6); nw = unitvector (z3 - z4); up_dist = up * 0.5 pen_thick / cosd (angle (ne)); rt_dist = right * 0.5 pen_thick / sind (angle (ne)); nw_dist = (ne rotated 90) * 0.5 pen_thick; ne_dist = (nw rotated -90) * 0.5 pen_thick; x4' := x4; x5' := x5; y6' := y6; x4 := hround (x4' + .5 pen_thick) - .5 pen_thick; x5 := hfloor (x5' + xpart rt_dist) - xpart rt_dist; y6 := vfloor (y6' + ypart up_dist) - ypart up_dist; crz_out = (z6 + up_dist) -- (z3 + nw_dist){ne} .. (top z3) .. (z3 + ne_dist){-nw} -- (z4 + ne_dist){-nw} .. (rt z4) .. (z4 - nw_dist){-ne} -- (z5 + rt_dist); crz_out := crz_out shifted (0, feta_shift) -- reverse crz_out yscaled -1 shifted (0, -feta_eps); fill crz_out -- reverse crz_out xscaled -1 shifted (-feta_eps, 0) -- cycle; if (thick > 1): x4 := hround (x4' - xpart rt_dist) + xpart rt_dist; x5 := hceiling (x5' - .5 pen_thick) + .5 pen_thick; y6 := vfloor (y6' - .5 pen_thick) + .5 pen_thick; crz_in = (bot z6){right} .. (z6 - nw_dist){ne} -- (z3 - up_dist) -- (z4 - rt_dist) -- (z5 + nw_dist){-ne} .. {down}(lft z5); crz_in := crz_in shifted (0, feta_shift) -- reverse crz_in yscaled -1 shifted (0, -feta_eps); unfill crz_in -- reverse crz_in xscaled -1 shifted (-feta_eps, 0) -- cycle; fi % ugh currentpicture := currentpicture shifted (hround (w / 2), 0); charwx := charwd; charwy := y1 + feta_shift; z12 = (charwx * hppp, y1 * vppp); labels (12); enddef; fet_beginchar ("Whole Crossed notehead", "s0cross"); save wid, hei; wid# := black_notehead_width# + 4 stafflinethickness#; hei# := noteheight# + stafflinethickness#; set_char_box (0, wid#, hei# / 2, hei# / 2); draw_cross (3.75); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Whole Crossed notehead", "s0cross"); save wid, hei; wid# := black_notehead_width# + 4 stafflinethickness#; hei# := noteheight# + stafflinethickness#; set_char_box (0, wid#, hei# / 2, hei# / 2); currentpicture := remember_pic; draw_staff (-2, 2, 0.5); fet_endchar; fi; fet_beginchar ("Half Crossed notehead", "s1cross"); save wid, hei; wid# := black_notehead_width# + 2 stafflinethickness#; hei# := noteheight# + stafflinethickness# / 2; set_char_box (0, wid#, hei# / 2, hei# / 2); draw_cross (3.0); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Half Crossed notehead", "s1cross"); save wid, hei; wid# := black_notehead_width# + 2 stafflinethickness#; hei# := noteheight# + stafflinethickness# / 2; set_char_box (0, wid#, hei# / 2, hei# / 2); currentpicture := remember_pic; draw_staff (-2, 2, 0.5); fet_endchar; fi; fet_beginchar ("Crossed notehead", "s2cross"); wid# := black_notehead_width#; hei# := noteheight#; set_char_box (0, wid#, hei# / 2, hei# / 2); draw_cross (1.0); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("Crossed notehead", "s2cross"); wid# := black_notehead_width#; hei# := noteheight#; set_char_box (0, wid#, hei# / 2, hei# / 2); currentpicture := remember_pic; draw_staff (-2, 2, 0.5); fet_endchar; fi; fet_beginchar ("X-Circled notehead", "s2xcircle"); save wid, hei; save cthick, cxd, cyd, dy; wid# := black_notehead_width# * sqrt (sqrt2); hei# := noteheight# * sqrt (sqrt2); set_char_box (0, wid#, hei# / 2, hei# / 2); d := d - feta_space_shift; cthick# := (1.2 + 1/4) * stafflinethickness#; define_blacker_pixels (cthick); cxd := w - cthick; cyd := h + d - cthick / 2; dy = .5 (h - d); pickup pencircle scaled cthick; fill fullcircle xscaled (cxd + cthick) yscaled (cyd + cthick) shifted (w / 2, dy); unfill fullcircle xscaled (cxd - cthick) yscaled (cyd - cthick) shifted (w / 2, dy); xpos := .5 cxd / sqrt2; ypos := .5 cyd / sqrt2; pickup penrazor scaled cthick rotated (angle (xpos, ypos) + 90); draw (-xpos + w / 2, -ypos + dy) -- (xpos + w / 2, ypos + dy); pickup penrazor scaled cthick rotated (angle (xpos, -ypos) + 90); draw (-xpos + w / 2, ypos + dy) -- (xpos + w / 2, -ypos + dy); charwx := charwd; charwy := 0; z12 = (charwx * hppp, charwy * vppp); labels (12); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; if test > 0: fet_beginchar ("X-Circled notehead", "s2xcircle"); save wid, hei; save cthick, cxr, cyr; wid# := black_notehead_width# * sqrt (sqrt2); hei# := noteheight# * sqrt (sqrt2); set_char_box (0, wid#, hei# / 2, hei# / 2); currentpicture := remember_pic; draw_staff (-2, 2, 0.5); fet_endchar; fi; %%%%%%%% % % SOLFA SHAPED NOTES % save solfa_pen_thick; solfa_pen_thick# = 1.75 stafflinethickness#; define_blacker_pixels (solfa_pen_thick); save solfa_base_notewidth; solfa_base_notewidth# := black_notehead_width#; solfa_whole_width := whole_notehead_width# / black_notehead_width#; solfa_half_width := half_notehead_width# / black_notehead_width#; solfa_quarter_width := 1.0; def draw_do_head (expr width_factor, dir) = save p_in, p_out; save left_dist, right_dist; path p_in, p_out; pair left_dist, right_dist; set_char_box (0, width_factor * solfa_base_notewidth#, 0.5 solfa_noteheight#, 0.5 solfa_noteheight#); pickup pencircle scaled solfa_pen_thick; bot y1 = -d; y1 = y2; lft x1 = 0; rt x2 = w; top y3 = h; x3 =.5 [x1, x2]; left_dist = (unitvector (z3 - z1) rotated 90) * 0.5 solfa_pen_thick; right_dist = (unitvector (z2 - z3) rotated 90) * 0.5 solfa_pen_thick; p_in := (((z1 - left_dist) -- (z3 - left_dist)) intersectionpoint (top z1 -- top z2)) -- ((top z1 -- top z2) intersectionpoint ((z2 - right_dist) -- (z3 - right_dist))) -- (((z2 - right_dist) -- (z3 - right_dist)) intersectionpoint ((z1 - left_dist) -- (z3 - left_dist))) -- cycle; p_out := bot z1 -- bot z2{right} .. rt z2{up} .. (z2 + right_dist){z3 - z2} -- (z3 + right_dist){z3 - z2} .. top z3{left} .. (z3 + left_dist){z1 - z3} -- (z1 + left_dist){z1 - z3} .. lft z1{down} .. {right}cycle; labels (1, 2, 3); charwx := charwd; charwy := -chardp + 0.5 stafflinethickness#; if dir = -1: charwy := -charwy; fi; enddef; fet_beginchar ("Whole dohead", "s0do"); draw_do_head (solfa_whole_width, 1); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Half dohead", "d1do"); draw_do_head (solfa_half_width, -1); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Half dohead", "u1do"); draw_do_head (solfa_half_width, 1); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Quart dohead", "d2do"); draw_do_head (solfa_quarter_width, -1); fill p_out; fet_endchar; fet_beginchar ("Quart dohead", "u2do"); draw_do_head (solfa_quarter_width, 1); fill p_out; fet_endchar; % % re - flat top, curved bottom: % (0,h/2) {dir -90} .. (w/2,-h/2) .. {dir 90} (w,h/2) -- cycle; % (broader along the base and with more vertical sides for half and % whole notes) % stem attachment: h/2 % def draw_re_head (expr width_factor, dir) = save p_in, p_out; path p_in, p_out; set_char_box (0, width_factor * solfa_base_notewidth#, 0.5 solfa_noteheight#, 0.5 solfa_noteheight#); pickup pencircle scaled solfa_pen_thick; save curve_start; curve_start = 0.7; lft x1 = 0; y1 = y5; x1 = x2; y2 = curve_start [y3, y1]; bot y3 = -d; x3 = .5 [x2, x4]; rt x4 = w; y4 = y2; top y5 = h; x5 = x4; labels (range 1 thru 5); p_in := (z1 + 0.5 solfa_pen_thick * (1, -1)) -- rt z2{down} .. top z3 .. lft z4{up} -- (z5 + 0.5 solfa_pen_thick * (-1, -1)) -- cycle; p_out := lft z1 -- lft z2{down} .. bot z3 .. rt z4{up} -- rt z5{up} .. top z5{left} -- top z1{left} .. {down}cycle; charwx := charwd; charwy := curve_start [-chardp, charht]; if dir = -1: charwy := -charwy; fi; enddef; fet_beginchar ("Whole rehead", "s0re"); draw_re_head (solfa_whole_width, 1); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Half up rehead", "u1re"); draw_re_head (solfa_half_width, 1); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Half down rehead", "d1re"); draw_re_head (solfa_half_width, -1); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Quart rehead", "u2re"); draw_re_head (solfa_quarter_width, 1); fill p_out; fet_endchar; fet_beginchar ("Quart rehead", "d2re"); draw_re_head (solfa_quarter_width, -1); fill p_out; fet_endchar; def draw_mi_head (expr width_factor) = save path_out, path_in; save ne_dist, se_dist, ne, se; path path_out, path_in; pair ne_dist, se_dist, ne, se; set_char_box (0, width_factor * solfa_base_notewidth#, 0.5 solfa_noteheight#, 0.5 solfa_noteheight#); pickup pencircle scaled solfa_pen_thick; lft x1 = 0; y1 = 0; bot y2 = -d; x2 = .5 [x1, x3]; rt x3 = w; x4 = x2; y3 = y1; top y4 = h; z6 - z5 = whatever * (z2 - z1); z8 - z7 = whatever * (z2 - z1); z8 - z5 = whatever * (z4 - z1); z6 - z7 = whatever * (z4 - z1); ne = unitvector (z4 - z1); se = unitvector (z1 - z2); ne_dist = (ne rotated 90) * 0.5 solfa_pen_thick; se_dist = (se rotated 90) * 0.5 solfa_pen_thick; z5 = whatever [z1, z4] - ne_dist; z5 = whatever [z1, z2] - 1.5 se_dist; z5 - z1 = -(z7 - z3); labels (range 1 thru 8); path_in := z5 -- z6 -- z7 -- z8 -- cycle; path_out := lft z1 .. (z1 + se_dist){-se} -- (z2 + se_dist){-se} .. bot z2 .. (z2 - ne_dist){ne} -- (z3 - ne_dist){ne} .. rt z3 .. (z3 - se_dist){se} -- (z4 - se_dist){se} .. top z4 .. (z4 + ne_dist){-ne} -- (z1 + ne_dist){-ne} .. cycle; enddef; fet_beginchar ("Whole mihead", "s0mi"); draw_mi_head (solfa_whole_width); fill path_out; unfill path_in; fet_endchar; fet_beginchar ("Half mihead", "s1mi"); draw_mi_head (solfa_quarter_width); fill path_out; unfill path_in; fet_endchar; fet_beginchar ("Quart mihead", "s2mi"); draw_mi_head (solfa_quarter_width); fill path_out; fet_endchar; def draw_fa_head (expr width_factor) = set_char_box (0, width_factor * solfa_base_notewidth#, 0.5 solfa_noteheight#, 0.5 solfa_noteheight#); save p_down_in, p_down_out, p_up_in, p_up_out, nw_dist, nw; path p_down_in, p_down_out, p_up_in, p_up_out; pair nw_dist, nw; pickup pencircle scaled solfa_pen_thick; lft x1 = 0; top y1 = h; rt x2 = w; y2 = y1; bot y3 = -d; x3 = x2; y4 = y3; x4 = x1; labels (1, 2, 3, 4); nw = unitvector (z1 - z3); nw_dist = (nw rotated 90) * 0.5 solfa_pen_thick; p_up_in := (((z1 - nw_dist) -- (z3 - nw_dist)) intersectionpoint (bot z1 -- bot z2)) -- (((z1 - nw_dist) -- (z3 - nw_dist)) intersectionpoint (lft z3 -- lft z2)) -- (z2 + 0.5 solfa_pen_thick * (-1, -1)) -- cycle; p_up_out := lft z1{down} .. (z1 + nw_dist){-nw} -- (z3 + nw_dist){-nw} .. bot z3{right} .. rt z3{up} -- rt z2{up} .. top z2{left} -- top z1{left} .. {down}cycle; p_down_in := p_up_in rotated 180 shifted (w, 0); p_down_out := p_up_out rotated 180 shifted (w, 0); charwy := 0.0; charwx := charwd; enddef; fet_beginchar ("Whole fa up head", "u0fa"); draw_fa_head (solfa_whole_width); fill p_up_out; unfill p_up_in; fet_endchar; fet_beginchar ("Whole fa down head", "d0fa"); draw_fa_head (solfa_whole_width); fill p_down_out; unfill p_down_in; fet_endchar; fet_beginchar ("half fa up head", "u1fa"); draw_fa_head (solfa_half_width); fill p_up_out; unfill p_up_in; fet_endchar; fet_beginchar ("Half fa down head", "d1fa"); draw_fa_head (solfa_half_width); fill p_down_out; unfill p_down_in; fet_endchar; fet_beginchar ("Quarter fa up head", "u2fa"); draw_fa_head (solfa_quarter_width); fill p_up_out; fet_endchar; fet_beginchar ("Quarter fa down head", "d2fa"); draw_fa_head (solfa_quarter_width); fill p_down_out; fet_endchar; def draw_la_head (expr width_factor) = set_char_box (0, width_factor * solfa_base_notewidth#, 0.5 solfa_noteheight#, 0.5 solfa_noteheight#); save p_in, p_out; path p_in, p_out; pickup pencircle scaled solfa_pen_thick; lft x1 = 0; top y1 = h; rt x2 = w; y2 = y1; bot y3 = -d; x3 = x2; y4 = y3; x4 = x1; labels (range 1 thru 4); p_in := (z1 + 0.5 solfa_pen_thick * (1, -1)) -- (z2 + 0.5 solfa_pen_thick * (-1, -1)) -- (z3 + 0.5 solfa_pen_thick * (-1, 1)) -- (z4 + 0.5 solfa_pen_thick * (1, 1)) -- cycle; p_out := top z1 -- top z2{right} .. rt z2{down} -- rt z3{down} .. bot z3{left} -- bot z4{left} .. lft z4{up} -- lft z1{up} .. cycle; enddef; fet_beginchar ("Whole lahead", "s0la"); draw_la_head (solfa_whole_width); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Half lahead", "s1la"); draw_la_head (solfa_half_width); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Quart lahead", "s2la"); draw_la_head (solfa_quarter_width); fill p_out; fet_endchar; def draw_ti_head (expr width_factor, dir) = set_char_box (0, width_factor * solfa_base_notewidth#, 0.5 solfa_noteheight#, 0.5 solfa_noteheight#); save p_in, p_out, p_top; save nw_dist, sw_dist, nw, sw; path p_in, p_out, p_top; pair nw_dist, sw_dist, nw, sw; save cone_height; cone_height = 0.64; pickup pencircle scaled solfa_pen_thick; x1 = .5 [x2, x4]; bot y1 = -d; lft x2 = 0; y2 = cone_height [y1, y3]; rt x4 = w; y4 = y2; x3 = x1; top y3 = h; labels (range 1 thru 4); nw = unitvector (z2 - z1); sw = unitvector (z1 - z4); nw_dist = (nw rotated 90) * 0.5 solfa_pen_thick; sw_dist = (sw rotated 90) * 0.5 solfa_pen_thick; p_top := (z2 - sw_dist) .. (top z3){right} .. (z4 - nw_dist); p_in := (((z1 - nw_dist) -- (z2 - nw_dist)) intersectionpoint ((z1 - sw_dist) -- (z4 - sw_dist))) -- (((z1 - nw_dist) -- (z2 - nw_dist)) intersectionpoint ((z2 + sw_dist) .. {right}(bot z3))) .. bot z3 .. (((bot z3){right} .. (z4 + nw_dist)) intersectionpoint ((z1 - sw_dist) -- (z4 - sw_dist))) -- cycle; p_out := bot z1 .. (z1 + nw_dist) -- (z2 + nw_dist) .. lft z2 .. (z2 - sw_dist){direction 0 of p_top} & p_top & {direction infinity of p_top}(z4 - nw_dist) .. rt z4 .. (z4 + sw_dist) -- (z1 + sw_dist) .. cycle; charwx := charwd; charwy := cone_height [-chardp, charht]; if dir = -1: charwy := -charwy; fi; enddef; fet_beginchar ("Whole up tihead", "s0ti"); draw_ti_head (solfa_whole_width, 1); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Half up tihead", "u1ti"); draw_ti_head (solfa_half_width, 1); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Half down tihead", "d1ti"); draw_ti_head (solfa_half_width, -1); fill p_out; unfill p_in; fet_endchar; fet_beginchar ("Quart up tihead", "u2ti"); draw_ti_head (solfa_quarter_width, 1); fill p_out; fet_endchar; fet_beginchar ("Quart down tihead", "d2ti"); draw_ti_head (solfa_quarter_width, -1); fill p_out; fet_endchar; fet_endgroup ("noteheads"); % % we derive black_notehead_width# from the quarter head, % so we have to define black_notehead_width (pixel qty) % after the black_notehead_width# itself. % % Let's keep it outside the group as well. % define_pixels (black_notehead_width); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-braces-a.mf000066400000000000000000000003111404105311000243360ustar00rootroot00000000000000% % feta-braces-a.mf -- 256 smallest braces % % source file of the Feta (Font-En-Tja) music font % % (c) 1997--2009 Han-Wen Nienhuys % font_count := 0; input feta-beugel; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-braces-b.mf000066400000000000000000000003051404105311000243420ustar00rootroot00000000000000% % feta-braces-b.mf -- next 256 braces % % source file of the Feta (Font-En-Tja) music font % % (c) 1997--2009 Han-Wen Nienhuys % font_count := 1; input feta-beugel; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-braces-c.mf000066400000000000000000000003051404105311000243430ustar00rootroot00000000000000% % feta-braces-c.mf -- next 256 braces % % source file of the Feta (Font-En-Tja) music font % % (c) 1997--2009 Han-Wen Nienhuys % font_count := 2; input feta-beugel; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-braces-d.mf000066400000000000000000000003051404105311000243440ustar00rootroot00000000000000% % feta-braces-d.mf -- next 256 braces % % source file of the Feta (Font-En-Tja) music font % % (c) 1997--2009 Han-Wen Nienhuys % font_count := 3; input feta-beugel; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-braces-e.mf000066400000000000000000000003051404105311000243450ustar00rootroot00000000000000% % feta-braces-e.mf -- next 256 braces % % source file of the Feta (Font-En-Tja) music font % % (c) 1997--2009 Han-Wen Nienhuys % font_count := 4; input feta-beugel; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-braces-f.mf000066400000000000000000000003051404105311000243460ustar00rootroot00000000000000% % feta-braces-f.mf -- next 256 braces % % source file of the Feta (Font-En-Tja) music font % % (c) 1997--2009 Han-Wen Nienhuys % font_count := 5; input feta-beugel; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-braces-g.mf000066400000000000000000000003051404105311000243470ustar00rootroot00000000000000% % feta-braces-g.mf -- next 256 braces % % source file of the Feta (Font-En-Tja) music font % % (c) 1997--2009 Han-Wen Nienhuys % font_count := 6; input feta-beugel; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-braces-h.mf000066400000000000000000000003051404105311000243500ustar00rootroot00000000000000% % feta-braces-h.mf -- next 256 braces % % source file of the Feta (Font-En-Tja) music font % % (c) 1997--2009 Han-Wen Nienhuys % font_count := 7; input feta-beugel; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-braces-i.mf000066400000000000000000000003051404105311000243510ustar00rootroot00000000000000% % feta-braces-i.mf -- next 256 braces % % source file of the Feta (Font-En-Tja) music font % % (c) 1997--2009 Han-Wen Nienhuys % font_count := 8; input feta-beugel; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-din-code.mf000066400000000000000000000405201404105311000243510ustar00rootroot00000000000000% feta-din-code.mf -- implement dynamic signs % % part of LilyPond's pretty-but-neat music font % % source file of the Feta (not the Font-En-Tja) music font % % (c) 1997--2009 Jan Nieuwenhuizen ex# := (dynamic_design_size / 2.4) * pt#; descender# := 0.5 ex#; ascender# := 0.72 ex#; staffspace# := 1.75 / 2.0 * ex#; horizontal_space# := .66 ex#; font_x_height ex#; font_normal_space horizontal_space#; define_pixels (staffspace, linethickness, ex, descender, ascender); % % TODO: blot diameter should be fixed, not scalable. % save serif_thick, med_thick, bottom_blot; serif_thick# = 1.1 linethickness#; define_blacker_pixels (serif_thick); med_thick = round (1.5 linethickness); bottom_blot = 1.3 serif_thick; code := 32; fet_beginchar ("Space", "space"); set_char_box (0, horizontal_space#, 0, ex#); fet_endchar; % % Couldn't find many z examples. This one is loosely inspired % by a sfz from Mueller Etuden fuer Horn (Edition Hofmeister). % code := 121; fet_beginchar ("dynamic z", "z"); save thin_thick, top_narrow, over_shoot; save bot_thick; set_char_box (0, .98 ex#, 0, 0.9 ex#); thin_thick = serif_thick; top_narrow = thin_thick; top_overshoot = .8 serif_thick; top_thick = .3 h; bot_thick = .2 ex; bot_overshoot = serif_thick; x0 = top_narrow; y0 = .9 [y1r, y1l]; penpos1 (top_thick, 80); y1l = .72 h; x1r = .34 ex; x2 = .66 ex; y2 = y1r - top_overshoot; y3 = h - .7 thin_thick; x3 = w - .6 top_narrow - .5 thin_thick; y4 = .5 thin_thick; x4 = .5 thin_thick; penpos3 (thin_thick, angle (z3 - z4) + 90); penpos4 (thin_thick, angle (z3 - z4) + 90); penpos5 (bot_thick, 70); x5l =.25 ex; y5l = .4 bot_overshoot; penpos6 (3/2 bot_thick, 70); y6l = -bot_overshoot; x6 = w - 3 top_narrow; x7 = w; y7 = .82 [y8r, y6r]; penpos8 (thin_thick, 20); x8r = w - .35 top_narrow; y8r = .45 h; penlabels (range 0 thru 8); % pickup pencircle scaled 1; % draw fill z0{down} .. z1l{dir (10)} .. simple_serif (z3l, z3r, 90) .. z2{left} .. z1r{left} ..tension 1.2.. cycle; % draw fill z3l -- z3r -- z4r -- z4l -- cycle; % draw fill simple_serif (z4r, z4l, 90) .. z5l{right} .. z6l{right} .. z7{up} .. simple_serif (z8r, z8l, 90) .. z6r{left} .. z5r{left} .. cycle; fet_endchar; % forte f, grabbed from Ed Breitkopf Mozart horn concerto 3. % % NOTES: % % * the bulbs are open % % * blotting around the serif % % TODO: insert blots around the serif % slant_angle = 20; code := 101; fet_beginchar ("dynamic f", "f"); save left_angle, right_angle; save serif_length, serif_eccentricity; save f_thick; save bulb_thick, bulb_diam, fill_up; save slant; save p; path p; set_char_box (0, 1.1 ex#, descender#, ex# + ascender#); bulb_diam = 7.5 / 40 ex; bulb_thick = 8.5 / 40 ex; fill_up = 1.5 serif_thick; left_angle = slant_angle - 6; right_angle = slant_angle - 3; f_thick = 7/16 ex; serif_length = 0.96 ex; serif_eccentricity = 0.01 ex; % z1 is the `base point' z1 = (0.2 ex, -serif_thick); penpos2 (f_thick, 0); y2 = y1 + ex; z2l = z1 + whatever * dir (90 - left_angle); penpos3 (med_thick, -90); y3l = y1 + ex + ascender; x3l = x1 + ex; penpos4 (bulb_thick, -20); z3r = whatever [z4r, z4l]; x4l - x3l = 1/10 ex; penpos5 (bulb_thick, -45); x5r = 0.1 [x4l, x4r]; y5l = y4l - bulb_diam; z6 = z2r + whatever * dir (90 - right_angle); y6 = y1 + 3/8 ex; penpos7 (med_thick, -90); x7 = x1 - 1/4 ex; y7r = y1 -descender; penpos8 (bulb_thick, 160); x8l = x7l - 1/10 ex; z7l = whatever [z8r, z8l]; penpos9 (bulb_thick, 135); x9r = 0.1 [x8l, x8r]; y9l = y8l + bulb_diam; labels (1, 6, 9); penlabels (2, 3, 4, 5, 7, 8, 9); % pickup pencircle scaled 1; % draw fill z1 -- z2l{z2l - z1} ..tension 1.1.. z3l{right} .. z4r{down} .. z5r{left} .. z5l{up} ..tension 0.8.. z4l{up} .. z3r{left} ..tension 1.1.. z2r{z6 - z2r} -- z6{z6 - z2r} ..tension 1.25.. z7r{left} .. z8r{up} .. z9r{right} .. z9l{down} ..tension 0.8.. z8l{down} .. z7l{right} .. {z2l - z1}cycle; x13 - x14 = serif_length; y13 = y14; y14 = y2; 0.5 [x13, x14] = x2 + serif_eccentricity; draw_rounded_block (z14 - (0, 0.7 serif_thick), z13 + (0, 0.7 serif_thick), 1.4 serif_thick); labels (13, 14); fet_endchar; % % Notes: % % - The `s' is trapezoidal (i.e., narrower at the top). % % - The white space is differently shaped at the top (the bulb's inner % curve is filled up). % % - Less heavy than the `f' and `p' signs. % code := 114; fet_beginchar ("dynamic s", "s"); save left_angle, right_angle; save s_thick, s_thin; save bulb_diam, bulb_len; save over_shoot; save base_point; pair base_point; set_char_box (0, 17/24 ex#, 0, ex#); over_shoot = 0; % .2 serif_thick; bulb_diam = 11/70 ex; bulb_len = 1.0 bulb_diam; left_angle = slant_angle - 2; right_angle = slant_angle - 11; s_thick = 16/70 ex; s_thin = serif_thick; base_point = (0, 0); penpos1 (bulb_diam, -45); z1 = 0.35 [z2l, z2r] + bulb_len * dir (45); penpos2 (bulb_diam, -25); y2l = 0.845 [y7r, y3r]; z2l = base_point + whatever * dir (90 - left_angle); penpos3 (s_thin, 100); x3l = 1/2 w; y3l = ypart base_point - over_shoot; penpos4 (s_thick, 25); y4l = y1r; z4r = base_point + (w, 0) + whatever * dir (90 - right_angle); penpos5 (s_thick, 40); z5 = z3l + whatever * dir (90 - right_angle); y5 = 0.48 [y7r, y3r]; penpos6 (s_thick, 25); z6l = base_point + whatever * dir (90 - left_angle); y6r = y9l; penpos7 (.9 s_thin, 110); z7l = 0.45 [z6r, z8l] + whatever * dir (90 - left_angle); y7r = h + over_shoot; penpos8 (.9 bulb_diam, -25); z8 = .6 [z4l, z4r] + whatever * dir (90 - right_angle); y8r = 0.23 [y7r, y3r]; penpos9 (.9 bulb_diam, -45); z9 = .4 [z8r, z8l] + .9 bulb_len * dir (-135); penlabels (range 1 thru 9); % pickup pencircle scaled 1; % draw fill z2l{down} .. z3l{right} .. z4r{up} .. z5r .. z6r{up} .. z7l{right} % .. z8l{down} .. z9l{dir (-125)} .. z9r{right} .. z7r{left} .. z6l{down} .. z5l .. z4l{down} .. z3r{left} .. z2r{up} .. z1r{up} .. z1l{left} .. cycle; fet_endchar; % for `p' and `m' save slant; slant := ypart (dir (slant_angle)); % % Piano `p', grabbed from Ed Breitkopf Mozart horn concerto 3. % % Notes: % % * There is no dishing in the serif (but we do it anyway). % % * The cheek is a little fatter than the stem. % % * The slant is extreme: 20 degrees. % % * The twiddle (what's-it-called) is a slightly darker than the serif. % % * The hole in the cheek has a straight right side. % % * Corners are filled up. % code := 111; fet_beginchar ("dynamic p", "p") % TODO: w really is 13/12 ex % but should do kerning save twiddle_thick, stem_thick, cheek_thick, cheek_width; save fill_up, straigh_len; save serif, dishing_angle, p, tmp; save cheek_medium, left_serif_protrude, right_serif_protrude; save lower_overshoot; save blot_t, corner_t; path serif, p; pair tmp, updir; set_char_box (0, 15/12 ex#, descender#, 1.0 ex#); twiddle_thick = med_thick; cheek_medium = 1/6 ex; dishing_angle = 5; fill_up = 1.5 serif_thick; straigh_len = 0.5 ex; lower_overshoot = .3 serif_thick; stem_thick = 2/6 ex; cheek_thick = 13/32 ex; cheek_width = 0.72 ex; left_serif_protrude = 18/60 ex; right_serif_protrude = 15/60 ex; currenttransform := currenttransform slanted slant; penpos1 (twiddle_thick, -slant - 5); penpos2 (cheek_medium, 90 - slant); penpos3 (cheek_medium, 90 - slant); x4r - x4l = cheek_thick; penpos4 (whatever, 0); penpos5 (whatever, -38); penpos6 (stem_thick, 0); penpos17 (straigh_len, 90 - slant); whatever [z17l, z17r] = z4l; y17 = 7/16 ex; x6l = 0; y6l = -descender + serif_thick / 2; z1l = z6l - whatever * dir (110); y1r = 0.5 ex; y2r = ex; z7 = whatever * up + z6l; y7 = 43/60 ex; z2l = whatever * up + 0.3 [z7, z1r]; y8 = 0.9 [y7, y2l]; z8 = 2/3 [z6l, z6r] + whatever * up; y3r = ex; z3l = 0.58 [(stem_thick, -descender), (stem_thick + cheek_width - cheek_thick, -descender)] + whatever * up; y4r = .38 ex; z4r = whatever * up + (stem_thick + cheek_width, -descender); z5l = whatever * up + z3l; y5r = -lower_overshoot; y5l = y5r + cheek_medium * ypart dir (55); z9 = z6r + whatever * up; y9 = .2 [y5l, y5r]; p := z2r{right} .. {dir (-60)}z8{dir 60} .. z3r{right} .. z4r{down} ..tension 1.1.. z5r{left} .. {curl 1}z9 -- z6r -- z6l -- z7{up} .. z2l{left} ..tension 1.2.. simple_serif (z1r, z1l, -90) .. cycle; blot_t := 0.13; corner_t := xpart (p intersectiontimes z9); % pickup pencircle scaled 1; % draw fill subpath (0, corner_t - 2 blot_t) of p .. subpath (corner_t + blot_t, length p) of p .. cycle; y12 = 0.5 ex; z12 = z6r + whatever * up; unfill z17l ..tension 1.5.. z17r .. z3l{left} ..tension 1.05.. z12{down} ..tension 1.05.. z5l{right} .. cycle; penlabels (1, 2, 3, 4, 5, 6, 17); labels (7, 8, 9); pickup pencircle scaled serif_thick; lft x11 = -left_serif_protrude; rt x10 = stem_thick + right_serif_protrude; bot y10 = bot y11 = -descender; z15 = z6l + up * fill_up; z16 = z6r + up * 1.2 fill_up; % Since pens are not affected by currenttransform we directly % transform the necessary points, then simulating the pen with % an outline while using the identity transformation. forsuffixes $ = 7, 10, 11, 15, 16: tmp := z$ transformed currenttransform; x$ := xpart tmp; y$ := ypart tmp; endfor; currenttransform := identity; updir = z7 - z15; serif := simple_serif (z10, z11, dishing_angle); penpos10 (serif_thick, -dishing_angle - 90); penpos11 (serif_thick, dishing_angle - 90); penpos13 (serif_thick, angle (direction 0.05 of serif) + 90); penpos14 (serif_thick, angle (direction 0.85 of serif) + 90); z13 = point 0.05 of serif; z14 = point 0.85 of serif; penlabels (10, 11, 13, 14); labels (15, 16); % draw fill z15{-updir} .. z14l{direction 0.85 of serif} .. z11l{-dir (dishing_angle)} .. z11r{dir (dishing_angle)} .. z14r{-direction 0.85 of serif} .. z13r{-direction 0.05 of serif} .. z10r{dir (-dishing_angle)} .. z10l{-dir (-dishing_angle)} .. z13l{direction 0.05 of serif} .. z16{updir} -- cycle; fet_endchar; % % NOTES: % % * Right stem is fatter and more straight than the left two stems. % % * The twiddle at the left is similar to the `p' twiddle. % % * The bottoms of the stems are blotted. % % % This is cut & paste programming. Somehow three `i' shapes in two % characters (`p' and `m') -- doesn't seem worth the trouble of writing % a macro. % code := 108; fet_beginchar ("dynamic m", "m"); save i_thick, i_angle, i_twiddle_thick; save i_twiddle_start_angle, i_twiddle_start_y; save i_twiddle_end_angle, i_left_space; save idir, center, right_ending; save overshoot; save p; pair center, idir, right_ending; path p; set_char_box (0, 1.5 ex#, 0, 1.0 ex#); % should share code with p for twiddle. overshoot = .25 serif_thick; i_thick := 21/80 ex; i_twiddle_thick = 1.2 serif_thick; i_twiddle_start_y = 8/16 ex; i_twiddle_start_angle = 0; i_twiddle_end_angle := 35; center = (0, 0); currenttransform := currenttransform slanted slant; i_angle := 0; idir := dir (90 - i_angle); i_left_space = 16/80 ex; penpos1 (i_twiddle_thick, -i_twiddle_start_angle); y1 = i_twiddle_start_y; z1r = center - (i_left_space, 0) + whatever * idir; y2l = ex + overshoot; z2l = .08 [z3l, z3r] + whatever * idir; z2r = 5/8 [z1r, z3l] + whatever * idir; y2r = y5l + 1/9 ex; z2 = 1/2 [z2l, z2r]; penpos3 (i_thick, 0); y3 = 0.5 bottom_blot + ypart center; z3l = center + whatever * idir; penpos4 (i_thick - bottom_blot, 0); y4 = ypart center; z4 - z3 = whatever * idir; penpos5 (i_thick, 0); z5 = z4 + whatever * idir; y5 = 55/80 ex; fill simple_serif (z1l, z1r, 90) ..tension 1.2.. z2r{right} .. z5l{z3 - z5} -- z3l{z3 - z5} .. z4l{right} -- z4r{right} .. z3r{z5 - z3} -- z5r{z5 - z3} ..tension 1.2.. z2l{left} .. cycle; right_ending := z5r; penlabels (1, 2, 3, 4, 5); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% clearxy; i_angle := -3.2; idir := dir (90 - i_angle); i_left_space := 14/80 ex; penpos1 (serif_thick, -i_twiddle_start_angle); z1r = right_ending; y2l = ex+ overshoot; z2l = .08 [z3l, z3r] + whatever * idir; z2r = 5/8 [z1r, z3l] + whatever * idir; y2r = y5l + 1/9 ex; z2 = 1/2 [z2l, z2r]; penpos3 (i_thick, 0); y3 = 0.5 bottom_blot + ypart center; z3l = z5l + whatever * idir; penpos4 (i_thick - bottom_blot, 0); y4 = ypart center; z4 - z3 = whatever * idir; penpos5 (i_thick, 0); z5l = right_ending + (i_left_space, 0); fill simple_serif (z1l, z1r, 90) ..tension 1.05.. z2r{right} .. z5l{z3 - z5} -- z3l .. z4l{right} -- z4r{right} .. z3r{z5 - z3} -- z5r{z5 - z3} ..tension 1.2.. z2l{left} .. cycle; right_ending := z5r; penlabels (1, 2, 3, 4, 5); %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% clearxy; i_thick := 23/80 ex; i_angle := -6; idir := dir (90 - i_angle); i_left_space := 14/80 ex; penpos1 (serif_thick, -i_twiddle_start_angle); z1r = right_ending; y2l = ex + overshoot; z2l = .08 [z3l, z3r] + whatever * idir; z2r = 5/8 [z1r, z3l] + whatever * idir; y2r = y5l + 1/9 ex; z2 = 1/2 [z2l, z2r]; penpos3 (whatever, 20); y3l = 1/8 ex + ypart center; z3l = z7l + whatever * idir; z3r = z7r + whatever * idir; penpos5 (whatever, 10); z5l = right_ending + (i_left_space, 0); z5r = z7r + whatever * idir; penpos6 (serif_thick, -i_twiddle_end_angle); y6l = 23/80 ex + ypart center; z6l = 1.6 [z3l, z3r] + whatever * idir; penpos7 (i_thick, 0); y7 = 0; z7l = z5l + whatever * idir; z8 = z7 - (0, overshoot); fill simple_serif (z1l, z1r, 90) ..tension 1.05.. z2r{right} .. z5l{z3 - z5} -- z3l{z3 - z5} .. z8{right} .. simple_serif (z6r, z6l, 90) ..tension 0.85.. z3r{z5 - z3} -- z5r{z5 - z3} ..tension 1.2.. z2l{left} .. cycle; penlabels (range 1 thru 8); fet_endchar; code := 113; fet_beginchar ("dynamic r", "r"); save base_point, stem_thick, bulb_diam; save twiddle_thick, attach_len, overshoot, taille; pair base_point; set_char_box (0, .75 ex#, 0, ex#); stem_thick =.26 ex; bulb_diam = .30 ex; twiddle_thick = 1.1 serif_thick; overshoot = .5 serif_thick; taille = -0.3 serif_thick; attach_len + bulb_diam / 2 + stem_thick = w; base_point = (0, 0); currenttransform := identity slanted ypart (dir (15)); penpos1 (stem_thick, 0); x1l = 0; y1l = .5 bottom_blot; penpos2 (stem_thick, 0); x2l = x1l; y2l - y1l = 36/47 ex; penpos3 (twiddle_thick, -20); x3r = x2l - .2 ex; y3 = .77 ex; x4l = -0.1 [x1l, x1r]; y4l = ex + overshoot; x4r = 0.62 [x3r, x2l]; y4r = 0.5 [y4l, y2l]; penpos5 (whatever, -74); y5l - y5r = bulb_diam; y5l = ex + overshoot; x5 = x2r + attach_len; penpos6 (bulb_diam, 0); z6 = z5; % z7 = z6l + taille * dir (180); z7 = z6 + .4 * bulb_diam * dir (-125); z8 = 9/10 [z1r, z2r]; x9 = 0.15 [x1r, x1l]; y9 = y4l - .12 ex; penpos10 (stem_thick - bottom_blot, 0); x10 = x1; y10 = 0; % pickup pencircle scaled 1; % draw fill z1r{down} .. z10r{left} -- z10l{left} .. z1l{up} -- z2l{up} .. z4r{left} ..tension 1.2.. {down}simple_serif (z3r, z3l,-90){up} ..tension 0.95.. z4l .. {curl 1}z9{curl 1} .. z5l .. z6r{dir (-80)} .. z5r{left} .. z7 .. z6l ..tension 1.2.. z8{down} -- cycle; penlabels (range 1 thru 10); fet_endchar; %%% KERNING ligtable "m": "p" kern 0.2 ex#, "f" kern -0.1 ex#; ligtable "f": "f" kern -0.13 ex#; ligtable "r": "f" kern 0.1 ex#; %% notes from old dyn code. % `f' obviously has a _lot_ bigger slant than `p' (see Wanske p.239). % However; perhaps we need two f symbols: % - a super-slanted one used in `f', `mf', `sfz', `sf' % - a more normal-slanted in `ff', `fff', `fp', `fp' (see Wanske p.241) % % Looking at professionally typeset music reveals that typesetters % are somewhat unsure about slanting in `mf', `fp', `sfz' % % `f' and `p' (in any combination) are a lot (factor two) fatter than % `s', `m', and `z'. Sometimes the `m' and `z' are a bit fatter than % `s'. % % Chester, Breitkopf suggest smaller sizes of these other glyphs, % using the x-height as reference point. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-eindelijk.mf000066400000000000000000000316021404105311000246260ustar00rootroot00000000000000% feta-eindelijk.mf -- implement rest symbols -*-Fundamental-*- % % part of LilyPond's pretty-but-neat music font % % source file of the Feta (not the Font-En-Tja) music font % % (c) 1997--2009 Jan Nieuwenhuizen fet_begingroup ("rests"); save block_rest_y, block_rest_x; save breve_rest_y, breve_rest_x; breve_rest_y# = staff_space#; breve_rest_x# = 3/5 staff_space#; block_rest_y# = 5/8 staff_space#; block_rest_x# = 3/2 staff_space#; define_whole_pixels (block_rest_y, block_rest_x); define_whole_pixels (breve_rest_y, breve_rest_x); def block_rest = draw_block ((0, 0), (block_rest_x, block_rest_y)); enddef; fet_beginchar ("whole rest", "0"); set_char_box (0, block_rest_x#, block_rest_y#, 0); block_rest; currentpicture := currentpicture shifted (0, -block_rest_y + feta_space_shift); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("half rest", "1"); set_char_box (0, block_rest_x#, 0, block_rest_y#); block_rest; draw_staff (-2, 2, 0); fet_endchar; % % should use ledgerline thickness? % fet_beginchar ("whole rest (outside staff)", "0o"); set_char_box (0, block_rest_x#, block_rest_y#, ledgerlinethickness# / 2); block_rest; currentpicture := currentpicture shifted (0, -block_rest_y); pickup pencircle scaled ledgerlinethickness; y5 = y6 = 0; lft x5 = -b - block_rest_y; rt x6 = w + block_rest_y; draw_gridline (z5, z6, ledgerlinethickness_rounded); draw_staff (-2, 2, -3); fet_endchar; fet_beginchar ("half rest (outside staff)", "1o"); set_char_box (0, block_rest_x#, ledgerlinethickness# / 2, block_rest_y#); block_rest; pickup pencircle scaled ledgerlinethickness; y5 = y6 = 0; lft x5 = -b - block_rest_y; rt x6 = w + block_rest_y; draw_gridline (z5, z6, ledgerlinethickness_rounded); draw_staff (-2, 2, 3); fet_endchar; fet_beginchar ("maxima rest", "M3"); set_char_box (0, 3 breve_rest_x#, breve_rest_y#, breve_rest_y#); draw_block ((0, -breve_rest_y + feta_shift), (breve_rest_x, breve_rest_y)); addto currentpicture also currentpicture shifted (2 breve_rest_x, 0); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("longa rest", "M2"); set_char_box (0, breve_rest_x#, breve_rest_y#, breve_rest_y#); draw_block ((0, -breve_rest_y + feta_shift), (breve_rest_x, breve_rest_y)); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("breve rest", "M1"); set_char_box (0, breve_rest_x#, 0, breve_rest_y#); draw_block ((0, 0), (breve_rest_x, breve_rest_y)); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("Quarter rest", "2"); save alpha, yshift, height; save xcenter; yshift# := -1.25 staff_space#; height# := 2.8125 staff_space#; define_pixels (yshift, height); alpha := -50; thick := 1/4 staff_space; rthin := 1.25 linethickness; xcenter := -0.1 staff_space; rthick := 2 thick + rthin; set_char_box (0, 0.95 staff_space#, -yshift#, yshift# + height#); save ne, nw, se, sw; pair ne, nw, se, sw; se = dir alpha; ne = dir (alpha + 90); nw = dir (alpha + 180); sw = dir (alpha + 270); penpos1 (rthin, alpha + 90); penpos2 (5/4 rthick, alpha); penpos4 (5/4 rthick, alpha); penpos5 (rthin, alpha + 90); penpos3 (3/4 rthick, alpha); % penpos6 (5/8 rthick, alpha); penpos6 (2/3 rthick, alpha); penpos7 (1/2 rthin, alpha + 90); z10 = z2l + 1/2 rthin * sw; % z11 = z4l + 1/2 rthin * sw; z11 = z4l + 1/2 rthin * sw + 1/2 rthin * se; z12 = z4r + 1/2 rthin * ne; % z13 = z2r + 1/2 rthin * ne; z13 = z2r + 1/2 rthin * ne + 1/2 rthin * nw; y1r = h; x1l = xcenter + 1/3 staff_space; z2r = z1 + staff_space * se; z3 = 1/2 [z2, z4]; x4 = xcenter + 3/8 staff_space; y4 = 0; y4l := vround y4l; z5 = round (z4l + 1.3 staff_space * se) + feta_offset; x6l = x4l; y6l = vround y4r; x6r := hround x6r + xpart feta_offset; y6r := vround y6r + ypart feta_offset; x7 = hround (xcenter + 2/5 staff_space) + xpart feta_offset; y7 = -d; save before, after; path before, after; before = z11{se} .. {se}z5l; after = z5r{nw} ..tension1.4.. z6l; (u, v) = before intersectiontimes after; fill z1l{se} .. {se}z10 .. z3l .. subpath (0, u) of before .. subpath (v, infinity) of after ..tension1.4.. {se}z7l .. z7r{nw} ..tension1.4.. z6r ..tension1.4.. {se}z5l .. z5r{nw} .. {nw}z12 .. z3r .. z13{nw} .. {nw}z1r .. cycle; penlabels (1, 2, 3, 4, 5, 6, 7); penlabels (10, 11, 12, 13); draw_staff (-2, 2, 0); fet_endchar; % % Notes about 8th rest: % % * 8th rest should be no wider than the black note head % % * The inside corner of the horizontal portion is usually a little blotted. % % * The top of the crook chops off the vertical brush % % * The crook widens a little % % * The bottom of the brush is slightly flat, as it has to align with % stafflines if it is inside the staff. % % * The top of the brush usually is a little lower than the top of bulb. % % * The bulb can vary. Sometimes it is open (like in the clefs). Sometimes % it is closed, or even a flare. % % from an email to Rune Zedeler: % % % For example, the 8th rest was vaguely based on a book with trumpet % studies by Duhem, and by Baerenreiters cello suites. I included my % findings in a comment in the mf file. One of the things that I tried % to do was make the rest a little lighter and narrower than the black % note head. I think this looks better in polyphonic music, when the % rest is below a head from a different voice. % save bulb_diam, thin, thick; bulb_diam# := 0.64 staff_space#; thin# := 1.4 linethickness# - 0.02 staff_space#; thick# := 2.2 linethickness#; crook_thin := 0.5 linethickness + 0.08 staff_space; lower_brush := 1.5 linethickness; define_pixels (bulb_diam); define_whole_blacker_pixels (thin, thick); % % PAT is a compact way to pass the brush shape to the routine. % def draw_rest_bulb (expr ycenter, ycut, pat, stretch, show_labels) = begingroup; save x, y, pt, res; z1l = point 0 of pat; z2l = point 1 of pat; z2r = point 3 of pat; z1r = point 4 of pat; z1 = 0.5 [z1l, z1r]; z2 = 0.5 [z2l, z2r]; y10 = ycenter; z10 = whatever [z2, z1] + left * bulb_diam * stretch; % this enforces similar bulb shapes for lower resolutions x10 := hround x10; z3 = z10 + bulb_diam / 2.15 * dir (-72); y3 := hround y3; z5 = z10 + up * bulb_diam / 2 + right * linethickness / 3; y5 := hround y5; z7 = 0.5 [z4, z5] + crook_thin * (0.45, 0.4) / 1.3; x8 = hround (x10 - 0.4 bulb_diam); y8 = y10 + 0.25 linethickness; z6 = whatever [z1l, z2l]; z6 = z3 + whatever * dir (12); z4 = z3 + whatever * (z6 - z3) + 1.1 crook_thin * (unitvector (z6 - z3) rotated 90); x4 = x10 + bulb_diam * .62; y4 := vround y4; (pt, whatever) = pat intersectiontimes ((0, ycut) -- (w, ycut)); path res; res = point pt of pat {-direction pt of pat} ..tension 2.. z4{left} ..tension 0.9.. z7 .. z5{left} .. z8 .. z3{right} .. {curl 0.2}z6; if show_labels = 1: labels (3, 4, 5, 6, 7, 8, 10); fi; res endgroup; enddef; def draw_eighth_rest (expr show_labels) = save ycenter; save pat, bulb; path pat, bulb; set_char_box (0, 1.0 staff_space#, 1.0 staff_space# + 0.5 linethickness#, 0.5 staff_space# + bulb_diam# / 2); penpos1 (thick, 0); penpos2 (thin, 10); y1 = -staff_space_rounded; y2 = h - vround lower_brush; x2r = w; z2 = z1 + whatever * dir (72); z9 = z2 + 0.5 linethickness * dir (angle (z2 - z1) - 10); y9 := vround y9; x1l := hround x1l; x1r := hround x1r; x1 := .5 [x1l, x1r]; if show_labels = 1: penlabels (1, 2); labels (9); fi; pat = z1l -- z2l .. z9 .. z2r -- z1r .. cycle; bulb = draw_rest_bulb (0.5 staff_space, y2r, pat, 1.0, show_labels); fill simple_serif (z1l, z1r, 40) -- z2r .. z9 .. bulb -- z1l .. cycle; draw_staff (-2, 2, 0); enddef; fet_beginchar ("Classical quarter rest", "2classical"); draw_eighth_rest (0); currentpicture := currentpicture xscaled -1 shifted (w, 0); fet_endchar; fet_beginchar ("8th rest", "3"); draw_eighth_rest (1); fet_endchar; % % TODO: the X dimensions of the rests are fucked up: % they can collide with augmentation dots. % fet_beginchar ("16th rest", "4"); save ycenter; save pat, bulb_a, bulb_b; path pat, bulb_a, bulb_b; set_char_box (0, 1.2 staff_space#, 2.0 staff_space# + 0.5 linethickness#, 0.5 staff_space# + bulb_diam# / 2); penpos1 (thick, 0); penpos2 (thin, 10); y1 = -2 staff_space_rounded; y2 = h - vround lower_brush; x2r = w; z2 = z1 + whatever * dir (74); z9 = z2 + 0.5 linethickness * dir (angle (z2 - z1) - 10); y9 := vround y9; x1l := hround x1l; x1r := hround x1r; x1 := .5 [x1l, x1r]; pat = z1l -- z2l .. z9 .. z2r -- z1r .. cycle; bulb_a = draw_rest_bulb (.5 staff_space, y2r, pat, 0.98, 1); bulb_b = draw_rest_bulb (.5 staff_space - staff_space_rounded, hround ((-0.5 + 0.2) * staff_space), pat, 1.02, 1); fill simple_serif (z1l, z1r, 40) -- z2r .. z9 .. bulb_a -- bulb_b -- z1l .. cycle; penlabels (1, 2); labels (9); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("32th rest", "5"); save ycenter; save pat, bulb_a, bulb_b, bulb_c; path pat, bulb_a, bulb_b, bulb_c; set_char_box (0, 1.3 staff_space#, 2.0 staff_space# + 0.5 linethickness#, 1.5 staff_space# + bulb_diam# / 2); penpos1 (thick, 0); penpos2 (thin, 10); y1 = -2 staff_space_rounded; y2 = h - vround lower_brush; x2r = w; z2 = z1 + whatever * dir (76); z9 = z2 + 0.5 linethickness * dir (angle (z2 - z1) - 10); y9 := vround y9; x1l := hround x1l; x1r := hround x1r; x1 := .5 [x1l, x1r]; pat = z1l -- z2l .. z9 .. z2r -- z1r .. cycle; bulb_a = draw_rest_bulb (.5 staff_space + staff_space_rounded, y2r, pat, 0.96, 1); bulb_b = draw_rest_bulb (.5 staff_space, hround ((0.5 + 0.2) * staff_space), pat, 1.00, 1); bulb_c = draw_rest_bulb (.5 staff_space - staff_space_rounded, hround ((-0.5 + 0.21) * staff_space), pat, 1.04, 1); fill simple_serif (z1l, z1r, 40) -- z2r .. z9 .. bulb_a -- bulb_b -- bulb_c -- z1l .. cycle; penlabels (1, 2); labels (9); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("64th rest", "6"); save ycenter; save pat, bulb_a, bulb_b, bulb_c, bulb_d; path pat, bulb_a, bulb_b, bulb_c, bulb_d; set_char_box (0, 1.4 staff_space#, 3.0 staff_space# + 0.5 linethickness#, 1.5 staff_space# + bulb_diam# / 2); penpos1 (thick, 0); penpos2 (thin, 10); y1 = -3 staff_space_rounded; y2 = h - vround lower_brush; x2r = w; z2 = z1 + whatever * dir (78); z9 = z2 + 0.5 linethickness * dir (angle (z2 - z1) - 10); y9 := vround y9; x1l := hround x1l; x1r := hround x1r; x1 := .5 [x1l, x1r]; pat = z1l -- z2l .. z9 .. z2r -- z1r .. cycle; bulb_a = draw_rest_bulb (.5 staff_space + staff_space_rounded, y2r, pat, 0.94, 1); bulb_b = draw_rest_bulb (.5 staff_space, hround ((0.5 + 0.20) * staff_space), pat, 0.98, 1); bulb_c = draw_rest_bulb (.5 staff_space - staff_space_rounded, hround ((-0.5 + 0.21) * staff_space), pat, 1.02, 1); bulb_d = draw_rest_bulb (.5 staff_space - 2 staff_space_rounded, hround ((-1.5 + 0.22) * staff_space), pat, 1.06, 1); fill simple_serif (z1l, z1r, 40) -- z2r .. z9 .. bulb_a -- bulb_b -- bulb_c -- bulb_d -- z1l .. cycle; penlabels (1, 2); labels (9); draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("128th rest", "7"); save ycenter; save pat, bulb_a, bulb_b, bulb_c, bulb_d, bulb_e; path pat, bulb_a, bulb_b, bulb_c, bulb_d, bulb_e; set_char_box (0, 1.5 staff_space#, 3.0 staff_space# + 0.5 linethickness#, 2.5 staff_space# + bulb_diam# / 2); penpos1 (thick, 0); penpos2 (thin, 10); y1 = -3 staff_space_rounded; y2 = h - vround lower_brush; x2r = w; z2 = z1 + whatever * dir (80); z9 = z2 + 0.5 linethickness * dir (angle (z2 - z1)- 10); y9 := vround y9; x1l := hround x1l; x1r := hround x1r; x1 := .5 [x1l, x1r]; pat = z1l -- z2l .. z9 .. z2r -- z1r .. cycle; bulb_a = draw_rest_bulb (.5 staff_space + 2 staff_space_rounded, y2r, pat, 0.92, 1); bulb_b = draw_rest_bulb (.5 staff_space + staff_space_rounded, hround ((1.5 + 0.20) * staff_space), pat, 0.96, 1); bulb_c = draw_rest_bulb (.5 staff_space, hround ((0.5 + 0.21) * staff_space), pat, 1.0, 1); bulb_d = draw_rest_bulb (.5 staff_space - staff_space_rounded, hround ((-0.5 + 0.22) * staff_space), pat, 1.04, 1); bulb_e = draw_rest_bulb (.5 staff_space - 2 staff_space_rounded, hround ((-1.5 + 0.23) * staff_space), pat, 1.08, 1); fill simple_serif (z1l, z1r, 40) -- z2r .. z9 .. bulb_a -- bulb_b -- bulb_c -- bulb_d -- bulb_e -- z1l .. cycle; penlabels (1, 2); labels (9); draw_staff (-2, 2, 0); fet_endchar; fet_endgroup ("rests"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-generic.mf000066400000000000000000000024311404105311000243020ustar00rootroot00000000000000% % feta-generic.mf -- implement generic stuff: include lots of files, but don't % set dims. % % source file of the Feta (defintively not an abbreviation for Font-En-Tja) % music font % % (c) 1997--2009 Han-Wen Nienhuys % if test = -1: mode := smoke; fi staffsize# := design_size * pt#; mode_setup; input feta-macros; input feta-params; font_x_height staff_space#; %% this is a fallback so we can run the font without including feta-bolletjes. black_notehead_width# := 1.0 staff_space#; fet_beginfont ("feta", design_size, "fetaMusic"); if test = 0: input feta-eindelijk; input feta-toevallig; input feta-arrow; input feta-puntje; input feta-bolletjes; input feta-schrift; input feta-banier; input feta-klef; input feta-timesig; input feta-pendaal; input feta-haak; input feta-accordion; else: input feta-test-generic.mf; fi autometric_parameter ("staffsize", staffsize#); autometric_parameter ("stafflinethickness", stafflinethickness#); autometric_parameter ("staff_space", staff_space#); autometric_parameter ("linethickness", linethickness#); autometric_parameter ("black_notehead_width", black_notehead_width#); autometric_parameter ("ledgerlinethickness", ledgerlinethickness#); autometric_parameter ("blot_diameter", blot_diameter#); fet_endfont; muse-4.0.0/src/share/scoreglyphs/feta-original/feta-haak.mf000066400000000000000000000033161404105311000235750ustar00rootroot00000000000000% feta-haak.mf -- implement bracket tips -*-Fundamental-*- % % part of LilyPond's pretty-but-neat music font % % source file of the Feta (not the Font-En-Tja) music font % % (c) 2005--2009 Han-Wen Nienhuys fet_begingroup ("brackettips"); def draw_bracket (expr thick_sharp, width_sharp, updown) = begingroup; save p, thin, thick, flattening, width, hair, flare; path p; pair tipdir; flattening = 0.72; thick# = thick_sharp; flare# = flattening * width#; width# = width_sharp; define_pixels (thick, width, flare); if updown = 1: set_char_box (0, width#, .5 thick#, flare#); else: set_char_box (0, width#, flare#, .5 thick#); fi; thin = 2 linethickness; hair = 1.25 linethickness; pickup pencircle scaled thin; y7 = y1; x7 = x2; z2 = z4r + (thin, 0); penpos3 (thin, 0); penpos4 (thin, 90); z3 = z4; top y3 = 0.5 thick; lft x3 = 0.0; x1 = x3l; y1 = -0.5 thick; z8 = .5 [z2, z7]; top y5 = flare; rt x5 = width; p := z8{right} ..tension 1.2.. z5{curl 0}; tipdir := direction 1 of p; penpos5 (hair, angle tipdir); penpos6 (hair, 90 + angle tipdir); z5 = z6; pickup pencircle scaled 1; fill (z1 -- z3l{up} .. z4r{right} -- z2{right} .. z6r{tipdir} .. z5r .. z6l{-tipdir} ..tension 1.1.. z7{left} & z7 -- z1 & cycle) yscaled updown; if updown = 1: penlabels (1, 2, 3, 4, 5, 6, 7, 8); fi; endgroup; enddef; fet_beginchar ("bracket tip up", "up"); draw_bracket (0.45 staff_space#, 1.9 staff_space#, 1); fet_endchar; fet_beginchar ("bracket tip down", "down"); draw_bracket (0.45 staff_space#, 1.9 staff_space#, -1); fet_endchar; fet_endgroup ("brackettips"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-klef.mf000066400000000000000000000410371404105311000236140ustar00rootroot00000000000000% feta-klef.mf -- implement Clefs -*-Fundamental-*- % % part of LilyPond's pretty-but-neat music font % % source file of the Feta (not the Font-En-Tja) music font % % (c) 1997--2009 Han-Wen Nienhuys , % Jan Nieuwenhuizen , % Juergen Reuter fet_begingroup ("clefs"); % % [Wanske] says the bulbs should be positioned about 1/4 right of the % `arrow'. % % TODO: The hair-curve at z6r looks a little awkward. % def draw_c_clef (expr reduction) = save hair, norm, reduced_ss, right_edge; save xoff; reduced_ss# = staff_space# * reduction; norm# := 2/3 reduced_ss#; hair# := 0.06 reduced_ss# + 0.5 linethickness#; right_edge# = 15/4 norm# + 2 hair#; define_pixels (norm, reduced_ss, right_edge); define_whole_vertical_blacker_pixels (hair); set_char_box (0, right_edge#, 2 reduced_ss#, 2 reduced_ss#); % make unreduced glyph fit exactly into five staff lines if reduction = 1: h := d := 2 staff_space_rounded; fi; % assure that the gap between the left and right stem % has the same number of pixels as the thickness of the right % stem draw_block ((0, -d + feta_shift), (3/4 norm + 1/2 hair, h)); draw_block ((3/4 norm + 1/2 hair + hround (3/2 hair), -d + feta_shift), (3/4 norm + 1/2 hair + 2 hround (3/2 hair), h)); % assure symmetry h := h - feta_shift; pickup feta_fillpen; xoff = 3/4 norm + 1/2 hair + 2 hround (3/2 hair); z5l = (xoff - 3/4 hair, 0); z5r = (x4, 0); penpos1 (hair - pen_top - pen_bot, -90); top z1l = (xoff + norm + hair, h); penpos2 (hround (norm - 3/2 hair) - pen_lft - pen_rt, 180); rt z2l = (w, h / 2); penpos3 (hair - pen_top - pen_bot, 90); bot z3l = ((right_edge - xoff) / 2 + xoff, vround (.5 norm - 1.5 hair)); penpos4 (hair - pen_lft - pen_rt, 0); top z4 = (xoff + 1/2 norm + 1/2 hair, vfloor (reduced_ss - linethickness - .2 hair)); bot z6 = (xoff + 3/4 norm, vround (.5 norm - .5 hair)); save t; t = 0.833; save pat; path pat; pat = z5l{curl 1} .. z4l{up} .. z4r{down} .. z3r{right} ..tension t.. z2r{up} ..tension t.. flare_path (top z1l, 180, 90, hair, hfloor (norm - 1/2 hair), -1) ..tension t.. z2l{down} .. z3l{left} .. z6 .. z5r{down}; filldraw pat shifted (0, feta_shift) -- reverse pat yscaled -1 shifted (0, -feta_eps) -- cycle; penlabels (1, 2, 3, 4, 5, 6); % ugh, should be bulb, not flare? draw_staff (-2, 2, 0); enddef; fet_beginchar ("C clef", "C"); draw_c_clef (1.0); fet_endchar; fet_beginchar ("C clef", "C_change"); draw_c_clef (.8); fet_endchar; % % New bulb routine: % % Insert a brushed piece of the path, and draw the rest of the bulb % separately. % % The bulb has circular form. Neat merging of the bulb and brushed path % is done by playing with tension. % def new_bulb (expr start_point, start_angle, outer_tangent_point, end_point, end_angle, big_radius, bulb_radius, flare, direction, turning_dir) = begingroup; save pat, before, after; save center; save u, v; path pat, before, after; pair center; clearxy; center = outer_tangent_point + big_radius * dir (0); % + (big_radius - bulb_radius) * dir (-turning_dir * 90) z1' = center + bulb_radius * dir (turning_dir * 180); z2' = outer_tangent_point + flare * dir (0); z3' = center + bulb_radius * dir (0); z4' = center + bulb_radius * dir (turning_dir * 90); z5' = center - 0.5 [big_radius, bulb_radius] * dir (turning_dir * 90); labels (1', 2', 3', 4', 5'); before := z3'{dir (turning_dir * 90)} .. z4'{-dir (0)} ..tension 1.1.. z1'{-dir (turning_dir* 90)}; after := z2'{dir (turning_dir * 90)} .. end_point{dir (end_angle)}; (u, v) = before intersectiontimes after; pat := start_point{dir (start_angle)} .. outer_tangent_point{dir (-turning_dir * 90)} ..tension 1.02.. z5'{dir (0)} .. subpath (0, u) of before .. subpath (v, infinity) of after; if direction = 0: pat := reverse pat; fi pat endgroup enddef; % % There is some variation in the shape of bass clefs. % % * In some clefs the size of the swoosh tip almost reaches the % bottom staff line; in some it crosses the 2nd line from the bottom % with a small overshoot. % % The most popular design is where the X part of the tip is aligned % with the left bulb boundary, and the Y part ends on the 2nd % staffline exactly. This is what we do. % % * The diameter of the bulb is the width of the open space. % % * The vertical center of the bulb can be on or slightly above the % staff line. % % * The vertical position of the dots can be symmetrical around the % staffline, centered in the staff space. The Baerenreiter SCS has % the bottom dot raised by approximately 0.1 staff space. % % * Uncarefully set music may have overshoots at the top. We have none. % % * It is not exactly clear where the vertical tangent at the right % of the swoosh should be. % def draw_bass_clef (expr exact_center, reduction) = save reduced_ss, swoosh_width; save right_thickness, right_offset, tip_protrude; save dot_diam, bulb_y_offset, bulb_flare; pair tip_protrude; reduced_ss# = staff_space# * reduction; 2.2 dot_diam# = reduction * (staff_space# - stafflinethickness#); right_thickness# = 0.37 staff_space# + 1.2 linethickness#; swoosh_width# = 2.1 reduced_ss#; define_pixels (swoosh_width); define_whole_pixels (reduced_ss); define_whole_blacker_pixels (dot_diam, right_thickness); right_offset = 0.05 staff_space; bulb_y_offset := 0.075 staff_space; bulb_flare := 2.5 linethickness; % tip_protrude := (-linethickness, -.2 staff_space); tip_protrude := (0, 0); set_char_box (-xpart exact_center, xpart exact_center + swoosh_width# + 7/12 reduced_ss#, -ypart exact_center + 2.5 reduced_ss#, ypart exact_center + reduced_ss#); y1 = bulb_y_offset; x1 = 0; x2 = .5 [x1, x3]; x2l = x2r = x2; y2l := vround_pixels (reduced_ss# + 0.5 linethickness#); y2l - y2r = linethickness; x3l - x1 = swoosh_width; x3l - x3r = right_thickness; % optical correction: the top dot seems farther away if y3l = 0. y3l = right_offset; z4 = -(0, 2.0 reduced_ss) + tip_protrude; penpos3 (whatever, 185); penpos4 (linethickness, 135); fill new_bulb (z2l, 180, z1, z2r, 0, 0.45 reduced_ss, 0.4 reduced_ss, bulb_flare, 1, 1) .. z3r{down} .. {curl 0}simple_serif (z4r, z4l, 90){curl 0} .. z3l{up} ..tension 0.9.. cycle; pickup pencircle scaled dot_diam; lft x5 = hround (x3l + 1/3 reduced_ss - dot_diam / 2); bot y5 = vfloor (.5 reduced_ss - dot_diam / 2); z6 = z5 yscaled -1; % for symmetry y5 := y5 + feta_shift; drawdot z5; drawdot z6; penlabels (1, 2, 3, 4, 5, 6); draw_staff (-3, 1, 0); enddef; fet_beginchar ("F clef ", "F"); draw_bass_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("F clef (reduced)", "F_change"); draw_bass_clef ((0, 0), 0.8); fet_endchar; % % Inspired by Baerenreiter % % % Beste lezers, kijk, % % Een bolletje hebben we bij toeval allemaal wel eens getekend, maar begint u % toch eenvoudig. Eerst een eenvoudig kruis of herstellingsteken % en via de dubbelslag naar een voorzichtig vlaggetje, en heb geduld! % Ikzelf heb bijvoorbeeld over mijn eerste gave G-sleutel % 35 kilobyte metafont, 12 patchlevels, 0 vriendinnen en 45 dagen gedaan % % -- vrij naar Van Kooten & De Bie % def debugfill = fill enddef; def draw_gclef (expr reduction) = save reduced_ss, downstroke_dir, downstroke_angle, center; save breapth_factor, inner_thick_end, thinness, thickness, thinnib; save start_angle, inner_start_angle, thinness; save upward_swoosh_angle, bot_angle; save pat; path pat; pair downstroke_dir, center; reduced_ss# = staff_space# * reduction; define_pixels (reduced_ss); thinness = 0.095 staff_space + 0.75 linethickness; downstroke_dir = unitvector (14, -75); downstroke_angle = angle downstroke_dir; bot_angle = -180; % downstroke_angle - 87 upward_swoosh_angle = 132; start_angle = -97; breapth_factor = 21/14; inner_thick_end = 45; inner_start_angle = downstroke_angle - 43; thickness = .32 reduced_ss + 1.1 linethickness; thinnib = thinness; set_char_box (0, 1.71 * breapth_factor * reduced_ss#, 2.6 * reduced_ss#, 5 * reduced_ss#); center := (breapth_factor * reduced_ss, 0); z1 = center + whatever * dir (inner_start_angle); x1 = xpart center - .28 reduced_ss; penpos1 (thinnib, inner_start_angle); x2r = xpart center; y2r = vround_pixels (reduced_ss# + .5 stafflinethickness#); penpos2 (thickness, 90); z3 = (z4 - center) rotated inner_thick_end + center; penpos3 (thinnib, -90 + inner_thick_end); x4 = xpart center - .1 reduced_ss; y4r = -y2r + feta_shift; penpos4 (thinnib, -90); x5r = -breapth_factor * reduced_ss + xpart center; y5r = .37 reduced_ss + ypart center; penpos5 (thickness, upward_swoosh_angle); z6 = center + whatever * downstroke_dir; y6 = ypart center + 2 reduced_ss; % penpos6 is computed later z7l - z6 = whatever * (z5 - z6) ; y7l = 3.5 reduced_ss; penpos7 (thickness, upward_swoosh_angle); x9 = .7 [x10, x7r]; top y9l = 5 reduced_ss; penpos9 (1.45 thickness, -70); x11 - x13r = 1.5 reduced_ss + 0.5 thinnib; y11 = ypart center - 47/28 reduced_ss; y12 = ypart center - 71/28 reduced_ss; y13 = .48 [y12, y4r]; x12r = xpart (.45 [z13r, z11] + .75 reduced_ss * downstroke_dir); % z10 = center + whatever * dir (downstroke_angle - 1.5); x10 = x6 - 2 thinnib; y10 = ypart center + 3.5 reduced_ss; y10l - y10r = 1.0 thickness; z10r - z10l = .7 thinnib * dir (downstroke_angle + 90) + whatever * downstroke_dir; z10 = .5 [z10l, z10r]; z11 = center + whatever * downstroke_dir + (-0.05 reduced_ss, 0); penpos11 (thinnib, start_angle + 90); penpos12 (thinnib, bot_angle + 90); penpos13 (thinnib + 0.14 staff_space, 180); % this auxiliary point ensures good contour overlapping z8 = .5 [z9l, z9r] + .25 ((z9r - z9l) rotated -90); z20 = z9l - (0, .25 blot_diameter); penpos20 (blot_diameter, 0); pat := z10{down} .. z6 ..tension 1.1.. z11{dir (start_angle)}; penpos6 (thinnib, angle (direction 1 of pat) + 90); % two auxiliary points to simulate `draw' with `penstroke' z10' = point 0.3 of pat; penpos10' (1.3 thinnib, angle (direction 0.3 of pat) + 50); z11' = point 1.5 of pat; penpos11' (thinnib, angle (direction 1.5 of pat) + 90); z21l = z20l; z21r = z9r; pat := z21l .. z10l{down} .. z10'l; fill z2l{right} .. z3l .. z4l{left} ..tension 1.07.. z5l{up} % inside curve .. z7l{up} ..tension 1.2.. z20r{curl 1} .. {direction 0 of pat}z20l -- z8 -- z9r % {dir (downstroke_angle + 0)} ..tension 0.8.. z7r{down} .. z5r{down} .. z4r{right} .. z3r .. z2r{left} ..tension .95.. z1r -- simple_serif (z1r, z1l, 80) -- z1l ..tension 0.85.. cycle; penstroke z21e .. z10e{down} .. z10'e .. z6e .. z11'e .. z11e{dir (-95)} .. z12e{dir (bot_angle)}; fill new_bulb (z12r, bot_angle, z13r, z12l, bot_angle + 180, 0.45 reduced_ss, 0.38 reduced_ss, thinnib + .05 staff_space, 1, -1) -- cycle; penlabels (range 1 thru 21); penlabels (10', 11'); draw_staff (-1, 3, 0); enddef; fet_beginchar ("G clef", "G"); draw_gclef (1.0); fet_endchar; fet_beginchar ("G clef", "G_change"); draw_gclef (0.8); fet_endchar; %%%% % % PERCUSSION % % % The percussion clef extent is not coincident with its bbox, since % the percussion clef needs more space in front than a normal clef. % def draw_percussion_clef (expr reduction) = save reduced_ss, razt; reduced_ss# = staff_space# * reduction; define_pixels (reduced_ss); set_char_box (-.67 reduced_ss#, 2.0 reduced_ss#, reduced_ss#, reduced_ss#); razt := hround (0.45 reduced_ss); d := d - feta_shift; draw_block ((-b, -d), (-b + razt, h)); draw_block ((w - razt, -d), (w, h)); draw_staff (-3, 1, 1); enddef; fet_beginchar ("percussion clef", "percussion"); draw_percussion_clef (1.0); fet_endchar; fet_beginchar ("percussion clef (reduced)", "percussion_change"); draw_percussion_clef (.8); fet_endchar; def draw_tab_T (expr pos, siz, slant) = begingroup; save vx, vy; pair vx, vy; clearxy; vx = (xpart siz) * dir 0; vy = (ypart siz) * dir 90; penpos1 (.75 penh, 100); z1 = z2 + (1/6 * vx - .15 * vy); penpos2 (hround (.9 penw), 0); x2l = hround xpart (pos + .75 vy); y2l = ypart (pos + .75 vy); penpos3 (penh, -100); z3l = pos + .4 vx + vy; penpos4 (penh, -90); z4 = -.1 vy + .5 [z3, z5]; penpos5 (.8 penh, -30); x5r = xpart (pos + siz); y5l = ypart (pos + siz); penpos10 (penw, 170); z10 = pos + .55 vx + .9 vy; penpos11 (.75 [penh, penw], 170); z11 = z10 - .5 vy + .025 vx; penpos12 (penh, 100); z12l = (xpart .5 [z13, z11], ypart (pos - .025 * siz)); penpos13 (.75 penh, 60); z13 = pos + .2 vx + .15 vy; % penlabels (range 1 thru 13); soft_penstroke (z1e ..tension 1.1.. z2e .. z3e{right} ..tension 1.5.. z4e ..z5e) slanted slant shifted (slant * -ypart pos, 0); soft_end_penstroke (z10e ..tension 1.5.. z11e .. z12e ..tension 1.1.. z13e{(z13r - z13l) rotated 90}) slanted slant shifted (slant * -ypart pos, 0); endgroup; enddef; def draw_tab_A (expr pos, siz, slant) = begingroup; save vx, vy, pat; pair vx, vy; path pat; clearxy; vx = (xpart siz) * dir 0; vy = (ypart siz) * dir 90; penpos1 (.75 penh, -110); z1r = pos + .07 vy; penpos2 (penh, -75); z2r = (.5 [x1, x3], ypart pos); penpos3 (.25 [penh, penw], -30); z3 = (.45 [x2, x4], .15 [y2, y4]); penpos4 (1 [penh, penw], 0); z4 = pos + .5 vx + .975 vy; penpos5 (1 [penh, penw], -180); z5 = z4; penpos6 (.2 [penh, penw], -150); z6l = (.8 [x5l, x7l], .9 [y5l, y7l]); penpos7 (penh,-90); z7r = (.5 [x6, x8], ypart pos); penpos8 (.75 penh, -70); z8r = (xpart (pos + siz), y7r + .075 ypart (siz)); pat := z2 .. z3 .. z4; penpos10 (penh, angle (direction 1.2 of pat) - 180); z10 = point 1.2 of pat; penpos11 (.9 penh, -90); z11 = .4 [z10, z6] - 0.05 vy; penpos12 (.75 penh, -75); z12 = .3 [z11, z6] + 0.02 vy; % penlabels (range 1 thru 12); soft_penstroke (z1e{(z1r - z1l) rotated 90} .. z2e .. z3e .. z4e) slanted slant shifted (slant * -ypart pos, 0); soft_end_penstroke (z5e .. z6e .. z7e .. z8e{(z8r - z8l) rotated 90}) slanted slant shifted (slant * -ypart pos, 0); soft_end_penstroke (z10e .. z11e .. z12e) slanted slant shifted (slant * -ypart pos, 0); endgroup; enddef; def draw_tab_B (expr pos, siz, slant) = begingroup; save vx, vy; pair vx, vy; clearxy; vx = (xpart siz) * dir 0; vy = (ypart siz) * dir 90; penpos1 (.75 penh, 100); z1 = z2 + (.15 * vx - .1 * vy); penpos2 (hround (.9 penw), 0); x2l = hround xpart (pos + .75 vy); y2l = ypart (pos + .75 vy); penpos3 (penh, -100); z3l = pos + .4 vx + 1.05 vy; penpos4 (.8 [penh, penw], -180); z4 = (xpart (pos + .75 siz), .5 [y3, y5]); penpos5 (.8 penh, 90); z5 = (.5 [x10, x4], ypart (pos + .55 siz)); penpos6 (.8 penh, 270); z6 = z5; penpos7 (penw, 180); z7l = (xpart (pos + siz), .5 [y6, y8]); penpos8 (.8 penh, 45); z8 = .5 [z12l, z11l] + .15 vx - .05 vy; penpos10 (.75 [penh, penw], 170); z10 = pos + .375 vx + vy; penpos11 (.8 [penh, penw], 150); z11 = z10 - .5 vy + .04 vx; penpos12 (penh, 100); z12l = (xpart .5 [z13, z11], ypart pos); penpos13 (.75 penh, 60); z13 = pos + .1 vx + .15 vy; % penlabels (range 1 thru 13); soft_penstroke (z1e ..tension 1.1.. z2e .. z3e .. z4e ..z5e {left}) slanted slant shifted (slant * -ypart pos, 0); soft_end_penstroke (z6e{right} .. z7e .. z8e{(z8r - z8l) rotated 90}) slanted slant shifted (slant * -ypart pos, 0); soft_end_penstroke (z10e ..tension 1.5.. z11e .. z12e ..tension 1.1.. z13e{(z13r - z13l) rotated 90}) slanted slant shifted (slant * -ypart pos, 0); endgroup; enddef; def draw_tab_clef (expr reduction) = save reduced_ss, letterheight, penw, penh; reduced_ss# = staff_space# * reduction; letterheight# = 1.8 reduced_ss#; define_pixels (reduced_ss, letterheight); set_char_box (-.2 reduced_ss#, 2.8 reduced_ss#, 1.6 letterheight#, 1.6 letterheight#); penw = .45 reduced_ss; penh = .2 reduced_ss; draw_tab_T ((-b + .15 reduced_ss, h - letterheight), (2.1 reduced_ss, letterheight), 0.2); draw_tab_A ((-b - .05 reduced_ss, -.5 letterheight +.15 reduced_ss), (2.2 reduced_ss, letterheight), 0.4); draw_tab_B ((-b + .025 reduced_ss, -d), (2.1 reduced_ss, letterheight), 0.25); draw_staff (-3, 2, 0.5); enddef; fet_beginchar ("tab clef", "tab"); draw_tab_clef (1.0); fet_endchar; fet_beginchar ("tab clef (reduced)", "tab_change"); draw_tab_clef (.8); fet_endchar; fet_endgroup ("clefs"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-macros.mf000066400000000000000000000224301404105311000241530ustar00rootroot00000000000000% % feta-macros.mf -- auxiliary macros for both feta and parmesan fonts % % source file of the GNU LilyPond music typesetter % % (c) 1997--2009 Han-Wen Nienhuys % % debugging % def print_penpos (suffix $) = message "z" & str$ & "l = (" & decimal x.$.l & ", " &decimal y.$.l & ");" & " z" & str$ & "r = (" & decimal x.$.r & ", " & decimal y.$.r & ");"; enddef; def test_grid = if test > 1: proofrulethickness 1pt#; makegrid (0pt, 0pt for i := -5pt step 1pt until 5pt: , i endfor) (0pt, 0pt for i := -5pt step 1pt until 5pt: , i endfor); proofrulethickness .1pt#; makegrid (0pt, 0pt for i := -4.8pt step .2pt until 4.8pt: , i endfor) (0pt, 0pt for i := -4.8pt step .2pt until 4.8pt: , i endfor); fi; enddef; def treq = tracingequations := tracingonline := 1; enddef; def draw_staff (expr first, last, offset) = if test <> 0: pickup pencircle scaled stafflinethickness; for i := first step 1 until last: draw (-staff_space, (i + offset) * staff_space_rounded) -- (4 staff_space, (i + offset) * staff_space_rounded); endfor; fi; enddef; % % Draw the outline of the stafflines. For fine tuning. % def draw_staff_outline (expr first, last, offset) = if test <> 0: save p; path p; pickup pencircle scaled 2; for i := first step 1 until last: p := (-staff_space, (i + offset) * staff_space_rounded) -- (4 staff_space, (i + offset) * staff_space_rounded); draw p shifted (0, .5 stafflinethickness); draw p shifted (0, -.5 stafflinethickness); endfor; fi; enddef; % % Transformations % def scaledabout (expr point, scale) = shifted -point scaled scale shifted point enddef; % % make a local (restored after endgroup) copy of t_var % def local_copy (text type, t_var) = save copy_temp; type copy_temp; copy_temp := t_var; save t_var; type t_var; t_var := copy_temp; enddef; % % Urgh! Want to do parametric types % def del_picture_stack = save save_picture_stack, picture_stack_idx; enddef; % % better versions of Taupin/Egler savepic cmds % def make_picture_stack = % override previous stack del_picture_stack; picture save_picture_stack[]; numeric picture_stack_idx; picture_stack_idx := 0; def push_picture (expr p) = save_picture_stack[picture_stack_idx] := p; picture_stack_idx := picture_stack_idx + 1; enddef; def pop_picture = save_picture_stack[decr picture_stack_idx] enddef; def top_picture = save_picture_stack[picture_stack_idx] enddef; enddef; % % save/restore pens % why can't I delete individual pens? % def make_pen_stack = del_pen_stack; pen save_pen_stack[]; numeric pen_stack_idx; pen_stack_idx := 0; def push_pen (expr p) = save_pen_stack[pen_stack_idx] := p; pen_stack_idx := pen_stack_idx + 1; enddef; def pop_pen = save_pen_stack[decr pen_stack_idx] enddef; def top_pen = save_pen_stack[pen_stack_idx] enddef; enddef; def del_pen_stack = save save_pen_stack, pen_stack_idx; enddef; % % drawing % def soft_penstroke text t = forsuffixes e = l, r: path_.e := t; endfor; if cycle path_.l: cyclestroke_; else: fill path_.l ..tension1.5.. reverse path_.r ..tension1.5.. cycle; fi; enddef; def soft_start_penstroke text t = forsuffixes e = l, r: path_.e := t; endfor; if cycle path_.l: cyclestroke_; else: fill path_.l -- reverse path_.r ..tension1.5.. cycle; fi; enddef; def soft_end_penstroke text t = forsuffixes e = l, r: path_.e := t; endfor; if cycle path_.l: cyclestroke_; else: fill path_.l ..tension1.5.. reverse path_.r -- cycle; fi; enddef; % % Make a round path segment going from P to Q. 2*A is the angle that the % path should take. % def simple_serif (expr p, q, a) = p{dir (angle (q - p) - a)} .. q{-dir (angle (p - q) + a)} enddef; % % Draw an axis aligned block making sure that edges are on pixels. % def draw_rounded_block (expr bottom_left, top_right, roundness) = begingroup; save size; save x, y; % Originally, there was `floor' instead of `round', but this is % not correct because pens use `round' also. size = round min (roundness, xpart (top_right - bottom_left), ypart (top_right - bottom_left)); z2 + (size / 2, size / 2) = top_right; z4 - (size / 2, size / 2) = bottom_left; y3 = y2; y4 = y1; x2 = x1; x4 = x3; pickup pencircle scaled size; fill bot z1{right} .. rt z1{up} -- rt z2{up} .. top z2{left} -- top z3{left} .. lft z3{down} -- lft z4{down} .. bot z4{right} -- cycle; endgroup; enddef; def draw_block (expr bottom_left, top_right) = draw_rounded_block (bottom_left, top_right, blot_diameter); enddef; def draw_square_block (expr bottom_left, top_right) = save x, y; x1 = xpart bottom_left; y1 = ypart bottom_left; x2 = xpart top_right; y2 = ypart top_right; fill (x1, y1) -- (x2, y1) -- (x2, y2) -- (x1, y2) -- cycle; enddef; def draw_gridline (expr bottom_left, top_right, thickness) = draw_rounded_block (bottom_left - (thickness / 2, thickness / 2), top_right + (thickness / 2, thickness / 2), thickness); enddef; def draw_brush (expr a, w, b, v) = save x, y; z1 = a; z2 = b; z3 = z4 = z1; z5 = z6 = z2; penpos3 (w, angle (z2 - z1) + 90); penpos4 (w, angle (z2 - z1)); penpos5 (v, angle (z1 - z2) + 90); penpos6 (v, angle (z1 - z2)); fill z3r{z3r - z5l} .. z4l .. {z5r - z3l}z3l .. z5r{z5r - z3l} .. z6l .. {z3r - z5l}z5l .. cycle; enddef; % % Make a superellipsoid segment going from FROM to TO, with SUPERNESS. % Take superness = sqrt(2)/2 to get a circle segment. % % See Knuth, p. 267 and p.126. def super_curvelet (expr from, to, superness, dir) = if dir = 1: (superness [xpart to, xpart from], superness [ypart from, ypart to]){to - from} else: (superness [xpart from, xpart to], superness [ypart to, ypart from]){to - from} fi enddef; % % Bulb with smooth inside curve. % % alpha = start direction % beta = which side to turn to % flare = diameter of the bulb % line = diameter of line attachment % direction = is ink on left or right side (1 or -1) % % Note that `currentpen' must be set correctly -- only circular pens % are supported properly. def flare_path (expr pos, alpha, beta, line, flare, direction) = begingroup; save thick; thick = pen_top + pen_bot; clearxy; penpos1' (line - thick, 180 + beta + alpha); top z1'r = pos; penpos2' (flare - thick, 180 + beta + alpha); z2' = z3'; penpos3' (flare - thick, 0 + alpha); rt x3'l = hround (x1'r + (1/2 + 0.43) * flare * xpart dir (alpha + beta)); bot y2'l = vround (y1'r + (1 + 0.43) * flare * ypart dir (alpha + beta)); rt x4' = x2'r - line * xpart dir (alpha); y4' = y2'r - line * ypart dir (alpha); penlabels (1', 2', 3', 4'); save t, p; t = 0.833; path p; p := z1'r{dir (alpha)} .. z3'r{dir (180 + alpha - beta)} .. z2'l{dir (alpha + 180)} .. z3'l{dir (180 + alpha + beta)} ..tension t.. z4'{dir (180 + alpha + beta)} .. z1'l{dir (alpha + 180)}; if direction <> 1: p := reverse p; fi; p endgroup enddef; def brush (expr a, w, b, v) = begingroup; draw_brush (a, w, b, v); penlabels (3, 4, 5, 6); endgroup; enddef; % % Draw a (rest) crook, starting at thickness STEM in point A, % ending a ball W to the left, diameter BALLDIAM. % ypart of the center of the ball is BALLDIAM/4 lower than ypart A. % def balled_crook (expr a, w, balldiam, stem) = begingroup; save x, y; penpos1 (balldiam / 2, -90); penpos2 (balldiam / 2, 0); penpos3 (balldiam / 2, 90); penpos4 (balldiam / 2, 180); x4r = xpart a - w; y3r = ypart a + balldiam / 4; x1l = x2l = x3l = x4l; y1l = y2l = y3l = y4l; penpos5 (stem, 250); x5 = x4r + 9/8 balldiam; y5r = y1r; penpos6 (stem, 260); x6l = xpart a; y6l = ypart a; penstroke z1e .. z2e .. z3e .. z4e .. z1e .. z5e{right} .. z6e; penlabels (1, 2, 3, 4, 5, 6); endgroup; enddef; def y_mirror_char = currentpicture := currentpicture yscaled -1; set_char_box (charbp, charwd, charht, chardp); enddef; def xy_mirror_char = currentpicture := currentpicture scaled -1; set_char_box (charwd, charbp, charht, chardp); enddef; % % center_factor: typically .5; the larger, the larger the radius of the bulb % radius factor: how much the bulb curves inward % def draw_bulb (expr turndir, zl, zr, bulb_rad, radius_factor)= begingroup; save rad, ang, pat; path pat; clearxy; ang = angle (zr - zl); % don't get near infinity % z0 = zr + bulb_rad * (zl - zr) / length (zr - zl); z0' = zr + bulb_rad / length (zr - zl) * (zl - zr); rad = bulb_rad; z1' = z0' + radius_factor * rad * dir (ang + turndir * 100); z2' = z0' + rad * dir (ang + turndir * 300); labels (0', 1', 2'); pat = zr{dir (ang + turndir * 90)} .. z1' .. z2' .. cycle; % avoid grazing outlines fill subpath (0, 2.5) of pat -- cycle; endgroup enddef; pi := 3.14159; % % To get symmetry at low resolutions we need to shift some points and % paths, but not if mf2pt1 is used. % if known miterlimit: vardef hfloor primary x = x enddef; vardef vfloor primary y = y enddef; vardef hceiling primary x = x enddef; vardef vceiling primary y = y enddef; else: vardef hfloor primary x = floor x enddef; vardef vfloor primary y = (floor y.o_)_o_ enddef; vardef hceiling primary x = ceiling x enddef; vardef vceiling primary y = (ceiling y.o_)_o_ enddef; fi; muse-4.0.0/src/share/scoreglyphs/feta-original/feta-nummer-code.mf000066400000000000000000000423421404105311000251060ustar00rootroot00000000000000% feta-nummer-code.mf -- implement bold Orator numerals % % part of LilyPond's pretty-but-neat music font % % source file of the Feta (not the Font-En-Tja) music font % % (c) 1997--2009 Jan Nieuwenhuizen height# := number_design_size; space# := number_design_size / 2; font_x_height height#; font_normal_space space#; % % DOCME! % % ugh. b and h are reused. % save b, h; 4 h + b = 1.15; 10 h + b = 1; fatten := number_design_size * h + b; save b, h; 4 h + b = 1.05; 10 h + b = 1; widen := number_design_size * h + b; tense = 0.85; thick# := 7/30 height# * fatten; thin# := thick# / 4 * fatten + max (.1 (height# / 10 - 1), 0); %% sqrt (.8 * blot_diameter# * thin#); hair# := thin# * .8; flare# := 9/8 thick# + .75 (height# / 10 - 1); save b, h; 4h + b = 1/8; 10h + b = 1/6; kuulleke# := thick# * number_design_size * h + b; foot_top# := thick#; foot_width# := 9/4 thick#; % % These numbers were taken from a part that that the EJE violas played % 1997 -- Probably Mendelssohn's ouverture `Heimkehr aus der Fremde'. % % % TODO all the invocation of flare_path are weird -- % the horizontal tangents should be more at the center of the % glyph. % define_pixels (height, thick, thick, thin, hair, flare); define_pixels (foot_top, foot_width); define_pixels (kuulleke); % % Yet Another Bulb Routine with smooth inside curve. % % alpha = start direction. % beta = which side to turn to % flare = diameter of the bulb % line = diameter of line attachment % direction = is ink on left or right side (1 or -1) % % % move_away_to = amount left (for 2) % turn_to = amount down (for 2) % def number_flare_path (expr pos, alpha, beta, line, flare, move_away_to, turn_to, taille, taille_ratio, direction) = begingroup; save res; path res; clearxy; % z5 = z2 + 0.43 * flare * dir (alpha - 1.5 beta); z4 = (0.75 - taille) [z2r, z2l] + whatever * dir (alpha - beta); z4 = (taille_ratio * taille) [z3l, z3r] + whatever * dir (alpha); z1r = pos; z2r = z1r + move_away_to * dir (alpha) + (line + turn_to) * dir (alpha + beta); z3r = 0.5 [z2l, z2r] + 0.5 * flare * dir (alpha + beta); penpos1 (line, 180 + beta + alpha); penpos2 (flare, alpha); penpos3 (flare, alpha + beta); penlabels (1, 2, 3, 4, 5); res := z1r{dir (alpha)} .. z2r{dir (180 + alpha - beta)} .. z3r{dir (alpha + 180)} .. z2l{dir (alpha - beta)} %%% Two versions of the curve: one with z4, the other with z5. % .. z5{dir (alpha - beta / 2)} .. z4{dir (180 + alpha + beta)} .. z1l{dir (alpha + 180)}; % pickup pencircle; % draw res; if direction <> 1: res := reverse res; fi; res endgroup enddef; def calc_kuulleke (expr w, alpha) = begingroup; save beta, gamma; beta = (alpha - 90) / 2; gamma = (90 + alpha) / 2; penpos1 (w / cosd (alpha), alpha); penpos2 (hair, 90 + beta); penpos3 (hair, gamma - 90); z2 = z1l + (1/2 hair / tand ((alpha + 90) / 2)) * dir (beta); z3 = z1r - (1/2 hair / tand ((90 - alpha) / 2)) * dir (gamma); z4 = z1 + kuulleke * dir (alpha - 90); endgroup; enddef; % should make generic macro? % def draw_foot (expr xpos) = begingroup; clearxy; penpos1 (thick, 0); penpos2 (foot_width, 0); penpos3 (hair, -90); penpos4 (hair, 90); z1= (xpos, foot_top); z2= (x1, 0); z3r = z2r; z4l = z2l; z5 = (x1, kuulleke); penlabels (1, 2, 3, 4); fill z1 .. {right}z1r{down} .. {right}z3l .. z3r{left} .. z5 .. {left}z4l .. z4r{right} .. {up}z1l{right} .. z1 .. cycle; endgroup; enddef; def draw_six = save outer_t, t; save before, after, u, v; path before, after; set_char_box (0, .68 height# * widen, 0, height#); message "w:" & decimal w; message "h:" & decimal h; penpos2 (hair, 90); z2 = (w / 2, h / 2 + thin - hair / 2); penpos3 (15/16 thick, 0); x3r = w; y3r = .5 [y4r, y2r]; penpos4 (hair, -90); z4r = (x2, 0); penpos6 (hair, 90); x6r = .56 w; y6r = h; penpos7 (thick, 180); x7r = 0; y7r = .50 h; penpos10 (thick, 180); z10r = (0, y3); penlabels (range 1 thru 10); outer_t = 0.88; t := tense; before := z7{right} .. z2r{right}; after := z7r{up} .. number_flare_path (z6r, 0, -90, hair, flare, w - x6r - hair / 2, .16 h, 0.05, 2.5, 1) .. z7l{down}; (u, v) = before intersectiontimes after; % draw fill subpath (u, infinity) of before ..tension outer_t.. z3r{down} ..tension outer_t.. z4r{left} .. subpath (0, v) of after .. cycle; unfill z2l{right} ..tension t.. z3l{down} ..tension t.. z4l{left} ..tension t.. z10l{up} ..tension t.. cycle; enddef; save dot_diam; dot_diam# = 7/8 flare#; define_pixels (dot_diam); code := 42; % , 43 fet_beginchar ("Plus", "plus"); set_char_box (0, .5 height#, -0.25 height#, 0.75 height#); save hthick, vthick, size, outer_hsize, outer_vsize; hthick# = vthick# = 2 linethickness#; size# = 1.1 staff_space#; define_whole_blacker_pixels (vthick); define_whole_vertical_blacker_pixels (hthick); outer_hsize = hround ((b + w - vthick) / 2); outer_vsize = vround ((h + d - hthick) / 2); centerx := hround (w / 2); centery := vround ((h - d) / 2); z1 = (centerx - hthick / 2, -d); z2 = (centerx + hthick / 2, h); labels (1, 2); draw_rounded_block (z1, z2, hthick); draw_rounded_block ((0, centery - vthick / 2), (w, (centery + vthick / 2)), vthick); fet_endchar; code := 43; % , = 44 fet_beginchar ("Numeral comma", "comma"); save pat, pos; path pat; set_char_box (0, dot_diam#, 3/2 dot_diam#, dot_diam#); pat := (dot_diam / 2, 0) .. (dot_diam, dot_diam / 2) .. (dot_diam / 2, dot_diam) .. (0, dot_diam / 2) .. cycle; pos = ypart (((w / 3, 0) -- (w / 3, dot_diam / 2)) intersectiontimes pat); z0 = point pos of pat; alpha = 65; penpos1 (thin, alpha + 90); z1l = (w / 2, -1.5 h + hair); z2 = (w, h / 2); pickup feta_fillpen; % include z0 to assist removal of overlaps fill subpath (0,3) of pat .. z0 .. cycle; filldraw z1l{dir (alpha)} .. {up}z2 -- z0{direction pos of pat} ..tension 0.95.. {dir (180 + alpha)}z1r .. cycle; labels (0, 2); penlabels (1); fet_endchar; fet_beginchar ("Numeral dash", "hyphen"); set_char_box (0, height# / 3, 0, height#); draw_rounded_block ((-b, h / 3 - thin / 2), (w, h / 3 + thin / 2), thin); fet_endchar; fet_beginchar ("Numeral dot", "period"); set_char_box (0, dot_diam#, 0, dot_diam#); pickup pencircle scaled dot_diam; drawdot (dot_diam / 2, dot_diam / 2); fet_endchar; % skip slash code := 47; % 0 = 48 fet_beginchar ("Numeral 0", "zero"); set_char_box (0, 11/15 height# * widen, 0, height#); message "w:" & decimal w; message "h:" & decimal h; penpos1 (thin, 90); penpos2 (thick, 180); penpos3 (thin,- 90); penpos4 (thick, 0); z1r = (w / 2, h); z2r = (0, h / 2); z3r = (w / 2, 0); z4r = (w, h / 2); fill z1r .. z2r .. z3r .. z4r .. cycle; save t; t = 1 / tense; penlabels (1, 2, 3, 4); unfill z1l ..tension t.. z2l ..tension t.. z3l ..tension t.. z4l ..tension t.. cycle; fet_endchar; fet_beginchar ("Numeral 1", "one"); save alpha, beta, gamma; % set_char_box (0, 19/30 height# * widen, 0, height#); set_char_box (0, 1/2 foot_width# + 3/2 thick# + 1/2 hair#, 0, height#); message "w:" & decimal w; message "h:" & decimal h; alpha = 0; calc_kuulleke (thick, alpha); z1 = (3/2 thick, height); penpos5 (thick, 0); z5 = (x1, foot_top); z6 = (0, h / 2); beta = angle (z1l - z6); penpos7 (thin, beta - 90); z7l = z6; penpos8 (thin / cosd (beta), -90); z8l = z1l; penpos9 (thin, beta - 90); z9r = z8r + (thin / cosd (beta)) * down; penlabels (range 1 thru 9); gamma = angle (length (z1r - z1), 2 kuulleke); fill z2r{dir (alpha - gamma)} .. z4 .. {dir (alpha + gamma)}z3l .. z3r{down} -- z5r -- z5l -- z2l{up} .. cycle; fill z7l -- z1l{dir (beta)} .. {dir (alpha - gamma)}z2r -- z9r{up} .. {dir (180 + beta)}z9l -- z7r{dir (180 + beta)} .. {dir (beta)}cycle; draw_foot (x1); fet_endchar; fet_beginchar ("Numeral 2", "two"); save tolerance; save alpha, beta, gamma, theta; save flare_start_distance; save t, pat, bow; path pat, bow; set_char_box (0, 22/30 height# * widen, 0, height#); message "w:" & decimal w; message "h:" & decimal h; alpha = -45 * widen; beta = 85; gamma = beta - 10; theta = 20 / widen; flare_start = 0.25; penpos1 (hair, 90 + beta); z1 = (0, 0) + (1/2 sqrt (2) * hair) * dir (45); penpos3 (hair,90 + gamma); z3 = (w, thick) + (1/2 sqrt (2) * hair) * dir (-135); penpos2 (thick, 90 + alpha - 15); x2 - x1 = x3 - x2; y2 = 10/16 thick / widen; tolerance := epsilon; % Find proper tension to exactly touch the x axis. % Later on we directly use `bow'. vardef f (expr t) = bow := z3l{dir (180 + gamma)} ..tension t.. {dir (180 + alpha -5)}z2l; ypart (directionpoint left of bow) < 0 enddef; % the return value of `solve' is stored in a dummy variable t = solve f (0.8, 1.2); fill z1r{dir (beta)} ..tension 0.9.. {dir (alpha + 10)}z2r .. {dir (gamma)}z3r .. bow .. {dir (180 + beta)}z1l .. cycle; penpos4 (thick, 0); z4r = (w - thin / 2, .71 h); penpos5 (hair, 90); y5r = h; x5r = 9/20 w; penlabels (range 1 thru 6); t := tense; pat := z1l{dir (beta)} ..tension t.. z4r{up} .. number_flare_path (z5r, 180, 90, hair, 1.05 flare, x5r - 1/2 hair, .21 h, 0.006, 0.4, 1) .. z4l{down} ..tension t.. {dir (180 + beta)}z1r -- cycle; % pickup pencircle scaled 1; % draw pat; fill pat; fet_endchar; %% % TODO: should widen a bit. The right edge of the 3 bumps into next glyph in % combinations % fet_beginchar ("Numeral 3", "three"); set_char_box (0, 2/3 height# * widen, 0, height#); message "w:" & decimal w; message "h:" & decimal h; penpos1 (hair, -90); % flare_start = 0.25; x1l = 36/80 w; y1l = h; % z1l = (17/16 thick, h); penpos2 (7/8 thick, 180); x2l = w - thick / 8; y2l = 3/4 h + thick * 3/32; penpos3 (thin, 90); z3 = (w / 2, h / 2 + 1/8 thick); penpos4 (thin, 90); z4 = (5/8 thick + 1/2 thin, y3); penpos5 (thick, 0); x5r = w; y5r = 17/64 h + thick / 16; penpos6 (hair, -90); x6r = 37/80 w; y6r = 0; penpos7 (3/2 thin, 90); x7 = .83 w; y7 = y3; penlabels (range 1 thru 7); save alpha, t, outer_t; alpha = 25; t = tense; outer_t := 0.93; % pickup pencircle scaled 1; % draw fill number_flare_path (z1l, 180, 90, hair, 7/8 flare, x1l - .5 hair, .16 h, 0.06, 1.5, -1) ..tension outer_t.. z2l{down} ..tension outer_t.. z7r{dir (180 + alpha)} .. z7l{dir (-alpha)} ..tension outer_t.. z5r{down} ..tension outer_t.. number_flare_path (z6r, 180, -90, hair, flare, x6l, .18 h, 0.06, 1.5, 1) .. z5l{up} ..tension t.. z3l{left} .. z4l{left} .. z4r{right} .. z3r{right} ..tension t.. z2r{up} ..tension t.. cycle; fet_endchar; fet_beginchar ("Numeral 4", "four"); save alpha, beta, gamma; set_char_box (0, 4/5 height# * widen, 0, height#); message "w:" & decimal w; message "h:" & decimal h; alpha = 0; calc_kuulleke (3/2 thick, alpha); z1r = (w - 3/4 thick, height); z5 = (thin, 1/4 height + thin); beta = angle (z3r - z5); penpos6 (thin, -90); z6l = z5; penpos7 (thin, -90); y7 = y6; x7 = w - 1/2 thin; penpos8 (thin, -alpha); z8r = z5; penlabels (range 1 thru 8); gamma = angle (length (z1r - z1), 2 kuulleke); fill z2r{dir (alpha - gamma)} .. z4 .. {dir (alpha + gamma)}z3l .. {dir (180 + beta)}z3r -- z8r -- z7l{right} .. {left}z7r -- z6r{left} ..tension 0.8 and 2.. z8l{dir (beta)} .. {up}z2l .. cycle; clearxy; alpha := beta; calc_kuulleke (thick, alpha); z1r = (w - 3/4 thick, height - (3/2 thin) / cosd (alpha)); penpos5 (thick, 0); z5 = (x1, foot_top); gamma := angle (length (z1r - z1), 2 kuulleke); fill z2r{dir (alpha - gamma)} .. z4 .. {dir (alpha + gamma)}z3l .. {down}z3r -- z5r -- z5l -- z2l{up} .. cycle; penlabels (1, 2, 3, 4, 5); draw_foot (x5); fet_endchar; fet_beginchar ("Numeral 5", "five"); save alpha, beta, gamma, delta; save inner_t, outer_t; save pat; path pat; set_char_box (0, 27/40 height# * widen, 0, height#); message "w:" & decimal w; message "h:" & decimal h; alpha = 0; calc_kuulleke (w - thin, alpha); z1 = (w / 2 + 1/8 thick, h); penpos5 (thin, 0); z5l = (x1l, h - 15/16 thick); penpos6 (hair, 90 - 45); z6 = z5r + 1/2 hair * dir (-45); penpos7 (thin, 0); z7l = (x1l, h / 2 + thin - hair); penlabels (range 1 thru 7); gamma = angle (length (z1r - z1), 2 kuulleke); pat := z2r{dir (alpha - gamma)} .. z4 .. {dir (alpha + gamma)}z3l .. z3r{dir (-135)} .. {left}z6r .. {down}z6l -- z7r{down} .. {up}z7l -- z2l{up} .. cycle; beta = 45; delta = 180 + beta + 10; z8r = (x7r, y7r - 1/16 thick + thin); z8l = directionpoint dir (delta) of subpath (6, 7) of pat; % include intersection point to improve overlap removal fill subpath (0, 6) of pat .. z8l .. subpath (7, length (pat)) of pat .. cycle; penpos9 (thin, 90); y9 = 10/16 [y5, y7]; x9 = .36 [x8r, x10r]; penpos10 (thick, 0); x10r = w + hair / 2; y10r = 1/2 [y9r, y11r]; penpos11 (hair, -90); y11r = 0; x11r = .7 [0, x10l]; penlabels (range 8 thru 12); inner_t = 1.0; outer_t = .85; fill z8r {dir (beta)} .. z9r{right} ..tension outer_t.. z10r{down} .. number_flare_path (z11r, 180, -90, hair, flare, x11l, .18 h, 0.06, 1.5, 1) .. z11l{right} ..tension inner_t.. z10l{up} ..tension inner_t.. z9l{left} .. z8l{dir (delta)} -- cycle; fet_endchar; fet_beginchar ("Numeral 6", "six"); draw_six; fet_endchar; fet_beginchar ("Numeral 7", "seven"); save tolerance; save alpha, beta, gamma, delta; save bow; save x_overshoot; path bow; set_char_box (0, 11/15 height# * widen - thin#, 0, height#); overshoot_x = .75 thin; message "w:" & decimal w; message "h:" & decimal h; alpha = -180; penpos1 (3/2 thick, 180 + alpha); penpos2 (hair, 180 + alpha - 45); penpos3 (hair, 180 + alpha + 45); z2 = z1l + (1/4 sqrt (2) * hair) * dir (alpha - 135); z3 = z1r + (1/4 sqrt (2) * hair) * dir (alpha - 45); z4 = z1 + kuulleke * dir (alpha - 90); z1l = (thin, 0); beta = 55; penpos5 (thin, 90 + beta); z5 = (w, h) + (1/2 sqrt (2) * thin) * dir (-135) + (overshoot_x, 0); gamma = angle (length (z1r - z1), 2 kuulleke); delta = 12; pickup pencircle; fill z3l{dir (alpha - gamma)} .. z4 .. {dir (alpha + gamma)}z2r .. z2l{dir (beta + delta)} .. {dir (beta)}z5r .. z5l{dir (180 + beta)} .. {dir (delta - 90)}z3r .. cycle; penlabels (1, 2, 3, 4, 5); alpha := -45 * widen; penpos11 (1/2 thick, 90); z11 = (3/2 thin, h - (thick + thin) / 2); penpos13 (thin, 90 + beta); z13 = z5; penpos12 (thick, 90 + alpha); x12 = 1/2 [x11, x13] - 1/4 thick; y12 = h - 15/16 thick + thin * widen; penpos14 (thin, 0); z14l = (0, h - thin / 2); penpos15 (thin, 0); z15l = (0, h / 2 + thin / 2); penpos16 (9/8 thick, 90); z16r = (thin, y11r + 2/16 thick); tolerance := epsilon; % Find proper tension to exactly touch the x axis. % Later on we directly use `bow'. vardef f (expr t) = bow := z11r{dir (beta)} ..tension t.. {dir (alpha)}z12r; ypart (directionpoint right of bow) > h enddef; % the return value of `solve' is stored in a dummy variable t = solve f (0.8, 1.2); fill bow .. {dir (beta)}z13r -- z13l{dir (180 + beta)} .. {dir (180 + alpha)}z12l .. {dir (180 + beta)}z11l .. {down}z16l -- z15r{down} .. {up}z15l -- z14l{up} .. {down}z14r -- z16r{down} ..tension 1.5.. {dir (beta)}cycle; penlabels (range 11 thru 16); fet_endchar; fet_beginchar ("Numeral 8", "eight"); save alpha, beta; set_char_box (0, 11/15 height# * widen, 0, height#); message "w:" & decimal w; message "h:" & decimal h; alpha = 60; beta = alpha - 15; z1 = (w / 2, h / 2 + thick / 8); penpos2 (14/8 thin, 0); z2 = (w / 3, h / 2 + thin); penpos3 (3/2 thin, 0); z3l = (0, h / 4 + thin / 2); penpos4 (hair, 90); z4l = (x1, 0); penpos5 (thick, 90 + 90 + alpha); z5 = z1 + w / 4 * dir (alpha - 90); penpos6 (thick, 90 + 90 + alpha); z6 = z1 + (w / 4 - thin / 2) * dir (90 + alpha); penpos7 (hair, 90); z7r = (x1 + .02 w, h); penpos8 (3/2 thin, 0); z8r = (w - thin / 2, 3/4 h + thin / 2); penpos9 (13/8 thin, 0); z9 = (2/3 w, h / 2); penlabels (range 1 thru 9); save t; t = tense; fill z2r{dir (180 + beta)} .. z3r{down} .. z4r{right} .. z5r{dir (90 + alpha)} -- z6r{dir (90 + alpha)} ..tension t.. z7r{right} .. z8r{down} .. {dir (180 + beta)}z9r -- z9l{dir (beta)} .. z8l{up} .. z7l{left} .. {dir (alpha - 90)}z6l -- z5l{dir (alpha - 90)} ..tension t.. z4l{left} .. z3l{up} .. {dir (beta)}z2l -- cycle; fet_endchar; fet_beginchar ("Numeral 9", "nine"); draw_six; % xy_mirror_char; currentpicture := currentpicture scaled -1; currentpicture := currentpicture shifted (w, h); fet_endchar; ligtable "3": "3" kern 0.1 space#, "0" kern 0.1 space#; ligtable "2": "7" kern 0.15 space#; muse-4.0.0/src/share/scoreglyphs/feta-original/feta-params.mf000066400000000000000000000141671404105311000241620ustar00rootroot00000000000000% % feta-params.mf -- global parameters for both feta and parmesan fonts % % source file of the GNU LilyPond music typesetter % % (c) 1997--2009 Han-Wen Nienhuys stafflines := 5; % % The design size of a staff should really be the % staff_space, but we use staffsize for historical reasons. % staff_space# := staffsize# / (stafflines - 1); staff_space_rounded# := staff_space#; % % Measuring on pocket scores turns out: stafflinethickness is % largely independent on staff size, and generally about 0.5 pt. % % By request of WL, we tune down the blackness a little % for increased contrast with beams. % %% !! synchronize with paper.scm save fixed_line_thickness, variable_line_factor; fixed_line_thickness + variable_line_factor * 5 pt# = 0.50 pt#; fixed_line_thickness + variable_line_factor * 4.125 pt# = 0.47 pt#; stafflinethickness# := fixed_line_thickness + variable_line_factor * staff_space#; stafflinethickness_rounded# := stafflinethickness#; % % The following tunes the general blackness of the glyphs. % linethickness# := stafflinethickness#; %% 0.5 pt#; linethickness_rounded# := linethickness#; % % bigger puff_up_factor, relatively thicker stafflines. % % 20 pt = puff_up_factor 0 % 10 pt = puff_up_factor 1 % puff_up_factor = (linethickness# - 0.1 staff_space#) / (0.1 staff_space#); stemthickness# := 1.3 stafflinethickness#; stemthickness_rounded# := stemthickness#; ledgerlinethickness# := 2 stafflinethickness#; ledgerlinethickness_rounded# := ledgerlinethickness#; define_pixels (staff_space, stemthickness, stafflinethickness, ledgerlinethickness, linethickness); define_whole_pixels (staff_space_rounded); define_whole_blacker_pixels (stemthickness_rounded); define_whole_vertical_blacker_pixels (stafflinethickness_rounded, ledgerlinethickness_rounded, linethickness_rounded); if ledgerlinethickness_rounded > 2 stafflinethickness_rounded: ledgerlinethickness_rounded := 2 stafflinethickness_rounded; fi; % % Because of the engraving/stamping process, no traditional % characters have sharp edges and corners. % The following variable controls the amount of `roundness'. % % This is not a meta variable: it is related to absolute sizes. % % FIXME: According to [Wanske], only outside corners should be round % I don't think we do this anywhere -- jcn % blot_diameter# = .40 pt#; if (blot_diameter# * hppp) < 1: blot_diameter# := 1 / hppp; fi if (blot_diameter# * vppp) < 1: blot_diameter# := 1 / vppp; fi define_pixels (blot_diameter); % % symmetry % -------- % % Some glyphs have to be positioned exactly between stafflines (clefs, % note heads). This needs some care at lower resolutions. % % Most glyphs use the staffline thickness and the space between two % staff lines as the fundamental parameters. The latter is the distance % between the middle of one staff line to the middle of the next. To % say it differently, the value `staff_space' is the sum of one staff line % thickness and the whitespace between two adjacent staff lines. % % Normally, feta's vertical origin for glyphs is either the middle % between two staff lines or the middle of a staff line. For example, the % lower edge of the central staff line is at the vertical position % `- / 2', and the upper edge at % ` / 2'. Here we need a value rounded to an integer % (the feta code uses `stafflinethickness_rounded' for that purpose). % % If we have an odd number of pixels as the staffline thickness, Metafont % rounds `-stafflinethickness_rounded / 2' towards zero and % `stafflinethickness_rounded / 2' towards infinity. Example: `round -1.5' % yields -1, `round 1.5' yields 2. The whitespace between staff lines is % handled similarly. If we assume that stafflinethickness_rounded is odd, % we have the following cases: % % o The glyph is centered between three stafflines or five stafflines % (clef, `c' meter). We have this: % % ___________ a % ___________ 1 % ___________ a % % whitespace % % ___________ a % ...... ___________ 1 .................. x axis % ___________ a % % whitespace % % ___________ a % ___________ 1 % ___________ a % % As can be seen, we get symmetry if we split staff lines into two % equal parts `a' and a pixel line with thickness 1. Consequently, we % use the following algorithm: % % . Decrease the height `h' by 1 temporarily. % % . Compute the path for the upper half of the glyph. % % . Mirror the path at the x axis. % % . Shift the upper half one pixel up and connect it with the lower path. % % . Restore height and decrease `d' by 1. % % o The glyph is centered between two or four staff lines, and the origin is % the middle of the whitespace. Assuming that the whitespace consists % of an odd number of pixels, we have this: % % ----------- % b % 1 % b % ___________ % b % .................. 1 ................. x axis % b % ___________ % b % 1 % b % ___________ % % For symmetrical glyphs, this leads to a similar algorithm as above. % Glyphs which can't be constructed from an upper and lower part need % to be handled differently, namely to shift up the vertical center by % half a pixel: % % ___________ % b % % 0.5 % .................. 0.5 ................ x axis % % b % ___________ % feta_eps := 0; feta_shift := 0; feta_space_shift := 0; % Use this for paths with a slant of 45 degrees to assure that % the middle point of a penpos gets covered. pair feta_offset; feta_offset := (0, 0); if known miterlimit: pickup nullpen; else: feta_eps := eps; if odd stafflinethickness_rounded: feta_shift := 1; fi; if odd (staff_space_rounded - stafflinethickness_rounded): feta_space_shift := 1; fi; feta_offset := (0.5, 0.5); pickup pencircle scaled 1; fi; feta_fillpen := savepen; muse-4.0.0/src/share/scoreglyphs/feta-original/feta-pendaal.mf000066400000000000000000000161171404105311000243000ustar00rootroot00000000000000% -*- Fundamental -*- (emacs-20 mf mode mucks % feta-pendaal.mf -- piano pedal markings % % part of LilyPond's pretty-but-neat music font % % source file of the Feta (not the Font-En-Tja) music font % % (c) 2000--2009 Jan Nieuwenhuizen % % Voor Cup % Kerning % % Pe = -0.034 pedalh == -0.07 staff-space % ed = -0.27 pedalh == -0.53 staff-space fet_begingroup ("pedal"); pedalpha = 40; pedbeta = 25; penh# = 0.7 linethickness#; penw# = 2 penh# + 0.14 staff_space#; define_pixels (penh, penw); pedalh# = 2 staff_space#; pedalbh# = 4/7 pedalh#; define_pixels (pedalh, pedalbh); fet_beginchar ("Pedal asterisk", "*"); save bulb, p, radius, thin, inner_r; path pat; set_char_box (0, 7/9 pedalh#, 0, pedalh#); z0 = (1/2 w, h - 1/2 w); thin = 0.8 linethickness; bulb + 2 radius = w; 0.9 thin + bulb = (radius * pi * 2) / 8; pickup pencircle scaled penh; penpos1 (bulb, 180); penpos2 (bulb, 0); penpos3 (thin, 0); z1 = z0 + (0, radius); z2 = z1; inner_r = .45 radius; z4l = z0 + inner_r * dir (90 + 360/16); z4r = z0 + inner_r * dir (90 - 360/16); z4 = .5 [z4l, z4r]; z3 = .75 [z1, z4]; penlabels (0, 1, 2, 3, 4); pat := z3r{up} .. z1l{up} .. z2l{down} .. z3l{down} .. z4l{dir (180 + 360/16)}; for i = 1 upto 7: pat := pat .. ((z3r{up} .. z1l{up} .. z2l{down} .. z3l{down} .. z4l{dir (180 + 360/16)}) rotatedaround (z0, 360/8 i)); endfor; pat := pat .. cycle; fill pat; pickup pencircle scaled (7/8 inner_r); undrawdot z0; fet_endchar; %% ugh. rounded corners! fet_beginchar ("Pedal dash", "M"); save dash_thickness; dash_thickness# := penw#; define_whole_vertical_blacker_pixels (dash_thickness); set_char_box (0, 3 penw#, 0, pedalbh#); penpos1 (dash_thickness, 60); penpos2 (dash_thickness, 60); z1l = (0, vround (2/3 h - 1/2 penw)); z2r = (w, vround (y1l + 1.2 penw)); penlabels (1, 2, 3); penstroke z1e{dir 40} ..tension 1.2.. z2e{dir 40}; fet_endchar; fet_beginchar ("Pedal dot", "."); set_char_box (0, penw#, 0, penw#); pickup pencircle scaled penw; drawdot (hround (1/2 penw), vround (1/2 penw)); fet_endchar; def draw_pedal_P (expr show_labels) = begingroup; clearxy; penpos1 (hround penh, 0); penpos2 (penw, 55); penpos3 (penw, pedalpha); penpos4 (1.2 penh, -pedalpha); penpos5 (penh, -pedalpha); penpos6 (penh, 180 - pedalpha); penpos7 (penh, 180 - pedalpha); penpos8 (penh, 90); penpos9 (4/3 penw, 50); penpos10 (1.4 penw, 50); penpos11 (penh, 90 + pedalpha); z1r = (hround 0.6 w, h); x2l = hround (x1l - penw) + xpart feta_offset; y2 = vround (0.7 h) + ypart feta_offset; x3r = hround (x2l + 2 penw); y3r = 0.4 h; z4l = z5r + 1/4 (z3r - z5r); x5 = 1/4 w; y5r = 0; z6 = z5; z7l = z6r + 1/3 (z8r - z6r); z8r = z5r + 3/5 (z3r - z5r); x9l = x10l - penh; y9l = 1/4 penh; x10l = w - tand (90 - pedalpha) * y11l; y10l = 1/4 penh; x11l = w; y11 = 1/5 h; % don't stick out at the top z1' = round (0.9 [z2, z1]) + (xpart feta_offset, 0); penpos1' (penh, 0); % shift start point of pen stroke to avoid overlapping contours z8'l = z8l; y8'r = y8r; z8'r = z8l + whatever * (z3r - z8l); penpos12 (hround penh, 0); penpos13 (penw, -90 - pedbeta); penpos14 (vround penh, 90); penpos15 (penw, -90 + pedbeta); penpos16 (penh, 180 + pedbeta); z12r = (hround (5/9 x1 + 1/2 hround penh), y2); z13l = (1/2 x12r, y15r); z14r = z1r; z15l = (1/2 [x16, w], y2l + 0.5 penw); x16 = 1/4 [x1, w]; y16r = y2r; % We now do a correction to `center' the stem horizontally. While % this is not the most elegant solution, it is definitely the % easiest... save horz_corr; horz_corr = 1/5 [x12, x16] - x2l; x2 := x2 + horz_corr; x2l := x2l + horz_corr; x2r := x2r + horz_corr; penstroke z1'e ..tension 2.5.. z2e ..tension 1.5.. z3e ..tension 2.5.. {dir (225)}z4e; soft_end_penstroke z8'e{right} ..tension 1.5.. z9e{dir (-pedalpha)} .. z10e ..tension 2.5.. z11e; fill z4r{dir (225)} ..tension 1.5.. z5r{left} .. z6r ..tension 1.5.. z7r ..tension 1.5.. z8r{right} -- cycle; soft_penstroke z12e{down} ..tension 1.1.. z13e{dir (180 - pedbeta)} ..tension 1.1.. z14e{right} ..tension 1.1.. z15e{dir (180 + pedbeta)} ..tension 1.1.. z16e{dir (90 + pedbeta)}; if show_labels = 1: penlabels (range 1 thru 16); penlabels (1', 8'); fi; endgroup; enddef; def draw_pedal_d (expr show_labels) = begingroup; clearxy; penpos1 (vround penh, -10 - 90); penpos2 (hround penw, 190); penpos3 (vround (2 penh), 90); penpos4 (hround (3/4 penw), 0); penpos5 (penh, -70); x1r = 0; y1l = h; z2 = (w - 1/2 hround penw, 1/2 pedalbh + penh); z4l = (hround (1/3 w), 1/2 pedalbh); z5l = (2/3 w, pedalbh); z3l = (hround x5l, 0); if show_labels = 1: penlabels (1, 2, 3, 4, 5); fi; soft_start_penstroke z1e{dir (-10)} ..tension 1.1.. z2e{dir (-90 + 10)} ..tension 1.1.. z3e{left} ..tension 1.1.. z4e{up} ..tension 1.1.. z5e{dir (-70 + 90)}; endgroup; enddef; def draw_pedal_e (expr show_labels) = begingroup; clearxy; penpos1 (penh, pedalpha - 90); penpos2 (penh, pedalpha - 90); penpos3 (3/5 penw, pedalpha); penpos4 (1.2 penh, 90 + pedalpha); penpos5 (2/3 penw, 180); penpos6 (penw, 180 + pedalpha); penpos7 (2/3 penw, -90 - 20); penpos8 (penh, pedalpha - 90); z1l = (0, 1/5 pedalh); % this helps to make the path go through z2 at low resolutions z2 = round (z1 + dir pedalpha * penw) + feta_offset; z3 = (3/5 w, 8/9 h); z4 = (2/9 w, y3); x4r := hround x4r; x5r = 0; y5 = y2; x6 = 3/8 w; y6r = 1/2 penh; z7l = (x6l + penh, y6l); x8r = w; y8 = 1/5 pedalh; soft_penstroke z1e -- z2e{z2 - z1} ..tension 1.1.. z3e ..tension 1.05.. z4e ..tension 1.1.. z5e ..tension 1.5.. z6e{dir - pedalpha} ..tension 1.1.. z7e ..tension 2.5.. z8e; if show_labels = 1: penlabels (range 1 thru 8); fi; endgroup; enddef; fet_beginchar ("Pedal P", "P"); set_char_box (0, 5/6 pedalh#, 0, pedalh#); draw_pedal_P (1); fet_endchar; fet_beginchar ("Pedal d", "d"); set_char_box (0, 2/3 pedalh#, 0, 7/8 pedalh#); draw_pedal_d (1); fet_endchar; fet_beginchar ("Pedal e", "e"); set_char_box (0, 2/5 pedalh#, 0, pedalbh#); draw_pedal_e (1); fet_endchar; fet_beginchar ("Pedal Ped", "Ped"); P_width# = 5/6 pedalh#; e_width# = 2/5 pedalh#; d_width# = 2/3 pedalh#; define_pixels (P_width, e_width, d_width); e_height = pedalbh; d_height = 7/8 pedalh; % Pe = -0.034 pedalh == -0.07 staff-space % ed = -0.27 pedalh == -0.53 staff-space Pe_kern# = -0.034 pedalh#; ed_kern# = -0.27 pedalh#; define_pixels (Pe_kern, ed_kern); w := hround d_width; h := vround d_height; draw_pedal_d (0); currentpicture := currentpicture shifted (hround (e_width + ed_kern), 0); w := hround e_width; h := vround e_height; draw_pedal_e (0); currentpicture := currentpicture shifted (hround (P_width + Pe_kern), 0); w := hround P_width; h := vround pedalh; draw_pedal_P (0); set_char_box (0, P_width# + Pe_kern# + e_width# + ed_kern# + d_width#, 0, pedalh#); fet_endchar; fet_endgroup ("pedal"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-puntje.mf000066400000000000000000000007751404105311000242040ustar00rootroot00000000000000% % feta-puntje.mf -- a duration dot % % source file of the GNU LilyPond music typesetter % % (c) 1997--2009 Han-Wen Nienhuys fet_begingroup ("dots"); save dot_diam; 2 dot_diam# = staff_space# - stafflinethickness#; define_whole_blacker_pixels (dot_diam); fet_beginchar ("duration dot", "dot"); pickup pencircle scaled dot_diam; lft x0 = 0; top y0 = vround (.5 dot_diam); drawdot z0; set_char_box (0, dot_diam#, .5 dot_diam#, .5 dot_diam#); fet_endchar; fet_endgroup ("dots"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-schrift.mf000066400000000000000000001001651404105311000243330ustar00rootroot00000000000000% -*- Fundamental -*- (emacs-20 mf mode mucks % feta-schrift.mf -- implement scripts % % source file of the Feta (defintively not an abbreviation for Font-En-Tja) % music font % % (c) 1997--2009 Han-Wen Nienhuys % Jan Nieuwenhuizen % fet_begingroup ("scripts"); def draw_fermata = save alpha, radius, crook_thinness, crook_fatness, dot_size; save pat; path pat; % [Wanske] and some Baerenreiter editions % suggest about 80 degrees instead of a half-circle alpha := 10; radius# = 1.25 staff_space#; crook_thinness# = 1.5 linethickness#; crook_fatness# = 0.25 staff_space# + 1.5 linethickness#; radius# + crook_fatness# / 2 = h#; radius# + crook_thinness# / 2 = w#; set_char_box (w#, w#, crook_thinness# / 2, h#); define_pixels (radius, crook_thinness, crook_fatness); dot_size# = 8/6 crook_fatness#; define_whole_blacker_pixels (dot_size); penpos1 (crook_thinness, 0); penpos2 (crook_fatness, -90); z1 = (-radius, 0); z2 = (0, radius); pat := z2l{left} .. z1l{dir (-alpha - 90)} .. {dir (90 - alpha)}z1r .. {right}z2r; pat := pat -- reverse pat xscaled -1 shifted (-feta_eps, 0) -- cycle; fill pat; pickup pencircle scaled dot_size; x4 = 0; bot y4 = vround (-crook_thinness / 2); drawdot z4; enddef; fet_beginchar ("fermata up", "ufermata"); draw_fermata; penlabels (1, 2, 4); fet_endchar; fet_beginchar ("fermata down", "dfermata"); draw_fermata; y_mirror_char; fet_endchar; def draw_short_fermata = save fat_factor, thinness, dot_size; save left_dist, right_dist, se, ne; pair left_dist, right_dist, se, ne; set_char_box (staff_space#, staff_space#, 0, 2.2 staff_space#); dot_size# = 0.266 staff_space# + 2.666 linethickness#; define_whole_blacker_pixels (dot_size); fat_factor = .11; thinness = 1.5 linethickness; pickup pencircle scaled thinness; rt x2 = w; lft x5 = -b; bot y5 = 0; top y3 = h; y1 = y2 = y5; x3 = 0; z1 - z4 = whatever * (charwd, -charht); z4 = fat_factor [z3, z5]; ne = unitvector (z3 - z5); se = unitvector (z2 - z3); left_dist = (ne rotated 90) * 0.5 thinness; right_dist = (se rotated 90) * 0.5 thinness; fill bot z5{right} .. (z5 - left_dist){ne} -- (((z5 - left_dist) -- (z3 - left_dist)) intersectionpoint ((z1 - right_dist) -- (z4 - right_dist))) -- (z1 - right_dist){se} .. bot z1{right} -- bot z2{right} .. (z2 + right_dist){-se} -- (z3 + right_dist){-se} .. top z3 .. (z3 + left_dist){-ne} -- (z5 + left_dist){-ne} .. cycle; pickup pencircle scaled dot_size; x1 - 2 x6 = x2; x6 := vround (x6); bot y6 = -d; drawdot z6; enddef; fet_beginchar ("short fermata up", "ushortfermata"); draw_short_fermata; labels (1, 2, 3, 4, 5, 6); fet_endchar; fet_beginchar ("short fermata down", "dshortfermata"); draw_short_fermata; xy_mirror_char; fet_endchar; def draw_long_fermata = save stemthick, beamheight, dot_size, wd; save pat; path pat; wd# = 2.5 staff_space#; stemthick = hround (1.5 linethickness); beamheight = 0.3 staff_space + linethickness; dot_size# = 0.266 staff_space# + 2.666 * linethickness#; define_pixels (wd); define_whole_blacker_pixels (dot_size); set_char_box (wd# / 2, wd# / 2, 0, 3/2 staff_space#); pickup pencircle scaled blot_diameter; top y1 = h; lft x1 = -b; pat := top z1{left} .. {down}lft z1; pickup pencircle scaled stemthick; x2 = -b + stemthick; y2 = h - beamheight; lft x3 = -b; bot y3 = -d; pat := pat -- lft z3 .. bot z3 .. rt z3 -- z2; pat := pat -- reverse pat xscaled -1 shifted (-feta_eps, 0) -- cycle; fill pat; pickup pencircle scaled dot_size; x4 = 0; bot y4 = -d; drawdot z4; enddef; fet_beginchar ("long fermata up", "ulongfermata"); draw_long_fermata; labels (1, 2, 3, 4); fet_endchar; fet_beginchar ("long fermata down", "dlongfermata"); draw_long_fermata; y_mirror_char; fet_endchar; def draw_very_long_fermata = save ibeamheight, obeamheight; save ihwd, ohwd, iht, oht; % inner/outer half_width/height save stemthick, dot_size; save opat, ipat; path opat, ipat; ihwd# = 1.0 staff_space#; ohwd# = 1.5 staff_space#; iht# = 0.9 staff_space#; oht# = 1.6 staff_space#; define_pixels (ihwd, ohwd, iht, oht) stemthick = hround (1.5 linethickness); ibeamheight# = 0.3 staff_space#; obeamheight# = 0.5 staff_space#; define_pixels (ibeamheight, obeamheight); dot_size# = (iht# - ibeamheight#) * 8/10; define_whole_blacker_pixels (dot_size); set_char_box (ohwd#, ohwd#, 0, oht#); pickup pencircle scaled blot_diameter; top y1 = oht; lft x1 = -ohwd; top y11 = iht; lft x11 = -ihwd; opat := top z1{left} .. {down}lft z1; ipat := top z11{left} .. {down}lft z11; pickup pencircle scaled stemthick; x2 = -ohwd + stemthick; y2 = oht - obeamheight; lft x3 = -ohwd; bot y3 = 0; x12 = -ihwd + stemthick; y12 = iht - ibeamheight; lft x13 = -ihwd; bot y13 = 0; opat := opat -- lft z3 .. bot z3 .. rt z3 -- z2; opat := opat -- reverse opat xscaled -1 shifted (-feta_eps, 0) -- cycle; ipat := ipat -- lft z13 .. bot z13 .. rt z13 -- z12; ipat := ipat -- reverse ipat xscaled -1 shifted (-feta_eps, 0) -- cycle; fill opat; fill ipat; pickup pencircle scaled dot_size; x4 = 0; bot y4 = -d; drawdot z4; enddef; fet_beginchar ("very long fermata up", "uverylongfermata"); draw_very_long_fermata; labels (1, 2, 3, 11, 12, 13, 4); fet_endchar; fet_beginchar ("very long fermata down", "dverylongfermata"); draw_very_long_fermata; y_mirror_char; fet_endchar; % % Thumbs are used in cello music. % TODO : thumbs should look like the finger-font and should be placed in % the same way in the score. % fet_beginchar ("Thumb", "thumb"); save thin, height, width, thick, depth; height# = 5/4 width#; height# = staff_space#; depth# = 1.6 (height# / 2); set_char_box (width# / 2, width# / 2, depth#, height# / 2); define_pixels (height, width); thin = .6 linethickness + 0.06 staff_space; 2 thick + 0.5 (height - 2 thin) = width; penpos1 (thick, 0); penpos2 (thin, 90); penpos3 (thick, 180); penpos4 (thin, 270); z1r = (w, 0); z2r = (0, h); z3r = (-w, 0); z4r = (0, -h); penlabels (1, 2, 3, 4); penstroke z1e{up} .. z2e{left} .. z3e{down} .. z4e{right} .. cycle; save brush_thick; y5 = -d + brush_thick / 2; brush_thick = 0.9 thick; x5 = 0; labels (5); draw_brush (z4r, 1.4 thin, z5, brush_thick); fet_endchar; % % `\accent' is TeX reserved. % def draw_accent (expr bottom_left, top_right, thickness, diminish) = save thinning_start; thinning_start = 0.4; pickup pencircle scaled thickness; lft x1 = xpart bottom_left; top y1 = ypart top_right; lft x6 = xpart bottom_left; bot y6 = ypart bottom_left; rt z4 = (xpart top_right, (ypart top_right + ypart bottom_left) / 2); x5 = x3 = thinning_start [xpart top_right, xpart bottom_left] - linethickness + 0.1 staff_space; z3 = whatever [z1, z4]; z5 = whatever [z6, z4]; penpos1 (thickness, angle (z3 - z1) + 90); penpos3 (thickness, angle (z3 - z1) + 90); penpos4 (thickness, 90); penpos5 (thickness, angle (z6 - z5) + 90); penpos6 (thickness, angle (z6 - z5) + 90); x4 - x7 = diminish * thickness; y7 = y4; fill z1l -- z3l -- z7 -- z5l -- z6l .. lft z6{down} .. bot z6 .. z6r -- z4l ..tension 0.8.. rt z4 ..tension 0.8.. z4r -- z1r .. top z1 .. lft z1{down} .. cycle; enddef; fet_beginchar ("> accent", "sforzato"); set_char_box (.9 staff_space#, .9 staff_space#, .5 staff_space#, .5 staff_space#); draw_accent ((-w, -d), (w, h), 0.05 staff_space + linethickness, 0.7); penlabels (1, 3, 4, 5, 6); labels (7); fet_endchar; fet_beginchar ("espr", "espr"); set_char_box (1.9 staff_space#, 1.9 staff_space#, .5 staff_space#, .5 staff_space#); draw_accent ((w - 1.78 staff_space, -d), (w, h), 0.05 staff_space + linethickness, 0.6); addto currentpicture also currentpicture xscaled -1; fet_endchar; fet_beginchar ("staccato dot", "staccato"); save radius; radius# = 0.20 * staff_space#; define_whole_pixels (radius); pickup pencircle scaled 2 radius; drawdot (0, 0); set_char_box (radius#, radius#, radius#, radius#); fet_endchar; def draw_staccatissimo = save radius, height; height# = .8 staff_space#; radius# = linethickness# + .1 staff_space#; define_whole_blacker_pixels (radius); define_pixels (height); draw_brush ((0, 0), linethickness, (0, height), 2 radius); set_char_box (radius#, radius#, blot_diameter# / 2, height# + radius#); enddef; fet_beginchar ("staccatissimo/martellato up", "ustaccatissimo"); draw_staccatissimo; fet_endchar; fet_beginchar ("staccatissimo/martellato down", "dstaccatissimo"); draw_staccatissimo; y_mirror_char; fet_endchar; fet_beginchar ("portato/single tenuto", "tenuto"); save thick; thick# = 1.6 linethickness#; define_whole_blacker_pixels (thick); set_char_box (.6 staff_space#, .6 staff_space#, thick# / 2, thick# / 2); draw_rounded_block ((-b, -thick / 2), (w, thick / 2), thick); fet_endchar; def draw_portato = save thick, dot_size; thick# = 1.4 linethickness#; dot_size# = 2.4 linethickness# + 0.08 staff_space#; define_whole_blacker_pixels (thick, dot_size); set_char_box (.6 staff_space#, .6 staff_space#, thick# / 2, .5 staff_space# + .5 dot_size#); draw_rounded_block ((-b, -thick / 2), (w, thick / 2), thick); pickup pencircle scaled dot_size; drawdot (0, h); enddef; fet_beginchar ("portato/tenuto with staccato", "uportato"); draw_portato; fet_endchar; fet_beginchar ("portato/tenuto with staccato", "dportato"); draw_portato; y_mirror_char fet_endchar; def draw_marcato = save fat_factor, thinness; save left_dist, right_dist, ne, se; pair left_dist, right_dist, ne, se; set_char_box (staff_space# / 2, staff_space# / 2, 0, 1.1 staff_space#); fat_factor = .3; thinness = linethickness; pickup pencircle scaled thinness; rt x2 = w; lft x5 = -b; bot y5 = 0; top y3 = h; y1 = y2 = y5; x3 =0; z1 - z4 = whatever * (charwd, -charht); z4 = fat_factor [z3, z5]; ne = unitvector (z3 - z5); se = unitvector (z2 - z3); left_dist = (ne rotated 90) * 0.5 thinness; right_dist = (se rotated 90) * 0.5 thinness; fill bot z5{right} .. (z5 - left_dist){ne} -- (((z5 - left_dist) -- (z3 - left_dist)) intersectionpoint ((z1 - right_dist) -- (z4 - right_dist))) -- (z1 - right_dist){se} .. bot z1{right} -- bot z2{right} .. (z2 + right_dist){-se} -- (z3 + right_dist){-se} .. top z3 .. (z3 + left_dist){-ne} -- (z5 + left_dist){-ne} .. cycle; enddef; fet_beginchar ("marcato up", "umarcato"); draw_marcato; labels (1, 2, 3, 4, 5); fet_endchar; % % The down marcato char (not very much used). % Contrary to what some MF/TeX `gurus' believe % it is *point*-symmetric with the "up" version % fet_beginchar ("marcato down", "dmarcato"); draw_marcato; xy_mirror_char; fet_endchar; % % used in french horn music todo % % TODO: too light at 20pt % fet_beginchar ("open (unstopped)", "open"); save thin, height, width, thick; height# = 5/4 width#; height# = staff_space#; thin = .6 linethickness + 0.06 staff_space; set_char_box (width# / 2, width# / 2, height# / 2, height# / 2); define_pixels (width, height); 2 thick + 0.6 (height - 2 thin) = width; penpos1 (thick, 0); penpos2 (thin, 90); penpos3 (thick, 180); penpos4 (thin, 270); z1r = (w, 0); z2r = (0, h); z3r = (-w, 0); z4r = (0, -h); penlabels (1, 2, 3, 4); penstroke z1e{up} .. z2e{left} .. z3e{down} .. z4e{right} .. cycle; fet_endchar; fet_beginchar ("plus (stopped)", "stopped"); save hthick, vthick, size, outer_hsize, outer_vsize; hthick# = vthick# = 2 linethickness#; size# = 1.1 staff_space#; define_whole_blacker_pixels (vthick); define_whole_vertical_blacker_pixels (hthick); set_char_box (size# / 2, size# / 2, size# / 2, size# / 2); outer_hsize = hround ((b + w - vthick) / 2); outer_vsize = vround ((h + d - hthick) / 2); w := b := (2 outer_hsize + vthick) / 2; h := d := (2 outer_vsize + hthick) / 2; draw_rounded_block ((-b, -d + outer_vsize), (w, -d + outer_vsize + hthick), hthick); draw_rounded_block ((-b + outer_hsize, -d), (-b + outer_hsize + vthick, h), vthick); fet_endchar; fet_beginchar ("Upbow", "upbow"); save ht, wd, thick; thick = 1.4 linethickness; wd# = 1.3 staff_space#; ht# = 1.6 wd#; set_char_box (wd# / 2, wd# / 2, 0, ht#); draw_accent ((-h, -w), (0, w), thick, 0.9); currentpicture := currentpicture rotated -90; fet_endchar; fet_beginchar ("Downbow", "downbow"); save stemthick, beamheight, wd; save pat; path pat; wd# = 1.5 staff_space#; define_pixels (wd); stemthick = hround (1.2 linethickness); set_char_box (wd# / 2, wd# / 2, 0, 4/3 staff_space#); beamheight = 4/10 h; pickup pencircle scaled blot_diameter; top y1 = h; lft x1 = -b; pat := top z1{left} .. {down}lft z1; pickup pencircle scaled stemthick; x2 = -b + stemthick; y2 = h - beamheight; lft x3 = -b; bot y3 = -d; pat := pat -- lft z3 .. bot z3 .. rt z3 -- z2; pat := pat -- reverse pat xscaled -1 shifted (-feta_eps, 0) -- cycle; fill pat; labels (1, 2, 3); fet_endchar; % % Inspired by a computer-set version of Auf dem Strom by Baerenreiter. % def draw_turn = save thin, thick, ball_diam, darkness; save wd, ht, thick_nibangle, ball_nib_thick; save turndir; pair turndir; wd# = 35/16 staff_space#; ht# = 18/17 staff_space#; darkness = 0.3 linethickness + 0.09 staff_space; set_char_box (wd# / 2, wd# / 2, ht# / 2, ht# / 2); thick_nibangle = 60; thick = 3 darkness; thin = darkness; ball_nib_thick = 2.7 darkness; ball_diam = ball_nib_thick + (h - ball_nib_thick) / 10; x3l = w; y3 = 0; y4l = h; x4 = x2; x2l = w / 2; y2l = -d; z1 = (0,0); penpos1 (1.1 thick, thick_nibangle); penpos2 (thick, thick_nibangle); penpos3 (thin, 180); penpos4 (ball_nib_thick, -90); path swoosh, ploop; swoosh := z1l{curl 0} .. z2l .. z3l{up} .. {left}z4l -- z4r .. z3r{down} .. z2r{left}; fill swoosh .. swoosh scaled -1 shifted (-feta_eps, -feta_eps) .. cycle; x5r = x4; y5r = y4l - ball_diam / 2; z6r = z5r; penpos5 (1.6 ball_diam / 2, 10); penpos6 (ball_diam / 2, 150); ploop := z4l{left} .. z5l .. z6l -- cycle; fill ploop; fill ploop scaled -1 shifted (-feta_eps, -feta_eps); enddef; fet_beginchar ("Reverse turn", "reverseturn"); draw_turn; currentpicture := currentpicture yscaled -1; fet_endchar; fet_beginchar ("Turn", "turn"); draw_turn; penlabels (1, 2, 3, 4, 5, 6, 7); fet_endchar; % % Inspired by a (by now) PD edition of Durand & C'ie edition of % Saint-Saens' Celloconcerto no. 1 % % FIXME take out hardcoded vars. % FIXME the two loops on the `t' should be smoother (and the left one bigger). % FIXME generic macros for serifs: top of the t and bottom of r % fet_beginchar ("Trill (`tr')", "trill"); save start_nib_angle, ascender_extra, ex, hair_thick, fatness; save slant, t_fatness, r_fatness, kerning, t_overshoot; save uitschieter, bulb_size, krul_ang; save u, v; ascender_extra# = 1/2 ex#; ascender# = ascender_extra# + ex#; ex# = 1.4 staff_space#; kerning# = 0.6 ex#; start_nib_angle = 20; bulb_size = 0.8; define_pixels (ex, ascender_extra, ascender, kerning); t_overshoot = 0.03 ex; fatness = 12/40 ex; t_fatness = 0.78 fatness; t_width = 1.9 t_fatness; r_fatness = 0.78 fatness; uitschieter = 0.48 ex; hair_thick = linethickness; r_flare = .5 hair_thick + 0.25 r_fatness; r_width = 2 r_fatness + 0.25 kerning; slant = .2; local_copy (transform)(currenttransform); currenttransform := currenttransform slanted slant shifted (-staff_space, 0); set_char_box (.85 staff_space#, .85 staff_space#, 0, ascender#); y1 = ascender; % try to position in such a way that the center is the visual % center x1l = 0.2 staff_space; x1r - x1l = t_fatness; penpos1 (start_nib_wid, start_nib_angle); z2 = (x1, 7/18 ex); penpos2 (start_nib_wid, start_nib_angle); z3l = (x2l + 0.5 t_width, - t_overshoot); z4l = (x2l + t_width, 0.23 ex); penpos4 (whatever, 180); % 200 x4l - x4r = hair_thick; x3r = 0.5 [x4r, x2r]; % 1.7 [x3l, x3r] = x4r; y3r - y3l = 0.6 t_fatness; save krul_p; path krul_p; krul_ang = 32; pickup pencircle scaled hair_thick; z5 = (x2l + t_fatness / 2, 2/3 ex); lft x6 = x2l - uitschieter; y6 = y5; % - 1/20 ex; z7 = z5 + whatever * dir krul_ang; up_angle = krul_ang; % = angle (z7-z5) x7 = 5/10 kerning + x5; krul_p := z4{up} ..tension 0.98.. z5 .. z6 .. z5{z7 - z5} -- z7; z4' = point 0.85 of krul_p; penpos4' (hair_thick, angle (direction 0.85 of krul_p) + 90); % the body of the `t' and the bottom loop fill z1r{dir (angle (z1l - z1r) + 30)} .. z1l{-dir (angle (z1r - z1l) - 45)} -- z2l{down} ..tension (1 + .5 slant).. z3l{right} .. z4l{up} .. z4'l{direction 0.85 of krul_p} -- z4'r{-direction 0.85 of krul_p} .. z4r{down} .. z3r{left} ..tension (1.5 + .7 slant).. z2r{up} -- cycle; z5' = point 1.1 of krul_p; penpos5' (hair_thick, angle (direction 1.1 of krul_p) + 90); z5'' = point 1.5 of krul_p; penpos5'' (hair_thick, angle (direction 1.5 of krul_p) + 90); z5''' = point 1.8 of krul_p; penpos5''' (hair_thick, angle (direction 1.8 of krul_p) + 90); z6 = point 2 of krul_p; penpos6 (hair_thick, angle (direction 2 of krul_p) + 90); z6' = point 2.3 of krul_p; penpos6' (hair_thick, angle (direction 2.3 of krul_p) + 90); z6'' = point 2.6 of krul_p; penpos6'' (hair_thick, angle (direction 2.6 of krul_p) + 90); z6''' = point 2.9 of krul_p; penpos6''' (hair_thick, angle (direction 2.9 of krul_p) + 90); penpos7 (hair_thick, up_angle + 90); z7' = point 3.2 of krul_p; penpos7' (hair_thick, angle (direction 3.2 of krul_p) + 90); % the left loop penstroke z5'e{direction 1.1 of krul_p} .. z5''e{direction 1.5 of krul_p} .. z5'''e{direction 1.8 of krul_p} .. z6e{direction 2 of krul_p} .. z6'e{direction 2.3 of krul_p} .. z6''e{direction 2.6 of krul_p} .. {direction 2.9 of krul_p}z6'''e; y9 = 3/4 ex; x9 = x1 + kerning; penpos9 (r_fatness, 0); x10 = x9; y10 = -0.3 linethickness; penpos10 (r_fatness, 0); penpos11 (hair_thick, -4); z11r = z9r; z13l = (x9l + r_width, y11 - linethickness); penpos13 (r_flare, 180); z15 = z13r - (bulb_size * r_fatness, 0); z14 = 0.5 [z13l, z15] - (0, bulb_size * r_fatness); save before, after; path before, after; before := z13l{up} .. {down}z11l; after := z9r{up} .. z7r{z7' - z7} -- z7'r; (u, v) = before intersectiontimes after; save before_bulb, after_bulb; path before_bulb, after_bulb; before_bulb := z9r{up} ..tension 0.94.. z13r{down}; after_bulb := z13l{up} ..tension 1.06.. z15{down}; (u_bulb, v_bulb) = before_bulb intersectiontimes after_bulb; % the connection between `t' and `r', the body of the `r', % and the bulb fill z7'l -- z7l{z7 - z7'} .. z9l{down} -- simple_serif (z10l, z10r, -30) -- z9r{up} .. subpath (0, u_bulb) of before_bulb .. subpath (v_bulb, infinity) of after_bulb .. z14 .. z13l{up} .. subpath (0, u) of before .. subpath (v, infinity) of after -- cycle; penlabels (range 1 thru 15); penlabels (4', 5', 5'', 5''', 6', 6'', 6''', 7'); fet_endchar; def draw_heel = save radius, thickness; save pat; path pat; radius# := .5 staff_space#; set_char_box (radius#, radius#, radius#, 2/3 staff_space#); thickness := hround (1.5 linethickness); pickup pencircle scaled thickness; rt x1 = b; top y1 = h; x2 =x1; y2 = 0; x3 = 0; bot y3 = -d; pat := top z3{right} .. lft z2{up} -- lft z1 .. top z1 .. rt z1 -- rt z2{down} .. bot z3{left}; pat := pat -- reverse pat xscaled -1 shifted (-feta_eps, 0) -- cycle; fill pat; enddef; fet_beginchar ("left heel", "upedalheel"); draw_heel; labels (1, 2, 3); fet_endchar; fet_beginchar ("right heel", "dpedalheel"); draw_heel; y_mirror_char; fet_endchar; def draw_toe = save ht, wd, thickness; thickness := 1.5 linethickness; ht# := 1.5 staff_space#; wd# := 1/3 ht#; define_pixels (ht, wd); set_char_box (wd#, wd#, 0, ht#); draw_accent ((-h, -w), (0, w), thickness, 0.9); currentpicture := currentpicture rotated -90; enddef; fet_beginchar ("left toe", "upedaltoe"); draw_toe; fet_endchar; fet_beginchar ("right toe", "dpedaltoe"); draw_toe; y_mirror_char; fet_endchar; fet_beginchar ("Flageolet", "flageolet"); save height, width, thickness, superness; height# = 4/15 staffsize#; width# = height#; thickness# = blot_diameter#; define_pixels (height, width); define_whole_blacker_pixels (thickness); set_char_box (width# / 2, width# / 2, height# / 2, height# / 2); penpos1 (thickness, 90); penpos2 (thickness, 180); penpos3 (thickness, 270); penpos4 (thickness, 0); x1 = 0; y1r = h; x4r = w; x2r = -x4r; y2 = 0; y4 = y2; x3 = x1; y3r = -y1r; penlabels (1, 2, 3, 4); % mf doesn't handle pixel dropouts in outline objects, so we use % `draw' if not called by mpost if known miterlimit: penstroke z1e .. z2e .. z3e .. z4e .. cycle; else: pickup pencircle scaled thickness; draw z1 .. z2 .. z3 .. z4 .. cycle; fi; fet_endchar; % % TODO: ARGRGHGH code dup. % fet_beginchar ("Segno", "segno"); save thin, thick, ball_diam, darkness, pointheight; save wd, ht, thick_nibangle, ball_nib_thick; save turndir; pair turndir; ht# = 3 staff_space#; wd# = 2 staff_space#; darkness = .08 staff_space + 0.4 linethickness; set_char_box (wd# / 2, wd# / 2, ht# / 2, ht# / 2); thick_nibangle = 30; thick = 3 darkness; thin = darkness; ball_nib_thick = 2.7 darkness; ball_diam = ball_nib_thick + (w - ball_nib_thick) / 10; pointheight = 2 linethickness; y3l = h; 2 x3 = x2 + x4; x4 = 0; y4 = y2; y2l = .6 h; x2l = -b; z1 = (0, 0); penpos1 (thick, 2 thick_nibangle); penpos2 (thick, thick_nibangle); penpos3 (thin, -90); penpos4 (ball_nib_thick, 180 - thick_nibangle); save swoosh, ploop; path swoosh, ploop; swoosh := z1l{curl 0} .. z2l .. z3l{right} .. {down}z4l -- z4r .. z3r{left} .. z2r{down}; fill swoosh .. (swoosh scaled -1) .. cycle; y5r = y4; x5r = x4l - ball_diam / 2; z6r = z5r; penpos5 (1.6 ball_diam / 2, 100); penpos6 (ball_diam / 2, 240); ploop := z4l{down} .. z5l .. z6l -- cycle; fill ploop; fill ploop scaled -1; penpos7 (2 thin, 0); z7l = (-b, -d); penpos8 (2 thin, 0); z8r = (w, h); penstroke z7e -- z8e; pickup pencircle scaled 2 thin; drawdot (-x2r, pointheight); drawdot (x2r, -pointheight); penlabels (range 1 thru 8); fet_endchar; fet_beginchar ("Coda", "coda"); save stickout, thin, thick, codawidth, codaheight; stickout# = 0.35 staff_space#; codawidth# = 2/3 staff_space#; codaheight# = staff_space#; define_pixels (codawidth, codaheight); set_char_box (codawidth# + stickout#, codawidth# + stickout#, codaheight# + stickout#, codaheight# + stickout#); thin = 1.2 linethickness; 0.1 (codaheight - 2 thin) = (codawidth - 2 thick); penpos1 (thick, 0); penpos2 (thin, -90); penpos3 (thick, -180); penpos4 (thin, -270); x1l = -codawidth; y2l = codaheight; y1 = 0; x2 = 0; z3 = -z1; z4 = -z2; penlabels (1, 2, 3, 4); fill z1l{up} .. z2l{right} .. z3l{down} .. z4l{left} .. cycle; unfill z1r{up} .. z2r{right} .. z3r{down} .. z4r{left} .. cycle; draw_gridline ((0, -h), (0, h), thin); draw_gridline ((-w, 0), (w, 0), thin); fet_endchar; fet_beginchar ("Varied Coda", "varcoda"); save thin, thick, codawidth, codaheight; thin# = 1.2 linethickness#; thick# = 1.0 linethickness# + 0.25 staff_space#; codawidth# = 2/3 staff_space#; codaheight# = staff_space#; define_pixels (thin, thick, codawidth, codaheight); set_char_box (codawidth# + thick#, codawidth# + thick#, codaheight# + thick#, codaheight# + thick#); x1 = -codawidth + thick - .5 blot_diameter; y1 = y2 - thin; x2 = codawidth - thick + .5 blot_diameter; y2 = codaheight; draw_square_block (z1, z2); x3 = -codawidth; y3 = -codaheight; x4 = x3 + thick; y4 = y2; draw_block (z3, z4); labels (1, 2, 3, 4); addto currentpicture also currentpicture scaled -1; draw_gridline ((0, -h), (0, h), thin); draw_gridline ((-w, 0), (w, 0), thin); fet_endchar; def draw_comma = save alpha, thick, thin, ht; alpha := 35; thin# = 1.2 linethickness#; thick# = 3 linethickness#; ht# = .6 staff_space#; define_pixels (thin, thick, ht); set_char_box (0, .5 staff_space#, ht#, ht#); penpos1 (thick, alpha); penpos2 (thick, alpha + 90); penpos3 (thin, 180 - alpha); penpos4 (thin, 90 - alpha); x3r = 0; x1l = x3l; y2r = -y4l = h; z1 = z2; z3 = z4; fill z1l{dir (alpha + 90)} .. z2r{dir alpha} .. z1r{dir (alpha - 90)} .. z3l{dir (270 - alpha)} .. z4l{dir (180 - alpha)} .. z3r{dir (90-alpha)} .. cycle; enddef; fet_beginchar ("Right Comma", "rcomma"); draw_comma; penlabels (1, 2, 3, 4); fet_endchar; fet_beginchar ("Left Comma", "lcomma"); draw_comma; xy_mirror_char; fet_endchar; def draw_varcomma = save thick, thin, ht, wd, alpha; alpha := 35; thin# = 1.2 linethickness#; thick# = 3 linethickness#; ht# = .6 staff_space#; wd# = .25 staff_space#; define_pixels (thin, thick, ht, alpha); set_char_box (wd#, wd#, ht#, ht#); z1 = (-b, -d); z2 = (w, h); draw_brush (z1, thin, z2, thick); enddef; fet_beginchar ("Right Varied Comma", "rvarcomma"); draw_varcomma; labels (1, 2); fet_endchar; fet_beginchar ("Left Varied Comma", "lvarcomma"); draw_varcomma; xy_mirror_char; fet_endchar; thick# := 1/24 designsize; define_blacker_pixels (thick); rthin := 0.075 * staff_space + 0.5 linethickness; rthick := 2 thick + rthin; def draw_arpeggio = save alpha; save ne, nw, se, sw; save x, y; pair ne, nw, se, sw; alpha := -40; nw = dir (alpha + 180); ne = dir (alpha + 90); se = dir alpha; sw = dir (alpha - 90); penpos1 (rthin, alpha + 90); penpos2 (5/4 rthick, alpha); penpos3 (3/4 rthick, alpha); penpos4 (5/4 rthick, alpha); penpos5 (rthin, alpha + 90); z1 = (width / 2, height) - overshoot * se; z2 = 2 [z4, (width / 2, height / 2)]; z3 = 1/2 [z2, z4]; x4 = 2/8 staff_space; y4 = rthin; z5 = 2 [z1, (width / 2, height / 2)]; z6 = z2l + 1/2 rthin * sw; z7 = z4l + 1/2 rthin * sw + 1/2 rthin * se; z8 = 2 [z6, (width / 2, height / 2)]; z9 = 2 [z7, (width / 2, height / 2)]; fill z1l{se} -- z6 .. z3l .. z7{se} -- z5l .. z5r{nw} -- z8 .. z3r .. z9{nw} -- z1r .. cycle; enddef; fet_beginchar ("Arpeggio", "arpeggio"); save height, overshoot, width; height# = staff_space#; width# = 0.8 height#; overshoot# = 0.25 staff_space#; define_pixels (height, overshoot, width); set_char_box (0, width#, 0, height#); draw_arpeggio; penlabels (range 1 thru 9); draw_staff (-2, 2, 0.0); fet_endchar; % % Extendable Trill symbol. % Not yet used % Rename me to Trill, rename Trill to Tr? % fet_beginchar ("Trill_element", "trill_element"); save height, overshoot; height# = staff_space#; width# = 0.8 height#; overshoot# = 0.25 staff_space#; define_pixels (height, overshoot, width); set_char_box (0, height#, 0, width#); draw_arpeggio; currentpicture := currentpicture shifted -(width / 2, height / 2); currentpicture := currentpicture rotated 90; currentpicture := currentpicture shifted (height / 2, width / 2); fet_endchar; % % Arpeggio arrow by Chris Jackson % def draw_arpeggio_arrow = save thinness, height, width, overshoot; save nw, ne, se, sw; save alpha; save before_left, before_right, after_left, after_right; save u_left, v_left, u_right, v_right; pair nw, ne, se, sw; path before_left, before_right, after_left, after_right; height# = staff_space#; width# = 0.8 height#; overshoot# = 0.25 staff_space#; define_pixels (height, overshoot, width); set_char_box (0, width#, 0, height#); alpha := -40; nw = dir (alpha + 180); ne = dir (alpha + 90); se = dir alpha; sw = dir (alpha - 90); penpos1 (rthin, alpha + 90); penpos2 (5/4 rthick, alpha); penpos3 (5/4 rthick, 0); z1 = (width / 2, height) - overshoot * se; % numbering is consistent % with the arpeggio symbol z2 = 2 [z4, (width / 2, height / 2)]; z3 = (0.5 width, 0.5 height); z4 = (0.25 staff_space, rthin); z6 = z2l + 1/2 rthin * sw; z9 = (width / 2, height) + overshoot * se; pickup pencircle scaled vround (0.5 rthin); bot z10 = (0.5 w, 0); lft z11 = (0.5 w - hround (0.8 w), 0.8 h); rt z12 = (0.5 w + hround (0.8 w), 0.8 h); before_left := z1l -- z6{z6 - z1l} .. {down}z3l; after_left := (z3 + (0, -0.25 rthin / cosd (angle (nw)))) -- (z11 + 0.25 rthin * ne); (u_left, v_left) = before_left intersectiontimes after_left; before_right := (z12 + 0.25 rthin * nw) -- (z3 + (0, -0.25 rthin / cosd (angle (nw)))); after_right := z3r{up} .. z9{z1r - z9} -- z1r; (u_right, v_right) = before_right intersectiontimes after_right; fill subpath (0, u_left) of before_left .. subpath (v_left, infinity) of after_left .. top z11 .. lft z11 .. {dir -50}(z11 + 0.25 rthin * sw) .. (z10 + 0.25 rthin * sw){dir -70} .. bot z10 .. {dir 70}(z10 + 0.25 rthin * se) .. (z12 + 0.25 rthin * se){dir 50} .. rt z12 .. top z12 .. subpath (0, u_right) of before_right .. subpath (v_right, infinity) of after_right .. cycle; % mf doesn't handle pixel dropouts in outline objects, so we use % `draw' if not called by mpost if not known miterlimit: pickup pencircle scaled 0.7 rthin; draw z1 -- (z9 + 0.5 rthin * dir (alpha - 90)); fi; enddef; fet_beginchar ("Arpeggio arrow down", "arpeggio.arrow.M1"); draw_arpeggio_arrow; penlabels (range 1 thru 12); fet_endchar; fet_beginchar ("Arpeggio arrow up", "arpeggio.arrow.1"); draw_arpeggio_arrow; currentpicture := currentpicture scaled -1 shifted (w - feta_eps, h - feta_eps); fet_endchar; % Hmm input feta-slag; % % Railroad tracks. We define two variants of these -- both as slightly % tapered, comma-shaped curves and as two straight parallel slashes. % fet_beginchar ("Curved caesura", "caesura.curved"); save slant, space_between, clearance; save alpha, pat; save botthick, topthick; save krom; path pat; botthick = 1.5 linethickness; topthick = 2.5 linethickness; pickup pencircle scaled botthick; slant = 3.5; space_between# = 0.6 staff_space#; clearance# = 0.2 staff_space#; height# = 1.2 staff_space#; set_char_box (0, 2.0 staff_space#, staff_space# - clearance#, height#); define_pixels (clearance, height); define_whole_pixels (space_between); bot y1 = -d; top y2 = h; lft x1 = 0; x2 = (y2 - y1) / slant; krom = 10; alpha = angle (z2 - z1); penpos1 (botthick, alpha - krom); penpos3 (botthick, alpha - krom + 90); penpos2 (topthick, alpha + krom); penpos4 (topthick, alpha + krom + 90); z3 = z1; z4 = z2; penlabels (1, 2, 3, 4); pat := z3r{(z1r - z1l)} .. z4r{z2r-z2l} .. z2r{z4l-z4r} .. z4l{z2l-z2r} .. z3l{z1l-z1r} .. z1l{z3r-z3l} .. cycle; fill pat; fill pat shifted (space_between, 0); fet_endchar; fet_beginchar ("Straight caesura", "caesura.straight"); save slant, space_between, clearance; save thick, ne, pat; path pat; pair ne; slant = 2.0; thick = 2.88 linethickness; space_between# = 0.56 staff_space#; clearance# = 0.2 staff_space#; set_char_box (0, 2.0 staff_space#, staff_space# - clearance#, 1.2 staff_space#); define_whole_pixels (space_between); x1 = 0; x2 = x1 + thick; y1 = y2 = -d; x3 = x4 + thick; x4 = x1 + (h + d) / slant; y3 = y4 = h; ne = unitvector (z4 - z1); z1a = z1 + blot_diameter * ne; z1b = z1 + blot_diameter * right; z2a = z2 + blot_diameter * ne; z2b = z2 + blot_diameter * left; z3a = z3 - blot_diameter * ne; z3b = z3 + blot_diameter * left; z4a = z4 - blot_diameter * ne; z4b = z4 + blot_diameter * right; pat = z1a{-ne} .. {right}z1b -- z2b{right} .. {ne}z2a -- z3a{ne} .. {left}z3b -- z4b{left} .. {-ne}z4a -- cycle; fill pat; fill pat shifted (space_between, 0); labels(range 1 thru 4); labels(1a, 1b, 2a, 2b, 3a, 3b, 4a, 4b); fet_endchar; fet_endgroup ("scripts"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-slag.mf000066400000000000000000000154651404105311000236270ustar00rootroot00000000000000% -*- Fundamental -*- (emacs-20 mf mode sucks) % feta-slag.mf -- implement trill symbols % % source file of the Feta (definitely not an abbreviation for Font-En-Tja) % music font % % (c) 1998--2009 Jan Nieuwenhuizen % % this file is included by feta-scripts.mf trill_thin# = 0.1 staff_space# + 0.6 stafflinethickness#; trill_stemwidth# = trill_thin#; define_pixels (trill_thin); define_whole_blacker_pixels (trill_stemwidth); trill_thick = 1/2 staff_space; trill_overlap = 1/6 staff_space; trill_width# = 5/6 staff_space#; trill_height# = 1/2 staff_space#; define_pixels (trill_height); define_whole_pixels (trill_width); pair trill_ne; trill_ne := unitvector ((2, 3)); % % The trill element sticks out on both the left and right side % of the normal bbox, so you can glue them together easily. % % `ending' is either 0 for none, 1 for left, or 2 for right. % def draw_trillelement (expr offset, ending) = clearxy; begingroup; save nw, pat, nw_dist, ne_dist; pair nw, nw_dist, ne_dist; path pat; pickup pencircle scaled trill_thin; x1 = -.5 trill_width; y1 = 0; z3 = whatever * trill_ne + z1; top y3 = vround trill_height; z2 = z3 - (trill_thick - trill_thin) * trill_ne; bot z3' = (top z3) scaled -1; nw = unitvector (z2 - z3'); ne_dist = (nw rotated -90) * 0.5 trill_thin; nw_dist = (trill_ne rotated 90) * 0.5 trill_thin; z5 = whatever * trill_ne + (z1 - nw_dist); z5 = whatever * nw + (z3' - ne_dist); pat := z5 -- (z1 - nw_dist){-trill_ne} -- (z1 + nw_dist){trill_ne} -- (z3 + nw_dist){trill_ne} .. top z3{right} .. (z3 + ne_dist){-nw}; pat := pat -- pat scaled -1 shifted (-feta_eps, -feta_eps) -- cycle; pat := pat shifted (offset, 0); fill pat; z4 = z1 - trill_ne * trill_overlap; x4 := hround (x4 + 0.5 trill_thin) - 0.5 trill_thin; pat := (z4 - nw_dist){-trill_ne} .. bot z4{left} .. lft z4{up} .. (z4 + nw_dist){trill_ne} -- (z1 + nw_dist){trill_ne} -- (z1 - nw_dist){-trill_ne} -- cycle; if ending = 1: fill pat shifted (offset, 0); elseif ending = 2: pat := pat scaled -1 shifted (-feta_eps, -feta_eps); fill pat shifted (offset, 0); fi; endgroup; enddef; fet_beginchar ("trilelement", "trilelement"); set_char_box (.5 trill_width#, .5 trill_width#, trill_height#, trill_height#); draw_trillelement (0, 0); labels (1, 2, 3, 3', 4, 5, 5'); fet_endchar; fet_beginchar ("prall", "prall"); set_char_box (trill_width#, trill_width#, trill_height#, trill_height#); draw_trillelement (-hround (.5 trill_width), 1); draw_trillelement (-hround (.5 trill_width) + trill_width, 2); fet_endchar; fet_beginchar ("mordent", "mordent"); set_char_box (trill_width#, trill_width#, 4/3 trill_height#, 4/3 trill_height#); draw_trillelement (-hround (.5 trill_width), 1); draw_trillelement (-hround (.5 trill_width) + trill_width, 2); clearxy; pickup pencircle scaled trill_stemwidth; top y1 = h; bot y2 = -d; x1 = x2; x2 = 0; draw_gridline (z2, z1, trill_stemwidth); labels (1, 2); fet_endchar; fet_beginchar ("prallprall", "prallprall"); set_char_box (1.5 trill_width#, 1.5 trill_width#, trill_height#, trill_height#); draw_trillelement (-trill_width, 1); draw_trillelement (0, 0); draw_trillelement (trill_width, 2); fet_endchar; fet_beginchar ("prallmordent", "prallmordent"); set_char_box (1.5 trill_width#, 1.5 trill_width#, 4/3 trill_height#, 4/3 trill_height#); draw_trillelement (-trill_width, 1); draw_trillelement (0, 0); draw_trillelement (trill_width, 2); clearxy; pickup pencircle scaled trill_stemwidth; top y1 = h; bot y2 = -d; x1 = x2 ; x2 = good.x (.5 trill_width); draw_gridline (z2, z1, trill_stemwidth); labels (1, 2); fet_endchar; save remember_pic; picture remember_pic; fet_beginchar ("upprall", "upprall"); set_char_box (1.5 trill_width#, 1.5 trill_width#, trill_height#, trill_height#); draw_trillelement (-trill_width, 1); draw_trillelement (0, 0); draw_trillelement (trill_width, 2); z11 = z4 shifted (-trill_width, 0); z12 = z11 + (0, -2 trill_height); penpos11 (trill_thin, angle (trill_ne) - 90); penpos12 (trill_thin, angle (trill_ne yscaled -1) + 90); penlabels (11, 12); pickup pencircle scaled trill_stemwidth; fill z11l{-trill_ne} .. z12l{trill_ne yscaled -1} .. bot z12 .. rt z12 .. z12r{-trill_ne yscaled -1} .. z11r{trill_ne} -- cycle; remember_pic := currentpicture; fet_endchar; fet_beginchar ("upmordent", "upmordent"); set_char_box (1.5 trill_width#, 1.5 trill_width#, 4/3 trill_height#, 4/3 trill_height#); currentpicture := remember_pic; clearxy; pickup pencircle scaled trill_stemwidth; top y1 = h; bot y2 = -d; x1 = x2; x2 = good.x (.5 trill_width); draw_gridline (z2, z1, trill_stemwidth); labels (1, 2); fet_endchar; fet_beginchar ("pralldown", "pralldown"); set_char_box (1.5 trill_width#, 1.5 trill_width#, trill_height#, trill_height#); currentpicture := remember_pic xscaled -1; fet_endchar; fet_beginchar ("downprall", "downprall"); set_char_box (1.5 trill_width#, 1.5 trill_width#, trill_height#, trill_height#); draw_trillelement (-trill_width, 1); draw_trillelement (0, 0); draw_trillelement (trill_width, 2); z11 = z4 shifted (-trill_width, 0); z12 = z11 + (0, 2 trill_height); penpos11 (trill_thin, angle (trill_ne xscaled -1) - 90); penpos12 (trill_thin, angle (trill_ne) - 90); penlabels (11, 12); pickup pencircle scaled trill_stemwidth; fill z11l{trill_ne xscaled -1} .. z12l{trill_ne} .. top z12 .. rt z12 .. z12r{-trill_ne} .. z11r{-trill_ne xscaled -1} -- cycle; remember_pic := currentpicture; fet_endchar; fet_beginchar ("downmordent", "downmordent"); set_char_box (1.5 trill_width#, 1.5 trill_width#, 4/3 trill_height#, 4/3 trill_height#); currentpicture := remember_pic; clearxy; pickup pencircle scaled trill_stemwidth; top y1 = h; bot y2 = -d; x1 = x2; x2 = good.x (.5 trill_width); draw_gridline (z2, z1, trill_stemwidth); labels (1, 2); fet_endchar; fet_beginchar ("prallup", "prallup"); set_char_box (1.5 trill_width#, 1.5 trill_width#, trill_height#, trill_height#); currentpicture := remember_pic xscaled -1; fet_endchar; fet_beginchar ("lineprall", "lineprall"); set_char_box (1.5 trill_width#, 1.5 trill_width#, trill_height#, 4 trill_height#); draw_trillelement (-trill_width, 1); labels (1, 2, 3, 3', 4, 5, 5'); pickup pencircle scaled trill_stemwidth; penpos10 (trill_stemwidth, 0); penpos11 (trill_stemwidth, 0); x10l = x4 - .5 trill_thin - trill_width; y10 = y4; z11 = z10 + (0, h); penlabels (10, 11); fill z11l .. top z11 .. z11r -- z10r -- z10l -- cycle; draw_trillelement (0, 0); draw_trillelement (trill_width, 2); fet_endchar; muse-4.0.0/src/share/scoreglyphs/feta-original/feta-test-generic.mf000066400000000000000000000006221404105311000252570ustar00rootroot00000000000000% % test stuff. % in a separate file to avoid tainting non-test font files for testing. % %input feta-eindelijk; input feta-toevallig; %input feta-puntje; %input feta-bolletjes; %input feta-arrow; %input feta-schrift; %input feta-schrift; %input feta-banier; % input feta-slag; %input feta-klef; %input feta-haak; % input feta-haak; %input feta-timesig; %input feta-pendaal; %input feta-accordion; muse-4.0.0/src/share/scoreglyphs/feta-original/feta-test11.mf000066400000000000000000000002631404105311000240100ustar00rootroot00000000000000% feta-test11.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 11; test := 1; % smoked cheese % test := -1; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-test13.mf000066400000000000000000000002641404105311000240130ustar00rootroot00000000000000% feta-test13.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 13; test := 1; % smoked cheese % test := -1; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-test16.mf000066400000000000000000000002641404105311000240160ustar00rootroot00000000000000% feta-test16.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 16; test := 1; % smoked cheese % test := -1; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-test20.mf000066400000000000000000000002641404105311000240110ustar00rootroot00000000000000% feta-test20.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 20; test := 1; % smoked cheese % test := -1; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-test23.mf000066400000000000000000000002641404105311000240140ustar00rootroot00000000000000% feta-test23.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 23; test := 1; % smoked cheese % test := -1; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-test26.mf000066400000000000000000000002631404105311000240160ustar00rootroot00000000000000% feta-test26.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 26; test := 1; % smoked cheese % test := -1; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta-timesig.mf000066400000000000000000000047101404105311000243310ustar00rootroot00000000000000% % feta-timesig.mf -- implement Time Signatures % % source file of the Feta (not an abbreviation of Font-En-Tja) music font % % (c) 1998--2009 Mats Bengtsson , % Christian Mondrup fet_begingroup ("timesig"); % % Originally by Mats B. nuked by Han-Wen, inspired by % Baerenreiter BA320 (Bach Cello Suites, Suite III). % % Notes: % % * The inside curve of the C is rather straight. % * The outside curve of the C is rather round. % * Right tips of the C point slightly outward. % * Lower tip protrudes to the right very slightly. % def draw_C = save hair, bulb_rad, left_fatness; save left_width, right_width; save width, lower_offset; width# := 1.8 staff_space# - stafflinethickness#; left_width := 1.0 staff_space; right_width := 0.8 staff_space; hair := stafflinethickness; bulb_rad := 0.40 staff_space - .6 stafflinethickness; left_fatness := 0.55 * staff_space; lower_offset := 0.3 stafflinethickness; set_char_box (0, width#, staff_space#, staff_space#); d := d - feta_shift; x1r = w; x3r = 0; y1r = .45 h; y2r = h + vround (.5 stafflinethickness_rounded); y4r = -d - vround (.5 stafflinethickness_rounded); x2 = x4; x2 = x3r + h; y3r = .5 (h - d); x5r = x1r + lower_offset; y5r = -0.37 h; penpos1 (hair, 10); penpos2 (stafflinethickness_rounded, 90); penpos3 (left_fatness, 180); penpos4 (stafflinethickness_rounded, -90); penpos5 (hair, -13); draw_bulb (-1, z1l, z1r, bulb_rad, .8); save s; s := 0.735; fill z1l{dir (100)} .. z2l{left} ..tension 0.8.. z3l{down} ..tension 0.8.. z4l{right} .. simple_serif (z5l, z5r, -90) .. z4r{left} .. super_curvelet (z4r, z3r, s, -1) .. z3r{up} .. super_curvelet (z3r, z2r, s, 1) .. z2r{right} .. {dir (-80)}z1r -- cycle; penlabels (1, 2, 3, 4, 5); draw_staff (-2, 2, 0); enddef; fet_beginchar ("4/4 meter", "C44"); draw_C; fet_endchar; fet_beginchar ("2/2 meter", "C22"); save eccentricity, top_stemlen, bottom_stemlen, thick, left_pos; draw_C; eccentricity = -1.75 stafflinethickness - 0.025 staff_space; top_stemlen# = bottom_stemlen# = 1.4 staff_space#; thick# = stafflinethickness# + 0.05 staff_space#; define_whole_pixels (top_stemlen, bottom_stemlen); define_whole_blacker_pixels (thick); bottom_stemlen := bottom_stemlen - feta_shift; draw_block ((x2 + eccentricity, -bottom_stemlen), (x2 + eccentricity + thick, top_stemlen)); fet_endchar; fet_endgroup ("timesig"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta-toevallig.mf000066400000000000000000000734741404105311000246730ustar00rootroot00000000000000% % feta-toevallig.mf -- implement Accidentals % % (c) 1997--2009 Han-Wen Nienhuys % % % also show in other configuration wrt staff lines. % def draw_shifted_too = if test > 0: fet_beginchar ("shifted too", ""); set_char_box (0, 0, 0, 0); currentpicture := remember_pic; draw_staff (-2, 2, 0.5); fet_endchar; fi; enddef; % % Accidentals from various sources, notably % % Baerenreiter edition of Schuberts `Auf dem Strom' (sharp, natural) % F Hofmeister edition of Muellers `Etueden fuer Horn' (double sharp, flat) % % % Naming for accidentals (including microtonal variants): % % SHARPBASE[.SLASHES.STEMS] % FLATBASE[.MODIFIER] % % Examples: % % sharp.slashslash.stem % mirroredflat.flat % % fet_begingroup ("accidentals"); % % Draw an arrow % % * `stemslant' gives the direction of the stem's left boundary % (needed for brushed stems, equals "up" for straight stems) % * `extend' is used to make the stem longer or shorter (if negative); % different kinds of accidentals need different values here % def draw_arrow (expr attach, stemwidth, stemslant, extend, pointingdown) = begingroup; save htip; % tip height save wwing; % wing `radius' save angle_wing_bot, angle_wing_top, angle_tip; save upshift; clearxy; wwing := 0.26 stemwidth; htip := staff_space * 0.85 + stafflinethickness - wwing; % `flip' is used to reflect the arrow vertically % if arrow points downward transform flip; if pointingdown: flip = identity reflectedabout (origin, right); else: flip = identity; fi; z1 = attach shifted (-stemwidth / 2, 0); upshift := max (0, wwing + 0.1 staff_space + extend); z2 = z1 shifted (((unitvector stemslant) scaled upshift) transformed flip); z7 = attach shifted ((stemwidth/2),0); z6 = z7 shifted (((unitvector (-xpart stemslant, ypart stemslant)) scaled upshift) transformed flip); z2 - z3 = ( 0.38 staff_space, 0.05 htip) transformed flip; z6 - z5 = (-0.38 staff_space, 0.05 htip) transformed flip; z4 = attach shifted ((-0.2 stemwidth, upshift + htip) transformed flip); z4'= attach shifted (( 0.2 stemwidth, upshift + htip) transformed flip); % `angle_wing_bot' is the angle at which the arc % from z2 to z3a enters z3a % `angle_wing_top' is the angle at which the arc % from z3b to z4 leaves z3b % `angle_tip' is the angle at which the arc % from z4 to z4' leaves z4 angle_wing_bot = 30; angle_wing_top = 55; angle_tip = 68; z3a = z3 shifted ((((dir angle_wing_bot) rotated -90) scaled wwing) transformed flip); z3b = z3 shifted ((((dir angle_wing_top) rotated 90) scaled wwing) transformed flip); z5a = z5 shifted ((((dir (180 - angle_wing_bot)) rotated 90) scaled wwing) transformed flip); z5b = z5 shifted ((((dir (180 - angle_wing_top)) rotated -90) scaled wwing) transformed flip); % Draw the arrow pickup pencircle scaled 1; fill z1 -- z2{stemslant transformed flip} .. {(-dir angle_wing_bot) transformed flip}z3a .. z3b{(dir angle_wing_top) transformed flip} .. z4{(dir angle_tip) transformed flip} .. z4'{(dir (-angle_tip)) transformed flip} .. {(dir (-angle_wing_top)) transformed flip}z5b .. z5a{(-dir (-angle_wing_bot)) transformed flip} .. z6{((-stemslant) reflectedabout (origin, up)) transformed flip} -- z7 -- cycle; labels (range 0 thru 7, 4', 3a, 3b, 5a, 5b); endgroup; enddef; save remember_pic; picture remember_pic; save sharp_beamheight; sharp_beamheight# := 0.3 staff_space# + stafflinethickness#; % % The beams of most sharps have horizontal endings (as if drawn with % a square pen). [Wanske] does not mention this, so we'll just ignore % this fact. % def draw_meta_sharp (expr width, offset) = save beamwidth, beamslope; save ne, nw_dist; pair ne, nw_dist; beamwidth := width; define_whole_vertical_blacker_pixels (sharp_beamheight); clearxy; beamslope = sharp_beamheight / beamwidth; pickup pencircle scaled 2 blot_diameter; rt x2 - lft x1 = beamwidth; z2 = z1 + whatever * (beamwidth, sharp_beamheight); .5 [z1, z3] = (.5 w, offset); x3 = x2; top y2 - bot y3 = sharp_beamheight; x4 = x1; top y1 - bot y4 = sharp_beamheight; ne = unitvector (z2 - z1); nw_dist = (ne rotated 90) * blot_diameter; fill lft z1{up} ... (z1 + nw_dist){ne} -- (z2 + nw_dist){ne} ... rt z2{down} -- rt z3{down} ... (z3 - nw_dist){-ne} -- (z4 - nw_dist){-ne} ... lft z4{up} -- cycle; labels (1, 2, 3, 4); enddef; def draw_sharp(expr arrowup, arrowdown) = save stem, stemx, stemwidth; save outer_space, interbeam; save stemlength, extendleft, extendright, height, depth; stemwidth# := stafflinethickness# + .05 staff_space#; define_whole_blacker_pixels (stemwidth); interbeam := 1.05 staff_space_rounded; stemlength# := 1.5 staff_space#; define_pixels (stemlength); height# = stemlength#; depth# = stemlength#; extendright# = 0; extendleft# = 0; if arrowup: height# := height# + 1.2 staff_space#; extendright# := extendright# + 1.5 stafflinethickness#; fi; if arrowdown: depth# := depth# + 1.2 staff_space#; extendleft# := extendleft# + 1.5 stafflinethickness#; fi; define_pixels (extendleft, extendright); set_char_box (extendleft#, 1.1 staff_space#, depth#, height#); stem := 7 / 16 * w; stemx := hround stem; outer_space := hround ((w - stemx - stemwidth) / 2); w := 2 outer_space + stemx + stemwidth; d := d - feta_space_shift; draw_meta_sharp (w, -.5 interbeam); draw_meta_sharp (w, -.5 interbeam + vround interbeam); % expand the charbox so that it encloses the whole arrow; % this must not happen earlier because some commands above % still rely on the old width w := w + extendright; pickup pencircle scaled stemwidth; lft x5 = lft x6 = outer_space; lft x7 = lft x8 = outer_space + stemx; bot y5 = -stemlength; top y6 = vround (1.5 staff_space - stem * beamslope); bot y7 = -top y6 + feta_space_shift; top y8 = stemlength; labels (5, 6, 7, 8); draw_gridline (z5, z6, stemwidth); draw_gridline (z7, z8, stemwidth); if arrowup: draw_arrow (z8, stemwidth, up, stafflinethickness / 2 + stemwidth / 2, false); fi; if arrowdown: draw_arrow (z5, stemwidth, up, stafflinethickness / 2 + stemwidth / 2, true); fi; remember_pic := currentpicture; draw_staff (-2, 2, 0); enddef; fet_beginchar ("Sharp", "sharp"); draw_sharp (false, false); fet_endchar; draw_shifted_too; fet_beginchar ("Arrowed Sharp (arrow up)", "sharp.arrowup"); draw_sharp (true, false); fet_endchar; draw_shifted_too; fet_beginchar ("Arrowed Sharp (arrow down)", "sharp.arrowdown"); draw_sharp (false, true); fet_endchar; draw_shifted_too; fet_beginchar ("Arrowed Sharp (arrows up and down)", "sharp.arrowboth"); draw_sharp (true, true); fet_endchar; draw_shifted_too; fet_beginchar ("1/2 Sharp", "sharp.slashslash.stem"); save stem, stemwidth; save outer_space, interbeam; stemwidth# := stafflinethickness# + .05 staff_space#; define_whole_blacker_pixels (stemwidth); interbeam := 1.05 staff_space_rounded; set_char_box (0, 0.7 staff_space#, 1.5 staff_space#, 1.5 staff_space#); stem := 7 / 16 * w; outer_space := hround ((w - stemwidth) / 2); w := 2 outer_space + stemwidth; d := d - feta_space_shift; draw_meta_sharp (w, -.5 interbeam); draw_meta_sharp (w, -.5 interbeam + vround interbeam); pickup pencircle scaled stemwidth; lft x5 = lft x6 = outer_space; top y6 = vround (1.5 staff_space - .5 stem); bot y5 = -top y6 + feta_space_shift; labels (5, 6); draw_gridline (z5, z6, stemwidth); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; fet_beginchar ("Sharp (3 beams)", "sharp.slashslashslash.stemstem"); save stem, stemx, stemwidth; save outer_space, interbeam; save sharp_beamheight; sharp_beamheight# := 0.22 staff_space# + stafflinethickness#; stemwidth# := stafflinethickness# + .05 staff_space#; define_whole_blacker_pixels (stemwidth); interbeam := 1.2 staff_space_rounded; set_char_box (0, 1.1 staff_space#, 1.5 staff_space#, 1.5 staff_space#); stem := 7 / 16 * w; stemx := hround stem; outer_space := hround ((w - stemx - stemwidth) / 2); w := 2 outer_space + stemx + stemwidth; d := d - feta_space_shift; draw_meta_sharp (.88 w, -.5 interbeam); draw_meta_sharp (.88 w, -.5 interbeam + vround interbeam); sharp_beamheight# := 1/.88 sharp_beamheight#; draw_meta_sharp (w, 0); pickup pencircle scaled stemwidth; lft x5 = lft x6 = outer_space; lft x7 = lft x8 = outer_space + stemx; bot y5 = -d; top y6 = vround (1.5 staff_space - stem * beamslope); bot y7 = -top y6 + feta_space_shift; top y8 = h; labels (5, 6, 7, 8); draw_gridline (z5, z6, stemwidth); draw_gridline (z7, z8, stemwidth); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; fet_beginchar ("1/2 Sharp (3 beams)", "sharp.slashslashslash.stem"); save stem, stemx, stemwidth; save outer_space, interbeam; save sharp_beamheight; sharp_beamheight# := 0.22 staff_space# + stafflinethickness#; stemwidth# := stafflinethickness# + .05 staff_space#; define_whole_blacker_pixels (stemwidth); interbeam := 1.2 staff_space_rounded; set_char_box (0, 0.95 staff_space#, 1.3 staff_space#, 1.3 staff_space#); stem := 7 / 16 * w; outer_space := hround ((w - stemwidth) / 2); w := 2 outer_space + stemwidth; d := d - feta_space_shift; draw_meta_sharp (.8 w, -.5 interbeam); draw_meta_sharp (.8 w, -.5 interbeam + vround interbeam); sharp_beamheight# := 1/.8 sharp_beamheight#; draw_meta_sharp (w, 0); pickup pencircle scaled stemwidth; lft x5 = lft x6 = outer_space; top y6 = vround (1.5 staff_space - .5 stem); bot y5 = -top y6 + feta_space_shift; labels (5, 6); draw_gridline (z5, z6, stemwidth); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; fet_beginchar ("3/4 Sharp", "sharp.slashslash.stemstemstem"); save stem, stemx, stemwidth; save outer_space, interbeam; stemwidth# := stafflinethickness# + .05 staff_space#; define_whole_blacker_pixels (stemwidth); interbeam := 1.05 staff_space_rounded; set_char_box (0, 1.6 staff_space#, 1.5 staff_space#, 1.5 staff_space#); stem := 9 / 32 * w; stemx := hround stem; outer_space := hround ((w - 2 stemx - stemwidth) / 2); w := 2 outer_space + 2 stemx + stemwidth; d := d - feta_space_shift; draw_meta_sharp (w, -.5 interbeam); draw_meta_sharp (w, -.5 interbeam + vround interbeam); pickup pencircle scaled stemwidth; lft x5 = lft x6 = outer_space; lft x7 = lft x8 = outer_space + stemx; lft x9 = lft x10 = outer_space + 2 stemx; bot y5 = -d; top y6 = vround (1.5 staff_space - 2 stem * beamslope); bot y9 = -top y6 + feta_space_shift; top y10 = h; y7 = .5 [y5, y9]; y8 = .5 [y6, y10]; labels (5, 6, 7, 8, 9, 10); draw_gridline (z5, z6, stemwidth); draw_gridline (z7, z8, stemwidth); draw_gridline (z9, z10, stemwidth); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; % % The stems of the natural are brushed (at least, in Barenreiter SCS) % def draw_natural (expr arrowup, arrowdown) = save stemwidth, top_stem_thick; save ne, pat_top, pat_bottom; save depth, height, extendleft, extendright, stemlength; save brush_scale_up, brush_scale_down; pair ne; path pat_top, pat_bottom; top_stem_thick# = stafflinethickness# + .10 staff_space#; stemwidth# = 0.09 staff_space# + .5 stafflinethickness#; define_whole_blacker_pixels (top_stem_thick, stemwidth); stemlength# = 1.5 staff_space#; define_pixels (stemlength); height# = stemlength#; depth# = stemlength#; extendleft# = 0; extendright# = 0; if arrowup: extendleft# := 3 stafflinethickness#; height# := height# + 1.2 staff_space#; fi; if arrowdown: extendright# := 3.15 stafflinethickness#; depth# := depth# + 1.2 staff_space#; fi; define_pixels (extendright); set_char_box (extendleft#, 2/3 staff_space#, depth#, height#); d := d - feta_space_shift; pickup pencircle scaled stemwidth; brush_scale_up := 1.0; brush_scale_down := 1.0; % to look nice, arrowed stems must be less brushed if arrowup: brush_scale_up := 0.85; fi; if arrowdown: brush_scale_down := 0.85; fi; penpos1 (top_stem_thick, 0); penpos3 (top_stem_thick, 0); penpos2 (stemwidth, 0); penpos4 (stemwidth, 0); % z1' and z3' are needed for the arrowed accidentals penpos1' (top_stem_thick * brush_scale_up, 0); penpos3' (top_stem_thick * brush_scale_down, 0); x2r = w; x4l = 0; x3 = x3' = x2; x1 = x1' = x4; y1 = y1' = stemlength; y3 = y3' = -stemlength; top y2 = vround (staff_space - 3/2 stafflinethickness); y4 = -y2 + feta_space_shift; pat_bottom := z4r{z4r - z1r} .. bot z4 .. z4l{z1l - z4l}; fill simple_serif (z1'l, z1'r, -30) -- pat_bottom -- cycle; pat_top := z2r{z2r - z3r} .. top z2 .. z2l{z3l - z2l}; fill simple_serif (z3'l, z3'r, 30) -- pat_top -- cycle; ne = (x2 - x4, stafflinethickness); z11' = z3l + whatever * (z2l - z3l); y11' = vround (.5 (staff_space - stafflinethickness)); z11 = z11' + whatever * ne; x11 = x12; z12 = directionpoint -ne of pat_top; z13 = z12 + whatever * ne; x13 = x1; z14 = z11 + whatever * ne; x14 = x1; z21' = z4r + whatever * (z1r - z4r); y21' = -y11' + feta_space_shift; z21 = z21' + whatever * ne; x21 = x22; z22 = directionpoint -ne of pat_bottom; z23 = z22 + whatever * ne; x23 = x3; z24 = z21 + whatever * ne; x24 = x3; fill z11 -- z12 -- z13 -- z14 -- cycle; fill z21 -- z22 -- z23 -- z24 -- cycle; penlabels (1, 1', 2, 3, 3', 4); labels (11, 11', 12, 13, 14, 21, 21', 22, 23, 24); if arrowup: draw_arrow (z1, top_stem_thick * brush_scale_up, z1'l - z4l, stafflinethickness / 2, false); fi; if arrowdown: draw_arrow (z3, top_stem_thick * brush_scale_down, z2r - z3'r, stafflinethickness / 2, true); w := w + extendright; fi; remember_pic := currentpicture; draw_staff (-2, 2, 0); enddef; fet_beginchar ("Natural", "natural"); draw_natural (false, false); fet_endchar; draw_shifted_too; fet_beginchar ("Arrowed Natural (arrow up)", "natural.arrowup"); draw_natural (true, false); fet_endchar; draw_shifted_too; fet_beginchar ("Arrowed Natural (arrow down)", "natural.arrowdown"); draw_natural (false, true); fet_endchar; draw_shifted_too; fet_beginchar ("Arrowed Natural (arrows up and down)", "natural.arrowboth"); draw_natural (true, true); fet_endchar; draw_shifted_too; % % Dedicated to my mom. (3/10/97) % % Mamma, ik hou van je; kom je alsjeblieft terug? % -- HW % % % TODO: remove crook_fatness % TODO: document, simplify! % def draw_meta_flat (expr xcenter, w, crook_fatness, arrowup, arrowdown) = save crook_thinness; save bottom_overshoot, bot_crook_dir; save top_stem_thick, top_stem_thick_orig; save bottom_stem_thick, hair, smaller_hole; save top_crook_thinness; save zwiep; save center; pair center, bot_crook_dir; save clearing, clearing_orig; clearxy; % the stem shouldn't reach the top staff line. %% TODO: should take from height. % % TODO: parameterize this % if w >= 0.75 staff_space: smaller_hole = 0.35 stafflinethickness; else: smaller_hole = 0; fi; crook_thinness = .7 stafflinethickness + .06 staff_space; top_crook_thinness = 1 stafflinethickness + .065 staff_space; clearing = 1.7 stafflinethickness; clearing_orig = clearing; if arrowup: clearing := 0.5 staff_space; fi; bottom_overshoot = stafflinethickness; bottom_stem_thick# = 0.06 staff_space# + 0.6 stafflinethickness#; top_stem_thick# = 0.1 staff_space# + 1.2 stafflinethickness#; top_stem_thick_orig# = top_stem_thick#; if arrowup: % to look nice, arrowed stems should be less brushed top_stem_thick# := top_stem_thick# * 0.8; fi; define_whole_blacker_pixels (bottom_stem_thick, top_stem_thick, top_stem_thick_orig); if odd (top_stem_thick - bottom_stem_thick): top_stem_thick := top_stem_thick - 1; fi; if odd (top_stem_thick_orig - bottom_stem_thick): top_stem_thick_orig := top_stem_thick_orig - 1; fi; center = (xcenter, 0); x1l = hround (xcenter - .5 top_stem_thick); y1 = vround (2 staff_space - clearing); x2l = hround (xcenter - .5 bottom_stem_thick); y2 = -.5 staff_space - .5 stafflinethickness; % z16 and the `*_orig' variables are needed for arrowed accidentals % because their inner part should be unchanged from plain ones but % the points z3l, z3r, and z10 depend on values that are different % for arrowed accidentals x16l = hround (xcenter -.5 top_stem_thick_orig); y16 = vround (2 staff_space - clearing_orig); penpos1 (top_stem_thick, 0); penpos16 (top_stem_thick_orig, 0); penpos2 (bottom_stem_thick, 0); y3l = vfloor ((staff_space - stafflinethickness) / 2); z3l = whatever [z2r, z1r]; z3r = .3 [z2r, (z16r shifted (0, clearing_orig - 1.7 stafflinethickness))] + (smaller_hole, 0); x3r := hceiling x3r; % we insert z3l to get better conversion with mf2pt1 fill simple_serif (z1r, z1l, 30) -- z2l -- z2r -- z3l -- cycle; z10 = whatever [z2r, z16r] + (smaller_hole, 0); y10 = -1/10 staff_space; x10 := hceiling x10; x11 = xcenter + bottom_overshoot / 3; y11 = -vround (.5 (staff_space + stafflinethickness) + bottom_overshoot); x2a = 0.2[x2r, x7]; y2a = 1.5[y2, y11]; penpos4 (whatever, 53); y4l - y4r = top_crook_thinness; y5r = .15 staff_space; x5l = hround (w + xcenter); y4 = staff_space / 2; x4r = .45 [x5r, x3r]; y4l := vround y4l; penpos5 (crook_fatness, -175); bot_crook_dir = unitvector ((x5l, 0) - z11); z8 = z11 + whatever * bot_crook_dir; y8 = -staff_space / 2; z7 = z8 + whatever * bot_crook_dir + crook_thinness * (bot_crook_dir rotated 90); x7 = .1 [x3r, x8]; unfill z3r{z3r - z10} .. z4r{right} .. z5r{down} .. z7{-bot_crook_dir} & z7 .. z10{z3r - z10} -- cycle; if arrowdown: fill z2l{down} .. z2a{up} .. z8{bot_crook_dir} .. z5l{up} .. z4l{left} .. z3l -- cycle; else: fill z2l{down} .. z11{right} .. z8{bot_crook_dir} .. z5l{up} .. z4l{left} .. z3l -- cycle; fi; if arrowup: draw_arrow (z1, top_stem_thick, z1l - z2l, 0.5 stafflinethickness, false); fi; if arrowdown: draw_arrow ((0.5 [x2l, x2a], y2), x2a - x2l, up, staff_space / 2, true); fi; enddef; def draw_arrowed_meta_flat (expr xcenter, width, crook_fatness, arrowup, arrowdown) = save depth, height, extendleft; depth# = 0.6 staff_space#; height# = 1.9 staff_space#; extendleft# := 1.2 stafflinethickness#; if arrowup: extendleft# := 3.45 stafflinethickness#; height# := height# + 0.8 staff_space#; fi; if arrowdown: extendleft# := 3.45 stafflinethickness#; depth# := depth# + 1.6 staff_space#; fi; set_char_box (extendleft#, width, depth#, height#); draw_meta_flat(xcenter, w, crook_fatness, arrowup, arrowdown); enddef; % % unfortunately, 600dpi is not enough to show the brush of the stem. % fet_beginchar ("Flat", "flat"); draw_arrowed_meta_flat (0, 0.8 staff_space#, 0.31 staff_space, false, false); penlabels (range 0 thru 11); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; fet_beginchar ("Arrowed Flat (arrow up)", "flat.arrowup"); draw_arrowed_meta_flat (0, 0.8 staff_space#, 0.31 staff_space, true, false); penlabels (range 0 thru 23); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; fet_beginchar ("Arrowed Flat (arrow down)", "flat.arrowdown"); draw_arrowed_meta_flat (0, 0.8 staff_space#, 0.31 staff_space, false, true); penlabels (range 0 thru 23); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; fet_beginchar ("Arrowed Flat (arrow up and down)", "flat.arrowboth"); draw_arrowed_meta_flat (0, 0.8 staff_space#, 0.31 staff_space, true, true); penlabels (range 0 thru 23); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; fet_beginchar ("Flat (slashed)", "flat.slash"); set_char_box (.4 staff_space#, .8 staff_space#, 0.6 staff_space#, 1.9 staff_space#); draw_meta_flat (0, w, 0.31 staff_space, false, false); clearxy; save slope, slash_width; slope = 0.5; slash_width = w; z11 = (0, h / 2); z12 = z11 - (slash_width, slash_width * slope) / 2; z13 = z11 + (slash_width, slash_width * slope) / 2; penpos12 (1.5 stafflinethickness, angle (z13 - z12) - 90); penpos13 (1.5 stafflinethickness, angle (z13 - z12) - 90); z14 = z12 - .75 stafflinethickness * unitvector (z13 - z12); z15 = z13 + .75 stafflinethickness * unitvector (z13 - z12); fill z13r .. z15 .. z13l -- z12l .. z14 .. z12r -- z13r .. cycle; penlabels (12, 13); labels (14, 15); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("Flat (slashed twice)", "flat.slashslash"); set_char_box (.4 staff_space#, .8 staff_space#, 0.6 staff_space#, 1.9 staff_space#); draw_meta_flat (0, w, 0.31 staff_space, false, false); clearxy; save slope, slash_width; slope = 0.5; slash_width = w; z11 = (0, 5/12 h); z12 = z11 - (slash_width, slash_width * slope) / 2; z13 = z11 + (slash_width, slash_width * slope) / 2; penpos12 (1.5 stafflinethickness, angle (z13 - z12) - 90); penpos13 (1.5 stafflinethickness, angle (z13 - z12) - 90); z14 = z12 - .75 stafflinethickness * unitvector (z13 - z12); z15 = z13 + .75 stafflinethickness * unitvector (z13 - z12); fill z13r .. z15 .. z13l -- z12l .. z14 .. z12r -- z13r .. cycle; penlabels (12, 13); labels (14, 15); z21 = (0, 2/3 h); z22 = z21 - (slash_width, slash_width * slope) / 2; z23 = z21 + (slash_width, slash_width * slope) / 2; penpos22 (1.5 stafflinethickness, angle (z23 - z22) - 90); penpos23 (1.5 stafflinethickness, angle (z23 - z22) - 90); z24 = z22 - .75 stafflinethickness * unitvector (z23 - z22); z25 = z23 + .75 stafflinethickness * unitvector (z23 - z22); fill z23r .. z25 .. z23l -- z22l .. z24 .. z22r -- z23r .. cycle; penlabels (22, 23); labels (24, 25); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; fet_beginchar ("Flatflat (mirrored)", "mirroredflat.flat"); set_char_box (0, 1.6 staff_space#, 0.6 staff_space#, 1.9 staff_space#); % This is a modified version of `draw_meta_flat'. save crook_thinness, crook_fatness; save bottom_overshoot, bot_crook_dir; save top_stem_thick, bottom_stem_thick, hair, smaller_hole; save top_crook_thinness; save zwiep; save center; pair center, bot_crook_dir; save clearing, wid; save pat; path pat; clearxy; wid = w / 2; % the stem shouldn't reach the top staff line. %% TODO: should take from height. % % TODO: parameterize this % if wid >= 0.75 staff_space: smaller_hole = 0.35 stafflinethickness; else: smaller_hole = 0; fi; clearing = 1.7 stafflinethickness; crook_thinness = .7 stafflinethickness + .06 staff_space; crook_fatness = 0.31 staff_space; top_crook_thinness = 1 stafflinethickness + .065 staff_space; bottom_overshoot = stafflinethickness; bottom_stem_thick# = 0.06 staff_space# + 0.6 stafflinethickness#; top_stem_thick# = 0.1 staff_space# + 1.2 stafflinethickness#; define_whole_blacker_pixels (bottom_stem_thick, top_stem_thick); if odd (top_stem_thick - bottom_stem_thick): top_stem_thick := top_stem_thick - 1; fi; center = (0, 0); x1l = hround (-.5 top_stem_thick); y1 = vround (2 staff_space - clearing); x2l = hround (-.5 bottom_stem_thick); y2 = -.5 staff_space - .5 stafflinethickness; penpos1 (top_stem_thick, 0); penpos2 (bottom_stem_thick, 0); y3l = vfloor ((staff_space - stafflinethickness) / 2); z3l = whatever [z2r, z1r]; z3r = .3 [z2r, z1r] + (smaller_hole, 0); x3r := hceiling x3r; z10 = whatever [z2r, z1r] + (smaller_hole, 0); y10 = -1/10 staff_space; x10 := hceiling x10; x11 = bottom_overshoot / 3; y11 = -vround (.5 (staff_space + stafflinethickness) + bottom_overshoot); penpos4 (whatever, 53); y4l - y4r = top_crook_thinness; y5r = .15 staff_space; x5l = hround (wid); y4 = staff_space / 2; x4r = .45 [x5r, x3r]; y4l := vround y4l; penpos5 (crook_fatness, -175); bot_crook_dir = unitvector ((x5l, 0) - z11); z8 = z11 + whatever * bot_crook_dir; y8 = -staff_space / 2; z7 = z8 + whatever * bot_crook_dir + crook_thinness * (bot_crook_dir rotated 90); x7 = .1 [x3r, x8]; pat := z3r{z3r - z10} .. z4r{right} .. z5r{down} .. z7{-bot_crook_dir} & z7 .. z10{z3r - z10} -- cycle; unfill pat; unfill pat xscaled -1; pat := z11{right} .. z8{bot_crook_dir} .. z5l{up} .. z4l{left} .. z3l; fill pat -- simple_serif (z1r, z1l, 30) -- reverse pat xscaled -1 shifted (-feta_eps, 0) -- cycle; currentpicture := currentpicture shifted (w/2, 0); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; fet_beginchar ("Semi flat", "mirroredflat"); set_char_box (1.2 stafflinethickness#, .8 staff_space#, 0.6 staff_space#, 1.9 staff_space#); draw_meta_flat (0, w, 0.31 staff_space, false, false); currentpicture := currentpicture xscaled -1 shifted (w - b, 0); fet_endchar; fet_beginchar ("Semi flat", "mirroredflat.backslash"); set_char_box (.4 staff_space#, .8 staff_space#, 0.6 staff_space#, 1.9 staff_space#); draw_meta_flat (0, w, 0.31 staff_space, false, false); clearxy; save slope, slash_width; slope = 0.5; slash_width = w; z11 = (0, h / 2); z12 = z11 - (slash_width, slash_width * slope) / 2; z13 = z11 + (slash_width, slash_width * slope) / 2; penpos12 (1.5 stafflinethickness, angle (z13 - z12) - 90); penpos13 (1.5 stafflinethickness, angle (z13 - z12) - 90); z14 = z12 - .75 stafflinethickness * unitvector (z13 - z12); z15 = z13 + .75 stafflinethickness * unitvector (z13 - z12); fill z13r .. z15 .. z13l -- z12l .. z14 .. z12r -- z13r .. cycle; currentpicture := currentpicture xscaled -1 shifted (w - b, 0); labels (1, 2, 3); fet_endchar; fet_beginchar ("Double Flat", "flatflat"); save left_wid, overlap, right_wid; left_wid = .7; right_wid = .8; overlap = .05; set_char_box (1.2 stafflinethickness#, (left_wid + right_wid - overlap) * staff_space#, .6 staff_space#, 1.9 staff_space#); draw_meta_flat (0, left_wid * staff_space, 1/3 staff_space, false, false); draw_meta_flat (hround ((left_wid - overlap) * staff_space), right_wid * staff_space, 1/3 staff_space, false, false); fet_endchar; fet_beginchar ("3/4 Flat", "flatflat.slash"); save left_wid, overlap, right_wid; left_wid = .7; right_wid = .8; overlap = .05; set_char_box (1.2 stafflinethickness#, (left_wid + right_wid - overlap) * staff_space#, .6 staff_space#, 1.9 staff_space#); draw_meta_flat (0, left_wid * staff_space, 1/3 staff_space, false, false); draw_meta_flat (hround ((left_wid - overlap) * staff_space), right_wid * staff_space, 1/3 staff_space, false, false); %% maybe we should clip part of the stems? %% or make the 1st flat smaller? %% or reverse it? pickup pencircle scaled 2 stafflinethickness; z12 = round (-.25 w - b, .55 staff_space) + feta_offset; z13 = round (.75 w, 1.45 staff_space) + feta_offset; penpos12 (2 stafflinethickness, angle (z13 - z12) - 90); penpos13 (2 stafflinethickness, angle (z13 - z12) - 90); z14 = z12 - stafflinethickness * unitvector (z13 - z12); z15 = z13 + stafflinethickness * unitvector (z13 - z12); fill z13r .. z15 .. z13l -- z12l .. z14 .. z12r -- z13r .. cycle; penlabels (12, 13); labels (14, 15); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; fet_beginchar ("Double Sharp", "doublesharp"); save klaverblad, klaversteel; save pat; path pat; klaversteel = 1/15 staff_space; klaverblad = .4 staff_space - .5 stafflinethickness; set_char_box (0, staff_space#, .5 staff_space#, .5 staff_space#); z1 = (klaversteel, 0); z2 = (w / 2 - klaverblad / 10, h - klaverblad); z3 = (w / 2, h); z4 = z2 reflectedabout ((0, 0), (1, 1)); z5 = z1 reflectedabout ((0, 0), (1, 1)); labels (1, 2, 3, 4, 5); pickup pencircle scaled blot_diameter; x2 := hfloor (rt x2) - blot_diameter / 2; x3 := hfloor (rt x3) - blot_diameter / 2; y3 := vfloor (top y3) - blot_diameter / 2; y4 := vfloor (top y4) - blot_diameter / 2; pat = (rt z1){dir45} .. {right}(bot z2) .. rt z2 -- rt z3{z3 - z2} .. top z3{z4 - z3} -- top z4{z4 - z3} .. (lft z4){down} .. {dir 225}(top z5); pat := pat -- reverse pat xscaled -1 shifted (-feta_eps, 0); % assure symmetry -- it's more important to center the glyph on the % staff line than centering it between staff lines, so we use % feta_shift, not feta_space_shift. h := h + feta_shift; fill pat shifted (0, feta_shift) -- reverse pat yscaled -1 shifted (0, -feta_eps) -- cycle; % ugh currentpicture := currentpicture shifted (hround (w / 2), 0); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; def draw_paren = save leftindent; leftindent := .2 staff_space; set_char_box (0, .5 staff_space# + stafflinethickness#, staff_space#, staff_space#); d := d - feta_shift; z1 = (leftindent, h); z2 = (w - stafflinethickness, .5 (h - d)); z3 = (leftindent, -d); penpos1 (stafflinethickness, 35); penpos2 (.1 staff_space + stafflinethickness, 0); penpos3 (stafflinethickness, -35); fill z2l{down} .. simple_serif (z3l, z3r, 90) .. z2r{up} .. simple_serif (z1r, z1l, 90) .. z2l{down} -- cycle; enddef; fet_beginchar ("Right Parenthesis", "rightparen"); draw_paren; penlabels (1, 2, 3); remember_pic := currentpicture; draw_staff (-2, 2, 0); fet_endchar; draw_shifted_too; fet_beginchar ("Left Parenthesis", "leftparen"); draw_paren; currentpicture := currentpicture xscaled -1; set_char_box (charwd, charbp, chardp, charht); fet_endchar; fet_endgroup ("accidentals"); muse-4.0.0/src/share/scoreglyphs/feta-original/feta11.mf000066400000000000000000000002231404105311000230270ustar00rootroot00000000000000% feta11.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 11.22; test := 0; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta13.mf000066400000000000000000000002231404105311000230310ustar00rootroot00000000000000% feta13.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 12.60; test := 0; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta14.mf000066400000000000000000000002241404105311000230330ustar00rootroot00000000000000% feta14.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 14.14; test := 0; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta16.mf000066400000000000000000000002231404105311000230340ustar00rootroot00000000000000% feta16.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 15.87; test := 0; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta18.mf000066400000000000000000000002531404105311000230410ustar00rootroot00000000000000% feta18.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; % todo change file name design_size := 17.82; test := 0; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta20.mf000066400000000000000000000002571404105311000230360ustar00rootroot00000000000000% feta20.mf % part of LilyPond's pretty-but-neat music font design_size := 20; input feta-autometric; % use feta-test for debugging. test := 0; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta23.mf000066400000000000000000000002541404105311000230360ustar00rootroot00000000000000% feta23.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; % todo change file name design_size := 22.45; test := 0; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/feta26.mf000066400000000000000000000002251404105311000230370ustar00rootroot00000000000000% feta26.mf % part of LilyPond's pretty-but-neat music font input feta-autometric; design_size := 25.20; test := 0; input feta-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/mf2pt1.mp000066400000000000000000000345371404105311000231100ustar00rootroot00000000000000%%%% %%%% MF2PT1.MP, by Scott Pakin, scott+mf@pakin.org %%%% %%%% This file is used to dump a special version of MetaPost with: %%%% mpost -progname=mpost -ini mf2pt1 \\dump %%%% %%%% To pretty-print this file, you'll need LaTeX and the mftinc package %%%% (available from CTAN). %%%% %%%% ==================================================================== %%%% %%%% mf2pt1 %%%% %%%% Copyright (C) 2008 Scott Pakin %%%% %%%% %%%% %%%% This program may be distributed and/or modified under the conditions %%%% %%%% of the LaTeX Project Public License, either version 1.3c of this %%%% %%%% license or (at your option) any later version. %%%% %%%% %%%% %%%% The latest version of this license is in: %%%% %%%% %%%% %%%% http://www.latex-project.org/lppl.txt %%%% %%%% %%%% %%%% and version 1.3c or later is part of all distributions of LaTeX %%%% %%%% version 2006/05/20 or later. %%%% %%%% ==================================================================== %%%% input mfplain; %%% addto makepath makepen %%% length clockwise counterclockwise %%% scaled dashed withcolor %% \begin{explaincode} %% Enable a \MF\ file to determine if it's being built with %% \texttt{mf2pt1}. %% \end{explaincode} newinternal ps_output; ps_output := 1; %% \begin{explaincode} %% The following was taken right out of \texttt{mfplain.mp}. The \mfcomment % |def| and the |special|s at the end %% are the sole additions. Normally, MetaPost outputs a tight bounding %% box around the character in its PostScript output. The purpose of the %% first \mfcomment % |special| %% is to pass \texttt{mf2pt1} a bounding box that includes the proper %% surrounding whitespace. The purpose of the second special is to %% provide \texttt{mf2pt1} with a default PostScript font name. %% \end{explaincode} def beginchar(expr c,w_sharp,h_sharp,d_sharp) = begingroup charcode:=if known c: byte c else: 0 fi; charwd:=w_sharp; charht:=h_sharp; chardp:=d_sharp; w:=charwd*pt; h:=charht*pt; d:=chardp*pt; charic:=0; clearxy; clearit; clearpen; scantokens extra_beginchar; def to_bp (expr num) = decimal (ceiling (num*bp_per_pixel)) enddef; special "% MF2PT1: glyph_dimensions 0 " & to_bp (-d) & " " & to_bp(w) & " " & to_bp(h); special "% MF2PT1: font_size " & decimal designsize; special "% MF2PT1: font_slant " & decimal font_slant_; special "% MF2PT1: charwd " & decimal charwd; % Must come after the |font_size| |special| for fvar = "font_identifier", "font_coding_scheme", "font_version", "font_comment", "font_family", "font_weight", "font_unique_id", "font_name": if known scantokens (fvar & "_"): special "% MF2PT1: " & fvar & " " & scantokens (fvar & "_"); fi; endfor; for fvar = "font_underline_position", "font_underline_thickness": if known scantokens (fvar & "_"): special "% MF2PT1: " & fvar & " " & scantokens ("decimal " & fvar & "_"); fi; endfor; special "% MF2PT1: font_fixed_pitch " & (if font_fixed_pitch_: "1" else: "0" fi); enddef; %% \begin{explaincode} %% Enable a character to specify explicitly the PostScript glyph %% name associated with it. %% \end{explaincode} def glyph_name expr name = special "% MF2PT1: glyph_name " & name; enddef; %% \begin{explaincode} %% Store the value of \mfcomment % |font_slant_|, so we can recall it at each |beginchar|. %% \end{explaincode} font_slant_ := 0; def font_slant expr x = font_slant_ := x; fontdimen 1: x enddef; %% \begin{explaincode} %% Redefine \mfcomment % |bpppix_|, the number of ``big'' points per pixel. \mfcomment % This in turn redefines |mm|, |in|, |pt|, and other derived units. %% \end{explaincode} def bpppix expr x = bpppix_ := x; mm := 2.83464 / bpppix_; pt := 0.99626 / bpppix_; dd := 1.06601 / bpppix_; bp := 1 / bpppix_; cm := 28.34645 / bpppix_; pc := 11.95517 / bpppix_; cc := 12.79213 / bpppix_; in := 72 / bpppix_; hppp := pt; vppp := pt; enddef; %% \begin{explaincode} %% Define a bunch of PostScript font parameters to be used by %% \texttt{mf2pt1.pl}. Default values are specified in %% \texttt{mf2pt1.pl}, not here. %% \end{explaincode} forsuffixes fvar = font_version, font_comment, font_family, font_weight, font_name, font_unique_id: scantokens ("string " & str fvar & "_;"); scantokens ("def " & str fvar & " expr x = " & str fvar & "_ := x enddef;"); endfor; forsuffixes fvar = font_underline_position, font_underline_thickness: scantokens ("numeric " & str fvar & "_;"); scantokens ("def " & str fvar & " expr x = " & str fvar & "_ := x enddef;"); endfor; boolean font_fixed_pitch_; font_fixed_pitch_ := false; def font_fixed_pitch expr x = font_fixed_pitch_ := x enddef; %% \begin{explaincode} %% We'd like to be able to use calligraphic pens. Normally, MetaPost's %% output routine does all the work for us of converting these to filled %% PostScript paths. The only exception occurs for paths drawn using a %% pen that was transformed from \mfcomment % |pencircle|. MetaPost outputs these paths as stroked PostScript %% paths. The following code tricks MetaPost into using a filled path %% for \mfcomment % |pencircle| by replacing the primitive |pencircle| pen with a %% non-primitive approximation. Note that we use a 20-gon for our circle %% instead of a diamond, so we get better results from \mfcomment % |draw|. %% \end{explaincode} pen fakepencircle, mfplain_pencircle; mfplain_pencircle := pencircle; fakepencircle := makepen (for deg=0 step 360/20 until 359: (0.5 cosd deg, 0.5 sind deg)-- endfor cycle); save pencircle; pen pencircle; pencircle := fakepencircle; %% \begin{explaincode} %% Return \mfcomment % |true| if a path is cyclic, |false| otherwise. %% \end{explaincode} def is_cyclic expr cpath = (point 0 of cpath = point (length cpath) of cpath) enddef; %% \begin{explaincode} %% Determine the direction of a path which doesn't intersect %% itself. \mfcomment % Returns |true| if the curve is clockwise, |false| if %% counterclockwise. For non-cyclic paths the result is not %% predictable. %% \bigskip %% %% The \mfcomment % |crossproduct|, |makeline|, and |is_clockwise| functions were %% provided by Werner Lemberg. %% \bigskip %% %% The algorithm used is quite simple: %% %% \begin{itemize} %% \item Find a point~$P$ on the path which has a non-zero direction, %% and which is on a not-too-short path element. %% %% \item Construct a ray of ``infinite'' length, starting in the %% vicinity of~$P$ which intersects the path at this point. %% %% \item Use \mfcomment % |intersectiontimes| to find the intersection. If the direction of %% the path at this point is (near) zero, or if we have a grazing %% intersection or even a tangent, get a new ray. %% %% \item Shorten the ray so that it starts right after the %% intersection. Repeat the previous step until no intersection is %% found. Then go back to the last intersection and compare the path's %% direction with the direction of the ray. According to the %% \emph{nonzero winding number} rule we have found a clockwise %% oriented path if it crosses the ray from left to right. %% \end{itemize} %% %% This method completely avoids any problems with the geometry of %% B\'{e}zier curves. If problems arise, a different ray is tried. %% Since it isn't necessary to analyze the whole path it runs quite fast %% in spite of using \mfcomment % |intersectiontimes| which is a slow MetaPost command. %% \end{explaincode} vardef crossproduct (expr u, v) = save u_, v_; pair u_, v_; u_ := unitvector u; v_ := unitvector v; abs (xpart u_ * ypart v_ - ypart u_ * xpart v_) enddef; vardef makeline primary p = save start, bad_n, loop, distance, d, i, n; pair start, d; loop := 0; bad_n := -1; for i := 0 step 1 until length p - 1: distance := length (point i of p - point (i + 1) of p); if distance <> 0: if distance < 1: % In case we don't find something better. bad_n := i; else: n := i; loop := 1; fi; fi; exitif loop = 1; endfor; if loop = 0: if bad_n <> -1: n := bad_n; loop = 1; fi; fi; % Add some randomness to get different lines for each function call. n := n + uniformdeviate 0.8 + 0.1; start := point n of p; if loop = 0: % Construct a line which misses the degenerated path. start + (1, 0) -- start + (1, 1) else: d := direction n of p; % Again, some added randomness. n := uniformdeviate 150 + 15; d := unitvector (d rotated n); % Construct a line which intersects the path at least once. start - eps * d -- infinity * d fi enddef; vardef is_clockwise primary p = save line, cut, cut_new, res, line_dir, tangent_dir; path line; pair cut, cut_new, line_dir, tangent_dir; line := makeline p; line_dir := direction 0 of line; % Find the outermost intersection. cut := (0, 0); forever: cut_new := line intersectiontimes p; exitif cut_new = (-1, -1); % Compute a new line if we have a strange intersection. tangent_dir := direction (ypart cut_new) of p; if abs tangent_dir < eps: % The vector is zero or too small. line := makeline p; line_dir := direction 0 of line; elseif abs (ypart cut_new - floor (ypart cut_new + 0.5)) < eps: % Avoid possible tangent touching in a corner or cusp. line := makeline p; line_dir := direction 0 of line; elseif crossproduct (tangent_dir, line_dir) < 0.2: % Grazing intersection (arcsin 0.2 ~= 11.5 degrees). line := makeline p; line_dir := direction 0 of line; else: % Go ahead. cut := cut_new; line := subpath (xpart cut + eps, infinity) of line; fi; endfor; tangent_dir := direction (ypart cut) of p; if tangent_dir <> (0, 0): res := (angle tangent_dir - angle line_dir + 180) mod 360 - 180; res < 0 else: false fi enddef; %% \begin{explaincode} %% Make a given path run clockwise or counterclockwise. \mfcomment % (|counterclockwise| is defined by \texttt{mfplain} but we override %% it here.) %% \end{explaincode} vardef counterclockwise primary c = (if is_clockwise c: (reverse c) else: c fi) enddef; vardef clockwise primary c = (if is_clockwise c: c else: (reverse c) fi) enddef; %% \begin{explaincode} %% Redefine \mfcomment % |fill| and |unfill| to ensure that filled paths run %% counterclockwise and unfilled paths run clockwise, as is required %% by PostScript Type~1 fonts. %% \end{explaincode} def fill expr c = addto currentpicture contour counterclockwise c t_ pc_ enddef; def unfill expr c = addto currentpicture contour clockwise c t_ pc_ withcolor background enddef; %% \begin{explaincode} %% Convert \mfcomment % |filldraw| and |unfilldraw| to |fill| and |unfill|. %% \end{explaincode} let mfplain_filldraw := filldraw; def filldraw expr c = begingroup message "! Warning: Replacing filldraw with fill."; fill c endgroup enddef; let mfplain_unfilldraw := unfilldraw; def unfilldraw expr c = begingroup message "! Warning: Replacing unfilldraw with unfill."; unfill c endgroup enddef; %% \begin{explaincode} %% Return \mfcomment % |true| if |currentpen| looks like a |pencircle|. %% \end{explaincode} def using_pencircle = begingroup path qpath, circlepath; qpath = makepath currentpen; numeric circlediv; circlepath = makepath pencircle; circlediv = xpart (lrcorner circlepath); (length qpath = length circlepath) and (pen_rt <> 0) and (pen_top <> 0) for pp = 0 upto (length qpath)-1: and ((xpart (point pp of qpath) / pen_rt, ypart (point pp of qpath) / pen_top) = point pp of circlepath / circlediv) endfor endgroup enddef; %% \begin{explaincode} %% If the pen looks like a circular pen, draw a nice circle. Otherwise, %% draw the pen as is. %% \end{explaincode} def drawdot expr z = if using_pencircle: begingroup path cpath; numeric clength; cpath = makepath currentpen; clength = length cpath; fill ((point 0 of cpath) ..(point clength/4 of cpath) ..(point clength/2 of cpath) ..(point 3*clength/4 of cpath) ..cycle) shifted z t_ endgroup else: addto currentpicture contour makepath currentpen shifted z t_ pc_ fi enddef; %% \begin{explaincode} %% Do the same as the above, but unfill the current pen. %% \end{explaincode} def undrawdot expr z = if using_pencircle: begingroup path cpath; numeric clength; cpath = makepath currentpen; clength = length cpath; unfill ((point 0 of cpath) ..(point clength/4 of cpath) ..(point clength/2 of cpath) ..(point 3*clength/4 of cpath) ..cycle) shifted z t_ endgroup else: unfill makepath currentpen shifted z t_ fi enddef; %% \begin{explaincode} %% MetaPost renders \mfcomment % |draw| with a filled curve. %% Hence, we need to ensure the orientation is correct (i.e., %% counterclockwise). Unfortunately, we have no way to check for %% overlap, and it's fairly common for MetaPost to output %% self-overlapping curve outlines, even if the curve itself has no %% overlap. %% \end{explaincode} def draw expr p = addto currentpicture if picture p: also p elseif is_cyclic p: doublepath counterclockwise p t_ withpen currentpen else: if is_clockwise (p--cycle): doublepath (reverse p) t_ withpen currentpen else: doublepath p t_ withpen currentpen fi fi pc_ enddef; def undraw expr p = addto currentpicture if picture p: also p elseif is_cyclic p: doublepath clockwise p t_ withpen currentpen else: if is_clockwise (p--cycle): doublepath p t_ withpen currentpen else: doublepath (reverse p) t_ withpen currentpen fi fi pc_ withcolor background enddef; muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-accidentals.mf000066400000000000000000000220031404105311000260240ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-accidentals.mf -- implement ancient accidentals % % source file of LilyPond's pretty-but-neat music font % % (c) 2001--2009 Juergen Reuter % fet_begingroup ("accidentals"); %%%%%%%% % % % % EDITIO MEDICAEA % % % fet_beginchar ("Ed. Med. Flat" , "medicaeaM1"); set_char_box (0.1 staff_space#, 0.6 staff_space#, 0.6 staff_space#, 1.0 staff_space#); save ellipse, pat, outline, T; path ellipse, pat, outline; transform T; T := identity xscaled 0.50 linethickness yscaled 0.22 staff_space; pickup pencircle transformed T; ellipse := fullcircle transformed T; x1 = x2 = 0; top y1 = h; bot y2 = -d; fill get_subpath (ellipse, up, down, z1) -- get_subpath (ellipse, down, up, z2) -- cycle; T := identity xscaled 0.50 linethickness yscaled 0.22 staff_space rotated -63; pickup pencircle transformed T; ellipse := fullcircle transformed T; z3 = (0.10 staff_space, -0.50 staff_space); z4 = (0.40 staff_space, +0.40 staff_space); z5 = (0.10 staff_space, +0.40 staff_space); pat := z3{(1, 2)} .. z4 .. z5{(-1, -1)}; % the original envelope curve created with `draw' contains % cusps which we avoid outline := get_subpath (ellipse, -direction 0 of pat, direction 0 of pat, z3) .. get_subpoint (ellipse, direction 1 of pat, z4) .. get_subpath (ellipse, direction 2 of pat, -direction 1.8 of pat, z5) .. get_subpoint (ellipse, -direction 1 of pat, z4) {-direction 1 of pat} .. cycle; save shift; pair shift; % make the outline touch the bounding box shift = find_tangent_shift (((w, -d) -- (w, h)), outline, (b, 0), (-b, 0)); outline := outline shifted shift; fill outline; labels (1, 2, 3, 4, 5); fet_endchar; %%%%%%%% % % % % EDITIO VATICANA % % % fet_beginchar ("Ed. Vat. Flat" , "vaticanaM1"); z1 = (0.00 staff_space, +0.80 staff_space); z2 = (0.00 staff_space, -0.08 staff_space); z3 = (0.25 staff_space, -0.23 staff_space); z4 = (0.50 staff_space, -0.24 staff_space); z5 = (0.50 staff_space, +0.03 staff_space); z6 = (0.25 staff_space, +0.20 staff_space); z7 = (0.15 staff_space, +0.26 staff_space); save pat, ellipse, T; path pat, ellipse; transform T; T := identity xscaled 0.50 linethickness yscaled 0.22 staff_space; pickup pencircle transformed T; ellipse := fullcircle transformed T; pat := z1 -- z2{down} ... z3 ... {up}z4 -- z5{up} .. z6 .. z7; % the original envelope curve created with `draw' contains % cusps which we avoid fill get_subpath (ellipse, up, down, z1) -- get_subpath (ellipse, down, direction 1.1 of pat, z2) ... bot z3 ... get_subpath (ellipse, direction 2.9 of pat, up, z4) -- get_subpath (ellipse, up, direction 4.1 of pat, z5) .. top z6 .. get_subpath (ellipse, direction 6 of pat, -direction 6 of pat, z7) .. bot z6 .. {down}bot lft z5 -- top lft z4{down} ... top z3 ... top rt z2{up} -- cycle; set_char_box (0.00 staff_space# + 0.25 linethickness#, 0.50 staff_space# + 0.25 linethickness#, 0.23 staff_space# + 0.11 staff_space#, 0.80 staff_space# + 0.11 staff_space#); labels (1, 2, 3, 4, 5, 6, 7); fet_endchar; fet_beginchar ("Ed. Vat. Natural" , "vaticana0"); save ellipse, T; path ellipse; transform T; T := identity xscaled 0.80 linethickness yscaled 0.22 staff_space; pickup pencircle transformed T; ellipse := fullcircle transformed T; z1 = (0.00 staff_space, +0.65 staff_space); z2 = (0.00 staff_space, -0.35 staff_space); fill get_subpath (ellipse, up, down, z1) -- get_subpath (ellipse, down, up, z2) -- cycle; pickup penrazor scaled 0.22 staff_space rotated 90; z3 = (0.00 staff_space, -0.30 staff_space); z4 = (0.40 staff_space, -0.08 staff_space); draw z3 -- z4; addto currentpicture also currentpicture xscaled -1 yscaled -1 shifted (0.40 staff_space, 0.0 staff_space); set_char_box (0.00 staff_space# + 0.40 linethickness#, 0.40 staff_space# + 0.40 linethickness#, 0.65 staff_space# + 0.11 staff_space#, 0.65 staff_space# + 0.11 staff_space#); labels (1, 2, 3, 4); fet_endchar; %%%%%%%% % % % % MENSURAL NOTATION % % % fet_beginchar ("Mensural Sharp" , "mensural1"); save stemthick; define_pixels (stemthick); stemthick# = linethickness#; save circle, pat, T; path circle, pat; transform T; T := identity scaled 0.8 stemthick; pickup pencircle transformed T; circle := fullcircle transformed T; z1 = 0.4 staff_space * (0.8, 1); z1 = -z2; pat := get_subpath (circle, z1 - z2, z2 - z1, z1) -- get_subpath (circle, z2 - z1, z1 - z2, z2) -- cycle; fill pat; fill pat xscaled -1; fill pat shifted (0.20 staff_space, 0); fill pat xscaled -1 shifted (0.20 staff_space, 0); set_char_box (0.8 * 0.4 staff_space# + 0.4 stemthick#, (0.8 * 0.4 + 0.2) * staff_space# + 0.4 stemthick#, 0.4 staff_space# + 0.4 stemthick#, 0.4 staff_space# + 0.4 stemthick#); labels (1, 2); fet_endchar; fet_beginchar ("Mensural Flat" , "mensuralM1"); save stemthick; define_pixels (stemthick); stemthick# = linethickness#; save ellipse, pat, outline, T; path ellipse, pat, outline; transform T; T := identity xscaled 1.4 stemthick yscaled 0.6 stemthick rotated 45; pickup pencircle transformed T; ellipse := fullcircle transformed T; z1 = (0.00 staff_space, +1.80 staff_space); z2 = (0.00 staff_space, -0.25 staff_space); z3 = (0.35 staff_space, -0.25 staff_space); z4 = (0.35 staff_space, +0.25 staff_space); z5 = (0.00 staff_space, +0.25 staff_space); pat := z2 .. z3 .. z4 .. z5; save dirs, s; pair dirs[]; s := 1/4; % we approximate `draw pat' for i = 2 step s until (length pat + 2): dirs[i] := direction (i - 2) of pat; endfor; outline := get_subpath (ellipse, up, down, z1) -- get_subpath (ellipse, down, dirs2, z2) for i = (2 + s) step s until (length pat + 2 - s): .. get_subpoint (ellipse, dirs[i], point (i - 2) of pat) endfor .. top z5 -- bot z5 for i = (length pat + 2 - s) step -s until 2: .. get_subpoint (ellipse, -dirs[i], point (i - 2) of pat) endfor -- get_subpoint (ellipse, up, z2) -- cycle; fill outline; set_char_box (0.00 staff_space# + 0.75 stemthick#, 0.40 staff_space# + 0.75 stemthick#, 0.25 staff_space# + 0.75 stemthick#, 1.80 staff_space# + 0.75 stemthick#); labels (1, 2, 3, 4, 5); fet_endchar; fet_beginchar ("Hufnagel Flat" , "hufnagelM1"); save stemthick; define_pixels (stemthick); stemthick# = linethickness#; save ellipse, pat, T; path ellipse, pat; transform T; T := identity xscaled 2.4 stemthick yscaled 0.4 stemthick rotated 45; pickup pencircle transformed T; ellipse := fullcircle transformed T; z1 = (0.00 staff_space, +1.80 staff_space); z2 = (0.00 staff_space, -0.15 staff_space); z3 = (0.25 staff_space, -0.30 staff_space); z4 = (0.50 staff_space, +0.00 staff_space); z5 = (0.30 staff_space, +0.30 staff_space); z6 = (0.00 staff_space, +0.15 staff_space); pat := z3 .. z4 .. z5; save t; numeric t[]; % we have to find the envelope intersections (if any) t1 = find_envelope_cusp (reverse ellipse, pat, 1/256) + 3; if t1 < 3: t1 := 3; fi; t2 = find_envelope_cusp (ellipse, reverse pat, 1/256); if t2 < 0: t2 := 3; else: t2 := length pat - t2 + 3; fi; save dirs, s; pair dirs[]; s := 1/8; % we approximate `draw pat' for i = 3 step s until 5: dirs[i] := direction (i - 3) of pat; endfor; fill get_subpath (ellipse, up, down, z1) -- get_subpath (ellipse, down, z3 - z2, z2) -- get_subpoint (ellipse, z3 - z2, z3) for i = 3 step s until 5: .. get_subpoint (ellipse, dirs[i], point (i - 3) of pat) endfor .. get_subpoint (ellipse, z6 - z5, z5) -- get_subpoint (ellipse, z6 - z5, z6) -- get_subpoint (ellipse, z5 - z6, z6) -- get_subpoint (ellipse, z5 - z6, z5) -- get_subpoint (ellipse, -dirs[5], z5) for i = (5 - s) step -s until t2: .. get_subpoint (ellipse, -dirs[i], point (i - 3) of pat) endfor .. get_subpoint (ellipse, -direction (t2 - 3) of pat, point (t2 - 3) of pat) -- get_subpoint (ellipse, -direction (t1 - 3) of pat, point (t1 - 3) of pat) for i = (floor ((t1 - 3) / s) * s + 3) step -s until (3 + s): .. get_subpoint (ellipse, -dirs[i], point (i - 3) of pat) endfor .. get_subpoint (ellipse, -dirs[3], z3) -- get_subpoint (ellipse, z2 - z3, z3) -- get_subpoint (ellipse, z2 - z3, z2) -- get_subpoint (ellipse, up, z2) -- cycle; % draw z1 % -- z2 % -- pat % -- z6; set_char_box (0.00 staff_space# + 1.0 stemthick#, 0.50 staff_space# + 1.0 stemthick#, 0.30 staff_space# + 0.5 stemthick#, 1.80 staff_space# + 0.5 stemthick#); labels (1, 2, 3, 4, 5, 6); fet_endchar; fet_endgroup ("accidentals"); muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-clefs.mf000066400000000000000000001245021404105311000246550ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-clefs.mf -- implement ancient clefs % % source file of LilyPond's pretty-but-neat music font % % (c) 2001--2009 Juergen Reuter % fet_begingroup ("clefs"); % % character aligment: % % Each clef is associated with a particular pitch: the treble clef % with the `g', the alto clef with the `c', the bass clef with the % `f', etc. The shape of each clef character defines a vertical % position that is assumed to represent this pitch. For the treble % clef, it is the vertical position of the center of the spiral % ending that represents the `g' pitch. For the bass clef, it is % the center between the two fat dots that define the vertical % position of the `f' pitch. For the alto clef, it is the vertical % center of the clef that is aligned with the `c' pitch. For each % clef character, this center should be vertically aligned with the % point (0, 0). The horizontal alignment of each clef character % should be such that the vertical line through the point (0, 0) % touches the left-most edge of the clef. % % TODO: document exact_center % % set_char_box() conventions: % % * breapth: Ignored (as far as I know). Should be set to 0. % % * width: Should match the clef's width. % % * depth: Should match the bottom edge of the clef. Affects vertical % collision handling. % % * height: Should match the top edge of the clef. Affects vertical % collision handling. % %%%%%%%% % % % % Editio Vaticana % % % def draw_vaticana_do_clef (expr exact_center, reduction) = save reduced_il; reduced_il# = staff_space# * reduction; set_char_box (0 - xpart exact_center, 0.5 reduced_il# + xpart exact_center, 0.8 reduced_il# - ypart exact_center, 0.8 reduced_il# + ypart exact_center); define_pixels (reduced_il); save pat, ellipse, clef, T; path pat, ellipse, clef; transform T; T := identity xscaled 0.6 linethickness yscaled 0.6 reduced_il; pickup pencircle transformed T; ellipse := reverse fullcircle transformed T; save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); rt z11 = (xoffs + 0.50 reduced_il, yoffs - .45 reduced_il); z12 = (xoffs + 0.25 reduced_il, yoffs - .50 reduced_il); lft z13 = (xoffs + 0.00 reduced_il, yoffs - .28 reduced_il); lft z14 = (xoffs, yoffs); pat := z11 .. z12 .. z13 -- z14; save shift; pair shift; % adjust vertically to fit into bounding box shift = find_tangent_shift (((0, -d + 0.3 reduced_il) -- (w, -d + 0.3 reduced_il)), pat, (0, -d / 2), (0, d / 2)); pat := pat shifted shift; clef := rt z14{down} .. top (point 1 of pat) .. get_subpath (ellipse, -direction 0 of pat, direction 0 of pat, point 0 of pat) .. bot (point 1 of pat) .. get_subpath (ellipse, direction 2 of pat, up, point 2 of pat); fill clef -- reverse clef yscaled -1 -- cycle; labels (11, 12, 13, 14); enddef; fet_beginchar ("Ed. Vat. do clef", "vaticana.do"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_vaticana_do_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("Ed. Vat. do clef", "vaticana.do_change"); draw_vaticana_do_clef ((0, 0), 1.0); % no reduction fet_endchar; def draw_vaticana_fa_clef (expr exact_center, reduction) = save reduced_il, xoffs, yoffs; reduced_il# = staff_space# * reduction; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (reduced_il, xoffs, yoffs); % left-handed punctum save ellipse, pat, T; path ellipse, pat; transform T; T := identity xscaled 0.6 linethickness yscaled 0.5 reduced_il; pickup pencircle transformed T; ellipse := reverse fullcircle transformed T; lft z21 = (xoffs + 0.00 reduced_il, yoffs + 0.00 reduced_il); z22 = (xoffs + 0.25 reduced_il, yoffs + 0.05 reduced_il); rt z23 = (xoffs + 0.50 reduced_il, yoffs - 0.05 reduced_il); pat := z21 .. z22 .. z23; fill get_subpath (ellipse, -direction 0 of pat, direction 0 of pat, z21) .. top z22 .. get_subpath (ellipse, direction 2 of pat, -direction 2 of pat, z23) .. bot z22 .. cycle; % stem pickup pencircle scaled 0.6 linethickness; x23 = x24; yoffs = bot y24 + 1.5 reduced_il; draw_rounded_block (bot lft z24, top rt z23, 0.6 linethickness); labels (21, 22, 23, 24); % right-handed puncta as in do clef draw_vaticana_do_clef (exact_center + (0.55 reduced_il#, 0), reduction); set_char_box (0 - xpart exact_center, 1.05 reduced_il# + xpart exact_center, 1.5 reduced_il# - ypart exact_center, 0.8 reduced_il# + ypart exact_center); enddef; fet_beginchar ("Ed. Vat. fa clef", "vaticana.fa"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_vaticana_fa_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("Ed. Vat. fa clef", "vaticana.fa_change"); draw_vaticana_fa_clef ((0, 0), 1.0); % no reduction fet_endchar; %%%%%%%% % % % % Editio Medicaea % % % def draw_medicaea_do_clef (expr exact_center, reduction) = save reduced_il, reduced_slt; reduced_il# = staff_space# * reduction; reduced_slt# = linethickness# * reduction; define_pixels (reduced_il); define_pixels (reduced_slt); set_char_box (0 - xpart exact_center, 1.0 reduced_il# + xpart exact_center, 1.5 reduced_il# - ypart exact_center, 1.5 reduced_il# + ypart exact_center); save flag_height; flag_height# = 0.5 reduced_il#; define_pixels (flag_height); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); % flags save ellipse, T; path ellipse; transform T; T := identity xscaled reduced_slt yscaled flag_height; pickup pencircle transformed T; ellipse := reverse fullcircle transformed T; xoffs = lft x1 = rt x2 - reduced_il; y1 = yoffs + 0.5 (reduced_il - flag_height - staff_space); y2 = y1 - reduced_il + flag_height; fill top z1 -- get_subpath (ellipse, z2 - z1, z1 - z2, z2) -- bot z1 -- cycle; xoffs = lft x3 = rt x4 - reduced_il; y3 = yoffs + 0.5 (reduced_il - flag_height + staff_space); y4 = y3 - reduced_il + flag_height; fill top z3 -- get_subpath (ellipse, z4 - z3, z3 - z4, z4) -- bot z3 -- cycle; % stem pickup pencircle scaled reduced_slt; lft x5 = lft x6 = xoffs; yoffs = top y6 - 1.5 reduced_il = bot y5 + 1.5 reduced_il; draw_rounded_block (bot lft z5, top rt z6, reduced_slt); labels (1, 2, 3, 4, 5, 6); enddef; fet_beginchar ("Ed. Med. do clef", "medicaea.do"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_medicaea_do_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("Ed. Med. do clef", "medicaea.do_change"); draw_medicaea_do_clef ((0, 0), .8); fet_endchar; def draw_medicaea_fa_clef (expr exact_center, reduction) = % inspired by Regensburger Edition of Medicaea (1885/86), in: % MGG, volume 2, col. 1327 ("Choralreform"), fig. 2. save reduced_il, reduced_slt; reduced_il# = staff_space# * reduction; reduced_slt# = linethickness# * reduction; define_pixels (reduced_il); define_pixels (reduced_slt); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); % stem pickup pencircle scaled reduced_slt; x11 = x12 = xoffs + 0.4 reduced_il; y11 = yoffs = bot y12 + 1.5 reduced_il; draw_rounded_block (bot lft z12, top rt z11, reduced_slt); % left-handed punctum save ellipse, T; path ellipse; transform T; T := identity xscaled reduced_slt yscaled reduced_il; pickup pencircle transformed T; ellipse := reverse fullcircle transformed T; lft z13 = (xoffs, yoffs); rt z14 = z11 + (reduced_slt / 2, 0); fill get_subpath (ellipse, left, right, z13) -- get_subpath (ellipse, right, left, z14) -- cycle; labels (11, 12, 13, 14); % right-handed puncta as in do clef draw_medicaea_do_clef (exact_center + (0.7 reduced_il#, 0), reduction); set_char_box (0 - xpart exact_center, 1.7 reduced_il# + xpart exact_center, 1.5 reduced_il# - ypart exact_center, 1.5 reduced_il# + ypart exact_center); enddef; fet_beginchar ("Ed. Med. fa clef", "medicaea.fa"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_medicaea_fa_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("Ed. Med. fa clef", "medicaea.fa_change"); draw_medicaea_fa_clef ((0, 0), .8); fet_endchar; %%%%%%%% % % % % Mensural Notation % % % % % width: interval from left end to right end % height: interval from bottom of lower beam to top of upper beam % exact_center: the coordinates of the vertical center point of the % left edge. % def draw_brevis (expr exact_center, bwidth, bheight, blinethickness) = save brevis_width, brevis_height, linethickness; brevis_width# = bwidth; brevis_height# = bheight; linethickness# = blinethickness; save beam_width, beam_height; save serif_size, serif_protrude, hole_height; beam_width# = 1.4 linethickness#; hole_height# = 3 linethickness#; 2 beam_height# + hole_height# = brevis_height#; serif_size# = (hole_height# - linethickness#) / 2; serif_protrude# = 1.5 serif_size#; save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); define_pixels (brevis_width, brevis_height, linethickness); define_pixels (beam_width, beam_height, serif_size, serif_protrude); z1l = (xoffs, yoffs - linethickness); z2r = z1r + serif_size * (1, -1); z3l = z2l + (-serif_size, -serif_protrude); penpos1 (beam_width, 0); penpos2 (beam_height, 90); penpos3 (beam_width, 180); save pat_in, pat_out; path pat_in, pat_out; pat_out := z3r{down} .. z3l{up} .. z2l{right}; pat_out := pat_out -- reverse pat_out xscaled -1 shifted (2 xoffs + brevis_width, 0); pat_out := pat_out -- reverse pat_out yscaled -1 shifted (0, 2 yoffs) -- cycle; pat_in := z1r{down} .. z2r{right}; pat_in := pat_in -- reverse pat_in xscaled -1 shifted (2 xoffs + brevis_width, 0); pat_in := pat_in -- reverse pat_in yscaled -1 shifted (0, 2 yoffs) -- cycle; fill pat_out; unfill pat_in; penlabels (1, 2, 3); enddef; % % Draw two brevis notes; the second one shifted down by `shift'. % The other parameters are the same as with `draw_brevis'. % def draw_double_brevis (expr exact_center, bwidth, bheight, blinethickness, shift) = save brevis_width, brevis_height, linethickness; brevis_width# = bwidth; brevis_height# = bheight; linethickness# = blinethickness; save beam_width, beam_height; save serif_size, serif_protrude, hole_height; beam_width# = 1.4 linethickness#; hole_height# = 3 linethickness#; 2 beam_height# + hole_height# = brevis_height#; serif_size# = (hole_height# - linethickness#) / 2; serif_protrude# = 1.5 serif_size#; save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); define_pixels (brevis_width, brevis_height, linethickness); define_pixels (beam_width, beam_height, serif_size, serif_protrude); z1l = (xoffs, yoffs - linethickness); z2r = z1r + serif_size * (1, -1); z3l = z2l + (-serif_size, -serif_protrude); penpos1 (beam_width, 0); penpos2 (beam_height, 90); penpos3 (beam_width, 180); z4 = z1 shifted (0, -shift); z5 = z2 shifted (0, -shift); z6 = z3 shifted (0, -shift); penpos4 (beam_width, 0); penpos5 (beam_height, 90); penpos6 (beam_width, 180); save pat_in, pat_out; path pat_in, pat_out; pat_out := z6r{down} .. z6l{up} .. z5l{right}; pat_out := pat_out -- reverse pat_out xscaled -1 shifted (2 xoffs + brevis_width, 0); pat_out := pat_out -- reverse pat_out yscaled -1 shifted (0, shift - 2 yoffs) -- cycle; fill pat_out; pat_in := z1r{down} .. z2r{right}; pat_in := pat_in -- reverse pat_in xscaled -1 shifted (2 xoffs + brevis_width, 0); pat_in := pat_in -- reverse pat_in yscaled -1 shifted (0, 2 yoffs) -- cycle; unfill pat_in; unfill pat_in shifted (0, -shift); penlabels (1, 2, 3, 4, 5, 6); enddef; % % Draw three brevis notes; the second one shifted down by `shift', % the third one by `2 shift'. % The other parameters are the same as with `draw_brevis'. % def draw_triple_brevis (expr exact_center, bwidth, bheight, blinethickness, shift) = save brevis_width, brevis_height, linethickness; brevis_width# = bwidth; brevis_height# = bheight; linethickness# = blinethickness; save beam_width, beam_height; save serif_size, serif_protrude, hole_height; beam_width# = 1.4 linethickness#; hole_height# = 3 linethickness#; 2 beam_height# + hole_height# = brevis_height#; serif_size# = (hole_height# - linethickness#) / 2; serif_protrude# = 1.5 serif_size#; save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); define_pixels (brevis_width, brevis_height, linethickness); define_pixels (beam_width, beam_height, serif_size, serif_protrude); z1l = (xoffs, yoffs - linethickness); z2r = z1r + serif_size * (1, -1); z3l = z2l + (-serif_size, -serif_protrude); penpos1 (beam_width, 0); penpos2 (beam_height, 90); penpos3 (beam_width, 180); z7 = z1 shifted (0, -2 shift); z8 = z2 shifted (0, -2 shift); z9 = z3 shifted (0, -2 shift); penpos7 (beam_width, 0); penpos8 (beam_height, 90); penpos9 (beam_width, 180); save pat_in, pat_out; path pat_in, pat_out; pat_out := z9r{down} .. z9l{up} .. z8l{right}; pat_out := pat_out -- reverse pat_out xscaled -1 shifted (2 xoffs + brevis_width, 0); pat_out := pat_out -- reverse pat_out yscaled -1 shifted (0, -2 yoffs) -- cycle; fill pat_out; pat_in := z1r{down} .. z2r{right}; pat_in := pat_in -- reverse pat_in xscaled -1 shifted (2 xoffs + brevis_width, 0); pat_in := pat_in -- reverse pat_in yscaled -1 shifted (0, 2 yoffs) -- cycle; unfill pat_in; unfill pat_in shifted (0, -shift); unfill pat_in shifted (0, -2 shift); penlabels (1, 2, 3, 7, 8, 9); enddef; def draw_neomensural_c_clef (expr exact_center, reduction) = save reduced_il, reduced_slt, stem_width; reduced_il# = staff_space# * reduction; reduced_slt# = linethickness# * reduction; stem_width# = 1.4 reduced_slt#; define_pixels (reduced_il, reduced_slt, stem_width); set_char_box (0 - xpart exact_center, 2 reduced_il# + 6 reduced_slt# + xpart exact_center, 2 reduced_il# - ypart exact_center, 2 reduced_il# + ypart exact_center); draw_brevis (exact_center + (3 reduced_slt#, 0), 2 reduced_il#, reduced_il#, reduced_slt#); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); save ellipse, pat, T; path ellipse, pat; transform T; T := identity xscaled stem_width yscaled blot_diameter; pickup pencircle transformed T; ellipse := fullcircle transformed T; lft x11 = lft x12 = xoffs; top y12 - bot y11 = 4 reduced_il; top y12 + bot y11 = 2 yoffs; x13 = x3; y13 = y11; rt x14 = rt x15 = w; y14 = y11; y15 = y12; pat := get_subpath (ellipse, down, up, z13) -- z3l -- z3r -- cycle; fill get_subpath (ellipse, down, up, z11) -- get_subpath (ellipse, up, down, z12) -- cycle; fill get_subpath (ellipse, down, up, z14) -- get_subpath (ellipse, up, down, z15) -- cycle; fill pat; fill pat xscaled -1 shifted (w, 0); fill pat yscaled -1 shifted (0, 2 yoffs); fill pat scaled -1 shifted (w, 2 yoffs); labels (11, 12, 13, 14, 15); enddef; fet_beginchar ("neo-mensural c clef", "neomensural.c"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_neomensural_c_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("neo-mensural c clef", "neomensural.c_change"); draw_neomensural_c_clef ((0, 0), .8); fet_endchar; def draw_petrucci_c_clef (expr exact_center, flare_align, reduction) = % inspired by Josquin Desprez, "Stabat Mater", Libro tertio, % 1519, printed by Petrucci, in: MGG, volume 7, Table 11. % Also by Petrucci's Canti C, Venedig 1503. In: MGG, volume % 9, p. 1681/1682. save reduced_il, reduced_slt; reduced_il# = staff_space# * reduction; reduced_slt# = linethickness# * reduction; define_pixels (reduced_il); draw_double_brevis (exact_center + (0, 0.5 staff_space#), reduced_il#, reduced_il#, reduced_slt#, staff_space); save half_reduced_il, left_depth, left_height; half_reduced_il# = staff_space# * sqrt (reduction); left_height# = half_reduced_il# * min (3.2, 3.2 + 0.2 + flare_align); left_depth# = half_reduced_il# * min (3.2, 3.2 + 0.2 - flare_align); define_pixels (half_reduced_il); define_pixels (left_depth, left_height); set_char_box (0 - xpart exact_center, reduced_il# + xpart exact_center, left_depth# - ypart exact_center, left_height# + ypart exact_center); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); save ellipse, T; path ellipse; transform T; T := identity xscaled 1.4 linethickness yscaled blot_diameter; pickup pencircle transformed T; ellipse := fullcircle transformed T; lft x11 = lft x13 = xoffs; top y11 = yoffs + left_height; bot y13 = yoffs - left_depth; rt x15 = rt x17 = xoffs + brevis_width; y15 = min (y11 - 0.2 half_reduced_il, yoffs + 2.2 half_reduced_il); y17 = max (y13 + 0.2 half_reduced_il, yoffs - 2.2 half_reduced_il); z12 = z14 yscaled -1; z14 = z6; z16 = z18 yscaled -1; rt z18 = lft z14 shifted (brevis_width, 0); penpos12 (1.4 linethickness, 0); penpos14 (1.4 linethickness, 0); penpos16 (1.4 linethickness, 0); penpos18 (1.4 linethickness, 0); if top y11 > -y6 + 0.7 linethickness: fill get_subpath (ellipse, up, down, z11) -- z12l -- z12r -- cycle; fi; if bot y13 < y6 - 0.7 linethickness: fill get_subpath (ellipse, down, up, z13) -- z14r -- z14l -- cycle; fi; if top y15 > -y6 + 0.7 linethickness: fill get_subpath (ellipse, up, down, z15) -- z16l -- z16r -- cycle; fi; if bot y17 < y6 - 0.7 linethickness: fill get_subpath (ellipse, down, up, z17) -- z18r -- z18l -- cycle; fi; labels (11, 13, 15, 17); penlabels (12, 14, 16, 18); enddef; fet_beginchar ("petrucci c1 clef", "petrucci.c1"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_petrucci_c_clef ((0, 0), +2, 1.0); fet_endchar; fet_beginchar ("petrucci c1 clef", "petrucci.c1_change"); draw_petrucci_c_clef ((0, 0), +2, .8); fet_endchar; fet_beginchar ("petrucci c2 clef", "petrucci.c2"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_petrucci_c_clef ((0, 0), +1, 1.0); fet_endchar; fet_beginchar ("petrucci c2 clef", "petrucci.c2_change"); draw_petrucci_c_clef ((0, 0), +1, .8); fet_endchar; fet_beginchar ("petrucci c3 clef", "petrucci.c3"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_petrucci_c_clef ((0, 0), 0, 1.0); fet_endchar; fet_beginchar ("petrucci c3 clef", "petrucci.c3_change"); draw_petrucci_c_clef ((0, 0), 0, .8); fet_endchar; fet_beginchar ("petrucci c4 clef", "petrucci.c4"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_petrucci_c_clef ((0, 0), -1, 1.0); fet_endchar; fet_beginchar ("petrucci c4 clef", "petrucci.c4_change"); draw_petrucci_c_clef ((0, 0), -1, .8); fet_endchar; fet_beginchar ("petrucci c5 clef", "petrucci.c5"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_petrucci_c_clef ((0, 0), -2, 1.0); fet_endchar; fet_beginchar ("petrucci c5 clef", "petrucci.c5_change"); draw_petrucci_c_clef ((0, 0), -2, .8); fet_endchar; def draw_mensural_c_clef (expr exact_center, reduction) = % inspired by Ockeghem, "Missa Prolationum", in: MGG, volume % 9, table 94. save reduced_il; reduced_il# = staff_space# * reduction; define_pixels (reduced_il); draw_triple_brevis (exact_center + (0, 0.5 staff_space#), 2 reduced_il#, 0.8 staff_space#, 0.8 linethickness#, staff_space); save half_reduced_il; half_reduced_il# = staff_space# * sqrt (reduction); define_pixels (half_reduced_il); set_char_box (0 - xpart exact_center, 2 reduced_il# + xpart exact_center, 2.2 half_reduced_il# + staff_space# - 2 ypart exact_center, 2.2 half_reduced_il# + 2 ypart exact_center); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); save ellipse, T; path ellipse; transform T; T := identity xscaled 1.4 linethickness yscaled blot_diameter; pickup pencircle transformed T; ellipse := fullcircle transformed T; lft x11 = lft x13 = xoffs; top y11 = yoffs + 2.2 half_reduced_il; bot y13 = yoffs - 2.2 half_reduced_il - staff_space; rt x15 = rt x17 = xoffs + brevis_width; y15 = yoffs + 1.4 half_reduced_il; y17 = yoffs - 1.4 half_reduced_il - staff_space; z12 = z14 yscaled -1 shifted (0, -staff_space); z14 = z9; z16 = z18 yscaled -1 shifted (0, -staff_space); rt z18 = lft z14 shifted (brevis_width, 0); penpos12 (1.4 linethickness, 0); penpos14 (1.4 linethickness, 0); penpos16 (1.4 linethickness, 0); penpos18 (1.4 linethickness, 0); fill get_subpath (ellipse, up, down, z11) -- z12l -- z12r -- cycle; fill get_subpath (ellipse, down, up, z13) -- z14r -- z14l -- cycle; fill get_subpath (ellipse, up, down, z15) -- z16l -- z16r -- cycle; fill get_subpath (ellipse, down, up, z17) -- z18r -- z18l -- cycle; labels (11, 13, 15, 17); penlabels (12, 14, 16, 18); enddef; fet_beginchar ("mensural c clef", "mensural.c"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_mensural_c_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("mensural c clef", "mensural.c_change"); draw_mensural_c_clef ((0, 0), .8); fet_endchar; def draw_diamond (expr exact_center, reduction) = save stem_width, reduced_nht, holeheight, beamheight; save rh_height, rh_width; stem_width# = 1.4 reduced_slt#; reduced_nht# = noteheight# * reduction; holeheight# = 3 reduced_slt#; beamheight# = 0.4 (reduced_nht# - holeheight#); rh_height# = 1.2 staff_space# * reduction; rh_width# / rh_height# = tand (30); define_pixels (beamheight, stem_width); define_pixels (rh_height, rh_width); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); save ellipse, T; path ellipse; transform T; T := identity xscaled beamheight yscaled stem_width rotated 45; pickup pencircle transformed T; ellipse := reverse fullcircle transformed T; x21 := xoffs - rh_width / 2; y21 := yoffs; x22 := xoffs; y22 := yoffs + rh_height / 2; x23 := xoffs + rh_width / 2; y23 := yoffs; x24 := xoffs; y24 := yoffs - rh_height / 2; fill get_subpath (ellipse, z21 - z24, z22 - z21, z21) -- get_subpath (ellipse, z22 - z21, z23 - z22, z22) -- get_subpath (ellipse, z23 - z22, z24 - z23, z23) -- get_subpath (ellipse, z24 - z23, z21 - z24, z24) -- cycle; save l; path l[]; l2122 := (directionpoint (z21 - z22) of ellipse) shifted z21 -- (directionpoint (z21 - z22) of ellipse) shifted z22; l2223 := (directionpoint (z22 - z23) of ellipse) shifted z22 -- (directionpoint (z22 - z23) of ellipse) shifted z23; l2324 := (directionpoint (z23 - z24) of ellipse) shifted z23 -- (directionpoint (z23 - z24) of ellipse) shifted z24; l2421 := (directionpoint (z24 - z21) of ellipse) shifted z24 -- (directionpoint (z24 - z21) of ellipse) shifted z21; unfill l2122 intersectionpoint l2223 -- l2223 intersectionpoint l2324 -- l2324 intersectionpoint l2421 -- l2421 intersectionpoint l2122 -- cycle; labels (21, 22, 23, 24); enddef; def draw_petrucci_f_clef (expr exact_center, reduction) = % inspired by L'homme arme super voces musicales in Misse % Josquin, 1502, Petrucci, in: MGG, volume 7, col. 200; also % inspired by Gaspar van Weerbeke, "Virgo Maria" (1502), in: % MGG, volume 9, col. 653 ("Motette"), fig. 3.; also by Andr'e % Campra, "Entr'ee des s'er'enades" (1710), in: MGG, volume 2, % col. 1649 ("Contredanse"), fig. 2. save interline, reduced_il, reduced_slt; interline# = staff_space#; reduced_il# = staff_space# * reduction; reduced_slt# = linethickness# * reduction; draw_brevis (exact_center, reduced_il#, reduced_il#, reduced_slt#); draw_diamond (exact_center + (1.6 interline# * reduction, interline# / 2), reduction); draw_diamond (exact_center + (1.6 interline# * reduction, -interline# / 2), reduction); define_pixels (interline, reduced_il, reduced_slt); save stem_width; stem_width# = 1.4 reduced_slt#; define_pixels (stem_width); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); % brevis stem save ellipse, T; path ellipse; transform T; T := identity xscaled stem_width yscaled blot_diameter; pickup pencircle transformed T; ellipse := fullcircle transformed T; rt x8 = xoffs + reduced_il; y8 = y3; rt z9 = (xoffs + reduced_il, yoffs - 4 reduced_il); penpos8 (stem_width, 0); fill get_subpath (ellipse, down, up, z9) -- z8r -- z8l -- cycle; % upper diamond's stem z10 = (xoffs + 1.6 interline * reduction + stem_width / 2, yoffs + interline * reduction); top z11 = z10 + (0, 1.5 interline * reduction); penpos10 (stem_width, 0); fill get_subpath (ellipse, up, down, z11) -- z10l -- z10r -- cycle; % lower diamond's stem z12 = (xoffs + 1.6 interline * reduction - stem_width / 2, yoffs - interline * reduction); bot z13 = z12 + (0, -3.5 interline * reduction); penpos12 (stem_width, 0); fill get_subpath (ellipse, down, up, z13) -- z12r -- z12l -- cycle; save reduced_il, rh_height, rh_width; reduced_il# = staff_space# * reduction; rh_height# = 1.2 reduced_il#; rh_width# / rh_height# = tand (30); set_char_box (0 - xpart exact_center, 1.6 interline# * reduction + 0.5 rh_width# + xpart exact_center, 4.5 interline# * reduction - ypart exact_center, 2.5 interline# * reduction + ypart exact_center); labels (9, 11, 13); penlabels (8, 10, 12); enddef; fet_beginchar ("petrucci f clef", "petrucci.f"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_petrucci_f_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("petrucci f clef", "petrucci.f_change"); draw_petrucci_f_clef ((0, 0), .8); fet_endchar; def draw_mensural_f_clef (expr exact_center, reduction) = % inspired by Philippe le Duc, "Dite Signori" (1590), in: MGG, % volume 3, col. 848 ("Duc"); also by John Dowland, "The First % Booke of Songes" (1597), in: MGG, volume 3, col. 721 % ("Dowland"), fig. 3. save width, reduced_slt, stem_width, dot_diameter; width# = 1.2 staff_space# * reduction; reduced_slt# = linethickness# * reduction; stem_width# = 1.4 reduced_slt#; dot_diameter# = 0.1 reduction * staff_space#; define_pixels (width, stem_width, staff_space, dot_diameter); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); save ellipse, T; path ellipse; transform T; T := identity xscaled 0.2 width yscaled stem_width rotated 45; pickup pencircle transformed T; ellipse := fullcircle transformed T; % half circle lft z10 = (0, 0); save pat; path pat; pat := halfcircle scaled width rotated -90 shifted (z10 - (xoffs, yoffs)); z5 = point 0 of pat; z6 = point 1 of pat; z7 = point 2 of pat; z8 = point 3 of pat; z9 = point 4 of pat; save dirs; pair dirs[]; dirs5 := direction 0 of pat; dirs6 := direction 1 of pat; dirs7 := direction 2 of pat; dirs8 := direction 3 of pat; dirs9 := direction 4 of pat; % we approximate `draw pat' fill get_subpath (ellipse, -dirs5, dirs5, z5) .. get_subpoint (ellipse, dirs6, z6) .. get_subpoint (ellipse, dirs7, z7) .. get_subpoint (ellipse, dirs8, z8) .. get_subpath (ellipse, dirs9, -dirs9, z9) .. get_subpoint (ellipse, -dirs8, z8) .. get_subpoint (ellipse, -dirs7, z7) .. get_subpoint (ellipse, -dirs6, z6) .. cycle; % upper dot rt x2 = xoffs + width; top y1 = yoffs + 0.5 width; z2 - z1 = (dot_diameter, -dot_diameter); fill get_subpath (ellipse, z1 - z2, z2 - z1, z1) -- get_subpath (ellipse, z2 - z1, z1 - z2, z2) -- cycle; % lower dot x3 = x1; top y1 - bot y4 = width; z4 - z3 = (dot_diameter, -dot_diameter); fill get_subpath (ellipse, z3 - z4, z4 - z3, z3) -- get_subpath (ellipse, z4 - z3, z3 - z4, z4) -- cycle; set_char_box (0 - xpart exact_center, width# + xpart exact_center, 0.5 width# - ypart exact_center, 0.5 width# + ypart exact_center); labels (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); enddef; fet_beginchar ("mensural f clef", "mensural.f"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_mensural_f_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("mensural f clef", "mensural.f_change"); draw_mensural_f_clef ((0, 0), .8); fet_endchar; def draw_petrucci_g_clef (expr exact_center, reduction) = % inspired by Josquin Desprez, "Stabat Mater", Libro tertio, % 1519, printed by Petrucci, in: MGG, volume 7, Table 11. save reduced_il, reduced_slt; reduced_il# = staff_space# * reduction; reduced_slt# = linethickness# * reduction; define_pixels (reduced_il, reduced_slt); set_char_box (0 - xpart exact_center, 1.25 reduced_il# + xpart exact_center, 0.65 reduced_il# - ypart exact_center, 3.80 reduced_il# + ypart exact_center); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); save ellipse, paths, sub_path, outlines, sub_outlines, T; path ellipse, paths[], sub_path, outlines[], sub_outlines[]; transform T; T := identity xscaled 0.5 reduced_slt yscaled 0.22 reduced_il rotated -35; pickup pencircle transformed T; ellipse := fullcircle transformed T; lft z1 = (xoffs + 0.80 reduced_il, yoffs + 0.00 reduced_il); lft z2 = (xoffs + 1.00 reduced_il, yoffs + 1.20 reduced_il); lft z3 = (xoffs + 0.70 reduced_il, yoffs + 2.00 reduced_il); lft z4 = (xoffs + 0.30 reduced_il, yoffs + 3.00 reduced_il); lft z5 = (xoffs + 0.80 reduced_il, yoffs + 3.70 reduced_il); lft z6 = (xoffs + 1.00 reduced_il, yoffs + 3.00 reduced_il); lft z7 = (xoffs + 0.60 reduced_il, yoffs + 2.00 reduced_il); lft z8 = (xoffs + 0.30 reduced_il, yoffs + 1.70 reduced_il); lft z9 = (xoffs + 0.00 reduced_il, yoffs + 0.75 reduced_il); lft z10 = (xoffs + 0.20 reduced_il, yoffs + 0.60 reduced_il); paths1 := z1{-1, 2} .. z2 .. z3 .. z4 .. z5 .. z6 .. z7 .. z8 .. z9 .. z10; save dirs, s; pair dirs[]; s := 1/4; % we approximate `draw paths1' for i = 1 step s until (length paths1 + 1): dirs[i] := direction (i - 1) of paths1; endfor; outlines1 := get_subpath (ellipse, -dirs1, dirs1, z1) for i = (1 + s) step s until (length paths1 + 1 - s): .. get_subpoint (ellipse, dirs[i], point (i - 1) of paths1) endfor .. get_subpath (ellipse, dirs10, -dirs10, z10) for i = (length paths1 + 1 - s) step -s until (1 + s): .. get_subpoint (ellipse, -dirs[i], point (i - 1) of paths1) endfor .. cycle; save len; len := length outlines1; sub_outlines1 := subpath (0, floor (1/4 len)) of outlines1; sub_outlines2 := subpath (floor (1/4 len), floor (2/4 len)) of outlines1; sub_outlines3 := subpath (floor (2/4 len), floor (3/4 len)) of outlines1; sub_outlines4 := subpath (floor (3/4 len), len) of outlines1; save times; numeric times[]; (times12, times21) = sub_outlines1 intersectiontimes sub_outlines2; (times13, times31) = sub_outlines1 intersectiontimes sub_outlines3; (times42, times24) = sub_outlines4 intersectiontimes sub_outlines2; (times43, times34) = sub_outlines4 intersectiontimes sub_outlines3; T := identity xscaled 0.75 reduced_slt yscaled 0.33 reduced_il rotated -35; pickup pencircle transformed T; ellipse := fullcircle transformed T; lft z21 = (xoffs + 1.05 reduced_il, yoffs + 0.45 reduced_il); lft z22 = (xoffs + 0.55 reduced_il, yoffs + 0.45 reduced_il); lft z23 = (xoffs + 0.55 reduced_il, yoffs - 0.45 reduced_il); lft z24 = (xoffs + 1.05 reduced_il, yoffs - 0.45 reduced_il); lft z25 = (xoffs + 1.10 reduced_il, yoffs + 0.00 reduced_il); lft z26 = (xoffs + 0.80 reduced_il, yoffs + 0.00 reduced_il); paths2 := z21 .. z22 .. z23 .. z24 .. {up}z25 -- z26; sub_path := subpath (0, 1) of paths2; times1 = xpart (sub_outlines1 intersectiontimes sub_path); times4 = xpart (sub_outlines4 intersectiontimes sub_path); % we have to find the envelope intersections (if any) save t; numeric t[]; t1 = find_envelope_cusp (reverse ellipse, subpath (1, 2) of paths2, 1/256) + 1; if t1 < 1: t1 := 1; t2 := 1; else: t2 = find_envelope_cusp (ellipse, subpath (3, 4) of reverse paths2, 1/256) + 3; t2 := length paths2 - t2; fi; t3 = find_envelope_cusp (reverse ellipse, subpath (2, 4 - epsilon) of paths2, 1/256) + 2; if t3 < 2: t3 := 3; t4 := 3; else: t4 = find_envelope_cusp (ellipse, subpath (1 + epsilon, 3) of reverse paths2, 1/256) + 1; t4 := length paths2 - t4; fi; fill subpath (times1 + s / 4, times13) of sub_outlines1 -- subpath (times31, infinity) of sub_outlines3 & subpath (0, times42) of sub_outlines4 -- subpath (times24, infinity) of sub_outlines2 & subpath (0, times34) of sub_outlines3 -- subpath (times43, times4 - s / 4) of sub_outlines4 -- cycle; unfill subpath (times12, infinity) of sub_outlines1 & subpath (0, times21) of sub_outlines2 -- cycle; fill subpath (times4 + s / 4, infinity) of sub_outlines4 & subpath (0, times1 - s / 4) of sub_outlines1 -- cycle; % we approximate `draw paths2' for i = 1 step s until (length paths2 - s): dirs[i + 20] := direction (i - 1) of paths2; endfor; sub_outlines21 := get_subpath (ellipse, -dirs21, dirs21, z21) for i = (1 + s) step s until (length paths2 - s): .. get_subpoint (ellipse, dirs[i + 20], point (i - 1) of paths2) endfor .. get_subpath (ellipse, up, z26 - z25, z25); sub_outlines22 := get_subpath (ellipse, z26 - z25, z25 - z26, z26) -- get_subpoint (ellipse, z25 - z26, z25); sub_outlines23 := get_subpoint (ellipse, down, z25) for i = (length paths2 - s) step -s until (t4 + 1): .. get_subpoint (ellipse, -dirs[i + 20], point (i - 1) of paths2) endfor .. get_subpoint (ellipse, -direction t4 of paths2, point t4 of paths2); sub_outlines24 := get_subpoint (ellipse, -direction t3 of paths2, point t3 of paths2) for i = (floor (t3 / s) * s + 1) step -s until (t2 + 1): .. get_subpoint (ellipse, -dirs[i + 20], point (i - 1) of paths2) endfor .. get_subpoint (ellipse, -direction t2 of paths2, point t2 of paths2); sub_outlines25 := get_subpoint (ellipse, -direction t1 of paths2, point t1 of paths2) for i = (floor (t1 / s) * s + 1) step -s until (1 + s): .. get_subpoint (ellipse, -dirs[i + 20], point (i - 1) of paths2) endfor; (times2223, times2322) = sub_outlines22 intersectiontimes sub_outlines23; (times2324, times2423) = sub_outlines23 intersectiontimes sub_outlines24; (times2425, times2524) = sub_outlines24 intersectiontimes sub_outlines25; fill sub_outlines21 -- subpath (0, times2223) of sub_outlines22 -- subpath (times2322, times2324) of sub_outlines23 -- subpath (times2423, times2425) of sub_outlines24 -- subpath (times2524, infinity) of sub_outlines25 .. cycle; labels (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); labels (21, 22, 23, 24, 25, 26); enddef; fet_beginchar ("petrucci g clef", "petrucci.g"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_petrucci_g_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("petrucci g clef", "petrucci.g_change"); draw_petrucci_g_clef ((0, 0), .8); fet_endchar; def draw_mensural_g_clef (expr exact_center, reduction) = % TODO: Rewrite me. The former mensural g clef looked ugly, and the % code was removed when it broke for small font sizes after some % global changes in the font. Currently, the character is mapped to % a copy of the petrucci g clef (which, after all, *is* a mensural g % clef, but not the one that we have in mind here). -- jr % % Possible sources of inspiration for this clef include: Francisco % Guerrero, "Lib. 1. Missarum" (1566), in: MGG, volume 3, col. 858 % ("Ducis"); Stefano Fabri, "Quam speciosa veteranis" (1611), in: % MGG, volume 3, col. 1698 ("Fabri"); Philippus Dulichius, % "Fasciculus novus ..." (1598), in: MGG, volume 3, col. 919 % ("Dulichius"), fig. 1; Noe Faignient, "Ic sal de Heer myn God % gebenedye" (1568), in: MGG, volume 3, col. 1735 ("Faignient"). enddef; % % FIXME: This clef is preliminarily mapped to the petrucci g clef % until the code for the mensural g clef will be rewritten. % fet_beginchar ("mensural g clef", "mensural.g"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_petrucci_g_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("mensural g clef", "mensural.g_change"); draw_petrucci_g_clef ((0, 0), .8); fet_endchar; %%%%%%%% % % % % Hufnagel % % % def draw_hufnagel_do_clef (expr exact_center, reduction) = % inspired by Graduale of Friedrich Zollner (1442), in: MGG, % volume 9, col. 1413 ("Neustift"), fig. 1. save reduced_il; reduced_il# = staff_space# * reduction; define_pixels (reduced_il); set_char_box (0 - xpart exact_center, 1.10 reduced_il# + xpart exact_center, 0.70 reduced_il# - ypart exact_center, 0.75 reduced_il# + ypart exact_center); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); save ellipse, pat, T; path ellipse, pat; transform T; T := identity xscaled 0.6 reduced_il yscaled 0.1 reduced_il rotated 40; pickup pencircle transformed T; ellipse := fullcircle transformed T; z1 = (xoffs + 0.90 reduced_il, yoffs + .45 reduced_il); z2 = (xoffs + 0.80 reduced_il, yoffs + .45 reduced_il); z3 = (xoffs + 0.50 reduced_il, yoffs + .60 reduced_il); z4 = (xoffs + 0.20 reduced_il, yoffs + .45 reduced_il); z5 = (xoffs + 0.20 reduced_il, yoffs - .45 reduced_il); z6 = (xoffs + 0.40 reduced_il, yoffs - .55 reduced_il); pat := z1 .. z2 .. z3 -- z4 -- z5 -- z6; fill get_subpath (ellipse, -direction 0 of pat, direction 0 of pat, z1) .. get_subpoint (ellipse, direction 1 of pat, z2) .. get_subpath (ellipse, direction (2 - epsilon) of pat, z4 - z3, z3) -- get_subpath (ellipse, z4 - z3, z5 - z4, z4) -- get_subpath (ellipse, z5 - z4, z6 - z5, z5) -- get_subpath (ellipse, z6 - z5, z5 - z6, z6) -- get_subpoint (ellipse, z5 - z6, z5) -- get_subpoint (ellipse, z4 - z5, z5) -- get_subpoint (ellipse, z4 - z5, z4) -- get_subpoint (ellipse, -direction (2 - epsilon) of pat, z3) .. get_subpath (ellipse, -direction 1 of pat, -direction 1 of pat, z2) .. cycle; labels (1, 2, 3, 4, 5, 6); enddef; fet_beginchar ("Hufnagel do clef", "hufnagel.do"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_hufnagel_do_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("Hufnagel do clef", "hufnagel.do_change"); draw_hufnagel_do_clef ((0, 0), .8); fet_endchar; def draw_hufnagel_fa_clef (expr exact_center, reduction) = % inspired by Bamberger Manuscript (15th century), in: % MGG, volume 2, table 59. save reduced_il; reduced_il# = staff_space# * reduction; define_pixels (reduced_il); set_char_box (0 - xpart exact_center, 1.20 reduced_il# + xpart exact_center, 1.15 reduced_il# - ypart exact_center, 1.00 reduced_il# + ypart exact_center); save xoffs, yoffs; xoffs# = xpart exact_center; yoffs# = ypart exact_center; define_pixels (xoffs, yoffs); save ellipse, pat, T; path ellipse, pat; transform T; T := identity xscaled 0.6 reduced_il yscaled 0.1 reduced_il rotated 40; pickup pencircle transformed T; ellipse := fullcircle transformed T; z11 = (xoffs + 0.90 reduced_il, yoffs + 0.70 reduced_il); z12 = (xoffs + 0.80 reduced_il, yoffs + 0.70 reduced_il); z13 = (xoffs + 0.50 reduced_il, yoffs + 0.85 reduced_il); z14 = (xoffs + 0.20 reduced_il, yoffs + 0.70 reduced_il); z15 = (xoffs + 0.20 reduced_il, yoffs - 1.10 reduced_il); pat := z11 .. z12 .. z13 -- z14 -- z15; fill get_subpath (ellipse, -direction 0 of pat, direction 0 of pat, z11) .. get_subpoint (ellipse, direction 1 of pat, z12) .. get_subpath (ellipse, direction (2 - epsilon) of pat, z14 - z13, z13) -- get_subpath (ellipse, z14 - z13, z15 - z14, z14) -- get_subpath (ellipse, z15 - z14, z14 - z15, z15) -- get_subpoint (ellipse, z14 - z15, z14) -- get_subpoint (ellipse, -direction (2 - epsilon) of pat, z13) .. get_subpath (ellipse, -direction 1 of pat, -direction 1 of pat, z12) .. cycle; z16 = (xoffs + 0.90 reduced_il, yoffs - 0.05 reduced_il); z17 = (xoffs + 0.80 reduced_il, yoffs - 0.05 reduced_il); z18 = (xoffs + 0.50 reduced_il, yoffs + 0.10 reduced_il); z19 = (xoffs + 0.20 reduced_il, yoffs - 0.05 reduced_il); pat := z16 .. z17 .. z18 -- z19; fill get_subpath (ellipse, -direction 0 of pat, direction 0 of pat, z16) .. get_subpoint (ellipse, direction 1 of pat, z17) .. get_subpath (ellipse, direction (2 - epsilon) of pat, z19 - z18, z18) -- get_subpoint (ellipse, z19 - z18, z19) -- get_subpoint (ellipse, -direction (2 - epsilon) of pat, z18) .. get_subpoint (ellipse, -direction 1 of pat, z17) .. cycle; labels (11, 12, 13, 14, 15, 16, 17, 18, 19); enddef; fet_beginchar ("Hufnagel fa clef", "hufnagel.fa"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_hufnagel_fa_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("Hufnagel fa clef", "hufnagel.fa_change"); draw_hufnagel_fa_clef ((0, 0), .8); fet_endchar; def draw_hufnagel_do_fa_clef (expr exact_center, reduction) = draw_hufnagel_do_clef (exact_center, reduction); draw_hufnagel_fa_clef (exact_center + (0, -2 staff_space#), reduction); set_char_box (0 - xpart exact_center, 1.20 reduced_il# + xpart exact_center, 1.15 reduced_il# + 2 staff_space# - ypart exact_center, 0.75 reduced_il# + ypart exact_center); enddef; fet_beginchar ("Hufnagel do/fa clef", "hufnagel.do.fa"); if test = 1: draw_staff (-1, 3, 0.0); fi; draw_hufnagel_do_fa_clef ((0, 0), 1.0); fet_endchar; fet_beginchar ("Hufnagel do/fa clef", "hufnagel.do.fa_change"); draw_hufnagel_do_fa_clef ((0, 0), .8); fet_endchar; fet_endgroup ("clefs"); muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-custodes.mf000066400000000000000000000275561404105311000254250ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-custodes.mf -- implement ancient custodes % % source file of LilyPond's pretty-but-neat music font % % (c) 2000--2009 Juergen Reuter % save black_notehead_width; numeric black_notehead_width; fet_begingroup ("custodes"); % % character aligment: % % The custos is assumed to be vertically centered around (0, 0). % The left-most edge of the custos should touch the vertical line % that goes though the point (0, 0). % % set_char_box() conventions: % % * breapth: Ignored (as far as I know). Should be set to 0. % % * width: Should cover the horizontal range of the custos that is to % be printed in the staff. If the custos has an appendage that is % supposed to reach beyond the right end of the staff, the width % should be set such that the appendage is outside of the char box. % % * depth: Should match the bottom edge of the custos. Affects % vertical collision handling. % % * height: Should match the top edge of the custos. Affects vertical % collision handling. % save between_staff_lines, on_staff_line, anywhere; between_staff_lines := 0; on_staff_line := 1; anywhere := 2; save dir_up, dir_down; dir_up := 1; dir_down := -1; %%%%%%%% % % % Hufnagel style % % % parameterized hufnagel custos def custos_hufnagel (expr verbose_name, internal_name, direction, staffline_adjustment) = fet_beginchar (verbose_name, internal_name); save alpha, dalpha, ht, wd, stem_ht, pen_size; ht# = noteheight#; wd# / ht# = 0.6; alpha = 35; dalpha = direction * alpha; if staffline_adjustment = between_staff_lines: stem_ht# = 1.00 staff_space#; elseif staffline_adjustment = on_staff_line: stem_ht# = 1.50 staff_space#; else: % staffline_adjustment = anywhere stem_ht# = 1.25 staff_space#; fi; pen_size# = 0.5 (wd# ++ ht#); define_pixels (ht, wd, pen_size, stem_ht); save ellipse, pat, T; path ellipse, pat; transform T; T := identity xscaled linethickness yscaled pen_size rotated -dalpha; pickup pencircle transformed T; ellipse := fullcircle transformed T; if direction = dir_up: top y1 = ht / 2; bot y2 = -ht / 2; else: bot y1 = -ht / 2; top y2 = ht / 2; fi; lft x1 = 0; rt x2 = wd; y3 - y2 = direction * stem_ht; (y3 - y2) = (x3 - x2) * tand (90 - dalpha); fill get_subpath (ellipse, z1 - z2, z2 - z1, z1) if direction > 0: -- get_subpath (ellipse, z2 - z1, z3 - z2, z2) else: -- get_subpoint (ellipse, z2 - z1, z2) -- get_subpoint (ellipse, z3 - z2, z2) fi -- get_subpath (ellipse, z3 - z2, z2 - z3, z3) if direction > 0: -- get_subpoint (ellipse, z2 - z3, z2) -- get_subpoint (ellipse, z1 - z2, z2) else: -- get_subpath (ellipse, z2 - z3, z1 - z2, z2) fi -- cycle; % The stem is intentionally outside of the char box. if direction > 0: set_char_box (0, wd#, ht# / 2, stem_ht#); else: set_char_box (0, wd#, stem_ht#, ht# / 2); fi; labels (1, 2, 3); fet_endchar; enddef; % custos hufnagel, stem up, between staff lines custos_hufnagel ("Custos Hufnagel", "hufnagel.u0", dir_up, between_staff_lines); % custos hufnagel, stem up, on staff line custos_hufnagel ("Custos Hufnagel", "hufnagel.u1", dir_up, on_staff_line); % custos hufnagel, stem up, anywhere custos_hufnagel ("Custos Hufnagel", "hufnagel.u2", dir_up, anywhere); % custos hufnagel, stem down, between staff lines custos_hufnagel ("Reverse Custos Hufnagel", "hufnagel.d0", dir_down, between_staff_lines); % custos hufnagel, stem down, on staff line custos_hufnagel ("Reverse Custos Hufnagel", "hufnagel.d1", dir_down, on_staff_line); % custos hufnagel, stem down, anywhere custos_hufnagel ("Reverse Custos Hufnagel", "hufnagel.d2", dir_down, anywhere); %%%%%%%% % % % Medicaea style % % def custos_medicaea (expr verbose_name, internal_name, direction, staffline_adjustment) = fet_beginchar (verbose_name, internal_name); save ht, wd, stem_ht; ht# = noteheight#; wd# / ht# = 0.25; if staffline_adjustment = between_staff_lines: stem_ht# = 1.00 staff_space#; elseif staffline_adjustment = on_staff_line: stem_ht# = 1.50 staff_space#; else: % staffline_adjustment = anywhere stem_ht# = 1.25 staff_space#; fi; define_pixels (ht, wd, stem_ht); save ellipse, T; path ellipse; transform T; T := identity xscaled 0.6 linethickness yscaled ht; pickup pencircle transformed T; ellipse := fullcircle transformed T; lft x1 = 0; y1 = 0; rt x2 = wd; y2 = y1; fill get_subpath (ellipse, left, right, z1) -- get_subpath (ellipse, right, left, z2) -- cycle; pickup pencircle scaled 0.6 linethickness; rt x3 = wd; y3 = 0; x4 = x3; if direction > 0: top y4 = stem_ht; draw_rounded_block (bot lft z3, top rt z4, 0.6 linethickness); else: bot y4 = -stem_ht; draw_rounded_block (bot lft z4, top rt z3, 0.6 linethickness); fi; if direction > 0: set_char_box (0, wd#, ht# / 2, stem_ht#); else: set_char_box (0, wd#, stem_ht#, ht# / 2); fi; labels (1, 2, 3, 4); fet_endchar; enddef; % custos medicaea, stem up, between staff lines custos_medicaea ("Custos Med.", "medicaea.u0", dir_up, between_staff_lines); % custos medicaea, stem up, on staff line custos_medicaea ("Custos Med.", "medicaea.u1", dir_up, on_staff_line); % custos medicaea, stem up, anywhere custos_medicaea ("Custos Med.", "medicaea.u2", dir_up, anywhere); % custos medicaea, stem down, between staff lines custos_medicaea ("Reverse Custos Med.", "medicaea.d0", dir_down, between_staff_lines); % custos medicaea, stem down, on staff line custos_medicaea ("Reverse Custos Med.", "medicaea.d1", dir_down, on_staff_line); % custos medicaea, stem down, anywhere custos_medicaea ("Reverse Custos Med.", "medicaea.d2", dir_down, anywhere); %%%%%%%% % % % Editio Vaticana style % % def custos_vaticana (expr verbose_name, internal_name, direction, staffline_adjustment) = fet_beginchar (verbose_name, internal_name); save ht, wd, u_offs, l_offs, stem_size, stem_end; save pen_ht, l_shift, curve_ht, bend_ht; ht# = noteheight#; wd# = 0.24 ht#; if staffline_adjustment = between_staff_lines: stem_size# = 1.00; elseif staffline_adjustment = on_staff_line: stem_size# = 1.50; else: % staffline_adjustment = anywhere stem_size# = 1.25; fi; curve_ht# = 0.6 ht#; bend_ht# = 0.10 ht#; l_shift# = 0.04 ht#; u_offs# = +direction * 0.5 * (bend_ht# + l_shift#); l_offs# = -direction * 0.5 * (bend_ht# - l_shift#); stem_end# = direction * stem_size# * staff_space#; pen_ht# = curve_ht# - l_shift#; define_pixels (u_offs, l_offs, stem_end, ht, wd, pen_ht); pickup pencircle scaled 0.6 linethickness; z1 = (0, u_offs); z2 = (0.7 wd, l_offs); z3 = (wd, l_offs); penpos1 (pen_ht, 90); penpos2 (pen_ht, 90); penpos3 (pen_ht, 90); penstroke z1e{z2 - z1} .. {right}z2e .. z3e; rt x4 = wd; x5 = x4; if direction > 0: y4 = y3r; top y5 = stem_end; draw_rounded_block (bot lft z4, top rt z5, 0.6 linethickness); else: y4 = y3l; bot y5 = stem_end; draw_rounded_block (bot lft z5, top rt z4, 0.6 linethickness); fi; if direction > 0: set_char_box (0, wd#, -l_offs# + 0.5 pen_ht#, stem_end#); else: set_char_box (0, wd#, -stem_end#, +l_offs# + 0.5 pen_ht#); fi; penlabels (1, 2, 3); labels (4, 5); fet_endchar; enddef; % custos vaticana, stem up, between staff lines custos_vaticana ("Custos Ed. Vat.", "vaticana.u0", dir_up, between_staff_lines); % custos vaticana, stem up, on staff line custos_vaticana ("Custos Ed. Vat.", "vaticana.u1", dir_up, on_staff_line); % custos vaticana, stem up, anywhere custos_vaticana ("Custos Ed. Vat.", "vaticana.u2", dir_up, anywhere); % custos vaticana, stem down, between staff lines custos_vaticana ("Reverse Custos Ed. Vat.", "vaticana.d0", dir_down, between_staff_lines); % custos vaticana, stem down, on_staff_line custos_vaticana ("Reverse Custos Ed. Vat.", "vaticana.d1", dir_down, on_staff_line); % custos vaticana, stem down, anywhere custos_vaticana ("Reverse Custos Ed. Vat.", "vaticana.d2", dir_down, anywhere); %%%%%%%% % % % Mensural style % % def custos_mensural (expr verbose_name, internal_name, direction, staffline_adjustment) = fet_beginchar (verbose_name, internal_name); save alpha, dalpha, ht, wd, stem_ht; ht# = noteheight#; wd# / ht# = 1.2; alpha = 35; dalpha = direction * alpha; if staffline_adjustment = between_staff_lines: stem_ht# = 1.00 staff_space#; elseif staffline_adjustment = on_staff_line: stem_ht# = 1.50 staff_space#; else: % staffline_adjustment = anywhere stem_ht# = 1.25 staff_space#; fi; define_pixels (ht, wd, stem_ht); save ellipse, T; path ellipse; transform T; T := identity xscaled linethickness yscaled 0.4 ht rotated -dalpha; pickup pencircle transformed T; ellipse := fullcircle transformed T; if direction > 0: bot y1 = bot y3 = bot y5 = -direction * 0.33 ht; top y2 = top y4 = +direction * 0.33 ht; else: top y1 = top y3 = top y5 = -direction * 0.33 ht; bot y2 = bot y4 = +direction * 0.33 ht; fi; lft x1 = 0.0 wd; lft x2 = 0.2 wd; lft x3 = 0.4 wd; lft x4 = 0.6 wd; lft x5 = 0.8 wd; y6 - y5 = direction * stem_ht; y6 - y5 = (x6 - x5) * tand (90 - dalpha); if direction > 0: fill get_subpath (ellipse, z1 - z2, z2 - z1, z1) -- get_subpoint (ellipse, z2 - z1, z2) -- get_subpoint (ellipse, z3 - z2, z2) -- get_subpath (ellipse, z3 - z2, z4 - z3, z3) -- get_subpoint (ellipse, z4 - z3, z4) -- get_subpoint (ellipse, z5 - z4, z4) -- get_subpath (ellipse, z5 - z4, z6 - z5, z5) -- get_subpath (ellipse, z6 - z5, z5 - z6, z6) -- get_subpoint (ellipse, z5 - z6, z5) -- get_subpoint (ellipse, z4 - z5, z5) -- get_subpath (ellipse, z4 - z5, z3 - z4, z4) -- get_subpoint (ellipse, z3 - z4, z3) -- get_subpoint (ellipse, z2 - z3, z3) -- get_subpath (ellipse, z2 - z3, z1 - z2, z2) -- cycle; else: fill get_subpath (ellipse, z1 - z2, z2 - z1, z1) -- get_subpath (ellipse, z2 -z1, z3 - z2, z2) -- get_subpoint (ellipse, z3 - z2, z3) -- get_subpoint (ellipse, z4 - z3, z3) -- get_subpath (ellipse, z4 -z3, z5 - z4, z4) -- get_subpoint (ellipse, z5 - z4, z5) -- get_subpoint (ellipse, z6 - z5, z5) -- get_subpath (ellipse, z6 - z5, z5 - z6, z6) -- get_subpath (ellipse, z5 - z6, z4 - z5, z5) -- get_subpoint (ellipse, z4 - z5, z4) -- get_subpoint (ellipse, z3 - z4, z4) -- get_subpath (ellipse, z3 - z4, z2 - z3, z3) -- get_subpoint (ellipse, z2 - z3, z2) -- get_subpoint (ellipse, z1 - z2, z2) -- cycle; fi; % The stem is intentionally outside of the char box. if direction > 0: set_char_box (0, wd#, +direction * 0.33 ht#, stem_ht#); else: set_char_box (0, wd#, stem_ht#, -direction * 0.33 ht#); fi; labels (1, 2, 3, 4, 5, 6); fet_endchar; enddef; % custos mensural, stem up, between staff lines custos_mensural ("Custos Mensural", "mensural.u0", dir_up, between_staff_lines); % custos mensural, stem up, on staff line custos_mensural ("Custos Mensural", "mensural.u1", dir_up, on_staff_line); % custos mensural, stem up, anywhere custos_mensural ("Custos Mensural", "mensural.u2", dir_up, anywhere); % custos mensural, stem down, between staff lines custos_mensural ("Reverse Custos Mensural", "mensural.d0", dir_down, between_staff_lines); % custos mensural, stem down, on staff line custos_mensural ("Reverse Custos Mensural", "mensural.d1", dir_down, on_staff_line); % custos mensural, stem down, anywhere custos_mensural ("Reverse Custos Mensural", "mensural.d2", dir_down, anywhere); fet_endgroup ("custodes"); muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-dots.mf000066400000000000000000000010561404105311000245300ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-dots.mf -- dot vaticana % % source file of LilyPond's pretty-but-neat music font % % (c) 2006--2009 Juergen Reuter % fet_begingroup ("dots"); save dot_diam; 3 dot_diam# = staff_space# - stafflinethickness#; define_whole_blacker_pixels (dot_diam); fet_beginchar ("duration dot", "dotvaticana"); pickup pencircle scaled dot_diam; lft x0 = 0; top y0 = vround (.5 dot_diam); drawdot z0; set_char_box (0, dot_diam#, .5 dot_diam#, .5 dot_diam#); fet_endchar; fet_endgroup ("dots"); muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-flags.mf000066400000000000000000000210021404105311000246440ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-flags.mf -- implement ancient flags % % source file of LilyPond's pretty-but-neat music font % % (c) 2001--2009 Juergen Reuter % fet_begingroup ("flags"); save between_staff_lines, on_staff_line, anywhere; between_staff_lines := 0; on_staff_line := 1; anywhere := 2; save dir_up, dir_down; dir_up := 1; dir_down := -1; %%%%%%%% % % % % Mensural Notation % % % def draw_mensural_outermost_flare (expr staffline_adjustment, d_) = define_pixels (linethickness, staff_space); save ellipse, pat, T; path ellipse, pat; transform T; T := identity xscaled 1.00 linethickness yscaled 0.22 staff_space rotated -35; pickup pencircle transformed T; ellipse := fullcircle transformed T; z11 = (+0.00 staff_space, -0.00 staff_space); z12 = (+0.15 staff_space, -0.00 staff_space); if staffline_adjustment = between_staff_lines: z13 = (+0.45 staff_space, -0.35 staff_space); z14 = (+0.45 staff_space, -0.85 staff_space); z15 = (+0.00 staff_space, -2.00 staff_space); elseif staffline_adjustment = on_staff_line: z13 = (+0.20 staff_space, -0.05 staff_space); z14 = (+0.20 staff_space, -1.15 staff_space); z15 = (+0.00 staff_space, -1.40 staff_space); else: % staffline_adjustment = anywhere z13 = (+0.33 staff_space, -0.20 staff_space); z14 = (+0.33 staff_space, -1.00 staff_space); z15 = (+0.00 staff_space, -1.70 staff_space); fi; pat := z13{z13 - z12} .. {z15 - z14}z14; fill get_subpath (ellipse, z11 - z12, z12 - z11, z11) -- get_subpoint (ellipse, z12 - z11, z12) -- get_subpoint (ellipse, z13 - z12, z12) -- get_subpoint (ellipse, direction 0 of pat, z13) {direction 0 of pat} .. {direction 1 of pat} get_subpoint (ellipse, direction 1 of pat, z14) -- get_subpath (ellipse, z15 - z14, z14 - z15, z15) -- get_subpoint (ellipse, -direction 1 of pat, z14) {-direction 1 of pat} .. {-direction 0 of pat} get_subpoint (ellipse, -direction 0 of pat, z13) -- get_subpath (ellipse, z12 - z13, z11 - z12, z12) -- cycle; if d_ = dir_up: labels (11, 12, 13, 14, 15); fi; enddef; def draw_mensural_inner_flare (expr il_shift, idx, d_) = define_pixels (linethickness, staff_space); save ellipse, pat, T; path ellipse, pat; transform T; T := identity xscaled 1.00 linethickness yscaled 0.22 staff_space rotated -35; pickup pencircle transformed T; ellipse := fullcircle transformed T; save i; numeric i[]; i0 := idx * 10; i1 := idx * 10 + 1; i2 := idx * 10 + 2; i3 := idx * 10 + 3; i4 := idx * 10 + 4; z[i0] = (0, -il_shift * staff_space); z[i1] = z[i0] + (+0.00 staff_space, -0.10 staff_space); z[i2] = z[i0] + (+0.33 staff_space, -0.30 staff_space); z[i3] = z[i0] + (+0.33 staff_space, -0.70 staff_space); z[i4] = z[i0] + (+0.00 staff_space, -0.90 staff_space); pat := z[i1]{2, 1} .. z[i2] .. z[i3] .. {-2, -1}z[i4]; % we avoid cusps originally present in `draw pat' fill get_subpath (ellipse, -direction 0 of pat, direction 0 of pat, z[i1]) .. get_subpoint (ellipse, direction 1 of pat, z[i2]) {direction 1 of pat} .. get_subpoint (ellipse, direction 2 of pat, z[i3]) {direction 2 of pat} .. get_subpath (ellipse, direction 3 of pat, -direction 3 of pat, z[i4]) .. get_subpoint (ellipse, -direction 2 of pat, z[i3]) {-direction 2 of pat} .. get_subpoint (ellipse, -direction 1 of pat, z[i2]) {-direction 1 of pat} .. cycle; if d_ = dir_up: labels ([i0], [i1], [i2], [i3], [i4]); fi; enddef; def draw_mensural_flag (expr staffline_adjustment, flares, d_) = save char_box_adjust, flare_shift; if staffline_adjustment = between_staff_lines: flare_shift := 0.5; elseif staffline_adjustment = on_staff_line: flare_shift := 0.0; else: % staffline_adjustment = anywhere flare_shift := 0.25; fi; char_box_adjust := flare_shift + 0.5; if d_ = dir_up: set_char_box (0, 0.60 staff_space#, (flares + char_box_adjust) * staff_space#, 0.10 staff_space#); else: % d_ = dir_down set_char_box (0.60 staff_space#, 0, 0.10 staff_space#, (flares + char_box_adjust) * staff_space#); fi; draw_mensural_outermost_flare (staffline_adjustment, d_); for flare_count := 2 step 1 until 4: if flares >= flare_count: draw_mensural_inner_flare (flare_shift + flare_count - 0.5, flare_count, d_); fi; endfor; if d_ = dir_down: currentpicture := currentpicture xscaled -1 yscaled -1; fi; enddef; % 8th mensural flag, upwards, between staff lines fet_beginchar ("8th Mensural Flag (up)", "mensuralu03"); draw_mensural_flag (between_staff_lines, 1, dir_up); fet_endchar; % 8th mensural flag, upwards, on staff line fet_beginchar ("8th Mensural Flag (up)", "mensuralu13"); draw_mensural_flag (on_staff_line, 1, dir_up); fet_endchar; % 8th mensural flag, upwards, anywhere fet_beginchar ("8th Mensural Flag (up)", "mensuralu23"); draw_mensural_flag (anywhere, 1, dir_up); fet_endchar; % 8th mensural flag, downwards, between staff lines fet_beginchar ("8th Mensural Flag (down)", "mensurald03"); draw_mensural_flag (between_staff_lines, 1, dir_down); fet_endchar; % 8th mensural flag, downwards, on staff line fet_beginchar ("8th Mensural Flag (down)", "mensurald13"); draw_mensural_flag (on_staff_line, 1, dir_down); fet_endchar; % 8th mensural flag, downwards, anywhere fet_beginchar ("8th Mensural Flag (down)", "mensurald23"); draw_mensural_flag (anywhere, 1, dir_down); fet_endchar; % 16th mensural flag, upwards, between staff lines fet_beginchar ("16th Mensural Flag (up)", "mensuralu04"); draw_mensural_flag (between_staff_lines, 2, dir_up); fet_endchar; % 16th mensural flag, upwards, on staff line fet_beginchar ("16th Mensural Flag (up)", "mensuralu14"); draw_mensural_flag (on_staff_line, 2, dir_up); fet_endchar; % 16th mensural flag, upwards, anywhere fet_beginchar ("16th Mensural Flag (up)", "mensuralu24"); draw_mensural_flag (anywhere, 2, dir_up); fet_endchar; % 16th mensural flag, downwards, between staff lines fet_beginchar ("16th Mensural Flag (down)", "mensurald04"); draw_mensural_flag (between_staff_lines, 2, dir_down); fet_endchar; % 16th mensural flag, downwards, on staff line fet_beginchar ("16th Mensural Flag (down)", "mensurald14"); draw_mensural_flag (on_staff_line, 2, dir_down); fet_endchar; % 16th mensural flag, downwards, anywhere fet_beginchar ("16th Mensural Flag (down)", "mensurald24"); draw_mensural_flag (anywhere, 2, dir_down); fet_endchar; % 32th mensural flag, upwards, between staff lines fet_beginchar ("32th Mensural Flag (up)", "mensuralu05"); draw_mensural_flag (between_staff_lines, 3, dir_up); fet_endchar; % 32th mensural flag, upwards, on staff line fet_beginchar ("32th Mensural Flag (up)", "mensuralu15"); draw_mensural_flag (on_staff_line, 3, dir_up); fet_endchar; % 32th mensural flag, upwards, anywhere fet_beginchar ("32th Mensural Flag (up)", "mensuralu25"); draw_mensural_flag (anywhere, 3, dir_up); fet_endchar; % 32th mensural flag, downwards, between staff lines fet_beginchar ("32th Mensural Flag (down)", "mensurald05"); draw_mensural_flag (between_staff_lines, 3, dir_down); fet_endchar; % 32th mensural flag, downwards, on staff line fet_beginchar ("32th Mensural Flag (down)", "mensurald15"); draw_mensural_flag (on_staff_line, 3, dir_down); fet_endchar; % 32th mensural flag, downwards, anywhere fet_beginchar ("32th Mensural Flag (down)", "mensurald25"); draw_mensural_flag (anywhere, 3, dir_down); fet_endchar; % 64th mensural flag, upwards, between staff lines fet_beginchar ("64th Mensural Flag (up)", "mensuralu06"); draw_mensural_flag (between_staff_lines, 4, dir_up); fet_endchar; % 64th mensural flag, upwards, on staff line fet_beginchar ("64th Mensural Flag (up)", "mensuralu16"); draw_mensural_flag (on_staff_line, 4, dir_up); fet_endchar; % 64th mensural flag, upwards, anywhere fet_beginchar ("64th Mensural Flag (up)", "mensuralu26"); draw_mensural_flag (anywhere, 4, dir_up); fet_endchar; % 64th mensural flag, downwards, between staff lines fet_beginchar ("64th Mensural Flag (down)", "mensurald06"); draw_mensural_flag (between_staff_lines, 4, dir_down); fet_endchar; % 64th mensural flag, downwards, on staff line fet_beginchar ("64th Mensural Flag (down)", "mensurald16"); draw_mensural_flag (on_staff_line, 4, dir_down); fet_endchar; % 64th mensural flag, downwards, anywhere fet_beginchar ("64th Mensural Flag (down)", "mensurald26"); draw_mensural_flag (anywhere, 4, dir_down); fet_endchar; fet_endgroup ("flags"); muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-generic.mf000066400000000000000000000013601404105311000251710ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-generic.mf -- implement generic stuff: include lots of files, % but don't set dimensions. % % source file of LilyPond's pretty-but-neat music font % % (c) 2002--2009 Juergen Reuter % if test = -1: mode := smoke; fi; mode_setup; staffsize# := design_size * pt#; input feta-macros; input feta-params; input parmesan-macros; font_x_height staff_space#; fet_beginfont ("parmesan", design_size, "parmesanMusic"); if test = 0: input parmesan-rests; input parmesan-heads; input parmesan-clefs; input parmesan-custodes input parmesan-accidentals; input parmesan-flags; input parmesan-timesig; input parmesan-scripts; input parmesan-dots; else: fi; fet_endfont; muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-heads.mf000066400000000000000000001017211404105311000246430ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-heads.mf -- implement ancient note heads % % source file of LilyPond's pretty-but-neat music font % % (c) 2001--2009 Juergen Reuter % % Neo-mensural heads originally by % Christian Mondrup and Mats Bengtsson % save black_notehead_width; numeric black_notehead_width; fet_begingroup ("noteheads"); % % character aligment: % % The head is assumed to be vertically centered around (0, 0). % The left-most edge of the head should touch the vertical line % that goes though the point (0, 0). % % set_char_box() conventions: % % * breapth: Ignored (as far as I know). Should be set to 0. % % * width: Should match the head's width. % % * depth: Should match the bottom edge of the head. Affects vertical % collision handling. % % * height: Should match the top edge of the head. Affects vertical % collision handling. % % TODO: should depth/height include appendages/stems? overdone_heads = 0; noteheight# := staff_space# + (1 + overdone_heads) * stafflinethickness#; define_pixels (noteheight); %%%%%%%% % % % % MENSURAL NOTATION % % % def draw_neomensural_brevis (expr brevwid) = save beamheight, head_width; save holeheight, stem_width; save serif_size, serif_protrude; head_width# = brevwid; holeheight = 3 stafflinethickness; stem_width = 1.4 stafflinethickness; define_pixels (head_width); set_char_box (0, head_width#, noteheight# / 2, noteheight# / 2); 2 beamheight + holeheight = noteheight; serif_size = (holeheight - stafflinethickness) / 2; serif_protrude = 1.5 serif_size; z1l = (0, 0); z2l = (0, -stafflinethickness / 2); z3r = z2r + serif_size * (1, -1); y4r = y3r; x4r = head_width / 2; z5l = z3l + (-serif_size, -serif_protrude); penpos1 (stem_width, 0); penpos2 (stem_width, 0); penpos3 (beamheight, 90); penpos4 (beamheight, 90); penpos5 (stem_width, 180); save pat_in, pat_out; path pat_in, pat_out; pat_out := z4l -- z3l{left} .. z5l{down} .. z5r{up} -- z1l; pat_out := pat_out -- reverse pat_out yscaled -1; pat_out := pat_out -- reverse pat_out shifted (-x4r, 0) xscaled -1 shifted (x4l, 0) -- cycle; fill pat_out; pat_in := z4r -- z3r{left} .. z2r{up} -- z1r; pat_in := pat_in -- reverse pat_in yscaled -1; pat_in := pat_in -- reverse pat_in shifted (-x4r, 0) xscaled -1 shifted (x4l, 0) -- cycle; unfill pat_in; penlabels (1, 2, 3, 4, 5); enddef; %%% This head does not seem to be used anywhere. Junk me? -- jr def draw_neomensural_left_stemmed_head (expr wid) = draw_neomensural_brevis (wid); x6 = x7 = stem_width / 2; y6 = y5; y7 = y5 - 2.25 staff_space; z17 = (x7, y7 - stem_width / 2); penpos6 (stem_width, 0); penpos7 (stem_width, 0); fill z7l -- z6l -- z6r -- z7r .. z17 .. cycle; penlabels (6, 7); labels (17); enddef; %%% This head does not seem to be used anywhere. Junk me? -- jr fet_beginchar ("Left stemmed notehead", "slneomensural"); draw_neomensural_left_stemmed_head (2 staff_space#); fet_endchar; % % Some sources (eg. Musix/OpusTeX) think that the appendage should be on % the left, some say right. Right wins democratically. % def draw_neomensural_longa (expr wid) = draw_neomensural_brevis (wid); save theta; x7r = head_width; y7 = y5; z6 - z7 = (stem_width / 2, -staff_space); theta = angle (z6 - z7) + 90; penpos7 (stem_width, 0); penpos6 (1.2 stem_width, theta); z7' = find_tangent (z6l, pat_out, (x7l + 0.5 stem_width, y7l), (x7l - 0.5 stem_width, y7l)); fill z7r .. z6r{z6 - z7} .. {z7 - z6}z6l -- z7' -- cycle; penlabels (6, 7); labels (7'); enddef; % % En wij presenteren U: de opvolgster van Emily % % (ze is wel breed) % fet_beginchar ("Neo-mensural maxima notehead", "sM3neomensural"); draw_neomensural_longa (2.6 staff_space#); fet_endchar; fet_beginchar ("Neo-mensural longa notehead", "sM2neomensural"); draw_neomensural_longa (2 staff_space#); fet_endchar; fet_beginchar ("Neo-mensural brevis notehead", "sM1neomensural"); draw_neomensural_brevis (2 staff_space#); fet_endchar; def draw_neomensural_black_head (expr wid, height) = save head_width; save ne, nw, ne_dist, nw_dist; pair ne, nw, ne_dist, nw_dist; head_width# = wid; set_char_box (0, head_width#, height / 2, height / 2); charwx := head_width# / 2; charwy := height / 2; y3 = y1 = 0; x2 = x4 = (x1 + x3) / 2; pickup pencircle scaled blot_diameter; top y2 = h; bot y4 = -d; lft x1 = 0; rt x3 = w; ne := unitvector (z2 - z1); nw_dist := (ne rotated 90) * 0.5 blot_diameter; nw := unitvector (z2 - z3); ne_dist := (nw rotated -90) * 0.5 blot_diameter; fill lft z1{up} .. (z1 + nw_dist){ne} -- (z2 + nw_dist){ne} .. top z2{right} .. (z2 + ne_dist){-nw} -- (z3 + ne_dist){-nw} .. rt z3{down} .. (z3 - nw_dist){-ne} -- (z4 - nw_dist){-ne} .. bot z4{left} .. (z4 - ne_dist){nw} -- (z1 - ne_dist){nw} .. cycle; labels (1, 2, 3, 4); enddef; def draw_neomensural_open_head (expr wid, height)= draw_neomensural_black_head (wid, height); save diamNW, diamSW; diamNW = length (z2 - z1) + blot_diameter; diamSW = length (z4 - z1) + blot_diameter; save hole_widthNW, hole_widthSW; hole_widthNW = 0.34 diamNW ; hole_widthSW + 2.6 linethickness = diamSW; (z7 + z5) / 2 = (w / 2, 0); (z8 + z6) / 2 = (w / 2, 0); z6 - z5 = hole_widthNW * unitvector (z2 - z1); z7 - z6 = hole_widthSW * unitvector (z4 - z1); unfill z5 -- z6 -- z7 -- z8 -- cycle; labels (5, 6, 7, 8); enddef; % % WL says the thin lines should be thinner. % fet_beginchar ("Harmonic notehead (Neo-mensural open)", "s0harmonic"); draw_neomensural_open_head (1.3 staff_space#, 1.3 noteheight#); charwx := head_width#; charwy := 0; fet_endchar; fet_beginchar ("Harmonic notehead (Neo-mensural black)", "s2harmonic"); draw_neomensural_black_head (1.3 staff_space#, 1.3 noteheight#); charwx := head_width#; charwy := 0; fet_endchar; fet_beginchar ("Neo-mensural semibrevis head", "s0neomensural"); draw_neomensural_open_head (staff_space#, noteheight#); fet_endchar; fet_beginchar ("Neo-mensural minima head", "s1neomensural"); draw_neomensural_open_head (staff_space#, noteheight#); fet_endchar; fet_beginchar ("Neo-mensural semiminima head", "s2neomensural"); draw_neomensural_black_head (staff_space#, noteheight#); fet_endchar; def draw_mensural_brevis (expr wid) = % TODO. For the moment, fall back to draw_neomensural_brevis. draw_neomensural_brevis (wid); enddef; %%% This head does not seem to be used anywhere. Junk me? -- jr def draw_mensural_left_stemmed_head (expr wid) = draw_mensural_brevis (wid); x6 = x7 = stem_width / 2; y6 = y5; y7 = y5 - 2.25 staff_space; z17 = (x7, y7 - stem_width / 2); penpos6 (stem_width, 0); penpos7 (stem_width, 0); fill z7l -- z6l -- z6r -- z7r .. z17 .. cycle; penlabels (6, 7); labels (17); enddef; def draw_mensural_longa (expr wid) = draw_mensural_brevis (wid); x6 = x7 = head_width - stem_width / 2; y6 = y5; y7 = y5 - 2.25 staff_space; z17 = (x7, y7 - stem_width / 2); penpos6 (stem_width, 0); penpos7 (stem_width, 0); fill z7l -- z6l -- z6r -- z7r .. z17 .. cycle; penlabels (6, 7); labels (17); enddef; %%% This head does not seem to be used anywhere. Junk me? -- jr fet_beginchar ("Mensural left stemmed notehead", "slmensural"); draw_mensural_left_stemmed_head (staff_space#); fet_endchar; fet_beginchar ("Mensural maxima notehead", "sM3mensural"); draw_mensural_longa (2.0 staff_space#); fet_endchar; fet_beginchar ("Mensural longa notehead", "sM2mensural"); draw_mensural_longa (staff_space#); fet_endchar; fet_beginchar ("Mensural brevis notehead", "sM1mensural"); draw_mensural_brevis (staff_space#); fet_endchar; def draw_diamond_head (expr head_h, pen_w, pen_h, angle, open) = save head_width, head_height; save ellipse, ellipse_r; path ellipse, ellipse_r, diamond_shape; head_height# = head_h; head_width# / head_height# = tand (angle); set_char_box (0, head_width#, head_height# / 2, head_height# / 2); charwx := head_width# / 2; charwy := head_height# / 2 - linethickness#; define_pixels (head_width, head_height); ellipse := reverse fullcircle xscaled (max (blot_diameter, pen_w * head_width)) yscaled (max (blot_diameter, pen_h * head_width)) rotated -angle; z1 = find_tangent_shift (((0, h) -- (0, -h)), ellipse, (0, 0), (w / 2, 0)); z2 = find_tangent_shift (((0, h) -- (w, h)), ellipse, (w / 2, h), (w / 2, 0)); z3 = find_tangent_shift (((w, h) -- (w, -h)), ellipse, (w, 0), (w / 2, 0)); z4 = find_tangent_shift (((0, -h) -- (w, -h)), ellipse, (w / 2, -h), (w / 2, 0)); diamond_shape := get_subpath (ellipse, z1 - z4, z2 - z1, z1) -- get_subpath (ellipse, z2 - z1, z3 - z2, z2) -- get_subpath (ellipse, z3 - z2, z4 - z3, z3) -- get_subpath (ellipse, z4 - z3, z1 - z4, z4) -- cycle; fill diamond_shape; if open: save l; path l[]; l12 := (directionpoint (z1 - z2) of ellipse) shifted z1 -- (directionpoint (z1 - z2) of ellipse) shifted z2; l23 := (directionpoint (z2 - z3) of ellipse) shifted z2 -- (directionpoint (z2 - z3) of ellipse) shifted z3; l34 := (directionpoint (z3 - z4) of ellipse) shifted z3 -- (directionpoint (z3 - z4) of ellipse) shifted z4; l41 := (directionpoint (z4 - z1) of ellipse) shifted z4 -- (directionpoint (z4 - z1) of ellipse) shifted z1; unfill l12 intersectionpoint l23 -- l23 intersectionpoint l34 -- l34 intersectionpoint l41 -- l41 intersectionpoint l12 -- cycle; fi; labels (1, 2, 3, 4); enddef; fet_beginchar ("Mensural semibrevis head", "s0mensural"); draw_diamond_head (staff_space#, 0.15, 0.30, 30, true); fet_endchar; fet_beginchar ("Mensural minima head", "s1mensural"); draw_diamond_head (staff_space#, 0.15, 0.30, 30, true); fet_endchar; fet_beginchar ("Mensural semiminima head", "s2mensural"); draw_diamond_head (staff_space#, 0.15, 0.30, 30, false); fet_endchar; fet_beginchar ("Petrucci semibrevis head", "s0petrucci"); % draw_diamond_head (1.8 staff_space#, 0.15, 0.40, 30, true); draw_neomensural_open_head (staff_space#, 1.8 staff_space#); fet_endchar; fet_beginchar ("Petrucci minima head", "s1petrucci"); % draw_diamond_head (1.8 staff_space#, 0.15, 0.40, 30, true); draw_neomensural_open_head (staff_space#, 1.8 staff_space#); fet_endchar; fet_beginchar ("Petrucci semiminima head", "s2petrucci"); % draw_diamond_head (1.8 staff_space#, 0.15, 0.40, 30, false); draw_neomensural_black_head (staff_space#, 1.8 staff_space#); fet_endchar; %%%%%%%% % % % % EDITIO VATICANA (including solesmes extensions) % % % def vat_punctum_char (expr verbose_name, internal_name, linea, cavum, straight, auctum, d_up, up_shift, down_shift, mag) = fet_beginchar (verbose_name, "s" & internal_name); save a_b, b_h, a_w; a_b := 1.54; % b_h * a_b / a_w = wd / ht b_h := 0.85; a_w := 1.09; save a, beta, ht, wd; ht# = noteheight# * mag; 2 beta = ht# * b_h; a = beta * a_b; wd# = 2 a / a_w; black_notehead_width# := wd#; % direction save d_, d_sign; pair d_; if d_up: d_ := up; d_sign := 1; else: d_ := down; d_sign := -1; fi; % convexity and eccentricity save u_convexity, u_eccentricity; if straight: u_convexity# := -0.01 ht#; u_eccentricity# := 0.0 ht#; % dummy elseif auctum: u_convexity# := -0.03 ht#; u_eccentricity# := +0.25 ht#; else: u_convexity# := -0.05 ht#; u_eccentricity# := 0.0 ht#; % dummy fi; save convexity, eccentricity; convexity# := d_sign * u_convexity#; eccentricity# := d_sign * u_eccentricity#; % y shift offset save yoffs; if up_shift: yoffs# := 0.08 ht#; elseif down_shift: yoffs# := -0.11 ht#; else: yoffs# := 0.00 ht#; fi; define_pixels (convexity, eccentricity, yoffs, ht, wd); pickup pencircle scaled linethickness; save height, yoffs_bt, p, circle, circle_r; path p, circle, circle_r; height# = 0.47 ht#; yoffs_bt# = yoffs# - 0.5 height# - 0.25 convexity#; define_pixels (height, yoffs_bt); circle := fullcircle scaled linethickness; x1 = x6; x2 = x5; x3 = x4; y1 + height = y6; y2 + height = y5; y3 + height = y4; save box_top, box_bt; if auctum: z1 = (0.00 wd + linethickness / 2, yoffs_bt); z2 = (0.21 wd, yoffs_bt + convexity); z3 = (0.42 wd - linethickness/ 2, yoffs_bt + eccentricity); box_top# = height# + yoffs_bt# + max (0, convexity#, eccentricity#); box_bt# = yoffs_bt# + min (0, convexity#, eccentricity#); p = z1 .. {right}z2 .. {d_}z3 -- z4{-d_} .. z5{left} .. z6 -- cycle; else: z1 = (0.00 wd + linethickness / 2, yoffs_bt); z2 = (0.21 wd, yoffs_bt + convexity); z3 = (0.42 wd - linethickness / 2, yoffs_bt); box_top# = height# + yoffs_bt# + max (0, convexity#); box_bt# = yoffs_bt# + min (0, convexity#); p = z1 .. z2 .. z3 -- z4 .. z5 .. z6 -- cycle; fi; labels (1, 2, 3, 4, 5, 6); save dirs; pair dirs[]; dirs12 := direction (0 + epsilon) of p; dirs2 := direction 1 of p; dirs32 := direction (2 - epsilon) of p; dirs45 := direction (3 + epsilon) of p; dirs5 := direction 4 of p; dirs65 := direction (5 - epsilon) of p; fill get_subpath (circle, down, dirs12, z1) .. (bot z2){dirs2} .. get_subpath (circle, dirs32, up, z3) -- get_subpath (circle, up, dirs45, z4) .. (top z5){dirs5} .. get_subpath (circle, dirs65, down, z6) -- cycle; if cavum: save pat, t; path pat[]; numeric t[]; pat123 := ((directionpoint -dirs12 of circle) shifted z1){dirs12} .. (top z2){dirs2} .. {dirs32}((directionpoint -dirs32 of circle) shifted z3); pat34 := lft z3 -- lft z4; pat456 := ((directionpoint -dirs45 of circle) shifted z4){dirs45} .. (bot z5){dirs5} .. {dirs65}((directionpoint -dirs65 of circle) shifted z6); pat61 := rt z6 -- rt z1; t61 := ypart (pat61 intersectiontimes pat123); t12 := xpart (pat123 intersectiontimes pat34); t34 := ypart (pat34 intersectiontimes pat456); t45 := xpart (pat456 intersectiontimes pat61); unfill subpath (t61, t12) of pat123 -- subpath (t34, t45) of pat456 -- cycle; fi; set_char_box (0.00 wd#, 0.42 wd#, max (0, -box_bt#) + linethickness# / 2, max (0, box_top#) + linethickness# / 2); if linea: save linea_width, linea_height; linea_width# = 0.6 linethickness#; linea_height# = 0.7 ht#; define_pixels (linea_width, linea_height); pickup pencircle scaled 0.6 linethickness; draw_rounded_block ((-0.10 wd - linea_width / 2, -linea_height / 2), (-0.10 wd + linea_width / 2, +linea_height / 2), 0.6 linethickness); draw_rounded_block ((+0.52 wd - linea_width / 2, -linea_height / 2), (+0.52 wd + linea_width / 2, +linea_height / 2), 0.6 linethickness); set_char_box (0, 0.62 wd# + linea_width#, linea_height# / 2, linea_height# / 2); currentpicture := currentpicture shifted (0.10 wd + linea_width / 2, 0); fi; fet_endchar; enddef; def plica_char (expr verbose_name, internal_name, d_up, mag) = fet_beginchar (verbose_name, "s" & internal_name); save a_b, b_h, a_w; a_b := 1.54; % b_h * a_b / a_w = wd / ht b_h := 0.85; a_w := 1.09; save a, beta, ht, wd; ht# = noteheight# * mag; 2 beta = ht# * b_h; a = beta * a_b; wd# = 2 a / a_w; black_notehead_width# := wd#; % direction save d_, d_sign; pair d_; if d_up: d_ := up; d_sign := 1; else: d_ := down; d_sign := -1; fi; % convexity and eccentricity save convexity, eccentricity; convexity# := d_sign * -0.10 ht#; eccentricity# := d_sign * -0.12 ht#; % y shift offset save yoffs; yoffs# := -0.11 ht#; define_pixels (convexity, eccentricity, yoffs, ht, wd); pickup pencircle scaled linethickness; save height, yoffs_bt, p, circle, circle_r; path p, circle, circle_r; height# = 0.47 ht#; yoffs_bt# = yoffs# - 0.5 height# - 0.25 convexity#; define_pixels (height, yoffs_bt); circle := fullcircle scaled linethickness; x1 = x6; x2 = x5; x3 = x4; y1 + height = y6; y2 + height = y5; y3 + height = y4; save box_top, box_bt; z1 = (0.00 wd + linethickness / 2, yoffs_bt); z2 = (0.21 wd, yoffs_bt + convexity); z3 = (0.42 wd - linethickness/ 2, yoffs_bt + eccentricity); box_top# = height# + yoffs_bt# + max (0, convexity#, eccentricity#); box_bt# = yoffs_bt# + min (0, convexity#, eccentricity#); p = z1 .. z2{right} .. z3 -- z4 .. z5{left} .. z6 -- cycle; labels (1, 2, 3, 4, 5, 6); save dirs; pair dirs[]; dirs12 := direction (0 + epsilon) of p; dirs2 := direction 1 of p; dirs32 := direction (2 - epsilon) of p; dirs45 := direction (3 + epsilon) of p; dirs5 := direction 4 of p; dirs65 := direction (5 - epsilon) of p; fill get_subpath (circle, down, dirs12, z1) .. (bot z2){dirs2} .. get_subpath (circle, dirs32, up, z3) -- get_subpath (circle, up, dirs45, z4) .. (top z5){dirs5} .. get_subpath (circle, dirs65, down, z6) -- cycle; pickup pencircle scaled 0.6 linethickness; save stem_bt; set_char_box (0.00 wd#, 0.42 wd#, max (0, -box_bt#) + linethickness# / 2, max (0, box_top#) + linethickness# / 2); fet_endchar; enddef; def epiphonus_char (expr verbose_name, internal_name, left_stem, d_up, down_shift, mag) = fet_beginchar (verbose_name, "s" & internal_name); save a_b, b_h, a_w; a_b := 1.54; % b_h * a_b / a_w = wd / ht b_h := 0.85; a_w := 1.09; save a, beta, ht, wd; ht# = noteheight# * mag; 2 beta = ht# * b_h; a = beta * a_b; wd# = 2 a / a_w; black_notehead_width# := wd#; % direction save d_, d_sign; pair d_; if d_up: d_ := up; d_sign := 1; else: d_ := down; d_sign := -1; fi; % convexity and eccentricity save convexity; convexity# := d_sign * -0.05ht#; % y shift offset save yoffs; if down_shift: yoffs# := -0.11 ht#; else: yoffs# := 0.00 ht#; fi; define_pixels (convexity, yoffs, ht, wd); pickup pencircle scaled linethickness; save height, yoffs_bt, p, circle, circle_r; path p, circle, circle_r; height# = 0.47 ht#; yoffs_bt# = yoffs# - 0.5 height# - 0.25 convexity#; define_pixels (height, yoffs_bt); circle := fullcircle scaled linethickness; x1 = x6; x2 = x5; x3 = x4; y1 + height = y6; y2 + height = y5; y3 + height = y4; save box_top, box_bt; z1 = (0.00 wd + linethickness / 2, yoffs_bt - 2.5 convexity); z2 = (0.06 wd, yoffs_bt + 1.4 convexity); z3 = (0.42 wd - linethickness / 2, yoffs_bt - 1.0 convexity); box_top# = height# + yoffs_bt# + max (-1.0 convexity#, 1.4 convexity#, 0); box_bt# = yoffs_bt# + min (-1.0 convexity#, 1.4 convexity#, 0); p = z1{-d_} .. {curl 1}z2{right} .. z3 -- z4 .. {left}z5{curl 1} .. {d_}z6 -- cycle; labels (1, 2, 3, 4, 5, 6); save dirs; pair dirs[]; dirs12 := direction (0 + epsilon) of p; dirs21 := direction (1 - epsilon) of p; dirs23 := direction (1 + epsilon) of p; dirs32 := direction (2 - epsilon) of p; dirs45 := direction (3 + epsilon) of p; dirs54 := direction (4 - epsilon) of p; dirs56 := direction (4 + epsilon) of p; dirs65 := direction (5 - epsilon) of p; fill get_subpath (circle, down, dirs12, z1) .. get_subpath (circle, dirs21, dirs23, z2) .. get_subpath (circle, dirs32, up, z3) -- get_subpath (circle, up, dirs45, z4) .. get_subpath (circle, dirs54, dirs56, z5) .. get_subpath (circle, dirs65, down, z6) -- cycle; save stem_bt; if left_stem: pickup pencircle scaled 0.6 linethickness; lft x11 = x1 - linethickness / 2; bot y11 = yoffs - 1.1 ht - linethickness / 2; x12 = x11; y12 = y1; draw_rounded_block (bot lft z11, top rt z12, 0.6 linethickness); stem_bt# = yoffs# - 1.1 ht#; labels (11, 12); else: stem_bt# = 0; fi; set_char_box (0.00 wd#, 0.42 wd#, max (0, -box_bt#, -stem_bt#) + linethickness# / 2, max (0, box_top#) + linethickness# / 2); fet_endchar; enddef; def inclinatum_char (expr verbose_name, internal_name, small, stropha, auctum) = fet_beginchar (verbose_name, "s" & internal_name); save ht, alpha; alpha := 35; if small: ht# = 0.50 noteheight#; else: ht# = 0.80 noteheight#; fi; draw_diamond_head (ht#, 0, 0, alpha, false); save off_angle; off_angle := alpha + 15; save stropha_ellipse, auctum_hook, circle; path stropha_ellipse, auctum_hook, circle; circle := reverse fullcircle scaled linethickness; stropha_ellipse := fullcircle xscaled 0.25 head_height yscaled 0.55 head_height rotated alpha; z11 = z12 + linethickness / 2 * dir (180 - off_angle) - directionpoint dir (90 - off_angle) of stropha_ellipse; z12 = directionpoint -dir (90 - off_angle) of diamond_shape + linethickness / 2 * dir (180 - off_angle); z13 = (0, -0.5 head_height + linethickness); auctum_hook := z12{-dir (90 - off_angle)} .. {dir (90 + alpha)}z13; labels (12); if (stropha and not auctum): clearit; save t_in, t_out; t_in := xpart ((stropha_ellipse shifted z11) intersectiontimes get_subpath (diamond_shape, left, up, (0, 0))); t_out := xpart ((stropha_ellipse shifted z11) intersectiontimes get_subpath (diamond_shape, up, right, (0, 0))); % the addition or subtraction of `1' is necessary % so that we get the right starting point fill get_subpath_i (diamond_shape, dir (angle (z2 - z1) - 1), dir (angle (z1 - z4) + 1), (0, 0)) -- get_subpath (stropha_ellipse, direction t_in of stropha_ellipse, direction t_out of stropha_ellipse, z11) -- cycle; labels (11); fi; if (auctum and not stropha): clearit; fill get_subpath (diamond_shape, left, -dir (90 - off_angle), (0, 0)) .. get_subpath (circle, dir (90 + alpha), -dir (90 + alpha), z13) .. get_subpath (circle, dir (90 - off_angle), right, z12) -- cycle; labels (13); fi; if (auctum and stropha): clearit; save t; t := xpart ((stropha_ellipse shifted z11) intersectiontimes get_subpath (diamond_shape, up, right, (0, 0))); % the addition or subtraction of `1' is necessary % so that we get the right starting point fill get_subpath_i (diamond_shape, dir (angle (z2 - z1) - 1), -dir (90 - off_angle), (0, 0)) .. get_subpath (circle, dir (90 + alpha), -dir (90 + alpha), z13) .. get_subpath (stropha_ellipse, dir (90 - off_angle), direction t of stropha_ellipse, z11) -- cycle; labels (11, 13); fi; fet_endchar; enddef; % punctum vat_punctum_char ("Ed. Vat. punctum", "vaticana.punctum", false, false, false, false, false, false, false, 1.0); % punctum cavum (for OpusTeX compatibility) vat_punctum_char ("Ed. Vat. punctum cavum", "vaticana.punctum.cavum", false, true, false, false, false, false, false, 1.0); % linea punctum (for OpusTeX compatibility) vat_punctum_char ("Ed. Vat. linea punctum", "vaticana.linea.punctum", true, false, false, false, false, false, false, 1.0); % linea punctum cavum (for OpusTeX compatibility) vat_punctum_char ("Ed. Vat. linea punctum cavum", "vaticana.linea.punctum.cavum", true, true, false, false, false, false, false, 1.0); % punctum inclinatum inclinatum_char ("Ed. Vat. inclinatum", "vaticana.inclinatum", false, false, false); % pes lower punctum vat_punctum_char ("Ed. Vat. pes lower punctum", "vaticana.lpes", false, false, true, false, true, false, false, 1.0); % pes lower punctum vat_punctum_char ("Ed. Vat. pes var lower punctum", "vaticana.vlpes", false, false, true, false, true, false, true, 1.0); % pes upper punctum vat_punctum_char ("Ed. Vat. pes upper punctum", "vaticana.upes", false, false, true, false, false, false, false, 1.0); % pes upper punctum (shifted variation) % % This note head is used instead of the regular pes upper punctum to % avoid collision with the lower punctum note of the pes when the upper % punctum sits directly on top of the lower punctum. % vat_punctum_char ("Ed. Vat. var pes upper punctum", "vaticana.vupes", false, false, true, false, false, true, false, 1.0); % small punctum as used in epiphonus vat_punctum_char ("Ed. Vat. plica", "vaticana.plica", false, false, false, false, false, false, false, 0.6); % small punctum as used in epiphonus plica_char ("Ed. Vat. var plica", "vaticana.vplica", false, 0.6); % eccentric punctum as used in epiphonus epiphonus_char ("Ed. Vat. epiphonus", "vaticana.epiphonus", false, true, false, 1.0); % eccentric punctum as used in epiphonus (shifted variation) % % This note head is used instead of the regular epiphonus punctum to % avoid collision with the plica head when the plica sits directly on % top of the lower head. % epiphonus_char ("Ed. Vat. var epiphonus", "vaticana.vepiphonus", false, true, true, 1.0); % small punctum as used in cephalicus vat_punctum_char ("Ed. Vat. rev. plica", "vaticana.reverse.plica", false, false, false, false, true, false, false, 0.6); % small punctum as used in cephalicus plica_char ("Ed. Vat. rev. var plica", "vaticana.reverse.vplica", true, 0.6); % eccentric punctum as used in cephalicus; without left stem epiphonus_char ("Ed. Vat. inner cephalicus", "vaticana.inner.cephalicus", false, false, false, 1.0); % eccentric punctum as used in cephalicus; with left stem epiphonus_char ("Ed. Vat. cephalicus", "vaticana.cephalicus", true, false, false, 1.0); % quilisma fet_beginchar ("Ed. Vat. quilisma", "svaticana.quilisma"); save a_b, b_h, a_w; a_b := 1.54; % b_h * a_b / a_w = wd / ht b_h := 0.85; a_w := 1.09; save a, beta, ht, wd; ht# = noteheight#; 2 beta = ht# * b_h; a = beta * a_b; wd# = 2 a / a_w; set_char_box (0, 0.42 wd#, 0.28 ht#, 0.36 ht#); black_notehead_width# := wd#; define_pixels (ht, wd); save ellipse, T; path ellipse; transform T; T := identity xscaled linethickness yscaled 0.44 ht; pickup pencircle transformed T; ellipse := reverse fullcircle transformed T; z1 = (rt 0.00 wd, top -0.28 ht); z2 = (0.11 wd, -0.14 ht); z3 = (0.12 wd, +0.03 ht); z4 = (0.25 wd, -0.09 ht); z5 = (0.25 wd, +0.08 ht); z6 = (lft 0.42 wd, -0.04 ht); z7 = (lft 0.40 wd, bot +0.36 ht); fill get_subpath (ellipse, z1 - z2, z2 - z1, z1) -- get_subpath (ellipse, z2 - z1, z1 - z2, z2) -- cycle; fill get_subpath (ellipse, z3 - z4, z4 - z3, z3) -- get_subpath (ellipse, z4 - z3, z3 - z4, z4) -- cycle; fill get_subpath (ellipse, z5 - z6, z6 - z5, z5) -- point 0 of get_subpath (ellipse, z6 - z5, z5 - z6, z6) -- get_subpath (ellipse, z7 - z6, z6 - z7, z7) -- get_subpath (ellipse, z6 - z7, z5 - z6, z6) -- cycle; labels (1, 2, 3, 4, 5, 6, 7); fet_endchar; % solesmes punctum inclinatum parvum inclinatum_char ("Solesmes punctum inclinatum parvum", "solesmes.incl.parvum", true, false, false); % solesmes punctum auctum ascendens vat_punctum_char ("Solesmes punctum auctum ascendens", "solesmes.auct.asc", false, false, false, true, true, false, false, 1.0); % solesmes punctum auctum descendens vat_punctum_char ("Solesmes punctum auctum descendens", "solesmes.auct.desc", false, false, false, true, false, false, false, 1.0); % solesmes punctum inclinatum auctum inclinatum_char ("Solesmes punctum incl. auctum", "solesmes.incl.auctum", false, false, true); % solesmes stropha inclinatum_char ("Solesmes stropha", "solesmes.stropha", false, true, false); % solesmes stropha aucta inclinatum_char ("Solesmes stropha aucta", "solesmes.stropha.aucta", false, true, true); % solesmes oriscus fet_beginchar ("Solesmes oriscus", "ssolesmes.oriscus"); save a_b, b_h, a_w; a_b := 1.54; % b_h * a_b / a_w = wd / ht b_h := 0.85; a_w := 1.09; save a, beta, ht, wd; ht# = noteheight#; 2 beta = ht# * b_h; a = beta * a_b; wd# = 2 a / a_w; black_notehead_width# := wd#; save convexity; convexity# = +0.05 ht#; define_pixels (ht, wd, convexity); set_char_box (0.00 wd#, 0.50 wd#, 0.25 ht# + convexity#, 0.25 ht# + convexity#); z1 = (0.00 wd + blot_diameter / 2, -convexity); z2 = (1/6 wd, +convexity); z3 = (2/6 wd, -convexity); z4 = (0.50 wd - blot_diameter / 2, +convexity); save height; height = 2 ypart (directionpoint right of (z1 .. z2 .. z3 .. z4)); save ellipse, T; path ellipse; transform T; T := identity xscaled blot_diameter yscaled (h + d - height); pickup pencircle transformed T; ellipse := fullcircle transformed T; % Adjust vertical coordinates to touch bounding box. y1 := top -d; y4 := bot h; save d_; pair d_; d_ := direction 0 of (z1 .. z2 .. z3 .. z4); fill get_subpath (ellipse, -d_, d_, z1) .. bot z2 .. bot z3 .. get_subpath (ellipse, d_, -d_, z4) .. top z3 .. top z2 .. cycle; labels (1, 2, 3, 4); fet_endchar; %%%%%%%% % % % % EDITIO MEDICAEA % % % % inclinatum fet_beginchar ("Ed. Med. inclinatum", "smedicaea.inclinatum"); draw_diamond_head (1.2 staff_space#, 0, 0, 35, false); fet_endchar; def med_punctum_char (expr verbose_name, internal_name, left_up_stem, left_down_stem) = fet_beginchar (verbose_name, "s" & internal_name); save a, ht, wd; ht# = 2 staff_space#; wd# = ht#; black_notehead_width# := wd#; define_pixels (ht, wd); save ellipse; path ellipse; ellipse := fullcircle xscaled blot_diameter yscaled 0.50 ht; z1 = (0.00 wd + blot_diameter / 2, 0); z2 = (0.4 wd - blot_diameter / 2, 0); labels (1, 2); pickup pencircle scaled linethickness; if left_down_stem: z4 = (0.00 wd + linethickness / 2, -1.25 ht); fill get_subpath (ellipse, left, down, z1) -- top lft z4{down} .. z4{right} .. top rt z4{up} -- (rt x4, -.5 ht / 2) -- get_subpath (ellipse, right, left, z2) -- cycle; labels (4); set_char_box (0.0, 0.4 wd#, 1.25 ht#, 0.25 ht#); elseif left_up_stem: z4 = (0.00 wd + linethickness / 2, +1.25 ht); fill get_subpath (ellipse, down, right, z1) -- get_subpath (ellipse, right, left, z2) -- (rt x4, .5 ht / 2) -- bot rt z4{up} .. z4{left} .. bot lft z4{down} -- cycle; labels (4); set_char_box (0.0, 0.4 wd#, 0.25 ht#, 1.25 ht#); else: fill get_subpath (ellipse, left, right, z1) -- get_subpath (ellipse, right, left, z2) -- cycle; set_char_box (0.0, 0.4 wd#, 0.25 ht#, 0.25 ht#); fi; fet_endchar; enddef; % punctum med_punctum_char ("Ed. Med. punctum", "medicaea.punctum", false, false); % left up-stemmed punctum med_punctum_char ("Ed. Med. reverse virga", "medicaea.rvirga", true, false); % virga (i.e. left down-stemmed punctum) med_punctum_char ("Ed. Med. virga", "medicaea.virga", false, true); %%%%%%%% % % % % HUFNAGEL % % % def huf_punctum_char (expr verbose_name, internal_name, down_stem) = fet_beginchar (verbose_name, "s" & internal_name); save alpha; alpha = 55; draw_diamond_head (staff_space#, 0, 0, alpha, false); if down_stem: set_char_box (0, head_width#, 1.5 staff_space#, head_height# / 2); save ellipse; path ellipse; ellipse := reverse fullcircle xscaled blot_diameter yscaled 0.7 staff_space rotated -alpha; z11 = (head_width / 2, 0); z12 = find_tangent_shift (((0, -d) -- (w, -d)), ellipse, (w / 2, -d), (w / 2, 0)); fill get_subpath (ellipse, up, down, z11) -- get_subpath (ellipse, down, up, z12) -- cycle; labels (11, 12); fi; fet_endchar; enddef; % punctum huf_punctum_char ("Hufnagel punctum", "hufnagel.punctum", false) % virga huf_punctum_char ("Hufnagel virga", "hufnagel.virga", true) % pes lower punctum fet_beginchar ("Hufnagel pes lower punctum", "shufnagel.lpes") save width, height, alpha; width# = 2 * staff_space#; height# = 0.7 * staff_space#; alpha = 35; set_char_box (0, width#, height# / 2, height# / 2); define_pixels (width, height); save circle; path circle; circle := reverse fullcircle scaled linethickness; pickup pencircle scaled linethickness; rt x3 = -lft x1 = width / 2; y2 = y3 = height / 2; y1 = y4 = -height / 2; tand (alpha) * (y2 - y1) = x2 - x1 = x3 - x4; fill get_subpath (circle, left, z2 - z1, z1) -- get_subpath (circle, z2 - z1, right, z2) -- get_subpath (circle, right, z4 - z3, z3) -- get_subpath (circle, z4 - z3, left, z4) -- cycle; currentpicture := currentpicture shifted (width/2, 0); % labels (1, 2, 3, 4); fet_endchar; fet_endgroup ("noteheads"); muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-macros.mf000066400000000000000000000111261404105311000250420ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-macros.mf -- macros for parmesan font % % source file of LilyPond's pretty-but-neat music font % % (c) 2001--2009 Juergen Reuter % % % Find point on `curve' which gives the tangent between point `p' % and `curve'. To guide the search, two auxiliary points must be % specified, `p_in' and `p_out'. The line between `p' and `p_in' % must intersect `curve', while the line between `p' and `p_out' % must not. % def find_tangent (expr p, curve, p_in, p_out) = begingroup; save mid, t, t_good, in, out; pair mid, in, out; in := p_in; out := p_out; forever: mid := 0.5 [in, out]; exitif abs (out - mid) <= eps; t := xpart (curve intersectiontimes (p -- mid)); if t > 0: in := mid; t_good := t; else: out := mid; fi; endfor; point t_good of curve endgroup enddef; % % Shift `curve' along the line given by the auxiliary points `p_in' % and `p_out' until `line' is a tangent, and return the shift. % If `curve' is shifted to position `p_in', it must intersect % `line', while shifted to `p_out' it must not. % def find_tangent_shift (expr line, curve, p_in, p_out) = begingroup; save mid, t, in, out; pair mid, in, out; in := p_in; out := p_out; forever: mid := 0.5 [in, out]; exitif abs (out - mid) <= eps; t := xpart ((curve shifted mid) intersectiontimes line); if t > 0: in := mid; else: out := mid; fi; endfor; mid endgroup enddef; % % Get subpath specified by `dir_in' and `dir_out' of `curve' % which is then shifted by `offset'. Assure that result has % the same orientation as `curve'. % def get_subpath (expr curve, dir_in, dir_out, offset) = begingroup; save t_in, t_out; t_in := directiontime dir_in of curve; t_out := directiontime dir_out of curve; if t_in > t_out: t_out := t_out + length curve; fi; (subpath (t_in, t_out) of curve) shifted offset endgroup enddef; % % Get point specified by `dir_' of `curve' which is then % shifted by `offset'. % def get_subpoint (expr curve, dir_, offset) = (directionpoint dir_ of curve) shifted offset enddef; % % This is the same as `get_subpath', except that the time values % used to construct the resulting subpath are rounded to integers. % def get_subpath_i (expr curve, dir_in, dir_out, offset) = begingroup; save t_in, t_out; t_in := directiontime dir_in of curve; t_out := directiontime dir_out of curve; if t_in > t_out: t_out := t_out + length curve; fi; (subpath (floor (t_in + 0.5), floor (t_out + 0.5)) of curve) shifted offset endgroup enddef; % % Find envelope cusp created by `object' moved along `curve', using % step value `s' for initial intermediate points. `s' must be small % enough so that this macro finds at least one point on the envelope % between the `entrance' and `exit' points of the cusp which has % a significantly different direction vector. % % This function returns a time value on `curve'; if there is no % cusp, it returns -1. % def find_envelope_cusp (expr object, curve, s) = begingroup; save mid, p, t, t_good, delta, start, stop, do_exit; pair p[]; boolean do_exit; p0 := (directionpoint (direction 0 of curve) of object) shifted (point 0 of curve); p1 := (directionpoint (direction s of curve) of object) shifted (point s of curve); t := s; forever: t := t + s; exitif t >= length curve; p2 := (directionpoint (direction t of curve) of object) shifted (point t of curve); if p2 <> p1: delta := angle (p2 - p1) - angle (p1 - p0); if delta > 180: delta := delta - 360; fi; % we check for a direction change by more than % than 45 degrees if abs (delta) >= 45: do_exit := true; else: do_exit := false; fi; p0 := p1; p1 := p2; fi; % having `exitif' within an if-clause doesn't work exitif do_exit; endfor; if t >= length curve: t_good := -1; else: % the wanted point lies between `t - s' and `t' start := t - s; stop := t; t_good := start; forever: mid := 0.5 [start, stop]; exitif abs (stop - mid) <= eps; p0 := (directionpoint (direction start of curve) of object) shifted (point start of curve); p1 := (directionpoint (direction mid of curve) of object) shifted (point mid of curve); p2 := (directionpoint (direction stop of curve) of object) shifted (point stop of curve); exitif (length (p1 - p0) = 0) or (length (p2 - p1) = 0); delta := angle (p2 - p1) - angle (p1 - p0); if delta > 180: delta := delta - 360; fi; if abs (delta) >= 45: stop := mid; t_good := mid; else: start := mid; t_good := stop; fi; endfor; fi; t_good endgroup enddef; % EOF muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-rests.mf000066400000000000000000000203411404105311000247150ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-rests.mf -- implement ancient rests % % source file of LilyPond's pretty-but-neat music font % % (c) 2001--2009 Juergen Reuter % % Neo-mensural rests originally by % Christian Mondrup and Mats Bengtsson. % fet_begingroup ("rests") %%%%%%%% % % % % NEO-MENSURAL NOTATION % % % % Neo-mensural longa and breve are identical with default longa and breve. neomens_block_rest_x# = 2/5 staff_space#; neomens_block_rest_y# = 1/1 staff_space#; neomens_half_block_rest_y# = 5/8 staff_space#; define_pixels (neomens_block_rest_x, neomens_block_rest_y, neomens_half_block_rest_y); def neomens_half_block_rest = draw_block ((0, 0), (neomens_block_rest_x, neomens_half_block_rest_y)); enddef; fet_beginchar ("Neo-mensural maxima rest", "M3neomensural"); set_char_box (0, 3 neomens_block_rest_x#, neomens_block_rest_y#, neomens_block_rest_y#); draw_block ((0, -neomens_block_rest_y), (neomens_block_rest_x, neomens_block_rest_y)); addto currentpicture also currentpicture shifted (2 neomens_block_rest_x, 0); fet_endchar; fet_beginchar ("Neo-mensural longa rest", "M2neomensural"); set_char_box (0, neomens_block_rest_x#, neomens_block_rest_y#, neomens_block_rest_y#); draw_block ((0, -neomens_block_rest_y), (neomens_block_rest_x, neomens_block_rest_y)); fet_endchar; fet_beginchar ("Neo-mensural breve rest", "M1neomensural"); set_char_box (0, neomens_block_rest_x#, 0, neomens_block_rest_y#); draw_block ((0, 0), (neomens_block_rest_x, neomens_block_rest_y)); fet_endchar; fet_beginchar ("Neo-mensural whole rest", "0neomensural"); set_char_box (0, neomens_block_rest_x#, neomens_half_block_rest_y#, 0); neomens_half_block_rest; currentpicture := currentpicture shifted (0, -neomens_half_block_rest_y); fet_endchar; fet_beginchar ("Neo-mensural half rest", "1neomensural"); set_char_box (0, neomens_block_rest_x#, 0, neomens_half_block_rest_y#); neomens_half_block_rest; fet_endchar; neomensrestsize# = 0.8 staff_space#; fet_beginchar ("Neo-mensural 4th rest", "2neomensural"); set_char_box (0, neomensrestsize#, 0, neomensrestsize#); pickup pencircle scaled 2 stafflinethickness; lft x1 = 0; bot y1 = 0; lft x2 = lft x3 = 0; top y2 = top y3 = h; rt x4 = w; bot y4 = h / 2; z11 = z1 - stafflinethickness * unitvector (z2 - z1); z14 = z4 + stafflinethickness * unitvector (z4 - z3); penpos1 (2 stafflinethickness, 0); penpos2 (2 stafflinethickness, 0); penpos3 (2 stafflinethickness, angle (z4 - z3) - 90); penpos4 (2 stafflinethickness, angle (z4 - z3) - 90); fill z1l -- z2l -- z2r -- z1r .. z11 .. z1l .. cycle; fill z3l -- z4l .. z14 .. z4r -- z3r .. z2l{up} .. z3l .. cycle; penlabels (1, 2, 3, 4); labels (11, 14); fet_endchar; fet_beginchar ("Neo-mensural 8th rest", "3neomensural"); set_char_box (0, neomensrestsize#, 0, neomensrestsize#); pickup pencircle scaled 2 stafflinethickness; rt x1 = w; bot y1 = 0; rt x2 = rt x3 = w; top y2 = top y3 = h; lft x4 = 0; bot y4 = h / 2; z11 = z1 - stafflinethickness * unitvector (z2 - z1); z14 = z4 + stafflinethickness * unitvector (z4 - z3); penpos1 (2 stafflinethickness, 0); penpos2 (2 stafflinethickness, 0); penpos3 (2 stafflinethickness, angle (z4 - z3) - 90); penpos4 (2 stafflinethickness, angle (z4 - z3) - 90); fill z1l -- z2l -- z2r -- z1r .. z11 .. z1l .. cycle; fill z3l -- z4l .. z14 .. z4r -- z3r .. z2r{down} .. z3l .. cycle; penlabels (1, 2, 3, 4); labels (11, 14); fet_endchar; fet_beginchar ("Neo-mensural 16th rest", "4neomensural"); set_char_box (0, neomensrestsize#, 0, staff_space#); pickup pencircle scaled 2 stafflinethickness; rt x1 = w; bot y1 = 0; rt x2 = w; top y2 = h; rt x3 = w; top y3 = h; lft x4 = 0; bot y4 = h / 2; rt x5 = w; top y5 = 5/8 h; lft x6 = 0; bot y6 = h / 8; z11 = z1 - stafflinethickness * unitvector (z2 - z1); z14 = z4 + stafflinethickness * unitvector (z4 - z3); z16 = z6 + stafflinethickness * unitvector (z6 - z5); penpos1 (2 stafflinethickness, 0); penpos2 (2 stafflinethickness, 0); penpos3 (2 stafflinethickness, angle (z4 - z3) - 90); penpos4 (2 stafflinethickness, angle (z4 - z3) - 90); penpos5 (2 stafflinethickness, angle (z6 - z5) - 90); penpos6 (2 stafflinethickness, angle (z6 - z5) - 90); fill z1l -- z2l -- z2r -- z1r .. z11 .. z1l .. cycle; fill z3l -- z4l .. z14 .. z4r -- z3r .. z2r{down} .. z3l .. cycle; fill z5l -- z6l .. z16 .. z6r -- z5r -- z5l -- cycle; penlabels (1, 2, 3, 4, 5, 6); labels (11, 14, 16); fet_endchar; %%%%%%%% % % % % MENSURAL NOTATION % % % pen_width# = 0.3 staff_space#; pen_height# = 0; pen_rotation = 30; slight_pen_rotation = 15; mens_block_rest_y# = 1/1 staff_space#; mens_half_block_rest_y# = 5/8 staff_space#; define_pixels (mens_block_rest_y, mens_half_block_rest_y, pen_width, pen_height); fet_beginchar ("Mensural maxima rest", "M3mensural"); set_char_box (0, pen_width#, mens_block_rest_y#, 2 mens_block_rest_y#); pickup pencircle xscaled pen_width yscaled pen_height rotated slight_pen_rotation; draw (w / 2, -mens_block_rest_y) -- (w / 2, 2 mens_block_rest_y); fet_endchar; fet_beginchar ("Mensural longa rest", "M2mensural"); set_char_box (0, pen_width#, mens_block_rest_y#, mens_block_rest_y#); pickup pencircle xscaled pen_width yscaled pen_height rotated slight_pen_rotation; draw (w / 2, -mens_block_rest_y) -- (w / 2, mens_block_rest_y); fet_endchar; fet_beginchar ("Mensural breve rest", "M1mensural"); set_char_box (0, pen_width#, 0, mens_block_rest_y#); pickup pencircle xscaled pen_width yscaled pen_height rotated slight_pen_rotation; draw (w / 2, 0) -- (w / 2, mens_block_rest_y); fet_endchar; fet_beginchar ("Mensural whole rest", "0mensural"); set_char_box (0, pen_width#, mens_half_block_rest_y#, 0); pickup pencircle xscaled pen_width yscaled pen_height rotated slight_pen_rotation; draw (w / 2, 0) -- (w / 2, -mens_half_block_rest_y); fet_endchar; fet_beginchar ("Mensural half rest", "1mensural"); set_char_box (0, pen_width#, 0, mens_half_block_rest_y#); pickup pencircle xscaled pen_width yscaled pen_height rotated slight_pen_rotation; draw (w / 2, 0) -- (w / 2, mens_half_block_rest_y); fet_endchar; mensrestsize# = .8 staff_space#; fet_beginchar ("Mensural 4th rest", "2mensural"); set_char_box (0, mensrestsize#, 0, mensrestsize#); pickup pencircle xscaled pen_width yscaled pen_height rotated pen_rotation; lft x1 = 0; y1 = 0; lft x2 = 0; top y2 = 5/8 h; rt x3 = 1/5 w; bot y3 = 6/8 h; rt x4 = w; bot y4 = 4/8 h; draw z1 -- z2 -- z3 -- z4; labels (1, 2, 3, 4); fet_endchar; % % FIXME: The left sharp corner produces ugly results at lower resolutions. % fet_beginchar ("Mensural 8th rest", "3mensural"); set_char_box (0, mensrestsize#, 0, mensrestsize#); pickup pencircle xscaled pen_width yscaled pen_height rotated pen_rotation; rt x1 = w; y1 = 0; rt x2 = w; top y2 = 5/8 h; lft x3 = 1/5 w; bot y3 = 6/8 h; lft x4 = 0; bot y4 = 4/8 h; draw z1 -- z2 -- z3 -- z4; labels (1, 2, 3, 4); fet_endchar; % % FIXME: So far, I have never seen a semifusa rest printed. Hence, % the shape of this font character is currently pure invention. --jr % % % FIXME: The left sharp corner produces ugly results at lower resolutions. % fet_beginchar ("Mensural 16th rest", "4mensural"); set_char_box (0, mensrestsize#, 0, staff_space#); pickup pencircle xscaled pen_width yscaled pen_height rotated pen_rotation; rt x1 = w; y1 = 0; rt x2 = w; top y2 = 5.5/8 h; lft x3 = 1/5 w; bot y3 = 6.5/8 h; lft x4 = 0; bot y4 = 4.5/8 h; rt x5 = w; top y5 = 2.5/8 h; lft x6 = 1/5 w; bot y6 = 3.5/8 h; lft x7 = 0; bot y7 = 1.5/8 h; draw z1 -- z2 -- z3 -- z4; draw z5 -- z6 -- z7; labels (1, 2, 3, 4, 5, 6, 7); fet_endchar; fet_endgroup ("rests") muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-scripts.mf000066400000000000000000000116341404105311000252510ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-scripts.mf -- implement ancient script symbols % % source file of LilyPond's pretty-but-neat music font % % (c) 2002--2009 Juergen Reuter % fet_begingroup ("scripts"); fet_beginchar ("ictus", "ictus"); save ht, wd; ht# = .35 staff_space#; wd# = linethickness#; define_pixels (wd, ht); save ellipse, T; path ellipse; transform T; T := identity xscaled wd yscaled blot_diameter; pickup pencircle transformed T; ellipse := fullcircle transformed T; set_char_box (wd# / 2, wd# / 2, ht# / 2, ht# / 2); x1 = 0; top y1 = ht / 2; x2 = 0; bot y2 = -ht / 2; fill get_subpath (ellipse, up, down, z1) -- get_subpath (ellipse, down, up, z2) -- cycle; labels (1, 2); fet_endchar; def draw_accentus = save thick, thin, ht, wd; thin# = 1 linethickness#; thick# = 1.2 linethickness#; ht# = .35 staff_space#; wd# = .25 staff_space#; define_pixels (wd, ht, thin, thick); z1 = z2 = (-wd / 2 + thin / 2, -ht / 2 + thin / 2); z3 = z4 = (wd / 2 - thick / 2, ht / 2 - thick / 2); penpos1 (thin, angle (z1 - z3)); penpos2 (thin, angle (z1 - z3) + 90); penpos3 (thick, angle (z3 - z1) + 90); penpos4 (thick, angle (z3 - z1)); fill z1r .. z2r -- z3l .. z4r .. z3r -- z2l .. cycle; set_char_box (wd#/2, wd#/2, ht#/2, ht#/2); enddef; fet_beginchar ("accentus up", "uaccentus"); draw_accentus; penlabels (1, 2, 3, 4); fet_endchar; fet_beginchar ("accentus down", "daccentus"); draw_accentus; y_mirror_char; fet_endchar; def draw_half_circle (expr diam) = save diameter, thick; diameter# = diam; define_pixels (diameter); thick = 0.6 linethickness; penpos1 (thick, 0); penpos2 (thick, -90); penpos3 (thick, -180); z1r = (diameter / 2, 0); z2r = (0, -diameter / 2); z3r = (-diameter / 2, 0); penstroke z1e{down} .. z2e{left} .. z3e{up}; enddef; fet_beginchar ("half circle", "usemicirculus"); draw_half_circle (5/16 staff_space#); currentpicture := currentpicture yscaled -1; set_char_box (diameter# / 2, diameter# / 2, 0, diameter# / 2); fet_endchar; fet_beginchar ("half circle", "dsemicirculus"); draw_half_circle (5/16 staff_space#); set_char_box (diameter# / 2, diameter# / 2, diameter# / 2, 0); penlabels (1, 2, 3); fet_endchar; fet_beginchar ("circle", "circulus"); save diameter, thick; diameter# = 5/16 staff_space#; define_pixels (diameter); thick = 0.6 linethickness; fill fullcircle scaled diameter; unfill fullcircle scaled (diameter - 2 thick); set_char_box (diameter# / 2, diameter# / 2, diameter# / 2, diameter# / 2); fet_endchar; fet_beginchar ("augmentum", "augmentum"); save diameter; diameter# = 0.25 staff_space#; define_pixels (diameter); pickup pencircle scaled diameter; drawdot (0, 0); set_char_box (diameter# / 2, diameter# / 2, diameter# / 2, diameter# / 2); fet_endchar; def draw_signum_congruentiae = set_char_box (0.50 staff_space# + 1.5 linethickness#, 0.50 staff_space# + 1.5 linethickness#, 2.5 linethickness#, 2.00 staff_space# + 3.5 linethickness#); save ellipse, pat; path ellipse, pat; ellipse := fullcircle xscaled 1.0 linethickness yscaled 5.0 linethickness rotated -35; z1 = (+0.50 staff_space, +2.00 staff_space); z2 = (+0.00 staff_space, +2.00 staff_space); z3 = (-0.50 staff_space, +1.50 staff_space); z4 = (-0.25 staff_space, +1.00 staff_space); z5 = (+0.10 staff_space, +0.80 staff_space); z6 = (+0.00 staff_space, +0.00 staff_space); pat := z1{-1, +1} .. z2 .. z3 .. z4 .. z5 .. z6; % we avoid cusps originally present in `draw pat' fill get_subpath (ellipse, -direction 0 of pat, direction 0 of pat, z1) .. get_subpoint (ellipse, direction 1 of pat, z2) {direction 1 of pat} .. get_subpoint (ellipse, direction 2 of pat, z3) {direction 2 of pat} .. get_subpoint (ellipse, direction 3 of pat, z4) {direction 3 of pat} .. get_subpoint (ellipse, direction 4 of pat, z5) {direction 4 of pat} .. get_subpath (ellipse, direction 4.8 of pat, -direction 5 of pat, z6) .. get_subpoint (ellipse, -direction 4 of pat, z5) {-direction 4 of pat} .. get_subpoint (ellipse, -direction 3 of pat, z4) {-direction 3 of pat} .. get_subpoint (ellipse, -direction 2.5 of pat, point 2.5 of pat) .. get_subpoint (ellipse, -direction 1 of pat, z2) {-direction 1 of pat} .. cycle; pickup pencircle scaled 3.0 linethickness; z7 = (-0.5 staff_space, +0.0 staff_space); z8 = (+0.5 staff_space, +0.0 staff_space); drawdot z7; drawdot z8; enddef; fet_beginchar ("signum congruentiae up", "usignumcongruentiae"); draw_signum_congruentiae; labels (1, 2, 3, 4, 5, 6, 7, 8); fet_endchar; fet_beginchar ("signum congruentiae down", "dsignumcongruentiae"); draw_signum_congruentiae; xy_mirror_char; fet_endchar; fet_endgroup ("scripts"); muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan-timesig.mf000066400000000000000000000232271404105311000252240ustar00rootroot00000000000000% -%-Fundamental-%- -*-Metafont-*- % parmesan-timesig.mf -- implement ancient time signatures % % source file of LilyPond's pretty-but-neat music font % % (c) 1998--2009 Mats Bengtsson , % Christian Mondrup % (c) 2002--2009 Juergen Reuter % fet_begingroup ("timesig"); path threequartercircle; threequartercircle := halfcircle & quartercircle rotated 180; save mensural_Cdiameter, mensural_Cthickness, mensural_Chairpin; mensural_Cdiameter# := 3/2 staff_space#; mensural_Cthickness# := 2 linethickness#; mensural_Chairpin# := 0.5 linethickness#; define_pixels (mensural_Cdiameter, mensural_Cthickness, mensural_Chairpin); %%%%%%%% % % % % MENSURAL NOTATION % % % def draw_nail (expr bottom_left, top_right) = save round; round = floor min (blot_diameter, xpart (top_right-bottom_left), ypart (top_right-bottom_left)); pickup pencircle scaled round; z2 + (round / 2, round / 2) = top_right + (mensural_Cthickness / 2, 0); z4 - (round / 2,round / 2) = bottom_left; y3 = y2; y4 = y1; x2 = x1 + mensural_Cthickness / 2; x2 - x1 = x4 - x3; x5 = x1; x6 = x4; y5 = y6 = y2 - 2 mensural_Cthickness; fill bot z1{right} .. rt z1{up} -- rt z5{up} .. rt z2 .. top z2{left} -- top z3{left} .. lft z3 .. lft z6{down} -- lft z4{down} .. bot z4{right} -- cycle; % labels (1, 2, 3, 4, 5, 6); enddef; def mensural_timesig (expr verbose_name, internal_name, full_circle, reverse_circle, slashed, dotted) = fet_beginchar (verbose_name, internal_name); if slashed: set_char_box ( 0, mensural_Cdiameter# + mensural_Cthickness#, .75 (mensural_Cdiameter# + mensural_Cthickness#), .75 (mensural_Cdiameter# + mensural_Cthickness#)); if (full_circle or dotted): draw_nail ((-mensural_Cthickness / 2, -d), (mensural_Cthickness / 2, h)); elseif reverse_circle: draw_nail ((0, -d), (mensural_Cthickness, h)); else: draw_nail ((-mensural_Cthickness, -d), (0, h)); fi; else: set_char_box ( 0, mensural_Cdiameter# + mensural_Cthickness#, .5 (mensural_Cdiameter# + mensural_Cthickness#), .5 (mensural_Cdiameter# + mensural_Cthickness#)); fi; save ellipse, pat; path ellipse, pat; ellipse := fullcircle xscaled mensural_Cthickness yscaled mensural_Chairpin; save rot; if reverse_circle: rot := 225; else: rot := 45; fi; if full_circle: pat := fullcircle scaled mensural_Cdiameter; fill get_subpoint (ellipse, direction 0 of pat, point 0 of pat) for i = 1 step 1 until length pat: .. get_subpoint (ellipse, direction i of pat, point i of pat) endfor .. cycle; unfill get_subpoint (ellipse, -direction 0 of pat, point 0 of pat) for i = 1 step 1 until length pat: .. get_subpoint (ellipse, -direction i of pat, point i of pat) endfor .. cycle; else: pat := threequartercircle rotated rot scaled mensural_Cdiameter; fill get_subpath (ellipse, -direction 0 of pat, direction 0 of pat, point 0 of pat) for i = 1 step 1 until (length pat - 1): .. get_subpoint (ellipse, direction i of pat, point i of pat) endfor .. get_subpath (ellipse, direction infinity of pat, -direction infinity of pat, point infinity of pat) for i = (length pat - 1) step -1 until 1: .. get_subpoint (ellipse, -direction i of pat, point i of pat) endfor .. cycle; fi; if dotted: pickup pencircle scaled 4 linethickness; drawdot (0, 0); fi; currentpicture := currentpicture shifted 0.5 (mensural_Cdiameter + mensural_Cthickness, 0); fet_endchar; enddef; %% tempus imperfectum cum prolatione imperfecta mensural_timesig ("Mensural 4/4 meter", "mensural44", false, false, false, false); %% tempus imperfectum cum prolatione imperfecta proportio dupla mensural_timesig ("Mensural 2/2 meter", "mensural22", false, false, true, false); %% tempus perfectum cum prolatione imperfecta mensural_timesig ("Mensural 3/2 meter", "mensural32", true, false, false, false); %% tempus imperfectum cum prolatione perfecta mensural_timesig ("Mensural 6/4 meter", "mensural64", false, false, false, true); %% tempus perfectum cum prolatione perfecta mensural_timesig ("Mensural 9/4 meter", "mensural94", true, false, false, true); %% tempus perfectum cum prolatione imperfecta proportio dupla mensural_timesig ("Mensural 3/4 meter", "mensural34", true, false, true, false); %% tempus imperfectum cum prolatione perfecta proportio dupla mensural_timesig ("Mensural 6/8 meter", "mensural68", false, false, true, true); %% tempus perfectum cum prolatione perfecta proportio dupla mensural_timesig ("Mensural 9/8 meter", "mensural98", true, false, true, true); %% tempus imperfectum cum prolatione imperfecta diminutio simplex mensural_timesig ("Mensural 4/8 meter", "mensural48", false, true, false, false); %% tempus imperfectum cum prolatione perfecta diminutio simplex mensural_timesig ("Mensural 6/8 meter", "mensural68alt", false, true, false, true); %% tempus imperfectum cum prolatione imperfecta diminutio duplex mensural_timesig ("Mensural 2/4 meter", "mensural24", false, true, true, false); %%%%%%%% % % % % NEO-MENSURAL NOTATION % % % save neomensural_Cdiameter, neomensural_Cthickness; neomensural_Cdiameter# := 3/2 staff_space#; neomensural_Cthickness# := 2 linethickness#; define_pixels (neomensural_Cdiameter, neomensural_Cthickness); def neomensural_timesig (expr verbose_name, internal_name, full_circle, reverse_circle, slashed, dotted) = fet_beginchar (verbose_name, internal_name); set_char_box ( 0, neomensural_Cdiameter# + neomensural_Cthickness#, .5 (neomensural_Cdiameter# + neomensural_Cthickness#), .5 (neomensural_Cdiameter# + neomensural_Cthickness#)); if slashed: pickup penrazor scaled neomensural_Cthickness; save t; t := neomensural_Cthickness / 2; if (full_circle or dotted): draw (0, -d + t) -- (0, h - t); elseif reverse_circle: draw (t, -d + t) -- (t, h - t); else: draw (-t, -d + t) -- (-t, h - t); fi; fi; save ellipse, pat; path ellipse, pat; ellipse := fullcircle scaled neomensural_Cthickness; save rot; if reverse_circle: rot := 225; else: rot := 45; fi; if full_circle: pat := fullcircle scaled neomensural_Cdiameter; fill get_subpoint (ellipse, direction 0 of pat, point 0 of pat) for i = 1 step 1 until length pat: .. get_subpoint (ellipse, direction i of pat, point i of pat) endfor .. cycle; unfill get_subpoint (ellipse, -direction 0 of pat, point 0 of pat) for i = 1 step 1 until length pat: .. get_subpoint (ellipse, -direction i of pat, point i of pat) endfor .. cycle; else: pat := threequartercircle rotated rot scaled neomensural_Cdiameter; fill get_subpath (ellipse, -direction 0 of pat, direction 0 of pat, point 0 of pat) for i = 1 step 1 until (length pat - 1): .. get_subpoint (ellipse, direction i of pat, point i of pat) endfor .. get_subpath (ellipse, direction infinity of pat, -direction infinity of pat, point infinity of pat) for i = (length pat - 1) step -1 until 1: .. get_subpoint (ellipse, -direction i of pat, point i of pat) endfor .. cycle; fi; if dotted: pickup pencircle scaled 4 linethickness; drawdot (0, 0); fi; currentpicture := currentpicture shifted 0.5 (neomensural_Cdiameter + neomensural_Cthickness, 0); fet_endchar; enddef; %% tempus imperfectum cum prolatione imperfecta neomensural_timesig ("Mensural 4/4 meter", "neomensural44", false, false, false, false); %% tempus imperfectum cum prolatione imperfecta proportio dupla neomensural_timesig ("Mensural 2/2 meter", "neomensural22", false, false, true, false); %% tempus perfectum cum prolatione imperfecta neomensural_timesig ("Mensural 3/2 meter", "neomensural32", true, false, false, false); %% tempus imperfectum cum prolatione perfecta neomensural_timesig ("Mensural 6/4 meter", "neomensural64", false, false, false, true); %% tempus perfectum cum prolatione perfecta neomensural_timesig ("Mensural 9/4 meter", "neomensural94", true, false, false, true); %% tempus perfectum cum prolatione imperfecta proportio dupla neomensural_timesig ("Mensural 3/4 meter", "neomensural34", true, false, true, false); %% tempus imperfectum cum prolatione perfecta proportio dupla neomensural_timesig ("Mensural 6/8 meter", "neomensural68", false, false, true, true); %% tempus perfectum cum prolatione perfecta proportio dupla neomensural_timesig ("Mensural 9/8 meter", "neomensural98", true, false, true, true); %% tempus imperfectum cum prolatione imperfecta diminutio simplex neomensural_timesig ("Mensural 4/8 meter", "neomensural48", false, true, false, false); %% tempus imperfectum cum prolatione perfecta diminutio simplex neomensural_timesig ("Mensural 6/8 meter", "neomensural68alt", false, true, false, true); %% tempus imperfectum cum prolatione imperfecta diminutio duplex neomensural_timesig ("Mensural 2/4 meter", "neomensural24", false, true, true, false); fet_endgroup ("timesig"); muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan11.mf000066400000000000000000000002331404105311000237170ustar00rootroot00000000000000% parmesan11.mf % part of LilyPond's pretty-but-neat music font design_size := 11.22; input feta-autometric; test := 0; input parmesan-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan13.mf000066400000000000000000000002341404105311000237220ustar00rootroot00000000000000% parmesan13.mf % part of LilyPond's pretty-but-neat music font design_size := 12.60; input feta-autometric; test := 0; input parmesan-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan14.mf000066400000000000000000000002341404105311000237230ustar00rootroot00000000000000% parmesan14.mf % part of LilyPond's pretty-but-neat music font design_size := 14.14; input feta-autometric; test := 0; input parmesan-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan16.mf000066400000000000000000000002341404105311000237250ustar00rootroot00000000000000% parmesan16.mf % part of LilyPond's pretty-but-neat music font design_size := 15.87; input feta-autometric; test := 0; input parmesan-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan18.mf000066400000000000000000000002341404105311000237270ustar00rootroot00000000000000% parmesan18.mf % part of LilyPond's pretty-but-neat music font design_size := 17.82; input feta-autometric; test := 0; input parmesan-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan20.mf000066400000000000000000000002311404105311000237150ustar00rootroot00000000000000% parmesan20.mf % part of LilyPond's pretty-but-neat music font design_size := 20; input feta-autometric; test := 0; input parmesan-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan23.mf000066400000000000000000000002341404105311000237230ustar00rootroot00000000000000% parmesan23.mf % part of LilyPond's pretty-but-neat music font design_size := 22.45; input feta-autometric; test := 0; input parmesan-generic; end. muse-4.0.0/src/share/scoreglyphs/feta-original/parmesan26.mf000066400000000000000000000002341404105311000237260ustar00rootroot00000000000000% parmesan26.mf % part of LilyPond's pretty-but-neat music font design_size := 25.20; input feta-autometric; test := 0; input parmesan-generic; end. muse-4.0.0/src/share/scoreglyphs/flags16d.png000066400000000000000000000007021404105311000210200ustar00rootroot00000000000000PNG  IHDR ^sRGBbKGD pHYs  tIME aovtEXtCommentCreated with GIMPWIDAT(υ=K\A\­!6lZF+kM,L2_("X ),- rlp̉nE2XD,]ZzCN++:*nfW83S# ^ܖX%X&+4-l?3>`ыAgkgpշ vqf~UZe_ч ߼ omj]GxځT2ęw ]"+IENDB`muse-4.0.0/src/share/scoreglyphs/flags16u.png000066400000000000000000000006551404105311000210500ustar00rootroot00000000000000PNG  IHDR{FsRGBbKGD pHYs  tIME  %tEXtCommentCreated with GIMPWIDAT(uM+a=f$5^,4 ER>&+FV V4 "/X /u[xFgN]}]seM!vaVc`!b)fmgjt{-bnbhoB )PC죈i<&S 71 я ]y\%XNR;8/G_8;7%\c85t`" 3M0t ! et2 N1zpFXH1$OMIENDB`muse-4.0.0/src/share/scoreglyphs/flags32d.png000066400000000000000000000010121404105311000210110ustar00rootroot00000000000000PNG  IHDR 8sRGBbKGD pHYs  tIME ztEXtCommentCreated with GIMPWeIDAT8}=kTQfQ (*16U>61&eJ*[Oc%BD6bʷ4 86sw[fyf0Cp^w̵>M\j4AWpvq&$g̰*^W37:ktXvnfe kķOk)I}kys/fqZ2,nGē4G +S rbM~Vnmv:Yj)"v3">Ժpٯ Oe?mb~Բh)"vCTŌx]WiRJ8Lۀ;Jo[v,Fn/r睴 ạ]?U}8b6c|A ϱ nʉ3 5V`ܶ(X_L2bgTXGP1$`1u@NѮyvN='9m)IENDB`muse-4.0.0/src/share/scoreglyphs/flags64d.png000066400000000000000000000011101404105311000210150ustar00rootroot00000000000000PNG  IHDR 2RsRGBbKGD pHYs  tIME tEXtCommentCreated with GIMPWIDAT8˅OQϹ53tjnlV" EVXذ6aA6c%QXI)bd7(e,$-9Go;wN==9#" l lXzZ*ˬ&SJC8> C}lb b^a5F+SJcHaFxpoѪCS3jP "b> ?-͏85E o0Rښw؍^T<\g)Wc~.0..CusY|2E\nJ s7\baUۮv(ل=n`G<[T{OњYmlԬ+|Y`DL'K)XLl܎oq%"4gTMV_dkIENDB`muse-4.0.0/src/share/scoreglyphs/flags8d.png000066400000000000000000000006001404105311000207360ustar00rootroot00000000000000PNG  IHDR ^sRGBbKGD pHYs  tIME "dtEXtCommentCreated with GIMPWIDAT(υҿ.Q=&WD} w(JQy3PFC. ٕ{)NV~h w؈I-_W K kc'io%h#\N8=c0?fqXXh `-`;g3[s1>.qN078ܔr^fj. ?Kb[C\ WX{<` !%x&3 I >IENDB`muse-4.0.0/src/share/scoreglyphs/quarter.png000066400000000000000000000004751404105311000211030ustar00rootroot00000000000000PNG  IHDR  sRGBbKGDC pHYs  tIME9ҁ"tEXtCommentCreated with GIMPWIDAT}=ja RI.El ;l-X Ώ$)<<ㄏ$;Ix H+65ٚ,V~5|n~b1a `'`WX@ӫd]Z.c1"֛ َ8'3PkZ kG`l. |l1hy]t84,p~TФx/Y^8>u=U\jgrLu.5ni= ӘZJ߮a ؋'Z3RrD#bf`'}DIENDB`muse-4.0.0/src/share/scoreglyphs/rest4.png000066400000000000000000000006571404105311000204630ustar00rootroot00000000000000PNG  IHDR ^sRGBbKGDC pHYs  tIMEG`vtEXtCommentCreated with GIMPW IDAT(}ұ.DAVBDVD! K# =RC!<#  *ZAAhf6IsN<)0S;LPCtfR:FpsU#n*TF'C Zu<dyDPsy}4J;|AYNYpSP ˲0F3ga el8tIENDB`muse-4.0.0/src/share/scoreglyphs/whole.png000066400000000000000000000006201404105311000205260ustar00rootroot00000000000000PNG  IHDR vWsRGBbKGDC pHYs  tIME(tEXtCommentCreated with GIMPWIDAT(]ѽ.EQo JG'! Z B< *W$_ #ivf֚k֤PFJiK-N9" 8=twa'7n{Lze,Fn U3=Upn0VaS&uDE> ]bJi~[>5{d`=}bUbי&maK` ")lZ~{ct#ssʎ<IENDB`muse-4.0.0/src/share/scripts/000077500000000000000000000000001404105311000160315ustar00rootroot00000000000000muse-4.0.0/src/share/scripts/CMakeLists.txt000066400000000000000000000026001404105311000205670ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file(GLOB script_files ConstantLength ConstantVelocityForNote DoNothing CreateBassline Humanize RandomizeVelocityRelative RandomQuantize1 RandomPosition1 RemoveAftertouch RemoveShortEvents Rhythm1 SpeedHalf SpeedDouble SwingQuantize1 TempoDelay ) install (PROGRAMS ${script_files} DESTINATION ${MusE_SHARE_DIR}/scripts ) muse-4.0.0/src/share/scripts/ConstantLength000077500000000000000000000062401404105311000207140ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QGridLayout, QLabel, QComboBox class ScriptClass(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle('Constant length') title = QLabel('Make all events of length:') self.titleEdit = QComboBox() self.titleEdit.addItem('1/1',1) self.titleEdit.addItem('1/2',2) self.titleEdit.addItem('1/4',4) self.titleEdit.addItem('1/8',8) self.titleEdit.addItem('1/16',16) self.titleEdit.addItem('1/32',32) self.titleEdit.addItem('1/64',64) self.titleEdit.setCurrentIndex(4) button = QPushButton("Execute") button.clicked.connect(self.execute) grid = QGridLayout() grid.setSpacing(3) grid.addWidget(title, 1, 0) grid.addWidget(self.titleEdit, 1, 1) grid.addWidget(button, 2, 1) self.setLayout(grid) self.resize(200, 100) button.setFocus() def execute(self): testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() beatDiv = self.titleEdit.itemData(self.titleEdit.currentIndex()) print ("beatDiv=",beatDiv) eventLen=0 #get beat length to calculate minimum length of event for line in inputEvents: if line.startswith('BEATLEN'): tag,tick = line.split(' ') eventLen=int(int(tick)/beatDiv * 4) break outputEvents=[] #loop through events for line in inputEvents: if line.startswith('NOTE'): tag,tick,note,length,velocity = line.split(' ') length=eventLen newLine=tag+" "+tick+" "+note+" "+str(length)+" "+velocity + "\n" outputEvents.append(newLine) else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() quit() app = QApplication(sys.argv) qb = ScriptClass() qb.show() sys.exit(app.exec_()) muse-4.0.0/src/share/scripts/ConstantVelocityForNote000077500000000000000000000060631404105311000225710ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson 2020 # Constant Velocity #============================================================================= # MusE # Linux Music Editor # # Copyright (C) 2002-2020 by the MusE development team # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time from PyQt5.QtWidgets import QApplication, QWidget, QComboBox, QLabel, QSpinBox, QPushButton, QGridLayout class ConstantVelocity(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle('Constant Velocity For Event') title = QLabel('Note number (60 = C3):') self.noteValueInputBox = QSpinBox() self.noteValueInputBox.setValue(60) numLabel = QLabel('New velocity (1-127):') self.numEntry = QSpinBox() self.numEntry.setMinimum(1) self.numEntry.setMaximum(127) self.numEntry.setValue(100) print ("setting execute") button = QPushButton("Execute") button.clicked.connect(self.execute) grid = QGridLayout() grid.setSpacing(3) grid.addWidget(title, 1, 0) grid.addWidget(self.noteValueInputBox, 1, 1) grid.addWidget(numLabel, 2, 0) grid.addWidget(self.numEntry, 2, 1) grid.addWidget(button, 3, 1) self.setLayout(grid) self.resize(200, 100) button.setFocus() def execute(self): fileToProcess = open(sys.argv[1],"r") inputEvents = fileToProcess.readlines() fileToProcess.close() selectedNote = self.noteValueInputBox.value() newVelocity = self.numEntry.value() outputEvents=[] #loop through events for line in inputEvents: if line.startswith('NOTE'): tag,tick,note,length,velocity = line.split(' ') if int(note) == int(selectedNote): outputEvents.append("%s %s %s %s %d\n"%(tag,tick,note,length,newVelocity)) else: outputEvents.append(line) else: outputEvents.append(line) fileToProcess = open(sys.argv[1],"w") fileToProcess.writelines(outputEvents) fileToProcess.close() quit() app = QApplication(sys.argv) qb = ConstantVelocity() qb.show() sys.exit(app.exec_()) muse-4.0.0/src/share/scripts/CreateBassline000077500000000000000000000067521404105311000206550ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Staffan Melin 2020 (based on scripts by Robert Jonsson 2009/2012) # CreateBassline #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QComboBox, QPushButton, QGridLayout, QLineEdit class CreateBassline(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle('Create bassline') self.timeEdit = QComboBox() self.timeEdit.addItem('1/1',1) self.timeEdit.addItem('1/2',2) self.timeEdit.addItem('1/4',4) self.timeEdit.addItem('1/8',8) self.timeEdit.addItem('1/16',16) self.timeEdit.addItem('1/32',32) self.timeEdit.setCurrentIndex(3) self.gateLengthEdit = QLineEdit() self.gateLengthEdit.setText('50') self.bassNoteEdit = QLineEdit() self.bassNoteEdit.setText('36') button = QPushButton("Execute") button.clicked.connect(self.execute) grid = QGridLayout() grid.setSpacing(3) grid.addWidget(QLabel('Interval'), 1, 0) grid.addWidget(self.timeEdit, 1, 1) grid.addWidget(QLabel('Length (% of interval)'), 2, 0) grid.addWidget(self.gateLengthEdit, 2, 1) grid.addWidget(QLabel('Note (MIDI #)'), 3, 0) grid.addWidget(self.bassNoteEdit, 3, 1) grid.addWidget(button, 4, 1) self.setLayout(grid) self.resize(200, 100) button.setFocus() def execute(self): print ("Execute") testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() beatDiv = self.timeEdit.itemData(self.timeEdit.currentIndex()) gateLength = int(self.gateLengthEdit.text()) bassNote = int(self.bassNoteEdit.text()) ticksInterval = 384 # default partLength = ticksInterval * 4 # default outputEvents = [] # get beat length to calculate length of note for line in inputEvents: if line.startswith('BEATLEN'): tag,tick = line.split(' ') ticksInterval = int(int(tick) * 4 / beatDiv) # assumes 4 beats per bar if line.startswith('PART'): tag,start,end = line.split(' ') partLength = int(end) - int(start) # loop through events and add old notes for line in inputEvents: outputEvents.append(line) noteLength = str(int(ticksInterval * (gateLength / 100))) for ticking in range(0, partLength, ticksInterval): newline = "NOTE " + str(ticking) + " " + str(bassNote) + " " + noteLength + " " + "100" + "\n" outputEvents.append(newline) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() quit() app = QApplication(sys.argv) qb = CreateBassline() qb.show() sys.exit(app.exec_()) muse-4.0.0/src/share/scripts/DoNothing000077500000000000000000000024201404105311000176460ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() outputEvents=[] #loop through events for line in inputEvents: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() muse-4.0.0/src/share/scripts/Humanize000077500000000000000000000076201404105311000175440ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Staffan Melin (based on RandomizeVelocityRelative by Michael Oswald and RandomPosition1 by Robert Jonsson) # Humanize Midi notes by randomizing velocity and position #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QGridLayout, QLabel import random class Humanize(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle('Humanize Midi notes by randomizing velocity and position') self.positionSpreadPosEdit = QLineEdit() self.positionSpreadPosEdit.setText('5') self.positionSpreadNegEdit = QLineEdit() self.positionSpreadNegEdit.setText('5') self.velocitySpreadPosEdit = QLineEdit() self.velocitySpreadPosEdit.setText('20') self.velocitySpreadNegEdit = QLineEdit() self.velocitySpreadNegEdit.setText('20') button = QPushButton("Execute") button.clicked.connect(self.execute) grid = QGridLayout() grid.setSpacing(3) grid.addWidget(QLabel('Pos spread (+%)'), 2, 0) grid.addWidget(self.positionSpreadPosEdit, 2, 1) grid.addWidget(QLabel('Pos spread (-%)'), 3, 0) grid.addWidget(self.positionSpreadNegEdit, 3, 1) grid.addWidget(QLabel('Vel spread (+)'), 4, 0) grid.addWidget(self.velocitySpreadPosEdit, 4, 1) grid.addWidget(QLabel('Vel spread (-)'), 5, 0) grid.addWidget(self.velocitySpreadNegEdit, 5, 1) grid.addWidget(button, 6, 1) self.setLayout(grid) self.resize(300, 150) button.setFocus() def execute(self): testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() positionSpreadPos = int(self.positionSpreadPosEdit.text()) positionSpreadNeg = int(self.positionSpreadNegEdit.text()) velocitySpreadPos = int(self.velocitySpreadPosEdit.text()) velocitySpreadNeg = int(self.velocitySpreadNegEdit.text()) outputEvents=[] random.seed() # get length of beat beatLen = 384 # default for line in inputEvents: if line.startswith('BEATLEN'): tag,tick = line.split(' ') beatLen = int(tick) # loop through events eventList=[] for line in inputEvents: if line.startswith('NOTE'): tag,tick,pitch,length,velocity = line.split(' ') # position: move note random % of beatlen tick = int(tick) tick = tick + int((random.randint(-positionSpreadNeg, positionSpreadPos) / 100) * beatLen) # velocity velocity = int(velocity) velocity = velocity + int(random.randint(-velocitySpreadNeg, velocitySpreadPos)) if tick < 0: tick = 0 if velocity < 0: velocity = 0 if velocity > 127: velocity = 127 newLine = "NOTE " + str(tick) + " " + str(pitch) + " " + str(length) + " " + str(velocity) + "\n" outputEvents.append(newLine) else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() quit() app = QApplication(sys.argv) qb = Humanize() qb.show() sys.exit(app.exec_()) muse-4.0.0/src/share/scripts/README.txt000066400000000000000000000064201404105311000175310ustar00rootroot00000000000000MusE midi event scripting format 0.6 ==================================== = INTRODUCTION MusE supports a simple format = SPECIFICATION The format of the scripts is just a set of text lines which deliberately has been kept very brief and should be possible to handle from any programming language, Python, Perl, C/C++, even shell script. Though for debugging reasons an interpreted language is probably recommended. There are two main requirements on scripts. 1. the actual script file must have the executable flag set, that is, it must be considered an executable from the perspective of the operating system. 2. a script takes it's input from a file given as argument and will send it's output to the same file. The tags that may occur in the file sent to the script are: TIMESIG PARTLEN BEATLEN QUANTLEN NOTE CONTROLLER TIMESIG, PARTLEN, BEATLEN and QUANTLEN are there for informational purposes, to make some transformations possible. e.g. quantization, beat delay. NOTE and CONTROLLER are the ones that are read back into MusE when the filter stops executing. These may be manipulated, removed or multiplied as seen fit by the filter. * Note that it is generally a good idea to just pass on the lines your script is not interested in, otherwise data may be unintentionally removed = INSTALLATION Scripts can be put in two different dirs. /share/muse/scripts for scripts bundled or $HOME/.config/MusE/MusE/scripts for user created scripts Remember that a script must have the executable bit set. This means that the script must be executable -on it's own-. For an interpreted language that means the file should start with a hashbang (#!) and the path to it's interpreter. e.g #!/usr/bin/python = EXAMPLES There are a few existing scripts in this directory that can be used as templates for creating more advanced scripts. - DoNothing, just reads the input file, loops through the lines and outputs them again. - DoubleSpeed, reads all notes and outputs them with all ticks divided in half. - RemoveShortEvents, a script with a GUI in PyQt which removes events shorter then the set note length For the moment there are only scripts written in python in the standard installation. = DEBUGGING The process of debugging a script may seem daunting. There is no immediate way to retrieve information about the internals of the script. Currently the standard output is suppressed from the scripts so while running from MusE the script won't make a peep in the console. if MusE is started with -D argument the contents of the data file is written to standard out both before it enters the script and after it returns, this can be used as a means for communication. The file containing the script data is normally removed but when running MusE with -D it will be kept. So, when the file exists you can use it and run the script from a command line, remember though that the file will be overwritten by the script. Running the script like this makes it a bit easier: cp origData testData && MyTestScript testData Then the data can be compared to the original and prints in the script can be read on the commandline. = END muse-4.0.0/src/share/scripts/RandomPosition1000077500000000000000000000057371404105311000210210ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson 2013 # Quantize #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QGridLayout, QLabel import random class Quantize(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle('Randomize note positions') self.spreadEdit = QLineEdit() self.spreadEdit.setText('3') button = QPushButton("Execute") button.clicked.connect(self.execute) grid = QGridLayout() grid.setSpacing(3) grid.addWidget(QLabel('Spread(ticks)'), 2, 0) grid.addWidget(self.spreadEdit, 2, 1) grid.addWidget(button, 3, 1) self.setLayout(grid) self.resize(300, 150) button.setFocus() def execute(self): testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() minSize=0 # fill in when we get the beat size value outputEvents=[] quantLen=0 random.seed() #loop through events and quantize to the given beat eventList=[] for line in inputEvents: if line.startswith('NOTE'): tag,tick,pitch,length,velocity = line.split(' ') newTick = int(tick) # apply randomness to the note position newTick=int(random.gauss(newTick,int(self.spreadEdit.text()))) if newTick < 0: newTick = 0 newLine="NOTE "+ str(newTick)+" " + pitch + " "+ length + " " + velocity print ("oldTick:", tick) print ("newLine:",newLine.strip()) outputEvents.append(newLine) else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() quit() app = QApplication(sys.argv) qb = Quantize() qb.show() sys.exit(app.exec_()) muse-4.0.0/src/share/scripts/RandomizeVelocityRelative000077500000000000000000000061571404105311000231330ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Michael Oswald 2018 # Randomize the Velocity relative to it's current position #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QPushButton, QGridLayout, QLabel import random class Quantize(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle('Randomize note velocity relative to it\'s current velocity') self.spreadEdit = QLineEdit() self.spreadEdit.setText('3') button = QPushButton("Execute") button.clicked.connect(self.execute) grid = QGridLayout() grid.setSpacing(3) grid.addWidget(QLabel('Spread'), 2, 0) grid.addWidget(self.spreadEdit, 2, 1) grid.addWidget(button, 3, 1) self.setLayout(grid) self.resize(300, 150) button.setFocus() def execute(self): testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() minSize=0 # fill in when we get the beat size value outputEvents=[] quantLen=0 random.seed() #loop through events and quantize to the given beat eventList=[] for line in inputEvents: if line.startswith('NOTE'): tag,tick,pitch,length,velocity = line.split(' ') newVelo = int(velocity) # apply random value to velocity newVelo=int(random.gauss(newVelo,int(self.spreadEdit.text()))) if newVelo < 0: newVelo = 1 if newVelo > 127: newVelo = 127 newLine="NOTE "+ tick + " " + pitch + " "+ length + " " + str(newVelo) + "\n" print ("oldVelo:", velocity) print ("newLine:",newLine.strip()) outputEvents.append(newLine) else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() quit() app = QApplication(sys.argv) qb = Quantize() qb.show() sys.exit(app.exec_()) muse-4.0.0/src/share/scripts/RemoveAftertouch000077500000000000000000000026431404105311000212460ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2014 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() outputEvents=[] #loop through events for line in inputEvents: #print (line.strip()) if line.startswith("CONTROLLER")==True and int(line.split()[2]) == 262148: print ("skipping ",line) pass else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() muse-4.0.0/src/share/scripts/RemoveDoubles000077500000000000000000000036671404105311000205460ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson (c) 2013 # RemoveDoubles #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2013 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() outputEvents=[] #loop through events and omit doubles for line in inputEvents: if line.startswith('NOTE'): tag,tick,pitch,length,velocity = line.split(' ') found=False for outline in outputEvents: outtag,outtick,outpitch,outlength,outvelocity = outline.split(' ') if tick == outtick and pitch == outpitch and outvelocity == outvelocity: found=True break if not found: newline = tag + " " + str(int(tick)) + " " + pitch + " " + length + " " + velocity outputEvents.append(newline) else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() muse-4.0.0/src/share/scripts/RemoveShortEvents000077500000000000000000000063401404105311000214240ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson 2009 # RemoveShortEvents #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QComboBox, QPushButton, QGridLayout class RemoveShortEvents(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle('RemoveShortEvents') title = QLabel('Remove events shorter than:') self.timeEdit = QComboBox() self.timeEdit.addItem('1/1',1) self.timeEdit.addItem('1/2',2) self.timeEdit.addItem('1/4',4) self.timeEdit.addItem('1/8',8) self.timeEdit.addItem('1/16',16) self.timeEdit.addItem('1/32',32) self.timeEdit.setCurrentIndex(3) button = QPushButton("Execute") button.clicked.connect(self.execute) grid = QGridLayout() grid.setSpacing(3) grid.addWidget(title, 1, 0) grid.addWidget(self.timeEdit, 1, 1) grid.addWidget(button, 2, 1) self.setLayout(grid) self.resize(200, 100) button.setFocus() def execute(self): print ("Execute") testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() beatDiv = self.timeEdit.itemData(self.timeEdit.currentIndex()) minSize=0 # fill in when we get the beat size value outputEvents=[] #get beat length to calculate minimum length of event for line in inputEvents: if line.startswith('BEATLEN'): tag,tick = line.split(' ') minSize=int(tick)/beatDiv * 4 print ("minSize = ",minSize) break #loop through events for line in inputEvents: if line.startswith('NOTE'): tag,tick,note,length,velocity = line.split(' ') if int(length) >= minSize: # only append long enough events outputEvents.append(line) else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() quit() app = QApplication(sys.argv) qb = RemoveShortEvents() qb.show() sys.exit(app.exec_()) muse-4.0.0/src/share/scripts/Rhythm1000066400000000000000000000051101404105311000173050ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson 2012 # Rhythm1 #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() outputEvents=[] BAR=0 BEAT=0 BEATLEN=0 QUANTLEN=0 PARTLEN=0 hihatNote="42" kickNote="35" snareNote="38" length="1" velocity="100" #loop through events reading configuration for line in inputEvents: outputEvents.append(line) if line.startswith('TIMESIG'): tag,bar,beat = line.split(' ') BAR = int(bar) BEAT = int(beat) if line.startswith('BEATLEN'): tag,beatLen = line.split(' ') BEATLEN=int(beatLen) if line.startswith('QUANTLEN'): tag,quantLen = line.split(' ') QUANTLEN = int(quantLen) if line.startswith('PART'): tag,start,end = line.split(' ') PARTLEN = int(end) - int(start) ###################################### ###################################### for ticking in range(0,PARTLEN): if ticking % (BEATLEN * 4) == 0: newline = "NOTE " + str(ticking) + " " + kickNote + " " + length + " " + velocity + "\n" outputEvents.append(newline) if ticking % (BEATLEN * 4) - (BEATLEN * 2) == 0: newline = "NOTE " + str(ticking) + " " + snareNote + " " + length + " " + velocity + "\n" outputEvents.append(newline) if ticking % (BEATLEN) == 0: newline = "NOTE " + str(ticking) + " " + hihatNote + " " + length + " " + velocity + "\n" outputEvents.append(newline) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() #testFile2 = file("/home/ddskrjo/o2.txt","w") #testFile2.writelines(outputEvents) #testFile2.close() muse-4.0.0/src/share/scripts/SpeedDouble000066400000000000000000000033131404105311000201470ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Staffan Melin # based on DoubleSpeed by Mathias Gyllengahm 2009 #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() outputEvents=[] #loop through events for line in inputEvents: if line.startswith('NOTE'): tag,tick,pitch,length,velocity = line.split(' ') print (line) newline = tag + " " + str(int(int(tick) / 2)) + " " + pitch + " " + str(int(int(length) / 2)) + " " + velocity print (newline) outputEvents.append(newline) else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() muse-4.0.0/src/share/scripts/SpeedHalf000066400000000000000000000033011404105311000176040ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Staffan Melin # based on DoubleSpeed by Mathias Gyllengahm 2009 #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() outputEvents=[] #loop through events for line in inputEvents: if line.startswith('NOTE'): tag,tick,pitch,length,velocity = line.split(' ') print (line) newline = tag + " " + str(int(tick) * 2) + " " + pitch + " " + str(int(length) * 2) + " " + velocity print (newline) outputEvents.append(newline) else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() muse-4.0.0/src/share/scripts/SwingQuantize1000077500000000000000000000105451404105311000206550ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson 2009 # Quantize #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time from PyQt5.QtWidgets import QApplication, QWidget, QComboBox, QLineEdit, QPushButton, QGridLayout, QLabel import random class Quantize(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle('Swing quantize V1') self.beatEdit = QComboBox() self.beatEdit.addItem('1/1',1) self.beatEdit.addItem('1/2',2) self.beatEdit.addItem('1/4',4) self.beatEdit.addItem('1/8',8) self.beatEdit.addItem('1/16',16) self.beatEdit.addItem('1/32',32) self.beatEdit.setCurrentIndex(2) self.spreadEdit = QLineEdit() self.spreadEdit.setText('10') button = QPushButton("Execute") button.clicked.connect(self.execute) grid = QGridLayout() grid.setSpacing(3) grid.addWidget(QLabel('Beat granularity:'), 1, 0) grid.addWidget(self.beatEdit, 1, 1) grid.addWidget(QLabel('Spread/Swing(ticks)'), 2, 0) grid.addWidget(self.spreadEdit, 2, 1) grid.addWidget(button, 3, 1) self.setLayout(grid) self.resize(200, 100) button.setFocus() def execute(self): testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() random.seed() beatDiv = self.beatEdit.itemData(self.beatEdit.currentIndex()) minSize=0 # fill in when we get the beat size value outputEvents=[] quantLen=0 #get beat length to calculate minimum length of event for line in inputEvents: if line.startswith('BEATLEN'): tag,tick = line.split(' ') beatLen=int(tick) quantLen=int(tick)/beatDiv*4 print ("quantLen=%d beatDiv=%d"%(quantLen, beatDiv)) print (line.strip()) #loop through events and quantize to the given beat eventList=[] for line in inputEvents: if line.startswith('NOTE'): tag,tick,pitch,length,velocity = line.split(' ') # over quantize lowerBound=(int(tick)/quantLen)*quantLen upperBound=lowerBound+quantLen lowDiff=int(tick)-lowerBound highDiff=upperBound - int(tick) if( lowDiff < highDiff): newTick=lowerBound else: newTick=upperBound # apply swing factor to every other 8 beat print ("float =%f int = %d"%((float(newTick+beatLen)) / beatLen/2,((newTick+beatLen))/beatLen/2)) if ((float(newTick+beatLen)) / beatLen/2 - ((newTick+beatLen))/beatLen/2) < 0.1: print ("adding swing to:",newTick) newTick=int(random.gauss(newTick,int(self.spreadEdit.text()))) if (newTick < 0): newTick=0 newLine="NOTE "+ str(newTick)+" " + pitch + " "+ length + " " + velocity print ("newLine:",newLine.strip()) outputEvents.append(newLine) else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() quit() app = QApplication(sys.argv) qb = Quantize() qb.show() sys.exit(app.exec_()) muse-4.0.0/src/share/scripts/TempoDelay000077500000000000000000000067641404105311000200370ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # MusE external midi processing script # By: Robert Jonsson 2014 # TEMPO delay #============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 2002-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= import sys,time from PyQt5.QtWidgets import QApplication, QWidget, QComboBox, QLabel, QSpinBox, QPushButton, QGridLayout class TempoDelay(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.setWindowTitle('Tempo delay') title = QLabel('Delay time:') self.timeEdit = QComboBox() self.timeEdit.addItem('1/1',1) self.timeEdit.addItem('1/2',2) self.timeEdit.addItem('1/4',4) self.timeEdit.addItem('1/8',8) self.timeEdit.addItem('1/16',16) self.timeEdit.addItem('1/32',32) self.timeEdit.setCurrentIndex(3) numLabel = QLabel('Number of delays:') self.numEntry = QSpinBox() self.numEntry.setMinimum(1) self.numEntry.setValue(1) print ("setting execute") button = QPushButton("Execute") button.clicked.connect(self.execute) grid = QGridLayout() grid.setSpacing(3) grid.addWidget(title, 1, 0) grid.addWidget(self.timeEdit, 1, 1) grid.addWidget(numLabel, 2, 0) grid.addWidget(self.numEntry, 2, 1) grid.addWidget(button, 3, 1) self.setLayout(grid) self.resize(200, 100) button.setFocus() def execute(self): testFile = open(sys.argv[1],"r") inputEvents = testFile.readlines() testFile.close() numDelay = self.numEntry.value() beatDiv = self.timeEdit.itemData(self.timeEdit.currentIndex()) delayLength=0 # fill in when we get the beat size value outputEvents=[] #get beat length to calculate minimum length of event for line in inputEvents: if line.startswith('BEATLEN'): tag,tick = line.split(' ') delayLength=int(tick)/beatDiv break #loop through events for line in inputEvents: if line.startswith('NOTE'): tag,tick,note,length,velocity = line.split(' ') outputEvents.append(line) for v in range(numDelay): outputEvents.append("%s %d %s %s %d\n"%(tag,int(tick)+delayLength*(v+1),note,length,int(velocity)/(v+2))) else: outputEvents.append(line) testFile = open(sys.argv[1],"w") testFile.writelines(outputEvents) testFile.close() quit() app = QApplication(sys.argv) qb = TempoDelay() qb.show() sys.exit(app.exec_()) muse-4.0.0/src/share/splash.jpg000066400000000000000000007437111404105311000163530ustar00rootroot00000000000000JFIF.TExifII*bt|(1 2iCreated with GIMP77GIMP 2.10.142021:03:31 21:40:33Created with GIMPDJ-JFIFC    $.' ",#(7),01444'9=82<.342C  2!!22222222222222222222222222222222222222222222222222" }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?wWQ41*ʹg_ҦE(ٰӏҹ٬-zk|(x|n_ΡD 8ٸ}^[]XE W-Et-2|cqFpvaOU+A{d`?!F5x{˳y%iD牗sR\sF=)d `-!ꮙ%xy+uQ=V^\}ԋYbI_'qCPϡpǕKK|HcۑڱQ4!%g[V+G&rc<#}3Ykђ$_R8TD!I8 {ަ܁%vP H 3OoU*'C?ֲ/[)r2_ҷY=J暺Ff;8br@e >WynQKܼeÐ rp}*I^Dtx9y (uk1HFFA#h;[2BeFVppǯcsi/\Wc]zX2<@*P:aY.VB2rQC,u5{6 =:yɵK ;ORK&562:M"IvVB  .]ㅾ`1&8ѽ}G:\jIӯ/m[MVI~\Oۼ2]sg|q 0x;j-pg1_:ʛƮԍဥYkfW*A}v4$Jr!cO"bc)Eٝ>;VIc6Ri6 vEmhz$@ qHsðX#Vه,ʕ& N|Mco)G/m8R{;kY59/ܰC~kBi.T+I[O>C&H4f8c'en8 ^2ڹ$ mp~UWѩM=` їX{"I&I]zsn8nSs,_iA!05ͭ-+*G><ֹeQMsY#T쵿sy Klc߮UlpE%Ēo.aG$UIoިI1U$mPy<(gٝu)Suw}^/,܉XR2zK؂J#*;#̉y7Z6Z5Ԩҏ1QB8^' RmFMƜtzݳw#pWCCgFk6ݕIa7$4n#}kif pFӸЩZ*1UGoW~Lμ 9ܙuQW5 X25< }r=sI:,yfp:q֪[R'VC1o|1]t1*?I^]NmE 37X˦1glpvX.WxXc$u= _ӯVY BǛ1ZLjPp?x s׆jWMS3EF KidSr ƻ'8:uoy$P5H Z%z״}.eB+sCP~߸ VIɫz2jje}"*9e~~>4 vkYJUʯ 'oFOk_%ո,ۘV=<=Ucfan+x30ؠ< 3IخJ@\Gtxu(lFG_!3f⛿5E4S֒erϽO <~b= F~4-vboc汧ͱ5 bO[CXwKL裓GWY%x0=##^wuK_*-1,JOa]t,bkbk*OyON0J[NԱcm'zrh/ԦtzCti`x%{7ws9L Ѐ1W}8Cu!7FB~cA= wWyE@q9 }̲pa\s ;Kd*tpΎib>ʣ;}N~a, ,2qG?u֓BL]sMv80rzogwTpqFuV(崛@L ʏ5 ݃K RSClK%1`Wae"H3\kJ橥%Vy.db''ιIk5}6^̅G0 쬤{׫ά>8*rZ2tʰ 54W|Yy?燚9_\&մ[3G0*Q9+=k17ڊ 1,rGEz:IW2ms+Vq׋۽˺RyVs'I2 8b@>p;i7O ny'$UsǟlrFV丐>YM]֥W;U:6m~\fSgؓ'Ҳ.tYLrBvt#5'N\λF>T/4[) B?r.p_qrR|Dp>@ǘU[#'󭛟@d*>ߨڬ2C%^ #aЩ ,wR0*ڥ#m/VےB>e TZy!h\7р+\%4۩s/хosy[VSjYGM 3,hp\?z<5oY+ NMj?6[〙YIdvPXj"ݤYN #@ı$\kZr-4[[2rA ԟsSjsi <62Qߚ穤.Df#ci_KAkWKs$gT~)HovFz.3+ ;VǓwUln=xxMޟR1|<z~ョ'fTukH#mdh' c^XRM픑|OXjvzZN8%Q7ݚ {0>~zIhoB+n>^Y%716Jɻ-޷}2m4qj(UNV%7V˩t.x]]ڿ)^ՔUVI39}L-cʐZ\V݌~g^#QV qV5y;:·U/Tjl1x}9as{ZM_R`&-;4ɬd"O cf++z7ݺƼKi <^{s1xҨ/o4T8R|~cHkn"*򾆶=M4lAV.SqXLhTևG bk͋/到zj>tFI|:AEu*В{2K1@16<ުVMKj#]ة#ױ;/XBXv/^rZ<1K bK`OE{UvWr{nof/ YoxdG_.p8N~nHzP<8Jv*J( M70h,wڎZt^jn;n-ysoG¹ԜB4S#l <t*f;͍ݼ|2~h,֌q򥫼,Oޘ*#];S.YY 3*V GV.{G),N:)U#zyi^12"DL=bY rF1+)ɥ׻὚ݲUA{?]A$+RQMcx # 5{ɲXfFp8&Omqsk?yb@?om&?L4M/ʤ.)=CFZN%*RrARN$$ AoC$l]5R[;LsE$n:W L9J M|QZGb;XCڵ"{艴XP U;fA9G cJtG[mCj-Ķ[bR{їqZ ,RHW+ӤKȃ.z91z*sͱeC4j\XN4N  :@VH|K~DqH,C-wS=9.Ag_lzNYeqM C׵/SadNzx^9p$IGʰިڸu$k'&D\r=alsh\Fx%9;Xw w_InZenZO Z&# "IemKbK@MQCUBH%І.0z`ol7y] S))t8Q&d)5<6L 'OyUcBN$,"F`>Z(>?z&Gy8]I5ZGEv }2$aꓦ1#P4d:/5\빥aSNvkk`8Enu8c$w15Ԍ2q&lNƥ}cV.ҮVk_!A6֥mosΡGRDאN,Pٞyi{{y#~lV )ULX>-լ8E=~|g%e`YفkrJ{_Wh$rާˑ\Ο44n]~$KY#8Xm ~F+Ax ݔ}SN[){[ČQܙ! G*+W湶iηHtKm*16>o<I MRkk(қB{o) %TՇRմ|eu6 zش ўHr1CJ=~}+9uhJ$`&iX]X_FrV2?/q<xvhqދ"T2QIɥ@IQ&Dz_ir5,R88tz'Kᖝ?aO[7⿯3:=~Vs-Qp3]{m* mb92 {9Ѿf5?y4f ];VDg>tH>9Ԅ/ 8'] 5-Q}~qKw2X}N yE 7(·zXjH6c${ZU^G`4kvgouaek1pyڼYƁ4J6,1"G.z{{WMɭo:_yC"<`."[<"bsaΕq#Ahұ66+W'C|tJΨNWV; *K-[}ܪF2+W/QW{&v&N2Ik(bU(4,=N?&[+S8!A)E@aYk75~8n76t`t?qHqm&7DWA"sQMZT%G}cgch7J6Z1#.$hxo1Ǣ~ɐ"I]Z1c<>&R_If/ U^)-u)rv˳*5]L?錫'Ma9N?<IFpKUcZ+vtm]@]F8+* V䌟1ʭ'r2I!?# %-9k&R<.J 9mnKlX= ?d u gB/u{g c8ƧٮJB_˳i .a2O֖_UdH9=jlzkw9Ǟ-ŬT$ @"VʢTߩ _\ȵgLgm(11Z^&KD*4G9OXun;*쯟sB!Ai>ps 0}thFTOy\xRIKY@:z#sU]Dz ai.Tc(m_=1~uE2G=#Xp?3Q,)<Hu%̶.|GWKmjCdoL9s/8KEs,!&57毢0Y=K'[2t݌6GQZ|Qvd([*q%v?W㹇SC#%dq 6MrUKKS>eoW{wgaޡt0-ы&/?>ETT8]0k3%<@g)=ZrfzXEZPV {CN:6p2$avg'-/{yk&I ;y=+/A%*P=}+Tk$PXl~+YÞq._MmsuvCz {n:p=v1Z)'S;3WKwYiR 2zJm^H{v!Mq4#xO|pЩMB1v<Ъ93n6XpA?ʮe|o?߸PGm'sV: uHPۭ+`~sD-{q ~}0xjCw؎0?BĻz)1+]i9$_[A>5v Q׃]^(eg`5a`W5;ΐ)b+OiyaEEryvnZB>[ +,~k]ܬ g'['9^9n` 8v$v=QgGh{2['BkaszxRw:[zRXBb&KzՋ?/e2}l%Xk+q+-bt'por*)4ĹS%hLg?wr<>n3Et`fE%4P䚱lE@n]XElJ[?'fz9v&-Y̻?ũ?Wm%ou;{Zci9+IRU'_*\қuc*x5WK[:?%r'˿_r3o?RdIn~t]fC/{-Z6v2Mbd!L>$?"ϽcR)'}V]J\R[II1?R\7J/|C'_տ:U޷XIl·W*r^·}Tz?U@c򥓹01*l:HP϶aIn#oΧRtԎUQCj}+U6y،.g>}ܟjߝBڜys(5֯5Fbriq?ҞU_Jwz(~d˶pϽkz6]sBv"aUd{djq+b'$=~'Zq20ǏʳC<*f7~ɯк/s'P ש?Y:=X_ ($嘚XmgⱰArZ'5$st$TWE16ICC_PROFILElcms0mntrRGB XYZ 3acspAPPL-lcms desc @cprt`6wtptchad,rXYZbXYZgXYZrTRC gTRC bTRC chrm4$dmndX$dmdd|$mluc enUS$GIMP built-in sRGBmluc enUSPublic DomainXYZ -sf32 B%nXYZ o8XYZ $XYZ bparaff Y [chrmT|L&g\mluc enUSGIMPmluc enUSsRGBC     C   Created with GIMP vvMMe'3UYթY*m<(rmi#謞__8sw3uwrw̹ݼSɨbkmt2U*L%\ y֝:qoƤctUM<3懲Wvtykx1`)֝5>}ViV[wŅ02zX^ϊM|wWbukL^}ܻth> UCd blV{8.+eET}X|7K ]ty{Ėϕ΁elI$I`MqJh ē.ے_tt5}Un,Vh1<ƚJ;ف йߐVy:̽[j{2<ƚiEzu3.Fu+ZR% :jv6.yMNߏ]QNjgz.5z{}9 ݞ<ܑ q%P!h\[<ƹCJ[枚MtZ\nI aĴ`'"S٩[uC&.dl wmjѮC].my}X ~usLu]WosGyy0*Z:qgٻT&1\h"-I+\i\&{ytyi{95WNn嘶|/`ԄQ Jqh4,Oh0>zMs7n7qbO$m]}z|Ɲ $B D$( bMp87QWSέVE>O>$nI"0 q% i:1gU'Nb-#Hu:(%DWIZYKM=7Tv:4y :uӑy9qA;f]ܑlt*fz {:,J%CHR6a>> &٣ɝ8r3Tƨ*3j}f‹z99-p6VT W8?=v{yރ{tm2[B^ޥ2cngCřXVJeBoAȁs}7o~~yev: *5 Ip"*CQ^ ;+W;2쬶Cv⨲ؑH%AU_gG-T@Ro+Ԧl}7mU"og@gE5sax>0yrYZJ\7u>I8g 0wqn׷n]ǶBE-Kox tinY\,UvL@ɻCEq֭_tk13<@ & ND\{4l֛8-QΘShI@arJ;9WvT8EԦm16v4Uw<>S7*Y??/yAYff0(Q4:o(iC=TzW]J}IWoBo>nNv{YΧ(\@U(IB`q5Wق5t=T!zWoRmnlm"UZQeMWׯx3Uʘn D cR읆8˞-u1JDqJf CDXгۈ/{*$?6S@`Aߺ5Jr>8 #} NV;2n o+ʆWIw'% 8^=9 |{{&MtV[PڸUVfTI4=~sGzO%ʺ/ypOaۺsts`]͋n,D@L"*6^w{YX7c4zGϤӂbP@ 1IJCכY @+MH7};ҵp_=*vuYl澓\N3и^ʵǕp@n :8`7?I61L q;|07뜊u6Ĵ]M>~R{?^wڜIzaDܷG2uuye`r+aN .v{pm~WQV_άtKp1 q}#kB~Sw_b`;|eVxz9n9MVG8 7?D}eL['XQ [2B֭ΖPf}@kKQnoGuGGSe5Ƕ;Fe)eh-k.ή 獩4Th0Rn6jls\.?Cu1K uLX>+% )ꑇ6[ldйqڑ^UnWNm=M/#UZC}*C2}r]%(n3` Cs\MJ\)=X߂N@\].lJ`NrDaar4UYPN9OPߘ+ie^>PtKLf4vc5T p"o.F7f&BD`rc;*?Brsiy[liAC )+mki"c%ҟOOYՠBy޽\8 MΨ` qw YY[xs}~{ry=""uynNlVh6\41K@>zO:.Jh!@cy9p.#Ui͚bwG.\jtDR$,$jk3:9׹Gm G3cNL J dClk 3r-v@xAV9EVG"pUHќ70 h}4T:rFߞH(gfuڭߖ$\dYQo>VJi0}3MY ;qD-guWng-5LR1s.rJ+!zݚS<W19XL@ Cʋmo!lWwwzd_)CE xpߟО+AOva[ޮ/kJO>%ZnU$`@ FviAͲT󎯞H;Z{ò&JtT;L\^p=]7Nm w*vcKR!vt=5M{ Ӯ6YjUdVfw@ -]Z;:Ri]YuwϽ~Նjo 3Z|c{$~O˼NH`DD #թn쭎['7`cοO:>wk0^ 'uf:ڕhp &`hPR o)8%}O9_<|lقrnEp'iK:2<5U&%^_j%ɏB5iN,=;fݯMp4a{cٝ. R}YyoWgP^YS.,3:o/#Q:5BWf4h%3q #v_F}](zt=ܬʖquV|[̶KyWLmZ%ǟIi<6U[;oFϋnZpr1}C_@! 5&,N;DzhWcɿUSyע×p7P姍;i6FvD&4,3nP Rǡt̫dwg^uAGǷ 'A 'sнܴ̬ u?mr~v]hn~1 [dwtu7sF ʍBm M%ǔR*|LTʢ- o>ȖUC7>#^mY>%_6{ʩK7]b*_n_Ĭfytڶ^y&FuVdɂ9.v }ʹ&1~nh+am !ĘI&UEѼϺ^r}s_Cߎ]tyQn} 8@re0n޾_&cʶ[m%MJkSRq (SV: 8A2Bi"LԦcuE JΪܫsK!渠ݭiYVݱlYVbJN*.g1)ԾӶժۇ..Z ]j+ez}N{% O៛z\ ՘y?gi ʴ4MIϫVky]_v^c<.sПBjns]L=.ۨ]z6 @L~=;7呅&Ta&a0z%IVvvi_R}G W6(NtTcwWmj˹[ aQtg{uA:A'VM,Z=YZit?ރNZݙ&>ᶫoi˔M5}Nk}.TK-ηKb0I ` NIJw] ֘r2D!r')'D돗K8su٣6au~iZXsMV +36 #$IKzNL)FNw]+3VcSHHI)LVUGmѦxJ+.nOC9`S4im.^Vz\Kن^rՋIU:.:rrHr !$J" T#qJᖻ3 Ο"Cٯo;|W܉N&';w[O3cv4;ovNs|CƱGIݑ./}tvXt{qeO.~hOA;gAg?yG;rr꯲q6\ڄV0UAU{3}_&aיj<(ο*5Qf貵" k Y55)>yX5\\=TFPUUh`D#din%&S%l CP(B9[* lYBWvljnڦo$sO7܅|>t*hp=s # :y?Kuy^Ok<:JCN>co;A#y{ymQWlYZ̤@SF}2Lz9gnKuMx|kn/~grFLI}sW1i IvwKcv]W_xax W<̑f)B %0:GbSVVBy[Tfh,nRkBEsv2Aʞxuky=[,n7ʃvt7R>77]bŶ(9`04b]viu4y{MJUs/h6SplKVh|cOFtg&/Cwj+)4-Eq˧r;ciܔ\]]~B+0Q8Zí2jno~t Ƃ(/G;T2&["H ,2v6ˆfMQ- Hz2 ˪zF?Uvr]\)bٳl9x%8ev6Yj~OU:ͫMEfIU՗^:r]uqt_<޿ =&7k֖4TٻQ=W;R^;\ti0=M7M-8]ڭ\9*=}&UWZwRqY̍81-&E#9 |KXDPWF;i D@'o9I'"kAG[IoS!ٟŅf&e[N"Br-Us`ׯnnQqZٱM~m~&~,4k@WAt"ŠA 0X7d+EW/.])1]8u M0{w=Ie:x`[ ;<;2~w9koȲL.s%s>v4oVh丣{3(\mx+np{2aO^R|9B:::Ч43]y:9Wuylh)7] M>^7yP':䩩QTnnmʹ5v﹎GusӁ; )S]e U˔u.zaW6bFqVșpLFҁruKOqaNo^4L5*^QBm׎}]b>UlR{F3>bDIZsI~kйƍJQ8ӒMZ>]C= ݖ.=V]]38c} ϙ;B,OGz,1lcޚy۳!10``Idщ@y7 *S5ukT3bamot#ST褝(MI$d]aUFgg26դKCyiQsWgBuͤ&1`#Ž?Z>91.:EKh.Qޗd8{L]Na_{j%ӼG|,šOOҒGY;;#=% D34!P,kSpe*+jCf)[ K l3fp9z8ǔrXEi17|W]6nCluEOgks ykµfnuX{<¼f]/n1M~UXvu9-5SKH B0S JDđ0! 6P) bc%JA) eUJ;-)AYVJqM-6fJ lĴ,NԾ؍v:M>5Jj瞏gt;u&IJS¿]u|t/SvP8_:nw%6|_3IySPjD!.=4ӊ"HI B"ЛqhM M 2 (& 7poQl"z*Hgu4$ [ͩ$@OE~%J-U-to5yJ]xA!m՗ƯNL c;[;E+}yޝ+Xd;_'fZD42UCJ5g7{ςZn?z+T%aIIe|YyWW?Q|n.{SW*}򙞏/cnx7!>\m:N֮buX|PSO޻ڜR[ed&UkgkT)d!m]\R)寮r9Ngn]A3:R#}}֥#j\]6%CJ"5xۂH]ٝi܌ׯIjjrzlvN^wg*UM`ނɵيW>VXW/jpt*&mY(ByTMu׀rB,jtF9dz|tt{#oM^9uPqj\Ip@LSJ.IrrB(r.ǥ+HNϪN%kEٓtkrS[ ͨ"B@D91qS|.ibN۰wn 5TJXgmUdĀݍSf<]AA,ռߧZww??s,໧+(j"놫fڥ)5|r)$@(aLDF[!nHlRnRsjYfZ|(&3 嬧4bB@jNv6\C`A^bB>-Fe=uRkM'c9YifOAw2.symLPCYۂB}v[%;3~ S^آߊ\V+mOnR]J%qy\B+9kz*S @a` immη!LI"psضjơk]WkRBa]fEѓSVRܩZ\uB.sEQm$D)3Z_QOb3 fumᒾ:tӾK29xS2u5t-,ع-OL:  #Lء`5 CP7 ֤)KM&nk$k窽Z54V0fy!Y⦗.;[d["ӂDsKMku.yژMح*MZGSѺTt哆f67TI᪟6lYgkF 8/Wk m]Y;:>+甸琩]tbR^JNXtɵ+ U&|8NufJڭ~mJj*a"a 0$ccB `0000P3yJ DI7 JS#gZ?Z}+0bY}1Og2M -rVO2=V2W6We^oo#E^FDjθvTc؁8WhZ3\*p;/,F{ucmOɑѩ(n00hA 0 0@m= DRZih0H RuM&7a{3W[pjUĄ `59NUznvB p %կklO6'᪴bJLI9 m0 0@```֤tk97a.5ٝ,ʶ``(jo>uܢj$ds]L# A\:lUW?Prr6Mjk&t:DiD"BDI$elG4h$܍+N-)%$A5'c|Ɏ.M:+ͧiyyejYu$ 2@j$`P1jnSݦ{@=;E,"Q!H&ĒX2""r[iH@@ǫwOl7*t$RE-<< STfjF08z:eWНOV+TY᪗}xVs" 2۩$8 ƒ ơSEw~9t#)f,5aeiRI H *Dd1$A3;M_a_e9(*KAZZ"!jSv7ʯtk *YݴWۂ=L QgfYAY4DHDȖ 'ڲ^wmҲ4r2!YCJ"$B6@d !4c!Ͱj(ƯhrXy@4@`B!Q7u][~ʎխ=fxip̍f8sRJ $@D8yxвrC\SWyHhH`в0mO],f$"`FkSzlsts"iB Tk1<"`?3! "0#$15P%2@A4&BL6RI}#{DϚvjP8|{דQ *Gգa7#,lYU8E^tJvp lRF XX61}Q tbBJK)M}!4%`AOA-N VcpKC+ZszzjםqH3XË&dZhZh( Z+ğfB%WؼOwRJZM/6 i5ʒvMBCe_1ǯeXfPk匞LYd;0M_W'_$hi'tq-BaUZgeZhW^uW>[""Sti-4fCFM!NIگQ=[co5xT,M-{Dž%k4\=" X 2vdfCj꾿$n߮2҉wu`^ݓ^$e /FbNOKu2ere[.z r2DE}dE/YTRD6> FiʤLg|:9+jsnjH !S=I|Uʑf ֿ;Qb=7Dr0J P9 ݤ;ӃӼ) JǯzLY%$][O̢[1!ᴳǜ52~EVw[[[;[[xAuKQ9S//\ U9gЕ[mUtTktP'ei3*691wUSEfhsi7A4ETF3(39 3U^UbGًx'҃C32 ~j͎\msJWNcYQiΊfle4NߣKKK_~x׸<|rs7L@BYتp/Y䫗2 Zȥړ?:satb,&nRf Apڐֿ,'*=mZHn]z#غ+M$ =B3A- ЯaH(lV4ib..ed.; kqHzLcPrG\83iBAp=s/dِQZZZZZZZZZL.+kjs|]Ϙb#m=F傧m厔(SX&'cIY$I֔bB~,L 3$mpnlݔI&P)ޱUw q K8Bis&f=tLYZeQ fl\\G%0a'8 ɐW˖k,)Wm<+eY4|6aavU5*1pV'g\Wq\WN3#@bFUy+6^>% /+ 3!x9X#y) GhQkUHZ\؛!#! _,hLwyFG2G*+jXXrILQ2yR[cOYN!RÉ@Fa^>.DDw%VۄڍRxܗ+9)0i3:ݕ{ʙboX,qqPfUeG|v]]8zdX\Z. xvNvI%uN:Zl13X1|0qkůcMOLzyẽ>/YZocR,}*Lӧ-$9*伊snxKȰ9t&6P( 2,YIFJC>.U/(IA#Tyb ~ܞeRS8O͔3zZXRtnEq\[deNzD69wX:04>V0GZֹ%i8A;jcN;QGg^G󥥥ci*H_NJ]FAV,<nZZ^18)'ߘ{,l ;4bSmbO(+_(+SO,QgC{ˇi(; y/ս--*jCI0νJD&vW8l\00Uɤ7U|K(J+?Þ4),7Ȋ](:e[(c4KKK?DSSN?W `.')߅,+ M1ǖg&l2Y4S& 'CCki‚'eiwzN9LOCcaG]xlcZU/Yiia&UFo/".됩YLRݐ|q: gvLST" z,a<Pzĕy=:X<_f)ixjJICY2,>Hث^GK1d X{n8RBB+ҳBa&RdGhsUQKՋ)$2x. ـ{tx(rXaY򳓂yX\<CViSļ+;, خ 7qw;|2l5 +.>ߖ@b.O}3-#rLi2cI6B* Q?Uzꪨ˶UH=&3R^?B/QƦ#DpEΧEh\)δq=-/QzA)mJ35^c ԉQ7 ͢JO k6b,4Njm^zQz^E/Qdԓ (:fUˬDgl 156ڔ]k2h"rP^Qt A063 K zH10pɫB ,uQ BCdtqbǡt.,BYr] е]*<.bz#pM,:BC#4OLtBj0iЬfV;B492EN&% .>L)l!+r"_\:2zB] t.кWBVAMI?Vj 0Xz/Szu!ɖ~xrDlĬXw QLJg#e ;LFL&QWC f :plZ.؍>Я3o7fƗBW1P] >J9ۯ гuF vT|4ra?кJu{VX\кN xQ4\K?u'+[ xJ8R 0l Y2/"*Y YPxLq6dX'LRXad`2${!LyOd.Q[L0jG}>bȮWg+XZq♔T]xX;UyzSsT.zcRRY\aVٛ0*sǔVP:q7<`5CWYU<`țPk^xMK (xC Yt̝wWUUz^[U&4x-ÓG^*q?uVT)C'poM>gNowU[6^U^ڱ+!%Yio;gfj^[v+ymOW"6⿯"S qkQPnrҝxEȕkYq`foTQO"XWM1c @Qh+J H|UVu QÆK%DrqpK |}C^*px~"J3x2VۑT^K]uNmruUǬH1&rW3i"]1NeR9Dt.@g٦IgTؓbGa$NB70DrK4jPiS.2@WlI@Nlq )(!g$I[S'.nrTVAL1NLvhs<8<,ԢVE1tym8wI,L3v抜SΡ%^ɸ޶UV,i XASI_H5 L+/a.Kׂdɔ]E49/ԧiXXqfSpk乮kb(DgCu_%ULlŪgz<{nj9JR7ddlQ;c$CjXH9)]-W>/Z.4fFzeJ^V.1&A.ï4OWaFKaD],ʸ4ca2 60D̤='eB*59bhDXQ dE ϯвz\TçMe0wiM_Sg[M$\5v&Hv|-[leշxiqOw[[L떔f%P3(fA8*~KG ,>Xb2 =,?+h~# UǵlBv8}wɐqٞ3bodoq졘s@՞Qr"{4*)T$EI[ KZR|fmuH+(3Jbr$)ĕwvQ'm|~VMU# LN%r\:TNRMq>*UJ mr]6-X08b3m_IzlgGZ.47N<_rVU+-eְ)I_20IO k 钎ZZ)*2'OY$m/ƹ_Jz@X0/ A{&FeO?b=6EY`QJYMNx?\fUsk0\!v.Fz',FxS4IͬXZCh1['IQc2K)#&ъ!yQO"''-ҹcS)<̴̚l$ t<^V܃fhqFbҋ*8Mг߹$Ӎ<UNogA9?es) ❿K!!{KWx1W-ml8eKLi˞8ΞOJesc )p]JPvNK_Ij.̪gUMf\[7R8ImiۦQhKsX?uɪŐXٱY>G Vd4ShIvT0uׂWcfV)R#!\/|TU"rWb7%5c>6SpR&Sm2v;yVX}gTmx'ⴚZmm3ɓMIA#q[u3\-]NʤY K7.iɹ1JjB)U1g+D ld ,G0;isʶ`j@Av+m<`䚉@H<1zp\z%Cc#S3a7qrYr b6A3Wl>*zב4DLj#)~BJ?sf,|Gѐ,;iX'yP1~%liqZMlba ~;.S(wxT}^2wqEœ<% Oo.iVSpiHKӉp\VF:i&#I,Ћb*YDN '\jmI1J}=/,08bv]C9k(%ʬau/6G+#[MҔdˏ(ح3H6˹RT/z6 Tcj@$dxXV//Zəf1e^'ZqQe%_(?fQ:]{dn3ȎAzWB̘|*g5 bJr"R8pUI♸9E\@ӈR!egԣ%;:vv%cZu#oeb}'j,VnNN'\qtLX5y}跌(ױ1aQ K8v9 >8MG&x)*;V58rWih,=R,Pax\\%$|uhsr^-f u'U*(dxX,~bK<:zU8t4_\w%njVm60wGI^C*Ug!#1Tl(ZvTwbF¡Dg݁Z=C)O(sO I5,J#4o(5JйCR{aծK@ɇ''8rpeqPreCv.?Nrt!LNCś5c;*~Cc_!cgeCbɕA9y7o.Ku14sLge鋤u;vMn\v٬%c57 jQ8 |y43`-; Y{נ/$uҘ'z7O\]߂ӭ'.~ĝ9^S%h9"T%XcYֽW<9.ZgiDX!|3,k^!z1x.ݫ9,s6w_ͭcV6Oɧ$ & IJ8wwZktT) ,Ep+)O{Q>: (NssNBMai2 ȵK6X̩{ ί\%,&b /5ľO\:n\!%`-mf/Fgy(R^5Jei[[[\%s\Mv.iDT-]U3(C11jv2Xo#1X6" +ewǭUD_Kz-"GOqZZ4M4 ㇵȴRGVv*;tvQاrX%41S1lCzSzG?Jҕ|e']I]0TKG!8K:fqG!OB7dBōq19dI6x rA|/ӥq\W}iik}6.KibZ rNR]us*<k /xkg,d}Z03%F1/|iW2Hn+k昊ݨVaM iqyS A-KW1ߌg/c!iIJ&.?$RU7K왞M؃`ԉ`Mysy% r;(&xDvUSXz7I9W$N3=g#7 "xd(fJ 7aI-Q |*׮[R̸]ku.ҺSKu.ԺZ.+ⴵ6&"b&2ҁl~Cvo(`k ,4ʏJUVȒsqYSE P<ć%"q]0`P\VRvE|K[V*1#̛Ʀr ~?2cUw&7v[҉teKZThim9JLhԆsg\uⲡw3aIρGg36oeK~/嶏8l~YC0̽NN*ӰQe&Ժٓtq3&eкB]+u.ֺ\ik툘Αlm]|qtZQ[}6tlU'vuQ!/ |qW*|/5<)(u_kUZ8;90bWI|(]C ju΅;5mBy<$g&U%P (W=Q!vms乻.͝==GQP%psgc 2X\LH%b zWqaIR əF(! MOůZC<ĆW.0|O`qs|[+GHBSe?32LDlP;Ɏӌڋ^u˲C g\M1 pf6RyaNJ5us8*XI͍HOÿ%ءl@[>mkg1_"".D3c- -y&԰ݬ\e7j (((\)Ҳg +XV|{>>byqvQ [ԇQ}c-Ak5cmk-uw=vog>RK!qX1w;\[3+saPezG v>pWLl9Cɮy88'wfm[VnLˇ>7u?Q|7$'/Ti)U0ฮ+_1pg- |cy™kp嬉SFpvaPqN\bB`~Ot%M{X3WUY$k9]R=CWC4#R7I1&$o'e*$ t3-%0E(r\'gӿc !775K'+?9TR^8[1+2P\miڱ{*̠Lds3E&R鯖 97u{aװu]{R^ܓ[k^B5&4 &MC$HP0%Pmy|^=2xƀV֦yDh]LKp\~i8sv)T˧tY͋PC+\a>&-!8<>?bc2Lq &r)G1F@ LT*|P,DXLzggTb<pnɢN8E# CfP,(`mǮ _8cyZxUع,=TЎ[ kINP|~>>jY0eBFr|T<&.Or\%r\:䶹2]vmk1kvQ7sj>TDbn?%ȍG.E_!2qRK"N_u<\9{A_,3)64".> Rg?X'YpRֿ\bb2YAySM7xa#AǕA4tO8 &Hܠs4F"i7IؼxVZ ;/a^XV(P +RM œx&iw&|-=S:}*Rԟ'<ߺzf%r\%.Kv}vmv"_g씎H%s]bN_⅗%%E95݈$}lTV<7DZvRɝ#` LX0,٪MȺw1QD t5qZmr\fsǚ OUqrS03\xF&;Z`d8qi Bn8{YDQ r w!hYݳ/!X:\گfڿihBΚyclf|Ý:4Sٞt-K.K{[[[[[[\Ir\5s\gbLE vy^4$Cg;f-T*jȟ'Un{O4@cτQvBWui8- avfxd|=@c!24p+-mu0TcqfQ9km϶ҹ%0V\t >m^[J5JK6O nZoov.K湮k]w+]$qM3̎ITkҹRSxK/Y{7<*> SA' aR\ibUr!yɴrkn +-׉f I*ֵ aړ"4v@ W.irὕ=4!VsoGk?---~55r\%HLEػjLtKl/aB⃝%%#fFiOwD"|-cG z 0 +Sa 6q1MO%bcQ, 7O;9 xF FkHvYfʾ8(3g\Kw.WXֲ kH4F\aruDž?x}]}3}.PR_4V%s湮iʅEᗰ'l䣓5J0jobS4hfFRK/ 3m*Ÿ_3{Q+q3+DN*So 4C3A{c2~*)r⸥mddμ#OnԲ4ߔo&k$OϗKҺR]KuֺZ]k. . ⸮+⸮+ⴝZZ.kvu͙ ( 6nٲpPpMӽbШDMKTRҟP͌0r"5CNoE%e/'Mnvn38qDLS6.N!S14J0R⳿)rҗؓOܰ7bҞ.zR]Ku'H޻WDGW=iM=iG]8V'RnPu>;m+Kⴴ.kbv.ܘw&^|eI5: ;%$H,›2RV/5S09UIr\v) Dc/D!/KrDvv#>PRȖde6f9L,"v@S_+UG#Q;t^28_1 c5 g SU3x,[&2ԌR3$. . p\Wp\Wq\WY*$_7RđŕKVo@ęptfe5JZ R'dȾ9z ^(c1M(҉r =,VtO$ͷ[iMzk^צ^IQw_%_%_%_%_5_5_5_5_5^5G^Yzˡt]qŔ`v/_n+8.ȮOiJ'kȯr+܊Y{m{ LZR<`qnmԊi@ްчX;)Oq|䤾RK&PP䦾Jk䦾Jk䦾Jk䦾Ji3u_!5_!5{^u:׸^&m}YQn gu:{{I.޽/awu鶣Pw[i%3Llh9'>ɴ<!&+.^ķ;u+dtummmmmmmmmmmmmmmmmmmmmmmmmmmmmrLW,%]FrRmmoӴLgfxɖ((=LW]K !1Q"2Aaq #03BR$4CS@PbTr%Dc?=`߻SIU(76? >'jYaLΌv>JZ}P? Fcv w9C<@5Uh#-i%Բ3WRc=䟣iCx5a%=TxuX7oSҐz~海6(M&i\ l \|ѷiFh k|S*w6FqeEhMPɱ޴!2S5hojRG>fWGcd:֮=1 Gfs?5% awU(%.`:ܛ1fJ6sglNU1ۏ7\7`_ZYUJN©f7lvj U?Xo~;)nY2ݝ`i!oSOky{{/Ѥu\}Tq㋦hZFG`;xSQs[|~暧n8S fm-\v5LiS?"o#v;.: EްX,!X,!jzp=5r8t.mck:Tdwg܄M6k~:JZ+ cϩMEڝ zcoDi)<߼:6U}U KOSb~ ^``V@"ߑ`ֵ,ތ,+TQM祟8fycicVjno`5`jvxpu!K:(Ge[1P,aQIzzq柣c!ާ|Mo/$=Lk>jDPo@=m$b^=Ej^Ņ?r8ਫk۫ڍkp֐H ;1韄qձTYb}m!~HFOQeCT0C+"DG)Lmv|J~RHџ]j*X-vrjت-LN8 }9y#,%X6SK,'FX1w}82mЎ {/K+'9|YH4ڶx#/ jmr+F㕯4iԣB|.,Gn(eǑISԟ ]_E;8'D,[t@iB)γfkЛO7i3cs O}SmH6lzюnJ/AVd%e^ƤyqEh7c.C'7JԱ؛+zb i z>y$p#P:EkN+]&*n&T7< ⽥MZytoU4NoQ=rzD-dD6f~DA 1;=ڤc`u&{%Ro⃄Eocwriݧu+/Ӑ#vhy"6-dc|#,q\I_>q97_dms~,LXzg93D\S%qO_Ma*hi߬qU$ )ϻ$is0irq"ѺE߂.ݗlUZ:g'AvXΠ+YoTnɒe8p69cu7n?}< %lLصn y(ÖC,1%4.EMAB̚覊B1J8?4(mcޥkZ1еX{S0]䰳KIS~nccϢSܠe(p>2sy'p@(7>Fд ZrOoDGVeaXU߻ex-~%LO2HZ=ϢR~JHEb:̺9rP,v>?,US\iT[TO8Nѐհ@ooXZ~VgەBGGf6v)Ts]Bof{ D,9 ϩ\jEqޚ:lU:&Xl m`MvQTuRZr=Yub$i%u0gYIO-9[P٬ʑ~%#ZcȪGa?ۗѬ nJ5>m!.fdG9sEښǂsbT&o9"Xk$7_6\-'K0\6ৢ}tnbmjۈoqt;-+7Q{ i63Hz[gsby{yieԝnPj Q_8Rז hڰ, Bc'EUZ1ñJX["11-L6m㷽Ŏ-xTb?0op@ "ZD__kv-sF%Cgc*Y[;<9/ic9@#E@My :Ri^tzO3 zvrBb݁6njf`OL3PGUK$>8S7Iu؍q1YINz֦FkSH O0V{p8F @uzɚ:Bdķq! Lsoto"|XRzArX0mm NF,D5&ޠ 12IG9@ov*j|xqqˏ-}c3NJLU~V żnt2x%:#衩̛״*_%:ZաPDƺaMOU6kA mita/;s 8YMKUG0Nh1-ZܹX92Q*×r^hWNppPҕLori6q9mSe-b  zR5S3)tm.7}`=w`5Mrg.ɣg`98ESC]Lx,2uɭ(4a(n™]QOqOxߒ72 )BxyiU3WR\ }y)_NzN#x9!mq֩[?P88TEMO!dCUm.;lm5$LZSv(e{ h>)`'҉S)c%$1ZZԠt,Ncģb2V0݇5Օ'=_NDzJ%bi*J;p88vOeTtxEy, ,NbsSڜD($ ;d豭Ya\*lDZAVM9_&KݡrMNJ`Kh|2QT"=NQW ,9n_j_N8TSTR+|V2ܩtVF&n)ecu@~ӴwqZiy&̀ M[. d`!`o(BM6k0U>#=E9b3>IF6?I|..[\G|m<Йup;b) U.\ܗ)vc7hTZq# ;8m#-4%) `%114.[\G3uSD~槵<'G) Oo6.8!/]$oR}ks-~_[ԛ!(9_@Mqivyj_G6i)43JJW@2 ƨژզrw+ u<tNE>LW+q5>)>IaU5T;:duZFSEK=Yhj#=ܞSUMpt)jhn)_Lpթ ջr+YM^0 ;JF: RiY濜{S|KF ZWFV6Fr!rnI(_<^^(lr|90?I °+ ¬h'zxOj -_5Wy#B#!jUG> C OmM`SŬϩiM!}UOqO <'ȋ}[tY^sr]xݳ[&q:9?ͰE>{g 7aXPztr(no~|qu&v+쐠>asVvsB°*^WJ/nHJn pRh˜P9 3ze!iiUAxٰ,+ kS<֭j tsGOW)T^,PJiJH:]K Bءv.Ns,BF'Q>))%yĝJ[6&C*ѺdZX,+ °+ °?<'%6BvGO+ߘRhI(hj"0&>ɲZP,+ ky3;bQꝑӄm$7Ik[/>j )GbEQY;w64̃°*9de<vmVMZHaXULu0*ܥT$tNv%URޥh0v9YHD&NaUteZ¬g͹~1QuvgVVPoyWAUgKYaZ?8_s -%1s#`XV؜\a,-,xhBF\^|jK= ©bgdSk}сOcFi: 9$$P LjYQre ʂ^N腖eeGx"=9;;@YR,N>* +[0cੴ={PIvo~KW-DS} 1OP,wNunJqW@(mJ6}Ʀ&(juZCn0?r!iGu]pu-R-Rn8Vjt)ÑUVj;X!JPOZ-Rn-7 _sJVSӇ볚yFgu;fh4L/܋g&TYVj c W%~'_x2"ޤ Àl4:6RuQj[Z;xhQFؚ݈v-YZYi|Ӿn oML9x&<1ڪ/׻.7;|$Vw ֕j:,#^=G7n!cgjbbFZ xI+nEjVBч4bZ5lR7,+RpGvjFkVjS@lxJ.ݫR+Dl8Jle6Pg'68ׇ SO) ZZZZx^mު~il:0&L~!N+RVjT1u잞g p$\*Y/^18mQ9ԏKIS:c,CB X-.7Qݙ X lREP9|]$𞞜JDs2%Q٥л˧ۨr*M#OBuRGx'ᚗI 1˩$Zp殏)=fJbF.+įvu5bX5bq+įv0,kMkH*6McXԙƏ kmX5 ,b=IαX4XQ:"08)hfӠ1BeM 6,13#;+mʫFC#aGc>|B`kƱkƱj<*D~A!K`U3gۭ#6;ڹJb\}4TuTqٹEacAodQڙ$;Y('D&hF'o-k[I7Lalf#JÓ\DSpOGIU)MN潁Hv_C{Q[\_;B7UoF=/:4)H, @8)w.nAIsܛONu3`~&Fޣ栊z_Lo` K{qCbBxn0ڛ9UhG Q*67ԜɁ3!'6tԶAѾb˫}4y>8TL,#.h)bnƩbjspDgakpځY+H=6Dٓ* *3aQְ@ϰ㎵=$Vr EƱ"Aċ*&99؋-ndiu-qZůk?K[!Pr,VX8MyaB^kX8NW! L!6&J啐`{ԑ]J ʸ$)"|msb|B%j-"[֙SO.n۽5ђhkA:8Xvr#- x2RZ(jU=Soְ4sdQV5gbfY= E%HW%˨uiSdE>a}6y)S能InT'A.ߓ++}ҚBkk:aS+eF![?z-EP,vd8Shz9n.џWDEXzA=cEbX%bDV5}5)Y 2_`?r-b2!5R!hf)`&Hn͟4~Lְ֣<|nnk\R.ߪlqMγǂ4oYQ8r}2Oѱ||tϧ2s szSjdpFG~$R}ŸD0fxRZOWǏ_oZ6tS@A䟢 U:5*JpU 6G dJ'9g6gyU5>6"3Դi*H@E)ͲK1䃘:X<6CH؇9  cX%32|KёKMG?I-xgHyzk?y<)i%Znp h\,KĮ9!;S_㌑b oڝaQ⻶)YQυ:K84Rvff +81In)џ8q42el'NYtgYTQTR~rf&MȪ-0Yͨ?OаbtvU +lYCZ 7XPN8tnibA&ʼnQIA8sUZ&*Y&mjeFT6z^[@iؠ9<{8I.r4Yߗۖ!'1P3GNdvUaM |wZ|8s}ɂP @%3{QíF&Ua7ַG~1͜xxaTΤvѾӹ٧Cj-vX:F-Q@ %y˯Obv~g,܍Ie3iDm=wO1֤eeh7"U &I"^Ӝc~+3kHL̤$LJhǎ>z)mq* *9!*=+RnDq>8xڨFRUvzqܪ(jzwYWz9MNbqcbQ y-'i%(㸓1% #j^cq'@XnmR%b %9أ1;jV#fA؃S]!n8޴$17ʜ2!r5ʞ6;S4pj=B5O&a|bA VC2*("H5ɰrZy6e:MsZ~oZ߳%P†u߀˹GS`|W3RH6F:lmAb4hCq$ah<d 3F$ ě!iMdw| 86ڮ)iQԴo8Ǘd.>>wRQϏ^2NeňHTZhw8vjehu>jO]KJu 'G=r,jL3z]jMӋVwZQw҇=z^U")]6GC g[S(-rBz7˿[2w#~}ic.Τi\]ǚ䡆=Orw:V ʡ{bs]w)7g7k{.JMNɯ~.i:w$}+o؁ :azThvXk|Mсdz(4n鰁!md;8ݵEj|s[8\-wK0tߋPnSTS"ڀs͂>lko֙W,\ֻ$ڦԋM?U%6ZXͭ~Z+!!쬭˻ szUӲ]an2=|1]Ԓnܑe!j$2~|zK68OB+&۝EM U>Ai2M8\fh\۠#; q-4U".-v AqꎈOyu)Ȏ^ɁmSkiG8uh4̪FD̜\- ijn&TDYI)&6{St\TOG4%K#Kkgm[~\|hZ#iZ"yF਴}=xz)(b,WT3\ww!ckR] GУYP4t2~gwH9~ָfpb펜T+vW܄lG'v排.<֦ z.(占~_E1x6Åa@6W;iru,dTuRǐ(VG'yMߤ8RS2 W%RDuuuucX  rkq]#W?/hfՎvђU437GI9TD[9&-d^=E^OwϏ\Y2w:VZ٬ܜnF@$49_&=^K<N||cѕ?ddL= 6R#M GЉF|{U]ixMѮ&kJad.X&?Wκ}l<ۼ{F\z.U~wF'moArA8'JL8É .ݟ0 .4N(ExOss(1\ mZFmr$pieL6jr% n18Lt2_>-<;FivH$W?vvZWM76Bv&:/G0a;GFBtǀRElT<=TdF|[ֺ=ZXŬjֵkCZes#ͮ^ =,s~ԕqFy #O fX]g< LSSѻ)P_;Fv?У:^mcԵԏfawCIR<^;JR.*M'Ox*-=GUS֑sUIDx]J9iz,0o^#iL\?/U_Rl5vqܹ\;:Z>K\캝)C\owoNkmMkˎ$! ۟gf]jJ2,wu_%Y^B;>I,8Eu=e3]OJi^ЦG@ !L c ]b XЄB`ב`Uv{E m>+ýrxaLgVYnXŭni qBR6!U&0 {{9Л8V #f,nP-?(WTϗ)4Dsti* o()]J&-AGU ;;cpF5 @RӚ~a>J1CTӇS6O6 sp}~$l , F55_%G6?"|H7/wV֐ʍU4`a~Md~|)x73tM{8$6&[Y6הq^GE\VČS؜[k28StNܙdfv| GM0@XG_OlsS՘Ae>ꆹV&pG 横]D꩟S,GSQ꯫89ٱbzoi |*3υT6~H=&j1;nP[n!Vr}&{к TUn(k:Gώ6$uHTZE9+ GS'C钒39)r;1mQ>'X;7[䆠 AJ‘mDQ-iX)IhUt`4wu7MT2:wU:SPsW5? yW7>;8nzr/=7zkED5USL.2vA97>=WXzmKZNFxz雙Tkv%[>;lG=qmv|ю7z|SGH"ŅYYYYv+,+ µh54VT7;yN/#pwǐOфN%$ vej[U5쪐xh>;}?}4ő=co1.ǒd͜`1o}“G΁/?u<-~ЃMaXWT5F0Ig_w~܌ߋxGPdU8MǦth4]$ v n8m}g~׋=lSË:,BIcnPa~>|ߗVS6Ր{ESPTGGg8G\z|N> &AZHEomەlrS}}\vuTciAsgϊm I%i䣂!Q>e褉X2M :Gj}٫x,-S^c(9owgj@EǖuubMmd Q=~a㎴y4tąCZm[%Iqtj"K1S9g4{o nd-Z~U.p51MoZՏ8/es;}AVrXVʹ8vl8uw)(eq0G^;n8Yr8j3wԑI ¿eeO_QM7d!x{С8ϻʌmDal9aB&1Bcgo):k lE1 Ϲ>y|ndXH'jPNy#n{}IK821cށDŝ{3)ƺ:ʎ.߹XءAaLC]fMU%݊G)p4bW.cq6٤irָ(ֿLgEdgDi}ÏCM4׸ P11YE؇nUt}G0uJq䟣tmfdy׶׉\F˜{Z(k *O~f_%-D9UzSPUӞn1HO&'K$uCO8h1摀lA̜]j!"`#n݊jP|[\JE.܊1o0mzAEjSQP9U"sv-LyUnQP5M2 XrtècOxT)#18ʚ sF=Prr]U%:#gTz9lPӶhǟ˄luuuuuuuuuuuuu V%6FXQk4 ),59⧠XySy^:!kKU*tG1NJ~*g aRM-VQ$_;z^-y^t^i"Q͙|ױD wIRRr0SG] 5T۟ {xgRT=EUf]s×^ Qfqrvs:g8OHRO9n(OG\jc_ }O:;TԳSJ}͊IMJr*MC>OȦxk5HGZ͊*!9a3"[QgZGEZd?5(jtL-Ka޴3!AmLv~KiFNt͎b2$q܆7Mq" 48x|>WGF/B=||6h~(Rrᙜqު͜|FgܟK32sVnA&9*=0%߂n5%('2 %%;gxUZ,ԲSyvUSPS:I6\rԼ*JBȀ|_t` m생#8e.-!j_h$qeO3ާյoTVgvSQԿr}=;#xv99Z:a?(iq\vvJcDd`mZNH!  qԻeZɹ`rW#jƱIa4ݨFi.X_՜X>lH4+H=2g0iC*ꆒ*Gxˏ5)a❢'(x*JyFCY<ҺܪcQ1ǒv{*w8ڦi[%bX*Q2<~jD[6䦡=V!5 "7Y˟݈>مprVŰ˫8X1. pfNI4j^WL.q _8Z8 MLI((M8LhgN60~>JyϏ^,Bk#qrfմ}<!mtP `=b[XqAR;Jߚi98R^Gb߽k$ބz֎3Ǣ1>~G $ߚ@6ڶA5]ʞ7HH([i]pzjq `h61+}˫37iLUG+u*i@;\yN-=|Q3t!ð)BkMڢҕQe]rIC8EC?͇wKX14bX]dZGxubX+&8ܵs~2w%%^.⎊&svhefТlPeĻPuVܟpPw-*K${~qGHbUTJr{R;"AN f1'FސI)Fv(izVh*=1Skb[pNUnEcxKwa}읤ibm|6NҴ=3LR9+M (8:"+ lw̛W]> tܵJ79sIdUTZx.|6ާL; 屻e*f⽣ގ`/?>%O E}'۬&1}_neeeeeeeeea ȵaaX3`kdjۇ?`H96Q !gώ4ӈtKe/(rEŵr1 ubXֱ Sk!W1ZsV\9?^>=xcp_Ǣm1b?RFV_jwz䤤\Ns;*U4jl 6d$ۭWR "Z)ǚt.0rMn@dAUOݖ6+~-[FwU-Wdr@C֘gMJhao!䣋r!Be܈GvduuuuubX4\Skno w1rgtx-fvzl;=ǻ⽑1;]SgJ+GjG_SE^/xÕ,Ff9-u8GGD)E> 61ѺlTDwWL#X4tEx(w*]QF+'F,*rQGp*:zgӓ#novr?u)yqe=L _ۑl]d?M&Cɰc` |wlO >?ۛr:6&j vQفmR8ѺnHxJ؜n[r+rZFMc:GIrp~ESf0ȥdlBٴ*{_GXءl߃jc5rfqNRDs7Y +PU6?o2wbV~%5&ێ_DteB}HY(&ÛA.ZW"?HD4\6'7+'H*vMlK!hn/VVr∥Ԉ`wsn}U3%~69nr/;73UpmaUM"6Fsqǎɞc~GM[ UeeoooeԎbX%k Pkk%oEGޣnQjg2zS#u]HQ9k%RhK 曣o 69xM.FJJd^K!sGWX9g޽_k>4;3˅ʪTRx݁ڢҳ3Si6͑`e+j|emZU<,}zjau ֣6cjmE=d@~!?Ъ(8N¦skfvߑQKNqo=F1~7] óf]ৣ>if3ћCw=)n'xAMމ7,6Q9ѻ ;d,؃0Vlڰ+ °+ °+ °* eoɺı,ec+XP;X0jcLcXnBhH|s]{JN ?S+i?80Y$;z/5N69ދ;ǯ׍LU.#w[%4DpAdև Xޓs#a>8|dLnȱT7Eu;qأOllw ( wk7'No(EpmyO~IG6oζ5-5$Ѷ' `wc~9-}i `l=~Ks $tH6I%Մt5X潏WeU*[2S+u;ZZTs>Ӓ0mؤ.}#8󲎑Vr޹,3$iZE(3<0(ďuaLljbaXVaXVYYYYYYYYYYYYYYYYYY[7Jă5kiZ҅K¹lcϚ3lQ#5ARO>zh"ĩ4%Ҕ\=^{ ڝT`-{$lV0OKǒ63qj<=*)y35Q͙ݿM]!#;On m$I^C~hڂƖ(ke$wX\m۵Ij$ɌhT|nn _5|tX/n/m^HZ|?st OjNbl0dx܅}8dŔ5l; 0Hû<ӛI3nCLkkrjVR2-Dv}$*h"?m橴lBur^p;o,;EMG@ݽSg󤘌]_5YeCގܽTDwFplc>E:AiZZX`X`X`XVaXQj°+ nXVaXVaXUeYYYYYYYYYYYYYYYYU"s͚.ѕOy{zOh詿RK:_ԺfB9))ֵ2D4{SU;< CڧiFñ6RWgYUQJ2Zԙ6,K< ] %}]vp8Y +Lı,Kı,KƙY,mӒRsڝ;lGb2\:6)k$S%q$r_ ;v7u [rò+XT3W|'19B58ܼ]P6H|Je 3]Mӵ~A^ *%;ƟlCg+tཧCDjQBC8xVC|մ4;_U$iA9.I^̡CEc꽏kR5g{cFy;Y4^G^տjEU69Zݫ_;ȯgU[Q#3+K$.I/>IfnmZj.I-rY\_|a\P?"WM5d5_44WX^Ęt(h|MЀ=1w=:Fӗ|{S:a#Hҧ(4 J$X #3+J:6~ݽ{)BSBZv΢^W(jъw<|@2 ZG.,Kı,KĮaZµk +XViZҵk\Z-iZ-ss\\\L܃3曤*[C潧W_*Jsd=潳[CLPUOrw佹]/nW>Kەznnx+7$*%{~x^߫<<<ӵy!+?/k?A^׬~佫X/iU5eQ?|#rzu,(o"?՜pWhFOyOOM'J~_r3zd+XV"VhT1I3F.QN)^8I+nJ*WfC3Ḳݽ$N޽.ގ;Ѯ;ӫ 5erY\XW*+'r8b*8TP=LI\R꾲G[)\1=kXqGۏ3#sM %e?:Zt[',pY[0_ݣGP4,jMv Lc[PR$|fS.+k73O`'2+jvx~ˤ2wk9[[^ɲBsTS*cw+C;nPǡXdQ8ҿb!>F qL3.%;&I 7iM7Ӛ*y;؅>AUsKCPѻ++4[{ 9I0&|VMks YlB䅸mu7XS⟉sBI=?>bS_ 1 V6X8l. DzTHMgSO7r- oB ^e#Qa@q Dmq'|XVul?/:ZwjS&c59. WD|@*GcjW R9lҰ{MHv5Lar -ӝw{)]c"GP_[a!4gz{{K]LgB7X:h7{j#v ޱ?t\kuUaSoPre:~)lUdg( 33Vݚ2X/2V-p[Xi2*YaXomfe!B4a=L6L+XBLFw'RD@]{ Fష1B6 +۴Bb8j[S'šB1F.L? ~m F,r`a DvnX`-B-͐RBv>/F %#n*DKL[6Nc )i'ciLdr4=RR@T9:+=olvAV9IR1ۂf #$Oz؂?v=N;Fm-H[6pQ+++y˫Fu[*gkol8+o5d)ɒ߲VF5WTqhV"e$M~4dHmA#ErP6*[`Ftv@jb{Ydy'156E͓2TEn쥙͏_,{0Z,wމe+L Zp{+e`"ʦ,"36D\vN 7Ӓ &"zu$7_*K ׅ,BfH2(Q\(ه5`k1ZGꚇijih$&x(/>MȭarQ) iLp*LNҴ eK]Ȭj苢H ga nlBLY=5M"lے@xEM5WW򊙛3⡔mNWvjV [%Qg)%W3u7,bFnaTh{~jT3(8 JMÅչzdGt!M|t[\Un=& zLkZr( 5 xxJ$X; Yݥ2aWWW-i"ƜX%Q4AT^DY d$F(Jz4s ӟ ш8$ᚑʞHq? }Qz|/z5G &v(cۈ ZH[w6JFcn ˭B A5-ݵu76Vbj&^s%׺iWWWWWWsdԛ=,5kGY|Sy2ӹ.&lO6},cG{4"Ʊ,H1G[-LVԣ#+2;2hWHvڧ뤼tAj wFid2-mTM+ä.GmphO>&gr Gdь"Ќm(Q9"'B>)AcXqRbP,**ݠ! NI$qs;'uJ3F٨| [-%uuuuut8粲)`k©`GiY4ŹvO6o.N{\M - fA=+KƅN%Pr] 9bgڱ9fd?@ ~De)rnE:'b&ܡ`-{1A=sC=3Ōv@S>e'zl{ܛ&EkvH98J$=LܜX5O. V!].,R5.xZ= !vVKnF#}NJlĮ@PCtOHᔙx,j|x5l UD_T{-كX!"J|:c-U ]AbQ9\궁mTJz)DgN39)*$dbǽ1⿎A)yx\ܰcaXk sV6̨Z+*>LTOrRDǛMH_03dͥbDX/88-ƘIbÒncUvք db`$,W%g|O{CZwB?NۄJ,FI42bh){Kh|u"FTT[Uퟗ?* y5=T,l9~0)iqdMoT` );"|WAN#6۪!Tජo%>5ON qܦ`{ ]\#0HznX{lIC"rMp*͂+_(4|(l{N:5Cs yvQ.Ne#b-nҽTk&/-^[ xoƵՖpU c<VuG4?U)ّf}GYaI ruC\FɿY}~~°pip\9*ȨUT)lL n+g ^~S#AGEӘw*Ѧl xY[>c##%NO'Gve SZCZM(d  QEUfP9Cm7V<=ÚQglgꎩ-ªQ-d2v߻s=E_WPx'\6C<.GGE748 QEUb\oR9bNInNphW';W,Jl`nRCRı'y~EbO.O+bPUԙEڪ9:%@ப1@7U3]#E $Y0.|V\oꮮ#9^[8q{!lBc7øep9\sSLޕ1vEu<큅Q4LjU@2DPMA$y$'LpQaslUg$ZU[ @c,zY/P`"QE[Z$,Kp8TӄHZH*˕qU/=d1bDj+ĞbS&čKBb2zM?X$OyA0ʞLQ4JFXz_j90]PbX%bX%ZosdHY1NUcȧfFM nJW5eܪ}X%\oA<жfȉvmMЬkƤkeZ+hƱ\z6R%`aX% d4aQ!oPN:ƱkƱkC͕$bF;BԲn*'rN'ъ;k#m4q UHdmِIR؆)޿B ft'Wnh*ܠ7W{-?X5`R5${n6bX9iYaaLqbX쨙2hʠF,c,K]r,I0M7꜖U07bXt^H[ϞJX)G!%X$ iV-&,G%Q!6KUXpf Zjoup_ ܔ٬s ı,Kı,Kı*9,҂yªZEC}_'Q3)4EX)KSEu8ɟv8y8EXN4g:!zoIMK/9Gq&hVXQB6{%@ kQ,)|/,Jnn1lz`cX%0P{\2*㊮@bX!rdL57i ~Jvĩ$kel, ?Gs`%4%N%bTӲ+FwOޅ}?z5g[YD8apWLa˸&toX"{u4T]tN+{e~=?tS/X%{AviPUhe+ ' kM#~(]VÉFvBdFB [69>?~^־jjJw&cdUc[,⣕ ]]Q\. WI]%r;'rmU}x*J3#jor47~T23p!tTSuxM|4X2fd9a]%tWI]%tU նymݹ>-slQt*J+N(E+J+J(T\B8G%nd[ʷ;z+fZf<&9:BrT[ W D?SO<-KYB7 |ULv%aB'G|QOr4 Pv,(]cܺ4H71YYYYaXVZL~Anl$TѽQڎFP0 mTV+G"2e*JRmMD,p[)VUl[9VU ,NB0USd3,2222Jm~*гmӢ!MVK޴YnM}7VUw}_1Pc 3nc1Jp'>=RzXyLF23 d7"4LYWWALCF㥎?$PްMq@:Y\Qӊ{^r%:ݎ!ճ\{R2_U/Ý*JW@P FZ5___]sQSNSa~JaKBa9-pj1j;(^wNO68#8fӸd,zOz] ] 6ZɄe<1 =xaAsJ\fR;^2(Rv^*X,us;`Bѻ࢜[wz`r,_ܶ0v%C&H *)Br&I4Zt`|}h$ hV At4NG]_zV"OޚPXn fz0;E 2B{TO2Z ˿8Ehk{Ԓ3 4 ! kp]Y78GMs_bWJ(Pgck 3eXm6xݿ6 ¬Qj,N}%mS7]Ѹ&,1dod$LXUEpǁ͵ T]c%9Rb:'Y26K&U8[gf.*Ŗr{F?܄X XMhT+bm' ڌ3zc!"Zdʸ\S$.[[fYɳ>>Qk)LNvmTo:"˩)|Q&M2*Nt謆FWz5_VQJuka&drmynri@ee]> Bk 7*96"PݗFpzEMdr Xen{+++sY5+e{qEYYYYYYYYYYYYaEKqΩ[JoJ1!CQh,殏QMjEGyܟDzNOhº,> ͮ4ۘ|Gzj|laMod}#ϪYl鄧Qs )I ;9bAV>]'rѪNO9Fl{C@\i=UwܹGU:1TeDoZ NcUO#p U,s0P7Üem\H^rn&YoD ȴUX6+ VVVV!;TJN 劮17⣫^R[B3bM@ٍG(\mTLTtH%իLC/kT~;u4x ϴ;0q42x} ;QTG7`SPY(+%͓E]]]ó* yM⃁ E܅F<HTl<f裗R0LMМ| 5WdjLn#Tӂ{.]`J#[8ϪO8y#(A_O8 uLI QܬF͂( RRESڪm1QWG9{.Īy*9zv&j?u8wtmYtqRPq FW&Qk>j;"kꤢ|g%E^e 9tM"Yȭ8so@ -nE;拝bwNⱻ'%I*F'bjR{bF\J!>D1 WT}1%n[vh qZн ,V2Oݤbݙ olќCfMk#eu%an*,,aNh jHiv!Q)[E:gUt͟T 7Mǝ37Py {k\w!RmK%'Q? >_-s5MQP%EPz۹N6PspBj/vݽSePqR;kFsq3Sj3 Fhg8)ۢ527"q]!r1CZ$z/ħJGchꨦtOog.ڛ RQ S%1I,et6[h}HoO;Xa6;!R[IY!꣫ڄ%%bW7rtmvrF-6fn1 ZaMVXXBمaܚUl 3 NRgIM38(;Vվ)nalVMoUBZ.I9ڗ[vPM8e=f*94 ZV?P{{9ےNNQ*"[l9MǗmpڜ8۪'ñhY`j1Ŝɉf:yuT7 dճ=vr)XL:Nm樉0IۑQHAoܡqM']]I-݁FXhM&S~HanOi4 ^=At( n(T SaXckJ4hޛmnE]_Ȳ!M8;Ȭw<7ӐPD3Miy%nN'ifSF$t A]:+trfIF\5qvEXtnL$&i߿4*0c{Xc$%,]X]]]]_4qyCMxv]_ɲ-UpԷ Ut$}~3ӂ3ߧ8\"외d NO|bл/eُ+ 6Qn& ӊw6*J&ے1MGRMQ2)Ζ<=dV]hf6lEP#xc \ Bl=XnU#N*dРo*&o\s71.iQdz]Zͷ(4dF,a`x-V h\7ކ!~Qqpji }w!!vVWWN7Ei4zgbg 8'5_XGp沲 ]<\XP_6=\zfd V* ))ă <*'վUT@po(EnqdBMQQg*~Q _Db]F"nF GUX^.w;r}6-,Q#ơlF;FWFPߕ"!Xb~d(a/޶h['M,nD=㊊[k! fVџRR;L-%1ED;:!`r7*Iell 43JؿҙlkpezͪŠ$`aZ)C.ѪPŇ~qvImd>CsktݟGl&1hزA :,[ʺsMv[P }Br;t]&+^88\- 4O=mԼ8cTTdß Z #~qOa8v =Stb粒KI NFp6ɲ6Av"l,=L,Di|/ وAЗON= 6RŠ}WXnb]l6$"Jej&l;JhT n&Zەd~d[5r_ı,hQEQ{x#RB;a #h9tFx0vha1CHڛ,[ uWjY+]_Yad,F| 9k070 Oz{A*:zH԰USFEʰ͜4uv*z,xi*h*g\}RrԌ%Gڧ0Hnܽ8WXme[ٕRuOˑBWD|s*|Y)6G!4UD.a:)&{@\GeUb7ݚ~])gdL2?@D/T~¨k}U4;Iuf 14pZ,lحơmV Us>xBcܶU1v޺f0裚9{CUYy.q+Rѽt8lx|L^.'f#muE.UˈPW,lϊ1i4*bav^b|9r~G"k {E Ix Of0Z5Ǫm1T}I]gUym=KdL4ATjMa U UJDy*NnT?.gռ @Q2'x=amۉsqéInN5mH#҅LGzk>vJ(d=cS~/znӰQ]DuU47u*iqߺo(lL,x9 YIѸ㋨!m)=3qA[Gre5,sv}$z#p*ox]kpLF$G$ddC8y88\*/rkfsU@G&Ӽsr1MՇ1Ybw*˄z,9*n>P< [1{#+ ѠVXq'Sg+;[ij&ˣq g3tzPZ vPBF;C䣆MZ9'$~? u1Kr'CD McN*sitޙg;>jO2,xvN6&NM ա]`i܃3Y>ZGh ϓkyf:"G=QN-4d M@M׫\\]|FhU[)ȪU Cbܲ*Ruw䵎 ܩJ1mM{^.ҁ#RNRH\_!;'SbjP쬬+,*YY:&NlY6F;C]ufTG䃚1hvhꮊa-cntvBpHh|?1ii!ѥDXupɣdm&t{e5mM/V^#Gb[%o̦s>@EiB'p> dd-w| ػ>5V+ddw|;[7p[3`<`<a<UtQrEЪnBF FTxc%Vv]zgjmDoЫ~kֻP4'rvBP ՠnHB fWWW]!nm~ɵ|sWI?^;Q5л' 5@'TOhb߲e\Rd 8tc\,}W梮L:+lR۲ 5Wd@2~+aHɚpTrhHϽQ!V⩙ kN?uQ Tڻ6R҇ūwc@!\*07&;Ep炄K@3ݨ(*bn8u)"zØ Q(6zOr]t=otn Z,OQ{LgSM@ tf0KnX.{".D}RtPtFB)FslXX21` fm܎JȣEхl;%]ܶz9mvlm+[ބAvM'dn\WEa, [Ib@vB1:BtMx'Q Y,ozٲMB1ܚo?_z7ޤfzFx⣝Tu&NhO->a_d<Ɗy@8(TI78M.dMCz{ZwNpjܼEagn{:w߂l{#f}*Z1x Xq4#e(ps`~`j?aLOz4⫒ $}``SW:DI+ٰќnFlnGMęSX.ӨWƐ>%t*U3v5܃jQ[Q6"GP|V,W1i Y_A䤍 f~Ԑ#BN{lIq+bKH,EVB𱫂taa[7Xn P7cxSLZ378Eь,hE]؍B޶j#yVVVVVN#G+eP8]meMQv."ᝓ-ðlI;5 ;zٿrq ;r@TcpZǚ]tiEIFeY5[ F3mBgW2b1 I^mzP(YGS_(F\f)ic3R<5m:e4]bX%bX%bX5cX;"-ڭ-[dڋPoeO^lQ+trRcܶcX %"?tY0Z|we, 5up5E*0˂[Kv5_YYYaX~PŇ]nN}\~Lu#3w=9Ӛ*S&kӣiSEXut*Ǖr.WB|;zBbcFa sFgԿ]8)IÚ9lWW5=A'{W.uut$ec[K)6WILKlwe>'PG,1Xi-[~(O=]|6aS6 n/]+]pV0l+-3YO;YdnQ+ NF7&;TCaOv1b5*z]R&<{NZN9|.&ld> RXK/z Vx`5o3ӥKnS;.[yv86U>(~QWo__/i}M;6cv}?5ܱc WWmfAlAtvX8%ԑ [>'ʓ^˗E;ceay KlD4OF)NvW*U֨[PI#l^x>N3_ot漜-΁5*\a&6hT.`pN a#em+Y{"рa]J~_4( :> ӿX;ٻ\(sX[T%B@wZ ¬乭vfjܑfnqbs{*XpYa(GR%n */;|؆C6bQ1Yzcfh0fO.Y 7:dYYe{,n l[ںXD3\a [ia%,lPIܻЫ\-ަxM1͹ȩuM]"M!ry&3 ,^4;GY! M7L7ɲbjͧ5YO<>6V1V*eeeeeeo"-#edZ مU&,qCqSE !73ܶTkY9 bxyo]?etWlm>*HXmlp *J{hYuE!РK sŋH {M#$1Z(␐ӅFl˽IgoX|8hu 8XcoV`XVaXVaXVaXVeaVVVVXUSpE(4GN7Wiu*/x_ѥ=vF7 Po62qXS$,7QHlEw-e/FBjBOY1F0;/Tp3B!nMd+e-Sɾ!ti}VOd#`6?0|d%%!>G 6!:m>aGK=sAJ7+9"5NӫeNWGDM7EtM xqQb-| 9[ J5u`wsX!a 9`DBT2 f۴J+^q=d dձb5l#il-N*VЯ/^ 7{+hem[amj۵mⶬ3n՜V՜VѼV6XcoPp*xmⱷ7ڳڳң])IҍWrgp[iݣP'}Czlޅ,CrXVnw84\P^]1ެxiAוݢ>8BY"QLxxU>++?Y[XlllIrzسسس333س00غ;Gb]1tf-|> alcloaec)ԡJ=b;#h,a 9NsZ.YϪ UVsꢉnVXBu8((LcPwWW线5~NWD:1ma]Ѻl naVVVVWEfSub)y1ĩ=Fjbh,0a3dH6RA3(7Ff>OB7vBth9!Lr7!t-VӪ1wk!1@yVVTog&tSkTmĮ,I򆋕||Pr7O*ı#+Fүi+YlwiSF55Ӥku)q륾Ch+8rG5:+++-N尌Gtf;sK5D5eBq8] $;++JMBt;JڄjPWI;j9!fhn[X.?Vs>k,W.}gZl\ҽ&GXٻ fD?|E> gtYWE> (j]/꿤|> UH.U# WςU_>L#ESi*=iSiϪ<G쮏']O;-lal5xE_lq̻Aޱ앴[f{ otnm[IO3Vwg]u]ugVw ˆ[V;v]lh5Y[1(< *$īrt~Ӽ4MtC~";10|⒎> j#/Ū~+YOVah}W_>+ 꿨|WO]>t꿨|S?|WI>+&ϊ>('QV+ ̬ , bo)3l`XaXVaXVenrQr̫4jې ptrF"m㙸Tu <[5eeeeeedB!F?0&y%bED NFj*:ɏ*J_]Bc)Z}#CP 2{}q t;$2F\y@9ɱ zc7& %Eb+Bi +d"p n XUM!1"2AQ#Raq03Bb $Sr4@CcPs%DT`?i&=Zv&F.=hhiylG-hBlWGP hde_V{v@Vf2bgkuI R:UMcelud]๦cT(#+Fo~Gm m*gr^q[QLqJfZK%Tc:mwj˽,KOR 4fp4BH/F;W%l0nn@Hݩ.\iOhNXx`Wi\~TZw 3"c#n ulYv8.~!N\)גs][;x5"5y֋ _7IHbs?E^a/3V;Z26~nY/쩪XNgo {SM>GՁPдp-kV6aF ?]*XcsY@{+w܃kRI#X?0l˹{+O_m@@a6Oaj:лd=G%vJҵG&HXHPg̙xbNܿ&t[Kh׊3>B}8Y׹oUcOvvy+Rg*н&bHChud1 qqֆCwz0/ёތά̫ױÐvoMK(Bx 1-1Mx31#r}fHI vŮhe5ybFc1D;M8uwf7Ϟڌ=[ pqU_ pFcOay"iΣqFvkX9pZνA[mNsױiAf}_#}oav!m2[+%9:I#fiZW .|Z#/ḯ-%Xg+?Bk;s7&qsZ퍢uHFFۃB96sHOw0o깺up(aD9qZDEbwfy𝗪QdT qkM(jLXz=Þ DÓKGE&|R%Ot4(col9Hd>Vhl}g϶ފ7J-Xen.+,33ftx8w#Fg-hcuYzmx(jo9/-/ݹ)ڲ`U cp֌5@5m+Zh")>ٿp|g)j=kimYb81qiEc9IRiֶZޱ:fy6)Ơե_kkٻQ!4-<{lӞrZSbQ)ϱ8>FUD= >j;L}⋬vG&z ]r+`Hwˏr,Oɳ٦lWpoQ[AgqܟԒ>a4+f<^Q~~F ŧSjI]JUsмFO4rNݰ1Zk|Zo5y'Lفl~m_2kqCWFRp+4 ?0Q5Qh7m9SchABhw926ivpu/~^b[>]7v&M HChBZ8ftZָPao`ZcyXWj}Y,eeH,<FK!/[ ^uP5׻1Z9Ef;ӤfØD] z˗%U 掚!7ǒ c1V[MxS \# ?}?FloWFRvOy|/{:x4Rtn ;ےxPW-1ۺYd 8/mzzmn5[$6\VUz(jŸu-28<RI p;e;VʴTD jlH#v]B6Fs;gmuT?͘MϣR9=F'Xk%͵fV8B7eXV]!`hPr *Ň'Vkia! ڝ~ UfFb :{RH,tĖbs߽srVtգe&!qܨ+QBK[3r:QoW #Gb꨾fpe]~ ?Rq[_%.h:5+YF7^7s[GUwmˡ4P;GM7QM4匆+4i5b~Bid"bVrڮAQF hsA|KOW]X"K'VzbMU;Ym)S*+N#Z~jY=GY 9᷶}/?[zE9`x $kIBXiչk;--O޼=NA'Q'eY<s8ܮu'Ǎ.rOz1H45็TyVi~(wm4Z3;߻ゔ{Ǘ5wHo#C7BQ-i-#xTɺa0ᒦZkxu5cz/i7X/UA]kEjWkyIUd٢P}>*ž2Y=m[ ʴi,7O#C0Ilqgbt{QY1ʎBkN6sZW%6wjX=_A;`UV5*=k࡫cxn*{,Kdy131Ns6}6mnՁGz<1Y܁-#;u*# ljr;Ng&tTY::Iog~ooF(s#.i޽I^dah<ܴGxFK9GBQA l7pzsda[KS[ $s"<pZGziZ`*וFT&mP?9lk1x[1+f?]-*U3Q =rkD~KHXki V3?E^oaQ=ps]_졷2kct]݈V{P|wm1Vw'Nk3~"j7Um;{+wýfiēƍ;h'#hdfZZW6;XT8+ ǹ{&׮+Iq.7'8W+IK\tVp9*Ǩ)OZ+ EfU\I5wgT3G#oR5|ѧm2Roaoݹ:*>0HdԕjYt;rANmh@7x/0Y}\l2?/;̻bg౽Ly\xFS'q`F8)(4fTvfl1vZpfLo*XHHHs Mw)?ʫSy7쓊cr [nT4xҋ(d"LnuzجZiMZ ^S+I|EsÁ+KesyZڏxV +T,3M9%AM959S}'TY>5Yt@kP;>Xs㲯@8] ?.Swxr6Fm5QE'*[ j0{HfG|}*)@~Dc=j:36ǂ63n:Ϗ+Rl.#yÝ&sw?4aM֑T2]dFrYɎBUBݦCgd:9rЙ;Ĭ5dPeh2]諒c}F5p*A`kNd2ZzlrӖUBhP2A6&ƜMV=ǖްUh;JW,F!mSb6@FIr[hghyKDWOM qUJCr +Zر3|%M,Ҽ;xq)qSP濢%#hX)ܚLG}g^u7b[ښn\JlR5ҳH9>{[[7JZc,ߵr#JwE@cs1Mf_1#k\6s 9̻ ;`~ ؼ_O3ڼo4JvO7]9 D5z/ppS4\vr_ǔuN-"T-;|B_-=˖U[+/AkZP4W$is{q h!F&ӆ 搾>*1Ap#uFw#=~<}9cuװgLo{ZqMwUhYnGɥCFSW רc,5A*1DҠco/Bݬ+Y]HcUVO+5mfk[VEjB?UtpwNW<Q ʫkے0¼vrSxÝ 6gIQqƜHZNXMz)˝ap9IOH UA!WuGXUͼBT8 UȖ^p@- _Р^]\u Vbkc Fc-3|u ݗި-K1{|?YIgaɢ{# A~Vv1mޯH]+ EANdm`|EE:Hh蠝\DCQ=ɰ壍~{.sde[<'Ѻ㺕dk&NIj m5Z\k=NpRuF()ܜǔ^jޡAP瑍*p׳%vdB18cǖy\FkCJ^F|O.p Ɩ;lK? V8TA+xHƹsN;xeZ W<`ZAș(V]ex=xu&܏.az9c:k_KdMiL0JUǏ+_n8 <[kjAQ}žךN!TjY,KelV*2v_^`lŒQljWo½[~׽^= jZ7V߅>'.P"[+Wrʆw]_dOSuU4dwc| oI-HTF{sL(q|i37|.o굉^YsB#}{Bnso0"^Z/vE1qT/<!6.7*>1+(Ƌ xQl[ [ Om׋"q-9XZun;CJHhG~ jۮoT@్ mS=S-77u-NCU E#֗6 c hgGf*@; }U)zŜ>7 G k0Z{~E6l=[xWH|naP/dc<{<ȲVZxѴ옎]1+`,X yk9ƶ ^=o-q+nsѾ7FV*!䶻9T٫̕&TP;%V+J8o³Ao ɮl b c~{HaXf>LcZ)% F F@xwKC^uikHjjox& /+B\V`e'2UбC l58bx-D=k2yj=(:r^U/>5<7"F-88w&گ^\s5ѿsV8 o.mX_Vڣd&j׵#:qZ3ոmP$X7bk>c}t3}+ލzT[{V%i ^% 5cV#"soP\VӑPZrUN8G? ۊ.ghX;(%fkÊ%y0sefV.zG;\(r}D^}{n[e g);+%05$i(G4sem9KaH6˷,/E x!%+uϹwFܳMrl 6~| U"غm,3Ym;l1pDcrDM#X*i2W/VAMm8ž.׷0 uFtXpнqswq&F3y!pI{nsRi=Ӛ7;&kut[ɚ΋jGD\jÐGjq K YcZJ%%'_G G+YbxZ9.u5[  C;'{Uh NDzL ja-*# =ӏ4ͦ%*l&w;.>|_c qW@.0?W'53,mӃT.)r5Zn۬g6?5[ ̭Ѳ{ hdҫF~H*̿mrl›juE,Pբdux4c&uz.e<"vDI<0ݨ6̸8bv_w+ 才z#W.T^5v:xO,ty-)},; CMNrxn MXD;.=x,޽칕DG bj Qe#&Odϩ]X00Ɉ4pN@%yj޲[ԏ qr*i>91#xG+K ͦm*Hh]׉nY,2Vq>iQe,쵰^[}4Ng^:Ai`< >KVﲓU˞|VOaTz 2[\;Og>GE'6i]TzfV6DFA]N,=b"LGl-D~W~Q7L]mfjd5'G^+ZYvxoT2dūG )_Xs}=RX} g<ܴh~$lr+0)FUL#/_{y<뷳 VkG&6 Wvѵ-\z\? ^3ۑZ1`kJ6 [mf844'Z|.c-؞Gj QyZAj×xN7.Bӯ*3 ճGs6\%C<$VqTs.*4_gE^Xt.vR@0d~[a; y.DAoq *#z,N'vH6/B91ih: EhΎke !{ - ;_[6\06=dv?*;|c7hkΈguGI\۠d{5?4;]{\\Oc?a޲ KñhsTId-m+^,:@`nW Z?(ID{QYSGYLvf&?VNJ=U!m;%[&mjQFi}i9Rkz0Y Z!;xWapKS2vieƼ~5Ȣ{˸T]#z]Y_gJ+;[-#\'HdOr ;AV7Orxma{Svy[n|isrW<--&!uoFFXezQܶܽc3lU"_C?R.o0KǬ ¼MTfŁm3NH1NRD=σ%h;~HHB(7lv-*9پ.˾F)㑻]^sa`'j?纝JgUWHӓΎ^*EYMYz{IBtS5yN![Z^o ջ8ySG@[?TdLۼ29>=ȗJc\U ƫ Hg)U&ZaZ?*gf5ygr-9sHmpdJ{Uڙy:C1Vv(0"f8w,nei,s:):KT ~{]g C\g?)֛mvahohBXo1:cf,q,'T4nc9Ţ\?f_Ӂ껴.n!{O BN +x)4[D@gWB'Od؛0n'V_Kd$n-$<:7UV0M 2dc/iū$Vq(hlֶSꜼ!c,cUiƉg;&Fxӂ"p뢦AJ`ke Ήe;eǟ0#:M#=KIǐp'rkS!TgVb9<<6tr; {W,ީ JyV{2?^M9I;cB_Ðb6kf?TlV3،+-[l!ݻl8'H΋M,qQ+[WU:LZS&Z1]`㊿5o=$fI4-.RSA<FsIa.[V\+)F?#"`FU=Xvsep+f8-,8M(LъnvM--a*BHJlm =c:]a2x\C_Kit#n9 -6)0mO~ż{quA[5q`l8c$?VIkS[1ؓ#&pE7$7WQ8HX5p-qRshLskRgu<G M- fӝw1[rs{F RqwR`JޭFOc+ч^|.1z^k#H9ʯ[JǬ<}fZ;5F9ƀ8R@ {[ 7H;lV|ak=Tc'fFqiAK}8i-fu4f:Q;B8oHv7-qwI*ӏWuGK_{҅ 1x+XڴW|jah-.7MDn4!yEoؚ&}mqjWݕuI&Q\C_dZ +͔{JZ!t VCu-y,xiB' K2Ep =_c=Z| ) ơ A Uk #4oZChݒ7^ lˣ $zoY&Kc%k>yu9`[(oYk9NJ{+׈JTƷ|jK5ٝѴ*#{:Y۹(+9pWm8~-W*<؝v>jet7TnTn՗U־YG]$֠#e5&EF'3yT嚻n2Q$'8eaZ>IiОLMu~ tgo?lj/6Z7c"5lm!͡9$ կz盏MF湒[hק{X;GMBnhwQk w+}pP@>nru8UW6Awjn%jWj ,igx M<$󃟳u%YH/E}cn;{h<xz~*a I63I*#[w*6[;la;DŽ+fv9QsIp&nsW fX;4Wf;D[켋Kd=8P{ԭѿܗQʳB-x7{W&͂+KK^Fw 6+@6FFF=kHh:8 rC/GÐ>MW&k3jYեX!b# XWӺ#ryY4&-F_'L)CCi%dtOҚmQ\ٵ_WR>nhu˸(3j5TyGX  \g*KOEjCVG%O_0wEQ£ܪ^8܏l8-;ʽg6v"ʼn͸\)zf+.AMtv?_{bqlU eh4oRYn3$m1ʏk#!u9dqň(G$VjR'F3LnYԅۃX7+L&DTZ\U.YXx+U$zmZi>Q,s i8 ;q[.d :_ [Elw5hQ؉>pbYQWfaT&p;24?GtOm޼+S5WcmoU*8͋UtóHXC^^5Sش`Y!{(啗,/UDLf Vek @E?Bhv;S-6jK!hQ7T>[EHNNnM+gQԹf|[6#p |O}۶he$Qm362 *c`L+Ɏ=TQVZwK5\':~ %~E FF%<;YsN1[g|6IcF 묆j \q=ʯΕᒺ*ᲅ3 &5ɿhtI7Mx+|u ƽ\cMHդKڵFw[w(]hĪչ+:rC[-kT}+VQ#<[V;FT_eP:d|y3YaYM +Ucn;k:aҾ+[`';lvܯ|+q(zkS6nCB?,wJc'^4tYaer5̐撯dDJzHJ,˗/{z;,+j@iN„ojI-^Us^q-K-o3V ۑ8_θ%4Ҍ~r&K%dga3Bܣ6Zo~˰O[S\ځ dlK)(Z~QGY':_%{.lF\qiZ+}ȺܐwZ,j,'8BԹG ´sB79!$b2EVf @x*O~ x+1\ђ /_Es8@a~IWgҎC|a^Z4WsB,Y!v|Fo#Z㵳싅sG-C/c\ٝ)?Մ ч)PؙEJ1fX^/#R5s MWn}F킃--l;/a~$guF ؄ jƨߐ5U =)ώڅ؂wdB B% bvNmppF묵gX@PZd u࠴;Z۽ZboL5N!lEWd@GwԤ9pTntOK{q͵.ܸWI')j1VaRcu XѱY ekiPATd ~NPm;ݺxUGҁו0p&gc 1. ^NQSVه'gZ9xeO!\3y] cuif: OCk <y3Z E}"sR;1iA$GD4 ?=<wwb_]d'ծt{v t,%R˲}=\%rMf|ߌ*xZqUǯxoqņȣu4GJʝҴ(sNS'j8pQ =٩mhvыϬ+3nem;PkdmLZS\鲎b,p43ߗR"=&=jf11n]t!, r7+m4} ň_coz|}d``?V::ww,: OM'-QyAnQdto˅ [v_h>NZ1arŝnZ{3){w -Qh%FdYC'P^iWdżUiQZâZ, U)y6E,_2!Zl{j@oEHU#M#~T>XZpؓ%ɒnh 8J:ԭioI;nF0rhOlM.]yԼhyDOZĊ4cnNx"Kx`#xP],sa+ޫbhn95@s7Pvp HΌ=Y>Kס6U:QPZ^~+YXV4๋uNaqasy 5ֻ KcMmy9,TPsX~RCꖯ0m[=UstŧdþuQu s5ߒ-{zy($pg?lO4 ڪɤk.yoi{\UY;uUU k('*G%#( ?Tn7y|/JuC{%I$nu|psCESBH,̟^]޼֛$VF{Ԗ uF+0peArhQy{7 5=VW_!R4pBi#n]_W+GPv+22N?ޥ-Y%@-$c}ɨ3{خ~grZ?e[l^^U'Nb,-}Rogh~j:M&#R aŧ}rlҟYrja;k83*ZḄ42ށŪ>_XVIp B.PWGJ[`U2+[>+W洃mCfNQVI^)$u˖g $mW@B ;c lLog/%%6l/UTJqW;D"Gc{uS߱]͗Y|ѻbwLh=\|qw,,AJ Y;U! Y;+'|%T Y;+'|%d𕓾? ,,- z`J6gدXZVٝFT*dgkJ*OVs7BzPOFF/#EVGd|GoZ{>B#\ Э{jZ2: C/V xäx,,.fY2MmůIw+5Xl2ZŒQ{dGƟ%ނF~kݻFUe ͺ[$g; S+}~Yv~˥`Re>kN4EcQYll440;[.Jإix6:~Yp]WpO[giz4s\ Y;$ͩ O˭Q|O5yS>QpoN-mٖ; w[N+e SZZYf}!I YdsY*X#4Qi.끐4*<dai7 gEb1s0:V]ɏjKt1njg"($k94*Xkz,zCtJBO4cإ8ʫsGXY,KphP/`Q"^#:'; ?wz CԩRcezQj(U̴7nT73rsr DJ;X*$vW9>ԑ*NxsH%BDnXMnǬl#=cqf 7fwW[ԑE~T_ۿ{8O9 էe}N*^A ح\jT*Ó5sʊ/ŠqVb^ RN%EZmRH]sj<6рiн2F1ׂH&aX8;Ef/Ra&wQWֲVF=, e߻,9VkIczcp,"fo%k'A'b#ɖ?o>k$i>Jqwj!cݞ0X d"3R] `s8lj@_PXxvKqFكceع.N ߊ89sY%<_G:ѴWkb(66PhcƳuG5[ǂiό7qA*A+pTn F9(qw'Ui-=J[W?cezQ>Vк;CFvQx?=(5jў^kD! W%{{e #A\!+q\ݺT\MdU0o __|Pi!wycS4*I}iѓzJ:%εvCd\qD;/mVjqT{ {G% ǵS. YU {ɬL^q#k߂L;V(uƈSR60nShedVIm-$}-Z[CB05Gћ8ց2!uGm#Q 5M BιMvV잢9rT Pâc:JQl|KPB^*\K%dY,K/扦'llTS3ʰH~\!u% a]\.h_[k7m~1 |c%k-insc > YN,[C=Jb⫟4KD;UFN=.r9VHv"n5sň_W욥cS5VAfV7its8ub-0u+t-1/VϽe~^Cemқ+r7blhpB 3gOѶHs*v1 9v+Xn/*Uhfy)`S!չ\~Nkw ~ʛA:H6;x|x/7cizcT@858)9*ӭ u9u6bpiS>̦CKyZ _z(ó"2s .qyU>Ϻԇg/RC ojePp likl_Ս}VhuZRjIv@fqkC#{[y5&x\M욄D6ɮS1T=Esx [9-1KMҜ] Ә`LZiWƽ.Jm٣ڄņ,O<$2]]ecdIo0>mi#kg⧲McVP^pܠy-t_V! ht8HtrLZ*9G4E$n4}IEY#5]8H( +B]> )Դc-wH~1OXUFD˗E$x)V^+O;ߢm5ŽKO)ds6BPZe  Q4:6=&:E0{8)}+mfk%w9Mkd_٭u=OOh#6dm.SGG(5Xpzm0jqlশ7ͬV!DkYŧҥpYgɚffs{ UcX=B^Y$[vZP(gjBˏ#FJ=KGsp޸cW7$rҤ`FUcEEݭB"SJ2Gp?LXaFPom͜.QqQ2x[E+u ~_'|7N Vl iK FXH4NF] o^VsFYJ|NH+kEbtgn KY;0Z5RU*FGtb2:6kD$hs;*{8WR}}#Ҹ)y/˃Z屚Cdl]ֺO|9bV٭n!FuAO c|3Z:o':24ZՑv+֋ *j?.Ekm;:)m!o}ңVI#>ŜQݧ".ۍ-YJGuήt3/^ۊlO Z ,lozNkc},%k;nU9] (dk^c?rTZ}µFG(ކg}Q]s8O'sYO7l^S_r^m5IbݦV~8 WM++j&wqda0B^V=bp1@;jITGy~4ͧ){wARN'D d>i~.bށ5oR(y=pdǬ} :;>x˃; w׊ǭhVQ m, lo6~v=%(W7=hî:FJ(֜bi 7];%N˂Kd4ެ0Ƭ9 `GA(NkĦ9*<2ZdhyںPNþFqS4+76AM؝5/1֎W9ct0>!kDr*/X[~ ,:L.o洌qwAtTA01(8 ռ57ٍoPuZg^ڂ ImOD*FֱgEdsIgXTu,|X]'1V4yp?|[썻e߹)O<>3ڹ5kY+͵Mf'n1yj0RCqOG c! ]C"${% St3;NOɶ<,~9JDrٟh/̼uhA bk-v,c1bY\4R}1iQOidQoH,^{n[lk)1jU{:Y48R,ꎤgWi5ezܩDG3r*j Y%q`v7DnKIi4sxbXѽNe ;׭pmj!tn-cN(6\k5[nXJVqh%x-Y-Q}V649gݭ-[mUiXZd[VjA,ZKl,"wQnUJ&};r_I⩧T!j&q Qg;sFzy}QP[#sE7Э{I[|bw/x"eqw`p Ck5>t#ܶES宱k z:𩠯xXψ_cdJnXߢS^TVpk9]`LӇKn w+.dpEzKֿXra~zu44*+:G2s;'bt2&Me_U`9eҋcisc6MJנ4,smHzf:mcұmF FclX[Y2%-R)EF[ z?Ư79,n1P\ ?XܶOnZ;YWonCU.=|U{Uէ;O8;.im7A%] ɚqUVUV fNH(nRjU/6>[*⮉?zL=i^{? ő;a#/!Z8__X;\+^oCk.WWp-p׵z~5a׳,#WO^R_'\½KOVüüüü%Wd఍ǹz>L ?zkGktMzִB^VMV SEX#UF6(ǺŌk8 ֊]ܮiڀv(a!ɣ D=9.I~J,wk5&k5fk5fYk5fY>+;a+z|K'3Nܽ{r/^|/^|qm7[MV~߄-!m7 пh>~hw_9~D+,4uz׶:C5jB}˛dq5chwv ڶk5fY8߶(%ĒY}tw|SbVk5VPC_OG@}U_mH1!\q=֭i{= vp,3ECUyDjŸor =T#- {A>KfRÃgj#h.<1l SpT ?Q.=AVKוևp٣mYqqyCZԯG;Β\b$@hhi0>Ϭ=SÃⰘh%X$<+Aэ.=J*bf7U.=~Uw`^;h>i/\Րvj#(]Vvn}ZguYa=s':gY%x^׵XF4'Wx+_Ҽ7-Sh[_-0;DZd|þmkN撅e݈ܵlu\ g2e;;6,mw3`.Įn~*;1\.IJ/;%dY,Kel[l|[l|[lV[lV[ eld-rͫrͪ۞K0Uo[?-rg8,?yȕ׫jX5 wඖ[kl+l+lϊ>+lϊ>+57Zk5 MJxcxp^!Wor 0זn?AnPkw&ƚ-Pv-w!*!1AQaq 0@P?!7k}Cz't2_Px*'[?Pӆ5-*4[z޾ecUo%GHia}br]s5F< j^hj}EuV7)h|Cp2eb현>eit 0E,+GA?DD{f{2[fa3%slhٴk?|Ώ?meX3b{ >OHhƒh E`f?%ȘL@b{JZ֯Z{mv)08Bu--4-cG[13IT N|-5 26eO^ ={M,#-NQ =1Rx 8%lgEO[YMC9F=KڏL cyMV?˵!tG<~\qZ"t"'OusBZ3c|>UXWJW [)mFQ?ݘkVU } }2ړZuu4JGßc99, l.SjLl_<\7_kҚ|PUeԁu;C_+63乔g#F3//Zs5JEJ`DthrUmQ)Tzvc1A6i'3ZDa%hXrf.tP;0:-l5-b_eRͰW{P/8#КŋM N)z ^ڞ&6陨BHB6IV03.xk&>a5]mᄬ!%u/2y]~UX'cv#_/ f-a,tH@k}_@5 zb&5ac?-oü/z˘uA%d[Y .r叔 t$ 9e}xV#.(lS+HRf _ @t8ZUeҲ鑧)4DbfL$w{ÿ' Ek)' Ƈ giRn)B"s8f\x#/~>Q\\J[^ӆ9 ԩK +Uoب =nløA\gƶ8V5$Q-٠xS_0K6;ZbS7kogԌU}Iɩ3_X ׬9Vsx7+=}Wjyy[jê_^g4-8_}/ \׬0Z߹Iį95(D):پ01V"S0{5򥶄 M(i`LKp3: oq3 &;<&t ~Q_Ɍ߆e2ȴ4Tc"?Q DݔH~rS@W șThﴵ{K= ް:i;lGgf!5 /nalLhGTo 'hBY@S&Aeow_oZѪUKauc P/+]/21?d .̉YO!*8r.qss5ۖ;<ŒogT 0Ԥ~5i o%cntLs+0Y_6b Sf>`*nh'"%dc];#q/uUoKǼVsmLvk5m(fFr~77͑<êPѻ/N([Bj[E$z!Ӭnļ[D@-e;ޱT;30 e0;v3 ^E\V$HԲ^07'QB'%w)5U {*(*fՙeE%K.`aZl*}.!XKFU6S"T5qh&Z{f.CfQ>⮼J{B ^]|Iǧ?SCUzBu'DB7lୣ%cGat}S.:XWO֑y9KG&tQb.G~"S0;56LƐ Nƙَ`kM`ئ a"pܶo; \b%;CT&v!cAv4+> ƻV(HHHK:0fz@5="-ԭK &hKNW1 *+jΑ\̐~tgO uReM\> T "u4oxܪC̓iacօ87œP2VC ixSe s, qQB_ \n΅h/Q!' 3@FE=zKCaKTzE[}>D|Z+a O5 CIt"4/})Xa&-(1C }ΰ`BSGoHhA?IAp'A8HL3: aUc`5bQMxM[غ2LfPG+3oi_;HTK~4AfRߍ%H! ph`[mxv2x/(MxfX"TG0 ߟyiKYoG ~=Q aZ?߿_x/|(І_x^ݥڬUi?N^ F;0U-kdm̝!DؖRet/jNCu)9oF)Ivb  /0KQH#T1PZEu_ʘ iytBG~";{Np1ES>(#Uu ]xԺTzGCR7RJ_C8KIEc[P$S&D陌KRwT sxF"hH)-{&JR0y"{pJG:N)QC/qgY1 &,qK#!nxƫWȌTи2t|K0o"!R}%JBb `މN/x&wZ;m ^ =qifDˉK2S~.n_Ne<%*aU{d% sc25^^daoNs(k\\1S_6;^7-ØD}^PxCe{->(hG/Oo0'Zvl&Ķ-[+\VUٕͤ۞`G`A>Xg30#eNN ,}͏Cl[QcZgfPKڳV$sw&ͩYnÅnq(TNxLa?ހ>>J=\ @_z[ۄ*{SR$[vIfGeр}X ſS>R'z M~q#UϹ~@5wȗ }MuRN2w= E5}̭dFM17p,i /vfq8 ?xP|˻>oi%F!ek6)iZ?5L<>2^f7RbƸjܡ!O[\Ŋâz25G]a6 Cwef=Vv܋8fEUЀoi{Zf@5zB~TZPIV#]kphNT}HЗsFhy:zW@'$`\L܆?kЎ.6of T4f"Vt{=6B{7Βs} -t€O#qJ13Qʳ~z;Px2WDt<'GCȝ*_xWd;Jm pJϕKN&FRԪ51 %@r $ꮱN?׫57^cc$7iyXSH)Cj{H PzH6vgٖL$ɰr+[E&2iJ61A} gP9#Sf4F >)"mC[n._W>}-ټ*6CT/|A=^Ž^xݎvGJ="UXK__3wXH z #48OK_U03^=+\(Sj>QaF˺trbh<) XPbnQBW(J՛!D5AQY=CէdxZsB1.{͠Nɂ{K^}ĉ&bZAKymT(󴭮h8}h//D0Ò:g܆HI&J_N]q,; =\T>~.b)wrޭ}bڰ2tgf F_hA-nm%c@e+"-OuvwzLbΛ%/O/ '_OW濺6Wh䃓 \.SGKF 0/z}q:K' !햫̙ Xr[ܿ/D[)*U>=z]>bQlF!&^{zTm$v9g'A iz3lԳ"l-&+)\2u6R%hk2=# V);T'LEdK$OaӷDAhIqNhU L:+;]&ID_g:ZCLÏp#CFF4 AȧB^ޤ: \vVQk|˒aV?,~aTٝ`N/ĢWaN#R6B6]FY wu6?Vm@6hSFhOzxgrtJP'h)@6 tכY5b]#VdžY8q*A9M̲Cg>bAmGЁU.]h!|LgHCBE7zpO9yj$"T#L1rzW)}}aӥN1#NiX^P35+ 5,X5vNɧih^d쀱J5=g*lɎ۠ fvNV΁"{M)YV| ,3vE(nf&m)C glF 4"#P-x!Ĭ >Pe͟j{g!D8y!*[7We\52^6;'d읓vN;&Hy;9 u/W]/~VǫH0SVe,BSb67x6t*Zc'kD6+du*7lXaeUj:Vj_Д0[N44,u#]jp 9@A~ӲvF[˫YSYv23vKcN"Cj^TXލ;%LI o6$yvN1kaԺ{B hTvN[hA9E*FnXz)ـhk;'d}k/\K= uvLaќUe%!2u7%CLSƸƉߙR{;:E`1_ iԌlx$DvF pS Z)bhSk_X=ϫE h$2JpS/E j0oַv˳EJWCX^0nTɯ~e\="bT㔺ji[lۖeg+#)G^g11H oϥZp{#vE#ԝBU!=yГc^SZY;fxJ +s{̩aن;vƕ T+Wy ;e C[fTJdGZKtWffvٝZzbJu-3'l흳v;f~#WF2J&7E,M?q-@pv׏:+boR!ѿjx, -ˬĵ.= fcOEQ^aLqaa>fP1/D;gH"m׻˃QIPx%5lqЇ-E"ۧ#۹6"J ˳|:t:5izS%xU6 C;tAiRx-~A_]8#J}McR$`Jq)ħ@mo ܩ;c؝ N%8S^&Z̸W~zlнɫįJ+įJG d)CD+v' QqBZ-㨎M?RR\Q-io9D1ܹ8J *$5|S(Qvۆw'Q9r'XJ<@Fl[ 'h#Mr"J0q'q30bl?uf!>Gϭ ,Po͖-{².vu< zNήhA:M =m@ȏ{?E}㗿ip5 ty#NJ%}ptG25+71ai:Ol4/G̒&|o׬-s ɫ4O26ST=?.{4gI\M@$`) r!>+F@0Y$/pIޛv1lN3MxVVyA'xQِ 2R5UA0 l׈^ $_AS Io4Dj7sQ;2<ƩDňz&Ɂ s&L02N2EBE Uq77]kK=+NO"Fpz<\KIHyF(`hҞtPAr؃nMfׄ<5C{[J1!!u\u?,1AN̺&3p[!\MjV95>iD&@➶i+Ne7w\‚ ݺik^ӬMm\S %ʆ^9y|Eҏ$/C?qz">^xzTF4x;8Qi2i `/78PEr]F?US2%~DJ4o.0z/UvPϱR˦zA`a>)j5z^_AUҝYׂ@ 5 1nٮqiPVo^{3:Ѕef)g|2һgjU~dR&N"O&x MAa)a'ssQ҃_n}㤝bF]ajvXhM̳ʜ\,Q%*c>-A)m؆P4bq̭..~啪홒%aaG'Pm݀]CKlĶS0˨A/1gXV {1ѠJ$ѣ&${ʠŤQ1)l:ޱjQq N;m2< 2qx[.GP f)eRַ=oh;Z3Mn[^^{iƎgx hGs ˭hr}LhE[' |oYͻT6 tCy` u g{@}RmL 4Eg #@iBv#וZCW ;º d.sr*MgeJ^O@GR ";ͥuQW &*YhG4}%j{k7+.?yu{AcF%᧹_a[FAԚzS/43X#C2M/5S9/6:S/@fg6Jg޿^Wwmet-BA *\-Fa߻iׅb-z/ʮn6 s4ƛ RQmJPĹPV׷g2}ʶ13\b5EWF&iKEE=7|y|6m-Tm3!Hލ: L7c HN%8`H;<2Õ17Lt%ZH(j/blY_^R> V4du7篗DG0GefYkpEy2]_E:Ȕ^x -8󓩚K R:hr ;oJT50qA] U]Vރ+2a8u}7 o1l8x1Zqפy Fj]GFuNX@9 ES4†5%&aVSov}~?q4t*rǂ)BG%Y<~UOHQ?"h۶/!Hݓjv:([ ;~B~)t D3cSx- xX1ay ӏsSa9Q$Z~(f_X-yaV RkFRYWPP=g[2 Hw4G 68LefWOSBtpYl/hiA@]*գTb%᥄@r8)F{K U1 p\!J^G ޶RNѦW7ǫѬ}&ܤgPu֕_tf[u\.ӄV .WW [9qѝ"%.dw:6fߕ5YX{UTY̗˰Sj5aó@a8M( ߧnD#Y)qG ԝ|,a-tXRͨv-UQI%FE޾`6͔~+a#lЯJdImnr2y[Bq5j-S3 gWKLwЂK??2X+\< ݆S) (ؔ-#HY{B~T͗ i݈[Q{B#%*#gJT[*-՛|:vVIu5ңOwta4>_1v _S(@ >l臯|0FȍU툋Ŋ֛5N+17n%kyIbY8EDZi͚LE+dvq2A`n^vtPR2!.mD Dafk]+Jz*R:0g`Cգ2^.yK֫i &]Ex#XvlIMK\ЎG?q,5[JU`WSO4Y}O'gsf'c$rWZ@-Mu"6 ɵ;7^qGNE|~&g0GЄfc=!1@LJ4vJoqhpQD[^.<@apiH&I()-y^" ިq/^z᭜vF_8އQj+МxC|QKwĿ zqiNOv;Գ)7%j Io푠m^~eU}ݍgI)LWc#˦ mpqΛŠZM6Q勄*ߌ5DƘ:bYzsivmP MO˥{Fn"3I elMn"uJeb=r*._ )Zq< LsX,*33mBX ~0t/SkLf)n$`2G)%AFm2|E |Lτ])YF\4l֖I+z5x=pi`'ĿvG[0JB?wA#Lh!}ω}ܻb|4mΛ;yEeR0{Jb|6 lj>󃲅|0P_QІ~͔eg̉[j]u|/_*.@׮R/JqJ&$,/u?W@Z3+X~FϝZVR$XP1Mn-zUb`+[]2UJJqQNy6DZFb3efzX@ޘa"}hMKk y6-o Tp_iiԶήpijeXɾҮ0E(WڍYi!D8g[q1fKrRSQEULYjYY ;1yZƚ"  SE 56ֳ שue 7S4OnR~`*NK܃:=bXk %}9wbO|F\x԰AtMjAornr B :U~'=PL/[q=2I{ܟhKtfp*\\F% mؖM ‚hZn#߼CR7q:A@!w&nΉ:^3ʲ6576 5 +~Y%H!_[k '̶Bỽr5 ^ gbv+ߑѿ52EBPvcV3!Sx kHޣ~Otw&?Qxj4J X E>L}ij%{G82Q8Ĥ+76GWN&о}=|d*R1w~Ջ{K |L(`^_ư2~|*DWZr06O7;}Kzru1:W ]n]|M]@;^y[j ?6c/.k@0bU}Ў57vXPi)8H|FQTkܗmQ9s].*Fd0u?[QS:bG7JyPUtk2k (z_uSoes @ *[-r @-:Xc x?.Ңrft)M5.F!rE^ZYa~;( 'yb ^@k;17#yx/˗.\PY0@&7o9D{#j~Ɠ[dVU k,a9r?hԿm{>(Ł{Ce]R")4Ic}wl"|?:L#.ZD#PmٺOU|X5/ע REDOblq#o:n_LowgovцoDAj-kD6.86vb`8 h@ r@A30( \Wr qxy=T A=-;ΓjP#j$AB4X{};Ze"a4e46z2?Bh. Rס8\yBF8MSX`&3 4Zg#usX)Є ndڡv''b_mJy1yC^߼rY}aD"X??d|2oYzC3|<|!V*ٹGݽ<4d*&kA0l&|D-: Cb(gQgN5zywü Q- `ڧ ejʼCQ#ak6Lht?C61,,?2~( 8f=1.{+B/ˏhO$~MXЮ(Ǯ~#\vDV/&nP'+ } M~(K=s/A}P,a/Z웢_TH0p~֗s.u% <G11<:Rei-1 _puc^łNCB@?5Ej>̣mL5ԟ 1 O9Exfi\2 w|z3t]-߲Y3mD? 7͹OsQf/& o\BCY*%q8E!،B{?3-t~e0o(GwJ2vFcPыS,4JSnNG}2ًO BLUՓ[abm%.mH&zYK8ƇDG#>?Z?#z}FϺoٝ}\BC)Uϴe50NuJ97 v>1W.oK^N=hRot)|= X'*" Elb ҥ?0lZt"oJ% @8+"GJ} .!|%dx,|I'}OK*AP1=țjT}_GU8٘Oo_gV N`v]Y^DQ1u*F{04W]0n:hm3R`㞑+\ ?f-J 5կ(2 Cv07ebaynQv?̪AjjNWkVb^c~p_.cYҽci1F,"{@9^>Q,4>gW&;,I&r4c)k:M F;]?{ _rg8#֘=s޺/P[aR~K1Ķo#|u=U]/~M4#L8u7(e&^!fYC\Z@}qp wVZ&-da-Շ]Y`,Y/9؏onӵX# @+Rh7QM/4sop]`ϛ-2Ad hؽqˣb8%9ەZΏS0t?CD[IA')MQ?25):A<4R[廌{!AQ,eEwWĚ8fF h}Mҧ0Cvrt.?2+ w-y"N__r[Y *eǰBClhSH '*ûnsg*  5C`n{-5˯CrQʶhWgYsy5=_t.9&m{AKI _ n'Z%dq!+N0VU&qh|4#܏}{B[܅e7Mk| OrUVbF* e( n1j/bk_]ozƘ,uMbO1B㗽Q O-8Y>LC9*=0uV5r#צ!\hq5jdks VʛV+ Z_JCgYo@ݖƋpt%bom|6>X!*ZytF2ϗWXZ+?r*?ںt:@4b,=\U(3t 5yQn;'_62R|!KyF(ws[2۪eЎ ]W &A.#$E+n0!3|ޑ|eu* JҌ j"%3[HcP)u7ZT2 e~#}?f`w|jMq+ p=NLˌE"`鄏:ƻ+wOZW*+~6E1&:~da, +a @NBVo(ȧC7]e~ i߬{)vCf(p+Sp?g!Rxf^'1KBz/ :4wt-$Tbj^uONCNk MfûfV=Y1}5M$I!n!_FOQ{{k<(>4&@#mi+JzӔMh7̦ ȞbN,T9S@`k 5g2.\r˗._CnR;KC0/U{9]^ \kKB_$}bM,[\~xV!_&Xpr\y(/Ȭ6|UVO0A'Vu&mecM7 Vʼn Y (]rka:G2z$ z3Oza-h_YΪ,+&÷ؚ5چfi4lk ZMhTbƬ\4Lf"<3~""kC`NHeH5(4}08mq7:Jw)-f[)aCMC D,M}{0iu~0&$CUWa&G&i[#eTOn+- 53VTJ[.8K.\r( m#6/ļ.Ao꽧 og fA&/ipK{{xRWEwX]B=¿S~}MrniS Zo)ymU%e֣zoS?f2mx (u<>Xw-l P28횵Htyፖ%Dicͷ)Z'Tk&W*WW*TRJ+ҽ_.\r˗._7/qtJuAQk!r^1} U򇕽R _4of ~p~`J^ 7?4am *%9.ʇ[z1R5puX#Fd[S_{h ؏W*a\6֝Kw Ze_MYvW`K}Da8yw'Nb3oԊ_0bņ xP?DHq]q~R͝6wH:hƾt |tzc(i ?},Q~(:v1~ӬŴ(8V"^msasJƞ+iRzWJRJ*T.\Gϭս Az1cx  }_rk̷' ٽlYr΂N65*d"ši,ra 2X񷑕[:s!B~qw424`.<׺%"<\3^d.nEJlo уvQj>DlN%߿(ݠKGVP?I:Ky8u{NU*yk]Y: [Kݩtշe)N !62Z/Qf|@h/V4Y\B8ܷY~ܲܳ_Yy->~E?jW*TRJ*TRJ*Tce&?G0|sI$8 ,[8R|`Zf{`% :]'QB74WL%xPuUֿiS  %,Ǝ: hh2PdۀJF93#g?@C~z答X)rX~A ?fx4m#CYWhj"j0O3pIja~#aɪ脴n2LO-(?>B&|<ՎdeՔ*Us.G2clE!+Uɂƺҕ Zat=*?/>*Wrer.bU\0|z m/ѽ@lo3*6]!YƎF4A2.wsk)-2p5 4y2DLwF$S^ߔǵ'+I {QEHƲG xr.6pt-W@ ,"]ˣ1=cEea^V C)PSI+ZE%@U^O ;¯]MGǙ{aKP:n4_-nѣ")D"X"h889h((;ʠ>5O(y'~.:L?5x\RvNn-З^1Rda~\ócmh&9D%% jnS=N:*| gJ:M}z&&&mt5v 0J1_`V>Vm˃+"$+iLS]UbeLbTQyl= lB{?E4'Kmk1gTz{H?@bAF 5<.}uSA?OBj;"*5Kt~! 5TvlZO Pj),7XĴDGᇿM5[W6_̿%˱KQ-ǸR_xz>^=-vvBlΏR_Y}e.\;tӺwN;wwwzwYޖ;ܥK&LK/̷3:RHU:]:MNuS@3?OG%u]/gav ?Q_XOq6 #Uk\]:N lbj`c Wx;Tִ[nmtpD&ABMoUڍ3Pm6˗.\rKĹr ;ׯx9}Y/|FE iBlB֮!s[_yG{ڔk`{K:GaMIL"D{Nl[}S?d겕3F_Pb]ZEAU$M>mo\r˗/^&ޣ.طrrr˗.\p]/C2(z w>Ӡ[<,MoY߼zT;Leb9=05җY%|6z716E͕o/̆ wb{U˗.\r/n_R^J*TRJ*TRJN-E̜ͳ+D~Qq1* *L bӀM]wQo빝ˮ‘QrВ2 BNCeE}0[e z#.:p%؎Hc3}?-ydXmlYr srBw|O'Jb1?O'TbSO'w?ʟN|E[3<A_ ,ca'F4(e|-.Bh./.Lu+wrcEU"oq59ϼ\\w kM61 ]W"忼$vSr/ dg?T5m6:/|bmTufyՋ#GBULE8>#uӬaK{ϣSt: )08N :~=\07uN]pQ~Nd*mN?I NXXLLmˈ64+i]~:_K垊\K4 a E7-Ya wOO?ܟO?ޟO}җվg[:x*-Z[ne3y6߀0Z}!/2s.uIuG,lCC[ ;eKl݀٢.ۈRe?<̼b!^bv] 2-P]`̿2K2j^ٚq_N%ɘg0DƖ20WZ-~[}nK1f-Eý5/:ҕ1z,\&橍2e,f4]i6] /S;|P.zs=}zX,0Ř2`iz.14C )efLW:,3=bm A?E TĬT5%l_IP.I[YG_FGf.t9M$膡VĴ/--贴ы} 2 <<5_>\ԸE˗.Hq,2E& v4bq2#TKbji^˗/_  ௥& IiI6h$$vI$F嗭ݎV$9q 7;E*&1:$2q$NΏRZ[xbz%$["I&ӽB*sH'WO$y\UʉHɴX(a"BvgI?Д$L*3.P-GC#ȧrI^$EYbLh^4]*2PRI p$Esm eNFg E2F]$,pRI J$ѿϱw@T^/a[Jw:ţתI"f$O$P!p [KWYRI! I$JgP r ϟs212G!2s2${h= !0k$VxFm{9IGEՠ$M$D`~kKDV"[m$B!n߻ETq)I$uZ&<_`xF"_7IplGH?⟿ $$2[I psUopFz2  I$(!ism bQT,J_ygNQ6_b& $\W[Vqxɹli7YpMs߸VI$ W1 Bnq-WF\X'Iʄ $7[_W@ݶl.<Ғcw$FJ(I$h ǣ[&SHđQPQzFI$YL4bxH?d v+.b<I$Pf%Ĵ\Qsڮ+ϭx(S٨#?$ڒg rղI$PNiW$ԜiapyP%IY'xJI$RھRrn/2ZsvBJ"$+sk){$XFW l osҙf?QVr~E1PG)I$*:h="Ygvߙ+iu\=SRBgr2:I$g;i]S^܆?Ye8u0ݪ$Kp! }vI$kgCWPץh)Oσn[tzJI$(oyi. E+o㚶3'gj%EhJI$uLqC2 l"+1/}whQƸlRI$mU8Lغ6,epTl^4DrIS:\Q伒 =GɟP+qq#ZIlHզHC, bX<6 Qa]~qVΫgfc?t/zb{*?E3ڀxSV'oĿ^S 0v;.u t9:mBDlÁ\vGz+黚ab?_ 7oClΕ6OBcˣ[\J|)KxeW? BfqQ zPT,w@bwJsqk,<ûؒYjcj1y~%:4 $Wf!5%@ݜl" ܖVtKK#}TђJ/EpCL0K9!swU?n+cr6+44W`yXC>E lĒ+x2oo ؅8\;J %֌pzno'xrޣFA(2&v蝃@b騨mUW׳-P%Li e][ǬιRĽ j3;Ӛy嶣 &H\ՏxRMyh?6חVNzI:UO#pu}f_8T;m]+Ict}]CYāTzxUh?{KCRI$4Z'Mb_zE(&\h2HycQ{~:6$IYhAIa*L)(ȯ?q*9ϩ+{ n9p)@m8xሗ} z0?漋q%YQdnsK1Ͷ:SsVzf8,t񢮏g*tmFfz1]:{ifW^Ĵ7L4͛$lKyk#K ɾ5s^No4IPyw\h VU$Vi~ ڹm`0^V mLtjg=zi$66 (|u,LoMAҴK[p@@3p &v\~ g">~kK:-V.Z[y۬|iBضgL[[0 k uPtEq|\`:='`];XJkz>fuY)FX\6MXvE?Uk6If1z\Шd׆T`4|͂DQ_jQU*3+#+@vC 7`z_LA״7j\i1cl(Jǁ;A٘J7")V5,~ gK6wCU7:}zMo/6l4j_xPKѾ8c9=vۆxJG%a8y@)l=8,cuWv R` ͕mʟC=[etp *AZ^Q|cbTӗҴox^kA~ 20G)vnX}o~}l{8;#Ke;OQME C;Ѫ/&Z7H8z|ܷ14 iO{'w|TK3uyASq -xX:QD(hn NǢdtL^bq7ۭaJ<׏U y? ]  Gb!iD7[龆McG$X޸*4Auzh& 9o7Fk+ OYږs!~k7\\[/e.L!X,f?Z \A SF9?˒XNM DRIjaZZt*mgL`ն0S2\p^m.|et4& ,׋m=+k4fJU+3c6wxuj2Ws87ƾݥabn} *ox8.4F ޹նƗwyL{K|#pvҺi$hcr471ħ[WQ[iH)XƆb/Ydh7i^vA9ﭼ!Q&r y7ʋ} ջMt ^uվ" T.c-moceݚUL ;^+95/R-eQt59JnCyB-0޳vYO vy bF 79><Y,p莍 ]i%h"x292x/[IJJ7D^>>6pI( ~ 7H1z =kǜsCwF,~x)10ug]W yw,mc>.%(Ƣ\K8Nb巎[ՂB44mRcҽv42p6۝}jqⲄ[^DždW.LXжR'@kL0R8}7eͷ"Zyt5= l_o(<kqf᎚;ɫ]qpho|z-Xz۽lke/F&Qr_%W3LjbUO31eGGj5+1Bة̝Pgs հ>}B'Xŷ/5^42gz,'T\3\fXRͲ;R0oZ@Dt5ަ 9$oO䀼2*55{6VP2UPhb:z!L7]wř9(k\*YYcFoQEJost+mhLU;b},^ 75{JrW͒F^2% Z;3Otא%SHk Cp鿦 jek&Rvqū;MV;:l]K_D m"7m3oxX0#X\5ī?urP.xtijz-OpK>c}d] *ֵfu,]f4)Bs@6ώ (J#- ƙ0Lt>`L]Vx̨8i7YxvTl/.1ZsL'n-Y!DF0m^:Um;0UR`AYcZ, [_+NϪ]r$.s2<+]֌/[yWmxU~!g~ bx..NJӋ"
awl/knob.h
Awl::FloatEntry QLineEdit
awl/floatentry.h
Awl::VolKnob Awl::Knob
awl/volknob.h
deicsOnzeTabWidget presetLineEdit feedbackSlider LFOSyncCheckBox PModSensSlider LFOWaveComboBox AModSensSlider PModDepthSlider AModDepthSlider LFOSpeedSlider LFODelaySlider algorithmComboBox LS1Slider RS1Slider OUT1Slider Fix1CheckBox AME1CheckBox EBS1Slider DET1Slider KVS1Slider WaveForm1ComboBox EGS1ComboBox LS2Slider RS2Slider OUT2Slider AME2CheckBox EBS2Slider DET2Slider KVS2Slider WaveForm2ComboBox EGS2ComboBox LS3Slider RS3Slider OUT3Slider AME3CheckBox EBS3Slider DET3Slider KVS3Slider WaveForm3ComboBox EGS3ComboBox LS4Slider RS4Slider OUT4Slider AME4CheckBox EBS4Slider DET4Slider KVS4Slider WaveForm4ComboBox EGS4ComboBox subcategoryLineEdit categoryLineEdit lbankSpinBox progSpinBox feedbackSpinBox PModSensSpinBox AModSensSpinBox PModDepthSpinBox AModDepthSpinBox LFOSpeedSpinBox LFODelaySpinBox CoarseRatio1SpinBox FineRatio1SpinBox Freq1SpinBox LS1SpinBox RS1SpinBox OUT1SpinBox EBS1SpinBox KVS1SpinBox DET1SpinBox AR1SpinBox D1R1SpinBox D1L1SpinBox D2R1SpinBox RR1SpinBox Fix2CheckBox Freq2SpinBox CoarseRatio2SpinBox FineRatio2SpinBox LS2SpinBox RS2SpinBox OUT2SpinBox EBS2SpinBox KVS2SpinBox DET2SpinBox AR2SpinBox D1R2SpinBox D1L2SpinBox D2R2SpinBox RR2SpinBox Fix3CheckBox CoarseRatio3SpinBox FineRatio3SpinBox Freq3SpinBox AR3SpinBox D1R3SpinBox D1L3SpinBox D2R3SpinBox RR3SpinBox LS3SpinBox RS3SpinBox OUT3SpinBox EBS3SpinBox KVS3SpinBox DET3SpinBox Fix4CheckBox CoarseRatio4SpinBox FineRatio4SpinBox Freq4SpinBox LS4SpinBox RS4SpinBox OUT4SpinBox EBS4SpinBox KVS4SpinBox DET4SpinBox AR4SpinBox D1R4SpinBox D1L4SpinBox D2R4SpinBox RR4SpinBox feedbackSlider valueChanged(int) feedbackSpinBox setValue(int) 111 114 111 114 feedbackSpinBox valueChanged(int) feedbackSlider setValue(int) 111 114 111 114 PModSensSlider valueChanged(int) PModSensSpinBox setValue(int) 111 114 111 114 PModSensSpinBox valueChanged(int) PModSensSlider setValue(int) 111 114 111 114 AModSensSlider valueChanged(int) AModSensSpinBox setValue(int) 111 114 111 114 AModSensSpinBox valueChanged(int) AModSensSlider setValue(int) 111 114 111 114 PModDepthSpinBox valueChanged(int) PModDepthSlider setValue(int) 111 114 111 114 AModDepthSpinBox valueChanged(int) AModDepthSlider setValue(int) 111 114 111 114 LFOSpeedSlider valueChanged(int) LFOSpeedSpinBox setValue(int) 111 114 111 114 LFOSpeedSpinBox valueChanged(int) LFOSpeedSlider setValue(int) 111 114 111 114 LFODelaySlider valueChanged(int) LFODelaySpinBox setValue(int) 111 114 111 114 LFODelaySpinBox valueChanged(int) LFODelaySlider setValue(int) 111 114 111 114 PModDepthSlider valueChanged(int) PModDepthSpinBox setValue(int) 111 114 111 114 Fix1CheckBox toggled(bool) Freq1SpinBox setEnabled(bool) 110 109 110 109 Fix1CheckBox toggled(bool) FineRatio1SpinBox setDisabled(bool) 110 109 110 109 Fix1CheckBox toggled(bool) CoarseRatio1SpinBox setDisabled(bool) 110 109 110 109 OUT1Slider valueChanged(int) OUT1SpinBox setValue(int) 66 109 96 109 OUT1SpinBox valueChanged(int) OUT1Slider setValue(int) 96 109 66 109 LS1Slider valueChanged(int) LS1SpinBox setValue(int) 110 109 110 109 LS1SpinBox valueChanged(int) LS1Slider setValue(int) 110 109 110 109 RS1Slider valueChanged(int) RS1SpinBox setValue(int) 110 109 110 109 RS1SpinBox valueChanged(int) RS1Slider setValue(int) 110 109 110 109 DET1Slider valueChanged(int) DET1SpinBox setValue(int) 110 109 110 109 DET1SpinBox valueChanged(int) DET1Slider setValue(int) 110 109 110 109 EBS1Slider valueChanged(int) EBS1SpinBox setValue(int) 110 109 110 109 EBS1SpinBox valueChanged(int) EBS1Slider setValue(int) 110 109 110 109 KVS1Slider valueChanged(int) KVS1SpinBox setValue(int) 110 109 110 109 KVS1SpinBox valueChanged(int) KVS1Slider setValue(int) 110 109 110 109 Fix2CheckBox toggled(bool) Freq2SpinBox setEnabled(bool) 110 109 110 109 Fix2CheckBox toggled(bool) FineRatio2SpinBox setDisabled(bool) 110 109 110 109 Fix2CheckBox toggled(bool) CoarseRatio2SpinBox setDisabled(bool) 110 109 110 109 OUT2SpinBox valueChanged(int) OUT2Slider setValue(int) 96 109 66 109 LS2Slider valueChanged(int) LS2SpinBox setValue(int) 110 109 110 109 LS2SpinBox valueChanged(int) LS2Slider setValue(int) 110 109 110 109 RS2Slider valueChanged(int) RS2SpinBox setValue(int) 110 109 110 109 RS2SpinBox valueChanged(int) RS2Slider setValue(int) 110 109 110 109 DET2Slider valueChanged(int) DET2SpinBox setValue(int) 110 109 110 109 DET2SpinBox valueChanged(int) DET2Slider setValue(int) 110 109 110 109 EBS2SpinBox valueChanged(int) EBS2Slider setValue(int) 110 109 110 109 EBS2Slider valueChanged(int) EBS2SpinBox setValue(int) 110 109 110 109 KVS2Slider valueChanged(int) KVS2SpinBox setValue(int) 110 109 110 109 KVS2SpinBox valueChanged(int) KVS2Slider setValue(int) 110 109 110 109 Fix3CheckBox toggled(bool) Freq3SpinBox setEnabled(bool) 110 109 110 109 Fix3CheckBox toggled(bool) FineRatio3SpinBox setDisabled(bool) 110 109 110 109 Fix3CheckBox toggled(bool) CoarseRatio3SpinBox setDisabled(bool) 110 109 110 109 OUT3Slider valueChanged(int) OUT3SpinBox setValue(int) 66 109 96 109 OUT3SpinBox valueChanged(int) OUT3Slider setValue(int) 96 109 66 109 LS3Slider valueChanged(int) LS3SpinBox setValue(int) 110 109 110 109 LS3SpinBox valueChanged(int) LS3Slider setValue(int) 110 109 110 109 RS3Slider valueChanged(int) RS3SpinBox setValue(int) 110 109 110 109 RS3SpinBox valueChanged(int) RS3Slider setValue(int) 110 109 110 109 DET3Slider valueChanged(int) DET3SpinBox setValue(int) 110 109 110 109 DET3SpinBox valueChanged(int) DET3Slider setValue(int) 110 109 110 109 EBS3Slider valueChanged(int) EBS3SpinBox setValue(int) 110 109 110 109 EBS3SpinBox valueChanged(int) EBS3Slider setValue(int) 110 109 110 109 KVS3Slider valueChanged(int) KVS3SpinBox setValue(int) 110 109 110 109 KVS3SpinBox valueChanged(int) KVS3Slider setValue(int) 110 109 110 109 Fix4CheckBox toggled(bool) Freq4SpinBox setEnabled(bool) 110 109 110 109 Fix4CheckBox toggled(bool) FineRatio4SpinBox setDisabled(bool) 110 109 110 109 Fix4CheckBox toggled(bool) CoarseRatio4SpinBox setDisabled(bool) 110 109 110 109 OUT4Slider valueChanged(int) OUT4SpinBox setValue(int) 66 109 96 109 OUT4SpinBox valueChanged(int) OUT4Slider setValue(int) 96 109 66 109 LS4Slider valueChanged(int) LS4SpinBox setValue(int) 110 109 110 109 LS4SpinBox valueChanged(int) LS4Slider setValue(int) 110 109 110 109 RS4Slider valueChanged(int) RS4SpinBox setValue(int) 110 109 110 109 RS4SpinBox valueChanged(int) RS4Slider setValue(int) 110 109 110 109 DET4Slider valueChanged(int) DET4SpinBox setValue(int) 110 109 110 109 DET4SpinBox valueChanged(int) DET4Slider setValue(int) 110 109 110 109 EBS4Slider valueChanged(int) EBS4SpinBox setValue(int) 110 109 110 109 EBS4SpinBox valueChanged(int) EBS4Slider setValue(int) 110 109 110 109 KVS4Slider valueChanged(int) KVS4SpinBox setValue(int) 110 109 110 109 KVS4SpinBox valueChanged(int) KVS4Slider setValue(int) 110 109 110 109 redSlider valueChanged(int) redSpinBox setValue(int) 111 114 111 114 redSpinBox valueChanged(int) redSlider setValue(int) 111 114 111 114 greenSlider valueChanged(int) greenSpinBox setValue(int) 111 114 111 114 greenSpinBox valueChanged(int) greenSlider setValue(int) 111 114 111 114 blueSlider valueChanged(int) blueSpinBox setValue(int) 111 114 111 114 blueSpinBox valueChanged(int) blueSlider setValue(int) 111 114 111 114 fcVolumeSlider valueChanged(int) fcVolumeSpinBox setValue(int) 285 155 494 158 fcVolumeSpinBox valueChanged(int) fcVolumeSlider setValue(int) 494 158 285 155 fcPitchSlider valueChanged(int) fcPitchSpinBox setValue(int) 285 180 494 177 fcPitchSpinBox valueChanged(int) fcPitchSlider setValue(int) 494 177 285 180 fcAmplitudeSpinBox valueChanged(int) fcAmplitudeSlider setValue(int) 494 216 285 219 fcAmplitudeSlider valueChanged(int) fcAmplitudeSpinBox setValue(int) 285 219 494 216 pitchBendRangeSlider valueChanged(int) pitchBendRangeSpinBox setValue(int) 486 483 538 486 pitchBendRangeSpinBox valueChanged(int) pitchBendRangeSlider setValue(int) 538 486 486 483 PortamentoTimeSlider valueChanged(int) PortamentoTimeSpinBox setValue(int) 293 483 345 486 PortamentoTimeSpinBox valueChanged(int) PortamentoTimeSlider setValue(int) 345 486 293 483 mwPitchSlider valueChanged(int) mwPitchSpinBox setValue(int) 224 168 539 171 mwAmplitudeSpinBox valueChanged(int) mwAmplitudeSlider setValue(int) 539 223 224 220 mwAmplitudeSlider valueChanged(int) mwAmplitudeSpinBox setValue(int) 224 220 539 223 mwPitchSpinBox valueChanged(int) mwPitchSlider setValue(int) 539 171 224 168 bcPitchSlider valueChanged(int) bcPitchSpinBox setValue(int) 248 147 539 150 bcAmplitudeSlider valueChanged(int) bcAmplitudeSpinBox setValue(int) 248 178 539 181 bcPitchBiasSlider valueChanged(int) bcPitchBiasSpinBox setValue(int) 248 209 539 212 bcEgBiasSlider valueChanged(int) bcEgBiasSpinBox setValue(int) 248 240 539 243 bcPitchSpinBox valueChanged(int) bcPitchSlider setValue(int) 539 150 248 147 bcEgBiasSpinBox valueChanged(int) bcEgBiasSlider setValue(int) 539 243 248 240 atPitchSlider valueChanged(int) atPitchSpinBox setValue(int) 248 147 539 150 atAmplitudeSpinBox valueChanged(int) atAmplitudeSlider setValue(int) 539 181 248 178 atAmplitudeSlider valueChanged(int) atAmplitudeSpinBox setValue(int) 248 178 539 181 atPitchBiasSlider valueChanged(int) atPitchBiasSpinBox setValue(int) 248 209 539 212 atPitchBiasSpinBox valueChanged(int) atPitchBiasSlider setValue(int) 539 212 248 209 atEgBiasSlider valueChanged(int) atEgBiasSpinBox setValue(int) 248 240 539 243 atPitchSpinBox valueChanged(int) atPitchSlider setValue(int) 539 150 248 147 atEgBiasSpinBox valueChanged(int) atEgBiasSlider setValue(int) 539 243 248 240 bcPitchSpinBox valueChanged(int) bcPitchSlider setValue(int) 539 150 248 147 bcAmplitudeSpinBox valueChanged(int) bcAmplitudeSlider setValue(int) 539 181 248 178 bcPitchBiasSpinBox valueChanged(int) bcPitchBiasSlider setValue(int) 539 212 248 209 bcEgBiasSpinBox valueChanged(int) bcEgBiasSlider setValue(int) 539 243 248 240 OUT2Slider valueChanged(int) OUT2SpinBox setValue(int) 66 109 96 109 chChorusSlider valueChanged(int) chChorusSpinBox setValue(int) 487 149 539 152 chChorusSpinBox valueChanged(int) chChorusSlider setValue(int) 539 152 487 149 chReverbSlider valueChanged(int) chReverbSpinBox setValue(int) 487 149 539 152 chReverbSpinBox valueChanged(int) chReverbSlider setValue(int) 539 152 487 149 transposeSlider valueChanged(int) transposeSpinBox setValue(int) 111 114 111 114 transposeSpinBox valueChanged(int) transposeSlider setValue(int) 111 114 111 114 chDelaySlider valueChanged(int) chDelaySpinBox setValue(int) 351 319 403 322 chDelaySpinBox valueChanged(int) chDelaySlider setValue(int) 403 322 351 319 delayPanLFOFreqKnob valueChanged(double,int) delayPanLFOFreqFloatentry setValue(double) 477 308 498 308 delayPanLFOFreqFloatentry valueChanged(double,int) delayPanLFOFreqKnob setValue(double) 497 319 477 326 delayBPMKnob valueChanged(double,int) delayBPMFloatentry setValue(double) 84 389 104 393 delayBPMFloatentry valueChanged(double,int) delayBPMKnob setValue(double) 121 404 78 409 delayBeatRatioKnob valueChanged(double,int) delayBeatRatioFloatentry setValue(double) 212 386 252 392 delayBeatRatioFloatentry valueChanged(double,int) delayBeatRatioKnob setValue(double) 271 398 228 411 delayFeedbackKnob valueChanged(double,int) delayFeedbackFloatentry setValue(double) 347 388 368 393 delayFeedbackFloatentry valueChanged(double,int) delayFeedbackKnob setValue(double) 379 405 352 409 delayPanLFODepthKnob valueChanged(double,int) delayPanLFODepthFloatentry setValue(double) 464 387 507 396 delayPanLFODepthFloatentry valueChanged(double,int) delayPanLFODepthKnob setValue(double) 518 407 471 409 muse-4.0.0/src/synti/deicsonze/deicsonzeplugin.cpp000066400000000000000000000522611404105311000223050ustar00rootroot00000000000000//=========================================================================== // // DeicsOnze an emulator of the YAMAHA DX11 synthesizer // // Version 0.5.5 // // // // // Copyright (c) 2004-2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #include "deicsonze.h" #include "deicsonzeplugin.h" #include "libsimpleplugin/simpler_plugin.h" //#include "ctrl.h" #include "fastlog.h" #include "muse/midi_consts.h" #include "awl/floatentry.h" #include "awl/slider.h" #include "awl/checkbox.h" using Awl::FloatEntry; using Awl::Slider; using Awl::CheckBox; class PluginDialog; void DeicsOnze::initPluginReverb(MusESimplePlugin::Plugin* pluginReverb) { if(_pluginIReverb) delete(_pluginIReverb); _pluginIReverb = 0; _pluginIReverb = pluginReverb->createPluginI( 2, // Channels sampleRate(), DEI_segmentSize, DEI_useDenormalBias, DEI_denormalBias); if(!_pluginIReverb) return; for(int i = 0; i < (int)_pluginIReverb->parameters(); i++) { // From DSSI host. Maybe use this to add automation controllers. Problems with this method. // // Support a special block for dssi synth ladspa controllers. // // Put the ID at a special block after plugins (far after). // //int id = MusECore::genACnum(MusECore::MAX_PLUGINS, cip); // int id = MusECore::genACnum(MusECore::MAX_PLUGINS, i); // FIXME FIXME Tim /* Ctrl* c = new Ctrl(); c->setCurVal((float)pluginReverb->defaultValue(i)); _pluginIReverb->setControllerList(c); */ //setReverbParam(i, pluginReverb->defaultValue(i)); //setReverbParam(i, _pluginIReverb->defaultValue(i)); _pluginIReverb->setParam(i, _pluginIReverb->defaultValue(i)); } //send build gui to the gui char data; data = SYSEX_BUILDGUIREVERB; MusECore::MidiPlayEvent evSysex(0, 0,MusECore::ME_SYSEX,(const unsigned char*)&data, 1); _gui->writeEvent(evSysex); } void DeicsOnze::initPluginChorus(MusESimplePlugin::Plugin* pluginChorus) { if(_pluginIChorus) delete(_pluginIChorus); _pluginIChorus = 0; _pluginIChorus = pluginChorus->createPluginI( 2, // Channels sampleRate(), DEI_segmentSize, DEI_useDenormalBias, DEI_denormalBias); if(!_pluginIChorus) return; //for(int i = 0; i < pluginChorus->parameter(); i++) { for(int i = 0; i < (int)_pluginIChorus->parameters(); i++) { // FIXME FIXME Tim /* Ctrl* c = new Ctrl(); c->setCurVal((float)pluginChorus->defaultValue(i)); _pluginIChorus->setControllerList(c); */ //setChorusParam(i, pluginChorus->defaultValue(i)); //setChorusParam(i, _pluginIChorus->defaultValue(i)); _pluginIChorus->setParam(i, _pluginIChorus->defaultValue(i)); } //send build gui to the gui char data; data = SYSEX_BUILDGUICHORUS; MusECore::MidiPlayEvent evSysex(0, 0,MusECore::ME_SYSEX,(const unsigned char*)&data, 1); _gui->writeEvent(evSysex); } void DeicsOnze::initPluginDelay(MusESimplePlugin::Plugin* pluginDelay) { if(_pluginIDelay) delete(_pluginIDelay); _pluginIDelay = 0; _pluginIDelay = pluginDelay->createPluginI( 2, // Channels sampleRate(), DEI_segmentSize, DEI_useDenormalBias, DEI_denormalBias); if(!_pluginIDelay) return; for(int i = 0; i < (int)_pluginIDelay->parameters(); i++) { // FIXME FIXME Tim /* Ctrl* c = new Ctrl(); c->setCurVal((float)pluginDelay->defaultValue(i)); _pluginIDelay->setControllerList(c); */ //setChorusParam(i, pluginDelay->defaultValue(i)); //setDelayParam(i, _pluginIDelay->defaultValue(i)); _pluginIDelay->setParam(i, _pluginIDelay->defaultValue(i)); } //setDelayDryWet(1); _pluginIDelay->setParam(5, 1.0); float f; char dataDelayBPM[sizeof(float)+1]; dataDelayBPM[0] = SYSEX_DELAYBPM; f = getDelayBPM(); memcpy(&dataDelayBPM[1], &f, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayBPM(0, 0,MusECore::ME_SYSEX, (const unsigned char*)dataDelayBPM, sizeof(float)+1); _gui->writeEvent(evSysexDelayBPM); char dataDelayBeatRatio[sizeof(float)+1]; dataDelayBeatRatio[0] = SYSEX_DELAYBEATRATIO; f = getDelayBeatRatio(); memcpy(&dataDelayBeatRatio[1], &f, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayBeatRatio(0, 0,MusECore::ME_SYSEX, (const unsigned char*)dataDelayBeatRatio, sizeof(float)+1); _gui->writeEvent(evSysexDelayBeatRatio); char dataDelayFeedback[sizeof(float)+1]; dataDelayFeedback[0] = SYSEX_DELAYFEEDBACK; f = getDelayFeedback(); memcpy(&dataDelayFeedback[1], &f, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayFeedback(0, 0,MusECore::ME_SYSEX, (const unsigned char*)dataDelayFeedback, sizeof(float)+1); _gui->writeEvent(evSysexDelayFeedback); char dataDelayLFOFreq[sizeof(float)+1]; dataDelayLFOFreq[0] = SYSEX_DELAYLFOFREQ; f = getDelayLFOFreq(); memcpy(&dataDelayLFOFreq[1], &f, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayLFOFreq(0, 0,MusECore::ME_SYSEX, (const unsigned char*)dataDelayLFOFreq, sizeof(float)+1); _gui->writeEvent(evSysexDelayLFOFreq); char dataDelayLFODepth[sizeof(float)+1]; dataDelayLFODepth[0] = SYSEX_DELAYLFODEPTH; f = getDelayLFODepth(); memcpy(&dataDelayLFODepth[1], &f, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayLFODepth(0, 0,MusECore::ME_SYSEX, (const unsigned char*)dataDelayLFODepth, sizeof(float)+1); _gui->writeEvent(evSysexDelayLFODepth); char dataDelayWetDryMix[sizeof(float)+1]; dataDelayWetDryMix[0] = SYSEX_DELAYWETDRYMIX; f = getDelayDryWet(); memcpy(&dataDelayWetDryMix[1], &f, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayWetDryMix(0, 0,MusECore::ME_SYSEX, (const unsigned char*)dataDelayWetDryMix, sizeof(float)+1); _gui->writeEvent(evSysexDelayWetDryMix); } void DeicsOnze::setReverbParam(int index, float val) { ///if(_pluginIReverb) _pluginIReverb->controller(index)->setCurVal((float)val); if(_pluginIReverb) _pluginIReverb->setParam(index, val); else printf("Warning : no DeicsOnze reverb loaded\n"); } void DeicsOnze::setChorusParam(int index, float val) { ///if(_pluginIChorus) _pluginIChorus->controller(index)->setCurVal((float)val); if(_pluginIChorus) _pluginIChorus->setParam(index, val); else printf("Warning : no DeicsOnze chorus loaded\n"); } void DeicsOnze::setDelayParam(int index, float val) { ///if(_pluginIDelay) _pluginIDelay->controller(index)->setCurVal((float)val); if(_pluginIDelay) _pluginIDelay->setParam(index, val); else printf("Warning : no DeicsOnze delay loaded\n"); } float DeicsOnze::getReverbParam(int index) const { ///if(_pluginIReverb) return _pluginIReverb->controller(index)->curVal().f; if(_pluginIReverb) return _pluginIReverb->param(index); else { fprintf(stderr, "Warning : no DeicsOnze reverb loaded\n"); return 0.0; } } float DeicsOnze::getChorusParam(int index) const { ///if(_pluginIChorus) return _pluginIChorus->controller(index)->curVal().f; if(_pluginIChorus) return _pluginIChorus->param(index); else { fprintf(stderr, "Warning : no DeicsOnze chorus loaded\n"); return 0.0; } } float DeicsOnze::getDelayParam(int index) const { ///if(_pluginIDelay) return _pluginIDelay->controller(index)->curVal().f; if(_pluginIDelay) return _pluginIDelay->param(index); else { fprintf(stderr, "Warning : no DeicsOnze delay loaded\n"); return 0.0; } } void DeicsOnzeGui::addPluginCheckBox(int index, QString text, bool toggled, QWidget* parent, QGridLayout* grid, bool isReverb) { CheckBox* cb = new CheckBox(parent); cb->setId(index); cb->setText(text); cb->setChecked(toggled); grid->addWidget(cb, index, 0); //push on vectors if(isReverb) { _reverbSliderVector.push_back(NULL); _reverbFloatEntryVector.push_back(NULL); _reverbCheckBoxVector.push_back(cb); } else { _chorusSliderVector.push_back(NULL); _chorusFloatEntryVector.push_back(NULL); _chorusCheckBoxVector.push_back(cb); } //connect slots if(isReverb) { connect(cb, SIGNAL(valueChanged(double, int)), this, SLOT(setReverbCheckBox(double, int))); } else { connect(cb, SIGNAL(valueChanged(double, int)), this, SLOT(setChorusCheckBox(double, int))); } } void DeicsOnzeGui::addPluginIntSlider(int index, QString text, double min, double max, double val, QWidget* parent, QGridLayout* grid, bool isReverb) { addPluginSlider(index, text, false, min, max, val, parent, grid, isReverb); } void DeicsOnzeGui::addPluginSlider(int index, QString text, bool isLog, double min, double max, double val, QWidget* parent, QGridLayout* grid, bool isReverb) { QLabel* l = new QLabel(text, parent); grid->addWidget(l, index, 0); FloatEntry* f = new FloatEntry(parent); f->setId(index); f->setLog(isLog); f->setLogRange(min, max); f->setValue(val); f->setMaximumWidth(72); grid->addWidget(f, index, 1); Slider* s = new Slider(parent); s->setId(index); s->setLog(isLog); s->setLogRange(min, max); s->setValue(val); s->setOrientation(Qt::Horizontal); //s->setFixedHeight(h); s->setLineStep((min-max)/100.0); s->setPageStep((min-max)/10.0); grid->addWidget(s, index, 2); //push on vectors if(isReverb) { _reverbSliderVector.push_back(s); _reverbFloatEntryVector.push_back(f); _reverbCheckBoxVector.push_back(NULL); } else { _chorusSliderVector.push_back(s); _chorusFloatEntryVector.push_back(f); _chorusCheckBoxVector.push_back(NULL); } //connect slots if(isReverb) { connect(f, SIGNAL(valueChanged(double, int)), this, SLOT(setReverbFloatEntry(double, int))); connect(s, SIGNAL(valueChanged(double, int)), this, SLOT(setReverbSlider(double, int))); } else { connect(f, SIGNAL(valueChanged(double, int)), this, SLOT(setChorusFloatEntry(double, int))); connect(s, SIGNAL(valueChanged(double, int)), this, SLOT(setChorusSlider(double, int))); } } void DeicsOnzeGui::buildGuiReverb() { if(!_deicsOnze->_pluginIReverb) return; MusESimplePlugin::PluginI* plugI = _deicsOnze->_pluginIReverb; QString name = plugI->name(); name.resize(name.size()-2); updateLadspaReverbLineEdit(name); //build super layout if(parametersReverbGroupBox->layout()) delete(parametersReverbGroupBox->layout()); QGridLayout* superLayout = new QGridLayout(parametersReverbGroupBox); parametersReverbGroupBox->setLayout(superLayout); //build super widget if(_reverbSuperWidget) delete(_reverbSuperWidget); _reverbSuperWidget = new QWidget(parametersReverbGroupBox); superLayout->addWidget(_reverbSuperWidget); //build grid QGridLayout* grid = new QGridLayout(_reverbSuperWidget); _reverbSuperWidget->setLayout(grid); grid->setSpacing(0); //init vectors if(!_reverbSliderVector.empty()) _reverbSliderVector.clear(); if(!_reverbFloatEntryVector.empty()) _reverbFloatEntryVector.clear(); if(!_reverbCheckBoxVector.empty()) _reverbCheckBoxVector.clear(); //build sliders for(int i = 0; i < (int)plugI->plugin()->parameter(); i++) { float min, max, val; plugI->range(i, &min, &max); val = _deicsOnze->getReverbParam(i); if(plugI->isBool(i)) addPluginCheckBox(i, plugI->paramName(i), val > 0.0, _reverbSuperWidget, grid, true); else if(plugI->isInt(i)) addPluginIntSlider(i, plugI->paramName(i), rint(min), rint(max), rint(val), _reverbSuperWidget, grid, true); else addPluginSlider(i, plugI->paramName(i), plugI->isLog(i), min, max, val, _reverbSuperWidget, grid, true); } //update colors of the new sliders (and the whole gui actually) setEditTextColor(reinterpret_cast(*etColor)); setEditBackgroundColor(reinterpret_cast(*ebColor)); } void DeicsOnzeGui::buildGuiChorus() { if(!_deicsOnze->_pluginIChorus) return; MusESimplePlugin::PluginI* plugI = _deicsOnze->_pluginIChorus; QString name = plugI->name(); name.resize(name.size()-2); updateLadspaChorusLineEdit(name); //build super layout if(parametersChorusGroupBox->layout()) delete(parametersChorusGroupBox->layout()); QGridLayout* superLayout = new QGridLayout(parametersChorusGroupBox); parametersChorusGroupBox->setLayout(superLayout); //build super widget if(_chorusSuperWidget) delete(_chorusSuperWidget); _chorusSuperWidget = new QWidget(parametersChorusGroupBox); superLayout->addWidget(_chorusSuperWidget); //build grid QGridLayout* grid = new QGridLayout(_chorusSuperWidget); _chorusSuperWidget->setLayout(grid); grid->setSpacing(2); //init vectors if(!_chorusSliderVector.empty()) _chorusSliderVector.clear(); if(!_chorusFloatEntryVector.empty()) _chorusFloatEntryVector.clear(); if(!_chorusCheckBoxVector.empty()) _chorusCheckBoxVector.clear(); //build sliders for(int i = 0; i < (int)plugI->plugin()->parameter(); i++) { float min, max, val; plugI->range(i, &min, &max); val = _deicsOnze->getChorusParam(i); if(plugI->isBool(i)) addPluginCheckBox(i, plugI->paramName(i), val > 0.0, _chorusSuperWidget, grid, false); else if(plugI->isInt(i)) addPluginIntSlider(i, plugI->paramName(i), rint(min), rint(max), rint(val), _chorusSuperWidget, grid, false); else addPluginSlider(i, plugI->paramName(i), plugI->isLog(i), min, max, val, _chorusSuperWidget, grid, false); } //update colors of the new sliders (and the whole gui actually) setEditTextColor(reinterpret_cast(*etColor)); setEditBackgroundColor(reinterpret_cast(*ebColor)); } //setReverbCheckBox is used, by the way, to send the value //of the parameter because it sends a double and does not //change any thing void DeicsOnzeGui::setReverbCheckBox(double v, int i) { // REMOVE Tim. Or keep. TESTING... // if(i>=256) { // printf("setReverbCheckBox Error : controller index >= 256\n"); // return; // } // float f = (float)v; // unsigned char message[sizeof(float)+4]; // message[0]=MUSE_SYNTH_SYSEX_MFG_ID; // message[1]=DEICSONZE_UNIQUE_ID; // message[2]=SYSEX_REVERBPARAM; // message[3]=(unsigned char)i; // memcpy(&message[4], &f, sizeof(float)); // sendSysex(message, sizeof(float)+4); // Putting directly to the control FIFO without SYSEX should be OK. Tim. _deicsOnze->setReverbParam(i, v); } //setChorusCheckBox is used, by the way, to send the value //of the parameter because it sends a double and does not //change any thing void DeicsOnzeGui::setChorusCheckBox(double v, int i) { // REMOVE Tim. Or keep. TESTING... // if(i>=256) { // printf("setChorusCheckBox Error : controller index >= 256\n"); // return; // } // float f = (float)v; // unsigned char message[sizeof(float)+4]; // message[0]=MUSE_SYNTH_SYSEX_MFG_ID; // message[1]=DEICSONZE_UNIQUE_ID; // message[2]=SYSEX_CHORUSPARAM; // message[3]=(unsigned char)i; // memcpy(&message[4], &f, sizeof(float)); // sendSysex(message, sizeof(float)+4); // Putting directly to the control FIFO without SYSEX should be OK. Tim. _deicsOnze->setChorusParam(i, v); } void DeicsOnzeGui::setReverbFloatEntry(double v, int i) { if(_deicsOnze->_pluginIReverb) { if(_deicsOnze->_pluginIReverb->isInt(i)) v = rint(v); updateReverbFloatEntry(v, i); updateReverbSlider(v, i); setReverbCheckBox(v, i); //because this send the SYSEX } else printf("Warning : no DeicsOnze reverb loaded\n"); } void DeicsOnzeGui::setReverbSlider(double v, int i) { if(_deicsOnze->_pluginIReverb) { if(_deicsOnze->_pluginIReverb->isInt(i)) v = rint(v); updateReverbFloatEntry(v, i); updateReverbSlider(v, i); setReverbCheckBox(v, i); //because this send the SYSEX } else printf("Warning : no DeicsOnze reverb loaded\n"); } void DeicsOnzeGui::setChorusFloatEntry(double v, int i) { if(_deicsOnze->_pluginIReverb) { if(_deicsOnze->_pluginIChorus->isInt(i)) v = rint(v); updateChorusFloatEntry(v, i); updateChorusSlider(v, i); setChorusCheckBox(v, i); //because this send the SYSEX } else printf("Warning : no DeicsOnze chorus loaded\n"); } void DeicsOnzeGui::setChorusSlider(double v, int i) { if(_deicsOnze->_pluginIReverb) { if(_deicsOnze->_pluginIChorus->isInt(i)) v = rint(v); updateChorusSlider(v, i); updateChorusFloatEntry(v, i); setChorusCheckBox(v, i); //because this send the SYSEX } else printf("Warning : no DeicsOnze chorus loaded\n"); } //updates void DeicsOnzeGui::updateReverbSlider(double v, int i) { if(i < (int)_reverbSliderVector.size() && _reverbSliderVector[i]) { _reverbSliderVector[i]->blockSignals(true); _reverbSliderVector[i]->setValue(v); _reverbSliderVector[i]->blockSignals(false); } } void DeicsOnzeGui::updateReverbFloatEntry(double v, int i) { if(i < (int)_reverbFloatEntryVector.size() && _reverbFloatEntryVector[i]) { _reverbFloatEntryVector[i]->blockSignals(true); _reverbFloatEntryVector[i]->setValue(v); _reverbFloatEntryVector[i]->blockSignals(false); } } void DeicsOnzeGui::updateChorusSlider(double v, int i) { if(i < (int)_reverbSliderVector.size() && _reverbSliderVector[i]) { _chorusSliderVector[i]->blockSignals(true); _chorusSliderVector[i]->setValue(v); _chorusSliderVector[i]->blockSignals(false); } } void DeicsOnzeGui::updateChorusFloatEntry(double v, int i) { if(i < (int)_chorusFloatEntryVector.size() && _chorusFloatEntryVector[i]) { _chorusFloatEntryVector[i]->blockSignals(true); _chorusFloatEntryVector[i]->setValue(v); _chorusFloatEntryVector[i]->blockSignals(false); } } //------------------------------------------------------------- // set Delay //------------------------------------------------------------- void DeicsOnze::setDelayBPM(float val) { ///if(_pluginIDelay) _pluginIDelay->controller(0)->setCurVal(val); if(_pluginIDelay) _pluginIDelay->setParam(0, val); else printf("Warning : no DeicsOnze delay loaded\n"); } void DeicsOnze::setDelayBeatRatio(float val) { ///if(_pluginIDelay) _pluginIDelay->controller(1)->setCurVal(val); if(_pluginIDelay) _pluginIDelay->setParam(1, val); else printf("Warning : no DeicsOnze delay loaded\n"); } float DeicsOnze::getDelayBPM() const { ///if(_pluginIDelay) return _pluginIDelay->controller(0)->curVal().f; if(_pluginIDelay) return _pluginIDelay->param(0); else { printf("Warning : no DeicsOnze delay loaded\n"); return 0.0; } } float DeicsOnze::getDelayBeatRatio() const { ///if(_pluginIDelay) return _pluginIDelay->controller(1)->curVal().f; if(_pluginIDelay) return _pluginIDelay->param(1); else { printf("Warning : no DeicsOnze delay loaded\n"); return 0.0; } } void DeicsOnze::setDelayFeedback(float val) { ///if(_pluginIDelay) return _pluginIDelay->controller(2)->setCurVal(val); if(_pluginIDelay) return _pluginIDelay->setParam(2, val); else printf("Warning : no DeicsOnze delay loaded\n"); } float DeicsOnze::getDelayFeedback() const { ///if(_pluginIDelay) return _pluginIDelay->controller(2)->curVal().f; if(_pluginIDelay) return _pluginIDelay->param(2); else { printf("Warning : no DeicsOnze delay loaded\n"); return 0.0; } } void DeicsOnze::setDelayLFOFreq(float val) { ///if(_pluginIDelay) _pluginIDelay->controller(3)->setCurVal(val); if(_pluginIDelay) _pluginIDelay->setParam(3, val); else printf("Warning : no DeicsOnze delay loaded\n"); } float DeicsOnze::getDelayLFOFreq() const { ///if(_pluginIDelay) return _pluginIDelay->controller(3)->curVal().f; if(_pluginIDelay) return _pluginIDelay->param(3); else { printf("Warning : no DeicsOnze delay loaded\n"); return 0.0; } } void DeicsOnze::setDelayLFODepth(float val) { ///if(_pluginIDelay) _pluginIDelay->controller(4)->setCurVal(val); if(_pluginIDelay) _pluginIDelay->setParam(4, val); else printf("Warning : no DeicsOnze delay loaded\n"); } float DeicsOnze::getDelayLFODepth() const { ///if(_pluginIDelay) return _pluginIDelay->controller(4)->curVal().f; if(_pluginIDelay) return _pluginIDelay->param(4); else { printf("Warning : no DeicsOnze delay loaded\n"); return 0.0; } } void DeicsOnze::setDelayDryWet(float val) { ///if(_pluginIDelay) _pluginIDelay->controller(5)->setCurVal(val); if(_pluginIDelay) _pluginIDelay->setParam(5, val); else printf("Warning : no DeicsOnze delay loaded\n"); } float DeicsOnze::getDelayDryWet() const { ///if(_pluginIDelay) return _pluginIDelay->controller(5)->curVal().f; if(_pluginIDelay) return _pluginIDelay->param(5); else { printf("Warning : no DeicsOnze delay loaded\n"); return 0.0; } } muse-4.0.0/src/synti/deicsonze/deicsonzeplugin.h000066400000000000000000000023721404105311000217500ustar00rootroot00000000000000//=========================================================================== // // DeicsOnze an emulator of the YAMAHA DX11 synthesizer // // Version 0.5.5 // // // // // Copyright (c) 2004-2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #ifndef __DEICSONZEPLUGIN_H #define __DEICSONZEPLUGIN_H #include "deicsonze.h" #include "deicsonzegui.h" //#include "plugins/pandelay/pandelaymodel.h" class DeicsOnze; class DeicsOnzeGui; #endif muse-4.0.0/src/synti/deicsonze/deicsonzepreset.cpp000066400000000000000000001137771404105311000223230ustar00rootroot00000000000000//=========================================================================== // // DeicsOnze an emulator of the YAMAHA DX11 synthesizer // // Version 0.5.5 // // deicsonzepreset.cpp // // // Copyright (c) 2004-2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #include "deicsonzepreset.h" #include #include #include //----------------------------------------------------------- // Constructor destructor //----------------------------------------------------------- Preset::Preset() {_subcategory=NULL;_isUsed=false;initPreset();} Preset::Preset(Subcategory* sub) { _subcategory=sub; _isUsed=false; initPreset(); if(sub) sub->_presetVector.push_back(this); } Preset::Preset(Subcategory* sub, int pr) { _subcategory=sub; _isUsed=false; initPreset(); prog=pr; if(sub) sub->_presetVector.push_back(this); } Preset::~Preset() { if(_subcategory) { std::vector::iterator iB=_subcategory->_presetVector.begin(); std::vector::iterator iE=_subcategory->_presetVector.end(); std::vector::iterator iP=std::find(iB, iE, this); if(iP!=iE) _subcategory->_presetVector.erase(iP); else printf("Error : preset %s not found\n", name.c_str()); } } //---------------------------------------------------------- // setIsUsed(bool b) // set the flag _isUsed and transmit in the parents //---------------------------------------------------------- void Preset::setIsUsed(bool b) { if(!_isUsed) { _isUsed=b; if(_subcategory) { _subcategory->_isUsed=b; if(_subcategory->_category) { _subcategory->_category->_isUsed=b; } } } } //---------------------------------------------------------- // getHBankLBankProg // return the hbank, lbank and prog of the preset // warning : if there is not subcategory of category // the value l or h are let unchanged //---------------------------------------------------------- void Preset::getHBankLBankProg(int* h, int* l, int* p) { *p = prog; if(_subcategory) { *l = _subcategory->_lbank; if(_subcategory->_category) *h = _subcategory->_category->_hbank; } } //---------------------------------------------------------- // linkSubcategory // links the preset to a subcategory parent and erase itself // from the last subcategory if not NULL //---------------------------------------------------------- void Preset::linkSubcategory(Subcategory* sub) { if(_subcategory) { std::vector pv=_subcategory->_presetVector; std::vector::iterator iP=find(pv.begin(),pv.end(),this); if(iP!=pv.end()) pv.erase(iP); else printf("Error linkSubcategory: preset %s not found\n", name.c_str()); } _subcategory=sub; if(sub) sub->_presetVector.push_back(this); } //---------------------------------------------------------- // linkCategory // links the subcategory to a category parent and erase itself // from the last category if not NULL //---------------------------------------------------------- void Subcategory::linkCategory(Category* cat) { if(_category) { std::vector sv=_category->_subcategoryVector; std::vector::iterator iS=find(sv.begin(),sv.end(),this); if(iS!=sv.end()) sv.erase(iS); else printf("Error linkCategory: preset %s not found\n", _subcategoryName.c_str()); } _category=cat; if(cat) cat->_subcategoryVector.push_back(this); } //---------------------------------------------------------- // linkSet // links the category to a set parent (there is always only one set) //---------------------------------------------------------- void Category::linkSet(Set* s) { _set=s; if(s) s->_categoryVector.push_back(this); } //---------------------------------------------------------- // Subcategory constructor and destruction //---------------------------------------------------------- Subcategory::Subcategory() {_category=NULL;} Subcategory::Subcategory(Category* cat) { _category=cat; _isUsed=false; if(cat) cat->_subcategoryVector.push_back(this); } Subcategory::Subcategory(const std::string name) { _category=NULL; _isUsed=false; _subcategoryName=name; } Subcategory::Subcategory(Category* cat, const std::string name, int lbank) { _category=cat; _isUsed=false; _subcategoryName=name; _lbank=lbank; if(cat) cat->_subcategoryVector.push_back(this); } Subcategory::~Subcategory() { while(!_presetVector.empty()) delete(*_presetVector.begin()); if(_category) { std::vector::iterator iB=_category->_subcategoryVector.begin(); std::vector::iterator iE=_category->_subcategoryVector.end(); std::vector::iterator iS=std::find(iB, iE, this); if(iS!=iE) _category->_subcategoryVector.erase(iS); else printf("Error : subcategory %s not found\n", _subcategoryName.c_str()); } } //-------------------------------------------------------- // Category constructor destructor //-------------------------------------------------------- Category::Category() {_set=NULL;_isUsed=false;} Category::Category(Set* s) { _set=s; _isUsed=false; if(s) s->_categoryVector.push_back(this); } Category::Category(Set* s,const std::string name, int hbank) { _set=s; _isUsed=false; _categoryName=name; _hbank=hbank; if(s) s->_categoryVector.push_back(this); } Category::~Category() { while(!_subcategoryVector.empty()) delete(*_subcategoryVector.begin()); if(_set) { std::vector::iterator iB=_set->_categoryVector.begin(); std::vector::iterator iE=_set->_categoryVector.end(); std::vector::iterator iC=std::find(iB, iE, this); if(iC!=iE) _set->_categoryVector.erase(iC); else printf("Error : category %s not found\n", _categoryName.c_str()); } } //--------------------------------------------------------- // initPreset // put the preset in the initial state as defined by YAMAHA //--------------------------------------------------------- void Preset::initPreset() { //algorithm algorithm=FIRST; //feedeback feedback=0; //lfo lfo.wave=TRIANGL; lfo.speed=35; lfo.delay=0; lfo.pModDepth=0; lfo.aModDepth=0; lfo.sync=false; //sensitivity sensitivity.pitch=6; sensitivity.amplitude=0; sensitivity.ampOn[0]=false; sensitivity.ampOn[1]=false; sensitivity.ampOn[2]=false; sensitivity.ampOn[3]=false; sensitivity.egBias[0]=0; sensitivity.egBias[1]=0; sensitivity.egBias[2]=0; sensitivity.egBias[3]=0; sensitivity.keyVelocity[0]=0; sensitivity.keyVelocity[1]=0; sensitivity.keyVelocity[2]=0; sensitivity.keyVelocity[3]=0; //frequency frequency[0].ratio=1.0; frequency[1].ratio=1.0; frequency[2].ratio=1.0; frequency[3].ratio=1.0; frequency[0].isFix=false; frequency[1].isFix=false; frequency[2].isFix=false; frequency[3].isFix=false; frequency[0].freq=255.0; frequency[1].freq=255.0; frequency[2].freq=255.0; frequency[3].freq=255.0; //oscWave oscWave[0]=W1; oscWave[1]=W1; oscWave[2]=W1; oscWave[3]=W1; //detune detune[0]=0; detune[1]=0; detune[2]=0; detune[3]=0; //eg eg[0].ar=31; eg[1].ar=31; eg[2].ar=31; eg[3].ar=31; eg[0].d1r=31; eg[1].d1r=31; eg[2].d1r=31; eg[3].d1r=31; eg[0].d1l=15; eg[1].d1l=15; eg[2].d1l=15; eg[3].d1l=15; eg[0].d2r=0; eg[1].d2r=0; eg[2].d2r=0; eg[3].d2r=0; eg[0].rr=15; eg[1].rr=15; eg[2].rr=15; eg[3].rr=15; eg[0].egShift=VOF; eg[1].egShift=VOF; eg[2].egShift=VOF; eg[3].egShift=VOF; //pitchEg pitchEg.pr1=99; pitchEg.pr2=99; pitchEg.pr3=99; pitchEg.pl1=50; pitchEg.pl2=50; pitchEg.pl3=50; //outLevel outLevel[0]=90; outLevel[1]=0; outLevel[2]=0; outLevel[3]=0; //scaling scaling.rate[0]=0; scaling.rate[1]=0; scaling.rate[2]=0; scaling.rate[3]=0; scaling.level[0]=0; scaling.level[1]=0; scaling.level[2]=0; scaling.level[3]=0; //function function.transpose=0; function.mode=POLY; function.pBendRange=4; function.portamento=FULL; function.portamentoTime=0; function.fcVolume=40; function.fcPitch=0; function.fcAmplitude=0; function.mwPitch=50; function.mwAmplitude=0; function.bcPitch=0; function.bcAmplitude=0; function.bcPitchBias=0; function.bcEgBias=0; function.atPitch=0; function.atAmplitude=0; function.atPitchBias=0; function.atEgBias=0; function.reverbRate=0; //globalDetune=0; //Name name="INITVOICE"; } //--------------------------------------------------------- // Preset::merge // copy the preset //--------------------------------------------------------- void Preset::merge(Preset* p) { if(p) { //algorithm algorithm=p->algorithm; //feedeback feedback=p->feedback; //lfo lfo.wave=p->lfo.wave; lfo.speed=p->lfo.speed; lfo.delay=p->lfo.delay; lfo.pModDepth=p->lfo.pModDepth; lfo.aModDepth=p->lfo.aModDepth; lfo.sync=p->lfo.sync; //sensitivity sensitivity.pitch=p->sensitivity.pitch; sensitivity.amplitude=p->sensitivity.amplitude; for(int k=0; ksensitivity.ampOn[k]; sensitivity.egBias[k]=p->sensitivity.egBias[k]; sensitivity.keyVelocity[k]=p->sensitivity.keyVelocity[k]; //frequency frequency[k].ratio=p->frequency[k].ratio; frequency[k].isFix=p->frequency[k].isFix; frequency[k].freq=p->frequency[k].freq; //oscWave oscWave[k]=p->oscWave[k]; //detune detune[k]=p->detune[k]; //eg eg[k].ar=p->eg[k].ar; eg[k].d1r=p->eg[k].d1r; eg[k].d1l=p->eg[k].d1l; eg[k].d2r=p->eg[k].d2r; eg[k].rr=p->eg[k].rr; eg[k].egShift=p->eg[k].egShift; //outLevel outLevel[k]=p->outLevel[k]; //scaling scaling.rate[k]=p->scaling.rate[k]; scaling.level[k]=p->scaling.level[k]; } //pitchEg pitchEg.pr1=p->pitchEg.pr1; pitchEg.pr2=p->pitchEg.pr2; pitchEg.pr3=p->pitchEg.pr3; pitchEg.pl1=p->pitchEg.pl1; pitchEg.pl2=p->pitchEg.pl2; pitchEg.pl3=p->pitchEg.pl3; //function function.transpose=p->function.transpose; function.mode=p->function.mode; function.pBendRange=p->function.pBendRange; function.portamento=p->function.portamento; function.portamentoTime=p->function.portamentoTime; function.fcVolume=p->function.fcVolume; function.fcPitch=p->function.fcPitch; function.fcAmplitude=p->function.fcAmplitude; function.mwPitch=p->function.mwPitch; function.mwAmplitude=p->function.mwAmplitude; function.bcPitch=p->function.bcPitch; function.bcAmplitude=p->function.bcAmplitude; function.bcPitchBias=p->function.bcPitchBias; function.bcEgBias=p->function.bcEgBias; function.atPitch=p->function.atPitch; function.atAmplitude=p->function.atAmplitude; function.atPitchBias=p->function.atPitchBias; function.atEgBias=p->function.atEgBias; function.reverbRate=p->function.reverbRate; //globalDetune=p->globalDetune; //Name name=p->name; } } //--------------------------------------------------------- // findPreset // return the first preset corresponding of hbank, lbank, prog //--------------------------------------------------------- Preset* Subcategory::findPreset(int prog) { std::vector::iterator pvi; for(pvi=_presetVector.begin(); pvi!=_presetVector.end(); pvi++) { if((*pvi)->prog==prog) return(*pvi); } return NULL; } Preset* Category::findPreset(int lbank, int prog) { Subcategory* s=findSubcategory(lbank); if(s) { Preset* p=s->findPreset(prog); if(p) return(p); } return(NULL); } Preset* Set::findPreset(int hbank, int lbank, int prog) { Category* c=findCategory(hbank); if(c) { Preset* p=c->findPreset(lbank, prog); if(p) return(p); } return NULL; } //--------------------------------------------------------- // isFreeHBank, firstFreeHBank -1 otherwise //--------------------------------------------------------- bool Set::isFreeHBank(int hbank) { if(findCategory(hbank)) return(false); else return(true); } int Set::firstFreeHBank() { for(int hbank=0; hbank<128; hbank++) if(isFreeHBank(hbank)) return(hbank); return(-1); } //-------------------------------------------------------------- // Set::merge //-------------------------------------------------------------- void Set::merge(Category* c) { if(isFreeHBank(c->_hbank)) c->linkSet(this); else { Category* cFromSet=findCategory(c->_hbank); cFromSet->_categoryName=c->_categoryName; for(std::vector::iterator i=c->_subcategoryVector.begin(); i!=c->_subcategoryVector.end(); i++) cFromSet->merge(*i); //delete(c); } } bool Category::isFreeLBank(int lbank) { if(findSubcategory(lbank)) return(false); else return(true); } int Category::firstFreeLBank() { for(int lbank=0; lbank<128; lbank++) if(isFreeLBank(lbank)) return(lbank); return(-1); } //-------------------------------------------------------------- // Category::merge //-------------------------------------------------------------- void Category::merge(Subcategory* s) { if(isFreeLBank(s->_lbank)) s->linkCategory(this); else { Subcategory* sFromCat=findSubcategory(s->_lbank); sFromCat->_subcategoryName=s->_subcategoryName; for(std::vector::iterator i=s->_presetVector.begin(); i!=s->_presetVector.end(); i++) sFromCat->merge(*i); //delete(s); } } //--------------------------------------------------------- // Category::unlink // unlink the subcategories, so don't delete them when delete //--------------------------------------------------------- void Category::unlink() { while(!_subcategoryVector.empty()) _subcategoryVector.erase(_subcategoryVector.begin()); } bool Subcategory::isFreeProg(int pr) { if(findPreset(pr)) return(false); else return(true); } int Subcategory::firstFreeProg() { for(int pr=0; pr<128; pr++) if(isFreeProg(pr)) return(pr); return(-1); } //--------------------------------------------------------- // Subcategory::unlink // unlink the presets, so don't delete them when delete //--------------------------------------------------------- void Subcategory::unlink() { while(!_presetVector.empty()) _presetVector.erase(_presetVector.begin()); } //--------------------------------------------------------- // findSubcategory // take hbank and lbank and return the subcategory corresponding, // NULL if doesn't exist //--------------------------------------------------------- Subcategory* Set::findSubcategory(int hbank, int lbank) { Category* c = findCategory(hbank); Subcategory* s; if(c) { s = c->findSubcategory(lbank); return s; } else return NULL; } //--------------------------------------------------------- // findCategory // takes hbank a category and return the first category, // NULL if doesn't exist //--------------------------------------------------------- Category* Set::findCategory(int hbank) { std::vector::iterator cvi; for(cvi=_categoryVector.begin(); cvi!=_categoryVector.end(); cvi++) if((*cvi)->_hbank==hbank) return(*cvi); return(NULL); } //--------------------------------------------------------- // findSubcategory // takes lbank a subcategory and return the subcategory // NULL if doesn't exist //--------------------------------------------------------- Subcategory* Category::findSubcategory(int lbank) { std::vector::iterator svi; for(svi=_subcategoryVector.begin(); svi!=_subcategoryVector.end(); svi++) if((*svi)->_lbank==lbank) return(*svi); return(NULL); } //-------------------------------------------------------------- // Subcategory::merge //-------------------------------------------------------------- void Subcategory::merge(Preset* p) { if(isFreeProg(p->prog)) p->linkSubcategory(this); else { Preset* pFromSub=findPreset(p->prog); pFromSub->merge(p); } } //--------------------------------------------------------- // readSet //--------------------------------------------------------- void Set::readSet(QDomNode setNode) { while(!setNode.isNull()) { QDomElement setEl = setNode.toElement(); if (setEl.isNull()) continue; if (setEl.tagName() == "setName") _setName=setEl.text().toLatin1().data(); if (setEl.tagName() == "deicsOnzeCategory") { //load category QString version = setEl.attribute(QString("version")); if (version == "1.0") { Category* lCategory = new Category(); lCategory->readCategory(setNode.firstChild()); //printf("Ready to merge!\n"); merge(lCategory); } } setNode = setNode.nextSibling(); } } //--------------------------------------------------------- // writeSet //--------------------------------------------------------- void Set::writeSet(AL::Xml* xml, bool onlyUsed) { xml->stag("deicsOnzeSet version=\"1.0\""); xml->tag("setName", QString(_setName.c_str())); for(std::vector::iterator i=_categoryVector.begin(); i!=_categoryVector.end(); i++) (*i)->writeCategory(xml, onlyUsed); xml->etag("deicsOnzeSet"); } //--------------------------------------------------------- // readCategory //--------------------------------------------------------- void Category::readCategory(QDomNode catNode) { while(!catNode.isNull()) { QDomElement catEl = catNode.toElement(); if (catEl.isNull()) continue; if (catEl.tagName() == "categoryName") _categoryName=catEl.text().toLatin1().data(); if (catEl.tagName() == "hbank") _hbank=catEl.text().toInt(); if (catEl.tagName() == "deicsOnzeSubcategory") { //load Subcategory QString version = catEl.attribute(QString("version")); if (version == "1.0") { Subcategory* lSubcategory = new Subcategory(this); lSubcategory->readSubcategory(catNode.firstChild()); } } catNode = catNode.nextSibling(); } } //--------------------------------------------------------- // writeCategory //--------------------------------------------------------- void Category::writeCategory(AL::Xml* xml, bool onlyUsed) { if((!onlyUsed || _isUsed)) { xml->stag("deicsOnzeCategory version=\"1.0\""); xml->tag("categoryName", QString(_categoryName.c_str())); xml->tag("hbank", _hbank); for(std::vector::iterator i=_subcategoryVector.begin(); i!=_subcategoryVector.end(); i++) (*i)->writeSubcategory(xml, onlyUsed); xml->etag("deicsOnzeCategory"); } } //--------------------------------------------------------- // readSubcategory //--------------------------------------------------------- void Subcategory::readSubcategory(QDomNode subNode) { while(!subNode.isNull()) { QDomElement subEl = subNode.toElement(); if (subEl.isNull()) continue; if (subEl.tagName() == "subcategoryName") _subcategoryName=subEl.text().toLatin1().data(); if (subEl.tagName() == "lbank") _lbank=subEl.text().toInt(); if (subEl.tagName() == "deicsOnzePreset") { //load preset QString version = subEl.attribute(QString("version")); if (version == "1.0") { Preset* lPreset = new Preset(this); lPreset->readPreset(subNode.firstChild()); } } subNode = subNode.nextSibling(); } } //--------------------------------------------------------- // writeSubcategory //--------------------------------------------------------- void Subcategory::writeSubcategory(AL::Xml* xml, bool onlyUsed) { if((!onlyUsed || _isUsed)) { xml->stag("deicsOnzeSubcategory version=\"1.0\""); xml->tag("subcategoryName", QString(_subcategoryName.c_str())); xml->tag("lbank", _lbank); for(std::vector::iterator i=_presetVector.begin(); i!=_presetVector.end(); i++) (*i)->writePreset(xml, onlyUsed); xml->etag("deicsOnzeSubcategory"); } } //--------------------------------------------------------- // readPreset //--------------------------------------------------------- void Preset::readPreset(QDomNode presetNode) { while(!presetNode.isNull()) { QDomElement presetEl = presetNode.toElement(); if (presetEl.isNull()) continue; //algorithm if(presetEl.tagName()==ALGSTR) algorithm=(presetEl.text()=="FIRST"? FIRST: (presetEl.text()=="SECOND"? SECOND: (presetEl.text()=="THIRD"? THIRD: (presetEl.text()=="FOURTH"? FOURTH: (presetEl.text()=="FIFTH"? FIFTH: (presetEl.text()=="SIXTH"? SIXTH: (presetEl.text()=="SEVENTH"? SEVENTH:EIGHTH))))))); //feedback else if(presetEl.tagName()==FEEDBACKSTR) feedback=presetEl.text().toInt(); //quick edit //else if(presetEl.tagName()==FINEBRIGHTNESSSTR) // brightness=presetEl.text().toInt(); //else if(presetEl.tagName()==MODULATIONSTR) // modulation=(unsigned char)presetEl.text().toInt(); //else if(presetEl.tagName()==GLOBALDETUNESTR) // globalDetune=presetEl.text().toInt(); //else if(presetEl.tagName()==ATTACKSTR) // attack=presetEl.text().toInt(); //else if(presetEl.tagName()==RELEASESTR) // release=presetEl.text().toInt(); //lfo else if(presetEl.tagName()=="lfo") { QDomNode lfoNode = presetNode.firstChild(); while(!lfoNode.isNull()) { QDomElement lfoEl = lfoNode.toElement(); if (lfoEl.isNull()) continue; if(lfoEl.tagName()==WAVESTR) lfo.wave=(lfoEl.text()=="SAWUP"? SAWUP: (lfoEl.text()=="SQUARE"? SQUARE: (lfoEl.text()=="TRIANGL"? TRIANGL:SHOLD))); else if(lfoEl.tagName()==SPEEDSTR) lfo.speed=lfoEl.text().toInt(); else if(lfoEl.tagName()==DELAYSTR) lfo.delay=lfoEl.text().toInt(); else if(lfoEl.tagName()==PMODDEPTHSTR) lfo.pModDepth=lfoEl.text().toInt(); else if(lfoEl.tagName()==AMODDEPTHSTR) lfo.aModDepth=lfoEl.text().toInt(); else if(lfoEl.tagName()==SYNCSTR) lfo.sync=(lfoEl.text()=="on"? true:false); lfoNode = lfoNode.nextSibling(); } } //sensitivity else if(presetEl.tagName()=="sensitivity") { QDomNode sensitivityNode = presetNode.firstChild(); while(!sensitivityNode.isNull()) { QDomElement sensitivityEl = sensitivityNode.toElement(); if (sensitivityEl.isNull()) continue; QString st=sensitivityEl.tagName(); if(st==PMODSENSSTR) sensitivity.pitch=sensitivityEl.text().toInt(); else if(st==AMSSTR) sensitivity.amplitude=sensitivityEl.text().toInt(); else if(st.contains(AMESTR, Qt::CaseSensitive)) { int op = (st.remove(AMESTR, Qt::CaseSensitive)).toInt()-1; sensitivity.ampOn[op]=(sensitivityEl.text()=="on"? true:false); } else if(st.contains(EBSSTR, Qt::CaseSensitive)) { int op = (st.remove(EBSSTR, Qt::CaseSensitive)).toInt()-1; sensitivity.egBias[op]=sensitivityEl.text().toInt(); } else if(st.contains(KVSSTR, Qt::CaseSensitive)) { int op = (st.remove(KVSSTR, Qt::CaseSensitive)).toInt()-1; sensitivity.keyVelocity[op]=sensitivityEl.text().toInt(); } sensitivityNode =sensitivityNode.nextSibling(); } } //frequency else if(presetEl.tagName()=="frequency") { QDomNode frequencyNode = presetNode.firstChild(); while(!frequencyNode.isNull()) { QDomElement frequencyEl = frequencyNode.toElement(); if (frequencyEl.isNull()) continue; QString ft = frequencyEl.tagName(); if(ft.contains(RATIOSTR, Qt::CaseSensitive)) { int op = (ft.remove(RATIOSTR, Qt::CaseSensitive)).toInt()-1; frequency[op].ratio=frequencyEl.text().toDouble(); } else if(ft.contains(FIXSTR, Qt::CaseSensitive)) { int op = (ft.remove(FIXSTR, Qt::CaseSensitive)).toInt()-1; frequency[op].isFix=(frequencyEl.text()=="yes"?true:false); } else if(ft.contains(FIXRANGESTR, Qt::CaseSensitive)) { int op= (ft.remove(FIXRANGESTR, Qt::CaseSensitive)).toInt()-1; frequency[op].freq=frequencyEl.text().toDouble(); } frequencyNode =frequencyNode.nextSibling(); } } //oscWave else if(presetEl.tagName().contains(OSWSTR, Qt::CaseSensitive)) { int op=(presetEl.tagName().remove(OSWSTR, Qt::CaseSensitive)).toInt()-1; oscWave[op]=(presetEl.text()=="W1"? W1: (presetEl.text()=="W2"?W2: (presetEl.text()=="W3"?W3: (presetEl.text()=="W4"?W4: (presetEl.text()=="W5"?W5: (presetEl.text()=="W6"?W6: (presetEl.text()=="W7"?W7:W8))))))); } //detune else if(presetEl.tagName().contains(DETSTR, Qt::CaseSensitive)) { int op=(presetEl.tagName().remove(DETSTR, Qt::CaseSensitive)).toInt()-1; detune[op]=presetEl.text().toInt(); } //eg else if(presetEl.tagName()=="eg") { QDomNode egNode = presetNode.firstChild(); while(!egNode.isNull()) { QDomElement egEl = egNode.toElement(); if (egEl.isNull()) continue; QString et=egEl.tagName(); if(et.contains(ARSTR, Qt::CaseSensitive)) { int op=(et.remove(ARSTR, Qt::CaseSensitive)).toInt()-1; eg[op].ar=egEl.text().toInt(); } else if(et.contains(D1RSTR, Qt::CaseSensitive)) { int op=(et.remove(D1RSTR, Qt::CaseSensitive)).toInt()-1; eg[op].d1r=egEl.text().toInt(); } else if(et.contains(D1LSTR, Qt::CaseSensitive)) { int op = (et.remove(D1LSTR, Qt::CaseSensitive)).toInt()-1; eg[op].d1l=egEl.text().toInt(); } else if(et.contains(D2RSTR, Qt::CaseSensitive)) { int op=(et.remove(D2RSTR, Qt::CaseSensitive)).toInt()-1; eg[op].d2r=egEl.text().toInt(); } else if(et.contains(RRSTR, Qt::CaseSensitive)) { int op=(et.remove(RRSTR, Qt::CaseSensitive)).toInt()-1; eg[op].rr=egEl.text().toInt(); } else if(et.contains(SHFTSTR, Qt::CaseSensitive)) { int op=(et.remove(SHFTSTR, Qt::CaseSensitive)).toInt()-1; eg[op].egShift=(egEl.text()=="VOF"?VOF: (egEl.text()=="V48"?V48: (egEl.text()=="V24"?V24:V12))); } egNode =egNode.nextSibling(); } } //pitchEg else if(presetEl.tagName()=="pitchEg") { QDomNode pitchEgNode = presetNode.firstChild(); while(!pitchEgNode.isNull()) { QDomElement pitchEgEl = pitchEgNode.toElement(); if (pitchEgEl.isNull()) continue; QString pt=pitchEgEl.tagName(); if(pt==PR1STR) pitchEg.pr1=pitchEgEl.text().toInt(); else if(pt==PR2STR) pitchEg.pr2=pitchEgEl.text().toInt(); else if(pt==PR3STR) pitchEg.pr3=pitchEgEl.text().toInt(); else if(pt==PL1STR) pitchEg.pl1=pitchEgEl.text().toInt(); else if(pt==PL2STR) pitchEg.pl2=pitchEgEl.text().toInt(); else if(pt==PL3STR) pitchEg.pl3=pitchEgEl.text().toInt(); pitchEgNode=pitchEgNode.nextSibling(); } } //outLevel else if(presetEl.tagName().contains(OUTSTR, Qt::CaseSensitive)) { int op=(presetEl.tagName().remove(OUTSTR, Qt::CaseSensitive)).toInt()-1; outLevel[op]=presetEl.text().toInt(); } //scaling else if(presetEl.tagName()=="scaling") { QDomNode scalingNode = presetNode.firstChild(); while(!scalingNode.isNull()) { QDomElement scalingEl = scalingNode.toElement(); if (scalingEl.isNull()) continue; QString st=scalingEl.tagName(); if(st.contains(RSSTR, Qt::CaseSensitive)) { int op=(st.remove(RSSTR, Qt::CaseSensitive)).toInt()-1; scaling.rate[op]=scalingEl.text().toInt(); } else if(st.contains(LSSTR, Qt::CaseSensitive)) { int op=(st.remove(LSSTR, Qt::CaseSensitive)).toInt()-1; scaling.level[op]=scalingEl.text().toInt(); } scalingNode =scalingNode.nextSibling(); } } //function else if(presetEl.tagName()=="function") { QDomNode functionNode = presetNode.firstChild(); while(!functionNode.isNull()) { QDomElement functionEl = functionNode.toElement(); if (functionEl.isNull()) continue; QString ft=functionEl.tagName(); if(ft==TRANSPOSESTR) function.transpose=functionEl.text().toInt(); else if(ft==POLYMODESTR) function.mode=(functionEl.text()=="POLY"?POLY:MONO); else if(ft==PBENDRANGESTR) function.pBendRange=functionEl.text().toInt(); else if(ft==PORTAMODESTR) function.portamento= functionEl.text()=="FINGER"?FINGER:FULL; else if(ft==PORTATIMESTR) function.portamentoTime=functionEl.text().toInt(); else if(ft==FSWSTR) function.footSw=(functionEl.text()=="POR"?POR:SUS); else if(ft==FCVOLUMESTR) function.fcVolume=functionEl.text().toInt(); else if(ft==FCPITCHSTR) function.fcPitch=functionEl.text().toInt(); else if(ft==FCAMPLITUDESTR) function.fcAmplitude=functionEl.text().toInt(); else if(ft==MWPITCHSTR) function.mwPitch=functionEl.text().toInt(); else if(ft==MWAMPLITUDESTR) function.mwAmplitude=functionEl.text().toInt(); else if(ft==BCPITCHSTR) function.bcPitch=functionEl.text().toInt(); else if(ft==BCAMPLITUDESTR) function.bcAmplitude=functionEl.text().toInt(); else if(ft==BCPITCHBIASSTR) function.bcPitchBias=functionEl.text().toInt(); else if(ft==BCEGBIASSTR) function.bcEgBias=functionEl.text().toInt(); else if(ft==ATPITCHSTR) function.atPitch=functionEl.text().toInt(); else if(ft==ATAMPLITUDESTR) function.atAmplitude=functionEl.text().toInt(); else if(ft==ATPITCHBIASSTR) function.atPitchBias=functionEl.text().toInt(); else if(ft==ATEGBIASSTR) function.atEgBias=functionEl.text().toInt(); else if(ft==REVERBRATESTR) function.reverbRate=functionEl.text().toInt(); functionNode=functionNode.nextSibling(); } } //globalDetune //else if(presetEl.tagName()=="globalDetune") // globalDetune=presetEl.text().toInt(); //Names else if(presetEl.tagName()=="name") name=presetEl.text().toLatin1().data(); //prog else if(presetEl.tagName()=="prog") prog=presetEl.text().toInt(); presetNode = presetNode.nextSibling(); } } //--------------------------------------------------------- // witePreset //--------------------------------------------------------- void Preset::writePreset(AL::Xml* xml, bool onlyUsed) { char s[MAXCHARTAG]; if((!onlyUsed || _isUsed)) { xml->stag("deicsOnzePreset version=\"1.0\""); //algorithm xml->tag(ALGSTR, QString((algorithm==FIRST? "FIRST": (algorithm==SECOND? "SECOND": (algorithm==THIRD? "THIRD": (algorithm==FOURTH? "FOURTH": (algorithm==FIFTH? "FIFTH": (algorithm==SIXTH? "SIXTH": (algorithm==SEVENTH? "SEVENTH": "EIGHTH"))))))))); //feedback xml->tag(FEEDBACKSTR, feedback); //quick edit //xml->tag(FINEBRIGHTNESSSTR, brightness); //xml->tag(MODULATIONSTR, (int)modulation); //xml->tag(GLOBALDETUNESTR, globalDetune); //xml->tag(ATTACKSTR, attack); //xml->tag(RELEASESTR, release); //lfo xml->stag("lfo"); xml->tag(WAVESTR, QString((lfo.wave==SAWUP? "SAWUP": (lfo.wave==SQUARE? "SQUARE": (lfo.wave==TRIANGL? "TRIANGL":"SHOLD"))))); xml->tag(SPEEDSTR, lfo.speed); xml->tag(DELAYSTR, lfo.delay); xml->tag(PMODDEPTHSTR, lfo.pModDepth); xml->tag(AMODDEPTHSTR, lfo.aModDepth); xml->tag(SYNCSTR, QString((lfo.sync==true? "on":"off"))); xml->etag("lfo"); //sensitivity xml->stag("sensitivity"); xml->tag(PMODSENSSTR, sensitivity.pitch); xml->tag(AMSSTR, sensitivity.amplitude); for(int i=0; itag(s, QString((sensitivity.ampOn[i]==true? "on":"off"))); } for(int i=0; itag(s, sensitivity.egBias[i]); } for(int i=0; itag(s, sensitivity.keyVelocity[i]); } xml->etag("sensitivity"); //frequency xml->stag("frequency"); for(int i=0; itag(s, frequency[i].ratio); } for(int i=0; itag(s, QString((frequency[i].isFix==true? "yes":"no"))); } for(int i=0; itag(s, frequency[i].freq); } xml->etag("frequency"); //oscWave for(int i=0; itag(s, QString((oscWave[i]==W1?"W1": (oscWave[i]==W2?"W2": (oscWave[i]==W3?"W3": (oscWave[i]==W4?"W4": (oscWave[i]==W5?"W5": (oscWave[i]==W6?"W6": (oscWave[i]==W7?"W7":"W8"))))))))); } //detune for(int i=0; itag(s, detune[i]); } //eg xml->stag("eg"); for(int i=0; itag(s, eg[i].ar); } for(int i=0; itag(s, eg[i].d1r); } for(int i=0; itag(s, eg[i].d1l); } for(int i=0; itag(s, eg[i].d2r); } for(int i=0; itag(s, eg[i].rr); } for(int i=0; itag(s, QString((eg[i].egShift==VOF?"VOF": (eg[i].egShift==V48?"V48": (eg[i].egShift==V24?"V24":"V12"))))); } xml->etag("eg"); //pitchEg xml->stag("pitchEg"); xml->tag(PR1STR,pitchEg.pr1); xml->tag(PR2STR,pitchEg.pr2); xml->tag(PR3STR,pitchEg.pr3); xml->tag(PL1STR,pitchEg.pl1); xml->tag(PL2STR,pitchEg.pl2); xml->tag(PL3STR,pitchEg.pl3); xml->etag("pitchEg"); //outLevel for(int i=0; itag(s, outLevel[i]); } //scaling xml->stag("scaling"); for(int i=0; itag(s, scaling.rate[i]); } for(int i=0; itag(s, scaling.level[i]); } xml->etag("scaling"); //function xml->stag("function"); xml->tag(TRANSPOSESTR, function.transpose); xml->tag(POLYMODESTR, QString((function.mode==POLY? "POLY":"MONO"))); xml->tag(PBENDRANGESTR, function.pBendRange); xml->tag(PORTAMODESTR, QString((function.portamento==FINGER? "FINGER":"FULL"))); xml->tag(PORTATIMESTR, function.portamentoTime); xml->tag(FSWSTR, QString((function.footSw==POR? "POR":"SUS"))); xml->tag(FCVOLUMESTR, function.fcVolume); xml->tag(FCPITCHSTR, function.fcPitch); xml->tag(FCAMPLITUDESTR, function.fcAmplitude); xml->tag(MWPITCHSTR, function.mwPitch); xml->tag(MWAMPLITUDESTR, function.mwAmplitude); xml->tag(BCPITCHSTR, function.bcPitch); xml->tag(BCAMPLITUDESTR, function.bcAmplitude); xml->tag(BCPITCHBIASSTR, function.bcPitchBias); xml->tag(BCEGBIASSTR, function.bcEgBias); xml->tag(ATPITCHSTR, function.atPitch); xml->tag(ATAMPLITUDESTR, function.atAmplitude); xml->tag(ATPITCHBIASSTR, function.atPitchBias); xml->tag(ATEGBIASSTR, function.atEgBias); xml->tag(REVERBRATESTR, function.reverbRate); xml->etag("function"); //globalDetune //xml->tag("globalDetune", globalDetune); //preset name xml->tag("name", QString(name.c_str())); //bank prog xml->tag("prog",prog); xml->etag("deicsOnzePreset"); } } //--------------------------------------------------------- // printPreset //--------------------------------------------------------- void Preset::printPreset() { printf("\n"); printf("Algorithm : %d, Feedback : %d\n", algorithm, feedback); printf("LFO : "); switch(lfo.wave) { case(SAWUP) : printf("SAWUP ,"); break; case(SQUARE) : printf("SQUARE ,"); break; case(TRIANGL) : printf("TRIANGL ,"); break; case(SHOLD) : printf("SHOLD ,"); break; default : printf("No defined, "); break; } printf("Speed : %d, Delay : %d, PModD : %d, AModD : %d, ", lfo.speed, lfo.delay, lfo.pModDepth, lfo.aModDepth); if(lfo.sync) printf("Sync\n"); else printf("Not Sync\n"); printf("LFO Pitch Sensitivity : %d, LFO Amplitude Sensitivity : %d\n", sensitivity.pitch, sensitivity.amplitude); for(int i=0; i::iterator i=_presetVector.begin(); i!=_presetVector.end(); i++) (*i)->printPreset(); } void Category::printCategory() { std::cout << " " << _categoryName << "\n"; for(unsigned int i=0; i<_subcategoryVector.size(); i++) _subcategoryVector[i]->printSubcategory(); } void Set::printSet() { std::cout << _setName << "\n"; for(unsigned int i=0; i<_categoryVector.size(); i++) _categoryVector[i]->printCategory(); } muse-4.0.0/src/synti/deicsonze/deicsonzepreset.h000066400000000000000000000417111404105311000217540ustar00rootroot00000000000000//=========================================================================== // // DeicsOnze an emulator of the YAMAHA DX11 synthesizer // // Version 0.5.5 // // deicsonzepreset.h // // // Copyright (c) 2004-2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #ifndef __DEICSONZEPRESET_H #define __DEICSONZEPRESET_H #include #include #include "al/xml.h" #include "midictrl_consts.h" #define NBROP 4 //do not change #define MAXCHARTAG 256 #define PROG_NBR 128 #define LBANK_NBR 128 #define HBANK_NBR 128 //--------------------------------------------------------- // define strings of the parameter names for load save and ctrl interface // number of ctrl // following the internal DX11 organization (c.f T81Z manual) //--------------------------------------------------------- //#define CTRLOFFSET 0x100 #define CTRLOFFSET (MusECore::CTRL_NRPN14_OFFSET) #define DECAPAR1 13 #define ARSTR "AR" #define ARLONGSTR "AttackRate" #define CTRL_AR 0+CTRLOFFSET #define MAXAR 31 #define D1RSTR "D1R" #define D1RLONGSTR "Decay1Rate" #define CTRL_D1R 1+CTRLOFFSET #define MAXD1R 31 #define D2RSTR "D2R" #define D2RLONGSTR "Decay2Rate" #define CTRL_D2R 2+CTRLOFFSET #define MAXD2R 31 #define RRSTR "RR" #define RRLONGSTR "ReleaseRate" #define CTRL_RR 3+CTRLOFFSET #define MAXRR 15 #define D1LSTR "D1L" #define D1LLONGSTR "Decay1Level" #define CTRL_D1L 4+CTRLOFFSET #define MAXD1L 15 #define LSSTR "LS" #define LSLONGSTR "LevelScaling" #define CTRL_LS 5+CTRLOFFSET #define MAXLS 99 #define RSSTR "RS" #define RSLONGSTR "RateScaling" #define CTRL_RS 6+CTRLOFFSET #define MAXRS 3 #define EBSSTR "EBS" #define EBSLONGSTR "EGBiasSensitivity" #define CTRL_EBS 7+CTRLOFFSET #define MAXEBS 7 #define AMESTR "AME" #define AMELONGSTR "AmplitudeModulationEnable" #define CTRL_AME 8+CTRLOFFSET #define KVSSTR "KVS" #define KVSLONGSTR "KeyVelocitySensitivity" #define CTRL_KVS 9+CTRLOFFSET #define MAXKVS 7 #define OUTSTR "OUT" #define OUTLONGSTR "OperatorOutputLevel" #define CTRL_OUT 10+CTRLOFFSET #define MAXOUT 99 #define RATIOSTR "Ratio" #define RATIOLONGSTR "Ratio" #define CTRL_RATIO 11+CTRLOFFSET #define MAXRATIO 64 #define DETSTR "DET" #define DETLONGSTR "Detune" #define CTRL_DET 12+CTRLOFFSET #define MAXDET 3 #define ALGSTR "ALG" #define ALGLONGSTR "Algorithm" #define CTRL_ALG 52+CTRLOFFSET #define MAXALG 7 #define FEEDBACKSTR "Feedback" #define CTRL_FEEDBACK 53+CTRLOFFSET #define MAXFEEDBACK 7 #define SPEEDSTR "Speed" #define SPEEDLONGSTR "LFOSpeed" #define CTRL_SPEED 54+CTRLOFFSET #define MAXSPEED 99 #define DELAYSTR "Delay" //TODO LFOD #define DELAYLONGSTR "LFODelay" #define CTRL_DELAY 55+CTRLOFFSET #define MAXDELAY 99 #define PMODDEPTHSTR "PModDepth" #define PMODDEPTHLONGSTR "PitchModulationDepth" #define CTRL_PMODDEPTH 56+CTRLOFFSET #define MAXPMODDEPTH 99 #define AMODDEPTHSTR "AModDepth" #define AMODDEPTHLONGSTR "AmplitudeModulationDepth" #define CTRL_AMODDEPTH 57+CTRLOFFSET #define MAXAMODDEPTH 99 #define SYNCSTR "Sync" #define SYNCLONGSTR "LFOSync" #define CTRL_SYNC 58+CTRLOFFSET #define WAVESTR "Wave" #define WAVELONGSTR "LFOWave" #define CTRL_WAVE 59+CTRLOFFSET #define MAXWAVE 3 #define PMODSENSSTR "PModSens" #define PMODSENSLONGSTR "PitchModulationSensitivity" #define CTRL_PMODSENS 60+CTRLOFFSET #define MAXPMODSENS 7 #define AMSSTR "AMS" #define AMSLONGSTR "AmplitudeModulationSensitivity" #define CTRL_AMS 61+CTRLOFFSET #define MAXAMS 3 #define TRANSPOSESTR "Transpose" #define CTRL_TRANSPOSE 62+CTRLOFFSET #define MAXTRANSPOSE 24 #define POLYMODESTR "PolyMode" #define CTRL_POLYMODE 63+CTRLOFFSET #define PBENDRANGESTR "PBendRange" #define PBENDRANGELONGSTR "PitchBendRange" #define CTRL_PBENDRANGE 64+CTRLOFFSET #define MAXPBENDRANGE 12 #define PORTAMODESTR "PortaMode" #define PORTAMODELONGSTR "PortamentoMode" #define CTRL_PORTAMODE 65+CTRLOFFSET #define PORTATIMESTR "PortaTime" #define PORTATIMELONGSTR "PortamentoTime" #define CTRL_PORTATIME 66+CTRLOFFSET #define MAXPROTATIME 99 #define FCVOLUMESTR "FCVolume" #define FCVOLUMELONGSTR "FootControllerVolume" #define CTRL_FCVOLUME 67+CTRLOFFSET #define MAXFCVOLUME 99 #define FSWSTR "FSW" #define FSWLONGSTR "FootSwitch" #define CTRL_FSW 68+CTRLOFFSET #define MAXFSW 99 #define MWPITCHSTR "MWPitch" #define MWPITCHLONGSTR "ModulationWheelPitch" #define CTRL_MWPITCH 71+CTRLOFFSET #define MAXMWPITCH 99 #define MWAMPLITUDESTR "MWAmplitude" #define MWAMPLITUDELONGSTR "ModulationWheelAmplitude" #define CTRL_MWAMPLITUDE 72+CTRLOFFSET #define MAXMWAMPLITUDE 99 #define BCPITCHSTR "BCPitch" #define BCPITCHLONGSTR "BreathControlPitch" #define CTRL_BCPITCH 73+CTRLOFFSET #define MAXBCPITCH 99 #define BCAMPLITUDESTR "BCAmplitude" #define BCAMPLITUDELONGSTR "BreathControlAmplitude" #define CTRL_BCAMPLITUDE 74+CTRLOFFSET #define MAXBCAMPLITUDE 99 #define BCPITCHBIASSTR "BCPitchBias" #define BCPITCHBIASLONGSTR "BreathControlPitchBias" #define CTRL_BCPITCHBIAS 75+CTRLOFFSET #define MAXBCPITCHBIAS 50 #define BCEGBIASSTR "BCEGBias" #define BCEGBIASLONGSTR "BreathControlEGBias" #define CTRL_BCEGBIAS 76+CTRLOFFSET #define MAXBCEGBIAS 99 #define MIDATTACK 64 #define ATPITCHSTR "ATPitch" #define ATPITCHLONGSTR "AfterTouchPitch" #define CTRL_ATPITCH 77+CTRLOFFSET #define MAXATPITCH 99 #define ATAMPLITUDESTR "ATAmplitude" #define ATAMPLITUDELONGSTR "AfterTouchAmplitude" #define CTRL_ATAMPLITUDE 78+CTRLOFFSET #define MAXATAMPLITUDE 99 #define ATPITCHBIASSTR "ATPitchBias" #define ATPITCHBIASLONGSTR "AfterTouchPitchBias" #define CTRL_ATPITCHBIAS 79+CTRLOFFSET #define MAXATPITCHBIAS 50 #define ATEGBIASSTR "ATEGBias" #define ATEGBIASLONGSTR "AfterTouchEGBias" #define CTRL_ATEGBIAS 80+CTRLOFFSET #define MAXATEGBIAS 99 #define PR1STR "PR1" #define PR1LONGSTR "PitchRateEG1" #define CTRL_PR1 81+CTRLOFFSET #define MAXPR 99 #define PR2STR "PR2" #define PR2LONGSTR "PitchRateEG2" #define CTRL_PR2 82+CTRLOFFSET #define PR3STR "PR3" #define PR3LONGSTR "PitchRateEG3" #define CTRL_PR3 83+CTRLOFFSET #define PL1STR "PL1" #define PL1LONGSTR "PitchLevelEG1" #define CTRL_PL1 84+CTRLOFFSET #define MAXPL 99 #define PL2STR "PL2" #define PL2LONGSTR "PitchLevelEG2" #define CTRL_PL2 85+CTRLOFFSET #define PL3STR "PL3" #define PL3LONGSTR "PitchLevelEG3" #define CTRL_PL3 86+CTRLOFFSET #define DECAPAR2 5 #define FIXSTR "FIX" #define FIXLONGSTR "FixedFrequency" #define CTRL_FIX 100+CTRLOFFSET #define FIXRANGESTR "FixRange" #define FIXRANGELONGSTR "FixedFrequencyRange" #define CTRL_FIXRANGE 101+CTRLOFFSET #define MAXFIXRANGE 255 #define OSWSTR "OSW" #define OSWLONGSTR "OperatorWaveform" #define CTRL_OSW 103+CTRLOFFSET #define MAXOSW 7 #define SHFTSTR "SHFT" #define SHFTLONGSTR "EGShift" #define CTRL_SHFT 104+CTRLOFFSET #define MAXSHFT 3 #define REVERBRATESTR "ReverbRate" #define CTRL_REVERBRATE 120+CTRLOFFSET #define MAXREVERBRATE 7 #define FCPITCHSTR "FCPitch" #define FCPITCHLONGSTR "FootControllerPitch" #define CTRL_FCPITCH 121+CTRLOFFSET #define MAXFCPITCH 99 #define FCAMPLITUDESTR "FCAmplitude" #define FCAMPLITUDELONGSTR "FootControllerAmplitude" #define CTRL_FCAMPLITUDE 122+CTRLOFFSET #define MAXFCAMPLITUDE 99 #define CHANNELPANSTR "ChannelPan" #define CTRL_CHANNELPAN 123+CTRLOFFSET #define MAXCHANNELPAN 127 #define CHANNELDETUNESTR "ChannelDetune" #define CTRL_CHANNELDETUNE 124+CTRLOFFSET #define MAXCHANNELDETUNE 63 #define CHANNELVOLUMESTR "ChannelVolume" #define CTRL_CHANNELVOLUME 125+CTRLOFFSET #define MAXCHANNELVOLUME 255 #define FINEBRIGHTNESSSTR "FineBrightness" #define CTRL_FINEBRIGHTNESS 126+CTRLOFFSET #define MAXFINEBRIGHTNESS 4095 #define MIDFINEBRIGHTNESS (MAXFINEBRIGHTNESS+1)/2 #define BRIGHTNESSSTR "Brightness" #define MAXBRIGHTNESS 127 #define MIDBRIGHTNESS 64 #define MAXMODULATION 127 #define MODULATIONSTR "Modulation" #define ATTACKSTR "Attack" #define MAXATTACK 127 #define MIDATTACK 64 #define RELEASESTR "Attack" #define MAXRELEASE 127 #define MIDRELEASE 64 #define NBRVOICESSTR "NumberOfVoices" #define MINNBRVOICES 1 #define CTRL_NBRVOICES 127+CTRLOFFSET #define CHANNELENABLESTR "ChannelEnable" #define MAXCHANNELENABLE 1 #define MINCHANNELENABLE 0 #define CTRL_CHANNELENABLE 128+CTRLOFFSET class Preset; class Subcategory; class Category; class Set; //--------------------------------------------------------- // Algorithm //--------------------------------------------------------- enum Algorithm { FIRST, // Op 0 modulated by Op 1 modulated by Op 2 modulated by Op3 SECOND, // Op 0 modulated by Op 1 modulated by both Op 2 and Op 3 THIRD, // Op 0 modulated by both Op 3 and Op 1 modulated by Op 2 FOURTH, // Op 0 modulated by both Op 1 and Op 2 modulated by Op 3 FIFTH, // (Op 0 modulated by Op 1) add to (Op 2 modulated by Op 3) SIXTH, // addition of the three Op 0, 1, 2 all modulated by Op 3 SEVENTH, // addition of the three Op 0, 1, 2 with 2 modulated by Op3 EIGHTH // addition of the four Op 0, 1, 2, 3 }; //--------------------------------------------------------- // Wave of the low frequency modulation //--------------------------------------------------------- enum Wave { SAWUP, SQUARE, TRIANGL, SHOLD }; //--------------------------------------------------------- // Lfo, low frequency modulation //--------------------------------------------------------- struct Lfo { Wave wave; unsigned char speed; //0 to 99 unsigned char delay; //0 to 99 unsigned char pModDepth; //0 to 99 unsigned char aModDepth; //0 to 99 bool sync; }; //--------------------------------------------------------- // Sensitivity // of the frequency and amplitude of the lfo // and the key velocity //--------------------------------------------------------- struct Sensitivity { unsigned char pitch; //0 to 7 unsigned char amplitude; //0 to 3 bool ampOn[NBROP]; unsigned char egBias[NBROP]; //O to 7 unsigned char keyVelocity[NBROP]; //0 to 7 }; //--------------------------------------------------------- // Frequency //--------------------------------------------------------- struct Frequency { double ratio; bool isFix; //if isFix no ratio but frequency double freq; }; //--------------------------------------------------------- // OscWave //--------------------------------------------------------- enum OscWave { W1, //sine wave W2, //sine� relative W3, //half sine W4, //half sine� relative W5, W6, W7, W8 }; enum egShiftValue {VOF, V48, V24, V12}; //--------------------------------------------------------- // Eg // Envelope //--------------------------------------------------------- struct Eg { unsigned char ar; //0 to 31 speed attack unsigned char d1r; //0 to 31 speed decay unsigned char d1l; //0 to 15 level sustain unsigned char d2r; //0 to 31 speed of sustain unsigned char rr; //1 to 15 egShiftValue egShift; }; //--------------------------------------------------------- // PitchEg //--------------------------------------------------------- struct PitchEg { unsigned char pr1;//0 to 99 unsigned char pr2;//0 to 99 unsigned char pr3;//0 to 99 unsigned char pl1;//0 to 99 unsigned char pl2;//0 to 99 unsigned char pl3;//0 to 99 }; //--------------------------------------------------------- // Scaling //--------------------------------------------------------- struct Scaling { unsigned char rate[NBROP];//0 to 3 unsigned char level[NBROP];//0 to 99 }; //--------------------------------------------------------- // Mode //--------------------------------------------------------- enum Mode { POLY, MONO }; //--------------------------------------------------------- // Portamento //--------------------------------------------------------- enum Portamento { FINGER, FULL }; //--------------------------------------------------------- // FootSw //--------------------------------------------------------- enum FootSw { POR, SUS }; //--------------------------------------------------------- // Function //--------------------------------------------------------- struct Function { int transpose; Mode mode; unsigned char pBendRange;//0 to 12 Portamento portamento; unsigned char portamentoTime;//0 to 99 FootSw footSw; unsigned char fcVolume;//0 to 99 unsigned char fcPitch;//0 to 99 unsigned char fcAmplitude;//0 to 99 unsigned char mwPitch;//0 to 99 unsigned char mwAmplitude;//0 to 99 unsigned char bcPitch;//0 to 99 unsigned char bcAmplitude;//0 to 99 signed char bcPitchBias;//-50 to 50 unsigned char bcEgBias;//0 to 99 unsigned char atPitch;//0 to 99 unsigned char atAmplitude;//0 to 99 signed char atPitchBias;//-50 to 50 unsigned char atEgBias;//0 to 99 signed char reverbRate;//O=off, 1 to 7 }; //--------------------------------------------------------- // Preset class //--------------------------------------------------------- class Preset { public: Subcategory* _subcategory; //subcategory parent bool _isUsed; //false if the preset has never been used or modified, //in this case the preset is not going to be //save with the project //Attributes Algorithm algorithm; unsigned char feedback; //0 to 7 Lfo lfo; Sensitivity sensitivity; Frequency frequency[NBROP]; OscWave oscWave[NBROP]; signed char detune[NBROP]; //-3 to 3 Eg eg[NBROP]; PitchEg pitchEg; unsigned char outLevel[NBROP]; //0 to 99 Scaling scaling; Function function; //int globalDetune; //-31 to 31 //now to the channel std::string name; //unsigned char modulation; //0 to 127 int prog; //0 to 127 //Methods void printPreset(); void initPreset(); void readPreset(QDomNode qdn); void writePreset(AL::Xml* xml, bool onlyUsed); void linkSubcategory(Subcategory* sub); void merge(Preset* p); //copy the data of p in the preset void setIsUsed(bool b); //set flag _isUsed and transmit in the parents void getHBankLBankProg(int* h, int* l, int* p); //return the hbank, lbank and prog of the preset //Constructor destructor Preset(); Preset(Subcategory* sub); Preset(Subcategory* sub, int prog); ~Preset(); }; //--------------------------------------------------------------- // Bank, organized by a tree of category, subcategory, preset //--------------------------------------------------------------- class Subcategory { public: Category* _category;//parent category bool _isUsed; //false if the subcategory has never been used or modified, //in this case the subcategory is not going to be //save with the project std::string _subcategoryName; int _lbank; //0 to 127 std::vector _presetVector; Preset* findPreset(int prog); void readSubcategory(QDomNode subNode); void writeSubcategory(AL::Xml* xml, bool onlyUsed); void printSubcategory(); void linkCategory(Category* cat); void unlink(); bool isFreeProg(int prog); int firstFreeProg(); void merge(Preset*); //Constructor destructor Subcategory(); Subcategory(Category* cat); Subcategory(const std::string name); Subcategory(Category* cat, const std::string name, int lbank); ~Subcategory(); }; class Category { public: Set* _set;//parent set bool _isUsed; //false if the category has never been used or modified, //in this case the category is not going to be //save with the project std::string _categoryName; int _hbank; //0 to 127 std::vector _subcategoryVector; Subcategory* findSubcategory(int lbank); Preset* findPreset(int lbank, int prog); void readCategory(QDomNode catNode); void writeCategory(AL::Xml* xml, bool onlyUsed); void printCategory(); void linkSet(Set* s); void unlink(); bool isFreeLBank(int lbank); int firstFreeLBank(); //return -1 if no free void merge(Subcategory*); //Constructor Destructor Category(); Category(Set* s); Category(Set* s,const std::string name, int hbank); ~Category(); }; class Set { public: std::string _setName; std::vector _categoryVector; Preset* findPreset(int hbank, int lbank, int prog); Subcategory* findSubcategory(int hbank, int lbank); Category* findCategory(int hbank); void readSet(QDomNode setNode); void writeSet(AL::Xml* xml, bool onlyUsed); void printSet(); bool isFreeHBank(int hbank); int firstFreeHBank(); void merge(Category*); //Constructor Destructor Set(const std::string name){_setName=name;} ~Set() { while(!_categoryVector.empty()) delete(*_categoryVector.begin()); } }; #endif /* __DEICSONZE_H */ muse-4.0.0/src/synti/deicsonze/pics/000077500000000000000000000000001404105311000173275ustar00rootroot00000000000000muse-4.0.0/src/synti/deicsonze/pics/SHold.fig000066400000000000000000000002631404105311000210300ustar00rootroot00000000000000#FIG 3.2 Landscape Center Metric A4 100.00 Single -2 1200 2 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 8 720 968 855 968 855 1035 990 1035 990 900 1125 900 1125 945 1260 945 muse-4.0.0/src/synti/deicsonze/pics/SHold.png000066400000000000000000000006121404105311000210450ustar00rootroot00000000000000PNG  IHDR' s/ pHYs M MέNtEXtSoftwareESP Ghostscript 7.07IDATx͔Q aAR-X h ,T3۲4͋{YDDDⶇ7I/՝sZ/1 ZwM9?>SZӴ|sMaѢ@D]MF19[G BIENDB`PNG  IHDR' s/ pHYs M MέNtEXtSoftwareESP Ghostscript 7.07IDATxc?uQ[Gm%^ IENDB`muse-4.0.0/src/synti/deicsonze/pics/algo1.fig000066400000000000000000000016551404105311000210300ustar00rootroot00000000000000#FIG 3.2 Landscape Center Metric A4 100.00 Single -2 1200 2 6 2070 1530 4275 1890 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 2430 1665 2655 1665 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3015 1665 3240 1665 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3600 1665 3825 1665 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 4185 1665 4275 1665 4275 1890 4006 1890 4006 1800 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2070 1530 2430 1530 2430 1800 2070 1800 2070 1530 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2655 1530 3015 1530 3015 1800 2655 1800 2655 1530 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3240 1530 3600 1530 3600 1800 3240 1800 3240 1530 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3825 1530 4185 1530 4185 1800 3825 1800 3825 1530 4 0 0 50 -1 0 12 0.0000 4 180 336 2093 1718 Op1\001 4 0 0 50 -1 0 12 0.0000 4 180 336 2677 1718 Op2\001 4 0 0 50 -1 0 12 0.0000 4 180 336 3262 1718 Op3\001 4 0 0 50 -1 0 12 0.0000 4 180 336 3847 1718 Op4\001 -6 muse-4.0.0/src/synti/deicsonze/pics/algo1.png000066400000000000000000000017621404105311000210460ustar00rootroot00000000000000PNG  IHDRoqz pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07"IDAThq qc2GC"#=%ʇ"/< xI)B1cY S*R[!bȖJaS-[,WC*hUx#L@S̄Ƒ53%77e=}&U`n.h+6dn1j! z$ jqڡ5KSI}BSDR ѣN+耧cBh3MؚSMVٚ!t> -i*OC^mu۩[U4gnCzt;ch3FmoePLesGV‘4oΠ$sjPP,ϙK4ιt>u\.UF ,=?%Cnj1Ң3,NUʘ2:+yL B=26pw2WiK\E4j-|KQBUo(T%sÖ.ٗchQsh.'.МŭvmFhz3f"t9a;UDvҭiv!)Nm95 old9,[Vk8ۢK:}v$yk_@G cο!咂(ԎnрfڝڝڝڝںSoi]_IENDB`PNG  IHDRoqz pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07"[IDATh @緄 YgjAdjAdjAdjAdjAdjAdj<3IENDB`muse-4.0.0/src/synti/deicsonze/pics/algo2.fig000066400000000000000000000016501404105311000210240ustar00rootroot00000000000000#FIG 3.2 Landscape Center Metric A4 100.00 Single -2 1200 2 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 2475 1710 2700 1710 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3060 1710 3195 1710 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4 3285 1530 3195 1530 3195 1800 3285 1800 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2115 1575 2475 1575 2475 1845 2115 1845 2115 1575 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2700 1575 3060 1575 3060 1845 2700 1845 2700 1575 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3285 1665 3645 1665 3645 1890 3285 1890 3285 1665 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3285 1440 3645 1440 3645 1665 3285 1665 3285 1440 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 3645 1800 3735 1800 3735 1935 3465 1935 3465 1890 4 0 0 50 -1 0 12 0.0000 4 150 315 3307 1590 Op3\001 4 0 0 50 -1 0 12 0.0000 4 150 315 3315 1822 Op4\001 4 0 0 50 -1 0 12 0.0000 4 165 315 2138 1762 Op1\001 4 0 0 50 -1 0 12 0.0000 4 150 315 2722 1762 Op2\001 muse-4.0.0/src/synti/deicsonze/pics/algo2.png000066400000000000000000000034601404105311000210440ustar00rootroot00000000000000PNG  IHDRs$5 pHYs M MέNtEXtSoftwareESP Ghostscript 7.07IDATxZ1n:\@>դ 0?2@o@vn#P/ A*rpR_ L˒Hi$.PppqգiiG94B!1+luRJJ)!R941cuZk0 dFdײZ8J)!Bc*$2 maU\ecZ7'Dڶ/4MqBVJ-۶bGBv"dUUyk6u#Wqeϟ_|a%✻"tUUGCZ8z9kW]Z/Ym}j" @17:疉*8Mi˴!d!V6M94M7أcOx_,^jFa}xxǏq퐟SAQ=lUUB1>ծX6A)E)$'Cu tԦRu]_jٸ{T,'Zl&Yz>==!Q5t=ǏF ٢(؀،t-eO0 ;m7hVP\وު"|\zXkaEshC L-ʠpE9"GT/$HK!'1Ӝ1c/62֝sS: N$($.,xy^D7fuy,K1Tx2?a>'Kc}8IY$J> R xe_R'@ k"C$ l4MW@ !sON|<ѻGsnV"x~~,D;ME}wRJ_k]M'1|g!$³z&zyJnuVoR~P 9lqaBL~@)S B[Q>T BhPn]P3zɟW-R8sRK}!0餵2q)U܊p8m0ͣu( {Ϡ!!D9L~:ks%Ui4Y4?2"PJ0RjIENDB`PNG  IHDRs$5 pHYs M MέNtEXtSoftwareESP Ghostscript 7.07jIDATx @7.؞Eg+V8[qlيg+V8[qlيg+V8[qlيg+V8[qlيg+q>EP9IENDB`muse-4.0.0/src/synti/deicsonze/pics/algo3.fig000066400000000000000000000016501404105311000210250ustar00rootroot00000000000000#FIG 3.2 Landscape Center Metric A4 100.00 Single -2 1200 2 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3060 1575 3285 1575 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 2475 1710 2610 1710 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 3060 1800 3150 1800 3150 1935 2880 1935 2880 1890 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4 2700 1530 2610 1530 2610 1800 2700 1800 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3285 1440 3645 1440 3645 1710 3285 1710 3285 1440 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2700 1665 3060 1665 3060 1890 2700 1890 2700 1665 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2700 1440 3060 1440 3060 1665 2700 1665 2700 1440 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2115 1575 2475 1575 2475 1845 2115 1845 2115 1575 4 0 0 50 -1 0 12 0.0000 4 150 315 2722 1590 Op2\001 4 0 0 50 -1 0 12 0.0000 4 150 315 2730 1822 Op4\001 4 0 0 50 -1 0 12 0.0000 4 150 315 3307 1627 Op3\001 4 0 0 50 -1 0 12 0.0000 4 165 315 2138 1762 Op1\001 muse-4.0.0/src/synti/deicsonze/pics/algo3.png000066400000000000000000000034611404105311000210460ustar00rootroot00000000000000PNG  IHDRm$ # pHYs M MέNtEXtSoftwareESP Ghostscript 7.07IDATxZA:-FsHnEU,r@$:70l@ ##rKYf_T U`WuL4Mm۶֚y1!$I$Iމ=Qk]!RJ)=1{)eǜ8E}_4I۶RB/[*0 sRҰV8sڹ70 is 0ƚY2)0 0a-+؎B nڔi xB!Di9n-8 |$If 4MUUavnK{=:}9P u]kc Fh'JJieJa(}!/Ϯ'[9R ;b 0{="8J]`J)6 a].l?t Bn2 Bo. RJ0ڶXp@%=RƵrE)R^҆7|m[Ji?~.߿V)k7!˲v4H)4=+ !1QeYfqU<Qm. ̲ׯQ8ӧOajz:>\)jJxU-= ܽI __xm,',H!D@fD$IFxcdž >6|lcdž >6|lcdž >6|lcdž >6|lcEHIENDB`muse-4.0.0/src/synti/deicsonze/pics/algo4.fig000066400000000000000000000016501404105311000210260ustar00rootroot00000000000000#FIG 3.2 Landscape Center Metric A4 100.00 Single -2 1200 2 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 2475 1710 2610 1710 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4 2700 1530 2610 1530 2610 1800 2700 1800 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2700 1665 3060 1665 3060 1890 2700 1890 2700 1665 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2700 1440 3060 1440 3060 1665 2700 1665 2700 1440 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2115 1575 2475 1575 2475 1845 2115 1845 2115 1575 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3285 1620 3645 1620 3645 1890 3285 1890 3285 1620 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 3645 1755 3735 1755 3735 1530 3465 1530 3465 1620 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3060 1755 3285 1755 4 0 0 50 -1 0 12 0.0000 4 150 315 2722 1590 Op2\001 4 0 0 50 -1 0 12 0.0000 4 150 315 2730 1822 Op3\001 4 0 0 50 -1 0 12 0.0000 4 165 315 2138 1762 Op1\001 4 0 0 50 -1 0 12 0.0000 4 150 315 3307 1807 Op4\001 muse-4.0.0/src/synti/deicsonze/pics/algo4.png000066400000000000000000000036401404105311000210460ustar00rootroot00000000000000PNG  IHDRs!egM pHYs M MέNtEXtSoftwareESP Ghostscript 7.07NIDATxZ1 ś\SRGG} @ ).R$t9Oc;Wv0o$8眵Z30Y}YcB!co5Xk1UUZkJiu6MC)V=w^u]k1B$I,\)ݻ$I$qUUU-mފ}몪j&4J))kCI`?\Jykw9=; |GBHӯeRʢ(Icag0nbz-_~}UQI27ZIkMBxaRƘiBZl96c|{R*.D}m۾)qPJ1Y6x{8xZ*8硅2 Mәa9Y-pBJ)UJom[ŬR1!dZk÷/dkmeYu9!DYeY:B*HIyz6˲$I1J*xcX2GJh5ώ=cs>>>>>})"pB 9yM2"{:H-[@>ۡ>.9 _Za@ia(bQZt,;N=+(*q7B,z~qM۲.H@O"0*Ab ZX@y!msn9 n0BG4ٛim?gÍv6=X"5c`޽0 ̰𪪖K@y?Kѿ}PRT`WJeٶtʲl*t>a^L[~K)mc=4𠙝5V\ !N11Y9R^Yd !Bu m[!4iIg]O>Ɂ]m %^'~.g e燖CuZxfL$E\*zf?@.J-90lթBw-zJ>eYƕ!v0<08z-Dp@\{ o?i8Z@.ROӝZQ04Mv rZvlanv]7K{={Q?:;34M xcqA},A)0(l9a*rM8?rmpW (Pi?}PuWDȅs;,$v]GU7("~B{s1cL)]mFB!VH)16?Y~2]TιCacRJBHQurQWU1s!Ji|PUU+kjZ,vt1˲v1u֎h[;~/je}o%hf05cl݌EQ1x/o=.?{m,ˆal DkM)b]ŷ?icK򑵖R{i΁t!8|OLK?3䟤>(,$I4s81u]???cwb1|>'I$IY!Ji$cJyGn'A$ BR.~7mc 'R˅(p뺺ՋZ-v1J)lqBbn&'}|p'6tvtظ뺢(BoŜ.1f1wj%@}|ʿmK0ڶ%/ 7[b q΋!nmyvN[>|up#@K-_^^VE7MjsY~:!s|+qi[~|ye.9wgEZ'qiUcL/Q N!w2ƦW۶W-L0N8>pD1fkrvAُ9?*cà Ӎ134M$BTURJ9BHJ_~Œ3:y"R$m4M9u]@)?fgbqq<"oMӬSb謵o0 #tWqNU(" C3B/BHLJ n#>5p3Υn&o=(Vi`k1? 3< 4jGf|,R]T({w]pw%{w1 Ȳ_-'?_>!\BREepRJ,>)coaNc{{{||CeY3eoEL^'yyyzz>w|;ʲ PO7o}Q! q__z߂Zi c1-"IENDB`PNG  IHDRoqz pHYs M MέNtEXtSoftwareESP Ghostscript 7.07[IDATx @緄 YgjAdjAdjAdjAdjAdjAdj<3 ^IENDB`muse-4.0.0/src/synti/deicsonze/pics/algo6.fig000066400000000000000000000021371404105311000210310ustar00rootroot00000000000000#FIG 3.2 Landscape Center Metric A4 100.00 Single -2 1200 2 6 2115 1575 2475 1845 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2115 1575 2475 1575 2475 1845 2115 1845 2115 1575 4 0 0 50 -1 0 12 0.0000 4 165 315 2138 1762 Op1\001 -6 6 3195 1575 3555 1845 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3195 1575 3555 1575 3555 1845 3195 1845 3195 1575 4 0 0 50 -1 0 12 0.0000 4 150 315 3218 1762 Op3\001 -6 6 3825 1575 4275 1935 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 3825 1575 4185 1575 4185 1845 3825 1845 3825 1575 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 4185 1710 4275 1710 4275 1935 4005 1935 4005 1845 4 0 0 50 -1 0 12 0.0000 4 150 315 3847 1762 Op4\001 -6 2 4 0 1 0 7 50 -1 -1 0.000 0 0 7 0 0 5 2655 1575 3015 1575 3015 1845 2655 1845 2655 1575 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 3825 1710 3555 1710 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4 3690 1710 3690 1530 2835 1530 2835 1575 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3 2835 1530 2295 1530 2295 1575 4 0 0 50 -1 0 12 0.0000 4 150 315 2678 1762 Op2\001 4 0 0 50 -1 0 12 0.0000 4 75 105 3060 1755 +\001 4 0 0 50 -1 0 12 0.0000 4 75 105 2520 1755 +\001 muse-4.0.0/src/synti/deicsonze/pics/algo6.png000066400000000000000000000032101404105311000210410ustar00rootroot00000000000000PNG  IHDR+ pHYs M MέNtEXtSoftwareESP Ghostscript 7.078IDATxZm*}9%Dl K %L P]S-mF+arorbo3@4 򀔲:g~|'qp1?F?A,|?AeY c聳bkUU6ir><1in8Oaa/UUc0ƔM9s΃9a{/8β,L`xi@BMFyn4lx[p}sBTm2Ʋ,;kJRj,)!sOO%cH.b9m9%0l`˺޴% u]u@0('n S !zٴ&`78:lra'PYmzEH)GQMc|qEOg)%!`ss@nq0[1ꧦiZ؁V^m qs 6WX$՚ .UC|c1ƻcyV&N&UUyQ0Rrc=CQBkUB8>Mujx>u]{  p\A-MEXWB3$( O\w ba_C_|_CB X]n_TU繽v$]uB(˲+CqkHp[UK0Eyڛ!l[Bc"[Z@羅;Ádlf">hZdAe~/>TcoԼS|c /gYY4ZF>q[s.ZB(3RJI)NkeYeM,wAJuRjVJa)yysB13sY1nrcT\gK |N$I!dSQ4(%4$I)y"Ix'iq]ƘiH3ڶk4<‡a"a)DY<0 6ȟ:B4MR;P =ppRhr躮( ;v ǽ8 I@4M#`By۶!U8˷7G]Y\żCA6,R<ϮCNN}b-!oVw9}Ȑ_vjVj;!`BR뺻+:"Vs VMlύa wq6=#oݹkOVu~yyq^H4I!D]P";u! Wi8EQ n4q47McgW~:ZQJ}C9"zmJq.Cq3!U6d!GvﶻO`'$:ZhwTg#o!ȲQ_]  k?9ّaY?!zc9aVÿ0r!ć$ιeYeeY!d5^] yMWGॵβ )`躪*rǾ9Mc;@=|ZCѶw_dˈg^.xOn[eZ6a:Bw>R:瞞`k᜻zx̲Lk HOTvB%Aucʠo0 MӀGʯ)PAu<ìv1^+9aؙI`\.WIPL:6@ٓ ~9ǗBCrzGk)jq*ၹ'u]xjF2\MM 4D2:Ukm緹I)V\=J !1313^^^:v\YZ×[֐]c{2JW2|'}s9'76ZC) `p<]1|||!qDI)5"+Y v13le| $~|zM'ܽ* @t IENDB`PNG  IHDR' s/ pHYs M MέNtEXtSoftwareESP Ghostscript 7.07IDATxc?uQ[Gm%^ IENDB`muse-4.0.0/src/synti/deicsonze/pics/triangl.fig000066400000000000000000000002611404105311000214550ustar00rootroot00000000000000#FIG 3.2 Landscape Center Metric A4 100.00 Single -2 1200 2 6 360 630 900 765 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 6 360 698 428 630 563 765 698 630 833 765 900 698 -6 muse-4.0.0/src/synti/deicsonze/pics/triangl.png000066400000000000000000000006631404105311000215020ustar00rootroot00000000000000PNG  IHDR' s/ pHYs M MέNtEXtSoftwareESP Ghostscript 7.07IDATxA ٵ,, ` 3fM[H;4_~ 1""o ~aD;ۆK)1s)Ek-&gf)rγe"b|.T5=o{aǤVrc?,| j5crAPJ1ѰmkoIENDB`PNG  IHDR' s/ pHYs M MέNtEXtSoftwareESP Ghostscript 7.07IDATxc?uQ[Gm%^ IENDB`muse-4.0.0/src/synti/deicsonze/pics/wave1.fig000066400000000000000000000022171404105311000210430ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1650 1350 2325 1500 2 1 0 1 -1 -1 10 0 -1 0.000 0 0 0 0 0 100 1658 1424 1665 1415 1670 1406 1678 1397 1684 1388 1692 1380 1697 1373 1704 1367 1711 1361 1718 1357 1724 1353 1731 1351 1738 1350 1745 1350 1751 1352 1758 1354 1765 1358 1770 1363 1778 1368 1784 1375 1792 1382 1797 1390 1804 1399 1811 1408 1818 1418 1824 1427 1831 1436 1837 1445 1845 1454 1850 1463 1858 1471 1864 1477 1871 1484 1877 1489 1884 1493 1891 1496 1898 1498 1904 1499 1911 1498 1918 1497 1925 1494 1931 1490 1937 1485 1945 1479 1950 1472 1958 1465 1964 1456 1971 1447 1977 1439 1984 1429 1991 1420 1998 1411 2003 1401 2011 1392 2017 1384 2025 1376 2030 1370 2037 1364 2044 1359 2051 1355 2057 1352 2064 1350 2071 1350 2078 1351 2084 1353 2091 1356 2098 1360 2103 1365 2111 1371 2117 1379 2125 1386 2130 1395 2137 1403 2144 1413 2151 1422 2157 1432 2164 1441 2170 1450 2178 1458 2183 1466 2191 1474 2197 1481 2204 1486 2210 1491 2217 1495 2224 1497 2231 1499 2237 1499 2244 1497 2251 1496 2258 1492 2264 1487 2270 1482 2278 1476 2283 1468 2291 1461 2297 1452 2304 1443 2310 1434 2317 1424 -6 muse-4.0.0/src/synti/deicsonze/pics/wave1.gnuplot000066400000000000000000000000461404105311000217640ustar00rootroot00000000000000set terminal fig plot [0:4*pi] sin(x) muse-4.0.0/src/synti/deicsonze/pics/wave1.png000066400000000000000000000010711404105311000210570ustar00rootroot00000000000000PNG  IHDR. ] pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07""IDAT8͔1 EM M I W+0G 5P3<+pIԬ'`őōG@S~ï~Q?VussSJ5Ms XU_DDD !e [u&H)o(Q ·Wz{YJO(c̹WJ9"*Rʫ҈4u]Ӯ:y9T"4(d%Dh2@ȲrպZkIxYVZ1d "7mۜ|em8}cq=8a"ƒ ~D9wgw" !" ZA> IENDB`PNG  IHDR. ] pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07")IDAT8c?Lu 60l`)SQ`K ݟIENDB`muse-4.0.0/src/synti/deicsonze/pics/wave2.fig000066400000000000000000000023151404105311000210430ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1650 1350 2325 1500 2 1 0 1 -1 -1 10 0 -1 0.000 0 0 0 0 0 2 2079 1350 2079 1353 2 1 0 1 -1 -1 10 0 -1 0.000 0 0 0 0 0 100 1658 1425 1665 1423 1670 1419 1678 1414 1685 1407 1692 1398 1697 1389 1704 1380 1711 1371 1719 1363 1724 1357 1731 1353 1738 1350 1743 1351 1751 1353 1758 1358 1765 1365 1770 1373 1777 1382 1784 1391 1792 1401 1797 1409 1804 1415 1811 1421 1818 1424 1824 1425 1831 1426 1838 1431 1845 1436 1850 1444 1857 1453 1865 1462 1872 1472 1877 1480 1884 1487 1891 1494 1899 1497 1904 1499 1911 1498 1918 1495 1923 1489 1930 1483 1938 1473 1945 1465 1950 1455 1957 1446 1964 1438 1971 1431 1977 1427 1984 1425 1991 1425 1998 1422 2003 1417 2011 1411 2018 1403 2025 1394 2030 1385 2037 1375 2044 1367 2052 1359 2057 1354 2064 1351 2071 1350 2076 1351 2084 1355 2091 1361 2098 1369 2103 1377 2110 1387 2118 1396 2125 1405 2130 1412 2137 1419 2144 1422 2151 1425 2157 1425 2164 1428 2171 1433 2178 1440 2183 1448 2190 1457 2198 1467 2205 1476 2210 1484 2217 1491 2224 1496 2232 1499 2237 1499 2244 1497 2251 1493 2256 1486 2263 1478 2271 1469 2278 1459 2283 1451 2290 1442 2297 1435 2304 1429 2310 1425 2317 1425 -6 muse-4.0.0/src/synti/deicsonze/pics/wave2.gnuplot000066400000000000000000000000621404105311000217630ustar00rootroot00000000000000set terminal fig plot [0:4*pi] sin(x)*abs(sin(x)) muse-4.0.0/src/synti/deicsonze/pics/wave2.png000066400000000000000000000010371404105311000210620ustar00rootroot00000000000000PNG  IHDR. ] pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07"IDAT8͔A E:B, D a- D@Z4&ͥ0EJig7\JieY~!S(9sC1^!!q/R!.c9c.1k%Ύe MbWL8GP}W5t4,nJVBz#1F)e⽯ V>N ޽Qofqu3z1k-clmۜso(yݕ8IENDB`PNG  IHDR. ] pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07")IDAT8c?Lu 60l`)SQ`K ݟIENDB`muse-4.0.0/src/synti/deicsonze/pics/wave3.fig000066400000000000000000000022171404105311000210450ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1650 1350 2325 1500 2 1 0 1 -1 -1 10 0 -1 0.000 0 0 0 0 0 100 1658 1496 1665 1478 1671 1458 1678 1442 1685 1424 1691 1408 1697 1396 1704 1382 1711 1372 1718 1362 1724 1358 1731 1352 1738 1350 1745 1352 1751 1352 1758 1358 1765 1364 1772 1374 1777 1386 1784 1398 1791 1412 1797 1428 1804 1446 1811 1462 1818 1482 1824 1496 1831 1496 1838 1496 1845 1496 1851 1496 1857 1496 1864 1496 1871 1496 1877 1496 1884 1496 1891 1496 1898 1496 1904 1496 1911 1496 1918 1496 1925 1496 1931 1496 1938 1496 1944 1496 1950 1496 1957 1496 1964 1496 1971 1496 1977 1496 1984 1496 1991 1486 1998 1468 2004 1450 2011 1432 2018 1418 2025 1402 2030 1388 2037 1378 2044 1368 2051 1360 2057 1354 2064 1352 2071 1350 2078 1352 2084 1354 2091 1362 2098 1370 2105 1380 2110 1392 2117 1406 2124 1420 2130 1438 2137 1454 2144 1472 2151 1492 2157 1496 2164 1496 2171 1496 2178 1496 2184 1496 2191 1496 2197 1496 2204 1496 2210 1496 2217 1496 2224 1496 2231 1496 2237 1496 2244 1496 2251 1496 2258 1496 2264 1496 2271 1496 2277 1496 2283 1496 2290 1496 2297 1496 2304 1496 2310 1496 2317 1496 -6 muse-4.0.0/src/synti/deicsonze/pics/wave3.gnuplot000066400000000000000000000001141404105311000217620ustar00rootroot00000000000000set terminal fig plot [0:4*pi] [-1:1] (x2*pi && x<3*pi))?sin(x):0 muse-4.0.0/src/synti/deicsonze/pics/wave3.png000066400000000000000000000010331404105311000210570ustar00rootroot00000000000000PNG  IHDR. ( pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07"IDAT8Q `+X [a+0 DA*^?327+ J)}J)zCOy'"9笵6zmDZEk73smVlyjZ[=0 4bVc9oZU <Ƙ{Oh{OD˜{Z~Ь xK_FyNyۧR*9(V)R*PFXk/+YED!5nYeYݝ{_օmEO2]#7#_IENDB`PNG  IHDR. ( pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07"(IDAT8c?Lu 60l`)SA0IENDB`muse-4.0.0/src/synti/deicsonze/pics/wave4.fig000066400000000000000000000022171404105311000210460ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1650 1350 2325 1500 2 1 0 1 -1 -1 10 0 -1 0.000 0 0 0 0 0 100 1658 1495 1665 1493 1671 1486 1678 1475 1685 1461 1691 1444 1697 1426 1704 1408 1711 1390 1718 1375 1724 1363 1731 1354 1738 1350 1745 1351 1751 1356 1758 1366 1765 1379 1772 1394 1777 1412 1784 1431 1791 1448 1797 1465 1804 1479 1811 1489 1818 1494 1824 1495 1831 1495 1838 1495 1845 1495 1851 1495 1857 1495 1864 1495 1871 1495 1877 1495 1884 1495 1891 1495 1898 1495 1904 1495 1911 1495 1918 1495 1925 1495 1931 1495 1938 1495 1944 1495 1950 1495 1957 1495 1964 1495 1971 1495 1977 1495 1984 1495 1991 1495 1998 1490 2004 1481 2011 1469 2018 1453 2025 1435 2030 1417 2037 1399 2044 1383 2051 1369 2057 1358 2064 1352 2071 1350 2078 1353 2084 1361 2091 1372 2098 1386 2105 1403 2110 1421 2117 1440 2124 1457 2130 1472 2137 1484 2144 1492 2151 1495 2157 1495 2164 1495 2171 1495 2178 1495 2184 1495 2191 1495 2197 1495 2204 1495 2210 1495 2217 1495 2224 1495 2231 1495 2237 1495 2244 1495 2251 1495 2258 1495 2264 1495 2271 1495 2277 1495 2283 1495 2290 1495 2297 1495 2304 1495 2310 1495 2317 1495 -6 muse-4.0.0/src/synti/deicsonze/pics/wave4.gnuplot000066400000000000000000000001301404105311000217610ustar00rootroot00000000000000set terminal fig plot [0:4*pi] [-1:1] (x2*pi && x<3*pi))?sin(x)*abs(sin(x)):0 muse-4.0.0/src/synti/deicsonze/pics/wave4.png000066400000000000000000000010241404105311000210600ustar00rootroot00000000000000PNG  IHDR. ( pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07"IDAT8] u,` JЀH(Z H vwgK<|s<{]&L)("y*9,pr9덒B8l末R "8h(h3Q=+R!J?䜵j ! D'Xky 3[k"bƘ_S!b=9_1+u8b*(zJT#ם@DRT& !|kiԆZk"c'xIENDB`PNG  IHDR. ( pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07"(IDAT8c?Lu 60l`)SA0IENDB`muse-4.0.0/src/synti/deicsonze/pics/wave5.fig000066400000000000000000000022171404105311000210470ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1650 1350 2325 1500 2 1 0 1 -1 -1 10 0 -1 0.000 0 0 0 0 0 100 1658 1425 1665 1406 1671 1388 1678 1373 1685 1361 1691 1353 1697 1350 1704 1352 1711 1358 1718 1368 1724 1382 1731 1399 1738 1417 1745 1436 1751 1454 1758 1470 1765 1484 1772 1493 1777 1498 1784 1498 1791 1494 1797 1485 1804 1472 1811 1456 1818 1438 1824 1425 1831 1425 1838 1425 1845 1425 1851 1425 1857 1425 1864 1425 1871 1425 1877 1425 1884 1425 1891 1425 1898 1425 1904 1425 1911 1425 1918 1425 1925 1425 1931 1425 1938 1425 1944 1425 1950 1425 1957 1425 1964 1425 1971 1425 1977 1425 1984 1425 1991 1415 1998 1397 2004 1380 2011 1367 2018 1357 2025 1351 2030 1350 2037 1354 2044 1363 2051 1375 2057 1390 2064 1408 2071 1427 2078 1446 2084 1463 2091 1477 2098 1489 2105 1496 2110 1499 2117 1497 2124 1490 2130 1479 2137 1465 2144 1448 2151 1429 2157 1425 2164 1425 2171 1425 2178 1425 2184 1425 2191 1425 2197 1425 2204 1425 2210 1425 2217 1425 2224 1425 2231 1425 2237 1425 2244 1425 2251 1425 2258 1425 2264 1425 2271 1425 2277 1425 2283 1425 2290 1425 2297 1425 2304 1425 2310 1425 2317 1425 -6 muse-4.0.0/src/synti/deicsonze/pics/wave5.gnuplot000066400000000000000000000001161404105311000217660ustar00rootroot00000000000000set terminal fig plot [0:4*pi] [-1:1] (x2*pi && x<3*pi))?sin(2*x):0 muse-4.0.0/src/synti/deicsonze/pics/wave5.png000066400000000000000000000011001404105311000210540ustar00rootroot00000000000000PNG  IHDR. ] pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07")IDAT8͔0 E5)!-S)).@ dV m;fgwo&y/\x:\1h 7@<#b{m[1!" !Xk)_Ƙ'"9 )%?CYfx"5嫮뺮۶q\ayR>iR=_a__%^y0,˒NRchЈy8I~%p{Mitc`)u&5DcZR*Pѻv6VJvFI !ҝ:(IENDB`PNG  IHDR. ] pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07")IDAT8c?Lu 60l`)SQ`K ݟIENDB`muse-4.0.0/src/synti/deicsonze/pics/wave6.fig000066400000000000000000000022171404105311000210500ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1650 1350 2325 1500 2 1 0 1 -1 -1 10 0 -1 0.000 0 0 0 0 0 100 1658 1425 1665 1420 1671 1407 1678 1389 1685 1371 1691 1357 1697 1350 1704 1353 1711 1365 1718 1382 1724 1401 1731 1416 1738 1424 1745 1426 1751 1436 1758 1453 1765 1472 1772 1488 1777 1497 1784 1498 1791 1490 1797 1474 1804 1455 1811 1438 1818 1427 1824 1425 1831 1425 1838 1425 1845 1425 1851 1425 1857 1425 1864 1425 1871 1425 1877 1425 1884 1425 1891 1425 1898 1425 1904 1425 1911 1425 1918 1425 1925 1425 1931 1425 1938 1425 1944 1425 1950 1425 1957 1425 1964 1425 1971 1425 1977 1425 1984 1425 1991 1423 1998 1414 2004 1398 2011 1380 2018 1363 2025 1352 2030 1350 2037 1358 2044 1373 2051 1391 2057 1409 2064 1421 2071 1425 2078 1430 2084 1444 2091 1462 2098 1480 2105 1494 2110 1499 2117 1495 2124 1482 2130 1464 2137 1446 2144 1432 2151 1425 2157 1425 2164 1425 2171 1425 2178 1425 2184 1425 2191 1425 2197 1425 2204 1425 2210 1425 2217 1425 2224 1425 2231 1425 2237 1425 2244 1425 2251 1425 2258 1425 2264 1425 2271 1425 2277 1425 2283 1425 2290 1425 2297 1425 2304 1425 2310 1425 2317 1425 -6 muse-4.0.0/src/synti/deicsonze/pics/wave6.gnuplot000066400000000000000000000001341404105311000217670ustar00rootroot00000000000000set terminal fig plot [0:4*pi] [-1:1] (x2*pi && x<3*pi))?sin(2*x)*abs(sin(2*x)):0 muse-4.0.0/src/synti/deicsonze/pics/wave6.png000066400000000000000000000010461404105311000210660ustar00rootroot00000000000000PNG  IHDR. ] pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07"IDAT8m DV\-.\-Pw]3?HH3ID!ceCfffkm)Z˽Oo!b+_xSJ̜RwޗR{g*DZkZ5ADU9VhH))9mێD\UEι #MissMgMz&8.%ixcZcgͪj5Z[& /5xMsLIENDB`PNG  IHDR. ] pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07")IDAT8c?Lu 60l`)SQ`K ݟIENDB`muse-4.0.0/src/synti/deicsonze/pics/wave7.fig000066400000000000000000000022171404105311000210510ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1650 1350 2325 1500 2 1 0 1 -1 -1 10 0 -1 0.000 0 0 0 0 0 100 1658 1495 1665 1459 1671 1425 1678 1395 1685 1372 1691 1357 1697 1350 1704 1353 1711 1364 1718 1385 1724 1413 1731 1446 1738 1482 1745 1472 1751 1437 1758 1405 1765 1380 1772 1361 1777 1352 1784 1351 1791 1360 1797 1377 1804 1402 1811 1433 1818 1468 1824 1495 1831 1495 1838 1495 1845 1495 1851 1495 1857 1495 1864 1495 1871 1495 1877 1495 1884 1495 1891 1495 1898 1495 1904 1495 1911 1495 1918 1495 1925 1495 1931 1495 1938 1495 1944 1495 1950 1495 1957 1495 1964 1495 1971 1495 1977 1495 1984 1495 1991 1477 1998 1441 2004 1409 2011 1382 2018 1363 2025 1353 2030 1351 2037 1359 2044 1375 2051 1399 2057 1428 2064 1463 2071 1491 2078 1454 2084 1421 2091 1392 2098 1370 2105 1355 2110 1350 2117 1354 2124 1367 2130 1388 2137 1417 2144 1450 2151 1486 2157 1495 2164 1495 2171 1495 2178 1495 2184 1495 2191 1495 2197 1495 2204 1495 2210 1495 2217 1495 2224 1495 2231 1495 2237 1495 2244 1495 2251 1495 2258 1495 2264 1495 2271 1495 2277 1495 2283 1495 2290 1495 2297 1495 2304 1495 2310 1495 2317 1495 -6 muse-4.0.0/src/synti/deicsonze/pics/wave7.gnuplot000066400000000000000000000001231404105311000217660ustar00rootroot00000000000000set terminal fig plot [0:4*pi] [-1:1] (x2*pi && x<3*pi))?abs(sin(2*x)):0 muse-4.0.0/src/synti/deicsonze/pics/wave7.png000066400000000000000000000011241404105311000210640ustar00rootroot00000000000000PNG  IHDR. ( pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07">IDAT8Q0 DD!DLPS(S0A D|d|3{OZ)c{1ǯrΏcl<ϔy&(J)LD {DU53UUMk3>Of1Z7Ar^֚RbVD8D4oj1Ƹ j8Dę+V!|`EUg*̬"2[@Dky!M+r[ma+^xJUIoY*-N۞lk۽/vt`6s֊o3/W} !,K)y"7Q?=IENDB`PNG  IHDR. ( pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07"(IDAT8c?Lu 60l`)SA0IENDB`muse-4.0.0/src/synti/deicsonze/pics/wave8.fig000066400000000000000000000022171404105311000210520ustar00rootroot00000000000000#FIG 3.2 Landscape Center Inches Letter 100.00 Single -2 1200 2 6 1650 1350 2325 1500 2 1 0 1 -1 -1 10 0 -1 0.000 0 0 0 0 0 100 1658 1495 1665 1486 1671 1461 1678 1426 1685 1390 1691 1363 1697 1350 1704 1356 1711 1379 1718 1412 1724 1448 1731 1479 1738 1494 1745 1492 1751 1472 1758 1440 1765 1403 1772 1372 1777 1353 1784 1352 1791 1369 1797 1399 1804 1435 1811 1469 1818 1490 1824 1495 1831 1495 1838 1495 1845 1495 1851 1495 1857 1495 1864 1495 1871 1495 1877 1495 1884 1495 1891 1495 1898 1495 1904 1495 1911 1495 1918 1495 1925 1495 1931 1495 1938 1495 1944 1495 1950 1495 1957 1495 1964 1495 1971 1495 1977 1495 1984 1495 1991 1493 1998 1475 2004 1444 2011 1408 2018 1375 2025 1354 2030 1351 2037 1366 2044 1394 2051 1431 2057 1465 2064 1489 2071 1495 2078 1484 2084 1457 2091 1421 2098 1386 2105 1361 2110 1350 2117 1358 2124 1383 2130 1417 2137 1453 2144 1481 2151 1495 2157 1495 2164 1495 2171 1495 2178 1495 2184 1495 2191 1495 2197 1495 2204 1495 2210 1495 2217 1495 2224 1495 2231 1495 2237 1495 2244 1495 2251 1495 2258 1495 2264 1495 2271 1495 2277 1495 2283 1495 2290 1495 2297 1495 2304 1495 2310 1495 2317 1495 -6 muse-4.0.0/src/synti/deicsonze/pics/wave8.gnuplot000066400000000000000000000001271404105311000217730ustar00rootroot00000000000000set terminal fig plot [0:4*pi] [-1:1] (x2*pi && x<3*pi))?sin(2*x)*sin(2*x):0 muse-4.0.0/src/synti/deicsonze/pics/wave8.png000066400000000000000000000011171404105311000210670ustar00rootroot00000000000000PNG  IHDR. ( pHYs M MέNtEXtSoftwareGNU Ghostscript 7.07"9IDAT8Qu0 E00Q0Q0c BDA* `C>xN}%}cZ)68Vkֺ~]W03 #include #include #include #include #include #include #include "muse_math.h" #include #include #include #include "muse/midictrl_consts.h" //#include "common_defs.h" #include "muse/midi_consts.h" #include "fluid.h" #include "fluidgui.h" //--------------------------------------------------------- // instantiate //--------------------------------------------------------- class QWidget; static Mess* instantiate(unsigned long long /*parentWinId*/, const char* name, const MessConfig* config) { ISynth* synth = new ISynth(); synth->setSampleRate(config->_sampleRate); if (synth->init(name)) { delete synth; synth = 0; } return synth; } //--------------------------------------------------------- // MESS //--------------------------------------------------------- extern "C" { static MESS descriptor = { "fluid", "fluid soundfont loader by Werner Schweer", "0.1", // fluid version string MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate, }; // We must compile with -fvisibility=hidden to avoid namespace // conflicts with global variables. // Only visible symbol is "mess_descriptor". // (TODO: all plugins should be compiled this way) __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } //--------------------------------------------------------- // ISynth //--------------------------------------------------------- ISynth::ISynth() : Mess(2) { _busy = false; sfont = 0; _gmMode = false; // General Midi Mode _fluidsynth = 0; initBuffer = 0; initLen = 0; fontId = -1; } //--------------------------------------------------------- // playNote // return true if busy //--------------------------------------------------------- bool ISynth::playNote(int channel, int pitch, int velo) { if (_busy) { // printf("fluid: playNote(): busy!\n"); return true; } if (velo) { int err = fluid_synth_noteon(_fluidsynth, channel, pitch, velo); if (err) { printf("ISynth: noteon error, channel %d pitch %d<%s>\n", channel, pitch, fluid_synth_error(_fluidsynth)); } } else fluid_synth_noteoff(_fluidsynth, channel, pitch); return false; } //--------------------------------------------------------- // setController // return true if busy //--------------------------------------------------------- bool ISynth::setController(int ch, int ctrl, int val) { if (_busy) { // printf("fluid: setController(): busy!\n"); return true; } switch(ctrl) { case MusECore::CTRL_PROGRAM: { int hbank = (val & 0xff0000) >> 16; int lbank = (val & 0xff00) >> 8; if (hbank > 127) // map "dont care" to 0 hbank = 0; if (lbank > 127) lbank = 0; if (lbank == 127 || ch == 9) // drum HACK lbank = 128; int prog = val & 0x7f; fluid_synth_program_select(_fluidsynth, ch, hbank, lbank, prog); } break; case MusECore::CTRL_PITCH: // MusE's range is from -8192 to +8191, fluidsynth seems to be [0, 16384] val +=8192; fluid_synth_pitch_bend (_fluidsynth, ch, val); break; default: // printf("controller %x val %x\n", ctrl & 0x3fff, val); fluid_synth_cc(_fluidsynth, ch, ctrl & 0x3fff, val); break; } return false; } //--------------------------------------------------------- // sysex // 7e 7f 09 01 GM on // 7e 7f 09 02 GM off // 7f 7f 04 01 ll hh Master Volume (ll-low byte, hh-high byte) // 7c 00 01 nn ... replace Soundfont (nn-ascii char of path // 7c 00 02 nn ... add Soundfont // 7c 00 03 nn ... remove Soundfont // // return true if busy //--------------------------------------------------------- bool ISynth::sysex(int len, const unsigned char* data) { if (_busy) { // printf("fluid: sysex(): busy!\n"); return true; } if (len >= 4) { //--------------------------------------------- // Universal Non Realtime //--------------------------------------------- if (data[0] == 0x7e) { if (data[1] == 0x7f) { // device Id if (data[2] == 0x9) { // GM if (data[3] == 0x1) { gmOn(true); return false; } else if (data[3] == 0x2) { gmOn(false); return false; } } } } //--------------------------------------------- // Universal Realtime //--------------------------------------------- else if (data[0] == 0x7f) { if (data[1] == 0x7f) { // device Id if ((data[2] == 0x4) && (data[3] == 0x1)) { if(len == 6) { float v = (data[5]*128 + data[4])/32767.0; fluid_synth_set_gain(_fluidsynth, v); return false; } } } } //--------------------------------------------- // MusE Soft Synth //--------------------------------------------- else if (data[0] == MUSE_SYNTH_SYSEX_MFG_ID) { int n = len - 3; if (n < 1) { #ifdef FS_DEBUG printf("fluid: bad sysEx:\n"); #endif return false; } char buffer[n+1]; memcpy(buffer, (char*)data+3, n); buffer[n] = 0; if (data[1] == FLUID_UNIQUE_ID) { // fluid //if (data[2] == 1) { // load sound font if (data[2] == SF_REPLACE) { // load sound font sysexSoundFont(SF_REPLACE, buffer); return false; } //else if (data[2] == 2) { // load sound font else if (data[2] == SF_ADD) { // load sound font sysexSoundFont(SF_ADD, buffer); return false; } //else if (data[2] == 3) { // load sound font else if (data[2] == SF_REMOVE) { // load sound font sysexSoundFont(SF_REMOVE, buffer); return false; } } } else if (data[0] == 0x41) { // roland if (data[1] == 0x10 && data[2] == 0x42 && data[3] == 0x12) { if (len == 8) { if(data[4] == 0x40 && data[5] == 00 && data[6] == 0x7f && data[7] == 0x41) { // gs on gmOn(true); return false; } } } } } #ifdef FS_DEBUG printf("fluid: unknown sysex received, len %d:\n", len); for (int i = 0; i < len; ++i) printf("%02x ", data[i]); printf("\n"); #endif return false; } //--------------------------------------------------------- // gmOn //--------------------------------------------------------- void ISynth::gmOn(bool flag) { _gmMode = flag; allNotesOff(); } //--------------------------------------------------------- // allNotesOff // stop all notes //--------------------------------------------------------- void ISynth::allNotesOff() { for (int ch = 0; ch < 16; ++ch) { fluid_synth_cc(_fluidsynth, ch, 0x7b, 0); // all notes off } } //--------------------------------------------------------- // guiVisible //--------------------------------------------------------- bool ISynth::nativeGuiVisible() const { return gui->isVisible(); } //--------------------------------------------------------- // showGui //--------------------------------------------------------- void ISynth::showNativeGui(bool flag) { gui->setVisible(flag); } //--------------------------------------------------------- // ~ISynth //--------------------------------------------------------- ISynth::~ISynth() { if(gui) delete gui; // p4.0.27 // TODO delete settings if (_fluidsynth) delete_fluid_synth(_fluidsynth); if (initBuffer) delete [] initBuffer; } //--------------------------------------------------------- // processMessages // Called from host always, even if output path is unconnected. //--------------------------------------------------------- void ISynth::processMessages() { //Process messages from the gui if (!_busy) { // // get and process all pending events from the // synthesizer GUI // while (gui->fifoSize()) processEvent(gui->readEvent()); } } //--------------------------------------------------------- // process // Called from host, ONLY if output path is connected. //--------------------------------------------------------- void ISynth::process(unsigned /*pos*/, float** ports, int offset, int n) { if (!_busy) { /* // // get and process all pending events from the // synthesizer GUI // while (gui->fifoSize()) processEvent(gui->readEvent()); */ fluid_synth_write_float(_fluidsynth, n, ports[0], offset, 1, ports[1], offset, 1); } // printf("%f %f\n", *ports[0], *(ports[0]+1)); } //--------------------------------------------------------- // processEvent // All events from the sequencer go here //--------------------------------------------------------- bool ISynth::processEvent(const MusECore::MidiPlayEvent& ev) { switch(ev.type()) { case MusECore::ME_CONTROLLER: setController(ev.channel(), ev.dataA(), ev.dataB()); //return true; // ?? break; case MusECore::ME_NOTEON: return playNote(ev.channel(), ev.dataA(), ev.dataB()); case MusECore::ME_NOTEOFF: return playNote(ev.channel(), ev.dataA(), 0); case MusECore::ME_SYSEX: return sysex(ev.len(), ev.data()); case MusECore::ME_PITCHBEND: setController(ev.channel(), MusECore::CTRL_PITCH, ev.dataA()); break; // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore - only CTRL_PROGRAM. //case MusECore::ME_PROGRAM: // setController(ev.channel(), MusECore::CTRL_PROGRAM, ev.dataA()); // break; default: break; } return false; } //--------------------------------------------------------- // getPatchName //--------------------------------------------------------- const char* ISynth::getPatchName(int /*ch*/, int val, bool /*drum*/) const { int prog = val & 0xff; if(val == MusECore::CTRL_VAL_UNKNOWN || prog == 0xff) return ""; prog &= 0x7f; int hbank = (val & 0xff0000) >> 16; int lbank = (val & 0xff00) >> 8; if (hbank > 127) hbank = 0; if (lbank > 127) lbank = 0; if (lbank == 127) // drum HACK lbank = 128; const char* name = ""; if (_busy) { //#ifdef FS_DEBUG printf("fluid: getPatchName(): busy!\n"); //#endif return name; } fluid_font = fluid_synth_get_sfont_by_id(_fluidsynth, hbank); if (fluid_font) { fluid_preset_t* preset = (*fluid_font->get_preset)(fluid_font, lbank, prog); if (preset) name = (*preset->get_name)(preset); else { //#ifdef FS_DEBUG fprintf(stderr, "no fluid preset for bank %d prog %d\n", lbank, prog); //#endif } } else fprintf(stderr, "ISynth::getPatchName(): no fluid font id=%d found\n", hbank); return name; } //--------------------------------------------------------- // getNextPatch //--------------------------------------------------------- const MidiPatch* ISynth::getPatchInfo(int ch, const MidiPatch* p) const { if (_busy) { //#ifdef FS_DEBUG printf("fluid: getPatchInfo(): busy!\n"); //#endif return 0; } if (p == 0) { // get font at font stack index 0 fluid_font = fluid_synth_get_sfont(_fluidsynth, 0); if (fluid_font == 0) return 0; (*fluid_font->iteration_start)(fluid_font); } fluid_preset_t preset; while ((*fluid_font->iteration_next)(fluid_font, &preset)) { patch.hbank = fluid_sfont_get_id(fluid_font); int bank = (*preset.get_banknum)(&preset); if (ch == 9 && bank != 128) // show only drums for channel 10 continue; if (bank == 128) bank = 127; patch.typ = 0; patch.name = (*preset.get_name)(&preset); patch.lbank = bank; patch.prog = (*preset.get_num)(&preset); return &patch; } return 0; } //--------------------------------------------------------- // getInitData // construct an initialization string which can be used // as a sysex to restore current state //--------------------------------------------------------- void ISynth::getInitData(int* len, const unsigned char** data) { if (sfont == 0) { *len = 0; return; } int n = 4 + strlen(sfont); if (n > initLen) { if (initBuffer) delete [] initBuffer; initBuffer = new unsigned char[n]; initLen = n; // p4.0.27 Tim. } initBuffer[0] = MUSE_SYNTH_SYSEX_MFG_ID; initBuffer[1] = FLUID_UNIQUE_ID; initBuffer[2] = SF_REPLACE; strcpy((char*)(initBuffer+3), sfont); *len = n; *data = (unsigned char*)initBuffer; } //--------------------------------------------------------- // sysexSoftfont //--------------------------------------------------------- void ISynth::sysexSoundFont(SfOp op, const char* data) { char c = 'x'; allNotesOff(); switch(op) { case SF_REMOVE: break; case SF_REPLACE: case SF_ADD: if (sfont && (strcmp(sfont, data) == 0)) { #ifdef FS_DEBUG fprintf(stderr, "fluid: font already loaded\n"); #endif break; } if (_busy) { //#ifdef FS_DEBUG fprintf(stderr, "fluid: busy!\n"); //#endif break; } _busy = true; if (sfont) delete[] sfont; sfont = new char[strlen(data)+1]; strcpy(sfont, data); _busy = true; write(writeFd, &c, 1); break; } } //--------------------------------------------------------- // fontLoad // helper thread to load soundfont in the // background //--------------------------------------------------------- static void* helper(void* t) { ISynth* is = (ISynth*) t; is->noRTHelper(); pthread_exit(0); } //------------------------------------ // noRTHelper //--------------------------------------------------------- void ISynth::noRTHelper() { for (;;) { char c; int n = read(readFd, &c, 1); if (n != 1) { perror("ISynth::read ipc failed\n"); continue; } int id = getFontId(); if (id != -1) { #ifdef FS_DEBUG fprintf(stderr, "ISynth: unload old font\n"); #endif fluid_synth_sfunload(synth(), (unsigned)id, true); } const char* fontname = getFont(); int rv = fluid_synth_sfload(synth(), fontname, true); if (rv == -1) { fprintf(stderr, "ISynth: sfload %s failed\n", fluid_synth_error(synth())); } else { setFontId(rv); // Inform the gui. p4.0.27 Tim int slen = strlen(fontname); int n = slen + 2; unsigned char d[n]; d[0] = FS_SEND_SOUNDFONT_NAME; if(slen != 0) memcpy(d + 1, fontname, slen); d[1 + slen] = 0; MusECore::MidiPlayEvent ev(0,0, MusECore::ME_SYSEX, d, n); gui->writeEvent(ev); #ifdef FS_DEBUG fprintf(stderr, "ISynth: sfont %s loaded as %d\n ", getFont(), rv); #endif } fluid_synth_set_gain(synth(), 1.0); //? _busy = false; } } //--------------------------------------------------------- // init // return true on error //--------------------------------------------------------- bool ISynth::init(const char* name) { fluid_settings_t* settings; settings = new_fluid_settings(); fluid_settings_setnum(settings, (char*) "synth.sample-rate", float(sampleRate())); _fluidsynth = new_fluid_synth(settings); //--------------------------------------- // create non realtime helper thread // create message channels // int filedes[2]; // 0 - reading 1 - writing if (pipe(filedes) == -1) { perror("ISynth::thread:creating pipe"); return true; } readFd = filedes[0]; writeFd = filedes[1]; pthread_attr_t* attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t)); pthread_attr_init(attributes); if (pthread_create(&helperThread, attributes, ::helper, this)) perror("creating thread failed:"); pthread_attr_destroy(attributes); char* p = getenv("DEFAULT_SOUNDFONT"); if (p) { sfont = new char[strlen(p)+1]; strcpy(sfont, p); char c = 'x'; _busy = true; write(writeFd, &c, 1); } gui = new FLUIDGui; gui->setWindowTitle(QString(name)); gui->show(); return false; } muse-4.0.0/src/synti/fluid/fluid.h000066400000000000000000000067261404105311000170000ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: fluid.h,v 1.7.2.4 2009/11/19 04:20:33 terminator356 Exp $ // // This file is derived from fluid Synth and modified // for MusE. // Parts of fluid are derived from Smurf Sound Font Editor. // Parts of Smurf Sound Font Editor are derived from // awesfx utilities // Smurf: Copyright (C) 1999-2000 Josh Green // fluid: Copyright (C) 2001 Peter Hanappe // MusE: Copyright (C) 2001 Werner Schweer // awesfx: Copyright (C) 1996-1999 Takashi Iwai // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef _SYNTH_H #define _SYNTH_H #include #include #include "libsynti/mess.h" #include "common_defs.h" //enum SfOp { SF_REPLACE = 1, SF_ADD, SF_REMOVE }; class FLUIDGui; //--------------------------------------------------------- // ISynth //--------------------------------------------------------- class ISynth : public Mess { bool _busy; bool _gmMode; unsigned char* initBuffer; int initLen; fluid_synth_t* _fluidsynth; char* sfont; mutable fluid_sfont_t* fluid_font; int fontId; int readFd, writeFd; mutable MidiPatch patch; pthread_t helperThread; FLUIDGui* gui; void gmOn(bool); void sysexSoundFont(SfOp op, const char* data); void allNotesOff(); void resetAllController(int); virtual void processMessages(); virtual void process(unsigned pos, float**, int, int); virtual bool playNote(int channel, int pitch, int velo); virtual bool setController(int, int, int); virtual bool sysex(int len, const unsigned char* p); virtual bool processEvent(const MusECore::MidiPlayEvent&); virtual const char* getPatchName (int, int, bool) const; virtual const MidiPatch* getPatchInfo(int, const MidiPatch *) const; virtual void getInitData(int*, const unsigned char**); virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool); virtual bool hasNativeGui() const { return true; } public: ISynth(); virtual ~ISynth(); // This is only a kludge required to support old songs' midistates. Do not use in any new synth. // Note for Fluid, do nothing because unlike other synths, Fluid already had correct sysex headers. //virtual int oldMidiStateHeader(const unsigned char** data) const; fluid_synth_t* synth() { return _fluidsynth; } const fluid_synth_t* synth() const { return _fluidsynth; } char* getFont() const { return sfont; } void setFontId(int id) { fontId = id; } int getFontId() const { return fontId; } bool init(const char* name); void noRTHelper(); }; #endif /* _SYNTH_H */ muse-4.0.0/src/synti/fluid/fluidgui.cpp000066400000000000000000000110541404105311000200260ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: fluidgui.cpp,v 1.6.2.1 2009/08/12 20:47:01 spamatica Exp $ // // This is a simple GUI implemented with QT for // fluid software synthesizer. // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include "common_defs.h" #include "fluidgui.h" #include "muse/midi_consts.h" #include "muse/mpevent.h" #include "muse/icons.h" //--------------------------------------------------------- // FLUIDGui //--------------------------------------------------------- FLUIDGui::FLUIDGui() : QDialog(0, Qt::Window), MessGui() { setupUi(this); //Connect socketnotifier to fifo QSocketNotifier* s = new QSocketNotifier(readFd, QSocketNotifier::Read); connect(s, SIGNAL(activated(int)), SLOT(readMessage(int))); fdialogButton->setIcon(QIcon(*MusEGui::openIcon)); connect(fdialogButton, SIGNAL(clicked()), SLOT(soundFontFileDialog())); connect(loadButton, SIGNAL(clicked()), SLOT(loadFont())); } //--------------------------------------------------------- // loadFont // sysex f0 lenH lenM lenL 7c 00 01 name //--------------------------------------------------------- void FLUIDGui::loadFont() { if (pathEntry->text().isEmpty()) return; QFileInfo fi(pathEntry->text()); if (!fi.exists()) { QString s = QString("SoundFont ") + pathEntry->text() + QString(" does not exists"); QMessageBox::critical(this, tr("FLUID: open Soundfile"), s); return; } QByteArray ba = pathEntry->text().toLatin1(); const char* path = ba.constData(); int len = ba.length() + 1 + 3; unsigned char buffer[len]; int k = 0; buffer[k++] = MUSE_SYNTH_SYSEX_MFG_ID; buffer[k++] = FLUID_UNIQUE_ID; // fluid buffer[k++] = SF_REPLACE; // load sound font strcpy((char*)(&buffer[k]), path); sendSysex(buffer, len); } //--------------------------------------------------------- // soundFontFileDialog //--------------------------------------------------------- void FLUIDGui::soundFontFileDialog() { //QString s = QFileDialog::getOpenFileName(this, QString(), QString(), QString("Soundfonts (*.[Ss][Ff]2);;All files (*)")); QString s = QFileDialog::getOpenFileName(this, QString(), QString(), QString("Soundfonts (*.sf2);;All files (*)")); if (!s.isEmpty()) { pathEntry->setText(s); } } void FLUIDGui::processEvent(const MusECore::MidiPlayEvent& ev) { // p4.0.27 if (ev.type() == MusECore::ME_SYSEX) { const unsigned char* data = ev.data(); switch (*data) { case FS_SEND_SOUNDFONT_NAME: { //const char* filename = data+1; //pathEntry->setText(QString(filename)); pathEntry->setText((const char*)(data+1)); break; } default: #ifdef FS_DEBUG printf("FLUIDGui::processEvent() : Unknown Sysex received: %d\n", ev.type()); #endif break; } } else { #ifdef FS_DEBUG printf("FLUIDGui::processEvent - unknown event of type %dreceived from synth.\n", ev.type()); #endif } } //--------------------------------------------------------- // readMessage //--------------------------------------------------------- void FLUIDGui::readMessage(int) { MessGui::readMessage(); // p4.0.27 } muse-4.0.0/src/synti/fluid/fluidgui.h000066400000000000000000000031021404105311000174660ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: fluidgui.h,v 1.2 2004/02/12 17:32:29 wschweer Exp $ // // (C) Copyright 2001 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __GUI_H__ #define __GUI_H #include "ui_fluidguibase.h" #include "libsynti/gui.h" #define FS_SEND_SOUNDFONT_NAME 1 //#define FS_DEBUG class QDialog; //--------------------------------------------------------- // FLUIDGui //--------------------------------------------------------- class FLUIDGui : public QDialog, public Ui::FLUIDGuiBase, public MessGui { Q_OBJECT private slots: void readMessage(int); void soundFontFileDialog(); void loadFont(); public: FLUIDGui(); virtual void processEvent(const MusECore::MidiPlayEvent&); }; #endif muse-4.0.0/src/synti/fluid/fluidguibase.ui000066400000000000000000000051401404105311000205130ustar00rootroot00000000000000 FLUIDGuiBase 0 0 248 94 Form1 6 11 6 0 Soundfont false Qt::Vertical QSizePolicy::Expanding 20 20 6 0 0 0 Load Qt::Horizontal QSizePolicy::Expanding 20 20 muse-4.0.0/src/synti/fluidsynth/000077500000000000000000000000001404105311000165775ustar00rootroot00000000000000muse-4.0.0/src/synti/fluidsynth/CMakeLists.txt000066400000000000000000000051211404105311000213360ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( fluidsynth_mocs fluidsynthgui.h fluidsynti.h ) ## ## UI files ## file (GLOB fluidsynth_ui_files fluidsynthguibase.ui ) QT5_WRAP_UI ( fluidsynth_uis ${fluidsynth_ui_files} ) ## ## Resource files ## QT5_ADD_RESOURCES (fluidsynth_qrc_files fluidsynth.qrc ) ## ## List of source files to compile ## file (GLOB fluidsynth_source_files fluidsynti.cpp fluidsynthgui.cpp ) include_directories(${INSTPATCH_INCLUDE_DIRS}) include_directories(${FLUIDSYN_INCLUDE_DIRS}) ## ## Define target ## add_library ( fluid_synth SHARED ${fluidsynth_source_files} ${fluidsynth_mocs} ${fluidsynth_uis} ${fluidsynth_qrc_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${fluidsynth_source_files} ${fluidsynth_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## # - tell cmake to name target fluidsynth.so instead of # libfluidsynth.so # - use precompiled header files # set_target_properties ( fluid_synth PROPERTIES PREFIX "" # Avoid treating warnings as errors here, unlike the rest of the app. COMPILE_FLAGS "-fvisibility=hidden -Wno-error" ) ## ## Linkage ## target_link_libraries(fluid_synth synti ${QT_LIBRARIES} ${FLUIDSYN_LIBRARIES} ${INSTPATCH_LIBRARIES} mpevent_module ) ## ## Install location ## install( TARGETS fluid_synth DESTINATION ${MusE_SYNTHI_DIR} ) install( FILES README.txt TODO DESTINATION ${MusE_DOC_DIR}/fluidsynth/ ) muse-4.0.0/src/synti/fluidsynth/README.txt000066400000000000000000000035251404105311000203020ustar00rootroot00000000000000README.txt ---------- Graphical frontend and built-in softsynth (MusE Experimental Soft Synth) for MusE, based on Fluidsynth (http://www.fluidsynth.org). Features: --------- - Loading/unloading of soundfonts - Easy control of fluidsynth's send effects and their parameters - Mapping of soundfonts to fluidsynth channels - Stores all settings in the current project file and automatically loads all effect parameters, soundfonts, channel settings and presets when re-opening the project. - Makes it possible to use several soundfonts in one single fluidsynth instance (thereby reducing CPU usage since they share the same send effects) Changelog/History ----------------- 040524 - Err... Fount out that this changelog is neglected. See ../../Changelog.txt instead. 031019 - Bugfixes and changes in storing/retrieving init parameters (Mathias Lundgren) 031009 - Unloading of soundfonts works (Mathias Lundgren) - Last dir stored in project-file (Mathias Lundgren) - Ordinary controller-events enabled (Mathias Lundgren) 031008 - Mapping of soundfonts to fluidchannels and selection of patches implemented. (Mathias Lundgren) - Permanent storage of channels & patches. Extended GUI. (Mathias Lundgren) 031002 - Various communication problems fixed between GUI and client (Mathias Lundgren) - Storage of synth parameters and soundfonts enabled (Mathias Lundgren/Robert Jonsson) 0309xx - Problem with loading of soundfonts resulting in Jack timeout fixed by moving loading of soundfonts to separate thread. (Robert Jonsson) Original code written by Robert Ham (no information about the history of his work) Known problems/TODO: -------------------------------------------------------------- * Turning on the chorus and/or modifying chorus parameters locks the client. * Illegal chorus parameters can be sent to fluidsynth. * Drum patches (lbank=128) not implemented yet muse-4.0.0/src/synti/fluidsynth/TODO000066400000000000000000000004311404105311000172650ustar00rootroot00000000000000 TODO o preset loading/saving o configuration loading/saving o soundfont information display o remembering the last directory that was dealt with o change gui<->synth communication to nrpns DONE o get all controllers working o soundfont stack operations o patch name retrieval muse-4.0.0/src/synti/fluidsynth/common_defs.h000066400000000000000000000023731404105311000212460ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./synti/fluidsynth/common_defs.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __FLUIDSYNTH_UNIQUE_ID_H #define __FLUIDSYNTH_UNIQUE_ID_H // Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. // 127 is reserved for special MusE system messages. #define FLUIDSYNTH_UNIQUE_ID 3 #endif muse-4.0.0/src/synti/fluidsynth/drop_down_triangle.svg000066400000000000000000000024731404105311000232060ustar00rootroot00000000000000 muse-4.0.0/src/synti/fluidsynth/fluidsynth.qrc000066400000000000000000000004111404105311000214730ustar00rootroot00000000000000 fluidsynth0.png fluidsynth1.png saw.xpm sine.xpm drop_down_triangle.svg muse-4.0.0/src/synti/fluidsynth/fluidsynth0.png000066400000000000000000000022531404105311000215600ustar00rootroot00000000000000PNG  IHDR szzrIDATXWnE={z^ƙ?#ٰe/ % (blH gDAX1#vgf׀%ͣνSu"4"_737;{az=XS\i?Q ;YodWǛy;P`bq ZۣvaV 6! \ƃN!~}ŭ_G5ťXH``5%1PMel &((Yoڗ%DUl/dCf )eYe|BnVR#*#AZ-7\V\*TŒOjm2;:W3Tˮ$pivl-[N .>Bf6s>*GԀ +[ҸK\̞Y(PK]#ϓ6)gf@I  3F6 |>`8<1JPorB48 F=F_tP %.J ݗF *Li9yu_~Ϟ㙝=U3 ZLhL-p'`(b8è̂jlAӪiPSyJ@`OrukC6oJ4f,!%ͩ\^L>pI3綴\=L΍>6`ɡRHiM4|7W0c 2N=8I1u`w3>9#9f if-RVR{%Xt%K q~e/{t)s}9Oxo$H޹w<]q~rL"~.v@a%i5p\_7_F@ƙ:(@"$(4i¯>ddj3c[ qRD9BFOp!p<|̒~„`^g~Fv n"S,PX"PAY%y^iNvuQJFW9!|x/%? _vDcbL+Wy'DˋRvzDJgQHmqx_nI~tAZX0$jh,q;f?sdK#]zN۲b-& +gp9>.Ki| /mu}E"BԖY ZԾݞ(Z8Y-S;wDDŽ@'pvk֊1aBe0zʕTd|۩6l c\Mt}1?E纏 KibLi PZ@Z 5Y:wnY9*ƣr2<?Wՠ4yӿ, R;"v':%wŸZnˇ_ t2 i9!C[u3x#b<"1 [ ;4˓NĄ3xo[YO>S-CSGq<%#mt6Ike*s cf+S'GsD _߮G$3NHqt`[/W^*NJ;ԗf(>ĥO?OV4~v]byK?A}q^.}OZ†i\ k9IXg{xSskX+odxj'~}͠i\U E,&4Vh,g/WH~d#Nf]|]<^ _V"8UXt?;7 S=.UmeoGէ/ T-៾{&PN`AIil;*EU!IĚ49خCINج7k "6D-Vx"A~La8팎ׯkWIIBĢvRY51DlZhRbzBVqDܪ+D$B 7D@kaFk[T?M⨁i룴7*Ҫ-nŚ Ma)yau$۫[q}02Cu~/QGgSl␰^'gjB0D J$eo4Ge+2v (N 6 ;mΣSjEzBGǭչ\~^Rz+|.[z 1>$QBhxaSNLn#P6@`̭$N+9ڂ4  X0YYD-L(Zefky0YrASNҘ f[MhVfgP#Z ay?vIĦ4G-@qܣXpA [LXx0^R$l6IzZ8116NG.J;X`(s8O@u~? c#d*h'hՖȩȴZ2e?KA{k29b "bP7NAن-X=:qkCgKr/^!,/mGy% e8~lij4ˇh,ΩybngXDljk8e{JRī޲~v0=S G::c~.T(3 yS RY>8FkѬ׉ m M: aTLM+3x(ng%q~v9g/xx]Խa yyHC埜7,g^"պϚ}4 }ngRf!FzyO'}f.ṯ>HR^{!r9c\!Sܘ"oH=AWs1AMK<)lN "^48 q; 0QHh0NЉB$6NcZ a}Sө^.M( ad |ب7>w:7] s9<_?-o~'PQ{fJ!=kJgqV,ĔiNjEv=zۑISˀ@<owOk>z$>`hMәA­ض%ҋb ՚adxL&K+WyP&yՋ Ţ?qDbq1^[~S[؄yX hLFq9~-T* C|3f8x׉c$+Nwz/t5%JIdGxbJyxDn *@?׮㶻z=vt8>W{Xt>OTՃYoA4,Nd4;@&я!m.(Ur׽qHԤ%;N~;on? b\3B-[ Ǜ X"^+H2kT]| a\بG>vE2pʇX$>&$J^ۣ|xX2('hƀ$\&t O߂~IT&Ö U198^qr7O:wm-[(SNcěBl߸W@> 'w6 hCԚ|yZ/6_? tL,"қvn=gCiI$Şn\N>xgc$D\C"ϡtPU@yX9W V,b#i"$"JpQN4u==j4Q[>w p1oAsxE b#!>vo%S8K]8VYuCw;{/Ęx8<.;ώx&oEHmӇ>:{]5X&4Al$^;w|7 C4|q~a<Wk\{qǔ93'|ci#}ѣ1Gm\s7H;ϟB994R_۫]|?N@TD=|#HwOv=m?Gyk͇_;z؛'/۸x4Jg۱t~CbmO!0XS' .eo͊lE%5qc\5hLl9:?cΉlA9%ļm$ yzeoǛj'ky8tꑬDP7qܡđ5WWxW^F 㸣ZFlm\#v=*/x͊d{jv"bEp_#‹hw$5c\~FIkc~kevx(mOպ;;lM[*aq _7,7ҝLU=l:]1UL2ǵ< */x7؁vFۤsnk1DU玼8$Zu~l44I~?^p\W";vFH\K0 IfN[ W3RB}]&>rhWES@ ]Wy'*1 !V _l|'?(gS`iD4Blo~闿|"없;x{B&@~m#Y$_WOD(]H7|tƤ65-Ϳ>'D/엎v&Q:z#Qk!&&fלشuyL/D3#6k' 2k?w7*6a!hfDw7T/B{IENDB`muse-4.0.0/src/synti/fluidsynth/fluidsynthgui.cpp000066400000000000000000001013411404105311000222010ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./synti/fluidsynth/fluidsynthgui.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= /* * MusE FLUID Synth softsynth plugin * * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) * * $Id: fluidsynthgui.cpp,v 1.13.2.2 2009/08/12 20:47:01 spamatica Exp $ * */ #include "fluidsynthgui.h" #include "fluidsynti.h" #include #include #include #include #include #include #include #include "muse_math.h" #include "muse/midi_consts.h" #include "common_defs.h" /* #include "muse/debug.h" #include #include #include #include #define MUSE_FLUID_DEBUG false */ FluidSynthGui::FluidSynthGui() : MessGui() { setWindowIcon(QIcon(":/fluidsynth0.png")); setupUi(this); channelListView->setRowCount(FS_MAX_NR_OF_CHANNELS); channelListView->setSelectionMode(QAbstractItemView::SingleSelection); QLabel *fluidLabel = new QLabel; fluidLabel->setPixmap(QIcon(":/fluidsynth1.png").pixmap(124, 45)); FluidGrid->addWidget(fluidLabel, 2, 1, Qt::AlignHCenter); ChorusType->setItemIcon(0, QIcon(":/sine.xpm")); ChorusType->setItemIcon(1, QIcon(":/saw.xpm")); connect(this->getGuiSignal(),SIGNAL(wakeup()),this,SLOT(readMessage())); connect (Push, SIGNAL (clicked()), SLOT(loadClicked())); lastdir = ""; ReverbFrame->setEnabled(true); ChorusFrame->setEnabled(true); if (!FS_DEBUG) dumpInfoButton->hide(); //Init reverb sliders: /*ReverbRoomSize->setValue((int)(16383*FS_PREDEF_REVERB_ROOMSIZE)); ReverbDamping->setValue((int)(16383*FS_PREDEF_REVERB_DAMPING)); ReverbWidth->setValue((int)(16383*FS_PREDEF_REVERB_WIDTH));*/ connect(Gain, SIGNAL(valueChanged(int)), SLOT(changeGain(int))); connect(dumpInfoButton , SIGNAL(clicked()), SLOT(dumpInfo())); connect(channelListView, SIGNAL(itemClicked(QTableWidgetItem*)), this, SLOT(channelItemClicked(QTableWidgetItem*))); connect(Reverb, SIGNAL (toggled(bool)), SLOT(toggleReverb(bool))); connect(ReverbLevel, SIGNAL (valueChanged (int)), SLOT(changeReverbLevel(int))); connect(ReverbRoomSize, SIGNAL (valueChanged (int)), SLOT(changeReverbRoomSize(int))); connect(ReverbDamping, SIGNAL (valueChanged (int)), SLOT(changeReverbDamping(int))); connect(ReverbWidth, SIGNAL (valueChanged (int)), SLOT(changeReverbWidth(int))); connect (Pop, SIGNAL (clicked()), SLOT(popClicked())); connect(sfListView, SIGNAL(itemClicked(QTreeWidgetItem*, int)), this, SLOT(sfItemClicked(QTreeWidgetItem*, int))); connect(Chorus, SIGNAL (toggled (bool)), SLOT(toggleChorus (bool))); connect(ChorusNumber, SIGNAL (valueChanged (int)), SLOT(changeChorusNumber (int))); connect(ChorusType, SIGNAL (activated (int)), SLOT(changeChorusType (int))); connect(ChorusSpeed, SIGNAL (valueChanged (int)), SLOT(changeChorusSpeed (int))); connect(ChorusDepth, SIGNAL (valueChanged (int)), SLOT(changeChorusDepth (int))); connect(ChorusLevel, SIGNAL (valueChanged (int)), SLOT(changeChorusLevel (int))); //Clear channels for (int i=0; itext(); if (fns.isEmpty()) return; const char * fn = fns.toLatin1(); int datalen = strlen(fn) + 3; unsigned char data [datalen]; data[0] = MUSE_FLUID_SOUNDFONT_PUSH; data[1] = MUSE_FLUID_UNSPECIFIED_ID; //This makes the client choose next available external id memcpy(data + 2, fn, strlen(fn) + 1 ); //Store filename sendSysex(data, datalen); data[0] = MUSE_FLUID_GUI_REQ_SOUNDFONTS; //For simplicity's sake, just get all the soundfont data again. sendSysex(data, 1); printf("Gui sent Sysex.\n"); return; } */ void FluidSynthGui::loadClicked() { QString filename = QFileDialog::getOpenFileName(this, tr("Choose soundfont"), lastdir, //QString("Soundfonts (*.[Ss][Ff]2);;All files (*)")); QString("Soundfonts (*.sf2 *.sf3);;All files (*)")); if (!filename.isEmpty()) { int lastslash = filename.lastIndexOf('/'); lastdir = filename.left(lastslash); sendLastdir(lastdir); sendLoadFont(filename); } } //--------------------------------------------------------- // sendLastdir // Send the last dir-value to the client //--------------------------------------------------------- void FluidSynthGui::sendLastdir(QString dir) { //int l = dir.length()+2; int l = dir.length()+4; byte data[l]; data[0] = MUSE_SYNTH_SYSEX_MFG_ID; data[1] = FLUIDSYNTH_UNIQUE_ID; data[2] = FS_LASTDIR_CHANGE; //memcpy(data+1, dir.toLatin1(), dir.length()+1); memcpy(data+3, dir.toLatin1().constData(), dir.length()+1); sendSysex(data,l); } //--------------------------------------------------------- // sendUpdateDrumMaps // Tell the client to update relevant drum maps //--------------------------------------------------------- void FluidSynthGui::sendUpdateDrumMaps() { int l = 3; byte data[l]; data[0] = MUSE_SYNTH_SYSEX_MFG_ID; data[1] = MUSE_SYSEX_SYSTEM_ID; data[2] = MUSE_SYSEX_SYSTEM_UPDATE_DRUM_MAPS_ID; sendSysex(data,l); } //--------------------------------------------------------- // sendLoadFont // Tell the client to load a font with first available id //--------------------------------------------------------- void FluidSynthGui::sendLoadFont(QString filename) { //int l = filename.length()+3; int l = filename.length()+5; byte data[l]; data[0] = MUSE_SYNTH_SYSEX_MFG_ID; data[1] = FLUIDSYNTH_UNIQUE_ID; data[2] = FS_PUSH_FONT; data[3] = FS_UNSPECIFIED_ID; //memcpy(data+2, filename.toLatin1(), filename.length()+1); memcpy(data+4, filename.toLatin1().constData(), filename.length()+1); sendSysex(data,l); } //--------------------------------------------------------- // sendLoadFont // Tell the client to pop a font //--------------------------------------------------------- void FluidSynthGui::sendPopFont(int id) { byte data[4]; data[0] = MUSE_SYNTH_SYSEX_MFG_ID; data[1] = FLUIDSYNTH_UNIQUE_ID; data[2] = FS_SOUNDFONT_POP; data[3] = id; sendSysex(data,4); } //--------------------------------------------------------- // processEvent //--------------------------------------------------------- void FluidSynthGui::processEvent(const MusECore::MidiPlayEvent& ev) { //Sysexes sent from the client if (ev.type() == MusECore::ME_SYSEX) { const byte* data = ev.constData(); switch (*data) { case FS_LASTDIR_CHANGE: lastdir = QString((const char*)data+1); break; case FS_ERROR: { char* msg = (char*) (data+1); printf("Muse: fluidsynth error: %s\n", msg); break; } case FS_SEND_SOUNDFONTDATA: { int chunk_len; int filename_len; int count = (int)*(data+1); //Number of elements const byte* cp = data+2; //Point to beginning of first chunk sfListView->clear(); //Clear the listview stack.clear(); //Clear the stack since we're starting over again while (count) { FluidGuiSoundFont font; filename_len = strlen((const char*)cp) + 1; font.name = (const char*)cp; font.id = *(cp + filename_len); chunk_len = filename_len + FS_SFDATALEN; stack.push_front(font); cp += chunk_len; //Move to next chunk count--; } updateSoundfontListView(); updateChannelListView(); break; } case FS_SEND_CHANNELINFO: { const byte* chptr = (data+1); for (int i=0; i< FS_MAX_NR_OF_CHANNELS; i++) { byte id = *chptr; byte channel = *(chptr+1); channels[channel] = id; chptr+=2; } updateChannelListView(); break; } case FS_SEND_DRUMCHANNELINFO: { const byte* drumchptr = (data+1); for (int i=0; isignalsBlocked(); Gain->blockSignals(true); // Update Gain-slider without causing it to respond to it's own signal (and send another msg to the synth) Gain->setValue(val); Gain->blockSignals(sb); break; } case FS_REVERB_ON: { bool sb = Reverb->signalsBlocked(); Reverb->blockSignals(true); Reverb->setChecked(val); Reverb->blockSignals(sb); break; } case FS_REVERB_LEVEL: { bool sb = ReverbLevel->signalsBlocked(); ReverbLevel->blockSignals(true); ReverbLevel->setValue(val); ReverbLevel->blockSignals(sb); break; } case FS_REVERB_DAMPING: { bool sb = ReverbDamping->signalsBlocked(); ReverbDamping->blockSignals(true); ReverbDamping->setValue(val); ReverbDamping->blockSignals(sb); break; } case FS_REVERB_ROOMSIZE: { bool sb = ReverbRoomSize->signalsBlocked(); ReverbRoomSize->blockSignals(true); ReverbRoomSize->setValue(val); ReverbRoomSize->blockSignals(sb); break; } case FS_REVERB_WIDTH: { bool sb = ReverbWidth->signalsBlocked(); ReverbWidth->blockSignals(true); ReverbWidth->setValue(val); ReverbWidth->blockSignals(sb); break; } case FS_CHORUS_ON: { Chorus->blockSignals(true); Chorus->setChecked(val); Chorus->blockSignals(false); break; } case FS_CHORUS_SPEED: { ChorusSpeed->blockSignals(true); ChorusSpeed->setValue(val); ChorusSpeed->blockSignals(false); break; } case FS_CHORUS_NUM: { ChorusNumber->blockSignals(true); ChorusNumber->setValue(val); ChorusNumber->blockSignals(false); break; } case FS_CHORUS_TYPE: { ChorusType->blockSignals(true); ChorusType->setCurrentIndex(val); ChorusType->blockSignals(false); break; } case FS_CHORUS_DEPTH: { ChorusDepth->blockSignals(true); ChorusDepth->setValue(val); ChorusDepth->blockSignals(false); break; } case FS_CHORUS_LEVEL: { ChorusLevel->blockSignals(true); ChorusLevel->setValue(val); ChorusLevel->blockSignals(false); break; } default: if (FS_DEBUG) printf("FluidSynthGui::processEvent() : Unknown controller sent to gui: %x\n",id); break; } } else if (FS_DEBUG) printf("FluidSynthGui::processEvent - unknown event of type %dreceived from synth.\n", ev.type()); } //--------------------------------------------------------- // readMessage //--------------------------------------------------------- void FluidSynthGui::readMessage() { MessGui::readMessage(); } //--------------------------------------------------------- // updateChannels //--------------------------------------------------------- void FluidSynthGui::updateChannelListView() { if (FS_DEBUG) printf("FluidSynthGui::updateChannelListView\n"); channelListView->clearContents(); for (int i=0; isetItem(i, FS_CHANNEL_COL, chan_); QTableWidgetItem* sfid_ = new QTableWidgetItem(bd_icon, sfidstr); sfid_->setToolTip("Ctrl to fill all channels\nShift to fill empty channels"); channelListView->setItem(i, FS_SF_ID_COL, sfid_); QTableWidgetItem* drum_ = new QTableWidgetItem(bd_icon, drumchanstr); channelListView->setItem(i, FS_DRUM_CHANNEL_COL, drum_); } channelListView->resizeColumnsToContents(); } //--------------------------------------------------------- // updateSoundfontListView //--------------------------------------------------------- void FluidSynthGui::updateSoundfontListView() { sfListView->clear(); //Clear the listview for (std::list::iterator it = stack.begin(); it != stack.end(); it++) { QTreeWidgetItem* qlvNewItem = new QTreeWidgetItem(sfListView); QString qsid = QString("%1").arg(it->id); qlvNewItem->setText(FS_ID_COL, qsid); qlvNewItem->setText(FS_SFNAME_COL, QString(it->name)); sfListView->addTopLevelItem(qlvNewItem); } sfListView->sortItems(1, Qt::AscendingOrder); } //--------------------------------------------------------- // changeGain //--------------------------------------------------------- void FluidSynthGui::changeGain(int value) { sendController(0, FS_GAIN, value); } //--------------------------------------------------------- // dumpInfoButton //--------------------------------------------------------- void FluidSynthGui::dumpInfo() { //byte data[1]; byte data[3]; data[0] = MUSE_SYNTH_SYSEX_MFG_ID; data[1] = FLUIDSYNTH_UNIQUE_ID; data[2] = FS_DUMP_INFO; //sendSysex(data, 1); sendSysex(data, 3); } //--------------------------------------------------------- // getSoundFontName //--------------------------------------------------------- QString FluidSynthGui::getSoundFontName(int id) { QString name = NULL; for (std::list::iterator it = stack.begin(); it != stack.end(); it++) { if (id == it->id) { name = it->name; continue; } } return name; } //--------------------------------------------------------- // channelItemClicked // change channel parameters like soundfont / drumchannel on/off //--------------------------------------------------------- void FluidSynthGui::channelItemClicked(QTableWidgetItem* item) { int col = item->column(); int row = item->row(); if (col == FS_SF_ID_COL) { QMenu* popup = new QMenu(this); QPoint ppt = channelListView->visualItemRect(item).bottomLeft(); QTableWidget* listView = item->tableWidget(); ppt += QPoint(40, listView->horizontalHeader()->height()); ppt = listView->mapToGlobal(ppt); int i = 0; int lastindex = 0; for (std::list::reverse_iterator it = stack.rbegin(); it != stack.rend(); it++) { i++; /*byte* d = (byte*) it->name.toLatin1(); for (int i=0; i<96; i++) { if (i%16 == 0) printf("%x:",(i+d)); printf("%x ",*(d-48+i)); if (i%16 == 15) printf("\n"); } for (int i=0; i<96; i++) { if (i%16 == 0) printf("%x:",(i+d-48)); printf("%c ",*(d-48+i)); if (i%16 == 15) printf("\n"); } printf("\n\n");*/ QAction* act1 = popup->addAction(it->name); act1->setData((int)it->id); lastindex = std::max(lastindex, (int)it->id + 1); } QAction *lastaction = popup->addAction("unspecified"); lastaction->setData(lastindex); QAction * act = popup->exec(ppt, nullptr); if (act) { Qt::KeyboardModifiers keymod = qApp->keyboardModifiers(); int sfid = act->data().toInt(); QString fontname; if (sfid == lastindex) { sfid = FS_UNSPECIFIED_ID; fontname = "unspecified"; //Actually, it's not possible to reset fluid-channels as for now, } //so this is just a dummy that makes the synth block any events for the channel else { //sfid = getSoundFontId(act->text()); fontname = getSoundFontName((byte)sfid); } //byte channel = atoi(item->text().toLatin1()) - 1; if (keymod & (Qt::ShiftModifier|Qt::ControlModifier)) { for (int i = 0; i < FS_MAX_NR_OF_CHANNELS; i++) { if (keymod & Qt::ShiftModifier && channels[i] != FS_UNSPECIFIED_ID && i != row) continue; sendChannelChange((byte)sfid, (byte)i); channels[i] = (byte)sfid; channelListView->item(i, col)->setText(fontname); } } else { sendChannelChange((byte)sfid, (byte)row); channels[row] = (byte)sfid; item->setText(fontname); } sendUpdateDrumMaps(); } delete popup; } // Drumchannel column: else if (col == FS_DRUM_CHANNEL_COL) { QMenu* popup = new QMenu(this); QPoint ppt = channelListView->visualItemRect(item).bottomLeft(); QTableWidget* listView = item->tableWidget(); ppt += QPoint(40, listView->horizontalHeader()->height()); ppt = listView->mapToGlobal(ppt); QAction * yes = popup->addAction("Yes"); yes->setData(1); QAction * no = popup->addAction("No"); no->setData(0); //byte channel = atoi(item->text().toLatin1()) - 1; byte channel = row; QAction * act2 = popup->exec(ppt, 0); if (act2) { int index = act2->data().toInt(); if (index != drumchannels[channel]) { sendDrumChannelChange(index, channel); sendUpdateDrumMaps(); drumchannels[channel] = index; item->setText(index == 0 ? "No" : "Yes" ); } } delete popup; } } //--------------------------------------------------------- // getSoundFontId //--------------------------------------------------------- int FluidSynthGui::getSoundFontId(QString q) { int id = -1; for (std::list::iterator it = stack.begin(); it != stack.end(); it++) { if (q == it->name) id = it->id; } return id; } //--------------------------------------------------------- // sendChannelChange // Tell the client to set a soundfont to a specific fluid channel //--------------------------------------------------------- void FluidSynthGui::sendChannelChange(byte font_id, byte channel) { //byte data[3]; byte data[5]; data[0] = MUSE_SYNTH_SYSEX_MFG_ID; data[1] = FLUIDSYNTH_UNIQUE_ID; data[2] = FS_SOUNDFONT_CHANNEL_SET; data[3] = font_id; data[4] = channel; //sendSysex(data, 3); sendSysex(data, 5); } //--------------------------------------------------------- // sendDrumChannelChange // Tell the client to set a specific channel to drum channel (equiv to midichan 10) //--------------------------------------------------------- void FluidSynthGui::sendDrumChannelChange(byte onoff, byte channel) { //byte data[3]; byte data[5]; data[0] = MUSE_SYNTH_SYSEX_MFG_ID; data[1] = FLUIDSYNTH_UNIQUE_ID; data[2] = FS_DRUMCHANNEL_SET; data[3] = onoff; data[4] = channel; //sendSysex(data, 3); sendSysex(data, 5); if (FS_DEBUG) printf("Sent FS_DRUMCHANNEL_SET for channel %d, status: %d\n", channel, onoff); } void FluidSynthGui::popClicked() { if(currentlySelectedFont >= 0) { sendPopFont(currentlySelectedFont); sendUpdateDrumMaps(); } } void FluidSynthGui::sfItemClicked(QTreeWidgetItem* item, int /*col*/) { if (item != 0) { currentlySelectedFont = atoi(item->text(FS_ID_COL).toLatin1().constData()); Pop->setEnabled(true); } else { currentlySelectedFont = -1; Pop->setEnabled(false); } } #if 0 void FluidSynthGui::readData (int fd) { unsigned char buffer[512]; int n = ::read(fd, buffer, 512); // dataInput(buffer, n); } void FluidSynthGui::changeReverbRoomSize (int value) { sendParameterChange(MUSE_FLUID_PARAMETER_REVERB, "roomsize", value); } void FluidSynthGui::changeReverbDamping (int value) { sendParameterChange(MUSE_FLUID_PARAMETER_REVERB, "damping", value); } void FluidSynthGui::changeReverbWidth (int value) { sendParameterChange(MUSE_FLUID_PARAMETER_REVERB, "width", value); } void FluidSynthGui::changeChorusNumber (int value) { sendParameterChange(MUSE_FLUID_PARAMETER_CHORUS, "number", value); } void FluidSynthGui::changeChorusType (int value) { sendParameterChange(MUSE_FLUID_PARAMETER_CHORUS, "type", value); } void FluidSynthGui::changeChorusSpeed (int value) { sendParameterChange(MUSE_FLUID_PARAMETER_CHORUS, "speed", value); //TODO: Right now illegal values may be sent. //Make sure they stay within fluidsynths legal boundaries (0.29-5Hz) dunno what that is in doubles //This might be the case for the other chorus parameters as well } void FluidSynthGui::changeChorusDepth (int value) { sendParameterChange(MUSE_FLUID_PARAMETER_CHORUS, "depth", value); } void FluidSynthGui::changeChorusLevel (int value) { sendParameterChange(MUSE_FLUID_PARAMETER_CHORUS, "level", value); } void FluidSynthGui::sysexReceived(unsigned char const * data, int len) { char * cp; double * dp; //std::cerr << "FluidSynthGui, sysexReceived: " << (int) *data << std::endl; switch (*data) { case MUSE_FLUID_CLIENT_SEND_PARAMETER: cp = (char *) (data + 2); dp = (double *) (data + strlen (cp) + 3); setParameter ((int) *(data+1), cp, *dp); break; case MUSE_FLUID_GAIN_GET: dp = (double *) (data + 1); Gain->setValue ((int) (*dp * 12.8)); break; case MUSE_FLUID_CLIENT_LASTDIR_CHANGE: { if (*(char*)(data+1) != MUSE_FLUID_UNSPECIFIED_LASTDIR) _lastDir = QString((char*)(data+1)); else _lastDir=""; } default: break; } } void FluidSynthGui::requestAllParameters () { unsigned char data[1]; //data[0] = MUSE_FLUID_ADVGUI_GET; //sendSysex (data, 1); dbgMsg("Requesting all parameters!\n"); sendParameterRequest (MUSE_FLUID_PARAMETER_REVERB, "on"); sendParameterRequest (MUSE_FLUID_PARAMETER_REVERB, "roomsize"); sendParameterRequest (MUSE_FLUID_PARAMETER_REVERB, "damping"); sendParameterRequest (MUSE_FLUID_PARAMETER_REVERB, "width"); sendParameterRequest (MUSE_FLUID_PARAMETER_REVERB, "level"); sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "on"); sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "number"); sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "type"); sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "speed"); sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "depth"); sendParameterRequest (MUSE_FLUID_PARAMETER_CHORUS, "level"); data[0] = MUSE_FLUID_GAIN_GET; sendSysex (data, 1); data[0] = MUSE_FLUID_GUI_REQ_SOUNDFONTS; sendSysex (data, 1); } bool FluidSynthGui::sendParameterRequest (int parameterSet, const char * parameter) { size_t parameterMem = strlen (parameter) + 1; int datalen = 2 + parameterMem; unsigned char * data = new unsigned char [datalen]; *data = MUSE_FLUID_GUI_REQ_FXPARAMETER_GET; *(data + 1) = (char) parameterSet; memcpy (data + 2, parameter, parameterMem); sendSysex (data, datalen); delete data; return true; } void FluidSynthGui::setParameter (int parameterSet, const char * parameter, double value) { int ival = (int) (value * 128); std::string ps (parameter); if (parameterSet == MUSE_FLUID_PARAMETER_REVERB) { if (ps == "roomsize") { ReverbRoomSize->setValue (ival); } else if (ps == "damping") { ReverbDamping->setValue (ival); } else if (ps == "width") { ReverbWidth->setValue (ival); } else if (ps == "level") { ReverbLevel->setValue (ival); } else if (ps == "on") { Reverb->setChecked (ival); } } else { if (ps == "number") { ChorusNumber->setValue (ival); } else if (ps == "type") { ChorusType->setCurrentItem (ival); } else if (ps == "speed") { ChorusSpeed->setValue (ival); } else if (ps == "depth") { ChorusDepth->setValue (ival); } else if (ps == "level") { ChorusLevel->setValue (ival); } else if (ps == "on") { Chorus->setChecked (ival); } } } //Sends parameter to reverb or chorus bool FluidSynthGui::sendParameterChange (int parameterSet, const char * parameter, int value) { size_t parameterMem = strlen (parameter) + 1; int datalen = 2 + parameterMem + sizeof (double); unsigned char * data = new unsigned char [datalen]; *data = (unsigned char) MUSE_FLUID_GUI_REQ_FXPARAMETER_SET; *(data + 1) = (unsigned char) parameterSet; memcpy (data + 2, parameter, parameterMem); double * dp = (double *) (data + 2 + parameterMem); *dp = ((double) value) / ((double) 128.0); sendSysex (data, datalen); delete data; return true; } void FluidSynthGui::dbgMsg(const char* msg) { if (MUSE_FLUID_DEBUG) std::cerr << msg << std::endl; } //--------------------------------------------------------- // main //--------------------------------------------------------- /*QString museProject; QString museGlobalShare; QString museUser;*/ int main(int argc, char* argv[]) { /* museUser = getenv("MUSEHOME"); if (museUser == 0) museUser = getenv("HOME"); museGlobalShare = getenv("MUSE"); if (museGlobalShare == 0) { museGlobalShare = "/usr/muse"; if (access(museGlobalShare.toLatin1(), R_OK) != 0) { museGlobalShare = "/usr/local/muse"; if (access(museGlobalShare.toLatin1(), R_OK) != 0) museGlobalShare = museUser; } }*/ char * instanceName = argv[1]; QApplication app (argc, argv, true); QWidget* w = new FluidSynthGui (); if (argc > 1) w->setCaption(QString(instanceName)); w->show(); app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); qApp->exec(); } #endif muse-4.0.0/src/synti/fluidsynth/fluidsynthgui.h000066400000000000000000000155531404105311000216570ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./synti/fluidsynth/fluidsynthgui.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= /* * MusE FLUID Synth softsynth plugin * * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) * * $Id: fluidsynthgui.h,v 1.10.2.3 2009/02/02 21:38:02 terminator356 Exp $ * */ #ifndef __MUSE_FLUIDSYNTHGUI_H__ #define __MUSE_FLUIDSYNTHGUI_H__ #include "ui_fluidsynthguibase.h" #include "libsynti/gui.h" #include class QDialog; class QTreeWidgetItem; struct FluidChannel; #define FS_DEBUG 0 //Turn on/off debug #define FS_MAX_NR_OF_CHANNELS 16 #define FS_UNSPECIFIED_FONT 126 #define FS_UNSPECIFIED_ID 127 #define FS_UNSPECIFIED_PRESET 129 #define FS_CHANNEL_COL 0 #define FS_ID_COL 0 #define FS_SFNAME_COL 1 #define FS_SF_ID_COL 1 #define FS_DRUM_CHANNEL_COL 2 #define FS_SFDATALEN 1 #define FS_VERSION_MAJOR 0 #define FS_VERSION_MINOR 5 //#define FS_INIT_DATA_HEADER_SIZE 4 #define FS_INIT_DATA_HEADER_SIZE 6 // Including MFG + synth IDs #define FS_INIT_CHANNEL_SECTION 255 // Predefined init-values for fluidsynth #define FS_PREDEF_VOLUME 0.063 #define FS_PREDEF_REVERB_LEVEL 0.125 #define FS_PREDEF_REVERB_ROOMSIZE 0.125 #define FS_PREDEF_REVERB_DAMPING 0.3 #define FS_PREDEF_REVERB_WIDTH 0.125 #define FS_PREDEF_CHORUS_NUM 3 #define FS_PREDEF_CHORUS_TYPE 1 #define FS_PREDEF_CHORUS_SPEED 0.5 #define FS_PREDEF_CHORUS_DEPTH 0.3 #define FS_PREDEF_CHORUS_LEVEL 0.5 typedef unsigned char byte; /* #define MUSE_FLUID_UNSPECIFIED_CHANNEL 127 #define MUSE_FLUID_UNSPECIFIED_LASTDIR 127 */ //Various messages the gui and the client uses to communicate enum { FS_LASTDIR_CHANGE = 1, FS_PUSH_FONT }; enum { //FS_GAIN_SET, FS_SEND_SOUNDFONTDATA = 4, FS_SEND_CHANNELINFO, //Used by synth to send info about all channels, on init FS_SOUNDFONT_CHANNEL_SET, FS_SOUNDFONT_POP, FS_SEND_DRUMCHANNELINFO, //Used by synth to send drumchannel status about all channels, on init FS_DRUMCHANNEL_SET //Used by gui to set drumchannel status for specific channel }; enum { FS_DUMP_INFO = 240, FS_ERROR, FS_INIT_DATA }; /* enum { MUSE_FLUID_REVERB = 100, MUSE_FLUID_REVERB_ROOMSIZE, MUSE_FLUID_REVERB_DAMPING, MUSE_FLUID_REVERB_WIDTH, MUSE_FLUID_REVERB_LEVEL, MUSE_FLUID_CHORUS, MUSE_FLUID_CHORUS_NUMBER, MUSE_FLUID_CHORUS_TYPE, MUSE_FLUID_CHORUS_SPEED, MUSE_FLUID_CHORUS_DEPTH, MUSE_FLUID_CHORUS_LEVEL, MUSE_FLUID_GAIN, MUSE_FLUID_SOUNDFONT, MUSE_FLUID_STRING, MUSE_FLUID_STRING_END }; enum { MUSE_FLUID_CLIENT_SEND_PARAMETER = 33, MUSE_FLUID_CLIENT_SEND_SOUNDFONTS, MUSE_FLUID_PARAMETER_GET, MUSE_FLUID_PARAMETER_REVERB, MUSE_FLUID_PARAMETER_CHORUS, MUSE_FLUID_GAIN_GET, MUSE_FLUID_SOUNDFONT_PUSH, MUSE_FLUID_SOUNDFONT_POP, MUSE_FLUID_CLIENT_SEND_ERROR = 44, MUSE_FLUID_SOUNDFONT_LOAD, , MUSE_FLUID_CLIENT_RESTORE_CHANNELDATA, MUSE_FLUID_CLIENT_INIT_PARAMS, MUSE_FLUID_CLIENT_LASTDIR_CHANGE, MUSE_FLUID_GUI_REQ_SOUNDFONTS = 60, MUSE_FLUID_GUI_REQ_FXPARAMETER_SET, MUSE_FLUID_GUI_REQ_FXPARAMETER_GET, MUSE_FLUID_GUI_SEND_ERROR, MUSE_FLUID_GUI_LASTDIR_CHANGE }; */ struct FluidGuiSoundFont { QString filename; QString name; byte id; }; //--------------------------------------------------------- // FluidSynthGui //--------------------------------------------------------- class FluidSynthGui : public QDialog, public Ui::FLUIDSynthGuiBase, public MessGui { Q_OBJECT private: virtual void processEvent(const MusECore::MidiPlayEvent& ev); void sendLastdir(QString); void sendLoadFont(QString); void sendPopFont(int id); void sendUpdateDrumMaps(); void sendChannelChange(byte font_id, byte channel); void sendDrumChannelChange(byte onoff, byte channel); void updateSoundfontListView(); void updateChannelListView(); QString getSoundFontName(int id); int getSoundFontId(QString q); QString lastdir; std::list stack; byte channels[FS_MAX_NR_OF_CHANNELS]; //Array of bytes, for mapping soundfonts to individual channels byte drumchannels[FS_MAX_NR_OF_CHANNELS]; // Array of bytes for setting channels to drumchannels or not (equiv to midichan 10) int currentlySelectedFont; //Font currently selected in sfListView. -1 if none selected /* unsigned _smallH; unsigned _bigH; QSocketNotifier * _notifier; bool sendParameterChange (int, const char *, int); void setParameter (int, const char *, double); void requestAllParameters (); void dbgMsg(const char*); bool sendParameterRequest(int, const char *); //void dealWithSysex (unsigned char const * data, int datalen); */ private slots: void loadClicked(); void readMessage(); void changeGain(int); void dumpInfo(); void channelItemClicked(QTableWidgetItem* item); void toggleReverb(bool); void changeReverbLevel (int); void changeReverbRoomSize(int val); void changeReverbWidth(int val); void changeReverbDamping(int val); void toggleChorus(bool); void changeChorusNumber(int); void changeChorusType(int); void changeChorusSpeed(int); void changeChorusDepth(int); void changeChorusLevel(int); void popClicked(); void sfItemClicked(QTreeWidgetItem* item, int); /* void readData(int); */ public: // virtual void sysexReceived (const unsigned char *, int); // virtual void controllerReceived(int, int, int); FluidSynthGui(); ~FluidSynthGui(); }; #endif /* __MUSE_FLUIDSYNTHGUI_H__ */ muse-4.0.0/src/synti/fluidsynth/fluidsynthguibase.ui000066400000000000000000000405151404105311000226740ustar00rootroot00000000000000 FLUIDSynthGuiBase 0 0 617 514 FLUID Synth 1 QFrame::StyledPanel QFrame::Raised Load false Delete Dump Info 0 0 ID Fontname 0 0 true Chnl Soundfont Drum Chnl false QFrame::StyledPanel QFrame::Raised 16383 16 1638 Qt::Horizontal QSlider::TicksBothSides 1638 16383 16 1638 Qt::Horizontal QSlider::TicksBothSides 1638 16383 16 1638 Qt::Horizontal QSlider::TicksBothSides 1638 16383 16 1638 Qt::Horizontal QSlider::TicksBothSides 1638 Level Qt::AlignCenter false Width Qt::AlignCenter false Damping Qt::AlignCenter false Room Size Qt::AlignCenter false Reverb 75 true CHANNEL SETUP false Qt::AlignCenter true false QFrame::StyledPanel QFrame::Raised Sine Triangle Type Qt::AlignCenter false 127 Number Qt::AlignCenter false 16383 16 1638 Qt::Horizontal QSlider::TicksBothSides 1638 Speed Qt::AlignCenter false 16383 16 1638 Qt::Horizontal QSlider::TicksBothSides 1638 Depth Qt::AlignCenter false 16383 16 1638 Qt::Horizontal QSlider::TicksBothSides 1638 Level Qt::AlignCenter false Chorus Gain Qt::AlignCenter false 0 127 5 13 true Qt::Horizontal QSlider::TicksBothSides 7 75 true LOADED SOUNDFONTS Qt::AlignCenter true 0 0 image3 true false muse-4.0.0/src/synti/fluidsynth/fluidsynti.cpp000066400000000000000000002205351404105311000215040ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./synti/fluidsynth/fluidsynti.cpp $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= /* * MusE FLUID Synth softsynth plugin * * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) * * $Id: fluidsynti.cpp,v 1.19.2.18 2009/12/06 10:05:00 terminator356 Exp $ * */ #include #include #include #include #include #include #include #include //#include "common_defs.h" #include "fluidsynti.h" #include "muse/midi_consts.h" // fluid_synth_error() is deprecated in 2.0.2 and will cause a compile error. #define FLUIDSYNTI_FS_VERSION FLUIDSYNTI_FS_VERSION_CHECK(FLUIDSYNTH_VERSION_MAJOR, FLUIDSYNTH_VERSION_MINOR, FLUIDSYNTH_VERSION_MICRO) #define FLUIDSYNTI_FS_VERSION_CHECK(major, minor, micro) ((major<<16)|(minor<<8)|(micro)) #if ( FLUIDSYNTI_FS_VERSION < FLUIDSYNTI_FS_VERSION_CHECK(2, 0, 2) ) #define FLUIDSYNTI_HAVE_FLUID_SYNTH_ERROR 1 #endif #ifdef HAVE_INSTPATCH #include typedef std::multimap < int /* note */, std::string > NoteSampleNameList_t; typedef NoteSampleNameList_t::iterator iNoteSampleNameList_t; typedef NoteSampleNameList_t::const_iterator ciNoteSampleNameList_t; typedef std::pair NoteSampleNameInsertPair_t; typedef NoteSampleNameList_t NoteSampleNameList; typedef std::map < int /*patch*/, NoteSampleNameList_t > PatchNoteSampleNameList_t; typedef PatchNoteSampleNameList_t::iterator iPatchNoteSampleNameList_t; typedef PatchNoteSampleNameList_t::const_iterator ciPatchNoteSampleNameList_t; typedef std::pair PatchNoteSampleNameListResult_t; typedef std::pair PatchNoteSampleNameInsertPair_t; typedef PatchNoteSampleNameList_t PatchNoteSampleNameList; #endif FluidCtrl FluidSynth::fluidCtrl[] = { //{ "Expression", MusECore::CTRL_EXPRESSION, 0, 127 }, //{ "Sustain", MusECore::CTRL_SUSTAIN, 0, 127 }, //{ "Portamento", MusECore::CTRL_PORTAMENTO, 0, 127 }, //{ "Soft Pedal", MusECore::CTRL_SOFT_PEDAL, 0, 127 }, //{ "Variation", MusECore::CTRL_VARIATION_SEND, 0, 127 }, //{ "Channel reverb send", MusECore::CTRL_REVERB_SEND, 0, 127 }, //{ "Channel chorus send", MusECore::CTRL_CHORUS_SEND, 0, 127 }, //{ "Pitch", MusECore::CTRL_PITCH, -8192, 8191 } // These controllers' initial values are set by the FS_PREDEF_ values, so just set them to zero here. { "Gain", FS_GAIN ,0, 127, 0}, { "Master reverb on/off", FS_REVERB_ON , 0, 1, 0}, { "Master reverb level", FS_REVERB_LEVEL, 0, 16384, 0}, { "Master reverb size", FS_REVERB_ROOMSIZE, 0, 16384, 0}, // Interval: [0,1] { "Master reverb damping", FS_REVERB_DAMPING, 0, 16384, 0}, // Interval: [0,1] { "Master reverb width", FS_REVERB_WIDTH, 0, 16384, 0}, // Interval: [0,100] { "Master chorus on/off", FS_CHORUS_ON, 0, 1, 0}, { "Master chorus num delay lines", FS_CHORUS_NUM, 0, 10, 0}, //Default: 3 { "Master chorus type", FS_CHORUS_TYPE, 0, 1, 0}, { "Master chorus speed", FS_CHORUS_SPEED, 0, 16384, 0}, // (0.291,5) Hz { "Master chorus depth", FS_CHORUS_DEPTH, 0, 16384, 0}, // [0,40] { "Master chorus level", FS_CHORUS_LEVEL, 0, 16384, 0}, // [0,1] { "Program", MusECore::CTRL_PROGRAM, 0, 0xffffff, 0}, { "Modulation", MusECore::CTRL_MODULATION, 0, 127, 0}, { "Portamento time", MusECore::CTRL_PORTAMENTO_TIME, 0, 127, 0}, { "Volume", MusECore::CTRL_VOLUME, 0, 127, 100}, { "Pan", MusECore::CTRL_PANPOT, -64, 63, 0}, { "Expression", MusECore::CTRL_EXPRESSION, 0, 127, 127}, { "Sustain", MusECore::CTRL_SUSTAIN, 0, 127, 0}, { "Portamento", MusECore::CTRL_PORTAMENTO, 0, 127, 0}, { "Soft Pedal", MusECore::CTRL_SOFT_PEDAL, 0, 127, 0}, { "Variation", MusECore::CTRL_VARIATION_SEND, 0, 127, 0}, { "Channel reverb send", MusECore::CTRL_REVERB_SEND, 0, 127, 40}, { "Channel chorus send", MusECore::CTRL_CHORUS_SEND, 0, 127, 0}, { "Pitch", MusECore::CTRL_PITCH, -8192, 8191, 0}, { "Channel pressure", MusECore::CTRL_AFTERTOUCH, 0, 127, 0}, // Added by T356 { "Pitch bend sensitivity", FS_PITCHWHEELSENS, 0, 24, 2} }; static int NUM_CONTROLLER = sizeof(FluidSynth::fluidCtrl)/sizeof(*(FluidSynth::fluidCtrl)); QString projPathPtr; // // Fluidsynth // FluidSynth::FluidSynth(int sr, QMutex &_GlobalSfLoaderMutex) : Mess(2), _sfLoaderMutex(_GlobalSfLoaderMutex) { gui = nullptr; setSampleRate(sr); fluid_settings_t* s = new_fluid_settings(); fluid_settings_setnum(s, (char*) "synth.sample-rate", float(sampleRate())); fluidsynth = new_fluid_synth(s); if (!fluidsynth) { printf("Error while creating fluidsynth!\n"); return; } //Set up channels: for (int i=0; i 2 || (ver_maj == 2 && ver_min >= 1)) chorus_speed_range_lower = .1; else chorus_speed_range_lower = .29; QObject::connect(&fontWorker,SIGNAL(loadFontSignal(void*)),&fontWorker,SLOT(execLoadFont(void*))); fontWorker.moveToThread(&fontLoadThread); fontLoadThread.start(); } FluidSynth::~FluidSynth() { fontLoadThread.exit(); for (std::list::iterator it =stack.begin(); it !=stack.end(); it++) { if(it->intid == FS_UNSPECIFIED_FONT || it->intid == FS_UNSPECIFIED_ID) continue; //Try to unload soundfont int err = fluid_synth_sfunload(fluidsynth, it->intid, 0); if(err == -1) #ifdef FLUIDSYNTI_HAVE_FLUID_SYNTH_ERROR std::cerr << DEBUG_ARGS << "Error unloading soundfont!" << fluid_synth_error(fluidsynth) << std::endl; #else std::cerr << DEBUG_ARGS << "Error unloading soundfont! id: " << it->intid << std::endl; #endif } #if FLUIDSYNTH_VERSION_MAJOR < 2 int err = delete_fluid_synth (fluidsynth); #else delete_fluid_synth (fluidsynth); #endif if(gui) delete gui; if (initBuffer) delete [] initBuffer; #if FLUIDSYNTH_VERSION_MAJOR < 2 if (err == -1) { std::cerr << DEBUG_ARGS << "error while destroying synth: " << fluid_synth_error(fluidsynth) << std::endl; return; } #endif } bool FluidSynth::init(const char* name) { debug("FluidSynth::init\n"); gui = new FluidSynthGui(); gui->setWindowTitle(name); lastdir= ""; currentlyLoadedFonts = 0; nrOfSoundfonts = 0; sendChannelData(); cho_on = false; cho_num = FS_PREDEF_CHORUS_NUM; cho_type = FS_PREDEF_CHORUS_TYPE; cho_level = FS_PREDEF_CHORUS_LEVEL; cho_speed = FS_PREDEF_CHORUS_SPEED; cho_depth = FS_PREDEF_CHORUS_DEPTH; setController(0, FS_GAIN, (int)(fluidCtrl[0].max*FS_PREDEF_VOLUME)); setController(0, FS_REVERB_ON, 0); setController(0, FS_REVERB_LEVEL, (int)(fluidCtrl[2].max*FS_PREDEF_REVERB_LEVEL)); setController(0, FS_REVERB_ROOMSIZE, (int)(fluidCtrl[3].max*FS_PREDEF_REVERB_ROOMSIZE)); setController(0, FS_REVERB_DAMPING, (int)(fluidCtrl[4].max*FS_PREDEF_REVERB_DAMPING)); setController(0, FS_REVERB_WIDTH, (int)(fluidCtrl[5].max*FS_PREDEF_REVERB_WIDTH)); setController(0, FS_CHORUS_ON, 0); setController(0, FS_CHORUS_NUM, FS_PREDEF_CHORUS_NUM); //setController(0, FS_CHORUS_TYPE, FS_PREDEF_CHORUS_TYPE); //? setController(0, FS_CHORUS_SPEED, (int)(fluidCtrl[9].max*FS_PREDEF_CHORUS_SPEED)); setController(0, FS_CHORUS_DEPTH, (int)(fluidCtrl[10].max*FS_PREDEF_CHORUS_DEPTH)); setController(0, FS_CHORUS_LEVEL, (int)(fluidCtrl[11].max*FS_PREDEF_CHORUS_LEVEL)); return false; } int FluidSynth::oldMidiStateHeader(const unsigned char** data) const { static unsigned char const d[2] = {MUSE_SYNTH_SYSEX_MFG_ID, FLUIDSYNTH_UNIQUE_ID}; *data = &d[0]; return 2; } //--------------------------------------------------------- // processMessages // Called from host always, even if output path is unconnected. //--------------------------------------------------------- void FluidSynth::processMessages() { //Process messages from the gui while (gui->fifoSize()) { MusECore::MidiPlayEvent ev = gui->readEvent(); if (ev.type() == MusECore::ME_SYSEX) { sysex(ev.len(), ev.data()); sendEvent(ev); } else if (ev.type() == MusECore::ME_CONTROLLER) { setController(ev.channel(), ev.dataA(), ev.dataB(), true); sendEvent(ev); } else { if (FS_DEBUG) printf("FluidSynth::processMessages(): unknown event, type: %d\n", ev.type()); } } } //--------------------------------------------------------- // process // Called from host, ONLY if output path is connected. //--------------------------------------------------------- void FluidSynth::process(unsigned /*pos*/, float** ports, int offset, int len) { /* //Process messages from the gui while (gui->fifoSize()) { MusECore::MidiPlayEvent ev = gui->readEvent(); if (ev.type() == MusECore::ME_SYSEX) { sysex(ev.len(), ev.data()); sendEvent(ev); } else if (ev.type() == MusECore::ME_CONTROLLER) { setController(ev.channel(), ev.dataA(), ev.dataB(), true); sendEvent(ev); } else { if (FS_DEBUG) printf("FluidSynth::process(): unknown event, type: %d\n", ev.type()); } } */ if (fluid_synth_write_float(fluidsynth, len, ports[0], offset, 1, ports[1], offset, 1)) { M_ERROR("Error writing from synth!"); return; } } //--------------------------------------------------------- // getInitData // Prepare data that will restore the synth's state on load //--------------------------------------------------------- void FluidSynth::getInitData(int* n, const unsigned char** data) { // Data setup: // FS_INIT_DATA (1 byte) // FluidSynth version (2 bytes, x.y) // n = Number of soundfonts (1 byte) // Lastdir (variable size) // // FS_FONTS_BEGIN // n blocks with font path (variable size) // n bytes with font external id // // for all channels (16), 1 byte each for external id + 1 byte for preset + 1 byte for bankno // which is mapped to internal id after all fonts are loaded. // // reverb + chorus on/off (2 bytes) if (FS_DEBUG) printf("FluidSynth::getInitData()\n"); //Calculate length: int len = FS_INIT_DATA_HEADER_SIZE + strlen(lastdir.c_str()) + 1; //header size for (std::list::const_iterator it = stack.begin(); it!=stack.end(); it++) { // if the soundfont is located under the projectPath we extract this from the filename int fileLen = it->file_name.size(); if (it->file_name.startsWith(projPathPtr)) { printf("project path found in filename, len %d shortened with %d\n",fileLen, projPathPtr.length()+1); fileLen = fileLen - projPathPtr.length()-1; } len+=fileLen + 2; } //Add length for lastdir and channels: len+=strlen(lastdir.c_str())+1; len+=(FS_MAX_NR_OF_CHANNELS*4); // 4 bytes: ext+int id + bankno + drumchannel status // + reverb len+=2; // sliders len += sizeof(float) + sizeof(double) * 7 + sizeof(byte) * 2; if (FS_DEBUG) printf("Total length of init sysex: %d\n", len); //byte* d = new byte[len]; if (len > initLen) { if (initBuffer) delete [] initBuffer; initBuffer = new byte[len]; initLen = len; } // Header: //d[0] = FS_INIT_DATA; //d[1] = FS_VERSION_MAJOR; //d[2] = FS_VERSION_MINOR; //d[3] = stack.size(); initBuffer[0] = MUSE_SYNTH_SYSEX_MFG_ID; initBuffer[1] = FLUIDSYNTH_UNIQUE_ID; initBuffer[2] = FS_INIT_DATA; initBuffer[3] = FS_VERSION_MAJOR; initBuffer[4] = FS_VERSION_MINOR; initBuffer[5] = stack.size(); //Lastdir: byte* chptr = initBuffer + FS_INIT_DATA_HEADER_SIZE; memcpy(chptr, lastdir.c_str(), strlen(lastdir.c_str())+1); //For each font... chptr+=strlen(lastdir.c_str())+1; for (std::list::const_iterator it =stack.begin(); it!=stack.end(); it++) { // if the soundfont is located under the projectPath we extract this from the filename int offset=0; if (it->file_name.startsWith(projPathPtr)) { offset= projPathPtr.length()+1; } memcpy(chptr, it->file_name.toLatin1().constData()+offset, it->file_name.size()-offset+1); //printf("path name stored=%s\n", it->filename.c_str()+offset); chptr = chptr + 1 + it->file_name.size()-offset; } //For each font again... *chptr = FS_INIT_CHANNEL_SECTION; chptr++; for (std::list::const_iterator it =stack.begin(); it!=stack.end(); it++) { *chptr = it->extid; chptr++; } //External id:s & preset for all channels: for(int i=0; i 0.4) float f = fluid_synth_get_gain(fluidsynth); memcpy(chptr, &f, sizeof(float)); chptr += sizeof(float); double d = fluid_synth_get_reverb_roomsize(fluidsynth); memcpy(chptr, &d, sizeof(double)); chptr += sizeof(double); d = fluid_synth_get_reverb_damp(fluidsynth); memcpy(chptr, &d, sizeof(double)); chptr += sizeof(double); d = fluid_synth_get_reverb_width(fluidsynth); memcpy(chptr, &d, sizeof(double)); chptr += sizeof(double); d = fluid_synth_get_reverb_level(fluidsynth); memcpy(chptr, &d, sizeof(double)); chptr += sizeof(double); byte b = static_cast(fluid_synth_get_chorus_nr(fluidsynth)); memcpy(chptr, &b, sizeof(byte)); chptr += sizeof(byte); b = static_cast(fluid_synth_get_chorus_type(fluidsynth)); memcpy(chptr, &b, sizeof(byte)); chptr += sizeof(byte); d = fluid_synth_get_chorus_level(fluidsynth); memcpy(chptr, &d, sizeof(double)); chptr += sizeof(double); // functions renamed in libfluidsynth 2, just for the fun of breaking things... // now (2.2) even the new ones are deprecated again, together with all the other setters/getters, // all supposed to be removed in fs 3 - there will be lot of fun when fs 3 is out... #if FLUIDSYNTH_VERSION_MAJOR > 1 d = fluid_synth_get_chorus_speed(fluidsynth); #else d = fluid_synth_get_chorus_speed_Hz(fluidsynth); #endif memcpy(chptr, &d, sizeof(double)); chptr += sizeof(double); #if FLUIDSYNTH_VERSION_MAJOR > 1 d = fluid_synth_get_chorus_depth(fluidsynth); #else d = fluid_synth_get_chorus_depth_ms(fluidsynth); #endif memcpy(chptr, &d, sizeof(double)); // chptr += sizeof(double); if (FS_DEBUG) { for (int i=0; i 4) { // magic numbers taken from FluidSynth::setController { float f; memcpy(&f, &d[arrayIndex], sizeof(float)); fluid_synth_set_gain(fluidsynth, f); MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_GAIN, int(f * 25)); gui->writeEvent(ev); arrayIndex += sizeof(float); } { double size, damping, width, level; memcpy(&size, &d[arrayIndex], sizeof(double)); arrayIndex += sizeof(double); memcpy(&damping, &d[arrayIndex], sizeof(double)); arrayIndex += sizeof(double); memcpy(&width, &d[arrayIndex], sizeof(double)); arrayIndex += sizeof(double); memcpy(&level, &d[arrayIndex], sizeof(double)); arrayIndex += sizeof(double); fluid_synth_set_reverb(fluidsynth, size, damping, width, level); MusECore::MidiPlayEvent ev1(0, 0, 0, MusECore::ME_CONTROLLER, FS_REVERB_LEVEL, static_cast(level * 16384/2)); gui->writeEvent(ev1); MusECore::MidiPlayEvent ev2(0, 0, 0, MusECore::ME_CONTROLLER, FS_REVERB_WIDTH, static_cast(width * 164)); gui->writeEvent(ev2); MusECore::MidiPlayEvent ev3(0, 0, 0, MusECore::ME_CONTROLLER, FS_REVERB_DAMPING, static_cast(damping * 16384)); gui->writeEvent(ev3); MusECore::MidiPlayEvent ev4(0, 0, 0, MusECore::ME_CONTROLLER, FS_REVERB_ROOMSIZE, static_cast(size * 16384)); gui->writeEvent(ev4); } { byte num, type; double level, speed, depth; memcpy(&num, &d[arrayIndex], sizeof(byte)); arrayIndex += sizeof(byte); memcpy(&type, &d[arrayIndex], sizeof(byte)); arrayIndex += sizeof(byte); memcpy(&level, &d[arrayIndex], sizeof(double)); arrayIndex += sizeof(double); memcpy(&speed, &d[arrayIndex], sizeof(double)); arrayIndex += sizeof(double); memcpy(&depth, &d[arrayIndex], sizeof(double)); // arrayIndex += sizeof(double); fluid_synth_set_chorus(fluidsynth, num, level, speed, depth, type); MusECore::MidiPlayEvent ev1(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_NUM, num); gui->writeEvent(ev1); MusECore::MidiPlayEvent ev2(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_TYPE, type); gui->writeEvent(ev2); MusECore::MidiPlayEvent ev3(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_SPEED, static_cast(speed * 3479 - chorus_speed_range_lower)); gui->writeEvent(ev3); MusECore::MidiPlayEvent ev4(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_DEPTH, static_cast(depth * 16383/40)); gui->writeEvent(ev4); MusECore::MidiPlayEvent ev5(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_LEVEL, static_cast(level * 16383)); gui->writeEvent(ev5); } } //if (FS_DEBUG) printf("--- END PARSE INIT DATA ---\n"); for (int i=0; iwriteEvent(ev); } //----------------------------------- // pushSoundfont - load a soundfont //----------------------------------- bool FluidSynth::pushSoundfont (const char* filename, int extid) { FS_Helper *helper = new FS_Helper; helper->fptr = this; helper->id = extid; QString fn = QString::fromLatin1(filename); if (QFile::exists(fn)) { helper->file_name = fn; } else { //printf("current path: %s \nmuseProject %s\nfilename %s\n",QDir::currentPath().toLatin1().constData(), MusEGlobal::museProject.toLatin1().constData(), filename); QFileInfo fi(fn); if (QFile::exists(fi.fileName())) // check if the file exists in current folder helper->file_name = QDir::currentPath() + "/" + fi.fileName(); else { // TODO: Strings should be translated, this does // however require the class to be derived from qobject // tried in vain to make the call in the gui object // could'nt get it to work due to symbol missing in .so ... // helper->file_name = QFileDialog::getOpenFileName(0, // QString("Can't find soundfont: %1 - Choose soundfont").arg(filename), // fn, // QString("Soundfonts (*.sf2);;All files (*)")); // QMessageBox::warning(NULL,"No sound font found.","Could not open soundfont: " + fn, QMessageBox::Ok); fprintf(stderr, "Warning: Could not open soundfont: %s\n", fn.toLatin1().constData()); return false; } } fontWorker.loadFont(helper); return true; } #ifdef HAVE_INSTPATCH static void loadSf2NoteSampleNames(FluidSoundFont& font, IpatchSF2 *sf2) { IpatchList *presets, *pZones, *iZones; IpatchIter pIter, pZoneIter, iZoneIter; IpatchItem *pset, *pZone, *inst, *iZone; char *psetName, *instName, *sampName; int bank, program, patch; gboolean pRangeSet, iRangeSet; IpatchSF2GenAmount pNoteRange, iNoteRange; IpatchSF2Sample *samp; // Get preset children of SoundFont presets = ipatch_container_get_children (IPATCH_CONTAINER (sf2), IPATCH_TYPE_SF2_PRESET); if(!presets) return; ipatch_list_init_iter (presets, &pIter); // Iterate over presets in list for (pset = ipatch_item_first (&pIter); pset != NULL; pset = ipatch_item_next (&pIter)) { // Get name of preset, MIDI bank, and MIDI program number g_object_get (pset, "name", &psetName, "bank", &bank, "program", &program, NULL); //fprintf(stderr, "psetName:%s bank:%d program:%d\n", psetName, bank, program); // Get preset zones pZones = ipatch_container_get_children (IPATCH_CONTAINER (pset), IPATCH_TYPE_SF2_ZONE); // No preset zone? Then we have no note name list for this preset. if(!pZones) { g_free (psetName); continue; } // Compose a patch numer. Drums are on special bank 128. patch = (bank << 16) | (0xff << 8) | (program & 0x7f); PatchNoteSampleNameListResult_t res_pnsnl = font._noteSampleNameList.insert(PatchNoteSampleNameInsertPair_t(patch, NoteSampleNameList())); iPatchNoteSampleNameList_t res_ipnsnl = res_pnsnl.first; NoteSampleNameList& nsl = res_ipnsnl->second; ipatch_list_init_iter (pZones, &pZoneIter); // Iterate over preset zones for (pZone = ipatch_item_first (&pZoneIter); pZone != NULL; pZone = ipatch_item_next (&pZoneIter)) { // Get linked instrument and preset zone note range set flag g_object_get (pZone, "link-item", &inst, "note-range-set", &pRangeSet, NULL); // Get instrument name and global zone note range g_object_get (inst, "name", &instName, NULL); //fprintf(stderr, "\tinstName:%s\n", instName); // Get instrument zones iZones = ipatch_container_get_children (IPATCH_CONTAINER (inst), IPATCH_TYPE_SF2_ZONE); // No instrument zones? Then we have an empty note name list for this preset. if(!iZones) { g_free (instName); g_object_unref (inst); continue; } ipatch_list_init_iter (iZones, &iZoneIter); for (iZone = ipatch_item_first (&iZoneIter); iZone != NULL; iZone = ipatch_item_next (&iZoneIter)) { // Get sample and instrument zone note range set flag g_object_get (iZone, "note-range-set", &iRangeSet, "link-item", &samp, NULL); // Get instrument name and global zone note range g_object_get (samp, "name", &sampName, NULL); if (pRangeSet) ipatch_sf2_gen_item_get_amount (IPATCH_SF2_GEN_ITEM (pZone), IPATCH_SF2_GEN_NOTE_RANGE, &pNoteRange); else ipatch_sf2_gen_item_get_amount (IPATCH_SF2_GEN_ITEM (pset), IPATCH_SF2_GEN_NOTE_RANGE, &pNoteRange); if (iRangeSet) ipatch_sf2_gen_item_get_amount (IPATCH_SF2_GEN_ITEM (iZone), IPATCH_SF2_GEN_NOTE_RANGE, &iNoteRange); else ipatch_sf2_gen_item_get_amount (IPATCH_SF2_GEN_ITEM (inst), IPATCH_SF2_GEN_NOTE_RANGE, &iNoteRange); if (ipatch_sf2_gen_range_intersect (&iNoteRange, &pNoteRange)) // Returns false if no common range { // Note range spans iNoteRange.range.low to iNoteRange.range.high //fprintf(stderr, "\t\t%s\tiNoteRange h:%u l:%u pNoteRange h:%u l:%u\n", sampName, iNoteRange.range.high, iNoteRange.range.low, pNoteRange.range.high, pNoteRange.range.low); for(int iNote = iNoteRange.range.low; iNote <= iNoteRange.range.high; ++iNote) // Yes, that's less than or equal. nsl.insert(NoteSampleNameInsertPair_t(iNote, std::string(sampName))); } g_free (sampName); g_object_unref (samp); } g_object_unref (iZones); g_free (instName); g_object_unref (inst); } g_object_unref (pZones); g_free (psetName); } g_object_unref (presets); } //--------------------------------------------------------- // loadNoteSampleNames // Extracts all the note sample names //--------------------------------------------------------- static void loadNoteSampleNames(FluidSoundFont& font) { IpatchSF2 *sf2; IpatchSF2Reader* sf2_reader; IpatchFileHandle *fhandle; IpatchSF2File *sffile; GError *err = NULL; const QByteArray ba = font.file_name.toLatin1(); const char* fname = ba.constData(); /* initialize libInstPatch */ ipatch_init (); sffile = ipatch_sf2_file_new (); if(!sffile) return; fhandle = ipatch_file_open(IPATCH_FILE(sffile), fname, "r", &err); if(!fhandle) { fprintf (stderr, "Failed to identify file '%s': %s\n", fname, ipatch_gerror_message (err)); g_clear_error (&err); g_object_unref (sffile); return; } sf2_reader = ipatch_sf2_reader_new(fhandle); if(!sf2_reader) { ipatch_file_close(fhandle); g_object_unref (sffile); return; } sf2 = ipatch_sf2_reader_load(sf2_reader, NULL); if(!sf2) { ipatch_file_close(fhandle); //g_object_unref(sf2_reader); // Needed? g_object_unref (sffile); return; } loadSf2NoteSampleNames(font, sf2); ipatch_file_close(fhandle); g_object_unref (sf2); //g_object_unref(sf2_reader); // Needed? g_object_unref (sffile); } #endif //--------------------------------------------------------- // playNote // called from host //--------------------------------------------------------- bool FluidSynth::playNote(int channel, int pitch, int velo) { if (channels[channel].font_intid == FS_UNSPECIFIED_FONT || channels[channel].font_intid == FS_UNSPECIFIED_ID) return false; if (velo) { if (fluid_synth_noteon(fluidsynth, channel, pitch, velo)) { if (FS_DEBUG) #ifdef FLUIDSYNTI_HAVE_FLUID_SYNTH_ERROR std::cerr << DEBUG_ARGS << "error processing noteon event: " << fluid_synth_error(fluidsynth); #else std::cerr << DEBUG_ARGS << "error processing noteon event: channel: " << channel << " pitch: " << pitch << " velo: " << velo << std::endl; #endif } } else { if (fluid_synth_noteoff(fluidsynth, channel, pitch)) if (FS_DEBUG) #ifdef FLUIDSYNTI_HAVE_FLUID_SYNTH_ERROR std::cerr << DEBUG_ARGS << "error processing noteoff event: " << fluid_synth_error(fluidsynth) << std::endl; #else std::cerr << DEBUG_ARGS << "error processing noteoff event: channel: " << channel << " pitch: " << pitch << std::endl; #endif } return false; } //--------------------------------------------------------- // sendSoundFontData //--------------------------------------------------------- void FluidSynth::sendSoundFontData() { int ndatalen = 2; //2 bytes for command and length //int ndatalen = 4; // 4 bytes for header, command and length //Calculate length in chars of all strings in the soundfontstack in one string for (std::list::iterator it = stack.begin(); it != stack.end(); it++) { ndatalen += 1 + it->name.size(); ndatalen += FS_SFDATALEN; //unsigned char for ID } byte ndata[ndatalen]; *(ndata) = FS_SEND_SOUNDFONTDATA; //The command *(ndata + 1) = (unsigned char)stack.size (); //Nr of Soundfonts //*ndata = MUSE_SYNTH_SYSEX_MFG_ID; //*(ndata + 1) = FLUIDSYNTH_UNIQUE_ID; //*(ndata + 2) = FS_SEND_SOUNDFONTDATA; //The command //*(ndata + 3) = (unsigned char)stack.size (); //Nr of Soundfonts // Copy the stuff to ndatalen: char* chunk_start = (char*)(ndata + 2); //char* chunk_start = (char*)(ndata + 4); int chunk_len, name_len; for (std::list::iterator it = stack.begin(); it != stack.end(); ++it) { name_len = it->name.size() + 1; chunk_len = name_len + FS_SFDATALEN; memcpy(chunk_start, it->name.toLatin1().constData(), name_len); //First, store the fontname *(chunk_start + name_len) = it->extid; //The GUI only needs to know about the external id, store that here chunk_start += chunk_len; } sendSysex(ndatalen, ndata); } //--------------------------------------------------------- // sendChannelData //--------------------------------------------------------- void FluidSynth::sendChannelData() { ///int chunk_size = 2; int const chunk_size = 2; int chdata_length = (chunk_size * FS_MAX_NR_OF_CHANNELS) +1 ; //Command and the 2 channels * 16 //int chdata_length = (chunk_size * FS_MAX_NR_OF_CHANNELS) +3 ; // Header, command and the 2 channels * 16 byte chdata[chdata_length]; byte* chdptr; chdata[0] = FS_SEND_CHANNELINFO; //chdata[0] = MUSE_SYNTH_SYSEX_MFG_ID; //chdata[1] = FLUIDSYNTH_UNIQUE_ID; //chdata[2] = FS_SEND_CHANNELINFO; chdptr = (chdata + 1); //chdptr = (chdata + 3); for (int i=0; i::iterator it = stack.begin(); it != stack.end(); it++) printf("Font: %s\tintid: %d\textid %d\tfilename:%s\n", it->name.toLatin1().constData(), it->intid, it->extid, it->file_name.toLatin1().constData()); printf("Reverb on: %d, width: %f, size: %f level: %f damp: %f\n",rev_on, rev_width, rev_size, rev_level, rev_damping); printf("-----------------------------------------------------\n"); } //--------------------------------------------------------- // nativeGuiVisible //--------------------------------------------------------- bool FluidSynth::nativeGuiVisible() const { return gui->isVisible(); } //--------------------------------------------------------- // showNativeGui //--------------------------------------------------------- void FluidSynth::showNativeGui(bool val) { gui->setVisible(val); } //--------------------------------------------------------- // setController //--------------------------------------------------------- bool FluidSynth::setController(int channel, int id, int val) { setController(channel, id, val, false); return false; } //--------------------------------------------------------- // setController //--------------------------------------------------------- void FluidSynth::setController(int channel, int id, int val, bool fromGui) { // // Channelless controllers // int err = 0; switch (id) { case FS_GAIN: { fluid_synth_set_gain(fluidsynth, (float) val/25); //gives val an interval of approximately[0,5] //Forward to gui if not from Gui if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_GAIN, val); gui->writeEvent(ev); } break; } case FS_REVERB_ON: { rev_on = val; fluid_synth_set_reverb_on(fluidsynth, val); // 0 or 1 //if (rev_on) // fluid_synth_set_reverb(fluidsynth, rev_size, rev_damping, rev_width, rev_level); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_REVERB_ON, val); gui->writeEvent(ev); } break; } case FS_REVERB_LEVEL: //Interval: 0-2 rev_level = (double)2*val/16384; //[0,2] //if (rev_on) fluid_synth_set_reverb(fluidsynth, rev_size, rev_damping, rev_width, rev_level); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_REVERB_LEVEL, val); gui->writeEvent(ev); } break; case FS_REVERB_WIDTH: // rev_width = (double)val/164; //[0,100] //if (rev_on) fluid_synth_set_reverb(fluidsynth, rev_size, rev_damping, rev_width, rev_level); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_REVERB_WIDTH, val); gui->writeEvent(ev); } break; case FS_REVERB_DAMPING: //[0,1] rev_damping = (double)val/16384; //if (rev_on) fluid_synth_set_reverb(fluidsynth, rev_size, rev_damping, rev_width, rev_level); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_REVERB_DAMPING, val); gui->writeEvent(ev); } break; case FS_REVERB_ROOMSIZE: //[0,1] rev_size = (double)val/16384; //if (rev_on) fluid_synth_set_reverb(fluidsynth, rev_size, rev_damping, rev_width, rev_level); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_REVERB_ROOMSIZE, val); gui->writeEvent(ev); } break; case FS_CHORUS_ON: {// 0 or 1 cho_on = val; fluid_synth_set_chorus_on(fluidsynth, val); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_ON, val); gui->writeEvent(ev); } break; } case FS_CHORUS_NUM: {//Number of delay lines cho_num = val; fluid_synth_set_chorus(fluidsynth, cho_num, cho_level, cho_speed, cho_depth, cho_type); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_NUM, val); gui->writeEvent(ev); } break; } case FS_CHORUS_TYPE: {//? cho_type = val; fluid_synth_set_chorus(fluidsynth, cho_num, cho_level, cho_speed, cho_depth, cho_type); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_TYPE, val); gui->writeEvent(ev); } break; } case FS_CHORUS_SPEED: {//(0.291,5) Hz (0.29Hz acc.to API docu, changed to 0.1Hz in fs 2.1.0) cho_speed = (double)(chorus_speed_range_lower + (double)val/3479); fluid_synth_set_chorus(fluidsynth, cho_num, cho_level, cho_speed, cho_depth, cho_type); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_SPEED, val); gui->writeEvent(ev); } break; } case FS_CHORUS_DEPTH: { //[0,40] cho_depth = (double) val*40/16383; fluid_synth_set_chorus(fluidsynth, cho_num, cho_level, cho_speed, cho_depth, cho_type); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_DEPTH, val); gui->writeEvent(ev); } break; } case FS_CHORUS_LEVEL: { //[0,1] cho_level = (double) val/16383; fluid_synth_set_chorus(fluidsynth, cho_num, cho_level, cho_speed, cho_depth, cho_type); if (!fromGui) { MusECore::MidiPlayEvent ev(0, 0, 0, MusECore::ME_CONTROLLER, FS_CHORUS_LEVEL, val); gui->writeEvent(ev); } break; } // // Controllers that depend on channels // case MusECore::CTRL_PITCH: // MusE's range is from -8192 to +8191, fluidsynth seems to be [0, 16384] if(val != MusECore::CTRL_VAL_UNKNOWN) { val +=8192; err = fluid_synth_pitch_bend (fluidsynth, channel, val); } break; case MusECore::CTRL_AFTERTOUCH: if(val != MusECore::CTRL_VAL_UNKNOWN) err = fluid_synth_channel_pressure (fluidsynth, channel, val); break; case FS_PITCHWHEELSENS: if(val != MusECore::CTRL_VAL_UNKNOWN) err = fluid_synth_pitch_wheel_sens(fluidsynth, channel, val); break; case MusECore::CTRL_PROGRAM: { //Check if MusE is trying to set a preset on an unspecified font. If so, ignore. if (FS_DEBUG) printf("Program select : channel %d val %d\n",channel, val); byte font_intid = channels[channel].font_intid; if (font_intid == FS_UNSPECIFIED_ID || font_intid == FS_UNSPECIFIED_FONT) return; byte banknum = ((val >> 16) & 0xff); byte patch = (val & 0xff); //printf("val: %d banknum: %x patch: %d\n", val, banknum, patch); if(val == MusECore::CTRL_VAL_UNKNOWN || patch == 0xff) return; if(channels[channel].drumchannel) banknum = 128; else if(banknum == 0xff) banknum = 0; // Is wise? Else try to keep a previous value when 'off' (0xff) like the HW values? err = fluid_synth_program_select(fluidsynth, channel, font_intid , banknum, patch); if (err) #ifdef FLUIDSYNTI_HAVE_FLUID_SYNTH_ERROR printf("FluidSynth::setController() - Error changing program on soundfont %s, channel: %d\n", fluid_synth_error(fluidsynth), channel); #else printf("FluidSynth::setController() - Error changing program on soundfont, channel: %d id: %d banknum: %d patch:%d\n", channel, font_intid, banknum, patch); #endif else { channels[channel].preset = val;//setChannelPreset(val, channel); channels[channel].banknum = banknum; } break; } default: if (FS_DEBUG) printf("Setting controller on channel: %d with id: 0x%x to val: %d\n",channel, id, val); err = fluid_synth_cc(fluidsynth, channel, id, val); break; } if (err) #ifdef FLUIDSYNTI_HAVE_FLUID_SYNTH_ERROR printf ("FluidSynth::setController() - error processing controller event: %s\n", fluid_synth_error(fluidsynth)); #else printf ("FluidSynth::setController() - error processing controller event, channel: %d, ctrl: %d val: %d\n", channel, id, val); #endif } //--------------------------------------------------------- // getControllerInfo //--------------------------------------------------------- int FluidSynth::getControllerInfo(int id, const char** name, int* controller, int* min, int* max, int* initval) const { if (id >= NUM_CONTROLLER) return 0; *controller = fluidCtrl[id].num; *name = fluidCtrl[id].name; *min = fluidCtrl[id].min; *max = fluidCtrl[id].max; switch(id) { case 0: *initval = (int)(fluidCtrl[0].max*FS_PREDEF_VOLUME); break; case 1: *initval = 0; break; case 2: *initval = (int)(fluidCtrl[2].max*FS_PREDEF_REVERB_LEVEL); break; case 3: *initval = (int)(fluidCtrl[3].max*FS_PREDEF_REVERB_ROOMSIZE); break; case 4: *initval = (int)(fluidCtrl[4].max*FS_PREDEF_REVERB_DAMPING); break; case 5: *initval = (int)(fluidCtrl[5].max*FS_PREDEF_REVERB_WIDTH); break; case 6: *initval = 0; break; case 7: *initval = (int)(fluidCtrl[7].max*FS_PREDEF_CHORUS_NUM); break; case 8: *initval = (int)(fluidCtrl[8].max*FS_PREDEF_CHORUS_TYPE); break; case 9: *initval = (int)(fluidCtrl[9].max*FS_PREDEF_CHORUS_SPEED); break; case 10: *initval = (int)(fluidCtrl[10].max*FS_PREDEF_CHORUS_DEPTH); break; case 11: *initval = (int)(fluidCtrl[11].max*FS_PREDEF_CHORUS_LEVEL); break; default: *initval = fluidCtrl[id].initval; break; } if (FS_DEBUG) printf("FluidSynth::getControllerInfo() id: %d name: %s controller: %d min: %d max: %d initval: %d\n", id,*name,*controller,*min,*max,*initval); return ++id; } #ifdef HAVE_INSTPATCH bool FluidSynth::getNoteSampleName(bool drum, int channel, int patch, int note, const char** name) const { if(!name || channel < 0 || channel >= FS_MAX_NR_OF_CHANNELS) return false; const FluidChannel& fc = channels[channel]; if(fc.drumchannel != drum) return false; // Force the low bank to don't care, we don't use it in fluidsynth MESS). patch |= 0xff00; if(drum) { patch &= 0xffff; // Remove the high bank. patch |= 0x800000; // Set high bank to 128 (special soundfont bank number meaning drums), and low bank to don't care. } for(std::list::const_iterator it = stack.begin(); it != stack.end(); it++) { const FluidSoundFont& fsf = *it; if(fsf.intid == fc.font_intid) // || fsf.extid == fc.font_extid) { ciPatchNoteSampleNameList_t ipnsnl = fsf._noteSampleNameList.find(patch); if(ipnsnl == fsf._noteSampleNameList.end()) { // The soundfont has no note name list for that patch. return false; } else { const NoteSampleNameList& pnsnl = ipnsnl->second; ciNoteSampleNameList_t insnl = pnsnl.find(note); if(insnl == pnsnl.end()) { // The soundfont has a note name list, but no name was found for that note. *name = NULL; return true; } else { // The soundfont has a note name list, and a name was found for that note. const std::string& str = insnl->second; *name = str.c_str(); return true; } } } } return false; } #endif //--------------------------------------------------------- // sendError //--------------------------------------------------------- void FluidSynth::sendError(const char *errorMessage) { int len = 2 + strlen(errorMessage); //int len = 4 + strlen(errorMessage); unsigned char data[len]; *data = FS_ERROR; //data[0] = MUSE_SYNTH_SYSEX_MFG_ID; //data[1] = FLUIDSYNTH_UNIQUE_ID; //data[2] = FS_ERROR; memcpy(data + 1, errorMessage, len - 1); //memcpy(data + 3, errorMessage, len - 3); sendSysex(len, data); } //--------------------------------------------------------- // getNextAvailableExternalId //--------------------------------------------------------- int FluidSynth::getNextAvailableExternalId() { unsigned char place[FS_MAX_NR_OF_CHANNELS]; for(int i=0; i::iterator it = stack.begin(); it != stack.end(); it++) place[it->extid] = 1; int i=0; while (i < FS_MAX_NR_OF_CHANNELS && place[i] == 1) i++; return i; } //--------------------------------------------------------- // sfChannelChange //--------------------------------------------------------- void FluidSynth::sfChannelChange(byte extid, byte channel) { if (FS_DEBUG) printf("FluidSynth::sfChannelChange()-Setting channel %d to font with extid %d intid %d\n",channel, extid, getFontInternalIdByExtId(extid)); channels[channel].font_extid = extid; channels[channel].font_intid = getFontInternalIdByExtId(extid); } //--------------------------------------------------------- // getFontInternalIdByExtId //--------------------------------------------------------- byte FluidSynth::getFontInternalIdByExtId(byte ext_id) { for (std::list::iterator it = stack.begin(); it !=stack.end(); it++) { if (it->extid == ext_id) return it->intid; } return FS_UNSPECIFIED_FONT; } //--------------------------------------------------------- // sendLastDir //--------------------------------------------------------- void FluidSynth::sendLastdir(const char* lastdir) { int n = strlen(lastdir) + 2; //int n = strlen(lastdir) + 4; byte d[n]; d[0] = FS_LASTDIR_CHANGE; //d[0] = MUSE_SYNTH_SYSEX_MFG_ID; //d[1] = FLUIDSYNTH_UNIQUE_ID; //d[2] = FS_LASTDIR_CHANGE; memcpy(d+1,lastdir, strlen(lastdir)+1); //memcpy(d+3,lastdir, strlen(lastdir)+1); MusECore::MidiPlayEvent ev(0,0, MusECore::ME_SYSEX, d, n); gui->writeEvent(ev); } //--------------------------------------------------------- // rewriteChannelSettings //--------------------------------------------------------- void FluidSynth::rewriteChannelSettings() { //Walk through the channels, remap internal ID:s to external ID:s (something that actually only needs to be done at //startup, since the fonts aren't loaded yet at that time and it isn't possible to give them a correct internal id //since they don't have any at that time, this can probably be fixed in a smarter way (but it works..)) for (int i=0; i"; else if (channels[i].preset == FS_UNSPECIFIED_PRESET) return ""; else { fluid_preset_t *preset = fluid_synth_get_channel_preset(fluidsynth, i); if (!preset) return ""; #if FLUIDSYNTH_VERSION_MAJOR < 2 return preset->get_name(preset); #else return fluid_preset_get_name(preset); #endif } } //--------------------------------------------------------- // getPatchInfo //--------------------------------------------------------- const MidiPatch* FluidSynth::getPatchInfo(int i, const MidiPatch* patch) const { //if (channels[i].font_intid == FS_UNSPECIFIED_FONT) if (channels[i].font_intid == FS_UNSPECIFIED_FONT || channels[i].font_intid == FS_UNSPECIFIED_ID) return 0; //else if (channels[i].preset == FS_UNSPECIFIED_PRESET) // return 0; else { //printf("Getpatchname, channel: %d\n",channel); if (!patch) //Deliver first patch return getFirstPatch(i); else //Deliver next patch return getNextPatch(i, patch); } } //--------------------------------------------------------- // getFirstPatch //--------------------------------------------------------- const MidiPatch* FluidSynth::getFirstPatch (int channel) const { static MidiPatch midiPatch; midiPatch.typ = 0; midiPatch.lbank = 0; fluid_preset_t* preset; int font_id = channels[channel].font_intid; //if (font_id == FS_UNSPECIFIED_FONT) if (font_id == FS_UNSPECIFIED_FONT || font_id == FS_UNSPECIFIED_ID) return 0; fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(fluidsynth, font_id); if (!channels[channel].drumchannel) { for (unsigned bank = 0; bank < 128; ++bank) { for (unsigned patch = 0; patch < 128; ++patch) { #if FLUIDSYNTH_VERSION_MAJOR < 2 preset = sfont->get_preset (sfont, bank, patch); #else preset = fluid_sfont_get_preset (sfont, bank, patch); #endif if (preset) { midiPatch.hbank = bank; midiPatch.lbank = 0xff; // Off midiPatch.prog = patch; #if FLUIDSYNTH_VERSION_MAJOR < 2 midiPatch.name = preset->get_name (preset); #else midiPatch.name = fluid_preset_get_name (preset); #endif return &midiPatch; } } } return 0; } else { //This is a drumchannel int bank = 128; for (unsigned patch = 0; patch < 128; ++patch) { #if FLUIDSYNTH_VERSION_MAJOR < 2 preset = sfont->get_preset (sfont, bank, patch); #else preset = fluid_sfont_get_preset (sfont, bank, patch); #endif if (preset) { midiPatch.hbank = 0xff; // Off midiPatch.lbank = 0xff; // Off midiPatch.prog = patch; #if FLUIDSYNTH_VERSION_MAJOR < 2 midiPatch.name = preset->get_name(preset); #else midiPatch.name = fluid_preset_get_name (preset); #endif return &midiPatch; } } } return 0; } //--------------------------------------------------------- // getNextPatch //--------------------------------------------------------- const MidiPatch* FluidSynth::getNextPatch (int channel, const MidiPatch* patch) const { static MidiPatch midiPatch; //First check if there actually is any soundfont associated to the channel. If not, don't bother int font_id = channels[channel].font_intid; if (font_id == FS_UNSPECIFIED_FONT || font_id == FS_UNSPECIFIED_ID) return 0; if (patch == 0) return getFirstPatch(channel); midiPatch.typ = 0; midiPatch.lbank = 0; //printf("Font has internal id: %d\n",font_id); fluid_preset_t* preset; fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(fluidsynth, font_id); if (!channels[channel].drumchannel) { unsigned prog = patch->prog + 1; for (unsigned bank = patch->hbank; bank < 128; ++bank) { for ( ; prog < 128; ++prog) { #if FLUIDSYNTH_VERSION_MAJOR < 2 preset = sfont->get_preset (sfont, bank, prog); #else preset = fluid_sfont_get_preset (sfont, bank, prog); #endif if (preset) { //printf("Preset info: bank: %d prog: %d name: %s\n", bank, prog, preset->get_name(preset)); midiPatch.hbank = bank; midiPatch.lbank = 0xff; // Off midiPatch.prog = prog; #if FLUIDSYNTH_VERSION_MAJOR < 2 midiPatch.name = preset->get_name (preset); #else midiPatch.name = fluid_preset_get_name (preset); #endif return &midiPatch; } } prog = 0; // Reset if we "come around" } } else { //This is a drum channel unsigned bank = 128; unsigned prog = patch->prog; for (prog = patch->prog + 1; prog < 128; ++prog) { #if FLUIDSYNTH_VERSION_MAJOR < 2 preset = sfont->get_preset (sfont, bank, prog); #else preset = fluid_sfont_get_preset (sfont, bank, prog); #endif if (preset) { //printf("Preset info: bank: %d prog: %d name: %s\n",bank, prog, preset->get_name(preset)); midiPatch.hbank = 0xff; // Off midiPatch.lbank = 0xff; // Off midiPatch.prog = prog; #if FLUIDSYNTH_VERSION_MAJOR < 2 midiPatch.name = preset->get_name (preset); #else midiPatch.name = fluid_preset_get_name (preset); #endif return &midiPatch; } } } return 0; } //--------------------------------------------------------- // popSoundfont //--------------------------------------------------------- bool FluidSynth::popSoundfont (int ext_id) { bool success = false; int int_id = getFontInternalIdByExtId(ext_id); //if (int_id == FS_UNSPECIFIED_FONT) { if (int_id == FS_UNSPECIFIED_FONT || int_id == FS_UNSPECIFIED_ID) { std::cerr << DEBUG_ARGS << "Internal error! Request for deletion of Soundfont that is not registered!" << std::endl; } else { //Try to unload soundfont int err = fluid_synth_sfunload(fluidsynth, int_id, 0); if (err != -1) {//Success //Check all channels that the font is used in for (int i=0; i::iterator it =stack.begin(); it !=stack.end(); it++) { if (it->intid == int_id) { stack.erase(it); break; } } //Resend fontdata & re-initialize sendSoundFontData(); sendChannelData(); rewriteChannelSettings(); success = true; currentlyLoadedFonts--; } else //OK, there was trouble #ifdef FLUIDSYNTI_HAVE_FLUID_SYNTH_ERROR std::cerr << DEBUG_ARGS << "Error unloading soundfont!" << fluid_synth_error(fluidsynth) << std::endl; #else std::cerr << DEBUG_ARGS << "Error unloading soundfont! id: " << int_id << std::endl; #endif } if (FS_DEBUG) printf("Removed soundfont with ext it: %d\n",ext_id); return success; } void LoadFontWorker::loadFont(void* h) { emit loadFontSignal(h); } //--------------------------------------------------------- // execLoadFont // helper function to load soundfont in the // background. //--------------------------------------------------------- void LoadFontWorker::execLoadFont(void * t) { FS_Helper *h = (FS_Helper*) t; FluidSynth* fptr = h->fptr; QByteArray ba = h->file_name.toLocal8Bit(); const char* filename = ba.constData(); if (FS_DEBUG) printf("execLoadFont() font name %s\n", filename); //Let only one loadThread have access to the fluidsynth-object at the time QMutexLocker ml(&fptr->_sfLoaderMutex); int rv = fluid_synth_sfload(fptr->fluidsynth, filename, 1); if (rv ==-1) { #ifdef FLUIDSYNTI_HAVE_FLUID_SYNTH_ERROR fptr->sendError(fluid_synth_error(fptr->fluidsynth)); #endif if (FS_DEBUG) #ifdef FLUIDSYNTI_HAVE_FLUID_SYNTH_ERROR std::cerr << DEBUG_ARGS << "error loading soundfont: " << fluid_synth_error(fptr->fluidsynth) << std::endl; #else std::cerr << DEBUG_ARGS << "error loading soundfont: " << filename << std::endl; #endif delete h; return; } //Deal with internal and external id etc. if (FS_DEBUG) printf("Soundfont %s loaded, index %d\n", filename, rv); FluidSoundFont font; font.file_name = h->file_name; font.intid = rv; if (h->id == FS_UNSPECIFIED_ID) { font.extid = fptr->getNextAvailableExternalId(); if (FS_DEBUG) printf("Font got extid %d\n",font.extid); } else font.extid = h->id; if (FS_DEBUG) printf("Font has external id: %d int id:%d\n", font.extid, font.intid); //Strip off the filename QFileInfo fi(h->file_name); font.name = fi.fileName(); #ifdef HAVE_INSTPATCH loadNoteSampleNames(font); #endif fptr->stack.push_front(font); fptr->currentlyLoadedFonts++; if (FS_DEBUG) printf("Currently loaded fonts: %d Nr of soundfonts: %d\n",fptr->currentlyLoadedFonts, fptr->nrOfSoundfonts); //Check whether this was the last font or not. If so, run initSynth(); if (fptr->nrOfSoundfonts <= fptr->currentlyLoadedFonts) { if (FS_DEBUG) printf("This was the last font, rewriting channel settings...\n"); fptr->rewriteChannelSettings(); //Update data in GUI-window. fptr->sendSoundFontData(); fptr->sendChannelData(); } delete h; } //--------------------------------------------------------- // instantiate // construct a new synthesizer instance //--------------------------------------------------------- class QWidget; static QMutex globalFluidSynthMutex; static Mess* instantiate(unsigned long long /*parentWinId*/, const char* name, const MessConfig* config) { printf("fluidsynth sampleRate %d\n", config->_sampleRate); projPathPtr = QString(config->_projectPath); FluidSynth* synth = new FluidSynth(config->_sampleRate, globalFluidSynthMutex); if (synth->init(name)) { delete synth; synth = 0; } return synth; } extern "C" { static MESS descriptor = { "FluidSynth", "FluidSynth soundfont loader by Mathias Lundgren", //Mathias Lundgren (lunar_shuttle@users.sf.net) "0.1", //Version string MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate, }; // We must compile with -fvisibility=hidden to avoid namespace // conflicts with global variables. // Only visible symbol is "mess_descriptor". // (TODO: all plugins should be compiled this way) __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } muse-4.0.0/src/synti/fluidsynth/fluidsynti.h000066400000000000000000000162161404105311000211500ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./synti/fluidsynth/fluidsynti.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // (C) Copyright 2016 Tim E. Real (terminator356 on users dot sourceforge dot net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= /* * MusE FLUID Synth softsynth plugin * * Copyright (C) 2004 Mathias Lundgren (lunar_shuttle@users.sourcforge.net) * * $Id: fluidsynti.h,v 1.15.2.5 2009/11/19 04:20:33 terminator356 Exp $ * */ #ifndef __MUSE_FLUIDSYNTI_H__ #define __MUSE_FLUIDSYNTI_H__ #include #include #include #include #include #include "fluidsynthgui.h" #include "libsynti/mess.h" #include "muse/debug.h" #include "mpevent.h" #include "midictrl_consts.h" #include "common_defs.h" // TODO: Try to not include this. Standalone build of plugin? #include "config.h" #ifdef HAVE_INSTPATCH #include #endif #define FS_DEBUG_DATA 0 //Turn on/off debug print of midi data sent to fluidsynth typedef unsigned char byte; struct FluidSoundFont { QString file_name; QString name; byte extid, intid; #ifdef HAVE_INSTPATCH std::map < int /*patch*/, std::multimap < int /* note */, std::string > > _noteSampleNameList; #endif }; struct FluidCtrl { const char* name; int num; int min, max; //int val; int initval; }; // NRPN-controllers: static const int FS_GAIN = 0 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_REVERB_ON = 1 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_REVERB_LEVEL = 2 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_REVERB_ROOMSIZE = 3 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_REVERB_DAMPING = 4 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_REVERB_WIDTH = 5 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_CHORUS_ON = 6 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_CHORUS_NUM = 7 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_CHORUS_TYPE = 8 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_CHORUS_SPEED = 9 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_CHORUS_DEPTH = 10 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_CHORUS_LEVEL = 11 + MusECore::CTRL_NRPN14_OFFSET; static const int FS_PITCHWHEELSENS = 0 + MusECore::CTRL_RPN_OFFSET; // FluidChannel is used to map different soundfonts to different fluid-channels // This is to be able to select different presets from specific soundfonts, since // Fluidsynth has a quite strange way of dealing with fontloading and channels // We also need this since getFirstPatch and getNextPatch only tells us which channel is // used, so this works as a connection between soundfonts and fluid-channels (one channel // can only have one soundfont, but one soundfont can have many channels) struct FluidChannel { byte font_extid, font_intid, preset, drumchannel; byte banknum; // hbank }; class LoadFontWorker : public QObject { Q_OBJECT public: LoadFontWorker() {} void loadFont(void*); signals: void loadFontSignal(void*); private slots: void execLoadFont(void*); }; class FluidSynth : public Mess { private: bool pushSoundfont (const char*, int); void sendSysex(int l, const unsigned char* d); void sendLastdir(const char*); void sfChannelChange(unsigned char font_id, unsigned char channel); void parseInitData(int n, const byte* d); byte* initBuffer; int initLen; double chorus_speed_range_lower; byte getFontInternalIdByExtId (byte channel); void debug(const char* msg) { if (FS_DEBUG) printf("Debug: %s\n",msg); } void dumpInfo(); //Prints out debug info FluidChannel channels[FS_MAX_NR_OF_CHANNELS]; std::string lastdir; QThread fontLoadThread; LoadFontWorker fontWorker; const MidiPatch * getFirstPatch (int channel) const; const MidiPatch* getNextPatch (int, const MidiPatch *) const; //For reverb and chorus: double rev_size, rev_damping, rev_width, rev_level, cho_level, cho_speed, cho_depth; bool rev_on, cho_on; int cho_num, cho_type; public: FluidSynth(int sr, QMutex &_GlobalSfLoaderMutex); virtual ~FluidSynth(); bool init(const char*); // This is only a kludge required to support old songs' midistates. Do not use in any new synth. virtual int oldMidiStateHeader(const unsigned char** data) const; virtual void processMessages(); virtual void process(unsigned pos, float**, int, int); virtual bool playNote(int channel, int pitch, int velo); virtual bool sysex(int, const unsigned char*); virtual bool setController(int, int, int); void setController(int, int , int, bool); virtual void getInitData(int*, const unsigned char**); virtual const char* getPatchName(int, int, bool) const; virtual const MidiPatch* getPatchInfo(int i, const MidiPatch* patch) const; virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) const; virtual bool processEvent(const MusECore::MidiPlayEvent&); #ifdef HAVE_INSTPATCH // Returns true if a note name list is found for the given patch. // If true, name either contains the note name, or is NULL if no note name was found. virtual bool getNoteSampleName(bool drum, int channel, int patch, int note, const char** name) const; #endif virtual bool hasNativeGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool val); void sendError(const char*); void sendSoundFontData(); void sendChannelData(); void rewriteChannelSettings(); //used because fluidsynth does some very nasty things when loading a font! bool popSoundfont (int ext_id); int getNextAvailableExternalId(); fluid_synth_t* fluidsynth; FluidSynthGui* gui; QMutex& _sfLoaderMutex; int currentlyLoadedFonts; //To know whether or not to run the init-parameters std::list stack; int nrOfSoundfonts; void initInternal(); static FluidCtrl fluidCtrl[]; }; struct FS_Helper //Only used to pass parameters when calling the loading thread { FluidSynth* fptr; QString file_name; int id; }; // static void* fontLoadThread(void* t); // moved to the implementation file -Orcan #endif /* __MUSE_FLUIDSYNTI_H__ */ muse-4.0.0/src/synti/fluidsynth/saw.xpm000066400000000000000000000004401404105311000201150ustar00rootroot00000000000000/* XPM */ static const char* const saw_xpm[] = { "12 12 2 1", ". c #000000", "# c #20ff18", "............", "............", "............", "............", "....#.......", "...#.#.....#", "..#...#...#.", ".#.....#.#..", "#.......#...", "............", "............", "............"}; muse-4.0.0/src/synti/fluidsynth/sine.xpm000066400000000000000000000004411404105311000202620ustar00rootroot00000000000000/* XPM */ static const char* const sine_xpm[] = { "12 12 2 1", ". c #000000", "# c #20ff18", "............", "............", "............", "............", "..###.....##", ".#...#...#..", ".#...#...#..", "#.....###...", "............", "............", "............", "............"}; muse-4.0.0/src/synti/libsimpleplugin/000077500000000000000000000000001404105311000176055ustar00rootroot00000000000000muse-4.0.0/src/synti/libsimpleplugin/CMakeLists.txt000066400000000000000000000052031404105311000223450ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= QT5_WRAP_CPP ( simpler_plugingui_mocs simpler_plugingui.h ) ## ## UI files ## file (GLOB simpler_plugingui_ui_files simplepluginchooserbase.ui ) QT5_WRAP_UI ( simpler_plugingui_uis ${simpler_plugingui_ui_files} ) ## ## List of source files to compile ## file (GLOB simpler_plugingui_source_files simpler_plugingui.cpp ) file (GLOB simpler_plugin_source_files simpler_plugin.cpp # simpler_plugin_lv2.cpp ) ## ## Define target ## add_library ( simpler_plugingui SHARED ## ${MODULES_BUILD} ${simpler_plugingui_source_files} ${simpler_plugingui_mocs} ${simpler_plugingui_uis} ) add_library ( simpler_plugin SHARED ## ${MODULES_BUILD} ${simpler_plugin_source_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${simpler_plugingui_source_files} ${simpler_plugingui_ui_files} ${simpler_plugin_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( simpler_plugingui PROPERTIES OUTPUT_NAME muse_simpler_plugingui ) set_target_properties( simpler_plugin PROPERTIES OUTPUT_NAME muse_simpler_plugin ) ## ## Linkage ## target_link_libraries(simpler_plugingui ${QT_LIBRARIES} simpler_plugin ) target_link_libraries(simpler_plugin plugin_cache_reader_module ${QT_LIBRARIES} dl ) ## ## Install location ## # if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS simpler_plugingui simpler_plugin DESTINATION ${MusE_MODULES_DIR} ) # endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/synti/libsimpleplugin/README000066400000000000000000000002711404105311000204650ustar00rootroot00000000000000This library is a simple LADSPA plugin loader and processor. It is simplified but similar to the 'plugin.cpp/h' source code in the main application, which is too complex for this use. muse-4.0.0/src/synti/libsimpleplugin/lv2extprg.h000066400000000000000000000155021404105311000217160ustar00rootroot00000000000000/* LV2 Programs Extension Copyright 2012 Filipe Coelho 2014 Deryabin Andrew Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file lv2extprg.h C header for the LV2 programs extension . */ #ifndef LV2_PROGRAMS_H #define LV2_PROGRAMS_H #define LV2_PROGRAMS_URI "http://kxstudio.sf.net/ns/lv2ext/programs" #define LV2_PROGRAMS_PREFIX LV2_PROGRAMS_URI "#" #define LV2_PROGRAMS__Host LV2_PROGRAMS_PREFIX "Host" #define LV2_PROGRAMS__Interface LV2_PROGRAMS_PREFIX "Interface" #define LV2_PROGRAMS__UIInterface LV2_PROGRAMS_PREFIX "UIInterface" #define LV2_PROGRAMSNEW_URI "http://yoshimi.sf.net/ns/lv2ext/programs" #define LV2_PROGRAMSNEW_PREFIX LV2_PROGRAMSNEW_URI "#" #define LV2_PROGRAMSNEW__Interface LV2_PROGRAMSNEW_PREFIX "Interface" #define LV2_PROGRAMSNEW__UIInterface LV2_PROGRAMSNEW_PREFIX "UIInterface" #ifdef __cplusplus extern "C" { #endif typedef void* LV2_Programs_Handle; typedef struct _LV2_Program_Descriptor { /** Bank number for this program. Note that this extension does not support MIDI-style separation of bank LSB and MSB values. There is no restriction on the set of available banks: the numbers do not need to be contiguous, there does not need to be a bank 0, etc. */ uint32_t bank; /** Program number (unique within its bank) for this program. There is no restriction on the set of available programs: the numbers do not need to be contiguous, there does not need to be a program 0, etc. */ uint32_t program; /** Name of the program. */ const char * name; } LV2_Program_Descriptor; /** Programs extension, plugin data. When the plugin's extension_data is called with argument LV2_PROGRAMS__Interface, the plugin MUST return an LV2_Programs_Instance structure, which remains valid for the lifetime of the plugin. */ typedef struct _LV2_Programs_Interface { /** * get_program() * * This member is a function pointer that provides a description * of a program (named preset sound) available on this plugin. * * The index argument is an index into the plugin's list of * programs, not a program number as represented by the Program * field of the LV2_Program_Descriptor. (This distinction is * needed to support plugins that use non-contiguous program or * bank numbers.) * * This function returns a LV2_Program_Descriptor pointer that is * guaranteed to be valid only until the next call to get_program * or deactivate, on the same plugin instance. This function must * return NULL if passed an index argument out of range, so that * the host can use it to query the number of programs as well as * their properties. */ const LV2_Program_Descriptor *(*get_program)(LV2_Handle handle, uint32_t index); /** * select_program() * * This member is a function pointer that selects a new program * for this plugin. The program change should take effect * immediately at the start of the next run() call. (This * means that a host providing the capability of changing programs * between any two notes on a track must vary the block size so as * to place the program change at the right place. A host that * wanted to avoid this would probably just instantiate a plugin * for each program.) * * Plugins should ignore a select_program() call with an invalid * bank or program. * * A plugin is not required to select any particular default * program on activate(): it's the host's duty to set a program * explicitly. * * A plugin is permitted to re-write the values of its input * control ports when select_program is called. The host should * re-read the input control port values and update its own * records appropriately. (This is the only circumstance in which * a LV2 plugin is allowed to modify its own control-input ports.) */ void (*select_program)(LV2_Handle handle, uint32_t bank, uint32_t program); void (*select_program_for_channel)(LV2_Handle handle, unsigned char channel, uint32_t bank, uint32_t program); } LV2_Programs_Interface; /** Programs extension, UI data. When the UI's extension_data is called with argument LV2_PROGRAMS__UIInterface, the UI MUST return an LV2_Programs_UI_Interface structure, which remains valid for the lifetime of the UI. */ typedef struct _LV2_Programs_UI_Interface { /** * select_program() * * This is exactly the same as select_program in LV2_Programs_Instance, * but this struct relates to the UI instead of the plugin. * * When called, UIs should update their state to match the selected program. */ void (*select_program)(LV2UI_Handle handle, uint32_t bank, uint32_t program); void (*select_program_for_channel)(LV2_Handle handle, unsigned char channel, uint32_t bank, uint32_t program); } LV2_Programs_UI_Interface; /** Feature data for LV2_PROGRAMS__Host. */ typedef struct _LV2_Programs_Host { /** * Opaque host data. */ LV2_Programs_Handle handle; /** * program_changed() * * Tell the host to reload a plugin's program. * Parameter handle MUST be the 'handle' member of this struct. * Parameter index is program index to change. * When index is -1, host should reload all the programs. * * The plugin MUST NEVER call this function on a RT context or during run(). * * NOTE: This call is to inform the host about a program's bank, program or name change. * It DOES NOT change the current selected program. */ void (*program_changed)(LV2_Programs_Handle handle, int32_t index); } LV2_Programs_Host; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LV2_PROGRAMS_H */ muse-4.0.0/src/synti/libsimpleplugin/lv2extui.h000066400000000000000000000067201404105311000215450ustar00rootroot00000000000000/* LV2 External UI extension This work is in public domain. This file 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. If you have questions, contact Filipe Coelho (aka falkTX) or ask in #lad channel, FreeNode IRC network. */ /** @file lv2_external_ui.h C header for the LV2 External UI extension . */ #ifndef LV2_EXTERNAL_UI_H #define LV2_EXTERNAL_UI_H #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #define LV2_EXTERNAL_UI_URI "http://kxstudio.sf.net/ns/lv2ext/external-ui" #define LV2_EXTERNAL_UI_PREFIX LV2_EXTERNAL_UI_URI "#" #define LV2_EXTERNAL_UI__Host LV2_EXTERNAL_UI_PREFIX "Host" #define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget" /** This extension used to be defined by a lv2plug.in URI */ #define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external" #ifdef __cplusplus extern "C" { #endif /** * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget. * UI is created in invisible state. */ typedef struct _LV2_External_UI_Widget { /** * Host calls this function regulary. UI library implementing the * callback may do IPC or redraw the UI. * * @param _this_ the UI context */ void (*run)(struct _LV2_External_UI_Widget * _this_); /** * Host calls this function to make the plugin UI visible. * * @param _this_ the UI context */ void (*show)(struct _LV2_External_UI_Widget * _this_); /** * Host calls this function to make the plugin UI invisible again. * * @param _this_ the UI context */ void (*hide)(struct _LV2_External_UI_Widget * _this_); } LV2_External_UI_Widget; #define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr) #define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr) #define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr) /** * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature. * LV2_Feature::data must be pointer to LV2_External_UI_Host. */ typedef struct _LV2_External_UI_Host { /** * Callback that plugin UI will call when UI (GUI window) is closed by user. * This callback will be called during execution of LV2_External_UI_Widget::run() * (i.e. not from background thread). * * After this callback is called, UI is defunct. Host must call LV2UI_Descriptor::cleanup(). * If host wants to make the UI visible again, the UI must be reinstantiated. * * @note When using the depreated URI LV2_EXTERNAL_UI_DEPRECATED_URI, * some hosts will not call LV2UI_Descriptor::cleanup() as they should, * and may call show() again without re-initialization. * * @param controller Host context associated with plugin UI, as * supplied to LV2UI_Descriptor::instantiate(). */ void (*ui_closed)(LV2UI_Controller controller); /** * Optional (may be NULL) "user friendly" identifier which the UI * may display to allow a user to easily associate this particular * UI instance with the correct plugin instance as it is represented * by the host (e.g. "track 1" or "channel 4"). * * If supplied by host, the string will be referenced only during * LV2UI_Descriptor::instantiate() */ const char * plugin_human_id; } LV2_External_UI_Host; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LV2_EXTERNAL_UI_H */ muse-4.0.0/src/synti/libsimpleplugin/simplepluginchooserbase.ui000066400000000000000000000054731404105311000251030ustar00rootroot00000000000000 SimplePluginChooserBase 0 0 777 681 SimpleDrums - Ladspa Plugin Chooser 6 10 10 10 10 Name Label Inports Outports Creator 6 0 0 0 0 Qt::Horizontal QSizePolicy::Expanding 301 31 &Cancel Alt+C &OK Alt+O muse-4.0.0/src/synti/libsimpleplugin/simpler_plugin.cpp000066400000000000000000001221071404105311000233450ustar00rootroot00000000000000// // C++ Implementation: plugin // // Description: // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // // Additions/modifications: Mathias Lundgren , (C) 2004 // (C) Copyright 2011 Tim E. Real (terminator356 at users.sourceforge.net) // // // 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; 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; 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 #include #include "simpler_plugin.h" #include "plugin_cache_reader.h" #define SS_LOG_MAX 0 #define SS_LOG_MIN -10 #define SS_LOG_OFFSET SS_LOG_MIN #define SP_TRACE_FUNC 0 #define SP_DEBUG_LADSPA 0 #define SP_TRACE_IN if (SP_TRACE_FUNC) fprintf (stderr, "->%s:%d\n", __PRETTY_FUNCTION__, __LINE__); #define SP_TRACE_OUT if (SP_TRACE_FUNC) fprintf (stderr, "<-%s:%d\n", __PRETTY_FUNCTION__, __LINE__); #define SP_ERROR(string) fprintf(stderr, "SimplePlugin error: %s\n", string) #define SP_DBG_LADSPA(string1) if (SP_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1); #define SP_DBG_LADSPA2(string1, string2) if (SP_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, string2); // Turn on debugging messages. //#define PLUGIN_DEBUGIN // Turn on constant stream of debugging messages. //#define PLUGIN_DEBUGIN_PROCESS namespace MusESimplePlugin { PluginList plugins; // // Map plugin parameter on domain [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] to domain [SS_LOG_MIN, SS_LOG_MAX] (log domain) // float SS_map_pluginparam2logdomain(int pluginparam_val) { float scale = (float) (SS_LOG_MAX - SS_LOG_MIN)/ (float) SS_PLUGIN_PARAM_MAX; float scaled = (float) pluginparam_val * scale; float mapped = scaled + SS_LOG_OFFSET; return mapped; } // // Map plugin parameter on domain to domain [SS_LOG_MIN, SS_LOG_MAX] to [SS_PLUGIN_PARAM_MIN, SS_PLUGIN_PARAM_MAX] (from log-> [0,127]) // (inverse func to the above) int SS_map_logdomain2pluginparam(float pluginparam_log) { float mapped = pluginparam_log - SS_LOG_OFFSET; float scale = (float) SS_PLUGIN_PARAM_MAX / (float) (SS_LOG_MAX - SS_LOG_MIN); int scaled = (int) round(mapped * scale); return scaled; } //--------------------------------------------------------- // initPlugins // search for LADSPA plugins //--------------------------------------------------------- void SS_initPlugins(const QString& hostCachePath) { SP_TRACE_IN MusEPlugin::PluginScanList scan_list; // Read host plugin cache file. We only want ladspa plugins for now... MusEPlugin::readPluginCacheFile(hostCachePath + "/scanner", &scan_list, false, false, MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA); for(MusEPlugin::ciPluginScanList isl = scan_list.begin(); isl != scan_list.end(); ++isl) { const MusEPlugin::PluginScanInfoRef inforef = *isl; const MusEPlugin::PluginScanInfoStruct& info = inforef->info(); switch(info._type) { case MusEPlugin::PluginScanInfoStruct::PluginTypeLADSPA: { //if(MusEGlobal::loadPlugins) { // Make sure it doesn't already exist. if(/*Plugin* pl =*/ plugins.find(PLUGIN_GET_QSTRING(info._completeBaseName), PLUGIN_GET_QSTRING(info._label))) { //fprintf(stderr, "Ignoring LADSPA effect label:%s path:%s duplicate of path:%s\n", // info._label.toLatin1().constData(), // info._fi.filePath().toLatin1().constData(), // pl->filePath().toLatin1().constData()); } else { plugins.push_back(new LadspaPlugin(info)); } } } break; case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSI: case MusEPlugin::PluginScanInfoStruct::PluginTypeDSSIVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeLV2: case MusEPlugin::PluginScanInfoStruct::PluginTypeLinuxVST: case MusEPlugin::PluginScanInfoStruct::PluginTypeMESS: case MusEPlugin::PluginScanInfoStruct::PluginTypeUnknown: case MusEPlugin::PluginScanInfoStruct::PluginTypeNone: case MusEPlugin::PluginScanInfoStruct::PluginTypeAll: break; } } SP_TRACE_OUT } //--------------------------------------------------------- // find //--------------------------------------------------------- Plugin* PluginList::find(const QString& file, const QString& name) { SP_TRACE_IN for (iPlugin i = begin(); i != end(); ++i) { if ((file == (*i)->lib()) && (name == (*i)->label())) { SP_TRACE_OUT return *i; } } //fprintf(stderr, "Plugin <%s> not found\n", name.toLatin1().constData()); SP_TRACE_OUT return 0; } PluginList::~PluginList() { //fprintf(stderr, "~PluginList\n"); //Cleanup plugins: for (iPlugin i = plugins.begin(); i != plugins.end(); ++i) { if((*i)->references() != 0) { fprintf(stderr, "~PluginList: Plugin <%s> reference count not zero! Cannot delete.\n", (*i)->name().toLatin1().constData()); continue; } //fprintf(stderr, "~PluginList: deleting plugin <%s>\n", // (*i)->name().toLatin1().constData()); delete (*i); } } //--------------------------------------------------------- // Plugin //--------------------------------------------------------- Plugin::Plugin(const MusEPlugin::PluginScanInfoStruct& info) : _fi(PLUGIN_GET_QSTRING(info.filePath())), _libHandle(0), _references(0), _instNo(0), _uniqueID(info._uniqueID), _label(PLUGIN_GET_QSTRING(info._label)), _name(PLUGIN_GET_QSTRING(info._name)), _maker(PLUGIN_GET_QSTRING(info._maker)), _copyright(PLUGIN_GET_QSTRING(info._copyright)), _portCount(info._portCount), _inports(info._inports), _outports(info._outports), _controlInPorts(info._controlInPorts), _controlOutPorts(info._controlOutPorts), _requiredFeatures(info._requiredFeatures) { } //--------------------------------------------------------- // LadspaPlugin //--------------------------------------------------------- LadspaPlugin::LadspaPlugin(const QFileInfo* f, const LADSPA_Descriptor_Function /*ldf*/, const LADSPA_Descriptor* d) : Plugin(f) { SP_TRACE_IN _plugin = NULL; _label = QString(d->Label); _name = QString(d->Name); _uniqueID = d->UniqueID; _maker = QString(d->Maker); _copyright = QString(d->Copyright); _portCount = d->PortCount; for(unsigned long k = 0; k < _portCount; ++k) { LADSPA_PortDescriptor pd = d->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) { if(pd & LADSPA_PORT_INPUT) { ++_inports; } else if(pd & LADSPA_PORT_OUTPUT) { ++_outports; } } else if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) { ++_controlInPorts; } else if(pd & LADSPA_PORT_OUTPUT) { ++_controlOutPorts; } } } /*if (SP_DEBUG_LADSPA) { printf("Label: %s\tLib: %s\tPortCount: %d\n", this->label().toLatin1().constData(), this->lib().toLatin1().constData(), plugin->PortCount); printf("LADSPA_PORT_CONTROL|LADSPA_PORT_INPUT: %d\t", pIdx.size()); printf("Input ports: %d\t", iIdx.size()); printf("Output ports: %d\n\n", oIdx.size()); }*/ if ((_inports != _outports) || (LADSPA_IS_INPLACE_BROKEN(d->Properties))) _requiredFeatures |= MusECore::PluginNoInPlaceProcessing; SP_TRACE_OUT } LadspaPlugin::LadspaPlugin(const MusEPlugin::PluginScanInfoStruct& info) : Plugin(info), _plugin(NULL) { SP_TRACE_IN SP_TRACE_OUT } //--------------------------------------------------------- // createPluginI //--------------------------------------------------------- PluginI* LadspaPlugin::createPluginI(int chans, float sampleRate, unsigned int segmentSize, bool useDenormalBias, float denormalBias) { LadspaPluginI* plug_i = new LadspaPluginI(); if(plug_i->initPluginInstance( this, chans, sampleRate, segmentSize, useDenormalBias, denormalBias)) { fprintf(stderr, "LadspaPlugin::createPluginI: cannot instantiate plugin <%s>\n", name().toLatin1().constData()); // Make sure to delete the PluginI. delete plug_i; return 0; } return plug_i; } //--------------------------------------------------------- // incReferences //--------------------------------------------------------- int LadspaPlugin::incReferences(int val) { #ifdef PLUGIN_DEBUGIN fprintf(stderr, "LadspaPlugin::incReferences _references:%d val:%d\n", _references, val); #endif int newref = _references + val; if(newref <= 0) { _references = 0; if(_libHandle) { #ifdef PLUGIN_DEBUGIN fprintf(stderr, "LadspaPlugin::incReferences no more instances, closing library\n"); #endif dlclose(_libHandle); } _libHandle = 0; _plugin = NULL; _pIdx.clear(); _poIdx.clear(); _iIdx.clear(); _oIdx.clear(); _requiredFeatures = MusECore::PluginNoFeatures; return 0; } if(_libHandle == 0) { _libHandle = dlopen(_fi.filePath().toLatin1().constData(), RTLD_NOW); if(_libHandle == 0) { fprintf(stderr, "LadspaPlugin::incReferences dlopen(%s) failed: %s\n", _fi.filePath().toLatin1().constData(), dlerror()); return 0; } LADSPA_Descriptor_Function ladspadf = (LADSPA_Descriptor_Function)dlsym(_libHandle, "ladspa_descriptor"); if(ladspadf) { const LADSPA_Descriptor* descr; for(unsigned long i = 0;; ++i) { descr = ladspadf(i); if(descr == NULL) break; QString desc_label(descr->Label); if(desc_label == label()) { _plugin = descr; break; } } } if(_plugin != NULL) { _uniqueID = _plugin->UniqueID; _label = QString(_plugin->Label); _name = QString(_plugin->Name); _maker = QString(_plugin->Maker); _copyright = QString(_plugin->Copyright); _portCount = _plugin->PortCount; _inports = 0; _outports = 0; _controlInPorts = 0; _controlOutPorts = 0; for(unsigned long k = 0; k < _portCount; ++k) { LADSPA_PortDescriptor pd = _plugin->PortDescriptors[k]; if(pd & LADSPA_PORT_AUDIO) { if(pd & LADSPA_PORT_INPUT) { ++_inports; _iIdx.push_back(k); } else if(pd & LADSPA_PORT_OUTPUT) { ++_outports; _oIdx.push_back(k); } } else if(pd & LADSPA_PORT_CONTROL) { if(pd & LADSPA_PORT_INPUT) { ++_controlInPorts; _pIdx.push_back(k); } else if(pd & LADSPA_PORT_OUTPUT) { ++_controlOutPorts; _poIdx.push_back(k); } } } } } if(_plugin == NULL) { dlclose(_libHandle); _libHandle = 0; _references = 0; fprintf(stderr, "LadspaPlugin::incReferences Error: %s no plugin!\n", _fi.filePath().toLatin1().constData()); return 0; } if ((_inports != _outports) || (LADSPA_IS_INPLACE_BROKEN(_plugin->Properties))) _requiredFeatures |= MusECore::PluginNoInPlaceProcessing; _references = newref; return _references; } //--------------------------------------------------------- // instantiate //--------------------------------------------------------- void* LadspaPlugin::instantiate(float sampleRate, void*) { if(!_plugin) return NULL; bool success = false; LADSPA_Handle h = _plugin->instantiate(_plugin, sampleRate); success = (h != NULL); if(success) { SP_DBG_LADSPA2("LadspaPlugin instantiated", label().toLatin1().constData()); } return h; } //--------------------------------------------------------- // range //--------------------------------------------------------- bool LadspaPlugin::port_range(unsigned long i, float sampleRate, float* min, float* max) const { if(!_plugin) return false; LADSPA_PortRangeHint range = _plugin->PortRangeHints[i]; LADSPA_PortRangeHintDescriptor desc = range.HintDescriptor; if (desc & LADSPA_HINT_TOGGLED) { *min = 0.0; *max = 1.0; return true; } float m = 1.0; if (desc & LADSPA_HINT_SAMPLE_RATE) m = sampleRate; if (desc & LADSPA_HINT_BOUNDED_BELOW) *min = range.LowerBound * m; else *min = 0.0; if (desc & LADSPA_HINT_BOUNDED_ABOVE) *max = range.UpperBound * m; else *max = 1.0; return true; } //--------------------------------------------------------- // range //--------------------------------------------------------- bool LadspaPlugin::range(unsigned long k, float sampleRate, float* min, float* max) const { SP_TRACE_IN k = _pIdx[k]; SP_TRACE_OUT return port_range(k, sampleRate, min, max); } //--------------------------------------------------------- // range //--------------------------------------------------------- bool LadspaPlugin::rangeOut(unsigned long k, float sampleRate, float* min, float* max) const { SP_TRACE_IN k = _poIdx[k]; SP_TRACE_OUT return port_range(k, sampleRate, min, max); } //--------------------------------------------------------- // defaultValue //--------------------------------------------------------- float LadspaPlugin::defaultValue(unsigned long k) const { SP_TRACE_IN if(!_plugin) return 0.0f; k = _pIdx[k]; LADSPA_PortRangeHint range = _plugin->PortRangeHints[k]; LADSPA_PortRangeHintDescriptor rh = range.HintDescriptor; LADSPA_Data val = 1.0; if (LADSPA_IS_HINT_DEFAULT_MINIMUM(rh)) val = range.LowerBound; else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(rh)) val = range.UpperBound; else if (LADSPA_IS_HINT_DEFAULT_LOW(rh)) if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) val = exp(log(range.LowerBound) * .75 + log(range.UpperBound) * .25); else val = range.LowerBound*.75 + range.UpperBound*.25; else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(rh)) if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) val = exp(log(range.LowerBound) * .5 + log(range.UpperBound) * .5); else val = range.LowerBound*.5 + range.UpperBound*.5; else if (LADSPA_IS_HINT_DEFAULT_HIGH(rh)) if (LADSPA_IS_HINT_LOGARITHMIC(range.HintDescriptor)) val = exp(log(range.LowerBound) * .25 + log(range.UpperBound) * .75); else val = range.LowerBound*.25 + range.UpperBound*.75; else if (LADSPA_IS_HINT_DEFAULT_0(rh)) val = 0.0; else if (LADSPA_IS_HINT_DEFAULT_1(rh)) val = 1.0; else if (LADSPA_IS_HINT_DEFAULT_100(rh)) val = 100.0; else if (LADSPA_IS_HINT_DEFAULT_440(rh)) val = 440.0; // No default found. Make one up... else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh) && LADSPA_IS_HINT_BOUNDED_ABOVE(rh)) { if (LADSPA_IS_HINT_LOGARITHMIC(rh)) val = exp(log(range.LowerBound) * .5 + log(range.UpperBound) * .5); else val = range.LowerBound*.5 + range.UpperBound*.5; } else if (LADSPA_IS_HINT_BOUNDED_BELOW(rh)) val = range.LowerBound; else if (LADSPA_IS_HINT_BOUNDED_ABOVE(rh)) { // Hm. What to do here... Just try 0.0 or the upper bound if less than zero. //if(range.UpperBound > 0.0) // val = 0.0; //else // val = range.UpperBound; // Instead try this: Adopt an 'attenuator-like' policy, where upper is the default. val = range.UpperBound; return true; } SP_TRACE_OUT return val; } //--------------------------------------------------------- // connectInport //--------------------------------------------------------- void LadspaPlugin::connectInport(void* handle, unsigned long k, void* datalocation) { SP_TRACE_IN if(!_plugin) return; _plugin->connect_port((LADSPA_Handle)handle, _iIdx[k], (LADSPA_Data*)datalocation); SP_TRACE_OUT } //--------------------------------------------------------- // connectOutport //--------------------------------------------------------- void LadspaPlugin::connectOutport(void* handle, unsigned long k, void* datalocation) { SP_TRACE_IN if(!_plugin) return; _plugin->connect_port((LADSPA_Handle)handle, _oIdx[k], (LADSPA_Data*)datalocation); SP_TRACE_OUT } //--------------------------------------------------------- // connectCtrlInport //--------------------------------------------------------- void LadspaPlugin::connectCtrlInport(void* handle, unsigned long k, void* datalocation) { SP_TRACE_IN if(!_plugin) return; _plugin->connect_port((LADSPA_Handle)handle, _pIdx[k], (LADSPA_Data*)datalocation); SP_TRACE_OUT } //--------------------------------------------------------- // connectCtrlOutport //--------------------------------------------------------- void LadspaPlugin::connectCtrlOutport(void* handle, unsigned long k, void* datalocation) { SP_TRACE_IN if(!_plugin) return; _plugin->connect_port((LADSPA_Handle)handle, _poIdx[k], (LADSPA_Data*)datalocation); SP_TRACE_OUT } //--------------------------------------------------------- // convertGuiControlValue // scale control value to gui-slider/checkbox representation //--------------------------------------------------------- float LadspaPlugin::convertGuiControlValue(unsigned long k, float sampleRate, int val) const { SP_TRACE_IN float floatval = 0.0; float min, max; if(!range(k, sampleRate, &min, &max)) return floatval; if (isLog(k)) { if (val > 0) { float logged = SS_map_pluginparam2logdomain(val); float e = expf(logged) * (max - min); e+=min; floatval = e; } } else if (isBool(k)) { floatval = (float) val; } else if (isInt(k)) { float scale = (max - min) / SS_PLUGIN_PARAM_MAX; floatval = (float) round((((float) val) * scale) + min); } else { float scale = (max - min) / SS_PLUGIN_PARAM_MAX; floatval = (((float) val) * scale) + min; } SP_TRACE_OUT return floatval; } //--------------------------------------------------------- // PluginI //--------------------------------------------------------- void PluginI::init() { _plugin = 0; _sampleRate = 44100.0f; _dSampleRate = _sampleRate; _segmentSize = 0; _instances = 0; _controls = 0; _controlsOut = 0; _controlsOutDummy = 0; _audioInPorts = 0; _audioOutPorts = 0; _controlPorts = 0; _controlOutPorts = 0; _audioInSilenceBuf = 0; _audioOutDummyBuf = 0; _hasLatencyOutPort = false; _latencyOutPort = 0; _on = true; } PluginI::PluginI() { _id = -1; init(); } //--------------------------------------------------------- // PluginI //--------------------------------------------------------- PluginI::~PluginI() { if(_audioInSilenceBuf) free(_audioInSilenceBuf); if(_audioOutDummyBuf) free(_audioOutDummyBuf); if(_controlsOutDummy) delete[] _controlsOutDummy; if(_controlsOut) delete[] _controlsOut; if(_controls) delete[] _controls; } //--------------------------------------------------------- // setID //--------------------------------------------------------- void PluginI::setID(int i) { _id = i; } //--------------------------------------------------------- // getGuiControlValue // scale control value to gui-slider/checkbox representation //--------------------------------------------------------- int PluginI::getGuiControlValue(unsigned long parameter) const { SP_TRACE_IN float val = param(parameter); float min, max; range(parameter, &min, &max); int intval; if (isLog(parameter)) { intval = SS_map_logdomain2pluginparam(logf(val/(max - min) + min)); } else if (isBool(parameter)) { intval = (int) val; } else { float scale = SS_PLUGIN_PARAM_MAX / (max - min); intval = (int) ((val - min) * scale); } SP_TRACE_OUT return intval; } //--------------------------------------------------------- // convertGuiControlValue // scale control value to gui-slider/checkbox representation //--------------------------------------------------------- float PluginI::convertGuiControlValue(unsigned long parameter, int val) const { SP_TRACE_IN float floatval = 0; float min, max; range(parameter, &min, &max); if (isLog(parameter)) { if (val > 0) { float logged = SS_map_pluginparam2logdomain(val); float e = expf(logged) * (max - min); e+=min; floatval = e; } } else if (isBool(parameter)) { floatval = (float) val; } else if (isInt(parameter)) { float scale = (max - min) / SS_PLUGIN_PARAM_MAX; floatval = (float) round((((float) val) * scale) + min); } else { float scale = (max - min) / SS_PLUGIN_PARAM_MAX; floatval = (((float) val) * scale) + min; } SP_TRACE_OUT return floatval; } //--------------------------------------------------------- // apply // If ports is 0, just process controllers only, not audio (do not 'run'). //--------------------------------------------------------- void PluginI::apply(unsigned /*pos*/, unsigned long frames, unsigned long ports, float** bufIn, float** bufOut) { #ifdef PLUGIN_DEBUGIN_PROCESS fprintf(stderr, "PluginI::apply nsamp:%lu\n", n); #endif if(!_plugin) return; if(ports > 0) // Don't bother if not 'running'. { connect(ports, 0, bufIn, bufOut); process(frames); } } //--------------------------------------------------------- // start // activate and connect control ports //--------------------------------------------------------- bool PluginI::start() { if(!_plugin) return false; // Activate all the instances. return activate(); } //--------------------------------------------------------- // stop // deactivate //--------------------------------------------------------- bool PluginI::stop() { if(!_plugin) return false; // Activate all the instances. return deactivate(); } //--------------------------------------------------------- // setParam //--------------------------------------------------------- void PluginI::setParam(unsigned long i, float val) { if(i >= _controlPorts) return; _controls[i]._val = val; } // TODO // //--------------------------------------------------------- // // connect // //--------------------------------------------------------- // // void PluginI::connectInport(unsigned long k, void* datalocation) // { // if(!_plugin) return; // // const unsigned long ins = _plugin->inports(); // // const unsigned long handle_idx = (k / ins) % instances; // // const unsigned long port_idx = k % ins; // // _plugin->connectInport(handle[handle_idx], port_idx, datalocation); // // const unsigned long ins = _plugin->inports(); // for (int i = 0; i < instances; ++i) { // for (unsigned long k = 0; k < audio; ++k) { // } // // const unsigned long port_count = _plugin->portCount(); // unsigned long port = 0; // for (int i = 0; i < instances; ++i) { // for (unsigned long k = 0; k < port_count; ++k) { // if (_plugin->isAudioIn(k)) { // //if(port < ports) // if(port < channel) // //_plugin->connectPort(handle[i], k, src[port] + offset); // _plugin->connectInport(handle[i], k, datalocation); // else // // Connect to an input silence buffer. // _plugin->connectInport(handle[i], k, _audioInSilenceBuf); // ++port; // } // } // } // } // // void PluginI::connectOutport(unsigned long k, void* datalocation) // { // if(!_plugin) return; // // const unsigned long outs = _plugin->outports(); // // const unsigned long handle_idx = (k / outs) % instances; // // const unsigned long port_idx = k % outs; // // _plugin->connectOutport(handle[handle_idx], port_idx, datalocation); // // const unsigned long port_count = _plugin->portCount(); // unsigned long port = 0; // for (int i = 0; i < instances; ++i) { // for (unsigned long k = 0; k < port_count; ++k) { // if (_plugin->isAudioOut(k)) { // //if(port < ports) // if(port < channel) // _plugin->connectOutport(handle[i], k, datalocation); // else // // Connect to a dummy buffer. // _plugin->connectOutport(handle[i], k, _audioOutDummyBuf); // ++port; // } // } // } // } // // void PluginI::connectCtrlInport(unsigned long k, void* datalocation) // { // if(!_plugin) return; // const unsigned long ctrl_ins = _plugin->parameter(); // const unsigned long handle_idx = (k / ctrl_ins) % instances; // const unsigned long port_idx = k % ctrl_ins; // _plugin->connectCtrlInport(handle[handle_idx], port_idx, datalocation); // } // // void PluginI::connectCtrlOutport(unsigned long k, void* datalocation) // { // if(!_plugin) return; // const unsigned long ctrl_outs = _plugin->parameterOut(); // const unsigned long handle_idx = (k / ctrl_outs) % instances; // const unsigned long port_idx = k % ctrl_outs; // _plugin->connectCtrlOutport(handle[handle_idx], port_idx, datalocation); // } //--------------------------------------------------------- // latency //--------------------------------------------------------- float PluginI::latency() const { if(!_hasLatencyOutPort) return 0.0; return _controlsOut[_latencyOutPort]._val; } //--------------------------------------------------------- // setControl // set plugin instance controller value by name //--------------------------------------------------------- bool PluginI::setControl(const QString& s, float val) { if(!_plugin) return true; for (unsigned long i = 0; i < _controlPorts; ++i) { if (QString(_plugin->getParameterName(i)) == s) { setParam(i, val); return false; } } fprintf(stderr, "PluginI:setControl(%s, %f) controller not found\n", s.toLatin1().constData(), val); return true; } //--------------------------------------------------------- // LadspaPluginI //--------------------------------------------------------- void LadspaPluginI::init() { _handle = 0; } LadspaPluginI::LadspaPluginI() : PluginI() { init(); } //--------------------------------------------------------- // LadspaPluginI //--------------------------------------------------------- LadspaPluginI::~LadspaPluginI() { if(_plugin) { // Deactivate is pure virtual, it cannot be // called from the base destructor. Do it here. deactivate(); _plugin->incReferences(-1); } if(_handle) delete[] _handle; } //--------------------------------------------------------- // initPluginInstance // return true on error //--------------------------------------------------------- bool LadspaPluginI::initPluginInstance(Plugin* plug, int chans, float sampleRate, unsigned int segmentSize, bool useDenormalBias, float denormalBias) { _sampleRate = _dSampleRate = sampleRate; _segmentSize = segmentSize; _channel = chans; if(plug == 0) { fprintf(stderr, "LadspaPluginI::initPluginInstance: zero plugin\n"); return true; } _plugin = plug; if (_plugin->incReferences(1)==0) return true; QString inst("-" + QString::number(_plugin->instNo())); _name = _plugin->name() + inst; _label = _plugin->label() + inst; const unsigned long ins = _plugin->inports(); const unsigned long outs = _plugin->outports(); if(outs) { _instances = _channel / outs; // Ask for one more instance for remainder if required. const int re = _channel % outs; if(re != 0) ++_instances; if(_instances < 1) _instances = 1; } else if(ins) { _instances = _channel / ins; // Ask for one more instance for remainder if required. const int re = _channel % ins; if(re != 0) ++_instances; if(_instances < 1) _instances = 1; } else _instances = 1; _handle = new LADSPA_Handle[_instances]; for(int i = 0; i < _instances; ++i) _handle[i]=NULL; for(int i = 0; i < _instances; ++i) { #ifdef PLUGIN_DEBUGIN fprintf(stderr, "LadspaPluginI::initPluginInstance instance:%d\n", i); #endif _handle[i] = _plugin->instantiate(_sampleRate, NULL); if(_handle[i] == NULL) return true; } const unsigned long port_count = _plugin->portCount(); _audioInPorts = 0; _audioOutPorts = 0; _controlPorts = 0; _controlOutPorts = 0; unsigned long port = 0; for (int i = 0; i < _instances; ++i) { for (unsigned long k = 0; k < port_count; ++k) { if (_plugin->isAudioIn(k)) { if(port < (unsigned long)_channel) ++_audioInPorts; ++port; } } } port = 0; for (int i = 0; i < _instances; ++i) { for (unsigned long k = 0; k < port_count; ++k) { if (_plugin->isAudioOut(k)) { if(port < (unsigned long)_channel) ++_audioOutPorts; ++port; } } } for(unsigned long k = 0; k < port_count; ++k) { if(_plugin->isParameterIn(k)) ++_controlPorts; else if(_plugin->isParameterOut(k)) ++_controlOutPorts; } if(_controlPorts) _controls = new Port[_controlPorts]; if(_controlOutPorts) { _controlsOut = new Port[_controlOutPorts]; _controlsOutDummy = new Port[_controlOutPorts]; } for(unsigned long k = 0; k < _controlPorts; ++k) { // Set the parameter input's initial value to the default. const float val = _plugin->defaultValue(k); _controls[k]._val = val; // All instances' parameter inputs share the same controls. // We don't have a mechanism to expose the other instances' inputs. for(int i = 0; i < _instances; ++i) _plugin->connectCtrlInport(_handle[i], k, &_controls[k]._val); } for(unsigned long k = 0; k < _controlOutPorts; ++k) { // Set the parameter output's initial value to zero. _controlsOut[k]._val = 0.0f; // Check for a latency port. const char* pname = _plugin->getParameterOutName(k); if(pname == QString("latency") || pname == QString("_latency")) { _hasLatencyOutPort = true; _latencyOutPort = k; } // Connect only the first instance's parameter output controls. // We don't have a mechanism to display the other instances' outputs. if(_instances > 0) { _plugin->connectCtrlOutport(_handle[0], k, &_controlsOut[k]._val); // Connect the rest to dummy ports. for(int i = 1; i < _instances; ++i) _plugin->connectCtrlOutport(_handle[i], k, &_controlsOutDummy[k]._val); } } #ifdef _WIN32 _audioInSilenceBuf = (float *) _aligned_malloc(16, sizeof(float) * _segmentSize); if(_audioInSilenceBuf == NULL) { fprintf(stderr, "ERROR: LadspaPluginI::initPluginInstance: _audioInSilenceBuf _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * _segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: LadspaPluginI::initPluginInstance: _audioInSilenceBuf posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif if(useDenormalBias) { for(unsigned q = 0; q < _segmentSize; ++q) { _audioInSilenceBuf[q] = denormalBias; } } else { memset(_audioInSilenceBuf, 0, sizeof(float) * _segmentSize); } #ifdef _WIN32 _audioOutDummyBuf = (float *) _aligned_malloc(16, sizeof(float) * _segmentSize); if(_audioOutDummyBuf == NULL) { fprintf(stderr, "ERROR: LadspaPluginI::initPluginInstance: _audioInSilenceBuf _aligned_malloc returned error: NULL. Aborting!\n"); abort(); } #else rv = posix_memalign((void **)&_audioOutDummyBuf, 16, sizeof(float) * _segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: LadspaPluginI::initPluginInstance: _audioOutDummyBuf posix_memalign returned error:%d. Aborting!\n", rv); abort(); } #endif // Don't activate yet. //activate(); return false; } //--------------------------------------------------------- // setChannels //--------------------------------------------------------- void LadspaPluginI::setChannels(int chans) { _channel = chans; if(!_plugin) return; const unsigned long ins = _plugin->inports(); const unsigned long outs = _plugin->outports(); int ni = 1; if(outs) { ni = chans / outs; // Ask for one more instance for remainder if required. const int re = chans % outs; if(re != 0) ++ni; } else if(ins) { ni = chans / ins; // Ask for one more instance for remainder if required. const int re = chans % ins; if(re != 0) ++ni; } if(ni < 1) ni = 1; if (ni == _instances) return; LADSPA_Handle* handles = new LADSPA_Handle[ni]; if(ni > _instances) { for(int i = 0; i < ni; ++i) { if(i < _instances) // Transfer existing handle from old array to new array. handles[i] = _handle[i]; else { // Create a new plugin instance with handle. // Use the plugin's current sample rate. handles[i] = _plugin->instantiate(_sampleRate, NULL); if(handles[i] == NULL) { fprintf(stderr, "LadspaPluginI::setChannels: cannot instantiate instance %d\n", i); // Although this is a messed up state not easy to get out of (final # of channels?), try not to assert(). // Whoever uses these will have to check instance count or null handle, and try to gracefully fix it and allow a song save. for(int k = i; k < ni; ++k) handles[i] = NULL; ni = i + 1; //channel = ?; break; } } } } else { for(int i = 0; i < _instances; ++i) { if(i < ni) // Transfer existing handle from old array to new array. handles[i] = _handle[i]; else { // Delete existing plugin instance. // Previously we deleted all the instances and rebuilt from scratch. // One side effect of this: Since a GUI is constructed only on the first handle, // previously the native GUI would close when changing channels. Now it doesn't, which is good. _plugin->deactivate(_handle[i]); _plugin->cleanup(_handle[i]); } } } // Delete the old array, and set the new array. delete[] _handle; _handle = handles; // Connect new instances' ports: for(unsigned long k = 0; k < _controlPorts; ++k) { for(int i = _instances; i < ni; ++i) { // All instances' parameter inputs share the same controls. // We don't have a mechanism to expose the other instances' inputs. _plugin->connectCtrlInport(handles[i], k, &_controls[k]._val); } } for(unsigned long k = 0; k < _controlOutPorts; ++k) { // Connect only the first instance's parameter output controls. // We don't have a mechanism to display the other instances' outputs. if(_instances == 0 && ni > 0) // Only if the existing instances was zero. We are creating one(s) now. _plugin->connectCtrlOutport(_handle[0], k, &_controlsOut[k]._val); else { // Connect the rest to dummy ports. for(int i = _instances; i < ni; ++i) _plugin->connectCtrlOutport(_handle[i], k, &_controlsOutDummy[k]._val); } } // Activate new instances. for(int i = _instances; i < ni; ++i) _plugin->activate(_handle[i]); // Finally, set the new number of instances. _instances = ni; } //--------------------------------------------------------- // connect //--------------------------------------------------------- void LadspaPluginI::connect(unsigned long ports, unsigned long offset, float** src, float** dst) { if(!_plugin) return; const unsigned long port_count = _plugin->portCount(); unsigned long port = 0; for (int i = 0; i < _instances; ++i) { for (unsigned long k = 0; k < port_count; ++k) { if (isAudioIn(k)) { if(port < ports) _plugin->connectPort(_handle[i], k, src[port] + offset); else // Connect to an input silence buffer. _plugin->connectPort(_handle[i], k, _audioInSilenceBuf + offset); ++port; } } } port = 0; for (int i = 0; i < _instances; ++i) { for (unsigned long k = 0; k < port_count; ++k) { if (isAudioOut(k)) { if(port < ports) _plugin->connectPort(_handle[i], k, dst[port] + offset); else // Connect to a dummy buffer. _plugin->connectPort(_handle[i], k, _audioOutDummyBuf + offset); ++port; } } } } //--------------------------------------------------------- // deactivate //--------------------------------------------------------- bool LadspaPluginI::deactivate() { if(!_plugin) return false; for (int i = 0; i < _instances; ++i) { _plugin->deactivate(_handle[i]); _plugin->cleanup(_handle[i]); } return true; } //--------------------------------------------------------- // activate //--------------------------------------------------------- bool LadspaPluginI::activate() { if(!_plugin) return false; for (int i = 0; i < _instances; ++i) _plugin->activate(_handle[i]); return true; } void LadspaPluginI::process(unsigned long frames) { if(!_plugin) return; for(int i = 0; i < _instances; ++i) _plugin->apply(_handle[i], frames); } } // namespace MusESimplePlugin muse-4.0.0/src/synti/libsimpleplugin/simpler_plugin.h000066400000000000000000000526171404105311000230220ustar00rootroot00000000000000// // C++ Interface: plugin // // Description: // // // (C) Copyright 2000 Werner Schweer (ws@seh.de) // Additions/modifications: Mathias Lundgren , (C) 2004 // (C) Copyright 2011 Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // #ifndef __SIMPLER_PLUGIN_H__ #define __SIMPLER_PLUGIN_H__ #include #include #include #include #include "muse_math.h" #include "globaldefs.h" #include "plugin_scan.h" #define SS_PLUGIN_PARAM_MIN 0 #define SS_PLUGIN_PARAM_MAX 127 namespace MusESimplePlugin { //--------------------------------------------------------- // Port //--------------------------------------------------------- struct Port { float _val; }; //--------------------------------------------------------- // Plugin base class //--------------------------------------------------------- class PluginI; class Plugin { protected: QFileInfo _fi; void* _libHandle; int _references; int _instNo; unsigned long _uniqueID; QString _label; QString _name; QString _maker; QString _copyright; // Total number of ports. unsigned long _portCount; unsigned long _inports; unsigned long _outports; unsigned long _controlInPorts; unsigned long _controlOutPorts; MusECore::PluginFeatures_t _requiredFeatures; std::vector _pIdx; //control port numbers std::vector _poIdx; //control out port numbers std::vector _iIdx; //input port numbers std::vector _oIdx; //output port numbers public: Plugin(const QFileInfo* f) : _fi(*f), _libHandle(0), _references(0), _instNo(0), _uniqueID(0), _portCount(0),_inports(0), _outports(0), _controlInPorts(0),_controlOutPorts(0), _requiredFeatures(MusECore::PluginNoFeatures) { } Plugin(const MusEPlugin::PluginScanInfoStruct& info); virtual ~Plugin() {} //---------------------------------------------------- // The following methods can be called regardless of // whether the libray is open or not, ie. zero or more // instances exist. The information is cached. //---------------------------------------------------- // Returns features required by the plugin. MusECore::PluginFeatures_t requiredFeatures() const { return _requiredFeatures; } // Create and initialize a plugin instance. Returns null if failure. // Equivalent to calling (new ***PlugI())->initPluginInstance(this, ...). // The returned type depends on the derived class (LadspaPluginI*, Lv2PluginI*, etc). // Caller is responsible for deleting the returned object. virtual PluginI* createPluginI(int chans, float sampleRate, unsigned int segmentSize, bool useDenormalBias, float denormalBias) = 0; int references() const { return _references; } virtual int incReferences(int) { return _references; } int instNo() { return _instNo++; } virtual void* instantiate(float /*sampleRate*/, void* /*data*/) { return 0; } QString label() const { return _label; } QString name() const { return _name; } unsigned long id() const { return _uniqueID; } QString maker() const { return _maker; } QString copyright() const { return _copyright; } QString lib(bool complete = true) const { return complete ? _fi.completeBaseName() : _fi.baseName(); } QString dirPath(bool complete = true) const { return complete ? _fi.absolutePath() : _fi.path(); } QString filePath() const { return _fi.filePath(); } QString fileName() const { return _fi.fileName(); } // Total number of ports. unsigned long portCount() const { return _portCount; } unsigned long parameter() const { return _controlInPorts; } unsigned long parameterOut() const { return _controlOutPorts; } unsigned long inports() const { return _inports; } unsigned long outports() const { return _outports; } bool inPlaceCapable() const { return !(_requiredFeatures & MusECore::PluginNoInPlaceProcessing); } //---------------------------------------------------- // The following methods require the library be open, // ie. at least one instance exists. //---------------------------------------------------- virtual bool isAudioIn(unsigned long) const { return false; } virtual bool isAudioOut(unsigned long) const { return false; } virtual bool isParameterIn(unsigned long) const { return false; } virtual bool isParameterOut(unsigned long) const { return false; } virtual bool isLog(unsigned long) const { return false; } virtual bool isBool(unsigned long) const { return false; } virtual bool isInt(unsigned long) const { return false; } virtual bool isLinear(unsigned long) const { return false; } virtual float defaultValue(unsigned long) const { return 0.0f; } virtual bool range(unsigned long, float /*sampleRate*/, float* min, float* max) const = 0; virtual bool rangeOut(unsigned long, float /*sampleRate*/, float* min, float* max) const = 0; virtual const char* getParameterName(unsigned long /*param*/) const { return ""; } virtual const char* getParameterOutName(unsigned long /*param*/) const { return ""; } virtual void activate(void* /*handle*/) { } virtual void deactivate(void* /*handle*/) { } virtual void cleanup(void* /*handle*/) { } virtual void connectInport(void* /*handle*/, unsigned long, void* /*datalocation*/) { } virtual void connectOutport(void* /*handle*/, unsigned long, void* /*datalocation*/) { } virtual void connectCtrlInport(void* /*handle*/, unsigned long, void* /*datalocation*/) { } virtual void connectCtrlOutport(void* /*handle*/, unsigned long, void* /*datalocation*/) { } virtual void connectPort(void* /*handle*/, unsigned long /*port*/, float* /*value*/) { } virtual void apply(void* /*handle*/, unsigned long /*n*/) { } }; //--------------------------------------------------------- // LadspaPlugin //--------------------------------------------------------- #define IS_AUDIO_IN (LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT) #define IS_AUDIO_OUT (LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT) #define IS_PARAMETER_IN (LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT) #define IS_PARAMETER_OUT (LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT) class LadspaPlugin : public Plugin { private: const LADSPA_Descriptor* _plugin; // Accepts a master port index. bool port_range(unsigned long k, float sampleRate, float* min, float* max) const; public: LadspaPlugin(const QFileInfo* f, const LADSPA_Descriptor_Function, const LADSPA_Descriptor* d); LadspaPlugin(const MusEPlugin::PluginScanInfoStruct& info); virtual ~LadspaPlugin() { } // Create and initialize a LADSPA plugin instance. Returns null if failure. // Equivalent to calling (new LadspaPlugI())->initPluginInstance(this, ...). // The returned type depends on the this class (LadspaPluginI*, Lv2PluginI*, etc). // Caller is responsible for deleting the returned object. PluginI* createPluginI(int chans, float sampleRate, unsigned int segmentSize, bool useDenormalBias, float denormalBias); int incReferences(int); bool isAudioIn(unsigned long k) const { if(!_plugin) return false; return (_plugin->PortDescriptors[k] & IS_AUDIO_IN) == IS_AUDIO_IN; } bool isAudioOut(unsigned long k) const { if(!_plugin) return false; return (_plugin->PortDescriptors[k] & IS_AUDIO_OUT) == IS_AUDIO_OUT; } bool isParameterIn(unsigned long k) const { if(!_plugin) return false; return (_plugin->PortDescriptors[k] & IS_PARAMETER_IN) == IS_PARAMETER_IN; } bool isParameterOut(unsigned long k) const { if(!_plugin) return false; return (_plugin->PortDescriptors[k] & IS_PARAMETER_OUT) == IS_PARAMETER_OUT; } bool isLog(unsigned long k) const { if(!_plugin) return false; LADSPA_PortRangeHint r = _plugin->PortRangeHints[_pIdx[k]]; return LADSPA_IS_HINT_LOGARITHMIC(r.HintDescriptor); } bool isBool(unsigned long k) const { if(!_plugin) return false; return LADSPA_IS_HINT_TOGGLED(_plugin->PortRangeHints[_pIdx[k]].HintDescriptor); } bool isInt(unsigned long k) const { if(!_plugin) return false; LADSPA_PortRangeHint r = _plugin->PortRangeHints[_pIdx[k]]; return LADSPA_IS_HINT_INTEGER(r.HintDescriptor); } bool isLinear(unsigned long k) const { if(!_plugin) return false; LADSPA_PortRangeHint r = _plugin->PortRangeHints[_pIdx[k]]; return !LADSPA_IS_HINT_INTEGER(r.HintDescriptor) && !LADSPA_IS_HINT_LOGARITHMIC(r.HintDescriptor) && !LADSPA_IS_HINT_TOGGLED(r.HintDescriptor); } bool range(unsigned long k, float sampleRate, float*, float*) const; bool rangeOut(unsigned long k, float sampleRate, float*, float*) const; const char* getParameterName(unsigned long k) const { if(!_plugin) return 0; return _plugin->PortNames[_pIdx[k]]; } const char* getParameterOutName(unsigned long k) const { if(!_plugin) return 0; return _plugin->PortNames[_poIdx[k]]; } float defaultValue(unsigned long k) const; float convertGuiControlValue(unsigned long k, float sampleRate, int val) const; void* instantiate(float sampleRate, void* /*data*/); void connectInport(void* handle, unsigned long k, void* datalocation); void connectOutport(void* handle, unsigned long k, void* datalocation); void connectCtrlInport(void* handle, unsigned long k, void* datalocation); void connectCtrlOutport(void* handle, unsigned long k, void* datalocation); void activate(void* handle) { if (_plugin && _plugin->activate) _plugin->activate((LADSPA_Handle)handle); } void deactivate(void* handle) { if (_plugin && _plugin->deactivate) _plugin->deactivate((LADSPA_Handle)handle); } void cleanup(void* handle) { if (_plugin && _plugin->cleanup) _plugin->cleanup((LADSPA_Handle)handle); } void connectPort(void* handle, unsigned long port, float* datalocation) { if(_plugin) _plugin->connect_port((LADSPA_Handle)handle, port, datalocation); } void apply(void* handle, unsigned long n) { if(_plugin && _plugin->run) _plugin->run((LADSPA_Handle)handle, n); } }; //-------------------------------- // PluginI // Plugin Instance base class //-------------------------------- class PluginI { private: void init(); protected: Plugin* _plugin; // Some APIs may need to point to a float or double samplerate, or both (LV2). float _sampleRate; double _dSampleRate; unsigned int _segmentSize; int _channel; int _instances; int _id; Port* _controls; Port* _controlsOut; Port* _controlsOutDummy; unsigned long _audioInPorts; unsigned long _audioOutPorts; unsigned long _controlPorts; unsigned long _controlOutPorts; bool _hasLatencyOutPort; unsigned long _latencyOutPort; float *_audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. float *_audioOutDummyBuf; // A place to connect unused outputs. bool _on; QString _name; QString _label; public: PluginI(); virtual ~PluginI(); Plugin* plugin() const { return _plugin; } MusECore::PluginFeatures_t requiredFeatures() const { if(!_plugin) return MusECore::PluginNoFeatures; return _plugin->requiredFeatures(); } bool on() const { return _on; } void setOn(bool val) { _on = val; } unsigned long pluginID() { return _plugin->id(); } void setID(int i); int id() { return _id; } bool inPlaceCapable() const { if(!_plugin) return false; return _plugin->inPlaceCapable(); } // Returns true on error. virtual bool initPluginInstance(Plugin* plug, int channels, float sampleRate, unsigned int segmentSize, bool useDenormalBias, float denormalBias) = 0; float sampleRate() const { return _sampleRate; } void setSampleRate(float rate) { _sampleRate = rate; _dSampleRate = rate; } unsigned int segmentSize() const { return _segmentSize; } int channels() const { return _channel; } virtual void setChannels(int chans) = 0; // Runs the plugin for frames. Any ports involved must already be connected. virtual void process(unsigned long frames) = 0; // Runs the plugin for frames. This automatically connects the given // data locations to the ports each time it is called. void apply(unsigned pos, unsigned long frames, unsigned long ports, float** bufIn, float** bufOut); // Connects ports with data locations. Multiple sources and destinations, with offset. virtual void connect(unsigned long ports, unsigned long offset, float** src, float** dst) = 0; // // Connects a single audio input port to a data location. // void connectInport(unsigned long k, void* datalocation); // // Connects a single audio output port to a data location. // void connectOutport(unsigned long k, void* datalocation); // // Connects a single control parameter input port to a data location. // void connectCtrlInport(unsigned long k, void* datalocation); // // Connects a single control parameter output port to a data location. // void connectCtrlOutport(unsigned long k, void* datalocation); // Returns true on success. bool start(); // Returns true on success. bool stop(); bool isAudioIn(unsigned long k) { if(!_plugin) return false; return _plugin->isAudioIn(k); } bool isAudioOut(unsigned long k) { if(!_plugin) return false; return _plugin->isAudioOut(k); } bool isLog(unsigned long k) const { if(!_plugin) return false; return _plugin->isLog(k); } bool isBool(unsigned long k) const { if(!_plugin) return false; return _plugin->isBool(k); } bool isInt(unsigned long k) const { if(!_plugin) return false; return _plugin->isInt(k); } bool isLinear(unsigned long k) const { if(!_plugin) return false; return _plugin->isLinear(k); } void range(unsigned long i, float* min, float* max) const { if(!_plugin) return; _plugin->range(i, _sampleRate, min, max); } void rangeOut(unsigned long i, float* min, float* max) const { if(!_plugin) return; _plugin->rangeOut(i, _sampleRate, min, max); } const char* getParameterName(unsigned long i) const { if(!_plugin) return 0; return _plugin->getParameterName(i); } const char* getParameterOutName(unsigned long i) const { if(!_plugin) return 0; return _plugin->getParameterOutName(i); } float defaultValue(unsigned long i) const { if(!_plugin) return 0.0; return _plugin->defaultValue(i); } // Return true on success. virtual bool activate() = 0; // Return true on success. virtual bool deactivate() = 0; QString pluginLabel() const { return _plugin->label(); } QString label() const { return _label; } QString name() const { return _name; } QString lib() const { return _plugin->lib(); } QString dirPath() const { return _plugin->dirPath(); } QString fileName() const { return _plugin->fileName(); } bool setControl(const QString& s, float val); unsigned long inports() const { return _audioInPorts; } unsigned long outports() const { return _audioOutPorts; } unsigned long parameters() const { return _controlPorts; } unsigned long parametersOut() const { return _controlOutPorts; } void setParam(unsigned long i, float val); float param(unsigned long i) const { if(i >= _controlPorts) return 0.0; return _controls[i]._val; } float paramOut(unsigned long i) const { if(i >= _controlOutPorts) return 0.0; return _controlsOut[i]._val; } // Alias for getParameterName. const char* paramName(unsigned long i) const { return getParameterName(i); } const char* paramOutName(unsigned long i) const { return getParameterOutName(i); } float latency() const; //CtrlValueType ctrlValueType(unsigned long i) const { return _plugin->ctrlValueType(controls[i].idx); } //CtrlList::Mode ctrlMode(unsigned long i) const { return _plugin->ctrlMode(controls[i].idx); } int getGuiControlValue(unsigned long parameter) const; float convertGuiControlValue(unsigned long parameter, int val) const; }; //-------------------------------- // LadspaPluginI // Ladspa Plugin Instance class //-------------------------------- class LadspaPluginI : public PluginI { private: LADSPA_Handle* _handle; // per instance void init(); public: LadspaPluginI(); virtual ~LadspaPluginI(); // Returns true on error. bool initPluginInstance(Plugin* plug, int channels, float sampleRate, unsigned int segmentSize, bool useDenormalBias, float denormalBias); void setChannels(int chans); // Runs the plugin for frames. Any ports involved must already be connected. void process(unsigned long frames); // Connects ports with data locations. Multiple sources and destinations, with offset. void connect(unsigned long ports, unsigned long offset, float** src, float** dst); // // Connects a single audio input port to a data location. // void connectInport(unsigned long k, void* datalocation); // // Connects a single audio output port to a data location. // void connectOutport(unsigned long k, void* datalocation); // // Connects a single control parameter input port to a data location. // void connectCtrlInport(unsigned long k, void* datalocation); // // Connects a single control parameter output port to a data location. // void connectCtrlOutport(unsigned long k, void* datalocation); // Return true on success. bool activate(); // Return true on success. bool deactivate(); }; static inline float fast_log2 (float val) { /* don't use reinterpret_cast<> because that prevents this from being used by pure C code (for example, GnomeCanvasItems) */ int* const exp_ptr = (int *)(&val); int x = *exp_ptr; const int log_2 = ((x >> 23) & 255) - 128; x &= ~(255 << 23); x += 127 << 23; *exp_ptr = x; val = ((-1.0f/3) * val + 2) * val - 2.0f/3; // (1) return (val + log_2); } static inline float fast_log10 (const float val) { return fast_log2(val) / 3.312500f; } //--------------------------------------------------------- // PluginList //--------------------------------------------------------- typedef std::list::iterator iPlugin; class PluginList : public std::list { public: Plugin* find(const QString& file, const QString& name); PluginList() {} ~PluginList(); }; extern void SS_initPlugins(const QString& hostCachePath); extern PluginList plugins; } // namespace MusESimplePlugin #endif muse-4.0.0/src/synti/libsimpleplugin/simpler_plugin_lv2.cpp000066400000000000000000006367241404105311000241470ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // simpler_plugin_lv2.cpp // (C) Copyright 2018 Tim E. Real (terminator356 on users.sourceforge.net) // // 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; 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; 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include "simpler_plugin_lv2.h" // #include "synth.h" #include "audio.h" // #include "jackaudio.h" #include "midi_consts.h" #include "midiport.h" #include "stringparam.h" #include "plugin.h" #include "controlfifo.h" #include "xml.h" #include "song.h" #include "ctrl.h" // #include "minstrument.h" #include "app.h" #include "globals.h" #include "globaldefs.h" #include "gconfig.h" #include "widgets/popupmenu.h" #include "widgets/menutitleitem.h" #include "icons.h" #include #include #include #include #include #include #include #include #include #define SS_LOG_MAX 0 #define SS_LOG_MIN -10 #define SS_LOG_OFFSET SS_LOG_MIN #define SP_TRACE_FUNC 0 #define SP_DEBUG_LADSPA 0 #define SP_TRACE_IN if (SP_TRACE_FUNC) fprintf (stderr, "->%s:%d\n", __PRETTY_FUNCTION__, __LINE__); #define SP_TRACE_OUT if (SP_TRACE_FUNC) fprintf (stderr, "<-%s:%d\n", __PRETTY_FUNCTION__, __LINE__); #define SP_ERROR(string) fprintf(stderr, "SimplePlugin error: %s\n", string) #define SP_DBG_LV2(string1) if (SP_DEBUG_LV2) fprintf(stderr, "%s:%d:%s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1); #define SP_DBG_LV2_2(string1, string2) if (SP_DEBUG_LV2) fprintf(stderr, "%s:%d:%s: %s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, string2); // Turn on debugging messages. //#define PLUGIN_DEBUGIN // Turn on constant stream of debugging messages. //#define PLUGIN_DEBUGIN_PROCESS // Uncomment to print audio process info. //#define LV2_DEBUG_PROCESS // Uncomment to print general info. // (There is also the CMake option LV2_DEBUG for more output.) //#define LV2_DEBUG // #ifdef HAVE_GTK2 // #include "lv2Gtk2Support/lv2Gtk2Support.h" // #endif // Define to use GtkPlug instead of GtkWindow for a Gtk plugin gui container. // This works better than GtkWindow for some plugins. // For example with GtkWindow, AMSynth fails to embed into the container window // resulting in two separate windows. // #define LV2_GUI_USE_GTKPLUG ; namespace MusESimplePlugin { #define NS_EXT "http://lv2plug.in/ns/ext/" #define NS_LV2CORE "http://lv2plug.in/ns/lv2core" #define LV2_INSTRUMENT_CLASS NS_LV2CORE "#InstrumentPlugin" #define LV2_F_BOUNDED_BLOCK_LENGTH LV2_BUF_SIZE__boundedBlockLength #define LV2_F_FIXED_BLOCK_LENGTH LV2_BUF_SIZE__fixedBlockLength #define LV2_F_POWER_OF_2_BLOCK_LENGTH LV2_BUF_SIZE__powerOf2BlockLength // BUG FIXME: 'coarseBlockLength' is NOT in the lv2 buf-size.h header file! // #define LV2_F_COARSE_BLOCK_LENGTH LV2_BUF_SIZE__coarseBlockLength #define LV2_F_COARSE_BLOCK_LENGTH LV2_BUF_SIZE_PREFIX "coarseBlockLength" #define LV2_P_SEQ_SIZE LV2_BUF_SIZE__sequenceSize #define LV2_P_MAX_BLKLEN LV2_BUF_SIZE__maxBlockLength #define LV2_P_MIN_BLKLEN LV2_BUF_SIZE__minBlockLength #define LV2_P_NOM_BLKLEN LV2_BUF_SIZE__nominalBlockLength #define LV2_P_SAMPLE_RATE LV2_PARAMETERS__sampleRate #define LV2_F_OPTIONS LV2_OPTIONS__options #define LV2_F_URID_MAP LV2_URID__map #define LV2_F_URID_UNMAP LV2_URID__unmap #ifdef LV2_URI_MAP_SUPPORT #define LV2_F_URI_MAP LV2_URI_MAP_URI #endif #define LV2_F_UI_PARENT LV2_UI__parent #define LV2_F_INSTANCE_ACCESS NS_EXT "instance-access" #define LV2_F_DATA_ACCESS LV2_DATA_ACCESS_URI #define LV2_F_UI_EXTERNAL_HOST LV2_EXTERNAL_UI__Host #define LV2_F_WORKER_SCHEDULE LV2_WORKER__schedule #define LV2_F_WORKER_INTERFACE LV2_WORKER__interface #define LV2_F_UI_IDLE LV2_UI__idleInterface #define LV2_F_UI_Qt5_UI LV2_UI_PREFIX "Qt5UI" #define LV2_UI_HOST_URI LV2_F_UI_Qt5_UI #define LV2_UI_EXTERNAL LV2_EXTERNAL_UI__Widget #define LV2_UI_EXTERNAL_DEPRECATED LV2_EXTERNAL_UI_DEPRECATED_URI #define LV2_F_DEFAULT_STATE LV2_STATE_PREFIX "loadDefaultState" #define LV2_F_STATE_CHANGED LV2_STATE_PREFIX "StateChanged" static LilvWorld *lilvWorld = 0; static int uniqueID = 1; //uri cache structure. typedef struct { LilvNode *atom_AtomPort; #ifdef LV2_EVENT_BUFFER_SUPPORT LilvNode *ev_EventPort; #endif LilvNode *lv2_AudioPort; LilvNode *lv2_ControlPort; LilvNode *lv2_InputPort; LilvNode *lv2_OutputPort; LilvNode *lv2_connectionOptional; LilvNode *host_uiType; LilvNode *ext_uiType; LilvNode *ext_d_uiType; LilvNode *lv2_portDiscrete; LilvNode *lv2_portContinuous; LilvNode *lv2_portLogarithmic; LilvNode *lv2_portInteger; LilvNode *lv2_portTrigger; LilvNode *lv2_portToggled; LilvNode *lv2_TimePosition; LilvNode *lv2_FreeWheelPort; LilvNode *lv2_SampleRate; LilvNode *lv2_CVPort; LilvNode *lv2_psetPreset; LilvNode *lv2_rdfsLabel; LilvNode *lv2_actionSavePreset; LilvNode *lv2_actionUpdatePresets; LilvNode *end; ///< NULL terminator for easy freeing of entire structure } CacheNodes; LV2_URID Synth_Urid_Map(LV2_URID_Unmap_Handle _host_data, const char *uri) { // LV2Synth *_synth = reinterpret_cast(_host_data); Lv2Plugin *_synth = reinterpret_cast(_host_data); if(_synth == NULL) //broken plugin { return 0; } return _synth->mapUrid(uri); } const char *Synth_Urid_Unmap(LV2_URID_Unmap_Handle _host_data, LV2_URID id) { // LV2Synth *_synth = reinterpret_cast(_host_data); Lv2Plugin *_synth = reinterpret_cast(_host_data); if(_synth == NULL) //broken plugin { return NULL; } return _synth->unmapUrid(id); } #ifdef LV2_URI_MAP_SUPPORT LV2_URID Synth_Uri_Map(LV2_URI_Map_Callback_Data _host_data, const char *, const char *uri) { // LV2Synth *_synth = reinterpret_cast(_host_data); Lv2Plugin *_synth = reinterpret_cast(_host_data); if(_synth == NULL) //broken plugin { return 0; } return _synth->mapUrid(uri); } #endif static CacheNodes lv2CacheNodes; LV2_Feature lv2Features [] = { {LV2_F_URID_MAP, NULL}, {LV2_F_URID_UNMAP, NULL}, #ifdef LV2_URI_MAP_SUPPORT {LV2_F_URI_MAP, NULL}, #endif {LV2_F_BOUNDED_BLOCK_LENGTH, NULL}, {LV2_F_FIXED_BLOCK_LENGTH, NULL}, {LV2_F_POWER_OF_2_BLOCK_LENGTH, NULL}, {LV2_F_COARSE_BLOCK_LENGTH, NULL}, {LV2_F_UI_PARENT, NULL}, {LV2_F_INSTANCE_ACCESS, NULL}, {LV2_F_UI_EXTERNAL_HOST, NULL}, {LV2_UI_EXTERNAL_DEPRECATED, NULL}, {LV2_F_WORKER_SCHEDULE, NULL}, {LV2_F_UI_IDLE, NULL}, {LV2_F_OPTIONS, NULL}, {LV2_UI__resize, NULL}, {LV2_PROGRAMS__Host, NULL}, {LV2_LOG__log, NULL}, {LV2_STATE__makePath, NULL}, {LV2_STATE__mapPath, NULL}, {LV2_F_STATE_CHANGED, NULL}, {LV2_F_DATA_ACCESS, NULL} //must be the last always! }; std::set supportedFeatures; // std::vector synthsToFree; std::vector synthsToFree; #define SIZEOF_ARRAY(x) sizeof(x)/sizeof(x[0]) // static // Pointer to this required for LV2_P_MIN_BLKLEN option when // composing LV2_Options_Option array in Lv2Plugin::Lv2Plugin(). const unsigned Lv2Plugin::minBlockSize = 0U; void initLV2() { // #ifdef HAVE_GTK2 // //----------------- // // Initialize Gtk // //----------------- // MusEGui::lv2Gtk2Helper_init(); // #endif // std::set supportedFeatures; uint32_t i = 0; if(MusEGlobal::debugMsg) std::cerr << "LV2: MusE supports these features:" << std::endl; for(i = 0; i < SIZEOF_ARRAY(lv2Features); i++) { supportedFeatures.insert(lv2Features [i].URI); if(MusEGlobal::debugMsg) std::cerr << "\t" << lv2Features [i].URI << std::endl; } lilvWorld = lilv_world_new(); lv2CacheNodes.atom_AtomPort = lilv_new_uri(lilvWorld, LV2_ATOM__AtomPort); #ifdef LV2_EVENT_BUFFER_SUPPORT lv2CacheNodes.ev_EventPort = lilv_new_uri(lilvWorld, LV2_EVENT__EventPort); #endif lv2CacheNodes.lv2_AudioPort = lilv_new_uri(lilvWorld, LV2_CORE__AudioPort); lv2CacheNodes.lv2_ControlPort = lilv_new_uri(lilvWorld, LV2_CORE__ControlPort); lv2CacheNodes.lv2_InputPort = lilv_new_uri(lilvWorld, LV2_CORE__InputPort); lv2CacheNodes.lv2_OutputPort = lilv_new_uri(lilvWorld, LV2_CORE__OutputPort); lv2CacheNodes.lv2_connectionOptional = lilv_new_uri(lilvWorld, LV2_CORE__connectionOptional); lv2CacheNodes.host_uiType = lilv_new_uri(lilvWorld, LV2_UI_HOST_URI); lv2CacheNodes.ext_uiType = lilv_new_uri(lilvWorld, LV2_UI_EXTERNAL); lv2CacheNodes.ext_d_uiType = lilv_new_uri(lilvWorld, LV2_UI_EXTERNAL_DEPRECATED); lv2CacheNodes.lv2_portContinuous = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__continuousCV); lv2CacheNodes.lv2_portDiscrete = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__discreteCV); lv2CacheNodes.lv2_portLogarithmic = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__logarithmic); lv2CacheNodes.lv2_portInteger = lilv_new_uri(lilvWorld, LV2_CORE__integer); lv2CacheNodes.lv2_portTrigger = lilv_new_uri(lilvWorld, LV2_PORT_PROPS__trigger); lv2CacheNodes.lv2_portToggled = lilv_new_uri(lilvWorld, LV2_CORE__toggled); lv2CacheNodes.lv2_TimePosition = lilv_new_uri(lilvWorld, LV2_TIME__Position); lv2CacheNodes.lv2_FreeWheelPort = lilv_new_uri(lilvWorld, LV2_CORE__freeWheeling); lv2CacheNodes.lv2_SampleRate = lilv_new_uri(lilvWorld, LV2_CORE__sampleRate); lv2CacheNodes.lv2_CVPort = lilv_new_uri(lilvWorld, LV2_CORE__CVPort); lv2CacheNodes.lv2_psetPreset = lilv_new_uri(lilvWorld, LV2_PRESETS__Preset); lv2CacheNodes.lv2_rdfsLabel = lilv_new_uri(lilvWorld, "http://www.w3.org/2000/01/rdf-schema#label"); lv2CacheNodes.lv2_actionSavePreset = lilv_new_uri(lilvWorld, "http://www.muse-sequencer.org/lv2host#lv2_actionSavePreset"); lv2CacheNodes.lv2_actionUpdatePresets= lilv_new_uri(lilvWorld, "http://www.muse-sequencer.org/lv2host#lv2_actionUpdatePresets"); lv2CacheNodes.end = NULL; lilv_world_load_all(lilvWorld); const LilvPlugins *plugins = lilv_world_get_all_plugins(lilvWorld); LilvIter *pit = lilv_plugins_begin(plugins); while(true) { if(lilv_plugins_is_end(plugins, pit)) { break; } const LilvPlugin *plugin = lilv_plugins_get(plugins, pit); if(lilv_plugin_is_replaced(plugin)) { pit = lilv_plugins_next(plugins, pit); continue; } LilvNode *nameNode = lilv_plugin_get_name(plugin); //const LilvNode *uriNode = lilv_plugin_get_uri(plugin); if(lilv_node_is_string(nameNode)) { bool shouldLoad = true; const char *pluginName = lilv_node_as_string(nameNode); //const char *pluginUri = lilv_node_as_string(uriNode); if(MusEGlobal::debugMsg) std::cerr << "Found LV2 plugin: " << pluginName << std::endl; // lilv_uri_to_path is deprecated. Use lilv_file_uri_parse instead. Return value must be freed with lilv_free. const char *lfp = lilv_file_uri_parse(lilv_node_as_string(lilv_plugin_get_library_uri(plugin)), NULL); if(MusEGlobal::debugMsg) std::cerr << "Library path: " << lfp << std::endl; if(MusEGlobal::debugMsg) { const LilvPluginClass *cls = lilv_plugin_get_class(plugin); const LilvNode *ncuri = lilv_plugin_class_get_uri(cls); const char *clsname = lilv_node_as_uri(ncuri); std::cerr << "Plugin class: " << clsname << std::endl; bool isSynth = false; if(strcmp(clsname, LV2_INSTRUMENT_CLASS) == 0) { isSynth = true; } if(isSynth) { std::cerr << "Plugin is synth" << std::endl; } } #ifdef DEBUG_LV2 std::cerr << "\tRequired features (by uri):" << std::endl; #endif LilvNodes *fts = lilv_plugin_get_required_features(plugin); LilvIter *nit = lilv_nodes_begin(fts); Plugin::PluginFeaturesType reqfeat = Plugin::NoFeatures; while(true) { if(lilv_nodes_is_end(fts, nit)) { break; } const LilvNode *fnode = lilv_nodes_get(fts, nit); const char *uri = lilv_node_as_uri(fnode); bool isSupported = (supportedFeatures.find(uri) != supportedFeatures.end()); #ifdef DEBUG_LV2 std::cerr << "\t - " << uri << " (" << (isSupported ? "supported" : "not supported") << ")" << std::endl; #endif if(isSupported) { if(strcmp(uri, LV2_F_FIXED_BLOCK_LENGTH) == 0) reqfeat |= Plugin::FixedBlockSize; else if(strcmp(uri, LV2_F_POWER_OF_2_BLOCK_LENGTH) == 0) reqfeat |= Plugin::PowerOf2BlockSize; else if(strcmp(uri, LV2_F_COARSE_BLOCK_LENGTH) == 0) reqfeat |= Plugin::CoarseBlockLength; } else { shouldLoad = false; std::cerr << "\t LV2: " << pluginName << ": Required feature: " << uri << ": not supported!" << std::endl; } nit = lilv_nodes_next(fts, nit); } lilv_nodes_free(fts); //if (shouldLoad && isSynth) if(shouldLoad) //load all plugins for now, not only synths { QFileInfo fi(lfp); QString name = QString(pluginName) + QString(" LV2"); //QString label = QString(pluginUri) + QString("_LV2"); // // Make sure it doesn't already exist. // std::vector::iterator is; // // for(is = MusEGlobal::synthis.begin(); is != MusEGlobal::synthis.end(); ++is) // { // Synth *s = *is; // // if(s->name() == name && s->baseName() == fi.completeBaseName()) // { // break; // } // } // // if(is == MusEGlobal::synthis.end()) // Make sure it doesn't already exist. if(MusESimplePlugin::plugins.find(fi.completeBaseName(), name) == 0) { LilvNode *nAuthor = lilv_plugin_get_author_name(plugin); QString author; if(nAuthor != NULL) { author = lilv_node_as_string(nAuthor); lilv_node_free(nAuthor); } //LV2Synth *s = new LV2Synth(fi, name, name, author, plugin, reqfeat); Lv2Plugin *s = new Lv2Plugin(&fi, name, name, author, plugin, reqfeat); if(s->isConstructed()) { // if((s->isSynth() && s->outPorts() > 0) // || (s->inPorts() > 0 && s->outPorts() > 0)) // //insert plugins with audio ins and outs to synths list too // { // MusEGlobal::synthis.push_back(s); // } // else // { // synthsToFree.push_back(s); // } // // if(s->inPorts() > 0 && s->outPorts() > 0) // insert to plugin list // { // MusEGlobal::plugins.push_back(new LV2PluginWrapper(s, reqfeat)); // // } MusESimplePlugin::plugins.push_back(s); } else { delete s; } } } lilv_free((void*)lfp); // Must free. } if(nameNode != NULL) { lilv_node_free(nameNode); } pit = lilv_plugins_next(plugins, pit); } } void deinitLV2() { for(size_t i = 0; i < synthsToFree.size(); i++) { delete synthsToFree [i]; } synthsToFree.clear(); for(LilvNode **n = (LilvNode **)&lv2CacheNodes; *n; ++n) { lilv_node_free(*n); } // #ifdef HAVE_GTK2 // MusEGui::lv2Gtk2Helper_deinit(); // #endif lilv_world_free(lilvWorld); lilvWorld = NULL; } // void LV2Synth::lv2ui_ExtUi_Closed(LV2UI_Controller contr) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)contr; // assert(state != NULL); //this shouldn't happen // assert(state->widget != NULL); // this too // assert(state->pluginWindow != NULL); // // state->pluginWindow->setClosing(true); // // // //state->uiTimer->stopNextTime(false); // } // // void LV2Synth::lv2ui_SendChangedControls(LV2PluginWrapper_State *state) // { // if(state != NULL && state->uiDesc != NULL && state->uiDesc->port_event != NULL && state->uiInst != NULL) // { // size_t numControls = 0; // MusECore::Port *controls = NULL; // size_t numControlsOut = 0; // MusECore::Port *controlsOut = NULL; // LV2Synth *synth = state->synth; // // if(state->plugInst != NULL) // { // numControls = state->plugInst->controlPorts; // controls = state->plugInst->controls; // numControlsOut = state->plugInst->controlOutPorts; // controlsOut = state->plugInst->controlsOut; // // } // else if(state->sif != NULL) // { // numControls = state->sif->_inportsControl; // controls = state->sif->_controls; // numControlsOut = state->sif->_outportsControl; // controlsOut = state->sif->_controlsOut; // } // // if(numControls > 0) // { // assert(controls != NULL); // } // // if(numControlsOut > 0) // { // assert(controlsOut != NULL); // } // for(uint32_t i = 0; i < numControls; ++i) // { // if(state->controlTimers [i] > 0) // { // --state->controlTimers [i]; // continue; // } // if(state->controlsMask [i]) // { // state->controlsMask [i] = false; // // // Force send if re-opening. // if(state->uiIsOpening || state->lastControls [i] != controls [i].val) // { // state->lastControls [i] = controls [i].val; // state->uiDesc->port_event(state->uiInst, // controls [i].idx, // sizeof(float), 0, // &controls [i].val); // } // } // } // // for(uint32_t i = 0; i < numControlsOut; ++i) // { // // Force send if re-opening. // if(state->uiIsOpening || state->lastControlsOut [i] != controlsOut [i].val) // { // state->lastControlsOut [i] = controlsOut [i].val; // state->uiDesc->port_event(state->uiInst, // controlsOut [i].idx, // sizeof(float), 0, // &controlsOut [i].val); // } // // } // // //process gui atom events (control events are already set by getData or apply. // size_t fifoItemSize = state->plugControlEvt.getItemSize(); // size_t dataSize = 0; // uint32_t port_index = 0; // char evtBuffer [fifoItemSize]; // while(state->plugControlEvt.get(&port_index, &dataSize, evtBuffer)) // { // state->uiDesc->port_event(state->uiInst, port_index, dataSize, synth->_uAtom_EventTransfer, evtBuffer); // } // } // } // // // // void LV2Synth::lv2ui_PortWrite(LV2UI_Controller controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, void const *buffer) // { // LV2Synth::lv2state_PortWrite(controller, port_index, buffer_size, protocol, buffer, true); // } // // void LV2Synth::lv2ui_Touch(LV2UI_Controller /*controller*/, uint32_t /*port_index*/, bool grabbed __attribute__ ((unused))) // { // #ifdef DEBUG_LV2 // std::cerr << "LV2Synth::lv2ui_UiTouch: port: %u " << (grabbed ? "grabbed" : "released") << std::endl; // #endif // // } // // void Lv2Plugin::lv2_FillFeatures(LV2_Feature *features, LV2_Feature **ppfeatures, LV2_Options_Option* options) { uint32_t i; for(i = 0; i < SIZEOF_ARRAY(lv2Features); i++) { features [i] = _features [i]; if(features [i].URI == NULL) { break; } if(i == _fOptions) { features [i].data = options; } else if(i == _fInstanceAccess) { features [i].data = NULL; } else if(i == _fExtUiHost) { features [i].data = &state->extHost; } else if(i == _fExtUiHostD) { features [i].data = &state->extHost; } else if(i == _fDataAccess) { features [i].data = &state->extData; } else if(i == _fWrkSchedule) { features [i].data = &state->wrkSched; } else if(i == _fUiResize) { features [i].data = &state->uiResize; } else if(i == _fPrgHost) { features [i].data = &state->prgHost; } else if(i == _fMakePath) { features [i].data = &state->makePath; } else if(i == _fMapPath) { features [i].data = &state->mapPath; } // _ppifeatures [i] = &_ifeatures [i]; ppfeatures [i] = &features [i]; } // _ppifeatures [i] = NULL; ppfeatures [i] = NULL; } void Lv2Plugin::lv2state_FillFeatures(LV2PluginWrapper_State *state) { uint32_t i; // LV2Synth *synth = state->synth; Lv2Plugin *synth = state->synth; LV2_Feature *_ifeatures = state->_ifeatures; LV2_Feature **_ppifeatures = state->_ppifeatures; //state->uiTimer = new LV2PluginWrapper_Timer(state); state->wrkSched.handle = (LV2_Worker_Schedule_Handle)state; // state->wrkSched.schedule_work = LV2Synth::lv2wrk_scheduleWork; state->wrkSched.schedule_work = lv2wrk_scheduleWork; state->wrkIface = NULL; state->wrkThread = new LV2PluginWrapper_Worker(state); state->extHost.plugin_human_id = state->human_id = NULL; state->extHost.ui_closed = LV2Synth::lv2ui_ExtUi_Closed; state->extData.data_access = NULL; for(i = 0; i < SIZEOF_ARRAY(lv2Features); i++) { _ifeatures [i] = synth->_features [i]; if(_ifeatures [i].URI == NULL) { break; } if(i == synth->_fInstanceAccess) { _ifeatures [i].data = NULL; } else if(i == synth->_fExtUiHost) { _ifeatures [i].data = &state->extHost; } else if(i == synth->_fExtUiHostD) { _ifeatures [i].data = &state->extHost; } else if(i == synth->_fDataAccess) { _ifeatures [i].data = &state->extData; } else if(i == synth->_fWrkSchedule) { _ifeatures [i].data = &state->wrkSched; } else if(i == synth->_fUiResize) { _ifeatures [i].data = &state->uiResize; } else if(i == synth->_fPrgHost) { _ifeatures [i].data = &state->prgHost; } else if(i == synth->_fMakePath) { _ifeatures [i].data = &state->makePath; } else if(i == synth->_fMapPath) { _ifeatures [i].data = &state->mapPath; } _ppifeatures [i] = &_ifeatures [i]; } _ppifeatures [i] = NULL; state->curBpm = 0.0;//double(60000000.0/MusEGlobal::tempomap.tempo(MusEGlobal::song->cpos())); state->curIsPlaying = MusEGlobal::audio->isPlaying(); state->curFrame = MusEGlobal::audioDevice->getCurFrame(); lv2_atom_forge_init(&state->atomForge, &synth->_lv2_urid_map); LV2Synth::lv2state_InitMidiPorts(state); } // void LV2Synth::lv2state_PostInstantiate(LV2PluginWrapper_State *state) // { // LV2Synth *synth = state->synth; // const LV2_Descriptor *descr = lilv_instance_get_descriptor(state->handle); // // state->_ifeatures [synth->_fInstanceAccess].data = lilv_instance_get_handle(state->handle); // // if(descr->extension_data != NULL) // { // state->extData.data_access = descr->extension_data; // } // else // { // state->_ppifeatures [synth->_fDataAccess] = NULL; // } // // state->controlsNameMap.clear(); // // size_t nCpIn = synth->_controlInPorts.size(); // size_t nCpOut = synth->_controlOutPorts.size(); // // if(nCpIn > 0) // { // state->lastControls = new float [nCpIn]; // state->controlsMask = new bool [nCpIn]; // state->controlTimers = new int [nCpIn]; // for(uint32_t i = 0; i < nCpIn; i++) // { // state->lastControls [i] = synth->_pluginControlsDefault [synth->_controlInPorts [i].index]; // state->controlsMask [i] = false; // state->controlTimers [i] = 0; // state->controlsNameMap.insert(std::pair(QString(synth->_controlInPorts [i].cName).toLower(), i)); // state->controlsSymMap.insert(std::pair(QString(synth->_controlInPorts [i].cSym).toLower(), i)); // } // } // // if(nCpOut > 0) // { // state->lastControlsOut = new float [nCpOut]; // for(uint32_t i = 0; i < nCpOut; i++) // { // state->lastControlsOut [i] = synth->_pluginControlsDefault [synth->_controlOutPorts [i].index]; // } // } // // //fill pointers for CV port types; // // uint32_t numAllPorts = lilv_plugin_get_num_ports(synth->_handle); // // state->pluginCVPorts = new float *[numAllPorts]; // int rv = posix_memalign((void **)&state->pluginCVPorts, 16, sizeof(float *) * numAllPorts); // if(rv != 0) // { // fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: posix_memalign returned error:%d. Aborting!\n", rv); // abort(); // } // // memset(state->pluginCVPorts, 0, sizeof(float *) * numAllPorts); // // for(size_t i = 0; i < synth->_controlInPorts.size(); ++i) // { // if(synth->_controlInPorts [i].isCVPort) // { // size_t idx = synth->_controlInPorts [i].index; // rv = posix_memalign((void **)&state->pluginCVPorts [idx], 16, sizeof(float) * MusEGlobal::segmentSize); // // if(rv != 0) // { // fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: posix_memalign returned error:%d. Aborting!\n", rv); // abort(); // } // for(size_t k = 0; k < MusEGlobal::segmentSize; ++k) // { // state->pluginCVPorts [idx] [k] = synth->_controlInPorts [i].defVal; // } // lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); // } // } // // for(size_t i = 0; i < synth->_controlOutPorts.size(); ++i) // { // if(synth->_controlOutPorts [i].isCVPort) // { // size_t idx = synth->_controlOutPorts [i].index; // rv = posix_memalign((void **)&state->pluginCVPorts [idx], 16, sizeof(float) * MusEGlobal::segmentSize); // // if(rv != 0) // { // fprintf(stderr, "ERROR: LV2Synth::lv2state_PostInstantiate: posix_memalign returned error:%d. Aborting!\n", rv); // abort(); // } // for(size_t k = 0; k < MusEGlobal::segmentSize; ++k) // { // state->pluginCVPorts [idx] [k] = synth->_controlOutPorts [i].defVal; // } // lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); // } // } // // for(size_t i = 0; i < state->midiInPorts.size(); i++) // { // lilv_instance_connect_port(state->handle, state->midiInPorts [i].index, (void *)state->midiInPorts [i].buffer->getRawBuffer()); // } // // for(size_t i = 0; i < state->midiOutPorts.size(); i++) // { // lilv_instance_connect_port(state->handle, state->midiOutPorts [i].index, (void *)state->midiOutPorts [i].buffer->getRawBuffer()); // } // // //query for state interface // state->iState = (LV2_State_Interface *)lilv_instance_get_extension_data(state->handle, LV2_STATE__interface); // //query for LV2Worker interface // state->wrkIface = (LV2_Worker_Interface *)lilv_instance_get_extension_data(state->handle, LV2_F_WORKER_INTERFACE); // //query for programs interface // state->prgIface = (LV2_Programs_Interface *)lilv_instance_get_extension_data(state->handle, LV2_PROGRAMSNEW__Interface); // if(state->prgIface != NULL) // { // state->newPrgIface = true; // } // else // { // state->newPrgIface = false; // state->prgIface = (LV2_Programs_Interface *)lilv_instance_get_extension_data(state->handle, LV2_PROGRAMS__Interface); // } // // LV2Synth::lv2prg_updatePrograms(state); // // state->wrkThread->start(QThread::LowPriority); // // } // TODO: UI // void Lv2Plugin::lv2ui_FreeDescriptors(LV2PluginWrapper_State *state) // { // if(state->uiDesc != NULL && state->uiInst != NULL) // state->uiDesc->cleanup(state->uiInst); // // state->uiInst = *(void **)(&state->uiDesc) = NULL; // // // #ifdef HAVE_GTK2 // // if(state->gtk2Plug != NULL) // // MusEGui::lv2Gtk2Helper_gtk_widget_destroy(state->gtk2Plug); // // #endif // // state->gtk2Plug = NULL; // // if(state->uiDlHandle != NULL) // { // dlclose(state->uiDlHandle); // state->uiDlHandle = NULL; // } // // } void Lv2Plugin::lv2state_FreeState(LV2PluginWrapper_State *state) { assert(state != NULL); // TODO: wrk // state->wrkThread->setClosing(); // state->wrkThread->wait(); delete state->wrkThread; if(state->human_id != NULL) free(state->human_id); if(state->lastControls) { delete [] state->lastControls; state->lastControls = NULL; } if(state->controlsMask) { delete [] state->controlsMask; state->controlsMask = NULL; } if(state->controlTimers) { delete [] state->controlTimers; state->controlTimers = NULL; } if(state->lastControlsOut) { delete [] state->lastControlsOut; state->lastControlsOut = NULL; } // TODO: UI //lv2ui_FreeDescriptors(state); if(state->handle != NULL) { lilv_instance_free(state->handle); state->handle = NULL; } delete state; } // void LV2Synth::lv2audio_SendTransport(LV2PluginWrapper_State *state, LV2EvBuf *buffer, unsigned long nsamp) // { // //send transport events if any // LV2Synth *synth = state->synth; // unsigned int cur_frame = MusEGlobal::audio->pos().frame(); // Pos p(MusEGlobal::extSyncFlag ? MusEGlobal::audio->tickPos() : cur_frame, MusEGlobal::extSyncFlag ? true : false); // float curBpm = (float)MusEGlobal::tempomap.globalTempo() * 600000.0f / (float)MusEGlobal::tempomap.tempo(p.tick()); // bool curIsPlaying = MusEGlobal::audio->isPlaying(); // unsigned int curFrame = MusEGlobal::audioDevice->getCurFrame(); // // if(state->curFrame != curFrame // // || state->curIsPlaying != curIsPlaying // // || state->curBpm != curBpm) // // { // // //send transport/tempo changes always // //as some plugins revert to default settings when not playing // state->curFrame = curFrame; // state->curIsPlaying = curIsPlaying; // state->curBpm = curBpm; // uint8_t pos_buf[1024]; // memset(pos_buf, 0, sizeof(pos_buf)); // LV2_Atom* lv2_pos = (LV2_Atom*)pos_buf; // /* Build an LV2 position object to report change to plugin */ // LV2_Atom_Forge* atomForge = &state->atomForge; // lv2_atom_forge_set_buffer(atomForge, pos_buf, sizeof(pos_buf)); // LV2_Atom_Forge_Frame frame; // lv2_atom_forge_object(atomForge, &frame, 1, synth->_uTime_Position); // lv2_atom_forge_key(atomForge, synth->_uTime_frame); // lv2_atom_forge_long(atomForge, curFrame); // lv2_atom_forge_key(atomForge, synth->_uTime_speed); // lv2_atom_forge_float(atomForge, curIsPlaying ? 1.0 : 0.0); // lv2_atom_forge_key(atomForge, synth->_uTime_beatsPerMinute); // lv2_atom_forge_float(atomForge, curBpm); // #ifdef LV2_EVENT_BUFFER_SUPPORT // buffer->write(nsamp, 0, lv2_pos->type, lv2_pos->size, (const uint8_t *)LV2_ATOM_BODY(lv2_pos)); // #else // buffer->write(nsamp, lv2_pos->type, lv2_pos->size, (const uint8_t *)LV2_ATOM_BODY(lv2_pos)); // #endif // } // // void LV2Synth::lv2state_InitMidiPorts(LV2PluginWrapper_State *state) // { // LV2Synth *synth = state->synth; // state->midiInPorts = synth->_midiInPorts; // state->midiOutPorts = synth->_midiOutPorts; // state->inPortsMidi= state->midiInPorts.size(); // state->outPortsMidi = state->midiOutPorts.size(); // //connect midi and control ports // for(size_t i = 0; i < state->midiInPorts.size(); i++) // { // LV2EvBuf *newEvBuffer = new LV2EvBuf( // true, // #ifdef LV2_EVENT_BUFFER_SUPPORT // state->midiInPorts [i].old_api, // #endif // synth->_uAtom_Sequence, // synth->_uAtom_Chunk); // if(!newEvBuffer) // { // abort(); // } // state->midiInPorts [i].buffer = newEvBuffer; // state->idx2EvtPorts.insert(std::make_pair(state->midiInPorts [i].index, newEvBuffer)); // } // // for(size_t i = 0; i < state->midiOutPorts.size(); i++) // { // LV2EvBuf *newEvBuffer = new LV2EvBuf( // false, // #ifdef LV2_EVENT_BUFFER_SUPPORT // state->midiOutPorts [i].old_api, // #endif // synth->_uAtom_Sequence, // synth->_uAtom_Chunk); // if(!newEvBuffer) // { // abort(); // } // state->midiOutPorts [i].buffer = newEvBuffer; // state->idx2EvtPorts.insert(std::make_pair(state->midiOutPorts [i].index, newEvBuffer)); // } // // } // // void LV2Synth::lv2audio_preProcessMidiPorts(LV2PluginWrapper_State *state, unsigned long nsamp) // { // for(size_t j = 0; j < state->inPortsMidi; j++) // { // state->midiInPorts [j].buffer->resetBuffer(); // } // // for(size_t j = 0; j < state->outPortsMidi; j++) // { // state->midiOutPorts [j].buffer->resetBuffer(); // } // // if(state->inPortsMidi > 0) // { // LV2EvBuf *rawMidiBuffer = state->midiInPorts [0].buffer; // // if(state->midiInPorts [0].supportsTimePos) // { // //send transport events if any // LV2Synth::lv2audio_SendTransport(state, rawMidiBuffer, nsamp); // } // } // // //process gui atom events (control events are already set by getData or apply call). // size_t fifoItemSize = state->uiControlEvt.getItemSize(); // size_t dataSize = 0; // uint32_t port_index = 0; // char evtBuffer [fifoItemSize]; // while(state->uiControlEvt.get(&port_index, &dataSize, evtBuffer)) // { // std::map::iterator it = state->idx2EvtPorts.find(port_index); // if(it != state->idx2EvtPorts.end()) // { // LV2EvBuf *buffer = it->second; // const LV2_Atom* const atom = (const LV2_Atom*)evtBuffer; // #ifdef LV2_EVENT_BUFFER_SUPPORT // buffer->write(nsamp, 0, atom->type, atom->size, static_cast(LV2_ATOM_BODY_CONST(atom))); // #else // buffer->write(nsamp, atom->type, atom->size, static_cast(LV2_ATOM_BODY_CONST(atom))); // #endif // } // // } // } // // void LV2Synth::lv2audio_postProcessMidiPorts(LV2PluginWrapper_State *state, unsigned long) // { // //send Atom events to gui. // //Synchronize send rate with gui update rate // // // size_t fifoItemSize = state->plugControlEvt.getItemSize(); // // size_t outp = state->midiOutPorts.size(); // // for(size_t j = 0; j < outp; j++) // { // #ifdef LV2_EVENT_BUFFER_SUPPORT // if(!state->midiOutPorts [j].old_api) // #endif // { // do // { // uint32_t frames, type, size; // uint8_t *data = NULL; // #ifdef LV2_EVENT_BUFFER_SUPPORT // uint32_t subframes; // if(!state->midiOutPorts [j].buffer->read(&frames, &subframes, &type, &size, &data)) // #else // if(!state->midiOutPorts [j].buffer->read(&frames, &type, &size, &data)) // #endif // { // break; // } // if(type == state->synth->_uAtom_Object) // { // const LV2_Atom_Object_Body *aObjBody = reinterpret_cast(data); // if(aObjBody->otype == state->synth->_uAtom_StateChanged) // { // //Just make song status dirty (pending event) - something had changed in the plugin controls // state->songDirtyPending = true; // } // } // if(state->uiInst == NULL) // { // continue; // } // unsigned char atom_data [fifoItemSize]; // LV2_Atom *atom_evt = reinterpret_cast(atom_data); // atom_evt->type = type; // atom_evt->size = size; // if(fifoItemSize - sizeof(LV2_Atom) < size) // { // #ifdef DEBUG_LV2 // std::cerr << "LV2Synth::lv2audio_postProcessMidiPorts(): Plugin event data is bigger than rt fifo item size. Skipping." << std::endl; // #endif // continue; // } // unsigned char *evt = static_cast(LV2_ATOM_BODY(atom_evt)); // memcpy(evt, data, size); // // state->plugControlEvt.put(state->midiOutPorts [j].index, sizeof(LV2_Atom) + size, atom_evt); // } // while(true); // // } // } // // } // // void LV2Synth::lv2ui_PostShow(LV2PluginWrapper_State *state) // { // assert(state->pluginWindow != NULL); // assert(state->uiDesc != NULL); // assert(state->uiInst != NULL); // // if(state->uiDesc->port_event != NULL) // { // uint32_t numControls = 0; // Port *controls = NULL; // // if(state->plugInst != NULL) // { // numControls = state->plugInst->controlPorts; // controls = state->plugInst->controls; // // } // else if(state->sif != NULL) // { // numControls = state->sif->_inportsControl; // controls = state->sif->_controls; // } // // if(numControls > 0) // { // assert(controls != NULL); // } // // // // for(uint32_t i = 0; i < numControls; ++i) // { // state->uiDesc->port_event(state->uiInst, // controls [i].idx, // sizeof(float), 0, // &controls [i].val); // } // // } // // // Set the flag to tell the update timer to force sending all controls and program. // state->uiIsOpening = true; // // state->pluginWindow->startNextTime(); // // } // TODO: UI // int LV2Synth::lv2ui_Resize(LV2UI_Feature_Handle handle, int width, int height) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // if(state->widget != NULL && state->hasGui) // { // ((LV2PluginWrapper_Window *)state->widget)->resize(width, height); // QWidget *ewWin = ((LV2PluginWrapper_Window *)state->widget)->findChild(); // if(ewWin != NULL) // { // ewWin->resize(width, height); // } // else // { // #ifdef LV2_GUI_USE_QWIDGET // // TODO Check this, maybe wrong widget, maybe need the one contained by it? // QWidget *ewCent= ((LV2PluginWrapper_Window *)state->widget); // #else // QWidget *ewCent= ((LV2PluginWrapper_Window *)state->widget)->centralWidget(); // #endif // if(ewCent != NULL) // { // ewCent->resize(width, height); // } // } // state->uiX11Size.setWidth(width); // state->uiX11Size.setHeight(height); // return 0; // } // return 1; // } // void LV2Synth::lv2ui_Gtk2AllocateCb(int width, int height, void *arg) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)arg; // if(state == NULL) // return; // if(!state->gtk2AllocateCompleted && state->widget != NULL && state->hasGui && state->gtk2Plug != NULL) // { // state->gtk2AllocateCompleted = true; // ((LV2PluginWrapper_Window *)state->widget)->setMinimumSize(width, height); // } // } // // void LV2Synth::lv2ui_Gtk2ResizeCb(int width, int height, void *arg) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)arg; // if(state == NULL) // return; // if(!state->gtk2ResizeCompleted && state->widget != NULL && state->hasGui && state->gtk2Plug != NULL) // { // state->gtk2ResizeCompleted = true; // ((LV2PluginWrapper_Window *)state->widget)->resize(width, height); // } // } // // void LV2Synth::lv2ui_ShowNativeGui(LV2PluginWrapper_State *state, bool bShow) // { // LV2Synth* synth = state->synth; // LV2PluginWrapper_Window *win = NULL; // // if(synth->_pluginUiTypes.size() == 0) // return; // // //state->uiTimer->stopNextTime(); // if(state->pluginWindow != NULL) // state->pluginWindow->stopNextTime(); // // if(!bShow) // return; // // LV2_PLUGIN_UI_TYPES::iterator itUi; // // if((state->uiCurrent == NULL) || MusEGlobal::config.lv2UiBehavior == MusEGlobal::CONF_LV2_UI_ASK_ALWAYS) // { // state->uiCurrent = NULL; // state->gtk2ResizeCompleted = false; // state->gtk2AllocateCompleted = false; // QAction *aUiTypeSelected = NULL; // if((synth->_pluginUiTypes.size() == 1) || MusEGlobal::config.lv2UiBehavior == MusEGlobal::CONF_LV2_UI_USE_FIRST) // { // state->uiCurrent = synth->_pluginUiTypes.begin()->first; // } // else // { // QMenu mGuisPopup; // MusEGui::MenuTitleItem *aUiTypeHeader = new MusEGui::MenuTitleItem(QObject::tr("Select gui type"), NULL); // aUiTypeHeader->setEnabled(false); // QFont fHeader; // fHeader.setBold(true); // fHeader.setUnderline(true); // aUiTypeHeader->setFont(fHeader); // mGuisPopup.addAction(aUiTypeHeader); // // for(itUi = synth->_pluginUiTypes.begin(); itUi != synth->_pluginUiTypes.end(); itUi++) // { // const LilvUI *selectedUi = itUi->first; // const LilvNode *pluginUiType = itUi->second.second; // QAction *aUiType = new QAction(QString(lilv_node_as_string(pluginUiType)), NULL); // aUiType->setData(QVariant(reinterpret_cast(selectedUi))); // mGuisPopup.addAction(aUiType); // } // // aUiTypeSelected = mGuisPopup.exec(QCursor::pos()); // if(aUiTypeSelected == NULL) // { // return; // } // state->uiCurrent = reinterpret_cast(aUiTypeSelected->data().toLongLong()); // } // // } // // itUi = synth->_pluginUiTypes.find(state->uiCurrent); // // assert(itUi != synth->_pluginUiTypes.end()); // // // const LilvUI *selectedUi = itUi->first; // bool bExtUi = itUi->second.first; // const LilvNode *pluginUiType = itUi->second.second; // state->uiIdleIface = NULL; // if(bExtUi) // { // state->hasGui = false; // state->hasExternalGui = true; // } // else // { // state->hasGui = true; // state->hasExternalGui = false; // } // // #ifdef LV2_GUI_USE_QWIDGET // win = new LV2PluginWrapper_Window(state, Q_NULLPTR, Qt::Window); // #else // win = new LV2PluginWrapper_Window(state); // #endif // // state->uiX11Size.setWidth(0); // state->uiX11Size.setHeight(0); // // if(win != NULL) // { // state->widget = win; // state->pluginWindow = win; // const char *cUiUri = lilv_node_as_uri(pluginUiType); // const char *cUiTypeUri = lilv_node_as_uri(lilv_ui_get_uri(selectedUi)); // bool bEmbed = false; // bool bGtk = false; // QWidget *ewWin = NULL; // #ifdef HAVE_GTK2 // QWindow *x11QtWindow = NULL; // #endif // state->gtk2Plug = NULL; // state->_ifeatures [synth->_fUiParent].data = NULL; // if(strcmp(LV2_UI__X11UI, cUiUri) == 0) // { // bEmbed = true; // ewWin = new QWidget(); // // #ifdef LV2_GUI_USE_QWIDGET // QVBoxLayout* layout = new QVBoxLayout(); // layout->setMargin(0); // layout->setSpacing(0); // layout->addWidget(ewWin); // win->setLayout(layout); // // #else // win->setCentralWidget(ewWin); // #endif // // state->_ifeatures [synth->_fUiParent].data = (void*)ewWin->winId(); // } // else if(strcmp(LV2_UI__GtkUI, cUiUri) == 0) // { // #ifndef HAVE_GTK2 // win->stopNextTime(); // return; // #else // // bEmbed = true; // bGtk = true; // // #ifdef LV2_GUI_USE_GTKPLUG // state->gtk2Plug = MusEGui::lv2Gtk2Helper_gtk_plug_new(0, state); // #else // state->gtk2Plug = MusEGui::lv2Gtk2Helper_gtk_window_new(state); // #endif // // MusEGui::lv2Gtk2Helper_register_allocate_cb(static_cast(state->gtk2Plug), lv2ui_Gtk2AllocateCb); // MusEGui::lv2Gtk2Helper_register_resize_cb(static_cast(state->gtk2Plug), lv2ui_Gtk2ResizeCb); // // #endif // // } // else if(strcmp(LV2_F_UI_Qt5_UI, cUiUri) == 0) //Qt5 uis are handled natively // { // state->_ifeatures [synth->_fUiParent].data = win; // } // else //external uis // { // state->_ifeatures [synth->_fUiParent].data = NULL; // } // // //now open ui library file // // // lilv_uri_to_path is deprecated. Use lilv_file_uri_parse instead. Return value must be freed with lilv_free. // const char *uiPath = lilv_file_uri_parse(lilv_node_as_uri(lilv_ui_get_binary_uri(selectedUi)), NULL); // // REMOVE Tim. LV2. Changed. TESTING. RESTORE. Qt4 versions of synthv1,drumk,? crashes on Qt5. // // TESTED: On my system it gets much farther into the call now, dozens of Qt4 calls into it, // // but ultimately still ends up crashing on a call to dlopen libkdecore.5 for some reason. // // state->uiDlHandle = dlopen(uiPath, RTLD_NOW); // //state->uiDlHandle = dlmopen(LM_ID_NEWLM, uiPath, RTLD_LAZY | RTLD_DEEPBIND); // Just a test // state->uiDlHandle = dlopen(uiPath, RTLD_NOW | RTLD_DEEPBIND); // // lilv_free((void*)uiPath); // Must free. // if(state->uiDlHandle == NULL) // { // win->stopNextTime(); // return; // } // // //find lv2 ui descriptor function and call it to get ui descriptor struct // LV2UI_DescriptorFunction lv2fUiDesc; // *(void **)(&lv2fUiDesc) = dlsym(state->uiDlHandle, "lv2ui_descriptor"); // if(lv2fUiDesc == NULL) // { // win->stopNextTime(); // return; // } // // state->uiDesc = NULL; // // for(uint32_t i = 0; ;++i) // { // state->uiDesc = lv2fUiDesc(i); // if(state->uiDesc == NULL) // break; // // if(strcmp(state->uiDesc->URI, cUiTypeUri) == 0) //found selected ui // break; // } // // if(state->uiDesc == NULL) // { // win->stopNextTime(); // return; // } // // void *uiW = NULL; // // lilv_uri_to_path is deprecated. Use lilv_file_uri_parse instead. Return value must be freed with lilv_free. // const char* bundle_path = lilv_file_uri_parse(lilv_node_as_uri(lilv_ui_get_bundle_uri(selectedUi)), NULL); // state->uiInst = state->uiDesc->instantiate(state->uiDesc, // lilv_node_as_uri(lilv_plugin_get_uri(synth->_handle)), // bundle_path, // LV2Synth::lv2ui_PortWrite, // state, // &uiW, // state->_ppifeatures); // // // lilv_free((void*)bundle_path); // Must free. // // if(state->uiInst != NULL) // { // state->uiIdleIface = NULL; // state->uiPrgIface = NULL; // if(state->uiDesc->extension_data != NULL) // { // state->uiIdleIface = (LV2UI_Idle_Interface *)state->uiDesc->extension_data(LV2_F_UI_IDLE); // state->uiPrgIface = (LV2_Programs_UI_Interface *)state->uiDesc->extension_data(LV2_PROGRAMSNEW__UIInterface); // if(state->uiPrgIface != NULL) // { // state->newPrgIface = true; // } // else // { // state->newPrgIface = false; // state->uiPrgIface = (LV2_Programs_UI_Interface *)state->uiDesc->extension_data(LV2_PROGRAMS__UIInterface); // } // } // if(state->hasGui) // { // if(!bEmbed) // { // #ifdef LV2_GUI_USE_QWIDGET // QVBoxLayout* layout = new QVBoxLayout(); // layout->setMargin(0); // layout->setSpacing(0); // layout->addWidget(static_cast(uiW)); // win->setLayout(layout); // #else // win->setCentralWidget(static_cast(uiW)); // #endif // } // else // { // if(bGtk) // { // // #ifdef HAVE_GTK2 // MusEGui::lv2Gtk2Helper_gtk_container_add(state->gtk2Plug, uiW); // MusEGui::lv2Gtk2Helper_gtk_widget_show_all(state->gtk2Plug); // // #ifdef LV2_GUI_USE_GTKPLUG // unsigned long plugX11Id = MusEGui::lv2Gtk2Helper_gdk_x11_drawable_get_xid(state->gtk2Plug); // #else // unsigned long plugX11Id = MusEGui::lv2Gtk2Helper_gtk_window_get_xid(state->gtk2Plug); // #endif // // x11QtWindow = QWindow::fromWinId(plugX11Id); // ewWin = QWidget::createWindowContainer(x11QtWindow, win); // state->pluginQWindow = x11QtWindow; // // #ifdef LV2_GUI_USE_QWIDGET // QVBoxLayout* layout = new QVBoxLayout(); // layout->setMargin(0); // layout->setSpacing(0); // layout->addWidget(ewWin); // win->setLayout(layout); // #else // win->setCentralWidget(ewWin); // #endif // // if(state->uiX11Size.width() == 0 || state->uiX11Size.height() == 0) // { // int w = 0; // int h = 0; // MusEGui::lv2Gtk2Helper_gtk_widget_get_allocation(uiW, &w, &h); // win->setMinimumSize(w, h); // win->resize(w, h); // } // #endif // } // else // { // if(state->uiX11Size.width() == 0 || state->uiX11Size.height() == 0) // win->resize(ewWin->size()); // } // } // // win->show(); // win->setWindowTitle(state->extHost.plugin_human_id); // } // else if(state->hasExternalGui) // { // state->widget = uiW; // LV2_EXTERNAL_UI_SHOW((LV2_External_UI_Widget *)state->widget); // } // // LV2Synth::lv2ui_PostShow(state); // return; // // } // // } // if(win != NULL) // { // win->stopNextTime(); // } // state->pluginWindow = NULL; // state->widget = NULL; // state->uiCurrent = NULL; // // //no ui is shown // state->hasExternalGui = state->hasGui = false; // // } // // // const void *LV2Synth::lv2state_stateRetreive(LV2_State_Handle handle, uint32_t key, size_t *size, uint32_t *type, uint32_t *flags) // { // QMap >::const_iterator it; // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // LV2Synth *synth = state->synth; // const char *cKey = synth->unmapUrid(key); // // assert(cKey != NULL); //should'n happen // // QString strKey = QString(cKey); // it = state->iStateValues.find(strKey); // if(it != state->iStateValues.end()) // { // if(it.value().second.type() == QVariant::ByteArray) // { // QString sType = it.value().first; // QByteArray arrType = sType.toUtf8(); // *type = synth->mapUrid(arrType.constData()); // *flags = LV2_STATE_IS_POD; // QByteArray valArr = it.value().second.toByteArray(); // if(sType.compare(QString(LV2_ATOM__Path)) == 0) //prepend project folder to abstract path // { // QString plugFolder = ((state->sif != NULL) ? state->sif->name() : state->plugInst->name()) + QString("/"); // QString strPath = QString::fromUtf8(valArr.data()); // QFile ff(strPath); // QFileInfo fiPath(ff); // if(fiPath.isRelative()) // { // if(strPath.indexOf(plugFolder) < 0) // { // strPath = plugFolder + strPath; // } // strPath = MusEGlobal::museProject + QString("/") + strPath; // valArr = strPath.toUtf8(); // int len = strPath.length(); // valArr.setRawData(strPath.toUtf8().constData(), len + 1); // valArr [len] = 0; // } // } // size_t i; // size_t numValues = state->numStateValues; // for(i = 0; i < numValues; ++i) // { // if(state->tmpValues [i] == NULL) // break; // } // assert(i < numValues); //sanity check // size_t sz = valArr.size(); // state->iStateValues.remove(strKey); // if(sz > 0) // { // state->tmpValues [i] = new char [sz]; // memset(state->tmpValues [i], 0, sz); // memcpy(state->tmpValues [i], valArr.constData(), sz); // *size = sz; // return state->tmpValues [i]; // } // } // } // // return NULL; // } // // LV2_State_Status LV2Synth::lv2state_stateStore(LV2_State_Handle handle, uint32_t key, const void *value, size_t size, uint32_t type, uint32_t flags) // { // if(flags & (LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE)) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // LV2Synth *synth = state->synth; // const char *uriKey = synth->unmapUrid(key); // const char *uriType = synth->unmapUrid(type); // assert(uriType != NULL && uriKey != NULL); //FIXME: buggy plugin or uridBiMap realization? // QString strKey = QString(uriKey); // QMap >::const_iterator it = state->iStateValues.find(strKey); // if(it == state->iStateValues.end()) // { // QString strUriType = uriType; // QVariant varVal = QByteArray((const char *)value, size); // state->iStateValues.insert(strKey, QPair(strUriType, varVal)); // } // return LV2_STATE_SUCCESS; // } // return LV2_STATE_ERR_BAD_FLAGS; // } LV2_Worker_Status Lv2Plugin::lv2wrk_scheduleWork(LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data) { #ifdef DEBUG_LV2 std::cerr << "LV2Synth::lv2wrk_scheduleWork" << std::endl; #endif LV2PluginWrapper_State *state = static_cast(handle); //assert(state->wrkEndWork != true); if(state->wrkEndWork != false) return LV2_WORKER_ERR_UNKNOWN; state->wrkDataSize = size; state->wrkDataBuffer = data; if(MusEGlobal::audio->freewheel()) //don't wait for a thread. Do it now state->wrkThread->makeWork(); else return state->wrkThread->scheduleWork(); return LV2_WORKER_SUCCESS; } LV2_Worker_Status Lv2Plugin::lv2wrk_respond(LV2_Worker_Respond_Handle handle, uint32_t size, const void *data) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; state->wrkDataSize = size; state->wrkDataBuffer = data; state->wrkEndWork = true; return LV2_WORKER_SUCCESS; } // void LV2Synth::lv2conf_write(LV2PluginWrapper_State *state, int level, Xml &xml) // { // state->iStateValues.clear(); // state->numStateValues = 0; // // if(state->iState != NULL) // { // state->iState->save(lilv_instance_get_handle(state->handle), LV2Synth::lv2state_stateStore, state, LV2_STATE_IS_POD, state->_ppifeatures); // } // // if(state->sif != NULL) // write control ports values only for synths // { // for(size_t c = 0; c < state->sif->_inportsControl; c++) // { // state->iStateValues.insert(QString(state->sif->_controlInPorts [c].cName), QPair(QString(""), QVariant((double)state->sif->_controls[c].val))); // } // } // // if(state->uiCurrent != NULL) // { // const char *cUiUri = lilv_node_as_uri(lilv_ui_get_uri(state->uiCurrent)); // state->iStateValues.insert(QString(cUiUri), QPair(QString(""), QVariant(QString(cUiUri)))); // } // // QByteArray arrOut; // QDataStream streamOut(&arrOut, QIODevice::WriteOnly); // streamOut << state->iStateValues; // QByteArray outEnc64 = arrOut.toBase64(); // QString customData(outEnc64); // for (int pos=0; pos < customData.size(); pos+=150) // { // customData.insert(pos++,'\n'); // add newlines for readability // } // xml.strTag(level, "customData", customData); // } // // void LV2Synth::lv2conf_set(LV2PluginWrapper_State *state, const std::vector &customParams) // { // if(customParams.size() == 0) // return; // // state->iStateValues.clear(); // for(size_t i = 0; i < customParams.size(); i++) // { // QString param = customParams [i]; // param.remove('\n'); // remove all linebreaks that may have been added to prettyprint the songs file // QByteArray paramIn; // paramIn.append(param); // QByteArray dec64 = QByteArray::fromBase64(paramIn); // QDataStream streamIn(&dec64, QIODevice::ReadOnly); // streamIn >> state->iStateValues; // break; //one customData tag includes all data in base64 // } // // size_t numValues = state->iStateValues.size(); // state->numStateValues = numValues; // if(state->iState != NULL && numValues > 0) // { // state->tmpValues = new char*[numValues];; // memset(state->tmpValues, 0, numValues * sizeof(char *)); // state->iState->restore(lilv_instance_get_handle(state->handle), LV2Synth::lv2state_stateRetreive, state, 0, state->_ppifeatures); // for(size_t i = 0; i < numValues; ++i) // { // if(state->tmpValues [i] != NULL) // delete [] state->tmpValues [i]; // } // delete [] state->tmpValues; // state->tmpValues = NULL; // } // // QMap >::const_iterator it; // for(it = state->iStateValues.begin(); it != state->iStateValues.end(); ++it) // { // QString name = it.key(); // QVariant qVal = it.value().second; // if(!name.isEmpty() && qVal.isValid()) // { // if(qVal.type() == QVariant::String) // plugin ui uri // { // QString sUiUri = qVal.toString(); // LV2_PLUGIN_UI_TYPES::iterator it; // for(it = state->synth->_pluginUiTypes.begin(); it != state->synth->_pluginUiTypes.end(); ++it) // { // if(sUiUri == QString(lilv_node_as_uri(lilv_ui_get_uri(it->first)))) // { // state->uiCurrent = it->first; // break; // } // } // } // else // { // if(state->sif != NULL) //setting control value only for synths // { // // bool ok = false; // float val = (float)qVal.toDouble(&ok); // if(ok) // { // std::map::iterator it = state->controlsNameMap.find(name.toLower()); // if(it != state->controlsNameMap.end()) // { // size_t ctrlNum = it->second; // state->sif->_controls [ctrlNum].val = state->sif->_controls [ctrlNum].tmpVal = val; // // } // } // } // } // // // } // } // // } unsigned Lv2Plugin::lv2ui_IsSupported(const char *, const char *ui_type_uri) { if(strcmp(LV2_F_UI_Qt5_UI, ui_type_uri) == 0 || (strcmp(LV2_UI__GtkUI, ui_type_uri) == 0) || strcmp(LV2_UI__X11UI, ui_type_uri) == 0) { return 1; } return 0; } // void LV2Synth::lv2prg_updatePrograms(LV2PluginWrapper_State *state) // { // assert(state != NULL); // state->index2prg.clear(); // state->prg2index.clear(); // if(state->prgIface != NULL) // { // uint32_t iPrg = 0; // const LV2_Program_Descriptor *pDescr = NULL; // while((pDescr = state->prgIface->get_program( // lilv_instance_get_handle(state->handle), iPrg)) != NULL) // { // // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. // uint32_t hb = pDescr->bank >> 8; // uint32_t lb = pDescr->bank & 0xff; // if(hb < 128 && lb < 128 && pDescr->program < 128) // { // lv2ExtProgram extPrg; // extPrg.index = iPrg; // extPrg.bank = pDescr->bank; // extPrg.prog = pDescr->program; // extPrg.useIndex = true; // extPrg.name = QString(pDescr->name); // // state->index2prg.insert(std::make_pair(iPrg, extPrg)); // hb &= 0x7f; // lb &= 0x7f; // uint32_t midiprg = (hb << 16) + (lb << 8) + extPrg.prog; // state->prg2index.insert(std::make_pair(midiprg, iPrg)); // } // ++iPrg; // } // } // // } int Lv2Plugin::lv2_printf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...) { va_list argptr; va_start(argptr, fmt); int ret = lv2_vprintf(handle, type, fmt, argptr); va_end(argptr); return ret; } int Lv2Plugin::lv2_vprintf(LV2_Log_Handle, LV2_URID, const char *fmt, va_list ap) { return vprintf(fmt, ap); } char *Lv2Plugin::lv2state_makePath(LV2_State_Make_Path_Handle handle, const char *path) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; assert(state != NULL); QFile ff(path); QFileInfo fiPath(ff); if(fiPath.isAbsolute()) { return strdup(path); } QString plugName = (state->sif != NULL) ? state->sif->name() : state->plugInst->name(); //------------------------------------------------- // TODO: Get rid of global! Keep a local version. //------------------------------------------------- QString dirName = MusEGlobal::museProject + QString("/") + plugName; QDir dir; dir.mkpath(dirName); QString resPath = dirName + QString("/") + QString(path); return strdup(resPath.toUtf8().constData()); } char *Lv2Plugin::lv2state_abstractPath(LV2_State_Map_Path_Handle handle, const char *absolute_path) { LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; assert(state != NULL); //some plugins do not support abstract paths properly, //so return duplicate without modification for now //return strdup(absolute_path); QString resPath = QString(absolute_path); int rIdx = resPath.lastIndexOf('/'); if(rIdx > -1) { resPath = resPath.mid(rIdx + 1); } QString plugName = (state->sif != NULL) ? state->sif->name() : state->plugInst->name(); QDir dir; QString prjPath = MusEGlobal::museProject + QString("/") + plugName; dir.mkpath(prjPath); QFile ff(absolute_path); QFileInfo fiPath(ff); if(resPath.length() && fiPath.isAbsolute() && resPath != QString(absolute_path)) { QFile::link(QString(absolute_path), prjPath + QString("/") + resPath); } if(strlen(absolute_path) == 0) { resPath = prjPath + QString("/") + resPath; } return strdup(resPath.toUtf8().constData()); } char *Lv2Plugin::lv2state_absolutePath(LV2_State_Map_Path_Handle handle, const char *abstract_path) { return lv2state_makePath((LV2_State_Make_Path_Handle)handle, abstract_path); } // void LV2Synth::lv2state_populatePresetsMenu(LV2PluginWrapper_State *state, MusEGui::PopupMenu *menu) // { // menu->clear(); // menu->setIcon(QIcon(*MusEGui::presetsNewIcon)); // LV2Synth *synth = state->synth; // //this is good by slow down menu population. // //So it's called only on changes (preset save/manual update) // //LV2Synth::lv2state_UnloadLoadPresets(synth, true); // MusEGui::MenuTitleItem *actPresetActionsHeader = new MusEGui::MenuTitleItem(QObject::tr("Preset actions"), menu); // menu->addAction(actPresetActionsHeader); // QAction *actSave = menu->addAction(QObject::tr("Save preset...")); // actSave->setObjectName("lv2state_presets_save_action"); // actSave->setData(QVariant::fromValue(static_cast(lv2CacheNodes.lv2_actionSavePreset))); // QAction *actUpdate = menu->addAction(QObject::tr("Update list")); // actUpdate->setObjectName("lv2state_presets_update_action"); // actUpdate->setData(QVariant::fromValue(static_cast(lv2CacheNodes.lv2_actionUpdatePresets))); // std::map::iterator it; // MusEGui::MenuTitleItem *actSavedPresetsHeader = new MusEGui::MenuTitleItem(QObject::tr("Saved presets"), menu); // menu->addAction(actSavedPresetsHeader); // // for(it = synth->_presets.begin(); it != synth->_presets.end(); ++it) // { // QAction *act = menu->addAction(it->first); // act->setData(QVariant::fromValue(static_cast((it->second)))); // } // if(menu->actions().size() == 0) // { // QAction *act = menu->addAction(QObject::tr("No presets found")); // act->setDisabled(true); // act->setData(QVariant::fromValue(NULL)); // } // // // // } // // void LV2Synth::lv2state_PortWrite(LV2UI_Controller controller, uint32_t port_index, uint32_t buffer_size, uint32_t protocol, const void *buffer, bool fromUi) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)controller; // // assert(state != NULL); //this shouldn't happen // assert(state->inst != NULL || state->sif != NULL); // this too // // if(protocol != 0 && protocol != state->synth->_uAtom_EventTransfer) // { // #ifdef DEBUG_LV2 // std::cerr << "LV2Synth::lv2state_PortWrite: unsupported protocol (" << protocol << ")" << std::endl; // #endif // return; // } // // if(protocol == state->synth->_uAtom_EventTransfer) //put atom transfers to dedicated ring buffer // { // #ifdef DEBUG_LV2 // std::cerr << "LV2Synth::lv2state_PortWrite: atom_EventTransfer, port = " << port_index << ", size =" << buffer_size << std::endl; // #endif // state->uiControlEvt.put(port_index, buffer_size, buffer); // return; // } // // std::map::iterator it = state->synth->_idxToControlMap.find(port_index); // // if(it == state->synth->_idxToControlMap.end()) // { // #ifdef DEBUG_LV2 // std::cerr << "LV2Synth::lv2state_PortWrite: wrong port index (" << port_index << ")" << std::endl; // #endif // return; // } // // uint32_t cport = it->second; // float value = *(float *)buffer; // // Schedules a timed control change: // ControlEvent ce; // ce.unique = false; // ce.fromGui = fromUi; // It came from the plugin's own GUI (or not). // ce.idx = cport; // ce.value = value; // // Don't use timestamp(), because it's circular, which is making it impossible to deal // // with 'modulo' events which slip in 'under the wire' before processing the ring buffers. // ce.frame = MusEGlobal::audio->curFrame(); // // ControlFifo *_controlFifo = NULL; // if(state->inst != NULL) // { // _controlFifo = &state->plugInst->_controlFifo; // if(fromUi) // { // // Record automation: // // Take care of this immediately, because we don't want the silly delay associated with // // processing the fifo one-at-a-time in the apply(). // // NOTE: With some vsts we don't receive control events until the user RELEASES a control. // // So the events all arrive at once when the user releases a control. // // That makes this pretty useless... But what the heck... // //AutomationType at = AUTO_OFF; // if(state->plugInst->_track && state->plugInst->_id != -1) // { // unsigned long id = genACnum(state->plugInst->_id, cport); // state->plugInst->_track->recordAutomation(id, value); // //at = state->plugInst->_track->automationType(); // } // // //state->plugInst->enableController(cport, false); // } // } // else if(state->sif != NULL) // { // _controlFifo = &state->sif->_controlFifo; // if(fromUi) // { // // Record automation: // // Take care of this immediately, because we don't want the silly delay associated with // // processing the fifo one-at-a-time in the apply(). // // NOTE: With some vsts we don't receive control events until the user RELEASES a control. // // So the events all arrive at once when the user releases a control. // // That makes this pretty useless... But what the heck... // if(state->sif->id() != -1) // { // unsigned long pid = genACnum(state->sif->id(), cport); // state->sif->synti->recordAutomation(pid, value); // } // // //state->sif->enableController(cport, false); // } // } // // if(fromUi) // { // state->controlTimers [cport] = 1000 / 30; // 1 sec controllers will not be send to guis // } // // assert(_controlFifo != NULL); // if(_controlFifo->put(ce)) // std::cerr << "LV2Synth::lv2state_PortWrite: fifo overflow: in control number:" << cport << std::endl; // // #ifdef DEBUG_LV2 // std::cerr << "LV2Synth::lv2state_PortWrite: port=" << cport << "(" << port_index << ")" << ", val=" << value << std::endl; // #endif // // // } // // void LV2Synth::lv2state_setPortValue(const char *port_symbol, void *user_data, const void *value, uint32_t size, uint32_t type) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)user_data; // assert(state != NULL); // std::map::iterator it = state->controlsSymMap.find(QString::fromUtf8(port_symbol).toLower()); // if(it != state->controlsSymMap.end()) // { // size_t ctrlNum = it->second; // uint32_t ctrlIdx = state->synth->_controlInPorts [ctrlNum].index; // float fvalue; // if (type == state->atomForge.Float) // { // fvalue = *(const float*)value; // } // else if (type == state->atomForge.Double) // { // fvalue = *(const double*)value; // } // else if (type == state->atomForge.Int) // { // fvalue = *(const int32_t*)value; // } // else if (type == state->atomForge.Long) // { // fvalue = *(const int64_t*)value; // } // else // { // fprintf(stderr, "error: Preset `%s' value has bad type <%s>\n", // port_symbol, state->synth->uridBiMap.unmap(type)); // return; // } // LV2Synth::lv2state_PortWrite((LV2UI_Controller)user_data, ctrlIdx, size, 0, &fvalue, false); // } // // } // // const void *LV2Synth::lv2state_getPortValue(const char *port_symbol, void *user_data, uint32_t *size, uint32_t *type) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)user_data; // assert(state != NULL); // std::map::iterator it = state->controlsSymMap.find(QString::fromUtf8(port_symbol).toLower()); // *size = *type = 0; // if(it != state->controlsSymMap.end()) // { // size_t ctrlNum = it->second; // MusECore::Port *controls = NULL; // // if(state->plugInst != NULL) // { // controls = state->plugInst->controls; // // } // else if(state->sif != NULL) // { // controls = state->sif->_controls; // } // // if(controls != NULL) // { // *size = sizeof(float); // *type = state->atomForge.Float; // return &controls [ctrlNum].val; // } // } // // return NULL; // // } // // void LV2Synth::lv2state_applyPreset(LV2PluginWrapper_State *state, LilvNode *preset) // { // //handle special actions first // if(preset == lv2CacheNodes.lv2_actionSavePreset) // { // bool isOk = false; // QString presetName = QInputDialog::getText(MusEGlobal::muse, QObject::tr("Enter new preset name"), // QObject::tr(("Preset name:")), QLineEdit::Normal, // QString(""), &isOk); // if(isOk && !presetName.isEmpty()) // { // presetName = presetName.trimmed(); // QString synthName = state->synth->name().replace(' ', '_'); // QString presetDir = MusEGlobal::museUser + QString("/.lv2/") // + synthName + QString("_") // + presetName + QString(".lv2/"); // QString presetFile = synthName + QString("_") + presetName // + QString(".ttl"); // QString plugName = (state->sif != NULL) ? state->sif->name() : state->plugInst->name(); // QString plugFileDirName = MusEGlobal::museProject + QString("/") + plugName; // char *cPresetName = strdup(presetName.toUtf8().constData()); // char *cPresetDir = strdup(presetDir.toUtf8().constData()); // char *cPresetFile = strdup(presetFile.toUtf8().constData()); // char *cPlugFileDirName = strdup(plugFileDirName.toUtf8().constData()); // LilvState* const lilvState = lilv_state_new_from_instance(state->synth->_handle, state->handle, &state->synth->_lv2_urid_map, // cPlugFileDirName, cPresetDir, cPresetDir, cPresetDir, // LV2Synth::lv2state_getPortValue, state, // LV2_STATE_IS_POD | LV2_STATE_IS_PORTABLE, NULL); // // lilv_state_set_label(lilvState, cPresetName); // // lilv_state_save(lilvWorld, &state->synth->_lv2_urid_map, // &state->synth->_lv2_urid_unmap, // lilvState, NULL, cPresetDir, // cPresetFile); // // lilv_state_free(lilvState); // free(cPresetName); // free(cPresetDir); // free(cPresetFile); // free(cPlugFileDirName); // LV2Synth::lv2state_UnloadLoadPresets(state->synth, true, true); // // } // // return; // } // else if(preset == lv2CacheNodes.lv2_actionUpdatePresets) // { // LV2Synth::lv2state_UnloadLoadPresets(state->synth, true, true); // return; // } // LilvState* lilvState = lilv_state_new_from_world(lilvWorld, &state->synth->_lv2_urid_map, preset); // if(lilvState) // { // lilv_state_restore(lilvState, state->handle, LV2Synth::lv2state_setPortValue, state, 0, NULL); // lilv_state_free(lilvState); // } // // } void Lv2Plugin::lv2state_UnloadLoadPresets(Lv2Plugin *synth, bool load, bool update) { assert(synth != NULL); //std::cerr << "Lv2Plugin::lv2state_UnloadLoadPresets: handling <" << synth->_name.toStdString() << ">" << std::endl; std::map::iterator it; for(it = synth->_presets.begin(); it != synth->_presets.end(); ++it) { lilv_world_unload_resource(lilvWorld, it->second); lilv_node_free(it->second); } synth->_presets.clear(); if(load) { if(update) { //rescan and refresh user-defined presets first QDirIterator dir_it(MusEGlobal::museUser + QString("/.lv2"), QStringList() << "*.lv2", QDir::Dirs, QDirIterator::NoIteratorFlags); while (dir_it.hasNext()) { QString nextDir = dir_it.next() + QString("/"); std::cerr << nextDir.toStdString() << std::endl; SerdNode sdir = serd_node_new_file_uri((const uint8_t*)nextDir.toUtf8().constData(), 0, 0, 0); LilvNode* ldir = lilv_new_uri(lilvWorld, (const char*)sdir.buf); lilv_world_unload_bundle(lilvWorld, ldir); lilv_world_load_bundle(lilvWorld, ldir); serd_node_free(&sdir); lilv_node_free(ldir); } } //scan for preserts LilvNodes* presets = lilv_plugin_get_related(synth->_handle, lv2CacheNodes.lv2_psetPreset); LILV_FOREACH(nodes, i, presets) { const LilvNode* preset = lilv_nodes_get(presets, i); #ifdef DEBUG_LV2 std::cerr << "\tPreset: " << lilv_node_as_uri(preset) << std::endl; #endif lilv_world_load_resource(lilvWorld, preset); LilvNodes* pLabels = lilv_world_find_nodes(lilvWorld, preset, lv2CacheNodes.lv2_rdfsLabel, NULL); if (pLabels != NULL) { const LilvNode* pLabel = lilv_nodes_get_first(pLabels); synth->_presets.insert(std::make_pair(lilv_node_as_string(pLabel), lilv_node_duplicate(preset))); lilv_nodes_free(pLabels); } else { #ifdef DEBUG_LV2 std::cerr << "\t\tPreset <%s> has no rdfs:label" << lilv_node_as_string(lilv_nodes_get(presets, i)) << std::endl; #endif } } lilv_nodes_free(presets); } } // TODO: prg // void LV2SynthIF::lv2prg_Changed(LV2_Programs_Handle handle, int32_t index) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // #ifdef DEBUG_LV2 // std::cerr << "LV2Synth::lv2prg_Changed: index: " << index << std::endl; // #endif // if(state->sif && state->sif->synti) // { // std::map::iterator itIndex = state->index2prg.find(index); // if(itIndex == state->index2prg.end()) // return; // int ch = 0; // int port = state->sif->synti->midiPort(); // const lv2ExtProgram &extPrg = itIndex->second; // uint32_t hb = extPrg.bank >> 8; // uint32_t lb = extPrg.bank & 0xff; // if(hb > 127 || lb > 127 || extPrg.prog > 127) // return; // hb &= 0x7f; // lb &= 0x7f; // state->sif->synti->setCurrentProg(ch, extPrg.prog, lb, hb); // const int rv = (hb << 16) | (lb << 8) | extPrg.prog; // if(port != -1) // { // MidiPlayEvent event(0, port, ch, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, rv); // //MusEGlobal::midiPorts[port].sendEvent(event); // MusEGlobal::midiPorts[port].sendHwCtrlState(event, false); // if(state->sif->id() != -1 && state->sif->_controls != NULL) // { // for(unsigned long k = 0; k < state->sif->_inportsControl; ++k) // { // state->sif->synti->setPluginCtrlVal(genACnum(state->sif->id(), k), state->sif->_controls[k].val); // } // } // } // } // } // LV2Synth::LV2Synth(const QFileInfo &fi, QString label, QString name, QString author, const LilvPlugin *_plugin, Plugin::PluginFeatures reqFeatures) // : Synth(fi, label, name, author, QString(""), reqFeatures), //Lv2Plugin::Lv2Plugin(const QFileInfo *fi, QString /*label*/, QString /*name*/, QString /*author*/, const LilvPlugin *plugin) Lv2Plugin::Lv2Plugin(const QFileInfo *fi, QString label, QString name, QString author, const LilvPlugin *plugin, PluginFeaturesType requiredFeatures) : Plugin(fi), _handle(plugin), _features(NULL), _ppfeatures(NULL), // _options(NULL), _isSynth(false), _uis(NULL), _hasFreeWheelPort(false), _isConstructed(false), _pluginControlsDefault(NULL), _pluginControlsMin(NULL), _pluginControlsMax(NULL) { _label = label; _name = name; _maker = author; _requiredFeatures = requiredFeatures; //fake id for LV2PluginWrapper functionality _uniqueID = uniqueID++; _midi_event_id = mapUrid(LV2_MIDI__MidiEvent); _uTime_Position = mapUrid(LV2_TIME__Position); _uTime_frame = mapUrid(LV2_TIME__frame); _uTime_speed = mapUrid(LV2_TIME__speed); _uTime_beatsPerMinute = mapUrid(LV2_TIME__beatsPerMinute); _uTime_barBeat = mapUrid(LV2_TIME__barBeat); _uAtom_EventTransfer = mapUrid(LV2_ATOM__eventTransfer); _uAtom_Chunk = mapUrid(LV2_ATOM__Chunk); _uAtom_Sequence = mapUrid(LV2_ATOM__Sequence); _uAtom_StateChanged = mapUrid(LV2_F_STATE_CHANGED); _uAtom_Object = mapUrid(LV2_ATOM__Object); // _sampleRate = (double)MusEGlobal::sampleRate; // _fSampleRate = (float)MusEGlobal::sampleRate; // _iSampleRate = samplerate; // _sampleRate = (double)_iSampleRate; // _fSampleRate = (float)_iSampleRate; // // _segSize = segment_size; /* //prepare features and options arrays LV2_Options_Option _tmpl_options [] = { {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_SAMPLE_RATE), sizeof(float), uridBiMap.map(LV2_ATOM__Float), &_fSampleRate}, {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_MIN_BLKLEN), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &Lv2Plugin::minBlockSize}, //&_segSize}, {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_MAX_BLKLEN), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &_segSize}, {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_NOM_BLKLEN), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &_segSize}, {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_P_SEQ_SIZE), sizeof(int32_t), uridBiMap.map(LV2_ATOM__Int), &_segSize}, {LV2_OPTIONS_INSTANCE, 0, uridBiMap.map(LV2_CORE__sampleRate), sizeof(double), uridBiMap.map(LV2_ATOM__Double), &_sampleRate}, {LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL} }; _options = new LV2_Options_Option[SIZEOF_ARRAY(_tmpl_options)]; // last option is NULLs for(uint32_t i = 0; i < SIZEOF_ARRAY(_tmpl_options); i++) { _options [i] = _tmpl_options [i]; }*/ _features = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; _ppfeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; _lv2_urid_map.map = Synth_Urid_Map; _lv2_urid_map.handle = this; _lv2_urid_unmap.unmap = Synth_Urid_Unmap; _lv2_urid_unmap.handle = this; #ifdef LV2_URI_MAP_SUPPORT _lv2_uri_map.uri_to_id = Synth_Uri_Map; _lv2_uri_map.callback_data = this; #endif _lv2_log_log.handle = this; _lv2_log_log.printf = lv2_printf; _lv2_log_log.vprintf = lv2_vprintf; uint32_t i; for(i = 0; i < SIZEOF_ARRAY(lv2Features); i++) { _features [i] = lv2Features [i]; if(_features [i].URI == NULL) { break; } if(std::string(LV2_F_URID_MAP) == _features [i].URI) { _features [i].data = &_lv2_urid_map; } else if(std::string(LV2_F_URID_UNMAP) == _features [i].URI) { _features [i].data = &_lv2_urid_unmap; } #ifdef LV2_URI_MAP_SUPPORT else if(std::string(LV2_F_URI_MAP) == _features [i].URI) { _features [i].data = &_lv2_uri_map; } #endif else if(std::string(LV2_F_OPTIONS) == _features [i].URI) { // _features [i].data = _options; _fOptions = i; } else if(std::string(LV2_F_INSTANCE_ACCESS) == _features [i].URI) { _fInstanceAccess = i; } else if(std::string(LV2_F_UI_PARENT) == _features [i].URI) { _fUiParent = i; } else if((std::string(LV2_F_UI_EXTERNAL_HOST) == _features [i].URI)) { _fExtUiHost = i; } else if((std::string(LV2_UI_EXTERNAL_DEPRECATED) == _features [i].URI)) { _fExtUiHostD = i; } else if((std::string(LV2_F_WORKER_SCHEDULE) == _features [i].URI)) { _fWrkSchedule = i; } else if((std::string(LV2_UI__resize) == _features [i].URI)) { _fUiResize = i; } else if((std::string(LV2_PROGRAMS__Host) == _features [i].URI)) { _fPrgHost = i; } else if((std::string(LV2_LOG__log) == _features [i].URI)) { _features [i].data = &_lv2_log_log; } else if((std::string(LV2_STATE__makePath) == _features [i].URI)) { _fMakePath = i; } else if((std::string(LV2_STATE__mapPath) == _features [i].URI)) { _fMapPath = i; } else if(std::string(LV2_F_DATA_ACCESS) == _features [i].URI) { _fDataAccess = i; //must be the last! } _ppfeatures [i] = &_features [i]; } _ppfeatures [i] = 0; //enum plugin ports; uint32_t numPorts = lilv_plugin_get_num_ports(_handle); const LilvPort *lilvFreeWheelPort = lilv_plugin_get_port_by_designation(_handle, lv2CacheNodes.lv2_InputPort, lv2CacheNodes.lv2_FreeWheelPort); _pluginControlsDefault = new float [numPorts]; _pluginControlsMin = new float [numPorts]; _pluginControlsMax = new float [numPorts]; memset(_pluginControlsDefault, 0, sizeof(float) * numPorts); memset(_pluginControlsMin, 0, sizeof(float) * numPorts); memset(_pluginControlsMax, 0, sizeof(float) * numPorts); lilv_plugin_get_port_ranges_float(_handle, _pluginControlsMin, _pluginControlsMax, _pluginControlsDefault); for(uint32_t i = 0; i < numPorts; i++) { const LilvPort *_port = lilv_plugin_get_port_by_index(_handle, i); LilvNode *_nPname = lilv_port_get_name(_handle, _port); const LilvNode *_nPsym = lilv_port_get_symbol(_handle, _port); char cAutoGenPortName [1024]; char cAutoGenPortSym [1024]; memset(cAutoGenPortName, 0, sizeof(cAutoGenPortName)); memset(cAutoGenPortSym, 0, sizeof(cAutoGenPortSym)); snprintf(cAutoGenPortName, sizeof(cAutoGenPortName) - 1, "autoport #%u", i); snprintf(cAutoGenPortSym, sizeof(cAutoGenPortSym) - 1, "autoport#%u", i); const char *_portName = cAutoGenPortName; const char *_portSym = cAutoGenPortSym; if(_nPname != 0) { _portName = lilv_node_as_string(_nPname); } if(_nPsym != 0) { _portSym = lilv_node_as_string(_nPsym); } const bool optional = lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_connectionOptional); LV2_MIDI_PORTS *mPorts = &_midiOutPorts; LV2_CONTROL_PORTS *cPorts = &_controlOutPorts; LV2_AUDIO_PORTS *aPorts = &_audioOutPorts; if(lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_InputPort)) { mPorts = &_midiInPorts; cPorts = &_controlInPorts; aPorts = &_audioInPorts; } else if(!lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_OutputPort)) { #ifdef DEBUG_LV2 std::cerr << "plugin has port with unknown direction - ignoring" << std::endl; #endif if(_nPname != 0) lilv_node_free(_nPname); continue; } bool isCVPort = lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_CVPort); if(lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_ControlPort) || isCVPort) { LV2ControlPortType _cType = LV2_PORT_CONTINUOUS; if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portDiscrete)) _cType = LV2_PORT_DISCRETE; else if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portInteger)) _cType = LV2_PORT_INTEGER; else if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portTrigger) || lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portToggled)) _cType = LV2_PORT_TRIGGER; else if(lilv_port_has_property(_handle, _port, lv2CacheNodes.lv2_portLogarithmic)) _cType = LV2_PORT_LOGARITHMIC; cPorts->push_back(LV2ControlPort(_port, i, 0.0f, _portName, _portSym, _cType, isCVPort)); if(std::isnan(_pluginControlsDefault [i])) _pluginControlsDefault [i] = 0; if(std::isnan(_pluginControlsMin [i])) _pluginControlsMin [i] = 0; if(std::isnan(_pluginControlsMax [i])) _pluginControlsMax [i] = 0; if(isCVPort) { _pluginControlsDefault [i] = 1; _pluginControlsMin [i] = 0; _pluginControlsMax [i] = 1; } else if (lilv_port_has_property (_handle, _port, lv2CacheNodes.lv2_SampleRate)) { _pluginControlsDefault [i] *= MusEGlobal::sampleRate; _pluginControlsMin [i] *= MusEGlobal::sampleRate; _pluginControlsMax [i] *= MusEGlobal::sampleRate; } } else if(lilv_port_is_a(_handle, _port, lv2CacheNodes.lv2_AudioPort)) { aPorts->push_back(LV2AudioPort(_port, i, NULL, _portName)); } #ifdef LV2_EVENT_BUFFER_SUPPORT else if(lilv_port_is_a(_handle, _port, lv2CacheNodes.ev_EventPort)) { bool portSupportsTimePos = lilv_port_supports_event(_handle, _port, lv2CacheNodes.lv2_TimePosition); mPorts->push_back(LV2MidiPort(_port, i, _portName, true /* old api is on */,portSupportsTimePos)); } #endif else if(lilv_port_is_a(_handle, _port, lv2CacheNodes.atom_AtomPort)) { bool portSupportsTimePos = lilv_port_supports_event(_handle, _port, lv2CacheNodes.lv2_TimePosition); #ifdef LV2_EVENT_BUFFER_SUPPORT mPorts->push_back(LV2MidiPort(_port, i, _portName, false /* old api is off */, portSupportsTimePos)); #else mPorts->push_back(LV2MidiPort(_port, i, _portName, portSupportsTimePos)); #endif } else if(!optional) { //#ifdef DEBUG_LV2 std::cerr << "plugin has port with unknown type - ignoring plugin " << label.toStdString() << "!" << std::endl; //#endif if(_nPname != 0) lilv_node_free(_nPname); return; } if(_nPname != 0) lilv_node_free(_nPname); } for(uint32_t i = 0; i < _controlInPorts.size(); ++i) { _idxToControlMap.insert(std::pair(_controlInPorts [i].index, i)); if(lilvFreeWheelPort != NULL) { if(lilv_port_get_index(_handle, _controlInPorts [i].port) == lilv_port_get_index(_handle, lilvFreeWheelPort)) { _hasFreeWheelPort = true; _freeWheelPortIndex = i; } } } const LilvPluginClass *cls = lilv_plugin_get_class(_handle); const LilvNode *ncuri = lilv_plugin_class_get_uri(cls); const char *clsname = lilv_node_as_uri(ncuri); if((strcmp(clsname, LV2_INSTRUMENT_CLASS) == 0) && (_midiInPorts.size() > 0)) { _isSynth = true; } const LilvNode *pluginUIType = NULL; _uis = NULL; _uis = lilv_plugin_get_uis(_handle); if(_uis) { LilvIter *it = lilv_uis_begin(_uis); #ifdef DEBUG_LV2 std::cerr << "Plugin support uis of type:" << std::endl; #endif while(!lilv_uis_is_end(_uis, it)) { const LilvUI *ui = lilv_uis_get(_uis, it); if(lilv_ui_is_supported(ui, lv2ui_IsSupported, lv2CacheNodes.host_uiType, &pluginUIType)) { #ifdef DEBUG_LV2 const char *strUiType = lilv_node_as_string(pluginUIType); //internal uis are preferred std::cerr << "Plugin " << label.toStdString() << " supports ui of type " << strUiType << std::endl; #endif #ifndef HAVE_GTK2 const char *cUiUri = lilv_node_as_uri(pluginUIType); if(strcmp(LV2_UI__GtkUI, cUiUri) != 0) #endif _pluginUiTypes.insert(std::make_pair(ui, std::make_pair(false, pluginUIType))); } else { const LilvNodes *nUiClss = lilv_ui_get_classes(ui); LilvIter *nit = lilv_nodes_begin(nUiClss); while(!lilv_nodes_is_end(_uis, nit)) { const LilvNode *nUiCls = lilv_nodes_get(nUiClss, nit); #ifdef DEBUG_LV2 const char *ClsStr = lilv_node_as_string(nUiCls); std::cerr << ClsStr << std::endl; #endif bool extUi = lilv_node_equals(nUiCls, lv2CacheNodes.ext_uiType); bool extdUi = lilv_node_equals(nUiCls, lv2CacheNodes.ext_d_uiType); if(extUi || extdUi) { pluginUIType = extUi ? lv2CacheNodes.ext_uiType : lv2CacheNodes.ext_d_uiType; #ifdef DEBUG_LV2 std::cerr << "Plugin " << label.toStdString() << " supports ui of type " << LV2_UI_EXTERNAL << std::endl; #endif #ifndef HAVE_GTK2 const char *cUiUri = lilv_node_as_uri(pluginUIType); if(strcmp(LV2_UI__GtkUI, cUiUri) != 0) #endif _pluginUiTypes.insert(std::make_pair(ui, std::make_pair(true, pluginUIType))); } nit = lilv_nodes_next(nUiClss, nit); } } it = lilv_uis_next(_uis, it); }; } _presets.clear(); lv2state_UnloadLoadPresets(this, true); _isConstructed = true; } Lv2Plugin::~Lv2Plugin() { lv2state_UnloadLoadPresets(this); if(_ppfeatures) { delete [] _ppfeatures; _ppfeatures = NULL; } if(_features) { delete [] _features; _features = NULL; } // if(_options) // { // delete [] _options; // _options = NULL; // } if(_uis != NULL) { lilv_uis_free(_uis); _uis = NULL; } } //--------------------------------------------------------- // instantiate //--------------------------------------------------------- void* Lv2Plugin::instantiate(float sampleRate, void* data) { // // _sampleRate = sampleRate; // _iSampleRate = sampleRate; // _sampleRate = (double)_iSampleRate; // _fSampleRate = (float)_iSampleRate; // // _segSize = segment_size; // bool success = false; // LADSPA_Handle h = plugin->instantiate(plugin, _sampleRate); // success = (h != NULL); // if (success) // SP_DBG_LADSPA2("Plugin instantiated", label().toLatin1().constData()); // return h; // _synth = s; // // //use LV2Synth features as template // // _state = new LV2PluginWrapper_State; // _state->inst = NULL; // _state->widget = NULL; // _state->uiInst = NULL; // _state->plugInst = NULL; // _state->_ifeatures = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; // _state->_ppifeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; // _state->sif = this; // _state->synth = _synth; // // LV2_Options_Option* options = (LV2_Options_Option*)data; // LV2_Feature *ifeatures = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; // LV2_Feature **ppifeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; // // //LV2Synth::lv2state_FillFeatures(_state); // //lv2state_FillFeatures(_state); // lv2_FillFeatures(ifeatures, ppifeatures, options); //_state->handle = _handle = lilv_plugin_instantiate(_synth->_handle, (double)MusEGlobal::sampleRate, _state->_ppifeatures); //LilvInstance* inst = lilv_plugin_instantiate(_handle, sampleRate, (LV2_Feature**)data); LV2PluginWrapper_State *state = static_cast(data); LilvInstance* inst = lilv_plugin_instantiate(_handle, sampleRate, state->_ppifeatures); // if(_handle == NULL) // { // delete [] _state->_ppifeatures; // _state->_ppifeatures = NULL; // delete [] _state->_ifeatures; // _state->_ifeatures = NULL; // return false; // } // if(inst == NULL) // { // delete [] ppifeatures; // //ppifeatures = NULL; // delete [] ifeatures; // //ifeatures = NULL; // return NULL; // } if(inst) { SP_DBG_LV2_2("Plugin instantiated", label().toLatin1().constData()); } return inst; // _audioInPorts = s->_audioInPorts; // _audioOutPorts = s->_audioOutPorts; // _controlInPorts = s->_controlInPorts; // _controlOutPorts = s->_controlOutPorts; // _inportsMidi = _state->midiInPorts.size(); // _outportsMidi = _state->midiOutPorts.size(); // // // // _inportsControl = _controlInPorts.size(); // _outportsControl = _controlOutPorts.size(); // // if(_inportsControl != 0) // { // _controls = new Port[_inportsControl]; // } // else // { // _controls = NULL; // } // // if(_outportsControl != 0) // { // _controlsOut = new Port[_outportsControl]; // } // else // { // _controlsOut = NULL; // } // // _synth->midiCtl2PortMap.clear(); // _synth->port2MidiCtlMap.clear(); // // for(size_t i = 0; i < _inportsControl; i++) // { // uint32_t idx = _controlInPorts [i].index; // _controls [i].idx = idx; // _controls [i].val = _controls [i].tmpVal = _controlInPorts [i].defVal = _synth->_pluginControlsDefault [idx]; // if(_synth->_hasFreeWheelPort && _synth->_freeWheelPortIndex == i) // _controls [i].enCtrl = false; // else // _controls [i].enCtrl = true; // _controlInPorts [i].minVal = _synth->_pluginControlsMin [idx]; // _controlInPorts [i].maxVal = _synth->_pluginControlsMax [idx]; // // int ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + i; // // // We have a controller number! Insert it and the lv2 port number into both maps. // _synth->midiCtl2PortMap.insert(std::pair(ctlnum, i)); // _synth->port2MidiCtlMap.insert(std::pair(i, ctlnum)); // // int id = genACnum(MAX_PLUGINS, i); // CtrlList *cl; // CtrlListList *cll = track()->controller(); // iCtrlList icl = cll->find(id); // // if(icl == cll->end()) // { // cl = new CtrlList(id); // cll->add(cl); // cl->setCurVal(_controls[i].val); // } // else // { // cl = icl->second; // _controls[i].val = cl->curVal(); // } // // cl->setRange(_synth->_pluginControlsMin [idx], _synth->_pluginControlsMax [idx]); // cl->setName(QString(_controlInPorts [i].cName)); // CtrlValueType vt = VAL_LINEAR; // switch(_controlInPorts [i].cType) // { // case LV2_PORT_CONTINUOUS: // vt = VAL_LINEAR; // break; // case LV2_PORT_DISCRETE: // case LV2_PORT_INTEGER: // vt = VAL_INT; // break; // case LV2_PORT_LOGARITHMIC: // vt = VAL_LOG; // break; // case LV2_PORT_TRIGGER: // vt = VAL_BOOL; // break; // default: // break; // } // // cl->setValueType(vt); // cl->setMode(((_controlInPorts [i].cType == LV2_PORT_CONTINUOUS) // ||(_controlInPorts [i].cType == LV2_PORT_LOGARITHMIC))? CtrlList::INTERPOLATE : CtrlList::DISCRETE); // // if(!_controlInPorts [i].isCVPort) // lilv_instance_connect_port(_handle, idx, &_controls [i].val); // } // // for(size_t i = 0; i < _outportsControl; i++) // { // uint32_t idx = _controlOutPorts [i].index; // _controlsOut[i].idx = idx; // _controlsOut[i].val = 0.0; // _controlsOut[i].tmpVal = 0.0; // _controlsOut[i].enCtrl = false; // _controlOutPorts [i].defVal = _controlOutPorts [i].minVal = _controlOutPorts [i].maxVal = 0.0; // if(!_controlOutPorts [i].isCVPort) // lilv_instance_connect_port(_handle, idx, &_controlsOut[i].val); // } // // // int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); // // if(rv != 0) // { // fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); // abort(); // } // // if(MusEGlobal::config.useDenormalBias) // { // for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) // { // _audioInSilenceBuf[q] = MusEGlobal::denormalBias; // } // } // else // { // memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); // } // // //cache number of ports // _inports = _audioInPorts.size(); // _outports = _audioOutPorts.size(); // // // if(_inports > 0) // { // _audioInBuffers = new float*[_inports]; // // for(size_t i = 0; i < _inports; i++) // { // int rv = posix_memalign((void **)&_audioInBuffers [i], 16, sizeof(float) * MusEGlobal::segmentSize); // // if(rv != 0) // { // fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); // abort(); // } // // if(MusEGlobal::config.useDenormalBias) // { // for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) // { // _audioInBuffers [i][q] = MusEGlobal::denormalBias; // } // } // else // { // memset(_audioInBuffers [i], 0, sizeof(float) * MusEGlobal::segmentSize); // } // // _audioInPorts [i].buffer = _audioInBuffers [i]; // lilv_instance_connect_port(_handle, _audioInPorts [i].index, _audioInBuffers [i]); // } // } // // if(_outports > 0) // { // _audioOutBuffers = new float*[_outports]; // // for(size_t i = 0; i < _outports; i++) // { // int rv = posix_memalign((void **)&_audioOutBuffers [i], 16, sizeof(float) * MusEGlobal::segmentSize); // // if(rv != 0) // { // fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); // abort(); // } // // if(MusEGlobal::config.useDenormalBias) // { // for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) // { // _audioOutBuffers [i][q] = MusEGlobal::denormalBias; // } // } // else // { // memset(_audioOutBuffers [i], 0, sizeof(float) * MusEGlobal::segmentSize); // } // // _audioOutPorts [i].buffer = _audioOutBuffers [i]; // lilv_instance_connect_port(_handle, _audioOutPorts [i].index, _audioOutBuffers [i]); // } // } // // LV2Synth::lv2state_PostInstantiate(_state); // activate(); // // // // return true; } //--------------------------------------------------------- // incReferences //--------------------------------------------------------- int Lv2Plugin::incReferences(int val) { #ifdef PLUGIN_DEBUGIN fprintf(stderr, "Lv2Plugin::incReferences _references:%d val:%d\n", _references, val); #endif int newref = _references + val; if(newref <= 0) { _references = 0; if(_libHandle) { #ifdef PLUGIN_DEBUGIN //fprintf(stderr, "Lv2Plugin::incReferences no more instances, closing library\n"); fprintf(stderr, "Lv2Plugin::incReferences Error: _libHandle is not NULL\n"); #endif //dlclose(_libHandle); } _libHandle = 0; //_plugin = NULL; // _pIdx.clear(); // _poIdx.clear(); // _iIdx.clear(); // _oIdx.clear(); return 0; } // if(_libHandle == 0) // { // _libHandle = dlopen(_fi.filePath().toLatin1().constData(), RTLD_NOW); // // if(_libHandle == 0) // { // fprintf(stderr, "Lv2Plugin::incReferences dlopen(%s) failed: %s\n", // _fi.filePath().toLatin1().constData(), dlerror()); // return 0; // } // // LADSPA_Descriptor_Function ladspadf = (LADSPA_Descriptor_Function)dlsym(_libHandle, "ladspa_descriptor"); // if(ladspadf) // { // const LADSPA_Descriptor* descr; // for(unsigned long i = 0;; ++i) // { // descr = ladspadf(i); // if(descr == NULL) // break; // // QString desc_label(descr->Label); // if(desc_label == label()) // { // _plugin = descr; // break; // } // } // } // // if(_plugin != NULL) // { // _name = QString(_plugin->Name); // _uniqueID = _plugin->UniqueID; // _maker = QString(_plugin->Maker); // _copyright = QString(_plugin->Copyright); // // _portCount = _plugin->PortCount; // // _inports = 0; // _outports = 0; // _controlInPorts = 0; // _controlOutPorts = 0; // // for(unsigned long k = 0; k < _portCount; ++k) // { // LADSPA_PortDescriptor pd = _plugin->PortDescriptors[k]; // if(pd & LADSPA_PORT_AUDIO) // { // if(pd & LADSPA_PORT_INPUT) // { // ++_inports; // _iIdx.push_back(k); // } // else // if(pd & LADSPA_PORT_OUTPUT) // { // ++_outports; // _oIdx.push_back(k); // } // } // else // if(pd & LADSPA_PORT_CONTROL) // { // if(pd & LADSPA_PORT_INPUT) // { // ++_controlInPorts; // _pIdx.push_back(k); // } // else // if(pd & LADSPA_PORT_OUTPUT) // { // ++_controlOutPorts; // _poIdx.push_back(k); // } // } // } // } // } // if(_plugin == NULL) // { // dlclose(_libHandle); // _libHandle = 0; // _references = 0; // fprintf(stderr, "Lv2Plugin::incReferences Error: %s no plugin!\n", _fi.filePath().toLatin1().constData()); // return 0; // } // if ((_inports != _outports) || (LADSPA_IS_INPLACE_BROKEN(_plugin->Properties))) // _requiredFeatures |= NoInPlaceProcessing; _references = newref; return _references; } // SynthIF *LV2Synth::createSIF(SynthI *synthi) // { // ++_instances; // LV2SynthIF *sif = new LV2SynthIF(synthi); // // if(!sif->init(this)) // { // delete sif; // sif = NULL; // } // // return sif; // } LV2_URID Lv2Plugin::mapUrid(const char *uri) { return uridBiMap.map(uri); } const char *Lv2Plugin::unmapUrid(LV2_URID id) { return uridBiMap.unmap(id); } // LV2SynthIF::~LV2SynthIF() // { // if(_state != NULL) // { // _state->deleteLater = true; // //_uiState->uiTimer->stopNextTime(false); // if(_state->pluginWindow != NULL) // _state->pluginWindow->stopNextTime(); // else // LV2Synth::lv2state_FreeState(_state); // _state = NULL; // // } // // LV2_AUDIO_PORTS::iterator _itA = _audioInPorts.begin(); // // for(; _itA != _audioInPorts.end(); ++_itA) // { // free((*_itA).buffer); // } // // _itA = _audioOutPorts.begin(); // // for(; _itA != _audioOutPorts.end(); ++_itA) // { // free((*_itA).buffer); // } // // if(_audioInSilenceBuf) // free(_audioInSilenceBuf); // // if(_audioInBuffers) // { // delete [] _audioInBuffers; // _audioInBuffers = NULL; // } // // if(_audioOutBuffers) // { // delete [] _audioOutBuffers; // _audioOutBuffers = NULL; // } // // if(_controls) // { // delete [] _controls; // } // // if(_controlsOut) // { // delete [] _controlsOut; // } // // if(_ppifeatures) // { // delete [] _ppifeatures; // _ppifeatures = NULL; // } // // if(_ifeatures) // { // delete [] _ifeatures; // _ifeatures = NULL; // } // } // // LV2SynthIF::LV2SynthIF(SynthI *s): SynthIF(s) // { // _synth = NULL; // _handle = NULL; // _audioInBuffers = NULL; // _audioOutBuffers = NULL; // _inports = 0; // _outports = 0; // _controls = NULL; // _controlsOut = NULL; // _inportsControl = 0; // _outportsControl = 0; // _inportsMidi = 0, // _outportsMidi = 0, // _audioInSilenceBuf = NULL; // _ifeatures = NULL; // _ppifeatures = NULL; // _state = NULL; // } // // bool LV2SynthIF::init(LV2Synth *s) // { // _synth = s; // // //use LV2Synth features as template // // _state = new LV2PluginWrapper_State; // _state->inst = NULL; // _state->widget = NULL; // _state->uiInst = NULL; // _state->plugInst = NULL; // _state->_ifeatures = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; // _state->_ppifeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; // _state->sif = this; // _state->synth = _synth; // // LV2Synth::lv2state_FillFeatures(_state); // // _state->handle = _handle = lilv_plugin_instantiate(_synth->_handle, (double)MusEGlobal::sampleRate, _state->_ppifeatures); // // if(_handle == NULL) // { // delete [] _state->_ppifeatures; // _state->_ppifeatures = NULL; // delete [] _state->_ifeatures; // _state->_ifeatures = NULL; // return false; // } // // _audioInPorts = s->_audioInPorts; // _audioOutPorts = s->_audioOutPorts; // _controlInPorts = s->_controlInPorts; // _controlOutPorts = s->_controlOutPorts; // _inportsMidi = _state->midiInPorts.size(); // _outportsMidi = _state->midiOutPorts.size(); // // // // _inportsControl = _controlInPorts.size(); // _outportsControl = _controlOutPorts.size(); // // if(_inportsControl != 0) // { // _controls = new Port[_inportsControl]; // } // else // { // _controls = NULL; // } // // if(_outportsControl != 0) // { // _controlsOut = new Port[_outportsControl]; // } // else // { // _controlsOut = NULL; // } // // _synth->midiCtl2PortMap.clear(); // _synth->port2MidiCtlMap.clear(); // // for(size_t i = 0; i < _inportsControl; i++) // { // uint32_t idx = _controlInPorts [i].index; // _controls [i].idx = idx; // _controls [i].val = _controls [i].tmpVal = _controlInPorts [i].defVal = _synth->_pluginControlsDefault [idx]; // if(_synth->_hasFreeWheelPort && _synth->_freeWheelPortIndex == i) // _controls [i].enCtrl = false; // else // _controls [i].enCtrl = true; // _controlInPorts [i].minVal = _synth->_pluginControlsMin [idx]; // _controlInPorts [i].maxVal = _synth->_pluginControlsMax [idx]; // // int ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + i; // // // We have a controller number! Insert it and the lv2 port number into both maps. // _synth->midiCtl2PortMap.insert(std::pair(ctlnum, i)); // _synth->port2MidiCtlMap.insert(std::pair(i, ctlnum)); // // int id = genACnum(MAX_PLUGINS, i); // CtrlList *cl; // CtrlListList *cll = track()->controller(); // iCtrlList icl = cll->find(id); // // if(icl == cll->end()) // { // cl = new CtrlList(id); // cll->add(cl); // cl->setCurVal(_controls[i].val); // } // else // { // cl = icl->second; // _controls[i].val = cl->curVal(); // } // // cl->setRange(_synth->_pluginControlsMin [idx], _synth->_pluginControlsMax [idx]); // cl->setName(QString(_controlInPorts [i].cName)); // CtrlValueType vt = VAL_LINEAR; // switch(_controlInPorts [i].cType) // { // case LV2_PORT_CONTINUOUS: // vt = VAL_LINEAR; // break; // case LV2_PORT_DISCRETE: // case LV2_PORT_INTEGER: // vt = VAL_INT; // break; // case LV2_PORT_LOGARITHMIC: // vt = VAL_LOG; // break; // case LV2_PORT_TRIGGER: // vt = VAL_BOOL; // break; // default: // break; // } // // cl->setValueType(vt); // cl->setMode(((_controlInPorts [i].cType == LV2_PORT_CONTINUOUS) // ||(_controlInPorts [i].cType == LV2_PORT_LOGARITHMIC))? CtrlList::INTERPOLATE : CtrlList::DISCRETE); // // if(!_controlInPorts [i].isCVPort) // lilv_instance_connect_port(_handle, idx, &_controls [i].val); // } // // for(size_t i = 0; i < _outportsControl; i++) // { // uint32_t idx = _controlOutPorts [i].index; // _controlsOut[i].idx = idx; // _controlsOut[i].val = 0.0; // _controlsOut[i].tmpVal = 0.0; // _controlsOut[i].enCtrl = false; // _controlOutPorts [i].defVal = _controlOutPorts [i].minVal = _controlOutPorts [i].maxVal = 0.0; // if(!_controlOutPorts [i].isCVPort) // lilv_instance_connect_port(_handle, idx, &_controlsOut[i].val); // } // // // int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * MusEGlobal::segmentSize); // // if(rv != 0) // { // fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); // abort(); // } // // if(MusEGlobal::config.useDenormalBias) // { // for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) // { // _audioInSilenceBuf[q] = MusEGlobal::denormalBias; // } // } // else // { // memset(_audioInSilenceBuf, 0, sizeof(float) * MusEGlobal::segmentSize); // } // // //cache number of ports // _inports = _audioInPorts.size(); // _outports = _audioOutPorts.size(); // // // if(_inports > 0) // { // _audioInBuffers = new float*[_inports]; // // for(size_t i = 0; i < _inports; i++) // { // int rv = posix_memalign((void **)&_audioInBuffers [i], 16, sizeof(float) * MusEGlobal::segmentSize); // // if(rv != 0) // { // fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); // abort(); // } // // if(MusEGlobal::config.useDenormalBias) // { // for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) // { // _audioInBuffers [i][q] = MusEGlobal::denormalBias; // } // } // else // { // memset(_audioInBuffers [i], 0, sizeof(float) * MusEGlobal::segmentSize); // } // // _audioInPorts [i].buffer = _audioInBuffers [i]; // lilv_instance_connect_port(_handle, _audioInPorts [i].index, _audioInBuffers [i]); // } // } // // if(_outports > 0) // { // _audioOutBuffers = new float*[_outports]; // // for(size_t i = 0; i < _outports; i++) // { // int rv = posix_memalign((void **)&_audioOutBuffers [i], 16, sizeof(float) * MusEGlobal::segmentSize); // // if(rv != 0) // { // fprintf(stderr, "ERROR: LV2SynthIF::init: posix_memalign returned error:%d. Aborting!\n", rv); // abort(); // } // // if(MusEGlobal::config.useDenormalBias) // { // for(unsigned q = 0; q < MusEGlobal::segmentSize; ++q) // { // _audioOutBuffers [i][q] = MusEGlobal::denormalBias; // } // } // else // { // memset(_audioOutBuffers [i], 0, sizeof(float) * MusEGlobal::segmentSize); // } // // _audioOutPorts [i].buffer = _audioOutBuffers [i]; // lilv_instance_connect_port(_handle, _audioOutPorts [i].index, _audioOutBuffers [i]); // } // } // // LV2Synth::lv2state_PostInstantiate(_state); // activate(); // // // // return true; // // } // // void LV2SynthIF::doSelectProgram(unsigned char channel, int bankH, int bankL, int prog) // { // // // Only if there's something to change... // // if(bankH >= 128 && bankL >= 128 && prog >= 128) // // return; // // if(bankH > 127) // Map "dont care" to 0 // bankH = 0; // if(bankL > 127) // bankL = 0; // if(prog > 127) // prog = 0; // // const int bank = (bankH << 8) | bankL; // // if(_state && _state->prgIface && (_state->prgIface->select_program || _state->prgIface->select_program_for_channel)) // { // if(_state->newPrgIface) // _state->prgIface->select_program_for_channel(lilv_instance_get_handle(_state->handle), channel, (uint32_t)bank, (uint32_t)prog); // else // _state->prgIface->select_program(lilv_instance_get_handle(_state->handle), (uint32_t)bank, (uint32_t)prog); // // // // /* // * A plugin is permitted to re-write the values of its input // * control ports when select_program is called. The host should // * re-read the input control port values and update its own // * records appropriately. (This is the only circumstance in which // * a LV2 plugin is allowed to modify its own control-input ports.) // */ // if(id() != -1) // { // for(unsigned long k = 0; k < _inportsControl; ++k) // { // // We're in the audio thread context: no need to send a message, just modify directly. // synti->setPluginCtrlVal(genACnum(id(), k), _controls[k].val); // } // } // // //set update ui program flag // _state->uiChannel = channel; // _state->uiBank = bank; // _state->uiProg = prog; // _state->uiDoSelectPrg = true; // } // } // // void LV2SynthIF::sendLv2MidiEvent(LV2EvBuf *evBuf, long frame, int paramCount, uint8_t a, uint8_t b, uint8_t c) // { // if(paramCount < 1 || paramCount > 3) // return; // // if(evBuf) // { // uint8_t midiEv [paramCount]; // midiEv [0] = a; // if(paramCount >= 2) // midiEv [1] = b; // if(paramCount == 3) // midiEv [2] = c; // #ifdef LV2_EVENT_BUFFER_SUPPORT // evBuf->write(frame, 0, _synth->_midi_event_id, paramCount, midiEv); // #else // evBuf->write(frame, _synth->_midi_event_id, paramCount, midiEv); // #endif // } // } // // int LV2SynthIF::channels() const // { // return (_outports) > MAX_CHANNELS ? MAX_CHANNELS : (_outports) ; // // } // // int LV2SynthIF::totalInChannels() const // { // return _inports; // } // // int LV2SynthIF::totalOutChannels() const // { // return _outports; // } // // void LV2SynthIF::activate() // { // if(_handle) // { // lilv_instance_activate(_handle); // } // } // // // void LV2SynthIF::deactivate() // { // if(_handle) // { // lilv_instance_deactivate(_handle); // } // // } // // void LV2SynthIF::deactivate3() // { // deactivate(); // } // // int LV2SynthIF::eventsPending() const // { // //TODO: what's this? // return 0; // } // // bool LV2SynthIF::lv2MidiControlValues(size_t port, int ctlnum, int *min, int *max, int *def) // { // // float fmin, fmax, fdef; // int imin; // float frng; // // fdef = _controlInPorts [port].defVal; // fmin = _controlInPorts [port].minVal; // fmax = _controlInPorts [port].maxVal; // bool hasdef = (fdef == fdef); // // if(fmin != fmin) // { // fmin = 0.0; // } // // if(fmax != fmax) // { // fmax = 0.0; // } // // MidiController::ControllerType t = midiControllerType(ctlnum); // // #ifdef PLUGIN_DEBUGIN // printf("lv2MidiControlValues: ctlnum:%d ladspa port:%lu has default?:%d default:%f\n", ctlnum, port, hasdef, fdef); // #endif // // // frng = fmax - fmin; // imin = lrintf(fmin); // //imax = lrintf(fmax); // // int ctlmn = 0; // int ctlmx = 127;// Avoid divide-by-zero error below. // // // #ifdef PLUGIN_DEBUGIN // printf("lv2MidiControlValues: port min:%f max:%f \n", fmin, fmax); // #endif // // bool isneg = (imin < 0); // int bias = 0; // // switch(t) // { // case MidiController::RPN: // case MidiController::NRPN: // case MidiController::Controller7: // if(isneg) // { // ctlmn = -64; // ctlmx = 63; // bias = -64; // } // else // { // ctlmn = 0; // ctlmx = 127; // } // // break; // // case MidiController::Controller14: // case MidiController::RPN14: // case MidiController::NRPN14: // if(isneg) // { // ctlmn = -8192; // ctlmx = 8191; // bias = -8192; // } // else // { // ctlmn = 0; // ctlmx = 16383; // } // // break; // // case MidiController::Program: // ctlmn = 0; // ctlmx = 0x3fff; // FIXME: Really should not happen or be allowed. What to do here... // break; // // case MidiController::Pitch: // ctlmn = -8192; // ctlmx = 8191; // break; // // case MidiController::Velo: // cannot happen // default: // break; // } // // float fctlrng = float(ctlmx - ctlmn); // // // // It's a floating point control, just use wide open maximum range. // *min = ctlmn; // *max = ctlmx; // // float normdef = (frng == 0.0) ? 0.0 : fdef / frng; // fdef = normdef * fctlrng; // // // FIXME: TODO: Incorrect... Fix this somewhat more trivial stuff later.... // // *def = (int)lrintf(fdef) + bias; // // #ifdef PLUGIN_DEBUGIN // printf("lv2MidiControlValues: setting default:%d\n", *def); // #endif // // return hasdef; // } // // //--------------------------------------------------------- // // midi2LadspaValue // //--------------------------------------------------------- // // float LV2SynthIF::midi2Lv2Value(unsigned long port, int ctlnum, int val) // { // // float fmin, fmax; // int imin; // float frng; // // MidiController::ControllerType t = midiControllerType(ctlnum); // // #ifdef PLUGIN_DEBUGIN // printf("midi2Lv2Value: ctlnum:%d port:%lu val:%d\n", ctlnum, port, val); // #endif // // fmin = _controlInPorts [port].minVal; // fmax = _controlInPorts [port].maxVal; // // if(fmin != fmin) // { // fmin = 0.0; // } // // if(fmax != fmax) // { // fmax = 0.0; // } // // // // frng = fmax - fmin; // imin = lrintf(fmin); // // // int ctlmn = 0; // int ctlmx = 127; // // #ifdef PLUGIN_DEBUGIN // printf("midi2Lv2Value: port min:%f max:%f \n", fmin, fmax); // #endif // // bool isneg = (imin < 0); // int bval = val; // //int cval = val; // // switch(t) // { // case MidiController::RPN: // case MidiController::NRPN: // case MidiController::Controller7: // if(isneg) // { // ctlmn = -64; // ctlmx = 63; // bval -= 64; // //cval -= 64; // } // else // { // ctlmn = 0; // ctlmx = 127; // //cval -= 64; // } // // break; // // case MidiController::Controller14: // case MidiController::RPN14: // case MidiController::NRPN14: // if(isneg) // { // ctlmn = -8192; // ctlmx = 8191; // bval -= 8192; // //cval -= 8192; // } // else // { // ctlmn = 0; // ctlmx = 16383; // //cval -= 8192; // } // // break; // // case MidiController::Program: // ctlmn = 0; // ctlmx = 0xffffff; // break; // // case MidiController::Pitch: // ctlmn = -8192; // ctlmx = 8191; // break; // // case MidiController::Velo: // cannot happen // default: // break; // } // // int ctlrng = ctlmx - ctlmn; // float fctlrng = float(ctlmx - ctlmn); // // // // Avoid divide-by-zero error below. // if(ctlrng == 0) // { // return 0.0; // } // // // It's a floating point control, just use wide open maximum range. // float normval = float(bval) / fctlrng; // float ret = normval * frng + fmin; // // #ifdef PLUGIN_DEBUGIN // printf("midi2Lv2Value: float returning:%f\n", ret); // #endif // // return ret; // } // // int LV2SynthIF::getControllerInfo(int id, QString* name, int *ctrl, int *min, int *max, int *initval) // { // size_t _id = (size_t)id; // // if(_id == _inportsControl || _id == _inportsControl + 1) // { // // // // It is unknown at this point whether or not a synth recognizes aftertouch and poly aftertouch // // (channel and key pressure) midi messages, so add support for them now (as controllers). // // // if(_id == _inportsControl) // { // *ctrl = CTRL_POLYAFTER; // } // else if(_id == _inportsControl + 1) // { // *ctrl = CTRL_AFTERTOUCH; // } // // *min = 0; // *max = 127; // *initval = CTRL_VAL_UNKNOWN; // *name = midiCtrlName(*ctrl); // return ++_id; // } // else if(_id >= _inportsControl + 2) // { // return 0; // } // // int ctlnum; // = DSSI_NONE; // // // No controller number? Give it one. // //if(ctlnum == DSSI_NONE) // //{ // // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. // ctlnum = CTRL_NRPN14_OFFSET + 0x2000 + _id; // //} // // // int def = CTRL_VAL_UNKNOWN; // // if(lv2MidiControlValues(_id, ctlnum, min, max, &def)) // { // *initval = def; // } // else // { // *initval = CTRL_VAL_UNKNOWN; // } // // #ifdef DEBUG_LV2 // printf("LV2SynthIF::getControllerInfo passed ctlnum:%d min:%d max:%d initval:%d\n", ctlnum, *min, *max, *initval); // #endif // // *ctrl = ctlnum; // *name = QString(_controlInPorts[_id].cName); // return ++_id; // // } // // // // bool LV2SynthIF::processEvent(const MidiPlayEvent &e, LV2EvBuf *evBuf, long frame) // { // int chn = e.channel(); // int a = e.dataA(); // int b = e.dataB(); // int type = e.type(); // // #ifdef LV2_DEBUG // fprintf(stderr, "LV2SynthIF::processEvent midi event type:%d chn:%d a:%d b:%d\n", e.type(), chn, a, b); // #endif // // // REMOVE Tim. Noteoff. Added. // const MidiInstrument::NoteOffMode nom = synti->noteOffMode(); // // switch(type) // { // case ME_NOTEON: // #ifdef LV2_DEBUG // fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_NOTEON\n"); // #endif // // REMOVE Tim. Noteoff. Changed. // // if(b) // // { // // sendLv2MidiEvent(evBuf, frame, (type | chn) & 0xff, a & 0x7f, b & 0x7f); // // } // // else // // { // // sendLv2MidiEvent(evBuf, frame, (ME_NOTEOFF | chn) & 0xff, a & 0x7f, 0); // // } // if(b == 0) // { // // Handle zero-velocity note ons. Technically this is an error because internal midi paths // // are now all 'note-off' without zero-vel note ons - they're converted to note offs. // // Nothing should be setting a Note type Event's on velocity to zero. // // But just in case... If we get this warning, it means there is still code to change. // fprintf(stderr, "LV2SynthIF::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n", e.time(), e.type(), chn, a, b); // switch(nom) // { // // Instrument uses note offs. Convert to zero-vel note off. // case MidiInstrument::NoteOffAll: // //if(MusEGlobal::midiOutputTrace) // // fprintf(stderr, "MidiOut: LV2: Following event will be converted to zero-velocity note off:\n"); // sendLv2MidiEvent(evBuf, frame, 3, (ME_NOTEOFF | chn) & 0xff, a & 0x7f, 0); // break; // // // Instrument uses no note offs at all. Send as-is. // case MidiInstrument::NoteOffNone: // // Instrument converts all note offs to zero-vel note ons. Send as-is. // case MidiInstrument::NoteOffConvertToZVNoteOn: // sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); // break; // } // } // else // sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); // // break; // // case ME_NOTEOFF: // #ifdef LV2_DEBUG // fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_NOTEOFF\n"); // #endif // // // REMOVE Tim. Noteoff. Changed. // // sendLv2MidiEvent(evBuf, frame, (type | chn) & 0xff, a & 0x7f, b & 0x7f); // switch(nom) // { // // Instrument uses note offs. Send as-is. // case MidiInstrument::NoteOffAll: // sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); // break; // // // Instrument uses no note offs at all. Send nothing. Eat up the event - return false. // case MidiInstrument::NoteOffNone: // return false; // // // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on. // case MidiInstrument::NoteOffConvertToZVNoteOn: // //if(MusEGlobal::midiOutputTrace) // // fprintf(stderr, "MidiOut: LV2: Following event will be converted to zero-velocity note on:\n"); // sendLv2MidiEvent(evBuf, frame, 3, (ME_NOTEON | chn) & 0xff, a & 0x7f, 0); // break; // } // // break; // // case ME_PROGRAM: // { // #ifdef LV2_DEBUG // fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_PROGRAM\n"); // #endif // // int hb, lb; // synti->currentProg(chn, NULL, &lb, &hb); // synti->setCurrentProg(chn, a & 0xff, lb, hb); // doSelectProgram(chn, hb, lb, a); // // // Event pointer not filled. Return false. // return false; // } // break; // // case ME_CONTROLLER: // { // #ifdef LV2_DEBUG // fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER\n"); // #endif // // // Our internal hwCtrl controllers support the 'unknown' value. // // Don't send 'unknown' values to the driver. Ignore and return no error. // if(b == CTRL_VAL_UNKNOWN) // return false; // // if(a == CTRL_PROGRAM) // { // #ifdef LV2_DEBUG // fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PROGRAM\n"); // #endif // // int hb = (b >> 16) & 0xff; // int lb = (b >> 8) & 0xff; // int pr = b & 0xff; // synti->setCurrentProg(chn, pr, lb, hb); // doSelectProgram(chn, hb, lb, pr); // // // Event pointer not filled. Return false. // return false; // } // // if(a == CTRL_HBANK) // { // int lb, pr; // synti->currentProg(chn, &pr, &lb, NULL); // synti->setCurrentProg(chn, pr, lb, b & 0xff); // doSelectProgram(chn, b, lb, pr); // // Event pointer not filled. Return false. // return false; // } // // if(a == CTRL_LBANK) // { // int hb, pr; // synti->currentProg(chn, &pr, NULL, &hb); // synti->setCurrentProg(chn, pr, b & 0xff, hb); // doSelectProgram(chn, hb, b, pr); // // Event pointer not filled. Return false. // return false; // } // // if(a == CTRL_PITCH) // { // #ifdef LV2_DEBUG // fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_PITCH\n"); // #endif // // b += 8192; // sendLv2MidiEvent(evBuf, frame, 3, (ME_PITCHBEND | chn) & 0xff, b & 0x7f, (b >> 7) & 0x7f); // // Event pointer filled. Return true. // return true; // } // // if(a == CTRL_AFTERTOUCH) // { // #ifdef LV2_DEBUG // fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_AFTERTOUCH\n"); // #endif // sendLv2MidiEvent(evBuf, frame, 2, (ME_AFTERTOUCH | chn) & 0xff, b & 0x7f, 0); // // Event pointer filled. Return true. // return true; // } // // if((a | 0xff) == CTRL_POLYAFTER) // { // #ifdef LV2_DEBUG // fprintf(stderr, "LV2SynthIF::processEvent midi event is ME_CONTROLLER, dataA is CTRL_POLYAFTER\n"); // #endif // sendLv2MidiEvent(evBuf, frame, 3, (ME_POLYAFTER | chn) & 0xff, a & 0x7f, b & 0x7f); // // Event pointer filled. Return true. // return true; // } // // ciMidiCtl2LadspaPort ip = _synth->midiCtl2PortMap.find(a); // // // Is it just a regular midi controller, not mapped to a LADSPA port (either by the plugin or by us)? // // NOTE: There's no way to tell which of these controllers is supported by the plugin. // // For example sustain footpedal or pitch bend may be supported, but not mapped to any LADSPA port. // if(ip == _synth->midiCtl2PortMap.end()) // { // // if(midiControllerType(a) == MidiController::NRPN14 // || midiControllerType(a) == MidiController::NRPN) // { // //send full nrpn control sequence (4 values): // // 99 + ((a & 0xff00) >> 8) - first byte // // 98 + (a & 0xff) - second byte // // 6 + ((b & 0x3f80) >> 7) - third byte // // 38 + (b & 0x7f) - fourth byte // // // sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 99, ((a & 0xff00) >> 8)); // sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 98, (a & 0xff)); // sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 6, ((b & 0x3f80) >> 7)); // sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, 38, (b & 0x7f)); // return true; // // } // else if(midiControllerType(a) != MidiController::Controller7) // { // return false; // Event pointer not filled. Return false. // } // // // Fill the event. // #ifdef LV2_DEBUG // printf("LV2SynthIF::processEvent non-ladspa filling midi event chn:%d dataA:%d dataB:%d\n", chn, a, b); // #endif // sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); // return true; // } // // unsigned long k = ip->second; // int ctlnum; // = DSSI_NONE; // // // No midi controller for the ladspa port? Send to ladspa control. // //if(ctlnum == DSSI_NONE) // //{ // // Sanity check. // if(k > _inportsControl) // { // return false; // } // // // Simple but flawed solution: Start them at 0x60000 + 0x2000 = 0x62000. Max NRPN number is 0x3fff. // ctlnum = k + (CTRL_NRPN14_OFFSET + 0x2000); // //} // // // float val = midi2Lv2Value(k, ctlnum, b); // // #ifdef LV2_DEBUG // //fprintf(stderr, "LV2SynthIF::processEvent control port:%lu port:%lu dataA:%d Converting val from:%d to lv2:%f\n", i, k, a, b, val); // fprintf(stderr, "LV2SynthIF::processEvent port:%lu dataA:%d Converting val from:%d to lv2:%f\n", k, a, b, val); // #endif // // // Set the lv2 port value. // _controls[k].val = val; // // // Need to update the automation value, otherwise it overwrites later with the last automation value. // if(id() != -1) // // We're in the audio thread context: no need to send a message, just modify directly. // { // synti->setPluginCtrlVal(genACnum(id(), k), val); // } // // // Since we absorbed the message as a lv2 control change, return false - the event is not filled. // return false; // } // break; // // case ME_PITCHBEND: // a += 8192; // sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, (a >> 7) & 0x7f); // break; // // case ME_AFTERTOUCH: // sendLv2MidiEvent(evBuf, frame, 2, (type | chn) & 0xff, a & 0x7f, 0); // break; // // case ME_POLYAFTER: // sendLv2MidiEvent(evBuf, frame, 3, (type | chn) & 0xff, a & 0x7f, b & 0x7f); // break; // // default: // if(MusEGlobal::debugMsg) // { // fprintf(stderr, "LV2SynthIF::processEvent midi event unknown type:%d\n", e.type()); // } // // // Event not filled. // return false; // break; // } // // return true; // // } // // // bool LV2SynthIF::getData(MidiPort *, unsigned int pos, int ports, unsigned int nframes, float **buffer) // { // const unsigned int syncFrame = MusEGlobal::audio->curSyncFrame(); // // All ports must be connected to something! // const unsigned long nop = ((unsigned long) ports) > _outports ? _outports : ((unsigned long) ports); // const bool usefixedrate = (requiredFeatures() & Plugin::FixedBlockSize);; // const unsigned long min_per = (usefixedrate || MusEGlobal::config.minControlProcessPeriod > nframes) ? nframes : MusEGlobal::config.minControlProcessPeriod; // const unsigned long min_per_mask = min_per - 1; // min_per must be power of 2 // // unsigned long sample = 0; // AudioTrack *atrack = track(); // const AutomationType at = atrack->automationType(); // const bool no_auto = !MusEGlobal::automation || at == AUTO_OFF; // CtrlListList *cll = atrack->controller(); // ciCtrlList icl_first; // const int plug_id = id(); // // LV2EvBuf *evBuf = (_inportsMidi > 0) ? _state->midiInPorts [0].buffer : NULL; // // //set freewheeling property if plugin supports it // if(_synth->_hasFreeWheelPort) // { // _controls [_synth->_freeWheelPortIndex].val = MusEGlobal::audio->freewheel() ? 1.0f : 0.0f; // } // // if(plug_id != -1 && ports != 0) // Don't bother if not 'running'. // { // icl_first = cll->lower_bound(genACnum(plug_id, 0)); // } // // bool used_in_chan_array[_inports]; // Don't bother initializing if not 'running'. // // // Don't bother if not 'running'. // if(ports != 0) // { // // Initialize the array. // for(size_t i = 0; i < _inports; ++i) // used_in_chan_array[i] = false; // // if(!atrack->noInRoute()) // { // RouteList *irl = atrack->inRoutes(); // for(ciRoute i = irl->begin(); i != irl->end(); ++i) // { // if(i->track->isMidiTrack()) // continue; // // Only this synth knows how many destination channels there are, // // while only the track knows how many source channels there are. // // So take care of the destination channels here, and let the track handle the source channels. // const int dst_ch = i->channel <= -1 ? 0 : i->channel; // if((unsigned long)dst_ch >= _inports) // continue; // const int dst_chs = i->channels <= -1 ? _inports : i->channels; // //const int total_ins = atrack->totalRoutableInputs(Route::TRACK_ROUTE); // const int src_ch = i->remoteChannel <= -1 ? 0 : i->remoteChannel; // const int src_chs = i->channels; // // int fin_dst_chs = dst_chs; // if((unsigned long)(dst_ch + fin_dst_chs) > _inports) // fin_dst_chs = _inports - dst_ch; // // static_cast(i->track)->copyData(pos, // dst_ch, dst_chs, fin_dst_chs, // src_ch, src_chs, // nframes, &_audioInBuffers[0], // false, used_in_chan_array); // const int nxt_ch = dst_ch + fin_dst_chs; // for(int ch = dst_ch; ch < nxt_ch; ++ch) // used_in_chan_array[ch] = true; // } // } // } // // // int cur_slice = 0; // // while(sample < nframes) // { // unsigned long nsamp = nframes - sample; // const unsigned long slice_frame = pos + sample; // // // // // Process automation control values, while also determining the maximum acceptable // // size of this run. Further processing, from FIFOs for example, can lower the size // // from there, but this section determines where the next highest maximum frame // // absolutely needs to be for smooth playback of the controller value stream... // // // if(ports != 0) // Don't bother if not 'running'. // { // ciCtrlList icl = icl_first; // // for(unsigned long k = 0; k < _inportsControl; ++k) // { // //don't process freewheel port // if(_synth->_hasFreeWheelPort && _synth->_freeWheelPortIndex == k) // continue; // // CtrlList *cl = (cll && plug_id != -1 && icl != cll->end()) ? icl->second : NULL; // CtrlInterpolate &ci = _controls[k].interp; // // // Always refresh the interpolate struct at first, since things may have changed. // // Or if the frame is outside of the interpolate range - and eStop is not true. // FIXME TODO: Be sure these comparisons are correct. // if(cur_slice == 0 || (!ci.eStop && MusEGlobal::audio->isPlaying() && // (slice_frame < (unsigned long)ci.sFrame || (ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame)))) // { // if(cl && plug_id != -1 && (unsigned long)cl->id() == genACnum(plug_id, k)) // { // cl->getInterpolation(slice_frame, no_auto || !_controls[k].enCtrl, &ci); // // if(icl != cll->end()) // { // ++icl; // } // } // else // { // // No matching controller, or end. Just copy the current value into the interpolator. // // Keep the current icl iterator, because since they are sorted by frames, // // if the IDs didn't match it means we can just let k catch up with icl. // ci.sFrame = 0; // ci.eFrame = -1; // ci.sVal = _controls[k].val; // ci.eVal = ci.sVal; // ci.doInterp = false; // ci.eStop = false; // } // } // else // { // if(ci.eStop && ci.eFrame != -1 && slice_frame >= (unsigned long)ci.eFrame) // FIXME TODO: Get that comparison right. // { // // Clear the stop condition and set up the interp struct appropriately as an endless value. // ci.sFrame = 0; //ci->eFrame; // ci.eFrame = -1; // ci.sVal = ci.eVal; // ci.doInterp = false; // ci.eStop = false; // } // // if(cl && cll && icl != cll->end()) // { // ++icl; // } // } // // if(!usefixedrate && MusEGlobal::audio->isPlaying()) // { // unsigned long samps = nsamp; // // if(ci.eFrame != -1) // { // samps = (unsigned long)ci.eFrame - slice_frame; // } // // if(!ci.doInterp && samps > min_per) // { // samps &= ~min_per_mask; // // if((samps & min_per_mask) != 0) // { // samps += min_per; // } // } // else // { // samps = min_per; // } // // if(samps < nsamp) // { // nsamp = samps; // } // // } // // if(ci.doInterp && cl) // { // _controls[k].val = cl->interpolate(MusEGlobal::audio->isPlaying() ? slice_frame : pos, ci); // } // else // { // _controls[k].val = ci.sVal; // } // _state->controlsMask [k] = true; // // #ifdef LV2_DEBUG_PROCESS // fprintf(stderr, "LV2SynthIF::getData k:%lu val:%f sample:%lu ci.eFrame:%d nsamp:%lu \n", k, _controls[k].val, sample, ci.eFrame, nsamp); // #endif // // } // } // // // bool found = false; // unsigned long frame = 0; // unsigned long index = 0; // // // Get all control ring buffer items valid for this time period... // while(!_controlFifo.isEmpty()) // { // unsigned long evframe; // const ControlEvent& v = _controlFifo.peek(); // // The events happened in the last period or even before that. Shift into this period with + n. This will sync with audio. // // If the events happened even before current frame - n, make sure they are counted immediately as zero-frame. // evframe = (syncFrame > v.frame + nframes) ? 0 : v.frame - syncFrame + nframes; // // #ifdef DEBUG_LV2 // fprintf(stderr, "LV2SynthIF::getData found:%d evframe:%lu frame:%lu event frame:%lu idx:%lu val:%f unique:%d\n", // found, evframe, frame, v.frame, v.idx, v.value, v.unique); // #endif // // // Protection. Observed this condition. Why? Supposed to be linear timestamps. // if(found && evframe < frame) // { // fprintf(stderr, // "LV2SynthIF::getData *** Error: Event out of order: evframe:%lu < frame:%lu idx:%lu val:%f unique:%d syncFrame:%u nframes:%u v.frame:%lu\n", // evframe, frame, v.idx, v.value, v.unique, syncFrame, nframes, v.frame); // // // No choice but to ignore it. // _controlFifo.remove(); // Done with the ring buffer's item. Remove it. // continue; // } // // if(evframe >= nframes // Next events are for a later period. // || (!usefixedrate && !found && !v.unique && (evframe - sample >= nsamp)) // Next events are for a later run in this period. (Autom took prio.) // || (found && !v.unique && (evframe - sample >= min_per)) // Eat up events within minimum slice - they're too close. // || (usefixedrate && found && v.unique && v.idx == index)) // Fixed rate and must reply to all. // // { // break; // } // // // _controlFifo.remove(); // Done with the ring buffer's item. Remove it. // // found = true; // frame = evframe; // index = v.idx; // // if(index >= _inportsControl) // Sanity check. // { // _controlFifo.remove(); // Done with the ring buffer's item. Remove it. // break; // } // // //don't process freewheel port // if(_synth->_hasFreeWheelPort && _synth->_freeWheelPortIndex == index) // { // _controlFifo.remove(); // Done with the ring buffer's item. Remove it. // continue; // } // // if(ports == 0) // Don't bother if not 'running'. // { // _controls[index].val = v.value; // Might as well at least update these. // } // else // { // CtrlInterpolate *ci = &_controls[index].interp; // // Tell it to stop the current ramp at this frame, when it does stop, set this value: // ci->eFrame = frame; // ci->eVal = v.value; // ci->eStop = true; // } // // // Need to update the automation value, otherwise it overwrites later with the last automation value. // if(plug_id != -1) // { // synti->setPluginCtrlVal(genACnum(plug_id, index), v.value); // } // if(v.fromGui) //don't send gui control changes back // { // _state->lastControls [index] = v.value; // _state->controlsMask [index] = false; // } // _controlFifo.remove(); // Done with the ring buffer's item. Remove it. // } // // if(found && !usefixedrate) // If a control FIFO item was found, takes priority over automation controller stream. // { // nsamp = frame - sample; // } // // // if(sample + nsamp > nframes) // Safety check. // { // nsamp = nframes - sample; // } // // // TODO: Don't allow zero-length runs. This could/should be checked in the control loop instead. // // Note this means it is still possible to get stuck in the top loop (at least for a while). // if(nsamp != 0) // { // LV2Synth::lv2audio_preProcessMidiPorts(_state, nsamp); // // // Get the state of the stop flag. // const bool do_stop = synti->stopFlag(); // // MidiPlayEvent buf_ev; // // // Transfer the user lock-free buffer events to the user sorted multi-set. // // False = don't use the size snapshot, but update it. // const unsigned int usr_buf_sz = synti->eventBuffers(MidiDevice::UserBuffer)->getSize(false); // for(unsigned int i = 0; i < usr_buf_sz; ++i) // { // if(synti->eventBuffers(MidiDevice::UserBuffer)->get(buf_ev)) // synti->_outUserEvents.insert(buf_ev); // } // // // Transfer the playback lock-free buffer events to the playback sorted multi-set. // const unsigned int pb_buf_sz = synti->eventBuffers(MidiDevice::PlaybackBuffer)->getSize(false); // for(unsigned int i = 0; i < pb_buf_sz; ++i) // { // // Are we stopping? Just remove the item. // if(do_stop) // synti->eventBuffers(MidiDevice::PlaybackBuffer)->remove(); // // Otherwise get the item. // else if(synti->eventBuffers(MidiDevice::PlaybackBuffer)->get(buf_ev)) // synti->_outPlaybackEvents.insert(buf_ev); // } // // // Are we stopping? // if(do_stop) // { // // Transport has stopped, purge ALL further scheduled playback events now. // synti->_outPlaybackEvents.clear(); // // Reset the flag. // synti->setStopFlag(false); // } // // iMPEvent impe_pb = synti->_outPlaybackEvents.begin(); // iMPEvent impe_us = synti->_outUserEvents.begin(); // bool using_pb; // // while(1) // { // if(impe_pb != synti->_outPlaybackEvents.end() && impe_us != synti->_outUserEvents.end()) // using_pb = *impe_pb < *impe_us; // else if(impe_pb != synti->_outPlaybackEvents.end()) // using_pb = true; // else if(impe_us != synti->_outUserEvents.end()) // using_pb = false; // else break; // // const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us; // // #ifdef LV2_DEBUG // fprintf(stderr, "LV2SynthIF::getData eventFifos event time:%d\n", e.time()); // #endif // // if(e.time() >= (sample + nsamp + syncFrame)) // break; // // if(ports != 0) // Don't bother if not 'running'. // { // // Time-stamp the event. // unsigned int ft = (e.time() < syncFrame) ? 0 : e.time() - syncFrame; // ft = (ft < sample) ? 0 : ft - sample; // // if(ft >= nsamp) // { // fprintf(stderr, "LV2SynthIF::getData: eventFifos event time:%d out of range. pos:%d syncFrame:%u ft:%u sample:%lu nsamp:%lu\n", // e.time(), pos, syncFrame, ft, sample, nsamp); // ft = nsamp - 1; // } // if(processEvent(e, evBuf, ft)) // { // // } // } // // // Done with ring buffer's event. Remove it. // // C++11. // if(using_pb) // impe_pb = synti->_outPlaybackEvents.erase(impe_pb); // else // impe_us = synti->_outUserEvents.erase(impe_us); // } // // // if(ports != 0) // Don't bother if not 'running'. // { // // //connect ports // for(size_t j = 0; j < _inports; ++j) // { // if(used_in_chan_array [j]) // { // lilv_instance_connect_port(_handle, _audioInPorts [j].index, _audioInBuffers [j] + sample); // } // else // { // lilv_instance_connect_port(_handle, _audioInPorts [j].index, _audioInSilenceBuf + sample); // } // } // // for(size_t j = 0; j < nop; ++j) // { // lilv_instance_connect_port(_handle, _audioOutPorts [j].index, buffer [j] + sample); // } // // for(size_t j = nop; j < _outports; j++) // { // lilv_instance_connect_port(_handle, _audioOutPorts [j].index, _audioOutBuffers [j] + sample); // } // // for(size_t j = 0; j < _inportsControl; ++j) // { // uint32_t idx = _controlInPorts [j].index; // if(_state->pluginCVPorts [idx] != NULL) // { // float cvVal = _controls [j].val; // for(size_t jj = 0; jj < nsamp; ++jj) // { // _state->pluginCVPorts [idx] [jj + sample] = cvVal; // } // lilv_instance_connect_port(_handle, idx, _state->pluginCVPorts [idx] + sample); // } // } // #ifdef LV2_DEBUG_PROCESS // //dump atom sequence events to stderr // if(evBuf) // { // evBuf->dump(); // } // #endif // // lilv_instance_run(_handle, nsamp); // //notify worker that this run() finished // if(_state->wrkIface && _state->wrkIface->end_run) // _state->wrkIface->end_run(lilv_instance_get_handle(_handle)); // //notify worker about processed data (if any) // if(_state->wrkIface && _state->wrkIface->work_response && _state->wrkEndWork) // { // _state->wrkIface->work_response(lilv_instance_get_handle(_handle), _state->wrkDataSize, _state->wrkDataBuffer); // _state->wrkDataSize = 0; // _state->wrkDataBuffer = NULL; // _state->wrkEndWork = false; // } // // LV2Synth::lv2audio_postProcessMidiPorts(_state, nsamp); // // // } // // sample += nsamp; // } // // ++cur_slice; // Slice is done. Moving on to any next slice now... // } // // return true; // } // // void LV2SynthIF::getNativeGeometry(int *x, int *y, int *w, int *h) const // { // if(_state->pluginWindow != NULL && !_state->hasExternalGui) // { // QRect g = _state->pluginWindow->geometry(); // if(x) *x = g.x(); // if(y) *y = g.y(); // if(w) *w = g.width(); // if(h) *h = g.height(); // return; // } // // // Fall back to blank geometry. // SynthIF::getNativeGeometry(x, y, w, h); // } // // double LV2SynthIF::getParameter(long unsigned int n) const // { // if(n >= _inportsControl) // { // std::cout << "LV2SynthIF::getParameter param number " << n << " out of range of ports: " << _inportsControl << std::endl; // return 0.0; // } // // if(!_controls) // { // return 0.0; // } // // return _controls[n].val; // } // // double LV2SynthIF::getParameterOut(long unsigned int n) const // { // if(n >= _outportsControl) // { // std::cout << "LV2SynthIF::getParameterOut param number " << n << " out of range of ports: " << _outportsControl << std::endl; // return 0.0; // } // // if(!_controlsOut) // { // return 0.0; // } // // return _controlsOut[n].val; // // } // // // QString LV2SynthIF::getPatchName(int /* ch */, int prog, bool) const // { // uint32_t program = prog & 0xff; // uint32_t lbank = (prog >> 8) & 0xff; // uint32_t hbank = (prog >> 16) & 0xff; // // if (program > 127) // Map "dont care" to 0 // program = 0; // if (lbank > 127) // lbank = 0; // if (hbank > 127) // hbank = 0; // const uint32_t patch = (hbank << 16) | (lbank << 8) | program; // // std::map::iterator itPrg = _state->prg2index.find(patch); // if(itPrg == _state->prg2index.end()) // return QString("?"); // uint32_t index = itPrg->second; // std::map::iterator itIndex = _state->index2prg.find(index); // if(itIndex == _state->index2prg.end()) // return QString("?"); // return QString(itIndex->second.name); // // } // // void LV2SynthIF::guiHeartBeat() // { // //check for pending song dirty status // if(_state->songDirtyPending){ // MusEGlobal::song->setDirty(); // _state->songDirtyPending = false; // } // // } // // bool LV2SynthIF::hasGui() const // { // return true; // } // // bool LV2SynthIF::hasNativeGui() const // { // return (_synth->_pluginUiTypes.size() > 0); // } // // bool LV2SynthIF::nativeGuiVisible() const // { // if(_state != NULL) // { // if(_state->hasExternalGui) // { // return (_state->widget != NULL); // } // else if(_state->hasGui && _state->widget != NULL) // { // return ((QWidget *)_state->widget)->isVisible(); // } // } // // return false; // } // // void LV2SynthIF::populatePatchPopup(MusEGui::PopupMenu *menu, int, bool) // { // LV2Synth::lv2prg_updatePrograms(_state); // menu->clear(); // MusEGui::PopupMenu *subMenuPrograms = new MusEGui::PopupMenu(menu->parent()); // subMenuPrograms->setTitle(QObject::tr("Midi programs")); // subMenuPrograms->setIcon(QIcon(*MusEGui::pianoNewIcon)); // menu->addMenu(subMenuPrograms); // MusEGui::PopupMenu *subMenuPresets = new MusEGui::PopupMenu(menu->parent()); // subMenuPresets->setTitle(QObject::tr("Presets")); // menu->addMenu(subMenuPresets); // // //First: fill programs submenu // std::map submenus; // std::map::iterator itIndex; // for(itIndex = _state->index2prg.begin(); itIndex != _state->index2prg.end(); ++itIndex) // { // const lv2ExtProgram &extPrg = itIndex->second; // uint32_t hb = extPrg.bank >> 8; // uint32_t lb = extPrg.bank & 0xff; // // 16384 banks arranged as 128 hi and lo banks each with up to the first 128 programs supported. // if(hb > 127 || lb > 127 || extPrg.prog > 127) // continue; // hb &= 0x7f; // lb &= 0x7f; // const uint32_t patch_bank = (hb << 8) | lb; // const uint32_t patch = (patch_bank << 8) | extPrg.prog; // // std::map::iterator itS = submenus.find(patch_bank); // MusEGui::PopupMenu *submenu= NULL; // if(itS != submenus.end()) // { // submenu = itS->second; // } // else // { // submenu = new MusEGui::PopupMenu(menu->parent()); // submenu->setTitle(QString("Bank #") + QString::number(extPrg.bank + 1)); // subMenuPrograms->addMenu(submenu); // submenus.insert(std::make_pair(patch_bank, submenu)); // } // // QAction *act = submenu->addAction(extPrg.name); // act->setData(patch); // } // // //Second:: Fill presets submenu // LV2Synth::lv2state_populatePresetsMenu(_state, subMenuPresets); // } // // void LV2SynthIF::preProcessAlways() // { // // } // // MidiPlayEvent LV2SynthIF::receiveEvent() // { // return MidiPlayEvent(); // // } // // void LV2SynthIF::setNativeGeometry(int x, int y, int w, int h) // { // // Store the native geometry. // SynthIF::setNativeGeometry(x, y, w, h); // // if(_state->pluginWindow && !_state->hasExternalGui) // { // //_state->pluginWindow->move(x, y); // //don't resize lv2 uis - this is handles at plugin level // //_uiState->pluginWindow->resize(w, h); // // #ifdef QT_SHOW_POS_BUG_WORKAROUND // // Because of the bug, no matter what we must supply a position, // // even upon first showing... // // // Check sane size. // if(w == 0) // w = _state->pluginWindow->sizeHint().width(); // if(h == 0) // h = _state->pluginWindow->sizeHint().height(); // // // No size hint? Try minimum size. // if(w == 0) // w = _state->pluginWindow->minimumSize().width(); // if(h == 0) // h = _state->pluginWindow->minimumSize().height(); // // // Fallback. // if(w == 0) // w = 400; // if(h == 0) // h = 300; // // _state->pluginWindow->setGeometry(x, y, w, h); // // #else // // // If the saved geometry is valid, use it. // // Otherwise this is probably the first time showing, // // so do not set a geometry - let Qt pick one // // (using auto-placement and sizeHint). // if(!(x == 0 && y == 0 && w == 0 && h == 0)) // { // // Check sane size. // if(w == 0) // w = _state->pluginWindow->sizeHint().width(); // if(h == 0) // h = _state->pluginWindow->sizeHint().height(); // // // No size hint? Try minimum size. // if(w == 0) // w = _state->pluginWindow->minimumSize().width(); // if(h == 0) // h = _state->pluginWindow->minimumSize().height(); // // // Fallback. // if(w == 0) // w = 400; // if(h == 0) // h = 300; // // _state->pluginWindow->setGeometry(x, y, w, h); // } // #endif // } // } // // void LV2SynthIF::setParameter(long unsigned int idx, double value) // { // addScheduledControlEvent(idx, value, MusEGlobal::audio->curFrame()); // } // // void LV2SynthIF::showNativeGui(bool bShow) // { // if(track() != NULL) // { // if(_state->human_id != NULL) // { // free(_state->human_id); // } // // _state->extHost.plugin_human_id = _state->human_id = strdup((track()->name() + QString(": ") + name()).toUtf8().constData()); // } // // LV2Synth::lv2ui_ShowNativeGui(_state, bShow); // } // // void LV2SynthIF::write(int level, Xml &xml) const // { // LV2Synth::lv2conf_write(_state, level, xml); // } // // void LV2SynthIF::setCustomData(const std::vector< QString > &customParams) // { // LV2Synth::lv2conf_set(_state, customParams); // } // // // double LV2SynthIF::param(long unsigned int i) const // { // return getParameter(i); // } // // long unsigned int LV2SynthIF::parameters() const // { // return _inportsControl; // } // // long unsigned int LV2SynthIF::parametersOut() const // { // return _outportsControl; // } // // const char *LV2SynthIF::paramName(long unsigned int i) // { // return _controlInPorts [i].cName; // } // // const char *LV2SynthIF::paramOutName(long unsigned int i) // { // return _controlOutPorts [i].cName; // } // // CtrlValueType LV2SynthIF::ctrlValueType(unsigned long i) const // { // CtrlValueType vt = VAL_LINEAR; // std::map::iterator it = _synth->_idxToControlMap.find(i); // assert(it != _synth->_idxToControlMap.end()); // i = it->second; // assert(i < _inportsControl); // // switch(_synth->_controlInPorts [i].cType) // { // case LV2_PORT_CONTINUOUS: // vt = VAL_LINEAR; // break; // case LV2_PORT_DISCRETE: // case LV2_PORT_INTEGER: // vt = VAL_INT; // break; // case LV2_PORT_LOGARITHMIC: // vt = VAL_LOG; // break; // case LV2_PORT_TRIGGER: // vt = VAL_BOOL; // break; // default: // break; // } // // return vt; // // } // // CtrlList::Mode LV2SynthIF::ctrlMode(unsigned long i) const // { // std::map::iterator it = _synth->_idxToControlMap.find(i); // assert(it != _synth->_idxToControlMap.end()); // i = it->second; // assert(i < _inportsControl); // // return ((_synth->_controlInPorts [i].cType == LV2_PORT_CONTINUOUS) // ||(_synth->_controlInPorts [i].cType == LV2_PORT_LOGARITHMIC)) ? CtrlList::INTERPOLATE : CtrlList::DISCRETE; // } // // LADSPA_PortRangeHint LV2SynthIF::range(unsigned long i) // { // assert(i < _inportsControl); // LADSPA_PortRangeHint hint; // hint.HintDescriptor = 0; // hint.LowerBound = _controlInPorts [i].minVal; // hint.UpperBound = _controlInPorts [i].maxVal; // // if(hint.LowerBound == hint.LowerBound) // { // hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; // } // // if(hint.UpperBound == hint.UpperBound) // { // hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; // } // // return hint; // } // // LADSPA_PortRangeHint LV2SynthIF::rangeOut(unsigned long i) // { // assert(i < _outportsControl); // LADSPA_PortRangeHint hint; // hint.HintDescriptor = 0; // hint.LowerBound = _controlOutPorts [i].minVal; // hint.UpperBound = _controlOutPorts [i].maxVal; // // if(hint.LowerBound == hint.LowerBound) // { // hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; // } // // if(hint.UpperBound == hint.UpperBound) // { // hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; // } // // return hint; // // } // // // // double LV2SynthIF::paramOut(long unsigned int i) const // { // return getParameterOut(i); // } // // void LV2SynthIF::setParam(long unsigned int i, double val) // { // setParameter(i, val); // } // // void LV2SynthIF::enableController(unsigned long i, bool v) { _controls[i].enCtrl = v; } // bool LV2SynthIF::controllerEnabled(unsigned long i) const { return _controls[i].enCtrl; } // void LV2SynthIF::enableAllControllers(bool v) // { // if(!_synth) // return; // for(unsigned long i = 0; i < _inportsControl; ++i) // _controls[i].enCtrl = v; // } // void LV2SynthIF::updateControllers() { } // // void LV2SynthIF::populatePresetsMenu(MusEGui::PopupMenu *menu) // { // LV2Synth::lv2state_populatePresetsMenu(_state, menu); // } // // void LV2SynthIF::applyPreset(void *preset) // { // LV2Synth::lv2state_applyPreset(_state, static_cast(preset)); // } // // // // void LV2SynthIF::writeConfiguration(int level, Xml &xml) // { // MusECore::SynthIF::writeConfiguration(level, xml); // } // // bool LV2SynthIF::readConfiguration(Xml &xml, bool readPreset) // { // return MusECore::SynthIF::readConfiguration(xml, readPreset); // } // // void LV2PluginWrapper_Window::hideEvent(QHideEvent *e) // { // if(_state->plugInst != NULL) // _state->plugInst->saveNativeGeometry(geometry().x(), geometry().y(), geometry().width(), geometry().height()); // else if(_state->sif != NULL) // _state->sif->saveNativeGeometry(geometry().x(), geometry().y(), geometry().width(), geometry().height()); // // e->ignore(); // QMainWindow::hideEvent(e); // } // // void LV2PluginWrapper_Window::showEvent(QShowEvent *e) // { // int x = 0, y = 0, w = 0, h = 0; // if(_state->plugInst != NULL) // _state->plugInst->savedNativeGeometry(&x, &y, &w, &h); // else if(_state->sif != NULL) // _state->sif->savedNativeGeometry(&x, &y, &w, &h); // // #ifdef QT_SHOW_POS_BUG_WORKAROUND // // Because of the bug, no matter what we must supply a position, // // even upon first showing... // // // Check sane size. // if(w == 0) // w = sizeHint().width(); // if(h == 0) // h = sizeHint().height(); // // // No size hint? Try minimum size. // if(w == 0) // w = minimumSize().width(); // if(h == 0) // h = minimumSize().height(); // // // Fallback. // if(w == 0) // w = 400; // if(h == 0) // h = 300; // // setGeometry(x, y, w, h); // // #else // // // If the saved geometry is valid, use it. // // Otherwise this is probably the first time showing, // // so do not set a geometry - let Qt pick one // // (using auto-placement and sizeHint). // if(!(x == 0 && y == 0 && w == 0 && h == 0)) // { // // Check sane size. // if(w == 0) // w = sizeHint().width(); // if(h == 0) // h = sizeHint().height(); // // // No size hint? Try minimum size. // if(w == 0) // w = minimumSize().width(); // if(h == 0) // h = minimumSize().height(); // // // Fallback. // if(w == 0) // w = 400; // if(h == 0) // h = 300; // // setGeometry(x, y, w, h); // } // #endif // // // Convenience: If the window was minimized, restore it. // if(isMinimized()) // setWindowState((windowState() & ~Qt::WindowMinimized) | Qt::WindowActive); // // e->ignore(); // QMainWindow::showEvent(e); // } // // void LV2PluginWrapper_Window::closeEvent(QCloseEvent *event) // { // assert(_state != NULL); // event->accept(); // // stopUpdateTimer(); // // //if(_state->gtk2Plug != NULL) // //{ // if(_state->pluginQWindow != NULL) // { // _state->pluginQWindow->setParent(NULL); // delete _state->pluginQWindow; // _state->pluginQWindow = NULL; // } // //} // // if(_state->deleteLater) // { // LV2Synth::lv2state_FreeState(_state); // // } // else // { // //_state->uiTimer->stopNextTime(false); // _state->widget = NULL; // _state->pluginWindow = NULL; // _state->uiDoSelectPrg = false; // _state->uiPrgIface = NULL; // // LV2Synth::lv2ui_FreeDescriptors(_state); // } // // // Reset the flag, just to be sure. // _state->uiIsOpening = false; // // // The widget is automatically deleted by use of the // // WA_DeleteOnClose attribute in the constructor. // } // // void LV2PluginWrapper_Window::stopUpdateTimer() // { // if(updateTimer.isActive()) // updateTimer.stop(); // while(updateTimer.isActive()) // { // QCoreApplication::processEvents(); // } // } // // // #ifdef LV2_GUI_USE_QWIDGET // LV2PluginWrapper_Window::LV2PluginWrapper_Window(LV2PluginWrapper_State *state, // QWidget *parent, // Qt::WindowFlags flags) // : QWidget(parent, flags), _state ( state ), _closing(false) // #else // LV2PluginWrapper_Window::LV2PluginWrapper_Window(LV2PluginWrapper_State *state, // QWidget *parent, // Qt::WindowFlags flags) // : QMainWindow(parent, flags), _state ( state ), _closing(false) // #endif // { // // Automatically delete the wiget when it closes. // setAttribute(Qt::WA_DeleteOnClose, true); // connect(&updateTimer, SIGNAL(timeout()), this, SLOT(updateGui())); // connect(this, SIGNAL(makeStopFromGuiThread()), this, SLOT(stopFromGuiThread())); // connect(this, SIGNAL(makeStartFromGuiThread()), this, SLOT(startFromGuiThread())); // } // // LV2PluginWrapper_Window::~LV2PluginWrapper_Window() // { // #ifdef DEBUG_LV2 // std::cout << "LV2PluginWrapper_Window::~LV2PluginWrapper_Window()" << std::endl; // #endif // } // // void LV2PluginWrapper_Window::startNextTime() // { // emit makeStartFromGuiThread(); // } // // // // // void LV2PluginWrapper_Window::stopNextTime() // { // setClosing(true); // emit makeStopFromGuiThread(); // } // // void LV2PluginWrapper_Window::updateGui() // { // if(_state->deleteLater || _closing) // { // stopNextTime(); // return; // } // LV2Synth::lv2ui_SendChangedControls(_state); // // //send program change if any // // Force send if re-opening. // if(_state->uiIsOpening || _state->uiDoSelectPrg) // { // _state->uiDoSelectPrg = false; // if(_state->uiPrgIface != NULL && (_state->uiPrgIface->select_program != NULL || _state->uiPrgIface->select_program_for_channel != NULL)) // { // if(_state->newPrgIface) // _state->uiPrgIface->select_program_for_channel(lilv_instance_get_handle(_state->handle), _state->uiChannel, (uint32_t)_state->uiBank, (uint32_t)_state->uiProg); // else // _state->uiPrgIface->select_program(lilv_instance_get_handle(_state->handle), (uint32_t)_state->uiBank, (uint32_t)_state->uiProg); // } // } // // // Reset the flag. // _state->uiIsOpening = false; // // //call ui idle callback if any // if(_state->uiIdleIface != NULL) // { // int iRet = _state->uiIdleIface->idle(_state->uiInst); // if(iRet != 0) // ui don't want us to call it's idle callback any more // _state->uiIdleIface = NULL; // } // // if(_state->hasExternalGui) // { // LV2_EXTERNAL_UI_RUN((LV2_External_UI_Widget *)_state->widget); // } // // //if(_closing) // //stopNextTime(); // } // // void LV2PluginWrapper_Window::stopFromGuiThread() // { // stopUpdateTimer(); // emit close(); // } // // void LV2PluginWrapper_Window::startFromGuiThread() // { // stopUpdateTimer(); // updateTimer.start(1000/30); // } // // // LV2PluginWrapper::LV2PluginWrapper(LV2Synth *s, PluginFeatures reqFeatures) // { // _synth = s; // // _requiredFeatures = reqFeatures; // // _fakeLd.Label = strdup(_synth->name().toUtf8().constData()); // _fakeLd.Name = strdup(_synth->name().toUtf8().constData()); // _fakeLd.UniqueID = _synth->_uniqueID; // _fakeLd.Maker = strdup(_synth->maker().toUtf8().constData()); // _fakeLd.Copyright = strdup(_synth->version().toUtf8().constData()); // _isLV2Plugin = true; // _isLV2Synth = s->_isSynth; // int numPorts = _synth->_audioInPorts.size() // + _synth->_audioOutPorts.size() // + _synth->_controlInPorts.size() // + _synth->_controlOutPorts.size() // + _synth->_midiInPorts.size() // + _synth->_midiOutPorts.size(); // _fakeLd.PortCount = numPorts; // _fakePds = new LADSPA_PortDescriptor [numPorts]; // memset(_fakePds, 0, sizeof(int) * numPorts); // // for(size_t i = 0; i < _synth->_audioInPorts.size(); i++) // { // _fakePds [_synth->_audioInPorts [i].index] = LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO; // } // // for(size_t i = 0; i < _synth->_audioOutPorts.size(); i++) // { // _fakePds [_synth->_audioOutPorts [i].index] = LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO; // } // // for(size_t i = 0; i < _synth->_controlInPorts.size(); i++) // { // _fakePds [_synth->_controlInPorts [i].index] = LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL; // } // // for(size_t i = 0; i < _synth->_controlOutPorts.size(); i++) // { // _fakePds [_synth->_controlOutPorts [i].index] = LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL; // } // // _fakeLd.PortNames = NULL; // _fakeLd.PortRangeHints = NULL; // _fakeLd.PortDescriptors = _fakePds; // _fakeLd.Properties = 0; // plugin = &_fakeLd; // _isDssi = false; // _isDssiSynth = false; // _isVstNativePlugin = false; // _isVstNativeSynth = false; // // #ifdef DSSI_SUPPORT // dssi_descr = NULL; // #endif // // fi = _synth->info; // ladspa = NULL; // _handle = 0; // _references = 0; // _instNo = 0; // _label = _synth->name(); // _name = _synth->description(); // _uniqueID = plugin->UniqueID; // _maker = _synth->maker(); // _copyright = _synth->version(); // // _portCount = plugin->PortCount; // // _inports = 0; // _outports = 0; // _controlInPorts = 0; // _controlOutPorts = 0; // // for(unsigned long k = 0; k < _portCount; ++k) // { // LADSPA_PortDescriptor pd = plugin->PortDescriptors[k]; // // if(pd & LADSPA_PORT_AUDIO) // { // if(pd & LADSPA_PORT_INPUT) // { // ++_inports; // } // else if(pd & LADSPA_PORT_OUTPUT) // { // ++_outports; // } // } // else if(pd & LADSPA_PORT_CONTROL) // { // if(pd & LADSPA_PORT_INPUT) // { // ++_controlInPorts; // } // else if(pd & LADSPA_PORT_OUTPUT) // { // ++_controlOutPorts; // } // } // } // } // // LV2PluginWrapper::~LV2PluginWrapper() // { // free((void*)_fakeLd.Label); // free((void*)_fakeLd.Name); // free((void*)_fakeLd.Maker); // free((void*)_fakeLd.Copyright); // delete [] _fakePds; // } // // LADSPA_Handle LV2PluginWrapper::instantiate(PluginI *plugi) // { // LV2PluginWrapper_State *state = new LV2PluginWrapper_State; // state->inst = this; // state->widget = NULL; // state->uiInst = NULL; // state->plugInst = plugi; // state->_ifeatures = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; // state->_ppifeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; // state->sif = NULL; // state->synth = _synth; // LV2Synth::lv2state_FillFeatures(state); // // state->handle = lilv_plugin_instantiate(_synth->_handle, (double)MusEGlobal::sampleRate, state->_ppifeatures); // // if(state->handle == NULL) // { // delete [] state->_ppifeatures; // delete [] state->_ifeatures; // return NULL; // } // // //_states.insert(std::pair(state->handle, state)); // // LV2Synth::lv2state_PostInstantiate(state); // // return (LADSPA_Handle)state; // // } // // void LV2PluginWrapper::connectPort(LADSPA_Handle handle, long unsigned int port, float *value) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // lilv_instance_connect_port((LilvInstance *)state->handle, port, (void *)value); // } // // int LV2PluginWrapper::incReferences(int ref) // { // _synth->incInstances(ref); // return _synth->instances(); // } // void LV2PluginWrapper::activate(LADSPA_Handle handle) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // lilv_instance_activate((LilvInstance *) state->handle); // } // void LV2PluginWrapper::deactivate(LADSPA_Handle handle) // { // if (handle) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // lilv_instance_deactivate((LilvInstance *) state->handle); // } // } // void LV2PluginWrapper::cleanup(LADSPA_Handle handle) // { // if(handle != NULL) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // // state->deleteLater = true; // if(state->pluginWindow != NULL) // state->pluginWindow->stopNextTime(); // else // LV2Synth::lv2state_FreeState(state); // // } // } // // void LV2PluginWrapper::apply(LADSPA_Handle handle, unsigned long n) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // // LV2Synth::lv2audio_preProcessMidiPorts(state, n); // // //set freewheeling property if plugin supports it // if(state->synth->_hasFreeWheelPort) // { // state->plugInst->controls[_synth->_freeWheelPortIndex].val = MusEGlobal::audio->freewheel() ? 1.0f : 0.0f; // } // // for(size_t j = 0; j < state->plugInst->controlPorts; ++j) // { // uint32_t idx = state->synth->_controlInPorts [j].index; // if(state->pluginCVPorts [idx] != NULL) // { // float cvVal = state->plugInst->controls [j].val; // for(size_t jj = 0; jj < n; ++jj) // { // state->pluginCVPorts [idx] [jj] = cvVal; // } // lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); // } // } // // for(size_t j = 0; j < state->plugInst->controlOutPorts; ++j) // { // uint32_t idx = state->synth->_controlOutPorts [j].index; // if(state->pluginCVPorts [idx] != NULL) // { // float cvVal = state->plugInst->controlsOut [j].val; // for(size_t jj = 0; jj < n; ++jj) // { // state->pluginCVPorts [idx] [jj] = cvVal; // } // lilv_instance_connect_port(state->handle, idx, state->pluginCVPorts [idx]); // } // } // // // lilv_instance_run(state->handle, n); // //notify worker that this run() finished // if(state->wrkIface && state->wrkIface->end_run) // state->wrkIface->end_run(lilv_instance_get_handle(state->handle)); // //notify worker about processes data (if any) // if(state->wrkIface && state->wrkIface->work_response && state->wrkEndWork) // { // state->wrkEndWork = false; // state->wrkIface->work_response(lilv_instance_get_handle(state->handle), state->wrkDataSize, state->wrkDataBuffer); // state->wrkDataSize = 0; // state->wrkDataBuffer = NULL; // } // // LV2Synth::lv2audio_postProcessMidiPorts(state, n); // } // LADSPA_PortDescriptor LV2PluginWrapper::portd(unsigned long k) const // { // return _fakeLd.PortDescriptors[k]; // } // // LADSPA_PortRangeHint LV2PluginWrapper::range(unsigned long i) // { // LADSPA_PortRangeHint hint; // hint.HintDescriptor = 0; // hint.LowerBound = _synth->_pluginControlsMin [i]; // hint.UpperBound = _synth->_pluginControlsMax [i]; // // if(hint.LowerBound == hint.LowerBound) // { // hint.HintDescriptor |= LADSPA_HINT_BOUNDED_BELOW; // } // // if(hint.UpperBound == hint.UpperBound) // { // hint.HintDescriptor |= LADSPA_HINT_BOUNDED_ABOVE; // } // // return hint; // } // void LV2PluginWrapper::range(unsigned long i, float *min, float *max) const // { // *min = _synth->_pluginControlsMin [i]; // *max = _synth->_pluginControlsMax [i]; // } // // double LV2PluginWrapper::defaultValue(unsigned long port) const // { // return _synth->_pluginControlsDefault [port]; // } // const char *LV2PluginWrapper::portName(unsigned long i) // { // return lilv_node_as_string(lilv_port_get_name(_synth->_handle, lilv_plugin_get_port_by_index(_synth->_handle, i))); // } // // CtrlValueType LV2PluginWrapper::ctrlValueType(unsigned long i) const // { // CtrlValueType vt = VAL_LINEAR; // std::map::iterator it = _synth->_idxToControlMap.find(i); // assert(it != _synth->_idxToControlMap.end()); // i = it->second; // assert(i < _controlInPorts); // // switch(_synth->_controlInPorts [i].cType) // { // case LV2_PORT_CONTINUOUS: // vt = VAL_LINEAR; // break; // case LV2_PORT_DISCRETE: // case LV2_PORT_INTEGER: // vt = VAL_INT; // break; // case LV2_PORT_LOGARITHMIC: // vt = VAL_LOG; // break; // case LV2_PORT_TRIGGER: // vt = VAL_BOOL; // break; // default: // break; // } // // return vt; // } // CtrlList::Mode LV2PluginWrapper::ctrlMode(unsigned long i) const // { // std::map::iterator it = _synth->_idxToControlMap.find(i); // assert(it != _synth->_idxToControlMap.end()); // i = it->second; // assert(i < _controlInPorts); // // return ((_synth->_controlInPorts [i].cType == LV2_PORT_CONTINUOUS) // ||(_synth->_controlInPorts [i].cType == LV2_PORT_LOGARITHMIC)) ? CtrlList::INTERPOLATE : CtrlList::DISCRETE; // } // bool LV2PluginWrapper::hasNativeGui() const // { // return (_synth->_pluginUiTypes.size() > 0); // } // // void LV2PluginWrapper::showNativeGui(PluginI *p, bool bShow) // { // assert(p->instances > 0); // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; // // if(p->track() != NULL) // { // if(state->human_id != NULL) // { // free(state->human_id); // } // // state->extHost.plugin_human_id = state->human_id = strdup((p->track()->name() + QString(": ") + label()).toUtf8().constData()); // } // // LV2Synth::lv2ui_ShowNativeGui(state, bShow); // } // // bool LV2PluginWrapper::nativeGuiVisible(const PluginI *p) const // { // assert(p->instances > 0); // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; // return (state->widget != NULL); // } // // void LV2PluginWrapper::setLastStateControls(LADSPA_Handle handle, size_t index, bool bSetMask, bool bSetVal, bool bMask, float fVal) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // assert(state != NULL); // // if(_controlInPorts == 0) // return; // // if(bSetMask) // state->controlsMask [index] = bMask; // // if(bSetVal) // state->lastControls [index] = fVal; // // } // // void LV2PluginWrapper::writeConfiguration(LADSPA_Handle handle, int level, Xml &xml) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // assert(state != NULL); // // LV2Synth::lv2conf_write(state, level, xml); // } // // void LV2PluginWrapper::setCustomData(LADSPA_Handle handle, const std::vector &customParams) // { // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)handle; // assert(state != NULL); // // LV2Synth::lv2conf_set(state, customParams); // } // // void LV2PluginWrapper::populatePresetsMenu(PluginI *p, MusEGui::PopupMenu *menu) // { // assert(p->instances > 0); // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; // assert(state != NULL); // // LV2Synth::lv2state_populatePresetsMenu(state, menu); // // } // // void LV2PluginWrapper::applyPreset(PluginI *p, void *preset) // { // assert(p->instances > 0); // LV2PluginWrapper_State *state = (LV2PluginWrapper_State *)p->handle [0]; // assert(state != NULL); // // LV2Synth::lv2state_applyPreset(state, static_cast(preset)); // // } // // void LV2PluginWrapper_Worker::run() // { // while(true) // { // _mSem.acquire(1); // if(_closing) // break; // makeWork(); // // } // // } // // LV2_Worker_Status LV2PluginWrapper_Worker::scheduleWork() // { // if(_mSem.available() != 0) // return LV2_WORKER_ERR_NO_SPACE; // _mSem.release(1); // return LV2_WORKER_SUCCESS; // // } void LV2PluginWrapper_Worker::makeWork() { #ifdef DEBUG_LV2 std::cerr << "LV2PluginWrapper_Worker::makeWork" << std::endl; #endif if(_state->wrkIface && _state->wrkIface->work) { const void *dataBuffer = _state->wrkDataBuffer; uint32_t dataSize = _state->wrkDataSize; _state->wrkDataBuffer = NULL; _state->wrkDataSize = 0; if(_state->wrkIface->work(lilv_instance_get_handle(_state->handle), // LV2Synth::lv2wrk_respond, Lv2Plugin::lv2wrk_respond, _state, dataSize, dataBuffer) != LV2_WORKER_SUCCESS) { _state->wrkEndWork = false; _state->wrkDataBuffer = NULL; _state->wrkDataSize = 0; } } } #ifdef LV2_EVENT_BUFFER_SUPPORT LV2EvBuf::LV2EvBuf(bool isInput, bool oldApi, LV2_URID atomTypeSequence, LV2_URID atomTypeChunk) :_isInput(isInput), _oldApi(oldApi), _uAtomTypeSequence(atomTypeSequence), _uAtomTypeChunk(atomTypeChunk) #else LV2EvBuf::LV2EvBuf(bool isInput, LV2_URID atomTypeSequence, LV2_URID atomTypeChunk) :_isInput(isInput), _uAtomTypeSequence(atomTypeSequence), _uAtomTypeChunk(atomTypeChunk) #endif { if(_isInput) { // Resize and fill with initial value. _buffer.resize(LV2_EVBUF_SIZE, 0); } else { // Reserve the space. _buffer.reserve(LV2_EVBUF_SIZE); // Add one item, the first item. _buffer.assign(sizeof(LV2_Atom_Sequence), 0); } #ifdef LV2_DEBUG std::cerr << "LV2EvBuf ctor: _buffer size:" << _buffer.size() << " capacity:" << _buffer.capacity() << std::endl; #endif resetBuffer(); } size_t LV2EvBuf::mkPadSize(size_t size) { return (size + 7U) & (~7U); } void LV2EvBuf::resetPointers(bool r, bool w) { if(!r && !w) return; size_t ptr = 0; #ifdef LV2_EVENT_BUFFER_SUPPORT if(_oldApi) { ptr = sizeof(LV2_Event_Buffer); } else #endif { ptr = sizeof(LV2_Atom_Sequence); } if(r) { curRPointer = ptr; } if(w) { curWPointer = ptr; } } void LV2EvBuf::resetBuffer() { #ifdef LV2_EVENT_BUFFER_SUPPORT if(_oldApi) { _evbuf = reinterpret_cast(&_buffer [0]); _evbuf->capacity = _buffer.size() - sizeof(LV2_Event_Buffer); _evbuf->data = reinterpret_cast(_evbuf + 1); _evbuf->event_count = 0; _evbuf->header_size = sizeof(LV2_Event_Buffer); _evbuf->stamp_type = LV2_EVENT_AUDIO_STAMP; _evbuf->size = 0; } else #endif { _seqbuf = reinterpret_cast(&_buffer [0]); if(!_isInput) { _seqbuf->atom.type = _uAtomTypeChunk; _seqbuf->atom.size = _buffer.size() - sizeof(LV2_Atom_Sequence); } else { _seqbuf->atom.type = _uAtomTypeSequence; _seqbuf->atom.size = sizeof(LV2_Atom_Sequence_Body); } _seqbuf->body.unit = 0; _seqbuf->body.pad = 0; } resetPointers(true, true); } bool LV2EvBuf::write(uint32_t frames, uint32_t subframes, uint32_t type, uint32_t size, const uint8_t *data) { if(!_isInput) return false; #ifdef LV2_EVENT_BUFFER_SUPPORT if(_oldApi) { size_t paddedSize = mkPadSize(sizeof(LV2_Event) + size); size_t resSize = curWPointer + paddedSize; if(resSize > _buffer.size()) { std::cerr << "LV2 Event buffer overflow! frames=" << frames << ", size=" << size << std::endl; return false; } LV2_Event *ev = reinterpret_cast(&_buffer [curWPointer]); ev->frames = frames; ev->subframes = subframes; ev->type = type; ev->size = size; memcpy((void *)(ev + 1), data, size); curWPointer += paddedSize; _evbuf->size += paddedSize; _evbuf->event_count++; } else #endif { size_t paddedSize = mkPadSize(sizeof(LV2_Atom_Event) + size); size_t resSize = curWPointer + paddedSize; if(resSize > _buffer.size()) { std::cerr << "LV2 Atom_Event buffer overflow! frames=" << frames << ", size=" << size << std::endl; return false; } LV2_Atom_Event *ev = reinterpret_cast(&_buffer [curWPointer]); ev->time.frames = frames; ev->body.size = size; ev->body.type = type; memcpy(LV2_ATOM_BODY(&ev->body), data, size); _seqbuf->atom.size += paddedSize; curWPointer += paddedSize; } return true; } bool LV2EvBuf::read(uint32_t *frames, uint32_t *subframes, uint32_t *type, uint32_t *size, uint8_t **data) { *frames = *subframes = *type = *size = 0; *data = NULL; if(_isInput) return false; #ifdef LV2_EVENT_BUFFER_SUPPORT if(_oldApi) { LV2_Event *ev = reinterpret_cast(&_buffer [curRPointer]); if((_evbuf->size + sizeof(LV2_Event_Buffer) - curRPointer) < sizeof(LV2_Event)) { return false; } *frames = ev->frames; *subframes = ev->subframes; *type = ev->type; *size = ev->size; *data = reinterpret_cast(ev + 1); size_t padSize = mkPadSize(sizeof(LV2_Event) + ev->size); curRPointer += padSize; } else #endif { LV2_Atom_Event *ev = reinterpret_cast(&_buffer [curRPointer]); if((_seqbuf->atom.size + sizeof(LV2_Atom_Sequence) - curRPointer) < sizeof(LV2_Atom_Event)) { return false; } *frames = ev->time.frames; *type = ev->body.type; *size = ev->body.size; *data = reinterpret_cast(LV2_ATOM_BODY(&ev->body)); size_t padSize = mkPadSize(sizeof(LV2_Atom_Event) + ev->body.size); curRPointer += padSize; } return true; } uint8_t *LV2EvBuf::getRawBuffer() { return &_buffer [0]; } void LV2EvBuf::dump() { #ifdef LV2_EVENT_BUFFER_SUPPORT if(_oldApi){ return; } #endif int n = 1; LV2_Atom_Sequence *b = (LV2_Atom_Sequence *)&_buffer [0]; LV2_ATOM_SEQUENCE_FOREACH(b, s) { if(n == 1) { fprintf(stderr, "-------------- Atom seq dump START---------------\n"); } fprintf(stderr, "\tSeq. no.: %d\n", n); fprintf(stderr, "\t\tFrames: %ld\n", (long)s->time.frames); fprintf(stderr, "\t\tSize: %d\n", s->body.size); fprintf(stderr, "\t\tType: %d\n", s->body.type); fprintf(stderr, "\t\tData (hex):\n"); uint8_t *d = (uint8_t *)LV2_ATOM_BODY(&s->body); for(uint32_t i = 0; i < s->body.size; i++) { if((i % 10) == 0) { fprintf(stderr, "\n\t\t"); } else { fprintf(stderr, " "); } fprintf(stderr, "0x%02X", d [i]); } fprintf(stderr, "\n"); n++; } if(n > 1) { fprintf(stderr, "-------------- Atom seq dump END---------------\n\n"); } } LV2SimpleRTFifo::LV2SimpleRTFifo(size_t size): fifoSize(size), itemSize(LV2_RT_FIFO_ITEM_SIZE) { eventsBuffer.resize(fifoSize); assert(eventsBuffer.size() == fifoSize); readIndex = writeIndex = 0; for(size_t i = 0; i < fifoSize; ++i) { eventsBuffer [i].port_index = 0; eventsBuffer [i].buffer_size = 0; eventsBuffer [i].data = new char [itemSize]; } } LV2SimpleRTFifo::~LV2SimpleRTFifo() { for(size_t i = 0; i < fifoSize; ++i) { delete [] eventsBuffer [i].data; } } bool LV2SimpleRTFifo::put(uint32_t port_index, uint32_t size, const void *data) { if(size > itemSize) { #ifdef DEBUG_LV2 std::cerr << "LV2SimpleRTFifo:put(): size("<incReferences(1)==0) // return true; _state = new LV2PluginWrapper_State; // _state->inst = NULL; _state->widget = NULL; _state->uiInst = NULL; _state->plugInst = NULL; _state->_ifeatures = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; _state->_ppifeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; _state->sif = this; _state->synth = static_cast(plug); //use Lv2Plugin features as template // // _features = new LV2_Feature[SIZEOF_ARRAY(lv2Features)]; // _ppfeatures = new LV2_Feature *[SIZEOF_ARRAY(lv2Features) + 1]; // // //LV2Synth::lv2state_FillFeatures(_state); // //lv2state_FillFeatures(_state); // lv2_FillFeatures(ifeatures, ppifeatures, options); Lv2Plugin* lv2plug = static_cast(plug); //prepare features and options arrays LV2_Options_Option _tmpl_options [] = { {LV2_OPTIONS_INSTANCE, 0, lv2plug->mapUrid(LV2_P_SAMPLE_RATE), sizeof(float), lv2plug->mapUrid(LV2_ATOM__Float), &_sampleRate}, {LV2_OPTIONS_INSTANCE, 0, lv2plug->mapUrid(LV2_P_MIN_BLKLEN), sizeof(int32_t), lv2plug->mapUrid(LV2_ATOM__Int), &Lv2Plugin::minBlockSize}, //&_segSize}, {LV2_OPTIONS_INSTANCE, 0, lv2plug->mapUrid(LV2_P_MAX_BLKLEN), sizeof(int32_t), lv2plug->mapUrid(LV2_ATOM__Int), &_segSize}, {LV2_OPTIONS_INSTANCE, 0, lv2plug->mapUrid(LV2_P_NOM_BLKLEN), sizeof(int32_t), lv2plug->mapUrid(LV2_ATOM__Int), &_segSize}, {LV2_OPTIONS_INSTANCE, 0, lv2plug->mapUrid(LV2_P_SEQ_SIZE), sizeof(int32_t), lv2plug->mapUrid(LV2_ATOM__Int), &_segSize}, {LV2_OPTIONS_INSTANCE, 0, lv2plug->mapUrid(LV2_CORE__sampleRate), sizeof(double), lv2plug->mapUrid(LV2_ATOM__Double), &_dSampleRate}, {LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL} }; _options = new LV2_Options_Option[SIZEOF_ARRAY(_tmpl_options)]; // last option is NULLs for(uint32_t i = 0; i < SIZEOF_ARRAY(_tmpl_options); i++) { _options [i] = _tmpl_options [i]; } if (_plugin->incReferences(1)==0) { delete [] _state->_ppifeatures; _state->_ppifeatures = NULL; delete [] _state->_ifeatures; _state->_ifeatures = NULL; return true; } // _state->handle = _handle = lilv_plugin_instantiate(_synth->_handle, (double)MusEGlobal::sampleRate, _state->_ppifeatures); //LilvInstance* inst = lilv_plugin_instantiate(_handle, sampleRate, (LV2_Feature**)data); //LilvInstance* inst = lilv_plugin_instantiate(_handle, sampleRate, _state->_ppifeatures); //LilvInstance* inst = static_cast(plug->instantiate(sampleRate, _state)); _state->handle = _handle = static_cast(plug->instantiate(sampleRate, _state)); if(_handle == NULL) { _plugin->incReferences(-1); delete [] _state->_ppifeatures; _state->_ppifeatures = NULL; delete [] _state->_ifeatures; _state->_ifeatures = NULL; return true; } // if(inst == NULL) // { // delete [] ppifeatures; // //ppifeatures = NULL; // delete [] ifeatures; // //ifeatures = NULL; // return NULL; // } //if(_state->handle) //{ SP_DBG_LV2_2("Plugin instantiated", label().toLatin1().constData()); //} //return false; QString inst("-" + QString::number(_plugin->instNo())); _name = _plugin->name() + inst; _label = _plugin->label() + inst; const unsigned long ins = _plugin->inports(); const unsigned long outs = _plugin->outports(); if(outs) { _instances = _channel / outs; // Ask for one more instance for remainder if required. const int re = _channel % outs; if(re != 0) ++_instances; if(_instances < 1) _instances = 1; } else if(ins) { _instances = _channel / ins; // Ask for one more instance for remainder if required. const int re = _channel % ins; if(re != 0) ++_instances; if(_instances < 1) _instances = 1; } else _instances = 1; _handle = new LADSPA_Handle[_instances]; for(int i = 0; i < _instances; ++i) _handle[i]=NULL; for(int i = 0; i < _instances; ++i) { #ifdef PLUGIN_DEBUGIN fprintf(stderr, "Lv2PluginI::initPluginInstance instance:%d\n", i); #endif _handle[i] = _plugin->instantiate(_sampleRate); if(_handle[i] == NULL) return true; } const unsigned long port_count = _plugin->portCount(); _audioInPorts = 0; _audioOutPorts = 0; _controlPorts = 0; _controlOutPorts = 0; unsigned long port = 0; for (int i = 0; i < _instances; ++i) { for (unsigned long k = 0; k < port_count; ++k) { if (_plugin->isAudioIn(k)) { if(port < (unsigned long)_channel) ++_audioInPorts; ++port; } } } port = 0; for (int i = 0; i < _instances; ++i) { for (unsigned long k = 0; k < port_count; ++k) { if (_plugin->isAudioOut(k)) { if(port < (unsigned long)_channel) ++_audioOutPorts; ++port; } } } for(unsigned long k = 0; k < port_count; ++k) { if(_plugin->isParameterIn(k)) ++_controlPorts; else if(_plugin->isParameterOut(k)) ++_controlOutPorts; } if(_controlPorts) _controls = new Port[_controlPorts]; if(_controlOutPorts) { _controlsOut = new Port[_controlOutPorts]; _controlsOutDummy = new Port[_controlOutPorts]; } for(unsigned long k = 0; k < _controlPorts; ++k) { // Set the parameter input's initial value to the default. const float val = _plugin->defaultValue(k); _controls[k]._val = val; // All instances' parameter inputs share the same controls. // We don't have a mechanism to expose the other instances' inputs. for(int i = 0; i < _instances; ++i) _plugin->connectCtrlInport(_handle[i], k, &_controls[k]._val); } for(unsigned long k = 0; k < _controlOutPorts; ++k) { // Set the parameter output's initial value to zero. _controlsOut[k]._val = 0.0f; // Check for a latency port. const char* pname = _plugin->getParameterOutName(k); if(pname == QString("latency") || pname == QString("_latency")) { _hasLatencyOutPort = true; _latencyOutPort = k; } // Connect only the first instance's parameter output controls. // We don't have a mechanism to display the other instances' outputs. if(_instances > 0) { _plugin->connectCtrlOutport(_handle[0], k, &_controlsOut[k]._val); // Connect the rest to dummy ports. for(int i = 1; i < _instances; ++i) _plugin->connectCtrlOutport(_handle[i], k, &_controlsOutDummy[k]._val); } } int rv = posix_memalign((void **)&_audioInSilenceBuf, 16, sizeof(float) * _segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: Lv2PluginI::initPluginInstance: _audioInSilenceBuf posix_memalign returned error:%d. Aborting!\n", rv); abort(); } if(useDenormalBias) { for(unsigned q = 0; q < _segmentSize; ++q) { _audioInSilenceBuf[q] = denormalBias; } } else { memset(_audioInSilenceBuf, 0, sizeof(float) * _segmentSize); } rv = posix_memalign((void **)&_audioOutDummyBuf, 16, sizeof(float) * _segmentSize); if(rv != 0) { fprintf(stderr, "ERROR: Lv2PluginI::initPluginInstance: _audioOutDummyBuf posix_memalign returned error:%d. Aborting!\n", rv); abort(); } // Don't activate yet. //activate(); return false; } } // namespace MusESimplePlugin muse-4.0.0/src/synti/libsimpleplugin/simpler_plugin_lv2.h000066400000000000000000000602111404105311000235720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // simpler_plugin_lv2.h // (C) Copyright 2018 Tim E. Real (terminator356 on users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SIMPLER_PLUGIN_LV2_H__ #define __SIMPLER_PLUGIN_LV2_H__ #include "simpler_plugin.h" #include "lilv/lilv.h" #include "lv2/lv2plug.in/ns/ext/data-access/data-access.h" #include "lv2/lv2plug.in/ns/ext/state/state.h" #include "lv2/lv2plug.in/ns/ext/atom/atom.h" #include "lv2/lv2plug.in/ns/ext/midi/midi.h" #include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h" #ifdef LV2_EVENT_BUFFER_SUPPORT #include "lv2/lv2plug.in/ns/ext/event/event.h" #endif #include "lv2/lv2plug.in/ns/ext/options/options.h" #include "lv2/lv2plug.in/ns/ext/parameters/parameters.h" #include "lv2/lv2plug.in/ns/ext/patch/patch.h" #include "lv2/lv2plug.in/ns/ext/port-groups/port-groups.h" #include "lv2/lv2plug.in/ns/ext/presets/presets.h" #include "lv2/lv2plug.in/ns/ext/state/state.h" #include "lv2/lv2plug.in/ns/ext/time/time.h" #ifdef LV2_URI_MAP_SUPPORT #include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h" #endif #include "lv2/lv2plug.in/ns/ext/urid/urid.h" #include "lv2/lv2plug.in/ns/ext/worker/worker.h" #include "lv2/lv2plug.in/ns/ext/port-props/port-props.h" #include "lv2/lv2plug.in/ns/ext/atom/forge.h" #include "lv2/lv2plug.in/ns/ext/log/log.h" #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #include "lv2/lv2plug.in/ns/ext/dynmanifest/dynmanifest.h" #include "lv2extui.h" #include "lv2extprg.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include "midictrl.h" #include "midictrl_consts.h" //#include "synth.h" #include "stringparam.h" //#include "plugin.h" namespace MusESimplePlugin { //--------------------------------------------------------- // Lv2Plugin //--------------------------------------------------------- // #define IS_AUDIO_IN (LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT) // #define IS_AUDIO_OUT (LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT) // #define IS_PARAMETER_IN (LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT) // #define IS_PARAMETER_OUT (LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT) #define LV2_RT_FIFO_SIZE 128 #define LV2_RT_FIFO_ITEM_SIZE (std::max(size_t(4096 * 16), size_t(MusEGlobal::segmentSize * 16))) #define LV2_EVBUF_SIZE (2*LV2_RT_FIFO_ITEM_SIZE) struct LV2MidiEvent { int64_t frame; uint8_t midi [4]; }; class LV2EvBuf { #ifdef LV2_EVENT_BUFFER_SUPPORT enum LV2_BUF_TYPE { LV2_BUF_EVENT, LV2_BUF_ATOM }; #endif std::vector _buffer; size_t curWPointer; size_t curRPointer; bool _isInput; #ifdef LV2_EVENT_BUFFER_SUPPORT bool _oldApi; #endif LV2_URID _uAtomTypeSequence; LV2_URID _uAtomTypeChunk; LV2_Atom_Sequence *_seqbuf; #ifdef LV2_EVENT_BUFFER_SUPPORT LV2_Event_Buffer *_evbuf; #endif public: #ifdef LV2_EVENT_BUFFER_SUPPORT LV2EvBuf(bool isInput, bool oldApi, LV2_URID atomTypeSequence, LV2_URID atomTypeChunk); #else LV2EvBuf(bool isInput, LV2_URID atomTypeSequence, LV2_URID atomTypeChunk); #endif inline size_t mkPadSize(size_t size); inline void resetPointers(bool r, bool w); inline void resetBuffer(); bool write(uint32_t frames, uint32_t subframes, uint32_t type, uint32_t size, const uint8_t *data); bool read(uint32_t *frames, uint32_t *subframes, uint32_t *type, uint32_t *size, uint8_t **data ); uint8_t *getRawBuffer(); void dump(); }; class LV2SimpleRTFifo { public: typedef struct _lv2_uiControlEvent { uint32_t port_index; long buffer_size; char *data; } lv2_uiControlEvent; private: //size_t numItems; std::vector eventsBuffer; size_t readIndex; size_t writeIndex; size_t fifoSize; size_t itemSize; public: LV2SimpleRTFifo(size_t size); ~LV2SimpleRTFifo(); inline size_t getItemSize(){return itemSize; } bool put(uint32_t port_index, uint32_t size, const void *data); bool get(uint32_t *port_index, size_t *szOut, char *data_out); }; struct LV2MidiPort { #ifdef LV2_EVENT_BUFFER_SUPPORT LV2MidiPort (const LilvPort *_p, uint32_t _i, QString _n, bool _f, bool _supportsTimePos) : port ( _p ), index ( _i ), name ( _n ), old_api ( _f ), supportsTimePos(_supportsTimePos), buffer(0){} #else LV2MidiPort (const LilvPort *_p, uint32_t _i, QString _n, bool _supportsTimePos) : port ( _p ), index ( _i ), name ( _n ), supportsTimePos(_supportsTimePos), buffer(0){} #endif const LilvPort *port; uint32_t index; //plugin real port index QString name; #ifdef LV2_EVENT_BUFFER_SUPPORT bool old_api; //true for LV2_Event port #endif bool supportsTimePos; LV2EvBuf *buffer; }; enum LV2ControlPortType { LV2_PORT_DISCRETE = 1, LV2_PORT_INTEGER, LV2_PORT_CONTINUOUS, LV2_PORT_LOGARITHMIC, LV2_PORT_TRIGGER }; struct LV2ControlPort { LV2ControlPort ( const LilvPort *_p, uint32_t _i, float _c, const char *_n, const char *_s, LV2ControlPortType _ctype, bool _isCVPort = false) : port ( _p ), index ( _i ), defVal ( _c ), minVal( _c ), maxVal ( _c ), cType(_ctype), isCVPort(_isCVPort) { cName = strdup ( _n ); cSym = strdup(_s); } LV2ControlPort ( const LV2ControlPort &other ) : port ( other.port ), index ( other.index ), defVal ( other.defVal ), minVal(other.minVal), maxVal(other.maxVal), cType(other.cType), isCVPort(other.isCVPort) { cName = strdup ( other.cName ); cSym = strdup(other.cSym); } ~LV2ControlPort() { free ( cName ); cName = NULL; free(cSym); cSym = NULL; } const LilvPort *port; uint32_t index; //plugin real port index float defVal; //default control value float minVal; //minimum control value float maxVal; //maximum control value char *cName; //cached value to share between function calls char *cSym; //cached port symbol LV2ControlPortType cType; bool isCVPort; }; struct LV2AudioPort { LV2AudioPort ( const LilvPort *_p, uint32_t _i, float *_b, QString _n ) : port ( _p ), index ( _i ), buffer ( _b ), name ( _n ) {} const LilvPort *port; uint32_t index; //plugin real port index float *buffer; //audio buffer QString name; }; struct cmp_str { bool operator() ( char const *a, char const *b ) const { return std::strcmp ( a, b ) < 0; } }; typedef std::vector LV2_MIDI_PORTS; typedef std::vector LV2_CONTROL_PORTS; typedef std::vector LV2_AUDIO_PORTS; typedef std::map LV2_SYNTH_URID_MAP; typedef std::map LV2_SYNTH_URID_RMAP; class LV2UridBiMap { private: LV2_SYNTH_URID_MAP _map; LV2_SYNTH_URID_RMAP _rmap; uint32_t nextId; QMutex idLock; public: LV2UridBiMap(); ~LV2UridBiMap(); LV2_URID map ( const char *uri ); const char *unmap ( uint32_t id ); }; //class LV2SynthIF; struct LV2PluginWrapper_State; typedef std::map > LV2_PLUGIN_UI_TYPES; //-------------------------------- // Lv2Plugin //-------------------------------- class Lv2Plugin : public Plugin { private: // #ifdef __clang__ // #pragma GCC diagnostic push // #pragma GCC diagnostic ignored "-Wunused-private-field" // #endif // LADSPA_Descriptor_Function ladspa; // #ifdef __clang__ // #pragma GCC diagnostic pop // #endif // const LADSPA_Descriptor* plugin; // Accepts a master port index. void port_range(unsigned long i, int sampleRate, float* min, float* max) const; // int32_t _segSize; const LilvPlugin *_handle; LV2UridBiMap uridBiMap; LV2_Feature *_features; LV2_Feature **_ppfeatures; // LV2_Options_Option *_options; LV2_URID_Map _lv2_urid_map; LV2_URID_Unmap _lv2_urid_unmap; #ifdef LV2_URI_MAP_SUPPORT LV2_URI_Map_Feature _lv2_uri_map; #endif LV2_Log_Log _lv2_log_log; // double _sampleRate; // float _fSampleRate; bool _isSynth; int _uniqueID; uint32_t _midi_event_id; LilvUIs *_uis; std::map _idxToControlMap; LV2_PLUGIN_UI_TYPES _pluginUiTypes; //templates for LV2SynthIF and LV2PluginWrapper instantiation LV2_MIDI_PORTS _midiInPorts; LV2_MIDI_PORTS _midiOutPorts; LV2_CONTROL_PORTS _controlInPorts; LV2_CONTROL_PORTS _controlOutPorts; LV2_AUDIO_PORTS _audioInPorts; LV2_AUDIO_PORTS _audioOutPorts; // MidiCtl2LadspaPortMap midiCtl2PortMap; // Maps midi controller numbers to LV2 port numbers. // MidiCtl2LadspaPortMap port2MidiCtlMap; // Maps LV2 port numbers to midi controller numbers. uint32_t _fOptions; uint32_t _fInstanceAccess; uint32_t _fUiParent; uint32_t _fExtUiHost; uint32_t _fExtUiHostD; uint32_t _fDataAccess; uint32_t _fWrkSchedule; uint32_t _fUiResize; uint32_t _fPrgHost; uint32_t _fMakePath; uint32_t _fMapPath; //const LilvNode *_pluginUIType = NULL; LV2_URID _uTime_Position; LV2_URID _uTime_frame; LV2_URID _uTime_speed; LV2_URID _uTime_beatsPerMinute; LV2_URID _uTime_barBeat; LV2_URID _uAtom_EventTransfer; LV2_URID _uAtom_Chunk; LV2_URID _uAtom_Sequence; LV2_URID _uAtom_StateChanged; LV2_URID _uAtom_Object; bool _hasFreeWheelPort; uint32_t _freeWheelPortIndex; bool _isConstructed; float *_pluginControlsDefault; float *_pluginControlsMin; float *_pluginControlsMax; std::map _presets; protected: int _iSampleRate; public: // Lv2Plugin(const QFileInfo* f, const LADSPA_Descriptor_Function, const LADSPA_Descriptor* d); //Lv2Plugin(const QFileInfo *f, QString /*label*/, QString /*name*/, QString /*author*/, // const LilvPlugin *plugin); //Lv2Plugin(const QFileInfo *fi, QString label, QString name, QString author, // const LilvPlugin *plugin, float samplerate, unsigned int segment_size); Lv2Plugin(const QFileInfo *fi, QString label, QString name, QString author, const LilvPlugin *plugin, PluginFeaturesType requiredFeatures); virtual ~Lv2Plugin(); // Just so we can point to a zero. static const unsigned minBlockSize; // Create and initialize a LADSPA plugin instance. Returns null if failure. // Equivalent to calling (new LadspaPlugI())->initPluginInstance(this, ...). // The returned type depends on the this class (LadspaPluginI*, Lv2PluginI*, etc). // Caller is responsible for deleting the returned object. PluginI* createPluginI(int chans, float sampleRate, unsigned int segmentSize, bool useDenormalBias, float denormalBias); int incReferences(int); //own public functions bool isConstructed() { return _isConstructed; } void lv2_FillFeatures (LV2_Feature *features, LV2_Feature **ppfeatures, LV2_Options_Option* options); static void lv2state_FillFeatures ( LV2PluginWrapper_State *state ); static void lv2state_FreeState(LV2PluginWrapper_State *state); // static void lv2ui_FreeDescriptors(LV2PluginWrapper_State *state); static int lv2_printf(LV2_Log_Handle handle, LV2_URID type, const char *fmt, ...); static int lv2_vprintf(LV2_Log_Handle, LV2_URID, const char *fmt, va_list ap); static char *lv2state_makePath(LV2_State_Make_Path_Handle handle, const char *path); static char *lv2state_abstractPath(LV2_State_Map_Path_Handle handle, const char *absolute_path); static char *lv2state_absolutePath(LV2_State_Map_Path_Handle handle, const char *abstract_path); static LV2_Worker_Status lv2wrk_scheduleWork(LV2_Worker_Schedule_Handle handle, uint32_t size, const void *data); static LV2_Worker_Status lv2wrk_respond(LV2_Worker_Respond_Handle handle, uint32_t size, const void* data); static unsigned lv2ui_IsSupported (const char *, const char *ui_type_uri); static void lv2state_UnloadLoadPresets(Lv2Plugin *synth, bool load = false, bool update = false); LV2_URID mapUrid ( const char *uri ); const char *unmapUrid ( LV2_URID id ); // int sampleRate() const { return _iSampleRate; } // void setSampleRate(int rate) { // _iSampleRate = rate; _sampleRate = (double)_iSampleRate; _fSampleRate = (float)_iSampleRate; } // bool isAudioIn(unsigned long k) const { // if(!_plugin) // return false; // return (_plugin->PortDescriptors[k] & IS_AUDIO_IN) == IS_AUDIO_IN; // } // bool isAudioOut(unsigned long k) const { // if(!_plugin) // return false; // return (_plugin->PortDescriptors[k] & IS_AUDIO_OUT) == IS_AUDIO_OUT; // } // bool isParameterIn(unsigned long k) const { // if(!_plugin) // return false; // return (_plugin->PortDescriptors[k] & IS_PARAMETER_IN) == IS_PARAMETER_IN; // } // bool isParameterOut(unsigned long k) const { // if(!_plugin) // return false; // return (_plugin->PortDescriptors[k] & IS_PARAMETER_OUT) == IS_PARAMETER_OUT; // } // // bool isLog(unsigned long k) const { // if(!_plugin) // return false; // LADSPA_PortRangeHint r = _plugin->PortRangeHints[_pIdx[k]]; // return LADSPA_IS_HINT_LOGARITHMIC(r.HintDescriptor); // } // bool isBool(unsigned long k) const { // if(!_plugin) // return false; // return LADSPA_IS_HINT_TOGGLED(_plugin->PortRangeHints[_pIdx[k]].HintDescriptor); // } // bool isInt(unsigned long k) const { // if(!_plugin) // return false; // LADSPA_PortRangeHint r = _plugin->PortRangeHints[_pIdx[k]]; // return LADSPA_IS_HINT_INTEGER(r.HintDescriptor); // } // bool isLinear(unsigned long k) const { // if(!_plugin) // return false; // LADSPA_PortRangeHint r = _plugin->PortRangeHints[_pIdx[k]]; // return !LADSPA_IS_HINT_INTEGER(r.HintDescriptor) && // !LADSPA_IS_HINT_LOGARITHMIC(r.HintDescriptor) && // !LADSPA_IS_HINT_TOGGLED(r.HintDescriptor); // } // bool range(unsigned long k, float sampleRate, float*, float*) const; // bool rangeOut(unsigned long k, float sampleRate, float*, float*) const; // const char* getParameterName(unsigned long k) const { // if(!_plugin) // return 0; // return _plugin->PortNames[_pIdx[k]]; // } // const char* getParameterOutName(unsigned long k) const { // if(!_plugin) // return 0; // return _plugin->PortNames[_poIdx[k]]; // } // float defaultValue(unsigned long k) const; // // float convertGuiControlValue(unsigned long k, float sampleRate, int val) const; void* instantiate(float sampleRate, void* data); void connectInport(void* handle, unsigned long k, void* datalocation); void connectOutport(void* handle, unsigned long k, void* datalocation); void connectCtrlInport(void* handle, unsigned long k, void* datalocation); void connectCtrlOutport(void* handle, unsigned long k, void* datalocation); // void activate(void* handle) { // if (_plugin && _plugin->activate) // _plugin->activate((LADSPA_Handle)handle); // } // void deactivate(void* handle) { // if (_plugin && _plugin->deactivate) // _plugin->deactivate((LADSPA_Handle)handle); // } // void cleanup(void* handle) { // if (_plugin && _plugin->cleanup) // _plugin->cleanup((LADSPA_Handle)handle); // } // void connectPort(void* handle, unsigned long port, float* datalocation) { // if(_plugin) // _plugin->connect_port((LADSPA_Handle)handle, port, datalocation); // } // void apply(void* handle, unsigned long n) { // if(_plugin && _plugin->run) // _plugin->run((LADSPA_Handle)handle, n); }; class Lv2PluginI; class LV2PluginWrapper_Worker; struct LV2PluginWrapper_State { LV2PluginWrapper_State(): _ifeatures(NULL), _ppifeatures(NULL), widget(NULL), handle(NULL), uiDlHandle(NULL), uiDesc(NULL), uiInst(NULL), // inst(NULL), lastControls(NULL), controlsMask(NULL), lastControlsOut(NULL), plugInst(NULL), sif(NULL), synth(NULL), human_id(NULL), iState(NULL), tmpValues(NULL), numStateValues(0), wrkDataSize(0), wrkDataBuffer(0), wrkThread(NULL), wrkEndWork(false), controlTimers(NULL), deleteLater(false), hasGui(false), hasExternalGui(false), uiIdleIface(NULL), uiCurrent(NULL), uiX11Size(0, 0), pluginWindow(NULL), pluginQWindow(NULL), prgIface(NULL), uiPrgIface(NULL), uiDoSelectPrg(false), newPrgIface(false), uiChannel(0), uiBank(0), uiProg(0), gtk2Plug(NULL), pluginCVPorts(NULL), uiControlEvt(LV2_RT_FIFO_SIZE), plugControlEvt(LV2_RT_FIFO_SIZE), gtk2ResizeCompleted(false), gtk2AllocateCompleted(false), songDirtyPending(false), uiIsOpening(false) { extHost.plugin_human_id = NULL; extHost.ui_closed = NULL; uiResize.handle = (LV2UI_Feature_Handle)this; // TODO: UI // uiResize.ui_resize = LV2Synth::lv2ui_Resize; prgHost.handle = (LV2_Programs_Handle)this; // TODO: prg // prgHost.program_changed = LV2SynthIF::lv2prg_Changed; makePath.handle = (LV2_State_Make_Path_Handle)this; makePath.path = Lv2Plugin::lv2state_makePath; mapPath.handle = (LV2_State_Map_Path_Handle)this; mapPath.absolute_path = Lv2Plugin::lv2state_absolutePath; mapPath.abstract_path = Lv2Plugin::lv2state_abstractPath; midiInPorts.clear(); midiOutPorts.clear(); idx2EvtPorts.clear(); inPortsMidi = outPortsMidi = 0; } LV2_Feature *_ifeatures; LV2_Feature **_ppifeatures; void *widget; LV2_External_UI_Host extHost; LV2_Extension_Data_Feature extData; LV2_Worker_Schedule wrkSched; LV2_State_Make_Path makePath; LV2_State_Map_Path mapPath; LilvInstance *handle; void *uiDlHandle; const LV2UI_Descriptor *uiDesc; LV2UI_Handle uiInst; // LV2PluginWrapper *inst; //Lv2PluginI *inst; float *lastControls; bool *controlsMask; float *lastControlsOut; PluginI *plugInst; // LV2SynthIF *sif; // LV2Synth *synth; Lv2PluginI *sif; Lv2Plugin *synth; char *human_id; LV2_State_Interface *iState; QMap > iStateValues; char **tmpValues; size_t numStateValues; uint32_t wrkDataSize; const void *wrkDataBuffer; LV2PluginWrapper_Worker *wrkThread; LV2_Worker_Interface *wrkIface; bool wrkEndWork; int *controlTimers; bool deleteLater; LV2_Atom_Forge atomForge; float curBpm; bool curIsPlaying; unsigned int curFrame; bool hasGui; bool hasExternalGui; LV2UI_Idle_Interface *uiIdleIface; const LilvUI *uiCurrent; LV2UI_Resize uiResize; QSize uiX11Size; LV2PluginWrapper_Window *pluginWindow; QWindow *pluginQWindow; LV2_MIDI_PORTS midiInPorts; LV2_MIDI_PORTS midiOutPorts; size_t inPortsMidi; size_t outPortsMidi; LV2_Programs_Interface *prgIface; LV2_Programs_UI_Interface *uiPrgIface; bool uiDoSelectPrg; bool newPrgIface; std::map index2prg; std::map prg2index; LV2_Programs_Host prgHost; unsigned char uiChannel; int uiBank; int uiProg; void *gtk2Plug; std::map controlsNameMap; std::map controlsSymMap; float **pluginCVPorts; LV2SimpleRTFifo uiControlEvt; LV2SimpleRTFifo plugControlEvt; std::map idx2EvtPorts; bool gtk2ResizeCompleted; bool gtk2AllocateCompleted; bool songDirtyPending; bool uiIsOpening; }; class LV2PluginWrapper_Worker :public QThread { private: LV2PluginWrapper_State *_state; QSemaphore _mSem; bool _closing; public: explicit LV2PluginWrapper_Worker ( LV2PluginWrapper_State *s ) : QThread(), _state ( s ), _mSem(0), _closing(false) {} void run(); LV2_Worker_Status scheduleWork(); void makeWork(); void setClosing() {_closing = true; _mSem.release();} }; //-------------------------------- // Lv2PluginI // Lv2 Plugin Instance class //-------------------------------- class Lv2PluginI : public PluginI { private: //LADSPA_Handle* _handle; // per instance LilvInstance *_handle; // per instance // LV2_Feature *_features; // LV2_Feature **_ppfeatures; LV2_Options_Option *_options; LV2_CONTROL_PORTS _controlInPorts; LV2_CONTROL_PORTS _controlOutPorts; LV2_AUDIO_PORTS _audioInPorts; LV2_AUDIO_PORTS _audioOutPorts; // LV2_Feature *_ifeatures; // LV2_Feature **_ppifeatures; // Port *_controls; // Port *_controlsOut; // bool init ( LV2Synth *s ); // size_t _inports; // size_t _outports; // size_t _inportsControl; // size_t _outportsControl; // size_t _inportsMidi; // size_t _outportsMidi; // float **_audioInBuffers; // float **_audioOutBuffers; // float *_audioInSilenceBuf; // Just all zeros all the time, so we don't have to clear for silence. // void doSelectProgram(unsigned char channel, int bankH, int bankL, int prog); // inline void sendLv2MidiEvent(LV2EvBuf *evBuf, long frame, int paramCount, uint8_t a, uint8_t b = 0, uint8_t c = 0); // bool processEvent (const MidiPlayEvent &, LV2EvBuf *evBuf, long frame); // bool lv2MidiControlValues ( size_t port, int ctlnum, int *min, int *max, int *def ); // float midi2Lv2Value ( unsigned long port, int ctlnum, int val ); LV2PluginWrapper_State *_state; int32_t _segSize; void init(); void lv2_FillFeatures(LV2_Feature *features, LV2_Feature **ppfeatures, LV2_Options_Option* options); public: Lv2PluginI(); virtual ~Lv2PluginI(); // Returns true on error. bool initPluginInstance(Plugin* plug, int channels, float sampleRate, unsigned int segmentSize, bool useDenormalBias, float denormalBias); void setChannels(int chans); // Runs the plugin for frames. Any ports involved must already be connected. void process(unsigned long frames); // Connects ports with data locations. Multiple sources and destinations, with offset. void connect(unsigned long ports, unsigned long offset, float** src, float** dst); // // Connects a single audio input port to a data location. // void connectInport(unsigned long k, void* datalocation); // // Connects a single audio output port to a data location. // void connectOutport(unsigned long k, void* datalocation); // // Connects a single control parameter input port to a data location. // void connectCtrlInport(unsigned long k, void* datalocation); // // Connects a single control parameter output port to a data location. // void connectCtrlOutport(unsigned long k, void* datalocation); // Return true on success. bool activate(); // Return true on success. bool deactivate(); }; } // namespace MusESimplePlugin #endif muse-4.0.0/src/synti/libsimpleplugin/simpler_plugingui.cpp000066400000000000000000000075721404105311000240620ustar00rootroot00000000000000// // C++ Implementation: ssplugingui // // Description: // // // Author: Mathias Lundgren , (C) 2004 // Contributer: (C) Copyright 2011 Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // // Copyright: See COPYING file that comes with this distribution // // #include "simpler_plugingui.h" #define SS_PLUGINCHOOSER_NAMECOL 0 #define SS_PLUGINCHOOSER_LABELCOL 1 #define SS_PLUGINCHOOSER_INPORTSCOL 2 #define SS_PLUGINCHOOSER_OUTPORTSCOL 3 #define SS_PLUGINCHOOSER_CREATORCOL 4 namespace MusESimplePlugin { //--------------------------------------------------------- // getPlugin //--------------------------------------------------------- Plugin* SimplerPluginChooser::getPlugin(QWidget* parent) { SimplerPluginChooser* dialog = new SimplerPluginChooser(parent); Plugin* p = 0; int rv = dialog->exec(); if(rv) p = dialog->getSelectedPlugin(); delete dialog; return p; } SimplerPluginChooser::SimplerPluginChooser(QWidget* parent) :QDialog(parent) { setupUi(this); selectedPlugin = 0; for (iPlugin i=plugins.begin(); i !=plugins.end(); i++) { //Support for only 2 or 1 inport/outports if ( ((*i)->outports() == 2 || (*i)->outports() == 1) && ((*i)->inports() == 2 || (*i)->inports() == 1) ) { QTreeWidgetItem* tmpItem = new QTreeWidgetItem(effectsListView); tmpItem->setText(SS_PLUGINCHOOSER_NAMECOL, (*i)->name()); tmpItem->setText(SS_PLUGINCHOOSER_LABELCOL, (*i)->label()); tmpItem->setText(SS_PLUGINCHOOSER_INPORTSCOL, QString::number((*i)->inports())); tmpItem->setText(SS_PLUGINCHOOSER_OUTPORTSCOL, QString::number((*i)->outports())); tmpItem->setText(SS_PLUGINCHOOSER_CREATORCOL, (*i)->maker()); effectsListView->addTopLevelItem(tmpItem); } } connect(okButton, SIGNAL(pressed()), SLOT(okPressed())); connect(cancelButton, SIGNAL(pressed()), SLOT(cancelPressed())); connect(effectsListView, SIGNAL(itemSelectionChanged()), SLOT(selectionChanged())); connect(effectsListView, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), SLOT(doubleClicked(QTreeWidgetItem*))); } void SimplerPluginChooser::selectionChanged() { selectedItem = effectsListView->currentItem(); } void SimplerPluginChooser::okPressed() { selectedPlugin = findSelectedPlugin(); done(QDialog::Accepted); } void SimplerPluginChooser::cancelPressed() { done(QDialog::Rejected); } void SimplerPluginChooser::doubleClicked(QTreeWidgetItem* item) { selectedItem = item; selectedPlugin = findSelectedPlugin(); done(QDialog::Accepted); } Plugin* SimplerPluginChooser::findSelectedPlugin() { if(!selectedItem) return 0; Plugin* selected = 0; for (iPlugin i = plugins.begin(); i != plugins.end(); i++) { if ((*i)->name() == selectedItem->text(SS_PLUGINCHOOSER_NAMECOL)) selected = (*i); } return selected; } } // namespace MusESimplePlugin muse-4.0.0/src/synti/libsimpleplugin/simpler_plugingui.h000066400000000000000000000036001404105311000235130ustar00rootroot00000000000000// // C++ Interface: ssplugingui // // Description: // // // Author: Mathias Lundgren , (C) 2004 // Contributer: (C) Copyright 2011 Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // // Copyright: See COPYING file that comes with this distribution // // #ifndef __SIMPLER_PLUGIN_GUI_H__ #define __SIMPLER_PLUGIN_GUI_H__ #include #include "ui_simplepluginchooserbase.h" #include "libsimpleplugin/simpler_plugin.h" namespace MusESimplePlugin { //------------------------------- // SimplerPluginChooser //------------------------------- class SimplerPluginChooser : public QDialog, Ui::SimplePluginChooserBase { Q_OBJECT private: Plugin* selectedPlugin; protected: public: SimplerPluginChooser(QWidget* parent); static Plugin* getPlugin(QWidget* parent); Plugin* getSelectedPlugin() { return selectedPlugin; } private slots: void okPressed(); void cancelPressed(); void selectionChanged(); void doubleClicked(QTreeWidgetItem* item); private: QTreeWidgetItem* selectedItem; Plugin* findSelectedPlugin(); }; } // namespace MusESimplePlugin #endif muse-4.0.0/src/synti/libsynti/000077500000000000000000000000001404105311000162435ustar00rootroot00000000000000muse-4.0.0/src/synti/libsynti/CMakeLists.txt000066400000000000000000000037441404105311000210130ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= QT5_WRAP_CPP ( mess_mocs gui.h ) ## ## List of source files to compile ## file (GLOB synti_source_files mess.cpp # mess2.cpp gui.cpp mono.cpp poly.cpp # midievent.cpp # Removed. Causing conflicts with /muse/mpevent ## mpevent.cpp ) ## ## Define target ## add_library(synti ${MODULES_BUILD} ${mess_mocs} ${synti_source_files} ) target_link_libraries(synti mpevent_module # TODO: Try to remove this dependency by splitting the gui out of synti. ${QT_LIBRARIES} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${synti_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## set_target_properties( synti PROPERTIES OUTPUT_NAME muse_synti ) ## ## Install location ## if ( ${MODULES_BUILD} STREQUAL SHARED ) install(TARGETS synti DESTINATION ${MusE_MODULES_DIR} ) endif ( ${MODULES_BUILD} STREQUAL SHARED ) muse-4.0.0/src/synti/libsynti/evdata.h.OLD000066400000000000000000000033511404105311000202770ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: evdata.h,v 1.1 2004/02/13 13:55:03 wschweer Exp $ // // (C) Copyright 1999-2003 Werner Schweer (ws@seh.de) //========================================================= #ifndef __EVDATA_H__ #define __EVDATA_H__ //#include #include // p4.0.2 //--------------------------------------------------------- // EvData // variable len event data (sysex, meta etc.) //--------------------------------------------------------- class EvData { int* refCount; public: unsigned char* data; int dataLen; EvData() { data = 0; dataLen = 0; refCount = new int(1); } EvData(const EvData& ed) { data = ed.data; dataLen = ed.dataLen; refCount = ed.refCount; (*refCount)++; } EvData& operator=(const EvData& ed) { if (data == ed.data) return *this; if (--(*refCount) == 0) { delete refCount; delete[] data; } data = ed.data; dataLen = ed.dataLen; refCount = ed.refCount; (*refCount)++; return *this; } ~EvData() { if (--(*refCount) == 0) { delete[] data; delete refCount; } } void setData(const unsigned char* p, int l) { if(data) delete[] data; // p4.0.27 data = new unsigned char[l]; memcpy(data, p, l); dataLen = l; } }; #endif muse-4.0.0/src/synti/libsynti/gui.cpp000066400000000000000000000112161404105311000175340ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // software synthesizer helper library // $Id: gui.cpp,v 1.5 2004/04/11 10:46:14 wschweer Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "gui.h" #include "muse/midi_consts.h" #include #include SignalGui::SignalGui() { } void SignalGui::create() { // int filedes[2]; // 0 - reading 1 - writing // if (pipe(filedes) == -1) { // perror("thread:creating pipe4"); // exit(-1); // } // readFd = filedes[0]; // writeFd = filedes[1]; // QSocketNotifier* s = new QSocketNotifier(readFd, QSocketNotifier::Read); // connect(s, SIGNAL(activated(int)), SIGNAL(wakeup())); } void SignalGui::clearSignal() { // printf("clearSignal %ld\n", (long)QThread::currentThreadId()); // char c; // ::read(readFd, &c, 1); } void SignalGui::sendSignal() { // printf("sendSignal - emit wakeup() %ld\n", (long)QThread::currentThreadId()); emit wakeup(); // write(writeFd, "x", 1); // wakeup GUI } //--------------------------------------------------------- // MessGui //--------------------------------------------------------- MessGui::MessGui() { // // prepare for interprocess communication: // guiSignal.create(); wFifoSize = 0; wFifoWindex = 0; wFifoRindex = 0; rFifoSize = 0; rFifoWindex = 0; rFifoRindex = 0; } //--------------------------------------------------------- // MessGui //--------------------------------------------------------- MessGui::~MessGui() { } //--------------------------------------------------------- // readMessage //--------------------------------------------------------- void MessGui::readMessage() { while (rFifoSize) { guiSignal.clearSignal(); processEvent(rFifo[rFifoRindex]); rFifoRindex = (rFifoRindex + 1) % EVENT_FIFO_SIZE; --rFifoSize; } } //--------------------------------------------------------- // sendEvent //--------------------------------------------------------- void MessGui::sendEvent(const MusECore::MidiPlayEvent& ev) { if (wFifoSize == EVENT_FIFO_SIZE) { printf("event gui->synti fifo overflow\n"); return; } wFifo[wFifoWindex] = ev; wFifoWindex = (wFifoWindex + 1) % EVENT_FIFO_SIZE; ++wFifoSize; } //--------------------------------------------------------- // sendController //--------------------------------------------------------- void MessGui::sendController(int ch, int idx, int val) { sendEvent(MusECore::MidiPlayEvent(0, 0, ch, MusECore::ME_CONTROLLER, idx, val)); } //--------------------------------------------------------- // sendSysex //--------------------------------------------------------- void MessGui::sendSysex(unsigned char* p, int n) { sendEvent(MusECore::MidiPlayEvent(0, 0, MusECore::ME_SYSEX, p, n)); } //--------------------------------------------------------- // writeEvent // send an event to synti gui //--------------------------------------------------------- void MessGui::writeEvent(const MusECore::MidiPlayEvent& ev) { if (rFifoSize == EVENT_FIFO_SIZE) { printf("event synti->gui fifo overflow\n"); return; } rFifo[rFifoWindex] = ev; rFifoWindex = (rFifoWindex + 1) % EVENT_FIFO_SIZE; ++rFifoSize; guiSignal.sendSignal(); } //--------------------------------------------------------- // readEvent // read event from synti gui //--------------------------------------------------------- MusECore::MidiPlayEvent MessGui::readEvent() { MusECore::MidiPlayEvent ev = wFifo[wFifoRindex]; wFifoRindex = (wFifoRindex + 1) % EVENT_FIFO_SIZE; --wFifoSize; return ev; } muse-4.0.0/src/synti/libsynti/gui.h000066400000000000000000000054631404105311000172100ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // software synthesizer helper library // $Id: gui.h,v 1.4 2004/06/19 09:50:37 wschweer Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SYNTH_GUI_H__ #define __SYNTH_GUI_H__ #include #include "mpevent.h" const int EVENT_FIFO_SIZE = 4096; class QWidget; class SignalGui : public QObject { Q_OBJECT int writeFd; public: SignalGui(); void create(); void clearSignal(); void sendSignal(); signals: void wakeup(); protected: int readFd; }; //--------------------------------------------------------- // MessGui // manage IO from synti-GUI to Host //--------------------------------------------------------- class MessGui{ // Event Fifo synti -> GUI MusECore::MidiPlayEvent rFifo[EVENT_FIFO_SIZE]; volatile int rFifoSize; int rFifoWindex; int rFifoRindex; // Event Fifo GUI -> synti MusECore::MidiPlayEvent wFifo[EVENT_FIFO_SIZE]; volatile int wFifoSize; int wFifoWindex; int wFifoRindex; SignalGui guiSignal; protected: SignalGui *getGuiSignal() { return &guiSignal;} void readMessage(); void sendEvent(const MusECore::MidiPlayEvent& ev); void sendController(int,int,int); void sendSysex(unsigned char*, int); virtual void processEvent(const MusECore::MidiPlayEvent&) {}; public: MessGui(); virtual ~MessGui(); void writeEvent(const MusECore::MidiPlayEvent&); int fifoSize() const { return wFifoSize; } MusECore::MidiPlayEvent readEvent(); }; //--------------------------------------------------------- // SynthGuiCtrl //--------------------------------------------------------- struct SynthGuiCtrl { enum EditorType { SLIDER, SWITCH, COMBOBOX }; QWidget* editor; QWidget* label; EditorType type; SynthGuiCtrl() {} SynthGuiCtrl(QWidget* w, QWidget* l, const EditorType t) : editor(w), label(l), type(t) {} }; #endif muse-4.0.0/src/synti/libsynti/mess.cpp000066400000000000000000000110671404105311000177230ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mess.cpp,v 1.2 2004/04/15 13:46:18 wschweer Exp $ // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "mess.h" #include "muse/midi_consts.h" #include "midictrl_consts.h" static const int FIFO_SIZE = 32; //--------------------------------------------------------- // MessP //--------------------------------------------------------- struct MessP { // Event Fifo synti -> Host: MusECore::MidiPlayEvent fifo[FIFO_SIZE]; volatile int fifoSize; int fifoWindex; int fifoRindex; }; //--------------------------------------------------------- // Mess //--------------------------------------------------------- Mess::Mess(int n) { _channels = n; _sampleRate = 44100; d = new MessP; d->fifoSize = 0; d->fifoWindex = 0; d->fifoRindex = 0; } //--------------------------------------------------------- // Mess //--------------------------------------------------------- Mess::~Mess() { delete d; } //--------------------------------------------------------- // getNativeGeometry // dummy //--------------------------------------------------------- void Mess::getNativeGeometry(int* x, int* y, int* w, int* h) const { *x = 0; *y = 0; *w = 0; *h = 0; } //--------------------------------------------------------- // sendEvent // send Event synti -> host //--------------------------------------------------------- void Mess::sendEvent(MusECore::MidiPlayEvent ev) { if (d->fifoSize == FIFO_SIZE) { printf("event synti->host fifo overflow\n"); return; } d->fifo[d->fifoWindex] = ev; d->fifoWindex = (d->fifoWindex + 1) % FIFO_SIZE; ++(d->fifoSize); } //--------------------------------------------------------- // receiveEvent // called from host //--------------------------------------------------------- MusECore::MidiPlayEvent Mess::receiveEvent() { MusECore::MidiPlayEvent ev = d->fifo[d->fifoRindex]; d->fifoRindex = (d->fifoRindex + 1) % FIFO_SIZE; --(d->fifoSize); return ev; } //--------------------------------------------------------- // eventsPending // called from host: // while (eventsPending()) { // receiveEvent(); // ... //--------------------------------------------------------- int Mess::eventsPending() const { return d->fifoSize; } //--------------------------------------------------------- // processEvent // return true if synti is busy //--------------------------------------------------------- bool Mess::processEvent(const MusECore::MidiPlayEvent& ev) { switch(ev.type()) { case MusECore::ME_NOTEON: return playNote(ev.channel(), ev.dataA(), ev.dataB()); case MusECore::ME_NOTEOFF: return playNote(ev.channel(), ev.dataA(), 0); case MusECore::ME_SYSEX: return sysex(ev.len(), ev.constData()); case MusECore::ME_CONTROLLER: return setController(ev.channel(), ev.dataA(), ev.dataB()); case MusECore::ME_PITCHBEND: return setController(ev.channel(), MusECore::CTRL_PITCH, ev.dataA()); case MusECore::ME_AFTERTOUCH: return setController(ev.channel(), MusECore::CTRL_AFTERTOUCH, ev.dataA()); // Synths are not allowed to receive ME_PROGRAM, CTRL_HBANK, or CTRL_LBANK alone anymore - only CTRL_PROGRAM. //case MusECore::ME_PROGRAM: // return setController(ev.channel(), MusECore::CTRL_PROGRAM, ev.dataA()); } return false; } muse-4.0.0/src/synti/libsynti/mess.h000066400000000000000000000153501404105311000173670ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mess.h,v 1.3.2.3 2009/11/19 04:20:33 terminator356 Exp $ // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __MESS_H__ #define __MESS_H__ #define MESS_MAJOR_VERSION 1 #define MESS_MINOR_VERSION 1 #include "mpevent.h" struct MessP; //--------------------------------------------------------- // MessConfig // Information to be passed to MESS::instantiate(). // The plugin is free to store these values as it wishes // without relying on any particular libraries. //--------------------------------------------------------- struct MessConfig { unsigned int _segmentSize; int _sampleRate; int _minMeterVal; bool _useDenormalBias; float _denormalBias; bool _leftMouseButtonCanDecrease; const char* _configPath; const char* _cachePath; const char* _globalLibPath; const char* _globalSharePath; const char* _userPath; const char* _projectPath; MessConfig() { _segmentSize = 1024; _sampleRate = 44100; _minMeterVal = 0; _useDenormalBias = false; _denormalBias = 0.0; _leftMouseButtonCanDecrease = false; _configPath = 0; _cachePath = 0; _globalLibPath = 0; _globalSharePath = 0; _userPath = 0; _projectPath = 0; } MessConfig(unsigned int segmentSize, int sampleRate, int minMeterVal, bool useDenormalBias, float denormalBias, bool leftMouseButtonCanDecrease, const char* configPath, const char* cachePath, const char* globalLibPath, const char* globalSharePath, const char* userPath, const char* projectPath) { _segmentSize = segmentSize; _sampleRate = sampleRate; _minMeterVal = minMeterVal; _useDenormalBias = useDenormalBias; _denormalBias = denormalBias; _leftMouseButtonCanDecrease = leftMouseButtonCanDecrease; _configPath = configPath; _cachePath = cachePath; _globalLibPath = globalLibPath; _globalSharePath = globalSharePath; _userPath = userPath; _projectPath = projectPath; } }; //--------------------------------------------------------- // MidiPatch //--------------------------------------------------------- #define MP_TYPE_GM 1 #define MP_TYPE_GS 2 #define MP_TYPE_XG 4 #define MP_TYPE_LBANK 8 #define MP_TYPE_HBANK 16 struct MidiPatch { signed char typ; // 1 - GM 2 - GS 4 - XG signed char hbank, lbank, prog; const char* name; }; //--------------------------------------------------------- // Mess // MusE experimental software synth // Instance virtual interface class // NOTICE: If implementing sysex support, be sure to make a unique ID and use // it to filter out unrecognized sysexes. Headers should be constructed as: // MUSE_SYNTH_SYSEX_MFG_ID The MusE SoftSynth Manufacturer ID byte (0x7C) found in midi.h // 0xNN The synth's unique ID byte //--------------------------------------------------------- class Mess { MessP* d; int _sampleRate; int _channels; // 1 - mono, 2 - stereo public: Mess(int channels); virtual ~Mess(); // This is only a kludge required to support old songs' midistates. Do not use in any new synth. virtual int oldMidiStateHeader(const unsigned char** /*data*/) const { return 0; } int channels() const { return _channels; } int sampleRate() const { return _sampleRate; } void setSampleRate(int r) { _sampleRate = r; } virtual void processMessages() { }; virtual void process(unsigned pos, float** data, int offset, int len) = 0; // the synti has to (re-)implement processEvent() or provide // some of the next three functions: virtual bool processEvent(const MusECore::MidiPlayEvent&); virtual bool setController(int, int, int) { return false; } virtual bool playNote(int, int, int) { return false; } virtual bool sysex(int, const unsigned char*) { return false; } virtual void getInitData(int* n, const unsigned char**) /*const*/ { *n = 0; } // No const: Synths may need to allocate member pointers. virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) const {return 0;} virtual const char* getPatchName(int, int, bool) const { return "?"; } virtual const MidiPatch* getPatchInfo(int, const MidiPatch*) const { return 0; } // Returns true if a note name list is found for the given patch. // If true, name either contains the note name, or is NULL if no note name was found. virtual bool getNoteSampleName(bool /*drum*/, int /*channel*/, int /*patch*/, int /*note*/, const char** /*name*/) const { return false; } // synthesizer -> host communication void sendEvent(MusECore::MidiPlayEvent); // called from synti MusECore::MidiPlayEvent receiveEvent(); // called from host int eventsPending() const; // GUI interface routines virtual bool hasNativeGui() const { return false; } virtual bool nativeGuiVisible() const { return false; } virtual void showNativeGui(bool) {} virtual void getNativeGeometry(int* x, int* y, int* w, int* h) const; virtual void setNativeGeometry(int, int, int, int) {} virtual void guiHeartBeat() {} }; //--------------------------------------------------------- // MESS // Class descriptor //--------------------------------------------------------- struct MESS { const char* name; const char* description; const char* version; int majorMessVersion, minorMessVersion; Mess* (*instantiate)(unsigned long long parentWinId, const char* name, const MessConfig* config); }; extern "C" { const MESS* mess_descriptor(); } typedef const MESS* (*MESS_Descriptor_Function)(); #endif muse-4.0.0/src/synti/libsynti/mono.cpp000066400000000000000000000044341404105311000177240ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // software synthesizer helper library // $Id: mono.cpp,v 1.2 2004/04/15 13:46:18 wschweer Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "mono.h" //--------------------------------------------------------- // playNote //--------------------------------------------------------- bool MessMono::playNote(int channel, int pitch, int velo) { if (velo == 0) { if (pitchStack.empty()) return false; if (pitchStack.back().pitch == pitch) { pitchStack.pop_back(); if (pitchStack.empty()) { note(channel, pitch, 0); return false; } PitchVelo pv = pitchStack.back(); note(pv.channel, pv.pitch, pv.velo); // change pitch return false; } for (std::list::iterator i = pitchStack.begin(); i != pitchStack.end(); ++i) { if ((*i).pitch == pitch) { pitchStack.erase(i); return false; } } // no noteon found // emergency stop: note(channel, pitch, velo); return false; } pitchStack.push_back(PitchVelo(channel, pitch, velo)); note(channel, pitch, velo); return false; } muse-4.0.0/src/synti/libsynti/mono.h000066400000000000000000000036571404105311000173770ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // software synthesizer helper library // $Id: mono.h,v 1.4 2004/04/15 13:46:18 wschweer Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SYNTH_MONO_H__ #define __SYNTH_MONO_H__ #include #include "mess.h" //--------------------------------------------------------- // PitchVelo //--------------------------------------------------------- struct PitchVelo { signed char channel; signed char pitch; signed char velo; PitchVelo(signed char a, signed char b, signed char c) : channel(a), pitch(b), velo(c) {} }; //--------------------------------------------------------- // MessMono // implements some functions for monophone // synthesizer //--------------------------------------------------------- class MessMono : public Mess { std::list pitchStack; protected: virtual bool playNote(int channel, int pitch, int velo); virtual void note(int channel, int pitch, int velo) = 0; public: MessMono() : Mess(1) {} virtual ~MessMono() {} }; #endif muse-4.0.0/src/synti/libsynti/mpevent.cpp.OLD000066400000000000000000000011471404105311000210450ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mpevent.cpp,v 1.1 2004/02/12 18:30:30 wschweer Exp $ // // (C) Copyright 2002-2004 Werner Schweer (ws@seh.de) //========================================================= #include "mpevent.h" //--------------------------------------------------------- // MEvent //--------------------------------------------------------- MEvent::MEvent(unsigned t, int port, int tpe, const unsigned char* data, int len) { _time = t; _port = port; edata.setData(data, len); _type = tpe; } muse-4.0.0/src/synti/libsynti/mpevent.h.OLD000066400000000000000000000071231404105311000205120ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: mpevent.h,v 1.1 2004/02/12 18:30:31 wschweer Exp $ // // (C) Copyright 1999-2002 Werner Schweer (ws@seh.de) //========================================================= #ifndef __MPEVENT_H__ #define __MPEVENT_H__ #include "evdata.h" //--------------------------------------------------------- // MEvent // baseclass for MidiPlayEvent and MidiRecordEvent //--------------------------------------------------------- //--------------------------------------------------------- // MEvent //--------------------------------------------------------- class MEvent { unsigned _time; EvData edata; unsigned char _port, _channel, _type; int _a, _b; public: MEvent() {} MEvent(unsigned tm, int p, int c, int t, int a, int b) : _time(tm), _port(p), _channel(c & 0xf), _type(t), _a(a), _b(b) {} MEvent(unsigned t, int p, int type, const unsigned char* data, int len); MEvent(unsigned t, int p, int tpe, EvData d) : _time(t), edata(d), _port(p), _type(tpe) {} ~MEvent() {} int port() const { return _port; } int channel() const { return _channel; } int type() const { return _type; } int dataA() const { return _a; } int dataB() const { return _b; } unsigned time() const { return _time; } void setPort(int val) { _port = val; } void setChannel(int val) { _channel = val; } void setType(int val) { _type = val; } void setA(int val) { _a = val; } void setB(int val) { _b = val; } void setTime(unsigned val) { _time = val; } const EvData& eventData() const { return edata; } unsigned char* data() const { return edata.data; } int len() const { return edata.dataLen; } void setData(const EvData& e) { edata = e; } void setData(const unsigned char* p, int len) { edata.setData(p, len); } bool isNote() const { return _type == 0x90; } bool isNoteOff() const { return (_type == 0x80)||(_type == 0x90 && _b == 0); } }; //--------------------------------------------------------- // MidiRecordEvent // allocated and deleted in midiseq thread context //--------------------------------------------------------- class MidiPlayEvent; class MidiRecordEvent : public MEvent { public: MidiRecordEvent() {} MidiRecordEvent(const MEvent& e) : MEvent(e) {} MidiRecordEvent(unsigned tm, int p, int c, int t, int a, int b) : MEvent(tm, p, c, t, a, b) {} MidiRecordEvent(unsigned t, int p, int tpe, const unsigned char* data, int len) : MEvent(t, p, tpe, data, len) {} MidiRecordEvent(unsigned t, int p, int type, EvData data) : MEvent(t, p, type, data) {} ~MidiRecordEvent() {} }; //--------------------------------------------------------- // MidiPlayEvent // allocated and deleted in audio thread context //--------------------------------------------------------- class MidiPlayEvent : public MEvent { public: MidiPlayEvent() {} MidiPlayEvent(const MEvent& e) : MEvent(e) {} MidiPlayEvent(unsigned tm, int p, int c, int t, int a, int b) : MEvent(tm, p, c, t, a, b) {} MidiPlayEvent(unsigned t, int p, int type, const unsigned char* data, int len) : MEvent(t, p, type, data, len) {} MidiPlayEvent(unsigned t, int p, int type, EvData data) : MEvent(t, p, type, data) {} ~MidiPlayEvent() {} }; #endif muse-4.0.0/src/synti/libsynti/poly.cpp000066400000000000000000000032641404105311000177370ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // software synthesizer helper library // $Id: poly.cpp,v 1.3 2004/06/01 14:25:50 wschweer Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include "poly.h" #include "midictrl_consts.h" //--------------------------------------------------------- // playNote //--------------------------------------------------------- bool MessPoly::playNote(int /*channel*/, int /*pitch*/, int /*velo*/) { return false; } //--------------------------------------------------------- // setController //--------------------------------------------------------- bool MessPoly::setController(int /*channel*/, int num, int /*val*/) { switch(num) { case MusECore::CTRL_VOLUME: case MusECore::CTRL_EXPRESSION: break; } return false; } muse-4.0.0/src/synti/libsynti/poly.h000066400000000000000000000032021404105311000173740ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // software synthesizer helper library // $Id: poly.h,v 1.2 2004/04/15 13:46:18 wschweer Exp $ // // (C) Copyright 2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SYNTH_POLY_H__ #define __SYNTH_POLY_H__ #include #include "mess.h" //--------------------------------------------------------- // MessPoly // implements some functions for monophone // synthesizer //--------------------------------------------------------- class MessPoly : public Mess { //float volume; //float expression; // cached values: //float mainLevel; protected: virtual bool playNote(int channel, int pitch, int velo); virtual bool setController(int, int, int); public: MessPoly() : Mess(1) {} virtual ~MessPoly() {} }; #endif muse-4.0.0/src/synti/organ/000077500000000000000000000000001404105311000155145ustar00rootroot00000000000000muse-4.0.0/src/synti/organ/CMakeLists.txt000066400000000000000000000037471404105311000202670ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( organ_mocs organgui.h ) ## ## UI files ## file (GLOB organ_ui_files organguibase.ui ) QT5_WRAP_UI ( organ_uis ${organ_ui_files} ) ## ## List of source files to compile ## file (GLOB organ_source_files organ.cpp organgui.cpp ) ## ## Define target ## add_library ( organ SHARED ${organ_source_files} ${organ_mocs} ${organ_uis} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${organ_source_files} ${organ_ui_files} CACHE INTERNAL "" ) # - tell cmake to name target organ.so instead of # liborgan.so # - use precompiled header files # set_target_properties ( organ PROPERTIES PREFIX "" COMPILE_FLAGS "-fvisibility=hidden" ) ## ## Linkage ## target_link_libraries(organ synti ${QT_LIBRARIES} mpevent_module ) ## ## Install location ## install( TARGETS organ DESTINATION ${MusE_SYNTHI_DIR} ) muse-4.0.0/src/synti/organ/common_defs.h000066400000000000000000000023471404105311000201640ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./synti/organ/common_defs.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ORGAN_UNIQUE_ID_H #define __ORGAN_UNIQUE_ID_H // Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. // 127 is reserved for special MusE system messages. #define ORGAN_UNIQUE_ID 1 #endif muse-4.0.0/src/synti/organ/organ.cpp000066400000000000000000000666771404105311000173540ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: organ.cpp,v 1.15.2.8 2009/12/06 10:05:00 terminator356 Exp $ // // Parts of this file taken from: // Organ - Additive Organ Synthesizer Voice // Copyright (c) 1999, 2000 David A. Bartold // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include "muse_math.h" #include "midi_consts.h" #include "mpevent.h" //#include "common_defs.h" #include "organ.h" #include "organgui.h" //#define ORGAN_DEBUG SynthCtrl Organ::synthCtrl[] = { { "harm0", HARM0, 0 }, { "harm1", HARM1, 0 }, { "harm2", HARM2, 0 }, { "harm3", HARM3, 0 }, { "harm4", HARM4, 0 }, { "harm5", HARM5, 0 }, { "attackLo", ATTACK_LO, 20 }, { "decayLo", DECAY_LO, 20 }, { "sustainLo", SUSTAIN_LO, 0 }, { "releaseLo", RELEASE_LO, 20 }, { "attackHi", ATTACK_HI, 10 }, { "decayHi", DECAY_HI, 10 }, { "sustainHi", SUSTAIN_HI, 0 }, { "releaseHi", RELEASE_HI, 10 }, { "brass", BRASS, 1 }, { "flute", FLUTE, 1 }, { "reed", REED, 1 }, { "velocity", VELO, 0 }, // next controller not send as init data { "volume", MusECore::CTRL_VOLUME, 100 }, }; static int NUM_CONTROLLER = sizeof(Organ::synthCtrl)/sizeof(*(Organ::synthCtrl)); static int NUM_INIT_CONTROLLER = NUM_CONTROLLER - 1; float* Organ::sine_table; float* Organ::g_triangle_table; float* Organ::g_pulse_table; int Organ::useCount = 0; double Organ::cb2amp_tab[MAX_ATTENUATION]; unsigned Organ::freq256[128]; //--------------------------------------------------------- // cb2amp // convert centibel to amplification (0 - 96dB) //--------------------------------------------------------- double Organ::cb2amp(int cb) { if (cb < 0) return 1.0; if (cb >= MAX_ATTENUATION) return 0.0; return cb2amp_tab[cb]; } //--------------------------------------------------------- // Organ //--------------------------------------------------------- Organ::Organ(int sr) : Mess(1) { //idata = new int[NUM_CONTROLLER]; idata = new unsigned char[3 + NUM_CONTROLLER * sizeof(int)]; setSampleRate(sr); gui = 0; ++useCount; if (useCount > 1) return; // centibels to amplitude conversion for (int i = 0; i < MAX_ATTENUATION; i++) cb2amp_tab[i] = exp10(double(i) / -200.0); for (int i = 0; i < 128; ++i) { double freq = 8.176 * exp(double(i)*log(2.0)/12.0); freq256[i] = (int) (freq * ((double) RESOLUTION) / sr * 256.0); } int size = RESOLUTION; int half = size / 2; int slope = size / 10; int i; // Initialize sine table. sine_table = new float[size]; for (i = 0; i < size; i++) sine_table[i] = sin ((i * 2.0 * M_PI) / size) / 6.0; // Initialize triangle table. g_triangle_table = new float[size]; for (i = 0; i < half; i++) g_triangle_table[i] = (4.0 / size * i - 1.0) / 6.0; for (; i < size; i++) g_triangle_table[i] = (4.0 / size * (size - i) - 1.0) / 6.0; // Initialize pulse table. g_pulse_table = new float[size]; for (i = 0; i < slope; i++) g_pulse_table[i] = (((double) -i) / slope) / 6.0; for (; i < half - slope; i++) g_pulse_table[i] = -1.0 / 6.0; for (; i < half + slope; i++) g_pulse_table[i] = (((double) i - half) / slope) / 6.0; for (; i < size - slope; i++) g_pulse_table[i] = 1.0 / 6.0; for (; i < size; i++) g_pulse_table[i] = (((double) size - i) / slope) / 6.0; } //--------------------------------------------------------- // ~Organ //--------------------------------------------------------- Organ::~Organ() { if (gui) delete gui; //delete idata; delete [] idata; // p4.0.27 --useCount; if (useCount == 0) { delete[] g_pulse_table; delete[] g_triangle_table; delete[] sine_table; } } //--------------------------------------------------------- // table_pos //--------------------------------------------------------- static inline float table_pos (float* table, unsigned long freq_256, unsigned *accum) { *accum += freq_256; while (*accum >= RESOLUTION * 256) *accum -= RESOLUTION * 256; return table[*accum >> 8]; } //--------------------------------------------------------- // init //--------------------------------------------------------- bool Organ::init(const char* name) { gui = new OrganGui; gui->setWindowTitle(QString(name)); gui->hide(); for (int i = 0; i < NUM_CONTROLLER; ++i) setController(0, synthCtrl[i].num, synthCtrl[i].val); for (int i = 0; i < VOICES; ++i) voices[i].isOn = false; return false; } int Organ::oldMidiStateHeader(const unsigned char** data) const { static unsigned char const d[3] = {MUSE_SYNTH_SYSEX_MFG_ID, ORGAN_UNIQUE_ID, INIT_DATA_CMD}; *data = &d[0]; return 3; } //--------------------------------------------------------- // processMessages // Called from host always, even if output path is unconnected. //--------------------------------------------------------- void Organ::processMessages() { //Process messages from the gui // // get and process all pending events from the // synthesizer GUI // while (gui->fifoSize()) { MusECore::MidiPlayEvent ev = gui->readEvent(); if (ev.type() == MusECore::ME_CONTROLLER) { // process local? setController(ev.dataA(), ev.dataB()); sendEvent(ev); } else { #ifdef ORGAN_DEBUG printf("Organ::process(): unknown event\n"); #endif } } } //--------------------------------------------------------- // process // Called from host, ONLY if output path is connected. //--------------------------------------------------------- void Organ::process(unsigned /*pos*/, float** ports, int offset, int sampleCount) { /* // // get and process all pending events from the // synthesizer GUI // while (gui->fifoSize()) { MusECore::MidiPlayEvent ev = gui->readEvent(); if (ev.type() == MusECore::ME_CONTROLLER) { // process local? setController(ev.dataA(), ev.dataB()); sendEvent(ev); } else printf("Organ::process(): unknown event\n"); } */ float* buffer = *ports + offset; for (int i = 0; i < VOICES; ++i) { Voice* v = &voices[i]; if (!v->isOn) continue; double vol = velo ? v->velocity : 1.0; vol *= volume; unsigned freq_256 = freq256[v->pitch]; unsigned* harm0_accum = &(v->harm0_accum); unsigned* harm1_accum = &(v->harm1_accum); unsigned* harm2_accum = &(v->harm2_accum); unsigned* harm3_accum = &(v->harm3_accum); unsigned* harm4_accum = &(v->harm4_accum); unsigned* harm5_accum = &(v->harm5_accum); unsigned long freq_256_harm2, freq_256_harm3; unsigned long freq_256_harm4, freq_256_harm5; float* reed_table = reed ? g_pulse_table : sine_table; float* flute_table = flute ? g_triangle_table : sine_table; unsigned freq_256_harm0 = freq_256 / 2; unsigned freq_256_harm1 = freq_256; if (brass) { freq_256_harm2 = freq_256 * 2; freq_256_harm3 = freq_256_harm2 * 2; freq_256_harm4 = freq_256_harm3 * 2; freq_256_harm5 = freq_256_harm4 * 2; for (int i = 0; i < sampleCount; i++) { int a1=0, a2=0; //prevent compiler warning: uninitialized usage of vars a1 & a2 switch(v->state1) { case ATTACK: if (v->envL1.step(&a1)) break; v->state1 = DECAY; // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case DECAY: if (v->envL2.step(&a1)) break; v->state1 = SUSTAIN; // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case SUSTAIN: a1 = sustain0; break; case RELEASE: if (v->envL3.step(&a1)) break; v->state1 = OFF; a1 = MAX_ATTENUATION; break; } switch(v->state2) { case ATTACK: if (v->envH1.step(&a2)) break; v->state2 = DECAY; // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case DECAY: if (v->envH2.step(&a2)) break; v->state2 = SUSTAIN; // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case SUSTAIN: a2 = sustain1; break; case RELEASE: if (v->envH3.step(&a2)) break; v->state2 = OFF; a1 = MAX_ATTENUATION; break; } if (v->state1 == OFF && v->state2 == OFF) { v->isOn = false; break; } buffer[i] += (table_pos (sine_table, freq_256_harm0, harm0_accum) * harm0 + table_pos (sine_table, freq_256_harm1, harm1_accum) * harm1 + table_pos (reed_table, freq_256_harm2, harm2_accum) * harm2) * cb2amp(a1) * vol + (table_pos (sine_table, freq_256_harm3, harm3_accum) * harm3 + table_pos (flute_table, freq_256_harm4, harm4_accum) * harm4 + table_pos (flute_table, freq_256_harm5, harm5_accum) * harm5) * cb2amp(a2) * vol; } } else { freq_256_harm2 = freq_256 * 3 / 2; freq_256_harm3 = freq_256 * 2; freq_256_harm4 = freq_256 * 3; freq_256_harm5 = freq_256_harm3 * 2; for (int i = 0; i < sampleCount; i++) { int a1=0, a2=0;//prevent compiler warning: uninitialized usage of vars a1 & a2 switch(v->state1) { case ATTACK: if (v->envL1.step(&a1)) break; v->state1 = DECAY; // NOTE: Error suppressor for new gcc 7 'fallthrough' level 3 and 4: // FALLTHROUGH case DECAY: if (v->envL2.step(&a1)) break; v->state1 = SUSTAIN; // FALLTHROUGH case SUSTAIN: a1 = sustain0; break; case RELEASE: if (v->envL3.step(&a1)) break; v->state1 = OFF; a1 = MAX_ATTENUATION; break; } switch(v->state2) { case ATTACK: if (v->envH1.step(&a2)) break; v->state2 = DECAY; // FALLTHROUGH case DECAY: if (v->envH2.step(&a2)) break; v->state2 = SUSTAIN; // FALLTHROUGH case SUSTAIN: a2 = sustain1; break; case RELEASE: if (v->envH3.step(&a2)) break; v->state2 = OFF; a1 = MAX_ATTENUATION; break; } if (v->state1 == OFF && v->state2 == OFF) { v->isOn = false; break; } buffer[i] += (table_pos (sine_table, freq_256_harm0, harm0_accum) * harm0 + table_pos (sine_table, freq_256_harm1, harm1_accum) * harm1 + table_pos (sine_table, freq_256_harm2, harm2_accum) * harm2) * cb2amp(a1) * vol + (table_pos (reed_table, freq_256_harm3, harm3_accum) * harm3 + table_pos (sine_table, freq_256_harm4, harm4_accum) * harm4 + table_pos (flute_table, freq_256_harm5, harm5_accum) * harm5) * cb2amp(a2) * vol; } } } } //--------------------------------------------------------- // playNote //--------------------------------------------------------- bool Organ::playNote(int channel, int pitch, int velo) { if (velo == 0) { noteoff(channel, pitch); return false; } for (int i = 0; i < VOICES; ++i) { if (voices[i].isOn) continue; voices[i].isOn = true; voices[i].pitch = pitch; voices[i].channel = channel; // velo is never 0 voices[i].velocity = cb2amp(int(200 * log10((127.0 * 127)/(velo*velo)))); voices[i].state1 = ATTACK; voices[i].state2 = ATTACK; voices[i].envL1.set(attack0, MAX_ATTENUATION, 0); voices[i].envL2.set(decay0, MAX_ATTENUATION, sustain0); voices[i].envL3.set(release0, sustain0, MAX_ATTENUATION); voices[i].envH1.set(attack1, MAX_ATTENUATION, 0); voices[i].envH2.set(decay1, MAX_ATTENUATION, sustain1); voices[i].envH3.set(release1, sustain1, MAX_ATTENUATION); voices[i].harm0_accum = 0; voices[i].harm1_accum = 0; voices[i].harm2_accum = 0; voices[i].harm3_accum = 0; voices[i].harm4_accum = 0; voices[i].harm5_accum = 0; return false; } #ifdef ORGAN_DEBUG printf("organ: voices overflow!\n"); #endif return false; } //--------------------------------------------------------- // noteoff //--------------------------------------------------------- void Organ::noteoff(int channel, int pitch) { bool found = false; for (int i = 0; i < VOICES; ++i) { if (voices[i].isOn && (voices[i].pitch == pitch) && (voices[i].channel == channel)) { found = true; voices[i].state1 = RELEASE; voices[i].state2 = RELEASE; } } if (!found) { #ifdef ORGAN_DEBUG printf("Organ: noteoff %d:%d not found\n", channel, pitch); #endif } } //--------------------------------------------------------- // setController //--------------------------------------------------------- void Organ::setController(int ctrl, int data) { int sr = sampleRate(); // Changed By T356. // Because of muse's auto-bias controllers, some of these negative-range // controls need to apply the auto-bias correction. switch (ctrl) { case HARM0: //harm0 = cb2amp(-data); harm0 = cb2amp(-data + 8192); break; case HARM1: //harm1 = cb2amp(-data); harm1 = cb2amp(-data + 8192); break; case HARM2: //harm2 = cb2amp(-data); harm2 = cb2amp(-data + 8192); break; case HARM3: //harm3 = cb2amp(-data); harm3 = cb2amp(-data + 8192); break; case HARM4: //harm4 = cb2amp(-data); harm4 = cb2amp(-data + 8192); break; case HARM5: //harm5 = cb2amp(-data); harm5 = cb2amp(-data + 8192); break; case ATTACK_LO: // maxval -> 500msec attack0 = (data * sr) / 1000; break; case DECAY_LO: // maxval -> 5000msec decay0 = (data * sr) / 1000; break; case SUSTAIN_LO: //sustain0 = -data; sustain0 = -data + 8192; break; case RELEASE_LO: release0 = (data * sr) / 1000; break; case ATTACK_HI: attack1 = (data * sr) / 1000; break; case DECAY_HI: decay1 = (data * sr) / 1000; break; case SUSTAIN_HI: //sustain1 = -data; sustain1 = -data + 8192; break; case RELEASE_HI: release1 = (data * sr) / 1000; break; case BRASS: brass = data; break; case FLUTE: flute = data; break; case REED: reed = data; break; case VELO: velo = data; break; case MusECore::CTRL_VOLUME: data &= 0x7f; volume = data == 0 ? 0.0 : cb2amp(int(200 * log10((127.0 * 127)/(data*data)))); break; case MusECore::CTRL_ALL_SOUNDS_OFF: for (int i = 0; i < VOICES; ++i) voices[i].isOn = false; break; case MusECore::CTRL_RESET_ALL_CTRL: for (int i = 0; i < NUM_CONTROLLER; ++i) setController(0, synthCtrl[i].num, synthCtrl[i].val); break; default: #ifdef ORGAN_DEBUG fprintf(stderr, "Organ:set unknown Ctrl 0x%x to 0x%x\n", ctrl, data); #endif return; } for (int i = 0; i < NUM_CONTROLLER; ++i) { if (synthCtrl[i].num == ctrl) { synthCtrl[i].val = data; break; } } } //--------------------------------------------------------- // setController //--------------------------------------------------------- bool Organ::setController(int channel, int ctrl, int data) { setController(ctrl, data); switch (ctrl) { case HARM0: case HARM1: case HARM2: case HARM3: case HARM4: case HARM5: case ATTACK_LO: case DECAY_LO: case SUSTAIN_LO: case RELEASE_LO: case ATTACK_HI: case DECAY_HI: case SUSTAIN_HI: case RELEASE_HI: case BRASS: case FLUTE: case REED: case VELO: { MusECore::MidiPlayEvent ev(0, 0, channel, MusECore::ME_CONTROLLER, ctrl, data); #ifdef ORGAN_DEBUG fprintf(stderr, "OrganGui:setController before gui->writeEvent ctrl:%d data:%d\n", ctrl, data); #endif gui->writeEvent(ev); } break; default: break; } return false; } //--------------------------------------------------------- // sysex //--------------------------------------------------------- bool Organ::sysex(int n, const unsigned char* data) { #ifdef ORGAN_DEBUG printf("Organ: sysex\n"); #endif // p4.0.27 if(unsigned(n) == (3 + NUM_INIT_CONTROLLER * sizeof(int))) { if (data[0] == MUSE_SYNTH_SYSEX_MFG_ID) // MusE Soft Synth { if (data[1] == ORGAN_UNIQUE_ID) // ORGAN { if (data[2] == INIT_DATA_CMD) // Initialization { int* s = (int*)(data + 3); for (int i = 0; i < NUM_INIT_CONTROLLER; ++i) { int val = *s++; #ifdef ORGAN_DEBUG printf("Organ: sysex before setController num:%d val:%d\n", synthCtrl[i].num, val); #endif setController(0, synthCtrl[i].num, val); } return false; } } } } #ifdef ORGAN_DEBUG printf("Organ: unknown sysex\n"); #endif return false; } //--------------------------------------------------------- // getInitData //--------------------------------------------------------- //void Organ::getInitData(int* n, const unsigned char**p) const void Organ::getInitData(int* n, const unsigned char**p) { // p4.0.27 *n = 3 + NUM_INIT_CONTROLLER * sizeof(int); idata[0] = MUSE_SYNTH_SYSEX_MFG_ID; // MusE Soft Synth idata[1] = ORGAN_UNIQUE_ID; // ORGAN idata[2] = INIT_DATA_CMD; // Initialization int* d = (int*)&idata[3]; //int* d = idata; for (int i = 0; i < NUM_INIT_CONTROLLER; ++i) *d++ = synthCtrl[i].val; //*n = NUM_INIT_CONTROLLER * sizeof(int); // sizeof(idata); *p = (unsigned char*)idata; } //--------------------------------------------------------- // MESS //--------------------------------------------------------- //--------------------------------------------------------- // getControllerInfo //--------------------------------------------------------- int Organ::getControllerInfo(int id, const char** name, int* controller, int* min, int* max, int* initval) const { if (id >= NUM_CONTROLLER) return 0; *controller = synthCtrl[id].num; *name = synthCtrl[id].name; *initval = synthCtrl[id].val; if(synthCtrl[id].num == MusECore::CTRL_VOLUME) { *min = 0; *max = 127; } else gui->getControllerMinMax(id,min,max); //*min = 0; //*max = 128*128-1; return ++id; } //--------------------------------------------------------- // nativeGuiVisible //--------------------------------------------------------- bool Organ::nativeGuiVisible() const { return gui->isVisible(); } //--------------------------------------------------------- // showNativeGui //--------------------------------------------------------- void Organ::showNativeGui(bool val) { gui->setVisible(val); } //--------------------------------------------------------- // getNativeGeometry //--------------------------------------------------------- void Organ::getNativeGeometry(int* x, int* y, int* w, int* h) const { QPoint pos(gui->pos()); QSize size(gui->size()); *x = pos.x(); *y = pos.y(); *w = size.width(); *h = size.height(); } //--------------------------------------------------------- // setNativeGeometry //--------------------------------------------------------- void Organ::setNativeGeometry(int x, int y, int w, int h) { gui->resize(QSize(w, h)); gui->move(QPoint(x, y)); } //--------------------------------------------------------- // instantiate // construct a new synthesizer instance //--------------------------------------------------------- static Mess* instantiate(unsigned long long /*parentWinId*/, const char* name, const MessConfig* config) { Organ* synth = new Organ(config->_sampleRate); if (synth->init(name)) { delete synth; synth = 0; } return synth; } //--------------------------------------------------------- // msynth_descriptor // Return a descriptor of the requested plugin type. //--------------------------------------------------------- extern "C" { static MESS descriptor = { "Organ", "Organ based on David A. Bartold's LADSPA plugin", "0.1", // version string MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate, }; // We must compile with -fvisibility=hidden to avoid namespace // conflicts with global variables. // Only visible symbol is "mess_descriptor". // (TODO: all plugins should be compiled this way) __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } muse-4.0.0/src/synti/organ/organ.h000066400000000000000000000151661404105311000170040ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: organ.h,v 1.5.2.3 2009/11/19 04:20:33 terminator356 Exp $ // // Parts of this file taken from: // Organ - Additive Organ Synthesizer Voice // Copyright (c) 1999, 2000 David A. Bartold // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ORGAN_H__ #define __ORGAN_H__ #include "midictrl_consts.h" #include "libsynti/mess.h" #include "common_defs.h" #define RESOLUTION (16384*2) #define VOICES 128 // max polyphony #define INIT_DATA_CMD 1 class OrganGui; static const int MAX_ATTENUATION = 960; enum EnvelopeState { ATTACK, DECAY, SUSTAIN, RELEASE, OFF }; //--------------------------------------------------------- // Envelope //--------------------------------------------------------- struct Envelope { int ticks; // len of segment int error, delta, schritt; int y, yinc; void set(int t, int y1, int y2) { ticks = t; y = y1; int dy = y2 - y1; int dx = t; error = -dx; schritt = 2*dx; if (dy < 0) { yinc = -1; delta = -2 * dy; } else { yinc = 1; delta = 2 * dy; } } // return false on envelope end bool step(int* a) { *a = y; if (ticks == 0) return false; error += delta; while (error > 0) { y += yinc; error -= schritt; } --ticks; return true; } }; static const int HARM0 = 0 + MusECore::CTRL_RPN14_OFFSET; static const int HARM1 = 1 + MusECore::CTRL_RPN14_OFFSET; static const int HARM2 = 2 + MusECore::CTRL_RPN14_OFFSET; static const int HARM3 = 3 + MusECore::CTRL_RPN14_OFFSET; static const int HARM4 = 4 + MusECore::CTRL_RPN14_OFFSET; static const int HARM5 = 5 + MusECore::CTRL_RPN14_OFFSET; static const int ATTACK_LO = 6 + MusECore::CTRL_RPN14_OFFSET; static const int DECAY_LO = 7 + MusECore::CTRL_RPN14_OFFSET; static const int SUSTAIN_LO = 8 + MusECore::CTRL_RPN14_OFFSET; static const int RELEASE_LO = 9 + MusECore::CTRL_RPN14_OFFSET; static const int ATTACK_HI = 10 + MusECore::CTRL_RPN14_OFFSET; static const int DECAY_HI = 11 + MusECore::CTRL_RPN14_OFFSET; static const int SUSTAIN_HI = 12 + MusECore::CTRL_RPN14_OFFSET; static const int RELEASE_HI = 13 + MusECore::CTRL_RPN14_OFFSET; static const int BRASS = 14 + MusECore::CTRL_RPN14_OFFSET; static const int FLUTE = 15 + MusECore::CTRL_RPN14_OFFSET; static const int REED = 16 + MusECore::CTRL_RPN14_OFFSET; static const int VELO = 17 + MusECore::CTRL_RPN14_OFFSET; //--------------------------------------------------------- // SynthCtrl //--------------------------------------------------------- struct SynthCtrl { const char* name; int num; int val; }; //--------------------------------------------------------- // Voice //--------------------------------------------------------- struct Voice { bool isOn; int pitch; int channel; double velocity; int state1, state2; Envelope envL1, envL2, envL3; Envelope envH1, envH2, envH3; unsigned harm0_accum; unsigned harm1_accum; unsigned harm2_accum; unsigned harm3_accum; unsigned harm4_accum; unsigned harm5_accum; }; //--------------------------------------------------------- // Preset //--------------------------------------------------------- struct Preset { char* name; bool brass, flute, reed; int attack0, attack1; int release0, release1; int decay0, decay1; double harm0, harm1, harm2, harm3, harm4, harm5; bool velo; }; //--------------------------------------------------------- // Organ //--------------------------------------------------------- class Organ : public Mess { static int useCount; static double cb2amp_tab[MAX_ATTENUATION]; static unsigned freq256[128]; static double cb2amp(int cb); //int* idata; // buffer for init data unsigned char* idata; // buffer for init data bool brass, flute, reed; int attack0, attack1; int release0, release1; int decay0, decay1; // ticks int sustain0, sustain1; // centibel bool velo; double volume; double harm0, harm1, harm2, harm3, harm4, harm5; Voice voices[VOICES]; static float* sine_table; static float* g_triangle_table; static float* g_pulse_table; void noteoff(int channel, int pitch); void setController(int ctrl, int val); OrganGui* gui; public: virtual void processMessages(); virtual void process(unsigned pos, float**, int, int); virtual bool playNote(int channel, int pitch, int velo); virtual bool setController(int channel, int ctrl, int val); virtual int getControllerInfo(int, const char**, int*, int*, int*, int*) const; virtual void getInitData(int*, const unsigned char**); // This is only a kludge required to support old songs' midistates. Do not use in any new synth. virtual int oldMidiStateHeader(const unsigned char** data) const; virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool); virtual bool hasNativeGui() const { return true; } virtual void getNativeGeometry(int* x, int* y, int* w, int* h) const; virtual void setNativeGeometry(int x, int y, int w, int h); virtual bool sysex(int, const unsigned char*); static SynthCtrl synthCtrl[]; Organ(int sampleRate); virtual ~Organ(); bool init(const char* name); }; #endif muse-4.0.0/src/synti/organ/organgui.cpp000066400000000000000000000157021404105311000200400ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: organgui.cpp,v 1.16.2.3 2009/11/16 04:30:46 terminator356 Exp $ // // This is a simple GUI implemented with QT for // organ software synthesizer. // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // // 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; 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; 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 #include "common_defs.h" #include "organgui.h" #include "muse/midi_consts.h" #include "midictrl_consts.h" //#define ORGANGUI_DEBUG //--------------------------------------------------------- // OrganGui //--------------------------------------------------------- OrganGui::OrganGui() : QWidget(0, Qt::Window), MessGui() { setupUi(this); // p4.0.17 connect(this->getGuiSignal(),SIGNAL(wakeup()),this,SLOT(readMessage())); dctrl[0] = SynthGuiCtrl(p1, lcd1, SynthGuiCtrl::SLIDER); dctrl[1] = SynthGuiCtrl(p2, lcd2, SynthGuiCtrl::SLIDER); dctrl[2] = SynthGuiCtrl(p3, lcd3, SynthGuiCtrl::SLIDER); dctrl[3] = SynthGuiCtrl(p4, lcd4, SynthGuiCtrl::SLIDER); dctrl[4] = SynthGuiCtrl(p5, lcd5, SynthGuiCtrl::SLIDER); dctrl[5] = SynthGuiCtrl(p6, lcd6, SynthGuiCtrl::SLIDER); dctrl[6] = SynthGuiCtrl(p7, lcd7, SynthGuiCtrl::SLIDER); dctrl[7] = SynthGuiCtrl(p8, lcd8, SynthGuiCtrl::SLIDER); dctrl[8] = SynthGuiCtrl(p9, lcd9, SynthGuiCtrl::SLIDER); dctrl[9] = SynthGuiCtrl(p10, lcd10, SynthGuiCtrl::SLIDER); dctrl[10] = SynthGuiCtrl(p11, lcd11, SynthGuiCtrl::SLIDER); dctrl[11] = SynthGuiCtrl(p12, lcd12, SynthGuiCtrl::SLIDER); dctrl[12] = SynthGuiCtrl(p13, lcd13, SynthGuiCtrl::SLIDER); dctrl[13] = SynthGuiCtrl(p14, lcd14, SynthGuiCtrl::SLIDER); dctrl[14] = SynthGuiCtrl(sw1, 0, SynthGuiCtrl::SWITCH); dctrl[15] = SynthGuiCtrl(sw3, 0, SynthGuiCtrl::SWITCH); dctrl[16] = SynthGuiCtrl(sw2, 0, SynthGuiCtrl::SWITCH); dctrl[17] = SynthGuiCtrl(sw4, 0, SynthGuiCtrl::SWITCH); for (int i = 0; i < NUM_GUI_CONTROLLER; ++i) { if (dctrl[i].type == SynthGuiCtrl::SLIDER) connect((QSlider*)(dctrl[i].editor), &QSlider::valueChanged, [this, i]() { ctrlChanged(i); } ); else if (dctrl[i].type == SynthGuiCtrl::SWITCH) connect((QCheckBox*)(dctrl[i].editor), &QCheckBox::toggled, [this, i]() { ctrlChanged(i); } ); } } //--------------------------------------------------------- // ctrlChanged //--------------------------------------------------------- void OrganGui::ctrlChanged(int idx) { SynthGuiCtrl* ctrl = &dctrl[idx]; int val = 0; if (ctrl->type == SynthGuiCtrl::SLIDER) { QSlider* slider = (QSlider*)(ctrl->editor); val = slider->value(); // By T356. Apply auto-bias center value. if(slider->minimum() < 0) val += 8192; } else if (ctrl->type == SynthGuiCtrl::SWITCH) { val = ((QCheckBox*)(ctrl->editor))->isChecked(); } sendController(0, idx + MusECore::CTRL_RPN14_OFFSET, val); } //--------------------------------------------------------- // getControllerInfo // return min max values for controllers //--------------------------------------------------------- int OrganGui::getControllerMinMax(int id, int* min, int* max) const { if (id >= NUM_GUI_CONTROLLER) return 0; const SynthGuiCtrl* ctrl = (const SynthGuiCtrl*)&dctrl[id]; //int val = 0; if (ctrl->type == SynthGuiCtrl::SLIDER) { QSlider* slider = (QSlider*)(ctrl->editor); *max = slider->maximum(); *min = slider->minimum(); //val = (slider->value() * 16383 + max/2) / max; //val = 16383 + 1/2 } else if (ctrl->type == SynthGuiCtrl::SWITCH) { //val = ((QCheckBox*)(ctrl->editor))->isOn(); *min=0; *max=1; } return ++id; } //--------------------------------------------------------- // setParam // set param in gui //--------------------------------------------------------- void OrganGui::setParam(int param, int val) { #ifdef ORGANGUI_DEBUG fprintf(stderr, "OrganGui:setParam param:%d val:%d\n", param, val); #endif param &= 0xfff; if (param >= int(sizeof(dctrl)/sizeof(*dctrl))) { #ifdef ORGANGUI_DEBUG fprintf(stderr, "OrganGui: set unknown Ctrl 0x%x to 0x%x\n", param, val); #endif return; } SynthGuiCtrl* ctrl = &dctrl[param]; ctrl->editor->blockSignals(true); if (ctrl->type == SynthGuiCtrl::SLIDER) { QSlider* slider = (QSlider*)(ctrl->editor); // int max = slider->maximum(); // if(val < 0) val = (val * max + 8191) / 16383 - 1; // else val = (val * max + 8191) / 16383; // By T356. Apply auto-bias center value. if(slider->minimum() < 0) val -= 8192; #ifdef ORGANGUI_DEBUG fprintf(stderr, "OrganGui:setParam setting slider val:%d\n", val); #endif slider->setValue(val); if (ctrl->label) ((QSpinBox*)(ctrl->label))->setValue(val); } else if (ctrl->type == SynthGuiCtrl::SWITCH) { ((QCheckBox*)(ctrl->editor))->setChecked(val); } ctrl->editor->blockSignals(false); } //--------------------------------------------------------- // processEvent //--------------------------------------------------------- void OrganGui::processEvent(const MusECore::MidiPlayEvent& ev) { if (ev.type() == MusECore::ME_CONTROLLER) setParam(ev.dataA(), ev.dataB()); else { #ifdef ORGANGUI_DEBUG printf("OrganGui::illegal event type received\n"); #endif } } //--------------------------------------------------------- // readMessage //--------------------------------------------------------- void OrganGui::readMessage() { MessGui::readMessage(); } muse-4.0.0/src/synti/organ/organgui.h000066400000000000000000000033761404105311000175110ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: organgui.h,v 1.6.2.1 2005/12/29 23:33:50 spamatica Exp $ // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __ORGANGUI_H__ #define __ORGANGUI_H__ #include "ui_organguibase.h" #include "organ.h" #include "libsynti/gui.h" //#include "libsynti/mpevent.h" #include "mpevent.h" class QWidget; #define NUM_GUI_CONTROLLER 18 //--------------------------------------------------------- // OrganGui //--------------------------------------------------------- class OrganGui : public QWidget, public Ui::OrganGuiBase, public MessGui { Q_OBJECT SynthGuiCtrl dctrl[NUM_GUI_CONTROLLER]; void setParam(int, int); private slots: void ctrlChanged(int idx); void readMessage(); public: virtual void processEvent(const MusECore::MidiPlayEvent&); int getControllerMinMax(int id, int* min, int* max) const; OrganGui(); }; #endif muse-4.0.0/src/synti/organ/organguibase.ui000066400000000000000000001042771404105311000205340ustar00rootroot00000000000000 OrganGuiBase 0 0 534 366 0 0 MusE: Organ 4 6 0 0 Drawbars 6 2 16' false 4' false 2 2/3' false 2' false 5 1/3' false 8' false -960 0 1 Qt::Horizontal QSlider::NoTicks 10 -960 0 1 Qt::Horizontal QSlider::NoTicks 10 -960 0 1 Qt::Horizontal QSlider::NoTicks 10 -960 0 1 Qt::Horizontal QSlider::NoTicks 10 -960 0 1 Qt::Horizontal QSlider::NoTicks 10 0 0 -960 0 1 Qt::Horizontal QSlider::NoTicks 10 -960 0 -960 0 10 -960 0 10 -960 0 10 -960 0 10 -960 0 10 0 0 Envelope Hi 11 2 Release false Sustain false Decay false 0 0 Attack false 500 1 Qt::Horizontal QSlider::NoTicks 50 -960 0 1 Qt::Horizontal QSlider::NoTicks 10 5000 1 Qt::Horizontal QSlider::NoTicks 500 0 0 ms 500 0 0 500 1 Qt::Horizontal QSlider::NoTicks 50 0 0 ms 5000 0 0 cB -960 0 0 0 ms 500 0 0 Envelope Lo Qt::AlignLeading 11 2 Attack false Decay false Sustain false Release false 0 0 80 0 500 1 Qt::Horizontal QSlider::NoTicks 50 5000 1 Qt::Horizontal QSlider::NoTicks 500 -960 0 1 Qt::Horizontal QSlider::NoTicks 10 500 1 Qt::Horizontal QSlider::NoTicks 50 0 0 ms 500 0 0 ms 5000 0 0 cB -960 0 0 0 0 ms 500 0 0 48 O-1 Qt::AlignCenter false 0 0 Oscillator 2 11 0 0 Brass 0 0 Reed 0 0 Flute Qt::Vertical QSizePolicy::Expanding 20 20 Velocity p1 p3 p2 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 sw1 sw2 sw3 p11 valueChanged(int) lcd11 setValue(int) 20 20 20 20 p12 valueChanged(int) lcd12 setValue(int) 20 20 20 20 p13 valueChanged(int) lcd13 setValue(int) 20 20 20 20 p14 valueChanged(int) lcd14 setValue(int) 20 20 20 20 p7 valueChanged(int) lcd7 setValue(int) 20 20 20 20 p8 valueChanged(int) lcd8 setValue(int) 20 20 20 20 p9 valueChanged(int) lcd9 setValue(int) 20 20 20 20 p10 valueChanged(int) lcd10 setValue(int) 20 20 20 20 p1 valueChanged(int) lcd1 setValue(int) 20 20 20 20 p2 valueChanged(int) lcd2 setValue(int) 20 20 20 20 p3 valueChanged(int) lcd3 setValue(int) 20 20 20 20 p4 valueChanged(int) lcd4 setValue(int) 20 20 20 20 p5 valueChanged(int) lcd5 setValue(int) 20 20 20 20 p6 valueChanged(int) lcd6 setValue(int) 20 20 20 20 lcd1 valueChanged(int) p1 setValue(int) 20 20 20 20 lcd2 valueChanged(int) p2 setValue(int) 20 20 20 20 lcd3 valueChanged(int) p3 setValue(int) 20 20 20 20 lcd4 valueChanged(int) p4 setValue(int) 20 20 20 20 lcd5 valueChanged(int) p5 setValue(int) 20 20 20 20 lcd6 valueChanged(int) p6 setValue(int) 20 20 20 20 lcd7 valueChanged(int) p7 setValue(int) 20 20 20 20 lcd8 valueChanged(int) p8 setValue(int) 20 20 20 20 lcd9 valueChanged(int) p9 setValue(int) 20 20 20 20 lcd10 valueChanged(int) p10 setValue(int) 20 20 20 20 lcd11 valueChanged(int) p11 setValue(int) 20 20 20 20 lcd12 valueChanged(int) p12 setValue(int) 20 20 20 20 lcd13 valueChanged(int) p13 setValue(int) 20 20 20 20 lcd14 valueChanged(int) p14 setValue(int) 20 20 20 20 muse-4.0.0/src/synti/s1/000077500000000000000000000000001404105311000147315ustar00rootroot00000000000000muse-4.0.0/src/synti/s1/CMakeLists.txt000066400000000000000000000032261404105311000174740ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## List of source files to compile ## file (GLOB s1_source_files s1.cpp ) ## ## Define target ## add_library ( s1 SHARED ${s1_source_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${s1_source_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## # tell cmake to name target s1.so instead of # libs1.so # set_target_properties ( s1 PROPERTIES PREFIX "" COMPILE_FLAGS "-fvisibility=hidden" ) ## ## Linkage ## target_link_libraries(s1 synti ${QT_LIBRARIES} ) ## ## Install location ## install( TARGETS s1 DESTINATION ${MusE_SYNTHI_DIR} ) muse-4.0.0/src/synti/s1/s1.cpp000066400000000000000000000162271404105311000157700ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: s1.cpp,v 1.9.2.5 2009/11/19 04:20:33 terminator356 Exp $ // // S1 - simple mono demo synthesizer // - plays only one note at a time // - has no gui nor any controller // // Version 0.2: stop note on wave zero crossing to avoid // clicks // // (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #include #include #include "muse_math.h" #include "libsynti/mono.h" #define RESOLUTION 16384 // Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. // 127 is reserved for special MusE system messages. #define S1_UNIQUE_ID 6 //--------------------------------------------------------- // S1 - simple mono demo synthesizer //--------------------------------------------------------- class S1 : public MessMono { static int useCount; static float *wave_table; int gate; float freq; unsigned accu; float sample; bool _showGui; int param; virtual void note(int channel, int pitch, int velo); //virtual void processMessages(); virtual void process(unsigned pos, float** buffer, int offset, int n); virtual bool hasNativeGui() const { return true; } virtual bool nativeGuiVisible() const { return _showGui; } virtual void showNativeGui(bool); virtual bool setController(int channel, int ctrl, int val); virtual int getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval) const; public: S1(); virtual ~S1(); }; float* S1::wave_table; int S1::useCount = 0; //--------------------------------------------------------- // S1 //--------------------------------------------------------- S1::S1() : MessMono() { if (useCount++ == 0) { // // create sinus wave table // wave_table = new float[RESOLUTION]; for (int i = 0; i < RESOLUTION; i++) wave_table[i] = sin ((i * 2.0 * M_PI) / RESOLUTION) / 6.0; } gate = 0; param = 0; _showGui=false; showNativeGui(true); } //--------------------------------------------------------- // ~S1 //--------------------------------------------------------- S1::~S1() { if (--useCount == 0) delete[] wave_table; } //--------------------------------------------------------- // noteon // process note on //--------------------------------------------------------- void S1::note(int /*channel*/, int pitch, int velo) { if (velo == 0) { // // note off // if (sample == 0.0) gate = 0; else if (sample > 0.0) gate = 2; else if (sample < 0.0) gate = 3; } else { // // note on // accu = 0; gate = 1; freq = 8.176 * exp(float(pitch)*log(2.0)/12.0); } } //--------------------------------------------------------- // write // synthesize n samples into buffer+offset //--------------------------------------------------------- void S1::process(unsigned /*pos*/, float** buffer, int offset, int n) { if (gate == 0) return; float* p = buffer[0] + offset; float sample1, sample2; unsigned freq_256 = (int) (freq * ((double) RESOLUTION) / sampleRate() * 256.0); for (int i = 0; i < n; i++) { accu += freq_256; while (accu >= RESOLUTION * 256) accu -= RESOLUTION * 256; sample1 = wave_table[accu >> 8]; // sinus component if (sample1< 0.0f) // square wave component sample2 = -0.4; else sample2 = 0.4; sample = ((1.0-float(param)/127.0)*sample1 + (float(param)/127.0)*sample2) / 2.0; // // stop on zero crossing // if in decay state // if (gate == 2 && sample <= 0.0) { gate = 0; break; } else if (gate == 3 && sample >= 0.0) { gate = 0; break; } p[i] += sample; } } //--------------------------------------------------------- // inst //--------------------------------------------------------- void S1::showNativeGui(bool show) { if (show) QMessageBox::information( NULL, "S1", "S1 is a demo synth mainly for\n" "developers wishing to learn\n" "how to make a M.E.S.S synth.\n" "\n" "One modulation parameter is available,\n" "it sweeps the signal between square and\n" "sinus wave.\n", 1 ); } bool S1::setController(int, int ctrl, int val) { if (ctrl == 1) { param = val; } return true; } int S1::getControllerInfo(int id, const char** name, int* ctrl, int* min, int* max, int* initval) const { if (id == 0) { *name = "Modulation"; *ctrl = 1; *min = 0; *max = 127; *initval = 0; return 1; } else return 0; } //--------------------------------------------------------- // inst //--------------------------------------------------------- class QWidget; static Mess* instantiate(unsigned long long /*parentWinId*/, const char* /*name*/, const MessConfig* config) { S1* s1 = new S1(); s1->setSampleRate(config->_sampleRate); return s1; } extern "C" { static MESS descriptor = { "S1", "S1 MusE Demo Software Synthesizer", "0.2", // version string MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate }; // We must compile with -fvisibility=hidden to avoid namespace // conflicts with global variables. // Only visible symbol is "mess_descriptor". // (TODO: all plugins should be compiled this way) __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } muse-4.0.0/src/synti/simpledrums2/000077500000000000000000000000001404105311000170345ustar00rootroot00000000000000muse-4.0.0/src/synti/simpledrums2/CMakeLists.txt000066400000000000000000000052761404105311000216060ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( simpledrums_mocs simpledrumsgui.h ssplugingui.h ) ## ## UI files ## file (GLOB simpledrums_ui_files simpledrumsguibase.ui ) QT5_WRAP_UI ( simpledrums_uis ${simpledrums_ui_files} ) ## ## List of source files to compile ## file (GLOB simpledrums_source_files simpledrums.cpp simpledrumsgui.cpp ssplugingui.cpp ) ## ## Define target ## add_library ( simpledrums SHARED ${simpledrums_source_files} ${simpledrums_mocs} ${simpledrums_uis} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${simpledrums_source_files} ${simpledrums_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## # - tell cmake to name target simpledrums.so instead of # libsimpledrums.so # - use precompiled header files # # Don't override the default optimization. Can be reconsidered # according to Mathias' feedback. - Orcan 2012-05-20 #if ( x${CMAKE_BUILD_TYPE} STREQUAL xrelease ) # SET(SIMPLEDRUMS_OPTFLAGS "-O6") #endif ( x${CMAKE_BUILD_TYPE} STREQUAL xrelease ) set_target_properties ( simpledrums PROPERTIES PREFIX "" COMPILE_FLAGS "-fvisibility=hidden" ) ## ## Linkage ## target_link_libraries(simpledrums synti ${QT_LIBRARIES} ${SNDFILE_LIBRARIES} ${SAMPLERATE_LIBRARIES} simpler_plugin simpler_plugingui mpevent_module wavepreview_module widgets dl pthread ) ## ## Install location ## install( TARGETS simpledrums DESTINATION ${MusE_SYNTHI_DIR} ) install( FILES COPYING README ReleaseNotes.txt DESTINATION ${MusE_DOC_DIR}/simpledrums ) muse-4.0.0/src/synti/simpledrums2/COPYING000066400000000000000000000001311404105311000200620ustar00rootroot00000000000000COPYING --------------------------------------- This software is licensed under GNU GPL. muse-4.0.0/src/synti/simpledrums2/README000066400000000000000000000032431404105311000177160ustar00rootroot00000000000000-------------------------------------- Simpledrums v 0.2, by Mathias Lundgren -------------------------------------- Simpledrums is a simple MESS-synth sampler (MusE Experimental Soft Synth) aiming at becoming a simple, tightly integrated sampler for MusE, specifically aimed at drumsamples. Features: - 16 channels/samples (1 sample/channel) - Simple controls for each individual channel: volume, balance, noteoff-ignore, channel on/off - Main volume - 4 LADSPA send-effects can be used, 4 effect taps for each individual channel - All channel parameters are controllable via the GUI, or by MusE:s controller handling (controller pane in pianoroll/drumeditor) - All effect parameters can be controlled via the GUI, or by Sysex messages (f.ex. turn effect on/off, modify effect parameters) - Complete synth state (fx-parameters, samples etc) is saved together with MusE project, and restored later when loaded - Samples automatically resampled when loaded (if needed) That's all folks! ------------- Known issues: ------------- - Not the prettiest gui in the world - All samples are read directly into memory (no caching) - Some obscure LADSPA-effects make SimpleSynth segfault - More... ------------- Future plans: ------------- - Fix all the known issues! ;-) - Sample loops - Sample offset variation w respect to note velocity - Treble/eq-controller for each individual channel - Treble level variation w respect to note velocity - More... Mathias Lundgren, (lunar_shuttle@users.sourceforge.net), 2004 Plugin management code based on Werner Schweers plugin management handling for MusE (C) Copyright Mathias Lundgren, Werner Schweer 2000-2004 Licensed under the GNU General Public License muse-4.0.0/src/synti/simpledrums2/ReleaseNotes.txt000066400000000000000000000013451404105311000221710ustar00rootroot00000000000000RELEASE NOTES: -------------- ????-??-?? ver 0.2 - Support for 4 LADSPA sendeffects added - Resampling of samples when loading (libsamplerate) - Synth state is saved to/restored from project file - Channel settings: balance, volume, effect tap for each sendeffect - Effect settings: all LADSPA parameters controllable and saved to MusE project, effect master volume, effect on/off - Support for mono + stereo samples - Support for stereo + mono LADSPA effects - Bugfixes, GUI-improvements etc... 2004-11-09 ver 0.1 - Simpledrums initial release - 16 channels (1 sample for each channel) with parameters: volume, balance, noteoff-ignore (C) Copyright Mathias Lundgren, Werner Schweer 2000-2004 Licensed under the GNU General Public License muse-4.0.0/src/synti/simpledrums2/common.h000066400000000000000000000161521404105311000205020ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./synti/simpledrums2/common.h $ // // Author: Mathias Lundgren , (C) 2004 // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= // // C++ Interface: common // // Description: // #ifndef __MUSE_TESTO_COMMON_H__ #define __MUSE_TESTO_COMMON_H__ #include "midictrl_consts.h" #include #define SS_VERSIONSTRING "1.0" #define SS_DEBUG 0 #define SS_DEBUG_INIT 0 #define SS_TRACE_FUNC 0 #define SS_DEBUG_MIDI 0 #define SS_DEBUG_LADSPA 0 #define SS_DEBUG_STATE 0 #define SS_DBG(string) if (SS_DEBUG) fprintf(stderr, "%s:%d:%s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string); #define SS_DBG2(string1, string2) if (SS_DEBUG) fprintf(stderr, "%s:%d:%s: %s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, string2); #define SS_DBG_I(string1, int) if (SS_DEBUG) fprintf(stderr, "%s:%d:%s: %s: %d\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, int); #define SS_TRACE_IN if (SS_TRACE_FUNC) fprintf (stderr, "->%s:%d\n", __PRETTY_FUNCTION__, __LINE__); #define SS_TRACE_OUT if (SS_TRACE_FUNC) fprintf (stderr, "<-%s:%d\n", __PRETTY_FUNCTION__, __LINE__); #define SS_ERROR(string) fprintf(stderr, "SimpleDrums error: %s\n", string) #define SS_DBG_LADSPA(string1) if (SS_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1); #define SS_DBG_LADSPA2(string1, string2) if (SS_DEBUG_LADSPA) fprintf(stderr, "%s:%d:%s: %s: %s\n", __FILE__ , __LINE__ , __PRETTY_FUNCTION__, string1, string2); #define SS_SYSEX_INIT_DATA_VERSION 3 // version 2 added pitching support // version 3 added multi-channel routing support (danvd) #define SS_SYSEX_EFFECT_INIT_DATA_VERSION 2 // Added Jun 15 2011. Original value was SS_SYSEX_INIT_DATA_VERSION (1). p4.0.27 Tim. #define SS_NR_OF_CHANNELS 16 #define SS_AUDIO_CHANNELS (2 + SS_NR_OF_CHANNELS*2) #define SS_NR_OF_SENDEFFECTS 4 // Controller-related: #define SS_CHANNEL_CTRL_VOLUME 0 #define SS_CHANNEL_CTRL_PAN 1 #define SS_CHANNEL_CTRL_NOFF 2 #define SS_CHANNEL_CTRL_ONOFF 3 #define SS_CHANNEL_SENDFX1 4 #define SS_CHANNEL_SENDFX2 5 #define SS_CHANNEL_SENDFX3 6 #define SS_CHANNEL_SENDFX4 7 #define SS_CHANNEL_CTRL_PITCH 8 #define SS_CHANNEL_CTRL_ROUTE 9 #define SS_PLUGIN_RETURN 0 #define SS_PLUGIN_ONOFF 1 #define SS_NR_OF_MASTER_CONTROLLERS 1 #define SS_NR_OF_CHANNEL_CONTROLLERS 10 #define SS_NR_OF_PLUGIN_CONTROLLERS 2 #define SS_NR_OF_CONTROLLERS (SS_NR_OF_MASTER_CONTROLLERS + (SS_NR_OF_CHANNELS * SS_NR_OF_CHANNEL_CONTROLLERS) + (SS_NR_OF_PLUGIN_CONTROLLERS*SS_NR_OF_SENDEFFECTS)) #define SS_FIRST_MASTER_CONTROLLER MusECore::CTRL_NRPN14_OFFSET #define SS_FIRST_CHANNEL_CONTROLLER (SS_FIRST_MASTER_CONTROLLER + SS_NR_OF_MASTER_CONTROLLERS) #define SS_LAST_MASTER_CONTROLLER (SS_FIRST_CHANNEL_CONTROLLER - 1) #define SS_LAST_CHANNEL_CONTROLLER (SS_FIRST_CHANNEL_CONTROLLER -1 + (SS_NR_OF_CHANNEL_CONTROLLERS * SS_NR_OF_CHANNELS)) #define SS_FIRST_PLUGIN_CONTROLLER (SS_LAST_CHANNEL_CONTROLLER + 1) #define SS_LAST_PLUGIN_CONTROLLER (SS_FIRST_PLUGIN_CONTROLLER -1 + SS_NR_OF_SENDEFFECTS*SS_NR_OF_PLUGIN_CONTROLLERS) #define SS_MASTER_CTRL_VOLUME SS_FIRST_MASTER_CONTROLLER #define SS_CHANNEL_VOLUME_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_VOLUME) #define SS_CHANNEL_PITCH_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_PITCH) #define SS_CHANNEL_ROUTE_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_ROUTE) #define SS_CHANNEL_PAN_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_PAN) #define SS_CHANNEL_NOFF_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_NOFF) #define SS_CHANNEL_ONOFF_CONTROLLER(int) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int) + SS_CHANNEL_CTRL_ONOFF) #define SS_CHANNEL_SENDFX_CONTROLLER(int1,int2) (SS_FIRST_CHANNEL_CONTROLLER + (SS_NR_OF_CHANNEL_CONTROLLERS * int1) + SS_CHANNEL_SENDFX1 + int2) #define SS_PLUGIN_RETURNLEVEL_CONTROLLER(int) (SS_FIRST_PLUGIN_CONTROLLER + (int * SS_NR_OF_PLUGIN_CONTROLLERS)) #define SS_PLUGIN_ONOFF_CONTROLLER(int) (SS_FIRST_PLUGIN_CONTROLLER + (int * SS_NR_OF_PLUGIN_CONTROLLERS) + 1) #define SS_LOWEST_NOTE 36 #define SS_HIGHEST_NOTE (SS_LOWEST_NOTE + SS_NR_OF_CHANNELS) #define SS_PLUGIN_PARAM_MIN 0 #define SS_PLUGIN_PARAM_MAX 127 typedef unsigned char byte; enum { SS_SYSEX_LOAD_SAMPLE = 0, // gui -> synth: tell synth to load sample SS_SYSEX_INIT_DATA, // synth reinitialization, the position of this (1) in the enum must not be changed since this value is written into proj file SS_SYSEX_LOAD_SAMPLE_OK, // synth -> gui: tell gui sample loaded OK SS_SYSEX_LOAD_SAMPLE_ERROR, // synth -> gui: tell gui sample ! loaded OK SS_SYSEX_CLEAR_SAMPLE, // gui -> synth: tell synth to clear sample SS_SYSEX_CLEAR_SAMPLE_OK, // synth->gui: confirm sample cleared OK SS_SYSEX_LOAD_SENDEFFECT, // gui -> synth: tell synth to load laspa-effect SS_SYSEX_LOAD_SENDEFFECT_OK,// synth->gui: plugin loaded ok SS_SYSEX_LOAD_SENDEFFECT_ERROR, // synth->gui: plugin _not_ loaded ok SS_SYSEX_CLEAR_SENDEFFECT, // gui->synth: clear plugin SS_SYSEX_CLEAR_SENDEFFECT_OK,// synth->gui: plugin cleared SS_SYSEX_SET_PLUGIN_PARAMETER, //gui->synth: set plugin parameter SS_SYSEX_SET_PLUGIN_PARAMETER_OK, // synth->gui: set plugin parameter (update gui) SS_SYSEX_ERRORMSG, // synth -> gui: general error message from synth SS_SYSEX_GET_INIT_DATA, // gui->synth: request init data SS_SYSEX_SEND_INIT_DATA, // synth->gui: give gui init data SS_SYSEX_PITCH_SAMPLE // gui->synth: set pitch and reload sample }; extern int SS_segmentSize; extern int SS_minMeterVal; extern QString SS_globalLibPath; extern QString SS_projectPath; extern bool SS_useDenormalBias; extern float SS_denormalBias; #endif muse-4.0.0/src/synti/simpledrums2/common_defs.h000066400000000000000000000024001404105311000214720ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./synti/simpledrums2/common_defs.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __SIMPLEDRUMS_UNIQUE_ID_H #define __SIMPLEDRUMS_UNIQUE_ID_H // Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. // 127 is reserved for special MusE system messages. #define SIMPLEDRUMS_UNIQUE_ID 4 #endif muse-4.0.0/src/synti/simpledrums2/simpledrums.cpp000066400000000000000000002111221404105311000221030ustar00rootroot00000000000000// // C++ Implementation: simpledrums // // Description: // // // Author: Mathias Lundgren , (C) 2004 // Contributer: (C) Copyright 2011 Tim E. Real (terminator356 at users.sourceforge.net) // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // #include "midictrl_consts.h" #include "midi_consts.h" #include "mpevent.h" //#include "common_defs.h" #include "simpledrums.h" #include "muse_math.h" #include #include #include #include #include const char* SimpleSynth::synth_state_descr[] = { "SS_INITIALIZING", "SS_LOADING_SAMPLE", "SS_CLEARING_SAMPLE", "SS_RUNNING" }; const char* SimpleSynth::channel_state_descr[] = { "SS_CHANNEL_INACTIVE", "SS_SAMPLE_PLAYING" }; #define SWITCH_SYNTH_STATE(state)\ synth_state = state; \ if (SS_DEBUG_STATE) \ fprintf (stderr, "SS STATE: %s\n", SimpleSynth::synth_state_descr[state]); #define SWITCH_CHAN_STATE(ch, s)\ channels[ch].state = s; \ if (SS_DEBUG_STATE) \ fprintf (stderr, "SS CHAN %d STATE: %s\n", ch, SimpleSynth::channel_state_descr[s]); #define SS_CHANNEL_VOLUME_QUOT 100.0 #define SS_MASTER_VOLUME_QUOT 100.0 int SS_segmentSize; int SS_minMeterVal; bool SS_useDenormalBias; float SS_denormalBias; QString SS_globalLibPath; QString SS_projectPath; QString SS_hostConfigPath; QString SS_hostCachePath; double rangeToPitch(int value) { // inrange 0 .. 127 // outrange 0.5 .. 2 double outValue; if (value == 64) outValue = 1.0; else if (value > 64) { outValue = double(value) / 64.0; } else { // value < 63 outValue = double(value) / 127.0 + 0.5; } //printf("rangeToPitch(%d) %f\n", value, outValue); return outValue; } /*int pitchToRange(double pitch) { // inrange 0.5 .. 2 // outrange 0 .. 127 int outValue; if (fabs(pitch -1.0) < 0.0001) outValue = 64; else if ( pitch < 1.0){ outValue = (pitch -0.5) * 127; } else if ( pitch > 1.0) { outValue = (pitch -1.0) * 127 + 32; } if (outValue < 0) outValue = 0; if (outValue > 127) outValue = 127; printf("pitchToRange(%f) %d\n", pitch, outValue); return outValue; }*/ //--------------------------------------------------------- // SimpleSynth //--------------------------------------------------------- SimpleSynth::SimpleSynth(int sr) : Mess(SS_AUDIO_CHANNELS), gui(0) { SS_TRACE_IN setSampleRate(sr); synth_state = SS_INITIALIZING; MusESimplePlugin::SS_initPlugins(SS_hostCachePath); initBuffer = 0; initLen = 0; master_vol = 100.0 / SS_MASTER_VOLUME_QUOT; master_vol_ctrlval = 100; //initialize for (int i=0; idata; delete channels[i].sample; } } SS_DBG("Deleting plugin instances"); for (int i=0; i

矤*ޝ˯X%Ezt =Msn(HR৭ A5*3Pw1]QdAWDx8];8gy /JVt}7)FIO I|ճ&tݭ,Oir,O'A>۔Q/8f@A 4(9eYN[z55Cf犽LE8#@%!<37?6ʥq;݂H_/NZWxŪhӏ9fb DjT q3:#SDt"IR2 y Wֶ/EoimAq"DoqY1+z~=3L{ü~祿o|.i`Л-K"c9je ǡՕէ@zߺ7A,w]. gLhUzsVjXa{O?e8ʯJ`WB+s5Gk3,y@M\̸;p@3n6+@ 2;:~=4 : A(E,hs'o`p<)rts5IMhPE9.^-EMul*W9 g6assxZ ϓ+0j_,;Usbv^yuEBSG5ʚ5ܾu{q7M0S`HQt|ωp\}jYE5|#FMBpQFvy;grz=hnZoiW)a!M]浍`nD :e[vsiAMn׷(aIw-vl)Djc%mń$ i}%#f]:t ڋ/P&dispKk=h+ Sf:)]tyY`* >->7;kH8r rE-:axqݺMBk6 qscM[ugW|@+|C;nUm,wIiA7ۧ9pN]&5jԺsM~O=` tuҭJry909qye#u+ _ψ@.ScD.R8Zf z[Q! ji<xus\jz(;`hrŐ uu5#p ~E)^619zr1hY[p[|_0K~=.YS/6y~I1SﮐGovV#J_3v\c2[8A]"E  XӇ<'ϣG˵31=#JCDi eL!rixtK3`:&z}FnӁ' JlRx ]Pn{އ&6͟<\$qT_mYlkaS:^Ɲ +COr@KS9[ӏ^: ؇DG`"kofݓ`Z<~s l8};34!kO tǬ; Xוn f/GCH:fڣ ZxiPWF(-{k@%0&JE Heӽz@*W"Ci'Y{ )\ 69j}COTDᴬ":0yzKjO5=od2èhM3+UgO\Ą]f:iO9KbG Erqo}-!_%EQ:thwcmn:FҧVx p |s@%-4a,= ʂi}q:iB\SZ^6 R? ~o?R.%cqc~@߾1__oEb X Ӓ Ijf<Px?>g'@7o+:=W/7wy…Vq!.l6類Ïg4iĮ DҞ?p`A_\.啝ܯ#o?'Lu?o%RzsEG}=_DPlnSNQQ'#5¦n.m4o?S^Ėa:>3g:VlcG{ˎ}!B2^X WF+J7 zNcOSCMwQyqL]e@Aw-=ߤjC翷kA ?[[n(։씦#WtA6sZ*jJiM.쵻):-7͢l)eDͰ0EXXRԚMŕȕiMfяX[f!6?A P^^5. 25~xSdYcD ~M,>{D:>̼ RM9K\hKKM4K36e^ߣ a;Zi)y@4 @` )<:Wƒ͍ uH4cMdd:yeN(T6G4$j~ӝJ*-cЁfcK5\Snu/CcLieaC7|ջ\ɓhSy3VYiK t,'srpϽ>tN:%je5+oXhP@·FVMø?N=a웏Sg1bxx6Իh惲_CP7J[P@e5–a T+L4K~@rL[ =6D\x: ] 7w hġ,E3Ij%oDT71J=":9=ņIXXQy,׉Ғ52FYt3k)@51~ckt?ytX_2X:}_~h .WRR᫛ Z'h.: u Qmv{gVŖW3@yPfRG0"&RD)VOE1^T`%i]^]A!TTEwQ/6QG]?PբQI9gQ= >DzbJE+Z4o"(Ch /Ȅ5a#J2g֥C_%CC[M^ dhSWe{條HbWdr%9:NL']}CnrV="݃ 8;czgy`-[2X%Mi^6+4Fyぬ6-j]N8,үxlq!ogٔ3B1SJGm~K5-Cv^.7IC1Ϊ/Lc=e;6r<9іkQ-*g V U뇽\3#$B?j6]fF& LQZe)2Ҁ荝ӻ([:"1wB_Igݮ=?&U7"XNv8ea{y&}ΟyR St>IC'޲~JM8Ǫ*e}QX| 㔠yğCޜJ=>r8 >f_s^sPM;*l)2;~I%8m*YYLv˽KGҬUV9-9f9MމGNs)@փwOq4E+=\@~ԩ:\\a7|ƚ(]%7΋<CVj.0zhv´\ N]/m }jcYڏ{'\suι:Z!O__۳_e>y^+׶']{uIO]}: v}Y-i.2;{k/5p~\!Q/klZ#Z{/{Z߲u5Azyta7tK %vДPvaS t#yrW;m\ q7|6?ZE/#?"xxVZ)RU\̜iƁrs'2=4 Yfօ5]NQg3 `A7:т v$|OObecAVXoç>{@` .5_Lg}٩xW r_iX ˋ_O7V7DHyLt:ANs}4|=#-|c9J!c| Á7cVLX߅#A_;ǴUx5S' fr۾81WQeb,rBV=g\7rb!+>OŘt\ˊy+OJsi?dJ #4!2W , Ykgpm=c|]ٹ02^"M31 _;w0SUxC8 A;K h2\;|pL"\1[fi*"`ֲS[|,a-HxJc&&lu&8OR5qHJ ;t0Y@\]OHx֝PyFm9 u)xYrku:,2׌om߽FoQ.wp? sb񶞧:Kn+`U ]UDLk"~)W3(W~;G:x)HJxkcz0|8𜈭[לᢌ28whA+\9B=tۘqT$xgj(0u)+pN4]O:58oap()6f^e#1JY|ܘ( 5ѦSGXY* [zvZna]Pc~ J7Q+#&B5n߆ZJ* `@ʐ8:qUgCSGB Bdɠ<:^2 t|D16!G?lcfXU* l+Bdm;}iF|g\U?lhWpѝSGSFlvzW`!B9 GTF[Gئ"rJNWgo^S 0L[>!#k- p. g ˽V(#QP[돈[d0(2s GMHH)! ;^,sju8u L$O)J`q$.a_'+zT]WBS#-QZgY‡d8k<.v^v"Prn#msvBiI\[WEah3+ 6kRm.VQCNXZ`nXŊr ic7 N_EXLMW{~Ii|<ޥh|kBUpD SL\YM;ǒ04]-燬l釔<ܒ}Y~o*GqY]|tĤ;+OCn :5u~jZJ[4D c5P m=H(N+*e0D  .ήL,u8&)j0%OGN@mXPaE 89 ?H>Wr746CxHg2 ?oࡖ0,;S)Ӿ]41/ _/ls'zӽEKEN ?Df_p^61963~Ӕt5}GeI$T4\s.᜗eU܏,8XG;;AoFBlxȋ\ Nt7o.˚5[Î{D@Cܾ.m%?YO%&2 yOc~E6wm|ro)uJFQwk<^GV.‰_:wO R5qh0"ÀՈS .CdfN7* l5!9NZ:3j%#̩Cg,'0,'$=snZcö9LCGg{?yA*nXKe՟0e­)5'zX`c#*h=u/QBw`å/YChB Z͕6KEMkί>O>nED#rTR(nA2j Ku|s1"WKX{Zrr=A]E+P˄'^@ qu$pÚ$*ps%u,*3m[hQɖ!"Жx^WH9 XSp+눘oHHٯ,xsb4 U-Y^nOC m}15p]ߘ2+NP3¢3{v:܌,9-,אǯ#.R8< ~V1eq¯Ч4u>hУWz98E4&w,: zM]E8 7?鉣w@|,CCaG]єK=MTza|CQ+a*ya̾E5([ +!`Ȕp>Ńꍚ2}% q2vHm7 }^JX bnBhcs1xVzQbGչbԶ(Rkqf审lǧӮ5C>yKM Zx[{5NE/IPU,XoƢa+(eB?sgLaT>vޯxtɆ3ҥ6JpJYK)3)՗lί ~DS-םL]i2@kǹݰ6t6p9kxq=+,Njfhjb[w)!'6)Tv~}}%BvUl\yC0%y#eV?%wjŀx/!] PN ~|%,1b$`YwZ"- tᡙGU&>ad_fչc9zwB.]wC̣_lSaGBq}`0.l4{n "Ji]v^zW3^O(R6]U)EuCȺyYy9ĝT%/a3I'G{w] v+GR7.c.5)\o忯,mh~s=֧S~#-Ӕ 3>usWg'CRkrTJ?3PYS-6_I}a= ͯg%)]{tճ|h&Cӯi 1)8ix\üP1sFsZu%C]ΡsGL0<=@"e19p볳i[3MRT%G Z4Vv,CpcPu&|ڙE9苪i,)yUƶfV627hm;P> <`I_HP_d)P7J q7#Εwp8#U:BP]%WUQj+6]ӱQ*5NmaK\[z1SUNnCssnu=UWYÉqID$O)#OZ+*ãL0,5tE08ԷHw: 5sg\WB䣡Vt R$F.3tPBJm70.Mݵ[zJrFZ֣fur-^%MU,aԨ@K-N4` UX.(d^-۵CKkz)\Ve߸HmS/miA\t p]J|Z&uzk91S8Y0 e ]J5 }ޱV%Z^2T kVZUᩙTQ)кsq,Ӝv TUȐ.Wa%"rAQ׋S.Ƕj ̝:4 A(˰v :g6%qށN u _j<3 F*qA99.-}G?(Մx-26E#+9 FOJ]bMXgGzTM6!c9%\ 4p9,δ2;ߌ]P6΍V8"_xmuSÈй=vń|ZNtLoqϗa#"lk pV 씑F@DѶVuگ}9t6GķSN9a{YM\qQږw(88kKAO5Sm~u=ƙ\x R8џprs:)!Vs/n݇ wXi"=e,ѳçhߜkG"3O,jA7@|X[ϑ4GuJ^x~ ID\yBJ7{v2:m4,1!bU.A3!C mdtSEn9˔~(A,Q?G-l與+PzGû|oԳCt?2 ҺۇJ=(z%XzK9$)"$&{^OR 6m[pfc}ϔ=eQG=!ԃp<5c!VVFɨe-sQGbnhlWx\T,ތ42tf`Ӳܫ Ut|- \ΔEK̯^KVS)5p`+'SΆ];M)mãQ嗐y!UN5['j'9МkKJh8i+jVscUzU*TD%B*8kj 6R fMeYZU4p`:ws-D -z5FϦ"Z',TVa, ta;V@3ěPKs95ΰF@;0pqtc{F>eiÏo W'<̀둶@3ݺYȍg B>Q=B\3mroMp\]@&i[҃gT1Ka-nbUNϝ ozK' 9| C{xe[/]n N~GzG0ޡbΗR~4gӇqGӖh򂱴ܯ:^XHu8i֩mg<8O.⬯VqT3sCh×W9Ư?u~%ihvg<_"zX4ҿXv+C\s,MG$,-/DUMޭB6zGw:j-U6r欰jqث S Br^6-`6yCO"b_T״+Pz9#.+.¸˭EZX],j7$(5 rQ E[l[/ (,U1iE޸ݩTi\42i5؛G-²ը/#2 w @њybp\ۏ^P)t:quBt-\Xpt4gk-.5WIcUwxX--2t7i kJ)+e(MO[+z>pxq Ӈ8!LѾ-чVVS]O]P;vۙqap8nz2=wʯ"{!uVޣ\[/YEVSpQw_g'w7{fC#%2A\:oXssSx /sRwRcW}b@u6oZ )/5bŶlL*Ơqazg'Y՘/et]w;J򾣢Lo6v`-]-gӜY3;˖g{Jv%D's=hu7۶[-+deFTu)e)ʹ%Lgg=ABrNĬM y9*l_LѮ 1B`tSyi4xVrz!il|•+₩9o-M4DAO9k6cgևF3VQ5Zth/<ީ;t+\JĹrZ Lf#}<w W 3o}-HuX+eJڡqxkԼBDV=} C. {2˗._ 2hކ}\/3LQ<$$u91=qSكU1+ NzDϦoUa7{D9|r-{%(,+:l<^eb:jRýATUT>H=rDnd?U9j:_0T #)ײ\s!Q8PxÂܪczߝ`y jz%Hf`?0{F9kz~%ynAMH޽b[P^g==b4܏=j5JALX>EPLӇט)& Kl-x'˛$j}d,EaߗKX.³W0̋]_-\D,4L ֑1=RȯWvBԍUŽG6{# .IX[H%Z'CS ._,esCJrz0Mmi͕MxZATzKvFhU7]L]c}{s)_qkD' {=A}ef,*w=7יM"лS*'F`o ʑXF&'ŗh°6=e\d:6r7pµqGsف=P 0/y?ӨƊ!T!rfTPdنS}JGdz.S >Ϟ&{~PtDW~:8Dc:zŗ5OsO01Λr9Qux %&a890^@}F#ښw*ҹl"N/_n _^1!jJ| Z-VЖG;րXڻU]*+2XFIKO'ny>b#:'y>b|Σ:'̷y>fxO_3L<'S)R*m*ffqEC0 8`邇D7]me^oX49ȴc(A5{7,|ޜPwXaŸWHvjR(8ԇP"F ᜦrgH{A"'gJQUuY ^,GHʄuFĎb,b4Wn+8tG(tJ8 9ѼV;^~f8zGLzp.Q߁1cxdDy3%a%¯RQ@ѳ4v ׸|ˊi o@䏹Mc|Pq3nvZFx{uaa9<^AaPυ͸G*\2~FR>}";Ѧk};MUt >NLgjޤCG~% i@^|e+Pf|=΅61h:4o(g:3T0[kzRg~t*n|:]a]oM@*V1ߐ"5 iMvզ*l.NK2qNo`!`N>6^3FQ(OJ9ә+s:s:s:s'2s'2 ŀpJQ>}C^*){!ˑX# |@S0Nļy E>[ r/Q.oL6:Kt#+JJA 8SFyQ)7W'ö#ڼbe=eacΞyaNyfcƞ>P yFc2w&pHh#R Hcb(t*GozFRXx_ }9>7W4f>b|OTs/xD{:b#DGD˭mK1wAΘQƇռtvϽzvOP=aB%n_L/\S(9e#|؋,tk^;!X}(U˸ڒ?ZGzmrбb,h=Yy ,xv1t˝7 th C4+eڎxlEڰsExD r\Z΍\_H W-ZioM$`׬g@VZc9?Îp0) VfY\X&E-EB:Lq%Te+n<%KUŀnBgHdy? _6gSiˡj ? @`[&]`eτy芚慞#uctڊy(C zLMѣF"-gAzjk_o]H^)l/0cUX;n:]̼N,d7y&U8p阀çd{/fbڴ5A=b߼^=%tS !Qׯ@h?q}ͳNȑpxZ+gJ4só@泏N.)vg55/{W12LӚ|2Ѱ#},-uS9xAt4ʭeׄ˝4QjmKM]٥'f 'ݣ2+#^,3\zʛ;rڕ4_J0̥s =U @h@Q%U2WI~C-)kPMDl&+r6hE?=!K߼V|^ҖcW_-cRW4LMRňggz* O!1X)jyDJFECo`>[j{FoNiX**S3]@0L;8qL948=Sh5x@uB!ƯX0R)tiX(têшXni `ꢜrqdb\-eKzrSSZ\kG[iB,]pr$hώ99<#X8s*8} [ x Xvɚe MlPFnEíZYBVN /T̩@`Jk : 4v(6jV5V& r;d3NAW2/89մrkkApiXM\o`,2h˻߀k%[Wi8uF/}+v`z0*VJp6"+hrvLAx]jTRJ*T1.\rˇ_Eu 0.E\= >ޞ]~SRpj: 4 R\Agz@$)0az!WMػ42g'[Q7RNu;Kϒ6*x"~pr~Ł2s7b Nfarc'Mx;ح]QZ ITƼG6l -vs(xo*9D8$ҴNzH =>]Kf+/RHy< aNΟndؙ ŇSh.&u<&ۄ`*K馁0(]57J޵Z&1K[YVSq̅.l&+ykfyvٖ-:JR+_tbZV4h!ja-n|沠Xboc|䪷((+ $8cg~CwIM+/Mk_+˗n\*MGu*+^]x~I_b'+~2wj%8s_(?k{_#cI`xfICh-rkޠi5)D!5sG9"r#Wp|VqzMn_o0gtz4=Ј?3:zo d lO0 v.cY占h+}[ply.0:ӡ.ѭ:"T,}Ц_/njps];T/s߆ QoV}tݎTJ%M滎ZDؘ E+596+\e\ĨʛM c#˗._raBTP9e4OL}Ra䧽ʫS4^G8k-[?+X]}tk!>bN@ JFyKjE:} ݥp3 l]7 `/gQoQ5 rhx.(F ")hi|qB l`7BGxTاLjolK(Mj&G{ 5,JLtpV؅X<^rvk دx)ҴPB +7mTr測Lg!B̀h^&tb5˅1]jO-ﯮ`mLDnxC@R=jmTa#BPz(h7 UOS^چ[64s:)q/ lbƀ9-‚q*7+AVEŒK|UM5s44oc2C*պaS8ܭ%|/(VkLwk[a///yiWiLS*S3*S3)*T_:?/rr AAbH !rG%=_$*j{atlpWө0y_~%Og2FíyK`Վo` W^ )YaPIx\釧>P7hΆ0HW>'ەa/*]`Y৳7<@ߴUaqw@gypXk}mBsqaˤ)իhTSFf>,NI7h..EP. qͦ^A:,?{{[ӿQ>G>?=Ē7XVTl#^kM/~ƒlB=qM} 8> ~)6=8Ƿh/R߅ +nsJ*TR2J#-x(8riOi\Cgw_|pzBe~tRG0f ;?o`bڀ CKnJ"zf z=n%HT ڏ]x>F*pidĹf@0!m{B&uxj4u[CwWP8 )f.GB@r)П^ko UCe| v8 zYXq2oyh׆P^lЯUGh) .KW!&s#Vgȋj%9[ܣ,IDyLtJEuӤI" l2MAuXcHXZt9D*]<1o (?DTX__OOⴴAApİ\63#K#YK+T ǤU͊]b\n:B :M5\LpR|;hS!g:Ey?<TFX(Whτ(]Hn5+쪻py+Oo%q_/ä(YJ9P h--t6XՂ!F /-ېJAYQ,Np7OP2PL}"j4z07m1 <9>8,q>Bsje'HmS,hmgпP@h϶|Gq9 b Zxoq?>#nD4}P.{;P) [Cˡ7wVzY_>?΁eS_x_HfY("ae|"k[vρ6ž}4dxs5DLs/:'2WF ˗.\%yyӝ999 9,Nt8dgȟ?p%Ett Jp>&t}~zuobGWP&R^hMa%cs$t1g) cPţոmY*фibbƠ3>ģ[]zO~ L='q<DZoVy-H]WQCbȲ˃qBm-徒F_-eї2F[}oeі2F[}o-b ? т1á~4}sḐr/r?KŘ>*UQ O=]r{לFbKaΟ1gߩTJD߬Ȝ-C/+%1T{߲bk`az*'p/.0-S?Q=uE. F2A ~EB1P  jnF#?_Q #~8 e[ e[e e_a!.SSQllzF Xmp}'/Hސ-!xol3 4%?;Ɯp֣ =:==&5R-"M+d6kf5t]\pay 2]oSMtxiЙq]`QI:]HKq-[~2b3g9dǮs~g?)1l2s!6&bHBrG~|+ycXq'4y~7UZƣIK%.c|3vYa˗p(+8 'Ny н5}#=/!*:hL`/2^,{6IJ` W%`2n s,reA bzL|?rړ|cņF@Uws. qb SIkI,.ff(B0H#kqb ,ͪ5$6(!ßN='.{u፬Ab6bD2jze?"_#Z pFӮwIE7xo\yS7X,;2 dg@bE7i_xER*J1mEj:DaI Af skt!+s)5Ks.dFT.K1m,ǻ%Eg޸#܆42ElgK FHou祛GFqj.cbfxg._rۏLx,$5$@q(œ9]x!ko-LവWK]]E,SKUKARq0yFi̱)eZ堠 @,/}oĉՠRjlIK|bZ* Я/HP,Kx1\,˾4A[B fβíz8,SFT<=0^I]qwKsKEVǎ!bj3L߼@WOpq;]HF qke.m&^gBݢN5:*l)TeqAxvqMBxN np]届aJ#LBey?(7ۦNWA=OmͶ Oɯ.45(~_xM tfIDU51b=З˿ycv%n3Bc`(@+hv>#rDS1=|D Ew{0J9; gn>_9E {t5rlvaf& {/mHP߮%^))KOL˵)ʼyv]s [YSE}~?1$׋yf7}ڑteC)Y.)\:֚z&hiEuqLu?JX7EsYqiz3[m:#k$#p6ٚ7W =<8 .|Ekn~oOV2j].\{[ҠM c\YrBUs2^BW8>ߒ:^)ñ򀍏ޑBqCNh NF%stFY>j{z“̯8gm:"FA|~˾i&gJ$)Q-[g),N5G)v4L5F'cb">X/-9梻_]P.kZge0y:1HË㚜C,<tfj g;hӧH 70;CA߉M"ZmM?Rҷ={]W6>}#8\׼wK XH_7߯2 304<+ecl;kjw?> OiHX#:.c^/u'~to( jՕPOIڡ&[H:ľ;ܡH}cj\&RT3L\箹| 1N'F%5BY^WGCC C^%ؚMˤV..˗/x> KY8`3x\K4Z6oō8+*;ڃSq¸a8x jx ث{8Zc˴v6 S꫃Q%y=>%]Qknrщk}%>C %7^a߼h<^C b &i7N hN!W"6B8+~̫߮}>+.Je4 >b Vryז X[?թI`8')igjÏ1M۔nL84ün;\b%5Y}_Ľ_]is2v j J݅ ijp] W=.qŬ>{5~@➟( gDFJ|Q<Ρg0<3r߸sV%_!6O3?6PB Uj> UX{u{_] u˽5ԕZ{Y ӕq>?O(.*v%ީ_Fǽ o8HBٹHd84N'f!@ /:yvcnAH`2ia5lN ]?uϧX{AzGRENE[N:Xgm*SQw!5XE{lALw{_,MvkG./0<f0Lu'.$F['gfX)م/ ¥"c g~NHlKBm4k`}՛^|ZneXն8:֌Ѐ(WW.(M);0Ŧ#h8vb0Yr[Jf>.|]?m2&SX"cyX "K W p;R'' fhh3 JO̵0W~ZQ) O34W_*†=JGNSS/?Ex6qNAr,>UFczr~3XLoO( fR@XJ0&RHrpI]K90_ؙFK5oQƪ)Z:a]&b"Ƭ<^טz#9 [2GKvVfj(N ,8pY1X0! ի\x 9u ~F/̙l[47 XE!\b~tIF<кVp~yٽ ǭ 98˾#bC[QE5$`4W>pmzudd5s-JFDN(V8ZHhJF8ڙ!\_86C6-~0mzZCC7C̏O({L!G@wuOgȽe˞o߉{f(> a8gr - i[~>85c\.ޣ߼e@<؈p5S&b QAH"oQcq*N6˛>.爤.\r˗w5LN8nLbYS^VYRlU`ۜ(J܆A4׆rb87⃈;AyqkG5W 3f3Xfg%xgNM+cȕ/`p_K.Yh|cW͚j.\eŘ<;l9BlξLI5KƱ.Dq 'r;Bf| _!wK3dA3zlc&O9Xh89K@u=xH2%T;;]<3GN/tú~D~&p.\r˃݉ %u Uӟ)pj'9O{wr{_ ?| b<^uZhR}9P F3?!M\w(WS+eSng_xe B׻r =BnS=wɆFNX&#~ٗ.\HrD@DO7p)}j3&xJ11ePa_h '_r˗4`݆9deKg_7klW5,6anpq:0ƬD!P^:< ѭW#CWZį|e˗.ag7v\Y秦|˗.5y{@ RgOzc8>'Tკ=Vf͝nPp.\.}b7QwFŊ/\SlpW&7 C2NU#D$ot-./xd Il0ੰoax=IUސ2ÏO[%P:a!3@؇/CR"~%ᇩ󯜸^@q|Ð׿6\wϫ,q-]OcI bŜ\%if֋sMf #K4ϩ;Gm:;.}B\i t}˕.`5|wYX%Do}Qyo\P?ӗs$q_~!:4{CMNqAŇ"h~wa޽뗤FE-jg l_]= tIZ ,c=o57~ߧlJz1Gzc Ɏ?$8Yޝ\o$5Kٯi>/4Cx Dfh) {xtbBʮx[*俛 aNa7]egU(>'򏈂#liڋ 8kJyyxvJ}35~VG xx֣T|G* &t9F@pĠ%u\jV[s9SVQuzt*yKp;~ _b۷sC b|.\_a+eo@=ĺ^}+".}/KM_]\_*x,H;}|1Дnj?94KNqemrI:'+$4yըo^]f&Nѝȅ^R7P E,0R ;1t{Kpm3o*|y2ek1P&V&QU.dbiIHF戴곣#}(+(G;u ]Aظ13_=%P{f]ˊeII@%%fxu8c: 2ݕ&7yY 2Xo~,JyWeԡ$>z$@Jn(WHϣU!@3 ?֑eD-KoK6iY3@1ѐ aYjS@;`]je5ͭ!mcھ Hv,W z}Izy yo8_ot}hQFt~4x 7r|+GrbZ6b {08 +? i5_R\b:s qɧpfmYr㦼}<>_ |3?XCB™fu5nG7 [N#Nte6 %9>2]095" Ä 2(GJiԜB9JD?7W~\*ߜaڳDJu+T;Bn|D b}ĵՆ3Y& ,3Z{!g 9NRri0Dq{*[W{AV^ҡr™5)*-5( o`4 ĻC9 Ou"O$sHSBrψ-Е<&ϺF'+bYh:whٿϋi:KۥyN|TbljTpԭmÖ~&yѦ) ӟ#r?)YRwּ'MGrcM.v;DKE؉D@jb^_-y݇vHxnPp)wCڠ .9ABzm i /IwƭAZ} vAzBUyDrw54[cr68YىHp<&^sι2u>tUsq۪OYk''ПVX':=,|xb'\:bTqXEEH1J1N{,n z;tafDN<̆sy, e ٚ68⍪N1ƒ?9Եw0אA 0X6V: 6!GggFp .P'%ǣpf-1iMOd8tM/q,6B~aV}/_CWzL3=)D4G јl_AT "z̓z˖'@W"{Ej .6e 5\n|LG9xiӗVG?d urKxe6}G\Hݷ?RF(>[lX!搊䮂7gʔ\84 Y[|-.h!xT]d x٬ij^xߞ*Q( XxNgVx1ԍlmo>83Xؓ ڼ9ngeQ'4P|1Vvێ]/sOԱvrlMt)90i5i# /%LF'Q @v!`,>~eyW#C_&zh;".P\(TP Ido;0jN{4S .[pht 4#X"!f|49+q^yC-Bˍ``Џ@{q&$5(a_xl1A۔piהv@*+Xjo<>2Es]s0ZubVplsטHӉu! <6lC=Ah߽ě7LҢp͞.G4).JlHQh b׼h4s^X| 󂮃~eb6(`>1yϮE,0I}%ZQ*=~cCr\L60P,6OQdV/D1U{B1&qe)sdOpmvjA2K kB?g-ONrr}-n|{MmþAʋ_ęFY_I׵cai'۸A6A$\)Xtq!(QiEaTti6Ii "d*+`"éO4Q:&_ ?A_Hf8=tN %"~q;{rөe`,yDTfZGB#nX07uΐ+5CykO3iǎ?. {$VF&|_mm(mO)nѬP[VNzVcjy3W>"AB#GE3oY7=_ fX0{^ÙɇN:QWQ)QnBP`yEo&wevaFTܑB-W]+GFF4r^'xmT8DQ+E (tUw)t}>~:M[rQ "ޱ<*TP.i|5F"ۧɤ{AǬWc rx~I< KKEJe;0/C z |M[\*p_m3NdYJTDhcS=`ɦd[Pn|~)xi\L=@Efnya>m)gE3o yo 12dkp L{tw;f7UrhAنW\eo@_~f?F64 k7jq 0y  IDQʎdQ`kտ>(0`$yK'AZA">%:u~W)h? 9 K !].áYt6Rkߡ^rwb`+qǗ8MD/fTQ0GF~KL%47LN~ n6PЃgc()oak8m<y w>[1(1_]}g~u5¹7r!URuX24kr5;0E0q+IV0XF&D9#XJLJ"*)&k&w2o'Ʉzoj-\?xi̪ӳ~~΍uXD<~fės% '}Gk[OXtsn" /V1.3ܻ<Թ5gb6 c믔BRfˁ6t_;7!3(] > 3 V4G*ƐWCai+ +]k77 +r_)B_zߐr&5&T6BpZRy5 ƒR-&4fAs[(OA.kAAa*'\b]*wZXclR]Yӗ\/OV>}m֮NwaS,.^e|mT RȋAZˢTkTZZ9,'VmlM z]\< O]axg!<͇% GrA#X\;a]V;J)&w8&wN>ahgU:?3*R8{Pc4z1c!i X rJzE4ΓMquZrS~W':AZ=x xH#qg1+V'h8V47UgggɎ,i]CJJw!PvЗ䫜 .Xy`Q>~1K ]5ă7[,:C_-*[hk@`QC%ӆǻ ??xD-|;D6JVhw5Jiv`xskĻ CsYTK`S&7/f._,{Kp{+|Jqbulplo8W2*,UrدK1!o-*1쓯] 'QK*)F3| `^iXe[ۏR (sD_ t5q%r" Hdrfoŋl4>)w/؏r0793 md(0-R)zSN_y>є%ُܚ?q&WrD\ ,mq˾ޅwg qQw]^UeQFn0|KK#LG[gO6Y|^&YztԋoxM|mR?3(@#LWyF Rs ;i.,5;_׬ʗ.\8;p&ȈvDSzet 8p`xrm '%VBUh4 dyw U C+|jq]65^-ScoP^qׇ,̹VS{t ꅔC=~DjP!@0qД/M_B502^nG-wWC[_Nڎc]zCT~JG +W% Kses'NyX*?HMJm9>⢢pHዋ'O(R~sSe_S)Gj[kVP_ -HoݮVwj#ifxm'ݵ&'!>WJ6J ,!tq:,'K ۜ,/P&>bt~_2jN&3^uwW>bL7,hOKky<ǫv(_/\1]fWc#+;p0rhˑ 9]rxp8֎ŭM-5*S,Ŵ"sи"7_~~KB _d7U. -1R BQɮ@virblƆ*c!A'3s0];+hx~xEk; 鈇J8pQAr[kxD.C ЁtJ*\ˊ0GXBexJKӦlT%M/8WQzR_åtgp]Ҵ4C+ۇ̠RŠq [zl)]|ҽ(҅``@ZNTU_Ĥɬ3in^GxvuQүQ;r|lcafҝ[hQ))),F2q=@+m2<OX󸛕yS k]OX$) !YIB,,̯ % aө^GW7yJgb5'W`3=k0[!"Ym#c9" Zgf)f iiBY軼sMJm֍tv<TG4}or>RaNO ogXaSׇkM8 /"0Z̗t1:J效O0l[渍z1b*-_ _McV/O1+{،j{馑DMx&-ۮ:N%cBja;*{ JW̡]= v\8~;;q,JJ!)i_H[}G߈VXGCRzf GH,џ Z)118j-8׸Xpb} fAhL||6M2)\WUP'͞fq)Ǝ~gt}}|D2aCen`.a0n(ZMG:cO__ƽi^c?s V>Vb~Zi\0rrC` _5`Z(hٷpW,^#ׇ(hWW¿feWX*$47׏*\LjZ^VqrRÄ)Aޢ$sݎ^2U˛?x9-QO/klwMRFcJJbi i9l[^ Q1S}f KralJcVSщtH^ |*1;'uҦOH=ȦD2G!.(7R<1e+*|N!bFOM<%!K:0E"˥[`A6k,Vv7^HɰWWxkHKRTRJJJ*TRxTAjDTn&UtT&X"ֿKJXdE&X<_ɹ뉟,{O͌L"\ {ͨ򿉯Q3oD7*$ɀ0K4oP]n14{1*1ƋV= Lr8O'2H6}łWN!UF0A,+E0)\!$pL#K }jpzˋSS*')S'=`\ kS^^-\0Òh@vܦ䗆Vxt7c\+3ˁjWJKƲ_`J4ڣ0]9dz}R}x2 Ϭnz n>L=<45i)^2`kXߐYI79"L_roRnXpǞxfc@~eٜ\1 "a-+ Sjz(@b- n0%Lej6Rõ~=}e h1 L7?цnjZfU}x#~#~#v~#vcvg=ٜf%//[SSSSSSg ASVL{Mrr}rl.}s:ȈnM~>ZGT3+C=N22K3:cٕ}c%#rlkWږ~' V)RY}չb1ǽ~bMM_r:^"xmO,4K{1#ٕ>ek֞dX56aw!Ĝkk\@f&S !qUY^A} rW2gEK@T.۾ÌͥˣlH{`)K* m!}N!M<3<9&7V> Gé^:K/~X\RM"z<ķ/Hs>&~LH|-=%L+ji7.ϾQyZ96Ux}''5(8\39^ϭJܴ}-9[8ͳթj (E6Xn9%r./&TwĨ{؁!}cmKqj4{>Ќ#-~jH8E8> ~6%m#Khh AkZp#m!&%6CgwсW-0Nswbɿ>pE'.tɛ_ľק.n U{r@sv*-`/'+h7r @r Zwfq 9уa R#-NXH=Ao7fCM_2x},F?x `e"T Я4E=#/݈ kP S6~ӈ}1crHs+gH(ɎB:0Xm#mA,Al Hvc6q>c)9v+ LpI=vOղ+|8%_XLtž<'Vo6!:Ն%Jn S\\{,xx]cxۃW  Ak),,sz7ZwAm.@:Yo۫ ޙ ,7_'9S#KZr _yYY}?CQ!Mo7y{?KJ}db,Х+2\^n ?v-^ : RY} ikYb{eJRJ*(>;8oH>FΓM}%Hb Ͽw}{~eateu+;5imk.>.M&'ɋUze,ƚ>k1CE&xL` *eE, *q \(3?f9 E(3ԩbuD06Lyõ(N+P[y^,ժs N쉹]}au7X@%/CV>ˋLn*o_oUmO9&ZYijHWx;jp2q t=7F,puuz%kT E|lfpdz1z_!yHUS<7_aDoƥxf *&?wRrtf%>E3vJu&MSaLJ_iJP2 j,쾑 )XbQr*=r}15ɞ"Tg %%C[PJrC7 YB"Vק+mfX\r_elg3-jo.--r˗⃬2觴zxGzA=SgJ}eKMSO3V/:ꎔ=GYG6w"7=@Z Bf'D-i{M??Spz9עf]S vaz‹]x._i‘b* 瓤 `ެŗRx$h_4YlJh(Eב+YvcCy@loBVuR#dkL:eZ_,s[p  :NKU{IH/3G|Zѩ&7:=%yKq˗/U*;_輱׼n.Ϯ@MC=`Gϱb:{׼/h|z̃觬u˨_pGG};~}%|x60֫69&uL]a>ԯd,2/`?g5dcpkdFQ :A(% ft-߽ M ~@Z3o3^RO |u% uk֫~ &f{+K#D4Y>H(|g5e8)fY4uu2ccOH1י O̶Ud6YǓxv֌k,\ڪ=yhRn Xsy,OqyGqL˙g{Nֽ8fi .Y,u5[5j>|F6x;K3(YUFk5 gANy!^yqzB^CH5=*; M TJqgMM5&k*TJaJRJ2RF0/4Oh+X->eh^k*oSf:oV=NW(;"86tj"p"Gu՜ctKTńwC^ެz&1q{q`f$*I aЀͣ3(k"6E0g1u91z=L9"td뤴^XirpϧYd.Ã×gHxP`?NW43j yơW?%SS̉nDP2Du|ۂbZ{JĨ҇bսB`A̵Hz"[],^:?pňyJ _Kn{vgW΢NDQj`-XCTyPVeFPA*m_ K˗._W%ƢJx)Ԗj;NG9@ Q6S2Lwh̢*o-NW )0m7 i+30xttZ)Qf0z7?/IMyoЈG:) OY9ΠNRc$2<@%x2/SYu{ЅHFIPoKC(\bA(Vn_HWc_{ˊ};S %8t~Ɉ%M*UMx&.\~ .\7._ܠܹr˗.\r˗.\r˗.\qe%YZO}g!KS D(8=AYRa80Vuju3 XRXFLi0AU"nEh0M"V2ѿ9c4Xɤt$@F u̸9tG bWѢs^298G>Nr|~Ϯg9_Fs13]w&M=>9N9N0 Ywywj;G9X~_(j.׫?#Yy #=ue??y~bG[ !O9f}K>Rϱ~fY֝I֝i֝^?ϱ~gؿ2ٟik7ALols7l!%ee%tJ.(C@(;BA  ǵFy}P+lr.\r2_b^5*T *TR:TRJ*W"/Y<e͚4CeJ11.\alX@\؀(^ s͉s!*$`E%Y}Tx*WJYΉp׊r.\˗.06QоD^|߽7;< 4rd+# O/(h4G¹hޜ e}SQRx.\r˗/RJ_"xF%6O~F҇;`,G Y#AH9MIr|K i!>?ojTtS~Rè\L#0P/?k6CJ_n\~R$Yǒ 7G\{?ܫPX/9Y0"eøg[{@ p y@{MÙ}IpgNWW-tR;>QIL2(N^pQ/lS3%z]@yQMBW;Nw;K^C?|O'oM :'S(ڢ:% %uP{Ql,w|ΟgQO#M1.]4JO8˱ez'TC4?pW/]G^[N+<Ҹ19ΗŅtrՆ\`R\$?¬8w|ʚ'wġFy6BJ\59 v> ^}?OkWm?|g[^+>b?2$WV6+++F nb24y %ee%%%a(A!IcsH(R +H*5\9s {Jj \A e. IR1IO0LQGSRx*TGYkD[LPFC@J_!RU1vR_(ǒ~~ JMBq3h.^e^eŋ,qq._̩Rh̤r)aCƼjT^ U56qq9S)FZ3PaVJ+ƿK\l1hR HeKK.\r˗.\r˗D('Cb _D*P_|k*fk*!1AQaq 0@P?"핳.1 9)qɬ(:s~:틞7*Na*= [Z CzHXgYzBvYEEgl9OL5[ Y \v=|r^wi(auL{q@Wihlpf;2J 0.mf+UݲPmL!cD] ދaaTu0Ho\uqdTٗR59nTtVh7U޵LBsk=-no2@`A( lu+h751HkVn- /͓(Ż *{q +WO1 r6[HG~.f?~&-1kr +{Tβq ,pΰ_ vZepl@b‰oT]K2 *KneAXATfI[@+,  :4R5 L l*RG:gw`Ѡ&Roh r)Oi{>kK@[9_8}v& E^#Qw&ʆP j)kߚ*Zx ቇn-2-# ڹ)Uh85J Y[s(5.t2 1ha\8;ݥyH(;F nrC R;k=-ˣxij8$ nLËXHȊTZlnwj ^UkkL1b6)ЙE%XX%sP^Wy\;YlЍ]̡qF)ȏr}A 䩒T|az,/%6y4!o[""jx yynzDy~:@FRx|zFfAQֆ#ב~G$|ׯf Am^$WUK?WZzè1Ud2roV+A(.$J!f@0d3dZMP2u52XStFֲ!33c@(x'{4q6OV2W 9N- !V6 t_bS{'Nx܏hoRu?|}\2J(l;KEַ$ẦpoOk])/9dJHV\lMY+(w@f0H@XR1^ʎ %Jl  \C1%,*UIOaBrI&Z4"\4y4XOM/ro `lǦ1^ IXH+kwVʮ\sJCW 6+;p`;a Câh)m$9L }kqw OmnuQL%q.S!5>-KH0ϙ?{,Wi>=zo,tTűPo [Y4/Ck޷@K8 1@ ?)ˌVk1D.jiů5рG~(Qho s zcd E g @6p;866zsWrZK'3]3v1JRjB)!&2/:֒CcWg}\b, xb=8]]p˝@W/j.q5JYI_yWu%I*Wn| ܽ|]{TҶ[EkefV5T>a* CB9ꏾH dyoL.9鮯Qte]8BmHSdŅ5qق7%A6dJ4pe fv%ᧀ;c ͡*p6Cb)x;n-l Ӆń0o-+ NmxohgA^^j JE)XvgU0ρp`71D. Y y֬,!]]`(2[s^s$\\%`|f>v>b#Ju|1 Ⱥ3#G&*lcw{ Pv 0B ٜ@u$%`D|0hoBP1E5l;bִ3/>G_{`b);t'hW[t GcVd6:b=أqŒxlVk /*V-P&*l( K+*-PhrѭqH;Q#}Wg{Kk% Z6҅xbӴqCD(ڂ.ID +0VU+5bu)Fz&Cun8д'Vz v=` O~tQYEfA-AԷn_TfaktᕚtZ!BJNuPj)"#3n[CST]W80a0<շX WX xʺNNͣZuFDvيBj$¼ZaHw͚#@xlG?1ʴ)WVx e]; Hw]T(y q6t<=~_W)~"}i7M 4}pBlUmluq{C|ڭf,fhkJkW,;Ż̄*-kmʳq;TUBcA,G5Rx2C"F#=c00(aSӑΠ QUg8B7GSUkl"/ri ʇX6@CM{\K`e/!TXl(t-Z - 6d?Ph`[/ /\;TȖB!nQ].pߙ,6ĔDBL=e@x`U{&, K8:[/);Φ9]HĬEo+b_]X+UR9ˣ5_dO0X ؕzc[~K;ӵ#9:;cKL<5Nɣ*Voh JTs = w5aȆE^Ůenil7)hWp9|nZzb [[o7c]-o(E W\Qjb.Edj!qKt<"UQ}kت/Xh:m7Agh.EAY-ֵB7(z0-eD][eX'7il#@+P#\ rT6ZnBKEuU3bC]t2!cX%* /ΞGEPG ꒴Fq"ػB.j0AsrȠrڳt[j{ ÈJQ#47-Z9ͤvUwM0vVLӯ&5Ce + aZ=K`n"16֠tK{W״?(= cVCX2 \)l[ 9΁SSѳUXҰMcQWK7XfSNmNW :MX[D}&PUE&Ӌ|Mk ڝ?GzI]ThAŁ;a@L.-xKT.6~БC]P Σ>?䉷hk qHndʭuv)h-+;|ðO v8,6eJ1bNRSՋAkiҙ76&VLnY*Z5~|+"Wi #-d>S˺aXxYV]x`tVe1TZ3k% LLdzdf!h#$UFTcp.>bzZ@\)>&EX"LJlYSKPnYYz VFNE]@hR%"R ?X5TJ8?2~ҪQS3ae6c` 'UcαRW6dFn(iqN3Z3Cblt"?J7v_]Ú:*)t"Ğ인C[bUCH:̥ߘYߊw` 5t+IK*{[<nbcR ɡzD54jP:=BM=c%l= FWƺzi(|ESN_1|d81&Њu-b?Aj9Am_p[1AdxE' [^@4jNyӈڦXyDW{Vrw(8/{/)BSɳ#cKujy7j5Qj:Oq+؄="uFU9~5:eUǍcA\h>&I%=ЇcEa-_ 0LCz> !|PQ9)Y إ_jY5-Ua0ak8j,汼^Zl3Š300W{[k@9P1PVپ5@e՚Xb߄DJ%Nl+@.b r,:y!t=NU[z:X1%fZ9BYf^Ab^z,$!1+a]Վ{i`ZnzGiov"R@B_ctDh _X gԦ=j kQ.E5cRk> D=>>ǒvnpy@zia.8~<6Y\Zۘ/vB$hbm/ vhO7Xo49oZAP[NzQC"vY[Ӏ{;1ˑ@Gj0Em0A{^M*NA0UlʼMXzmvtAP(Llv7b$'UwrFKCmA8t 4Y} ya_-VziYXSZV3aZL/ ir:QjdoqczZ`a\cB'P~ B215c@F` /~cv!+WĢ*JoУS,zJŰEtB-~P`]56pP-.͗JamZU3aZj:~%d r 3va7}.w_Tj8tt.@ (]\v&́@㣘Z7y+!jVwG1 ݟ б}R]V< [j&V"yX̅Y\[t}&-%4+VmAmO%Jc1J_Ayg)Xg"] )M.44|jʌrm79x}.p>Fdy<0= 06l *ݬىџ96CE}pAc/UeWɚE1tgpc5vfy3`՞r7TŠIC}.y[+ZYe@Ymg8rk (^N2cOXXUV"ۀSv+uKh3\bT;un FW4v^w.a̳ /y(9){G5pmƪ%_STd5^\}37!#%jyI* ,IO1Eyc(ՙg{Qf\{Y2`ZXDL( `-6L@3 _ɗ^%wJPߤf4䄣0͠1 z-MfvlufaVC.7F E9 GuPJ4 Mz^ %G' w>,1Yk;4+ADꆋ򟈛28)DF ދFi˟4bYN).VlN5va()>ѷVJw#1,e]bƺN< FmhU'VRwz͔Z10ez7pEV[ش$S5dgbY9{? ]utN isq @$EXQ=yEX 7Q칋7S)T&3}[I*ߟZ bW kƞ=F {L5 gEwa2[PD32|c6Gf{BtPpA3Jbds!q1q̌vKFcJOu * ,pia./:j _Mƨ5q"0K$Pm_&K=YgwewKF yĴz βo`X:9K[_QLJnl3wdM$"1p5O c8p*09JS92US b3DyXrs$j`-u cn;3Yrl0ɯ1x}=~\JPwYHc0I>;4Z_Yټw/Ee^J`2%|W+WGMi84du)+e׀X|gt9 "z#`9Wد}qp gMy~nx!)]qۈHϖt^c?A"L%9Wَ^]j1eޠ*FG]iP⧃}s+ڿɭ\lXc~x B`D x{$ep/UMuӳ @[]ek;Ve;OitLDjۖߌ6tԵ narFHqnr%s4VH01PDӬk^AXz5fĎ^ Apd0~n VS,I-DFޞ)+[FVfh:7K(V"VQ{L,jA5̫0lO(Օ7%n^?#ATy#0Nc!Z͋Ҷv?r)?1dú(׉Y:z,kmNkQM(X#a=0̧ջCO$4(UC XiT;[8VdgBK3$`~3gԶ\)z?-2 =x`\k޹0w[{S;D@z.^tUdhŧ1/`Xvk՗! /8ΚIJ01<62zћOHkPM& pN&/_,JZZ"%HN]we_ء;hj6;,QwWXĻfLy6Y1 hL4ttz?D4e.ͼd7Awf+n`6Gs)yh5CKMJSI0JQLVӾ);zܢa@9/ U &ҝ6xMxDb_0Hc1$zCZ:3Pk)Y+mNofVf9 2G4KPس\r@7W/_%N*Z KQG? )(`^*L|NȂ~bc ʎLUֽ_W&Y}v0rd}ӏ'2(izw3Bn<2j+մwbM8:DZP;K3+*j}E ƥߒE;Y*cr)؃,W~"^|DP/$#`}{Z*GGL3!+L1?r<\B Rld{UtZpc6q.({>+.l,\YMϛnzj/.('>l6u-w#5SpUpe0Ҵ8[y=Tr0E K9%ȋX/[Ŋəc(\䭂$M R"Bh34Rc ]v 1QJ. nV\.WHM ZV}gQHUT< !AཛྷDʺw@)V,֜ W墡`_xj#:s+D `Ԫ]&G7Хg'Od1i70O6cP X BR,6pbtt(!sxx(&Ĵ4[f̀ 2  5WUd}eL2*! /F*Z?KG\!&907Er›ͪ !ľ^_Wgf<,˪|DnUӗE0"4#ҍA0`~a8YX4M!߯W$,͐r¥bn]\.)\:>z*,+;3f.tȢNT ;"<.Ǝ-맍 ZdUVEy -Māe7.ͺhq"ƤsWxn aj7K+7_tMEy/c]>^XV)bگ4J7mʮܢ5z ZhVmĩ~2 } ͋~c8 S)^KYle7Yi~r rIA4ۤ/\e>*\YVRzҵlҀP-a5ퟖ pST-/*@r8/Xbx/=|EKK9Ǖm=M=sGjBاF7w2ǝEyGYLd5's]4Ub۪C̕ljgLvp{1Q(=&ue^Q as|Fe85XFcZV J0.W̭J6bu)/eБ??0RXcn"BuRy*!ȡt^.#x@Zւ1Rl}cN-=@M(UūNYل,m- 7VjEpjK梺Nt5; F\!NXxvh,Zv= Ɨv * &{`M hE5|R w C3BR/Q~%wlŐimkif+*mdj{?~}gW81g-7PҀUl R",֕h8u)!1__x/K@)-fb1QhjfIVlh|]mf{V@%-p'v~!C.q[+MjNقŅz :QߥCa^ ?#t2(T%@ Z~#7"ZbwUqHԣh m"`rUC>1@-b ClG 2!5IY]uT  7,;9CP.){8>T< ב1v "›hȢbϙre\MܠȘ#+Xaɞ-]Pg@"h)X98 0lp^މvc`9|lc/X0l0s3 TN0i^[t@tDB5x҆:YUK>M(e&柌o2=QYVpqE$,rRp3Xt6WMG&W Po"y{-=.TTmԧ!->VZq_EN@=}&afUf`.8aYD4uJu1e/ {JV C5y}ᡊ”1ېw M15~u=iX;Ʊd7&_J|(wgb@4#n_ EG)O 5K)雝RzG~bZ4zʕ"[ǹ%Jqq+uEz'SԶ hBМ%Wq/S^&sx5?컃8DWp : f~H6D㤹j$SP 6K SL:&sm1*TC=PnƯE+ C)6juOTޘTrn%7+7K]j5q. G͝wS{KdBW^#RfZPg J.#cSzUY%Ũ,e X'vT.}EY:Bֱ#!tGp*Vb:z/YLքdzP`{`Ujq(WVc^I@?yY_ ,q]ߗ+ש. );u*+e8]Uܵ2'T*U);Xlt^p\CU5ܡ\[Ǣhۺ-LuEwl⥁- +vbY.AL  i&-;13%;PѐW286B>e@[i?p?B}6y!,|R,YcA͓Ag c* `Y}1ND\Y]m@X~RJupSbXe$r* .`)HEUxEӨ#WܡZ4-G"Z*eu^-ㄪ:5ݼ]ON;9)Ҹ7yW"?[ r}XE e7xg؏}!-@A@\_Y" ڕ@ߑc:ءe⸟} %*xeo$&5p0tLjHTT߯??Wqa_GGU3HBnaŒϞ1wj.@WCTiA@]Z{ˬ2Ֆ9v EUV3{{US))T)̩p]/Fuh(k[vhBCJK5 pP)P:" ֱR7T ŽOB e /4l뷘{ލqW^kД;+V[LZ ;EpE5 F%pEš U #eŜHq/t?x"8VXDNS<]VɺT+#Ri4xI,e^OI l F(c 1]GQdKϢS9.'f>7DX;LE̺c!HESIMzoh'"ԹE։8$it#\ Hdв,_b<@f*&L 2zA@[Û9Db,1l`}@0  S\ā[{GE[+mfu5b i~ػLyYFu^E7]1*iU☪=~c<M?%ɼ֮\i%b s' 2|>5y8V)㔾#Z юĬ'*<9V#yЀ4Aė}Mw !  wYx%Y.=IV|޲ķJ (l|mJ^4c/5vO0޺Nj} [^"j(2mw{spuW~p**>:C]5{D89[Zk x/)B-](95hYN4u.e[H;튅Sx!8, VV(. AT` X40HGR\R3jgX0kr޴:`(֛ H7b[нR^ DLYA`7ٸa;*O3N@fڬgفG(Qv&U1iYwuj~DGyԚ!jΐ܀;b_xV }^^8^biSTh@|`/z;`{} 0 ̍dt:)ObrCdAJ+ȚpFdžbgw,B[bjF$ڸ0z_eorLN!|>zU[!<%('e7ZKs}OTLU#.Cv8Lr+cBP1dUBu+2ꚺ( e=`@ ubR?R1fjoQ2v* epAB9 vub"8:R.EVcO0ȼj$UQ0% {\-ɛ!lP>:EʻT8G_RV*B\B6sKesмB1̱ lV߫9wX2,]R+ a6mn>%xBn'vXrDV6 6p*/[4UJ:nSFB4 s+}3-]uy<@Iߪ/Wlw~eM'HjŨLJb5]= Xzˇ=5$>D82.x~c@wf;us}BJV)ɸWHלּB,mtO_ܢ|YpƸy jΌREI{vf%šzׇpG!\e Gll_P1_*ӓ]_P̮U5yA:qk1l@|hS,)oB:JTOZ5H#qsQq#5M3~уu:0}j{SovsXv^P8^ӴNi5=؇0E~<.WB: k3KyAmCyx:OA}Tt:Vkwҵs,X+O1e\ʹCS9t1Ns*fk^W@0*/b=Tҽ"YxJ&sنAWЉkN/Mݬ>(8sZ*w\tô+pAcEɔ:ՠ[2hţ/ /4i*,n:T)gdY }%a`lT,0 N`hOMwBzwFDX`NL4=Bc=%g˃B];AS\+&ͧz*&1/fWew> ,H&Hs>G=@#j;Lw-4OZu/ *ªV q:},ahM:KCxl<$sh,=f U~\_0u i|с:G^yhPP 9:ᖪS1ye1Za?e*"4Jauj zřFBj0 %^mZ{gA`]:w{q|OrlDoקxA SÈtAX 6IE-po&bXrYWUʛ&VXKkUЭٌc#= PPYz(][v;TiÄla]bӐcnQt\-Yܜs H {W`qk WM6yw*":p=)⧦729nt ~/{J1 oЈ2%~frkل6ȑT2*;1~a2fynQ2!v"]dd,~LP!:BՓZGG%m9S)𔷚E_0D)ŵ72Xp$wt7v4*SvpTZYc ,$_xUcJMl«0x:xQ *lZbu3HҡDDDL1bːZzYbռwA*# }e3q BׄSд)ۜx=@,)RUD;S(^\}*ڛKi~nTVQ9u|΃o_,p"G=INrl &Va`{ TT [c8ZleWnཷ6dPqѐ0Tz cXH)hc2կ!]qBIט;~Zf.E6jk"*}3VKZ]V@"f(Jn-6`K/_#杽1[,_鋓tV)X*-u XjO5ivcR|J)>1E} mWpՌҳxF khPg0l5B/?pR,.莀y ܦ'χ xS=L$j8O"H<1 4"A: _a; of)퉈7! "ywf#R=LW:Sd4Fo4|Z]+DLi߄8[u{mz]`8O~+#^92fĵ-@J Tx((.X96UBRZ[NUA1Pc_4ca2DY Y\6AM()`ʖiLŹ`&pa{Eg0S} !Ɉ,Kej-h9 ɷv]%ؾO1Zj6% ɧh60ĮUAt4~Yz[^+lB'h !+nW[1]**qtʮ"d uCq9B7̩sUB ZL^XmvHC`1k35BvluU26\_np5ƥD)!JVSWt?PIа*N^[+a4Pt-]c1feko:>Qc&Cs|Mڌǩ^F@Zp4MdlĩlQcA:Lz\+&F#Rۺ5OA]mcx#?Q=ӭ ޗ1 *ƣ`=&K+?Tme( / -!O8{07U'q6x m6m&oqyF}ZbèKq֚D(ar^t&2ׂzwDVЁ*x!eȮ 1 MtA`vo1`BD=@=A5\ЉD&mpۇTj˔"e{{؈P1~- P+gRMP@[Y-hK'>| [`3#k˺%oYRd~gzJa%20u xp}CbjQ`:?1E)5F bBE+NJv"q9˖"mפ5e(a4 -؅@=W] XZ. +!k3_$ẕ9],p.t-ZW*h Z\ n'Xk!һ (saA5j/wuAM(oq,8 ѓKQ-Ќ"\C# G@[buIs`ŚdTGu*m!J \$t RA@Db-&D#H'.%2˒6ר4;a*g&$_F1,KEr%& Z =}`2KS-j $"xEQX^ _(0-Y{*Hr (gqFw{2D4 KX[K`UPTk%S= &Z <|᧓ $P3&BjYGwCzpL-a;wh^)k0:֓Mo%LŘ)L8m\x]OF4WcEuSxTHȮ! ҶE8LD;I, \1Y,*R@*9,Uʔ,D$V6Km ՞(ۅmaje.7"52=O!ٚ7GIHFV0 €8V_gs( bhMqֺFRJ!폴A^FG,<^KJ*.sW"hLPiޥz5"W1#r^F;,zh4@'Cn(lXHZ4ܺO 8 4>#xqJ+y  6XT"gzeģQ%{_H`S )Nr,qk 1dLJ3e=/,Tɓ:rc>ي(hczNr0AX@dL3 k(tH'@=Q o{{ Mm5 / ^u͓%VdH7WZNǔPDup{B6Q r\uNUII/8X1D5韈W (mV 9[{ =5 54=k}[oN"ʀ&x0ګz0#Gh*INtH QKF_:уMLLSa2RVh~{Q!*eX~?jFPAmvgl D_s{B^XڻC^+B%ErEx6%4Q"lFc"HavĊ~i\CeeVȣ"3(geUs/iרJf#lȱC0AR|¦0(P[ex4c17.)*c( c5WHxݜYi\s[ħql ^e #:U"ˡ,df7gN)wnhF?O|dnԴ#b0[ !ՐTGuoMlV9|wNofø0:ě74Kgp5Sshkp-}m[^#`p1KձXjڅ  q w- k n]X c=5Т8u]Cek8fij1A%\Av;ٿ]:WW+ʛ_sPP} xXꉒSLҎ !9z vE6iƧ3X.XMt'n()B(=S1̧=vFTd^h|oVtG$X!`zkgf]]k^׀FkڗDZ!E-ٹnl@T%Fd*)75YW!{ JgDOs@]>VH zje|wHo$dJU9Cc /ՋLyN|zR8 fV`RV7y=sX L8*AP^3OD{ex*#zQH^LrPjUK/Tjƒ 1IS+5HCX>HKBuh^z, v qNeF` 6XNsb8i`YCky/:O KF% /uy\|xGyD(]r-/l\W&)U~.zB OyNh"k^&=3jBC% {j\dTˎ.Q2x۔z9J jDԅ8>RV@q̺!"_\|M>}u˶2Y^PGE4x -9>Vݼy^7wCyԩrX^h{6liWEMUDh=>("zҊF:TjDZԂַdD5ziIx( Q]Ap{:׼8n}z)3ػa yߦ71,L[$/t+)?r/so~9j/S0s;#BO4dvWo)w3 wIņWDoepyqz#a65 7U9Ξ@iݏFZXj{fl61Nm%]:pctOX#tP<*pꆦI/ a Ohon|?f}U3i(@>T/yg1ֱ6O@!ڱm۝ۺY`fG>&*ecR 1ZLoH: }TLʼw& 0JAZ2<l`TTY:È8Ԁm(Y)qTC.lY0M [Bf01h/Q }{!PC #"6.Wl?p΢8E s>!cO4+_3rhNHrbR, kczvπ(QĪzS "Y%>iy1MQAh-=*WPjdp}: *s]u i^l,EEfřCljZZ2@T< 5FmѹG +ϟb1]:R!B+V2 :֫yndJp9/"(DWs2.YE,(0J[Cr[nz<* At z>=/-Ǭ l,ui/%YԪ(_.zuk#JFr/SḰA384uSY(0$,Z[IaU1iU?eQ,_s+?-@Yu"AC[f3e̢3aE#Ԋpd:n.Ga_c[wh "+!E#aK;l^/:Q(Ȣɚ-ћXK6HV8l{b1 m*OIܔUVxvHE=T%rt3L׫c"T^F36yiu2Q$v\G0Kw43 C W^$^L)_E<~:OmXe.1BfeF<7l6J쨫#ӍJX&Q }rCc(%O"W+I"%b"zyK%D fEf>T)G/vz=J}r}A 2S V˘(y @D -Z+LKGt~b+e_ʟ Ԧ?ro1H 9T @:N%]gmP:wh:kPL: ^(6x0K V1VBNi*yn_6yv8Z(Svf.*ztT\lQetX1l R_F!Uf1 >™+SX)xVVFT e 9|D4.!UZJǬjbJbc8ܽdjxT(  TxU2hQq6k8vHu9z$WVũ|G1I4Sq,d66 ҟ(\%e4j3ϸ~b+-˔-h]ʯ/&c!%\Dp~ ȡϚ(*WJ{E-4IL+:)~'B"4yz7i8Pxb''s>sc[᩷3'Q;EDFU!~/\Nѝ7L/ `NX B#8\ Yp^7=+]Blo>6/g0N}bH]iZ7Unk6md,:sz_Sxi,p3Gտl_I5eSmW YMcB踡1"|B S9}Oܤ:S K/8@zk 23KGu0_XWh}e D9-Yzʰ6+TE,~pU|ݿ"~34 ̾eyB<\ Y,ܰJpz@n6tJ]0'ߨ;=w,$!zo(*+Ā0* eQq?gu+=ˈ5~ĩ"S2U^8%.R]slS~!KʃJƺ%^|c+^댻KB.}&pDcԺiWy@mSlS9`Ppy,x.HÛshWSӗ34讼t,SN. (I AkQz9 I4Z蠻٢J~U1WFNvG?*eD*U>b2>BhwmC*ςU-~D({kBUCWJ*viUGOH!hgڿ>ס&}/k ʕ{i@:w;0-2fYmX}杮3D*st}(\v)Db'Dz.P}H9+\ Mڻ uL ҷKR)r-^*Q JrEwnYr ޠ%j5*؃JvӼq8$yPAv=બޅ;m@kճjFV6WI@^7b Ϥ4 Pp-9j f ai!iܺ/dQwAg~KT}1V';.bF >hr To9j-Y@X.a>@hk2Vj1CGD5F74 #(XrqWYJXFv#@ynN@VK?F$6l{83=:vLbl5xXD磲C[bK1ުqzTCwyqmSrSw3 e{6gv)4S6_)\=ye8qA[*> /9a GoJ02HSY9 h%LP+ :7+kAgT-+*7^qE0j!Uw킧n560!DkPUdUs{@Zbܷ0Z"ĸZ1,&}^ j Kt({` =^* k:U|^Wz OiPU%"x"].fB bsiQ^Yb%2Rx7**9Li 7D[!/TU̱j%g^r`,nbե;k2 чT̼"^aQMho"!DKY+B#wJch(]HL GϖIy$3/*;P!C@ݯXER4T[+yܙ$*vGXin2uFE+I|럿SG;L3v8bl J8'XדDMrx $`fGu\Rʚ'r";K0\4K.bbEC pa.%m!HK}&wW6E䊀dِwApjG$=" ez R=8kpFW6ˆd8D$VRcfrlAzAF55.CfZ\q0Gf-QtK)*5RY~E݈s QW *꽡T6&a^ r(/(j;: @l,*@Qw/hE;KE V8*v($h OَZ﹊mH"n`P\ X&{A,[tL yTS2Et|LU(N̬1;N"I92?O:6޿@] [\y⥖]K7 zmvHy;SÓn el4 sL|x5XGJQH}8+1)."(a2fN0<\ARهPPut"4:"x_aNn^ (첇o8S{{F B B׶xAƴF`li u,=ay,Kg ?"Kv r:I/=UI1{l;.A\.:Av1ӑX GՅۉ/bP A48^oIh%Zet+G)OjD}枈3U a6k9".cZ+5ztx9cHt&I;w#HQx_1a4pu AGaz p2C7B1BEP^ >T8_?B+h_h?mD7QN=ԤܖEDt5M2C'RͼL`^-yŰ0|Bw~Z%?-<eRtnX `&dY/v;}Gr!' CqONޫco iPC0T9?LKO|+׃y &1^L)u> MsGD،'Qqi|AV @ӎHP{@K{I>k剖vS<(I{:RxHð2h55'@W} ]uNF t-y]R@zIFS4sVˈzoWUC4]l.Ťfc" ag EZ9a6Ę5[hYXQtYH9DU(kƀUMѶ0Z(Nw=NkҒl*z~zf1c1H4* Q5Pfߤ@Ų.mo9F< > amah>G*Y#ɋ0MoD/# CozכT=-(9`h^,;-op@3`ZQ!0{Y`: s+xwfǬUHu/[K1.ؠ*j@c.crutq#F_BhE)Յsc!ާEAiUjȞΟHL\!2rs5X\A ѽkz#$;Q،0'RMHcтs(>?~~ĠQA!Sꇬ8/ 7Mx9G%Ć6uTi!O3{K[`F=?< 4Coa {StCJݎbcj5u)U}nQV[oG7*`O"3e_E:~_ڛ 4dgL*hKAG}"=K=o.T%Ju)rC>ۄ [ף- 0%`;Zbu/T-iO4|Fȅ uGzL\v~ڂX#v6"خ;J'H 3ȮsCh1\bhu |p;b*QVt. w>G䧗ծKNU7o8Z]ƀ -1^y vU8pjWG(0^;J(y\ AJCD(0`W;œLÌW>% %>T`GJ;`5Ogg6& "k-ȡa _.\Q-Ma%fْWPU6ш=bFsY-?"UIpς^0 \36Bo4Zl.ޯzVXuNS*hX3LxzI#o ½x>Y)ݫx%\1Ia2vUg/ՖR:'_x"F # y>)g9Qp`3b9#S/J>wQŮ!]'{yl,ͩ`,#%F8AV&1|c}K,s(L\p;`^+N( 2\{'jEFbx^fdx@a>%gN>(H3j[k.nAba|b54+h,>4Z t&f겳mh~bDzzN@3NڮeVɃ%pWgl;6LFM[@/.RRG]]#'psHX4Sg)pضN~ˏ~~?Ib/G l瓽h2qי?Ph/uյYEǫewKY}Tu÷j^֚?p\Hgma dH+㯈JG~~43/Wf=GPD%pųTׄB/ai++|@\-%ǩ 2P똘2yK91eylYxt[> /)nvixKgE6;FԨ!_Avסy|L9KiY;2|}0h+gis-e߹0MA!qL䠷sb͚; QRunT6+"EGf7dZ)-+x1N ^ԭS0;r >c_p}5[ UCp-(-ކ р` GHLA灕TX{wedڄ u$qVti[!t~Β.MJo(V:KBok3䁺.Prx`[Y0F^6@W0n&D̶.};f!`_h_uB?fqT?yumvːEx`0Pa"vhP/jj=,a9}e'vC"Kq#eQ`#} Jr!ī@"rK'8VVd(]e/"=?.jXZ[ p7,4Q#D0^$u.~9)c*-(URZf N>C괄G 8y pl2tۋuBX%]pKkP,)~GZ?z>lEv{aQFæ/G*9Cbt[Rp)]ܪ e{YhM뎕+S߫ohmbҼo<-;:cKjH}KåS䁸żoEԡe%ob-U*q^!A [J#z:e\\t?^bx# |y(*m@mvO(PkK&Y%Uy1sPRkpX(ՊЊ@YHbwF.y.% !Bۉ)e FzbqYm/WQW/P2l4=R)QoE s+ߺ^giۀTVaƍVlJDr^zKN|_V(9`)#)KNCbpzGB@v! H,./I#z"jDP%KFRdP[Y]Mű-uF`lС"LX)KIiV)u&%5M qD4ư2%ʜ Y ISED^Z_RO fuQ.XO^eDraE֐o-`CQh86PCehЅݦ;Ix/ ʫF~>* ńPuBPIK,2 ZzL.BR3rd#Oo.̖DnL"̓uŕfVD?i J5@-wK:\ĦҢ!thՌ_f_f_f_g69n)yiify]A^})wFc*wwü ]!;i^_iB7M=`Okl= $p7݈wE"<งHk*/j# 2i)\| EQwÈ2ݱ̨)w+wMTéy ĖL?lƣ*~:.F,0ȼvNX/f4{1F;N2* Ì 9M\UU%JLK!0R^_@=f8 B.eFy_"-;T@XdrXl+sG-LIBZVXZ-\Z4:"~}GJO}(7Yr;r"cECόua)g;k(jk\0^e&A"*|uJ7tKP]beL\%8*uuZL Fd)Ye[-5RItX8*NW 7Xy1r=q!,jAN1NPIm{Ztf@G–.LzKȔ }I~hA-~b$[ x aYUd"UaNiC3,@X /-:AE,#J0Q)&LF؅iMJ51URWP ݅% x0C8dEx< !XM;,a +H+$)PtzAo >c¦ {?+0,h]V^2`OZ0EkWG~ Wti(ܸhA0qsh^!:]^[v6&6Q]#7*eZ]u4F6VKU2 ZSlC?+eh%9*zvFXAʑ.z>NнqiAp Ĺ"-J(:htv5hkثX% #p ̾uuPq&#yKjA~ICi}z1Z ԧjZX""%'BU&ETS9~%mܶZZ-8#80Xy0:n- !5<:P.T_UǶ эҐToY܋$c OrG/H(4Hk12IJJL.%b,^zS; .DPYZj5C)ܫQKET=]] EP k <O?eXNvc Yd5[Ȍ3`4'B61NkF(qTQrz X`GucZ#PZS Ld#WehP hR`^mp#Z$(#xhEiׇt±H ;x 'xR2b `bˤ sTPBej +1n>enŪ^eaC_tzӸue}Xn-ՖՖwQ%YnyHl0zR~in Eັxn0<@s5;{a )]H7#N$=f$*Hn/ Q@tU(d-K  B (D Mg$E)@!Q@*C Q-,|%!Ro#W/8< TQ 9#-94RA_WT[^{3 #$߉MNqW 8P̝O_AAGs̤770 [&$<ցE@)U%?U[Oקt~}D }!.@g1*(kY|/AEve^{{Qc@GNbPǯ/Y yF2ʁ \~=oH~`РhSE\CR(S7t`q,"Qa1A{[ܫotT]ER*EdZ;)ӯYMV6J6/tza3,򹈈nZ^Wȣm[F!Y~*]ξ>E?(q<%j="F7&ML/%+InnQ nkO~!g *T%f,Uxs-`_?C/UC5]*ʀ'edXC!>`%@/ڎo8/pFdY9bR/PGgt.@0q%=VUzϰK}ߤ'r]9cڝb9 =z}Ti){d̋LPv+yIpȥ11(R^^_ynyny~V[_O4W_{u{Ŝ) ^Vh.ynyoIoIf%A#2-2q0/;9ǩ$0e/V:B(ukO +*5HWRw*ɾ̳>&E?ߨ+Ծ2CGu܄m$g??E"xh|@74}xYkqЊZI> о,2K ErlKWw^y@DЎ"ZWKE,E#q6Jd22ޟXڲ( >h8"RzQ`ijgxsCvĤuJz0J? ˗.yl[----:4F#7 APP(A."ܼ\V mrIlE:R/^/Z^*eQ2ОTcKŒt|H̺{2X>]`#QS;U[etzD}]]UFv"=c9ysPHxf3|Ofl?ݢ=UCQ@YQ)>t)c(6mDuP1`UMX8%Ĺq\l[-'̣EpM+:YywQu9#X7-~>˗-e.l>6Q6.aiH%*2)qkxʊ7OBj{]F43֌E Vhb 3W1Ndm.q.^̶'$lѧ8- sYǬ=X&z1h1)Qn2~u剌TO+/9Ҙ1b\@1&r˗.[-CY,._ա }-ǘ<'us~(H0w'4mımJ^&[{Z@ӨS{b_閩Vz7gebFƘZ\wb(AbۃxuW.|syQ"F#\ B>yM*n ~2U|1?57LwYWڹ~&Ym]]/pUo}%vWb%%i[-.> ;P//SJu}ӼNmJ}ҽ_t D.tW+@/!VwwwW+~WowS~zߨ*zWr 0H$3ou AZF-#b"4g J=i.mdPBM􆼪KE66c$hʕ7c4\w>Ű0=E@_jEUC'2"'@7-i_u!K" Y>X3'5o$^D* /9lRjFCex%؛c/hvG_ 5Yr"^mTj63CǴ@'Y@KYO?R}?SA4JlR4&@3OSqn\Y Pa?.1|5,xa;_=bw Q,ת̠hY&+8z̓ ڀz`zcCI=\h^"9^OczRsԑS*sl_ox`u@wMʢ >p O,:-G&vC~M1 ֶXG߈Z_Gք*{27oM~g_}+1aU7t`cbe;$X`fC݆!;=J9/[:x?/yig)3cW-^ȑCvNVq7?yk5wAb3Ozٍݿx(,D(O&g\mKà= 7908ndXQߟ2 AV־?2wW,Bi,ǨVXA %FxhZw^Ůsȣ1 C!@-/ d"X״R{;9geEvY ,NXbH9/ AFJlKکEBرkK-0=! rk,,hvKz!<NN_Ypg:6ţxdaP^b/l]"c~}&f> d%J5ak>\SsJն l  3qy*\ ͨg^n-57\_aMGR.c<+,]윧7zDbQa=:%ceL.b*14/;WCx䣍(5OYEǴ-˽3,u:$ ر7"-u *(aI%>E0Ƹ P{W~1k5јr2=P&s-̲T[mKRS®!'A ?žeP}Ⴠ: u(|ZPٗ+ڪTRJP. /%6^""%񩂱0 @:LP"^k̸׈EHRRPd1@B}5~xb 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 230400 1 44100 Out 1 0 0 0 0 2 1 1 0 1 0 Input 1 0 1 0 0 2 1 0 0 0 0 0 Track 1 0 0 0 0 1 0 0 0 0 0 0 Track 2 0 0 0 0 1 1 0 0 0 0 0 Track 3 0 0 0 0 1 0 0 0 0 0 0 Track 4 0 0 0 0 1 0 0 0 0 0 0 Group 1 0 0 0 0 1 1 0 0 0 0 0 Group 2 0 0 0 0 1 0 0 0 0 0 0 Aux 1 0 0 0 0 2 1 0 0 0 Aux 2 0 0 0 0 2 1 0 0 0 0 500000 0 4 4 0 1 muse-4.0.0/src/share/templates/default.med000066400000000000000000000034211404105311000204530ustar00rootroot00000000000000 1 1 0 0 0 1 0 0 0 0 0 1 0 0 0 0 230400 1 44100 Out 1 0 0 0 0 2 0 1 0 1 0 0 500000 0 4 4 0 1 muse-4.0.0/src/share/templates/midiGM.med000066400000000000000000000077221404105311000202050ustar00rootroot00000000000000 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 230400 1 44100 Track 1 0 0 0 0 0 0 1 -1 0 0 1 0 0 0 100 100 1 0 Track 2 0 0 0 0 0 0 -1 1 0 1 0 0 0 100 100 1 0 Track 3 0 0 0 0 0 0 -1 2 0 1 0 0 0 100 100 1 0 Track 4 0 0 0 0 0 0 -1 3 0 1 0 0 0 100 100 1 0 Track 5 0 0 0 0 0 0 -1 9 0 1 0 0 0 100 100 1 0 0 500000 0 4 4 0 1 muse-4.0.0/src/share/templates/monorecord.med000066400000000000000000000060501404105311000211770ustar00rootroot00000000000000 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 230400 1 44100 Out 1 0 0 0 0 1 1 0 1 0 Input 1 0 0 0 0 1 1 0 0 0 Track 1 1 0 0 0 1 0 1 0 0 0 0 500000 0 4 4 0 1 muse-4.0.0/src/share/templates/synti.med000066400000000000000000000124771404105311000202100ustar00rootroot00000000000000 1 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 230400 1 44100 Out 1 0 0 0 0 2 0 0 1 0 organ-1 0 0 0 1 1 0 0 0 0 MESS organ 198 0 7c 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 00 14 00 00 00 00 00 00 00 14 00 00 00 0a 00 00 00 0a 00 00 00 00 00 00 00 0a 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 00 00 00 00 vam-1 0 0 0 0 1 0 0 0 0 MESS vam 199 0 7c 02 01 00 20 00 00 00 20 00 00 01 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fe 3e 00 00 fe 3e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 7a 3a 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 Track 1 0 0 0 0 0 0 1 199 0 0 1 0 0 0 100 100 1 0 Track 1 0 0 0 500000 0 4 4 0 1 muse-4.0.0/src/share/themes/000077500000000000000000000000001404105311000156275ustar00rootroot00000000000000muse-4.0.0/src/share/themes/Ardour.cfc000066400000000000000000000175401404105311000175470ustar00rootroot00000000000000 muse-4.0.0/src/share/themes/Ardour.qss000066400000000000000000000037241404105311000176210ustar00rootroot00000000000000QWidget { background-color: rgb(108,109,121); color: rgb(193,193,197); } QWidget:!enabled { color: rgb(150,150,154); } QToolTip { background-color: rgb(180, 180, 180); } QToolBar QToolButton { background-color: rgb(93,94,106); border-color: rgb(70,70,70); border-style: outset; border-width: 1px; border-radius: 5px; padding: 1px; } /*QMenu::item { background-color: rgb(150,150,150); spacing: 3px; padding: 1px 4px; background: transparent; border-radius: 4px; }*/ QMenu::item:selected { background-color: rgb(40,90,40); } QToolBar QToolButton:hover { background-color: rgb(50,50,50); border-color: rgb(58,167,221); border-style: outset; border-width: 1px; border-radius: 3px; padding: 2px; } QToolBar QToolButton:pressed { background-color: rgb(125,247,131); border-style: inset; border-width: 1px; border-radius: 2px; padding: 2px; } QToolBar QToolButton:checked { background-color: rgb(125,247,131); border-style: inset; border-width: 1px; border-radius: 2px; padding: 2px; } QToolBar QToolButton[popupMode="1"] { padding-right: 12px; } QTreeView, QListView { alternate-background-color: rgb(127,127,137); } QWidget#MidiStripTabWidget QTabBar::tab { padding: 2px 3px 2px 3px; border: none; } QWidget#MidiStripTabWidget QTabBar::tab:selected { background-color: darkgreen; } /* The category item background colour in the advanced router trees: */ MusEGui--RouteTreeWidget { qproperty-categoryColor: DarkCyan; } MusEGui--SnooperDialog { qproperty-flashColor: DarkCyan; } /* The title header background colour in popup and routing menus: */ MusEGui--MenuTitleLabel { background-color: DarkCyan; } /* Didn't work. The # selector never seems to work. */ /* QMenu::item#menuTitleItem { background-color: DarkCyan; } MusEGui--PopupMenu::item#menuTitleItem { background-color: DarkCyan; } */ muse-4.0.0/src/share/themes/CMakeLists.txt000066400000000000000000000022341404105311000203700ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2013 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB theme_files *.qss *.cfc ) install( FILES ${theme_files} DESTINATION ${MusE_SHARE_DIR}/themes ) install(DIRECTORY "Deep Ocean" DESTINATION ${MusE_SHARE_DIR}/themes/) muse-4.0.0/src/share/themes/Dark Flat.cfc000066400000000000000000000200771404105311000200420ustar00rootroot00000000000000 muse-4.0.0/src/share/themes/Dark Flat.qss000066400000000000000000000307731404105311000201210ustar00rootroot00000000000000QWidget { background-color: rgb(40,40,40); color: rgb(220,220,220); } QWidget:!enabled { color: rgb(100,100,100); } * { backward-icon: url(":/xpm/left-128.png"); forward-icon: url(":/xpm/right-128.png"); filedialog-new-directory-icon: url(":/xpm/newdirectory-128.png"); filedialog-parent-directory-icon: url(":/xpm/up-128.png"); filedialog-detailedview-icon: url(":/xpm/viewdetailed-128.png"); filedialog-listview-icon: url(":/xpm/viewlist-128.png"); } QDialogButtonBox { button-layout: 2; } QStatusBar #PaddedValueLabel { border: 1px solid rgb(90,90,90); } QStatusBar { color: LightSkyBlue; } /* QWhatsThat doesn't work at the moment (known problem) * Just in case Qt implements it in future... */ QToolTip, QWhatsThat { background-color: rgb(96, 211, 201); color: rgb(30,30,30); font-size: 10pt; } QMenu { border: 1px solid rgb(0,90,160); /* menu-scrollable: true; */ } QMenu::separator { background-color: rgb(70,70,70); height: 1px; } /* menu styling is incompatible between Qt releases */ QMenu::item { padding: 3px 22px 3px 7px; } QMenu::indicator { left: 6px; border: 2px solid rgb(110, 110, 110); } QMenu::indicator:exclusive { border-radius: 5px; } QMenu::indicator:checked { background: rgb(0, 204, 255); } QMenu::indicator:unchecked { background: transparent; } QMenu::icon { left: 4px; border: 1px solid transparent; } QMenu::icon:checked { border: 1px solid yellow; } QMenu::item:selected, QMenuBar::item:selected { background-color: rgb(0,90,160); } #MidiRouteMenuItem, #SwitchBarActionWidget { qproperty-highColor: rgb(0,90,160); } #SwitchBarActionWidget { qproperty-borderColor:rgb(120, 120, 120); qproperty-checkedColor: rgb(0, 204, 255); } QDockWidget::title { text-align: center; background-color: rgb(60,60,60); } QDockWidget QTreeWidget:focus { border: 1px solid rgb(0,120,200); } QWidget#MarkerView QLineEdit:enabled { background: rgb(60,60,60); } QToolBar QToolButton { background-color: rgb(90,90,90); margin-right: 1px; border-style: none; border-width: 0px; border-radius: 0px; padding: 2px; } QToolBar QToolButton:checked { background-color: rgb(0,90,160); border-style: none; border-width: 0px; border-radius: 0px; padding: 2px; } QToolBar QToolButton:hover { border-color: Yellow; border-top-style: solid; border-width: 2px; border-radius: 0px; padding-top: 0px; } QToolBar QToolButton:pressed { border-color: Orange; border-top-style: solid; border-width: 2px; border-radius: 0px; padding-top: 0px; } QToolBar QToolButton[popupMode="1"] { padding-right: 16px; } QToolBar QToolButton::menu-button { border-radius: 0px; border-left: 1px solid rgb(40,40,40); width: 13px; } QToolBar QAbstractSpinBox { background-color: rgb(40,40,40); /* color: DeepSkyBlue; */ color: rgb(0,140,210); border-radius: 0px; height: 20px; border: 1px solid rgb(90,90,90); } QToolBar QAbstractSpinBox::up-button { width: 16px; border: none; } QToolBar QAbstractSpinBox::down-button { width: 16px; border: none; } QToolBar QAbstractSpinBox::up-arrow { image: url(:/svg/up_arrow.svg); width: 8px; } QToolBar QAbstractSpinBox::down-arrow { image: url(:/svg/down_arrow.svg); width: 8px; } QToolBar QAbstractSpinBox::up-arrow:disabled, QToolBar QAbstractSpinBox::down-arrow:disabled { image: none; } QToolBar QComboBox { background-color: rgb(40,40,40); /* color: DeepSkyBlue; */ color: rgb(0,140,210); border: 1px solid rgb(90,90,90); padding: 1px; height: 20px; } QToolBar QComboBox::drop-down { background-color: rgb(40,40,40); border: none; } QToolBar QComboBox::down-arrow { image: url(:/svg/down_arrow.svg); width: 10px; } /* does not work as documented (Qt 5.9) */ QToolBar QComboBox QAbstractItemView { color: rgb(220,220,220); selection-background-color: rgb(0,90,160); } QToolBar QLabel#PosLabel, QToolBar QLabel#PitchLabel, QToolBar QLabel#TempoLabel, QToolBar QLabel#PosTicks, QToolBar QLabel#PosFrames, QToolBar QLabel#PaddedValueLabel { background-color: rgb(40,40,40); border: 1px solid rgb(90,90,90); height: 20px; } QCheckBox::indicator { border: 2px solid rgb(110, 110, 110); } QCheckBox::indicator:checked { background: rgb(0, 204, 255); } QRadioButton::indicator { border: 2px solid rgb(110, 110, 110); border-radius: 5px; } QRadioButton::indicator:checked { background: rgb(0, 204, 255); } QFrame#SliderMeterFrameAudio, QFrame#SliderMeterFrameMidi { border-top: 1px solid rgb(60,60,60); border-bottom: 1px solid rgb(60,60,60); } /* QWidget#Rack { border: 1px solid rgb(0,120,200); } */ QWidget#Strip { border: 1px solid rgb(90,90,90); } QWidget#Strip > QAbstractButton, QWidget#AudioAutoType { /* margin: 1px; */ background-color: rgb(90,90,90); border: 1px solid rgb(40,40,40); /* border: 1px solid rgb(0,120,200); */ border-radius: 3px; } QWidget#Strip > QAbstractButton:!enabled { /* border-color: rgb(90,90,90); */ background-color: rgb(60,60,60); } QWidget#Strip > QAbstractButton:hover { border-color: rgb(255, 208, 0); } QWidget#Strip > QAbstractButton:pressed, QWidget#Strip > QAbstractButton:checked { background-color: rgb(0,90,160); /* border-color: rgb(0,90,160); */ } QWidget#VolumeEditAudio, QWidget#VolumeEditMidi { background-color: rgb(60,60,60); } /* doesn't work */ /* QFrame#ClipperLabel { border: 1px solid rgb(90,90,90); } */ QTreeView, QListView { alternate-background-color: rgb(50,50,50); background: rgb(40,40,40); } QMdiArea > QTabBar::tab:hover { color: yellow; } QMdiArea > QTabBar::tab:selected { color: MediumSpringGreen; } /* TabWidget in Midi mixer strip */ QWidget#MidiStripTabWidget::pane { position: absolute; margin: 0; padding: 0; border-top: 1px solid rgb(120,120,120); padding-top: 1px; } QWidget#MidiStripTabWidget::tab-bar { /* left: 3px; */ alignment: center; } QWidget#MidiStripTabWidget QTabBar::tab { margin: 0px; padding: 1px 4px 1px 4px; border: 1px solid rgb(120,120,120); border-bottom-style: none; border-top-left-radius: 3px; border-top-right-radius: 3px; } QWidget#MidiStripTabWidget QTabBar::tab:selected { /* border-color: rgb(255, 208, 0); */ border-color: rgb(0, 165, 180); border-bottom: none; /* background-color: rgb(40,40,40); */ background-color: rgb(0, 100, 109); /* color: coral; */ margin-left: -2px; margin-right: -2px; } QWidget#MidiStripTabWidget QTabBar::tab:first:selected { margin-left: 0; } QWidget#MidiStripTabWidget QTabBar::tab:last:selected { margin-right: 0; } QWidget#MidiStripTabWidget QTabBar::tab:!selected { margin-top: 1px; } QWidget#MidiStripTabWidget QTabBar::tab:focus { /* border: 1px dotted rgb(150,150,150); */ border-style: dotted; border-bottom: none; } QScrollBar:horizontal { border: none; background-color: rgb(40, 40, 40); height: 14px; margin: 1px 14px 1px 14px; } QScrollBar::handle:horizontal { border-radius: 4px; border-color: rgb(70, 70, 70); border-width: 1px; border-style: solid; background-color: rgb(40, 40, 40); min-width: 25px; } QScrollBar::add-line:horizontal { border: none; /* border: 1px solid rgb(70, 70, 70); */ /* border-radius: 2px; */ width: 12px; height: 12px; subcontrol-position: right; subcontrol-origin: margin; } QScrollBar::sub-line:horizontal { border: none; /* border: 1px solid rgb(70, 70, 70); */ /* border-radius: 2px; */ width: 12px; height: 12px; subcontrol-position: left; subcontrol-origin: margin; } QScrollBar:left-arrow:horizontal { width: 12px; height: 12px; image: url(":/svg/scroll_left.svg"); } QScrollBar::right-arrow:horizontal { width: 12px; height: 12px; image: url(":/svg/scroll_right.svg"); } QScrollBar:vertical { border: none; background-color: rgb(40, 40, 40); width: 14px; margin: 14px 1px 14px 1px; } QScrollBar::handle:vertical { border-radius: 4px; border-color: rgb(70, 70, 70); border-width: 1px; border-style: solid; background-color: rgb(40, 40, 40); min-height: 25px; } QScrollBar::add-line:vertical { border: none; /* border: 1px solid rgb(70, 70, 70); */ /* border-radius: 2px; */ height: 12px; width: 12px; subcontrol-position: bottom; subcontrol-origin: margin; } QScrollBar::sub-line:vertical { border: none; /* border: 1px solid rgb(70, 70, 70); */ /* border-radius: 2px; */ height: 12px; width: 12px; subcontrol-position: top; subcontrol-origin: margin; } QScrollBar:down-arrow:vertical { /* border: 1px solid rgb(70, 70, 70); */ /* border-radius: 2px; */ width: 12px; height: 12px; image: url(":/svg/scroll_down.svg"); } QScrollBar::up-arrow:vertical { /* border: 1px solid rgb(70, 70, 70); */ /* border-radius: 2px; */ width: 12px; height: 12px; image: url(":/svg/scroll_up.svg"); } QWidget#ScrollScaleZoomButton { width: 8px; height: 8px; border: none; } /*** MusE properties ***/ /* The title header background colour in popup and routing menus: */ MusEGui--MenuTitleLabel { background-color: #004d4d; } /* The category item background colour in the advanced router trees: */ MusEGui--RouteTreeWidget { qproperty-categoryColor: #004d4d; } MusEGui--SnooperDialog { qproperty-flashColor: #004d4d; } MusEGui--TList { qproperty-sel3d: false; /* qproperty-curSelBorder: true; qproperty-curSelBorderColor: Red; */ } MusEGui--AudioStripProperties { qproperty-sliderRadius: 1; qproperty-sliderRadiusHandle: 1; qproperty-sliderHandleHeight: 24; qproperty-sliderHandleWidth: 14; qproperty-sliderGrooveWidth: 6; /* draw groove part above the handle */ qproperty-sliderFillOver: true; qproperty-sliderUseGradient: false; /* None = 0, Left = 1, Right = 2, InsideVertical = 6 */ qproperty-sliderScalePos: 2; /* draw vertical line in slider scale qproperty-sliderBackbone: true; */ /* fill slider handle with color qproperty-sliderFillHandle: false; */ qproperty-sliderFrame: true; qproperty-sliderFrameColor: rgb(80,80,80); qproperty-meterWidth: 10; /* apply meterWidth for each channel separately qproperty-meterWidthPerChannel: true; */ qproperty-meterFrame: true; qproperty-meterFrameColor: rgb(80,80,80); /* Spacing between meters qproperty-meterSpacing: 4; */ } MusEGui--MidiStripProperties { qproperty-sliderRadius: 1; qproperty-sliderRadiusHandle: 1; qproperty-sliderHandleHeight: 24; qproperty-sliderHandleWidth: 14; qproperty-sliderGrooveWidth: 6; /* draw groove part above the handle */ qproperty-sliderFillOver: true; qproperty-sliderUseGradient: false; /* None = 0, Left = 1, Right = 2, InsideVertical = 6 */ qproperty-sliderScalePos: 2; /* draw vertical line in slider scale qproperty-sliderBackbone: true; */ /* fill slider handle with color qproperty-sliderFillHandle: false; */ qproperty-sliderFrame: true; qproperty-sliderFrameColor: rgb(80,80,80); qproperty-meterWidth: 10; qproperty-meterFrame: true; qproperty-meterFrameColor: rgb(80,80,80); } MusEGui--Meter { qproperty-radius: 1; qproperty-vu3d: false; } MusEGui--TrackNameLabel { qproperty-style3d: false; } MusEGui--EffectRack { qproperty-style3d: false; qproperty-radius: 3; /* use smaller non-standard scrollbar */ /* qproperty-customScrollbar: false; */ } MusEGui--CompactSlider { qproperty-barSameColor: true; qproperty-radius: 3; } MusEGui--ElidedLabel { qproperty-radius: 3; qproperty-style3d: false; } MusEGui--CompactPatchEdit { qproperty-radius: 3; qproperty-style3d: false; } MusEGui--CompactKnob { qproperty-style3d: false; qproperty-xMargin: 3; qproperty-yMargin: 3; /* qproperty-drawChord: true; qproperty-totalAngle: 270; qproperty-radius: 2; qproperty-symbol: Dot; qproperty-borderWidth: 4; */ } MusEGui--DList { /* 0-255, sensible values 0-64 */ qproperty-alphaOverlay: 32; } MusEGui--CtrlCanvas { /* 0-255, sensible values 0-64 */ qproperty-bgAlpha: 0; } MusEGui--Strip { qproperty-expanderWidth: 1; } MusEGui--PianoRoll { qproperty-pianoWidth: 60; } MusEGui--CompactSlider#MixerStripAudioPan, MusEGui--CompactSlider#MixerStripMidiPanController { /* better use even numbers */ qproperty-thumbLength: 2; /* qproperty-thumbColor: red; */ } muse-4.0.0/src/share/themes/Dark Theme.cfc000066400000000000000000000175351404105311000202230ustar00rootroot00000000000000 muse-4.0.0/src/share/themes/Dark Theme.qss000066400000000000000000000036031404105311000202650ustar00rootroot00000000000000QWidget { background-color: rgb(30,30,30); color: rgb(120,120,120); } QWidget:!enabled { color: rgb(80,80,80); } QToolTip { background-color: rgb(160, 160, 160); } QToolBar QToolButton { background-color: rgb(50,50,50); border-color: rgb(70,70,70); border-style: outset; border-width: 1px; border-radius: 3px; padding: 1px; } QMenu::item:selected { background-color: rgb(40,90,40); } QToolBar QToolButton:hover { background-color: rgb(50,50,50); border-color: rgb(58,167,221); border-style: outset; border-width: 1px; border-radius: 3px; padding: 2px; } QToolBar QToolButton:pressed { background-color: rgb(30,30,30); border-style: inset; border-width: 1px; border-radius: 2px; padding: 2px; } QToolBar QToolButton:checked { background-color: rgb(30,30,30); border-style: inset; border-width: 1px; border-radius: 2px; padding: 2px; } QToolBar QToolButton[popupMode="1"] { padding-right: 12px; } QTreeView, QListView { alternate-background-color: rgb(60,60,60); background: rgb(30,30,30); } QWidget#MidiStripTabWidget QTabBar::tab { /* margin: 0px; */ padding: 2px 3px 2px 3px; border: none; } QWidget#MidiStripTabWidget QTabBar::tab:selected { color: orangered; } /* The category item background colour in the advanced router trees: */ MusEGui--RouteTreeWidget { qproperty-categoryColor: DarkSlateGray; } MusEGui--SnooperDialog { qproperty-flashColor: DarkSlateGray; } /* The title header background colour in popup and routing menus: */ MusEGui--MenuTitleLabel { background-color: DarkSlateGray; } /* Didn't work. The # selector never seems to work. */ /* QMenu::item#menuTitleItem { background-color: DarkSlateGray; } MusEGui--PopupMenu::item#menuTitleItem { background-color: DarkSlateGray; } */ muse-4.0.0/src/share/themes/Deep Ocean.cfc000066400000000000000000000200731404105311000201710ustar00rootroot00000000000000 muse-4.0.0/src/share/themes/Deep Ocean.qss000066400000000000000000000247761404105311000202620ustar00rootroot00000000000000QWidget { background-color: rgb(24,34,44); color: rgb(220,220,220); } QWidget:!enabled { color: rgb(100,100,100); } * { backward-icon: url(":/xpm/left-128.png"); forward-icon: url(":/xpm/right-128.png"); filedialog-new-directory-icon: url(":/xpm/newdirectory-128.png"); filedialog-parent-directory-icon: url(":/xpm/up-128.png"); filedialog-detailedview-icon: url(":/xpm/viewdetailed-128.png"); filedialog-listview-icon: url(":/xpm/viewlist-128.png"); } QDialogButtonBox { button-layout: 2; } QStatusBar #PaddedValueLabel { border: 1px solid rgb(0,90,160); } QStatusBar { color: LightGreen; } /* QWhatsThat doesn't work at the moment (known problem) * Just in case Qt implements it in future... */ QToolTip, QWhatsThat { background-color: rgb(190,190,190); color: rgb(30,30,30); font-size: 10pt; } QMenu { border: 1px solid rgb(0,90,160); menu-scrollable: true; } QMenu::separator { background-color: rgb(0,90,160); height: 1px; } QMenu::item { padding: 3px 22px 3px 7px; } QMenu::indicator { left: 6px; border: 2px solid rgb(90, 90, 90); } QMenu::indicator:exclusive { border-radius: 5px; } QMenu::indicator:checked { background: aqua; } QMenu::indicator:unchecked { background: transparent; } QMenu::icon { left: 4px; border: 1px solid transparent; } QMenu::icon:checked { border: 1px solid greenyellow; } QMenu::item:selected, QMenuBar::item:selected { background-color: rgb(0,90,160); } #MidiRouteMenuItem, #SwitchBarActionWidget { qproperty-highColor: rgb(0,90,160); } #SwitchBarActionWidget { qproperty-borderColor:rgb(100, 100, 100); qproperty-checkedColor: aqua; } QDockWidget::title { text-align: center; border: 1px solid rgb(0,90,160); border-radius: 2px; } QDockWidget QTreeWidget:focus { border: 1px solid rgb(0,90,160); } QToolBar QToolButton { background-color: rgb(40,50,60); margin-right: 1px; border: 1px solid rgb(0,90,160); border-radius: 2px; padding: 2px; } QToolBar QToolButton:checked { background-color: rgb(0,90,160); border: 1px solid rgb(0,127,255); border-radius: 2px; padding: 2px; } QToolBar QToolButton:hover { border-color: orange; border-width: 1px; border-radius: 2px; padding: 2px; } QToolBar QToolButton:pressed { border-width: 1px; border-radius: 2px; padding: 2px; } QToolBar QToolButton[popupMode="1"] { padding-right: 16px; } QToolBar QToolButton::menu-button { border-radius: 2px; border-left: 1px solid rgb(40,40,40); width: 13px; } QToolBar QAbstractSpinBox { background-color: rgb(30,40,50); /* color: rgb(152, 251, 152); */ border-radius: 2px; height: 20px; border: 1px solid rgb(0,100,210); } QToolBar QAbstractSpinBox::up-button { width: 16px; border: none; } QToolBar QAbstractSpinBox::down-button { width: 16px; border: none; } QToolBar QAbstractSpinBox::up-arrow { image: url(:/svg/up_arrow.svg); width: 8px; } QToolBar QAbstractSpinBox::down-arrow { image: url(:/svg/down_arrow.svg); width: 8px; } QToolBar QAbstractSpinBox::up-arrow:disabled, QToolBar QAbstractSpinBox::down-arrow:disabled { image: none; } QToolBar QComboBox { background-color: rgb(30,40,50); /* color: rgb(152, 251, 152); */ border: 1px solid rgb(0,100,210); border-radius: 2px; padding: 1px; height: 20px; } QToolBar QComboBox::drop-down { background-color: rgb(30,40,50); border: none; } QToolBar QComboBox::down-arrow { image: url(:/svg/down_arrow.svg); width: 10px; } /* does not work as documented (Qt 5.9) */ QToolBar QComboBox QAbstractItemView { color: rgb(220,220,220); selection-background-color: rgb(0,90,160); } QToolBar QLabel#PosLabel, QToolBar QLabel#PitchLabel, QToolBar QLabel#TempoLabel, QToolBar QLabel#PosTicks, QToolBar QLabel#PosFrames, QToolBar QLabel#PaddedValueLabel { background-color: rgb(30,40,50); border: 1px solid rgb(0,90,160); border-radius: 2px; height: 20px; } QWidget QTreeView, QTreeWidget { background-color: rgb(30,40,50); } /* QWidget#MarkerView QTreeView, QWidget#ClipList QTreeView, QWidget#MasterTrackList QTreeView, QWidget#ListEdit QTreeView { background-color: rgb(40,50,60); } */ QCheckBox::indicator { border: 2px solid rgb(90, 90, 90); } QCheckBox::indicator:checked { background: aqua; } QRadioButton::indicator { border: 2px solid rgb(90, 90, 90); border-radius: 5px; } QRadioButton::indicator:checked { background: aqua; } QWidget#Rack { border: 1px solid rgb(0,90,160); /* border: 1px solid rgb(24,151,255); */ } QWidget#Strip { border: 1px solid rgb(90,90,90); /* does not work border-right: 1px solid rgb(90,90,90); border-left: 1px solid rgb(90,90,90); */ } QWidget#Strip > QAbstractButton, QWidget#AudioAutoType { background-color: rgb(30,40,50); border: 1px solid rgb(0,90,160); border-radius: 3px; } QWidget#Strip > QAbstractButton:checked { background-color: rgb(0,90,160); border: 1px solid rgb(0,127,255); } QWidget#Strip > QAbstractButton:hover { border-color: orange; } QWidget#Strip > QAbstractButton:!enabled { border: 1px solid rgb(90,90,90); } QTreeView, QListView { alternate-background-color: rgb(30,40,50); /* background: rgb(24,34,44); */ } QMdiArea > QTabBar::tab:hover { color: yellow; } QMdiArea > QTabBar::tab:selected { /* color: rgb(75, 183, 255); */ color: orange; } QWidget#MidiStripTabWidget::pane { margin: 0; padding: 0; border-top: 1px solid rgb(90,90,90); padding-top: 1px; border-bottom: 1px solid rgb(90,90,90); } QWidget#MidiStripTabWidget::tab-bar { left: 2px; } QWidget#MidiStripTabWidget QTabBar::tab { margin: 0px; padding: 1px 4px 1px 4px; border: 1px solid rgb(90,90,90); border-bottom-style: none; border-top-left-radius: 4px; border-top-right-radius: 4px; } QWidget#MidiStripTabWidget QTabBar::tab:selected { background: rgb(0,120,60); border-color: rgb(0,120,200); border-bottom-style: none; } QWidget#MidiStripTabWidget QTabBar::tab:!selected { margin-top: 1px; } QWidget#MidiStripTabWidget QTabBar::tab:focus { border-style: dotted; border-bottom: none; } /* Scroll bars */ QScrollBar:horizontal { border: none; background-color: rgb(20, 30, 40); height: 14px; margin: 1px 1px 1px 1px; } QScrollBar::handle:horizontal { border-radius: 2px; border-color: rgb(50,70,90); border-width: 1px; border-style: solid; background-color: rgb(20, 30, 40); /* min-width: 25px; */ } QScrollBar::add-line:horizontal, QScrollBar::sub-line:horizontal { width: 0; height: 0; } QScrollBar:vertical { border: none; background-color: rgb(20, 30, 40); width: 14px; margin: 1px 1px 1px 1px; } QScrollBar::handle:vertical { border-radius: 2px; border-color: rgb(50,70,90); border-width: 1px; border-style: solid; background-color: rgb(20, 30, 40); /* min-height: 25px; */ } QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { width: 0; height: 0; } QWidget#ScrollScaleZoomButton { width: 8px; height: 8px; border: none; } /*** MusE properties ***/ /* The title header background colour in popup and routing menus: */ MusEGui--MenuTitleLabel { background-color: SeaGreen; } /* The category item background colour in the advanced router trees: */ MusEGui--RouteTreeWidget { qproperty-categoryColor: SeaGreen; } MusEGui--SnooperDialog { qproperty-flashColor: SeaGreen; } MusEGui--TList { qproperty-sel3d: false; /* qproperty-curSelBorder: true; qproperty-curSelBorderColor: Red; */ } MusEGui--AudioStripProperties { qproperty-sliderRadius: 1; qproperty-sliderRadiusHandle: 1; qproperty-sliderHandleHeight: 26; qproperty-sliderHandleWidth: 14; qproperty-sliderGrooveWidth: 6; /* draw groove part above the handle */ qproperty-sliderFillOver: true; qproperty-sliderUseGradient: false; /* None = 0, Left = 1, Right = 2, InsideVertical = 6 */ qproperty-sliderScalePos: 2; /* draw vertical line in slider scale qproperty-sliderBackbone: true; */ /* fill slider handle with color qproperty-sliderFillHandle: false; */ qproperty-sliderFrame: true; qproperty-sliderFrameColor: rgb(15,82,148); qproperty-meterWidth: 10; /* apply meterWidth for each channel separately qproperty-meterWidthPerChannel: true; */ qproperty-meterFrame: true; qproperty-meterFrameColor: rgb(15,82,148); /* Spacing between meters qproperty-meterSpacing: 4; */ } MusEGui--MidiStripProperties { qproperty-sliderRadius: 2; qproperty-sliderRadiusHandle: 2; qproperty-sliderHandleHeight: 26; qproperty-sliderHandleWidth: 14; qproperty-sliderGrooveWidth: 6; /* draw groove part above the handle */ qproperty-sliderFillOver: true; qproperty-sliderUseGradient: false; /* None = 0, Left = 1, Right = 2, InsideVertical = 6 */ qproperty-sliderScalePos: 2; /* draw vertical line in slider scale qproperty-sliderBackbone: true; */ /* fill slider handle with color qproperty-sliderFillHandle: false; */ qproperty-sliderFrame: true; qproperty-sliderFrameColor: rgb(15,82,148); qproperty-meterWidth: 10; qproperty-meterFrame: true; qproperty-meterFrameColor: rgb(15,82,148); } MusEGui--Meter { qproperty-radius: 2; qproperty-vu3d: false; } MusEGui--TrackNameLabel { qproperty-style3d: false; } MusEGui--EffectRack { qproperty-style3d: false; qproperty-radius: 2; /* use smaller non-standard scrollbar */ qproperty-customScrollbar: false; } MusEGui--CompactSlider { qproperty-barSameColor: true; qproperty-radius: 2; } MusEGui--ElidedLabel { qproperty-radius: 2; qproperty-style3d: false; } MusEGui--CompactPatchEdit { qproperty-radius: 2; qproperty-style3d: false; } MusEGui--CompactKnob { qproperty-style3d: false; qproperty-xMargin: 2; qproperty-yMargin: 2; qproperty-symbol: Dot; /* qproperty-drawChord: true; qproperty-totalAngle: 270; qproperty-radius: 2; qproperty-borderWidth: 4; */ } MusEGui--DList { /* 0-255, sensible values 0-64 */ qproperty-alphaOverlay: 32; } MusEGui--CtrlCanvas { /* 0-255, sensible values 0-64 */ qproperty-bgAlpha: 0; } MusEGui--Strip { qproperty-expanderWidth: 1; } MusEGui--PianoRoll { qproperty-pianoWidth: 80; } MusEGui--CompactSlider#MixerStripAudioPan, MusEGui--CompactSlider#MixerStripMidiPanController { /* better use even numbers */ qproperty-thumbLength: 2; /* qproperty-thumbColor: red; */ } muse-4.0.0/src/share/themes/Deep Ocean/000077500000000000000000000000001404105311000175125ustar00rootroot00000000000000muse-4.0.0/src/share/themes/Deep Ocean/fileopen.svg000066400000000000000000000015221404105311000220340ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/share/themes/Deep Ocean/monitor_off_default_col.svg000066400000000000000000000030121404105311000251110ustar00rootroot00000000000000 I muse-4.0.0/src/share/themes/Deep Ocean/monitor_on.svg000066400000000000000000000030231404105311000224140ustar00rootroot00000000000000 I muse-4.0.0/src/share/themes/Deep Ocean/mute_and_proxy_on.svg000066400000000000000000000035011404105311000237630ustar00rootroot00000000000000 M muse-4.0.0/src/share/themes/Deep Ocean/mute_off.svg000066400000000000000000000013221404105311000220350ustar00rootroot00000000000000 muse-4.0.0/src/share/themes/Deep Ocean/mute_on.svg000066400000000000000000000013211404105311000216760ustar00rootroot00000000000000 muse-4.0.0/src/share/themes/Deep Ocean/mute_proxy_on.svg000066400000000000000000000035211404105311000231430ustar00rootroot00000000000000 M muse-4.0.0/src/share/themes/Deep Ocean/pre_fader_on.svg000066400000000000000000000037271404105311000226670ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/share/themes/Deep Ocean/rec_arm_off_default_col.svg000066400000000000000000000030121404105311000250320ustar00rootroot00000000000000 R muse-4.0.0/src/share/themes/Deep Ocean/rec_arm_on.svg000066400000000000000000000030231404105311000223350ustar00rootroot00000000000000 R muse-4.0.0/src/share/themes/Deep Ocean/routing_input.svg000066400000000000000000000014741404105311000231470ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/share/themes/Deep Ocean/routing_output.svg000066400000000000000000000015641404105311000233500ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/share/themes/Deep Ocean/solo_and_proxy_spotlight_on.svg000066400000000000000000000035221404105311000260650ustar00rootroot00000000000000 S muse-4.0.0/src/share/themes/Deep Ocean/solo_proxy_spotlight_on_alone.svg000066400000000000000000000035141404105311000264220ustar00rootroot00000000000000 S muse-4.0.0/src/share/themes/Deep Ocean/solo_spotlight_off.svg000066400000000000000000000025761404105311000241500ustar00rootroot00000000000000 muse-4.0.0/src/share/themes/Deep Ocean/solo_spotlight_on_alone.svg000066400000000000000000000032521404105311000251600ustar00rootroot00000000000000 S muse-4.0.0/src/share/themes/Deep Ocean/stereo_on.svg000066400000000000000000000054531404105311000222370ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/share/themes/Light Theme.cfc000066400000000000000000000176001404105311000204020ustar00rootroot00000000000000 muse-4.0.0/src/share/themes/Light Theme.qss000066400000000000000000000001121404105311000204430ustar00rootroot00000000000000QWidget#MidiStripTabWidget QTabBar::tab { padding: 2px 6px 2px 3px; } muse-4.0.0/src/share/wallpapers/000077500000000000000000000000001404105311000165145ustar00rootroot00000000000000muse-4.0.0/src/share/wallpapers/CMakeLists.txt000066400000000000000000000021321404105311000212520ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= file (GLOB wallpapers_files *.gif *.jpg *.png) install( FILES ${wallpapers_files} DESTINATION ${MusE_SHARE_DIR}/wallpapers ) muse-4.0.0/src/share/wallpapers/Muse2_Wallpaper_1.png000066400000000000000000000765441404105311000224640ustar00rootroot00000000000000PNG  IHDR~sRGB pHYs  tIME  *6] 1iTXtCommentCreated by Geoff Beasley and the GIMPl7 IDATx׳%}ʪ,Ns{w}oq  @@KJ\-`P\"Z=HA Ŋ $!D Ep}]܇_q+"P}%A4%{۷憎!c8M! 8=β2=uMKW'" vŪ;ܾ+qv-Jr*d`^6' Eji5=<Bxj&1ؑ"1ec=AU˗WJa&J3'xyh}wmEʝ۵F0$"BRkaz!I~󟻴810[mQU1Ɯ(rsaaiiA5 fەt͟kBʪïYKoo#I~87فeI[mh0<._ONDQԬ,?ٵ{4iDҴ+/?uq(7n|z^I J7OOOB$^w|2 Q2,~Z'OIih@Vd48^9hwo&FlC7}lJ?RQWp_n,^Z,2vm{֍?|?󫯽:=7=S`?g ҊfQsiVYC % hw]]}vֵYYfzN/ |XnN@^6MiLJN'KF'nCA~ܬ"eCaOO7Bm./6'BgjVdBEY "s!|ϗuԵ0Z0pܳq( =obvۧ ^}cufRyUE`iΰkW_귻h8Ru\.Eq8ty~V?=B<Ե,Ǯgi:9 }hTjeyaLQ,V͒NM4=MHSI-OM:{,%inHX&bBȠa.p9]۶e:p\TY>:59U)! ,gصmYUDž/R[_DcF1&00<a'Aٮ0)FQT+8j71 (V)c?` #&NsGƽ|I,zA5Sg!c`n[r:U8ogQs]4;t9^P$i4q"b,N!!0$p t$˳ } _$w iaLQTg߹I>9 "C3!2azui!!MegY 4a44M/NM]KR2-^IrLk6QӨ- 4tI RD ©ehioP1KS,s$ ("O؋m?Uz*^q^P8wO(`aff*a(Ҍˋ%zY%YvklsbCco._]\|{8h*4 L<[̂q \d~rRixe`So0- EYNQTod/6K3 ?Y])Y0Ttʊi^?I oV^8D޿!6jyݾf(Jw4dih;cme7ٙ "8A1F,GLAY]YQ.<jYxgXV(tVas(¥Thh;S%M<:YT$)bdQa_l/,L.)|9!4MaCP`87;JAWy7vtYp" N̓ÿ 6+yQI4Rw4|u$I;gAԛap!4=gZyL&|e{6~dթj,cv~ݻSޘq=2~W^fYHnRiye9"aX/<+Ol{G-W_~+#8nX9͝{wZݚZZeQd/M5,'yVBYdYxK?W}h|gYyG" ` ч7gg>Sò.' ٫xE!8˲sIP.UaY,!!q:4 b!!/0(7^8Oi"{᷿K4s BIIp3:iڏpoW(1^kۻ';$M/-%aO7K_jp}E4M0n;<9LԶGw?TLc*y@' hwwlޘ_z~?٢Uǣ#V]uY GƽxM7${w߫/-.^^a9nlك?)1dwޯNN,\JeX7>:sWV_}|8^x@̢V(H) `XhS4U䓃%CUlWDA44E ԧ'+R @CXI" yXH4JFJأV0Z'z]q0cY)&iJU,Ik͹J8T.,y.bLTkׯ<{QGaV^^E7A/c?eU0˒9Ƣ 5յ8M]4cbƎ3U)AQrFcQUcͫC8Hyг4c9 %Mŀy* ~Y YMNq,llBd@Q3щ^AGc*JyQ0$)'@QUQb8IJ(xFCJR?]I,?0)NqNY(I,Dz$R9M d!ibi"E/8(B," , 8 !V;M ƣQ)(NRI$s0rC!HEC0J(˳xgOUg9cq4E,ɢ>(/_)2);,EG(SL09V78JDĔ Z,HdD80&)aIiVѕZE% A, ksH50^~k儌pg!ҝ saƮ,::븛݃ []*|0NXӴ ,O~4%Ea!qif,NV/|NgYME bO6?`Xt[30IY9;;-V*ٙ"ʢx>ܺKz{;oUg3SbIQ?+V:=9}λ/]^Y`|GO|e]iY60 ,~ᯯ4wOX]qMĂ"ѣuwioh(& v2ģh\5zdx>MM4;=vPj KSMH$$*\Ic8M8YV2뗖Es ,b0&+Uβ< /|KYUJ,MOZ<)SR^]ܵq0Քn,~%U7 9")?IU(JU>؊ 箬h10Ii,MMNԪO|fyIڽՙdo8B,7;xb6iZ&vw]۶շxmqnZ8𣏏76g]rZ&a$]ucrjRvW uW>rI4~;O_ s/ntlol~tO+nbhI@պo?w߻kϲ(:?<ك \uƥ%B^ȴ͹قU(˘,Mv7KVЋw|'uId$)#YQRe'J|綪k'DŽa'jtóݳVI}a~ja4tBEC(V ǂ\\֕p~sv`Yi=+POOUk$> јbwl_qpT4MbVU^l/gMCZ I9аRА$ xviQ6~@# &4 DfP$<./:>6eUU& IR]3@FjųZN 5S/:akUnEf\V, XVrY0*WAQtI LraLra/~ߌ\P$M:c7g0(Ixr,G1b82S(@%֝E$xεɞ2 IDAT$IdQDq0̳3,Ղֳ] "۞_)2 ɂ(*iƘ`(N0 @QH(890 EQ"010rFas0 aB@b{Nopma&rb9*7]-RǦiEeMER$i`n]Z 4 Ò,#=uiah64,WDe`#?YE]๕%;, 8sSSf!9`qr*J)>g۶/NK<7ۨizXW,gjI0Xl֣$^_,(sOZؙW914EQU0,8^4~U191!<,LN4ڈeddIE%,)y^'&q(Z*JR@yE06dQ (Mi.j\:>yYf!Ks&kVd?wyY}V.M6?|"`8KHT|l.RV)_~)kJ޸sдNfYolNX2?' afuh Fhhi@r4(zV˷X_/QӤC5d++׿֏Zp<]NS! $@0,,{}0&fb}{=ݨ]^^Y3LR)wW/Su,yyӉ! Io8}1+Cq^B|u$Ku4t=ƽyQD82;}1 ^kaZ4RK8UxW.rnv?XZ<)V/~̣z9d"G{?o<pvN~UT{p9i ҐcG8, ! "/}qjtzYQ{qǼ,^YAAxԯ['WVp{`a{t?a*wW^zUL]c oSmkjSz-ď,vvO%"@_unwʺ_$MhٛWo\'Jѧ%2Ǟu{h+o H,˲_3k(J~-\r=W8"]l/|3_xjN\y10L4D+UpHC܏~89 @^g8u|wڒ1LA///b4Jx~I{8Nb{>-R1&k"'ij;i `& ,' !?wcBɂ$)&MQ7wi i6ߨ Ydcw-PkxP2 ]z=n FSUc%~;A!BPFAqeIF $LpT8Ix7@,eRK? U8, 4|8J lW$1$KeX^~J8NV壃#._G;ݽ쫟d*aDBV^X7%J(r(EK+J╫=|xtꛟ4-+co(U'ok\^!Ѡۻ͂,4szvWhhRQnn =%SH4Bhqun1;!^_š297p\$8ͮ.7Ddl0n&Y@Z摤( p({Ojb=;ourpvnJɊA d^W66;P3M@eCyٍfsx)de~c<K|.rK?Y9{neXqvgeL4M'jU2 y~>}Y`j&&^sT2t r w>h>-~}9{gVr]վKaPh&40 B4 tCuwuJ{wg{֚Oq?7w~ lJjQ'FԨ$:.M"4}/onnܽLO)֘E1 Gw~O ;[g8Qjٸړaõuݺ<7듓t{\Z,׫pay!˲f}=U)}?CYµ+ӓGtFY Dut| Xvujjb0wZq] Ƌvvڕ9eR4;^WkdkyCeY,4}׋rм8i>ˋ!Ǿ&HB,49O?~dYQ!iw !B;Vma!t\ad]_-Vύ W1JBz (qF~ÿkqez}{ĉµW^7*hΛ z}Pڳ\yZ(y}ZpN}ϾŰOIB+~x2LfE\[-YW BՊk(v}Q 3 \*0z^mjl(BI0,ǕKQ.],d(dQR]^<=0 {>qMea< &~:]^WWtMKT/'R%Hu-(I(i>;qݩkqo/|_05"\ pqUIu`|)<4@ZyFQP*Jʄ, @Yh0,,LJIfRF<)aJIEA|ai,C$3kZОr$+쌮IL3)1 I0 k˲ e "2p /Ϟo/?@YEhN$q.uF޸8LsaHSEEI%I(q ,Nbe4c MӴsH#L:2P8 !Yq`qy4"ď0 Y)i,ǯ#?,jie1ˑFE dzpjOe!Ak5'MIc"Y>[+6kZw0h;08CR40٨yiv{ ϪI0tled MS#B|q|Y4UID;+ov<Ѩ^ E #a$]{쭭QdU+e]Y6$ &ϲ9"cBRO飽0D!d/~\wڒayI4yGU0N[ZEnW67~ 5em$&";dLvΛߋl2p&iڵW_nLMq{ei(/o_Qˆe(K7LB4=^=:{QJI zY=i+[&/ڝԤYڧgҵOP440|ɽ+ַ>J g+Ks>|W^yyf~f<;32+hUuw8jya4+;<#Q/޼Zˌ<Ea` 3η~g~MpnnQɳ>=u$)ov(3.]SElr3c9NѴM^XhA!W% ϝ.c&, bhG9n,..֋crz'潑sǰ[Wg4I$V4)Alea7<u{^~vN[ \.I[E|{v+B=W%s ȳu|A٬֫< ,𢄢 ZG9•zEQ~;=LNMBJa{ +rIV <2qҠa8 Ih;~QQRkHW AƯqg9ByowV(&NJN8bhPӐ텷?eM!ah,IULd蜪1RaSsce{Q4( $I<^$b8GOX$ba&i1 cDq׋?izV: &QЫc dI2\qvA(irfYV  xFiEIPOzCUj2ro,?tyATeټ 1 iB7G~HS qBatYFis}K_ieQp icLQBoB*C[BP`(I,Cq\Ajyð4M/M5,COZ*e{~g)P' 8V M5%QAR ]ofQa!TIhV1u7ZŹ,+&,r l,MU/NZUM0)0*J$;gQA8;;U)a8\kJry~osyr 1qaң 푦*Va( y `xajZ0he B' 89jwVEQ㲆,M6f{?^@S `+i{4KϽ_u ׮T "]c(~ʭv푮Cn?;ks0U{zXT!nb9yO b SeW8n˒ Z|}A$r*+Kf0ootQB<=9ݬ(:Q}FIW7OfkeefTY™(E0|ys1>^XZV5U8!4MQ2rOַ9J\:)>:wl?4M#,G禄dıHN'<9m 6=78vLyµHӝWvokkgmgsutՂ |o1E@f8;#VeiɲR>~f֠?^W1eYvp37fuI<=PFez !2}Z,S̑'?:=1ͅ }|P=P*>ӭmX\}\,ڎ3pz '㓃Hpqyfv>AYV8.w''f2vyI]KC1y>' BATe!saIІo/| ?M8g('cIX*9^ƪ-.AB;kWE*8ۓ5֥ j%C̳tω]eb/?_TgT7^>scoEp4ju{{.EQ3 fy4M2~VnD?<,sħ]%J@p>;GjtFï\+!=<`MYxAHUY]5IB8Gg2l?s=IVxn 텟O: [ŹIY>axㄱۇG`>Zk Ja #Jxk[|i8IӔCUeQ)`pxziA!M#60zAw:$%cB59ə'2Aid{I+R\E1LІ5MeqvOhsK?( Ba(ҒQ+iU)WA 4xMiƭ7M˰$Nq!$ Uǭײ$0(qb!IF1@a0PqgY~X7 і<&Y BQd5tQ̲,p,:MAo/ u]!#? 0zyU(K]s<ƍKGg4Mo/+OOvlQI: {xҗ0ר_uP* fc`zi~u~vCws+C[u]?+/0lfmv9;]*_}R<|.^ZT0?ާŲ4tctoߪ՚ӓgV|gi/5V_d*{hٕFR‹yΰ׊F Bȏr Tu-#qik5u>,+ZFqjp::fa\B(*LRy>@PUDUv*0NBf&香#>99eYekVaZ6( i^!cmR.תIEQx&YTplgfq Y䛷oI?tNJE|{_9ձ%>LRCdIhvO&f0ƭSUSۊ4MӓE=Y$0Gh8MMdIv (\qq !XBHv>n;ya0 RI/ ʄ>yxmel>;,G !`r﹫a34'i4Eڽ7C?^}߇IB?֫*Y. ;m P%M|wny8]UE~8NfP( !? & 춻"EQEi"uh17ߨvFMC>iIkj ]UoQUjaUl{x*,,?nw,[zW4' Q2eщ#*˜o//U-PtfQ#/Gxyd{8,6fv+)Jb:q7*ųhIIj-Sá˳Szs (r 5Ii֏;]iM,1bY ɉ'{fq'z B#0hR^2?w(I+i?m{ Ipp7,Mi60}?Hv Jg/]QdmgD(8"+Ji)JYJ$Ԫe#Wϼ,B74UEE@IV=YFd1 <:^yYo(KRhEi^4IH,|I$Kyzɲή]\}DE`@MU˵J9QA$ Wُla\T1 DrWfT8<{tz,]pyqA๝(y?^o(fDsnp4Rd);ײ4y[ meڽ~Z)õݳsEro\\ZP7 ߺs "a8^7kթZmIuyp`kOq]`fTڟ sdw?+117 "K%U_ h+/097=۬IQ$ Ìc(EQ/ s=l U~=-CKSc;[~wol>J3 EQ'$ ~.(Z0ix%yӍuAJ38" 0Q/X0};=m!L Ӌ i( (i9USCxٞwޠ)jo/|K_d( yPDq3w68Hã$N˕R'l?ZYmﷺ<ˈ /,iVׇ'a, "M?¼iYj;rr`>(( r`Qn>X]82^vizmDZ3ssӍ*RWIM֫;G{[xUJ&\{[(M70Fcj!͵fIYBؗZԝ7ۣ,IE=Z}k?2WIR[OVVn?wkxzQQ/~.Z]yṋV]߷ 7<5U믿0 ,GΓw?q ([js7]68 W~g,in\[q,ɪꃏ_W<{Kx y퓳$<Ϳ_vuybQzn9X[{_9c~돞V&'ݸ6.;\Rb4d./ͿK VW{aH(7 {$;ٹŋ<˺QF1&D7 NU{$kՕ W8slsz |ݮjA}a `!TMS6dwӣh./9=8BQ0Lhz8{EqFR ܰ+W+ ϷzNJ$"LeNka#c d #BH8;Aq,1n *,4(4MӴiEae F;e9ʧ4σ$h27) 8٭6s*%+ͱm4(H, ,FI2M$),Ar9J텗_ Mӊȳ dIb053S,QY ai*Cy8I|˒L%(uIHs4 B8e_Sf„DI8щ㸅fy%kF1IrdȢ%ò8SUUyIE%IʱlPk%K^x:>8f F8EP9@3Y˼pBi?iu]xg96sq ]cYfUqF^EAGqEz-ne}Vz0[/O7Nk{p<0O[5+ 33C{|$Q]}|BԸ4ې;aE﻾c;B1,G^xH0 ]S4{MQa0D \_d$M } J}ps|&qLٲh::;CS f-tgg~B;w6yBQ[FpGd {5k~q↱D 9 x`Ow $K, 킮%iưL$'g'AN2yo/2gaw0T9~@QT$!h6pgx,Ksʊ<X$ !$K& Aa(*FN`BnzEW !oB0/9s p 2vcX1i;LY,XC?ti}?<^&8Ďޫ}꽛(nK֌%k f C r K0rLK,-;hEEQͭ{W^_O U!ϱCk}hfjafXCZ^g4M5o ZcfzQ=$0ԣp4xngپy/*ήHeoO m FN!xMS'Ji;kO_|<|Fv$a8M9hjm}#}__gb̉ba<Nt8. jgO~O_(ftMp4EDpmes %MkON7ͪjFit,/,,/< j>gQK /rZZeѴe1H9?D4jB|2?̑EVs7 O84Ǒ vh6j4{}eTI9Q,g2N܅BVm?ĶmK(˲8ƈ;DF("yY2&D$\вJ{hqba#MmO4E+%e%Vu-!N+Q4ƣ3,LVxIZkr:8Ѝ|LJ#x8rJL8Iζ]CAdDxK,=rb^gD "kQ@DIH"K / T-%~ g2.h j.,)CS)HCQQ*Jyq^@"İ,$IM tM4@Mk@Q"X pd|V2 E?`29Hgۋow* G>Me-'r<F[K A;~P)aC"ѹFew ȧ")ya@:S)P 0 d1DQf.IRNci*I cpJ7(IZVTYL78 Mq!"//3W`h),M,g ͑Y+kBgh&I|tCQd2$hj6IU=3tc( !#?uY,FN8Y=?Q.A$8i2ߨ$ٳRpi~F⹎iK,Rd{d =?=U4 'IZtaf*I9;eOMk_LVʹj)hYMOVW95Q-_[Z2:c70&.So8ED$;{{㶆&KKZF JOn<EGuOy'ݓN")kl' KI jH"R8"}s `:U߽zqy\PXkO99VGdg[Iyfx~FSe7hK~ꅕjd0|;dEmB4);+Go$InbcSduJ ϕ\s !Bx?ix{{w8F]rqO>~`guͱ+o_=xW(pOӞ$In4HWVOFZ^ٍho{T]&|0f`+'u#g_vEA_]9רw C{I'I[/wϾXtQ8NNOڈfVWfx *׿ c0dBxz!ȖZ>d)qwY5d!?w1(NVEEa?4MBcs;<řClg4O 8 Je[,/0GCEQz_$Ѩ{={89vݛJI{݇ߣYvc x.8l{~`_{вǃvgE|ogVW"9 ã4ǽAbШM͸ "Ʒ[PVڝ{WL4-al~tۊ?ɉ+wn!\D+9] ܣ'<4%e!B/}ͥ۷ԼG^X]bt$E^sK(Nζ擵RvwI!VE~y8iERUȺvau mO B&H$%vzP)<0,F$Ez<$yJ%D:/ /g mƭ$i[6IӪ,2'ɠۏnp"A4I2Ԭah4u]Q!!NGS؆*r= Xf(4Ӵ'Kq:~`۶"+gۋ&/]8I-?Pe1ĸ=s hbNڝ\xL@8S+t= |'WY8Jx? z94-k',|olij6rWsQ$bhMHi5ISʩ,w츖u5iV/Xew8*yM(a`|l{^)\HIENDB`muse-4.0.0/src/share/wallpapers/Muse2_Wallpaper_5.png000066400000000000000000000224771404105311000224640ustar00rootroot00000000000000PNG  IHDR~sRGB pHYs  tIME 7iV1iTXtCommentCreated by Geoff Beasley and the GIMPl7 IDATxڭ]MZLW=k } p.гᨈN1*K?R(1c=~y{{忎1_p)_| pY\cf>1 _g^-hٌn%c!~њ$wωAcϯp-9а_߷s< *L*kԟ? ?3_\CX<2Õ,W]_Xe yyV!o8F7n>7x~~U_|q51$2_?߮L4o v7u^HM.?ۅ"ʴm㲨~0v?QYL;7kV%^-5?dSW9 ^ɴ\^~p |%xF!|'{ R6.m|V{>oՀލ[rM|qOOOOc|{{ߟ3~{{q{{{>2[/+|yeٍ󯷷GV1?s>]>>>'#̋+̏͵/:r/Ǐ͟e:o h EW;WO }W.sUf o KqרGV#yO|ɘq&f oYsr7';& oy_,;7=>\F =??`s-GD ;!~OOOOoג=C8i, w/{a /^maםØv MCc+;LcOGZy( 4Bo" ϓWn .ؘob7D\9?DWLcfD'B!%+"}鉝%9V$Ic>[`kgN̼;l8$GO;6f[W(%$5:y +ؗoSUmѵ*>zFfYeEwk̈́a0]U٘ Z^v*XǭEk~޲>s$WKqԔQNv*(}lh{{}}WKT<>g8n8yT|Z/qmd X*o/L,N^%&> <2MDضIE=HƾC+1z:{:5z~Ņftw>F>BY4Ts?1[Iɠ+|^h$o %%z7x-UI<Il:YUwhh Ga24h j;NPik8ޠE13!Tj61a0O1[qra݅x"Cl|hMęO&Ì,|Tr2 1㐛c_ HaR8&NćOUkydT4lR9|6ĵXيF{ q{a5ʂk#!cY*ODDŽ޷8Аoib~(:&nTEGc_U|Y1=7S|f~%Ϙ4jE)4Gz8 Ӥ+ZNyT~8/#nGc)wy&-X}Q,i q֩hIV!u:3_܋u &vMi.r֛Z{T}NIT9UDcȘڒczp~c7:0@&ycP,9$<кgf8?hOwww. r0`S z㣿$NLEwf/Ƽ:e@NS()RCɅߪhA1*p$f"ϱpN9QbU%0#ƓJx5~zoLPJڜ&1L RŔj1B$b qXAs'. ^`1UɅGbn]zQʳ$e_v߾S|(3nw?y -Ea mQBvctUo[klZ|%T}IdVY6#7 "pce UsDLUQQSuTA ܊p|pVW꼗B9[_<1,\N9w?=<<ΑGs!U;/I H( $DsgpQ%(_X#$O921Bm##c*ljuIϻgHō_%Hh*d|%UU4l- B?(0r<+c5pHαzDU{uzU< ?8GhDhM+X%Ĝf_ب2r q͚pԩ*"x4y+9ބD`b3ku(H7bz@p*ƒQ?/1Rfr-C8է#0~Wc1hH, xx.@= KPl@`$|A Gn qě._eMF<. UϡnZ/ > Tz|R$G-7ɇ90SW5L8)A 4G|35yR90w~ؔI/Oc"`N`[ZeOe\s+,A'#q6DӨ©sB^\gWGSٚLtrgC󒏆SԴHt.&Xj/)# Qߊ|o0H(Gdw,;XeˋT,HE) _Ϝ~Z2}F<vGG0 >Q/FI3^qwڝASxұVc|;W-+U\xOiŌiUV6/k?ZeXx#!7u]U~*:{DWȎ#iQG*zԏVȋH~(~/8XndYM-iH pH'OXqphJADDQuv$DCT%Y/RbXּ {ŋ9ㄩހ'/C9A/"PˊQA-׉a3d-W_T{N5ߊY4[_S7qIM|s^9ei2?^eEk%-EȘ#@A*ؑi.L6WY\~g(. ; m UN,tߪ"ܪ" f/m8QSivU#@UX@apU5BoJECDOGj}7Li:##)b\⨅ DXQMƧgyhsO3yp !ƎFY Wa;)pʥpW'K]aɌ}8\x}=Su)bLcOwww3Y/9)s`@Nf߇NeYx!`{"d6<+9 c1I 3R<&zhBzA]I9"ΤD{Vu 0eCx ƨ^(>p]> x#bXMLgCaʂȃ0 `,D RėBrɐ`] &dY/_$+a㎨Ƽ9ÈgLqKDuBhZ~$;m0I."!Gb8%EFW*ð eΙlr*[f2owi/fFX?q/gg*&2?%S)i>QU[yŨpqWm˾ 4[GJ^F}"kD8@ʫ5ԘzRgU]\X5D]dοbfKewOKƾ/0Sjܖs/?֢e> +_˸"n#[R %QbŒAXH/FV%y"4W~P J82v+V6J8Yh4QI_*to%5S(n9Yr5O*צnjt.*U^aqL~$}ɜIg]o[_A>I6pks_.V+ޯbuV *6nX.]4`OlE5[:?E.FAU)0%S1yB$ `Ɋ5}{';1S^.Ž dRq&LR`LXIl6ByG"E5%[5.@PㇴuqH:(ʬEaiG0yRԉ,h҈O?mꪴAEޑ©LJ_gayóUfLJjT4zb vY)Zt^JoSz8-~IP`W~!ղ(_+ 6V?xR&ܬ:Cl%s࿉~Rc`r Bi n zx@?V0*I<\. ,xKǧ)L];9 _ܛpaV1)k9_$8_uW"Ôy'$dh$cݪ'{5*&]9}u7/YPu)]fxCxo10.)!Cإrmd')QN!t<WXXwrw3"Kɤa{ J3$x)L^ sBƌN/ brb4b"Ø3k1b컰c̯_䩃)2M%+$,!)ADyN$18ED m:F!&,iQF  gO>dOLfU7/n`VXsu*Ob&Ap̣lOʁ$qϼa i$NSL7+EӓM2D&Q㿸3E脅$Q3uB<;"?~Zϟ S:/r> j(_)u?F*jo(mkC~+La.IDAT@2[lA}r-&ZsM*_69"SAܫsȹ t ֩ZkҭuHq#q%{Vfשd ֱo5bdUqEc%Dxc wb =s\Ȉ ^Qs HxSYhm\q0E!FcPm8ّíW*ҽ0%F[솈KҴVO*Zn9qc=Irӣ=kS3 N~$D[;*dPI$@#gc_c^ٲ9?x>u*6{k? :ϝc^9]#!>tOS)4B$slG}yvUZUT6ȿRMUkU4YnEwn8)RZU4[u4H*ǸV&3ՆyOϪ p\3jq6]KbwTBh9KFV-Nc%l=dzMW${{1^:cGpҫ쳼WsԔuMge ^4覢d\e⛈m~{ %#j}?i~İm2xI49eaP9rgR2 DV8ˢ]@ɥ@œ^8sR2E@(a+,~Y{ʷ܅R;zSШ>Ws|*6 >:XLU E] 4; =̡V!#XFBxDz{ FiVF||CCV"ܪX~?`=FZo](lW ^FÕ4jU*? yHIENDB`muse-4.0.0/src/share/wallpapers/gray.gif000066400000000000000000000150531404105311000201510ustar00rootroot00000000000000GIF87a,`%$h$)!t]'pd-`G 0lG*4&`NB(U00թ:`iObV,*wMgs* # 9o4lP?vrb0X6{w0 $n|6GPR6Hs; }mD$(6 P  V yh80  g d(0^:vf S`F1~I 2E1Ew R$ : =0fQ8#!ah͝"(lb` P֔ xj|f4$!ByIPm((^H`kmZЀs*@ln@kZ[&g"Wb ۞l l, ˉD?ilxj ] 06D #!i Y+%tZcKnJ@N8ٳxGڼ#0@M\BQ@7-)'H%[dE6\V8s rΗ4RK_9<`Vô ;dnmmƉ/ە@aJ0wFHr@Rq9SRq&NzXr&p `mx`)lTh~OI2=6=G˞T0x-"4&I`vIb#.>J\z?7LP7 R+)DDޢc&+Cx+j8*2lP[hZ,eD=]4Q Yuai„w( bz+1ڄQ=$kJ}]D/}av6;tŲ )H/uԧ6$qR(9C"maAI313)r-~)$@ٖEF/\JT(pCVȒr9h民8)$۰`-KK3Am^, Q< $o!NYj@r:x'&_HY4 uϖXfC7e@u`O 4X1q68DȒͣ%%Jֆ F0X{͜tلbqh8\"&@$$/A)1稑ۨ}v"`T^DG$ CkP׽aYZ*$EL5{⪂F(I@`J6Q:B=Y/T`k@a>35="Y,1VCad}ই$l2q E^.pPvjPjQ[Dy- @TE | 1AdWPGбd&N 2n<`S˥kw WIksfqH64l|v@'u;v+1Nimm˜7 @.~ /\]>-!8x hlz\MF+Lޛ, ֶ&%uUڂ,4pj6V ZPt@7DL% V"|ryP̿ 5 ,x'VEv14ZMmoی[h0@+uESݚ7+ \ ``ݨNњ>`O$ ^';bF'k l8E>6t@D zfQTpg n}6)T-wyi|9{(m1r˰:CXܸZ@6a]pSYoL5 詀jw$e?<4Qq_߸\|wcrtNHៜQ##' gD,M} Cfi35|@&A.D E Ahh͏ 9*~2^${*+P(?fQRi#B3Zt <\FrAG>Sf}*:C6 v'aSQF1 c:9Z0B@-5(v3>Y198g#F۲E`53P96TuV/ c`ucbe-4`y[OQ.+!|~lA+RwVGy&vPmTe7 9s{Pauf'Zg_h"\'pp`~.V%$*1Sy7d,yx D/&M2(?f#'Ø0xOc|":hqgEx MT"%2UB{HAJBvreV!Xɲkg,gY1@D2e7{+`]JgQvF(3`ih yyC)\&sR4⑗+Ne5>8޴x1ГWRyP9ZI`|PylTAw4hܧ +F6=w I~6@",`H6 HY]M$ Md P 5DXAfEC909e# [wn ~le Ia{;}ts @Rgv#v\yTh$`@7LEm^s@i -&D]vn xk6bۀ`AG(+:AJ8W0q>i!9$Q^j jZa?bBITgpፈ<aBVPÇ0@13ffS䇟0Lh ҹZyB э /ZنdB917LƉdbtT#ڈ& e%&P ;t'm  Yy7 tapI(ScPF*V` 3I j%2uOQWHsiNT9tRq{ʧ}nah=)|0( yu ęd"P1s$s;xb(B: fyQl&tJAa)רЏUS׬ȧB/"{s)#ª 0!2j9K*Ɖ'3ʆ)>kYZ=>tmwU g 9PqvZ͙PF|GtP0(s-`Bbp'$NY*hZJ6L1dT[L E@cQ* I4RX2 uG!  QpKE;1lH$pOU=r`cfǶ+7Ǫɵt)`gj!j T}vFaB* g^Cx`| P 0 {yr8QcDbاj G~ӄ_0$pWeH.f @TRy؇| Y+%Ơնdq ᆨZdP4+}r#+7{ci3yer`u(IB {}Ca^_nIovF2c30 : ]'2("baq˩g2x_0=V5K{RCbŕcW@`1<- d|-^UbSknHb]`cZ|C}wlZQɋ9+v,p?Uڟ{W!v|;\;PAQɗ\C5ʱhz׻Y6p(Rgf5vʟ  @A;9snY>8Q?Tp™,-vےSk,аӅR!< #T%D qwYԖV2³EGy'.5Ŕ|$%^4XFk|@Tt@)NVI HC@1SVX J6YB,ҔaṖWmAunTME]C j4>'n\>elR# G8ɴ}|)%v(t)>d=]mHa3wI+쒉9Y/!Vm|)."5u&5!2AʻPMLYG`ea% G,Y#VzA`2{hp{ǖ2Jo<3uV@? ,RrLd: ٍ ̣=4Uݑt " G='+^(޹qxpYS0" d#Umk Vډ/f3u%6@Wڎo.|kL ` {,O0͛W |hY|b$4NXhhCDjQ MQ՗ފXq1VvԷnpY!BP @w|O=]5l&1@ kE)`-e!y)P(7Oe-M6K  бdK { $TUJUwT(yMPuJT2 TysRa?* 2^3z'yf; Z!cZ\sv.p=EQ aG_>:FmxlS8Vo.bYO4# SZ(1Dk -ƈMPoM@-&G ̉ }&mi(<ܭT~^ |tHWQp<gR@PPH$mX  h0< &>2*<*N5" 048 m5@!I(O(ܕ8* (˖MD<*ZE^# 4Z@4Trsre ̩Kv 9( rq J3Am0A5^UFd|_xuS^!$+Tcsx<Ci&$.aщ0 4PI%"L~PCVL@,H\u9C3xFd;>-8 -R`c%pA $(@k7AdT"8`$0\!^xQWEw$6>R -&0`6`&Xʦh&a߱Ӎ*ΣTG@#ᛏxIR)ǰK@<0Z32a@9⚀Oa~Ht =ͷ̥@0SFm7.lTH%JJ邠{^qp. 0ܒP gQ+*Td6ٲ \ C25ānIm.xk@C$Qn:;H1 86~O 5l%ڦ01QJd#K#)ØcYp5U;muse-4.0.0/src/share/wallpapers/gray_rock.gif000066400000000000000000000563441404105311000211770ustar00rootroot00000000000000GIF87a~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbaaa```___^^^]]]\\\[[[ZZZYYYXXXWWWVVVUUUTTTSSSRRRQQQPPPOOONNNMMMLLLKKKJJJHHHGGGFFF@@@,:E<9IGAHC1KB627K4=45LLMG:8CRKD(:1$/?;%npH)DdS:v:"FPC"L$EHA(H"%!Rc/vX)Ӈ :Xd HzW(/n1 ".j&ѶG2\Ę㛏kIVd!DvArbܩD G#7rLXLC9ẗiƍy?1BR8(1Ō"4!)FA<֨pUtdȏwHZRImw bDF$<~dƔKd`ɸDفD K@X dˏ 1 G"C?Q!B3I+7TCC1 =98f. B?A0O$1HO8uB>8e O 7 /DP + qUBDQ =ܥU 9C HlC0$$2C` z9D=0"p! :V $@J.? !j3C𖚡ADQ߬@BZ)N7oҀ0CЃB HCG`C< CP<r y|]C 3PWӵJ$QO79m6%eL:, <FH C 5C5P ' 9TkE;0#@?FC )MHD`>@8dʄܡ7(= .؂NFA*bJȯ(""V8 &A A E(D8h C"*c(Yq3FED2T2 D}Ă)x>Da NQ0̠B M).JD45EId7@1s=$xNh~ Q9d~{= BL~1 EFrP$!G~#&!%bP4'1 DCP zFD)^t$!<( ~հ5lB)T?3;>Mjr㐊 n#VP0A =)I!"HMGlp J)Aj;v |4̂ h&tV 2E `BಕU^ܡ&V>j 9iR$5M!A0K 0p6!YbNNB-05w^0qg"NH1,50:`ǂ{42Jf %Xq 'X8d%B=M5@j 4xZ: yE1&"~BE$ 2&E],@6gu|c wro26C=QVf f9-H'S7 PIHXP@!X3%mQ|?̰c &2>@0xGpUG0T= )Oe&lK .A%2OLQ0 G44`eAP02zwj 0`8HCW@+2QkCH]sKN$n 4,ˤIЗx ?7#GKR]& 9p1?<E2]8?w*g 1/Q@  Yt as+`rEA-.,-$AOp> 3P$.\q>?A?&LKEE~3IPiX`[Y EUg`8MA&`V`/CV ZAr!Y>2WRsC1crM9%!NYg)Rpr<>Mh!EaDS`E0VfmW J4 N!83o0Q ewZ!>`cL8@(*8s> . c6F2A2@U"A4p0<2j`DeP >V#ZrL1VPW,}XQ=Qv[|PZahB>-QI )L'!ŧNE06Erq,@& &CFa;P\M/f%9hi<@as_1?uFT B(==f )q66^ o80(&Q"vT{Z^)1TpQP~4֕ {!+8.L'!Wc؃#>oO 0arXl`m0PY`)Z3EP&:=P[ђN)CPgS1f&nAy?&Dz D*KO5`:KP;%܀YbIk; =VBS4$=7Gua'A(`=3PTT)yA)rIU_4@9 1"BONHL  8J@? Њ1 gF[TQbXHp0F p!8`a;(%#h%#QvH R=&'24D0MUa!ʢ?:)?p0,Rb6f#A +)0 ibYRSU0#^r#2LdA[@ &P( !@]BCB҂6 s$Fp^r0S[4lE`8@VpkFdT:"y5dJm!@%9gEr*M ;gH@d<1஖a@s )[o%>`=@, =[JK s2А&җT<6!-y#{.: 1l{U)OU!GNQA:A`1 _GXP(E?0`S035 "=&8] 6>f&"TT4è݂-J@|sVU?߀ER0&TB+ZE%&iu1#أ)i*-y!" @0P>S!`b#?]c>pC-}-.{f\V Y2*ۀQm]ǥQѣ yQiKtC)T,3o˔.LP0@6&JY@0 TG)n'`ιC:-7"@&1=Jrr2KpSa 4<[OUH~&ܖ#@0%@+`K7m$=gwvc(1Sp\.AKsm-7NQ@1>@U0IE0QaWHϱ'PMLjmê{'`D7N RCUr4C :]@-C@1Ol8 ArzY*9%o$B97 G$&sTJ{C *cVab-=R, [p}Z'G4D0_}Q+""haKQT<'.&02@^Q@%PQ+PEH TςDBm r}.K}#((/660.4>(uj1p$$K`Cq} R0PmC\~ oS@%Ro>WI*` 1`9l R4CDMGF.?A*><5?QHNJN2>BB9=>L>>GI:>IBL8C.DTA7>4%;@>1I&R76;5.EJ4BF?411B;<1'3IHG#48#\R@.@EPD!GԐCF PCqI.F` ?@@cƍ1|P E |Ǎpq PxH" #1Cri SvCK=bN'@`A 4CJ H @VĞGPA D#@+C~ K,Q!JD:Г#(DB\a"9АAH Ci`G *? H0a2 >DpID؂7XCrCC8UAE\B! 4i:A  dCQ RF p`.$AQ]]G <>lB %Y3gJX VM ZuwX$]D O`5P!7 0i%NQ8Wzg"+`&L`q D 08OH79`sKf f6@%- gkPWH4WKS0"7oʄ&D08O>s)!@FTFp;>BP@ ?aD&*%;@hq@sCwM~D@N`(d4E`X@=q7%N4 T% u09ϕ&,xvݥJp <(H:(MJp5NC I`* 7Q3J 9GFs)-hBB:PCm;"AUpD401Se]D%fD$`Y<0rtD0$>qI6"XSG=d./P,##w0@@VK-2`P5 8 A!YOI[ k@;ц@+I0s;0@Vʲ(PG_GQ"@%d.,wEsGrBr!Y o/86Z !#iM) *` l6,i0 {C>,+>*P4[1QE`3^B)\%G> v*1"aR01`c=0G82" @X1Zp7#Y#;k15 A 6@M CJd}w`+D%i3 k 0jOP_OGn=VA00c^R=2;t'dMuI=,&GED~/у4@)R<-)o RBXn#.NNec4!p\(Op?p=vF ;3Y?)H M9R00*u e+$P)I*R0;qS%&:F"Gu+{@<eF*D #4 2`6*K. `F4 W1zBZN 3Mib7~TjqtX KG&(L$ 0-#ncrj#0k`6:5J%tQ9TsCYSŊsgE3F@vcaSJ 2= 2 C  |J|1џ[ŕelc '8W]u91b#a RH0()Kw"QPKZ@'v't'^pb5G4 KH+AS՜>4S 1q{89@/>?P?B;V\*4??60NG<>2@CDF?J9R=AE4:>9?I8>:298=HMh>B+@E1<<*1A=>C@3DLR@:0=BfGAAOL3A7@21)CG6,5DGBXb7<ć5}3ѬF!9t9gc/64!I3|)"NjV$Ev1 .`1DA '`$ :&,$\K~0ĢMHĈ,_"9݈a'&AD2haD"HNiXQbG~D3$T0PĐ&@p@B .B|ARƒ;8ٱ)"3nnCD1>ne-^1bKDEF4(DIh9E?pILAR:@+0-@$U $` =P PD1DłJ$ =LPa#CL ERE1`&r?@I815`up-Т hO4I > QИc6AP/@ ; `48 AB I 2@ z'PeaS%QXE1BAF(C!D /`EUEDP>4A0>0{ $8l lĚ>L7L7<mADI@E GB$01P3U:$i!=Q B HQ%頂 . Ļ>ČB#l@ OфA?1->(A-LW`GqOQC ;0CpNPNC< HЏ O1W`=< PG(UpY@ %$.Lr;J,LЃ ~'BT `VA] =\A:aB*fXP^ 3а7A07(AE2`2%$bMYr)@p?`Bl!_0 3W% aCXW"h iA B  VxBB DQB:@ [`0? |d0A&(@ 18} (H B!8A0AFABjT2*@&A,GhA%P ;ys;PXj"H Qhd !EB҂ CxAj%9Lh23iPP}ؑADf DXJ\QMɨ$H20P @e0zkv 0ri .A Tp) ր# ZA(< afG0WhԄ#! \@8 !H>8E1\pF6 nq67zE@AhAK-J\M)HA݄gnOXBݠ^ s1/hPE(`]% !G\ 83A WP ^Ԡ(d,H.˒W {@ P@B2qTi[ׂ 9O ]q hNp_bBy,4n Mp @8#2@_~p A\ j@Wp-4`^Й ,p`Q: py` `C0,,A u"%-KRaHp)'>l }^2dHmK ɒ g@8x-68 R+o 3ؒ- 2 3¬Єb2  TQz0a 9OU%a Rx TO*\A1)Qfj4 PP(࠷)BZ'(a J j P9خvУLޕkS/`d!NX3z\L%HB + ,fAjR;Bc C* #‚.C ͻ& ^`'8N>(0N <C@ Hጼ@ bv.3Pd hLkՙ@A; Zr9nGg@V b;pA jЍ:JNP vG#`Z ?:&׮hP8ʁ̂1 Q6De CV, @.Hf #䄎gGHkB ̪UF1PL4UBi7xDA| (rXf7=x XPz QցD;rfYF z/h! > x'\ (A>PJPLw?,3N<!Е.a% G *ANF2FM&G61C d2G>S޷uM0 ]I R .B@O`6#&>UUo2_Q,@#O@(0c29jtps#8QmՑVy?L0 a2lHv^=`KK%a3 '×M3@C`P(+<(w9qoF@U`+.&,Kj`ze b|B+'B#VUM49PFf5PAPDa5rU\ RM/pM06pLЄq$Iސ8ޓCR`D 8P61@d Ut@MKR+PLK7HKPZ#,aV0%@Hc69=`ROA;#prTr(0R-9,F1b6 jWPU=}3pi4C?*s E h p #66F"HgkF`h-FVpc7W:@h6!%+&8l MH3tF`5BcL%:PU[`mGp.@ͥKAhrp.!@OcYnC`AEM|f-cTP 2R`@f. = &K>_NZc#wB M&`'+2.kY9!@&]\󚤠/8H8BT CfRQ$1K6S^@ Z5u(`+HL?ᕞِJq4&@P9F(u0)?)- !4o#=ND1* Bp2NKIPePP O'N08 MtH_0"i> 0)o'E6CSgY<9C%(Eˑ& M]P60>TpZ -0Y@wp84cgUph2C y5Ep6r2@*XMOq'UlZa.E5X&1yWY/6W0zLp*$p %I%?lb.s#pnT(V4) IB LpU>Ov,1Vޣ* $BaS`&%KW/Uu= 6P8 NO]=:Lu+?S'^ JQvK460.7Z@#rƧD0 *.KT`]06ACG@\^D1 ^B70l(w K8&7J`!b-H g/U 0 kBƒG00/1k_ɘǓ"%M@Pt]NS "4s2Q0o61&#1 za]pR0p4QBrZ A3eq|ї>b0&4#S?0lt1pHbk"]c,/bVY6Z+{i R8nhs8R4Q`3ڈ47dYd!&P7& ^3D3v A0ӰZ "%h)aa d OJP7."M(@\IkΖ&#bP8s2EϠ9cb). i*psSvga% 9%1s#IZ0tas)YSJ2O'5FN*#VI#SP70@;\`Aރ>Ck90A!RRa08pe5D1xr37,S 6Pie` @sW!t2ܚBpHC\E`76u9`OsQ)O/HjYYǫXs=H689 00Jb{>j%N}5Y$: q|A`0`,0b@S'XIq2R`#)`86ɐZ4)b&ΫC R p!ްXwQ9V5V(o$SLz"\6n$ nBS0 8PHYO~Ib [5# *9W>ƣ8R`2*` [8oB-g$p%dY4 @@p; &Mg[J<:OB 1er'CR> T$( O;<H*D@b[)04yj;5="]F˪_.))ACBm('kUi@sëh50ze P ; ]JP{ >R K EPנI0&Z`*&0@J (1m >Avh$[JR Q00M2 7LL*]JBDg;` 6 1qӖ";2tR+PM ;E@huF!}KrF.2>QB,Vp [/q"?Z46g|Mz(C"P0$i7?2~3J(9$6o9/NaP,Pt~A3whRJk&'KWdo^510 C["Y02`BN@ ¢j #EJ2X|b2d9PtBVQcݍ d2TG tc\ΑE@]L[KP4K뻆mF8͕!pUa2F.4<`(p)3 [m36sp"^oa1qXQpQ Z0Qwtj)P+F:4M7Ь#Asnf7WVww .T+Sp;%&A`^45) '&8?MA,;9/=9.GDPB9@<:8QU<36F4:>RC>B5A<6F@HH;AX989BK==I@@>*&=$71*Rg=>1>.<<>7)+F8SPL869:5>D;:/B@FEJ;=E?,DJN=3Ft0RƅX|q #X,aG8<=ȓ:*ԄlCCQÈ(BpY#2amqhAj-9nabG5ĈBlXϤ7`X! Ѣ L12>,qXx*LQE5PL 6(BT8DPMIA&5DdD1 Yp"K0^ 4|SB, 3$AEA0 13e0ܠBTED9 1` +B 8EPOeŅЄ 6;B6 aD` cEsC LI$_]OH7D.)1A -> q]DC4ҕ 5PN\APh1Ɖ$p 8EQhiRAKd4ܰQ%Ђ +@bx8ܰ‰RaxD\qHg'3B# :zBHE*8\тT [& 7wS8 HL HH+b%`BBZ@5_QA !!(Ch l)|&c9 AEDFљ -_a@lp`E$Ab`>2 L` (1' /GQf) : _',A8 ) Q`Wp tr 50j :cx@\@_lBrEAp$*A,f0͉a)HP]ذ'>y?-MY)O:HBȰ\AE:(Rp~Dhփ4>(L` T 3B) Ԁ(M  B&0@Qag R^8J| N*A2pK9 _A Rl!2 P'MA-Q!\?P+@P ^,(a RpBBA% ^b L$L0A(DaL JB^.8 NHHAbt+8+_蠦HRVx=؀2`XH%& IL M/ B(0AhV` $ kFޮAwR>2!9p74]Þ,ƃõd9YZD +|<Ib4@ &$ s;H$ 4p_(r8ƍq Pd@d y ݀8! 6 S[(=Hg>}<=ȡ=q3H 0lE"> L(m T2n҅n W5FxBmDIhZ%"d MH,lJa9 9Rb3( >H0]@#")!DiSEc b8x$@ n- =7X(@mKsS<@ YA <) a3X1"S B5_(4a߈B ~!( V0<}չM*Xk&l-G-W#>\c"(qS>A#)@"<# `@JHQG01)#p!,%g, 5@8 8PL^-9` Z8|$J@:;GA$.+`Fp"K(FRE0}HCC8SQu!1G69:EM҉Dp!`2p2P*y;B?2]1tDRVpH!'ȑ2MfD6q}d3 'd+PG`!1C BLBGN? a!尅р@6& 5`3u*H8䁅DP*:p(OO QD0YE$WiThmG@6 9PP6TT;` _V$;p2,Ltf8ZVY =gz"HB`,Qbf$4 prU.' 6&1M`L |–V:DpT=}sK@H _;ڹx䫕JcN@m8Us`mp/(Q?5+BNHF2p{D280-IFkTFjdCd7P:|ڰP.PjJEvP 5 -kV 5%sFE`3p|T~T(%pwT%oMP;C$'JA `0t,PkE3xl+AUIt`Ms)/5 p{[P {QH(4n*20CF} `kȩFdMFA;F~t?r x@)B {B(uC6MJ?PX!1 )O?[WBEP5!81 e%p/VKJT@x902r9%Ѣ ù6-M+RA(5Ts ,M 19Xtq{z:p aFP<EP"h E8A{: o?g#0*2I]b 0&Hi4` LC+cd4\5*`/Ip;eW??'7ץF'P'3։W6*]>6:;q%FOP B) ~g>P?$J16ApJ&2oMnp-Yp,9I0/`6pv!)\`41иC-K VD^ W\p^qG5JF%rH.`+CO{DE@K] J#BfU4m>E&=pQ *aikxO[  I.+R*p} _1/JIq0EvDFjet`1< ;Bp],4hD-`;Dq!}H T FM?ieCPQ :7mcb؆)!e)׉{׾d'uiNA:I /!)6%(H{XvR NJ Q HM:QfLXAFw{G mB"N(2mK*P'  ϱa܇) 1tA rV"/H tR(  b+gPCc=Tš(. jn+ mǂ g1 M2N1 H2MRv4= ‘rT/ze78ˠ*À;m >tR'c;8P"ׇfc@`Asw{ @k, .U{2 @sэ@. QSFıu3@"@ccBU9N/U.d}FD }5p%& }D;YڀQf_U5 HP2wHy AE3FTͤ,H8 GD -V&ZPM *PG1:Q.0Sj8&l)/CH?@CBBC@8>DFICD9HB>:<48'=GIKP:NU>G;M>4D-1@3=36:?H=H<7:8/=OEB6AKM=EII;AB3D/@:1%+56IO@RX5B>=8-=7=.9ZFC<>5=F===?6-?>EN8t("eI 4|v( ` H,<L bD "03գD(0K.4(QuA0H?_DGa9?wC7eLqN,7x BI@c 6,Q8 B'ȢIdCNAC!RΐC 28G!T =C@1RoI>9( !*$=,= cCfQ@ IO<J@PLq O)$/9` S,CQ.P 9tV@4`~2Y3GTJ/>W; aK Hf&@a.,Di@NAG@!&ĕ)wDn!K?`DPXF4QB@P( 4p ZD6 DWD 2ABBth9QDn~OLBFHC,}JlD-XZ?lFƐ$ B+8 E|\u= - a'L It R_2[¤M(av00M=mEņIu 7&3сb = 5n) PPT6L.%?4!`' l0% [B";muse-4.0.0/src/share/wallpapers/gray_stucco.gif000066400000000000000000000506471404105311000215410ustar00rootroot00000000000000GIF87a~~~}}}|||{{{zzzyyyxxxwwwvvvuuutttsssrrrqqqpppooonnnmmmlllkkkjjjiiihhhgggfffeeedddcccbbbaaa```___^^^]]]\\\[[[ZZZYYYXXXWWWVVVUUUTTT,9>DIHB<84116>EFB>>AA>6*$+4<;40.-,+'#!"#" &)*))+-/28@GIG4+#$'*.365321/,#$""$),-++,07?DB=9985-&"%,4:<=<('(R :h!E &XCH ;fr#@v1#E *x0G#DxzAf&BpȐaJ@0X ua @|Kb k.X8QbDP#J##LAʼn N#=l3[ &Lp/` =w>A텊 H " F6"8hANa @D|CƍCq f*Cr5!Za"ɐ4L/+b#F < TG O:00PC;PwaJtP'@$ 4 " (Ž@dg 4``HDC5.s8b`yQV06U4D 0B 'CK,DGAZ,D *`=O00W4|…*X 5Q7< cw/;z6)'PfF,t  M: ]م3UdA;B &J(?B t2dP pS 39԰H50)jAtbs.YXs9ڂ ZpO]C <gޡ rzzVdaEAch 30DC #h̎GQSM 4`w lXvd @\CC4t Otk5O D 2|p?*6Hӝ #)R3 a2,P(-@cȇ/BEj/]"H'jaJQ,:&;kѓ7'A(`z@F@B=l\V F , vAT8;cT@ެ`8WS sU}i"b@ ;] Pɦ%9H@6o CAPA@j ª(D N0ed,βPa^(!X@@@48HStr 0Θ(DX@ٰ&V dš(J;x_wZ` 8dZϤN^AЀ h3̩ 6ImPu x@cA *H!sH x cS -@ȉU41FG5z Ϡ(nT)0#R%.P1Nײ`P}`JjĺΦ@8:085k@9|B"!Qxf2 b V.b\@`@J%P&@ xF76|{KG7؈ፈHZMd]";)oO-ؠ^&89p  /E&},=ZePreh t<`Dڪ)҂^Mf%!"@bP- XV)ѨdHˆK *a>x Z LUC1&$L^莠:|ArE*GV90$tK$ɮ@TC^@V|'B @ SpBfp D d+];E9٨q X>Y=zUsA4 o0_zG/f\r@7HQ%L!dw(H! պX0CX d@?hxH+ ?Qa7P|gR7?!DVIP= P.tP[hpr&  &SSzukb5dps@Z ?Sxc5.P4`tmҋ Y5FG:!QP,8p 0X<@ j11`&(@fӑHO](A sFR la1K '7v_NI@+8 w&Rb<0 , R5GC.эn(8q %Z.Ft#%G׀[^*5UMJ`AP<TY:@hvA7%BRt3JpbA`y% )?;p51b (USGk 4` N"VhzW"whl@) &:i x9+@T Yj>^1ahR*N+TPV y'Y=ԒCU$ b>6*  ,`$[v*@JE vh2w) W*@li4Й~ËJ:+ p'A,sR)K QOf Ruab8:gu!b_RGό.z88\`p@{d ;Bw>d(`7 Q r9d9RfENp2Q35)o@GV o y G xtVotjnO`}}kzP8Pm D2T^.N-(NUP5A70h  /@ڡS "Z.%BghyW;)Rp+O(=PGcG-m%l^3< qP뺾R 0" lkNQ v!E@ 4cCU:d(;+QDǔ4&%|Ve?W>;fX+dب4B| Cs&pi8Pq j$3ХDѓYGhRٙ UHrWOT ᡜ6QfC.Z H+-,(%*7EKHB><<:73-##% $*-/13578:<;8532/,**.4;>=:40010.)%%'*++,024552-.% ,:EJMPRQLC91.29AGJIF@93/--+!!&Pa,8a4Y*dH$X2Bʈk `BYH[J"C~q8v  G0T ƋS7lh':DH`.|n#?jaÇlD "4P!B%Lpr0h.`@~30q E Q )B z@#F!D A.͠ac#E|AY 8x(t$RĐ[84^TB 8C[ bD&6LpKOE똊!08((D $ɒ KPB E3CI82$6 " h$t/10 AO% s&BŐ:=U X y0 & ? *x @dAȥF0#x`&A \B <Ő @̊MDaTmb.Q0!KVxQ/mhѐ*Ps3,t 7C&*$DH ȥpǰE "\Ѐ psJ| p|P:-|P$>0B@bB:&SL.ԐD1+?$*O $F!mr 2 D010 R0% d`<@ _ 3 DB#M)5OPqF ,@ H DA'o68cŤ̀":4 8РpD "lpW4E!COL6 KL՝ AONhP2BתA4A?0L0PpSaS\=*@W Xm}L2C>ܠb:!! .$!A' ANЂ< .7P-=|'p+NRILS(Y>ܠpx@0@"N' pGXT1[Tk3K|!t{6@_7 PL*@RP6F|P@)1aرj@</ap*JnT9 2=j!A6.6(.B: JN--'B@@h(j2xZ5BE#0R3 p&'kR)BRQ0H@p) 0*p*0)V} 4@ղGp~W!@?B$(?v&(!cz^-8NP#0:u$!AMpKvw2c)2G!70rp40O\E;ge? P/r &@o \QAF8`)2JPB :) Pa8q:5,U1GNF1*/1H=`'O'0@RPp$!6rQ/҈kd]r]Ȗ2W:L"C1?ptispx(@IIё1 XD>>PeOHP0 @BpXTpbX?yON%t#79/.@R 6c-L69 NG`uY!xtPd(K~ـ D<r%JUQ%H F(dAiRJ!Ӓ6/@@ J3 q Ҏ/Dr:InSv AJCu&PKjP0PT\KFZ*kM" )<& 9 -8pASe1qi6P7xV9c`zd!}5?vdQF #P?@CEt =`-_È@0(@<>0"j"A! .p0:GXVbRBFE?X&#c;X`1Z)g1X6 q,$*o;v2C>OQ;@15`8pjV!D-PeV9`I=0g1?p)؋ӈS_$sHVw&/v) , 2`!" p*P'yKP*g+`" 0ZIK`m'!*69 = DM 4@IfuD_8pBj@BW;2I tmb Pd!q@ /7`fsVQb|S!} &Eg yй#Ul,.SZ*r!pis" 1`G!cPI?s:` 0-c 1Z5 g 2i[::1izc/ 00S  K- Ur 2B8&Q#$(7z‚l5r 6 4j!ISm@uؒ]P!R}׆K&PXO+` /.+Abl8K !!cSp`b9?  0FA: 1TjPl!=O( 0NUa PcHeL:b sYsK<-uZ{L")our kO~DvyM62)`G(!P 2.!!jj "P6e$k6!41(=njDK5@R`O)+ fBhf%< cںWЩsq!0}Ss!H b(`J]=a% Q #Fb9"=ZpuO`P5XĹ`$Q8%LDc!A˴ xcCAi) p{PF ˆ&PPEuCFZU!sR@K0H,Z,p~TmyHgmՁJ&_/ KG`=PzAaWڒ[MQ827Y%kxFO$=P^XL6(L78P @4vh1E`u5Ji'EYtAbFo6auWcF6 c4V$xaK8VDzd7*K0E-):~"y_M08dM PEMY%qP&MdH(pM UGeGQerW2 j@TIO`ߵLK6?)ސ41 o(\OL*>r5C &Av 7@QynS4p"2h1g⺂EMO:@T@Q,уhIWR P2*x~r<IA\g^;0!v8 61š@# xB.$uEPNtY$ ^ 2#zY`xU 6%S]E :"@H2e#Q{80[Z)㕶RJ!KZ@dl!zFB `LX4BaO'Kש~SyW?0Rd.QXtPuVSP;k` ~pA>G@@ ɰMZ\9^6-QsBj,Ű( '`K4-S zjn>mдP`ba_@Zʂ١70MUV?,b[98jO 4y[+-3:<92,**,.249AD@6-'#"%,5<@=5+%&-4:=?ABA)&(+/10-0:DLSTOF;3/-Œ2;FMOMHA93.*"  )>CIMNORSPG<-/4c)JxaG S ~dH+":BPcB4THA/c+^+ZܔS&M< A6Ž.CHAU6Pk#9 /P ߆$P4qz*`BTA`>P@ "p` 0<ưCI8|7ϲ)ڍ*£qreAdKTa>8u ޒ`?-p ѓOpB 2804"X :C d<Ό~R} & g JCQH7} (h0G ~ᴠ@X cUj@ @I->UHm>0PG8t hi| ^] D4muV $  b3q``@0(RУ=88+x @S>;$40D @4`/8HMqZh7p8pXLBX(nf3?[GlӁx LT^nV. .eYG 4XF QITz0OhӺPi JI^́ nvI AxI la&0:5Hj ID(| n !0 a TV} ͕ys@жࢹ"|ux `h%AكsB!D TPfN *L`e#W0Xp#`pv!A)3! E(B^>j #@Z) 77H6ՏET\+C~q < ب n`mT%6-aZ>K)OC5w|n|C@임q! FB2*R!>ڰ ؅9xK_a(u,Оᓿ@:;t:TDX[pБ -8 tEW 'm:{(OCY]0UpeC1OR=6@4ۢvpt8 "o#GWvC84@\`#6M`Z{8FAv w|YF>;%@%? Cg,JR}dr58 14@dR1tTv uϲOHb4"`5ɅE~ 7 l&) }a& B DYP-5B1[D@ZQG #R |U: {$R5 *` S CUvJ0Pn?0sY pBH%00(Pp2b>2p a!"~$)}9ՀG $;AS@id,IA'sce:mYHQJ!dHp+xvrp?]6//P?L` u}cp5v` ;1#kjtDDrM$PD#rsD&u3*(0F.0 !STaH"r{c' ZYn+rcy ` l%Pv-G ` r(,L`U7>|[i%'b$ V/-8#3Q>@1"0apy5`ҰJ_CvQ(0Pp $Y0Dp?Q0 2H*:! 4RDޒ-% %")B>0T\c}WpQ8u/`V CC<0u`'P;{p fi@;=Ҁ!oP%/8&[*qȝ4C;5CJvM1>PF@-p% PHd*̆:hT:@U%70p|%%вG{_ l,p/ &I~@GpOCRYŎ0FB  948QЕ= [=Pn +|}6) 'e:v+P$0ɑ-!fO ycۖ,,92.[>V  ~pw# =o3,t#%rnX 4A aQ2@B#NX=P@Mu5d%Ӊ e 5 Fg A׸39@B>&fVH`[vpSnk\J"?0қ-RI M NB!? EN'[ _۞Uᅄ5F>j]J)4UW!'*pDCRv 2.iǛ9 rzg.6pz{I2A6v\!v֊)/ }r1" 5aLW#r4!4A8a E-ag<662w^ GEI@ c dF9 4Q"#@bQ^4ϴK&@@;yw5P$ dL3Aɢ'd:HK а[AMLB"%4K -(RB`*PyD-2Bcᇍ e>*t)$ie0 Y-郠X20CJqoQ&e婉IJjZa߇>_$2{&v$Z$tC@#5>4v)OcR&8^%uNQ81*TPpMfX 1 Hٲ->8ple-5HsT^>"{ALnN0aDe-[fy&.-c PI1ˍ0-Q ď|֮Q;50-,.-+'$$(*./038?EHIF?5,&""&,269971"--.01-$'+2&%:DLNH=3.+Ph 6XhlǏ ?rSQc̘ᮃ @D 8lÇ%J B*ѣH 8d,b+W,C'ML1lF @ ]-Nx#2jԐ 0hHׂƎ!K8)C)bEEkX#R0qE4\ѡƜN(1b'P ^D ,p E6+2aewX5lƍ I<ÆV*U -8585 EĀ٨`/' &j%l Y1E! /p L(!J ĸ@WL41\/@3B @Cy+p(Tc 0x ""7@F5eDJ$A"P00HLp:50; /$00Kh:zȚ!m9c!Jd| %e=z# 4BR0XmB>`PĚ}0DtQ4a1SADA'd  (=+(&<` `pA/ l9¡! +.h4C;/S? qDE 8a;PBppf$’CD: !t=ؚP 5`&+SXACCR|P_+ QxT ' +Ӂ H @:L)<\|\ "P8J/;PRЂK,MI "D  eu9hDxa/AB!|`w &Vx'(40J@8xÂG"H5@s].y D樀>R^Q,PB& @yR;3}AVxheAPNEkz`EpKGEhw@(p_Fy.Tsq+v#I՝yFI4ȢJq YV@8 p"W0|1=IlZu[(`fկ| l^Ց:7$\VhB6  ,GGa! ?T,˕+VtPXz1' l . V=D3RpDQKDbcn>2C #9td x{ )> P$"XA a! \E+P JM;l`dwSwٝ@Wr<6}i93u|2PS|0(r1BU.lp'h5w @"@0Б4xыX`!(a ̸AW-~qXil@/xg֡J%b0Pk)p 2W2 s ih&AqrߙYx!NEи䥷deS&%\*,8M+L0x`~_")'ZӂR dfzbbnQeH 1 +Z8dXƔi ;+$QxyM3":욆tl>0F^>1 gml1glX !,X&@p,Ei&q`d;r8%qgzn`6Ti!ZG5t`@!`d8ppjmޤP+Q^z'~7><3xR(mc|#IA*ȻO-QAh1bFblaI6 mc< \髴Z_ͬf!Lm&VDH!a ?,(EU3b[O\MaG@(Kiwr? D5707m*UFex"`75mF F! F %&K@7G4yA:$o|J=  s9+DESvMP"-LtLD e=(\@%+B0BRqPOB=HðlU f` 7%@8cт!*ٱA0//eE, $f'^}V4GK$K $5 L<0R! )4QL! n0r#i,E' P#P OH8FpBV3sS4]i7o@l,?P& a e}3jgB<4(.r?)`!A1u1cGmu25=(W>7CYET$Р1,fEp %cl4F<`<> j$w#0w6CCE~4(7Gt P1!`9 #@3]aTtr66xKp#糓=elѐ"!Zrw'0ݡ(Z#+cۂk Vv s;?Ep(! @ VcIYR0A`)%J<`<ɱ3@L 9@cQ ȋZF28bXv1iH I) 11Wc85>s{"9K`1w1010 ;a`6w5d2 h ynx|nx&=#;Z# a= P1q{@@}0A %9HPW@PS$)Gq B&(@R:TPZT1mi`HvpA9t O^{D{|$(ΑC4R#v 8Y &bm*"<@6Dl  A,#3^ؘyєW/I(4RD%aqDI)&(eh4v< .FP587H?z#XrkrDDA1K#YbQzH&?Q(8bvJ/`%`SrR=!C4!e4<$p36awWQ ' ܱd#4@C/ ! ߳ "IJ`A~+~"C-Їa''}HcQB D)Zt*Njbd sbTfz3C2>v `4E;muse-4.0.0/src/share/wallpapers/paper1.jpg000066400000000000000000000031761404105311000204150ustar00rootroot00000000000000JFIF``U-Lead Systems, Inc.    $.' "+"(6(+/1343&8<82<.231  1!!11111111111111111111111111111111111111111111111111  }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzw!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyzdd! ?NOH0hOGӚ8[<2x4" NsN`p( 8ȣ,@!Jpb=ҕoOydzsxO֙Pc8vO8$w@n9ǥEPU`})6?JN,֗0P1 @Tq!#o fNO= RU@ րX>@$ɐ8(T|i^i䑝(i>`X{h-j6+"h27mPHMj1@zC7j#'9_SDUU@zzp:V;W y=;Fh= sހF…4x3>FOZ(Sdc=hœݣp Ӌ`n? N (S.:4dp;)$hp(8t4@ 'ތ2q?0= \g#rHz6΍p?zv}.3(>` ccc@ A\W9T㰥}E#:S;FIlF:x$<@ W $c Lf4|i@$/,րpJ7{JN rJRv@vR IǮi3cPŁ8pP@ !*RoOʀΑr;y; F'rۓ#n:U2hPcL8 @ 9s)0} ~ހI{R c&|[|zbXTjT+:BRn4muse-4.0.0/src/share/wallpapers/paper2.jpg000066400000000000000000000035231404105311000204120ustar00rootroot00000000000000JFIFHHExifMM*C  !"$"$C"&!1aAQq2 ?Uvyl=68Jw$(#Df0hL+5y ɝTDƬU|: :XIZ؅#I+FghFZ ©DΦJi/ v3A*u NIXDfQ,,XX:%We}Zfu2vϳ7g<4_CvuWeM0rO;*TT#kb+EmJ*ZvR+s$YlӬFM* "_OB PMGjUuA ա*Oaٛ?1PVhaӯFieF*n'w6JeljV;[+Fu!}:1}/ [4'yocf3F&h|1Tۂ: Oagզ/e啸W[8}Rkru( p>}?DU8]Fьl&/!>-豨ͨ3Q]GVZ!%Ik{ӦDf 6^JEᙬU ~ΪZal˪5Q UX-{)@Q}%Yf$x/`*U|1Ȗa5Q^Y[j-5:!%n: W ͦ- ۣfV ƧF[&H+baH*-z'STΤP0%tW:pE?EU7xN>m$m =3M%\Բ\OE1<2R+FEdz+?|:A5x/<3ůE: 9N~ дR-l%FM88u遧8\W #YErT!$qznJRa&xgOit d8o.N:8Z t]]C uu:^jIjB( R5 ^ Y:@f'Kحc|gTDN@f3#5&{D.yD)nEVt-pV2@RWJy#:muse-4.0.0/src/share/wallpapers/stone1.gif000066400000000000000000000023441404105311000204170ustar00rootroot00000000000000GIF87a@@@@@lll,@@X%SM‘t H3'|p2"QnJ, w9K*!!E1)% B, M|"1z8{fOwJ+{-iie-9+}{5jpu+B"ZCf2_)&T^TҴJuBѬ P-(UⲈ@8 ðͰ<1֨@ nD dlLhfH' /RF\3,S!Nu ;%`AF [Z&%ky蓋(v*$$bMGɑh-͂HHU=l@ən Il>F?ݱ&F =x)aaiH1C)$ AOCc+0dȧ4 h9-DjBɊBd^ vg퉖4 q#g.{QPƭF9')%bOYQA-7hpK)?6v $aIv`)Ѡ Q1gR M:C^d[)rQ83=`HCY~!&-RJ#!e]u?x:3,eӊ#Y$B!@aW.TUl6| (\YIL7]Ep dH(}D SV5LQOՠ\W߼2H|7 xmTRD$)]dj 5 ХQ(DžOTmOFk'7b cR4 ¸{G' MEejzg 's!S0egV%|QQBĨ%{x%I9F*ɐHÄ4 G|dC9 4MX!IVy<!WܦfBP 0f5[D]PD[ЙH#Qyq&,P.| ov_mFT_~=,AP6X$F3XT-<7^5^Rk!Ri1 2}67 ..8Bi:>Mu;'/N9rb9'8,$0Q:a6ay:]W,#:29[z58G~R>*hjzZJL ք5 y0;H;muse-4.0.0/src/svg/000077500000000000000000000000001404105311000140375ustar00rootroot00000000000000muse-4.0.0/src/svg/anker.svg000066400000000000000000000042071404105311000156630ustar00rootroot00000000000000 muse-4.0.0/src/svg/appearance.svg000066400000000000000000000034421404105311000166620ustar00rootroot00000000000000 muse-4.0.0/src/svg/appexit.svg000066400000000000000000000037111404105311000162340ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/arranger.svg000066400000000000000000000013751404105311000163670ustar00rootroot00000000000000 muse-4.0.0/src/svg/arrow_down.svg000066400000000000000000000026201404105311000167410ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/arrow_up.svg000066400000000000000000000026021404105311000164160ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/audio_resample.svg000066400000000000000000000361231404105311000175560ustar00rootroot00000000000000 muse-4.0.0/src/svg/audio_stretch.svg000066400000000000000000000162141404105311000174210ustar00rootroot00000000000000 muse-4.0.0/src/svg/bigtime.svg000066400000000000000000000032401404105311000161770ustar00rootroot00000000000000 muse-4.0.0/src/svg/clear.svg000066400000000000000000000016251404105311000156520ustar00rootroot00000000000000 muse-4.0.0/src/svg/closed_hand.svg000066400000000000000000000023301404105311000170210ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/copy.svg000066400000000000000000000047161404105311000155420ustar00rootroot00000000000000 muse-4.0.0/src/svg/copy_range.svg000066400000000000000000000077051404105311000167170ustar00rootroot00000000000000 muse-4.0.0/src/svg/cpu.svg000066400000000000000000000046331404105311000153550ustar00rootroot00000000000000 CPU muse-4.0.0/src/svg/ctrl.svg000066400000000000000000000026761404105311000155370ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/cursor.svg000066400000000000000000000016661404105311000161060ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/cursor_move.svg000066400000000000000000000163331404105311000171310ustar00rootroot00000000000000 muse-4.0.0/src/svg/cut.svg000066400000000000000000000075511404105311000153630ustar00rootroot00000000000000 muse-4.0.0/src/svg/cutter.svg000066400000000000000000000021521404105311000160660ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/delete.svg000066400000000000000000000040051404105311000160210ustar00rootroot00000000000000 muse-4.0.0/src/svg/delete_sel_tracks.svg000066400000000000000000000010551404105311000202350ustar00rootroot00000000000000 muse-4.0.0/src/svg/delta.svg000066400000000000000000000007021404105311000156500ustar00rootroot00000000000000 muse-4.0.0/src/svg/deselect_all.svg000066400000000000000000000010701404105311000171760ustar00rootroot00000000000000 muse-4.0.0/src/svg/down_arrow.svg000066400000000000000000000066421404105311000167510ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/down_vee.svg000066400000000000000000000106751404105311000163770ustar00rootroot00000000000000 muse-4.0.0/src/svg/downmix_off.svg000066400000000000000000000011511404105311000170750ustar00rootroot00000000000000 muse-4.0.0/src/svg/downmix_on.svg000066400000000000000000000011701404105311000167400ustar00rootroot00000000000000 muse-4.0.0/src/svg/downmix_track.svg000066400000000000000000000022621404105311000174330ustar00rootroot00000000000000 muse-4.0.0/src/svg/draw.svg000066400000000000000000000022731404105311000155210ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/drop_down_triangle.svg000066400000000000000000000024731404105311000204460ustar00rootroot00000000000000 muse-4.0.0/src/svg/drumedit.svg000066400000000000000000000030021404105311000163700ustar00rootroot00000000000000 muse-4.0.0/src/svg/duplicate_sel_track.svg000066400000000000000000000011061404105311000205570ustar00rootroot00000000000000 muse-4.0.0/src/svg/duplicate_sel_tracks.svg000066400000000000000000000012751404105311000207510ustar00rootroot00000000000000 muse-4.0.0/src/svg/edit_instrument.svg000066400000000000000000000037211404105311000200000ustar00rootroot00000000000000 muse-4.0.0/src/svg/empty_bar.svg000066400000000000000000000013111404105311000165360ustar00rootroot00000000000000 muse-4.0.0/src/svg/eraser.svg000066400000000000000000000026531404105311000160470ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/exit.svg000066400000000000000000000072411404105311000155350ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/expand_left_right.svg000066400000000000000000000126101404105311000202460ustar00rootroot00000000000000 muse-4.0.0/src/svg/extern_sync_off.svg000066400000000000000000000176061404105311000177650ustar00rootroot00000000000000 muse-4.0.0/src/svg/extern_sync_on.svg000066400000000000000000000210031404105311000176110ustar00rootroot00000000000000 muse-4.0.0/src/svg/eye.svg000066400000000000000000000045421404105311000153470ustar00rootroot00000000000000 muse-4.0.0/src/svg/eye_crossed.svg000066400000000000000000000046451404105311000170750ustar00rootroot00000000000000 muse-4.0.0/src/svg/eye_grey.svg000066400000000000000000000045451404105311000164000ustar00rootroot00000000000000 muse-4.0.0/src/svg/fast_forward.svg000066400000000000000000000120031404105311000172350ustar00rootroot00000000000000 muse-4.0.0/src/svg/fileclose.svg000066400000000000000000000021111404105311000165200ustar00rootroot00000000000000 muse-4.0.0/src/svg/filefromtemplate.svg000066400000000000000000000022731404105311000201230ustar00rootroot00000000000000 muse-4.0.0/src/svg/filenew.svg000066400000000000000000000020711404105311000162110ustar00rootroot00000000000000 muse-4.0.0/src/svg/fileopen.svg000066400000000000000000000022671404105311000163700ustar00rootroot00000000000000 muse-4.0.0/src/svg/filesave.svg000066400000000000000000000022421404105311000163560ustar00rootroot00000000000000 muse-4.0.0/src/svg/filesave_project.svg000066400000000000000000000053761404105311000201170ustar00rootroot00000000000000 muse-4.0.0/src/svg/filesave_revision.svg000066400000000000000000000045161404105311000203020ustar00rootroot00000000000000 muse-4.0.0/src/svg/filesave_template.svg000066400000000000000000000030021404105311000202440ustar00rootroot00000000000000 muse-4.0.0/src/svg/filesaveas.svg000066400000000000000000000040771404105311000167120ustar00rootroot00000000000000 muse-4.0.0/src/svg/glue.svg000066400000000000000000000034711404105311000155210ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/grid_on.svg000066400000000000000000000170701404105311000162060ustar00rootroot00000000000000 muse-4.0.0/src/svg/hand.svg000066400000000000000000000023271404105311000154760ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/headphones_off.svg000066400000000000000000000201651404105311000175340ustar00rootroot00000000000000 muse-4.0.0/src/svg/headphones_on.svg000066400000000000000000000201531404105311000173730ustar00rootroot00000000000000 muse-4.0.0/src/svg/info.svg000066400000000000000000000033051404105311000155140ustar00rootroot00000000000000 muse-4.0.0/src/svg/jack_transport_off.svg000066400000000000000000000155631404105311000204500ustar00rootroot00000000000000 muse-4.0.0/src/svg/jack_transport_on.svg000066400000000000000000000672201404105311000203070ustar00rootroot00000000000000 muse-4.0.0/src/svg/key.svg000066400000000000000000000012671404105311000153560ustar00rootroot00000000000000 muse-4.0.0/src/svg/last_part.svg000066400000000000000000000022021404105311000165450ustar00rootroot00000000000000 muse-4.0.0/src/svg/led_blue.svg000066400000000000000000000021271404105311000163350ustar00rootroot00000000000000 muse-4.0.0/src/svg/led_green.svg000066400000000000000000000021241404105311000165030ustar00rootroot00000000000000 muse-4.0.0/src/svg/led_green_dark.svg000066400000000000000000000021271404105311000175070ustar00rootroot00000000000000 muse-4.0.0/src/svg/led_off.svg000066400000000000000000000023621404105311000161610ustar00rootroot00000000000000 muse-4.0.0/src/svg/led_red.svg000066400000000000000000000021241404105311000161550ustar00rootroot00000000000000 muse-4.0.0/src/svg/led_yellow.svg000066400000000000000000000021241404105311000167160ustar00rootroot00000000000000 muse-4.0.0/src/svg/listedit.svg000066400000000000000000000015441404105311000164050ustar00rootroot00000000000000 muse-4.0.0/src/svg/lock.svg000066400000000000000000000030601404105311000155070ustar00rootroot00000000000000 muse-4.0.0/src/svg/loop.svg000066400000000000000000000023511404105311000155320ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/magnet.svg000066400000000000000000000020221404105311000160270ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/master_track_off.svg000066400000000000000000000030711404105311000200720ustar00rootroot00000000000000 muse-4.0.0/src/svg/master_track_on.svg000066400000000000000000000214671404105311000177450ustar00rootroot00000000000000 muse-4.0.0/src/svg/masteredit.svg000066400000000000000000000015071404105311000167240ustar00rootroot00000000000000 muse-4.0.0/src/svg/meta.svg000066400000000000000000000014711404105311000155110ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/metronome_off.svg000066400000000000000000000140641404105311000174240ustar00rootroot00000000000000 muse-4.0.0/src/svg/metronome_on.svg000066400000000000000000000607101404105311000172650ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_controller_new.svg000066400000000000000000000301761404105311000206250ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_controller_remove.svg000066400000000000000000000301741404105311000213270ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_controller_select.svg000066400000000000000000000270511404105311000213110ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_import_export.svg000066400000000000000000000011611404105311000204740ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_init.svg000066400000000000000000000007521404105311000165310ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_input_filter.svg000066400000000000000000000007301404105311000202660ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_input_remote.svg000066400000000000000000000040341404105311000202750ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_input_transform.svg000066400000000000000000000051721404105311000210210ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_input_transpose.svg000066400000000000000000000175321404105311000210270ustar00rootroot00000000000000 ♯♭ muse-4.0.0/src/svg/midi_local_off.svg000066400000000000000000000013511404105311000175060ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_port.svg000066400000000000000000000042621404105311000165520ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_reset.svg000066400000000000000000000050731404105311000167110ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_sync.svg000066400000000000000000000010701404105311000165340ustar00rootroot00000000000000 muse-4.0.0/src/svg/midi_transform.svg000066400000000000000000000047431404105311000176050ustar00rootroot00000000000000 muse-4.0.0/src/svg/midiin.svg000066400000000000000000000032031404105311000160270ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/minus.svg000066400000000000000000000005561404105311000157210ustar00rootroot00000000000000 muse-4.0.0/src/svg/mixer.svg000066400000000000000000000024361404105311000157110ustar00rootroot00000000000000 muse-4.0.0/src/svg/monitor_off.svg000066400000000000000000000117211404105311000171030ustar00rootroot00000000000000 muse-4.0.0/src/svg/monitor_off_default_col.svg000066400000000000000000000111111404105311000214350ustar00rootroot00000000000000 muse-4.0.0/src/svg/monitor_off_default_col_round.svg000066400000000000000000000107211404105311000226520ustar00rootroot00000000000000 muse-4.0.0/src/svg/monitor_off_round.svg000066400000000000000000000117211404105311000203120ustar00rootroot00000000000000 muse-4.0.0/src/svg/monitor_on.svg000066400000000000000000000133261404105311000167500ustar00rootroot00000000000000 muse-4.0.0/src/svg/monitor_on_round.svg000066400000000000000000000117231404105311000201560ustar00rootroot00000000000000 muse-4.0.0/src/svg/mute_and_proxy_on.svg000066400000000000000000000120741404105311000203150ustar00rootroot00000000000000 muse-4.0.0/src/svg/mute_off.svg000066400000000000000000000101551404105311000163660ustar00rootroot00000000000000 muse-4.0.0/src/svg/mute_off_waves.svg000066400000000000000000000212761404105311000176010ustar00rootroot00000000000000 muse-4.0.0/src/svg/mute_on.svg000066400000000000000000000111341404105311000162260ustar00rootroot00000000000000 muse-4.0.0/src/svg/mute_on_X.svg000066400000000000000000000113531404105311000165200ustar00rootroot00000000000000 muse-4.0.0/src/svg/mute_on_X_waves.svg000066400000000000000000000113521404105311000177240ustar00rootroot00000000000000 muse-4.0.0/src/svg/mute_on_waves.svg000066400000000000000000000222471404105311000174420ustar00rootroot00000000000000 muse-4.0.0/src/svg/mute_parts.svg000066400000000000000000000350411404105311000167460ustar00rootroot00000000000000 muse-4.0.0/src/svg/mute_proxy_on.svg000066400000000000000000000111151404105311000174660ustar00rootroot00000000000000 muse-4.0.0/src/svg/next_part.svg000066400000000000000000000022021404105311000165600ustar00rootroot00000000000000 muse-4.0.0/src/svg/note.svg000066400000000000000000000015751404105311000155350ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/override_latency_off.svg000066400000000000000000000172071404105311000207570ustar00rootroot00000000000000 muse-4.0.0/src/svg/override_latency_on.svg000066400000000000000000000172051404105311000206170ustar00rootroot00000000000000 muse-4.0.0/src/svg/panic.svg000066400000000000000000000022521404105311000156530ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/paste.svg000066400000000000000000000030511404105311000156730ustar00rootroot00000000000000 muse-4.0.0/src/svg/paste_clone.svg000066400000000000000000000033521404105311000170570ustar00rootroot00000000000000 muse-4.0.0/src/svg/paste_clone_selected_track.svg000066400000000000000000000033521404105311000221130ustar00rootroot00000000000000 muse-4.0.0/src/svg/paste_ctrl.svg000066400000000000000000000320721404105311000167240ustar00rootroot00000000000000 muse-4.0.0/src/svg/paste_ctrl_erase.svg000066400000000000000000000267321404105311000201110ustar00rootroot00000000000000 muse-4.0.0/src/svg/paste_ctrl_erase_inclusive.svg000066400000000000000000000272151404105311000221670ustar00rootroot00000000000000 muse-4.0.0/src/svg/paste_ctrl_erase_wysiwyg.svg000066400000000000000000000271131404105311000217050ustar00rootroot00000000000000 muse-4.0.0/src/svg/paste_dialog.svg000066400000000000000000000032111404105311000172100ustar00rootroot00000000000000 muse-4.0.0/src/svg/paste_selected_track.svg000066400000000000000000000030461404105311000207330ustar00rootroot00000000000000 muse-4.0.0/src/svg/pencil.svg000066400000000000000000000032241404105311000160330ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/pencil_move_4_way.svg000066400000000000000000000146621404105311000201740ustar00rootroot00000000000000 muse-4.0.0/src/svg/pencil_move_horiz.svg000066400000000000000000000142721404105311000203010ustar00rootroot00000000000000 muse-4.0.0/src/svg/pencil_move_vert.svg000066400000000000000000000142771404105311000201330ustar00rootroot00000000000000 muse-4.0.0/src/svg/pianoroll.svg000066400000000000000000000023211404105311000165550ustar00rootroot00000000000000 muse-4.0.0/src/svg/play_off.svg000066400000000000000000000111041404105311000163540ustar00rootroot00000000000000 muse-4.0.0/src/svg/play_on.svg000066400000000000000000000116031404105311000162220ustar00rootroot00000000000000 muse-4.0.0/src/svg/plus.svg000066400000000000000000000006571404105311000155530ustar00rootroot00000000000000 muse-4.0.0/src/svg/pointer.svg000066400000000000000000000027541404105311000162500ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/pre_fader_off.svg000066400000000000000000000143661404105311000173530ustar00rootroot00000000000000 muse-4.0.0/src/svg/pre_fader_on.svg000066400000000000000000000034711404105311000172100ustar00rootroot00000000000000 muse-4.0.0/src/svg/projects/000077500000000000000000000000001404105311000156705ustar00rootroot00000000000000muse-4.0.0/src/svg/projects/audio_resample.odg000066400000000000000000000243301404105311000213560ustar00rootroot00000000000000PK&O.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK&Ometa.xmlM0(W&VBEV7;M䳴 U>yzƞXWN q@jn61%A/ՎMGƘmAhhdl B)it}N9Z:9R,fZƋN Q:RXBQ*ٽ_V{ebWovΝ>s;VJU N *kZ[PVrppG.UupS7}+톟posg,F[9v(s fG0[QFhDh*qo(2"M&%;x0b?'IlrB~h+?8ϳs)x%]o9>ԲW?_:"zxR=$z!ƚ_{HÇVU_4nƸ9 I[a<݇&> PKgQF</PK&O styles.xml\rۺSpxIQmq$'8igzu"! Ep@ȲI_H eu8~ b⫟WwIp~."߃yS/n}\?+<NSW0AI2Xzq^Ne%_RZL0,8&6 GM)%*9XrJ)oֲ2B?YXP *X/ LPtڝkA48l6? a ([/骱`2Vx dc*; AuGHP8O5۲Z`?Jϧ26cqYfp)͇ٖ{ge| 83̅- ٶ{VZkdQ-9^4a96/ A djIO,w gw}9kqo+X̂9nQ`F*1Ver .Fk = >5ɄT0|2\ B-,qp!l*E i5qH` /!l66u_|AYl^6hC2C<O@XTP>`fS5M(2xzןcA&Y*gލz_@6$(ۆn?ܒgu^=+[ jA]5I7.x >c$N; cݭ!RPRzNT 罃w  ְ]&Sd $]Բ'=erԠ2g_}y:do58_aSwa9aO|R,/!۹!(2GUMHďՆ?>%Ah\}E`IwL&[Km")^œyu8Ǘ3C,>j 8%2 F 2L x@ oZ.pS84Y~Pee#V-]f1= /KP҆@`BYr?JO6Fǚ-؁pwHL|o>"3$yq #YΎ, pG?M6sTdNotjd|KZV} v "XKJwXhxa]sTYC*~ 8R(Ȋ%{uoUM[q0#3%QB~1K!G9JX1b9\1]J+i]`Fě@ d -N#NQ_>Te=$HH ׷ov a [j]ckh1y${Fǭjh{ϟz~,cZ`v~&M(g(uA)tF߻|z; /bt>pc /gQM,1A?02l꿭K ; se28vAQ#'` RAiZ?€=6vCc~_f"[6defR8d%/gہ/?ʪn_6'ڬPd`jd6/l%H$9y>7\NΩic(z5*>Gxck_3{Ôd;Eg 9gMZ㻑'HbOs5&#~$^d+ƞ$E.G{dU {Xad AzL 'xQ9::Iva''r+ϖQC$l'etA&X1Sݍm=;QExW1 tyvkj>E~Lu'Hx) ʰJ?)Ub Eq~J'wPqb ςWow8ӥ*V$0PןͷHvk}]ddРlMuH(@'a$E[N~y}k!0&xSakJ_PKZ TPK&OConfigurations2/images/Bitmaps/PK&OConfigurations2/popupmenu/PK&OConfigurations2/toolpanel/PK&OConfigurations2/progressbar/PK&OConfigurations2/toolbar/PK&OConfigurations2/statusbar/PK&O'Configurations2/accelerator/current.xmlPKPK&OConfigurations2/menubar/PK&OConfigurations2/floater/PK&O content.xml\돬޿9L!5ѭܤ q05LfMRV>>^L{Ĭ"KKpјo@MULMڈhQçETJ鵾reą,5 My72 aŊTZъ W@9X$y Z3)QNQu,.rfjƨF#/ZݺOJaڱVdDm~xw>ZjT¼⧥:愲wLIncj?B:*b'KpSO漪,z$Ɓ]C۩sq^X+t1snx79H }'G3k4UW7X=8S)bxWbF8 e Ĭ =Ɏ?#H@:hrC‘^iOU %F^RԘrHv)ƑSյ63' 6 Ĵqd sEi_fd:4Z ᒲ[~ \QU~i_Н_&l2r!۳rAaβG(zxjL zXg-ZSJ7m_#oE؈qU7W2ݰ&L -0ecۀOZO57R\@ q`3{DM$8K u/a'y &UQ鴯#*P ֝~i;ߢ,G+[-x$ sZЕ?J  l2GWd=oqiw5>4QT ͱhʎDS51~ǜXo6"F }f5jU3-!;uьF%t g Au[izBL8]4ڱƓ4?:֏ˆc$`ᦀ d)#?SM(#)j?m$!#Ht` Tqs 6ѭ$]D *&t.ok[QӒŀpO뚟O2uss XΕyJJDLЌ2s^f9[f*]TIݯU&KsK֕A"q"y-,wT( nPT nQ. ~7EQED!6|ޠk:3ia6?7`>٣5?X(Xsn4wM|s% 4O:ȯmf#zfCq0y6AcXȻރo[J|M-񪃙v[ ; 2r͕]X1lhk!)WYc/]"%%E]X\{7Kcn^ x-{Ibfpw`9IB0y>X5H;Puڝ q|[?4' 6Ikе-t=}Ym_ޔĴgվoKwy^ rL[ no<- bCyOJ#f>)hՎ* M|L|R`ޣV[~ `<>z΀Ǝp7cHG;Iü#x=0'Pv B'wI~I)Blܹ|`IϳZ־3z9;52' b: ;5.*@2+j8Q˩(cי~!c>ry0sߙ@w 8-0 (P!QnA/}&* I&KwAu(Zl>GBGRrKa#LESɡtzne]FP%^H>S|g7(^>X"O&Ψ +zC)YvK$Dj>TG71E;)`rY:LC(!  2EծT8խFRٞ*@#nMuAZok\0JC4ReM| yI b=QOY}DmPmfIiʌ"&O fJmθr~GD 5Q"r)!*,PF.>:h!̚Ih"Uu}|>=n߃RYeD1ϋ{a.Nό'۪X'x2RLJZ$ΫHS ʢjc8~wQiT2;0 B!9a̻[1d:>!~OXD3.ߟ\|y5>o <_(˾qc>Y s Knkh,|8ꠥ+݀O91|@ϥ%৵9!d`xRU2tO%QTNKEx2Tf|S&t"X} :_Ĵy՛"h hؗ6sNM1~nieVCrHLjteJEa͑Cz18tTXM\= S%A ZG.? UpU_x`k$fʔx^Z7nfOӌ`o̬_PP9 ,Q|;\ZD[gcd=3Mj9HǕm [y_97\Qk6GܙvH?|{ȲZeOqڵg^6֢LKJ c2n0wRHke3P} 02ΰnD~rk }Z[am`N<[td/'yLjp6b͊%Z0\10z޸'d)'^@T՞4|{_2j#^`_PK%l(PK&O)Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE ++22&&&,,,333:::II\\bbiippEEEKKKSSSXXXbbblllppp|||iIDATx՗}s0C\E<쁠 xuNcZt?dIw,EX Vr] 72N~(w=xN`Qk2vxuuʏwm' µye?fr ;ETpe 1t"[ s l T%,`kҕF ZKk Kq6V6@wٶrq(ZuuC UjhR=dTJĤ-# P(SgqdXR-چ~>Xu׺k+c[hUAX%YYǶm]snnu-MG؃geRjbjdxW\-nՆ BM]sc6P)&NfVP`E|^qU8."9笀i}}IkP\lhIVPg=QCc$P\6>BZN0=ַ6'üanz7m[ 4^PKND.PK@O styles.xml\r۸SpII&l$q Ep@ȲI_H eu$98꧇eCR ‹, 7}_~zWx6C1$8^-aFnRXxrVLc|[PO}L^`2GA0瞪2H6@$KXLhL!a_ @=\(FBm'0DŽ L'P6b@z2ϿWZN!q,@6.i&  -*-Bj0OyՂՊ0sxz?lş +eYgnM#b#-H,hU onz9.' {40cx/Afil .&gNqSSXq s?V:&l"%;i M b= k҂ҝw Uh|*ׯxd/ A,{I&=Y 9(UVXBbv0?JR} DahTl{*biBTuT)gCƳ`4.gy|ֆTp{5/G5`2 Z2Lx!<346 5pë`xT|YUkx6iaaC 0=jAp}hXQ[. !SD?c$/ A vSR?䅋(^6XP  wYΆ-vw?k}E{`dJ!Cl>)<\Q4D5h"u7x}9ir,Ϡg;B W?' w*U Q~^[ zs6b"XaUqx9 T; O֬)2ka?WFљՕ]V9hPTi)_e1]-hBeηR΃?%|cޱrm8O UޛH)@Zfo_ͶHk#^sE%+hմ dt y3y4̩(KydsVė~V8%l\߾imkكQueZzӂw䉩>Ԫ.vϥϟ{X~I3- I0f=k(dďMUS|o߼-/*(Zݼw]vJ܇3i6u3߅JՍ&7c{tWh~l A [I>|˛0Kj5,x2DSwflc3=߈?D4h\{R\V⮮q~W)6@2weS>%Hl&3rr,|oSliic(:5*~#|0FtWD}3Lyp!ƾϢo 9gMRxL@\Y$W9=j'^V=5.\}"lV jorɹob8QFMtF;G&,|$;8=QXy kN"&yXF+rs8ƽJOƾhg(SЫis 2EP:.-oXbeALPLA7ɏ9?%Nc7Prb8 ќ/NpK %77|(^WG$S;^4(Uc-*"QI!IQ`9(]Ŋ?+5>k| i~a_PKb ]VPK@OConfigurations2/images/Bitmaps/PK@OConfigurations2/popupmenu/PK@OConfigurations2/toolpanel/PK@OConfigurations2/progressbar/PK@OConfigurations2/toolbar/PK@OConfigurations2/statusbar/PK@O'Configurations2/accelerator/current.xmlPKPK@OConfigurations2/menubar/PK@OConfigurations2/floater/PK@O content.xmlZYo6~Sк^h̻2z*njg倾kUF9b٠e@d!jːB/VN=-uu,j ƾuBvsݮع.Ԋ>]Ľh<܋t AT<,uuʟ*?eI)1[QURdWN5,4 o $L6X 8xZH'bweb6~LI]GCJ` A8+Z7Ki~7LRn̒X'lki")J9wF7\a>d>4SqV'_aJY+,KO!YuvKh堤a'r)lTqvBЉ-:\'plKFU5/{MuޔV;8$("aZ^^TmY4)Zp4p/pzzՠitLK}YFAEskw Lƣ|O[&鈵Ok>퐦3¥r_ +8xM68 ;~izû1^6ONj҃6~$bkd"e%BY[T8d1*phqa3\`.N*ls"#[CP %j)8#hK#7 3S,epо?ӐnCpvp4M[qlq\K9 h8c&p*}^F.(W(4֒{ζhEh6i Ҙ-2plto ɮ Vȥ8'eBȞ,%P{^Mވdiyp*)'LBbBncñg 7O.,͜dهy?>Ț}PFûڄcNnՉ ` <КaఌQ>㜭7h Rh-eѾ}B"Z|v]n@\Q-j31^¬M,,C А6E;5x/N'I3O4j'I?^EpWُ ؆m̮k^ǜV0,O( vB]K*x)fƾ @18OZXD}jR_n7g`ƫE]wYS*]}pd 06{K\WWw-e\ÝaiNu ƫ/b򲠖gg}#sf5y`tUq>slڇ)Dg}`E#+@ *pAs;Udt d7XV?"xhvV^?I58A;6 ǻJ^Ut!g"|&g"|&e=#@w8} `6EPVL`Aܟz?3VZK̟S{x_`3{friWm@9ȟ{rO6F_]\h2gN0ޤ p{т3 = l.Q2\W=ڠ:5? .PK1 PK@O settings.xmlZms8~"fڹ!84Ik&c^ 4@xMP"K>I~I)Blܹ|`IϳZ־3z9;52' b: vk[+v]T S|g7(^>X"O&Ψ +q5aCͱ0F= %9&>zC)L삗=n߃RYeD1ϋ{a.Nό'۪X'x2RLJZ$ΫHS ʢjc8~wQiT2;0 B!9e̻[1d:>!~OXD3.ߟ\|y5>o <_(˾qc>Y s K@ݰXꠥ+݀O91|@ϥ%৵9!d`NRm͠3*=쵟!?Bwj9 ͩg 7?qr'f 'm8#lV,*jF!Cƭt2%d{z==0Rx!}D}%Q0ڕ2_ x_x{vJj[k 0 ȶh|ެ2 7dYjagTQ~ІBUmp70PvUlYzU&o5&/V/.v"+ J y5lro} k wM]}/6F*N+_߈,;߲^# fyT-~G:ma6x}{ m7ߢNijjlq|TzqetJJ8vCFҡ=uZ%6'sr-c(@m\iY$k*G+*66wU Q[mK6Im'6wьvq"x&`[xX${1wgk14B$ۊqIDz_BUIENDB`PK@OMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PK@O.++mimetypePK@OND.Qmeta.xmlPK@Ob ]V Lstyles.xmlPK@OConfigurations2/images/Bitmaps/PK@OConfigurations2/popupmenu/PK@OConfigurations2/toolpanel/PK@O2Configurations2/progressbar/PK@OlConfigurations2/toolbar/PK@OConfigurations2/statusbar/PK@O'Configurations2/accelerator/current.xmlPK@O1Configurations2/menubar/PK@OgConfigurations2/floater/PK@O1 content.xmlPK@Oe fl( settings.xmlPK@O0Thumbnails/thumbnail.pngPK@O`41!META-INF/manifest.xmlPK6b#muse-4.0.0/src/svg/projects/cursor_move.odg000066400000000000000000000233251404105311000207330ustar00rootroot00000000000000PK2FNP.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK2FNPmeta.xmln0E AɖClEE tg$H"﫧kF%ΝrC]o:e& J3X̼(.0zvl2lWi%$p:i ?V3S8.jlHK  :zw]uMnQZշNΓ>q;TJ N *kZ[PVrppG.UukQ ~ꬺ'ܚf`'#a˳V+ʜ (h%ј8"q] ^QdYJ@`E1)I0&\U c;M9?._wRj٫͟oc0h"rt{\''RtUd6o\W7c}0}PKB -PK2FNP styles.xml]ْ۸}W8h.;ITi "! 6E@c/_$((JGGr] \W??R ׁY-nݿ}ݸ?+<NW0^ASX8qVLe%]RO}0W,(&6 C[wM)*XbJ)oV NdaBm\֋*gRhvX0AѽmwfI`\m_ Gpn񰤫d2EMggS*Jvbrj͏61V52]-[0X۱,3Ms lK5I\f5 ٶӆ[ $Y.<,&,dž9$WT~a ~yxnv[6mi۶}(Ro[?G0vWꠑJUᡌB!EHkKK|MR!>L!ëײk !6 13Cas(w>j۳L/N7Tfǝ1HD!TgSlZsasQkz՟cA i,vw>[#bu-H,hQ ?޺9.%' ]50cx@VRs\Sc]έ1ϘnJsL!c1ⱠpuԜFyAa욤= Mg n3K-{9'(רz_/o;X'ow\nz^! bA(Ma"ܐRele?(R֮⣪آUVSk>d0~o4jق>U0$+&B- ~%]C6AEGqP:9 hi` u > P's QXi^Xk> 1@=@KR1@{.I9p%({xswo+ 1=kogz3F:$1HHǗ0aa9N7HoGz8nKipe rX1S֡L ŏ:Z YK< !|S[̐ND3dyr~ YYꅼp?Z`wR1gDew/'8"X JwXp<z Nf֕sy:a&`D?"%KQKSq:ZE߷nV9?܌;T-Nj=% >qadQ0c vX8ǵv]@oFO d kj;`L3̩(K+'VuF S7;z@ W2o-#GPAtݏQ銷wg)|3j)8nYD#UVVfYHw~pt J ^{=KOF*MwcOx8jo."\Cc%vO5ܴs5o "sV`.(? AoygTh'cT0JS] Tخ h4j g(I(|^@=٨W>6Vj7ì@\HQQ[6dR؛RGVrR48DʼXM1ළ8maԵ M3BڃĪIxy NP(14b )b3b驴9Į}̷dZ2b'm.Owws˴eYWAAfj#2cr(]ANg"OԘɐk;,&Gbݴe-IY)U91Z ip+Rt)o"", %&S7Yg Ux0`{_x ti˃T, ]NOf9ZJ bЃOa7fUr4و}8vxk5m6:-ԡT<|epeɞD{к$u5(}3v}|CXmr{ԭ]8cmz%?A]%azaԩBV.t7tKp14.-KwAҦB.Ag7{auu~52H_~h百b˼l} =lr5%jrڳD} lRέKzzMȱ{0pQquUԳN~~] _eBWMrgoԁUCV+;lH{ p J?ֈV>(Z=i+ f6-lP:*Q;:>)gr;͵5+D%l%ApW0˛}.o,ilrgX⍷UgLzeKfL[FF9D=';ޱ#i\wG&2}ٔǠOI:gG'kv\ '9LQ;jTFaN|~7LyrASϢo Y)ϛ90wsHbOs4&=j'^=fciЁQ]-7ʊvM*}k1YBKֲX͏ F &z ;0@n|'HrKy%'f7p.R|b#跪KU@AI>Vdx'_}7_ Qoةk##EhlCBŀT$8 8$) ,縦w*VTYYP-.APK!P `PK2FNPConfigurations2/floater/PK2FNPConfigurations2/menubar/PK2FNP'Configurations2/accelerator/current.xmlPKPK2FNPConfigurations2/statusbar/PK2FNPConfigurations2/toolpanel/PK2FNPConfigurations2/popupmenu/PK2FNPConfigurations2/images/Bitmaps/PK2FNPConfigurations2/progressbar/PK2FNPConfigurations2/toolbar/PK2FNP content.xmlZ[o6~߯aXd]8'ZE ^ Fd6/;$%Z-GKs0xssH痫,qBz'Ib[BS;J6uOf,15%e-}U"V%fڰК"=EQ hA/[կH}uWGp3f[^_soQ)w[@9J@;}J[>K=e֛_~nXpR-G˗UqT *^B>[s 8P%&lG5a.Nh%y͉npL-cN*ДD:zYH^ w2 ()Slm)C3t"h1IS5|*Izx_Q4Xl9ڊR?HQ'UGOS]ur*ku@8g]sqmݎwPKBK(PK2FNP settings.xmlZ]s8}_tgCph0›l)F iS$qgg9Z՗ONk4Nt~mN*B>2'j8Qө(o3ƮA1g뫧Ӵfؿ6BN ,HWS?$c҃t[tOo%-H<kI0}=S̲||i1y+(Bc ij)xx f%%ul.kkC-X{TdG0E{)[YL=C%h 6*աT9ԣF2Y>@.#o-uAښotaIyɕEiz@WF]_'誽OYZ{@uPfKaY8*O jH78Sۂq |ڂGn8 fP9B0DKYxoLCTQ Hth$,;Wv,@5&\y`|40ګL~ Q!N+XΫH3"jc8~wqijT2{0) A!9c̿;M={¢o~wH]8ɾocYU \R:a7>|t:gB7䏼X 򇏡F@^H5px*LI8P0_tl$+4?7K "3Ä=TrwtVH"h 8;pL1N-cgx̖:6`' \$nc 'ÃGt=4=)3ɜ ,α9S$M{1Fwf#Ɲ8Y7vLWTͨUEE9jVG>z7^#fq8a ɷ#_훑S z zwHFr9ٴmrVgTsFS?Ֆı:CTr Nˊwq }r-_5 #.F&x#[Z!LdJWٴbݾdNo,CN*>Q}~ VPKHeAL&PK2FNPThumbnails/thumbnail.pngPNG  IHDR<< ")@cPLTE """***444;;;CCCJJJSSS[[[ccckkkssszzzwIIDATx뒢0O qEc  8vkSR&}Nv c GQܗ(و=z n >`> Hߤa ȘArX\}JW \Z o˄a6&?r]2:k·%1mӭIuȇlEV_W8~{L0vCt+=zwڽBͤ'Kz姣|.'ziQb zӇ~4fS5'\hca䜘=6_U]2=vWQN›z/_$YgtIENDB`PK2FNPMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PK2FNP.++mimetypePK2FNPB -Qmeta.xmlPK2FNP!P ` Lstyles.xmlPK2FNPConfigurations2/floater/PK2FNP Configurations2/menubar/PK2FNP'@Configurations2/accelerator/current.xmlPK2FNPConfigurations2/statusbar/PK2FNPConfigurations2/toolpanel/PK2FNPConfigurations2/popupmenu/PK2FNP?Configurations2/images/Bitmaps/PK2FNP|Configurations2/progressbar/PK2FNPConfigurations2/toolbar/PK2FNPBK( content.xmlPK2FNPHeAL& psettings.xmlPK2FNPThumbnails/thumbnail.pngPK2FNP`41!META-INF/manifest.xmlPK6"muse-4.0.0/src/svg/projects/down_vee.odg000066400000000000000000000217371404105311000202030ustar00rootroot00000000000000PK ~Wx@1%8.0AARXxsV̤c|(gas L0> C$  a1wj]@3al,(u1^ly ar (wc$IVl6H0wn~=Wtc0NC!ժ ^ =FIENLPN&&u;lV-OL(BFu.\Ӝ.!Ľ(1RxL׵ogE35vNKq ,-eHsE U[zf`A[:[`n3k;4M(:x_ +eܼ#b #H,hUv ?9.'}K1{ 4rDcvilȅm\}w3ViœwGm`RPP~MDL罃w Kֱݺ&S]Ժ'=eq SZP/nL[WT~C&{ bA(Ma&|NnA6v²맅DЪcY(ccE7:5K@䳏H51I4½~FPMP!0UgerX)OS!oI5W`~1ZhGa@;$&7GϜȋa"na- q5~gOLUJu0>}>9AB,%;#rb/#mPeV)\dTBo9().ro(X4_}eR<9NH9`N `wJf?u,SyB&R~ b]Qs8/n{΍߁T0ذ&+()eTC:S ɇ㜊8X&YCnJu}fg܀AX[𱵎A&Ѳ+-QiϻsA Zi5[IWh{J~>1_v0V'TS>٠x>r2CuWCB.j)]|Vڷpl?+L/lG%[oeAA`:؞ n!cJy&I-WD. $"~6{^- m6lCPc D3EA|TKLr0/1K=)8G) xϿU91seA LR%8oa+a=ǩ3c*Bڃ#xGC0 L&14:C/~ Mb"<Cyqh _h(ojCnlaū3{f%,o  nfXA_&sX`T˩Hs 5HQ(abYo :1mLKMnvHNWUrcJ!iL<݋-\ll^qqɖȬɉnZt&D xÂW뇞F;gd_mAkl^csĝ ]rח e9 WvkV]_ X*ۺVswJ:['&@݃ggXz3e)nzBWm3& ~Yԧ .AKs& ^wZF܊ÞeUk;lH{ pU V5UiV.kBY`1ͦ*]ULś0Kl+ Q?[@j/[،iH,佧qG`ru^JEF-s_6kЧSgC'uFN;^<79Niic(z5*^@Dck_3{ÔdEW 9g90w#Osw<Ş՘Xᓗ(7k&[98Nlr9vŠ4aorGɹ+1WlmtdDSDaR7n(Dm!(Jrs8Xֻ5~C}ߕZy^€]qIUۧorOb>E@~DVֲYG#TB!-ɏ9?%NkĿ^81w<͒_RK%p [K ֠%Xax? QW?#uёI]5f>Jpڡb@*Emsl;:VY{[lq6zPK SPK X ե+TLUd?8Hiu`dp5$Pfuy/wQ3LL떪u}E$Ě8:ooI2x|c3G:=w/Y3JX[w6gCyPH^%+nh?40z04;V~԰)`FmCˆ$x-2jz&V4BDf.,D/_X1xrҐž ip"#;x7 >plIGBw֢E+(kYukF &&AJ0˻:ث B*ʃBa8,QG:5>I d^pm&p@d66 | _E9}s 3$y&ijy(~Ρ{ !V7^ғ\Z`G 7 ?W7sQ\?p`Ah~w|K*k|hvBS\Uk 4AW?V;|2ʘ*hx  "%҄W*I~ Dq<ƅ0gnw=m?<36i<.#&Q\w {ᗼ$́^Wx%j 9\W%2H"o6f׭q^*vy7+XޙiӉ$˦!$)oPKPK 4`K+]{t՗GN٩;AaS:7r_ʿ]dN!*I56c:/ Gt}qP4 \iEtJz3QX ˜t/ۡ/|Yq(9/ﶇax~>3M}*?b BSɬRxy[fCeQE\$xQ' 7@,2[|&"Y]/y͛#h ؗ6sLN-1~b(їݭP9yhKb-Ǽ!B4i4ԪPQMYB S%LPp-:+rlg@:%ERx8f7 Tu7أr&s pk!5dٴ#tˌuLЈFġ;Yili3_g`3K7IbLI n# FUS3 HQJp/Aw\yrhu?!F<9uB2g-v>W" fuo8sscKacfqֻu1\S=μ>6>sD`!ݡu;+^ԶG>V{9`֫'r6۳i_jY3ΨfF<-mvF߾ +1Ƣ3/7;'4\DG&Qh0E8|eNw|ԞN4Cd{_2j#^맂PK_,Yl(PK , NpKYteCۂR#xdȃf!`c &3&57`{b+O,, &H\y\? $^{ nO+.݂9(؇g,ث='⩒@v+Gz0\0UOCV'n_0$x,Ž8}aV< ] [8mog1}rB:w1nb/eѳj&?x62%Ԑ6oWxlu& 46wfyIENDB`PK  X0r>^Qf@Ֆ`SFhDhHƋ^QYL[l pFW)~'G̹Rl|RNmlVʇˈvDA^qpjkH!Ew_U @&q`]?8+ qݓ6wa³kPK&b/PK|(O styles.xml\r۸SpѤlKM&O8igz"8 hy^$/ H$ʲٙdsqxW?߯S\D'([>~Wx@1%8.0AARXxsV̤c|(gas L0> C$  a1wj]@3al,(u1^ly ar (wc$IVl6H0wn~=Wtc0NC!ժ ^ =FIENLPN&&u;lV-OL(BFu.\Ӝ.!Ľ(1RxL׵ogE35vNKq ,-eHsE U[zf`A[:[`n3k;4M(:x_ +eܼ#b #H,hUv ?9.'}K1{ 4rDcvilȅm\}w3ViœwGm`RPP~MDL罃w Kֱݺ&S]Ժ'=eq SZP/nL[WT~C&{ bA(Ma&|NnA6v²맅DЪcY(ccE7:5K@䳏H51I4½~FPMP!0UgerX)OS!oI5W`~1ZhGa@;$&7GϜȋa"na- q5~gOLUJu0>}>9AB,%;#rb/#mPeV)\dTBo9().ro(X4_}eR<9NH9`N `wJf?u,SyB&R~ b]Qs8/n{΍߁T0ذ&+()eTC:S ɇ㜊8X&YCnJu}fg܀AX[𱵎A&Ѳ+-QiϻsA Zi5[IWh{J~>1_v0V'TS>٠x>r2CuWCB.j)]|Vڷpl?+L/lG%[oeAA`:؞ n!cJy&I-WD. $"~6{^- m6lCPc D3EA|TKLr0/1K=)8G) xϿU91seA LR%8oa+a=ǩ3c*Bڃ#xGC0 L&14:C/~ Mb"<Cyqh _h(ojCnlaū3{f%,o  nfXA_&sX`T˩Hs 5HQ(abYo :1mLKMnvHNWUrcJ!iL<݋-\ll^qqɖȬɉnZt&D xÂW뇞F;gd_mAkl^csĝ ]rח e9 WvkV]_ X*ۺVswJ:['&@݃ggXz3e)nzBWm3& ~Yԧ .AKs& ^wZF܊ÞeUk;lH{ pU V5UiV.kBY`1ͦ*]ULś0Kl+ Q?[@j/[،iH,佧qG`ru^JEF-s_6kЧSgC'uFN;^<79Niic(z5*^@Dck_3{ÔdEW 9g90w#Osw<Ş՘Xᓗ(7k&[98Nlr9vŠ4aorGɹ+1WlmtdDSDaR7n(Dm!(Jrs8Xֻ5~C}ߕZy^€]qIUۧorOb>E@~DVֲYG#TB!-ɏ9?%NkĿ^81w<͒_RK%p [K ֠%Xax? QW?#uёI]5f>Jpڡb@*Emsl;:VY{[lq6zPK SPK|(OConfigurations2/floater/PK|(OConfigurations2/menubar/PK|(O'Configurations2/accelerator/current.xmlPKPK|(OConfigurations2/statusbar/PK|(OConfigurations2/toolpanel/PK|(OConfigurations2/popupmenu/PK|(OConfigurations2/images/Bitmaps/PK|(OConfigurations2/progressbar/PK|(OConfigurations2/toolbar/PK|(O content.xmlXKo6Wڢ.YrjbmXn7Fdn$Q%)?;CI4e[GrpL 3CҷVL*.|UHy/_'7"xT$M*MQiRq+B,ufU;{&dDMЎC+^.FV%SNbvj06T<USKhOUr\]L`^ATSlgl<onn#PGT%+)ҴڅyҗY Y|rT3 [A,Z%z~Z4=X uB`W&z-BR9Ol39Hlr<t*.tU~!v)h-6LrѢk1aU>y|l/o:,dSULqVygh( Nu]uW nmnY l^5\70f$`CE* &K*'$̽$Զ: 3`V\O]WՔOLN#p4ld]%mpIxBdN|?(| ^K'gb|Jw=oT`'2ߐ&,)m\;cw?p)3ȃC]Zp T#䬂fHQjN0[QMp鮁lo| >-NG"^ÝW[s!3%U')xjy?hq_(h<*>l#?=;˛ʗ+w$d5 {>hmZ5. hN^P F;aFP >C}zz##}gW]:k"//m]EK)-eӗDJQZ"KrxPI0bQ$vmx]f6IWo'83N1# :͉9·NG}ӬCss-ni>Đ`|O/ ^1k;kС=;d h@*!hͼlˎ馕xGsQv̶`Ă}PK5PK|(O settings.xmlZr8}߯HQUEpeT”MP"K^Ia~%Cf2!-jwZ33z9;52' b: vKkk]T ̭~gsM&@DA=c,JB$t/סk}_ u?Ps^c-Oe4<>3M3}*TG71E;)`;b,t@ `jU_*Q#,O`S Qed -ͷ5.]F\urzA`&>ꢤL'A,M>!>E}6 No2#쭈S/ !zfj3\=Q!A ~T\` h OUwf$4Ay ت>>M`D EpesYErr1Qme@;e?3f{DOnbmɐJZWhГگr O(,GzܧGCDR]$( 4z 1nIǐ٢Jxaàxr{N~ՅGY#r( KX*E§W-]sQD~?/}(X R  ēJ%ʍe6P"J$Vԍ'CyjƇ1eB R՗۫eLW=AK`@.Ǿٞg|hHh/vo uG(sD]vBm !Ѧ[yѸ1";PV̊plukZ%4Tw c#T);?i| ,72u Qd<\: 0IFV/)2a"wUJAPU{: =ڐQ1EܮWPKxrl(PK|(OjnHHThumbnails/thumbnail.pngPNG  IHDR<< ")@KPLTE (((555:::GGGLLLnnnrrr |IDATx0 E 5*.\ ěl9M;ΨSࡁl` B3`Ҩx\ܒ$4ng43WxNVpgE$Ac,ŋ5ٞaUFH4?i_VA6ai F6olIENDB`PK|(OMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PK|(O.++mimetypePK|(O&b/Qmeta.xmlPK|(O S Lstyles.xmlPK|(OConfigurations2/floater/PK|(OGConfigurations2/menubar/PK|(O'}Configurations2/accelerator/current.xmlPK|(OConfigurations2/statusbar/PK|(O Configurations2/toolpanel/PK|(ODConfigurations2/popupmenu/PK|(O|Configurations2/images/Bitmaps/PK|(OConfigurations2/progressbar/PK|(OConfigurations2/toolbar/PK|(O5 )content.xmlPK|(Oxrl( settings.xmlPK|(OjnHHThumbnails/thumbnail.pngPK|(O`41aMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/expand_left_right.odg000066400000000000000000000223741404105311000220610ustar00rootroot00000000000000PK,P.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK,Pmeta.xml͎ } &cd{JE0) IӉ,s\sSG/:etHA$57Bc>-x,YqɄ]#UڱTN9F:93Գ-i6;Z_}:Nɲ 3*k;[HֲwplP=Zi処WcY2\BC3Uس VͽezU(!3E땯j ri r9|xn CY&S]B؊ pڒun(r$M0;28Ma;ItFW)/~O||^1RҕG( jc'up}qI7h[k~J ~xשZ@:8:\>'yţa az: @*st}tǗPK/PK,P settings.xmlZ]s6}0ۙtɦI1 |C&[ h#K$__@%@;!-sյхs .0s#s p`e|-rÞ $"t*۷""EzZqst1"[^Lo33)b.yxy4wQ(rP# >ps&91jt,2DOr'Eb1r ='F#<4X,ܦB\c FanY)$WY)霈ǰ!V؂w#L [!YK$D)j>TG71E{)N;rY:L=C8! & "թq0G\V![5DScKUz>=^ۥʎƘ?M?(/ + p~aĭt?RbɐZZ+I[s9qNőrthHi*Bf&0)I1(٤*aaMxqqؽ${@~B*ƍd1 N_X*G:§W-tN~ˉ(@^?} sYD J tQʤH*'|Q7 w@R >izʬܭ._bƼ9ZT6qCk*%o5Rv%V h< 4ԊSn cjM1՝U O'_ ,koe@=*qj 328xLַ2cʙ }NqDtZeJmEPva͑J<n86Զf1{@F^5"hH4V1f+$J@ivc:JL9g|L`$}sUs`=⣤qY% (wa,Zy5/8iƹ3)Z0\),(i{žQm϶###6 UvmUSׯIYaL^!a\eOzW,7̮QhpbF[Yway1k;ywl1P}F_< a"wվN+>ܪ~4Q-캱Q%Uhk SnP2G^[cۤ( M LJ7^GA{B|~6;h7 \+_$juԐ}J]7U )'Eu.d D QO )6%9ZIu |oO]\3$t?ƀQv@aY|"Qfneث(<m* ܦW 7\ClVn 5uqqC;1-@W )1'M<1XĪRwߝm?j=[UFe&aY_ig4hX':֑Q|758=w?ֺ8]n<ֻ ?(r4z*ź^cĜ4fPt:U}#7<("zvH8"}l .tl`q)l?X-5RG4'J.E s" Q ~X׍Hh*=K8ԫtAM&陡"mj+8pxfwBeT` 8 kEn'u0> + C{*JApӟ}[Sc%+I[&}11BbI( zAoF*@rc2_/+g"a^;V8l?~e_N8^[h)Ҩ 1$;t%Eb5yDҊD*Ug+dl|-mXH / o GSȕIUћ̩-P8s4l{.߃αƓk8[QM`SsqeY,\-Hf(\w{zpE6mՃQwbdWF{3?4xs ^C14P~Wh /Ưˣ14z\Wzqz+P[b}b^N;KX"BDTReE *9"JQNe]h%(.[ ꡀ؉i+dg|ztnb_ōk`pq# Hs`j R˦,Rvbr1X,p:}Yc2U/ACԌa5N ['5aÁ;G&,|$U&&1qԝKݸ&D8L&ku}#vݓ&Oքtp}>wan|C?XE,YX=lJ^3f:&IvBeB0v[a@[CBV ^̈L;lk@Xxx)^4ſT>,7ڍ\N_3#ύ;20;)*:lGe^@ʆk;:]uT7+u rXm4g_WEg&@݁gr25,uS넮h^j_Uyft WLåpp=p>OkF[p*>˨a@Pjw}PL;~Ott[(+p\;&{.tnt܂ϰMvLdZMmRw{"1ϝ!IvdWMs^U86c3"e|G3c{`?] | 5:m^;܄ 3xlVk")#/Mb^ E AxgP4PpbߖL|)>8\ZI-8@"s~[. ߓ'f -".HšH`ǕQ-gR韥Z/{ZJå_oPK0jO 3QPK,PMETA-INF/manifest.xmlSKj0F{KmVEɢP3 >!}e'.%%065BDG {/ҮEnXEALr*mX$%) Q&-j(ɟrdv3kv6NnԹLJeB'2uj3in:4Pp 1W0qWТCÔ(H :ԑ%:,$ŋ;xxhJQ>Q}Z(@i]:7LÕrP[<_&G"twH7M!֚_{HN_4n9B;! aË[^PK}-PK0LO styles.xml\ے۸}WqHJvs[c'UyڂHHM fyK+ H$j4[e砻A4[z:n!)ί,='8Eۗ+׿{ Yj s>Lvb, qsz2E4}5h}W$aPr5,g4zY# (Sp&ep 2Ϡ9MA`әc̹$Mfskpz݊[G4Z4뎩IT$ԙ!mBY`=\jՁ*:_?ظei˥dX IⲖrpf [$A \sYslM<- H]x<&,׆w$wL̘~t~j 8'-`h`oj mz3Ȁ/ҋ̀8O 0%7U4yUݺ\HU [o ZJuDyLTe5s5$L ׷ov @@}gidZFxT+ݣVo?ɳB۳j-պnݻB+]7c%̃*p` 'n#=D< 1m9Ek̝F΢6`0A1Ϭ0DK_ŽH|ι2FSm}\P`N4mcCˇ:{0_-?{dO?GIb!GqdmǷ n f{v!dЎ&!O!T" hFnH[O]^w)M/u<ܥk {aU~?3c达lxOLu[`&5V;8KCPr@"6(T lRַ7oIU9`;ng|k={"'f@-$"H EWqGs& ^RFܚÁeW{lH{ pU3հiVe.BY`1ͦ]hDTP8>w9e]|i"cy(5cҩ޳ޢT:#=EOic(5*^@Dc_3Ô䀂EW 9O17 HS`F28{<՘؎ѣ(47k&[9$N8\e,|;Z7A +1WlNF]tbw9IBӑdDSHaR7i)D9yPF9:N}u7.G/íXQwO?&B/͛8$v3<6mօHElxwQU|ͻpRL4{ VEQr,d[kHAaOF[qv;8)uXj 3-fenLB]+"D jc 5l9aF Q=G{;?/˦bE*fۜI"T%fѲŸ@Ħ6D 8bT?s^M[8p?,y7B8JQ0)E 6Mx~27f3caQ?ކf|-=m,ahHֺJ-3IIB;L+ 27tl>2i\GdliHHihC;8vkfa nz{.VUI#= {]pػ>Ok$4|oeI||-HnK1=hl}wֻrPy{/wUt]lBGi ~-fj .t);ХpN%v)ADTFvm89+,8S:=s6<&qmQ(϶m{&aF0ɘ Gq< tvJ$XZ{0*i#|h|N/s]^[` _yBA:r#|oyGq^ܕ"?./[[ :/_̭<#Z: wc#!C<1CvO͐;5~|ʉ;T|}Ò K3'f!1K7 JCGq= #Nz؆fN]Y!=#f'0|/J;Sr˫k-[PJǂt'".z@7#yޱ"5AC2~@9G #Ʌ:֫:n$Ȏ\҈ }Oi~HnۊF&}Nsׂv;aaH?}#3Ȧ۾ f/?BH?hw2OF7p?w'dʹ;GswrNɰswrNܝc˯28oqw~qPKFR,PK0LO settings.xmlZ]s8}_a3!84&c |Cx-ȒWc_!mJuv6lsu.\}Y qΜZD=1]g'9{x*B>2+j8Qө(n3Ʈ3s)b.DWWOUȭ>gsu&ȀHDQzE=c,Jb1V&KwAt(lGBݏUrK(NOB>>&Y"tO=FP'^H>Sz0(]^X"_&Ψ+>a}̶y?am9doenb*3kw!7уR1mˋRͷ?t(|ASnRh:sT!QԀ**2%gQ6.c)="P|3c7D`4.xÔ0C-g s$Sns!O:\]4a2cL($H$KCW`W1|t:gB7䏜r 򇏡Z@NH5px*LI8P0_tl$+44K1ÄTrGH"h h8sL1bh1~hj5d@zyҒq?&z2{4KCݓ2ce=8ǾbNMb`iZ1(׌tc6Y ~`#u_J&,`T0`u r4X/^w ձEa[O {U~8MmquC@t{V xC]*6|6uʪ PrSRCq읬G@-9g~p>= HT3gA%f=Ђ΀me?>7lg:Tn׶~ju߷ZV_XIˏkp,w=6ZV?R agT~hoy?&Vo?y5B~sX#;iӚ7-WXQJ(Y{b:WS~@|9y0n:j7_ZcA~ 0O.5m]n]VꍆՅ3v snrNʊwv=r-L_5F& ~ &Ldҕ~Y ݗ x@UeqO>S'ʽOn/JPK~BK&PK0LOK1--Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   $( *#-&1(5+8###*** ,:"0>333;;;%4D'8H):L,>Q.@S1EZ5Kb8Og9Oh:Rk>XrDDDKKKRRRZZZA[vB\yD`}dddiiiqqqzzzFcHeMlPpTuVyZ}[]``beghjmnq΄9zIDATx[w@7N %.5%ME1d[d9mW^zÙ}?3<_uHe-xm @Zv,H_'n(t5J0WH-ʐ.)a ,PK_AF:)N h\RGʷ`'#ba-b,K=f | /pKOb kdxZp}D8RR5T[j5SYn# _<<NrU^o5C 9y{E{9unrU˜e2 K%&i st( F}}!WIs䐴C!)+m+Ɋ<#ebt|?W^vKULO~ؓMȝ "w܏=DZgY:UrNrOX`jK{˃ NDQ]n1tӫR9tY<( ? ݔنmN2||[u`8Q7fpb$ı,=G̖ܳQagasB .Q" $Gis:joFX+xvϔ+EE8[F7FzqTxn1pCܖ(q֝ݸ~ l=7OX'?<ɂ;{hPW4@Pc  f.,y f.A47=ƫxv8\o<ƹI'c۶?w{IENDB`PK0LOMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PK0LO.++mimetypePK0LO}-Qmeta.xmlPK0LOԡ U Istyles.xmlPK0LOEConfigurations2/progressbar/PK0LOConfigurations2/toolbar/PK0LO'Configurations2/accelerator/current.xmlPK0LO Configurations2/toolpanel/PK0LODConfigurations2/popupmenu/PK0LO|Configurations2/images/Bitmaps/PK0LOConfigurations2/statusbar/PK0LOConfigurations2/floater/PK0LO'Configurations2/menubar/PK0LOFR, ]content.xmlPK0LO~BK& settings.xmlPK0LOK1--dThumbnails/thumbnail.pngPK0LO`41"META-INF/manifest.xmlPK6>$muse-4.0.0/src/svg/projects/extern_sync_on.odg000066400000000000000000000254071404105311000214300ustar00rootroot00000000000000PKLO.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKLOmeta.xmlM w{TUU[ҦRo :gl*2=*x)7!PH͍P܄?:|?dEqɄm-EKcӑ1fz0M#-!AiΚJק)ZLcc!b.5l/ѻ뺨PoJvwЪu`/,_liwCkbJ)OT#״) 0\XWK ,nV쫳 ?4;i*O> X0r>_Q@1@-,#SD5~E&8[`z"RJF\U c;_ׄ<6ȹty\J-{[mQ/':ݥIpk~Ia?62Nyu8x0~/j I 5^/PKqÞ.PKLO styles.xml\ے۸}Wqxe$-]\lIU `SDJIFxиhՏĺ$G8+϶`o_~vf 1 "aiCs6N3+LVnysWe͍]tAE 60_5U,jYP .DaAiL &,wSA޽iwz`^mG ](s~]lZ[J4›}J8N) c~%xyޖպH+_?TؚdjEd7Ezd La& 1[x\$ʄc\Pa16 A $ҤZ w+cwñi8mr 6m{'N;#(֠JMg }oݞV VrYI..L 1w+Uc s!6q 0)*cKsXa6ܺaWJcwl{hK]2M<A68!r >m}fFM=ؖT_A _]?tǏAI*o6 k})ݐPt~2qGNVk&`JR ހ!"[ sOv͍ crp$1w+{?!M`JC^ͣD0p;xR g&}y d o&TgŽժr:[oW[d|]NCWe҇}!-KF(χt ,;,}H0ݻ~YE7"mC{F5 ]G' DlǤAo?%%m#g xptK? :adL =.`zN'-`z VxM6?`mֆ N-p7~e[*+?~e))N`c /))^XOu4.)jX/^_}Lc.Y¢"^ MO<ƞTc&#o(=7|Dd",bH<˳yT m[!],w0βǻn)vqcND9fR'KZSp܏rJ?Y6qMM3mC:!Cd Wd ^$?קP?=?!C/O(o9jZܮWoMO;+X"BDTReE"CK9sAN/N1ـ X,SB#pY)V';OX\֎C َLGdwjvT˟(SȚ.QVO,١ljZ7*ғRǂlMWjJnɉFW@3ԗa$W+nЕH쪾x4@M 'k6j+ -*OWL[흽ǐ4"PSgWlY__:04@D7JZ[)ih ^ރB{ I=)f_v noř*Fv.`І! !"1)Rs1\QXs@X`2%rtf3~z}` iTCg3~ʜLX?ƪs'9*{(rfJg\c6n$x('dK5ܷeJwę c`q[SwZ,ŭ0'TE)rfOA/"tpЕ7;r3:V'oޘj-:vpi.1^*G#A7rL@עA aMcBG-jF:3<&wc{l5?tFqᨀ N=J Ө]Gf[LQ!.|5%N͘Ҍr̍)'78޳xj\+N[lkЧS޳ޡd#/zE.ri玣(*c<̩u劈La9 !Ϣ `gM:㻞gV$vOǓuUZeXb9?()˸C  Er~='ֈ^:8 }mEؗRw nrɊ &Jy+byouz&2"hwTG Nz$HxC"2N]Z?kվh}8mKѯ PKa) uZPKLOConfigurations2/toolbar/PKLOConfigurations2/progressbar/PKLO'Configurations2/accelerator/current.xmlPKPKLOConfigurations2/statusbar/PKLOConfigurations2/floater/PKLOConfigurations2/menubar/PKLOConfigurations2/toolpanel/PKLOConfigurations2/images/Bitmaps/PKLOConfigurations2/popupmenu/PKLO content.xmlZ}@+Cv\+\%J͞$(i_걲%tuZ37CۇmOL\w{Ktug7o>VD2At/+\EmreYջYchʓ;RМ˔&,_<]ZK`5'<5sǘud堨bV2 v 6k҂ZOmku "ba+iӮۻ5KӼ2o8UL}bWF9ɨeа,Qg~)Ժhb;5w Two$΅+Bp/b ^$I9f+; F.pO2?/AU[S"QaRl1QD,;vBZi"ҵ!"scU ;n؊ĈgqûvR+ɡRLlAwmc~L(Vof, "'K0h'Jif1CC޸vNܝIbMeԖ,h>gRW 诿(0۸2ydRKiHQ4v؉a nz:6FUͽ:or~cE4`VȂ86w|aT‘5Q=DÞ^5h;QZ6C;/8̞jqm}d*~ ~8bƷp'/C `g$tO쉦tqoh?࣎7dlds;AKgǿ [1beջz+Nla涓1wc++ك$f?=ax >>7il/TVI5Q Dgr >p{G8GqAU ";L"?/_VqL.!!{{z0N}G>ew{T|E_|9JP:ޒPO:@:Wu\=ޑTpݎ {s Kz$̜gd[&=Aɵ(nadz2̩k7CEAC_?SC\ ple"ՏjWr-6Š ;rl!p'eRRۮ/i×Fm'4;|Vc$z~pgdّ~_&ә3sp?w'dʹ;GswrNɸswrN;𧑗_epHbcPKs~ .PKLO settings.xmlZ]s8}_a3I !ɖjd+1_ɐ6%@uv6l{u.\~]qʜZD=1_eFC'%%*B>2+j8Qө(o3Ʈ2 )b.DWOUȭ?g6sU&ȀHDQzE=s,bl k 7>a 8~lkQy$=clYPw Y"t/{k֣7x"L1Ӡt~b|#'˚*Q#{2:g Yy)W̔/sQބBwrt"|\B_XS7/vvՑ b m*^xz(j-+* 2%gQ6&[.c)"P|3c7ŀ`4>xÔ]0Gm s$ Saq#O:\]4e2L($H$+SWr0GY>@.#8n-uAښotaYIye£͂Jm| Ч22Ftu> Lw:̖jvM*&ߟ!A _oתpn*! q*4PBw?DKYxoLCTQiC$Y{ N o ɕa.^Ϭ'S;J2<ACk=*qUDY,67qkƏQc`Rn@Goz m7xwH[ȾmcYU sKPݨWxꜥnɇXr 2ƀj0T07UJp0_tlL:rϟ:% XaBTf DB:8qq<%'ts,|L녀`4=:Um ~mPU PrSRGqlF@-9gp[ma $j q LQ[>odAgHʃC'ݶß3{guǺ -+/ٴYl-]{OBX*~A&;.?xw:!?Rw 3^~tN,OYN;qVԪڢ]jVG>/FG̦3qʶ''gGx|L:;GNOw;d'fà=o]n=՜G3q wQeŻ>tgm¯ jcx?^V&*SL,>K< Rwzc"urK&PKLO{bDDThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE  "# -, 51 < 2<###***333;;;A IM BCQQT ]TWPZ^_ k abllp { tw{{` j }##})(DDDKKKRRRZZZdddiiiqqqzzz%%++ ,,"!21 %%%$100099('&&('++::;: ('.-22<<33==%%+*!!,,22<<10>>  "",+0/433288##('++0/4387;:>>DCEDIIIIKJCCMMCCKKPOQPQQDP7IDATxwT(e%0]l{n@ ivUV:Bm( _#$- Xߏ}w 3vs`xCE[xsVVWMO4#3kqώ* ? '*b]Kb9k0':$.^$ c=HVr٤",Kg&0sX1Ö$ :bwОqV*xt:{ȢrK#caA<_w cr8eD.J$HKag܀UcBa #(9 x NRio ۤ Vž}C8Lfl ۆkXcɷ4 XjI2)瞘CUX3(hdݻKֹM ԝ1(-zī;X2â`{.%-y<~'P;q9TdelIoӧŠ`,^|1"/gxʕMJc" ݖ˕|~K& Rsettings.xmlPKLO{bDDThumbnails/thumbnail.pngPKLO`41D%META-INF/manifest.xmlPK6&muse-4.0.0/src/svg/projects/fast_forward.odg000066400000000000000000000176341404105311000210570ustar00rootroot00000000000000PKM!L.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKM!Lmeta.xmlQo0+Wcc i6i76M i*/߹x8vmµJ 1dPgCQl踴T:o`ВCeqC-r5MѼrlS_q1>(swagt멆!(N o 5H7kͺRz tqb!S]~ciiOc<eʦ )Ls :C^CC|%MbJ9&Im tEQ4. }(LIn(p-a͠}% q)dYM Nt(G0 ݩYK`z~sfQ;|im\4 u*-*Eѵ^PK{ݫ-PKM!L settings.xmlZ]s8}_tg4 t St& h#KI1Y 6!yȇ-stuAο< fR4w"i{uk/_dhP!S` 8]uӋhHnn!#iэl3fD ߟC~OޮF 4Bޏp}:m ńMEY^/|m',?>:/^!rٱ@jlE cVT[ًt\ka85f >r>R6_˚L[K%k?(oBdANg̲ЏΥѯMg맧gB՘ t AWS}üG7Tj ufc)9&k(URl  eɃ7*.> S!jʄvGb\ t7 U9@r5n9Y8t_˥"!5d{8 /i[{\.VnH,0m >^Z_Ŵ 4俢/GK_m`boeGr-}wsPFx3TΤ£S ԂW Ӊ+H=})c4rq抲(\&!̈½\ 9<{ *\a]}WmؑDi^?*8!zs]!T]&|?nhVHk$yaǿnIhiwr+yEw)ex'2k"F'P.}` p{6h0wbٷQ%1| .tdᾟӧxKh3GŻ1 `qG=r\L "`¦:2KxJs$C|A11D*B j A9C4U8ڕn֯PGrCN&&~WgE7PX&ޙkؒBG\s <(9v?KwqSkF)$I{5跮ڞ Èqpks6)*GzJscb&'NX;Au IfU`.3U$ 0tu&[L83f/I`A0bl퓅lzf5L$2KQfej1o/o|_~{':\jgxtSV&лbŸ1A1mXRN[yPKle!PKM!L content.xmlXKo6W ГvcV |h""W)Q } )K!M`oݮJD Y""cU |]V?݊ٷ!3d|r;BZ\T0ؓ# F3~$Ԯ@&]ֺOBfY(@cmݾ eb>R `sT=4.Yޚ: JfD[t`aǞAb/ !RŠ:=Oi%Y~l"J-9>|,2-Zw7U?״Bn#&1QF W۶?]=fg< ^ h"=~k~=µa00mQA+H pޝCw'Lxc_;IHq=B6<>ѢXjL =0>n0]D֎{yAlYúoƅSBkFKgQd ,v*Q 3Qe4ڲL,_2EDpobmr3p" /F$qV3ce M~a&a %}. =B† f<'4őDb ]I9~ն3QysLs@|&} 5464z:n1}uk2|o=laIOa{e:2A^oF9n{ h'6qDܐ?-eo밦maI.͝Ս[7a0A\-.$NSk|Vtq_YpukRa/^\z@-4`+PKk,DPKM!LConfigurations2/statusbar/PKM!LConfigurations2/toolpanel/PKM!LConfigurations2/menubar/PKM!LConfigurations2/progressbar/PKM!LConfigurations2/popupmenu/PKM!L'Configurations2/accelerator/current.xmlPKPKM!LConfigurations2/images/Bitmaps/PKM!LConfigurations2/floater/PKM!LConfigurations2/toolbar/PKM!LH5LLThumbnails/thumbnail.pngPNG  IHDR<< ")@cPLTE ###+++222<<]u5Dgppiw;n>Wkhܸ7^Ăeю82,f3 .uª7,}þ,UbBB)D RM LK " upM:Eka\J$&qeèH<x/ I4e .,P{&~rXQx7Yfm=q}BIm9:kS"?Gg,;֋ 9g^;<'.$7.3=u/nOa{;Žv[{[ X o_7R)~Ulٷ;d&bw.3`v]v]k E%x ŨGHIENDB`PKM!L styles.xml[_s6Oa}Iqbbwt2smizځHPB|>OrA)SlNfl .7eܻd"'g< WW\_HSE"* J@8/xW2_Rr /DAs+pz)3'+]iEXaȒ5+H++w%R"+b;ZqJ0l6gٙpryyjjp+C).VIhy3XU)%!j!i ,`.帉\ݮF{jxMh?]W%]eQoƮbɊfnW^Ѩ&ص(ý9ȾLQcq|8z.؝1>jp#JPBHOʴIkᔁT˺I BH-oip.wAD4{ Lyh jTTybH *b nqS3CP`Lј;AV,‘diuԿ~*OILƼ~cu3Ϩ+ԯ> Re^ߑB3יj|)2w8 bHD2x`tnD?T1sxFTnKE$KrOdq?K}IJ6!=?}xFW'r!%zTo05H~~Rlx(ȋi"3fcX* 22a)ªiVkb6`M]Y$pH%U].qK_D@wS8+tLJ\6E7*q.hHWgp$i'K4{ ҏMַ?~fK1w(;d8L>%žZ^B!41~_$BNEt<}q2/4O_' :k%j\WlPu{kCBMQ J5J=kj'Q1@0b{Bȝ ΢$yV.r: ig)Q)TV]V~.Ϥ@gBWxMך\=3f-j =+6i>.&vMv:9r:}mBP<iIZz@@u.&\C}QLyce>l4kb\v/$匦=)nN mzp`#Ն)8hL fD6/{6&H5}]ׇh&h3ӏE$DŽjdڥ91>0L)UVis?q. Zoֆ'9\x(( =bd8`Nkh95=|& 9*l>x:.\yD-lY5&1<h9l||쉏Qd."cczvL,nnKQ\iǗ_ rnl2*h=e鳣5%s_9FA_99=Kz> X #yO)f|x/ORbi+7~6CBr|Mr@]_.×,+@]lgu=<\pG[^nbѦ%mk$PdJ]zIcŰs$^ &⫧+[2SB0ܮRiܶOQ8j\;t?a|T ~X5Wk61yZ?[ﵹM zsSw wPK=J7PKM!LMETA-INF/manifest.xmlSn +" %aҾF S5?mSfcg?V'xl+aew)4=$*sirDU2Ir$i`uv$˙-lnSzc.qu:(:L=;茪i 25ZQi',/u!p4:1FRxLb*M@T|HOiٸ{388m,GssASIφ%:1/T&A0N :iGwPKH PKM!L.++mimetypePKM!L{ݫ-Qmeta.xmlPKM!Lle! Osettings.xmlPKM!Lk,D bcontent.xmlPKM!LH Configurations2/statusbar/PKM!L Configurations2/toolpanel/PKM!L Configurations2/menubar/PKM!L Configurations2/progressbar/PKM!L( Configurations2/popupmenu/PKM!L'` Configurations2/accelerator/current.xmlPKM!L Configurations2/images/Bitmaps/PKM!L Configurations2/floater/PKM!L*Configurations2/toolbar/PKM!LH5LL`Thumbnails/thumbnail.pngPKM!L=J7 styles.xmlPKM!LH META-INF/manifest.xmlPK6Pmuse-4.0.0/src/svg/projects/grid_on.odg000066400000000000000000000215531404105311000200120ustar00rootroot00000000000000PKQ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKQConfigurations2/floater/PKQConfigurations2/menubar/PKQConfigurations2/popupmenu/PKQConfigurations2/toolbar/PKQConfigurations2/statusbar/PKQ'Configurations2/accelerator/current.xmlPKPKQConfigurations2/images/Bitmaps/PKQConfigurations2/toolpanel/PKQConfigurations2/progressbar/PKQ styles.xml\[sۺ~LFٖLN2I/INI;ӧ3 IH(B?bq!A@Y]י#^@,V|*0 Moz41I7}_~~Wt>'4Zpo{sORz[rMf8U-ANnzkN)I>M SMfZ".8S$U&F%&c8rx>XQyf _op5(; lGN0,,bhwF2W[8zh5fh: sP݁+0jvD( a杩6Smu.൧OHζ$T:? *lю?OpR1an0øb-hDlN׫fs#`.7(UXbilQ9]/j4|aFmZaCX2ڡc,:[CFL!6kU'N}NJ} mO.e3^QtIo%GdPqW"ǒj"P{z^]NŎ?Gc%WfO}e>MR M°mx{h>*CP錮PZ#Ҟ!Nks,3yd8,)&FI,ޣ${yϲ ,c0_}y:A]t8^AS01bsA1̓4$IpSMM<`JXP4?$"dAO4'\[)(]0 4,A/1ɳm}ų臅LQxp9yra?OۤĽEqŽ-VNf|_uf<E'Ni"pǓgpDY U!QUIozbhkJY B )ۅ\!3[7`_&BozPO}і}h\F9Z':+d6H%zW3x'8 ^12 kڋws:݈|MJ}\.*C *SI |4ZIbEIDf?i9P-;ܤ`a$b$~.gM1gwHQ`!26GI 1MJvf vkOun|9F3eԹ`K`I:>e\%h! H8}7/2(},y`lãfШI PlHY y \WFW(ˋ0{=H p!1MHܳjCb8ѪwTtU1/ua8'0@j.^MgpBfv$_RF~P8 % o뜓V`bf`)cfsH&uqYGBY *ؘm,ǣ CrգEySƵM%p}w(_6EWԈˈ,ܜ ]qq ZewjW%Dꂼ+1w$'2 (EΒsx+m܆TJ sj\iT+W > eN>C췋vT*&ƾGs"bK~ӽHFh h"r =UTIKu!$ W#!S4kloe rQifF%jQe5`9+ʚSL!`i~9WǵEj~?O,QNeKh(;FFg5bJiMyl,CCA%3<'JQD'p їgJeY+91GB . ʩ!P8 K=4`%HJ{~$&އ??xhXW_xyҐcLufY2(pJF] Ԍ*ϬmݠgZ  %Oۡ)gR #ry&A$_){7bz6qSZYl.h򼪄L^_'A,>!6Em3 N#쵈upfj2\=Q!A ~Tඈ\` h(#v,4uwf$4Am lYf@0Dgp3\*l9(4 J Y@ i%@NHpx*O*UpJ7^cꙙD:jʩiij221Tb PyuAЁ˱/lO/#L'ڋdD;*c(줦KUM'jNc+ZT{RʤNPS:ީsbә8F ,V ZBo #Kk1XtdTף:b1M S9 !}5Ztg񇈪to0}s tx^Z7,7͞d"VZXWަ* (<,MDje">Ցs 6ӣ [ܵ!q<{aP?7ÆE5m1'0^ Fq`ktȏ%hozz$M{QJ꾭7̥k^o6 q50u=;9 лRCt S\Oٷe+N.Ò,KG~|X#;ޛ3Bus f잏մZfn朠)^]ڍRݶ]Ӟan}1̖*9έbVqŜۀ*QI yo/rKk4UY.Dg>T*R]*}Chh{ܮ_UPKO[&PKQОvvThumbnails/thumbnail.pngPNG  IHDR<< ")@NPLTE $$$)))777<<6GQТ%Tikys" ~"~&L>H.D{C7gǘIENDB`PKQmeta.xmlM0ݫ4RU[RRogނlI}@DUgUz )I-Tz7_*/?I ҈QZvzS僇jn*U{M z%C>1Ƭg[=fx$6{,+_W3QiA{![ s5H_܍!a)00ӘY{}B4snO owOf3PHb?$*)JVpB>%#4C$A$0ӄ$fINX |( 9g+8cI1ecT dgdž.~Na.$oIGJOQ =mlvqz {P;ne8m[k^@xLN)?cκaߜWbf7xu . ֗PK:]3PKQMETA-INF/manifest.xmlMn =ПMdQ'H@Oܾ؊WU }o͡BDG {⏬ҮE}_ft=a9t*bz2i/Gs5xa'i0NNBejU*'#8x8 g D0h.x0ګkuYt0E==|G]P~:2͑.Q:> 5X( B ߼3 <#se1-YRyF1t{GӍR(t䞺+&ث_CRwďoPK161PKQ.++mimetypePKQQConfigurations2/floater/PKQConfigurations2/menubar/PKQConfigurations2/popupmenu/PKQConfigurations2/toolbar/PKQ+Configurations2/statusbar/PKQ'cConfigurations2/accelerator/current.xmlPKQConfigurations2/images/Bitmaps/PKQConfigurations2/toolpanel/PKQ/Configurations2/progressbar/PKQg [T istyles.xmlPKQd^ Rcontent.xmlPKQO[& ssettings.xmlPKQОvvThumbnails/thumbnail.pngPKQ:]3meta.xmlPKQ161META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/headphones_off.odg000066400000000000000000000216341404105311000213410ustar00rootroot00000000000000PK$nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK$nJ77Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE  $( *"-&2)6*8###((( -;"0>222<<<$3C'7H*;M,=P.AU0CW1EZ4I_5Kb8Og9Oh;Sm>Wq@@@JJJTTT^^^@YuB]yEa~dddjjjrrr}}}FcIeLkQqSuVyY}]`abeghkmnq΃IDATxms@dz9 &`JLSQ֘T"BI4Ӿhgro;^| }Ϟ߆=Y$)6?5&/훳rIENDB`PK$nJmeta.xml͎0} IFbVjF."'u 6͐} :Qs|}?:xJ #R |uP.l/UNG茢RU5RǨn%tIh9R*Z aQGڜ β 3ٕk;SgPbpGPaiƦWcYZ_CctPLF:ltzcidso}d8o=CU{62#lx21f ( »! ILI8C$I&79gt%!o&O\wfU~S~Nv.6of%/|Jx6<e=D8FI|n$X蟂9Q>t!89\:ɂq_ ~x: Xp|x×PK Ճ.PK$nJConfigurations2/menubar/PK$nJConfigurations2/floater/PK$nJConfigurations2/toolbar/PK$nJConfigurations2/progressbar/PK$nJ'Configurations2/accelerator/current.xmlPKPK$nJConfigurations2/statusbar/PK$nJConfigurations2/toolpanel/PK$nJConfigurations2/popupmenu/PK$nJConfigurations2/images/Bitmaps/PK$nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PK$nJ content.xmlZo6BP%'@;ÖEmݾ1%Db{Ôl9@1$x<w4r~Mbtec$Y@he57//~8gaH} _$$R|YJWf%9͗)NHe$QK]{*GrÕd+n oϬutf(XBPuxȆyl dXЎۘ+s-Df3x##- [IF/+x&16!MCKi>84X]8A+s!ʯhpvE=aט3N/*^c,=;AޮyœsIV|N,u$یp*E8Ve˂[1&zYh)J⚹%5Øp`YǓ0s Z8RfɒEIku\kJf Oq~q^֧f(+BS5XH Gs2蕃2-:42*|(wS}pd87-|¿c p)$kNHCkz kW+XCQB0Y9|KiNzo-P3Y/2i9Fq_g˫zJYۭ VB$WQg@vZ&~b" '3 64fp=؝k@;.i7g"J"i 5&91>᮫x' TZr&4Zc8AeBfyt2ȫ%X&un:&Ifpד"sZJ NΔ|z$SFfKvƁLZH:< V) RCGwO k0X܅:>͡v/V욇B/ΕSHKgTI;ԃ*[ueq 2CĂgАb&ZGm-NjūO>fDZOԜdl<Mx1qt5`xKpn\υ/xQڏէTjɠe7]=?aPyC_2>@]cYOqB|itP1dbW}Q{N*m,z6x"Vch(uU/ɬ3PZTHQCw"M:_8PPn^-ZS kx{l[ų+jRmu {;_PK?^!PK$nJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@MEܐ|zn ӼwWPX&ޙdzRG\sKx3ykO{R8.]FFpawİ?l>PKxƫ@!PK$nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK$nJ.++mimetypePK$nJ77QThumbnails/thumbnail.pngPK$nJ Ճ.meta.xmlPK$nJConfigurations2/menubar/PK$nJConfigurations2/floater/PK$nJ&Configurations2/toolbar/PK$nJ\Configurations2/progressbar/PK$nJ'Configurations2/accelerator/current.xmlPK$nJConfigurations2/statusbar/PK$nJ%Configurations2/toolpanel/PK$nJ]Configurations2/popupmenu/PK$nJConfigurations2/images/Bitmaps/PK$nJ]b66 styles.xmlPK$nJ?^! content.xmlPK$nJxƫ@! settings.xmlPK$nJ(META-INF/manifest.xmlPK6Pmuse-4.0.0/src/svg/projects/headphones_on.odg000066400000000000000000000222211404105311000211740ustar00rootroot00000000000000PK$nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK$nJ<Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   $+25 =%###)))222<<<@&E)K,S1\6^8b:i?mAtE|JAAAJJJTTTZZZdddkkksssxxxMPQWX]aghknpuy~րۃdBIDATxis0ǟ<@k׵ţci;l[z < ڢfg^89~$| }ZoÞot/l0lk s15QGWpx)C~<[j2>Fbt<|g1Nh/Kƌ%f*eт=MgS:p+KEn ?‰{JHOtM 76\PVORvz|N+mb|9̩59Ń 17έ++-E/z)5- ߺM6  ADkE!;F"u}fBk*܃cXrA I%ol .SNa(T,*bE y4Tc8>Y,cbTZ{DS뚰^lZ wY&O vm]ؼ.[))DBh큄/i`wU;;܈@xCڨpYfQU0PEd6b@]Fl~]Fe0uܒ_o`IE-&h )fʑZ? B{'iI Т'\>ȫobmiFX=߄g[aIENDB`PK$nJmeta.xml͎0} IFbVjF."'u 6͐}:Qs|}?:xJ #R |uP.lA, EuejQ 5K蒦h9R*Z aQGڜ β ʵ=,3;&7.#Fr)XjУljNJഞi^Ǒ nXzZ)[fdߤ>[2dU{62#l|2ŒP]$$H!x3d4Dig8MK7|w*}ޤ.6of%I/|J jm'y4⋿=D8FI|n$X蟂9Q>t!89\:cax: Xp|x×PK䣫.PK$nJConfigurations2/menubar/PK$nJConfigurations2/floater/PK$nJConfigurations2/statusbar/PK$nJConfigurations2/popupmenu/PK$nJConfigurations2/images/Bitmaps/PK$nJ'Configurations2/accelerator/current.xmlPKPK$nJConfigurations2/toolpanel/PK$nJConfigurations2/progressbar/PK$nJConfigurations2/toolbar/PK$nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PK$nJ content.xmlZo6BP %Ev[oDl$Q_#0%[>P x;_nظ#<,]hI}4Z}xmy, O󋄤Y*4_ҕYtpNe/dIk^ʑ\ k C%oϬvfBPu U2zaA;^lcޮ̵Ҷ7h31hXJ87BMb"'m4BvMCXݥHn,ffHi]480kSe Oq&X{׳AޮyœsIl+T>eh]1ָ*îugbzsTp~q  ɔo DޣڥAW5IL[4N$-NT؃e2F|js1. mbIOaRZC#G:2(<1[tb.:>kI p񨩭!+Ҡ܇2dNJmٲVɪkZEffIG0f%9$2X-5vv9yHYv-v8RfɒEIku\kJf Oq~q^֧f(+BS5XH [??upiLKTY"‡bp9Ugw L|o83 3| |O9!=i&Z_w$*b{Cԧej[NsZ{kmAɪ5T}H ͡jT#5 sy5QV$kjy`v++B!\P悳[bmh ;-g䌧~b" '3 64fp;uw\8nlbؖ ֚qhF@]\p{XBR %Ȋ8XkB50f![&4mFx:UPy5$M܎ !cjw6Hx=y)9pXLّLyfKvƁLZH:< V) RCGwO kj u}"{Cg^f7,5^+$ Ϩv WU+"fe҇4b!ȡ!MZ=>X!3˸>U̚zl+'N \ٕ$]S%DkK&MᢙSWN}i r/cfZEwkʽ#7pl\xM5V p'jNZ 2|2Md1ul5O`xK3S ^SR@2(oM&eNX/TvSԴO)™c[Z7UDSFFWch-YiC%7=%p! mEň&*=r-I{CjN FD{((RP:h av^k}d@!l@4/Ɠ d:Ms`Cw~ _%T+YUT\zƞ?2}?=NgXZ+GH1^49Bl:DU'ɬ3PJTz/; DݥzTF74w!C?;y$Co"z hW,a:nck=vCkgWiWp9.vmGſPKg&KR!PK$nJ settings.xmlZ]s8}_a3t$ݘIc7"KI~e-+-sյՅ3\".0@e}f`ճϥh]T &&L#-N  "np\'~Wv,@&\y}nѝp"h4s? $* ? Q~c39W/{XMWMF# ABa:d{;1w\\?Z﻽< <'8Ns}2ī@2P+ J>}}XT'.`N25@@UQ¼,%U\i>dU &g2L@@ӗK|&sk`sflpzdq2=@_Iҽ+n@],OD2cSeLeN8sl>LvtO1z˄IH !o*͘#Bs,LP~p*o³o3I0M dmDрn!I[{>nw3ǜʮY~yimq@t)]so6Unm9 |2¬* "3OX"݋ȓhH%ί ݩ1Ax<ǃ/CM⎍?5`"'hM>3cQ^ƬBm͂ /rH꾭>5]RsDyuߵYO\gNF8+aتmߜLֵ4)pNC0C|ZZX!YFkj6j_:yOV"F̻AU)ewߨu].ufiOF@=fuggMo>xE{GZ!LЇɀ>Te*< y>9P/&s[5M_PK|#PK$nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK$nJ.++mimetypePK$nJ<QThumbnails/thumbnail.pngPK$nJ䣫.meta.xmlPK$nJConfigurations2/menubar/PK$nJConfigurations2/floater/PK$nJConfigurations2/statusbar/PK$nJ-Configurations2/popupmenu/PK$nJeConfigurations2/images/Bitmaps/PK$nJ'Configurations2/accelerator/current.xmlPK$nJConfigurations2/toolpanel/PK$nJ1Configurations2/progressbar/PK$nJkConfigurations2/toolbar/PK$nJ]b66 styles.xmlPK$nJg&KR! content.xmlPK$nJ|# settings.xmlPK$nJ(META-INF/manifest.xmlPK6E muse-4.0.0/src/svg/projects/jack_transport_off.odg000066400000000000000000000224231404105311000222440ustar00rootroot00000000000000PK픕L.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK픕Lmeta.xmlSM0W wRUU=lJM"b[m@HI7r=f{60VjUbu-ձv` 7~~\ZV*TeT(++-SU+,sNh6 7F_\!P#"YuzL#шQ"`GkKֿVn}5֜u-H0Yh>/h^ n;yhfZ)[fľr}4uZ!?UE-.ݞc 咲qe>-1y^P-%,Be)#9[)1dGben8P4UE-TBGJ>mgWk*a$~GgkS>ɃQ$UO}+3awljH2+ kdZ'y0XW@enPKϸy/PK픕L settings.xmlZms6_\g>Cn s$\{&ȒGC]r`bNgygVg7\~y 3HEo8s>ӆ8./_.dB=‹຤@kp:WI^DQU$U^]W뱱FSÙi]w>? 9u+Z͍߮Bu:e9z}╴\XL\˿WU@پؒ'9O"˜[^ͽ=٬9qxXA2qGjL% dž&ywZH^r4KZFzz&:|~ZwYYZtʿZN) ar^ߴ`>a7h*-1 J>O|c!4')(ߕo=5$ c% ^˨ L)ʞO9d8Z2x,f"uC@؜,, ]J=׌ @rZ.]ζ-+J6O6񉂳jIȅth2;R 6ԝhiLcx':mﻇ|377Lěr&$f 4 A{*[[pEO]a{if.b&6Й>,ofȨjA 4^Iـ͹ ]~>ϯ8;H+ lлw/u~h#ba-Ң+ca6~ ag= GcݓY݂>SdCpnq&?01Ôx'|A>@޿|8پ- CxT &mq#w~Ňcd\5#ܐ0\c?%\q0|PHNvD`Z!7BJrb.rW NG599DL#NWBӜwšJ^W1}qTGFilDRKP(U0tr#\sۙi!ڳwgy%70ft$, 4ݝO{Q?*/l6iޥڒvK ķa0w:䱅e`O#j"v%eJ" |y;XfAHz$/$SI0 [oiEQ"d3<>Y(C|4+,2,d2/P!hS澷?$}{aB8CD 11a FlG.{_W'3ܬ4PK:'#PK픕L content.xmlZmo6_!Űn[^^-)}c$"E~HI_(MbCysG'yfPQ1^1 Z./6y%Xr259o]E}ؕAWI*:(<&YF燂8Eo0Cz<[[pNxAs+*IMdew=IK 68wt&CoC[В nP:BY?E; tdb0zanU$T[ *N U7Kښe}% E$Sj񯘲ǝY\NùWp`y9ho<)Ow+*_ʇ/lYXՖ:g5}nRoJhUg'>Æ~ PD(=)yNԈ洀@ b Q2C1!쳇S|CS8I~[Y3j]I?'Y );~j?ߓ,"/^Pڳѿ.^۟1>5>2#7T} /`qplyZQ[kKJ5VcX%wB:{sAV$!"1ykrB2ZVRkjX]3r0ͮ( G3 t\qMXy*wJ,ƠK:#qZK jH V X?\9#dlUe%Y ,gФ[j_Y 0ig9+Qgz]xpI[+Rb{HTƓhkfi'siMFi@Ϟl?A9g|kr['vZD!ubxf}ܫoIDe 䍾ț:mmvqÚ=)o=}µAn>v\dv7E6 a G T[d5W,3iSpxnAt".m>6'|4ShNiX3'owvK"Cw~8&+C2%uGǹ[Uڥ]/ |@ss*ZǗ"?q|;cxة?Ζ*9ܶSкL]ӸFk/[KC/bCv2+m݁k1L{@x:7:G^{\+7op7녻z*x`xgP&}kPތ3$p P-)![ ˗Wە[-שu`5. ]JqFӣ‰3>4QK(8 |%Ll|!RsAP{TcGMu1x%(LF7ut:i=BPn5<OKoh]O7>#B{'Um7!t같мf.셶_m׽794vOJ)9n{Q}9,qӗi}w7.sx "0t5h䅓/L^u_ 抉Cho]-tPK V%PK픕L'Configurations2/accelerator/current.xmlPKPK픕LConfigurations2/popupmenu/PK픕LConfigurations2/statusbar/PK픕LConfigurations2/toolpanel/PK픕LConfigurations2/menubar/PK픕LConfigurations2/progressbar/PK픕LConfigurations2/images/Bitmaps/PK픕LConfigurations2/toolbar/PK픕LConfigurations2/floater/PK픕L#?(Thumbnails/thumbnail.pngPNG  IHDR<< ")@/PLTE   #)!&#+%1*7 ---#0?+3<111%4D(9J,>Q2IU6Kb8Og:Rk?XsEEELLLBO\DP]VVV^^^KYgA[wC]zEa~SbrYi{cccnnnFbHdLj^oQqY|`rcufyj~^mp``bgghux}kmnqΉŽėϚԝמآߤ IDATxO@}@0@%LPlI &-ʜ)"^spGY=\/ϯӍ`,lso܏<3^o <܇WK A2b|3YKυg<~P_iN0Ҽ04׶AKdp B&h]0| r:yp섧+}BbU,vm- b[pcM(թt:@ö(3e/ %T$ec`o"L8j <:ОSg YtLў&"~`= %J]y`]҃ 0KB;`{wW4 bw5B^^^odDY$;Ӄh4[f f}1ڶ#dIq">&_גY[}{աƊS҆D٪ZUǜ/ˊhڵ$k2eG\5E m;QG9 G*x{ˏE_A6p.}dLX6dF oW*,JX),3i|+A]c%<LcIU+Vcbv[RX ZX /( 7~V`IENDB`PK픕L styles.xml[_s۸}IQcoI9$igt$8 (}'.?Dʔ-9_f.!vaw]RoM`"r'g,1VW??.V$ "QLyp@8+x2[Rb -DNZhas/RfDO6V\3Ҋުۑ%+kf[:d;VyS[<co %‹Dv,z宕n϶3!Wd>(5|y)#rl׼)Ud~kEv50rDLǿ6޵Y g*xLc[6%j= n~iJcBTdh3 -/hTEZ0fyٷ)*- {Dx .>ЀoG7)㣭'! =D-<%ͅT MX ֬+ǽԇmvNÎ0qZLv=(0 |i\=(9Qf ͩdH"\-:3ػ?kΡ(3EcR4Bxi D KbGiu@^ק~"OHDF~ku3gTA;H)fr@rQq NgjV4!HIș 5nd7(ݐ9~!*cfP+M,9? ~n=Η$:F˚Yt|/)\|JoΧwG*9P /(Tʸ~N"3}~2&5 ~ /.\CYsruy*8jLj lps/* !!LY6 [Bpz1^6>S/25IzL}6uzh3``G;#|k48 (ȏ45S38-~+iydon)b{ύ|繧 ҃ƦD~6')Q̧x''pf.w7aM1MHɫJ< k5opR1Z %W@1]YeC8o;'DnjLx*Ib4 o)(r SaX פyE*=7,2oRR( 99XS,iYq5ݠJ/h =X.a3,Y0ocp8k 6xBo FHR%wsc׃AU&9*cB)8M6` jmQI{=,1A.{Um.X~f8Pne9U!8y4$zWI,2)%M5?uNDdgWJ|ޜW/O zyz VU^6T;+4TU Q DwT)ݱ@u{`MiFaSݥFLȔv%,6o3QH[KRMegj-EZ{U^bI;< ]ӽqe1ce]\qMI=ݶz*كr:}M 9^dJGiޒс揁&p]L,0t[ALb nMn8nXnÆhOi&ebJRhb} UJhӃ%_hLAc̽]Jd󉞷xllr>+  khowL6Ѧ&I:O g6`ڥӘ~oNѪ]oP};p.j1j4dAiD߶FIsoC˥^RjrVa =atmY7t6 4#*nPU#`hbȳQ&M^¦W9ʞE"2v;#|򳸹-{$I rnl2*h=nQɚМCp5F/^näp,w ݬyXDSJR>G')4>:CB/3|}9zďg2WjWԃ .&p{xL^ļ?G @?t_Hl|,+&dC'h0?_IJU?]!%.y~ FXh){V1!}e'.%%065BDG {/ҮEnXEALr*mX$%) Q&-j(ɟrdv3kv6NnԹLJeB'2uj3in:4Pp 1W0qWТCÔ(H :ԑ%:,$ŋ;xxhJQ>Q}Z(@i]:7Lm&M6qU0Wq:I3w9\.m7aԪ$ i.ա0ՇBH&(׬orFt>*Aoյ:Fu'"kzyHua!9 9 ٙzx3$19XDBvJxk]Gꌰi|Sn֬kiϡ'=8Azm+['2v羹id{k}`8o=ʘ9FckW7);p1m~i Vj9? 3g"L6oIL8GIF tEQpFiq#% S04m |?#.7k?Ĩ֦z{#ˣ4x'G$X蟂9q?0:8;:SqvzJTPK -PKL settings.xmlZms8~"ʹ!o0!aJ. k1ȒGC]`(C^lG%^"~ J3)^$e"lxN˅Yu*$a*! ]nxuI4uA"ue b1<?yL<51qPЯXF(bR»\l6b]Q棗K)_I ďbՖ[ d#WDhW;vmsV۹r6k)D"aмȜ3Qa"#c˱3kxy~i뀖2xif1dxQ_yt&0j&yG''o\򵳓]++LPxj XS]ôKWXj0 {Y1ՙ;VGRr k PXu>лz H<&x}-Q!ڝl47ݏt>tn/ YL'A ]JpG;ٞk"&o=|`Z^tZ]κ-+6GfV񉆳K$f^c>ޑWdTCɖ0rw-\X{H8fx5UN£S ԂPԁ+J&6g^5ep3JؿgtԃAA-hU[p[{gU\Lt$oE`[a#Zh| RۈCʺ8U|)?lVHV٩z`e6 f]*FkZX$*! ,aѽI&6?s9AH۳T2~3?@ٽ34˾Ԋc>{Ԡ!OϗB6zޏnV&(cq DC-GX(Qj61m n*R\f徑p\ePg9$ ɗ@ӂF@;MY8;shX 1]9_@fDd/uO.IRZ)Znb)zG:3p(T8鹦6,^hja8Ӑ9o;ׂ:nҒ>RڭĪoDXkdxO T- 6/oBE "Woi'32]Tsnd,PWPIe2s3+;]3dU׼; uhs]AMəBo?fhQ̷䲷ji}owPK|M!PKL content.xmlZێ6}WJQ)deMmRIZ} eDv3$y$@`[3pxxͦȍ;*j+ӝ8A˄\\~fOxS ZJ+᥄ozWJsNjVKRz.9hzKrvWྷ9{v NYuF,$Λ:2Y/*"Ao̥ܶdOXl6 8pJ &6)Vĵ[lA%b!▊ѩ!j%h .s\G}=~-Fn1dIh)>UtfQ3sֱ28XI$523(v*+RJ@i5WWw8h{]#ZR'xA=Ddg Fņ/|z!Zbj8zGJN?vHߒ([Gt`q{.ӟ~6^=5^*'b0UC,_4d%9VRtʣ>Vn7X3aUdKͱB.,i+",瓔lm\ ę hZKRkR<:std{n ύᖧ9 ^?Q1QgB=9U'F`B/U.ͦlwΝI%'[|s _|%7J7i+ϭmSwSpI$5|8̋B?g7BN<70 nC%K *V o9gZg̛ A7kNSϹɦO& ~/ߘzGVVV:K\t^e uSj.ݥ+|/O㗖~cVF+K0s ;`ƾ<g罂|[\~ᡟʡ=ޛhn4Xi(F7fo/ (HƂBϊU;F55QNo jw8PRI|VQI8uȇ,8APINQPxXMu/Dj-}C(7tTCAͦINv.X=+C%^ao+s !kY~Y./ ؤ.aPK5|z\&PKLConfigurations2/popupmenu/PKLConfigurations2/statusbar/PKLConfigurations2/progressbar/PKLConfigurations2/toolpanel/PKLConfigurations2/menubar/PKL'Configurations2/accelerator/current.xmlPKPKLConfigurations2/toolbar/PKLConfigurations2/floater/PKLConfigurations2/images/Bitmaps/PKLX~~Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE     !, 69;# .&))*':5""",,,111<<3&6 ? B) +rCYz$: ߄Dzl:3[A1Hh@)ugqCp$XLcWNsm־*\N0E qmu863lFDlN!'N3!ۆn1Z[[%$,K&d] 0cdLl΃ylg̰,6 O'+N'h&IqX .:u[lW򁠥s1JT4~{e9{ ߐ$0']z{MLVw,!>vXb;I`TuV H䋔y$c-u02^ee=zK^-<8Uq*eϑqWm3MST8t? g !z,Pւ eeeϪUdjU_s&~v ӬLe^OC0-.\HX5a ͺ!auݬۥ8´K;!ô-;{´vXKi橗'4Ú@یvX[5/ TIENDB`PKL styles.xmlݝre;ZHʦLtfN6m"A YeK')>)eٙ$.ۧ,=bV?;<1s7$!ip^9er |oY~OQIe{Z)t߷Uyťqt,ZMYK ܧ ZL: TV<$0TUqXvwCz0\ܶQkWlY*hS,*+, WhjmI6[a65BZ0\r.WiqsAl8áӇfڌ#ϔC7X6.a;pUH1YMdͅ/ ytu }3 Npo Ъ+"d.M;}nlcʁ,b+{+_q׻@pDVPdCr>Ahq+=ZeUǺ"wDzz ~B?Avbw*Z3[4a_}B9`<6|"FXLod2m=̈́&e361JbY;r[w EJgtn/%[#o}0 )b+p1-LV߉@D(&@P X:g Ěj!8 d@3 b OWk@GP*F#(`AA#:V0"(#(`A#:V0"(#(hA#:V0"(#(hA#:V6Ueo u"(##(hA ZOFɳ ѓg='*x2b'ONUdN<Ɉ<9yvV8yVT6yV=evtɳ(;ONU?:r +5B'_NUO?:v`+5'_OtFɷ';_OUO:~zoA:"(;_GPUevt*̎AV]#*2; tXEPfGw.T`A: "(@GPUePʈ?V>-u iUdOK<-'#vZii;RNKɈ:rZENFZu<讕dߧ\iy'BOyu:jZ^DyeA0g^ \Qb\UZ9'._/T_EO8>! ۇ2^ Δ٤Mq'ްY_Ÿ;"O2?'^`Π;$%GR tto qi^歝@{ m)-0.ˊϸu/Ԍ焤iaDx~c:4%?Ov$wl>j;*4o(z*uƋ@v,sF|>9*Tg$Yu[V$yc@ˆh hL/cEJX^J `dؠX~dqw7MW磙M(II9{coG*cgMS\TH7=v.sLFr'E+ X&p4cOي)iw@ަhykpV8 7Nx{.!AݽIߤ&s{98}w1o4NیbB. ~[]c nY jQ/!f1LUaZ@j;JL>G9eJT/էrIUEhbeɦWFSo~84= ./=(Y (ncjקrW|t.4!'>syˌ}<:\ϔӵíSDdnG%[m9pVlsF1:$tᡷ _qTHo4j.Xۊn}raQv2}hwDTL?כ*輤;HJ"{}r~/#]76N)tq&/#1q-_ު.Q̷FL]f&1L^pf~1_F%Jl'F\O\s20+p,&T)Bx MW]9})/~/ni}$Q}Ŏ>2U-7`d8ҿ_[:;f(~mڛ>s'}th|*40iv8V4w k]k"䑩G3-|=\|_d}IFOmy@椪SM?eku_zSC)YnI}pP&zDỂ!OOARxG\I?`W]z\vWC8%B8R>oyߵw(M#>FMOX1@ViסR>wi.Vt_^.:'IiS?PK 6O UPKLMETA-INF/manifest.xmlSKj0F{KmVEɢP3 >!}e'.%%065BDG {/ҮEnXEALr*mX$%) Q&-j(ɟrdv3kv6NnԹLJeB'2uj3in:4Pp 1W0qWТCÔ(H :ԑ%:,$ŋ;xxhJQ>Q}Z(@i]:7L%hD&bZ}yt=%?yL<51qPЯXF(bR.Y pWRWv|ajğ#wc+bлz H ᖨ ~1)`_‡YϐyotO̡Z_ xԸ,bK>Kerjrֹ\(ېa['N.QyMύbZxG_QӓзoCɖ0rw-\X{H87#Nq|{Y@*'R)xnJj թ+J]}%4ri/暲Q MJОƒW pFAA=iUr6 j n<9+.tO5ے( תE#?[B`Mx9䭪Cی/͊r\af/X_X74c4&<KEw)et/r>4D Ϡ\N{`(p: `v$*i5+b%3СLJކFŇ#t?zB1 ~`衖b0)Q37Ֆmb%5KSHA8.;(艳5$ܠωjGw:H7WE(̠a1]7vyR4}%ZOD`@6-/|8'$ick4 ^n:d)zG;#aB!8i¦Us4e2n4dNή$@|c2Wbշ@tQMx Tɭ v3ēAE.~Kgfe&),.&X=2ɚ3o49]f)ʜSЈ0`ռ=yOEK, OM 㬼DBު7H!"Myq`uG*!QudF`Udt*(*׌K||U"Zt)i-x]; %xu39IL]꿦Av/xV! |1Rظxazk6Uġ!k̉!V= v(&~lY5)iW̖.a+$2XV,~~9uHYZ<)X?J橖M]ɺDmwiD/G)2Rq{'34c{GizTA&AhnQ'fuz+pD@DUQB3"Nt]({8AlŒpI3wx*TwG>~skO>0ɚ7  UC ߌ/h#©ʣeuhkоcx5*,jU ~ZpTIM׈<=%u?nP1 ͪ=aoG2uk &AE<`m5־xKÖWxf.u1 S6T+=F5>7I(:4g! U'3 ڍZ[9\l X򃆫_2N΃yz=U)T?;JUqA."(.R R}`2/O &UIW3 gINE'li<}hBx>Q1EY4H_6Ld8Of:Qi@ZuEa~pppGdHeRrTvUxZ~[\``cjmnq·-IDATxr0eQ~"VptRB##W:|~l'/(jn =;"y%>}xFsv[p|@(E!X"X cfƪS W2UL$xsDm92cIꫮ t 4tM<\Ipp6]eisa[Ẅ́n=®Lص ;vsa;8IGr҆'|l w?%=B F))I1t'iL ~ E4+IENDB`PK L styles.xml[Qs۸~&&)ɱƾ&sI;'DeOJLŲsn&9bw zmƽ*K&Qt<"abOяx#ҔthRqZz sCU2 RrsEAs+4wz)3'*]iEoPamɒ5+H*+·%S"+b[Zr*A^O֓!A4Mm\s%q@9 :˛QEꇼJy- QdgW IK`s/Mʴf9ػn=0+"fn$*ĕ͈ZypDеU,Y1L !UQVw<;k{=&ZfƤig9(t+kDumߧ_x"Bίq.ѥ=S'~Jb'4a<~m`R-+菤_kM`5[S1"`j0e:H7d| ?~*wكt%z9!9Y`v]O{_hiyDҞuH1}|B'>%zTo0IDOƤO$_qx_ʹdi + N|k1\)_ u.C4Ip6xJ0l` K( _9BW4,L AmlF*]Sǭ$4pn\E0Wk3ari^ϯaodzN 'sFIceE*#0}9͗xfg6S+c^m7)x]^Z}XxdygJŠEjC~MOɴ^6e7,aS1_T(L 7JDXk)M'eAPaX /VQ屪`YCGG@x: o͊=% "pADQbhް;&U%RM׋p)YіR}G*% PzWd C41\}}μ>gI}&nxpncVq+KWv=puPWl{nfdtxL8Β'k`lq67|LRMֳ'cݥoE[ um,gMA&OJHU"-a?Wb$!fPh|8P .l$[ـIt˒+ohwDky!C 83D׉\?m~q8gLrP~ }&a?e GCRS;LOA BR Chr4"/: E"hM^$>g!4}yzvD^f£AtJ؂koֆjg*.!A[AGMk zXVțjn {1]:΅̢$ c.r:ig)Q)TV]+n-K2b3+mwy[=3f-kj +6i>.&vuv:هr:}m P(;N{ze k{hgh&{hE$DŽj7dڥ 91>0L)UV_Ԡo_|\/hx6i<˅BY&A|3S~Z.DrWplSF{F90YQ*QL+ضÀRo}㮿v䊍nw6\x ˠljPKf֎x 8PK LMETA-INF/manifest.xmlSKj0F{KmVEɢP3 >!}e'.%%065BDG {/ҮEnXEALr*mX$%) Q&-j(ɟrdv3kv6NnԹLJeB'2uj3in:4Pp 1W0qWТCÔ(H :ԑ%:,$ŋ;xxhJQ>Q}Z(@i]:7L@sBlt)p?Ut|["½zcגU #m6hSR] aW@o/· xr"ьi]@8x#S^"IPF<4C%:ɗ@Ձ^bt;"SAbO0eLehV,KvМY(PΠ8/^wQbR%2{Es%FHCb]Wi@1#R Y0U`dž^"*Y3:%DK륡y܍3')McG+<W򡩵+W5M㮞~d(N^7IO[X}chv^UP%tP8GTSiwr\P-QCD$ 6}xd67 νџ˜.=_>1JRoPrP{#Jxao5D҃wTfzxm}@l!Ɨ;T`yTvQvtQ׋{h159;QW$kk8vgUqj(Θ b F%z/&mZPx9x᩟mI˞0|PT6p8/HQ|7&nɯS} [@kp]ASET`9+?T6($>yd$9Jܢd)uV+X)CiF%73v^o;u5J建#h-~ &|NVLG9Јdt)g}%bEB){,9ʣpGɣVxlPa~̨Y;C#'xGN !۝#]溰cIrk=IVEVHӽZ"vU/9[bJ3S.UZnM&Ph!tNRp::];hߍnmJDi+|yJgkcP1P뒪Mcɽ%ZY[TO"B}A_ΛLgc":3|aT I88.aKK %gpMg^{[$|Q8}Jt[;57.ќϯWW~ן+uqZ+.IENDB`PK L styles.xml[_s۸&&)ƹ&sI;'DeK߯A)Sl眛I]`vI6 %(: Gc|y97{-ҔthRqZz sCU2 RrsEAs+4wz)3'*]iEoPamɒ5+H*+·%S"+b[Zr*A^O֓!A4Mm\s%q@9 :˛QEꇼJy- Qdǫ%&reZu]7L3Ce IfDz{\Qu&d6t-mAKV 6pBFU0];i`^dJ=7 4 |cw`'HHC;*^ OI !UH:<*&eTSR-R&I'+3 }o]0ja:'ܻW i/栐(KE'@z[PɐDfp7LY CQn3EcR4BY6(t+kDuӯ|<!8=S'~Jb'4&[7ÞyF%/G0Hx#aAJq.G'(gG^kj4aKQ0Cj!StCƧPLURyW*=H')X%kz'/HeIt|/)qCzL >%o7[W8P  /x9D$a2"PLx/ơ7 8ۮ"~qTR|Fʕ;;x rxv  6X4_b_mV$ǞM4>983|XX<_JhA LNz)㼡gi qEaj\Q ZKi^}R)Qa bESTy*}sIJ C@/@QpbI/͊YJxID@DQ֯{ÎT_ÊbkhK+-% PzCWd C7a "EI۟v$tÃs[Bc}]YJ%&VԜ޳jչnC]_>6iuf- =~Gp}f 0Fn|Ew1ІhScζ¸y `hOJHU` ;-RN`Al4gPh|8P 8]4NS &H\9It%}lTm4ĻNbs?_\=1qzCaTy:GCx93sZ&c(/ac#x]|4$5T*>Ka/h*$ <&GC(2qL_$BgGCh"9?>/4O_ ӳ#"2t Vvu^{6T;K4Tu Q =jXSú=F`T#tދ#w.dFf%,6osAH;KJMe琴j%E\uk]bI[<= ]o 1kYW[ X\ֵۤ| g\:7 iXў揁%ZSۘ8pahd n{Mn8nXznHY"&dwѴ#w8 ZmqYoq4CiD]GY&A|3S~Z.DrW38H [)o#κ˵y/nl0FPA [V )aH O2=MBs=9|Pwwn˵|q<}_>*ۍU:mAɊ乯ќG1:lcX%U=H톉p,Ow ݴlf|[|ҤSǷ6CBr|]9 ~ďg:o %B\}|-SWgviIZ?!v"IcEp^%N &NK]eP )ɻ`ዅͳusw~1Ĩ|G0j`Jm,b&Wlsη\s-,?PK,j \<PK LMETA-INF/manifest.xmlSKj0F{KmVEɢP3 >!}e'.%%065BDG {/ҮEnXEALr*mX$%) Q&-j(ɟrdv3kv6NnԹLJeB'2uj3in:4Pp 1W0qWТCÔ(H :ԑ%:,$ŋ;xxhJQ>Q}Z(@i]:7LyzƞXWN q@jn66c.3//K& ok=/^1 4ROR8YX4][)rLts6Xص:yw]ut񥰄xU{( {]ovΝ>s;VJU N *kZ[PVrppG.UupS7}+톟posg,F[9v(s zG0)#4"4E$Uo(2"M&zW 'lbbD3`Dk\7q?Sϳs)x%]&o9>ԲW?_a~=<)?6>3dXKr)zЪJ_4nƸ9 I[a<݇&> PKܻ/PKO styles.xml\r۸SpѤlKM&O8igz"8 hy^$/ H$ʲٙdsqxW?߯S\D'([>~Wx@1%8.0AARXxsV̤c|(gas L0> C$  a1wj]@3al,(u1^ly ar (wc$IVl6H0wn~=Wtc0NC!ժ ^ =FIENLPN&&u;lV-OL(BFu.\Ӝ.!Ľ(1RxL׵ogE35vNKq ,-eHsE U[zf`A[:[`n3k;4M(:x_ +eܼ#b #H,hUv ?9.'}K1{ 4rDcvilȅm\}w3ViœwGm`RPP~MDL罃w Kֱݺ&S]Ժ'=eq SZP/nL[WT~C&{ bA(Ma&|NnA6v²맅DЪcY(ccE7:5K@䳏H51I4½~FPMP!0UgerX)OS!oI5W`~1ZhGa@;$&7GϜȋa"na- q5~gOLUJu0>}>9AB,%;#rb/#mPeV)\dTBo9().ro(X4_}eR<9NH9`N `wJf?u,SyB&R~ b]Qs8/n{΍߁T0ذ&+()eTC:S ɇ㜊8X&YCnJu}fg܀AX[𱵎A&Ѳ+-QiϻsA Zi5[IWh{J~>1_v0V'TS>٠x>r2CuWCB.j)]|Vڷpl?+L/lG%[oeAA`:؞ n!cJy&I-WD. $"~6{^- m6lCPc D3EA|TKLr0/1K=)8G) xϿU91seA LR%8oa+a=ǩ3c*Bڃ#xGC0 L&14:C/~ Mb"<Cyqh _h(ojCnlaū3{f%,o  nfXA_&sX`T˩Hs 5HQ(abYo :1mLKMnvHNWUrcJ!iL<݋-\ll^qqɖȬɉnZt&D xÂW뇞F;gd_mAkl^csĝ ]rח e9 WvkV]_ X*ۺVswJ:['&@݃ggXz3e)nzBWm3& ~Yԧ .AKs& ^wZF܊ÞeUk;lH{ pU V5UiV.kBY`1ͦ*]ULś0Kl+ Q?[@j/[،iH,佧qG`ru^JEF-s_6kЧSgC'uFN;^<79Niic(z5*^@Dck_3{ÔdEW 9g90w#Osw<Ş՘Xᓗ(7k&[98Nlr9vŠ4aorGɹ+1WlmtdDSDaR7n(Dm!(Jrs8Xֻ5~C}ߕZy^€]qIUۧorOb>E@~DVֲYG#TB!-ɏ9?%NkĿ^81w<͒_RK%p [K ֠%Xax? QW?#uёI]5f>Jpڡb@*Emsl;:VY{[lq6zPK SPKOConfigurations2/statusbar/PKOConfigurations2/floater/PKOConfigurations2/menubar/PKOConfigurations2/toolpanel/PKOConfigurations2/popupmenu/PKOConfigurations2/images/Bitmaps/PKO'Configurations2/accelerator/current.xmlPKPKOConfigurations2/progressbar/PKOConfigurations2/toolbar/PKO content.xmlY[o6~߯mX7ȺY-NÖE HFQdʱe2,@~xwHj|m4p҈ka^]~sA㘄xѰpέ ˕RJbe۴ZP؞,ml2('ڬX$*G.W<\ ȋ8x2#X| rb]Mp RXu+aQ9vۉ!-~ór%e-Ijts! Y$/9ʏiޗR-sLYV{"7z5 )zj҂WxaQ~\1kKB"GHx?:I̝$a ^]v'c,i1%URJkYi:oсo$X49xYHԊi1iSwM%VVRphҤuuw$zoY@IFakX*FS,;q7 6e$n Lf݉nwdmes4K4tW^e,Z6z7ؗ;F`4p=DiIC_2?h|!hb- z:1*g^65oJ]|c(VôPm ,]vJ`|@9<=*h Z4jo%8f0Q0"Fdrxo9{K9XƟհCCq$%4g[;~Aiz»1^6qjӃWf|xeI"p||}xWSq -Bى-*XC}6 h/{ CņruK kNXd6zksfRfakG"q p&BR/cm3;"^ aFyzLRٕ4%Z{8k !.aNΦ5U PFP1-$>2nE#[Fbk.V`lR1]e$oWE81^.P$XN-\RK+l~Ba$TB_H3 آv69G?c''t7s]0_=]O`$].7(zE 0͝'2 G.PH8P]n|ɾPH 7 +Y SB!GwOD;k-C={///b+T\m_9랆<39x*f2~SHo;WJ _ 悻ټt5;AF9aƸқ~XΗ/ ɕJ3ÝBKB>+:okP4Üq*p ϝ9gh`NJ5UPrm^3u >8:zpIIPSp H"DV/훌F$&p]soA|:3q/Co u[lWZ ōbP6c{*wFGٻa9T*y MG&6cX08 Gcw0 oY 5`>ނEJL:+D;;!_,x ޕm ~ {O,,g-wdn]{./݉l|X' 9`-*b>wZut̋i'WC] PYor[t6\ي?lg,a/Cv{ 9d#9=kPK,> PKO settings.xmlZQs8~_fIZ@0% HzoZ@YHr+ !6ý$zwSAi&E#VLLS|jq&cB0@c}˅/nK)Ԙ^.TQR+/>"X%hD&ۥDNaJxniW%XNŮ(k8?f{Z:b|W:l)Q#P1 xz>RxxYd D6F˖~DO5_fM&L,}8+ü c}RQ3B?PX d)zz|+|)"q O@W`Qn "f]RQ4mLT9bSw@D~GI}w@x(-&x}2k&Lh$g H7ݎt^җ1}E[|F׆.qd=EL}o=`Z/3/T*yYaQ!=2ǪO45WF#Gs!Yz2|2Ee2G>~#ےu]AX! jiJ31{j גzpEW_,|“lc,BS"&p'iQ}Pwr9%z%c\j̹&8&4kؖDiبZ[~o^6Őm;4'<*bqBnc8;]gi.md6a 4C#ѭ(!>s9WMӓ|ŅD*M+^ <3m6 ޼)R ļgᠷe˰ !`$n 嵯d7}iPKA/#PKO%9#ddThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE    #) )"-&2(5+9###*** -;"0>222<<<$3C'7H):L,>Q.AU0BW1EZ3H^5Kb8Nf;Rl=Vq?XsCCCJJJSSS]]]@YuB]yD`}aaakkkqqq{{{FcHeLkOnOpQrTuVxY}[]``beghkmnq΅5]fIDATxڵR0{ZfYyAa0XV?&&i2Ξꤞk9DePb\,#^ 0qX-1^.X;;: XiiYffR8gY #hM/o6 G  ] |Qo ,-5:YUZDSh۟z4 yT WC/tAD=Y+$cl"Pq|wm-(5Ӈ>Gۘ|a,!'m,_ '2re+ff(aKcKZ}`ψDOOYZp"" Ia!l rGdbЍ.E͝F||Kb@ڪlX/P5a?%@b2k'.ym µ`?D #8#ǎZD; >pc 4W茇UL R؝ *l aGug"1-֒EipR`VS  (content.xmlPKOA/# nsettings.xmlPKO%9#ddThumbnails/thumbnail.pngPKO`41[!META-INF/manifest.xmlPK6"muse-4.0.0/src/svg/projects/metronome_on.odg000066400000000000000000000246751404105311000211020ustar00rootroot00000000000000PKO.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKOmeta.xml]o +,c#ǕiEMj*.rzl6XϟiDS=/u cV 4o O~yLPY[ @-\Retk(jڔa ?k#lpkkՅ:FS4WG낎,RtEd}^XCVù'nJ DitA9;rMk J nož:nM3va?_~ـ3#EC!z$$ qU(2"M½zV8!$8M2`xk\Ou"Os.eo6_G9>PWk?Ƚ?a~ {=~%.dG0Ixxy89cdA?iƏ}gPKŦ/PKO styles.xml\[sۺ~LFٖLN2I/INI;ӧ3 IH(B?bq!A@YԵgX}] ~] fٕ?8|g1MH}pwbAb CLQ Q4 g_AY_(*H13ϠfV".ITa&k t^).޵;ג%IZrn϶#.qɴ]uct: eM躥A($|#T*JvbFȑ,(sۊZ` ?Jϧ%2c0M{n3I [3@pgAFȵ1m=mi)@e ejl|cF ڥgV57Kg7ϣ >BhVk" Er4X8f4YnS}oP[|\J$qa" B#sp)l+ $d8΄!&^Pzۋb^neDL%w(kTZP _>O٬C=4m(k= f_P/Pi,gPHH}/( FT,wWPN?ȩB߫u g5j9Xjn#]5I7| >SN+%CwǦ[!KAx}/%{oPavMҏ{sw>-kU=9{oW[/7)b .Wׯ d}=IJ'*χbJV4 /·'{]Zh#^|'M<)vaIMdw>O.i#Z#U,%(mĊ/k$N|e`Kb OӠ/+ᘋ2)++Rٟk c5b1a |{ȉBȋa"U.ן, PF?/&թHg"od ;p8~Z4> "mEWU0|CrP pZ$T4_!pooC-^:+ ^{!Ya IZ) d.O xi׽ m lňt5]mpM헠S䇨s.R0qE节X9^lwR@FFyԃ+SG3o? )j]ݑ˪?ƒ:S~hJ-I k(T81 φt>Ɛ3&Z\%vw5`HSVIbm NwsH |T 'U1C. .V0\Y5jV()$S, Nmy0oEe?k'#*%:T7@}QBx2' 2AkO4ۍNG-u&h [8}@˟%po%n4!'Fqipv?(XK-b Aڥw >ѦA;FBEa/`HS( D=S.-Kwy>/GKJwytnw> 39hn|=}Sts Zc{ks&l˔}КrGOM.\BV`3R5G&@݃ggʙ/~>T\+z@S4=;E=Cʢ>\ =|_,D뎷FȈ[r>2hq}d=5iGw]@WAwcM߉wuP۱;jɻ0[RxTVt}&Βv-w6p}ԟwX.qjƌe$bcN$Sx0qYE^uj"#uܗMu tsRyDZ9-4{JM[W;E+hP9&œ]]50 Dž}sKȑH?= 6}73I^a+Kb`atAApVX-6(ٵn|p(e}%.93s}qT'^aJY_+sOY"#|0rTҰћ޺p ۠V(ΝJq?٢5CkrlcUVjk^}Myv3AcE8 VH8( {iP)pԪx.q_AhM-%I!%8midTp1*ph0e4T| p`k4K]t@w a⌆j9%Y/) u+NHjIg^oɲ1?/q\&^iP}| 3,VPVZhMtqA=a.o*5'<4y++pAI 8#Ec6="4 {cwGiyDX9R ZX0fۘ"F'; VOLεE.h;T\ː{UTZrX+B+X(q҈-6hxj|TV g8,2)@c@pDŽ7SDjw2=@)eS_jO-vbCŊBX_i{ 6 jÝ׆&ژjc2q0s0qwaɚ睅Q|C$m Z@9V]8 E,~sDXe%hyﯭ8j Cery]}GPw|]Gl>vgR XkBJ /Q]^":Ǫo.baVS8,|2r<|.jqZ|_p\ \a5_,FT;N9X'˃sw{l>?7Vu.҈fSw삳t[C;[CztSxP׿zEyr(1mTfa}v{gho]G Ngr3 @mfҵ_ mC+v`5[ P[ mɾ>m)WxtE+|1MOIn7 lj0P;<&֕yhOۇVCyXNbwȬЗQ" 9l{oR͗xx yd D6F˖՟̾GO5_fM&L,W0odb ŨfQXd)zvr+|)"q @W`Qn "f]RQ4mLT9bSw@D~GI}w@x(-&x}2&Lh$g H7ݍt^җ1}M[|F׆.qd=WEL}o=`Z/3/Vrf^aQ!=2ǪO4\5׳F#Gs!Yz2|:Eu[2G>~+ےu}AZ! jiJ31{j 7zpEW_,|³lc,BS"&p/iQ}Pr'9%z%c\j,&9x۴%Q6jVV[B`Mx1䭪C͉ϫ4D-wO\z0!U*{$fq!Qʾepӊc>Р.!/̠Cw wG ZMPSf'(C* >L=rO6%`2?;26qU.ba> DO9cp& U4ҌAXl:r3T;;m.2=Yweo|V2̶ނ}O517p%Jפ$[fņhv|r!#>[g̅I/:3zQ _2vh1 ~2/`JPo&,I=*xN<)Vڿo#UD2O9gK}l?'AE?™Yu{!ţ4diᡪ5e[0O1$T2IVϽyT&')F {"DK#% B+~z\&CL [J@_^JFyӗvPK)s#PKOFThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE     #,+, 41>>$?) """***555<<v:s= {;mF|C }K{QCCCKKKSSS[[[ccckkkrrr{{{=C IEMORZFL GO S TYZ[] bg!XW]_T\ ^ bfialoqo#q'v"{(_b`g klv{cgk opsty{}"}ڂǁ'Ɔ-ȃ(Ѓ&ԋ+ك!ޖ1 !($+')-21.0449y&܅]r۪̉)KCRUtRNODN(`\.S_Co8lRE6a.],>H!w-90Gذ2^R5v+g0H$jm- `zeT!AlzL<6xXYa Vk۵4j"S*+W!M)l֬qRӨ nHջfKJǡp ^TlH'*ߞ(_-GPx7 Že1?-Ys˶$6H-"f8,0{Q!5H#O 4Ky}zD3k c,k>e(f$8cʁ]sqepfwaAA܃| <[~mJ-mA: P R\RK 2  MLקbt",: Gin_w`Y+㎭ {$ 3Se0IV.tj<.cap|Fu=\( 7 ‡pW&<7|JiIB7 L5tħm\.% Q>pX2bTy24a8 #5Zr"J؂UX>b5[i/#nt[Urq&|gx$I:dt[Us^Cb˝(rHSUi #[jQ&iggh>[ՀO(qsla7?G`<;S6TOd݌Ga|ըK9;Ǎ ezcP7avm#pme'7a]aïLoIf/YA}kÚ£5z.`|i?ܗ._D:IENDB`PKOMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PKO.++mimetypePKOŦ/Qmeta.xmlPKO=% U Mstyles.xmlPKOSConfigurations2/images/Bitmaps/PKOConfigurations2/popupmenu/PKOConfigurations2/toolpanel/PKOConfigurations2/progressbar/PKO:Configurations2/toolbar/PKOpConfigurations2/statusbar/PKO'Configurations2/accelerator/current.xmlPKOConfigurations2/menubar/PKO5Configurations2/floater/PKO3? kcontent.xmlPKO)s# settings.xmlPKOF Thumbnails/thumbnail.pngPKO`41#META-INF/manifest.xmlPK6q%muse-4.0.0/src/svg/projects/midi_controller_new.odg000066400000000000000000000260171404105311000224270ustar00rootroot00000000000000PKB;P.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKB;Pmeta.xmlM W Ď㕪aWY֥3l*Gwf`C]yX{B1ͥ*7XLH&(׬rztZo5B5EmJJtgMcntmRURb1::Kyu.7%+EQ(Y:o|hv:j8OPIZap]PΎ\Ӛj8a…n-j`uӏb_U7[ 줝M SMfZ".8S$ U&Z%n&c8y:v[ If9 aяG-L%_L&՘􅆏oŐ򂝈;#m(sʜtb .Pck_+L.fؽ0r|LV \aP9r vvOmyZBKlQXeDjv vytzamJ v{'6 Npb쮘Z*)WORͦVmߛ#H lG}`1}-splAi4q2^8;T|zWJ׫fn/yٴц1ezԪ$PoAJ|Q?jJ Ј6}y|@{mV9+E؏q_).bO=z9^ Jł$"QUw(Taϫ4 J+Xjn#˾Ds)rXq?t4&FI,[<:S=Jd ks9{oW[/ b7;]=_A}=I%I*Crc +6 kOsGĢU"RiN`c*KPއA fŰb ^7apKƺnL,AYP/<:C ,>mj+8E< uaP;y|$ 'NiZpltq]ZF+&w׊#? !P@=w:Eq^zqǀBDΫhO ꍋ{v+0 \!3 RP$/ aާ(K.G`.6"(_ڭ*݄7bsNnqTk,JP7%T<t ,% a&\Ŷ0sNt.lOXJ$Qg?c 8dNSl&FŒ"N7)f*RAe!Y+)֜<$T$["i(Oयa]H؞r#Iʖ1$e+0$Y4aO P\;h97GeIָ2`ZsCQeY~6T 31o룜t0Z@FG)ُ֝ތ>!ݗ=Z%s/C #MD;4!qv ,|CMų+CӘhEmsuk*_=]Jp5IA!e&,[ f(^ `,r_d s `[biI,Q,ӎ$,)3ܿQ V U$"-6d%'Jٟ{J2G%!Q|ϿUՈesΊ~fBR l8m3~5I%4Uh,~<A )Æh 8FGdE24>CE2t~4/B8=9"C/3NQ4 ԖXMĂ[vX"BDTReE*%J%sD&^N1N)[l3q,VRb'%CKF׋sTvJ>iLȓ6'kӞ?Tj7&Bv]/fM3Mgd!]ʉEWB5JI.!͠ ؅x4ʖHO1<1Oz[gu :$JI7 K? Ӕ3XĊNKf9*(yʡC_1x2|Ռmx)'ەG%u&` [8~ϞD!ĺUoFݧ]N>۾Q8w[ <`%K|N'v7u' ܆P<'a'`HS(B.AƔ&燺K]Ƌ wiB.A6ծ{a})}~f9g5hg{f226נ59IΆ(ە =R7s@q:\ P`=blm}] -+'&@݁ggҙ/>T\+zDS4x[WUǸ1HUeQ//t\ =4@wL;QQ flZ7,T>Ui_,wӸ^Ý|I7:%\cΖWU،ˀ@uο,ŃJ4.W&2To^>&=-ɉ/PO訤Aެ e4i1 " a"E+Nx}k}s&o/PK E WPKB;P'Configurations2/accelerator/current.xmlPKPKB;PConfigurations2/toolbar/PKB;PConfigurations2/progressbar/PKB;PConfigurations2/statusbar/PKB;PConfigurations2/floater/PKB;PConfigurations2/menubar/PKB;PConfigurations2/toolpanel/PKB;PConfigurations2/images/Bitmaps/PKB;PConfigurations2/popupmenu/PKB;P content.xmlY[۸~ E p$J- R`Y,6/DJ$Q[~}9e3QdbJ}›,^5mۙgLUNjs;<ey֩Nv:誃']vzyV |U^vMֲu%Kծd+;@&f-be Z 媩#Εqum4-ChN}Y+#WT _>&[UJWm'sml![TW5W{&M^wEnhTiuevWb{@Y#Sm!/\ڄt0ue;u$MTFQT' #+̏.f~N6aMJ͔2ձVM$YK0r}~se%T:`6Z˒#Ҟ*{l +\<JyJѩFkGUw, Èٵm=_~qB~UQ5YLiHyyhrHÞȞ"AwL|[4Zyx=䙱*N0ߴ0g2Q) *`OOxiۍgľHW6Oq{fʳnT_t?YJ!Oxuf3@܄Ka;SFGz綏(]#5"YPmZzgA'ݶa1YdB#p/Ya|hfPFп DhXYp8̂yDX]h |d8ruÈ1Mb82 {<ޑ&E:G$@Q`ZE|޸5JX(v .m<@'Q?|f)4' 86ϢXdYeDfbZXA09"A B iI-x{)d n\X {ta(c4bPCօA`@`e>8 z(܏bJP9 ٧XŹh;2Q-!5T{{;2 uWa= &4a"0q6a9,2Fe"pm #*ދrK(NXY>w i"tO=F@ѫx"TݠxzIc|#'˚:6Q{RDr+v`1r =}9lUe>ҘB@p E7*/#E RH U>ql!jÆX/At\c7D`4.x;`Z0H&h'}@th3dƘ EPHIWzS`W1=U}LDF5ikq²˄E iz@\IT]_''i&|"6TRlĂYZupQs3W?*pCĮbR4Dj{UwfChbUu}|>=m߃\pVeġ1ϋ"2yiXf S4# tGkWurh]Ţ'mu^@2n(,Gvܙ! OT.dva( 4BrʘKch:>!~OXD3-?\|yu{}>@ ~B,ƍd9WO%,w^sGcgtCNȈ9(@>}?2BSdTQx[f%E\\*K&ܘ&P!JV_n/β 78EyLy؇t4xS_X4;lq Ɯ}LWH8OZ# NǤRO{kut?أ28A x&N/96&QYL1Fw4T\ BpFz1Fo5 Lu`#uJpTd*9WmuAw΁.>zlNlztA0o*ƞ rsjŰ7YFErn,YjަNYiUr3j3?E{'t@3dYmc  $ q[a[fohAg@JCǶݲ3Gk}?{5Ǻۋ2-)/餑eGt-[OBX-~^3*=y췟!?Bwk9 S^vx㎝4KiNږq*VԬآU՜rZE6˷7^-)q8qtςqt)Uټ֚mݻ1$,tӶvaw|{Xu7VɷVϩD*f{!L_>U`z?Z,喖 &2]SL-K< t` urS}72~PKIJ=^&PKB;P&Thumbnails/thumbnail.pngPNG  IHDR<< ")@YPLTE  "0!%$) 6$;"""!%*#(....&+2(-4+06.3:33305<39?>>>4F'A9^ ;BL>FPBBBKKKAISFP\IR^QQQ]]]KUaPZeR]jU`lXbnZft\hv^k{ccclll`m|qqq}}}esgvivhwkxjxq~[il}~rtv{|~ÝƟȡʥΧШrlQIDATxŗkSY⠱V%Ԫ[Df%1 w Jg.!_eb.oӧ%뚿"]UA)y9a*Ց/Vf~3ݮ _!Ļx>g5xqpZW-ATNp> }_aݤ5,MLF=tat7Ly]eOx9#o RXyPR ӭ<ɣowoԣvLޒa M8S]`tNehNI 1yVcy=l7F ?KuޛA}GV5zOT m/ +sFΟa{mxl#I]w~TWvwlw=k6A("|Bb(Va=9ȷ%j]v%NPo2j&am `0'y~ Lnμ?*Ov^k>5f|-&n(mp0mV)֫5Q?%~4גFG`RVT*bh,uu-ahZPD*BhVmZge:. @>UpMulubKLuMU*vϰUrJ wbT*pbƌYZ|u/3cDGa3^Ζ8hhIg)Weڕl)b(bwK4bjDX/5L҂aSEk!OB?^$ͺ8âe]a,OsFHYKU::l~Ը.ؒ^?LWzwE}5MpPHO“[E y]xJpn?>A7M:䟁Wm$IENDB`PKB;PMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PKB;P.++mimetypePKB;Pd-Qmeta.xmlPKB;P E W Lstyles.xmlPKB;P'Configurations2/accelerator/current.xmlPKB;PConfigurations2/toolbar/PKB;P,Configurations2/progressbar/PKB;PfConfigurations2/statusbar/PKB;PConfigurations2/floater/PKB;PConfigurations2/menubar/PKB;P Configurations2/toolpanel/PKB;PBConfigurations2/images/Bitmaps/PKB;PConfigurations2/popupmenu/PKB;P޲J  content.xmlPKB;PIJ=^& settings.xmlPKB;P&' Thumbnails/thumbnail.pngPKB;P`41L&META-INF/manifest.xmlPK6'muse-4.0.0/src/svg/projects/midi_controller_remove.odg000066400000000000000000000262051404105311000231320ustar00rootroot00000000000000PKk;P.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKk;Pmeta.xmln0E A͖C%EEtg$H"﫧cF%ΝrC]yX=Ry $})// 5kk+^,s P7BM@R8Ya4][.T1:X襆Np^ p a y *EyQJַ>4;n5'}v{A)o#״)Π`!0\X[֢0]7(YuO5Nڹ˓G|eg#̌W9A8((L0 aHU(2,% I$ 6 .]f̕_we*T 1W%Ŵe&~E|: GޚS JTNs&S^Uw3ter2nUZen dy:v[ `ٜm觀Oݒjk &I_HjW ML#lHa'!mv]6WLOL( #`eiwa_}u)&:]a)u6 W ¸T .rՂv vyxzqω6uǴ}ޕY8 ß#i3sPK% QN!Ԫm7w{s$Rc+$Zi҇=٠u% .AB m'g3v_IYi_\r3j۳l^6n!M' K*-BgS^mFi s^Ľz՟cA&Y -H,hU _~ \qKOi.`2 ^(MRs \ƚ{+bp1S n1p1R]ނ,XizA@gZ$c|O l\K#0_/okXg GT<~C& 'E*2drPlCav4-ZF?N}KD(U^}E`IWL &n~PMQYd>UKE<'uPq|>ybmXm|\v_6Z iO>1,Û6'5pgh$p?2x<U#0ሹk:0@=l\3zN.p t!P֞ Arw{謝6Fg}+@Cb{C1|OS`$ a$ӫާ+/\`I 6"\ڭc2ė7lsOnXz{`Ki9`0'(NV(v5MĸyFma `\ޚbUaAPKbl&Fccsw|yT qȟAљӕDxP TYzwYQvhU𓾔s gLB:Rg0x]D6~F(1pQk؍:u oBѹ5tt~ft y~0y<*(˸+&VuN ׷ov A@}txZGxThُ޴>yN/w{z5ZmlDܫ1x=1K>a PڳdR9d Y׎<ɍ;XmH;?438j^Cã14vh gH~. ӗGchB 8=F+P[bub7ZovYpP RyJqE2MJsH$^N;0cY %X,8^L[]5C4';K$NP%.v}Ӑ'msy;[5T9Y56">6'kݞ?H9d\j7:uUo_u3M`b!#]ĢEMRҀr#d ڨ46f *Xi;>E`|o߼-/UUyll&m9xUoW:.NPN<|epe=B(4/;+u ~ 9-,?JߌO}|۾Q8[@4+ |N'7u'x" RyNN!N! D=S. <A4]Noܮ{au~f9o]hg{/oX},gC߆ʆy W8 l ;w`TXŌm6w+|uqbdxVq鸮LŵtG4E}SrbTA¯2xt}rpg9=j#}BYTn@ >+#}PL;тJ;Pfp|{ԏ|TȆxmr3G-q,P >i_4ͻ\ބyZ[fěp#1ugKCo86c2uCHu:[g%jz+Fʷ]ٔANH&7rZᣜ)Gڸ)NyGC"̱5?3qw)<_.ĸY-!O:xY8&i󻑌'^$Wy5&#~+ IV=I&5>N.{پv6hf5{79NobxQMt9IBӑ;ʼnʳnTsuQC]AP)3pꣻ5gy9}@z俕LB-6闥z YcNfz!깛Ϋvr{Z4=UyP;+1 RV\ZtL>N&D\FQt #Pf~aNN~M̮L픲u$UtKja~=J`:LrlM%GF=U<3Wg.\9x1m+EkSCu"/?]_:iqa0ivnl¦IoMO5M6{A<&#.<|$F֤9tÈ5mȢv;ĆY爄(j#`naP:KvxQBvl-*<S\rPsT" } "< yddr/L(CKxb<A 0 jA~XK&Dgh"`N8 Q$;@Q0Ba*Iʽ+f*6\Ƃ ۘ/mjkrjӪV?wx.\.0WQege\zIuRn0ߵuӼ UB? "NoّN 5\I?v*2<N`M&㹂'iw<1iKhEap>@j}T94|8=/h~ߊgf?3~88zemPO0KFŻ%-Υd>- YP X 6xBjr1و; 1 ΛG|ë:Zz1w4?-?0{wgPKI(b' PKk;P settings.xmlZ]o8}_QYiF+JJi5Q074o&vcgmk:ӡ@ۀGC>\ W>9yB\`FSgVQALgשIM}+qﱇ yL $"Nt* ی\ʠɰSge3ϩ@ 9-0 (P#Q^AO}&* XI6VK[PcEy9%ւ`C{Ey,gC{ش@N/=F@ѫx"TݠxzIc|#'˚:6Q{R:g Xz)LU5̇^nΝgc(г׃[ş]. AT(ھQ4|(j Brd Q6z96ৌhx@;%>zC)L삗<<f SaDobvRanȓWtz! 6ҠjUo*Q#FJ)2^ mͷ5.]X #vC2azC`&>2WR"2UG8:*I`ɼGߦȼ fT{jb۬{- of:ިĹ+WO}DTBP!bWh1h`)BeZ*;!41Ay ت>>M`DA.aTexx%,fa.NgVL|U~^ɡuyɸA ,6v7qgƎ+Dee3 Pq&cQ\im3 0Afiם*MRY `"hH_y:NwcsfSGգ3y?}S7n~HW+E2+js`Wk6uJPy喧(;Y<}$hV_``Q-5ֿS#O - H?z{ض[C8v^ͱLK ˝4(t'߃e$ѲzX`;ғG{˻1~ɫc-im1ae7ӱt$tkpҶbE͊-Z(Z/(YнmN\2U㔶Gwςqt)Uټ֚mU8))χnԽC2̒KwIOjۥݵaNhX]8c'X=4#A~Oh0WL th3[Z&L|}O/3 z?00ӁfIrOFTHʼ)Pf׏ŊPKIn&PKk;Pf0GGThumbnails/thumbnail.pngPNG  IHDR<< ")@kPLTE  !!%$)">###!%*#(.+++&+1).4+06.3:22205=39?>>>4;C7?H8>F;CL>FP@MV\dkyBBBJJJAJTFQ]HR]SSS]]]JT`Q[gR]jU`mWcqZft\hv]k{ccclllbn|sss|||erivlzqrsu||~#%&12578<>@ÝƟȡʥΧШZpIDATxڵsFp/ 6I4ic0׏4i&-`%xwB: HdzfW{c~{ٟz- $Q X"<~JWRq"|@ML|i?RV}8߃Rɚl\-`WVsnG\N9skcuq *'%j7g=7-}M,ٸ.0nf4U$|t\b u bO 6N#>at-WXNctigfg[HY hyc7xxNj"3j4ҫ셦$ shFpֆ+A}S[[oaӖ ,xV,_3<>Hp5`DZ?B*5鋝 ;II$DMpw,}W5x,erv5ѳSNqp==Ўá"pqfQuE9nd6GRb"?Y13i^ڲ@ c v#-Ά\5u ül4m75>naU44l#"|ӌyL-UMu4xvVX7옑cĨOh[qxNy5Uj?f[2ǸEm1jGG85NUҧ*:K8K)&M,=,K'5 t3 9ݴ<{dp7sFzzurpӧiŠ m2[󈰾fgѥI*ě9s;]689}{%͞˝_xiG)=y);_8GxXcs #?=q+q] Rܡɿnz%?ѤU_LuQIENDB`PKk;PMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PKk;P.++mimetypePKk;PWw-Qmeta.xmlPKk;Pr W Lstyles.xmlPKk;PConfigurations2/statusbar/PKk;PConfigurations2/images/Bitmaps/PKk;PConfigurations2/popupmenu/PKk;P=Configurations2/toolpanel/PKk;PuConfigurations2/menubar/PKk;PConfigurations2/floater/PKk;P'Configurations2/accelerator/current.xmlPKk;P8Configurations2/progressbar/PKk;PrConfigurations2/toolbar/PKk;PI(b' content.xmlPKk;PIn& settings.xmlPKk;Pf0GGE Thumbnails/thumbnail.pngPKk;P`41&META-INF/manifest.xmlPK69(muse-4.0.0/src/svg/projects/midi_controller_select.odg000066400000000000000000000256371404105311000231240ustar00rootroot00000000000000PK:P.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK:Pmeta.xmlM W 㕪aVT-ruim_4DU ;Ƕ^6Bɭ YoO wzyhriAmRi|?rJ#/Ns3kITe!j!b5#2:kXs!75dUQI+7iv>kLVap]QFO\f0ʚV4X@Uۍ84չpoڥ˳re+ O+pPfCGGr,L xCQ0J)F%ov("aJ8H8#+6r&zU~ ?Igϋs)hMdo%>5|T+]>_a Óq#KiN_Zhɿ|sӺ6 MqupA{i>aY[^PKYi!-PK:P styles.xml][۸~0h>hl]}i&I/nl )%FJOeK"eyluyx9Dw4DؼG0Qƛyg߿+^>\`itD g",K J`SLt1 x%i.c2󗴨hfYʂtp]V@e R*r5 )dV' -ۓ$0BxNxfb\!]CHt)-0ɴäe(keXm0'I&ݶ,1Mہ~q;fQ[ITb5L+J-LeyZJxS〛0o>&4 DK/*66 ֺu5)g+F,R1mݲidQC!cPJ]jq1JRi(6G[_%{1ӉygNX™)LCH ™J!ܖvV_>MhAw+ݞ$rhuɐI~" ՄȄ ~J-+5TC#8œNxDk1F(}Hgڽ 3ɄD"%w AxxTD@\H'S!DkʵVߨ5:P kt-c!IFKS]>a[b!{W'}~QW^ ٫apAaΣ7'G5iwy:yLBA (>P\W2%GAA%[rl[%GA%[rl[%GA%[rl[%GA%[rl[%GA%[rl[%WA%[rl[4*ؒ;(daK-bKv*ؒ;(daK-bKv*ؒ;(daK-bKv*ؒ;(daK-bKv)ؒ7(daK-ybKN)ؒ7(aK-ybKN)ؒ7(aK-ybKN)ؒ7(aK-ybKN)ؒ7(aK-ybKN4S>[rs{5}V^f 4gKPmse`K3} rd{AUYt*L-`K3}b7`K3}u(XflـliϖGu1o$0BH} nі$?U1V+RzalA+20z:GDLGӑ5eizp?h0i֚~+*L3B#V-yX/_Zc?SeB(KMX~4"owRU$C똞iΓS\}_ N/凢$ H0J B2کuE'akR~'B \v**M,zF x". d dٞduߏpð<|%9[a@zȗDikŠ5RXh$+9lWs qH":\]Ju4F:I"`C"}a2o~8iC [r=捈o[&2t&=GETmE6`64sȳ za?WapR%ͱ206""9X{r??"pLFnNhe i=X=cC!b28 9EjȽ;EjȻBbr^h^\PC/3NRmպz@&o; EyxjZ'-2Ie U]Ccw *k0y-%(ZBvxX׳-F&NHae]udZ<_iYU >NDu|E}?u:fD7b1b-Ez^P$iHmX᪕3UHj.] %$ fЅH"x4KOap]c:PiLVB?;ٖӴ&[VNV?,Oٟ CY2<>7FoZLzc-="E˅.!س ˯^~Q,By 3Ҟ <[x)xcD+`- f:5{uQhr3Qꦣq>`)o,6wټ 㠶\Þt>Swm}mJ\Zc2vsH|3u^oMWg&j}b+sWmϠϩ;CYvj#Z> Oi#/QtZwuxN4"Iuzg|pAvS"v i.^ Ը;q1Wߋ,Y.18/# fQ(FQD"PDp+%Ku\v+J,WXwR*H"Y /PKO6PK:PConfigurations2/images/Bitmaps/PK:PConfigurations2/popupmenu/PK:PConfigurations2/toolpanel/PK:PConfigurations2/progressbar/PK:PConfigurations2/toolbar/PK:PConfigurations2/statusbar/PK:P'Configurations2/accelerator/current.xmlPKPK:PConfigurations2/menubar/PK:PConfigurations2/floater/PK:P content.xmlYmo6~pHe[v7)p]wC[H, l';3iɉB_6Ipw?<+vԶ-M};7:7EYoogf?l\ xnzuvTclu:Lk7{c6JxQul6-u]wEWupMd/ 0X顚ITuC-*t:ݜ_FۨPROnXVQk[^Q-^n;U_ c$rd\UBܖM7y{Z4=Uy;+1΢{ Rs1xfi-Z+,b(ABQAtϕadIwj0e߮+svnÞ1p%lFI귘@v핝l0Kfo,92JOm7 M+wa ^ 1|Nik@z*w&kieL4~QE 7["tQQy|!%{̝GV7v: #桭meZD9Bǂ~Շ8Y^4,ͺ<aɞ*k'>1Aw>G{:BXN2w޴QoTy{#V`T G1.Ϸƴ_F~5ÚGMp-)thr3 2~ p1y;b[yU; h3Rvz0YSdGU-^GAUգ?OAy V+; B:oG*eZk]?WR;hD0ߍyм[]FY|wBS([̼^7pж+uس3 ,f9^Z]<$C=@tS.=,5",ߏH +Y[ WpNUKjJ8jSa$^lSVE(!hɀSd5RSvKiQ*ϪKEORgCgjč\ _;r`4D$R?~w20b&2~Zx'%v3K_AϾSrL9K2dɄ-̧p/0>;Qh4KTNӟt(m">)d# "2DK;In;r}T 38v"rd#{8\gG 'P.8A=?",F n1d0h ^[=NkXBmQ}LHpYc, Lc_G8 HJQMKj넞SNA@D0Nbp>@ H9 @#t[ 5% 3<8 zHG1 Ȝ_d7xJxgQYz g6ȕ!0~4܂RT0x2&Lf(/@37(rsyuB$%9Ob&SBLLr9gI̗s&AJ_ b9p_R!LZk9/R4t0b'`(rBe(ChAC/pP vpc3ѕPK**)oPK:P settings.xmlZs8~"ʹ! t Sp&t%$J8-Cp/Iv]m8'PI ǕD()f0>[ɄР2L"|DrRf03&n2z,մ\TA5D$  v QĠĆS-?Kڻ+xߩeu*3g|~\zb%DjA%^ u.Y*1%ka'odq-md$Yx J''MWAab2뵳1jfYi05,S|}}KKLPxNrk0hb05(ec"#>)F{:uXJD w.>zOGҲl7*)> S!jʄGbp_MWE,c\QjqS4C({9ރzt^snIm?o ;( [W+y+w wY%NmD}UET9.AHmla9_`W?v,͕#L{h$2"Ԗ'P.}p*qqؽvz~@^(We_3i1_6>>6>G8>F;CL>FPBBBKKKAJTENXFQ\HR]RRR]]]KU`Q[gR]jV`lXbnWcqZft\hv]jzccclll`m|rrr}}}esivjwkyqrtvx{|~ƟȡʥΧШQb@IDATxڽ SHD#*:dENx!" yP*uLHxhW{Uwg{1k;vx? H&V8w=/_3AVn.d!oC1s&ϠRTA'I‹}}I0luu}Ǐ0c"xJlD$wE]M1A/4ڽ}yI%9ukݒ!'빞{Va8NGIy(Mx))^yWlqMWОi~'8'aݎ4[w?y{غn A'Q}}2x ؙ+jNX!ɎK{ϝI8-R*zX ٗOC!Nq?YU~{_:!]hC3͋C?ICiW_8{`0azwOQT!âhl.Q}ϯAE+Wj_SGmIENDB`PK:PMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PK:P.++mimetypePK:PYi!-Qmeta.xmlPK:PO6 Lstyles.xmlPK:PConfigurations2/images/Bitmaps/PK:PConfigurations2/popupmenu/PK:P/Configurations2/toolpanel/PK:PgConfigurations2/progressbar/PK:PConfigurations2/toolbar/PK:PConfigurations2/statusbar/PK:P'Configurations2/accelerator/current.xmlPK:PfConfigurations2/menubar/PK:PConfigurations2/floater/PK:P**)o content.xmlPK:P .!$ 4settings.xmlPK:P~##Thumbnails/thumbnail.pngPK:P`41%META-INF/manifest.xmlPK6S'muse-4.0.0/src/svg/projects/monitor_off.odg000066400000000000000000000205661404105311000207150ustar00rootroot00000000000000PKc$nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKc$nJ]̆Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   ##"" ++** &&,,3344 99== 55<<<<---;;;@@ DDBBHHMMQQLL!TT!XX%[[(DDDJJJbbbppp4 IDATxY0 "֊:S]s8mOO&d~Op׶mrބ!I& =Sz7n5t}0%bo#L:@1SJ^%p>\7:UQ>Vp2.RZnٯY_eFxB_ >a8h^};Ls c'd/j5ZҊ(]"T)ZNS X/l>\l|Y-RJ mZ\ - m͌R%RJԚ IlZ cQHK%d[ESHK?`+SmdL0.J-< aE)uq=Tܩu'.XlsLS5mt*J:+GMP2MvW*V/ݵvUƤGHxb&x%w!apI <|ޕEPN}˥ZnI!.$6YK긜%dIo4!va!>T3zxQ>:bfvLxo]F47)XjRУlj!Liӊ^Ǒj;ylV=h-3oR4cͭGq^:z|x-bpXS6>GUGl3(`lI'i6%(%+)JdC`gE&K0c+gº^Z= vqyC7m?'.S+]=_&F6OB|{ئ8tZ[ o]8 }u+uAUU^PK -PKc$nJConfigurations2/menubar/PKc$nJConfigurations2/floater/PKc$nJConfigurations2/statusbar/PKc$nJConfigurations2/popupmenu/PKc$nJConfigurations2/images/Bitmaps/PKc$nJ'Configurations2/accelerator/current.xmlPKPKc$nJConfigurations2/toolpanel/PKc$nJConfigurations2/progressbar/PKc$nJConfigurations2/toolbar/PKc$nJ styles.xml[_o6O!c. ]M{*˄z%_?IgHQv6^ǩ 83̏3CH~}ƽ;*K&krc|u_^4e1]$"2T[NKra~% %+9hPB4B {2#zٕV^Fގ,Y_Y3҉$ +|_ A,(=gkTfs 'WWW6 _QI8be897y]*[R9ޮ_ȕjwݭ`D3uY2Uf+۟ZصU,Y1L !UQVwE<;ܛ{|=&=x.a4,s\0ucpk 6P BOXl[*K `o$AU&sT!,RXz8M6` jPI#$`gMIUpEK/-nr!C s83D7`%|5LXpJ`@8~lⰾv6[ IMݼ3?'Xx#p>}jK 9 +OO/i袽:E3dHZu|DŽ㿹~sῠU;fm辅2x8 =BOY&A|6SZ.E}NM_$G[-_κ˵yemm0FQAt [V)a#G924y__E((21=~;O~7}/ك4㫫kYeQp6h4nQɚ乯ќONct\`X%U=H݆p,OW ݼyXD3JJ~x/ORb}i+7}6CBr|Mr@aߝq`  BKF~!;Q>▩׫gviIZ?!Y!A$DZb9ob/Iֿ &.Iq%z# ppjBFJ]r~P FXh/-ͧ{V1dFUM>t2 t!hƯbIP1}*3x 3Z<~.2#Zwՙ^1;A@D9LAn^( ;%`P(2 ~:µ06$5$ZA5aAZCŤ ޼33!$WT&<=7xoÛ#)h |cEQ8~\`oHb{q$PԴ'DbMq/HOFfHa34"5%fUiɟIi% WœqоJOb&w0]͌w^_PFr {BlQnϳ#Kn0Ʒ> \ү41ZB(M1pc¸%4,Ϸњr m@LYEk'M..o;a^,Pn1#ͅQ#)*#hU\&di|3tb׆]S 3&Owix޽tոln4i lYw|"m1G}/Ptع@;^Ej9udrPz C{i x#ڞhgiѿ՝I6$ZpsA{ۦRrU _PYD->?61KyHVf_M'h$I#?awϼdH D/O'O: [x/380q.02oPKd럛PKc$nJ settings.xmlZ[s6~x3@.0B†Y@ݾ jd#!JƤ,đQQ3WAp_؂pN1=ETBlć@Cјj\S 9]&vc2拾jv9\.Vn-uGO/~-;-C}G*_Q3෥oC`bxS+}SoF{|sYӁx;TιG܌+ ?(p_&p˱SOcxQטF24 Aw J8<͈ r/pujdl@rZn:>>E3TˑV{:'^&KDqf9td/9,/㇛ue1kfܒDb`$r޳ZDs:uZgdVbD4g:QDR!(бLJANG]תO#~\~kaRb_*]q!%ة @:]i&TIVؑ^!9?*-gSZҸp >֛7A*8mo XGĆ1 T7.E1u%Oz1 .Qd,Moh7 Ge+HpC0$:a&Uo1a n4&N5flpajoX#i7ľ !,g,C8 ]]#JԶtX}ϚO0u %wlz勊N82KQf@2BG>`g]ʥ3< tS%+O;븥#c80{bX[;byPKC"p(!PKc$nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKc$nJ.++mimetypePKc$nJ]̆QThumbnails/thumbnail.pngPKc$nJ - meta.xmlPKc$nJConfigurations2/menubar/PKc$nJ>Configurations2/floater/PKc$nJtConfigurations2/statusbar/PKc$nJConfigurations2/popupmenu/PKc$nJConfigurations2/images/Bitmaps/PKc$nJ'!Configurations2/accelerator/current.xmlPKc$nJxConfigurations2/toolpanel/PKc$nJConfigurations2/progressbar/PKc$nJConfigurations2/toolbar/PKc$nJPr ) 7 styles.xmlPKc$nJd럛 content.xmlPKc$nJC"p(! settings.xmlPKc$nJ(META-INF/manifest.xmlPK6*muse-4.0.0/src/svg/projects/monitor_off_default_col.odg000066400000000000000000000206351404105311000232530ustar00rootroot00000000000000PKoJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKoJThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   $#.&&&((( -:333&6F):L,=P/BW1E[4H^5KbGC~b I{Or }-`* bמ0S 〈”*'p\. exE-#,JWU3p8Yɧ9p 'p\. ʥRY$qz(uwl.a} ?ᅰ_n_ů ǒO'%S^JR9_6l~\38Rq {.^kB.Go ~(z;C]]y*U8Uyγ!.$Gi w'1nN"M$sM]'^^a¹g=#=[E8Kj1%wҔw^m֠<@eUIhRIni%c]]am{$@\vSD]"p7]=97t*m<.Qۯ#qcg_taȠtRoYU}BIENDB`PKoJmeta.xml_o ),6J{ЩJ} :8N5=˥?ʔRl}F} 6}//0L%[&L2S{V*4~ee,K;ipg2 q((;QJ.\׫QְAB0c[C2RqMR%eS\tE 3[8mg=ͪeFC꣢(6s lkS 1X yJ(D1-B̃(| NbE LKpEQRWR`ea ̳8ٔ`&WF r;X|0y4LWi~ޟ#̪AGw{0BtE?o}x `)`Fw_zm+NԆXO/m}/PKϮS.PKoJConfigurations2/menubar/PKoJConfigurations2/floater/PKoJConfigurations2/toolbar/PKoJConfigurations2/progressbar/PKoJ'Configurations2/accelerator/current.xmlPKPKoJConfigurations2/statusbar/PKoJConfigurations2/toolpanel/PKoJConfigurations2/popupmenu/PKoJConfigurations2/images/Bitmaps/PKoJ styles.xml[_s۸}Ico\9\}HPBB|>t(P"e*㜓8?.Ky6g⢸ Q0bE,^n#[5Q8nZf+C1\ Lѡ!RQK&CC=R X\a2-_ U~ۮ2M4es={Qsίd>t-mAK^6pBFU0՝D,4(FrŤei7 4A=|cwγVoЂy\U"^Btx҅U&MXn3[ /(츹߾ٓ&uIcN_/CIxUftK#Gd14ݎLEŮ=|^i>BWgDuqt6.[#ۊ-+6`mvVh"l a]5UJ{)Pn7q@0bb]l7PP( DPŦHZu^K2mәЕ<_zϵ׶bƜem\]x`q &MM{B:7r{ybGMחu=OjmL<0[Azი s G K i,/ԸALIqv.vk=ˏ,V1.kR|BzOm]/{ze kZGhowL6Ѧ&I: jڥ9110L)UV,/΅?A ƛL39FD3FI_߆Kl_R<{5dr6Yw6 4o*vʪpphbȳI&_¦W9ʾ9=0Y3c٤x8FERˁnRd=SZfu hhU˯YJ/M:zO֥tHuχѤ+{_G ?W@]lky<\xG[^76hӒ6V?!AxIgEs%ACp4*=B0.KiKnwߐQ8/vSN!'{n.[KFlK_Ưl.;t;XKOXN$yyZW nl"dDwwwvenPIb*&Qb XT CC5=9J2.&8C._ltvm0k*GSeOYr {<<y%k!Xj6- !:W`ݸ; ë;EVrͤ/c]Eq.h@;JMYA`m0ҴPB3$g,v̈́l8۾{rb%iߢ; .0@LN UKflME]&lٮb憶YpO+d3 !YvnKgA  +Q3WAp_؂pN1=ETBlć@Cјj\S 9]&vc2拾jv9\.Vn-uGO/~-;-C}G*_Q3෥oC`bxS+}SoF{|sYӁx;TιG܌+ ?(p_&p˱SOcxQטF24 Aw J8<͈ r/pujdl@rZn:>>E3TˑV{:'^&KDqf9td/9,/㇛ue1kfܒDb`$r޳ZDs:uZgdVbD4g:QDR!(бLJANG]תO#~\~kaRb_*]q!%ة @:]i&TIVؑ^!9?*-gSZҸp >֛7A*8mo XGĆ1 T7.E1u%Oz1 .Qd,Moh7 Ge+HpC0$:a&Uo1a n4&N5flpajoX#i7ľ !,g,C8 ]]#JԶtX}ϚO0u %wlz勊N82KQf@2BG>`g]ʥ3< tS%+O;븥#c80{bX[;byPKC"p(!PKoJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKoJ.++mimetypePKoJQThumbnails/thumbnail.pngPKoJϮS.meta.xmlPKoJConfigurations2/menubar/PKoJ8Configurations2/floater/PKoJnConfigurations2/toolbar/PKoJConfigurations2/progressbar/PKoJ'Configurations2/accelerator/current.xmlPKoJ5Configurations2/statusbar/PKoJmConfigurations2/toolpanel/PKoJConfigurations2/popupmenu/PKoJConfigurations2/images/Bitmaps/PKoJ 7 styles.xmlPKoJS/N pcontent.xmlPKoJC"p(! settings.xmlPKoJ(META-INF/manifest.xmlPK6Qmuse-4.0.0/src/svg/projects/monitor_off_default_col_round.odg000066400000000000000000000202101404105311000244470ustar00rootroot00000000000000PK$nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK$nJy>>ggThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE &!+$0*7--- -<;;;'7H(9J1E[8Nf9Oh>Wq>XrDDDJJJbbbpppGcHdNnOnRsTvZ^cmnq΀17IDATxr0FQiD H}ES+B:#+|N,j"\'18zI43EN"ډ,` `ѥ7Kܓ/ X2gKُ}8S0gx߿\1 -VlqbΔg [AAw _/t1-6B"jR"N!oLPcm=2^۸=ߟ7.!&#d+OIO?٦U{?I$B7È3Nfng{mUdd~+~6mX4[3f`$EhCnIdaaNc xLh×X鞈?=~io¿_ pԓnIENDB`PK$nJmeta.xmlM ݫ I6F6+VT-rͲ`^Ǒrd晙w>NJ^u Dhf}M~ӓdpVh7\5&qݴψ鄞CȒ&d9)@8 C6G˲;]*RAD 0٠VQ]Jpq(X,sc.C4(=GhLV[+6eƚu5F4}h9W-e\qoW) my|,Vq)GmQpGv߬yCZtyd8#cߧHj)JʬXm5Vp&EK?~6NoE:eJ8g^> dICix} ri&?PK9PK$nJConfigurations2/toolbar/PK$nJConfigurations2/progressbar/PK$nJConfigurations2/images/Bitmaps/PK$nJConfigurations2/popupmenu/PK$nJ'Configurations2/accelerator/current.xmlPKPK$nJConfigurations2/statusbar/PK$nJConfigurations2/floater/PK$nJConfigurations2/menubar/PK$nJConfigurations2/toolpanel/PK$nJ styles.xml[_o6O!c. ]M{*˄z%_?IgHQv6^ǩ 83̏3CH~}ƽ;*K&krc|u_^4e1]$"2T[NKra~% %+9hPB4B {2#zٕV^Fގ,Y_Y3҉$ +|_ A,(=gkTfs 'WWW6 _QI8be897y]*[R9ޮ_ȕjwݭ`D3uY2Uf+۟ZصU,Y1L !UQVwE<;ܛ{|=&=x.a4,s\0ucpk 6P BOXl[*K `o$AU&sT!,RXz8M6` jPI#$`gMIUpEK/-nr!C s83D7`%|5LXpJ`@8~lⰾv6[ IMݼ3?'Xx#p>}jK 9 +OO/i袽:E3dHZu|DŽ㿹~sῠU;fm辅2x8 =BOY&A|6SZ.E}NM_$G[-_κ˵yemm0FQAt [V)a#G924y__E((21=~;O~7}/ك4㫫kYeQp6h4nQɚ乯ќONct\`X%U=H݆p,OW ݼyXD3JJ~x/ORb}i+7}6CBr|Mr@aߝq`  BKF~!;Q>▩׫gviIZ?!Y!A$DZb9ob/Iֿ &.Iq%z# ppjBFJ]r~P FXh/-ͧ{V1+elv>JJڵUrjSI6˒[SgKͺ~r25XSm%Uf(צ]jfrrY0TPnj|!d=u-PE$k'oӡC}!w(di2q}LSɳp9i@ 1!Fĉ=X|OV0:FiQ5O#A69 $u Q/I[!?rzUrJ|idQ;at"t(0mY,M ƗC㒕ﭥX7;G ݵT2#ܪ-Lv80hRr-5#2$hz7͜IRQ QjÇw!u+W<5OpA%&puw}3hv=nJ@顐G M6@:)j -Kf/,'fsO .Ҵ7>8ov~޿jͱhL?+Kn0 kg}V_Cw/F-jNU+yZIܮ[,ͧ dzjev;BAR3"'gPhDCnmYa._,ͯ0ܬ' P`;a8 Y?| #-Z#^G'kqZUD8Py[(f>OV/_VҚ1@ kE  ( ΐ0 `F'rp"BĨ<[O/g8j4K 6Cԁ.TmRԐ(jĠj:ab1 a݁>X. z^=*W _3WNOv/$)1A*L71&ITZ/'* E2Vss94L|KX pz$},׾@9bB#;LF=n7D%C,]4Gb-]sEj_QRסn?𱁷x%ScWW#9:9MҀZsLV#_p=Y_Q1 qc"Fp"a4=(.{dA,Z%cU[N}Em^ k&$EV{DZbs֕Rd eL2v,#Iq/c_S™^5{&6y^bfqLZ<,Dq,;5=ji<׮w₣q{9~*: PLd'|K {[ʦpp??4~PKk[>!PK$nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK$nJ.++mimetypePK$nJy>>ggQThumbnails/thumbnail.pngPK$nJ9meta.xmlPK$nJConfigurations2/toolbar/PK$nJConfigurations2/progressbar/PK$nJ:Configurations2/images/Bitmaps/PK$nJwConfigurations2/popupmenu/PK$nJ'Configurations2/accelerator/current.xmlPK$nJConfigurations2/statusbar/PK$nJ>Configurations2/floater/PK$nJtConfigurations2/menubar/PK$nJConfigurations2/toolpanel/PK$nJPr ) 7 styles.xmlPK$nJih Ccontent.xmlPK$nJk[>! settings.xmlPK$nJ(META-INF/manifest.xmlPK6<muse-4.0.0/src/svg/projects/monitor_off_round.odg000066400000000000000000000205651404105311000221230ustar00rootroot00000000000000PK%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK%nJ]̆Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   ##"" ++** &&,,3344 99== 55<<<<---;;;@@ DDBBHHMMQQLL!TT!XX%[[(DDDJJJbbbppp4 IDATxY0 "֊:S]s8mOO&d~Op׶mrބ!I& =Sz7n5t}0%bo#L:@1SJ^%p>\7:UQ>Vp2.RZnٯY_eFxB_ >a8h^};Ls c'd/j5ZҊ(]"T)ZNS X/l>\l|Y-RJ mZ\ - m͌R%RJԚ IlZ cQHK%d[ESHK?`+SmdL0.J-< aE)uq=Tܩu'.XlsLS5mt*J:+GMP2MvW*V/ݵvUƤGHxbX#@y:^ӵ惃(F`byVsN+r[f^K)u =6ou؈uzJ2!Q3V_{9땭Wb r4es9C^b6L"aD҄$IqU( FB$n|<[Y<LX"i_C*<| BFknG%wjq_bc| dw?g: f7 /Y4ټW. siWNZX\_PKZ=-PK%nJConfigurations2/menubar/PK%nJConfigurations2/floater/PK%nJConfigurations2/statusbar/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ'Configurations2/accelerator/current.xmlPKPK%nJConfigurations2/toolpanel/PK%nJConfigurations2/progressbar/PK%nJConfigurations2/toolbar/PK%nJ styles.xml[_o6O!c. ]M{*˄z%_?IgHQv6^ǩ 83̏3CH~}ƽ;*K&krc|u_^4e1]$"2T[NKra~% %+9hPB4B {2#zٕV^Fގ,Y_Y3҉$ +|_ A,(=gkTfs 'WWW6 _QI8be897y]*[R9ޮ_ȕjwݭ`D3uY2Uf+۟ZصU,Y1L !UQVwE<;ܛ{|=&=x.a4,s\0ucpk 6P BOXl[*K `o$AU&sT!,RXz8M6` jPI#$`gMIUpEK/-nr!C s83D7`%|5LXpJ`@8~lⰾv6[ IMݼ3?'Xx#p>}jK 9 +OO/i袽:E3dHZu|DŽ㿹~sῠU;fm辅2x8 =BOY&A|6SZ.E}NM_$G[-_κ˵yemm0FQAt [V)a#G924y__E((21=~;O~7}/ك4㫫kYeQp6h4nQɚ乯ќONct\`X%U=H݆p,OW ݼyXD3JJ~x/ORb}i+7}6CBr|Mr@aߝq`  BKF~!;Q>▩׫gviIZ?!Y!A$DZb9ob/Iֿ &.Iq%z# ppjBFJ]r~P FXh/-ͧ{V17ww 6D*eΒ0 59emt^pNjb7uywֵZ2ldqjTiqA*љ](g-طd=M_ق}\TcjS,*8dHӣ(vpxζeV',*1a,t="M`z"rrjF'*$QbNs )'kS3 ReO"m+#{߃>x%k UXR1y-ڷ眻PA[6y\޾JE8F \ƀPI1ǭځU>wxM*to#Z+CfTESiQM'S`OJG2f2DpӇ2w9Z늍0PgC;fm(پ ebӶoX? ^4H`\;,l-xS9 $;A$5*ĬY6e7=!E ;Y7G"CryÙ'4wHy^{LJǏEfPV:pk>b'(~('];8hMag<  _?"OGVD@B!0 6HR˾xzhಘBߛsa&JV2o~-xx|$eÐMјx(:9j47G֏-[Cw/*ԜĴH)Iib_2)`ćYϐ Yn[E5\I2QcVрra(2K>VEecEɆ 6>Qpvz"i[Ŵ 俢f 'ߖ u':މ`bWb!b ߌ8ev G+ |~Pྊ]VLWW"B#w}93x*4#F}PCb_JWmm'vh RAZh|R=L /WuqhŢòY-E+%u|3FCsM“YH /!{^hNyYZgN tA$3͙NyE49|ȼ:60h{u9|}H]"?VbRL &`%"0 E&Tq抳V#"j~p+DLNO=j{L ;5gI|G͊~E/ yGQ‡t7.E1uO{Qylrm2&N)FQ]0$qÌ9 b0 iL7k)*G/8[ZX-Ix;$XedOȀ3!&3I( [naGQm2݆dY &Yh1`%XdY2',9 tl+tD{eB8#-_y~ }[1!òo7wPK 8f(!PK%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK%nJ.++mimetypePK%nJ]̆QThumbnails/thumbnail.pngPK%nJZ=- meta.xmlPK%nJConfigurations2/menubar/PK%nJ=Configurations2/floater/PK%nJsConfigurations2/statusbar/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ' Configurations2/accelerator/current.xmlPK%nJwConfigurations2/toolpanel/PK%nJConfigurations2/progressbar/PK%nJConfigurations2/toolbar/PK%nJPr ) 7 styles.xmlPK%nJb content.xmlPK%nJ 8f(! settings.xmlPK%nJ(META-INF/manifest.xmlPK6)muse-4.0.0/src/svg/projects/monitor_on.odg000066400000000000000000000214621404105311000205530ustar00rootroot00000000000000PKoJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKoJgThumbnails/thumbnail.pngPNG  IHDR<< ")@,PLTE   # $,.9&&&(((333#H'\$)W",[$1\$+b'/m+5b&3m+Gd93b(G:kJS4;L ^,u͏~%݉Ud:JQIwlE|)э"ceaLyQF,r", WgA3Ƣ)k'g:."NȚ-z.NpF0dٖUzz~ _Q<")U|nopUXUAƸqPKGu'Ů_U܋6[BSmlw ~? ؉Ɖ2 םgh@_f{ i#=Yi< 1o؍=\KY.:xR.” S]xs=,G/!ֲf[ @uN2RT,j_9x;^2qFkIENDB`PKoJmeta.xml_o0)+P=ڤe"b7mJgL`<{SS\6D1 .bBݧhޗ "('LѮF UҐhvZUanD\N27wNOk[@qJ eµ=(5 @1;$5#wZYߔj5ZJKA66G`\OiD}FT5<ԋVR4wڣ p]l  c۞`6x8,),P}C`#&'1Nr pEQ0JRК`H'n'ѕ3aX}+zܭ?gs;cRFkn G%wj'q>bb|$dwج4V_Z`:QmV. iWNZXt\PKk/PKoJConfigurations2/menubar/PKoJConfigurations2/floater/PKoJConfigurations2/statusbar/PKoJConfigurations2/popupmenu/PKoJConfigurations2/images/Bitmaps/PKoJ'Configurations2/accelerator/current.xmlPKPKoJConfigurations2/toolpanel/PKoJConfigurations2/progressbar/PKoJConfigurations2/toolbar/PKoJ styles.xml[_s۸}Ico\9\}HPBB|>t(P"e*㜓8?.Ky6g⢸ Q0bE,^n#[5Q8nZf+C1\ Lѡ!RQK&CC=R X\a2-_ U~ۮ2M4es={Qsίd>t-mAK^6pBFU0՝D,4(FrŤei7 4A=|cwγVoЂy\U"^Btx҅U&MXn3[ /(츹߾ٓ&uIcN_/CIxUftK#Gd14ݎLEŮ=|^i>BWgDuqt6.[#ۊ-+6`mvVh"l a]5UJ{)Pn7q@0bb]l7PP( DPŦHZu^K2mәЕ<_zϵ׶bƜem\]x`q &MM{B:7r{ybGMחu=OjmL<0[Azი s G K i,/ԸALIqv.vk=ˏ,V1.kR|BzOm]/{ze kZGhowL6Ѧ&I: jڥ9110L)UV,/΅?A ƛL39FD3FI_߆Kl_R<{5dr6Yw6 4o*vʪpphbȳI&_¦W9ʾ9=0Y3c٤x8FERˁnRd=SZfu hhU˯YJ/M:zO֥tHuχѤ+{_G ?W@]lky<\xG[^76hӒ6V?!AxIgEs%ACp4*=B0.KiKnwߐQ8/vSN! =+du$Y#(>`yZ-d]B84l$Lljy{BLfb@,x #z\gk.:` U4fJ\v0Rs[FR) 2hVpj?S' +Uj/ûuG> n%3ݽ17$)#KKugS/;ƽ_qv&{ZC!Pu=mga0pxRNDu `K%7싦.I!'>33!$uPUKH@꿌7x˯;V%)3E'89RbbeM/^jM yfZHlx ib^5™QogBh(LjΔɪR|ad3iųxqVJE)B$6?aaL~aVx~=Gc>ei6q'vsip`ϔl lUSaa:#,+vdxR6׽4=Noډ$ѢAEr5?Q<~vIWܬ2_>;HU!5=su+paGf| LW=ÞL1 G~";/]%ߙl6<|O1%q4H\i[sDEԆ6ܑ)Оn2|4%=g!ձ']s0z"90.xP~? zIk`@UdDOVo}`]fSw2c1e 0/PK]\_cPKoJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@ME޼I>b4hjFD/w&ź(..*uM$xCi0%mfr= nX'=̄â߹-& ̉~ٽL^9 T >w[ :V0[epY@&cRmBZl07(Mqt=&M6$:O01v\[_z_v(SNe&S '^.p?tKwWŸ1@1,,shPKY(!PKoJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKoJ.++mimetypePKoJgQThumbnails/thumbnail.pngPKoJk/meta.xmlPKoJConfigurations2/menubar/PKoJConfigurations2/floater/PKoJConfigurations2/statusbar/PKoJ,Configurations2/popupmenu/PKoJdConfigurations2/images/Bitmaps/PKoJ'Configurations2/accelerator/current.xmlPKoJConfigurations2/toolpanel/PKoJ0 Configurations2/progressbar/PKoJj Configurations2/toolbar/PKoJ 7 styles.xmlPKoJ]\_c content.xmlPKoJY(! settings.xmlPKoJ(META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/monitor_on_round.odg000066400000000000000000000212351404105311000217600ustar00rootroot00000000000000PK%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK%nJvo%Thumbnails/thumbnail.pngPNG  IHDR<< ")@bPLTE #'!*$+&-( 0(3-61 ;2<4 ---;;;@7 A: K>B;MC F@PF RIZK\NTLXO_R XPgViY h^q^resfujxgyi}opxm DDDJJJbbbpppkn r vux~|} y# $') "&-&+."*ť ͬ ϯϲҲնָػǰ&ǵ4ȵ4͹3ֻ"۾!ٿ($9'),00259=>@qPIDATxOA9O HxHc@ A)φH@ Ż"z^C 8gvw{2;. =Z-7ysDӀB007igggm^ z `xu hy.OIPtu+bəYɺp/q/^,vJH,܀ozi)֡-/G"VlqW xP6}ҳaF a=a5L[%-{ ~7ᨦ c2mC3Gk Wl[|k3[Q =X %%3n1T)p41fxvLliV mZ+Њ}Қ˜SQ:OyLGfŊRKmubEXNMc*UQڰV*]-OK~Aca ;ROWbq֔Fz}xgq@JUj_J.QCr%QҳN])4Z>F)$ (-Im3 O퐲Z7'Çb˴fdr6Xcu1讚e*SVO;UxKaH3cf!L//rju6HF_ߺM"^C$]w҆b/z|_R8:/ޛ1? qV  IENDB`PK%nJmeta.xml0E Dg !`FbVjF."'u 6͐} )t*s߽~~.mrm0 yڅQ>VJ"('LѾF-uҐikZUa[nDu\7V΍vk;0 J*eun<(  @13;&7.#u[[ߔj,5ZJkQ65GO `iӊ^Ǒj;ylV=h-3oR4cͭKqS^:z|x-b𒰚lB5"gȫmn "8%I'iM n(JFJBݗ,/6۬36r&{뵯U}ݣ~diMNt$Y<< ٟ`:~rj-|ЋEߊq048VpK[PK .PK%nJConfigurations2/menubar/PK%nJConfigurations2/floater/PK%nJConfigurations2/statusbar/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ'Configurations2/accelerator/current.xmlPKPK%nJConfigurations2/toolpanel/PK%nJConfigurations2/progressbar/PK%nJConfigurations2/toolbar/PK%nJ styles.xml[_s۸}IqboLsڹ DeK> JLrs2]`vI>;J.`|#2:U41[$22kR`raA%/9XB,>,eGdC /ٽ*-YaE7C0S9T$$YA5^uֺXfL/Z|jp+C&.VqXfLӡ!R^eKCC5=BX\a2-[ U~fۮ2M4e3={sίT6t-mA+^ 6rRFUnԝD,(Fq͔eeC ];b$4烡GWU~bT$taI2:)5J%I'+3 !}@;6#ևD { yjeLq$Qa t7CPLԙ1s)IVCbIVYNu}9Dr[ǻLO%)IX,ʛ76[7#J^?qH{CAJY!ph!?`ԚɊ`5[1;pj0e:H7d}c PܖeGXB)5x'4oG?U%ꅨOlx}A &Ȟ4#dM@e_&eh / A%s wcp̀HHl}l>K NhE-Ly56?zBW6,J36S?(Vyh4FȎCØ c\O;HXJ+᪍z_vŚAIِ)͡:K'@13gCʅh('4RTDK; Y!a2%Ҳ,(DAڣ XTEф1:nb n{Mn8nXznOcYebJ1Y;\Jإ @K.?Xo;`u#crxlb|+̟^X2?E{}f69T4myJ8]V%7.$}/!>dH? ǟy݄sOj7V~e\Pz_ #&/sC˥Lϩ dr6Yw ʹo*vPUZ=0Y3G}i3<&5yuPȶv_QswOepcmZv8"J~(wۤ8HsN`8ƟV v[eP-q;R0 Z|y@;v?~|TIqL+ܷÂE6_r?w}K>25PK% 7PK%nJ content.xmlXK6W Л,ާvȡAvS\Dȯhʶ6BtZhp ׻"2LgI26 _~EA1r:¼?Zev68RTe54θ uo̮J޳[kS v`lu.vBR}O5)^ Q;FeZdqngŌ2Nooocucd0bSq:K[g~HuS=995HS(v 19mڔٵ)GҌHNȧSeAiԵ v*,s]Ơ-vaHh?WQ3WAp_؂pN1=ETBlć@Cјj\S 9]&vc2拾jv9\.Vn-uGO/~-;-C}G*_Q3෥oC`bxS+}SoF{|sYӁx;TιG܌+ ?(p_&p˱SOcxQטF24 Aw J8<͈ r/pujdl@rZn:>>E3TˑV{:'^&KDqf9td/9,/㇛ue1kfܒDb`$r޳ZDs:uZgdVbD4g:QDR!(бLJANG]תO#~\~kaRb_*]q!%ة @:]i&TIVؑ^!9?*-gSZҸp >֛7A*8mo XGĆ1 T7.E1u%Oz1 .Qd,Moh7 Ge+HpC0$:a&Uo1a n4&N5flpajoX#i7ľ !,g,C8 ]]#JԶtX}ϚO0u %wlz勊N82KQf@2BG>`g]ʥ3< tS%+O;븥#c80{bX[;byPKC"p(!PK%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK%nJ.++mimetypePK%nJvo%QThumbnails/thumbnail.pngPK%nJ .=meta.xmlPK%nJ8Configurations2/menubar/PK%nJnConfigurations2/floater/PK%nJConfigurations2/statusbar/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ'QConfigurations2/accelerator/current.xmlPK%nJConfigurations2/toolpanel/PK%nJConfigurations2/progressbar/PK%nJ Configurations2/toolbar/PK%nJ% 7 P styles.xmlPK%nJca content.xmlPK%nJC"p(! settings.xmlPK%nJ(META-INF/manifest.xmlPK6Qmuse-4.0.0/src/svg/projects/mute_and_proxy_on.odg000066400000000000000000000226621404105311000221240ustar00rootroot00000000000000PKy?nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKy?nJkkThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE      % *$/$0)5+9"+5 : ##++22&&&///6 6:":222;;;&5F-@S3G]4H^7MdIIDATxڥi[@=1"j@[ZPqAEPQTh@ٴLX; Ya>}̙;瞜ɜb)# .͚1$ljRsH(Z Fh‹{πң˺ ><[GF\'a|,"pV.0v/LBNwݰ6xCC4MٴvIq-c4wE*GN`%pkI`RC<ob0{=:<|ܝcEa _#m^O` iJb%3Sڕ<8ceɱ@'F K[dAp1 6noc8C5s/QWJT4xSyVX99ɋPល~t)$sqZmGNݾ=.NSeǀG)ǚG5uTAhĈ56lymxnf$V!  "pj@gڀ)V"@ǁ&)Tx24 'uPwùW.O}"Pyf=[F %:JY|Ov5n4{SUݓ_t?7ltL(,y]Ġ5C aUD(Q M qݑB&ʎ˹gb<H*Ay:@n#· Nˣ9XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKy?nJ content.xmlZY6~ȱ.{}t)AE߸%3Dv}DSe94|3g43fzg=fťq.):A&V%ϳ~ V5eq|P ]3 #ӝ|0%՟H%+t4@nKuoMh]**x]bFe`f+eӏ:Me.}K+(d8\nczv8q zx2G|hj%c vQO-)#Sќ$+N\J-qXѕ* *zMzN=فMdz0^6i+f])_#)Ӯr[/ٽ$κݽx/6~u!\nP\M ($a4ۤ0!}:dh#~Zsۣ3l=fZ(F*g<'J O.`|&k5_fˤBDYtD!rS-Q" Ep2(Tg'jQm`T?/1c4zy0ϟXR'2(-&wR^Ug9 &"|piY{ "!$.8`8.]'̟Na?EF'= wA*8樚: I:ZgPu=9t9W2:a>a)H"J]rj- 0 QP0G)k1HZ{iBcV`r@{>Bc~ňxx͔=G.$Σ0PK* !PKy?nJ settings.xmlZ[s8}_a3t$ݚ t !87R@EGBA >p}:3>1*:0,^дhar,W462Wޱ0-k_/km2~Xm9,ȋrQFGb~fuS}uS)g6oAѳ܉}u?C9م^(Dx<)>X, I Q{ϏP)$W SDB778-tAfʒ:gtïπ-ѧXr~+cLEz$6h/t|Gsu1uSP:H BAB0kD{뎃q_=i$a $12V:9tŢj@2"ǝ*e8th8chj4JxD'$z 1hqqw{Y <%8No}2,Vf{O{U91|@|U@ $ *.`N25*A@eQʼu,% Ui>dw@LeP zJN_N=򅛄έyuNbȞݑdz@_Iҳ^@]LODr2&u piT$';S^2aPfkCwj4?4HϞx;$#_ -Ro}4!2ꣁz:tl6ڰoTiE~ZYW8< `ÞW=]ݷgWf.5GUm^^~zwz*<,Z#T3'Qf6Pa\Oof70:]yOV"}YvbmW:=yz|`Y=|9.%l ًi0FH|OCCnp$PA*,@J?[~*cPK="#PKy?nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKy?nJ.++mimetypePKy?nJkkQThumbnails/thumbnail.pngPKy?nJ(b.meta.xmlPKy?nJConfigurations2/menubar/PKy?nJ#Configurations2/floater/PKy?nJYConfigurations2/toolbar/PKy?nJConfigurations2/progressbar/PKy?nJ'Configurations2/accelerator/current.xmlPKy?nJ Configurations2/statusbar/PKy?nJX Configurations2/toolpanel/PKy?nJ Configurations2/popupmenu/PKy?nJ Configurations2/images/Bitmaps/PKy?nJ]b66  styles.xmlPKy?nJ* ! content.xmlPKy?nJ="# settings.xmlPKy?nJ( META-INF/manifest.xmlPK6f!muse-4.0.0/src/svg/projects/mute_off.odg000066400000000000000000000172271404105311000202000ustar00rootroot00000000000000PKI%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKI%nJ* 4Thumbnails/thumbnail.pngPNG  IHDR<< ")@{PLTE  )5+9///"/>222'6G3G]4H^7Md:QjGGG\\\A[wdddllltttyyyLk^`cknqΎ~IDATx0PBUAD[0 &t˜0g3vNplog:qX8=L9ؖUؕ(VA,0\bMK`ꃯNWY} H{<N6Ea$]:yai8X#@y:^ӵ惃(F`byVsN+r[f^K)u =6ou؈uzJ2!Q3V_{9땭Wb r4es9C^b6L"aD҄$IqU( FB$ejq6Y<LX"i_C['ϳdoL?y\rV|*F:wBvsޯ`[~sj ҉El^qtW3 ~_ nx:i]CP`|p×PKЏ.PKI%nJConfigurations2/floater/PKI%nJConfigurations2/menubar/PKI%nJConfigurations2/statusbar/PKI%nJConfigurations2/popupmenu/PKI%nJConfigurations2/images/Bitmaps/PKI%nJ'Configurations2/accelerator/current.xmlPKPKI%nJConfigurations2/toolpanel/PKI%nJConfigurations2/progressbar/PKI%nJConfigurations2/toolbar/PKI%nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKI%nJ content.xmlXMs6WpBb-eڝLvzABMID/$X}}P+-d Yf2u 1 ߯y.O3ɶMdm;ZunUJNkZqʆ׽WSV;ͱn3_<}e 3ES |eK`jgQJQ1MJ~3 ,Kb.`pV1Kic+nTog&SC jl9m"g]1Y]bfj,x(E6]*ٌ<~<>tk!v@SK)]vEu+yWy5m prxqV}۟p>cܫA@O*M*x D@SàV}dzh0e>)6l_LI?|$ _F47˯Ӈ3/%U9k|Cэӭ9Xdq~5[Cw/Zx\µP7T᫉}Z'UY۹D ڲ/<ڋ oZ,^ܲ*ML.ob6\ W"\N9:hX lX T􈖢T hs\r u,2Aƺ{gi ^vJ,JτyOħ<hv#>Ѱ6n!X/% 2;N>{K'%j_V](J9ݖ&짔局x1K@vzKI_rkZ=9[.{mxdvs< h?,*DHwq$N W-ݏ;qhu}o_LorX'PKvO#bgPKI%nJ settings.xmlZ]s6}x3@>fa; $l}ȺIW2`cNyH[:J_KȏA*U)8>{_\xS .) QGfPKQG 렎մzBx™xjxSf1ʉ_j~v54 щlsic6Ɋ>_E %«BƳrٱhlE ر[^گ}YkG5Wau K,Оc3<;S- dzyɄ .u*i0izZ+~l2M_9?= _ ITb M*oT2ǜ59"f]RiiN״g_%]4^Sw$M W9ѻj H4Drx+>-&;8GA;Qp~ziDȹ3|n7.RO@ߖ u-ma9nK,baÛr\fܸ4][LUW#9 )$)xEpxgT s~UR6 q9s>YsfTS{W?{Bob{U燶_";S5KQ:2Ev`9Ǐa?ܬ\ , $Ab'bx/Rk)+]z:{Kf I{OoE|@^M2/6 ڳ;ԵDnï'>Fا% GԘAE6haC# U]+ ^5=iN-0&1F@YN^#tS3~2senI>=7Di^ h)FD?&ź+^D|2qNpI':ca49bFF)$Io3wz Ðqpk!sv|=+ }BQQGՆX=@vP$蛜e"I4a&'췘p7^ <~ibk1޲qLZ<,Drۗ,Kx3yot@{{\A`.._~[&2C#';&m?4PKՔ?!PKI%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKI%nJ.++mimetypePKI%nJ* 4QThumbnails/thumbnail.pngPKI%nJЏ.9meta.xmlPKI%nJ5Configurations2/floater/PKI%nJkConfigurations2/menubar/PKI%nJConfigurations2/statusbar/PKI%nJConfigurations2/popupmenu/PKI%nJConfigurations2/images/Bitmaps/PKI%nJ'NConfigurations2/accelerator/current.xmlPKI%nJConfigurations2/toolpanel/PKI%nJConfigurations2/progressbar/PKI%nJConfigurations2/toolbar/PKI%nJ]b66 Mstyles.xmlPKI%nJvO#bg Vcontent.xmlPKI%nJՔ?! settings.xmlPKI%nJ(META-INF/manifest.xmlPK6Kmuse-4.0.0/src/svg/projects/mute_off_waves.odg000066400000000000000000000211051404105311000213730ustar00rootroot00000000000000PKR%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKR%nJ44Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE +9###*** .<111;;;-?S;SkCCCJJJSSSYYYbbblllsss{{{JgTucegnq΄eIDATxڵ뚢0 SaVV:ȠzEZ*g /_I bA cC6OÅ\1aA'X. Wt %(pMX̭-|ſ6,D {G؁؛5ש\9/vQx#/_),BvJ=oImp M!yAYam¥ 2vLK 83s~$8e-J4…qxE:G!lrU`QyD(Giۅ]X$W >]kIYmC/:i8| eZ7B8u26~YFNe!jՀ+P>{(w=HyKe4p:Aic}q>Oʰr[VMT-מ'*@`G dp9iC̍A=TβSmC|]Kdn:{ߛˮ{JhVtIOmS.Y/Kd,A~2o$m6d&n[!w&4: g}I,ˀ?Q.\ۙSQ#0c[C2RkN+r[fYKk} =ʦL뙶ouو<ͭeF]Ἶ(CfWޤ1X y(s(- E!FDpÈ&bcA!W9gt%EB`Jqm MK7|w*}?ۼCJ–d~ޟPbPkS>Ƀ_CMNfIo)6c'kߊq0,0VSI}PKic.PKR%nJConfigurations2/menubar/PKR%nJConfigurations2/floater/PKR%nJConfigurations2/statusbar/PKR%nJConfigurations2/popupmenu/PKR%nJConfigurations2/images/Bitmaps/PKR%nJ'Configurations2/accelerator/current.xmlPKPKR%nJConfigurations2/toolpanel/PKR%nJConfigurations2/progressbar/PKR%nJConfigurations2/toolbar/PKR%nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKR%nJ content.xmlYێ6}WJPdȺ&w/@SM I/Wdf%Q%ݯhʶn&q ᙡs~)r3Nhya#϶pӄمܾ朦)"pbZ @ZzaX(QB ZZ腚bVW`S[,]t=|f6C%jtNJ!Eb^ Q-\w^ሲ(rT;k\bB%s,'?[`'K媸lph@Z1˕9̐ɯlpvf=a 3J O01u $=;w߂P=޾+%PŌTYM}JvU*ԇ]xح $|͈̀'1cqZ |) QZ<57%.L;w=IJ28$0  W A":FKQ&-4cIr .sKݩN3KLEw(Y N*+1N UwetU&>ě 3"E(Wjsr7L6ذ)R-s%5CSpHd8ZݚNJ0s$ݷGX18>RȒEhukjHqRc'q/]}a!PTx"Bhn/oQEw{zж:%p c@eQC1Es&AIp:33%5ø!C%=xGU^g)crE3V;ʎ>-Y[Rzf M_T(3BsՌjIvBL^MԋS+ɚ حFũP0sU.Κ$F? 4>ےnCcn0)L*()ɡqѳH$I%K:('$o P;qd%&84E9iJ)(pmHf0fa; $l}ȺIW2`cNyH[:J_KȏA*U)8>{_\xS .) QGfPKQG 렎մzBx™xjxSf1ʉ_j~v54 щlsic6Ɋ>_E %«BƳrٱhlE ر[^گ}YkG5Wau K,Оc3<;S- dzyɄ .u*i0izZ+~l2M_9?= _ ITb M*oT2ǜ59"f]RiiN״g_%]4^Sw$M W9ѻj H4Drx+>-&;8GA;Qp~ziDȹ3|n7.RO@ߖ u-ma9nK,baÛr\fܸ4][LUW#9 )$)xEpxgT s~UR6 q9s>YsfTS{W?{Bob{U燶_";S5KQ:2Ev`9Ǐa?ܬ\ , $Ab'bx/Rk)+]z:{Kf I{OoE|@^M2/6 ڳ;ԵDnï'>Fا% GԘAE6haC# U]+ ^5=iN-0&1F@YN^#tS3~2senI>=7Di^ h)FD?&ź+^D|2qNpI':ca49bFF)$Io3wz Ðqpk!sv|=+ }BQQGՆX=@vP$蛜e"I4a&'췘p7^ <~ibk1޲qLZ<,Drۗ,Kx3yot@{{\A`.._~[&2C#';&m?4PKՔ?!PKR%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKR%nJ.++mimetypePKR%nJ44QThumbnails/thumbnail.pngPKR%nJic.meta.xmlPKR%nJConfigurations2/menubar/PKR%nJConfigurations2/floater/PKR%nJ"Configurations2/statusbar/PKR%nJZConfigurations2/popupmenu/PKR%nJConfigurations2/images/Bitmaps/PKR%nJ'Configurations2/accelerator/current.xmlPKR%nJ&Configurations2/toolpanel/PKR%nJ^Configurations2/progressbar/PKR%nJConfigurations2/toolbar/PKR%nJ]b66 styles.xmlPKR%nJYՏ% content.xmlPKR%nJՔ?! settings.xmlPKR%nJ(META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/mute_on.odg000066400000000000000000000217661404105311000200450ustar00rootroot00000000000000PK]%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK]%nJ=KThumbnails/thumbnail.pngPNG  IHDR<< ")@5PLTE    %( *$0)5+9"+5 : &&&/// -;222<<<&6F(9J.@T2F[4H^7Md:Rl>WrD LS\alu{EEELLLRRR[[[A[wB\xEa~ccclllttt}}}JhLkStUwVx^`behjnnq΅ !!##$$&&''((**++,,..0022 fIDATxڵkS@`& -PJoT\PJ-la@IY6 Ls;g91Zl~%WF.<+>5KaOE2{)8K2ss Q9b= Ű ~º!g 8(` 7(Pj ?y.r<6 >LfUdY܌gܔfPȹZ7Pet -#͠N |p涡}[2dU{62#l|2ŒP]$$H!x3dMd.0qJr8c  ʯ{>ؼAJ–?'Ġ֦|G#MmD·Mph)5C'kߊq0,8VS2[PKZ.PK]%nJConfigurations2/menubar/PK]%nJConfigurations2/floater/PK]%nJConfigurations2/statusbar/PK]%nJConfigurations2/popupmenu/PK]%nJConfigurations2/images/Bitmaps/PK]%nJ'Configurations2/accelerator/current.xmlPKPK]%nJConfigurations2/toolpanel/PK]%nJConfigurations2/progressbar/PK]%nJConfigurations2/toolbar/PK]%nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PK]%nJ content.xmlY[o6~߯a%.ۋ`%Evð7FdQTlRlma|+ܭyf\e:yl7unib:Ox^K z 圓x+Zv+]z|0]mk9.yYmv"j(Tu{)lϊu w)e5ju]rl6\Ո\ا9Ee^~-$CCmRT v 5@v11mdsv)]OY%LwSe OQbs "= ToۼP]qU,X565s΍HŮ̍`k : _ &IxLxǜG0M#BkIֿ< [0;XYKRn=S,$-)T@AL^O|A+. P:|h)f"IB ꙻ3N'l/1U?GQ {$N UKdf)@`("wve;vy׹-݆s!`y5ػ3Oaa$qau={e|j&ݹF7 )oKh׷7z>eG_ Su>  Yߐlx/%8Z)w1 '">ie:6}O?>0L7߱鍠 KOZKyÏ·{59.#|1}EѮFr1բw-B\U$\sH5Z[GuiԄm)^ hZK?SozHvQnf7qz֔4t-y`ڪIߝ}IryŸ٬+m掷kiSxL# i8Ĕtd9EL/z7 aw^$=抨YWۻm~0\I:]f Gar% q¾+-bs( .E\XM4[ܵ+Eɲ^  ]:WYm/mbT{Cl*k8iΡM ' p|N.4ðKUJfۢ;[MyU G`p ׁ>C)#^cc4>y^OӼq;1"CGIW$0?NitN$N,}~^;#"xq>;jw e,ős|FyؓDbT3t4 egfZ[8;Rm0eXࡤGNZMMݴ}l3{jPKWPK]%nJ settings.xmlZ[s8~_a3 I:51c7"KI~e)jNgyĒ;Gst# ]&e.ut|X_l4.*B2+Pé(.2EE <$-2հzbLx2#N2)b.exu8u= d,81.#<>e{}D$Do>"k_wc*#0E{)G;retB%$ {$[U}H*S#HedH#y,hZRsҰh9!-0WAoDy;.:"%SԴ;E}):̐wҘ0¶%~ޙA<ŀ*oF j$7}Dԑ`~VঈB\U('Wv{)41AelqI8?M`Dq\mU^1QpZ9Usl}<լ0ګ=%*;$4䃪CG_,nHS䨪Kva9`K:v4E1F45%<{D'$z 1hpfyyYA y|If qڌd>We,VfkOGurb8(򇏁iIRݸ9ԨE zbT &Tq,sV04JN_N=򅛄έyuΝj˞ݑdzt?gn@]LOD2cSeLeNӸs,>LNt O1z˄AH{ !o*͘#BUs,LP~p*o³3I0M dmDрn!A*yGu=󚓰8U9˘fkAwj q<{7ߡ&qhnr~a ӎQFӱ٨k/}cVee6|nfyz$ {^tnu DF'&p}26 ܈ot PK(PK]%nJ.++mimetypePK]%nJ=KQThumbnails/thumbnail.pngPK]%nJZ.meta.xmlPK]%nJ{Configurations2/menubar/PK]%nJConfigurations2/floater/PK]%nJConfigurations2/statusbar/PK]%nJConfigurations2/popupmenu/PK]%nJWConfigurations2/images/Bitmaps/PK]%nJ'Configurations2/accelerator/current.xmlPK]%nJConfigurations2/toolpanel/PK]%nJ#Configurations2/progressbar/PK]%nJ]Configurations2/toolbar/PK]%nJ]b66 styles.xmlPK]%nJW content.xmlPK]%nJ2# ,settings.xmlPK]%nJ(RMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/mute_on_X.odg000066400000000000000000000206021404105311000203200ustar00rootroot00000000000000PKr%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKr%nJ}xThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE  )5+9$7 ---"/>222'6G3G]4H^7Md:QjU]`lyGGG[[[A[wdddjjjttt{{{Lk^`cknq΂!!0022*~IDATxŗis0 ZVG hEq !.Й`}Yx)w.1 ލb1$0^A 0C8FsB_QFC%B3EipQ`6\GA3+y #fk"X܄4YNeÒfճ("鱻h$ʁrIgB9% wgұ3_Wop""2}`pM35xXEUV3Hgp]ڔsֲO$+Mz޷Qq-lVkتc3?5=:VNeXܮ*mԳ$$!,5[ s=mx^t3U]dX9O8s2={/RhA;e`I2hLeP] HeP=E)ړV(Sz;L2h)eP t8tC0Rq W' (2!BKIIENDB`PKr%nJmeta.xml͎0} !FbVjF."'u 6͐}1:Qs|}?:xJ #R c.// 5\WTY:3JKUKj%MGi\K?k)}G}is828(gWL=RAQ `!0YP]FjiƦWcYZ_C{Ԁ1:A(zm#{=2ݴ^Rgߤ>[2dU{62#?GŒ( ! ILI8C$Mo(rJ=B^Q% e\n!XF V߬ZI2n"yx;C S01jÇN<$'~0,WS2[PK},s.PKr%nJConfigurations2/menubar/PKr%nJConfigurations2/floater/PKr%nJConfigurations2/statusbar/PKr%nJConfigurations2/popupmenu/PKr%nJConfigurations2/images/Bitmaps/PKr%nJ'Configurations2/accelerator/current.xmlPKPKr%nJConfigurations2/toolpanel/PKr%nJConfigurations2/progressbar/PKr%nJConfigurations2/toolbar/PKr%nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKr%nJ content.xmlXm4ίrqҼmJ{:͛88N_uڦ8@JM3x7wp6qBNUBSR+o{w 2eJĕZ u@K-] q—*1_dIk\uZKTs.huމEOgV`[;eh;VYb!zF*xe^H#/v>ܵdO(pXJjN nXPiɸNBÖXIRՔO dWk9@`21uzGg&sFlt)p?Ut|ĩ["߹Buy?+%P%ԣѶ>Ը*t+w Bo/·,xr"1幠.LySD2|@!؀y:hdKt@!2$]@ҢN=)&a032a6(`h-b¤,MB:26o_t91G9{E0% q֌6UAj̈B-{*_0cBI\tet ^!`i=qd4;xDX 8RɖM:Dmw<}3P' ;Yr 4;^UD:(~~jʿ=A陖x/U=DMD`Qw Lf|ùGss9ffK|.?ǧ7 ,?[*Z{7b!RE;AeH*PzdN0'KXj-ߑRh`ĂCeN^a2F ¼hWS8«a9E;}&#*! C;U]kbtwYo_#5lU(jhJ@ecjͮabXWv]}wSydo裦Yl.p|Y_7ap /:*%X. *ѱ"@yXQm_ |XH迳`i@nOʷ]KeP P;2)rr1|t:>Vgh-g~8eaPV$횮+rU}C%}BCvA{37FKU[|{8@熨PKǬ&XPKr%nJ settings.xmlZ]s8}_tgbBNaBBÔ$l ȒGC{e K&vv6st?痐=Lwr\@21mzn˅LX *8a*! Xnz I4 AB 4db=9-p&̘x~z,?~v=4RDp>96͋9_J*NX.,^V:dEs}j2;Ґ -N  CAq+P9 Ns3WP ~PN\VR+;O#x1הeF24 AgFJ8<͐3 zA wuUR6 j n:>-}&S͎$JCjQ^ >.m3DwX6+brqUvdnIhiMx2KEw)ex/Rk)7K׫j=}'|$LsE|A]C2//<>3c]k>zF(~=|>FaVľ6XqES "E mCѶF( U⬕Hʴ34Iq1ЮtS3~Q2<7䴘bhkF &+^ O.*M$xsi<%]f Ŝ7RHfa\N#D^ zkI*_8X-+x;mU/X'cpR]BZl 0g*͆qt-&mB$zO61B;_y ev(SN%G_s1o/?"g^t.0馤KWŀ#q #c80{bXY6;nYPK࿚@!PKr%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKr%nJ.++mimetypePKr%nJ}xQThumbnails/thumbnail.pngPKr%nJ},s.lmeta.xmlPKr%nJfConfigurations2/menubar/PKr%nJConfigurations2/floater/PKr%nJConfigurations2/statusbar/PKr%nJ Configurations2/popupmenu/PKr%nJBConfigurations2/images/Bitmaps/PKr%nJ'Configurations2/accelerator/current.xmlPKr%nJConfigurations2/toolpanel/PKr%nJConfigurations2/progressbar/PKr%nJHConfigurations2/toolbar/PKr%nJ]b66 ~styles.xmlPKr%nJǬ&X content.xmlPKr%nJ࿚@! settings.xmlPKr%nJ(META-INF/manifest.xmlPK66muse-4.0.0/src/svg/projects/mute_on_X_waves.odg000066400000000000000000000206411404105311000215300ustar00rootroot00000000000000PK%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK%nJ Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE  +9!7 < /// .<-?S;SkK]`lIIIWWWXXXkkkttt{{{JgTucegnq΄!!%%--1122-'IDATxڥr mCjVXO(HR͆.w @χ{/Ge]/`eM` ybesc 8yj=- FO} TZ) gh V AD mj $R??` 3j 4"rOtP=^9o1|;{2s>1pitk8`w6Wl*aH|óE{ ~^K=H_x ᠕b=26d5^3:(6lGlZY lNm%32S>Æ3glRt]smlT|g#:9vr&^.:x?]C_1&3 2{+3.\5ԨUI}85U*ɂUJogТ>G3f`7|Ajٺ')Zwic7AN#VV8Np޷:"zOӸm~{yIe\g`k1n( v7nA}jWntv sIENDB`PK%nJmeta.xml͎0}  Z؞-6kLHa&EsupjE+*NbKu(sՇB??K&(׬orq+\yt*Aoյ:Fu'$sqH(196%ZQNW N {?~nֻù]goĨEwzmW.idQX^.=PK:˛.PK%nJConfigurations2/floater/PK%nJConfigurations2/menubar/PK%nJConfigurations2/statusbar/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ'Configurations2/accelerator/current.xmlPKPK%nJConfigurations2/toolpanel/PK%nJConfigurations2/progressbar/PK%nJConfigurations2/toolbar/PK%nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PK%nJ content.xmlXm4ίrqܼK[E}@oI}v-NtҦx<ݫMUzO k?Ge.>As+4%ˌmEjRVK@K#[^/T,k\5.z2#BnjKSvϬvzBP]MUވ ^5X=/6%?]+)e:1^b:Z\R4 %Q EASSXץ ,6rUbN3 멘]OHLdS%\ ΃{.x5u.*崙LvcU`]Y`$|ͩ$܁').SqV P2Z>4n&7@㔠2{]}Qhv:%P(nvg}o`ZQAjPgTg_050OM '9 .pBFrDodz~^{IіhLoW0V8n%S#Eڎ-k";Y\\89T-jqi?`&%S33۩pIQOiNZ,LwE)+h_p0᰷Sc",+o9j:ɞVǗk& /hҎo;hD+&oek$9 bN^Zd( ʴjmĂ \~Yۢ_-QepvXEӿmu]wP֒]%u:SjOORP e]}Fݑ`@ȯ7PKqqVPK%nJ settings.xmlZ]s8}_tgbBNaBBÔ$l ȒGC{e K&vv6st?痐=Lwr\@21mzn˅LX *8a*! Xnz I4 AB 4db=9-p&̘x~z,?~v=4RDp>96͋9_J*NX.,^V:dEs}j2;Ґ -N  CAq+P9 Ns3WP ~PN\VR+;O#x1הeF24 AgFJ8<͐3 zA wuUR6 j n:>-}&S͎$JCjQ^ >.m3DwX6+brqUvdnIhiMx2KEw)ex/Rk)7K׫j=}'|$LsE|A]C2//<>3c]k>zF(~=|>FaVľ6XqES "E mCѶF( U⬕Hʴ34Iq1ЮtS3~Q2<7䴘bhkF &+^ O.*M$xsi<%]f Ŝ7RHfa\N#D^ zkI*_8X-+x;mU/X'cpR]BZl 0g*͆qt-&mB$zO61B;_y ev(SN%G_s1o/?"g^t.0馤KWŀ#q #c80{bXY6;nYPK࿚@!PK%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK%nJ.++mimetypePK%nJ QThumbnails/thumbnail.pngPK%nJ:˛.meta.xmlPK%nJConfigurations2/floater/PK%nJConfigurations2/menubar/PK%nJConfigurations2/statusbar/PK%nJ+Configurations2/popupmenu/PK%nJcConfigurations2/images/Bitmaps/PK%nJ'Configurations2/accelerator/current.xmlPK%nJConfigurations2/toolpanel/PK%nJ/Configurations2/progressbar/PK%nJiConfigurations2/toolbar/PK%nJ]b66 styles.xmlPK%nJqqV content.xmlPK%nJ࿚@! settings.xmlPK%nJ(META-INF/manifest.xmlPK6Umuse-4.0.0/src/svg/projects/mute_on_waves.odg000066400000000000000000000231031404105311000212350ustar00rootroot00000000000000PKh%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKh%nJf~Thumbnails/thumbnail.pngPNG  IHDR<< ")@#PLTE     '(#.(5+9#(.4 ; %%%+++ .<333;;;+[2dU{62#?GŒ( ! ILI8C$479gt%!/(ޡ,M2/UpΌʯ{L>gB]l k%ja8OI(1)ш/ah'ns$8FA5o_L$ }}+@`U_PK.PKh%nJConfigurations2/menubar/PKh%nJConfigurations2/floater/PKh%nJConfigurations2/toolbar/PKh%nJConfigurations2/progressbar/PKh%nJ'Configurations2/accelerator/current.xmlPKPKh%nJConfigurations2/statusbar/PKh%nJConfigurations2/toolpanel/PKh%nJConfigurations2/popupmenu/PKh%nJConfigurations2/images/Bitmaps/PKh%nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKh%nJ content.xmlZ듛6޿q3$= a7w7tM27_ߕx0`$mN`vW]qv=%e AE4Og?|1 :b6#0C  z=|pIu3REfνVsU#8.-^LY|7}fŬKG K^p.25c^ ,og!evΝ3X(KQ[ÖTqEER"'+-4GVÛ'yumvGd`{Q-8)+s"]_OFn0g*n4=UHͰ،we|W<:*䴘̊[gJj+sYޝdq*Ó!N,r! 8Lr/SD刀cU䖹FUśpC2L34/ڈ,0ImXE`k>u SA9Ťׂȶ$O ''mm68T$p*I8Ub=Z){u=4t%i\0ff%$2lXV5n~:IYk<,]1?`I)K>z&qf7M_SXi17fCbF$L˛gU}jY}={A)op(Eay} V~uW Όjo&$GqQPB1ǜ쳦*II¸oii<L* -'d wZ k<{slSG]4&emzo#4vkgmQ5}YDsM_4=фbCf\MԃY ɚaY.()WK[bh$;-{n~tRR lh[aFhߑ:a'0*)M2 eK`]<RyJ\#1!d3DKI,Ԥdd5ߟ #t 4zLS*JHigC=چq' R05Kn3lgnHe(= ë7zңH)U>Fso(U`ߛ*;.|İ@b$G.5{% !oGoB"۫pi .@o5XkY w,:uWxL%{ʼj@ƄƖl1ަb֨d!NO3۝ׁ %vnl+*i <napAtP[E#k&Y Xض HW@Uw`K5 LVW] aYnpA:94_>+{JxlRED_ /}iJ!7Yg}:HzT],3Ap :.Q ]·&MXDcJ8D~V;+p >dr᭐\.jB8ո=qgQx9c[7"^Ӑ~df҂4Ds6/ӂn_MMӂGbP˳r }(  Cn՟aGMzIFnKrl+SLx_1xCql"mfϗlN l+s `;X>!'dl ټQds#PkoWI[kÖ_1gd+УSFĬt|x4%)MG93יЫ-ØҐ6^v)b̂}~VHr r@ZGx$l9F2?.F#0'mh6^Wqe`uz)MWjHMFJޒ;#KffթhmH+!vkz] A'; 9VXwPKov'PKh%nJ settings.xmlZ[s8~_a3 L$t c7R@E<~e)jNgyĒ;Gst#Sf>2 b: ZsK;]T aŘldF0gR\. Q.z.n]u9 Xqci]F_XE7рa]z cYy_|(nYPuX}9YkK)jYeD^F.#EYb̮q`p,gVrFLe-a~ ^Nkd' xz@x4)>{}},|~SfnRpDcZc 7T *Ȕ5O:ǰ)؀w#LI_nz )Xί}#|H$8ȕ5ej cL($H$VId8N#LO`S Qy췖z otiIJâ儴\M| uYL)w}2-]uDJi1wRrCu!1VamK<3x':8)njpQGY"^ mSpES<@9B;LV!O *c@GdKiC$,=Gjǯʎ ڄιFcsf.^y-^S!!T:buk@2"ǃd3pvcGScDSQ³JtBbzoǗdGߩO!2Ԋl|SNG9%}U1P>-sBR0' p(^a^TOarք*N[ߵxen2 g2LH%/wMBV=KXbeQ2= 8 qj h7 &٧UWr2&u2p'iH9O&Q'w:e x$ACJm3;ic ,[LR9 @(`mp4[eeпJ^Qyxq$,tU2KЍWX/MRao<7bS`UFXUi#AdI;v(ؽXFې?h@Oa=7fZVhn`{GXOW\tԥh3u넸?Yˏ]gz8+az0D;&ZMMqpva=և) #߇VHgV;hPzٰCq8:kthw| nE6(FӵrٮU.M{j cY{V~y9OX)_? a"wN >\HDF'&p}26 ܈ot PK(PKh%nJ.++mimetypePKh%nJf~QThumbnails/thumbnail.pngPKh%nJ.meta.xmlPKh%nJ|Configurations2/menubar/PKh%nJConfigurations2/floater/PKh%nJConfigurations2/toolbar/PKh%nJConfigurations2/progressbar/PKh%nJ'XConfigurations2/accelerator/current.xmlPKh%nJConfigurations2/statusbar/PKh%nJConfigurations2/toolpanel/PKh%nJ Configurations2/popupmenu/PKh%nJW Configurations2/images/Bitmaps/PKh%nJ]b66 styles.xmlPKh%nJov' content.xmlPKh%nJ8$6# ysettings.xmlPKh%nJ( META-INF/manifest.xmlPK6!muse-4.0.0/src/svg/projects/mute_parts.odg000066400000000000000000000245501404105311000205540ustar00rootroot00000000000000PKo.rO.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKo.rOmeta.xmlM W̗㕪aVT[`^LM81<ZJ(XI͍Pz~n4x(>EqɄm-媗jǦ#c:} 4S44v Bit}ҏ̔N9Z:9R,fRƋN:zw]vtpEKdqQZշFovΝ>q;TJV β *kZ[PVrpp.UupS7}+v톟pks'G[9v( =@ ,А (q(r"Xa'Qp&W)֎|o$}}.s)+8z K-{ţY},&! ݣa&$Z ƚߒ{HݧVU8n_qگ"g݇&PKAx/PKo.rO styles.xml\r۸SpѤlKM&O8igz"8 hy^$/ H$ʲٙdpqxW?߯S\D'([>~Wx@1%8.0AARXxlpVd'_Q0,aMCWc@7~I*fXbFղ2gB?XQ: b,LPL:9ƜkI$8l6? a(Gܯq`:W&x2 dc*;1A9uGHP8o5DzZ`?Nק26kqYfr %G9{geI\i-OKq ,+^2K ˵} ] U.=3f--[</\ 6n[}b" Ej8X($8S}o2K $a E8Z6^lBTjhZ9;6׊bDz_++sHݞdt Al S<ilQ@B|6?f vhN-Qe|O 0_Sى1 W˪ٓz7G6F};XТ~r\qKN6550cxAfI쨹 .Cwؔ 'ݸ))f8:7[!CA⡠p(FX{@l`vMҧ=H9heO{a]OAeۿֻ2do 8_a[Sa$aO|R4/!;! (RήbV?26Q{cPsȖ|Q9& ?1Do_ *<gz5.&dx9=3.ثy|9i;^]g{[Yh#^|\&M<@ oZ.p86Y~Y6cTt7Z+~0?cbգ0Dzu ^;$&7p+nD^ #ovv4`mi0a>?en SG#c'_ײs&$B_P:/'@9N^نM\f2ϵR9 @,f-%M-%EM%KQ+bZl17;:W"Ϸ8ؽbj(K t嫉a, ~·^sw  6lE .JJh)v`t`qNE[,Y?^XCn{}}fgހsAh^𹵌\A}?VG;~]'?RxgJqݪwWٯ2x1K 1I>Bw 6NQ~A aF>O!C&j >\ıy+,ziO Hd[,(Z<A`8sL)F7pA+Gn $" ~{. m6·N:1+vݲ!S,w0d ԓ(6UgLb؜FY9ڶw MvO1v!sә5fot2 j#_Si^Vі䏤zmN :TP B'! n\IDZ`W[Dudw|Ey&lP!FlO Ͽn]>my0o* v}ǑceBg4\}o|./Ӫnԡ؃ylw9{Pi?wN^ 9zo]l>~P08w[@4k |lL/v7'x" yN0rCB*t7uKlLizy `.m[(44`/s C]iU+}Cgdzbn ,\ؼ{;l/JS7s@w]+V`_J!X*ݺvsW- 3lmL%gي[^Oh-rbOA¯2ttEt=hz|D뎷Z[qسPVqj-vGpi*]עF A1@Ъt-tW(p\;T 'xLn%jn~l A [I>f fUݧxfI㸖b7A!1g(Vm>/%͘r J{*;q&.'q_Ӹ땯[Md$2eS}J:{;T qJ\gyyõ᜚8fWQ tH96Z5~7LY|A(Yt#u H]Dbsw,H! oi+sUҶYjuxl{gƠ$Wf%C9ɗ)Jp,6z{^TKi`' x$)b2s`⇊1!U>l6߷c;Do5H"baKbIhC H(ݻyQ9b<{="7!Kz%"ŝdxInݵBPձ;; ʏ[36Cu ^]]p8ul$x[Q(ar.Pձ+|P7cS"VaURn0'hbN2X8luXVjnsjE`䝊QW2f%9ts-5.nKIߙU0͢10:T~Pag%Rmؘb!(ݑk7 %#uM\̝g: ̺ G?=,@oNd Z8wK&}@eKBעI+\o8=H_ƫ_~5>2㸤w:* &^%F(?[{ 3W)PVb5]h;ИlEf9C\:U5'D<45` 87`%kCBq1pF? )`̑?f%)As=؛xs|JBfj\bQTiƕ)qMs8Y+L$SS$2=[Bk.sm& 4E@2!ZuF[^NgwV/g(oe{{VyFq$xCq1"V3̿}9 3DR&*a"VMx,.N#7v|ޏ$wب ,ܩzF٢2ڠxv79U r(?B%-Zd+_ waEjAk(XYTGt}&fq+[7nwRPī>$@_ 1cxԩ?wq4VE;CokxKv?25atÕ^/zgv?ʫ-'ͦFoHZ@Ofsp'?w7^V#Io|`Fd/|? pAT/9+P!gl1A/Ƌ䡒]ۇVoC'н?N%sƵPZ:9̂ -%DS:suVGX*D44J1eB7;|>Sxnv!Ie$J_eFgQc\ôO!CTdyG4p3^s7>niफ1l4hzIRi6J!D?D37.ዣ[ v;)) 3ݞ$FBZo ߝwҩ +(Æ74TąylQy` &I):VwyO=[ %F ,vk{~PKPKo.rO settings.xmlZms8~"`h LBZ 'l P%$c?@J Fˇzyzwpy9dQru zLn]k+Q7!9KNxy5M)զB|F)䋺^ʯ>k= V )S/C^n9޺!*X H&=JwC^µ[l\'Re{6#2{=S"wT*R#^xmΥ~ .%c4y/jj[.R*ظAfeCc[MXy`=ھ}?u1 ͤXrUrna~ b/^ <1݇^ֳނh2+|99D<.TG: [G`)` Jth2;U ad/cyJ6X4f9>.`D:xfwAW4TҧRL!7poyo&Zw LIJO2[G#.u=:Th>Xʤ8J!Rt}Pҡ zoS߆QCx0x(EC0A̛mJt~>X 'nS`64r #wf{`BԦLIWiy!3i .RKi=Ɣ?WKÃ37dvLlGNWhO=y]e@PTXNxVG'^LS'q!s 0#J{'Bf2A@/NsƘF8׳Ԟ[E~5%YW6FCv8}z"!b}&/Zq2WO`SP Z<r1`9N:dQ€Xq/YRqIEmEt2>Uf|/Lp B,X */Sڼ;38d2=rL M;.;2bcݓ$(ӥɀdUtrp=Ӵ%WHC,[-nT)IeT;Sq8cL"b@BQ-&h!σIp=UB7SnҊEgBQh݅ ɫ.ֺ~e(}d |ԗ焁`:}_UM>%9o <¾]\>?bnc{S@^P֢sg|sKLڷں41*hFO8,m|mdQ WzoSYjHb\^222;;;$2A(9J,=P/BV0DY6Lc:Rk>Vq?YtA ORR^bmr~@@ HHNNTTZZccjjww{{KKKRRR@ZuD_{E`~cccnnnssszzzKjPoQrTvVxY|^ceghjnq΂ ""$$%%&&''(())++--..0022!#%&((*,-/01346!IDATxkWQ`3AʊJL !IRKhҴet3\4*Ew37>a=?HÿK}n|q%pjYb^<s&ப8.A|GU0ŕ5CO00]vXQSyRs hi^4[ҷs38O |eMx|9j(AxX, P-Ki Em.Qɕdv";a.;͛coT0L;U:i7.=+pu|5Xda|QǞW.Vq; .F=ꉼ5J`S| o+jemS/zaL ,[tpj818 Ԯ/Twb:ȖA v:Ժ3/:ĔMN`M9-oVSLr,(Y}pܤcx^am C/m,qs. ]^kY֔a ̻?Gp,$><|B~ff_HLaƟ]_ S&Qz`]P0"kFe4Ę&ܔoD뎏TΊ5giR 8FF7Dn{1ځ6Ye\6w}}7urGt*6e6ΦӼ2~`&X-ޘ 3d|3&y!/G'V"Q|\QHdAF zYg eO?*س']>tqb+..twϜv >;:{s$4?oW럷іw>./?u ҺPIENDB`PKo.rOMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PKo.rO.++mimetypePKo.rOAx/Qmeta.xmlPKo.rO T Mstyles.xmlPKo.rO#Configurations2/floater/PKo.rOYConfigurations2/menubar/PKo.rOConfigurations2/toolpanel/PKo.rOConfigurations2/toolbar/PKo.rOConfigurations2/progressbar/PKo.rO7Configurations2/statusbar/PKo.rO'oConfigurations2/accelerator/current.xmlPKo.rOConfigurations2/popupmenu/PKo.rOConfigurations2/images/Bitmaps/PKo.rO ;content.xmlPKo.rOy-AL& settings.xmlPKo.rO zLmmThumbnails/thumbnail.pngPKo.rO`41#META-INF/manifest.xmlPK6%muse-4.0.0/src/svg/projects/mute_proxy_on.odg000066400000000000000000000221341404105311000212740ustar00rootroot00000000000000PKf?nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKf?nJѢ+bbThumbnails/thumbnail.pngPNG  IHDR<< ")@SPLTE     %( *$/%0)5+9##++44&&&///!.<6 6:":222;;;&5F)9K-@T2F[4H^7Ld;Sl>WrC(CL-LS1S[6[_9_a:ai?iEEEMMMSSS[[[A[wA\xFamAmtEtzIzdddmmmttt{{{JgLkRsUvWy^`behjmnq΃NPSVY]`dhlouyԃրڂxg#IDATxڥS@pIQ(zV ֫U YnMvayÓe7vC{Ɯ6V<}# 9+ЖMdMӍx F䃓"B"6Hpe-[VTv֯~$ްx/, 1Fu%)7 5L5',eijHx[jgيW+۵4+g;.Ⱥ֙]1_yŏ,]VMah72T8= n2ɞn(KYpQm Hqӝ`LH~NYps&d%cs1np@9`B^~ ɚmf0&dGN }74%qgLz7; y&`9B~=={Տ]mEeջ#,;n&SYcġw|h2W-a69AeOg+v)Π`!0!ὡv5ʍMRdSN뙶uؐzJ2&p^z9땫W)`5mve G@9Oe>> 3bzCQ!CL8"qHio(rJ=iQLm&6MIglr\:BޙV}4vy7-?'WkS>ɣ_4&"ORuIph)5C'kߊa0,qS2[PK+`.PKf?nJConfigurations2/floater/PKf?nJConfigurations2/menubar/PKf?nJConfigurations2/toolbar/PKf?nJConfigurations2/progressbar/PKf?nJConfigurations2/statusbar/PKf?nJ'Configurations2/accelerator/current.xmlPKPKf?nJConfigurations2/toolpanel/PKf?nJConfigurations2/popupmenu/PKf?nJConfigurations2/images/Bitmaps/PKf?nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKf?nJ content.xmlY[o6~߯a%.X>lIQ0썑($j;ʶma|+\yf\(pZ I)"tDC|-6:̞{%)=S,$-)T@QL^O}A+. P:|h-f"INB ۙNsq?EQ {%N UlMLt鮢䊶`G+af[;ty۹-܆s!`yݙ'ݰHyxаS2G> Fuԉ\X훅7^Jb%4+=̲{`):H ]H ܯoHopzu:[#h V'xAb2a@S7 Lنø',}-iwlz#(1咫u^f^hFr1բ;TQCs.*Y9f√ RY.WDࣉ4 gjBD6BdV^e Q0ƅkbs8> ԟp0p{$G6i96'0&Z#9ˠ=  }%8I^!=(%Ra,TJ/g%8[?;M4= њ ;Ӧ,Bax[oMuR:+*3zkA#I8! YobO=E[>̜Hސ_X/6{EQy:lR.(Ĭ.TڹM.vK3G{jRSZIN |#u!-mqY&;۫gSxD# y8Ԕsc9ELL/zWǝ awj^&=动ѓGc]Ћz[Z? .p4jm1Yp&W2ڸp-^v传R]A&  cfzmXnKPt04Fs{T_.N;5WWTrӜC!>2b3d{qPKO=UPKf?nJ settings.xmlZ[s8}_a3t$ݚ t !87R@E<CR--sO ~yq-eZQALǥg?geEECTfR j:R&ȀHDQE#V\]Wfi)3/ra^Wsy]sP#G>geEY^{MX /hunj09+u+WbXh UڗE j?Av[KE{by9X( FGb~fuS}uS)g6oAѳ܉}uu}2#r P9xS|ձYYL!!I Q{ϏP)$W SD>#ÖXca @3et |3Wg@DBS[,b9zQ1"=oS nw#Wֹ:KG(cz$M@!A !{O"u40~k ҈vK7Ԝm4,Z.HU}(|)~}.%SG6s-%6TRN eUFض;3 pÛr¸zt>7}Dԑ`~VB\w,PNN-45FShb12٢H8?M`Dq ayeGmeܟ͹*\Z*;$4䃪CG_,f$)rܩ";^c1Lǎ3ƈFgoItBbz+}[2 #f '@HpYRjE{a`޽WuQDG_U޽K✐^ $STū̋Q7RRΚPiMFz^ g#_Iܚ XgԌ  V9N {U$=ۿ!N ԕ$JT/W*cR'wFEqx2:i`}Of ƬJ+m͂yH꾭>5=Rs>|$Z뾸 Bڬ'}3'?`#Owaؾ3D;ً)6MֳZ41pMGO#XG,-ʃYDkj6j:]yOV"F̻AwS!Q,RzͶ+Ӟ<[f>0ìV>iz6ًi0FH|OCCnp$PA*,@J?[~*cPK#PKf?nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKf?nJ.++mimetypePKf?nJѢ+bbQThumbnails/thumbnail.pngPKf?nJ+`.meta.xmlPKf?nJConfigurations2/floater/PKf?nJConfigurations2/menubar/PKf?nJPConfigurations2/toolbar/PKf?nJConfigurations2/progressbar/PKf?nJConfigurations2/statusbar/PKf?nJ'Configurations2/accelerator/current.xmlPKf?nJOConfigurations2/toolpanel/PKf?nJConfigurations2/popupmenu/PKf?nJConfigurations2/images/Bitmaps/PKf?nJ]b66 styles.xmlPKf?nJO=U content.xmlPKf?nJ# settings.xmlPKf?nJ(META-INF/manifest.xmlPK6 muse-4.0.0/src/svg/projects/override_latency_off.odg000066400000000000000000000244471404105311000225660ustar00rootroot00000000000000PKrO.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKrOmeta.xmlM0(W$VBEV7;M䳴 U>yzƞXWN q@jn61%A/ՎMGƘmAhhdl B)it}N9Z:9R,fZƋN Q:RXBQ*ٽ_V{ebWovΝ>s;VJU N *kZ[PVrppG.UupS7}+톟posg,F[9v(sp (V"J:7lcNI"zSdp&W)֎oɗ5z}K)+R^mlV~ˇDIlUpk~I!EZU @f'apW<0'm߆4y/oM|PK/PKrO styles.xml\ے۸}Wy㐔4)oynۛ;DBl`h?_$h\H@")PL&*{4> ҫVwYAhv=&$[\5^xxY} Ί줔^eO8g-aNyƠkM)*H1 2P̴E%Wp*ŊUg؜&gbz1\ $if9ی4QT"zݲ\5&I zhBW-SG-W؉Kg~ eN9ղUw>~1q] di%l%9 83 IDAi-OKi -*uHk3](U.=5f..Z<]s>n8X2mױwEisSmw4RI*||j6KI=@oeq%Y*8) :-/sp!l* ypNYY9;6׊bT6&>•W3̜ݞt #| S?aSk;4MQSk< ?ğ +eϠ#>H<hQ~ _rZiKN6QPEK-{3[6zL *s ^Sր;./=_A(!<{")NI=(OX@R~v0T?XCVƠ-`Qe \" a )gz9&䉡->j 8 `p`o* mzˀ/ɀ'(O 8M %7U8~(Dž `*:yV|o~1Zha@D<(~~RKxMBBZ"n C|N\%ó"^vFu:hwCBfno :?SNr0FlďoK{5aQ21Xa@zj=x=E7?)&? 3;sNUO8v/I<в곟-f%V~|q̹s:|K3Z1$]p;ѺE I0(͗H:˵^174 D3s i<4,pDtWgax=$~! fqh`0 fROD-Eo [!hwg?Kw I d(^`xsU9(cthYB/R`d4z,Q"?$Ig:͆yة_-Nyp;lK-N?[Ǔғ.(|H]UgLb؜"N-m3M.Ɏ;sq]H{0|hgxN$Ng"OԘɐWb;,!|5i{AkF[?ZS91SA]/%4Jr%i]oWI)S IoI@:<i=/:rC%i˃|+bU>+%:PR>QVu#1w9sx`LJˇ:{0_-?{hO?GIb4!G/q͸mǷ n LۦmB;ˠ}Q!T" F0-B.aƔ&K ]AҶ]NM_{aU~?3RhL65hkwhK4u3:JzM.l(r،ҭk7w/|U؁ߒ81r <8sv^V$[q#MQQvEǸZ1HMfQ/: Lx= eˮ֢aw @ઊx-xkD`*]@עAw cMUQxvlZ0vCkWxJJ4K0Z>ś8KǵlK);lvlc3-*y#T=~Oz;$kub[lנIzzK RSؕˎ" h9Sjz)^yG *sltm"k&vzoQc׳*!G: Y,}=n(cSq^>zB}fcy큋U΢ʷa+~>u,1JÉzxF0r稟ch$T=Iva/N4uV-uIRr' bNᯎ9S_ݍm;Q`KG4bOץKdL@SXOɯ֪Z6+PF_v*c(ڍcI#z)LPqb6 ς/y}p ZKuPQ &??2 QW?E_"#EhmѴCBŀT$m8vv+j\ PKn{ 5UPKrOConfigurations2/floater/PKrOConfigurations2/menubar/PKrO'Configurations2/accelerator/current.xmlPKPKrOConfigurations2/statusbar/PKrOConfigurations2/toolpanel/PKrOConfigurations2/popupmenu/PKrOConfigurations2/images/Bitmaps/PKrOConfigurations2/progressbar/PKrOConfigurations2/toolbar/PKrO content.xmlZ[4~Wx PZǖ; Nhm-#+,sK$N\.ehg6tI.K[&K.MFm<ϓӏ/}}S~`&r^nt^BGiPvr :sNA4DAI5Ju5'25)\⬴0'; krp79M ؿOcu)[ؔŵxUV&3ak hk+;ij)4Y :4 싿oJC[,iW8\us%N"Y3U,ˌ('k=ޔ*h% %c@p#[)Yb$q^N<זiˏf"uاC6Y6MӏUmTO=4BʣoF{11@8{̈́ o[Cv;{ =Ղݵ wUxULړ֞ υNs&Uvm2fSi b|HRzoOب[ XqÓ&][~Sg)U dO'd-YZ+R2m,2 #C-:!|s1%wSb-ԫ?~M7)UcTETOg,X}X3_wPw t'-!g"1go6 `{sum~ʪRy iǦz!aV܄VecC VqUγ}78 8qC=;\,ʬt fj \kP k΁=gl鮒Wvza5B**bOUJpUȈr 8cnqO<d\J!]5S-n.t+[ ;MCSkcE@34X:|G4esrvjS2Ǔ?BQMşN8:;"=9I1M.49`/x񞴃ޜ\ >&ocE8#?Go<,DS<P.v?[F#<_vy~V,-ܦBu稘᫷FQn uH%sDcwl#Lໜm!YKD9j>T#GpE,!3tH(BC"2:t՛ zԈc $0TvKԜ\&,,HT]u2$0dS^j3G {fmVf~W3Dr|ިĹPO}D?*pCġbЀ+B2-eY3 MLP^:G=.O'"AǢkppT^ٱ֘pY5g5eʰh3+i~@z*tWjC>:9.bѓ:r 7QA#p;qiT1{O0)n2΀5x'wH[8ɾmY  s Ke@ݰWq9K!'p Qf?>| ?6rBSfTU2uO7JQkRNcsCd*@,9&ԄAH՗EBW=Q!hi v~o8ƻ1[j@v[c>Og \$- nc 'ÃGt=4=)11NPރss-$* i/.j ACzH;mSa 26_nbLV!GCT@ U.>yJNZsA臾oj#bX袿V9 V,|MCUVU،T;'cd32Mj9H?}@9 ?Zqk> fGZRб㴜Nauo^͵βLK k:iؽ(NwRIkUٷzZ`lQѣݘXe\ОÛ]M$ܿm4NŊGQcQks{b:=L>6.0233458:8:<%3C'7H):K,>R-@T>AC2F[3H^6Kb8Nf;Rk>Wr?XrCDEIIISSSUX\[[[A[wB]yE`}]`cbbccfifjnhhhlptqrrGdHfKjOoPoPqTuVyWzY|w{[\`aceghkmnq΂ƻcIDATxS@L4T舶ڪ`lr+QU䎦_ h:aoYv{'sQP āiT(iFC7t0|<4." M T0,vJjzMkvۮk_ R|$% UU$kDEdB"))EkX/͠(\Tm0]"n Vmx+"@zJŽK}ab[u^L*4#I*-9) Rt6k+vk@(o*Ӟf\N|͊AI>c=fݮ/y]RS`f'~Ad}3pFӽ vj[oED?;Bʡǜl}6+QJV-bqlTb2sHRxb0 '"F84#h}Ebh%8u|1_!zV c]0w6bEb e43s=Fֈt$/B(bql4 ]R1!Aj fZv?B+ ā ڊƒ{WHZcI? {d}סp)ګ\ F]3 sʯ5:fW'(!pB)K`ZDɛCpJD Lc}K\덑oz_-[0IENDB`PKrOMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PKrO.++mimetypePKrO/Qmeta.xmlPKrOn{ 5U Lstyles.xmlPKrOpConfigurations2/floater/PKrOConfigurations2/menubar/PKrO'Configurations2/accelerator/current.xmlPKrO3Configurations2/statusbar/PKrOkConfigurations2/toolpanel/PKrOConfigurations2/popupmenu/PKrOConfigurations2/images/Bitmaps/PKrOConfigurations2/progressbar/PKrORConfigurations2/toolbar/PKrO3Raj' content.xmlPKrOh@K& "settings.xmlPKrOc@bڒThumbnails/thumbnail.pngPKrO`41d#META-INF/manifest.xmlPK6$muse-4.0.0/src/svg/projects/override_latency_on.odg000066400000000000000000000246211404105311000224220ustar00rootroot00000000000000PKrO.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKrOmeta.xml } ˻Wo#+UUV[륵N߾dU'0Vj QPLsMcC_^$k5B9WTYꏴ֛չB[|4Ҧ83iCMQTVZFXS,fRCxe8}G=Y| /] ޤEds^X]~wC-k,t3DqE-F QFַ^jdM;b_n Ÿ`xEQ & NфPL"L'k+3H3@eg4NA8C9\0*tޙ)W}ڢ/$>6ȹaEJsÕPbPkS<ɽߦ*"Q|$Uw\v$8Avѿs;Ysgq^87ILqËvmB"g͇ PK.PKrO styles.xml\ے۸}Wy㐔4)oynۛ;DBl`h?_$h\H@")PL&*{4> ҫVwYAhv=&$[\5^xxY} Ί줔^eO8g-aNyƠkM)*H1 2P̴E%Wp*ŊUg؜&gbz1\ $if9ی4QT"zݲ\5&I zhBW-SG-W؉Kg~ eN9ղUw>~1q] di%l%9 83 IDAi-OKi -*uHk3](U.=5f..Z<]s>n8X2mױwEisSmw4RI*||j6KI=@oeq%Y*8) :-/sp!l* ypNYY9;6׊bT6&>•W3̜ݞt #| S?aSk;4MQSk< ?ğ +eϠ#>H<hQ~ _rZiKN6QPEK-{3[6zL *s ^Sր;./=_A(!<{")NI=(OX@R~v0T?XCVƠ-`Qe \" a )gz9&䉡->j 8 `p`o* mzˀ/ɀ'(O 8M %7U8~(Dž `*:yV|o~1Zha@D<(~~RKxMBBZ"n C|N\%ó"^vFu:hwCBfno :?SNr0FlďoK{5aQ21Xa@zj=x=E7?)&? 3;sNUO8v/I<в곟-f%V~|q̹s:|K3Z1$]p;ѺE I0(͗H:˵^174 D3s i<4,pDtWgax=$~! fqh`0 fROD-Eo [!hwg?Kw I d(^`xsU9(cthYB/R`d4z,Q"?$Ig:͆yة_-Nyp;lK-N?[Ǔғ.(|H]UgLb؜"N-m3M.Ɏ;sq]H{0|hgxN$Ng"OԘɐWb;,!|5i{AkF[?ZS91SA]/%4Jr%i]oWI)S IoI@:<i=/:rC%i˃|+bU>+%:PR>QVu#1w9sx`LJˇ:{0_-?{hO?GIb4!G/q͸mǷ n LۦmB;ˠ}Q!T" F0-B.aƔ&K ]AҶ]NM_{aU~?3RhL65hkwhK4u3:JzM.l(r،ҭk7w/|U؁ߒ81r <8sv^V$[q#MQQvEǸZ1HMfQ/: Lx= eˮ֢aw @ઊx-xkD`*]@עAw cMUQxvlZ0vCkWxJJ4K0Z>ś8KǵlK);lvlc3-*y#T=~Oz;$kub[lנIzzK RSؕˎ" h9Sjz)^yG *sltm"k&vzoQc׳*!G: Y,}=n(cSq^>zB}fcy큋U΢ʷa+~>u,1JÉzxF0r稟ch$T=Iva/N4uV-uIRr' bNᯎ9S_ݍm;Q`KG4bOץKdL@SXOɯ֪Z6+PF_v*c(ڍcI#z)LPqb6 ς/y}p ZKuPQ &??2 QW?E_"#EhmѴCBŀT$m8vv+j\ PKn{ 5UPKrOConfigurations2/floater/PKrOConfigurations2/menubar/PKrO'Configurations2/accelerator/current.xmlPKPKrOConfigurations2/statusbar/PKrOConfigurations2/toolpanel/PKrOConfigurations2/popupmenu/PKrOConfigurations2/images/Bitmaps/PKrOConfigurations2/progressbar/PKrOConfigurations2/toolbar/PKrO content.xmlZێ6}WJQ4Y{] &EѴEї+Q2IT)zw-[iMZdZ9sTz-%&#϶Xܾ⩈ce$Mr"Wt^.+be*+ W#![=5<[/-yiʥ m,TAK`5Xf<ju!+7)35gbL[vm&M܈*r} vkDbjiZeyh/.ZXȬJ(G" e(yϗ6KQB{jWt*6)״`džYe-HRzoFب Mrܥ$R('?bxhiTKCn(@3!pX7EƔ5b-ԫ?L7)UcTETOg,X}X3_wPw tf'-f"1go6 `sum~ʪRy CM譸%/=v! 㪜g g|78 8qC=;\,ʬt fvf \Π)! ;26~9]%Z j& dU*fϬUJpUȈr 8cqO<?.ɸB+Z\Vvג i%'?&gh(1viRՌd2's<& ?purwD:s;rc2\`#isp!#*_b#]i9x~׸+>&'ɀ1] IO ~FHG^Q;5!O1o8n/PK,OZiF'PKrO settings.xmlZms8~"fzsCphy1ż߄YIr O2M Ɲˇ-yvW/K<".0׹S#w ȱs_ʿ]{d^#*Iz1v[H  {P4 m\iEJzQ\ X A-[ 7>c 8j<(rɮsjJE߽'kkXh],ÂӠ\jS+*yp] H >P.v?Fo"<_yyL!Z"MQ.WoE-%\@=,!Öؘc ~An-1 pf/yS8ngߐ'mnfcF EPHHVv]|5 1Yvk|;҅0K,4lHT]W|+7d=$Țͼ_(eT{{)b4 f:ިĹ+WOCDTBP%bW0)ZBe*ރެ&&.[ǧ !>`=8O.Uvl@s%e°hgF<mU<AC,zUgU$reqw@lh4u˘eFϠtoz -Nx7 o'$ޫYycӴFɹR~7>|quun@OC% BɀSfɤR% nUƟn9JpqFzd0LD*rw tVHh?CСq mvܙܫc;-':*bc8dKUje.71ԓYnT{PdNPC;̩ITΦXfTKmBk4cͪ38lLm:b1@r@Zw!G#TbG U6>yJNYsAU=ӴbX[V9 V,|aCU^U،ZT;'cd33Mj9H6??^Ig>j~ ft@vHe8~}IJ:u˲ݑ|-kxаҊp-sPkp=1^U}Sw3+&ĨGAg6F+nf{I6k4N͈5KtQ^c8ֿ]N]2Sl? '\#_.9ȮESzpZQK7C_M ɥw:кZVe ;ۣ1K{b]صXtg\./OC t`A{^o*a"7TʦCn%PU{:8H$T~;}‹~ + PK`MY&PKrOWThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE    "!+",&2)5,:#. 2 < !!!)** .<.0233458:8:<%3C'7H*;M-?S-@S>AC2F[7Ld8Nf>Wr?XsC NTZchtx|CDEIIISSSUX\[[[A[wB\x]`cbbccfifjnhhhlptqrrGdHfOoPoPpWzX|w{[`cegkmnq΃ !!##%%&&(())++,,..0022ƻz~ IDATxOPMV@6gbP1LlcMQ .É:S"QʬWhK{oY~ZiH|==p[X-#a)Wi70BdY,%Y~ڏ:vg7bmSqt@Vg9$o9._g! ht~ZR=H^`Z^Qw H ))t0$: W^0UV=!Gb *>z-B Xo0c=~P$DILG-Tu!&QԭʩWp<0z}= ڠʳdZ=u;,z 6DZ(^2hjJ~3 q)UBu &QpDXft ,rsy$6'Jj߻¢=Z-¥(qrbzqYP-xXv7kMh5f#JqKBpd҈6p#p`R]c?bf=؀J*=ݩ2?2];ۛCJ?dg/1=S?m/pkX|c0{M1+V8Gx2+},r~UۉM0URDSC^I~V%.oR_ÿ"% IENDB`PKrOMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PKrO.++mimetypePKrO.Qmeta.xmlPKrOn{ 5U Lstyles.xmlPKrOpConfigurations2/floater/PKrOConfigurations2/menubar/PKrO'Configurations2/accelerator/current.xmlPKrO3Configurations2/statusbar/PKrOkConfigurations2/toolpanel/PKrOConfigurations2/popupmenu/PKrOConfigurations2/images/Bitmaps/PKrOConfigurations2/progressbar/PKrORConfigurations2/toolbar/PKrO,OZiF' content.xmlPKrO`MY& *settings.xmlPKrOWThumbnails/thumbnail.pngPKrO`41#META-INF/manifest.xmlPK6E%muse-4.0.0/src/svg/projects/paste_ctrl.odg000066400000000000000000000260251404105311000205300ustar00rootroot00000000000000PK|*yM.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK|*yMmeta.xml͎0} BFZ]D'u 6͐} t*K߹:)C?eSCz6[ovK$}}~vz @#]5PKw.PK|*yM settings.xmlZ]s8}_a3!dc&c_-b&Td#W2NgyĖ9Z煏qDmx q677tps&KMEYޜO)!ZMX /iEah#|,wHW X5+U?k#DJKj8c#S֑vKKMKjk,,Z/H,e^]TU Goi=REM3-6TB&'b8fU&T9L>: rK?)pǡХ^h;J'0rg\f441Am tuJ8=#>d&vAJSye[meKýڋZ|t YЪE?䱙AYwW/{XNmWNTH0P#J{#BA\ R1QN|qq3{B,Fx9NHHC-wCQMU!0Xf??U?|  \y:TɢYUS,)E\\*|P7;9K A5b2dC̡dZ@٭4i 18WtuR:dNP,&hjgHd-y0 2i U;4fBW:2nʢ3!({˷Vw!!CS~Z(kP惝',q& AYmq=#4,՗2v|6YeGqLEaiBr#SA QE]la Ko?.hv|{ aK| 5a~{at ӎ y0#ОƢFKm͒k˚%k57wߵE/N] ߜbE[ΝF)-/2ijkӴKHQs4=PҢ>\SY:l6^YM[SYt {t[vj7mW=[Cn>\fˎ Q,zeF~^C{{RÔg%4Lbdz-@FC((Haߏ*PK =$PK|*yM content.xml\ms۸_tV |Scgwsssv:Ą$X.)ѱ8N,6ln9-hYVdxc|WMeRjճ0GdGͱJq4TȪS+%.TfZE!V0QƬPCD yA:v1&CmN۝(7ًɋ,(yx}y=`Ou\z^:dePq]w_+dSW-YG  ݅*J"C@B<Ջ5@jGӿ6= e}E5nNZS_r6ekyBSV0FY?kw9( ٪%v\}N7zomjj~QxKH D ;BBh.CdAHzObTH )&666\t6!v7)UrFq3S"ւ`cGĵ61]Ƕ充C&DRdj;&W63Ni# >PggO+C7Yg(NkH˛қ<; kuS@u缈.lM)'~C{Tz23ccK5 y[5kݸL8գz"O{9R2Ae_-ΫҬgҌ. p p!8ȾQ1p?|o ["iyJyeϗ a"p<'/\еt~ UC jJ8EKZ2O:ZخuOWJT'`F@[SgDGE3}JFoۮy>5Dz'qMۿDK~#WGPm_BPNRF١;PB$7 ۃ !t<I]iv@f .it/&+gi0\ ~tqF]c840YR`Xch,}4dͶ噾 Wr>k[0hj5PM\ Hwˆ̥ܧcjݴnuF7'u z 3kaG9Ks|.7|eE E](BQ1uLP=y?;g3PJQCc>bVӳ=v> XV%B.bGZU$D:\#1#p<2X ̉c1|QE_6T'cu115C[.2/jN6Zwl{p~swda%$ؗ`LFG dPԅ]*يlRb\ܮPKXf NPK|*yMConfigurations2/toolpanel/PK|*yMConfigurations2/menubar/PK|*yM'Configurations2/accelerator/current.xmlPKPK|*yMConfigurations2/popupmenu/PK|*yMConfigurations2/statusbar/PK|*yMConfigurations2/progressbar/PK|*yMConfigurations2/images/Bitmaps/PK|*yMConfigurations2/floater/PK|*yMConfigurations2/toolbar/PK|*yMM-Thumbnails/thumbnail.pngPNG  IHDR<2 PLTE '0") 5! ;" :-""#***$.:4449995BO:MaE' M<+A<8g3g9 r> B@?oBlE}D aI0CCCHHHPPP[[[gggkkkgnw`n~noqis~tpmtttzzzUk[v\{Z~\fscietrσF LPQVWbchmm!q)u3x>v2kGnMn[{vuB}N{E~Hcihopujnp pu qu{/w"y(~7=rw uxz}YNIXajt}csp}‚BM`l} |¤Ƶ·ĻõǻIDATx횋sE/7`blZ;(-@E%<(@+0N!9;vQwo7D;tm}oc(C!D2dBT)[RM]*GHTzRHOMS͂Duu^:Զm# ~i01>aɨEq .:ˊ0ZtB]cj5@j1fha#ah頑ZfX5vXw6u?Kk7~P7JS.u]WIq}2u}b-];{hI涫LcwWv M}w@+%x[PEj*n?UtP]N&3_}[F8f{44Yh[I'A9q^*j8.(5 {ЅY:dlx A5kǝ ԇ׻%x͸ ;bdh}dc`` zk4[[YB[[Ĉ ]42qM0>p)ÈO![ q/9ξJ&FI,ŷ(Gk/]SJ>HixD=ƵH)AΣ7R'w]z^ l(&G) '|VUIRL"E2y:6!H)d\$J4 0Ep@-Lhuꦠ|-fN&aEY,rwT K>*w(Wa~ aE2;agV8MvH1/:]ͮ'GW7C3f1&F$;0h:Q퐉 -Ѹ '_UТ#[lH46W@'㗜ϸYϳ92P$^Hr CyPzs#T$y,(אSŊbLt,<~=IV[х3P8Wފ$8*djR׈@6Ѣ_]RUbo 3ҭ''pqVP})%pVpٖCUh M"pAC=g`7 S#6RDX}T$Onj|NHh֗~L0,>[tqg6۔?8_N(Chz2g]%B!^˓!4=K|Nϙ!4;88!B E'65X=ᴳ S>BFshSJ>fY`_sU"GdA>  (X]4NH[Sъ 득E z=>/vxz*$.wgZ6YWs@Ӹ1U11mxzagӹuVoL1d`+o0}w SQD\5C4tod{U9YzԮHOYAe)I{.b! V]}!>}u0SoE~QU?)OVJj,ORtQs*cAQdth_aFbQNG;VPh/ $<}V0weʳJvLHagaOs.uQQYhiJz}vH qgһe.S`L ׹#Ax; @{U?:C,vAI uki8t%h[*չm/u0;>o &mqP*7*f 1wshU OKMLbN^ בKmѯI3`NewgnЁܥӘT?u1ym{&\j<9Cas7gnΡ̥豹U?v1y9}FmH>X'(v .ϡe᧨6UCT7w %%(Gh|j"VypB٠\s~|1U =s[KT0}{iqљ2[7 A-/.+"z1tݨjgCӃ"|e|7WAǥtGZ2/Yǃ*#WD|D"vkVe;x0~w'ӡlvq= zƬ%d4*7"..A&O Pb79nЦU2uxZL2Tryo`J&lUi]u£e?kh:_&E.]=(ZYqJo 8иh׍=˸ַwPK. LPK|*yMMETA-INF/manifest.xmlSKj0F{KmVEɢP3 >!}e'.%%065BDG {/ҮEnXEALr*mX$%) Q&-j(ɟrdv3kv6NnԹLJeB'2uj3in:4Pp 1W0qWТCÔ(H :ԑ%:,$ŋ;xxhJQ>Q}Z(@i]:7LConfigurations2/toolbar/PK|*yMM-tThumbnails/thumbnail.pngPK|*yM. L styles.xmlPK|*yM521T&META-INF/manifest.xmlPK6'muse-4.0.0/src/svg/projects/paste_ctrl_erase.odg000066400000000000000000000253411404105311000217070ustar00rootroot00000000000000PKP*|M.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKP*|Mmeta.xml͎ } ˝-l?HUUu1U+5`RZ,q:=˥zB\% zQ[>9# vJxo]G57)XkֵRГln#yЦ zyhzH[fb_>j[2'u x||^o[^n{aLujg|c&$N8)QxSQH 1B$,+Rͮ >`/L76׳+ZJӖ3|;8h_fܽNE`{~rj!F2M֛_XA }ºW͇>|PK.PKP*|M settings.xmlZ]s8}_a3 M2111Ix-*d +Rx<bI{q.|YxlZ QAL'_Ta1vQ27yT]ęNEy| 8-3 (S!Qn +o.d' 67/ a֗>GBA >ps&OEY):2,^ҴXoe4;7_ca^:]}=YKePY;gy9[?4eyQsv}E#`1?(jT*b]4/RC?b(/-'ӝKך~,|~SnSpTdG`RMwW3g31O$ e7Zj$a $3Q:Ԝ\YXX*|~]&Wgsg-6TR' #ߌ8 ۡrʸ: u!-e0W k3 -dQ8 MLP:A&[]NO3 "Gd9Hx*였M8;=QXc W{QK8rC!T:JbO=ui@2!ǝcp<v E9cw -~_0@A 2d9 NX*Ch Ξ:ŇW( 8TSQD\1P?T9TfTpqMCdf|PH%k w'AeB=b3s$nv74}:w|~$Z&!mQS׃S@Y$}л0kߝbzʿ-8M4z ui?f#K k`{hPݜf۰٠##oxe5m-l͢\t;K^߲Um5u0[vt\Xld+4٫5a"04pU,ɦ`C$P^Ѯi q#}?- PK#5$PKP*|M content.xml\m۸_!E]PrkQWl%Rz_!bʶlmv7)@3CHB.oL'Jiq"TRD4NizsK$iD19)j-E Xբ^kV,(jQT:ZВB^fͅl]z1# e:fx3՘yB?TP@=/qyŧ+uU06uʖ +,Zqd*tsRq]٥b6\U-@sZG ~9slr aPc6jd}=.X>u,;*bi9ylO)]fZͽ9aiMTph =~q hĽrvN9=EUbLUݑE:9a<Fw FJ~b<0.T,;tdݧd:8Nfϙ%,8iLt;ز?[.fCIXE8fAje3lc!c~JGT+dذ\H3Ӻ㛔~{U]~5LG>jHRc]^Xe 7Z#$ʪ|ꛕ}ަp xCSRKZuOiTA\_[c4@L{R}tנdo\K83iz/O#CmUI>1b8'r qii?,ѧ)^v_wŕD/AʏGW\g=g㻶kʓHD(%kЖnS_׵N$++w8}F@M_` ," ?1&s}r+ ӭEDxA 6_ƵlP1޶;o6Nlq Gjdž;  qK\LZWj&xje$DsZv$㞞4K'?o8mVG/IMWwחEIibNP^Мl+SQTLB^;o% Ų'ϾR<~p|Ѝ5(?e<5@$=C4|4.iq =k=,z>~=@+~C4- $mskJ²" 2G'E@ dG`n-;@LݕXүÁ__3x,N9qFgLn֖%@pQ5?qM?iHw}=r PR&֊;?<)cIF_  M:AuJ4I 3bu}?BU<۵Atq#~~[F6`R5,:WHQS>ǷWGP5LPzAv)!$7TcAr!(ڀ\Mԃ(|ЃqF"+~tF4AY>GMt\ ; \>r>Zurg`^QMXd剀wN4hN4Q[f)jP!A!w9o|;Wc(>✁iE6or7qZo뎅BHiC:T\>`|IMGcK۴T}fz]fOf)@Q+LMscE1\PKHGPKP*|MConfigurations2/menubar/PKP*|MConfigurations2/toolpanel/PKP*|MConfigurations2/popupmenu/PKP*|MConfigurations2/progressbar/PKP*|MConfigurations2/statusbar/PKP*|MConfigurations2/images/Bitmaps/PKP*|MConfigurations2/toolbar/PKP*|MConfigurations2/floater/PKP*|M'Configurations2/accelerator/current.xmlPKPKP*|MYTiiThumbnails/thumbnail.pngPNG  IHDR<2 PLTE ") 5! ;" ""#***$.:444999)6C5BOE' D/A<8g3g9 w>B@?oBaI0DDDHHHPPP[[[@Ulgggkkk`n~is~rpotttzzzUk\v[zZ~\fscitrσF LPQWbbchnf n.m!q)u3x>kG{vuB~O{E~Jchopup qu u{w"y(~7=qw x vxz~YOIXajt{sMǓ^`lÛr}¤Ĺõǻ JbVIDATx횉s@DGibm=Gmb:zꨩQZ[Qqﺛ 6I;7tx/oTfuCQW+ C]KqqZ戟0ᨓ<ԉȨi9C3DoWU,ϬH֩ Pת),Ƈ#ZtN\Sx5aU #} ҍF\ =}JlUZDi U I6ֺnB4+9VCzɅwFz)O<4N7Y%dPñ{(D7>e|vZF bnԌٰ=FU>$N /1٧27"{b#~,uʤp.ߍE1.& Wl޸^󘍶[Mb5`ﺞ4nO jF>gok5;Z0S/jS5~m5җ|Pݨmn nq0L5vm4r#^NRO?эn*- u9057{M>T2mZ1SWj=>TᛍACtPcfc;4~pсow{+|~LuKq37Puh\J“:9yw&GhGݷ#ǃV+Ԅp/lgS7{=ZVjגFMd@q9?؇w7|{m#D˪M7zQ#=ԭ^K'5Kns#tUb.g4?^cCx]z ݃'s5D>ē?/暍Vj\{ Rw<(AuK5o|zMg >k . ؍v{ȣDjX˵k ~pNjkZ&CQCx^cEM gETnKO-];{hI涫LcwWv M}w@+%x[PEj*n?UtP]N&3_}[F8f{44Yh[I'A9q^*j8.(5 {ЅY:dlx A5kǝ ԇ׻%x͸ ;bdh}dc`` zk4[[YB[[Ĉ ]42qM0>p)ÈO![ q/9ξJ&FI,ŷ(Gk/]SJ>HixD=ƵH)AΣ7R'w]z^ l(&G) '|VUIRL"E2y:6!H)d\$J4 0Ep@-Lhuꦠ|-fN&aEY,rwT K>*w(Wa~ aE2;agV8MvH1/:]ͮ'GW7C3f1&F$;0h:Q퐉 -Ѹ '_UТ#[lH46W@'㗜ϸYϳ92P$^Hr CyPzs#T$y,(אSŊbLt,<~=IV[х3P8Wފ$8*djR׈@6Ѣ_]RUbo 3ҭ''pqVP})%pVpٖCUh M"pAC=g`7 S#6RDX}T$Onj|NHh֗~L0,>[tqg6۔?8_N(Chz2g]%B!^˓!4=K|Nϙ!4;88!B E'65X=ᴳ S>BFshSJ>fY`_sU"GdA>  (X]4NH[Sъ 득E z=>/vxz*$.wgZ6YWs@Ӹ1U11mxzagӹuVoL1d`+o0}w SQD\5C4tod{U9YzԮHOYAe)I{.b! V]}!>}u0SoE~QU?)OVJj,ORtQs*cAQdth_aFbQNG;VPh/ $<}V0weʳJvLHagaOs.uQQYhiJz}vH qgһe.S`L ׹#Ax; @{U?:C,vAI uki8t%h[*չm/u0;>o &mqP*7*f 1wshU OKMLbN^ בKmѯI3`NewgnЁܥӘT?u1ym{&\j<9Cas7gnΡ̥豹U?v1y9}FmH>X'(v .ϡe᧨6UCT7w %%(Gh|j"VypB٠\s~|1U =s[KT0}{iqљ2[7 A-/.+"z1tݨjgCӃ"|e|7WAǥtGZ2/Yǃ*#WD|D"vkVe;x0~w'ӡlvq= zƬ%d4*7"..A&O Pb79nЦU2uxZL2Tryo`J&lUi]u£e?kh:_&E.]=(ZYqJo 8иh׍=˸ַwPK. LPKP*|MMETA-INF/manifest.xmlSKj0F{KmVEɢP3 >!}e'.%%065BDG {/ҮEnXEALr*mX$%) Q&-j(ɟrdv3kv6NnԹLJeB'2uj3in:4Pp 1W0qWТCÔ(H :ԑ%:,$ŋ;xxhJQ>Q}Z(@i]:7Ls }kW)b ޾mqi Gj92< 3"dPT5@)@-4K)Id9~_+3:Ks1 ! d K8cxoBş ɞN>K)a|~ڟP«ވpQv,Z_9^6ɿzSw\Z'Y~/bXڇPKWg.PK))|M settings.xmlZ]s8}_a3!dc&c_-b&Td#8ׯdNgyĖ9Z!㈒\\˝ARmnh5׹ϕ?nx\Xps&KMEYޜO)!ZMX /iEaz4>Kͻf+@,ꥪW"\e0ܬ|#}#gJmNRF?'kޯslĕ EX&""W_]sSx.'/O[M;ˣAGă/ۦXccèm(UTL9ɐ+wl;bH24 FɶO*})`MّD^]`b,}*l㐷0"U+?10G g:9bO=ylf@P!ǝ OC)𯕀r0`9N2U(a@VnF}T-8KJ:ii,4Mifjcb!e *XL@A96|3s$nv3k- d;mÎo/cw2l} bbwb5,\o}4!~6|91ud6xR=ud/8-?74}:wx~Z&woN{"vȃޭEQ]#^/2ijkӴKHQs4=PҢ>\SYjHb\.?4ٽ+iki] nծiOPzg#^Y^0Y+sI6 p'0d饞z >P7 ~:RʿPK֧$PK))|M content.xml\드.\ 7*w|IM bF6 N0;;fXʌQwK?߼KRE CUHfK_|ް8!YF,ܤ$+e%* ˊznxd2))ed9e{)תFr]ܕsoO_Y2w#s/\"b9.w >]̗n[kJGA*|'+ uXޔx~RIo .`WsN `scΛ+l]M1g*V4U+r=zWqU[cɢj*]N8$Heon%3qg^$i"Wk4ii ˎt?nt␲(> `Eacۯi"CRW:z5U+v FqHIqOR /k N«|D H" +ݥ'o|ՠڊd:R8rZ n1,s(3+qZP'{s\ ]QA:qF#^vcϭ;$784GˆYt| )5hYT>JV hxSǛ nƻC[@gK)M{8jkBWkX)IԘ-S"LaN=Zp#@+Y.Ȳ7,E>FKH,W1*ӎs$n>r6آc5gg=3x6s,!-̱XjN,XzT66g9# 6 ,|[_pD븴3F_U1٫zc`_qo-g>.]ٚg ; zpnDC=Aw{Tz<ssE̫ P9@7dDPz#-MdP\~zIفxvs{mDžo)0`|ruF>6u h` d _\ AE O]x׼,p h-0~><k1ٟ4̛w;RDG}h0WrWWf_Xc-|[\"lj !L(gN(_""_: *t4!n}fLKgBmdXЩ{± u,0ϙ L_1o}"bFi{C'VI_׷xN ɏާotqPK)CPK))|MConfigurations2/menubar/PK))|MConfigurations2/toolpanel/PK))|MConfigurations2/popupmenu/PK))|MConfigurations2/progressbar/PK))|MConfigurations2/statusbar/PK))|MConfigurations2/images/Bitmaps/PK))|MConfigurations2/toolbar/PK))|MConfigurations2/floater/PK))|M'Configurations2/accelerator/current.xmlPKPK))|M$2Thumbnails/thumbnail.pngPNG  IHDR<2 PLTE "/!) 5! ;" """***$.:2*#333999/>N3kG{vuB~O{Echnpup pt t!v"y(~7=rw x vxz~YNIQXajt}sM`l |¤ûõǻɿt,hIDATxyS@"z Ңփz RYvsQCţ16_$Mld6L%x00XԞ?Ry"1L5_\zU>APGYpBA1]0PR̟mQI ˺%PZ6P@F9at5awHߡt?Q㸈I-@EC!jEZj=tߕqp/i:'+#Lԅ=תmE<[j }#V5{ =%v=Z!B@V3;$w(E]Xܳ{buMO꠨a lVz,68CVa?ՆӲ{vģj}"\M /DYXT5\+\}b`1TT3wY%jn~#-DݧQCQCȞ_T7S'h&XV}u19Y#6UGh0}d-kJ̠̳W_[:QP5Rvv!שF_5O;3·?k@@ji۳@>k`}vyF}:8;ev3x"5RjG2ՑJq /59j曩L=^|@g-ԫY.~~bqb:\+PzbֻYS5}r+VaJiW.D"m%kkldC>H_#5C !&ZVL󲑵]5L_ddfֶҲKuYggYK)&|@7yuN0vֲ5:jG#5ѕJKPmĵ1wǻMZăliC]Wwj׶YC<Ԟz?9僜IENDB`PK))|M styles.xml\_۶(Ӿ(RI9ؓNigHPM-];{hI涫LcwWv M}w@+%x[PEj*n?UtP]N&3_}[F8f{44Yh[I'A9q^*j8.(5 {ЅY:dlx A5kǝ ԇ׻%x͸ ;bdh}dc`` zk4[[YB[[Ĉ ]42qM0>p)ÈO![ q/9ξJ&FI,ŷ(Gk/]SJ>HixD=ƵH)AΣ7R'w]z^ l(&G) '|VUIRL"E2y:6!H)d\$J4 0Ep@-Lhuꦠ|-fN&aEY,rwT K>*w(Wa~ aE2;agV8MvH1/:]ͮ'GW7C3f1&F$;0h:Q퐉 -Ѹ '_UТ#[lH46W@'㗜ϸYϳ92P$^Hr CyPzs#T$y,(אSŊbLt,<~=IV[х3P8Wފ$8*djR׈@6Ѣ_]RUbo 3ҭ''pqVP})%pVpٖCUh M"pAC=g`7 S#6RDX}T$Onj|NHh֗~L0,>[tqg6۔?8_N(Chz2g]%B!^˓!4=K|Nϙ!4;88!B E'65X=ᴳ S>BFshSJ>fY`_sU"GdA>  (X]4NH[Sъ 득E z=>/vxz*$.wgZ6YWs@Ӹ1U11mxzagӹuVoL1d`+o0}w SQD\5C4tod{U9YzԮHOYAe)I{.b! V]}!>}u0SoE~QU?)OVJj,ORtQs*cAQdth_aFbQNG;VPh/ $<}V0weʳJvLHagaOs.uQQYhiJz}vH qgһe.S`L ׹#Ax; @{U?:C,vAI uki8t%h[*չm/u0;>o &mqP*7*f 1wshU OKMLbN^ בKmѯI3`NewgnЁܥӘT?u1ym{&\j<9Cas7gnΡ̥豹U?v1y9}FmH>X'(v .ϡe᧨6UCT7w %%(Gh|j"VypB٠\s~|1U =s[KT0}{iqљ2[7 A-/.+"z1tݨjgCӃ"|e|7WAǥtGZ2/Yǃ*#WD|D"vkVe;x0~w'ӡlvq= zƬ%d4*7"..A&O Pb79nЦU2uxZL2Tryo`J&lUi]u£e?kh:_&E.]=(ZYqJo 8иh׍=˸ַwPK. LPK))|MMETA-INF/manifest.xmlSKj0F{KmVEɢP3 >!}e'.%%065BDG {/ҮEnXEALr*mX$%) Q&-j(ɟrdv3kv6NnԹLJeB'2uj3in:4Pp 1W0qWТCÔ(H :ԑ%:,$ŋ;xxhJQ>Q}Z(@i]:7Lu'{ޯh; Fzp@&M/ՎcidQׇQGWηaU«[PKȕl-PK*|M settings.xmlZ]s8}_a3 M2111Ixm*dW2Rx<bI{q.|y2(ϵ$.V#R놎ȅes(p\Hxp4d=ٻ-`DfAP<|N٤Pu r D,81.%c49e{s.`Dxv$onwCW4|9K=1} Xd|ҕ VÙLO# `E췎| ŷ\*^jZRs^seajB:`!6>*EER8u}LK_)j:ԝAm)z28YL`(%?CfIo2o)er$7=~R6Bzh(\ȝYE=7 jS@&ФKiy!3i{.+;& >jN;N~(.&k0j/jI/ӡ7dwHx:䃪C/ԓfer;va9`s6vy0*:QQ@g(Ɏz qe|sƘF8Aj[Ua`w՟僐A=e #b!>:WZj Ξ:Ň6 *)`,̀~JV \΀y:TɜU S,)9\)|P7;9K A5b2dCrGgI2]- fy4|+ƒW)Ic2'w]i`Y45<) $2+<T]c˪3XldLZtx7IeQx[ !yU(YWAv/2ř0Lgu+!mİezz14|j{^fqT;v d=[LErFvtl'omyxNCLN?5Լ5/;Fc430fC{/5R_jnp5x^T\^^|]sCzطsGmb^j~q^,bY*0G )" \K)Ї=ɀ'S/-@BC\((HaO*PKϘx$PK*|M content.xml\ms_aNӚ"wo܇ƗN`pG HYV}H}]S&vX^̗n[kJGAR:z'R+ u1XݔxBRIo .Ѫ9M/Vn5s|rI~XPm;˻}\tXBUi>yvמ1ֺ* .5 ֫ӒzxR=I"!@頡;&@#^["nއk2=Ѭ(qGHi2y@w$hqF'=:\(Ix.P<= (fѺLq FuţhPܱt3 펒7j/; 0%՟3J\piBuͭ1dQ>' NٲCw ]C/IQ4Niƴ@ eǺxz?;IYx@XaQXP<.dH:^7uMc6Ph10)/6+սJ${K@iTakԿ8ЫU׵V$ 8KqiB2Üӧ]Ɠ|ùXN#j|w~ 6Lp%Ig4)?;{7|x~rBF#zNpV׏_j`~> /}v)( BMC}r@slG+5 sq7Ze$ʥHmMr%%D(9H-Dm, 䇩,apVƐ?"܆$F:~ol0j@e3,iuJuZbSKV3$9l2Hi]KEfᄮ }%wwpR(H%T[qք0R1[4k[E`b:u{;Kos'Zr!@} ee)!YBb98PDvbT $v喖k))+As^'< ݬ$/ 6 rAyp;c9i ?Rd;\z$kFT6=l9#?mX9D@(̈́)a4k;%lru O6x^*٣ 8g֓j-vM}|ވC\W5gqԆ)@kOkxje$RXw$O-ύ$; >E͘ha6Nj-wח`l-Gpf))NdЅ\6rpCݍUKmW|N7yei(S~xk-Ay+0gnŲ!Q̳ /^Ax_3H.[ ||J vWHpkkh{(d?Ą ;m1d4Wcv>8Q4 {tY)gmA{)9Ί?g%.W -ׂ>H!`f"wOC8)Q=t/j8@Pׁ_Ofb'Sјfľxc.!ϵ= PwWT!Ch+Sژ~T:R>Q$J)~Yg,C vv$kNL}h& tik)gFu& Y\pPW:k9+h#XoNӍ8p}%nL}Tm"T`Wd3C 53WPT3"(3$'c0dP]~z$NcA6<нڶ͋J·B(T=Sc#c4…k/eРy46"3Ed`C8 ߁#+}:8~.k;aE"ꋠ/3E5SLQ/P3jn/| E4{u@U>*iZ@a}m_89O/ Mϳ(asmxcA9g9Ι )֍LQ3E:)^ #'5Sw4xjh_:{_IGPK}LZJPK*|M'Configurations2/accelerator/current.xmlPKPK*|MConfigurations2/popupmenu/PK*|MConfigurations2/statusbar/PK*|MConfigurations2/toolpanel/PK*|MConfigurations2/menubar/PK*|MConfigurations2/progressbar/PK*|MConfigurations2/images/Bitmaps/PK*|MConfigurations2/toolbar/PK*|MConfigurations2/floater/PK*|MW)Thumbnails/thumbnail.pngPNG  IHDR<2 PLTE ") 5! ;" "##***$.:333999E' C.A<8g3g9 B@?oBzKaI0CCCHHHSSS[[[Nfaaammm`n~is~rpouuuzzzZ~\fs{citrσF LPQWbcnm!q)u3x>gKkGkK{vuB~O{Echkopup pu w"y(~7=rw vxz~YOISXajt}sMŽZ`l |¤̼Ĺõǻ9IDATx횉S@œV5/LT-P'L^<+V86njwmf4,uwi(CݡP#"=ʋTyVŨ^kg(u<4'FQ# C\Ө,!:ZUU&^yAbKhԫ@(ԣtEHB dQ/y4R8{]Vlt6U-Ko ZfjB%Dk}'DKQԋ\!>I)[7!%\$" >!p9ӻ" mM*hi h3|zۍ߾>l":45a!F9|O*Qej9Nˣvxr.cw͑{2G+@{B\{gRܝH$Z53Ϡ{D5P}Bl@SsyqOpMs j _Nu^5% Y@_|c7j!5ԩyL ^clN3Sϻ$Puΐ#uekAt5zkuZk q$mmn}^{GAPZ̨¸K-h6HW,"xrʱ3#Zt88RGe5v?-Mr'W- .5ZG_@-7k>5z?0usH3NZx-jNn2bF6^/:ScMVݞCۻuעڐ{s5s;%kBVd)fg줟P[^c4Ԧ煦S7xFg]F<{ZU&{'?gK_ZopVN 2Oՠ=5^lN&E-s{P൉zf'uI]ԃlqWw:ҡPw;lφIENDB`PK*|M styles.xml\_۶(Ӿ(RI9ؓNigHPM-];{hI涫LcwWv M}w@+%x[PEj*n?UtP]N&3_}[F8f{44Yh[I'A9q^*j8.(5 {ЅY:dlx A5kǝ ԇ׻%x͸ ;bdh}dc`` zk4[[YB[[Ĉ ]42qM0>p)ÈO![ q/9ξJ&FI,ŷ(Gk/]SJ>HixD=ƵH)AΣ7R'w]z^ l(&G) '|VUIRL"E2y:6!H)d\$J4 0Ep@-Lhuꦠ|-fN&aEY,rwT K>*w(Wa~ aE2;agV8MvH1/:]ͮ'GW7C3f1&F$;0h:Q퐉 -Ѹ '_UТ#[lH46W@'㗜ϸYϳ92P$^Hr CyPzs#T$y,(אSŊbLt,<~=IV[х3P8Wފ$8*djR׈@6Ѣ_]RUbo 3ҭ''pqVP})%pVpٖCUh M"pAC=g`7 S#6RDX}T$Onj|NHh֗~L0,>[tqg6۔?8_N(Chz2g]%B!^˓!4=K|Nϙ!4;88!B E'65X=ᴳ S>BFshSJ>fY`_sU"GdA>  (X]4NH[Sъ 득E z=>/vxz*$.wgZ6YWs@Ӹ1U11mxzagӹuVoL1d`+o0}w SQD\5C4tod{U9YzԮHOYAe)I{.b! V]}!>}u0SoE~QU?)OVJj,ORtQs*cAQdth_aFbQNG;VPh/ $<}V0weʳJvLHagaOs.uQQYhiJz}vH qgһe.S`L ׹#Ax; @{U?:C,vAI uki8t%h[*չm/u0;>o &mqP*7*f 1wshU OKMLbN^ בKmѯI3`NewgnЁܥӘT?u1ym{&\j<9Cas7gnΡ̥豹U?v1y9}FmH>X'(v .ϡe᧨6UCT7w %%(Gh|j"VypB٠\s~|1U =s[KT0}{iqљ2[7 A-/.+"z1tݨjgCӃ"|e|7WAǥtGZ2/Yǃ*#WD|D"vkVe;x0~w'ӡlvq= zƬ%d4*7"..A&O Pb79nЦU2uxZL2Tryo`J&lUi]u£e?kh:_&E.]=(ZYqJo 8иh׍=˸ַwPK. LPK*|MMETA-INF/manifest.xmlSKj0F{KmVEɢP3 >!}e'.%%065BDG {/ҮEnXEALr*mX$%) Q&-j(ɟrdv3kv6NnԹLJeB'2uj3in:4Pp 1W0qWТCÔ(H :ԑ%:,$ŋ;xxhJQ>Q}Z(@i]:7LXu:w0;{ؗ=T4 Đ5PꊖxHTEYBzs.'Idhv<:gh+a=)g<{o8G v`aMcd ?;'dY249CY2tq2Ο%?髓14~yzzBgE'h򿕨-:} %,O *O)6s@Q͢2²PA`ځT˩x{s(kF啔؉ik*ZWDsDjh\eOT=[:=E Q:vs˵FW!46eccsۨ8NDY1wd;(ߕYiycOGj E*i v019Qz1rx]txc8#xsThRU"SO86ZAn."fnM[_4ΧgM#Ǥk0˩{{Еt%@VEM48UΗ4{ ~Xoe;;u{/87Jb^ ڪ}T h{P}rhG|!/!T* ܑO&\pW^.=M|?/¥o E< :7ߴ>t޳ wv,sQ_G9t]lx붠ekQ}~,ugC< e9 Wvhws +[ r؍6w(|uoK swgX&.~WvE}Wq~[P ~ЯH9F=w"Z7o֑px@y=eUaYL{$puUxz8ތf`ӎmnT= c t2BGF[0 =:k|5Vq$hN*+~%Lz^+ T]lɫC|5N͘ ~#~pN#AhbU ?E~N]u4;;sR.cټVbFZH["Aտb|ÒݸfoժC kTV"Lo(_7oOH;;^fWMh6ƨ\msZWvy[6l}#ao y_PKOз DPKvKSPConfigurations2/statusbar/PKvKSPConfigurations2/toolbar/PKvKSPConfigurations2/progressbar/PKvKSPConfigurations2/toolpanel/PKvKSPConfigurations2/images/Bitmaps/PKvKSPConfigurations2/popupmenu/PKvKSPConfigurations2/menubar/PKvKSPConfigurations2/floater/PKvKSPConfigurations2/accelerator/PKvKSP content.xmlZ[8~_a1}*l lZZk4] 5̯cn1IP5ZiP?zgY*" Bd~ā/eX2墨q$jRkk[ʶe)vt!Ʀ"N+ɫZ<ժVZ:<\5Ufsh^9/AS&L5063eL]ـ~_>~19~N^Ⱝ$ CU:zk9iQռ8|Rs"U^MS%̱'5]E*-pjs5) kg;q`?x4D~- bwcS iQBLFX3@5+yMٗ3e~&OAPS+6ZP jϺ24By3Q d2WL@Z7_ d 'r$Y9 8RBreHYZ|Ljae]d?K8 ;5fЊ*,F[fLlZR&M\V?ϰٴU[_C_lM:]$:;EJD_˽Ja Eg[;~4EkHø^<VPkHXDYuwFwm}JaЯ-jL qm_;hj͏7`2y1(:왫מLf¹d-_Flx w>2xfTZ_哒iܘC3/~ȳG_x󾉏'XNW_kݞ8Rd"J95.`4q7Fu!QܦQ?\rpn!*z.aNE`O/ P*1c^Sďy|#HGZ;}܉h+gCD"^]]wRI驒Y# eD9(zw9J.ynV$f=븣W鮝g&"ډiMa53pXg 0:ͿfnLOJ$ p-K#a @ 7 hHjMKvFQfk^$Vnz5|Y{;/Bp< e8ԎNmI?;l=Ql}Uu&oå;QrMgUyZ bI.G.+G+T J࿦|-ͪ5~4C6(6Az Ri=7s ;kݿ}_/6o~WauWo=yVO2>ݡ}>g2&ќ :՜p-@wYm *5sOQ7Z>Te@T9p@s߹dy%6MFfDNNaB'^0}\dz5v+<-Ű !^KVEQ~`}-51C._^4k2ՖT$TM ]hwGY"~㍐JJ|oTF:&oLv/Ƶ^Ku8MR@_z" 8AB 曆q==n \5s`l^-?t }HHwg׉gg]zƝtRuܭdk;l~]FR]n"~I\H87$!K4A㛖؄-{ބ8?!w\*m 숊Є0|k&ehdxtAp`TH5b Eb* _)PK.$PKvKSP settings.xmlZ]s8}_a3I ! K%_ɐ&%Pwv6ls{.\}[9 qΜDm1]\gFC+56*Bf{2+j8Qө(o3Ʈ3K)b.\DWO_Qȭ?g6suȀHDQE=2H%*ܐ;ߢ[:pc+6#*Ey=5֊`CPYPȅw CجWOmFPJr|nPLYdDeMvQV|=y?cm19CfoU71U-/нB^|H >P._/ Hg_gf1h6)\F~n+ȔG[:ǰ)6˱?g @3{@JoqFM12Ͳ ^r/!z,P"=)Iqe? [Z\]L31] a)v]b5v0u uk |qˆ-Wm6@e%RÿUOYZ~D}PfJi,xUao%&O} nJ۩mɸr~>@D 5Q"t6),ESTB8D+YxgLBT.Pk Ht3?zcK*DYZ"_V~s?_HOnRmd{ULJY(zQgU$_*,,6vwXr%MxFu)4)3Ɯ[`٤J$x=a~ - 0=@ yxf}a΍$ ī@g2СvQ㜫.v=N>psQf???|O9!``xT]*x9{fC%u\X-fOTej`LMxD*Y]ŏ5g)0cWZli44ڙZO;I@T uKð-u V75g_N \-nc-d5R)3I 49N1DL40(RikV9db]!NEn(L-ĕ#)XF@x֦y Sɋ[; Uw9'zZeO aoZf7ަYYUr3i3QF<,uDfd".pՖs ӣm ?}@_935s'O h Iy0~4ۦiZz}LspnwsUee16 ؛MwAg;ăqPq X 0)xqɦnBy+~^_-a}T9 fyp1j)_)؇Eл1\SgN@<̆}p3.OK($&rK+42VZ-x#N!R'=8ʽOnׯJPKB_&PKvKSPbGThumbnails/thumbnail.pngPNG  IHDRAA)9wPLTE    ! 74:6 <:$ 4 >"$$$***333===D&H(W1]4b7o>p?r@CCCKKKSSSZZZccckkkttt{{{-2 J#_7Q'R(U)Y+b8Z,VIDATxo@`_n۲M5E,nsӭu()"wbi$=Qvm9W !UBz|tAgtP!m# wZ@Bc焂`dCqt xt tbch0ef9^$Ud9X"Յt 'a*lU\hP[hϡJz+rQ>@_{FØ_;P= Q,B,q+`?080A\tCDifףN! DxXaSn~` p50=a;I]{IZ$c n!y/D q^&I*jStjr+jJ $֝$F9hdo"'Z[UU@[A$9#IDбAlŒ>Ţ F=Ԧ&b.u}B8Pbo&h ;\x[X;sgDA`un}$OjY8S]='ڄT)Ԁ2 Z˿~.X!!4zm nj pwC"!w˪A( UBBǣ$!Sիj x*V%~|TJ;xK,[EpoE$\JX?pDf"tǎ2nrI-5x~|Ϧ9S}B|8q͈74 =f7ʲ+dU?;_xIENDB`PKvKSPMETA-INF/manifest.xmlSj0 )ﱷiCaO=ȩJsBf IH@17i# :|l]m"4Q+W $ ɬ@e{_NLn!`ͮyƍ/ԫ\Dpq)+1A.&[0hr`mP1P=Qc*z$N)·ub\qh {OZ3Th rDx1g97x}Zcqߤ&Sq^Y6+nPK"PKvKSP.++mimetypePKvKSPfyQmeta.xmlPKvKSPOз D &styles.xmlPKvKSP Configurations2/statusbar/PKvKSPM Configurations2/toolbar/PKvKSP Configurations2/progressbar/PKvKSP Configurations2/toolpanel/PKvKSP Configurations2/images/Bitmaps/PKvKSP2Configurations2/popupmenu/PKvKSPjConfigurations2/menubar/PKvKSPConfigurations2/floater/PKvKSPConfigurations2/accelerator/PKvKSP.$ content.xmlPKvKSPB_& Xsettings.xmlPKvKSPbGThumbnails/thumbnail.pngPKvKSP""META-INF/manifest.xmlPK+}#muse-4.0.0/src/svg/projects/pencil_move_horiz.odg000066400000000000000000000235011404105311000220770ustar00rootroot00000000000000PK3LSP.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK3LSPmeta.xmlS[o ~߯X_mv9TÞ:m2ioԥ`?KєG.FplU*FoNfKlݗ<yzLnX V:ڑ9eقg;鄞 L* 2\{tD׭p3$V3CBxc9W'a![}3@^:~b2j-[]4;n5\&}vTR .N VM(ΠP"88 WkiquQ][ev.]^ zeF+ȸ:AS1.w('YF2A8KEWX5 .x15})n>%aJ8bdKoSѰH%ݣqytwּa>R8]LO±;/Y4!5o@ZkPKqPK3LSP styles.xml\rۺSht&E?R9Ir:NNgzHHBBD/'.J$ɎLa~vVj8:'ϡ_?#Bd'}tCPէ6 SIxYRʕ*Ɋ%1sΓj-eذyU% f8{ W<|xq,jYRMǓ:bxgCslww[:cH[A.%,XrpK~.g/3ӓGdyQho%jGOlasig)B!t9A4[)f(YVTW+h1Y;P{9/P;bEzJ9\VyNv M';G$M@)vbK(!K^nR?TAwaل*6zx:hӹ,nlZhfYF^QQBZd!GYzxzNbF75V+獵CEz!]Ky'.m6.M ["U#3Z"`2vO8sga1x5|v:vZ&U7{ ~Ttoe;;s}/x ~4>|2㻹U2^#hzTОC,!* HKB.QoDߍ+MΏ 6\Qҵ2\oSgل M;;yhno|6y<3f\M[а7i9?xa ʆ/UxoعZ"e%&|_vXPۯ_k{}tԛ[5ӃYƊrruq(R4X丱J:j>H,-._NPK| Ϟ DPK3LSPConfigurations2/toolpanel/PK3LSPConfigurations2/menubar/PK3LSPConfigurations2/floater/PK3LSPConfigurations2/statusbar/PK3LSPConfigurations2/progressbar/PK3LSPConfigurations2/toolbar/PK3LSPConfigurations2/images/Bitmaps/PK3LSPConfigurations2/popupmenu/PK3LSPConfigurations2/accelerator/PK3LSP content.xmlZo6_!EВowE6E^FdJc ASd+)Ȋ8qf8P uʍnL]eL7? 7Xd],lPH26iaݳː2+6kF^Btg}% Xn "˗*`sQ眪j*Nk`l/UʨRǹ`߯mFL^2v8lbR hfT[Ɵ~ (+놔'u|[J2' ?6:bV3p I:Y,^[VLTZ;'s ^urq;VezX %Dsb$ڐ‚WY$OeMIخ榇LHޗaKtdGol(%l$wr &d 5TrViU!OIP<,Y'瓟%lɉiȠ9jZYm _2-:5i6r]ӊFf'y^a-:WTv3DBK]L:;EJBg {t="y4֎2IAH|!iZjeGTZ}MԆHA (i:p= {7 Vu@AX8nIA](%YAG5lf/g-r@e6v1漧)< LuCϲ,nR筛~k;"yLOKx_3ӄYsգ'LZ=#[ʒV&Hz) єjEpE8õX"]&& 8%++C(&5>iͧ'<8O8|N|ϼbѓQ1VmȲr@Ty4TpzY4H9[l>cBz]qg1 g)Ѳ|/UdpYB/Dt `!T?g%VPHfon[RJ|jX%|P7-I#(w(&KK J9ۣ-R.!yݥvrSIWmM*V 4ZnEkŮZob_ 䋁WkEm}Uk̞z3?ݿkǢμAlz ړU_L$d7Tl/+Y:G/%'Gnltu\VC?î" +|l<x .(F@s7G4.m DpE{0 < JMÞP~P ς丗9#}#ke$Fk``ͱ99恓 |an֞jekjUl! B7ւgG(7c1ez,zX 8 K\t'M]ޙv;Xy'\Ϻs@pٖ?yqf=wxn8nQrY+ē\)plx4#A<\]䇹m!"́ # >DDA:>/Zl^5VzK*:; {*H˛C*϶Gabc4܊cRVІ/ĖgC8g\OeOtL6Z!W{H,\붜;޴" 3B'X_dm8gmpk;Xu}7DBO䤄ਖ;q暡CG9qry(V39b:pZd@`QC(ݢLT| XIҝoЭ>JϑP#kA0}= sP(䢻Cɡtzle'.xv(jjz%^H>Szg7(]^X"O&Ψ +>a̶y?am19637W71U-/н܆O >Pη^[ş]~=X|~SnRp6Es!Q؄*2%g1lrlO#L[%>zS (mYvK$Dj>TG0E;)LJtz1K$Le]Wr0G=5=LD]FѺijqˆ-W7*m.J_fJ:GW> fi1)rCu)UVa<>">7*qn9cGn MQeZ ά&">[ǧ "g=8.ժlـ(4&/%rs°hGz"tKj'Cޫ:>"ѓ:r ?ʼTeQ$wÒ,i<5KFOtn{ &uU@P'' [he -_w^HްY]xhsc>YU 3tKeoo-|8ꬥ݀O91|@%Z@NH5px*4*Up7^a鞙$,w㉪1OM ݃H%k WFcl͛"h ؗsv&N$-B~b(fKUMgt5t{qit`y-YcMTʌ=*cR'(1k}$*+s-,S !*TFnlLPdTfM\Y >mDѐl Ÿcq|#O`I[,MuQKYcVV܌D[g:cd=2Mj9H6?}_97Zq{6l|OܙвIy0z4sӴz}LspnwsQeeL~~8ݲs=hY H 0.Qɥݘ q1xpÛZ:y8uo8SS5VJN}~ݸp&qJۣ ȃc?sD`!]ۼZZ/_Z#z~ N cvi{{wcHyr~ۙYn=֬菆5 [հƘw>}wxB\: 0IiGߩ a"7pUzzK0ЇӁF$۽;I7X_PK=SKo&PK3LSPRҗThumbnails/thumbnail.pngPNG  IHDRAA)9wPLTE   ! 74:6 <:$ 4 >"%%%+++444;;;D&H(W1]4b7o>p?r@DDDKKKSSS[[[ccclllrrr{{{-2 J#_7Q'R(U)Y+b8Z,+ʢgIDATxn0r٦%P)Z֍lMFI0d5ZBA$O^ccAzi U!$1?!$khPIVQ@sWB:%X Cfs-ClΠm0.aЄVa*҄xRa̅c 4Nev zp7KÞâ&7}oe{_cj՜a"FtT o{y"=#O3P:GsAM/|7NӋ&`>IENDB`PK3LSPMETA-INF/manifest.xmlSj0 )ﱷiCaO=ȩJsBf IH@17i# :|l]m"4Q+W $ ɬ@e{_NLn!`ͮyƍ/ԫ\Dpq)+1A.&[0hr`mP1P=Qc*z$N)·ub\qh {OZ3Th rDx1g97x}Zcqߤ&Sq^Y6+nPK"PK3LSP.++mimetypePK3LSPqQmeta.xmlPK3LSP| Ϟ D 'styles.xmlPK3LSP% Configurations2/toolpanel/PK3LSP] Configurations2/menubar/PK3LSP Configurations2/floater/PK3LSP Configurations2/statusbar/PK3LSPConfigurations2/progressbar/PK3LSP;Configurations2/toolbar/PK3LSPqConfigurations2/images/Bitmaps/PK3LSPConfigurations2/popupmenu/PK3LSPConfigurations2/accelerator/PK3LSPr$ content.xmlPK3LSP=SKo& _settings.xmlPK3LSPRҗThumbnails/thumbnail.pngPK3LSP"!META-INF/manifest.xmlPK+#muse-4.0.0/src/svg/projects/pencil_move_vert.odg000066400000000000000000000234061404105311000217300ustar00rootroot00000000000000PKLSP.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKLSPmeta.xmlSI WXt6&H=4U+5#9,xIʑmFE:i px~(ˋdpúFh7W@ՎL)c%V)[ No0sZ+ SѵIGtG<#Ab1#9d,tזsu>75䕯W) Ҳ5`YYu< Ӣ(]qmgՈ %i-*`cfvQ]U.^s\Yђ32N e(FY{I!'Tl7ޔpΎ[Kv0%-ޠX$V|k'(Na `b𩓊lOo?1nyɢ1n?l~V^ /PKjR PKLSP styles.xml\rۺSht&E?R9Ir:NNgzHHBBD/'.J$ɎLa~vVj8:'ϡ_?#Bd'}tCPէ6 SIxYRʕ*Ɋ%1sΓj-eذyU% f8{ W<|xq,jYRMǓ:bxgCslww[:cH[A.%,XrpK~.g/3ӓGdyQho%jGOlasig)B!t9A4[)f(YVTW+h1Y;P{9/P;bEzJ9\VyNv M';G$M@)vbK(!K^nR?TAwaل*6zx:hӹ,nlZhfYF^QQBZd!GYzxzNbF75V+獵CEz!]Ky'.m6.M ["U#3Z"`2vO8sga1x5|v:vZ&U7{ ~Ttoe;;s}/x ~4>|2㻹U2^#hzTОC,!* HKB.QoDߍ+MΏ 6\Qҵ2\oSgل M;;yhno|6y<3f\M[а7i9?xa ʆ/UxoعZ"e%&|_vXPۯ_k{}tԛ[5ӃYƊrruq(R4X丱J:j>H,-._NPK| Ϟ DPKLSPConfigurations2/toolbar/PKLSPConfigurations2/progressbar/PKLSPConfigurations2/images/Bitmaps/PKLSPConfigurations2/popupmenu/PKLSPConfigurations2/accelerator/PKLSPConfigurations2/statusbar/PKLSPConfigurations2/floater/PKLSPConfigurations2/menubar/PKLSPConfigurations2/toolpanel/PKLSP content.xmlZ[o6~ ԢEhI'bЧIQݢؗ(Y3CLْ̤X`g"+<琔g腉*F3WX(-¾p;iw9+j;ZTFuu^]q0}:ZFۉbiVZZv'^Zvl >%hyΘFRu+Q DhMKmOJA u`զ-)N Cݖc.j-gx>80{*iY/[V^*-yUYȝ ƾ$/Ԋj.V\܊Tٚ$yGQ3a<tUeW}V?)|WDmI١d"$u-fh,%<^ GQ[.2J*1q?NY0Pr :E/׊ŧ$jmYG񳂍pYc6 u1͕ᖊ%0I肕\Cu0ª)DU4, zL,#\4Lj]E ӠWC#(n >H/# ^KW0 ]hJeC~_Ӯ7> İ1 XUZpa>K7ڇf F X8niɫA TK~<ňL*\}k2^d| ~5g)< \UI4j̡4y隸ϟh=/{7W 4@\\ FVϑQi:/&.iz) DrpI<5Hv"U:e=?@ hpO=O 1Hɍ0+mӢ7HRHˍN*N38=U4)ÑN4)ܿ.AeT9Z{~)=Wwp㻺mC|/*A>[N?!G -VK6ڣiȶ /B$0\L?p0z]dNa@ hj( ̂Dd;#B<#seX$Fc¥`ddfmm $mbb 035\d:\Vf:A0 g TNn@dzc‹)Y%z3itPKqZ$PKLSP settings.xmlZ]s8}_a3 1 |@&,Ȓ+1_ɐ6%@Rwv6lsu.\~YqʝD=1_Ra!BQHJ5DT׷cWAP`뫧 E(֟s9b*rZf@`QG,DT1H%+ܐ;ߢ[z+glGBݏUXKwQFcgR}*|LJ=pF~R1zoZ^xYsxx f%%ul.kZ#F1#` tSuS/0Awr7 bGPFo!<_yyL!Z"M QAWo6R)$WQH&ǰ-6؂1F 8.>z[(lYKD9>TdGw0E{)ছ=YL=C'h &*ա9ԣF2Y>@>#ouAo0+ 6 +@U%rVtu> f0) C)Ն==Vc~W3Dr|^ĹPO#D?*p[ġe0WE5-eY3 MLP[:GCO3""G|ȢkppT^ٱ֘pYEYz*?'E[.^gBTvHx:䃪C -=*{IQ{hAr;,Ȓ<Ӡ{IQh I1HlRO% uyD[% q{q~ۛ6 !Sto +G ^?GWT:agOwW-]|(@~{*6 BSfTʄUuPl%騕\\/gfLTEf`BMD*Y#6J6<ia`"h=A4m Q[op lir~3 y2]- E"q?&z2"%%%+++444===D&H(W1]4b7o>p?r@CCCHHHRRRXXXfffkkkqqq{{{-2 J#_7Q'R(U)Y+b8Z,Nc8BIDATx k@aX[_ a%Qe3ےY5Ƙ=0r?U2ɣ ' 6-3NKT%BB?8_! )|Ob$mIEJ^51 @^+Ο߳X^fQ !53JOsR/2L2e33pƓ2pE @SFZ^PZB bUVcED@ e[v@km[ {Q""ԁ9@34IhZF*,nr\-ܟ]B G_PZ@U~~eF]$%.-AwSlvQD_O|u !P".ay-B [E~5 I8̐$J0p[$P 4J>R`| ŠSҙ<=5gfHW.8 Lmcu'5f=}H¾|`ywZ,9%(ᘵW}$#:>a'yόI;[CdӄY1TIENDB`PKLSPMETA-INF/manifest.xmlSj0 )ﱷiCaO=ȩJsBf IH@17i# :|l]m"4Q+W $ ɬ@e{_NLn!`ͮyƍ/ԫ\Dpq)+1A.&[0hr`mP1P=Qc*z$N)·ub\qh {OZ3Th rDx1g97x}Zcqߤ&Sq^Y6+nPK"PKLSP.++mimetypePKLSPjR Qmeta.xmlPKLSP| Ϟ D &styles.xmlPKLSP$ Configurations2/toolbar/PKLSPZ Configurations2/progressbar/PKLSP Configurations2/images/Bitmaps/PKLSP Configurations2/popupmenu/PKLSP Configurations2/accelerator/PKLSPCConfigurations2/statusbar/PKLSP{Configurations2/floater/PKLSPConfigurations2/menubar/PKLSPConfigurations2/toolpanel/PKLSPqZ$ content.xmlPKLSP5Gs& Msettings.xmlPKLSPj\\Thumbnails/thumbnail.pngPKLSP"`!META-INF/manifest.xmlPK+"muse-4.0.0/src/svg/projects/play_off.odg000066400000000000000000000173611404105311000201720ustar00rootroot00000000000000PKh!L.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKh!Lmeta.xml͎0} DgkI"ETԌ]DlOld!}R҉HlsuxjU+8Aq$\6~}yX+ˋdrV(ZK6RURǨ%tI`4~mua!M9B\ 3مz3(1:X gvLxo]Fꌰ~v)Xj֗Уlj@N?Ӷͽ# n;yhzZ+[fdߤ>Λ[3wv5xbx_O[\N{l Œـ7AxP fG0RJ҄"YW%(94os(AIJ6ENź36 .&ԪzOx6ok%a,?OQ(TO`ėpzJI,Z蟂9=|e989\ޫ:ɢc?%3k]RdǣƝl5g!@OYb.>0-˗y/WsbEt *>prt"k;6iiEMWO@ߖ u'[KCAq+P9 NsWP ^*pG'p+StȗOx1הmEh cx*|>gtԃfQU[<3?&'n$JFjۘm^ y6KDye"F*WXe'8 fdZ؄gcIT 1ӿ EFx-CsK x j c2.^X%Q=V٣uE )?<_0 X{|nhT|5 QG|}J}m#k9̧E0cS2KzR$EγnlS H lvph[)S(B 9Χ熘] '_AfDbxk^_cK )p1 .HZ,L^WfQN nz'Ԅâ߹js& ̉~dk&I*ߘX-+xk4笔18T!ɬr#z&8 ]]ً̪4>XPLw퇩[d&5ۧ^x3cB=e١bī{} ދK.3@"Ep>Ljb +MqPK워!PKh!L content.xmlXKo6W ГeIk 9vi+MQZ֔(׿ %qݕ#}A7y||~W`Ô^e2u) /E)rIMDemoڵZ"\:Dsդb:34 {GgvGna;3U]µa00d5$:%+R 7(nOw L|C[8M\33%׆U'%yn > I ޝCτLǾc#DK#?[!ʵ j4Uc -_퓵؅idĖ7;ƈRz9U-ՉjHnPʛc;?ÔY il2߻E ziOa{e:2A^oFY &ؗ Olػ2!{ {?rmZ;̯bmaInzop78"$:H;0+GLraEm\ Hq HuKȡ.ȿPK+̨OPKh!LConfigurations2/toolpanel/PKh!LConfigurations2/menubar/PKh!L'Configurations2/accelerator/current.xmlPKPKh!LConfigurations2/popupmenu/PKh!LConfigurations2/statusbar/PKh!LConfigurations2/progressbar/PKh!LConfigurations2/images/Bitmaps/PKh!LConfigurations2/floater/PKh!LConfigurations2/toolbar/PKh!LԷThumbnails/thumbnail.pngPNG  IHDR<< ")@`PLTE ###***111:::CCCLLLRRR```jjjrrr}}}Ix\IDATxK0 Ї"RR~(I8;HӼ+^1|c ¢F&ć'& cSIh#v:`a$#bw0= Īg b3jPp a8H28[7[*o{c}kFCZ ˗˥MUX9IZy)~F]w5?=ozfJɷbzv]|z._ToHr=W77Ui gIENDB`PKh!L styles.xml[_s6Oa}Iqbbwt2smizځHPB|>OrA)SlNfl .7eܻd"'g< WW\_HSE"* J@8/xW2_Rr /DAs+pz)3'+]iEXaȒ5+H++w%R"+b;ZqJ0l6gٙpryyjjp+C).VIhy3XU)%!j!i ,`.帉\ݮF{jxMh?]W%]eQoƮbɊfnW^Ѩ&ص(ý9ȾLQcq|8z.؝1>jp#JPBHOʴIkᔁT˺I BH-oip.wAD4{ Lyh jTTybH *b nqS3CP`Lј;AV,‘diuԿ~*OILƼ~cu3Ϩ+ԯ> Re^ߑB3יj|)2w8 bHD2x`tnD?T1sxFTnKE$KrOdq?K}IJ6!=?}xFW'r!%zTo05H~~Rlx(ȋi"3fcX* 22a)ªiVkb6`M]Y$pH%U].qK_D@wS8+tLJ\6E7*q.hHWgp$i'K4{ ҏMַ?~fK1w(;d8L>%žZ^B!41~_$BNEt<}q2/4O_' :k%j\WlPu{kCBMQ J5J=kj'Q1@0b{Bȝ ΢$yV.r: ig)Q)TV]V~.Ϥ@gBWxMך\=3f-j =+6i>.&vMv:9r:}mBP<iIZz@@u.&\C}QLyce>l4kb\v/$匦=)nN mzp`#Ն)8hL fD6/{6&H5}]ׇh&h3ӏE$DŽjdڥ91>0L)UVis?q. Zoֆ'9\x(( =bd8`Nkh95=|& 9*l>x:.\yD-lY5&1<h9l||쉏Qd."cczvL,nnKQ\iǗ_ rnl2*h=e鳣5%s_9FA_99=Kz> X #yO)f|x/ORbi+7~6CBr|Mr@]_.×,+@]lgu=<\pG[^nbѦ%mk$PdJ]zIcŰs$^ &⫧+[2SB0ܮRiܶOQ8j\;t?a|T ~X5Wk61yZ?[ﵹM zsSw wPK=J7PKh!LMETA-INF/manifest.xmlSn +" %aҾF S5?mSfcg?V'xl+aew)4=$*sirDU2Ir$i`uv$˙-lnSzc.qu:(:L=;茪i 25ZQi',/u!p4:1FRxLb*M@T|HOiٸ{388m,GssASIφ%:1/T&A0N :iGwPKH PKh!L.++mimetypePKh!LH.Qmeta.xmlPKh!L워! Psettings.xmlPKh!L+̨O gcontent.xmlPKh!LH Configurations2/toolpanel/PKh!L Configurations2/menubar/PKh!L' Configurations2/accelerator/current.xmlPKh!L Configurations2/popupmenu/PKh!LE Configurations2/statusbar/PKh!L} Configurations2/progressbar/PKh!L Configurations2/images/Bitmaps/PKh!L Configurations2/floater/PKh!L*Configurations2/toolbar/PKh!LԷ`Thumbnails/thumbnail.pngPKh!L=J7 7styles.xmlPKh!LH QMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/play_on.odg000066400000000000000000000207641404105311000200350ustar00rootroot00000000000000PK"L.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK"Lmeta.xml E ˝-lHUTԌ]`_DU$o {y"tYߔ OTfJ]-4;//?b('|bN˫c.B4Gp *b/&R)$WQ)1:ǰ)V؂2F>J_n{|= <,׾F1j>TGlaRMȑ5.eJǘ.EPH0HoEo"[w{I#,O`S QE䷖ otqy͕BL֣*m/J_dJџ/t)Ŝ)oKކ0CFDvXZb0}x&ܫ4('''cCDh'n( crr`gjB7 '*@d3izC$,ؽ1+; ic.N~侎_iV՞sEjC“!T:,"Ouh@2"ǝ:cGp<v",h1pqq -{@ϣ$.yd N2P+ ӥQ'MLG(+aVĚl8<lOE8P07\}7KI9iUq,$(xw@LNiS H%'wW:ULW!5N=#4tN;ЮOry2&u2pi69:;6W^PNN(ϧI8BNZΈ' ?/W)<6WD2Jb m# |UccQ*9.yy諸é7֦E؃nO`oZ77ʭ-`+߯LUYU1f&Y"D4=$1yh|tHqR8cuXHg=0چeGӨ {:|0[f?yWY0?x=/K|X ~.j.^q;3NSWq7t OXϑ!}(+Aк3D~ *6Lֵ9cPPA}0< |9҃ )?,\#T5'^ޯx_[u[5U3yQvjk]+Tm6l؁qi=][ f^8pO+4BQ{|:MpO%p߃RŖk!#ʢӸ m;PK1q $PK"L content.xmlXn6}W 'k%ڎz1\ N4IiYS@R{)K9g37%T*&eΒ0U2> ?p#aV:¢7Jeu6RLe*4DM+љ]Qz'[VOu6؁/zDTgR}\Lu*X/kA[ΪeҺx6E^__W7[1,t=M`|r25HS%UaN .&k]ЌWHN֙ ӥ oj|G0_{]rZ; KVOf.UMw$q;ЛW4 LjcǸ(O4DDF jafVd4OxEK#V)=3d|)vDb`G#F%MV;VWBr ,b2֌nޅ0iX ^uH`uR7.Yޚ"9mM%3&ĭ[6 d׏&(o.aJBuy{,™"$?xpaa|-2-ZԙT&rxD94"su{'7c2|`,&C4wGT  Ah ZpIQj H2xzj2Erw/G:gfBJj4-UNR0b UN:{3/So㽤tp=Bv-x|EÑj?+`yha0lw߃:uu{15Ǯr-Q*m|)JPF̃7%+\LXp/|7yx4saio[.rң;r~fG B5>J1/Ɋg22OG DpR* =ᘚ1 hEvgAvnЕ`a5ek(lAʍ)|Ԡd?vsF rqيҀUzOn jяwV:~l? rqIz٭[ xWUrfMgU@ߛ!lO6uC`dxpnPKPK"LConfigurations2/statusbar/PK"LConfigurations2/toolpanel/PK"LConfigurations2/menubar/PK"LConfigurations2/progressbar/PK"LConfigurations2/popupmenu/PK"L'Configurations2/accelerator/current.xmlPKPK"LConfigurations2/images/Bitmaps/PK"LConfigurations2/floater/PK"LConfigurations2/toolbar/PK"L0$ɐThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE "+3:###+++:::CMRX`iyDDDLLLQQQ```jjjsss|||fZ4IDATx]W01,P$M)(~ tNLyw7wp|vǓ4NPř 3fxlɒ+xNPKeQ!xMbMxQq.cZBLEqڋ 1Խ \8\T[noıU(Z+z&R@ (Ř~^땊ǣexg2"%k;u$㷚v3; հZX5bR:#.㺹+"Kې}SYb1/0<;2Z2Z.u%g \~  hvuPheaq8+81z %gyܽ=;o\L EIENDB`PK"L styles.xml[Qs6~_Q&)Ɏ%NMk"A Hp@P'_Iw%RlN$%v.f[QY2_p< ף_?/G߿k,DUFsjip^ zT|&HYN2ZT<ͭ̈lfvUC%KW̮t"z0x* ߖO hqYzTz>[O΄\UqWTk$(XDgQ`y3PU)9!j!i ,`.尉\k9^94sU&pW$lFԲg/ n~̆-bɊfnW^Ѩ&ص0^d_Kt1qȺ@(3[ =NBr6zsUZxHZ@IV7)c2ޟ2jY2I:YAI_1n: ;Վ#zfrsA( _pA!Q*O>mA%CZl֚-.1e}93FIgrQV*ˉھOHD_\ƣ7O)Иo^l { k㽏$jY!p6ףB3#55 Rd$oqLŐWD2p`tnʽØ9<T*7كt%z9],@_.=|N/CO{Ii:P>~ U L=n71IDOƤopiJʂ,.6ZLz kps0Mð&IM02~), ÿ:B!|OYB&xS@P?䝆jϥm%ɴ)":jǞo.Ώ!# ?H)&bq ^Coq8o"~qTR|H ҝW$Itw@I4|^Q"UFs/(ڸ:cet;duf:K,opR1ZjjlOo8o(Wi |*fk)$sVHk-Ix5IDY4%TV‹%ɭXUDzo%%P 7I,O(ױYq5}I /i~9Q7uoQ=kXQ}x m)w_85d_>Jq,` pV{P|³-YƱ^Ҿ.-^~_5NoX\. nf dt|L8 %#7N,yW<ygzG0~h4Z^B!?qMODdϦ/!4y:>/4O_ W'De(<DD-+v`mvh"D{T)@u{`I_FaS݂E\Ȍ,JBYl^"vB!iRj^>;<= ]cq1kYW[ Y\Iq1ӭ뽶G>ӹuvol3@Z;M+K"1кD5v+kAL䆣膥ox4Kb\v/$匦)V]Pmzp`w3՚)8hL㼳 fD6ߟ{6΢^Ytš~.ګ]4MǢicYg+vG{r8{8#e֪T> oֆ3sxPQW#${9ϡ\$\ArUBO}t]{vh3ͅ5 ]زj LgM Cy:=ʑѤ9l}}쉏Qd."Cc|v,nnK\k4=fFY*h{nQɒ乯ќG1:.0,Fϒvv8'CHi囅nkb <~(%e%"^]_t|MЫ>G|Z?eطPA!ڥ܄Cw|-SWgviIZ?!W\\IZy{YA4{z/$6+5J0ݯRilWp,l/:w{CxLgI8$UsvPjc_0ݶb[uFu>2?PK!|R :PK"LMETA-INF/manifest.xmlSn +" %aҾF S5?mSfcg?V'xl+aew)4=$*sirDU2Ir$i`uv$˙-lnSzc.qu:(:L=;茪i 25ZQi',/u!p4:1FRxLb*M@T|HOiٸ{388m,GssASIφ%:1/T&A0N :iGwPKH PK"L.++mimetypePK"L -Qmeta.xmlPK"L1q $ Nsettings.xmlPK"L content.xmlPK"L Configurations2/statusbar/PK"L Configurations2/toolpanel/PK"LConfigurations2/menubar/PK"LDConfigurations2/progressbar/PK"L~Configurations2/popupmenu/PK"L'Configurations2/accelerator/current.xmlPK"L Configurations2/images/Bitmaps/PK"LJConfigurations2/floater/PK"LConfigurations2/toolbar/PK"L0$ɐThumbnails/thumbnail.pngPK"L!|R : |styles.xmlPK"LH TMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/pre_fader_off.odg000066400000000000000000000203621404105311000211470ustar00rootroot00000000000000PKmJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKmJzcThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE  #",,9+++!.<333???%3C+IS g8 L(3Ia'IziV͏A`9YTW9UtPJPH8IENDB`PKmJmeta.xmlQ + WdtGn҇۴IgM` p*Sݝl=%85u"ZGB1ͥ:i9܂C%k5B t:*@gՕ:Fu+,KziTKk)}G=9Be3مk;S{3(j1:X# gvLxk]Fji|Sn,ki/G=FhLFַ:ltzJ2#&p^_{!3C+W/Rj ޿mvy r9? 3g CQ߇~c!4&QL2o8E]I1& E$Bmq836 .B_K}?.oee|J jmGy0⛿89\:cav: Xp{xPK͹-PKmJConfigurations2/menubar/PKmJConfigurations2/floater/PKmJConfigurations2/toolbar/PKmJConfigurations2/progressbar/PKmJ'Configurations2/accelerator/current.xmlPKPKmJConfigurations2/statusbar/PKmJConfigurations2/toolpanel/PKmJConfigurations2/popupmenu/PKmJConfigurations2/images/Bitmaps/PKmJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKmJ content.xmlZێ6}WJQ$)hY׷@S $-Ҿq%Jf" IRSU6$/!g3Cc/46 ʲE4^E O S潷YM}XMU)]ÑS<YSIYzhs@ Q!_"eѡ9s7oIL# eDJ^lG=::\C/NreUF+&)LI+h$3H&hCݨNs LR UpBPx\ֈCa@ J6o`z+a "EIRezK'a ,vto:uHYgl%@_wo%CCQ+W2ZϾ" A Iˢ>Vx/k Nz3PD*( L՘ZQL20:R59 TGӟL⦀_ok}f%_%'!?bZ/~zzCuy^Nס(Z2U9O~o-zre_r9VjqA5c&Jv5orH(KJ}$hKCi C"HmSACdlKцƌ$Vf]xJD@;-W hcbTC81$k!io7vp!)Q(lV+ȋNH#6OiVB?*xŭq.H\vG^KpˤT)YB"9$X2}ޣС|? [ˈ&=,eSG?]~uHvèBvڜqӼnÀ3=Svwz[뇲Ǹ\TQ*2JU&qݢu:v9e HpN'ҭKm$x~08'E9pU廮Q;TO{}bRԙ;%C !~,%5^ݓejkhyxXr,W@h s0Pw8=7/||kuqD%8 uMKYHA?n` e4[n]ߧZUzU9|Z"#^BGsNm+8 =]FiNw:|$^{`k=ݱLVVieYFm=ȁ=.j:סm"М!ԚuмS=2"D7T"G Tl՝@IS<CtjT yqzU=7*zfAdr΃m$‘7Rd Sc*|L_9&4;ur!5wzsޫFsjR_,=odsMvVol;@ i?Hi\PKՅ"PKmJ settings.xmlZ[s8~_tgbm&!$4LI}%clBmyŖё΅\|z 3HP4a; ebGS YuAZ!Lxb)HSuABPu1V_]OO^8O ouTlv8;>D9k~v94 щW(l+b|D|m',?VO*d|W.;`#`0vƪk?՞n֊yͥ?,ݠyg4GVc3gW8+S- d|yɄ酿 S=Lӳ0YGգ7&L񵳓WBUt f1&YTZ/'j Y2U9GcH&zW hYൌKn0ܑ=& bf H9Kr> j;t_+I&sԸ,dh#'ve2jr6\(ݐO\r5>6i;RO@ߖ u-ma9nJ,=8@*()xn͕ԂWE]u1ri/(\&!hO. 8 p'_c Hڂ?O&ݿO5H\j;&&VšmƗΔ͒h9Ld/9,{/㛛utU1ZkaܒDb`$!"#9ji:ֳ(лdbDiδ+C%fC |5%p#~=|>6>ľҦ"*ScIL%چm &TIV#"jw+ӂZ`LbN=jG{vMY̑-yb7D߽i; HR~,oMuQl]|dO.*UM$v`J;L ~;0J! Nz Es[L[; {-ޯr ;X-Kx4, M!,g 6&7HMqt=&u6$ &`#s7lzYŤ_cR=e١b @y϶B.>`m@)g1Lje"c80[bX[byPKlU|(!PKmJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKmJ.++mimetypePKmJzcQThumbnails/thumbnail.pngPKmJ͹-umeta.xmlPKmJpConfigurations2/menubar/PKmJConfigurations2/floater/PKmJConfigurations2/toolbar/PKmJConfigurations2/progressbar/PKmJ'LConfigurations2/accelerator/current.xmlPKmJConfigurations2/statusbar/PKmJConfigurations2/toolpanel/PKmJConfigurations2/popupmenu/PKmJKConfigurations2/images/Bitmaps/PKmJ]b66 styles.xmlPKmJՅ" content.xmlPKmJlU|(! Osettings.xmlPKmJ(NMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/pre_fader_on.odg000066400000000000000000000217631404105311000210170ustar00rootroot00000000000000PK%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK%nJ\BThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   $#.%1,9,< &&&,,,!.<555>>>$2B*`Np?0g*@{)I<8oK؀Q||P{[Z!<.iiZi/EZ/e[\mX%1\|yp%ҞE*uwP`&t gH{aN ,~^heph<7`CCeL.L$ڋujXbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PK%nJ content.xmlZm6_!Cdֻ@{臻MQ4C߸e3DE7C-[NIPl][!7w41(ϖ;rLf!XZ'kf~w㘅tpLZ!$|`R47"[pRb .xNjk/\BJYt'n˖< Y)֑ ۡƨ Nc>xW$ViN$%,4R n?beVp((iBqvG]TPWm*Hr\T`mZ]7k"Rn O?mS"=oA~?H΅-W僗Yj*]'gM{{V}+BSϪ$ @ϵAâ&BG=]"wᚦ.+[,+$)R $-T@ݣ#0z=>͹MEfeSJkՕǷ΀LGFߛt>1TTD)ଁب pUS[cɢ2.$lAV?aȪk#I\͒NùȰayЬ5Oa&QCXaQT>2 K>j&ۺ)y17VLBjE4Lۛ>5q/{NxG2` ("*l+y^4ZC8Nd-#Le= †_yi: Bs0$(H?szOݏƯtIuQ{+Ghqݢ~n3Y/r\slG+A5 9x4QVi55""2q++BB2Z(Rڲ;-g05uQ g3 7X׀wZ#Y$a+ BxU|DA-ePiVo5e5bL1_,kB?!}z9v#xRb{uJXeN('EYZυ{SV8:o5h(LF,I>1(؈`ifoyU_f_F۪`1$Ҭ a7*$g-r$d׾l_aQ>S.:w:tL\;81.`l ܮ_Uw1|y3۝9/_hW;ğ7Hb8ot퀳fˆaY XNO}lvj=0/GSzF7솲kh߭PfKZlF^lh[&`?QkgA)fkjJy{sOF-Yo zEK(p[\n g.|g.|/ͅ Nrˁ^?\ jz;iAC?qN.|2qmppmln{k; .8R/jnpa[. 8L|}_J'Q2gk1ԩӔ v/e6$d ] HBM(Bn8&]G AIBp;͂ r[tG]{yu] 5pTKp X,_ӱsem/qbDeۻxqea٬L]QY[7ƹox5uq&7&w{hGi,_)6Q:3g(W!%y]ۇձd]+uf?T?PK*(*PK%nJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@ME޼I>b4hjFD/w&ź(..*uM$xCi0%mfr= nX'=̄â߹-& ̉~ٽL^9 T >w[ :V0[epY@&cRmBZl07(Mqt=&M6$:O01v\[_z_v(SNe&S '^.p?tKwWŸ1@1,,shPKY(!PK%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK%nJ.++mimetypePK%nJ\BQThumbnails/thumbnail.pngPK%nJ1G.(meta.xmlPK%nJ$Configurations2/floater/PK%nJZConfigurations2/menubar/PK%nJConfigurations2/statusbar/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ'=Configurations2/accelerator/current.xmlPK%nJConfigurations2/toolpanel/PK%nJConfigurations2/progressbar/PK%nJConfigurations2/toolbar/PK%nJ]b66 <styles.xmlPK%nJ*(* Econtent.xmlPK%nJY(! Osettings.xmlPK%nJ(OMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/rec_arm_off.odg000066400000000000000000000204011404105311000206220ustar00rootroot00000000000000PK%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK%nJw  Thumbnails/thumbnail.pngPNG  IHDR<< ")@fPLTE "+3=---;;;CKS[`DDDJJJbbbpppi9bIDATx]w '@ @b5—ֶ{E^=y`o/z6y9]?/=RZI!(@X44M-) n&GNcП?6]DGvpx /{sd ~y/= Kv:\>T1$‡dN7Ϊ&Bc&|YVQ5o-:'5/oé0.m:+ pZj-"l+d WdOO1bSZ+8&H w/%LDVMQijMQ\gNY9Pe^ÿ\6d]VٔhA0ShqdzwLnT^ooq=3B;d[5 ZJ_Uo0T)g6TyUON!8x/ѺI[3gwrU*E>^o[\o{NS6<-ã0#B !$0Kf3gJpCQrFR!$dmrؖ`FW =ŸV3.6of4> %Z$F| >WNÓT}>hZ |ɚ'bpvL$¾>tǀ`|p?PKg.PK%nJConfigurations2/floater/PK%nJConfigurations2/menubar/PK%nJConfigurations2/statusbar/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ'Configurations2/accelerator/current.xmlPKPK%nJConfigurations2/toolpanel/PK%nJConfigurations2/progressbar/PK%nJConfigurations2/toolbar/PK%nJ styles.xml[_s6Oa}Ic;m::w$8 hYx> JLr:C._t%?9 }呈y7rt"Ix劔j\_|!hEN3V.T˭^̈lfvWc#KW̮t,f0x" ߗ)IDVPwOy_+U,`ٜmfgBUQWT2\qbe097cy]*[29ޮ_ȕjwݭ`T3uYЀoawOG[ NBs>zsUZxHV@IV6)ct8e ղd:/l9 ;Վ#frsAI OA!WQ*>}$GMآ3[)ϙs(pLј;$+ !@Dp;Y,go> FD_\='~B#Fbka<R6{Os0Hg{@ Qg}35j|)2w8 "HwTrx`dn5}cPܖe7؀B)5x+zow[U) `((qs3y'9īI?R ~Jޱ7l1/neӌ^U_zz p4._],CSDhC02F+q;BPW5,Am|j3bc_=x7 Yh!N`,%\Oϭ,UZWv5|[?#CJOb~qN OӆjzD"TD* ϭRT/YJ0Z)Q $3aXiZu#U|WYgd? }A/c,,. VU%:Y ;ڵgmU25N,6D/^#d:J %c@ZKyX V \Dp* [y ܄hM[jE,R,5.VmzmkrC[ݟN,؍q wLJK0^`wChcK(, 5VlƺhҰq=׍Ñ)B^ X cU*l4 AU&sT!,RXR(m.Aڡ XXWI<1AGN%n{Hnr!-*5dY)S!8y4$q8gLs%PcR;s},=#pЧY2(ҙ!4;B/ | E"tq2f/W˓!4y<= O+Q;b Xlm5UJw)PaX3#o:=0㺁`XTʐ;2i;PwS(DPŦsHZuZIsgRbg3+]wykM.uql4];ݦk;_LALu{c!{("iz":1кD5n+P|5a46a0>=eŚ݋)RΒw}P6=8m_bEj4X*[^㱍@, kZhwL6f&I:O gxɵK?I ,sOcCa::RF:bBg_8\j1ެ PfrᡴW #$/sʟC˥ϩ ^}5hp66Y7t6 4o#*n`˪pp>kbQ&MæW9ʾ9>EdvLߎ铟m)lu Iڃ1AnVmMZ g[`6}vaf4~+(k4!8gIURab;KCDi囅nk [!W9Wu}>v}9 ~XyuPvv韷swej&mZfOrEH~/)wqv۸K/s%>$~]orD! nWMhKnJ( s;t?a|TG0j`Jm,buڭ|Ku>2PK- 7PK%nJ content.xmlXo8QX`7I.UJ^* `d_3fwI}lx<͌]ɽ jdzhU>߬~yRd"mJZ _+ڥ*D1T*i"jZVNZD=ln;=[4}ev3IS Ius1xx zY͎qV=/uv]̄,&4Zpjqu#AeiH9TǖT! j'*'hrr ]$4G̀_b26H5yfC,TYdmIz|;P݇d9u-RJVOfv6T4h݄;}w[4<}ڌ\)oF a`C%9&!3d|)vbؓґHь_Bj{@!mֺ- =Yv ,BhgL W`:L̛#bV=rgq"ƶap\4U֞C@d"ܘ%iq\v0Rs-u#DP* 2z8dTdǣ*x.C[ɹUi MMZݶɊ^ ;^T%`P(Brk7p$Z%HfN \bCJh^Ι+M{IH@{[ 'r4EcY±'G?Zsb{qPIͩi!OL I5K{qM$~5™$PU&JKL-͢eZ*\Ud 3L%r.y}87WO΂Fӣ=̣/`!}$U!?4댰BjH}]YMk4 &=J3v("6V ׫Swz (eV< By~4 czjƋd6\vybՏ U9J-GS6(,^IoTt1Z%iWswMTFi?iz@gTO'y+ + 6,7*}M,ER)^഍O/}c ~Ȁp=%LB@mC刓p% ^$>2)`ਫ਼ Q!;H^3 Kk\Wr)l=WCdn@u _Z-].+J6d@:m N/tnV1-C}G*_Q3@ץCbG0Ewj)o.:o9 }t 0}o ܗ+ܠo}y6rQq ʧQ MLН>{\U 1E~UR6 j n<>tGv kբ=KtOx9佪C/.Q(,r\*;^2X_Xw;8c44W$<%Q[HLB xJxͣ9fi:HP{2#[30d (ryA^ ykWΉ7$Lˇ_O.޽ +%N :] LCѴZA8m}uY+^9?Z`J"JO=j{L >G͊^~E xGR‡T7.E1u^u =ڨ<696KN)FQU0$qÌ9 b0 iL7+)*GΖV{DFN! ?V|" Lqs |K7#¨6{nC Y h1\Tt,2,Erc*S 'wJgxitWŀB_-1!òo7PK; (!PK%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK%nJ.++mimetypePK%nJw  QThumbnails/thumbnail.pngPK%nJg.meta.xmlPK%nJConfigurations2/floater/PK%nJConfigurations2/menubar/PK%nJConfigurations2/statusbar/PK%nJ2Configurations2/popupmenu/PK%nJjConfigurations2/images/Bitmaps/PK%nJ'Configurations2/accelerator/current.xmlPK%nJConfigurations2/toolpanel/PK%nJ6Configurations2/progressbar/PK%nJpConfigurations2/toolbar/PK%nJ- 7 styles.xmlPK%nJ1Z content.xmlPK%nJ; (! [settings.xmlPK%nJ(]META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/rec_arm_off_dark_red.odg000066400000000000000000000203771404105311000224710ustar00rootroot00000000000000PK%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK%nJw  Thumbnails/thumbnail.pngPNG  IHDR<< ")@fPLTE "+3=---;;;CKS[`DDDJJJbbbpppi9bIDATx]w '@ @b5—ֶ{E^=y`o/z6y9]?/=RZI!(@X44M-) n&GNcП?6]DGvpx /{sd ~y/= Kv:\>T1$‡dN7Ϊ&Bc&|YVQ5o-:'5/oé0.m:+ pZj-"l+d WdOO1bSZ+8&H w/%LDVMQijMQ\gNY9Pe^ÿ\6d]VٔhA0ShqdzwLnT^ooq=3B;d[5 ZJ_Uo0T)g6TyUON!8x/ѺI&x%w!apI "|ޕEPN}˥ZnI!.$6YK긜%dIo4!va!>n?QF\S#0c{C2RqMReS|t a LV4:lDU9ci-E{o}kn=˜ѫp5z,@zkU_~*> ߴJNt$YχEv`:~rj-|ЋEbpfKc :Һއ*PK}-PK%nJConfigurations2/floater/PK%nJConfigurations2/menubar/PK%nJConfigurations2/toolbar/PK%nJConfigurations2/progressbar/PK%nJConfigurations2/statusbar/PK%nJ'Configurations2/accelerator/current.xmlPKPK%nJConfigurations2/toolpanel/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ styles.xml[_s6Oa}Ic;m::w$8 hYx> JLr:C._t%?9 }呈y7rt"Ix劔j\_|!hEN3V.T˭^̈lfvWc#KW̮t,f0x" ߗ)IDVPwOy_+U,`ٜmfgBUQWT2\qbe097cy]*[29ޮ_ȕjwݭ`T3uYЀoawOG[ NBs>zsUZxHV@IV6)ct8e ղd:/l9 ;Վ#frsAI OA!WQ*>}$GMآ3[)ϙs(pLј;$+ !@Dp;Y,go> FD_\='~B#Fbka<R6{Os0Hg{@ Qg}35j|)2w8 "HwTrx`dn5}cPܖe7؀B)5x+zow[U) `((qs3y'9īI?R ~Jޱ7l1/neӌ^U_zz p4._],CSDhC02F+q;BPW5,Am|j3bc_=x7 Yh!N`,%\Oϭ,UZWv5|[?#CJOb~qN OӆjzD"TD* ϭRT/YJ0Z)Q $3aXiZu#U|WYgd? }A/c,,. VU%:Y ;ڵgmU25N,6D/^#d:J %c@ZKyX V \Dp* [y ܄hM[jE,R,5.VmzmkrC[ݟN,؍q wLJK0^`wChcK(, 5VlƺhҰq=׍Ñ)B^ X cU*l4 AU&sT!,RXR(m.Aڡ XXWI<1AGN%n{Hnr!-*5dY)S!8y4$q8gLs%PcR;s},=#pЧY2(ҙ!4;B/ | E"tq2f/W˓!4y<= O+Q;b Xlm5UJw)PaX3#o:=0㺁`XTʐ;2i;PwS(DPŦsHZuZIsgRbg3+]wykM.uql4];ݦk;_LALu{c!{("iz":1кD5n+P|5a46a0>=eŚ݋)RΒw}P6=8m_bEj4X*[^㱍@, kZhwL6f&I:O gxɵK?I ,sOcCa::RF:bBg_8\j1ެ PfrᡴW #$/sʟC˥ϩ ^}5hp66Y7t6 4o#*n`˪pp>kbQ&MæW9ʾ9>EdvLߎ铟m)lu Iڃ1AnVmMZ g[`6}vaf4~+(k4!8gIURab;KCDi囅nk [!W9Wu}>v}9 ~XyuPvv韷swej&mZfOrEH~/)wqv۸K/s%>$~]orD! nWMhKnJ( s;t?a|TG0j`Jm,buڭ|Ku>2PK- 7PK%nJ content.xmlXKo8T`o$;Imۢݦ(tKP̆ ~DS hr5p8f]ł z$ HyNr~~|]oV?򢠘d9MEja^k VY]3UY*23.H[e>:kl֚Tcآ+[oKjlT߼SwEWizŎyYov1㲌ӛj]D#E8&TҸVDRTODNN T$ ]Ci|6dvmʑ45yfC,TYmz|;P݇d5u- K*&oEs1h݆;O}[I5Ljaq^Kʻ"2P#U;G]yI`}pDkQ}Ȍ(| !-d*IHCь_ƒ.;bU.Gk]f=y~ ,bhgL %W`:L̛#bV= gik'@o=6d'F5ba0(a}r[:R@d(X.2z8d)ϋcG +x]dF> n%3Wc*v7Q0rZݶɉĽ ?Pv6KPe3\rka0pmQIjH:+Tj `$싧.Ih^Ι+M{IH@#7x Cr4EcYⱢLY?Zs\nnݽ8RsjZR" $ͧ}Z#3Ss$ۙD e$ܴYU0g./ Lp1DŠp;/G;/(cv#9=[[jztǻy,d~Q]r{wFBjP~㦻FFiZ쏁|Uf.P!քkb *Z;i2kىIgf0b~xZK9#]c~uN˷*_St/ s }~1pgێGS<|^\xMmۋ_n;Cca:\b`"F:89(OĄV;h﫷''Ynomuk6 .@R}ٗbQt$VJPSgM…r"~'y-Oz8i7PK7&PK%nJ settings.xmlZ]s8}_tgb i70!aJ6v߄}md]$+`F;;|9GWWs@HԎp}g-aܫ9ۿtJ=hEpU"t.-'DR$T^Ci͘lQrJM],Njc3h4zh(@j(b7lzȧte5zs>"6V ׫Swz (eV< By~4 czjƋd6\vybՏ U9J-GS6(,^IoTt1Z%iWswMTFi?iz@gTO'y+ + 6,7*}M,ER)^഍O/}c ~Ȁp=%LB@mC刓p% ^$>2)`ਫ਼ Q!;H^3 Kk\Wr)l=WCdn@u _Z-].+J6d@:m N/tnV1-C}G*_Q3@ץCbG0Ewj)o.:o9 }t 0}o ܗ+ܠo}y6rQq ʧQ MLН>{\U 1E~UR6 j n<>tGv kբ=KtOx9佪C/.Q(,r\*;^2X_Xw;8c44W$<%Q[HLB xJxͣ9fi:HP{2#[30d (ryA^ ykWΉ7$Lˇ_O.޽ +%N :] LCѴZA8m}uY+^9?Z`J"JO=j{L >G͊^~E xGR‡T7.E1u^u =ڨ<696KN)FQU0$qÌ9 b0 iL7+)*GΖV{DFN! ?V|" Lqs |K7#¨6{nC Y h1\Tt,2,Erc*S 'wJgxitWŀB_-1!òo7PK; (!PK%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK%nJ.++mimetypePK%nJw  QThumbnails/thumbnail.pngPK%nJ}-meta.xmlPK%nJConfigurations2/floater/PK%nJConfigurations2/menubar/PK%nJConfigurations2/toolbar/PK%nJ1Configurations2/progressbar/PK%nJkConfigurations2/statusbar/PK%nJ'Configurations2/accelerator/current.xmlPK%nJConfigurations2/toolpanel/PK%nJ2Configurations2/popupmenu/PK%nJjConfigurations2/images/Bitmaps/PK%nJ- 7 styles.xmlPK%nJ7& content.xmlPK%nJ; (! Ysettings.xmlPK%nJ([META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/rec_arm_off_default_col.odg000066400000000000000000000177441404105311000232030ustar00rootroot00000000000000PK%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK%nJy>>ggThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE &!+$0*7--- -<;;;'7H(9J1E[8Nf9Oh>Wq>XrDDDJJJbbbpppGcHdNnOnRsTvZ^cmnq΀17IDATxr0FQiD H}ES+B:#+|N,j"\'18zI43EN"ډ,` `ѥ7Kܓ/ X2gKُ}8S0gx߿\1 -VlqbΔg [AAw _/t1-6B"jR"N!oLPcm=2^۸=ߟ7.!&#d+OIO?٦U{?I$B7È3Nfng{mUdd~+~6mX4[3f`$EhCnIdaaNc xLh×X鞈?=~io¿_ pԓnIENDB`PK%nJmeta.xmlK ;[0r|߮,蟂9Q>t 8:\:ɢC??PK.PK%nJConfigurations2/menubar/PK%nJConfigurations2/floater/PK%nJConfigurations2/statusbar/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ'Configurations2/accelerator/current.xmlPKPK%nJConfigurations2/toolpanel/PK%nJConfigurations2/progressbar/PK%nJConfigurations2/toolbar/PK%nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PK%nJ content.xmlXK6W WOuMC٤(zR,%$hʶ6s24p8fG4".8 kE{t]~w/ʒkDDc7FvU \S I*ѹ[h86tg[?O_فCBTc楘j^KlQ;ΚE2FIn/ $KLe7!(;Y7BZ`Sn;[(=5,ܜO!;au+WY5mӉpA%&poG{?0v.nKPE=Bpkq4pm $Z5n a9%C,'~=gfBHz IL#2ϿDOOZsFS4f++NFA={+d݋USJV #Xbe?M jL-*og$4 eU%^(ڲ޴ҋt~E8T\hߤ/lp1S/Tp;Ϯf8D;/agE JRv`b%欂fu +"9jv({jRbVU+%kxizquyk}tR V]Ύ{;t̮ݩ~Vec)O;Ãpq`OO:v~VOx޿tewAbikl߳.:"B+We%^sw. PHH\yr jcxoGM[<W`Գ*omy>eH н|oۛޏºh]ݳ6SJbD>kQ3WAp_؂pN1=ETBlć@Cјj\S 9]&vc2拾jv9\.Vn-uGO/~-;-C}G*_Q3෥oC`bxS+}SoF{|sYӁx;TιG܌+ ?(p_&p˱SOcxQטF24 Aw J8<͈ r/pujdl@rZn:>>E3TˑV{:'^&KDqf9td/9,/㇛ue1kfܒDb`$r޳ZDs:uZgdVbD4g:QDR!(бLJANG]תO#~\~kaRb_*]q!%ة @:]i&TIVؑ^!9?*-gSZҸp >֛7A*8mo XGĆ1 T7.E1u%Oz1 .Qd,Moh7 Ge+HpC0$:a&Uo1a n4&N5flpajoX#i7ľ !,g,C8 ]]#JԶtX}ϚO0u %wlz勊N82KQf@2BG>`g]ʥ3< tS%+O;븥#c80{bX[;byPKC"p(!PK%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK%nJ.++mimetypePK%nJy>>ggQThumbnails/thumbnail.pngPK%nJ.meta.xmlPK%nJConfigurations2/menubar/PK%nJConfigurations2/floater/PK%nJTConfigurations2/statusbar/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ'Configurations2/accelerator/current.xmlPK%nJXConfigurations2/toolpanel/PK%nJConfigurations2/progressbar/PK%nJConfigurations2/toolbar/PK%nJ]b66 styles.xmlPK%nJ=8M content.xmlPK%nJC"p(! >settings.xmlPK%nJ(@META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/rec_arm_on.odg000066400000000000000000000207231404105311000204730ustar00rootroot00000000000000PK%nJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK%nJThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE #+34<? ---;;;CC LK TTZ_ lj elur {z sDDDJJJbbbppp    !!$$ $$((''((¬5IDATxmS@MފV*E&hhiL2r {y9%`;gvrw࿇r^-4$dzOeJ;꾫}G<`jzCcz C3HCMQǭD2zb'*fiz~e"d1qF!Fܶmr \܇ F1%6)S iX(k:f7ԲoKZp&MXK /5gei g$ǧp&'뤳LsX3}5- K61۪\s3Y5L^/Ij<$ش -R˸EKͲuüIK7Q1ᚍhHi\fE;y|n(kƖ䘭X0ri i&mL=;Z8;s3'CVtc. wbcG[+9?fbϹ^ʧQzhu~-GQH'!ՇLLۈzZ'eIr}T6ƚ D'/Xx<,+Մg۩xn #1*so66Di5scл}cU[w%ϙ1UjG$p7骊TżE3}}Uuj <6l}az0oG5wXh꧶;,~*Vg0o/4?Uj@QIENDB`PK%nJmeta.xml͎0} Dg@#uQu1U+5#uۓfH߾Ƅ:Qs|}]>&x%w!( yڅ6|ޕEPN}˥ZnI!.$6YK긜%dIo4!vaOE2z^7bb̎ 5HƝ7KͲRzM cR8gڴqd#yUOk){ˌ'XsQ\׶^އ1mm/c8)*P}*F8P|c&$N@(NxSQtIs bS p&W΄u b{>ao7ZIӆ*/|;Փ8jnχ쐥8tZBZ ߊq048VpKª[PKo(.PK%nJConfigurations2/floater/PK%nJConfigurations2/menubar/PK%nJConfigurations2/toolbar/PK%nJConfigurations2/progressbar/PK%nJConfigurations2/statusbar/PK%nJ'Configurations2/accelerator/current.xmlPKPK%nJConfigurations2/toolpanel/PK%nJConfigurations2/popupmenu/PK%nJConfigurations2/images/Bitmaps/PK%nJ styles.xml[_s۸Rcܥsir DeKc_?Iw%Rb99C.|wg;&+.`|#V":?໛?icHD\PRیU#.!^,VZ4gB Q -|^ʌɆkf_Z{5Ty[t9|eK'n #/`ꋧb}TX%U|OR" 7fz!*PSㆯe8dŪp|1o*udr04Tу]-%E6/`[g*dL_6jݳW-??JCBT`3 //hTEZIBqoo$WLzQfqȻ@qݡ7v<l58 -`U%ke(Y)jI']XeҤʳTǺI LCH䎳7A48=GЩ!ޣ(D&|5v\5GY*"1`d%I4b ne37CP,s1s!H^^@rIVUMUK4'"|w7O)IXU7oLnG~X!pi)`ԚɊ`59-Z%W1;*pj0e:H7d}c PVGXB)5x'_تΨ셨Olx}Aa &Ȟ4#dLm6 %U-is^*t;W 82ot , C#ӒQT\f(bOiB&xSIMx< A`&hqscד:kqx3)!28T&8(WtfmHy5דyC:HbSQkg!>*zRZX JrM[uZ=7K< ,%pyBPA\$ eWy0<<ӳ"KiV,8FTQVf5hׁu3ERɚKW'R |+b(Ub?sxpncVEQjx6\65_!]۟V, M/0x! QFI'x1ACz\ŀ _\aƂHtm FBOU|[OK`;@̠*%*B)6hK6` jQi+"$ bܷhDgU t$U;ϖAf!8y4$u87sZ&(^¾Rd;K},=#pЧY2(ș!4=Bc/z] E"lM_$>χ WgChB<=+Q{d[} 6U-!젫 ڣJi5539ALu_ !sfQbTvIRԫ5}1C&-xz:wkV@̘+,aۤ6v|6@9]>Qg5CtbP"ȺiCc:\-v+X/|3a57a1â==)2w]`S.=xm_bj4sX*YA㱍eO, kZGhwL6Ѧ&I:O jwڥ9ç1!0L)UV,/΅?A ƛL39FDFI?KlS<{5yVa =mėrm^ٵ'i._'TU*{0đgM?M"s}csx혜'?R{/ڃ4ڃ!AnVo\{Z c?[`6}zaf4y+ -IpN ѳ0qȒӡ{B75qA?ќѪ_"^ﳔ]_t|K+[ݣIW2~LGp8 .6 :x LnlޟѦ%m~(Bݯ7rϊ~Mʃ$i1 &.iq%oU{`ػ]Ji!p_,/-ͧCxLgGȎpUsvPޗk]{c{XݿvPKp԰5 7PK%nJ content.xmlXo6~_!dIv&j[RM;{)P"ARѿ~GR)Jm]׻"2LgI2mt^tˋb7uyֵʜv68RTe54θ ug̮$Jds 5;EW`:h;`!y Y(2\k-8nbe777',*1a,t"M`z$rrjF'*$QbNs )'kS3 ReO"m+#{߃>Ե v*,M9} w${g[I5? Lj>:41 "1$Bc*uI`}pDkQ}Ȍ(| !-d*IHCь_ƒ.u@!u[vRY(A36l_<1oi[L,ȟIl0};,Z9 b,xOಝǞuY7G"CryÙ'4wHy^{LJbȇ֮]u᪻׸>^P&*&QN0S[7zqM° P ]H/ß `࣒ԐhuW IjO \b3#_Wg|<̄^iRJBFT?2w\sol)3E+Gfr>{E/G^jNMKybZJ$wb4/3235G2;I$)Q6JKD-M+%K\ sB"a`†|"Vx~97>y}A) bC{}2Ot8 (ZsIqP#h }k4->* c(s,FkB51ڂg{i2s[kŇl2\yz1?Rbn\I0OTDý>:1Ezp$p2^_,^穕g+Z'1F~RLq*y a Ul/a?uk"6V ׫Swz (eV< By~4 czjƋd6\vybՏ U9J-GS6(,^IoTt1Z%iWswMTFi?iz@gTO'y+ + 6,7*}M,ER)^഍O/}c ~Ȁp=%LB@mC刓p% ^$>2)`ਫ਼ Q!;H^3 Kk\Wr)l=WCdn@u _Z-].+J6d@:m N/tnV1-C}G*_Q3@ץCbG0Ewj)o.:o9 }t 0}o ܗ+ܠo}y6rQq ʧQ MLН>{\U 1E~UR6 j n<>tGv kբ=KtOx9佪C/.Q(,r\*;^2X_Xw;8c44W$<%Q[HLB xJxͣ9fi:HP{2#[30d (ryA^ ykWΉ7$Lˇ_O.޽ +%N :] LCѴZA8m}uY+^9?Z`J"JO=j{L >G͊^~E xGR‡T7.E1u^u =ڨ<696KN)FQU0$qÌ9 b0 iL7+)*GΖV{DFN! ?V|" Lqs |K7#¨6{nC Y h1\Tt,2,Erc*S 'wJgxitWŀB_-1!òo7PK; (!PK%nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK%nJ.++mimetypePK%nJQThumbnails/thumbnail.pngPK%nJo(.meta.xmlPK%nJConfigurations2/floater/PK%nJConfigurations2/menubar/PK%nJConfigurations2/toolbar/PK%nJ,Configurations2/progressbar/PK%nJfConfigurations2/statusbar/PK%nJ'Configurations2/accelerator/current.xmlPK%nJConfigurations2/toolpanel/PK%nJ-Configurations2/popupmenu/PK%nJeConfigurations2/images/Bitmaps/PK%nJp԰5 7 styles.xmlPK%nJ)Qif content.xmlPK%nJ; (! -settings.xmlPK%nJ(/META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/rewind.odg000066400000000000000000000176611404105311000176660ustar00rootroot00000000000000PKY!L.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKY!Lmeta.xml͎0} $`#uQu1U+5#uۓfH߾BJ:Q w9.Om cV%PLs%x} 3X+ˋdrV(Ձ*KFQ][i[acTwB-d4~sph#myui&3(1:X# vLxo]Gꌰ~vSS֬ki/G܀):A(BV6:ltyCsZ2#&pܺ9뵫W)j rj:bfĔ-"oCh#&1%qD$io( "BMydh)ͮK@{3ժzO8g6ok%ag(jm'y0tzFI$U6M}gOLP>!98;\ޫ:ɂi] ~xzJ@ x|xWPKK.PKY!L settings.xmlZ[s8~_tgbȥ0!aJ6vo:6R0![OGG:r9O4@R&& aة|>7;1 Ne LE18Dt /V.f.Hn@,WGœgcÛ}6Ύ3?y)EL"x?9c6e1zuEXX"Z=/G}J 5/"ر^ڭE{Y+.r5tyjŏ 3Z9H[ )O f/eu7Z PFG 5k'&ʛ{06YЕj0wF4 vvZd)vz7|%$Q @ש`Ot55G0̺tM6 kZ3hWXIɁ9&\Cbew@h(-6x}2&Lhw${ JQFC`: :gK#sk]Td2Ɲlg!@_yb>0-˗}/TzŊ 9u|PsOKHC+jz2xu[2F.6V%w1jɏo/CT*<:98^i@-x]L՗2F#9 l($)"|gtԽeA4Z%cx?̹?͹s-ҰU{2?wH/ob;U燶 _"[,1R9;I^_X7$a4W;[+yEw%ex'2k["F'Pc.g=` p:m d`,* oi4KbH\}oK{,_O?"(CJkQPQ>5( jж.mmRJݒ \=-] ͩ$ ̖4ѯЎtS~Q2<ʥrO51A:_-͈"0b\EQʱYot7Ì\3J! Nګ EuL[; |%hL^9{ T 5?+ :V 6:!i2p턵TGdZ9K>2E CWnaĄ3n"T_*xaf^tM.eiOYh0z.Q995X㓪֒*@ :~m#i&k,'̂Tgө2|H}WRE$'٢}}!s(ݺ;㷨}[4'qQKn !2P# 3ԊXe|$kZ}Ji\2J'W#L=9:Ґa4WHZH OohK>~i-d;s\&цpН^&1U- { I ]'@C㒅뭹hC@dFUKjq7Lvس0hRr!5#DT* 2XQr7͜9"ˏ-]XD>LJ/ECSvFYS-0D9&4(ju'exϠ[H_?Zpb L|T W%i`%C]$ 5H4/LpI핦?I 킏t+\wЯ )^WNR:R\OozТ /ϴh8SnBcL-L&n=-YCոRsZBzH\ci^C*$2 {JTLfT٬*-3,3Sk|ϮI""7q\L%Ay$Ίsa,P[9x6>ZB/ z"LPd#Yd0y@'3oȑq#=̟d:I$+֞+x=?m}ZmK?S74 gRgPOcCsSL%Qޤ {vQ'S/xo~G4b?k_л{[>g3, **Hd[ %/@|q_ȅW1,tukMP' nHt# PKo IPKY!LConfigurations2/toolpanel/PKY!LConfigurations2/menubar/PKY!L'Configurations2/accelerator/current.xmlPKPKY!LConfigurations2/popupmenu/PKY!LConfigurations2/statusbar/PKY!LConfigurations2/progressbar/PKY!LConfigurations2/images/Bitmaps/PKY!LConfigurations2/floater/PKY!LConfigurations2/toolbar/PKY!Lϋ^^Thumbnails/thumbnail.pngPNG  IHDR<< ")@fPLTE $$$+++333<<uN|p`ބ>.V@"^$CfI \ķđ2ϮҿFSaͻҏ׺:\^ r 7[G2$ZSGw 04֑:%S6]\V| I a??kLsc/#:!O ?R>$Ϩ~KV90Xo#C料DBϖJςe~u*)$CQ9J^vzNOlc~Ű{W4 CWuK8KQb68:in8h㋛NhF{5ԍ߆Bo&g RLwIENDB`PKY!L styles.xml[_s6Oa}Iqbbwt2smizځHPB|>OrA)SlNfl .7eܻd"'g< WW\_HSE"* J@8/xW2_Rr /DAs+pz)3'+]iEXaȒ5+H++w%R"+b;ZqJ0l6gٙpryyjjp+C).VIhy3XU)%!j!i ,`.帉\ݮF{jxMh?]W%]eQoƮbɊfnW^Ѩ&ص(ý9ȾLQcq|8z.؝1>jp#JPBHOʴIkᔁT˺I BH-oip.wAD4{ Lyh jTTybH *b nqS3CP`Lј;AV,‘diuԿ~*OILƼ~cu3Ϩ+ԯ> Re^ߑB3יj|)2w8 bHD2x`tnD?T1sxFTnKE$KrOdq?K}IJ6!=?}xFW'r!%zTo05H~~Rlx(ȋi"3fcX* 22a)ªiVkb6`M]Y$pH%U].qK_D@wS8+tLJ\6E7*q.hHWgp$i'K4{ ҏMַ?~fK1w(;d8L>%žZ^B!41~_$BNEt<}q2/4O_' :k%j\WlPu{kCBMQ J5J=kj'Q1@0b{Bȝ ΢$yV.r: ig)Q)TV]V~.Ϥ@gBWxMך\=3f-j =+6i>.&vMv:9r:}mBP<iIZz@@u.&\C}QLyce>l4kb\v/$匦=)nN mzp`#Ն)8hL fD6/{6&H5}]ׇh&h3ӏE$DŽjdڥ91>0L)UVis?q. Zoֆ'9\x(( =bd8`Nkh95=|& 9*l>x:.\yD-lY5&1<h9l||쉏Qd."cczvL,nnKQ\iǗ_ rnl2*h=e鳣5%s_9FA_99=Kz> X #yO)f|x/ORbi+7~6CBr|Mr@]_.×,+@]lgu=<\pG[^nbѦ%mk$PdJ]zIcŰs$^ &⫧+[2SB0ܮRiܶOQ8j\;t?a|T ~X5Wk61yZ?[ﵹM zsSw wPK=J7PKY!LMETA-INF/manifest.xmlSn +" %aҾF S5?mSfcg?V'xl+aew)4=$*sirDU2Ir$i`uv$˙-lnSzc.qu:(:L=;茪i 25ZQi',/u!p4:1FRxLb*M@T|HOiٸ{388m,GssASIφ%:1/T&A0N :iGwPKH PKY!L.++mimetypePKY!LK.Qmeta.xmlPKY!LWT! Psettings.xmlPKY!Lo I acontent.xmlPKY!LK Configurations2/toolpanel/PKY!L Configurations2/menubar/PKY!L' Configurations2/accelerator/current.xmlPKY!L Configurations2/popupmenu/PKY!LH Configurations2/statusbar/PKY!L Configurations2/progressbar/PKY!L Configurations2/images/Bitmaps/PKY!L Configurations2/floater/PKY!L-Configurations2/toolbar/PKY!Lϋ^^cThumbnails/thumbnail.pngPKY!L=J7 styles.xmlPKY!LH META-INF/manifest.xmlPK6emuse-4.0.0/src/svg/projects/rewind_to_start.odg000066400000000000000000000176241404105311000216040ustar00rootroot00000000000000PK!L.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK!Lmeta.xml͎0} '0RUSR3Rw1-6C5&d&Eb{c/\d HA%S m`t_qv8W3 ~] nxiKTڃPK|.PK!L settings.xmlZ[s7~3^L` MX@Vڑ%`N~eWOGG:|9GO4Վ@R&Maԭ|~9 Ae LE18DtM/V!f!Ha@5G73g͌?ϏRMZ^HF(bRLPRvraNI_Cc+ #r<R6_KL[K%k>(oBĤAWNgOjT񵳳WBU t 栫!aޣ*Q^˺D-!*F{:1XJDx w&.D#iYvD)5eBGbna,g xtɢD\*2Qlg!@[Kլ*cEɆ&>P MHC+j2xu[6elH.%cՈ^0oʙTxt2!pҀZB{ڹ%/eFp<\Q3 qS"x!gԽALZ%eUq[9w~9YL#ҰS{5pGz..~Cޫ:;M?nhVHU"%.yRǏa?YGO\ 쬈rO);]6=p9ÔUIc }sxg V.Ⱦ Ԏ"xР.!G fp#w}wﱄ6|=# p^{ wDc-`.&l#CۺJ)4wsr+$zVQ LH YF_c]YNU(ni6=,k|J5#b{| [_H)eE{IF/л̔kF)_J{qX۲ڞa8k+Em:_ Z5U$r7nySLҌUI*`^)@i#+QI* D &UԬHc Vd*klO=_ـ]LXe9,9x,VOsTfl.0 W3  Ivؒ(4ƺUHh Z "Չ9ΐ˯u1:ŀyfTfSe%Rˁ^ 4}^rZss-UvC7a3vЛ1bz8D@:!Ҏ I؈-Xf{KR=>h%%e؁E {ttNA_\(||U룥*p Z,; :`MN 1MNŀ^pV!BRu߸Da{kWUġq DP-B̨= n&~X5)JXWN<(%$2X^vr;Μ><-,,TʇϟB- tˇ֮\uk:&C;&&AF0ۦ?iksB3PUH?˟pͤLk|P NU=DMfF S(nޑ/y9N*R~'ifޑ5PN_{#OcXv^2\Go2o6_'RRn L;V.cV-cX#QXG@nFBA(IDwv[LDJ% 64ӷh%׸]|OӆKx"V^9Һ.n'9ee`;(XrA⺻T4z;x+)9H 7b 'GL4iI+;Mw۩e%;Pxz} xJ+)#Y!M$[s\J$˔$mGj*űߋǣC&>w"Z^ĚqՀQL+ב m,/PK FPPK!L'Configurations2/accelerator/current.xmlPKPK!LConfigurations2/popupmenu/PK!LConfigurations2/statusbar/PK!LConfigurations2/toolpanel/PK!LConfigurations2/menubar/PK!LConfigurations2/progressbar/PK!LConfigurations2/images/Bitmaps/PK!LConfigurations2/toolbar/PK!LConfigurations2/floater/PK!LyThumbnails/thumbnail.pngPNG  IHDR<< ")@ZPLTE """,,,:::IIITTT[[[kkkpppyyy|2 IDATx zMټυpFZG6@- _| ?qk$;*8s2< X}2) dm eUg] [e^1}uӍĺ!-{9K>Us5/95;c=6 2 </ v+sIENDB`PK!L styles.xml[_s6Oa}Iqbbwt2smizځHPB|>OrA)SlNfl .7eܻd"'g< WW\_HSE"* J@8/xW2_Rr /DAs+pz)3'+]iEXaȒ5+H++w%R"+b;ZqJ0l6gٙpryyjjp+C).VIhy3XU)%!j!i ,`.帉\ݮF{jxMh?]W%]eQoƮbɊfnW^Ѩ&ص(ý9ȾLQcq|8z.؝1>jp#JPBHOʴIkᔁT˺I BH-oip.wAD4{ Lyh jTTybH *b nqS3CP`Lј;AV,‘diuԿ~*OILƼ~cu3Ϩ+ԯ> Re^ߑB3יj|)2w8 bHD2x`tnD?T1sxFTnKE$KrOdq?K}IJ6!=?}xFW'r!%zTo05H~~Rlx(ȋi"3fcX* 22a)ªiVkb6`M]Y$pH%U].qK_D@wS8+tLJ\6E7*q.hHWgp$i'K4{ ҏMַ?~fK1w(;d8L>%žZ^B!41~_$BNEt<}q2/4O_' :k%j\WlPu{kCBMQ J5J=kj'Q1@0b{Bȝ ΢$yV.r: ig)Q)TV]V~.Ϥ@gBWxMך\=3f-j =+6i>.&vMv:9r:}mBP<iIZz@@u.&\C}QLyce>l4kb\v/$匦=)nN mzp`#Ն)8hL fD6/{6&H5}]ׇh&h3ӏE$DŽjdڥ91>0L)UVis?q. Zoֆ'9\x(( =bd8`Nkh95=|& 9*l>x:.\yD-lY5&1<h9l||쉏Qd."cczvL,nnKQ\iǗ_ rnl2*h=e鳣5%s_9FA_99=Kz> X #yO)f|x/ORbi+7~6CBr|Mr@]_.×,+@]lgu=<\pG[^nbѦ%mk$PdJ]zIcŰs$^ &⫧+[2SB0ܮRiܶOQ8j\;t?a|T ~X5Wk61yZ?[ﵹM zsSw wPK=J7PK!LMETA-INF/manifest.xmlSn +" %aҾF S5?mSfcg?V'xl+aew)4=$*sirDU2Ir$i`uv$˙-lnSzc.qu:(:L=;茪i 25ZQi',/u!p4:1FRxLb*M@T|HOiٸ{388m,GssASIφ%:1/T&A0N :iGwPKH PK!L.++mimetypePK!L|.Qmeta.xmlPK!L! Psettings.xmlPK!L FP acontent.xmlPK!L' Configurations2/accelerator/current.xmlPK!L7 Configurations2/popupmenu/PK!Lo Configurations2/statusbar/PK!L Configurations2/toolpanel/PK!L Configurations2/menubar/PK!LConfigurations2/progressbar/PK!LOConfigurations2/images/Bitmaps/PK!LConfigurations2/toolbar/PK!LConfigurations2/floater/PK!LyThumbnails/thumbnail.pngPK!L=J7 styles.xmlPK!LH META-INF/manifest.xmlPK6Hmuse-4.0.0/src/svg/projects/routing_input.odg000066400000000000000000000212301404105311000212670ustar00rootroot00000000000000PKoJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKoJPThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   %(31%%%222===&E!K*M#Q %X#.U!2o,0p,;u.@u.@@@HHHVVV\\\aaattt|||<6>9?;@6@9K7A;NITD[DTKYJ[NZO[P[T`O`OjNlO`PcXkXpPsSbZmese{e_aee9}IDATxS0'/NGsE tjG/E%ΒiӦo7v'\Q1[ n QU؇u&=tK+jhUmbkV&nҪ0Śyfv//Fz̆\JQ:75Q#}ox`xG^aVmmN&o4b&5,d]]T0*Ms{F~C>|IPY$̪r䆅z.>Ie.!'HRC oaZl8̩[+k ٱ2mf{ I9> ]Xk励d~U҉u(fbc3`&ٱ1?0ZѹF#Tֱx?tϑEgКTT^'VA˒*m v8% GhmbNuxXf)~NI})f"[x˯RfuӔ#x.Ok8\Ѫ 3gn81b%uD˱ߑLScglx_|+1(ZslÅJ{sy4w $}lql"Qvx^~2u:oje +f\!}gh@P X<_zb|;ܻ)(sIENDB`PKoJmeta.xml͎0} Dg !#UUTT."'u 6͐} )t*K߹|:pLV4:lDU9ci-E{o}kn=˜ыp5mqe j9? g!0DYf{&!8qR@QZQt2!I8"KIglrLX"k_ﳏI)_/> 7U/|;Փ8j_11~x?v,CON-@zѰ_lV֌iWDZ^ZT%XuPKu>/PKoJConfigurations2/floater/PKoJConfigurations2/menubar/PKoJConfigurations2/statusbar/PKoJConfigurations2/popupmenu/PKoJConfigurations2/images/Bitmaps/PKoJ'Configurations2/accelerator/current.xmlPKPKoJConfigurations2/toolpanel/PKoJConfigurations2/progressbar/PKoJConfigurations2/toolbar/PKoJ styles.xml[_s۸}Iƾs7מ9 DeK߯A)Sl|]`vI>M;* Ƴ+wtBl}ӏޥ8f!]F<,SIgRRdKN V,3b)%if6R-GdC--*-Yb#AC0c>TH{!Os"ٞ ˾\)oۋ䂋?Z, 5_^DqEObޔJ2T?UtE`h$ Z ~9l"[_wu91sU&pWDlJg/ H-Bfjn[s^:ؕ e &ã!Iqv|#8 x;F߹#,AևD{ yh YAHs*HĖJ#?k֡hb2EmR4{i KKb"ӯ><<!WX{mNÉz Q+_ܒ  Ra]"9/ǧ]55{k$kqL`7 ݐ%+L]R+$Mӏ,sjKu>W."z!8^~_PTf|dO1WG=i~~AFVyBv^1 W9Wcpŀc(pn"A S %)#+FAGKB5> j`E;:9ne -W2h'?~:m̎tlOWьv> /g>??KBǓsJ*lCou_=83IܾXpp36-1)r69J]lakxg/ ɠ )ZC1g`$)Ƌ8--L\QQ*'ZK)^ &AK}6qIψ^Švr!-?ުL*Mq8+\I6s?YX>dRy Y98oO!w3u,=pP1Ԁ 7]IfgCh|1}φM|<}y6o4O/Έӣl~_"WWxm,P`]ET%D5h*RcuR jyQS  ϋ)IYXWxF!m-K*֕ERˍzU}1{C&xz:w{Z˪njem\Mx`q &tLWP?{PNgOٽ1͐#oPDLD{?Zµ1:n{3\3\s]] aQF|CĔ q{.bRB6/LCe{삙F,YA㩍ѬW:zauH.&DSI9ᬲ+rg{=r8{};~׷y/ڃ8ڃ!AWo\zZ5c-Л>90PcqpN ѫ01Oӡ{B751 A>є/YJ/M:zO֤tHedUݣqW3qDyM=렐m?o(pTU33Zp` yCfcY7Q~$y8A=x+~Y BU{߻]!%T.kp_,4Sz(YCQ#UĪ};4(ޗk&W4xagm~O2PK#dk 9PKoJ content.xmlXmo6_!Űf;':tE þDl%Q(HJ4e[n+=x/Tnwyl(+n0]1KhZ}xf7,MiL ŬvQ-tּX0\jQT /XI ^>LW`-N %Kl6;x;,T]A zd.祻\xvm+/瞒c+k)T{$#r Fbs"P$6g |r%'@]ٜN~mVk s|p)p7UdxDͱX{z<>CNbNnjgS%A27-"|˩ ܂1bq ldʛ"z\%x|x$0F.r >$-.Tؓ2z#>8)Cv M"[OP0'A3AJ/tTEYpĘvz\|efk"Hv%Tph2T6Rdm2.U+Hd(XV.,vw|7L,RV\U8{Rȇd]uB>Vyf! JqLPB⬺,;]ڽt( ;^ %`P(]Zъhu J*b̩>oi2dD>10L c%/-xS:yz':ü7D}-^^_Q4LN)=ZԳ[RfƇ^JBsJ]rMv\~Ir&'n ႒JE}&hKyG~4s,cp}#͆L89l#`4˔# ۭEԮ5ܼ~ Q |h8ɶpFW>Օ>XNa%e[&ti9-*x0IuX47%N$` ~vNT29pcԴҥ?wޝ VGޙ2lv5OLcqxY7/M%*e=icy=h̷KDd *D6יp1$Q%9U Ԥ[ ߵ g!bdO0YUro3#w&|Lh~3_9 )?u>KkyN2z{7~;SyڎBtܧL'lcH9g*HSD-îPKU]*a3ǖ ),|Mn4'`2Ofx0|Պ~טOFh6-ȗZ_lKې3at_]b' #ӱ) 1D8f꿝_/6B((MZ9i]A;_n3lJ~h:^_)PKmPKoJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd]$+R !tՕ'{w"@Ĥ 's Y5A%Z!Lx]b)jHS5ABP50V[]KO9uouTlv<;9F9+~v54 щ(l/b|D|m',˧h2/[ 0VDp_;vk Uh_n֚TVnиXg4Gƺg(kO f/y7#oR# 5J M]T WO7&Tӳ}K!JLPxIJ_}ìC7T*-x |"96GcoK&>zGhYൌ n0ܑ]& f nK9Kr j;t_+I&}sԸ,dh!'viR累rlsXrCdn&>QpvziDȹTϧgHM+j<}]:6 #x--}sFjf4G'7WP ~PJ\St-΂qto S{7rCz!Iv /Su~h%3e$C+Se'K8 fdZ؄'$*Xt_H hNy^Zg^V{2L"[3(yE49|ȼd,t`}ah{oZWS"7דwc#ÒWT\Dc|jL ɢd CPrЄ*I[_]qʂ|WDMneS I̵G1q6HB97o7{)ыEt./b\EαXnL zoOF)$I3wz Àqpksvv=5Wsb CnXd` B2$Lβgr$ѴK7c™4{nC cY ]2xK/8+ ylY2/Y_.1<孭>yoXErq9nJzwxQLd F|G ˾3#Cl??PKM(!PKoJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKoJ.++mimetypePKoJPQThumbnails/thumbnail.pngPKoJu>/<meta.xmlPKoJ8Configurations2/floater/PKoJnConfigurations2/menubar/PKoJConfigurations2/statusbar/PKoJConfigurations2/popupmenu/PKoJConfigurations2/images/Bitmaps/PKoJ'QConfigurations2/accelerator/current.xmlPKoJConfigurations2/toolpanel/PKoJConfigurations2/progressbar/PKoJConfigurations2/toolbar/PKoJ#dk 9 Pstyles.xmlPKoJm content.xmlPKoJM(! settings.xmlPKoJ(META-INF/manifest.xmlPK6Lmuse-4.0.0/src/svg/projects/routing_input_curved.odg000066400000000000000000000210261404105311000226420ustar00rootroot00000000000000PKmJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKmJD1||Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE    ##-%1(5+8###...!.<444===%5E*;M+=P-@S2F[5I`8Og9Oh;Rl>Wq?XsAAAIIISSS]]]AZvC^{EaeeelllrrrxxxIeLkOoPoQqUvVxY|[^`ceghjmoq΅]{JIDATxڥks0=d躺+BkguT팭PX"RѶ$I<aɹ)gXϥa:} kql {% %a?Yi WYkж0v.Ҵ70vJZ$ cGr[&ޤ߰䂣[hSޗъ4k08=s<%p6Bpi~uF~%i# {3=m.l!?}rn_ИM>94k9-#zj_9.tg0Œq%ypm#j GdRIVRCˆWba=q\9ʈ̩hhiKC9Ae3ٕk;S{3(j1:X# gvLxo]Fji|S,kiG i=Ӷ#2ݴ^RȾI}2׷eá땫W)`5mve G@9Oe᳅( ! ILI8C$479gt%xbIMm6Hglr\3VuO'~ξ]\k!jat~ٟPbPkS>ɣ_ah'ns$8FA5o]8I}}+ )W`U^PKž܎-PKmJConfigurations2/floater/PKmJConfigurations2/menubar/PKmJConfigurations2/toolbar/PKmJConfigurations2/progressbar/PKmJConfigurations2/statusbar/PKmJ'Configurations2/accelerator/current.xmlPKPKmJConfigurations2/toolpanel/PKmJConfigurations2/popupmenu/PKmJConfigurations2/images/Bitmaps/PKmJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKmJ content.xmlYێ6}WJPdкy )&AK[+Q2wHI4e[^I/]@Ҋsp83v/oY.(+`)c2r?~xw,MiL 낔Ŭt]e#rk^.T,K\UXKs5#Bnt ْ˱dq5f'oƒjS6|/r2zQaIiyw室l&hxRcplpUsJbDM&`x Oamʺ%|kQ8kήu6x<~DT[`{ B}{jW;\sZ^f1c"4]k-$|é$܂'1cqVsUʛM!׈ X${}"ށ`DK!q(h>: HZ\ѩ[dIŸ4J7%4>Z".aJA3$G`NA9b֔lt:1{KC {IB{ c q֔eġq JsM[4_PcKCIQw,P! aòji=ԩMʒt_^|S2Z>4v&ݹc3)oPcOfiޕW+ NyKD:(l=a NO£(u!**chk̩>oi2eH>_>0#L[!I56 dN 0z?#Yc>!77)q\K:G[ ds1pf搚jqq7\a>M jH&'n *$gwmhNZďµE1hup0wÌpۑPmrUlV?|4}Z`~HZ8۠ bsҔ- ZQ83eD;nl[̺ '#IV)A0 LJu:&I*Rg{ЉTvY?oƇPcJX3bV~\u8,c%[֫KqU7)%r6:n2ȡ"]MJm'V}|ÃղI6$Lwȡ4_g4q \YQ[LgZ9_q뿮T^RMDW͟38o{([-vA_(6g<(,p q\,&s? `1|>ufQMf"-X^/ΞM:++_~^ [Dy|7i~}h`+X?xw>3;%8EKZnRyU&F`qOnFʉGfGy](͏*$#xɎ6wcpk49Nh-QB OZS |߀L,[Eld] M5ͮm՛~zZ oE-߉Y5/|]툾#M"Ueݢ'sZ*GJ̜ys3rqM/zoPK8PKmJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@ME޼I>b4hjFD/w&ź(..*uM$xCi0%mfr= nX'=̄â߹-& ̉~ٽL^9 T >w[ :V0[epY@&cRmBZl07(Mqt=&M6$:O01v\[_z_v(SNe&S '^.p?tKwWŸ1@1,,shPKY(!PKmJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKmJ.++mimetypePKmJD1||QThumbnails/thumbnail.pngPKmJž܎-meta.xmlPKmJConfigurations2/floater/PKmJ3Configurations2/menubar/PKmJiConfigurations2/toolbar/PKmJConfigurations2/progressbar/PKmJConfigurations2/statusbar/PKmJ'Configurations2/accelerator/current.xmlPKmJhConfigurations2/toolpanel/PKmJConfigurations2/popupmenu/PKmJConfigurations2/images/Bitmaps/PKmJ]b66 styles.xmlPKmJ8 content.xmlPKmJY(! rsettings.xmlPKmJ(rMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/routing_input_terminal.odg000066400000000000000000000204501404105311000231650ustar00rootroot00000000000000PK0mJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK0mJ>O33Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE  $) )'2)6%%%*** -:111:::$3C*;M,=P/CW2F[3H^7Md8Nf:QjFFFIIIQQQ\\\@ZuB]yE`}kkkpppzzzFbNnOnQrStVxX|\]`ceghjmoq΅ UIDATxm[0=5ms8XmAy ؤo.?$9Go|kƞ/6ᅭe@tp~ZauFʵwr-w TL3xX ],ѓZ¿3j Fn5y|'֘ӷ0-&J,}[ ,Y Q`>B8LWL?;jW~qX3ina.@;+d08>€ 7œ ~܋ D-ývtæE&_O0enx9pWϬ|eqF![5luV–*YS\'\xw9w-5| nz|*4W5&_96= {Kd8*'>6)q=);+wOPF㠺{ƾmo'7L**FR:V-tEUW>Sv`70 xOTW'!tC͇aWwfB0IENDB`PK0mJmeta.xmlSK0W ^ RU[RSެ[mCHAU9z<ɟM cVPLsNS!// 5\WUY:]3JKUKj%FS\Kε¾>9Aeog(gW\ۙzDqE-8pe;Vnl}KRKk} iSNmY!M;xUO+%{e<]Ἶ)C]\I?1X XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PK0mJ content.xmlXKo6WEo$bqt=IGQH̍$$G}DSۢl|p43|{/roK\(=R,e?}|w,MiLV 낔Ŭ+]5/W *V%.Xx*R^鵌DC>.[JV?_Y]vn(Ya!.=eC{Aԋ Kz>HY`ۍvY-@kñU55*Q؂H8 )Z\g[b,b\Ç21"oaJB3$$vm)ٽ;za.O S/Q4ȝW Q(m'P3;[SVɃ WS¹:lolc3(eP!aòj尻3a&eIzjaBLz>P:F> f%3sc"7(1A sqwk{^( ;^.Ki5k{\1 }cZQFJ: \v1 -TW_050M ~"9.rBzrTOL2ޛ>ޓ1 Q+ג#m}<[REvs1p朚3jqq+0W& m(BD A;V8 ']UrW=6 "l.ejN6FLu>sA\ I)p܎<$ JeeІl}1ŹmV-4Eu-jN D̤TKzDՍJkolq13v>tZ^X[Ů WLI|ݠ5gMӰnuU27Ɉ[5e=~9:~c2 ȹ!mYt*]A})ey[N=[w~w6ARnԫJs??h:sȔ /E^tfO.{PK֑y PK0mJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@ME޼I>b4hjFD/w&ź(..*uM$xCi0%mfr= nX'=̄â߹-& ̉~ٽL^9 T >w[ :V0[epY@&cRmBZl07(Mqt=&M6$:O01v\[_z_v(SNe&S '^.p?tKwWŸ1@1,,shPKY(!PK0mJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK0mJ.++mimetypePK0mJ>O33QThumbnails/thumbnail.pngPK0mJ)@.meta.xmlPK0mJConfigurations2/menubar/PK0mJConfigurations2/floater/PK0mJConfigurations2/toolbar/PK0mJSConfigurations2/progressbar/PK0mJ'Configurations2/accelerator/current.xmlPK0mJConfigurations2/statusbar/PK0mJConfigurations2/toolpanel/PK0mJTConfigurations2/popupmenu/PK0mJConfigurations2/images/Bitmaps/PK0mJ]b66 styles.xmlPK0mJ֑y  content.xmlPK0mJY(! settings.xmlPK0mJ(META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/routing_input_terminal_arrow.odg000066400000000000000000000212451404105311000244020ustar00rootroot00000000000000PKqoJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKqoJ'Y  Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE    !",&1)6+9###,,, ,:444<<<&5E'7H'8I(8J,>R.AU3G]4I_5Ja:Qj>WrDDDNNNRRR[[[@YuB]zE`}bbblllttt|||GcHeJhLkPpTuVyX{[^``ceghkmnq΃q.IDATxڥr0= ^P3-EuΒ0A3I I89d[C(pus5NVV'QX au2X0j)x/ؚ10 %x X. L>P{сM^@rab.8+¬[h΃QN  %zư$!ؙ-v@q}Lo6ջ& ׫8Q'tA~b}x d 譏ừp4AC:>.05R^}K ڱFt-\u>Ako2{Mye7,h/WgA7kfIOtfW"[=5ɸU{fԺb-ԮXXj]1;lpv\jnS3 33/ag[b-yS>b}ww0F8#oc^pg7vH+u7iJtRVhQE(i4U`4uBfGDL2IOh|se%=l@s:3De=m.pm= f\9ܳʒ⭪ނ=99z P(# bNɜq|6"OC>ɜhGÊ++5ƭIn[)TuX-E=k)^5U~-7F'JE׉tGiIENDB`PKqoJmeta.xml͎0} !FZ]DlOld!} )t*K߹:<7u"ZGB1ͥ:C\??K&(׬kra#\ RetT(++-UU#,uVYB4FιW~8R8qeП(gWL)Π`!01ὡFv5MReS|tPLF:ltzJ2#*p^z!s W _߂1X XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKqoJ content.xmlYێ6}WJЇn^.yhw E߸%3D|wHJ4e[E`r83M`% lǯ,&;ah;,Tұ];)ȋ]NO+{ytv;ن2_,jcKT8b'a XtlgFqtՊ ]Q&29dk 9^#6$_*a2T;w(bصj6SJt7𼩫 "|̀1cqZ |ވׇH 2kp NS:uCʚ ,vhQIF&a07.e\'(`@h͋|X„f,IB91q6o_ؽt0G)E^p{h9R=4.ޚҦLTTŒ%mٛVN?cʶ3y\zKg)j(d8Z~cnt$=Hzx6G|hjJU'{ұ)oIpw?aK Wf'k=TH_ߢ?Ԡmx'%=DEx `w LG&s?r }q%>f8dIPN~W?b= :8j8#v ( 'ct1֠)ktYRp׾P">N+iGgʹb8UD1422~)z鏼l这'ҍd:ur^X[Į Wet]wPZ3Mլ[u\*W1Q_[ LJӵ59)cH>-z*|:B]Yrm*eyW]= L?ڃn?Tn.c%NB۟%Xܺb˳?ڧ(Z Ss?AgWY!*EH4ߛFdtȚMoFA0 of ۽ΏZ~}FԻX[o?uC2vR+o%0ctԹRx'6x8=&ާ+u}sUsѫαaaU( K݁+QYx٥52T 9h NraW[u`jHH6fDzJӍ_E: z.9kH$|'m`ЏBN|p_ȧ)jDav~cev*`ӹ3Yp6y<\EnnqPKKo%PKqoJ settings.xmlZ[s8~_tgb=LCHhnMXFHr~%Y 6!6ٙC.}t.KA*U)oμ.p8dT)qBhm=3]u͋"QLU AUuPbZuyt5!=yL<ռQ'pȯH2PD'#\LѶ(U0[X"\>g/FmRad4/"رkڬU|\/aqf?JLCh}dojy`=^ڼ_|g!1/42/^trR9ׁށNE??>=+Q=N?(VN SWζ/$*1A*Lw+cNNQ 6]4խcTYdX r « WP%Qxm$eɂ2.%#%rĄrGbwL~%C,]4bk2p9&b8+vO㌂|I~UR6 jsn .9Ήo3& S}7|A{!7 /Qu~h%3$C+So')L8~ fmZج'.*Xt韈HhNy\_g3L{4(GhyɴYhC')n'_?"ȇ/?|Osb_iSvI1Y$&M mk6FN$w}sY+ ^5޹iN-0$1FNOƽ@[p" e޼Q>8h(FD77fZ)8D|2qNpI'2NcasC2%-s- nX'̄â߹j0& }̉~޽tL^9HO%<<o|2$LN@&#Iq/CWnab™^5{C mYY2xs/8) ylY2Yhb1z<yﳏZErq{9nJzs1&n!}gG~?<PK12!PKqoJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKqoJ.++mimetypePKqoJ'Y  QThumbnails/thumbnail.pngPKqoJp /meta.xmlPKqoJConfigurations2/menubar/PKqoJConfigurations2/floater/PKqoJConfigurations2/toolbar/PKqoJ3Configurations2/progressbar/PKqoJ'mConfigurations2/accelerator/current.xmlPKqoJConfigurations2/statusbar/PKqoJConfigurations2/toolpanel/PKqoJ4Configurations2/popupmenu/PKqoJlConfigurations2/images/Bitmaps/PKqoJ]b66 styles.xmlPKqoJKo% content.xmlPKqoJ12! settings.xmlPKqoJ(META-INF/manifest.xmlPK6Ymuse-4.0.0/src/svg/projects/routing_input_triangle.odg000066400000000000000000000200231404105311000231530ustar00rootroot00000000000000PKboJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKboJ;Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   *!,"""///777999%4D):K3G]3H^;SmVVVXXXA[wB\xuuuGcLjOn[]fghmnqΗp;3+IDATxio@q4RE葬=dJ {ZCe7q)7S @O&1(9̲>.(²e iI/\z5wPY7S~rWPN-t9BS*F2XշΪrN[ yc $7{HD8)bnQVQlr$K#d,ʙ׾VS^|^1k)MnM~ٟܩQ0PGI|y,CON-Dq?EB|[qr~X3N~_ nzzi]J>?PK&d/PKboJConfigurations2/menubar/PKboJConfigurations2/floater/PKboJConfigurations2/statusbar/PKboJConfigurations2/popupmenu/PKboJConfigurations2/images/Bitmaps/PKboJ'Configurations2/accelerator/current.xmlPKPKboJConfigurations2/toolpanel/PKboJConfigurations2/progressbar/PKboJConfigurations2/toolbar/PKboJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKboJ content.xmlXn6}WڢvAĎ8>bE+*Iүdʶ<pf8C~Wކ xI{$M7`N%kll.;x;TQE;b_+U-`N Y-H;IjTBS= I؂*<>uM*♊Ѯ DTs"˯M6:6ـyfT&Se@|oG!D1v.V^E|yg&n̍*z{LQE89<΋sN\эNniGBXqɠȚf/+3`(v iqFƞlaׁ P: ,q磵*-4Ir L(gPLІ+ם.'(1Mb@n/H@crzh\"zk2q E87eO M?v4y[%U9*$$2lX^-vb7Nޤ%鞚`ކ*((T(FTh} '+"pvFwt`03OGw:u;j"f&-` 󨭅)s7* HqEOХE/? ~xl*7Q&Wmr YJBw)}hH8^f]N7h\]et2mv " Us!%_@{R?w/+j]Fxg65u:Z[91]g%+^A PKfd$PKboJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@ME޼I>b4hjFD/w&ź(..*uM$xCi0%mfr= nX'=̄â߹-& ̉~ٽL^9 T >w[ :V0[epY@&cRmBZl07(Mqt=&M6$:O01v\[_z_v(SNe&S '^.p?tKwWŸ1@1,,shPKY(!PKboJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKboJ.++mimetypePKboJ;QThumbnails/thumbnail.pngPKboJ&d/meta.xmlPKboJConfigurations2/menubar/PKboJConfigurations2/floater/PKboJConfigurations2/statusbar/PKboJ$Configurations2/popupmenu/PKboJ\Configurations2/images/Bitmaps/PKboJ'Configurations2/accelerator/current.xmlPKboJConfigurations2/toolpanel/PKboJ(Configurations2/progressbar/PKboJbConfigurations2/toolbar/PKboJ]b66 styles.xmlPKboJfd$ content.xmlPKboJY(! osettings.xmlPKboJ(oMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/routing_input_unconnected.odg000066400000000000000000000207451404105311000236660ustar00rootroot00000000000000PK;oJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK;oJ>]Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE      ) )*6+8%%%!.<"0>111===&5E'7H1EZ3H^4I`Vq?Yt@@@HHHVVV]]]A\wB\yaaattt{{{LkQrSuY|]celmoq΅ЫIDATxmO@ `:B|m@ B:&*,džq/~ߟڮ;!j?|>&pVb2N3le-CRkB[-cE{4j}=MXbYgQDi=PI81$%LuqNDs *s;UӰzT&} 6y^1Y."p\a06o|Em3~jQKMHFmM9Ou/8\_,4_ k0LҔzz}$t; owBAP`IENDB`PK;oJmeta.xml͎0} Dg @#UUTT."'u 6͐} )t*K߹|@zkU_}0zyŬ7m?'.S+]=&Fq'!6;diV?9>E"|[qr~X3N~_ nzzi]CP`}pWPKuD/PK;oJConfigurations2/menubar/PK;oJConfigurations2/floater/PK;oJConfigurations2/toolbar/PK;oJConfigurations2/progressbar/PK;oJ'Configurations2/accelerator/current.xmlPKPK;oJConfigurations2/statusbar/PK;oJConfigurations2/toolpanel/PK;oJConfigurations2/popupmenu/PK;oJConfigurations2/images/Bitmaps/PK;oJ styles.xml[Qs۸~}IQcqn\s}@$(!! ZVDK (4˱ϗKv?.KwYRQ2_uhk_5Oẹ*JKip^.5ڭDd2'-2ZFhis/RzDM6V\1Ғɱے%++f[:d;VyS[*a262KSR Zq'+3!}@znp[#,AD{&<<!X}cNÉz1kaG? R2@r0HgwtiVC Q0Aj%)ǫS&tC_gj3gJ宔4{N? J{H;.y8:J腨Olx}AQט| {A:l4->א)fegqZ 9cpɀ(p!ICo<UKF^2 MP7gXq8SVa ۆ0Iڝ7D;$ d =oy:l,6Gy6l,6/AvA|b iiQ<ΞYX; NrH4US|4N5~92F^Zl @Sŋ_884pCXMpp_'yLRuȜ! ~ R!- m,.diCy..r _na*asJDXk)E+G*˂`ŔkVb.EGRB57\XV`EJP(nPex!ujx+pA /h?qΈՋDh~6‹ًDlM_$>· WgChB<= -r]}`cֆjg*.!ASAGuk zX6T*j h{1Uo4;"&E (9(x%QŦHJuZoEf/q$wOOgBUx\krU@),`ۄؘ^g\:72Z4}o }hOGCkU6&\ V[QL䆣膥ox4](MM:ܓvygCj4[&ѽ.i|>({zeA`{aM|j69T4uyL8vJ\Q^`9C=| T2Zߔ ǿs ¿A ƛJJONv@m =bd8`N}h)5WK8HN [ o#u}kʮ?L}a~FPA SVIGNrd4i6 QQ/"c#<};G?R/ڃ$y ׫6 .&=б-Л>=0P?c5p2`$@j7LLct)<[f&&!ȣ"QRV{q}KNS5)z3Yc(#~w?yGI L#\ZsC( )r0 R[|yD;Gw?~|TtXh61> m+^Y\X?>~PK c <PK;oJ content.xmlX[o6~߯bXкَ }ؒha#Q2[I(ʗMٖ-V J}Jq_ΖruH[pgiJcJX(fzk֫^x*Rv^J-hlAb,Yb{\2~g ǻd% ^TX+9-ݍvdMϼ`\zJj jxPI쑜j/^-cXۤ)^,YT+NjqebSdszFg6ps|t)p?Ud|D- w=P=yŋ{IlU1cjgS%A27-*|ǩ ܂W1cqV\rleʛ"\'{~oH`zhY \=S4$-.Tس2=>8&@!GQ-LJ;hƓ"̉  yX_ߝTK79P!]Qt /D1U$UA#il3ZGuߟf('~OޮF":p}:mb&yQ#h;aDQz/^V!yJad4O"yaحU/T~}Yk.r5Wr8!>r|l568' ccg4_NkF)$I 3跮Z Ðqpk!s6v5STzsb b۰ vL~>Ar`or$tK7c™4{fC kY=2xK/8 ylY2/Yr/fN<ǫwQ8ÃH7[,QLd F|G ˾3##l??PKA&!PK;oJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK;oJ.++mimetypePK;oJ>]QThumbnails/thumbnail.pngPK;oJuD/Rmeta.xmlPK;oJOConfigurations2/menubar/PK;oJConfigurations2/floater/PK;oJConfigurations2/toolbar/PK;oJConfigurations2/progressbar/PK;oJ'+Configurations2/accelerator/current.xmlPK;oJConfigurations2/statusbar/PK;oJConfigurations2/toolpanel/PK;oJConfigurations2/popupmenu/PK;oJ*Configurations2/images/Bitmaps/PK;oJ c < gstyles.xmlPK;oJ+4T acontent.xmlPK;oJA&! @settings.xmlPK;oJ(AMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/routing_output.odg000066400000000000000000000212371404105311000214770ustar00rootroot00000000000000PK.oJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK.oJZ-Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE      %2$$$222===#E#K(J'Q *R!-X#1g(5o,(XgwE{Z%B̲ x p{(Z%rvߌa>]1«Y1hB,2bĮSBAmXX+#N>rrIebf!dzɊzxEn25'U` Ϥ-96Jg!{;&VV삕K6Z8KZ|Qj'N_}qnjyhfטA8Q J.w>[>ǐ"w=?Z,ebeieTcBkV0(see&gɹ+Y>gHx`GQ:lPDb|؄9$}Ε݊$>(?ɝ#i/&,?' lF9Xcdc%櫷a5.g-Ǝᖴ6ô-VVZ(kP~$c:PLIENDB`PK.oJmeta.xml͎0} Dg !#UUTT."'u 6͐} )t*K߹|:pLV4:lDU9ci-E{o}kn=˜ыp5mqe j9? g!0D&1"$I&Gi n(JFJ=.0)`&W΄u b/WZKӆj_'%wj'q?Hcg1~x?! iS lû^4,'5h+hqƧֵ?U V|PK͂0PK.oJConfigurations2/floater/PK.oJConfigurations2/menubar/PK.oJConfigurations2/toolbar/PK.oJConfigurations2/progressbar/PK.oJConfigurations2/statusbar/PK.oJ'Configurations2/accelerator/current.xmlPKPK.oJConfigurations2/toolpanel/PK.oJConfigurations2/popupmenu/PK.oJConfigurations2/images/Bitmaps/PK.oJ styles.xml[_s۸}Ibo͵sIz$8 hY}'.PDT,9f#vaw]Ro3]DG'X]cp Re^"%ǧ}55+ZhqLƐ`7 ݐ9+D]R$ӏSjKއwޯtUgDB'p6¾0f|dO0WO=i~2c #+Jª2#۠Y Uf p e.Zi:d0sZ22IlEt)X(2K.G8qC'7t6skGhOEszEx2}=͉LkB=Aڡ XDU,Iv_ pe hNbsFDxlej'=r2(3,,D׏`C83sJ&gE%Pa_|3Hjj:c8S#XҔ jAx}MΆHChzF^%BgCh|1}ΆU|<}u64Oψӣl]C6[Wxm,P`]E =XSaXS-;=0cYT:"'n), ^AH;KZMe琔r-xZ/.q$OOgBUt/ \ktez zZװmBw}\Ltv>TB@9]>Qg6CtP0"iz햾Cu*\ v+AL䆣膥ox4k] M;ܓuygI h' psg6bh>N zm].{ze kZGhowt6&:N:O jwbʥ9ç1!НL)UR_/o^6\/hx6@i<+BOY:A|5S~Z.y}IM_dVa =emYw֯ o*v`˪p0hbI&^¦W>ʞE"2t;Ƨo˵ Mx>`HUVAC߯Mt)I^1ztx4b"@j7Llcɳt(|M;zMLBǏE4sa}KNS)z]0i{4ޗ;ā%XW4 .6;x LnߟѦ%~(\O|w 0NJ~Mʃ$i~l &.Iy%}U{`ػ]J* p_,/-ͧCxtgGώpUqvhPk]{cgs xPK~0VJ 8PK.oJ content.xmlXmo6_!ŰjXؒhaؗ($IlmbG9ȭ f5'm2 )ۇ7ܾ斦)2qS;1-9|[.륒솕KjR/KTz%pٱ&z)R+5l9dq%d' mǒjS:s'BYIye9n'pBY•RmpqUrJbXlVw;l9k&M NNb+s"˯M6:6@5bL&S%LLnz|#!X1v/*fB|J6UTKsϋ\l[F8f<Q\绀pF."zJu2LJx t`5G!2uAѧ؁E% {R:L$`ħ.e\P:~.њp $9 sB4gCݛNsq_H9 .|N Uez)u*xWaF咶i0O+@e; !Epu.Y5:E Kwi2e?ߛ130#L5a<`!/-xK9ZzzmY#6!¿/w(up*&GH=ZgϷfةPf攚jP&q\!&^M䃣Hb&%v848j-IM˛x4.lSӜ…'lK 33.{r0 D;/OIKGNzs1>-Nt69k_TM$9I9> (q-:kL545HS,HW\xЮkE.W(oQp$x9YSH´R$RHyw:+Hdyg2kNHg >3!bĝX+x^?tw+MUg5,YmB3`Aɹݪ!hkTSE@u]'^m6{IU\gRɠF[p:QEF, ʥЛ=?.,rW|\u;^H#gyVϼtL(ܧS>wACeFAlܗvU L)WBd^P&S yp3bNof,?GyE-CXGIX^kOߛ{\Q XSx Q#.b`엞e ÿp*/]0:ɀ\oz̻OC4\2{vZq  m2v`eVu5s{mx@vJnAcС}u`'0L&zҡFڠ;OʻPKsEPK.oJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@ME޼I>b4hjFD/w&ź(..*uM$xCi0%mfr= nX'=̄â߹-& ̉~ٽL^9 T >w[ :V0[epY@&cRmBZl07(Mqt=&M6$:O01v\[_z_v(SNe&S '^.p?tKwWŸ1@1,,shPKY(!PK.oJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK.oJ.++mimetypePK.oJZ-QThumbnails/thumbnail.pngPK.oJ͂0^meta.xmlPK.oJ[Configurations2/floater/PK.oJConfigurations2/menubar/PK.oJConfigurations2/toolbar/PK.oJConfigurations2/progressbar/PK.oJ7Configurations2/statusbar/PK.oJ'oConfigurations2/accelerator/current.xmlPK.oJConfigurations2/toolpanel/PK.oJConfigurations2/popupmenu/PK.oJ6Configurations2/images/Bitmaps/PK.oJ~0VJ 8 sstyles.xmlPK.oJsE content.xmlPK.oJY(! settings.xmlPK.oJ(META-INF/manifest.xmlPK6Smuse-4.0.0/src/svg/projects/routing_output_curved.odg000066400000000000000000000210371404105311000230450ustar00rootroot00000000000000PKmJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKmJ[LThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   '("-&2)6+9$$$*** -;"0?333999%4D*Q2F[4I_6Lc8Nf;Sl=Vp?XsBBBRRR]]]@ZuB]yeeettt{{{FcHeJhJhQqTuW{X{^`dehjnq΀TWaIDATxڥ}s@ cǶElZ|9w vGEA7kSH< 7N$\Z Y+I8ShOJ]Y8:=$<0g27FbX'{LGMNg!)_nQ4 k{v3;~XRv:wuFvJ<&k6+UuéPU/ӳ mFp -$K{FsԷ_$};d6@~VAfVpe0bg-n>fCx>m3IMN" Pr:mq#wq%.z[Nȧ}+u }/}98H?jۑnښ*){_g[ lDIENDB`PKmJmeta.xmlK ÏI푺`RZ,qjD,s\SS/XUp@ \cv x(?Y2Af]# `*KtFQ]YiacTB7vNT ˹B}Ǒ6G-3مk;S{3(j1:X# gvLxk]Fji|Sn,ki/G i=Ӷ#2ݴ^RȾI}4eá땫)`5v{y G@9Oe᳅( !Ct#&1%qD-"99+);ShF$%$MK7|w* qy/-+?GĠ֦|#4QG&gIѿs}dCr_qrW;u~_ tXp|x×PKzs*.PKmJConfigurations2/floater/PKmJConfigurations2/menubar/PKmJConfigurations2/statusbar/PKmJConfigurations2/popupmenu/PKmJConfigurations2/images/Bitmaps/PKmJ'Configurations2/accelerator/current.xmlPKPKmJConfigurations2/toolpanel/PKmJConfigurations2/progressbar/PKmJConfigurations2/toolbar/PKmJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKmJ content.xmlYYo6~pu] $E$rEh2D:]& P, 3ʾe!\PloCP‚SMz̆w"E1gȊ]JX:vm- GK[W<ը(tHJdF`3"P4)/;K|ՂXJaLN'6$=nט3 J O 2Ν Էׯyųs)lU!eVhkMUjks};ջ^o9"`Ds!q~h:8 IZ'[d`\7o}Y_”&;rb.S(du-'œƬ̣*ɮ *N5m`F+e@eݏ &EITvIg02 Ky< S6)cG+";GɐjꙌo4皪 |b07Uj]ٽ_Qhvڽ{C&@akW`#\5h[ #qἃ( l0:ᦁxm =|¿}f`$Bklz 1aZ/~zzG2żE}o/oS㸔Lui=nڢk'PQ5ԄP5 si_PER=5<5PPU!9'hK#urGn0 3,ep}Rf3a@'"h81UNiS)$2;<2 ( pEkB51[f4oGMxkb 8RHB $cR#9YJb:Ixe@#ReDR¯V8>fߞl|5֡lێWD$NuzX-cѾ}͵Z+[ջWGD]ꔀuCMqJVBYT.mJtm#I^}|ÃJ:ިMC)ґC9hb=A@4-Ƌ+KR-,&x\+S/kEAa|UG Z7u{9~XْM} KYx0(49|yh:q}5 `4u7A0)m~nYcc|֗_#*֛&sŸ*B{[*iēǮIAo^]%-y-_?r3218%zcv3RNL]0z=={7;](*$#TqٍV7 |W/ɪߑsR#*f*ח@xWX:7q#0Er2eV.cHѬy۫7u)IKFM<\+AuSxq0y}GE2]0AAH|hiӹSW1|fNtzcp7PKrPKmJ settings.xmlZ[s8~_tgb m@0% }%Y 6!6zu>" KWcr ߕˎ-FFb+"X+깪^/6kE^cK7h/3QbB#Vc3':SM d|gyɄj߄ytF:0wF$ zTF6 _ ITb3u*oT2"RiikXȹ%0^cD"mb@慨 -KqA c&;dRLwÿ!mi:g9Cn㱺&rPM>%3k]r)ɸow=WL =n]@7-_tR.]&-6Kf&Fy 篏|Zz俢#ץCbS0tBw1jɏo//& Jstr>ps{n:0EG]bl,x,3 IZ"p*a4}({qX%e6ϓ[?-$RAJ9o|Bmm^ y6KDykfI4JN =p::*-͕ OfIT 1ӿ;^wќpz(лdbD4gQg %d!iЁnF{S ~D]{aľҦ"*ScIL%[چm &TIꊳV#$jw+ӜZ`DbNO=*Փƽ @f, e޼q>k$h*FD/&ź(.@|4qNpAG*&casCi0%ms X'=̄âߺ-& ̉~ٽtL^9H=%͡PK>k(!PKmJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKmJ.++mimetypePKmJ[LQThumbnails/thumbnail.pngPKmJzs*. meta.xmlPKmJConfigurations2/floater/PKmJ=Configurations2/menubar/PKmJsConfigurations2/statusbar/PKmJConfigurations2/popupmenu/PKmJConfigurations2/images/Bitmaps/PKmJ' Configurations2/accelerator/current.xmlPKmJwConfigurations2/toolpanel/PKmJConfigurations2/progressbar/PKmJConfigurations2/toolbar/PKmJ]b66 styles.xmlPKmJr (content.xmlPKmJ>k(! {settings.xmlPKmJ({META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/routing_output_terminal.odg000066400000000000000000000205221404105311000233660ustar00rootroot00000000000000PK9mJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK9mJPDLLThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE  $!+",(5+9$$$... -:222999$2B):L,=P.AU1EZ5Kb9Og=UpDDDKKKSSS]]]@ZuB\yE`~lllpppyyyGcHdNlOnRsTvZ~^`abeghjmnqΉt)IDATx@ سP*Uy647ћs ,KߟwGg9`8IENDB`PK9mJmeta.xmlSˎ0+"wCTU]M.R؞-6CRDU{}8:7UZ.H͍PToOil ,`Pu@ e-Gqkj.-V![0j,9ZƘ6 X'%pzhר^qӴ^ʹҪ̈}do-eL0Wѫ{:w](+Qa)-eIFFh'%!co0rيSF8&i4Kr8&U)>@$:j_}&&߼L?K|Y1Ә<<+ݝw!M6 bԠED.2+N ׯƛt^MXaNoPKLFE)PK9mJConfigurations2/menubar/PK9mJConfigurations2/floater/PK9mJConfigurations2/statusbar/PK9mJConfigurations2/popupmenu/PK9mJConfigurations2/images/Bitmaps/PK9mJ'Configurations2/accelerator/current.xmlPKPK9mJConfigurations2/toolpanel/PK9mJConfigurations2/progressbar/PK9mJConfigurations2/toolbar/PK9mJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PK9mJ content.xmlXo6_!h}q/NuÖEJFR_#)єmښ {Xwݏwoe UK?Gʗoѕw 2Eʒ$D $|z].W  D,d`5:^̈bڒlXe3krTW=cc@5mAK%ef3L'At}}hu8FI@ &A4[KUS>>:4X⣬֜r1ruzZٵœ03 Se4uuK,W @{^r\ Ui=z3ƬJlvn;YSIO\$6<4E Y+M!-X{JV{0:JH\##JZ`H+: {u"`/NjƥMP6 ,Jp S4= w3(&hMNy}@L]꿦An/8rT7.ޚJMLɶ&*.ڢgVɶ;zM\vI'0JD ꅣy܎36)KC+b*O@ɐjڙNvSD`28ߠ '$)Ov3OzO*M-q83{= rRA qV⪇L1}xd677|¿6c`F$vB[|z p<2z7?=ޓ)0 ѐ‹+8n$S#Aڎ-[[ZGvv 8wBs#՜zEn\]M 2J~gUA5%%BGUH>:ipz+JX@*܆L8r|+e\'p SP>5Blw\+=IR@9g"4_A]p!@EI+;Nhh w5N%IVk,><3)딬 %_Hx)v9?\gTpL:6vM/̭bׅ+!0qG{\!fpdl^`Lzu>" KWcr ߕˎ-FFb+"X+깪^/6kE^cK7h/3QbB#Vc3':SM d|gyɄj߄ytF:0wF$ zTF6 _ ITb3u*oT2"RiikXȹ%0^cD"mb@慨 -KqA c&;dRLwÿ!mi:g9Cn㱺&rPM>%3k]r)ɸow=WL =n]@7-_tR.]&-6Kf&Fy 篏|Zz俢#ץCbS0tBw1jɏo//& Jstr>ps{n:0EG]bl,x,3 IZ"p*a4}({qX%e6ϓ[?-$RAJ9o|Bmm^ y6KDykfI4JN =p::*-͕ OfIT 1ӿ;^wќpz(лdbD4gQg %d!iЁnF{S ~D]{aľҦ"*ScIL%[چm &TIꊳV#$jw+ӜZ`DbNO=*Փƽ @f, e޼q>k$h*FD/&ź(.@|4qNpAG*&casCi0%ms X'=̄âߺ-& ̉~ٽtL^9H=%͡PK>k(!PK9mJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK9mJ.++mimetypePK9mJPDLLQThumbnails/thumbnail.pngPK9mJLFE)meta.xmlPK9mJConfigurations2/menubar/PK9mJConfigurations2/floater/PK9mJ4Configurations2/statusbar/PK9mJlConfigurations2/popupmenu/PK9mJConfigurations2/images/Bitmaps/PK9mJ'Configurations2/accelerator/current.xmlPK9mJ8Configurations2/toolpanel/PK9mJpConfigurations2/progressbar/PK9mJConfigurations2/toolbar/PK9mJ]b66 styles.xmlPK9mJXN content.xmlPK9mJ>k(! settings.xmlPK9mJ(META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/routing_output_terminal_arrow.odg000066400000000000000000000211561404105311000246040ustar00rootroot00000000000000PKzoJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKzoJThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   #( )#-&1(4+8$$$,,,!.<555:::%4C(9K,=P.@T2G]4I_6Kb8OhXrDDDLLL[[[E`}fffkkksss~~~FbHeKiOoQqSuVxW{X|[]``ceghjmnq΄wIDATxڽko@QQMתݵkj (芢]eې!?(fsh% 8iҧpIÕ,a}qsiern'C+cVWGXK+u72Uv^>*437o?z%h(} @XNY<&:qtC)F| R:`X43!tcQ+ghR+Cg@6w\ 4i5k_}@_E>|btuh1V͵gNN_OV]u.vel ҧl_}CsamlGuCsNM۳ jؚ0k̫|v^Xz^?鬉:Kʑa4_Ur I|ضwɍ'l\ ``2ЂtfYWraZ Ї5SWC5żfOGUiHb~IENDB`PKzoJmeta.xml͎0} C FZ]DlOld!})t*K߹:<7u"ZGB1ͥ:C\??K&(׬kra#\xt:*@gՕ:Fu+,KFιW~8R8qepf_'$jm'y4x}D8J#$Uw>|ߥt,CkOĨA:Yp[qr~X;Lu>tXp}xǗPKw2/PKzoJConfigurations2/menubar/PKzoJConfigurations2/floater/PKzoJConfigurations2/statusbar/PKzoJConfigurations2/popupmenu/PKzoJConfigurations2/images/Bitmaps/PKzoJ'Configurations2/accelerator/current.xmlPKPKzoJConfigurations2/toolpanel/PKzoJConfigurations2/progressbar/PKzoJConfigurations2/toolbar/PKzoJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKzoJ content.xmlYɎ6+!Z[q$JM[%_")^/A10Y*mY8k8խUBSR/{ 2eJĕZ vŗz6ZR _V|)%qձ6z#\toPi lS6C AJ(D ^l R}uWBKl6M<,b+q81aB ,~8 [b'KUS>a684H sveaku~& uR6Dbu&s+V]Kb{JoSm>Ը* +w Bo.7,xr"1婠.,ysd BkԿ?>KVD{0H%)ggUdp)HaF|3\S&LMVLV,KvМI( g &ޚ3ם.0_( .#'Pr[3T΃ ֘iB-{3*gLck^"l4z%BKdq2}K^SA~~u޽p);s/ܡhQ#1E={{K,{5ʭSsvDW$k䧉z4Im)^ r'mH*oZ('Iڦ.up1c0Ჷ'GHmv)N6VT>$pA!p-N@?-Jfx+Lb ֌.KRQp"J'q[8[5JG'h DWSgzŒ$GNX~H7R45#vzam.\ uRh3/fݨK׾͈]JڲeJf)N@iDJ MY*Mg)N~_M禺GnhA@FIj%VRh%>Ng*gp&hݶ cYn6w7$ rLL' `pIiJʀi4ZDad|1;t~WV ^uv {Wu=_S]jZ#CKF*;j}Tzݕ9v9VǴ33Ps/Ҝ-z5gO&PwkN^42 q8;%Y;ЊQO[.$P@Xp2Zx6<>f:iGh= [?UktfjQ];v}ީM3r7PK/brPKzoJ settings.xmlZ[s8~_tgb=LCHhnMXFHr~%Y 6!6ٙC.}t.KA*U)oμ.p8dT)qBhm=3]u͋"QLU AUuPbZuyt5!=yL<ռQ'pȯH2PD'#\LѶ(U0[X"\>g/FmRad4/"رkڬU|\/aqf?JLCh}dojy`=^ڼ_|g!1/42/^trR9ׁށNE??>=+Q=N?(VN SWζ/$*1A*Lw+cNNQ 6]4խcTYdX r « WP%Qxm$eɂ2.%#%rĄrGbwL~%C,]4bk2p9&b8+vO㌂|I~UR6 jsn .9Ήo3& S}7|A{!7 /Qu~h%3$C+So')L8~ fmZج'.*Xt韈HhNy\_g3L{4(GhyɴYhC')n'_?"ȇ/?|Osb_iSvI1Y$&M mk6FN$w}sY+ ^5޹iN-0$1FNOƽ@[p" e޼Q>8h(FD77fZ)8D|2qNpI'2NcasC2%-s- nX'̄â߹j0& }̉~޽tL^9HO%<<o|2$LN@&#Iq/CWnab™^5{C mYY2xs/8) ylY2Yhb1z<yﳏZErq{9nJzs1&n!}gG~?<PK12!PKzoJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKzoJ.++mimetypePKzoJQThumbnails/thumbnail.pngPKzoJw2/Zmeta.xmlPKzoJVConfigurations2/menubar/PKzoJConfigurations2/floater/PKzoJConfigurations2/statusbar/PKzoJConfigurations2/popupmenu/PKzoJ2Configurations2/images/Bitmaps/PKzoJ'oConfigurations2/accelerator/current.xmlPKzoJConfigurations2/toolpanel/PKzoJConfigurations2/progressbar/PKzoJ8Configurations2/toolbar/PKzoJ]b66 nstyles.xmlPKzoJ/br wcontent.xmlPKzoJ12! settings.xmlPKzoJ(META-INF/manifest.xmlPK6"muse-4.0.0/src/svg/projects/routing_output_unconnected.odg000066400000000000000000000206201404105311000240570ustar00rootroot00000000000000PKJoJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKJoJ-Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE      )+8$$$!.<"0>222===&5E.@T1EZ4I_4I`Vq?Xs@@@HHHVVV]]]A[wB\xcccrrrzzzLkQrStXzY|]belmoq΅IDATxYS0` L<iB+ƣiuߒX7> yA8J wA"{ J^M°iY|!t/f笣RobzAkUiaP q#qmjz^BoZf2L\ǥ64۞y_Šm}xK_rK;5,0KiLYu$HLZ'0cUh1gO?=m%֌-R0CUnamik8v-iL]x{v1W:dhuϵ/;V:+^(㉔|?5Zy>OG_%m]{7^. 1o|4[ȌʮЌE=lYijN[s/홅EpYY --_`|jgK\yw(?SU9IENDB`PKJoJmeta.xml_o0)+IPi=TꤥR"b7mJgLHa<{SS\6D1 .bB[B SkQm84d<چDUF"b)Q-iƝS-mږ}'G<tBpmkO1 xP o 5HƝV7sͼRz 1+0'4q`#yEO+)[ GX}Q\W^?1mmc0<Ӕ QQ>[ y!J#D0aDV IU(% )ZPJ & L<٦6r&봯U@_whe?.њ2#|;8h/171{;ퟳ~ f7 /Y6Gw5\+haf>e{PKv-PKJoJConfigurations2/floater/PKJoJConfigurations2/menubar/PKJoJConfigurations2/statusbar/PKJoJConfigurations2/popupmenu/PKJoJConfigurations2/images/Bitmaps/PKJoJ'Configurations2/accelerator/current.xmlPKPKJoJConfigurations2/toolpanel/PKJoJConfigurations2/progressbar/PKJoJConfigurations2/toolbar/PKJoJ styles.xml[_s۸}Iqbo\s}HPBB|>']@Xsvfb .7wGExq."ߣEV,|1,uN Trᢚk_bIŪyArZe<%-垫lbv%C%KWV̮t"z0xʇ WY yI$>ck%e9z}\p G,TF+k)$iFq*]B˛SIꇼJE/ doWKA+`s/Mʴn9ػ=0+"bn$*ĕ͉\Ux D[еU,X9LsUQ@RwEPvׂI* {LA](ޡ7v,l58 )`wUke(hɅlI']Xeܤ̳T˺I LBHipf;RC"ͽFQ Re\!%ç}55KZhqLƐ`7 ݐ);D[R$ӏ95x%Wzo/[=] /(̸߼ٓ$ SI0c_!#+Jª2#Y Uf p e.Zfdi`(*&QꦐbO\$x4=H t{40 5nk*޸q>yxS12oyNg*\udo6,"oyHv;,j}٦LqiD7`\X[^9(B2(FqJ 6PL.ؐ,k(dz4 5LRWVk-Hxv* ֿ,\\Iʺe57dxt`!(*gBRek5Y-@ ')`7o]R[ fK%}&^3z W uvWnH?ӊ1n`h!)p1K|7,3sҩ й ^QtSZm1`BW9C㺱?]@+.7iVTW%I@̠*%*\J,0hM2` rPH`$`7ྍF$Zۯ"^_uLJ\ L3:˨4GM q5LYddf_a_|3Hjj:C8S#XД jAxyMNHChzB^$B'Ch|1}:BB<=;!B/3OAtJTla_ukCDuaJ3h+RcMbU{`EBf}S E[sv),֏ ^AH;KZMe琔r%x\/.$7OOgBUr;52==f-kj =k6>.&vk;?*~.T{c!{(G64}xK"кDāCt &kpkrotcvC7Ey.Sfﺼmzp`I8htﹳ fD4.{6.F=(rGe~pMpomshsN8MVcS.}WXvcC;*RR1POp. Zm1ެ Pz +t qrsjzg& 9*l>x:.]yD.lY5&gM Cy:=ʑѤs*GãH_Dn,nnK\k4gok^eQp1i4nQɊ乯7hN}pG1:.0,Fϒvv8:4X೬VJaFk 9`>Zg ܗ4/ibs ,6]x`T秣x1v-*M1$î }?]8[*R##ZGg%-=;:\a03qaoJhbE>\¤ft]j pA) JH?aGKf|%T h"a~+VwӃӛZQFJ: \14-TLM~$Y.ՇZ2e/=xSu~g59!"kE;d爑ǜ-X^*Y9fq\mh WO4Im)BVQg_D޴?Ņkb3о?בnŌ!VޑBI.eq10Nd! 4=+(CҢd&Pm6PS`M٪{~0NƷżp"?`4QxbxaB+%[NR:;p@g2>gهtcN\3b֖M^Xr0/hUԥtu=h3bQ;4Z&n;e g7"`dSΒ_ZmIԉ(IDBpR&Lg`EK?X荣@2h .`r-kBʍ&Pv|(ZZ^Ѿgp >IÝ19i?s R!}kwZAГ*Uɠҁ[J 7k"Db|&w`f0Zc՞aO'x}Ո~טO[ߝw*nі.b2ePkqk` 8ɛnyT@n7;=S4r<5u2!irXGe+uAE#>=f*Ӎɚt,y2 =/PK PPKJoJ settings.xmlZ]s8}_tgbHl:)I@}u=¿d K&vv6st?79P!]Qt /D1U$UA#il3ZGuߟf('~OޮF":p}:mb&yQ#h;aDQz/^V!yJad4O"yaحU/T~}Yk.r5Wr8!>r|l568' ccg4_NkF)$I 3跮Z Ðqpk!s6v5STzsb b۰ vL~>Ar`or$tK7c™4{fC kY=2xK/8 ylY2/Yr/fN<ǫwQ8ÃH7[,QLd F|G ˾3##l??PKA&!PKJoJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKJoJ.++mimetypePKJoJ-QThumbnails/thumbnail.pngPKJoJv-Rmeta.xmlPKJoJMConfigurations2/floater/PKJoJConfigurations2/menubar/PKJoJConfigurations2/statusbar/PKJoJConfigurations2/popupmenu/PKJoJ)Configurations2/images/Bitmaps/PKJoJ'fConfigurations2/accelerator/current.xmlPKJoJConfigurations2/toolpanel/PKJoJConfigurations2/progressbar/PKJoJ/Configurations2/toolbar/PKJoJȩAk 8 estyles.xmlPKJoJ P content.xmlPKJoJA&! settings.xmlPKJoJ(META-INF/manifest.xmlPK6Dmuse-4.0.0/src/svg/projects/solo_and_proxy_spotlight_on.odg000066400000000000000000000222621404105311000242170ustar00rootroot00000000000000PKԛlJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKԛlJhfJJThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE     #,##/ *(--""//4# ;' ;( 7433 ;7;; 00###+++??%555;;;B- G0@<L4P6U:Z=IEMMVQUUYS__TT[[KK,XX4cCjHqMvP}Ullbbiipi}uyyppcc;ii?CCCLLLTTT^^^ppC}}Kaaalllsss}}}Y^adill q t"y#%  #%MRZ[ghkoqÄ&ȇ'Ɉ'Ȉ(Ѝ)ܕ+¶Ź-./02 %'()+ ,#.'+,02vzׁ݄syrIDATxڭoFp?5IH-6fa0KG+(168ePAXiċ䢬$T\mHl-!u[lҺR~pl{#ܺFe$k2eEhO¡xN#=WMH聩L%XH uqUb7 G(σjbQzaNP|)][}&9yt^nIE:Wl[:]Z>3(ֹyx͗V%ޱ׹eic\ 3Er~(Y%wa|eʰ SSrˍIu θ-2@q*hgQ8EqN ŦK~/1/&Ӻגibf.dd##X 7vձiO0=5ܙ c{d璉N>& [cZ.x6gmF,i5q?VveNĊ1Rk0ą: 6-:rgۗZ\ oq5VxXEm{H,;5H5N@j/|q" :R!ڝl : η:ݨГSTvF.f&>?F]h?6'q\\)e3}K_Jvj z,b9l|n- eAޅh'Ŕ"'xLpbFq')N *EkZ=3vqyC7+?'e6xRG+Gۈ<<)ݞߓa XSr1ÇVU"$'wu\:x0PVP zoPKsH-PKԛlJConfigurations2/menubar/PKԛlJConfigurations2/floater/PKԛlJConfigurations2/toolbar/PKԛlJConfigurations2/progressbar/PKԛlJ'Configurations2/accelerator/current.xmlPKPKԛlJConfigurations2/statusbar/PKԛlJConfigurations2/toolpanel/PKԛlJConfigurations2/popupmenu/PKԛlJConfigurations2/images/Bitmaps/PKԛlJ styles.xml[_s۸:$'暛v.׾z -+_I (2qLbwv%LL*̯,&,e~"z#ӔlȸXI7˹%^y璖4c\sYs17̮fz0vdbʆٕN]F^OXRTXf|G{WJbl==j釗 -_Q)agb~Û1MꇼJy- toW J`s/Mtn9ڻ0+Fa4*ĕͨ^ D/[RصUx1LK)[UQQ7 oA)=>Sˬ4 } ];b;$4磡G=WUbT$taM+ᔁԆuԙ> xgoip.wևD { ptLMA?(%V{̚W^0 &Q0v&1AB\E'\LRj%?’\,Խ.o=ˑtC\_绚~+FD2$>%^Dn4ǫ` Ki%*ަŊ^[?SCU ԆRHH~3:zٔ R^Gi "W\|*UPEb eA0iY(Vqc]`YgUb?G<-1p'L`z^t CK+3 *bJ`ʢDG6eGJFI䚘k$XG Hz?=ڌ /+!A`7/ wޖnx 5" Ӫԫ5;`rRW|w;o?Xh6 3RBU.NbyК'AyFW+/Yݣqq c\ GkS5x%`KUy1>X[bU sT!,X)K1`rPhb$ dw7K&,*!X"o-`z￿LxN]W`?~{l!7&5u8!K!z 4k@xyMOPh!4;!B/!^$BNEt<}q2f/4O_dC+6:XYKz J鎵J=l+f ֌i X15=-Υʨ΢R.s6 ig)YiTQ]+Rw%LJ~]ѼZEce]\q MٮI3ݺf:@9>QoiP+ ̟^X?E{}fx69X4myJ8vKn\I^a9C=| 42Z?s\b56t_i&8$(zȲ qr!sjzW_M^ArUBO9|\Wv݆f kDhʪpp-51q(GFiU`/|l"{=Yޖ{i_^~]{0&*pUzvӧG&+FrFs 1:.0%U=H݆IXHYOkf_|ҤWngo ʹ{ ;c@]lgux=<\pG[Y.ϚѶ%mkP_.>y{I{ǥZH`!nWMhKnQ8/=V1zajF VOuF>LـީΈRU^T,>\Km6& {AǞڄW04Xp2%tj~T&'SC5=Z2X. sZ@_|ɒ:3TtT[RKv+YN l}!Mn }k{ͣY =::0ܓR ʦ7%-uY0v\I(yPΠ5gg;=.@ŀQc Hu߸dn{k&VUCC Lr4¸-U'lq/ IqVt.d!H@p`Ey{,!iv`%JE޽FCSkg]uB4uLyv hHʒB\5;3}rhv^-J@p4wO\3:Ј'9(uR jhk*Q7=5M Dr/{qz )Ҭ^IF꙾e -z}ar1Ɨ7v(q;GBL{Z`mi5bRӼGͱk.\sI%OJ|51qžR]օPPԜ)#)UKd}߇}7[6싘R.\$IU]R/=ԃt?g—X馾4/l3[q75G9R@;"r6x pKOfe|#]vupP@`8h@І`!" IFI&׻XD:1." ,9/LH8p 8 rya`TgFP4L|y,O={#Gt7PK\dZdPKԛlJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@MEܐ|zn ӼwWPX&ޙdzRG\sKx3ykO{R8.]FFpawİ?l>PKxƫ@!PKԛlJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKԛlJ.++mimetypePKԛlJhfJJQThumbnails/thumbnail.pngPKԛlJsH-meta.xmlPKԛlJConfigurations2/menubar/PKԛlJ Configurations2/floater/PKԛlJ7 Configurations2/toolbar/PKԛlJm Configurations2/progressbar/PKԛlJ' Configurations2/accelerator/current.xmlPKԛlJ Configurations2/statusbar/PKԛlJ6 Configurations2/toolpanel/PKԛlJn Configurations2/popupmenu/PKԛlJ Configurations2/images/Bitmaps/PKԛlJ׏ 6 styles.xmlPKԛlJ\dZd 2content.xmlPKԛlJxƫ@! settings.xmlPKԛlJ(META-INF/manifest.xmlPK6f muse-4.0.0/src/svg/projects/solo_proxy_spotlight_on.odg000066400000000000000000000223341404105311000233750ustar00rootroot00000000000000PKЛlJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKЛlJ9nnThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   !&#)#(-/&/ 0&4# ;' ;( ?2###"'-"(.+++'-4(.5,4<55507?;;;3eh];,veEi|>]şoW /k?)Ȍ,i~!ai&WyVdT[m`:Kݎ9Cf*/ K{NixY2ٴ%uKliƫ&2si;Ib3xr]6x<>uE?ԍ4̘W[%W72/3Ll<˃;:=%3`Z2g6gɲ804[f>8͇ZqY[qq kUT\ 5; [Rg Nm<0(9<`N7U?Sˬ4 } ];b;$4磡G=WUbT$taM+ᔁԆuԙ> xgoip.wևD { ptLMA?(%V{̚W^0 &Q0v&1AB\E'\LRj%?’\,Խ.o=ˑtC\_绚~+FD2$>%^Dn4ǫ` Ki%*ަŊ^[?SCU ԆRHH~3:zٔ R^Gi "W\|*UPEb eA0iY(Vqc]`YgUb?G<-1p'L`z^t CK+3 *bJ`ʢDG6eGJFI䚘k$XG Hz?=ڌ /+!A`7/ wޖnx 5" Ӫԫ5;`rRW|w;o?Xh6 3RBU.NbyК'AyFW+/Yݣqq c\ GkS5x%`KUy1>X[bU sT!,X)K1`rPhb$ dw7K&,*!X"o-`z￿LxN]W`?~{l!7&5u8!K!z 4k@xyMOPh!4;!B/!^$BNEt<}q2f/4O_dC+6:XYKz J鎵J=l+f ֌i X15=-Υʨ΢R.s6 ig)YiTQ]+Rw%LJ~]ѼZEce]\q MٮI3ݺf:@9>QoiP+ ̟^X?E{}fx69X4myJ8vKn\I^a9C=| 42Z?s\b56t_i&8$(zȲ qr!sjzW_M^ArUBO9|\Wv݆f kDhʪpp-51q(GFiU`/|l"{=Yޖ{i_^~]{0&*pUzvӧG&+FrFs 1:.0%U=H݆IXHYOkf_|ҤWngo ʹ{ ;c@]lgux=<\pG[Y.ϚѶ%mkP_.>y{I{ǥZH`!nWMhKnQ8/=V1VA(Šx](U=o^Oф 8:L,ZܠќͤLTilץrYQ1:5XSp51Fk ,3S%JS%JV=Ah>ؽ4*"X5:[WB]O^STt(،Px@t)oH'B(^-` ,h`4R*\n3# >l44vt&`ƧP:~.Bp $9w"4btMČwiZS*ԝGN%2;[S,҇ E87ji&yܱRm!49*$ Wva9]ir-d6o#ipkk?))z"s7ט/5[4)si$m$„)Yҽт uE8g/KXtV0ǴAi"L5ZP- )$f Yp?;;45;h7oD!/pb- ; a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@MEܐ|zn ӼwWPX&ޙdzRG\sKx3ykO{R8.]FFpawİ?l>PKxƫ@!PKЛlJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKЛlJ.++mimetypePKЛlJ9nnQThumbnails/thumbnail.pngPKЛlJ0xv/meta.xmlPKЛlJConfigurations2/floater/PKЛlJ' Configurations2/menubar/PKЛlJ] Configurations2/statusbar/PKЛlJ Configurations2/popupmenu/PKЛlJ Configurations2/images/Bitmaps/PKЛlJ' Configurations2/accelerator/current.xmlPKЛlJa Configurations2/toolpanel/PKЛlJ Configurations2/progressbar/PKЛlJ Configurations2/toolbar/PKЛlJ׏ 6 styles.xmlPKЛlJHb Xcontent.xmlPKЛlJxƫ@! 8settings.xmlPKЛlJ(8META-INF/manifest.xmlPK6 muse-4.0.0/src/svg/projects/solo_proxy_spotlight_on_alone.odg000066400000000000000000000211001404105311000245410ustar00rootroot00000000000000PKJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKJ2}Thumbnails/thumbnail.pngPNG  IHDR<< ")@>PLTE    "+/&/ 0&4# ;' ;( ?2"""...555;;;@+ J2K<P6U:Z=XF^@aNbBjHfRiTpLpY}T}dCCCKKKTTT^^^aaakkkttt}}}Z]gm`qdgiyyll q u"}$%ƞƒ&ȇ'Ɉ'Ȉ(Ѝ)۔+̣׬ݱ-./02._IDATx[S@c@*նj5 Z2T)`cKBtdCKl0bF';|"̒GijI0[.r 0gCkZ:(,.ѠU-R1ZimdX?Z<3Kl fqy!N<)Y̨V-iX;9lǒ|7`RJN;f/,2^%.:xn@wȊa>8 K-FmMٖR*i{`mcӭr!v/| w^O+ "lEleʷErWӰ9⧅ N˷3ri4+e,9RCsɼP"l!?d/>/?ǚ S6/nnsR@Ӂ@!ȯ$NGXb9n0owdRv`!w@ҸvlWi[:U.$2:-1J&ցMlLƏTan=Fq"3MBS"$2=ϓfRPTUTX2N=OoQHqIENDB`PKJmeta.xml͎ } cOld{JE0) Iq:=˥|&:Sqz*@Kt}tPK`/PKJConfigurations2/menubar/PKJConfigurations2/floater/PKJConfigurations2/toolbar/PKJConfigurations2/progressbar/PKJ'Configurations2/accelerator/current.xmlPKPKJConfigurations2/statusbar/PKJConfigurations2/toolpanel/PKJConfigurations2/popupmenu/PKJConfigurations2/images/Bitmaps/PKJ styles.xml[_s۸:$'暛v.׾z -+_I (2qLbwv%LL*̯,&,e~"z#ӔlȸXI7˹%^y璖4c\sYs17̮fz0vdbʆٕN]F^OXRTXf|G{WJbl==j釗 -_Q)agb~Û1MꇼJy- toW J`s/Mtn9ڻ0+Fa4*ĕͨ^ D/[RصUx1LK)[UQQ7 oA)=>Sˬ4 } ];b;$4磡G=WUbT$taM+ᔁԆuԙ> xgoip.wևD { ptLMA?(%V{̚W^0 &Q0v&1AB\E'\LRj%?’\,Խ.o=ˑtC\_绚~+FD2$>%^Dn4ǫ` Ki%*ަŊ^[?SCU ԆRHH~3:zٔ R^Gi "W\|*UPEb eA0iY(Vqc]`YgUb?G<-1p'L`z^t CK+3 *bJ`ʢDG6eGJFI䚘k$XG Hz?=ڌ /+!A`7/ wޖnx 5" Ӫԫ5;`rRW|w;o?Xh6 3RBU.NbyК'AyFW+/Yݣqq c\ GkS5x%`KUy1>X[bU sT!,X)K1`rPhb$ dw7K&,*!X"o-`z￿LxN]W`?~{l!7&5u8!K!z 4k@xyMOPh!4;!B/!^$BNEt<}q2f/4O_dC+6:XYKz J鎵J=l+f ֌i X15=-Υʨ΢R.s6 ig)YiTQ]+Rw%LJ~]ѼZEce]\q MٮI3ݺf:@9>QoiP+ ̟^X?E{}fx69X4myJ8vKn\I^a9C=| 42Z?s\b56t_i&8$(zȲ qr!sjzW_M^ArUBO9|\Wv݆f kDhʪpp-51q(GFiU`/|l"{=Yޖ{i_^~]{0&*pUzvӧG&+FrFs 1:.0%U=H݆IXHYOkf_|ҤWngo ʹ{ ;c@]lgux=<\pG[Y.ϚѶ%mkP_.>y{I{ǥZH`!nWMhKnQ8/=V1Ud4fU'to{M]Ǥg)oP E %;Xǽt4;^P%P8W ]ZQFKH: \S f \b"}}9g̈^*Z J~力:o~yx|:b0EC,_Сhx?gomIɚ54bTᬓSLfW+FZq41T鞚`UPPPJG Y%nW,!oI| 珐pXoI۲D_MISs _3V$3%jƄ.~0~xa,Cg yot/ȲD\)2Ql5g>Ped>0{/UYUƊ2 KzD%PKʦew!5=}[6ԝ 26Nv%w!fɎo/owC\*<:8^i@-x}­%dFr=\SD93x*xgԃ\AdmEl@tfu}5Mϲ]IDjV !m3HwX6+b* ({I`)ǿ嬣\ $D9^Bb i4<.^LAL'Q"2//<> >b]k>zN(~=1DaĮ6XqEdS "EmCѶ(Є*J[\qʼlWD 2ͨ$L|Qe4?ړԌ_ lᆜesCWγ@fD C`bK)p1\社d`17 4"3bJF)Dfa9F cơ\;Y)mZ¯r$@|YjoPkCIP=|[dl 1g)GuF[H83fӅ`A1ϵlbl l2.L$2sQƜ5Kc&_"g^teᑇ.>\f~ (-aɷb}&CoPKކ@!PKJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKJ.++mimetypePKJ2}QThumbnails/thumbnail.pngPKJ`/meta.xmlPKJConfigurations2/menubar/PKJConfigurations2/floater/PKJConfigurations2/toolbar/PKJ>Configurations2/progressbar/PKJ'xConfigurations2/accelerator/current.xmlPKJConfigurations2/statusbar/PKJConfigurations2/toolpanel/PKJ?Configurations2/popupmenu/PKJwConfigurations2/images/Bitmaps/PKJ׏ 6 styles.xmlPKJH^ content.xmlPKJކ@! settings.xmlPKJ(META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/solo_spotlight_off.odg000066400000000000000000000220641404105311000222720ustar00rootroot00000000000000PKڛlJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKڛlJ=ǭThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE  &!&$*'3(1###"'-"(.+++&-4 .<%1>-4<555;;;%4D*5A-;J-?S3QfWq?XsCCCLLLAKVBLXFQ]TTT^^^BUhKWdO[iC^{I]sFa~K`wNd{S`nWdsYfv]l|aaalllsss}}}GbKgOjNlQhVoSrTvZu^yZyY|csgwgxiza|[\ntwf`imjcohsv~jmory̋Åӈ֓Õɚ͜ўӢئިߩ:IDATxڭWV| y6sS:7\gŝz&'SbRny$MrCĹOIC.{`-La@[[3``oo [ڧǸ><{`\D]Pu:k[W芍CZߕ. EqӢYU&u+0Y1B]f kfv.[F&@[b'EWpe:ܣ" ,֭2*5Ad?Rd~Z;mG1^@vGg װ'N.c!9G d0.A8n[Mt?9*Q.}})JE_øUm6,-Z0:6f67V&XzPukI`sN)v a}Eji9l=%pL-눮#0ZXn&`^٫aj_ɓxL}w$Ap&ؠO6BӜ@p2c躱`l[VVG"w`Uqa1?5 ]G?o#V{٨[Ny?ڲYgwZxe1~:Ҟ2ý]C.}Ʈnaa{_/lZ5wkX#I/ýQL.adӆ3IX>K8~qqC>;3?cl&u [0 !KꁖRfd =aT ?2B|A 0n^o> :hK)7B N+ݢېB !{\7{zY)cÌX2 zۨO A‰!.Rs)܍7q bC;HbyM| xC#>P.xv#~_7 7<::zeaf'9}M{Q0ig1X^aԖ$I FR._-X2=IIENDB`PKڛlJmeta.xmlK ?bd{..jf" 8#NNTe |?{J xB1ͥ:y߁]_^$k5B9t>*@gՕ:Fu+"kNFιW~8Rzhs(28.(gWL=QAQB c{C:RkN+75kͺz 0^h^ǑnXozZ)[fdߤ>[2d&pz*U=،o]o{aP.S6>GOŒ(qR?$~1d!v( EHޣ8,%(r` 睙j_|"s oV [?eOBAM$F|.I·D 8FA6ÇNgw\Z'7XaNnzoPK(> (-PKڛlJConfigurations2/floater/PKڛlJConfigurations2/menubar/PKڛlJConfigurations2/toolbar/PKڛlJConfigurations2/progressbar/PKڛlJConfigurations2/statusbar/PKڛlJ'Configurations2/accelerator/current.xmlPKPKڛlJConfigurations2/toolpanel/PKڛlJConfigurations2/popupmenu/PKڛlJConfigurations2/images/Bitmaps/PKڛlJ styles.xml[_s۸:$'暛v.׾z -+_I (2qLbwv%LL*̯,&,e~"z#ӔlȸXI7˹%^y璖4c\sYs17̮fz0vdbʆٕN]F^OXRTXf|G{WJbl==j釗 -_Q)agb~Û1MꇼJy- toW J`s/Mtn9ڻ0+Fa4*ĕͨ^ D/[RصUx1LK)[UQQ7 oA)=>Sˬ4 } ];b;$4磡G=WUbT$taM+ᔁԆuԙ> xgoip.wևD { ptLMA?(%V{̚W^0 &Q0v&1AB\E'\LRj%?’\,Խ.o=ˑtC\_绚~+FD2$>%^Dn4ǫ` Ki%*ަŊ^[?SCU ԆRHH~3:zٔ R^Gi "W\|*UPEb eA0iY(Vqc]`YgUb?G<-1p'L`z^t CK+3 *bJ`ʢDG6eGJFI䚘k$XG Hz?=ڌ /+!A`7/ wޖnx 5" Ӫԫ5;`rRW|w;o?Xh6 3RBU.NbyК'AyFW+/Yݣqq c\ GkS5x%`KUy1>X[bU sT!,X)K1`rPhb$ dw7K&,*!X"o-`z￿LxN]W`?~{l!7&5u8!K!z 4k@xyMOPh!4;!B/!^$BNEt<}q2f/4O_dC+6:XYKz J鎵J=l+f ֌i X15=-Υʨ΢R.s6 ig)YiTQ]+Rw%LJ~]ѼZEce]\q MٮI3ݺf:@9>QoiP+ ̟^X?E{}fx69X4myJ8vKn\I^a9C=| 42Z?s\b56t_i&8$(zȲ qr!sjzW_M^ArUBO9|\Wv݆f kDhʪpp-51q(GFiU`/|l"{=Yޖ{i_^~]{0&*pUzvӧG&+FrFs 1:.0%U=H݆IXHYOkf_|ҤWngo ʹ{ ;c@]lgux=<\pG[Y.ϚѶ%mkP_.>y{I{ǥZH`!nWMhKnQ8/=V1 iLhb.p,oh/LDE?\4// a4zUT ,iҰxz-襝fvஷ;3=_0~f z'n:#HrN$zQQ#x㯌An'D,E`.apZ'A4[pCnJxj45Г]0Fk VT֙2MKet} jV{܁~ܽ>JcBl*D5z5/tC}mq^{}UpՁG1.s. A%6;NCu[`"Jmhy`F" --h) ( @J*6(`q2E>\B3$g4rńl>{qa.iK\, u1TKer]&>]ŕ@ͭ۲[M?D5)r% #%)4~Sn\8<2I#,Ԝ|hjLN߶ p!)e$Onث1fg4 GsD+>Ճ xRdAA3P%AtTn7{??׏sHI| wi]߼w/<[T R4 E3NFb`q~֖TYCs/&:Ԝf5SZ WTWBj' Um\H+#0Oɏ s1P |O}|uNjzKV$xM 'tƊ\mYhUt!b<1b =y&)K(x?6CX5JVRO /Ƞ>~Xk#1pЎyB!L-Yq`Y)5XS,DFI40f]H3T#+ ݝ<3#j'oL&hb^ZwNp(t2CM3%3rHoN $HZݐFV W]n$XnJ e}dw/M{Iaӫ;߇BadsC+I{kJ:7 iVltǤW޴7xȵph2t=!pa^,m{ ~"^4z  ?<(< "V>MaN'XpX4!<ǃ!ayxx a==Apye`Xx!^D`jn0&laU1p!WPy3 x8S }PK9PKڛlJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}0MKz-},S;z-K6񉆳K^S=^Wteu)Pwi;ْ\nK,1jɏo7#T*<:9M8h@-A;:q[IdFnq<\S$)x"4}(9K߃Ӝ/Z%ex̹Oܿ4[( +弑 1%Nm—fET9N p|:::-͵ OfET 1ӿ EJxGs:XOߒY5 (Yjm7lRP%i+ZYLsj1A9}zTyӃp-Ԍ_ 9ɧ熘:{%ыE`Iv抭/|8'$dcg0 nN zAqOF)$Ik3wZ Àqpks6|=5W$ VbsT[99=Y&D~.~ gfE ,M]2^嬄i]f!ʔӾd٣胉7󗷶H ->w-.3@)i>aD1nad F|G ˾3&m??PKghL?!PKڛlJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKڛlJ.++mimetypePKڛlJ=ǭQThumbnails/thumbnail.pngPKڛlJ(> (-rmeta.xmlPKڛlJmConfigurations2/floater/PKڛlJConfigurations2/menubar/PKڛlJConfigurations2/toolbar/PKڛlJ Configurations2/progressbar/PKڛlJI Configurations2/statusbar/PKڛlJ' Configurations2/accelerator/current.xmlPKڛlJ Configurations2/toolpanel/PKڛlJ Configurations2/popupmenu/PKڛlJH Configurations2/images/Bitmaps/PKڛlJ׏ 6 styles.xmlPKڛlJ9 content.xmlPKڛlJghL?! settings.xmlPKڛlJ(META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/solo_spotlight_on.odg000066400000000000000000000221301404105311000221260ustar00rootroot00000000000000PKʛlJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKʛlJThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE  "'#("")*--0034 78 ??;; """"'-"(.+++'.4-4<555;;;3;E8BK=FQDF KKMMQSXXYY TT[[ccggjjbbiipp}}}~ qrCCCLLL@KVFQ]TTT^^^JVcO[iS`nWdsYgv]m}aaalllsss}}}dsfwgxjzouw~ ! "" %'()(*,"+#,,*-02×ə˝Оӣ٦ݨߩ9uIDATxڭSG{%i(ЌHE[QS"T/v,B}iZ%VAJ].QL^eGs$GԡD0:LNߤB~vo`CЯ $5ų%^PZX|_|\ hmQǣSxD-MF Ǣu-HĻʵ=,3;&7.#Fr)XjУljNJഞi^Ǒ nXzZ)[fdߤ>[2dU{vwe G@9Oe᳅( Ye{iSG$!lq3b)%8JR*tyg|+'3I]\k!jat~ٟPbPkS>ɣ_ah'vs$8FA5o]8I}}+ )W2[PK^2 -PKʛlJConfigurations2/floater/PKʛlJConfigurations2/menubar/PKʛlJConfigurations2/toolbar/PKʛlJConfigurations2/progressbar/PKʛlJConfigurations2/statusbar/PKʛlJ'Configurations2/accelerator/current.xmlPKPKʛlJConfigurations2/toolpanel/PKʛlJConfigurations2/popupmenu/PKʛlJConfigurations2/images/Bitmaps/PKʛlJ styles.xml[_s۸:$'暛v.׾z -+_I (2qLbwv%LL*̯,&,e~"z#ӔlȸXI7˹%^y璖4c\sYs17̮fz0vdbʆٕN]F^OXRTXf|G{WJbl==j釗 -_Q)agb~Û1MꇼJy- toW J`s/Mtn9ڻ0+Fa4*ĕͨ^ D/[RصUx1LK)[UQQ7 oA)=>Sˬ4 } ];b;$4磡G=WUbT$taM+ᔁԆuԙ> xgoip.wևD { ptLMA?(%V{̚W^0 &Q0v&1AB\E'\LRj%?’\,Խ.o=ˑtC\_绚~+FD2$>%^Dn4ǫ` Ki%*ަŊ^[?SCU ԆRHH~3:zٔ R^Gi "W\|*UPEb eA0iY(Vqc]`YgUb?G<-1p'L`z^t CK+3 *bJ`ʢDG6eGJFI䚘k$XG Hz?=ڌ /+!A`7/ wޖnx 5" Ӫԫ5;`rRW|w;o?Xh6 3RBU.NbyК'AyFW+/Yݣqq c\ GkS5x%`KUy1>X[bU sT!,X)K1`rPhb$ dw7K&,*!X"o-`z￿LxN]W`?~{l!7&5u8!K!z 4k@xyMOPh!4;!B/!^$BNEt<}q2f/4O_dC+6:XYKz J鎵J=l+f ֌i X15=-Υʨ΢R.s6 ig)YiTQ]+Rw%LJ~]ѼZEce]\q MٮI3ݺf:@9>QoiP+ ̟^X?E{}fx69X4myJ8vKn\I^a9C=| 42Z?s\b56t_i&8$(zȲ qr!sjzW_M^ArUBO9|\Wv݆f kDhʪpp-51q(GFiU`/|l"{=Yޖ{i_^~]{0&*pUzvӧG&+FrFs 1:.0%U=H݆IXHYOkf_|ҤWngo ʹ{ ;c@]lgux=<\pG[Y.ϚѶ%mkP_.>y{I{ǥZH`!nWMhKnQ8/=V1ZP X@OO_|dhp_*I:^*IX-"yו(Υ=`e6hןsnSa7ax4z |#2S. ZK"Mpl2 dIK/՞Yb.v@bGGGh1 2> PvMf-GKU%L[;h.$I(gPLК ם@ŀ^Cc Hu߸Dn{kWUCC T0m…q["U'lפ-et2 稔 d8^8'p4;xP:mCCSkgr:k:&;ePRR˦?ayy_o4;CWP%tP8+'\s^hG9(u!j45̨/F妁Dr'äN*Z~MNo 9o\q^կy^yi*h E;WAcO-#;Y^j;5^2 XW'SS,Rۺ Q`>/Xc(x 4ع͖ K5-ɔ=K,t͵>bZyNp(2%⛄H3A!ylYr`#`azS} T5<"rkܷ]ϒl0xAl w<ه&x}i&f9g_̕-p" ېjy\,w;s&|jK{N֜<û| n -hgikza֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@ME޼I>b4hjFD/w&ź(..*uM$xCi0%mfr= nX'=̄â߹-& ̉~ٽL^9 T >w[ :V0[epY@&cRmBZl07(Mqt=&M6$:O01v\[_z_v(SNe&S '^.p?tKwWŸ1@1,,shPKY(!PKʛlJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKʛlJ.++mimetypePKʛlJQThumbnails/thumbnail.pngPKʛlJ^2 -ymeta.xmlPKʛlJsConfigurations2/floater/PKʛlJConfigurations2/menubar/PKʛlJConfigurations2/toolbar/PKʛlJ Configurations2/progressbar/PKʛlJO Configurations2/statusbar/PKʛlJ' Configurations2/accelerator/current.xmlPKʛlJ Configurations2/toolpanel/PKʛlJ Configurations2/popupmenu/PKʛlJN Configurations2/images/Bitmaps/PKʛlJ׏ 6 styles.xmlPKʛlJ͕t=d content.xmlPKʛlJY(! settings.xmlPKʛlJ(META-INF/manifest.xmlPK6 muse-4.0.0/src/svg/projects/solo_spotlight_on_alone.odg000066400000000000000000000214131404105311000233070ustar00rootroot00000000000000PK㓈J.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK㓈JPbThumbnails/thumbnail.pngPNG  IHDR<< ")@ PLTE  ""//,,0044 ??;; !!!+++555;;;@@ KKKKXXUU[[cciibbkkpp}}pp}}DDDLLLUUU]]]dddlllttt||| "$&'()+-.02Ym3I{IDATxڭkSP&FP[he i+EH?I4|xf29'ga{D(h#q/fL C]XX0s5Nu?hshGd,;#.UÔz%#jx>4=7c"VGhWhB|Y1֡Wthְk^%ְjتPCVϰti4l+5>CH5ҧ$ ^Vޚ^bG9X :.bVqEM@<1Zְi`1YR5>|/t{5#B7]CSh*t^3oQr=,ޫIzMƸ54w-&ؤʪ )Ԕ.4EܖzQ̲u-WdK b{0S!@qօ; JWdU8yFʢ[o!QE(vç!i.Î `Tv; SV?> mѩN(˳uh̄3ژZ]XX%xaUz\+hFC1/ eIENDB`PK㓈Jmeta.xml͎0} Fjf"b<[mk UY=mWaԪ$ Zcw`w~y\ZV*Re|T(++-SU+,sNE4S#կpc QvzLT͑h` Z1ὡFv3MMZes녶luYuyCsJ2#&usQ|WR mc< e(QS6 EI1I! Nw8b4 i4Hl%!v$fa8 qӍ.*juoZWqGɷZF2I3|Jx6<e;JBnB$U6M}gO"zԐmV.Վcic?:r9j=?PK)ٵ_,PK㓈JConfigurations2/floater/PK㓈JConfigurations2/menubar/PK㓈JConfigurations2/statusbar/PK㓈JConfigurations2/popupmenu/PK㓈JConfigurations2/images/Bitmaps/PK㓈J'Configurations2/accelerator/current.xmlPKPK㓈JConfigurations2/toolpanel/PK㓈JConfigurations2/progressbar/PK㓈JConfigurations2/toolbar/PK㓈J styles.xml[_s۸:$'暛v.׾z -+_I (2qLbwv%LL*̯,&,e~"z#ӔlȸXI7˹%^y璖4c\sYs17̮fz0vdbʆٕN]F^OXRTXf|G{WJbl==j釗 -_Q)agb~Û1MꇼJy- toW J`s/Mtn9ڻ0+Fa4*ĕͨ^ D/[RصUx1LK)[UQQ7 oA)=>Sˬ4 } ];b;$4磡G=WUbT$taM+ᔁԆuԙ> xgoip.wևD { ptLMA?(%V{̚W^0 &Q0v&1AB\E'\LRj%?’\,Խ.o=ˑtC\_绚~+FD2$>%^Dn4ǫ` Ki%*ަŊ^[?SCU ԆRHH~3:zٔ R^Gi "W\|*UPEb eA0iY(Vqc]`YgUb?G<-1p'L`z^t CK+3 *bJ`ʢDG6eGJFI䚘k$XG Hz?=ڌ /+!A`7/ wޖnx 5" Ӫԫ5;`rRW|w;o?Xh6 3RBU.NbyК'AyFW+/Yݣqq c\ GkS5x%`KUy1>X[bU sT!,X)K1`rPhb$ dw7K&,*!X"o-`z￿LxN]W`?~{l!7&5u8!K!z 4k@xyMOPh!4;!B/!^$BNEt<}q2f/4O_dC+6:XYKz J鎵J=l+f ֌i X15=-Υʨ΢R.s6 ig)YiTQ]+Rw%LJ~]ѼZEce]\q MٮI3ݺf:@9>QoiP+ ̟^X?E{}fx69X4myJ8vKn\I^a9C=| 42Z?s\b56t_i&8$(zȲ qr!sjzW_M^ArUBO9|\Wv݆f kDhʪpp-51q(GFiU`/|l"{=Yޖ{i_^~]{0&*pUzvӧG&+FrFs 1:.0%U=H݆IXHYOkf_|ҤWngo ʹ{ ;c@]lgux=<\pG[Y.ϚѶ%mkP_.>y{I{ǥZH`!nWMhKnQ8/=V1J Y],=^1*\/oY&_K^deu_j)zYђaKYZK;W#fWL6ྵf1baܷNN5F,$oɩƏ 55 BT12l̤ʃhXVfWUaQ) xq2D(%7tj|T&z \$4G}6dvm4UyfC$t$i߶f5-(W:bbJԓ٠RJ*4݆YЌ{= g`.<4E  !D8hQܾc+^=X<&҆VRw#Ld%?,De$<s/-iq[5-ɜK>qk cE&M(kʄ]߷"5JVR /ȡ>~\k#!pYc{X)D ["_2ZhfrYIY40V-4ObdmgASgv@[U+;(W=>EK/f^ϹcY:s" C z5׸1zM;}O0ܔnkt~.ol0ZŃLwnж+6&f8WGQFׅK:v`;;tKMs>[G;yb zNI'Yw#I(B/_$^ P > O7A o!zItkqCʟIB~YVx7UNWN禇 Q(L |2B Yh&"(O N ק n]l+ jc.辋}-◚e@Gg~zU_|Ru|DձxU/GX1$cGCDcHSasHtAa'Ps.n^HߓY]y~9cxY@P+<uK͔13Kqx1ڡi0EU,c&3 B%DzGN1XddD Wfa@F2k[>k7]ގGWwL&@G&iwݤ6(º> XnCxCճ7*6{_GaAZ_Qx7,5DzE4=$RԨ &'oDML`"haT}>u جUAXiI _'o,uf/ˮsU/߱Ks n{qi:.F9kY2]U!Zu[WnQ _]l:=҂2)uJ{SX|תth==XvdWmZ=[}bvaVU^3'M#{Z&L$zϓ9GЧTe*\ 9Q/3{L_PKhg#PK㓈JMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK㓈J.++mimetypePK㓈JPbQThumbnails/thumbnail.pngPK㓈J)ٵ_,Rmeta.xmlPK㓈JLConfigurations2/floater/PK㓈JConfigurations2/menubar/PK㓈JConfigurations2/statusbar/PK㓈JConfigurations2/popupmenu/PK㓈J(Configurations2/images/Bitmaps/PK㓈J'eConfigurations2/accelerator/current.xmlPK㓈JConfigurations2/toolpanel/PK㓈JConfigurations2/progressbar/PK㓈J.Configurations2/toolbar/PK㓈J׏ 6 dstyles.xmlPK㓈J4gb content.xmlPK㓈Jhg# Nsettings.xmlPK㓈J(gMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/solo_spotlight_on_with_mic.odg000066400000000000000000000227271404105311000240250ustar00rootroot00000000000000PKJkJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKJkJ( Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE ((11999HHRROO>DDDMMMSSS[[[``WggXbbbjjjqqq}}} 1 $9lluPq}t;&/2?   #+5BK\tldns{CN^clu~Ć̃ˈ™Ζ˘׊߅ۍѓܕƨȡ̭³¿Ͳ˾աߥ߭յܴ/lIDATxSApBÔ+1,l 5=.8htJ_@{-7voYaY>eַwx-OV<$:Yjb1rBv=`eLx笉![p*\:ȫnO8 U 5&ZOUz^I"9 `5'9aI]-jex-m 6;bMy|[wtL\~8'ڨ ABL<}d l*2W{v : K 6?΅T_Lޜ ZчǚBI&VF'R" C%Km0K=[Aam d\d504&  -Ɲ8,jLuʃ4&"wRufIo-5S'ka0,aS)eW͇>|PK1.PKJkJConfigurations2/menubar/PKJkJConfigurations2/floater/PKJkJConfigurations2/toolbar/PKJkJConfigurations2/progressbar/PKJkJ'Configurations2/accelerator/current.xmlPKPKJkJConfigurations2/statusbar/PKJkJConfigurations2/toolpanel/PKJkJConfigurations2/popupmenu/PKJkJConfigurations2/images/Bitmaps/PKJkJ styles.xml[_s۸&(ƾ:mi|"A Hp@в%O]@؎}g `]`SQY0]“ȣY$b.G'bދ$a]"*S)P[N baRf A V,2bif.BeZtgC5+* Y>fvcI6C0u1T~"H9Qloe/GkEl6DU@S G5_^J(`EM"C燼2]R9]ȕijuݭ:`D34iADpQ(m~%@;"j 0]]?'$~L#^\7Ѻn3Nr3ЯnH!ղl/G'(gG^kW4HIș 4ɴmç]&憌br?Oe?fπ)B1sIR1!3(zw ]N )vs2z'Jn'PbVl&g3&8a.E[ ׄ21),9Bpx\5,t@=O<}EzgP_ѳJ]~=yq=/g^[;_hdyg?C#BP)jk:;;%vI5|2O"pD,6Е/rgݤr"1&'ENPSaX yE07,10t2/%%` Wi#V~*bK_-:b9Vt/8]J/h d ժQm˂Ql|=x%mL Hkz}2˕n%YAtCY~8ׇdmy em)hᘘ"sz5W5uGI8ڭO¸a`FGGǻYS '8JaaiVkb`Mb]qgv7O#p<kKI-o!;s͞7!49IΞ yq8=BfOo+P;*c  6YDBT6h,V'(U.7(h1]@0b;2EIH͕}&2:ig(Q*b9$=u\qȤOGeBg|z{^T5f5kjKX6i>.&Mֶهt:u-\ZPLx((zF2qۘRۗi:FrVXBOvu͕]z<"h&6SEC͎2dT)| :f+p/219~9&Ͼק^IEZ!NnF/в_W-0>=j3YScIp8F9EBˁ, Rx)Zf-&ɣ"RR[a$)vۛ&r8kkC:2cχѤ-{/dW~w`q^Q:Hd񨅧+G2xp?uI4W @υ$a.>yAjL\J0R6$ArUJv=(Mx{v_4ZׯwwcE7k6*bVa@E>ob>wv;|˰ePKoS -<PKJkJ content.xmlZm6_!wE,ŻwEѴ\,HJk;DKe+ݦ`%kpp8v[#Ⴒra{׮m2a)- G3,hB)K)%JX)nv)^rΰb^₈L"e5zZ".]mIrt3kpW;x3VYaԮz*oE2mN {%e5wfs _:^ǎ5'WyQi␜Ʉ]{N-cSخKxOhjGQ8g˯z\М0gO *A-\ wàcRU e>c̸ͮ]w,|é$O'qV" pDUʛM SHM{H`zh)$.̈棣؁% {uJACqi?`pE>\h ]4= wHkw:O 1uAݳଂ: c Ksfl]ujɶ"!kyB7Z9 yܱ;(et cȰaY5h<^lǙS١ŃSOCȇCMuL8FA2$7ɈY(v^-.J!Bak7bZh[= "qVಇL0xĜsƻ&Q)w7y:.xOsB ?0z7Ys)Rx6MZ2ur$H1E_{kK+L֬QjUHuqI+0W&JLM1On*((KJ8bewEHI UJ}xg@΃?ip t__cnaF3EpB%,rڈTuab~d4"%TBlw|TJI ZrA+B+p*ùь Z{^RufgSݻuR,D\? 4}8vLxBXڵEewi(('09rLR f6j̾t!zƽxҝyh:EէgϴZ [ |ԾexKۘc7%WlGrS_3Íeݫ5˻*dJV7xV]i vmWH`r-i`yA #uW(k8I\[JQaTcϼTJKQ$>;(| ]w:fqWgKb8޲ ԥ牅"͉LVzwm>cn&_ԏF`)(o(n)n^|%k tg>ϴצMٝW: {;aP/ >ܫFC#*A4 rN2̷M$Fj\?*gRm˚ӓ+ϫN-V&h>C7*6g݅k k)-Pi܁qfQ8]NFcMcgPKYLV#PKJkJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@ME޼I>b4hjFD/w&ź(..*uM$xCi0%mfr= nX'=̄â߹-& ̉~ٽL^9 T >w[ :V0[epY@&cRmBZl07(Mqt=&M6$:O01v\[_z_v(SNe&S '^.p?tKwWŸ1@1,,shPKY(!PKJkJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKJkJ.++mimetypePKJkJ( QThumbnails/thumbnail.pngPKJkJ1.Mmeta.xmlPKJkJHConfigurations2/menubar/PKJkJ~Configurations2/floater/PKJkJConfigurations2/toolbar/PKJkJConfigurations2/progressbar/PKJkJ'$Configurations2/accelerator/current.xmlPKJkJ{Configurations2/statusbar/PKJkJConfigurations2/toolpanel/PKJkJConfigurations2/popupmenu/PKJkJ# Configurations2/images/Bitmaps/PKJkJoS -< ` styles.xmlPKJkJYLV# Dcontent.xmlPKJkJY(! 3settings.xmlPKJkJ(3 META-INF/manifest.xmlPK6!muse-4.0.0/src/svg/projects/speaker_chords.odg000066400000000000000000000216571404105311000213720ustar00rootroot00000000000000PKnP.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKnPmeta.xmlM ݫ N#zzXz`l1X鿯GU#3;3̔FE:i p-x}7~*ˋdpúFh7W\Ƙ-x%Vɚ[CP7cZ+ܐjg51xFBEƐ^[I ֐Wߥ?/ͽU^ygjG%LwA9;qmgHqA4IºF{ lL8&ܛ&Sa@- -GZK#I?ϡ_?#Bd'}tCPէ6 SIxYRʕ*Ɋ%1sΓj-eذyU% f8{ W<|xq,jYRMǓ:bxgCslww[:cH[A.%,XrpK~.g/3ӓGdyQho%jGOlasig)B!t9A4[)f(YVTW+h1Y;P{9/P;bEzJ9\VyNv M';G$M@)vbK(!K^nR?TAwaل*6zx:hӹ,nlZhfYF^QQBZd!GYzxzNbF75V+獵CEz!]Ky'.m6.M ["U#3Z"`2vO8sga1x5|v:vZ&U7{ ~Ttoe;;s}/x ~4>|2㻹U2^#hzTОC,!* HKB.QoDߍ+MΏ 6\Qҵ2\oSgل M;;yhno|6y<3f\M[а7i9?xa ʆ/UxoعZ"e%&|_vXPۯ_k{}tԛ[5ӃYƊrruq(R4X丱J:j>H,-._NPK| Ϟ DPKnPConfigurations2/progressbar/PKnPConfigurations2/toolbar/PKnPConfigurations2/menubar/PKnPConfigurations2/floater/PKnPConfigurations2/toolpanel/PKnPConfigurations2/popupmenu/PKnPConfigurations2/images/Bitmaps/PKnPConfigurations2/statusbar/PKnPConfigurations2/accelerator/PKnP content.xmlY[8~_aQicLUէΪj}yZȐﱹę ݶbb}Mվ,Ж&"Wx$78^t-42UcQD]5ˎ*XY붭-j^uWBf8ͭUy6Z Ͳb%omTZp 2/ PXA}M1 LSV&cvtڙmw/-r(lMM>!޾yp^5-a7\#8l&ݑp"Iɀ| !M,󺝝/6CTVXz"ơ} D=ܾ=l7WBI41y[$=*#GA-ˬ&U~!Nl)TIWRn}Y\ff@se\Վm0?y11gC).-^ܦ}W;Y5?*$YhIl+AM=`5KFMMRɝےBcuih{.ۊUA~U}ND.N_ǬBԉA7 _-$ZhqlP+k iq! NYq㢹;.n]YosȔv}`yc(\<VϬ/pݢD+Lǟ;_ϧdX>::#~##b?8?tNGJV֭/;AQuW|p]DA| 4 X8kL 3ĮS/\ 8098p$!VZF@c~yG.X wMF* 0sD^aT '. x0_` (^ /p xF Xp =x8O'@i`y?{CrU!_ )Bd@(/g*=3G=PK](hPKnP settings.xmlZQs8~_f␤ t S@ k]d#![ `Q% }ֻ߮ gbJ3)Au Iĸڕ?h"Q1S` >pеm)e=P& Ws! U&f&H f]LT,1(f鎗柶%]`KhTlf3Ct:ݟ9}~TnjoKgD2NpQI1bmOgXàa8˖U^vْܰ# +֖2I|Ʉ Y *.L.5<Óү'NN$0A 2L7ʭQmôCTj0 j1G|Rvt%#5o+)] eYoTZGpMԘ 2k)v‹Yz!?xtOc?.U^璳 b'LEYaQ!]2êO45 WzGs!Y2z2E2ԍlLC-.^mrsMP/}Zw kI=/dNnq= \Rjq cz4}(;9߃.5y\[6 :6m-Iګ7H/fmrU W1R3?I.@HnawOt,ϥcL{h&29%v""ԖGP#.]hV,{8f~!UԾfzL>נ.!̠xw5Ӆ7o O[M Q&) | ZDlTKvhfNe,_44*7ӂ`DRnPVBSe<Ԍ~XbrxT;;m.3]Yeo|R2̶ނ}\L17pR' JפȤh&ivr.D>NͥI2:63Q a2v|1 ~0/tRPo馨,I*xN܋QVֿ<ͺ@KxT1=*{+LiJ.Ҿ32, Goi]2hSU;lSkf)ʜ>cb6aHd.7;:\W@A0>E ܊GJGW^m<)|P,6H-k2uiPKp"#PKnP#ֵThumbnails/thumbnail.pngPNG  IHDRAA)9wPLTE $6>###,,,333===LQYbmp|CCCMMMUUU[[[aaajjjtttyyyTIDATxQw K2JHkKJ $^|D\(FtD=hvO`FL @0|Z 䵐0s0Wޑ+g@3:V$ MF@֒`(`0ƒ1eڪZ X!HdT {5/yTUP}˕ [`*+rꎫ9i4K8 cMQXVX[()V3|Ss1ɓ0 ѐl ] >|\t23{ &eYvE=q]oD1 $JV{lHuۍ8ȋ&ܛƳvlR@-&ኛiCoEq>< ?}3`SQiUq58I4Mf$Ǩ"BEPQeWl3ƯL[A/Yu~,7ĥ[_KxfOmN`oj|^-$|PKY.PKP styles.xml\_s۸&EKƹk&ig\uO$$IΗ}$]%($+N:3I]awA,!~yw,='4%^ł$xҤ^ !år&kUU, iszF2hϾrk(*I9*zY*0p&쓍dp Fg`E1pv{%Kpl6y STZwMPHjJ=CBTvFʙmCYP<\ kt[\\\Ӟ.e{[{gny83̕[$ vvHoEZFliBxA<.,fPBzfЂ~t~Xu:w0;{ؗ=T4 Đ5PꊖxHTEYBzs.'Idhv<:gh+a=)g<{o8G v`aMcd ?;'dY249CY2tq2Ο%?髓14~yzzBgE'h򿕨-:} %,O *O)6s@Q͢2²PA`ځT˩x{s(kF啔؉ik*ZWDsDjh\eOT=[:=E Q:vs˵FW!46eccsۨ8NDY1wd;(ߕYiycOGj E*i v019Qz1rx]txc8#xsThRU"SO86ZAn."fnM[_4ΧgM#Ǥk0˩{{Еt%@VEM48UΗ4{ ~Xoe;;u{/87Jb^ ڪ}T h{P}rhG|!/!T* ܑO&\pW^.=M|?/¥o E< :7ߴ>t޳ wv,sQ_G9t]lx붠ekQ}~,ugC< e9 Wvhws +[ r؍6w(|uoK swgX&.~WvE}Wq~[P ~ЯH9F=w"Z7o֑px@y=eUaYL{$puUxz8ތf`ӎmnT= c t2BGF[0 =:k|5Vq$hN*+~%Lz^+ T]lɫC|5N͘ ~#~pN#AhbU ?E~N]u4;;sR.cټVbFZH["Aտb|ÒݸfoժC kTV"Lo(_7oOH;;^fWMh6ƨ\msZWvy[6l}#ao y_PKOз DPKPConfigurations2/accelerator/PKPConfigurations2/floater/PKPConfigurations2/menubar/PKPConfigurations2/progressbar/PKPConfigurations2/toolbar/PKPConfigurations2/toolpanel/PKPConfigurations2/images/Bitmaps/PKPConfigurations2/popupmenu/PKPConfigurations2/statusbar/PKP content.xmlY[oF~@DS0.WQV}0$a3,c.k$n`|~\W-D}e ׶x+ϏoHdRdYU*MkEQ+[]*J5+ !ssz%X[U]d)p ۪( @,\5 C1-8'QԽ %Y,+FT Aad-dECXY,#Ǹ-$y@Uu50˹$y$}k&6JMD&D L\ eF|CcԠj~~L@SDhGY׆Sg )^|DEfĻ %t!ݻT 3 p(?,?ԏjCw&wNu__jphPߨˀh|DB%~AYPЀИx sA@kOI%`%\Aؒ'G46!J]{ @/Z@\#X?7 |qvXiBpM QvIDcJkI_"wKF_& DxͨRxJ톣Jq|RBAoj2|0^"/]OAa'gt0΄1a!F3_?_PKv;PKP settings.xmlZ]s8}_tgCpH0›l)D iS$qgg9Z՗ONk4Nt~mN/K+v=T }DeN )qSQ\f] )b>DWO iן@mX)(J>RI, 7.֟K[PcEy9%֒`M{Ey̲||y1y/(Bc ċ)xxf%%ul.kkC-X [8g Y`<ߔ@T\ʿ);/./RO0] 狝>_399L!Z"M QAW&R)$WQ`tL%sDcwlQD4gt@oi})ѻ`ڀ1ّ-L^#}@t9Kg(c$ @!A&Xe:t՛*zH&S#Hed.H[ܗ>fRs^seafAZ`6>DQjD8j k|"6TRmn #S?$!F%Զ`\z8 5Q"6),TN=R ޛ5sg4!],ڽ +C o ^$G?T$Nt]U~VH~@z*tGkC>:9bӎ:r ?gTeq$w’,i<5ǘŁFϠ1)m$NOxd;.<{﻽9 |f}!qd5We,Cx~kOwGӖ.wCNȋ(@?| ?6BSdTe*xfC%騕\\/^2 U 9&ԄAH5K[jwtqH8~&FAb h1~nidfKnYϓje.71ԓZN{TdNPcٗͩITΦ;XfTLmBlcɪ3 1Ai3ѯ5 3nbL n# BFU+kS%<Ɓ}󨜰U眃`1}?7^nOɊaoZZX7ѦNU9Ua3j3SQxY"T4]% nקG[wMH\|O`ЉmFpw{Yee's6mZ8MwrIUŷz`bqɣ݄ 1[_.`}d|Gxэ;qV guo8혮ƩQjv%sլ>`}nz7^#fq8q l/It)zKkm $ l׶viL39jKgXwQeŻ踅>}3.N QI]y?^VYd[Ї;~ɀGT՝XHTzOsPKہ@R&PKPZffThumbnails/thumbnail.pngPNG  IHDRAA)9wPLTE $6>###,,,333LQYbmp|@@@MMMTTTZZZbbbjjjrrr}}}?IDATxmo0 9ND;R["=ސrGZrlS'p.Jp#H! eSؐP%ؙ _3X7 0#Ƅ5 VO+ Sw-V,ـS Pp4KU0%d gcBb,i-,|(,൰LLzwWBFAMy"Gݟ')B|o7COS 5ߺ`Hx=LV%Xͯq[O":Dԛ,<ଦX]I ʂ Q \+6Q^(XnĪZƂ؞ۅ2p92"N( PHAƥ@/N^|cXA^I?,R^ׁɻbݽs܎To,rv#, kY{L7ošM3v㑰X~ـ3# ;iSG$I6x3H314AtIL$ 6 .;3*O;Bg+RZ"E9>P«)cp~=>K?7: ΐ}k/N_4v$ Ƹ> zr0 aË[^PK 3I-PKqO styles.xml\r۸SpѤlKM&O8igz"8 hy^$/ H$ʲٙdsqxW?߯S\D'([>~Wx@1%8.0AARXxsV̤c|(gas L0> C$  a1wj]@3al,(u1^ly ar (wc$IVl6H0wn~=Wtc0NC!ժ ^ =FIENLPN&&u;lV-OL(BFu.\Ӝ.!Ľ(1RxL׵ogE35vNKq ,-eHsE U[zf`A[:[`n3k;4M(:x_ +eܼ#b #H,hUv ?9.'}K1{ 4rDcvilȅm\}w3ViœwGm`RPP~MDL罃w Kֱݺ&S]Ժ'=eq SZP/nL[WT~C&{ bA(Ma&|NnA6v²맅DЪcY(ccE7:5K@䳏H51I4½~FPMP!0UgerX)OS!oI5W`~1ZhGa@;$&7GϜȋa"na- q5~gOLUJu0>}>9AB,%;#rb/#mPeV)\dTBo9().ro(X4_}eR<9NH9`N `wJf?u,SyB&R~ b]Qs8/n{΍߁T0ذ&+()eTC:S ɇ㜊8X&YCnJu}fg܀AX[𱵎A&Ѳ+-QiϻsA Zi5[IWh{J~>1_v0V'TS>٠x>r2CuWCB.j)]|Vڷpl?+L/lG%[oeAA`:؞ n!cJy&I-WD. $"~6{^- m6lCPc D3EA|TKLr0/1K=)8G) xϿU91seA LR%8oa+a=ǩ3c*Bڃ#xGC0 L&14:C/~ Mb"<Cyqh _h(ojCnlaū3{f%,o  nfXA_&sX`T˩Hs 5HQ(abYo :1mLKMnvHNWUrcJ!iL<݋-\ll^qqɖȬɉnZt&D xÂW뇞F;gd_mAkl^csĝ ]rח e9 WvkV]_ X*ۺVswJ:['&@݃ggXz3e)nzBWm3& ~Yԧ .AKs& ^wZF܊ÞeUk;lH{ pU V5UiV.kBY`1ͦ*]ULś0Kl+ Q?[@j/[،iH,佧qG`ru^JEF-s_6kЧSgC'uFN;^<79Niic(z5*^@Dck_3{ÔdEW 9g90w#Osw<Ş՘Xᓗ(7k&[98Nlr9vŠ4aorGɹ+1WlmtdDSDaR7n(Dm!(Jrs8Xֻ5~C}ߕZy^€]qIUۧorOb>E@~DVֲYG#TB!-ɏ9?%NkĿ^81w<͒_RK%p [K ֠%Xax? QW?#uёI]5f>Jpڡb@*Emsl;:VY{[lq6zPK SPKqOConfigurations2/toolpanel/PKqOConfigurations2/popupmenu/PKqOConfigurations2/images/Bitmaps/PKqOConfigurations2/statusbar/PKqOConfigurations2/toolbar/PKqOConfigurations2/progressbar/PKqO'Configurations2/accelerator/current.xmlPKPKqOConfigurations2/floater/PKqOConfigurations2/menubar/PKqO content.xml[[4~WD]P[e.}YX@ oI&qi˯NR'm:YVd^s{u33Nhqm34pјӏм& :6EeE邯Rzmn\6-qFg8+[= *H~mnY.PBg}EXAl 6%hϰdT?V.JYgm vl7vjǨBJʖԖծݛhsdW8yǥZ愲{"715 D-7)zhc. qh|{}zn\֊>]xޏ,̝ ?di) ZRjn/1#b֚h$X49xYHyf%t଩6gCIs@ 3Z5i]>#ŻadT}m,Iނvr29m8Bݙk7KF231I.)Չǯ0ɬqW"YbA~8;ɒD.#=,_2XBlZh؂4ZϼipHa+QoTpaC= {;oP[`L SQ-H9*:%"#2x@e26}G+z9b-s5 l3$~MXNg|PRs e=ލ}_1M r6[W8f=^='=w8VT"KiPw|KZhNESv"2TnH S|8ĈfJ+02bv$g(7uRD3 ǎgl83w0'u؊ɎӌH;[%QRS(q ,PFbWPFR~$>?:Υ)RFwt%(@M:'E;N(5'h)iUc9Z9,{HLҹTG޿SG2nZڌ:}˙B?Bo|0ɦd P<e;> 'rӤ#<|;ʿ[X{ 4>usqs%-2O9(]YDž,z@MO6Ze+ߠ*=KF:`}CAn+wg6(,^V*s|8>\ɛҮ FW 5T1s~o.b8섎`Woe=4#T5"Y5PBp7U۟9N1~55u=O@'uTwʧX02־ áˠ6dN`OL= 1If"Gz)| ʖ`[x&q'?W/r\9u÷`kRIoV_h04F88]i_p8g/n$:/_^Vge>M<ܼ/E_LA_Pm#$g=@_ 9jGݓQ 2%#Xhv|'8wi F$`Voy'Pý#:EۚV WcB6^8bp }iHLRݙhԃϗhN9sfn膀 u1ә͗ 2 2'Ȝ sd2%Ĵuj9wr5{\40x2 ci抵=v o'[TY^Уp+ZթE s 2<]V*pj{9 L7 n'C[T?;?͟PKr8PKqO settings.xmlZQs8~_fbH:)I@z{ȒGCNKPb>Kl]wW?> fR4q58IĴpЍm)e34P& W'j=\~5DJ4$L7A7L԰K_ߟ%%6Œt'Ӿ+l}QV׬b=s&iSVCwQ3/}8A1A}$ńMZ>\yb%DNKn *e c̀A,< ]R_5e{8 /x-_n^z_5gˇEن:>EEк78 җ#ПS?m {ᷲ#ܔX>?ݧO+Dq|\?c\/m30 8 =BFRK;E#x6WmehAgF岻><͐3 z^`DlK$m ۦ#ҰU{Z}!z]!T]vhN߷x^UHtp|v|cGO\ l=”h ,F);S^-",@m {5rއ)R9Wng'45 rU5vŃuI 9|y` ٸ{o,y;VxвnPψ0!̀O/޼MQL@ >L=r\L6%`:2?;26qU.cQ1 Dϼ9rfKtϯ1жfD #ӕ;Nţh'vcκ.x"ab-gP½&TD&y6+7D )$zq]9c.ME|ѡwKe׌Rp跾Fˎa8ӈyoWzK7EeIBP57@tAlLZKuI)liTd6LWugJ83n/3rzxtƚ' n>6#HG=8&@Q/ `Dҭ,^Ea[[wqhn2딴p3뷆x[օTZCX`YS5ɄV02\ B/qplhZM&0wB+.m"me|Cc41wǺ{@ÐmIAc"%;9Xk>8߃,(Y@b\C#{z_zp9;NWT~C&{ bA(`"|VnA2² T|-Z |KD(QY}E`IWL0l UEP4n:L'y|ޅTp{5/' `ëV_&IO|bY7]X-  LWI~Y6cړTRoo6F+` =~e{ t~ p^s2gQU'&KU/ +@Cb"xC_y\/"oyHm+˂/\BSTQ/9S,Qk]=(`)/)aČ/'@ΔU)ų k*L8AљՕmii @,UAV,~(yLbO!f$hUssw&졅ZO+(O ܫP彉m V=%c%i{i]`zě@td AB^L>Te<&+k)+֖f׷o ئZudGՉXjM ~gz2xJJD=W?kX czM<$ ` %9O6( (^'ŐWݗǐ缴Z Ow!T* FH&ds.Qt+M/.-|0^ .m&* Z]ew]*3i6u݅ZՍlgxLn%>sk~l Q [I!:v˛0O,xsDSށr6%_ƩӞ^9{οw,ŃJ5#VSlcǤSޡuFN;E 'ۧic(z5*~##UH]9k~['n3ûfm+*^!t@({ťMFR%4);M*7$jKĩS>8Ĉz1J+02b=6v$ka^*)sK5a6{`FhŽadt$&`2hcF~BJ0n[ٚ%Z26QC(#)lhoyEÐA":Ψs`n`e ]Fg³f\q.Q,. `/V82e88j@a5IRFj{jJƭ_gub9S("q"XOP~<٠0LtMqs%Gm^xv}^fg1sAY7*#X47Te[Jdt[ի){aAR e.p|7 '9A oPYcVV]FIIQq ,ھ[YZ=&`鮚ZS3"ߛIG&0Q"J2Rb:JyK;;[oN71h>G |dYN/ժO*"F`5˴& >n%*wC6_x>F+Ta텵0t _j+T{aakp@uC_Π9 3hAY؁v1Z,Wv3fΘ9c?3̳nϿ0ؖ텫zp@3 \ ~=h1ʔT%} 3΀;'ܤZv߅.]-_R&u3gcp{ZKX!o`tG}>xogl$U~!O9=̑xPKɫ8PKϵqO settings.xmlZS"9~šګ}@~{'%l!J-*'w{oa@L2dDqa@!p/*3i9GPID()Fp? >5;1 NeD LI1>B`jL\/e bqPIVt ,juI4uA"uggAu0%JwB+aj[@}bW5XOg鮞GutR( GP1 xz>RԂs<}:ԍlL#m.^mrsMPZw kI=/dNns> \Rjq)z4(>;9ރ.5w9x۳%Q6jV[B`Mx1䭪C͉O4D\z0!E*{ fq!QȾelӊc>נ.!̠Cww swG YMPSfG'(C* >L=rO$`2?;0pSfyOSohNM0& 7(k`j$&\F#-͈Ŧ#D󇹣?V"  u+\fgE(-'t]{y{/{MMVlf)R>1 I`̱u\yC0㩹J 8/#mao|Ðq!/nʒkDXj Q"G:RE$؜s~tH<$Q+H+!Uj9XP]<Pc푹L|FJrKX3 Qf)cObO%dܛAe¿q? 0b ¸I*]|{V>R ļgᠷj˰ !`$mBNoh7}PK`}$PKϵqO.dThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE $-3:###777<<<CKTZcks|AAALLLRRRYYYcccnnn}}}q IDATxV@OHFj ᒘ TQh3AWZQs˞ /( ;pN7u'mʵ\". ^Ȥ :\X,Za.e`G,b|ހy"lvLև͇%R#)-4 -2^>"``Bw`)1ٝ79R.܀I^:2WREMjQ}%pJ >BA񡕛БrMIgԞ*-j3McP,-RŖ$"978q'J&8-GhK64(g(Nلm%p S8$:VӠz1N;mڌS JyE3f` )FԊTF9#f9eLW(cwpgb,¸cB=枚jx0aaֺ #J)?.VG~9@.c 5&QJ`g-Ci^*Q;4ES\IX 5 s^U"1zN`jtԘG( ﱯS*S-작p2gjL-\){}MyDV}R4]urbxʼnG]+4MgA4J7qŅSҭH}wKCo]=[-K`sN߃GAWކZ_M{Xu#}\n?{}+`tNhW|]࿏?8ߘIENDB`PKϵqOMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PKϵqO.++mimetypePKϵqOF/Qmeta.xmlPKϵqOc U Mstyles.xmlPKϵqO>Configurations2/menubar/PKϵqOtConfigurations2/floater/PKϵqOConfigurations2/popupmenu/PKϵqOConfigurations2/images/Bitmaps/PKϵqOConfigurations2/progressbar/PKϵqOYConfigurations2/toolbar/PKϵqOConfigurations2/toolpanel/PKϵqOConfigurations2/statusbar/PKϵqO'Configurations2/accelerator/current.xmlPKϵqOɫ8 Vcontent.xmlPKϵqO`}$ .settings.xmlPKϵqO.d|Thumbnails/thumbnail.pngPKϵqO`41!META-INF/manifest.xmlPK6D#muse-4.0.0/src/svg/projects/stereo_off.odg000066400000000000000000000207721404105311000205260ustar00rootroot00000000000000PKmJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKmJU(Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE !(",#-%0(5"""+++!.="0>111:::%3C)9K+=P.AT1E[4H_7Me8Nf9Qi>Vq?XsDDDKKK]]]@ZvE`~dddkkkttt{{{GcHeJhLjOnQqUvVyY|]`abfhjnq΅K9IDATxw0}FiWZDiuPPsJ$H<9<沈@TN&` oj$AiКʺjWjj045:R=iN4 Ѯ4f^~By+›: szV':0H@4Pv8]ٳ?Bxעl"lX3O&1~pBS=<䖄1Ja~0U~0U:t*^jCqf+Uu8xphK#xl{ ]BpG76•#)=N^MkOv#p3ÿv`)UfjW,&deA[mh_ejۍ2IY5;_|7c.m;y*g3;KRO>coWo0r>Âon\VYIV%eUeUKwIV\ԍb9 YoѴ<$i%Qͪ'FIQ qW#UUcUQ)*Il m6 ]g8 v6 A̱8oǧxx{Ͳa7QKX3zxQ>:cfvLxo]F47)XjRУlj$zM+{G6YȾI}Ҍ5euu*>\o[\o{ࡰl|B5"g+M($)& J0+ E(YI!CLM9 36r&뵯U}>g鷋diMf >qɝZI5oyχ8tZÇ^4,B'5`+hq5?U V>|PK.PKmJConfigurations2/menubar/PKmJConfigurations2/floater/PKmJConfigurations2/toolbar/PKmJConfigurations2/progressbar/PKmJ'Configurations2/accelerator/current.xmlPKPKmJConfigurations2/statusbar/PKmJConfigurations2/toolpanel/PKmJConfigurations2/popupmenu/PKmJConfigurations2/images/Bitmaps/PKmJ styles.xml[_s۸}IqboLssIz$8 hY}OrC)S$&vaw]o+ɚ*aD\]#eЀoaG[ NBK>z=pUZxIV Z@IV)c|8e ձd:/l]9 ;#zfsQI!OBڣ,M}0I$kEgrSϛs(pLњ; /!@D;Ygo>!9߮];3'~FFRxOK0Hg{V OV!(hᨸJ 5RɵoDU)Q!P#os3gJVxN?JHO;7xUS9ѐ 777| {ҔCɘtu+?,'Hk#u-qf :< :81o u}8$8 zRYFX JjMUMF'=7 /*< ,%py/FPNB eqy2`gAEtMѼf-XpѳjѮk(nR!zq (5iMgOc9ZCW0J=$l%p~܎Ǭ`!8絣5{VmzmkrKǻp?Xp.!2 O0^`4ChS,H, 5NleƹhҰq=׭Ñ)Bޱ X cS+mV4AU&sT#,RXzr)m>Aڣ XTG;0Y3>ctd9~YGp8 .&{x LnbޟѶ%m~(BrϊaI$iK-&.ii%oU{`4]PZi>(p_,/-cxLfMGȏpUsEvPgk]{c׺{XFv;PKS' 7PKmJ content.xmlXK6W -rw7@SAr%JfV~ghɶ6B_{0M7z3`*wݷ?܉, ۤ"ٕ$Jm齻FPզ%SlDͪNkGo^͊ҧb5;ʈ;p_;0UԾz&*UA2Q/kǂW/Vzyavτ̽ cHÉ;YTx`Yuؒi:?]v3CC5Z28.s΀_|2H-yfCT}ݒ'3d9u/BH^O>f !$q7{ë8.0u廂)kƒ];-s$رb>gKn֞}\J5[ğ&fB%)mUH I͙2QUZF<ŗ?QR}Q" 7stfI{g0 Qw[O,WpJBk!ϡd})ͳ%pIΰCd- RȖ| 2i&6%?BVn v),$ZF/Bk|tݒ,3ՅDv"< ,2$c%HցMsD}8x0PNz Ez63[pt1dTK5eit5B֤^0ye 5Ȟ'5&-Z~(Tym&dM/|,N~#gS@EHG I8I 'bA־÷јDM/͈&"N;OA;I k4|_3|!qebb. @s vp%@% S bC/Y~Dyi\ulL.MMGj%{c7`7ooPK!GgPKmJ settings.xmlZ]s8}_tgbHl:)I@}u=¿d K&vv6st?79P!]Qt /D1U$UA#il3ZGuߟf('~OޮF":p}:mb&yQ#h;aDQz/^V!yJad4O"yaحU/T~}Yk.r5Wr8!>r|l568' ccg4_NkF)$I 3跮Z Ðqpk!s6v5STzsb b۰ vL~>Ar`or$tK7c™4{fC kY=2xK/8 ylY2/Yr/fN<ǫwQ8ÃH7[,QLd F|G ˾3##l??PKA&!PKmJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKmJ.++mimetypePKmJU(QThumbnails/thumbnail.pngPKmJ.cmeta.xmlPKmJ^Configurations2/menubar/PKmJConfigurations2/floater/PKmJConfigurations2/toolbar/PKmJConfigurations2/progressbar/PKmJ':Configurations2/accelerator/current.xmlPKmJConfigurations2/statusbar/PKmJConfigurations2/toolpanel/PKmJConfigurations2/popupmenu/PKmJ9Configurations2/images/Bitmaps/PKmJS' 7 vstyles.xmlPKmJ!Gg content.xmlPKmJA&! Usettings.xmlPKmJ(VMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/stereo_off_one_ring.odg000066400000000000000000000203761404105311000224060ustar00rootroot00000000000000PKrmJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKrmJպXXThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE   %"-$0 +++ -;444:::$2A0CW1EZ3H]6Lc8NfXsCCCIIISSS]]]@ZuD_|D`}```nnnppp|||GcLkNnRsVxZ~]`bffkmoq΀=>IDATxmw@*T&bJR '|\γ޽;38~"pgadcܚf I4o!ۇsІ]z,_9^:CQZ8젟EOxVXaJ8ԥWK)K`QxbrغYyUR8z^"(u׹E܈V К?ѡrTjSAc?I`>,Fk -.1ilGϱ+by)U#`CMO&:7vK*0{?z],O&Ws?+z)2民D]R1esXő^+9ZI9,IENDB`PKrmJmeta.xmln0E f F"uUnZ1uM;6X9s΃|{<8QmHTU1Y%܀m*EZA 8SAPZ-†$YTC吂4./gw>m]E2RIeG"#״{"r*D Xp)N-5>EIŎجU0VN&-Ubo{DPK{F_QPKrmJ'Configurations2/accelerator/current.xmlPKPKrmJConfigurations2/toolpanel/PKrmJConfigurations2/floater/PKrmJConfigurations2/menubar/PKrmJConfigurations2/images/Bitmaps/PKrmJConfigurations2/popupmenu/PKrmJConfigurations2/progressbar/PKrmJConfigurations2/toolbar/PKrmJConfigurations2/statusbar/PKrmJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PKrmJ content.xmlXo6~_!H,;X>lPݰWFd(Ptl)ɔmJXL;~|A V+zxr?m9<"IOWPGʇKz]1}zA Eswc^9}p:X]. Gpj|3B,x NUD.G&s>okd:7~~_sfa&o|!K_zPBw=϶)3VζJNZ fm ,Rsn3Lzǝf?M#S&[5!54.U I]˿2e 'Wx4/v!̝sQEL%q!݋nP|‚;z&JԸxJBM0_hdm,;;6RS 4XΘ^.oIpnvV}ӾGΥ%|rm vK=znaZouU-Y<Ⱥ z^# _ſD9Ci`n\|E ';`ݠMn?Rj,sT~0~0%|/0h}DwEC:s=F$!I^lNtNB8>P3#HAK&hi:p1i4zEt%1%835z)Hh K#]y\xL$pU"t.ku/DRY$YSA ci5Kx({Sf1_9??XPDYY P"LX, ˧j(;ˌ-Zd+it(ꅪ_/7kE^c+7h\,S "#GFcӔ' <Mdz~I酿 &.U*rn?hiՓr57 4U|PRD!pvzETϦoHE+jrJ1aZ>zr}}XRzX((tM/lO٬\9-\KQ I”3PI5kFM)E\!'ӬWW))ὄ*ٛ{빘2Q/9% ]*{[IeT틕 nhN:à߹DZ!eNCD_k&$aHL<2XoZKtpmI~2V=L$\]~OjyDGN[dɖ^pV,3ei_\b @%[;C } Tzz !uґ1B=1m-o]PKz>.#U!PKrmJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKrmJ.++mimetypePKrmJպXXQThumbnails/thumbnail.pngPKrmJ{F_Qmeta.xmlPKrmJ'tConfigurations2/accelerator/current.xmlPKrmJConfigurations2/toolpanel/PKrmJConfigurations2/floater/PKrmJ9Configurations2/menubar/PKrmJoConfigurations2/images/Bitmaps/PKrmJConfigurations2/popupmenu/PKrmJConfigurations2/progressbar/PKrmJConfigurations2/toolbar/PKrmJTConfigurations2/statusbar/PKrmJ]b66 styles.xmlPKrmJ/ content.xmlPKrmJz>.#U! Zsettings.xmlPKrmJ(ZMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/stereo_on.odg000066400000000000000000000220601404105311000203600ustar00rootroot00000000000000PK lJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK lJzl=1Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE  "$!($+(2/40;3<9"""+++111:::A;LDLJQGQDSMXJ VQ\T]Q\Yd\gX j\ gajcobnkpgsd sl|s~q ~{DDDKKK]]]dddkkkttt{{{vr {xw{ ~          ®«ɬǵ ɴ ж׼ݻ       l"?IDATxSWOv+c iS4aPjKi)- K:be`>#DlܻG9Ow<=s{z^H9>92ɡo{gQ ѫwЊ_t6D)Tmk04NjF C|gh ws0k"0Ã%; | 'Ym%Q^0~E[z}HP.J, ƒrmI3=d-.h$d]?i nÊ,#!(8p,qEAV8,&{'t8&ZRw~9WEW!Uw<ϟ0#yp *c cUЦpdteX^Y9\n~/`8QrW/S]=IHAS/_YLco/l1n/pH%G*M1ym]!(Ԍh9xH ғ&WQ^-uݡN8- __㲶ؐ^ |K}ua6!´)5.bO,[Ie7H(36lTk!L:*KLX϶ebd.}J^c6Dou^eDΖeSֹ_@^Jsei?aT^6=!ǐ{{&d݁帏Tﺞb*qȡ0T< ~ZV.__%rH~16ѲYh|1Ց^1] I>f;1ciO\wޞe1ksY4{QWtXY(CAUTAyy}Zc=fʳw4Tc\KpTq_;.[dVhmݢPCgAiSE4 T oMAM=|#ا)CYmPTYh6jzXlCyHWG>;{k,fÄϷs_u}K*BIENDB`PK lJmeta.xmlK ;[##uQu1U+5#u96X/qjD,s\ʇS/\6D1 .bBs z~hri:pRit {-0D-7R:.g YM;F?`xHbE2z^7bjd:͍;om5e-%(cS0gڴqd#yUOk)[ˌGXsQ\׶^>1x1X yJ(Ts-rG0#&'1N JpEQ2JVRw ('84L36r&뵯U}ߡK Vҿ+?G.S+]=A8ݣis4XNߜZ` >alpfLc :Һ懠*ڇPK .PK lJConfigurations2/floater/PK lJConfigurations2/menubar/PK lJConfigurations2/statusbar/PK lJConfigurations2/popupmenu/PK lJConfigurations2/images/Bitmaps/PK lJ'Configurations2/accelerator/current.xmlPKPK lJConfigurations2/toolpanel/PK lJConfigurations2/progressbar/PK lJConfigurations2/toolbar/PK lJ styles.xml[_s۸}IqboLssIz$8 hY}OrC)S$&vaw]o+ɚ*aD\]#eЀoaG[ NBK>z=pUZxIV Z@IV)c|8e ձd:/l]9 ;#zfsQI!OBڣ,M}0I$kEgrSϛs(pLњ; /!@D;Ygo>!9߮];3'~FFRxOK0Hg{V OV!(hᨸJ 5RɵoDU)Q!P#os3gJVxN?JHO;7xUS9ѐ 777| {ҔCɘtu+?,'Hk#u-qf :< :81o u}8$8 zRYFX JjMUMF'=7 /*< ,%py/FPNB eqy2`gAEtMѼf-XpѳjѮk(nR!zq (5iMgOc9ZCW0J=$l%p~܎Ǭ`!8絣5{VmzmkrKǻp?Xp.!2 O0^`4ChS,H, 5NleƹhҰq=׭Ñ)Bޱ X cS+mV4AU&sT#,RXzr)m>Aڣ XTG;0Y3>ctd9~YGp8 .&{x LnbޟѶ%m~(BrϊaI$iK-&.ii%oU{`4]PZi>(p_,/-cxLfMGȏpUsEvPgk]{c׺{XFv;PKS' 7PK lJ content.xmlXK6W -rw7@Sf I.EL$Q W~}g%m=&p w/EBn0]L&?>&kw2MD]KC, hzSKݝ*7j7%-/[M{+ڜ6h*#vKl}DTeBP꩜|9I%D^sQ~wT;|&Uq{V9:\SE%9ʹЩ!R+jխVkq ugٵF̶TM2OSeu j#Ah?ߜy{!v*D55/\E:٭/zC0\Y89".[A\=RK" QZ܁u2j7ؖ QjCsdt!ɷRLbRG!F#yWRMv mM0L%M(3A1!{/Awz1m{ !+'@sRY[S+cŕ@ͭf`[&~ܳ0hRmtӌ@dHXYmzÞ4s2I/-^,|hjNN>i[H}CR8I8]ݟeΆ@KDZ(VRt]g`$%J-J`O&I!p{>f=I^^+GKJ#yW|x.j4Dc -^XR4tg$vF.[lIt5ghŤY/4תRUNH=5*q[ pe6(Hy /b2}?Aap;cF!*nS {]kNx>2wx gd+*| 6"=]xvvjk"BqMiAM!n՟E=I0&KYY"zf2vb$t U C %XYhh.KRZt֥)L<sq0gPK)V@`PK lJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@ME޼I>b4hjFD/w&ź(..*uM$xCi0%mfr= nX'=̄â߹-& ̉~ٽL^9 T >w[ :V0[epY@&cRmBZl07(Mqt=&M6$:O01v\[_z_v(SNe&S '^.p?tKwWŸ1@1,,shPKY(!PK lJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK lJ.++mimetypePK lJzl=1QThumbnails/thumbnail.pngPK lJ .meta.xmlPK lJ|Configurations2/floater/PK lJConfigurations2/menubar/PK lJConfigurations2/statusbar/PK lJ Configurations2/popupmenu/PK lJX Configurations2/images/Bitmaps/PK lJ' Configurations2/accelerator/current.xmlPK lJ Configurations2/toolpanel/PK lJ$ Configurations2/progressbar/PK lJ^ Configurations2/toolbar/PK lJS' 7 styles.xmlPK lJ)V@` content.xmlPK lJY(! settings.xmlPK lJ(META-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/stereo_on_alt.odg000066400000000000000000000225531404105311000212270ustar00rootroot00000000000000PKlJ.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKlJ^ڦThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE %%")&,)2,30:3=9"""+++333<<wKt:}6"Os߽gp 0VJ|Hl :+єa\Mxݘx~<'?s?ۼ  }*@?,O@; Wd=۲9"SX#JlCe}XCru(ko ~p:bnn;w`,kvE#\/B,XzR:+\Co)JC#X-Iw@˭5)wQ,IVc0@+96=:e_НDʢ d i6ءr2^D8Y$'QSTvh qra$,{PĮmEy q7C#k #5a~1*}ZZ  O6 N+ةAMTta޴;XCYU!?Xcrf %v]'z$= !|20StwoGV3_6(}&7 SK#R㮡̷ c2.<7),S8RĞs AvB(ܨEeŃ ʷ*76$ ?8}׽ۓTYg uﶪ2]W#vW:'*09N^%[Ӗ 3riS),Pi-o GS?3 ȋ_mvTUpU @aRc`F",Ik#VNCbKӬ.V[!LV H0=lEVΒ)e1Ä4$*mhY2'7aM{ugniz`XSՂo?@եV{n#ɨڃ^~dx{*ъ^|BruVl2GPV-ltS2ܸBʎAם $ז[3̅R :]Wՙ֦ 2uT!FYh;ItUY K2~yp|l6巡!L: {m<ߺ/mf&t7@if^Oz7^aIENDB`PKlJmeta.xml_o0)+Pi:uRo6%3&FSm=WPrT1!i5ڄB SkQm84d<چDUF"b)Q-iƝS-mږ}'G<tBpmkO1 xP o 5HƝV7sͼRz 1)mQ8UM<ͭeCf(.s\+[E1 iʆ( (Ts-r#DpÈ ItV(% );,O|Q<LX"i_C7 gRIhM#|;8hoV1=ٝϛl طZx5pfLc : tҺ懠,ڇ/PKu.PKlJConfigurations2/menubar/PKlJConfigurations2/floater/PKlJConfigurations2/statusbar/PKlJConfigurations2/popupmenu/PKlJConfigurations2/images/Bitmaps/PKlJ'Configurations2/accelerator/current.xmlPKPKlJConfigurations2/toolpanel/PKlJConfigurations2/progressbar/PKlJConfigurations2/toolbar/PKlJ styles.xml[_s۸FcVcܥs\=@$(! ZVDI $(2qLlawv%{wTL,="aK?ibHD\e4WAJ AJV.rr(hn.B/eFdc5++YfvI6c0uS1VA*XdQlG{Zbl3;rNBMm\s%qH9pr6 -oF*Ur44D]-$-E7+h[ g*dW6#j=-?~%k!oXbەB4 v4poo$ST:AA\d}$ޡ7vgxl4Ȼ<R5.2mRZe|8e ղd:opsvGЩ!ރ(D&|5ڃB,U}0J$µآ3[\|Ɣ96S4&N#D!@Dp;Y,go"-r~s7O)iИ7Mn=b;CAJ8kϤ_v̠uF`Es_RLF8^522~ J?_Ø9<#T*ctQR:CzJ %ޛ{*N DC'+ z܌o^cIOƤ$_ g AZ + N4opC= 8[7)DQM!$iԑL(꧀")dw=[\l[y zOy{IV4{$Ǖ72C( )vm0C`iin^ܣ1ݏr5s&Va@Ew{o{cgsױw xPKknP` 8PKlJ content.xmlXo6_A}ۖ:a톽 El%Q 8_;ò¶΁LO\~+r/«#*Myg^~wBrբH1ztVHnX-jE3pխ{f;ۊ;'nfr2#:e/\SBAYyfC%/7vj_)]aetP9E%Pël};gYdW[- @]Ly<'u_ήByS%Χ5 7@t_Ǽ\]= ײf+FS+vgn'V cU\S~," y6o?,15ȘZVw5rv* IhLO}-ZA&Z1F[[W0RP0'8ÄKt3;KLwŁE03'ƥ˱j>tIrl ݪ?㉄&%E5\KϊQ V iH&WȻ59fq -42]I7Go /8% P(Í=k -zD4i)uZլ9ArhLK}|@d16$>?w/la9+cZ MH҂ߔU__ɇ7(wCt?(u ;NX-ķ Ge)k[ɇii&cž3{ܞpm0.jEн\qkoJRp}Gl/BF4B)BVs$`dqTXgT(qs>T7샨0UZ>*PvcmPVΧ+'ܩ0NvbvFwf<"==W})]/خ(n p?fI](;Lv'[xfPCb½א`x?t9LB H+ĽB\6i H].HV%Y&4^0(L` 8,Qr#</p(Q@;VLH dNu %^4& 0HH$)YŊJ+q#<'i< ,!!nQud$X$$mzQ(I?|Q؍h0D&NI@&X 4s(tɂr Y)0-MpVM"O"g.=#Q4f񽜔X,I)v7PKffPKlJ settings.xmlZ]s8}_tgb iaBBÔ$lv߄umd#!^R !tՕ'PIQ*eD )7Kύ_.xQ!S` G8]u݋I &#iѵl3XD5ߟfdzc&~OޮF 4Bޏp}:m ŘMEY^/|m',˧h2/[ dVDp_;vk Uh_n֚TVnиXg Gƺ'<Md #o#|Ʉم &.M*ra?54IZd*rveJ!JLPxIJ栯>a֡*Q^D%!(F;zi c5o+)6M|  eɂ7*.#%j„vGbwL~7s3f1="(32wu+E&}d^2 X_>t3c]k>zJ(~=|>Faľ6XqES "E mCѶ( U⬕H47G7=G@ME޼I>b4hjFD/w&ź(..*uM$xCi0%mfr= nX'=̄â߹-& ̉~ٽL^9 T >w[ :V0[epY@&cRmBZl07(Mqt=&M6$:O01v\[_z_v(SNe&S '^.p?tKwWŸ1@1,,shPKY(!PKlJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PKlJ.++mimetypePKlJ^ڦQThumbnails/thumbnail.pngPKlJu.-meta.xmlPKlJ) Configurations2/menubar/PKlJ_ Configurations2/floater/PKlJ Configurations2/statusbar/PKlJ Configurations2/popupmenu/PKlJ Configurations2/images/Bitmaps/PKlJ'B Configurations2/accelerator/current.xmlPKlJ Configurations2/toolpanel/PKlJ Configurations2/progressbar/PKlJ Configurations2/toolbar/PKlJknP` 8 A styles.xmlPKlJff content.xmlPKlJY(! settings.xmlPKlJ(META-INF/manifest.xmlPK6!muse-4.0.0/src/svg/projects/stop.odg000066400000000000000000000170051404105311000173530ustar00rootroot00000000000000PK`!L.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK`!Lmeta.xml͎0} 20RUSR3Rw=`# טNTEb{cDoX& #R6C_^$kַB9 WG^,6qoյ:Fu',KiHk:QaH4qQ0(ggM(Πh`!N01᭡Fv3څVcYZC!:A(LV6:,`羹id{k}`8o]ϜBM.84#q5OxU.o(*` I DJxEQrFgi甬):G$ϊ4+MK o[>/8~y\*o[?i}B ֦z{#U%XORݏaE `S03Ԣl8mVՎidQX^9V%h>?PKy5.PK`!L settings.xmlZ[s7~3^q&0 3658m&VPv$1Gbq ,ө|ٕё}y(ͤhyw"i{u+/_dhR!S` G8]uˋhJn n)#iMG|̙xly3cɱTSh4|v54R'09봁6=e9z}E\^V?˿Wu\vl%a'[CaحU/Uxўl֚ԽVn>KQaB#Gc!e{y?δ5@G[4_2av㙿&>LLt;f^5jѯMgkC+!*LPxI}rs0 (tm]"!*F{:1XJDx w&.D#iYvD)5eBGbna,g xtɢD\*2Qlg!@[Kլ*cEɆ&>9P MHC+j2xu[1el୼\nKԫ';a 3d<7CxsIK0EO_|Y8gsE(\̈½\ 9<}2f*)ڌιN'3 Ikլ=sw1'<^١mDqE"F9.v8~ Q:z%J|ggE#/%JމZ0> Ԅy$X=H<\wN4û?rA-mNŃuI )>:3 Y{S{%QG|}H}m#k9ΦsE0aS2KxBs7! KgkܠّO3*r֯PGpCN&&(~WgY7PX&ޛkؒ\G\L$x,(ZM7z9/e\3JRګâߖt #ơ\;X)mJ¯r$@|g~c7@tAluBd c,ऺR$X0*͆qu:-&Mi8XP=g[d!-'^y5c\)=a9` ě[Cߗމ .ugx,tlŸ1A=1m+]PK8z!PK`!L content.xmlXo6_!hv$k-)1%D~GJ)J}6x;}Ƀ-jgqЊU: o~X,c& MI+4]FV%U&ik%>:k3Jdu 5;OW`_;x7U`!z&*G5ċ=g:,(vr&dooo#+u-*%,lؒjfR`9g<2NeX#@hԵ v*"Y=y-B8WB[E_EC^$Tzp"`N\Ey)hG@tk(B(,V*5#)h`}0bҸ:FFO`GH+6 {V:Ґa4o#Ik!KP6 * B|34wgp-7;Lbڣ{*qd08]oDSm}M%3"̭Z2g`ǞAb'E3BR`ExÞ'4sHEZ<9RK}_DFLˇ֭]uᦿ״瘊DaBQJ WU۟tЎA q4 yX?ZNpd L*/Eq8l4F/a œ5Jp ܚ#dpdRPAY^@0W DRƳŕqwNϯ桍`ys"xZk%]eny"Ȱc8->hm!BGj xcBM?w6-N$҃tYYL4yFڶ:AK[@oRn;#J \sCp|0OcnQ㺯#Rm?FS qg&,b1.i ȟNPK/`̾PK`!LConfigurations2/menubar/PK`!LConfigurations2/toolpanel/PK`!LConfigurations2/popupmenu/PK`!LConfigurations2/progressbar/PK`!LConfigurations2/statusbar/PK`!LConfigurations2/images/Bitmaps/PK`!LConfigurations2/toolbar/PK`!LConfigurations2/floater/PK`!L'Configurations2/accelerator/current.xmlPKPK`!L@Thumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE888p ;IDATx! 3 0&'G_q~Q0 cH], yق!p[(IENDB`PK`!L styles.xml[_s6Oa}Iqbbwt2smizځHPB|>OrA)SlNfl .7eܻd"'g< WW\_HSE"* J@8/xW2_Rr /DAs+pz)3'+]iEXaȒ5+H++w%R"+b;ZqJ0l6gٙpryyjjp+C).VIhy3XU)%!j!i ,`.帉\ݮF{jxMh?]W%]eQoƮbɊfnW^Ѩ&ص(ý9ȾLQcq|8z.؝1>jp#JPBHOʴIkᔁT˺I BH-oip.wAD4{ Lyh jTTybH *b nqS3CP`Lј;AV,‘diuԿ~*OILƼ~cu3Ϩ+ԯ> Re^ߑB3יj|)2w8 bHD2x`tnD?T1sxFTnKE$KrOdq?K}IJ6!=?}xFW'r!%zTo05H~~Rlx(ȋi"3fcX* 22a)ªiVkb6`M]Y$pH%U].qK_D@wS8+tLJ\6E7*q.hHWgp$i'K4{ ҏMַ?~fK1w(;d8L>%žZ^B!41~_$BNEt<}q2/4O_' :k%j\WlPu{kCBMQ J5J=kj'Q1@0b{Bȝ ΢$yV.r: ig)Q)TV]V~.Ϥ@gBWxMך\=3f-j =+6i>.&vMv:9r:}mBP<iIZz@@u.&\C}QLyce>l4kb\v/$匦=)nN mzp`#Ն)8hL fD6/{6&H5}]ׇh&h3ӏE$DŽjdڥ91>0L)UVis?q. Zoֆ'9\x(( =bd8`Nkh95=|& 9*l>x:.\yD-lY5&1<h9l||쉏Qd."cczvL,nnKQ\iǗ_ rnl2*h=e鳣5%s_9FA_99=Kz> X #yO)f|x/ORbi+7~6CBr|Mr@]_.×,+@]lgu=<\pG[^nbѦ%mk$PdJ]zIcŰs$^ &⫧+[2SB0ܮRiܶOQ8j\;t?a|T ~X5Wk61yZ?[ﵹM zsSw wPK=J7PK`!LMETA-INF/manifest.xmlSn +" %aҾF S5?mSfcg?V'xl+aew)4=$*sirDU2Ir$i`uv$˙-lnSzc.qu:(:L=;茪i 25ZQi',/u!p4:1FRxLb*M@T|HOiٸ{388m,GssASIφ%:1/T&A0N :iGwPKH PK`!L.++mimetypePK`!Ly5.Qmeta.xmlPK`!L8z! Osettings.xmlPK`!L/`̾ `content.xmlPK`!Lk Configurations2/menubar/PK`!L Configurations2/toolpanel/PK`!L Configurations2/popupmenu/PK`!L Configurations2/progressbar/PK`!LK Configurations2/statusbar/PK`!L Configurations2/images/Bitmaps/PK`!L Configurations2/toolbar/PK`!L Configurations2/floater/PK`!L',Configurations2/accelerator/current.xmlPK`!L@Thumbnails/thumbnail.pngPK`!L=J7 Kstyles.xmlPK`!LH eMETA-INF/manifest.xmlPK6muse-4.0.0/src/svg/projects/timebase_master_off.odg000066400000000000000000000264121404105311000223660ustar00rootroot00000000000000PK{AO.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK{AOmeta.xmlM0(W&VBEV7;M䳴 U>yzƞXWN q@jn61%A/ՎMGƘmAhhdl B)it}N9Z:9R,fZƋN Q:RXBQ*ٽ_V{eb7N{ N+*i 5FJp(+988# ժR vO7NڹʳG|eg#̭W9Axh#Ŕ"'xLpHS @Q1[QY%)MR(sL_s\J^I' >'Zjc'uX>\G_$BOJd_o%=|hU%meNq^`à~!3x}xkPKk /PK{AO styles.xml\r۸SpII&l$q Ep@ȲI_H eu8߇⫟wIpv /'(^ px IaY1BMoAi>q3Yzɼ/{" ,a1WL*]@a,,(um1^欕i fr (wmάc5'I@P#AxXemp<T&xtg>g&%;1A9uGhPf85벱Z4`bBOKd]52Hܫ.$q]pf+[$A \+s]nLnʹW ɼ3²oCjJO,sg󆙇gmh^W\۵C3` T]9Tb/ eOfs)ʾ5G.->.IVa yE?Z7^B4h>9&L`>!W\ZG>|r gwgqYN5Al U=ilQoAJ%|VyʫM֭VA<Ƌ@{Wf+ O`_I.bO>snzadlAb@exs=q-=Y󬦹?˸'x 2K#G4fK= Hpw759s+¸ϘvJSj7q0.e(y=UlNvi:7+Il|O l\C_/o[8_ GT~C&{ bAOT0"~VnA2²뇙Dl*cY(c# Jy PSLb2^wQpsn<T ~WxAFP?0ڰ `l]]FљޖZ _&I0gOx݆pѸtxu */kj І4߳9,lƔ'W !nzxc>kb? cG?Dt52JK@Ab"xCN|i1 (,g[O!+K.{`A ]P,V߱g9;`E;8w):i22G?ٜ' (5MxrBA-"MnT,yG(Da~X*ߑԫs|U~NfP 1 `/G@s?cvE`'k֔syͰϟ+r@Jx&+p4(X4_6N^2[n)3˟1 ezADYy**Mg -`3p^7fKunU5}ѹj2:@ߙ<T<:9+/ ƫ6ol{W𶵎A:Ѳ -Qiϻ}Z Zj[JhRϽj|,czϖ}NSyF22Uб1iFQSOAtڀ' yH4gC۪h>/lpShč1SL)?pFmA!H$Et͆- 5e^ NR\2RxSOWUzRE RM?WpF#ω:K)2I9֗r!?:phFf9i%4qg8J iG  g@Mchp4B/~ Pt1| ]x8}}4/4Nˌap4F[[i՚=`}ΜPza]]VnPTH,/kT0DATFq %HV(aby'+tb (7"a:]/|Z2U|K!3!vt[^,l^q YZnO?HgLj7:rÂcPLaW6$$PLW%91jQSAmNJph]4.Teew)uYBB4؞cFt4tj>+%:0ֹ ̪1wٌ2߬t\>ԡ؃y|#=B(4/;+q~ 9u(7J }|`,qh*N* |N'v7v <܆P<'Q'ܑZOf%x64˨Va@.+#Z v hЕjwm}h0sfS.pRu#3ĕs>BxPVy&̒z w6^ Quԝ-}/[،i79oYÑJ5#FSlcЧSޣuFN[E r9-m\qEFo!ȟ;Ô?/b,#贘, H}Éz0j3 9614Gg U#ɾwʳnXsuQ62XSWǜé6ƨ +ͿZyCmZOAF/Hwx)Bs]Zް>)+t Es~J Lj7Prb8 Ӝ𯶩NpK %7w(^WG$S;^4(Uc-*"QI!IQ`9(?ޮbE埕J5>e PKE !WPK{AOConfigurations2/floater/PK{AOConfigurations2/menubar/PK{AO'Configurations2/accelerator/current.xmlPKPK{AOConfigurations2/statusbar/PK{AOConfigurations2/toolpanel/PK{AOConfigurations2/popupmenu/PK{AOConfigurations2/images/Bitmaps/PK{AOConfigurations2/progressbar/PK{AOConfigurations2/toolbar/PK{AO content.xml\m۶_Q:8 oP}]&'_nx$(ѦN:.#%Qb9I(],v<\_^mWLe~9# NR-:NI:%U-P b= /&O~12jC&Ggk&2TmD"kq`(īJv6K̤{ks;mdM 2NvYq \Uf#KdB*-rAV6Zj1Q亖TU:û+NJʒU"OzAw׫;&\Y4$zst]nT iGGţ08u7BAV, 6ؖR^uM})!'a$p,|n׌s:L a(*}R\kk`2|~<N9y-އZw'3|R2p赸p~n?߄YvFx>o#u}I:8O{XP“㻦=\WRo"ltDhkƸ.ԞB2"TziNp9qYkQBU(MTJoXo ; jd&Bm̐v P0" ʌ\'i;RfikF)O㴞(@duuE0/J?J}Ua."~]Vi+x=RؗFJ/HK( z9_yj_8MMҶc}Wsw:dUm⡾L$fw:T; AZ.OGuQd8ZP98t"{}D<#WA((_ (>Pzm8Kk8ڷ4[}| X(,{,{ u_BUUS$r4!}MȽi{2]>vFYnJu\8<`w9۽B/[}~ lOܻe7jNmoO /^md'5z鸓Cwax3[ThnU4SPno&:f2,ľ!s[}亪!(u߲d[ fvZ]}}<+Q/c/\J=RA6ijcl-ptX0ZmRbM[R?8`G=Vȉ,K-y!/{GpӳS{ɛO:Oz2''4a>7'!c^+QEKVm:*0*a*q$\=T_8MRtI wJtm9i'u I;wͷ~F@褷a~ѣuD?f\gqg1g}WW H-/^7Bw; }w\*_9zP#LPswL-O+zu1A?8CqOj7O*=ԑ+2CeZPl[nxkbا`= ŰMWcnzv_ !2tY wAdx&3D+heȽ3̅g.cq!߻ C:Bg2< d#Cyd}2t\U0/yŠE}&ǩ1B^ %/`PGf9O_ c!3PSr0UNz4Jdk}=?s\|b3&.%;p]Jc}J{~@Ռ.;|_όzf3u"xIQ&=b}{ &%PR_uvmP2(oh u{|x/@(?Ipvێc;x2oW(-0<!{h%j t D@Лy:x>kQh1ñW_m7c0v Q %3 "x  ch^ߋVɼIe ޳FWPK 9DPK{AO settings.xmlZms8~"fڹ!84Ik&c^ 4@x1/ᛰP"K>I~I)Blܹ|`IϳZ־3z9;52' b: ;5.*@2+j8Q˩(cי~!c>ry0sߙ@w 8-0 (P!QnA/}&* I&KwAu(Zl>GBGRrKa#LESɡtzne]FP%^H>S|g7(^>X"O&Ψ +zC)YvK$Dj>TG71E;)`rY:LC(!  2EծT8խFRٞ*@#nMuAZok\0JC4ReM| yI b=QOY}DmPmfIiʌ"&O fJmθr~GD 5Q"r)!*,PF.>:h!̚Ih"Uu}|>=n߃RYeD1ϋ{a.Nό'۪X'x2RLJZ$ΫHS ʢjc8~wQiT2;0 B!9a̻[1d:>!~OXD3.ߟ\|y5>o <_(˾qc>Y s Knkh,|8ꠥ+݀O91|@ϥ%৵9!d`xRU2tO%QTNKEx2Tf|S&t"X} :_Ĵy՛"h hؗ6sNM1~nieVCrHLjteJEa͑Cz18tTXM\= S%A ZG.? UpU_x`k$fʔx^Z7nfOӌ`o̬_PP9 ,Q|;\ZD[gcd=3Mj9HǕm [y_97\Qk6GܙvH?|{ȲZeOqڵg^6֢LKJ c2n0wRHke3P} 02ΰnD~rk }Z[am`N<[td/'yLjp6b͊%Z0\10z޸'d)'^@T՞4|{_2j#^`_PK%l(PK{AOWWThumbnails/thumbnail.pngPNG  IHDR<< ")@#PLTE   $) *",%1(4,9""",,, -;"0>555:::$3B$6I(7H):L+=Q.@T,BY0CW1DY3H^5Kb8Nf8Oh:QkXr>\{CCCJJJSSSZZZAZvB]yEa~ccchhhvvv{{{@_FbEfIfMlLmLpNsQqTvVyS{U~Y|W[\Y`aceghkmnq΃ IDATxڥ WX"X) ]EH#T]in yCpw_l+h~+!E}=TVa,ˮv.Q0:Sl-c',ZiNBcQ6IBXJ[=z*-NnݞM>V^\l^GB\@JNCׂ/W_,XZxpP _Bٷ Kt' qب\{ɥR_&Zvp$_dyVQ RSɂ, nSb>{}Tl3ɏrz3/Va}9~' GW{Wp{ B1ۅǡS~Qnާ1M^\8^l1&bH+4ts5d==/]X"[)q*I>KZ.óRF>zP/ᙒ# 3Wb{vlĮ$L,g&Uyd[Pgz]]z *L\cGMyy#c =[iZ$k`̩b4W«33UoIENDB`PK{AOMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PK{AO.++mimetypePK{AOk /Qmeta.xmlPK{AOE !W Lstyles.xmlPK{AOConfigurations2/floater/PK{AOConfigurations2/menubar/PK{AO'Configurations2/accelerator/current.xmlPK{AO_Configurations2/statusbar/PK{AOConfigurations2/toolpanel/PK{AOConfigurations2/popupmenu/PK{AOConfigurations2/images/Bitmaps/PK{AODConfigurations2/progressbar/PK{AO~Configurations2/toolbar/PK{AO 9D content.xmlPK{AO%l( settings.xmlPK{AOWW!Thumbnails/thumbnail.pngPK{AO`41G'META-INF/manifest.xmlPK6(muse-4.0.0/src/svg/projects/timebase_master_on.odg000066400000000000000000000274411404105311000222330ustar00rootroot00000000000000PKAO.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKAOmeta.xmlM w/ol#+UUV[륵I}&81<䏇ޤuu#Rs#gŇܼ(.0k zvl:2ƬW[i$epڇiJ?vV3S:.lHK /:++D}ў.VPoJEUs^X]Vù'nZ YtA?rmgZÅuonoŮ>nM3v㑰X~ـs+eAN%YL"qrxE i0( 0a(2DŽ`Lr`Dg\wiCC<\0R)y I6Wa3]mWqpl[k~I!E;U 89 7A?i:BXPKY*/PKAO styles.xml\r۸SpII&l$q Ep@ʲIH eu3+|~[%-f9?NCtq5׿{E㘄xpi}sWNRJˢf!p*K([ ϛRY:(',E+ϊpUu7JO+6nHWoe`+m3m-X%%l3G0Bro k-Uc :TFtҴ?.&%;!#Ya͏6ĔYuX-[0]?qק%26}.wM"Ȭ[;k@0I@A׬=[3-H(pLi$]X2Phva v2hly`P6ukֽ7-'1hRw$-0̨m6w[{$iaY"p =Cjp e(!&C3ʊr2f_a&^A+q3i FFazFžQ(y)TZF3p^cW؍p_I.bG a|A@ej{O[zpԚ}wSs 5!_jn#ˡi6P| >ӂv3Vj<9on  (Dw;|Rۭk~j;ߣ$𛍕ZIl|0n\C_/okX' GkT~!?죈 _T$8"|Wn T_aEBC,/Z :lQY>>1$.5 L$p?27bo 4M:F^i@Ɂt*Nt tl  t tpuϻ[m}J:!/KGX!UX{31?cO_y5 z=-8M'{ZUJ igOǍ[BuYqp_;`]L`X! 3` `(?S> I# j8OЛ8wQ㒑9s4!En{@Z+ 52N!w.Y!XЂt7;('(-V:ж֑=(U'Z}%7-xyO8Iݞ^K~Kl\yPO/i! cvhB9O6$"Ǿ#ì+øxV*-Og!F|>Kw ' |FyA{ 0iQqz,q!l Y, %)*A$ ]9.ܻ:u}Pc9!S-8|$pE ?ȅsF uV$u4rB~tо՜&T;XU]H{<4sS5 /򥟿8GdE249CE2t~4F/B8=="C/3N(ojCvlöWgw; ,wj ^*w)rEz`e}^㥂9"Ru/hPRR0-~Sb+˝!KF׋RKfJ}Ӓ;mwye[_, 56&>&'kݞgoS'jdHT޳ʹݶі䏤骤uN :ꩠNVJp h].[TUDew q{&lRP! FlOa!Œtw''}yƉ@ ֦*L:M1VUPOfhI^߬t\>jC9񳗍Ǣ{ g HiS` C:x/A" RyN^:˟ t:l\iz~tiAXN"8h .O{au]~~f9k.sk9M"{6{}P9reGOpmA6u _ЁȾ{0q]Mw4N2rz=+{*Ò1HUfQ//t]-w3Q:0O(_CJ?8[>0\fR5iVـT e%C t4z}͆JՎ'xLn%h~l A [I>|˛8̗tSR3l|5͘ ȉRo4wc)'6+q_׸+[]d$O)ANuzKr"6ryq,|o9Oiic(z5*C"̱ѵ)<_=.[Bt@,I@O581~Uqp~EډWlðzy$l{;[n=QdQtV+6AoQ9:I:Ih)<[ W'I'yPFWk2[wcl[Vcx> J'CW 4 ?]Q'oРz{t7|s9G`gYҐu '4؎',|6Xrm>1 %3C *{]uMߔFC} 8aZ^5\{9I SzmÖӊBx9[P[ru L+y9!E"B]$ߔpQ$;?d&T>>')H^ 8>/ M/[/Hr{]Ag~~UR8Y1kڃU%ԥWq0 `]ՅQ]ȠX&a\R]u\+˜(Ѭۨ .:e%I. #a6w"~c?3܆kK 'eFr_qz"Qq5F$*M /L> M M_U?n >ObYR9/yX@qy]yazzM{7kJ"ԝ\BuU<֣PC>tjHhjtQZ_j=0E!AqG1~H$_Ombb:(J GA Qy GZzwPQNZ#P&9/}7 Q.0>P`m)jm\;< G`:_o 5@O'߶(VU'{<hܹ4A$  V1 \nuvp>EfNL8C$^DIsK=V礣קF@jH m#ޚUZ!"2(!};ncۓ |UUyM݄ tиѷBQ\t^釲8zVPoC(3D8fsl8.`JL%ƈꃉ6}B!5Oha> ʧx-y.uZP똡؂ g$]`C(eV&$NTy~j+`:Ѝu{=MkM DB% W  rf3V-u ?^AI~I)Blܹ|`IϳZ־3z9;52' b: vk[+v]T S|g7(^>X"O&Ψ +q5aCͱ0F= %9&>zC)L삗=n߃RYeD1ϋ{a.Nό'۪X'x2RLJZ$ΫHS ʢjc8~wQiT2;0 B!9e̻[1d:>!~OXD3.ߟ\|y5>o <_(˾qc>Y s K@ݰXꠥ+݀O91|@ϥ%৵9!d`NRm͠3*=쵟!?Bwj9 ͩg 7?qr'f 'm8#lV,*jF!Cƭt2%d{z==0/8N#\@߷֮bB`]O--ېbY&X$$!lI?MV̙a@ Z`+ U3&EvgEi{lѬ7u=ѻfm3I7>qM6 ++R^MCm@ף6}rIb~.NdfaZZY5.]V˘;6^%_oJڭOO₦UONNLcS ]%pjQ 5 R'm>ѡzU[KCERa+V0ϏX=3 M-=ՆLHJ|Zc@Z&vE2اXGVGZTV8a mU ƴGo]o IY'-_|-EÃOQ=m|Iz$!EZH{ )^jT\axZd?6,\ӂCI?6aNޕ7E~7-';;;\_aM Ռ̔tZWbj7M!<;SaIENDB`PKAOMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PKAO.++mimetypePKAOY*/Qmeta.xmlPKAOU ] Lstyles.xmlPKAOConfigurations2/images/Bitmaps/PKAODConfigurations2/popupmenu/PKAO|Configurations2/toolpanel/PKAOConfigurations2/progressbar/PKAOConfigurations2/toolbar/PKAO$Configurations2/statusbar/PKAO'\Configurations2/accelerator/current.xmlPKAOConfigurations2/menubar/PKAOConfigurations2/floater/PKAO& E content.xmlPKAOe fl( ~settings.xmlPKAO3 I"Thumbnails/thumbnail.pngPKAO`41^)META-INF/manifest.xmlPK6*muse-4.0.0/src/svg/projects/track_off.odg000066400000000000000000000231541404105311000203260ustar00rootroot00000000000000PKUrO.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKUrOmeta.xmlM0(Whk+J!@HܪCbG)|v['w^ؓ=*zQkk1NP)#Ԧw&~dY ťmL EKӑv F)XWB>5SOSѵukMQ+σCŌ_kx)^:)wuIG_ K(P0u`,_liw3c[bOT״))!N0\X_K ,nV ?4;i*> Xpj>_Q5@`RNhB(C$wȤ^0)IJV\UIuc+;~36\1RGT>'TFj'}pX>\%_$AOڴjJ3d8K)zxJ289A?ik6&13x}xkPKKe~/PKUrO styles.xml\r۸SpѤlKM&O8igz"8 hy^$/ H$ʲٙdsqxW?߯S\D'([>~Wx@1%8.0AARXxsV̤c|(gas L0> C$  a1wj]@3al,(u1^ly ar (wc$IVl6H0wn~=Wtc0NC!ժ ^ =FIENLPN&&u;lV-OL(BFu.\Ӝ.!Ľ(1RxL׵ogE35vNKq ,-eHsE U[zf`A[:[`n3k;4M(:x_ +eܼ#b #H,hUv ?9.'}K1{ 4rDcvilȅm\}w3ViœwGm`RPP~MDL罃w Kֱݺ&S]Ժ'=eq SZP/nL[WT~C&{ bA(Ma&|NnA6v²맅DЪcY(ccE7:5K@䳏H51I4½~FPMP!0UgerX)OS!oI5W`~1ZhGa@;$&7GϜȋa"na- q5~gOLUJu0>}>9AB,%;#rb/#mPeV)\dTBo9().ro(X4_}eR<9NH9`N `wJf?u,SyB&R~ b]Qs8/n{΍߁T0ذ&+()eTC:S ɇ㜊8X&YCnJu}fg܀AX[𱵎A&Ѳ+-QiϻsA Zi5[IWh{J~>1_v0V'TS>٠x>r2CuWCB.j)]|Vڷpl?+L/lG%[oeAA`:؞ n!cJy&I-WD. $"~6{^- m6lCPc D3EA|TKLr0/1K=)8G) xϿU91seA LR%8oa+a=ǩ3c*Bڃ#xGC0 L&14:C/~ Mb"<Cyqh _h(ojCnlaū3{f%,o  nfXA_&sX`T˩Hs 5HQ(abYo :1mLKMnvHNWUrcJ!iL<݋-\ll^qqɖȬɉnZt&D xÂW뇞F;gd_mAkl^csĝ ]rח e9 WvkV]_ X*ۺVswJ:['&@݃ggXz3e)nzBWm3& ~Yԧ .AKs& ^wZF܊ÞeUk;lH{ pU V5UiV.kBY`1ͦ*]ULś0Kl+ Q?[@j/[،iH,佧qG`ru^JEF-s_6kЧSgC'uFN;^<79Niic(z5*^@Dck_3{ÔdEW 9g90w#Osw<Ş՘Xᓗ(7k&[98Nlr9vŠ4aorGɹ+1WlmtdDSDaR7n(Dm!(Jrs8Xֻ5~C}ߕZy^€]qIUۧorOb>E@~DVֲYG#TB!-ɏ9?%NkĿ^81w<͒_RK%p [K ֠%Xax? QW?#uёI]5f>Jpڡb@*Emsl;:VY{[lq6zPK SPKUrOConfigurations2/toolpanel/PKUrOConfigurations2/menubar/PKUrOConfigurations2/floater/PKUrOConfigurations2/popupmenu/PKUrOConfigurations2/images/Bitmaps/PKUrOConfigurations2/statusbar/PKUrO'Configurations2/accelerator/current.xmlPKPKUrOConfigurations2/toolbar/PKUrOConfigurations2/progressbar/PKUrO content.xmlYێ6}Wʢnv׻@ $mӾq)JfVUR;.d6Mڇ gIf&ƚpAY0ݑc$,Y0ƚ7](CW) oЙR\E>m1۞٬0(\lΐbyZ3<+Ad >9XOn&D2?Ԝm< &f};CT kMI.(f6vOwȢ(POVQrx*z=!K{-E֐F[`Nbz)m=rhcv=9w Tn8 KBӅPd;8IR.!3,[I" :t;}кmB~fR7f¨;yaCLx M" vn%'V$gh0υcޖ2K6tWJ #4,d_n8eIuJŹ_)5o4[\6'p[FUQ3sM|cE+$8Wera|.[ +0> q?ӐnÝ?3a&G4` f6!"k8ɍB av]ŵ R WORiUb6֒x {n҈S5h(:j(G|`,YQ[1YB"5\v$**TxɿQ&pv#e2T RKP^#ưnBKPgy9teJp]PrJj^|v+P\U"\Ux"J 2Xj%ɡ!Mu]-INY{jO?n=eP?T8R~}tv&ɖ W{KIw _dҌ830/gN` \?-Pu ][\Ph*0o"߸'L9g\kΦtqݒmaˉp x.h&OsjSٳFxo_~pwz^Dq7&]3<0K<!$4lz,YgA픙сPݰUo |Oْ<ߒ+Җq'bm-ؾhPmKysN-9s z_@heQmH(􀿴4B!h`:V|#38_xIϦ)qd͞bh3?Tapm)e#eX\=jRU*g ]CAD]t]t݄uYbPb)LI->Jڻ+xߩey23glv8tWˣ:b|W:l)Q#P1 xz>RԂK,J7GXV񉆓 Azhc37KO@_(u+[ȇoe[r.x~O8W+D~|\`\-mS0s8 |]BFRKۜCx6WmEhA{J墻?̀3 ^β(gDdlK9$mOmIګ;جm^ yжCs"F* 66/;uxJFf@c`=4H݉-bjK1LH8{ʸ|t;=g?>Y\Hoܴ4Kb3h}o`ƒt?((DŽNjw!P&j9ʧ EsSm Q^8*{<'7M4&50mQvsЖfD Bbӑ[`h+v a κ.x"a| Ӽ=P½&D&zX+6U )$|q]:t.LUѡw\׌Rp 跾f͎a8Ӑyo'WzK7yeIBP57@tA҆c"G:RE$؜s~tH<$Q+H3!Uj9XP]<Qc푹L|FJrKX3 Qf)sObO%dܛAe¿q? O0b ¸Q*]|N>R ļgᠷj?˰ !`$mBNh7}PKZ$PKUrOVThumbnails/thumbnail.pngPNG  IHDR<< ")@PLTE #*4;$$$,,,444<<'ߜ>D3'ޞ(<Ѻ֊79{!O\ahz /P{a%> c8[n? lvCzLۿ3VG\Tk= V͠{N2$Y=Ȁܴziڇf?g/|UôIENDB`PK'$nJmeta.xml͎0}  FZ]DlOld!} )t*K߹:<7u"ZGB1ͥ:C\??K&(׬kra#\ RetT(++-UU#,uVYB4FιW~8R8qeП(gWL)Π`!01ὡFv5MReS|tPLF:ltzJ2#*p^z!s W _߂1X fe_'$ڔOhg}pÓT}=l`Z F zxɚbpvL$ >+֏/PKl,/PK'$nJConfigurations2/floater/PK'$nJConfigurations2/menubar/PK'$nJConfigurations2/toolbar/PK'$nJConfigurations2/progressbar/PK'$nJConfigurations2/statusbar/PK'$nJ'Configurations2/accelerator/current.xmlPKPK'$nJConfigurations2/toolpanel/PK'$nJConfigurations2/popupmenu/PK'$nJConfigurations2/images/Bitmaps/PK'$nJ styles.xml[_s۸Vc\ӹs"A Hp@в%O] ADsNfl .7?XbU.QZ%&Xb7`CS($ dݒiEKW/0nr!M@ !,3YUB0=D;T`?Z&87sF&9tf_Pq{l%E0&5u{e,=`N bP2Ch~6_B3"t"<BŋDH|^Ά3"24:DD-*6`mvh"tDwV)ݱ@M{`ì479nQLMs2*Y>eF!-%+*6G2덒zCLzW t&L%.OsUcβ.@фkMbu :" nMn8nXnÆOcYebJ1Y;Ц HK>Xo{`KU~9}|ibz9+̿^X2?E{}fx69T4myJ8vKn\I^a9C=| 42ZE;ߠU;lHxY#4c JO@0ll0>\dWWplSF|=.]yB-\Y5ų&1Xh9l||٣+^Dn=Yܖ\g4=vFtUzv'&FrFs1s6i0zT| u&ñ"9'J+,t^"1ZVka}M^Su)zs]ch֗½cGp8 .. zx L^ >?sMK)HoIrϊaK$Y_ &hqzVR%x*>ZU2Zj㒻J0 BZ[}G9*)p9 "{/\g. la7nPK]b66PK'$nJ content.xmlY[6~`N' ƀ/LC$iM VY`eCwtO}ླྀf'4ضpјKoо& 2Q\8|Z^%˗q—90_hI 7^{U#\t6oPĶnd m%j:婓PzV A:^lS_k!n6&Qrb*v8ҸdBőS,o]o 6 ObM2lph@Z0˕9l"ʯzX9Z#68*A<\d>%F{XFv%wY1q֘֠ J9XHGǣ HfPbZH7;;*lŎR;yšW&FfJ47J"<&p7S݁@j U#t$!ZDM#,C^ w4ZN.SRT+%kv<JL;MNP 2ר):dKvŃkƶr6טJP?n$a*JG3,NS+*'Ɩ{<>cN8<صv‘$ 5]ep'-1~9{R_na9 ^Ihݨυu߽fO CEd ~6{/HqD6V\ړ"x]:$~qpI$uxςir%Kxyiޤ@viJlj.BOc |qQw;UdL"d7@J-+3`%n'l(@$+e%鱣meNL(7tu6D0#Q[V+Kit*ʖG,fN,f`1 g~8𸢽:Բ)W"~^`kg5@تY=a#6^D|IIB<5*s lfΙT]STMSw@$tZ<묣!gzIYĤ 25ruou趚DxWPK^=PK'$nJ settings.xmlZ]s8}_tgbBNaBBÔ$l ȒGC{e K&vv6st?痐=Lwr\@21mzn˅LX *8a*! Xnz I4 AB 4db=9-p&̘x~z,?~v=4RDp>96͋9_J*NX.,^V:dEs}j2;Ґ -N  CAq+P9 Ns3WP ~PN\VR+;O#x1הeF24 AgFJ8<͐3 zA wuUR6 j n:>-}&S͎$JCjQ^ >.m3DwX6+brqUvdnIhiMx2KEw)ex/Rk)7K׫j=}'|$LsE|A]C2//<>3c]k>zF(~=|>FaVľ6XqES "E mCѶF( U⬕Hʴ34Iq1ЮtS3~Q2<7䴘bhkF &+^ O.*M$xsi<%]f Ŝ7RHfa\N#D^ zkI*_8X-+x;mU/X'cpR]BZl 0g*͆qt-&mB$zO61B;_y ev(SN%G_s1o/?"g^t.0馤KWŀ#q #c80{bXY6;nYPK࿚@!PK'$nJMETA-INF/manifest.xmlAn E9fU!;YT PDF'&p}26 ܈ot PK(PK'$nJ.++mimetypePK'$nJuHMMQThumbnails/thumbnail.pngPK'$nJl,/meta.xmlPK'$nJConfigurations2/floater/PK'$nJConfigurations2/menubar/PK'$nJ=Configurations2/toolbar/PK'$nJsConfigurations2/progressbar/PK'$nJConfigurations2/statusbar/PK'$nJ'Configurations2/accelerator/current.xmlPK'$nJ<Configurations2/toolpanel/PK'$nJtConfigurations2/popupmenu/PK'$nJConfigurations2/images/Bitmaps/PK'$nJ]b66 styles.xmlPK'$nJ^= content.xmlPK'$nJ࿚@! settings.xmlPK'$nJ(META-INF/manifest.xmlPK6.muse-4.0.0/src/svg/projects/transport_affects_latency_off.odg000066400000000000000000000233721404105311000244720ustar00rootroot00000000000000PK[rO.++mimetypeapplication/vnd.oasis.opendocument.graphicsPK[rOmeta.xml]o +,#ǕjW:i`?fIMW'{8ԕ.Zm|'\r~k!W嚵P^/UNGZ\C!ԍPS4Ц8 S8Ya4][.T1:X襆:zw]td%px[{abON[ N*\+i 5F3(*18XֲR vO5Nڹʓ|eg#̌W9QBd$$ qF ^QdEJ(8UI\[3ʿ-rA  \ %z62 Tak;Avѿs8XlejN2oUnc<=6_PK6-PK[rO styles.xml\ے۸}Wy㐔4)oynۛ;DBl`h?_$h\H@")PL&*{4> ҫVwYAhv=&$[\5^xxY} Ί줔^eO8g-aNyƠkM)*H1 2P̴E%Wp*ŊUg؜&gbz1\ $if9ی4QT"zݲ\5&I zhBW-SG-W؉Kg~ eN9ղUw>~1q] di%l%9 83 IDAi-OKi -*uHk3](U.=5f..Z<]s>n8X2mױwEisSmw4RI*||j6KI=@oeq%Y*8) :-/sp!l* ypNYY9;6׊bT6&>•W3̜ݞt #| S?aSk;4MQSk< ?ğ +eϠ#>H<hQ~ _rZiKN6QPEK-{3[6zL *s ^Sր;./=_A(!<{")NI=(OX@R~v0T?XCVƠ-`Qe \" a )gz9&䉡->j 8 `p`o* mzˀ/ɀ'(O 8M %7U8~(Dž `*:yV|o~1Zha@D<(~~RKxMBBZ"n C|N\%ó"^vFu:hwCBfno :?SNr0FlďoK{5aQ21Xa@zj=x=E7?)&? 3;sNUO8v/I<в곟-f%V~|q̹s:|K3Z1$]p;ѺE I0(͗H:˵^174 D3s i<4,pDtWgax=$~! fqh`0 fROD-Eo [!hwg?Kw I d(^`xsU9(cthYB/R`d4z,Q"?$Ig:͆yة_-Nyp;lK-N?[Ǔғ.(|H]UgLb؜"N-m3M.Ɏ;sq]H{0|hgxN$Ng"OԘɐWb;,!|5i{AkF[?ZS91SA]/%4Jr%i]oWI)S IoI@:<i=/:rC%i˃|+bU>+%:PR>QVu#1w9sx`LJˇ:{0_-?{hO?GIb4!G/q͸mǷ n LۦmB;ˠ}Q!T" F0-B.aƔ&K ]AҶ]NM_{aU~?3RhL65hkwhK4u3:JzM.l(r،ҭk7w/|U؁ߒ81r <8sv^V$[q#MQQvEǸZ1HMfQ/: Lx= eˮ֢aw @ઊx-xkD`*]@עAw cMUQxvlZ0vCkWxJJ4K0Z>ś8KǵlK);lvlc3-*y#T=~Oz;$kub[lנIzzK RSؕˎ" h9Sjz)^yG *sltm"k&vzoQc׳*!G: Y,}=n(cSq^>zB}fcy큋U΢ʷa+~>u,1JÉzxF0r稟ch$T=Iva/N4uV-uIRr' bNᯎ9S_ݍm;Q`KG4bOץKdL@SXOɯ֪Z6+PF_v*c(ڍcI#z)LPqb6 ς/y}p ZKuPQ &??2 QW?E_"#EhmѴCBŀT$m8vv+j\ PKn{ 5UPK[rOConfigurations2/images/Bitmaps/PK[rOConfigurations2/popupmenu/PK[rOConfigurations2/toolpanel/PK[rOConfigurations2/progressbar/PK[rOConfigurations2/toolbar/PK[rOConfigurations2/statusbar/PK[rO'Configurations2/accelerator/current.xmlPKPK[rOConfigurations2/menubar/PK[rOConfigurations2/floater/PK[rO content.xmlY[o6~߯aXW08c/NÖE ^ Fd6/;$-GڬɃmx.ry3gEX:XLt5pdIB#2YT夐(boЅT]JY=WOg^6\|V3,8'b.z;^,¹6^9͆ P`s^bJN0jC[V831XgQKKVRGl64հ #:~y-I-.?2 2,  D[Dr~in;JP⵱;˞_x@73Cu)^[]Gp(lIv Bn328Š$"P⵱+|3s$*⺤jdSN gM[:ҞN2XtXH'b7"C 1?$kP2rBʹm2Zw&Eks3Lj ,Nle36<%HxYർ%mM\{KSnxif[W$Hw!UƯG`4ld]9mأ΢N(ɸQ 6HZ>iE{u:{՞ky#<%8"(&Q&.e~V. ;pFۅ.q^thŏRR@`38q(`0:p@d26ƽax s^OAVH'8V"+\~l;_,+[G5'gs-җ_/w$2=gu\I^rL#ԟ201*qjhqQ\bn u͉1VnA%+G8Wbt%V`iA>WDz+5?G("1|1'̄SC9`~'4`-&dBǴ )*`6laÑp PIBђqSጦ0?UBdϸR!vl9n+Ne J9[%Re 00P6mTyGcR[1ZF!L(U^T[x*-/_sMY ʰCo: 9Rc%l{sfED]<7w 6;oC3.]jPρ?fS'WtˑX S\)m)ު;%|J4Ypv]5X=gK|FPExNƣl:{hLSL FJsC*!R,ջN-,'ov'o> *JF )u ,pyLk*՜Vu:PF4?;Vm @F g"5,g1M(5O#hZEq4RujWn:8o$ V5ʭT:zƄu8OGGzӨCu#ڍӸ>NN ʴOd vcۡB A p4]+ߧMMSS52]%XSWy*_cWgw2g#srz|7PK=SPK[rO settings.xmlZr8}߯HQUEp$;s߄&W2d&C$6<sNV՗ON٩;AcunϹ/߮=P 2/yTCĉNEi}1v[H  zP4 ׹>%'ҏ@*X [ 7>c 8~(3M}*|LJ=pF~R1zoZ^xYsxx f%%ul.kZ#F1# tSuS+?]^ N L >P.v?Fo"<_vy~V<-ܦBu稘᫷FQ n ȕuH%sDcwl#\ۜm%,%Suc*#ۘGpYzL=C#h *ա8գF2Y:>@#omuAZotaIyɕEiJz@Wr3NS{Yס̒jÞe.2^JL? B"9\oTNm U'!"j BP%P0+ZB*ރެ&&.[ǧ !>`58O]*XxkLs5eʰh3#i~@z*t[jC>:9.bӮ:r ϐ(=,GvecGKS]= 4zu˘Kwdch:>"~OXFsޟv\|~w{s=@ nB$d5׀,Ѡ㌫s.tCN(@|*lT͒R5 pU֟n9JQkRNcsCdj@,2s>L ݃H%k(wAgŋ>3-zfɣ:ѱC2{iv_[2bfd@z=% 4~Lz!dxh'1T99sji/6R&Z&Cw, &([?98_0&,T"hD_46[–JhJ^f;O [K=U~9|H뇀`4=:Um _~mPW6#63厧(;ٌLEZr!v}zA|'O_'gdW`ҙ_on'FR_:eH>5MK$cVp8wGvp; 0?̸]3:G\qwB҇3mK'\}gV.Gá~9"k[:5s Etzc"qT.&rZu!;, }(UQo?—Jdkb;B\jԢc\V2:x [-V .sIENDB`PK[rOMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PK[rO.++mimetypePK[rO6-Qmeta.xmlPK[rOn{ 5U Istyles.xmlPK[rOmConfigurations2/images/Bitmaps/PK[rOConfigurations2/popupmenu/PK[rOConfigurations2/toolpanel/PK[rOConfigurations2/progressbar/PK[rOTConfigurations2/toolbar/PK[rOConfigurations2/statusbar/PK[rO'Configurations2/accelerator/current.xmlPK[rOConfigurations2/menubar/PK[rOOConfigurations2/floater/PK[rO=S content.xmlPK[rO$K[& settings.xmlPK[rOˬkkThumbnails/thumbnail.pngPK[rO`417!META-INF/manifest.xmlPK6"muse-4.0.0/src/svg/projects/transport_affects_latency_on.odg000066400000000000000000000233341404105311000243320ustar00rootroot00000000000000PKYrO.++mimetypeapplication/vnd.oasis.opendocument.graphicsPKYrOmeta.xmln0E A͖CEQt2ɨl%R رQ\qx\ΐ{J6> 4o O~yLPY[ @-\Retk(jڔ iϚۧ)Eua!bF/5t/ ѻ뺠 RtEd}^XՇbݭsOT4rvT#, kkYzLMߊ}uv'ܚf`'\#~˳fF+ o 0 a* ^QdE8$k$I1!q/]Up5c'=>̹J<&9>PWk?Ƚ?a"{=~%.Zy'Ȯ1`K++q`08Iqm|PKCXu/PKYrO styles.xml\r۸Sp#Gjl2md;Nڙ^@$$!Zv^}>IKI,nv&Ysp|:n!)ίGY8`߿F?+XRTkS,=8/gc|=ZQẐ0g,8 =Rm@ף3 JTr&3T3kde ΄~\̵.'x]^4),޵;גiVsl6c,P"A[ukh:Vxu0 2ʚ:#M( LeͶVLWGV)FFu,.\.%Խ85RxLOgE15ϴLp,)UJcûī@~t~yx^vȊ6m˵]ޕ/̴6hc](bf6P;ZH&$#AtZ6YBTh&L`>!9VRcF|ոj=yڳ3]6hC3!' K,*-BZ0OyՃW( Sklk/0[ ~ |JrY{7Wz1} g Z-ף?ڒ#K3{ $ Dj˅n\}3VKBw+Gկ!Ry_R~NT 罃 Kְ[6Sd$\Բ'=øFcjP/>n[ WT<~C& _Te0"?d+7T[aن"la!*hcۇDF/0TsLRb2`/ )* gzOIy|1}b0`/y ('{S{h+ԛA|^m<>1,Û> O['WI~^6TT$wףzecB V8.s6\ӸL`5g`+W^%wʨ(S\34[[,e0[px^^$kUNiMN%xr|Tbjk@Ab7F~l~^š7eyz=̏x*olwJ {;VsYƜ-0-#O?;$z2[&cXdeo lI(֋hXػ/XنuBҟcn*b0k(Q>(. Ѡb) bV¼o+Dşʶ/<(ȷO [j]}kh1y-w{FZǭkWh{ϟF},׆1_gK H:>'YJG<٠͐ǡ}G#_._Y\0><]k\wsl4擷F=E wm擿}<_f$Z2J M>3\(>sL)V+ZiHEi>;═w6v}k~ ~bۙROVrOY,?WpF'ω6k| ڷ?3 ~_q6r MvܙU%C:<&PHchrD^$CGc(>H.Esy<~^h:CGde(<E[ڨV'՛=`cΒuPaS]VPTHl/T0DAT<9&k5PŽVsĴ1(W>UBu"Z|Z2ŖLGfBlVtr L#y3YFZt.D yÂjga##5+tU6']=v*y\ wB%A"n"X;s E{6lP! FlO Ͽ„n]>]y0o)d?xn#3P yj1eZ5'Nn<އcXSfaa g/[IB{ YIQkoGQf2 ۿQ08w_@4O | v7 +zz؅P<' ܑzMOf%|648tt x4]L(iйi=5{"/8L$5Ԯ l϶. uo&1V;8KзT4us:Jz7WSۦu7u(@ v,n}ܝ}Cۨ=- 3㦙L7j7rz=+=m3 ~Y WQǝLx5ʗ\ڈgkF\7ÁwFA1@eЍp}}h0sfS/шxmr7G-q1[CgWxJJ4`wŇ\ބyڮfUϘoweK1F!O$?P8:wYe^HE)P6cǤS=gE%j8M\gyxõ)5\qŠFo/!La 1}}Kȑ,zI@Sg~7q1}Uq sď~E97lؓd:}^6pr{ogQͷž]~FZLVO}Éz$39614GO$aN4uV-uI<5eEN1_s><;٨Ax+?B>;ZVp"z2xHKֲXG#|ԓdcvΏi$를pMJ~1 tKUAIK7%bx'6?2 Qן"ةouI[4a>Jp#b@&EmslY|y6k|*7k|iˠ PKa 0WPKYrOConfigurations2/statusbar/PKYrOConfigurations2/images/Bitmaps/PKYrOConfigurations2/popupmenu/PKYrOConfigurations2/toolpanel/PKYrOConfigurations2/menubar/PKYrOConfigurations2/floater/PKYrO'Configurations2/accelerator/current.xmlPKPKYrOConfigurations2/progressbar/PKYrOConfigurations2/toolbar/PKYrO content.xmlY[o6~߯aXW08c/NÖE ^ Fd6/;$-GڬɃmx.ry3gEX:XLtpdIB#2YT夐(boЅT]JY=WOg^6\|V3,8'b.z;^,¹6^9͆ P`s^bJN0jC[V831XgQKKVRGl64հ #:y-I-.?2 2,  D[Dr~in;JP⵱;˞_x@73Cu)^[]Gp(lIv Bn328Š$"P⵱+|3s$*⺤jdSN gM[:ҞN2XtXH'b7"C 1?$kP2rBʹm2Zw&Eks3Lj ,Nle36<%HxYർ%mM\{KSnxif[W$H!UƯG`4ld]9mأ΢N(ɸQ 6HZ>iE{u:{՞ky#<%8"(&Q&.e~V. ;pFۅ.q^thŏRR@`38q(`0:p@d26ƽfx s^AVH'8V$+\~l;_,+[G'gs-җ/:~vޒ08-^^_kq%zEH1Pv|(k|hƨĩCh)FruT5'T!?KމX5輺vZ[X3vG \O^Ԟn\emDFP|,GbKr(HOdSdx*Yw.pfyv,b9jwzI,=AY8칣2MBr?gHTojzy/`%(Q&O-婥pm)e#eX\=jRU*g ]CAD]t]t݄uYbPb)LI->Jڻ+xߩey23glv8tWˣ:b|W:l)Q#P1 xz>RԂK ŨfN+ť_L3WOw/E$.1A*̲7ʭQ]ìKWTj0 j>G|Vvuc5()V]eoTRO&pCԄ 1)঻ѿ‹Y!?ytc?. U^犳 b/LeJ%9\>,J7GXV񉆓 Azhc37KO@_(u+[ȇoe[r.x~O8W+D~|\`\-mS0s8 |]BFRKۜCx6WmEhA{J墻?̀3 ^β(gDdlK9$mOmIګ;جm^ yжCs"F* 66/;uxJFf@c`=4H݉-bjK1LH8{ʸ|t;=g?>Y\Hoܴ4Kb3h}o`ƒt?((DŽNjw!P&j9ʧ EsSm Q^8*|0KޜOsj1IAYUk9}~4#b*rՎgD[ ,x3/p֝pYƛ- `uML m 5'"4ֳY!\Hx' ᣏ2sa,~Ì^*%f׼E5Zv C̋~;ؿ[+KzJbmo 7Ց*"䜳>E "_Eڟ ̬Gkne4PU[ĚY2OYv}~* $޼*y#P=LNwͥ!? }.!Xm [-%mw/}%K;PKj!#PKYrO,uBBThumbnails/thumbnail.pngPNG  IHDR<< ")@YPLTE  /&%%*%&***0,,666?56EK TY^ j ur y|DDDIIITSSXXXe[\n]_o^`p^`k`asbdlmqqq  prwy} !!$$ˬ˳ѱֵոٷܻ¿Ϳ߽ ƹIDATxO@AEA/c@ ;j) q E;?yuw=]I~[!105U0+XǺ$7uxIqIX9au 5t(~VP^y#DlBЂī~%|,fQXrd,+ULmQ] U0Cf(_ .L>^ "|X*VZ(BײC7|2DB6!NUJQ(z5W9^Ft/!NuxEX/RRr.,iZ2m*Ni=iiuOQs>W֎6"u!M Q_h#oc_ÿ?EPI(fIENDB`PKYrOMETA-INF/manifest.xmlSj0 )ﱷi`O=ȩJsBfuMB#!='Vi"u {߿/l]mzEh &9UxMI"FI(N(ɯrdf3kvNnd\V}!CIYA r02mW͂A uiJE]%@Ng S[ԣ q;s PG&CRYMśwt`øJkPR! [X/3 xF?R*/ 64S@@ ²~ PK`41PKYrO.++mimetypePKYrOCXu/Qmeta.xmlPKYrOa 0W Lstyles.xmlPKYrOConfigurations2/statusbar/PKYrOConfigurations2/images/Bitmaps/PKYrOConfigurations2/popupmenu/PKYrONConfigurations2/toolpanel/PKYrOConfigurations2/menubar/PKYrOConfigurations2/floater/PKYrO'Configurations2/accelerator/current.xmlPKYrOIConfigurations2/progressbar/PKYrOConfigurations2/toolbar/PKYrO;] content.xmlPKYrOj!# Osettings.xmlPKYrO,uBBThumbnails/thumbnail.pngPKYrO`41!META-INF/manifest.xmlPK6"muse-4.0.0/src/svg/projects/up_vee.odg000066400000000000000000000217141404105311000176530ustar00rootroot00000000000000PK):vl<2Ƭ7k$%p܇uir?6V3;+O1s .: +Dyn6jKaEsd{3+rk]{ƟwN>rۗJT N3*[PwpGάTyzpS]+vpmSGz,K[9tt(3 rC0)#4"4A$^^PYLD 'Y.2wI gltBnh+7ds*_^J%>GZvjc'P=\D[$BOJ7b#d[[[r)}hT)lqt8̫yŃ!nv{F!Rx|xiPK.PK)~Wx@1%8.0AARXxsV̤c|(gas L0> C$  a1wj]@3al,(u1^ly ar (wc$IVl6H0wn~=Wtc0NC!ժ ^ =FIENLPN&&u;lV-OL(BFu.\Ӝ.!Ľ(1RxL׵ogE35vNKq ,-eHsE U[zf`A[:[`n3k;4M(:x_ +eܼ#b #H,hUv ?9.'}K1{ 4rDcvilȅm\}w3ViœwGm`RPP~MDL罃w Kֱݺ&S]Ժ'=eq SZP/nL[WT~C&{ bA(Ma&|NnA6v²맅DЪcY(ccE7:5K@䳏H51I4½~FPMP!0UgerX)OS!oI5W`~1ZhGa@;$&7GϜȋa"na- q5~gOLUJu0>}>9AB,%;#rb/#mPeV)\dTBo9().ro(X4_}eR<9NH9`N `wJf?u,SyB&R~ b]Qs8/n{΍߁T0ذ&+()eTC:S ɇ㜊8X&YCnJu}fg܀AX[𱵎A&Ѳ+-QiϻsA Zi5[IWh{J~>1_v0V'TS>٠x>r2CuWCB.j)]|Vڷpl?+L/lG%[oeAA`:؞ n!cJy&I-WD. $"~6{^- m6lCPc D3EA|TKLr0/1K=)8G) xϿU91seA LR%8oa+a=ǩ3c*Bڃ#xGC0 L&14:C/~ Mb"<Cyqh _h(ojCnlaū3{f%,o  nfXA_&sX`T˩Hs 5HQ(abYo :1mLKMnvHNWUrcJ!iL<݋-\ll^qqɖȬɉnZt&D xÂW뇞F;gd_mAkl^csĝ ]rח e9 WvkV]_ X*ۺVswJ:['&@݃ggXz3e)nzBWm3& ~Yԧ .AKs& ^wZF܊ÞeUk;lH{ pU V5UiV.kBY`1ͦ*]ULś0Kl+ Q?[@j/[،iH,佧qG`ru^JEF-s_6kЧSgC'uFN;^<79Niic(z5*^@Dck_3{ÔdEW 9g90w#Osw<Ş՘Xᓗ(7k&[98Nlr9vŠ4aorGɹ+1WlmtdDSDaR7n(Dm!(Jrs8Xֻ5~C}ߕZy^€]qIUۧorOb>E@~DVֲYG#TB!-ɏ9?%NkĿ^81w<͒_RK%p [K ֠%Xax? QW?#uёI]5f>Jpڡb@*Emsl;:VY{[lq6zPK SPK)odh2$H f]o) pip8Q/'z:5>I d^p퐠&p@e67 | ~_E;wCfK()OԘ>*Q~.{!^7^ғ\Gz`7[BͻfmO/=vߝUl5{hAr'4\Qs r~u&w՞ ߪ2nPE4Jgi_hq"]d~|OL y5E|&wÅ`T9]C~YW+pP`y(^}L%g}͕o "F2D"hqD4tY WȤR_/$t:,E:0m,0ѷsޢmkGSw]Ww$-;SMڡ)=@2eoUrvY?Wb S_Z CG}?Pu%mumHV02 )`1,ϓr>.{ƌK{(r_9\#/7UrMͼ&i|wiZu"a!f/鲉;i {t7PK&PK)]wWk}HE?Z=)'WK3qO=R }u^a:\BL\(C!e ߹ĩ %/ *sU^,}"*4X%\;^[z+Jgl$ c)ފ|s9QFcJR!}|Jg=@貟ӷ-g/ׯ,Eʓ?fi ) Cu!`Y#j"OjHo%$6\= D+pKŮp%ph kz2uLf441AmIJ?MQLTȞ6qp VٰqhL"sEiB&TdG?S=.!:9bo;p^H !"6q-WxM!00!k!Cf{ 6){&6"o,cI~E5 {7vPy4zR  {-] -S醒}* j?W?| AO+ Jd$n2(*7YiDki4L f'ܣi7WAGœ6.=I'^ad=N4 ކ u(G]B!]1o6b ǼMDx-5;aE$e -*Rn\6_ߐS,MLi/ƥ;>z3wc?|wn KQ^qx鎝ŤIv,\XQVW(Z)-&QշKQ sa0{ d/quЩo%#qMps7lXdAp5^s0Ֆ}c:ú3za}Rg`ZǷUcq=v-N" f8,5?ZtkL,2_߃M;5 N< 0T{& ڐqE¶W+PKèl(PK){hlKcXOq{|c҅"4'njPF`eK//uRzy좍kؖ~^{9wl^Wg186؎w6@׾Þ m~DKb\ NaM65vO[x ,Zjz A1W$wK&笺BiF]838(dwÛAIENDB`PK)Xu:w0;{ؗ=T4 Đ5PꊖxHTEYBzs.'Idhv<:gh+a=)g<{o8G v`aMcd ?;'dY249CY2tq2Ο%?髓14~yzzBgE'h򿕨-:} %,O *O)6s@Q͢2²PA`ځT˩x{s(kF啔؉ik*ZWDsDjh\eOT=[:=E Q:vs˵FW!46eccsۨ8NDY1wd;(ߕYiycOGj E*i v019Qz1rx]txc8#xsThRU"SO86ZAn."fnM[_4ΧgM#Ǥk0˩{{Еt%@VEM48UΗ4{ ~Xoe;;u{/87Jb^ ڪ}T h{P}rhG|!/!T* ܑO&\pW^.=M|?/¥o E< :7ߴ>t޳ wv,sQ_G9t]lx붠ekQ}~,ugC< e9 Wvhws +[ r؍6w(|uoK swgX&.~WvE}Wq~[P ~ЯH9F=w"Z7o֑px@y=eUaYL{$puUxz8ތf`ӎmnT= c t2BGF[0 =:k|5Vq$hN*+~%Lz^+ T]lɫC|5N͘ ~#~pN#AhbU ?E~N]u4;;sR.cټVbFZH["Aտb|ÒݸfoժC kTV"Lo(_7oOH;;^fWMh6ƨ\msZWvy[6l}#ao y_PKOз DPK'PConfigurations2/accelerator/PK'PConfigurations2/floater/PK'PConfigurations2/menubar/PK'PConfigurations2/progressbar/PK'PConfigurations2/toolbar/PK'PConfigurations2/toolpanel/PK'PConfigurations2/images/Bitmaps/PK'PConfigurations2/popupmenu/PK'PConfigurations2/statusbar/PK'P content.xmlX[o8~_aQiMRiU]Umwoc 02Kw\INU`7Wό͹|{JrP-t} !:-{;oYVHMWuOuZ+guuu~B垗xD]TW[-ڢ֢R[dl ~rĂURnK5Њ_+n34-Nj?ﻹNCoFӮ`I:AhSEv~M53m =y:+T DheТ܈B8v??Lq\ P+su~:iu(T#ET'2#vr*N|)i_Rnuj)$ʡlgՎ3@gke ~y#^{jK3g*ǼPjM:En3SYw;RyZNl*'ޔVm*]U*Ժ삹#VY]2pD#v!akE+"!CVή:ܹ5yӺB hd^_I?G{u|5ycF(\<E?E,D#檆f]zhNavuכ"U!5KwM0&ٿl ¤TK6]Vy909k^子wkbg} YWǰ.IqakڰI`pӤg>e܈fWqLѐ :܁6pJ+%h3tY|xQ7ܻ D'78 cܻPV ԥg :*a`!G9toֵZ(F 8(˥2$@b+N󋊲AT=jV>> L&N;ݠ',~=G+UY7#)݌I;KYIRA %3\ά ғ p(OwV@4J՛׻s6%CRI3$s&[S",H)=]SVGX1;o'Y<.DSY8 Ԉ""FXȒw@ IH6HML˜ 'GO!,IX9a~D%Iߧ<$GGqQC7F؇rFa4Аa,$Nj,!ᑤh K/l"F Kd: %,HH(aI~ kᐐ QhPRs:?r ,09! !a`dLCsL=qa0$qp1b]]U . CٟFS>>1uw3w^PKO0PK'P settings.xmlZ]s8}_tgCph-LB|h MP%$ǰ~%Cڔ6l9^]_]]33zk\.n|6aECTfR gj:mح1/dGt}Y6g(n@*H ӱ{_ u?Rq,97a"|>}*ٱ@O Q|0(l_2X"O&5Ԃ1 EkޏsFXh[,MMLQnoQ~ .̧c |W;_\e %T(ܽN3|u`6J! C"#jÆظc a @# %9.>zC)L샗DQ8:*I6K ߧȾfTImVf<8{A ">έwj挫P}DTBP!Ph1+2b M3 MDP:C=OO'"Akp3]*X(5\w|n,3ګ"w|D* O|Pu|h]E'muX@2~z|' ʢjc)H~%)YQyT2{1 jB-9e̻;vSh.x#aàAۛ;6 vڟiXOVCxHp-2tok-|punY1e}@O%j0\0'*Up7^fn@I:i!I,mOCyj·15GdJ(~Jz]}iiMceDd2/p=Lj8XeԂ}OWH7'p&CFI' ϒt B:A 49M8cncR1!cQTŎ84z;r0NEW)L-ĕ!R87M4Z6/n3G*?g~yi%(Һ Xn=I#+E.;i6o|6uʨ @rSRCqmF&_-9g^`>= XX3kBxfWߡ=άЄ-e-nXt^Nײ^jзeZR_I#ˍ*u]6Z?RmAgTzvio0&fk?5B̥~ԖsX槞-۫iӚ7MGT̰YD9մ6 amo=uz[L'SqJbtρqt.UzcH9r=餧mzQ1gS(5opbFٕT1o;;S!O}s($&rO˄4v:h w}=hH$T~o}۟_PK+Q_&PK'Pk_t44Thumbnails/thumbnail.pngPNG  IHDRAA)9wPLTE    $#.%1)5+8"""))) -;333679===%4E'7H):L+=P.AT0DY5Ja8Nf:Rk>WrBBBLLLPPP[[[@YtB\xE`}eeejjjuuu}}}FbIfLkOoQqTvVyY|\`ceghjmnq΄v&}IDATxo@o<PB5ZWM[\'B=];mre[o{P~X!',; x+'y=)䄉m)%Pav2w'5?2 IG'꣘ߐ c0{1:XTiPєK<҈7sDPૉ}_@GLV2Bi{d2B)e#>Tㄼv z3<06P#@SF`Äc:ib5Sr1Q]YRبPTR̓ en4 p ձ/T*E.*Kh JDyF޷$T&7"Yr/l@?$=X)URp:0 cD>쒌fMlr8e+6BkD7Vo;<~!ᆸ˒ _3|f֯uS\|WCʍ3 +[PK ]fۤPK3P styles.xml\rۺSht&E?R9Ir:NNgzHHBBD/'.J$ɎLa~vVj8:'ϡ_?#Bd'}tCPէ6 SIxYRʕ*Ɋ%1sΓj-eذyU% f8{ W<|xq,jYRMǓ:bxgCslww[:cH[A.%,XrpK~.g/3ӓGdyQho%jGOlasig)B!t9A4[)f(YVTW+h1Y;P{9/P;bEzJ9\VyNv M';G$M@)vbK(!K^nR?TAwaل*6zx:hӹ,nlZhfYF^QQBZd!GYzxzNbF75V+獵CEz!]Ky'.m6.M ["U#3Z"`2vO8sga1x5|v:vZ&U7{ ~Ttoe;;s}/x ~4>|2㻹U2^#hzTОC,!* HKB.QoDߍ+MΏ 6\Qҵ2\oSgل M;;yhno|6y<3f\M[а7i9?xa ʆ/UxoعZ"e%&|_vXPۯ_k{}tԛ[5ӃYƊrruq(R4X丱J:j>H,-._NPK| Ϟ DPK3PConfigurations2/accelerator/PK3PConfigurations2/toolbar/PK3PConfigurations2/progressbar/PK3PConfigurations2/statusbar/PK3PConfigurations2/floater/PK3PConfigurations2/menubar/PK3PConfigurations2/toolpanel/PK3PConfigurations2/images/Bitmaps/PK3PConfigurations2/popupmenu/PK3P content.xmlX[o8~_aQi0fTZU}jVU][aF~1dHVJ1w>SYЍTՍŮ*QoidZeL6UɾUKU$]5ێvm[om[բVmqb[=dycuUͶhmEA-X5vM) 65Hy(TLE@#~)TVӴcp<gv[NR ][^`qۆ:B }DɩW0\#8SlݑpEɀ!Meݮ/UFrgȾ>i~\ P+Su^:miu;+#YD2#vz*Pg'u>L+N8BK$o1ۉ||l kͺz^,򞚂fjLbȻq.R)Z6)ZUpOŝ Y=.w&NkN36fϕ?+4{]ibBfWɎ%cSM{bXa}TW4^{)߾HXУ_վ|zuEIF_ˣ x*x]ByQ1;Y PK 2PK3P settings.xmlZ[s8~_a3!84LB\ 4@K&,ȒWc_!mJPwv6l9:>73z87rgz b:ɍ;)5{x*A>2/j8S۩(o3nr )RDWa֟s=b&rZb@`QG$LTH%b)pCzrnX/Kq,=/oGQt}0M}^*|LkE8#?H1Wo,E?xÔ=0G瘊H6h/ t7`3L.Uv@DʶDNQxVx|mU<AC,zU*[;I QW{hNAr+4H5MWq?XsEEELLLSSS^^^@YtB\xD`}cccjjjqqq}}}FcHeNmPoY}\_`bhjmnq΅GޣIIDATxQO0p.NH61HEЍ L6DLz{/w6wHjT`_ &GH~hF1I8%Ǻ;-/7橎`-4&y%Dul0W [-A⨊2 z@_2wWxu"GHZI@R @*$ گF0_ i:R]!]0zBڴ'Ӕ q30tojLaP\ĊK B᰽RչJ # 0,&={xqp a image/svg+xml muse-4.0.0/src/svg/punchout.svg000066400000000000000000000032421404105311000164260ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/quantize.svg000066400000000000000000000012021404105311000164130ustar00rootroot00000000000000 Q muse-4.0.0/src/svg/range_to_selection.svg000066400000000000000000000011131404105311000204170ustar00rootroot00000000000000 muse-4.0.0/src/svg/rec_arm_off.svg000066400000000000000000000117151404105311000170270ustar00rootroot00000000000000 muse-4.0.0/src/svg/rec_arm_off_dark_red.svg000066400000000000000000000117151404105311000206620ustar00rootroot00000000000000 muse-4.0.0/src/svg/rec_arm_off_default_col.svg000066400000000000000000000104061404105311000213640ustar00rootroot00000000000000 muse-4.0.0/src/svg/rec_arm_on.svg000066400000000000000000000117201404105311000166650ustar00rootroot00000000000000 muse-4.0.0/src/svg/redo.svg000066400000000000000000000014161404105311000155130ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/restart.svg000066400000000000000000000063421404105311000162510ustar00rootroot00000000000000 muse-4.0.0/src/svg/rewind.svg000066400000000000000000000120111404105311000160430ustar00rootroot00000000000000 muse-4.0.0/src/svg/rewind_to_start.svg000066400000000000000000000120321404105311000177650ustar00rootroot00000000000000 muse-4.0.0/src/svg/route_auto_adjust.svg000066400000000000000000000051061404105311000203220ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/route_destinations.svg000066400000000000000000000077471404105311000205210ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/route_in.svg000066400000000000000000000021341404105311000164040ustar00rootroot00000000000000 muse-4.0.0/src/svg/route_in_midi.svg000066400000000000000000000036621404105311000174150ustar00rootroot00000000000000 muse-4.0.0/src/svg/route_out.svg000066400000000000000000000021551404105311000166100ustar00rootroot00000000000000 muse-4.0.0/src/svg/route_out_midi.svg000066400000000000000000000037301404105311000176120ustar00rootroot00000000000000 muse-4.0.0/src/svg/route_sel_dest.svg000066400000000000000000000117451404105311000176100ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/route_sel_source.svg000066400000000000000000000122271404105311000201450ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/route_sources.svg000066400000000000000000000077421404105311000174730ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/router.svg000066400000000000000000000013121404105311000160750ustar00rootroot00000000000000 muse-4.0.0/src/svg/routing_input.svg000066400000000000000000000017371404105311000174760ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/routing_input_unconnected.svg000066400000000000000000000017371404105311000220630ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/routing_output.svg000066400000000000000000000020671404105311000176740ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/routing_output_unconnected.svg000066400000000000000000000016601404105311000222570ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/scoreedit.svg000066400000000000000000000021641404105311000165440ustar00rootroot00000000000000 muse-4.0.0/src/svg/scroll_down.svg000066400000000000000000000004511404105311000171050ustar00rootroot00000000000000 muse-4.0.0/src/svg/scroll_left.svg000066400000000000000000000004441404105311000170720ustar00rootroot00000000000000 muse-4.0.0/src/svg/scroll_right.svg000066400000000000000000000004431404105311000172540ustar00rootroot00000000000000 muse-4.0.0/src/svg/scroll_up.svg000066400000000000000000000004451404105311000165650ustar00rootroot00000000000000 muse-4.0.0/src/svg/select_all.svg000066400000000000000000000022511404105311000166670ustar00rootroot00000000000000 muse-4.0.0/src/svg/select_all_track.svg000066400000000000000000000027371404105311000200640ustar00rootroot00000000000000 muse-4.0.0/src/svg/select_inside_loop.svg000066400000000000000000000012701404105311000204230ustar00rootroot00000000000000 muse-4.0.0/src/svg/select_invert.svg000066400000000000000000000027161404105311000174340ustar00rootroot00000000000000 muse-4.0.0/src/svg/select_outside_loop.svg000066400000000000000000000011051404105311000206210ustar00rootroot00000000000000 muse-4.0.0/src/svg/settings.svg000066400000000000000000000062371404105311000164300ustar00rootroot00000000000000 muse-4.0.0/src/svg/show_fields.svg000066400000000000000000000025741404105311000170760ustar00rootroot00000000000000 muse-4.0.0/src/svg/snapshot.svg000066400000000000000000000007731404105311000164260ustar00rootroot00000000000000 muse-4.0.0/src/svg/solo_and_proxy_spotlight_on.svg000066400000000000000000000155001404105311000224110ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/solo_proxy_spotlight_on.svg000066400000000000000000000154721404105311000215770ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/solo_proxy_spotlight_on_alone.svg000066400000000000000000000143521404105311000227510ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/solo_spotlight_off.svg000066400000000000000000000154651404105311000204760ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/solo_spotlight_on.svg000066400000000000000000000154641404105311000203370ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/solo_spotlight_on_alone.svg000066400000000000000000000142261404105311000215100ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/speaker.svg000066400000000000000000000013761404105311000162210ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/speaker_chords.svg000066400000000000000000000145071404105311000175630ustar00rootroot00000000000000 muse-4.0.0/src/svg/speaker_single_note.svg000066400000000000000000000131271404105311000206040ustar00rootroot00000000000000 muse-4.0.0/src/svg/speed_off.svg000066400000000000000000000275601404105311000165240ustar00rootroot00000000000000 muse-4.0.0/src/svg/speed_on.svg000066400000000000000000000313171404105311000163610ustar00rootroot00000000000000 muse-4.0.0/src/svg/steprec.svg000066400000000000000000000014471404105311000162330ustar00rootroot00000000000000 muse-4.0.0/src/svg/stereo_off.svg000066400000000000000000000122311404105311000167120ustar00rootroot00000000000000 muse-4.0.0/src/svg/stereo_on.svg000066400000000000000000000161361404105311000165640ustar00rootroot00000000000000 muse-4.0.0/src/svg/stop.svg000066400000000000000000000111301404105311000155410ustar00rootroot00000000000000 muse-4.0.0/src/svg/synth.svg000066400000000000000000000037011404105311000157260ustar00rootroot00000000000000 muse-4.0.0/src/svg/synth_gui.svg000066400000000000000000000025351404105311000165760ustar00rootroot00000000000000 muse-4.0.0/src/svg/sysex.svg000066400000000000000000000055201404105311000157350ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/timebase_master_off.svg000066400000000000000000000344211404105311000205620ustar00rootroot00000000000000 muse-4.0.0/src/svg/timebase_master_on.svg000066400000000000000000000413621404105311000204260ustar00rootroot00000000000000 muse-4.0.0/src/svg/track_aux.svg000066400000000000000000000025311404105311000165420ustar00rootroot00000000000000 muse-4.0.0/src/svg/track_group.svg000066400000000000000000000033621404105311000171040ustar00rootroot00000000000000 muse-4.0.0/src/svg/track_input.svg000066400000000000000000000017251404105311000171100ustar00rootroot00000000000000 muse-4.0.0/src/svg/track_off.svg000066400000000000000000000214711404105311000165230ustar00rootroot00000000000000 muse-4.0.0/src/svg/track_on.svg000066400000000000000000000177451404105311000163760ustar00rootroot00000000000000 muse-4.0.0/src/svg/track_output.svg000066400000000000000000000017321404105311000173070ustar00rootroot00000000000000 muse-4.0.0/src/svg/tracktype.svg000066400000000000000000000013431404105311000165670ustar00rootroot00000000000000 muse-4.0.0/src/svg/transport.svg000066400000000000000000000016711404105311000166210ustar00rootroot00000000000000 Bus Stop muse-4.0.0/src/svg/transport_affects_latency_off.svg000066400000000000000000000162761404105311000226740ustar00rootroot00000000000000 muse-4.0.0/src/svg/transport_affects_latency_on.svg000066400000000000000000000167671404105311000225430ustar00rootroot00000000000000 muse-4.0.0/src/svg/undo.svg000066400000000000000000000014141404105311000155250ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/up_arrow.svg000066400000000000000000000066401404105311000164240ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/up_vee.svg000066400000000000000000000106731404105311000160520ustar00rootroot00000000000000 muse-4.0.0/src/svg/velocity_all_notes.svg000066400000000000000000000154611404105311000204650ustar00rootroot00000000000000 muse-4.0.0/src/svg/velocity_per_note.svg000066400000000000000000000126531404105311000203200ustar00rootroot00000000000000 muse-4.0.0/src/svg/waveeditor.svg000066400000000000000000000014721404105311000167350ustar00rootroot00000000000000 muse-4.0.0/src/svg/whatsthis.svg000066400000000000000000000040671404105311000166050ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/zoom.svg000066400000000000000000000056651404105311000155600ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/svg/zoomAt.svg000066400000000000000000000214651404105311000160410ustar00rootroot00000000000000 image/svg+xml muse-4.0.0/src/synti/000077500000000000000000000000001404105311000144065ustar00rootroot00000000000000muse-4.0.0/src/synti/CMakeLists.txt000066400000000000000000000033361404105311000171530ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= include_directories( . ${CMAKE_CURRENT_BINARY} ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/muse ${PROJECT_SOURCE_DIR}/muse/widgets ${PROJECT_SOURCE_DIR}/synti # Make sure to include the uic generated headers paths. ${CMAKE_CURRENT_BINARY_DIR}/libsimpleplugin ) set (SubDirs libsimpleplugin libsynti deicsonze # deicsonze2 organ # simpledrums simpledrums2 vam ) if (ENABLE_EXPERIMENTAL) set (SubDirs ${SubDirs} s1 ) endif (ENABLE_EXPERIMENTAL) if (HAVE_FLUIDSYNTH) set (SubDirs ${SubDirs} fluidsynth ) # removed fluid, fluidsynth should supercede it in every way. endif (HAVE_FLUIDSYNTH) # subdirs(${SubDirs}) foreach(subdir ${SubDirs}) ADD_SUBDIRECTORY(${subdir}) endforeach() muse-4.0.0/src/synti/deicsonze/000077500000000000000000000000001404105311000163715ustar00rootroot00000000000000muse-4.0.0/src/synti/deicsonze/ARCH_ALIN000066400000000000000000043105251404105311000176460ustar00rootroot00000000000000* TX81Z/DX11 Voice Archive * $Header: /cvsroot/lmuse/muse/synti/deicsonze/ARCH_ALIN,v 1.3 2005/07/14 10:25:45 a-lin Exp $ * Copyright 1991 by Bryan Sutula. See README file for restrictions. --Bank 1 * CATEGORY ORCHESTRA * SUBCATEGORY Orchestra ** Name: EleOrchtra ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 03 03 04 0b 00 02 4e 00 02 18 03 03 05 08 00 02 51 08 00 11 07 03 07 0b 00 05 63 04 06 09 10 03 05 0f 00 06 63 08 05 34 21 23 0f 00 12 18 05 04 00 63 63 63 00 00 32 00 45 6c 65 4f 72 63 68 74 72 61 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: EleOrchtra ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 09 03 03 04 0b 00 00 4e 00 02 18 03 03 05 08 00 00 51 08 00 11 07 03 07 0b 00 00 63 04 06 09 10 03 05 0f 00 00 63 08 05 34 21 23 0f 00 12 18 05 04 00 63 63 63 00 00 32 00 45 6c 65 4f 72 63 68 74 72 61 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Orchestra* ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 1f 00 02 0f 00 01 4b 0a 08 07 1f 00 05 0f 0b 00 61 09 0e 0f 1f 00 05 0f 00 01 63 0a 08 08 1f 00 05 0f 00 00 61 05 08 3e 21 21 07 00 32 05 02 04 00 28 63 00 00 00 32 00 4f 72 63 68 65 73 74 72 61 2a 63 63 63 32 32 32 00 00 00 12 00 00 00 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntOrkest ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 0c 03 04 0f 32 01 47 08 0d 10 0c 03 04 0f 32 02 3e 08 0a 0e 15 06 05 0f 1e 05 5d 13 0a 1f 15 06 05 0f 1e 05 61 08 0e 24 20 2c 14 00 42 0c 0c 04 00 63 32 32 00 00 32 00 4f 72 6e 74 4f 72 6b 65 73 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntOrkest ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 0c 03 04 0f 32 00 47 08 0d 10 0c 03 04 0f 32 00 3e 08 0a 0e 15 06 05 0f 1e 00 5d 13 0a 1f 15 06 05 0f 1e 00 61 08 0e 24 20 2c 14 00 42 0c 0c 04 00 63 32 32 00 00 32 00 4f 72 6e 74 4f 72 6b 65 73 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Symphony ** Name: Sympathy.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 03 0f 00 04 59 04 03 1f 1f 00 03 0f 00 04 5f 00 06 0f 1f 00 04 0f 00 05 5b 08 02 0f 1f 00 04 0f 00 06 5f 08 06 2c 23 00 00 00 02 18 04 04 00 28 32 00 00 00 32 00 53 79 6d 70 61 74 68 79 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sympathy.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 03 0f 00 03 52 04 03 1f 1f 00 03 0f 00 04 55 08 06 0f 1f 00 04 0f 00 06 5b 08 02 0f 1f 00 04 0f 00 05 5e 08 06 2c 23 00 00 00 02 18 04 04 00 28 32 00 00 00 32 00 53 79 6d 70 61 74 68 79 2e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sympho * ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 04 03 04 0e 16 00 4a 03 03 0d 04 00 05 0e 1d 00 52 04 06 0c 04 03 05 0e 0e 00 63 03 03 07 04 03 05 0e 0c 00 63 04 00 3c 23 0e 0e 00 32 11 02 04 00 28 63 00 00 00 32 00 53 79 6d 70 68 6f 20 20 20 2a 63 63 63 32 32 32 00 06 00 18 00 06 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony * ** Controls: KV, MW (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 04 00 01 0e 00 49 4d 04 03 11 17 03 04 0f 00 00 45 04 06 0c 08 03 05 0f 00 78 63 04 03 0a 04 03 05 0e 00 78 63 03 00 3c 1f 0c 10 00 4e 05 02 04 00 28 0c 07 00 00 32 19 53 79 6d 70 68 6f 6e 79 20 2a 63 63 63 32 32 32 00 08 30 38 00 08 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 13 07 02 0e 0a 04 59 13 12 0e 09 00 05 0e 00 04 51 08 03 18 09 05 04 0e 0a 04 4b 0d 0b 0c 0d 00 05 0d 00 05 63 08 03 64 26 18 0f 00 26 0c 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 31 63 63 63 32 32 32 00 10 00 20 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 19 13 07 02 0e 0a 02 59 13 12 0e 09 00 05 0e 00 01 51 08 03 18 09 05 04 0e 0a 01 4b 0d 0b 0c 0d 00 05 0d 00 00 63 08 03 64 26 18 0f 00 26 0c 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 31 63 63 63 32 32 32 00 10 00 20 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 05 00 07 0e 41 01 51 08 05 19 13 06 04 0e 0a 03 4d 19 0a 0c 0a 00 07 0f 0a 05 62 08 01 1f 0a 06 04 0b 00 02 4d 0d 00 34 22 18 0f 00 22 0c 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 32 63 63 63 32 32 32 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 19 05 00 07 0e 41 01 51 08 05 19 13 06 04 0e 0a 00 4d 19 0a 0c 0a 00 07 0f 0a 00 62 08 01 1f 0a 06 04 0e 00 00 4d 0d 00 34 22 18 0f 00 22 0c 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 32 63 63 63 32 32 32 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 05 00 04 0e 29 03 4c 04 01 0f 0c 01 05 0e 0a 03 39 08 02 0d 0a 00 06 0e 0a 03 5d 08 03 12 08 03 06 0d 00 06 58 08 01 2c 1e 00 06 00 32 0c 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 33 63 63 63 32 32 32 00 00 00 20 00 00 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.3 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 05 00 04 0e 29 00 4c 04 01 0f 0c 01 05 0e 0a 00 39 08 02 0d 0a 00 06 0e 0a 00 5d 08 03 12 08 03 06 0d 00 00 58 08 01 2c 1e 00 06 00 32 0c 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 33 63 63 63 32 32 32 00 00 00 20 00 00 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 1a 05 07 0d 34 02 49 08 0c 17 0c 00 07 0f 0a 03 4b 13 05 15 0d 05 07 0e 34 05 63 08 0b 0c 0a 00 05 0f 0a 03 63 08 01 74 1e 0b 09 00 32 00 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 34 63 63 63 32 32 32 00 30 00 20 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.4 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 1a 05 07 0d 34 00 49 08 0c 17 0d 00 07 0f 0a 00 3f 13 05 15 0d 05 07 0e 34 00 63 08 0b 0c 0a 00 05 0f 0a 00 5a 08 01 74 1e 0b 09 00 32 00 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 34 63 63 63 32 32 32 00 30 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 03 06 05 0a 29 03 48 08 14 0c 0a 08 06 09 0a 00 4a 0d 0a 0d 0b 06 05 09 1a 03 61 0a 04 0c 09 08 07 09 15 03 63 08 01 35 1e 1a 0c 00 22 00 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 35 63 63 63 32 32 32 00 40 00 40 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.5 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 03 06 04 0f 29 00 48 08 14 0c 0a 01 05 0e 0a 00 4a 08 0a 0d 0b 06 04 0f 1a 00 4f 0a 04 0c 09 00 04 0e 15 00 5e 08 01 35 1e 1a 0c 00 22 00 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 35 63 63 63 32 32 32 00 40 00 10 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.6 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0a 02 05 05 00 40 4c 08 0a 1a 05 00 07 0e 0a 6d 50 19 04 0b 0a 03 05 0f 00 40 56 08 10 17 0a 00 06 0f 14 44 5b 0d 05 24 1d 19 3d 25 26 0c 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 36 63 63 63 32 32 32 00 30 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.6 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 09 0a 02 05 05 00 40 4c 08 0a 0a 05 00 07 0e 0a 41 35 13 04 13 08 02 05 0f 00 40 56 08 10 10 0a 00 06 0f 14 40 5b 08 05 24 1c 00 2a 0b 26 0c 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 36 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.7 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0c 04 04 0e 49 03 51 04 08 12 0c 04 04 0e 0a 04 4d 08 0a 0e 10 00 07 0e 49 03 5f 08 04 12 10 00 05 0e 00 04 51 08 01 2c 1c 00 0f 00 42 18 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 37 63 63 63 32 32 32 00 40 00 10 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Symphony.7 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0c 04 04 0e 49 00 51 04 08 12 0c 04 04 0e 0a 00 4d 08 0a 0e 10 00 07 0e 49 00 5f 08 04 12 10 00 05 0e 00 00 51 08 01 2c 1c 00 0f 00 42 18 04 04 00 63 32 63 00 00 32 00 53 79 6d 70 68 6f 6e 79 2e 37 63 63 63 32 32 32 00 40 00 10 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * CATEGORY WIND * SUBCATEGORY Brass ** Name: AnlgBrass1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 07 00 07 0e 0f 04 55 08 0b 0d 1f 00 07 0f 0a 04 5f 04 03 0d 1f 00 08 0f 00 04 5f 08 03 0d 1f 00 07 0f 0a 04 5f 04 03 2e 1f 0e 10 00 52 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 31 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 07 00 07 0e 0f 01 55 08 0b 0d 1f 00 07 0f 0a 00 5f 04 03 0d 1f 00 08 0f 00 01 5f 08 03 0d 1f 00 07 0f 0a 00 5f 04 03 2e 1f 0e 10 00 52 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 31 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 04 03 07 0c 00 02 53 04 03 0f 05 01 08 0f 00 04 56 04 03 10 1f 00 09 0f 00 06 55 04 03 0f 05 01 08 0f 00 06 5a 04 03 2d 1f 00 05 00 62 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 32 63 63 63 32 32 32 00 00 00 20 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 04 03 07 0c 00 02 53 04 03 0f 05 01 08 0f 00 00 56 04 03 10 1f 00 09 0f 00 01 55 04 03 0f 05 01 08 0f 00 00 5a 04 03 2d 23 00 05 00 52 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 32 63 63 63 32 32 32 00 00 00 20 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 07 00 07 0e 00 04 4d 04 01 0d 1f 00 07 0f 0a 02 5f 04 04 0d 1f 00 08 0f 00 05 5f 08 04 0f 1f 00 07 0f 0a 05 5f 08 06 3e 1f 0e 10 00 52 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 33 63 63 63 32 32 32 00 20 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 07 00 07 0e 00 01 4d 08 01 0d 1f 00 07 0f 0a 01 5f 04 04 0d 1f 00 08 0f 00 01 5f 08 04 0d 1f 00 07 0f 0a 01 5f 08 06 3e 1f 0e 10 00 52 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 33 63 63 63 32 32 32 00 20 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 04 00 07 0f 00 05 53 04 0a 0f 03 04 08 01 00 03 55 04 06 10 1f 00 09 05 00 05 55 04 04 0f 03 04 08 01 00 05 5a 04 06 2d 23 00 00 00 62 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 34 63 63 63 32 32 32 00 30 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 04 00 07 0f 00 01 53 04 0a 0f 03 04 08 01 00 00 55 04 06 10 1f 00 09 05 00 01 55 04 04 0f 03 04 08 01 00 00 5a 04 06 2d 23 00 00 00 62 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 34 63 63 63 32 32 32 00 30 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 0a 06 07 0c 00 02 53 04 0b 15 10 06 07 09 00 03 49 04 06 16 07 06 08 0f 00 03 63 04 03 10 1b 06 07 0c 00 03 62 04 06 34 23 00 05 00 32 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 35 63 63 63 32 32 32 00 00 00 20 00 30 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 0a 06 07 0c 00 01 5b 04 0b 15 10 06 07 09 00 00 59 04 06 16 07 06 08 0f 00 01 63 04 03 10 1b 06 07 0c 00 00 62 04 06 34 23 00 05 00 32 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 35 63 63 63 32 32 32 00 00 00 20 00 30 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 0a 06 07 0c 00 02 53 04 09 15 09 06 06 0b 1e 02 52 04 0d 0f 07 06 08 0f 00 06 4c 04 04 11 0a 05 07 0c 0f 05 62 04 06 23 1e 25 25 00 32 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 36 63 63 63 32 32 32 00 20 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 0a 06 07 0c 00 02 53 04 09 15 09 06 06 0b 1e 01 52 04 0d 16 07 06 08 0f 00 01 4c 04 04 11 0a 05 07 0c 0f 00 62 04 06 23 28 25 02 00 32 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 36 63 63 63 32 32 32 00 20 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass7 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 0a 03 0d 0c 00 02 58 04 0c 10 1f 09 0e 0d 00 02 63 04 05 16 1f 05 0d 0e 00 05 63 04 06 10 1f 06 0e 0d 00 05 62 04 06 25 22 24 0f 00 42 0c 02 05 00 63 63 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 37 63 63 63 32 32 32 00 20 00 40 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass7 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 14 0a 03 0d 0c 00 02 58 04 0c 0f 1f 09 0e 0d 00 00 63 04 05 16 1f 05 0d 0e 00 00 63 04 06 10 1f 06 0e 0d 00 00 62 04 06 25 22 24 0f 00 42 0c 02 05 00 63 63 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 37 63 63 63 32 32 32 00 20 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 06 0d 0f 00 01 61 00 0a 1f 0a 06 0d 0f 00 03 5e 00 04 1f 18 06 0d 0f 00 03 5e 04 03 1f 18 06 0d 0f 00 03 5e 00 05 26 28 00 00 00 32 18 02 05 00 63 63 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 38 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass8 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 06 0d 0f 00 01 61 00 0a 1f 0a 06 0d 0f 00 00 5e 00 04 1f 18 06 0d 0f 00 00 5e 04 03 1f 18 06 0d 0f 00 00 5e 00 05 26 28 00 00 00 32 18 02 05 00 63 63 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 38 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass9 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 06 0d 0f 00 01 51 04 0b 12 0c 02 0f 0a 0a 03 51 04 0e 1f 16 08 0f 0c 00 04 63 04 06 1f 06 04 0d 0c 00 03 5e 04 03 3c 2b 00 00 00 32 0c 02 05 00 63 63 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 39 63 63 63 32 32 32 00 00 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBrass9 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 06 0d 0f 00 01 51 04 0b 12 0c 02 0f 0a 0a 01 51 04 0e 1f 16 08 0f 0c 00 00 63 04 06 1f 06 04 0d 0c 00 00 5e 04 03 3c 2b 00 00 00 32 0c 02 05 00 63 63 00 00 00 32 00 41 6e 6c 67 42 72 61 73 73 39 63 63 63 32 32 32 00 00 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AtackBrass ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 6 0f 1f 04 07 0f 00 02 53 05 0d 0f 0e 00 08 0e 00 01 5b 04 02 10 1f 00 09 0f 00 04 61 05 05 10 05 00 07 0e 00 04 63 04 09 34 1c 15 06 00 52 11 04 04 00 63 32 00 00 00 32 00 41 74 61 63 6b 42 72 61 73 73 63 63 63 32 32 32 00 01 00 38 00 01 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Back Brass ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0d 1f 00 08 0f 00 00 43 04 03 0d 0e 00 0f 00 00 00 47 04 03 1f 1f 00 0f 0f 00 00 00 04 03 0f 1f 00 08 0f 00 00 5a 04 03 3a 1e 00 00 00 52 0c 04 04 00 63 32 00 00 00 32 00 42 61 63 6b 20 42 72 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BC1 Feed ** Controls: KV, MW (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 03 00 0a 0e 00 3c 47 04 03 12 1f 00 0a 0f 00 04 4b 08 02 12 1f 00 0a 0f 45 04 4f 08 03 12 1f 00 0a 0f 01 05 5f 08 03 3b 23 01 0a 00 62 0c 02 04 00 63 32 00 00 00 32 63 42 43 31 20 46 65 65 64 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BC1 Horns ** Controls: KV, MW (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 09 00 08 0e 00 3b 4a 04 0b 09 1f 00 08 0f 00 24 3d 04 03 0a 1f 00 0f 0f 00 25 21 0b 03 0e 1f 00 09 0f 00 2e 63 04 0b 3a 20 05 03 00 62 0c 02 04 00 63 63 00 00 00 32 63 42 43 31 20 48 6f 72 6e 73 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BC1 Horns ** Controls: MW (LFO pitch), BC (EG amplitude) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0e 09 00 08 0e 00 00 4a 04 0b 0e 1f 00 08 0f 00 20 3d 04 03 0f 1f 00 0f 0f 00 20 21 0b 03 12 1f 00 09 0f 00 28 63 04 0b 3a 20 05 03 00 62 0c 02 04 00 63 63 00 00 00 32 63 42 43 31 20 48 6f 72 6e 73 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 9 10 09 00 09 0a 00 01 50 04 09 10 0b 00 09 0a 00 01 52 04 0a 14 1f 00 0a 0f 00 01 62 04 0a 13 1f 00 0a 0f 00 01 62 08 0c 3c 20 06 03 00 52 18 02 04 00 28 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 31 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 09 00 09 0d 00 01 50 04 09 10 0b 00 09 0d 00 01 52 04 0d 14 1f 00 0a 0f 00 01 62 04 0a 13 1f 00 0a 0f 00 01 62 08 0c 3c 20 06 03 00 52 18 02 04 00 28 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 31 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 1 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0d 0d 00 07 0d 01 00 47 04 0e 0d 0d 00 0a 0f 2d 00 4f 04 0e 0d 0d 00 08 0b 00 00 63 04 08 0d 0d 00 08 0b 00 00 63 04 09 3c 23 00 00 00 62 18 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 10 11 0a 00 09 0a 00 02 53 04 0c 0a 08 00 08 0a 00 02 59 00 08 15 1f 00 0a 0f 00 01 62 04 0e 12 1f 00 09 0f 00 01 63 00 0a 3c 20 06 03 00 52 18 02 04 00 28 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 32 63 63 63 32 32 32 00 00 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 11 0d 00 09 0d 00 02 53 04 0c 0d 08 00 08 0d 00 02 59 00 08 15 1f 00 0a 0f 00 01 62 04 0e 12 1f 00 09 0f 00 01 63 00 0a 3c 20 06 03 00 52 18 02 04 00 28 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 32 63 63 63 32 32 32 00 00 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 2 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0c 07 03 04 0f 16 00 4e 00 06 0e 07 02 06 0e 21 00 54 00 06 17 0d 00 04 0f 00 00 5c 00 08 16 0d 00 06 0f 00 00 5c 00 08 34 23 00 00 00 62 18 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 3 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 19 05 00 05 0e 0a 00 51 04 06 1f 04 00 05 0a 00 00 37 04 00 14 0b 0f 0a 0f 00 00 50 04 06 14 1f 00 0a 0f 00 00 63 04 00 32 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 4 ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0f 0a 00 05 0d 00 00 49 04 03 15 1f 00 0a 0f 00 00 5c 04 03 15 1f 00 0a 0f 00 00 5c 04 03 15 1f 00 0a 0f 00 00 5c 04 03 3d 23 14 0a 00 62 0c 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 5 ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 11 0e 00 05 0e 0a 00 4a 04 03 11 11 00 09 0c 00 00 46 04 03 1f 10 00 0f 05 20 00 34 0b 0b 15 1f 00 0b 0f 00 00 60 04 0b 3a 23 1b 0a 00 62 0c 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 6 ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 13 00 03 0b 00 00 55 04 03 1f 15 11 03 0c 1e 00 4f 10 03 1f 1f 00 03 0f 1e 00 46 04 03 1f 1f 00 05 0f 00 00 5a 04 03 3b 23 0f 10 00 62 0c 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 7 ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 11 13 0c 0b 0f 00 48 22 03 1f 0b 00 03 0f 1e 00 39 04 03 1f 11 00 03 0e 1e 00 5d 04 03 19 1f 00 05 0f 00 00 5a 08 03 39 23 0f 10 00 62 0c 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 20 20 20 20 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 5th ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 06 06 08 0b 21 02 55 0a 02 13 05 06 08 0b 0a 04 4b 08 06 13 15 06 08 0b 0a 06 63 0a 02 13 05 06 08 0b 0a 05 63 08 06 2c 26 21 00 47 12 0c 04 05 00 28 32 00 00 00 32 00 42 72 61 73 73 20 35 74 68 20 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass 5th ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 10 06 06 08 0b 21 02 55 0a 02 13 05 06 08 0b 0a 02 4b 08 06 13 15 06 08 0b 0a 01 63 0a 02 13 05 06 08 0b 0a 00 63 08 06 2c 26 21 00 47 12 0c 04 05 00 28 32 00 00 00 32 00 42 72 61 73 73 20 35 74 68 20 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 08 07 06 08 0c 27 02 58 04 06 08 09 05 09 0e 47 03 60 04 04 14 08 06 08 0d 00 05 5a 04 02 13 07 06 08 0c 00 04 5f 04 00 3c 1f 2d 01 00 42 18 04 05 00 28 32 00 00 00 32 00 42 72 61 73 73 2e 2e 2e 2e 31 63 63 63 32 32 32 00 20 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 08 07 06 08 0c 27 02 58 04 06 08 09 05 09 0e 47 01 60 04 04 15 08 06 08 0d 00 00 5a 04 02 15 07 06 08 0c 00 00 5f 04 00 3c 1f 2d 01 00 42 18 04 05 00 28 32 00 00 00 32 00 42 72 61 73 73 2e 2e 2e 2e 31 63 63 63 32 32 32 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 1b 06 07 09 27 01 51 04 04 0e 0a 04 0a 00 00 05 4b 04 0c 0f 0a 06 07 0c 00 06 4e 04 05 11 07 06 08 0c 00 06 63 04 0d 3b 23 2d 05 00 22 18 04 05 00 28 32 00 00 00 32 00 42 72 61 73 73 2e 2e 2e 2e 32 63 63 63 32 32 32 00 50 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 14 1b 06 07 09 27 01 51 04 04 0e 0a 04 0a 00 00 01 4b 04 0c 0f 0a 06 07 0c 00 02 4e 00 05 11 07 06 08 0c 00 01 63 04 0d 3b 23 2d 05 00 22 18 04 05 00 28 32 00 00 00 32 00 42 72 61 73 73 2e 2e 2e 2e 32 63 63 63 32 32 32 00 50 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 1b 06 07 09 27 05 59 04 05 0c 0a 04 0a 00 00 05 43 04 08 0f 0a 06 07 0c 00 05 49 04 04 10 07 06 08 0c 00 05 63 04 0c 33 23 0c 0f 00 32 18 04 05 00 28 32 00 00 00 32 00 42 72 61 73 73 2e 2e 2e 2e 33 63 63 63 32 32 32 00 20 00 40 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 1b 06 07 09 27 02 59 04 05 0c 0a 04 0a 00 00 01 43 04 08 0f 0a 06 07 0c 00 01 49 04 04 10 07 06 08 0c 00 01 63 04 0c 33 23 0c 0f 00 32 18 04 05 00 28 32 00 00 00 32 00 42 72 61 73 73 2e 2e 2e 2e 33 63 63 63 32 32 32 00 20 00 40 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassChime ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 11 04 04 09 0f 00 03 50 04 0b 1f 06 06 05 0d 00 45 52 19 03 14 07 05 06 0e 00 44 61 04 0b 1f 06 06 06 0d 00 46 53 0d 02 64 24 23 00 2b 06 18 04 04 00 63 01 63 00 00 32 00 42 72 61 73 73 43 68 69 6d 65 63 63 63 32 32 32 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassChime ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 11 04 04 09 0f 00 01 50 04 0b 1f 06 06 05 0d 00 41 52 19 03 14 07 05 06 0e 00 40 61 04 0b 1f 06 06 06 0d 00 41 53 0d 02 64 24 23 00 2b 06 18 04 04 00 63 01 63 00 00 32 00 42 72 61 73 73 43 68 69 6d 65 63 63 63 32 32 32 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassEns.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 11 03 06 0f 00 02 47 00 0e 15 13 05 07 0f 3c 03 44 00 00 10 1f 04 06 0f 00 04 61 00 02 0e 0c 07 06 0e 14 04 60 00 02 5c 0e 00 0d 00 12 18 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 45 6e 73 2e 31 63 63 63 32 32 32 00 00 00 30 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassEns.1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 11 03 06 0f 00 00 47 00 0e 15 13 05 07 0f 3c 00 44 00 00 13 1f 04 06 0f 00 00 61 00 02 14 0c 07 06 0e 14 00 60 00 02 5c 0e 00 0d 00 12 18 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 45 6e 73 2e 31 63 63 63 32 32 32 00 00 00 30 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassEns.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 11 03 06 0f 00 01 4f 04 0d 0e 13 04 07 0f 2b 01 60 04 01 13 1f 04 06 0f 00 45 62 04 01 14 0c 07 06 0e 14 04 63 04 05 35 26 00 00 26 16 18 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 45 6e 73 2e 32 63 63 63 32 32 32 00 30 00 60 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassEns.2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 11 03 06 0f 00 01 4f 04 0d 0e 13 04 07 0f 2b 01 60 04 01 13 1f 04 06 0f 00 40 62 04 01 14 0c 07 06 0e 14 01 63 04 05 35 26 00 00 26 16 18 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 45 6e 73 2e 32 63 63 63 32 32 32 00 30 00 60 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassEns.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 05 03 04 0f 00 01 57 04 0a 0e 09 04 06 0f 00 02 45 04 00 0c 1f 04 06 0f 00 04 5c 04 05 0f 0c 04 05 0e 00 05 60 04 05 64 20 33 0b 00 22 18 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 45 6e 73 2e 33 63 63 63 32 32 32 00 30 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassEns.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 05 03 04 0f 00 01 52 00 0b 16 09 04 06 0f 00 01 45 04 03 10 1f 04 06 0f 00 00 5c 00 03 13 0c 04 05 0e 00 00 60 00 03 64 20 33 0b 00 22 18 04 04 00 63 32 00 00 00 32 00 42 72 61 73 73 45 6e 73 2e 33 63 63 63 32 32 32 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassEns.4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 03 09 0c 00 02 3a 04 09 14 08 04 09 0f 00 02 41 04 02 0c 0c 09 07 0f 00 05 3f 04 05 11 05 04 07 0e 00 04 63 04 04 63 20 33 0b 00 32 18 02 04 00 63 32 00 00 00 32 00 42 72 61 73 73 45 6e 73 2e 34 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassEns.4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 03 09 0c 00 02 3a 04 09 1f 08 04 09 0f 00 02 41 04 02 1f 0c 09 07 0f 00 01 3f 04 05 0f 05 04 07 0e 00 00 63 04 04 63 20 33 0b 00 32 18 02 04 00 63 32 00 00 00 32 00 42 72 61 73 73 45 6e 73 2e 34 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrassSynth ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 0d 0a 00 06 0d 00 39 53 04 0c 09 05 00 05 0d 00 39 59 00 08 0e 16 00 07 0f 00 01 62 04 0e 0e 16 00 06 0f 00 01 63 00 0a 3c 20 06 03 00 52 18 02 04 00 28 32 00 00 00 32 00 42 72 61 73 73 53 79 6e 74 68 63 63 63 32 32 32 00 00 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass.Vibe ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 1f 06 07 09 00 02 54 04 06 10 0a 05 0a 00 00 03 52 04 03 0f 0a 06 07 0c 5a 06 4d 04 03 11 07 06 08 0c 00 06 60 04 00 73 23 00 19 00 32 18 02 05 00 63 32 00 32 00 32 00 42 72 61 73 73 2e 56 69 62 65 63 63 63 32 32 32 00 10 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brass.Vibe ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 1f 06 07 09 00 02 54 04 06 10 0a 05 0a 00 00 02 52 04 03 0f 0a 06 07 0c 5a 00 4d 04 03 11 07 06 08 0c 00 01 60 04 00 73 23 00 19 00 32 18 02 05 00 63 32 00 32 00 32 00 42 72 61 73 73 2e 56 69 62 65 63 63 63 32 32 32 00 10 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brassy * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 08 00 03 0f 19 01 51 04 06 0d 08 00 04 0f 27 00 4d 04 00 12 1f 00 06 0f 09 00 63 04 06 10 0c 00 05 0f 0d 00 63 04 03 34 17 00 0b 00 52 05 02 04 00 28 1e 00 00 00 32 00 42 72 61 73 73 79 20 20 20 2a 63 63 63 32 32 32 00 08 00 08 00 08 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chariots ** Controls: KV, MW (LFO amplitude) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 0e 0e 00 02 0f 00 40 4e 04 00 0d 0e 00 02 0f 00 00 32 00 06 0f 0c 00 04 0f 00 42 63 08 08 0d 0e 00 04 0f 00 02 63 04 0e 3c 1d 00 00 08 0d 18 02 04 00 28 32 0d 00 00 32 00 43 68 61 72 69 6f 74 73 20 20 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ChorusBras ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 4 0e 08 00 06 0d 00 02 4e 05 06 0e 08 00 09 0d 00 02 57 04 00 0e 12 00 08 0f 00 02 63 05 06 0e 12 00 08 0f 00 02 63 04 00 3c 1c 06 06 00 52 05 04 04 00 63 32 00 00 00 32 00 43 68 6f 72 75 73 42 72 61 73 63 63 63 32 32 32 00 01 00 38 00 01 00 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ensemble * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0c 00 02 0f 00 00 46 0a 00 0d 14 00 04 0f 2f 00 4e 09 06 0b 0b 00 05 0f 00 02 63 09 06 0d 0b 00 05 0f 00 02 63 13 04 3c 1e 2d 0c 00 42 05 02 04 00 28 63 00 00 00 32 00 45 6e 73 65 6d 62 6c 65 20 2a 63 63 63 32 32 32 00 30 00 12 00 02 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ensemble 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 3 14 06 00 04 0a 14 02 3d 1c 06 0b 04 02 04 07 14 01 52 13 00 0a 07 02 05 0a 00 01 63 09 06 0f 06 01 05 0b 00 01 5e 0a 00 3c 21 08 0c 00 52 05 02 04 00 63 32 00 00 00 32 00 45 6e 73 65 6d 62 6c 65 20 31 63 63 63 32 32 32 00 40 00 30 00 22 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ensemble 1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 14 06 00 04 0a 14 02 3d 1c 06 0b 04 02 04 07 14 01 52 13 00 0a 07 02 05 0d 00 01 63 09 06 0f 06 01 05 0b 00 01 5e 0a 00 3c 21 08 0c 00 52 05 02 04 00 63 32 00 00 00 32 00 45 6e 73 65 6d 62 6c 65 20 31 63 63 63 32 32 32 00 40 00 30 00 22 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ensemble 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 4 14 06 00 04 08 14 02 3d 1c 06 0b 04 02 04 08 14 01 50 13 00 0c 07 02 05 0b 00 01 63 09 06 11 06 01 05 0b 00 01 5e 0a 00 3c 21 08 0c 00 52 05 02 04 00 63 32 00 00 00 32 00 45 6e 73 65 6d 62 6c 65 20 32 63 63 63 32 32 32 00 50 00 30 00 42 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Full Brass ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 2 0f 1f 08 09 0f 00 03 4f 04 05 0f 1f 09 09 0f 00 02 63 04 03 10 1f 00 08 0f 00 02 63 04 05 11 1f 00 08 0f 00 02 63 04 06 3d 1d 0f 08 00 52 0c 04 04 00 63 32 00 00 00 32 00 46 75 6c 6c 20 42 72 61 73 73 63 63 63 32 32 32 00 00 00 30 00 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hard Brass ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 14 1f 00 0a 0f 00 00 47 04 03 1f 1f 00 0f 0f 00 00 3a 04 03 1f 1f 00 0f 0f 00 00 34 04 03 19 1f 00 0a 0f 00 00 63 04 03 3a 23 00 00 00 62 18 07 04 00 63 63 00 00 00 32 00 48 61 72 64 20 42 72 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 14 0a 07 00 09 0e 0a 01 4d 00 0b 10 08 00 0a 0a 1c 02 43 00 0b 10 0f 00 0a 05 09 03 40 06 0b 0f 18 00 0a 0f 00 02 63 00 0b 3a 1e 09 02 00 62 18 02 04 00 28 13 00 00 00 32 00 48 6f 72 6e 20 20 20 20 20 20 63 63 63 32 32 32 00 00 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 07 00 09 0e 0d 01 4d 00 0b 10 08 00 0a 0a 1c 02 43 00 0b 10 0f 00 0a 05 09 03 40 06 0b 0f 18 00 0a 0f 00 02 63 00 0b 3a 1e 09 02 00 62 18 02 04 00 28 13 00 00 00 32 00 48 6f 72 6e 20 20 20 20 20 20 63 63 63 32 32 32 00 00 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 10 09 00 09 0d 0a 00 41 04 03 0f 0b 00 08 0e 00 00 3f 04 03 1f 11 00 0f 03 0a 00 31 18 0b 12 1f 00 0a 0f 00 00 5a 04 03 3a 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 48 6f 72 6e 20 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 1f 0b 07 0f 00 02 55 04 04 0e 1f 00 08 0f 00 03 51 04 06 10 1f 00 09 0f 00 05 5a 08 04 0f 1f 00 08 0f 00 05 5b 04 06 3d 23 00 00 00 62 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 1f 0b 07 0f 00 02 55 04 04 0e 1f 00 08 0f 00 01 51 04 06 10 1f 00 09 0f 00 01 5a 08 04 0f 1f 00 08 0f 00 00 5b 04 06 3d 23 00 00 00 62 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0a 06 07 09 00 01 50 08 06 0f 08 06 08 0a 00 05 49 04 03 16 10 06 0a 00 00 05 46 1a 14 10 07 06 09 0c 00 06 60 04 01 22 23 00 04 00 32 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0a 06 07 09 00 01 50 08 06 0f 08 06 08 0a 00 01 49 04 03 16 10 06 0a 00 00 02 46 1a 14 10 07 06 09 0c 00 01 60 04 01 22 23 00 04 00 32 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 1f 06 07 0f 00 01 4e 04 05 0f 09 05 07 05 42 06 54 04 1a 0f 09 06 07 04 00 04 49 04 14 10 06 04 08 0b 00 06 60 04 02 2b 23 00 08 00 42 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 1f 06 07 0f 00 01 4e 04 05 0f 09 05 07 05 42 01 54 04 1a 0f 09 06 07 04 00 00 49 04 14 10 06 04 08 0b 00 01 60 04 02 2b 23 00 08 00 42 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 07 03 08 0c 00 03 51 04 06 10 0a 04 0a 0c 00 04 63 04 04 0f 0a 03 0a 0f 00 04 63 04 02 12 16 02 0a 0b 00 06 61 04 04 2d 20 13 09 00 32 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 34 63 63 63 32 32 32 00 50 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 07 03 08 0c 00 03 51 04 06 10 0a 04 0a 0c 00 00 63 04 04 0f 0a 03 0a 0f 00 01 63 04 02 12 16 02 0a 0b 00 00 61 04 04 2d 20 13 09 00 32 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 34 63 63 63 32 32 32 00 50 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 07 08 0a 0f 00 04 49 04 01 11 09 08 09 0d 00 03 53 04 0d 15 04 01 0c 0f 1e 06 3c 04 01 11 06 05 09 0f 00 05 5a 03 05 63 23 00 00 00 32 11 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 35 63 63 63 32 32 32 00 08 00 38 00 08 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 07 08 0a 0f 00 02 49 04 01 11 09 08 09 0d 00 01 53 04 0d 15 04 01 0c 0f 1e 00 3c 04 01 11 06 05 09 0f 00 00 5a 03 05 63 23 00 00 00 32 11 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 35 63 63 63 32 32 32 00 08 00 38 00 08 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 07 02 0a 0f 00 02 4f 08 06 11 08 06 09 0d 00 04 63 08 0c 15 04 01 0c 0f 1e 06 57 0d 02 10 06 05 09 0f 00 05 63 04 04 75 23 00 08 00 42 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 30 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 07 02 0a 0f 00 02 4f 08 06 11 08 06 09 0d 00 00 63 08 0c 15 04 01 0c 0f 1e 00 57 0d 02 10 06 05 09 0f 00 00 63 04 04 75 23 00 08 00 42 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 30 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 0a 06 07 09 00 01 4e 08 06 13 08 06 08 0a 00 04 47 04 03 0f 03 06 07 09 55 06 3e 10 04 15 07 06 09 0c 00 05 62 04 01 2b 23 00 00 00 32 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horn.....7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 10 0a 06 07 09 00 01 4e 08 06 13 08 06 08 0a 00 00 47 04 03 0f 03 06 07 09 55 01 3e 10 04 15 07 06 09 0c 00 01 62 04 01 2b 23 00 00 00 32 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 2e 2e 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horns ** Controls: KV ** Source: DX100 GLIB patch (Michael Kesti and others) 0e 1f 08 07 0f 00 00 46 04 03 0e 1f 04 08 0f 00 07 4b 04 06 10 1f 05 09 0f 00 00 56 04 03 0f 1f 13 08 0f 00 00 46 04 06 3d 23 00 00 00 62 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 73 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horns ** Controls: KV ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 08 08 07 08 00 03 52 04 03 1f 0a 04 08 0f 00 03 48 04 06 1f 10 05 09 0f 00 04 5b 08 03 1f 05 09 08 0f 00 04 63 08 03 3d 23 00 00 00 62 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 73 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horns ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0d 1f 0b 07 0f 00 00 46 04 03 0e 1f 00 08 0f 00 00 48 04 06 10 1f 00 09 0f 00 00 5b 04 03 0f 1f 00 08 0f 00 00 5b 04 06 3d 23 00 00 00 62 0c 02 04 00 63 63 00 00 00 32 00 48 6f 72 6e 73 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Horns BC ** Controls: KV, BC (EG amplitude) ** Source: Yamaha TX81Z factory preset, bank B, voice 8 0e 09 00 07 0e 00 22 51 04 03 0f 1f 00 08 0f 00 22 52 05 06 11 1f 00 09 0f 00 23 63 04 00 10 1f 00 09 0f 00 2a 60 04 00 3d 1d 00 00 00 52 05 04 04 00 63 32 00 00 00 32 63 48 6f 72 6e 73 20 42 43 20 20 63 63 63 32 32 32 00 08 00 01 00 08 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: JonesBrass ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0b 03 01 07 0d 1f 00 49 04 06 0b 03 01 07 0d 00 00 5d 04 0e 11 01 01 05 0d 00 00 63 04 10 16 04 01 05 0d 00 00 3c 04 10 3c 23 00 00 00 62 0c 02 04 00 63 32 00 32 00 32 00 4a 6f 6e 65 73 42 72 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LightBrass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 04 01 06 0d 00 00 3b 04 04 1d 12 13 06 0c 00 03 41 08 03 1d 0a 09 06 0c 5a 05 42 04 03 10 03 01 09 0f 00 05 60 04 03 02 1c 0f 10 00 42 18 02 05 00 63 32 00 32 00 32 00 4c 69 67 68 74 42 72 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LightBrass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 04 01 06 0d 00 00 3b 04 04 1d 12 13 06 0c 00 00 41 08 03 1d 0a 09 06 0c 5a 00 42 04 03 10 03 01 09 0f 00 01 60 04 03 02 1c 0f 10 00 42 18 02 05 00 63 32 00 32 00 32 00 4c 69 67 68 74 42 72 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 2 ** Name: MelloBrass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 11 02 06 0d 00 02 53 04 00 0e 07 02 07 0c 00 05 5a 04 06 0f 0f 01 08 0f 5a 06 63 04 00 10 12 00 07 0f 00 06 5f 04 05 34 1c 0f 0b 00 42 18 02 05 00 63 32 00 32 00 32 00 4d 65 6c 6c 6f 42 72 61 73 73 63 63 63 32 32 32 00 00 00 20 00 00 00 20 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MelloBrass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 11 02 06 0d 00 02 53 04 00 0e 07 02 07 0c 00 02 5a 04 06 0f 0f 01 08 0f 5a 01 63 04 00 10 12 00 07 0f 00 01 5f 04 05 34 1c 0f 0b 00 42 18 02 05 00 63 32 00 32 00 32 00 4d 65 6c 6c 6f 42 72 61 73 73 63 63 63 32 32 32 00 00 00 20 00 00 00 20 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MufflBrass ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 16 0f 00 09 0d 00 02 52 04 0c 12 0a 00 08 0d 00 02 47 00 08 1a 1f 00 0a 0f 00 01 63 04 0e 17 1f 00 09 0f 00 01 63 00 0a 3c 20 06 03 00 52 18 02 04 00 28 32 00 00 00 32 00 4d 75 66 66 6c 42 72 61 73 73 63 63 63 32 32 32 00 00 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Brass ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 09 09 07 0d 3f 03 4b 08 0b 12 09 09 07 0d 0d 04 52 04 0b 0f 09 09 07 0d 63 05 42 04 13 10 09 09 07 0d 23 06 63 04 0b 7a 20 3e 06 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 20 42 72 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Brass ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 09 09 07 0d 3f 00 4b 08 0b 12 09 09 07 0d 0d 00 52 04 0b 0f 09 09 07 0d 63 00 42 04 13 10 09 09 07 0d 23 00 63 04 0b 7a 20 3e 06 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 20 42 72 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PianoBrass ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 12 06 07 0f 0a 04 5d 04 0b 1b 06 06 07 08 00 06 56 04 0b 15 07 06 08 0f 00 05 5c 04 02 1b 06 06 07 08 00 05 63 04 0d 64 23 00 00 00 22 0c 02 04 00 28 02 00 00 00 32 00 50 69 61 6e 6f 42 72 61 73 73 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PianoBrass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 08 1f 00 05 0f 10 04 4e 04 06 1f 05 00 0f 0b 63 06 46 0d 1b 0a 1f 00 09 0f 63 03 63 04 1b 1f 06 04 06 0f 00 06 63 08 13 3c 23 34 03 00 62 0c 07 04 00 63 32 63 32 00 32 00 50 69 61 6e 6f 42 72 61 73 73 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PianoBrass ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 18 12 06 07 0f 0a 01 5d 04 0b 1b 06 06 07 08 00 01 56 04 0b 15 07 06 08 0f 00 00 5c 04 02 1b 06 06 07 08 00 00 63 04 0d 64 23 00 00 00 22 0c 02 04 00 28 02 00 00 00 32 00 50 69 61 6e 6f 42 72 61 73 73 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PianoBrass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 08 1f 00 05 0f 10 01 4e 04 06 1f 05 00 0f 0b 63 02 46 0d 1b 0a 1f 00 09 0f 63 00 63 04 1b 1f 06 04 06 0f 00 00 63 08 13 3c 23 34 03 00 62 0c 07 04 00 63 32 63 32 00 32 00 50 69 61 6e 6f 42 72 61 73 73 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pianobrass ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0b 03 01 07 0d 1f 00 48 04 0e 16 0c 08 04 0b 34 00 35 2a 16 11 01 01 05 0d 00 00 63 04 10 16 04 01 05 0d 00 00 63 04 10 3c 23 00 00 00 62 0c 02 04 00 63 32 00 32 00 32 00 50 69 61 6e 6f 62 72 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: POWERBRASS ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 10 0f 00 03 0c 00 00 49 0a 06 1a 1f 00 03 0f 00 00 49 04 03 13 1f 00 05 0f 00 00 48 04 03 1b 1f 00 0b 0f 00 00 63 04 03 38 20 14 1f 00 52 0c 07 04 00 63 63 00 00 00 32 00 50 4f 57 45 52 42 52 41 53 53 63 63 63 32 32 32 00 30 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PowerBrass ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 10 0f 00 03 0c 00 00 49 0a 06 1a 1f 00 03 0f 00 00 49 04 03 13 1f 00 05 0f 00 00 48 04 03 1b 1f 00 0b 0f 00 00 63 04 03 38 20 14 1f 00 52 0c 07 04 00 63 63 00 00 00 32 00 50 6f 77 65 72 42 72 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SplatBrass ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 1f 00 08 0f 63 02 5b 04 03 0c 0a 04 09 0a 00 05 54 04 0c 15 1f 00 0a 0b 4c 05 45 0d 03 14 07 03 09 0c 00 05 62 04 03 73 23 01 0d 00 32 18 04 05 00 28 32 00 00 00 32 00 53 70 6c 61 74 42 72 61 73 73 63 63 63 32 32 32 00 40 00 20 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SplatBrass ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 1f 00 08 0f 63 02 5b 04 03 0c 0a 04 09 0a 00 01 54 04 0c 15 1f 00 0a 0b 4c 01 45 0d 03 14 07 03 09 0c 00 01 62 04 03 73 23 01 0d 00 32 18 04 05 00 28 32 00 00 00 32 00 53 70 6c 61 74 42 72 61 73 73 63 63 63 32 32 32 00 40 00 20 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpitBoneBC ** Controls: KV, MW (LFO pitch), BC (EG amplitude) ** Source: Yamaha TX81Z factory preset, bank B, voice 7 0c 05 00 08 0e 00 2a 52 04 0b 10 1f 00 08 0f 00 1a 46 04 03 1f 0f 00 08 06 23 23 48 0b 03 0f 1f 00 09 0f 00 2a 63 04 0c 3a 1f 19 07 00 52 0c 04 04 00 63 32 00 00 00 32 63 53 70 69 74 42 6f 6e 65 42 43 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Brass 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 3 12 0a 00 09 0e 00 01 53 04 08 12 0b 00 09 0e 00 01 52 04 0e 18 1f 00 0b 0f 00 01 62 04 00 18 1f 00 0b 0f 00 01 62 04 06 3c 21 1e 04 00 62 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 42 72 61 73 73 20 31 63 5f 63 31 32 32 00 00 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Brass 2 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank A, voice 4 12 0c 00 09 06 00 01 55 04 08 12 0c 00 09 06 00 01 5a 04 0e 18 1f 00 0b 0f 00 01 62 04 00 18 1f 00 0b 0f 00 01 62 04 06 2c 21 00 00 00 62 0c 02 04 00 28 32 00 00 00 32 00 53 79 2e 42 72 61 73 73 20 32 63 63 63 32 32 32 00 30 00 30 00 70 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Brass 3 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank A, voice 5 0f 08 00 04 0e 07 01 52 04 0b 12 0a 00 07 0b 00 00 2d 04 0b 0f 0b 00 05 0b 00 00 4d 04 0b 11 1f 00 0a 0f 00 01 61 04 0b 3a 1f 00 00 00 62 0c 02 04 00 28 32 00 00 00 32 00 53 79 2e 42 72 61 73 73 20 33 63 63 63 32 32 32 00 30 00 60 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Brass 3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 08 00 04 0e 07 01 52 04 0b 12 0d 00 07 0b 00 00 2d 04 0b 0f 0b 00 05 0b 00 00 4d 04 0b 11 1f 00 0a 0f 00 01 61 04 0b 3a 1f 00 00 00 62 0c 02 04 00 28 32 00 00 00 32 00 53 79 2e 42 72 61 73 73 20 33 63 63 63 32 32 32 00 30 00 60 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Brass 4 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 6 0f 0f 00 01 0e 00 01 4c 04 06 0f 0f 00 01 0e 00 02 4c 04 00 11 09 00 0a 0c 00 00 63 04 06 11 09 00 0a 0c 00 00 63 04 00 3c 1c 1c 0a 00 52 18 02 04 00 63 32 00 00 00 32 00 53 79 2e 42 72 61 73 73 20 34 63 60 63 30 32 32 00 10 00 10 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: TightBrass ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 06 03 09 0a 21 02 5b 04 0b 13 0e 09 0a 0a 00 04 53 08 0c 15 06 06 09 0b 0a 06 63 04 0b 15 06 08 09 0c 00 05 61 04 1b 2c 22 21 00 10 32 18 04 05 00 28 32 00 00 00 32 00 54 69 67 68 74 42 72 61 73 73 63 63 63 32 32 32 00 20 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: TightBrass ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 06 03 09 0a 21 02 5b 04 0b 13 0e 09 0a 0a 00 01 53 08 0c 15 06 06 09 0b 0a 01 63 04 0b 15 06 08 09 0c 00 00 61 04 1b 2c 22 21 00 10 32 18 04 05 00 28 32 00 00 00 32 00 54 69 67 68 74 42 72 61 73 73 63 63 63 32 32 32 00 20 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tpt.Sect.* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 0e 00 09 0e 00 01 54 04 03 15 0f 00 0a 0c 00 00 5f 08 0e 19 11 00 0a 0f 00 00 63 04 01 17 0e 00 0a 0e 00 03 63 08 05 3c 23 00 00 00 52 0c 02 04 00 28 63 00 00 00 32 00 54 70 74 2e 53 65 63 74 2e 2a 63 63 63 32 32 32 00 00 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tpt&Woodwd ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 02 05 0f 1b 42 4d 05 06 07 1f 02 05 0f 27 03 4f 04 00 11 1f 00 07 0f 00 01 63 05 0e 0a 1f 00 04 0f 0c 01 60 04 08 3c 1f 0e 1c 00 32 11 04 05 00 28 32 00 00 00 32 00 54 70 74 26 57 6f 6f 64 77 64 63 63 63 32 32 32 00 31 00 38 00 11 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WindEns..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 0d 03 09 0f 23 02 49 08 05 0f 10 06 07 09 00 02 41 0b 03 12 0c 05 08 0f 45 05 4c 04 02 0c 07 05 07 0b 00 03 5f 04 02 2b 1d 1e 35 00 12 18 02 04 00 63 32 00 00 01 32 00 57 69 6e 64 45 6e 73 2e 2e 31 63 63 63 32 32 32 00 20 00 20 00 00 00 10 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WindEns..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 0d 03 09 0f 23 02 49 08 05 0f 10 06 07 09 00 02 41 0b 03 12 0c 05 08 0f 45 01 4c 04 02 15 07 05 07 0b 00 00 5f 04 02 2b 1d 1e 35 00 12 18 02 04 00 63 32 00 00 01 32 00 57 69 6e 64 45 6e 73 2e 2e 31 63 63 63 32 32 32 00 20 00 20 00 00 00 10 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WindEns..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 03 09 0f 23 02 50 00 05 16 10 06 07 09 00 02 47 04 03 0d 0c 07 08 0f 45 04 4c 00 02 0d 07 05 07 0b 00 04 5f 04 02 2b 1d 1e 35 00 12 18 02 04 00 63 32 00 00 01 32 00 57 69 6e 64 45 6e 73 2e 2e 32 63 63 63 32 32 32 00 10 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WindEns..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0d 03 09 0f 23 02 50 00 05 1f 10 06 07 09 00 01 47 04 03 14 0c 07 08 0f 45 00 4c 00 02 0f 07 05 07 0b 00 00 5f 04 02 2b 1d 1e 35 00 12 18 02 04 00 63 32 00 00 01 32 00 57 69 6e 64 45 6e 73 2e 2e 32 63 63 63 32 32 32 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wood Wind ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 18 12 08 00 0a 0e 36 01 4c 08 08 1f 1f 00 07 0f 00 01 41 08 05 10 1f 00 0c 0f 1a 03 56 04 01 0e 1f 00 0b 0f 00 03 63 08 0e 3c 20 06 04 00 52 0c 02 04 00 28 32 00 00 00 32 00 57 6f 6f 64 20 57 69 6e 64 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 61 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wood Wind ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 08 00 0a 0e 36 01 4c 08 08 1f 1f 00 07 0f 00 01 41 08 05 10 1f 00 0c 0f 1a 03 56 04 01 0e 1f 00 0b 0f 00 03 63 08 0e 3c 20 06 04 00 52 0c 02 04 00 28 32 00 00 00 32 00 57 6f 6f 64 20 57 69 6e 64 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: pianohorn ** Description: a horn sound ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 13 05 00 0e 0a 00 05 59 04 06 1f 09 00 0b 00 43 05 4d 04 13 1f 09 05 08 0e 00 05 59 08 03 1f 12 08 08 0a 00 05 62 04 03 3c 1c 1d 10 00 52 18 01 04 00 63 32 00 32 00 32 00 70 69 61 6e 6f 68 6f 72 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Accordion ** Name: Accordion* ** Controls: BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 04 03 0a 02 10 50 40 00 03 13 04 03 07 02 00 00 43 04 02 1f 04 03 07 02 23 50 38 13 04 0f 04 03 0a 02 10 00 63 08 03 02 23 00 00 00 5a 18 02 04 00 28 00 00 00 00 32 63 41 63 63 6f 72 64 69 6f 6e 2a 63 63 63 32 32 32 00 60 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Accordion ** Controls: KV ** Source: Yamaha DX11 factory preset, bank C, voice 32 14 12 00 08 0f 00 02 54 04 08 1f 1f 00 08 0f 00 00 46 04 0e 0e 1f 00 08 0f 0e 00 53 0a 08 0e 1f 00 08 0f 06 02 5c 08 0e 1c 1c 0a 00 00 62 18 02 04 00 28 32 00 00 00 32 00 41 63 63 6f 72 64 69 6f 6e 20 63 63 63 32 32 32 00 70 00 10 00 10 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Accordion1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 05 03 07 0f 0a 02 3e 19 03 1f 04 00 05 0f 00 06 49 0d 00 12 05 03 0a 0d 63 06 63 08 04 0e 1f 00 0a 0f 00 06 62 04 06 2c 12 00 03 00 62 18 02 04 00 28 02 00 00 00 32 00 41 63 63 6f 72 64 69 6f 6e 31 63 63 63 32 32 32 00 20 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Accordion1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 03 07 0f 0a 01 3e 19 03 1f 04 00 05 0f 00 01 49 0d 00 12 05 03 0a 0d 63 01 63 08 04 0e 1f 00 0a 0f 00 01 62 04 06 2c 12 00 03 00 62 18 02 04 00 28 02 00 00 00 32 00 41 63 63 6f 72 64 69 6f 6e 31 63 63 63 32 32 32 00 20 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Accordion2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 06 00 05 0e 0a 01 41 0a 00 1f 04 00 05 0f 00 05 37 0d 00 1f 0b 00 0a 0f 00 05 36 08 06 0e 1f 00 0a 0f 00 06 5d 04 06 3a 12 00 03 00 62 18 02 04 00 28 02 00 00 00 32 00 41 63 63 6f 72 64 69 6f 6e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Accordion2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 06 00 05 0e 0a 01 41 0a 00 1f 04 00 05 0f 00 00 37 0d 00 1f 0b 00 0a 0f 00 00 36 08 06 0e 1f 00 0a 0f 00 01 5d 04 06 3a 12 00 03 00 62 18 02 04 00 28 02 00 00 00 32 00 41 63 63 6f 72 64 69 6f 6e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Accordion3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 06 00 05 0e 0a 03 3e 1c 00 1f 04 00 05 0f 00 04 37 0d 00 1f 0b 00 0a 0f 00 04 36 0d 06 0e 1f 00 0a 0f 00 06 5c 04 06 3a 12 00 03 00 62 18 02 04 00 28 02 00 00 00 32 00 41 63 63 6f 72 64 69 6f 6e 33 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Accordion3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 06 00 05 0e 0a 02 3e 1c 00 1f 04 00 05 0f 00 00 37 0d 00 1f 0b 00 0a 0f 00 00 36 0d 06 0e 1f 00 0a 0f 00 01 5c 04 06 3a 12 00 03 00 62 18 02 04 00 28 02 00 00 00 32 00 41 63 63 6f 72 64 69 6f 6e 33 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Accordion4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 06 00 05 0e 0a 02 3e 0d 00 1f 04 00 05 0f 00 06 3d 08 00 1f 0b 00 0a 0f 00 05 36 08 06 0e 1f 00 0a 0f 00 06 5c 08 06 3a 12 00 03 00 62 18 02 04 00 28 02 00 00 00 32 00 41 63 63 6f 72 64 69 6f 6e 34 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Accordion4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 06 00 05 0e 0a 02 3e 0d 00 1f 04 00 05 0f 00 01 3d 08 00 1f 0b 00 0a 0f 00 00 36 08 06 0e 1f 00 0a 0f 00 01 5c 08 06 3a 12 00 03 00 62 18 02 04 00 28 02 00 00 00 32 00 41 63 63 6f 72 64 69 6f 6e 34 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Squeezebox ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 32 10 06 00 05 0e 0a 01 4a 0a 00 1f 04 00 05 0f 00 01 49 0d 00 1f 0b 00 0a 0f 00 01 46 08 06 0e 1f 00 0a 0f 00 01 5a 04 06 39 19 00 03 00 52 18 02 04 00 63 32 00 00 00 32 00 53 71 75 65 65 7a 65 62 6f 78 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Bag Pipe ** Name: Bag Pipe ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 0a 04 02 0c 00 05 57 0a 03 17 0a 04 02 0d 00 05 4e 04 03 13 07 06 0a 0c 00 06 60 04 0b 13 07 06 0a 0f 00 06 63 0a 0b 24 26 3c 06 00 32 00 02 04 00 63 32 00 32 00 32 00 20 42 61 67 20 50 69 70 65 20 63 63 63 32 32 32 00 00 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bag Pipe ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 0a 04 02 0c 00 01 57 0a 03 17 0a 04 02 0d 00 00 4e 04 03 13 07 06 0a 0c 00 01 60 04 0b 13 07 06 0a 0f 00 01 63 0a 0b 24 26 3c 06 00 32 00 02 04 00 63 32 00 32 00 32 00 20 42 61 67 20 50 69 70 65 20 63 63 63 32 32 32 00 00 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Bassoon ** Name: Bassoon ** Controls: KV, MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 13 10 17 00 06 0f 00 41 44 00 03 19 1f 00 07 0f 35 42 4e 00 03 10 1f 00 01 0f 00 42 4b 04 03 0f 1f 00 09 0f 00 02 5b 08 03 38 1f 08 07 0a 5e 0c 02 04 00 63 23 1e 32 00 32 00 42 61 73 73 6f 6f 6e 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bassoon..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 00 03 0a 0d 47 05 50 00 03 12 0d 09 0a 0d 41 05 41 04 03 12 0d 09 0a 0d 43 04 62 08 03 12 0d 09 0a 0e 00 05 63 04 04 1c 25 29 00 00 12 0c 02 04 00 63 32 00 32 00 32 00 42 61 73 73 6f 6f 6e 2e 2e 31 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bassoon..1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 00 03 0a 0d 47 01 50 00 03 12 0d 09 0a 0d 41 00 41 04 03 12 0d 09 0a 0d 43 00 62 08 03 12 0d 09 0a 0e 00 00 63 04 04 1c 25 29 00 00 12 0c 02 04 00 63 32 00 32 00 32 00 42 61 73 73 6f 6f 6e 2e 2e 31 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bassoon..2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 11 16 00 06 0f 00 02 38 04 03 18 1f 00 08 0f 47 05 53 00 03 11 1f 00 01 0f 00 04 45 04 03 0f 1f 00 08 0f 00 04 58 08 03 20 20 0c 06 00 52 0c 00 04 00 63 32 00 32 00 32 00 42 61 73 73 6f 6f 6e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bassoon..2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 11 16 00 06 0f 00 00 38 04 03 18 1f 00 08 0f 47 01 53 00 03 11 1f 00 01 0f 00 01 45 04 03 0f 1f 00 08 0f 00 00 58 08 03 20 20 0c 06 00 52 0c 00 04 00 63 32 00 32 00 32 00 42 61 73 73 6f 6f 6e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bassoon..3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 18 06 04 0f 63 01 45 04 03 13 06 06 09 0f 47 03 63 0d 03 13 18 06 09 0f 00 45 63 04 03 12 18 06 09 0f 00 03 63 08 03 35 27 21 00 26 36 0c 00 04 00 63 32 00 32 00 32 00 42 61 73 73 6f 6f 6e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bassoon..3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 18 06 04 0f 63 01 45 04 03 13 06 06 09 0f 47 00 63 0d 03 13 18 06 09 0f 00 40 63 04 03 12 18 06 09 0f 00 00 63 08 03 35 27 21 00 26 26 0c 00 04 00 63 32 00 32 00 32 00 42 61 73 73 6f 6f 6e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bassoon..4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 06 03 0a 0e 63 01 4e 00 03 12 0c 06 0a 0d 47 03 49 08 0b 13 0d 09 0a 0e 00 45 63 08 03 12 0c 06 0a 0e 00 05 63 04 0b 1c 27 21 00 26 26 0c 00 04 00 63 32 00 32 00 32 00 42 61 73 73 6f 6f 6e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bassoon..4 ** Source: from a library of patches collected by Duane Bowker, author unknown 13 06 03 0a 0e 63 00 4e 00 03 12 0c 06 0a 0d 47 00 49 08 0b 13 0d 09 0a 0e 00 40 63 08 03 12 0c 06 0a 0e 00 00 63 04 0b 1c 27 21 00 26 26 0c 00 04 00 63 32 00 32 00 32 00 42 61 73 73 6f 6f 6e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassoonSyn ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 08 05 08 07 19 02 59 00 0e 16 10 00 04 0f 46 05 55 00 03 0f 06 00 0b 0f 00 06 63 04 05 10 1f 00 0a 0f 00 05 60 0a 0b 0d 23 00 00 00 02 0c 07 04 01 63 32 00 1e 0a 32 63 42 61 73 73 6f 6f 6e 53 79 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassoonSyn ** Source: from a library of patches collected by Duane Bowker, author unknown 16 08 05 08 07 19 00 59 00 0e 16 10 00 04 0f 46 00 55 00 03 0f 06 00 0b 0f 00 00 63 04 05 10 1f 00 0a 0f 00 00 60 0a 0b 0d 23 00 00 00 02 0c 07 04 01 63 32 00 1e 0a 32 63 42 61 73 73 6f 6f 6e 53 79 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Clarinet ** Name: Bass Clair ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 12 00 09 0d 22 01 47 08 0c 1f 1b 06 04 0e 41 05 3f 08 03 1f 11 00 06 0c 5a 04 52 0d 03 10 1f 00 0b 0f 00 45 55 04 0c 02 25 00 00 29 16 00 02 05 00 63 32 00 32 00 32 00 42 61 73 73 20 43 6c 61 69 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Clair ** Source: from a library of patches collected by Duane Bowker, author unknown 13 12 00 09 0d 22 00 47 08 0c 1f 1b 06 04 0e 41 00 3f 08 03 1f 11 00 06 0c 5a 00 52 0d 03 10 1f 00 0b 0f 00 40 55 04 0c 02 25 00 00 29 16 00 02 05 00 63 32 00 32 00 32 00 42 61 73 73 20 43 6c 61 69 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clairnet.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 12 00 09 0d 22 01 4c 08 0b 0c 14 00 05 0e 3c 05 3b 04 03 1f 11 00 06 0c 5a 45 4f 10 03 10 1f 00 0b 0f 00 05 55 04 03 3a 22 14 0a 18 5e 0c 02 04 00 63 32 00 32 00 32 00 43 6c 61 69 72 6e 65 74 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clairnet.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 12 00 09 0d 22 01 4c 08 0b 0c 14 00 05 0e 3c 00 3b 04 03 1f 11 00 06 0c 5a 40 4f 10 03 10 1f 00 0b 0f 00 00 55 04 03 3a 22 14 0a 18 5e 0c 02 04 00 63 32 00 32 00 32 00 43 6c 61 69 72 6e 65 74 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clairnet.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 0c 0f 07 0f 41 01 33 0d 05 1f 06 04 06 0f 5c 02 46 0d 04 10 0c 0c 07 0e 47 05 54 0d 03 0f 06 06 07 0e 28 05 63 08 0b 7c 24 27 00 00 12 00 0c 04 00 63 32 32 00 00 32 00 43 6c 61 69 72 6e 65 74 2e 32 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clairnet.2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 0f 07 0f 41 00 33 0d 05 1f 06 04 06 0f 5c 00 46 0d 04 1f 0c 0c 07 0e 47 00 54 0d 03 0f 06 06 07 0e 28 00 63 08 0b 7c 24 27 00 00 12 00 0c 04 00 63 32 32 00 00 32 00 43 6c 61 69 72 6e 65 74 2e 32 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clairnet.3 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 06 06 0f 36 01 51 04 03 1f 1f 04 06 0f 34 44 50 04 03 12 1f 05 09 0f 0e 05 63 00 03 12 1f 05 07 0f 0e 45 63 00 0b 3e 24 02 1f 51 16 24 0c 04 00 63 32 32 00 00 32 00 43 6c 61 69 72 6e 65 74 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clairnet.3 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 06 06 0f 36 01 51 04 03 1f 1f 04 06 0f 34 41 50 04 03 12 1f 05 09 0f 0e 00 63 00 03 12 1f 05 07 0f 0e 40 63 00 0b 3e 24 02 1f 51 16 24 0c 04 00 63 32 32 00 00 32 00 43 6c 61 69 72 6e 65 74 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ClaraPiano ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 07 07 08 07 00 03 47 13 0e 0b 06 07 04 07 00 04 4a 08 0b 13 06 07 03 07 00 04 36 0d 0b 1f 07 07 04 07 00 45 62 04 0b 20 14 00 00 10 66 18 01 04 00 63 00 00 32 00 32 00 43 6c 61 72 61 50 69 61 6e 6f 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ClaraPiano ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 07 07 08 07 00 01 47 13 0e 0b 06 07 04 07 00 01 4a 08 0b 13 06 07 03 07 00 00 36 0d 0b 1f 07 07 04 07 00 40 62 04 0b 20 14 00 00 10 66 18 01 04 00 63 00 00 32 00 32 00 43 6c 61 72 61 50 69 61 6e 6f 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clarinet ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: Yamaha TX81Z factory preset, bank B, voice 15 12 02 00 09 0e 09 02 51 08 0b 0e 14 00 05 0e 00 02 48 08 0b 13 11 00 06 0b 00 43 56 0a 0b 11 1f 00 09 0f 00 02 59 04 0b 02 1f 14 07 10 5e 0c 02 04 00 63 32 1e 00 00 32 00 43 6c 61 72 69 6e 65 74 20 20 63 63 63 32 32 32 00 00 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clarinet 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 19 15 0a 00 07 0e 2f 01 4b 08 03 15 0f 00 07 0a 1b 03 54 08 13 10 10 00 07 0e 1f 03 41 10 0b 11 1f 00 0a 0f 00 02 5c 04 0b 3a 1e 0a 03 05 6e 18 02 04 00 28 1a 0f 00 00 32 00 43 6c 61 72 69 6e 65 74 20 31 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clarinet 1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 0d 00 07 0e 2f 01 4b 08 03 15 0f 00 07 0d 1b 03 54 08 13 10 10 00 07 0e 1f 03 41 10 0b 11 1f 00 0a 0f 00 02 5c 04 0b 3a 1e 0a 03 05 6e 18 02 04 00 28 1a 0f 00 00 32 00 43 6c 61 72 69 6e 65 74 20 31 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clarinet 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 20 1f 1f 00 09 0f 0f 02 4b 08 03 0f 0f 00 09 0a 52 02 4c 08 13 12 12 00 09 0a 3a 01 4a 0a 0b 11 1f 00 0b 0f 00 02 5e 04 0b 22 1e 0a 03 05 6e 0c 02 04 00 28 1a 0f 00 00 32 00 43 6c 61 72 69 6e 65 74 20 32 63 63 63 32 32 32 00 00 00 50 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clarinet 2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 09 0f 0f 02 4b 08 03 0f 0f 00 09 0d 52 02 4c 08 13 12 12 00 09 0d 3a 01 4a 0a 0b 11 1f 00 0b 0f 00 02 5e 04 0b 22 1e 0a 03 05 6e 0c 02 04 00 28 1a 0f 00 00 32 00 43 6c 61 72 69 6e 65 74 20 32 63 63 63 32 32 32 00 00 00 50 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HolowClair ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0b 06 04 0d 22 02 47 04 0c 10 04 03 08 0d 3c 04 44 08 03 0f 10 03 07 0c 04 04 5a 00 03 10 04 03 09 0f 00 05 5a 04 0c 2c 26 07 03 00 42 0c 02 04 00 63 32 00 32 00 32 00 48 6f 6c 6f 77 43 6c 61 69 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HolowClair ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0b 06 04 0d 22 00 47 04 0c 10 04 03 08 0d 3c 01 44 13 03 0f 10 03 07 0c 04 00 5a 00 03 10 04 03 09 0f 00 00 5a 04 0c 2c 26 07 03 00 42 0c 02 04 00 63 32 00 32 00 32 00 48 6f 6c 6f 77 43 6c 61 69 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynClarnet ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 07 06 01 06 0f 21 02 37 08 15 0e 07 01 06 0c 1b 03 45 0d 0b 0e 05 01 06 0b 10 02 4d 16 18 0f 08 00 06 0d 00 01 63 04 13 39 1d 00 00 00 52 18 02 04 00 63 32 00 16 00 32 00 53 79 6e 43 6c 61 72 6e 65 74 63 63 63 32 32 32 00 70 00 30 00 10 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY EnglishHorn ** Name: English Hn ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 0a 04 02 0c 52 02 4b 08 03 1f 0a 04 02 0c 3a 26 47 08 03 10 0a 04 0a 0b 5c 1e 63 08 03 12 07 06 0a 0d 0a 75 60 0a 03 14 1d 3c 00 24 26 0c 02 04 00 63 32 00 00 00 32 00 45 6e 67 6c 69 73 68 20 48 6e 63 63 63 32 32 32 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: English Hn ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 14 0a 04 02 0c 52 02 4b 08 03 1f 0a 04 02 0c 3a 22 47 08 03 10 0a 04 0a 0b 5c 18 63 08 03 12 07 06 0a 0d 0a 70 60 0a 03 14 1d 3c 00 24 26 0c 02 04 00 63 32 00 00 00 32 00 45 6e 67 6c 69 73 68 20 48 6e 63 63 63 32 32 32 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY FlugelHorn ** Name: Flugel * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 0a 00 07 0d 00 01 4d 04 0e 10 0a 00 0a 00 0d 01 3d 04 0b 15 11 00 07 06 05 02 4f 09 0b 0e 07 01 08 0d 00 02 63 04 0b 3a 23 00 00 00 32 18 02 04 00 28 63 00 00 00 32 00 46 6c 75 67 65 6c 20 20 20 2a 63 63 63 32 32 32 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FlugelHorn ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 05 00 08 00 00 01 51 04 0b 0f 1f 00 08 0f 00 04 3c 04 04 1f 0f 00 08 00 00 06 37 07 04 10 1f 00 09 0f 00 06 5d 04 0c 2a 23 19 04 00 52 18 02 04 00 63 63 00 00 00 32 00 46 6c 75 67 65 6c 48 6f 72 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FlugelHorn ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 05 00 08 00 00 01 51 04 0b 0f 1f 00 08 0f 00 00 3c 04 04 1f 0f 00 08 00 00 00 37 07 04 10 1f 00 09 0f 00 01 5d 04 0c 2a 23 19 04 00 52 18 02 04 00 63 63 00 00 00 32 00 46 6c 75 67 65 6c 48 6f 72 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flugelhorn ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 3 0c 05 00 08 00 00 02 4a 04 0b 0f 1f 00 08 0f 00 02 3f 04 03 1f 0f 00 08 00 23 03 44 0b 03 10 1f 00 09 0f 00 02 5e 04 0c 3a 1f 19 07 00 52 18 04 04 00 63 32 00 00 00 32 00 46 6c 75 67 65 6c 68 6f 72 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 3 * SUBCATEGORY Flute ** Name: AdditivFlt ** Controls: KV, MW (LFO pitch), BC (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 02 02 01 01 0f 00 52 3c 1a 01 05 06 01 02 0d 26 63 50 0a 00 04 05 01 01 0c 12 6b 4d 09 06 0d 1f 00 04 0f 00 42 63 04 0b 03 1e 24 07 00 5a 11 02 04 00 63 15 00 08 00 32 0e 41 64 64 69 74 69 76 46 6c 74 63 63 63 32 32 32 00 16 00 30 00 32 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Alto Flute ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0f 05 06 0f 00 00 28 08 03 0c 13 07 07 0f 47 04 3c 04 03 16 1f 04 01 0f 00 45 3d 04 03 0d 12 04 09 0f 00 04 53 04 03 2b 23 29 07 36 16 0c 02 04 00 63 32 00 32 00 32 00 41 6c 74 6f 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Alto Flute ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0f 05 06 0f 00 00 28 08 03 0c 13 07 07 0f 47 00 3c 04 03 16 1f 04 01 0f 00 40 3d 04 03 0d 12 04 09 0f 00 00 53 04 03 2b 23 29 07 36 16 0c 02 04 00 63 32 00 32 00 32 00 41 6c 74 6f 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Alto Flute ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0f 0f 05 06 0f 00 00 28 08 03 0c 13 07 07 0f 47 00 3c 04 03 16 1f 04 00 0f 00 40 3d 04 03 0d 12 04 09 0f 00 00 53 04 03 2b 23 29 07 36 16 0c 02 04 00 63 32 00 32 00 32 00 41 6c 74 6f 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Flute ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0f 05 06 0f 00 03 32 04 03 0c 13 07 07 0f 47 04 3c 00 03 16 1f 04 01 0f 00 44 41 00 03 0d 12 04 09 0f 00 05 5a 04 03 2b 23 29 07 36 16 0c 02 04 00 63 32 00 32 00 32 00 42 61 73 73 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Flute ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0f 05 06 0f 00 00 32 04 03 0c 13 07 07 0f 47 00 3c 00 03 16 1f 04 01 0f 00 40 41 04 03 0d 12 04 09 0f 00 00 5a 00 03 2b 23 29 07 36 16 0c 02 04 00 63 32 00 32 00 32 00 42 61 73 73 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Flute ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0f 0f 05 06 0f 00 00 32 04 03 0c 13 07 07 0f 47 00 3c 00 03 16 1f 04 00 0f 00 40 41 04 03 0d 12 04 09 0f 00 00 5a 00 03 2b 23 29 07 36 16 0c 02 04 00 63 32 00 32 00 32 00 42 61 73 73 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute * ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 03 02 05 0f 19 03 4f 04 0e 1d 12 12 07 0e 00 06 47 05 0c 1d 0a 09 06 0d 00 02 59 04 08 0f 03 01 09 0f 00 04 63 04 0a 02 1d 18 06 19 42 18 02 04 00 28 31 00 29 12 32 00 46 6c 75 74 65 20 20 20 20 2a 63 63 63 32 32 32 00 00 00 04 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute ** Controls: KV, MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 11 10 16 00 06 07 00 42 49 07 03 19 1f 00 07 0f 0b 43 47 04 03 12 19 00 01 0f 0d 42 3a 04 03 0f 1f 00 09 0f 00 42 5a 04 0b 3b 1e 07 07 0d 5e 24 02 04 00 63 1e 40 32 00 32 00 46 6c 75 74 65 20 20 20 20 20 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 22 11 0e 03 05 09 0e 47 39 04 0e 0b 04 00 05 0a 18 42 4d 08 16 11 10 02 05 0a 1b 42 4c 0a 0b 0f 04 00 09 0f 00 43 5a 08 0b 3b 1f 08 20 02 4a 18 02 04 00 28 63 00 00 00 32 00 46 6c 75 74 65 20 20 20 20 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute 1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 11 0e 03 05 09 0e 47 39 04 0e 0b 04 00 05 0d 18 42 4d 08 16 11 10 02 05 0a 1b 42 4c 0d 0b 0f 04 00 09 0f 00 43 5a 08 0b 3b 1f 08 20 02 4a 18 02 04 00 28 63 00 00 00 32 00 46 6c 75 74 65 20 20 20 20 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 23 13 04 00 05 0e 03 41 35 13 10 13 04 00 05 0e 03 42 48 09 16 0f 00 00 09 0f 00 43 63 0a 08 0f 00 00 09 0f 00 43 63 09 0e 3c 1f 08 20 02 4a 05 02 04 00 28 32 00 00 00 32 00 46 6c 75 74 65 20 20 20 20 32 63 63 63 32 32 32 00 00 00 02 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 19 00 01 0d 45 04 5d 08 03 16 15 12 01 0d 59 04 56 08 02 0b 00 03 09 0d 46 05 35 08 04 11 00 03 09 0d 14 04 5a 08 03 2b 1e 14 2d 63 32 18 02 04 00 63 32 00 00 01 32 00 46 6c 75 74 65 2e 2e 2e 2e 31 63 63 63 32 32 32 00 20 00 30 00 00 00 20 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 19 00 01 0d 45 02 5d 04 03 16 15 12 01 0d 59 02 56 08 02 12 00 03 09 0d 46 01 35 04 04 12 00 03 09 0d 14 00 5a 04 03 2b 23 14 07 63 32 18 02 04 00 63 32 00 00 01 32 00 46 6c 75 74 65 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 30 00 00 00 10 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 19 00 01 0d 45 02 4b 04 03 12 15 12 01 0d 59 44 58 04 03 1f 00 03 09 0d 46 45 3c 04 03 0f 00 03 09 0d 14 04 5b 04 03 02 1c 16 2a 00 36 24 02 04 00 63 32 00 00 01 32 00 46 6c 75 74 65 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 19 00 01 0d 45 01 4b 04 03 12 15 12 01 0d 59 41 58 04 03 1f 00 03 09 0d 46 42 3c 04 03 0f 00 03 09 0d 14 00 5b 04 03 02 1e 16 0c 00 36 24 02 04 00 63 32 00 00 01 32 00 46 6c 75 74 65 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 19 00 01 0d 45 00 41 04 03 0f 0d 0f 09 0f 00 43 29 04 03 1f 00 03 09 0d 46 45 33 04 03 0d 00 03 09 0d 14 05 5b 04 03 0a 20 14 35 00 16 24 02 04 00 63 32 00 00 01 32 00 46 6c 75 74 65 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 19 00 01 0d 45 00 41 04 03 0f 0d 0f 09 0f 00 43 29 1f 03 1f 00 03 09 0d 46 41 33 04 03 0d 00 03 09 0d 14 00 5b 04 03 0a 20 14 0a 00 16 24 02 04 00 63 32 00 00 01 32 00 46 6c 75 74 65 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute....4 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 12 00 09 0d 22 03 3b 08 08 1f 12 00 05 0e 2e 45 34 04 00 19 13 00 05 06 00 45 53 07 06 10 1a 00 08 0f 00 04 5d 04 0e 3a 1f 09 0f 1e 5e 18 04 04 00 63 32 00 32 00 32 00 46 6c 75 74 65 2e 2e 2e 2e 34 63 63 63 32 32 32 00 10 00 10 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute....4 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 12 00 09 0d 22 01 3b 0d 08 1f 12 00 05 0e 2e 42 34 0d 00 19 13 00 05 06 00 41 53 12 06 10 1a 00 08 0f 00 00 5d 04 0e 3a 1f 09 0f 1e 5e 18 04 04 00 63 32 00 32 00 32 00 46 6c 75 74 65 2e 2e 2e 2e 34 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute....5 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 17 00 06 00 00 05 36 09 03 15 1f 00 07 0f 47 05 45 08 03 10 1f 00 01 0f 00 04 3b 04 03 10 1f 00 09 0f 00 06 57 04 03 3b 23 04 07 00 52 18 02 04 00 63 32 00 32 00 32 00 46 6c 75 74 65 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flute....5 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 17 00 06 00 00 01 36 09 03 15 1f 00 07 0f 47 01 45 08 03 10 1f 00 01 0f 00 01 3b 04 03 10 1f 00 09 0f 00 00 57 04 03 3b 23 04 07 00 52 18 02 04 00 63 32 00 32 00 32 00 46 6c 75 74 65 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jazz Flute ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: Yamaha TX81Z factory preset, bank C, voice 24 13 1e 00 09 0e 00 00 63 3f 0b 1f 14 00 05 0e 24 01 51 04 03 1f 14 00 0f 01 00 41 4d 08 03 0f 1f 00 0b 0f 00 02 63 04 0b 3b 20 11 12 33 56 18 04 04 00 63 37 0e 00 00 32 00 4a 61 7a 7a 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pan Floot ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: Yamaha TX81Z factory preset, bank B, voice 12 1b 12 00 09 0d 14 02 42 0d 08 1f 14 00 05 0e 00 42 40 08 10 19 14 00 06 06 00 03 50 12 06 10 1f 00 08 0f 00 02 5f 04 0e 02 1e 0d 08 1a 5e 18 04 04 00 63 32 23 00 00 32 00 50 61 6e 20 46 6c 6f 6f 74 20 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pan Flute1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 15 0f 08 0f 2e 05 46 14 02 1f 06 03 07 09 00 04 3e 0a 04 12 06 03 08 0d 00 06 3a 00 0a 10 06 02 09 0e 00 06 63 0a 06 7c 2e 0e 09 00 32 0c 02 04 00 63 32 00 32 00 32 00 50 61 6e 20 46 6c 75 74 65 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pan Flute1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 15 0f 08 0f 2e 00 46 14 02 1f 06 03 07 09 00 00 3e 0a 04 12 06 03 08 0d 00 01 3a 00 0a 10 06 02 09 0e 00 01 63 0a 06 7c 2e 0e 09 00 32 0c 02 04 00 63 32 00 32 00 32 00 50 61 6e 20 46 6c 75 74 65 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pan Flute2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 12 00 09 0d 22 01 46 10 08 1f 14 00 05 0e 24 44 2e 08 00 19 14 00 06 07 00 05 37 04 06 10 1f 00 08 0f 00 05 60 04 0e 3b 1f 0d 0e 1e 5e 18 04 04 00 63 32 00 32 00 32 00 50 61 6e 20 46 6c 75 74 65 32 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pan Flute2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 12 00 09 0d 22 01 46 10 08 1f 14 00 05 0e 24 40 2e 08 00 19 14 00 06 07 00 00 37 04 06 10 1f 00 08 0f 00 01 60 04 0e 3b 1f 0d 0e 1e 5e 18 04 04 00 63 32 00 32 00 32 00 50 61 6e 20 46 6c 75 74 65 32 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pan Flute2 ** Controls: MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 11 17 01 01 06 00 40 63 0d 03 11 17 01 01 06 00 00 35 04 03 10 04 04 0a 07 11 00 3b 04 1b 10 04 04 0a 07 11 00 63 04 02 04 63 00 14 37 47 24 04 04 00 28 32 00 00 00 32 00 50 61 6e 20 46 6c 75 74 65 32 63 63 63 32 32 32 00 30 00 30 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PanFlute * ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 19 07 0f 0f 3b 00 37 18 00 1f 1f 11 0f 0f 00 03 5a 0d 03 0c 19 0a 08 0f 00 02 59 04 02 0f 1f 04 07 0f 00 01 63 04 03 04 63 00 0c 13 6f 18 02 04 00 3b 1f 00 00 00 31 00 50 61 6e 46 6c 75 74 65 20 2a 63 63 63 32 32 32 00 02 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Perc Flute ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 12 09 0a 00 07 5a 10 05 1f 1f 1d 07 0f 47 05 48 04 04 15 1f 00 0a 0f 00 46 63 04 03 13 15 15 09 0a 00 05 63 04 01 04 23 29 07 36 16 18 02 04 00 63 32 00 32 00 32 00 50 65 72 63 20 46 6c 75 74 65 63 63 63 32 32 32 00 20 00 40 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Perc Flute ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 12 09 0a 00 01 5a 10 05 1f 1f 1d 07 0f 47 00 48 04 04 15 1f 00 0a 0f 00 41 63 04 03 13 15 15 09 0a 00 00 63 04 01 04 23 29 07 36 16 18 02 04 00 63 32 00 32 00 32 00 50 65 72 63 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 40 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PercFlute ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 31 1f 17 11 0a 0d 00 02 55 13 09 1f 19 00 01 0d 00 42 47 0a 00 1f 14 09 09 0b 00 02 5d 09 06 10 08 00 06 0e 00 42 63 04 0b 43 1c 04 0b 00 5a 05 04 04 00 63 32 00 00 00 32 00 50 65 72 63 46 6c 75 74 65 20 63 63 63 32 32 32 00 00 00 00 00 02 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Soft Flute ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 17 00 06 00 00 03 34 07 03 0f 1f 00 07 0f 47 03 3b 08 03 10 1f 00 01 0f 00 45 2c 04 03 0f 1f 00 09 0f 00 05 5e 08 03 2b 23 12 0a 35 16 18 02 04 00 63 32 00 32 00 32 00 53 6f 66 74 20 46 6c 75 74 65 63 63 63 32 32 32 00 03 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Soft Flute ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 17 00 06 00 00 00 34 07 03 0f 1f 00 07 0f 47 00 3b 04 03 10 1f 00 01 0f 00 40 2c 04 03 0f 1f 00 09 0f 00 02 5e 08 03 2b 23 12 0a 35 16 18 02 04 00 63 32 00 32 00 32 00 53 6f 66 74 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Song Flute ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 12 15 01 0e 02 03 58 08 0b 15 1c 15 0a 0d 00 03 63 08 08 07 01 03 0a 0d 00 05 63 04 0b 12 01 03 0a 0d 00 06 63 04 0b 2e 21 00 05 00 42 18 02 04 00 63 32 00 32 00 32 00 53 6f 6e 67 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Song Flute ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 12 15 01 0e 02 00 58 08 0b 15 1c 15 0a 0d 00 00 63 08 08 07 01 03 0a 0d 00 00 63 04 0b 12 01 03 0a 0d 00 00 63 04 0b 2e 21 00 05 00 42 18 02 04 00 63 32 00 32 00 32 00 53 6f 6e 67 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Song Flute ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 18 12 15 00 0e 02 00 58 08 0b 15 1c 15 0a 0d 00 00 63 08 08 07 01 03 0a 0d 00 00 63 04 0b 12 01 03 0a 0d 00 00 63 04 0b 2e 21 00 05 00 42 18 02 04 00 63 32 00 32 00 32 00 53 6f 6e 67 20 46 6c 75 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthFlute ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 12 18 01 02 0b 16 03 58 21 03 12 18 00 02 0d 00 02 4b 10 13 0d 0e 04 04 07 00 04 63 16 13 0d 18 03 04 0e 00 01 5e 04 13 14 63 00 0c 00 33 18 02 04 00 28 32 00 00 00 32 00 53 79 6e 74 68 46 6c 75 74 65 63 63 63 32 32 32 00 40 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY FrenchHorn ** Name: Fr.Horn * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 08 03 04 0f 00 02 54 00 00 0a 08 05 07 0f 0a 02 5a 00 06 0e 1f 00 06 0f 1d 01 63 04 06 16 0d 00 06 0f 00 00 63 00 00 34 16 00 00 00 32 18 02 04 00 28 63 00 00 00 32 00 46 72 2e 48 6f 72 6e 20 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FrenchHorn ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: Yamaha TX81Z factory preset, bank B, voice 5 08 05 00 06 00 00 42 47 04 08 0e 1f 00 06 0f 00 42 43 04 00 1f 0e 00 06 00 0e 43 3b 0b 06 0c 1f 00 06 0f 00 42 61 04 0c 39 1a 19 08 0a 4a 0c 04 04 00 63 32 1e 00 00 32 00 46 72 65 6e 63 68 48 6f 72 6e 63 63 63 32 32 32 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FrnchHorn1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 05 00 08 00 00 01 4b 04 0b 0f 1f 00 08 0f 00 03 36 04 04 0d 0f 00 08 00 00 05 39 0b 04 12 1f 00 09 0f 00 06 62 04 0c 3a 25 00 04 00 22 18 02 04 00 63 63 00 00 00 32 00 46 72 6e 63 68 48 6f 72 6e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FrnchHorn1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 05 00 08 00 00 01 4b 04 0b 0f 1f 00 08 0f 00 00 36 04 04 0d 0f 00 08 00 00 00 39 0b 04 12 1f 00 09 0f 00 01 62 04 0c 3a 25 00 04 00 22 18 02 04 00 63 63 00 00 00 32 00 46 72 6e 63 68 48 6f 72 6e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FrnchHorn2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 05 00 08 00 00 05 4e 04 0b 0f 1f 00 08 0f 00 04 48 04 03 0d 0f 00 08 00 00 05 40 0d 03 12 1f 00 09 0f 00 05 5f 04 0b 1b 25 00 0a 00 32 18 02 04 00 63 63 00 00 00 32 00 46 72 6e 63 68 48 6f 72 6e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FrnchHorn2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 05 00 08 00 00 02 4e 04 0b 0f 1f 00 08 0f 00 01 48 04 03 0d 0f 00 08 00 00 00 40 0d 03 12 1f 00 09 0f 00 01 5f 04 0b 1b 25 00 0a 00 32 18 02 04 00 63 63 00 00 00 32 00 46 72 6e 63 68 48 6f 72 6e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Harmonica ** Name: Harmonic.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 0d 01 0a 0f 0a 03 4e 08 03 12 0c 06 09 0f 00 04 4f 04 0e 10 1f 04 0c 0f 00 06 63 08 03 1d 1f 00 0d 0f 00 05 4e 04 03 64 29 00 00 00 22 0c 02 04 00 28 02 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonic.1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 10 0d 01 0a 0f 0a 01 4e 08 03 12 0c 06 09 0f 00 01 4f 04 0e 10 1f 04 0c 0f 00 00 63 08 03 1d 1f 00 0d 0f 00 00 4e 04 03 64 29 00 00 00 22 0c 02 04 00 28 02 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonic.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 0d 01 0a 0f 0a 02 57 04 0b 12 0c 06 09 0f 00 05 56 00 0e 10 1f 04 0c 0f 00 05 5a 04 0b 1d 1f 00 0d 0f 00 06 63 04 03 6c 29 00 00 00 22 18 02 04 00 28 02 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonic.2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 0d 01 0a 0f 0a 02 57 04 0b 12 0c 06 09 0f 00 01 56 00 0e 10 1f 04 0c 0f 00 00 5a 04 0b 1d 1f 00 0d 0f 00 00 63 04 03 6c 29 00 00 00 22 18 02 04 00 28 02 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonica* ** Controls: KV, BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 0d 01 09 0d 00 01 42 00 03 0c 08 00 09 0c 00 00 3e 00 06 0e 09 01 09 0f 00 60 3c 10 03 0d 05 01 09 0f 00 40 63 04 00 19 1f 21 00 25 3a 24 02 04 00 28 63 00 00 00 32 29 48 61 72 6d 6f 6e 69 63 61 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonica ** Controls: KV, MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 16 10 09 02 06 0d 00 41 3e 04 15 0c 0b 02 03 0e 00 41 4f 04 12 0c 0e 02 08 0d 00 42 4b 0a 1c 0d 0b 01 09 0e 00 03 63 0a 14 00 23 0f 02 1b 5e 18 04 05 00 63 28 50 32 00 32 00 48 61 72 6d 6f 6e 69 63 61 20 63 63 63 32 32 32 00 50 10 50 00 50 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonica ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 25 0e 1f 00 09 0f 00 02 4b 0a 0b 0c 1f 00 09 0f 00 41 5f 04 0b 0c 1f 00 09 0f 00 41 5c 04 0b 0a 1f 00 0a 0f 00 41 5a 04 0b 06 1d 04 01 27 56 18 02 04 00 28 32 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 61 20 63 63 63 32 32 32 00 40 00 50 00 30 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonica ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 1f 00 09 0f 00 02 4b 0a 0b 0c 1f 00 09 0f 00 41 5f 04 0b 0c 1f 00 09 0f 00 41 5c 04 0b 0d 1f 00 0a 0f 00 41 5a 04 0b 06 1d 04 01 27 56 18 02 04 00 28 32 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 61 20 63 63 63 32 32 32 00 40 00 50 00 30 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonica1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 09 0f 00 79 29 17 00 1f 1f 00 03 0f 00 5b 3b 04 03 0f 10 09 09 0f 00 5d 61 04 03 0d 1f 00 08 0f 00 7c 63 08 03 04 1f 00 0b 00 46 18 02 04 00 63 32 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 61 31 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonica1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 09 0f 00 79 29 17 00 1f 1f 00 03 0f 00 59 3b 04 03 0f 10 09 09 0f 00 59 61 04 03 0d 1f 00 08 0f 00 78 63 08 03 04 1f 00 0b 00 46 18 02 04 00 63 32 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 61 31 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonica2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 1f 09 0a 00 3a 2f 2c 00 10 0a 05 0a 06 00 1c 4b 04 03 0f 10 09 09 0f 00 1e 5b 04 03 0d 1f 00 08 0f 00 3d 63 04 03 04 1d 3c 04 00 00 18 02 04 00 63 32 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 61 32 63 63 63 32 32 32 00 10 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonica2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 1f 09 0a 00 3a 2f 2c 00 10 0a 05 0a 06 00 19 4b 04 03 0f 10 09 09 0f 00 19 5b 04 03 0d 1f 00 08 0f 00 38 63 04 03 04 1d 3c 04 00 10 18 02 04 00 63 32 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 61 32 63 63 63 32 32 32 00 10 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonica3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 09 0f 00 7a 2a 08 00 1f 1f 00 07 0f 00 5a 3b 00 03 0f 10 09 09 0f 00 5d 5a 08 03 0d 1f 00 08 0f 00 7d 63 04 03 06 1f 00 0b 00 46 18 02 04 00 63 32 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 61 33 63 63 63 32 32 32 00 00 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harmonica3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 09 0f 00 7a 2a 08 00 1f 1f 00 07 0f 00 58 3b 00 03 0f 10 09 09 0f 00 58 5a 08 03 0d 1f 00 08 0f 00 78 63 04 03 06 1f 00 0b 00 46 18 02 04 00 63 32 00 00 00 32 00 48 61 72 6d 6f 6e 69 63 61 33 63 63 63 32 32 32 00 00 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Oboe ** Name: BC1 Oboe ** Controls: MW (LFO pitch), BC (LFO pitch), BC (LFO amplitude), BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 11 00 07 0f 63 40 63 00 1b 0f 0d 00 08 0f 12 40 40 00 1b 0e 18 00 08 0f 63 78 50 0a 1b 09 0f 00 09 0f 00 78 54 08 1b 22 23 0b 08 0a 56 18 07 04 00 63 32 00 18 1e 32 63 42 43 31 20 4f 62 6f 65 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oboe * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0a 00 02 0c 23 43 33 04 00 1f 0a 00 02 0d 22 42 3a 08 06 10 07 00 0a 0f 08 01 63 08 08 0f 07 00 0a 0f 09 01 63 0a 0e 04 22 14 19 1b 3a 18 02 04 00 28 63 00 00 00 32 00 4f 62 6f 65 20 20 20 20 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oboe ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: Yamaha TX81Z factory preset, bank B, voice 14 10 12 00 09 0d 05 01 51 00 03 12 14 00 05 0e 23 41 5b 00 03 10 0a 00 06 0d 30 02 4b 08 1b 0f 1a 00 0a 0e 06 03 59 08 0b 02 1f 11 08 0e 5e 24 02 04 00 63 28 19 00 00 32 00 4f 62 6f 65 20 20 20 20 20 20 63 63 63 32 32 32 00 20 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oboe ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 21 14 12 00 07 0a 0a 02 53 0a 0b 1f 1f 00 03 0f 00 01 4e 04 0b 15 14 00 05 0a 09 01 3c 08 0b 12 1f 00 0b 0f 06 03 5a 0a 0b 03 1f 0a 0f 00 52 18 02 04 00 28 32 00 00 00 32 00 4f 62 6f 65 20 20 20 20 20 20 63 63 63 32 32 32 00 20 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oboe ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 14 12 00 07 0d 0a 02 53 0a 0b 1f 1f 00 03 0f 00 01 4e 04 0b 15 14 00 05 0d 09 01 3c 08 0b 12 1f 00 0b 0f 06 03 5a 0a 0b 03 1f 0a 0f 00 52 18 02 04 00 28 32 00 00 00 32 00 4f 62 6f 65 20 20 20 20 20 20 63 63 63 32 32 32 00 20 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oboe.....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 04 06 07 0c 00 02 4d 08 0e 10 04 06 07 0c 00 03 4d 04 03 0f 04 06 07 0c 00 04 50 10 04 10 04 06 07 0c 00 04 5b 04 02 0d 22 07 0c 43 52 0c 02 04 00 63 63 00 00 00 32 00 4f 62 6f 65 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oboe.....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 04 06 07 0c 00 01 4d 08 0e 10 04 06 07 0c 00 00 4d 04 03 0f 04 06 07 0c 00 00 50 10 04 10 04 06 07 0c 00 00 5b 04 02 0d 22 07 0c 43 52 0c 02 04 00 63 63 00 00 00 32 00 4f 62 6f 65 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oboe.....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 04 06 07 0c 00 01 46 0d 0e 10 04 06 07 0c 00 04 45 04 03 0f 04 06 07 0c 00 05 5f 08 04 10 04 06 07 0c 00 04 5f 08 02 34 22 07 0c 43 52 18 02 04 00 63 63 00 00 00 32 00 4f 62 6f 65 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oboe.....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 04 06 07 0c 00 01 46 0d 0e 10 04 06 07 0c 00 00 45 04 03 0f 04 06 07 0c 00 00 5f 08 04 10 04 06 07 0c 00 00 5f 0a 02 34 22 07 0c 43 52 18 02 04 00 63 63 00 00 00 32 00 4f 62 6f 65 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oboe.....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 11 00 09 0f 63 41 55 00 0c 0f 0d 00 08 0f 12 43 47 00 1c 0d 18 00 08 00 63 45 49 0a 1b 09 0f 00 09 0f 00 44 50 0a 1b 72 23 25 08 1e 56 18 02 05 00 63 63 00 00 00 32 00 4f 62 6f 65 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oboe.....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 11 00 09 0f 63 41 55 00 0c 0f 0d 00 08 0f 12 40 47 00 1c 0e 18 00 08 00 63 41 49 0a 1b 09 0f 00 09 0f 00 40 50 0a 1b 72 23 25 08 1e 56 18 02 05 00 63 63 00 00 00 32 00 4f 62 6f 65 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Oboe ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 10 02 06 0b 1b 04 49 08 0b 14 08 07 07 0b 42 42 35 2a 0b 1e 0c 04 07 0b 42 44 50 08 0b 16 06 01 09 0b 0a 04 60 0a 03 42 22 0a 11 1e 36 0c 04 04 00 63 32 00 00 00 32 00 4f 72 6e 74 20 4f 62 6f 65 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Oboe ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 10 02 06 0b 1b 00 49 08 0b 14 08 07 07 0b 42 40 35 2a 0b 1e 0c 04 07 0b 42 40 50 08 0b 16 06 01 09 0b 0a 00 60 0a 03 42 22 0a 11 1e 36 0c 04 04 00 63 32 00 00 00 32 00 4f 72 6e 74 20 4f 62 6f 65 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Ocarina ** Name: Ocarina ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 03 18 03 0f 00 04 63 0d 0b 13 03 0f 06 03 00 05 31 0d 12 13 1d 00 0a 0f 00 06 5d 0d 0b 12 03 1f 09 0e 00 07 5a 0d 13 3c 2e 27 07 36 22 0c 02 04 00 63 63 00 00 00 32 00 4f 63 61 72 69 6e 61 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ocarina ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 03 18 03 0f 00 01 63 0d 0b 13 03 0f 06 03 00 00 31 0d 12 13 1d 00 0a 0f 00 01 5d 0d 0b 12 03 1f 09 0e 00 01 5a 0d 13 3c 2e 27 07 36 22 0c 02 04 00 63 63 00 00 00 32 00 4f 63 61 72 69 6e 61 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Piccolo ** Name: Piccolo * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 12 00 05 0f 2c 40 31 23 06 0c 12 00 05 0f 13 40 3c 13 06 0e 13 00 09 0f 0a 02 63 12 08 10 13 00 09 0f 0b 01 63 13 09 3c 21 0d 0e 21 4a 11 02 04 00 28 63 00 00 00 32 00 50 69 63 63 6f 6c 6f 20 20 2a 63 63 63 32 32 32 00 17 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Piccolo..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 15 04 0a 09 00 06 32 12 03 15 03 04 0a 08 47 06 63 08 03 0f 04 05 0a 09 00 06 63 08 03 10 06 04 0a 09 00 05 63 08 03 06 28 29 00 00 12 18 02 04 00 63 32 00 32 00 32 00 50 69 63 63 6f 6c 6f 2e 2e 31 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Piccolo..1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 15 04 0a 09 00 01 32 12 03 15 03 04 0a 08 47 01 63 04 03 15 04 05 0a 09 00 00 63 04 03 15 06 04 0a 09 00 00 63 08 03 06 28 29 00 00 12 18 02 04 00 63 32 00 32 00 32 00 50 69 63 63 6f 6c 6f 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Piccolo..2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 1f 0a 0e 00 00 2c 08 03 12 04 0c 0a 0e 47 04 63 08 03 12 09 09 0a 0e 00 05 63 08 03 12 12 0f 0a 0e 00 05 63 08 03 06 28 22 07 00 22 18 02 04 00 63 32 00 32 00 32 00 50 69 63 63 6f 6c 6f 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Piccolo..2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 1f 0a 0e 00 00 2c 04 03 12 04 0c 0a 0e 47 01 63 0d 03 12 09 09 0a 0e 00 03 63 0d 03 12 12 0f 0a 0e 00 00 63 04 03 06 28 22 07 00 22 18 02 04 00 63 32 00 32 00 32 00 50 69 63 63 6f 6c 6f 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Piccolo..3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 05 05 0a 0e 00 04 4a 19 03 12 05 05 0a 0e 00 04 50 0d 03 12 05 05 0a 0e 00 04 52 0d 03 12 05 05 0a 0e 00 04 58 0d 03 06 28 29 00 00 12 18 02 04 00 63 32 00 32 00 32 00 50 69 63 63 6f 6c 6f 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Piccolo..3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 05 05 0a 0e 00 01 4a 19 03 12 05 05 0a 0e 00 01 50 0d 03 12 05 05 0a 0e 00 01 52 0d 03 12 05 05 0a 0e 00 00 58 0d 03 06 28 29 00 00 12 18 02 04 00 63 32 00 32 00 32 00 50 69 63 63 6f 6c 6f 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 4 * SUBCATEGORY Recorder ** Name: OrntRecord ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 18 00 09 0c 00 03 4e 16 03 12 16 12 0a 07 00 04 51 08 03 12 12 00 09 0d 00 05 63 04 03 12 16 0f 09 06 00 05 5e 04 03 34 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 52 65 63 6f 72 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntRecord ** Source: from a library of patches collected by Duane Bowker, author unknown 13 18 00 09 0c 00 00 4e 16 03 12 16 12 0a 07 00 00 51 08 03 12 12 00 09 0d 00 00 63 04 03 12 16 0f 09 06 00 00 5e 04 03 34 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 52 65 63 6f 72 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 24 14 0a 00 0a 0a 00 44 36 08 06 14 00 00 03 0f 00 42 2d 0a 00 14 1f 0f 08 0f 00 44 32 04 03 11 08 00 0b 0e 02 43 63 04 06 3a 1b 04 0f 06 46 18 02 04 00 28 43 00 00 00 32 00 52 65 63 6f 72 64 65 72 20 20 63 63 63 31 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 14 0a 00 0a 0a 00 44 36 08 06 14 00 00 03 0f 00 42 2d 0d 00 14 1f 0f 08 0f 00 44 32 04 03 11 08 00 0b 0e 02 43 63 04 06 3a 1b 04 0f 06 46 18 02 04 00 28 43 00 00 00 32 00 52 65 63 6f 72 64 65 72 20 20 63 63 63 31 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 1f 16 01 0e 00 01 5d 08 1b 0f 00 03 0a 0d 00 44 61 04 0b 0f 00 03 0a 0d 00 05 61 04 0b 0f 00 03 0a 0d 00 06 61 04 0b 2e 25 2a 26 36 16 18 02 04 00 63 63 00 00 00 32 00 52 65 63 6f 72 64 65 72 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 1f 16 01 0e 00 01 5d 08 1b 0f 00 03 0a 0d 00 41 61 04 0b 0f 00 03 0a 0d 00 01 61 04 0b 0f 00 03 0a 0d 00 01 61 04 0b 2e 25 2a 26 36 16 18 02 04 00 63 63 00 00 00 32 00 52 65 63 6f 72 64 65 72 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 1f 10 01 0e 00 03 00 08 1b 0d 0a 06 0a 00 00 05 63 08 03 0d 15 07 09 0c 00 45 63 08 03 0f 12 07 09 0c 00 05 5f 08 03 45 20 2a 15 43 26 18 02 04 00 63 63 00 00 00 32 00 52 65 63 6f 72 64 65 72 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder.2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 1f 10 01 0e 00 00 00 08 1b 0d 0a 06 0a 00 00 00 63 08 03 0d 15 07 09 0c 00 40 63 08 03 0f 12 07 09 0c 00 00 5f 08 03 45 20 2a 15 43 26 18 02 04 00 63 63 00 00 00 32 00 52 65 63 6f 72 64 65 72 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder.2 ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 18 1f 10 00 0e 00 00 00 08 1b 0d 0a 06 0a 00 00 00 63 08 03 0d 15 07 09 0c 00 40 63 08 03 0f 12 07 09 0c 00 00 5f 08 03 45 20 2a 15 43 26 18 02 04 00 63 63 00 00 00 32 00 52 65 63 6f 72 64 65 72 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 1c 15 0c 0e 00 01 3f 08 1b 13 1d 15 0c 0e 00 06 63 08 03 10 03 03 0a 0d 00 45 63 04 0b 10 03 03 0a 0d 00 05 63 08 0b 76 20 2a 15 43 26 18 02 04 00 63 63 00 00 00 32 00 52 65 63 6f 72 64 65 72 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 1c 15 0c 0e 00 01 3f 08 1b 13 1d 15 0c 0e 00 00 63 08 03 10 03 03 0a 0d 00 41 63 04 0b 10 03 03 0a 0d 00 01 63 04 0b 76 20 2a 15 43 26 18 02 04 00 63 63 00 00 00 32 00 52 65 63 6f 72 64 65 72 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder.4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 1c 15 0c 0e 00 01 3f 08 1b 13 1d 15 0c 0e 00 05 63 08 03 10 03 03 0a 0d 00 46 63 04 0b 10 03 03 0a 0d 00 07 63 04 0b 76 20 2a 15 43 26 18 02 04 00 63 63 00 00 00 32 00 52 65 63 6f 72 64 65 72 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Recorder.4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 18 1d 15 0a 0f 00 01 5e 08 13 10 0f 0a 0a 0f 00 01 62 04 0b 0f 09 0a 0c 04 00 01 5e 04 0b 10 1f 03 09 0f 00 01 63 00 0b 2e 21 2a 00 00 12 18 02 04 00 63 63 00 00 00 32 00 52 65 63 6f 72 64 65 72 2e 34 63 63 63 32 32 32 00 30 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Saxophone ** Name: Alto Sax ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 1f 00 08 0f 31 00 52 00 1b 0c 1f 00 09 0f 3a 03 44 08 1b 11 1f 00 08 0f 00 01 37 12 03 0f 1f 00 08 0f 00 00 63 00 03 78 23 00 00 00 42 18 02 04 00 63 32 00 00 00 32 00 41 6c 74 6f 20 53 61 78 20 20 63 63 63 32 32 32 00 00 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BC1 AltSax ** Controls: KV, BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 1f 00 08 0f 31 25 63 00 1c 0c 1f 00 08 0f 3a 25 49 00 1b 11 1f 00 08 0f 00 1d 35 12 04 0f 1f 00 08 0f 00 3d 63 04 03 28 23 00 00 00 42 18 05 04 00 63 32 00 0a 56 32 63 42 43 31 20 41 6c 74 53 61 78 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sax ** Controls: BC (EG amplitude) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 10 1f 00 09 0f 00 20 44 00 03 10 1f 00 09 0f 2a 20 42 00 03 10 1f 00 09 0f 00 18 30 12 06 10 1f 00 09 0f 00 38 63 04 03 3a 21 00 00 00 52 18 02 04 00 63 32 00 00 00 32 63 3c 42 43 31 3e 20 53 61 78 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BCSexyPhon ** Controls: KV, MW (LFO pitch), BC (EG amplitude) ** Source: Yamaha TX81Z factory preset, bank C, voice 19 1d 04 00 06 0e 00 01 51 04 09 1d 1f 00 03 0f 11 01 4b 04 08 1d 0f 00 08 0a 00 01 46 27 0b 10 1f 00 0a 0f 00 3b 63 08 0b 3a 1e 17 0c 00 52 18 02 0d 0f 63 32 01 00 00 32 63 42 43 53 65 78 79 50 68 6f 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ElectroSax ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 0a 00 09 0e 0d 39 52 00 0e 1f 0b 00 0a 0a 1c 1a 46 00 08 1f 12 00 0a 0d 09 1a 4c 10 0b 16 1b 00 0a 0f 00 02 63 00 0b 3a 1e 09 02 00 62 18 02 04 00 28 0b 00 00 00 32 00 45 6c 65 63 74 72 6f 53 61 78 63 63 63 32 32 32 00 20 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GreatDXled ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 00 0a 0f 0c 04 40 08 03 12 0c 06 09 0f 00 05 5c 04 06 1b 1f 04 0c 0f 00 05 5c 04 03 18 1f 00 0c 0f 00 06 62 04 03 7c 29 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 47 72 65 61 74 44 58 6c 65 64 63 63 63 32 32 32 00 10 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GreatDXled ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0d 00 0a 0f 0c 01 40 08 03 12 0c 06 09 0f 00 01 5c 04 06 1b 1f 04 0c 0f 00 00 5c 04 03 18 1f 00 0c 0f 00 00 62 04 03 7c 29 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 47 72 65 61 74 44 58 6c 65 64 63 63 63 32 32 32 00 10 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MelloTenor ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 9 11 04 00 06 0e 33 01 4c 04 0b 0f 1f 00 03 0f 34 02 50 04 0b 10 0e 00 08 0c 63 03 44 22 13 10 1f 00 0a 0f 00 02 63 08 0b 3a 1e 11 0b 00 52 0c 04 05 00 63 32 02 32 00 32 00 4d 65 6c 6c 6f 54 65 6e 6f 72 63 63 63 32 32 32 30 30 20 30 20 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RaspAlto ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 10 10 1f 00 08 0f 00 42 4a 00 03 11 1f 00 08 0f 0a 43 51 00 03 11 09 00 08 0d 00 42 2b 16 06 10 1f 00 09 0f 00 42 63 04 0b 3a 1e 09 09 00 5a 24 04 04 00 63 32 31 00 00 32 00 52 61 73 70 41 6c 74 6f 20 20 63 63 63 32 32 32 00 10 00 20 00 54 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sax 1 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank C, voice 16 14 08 00 08 0a 17 03 49 04 0b 10 0c 00 09 0a 21 04 45 04 0b 12 13 00 06 0b 1b 04 50 0a 0b 14 1f 00 0a 0f 00 01 5f 04 0b 32 20 00 00 00 52 0c 02 04 00 28 32 00 00 00 32 00 53 61 78 20 20 20 20 20 20 31 63 63 63 32 32 32 00 00 00 10 00 60 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sax 1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 14 08 00 08 0d 17 03 49 04 0b 10 0c 00 09 0d 21 04 45 04 0b 12 13 00 06 0b 1b 04 50 0a 0b 14 1f 00 0a 0f 00 01 5f 04 0b 32 20 00 00 00 52 0c 02 04 00 28 32 00 00 00 32 00 53 61 78 20 20 20 20 20 20 31 63 63 63 32 32 32 00 00 00 10 00 60 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sax 2 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank C, voice 17 10 08 00 08 0a 2c 03 49 04 0b 11 0f 00 09 0a 0f 04 4e 04 0b 15 13 00 06 09 00 04 41 0e 0b 12 13 00 0a 0a 00 01 5f 08 0b 32 20 00 00 00 52 0c 02 04 00 28 32 00 00 00 32 00 53 61 78 20 20 20 20 20 20 32 63 63 63 32 32 32 00 00 00 10 00 60 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sax 2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 10 08 00 08 0d 2c 03 49 04 0b 11 0f 00 09 0d 0f 04 4e 04 0b 15 13 00 06 09 00 04 41 0e 0b 12 13 00 0a 0d 00 01 5f 08 0b 32 20 00 00 00 52 0c 02 04 00 28 32 00 00 00 32 00 53 61 78 20 20 20 20 20 20 32 63 63 63 32 32 32 00 00 00 10 00 60 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sax Synth ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 06 12 04 03 31 03 63 00 0c 0a 1f 00 08 0f 3a 26 59 00 1b 0a 1f 00 08 0f 00 1d 29 13 04 11 1f 00 08 0f 00 3e 63 04 03 28 23 00 00 00 42 18 05 04 01 63 32 00 00 00 32 00 53 61 78 20 53 79 6e 74 68 20 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sax Synth ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 10 06 12 04 03 31 01 63 00 0c 0a 1f 00 08 0f 3a 21 59 00 1b 0a 1f 00 08 0f 00 19 29 13 04 11 1f 00 08 0f 00 38 63 04 03 28 23 00 00 00 42 18 05 04 01 63 32 00 00 00 32 00 53 61 78 20 53 79 6e 74 68 20 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone* ** Controls: KV, MW (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0a 04 00 06 0e 1d 61 45 04 0b 0f 1f 00 03 0f 36 02 54 04 0b 0c 0e 00 08 0c 0f 63 4d 22 0b 18 1f 00 0a 0f 00 02 63 08 0b 39 1e 11 0b 00 4a 0c 02 05 00 63 32 04 00 00 32 23 53 61 78 6f 70 68 6f 6e 65 2a 63 63 63 32 32 32 00 30 00 30 30 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 03 01 09 0f 00 01 37 00 03 12 07 04 09 0f 2a 22 4a 04 03 10 04 02 09 0f 00 1d 3d 12 06 11 03 03 09 0f 00 3c 58 04 03 7b 22 0b 07 00 42 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 31 63 63 63 32 32 32 00 00 00 20 00 44 00 10 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 03 01 09 0f 00 01 37 00 03 12 07 04 09 0f 2a 22 4a 00 03 1f 04 02 09 0f 00 1a 3d 12 06 11 03 03 09 0f 00 38 58 04 03 7b 22 0b 07 00 42 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 31 63 63 63 32 32 32 00 00 00 20 00 40 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 1e 00 08 0f 00 00 2e 03 03 0e 1f 00 09 0d 2a 22 4b 04 03 11 1f 00 09 0e 00 1b 3b 12 06 11 05 00 08 0e 00 3b 5e 04 03 3b 21 00 00 00 52 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 32 63 63 63 32 32 32 00 00 00 40 00 14 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 13 1e 00 08 0f 00 00 37 03 03 0e 1f 00 09 0d 2a 22 4b 00 03 11 1f 00 09 0e 00 1b 3b 12 06 11 05 00 08 0e 00 38 5e 04 03 3b 21 00 00 00 52 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 32 63 63 63 32 32 32 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 03 01 08 0f 19 00 41 04 03 0f 1f 00 09 0d 4b 23 4f 04 03 1f 03 00 09 0e 3d 1d 3d 0f 06 10 1e 00 08 0e 00 3d 63 04 03 3b 21 00 00 00 52 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 33 63 63 63 32 32 32 00 00 00 10 00 43 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 14 03 01 08 0f 19 00 41 00 03 0f 1f 00 09 0d 4b 22 4f 00 03 1f 03 00 09 0e 3d 1a 3d 15 06 10 1e 00 08 0e 00 39 63 04 03 3b 21 00 00 00 52 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 33 63 63 63 32 32 32 00 00 00 10 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 1f 00 08 0f 31 02 52 04 1c 0c 1f 00 07 0f 3a 25 42 00 1b 11 1f 00 08 0f 00 1c 3d 12 04 0f 1f 00 08 0f 00 3c 63 00 03 78 23 00 00 00 42 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 34 63 63 63 32 32 32 00 00 00 30 00 04 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 1f 00 08 0f 31 02 52 00 1c 0c 1f 00 07 0f 3a 22 42 04 1b 11 1f 00 08 0f 00 19 3d 12 04 0f 1f 00 08 0f 00 38 63 04 03 78 23 00 00 00 42 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 34 63 63 63 32 32 32 00 00 00 30 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 0f 00 09 0f 0a 03 4f 00 03 11 1f 00 08 0e 14 04 4e 00 0b 11 0f 00 08 0e 00 05 2f 17 03 11 1f 00 09 0f 00 04 63 04 03 32 1e 11 0a 00 52 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 35 63 63 63 32 32 32 00 10 00 30 00 42 00 30 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 0f 00 09 0f 0a 01 4f 00 03 11 1f 00 08 0e 14 01 4e 00 0b 11 0f 00 08 0e 00 03 2f 17 03 11 1f 00 09 0f 00 01 63 04 03 32 1e 11 0a 00 52 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 35 63 63 63 32 32 32 00 10 00 30 00 42 00 30 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 1e 00 08 0f 00 03 46 04 03 0c 1f 00 09 0d 2a 24 4b 04 03 0f 1f 00 09 0e 00 1c 39 10 03 11 1e 00 08 0e 00 3c 63 04 03 7b 21 05 09 00 52 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 36 63 63 63 32 32 32 00 40 00 40 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saxophone6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 1e 00 08 0f 00 00 46 04 03 0c 1f 00 09 0d 2a 21 4b 00 03 0f 1f 00 09 0e 00 19 39 04 03 11 1e 00 08 0e 00 38 63 04 03 7b 21 05 09 00 52 18 02 04 00 63 32 00 00 00 32 00 53 61 78 6f 70 68 6f 6e 65 36 63 63 63 32 32 32 00 00 00 30 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SopranoSax ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 1f 00 08 0f 0c 42 2c 04 03 13 1f 00 08 0f 26 42 58 00 03 11 09 00 08 08 2d 42 1e 16 06 11 1f 00 09 0f 00 44 63 04 0b 02 1e 09 09 00 5a 24 04 04 00 63 32 31 12 00 32 00 53 6f 70 72 61 6e 6f 53 61 78 63 63 63 32 32 32 00 00 00 20 00 50 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Trombone ** Name: Mute Trmbn ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 1f 00 08 0f 00 01 4a 00 03 15 1f 00 08 0f 00 03 2c 00 03 16 19 12 09 0f 01 05 54 00 0b 12 1f 00 08 0f 00 05 5e 04 03 3a 27 00 29 00 22 0c 06 04 00 63 63 00 00 00 32 00 4d 75 74 65 20 54 72 6d 62 6e 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mute Trmbn ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 1f 00 08 0f 00 01 4a 00 03 15 1f 00 08 0f 00 00 2c 00 03 16 19 12 09 0f 01 01 54 00 0b 12 1f 00 08 0f 00 01 5e 04 03 3a 27 00 29 00 22 0c 06 04 00 63 63 00 00 00 32 00 4d 75 74 65 20 54 72 6d 62 6e 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trombone * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 0a 00 08 0d 00 03 4e 04 0e 0c 0a 00 06 0d 00 02 53 04 0e 1f 08 00 08 0d 14 03 2a 0a 0b 0e 07 00 08 0d 00 00 63 04 0b 3a 1d 00 00 00 52 0c 05 04 00 28 63 00 00 00 32 00 54 72 6f 6d 62 6f 6e 65 20 2a 63 63 63 32 32 32 00 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trombone ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 13 0e 0b 00 09 0e 0f 02 50 00 0b 10 0c 00 0a 0a 1c 02 47 00 0b 10 11 00 0a 05 09 03 47 06 0b 0f 18 00 0b 0f 00 02 63 00 0b 3a 20 09 03 00 52 18 02 04 00 28 32 00 00 00 32 00 54 72 6f 6d 62 6f 6e 65 20 20 63 52 63 2f 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trombone ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0f 1f 00 08 0f 00 00 42 04 03 0f 1f 00 08 0f 00 00 2e 04 03 1f 0e 00 08 00 01 00 52 0b 13 13 1f 00 08 0f 00 00 5a 04 04 3a 23 00 00 00 62 0c 02 04 00 63 63 00 00 00 32 00 54 72 6f 6d 62 6f 6e 65 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trombone.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 1f 00 08 0f 00 01 52 04 03 15 1f 00 08 0f 00 04 36 04 03 14 0e 00 08 00 01 05 57 08 13 12 1f 00 08 0f 00 06 5e 04 03 32 27 00 29 00 22 0c 06 04 00 63 63 00 00 00 32 00 54 72 6f 6d 62 6f 6e 65 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trombone.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 1f 00 08 0f 00 01 52 04 03 15 1f 00 08 0f 00 01 36 04 03 14 0e 00 08 00 01 01 57 08 13 12 1f 00 08 0f 00 01 5e 04 03 32 27 00 29 00 22 0c 06 04 00 63 63 00 00 00 32 00 54 72 6f 6d 62 6f 6e 65 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trombone.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 1f 00 08 0f 00 01 4f 00 03 15 1f 00 08 0f 00 04 2b 04 03 0f 10 05 08 06 01 06 50 0b 13 12 1f 00 08 0f 00 04 5d 04 03 3a 27 00 29 00 22 0c 06 04 00 63 63 00 00 00 32 00 54 72 6f 6d 62 6f 6e 65 2e 32 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trombone.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 1f 00 08 0f 00 01 4f 00 03 15 1f 00 08 0f 00 02 2b 04 03 0f 10 05 08 06 01 00 50 0b 13 12 1f 00 08 0f 00 00 5d 04 03 3a 27 00 29 00 22 0c 06 04 00 63 63 00 00 00 32 00 54 72 6f 6d 62 6f 6e 65 2e 32 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Trumpet ** Name: 2 Trumpets ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 05 00 08 0b 0f 05 58 04 08 10 1f 00 07 0f 0f 05 54 05 0e 0f 10 00 0c 00 1a 04 2d 0a 0c 10 1f 00 07 0f 00 07 60 04 0b 02 23 19 06 00 52 11 04 04 00 63 1d 00 08 27 32 00 32 20 54 72 75 6d 70 65 74 73 63 63 63 32 32 32 00 38 00 31 20 02 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BC1Trumpet ** Controls: MW (LFO pitch), BC (EG amplitude) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 10 1f 00 08 0f 00 38 48 04 0b 0e 1f 00 08 0f 00 20 3c 04 03 10 1f 00 08 0f 00 38 24 0b 03 12 1f 00 09 0f 00 20 63 04 0b 3a 23 13 04 00 62 18 02 04 00 63 63 00 00 00 32 63 42 43 31 54 72 75 6d 70 65 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BC1 Trumpt ** Controls: KV, MW (LFO pitch), BC (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 1f 00 08 0f 00 3d 49 04 0b 0e 1f 00 08 0f 00 25 42 04 03 10 1f 00 08 0f 00 3d 20 0b 03 12 1f 00 09 0f 00 26 63 04 0b 2a 23 13 04 00 62 18 02 04 00 63 63 00 03 03 32 63 42 43 31 20 54 72 75 6d 70 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FanfarTpts ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 29 1f 06 06 04 0f 00 40 48 05 00 0c 08 07 05 0f 05 00 52 04 00 0f 07 03 07 0e 08 03 63 04 06 0f 08 04 05 0f 00 03 63 04 06 7c 1f 00 06 00 4a 11 02 04 00 63 32 00 00 00 32 00 46 61 6e 66 61 72 54 70 74 73 63 63 63 32 32 32 06 01 00 38 00 08 00 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mute Trmpt ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0a 08 08 0d 00 01 4c 08 00 15 1b 07 08 0a 00 04 43 08 14 0f 10 07 07 09 02 05 36 0a 14 12 07 09 09 0f 00 06 63 08 0c 1a 22 23 05 00 22 0c 02 04 00 63 63 00 00 00 32 00 4d 75 74 65 20 54 72 6d 70 74 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mute Trmpt ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0a 08 08 0d 00 01 4c 04 00 15 1b 07 08 0a 00 00 43 0a 14 0f 10 07 07 09 02 00 36 0b 14 12 07 09 09 0f 00 00 63 0a 0c 1a 22 23 05 00 22 0c 02 04 00 63 63 00 00 00 32 00 4d 75 74 65 20 54 72 6d 70 74 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Trmpt ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 11 05 06 07 0d 00 02 32 04 03 12 10 06 07 0d 00 03 55 04 0b 13 16 06 07 09 00 05 4a 04 13 12 10 06 07 0f 01 04 63 04 0b 7b 24 00 0c 00 52 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 20 54 72 6d 70 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Trmpt ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 11 05 06 07 0d 00 00 32 04 03 12 10 06 07 0d 00 00 55 04 0b 13 16 06 07 09 00 00 4a 04 13 12 10 06 07 0f 01 00 63 04 0b 7b 24 00 0c 00 52 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 20 54 72 6d 70 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet * ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 09 00 07 0e 0a 53 52 04 10 15 1f 00 09 0f 00 2a 63 04 03 15 1f 00 09 0f 00 2a 63 04 04 15 1f 00 09 0f 00 12 63 04 02 3d 63 00 0e 00 4a 18 02 04 00 28 63 00 48 00 32 00 54 72 75 6d 70 65 74 20 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 0c 05 00 06 0f 00 02 49 04 03 19 1f 00 07 0f 0b 03 47 04 03 12 0b 00 01 0f 0d 02 3a 04 03 10 1f 00 09 0f 00 42 5a 04 0c 02 1c 19 00 00 5e 18 04 04 00 63 0b 00 00 19 32 63 54 72 75 6d 70 65 74 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 10 1f 00 08 0f 00 38 48 04 0b 0e 1f 00 08 0f 00 20 3c 04 03 10 1f 00 08 0f 00 38 24 0b 03 12 1f 00 09 0f 00 20 63 04 0b 3a 23 13 04 00 62 18 02 04 00 63 63 00 00 00 32 00 20 20 20 54 72 75 6d 70 65 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 11 12 10 00 09 0a 0a 01 4e 04 0b 13 0e 00 0a 0a 1c 02 43 04 0b 10 0f 00 0a 05 09 03 40 06 0b 14 18 00 0b 0f 00 02 63 04 0b 3a 1f 09 04 00 62 18 02 04 00 28 13 00 00 00 32 00 54 72 75 6d 70 65 74 20 20 31 63 63 63 32 32 32 00 00 00 30 00 30 00 50 00 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet 1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 10 00 09 0d 0d 01 4e 04 0b 13 0e 00 0a 0a 1c 02 43 04 0b 10 0f 00 0a 05 09 03 40 06 0b 14 18 00 0b 0f 00 02 63 04 0b 3a 1f 09 04 00 62 18 02 04 00 28 13 00 00 00 32 00 54 72 75 6d 70 65 74 20 20 31 63 63 63 32 32 32 00 00 00 30 00 30 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 12 10 0e 00 03 0e 00 01 4e 04 03 0c 0e 00 03 0e 28 01 40 04 03 0c 0c 00 09 00 00 07 33 03 03 13 09 00 0a 0f 12 04 63 04 03 3a 20 14 1a 00 32 18 02 04 00 63 3c 00 00 00 32 00 54 72 75 6d 70 65 74 20 20 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet 3 ** Description: A trumpet similar to DX11 factory preset 'Trumpet 2' ** Controls: KV ** Source: Art Hitzeman 0e 0d 00 03 0e 00 01 4e 00 03 0a 0d 00 03 0e 28 01 40 04 03 0a 0b 00 09 00 00 07 33 03 03 11 08 00 0a 0f 12 04 63 04 03 3a 20 14 1a 00 32 24 02 04 00 63 00 00 00 00 32 00 54 72 75 6d 70 65 74 20 20 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0a 07 09 0f 00 02 50 04 0a 0f 0f 05 0a 00 00 06 46 07 13 0f 07 09 07 0b 00 06 42 04 0b 12 07 05 0a 0d 00 07 63 04 04 7a 1d 1e 0f 00 42 0c 02 04 00 63 63 00 00 00 32 00 54 72 75 6d 70 65 74 2e 2e 31 63 63 63 32 32 32 00 00 00 03 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0a 07 09 0f 00 02 50 04 0a 0f 0f 05 0a 00 00 01 46 07 13 0f 07 09 07 0b 00 00 42 04 0b 12 07 05 0a 0d 00 01 63 04 04 7a 1d 1e 0f 00 42 0c 02 04 00 63 63 00 00 00 32 00 54 72 75 6d 70 65 74 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0a 07 09 0f 00 41 4e 04 0a 0f 0f 05 0a 00 00 04 41 04 13 0f 10 06 07 0e 02 06 46 0b 0b 12 07 05 0a 0d 00 06 63 04 04 3a 23 1e 17 10 36 0c 02 04 00 63 63 00 00 00 32 00 54 72 75 6d 70 65 74 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0a 07 09 0f 00 41 4e 04 0a 0f 0f 05 0a 00 00 00 41 04 13 0f 10 06 07 0e 02 00 46 0b 0b 12 07 05 0a 0d 00 01 63 04 04 3a 23 1e 17 10 36 0c 02 04 00 63 63 00 00 00 32 00 54 72 75 6d 70 65 74 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet..3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0a 06 08 0d 00 02 59 04 0a 15 1c 06 08 0c 00 04 4f 04 15 0f 10 06 09 0e 02 06 49 0d 09 13 07 06 09 0d 00 06 63 04 04 2a 23 00 00 00 22 0c 02 04 00 63 63 00 00 00 32 00 54 72 75 6d 70 65 74 2e 2e 33 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet..3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0a 06 08 0d 00 02 59 04 0a 15 1c 06 08 0c 00 00 4f 04 15 0f 10 06 09 0e 02 00 49 0d 09 13 07 06 09 0d 00 01 63 04 04 2a 23 00 00 00 22 0c 02 04 00 63 63 00 00 00 32 00 54 72 75 6d 70 65 74 2e 2e 33 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet..4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0a 06 08 0d 00 42 53 04 0a 15 1c 06 08 0c 00 03 4d 04 13 0f 03 06 07 09 02 06 31 10 0a 13 07 06 09 0d 00 07 63 04 04 3a 22 00 08 2e 46 0c 02 04 00 63 63 00 00 00 32 00 54 72 75 6d 70 65 74 2e 2e 34 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet..4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0a 06 08 0d 00 42 53 04 0a 15 1c 06 08 0c 00 00 4d 04 13 0f 03 06 07 09 02 00 31 10 0a 13 07 06 09 0d 00 01 63 04 04 3a 22 00 08 2e 46 0c 02 04 00 63 63 00 00 00 32 00 54 72 75 6d 70 65 74 2e 2e 34 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet..5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 0a 06 08 0d 00 02 4d 04 0a 15 1c 06 08 0c 00 05 46 04 13 0f 03 06 07 09 02 06 34 07 0a 15 07 06 09 0d 00 06 63 04 04 2a 22 23 05 00 22 0c 02 04 00 63 63 00 00 00 32 00 54 72 75 6d 70 65 74 2e 2e 35 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet..5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 0a 06 08 0d 00 02 4d 04 0a 15 1c 06 08 0c 00 00 46 04 13 0f 03 06 07 09 02 00 34 07 0a 15 07 06 09 0d 00 01 63 04 04 2a 22 23 05 00 22 0c 02 04 00 63 63 00 00 00 32 00 54 72 75 6d 70 65 74 2e 2e 35 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet81Z ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 1 0f 1f 04 08 0f 00 3a 51 04 0b 0e 1f 00 08 0f 00 22 4b 04 03 0f 1a 0b 08 0d 00 3b 36 09 03 12 1f 00 08 0f 00 22 5d 04 0b 3a 1e 19 05 00 52 18 04 04 00 63 32 00 00 00 32 00 54 72 75 6d 70 65 74 38 31 5a 63 63 63 32 32 32 00 00 00 50 00 51 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: TrumptSolo ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 18 13 00 00 04 0f 00 00 3a 0b 06 0f 1f 03 0a 0f 05 01 5d 05 00 11 0e 06 0a 09 08 00 36 03 06 13 1f 01 08 0f 00 02 63 04 03 3b 1d 03 0f 01 42 11 07 04 00 63 32 00 00 00 32 00 54 72 75 6d 70 74 53 6f 6c 6f 63 63 63 32 32 32 00 61 00 11 00 06 00 08 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Trumpet V ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0f 1f 04 08 0f 00 3a 52 04 0b 0e 1f 00 08 0f 00 22 4b 04 03 10 1f 0b 08 0f 00 3b 2f 0b 03 12 1f 00 09 0f 00 22 63 04 0b 3a 1e 13 04 00 42 18 02 04 00 63 63 00 00 00 00 00 54 72 75 6d 70 65 74 20 56 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Tuba ** Name: << Tuba >> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 05 06 0d 45 03 4c 00 03 14 0d 02 07 09 59 04 48 00 03 0f 10 03 07 09 46 05 48 00 03 0e 0f 03 08 0f 14 06 63 04 03 2b 23 00 00 00 32 0c 02 04 00 63 32 00 00 01 32 00 3c 3c 20 54 75 62 61 20 3e 3e 63 63 63 32 32 32 00 00 00 00 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: << Tuba >> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 05 06 0d 45 01 4c 00 03 14 0d 02 07 09 59 00 48 00 03 0f 10 03 07 09 46 00 48 00 03 0e 0f 03 08 0f 14 01 63 04 03 2b 23 00 00 00 32 0c 02 04 00 63 32 00 00 01 32 00 3c 3c 20 54 75 62 61 20 3e 3e 63 63 63 32 32 32 00 00 00 00 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tuba ** Controls: KV ** Source: Yamaha DX11 factory preset, bank C, voice 15 0f 0f 00 09 09 15 01 52 00 03 10 0a 00 06 0a 00 01 47 00 03 10 0c 00 09 0b 00 01 45 00 03 10 0a 00 0b 0a 0e 02 63 00 03 3a 1f 00 00 00 52 0c 02 04 00 63 32 00 00 00 32 00 54 75 62 61 20 20 20 20 20 20 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tuba ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0f 00 09 09 15 01 52 00 03 10 0d 00 06 0a 00 01 47 00 03 10 0c 00 09 0b 00 01 45 00 03 10 0d 00 0b 0d 0e 02 63 00 03 3a 1f 00 00 00 52 0c 02 04 00 63 32 00 00 00 32 00 54 75 62 61 20 20 20 20 20 20 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Whistle ** Name: Whistle * ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 00 00 08 03 0b 10 00 09 0e 00 00 63 08 02 0a 0f 1c 09 00 00 00 63 08 00 0c 0f 00 09 0e 00 00 63 08 06 05 20 16 15 43 52 18 0c 04 00 28 63 00 00 00 32 00 57 68 69 73 74 6c 65 20 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Whistle..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 1c 15 03 0e 00 03 00 08 03 13 06 03 0a 0d 00 44 00 08 03 13 06 03 0a 0d 00 45 00 08 03 13 06 03 0a 0d 00 06 63 08 03 3e 25 0b 26 63 36 18 04 04 00 28 32 00 00 00 32 00 57 68 69 73 74 6c 65 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Whistle..1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 1c 15 03 0e 00 00 00 08 03 13 06 03 0a 0d 00 40 00 08 03 13 06 03 0a 0d 00 40 00 08 03 13 06 03 0a 0d 00 00 63 08 03 3e 25 0b 26 63 36 18 04 04 00 28 32 00 00 00 32 00 57 68 69 73 74 6c 65 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Whistle..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 08 1f 07 05 0f 47 05 62 0d 05 08 1f 02 05 0f 00 03 3e 0d 02 07 1f 02 05 0f 4e 04 3c 0d 0b 07 1f 02 05 0f 00 04 63 0d 1b 57 22 00 14 00 32 0c 04 04 00 28 32 00 00 00 32 00 57 68 69 73 74 6c 65 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Whistle..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 08 1f 07 05 0f 47 01 62 0d 05 08 1f 02 05 0f 00 01 3e 0d 02 07 1f 02 05 0f 4e 01 3c 0d 0b 07 1f 02 05 0f 00 00 63 0d 1b 57 22 00 14 00 32 0c 04 04 00 28 32 00 00 00 32 00 57 68 69 73 74 6c 65 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Whistle..3 ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 00 08 00 00 03 42 08 03 19 10 00 09 08 00 04 42 04 03 0f 09 00 08 0f 00 06 5d 08 03 0d 08 00 09 0e 1a 06 62 08 03 3c 23 09 0b 00 42 18 02 04 00 63 32 00 00 00 33 00 57 68 69 73 74 6c 65 2e 2e 33 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Whistle..3 ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 00 08 00 00 01 42 08 03 19 10 00 09 08 00 00 42 04 03 0f 09 00 08 0f 00 01 5d 08 03 0d 08 00 09 0e 1a 01 62 08 03 3c 23 09 0b 00 42 18 02 04 00 63 32 00 00 00 33 00 57 68 69 73 74 6c 65 2e 2e 33 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Whistling ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 20 1f 0f 00 08 00 00 04 4e 07 03 19 10 00 09 08 00 00 38 01 03 0f 09 00 08 0f 00 02 4e 08 03 0d 08 00 09 0e 1a 02 5e 08 03 3c 23 03 0f 00 62 18 02 04 00 63 32 00 00 00 33 00 57 68 69 73 74 6c 69 6e 67 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 5 * CATEGORY STRINGS * SUBCATEGORY Strings ** Name: AnlgStr..1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 0a 00 07 0e 28 04 51 04 08 0e 05 12 08 0c 00 00 4f 04 04 0c 0a 00 07 0e 07 05 63 04 00 0d 04 12 08 0c 14 04 5f 04 03 2c 26 21 20 47 12 0c 06 04 00 63 32 00 32 00 32 00 41 6e 6c 67 53 74 72 2e 2e 31 63 63 63 32 32 32 00 40 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 0a 00 07 0e 28 01 51 04 08 0e 05 12 08 0c 00 00 4f 04 04 0c 0a 00 07 0e 07 00 63 04 00 0d 04 12 08 0c 14 00 5f 04 03 2c 26 21 20 47 12 0c 06 04 00 63 32 00 32 00 32 00 41 6e 6c 67 53 74 72 2e 2e 31 63 63 63 32 32 32 00 40 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 0a 00 07 0e 1e 02 49 04 08 0d 0c 01 07 0e 00 01 4d 04 04 0d 09 00 07 0f 05 04 5b 04 00 0b 09 00 07 0e 14 03 5f 04 02 74 26 21 11 47 32 0c 06 04 00 63 32 00 32 00 32 00 41 6e 6c 67 53 74 72 2e 2e 32 63 63 63 32 32 32 00 50 00 30 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 0a 00 07 0e 1e 00 49 04 08 0d 0c 01 07 0e 00 00 4d 04 04 0d 09 00 07 0f 05 00 5b 04 00 0b 09 00 07 0e 14 00 5f 04 02 74 26 21 11 47 32 0c 06 04 00 63 32 00 32 00 32 00 41 6e 6c 67 53 74 72 2e 2e 32 63 63 63 32 32 32 00 50 00 30 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 09 03 06 0e 28 02 39 0d 0b 0e 0a 01 04 0e 00 02 4b 08 04 0d 0a 02 05 0f 0a 04 59 08 03 0b 0a 01 05 0d 14 04 5d 08 0b 2c 26 21 16 3b 22 0c 06 04 00 63 32 00 32 00 32 00 41 6e 6c 67 53 74 72 2e 2e 33 63 63 63 32 32 32 00 20 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..3 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 09 03 06 0e 28 00 39 0d 0b 0e 0a 01 04 0e 00 00 4b 08 04 0d 0a 02 05 0f 0a 00 59 08 03 0b 0a 01 05 0d 14 00 5d 08 0b 2c 26 21 16 3b 22 0c 06 04 00 63 32 00 32 00 32 00 41 6e 6c 67 53 74 72 2e 2e 33 63 63 63 32 32 32 00 20 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 00 05 0e 00 42 44 0d 02 0d 07 0e 06 06 0e 44 57 08 04 0c 1f 00 06 0f 52 05 53 0d 04 0b 1f 06 08 0f 0a 03 63 01 04 2d 22 0a 0a 08 26 0c 07 04 00 63 63 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 34 63 63 63 32 32 32 00 30 00 20 00 00 00 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..4 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 00 05 0e 00 40 44 0d 02 0d 07 0e 06 06 0e 40 57 08 04 0c 1f 00 06 0f 52 00 53 0d 04 0b 1f 06 08 0f 0a 00 63 01 04 2d 22 0a 0a 08 36 0c 07 04 00 63 63 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 34 63 63 63 32 32 32 00 30 00 20 00 00 00 17 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 03 1e 05 04 56 04 48 04 06 0c 03 1e 05 04 23 01 49 0a 0d 09 1f 01 08 0f 15 03 57 08 08 09 1f 00 05 0f 25 03 5b 04 02 54 20 0f 0a 00 52 0c 0c 04 00 63 32 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 35 63 63 63 32 32 32 00 00 00 40 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..5 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 03 1e 05 04 56 00 48 04 06 0c 03 1e 05 04 23 00 49 0a 0d 09 1f 01 08 0f 15 00 57 08 08 09 1f 00 05 0f 25 00 5b 04 02 54 20 0f 0a 00 52 0c 0c 04 00 63 32 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 35 63 63 63 32 32 32 00 00 00 40 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 03 1e 05 04 56 04 41 1f 06 0d 03 1e 05 04 23 02 48 0d 0d 0a 1f 01 08 0f 15 03 5b 08 08 0a 1f 00 05 0f 25 04 5d 08 01 54 20 0f 0a 00 52 0c 0c 04 00 63 32 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 36 63 63 63 32 32 32 00 50 00 20 00 30 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..6 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 03 1e 05 04 56 00 41 1f 06 0d 03 1e 05 04 23 00 48 0d 0d 0a 1f 01 08 0f 15 00 5b 08 08 0a 1f 00 05 0f 25 00 5d 08 01 54 20 0f 0a 00 52 0c 0c 04 00 63 32 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 36 63 63 63 32 32 32 00 50 00 20 00 30 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..7 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0f 04 05 0d 56 04 3a 1f 06 0e 0b 01 05 0e 23 02 4b 08 05 0d 15 03 08 0f 15 04 57 08 00 0b 04 00 05 0f 25 04 5b 08 01 6c 20 0f 0a 00 52 0c 0c 04 00 63 32 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..7 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0f 04 05 0d 56 00 3a 1f 06 0e 0b 01 05 0e 23 00 4b 08 05 0d 15 03 08 0f 15 00 57 08 00 0b 04 00 05 0f 25 00 5b 08 01 6c 20 0f 0a 00 52 0c 0c 04 00 63 32 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..8 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 0b 05 05 0d 0a 42 44 08 11 0d 04 02 06 0d 0e 43 5c 08 02 0c 07 06 06 0f 52 05 56 19 03 0a 03 06 08 0f 0a 04 63 08 00 2d 22 0e 0c 08 56 0c 07 04 00 63 63 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 38 63 63 63 32 32 32 00 60 00 30 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..8 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 0b 05 05 0d 0a 40 44 08 11 0d 04 02 06 0d 0e 40 5c 08 02 0c 07 06 06 0f 52 00 56 19 03 0a 03 06 08 0f 0a 00 63 08 00 2d 22 0e 0c 08 56 0c 07 04 00 63 63 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 38 63 63 63 32 32 32 00 60 00 30 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..9 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 0b 05 05 0e 00 42 42 04 03 0d 0c 00 05 0e 0e 41 3c 08 0e 0a 09 06 06 0f 52 04 5e 08 02 0a 03 00 06 0f 0a 04 5d 08 04 24 1e 0e 0c 08 56 00 07 04 00 63 63 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 39 63 63 63 32 32 32 00 30 00 50 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr..9 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 0b 05 05 0e 00 40 42 0d 03 0d 0c 00 05 0e 0e 40 3c 13 0e 0a 09 06 06 0f 52 00 5e 04 02 0c 03 00 06 0f 0a 00 5d 08 04 24 22 0e 0c 08 56 00 07 04 00 63 63 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 2e 39 63 63 63 32 32 32 00 30 00 10 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr.10 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 03 1e 05 04 56 03 4c 04 00 0c 03 1e 05 04 23 02 48 04 0e 09 1f 01 08 0f 15 04 5f 04 0e 09 1f 00 05 0f 25 03 60 04 06 54 17 13 29 00 32 0c 0c 04 00 63 32 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 31 30 63 63 63 32 32 32 00 00 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr.10 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 03 1e 05 04 56 00 4c 00 00 0c 03 1e 05 04 23 00 48 08 0e 09 1f 01 08 0f 15 00 5f 00 0e 09 1f 00 05 0f 25 00 60 00 06 54 17 13 29 00 32 0c 0c 04 00 63 32 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 31 30 63 63 63 32 32 32 00 00 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr.11 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 0b 02 05 0c 23 03 49 04 08 10 03 1e 05 04 58 03 48 0d 00 0c 0b 01 07 0e 26 05 5c 08 06 0c 1f 00 05 0f 25 03 60 04 03 24 21 19 0e 00 22 24 0c 04 00 63 32 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 31 31 63 63 63 32 32 32 00 00 00 30 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgStr.11 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 0b 02 05 0c 23 00 49 04 08 10 03 1e 05 04 58 00 48 0d 00 0c 0b 01 07 0e 26 00 5c 08 06 0c 1f 00 05 0f 25 00 60 04 03 24 21 19 0e 00 22 24 0c 04 00 63 32 00 00 00 32 00 41 6e 6c 67 53 74 72 2e 31 31 63 63 63 32 32 32 00 00 00 30 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BC1 String ** Controls: KV, MW (LFO pitch), BC (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 03 1e 05 04 56 04 53 0d 06 0c 03 1e 05 04 23 3c 52 08 0d 09 1f 01 08 0f 15 05 57 08 08 09 1f 00 05 0f 25 06 5b 04 01 64 20 0f 0a 00 52 0c 0c 04 00 63 32 00 05 09 32 63 42 43 31 20 53 74 72 69 6e 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DualStr..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 03 02 04 08 2d 02 4a 08 08 17 0c 07 01 0f 3f 02 3d 0d 04 0c 0b 04 07 0f 12 04 61 08 05 0c 08 00 06 0b 25 04 5b 08 06 2c 21 1e 0f 00 22 18 0c 04 00 63 32 00 00 00 32 00 44 75 61 6c 53 74 72 2e 2e 31 63 63 63 32 32 32 00 00 00 40 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DualStr..1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 03 02 04 08 2d 00 4a 08 08 1f 0c 07 01 0f 3f 00 3d 1c 04 09 10 04 07 0f 12 00 61 08 05 0d 08 00 06 0b 25 00 5b 04 06 2c 21 1e 0f 00 22 18 0c 04 00 63 32 00 00 00 32 00 44 75 61 6c 53 74 72 2e 2e 31 63 63 63 32 32 32 00 00 00 40 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DualStr..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 03 02 04 08 2d 03 46 08 0b 16 0c 07 01 0f 3f 02 49 04 01 09 10 04 07 0f 12 04 59 08 05 0a 08 00 06 0b 25 04 60 08 05 74 21 1e 0f 00 32 18 0c 04 00 63 32 00 00 00 32 00 44 75 61 6c 53 74 72 2e 2e 32 63 63 63 32 32 32 00 00 00 30 00 10 00 30 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DualStr..2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 03 02 04 08 2d 00 46 13 0b 16 0c 07 01 0f 3f 00 49 08 01 09 10 04 07 0f 12 00 59 04 05 0a 08 00 06 0b 25 00 60 08 05 74 21 1e 0f 00 32 18 0c 04 00 63 32 00 00 00 32 00 44 75 61 6c 53 74 72 2e 2e 32 63 63 63 32 32 32 00 00 00 30 00 10 00 30 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DualStr..3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 05 01 05 08 2d 03 50 08 03 15 0a 01 05 0f 3f 03 38 13 00 0a 10 01 05 0f 12 03 59 08 03 0a 07 01 05 0b 25 04 5b 08 06 2c 21 1e 0f 00 22 18 0c 04 00 63 32 00 00 00 32 00 44 75 61 6c 53 74 72 2e 2e 33 63 63 63 32 32 32 00 10 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DualStr..3 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 05 01 05 08 2d 00 50 08 03 15 0a 01 05 0f 3f 00 38 13 00 0a 10 01 05 0f 12 00 59 08 03 0a 07 01 05 0b 25 00 5b 08 06 2c 21 1e 0f 00 22 18 0c 04 00 63 32 00 00 00 32 00 44 75 61 6c 53 74 72 2e 2e 33 63 63 63 32 32 32 00 10 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ELKA Strg1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 16 03 04 0e 00 04 58 0d 02 18 03 03 05 08 00 04 5b 08 00 0f 04 03 07 0f 00 04 63 0d 06 09 10 03 05 0f 00 07 5f 08 05 24 21 23 0f 00 12 00 05 04 00 63 63 63 00 00 32 00 45 4c 4b 41 20 53 74 72 67 31 63 63 63 32 32 32 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ELKA Strg1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 16 16 03 04 0e 00 01 58 0d 02 18 03 03 05 08 00 01 5b 08 00 0f 04 03 07 0f 00 00 63 0d 06 09 10 03 05 0f 00 01 5f 08 05 24 21 23 0f 00 12 00 05 04 00 63 63 63 00 00 32 00 45 4c 4b 41 20 53 74 72 67 31 63 63 63 32 32 32 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ELKA Strg2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 16 03 04 0e 00 03 4b 08 0a 14 03 03 05 08 00 04 51 04 00 0b 04 03 07 0f 00 05 63 08 06 0b 10 03 05 0f 00 06 63 04 0d 7c 23 19 14 00 42 18 05 04 00 63 63 63 00 00 32 00 45 4c 4b 41 20 53 74 72 67 32 63 63 63 32 32 32 00 00 00 30 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ELKA Strg2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 16 03 04 0e 00 01 4b 08 0a 14 03 03 05 08 00 01 51 04 00 0b 04 03 07 0f 00 00 63 08 06 0b 10 03 05 0f 00 00 63 04 0d 7c 23 19 14 00 42 18 05 04 00 63 63 63 00 00 32 00 45 4c 4b 41 20 53 74 72 67 32 63 63 63 32 32 32 00 00 00 30 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HiString 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 21 0b 04 00 04 0f 14 02 49 09 06 0a 07 00 04 0f 14 02 51 0a 00 08 1f 00 05 0f 00 01 63 09 06 0a 1f 00 05 0f 00 01 63 0a 00 3c 1f 06 09 00 52 05 04 04 00 63 32 00 00 00 32 00 48 69 53 74 72 69 6e 67 20 31 63 63 63 32 32 32 00 02 00 30 00 32 00 10 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HiString 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 30 17 04 00 04 0f 14 02 49 09 06 19 07 00 04 0f 14 02 51 0a 00 15 1f 00 05 0f 00 01 63 09 06 14 1f 00 05 0f 00 01 63 0a 00 3c 1f 06 09 00 52 05 04 04 00 63 32 00 00 00 32 00 48 69 53 74 72 69 6e 67 20 32 63 63 63 32 32 32 30 02 20 30 00 32 00 10 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Kalimba * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 00 1f 01 00 00 00 3f 04 0e 1a 0d 1f 0f 00 00 00 32 08 1b 1a 0c 1f 05 00 00 01 63 08 08 1e 12 1f 09 00 00 01 63 34 13 04 1c 00 00 00 42 18 02 04 00 28 32 00 00 00 32 00 4b 61 6c 69 6d 62 61 20 20 2a 63 63 63 32 32 32 00 30 00 00 00 00 09 1f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LowString ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 22 0b 04 00 04 0f 14 02 49 05 06 0a 07 00 04 0f 14 02 55 04 00 09 1f 00 05 0f 00 01 63 09 06 08 1f 00 05 0f 00 01 63 04 00 3c 1d 06 0a 00 52 05 04 04 00 63 32 00 00 00 32 00 4c 6f 77 53 74 72 69 6e 67 20 63 63 63 32 32 32 00 01 00 38 00 32 00 18 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntPluck1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 19 16 01 0d 00 02 52 19 03 1f 09 0a 07 0c 00 02 4e 00 03 1b 15 06 0c 0d 00 05 36 16 03 1f 0a 04 06 0c 00 05 63 04 0b 7a 15 00 00 00 22 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 50 6c 75 63 6b 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntPluck1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 19 16 01 0d 00 00 52 19 03 1f 09 0a 07 0c 00 00 4e 00 03 1b 15 06 0c 0d 00 00 36 16 03 1f 0a 04 06 0c 00 00 63 04 0b 7a 15 00 00 00 22 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 50 6c 75 63 6b 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntPluck1 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 19 16 00 0d 00 00 52 19 03 1f 09 0a 07 0c 00 00 4e 00 03 1b 15 06 0c 0d 00 00 36 16 03 1f 0a 04 06 0c 00 00 63 04 0b 7a 15 00 00 00 22 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 50 6c 75 63 6b 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntPluck2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 07 19 07 0f 00 02 63 04 03 18 0c 0f 07 0c 00 03 41 2d 13 18 0c 0f 07 0c 00 06 63 04 0b 16 0f 16 06 0c 00 04 63 04 0b 7c 15 00 00 00 22 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 50 6c 75 63 6b 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntPluck2 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 07 19 07 0f 00 00 63 04 03 18 0c 0f 07 0c 00 00 41 2d 13 18 0c 0f 07 0c 00 00 63 04 0b 16 0f 16 06 0c 00 00 63 04 0b 7c 15 00 00 00 22 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 50 6c 75 63 6b 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pizz. 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 25 1f 1f 08 0a 0a 09 00 3c 04 03 1d 1d 11 0f 0a 00 01 63 04 03 1c 12 00 0b 00 00 01 63 04 03 1f 11 0e 0f 09 00 01 5a 04 03 3e 23 00 0a 00 32 18 02 04 00 63 32 00 00 00 32 00 50 69 7a 7a 2e 20 20 20 20 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pizz. 1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 08 0a 0d 09 00 3c 04 03 1d 1d 11 0f 0a 00 01 63 04 03 1c 12 00 0b 00 00 01 63 04 03 1f 11 0e 0f 09 00 01 5a 04 03 3e 23 00 0a 00 32 18 02 04 00 63 32 00 00 00 32 00 50 69 7a 7a 2e 20 20 20 20 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pizz. 2 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 26 1d 10 0a 09 0f 00 01 41 04 03 1f 1f 16 0a 0a 00 00 4a 04 03 1f 1f 1f 0f 00 00 01 51 00 03 1f 13 11 0f 00 00 01 63 04 03 39 23 00 00 00 32 18 02 04 00 63 32 00 00 00 32 00 50 69 7a 7a 2e 20 20 20 20 32 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pizz. 2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 10 0a 09 0f 00 01 41 04 03 1f 1f 16 0a 0d 00 00 4a 04 03 1f 1f 1f 0f 00 00 01 51 00 03 1f 13 11 0f 00 00 01 63 04 03 39 23 00 00 00 32 18 02 04 00 63 32 00 00 00 32 00 50 69 7a 7a 2e 20 20 20 20 32 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pizzicato* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 13 15 0f 00 2b 04 58 04 03 1f 1f 07 0a 0c 00 01 54 04 06 1f 0d 00 0a 00 00 01 5b 04 00 1e 17 10 07 0b 00 02 63 04 03 01 23 00 00 00 32 18 02 04 00 63 32 00 00 00 32 00 50 69 7a 7a 69 63 61 74 6f 2a 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pizzicato ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank B, voice 23 1b 18 15 0f 0a 00 01 61 04 03 1f 1f 00 07 0c 00 01 54 04 06 1f 0d 00 08 00 00 01 5b 04 03 1f 10 00 06 00 00 01 63 04 03 39 23 00 00 00 62 18 04 04 00 63 32 00 00 00 32 00 50 69 7a 7a 69 63 61 74 6f 20 63 63 63 32 32 32 00 00 00 00 00 30 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ReverbStrg ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 25 0b 04 00 04 0f 00 01 49 05 06 0a 07 00 04 0f 00 01 49 04 00 09 1f 00 05 0f 00 01 63 09 06 09 1f 00 05 0f 00 01 63 0a 00 3c 1d 08 0a 00 52 05 04 04 00 63 32 00 00 00 32 00 52 65 76 65 72 62 53 74 72 67 63 63 63 32 32 32 00 31 00 28 00 32 00 30 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StrgPad A ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 04 00 04 0f 14 02 48 03 01 0a 07 00 04 0f 14 02 50 04 04 09 1f 00 05 0f 00 01 63 03 01 09 1f 00 05 0f 00 01 63 04 04 3c 21 06 0c 00 52 05 04 04 00 63 32 00 0c 00 32 00 53 74 72 67 50 61 64 20 41 20 63 63 63 32 32 32 00 16 00 38 00 36 00 18 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StrgPad B ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 04 00 04 0f 14 02 47 05 02 0a 07 00 04 0f 14 02 4e 03 00 09 1f 00 05 0f 00 01 63 04 06 08 1f 00 05 0f 00 01 63 03 00 3c 1f 06 13 00 52 05 04 04 00 63 32 00 11 00 32 00 53 74 72 67 50 61 64 20 42 20 63 63 63 32 32 32 00 11 00 36 00 38 00 16 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Stringed * ** Controls: KV, MW (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 04 0e 0a 50 45 09 06 1f 00 00 04 0e 0a 40 4c 0a 00 09 07 00 06 0e 00 02 63 09 10 09 07 00 06 0e 20 02 63 0a 16 3c 1c 00 0b 00 4a 05 02 04 00 28 63 00 00 00 32 10 53 74 72 69 6e 67 65 64 20 2a 63 63 63 32 32 32 00 02 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 03 02 04 08 41 12 42 04 0b 0a 0c 07 03 0f 63 24 39 16 00 0b 10 04 07 0f 12 1b 61 08 03 0b 07 05 05 0d 25 1b 5c 04 02 24 21 23 0c 00 22 18 0c 04 00 63 32 00 00 00 32 00 53 74 72 69 6e 67 45 6e 73 31 63 63 63 32 32 32 00 30 00 20 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 03 02 04 08 41 00 42 04 0b 16 0c 07 03 0f 63 00 3f 19 00 0b 10 04 07 0f 12 00 61 04 03 0b 07 05 05 0d 25 00 5c 08 02 24 21 23 0c 00 22 18 0c 04 00 63 32 00 00 00 32 00 53 74 72 69 6e 67 45 6e 73 31 63 63 63 32 32 32 00 20 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 01 02 05 08 41 02 4f 08 0b 16 0c 07 06 0f 63 02 3b 08 00 09 10 04 06 0f 12 03 60 0d 03 0b 08 05 06 0d 25 03 5e 08 02 2c 21 23 0c 00 22 0c 0c 04 00 63 32 00 00 00 32 00 53 74 72 69 6e 67 45 6e 73 32 63 63 63 32 32 32 00 20 00 60 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 01 02 05 08 41 00 4f 04 0b 16 0c 07 06 0f 63 00 3b 13 00 09 10 04 06 0f 12 00 60 0d 03 0b 08 05 06 0d 25 00 5e 08 02 2c 21 23 0c 00 22 0c 0c 04 00 63 32 00 00 00 32 00 53 74 72 69 6e 67 45 6e 73 32 63 63 63 32 32 32 00 00 00 60 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 15 0a 07 0e 0a 02 4d 04 0e 0a 06 1a 08 05 28 02 34 08 0e 0f 09 07 06 0f 1e 03 63 04 04 0a 06 1a 08 05 28 03 5f 08 0e 2c 21 2e 1e 09 21 18 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 33 63 63 63 32 32 32 00 40 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns3 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 15 0a 07 0e 0a 00 4d 04 0e 0a 06 1a 08 05 28 00 34 13 0e 0f 09 07 06 0f 1e 00 63 04 04 0a 06 1a 08 05 28 00 5f 08 0e 2c 21 2e 1e 09 12 18 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 33 63 63 63 32 32 32 00 40 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 10 06 04 0e 39 01 50 08 00 13 09 00 04 0e 14 02 44 08 05 0b 10 00 05 0e 14 04 63 08 03 0b 10 00 07 0e 14 04 5f 0d 05 2c 1c 00 0f 00 32 18 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 34 63 63 63 32 32 32 00 20 00 30 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns4 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 10 06 04 0e 39 00 50 04 00 13 09 00 04 0e 14 00 44 08 05 0f 10 00 05 0e 14 00 63 08 03 0f 10 00 07 0e 14 00 5f 08 05 2c 21 00 0f 00 32 18 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 34 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 0a 00 05 0d 39 03 44 04 06 0b 09 00 05 0e 0a 02 39 04 0b 0b 09 06 06 0c 0c 04 5e 08 04 0b 0b 00 05 0e 0a 03 60 04 02 24 21 0a 0e 00 32 18 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 35 63 63 63 32 32 32 00 40 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns5 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 0f 00 05 0d 39 00 44 08 06 0b 09 00 05 0e 0a 00 39 04 0b 0f 09 06 06 0c 0c 00 5e 08 04 0c 0b 00 05 0e 0a 00 60 08 02 24 21 0a 0e 00 32 18 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 35 63 63 63 32 32 32 00 40 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 0f 00 06 0d 39 01 50 08 06 15 0f 00 06 0d 0a 03 48 13 0b 0b 09 06 06 0c 0c 02 62 08 04 0b 17 00 06 0e 0a 03 62 0d 02 2c 21 0a 09 00 22 0c 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 36 63 63 63 32 32 32 00 40 00 10 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns6 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 0f 00 06 0d 39 00 50 08 06 15 0f 00 06 0d 0a 00 48 13 0b 10 09 06 06 0c 0c 00 62 08 04 10 17 00 06 0e 0a 00 62 0d 02 2c 21 0a 09 00 22 0c 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 36 63 63 63 32 32 32 00 40 00 10 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns7 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 0f 00 05 0d 28 01 53 00 00 15 0f 00 05 0d 0a 03 47 00 04 0c 09 06 06 0c 06 03 61 00 00 0b 17 00 05 0e 05 04 62 00 05 24 21 0a 09 00 22 18 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 37 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns7 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 0f 00 05 0d 28 00 53 00 00 15 0f 00 05 0d 0a 00 47 00 04 0d 09 06 06 0c 06 00 61 00 00 0d 17 00 05 0e 05 00 62 00 05 24 21 0a 09 00 22 18 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 37 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns8 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 04 09 09 0d 28 01 3e 08 08 0d 0f 00 05 0d 0a 03 44 08 04 0b 09 06 06 0c 06 05 63 08 00 0b 17 00 05 0e 05 03 62 08 05 24 21 0a 09 00 22 00 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 38 63 63 63 32 32 32 00 20 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringEns8 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 04 09 09 0d 28 00 3e 13 08 0f 0f 00 05 0d 0a 00 44 19 04 0d 09 06 06 0c 06 00 63 08 00 0e 17 00 05 0e 05 00 62 08 05 24 21 0a 09 00 22 00 0c 04 00 63 32 32 00 00 32 00 53 74 72 69 6e 67 45 6e 73 38 63 63 63 32 32 32 00 20 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringLead ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0d 00 05 0e 0a 03 58 08 03 0a 02 00 07 09 00 05 5a 19 08 0f 15 06 05 0f 00 05 62 08 08 0c 15 06 08 0e 00 05 5f 04 08 34 26 20 20 00 12 0c 02 04 00 28 02 00 00 00 32 00 53 74 72 69 6e 67 4c 65 61 64 63 63 63 32 32 32 00 00 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StringLead ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0d 00 05 0e 0a 01 58 08 03 0a 02 00 07 09 00 02 5a 19 08 0f 15 06 05 0f 00 00 62 08 08 0c 15 06 08 0e 00 00 5f 04 08 34 26 20 20 00 12 0c 02 04 00 28 02 00 00 00 32 00 53 74 72 69 6e 67 4c 65 61 64 63 63 63 32 32 32 00 00 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Strings ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 11 0b 05 05 0a 17 00 43 04 0c 0c 1f 08 04 0f 22 00 35 13 09 07 1f 04 05 0f 14 00 46 04 0b 0a 1f 05 06 0f 00 00 57 04 0b 3a 21 0e 2a 00 42 0c 02 04 00 63 32 00 32 00 32 00 53 74 72 69 6e 67 73 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Strings ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 0a 00 05 05 0f 17 00 43 08 0c 0c 00 00 04 0f 22 00 34 13 09 0e 00 00 05 0f 14 00 46 0a 0b 0c 00 06 06 0f 00 00 4c 0a 0b 3a 21 0e 2a 00 42 0c 02 04 00 63 32 00 32 00 32 00 53 74 72 69 6e 67 73 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Strings 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 1 1f 04 00 04 0f 1e 02 46 09 06 1f 04 00 04 0f 1e 02 46 0a 00 0c 1f 00 05 0f 1e 01 63 09 06 0c 1f 00 05 0f 1e 01 63 0a 00 3c 1f 06 0a 00 52 05 02 04 00 63 32 00 00 00 32 00 53 74 72 69 6e 67 73 20 20 31 63 63 63 32 32 32 00 02 00 00 00 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Strings 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 2 14 05 00 04 09 14 02 3d 1c 06 0a 04 02 04 09 01 01 47 13 00 0c 05 02 05 0c 00 01 63 09 06 0a 04 01 05 0c 00 01 63 0a 00 3c 1f 00 1a 00 52 05 02 04 00 63 32 00 00 00 32 00 53 74 72 69 6e 67 73 20 20 32 63 63 63 32 32 32 00 10 00 20 00 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Strings 2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 14 05 00 04 09 14 02 3d 1c 06 0d 04 02 04 09 01 01 47 13 00 0c 05 02 05 0c 00 01 63 09 06 0d 04 01 05 0c 00 01 63 0a 00 3c 1f 00 1a 00 52 05 02 04 00 63 32 00 00 00 32 00 53 74 72 69 6e 67 73 20 20 32 63 63 63 32 32 32 00 10 00 20 00 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Strings 3 ** Description: A string sound with a little horn mixed in ** Controls: KV, MW (LFO pitch) ** Source: Art Hitzeman 1f 04 00 04 0f 1e 02 46 09 06 1f 04 00 04 0f 1e 02 46 0a 00 0c 1f 00 05 0f 1e 01 63 09 06 12 1f 00 05 0f 1e 01 63 0a 00 3c 1f 06 0a 00 52 05 02 04 00 63 32 00 00 00 32 00 53 74 72 69 6e 67 73 20 20 33 63 63 63 32 32 32 00 02 00 00 00 32 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Syn.Str 1 ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 1 1f 1f 00 03 0f 00 00 46 0a 00 1f 1f 00 03 0f 00 00 44 09 06 0b 1f 00 05 0f 1e 00 56 0a 00 0b 1f 00 05 0f 1e 00 56 0a 06 3c 23 00 2e 00 42 05 02 04 00 63 32 00 00 00 32 00 53 79 6e 2e 53 74 72 20 20 31 63 63 63 32 32 32 00 30 00 62 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Syn.Str 2 ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 2 1f 1f 00 03 0f 00 00 31 13 00 1f 1f 00 03 0f 00 00 3b 09 06 0c 1f 00 05 0f 1e 00 5b 0a 00 0c 1f 00 05 0f 1e 00 53 0a 06 3c 20 00 4b 00 42 05 02 04 00 63 32 00 00 00 32 00 53 79 6e 2e 53 74 72 20 20 32 63 63 63 32 32 32 00 70 00 62 00 30 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynString ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 26 1f 04 00 02 0f 0f 00 44 04 03 1f 09 03 02 0f 15 01 3a 08 00 1f 00 00 0b 0f 0c 02 44 08 06 1a 0a 00 05 0f 05 01 5c 08 13 3a 1d 06 0f 00 52 0c 04 04 00 63 32 00 00 00 32 00 53 79 6e 53 74 72 69 6e 67 20 63 63 63 32 32 32 20 00 30 30 10 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: T Pluck..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 0d 0a 03 0d 00 3a 4f 0a 0a 1b 13 00 0c 07 00 1e 51 16 0b 1f 0a 0a 06 0c 00 1c 63 04 08 1b 09 0a 07 0c 00 3d 63 04 0b 6c 15 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 54 20 50 6c 75 63 6b 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: T Pluck..1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 0d 0a 03 0d 00 3a 4f 0a 0a 1b 13 00 0c 07 00 19 51 16 0b 1f 0a 0a 06 0c 00 18 63 04 08 1b 09 0a 07 0c 00 38 63 04 0b 6c 15 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 54 20 50 6c 75 63 6b 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: T Pluck..2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 0d 0a 03 0d 00 3b 53 08 0a 1b 13 00 0c 07 00 1d 3b 00 0b 1f 0a 0a 06 0c 00 1e 4f 16 08 1e 0a 09 06 0c 00 3d 63 04 13 72 15 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 54 20 50 6c 75 63 6b 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: T Pluck..2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 0d 0a 03 0d 00 39 53 08 0a 1b 13 00 0c 07 00 1a 3b 00 0b 1f 0a 0a 06 0c 00 19 4f 16 08 1e 0a 09 06 0c 00 38 63 04 13 72 15 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 54 20 50 6c 75 63 6b 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: T Pluck..3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 0f 09 04 0c 15 3b 53 0a 0e 15 0d 1f 03 0a 12 1d 4d 1f 19 1c 09 0f 05 0a 19 1e 63 04 0a 13 07 09 05 0e 00 3d 63 00 13 6c 22 21 00 00 12 18 02 04 00 63 32 00 00 00 32 00 54 20 50 6c 75 63 6b 2e 2e 33 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: T Pluck..3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 0f 09 04 0c 15 39 53 0a 0e 15 0d 1f 03 0a 12 19 4d 1f 19 1c 09 0f 05 0a 19 18 63 04 0a 13 07 09 05 0e 00 38 63 00 13 6c 22 21 00 00 12 18 02 04 00 63 32 00 00 00 32 00 54 20 50 6c 75 63 6b 2e 2e 33 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WarmString ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 0d 05 00 04 0f 14 00 40 05 06 0c 08 00 04 0f 14 00 49 04 00 0b 1f 00 05 0f 00 03 63 09 06 0a 1f 00 05 0f 00 03 63 04 00 3c 1d 06 10 00 52 11 04 04 00 63 32 00 00 00 32 00 57 61 72 6d 53 74 72 69 6e 67 63 63 63 32 32 32 00 01 00 38 00 32 00 18 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WindString ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 03 03 0f 56 03 63 0d 03 0d 04 02 04 0f 29 04 60 08 0b 06 00 00 04 0f 4c 06 3d 08 13 0a 04 02 06 0f 5c 05 63 08 0b 7c 29 00 00 00 22 00 02 04 00 28 02 00 00 00 32 00 57 69 6e 64 53 74 72 69 6e 67 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WindString ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 03 03 0f 56 00 63 0d 03 0d 04 02 04 0f 29 02 60 08 0b 06 00 00 04 0f 4c 00 3d 08 13 0a 04 02 06 0f 5c 00 63 08 0b 7c 29 00 00 00 22 00 02 04 00 28 02 00 00 00 32 00 57 69 6e 64 53 74 72 69 6e 67 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: phantstrng ** Description: a string voice ** Controls: KV ** Source: GLIB (Michael Kesti and others) 08 03 06 01 0b 00 03 58 0a 02 1f 02 04 03 0e 43 03 5b 04 10 1f 0c 02 04 0a 00 03 4e 08 00 0b 09 04 06 0b 00 03 63 04 03 28 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 70 68 61 6e 74 73 74 72 6e 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Banjo ** Name: Banjo * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 0c 00 04 0c 00 01 51 04 15 1f 12 00 01 0f 00 00 58 04 16 1f 0a 09 01 0a 00 02 4e 19 18 1c 0b 0a 06 09 00 41 63 0a 13 7b 3a 00 00 00 09 18 02 04 00 63 00 42 00 00 32 00 42 61 6e 6a 6f 20 20 20 20 2a 63 63 63 32 32 32 00 00 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Banjo ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 10 1f 0b 08 01 0f 00 00 48 1c 15 1f 1f 00 01 0f 00 00 53 04 16 1f 07 11 01 0f 00 00 40 08 16 1f 0e 0a 06 0a 00 0b 63 0a 10 3b 1d 00 10 00 32 18 02 04 00 63 32 00 00 00 32 00 42 61 6e 6a 6f 20 20 20 20 20 63 63 63 32 32 32 00 70 00 30 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Banjo....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 17 00 04 0c 00 02 5a 04 0d 1f 02 03 01 0f 00 05 5a 04 0e 1f 1b 08 01 0c 00 06 42 08 0e 1f 0d 0b 06 09 00 06 63 04 10 2a 06 00 01 00 32 18 07 04 00 63 32 00 00 00 32 00 42 61 6e 6a 6f 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Banjo....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 17 00 04 0c 00 02 5a 04 0d 1f 02 03 01 0f 00 01 5a 04 0e 1f 1b 08 01 0c 00 01 42 08 0e 1f 0d 0b 06 09 00 01 63 04 10 2a 06 00 01 00 32 18 07 04 00 63 32 00 00 00 32 00 42 61 6e 6a 6f 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Banjo....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 17 00 04 0c 00 02 5b 04 0d 16 09 03 05 00 00 06 4d 13 16 1f 1b 08 05 0c 00 04 3e 08 0e 1f 0d 0b 06 09 00 05 63 04 10 2c 06 00 01 00 32 18 07 04 00 63 32 00 00 00 32 00 42 61 6e 6a 6f 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Banjo....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 17 00 04 0c 00 02 5b 04 0d 16 09 03 05 00 00 02 4d 13 16 1f 1b 08 05 0c 00 00 3e 08 0e 1f 0d 0b 06 09 00 00 63 04 10 2c 06 00 01 00 32 18 07 04 00 63 32 00 00 00 32 00 42 61 6e 6a 6f 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Old Banjo ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 7 1e 17 00 04 0c 00 01 5f 04 15 1f 0b 03 01 0f 00 00 5c 04 1e 1f 1b 08 01 0c 00 02 45 19 1e 1f 0d 0b 06 09 00 03 63 0a 10 2a 23 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 4f 6c 64 20 42 61 6e 6a 6f 20 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 6 * SUBCATEGORY BassGuitar ** Name: AnalogBass ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 13 1f 0d 00 08 0c 1e 03 51 04 08 1f 09 00 09 0a 00 01 53 00 00 1f 0c 00 0a 0b 00 01 4a 00 06 1f 1f 00 0c 0f 00 02 63 00 03 3a 23 00 00 00 62 0c 04 04 00 63 32 00 32 00 32 00 41 6e 61 6c 6f 67 42 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 08 03 0a 0f 01 61 00 13 1f 0f 0b 03 0d 0c 01 4c 04 0b 15 15 08 05 0b 2e 06 36 10 0b 1f 07 0a 09 0d 00 05 63 04 13 5a 22 35 0a 00 72 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 08 03 0a 0f 01 61 00 13 1f 0f 0b 03 0d 0c 01 4c 04 0b 15 15 08 05 0b 2e 01 36 10 0b 1f 07 0a 09 0d 00 00 63 04 13 5a 22 35 0a 00 72 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 1b 0c 03 0c 0f 01 5f 00 03 1c 1b 11 03 0c 0c 02 4a 04 0b 18 09 0f 04 0d 2e 05 32 04 0b 1f 0f 04 09 0f 00 05 63 04 13 1a 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 1b 0c 03 0c 0f 01 5f 00 03 1c 1b 11 03 0c 0c 01 4a 04 0b 18 09 0f 04 0d 2e 00 32 04 0b 1f 0f 04 09 0f 00 00 63 04 13 1a 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 0c 0c 0c 0f 01 51 04 03 1f 15 12 05 0d 0c 42 57 04 0b 1f 0a 0a 03 04 2e 45 46 08 0a 1f 07 05 0a 0f 00 06 5f 04 13 0a 26 1c 02 50 36 00 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.3 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 0c 0c 0c 0f 00 51 04 03 1f 15 12 05 0d 0c 40 57 04 0b 1f 0a 0a 03 04 2e 40 46 08 0a 1f 07 05 0a 0f 00 00 5f 04 13 0a 26 1c 02 50 36 00 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 0c 0c 0c 00 01 5a 00 03 1f 15 12 05 0d 00 41 63 04 13 1f 0a 0a 03 04 00 45 46 08 12 1f 07 09 0a 0f 00 04 63 04 0b 1a 26 1c 02 50 36 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 0c 0c 0c 00 01 5b 00 03 1f 15 12 05 0d 00 40 63 04 13 1f 0a 0a 03 04 00 43 46 08 12 1f 07 09 0a 0f 00 06 63 04 0b 1a 26 1c 02 50 36 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 0c 0c 0c 00 01 5a 00 03 1f 15 12 05 0d 00 41 63 04 13 1f 0a 0a 03 04 00 41 46 08 12 1f 07 09 0a 0f 00 00 63 04 0b 1a 26 1c 02 50 36 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 0c 0c 0c 00 01 5b 00 03 1f 15 12 05 0d 00 40 63 04 13 1f 0a 0a 03 04 00 41 46 08 12 1f 07 09 0a 0f 00 00 63 04 0b 1a 26 1c 02 50 36 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1f 08 08 0f 0f 02 60 04 03 1c 1c 0a 04 09 0c 02 63 00 03 1b 0c 08 08 04 2e 06 46 1c 03 1f 12 06 09 0f 00 05 63 00 03 1a 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1f 08 08 0f 0f 02 60 04 03 1c 1c 0a 04 09 0c 00 63 00 03 1b 0c 08 08 04 2e 01 46 1c 03 1f 12 06 09 0f 00 00 63 00 03 1a 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1f 08 08 0c 0f 01 5a 04 03 1c 1f 0a 04 0f 0c 03 4c 00 03 1b 1f 08 08 0e 2e 05 63 04 03 1f 1f 06 09 0f 00 05 63 00 03 3c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1f 08 08 0c 0f 01 5a 04 03 1c 1f 0a 04 0f 0c 01 4c 00 03 1b 1f 08 08 0e 2e 00 63 04 03 1f 1f 06 09 0f 00 00 63 00 03 3c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1f 08 06 0c 0f 01 63 00 06 1c 1f 09 0a 0c 0c 02 63 00 02 1b 1f 07 09 0f 2e 05 63 04 06 1f 1f 09 0a 0c 00 05 63 04 02 2c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.8 ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1f 08 06 0c 0f 00 63 00 06 1c 1f 09 0a 0c 0c 00 63 00 02 1b 1f 07 09 0f 2e 00 63 04 06 1f 1f 09 0a 0c 00 00 63 04 02 2c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.9 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 07 07 09 0f 00 01 52 00 06 1c 0c 08 0a 0f 00 01 61 00 02 1c 07 07 09 0f 00 06 63 08 06 1c 07 07 09 0f 00 06 63 04 02 3c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass.9 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 07 07 09 0f 00 01 52 00 06 1c 0c 08 0a 0f 00 01 61 00 02 1c 07 07 09 0f 00 00 63 08 06 1c 07 07 09 0f 00 00 63 04 02 3c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 2e 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass10 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 1f 07 02 00 03 32 04 06 1c 0f 1f 07 02 00 03 5e 00 02 1c 1f 06 07 0f 00 05 63 00 06 1c 1f 09 07 0f 00 05 63 00 02 3c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgBass10 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 1f 07 02 00 02 32 04 06 1c 0f 1f 07 02 00 01 5e 00 02 1c 1f 06 07 0f 00 00 63 00 06 1c 1f 09 07 0f 00 00 63 00 02 3c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 41 6e 6c 67 42 61 73 73 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgCut.BS ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 12 07 08 04 00 02 61 19 1b 19 1b 09 04 0c 00 03 63 00 03 1d 0c 02 08 0d 00 04 42 19 03 1d 12 06 09 0e 00 05 63 00 03 34 23 00 00 00 32 0c 02 04 00 63 11 06 00 00 32 00 41 6e 6c 67 43 75 74 2e 42 53 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgCut.BS ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 12 07 08 04 00 01 61 19 1b 19 1b 09 04 0c 00 01 63 00 03 1d 0c 02 08 0d 00 02 42 19 03 1d 12 06 09 0e 00 00 63 00 03 34 23 00 00 00 32 0c 02 04 00 63 11 06 00 00 32 00 41 6e 6c 67 43 75 74 2e 42 53 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgFunkBS ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 02 05 06 14 03 5e 04 03 1f 1c 0f 07 04 19 03 5f 04 14 1f 1b 1f 07 0f 00 07 5d 04 0b 1f 06 12 07 0c 00 06 5c 04 0b 75 08 00 0b 00 22 00 02 04 00 63 11 06 00 00 32 00 41 6e 6c 67 46 75 6e 6b 42 53 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgFunkBS ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 08 02 05 06 14 01 5e 04 03 1f 1c 0f 07 04 19 01 5f 04 14 1f 1b 1f 07 0f 00 02 5d 04 0b 1f 06 12 07 0c 00 00 5c 04 0b 75 08 00 0b 00 22 00 02 04 00 63 11 06 00 00 32 00 41 6e 6c 67 46 75 6e 6b 42 53 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSplat ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 0c 01 09 0b 14 02 63 04 06 18 1f 1f 09 0f 19 04 61 04 06 0d 1f 08 09 0f 00 05 62 08 0d 1c 0f 07 09 0c 00 06 63 04 01 65 08 00 0b 00 22 00 02 04 00 63 11 06 00 00 32 00 41 6e 6c 67 53 70 6c 61 74 20 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSplat ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 0c 01 09 0b 14 01 63 04 06 18 1f 1f 09 0f 19 02 61 04 06 0d 1f 08 09 0f 00 01 62 08 0d 1c 0f 07 09 0c 00 00 63 04 01 65 08 00 0b 00 22 00 02 04 00 63 11 06 00 00 32 00 41 6e 6c 67 53 70 6c 61 74 20 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass 4 ** Description: Was called "Brass 4" but sounds more like a bass guitar ** Controls: KV, MW (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 19 00 05 00 00 7b 63 19 03 15 0b 00 0a 09 00 47 57 0a 06 10 08 02 0a 05 00 46 2a 03 03 1f 0b 06 0a 0e 00 47 63 04 03 39 1c 1a 03 13 66 0c 04 04 00 63 32 00 00 00 32 00 42 61 73 73 20 20 20 20 20 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass 4 ** Description: Was called "Brass 4" but sounds more like a bass guitar ** Controls: KV, MW (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 19 00 05 00 00 7b 4e 19 03 15 0b 00 0a 09 00 47 59 00 06 1f 08 02 0a 05 00 42 4e 00 03 1f 0b 06 0a 0e 00 47 63 04 03 3b 1c 1a 03 13 66 0c 04 04 00 63 32 00 00 00 32 00 42 61 73 73 20 20 20 20 20 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass 4 ** Description: Was called "Brass 4" but sounds more like a bass guitar ** Controls: KV, MW (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 19 00 05 00 00 7b 5c 00 03 15 0b 00 0a 09 00 47 57 0a 06 1f 08 02 0a 05 00 42 4e 00 03 1f 0b 06 0a 0e 00 47 63 04 03 3b 1c 1a 03 13 66 0c 04 04 00 63 32 00 00 00 32 00 42 61 73 73 20 20 20 20 20 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass 4 ** Description: Was called "Brass 4" but sounds more like a bass guitar ** Controls: KV, MW (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 19 00 05 00 00 7b 5c 00 03 15 0b 00 0a 09 00 47 57 0a 06 04 08 02 0a 05 00 42 58 19 03 1f 0b 06 0a 0e 00 47 63 04 03 01 1c 1a 03 13 66 0c 04 04 00 63 32 00 00 00 32 00 42 61 73 73 20 20 20 20 20 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass 4 ** Description: Was called "Brass 4" but sounds more like a bass guitar ** Controls: KV ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 0f 07 09 0a 0f 07 4e 16 03 1f 1f 00 04 0f 00 03 47 00 03 1f 07 05 06 0a 00 02 63 00 13 1f 07 05 06 0a 00 02 63 00 13 04 23 00 00 00 62 18 04 04 00 63 32 00 00 00 32 00 42 61 73 73 20 20 20 20 20 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Pluck ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 16 06 08 0d 00 00 59 00 1b 1f 1f 1f 0f 0f 00 00 51 04 1b 1f 0d 0c 08 0a 63 04 63 04 13 1f 1f 1f 0f 0f 00 05 63 04 1b 64 26 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 42 61 73 73 20 50 6c 75 63 6b 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Pluck ** Source: from a library of patches collected by Duane Bowker, author unknown 15 16 06 08 0d 00 00 59 00 1b 1f 1f 1f 0f 0f 00 00 51 04 1b 1f 0d 0c 08 0a 63 00 63 04 13 1f 1f 1f 0f 0f 00 00 63 04 1b 64 26 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 42 61 73 73 20 50 6c 75 63 6b 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 1a 07 04 04 00 01 51 1c 1b 19 1c 09 04 09 00 00 63 00 13 1b 0c 02 04 04 63 03 4f 10 13 1c 12 06 04 0e 0e 06 63 00 1b 33 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 30 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 1a 07 04 04 00 02 51 1c 1b 19 1c 09 04 09 00 00 63 00 13 1b 0c 02 04 04 63 01 4f 10 13 1c 12 06 04 0e 0e 00 63 00 1b 33 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 30 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 09 06 05 00 00 01 4c 04 13 18 1f 0c 06 0f 00 01 4a 04 0b 15 18 06 08 0e 63 03 63 04 0b 19 07 06 07 00 0e 43 62 00 13 7c 23 20 00 29 26 0c 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 31 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 09 06 05 00 00 01 4c 04 13 18 1f 0c 06 0f 00 01 4a 04 0b 15 18 06 08 0e 63 00 63 04 0b 19 07 06 07 00 0e 40 62 00 13 7c 23 20 00 29 26 0c 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 31 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 06 02 05 0e 63 01 4a 0d 13 1e 09 04 08 00 63 01 4f 04 13 1a 09 0a 08 08 63 03 4d 0d 13 19 12 06 06 0f 63 05 63 04 13 11 21 13 00 00 02 00 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 32 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 06 02 05 0e 63 01 4a 0d 13 1e 09 04 08 00 63 01 4f 04 13 1a 09 0a 08 08 63 01 4d 0d 13 19 12 06 06 0f 63 00 63 04 13 11 21 13 00 00 02 00 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 32 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 06 01 05 0e 63 01 5a 04 16 19 0e 07 06 0f 63 01 46 04 10 18 06 08 08 0a 63 05 41 0a 10 19 0e 07 06 0f 63 05 63 04 13 22 21 13 00 00 02 0c 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 33 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 06 01 05 0e 63 01 5a 04 16 19 0e 07 06 0f 63 01 46 04 10 18 06 08 08 0a 63 00 41 0a 10 19 0e 07 06 0f 63 00 63 04 13 22 21 13 00 00 02 0c 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 33 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1a 07 07 04 00 41 3f 19 13 1f 1c 09 07 09 00 41 55 0d 13 1f 0c 02 07 04 63 44 4e 00 13 1f 12 06 07 0e 0e 45 63 04 1b 2a 63 00 00 0f 06 0c 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 34 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1a 07 07 04 00 41 3f 19 13 1f 1c 09 07 09 00 41 55 0d 13 1f 0c 02 07 04 63 40 4e 00 13 1f 12 06 07 0e 0e 40 63 04 1b 2a 63 00 00 0f 06 0c 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 34 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 16 06 08 0d 00 01 5f 04 1b 19 12 04 0a 0c 23 01 56 08 13 19 0f 06 09 0d 63 03 63 08 13 19 0c 04 0a 0e 0e 05 63 04 13 64 26 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 35 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 16 06 08 0d 00 01 5f 04 1b 19 12 04 0a 0c 23 01 56 08 13 19 0f 06 09 0d 63 00 63 08 13 19 0c 04 0a 0e 0e 00 63 04 13 64 26 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 35 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 06 01 05 0e 63 01 45 0a 16 1b 0d 02 05 0e 63 01 5b 04 10 1e 0a 0c 06 08 63 02 49 0a 10 1a 0f 06 05 0e 63 05 63 04 13 23 21 13 00 00 02 00 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 36 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 06 01 05 0e 63 01 45 0a 16 1b 0d 02 05 0e 63 01 5b 04 10 1e 0a 0c 06 08 63 00 49 0a 10 1a 0f 06 05 0e 63 00 63 04 13 23 21 13 00 00 02 00 02 04 00 63 32 00 32 00 32 00 3c 3c 42 61 73 73 2e 36 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 05 09 0c 0f 01 53 16 1d 1e 1b 01 09 0d 0c 01 41 04 01 1d 01 01 0c 00 2e 04 55 04 14 1e 1c 05 09 0c 00 04 63 08 13 2a 23 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 3c 3c 42 61 73 73 2e 37 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 05 09 0c 0f 01 53 16 1d 1e 1b 01 09 0d 0c 01 41 04 01 1d 01 01 0c 00 2e 00 55 04 14 1e 1c 05 09 0c 00 00 63 08 13 2a 23 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 3c 3c 42 61 73 73 2e 37 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 0a 09 0c 0f 02 63 04 05 12 0c 07 07 0a 0c 01 61 04 01 1d 01 01 0c 00 2e 03 63 04 04 1e 1c 05 09 0c 00 05 63 08 03 1c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 3c 3c 42 61 73 73 2e 38 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 0a 09 0c 0f 02 63 04 05 12 0c 07 07 0a 0c 01 61 04 01 1d 01 01 0c 00 2e 00 63 04 04 1e 1c 05 09 0c 00 00 63 08 03 1c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 3c 3c 42 61 73 73 2e 38 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 06 09 0c 0f 01 3e 13 15 18 17 03 09 0d 0c 01 54 04 01 1d 01 03 0c 0a 2e 03 4d 04 14 1e 1c 06 09 0c 00 05 63 04 13 1b 23 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 3c 3c 42 61 73 73 2e 39 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 06 09 0c 0f 01 3e 13 15 18 17 03 09 0d 0c 01 54 04 01 1d 01 03 0c 0a 2e 00 4d 04 14 1e 1c 06 09 0c 00 00 63 04 13 1b 23 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 3c 3c 42 61 73 73 2e 39 3e 3e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass....FM ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 18 04 09 0f 00 02 4e 00 19 1f 12 12 08 0f 00 03 4b 1f 04 1f 0c 02 08 0f 00 05 54 00 0d 1f 0c 0b 0a 0f 00 05 63 04 0a 68 23 00 19 00 32 0c 02 04 00 63 32 00 00 00 32 00 42 61 73 73 2e 2e 2e 2e 46 4d 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass....FM ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 18 04 09 0f 00 02 4e 00 19 1f 12 12 08 0f 00 02 4b 1f 04 1f 0c 02 08 0f 00 02 54 00 0d 1f 0c 0b 0a 0f 00 00 63 04 0a 68 23 00 19 00 32 0c 02 04 00 63 32 00 00 00 32 00 42 61 73 73 2e 2e 2e 2e 46 4d 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BombBass ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 04 07 09 0e 00 07 53 0a 08 1f 05 01 0a 0e 3f 03 51 00 08 1f 06 02 09 0d 32 03 63 00 0e 1f 1f 02 08 0f 00 07 63 04 03 3a 63 00 00 00 73 0c 04 04 00 63 63 00 00 00 32 00 42 6f 6d 62 42 61 73 73 20 20 63 63 63 32 32 32 00 40 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: CutupBass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 12 07 08 04 00 01 56 19 0b 18 1c 09 04 09 63 01 57 1c 1c 1b 0c 09 07 0d 00 04 3b 13 13 1f 12 06 07 0f 00 05 5f 00 03 1a 21 13 07 00 62 0c 02 04 00 63 32 00 32 00 32 00 43 75 74 75 70 42 61 73 73 20 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: CutupBass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 12 07 08 04 00 01 56 19 0b 18 1c 09 04 09 63 01 57 1c 1c 1b 0c 09 07 0d 00 01 3b 13 13 1f 12 06 07 0f 00 00 5f 00 03 1a 21 13 07 00 62 0c 02 04 00 63 32 00 32 00 32 00 43 75 74 75 70 42 61 73 73 20 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Bass 1 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 27 1f 01 00 08 04 00 03 4c 00 1b 1f 08 03 08 04 00 02 55 00 13 13 0a 05 08 04 2b 06 3c 16 1b 1f 09 03 09 04 00 01 63 00 0b 3b 1c 00 00 00 52 0c 02 05 00 63 4b 00 00 00 32 00 45 2e 42 61 73 73 20 20 20 31 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Bass 2 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 28 1f 0f 10 07 0f 00 03 43 2d 03 1e 08 09 07 0f 23 02 52 00 03 1f 0b 0a 07 0f 00 03 41 10 03 1d 0c 0a 09 0f 28 01 63 04 03 38 1e 00 00 00 52 0c 02 04 00 28 32 00 00 00 32 00 45 2e 42 61 73 73 20 20 20 32 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Bass 3 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 29 06 06 07 01 0a 06 42 56 00 1e 1f 05 06 01 0e 04 41 53 00 16 1f 05 06 01 0e 04 40 33 08 1e 19 05 08 07 00 00 42 63 00 16 20 1a 10 14 15 46 0c 02 04 00 28 32 00 00 00 32 00 45 2e 42 61 73 73 20 20 20 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Bass 3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 06 06 07 01 0d 06 42 56 00 1e 1f 05 06 01 0e 04 41 53 00 16 1f 05 06 01 0e 04 40 33 08 1e 19 05 08 07 00 00 42 63 00 16 20 1a 10 14 15 46 0c 02 04 00 28 32 00 00 00 32 00 45 2e 42 61 73 73 20 20 20 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ElecBass 1 ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 9 1f 01 00 08 04 00 03 3d 0a 1e 1f 01 00 08 00 00 00 43 00 10 1f 09 06 08 0c 1b 07 46 1c 1e 1f 09 00 09 09 00 01 63 04 03 3a 1c 00 00 00 52 0c 04 05 00 63 4b 00 00 00 32 00 45 6c 65 63 42 61 73 73 20 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ElecBass 2 ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 12 1f 0e 00 0f 02 31 03 4b 28 16 1f 06 00 07 02 3b 00 4d 00 06 1f 0a 00 0f 02 3b 03 52 04 16 1f 06 00 08 02 00 02 63 00 00 3a 22 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 45 6c 65 63 42 61 73 73 20 32 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fat Lead.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 0a 07 09 0e 4d 05 54 00 03 12 1f 00 0a 0f 00 05 3e 08 03 12 1f 00 0a 0f 4b 05 5f 08 03 12 1f 00 0a 0f 01 06 5f 04 03 3b 1e 05 0a 00 62 0c 02 04 00 63 32 00 00 00 32 00 46 61 74 20 4c 65 61 64 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fat Lead.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 0a 07 09 0e 4d 03 54 00 03 12 1f 00 0a 0f 00 02 3e 08 03 12 1f 00 0a 0f 4b 01 5f 08 03 12 1f 00 0a 0f 01 00 5f 04 03 3b 1e 05 0a 00 62 0c 02 04 00 63 32 00 00 00 32 00 46 61 74 20 4c 65 61 64 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fat Lead.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 03 01 07 0d 27 04 53 13 0d 11 1f 06 05 0f 00 04 51 00 0d 16 04 00 0a 0d 11 06 49 08 0b 16 1f 00 0a 0f 27 05 5a 00 03 62 24 00 04 00 42 0c 02 04 00 63 63 00 00 00 32 00 46 61 74 20 4c 65 61 64 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fat Lead.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 03 01 07 0d 27 01 53 13 0d 11 1f 06 05 0f 00 01 51 00 0d 16 04 00 0a 0d 11 00 49 08 0b 16 1f 00 0a 0f 27 00 5a 00 03 62 24 00 04 00 42 0c 02 04 00 63 63 00 00 00 32 00 46 61 74 20 4c 65 61 64 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fat Lead.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 03 01 07 0d 27 04 3f 10 0d 11 1f 06 05 0f 00 04 56 04 0d 16 04 00 0a 0d 11 05 4e 08 0b 16 1f 00 0a 0f 27 07 5a 04 03 61 24 00 04 00 42 0c 02 04 00 63 63 00 00 00 32 00 46 61 74 20 4c 65 61 64 2e 33 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fat Lead.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 03 01 07 0d 27 01 3f 10 0d 11 1f 06 05 0f 00 01 56 04 0d 16 04 00 0a 0d 11 00 4e 08 0b 16 1f 00 0a 0f 27 00 5a 04 03 61 24 00 04 00 42 0c 02 04 00 63 63 00 00 00 32 00 46 61 74 20 4c 65 61 64 2e 33 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fat Lead.4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 03 01 07 0d 27 05 55 04 0d 11 1f 06 05 0f 00 04 63 00 0d 16 04 00 0a 0d 11 07 4d 00 0b 16 1f 00 0a 0f 27 05 5a 00 03 63 24 26 34 00 42 0c 02 04 00 63 63 00 00 00 32 00 46 61 74 20 4c 65 61 64 2e 34 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fat Lead.4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 03 01 07 0d 27 01 55 04 0d 11 1f 06 05 0f 00 02 63 00 0d 16 04 00 0a 0d 11 01 4d 00 0b 16 1f 00 0a 0f 27 00 5a 00 03 63 24 26 34 00 42 0c 02 04 00 63 63 00 00 00 32 00 46 61 74 20 4c 65 61 64 2e 34 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: filtrsweep ** Description: a bass guitar with interesting decay ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 02 03 06 00 0e 03 54 08 1b 1f 0b 00 09 0c 00 00 4e 04 04 1f 06 00 09 01 00 00 5f 04 03 1f 1c 04 09 0a 00 03 63 08 03 38 1e 09 04 00 62 0c 02 05 00 63 32 00 32 00 32 00 66 69 6c 74 72 73 77 65 65 70 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FlangeBass ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 00 01 0e 00 01 52 04 04 1f 12 06 09 0e 00 02 52 04 1c 1f 0c 02 08 0e 00 06 45 13 12 1f 12 06 09 0e 00 05 63 04 1a 12 21 13 00 00 02 00 02 04 00 63 32 00 32 00 32 00 46 6c 61 6e 67 65 42 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FlangeBass ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 00 01 0e 00 01 52 04 04 1f 12 06 09 0e 00 01 52 04 1c 1f 0c 02 08 0e 00 01 45 13 12 1f 12 06 09 0e 00 00 63 04 1a 12 21 13 00 00 02 00 02 04 00 63 32 00 32 00 32 00 46 6c 61 6e 67 65 42 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fretless * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0a 08 02 07 0f 15 03 53 00 08 11 08 03 07 0f 00 00 63 00 0b 11 06 03 07 0f 00 00 63 05 0e 11 06 03 07 0f 00 01 63 03 08 2d 17 21 09 00 52 05 02 04 00 28 31 00 00 00 32 00 46 72 65 74 6c 65 73 73 20 2a 63 63 63 32 32 32 00 04 00 04 00 01 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fretless.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 08 0c 0c 07 00 63 02 42 00 0b 1b 0d 02 08 0e 63 01 42 0a 16 1e 0d 0c 08 08 63 04 4a 00 13 19 12 05 06 0f 63 05 60 00 13 3a 21 13 07 00 62 00 02 04 00 63 32 00 32 00 32 00 46 72 65 74 6c 65 73 73 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fretless.1 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 08 0c 0c 07 00 63 00 42 00 0b 1b 0d 02 08 0e 63 00 42 0a 16 1e 0d 0c 08 08 63 00 4a 00 13 19 12 05 06 0f 63 00 60 00 13 3a 21 13 07 00 62 00 02 04 00 63 32 00 32 00 32 00 46 72 65 74 6c 65 73 73 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fretless.2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 00 01 00 00 02 40 08 1b 1b 0d 02 05 0e 63 03 46 04 16 1e 0d 0c 06 08 63 05 3e 10 13 19 12 05 06 0f 63 07 60 0a 13 2a 21 13 07 00 62 00 02 04 00 63 32 00 32 00 32 00 46 72 65 74 6c 65 73 73 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fretless.2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 00 01 00 00 00 40 08 1b 1b 0d 02 05 0e 63 00 46 04 16 1e 0d 0c 06 08 63 00 3e 10 13 19 12 05 06 0f 63 00 60 0a 13 2a 21 13 07 00 62 00 02 04 00 63 32 00 32 00 32 00 46 72 65 74 6c 65 73 73 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fretless.2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 06 00 00 00 00 00 40 08 1b 1b 0d 02 05 0e 63 00 46 04 16 1e 0d 0c 06 08 63 00 3e 10 13 19 12 05 06 0f 63 00 60 0a 13 2a 21 13 07 00 62 00 02 04 00 63 32 00 32 00 32 00 46 72 65 74 6c 65 73 73 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 7 ** Name: FunkyBass1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 0a 09 0c 0f 01 4b 04 04 12 1f 02 07 0f 00 02 46 04 11 18 00 00 07 00 00 05 47 0a 06 1f 13 09 0a 0c 00 05 63 04 04 2a 15 00 0b 00 00 00 02 04 00 63 32 00 00 00 32 00 46 75 6e 6b 79 42 61 73 73 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FunkyBass1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 0a 09 0c 0f 00 4b 04 04 12 1f 02 07 0f 00 00 46 04 11 18 00 00 07 00 00 00 47 0a 06 1f 13 09 0a 0c 00 00 63 04 04 2a 15 00 0b 00 00 00 02 04 00 63 32 00 00 00 32 00 46 75 6e 6b 79 42 61 73 73 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FunkyBass2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 0a 09 0c 0f 00 50 08 05 12 1f 1f 07 0f 00 02 4b 04 11 12 1f 0c 07 0f 00 05 40 0d 12 1f 13 09 0a 0c 00 04 63 04 04 32 23 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 46 75 6e 6b 79 42 61 73 73 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FunkyBass2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 0a 09 0c 0f 00 50 08 05 12 1f 1f 07 0f 00 00 4b 04 11 12 1f 0c 07 0f 00 00 40 0d 12 1f 13 09 0a 0c 00 00 63 04 04 32 23 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 46 75 6e 6b 79 42 61 73 73 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jaco Bass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 14 07 02 00 08 0f 00 00 43 04 03 10 0b 00 08 0c 00 00 50 00 0e 15 0b 0f 08 0b 00 01 51 00 08 11 08 00 08 0b 00 01 63 00 03 38 19 08 06 00 62 0c 04 05 14 63 32 00 32 00 32 00 4a 61 63 6f 20 42 61 73 73 20 63 63 63 32 32 32 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jaco Bass2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 05 08 02 00 01 46 08 03 0f 0a 00 08 0c 00 00 50 00 0e 12 0b 0f 08 0b 00 01 51 00 08 0f 08 00 08 0b 00 01 63 00 03 09 19 08 06 00 62 0c 02 05 00 63 32 00 32 00 00 00 4a 61 63 6f 20 42 61 73 73 32 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: JazzWalker ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 0a 00 08 04 00 03 46 08 1e 1f 01 00 08 00 00 00 4a 00 10 1f 11 06 08 0d 1b 07 41 04 1e 1f 09 00 09 09 00 01 63 04 03 3b 1c 00 00 00 56 0c 04 05 00 63 4b 00 00 00 32 00 4a 61 7a 7a 57 61 6c 6b 65 72 63 63 63 32 32 32 20 30 30 30 30 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Kai Bass * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 13 0f 0f 00 05 63 13 13 1f 1f 00 0b 0f 00 00 44 00 10 1c 1f 0d 0f 0f 34 07 46 1c 13 1f 14 0a 0a 0d 0d 01 63 04 0e 43 1b 00 00 00 52 0c 02 04 00 28 63 00 00 00 32 00 4b 61 69 20 42 61 73 73 20 2a 63 63 63 32 32 32 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LatelyBass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 15 1f 10 07 08 03 00 03 4f 04 03 1f 09 00 08 00 00 01 4a 00 0b 1f 11 00 08 00 00 01 47 04 08 1f 09 00 08 00 00 00 63 00 0b 39 19 00 07 00 52 0c 02 04 00 63 32 00 32 00 32 00 4c 61 74 65 6c 79 42 61 73 73 63 63 63 32 32 32 02 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MonophBass ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 16 1f 01 00 08 0a 00 03 43 0a 1e 1f 01 00 08 07 00 00 46 00 10 1f 09 06 08 0f 1b 07 4a 04 1e 1f 09 00 09 0f 00 01 63 04 03 3a 1c 00 00 00 52 0c 04 0d 00 63 4b 00 00 00 32 00 4d 6f 6e 6f 70 68 42 61 73 73 63 63 63 32 32 32 00 00 00 00 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MoogerBass ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0a 07 08 03 00 06 4f 00 03 1f 09 00 08 0d 00 02 56 00 0b 1f 10 00 08 0a 00 00 60 00 08 1f 09 00 08 0f 00 00 63 00 0e 3c 1d 00 07 00 52 0c 02 0c 00 63 32 00 32 00 32 00 4d 6f 6f 67 65 72 42 61 73 73 63 63 63 32 32 32 02 70 00 50 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntBass.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 03 0a 09 0d 2e 01 55 04 03 1c 10 0a 09 00 00 02 48 10 01 1c 10 0a 09 0a 00 06 4f 04 03 1c 16 04 09 0e 00 03 62 04 04 42 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 42 61 73 73 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntBass.1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 03 0a 09 0d 2e 00 55 04 03 1c 10 0a 09 00 00 00 48 10 01 1c 10 0a 09 0a 00 00 4f 04 03 1c 16 04 09 0e 00 00 62 04 04 42 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 42 61 73 73 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntBass.2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0f 0a 07 06 2e 02 4d 16 03 1c 0f 0a 07 00 00 03 41 10 01 1c 15 0a 07 0a 00 05 63 04 03 1c 13 0a 07 0e 00 05 62 04 04 44 2e 00 11 00 11 00 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 42 61 73 73 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntBass.2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0f 0a 07 06 2e 00 4d 16 03 1c 0f 0a 07 00 00 00 41 10 01 1c 15 0a 07 0a 00 00 63 04 03 1c 13 0a 07 0e 00 00 62 04 04 44 2e 00 11 00 11 00 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 42 61 73 73 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Precision* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 17 0c 09 08 04 18 05 58 10 13 1f 1c 04 01 0d 00 00 53 00 18 18 15 0a 04 09 00 03 4f 00 13 0f 1d 04 08 0e 14 00 63 00 1e 03 1d 00 00 00 52 0c 02 04 00 28 53 00 00 00 32 00 50 72 65 63 69 73 69 6f 6e 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ResoMonoBs ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 07 08 03 00 03 4c 04 03 1f 09 00 08 0d 00 01 52 00 0b 1f 11 00 08 0b 00 01 55 04 08 1f 09 00 08 0f 00 00 5e 00 0b 39 19 00 07 00 52 0c 02 0c 00 63 32 00 32 00 32 00 52 65 73 6f 4d 6f 6e 6f 42 73 63 63 63 32 32 32 12 10 00 10 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Roto Bass ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 07 04 04 00 01 5b 00 1b 1f 12 06 09 0e 00 01 49 00 1b 1f 0c 02 08 03 63 04 58 13 13 1f 12 06 09 0e 0e 05 63 00 1b 22 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 52 6f 74 6f 20 42 61 73 73 20 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Roto Bass ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 07 04 04 00 01 5b 00 1b 1f 12 06 09 0e 00 01 49 00 1b 1f 0c 02 08 03 63 01 58 13 13 1f 12 06 09 0e 0e 00 63 00 1b 22 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 52 6f 74 6f 20 42 61 73 73 20 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SUPER BASS ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 05 07 05 00 00 41 04 03 1f 05 05 07 0c 00 00 49 00 03 19 09 00 07 00 00 40 47 08 00 1f 05 05 07 0c 00 00 63 00 03 39 19 00 09 00 52 0c 02 04 00 28 32 00 00 00 32 00 53 55 50 45 52 20 42 41 53 53 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Science.BS ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 08 08 0a 0a 00 03 53 04 02 13 09 06 06 0c 00 03 57 04 03 14 02 0f 09 0d 00 05 63 04 04 19 03 0f 0a 0e 00 05 63 08 03 3c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 53 63 69 65 6e 63 65 2e 42 53 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Science.BS ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 08 08 0a 0a 00 01 53 04 02 13 09 06 06 0c 00 01 57 04 03 14 02 0f 09 0d 00 01 63 04 04 19 03 0f 0a 0e 00 00 63 08 03 3c 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 53 63 69 65 6e 63 65 2e 42 53 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SlapBass ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 0a 08 06 00 07 5a 18 1e 1f 01 00 08 00 00 00 48 00 10 1f 09 06 08 0c 1b 06 4b 1c 1e 1f 09 00 09 09 00 01 63 04 03 00 1c 00 00 00 52 0c 04 05 00 63 50 00 3c 00 32 00 53 6c 61 70 42 61 73 73 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SlapBass.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 00 0a 08 00 01 46 04 03 18 0c 1f 09 00 00 01 49 1f 13 1e 10 07 09 0c 00 04 4c 0a 08 1e 10 07 09 0c 00 06 63 04 0b 3b 19 00 09 00 52 0c 02 04 00 63 32 00 32 00 32 00 53 6c 61 70 42 61 73 73 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SlapBass.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 08 00 0a 08 00 01 46 04 03 18 0c 1f 09 00 00 01 49 1f 13 1e 10 07 09 0c 00 01 4c 0a 08 1e 10 07 09 0c 00 00 63 04 0b 3b 19 00 09 00 52 0c 02 04 00 63 32 00 32 00 32 00 53 6c 61 70 42 61 73 73 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SlapBass.2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 1c 09 09 0c 00 01 44 10 0b 16 00 00 0a 09 00 01 4b 04 13 14 0a 05 09 0f 00 03 52 04 10 1e 10 07 09 0c 00 06 63 04 0b 2a 19 00 09 00 52 00 02 04 00 63 32 00 32 00 32 00 53 6c 61 70 42 61 73 73 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SlapBass.2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 1c 09 09 0c 00 00 44 10 0b 16 00 00 0a 09 00 00 4b 04 13 14 0a 05 09 0f 00 00 52 04 10 1e 10 07 09 0c 00 00 63 04 0b 2a 19 00 09 00 52 00 02 04 00 63 32 00 32 00 32 00 53 6c 61 70 42 61 73 73 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SqncrBass ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 10 1f 0f 0f 0e 08 00 03 4b 25 06 1f 0f 05 0b 0e 4e 03 5d 00 0b 1f 13 06 08 0b 1c 03 60 00 10 1f 13 06 08 0e 00 04 63 04 0e 3b 1f 00 00 00 42 0c 02 04 00 63 32 00 32 00 32 00 53 71 6e 63 72 42 61 73 73 20 63 63 63 32 32 32 03 60 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SqncrBass2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 0f 0e 08 00 03 42 25 06 1f 0f 05 0b 0f 39 03 61 00 0b 1f 13 06 08 0b 1c 03 52 00 10 1f 13 06 08 0f 00 04 63 04 0e 3b 1f 00 00 00 42 18 02 04 00 63 32 00 32 00 32 00 53 71 6e 63 72 42 61 73 73 32 63 63 63 32 32 32 03 30 10 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Bass 1 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank A, voice 16 1f 03 10 06 00 34 01 49 04 15 1f 00 12 06 0f 5e 04 63 0a 0b 14 1f 17 06 0b 3d 01 57 16 18 1f 1e 00 07 0f 00 01 5d 04 13 3a 23 00 00 00 62 00 02 04 00 63 32 00 00 00 32 00 53 79 2e 42 61 73 73 20 20 31 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Bass 1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 03 10 06 00 34 01 49 04 15 1f 00 12 06 0f 5e 04 63 0d 0b 14 1f 17 06 0b 3d 01 57 16 18 1f 1e 00 07 0f 00 01 5d 04 13 3a 23 00 00 00 62 00 02 04 00 63 32 00 00 00 32 00 53 79 2e 42 61 73 73 20 20 31 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Bass 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 17 1d 0e 00 06 09 00 02 51 04 0e 1f 0a 00 0a 00 00 00 61 00 01 1f 0a 00 0a 00 00 00 61 00 06 1f 0a 00 0a 00 00 00 61 00 10 3d 1b 42 19 00 52 0c 02 04 00 63 32 00 00 00 32 00 53 79 2e 42 61 73 73 20 20 32 63 63 63 32 32 32 00 10 00 50 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Bass 3 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank A, voice 18 1f 12 0c 02 07 00 02 59 10 13 1e 04 02 07 09 00 02 55 00 13 1f 0c 03 07 05 00 02 46 00 16 1e 09 03 07 00 00 00 57 00 13 00 1f 00 00 00 62 0c 02 04 00 28 32 00 00 00 32 00 53 79 2e 42 61 73 73 20 20 33 63 63 63 32 32 32 00 10 00 10 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Bass 4 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank A, voice 19 1a 0b 00 08 04 00 03 4e 00 13 1a 09 03 08 04 00 02 50 00 13 15 0b 05 08 04 0a 05 58 00 1b 18 09 03 09 04 00 01 63 00 0b 3a 1c 00 00 00 52 0c 02 05 00 63 4b 00 00 00 32 00 53 79 2e 42 61 73 73 20 20 34 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Bass 4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 0b 00 08 04 00 03 4e 00 13 1a 09 03 08 04 00 02 50 00 13 15 0b 05 08 04 0d 05 58 00 1b 18 09 03 09 04 00 01 63 00 0b 3a 1c 00 00 00 52 0c 02 05 00 63 4b 00 00 00 32 00 53 79 2e 42 61 73 73 20 20 34 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Bass 5 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank A, voice 20 1f 12 08 02 0b 00 02 59 10 13 1e 06 02 07 09 00 04 56 00 13 1f 0c 03 07 05 00 05 46 00 16 1e 05 03 07 09 00 02 5d 00 13 02 20 00 00 00 62 0c 02 04 00 28 32 00 00 00 32 00 53 79 2e 42 61 73 73 20 20 35 63 63 63 32 32 32 00 10 00 10 00 50 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Bass 6 ** Description: A clavier with bite, derived from DX11 factory preset ** 'Sy.Bass 5' ** Controls: KV ** Source: Art Hitzeman 1f 12 08 02 0b 00 02 59 10 13 1e 06 02 04 09 00 04 56 00 13 1f 0c 03 05 05 00 05 46 00 16 1e 05 03 05 09 00 02 5d 00 13 02 20 00 00 00 62 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 42 61 73 73 20 20 36 63 63 63 32 32 32 00 10 00 10 00 50 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Bass 7 ** Description: a milder version of Sy. Bass 6 ** Controls: KV ** Source: Art Hitzeman 1f 12 08 02 0b 00 02 46 1f 13 1e 06 02 04 09 00 04 56 00 13 1f 0c 03 05 05 00 05 46 00 16 1e 05 03 05 09 00 02 5d 00 13 02 20 00 00 00 62 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 42 61 73 73 20 20 37 63 63 63 32 32 32 00 10 00 10 00 50 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynFunkBas ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 11 1f 0f 0f 0e 09 00 03 46 1d 16 1f 0f 05 0e 0e 0d 03 63 00 0b 1f 13 06 05 0d 1c 03 63 00 00 1f 13 06 08 0f 00 04 63 04 06 3b 1f 00 00 00 42 0c 04 04 00 63 32 00 32 00 32 00 53 79 6e 46 75 6e 6b 42 61 73 63 63 63 32 32 32 0d 6c 00 00 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynFunkBs2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0d 19 08 06 00 03 3c 1d 16 1f 0f 05 07 0e 1a 03 63 00 0b 1f 13 06 05 0d 2a 03 54 00 08 1f 13 06 08 0f 00 04 63 04 06 3b 1f 00 00 00 42 18 02 04 00 63 32 00 32 00 32 00 53 79 6e 46 75 6e 6b 42 73 32 63 63 63 32 32 32 0c 1c 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: TeknoBass* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 1e 13 05 0e 2d 03 53 2f 00 15 0b 09 05 0e 0a 02 55 00 06 19 1e 09 08 0e 17 00 4a 08 00 15 02 07 06 0e 10 01 63 00 0b 02 23 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 54 65 6b 6e 6f 42 61 73 73 2a 63 63 63 32 32 32 00 04 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wood Bass ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 30 17 15 0c 09 0b 2c 02 63 04 0b 1f 11 0b 04 0f 27 01 47 04 0b 1c 11 0b 02 0f 31 01 4b 0a 0b 1a 15 0a 08 0f 00 01 63 04 0b 10 1f 00 00 00 52 00 02 04 00 28 32 00 00 00 32 00 57 6f 6f 64 20 42 61 73 73 20 63 63 63 32 32 32 02 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WoodBass2 ** Description: A modified version of the DX11 factory preset 'Wood Bass' ** Controls: KV ** Source: Art Hitzeman 17 15 0c 09 0b 2c 02 63 04 0b 1f 11 0b 04 0f 27 01 47 04 0b 1c 11 0b 02 0f 31 01 4b 0a 0b 1a 15 0a 05 0f 00 01 63 04 0b 10 1f 00 00 00 52 0c 02 04 00 28 32 00 00 00 32 00 57 6f 6f 64 42 61 73 73 32 20 63 63 63 32 32 32 02 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Cello ** Name: BowCello ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 18 1c 09 00 06 0e 35 00 50 04 00 0d 0a 00 06 0e 09 01 52 04 09 12 0b 00 08 09 2c 02 49 10 0e 0c 0a 00 06 0f 00 01 63 04 0b 2a 1a 07 13 00 52 0c 04 04 00 63 32 00 00 00 32 00 42 6f 77 43 65 6c 6c 6f 20 20 63 63 63 32 32 32 00 00 00 10 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BoxCello ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 19 1c 04 00 06 0e 0f 00 48 04 06 15 09 03 04 0d 15 01 3a 08 00 11 10 00 0b 09 0c 02 51 16 06 0b 0a 00 05 0f 05 01 63 08 13 32 1d 06 0f 00 52 0c 04 04 00 63 32 00 00 00 32 00 42 6f 78 43 65 6c 6c 6f 20 20 63 63 63 32 32 32 00 10 00 40 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BoxCello2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 04 00 06 0e 0f 00 48 04 06 0f 12 01 04 0d 15 01 3a 08 00 11 10 00 0b 09 0c 02 51 16 06 0b 0a 00 05 0f 05 01 63 08 13 32 1d 06 0f 00 52 0c 04 04 00 63 32 00 00 00 32 00 42 6f 78 43 65 6c 6c 6f 32 20 63 63 63 32 32 32 00 10 00 40 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cello * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 05 0f 4d 00 48 08 0b 10 19 00 03 0e 2b 00 61 08 0b 0c 09 00 05 0d 00 01 63 08 15 0c 05 00 06 0e 4d 01 5c 13 12 3c 1f 19 15 00 32 00 02 04 00 28 63 00 00 00 32 00 43 65 6c 6c 6f 20 20 20 20 2a 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cello 1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 7 16 07 00 06 0f 00 41 4c 00 0b 15 0b 00 03 0e 00 02 35 00 0b 19 08 00 08 0c 10 05 5f 08 14 0e 0b 00 07 0e 00 02 63 00 0c 3a 1f 12 10 00 52 18 02 05 00 63 28 50 32 00 32 00 43 65 6c 6c 6f 20 20 20 20 31 63 56 63 30 32 32 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cello 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 8 1d 08 00 05 0a 12 01 4e 04 06 17 0c 00 05 0f 00 00 42 04 00 1e 0e 00 05 0f 25 01 3e 10 00 0a 06 00 07 0f 00 02 63 04 06 32 1d 16 2c 00 32 0c 02 04 00 63 32 00 00 00 32 00 43 65 6c 6c 6f 20 20 20 20 32 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cello 2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 08 00 05 0d 12 01 4e 04 06 17 0c 00 05 0f 00 00 42 04 00 1e 0e 00 05 0f 25 01 3e 10 00 0d 06 00 07 0f 00 02 63 04 06 32 1d 16 2c 00 32 0c 02 04 00 63 32 00 00 00 32 00 43 65 6c 6c 6f 20 20 20 20 32 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cello....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 15 0a 08 0d 19 02 4c 19 0c 15 10 07 07 0c 19 01 5d 04 03 11 15 06 08 0f 3c 04 50 0a 0b 0c 09 04 06 0c 00 04 63 04 0a 5b 1b 1b 20 00 32 00 04 04 00 63 01 63 00 00 32 00 43 65 6c 6c 6f 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cello....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 15 0a 08 0d 19 02 4c 2a 0c 1f 17 0a 07 0d 19 01 5d 04 03 10 15 0a 08 0d 3c 01 58 0a 0b 0f 09 07 06 0d 00 00 63 04 0a 5b 22 00 14 00 32 00 04 04 00 63 01 63 00 00 32 00 43 65 6c 6c 6f 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cello....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 09 04 03 0e 02 01 53 00 12 1e 04 10 05 08 52 01 49 00 0a 14 1f 0b 05 03 52 05 63 00 0c 0b 03 1f 06 02 10 05 60 00 0c 32 1c 2e 1e 00 42 18 0c 04 00 63 32 32 00 00 32 00 43 65 6c 6c 6f 2e 2e 2e 2e 32 63 63 63 32 32 32 00 10 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cello....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 09 04 03 0e 02 01 53 00 12 1e 04 10 05 08 52 01 49 00 0a 14 1f 0b 05 03 52 01 63 00 0c 0c 03 1f 06 02 10 00 60 00 0c 32 1c 2e 1e 00 42 18 0c 04 00 63 32 32 00 00 32 00 43 65 6c 6c 6f 2e 2e 2e 2e 32 63 63 63 32 32 32 00 10 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY DoubleBass ** Name: ArcoBass * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 15 00 06 0d 13 01 56 04 08 1e 05 00 06 0e 33 02 63 04 0e 0f 09 00 06 0f 00 01 63 04 06 0d 08 00 05 0e 00 05 63 04 08 3c 1e 17 0f 00 42 0c 02 04 00 28 63 00 00 00 32 00 41 72 63 6f 42 61 73 73 20 2a 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassBeast ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 09 09 03 05 0a 22 02 63 00 04 0b 05 04 03 0f 00 02 4a 08 02 03 08 03 05 08 38 42 5a 0d 0e 10 05 03 06 0d 00 00 61 00 01 62 1c 06 04 06 4e 18 02 04 00 63 32 00 00 00 32 00 42 61 73 73 42 65 61 73 74 20 63 63 63 32 32 32 07 00 11 50 00 30 01 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassPluck2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 16 06 08 0d 00 01 40 08 1b 15 16 06 08 0d 00 01 51 00 1b 1f 0d 0c 08 0a 63 06 63 00 13 1f 0d 0c 08 0a 63 05 63 00 13 54 26 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 42 61 73 73 50 6c 75 63 6b 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassPluck2 ** Source: from a library of patches collected by Duane Bowker, author unknown 15 16 06 08 0d 00 00 40 08 1b 15 16 06 08 0d 00 00 51 00 1b 1f 0d 0c 08 0a 63 00 63 00 13 1f 0d 0c 08 0a 63 00 63 00 13 54 26 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 42 61 73 73 50 6c 75 63 6b 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DoubleBass ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 17 1c 02 00 04 0e 63 00 4e 04 03 0d 05 00 06 0e 63 01 56 0a 08 12 07 00 06 09 63 02 47 1b 0e 0a 0a 00 05 0f 00 01 63 04 0b 32 1a 08 0d 00 52 0c 04 04 00 63 32 00 00 00 32 00 44 6f 75 62 6c 65 42 61 73 73 63 63 63 32 32 32 00 10 00 00 00 03 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 8 * SUBCATEGORY Guitar ** Name: 12 String1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0f 09 03 0c 39 02 4f 22 1c 0d 0f 09 02 0c 00 43 3c 0d 0a 16 1f 07 06 0f 0a 45 58 08 08 1c 07 06 06 0c 00 46 5f 08 0e 0c 22 21 00 00 36 0c 00 04 00 63 63 00 00 00 32 00 31 32 20 53 74 72 69 6e 67 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: 12 String1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 0f 09 03 0c 39 01 4f 22 1c 0d 0f 09 02 0c 00 41 3c 0d 0a 16 1f 07 06 0f 0a 40 58 08 08 1c 07 06 06 0c 00 40 5f 08 0e 0c 22 21 00 00 36 0c 00 04 00 63 63 00 00 00 32 00 31 32 20 53 74 72 69 6e 67 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: 12 String2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 09 04 03 0c 2b 42 51 24 1c 1a 04 01 0d 0b 00 42 53 04 1a 1b 09 06 0e 0c 0a 45 4e 04 10 1c 07 03 07 0e 00 44 63 04 16 14 23 46 00 01 3a 18 00 04 00 63 63 00 00 00 32 00 31 32 20 53 74 72 69 6e 67 32 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: 12 String2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 09 04 03 0c 2b 41 51 24 1c 1a 04 01 0d 0b 00 41 53 04 1a 1b 09 06 0e 0c 0a 40 4e 04 10 1c 07 03 07 0e 00 40 63 04 16 14 23 46 00 01 3a 18 00 04 00 63 63 00 00 00 32 00 31 32 20 53 74 72 69 6e 67 32 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: 5th Lead.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 06 07 09 06 0f 03 58 08 0a 19 09 07 09 06 0c 05 61 0a 0d 19 0c 07 09 06 2e 05 63 08 03 1f 15 07 09 0d 00 06 63 0a 02 2c 1e 17 00 00 22 0c 02 04 00 63 32 00 00 00 32 00 35 74 68 20 4c 65 61 64 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: 5th Lead.1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 06 07 09 06 0f 01 58 08 0a 19 09 07 09 06 0c 01 61 0a 0d 19 0c 07 09 06 2e 00 63 08 03 1f 15 07 09 0d 00 00 63 0a 02 2c 1e 17 00 00 22 0c 02 04 00 63 32 00 00 00 32 00 35 74 68 20 4c 65 61 64 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: 5th Lead.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 00 09 0c 20 03 4c 08 0a 1f 09 09 09 0c 00 05 51 0a 05 1f 1f 00 09 0f 40 05 63 08 03 1f 09 09 09 0c 00 05 63 0a 02 2c 1e 17 00 00 22 18 02 04 00 63 32 00 00 00 32 00 35 74 68 20 4c 65 61 64 2e 32 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: 5th Lead.2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 00 09 0c 20 01 4c 08 0a 1f 09 09 09 0c 00 01 51 0a 05 1f 1f 00 09 0f 40 00 63 08 03 1f 09 09 09 0c 00 00 63 0a 02 2c 1e 17 00 00 22 18 02 04 00 63 32 00 00 00 32 00 35 74 68 20 4c 65 61 64 2e 32 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Guitar1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 17 05 04 03 0e 23 03 45 08 13 1f 0c 08 05 0f 43 05 42 04 03 1f 08 11 06 0f 00 05 40 0a 03 1b 15 06 06 0e 23 04 63 08 13 31 23 00 00 00 52 0c 04 04 00 63 32 00 00 00 32 00 41 43 20 47 75 69 74 61 72 31 63 63 63 32 32 32 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Guitar1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 17 05 04 03 0e 23 01 45 08 13 1f 0c 08 05 0f 43 01 42 04 03 1f 08 11 06 0f 00 01 40 0a 03 1b 15 06 06 0e 23 00 63 08 13 31 23 00 00 00 52 0c 04 04 00 63 32 00 00 00 32 00 41 43 20 47 75 69 74 61 72 31 63 63 63 32 32 32 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Guitar2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0e 04 03 0e 23 02 4b 0a 13 1f 1f 08 05 0f 43 03 42 04 03 1f 15 11 06 0f 00 04 35 0a 03 1f 1f 06 0a 0e 00 03 63 04 0b 38 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 41 43 20 47 75 69 74 61 72 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Guitar2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 04 03 0e 23 01 4b 0a 13 1f 1f 08 05 0f 43 01 42 04 03 1f 15 11 06 0f 00 00 35 0a 03 1f 1f 06 0a 0e 00 00 63 04 0b 38 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 41 43 20 47 75 69 74 61 72 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Guitar3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0e 00 0f 0f 23 03 4c 04 1b 1f 1f 00 0b 0f 43 03 51 0a 13 1f 1a 06 0d 0d 00 04 63 04 03 1f 1f 08 0e 0f 00 04 57 08 0b 24 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 41 43 20 47 75 69 74 61 72 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Guitar3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 00 0f 0f 23 02 4c 04 1b 1f 1f 00 0b 0f 43 01 51 0a 13 1f 1a 06 0d 0d 00 01 63 04 03 1f 1f 08 0e 0f 00 00 57 08 0b 24 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 41 43 20 47 75 69 74 61 72 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Guitar4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0f 0c 04 0f 23 41 53 04 13 1a 15 06 0b 0f 2b 41 4f 04 13 1d 0a 09 06 0f 00 45 63 04 13 1c 04 04 0e 0f 00 45 63 04 0b 34 22 33 00 01 36 0c 04 04 00 63 32 00 00 00 32 00 41 43 20 47 75 69 74 61 72 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Guitar4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 0f 0c 04 0f 23 41 53 04 13 1a 15 06 0b 0f 2b 41 4f 04 13 1d 0a 09 06 0f 00 40 63 04 13 1c 04 04 0e 0f 00 40 63 04 0b 34 22 33 00 01 36 0c 04 04 00 63 32 00 00 00 32 00 41 43 20 47 75 69 74 61 72 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AllThatJaz ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 5 1f 09 00 06 0c 27 04 48 1c 0b 1c 06 00 04 07 37 01 43 0a 00 1f 0a 00 02 06 3d 00 45 0a 0e 1f 12 08 08 0f 00 05 63 04 0b 38 1f 00 03 00 52 0c 03 04 00 63 32 01 32 00 32 00 41 6c 6c 54 68 61 74 4a 61 7a 63 63 63 32 32 32 00 30 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: A.Guitar ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 8 1f 1f 06 06 0a 28 03 4b 04 0a 1c 0a 07 07 0a 28 02 3d 0a 0a 1f 0b 07 07 0b 28 02 45 1f 0b 1b 0c 08 09 0a 00 02 62 04 0c 3a 1e 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 41 2e 47 75 69 74 61 72 20 20 63 63 63 32 32 32 00 00 00 20 00 40 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: A.Guitar ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 06 06 0d 28 03 4b 04 0a 1c 0a 07 07 0d 28 02 3d 0d 0a 1f 0b 07 07 0b 28 02 45 1f 0b 1b 0c 08 09 0d 00 02 62 04 0c 3a 1e 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 41 2e 47 75 69 74 61 72 20 20 63 63 63 32 32 32 00 00 00 20 00 40 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Guitar ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 11 1f 1f 04 03 0f 0c 01 46 0a 16 1f 1f 03 03 0f 08 00 56 04 0a 1f 1f 04 03 0f 08 01 53 00 04 18 1f 05 0b 0f 00 02 55 04 00 3a 23 1c 08 00 42 18 02 04 00 28 63 00 00 00 32 00 45 2e 47 75 69 74 61 72 20 20 63 63 63 32 32 32 00 00 00 20 00 60 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Guitar ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 04 03 0f 0c 01 46 0d 16 1f 1f 03 03 0f 08 00 56 04 0a 1f 1f 04 03 0f 08 01 53 00 04 18 1f 05 0b 0f 00 02 55 04 00 3a 23 1c 08 00 42 18 02 04 00 28 63 00 00 00 32 00 45 2e 47 75 69 74 61 72 20 20 63 63 63 32 32 32 00 00 00 20 00 60 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Echo Lead ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 09 03 0d 00 06 57 08 0e 1f 16 00 05 0e 00 05 63 04 03 1f 16 00 08 0e 00 05 63 04 0b 1f 16 00 05 0e 00 05 63 04 03 75 25 00 00 00 32 18 04 04 00 63 01 63 00 00 32 00 45 63 68 6f 20 4c 65 61 64 20 63 63 63 32 32 32 00 00 00 20 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Echo Lead ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 09 03 0d 00 02 57 08 0e 1f 16 00 05 0e 00 00 63 04 03 1f 16 00 08 0e 00 00 63 04 0b 1f 16 00 05 0e 00 00 63 04 03 75 25 00 00 00 32 18 04 04 00 63 01 63 00 00 32 00 45 63 68 6f 20 4c 65 61 64 20 63 63 63 32 32 32 00 00 00 20 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: F.Guitar ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 9 1f 1f 05 06 0f 28 03 4f 04 0a 1a 09 07 07 0a 28 02 49 0a 0a 1f 0b 04 07 0b 28 02 45 1f 0b 1a 0c 06 08 0a 00 02 62 04 0c 3a 1e 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 46 2e 47 75 69 74 61 72 20 20 63 63 63 32 32 32 00 00 00 10 00 70 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: F.Guitar ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 05 06 0f 28 03 4f 04 0a 1a 09 07 07 0d 28 02 49 0d 0a 1f 0b 04 07 0b 28 02 45 1f 0b 1a 0c 06 08 0d 00 02 62 04 0c 3a 1e 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 46 2e 47 75 69 74 61 72 20 20 63 63 63 32 32 32 00 00 00 10 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flamenco ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 7 18 0b 0a 07 0a 28 02 52 0a 0b 1f 12 00 05 0f 07 02 40 04 0b 17 0f 0e 06 09 11 06 52 0a 0b 17 13 09 08 0e 00 01 63 04 0b 31 1e 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 46 6c 61 6d 65 6e 63 6f 20 20 63 63 63 32 32 32 00 30 00 00 00 10 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flamenco ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 18 0b 0a 07 0d 28 02 52 0a 0b 1f 12 00 05 0f 07 02 40 04 0b 17 0f 0e 06 09 11 06 52 0a 0b 17 13 09 08 0e 00 01 63 04 0b 31 1e 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 46 6c 61 6d 65 6e 63 6f 20 20 63 63 63 32 32 32 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Funkfifth* ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 00 06 0c 00 00 4a 04 00 1f 0a 00 02 0b 00 00 58 04 06 1b 1f 00 09 0f 00 00 63 04 06 1b 1e 00 09 0f 11 00 54 04 00 3c 23 00 00 00 42 0c 02 04 00 28 63 00 00 00 32 00 46 75 6e 6b 66 69 66 74 68 2a 63 63 63 32 32 32 00 00 00 78 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Funky Pick ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 4 1f 19 08 0a 03 00 03 63 10 18 1f 0c 05 0b 05 00 03 52 00 0b 1f 19 06 0b 05 00 03 63 0a 16 1f 0f 11 09 07 00 04 63 04 03 3a 14 00 00 00 42 18 02 04 00 63 32 00 32 00 32 00 46 75 6e 6b 79 20 50 69 63 6b 63 63 63 32 32 32 00 30 00 00 00 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FuzzyLead ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 05 06 07 0d 00 43 63 00 08 1c 0a 04 03 0c 00 44 58 04 10 1f 0c 02 07 0d 00 45 62 00 16 1d 0a 06 07 0d 15 46 63 04 16 2c 23 00 00 01 06 0c 02 04 00 28 02 00 00 00 32 00 46 75 7a 7a 79 4c 65 61 64 20 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FuzzyLead ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 06 07 0d 00 42 63 00 08 1c 0a 04 03 0c 00 41 58 04 10 1f 0c 02 07 0d 00 40 62 00 16 1d 0a 06 07 0d 15 40 63 04 16 2c 23 00 00 01 06 0c 02 04 00 28 02 00 00 00 32 00 46 75 7a 7a 79 4c 65 61 64 20 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Guitar ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 1f 0b 06 0a 63 03 37 08 0a 1a 0b 04 05 07 3c 01 43 0a 0a 1a 0b 04 05 07 00 01 32 1d 08 1f 0b 0b 06 0b 1e 02 63 04 0c 32 19 00 00 00 32 0c 02 04 00 28 63 00 00 00 32 00 47 75 69 74 61 72 20 20 20 20 63 63 63 32 32 32 00 00 00 30 00 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Guitar #1 ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 2 1f 0e 0e 0f 0f 00 03 48 2d 06 1f 0f 04 0f 0f 25 03 5b 00 00 1f 12 06 0c 0e 1c 03 55 00 10 1f 13 07 08 0e 06 04 62 04 0e 3b 14 00 00 00 42 18 02 04 00 63 32 00 32 00 32 00 47 75 69 74 61 72 20 23 31 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HardHabits ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 04 0c 08 00 2f 3c 4b 04 0b 1c 15 06 08 0c 00 1f 52 24 13 1c 04 00 08 0c 1e 1c 63 04 0b 1c 07 07 08 0b 00 3f 63 04 0b 04 22 21 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 48 61 72 64 48 61 62 69 74 73 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HardHabits ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 04 0c 08 00 2f 39 4b 04 0b 1c 15 06 08 0c 00 1a 52 24 13 1c 04 00 08 0c 1e 18 63 04 0b 1c 07 07 08 0b 00 38 63 04 0b 04 22 21 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 48 61 72 64 48 61 62 69 74 73 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HawaiiGitr ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 00 06 00 27 02 38 22 0b 13 06 00 08 00 37 02 3b 0a 03 13 0a 00 09 00 3d 02 45 0a 0e 13 12 08 08 0f 00 03 61 04 0b 32 1f 00 03 00 52 0c 03 04 00 63 32 01 32 00 32 00 48 61 77 61 69 69 47 69 74 72 63 63 63 32 32 32 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HeavyLead ** Controls: MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 25 11 06 08 0f 0a 2f 00 4d 04 03 1f 1f 00 0f 0f 00 00 50 00 03 1f 0c 00 0f 0d 00 00 4e 00 03 1f 1f 00 0f 0f 00 00 5a 00 03 30 20 27 06 00 62 18 07 04 00 63 32 00 00 00 32 00 48 65 61 76 79 4c 65 61 64 20 63 63 63 32 32 32 00 00 00 10 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HeavyMetal ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 6 1f 08 00 03 0f 20 01 58 00 06 1f 03 00 02 0f 1b 01 4c 0d 02 15 05 04 03 0f 1b 00 5b 0a 05 1f 0f 00 08 0f 00 00 5c 04 03 3a 1f 16 05 00 62 18 07 04 00 63 32 00 00 00 32 00 48 65 61 76 79 4d 65 74 61 6c 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HollowLead ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 1f 00 07 0f 00 04 47 08 06 12 0c 06 09 0f 00 04 5a 04 0e 14 1f 00 09 0f 00 05 62 04 06 18 1f 00 0d 0f 00 05 56 04 03 74 29 00 00 00 22 18 05 04 00 63 63 63 00 00 32 00 48 6f 6c 6c 6f 77 4c 65 61 64 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HollowLead ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 1f 00 07 0f 00 01 47 08 06 12 0c 06 09 0f 00 01 5a 04 0e 14 1f 00 09 0f 00 00 62 04 06 18 1f 00 0d 0f 00 00 56 04 03 74 29 00 00 00 22 18 05 04 00 63 63 63 00 00 32 00 48 6f 6c 6c 6f 77 4c 65 61 64 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jazz Guit1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 09 07 07 0b 55 43 5f 0a 13 1b 09 07 07 0b 63 45 5b 0a 0b 18 09 07 07 0d 63 45 63 04 1b 16 09 07 07 09 0a 46 5f 04 03 04 19 00 01 03 16 18 00 04 00 63 63 00 00 00 32 00 4a 61 7a 7a 20 47 75 69 74 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jazz Guit1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 19 09 07 07 0b 55 41 5f 0a 13 1b 09 07 07 0b 63 42 5b 0a 0b 18 09 07 07 0d 63 40 63 04 1b 16 09 07 07 09 0a 40 5f 04 03 04 19 00 01 03 16 18 00 04 00 63 63 00 00 00 32 00 4a 61 7a 7a 20 47 75 69 74 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jazz Guit2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 11 1f 0f 00 2e 03 60 31 03 1c 07 0c 09 0e 00 03 4a 08 0b 17 1a 07 07 0d 4f 05 51 04 00 16 1a 09 09 0e 00 04 63 04 0b 04 23 00 05 00 22 0c 03 04 00 63 63 00 00 00 32 00 4a 61 7a 7a 20 47 75 69 74 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jazz Guit2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 11 1f 0f 00 2e 02 60 31 03 1c 07 0c 09 0e 00 01 4a 08 0b 17 1a 07 07 0d 4f 00 51 04 00 16 1a 09 09 0e 00 00 63 04 0b 04 23 00 05 00 22 0c 03 04 00 63 63 00 00 00 32 00 4a 61 7a 7a 20 47 75 69 74 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jazz Guit3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 19 08 0f 0d 2e 04 5e 2a 1b 1b 11 0c 09 0c 00 03 48 08 0b 18 19 07 07 0d 4f 04 51 04 00 16 19 09 09 0e 00 06 63 04 0b 04 23 00 05 00 22 0c 03 04 00 63 63 00 00 00 32 00 4a 61 7a 7a 20 47 75 69 74 33 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jazz Guit3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 19 19 08 0f 0d 2e 01 5e 2a 1b 1b 11 0c 09 0c 00 01 48 08 0b 18 19 07 07 0d 4f 00 51 04 00 16 19 09 09 0e 00 00 63 04 0b 04 23 00 05 00 22 0c 03 04 00 63 63 00 00 00 32 00 4a 61 7a 7a 20 47 75 69 74 33 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jazz Guit4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 1f 0f 00 63 44 5b 2a 03 1b 09 07 07 0b 49 45 5c 0a 0b 18 09 07 07 0d 2d 44 63 0a 1b 16 09 07 07 09 0a 46 5f 04 03 3c 23 00 01 04 3a 18 00 04 00 63 63 00 00 00 32 00 4a 61 7a 7a 20 47 75 69 74 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jazz Guit4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 1f 0f 00 63 42 5b 2a 03 1b 09 07 07 0b 49 42 5c 0a 0b 18 09 07 07 0d 2d 40 63 0a 1b 16 09 07 07 09 0a 40 5f 04 03 3c 23 00 01 04 3a 18 00 04 00 63 63 00 00 00 32 00 4a 61 7a 7a 20 47 75 69 74 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: JonesLead ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0a 04 07 0f 00 03 53 04 13 1b 0a 04 07 0c 00 04 4b 10 13 1b 18 06 07 0f 00 06 62 04 12 1b 18 06 07 0f 00 05 63 04 13 7c 2d 00 00 00 22 0c 02 04 00 63 63 00 00 00 32 00 4a 6f 6e 65 73 4c 65 61 64 20 63 63 63 32 32 32 00 00 00 40 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: JonesLead ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 0a 04 07 0f 00 01 53 04 13 1b 0a 04 07 0c 00 01 4b 10 13 1b 18 06 07 0f 00 00 62 04 12 1b 18 06 07 0f 00 00 63 04 13 7c 2d 00 00 00 22 0c 02 04 00 63 63 00 00 00 32 00 4a 6f 6e 65 73 4c 65 61 64 20 63 63 63 32 32 32 00 00 00 40 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: KickLead ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 0f 07 07 0e 19 04 52 04 10 0d 01 06 08 0c 19 06 58 04 10 1f 15 07 07 0e 00 05 63 04 08 0e 03 02 06 0e 00 46 63 04 0e 3c 0f 1d 23 3d 26 0c 04 05 00 28 32 00 00 00 32 00 4b 69 63 6b 4c 65 61 64 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: KickLead ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 0f 07 07 0e 19 01 52 04 10 0d 01 06 08 0c 19 01 58 04 10 1f 15 07 07 0e 00 00 63 04 08 0e 03 02 06 0e 00 40 63 04 0e 3c 0f 1d 23 3d 26 0c 04 05 00 28 32 00 00 00 32 00 4b 69 63 6b 4c 65 61 64 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mute Gtr ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 12 1f 14 1f 03 0e 0a 02 4e 13 0b 1f 16 0a 08 0e 1e 00 52 04 1b 1f 10 09 09 0e 00 01 63 04 1b 1f 11 09 08 0e 00 01 63 04 1b 7c 1c 00 16 00 32 0c 02 04 00 28 32 00 00 00 32 00 4d 75 74 65 20 47 74 72 20 20 63 63 63 32 32 32 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mute Gtr ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 14 1f 03 0e 0d 02 4e 13 0b 1f 16 0d 08 0e 1e 00 52 04 1b 1f 10 09 09 0e 00 01 63 04 1b 1f 11 09 08 0e 00 01 63 04 1b 7c 1c 00 16 00 32 0c 02 04 00 28 32 00 00 00 32 00 4d 75 74 65 20 47 74 72 20 20 63 63 63 32 32 32 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: NastyLead1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 0d 09 08 0f 00 04 61 04 08 0f 04 11 08 0c 00 04 5f 04 09 1f 15 08 08 0f 00 05 63 04 08 0f 15 06 08 0c 00 05 63 04 0e 6c 23 00 00 00 02 18 05 04 00 63 63 63 00 00 32 00 4e 61 73 74 79 4c 65 61 64 31 63 63 63 32 32 32 00 00 00 40 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: NastyLead1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 0d 09 08 0f 00 02 61 04 08 0f 04 11 08 0c 00 01 5f 04 09 1f 15 08 08 0f 00 00 63 04 08 0f 15 06 08 0c 00 00 63 04 0e 6c 23 00 00 00 02 18 05 04 00 63 63 63 00 00 32 00 4e 61 73 74 79 4c 65 61 64 31 63 63 63 32 32 32 00 00 00 40 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: NastyLead2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 0d 09 08 0f 00 04 62 08 00 0f 0e 05 08 07 00 05 62 04 01 1f 15 08 08 0f 00 05 63 08 00 0f 16 06 08 0e 00 05 63 10 06 6c 26 00 03 00 42 18 05 04 00 63 63 63 00 00 32 00 4e 61 73 74 79 4c 65 61 64 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: NastyLead2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 0d 09 08 0f 00 02 62 08 00 0f 0e 05 08 07 00 02 62 04 01 1f 15 08 08 0f 00 00 63 08 00 0f 16 06 08 0e 00 00 63 10 06 6c 26 00 03 00 42 18 05 04 00 63 63 63 00 00 32 00 4e 61 73 74 79 4c 65 61 64 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Nuln Guit3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0a 04 0c 09 63 01 48 1f 0b 19 1e 09 09 09 00 03 51 0d 1c 1d 0a 04 0c 03 00 05 5d 0d 1b 15 03 06 09 0f 0a 05 5c 04 0b 3c 23 00 1b 00 22 00 04 04 00 63 32 00 00 00 32 00 4e 75 6c 6e 20 47 75 69 74 33 63 63 63 32 32 32 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Nuln Guit3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0a 04 0c 09 63 01 48 1f 0b 19 1e 09 09 09 00 01 51 0d 1c 1d 0a 04 0c 03 00 00 5d 0d 1b 15 03 06 09 0f 0a 00 5c 04 0b 3c 23 00 1b 00 22 00 04 04 00 63 32 00 00 00 32 00 4e 75 6c 6e 20 47 75 69 74 33 63 63 63 32 32 32 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Nyln Guit1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0f 0c 06 0b 23 02 4a 1c 13 14 15 03 07 0f 27 02 4a 04 13 15 0a 09 06 0f 00 04 43 16 03 18 04 04 06 0e 00 04 63 04 0b 3c 23 00 1b 00 22 0c 04 04 00 63 32 00 00 00 32 00 4e 79 6c 6e 20 47 75 69 74 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Nyln Guit1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0f 0c 06 0b 23 02 4a 1c 13 14 15 03 07 0f 27 01 4a 04 13 15 0a 09 06 0f 00 00 43 16 03 18 04 04 06 0e 00 00 63 04 0b 3c 23 00 1b 00 22 0c 04 04 00 63 32 00 00 00 32 00 4e 79 6c 6e 20 47 75 69 74 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Nyln Guit2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0f 0c 06 05 23 02 4f 10 13 1c 1e 03 07 04 00 05 42 3f 14 1f 19 03 07 04 00 05 60 04 1b 18 04 03 07 04 00 04 5f 04 0b 21 23 00 1b 00 22 0c 04 04 00 63 32 00 00 00 32 00 4e 79 6c 6e 20 47 75 69 74 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Nyln Guit2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0f 0c 06 05 23 02 4f 10 13 1c 1e 03 07 04 00 01 42 3f 14 1f 19 03 07 04 00 01 60 04 1b 18 04 03 07 04 00 00 5f 04 0b 21 23 00 1b 00 22 0c 04 04 00 63 32 00 00 00 32 00 4e 79 6c 6e 20 47 75 69 74 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Nylon * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 08 04 0b 2f 03 4d 0d 13 1c 1f 03 05 0f 2c 02 36 03 09 1b 05 07 04 0a 29 04 4c 0d 16 15 13 08 05 0e 11 03 63 04 0a 3b 1f 00 00 00 32 11 02 04 00 28 63 00 00 00 32 00 4e 79 6c 6f 6e 20 20 20 20 2a 63 63 63 32 32 32 00 08 00 06 00 08 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: NylonGuit ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 1 1f 09 00 06 00 27 02 51 19 0b 1c 06 00 08 00 37 02 47 0a 03 1f 0a 00 09 00 3d 02 4d 0a 0e 1f 12 08 08 0f 00 03 61 04 0b 28 1f 00 03 00 52 0c 03 04 00 63 32 01 32 00 32 00 4e 79 6c 6f 6e 47 75 69 74 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntGuitar ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0c 07 0a 0d 63 02 50 19 1b 1b 0c 07 0a 0d 63 02 4f 00 1b 1b 12 07 0a 0d 00 05 5b 1f 13 1b 12 07 0a 0d 00 05 62 08 15 04 23 00 00 00 62 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 47 75 69 74 61 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntGuitar ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 0c 07 0a 0d 63 00 50 19 1b 1b 0c 07 0a 0d 63 00 4f 00 1b 1b 12 07 0a 0d 00 00 5b 1f 13 1b 12 07 0a 0d 00 00 62 08 15 04 23 00 00 00 62 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 47 75 69 74 61 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ped Stl..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 0f 09 06 0e 39 02 51 04 06 1c 09 0a 08 0e 00 03 59 0a 01 1f 09 09 08 0f 00 01 63 08 05 18 0c 09 07 0f 00 04 63 04 02 76 22 00 00 00 22 18 03 04 00 63 63 00 00 00 32 00 50 65 64 20 53 74 6c 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ped Stl..1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 13 0f 09 06 0e 39 02 51 04 06 1c 09 0a 08 0e 00 00 59 0a 01 1f 09 09 08 0f 00 00 63 08 05 18 0c 09 07 0f 00 00 63 04 02 76 22 00 00 00 22 18 03 04 00 63 63 00 00 00 32 00 50 65 64 20 53 74 6c 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ped Stl..2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 0f 09 06 0e 39 03 45 13 06 1c 09 0a 08 0e 00 02 63 08 01 1f 09 09 08 0f 00 05 63 04 05 0e 0c 07 08 0f 00 04 63 04 0a 76 22 00 00 00 22 18 03 04 00 63 63 00 00 00 32 00 50 65 64 20 53 74 6c 2e 2e 32 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ped Stl..2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 13 0f 09 06 0e 39 02 45 13 06 1c 09 0a 08 0e 00 00 63 08 01 1f 09 09 08 0f 00 00 63 04 05 0e 0c 07 08 0f 00 00 63 04 0a 76 22 00 00 00 22 18 03 04 00 63 63 00 00 00 32 00 50 65 64 20 53 74 6c 2e 2e 32 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ped Stl..3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 0f 09 06 0e 39 02 3c 08 06 1c 09 0a 08 0e 00 03 63 08 01 1f 09 09 08 0f 00 03 63 04 05 0e 0c 07 08 0f 00 06 63 04 0a 7d 22 00 00 00 22 18 03 04 00 63 63 00 00 00 32 00 50 65 64 20 53 74 6c 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ped Stl..3 ** Source: from a library of patches collected by Duane Bowker, author unknown 13 0f 09 06 0e 39 00 3c 08 06 1c 09 0a 08 0e 00 00 63 08 01 1f 09 09 08 0f 00 00 63 04 05 0e 0c 07 08 0f 00 00 63 04 0a 7d 22 00 00 00 22 18 03 04 00 63 63 00 00 00 32 00 50 65 64 20 53 74 6c 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PedalSteel ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 10 00 0c 00 27 02 3c 24 0b 1f 10 02 0a 0e 37 02 5d 04 0c 1b 06 05 0b 0d 3d 02 5a 0a 0b 1b 09 06 0b 0d 00 06 63 04 0b 03 1d 16 09 00 52 18 02 04 00 63 19 00 0a 00 32 00 50 65 64 61 6c 53 74 65 65 6c 63 63 63 32 32 32 00 40 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PercLead.1 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 0d 00 0a 0b 06 44 55 08 1b 1f 08 00 0d 0c 05 06 40 00 0b 1f 1f 00 0d 0f 0d 06 53 04 1b 1f 1f 00 0d 0f 00 45 5e 04 03 11 23 35 00 10 46 18 0c 04 00 63 32 32 00 00 32 00 50 65 72 63 4c 65 61 64 2e 31 63 63 63 32 32 32 00 00 00 00 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PercLead.1 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 0d 00 0a 0b 06 41 55 08 1b 1f 08 00 0d 0c 05 01 40 00 0b 1f 1f 00 0d 0f 0d 01 53 04 1b 1f 1f 00 0d 0f 00 40 5e 04 03 11 23 35 00 10 46 18 0c 04 00 63 32 32 00 00 32 00 50 65 72 63 4c 65 61 64 2e 31 63 63 63 32 32 32 00 00 00 00 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PercLead.2 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 0d 00 0a 0b 06 46 63 04 1b 1f 08 00 0d 0c 05 04 5e 0a 0b 1f 1f 00 0d 0f 0d 06 52 04 1b 1f 1f 00 0d 0f 00 44 57 04 03 25 23 35 00 10 46 18 0c 04 00 63 32 32 00 00 32 00 50 65 72 63 4c 65 61 64 2e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PercLead.2 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 0d 00 0a 0b 06 42 63 04 1b 1f 08 00 0d 0c 05 00 5e 0a 0b 1f 1f 00 0d 0f 0d 00 52 04 1b 1f 1f 00 0d 0f 00 40 57 04 03 25 23 35 00 10 46 18 0c 04 00 63 32 32 00 00 32 00 50 65 72 63 4c 65 61 64 2e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PhasedGuit ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 0e 0e 0f 0f 00 03 42 2d 06 16 0f 04 0f 0f 20 03 60 00 00 1c 12 06 0c 0e 14 03 5a 00 08 1f 13 07 08 0e 06 04 62 04 0e 3b 1d 00 00 00 42 18 02 04 00 63 32 00 32 00 32 00 50 68 61 73 65 64 47 75 69 74 63 63 63 32 32 32 10 30 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Piknylon * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 1e 1f 07 00 00 03 5a 28 03 1d 08 1f 05 0a 21 02 43 0a 08 1d 0d 08 02 0c 18 02 53 0a 15 1d 0a 0c 05 0a 00 03 63 04 04 02 1e 0a 09 00 32 18 02 04 00 28 63 00 00 00 32 00 50 69 6b 6e 79 6c 6f 6e 20 2a 63 63 63 32 32 32 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Power Lead ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 07 00 01 00 2e 03 5c 08 13 1f 0c 06 09 0f 4b 06 4d 00 13 1f 0d 03 09 04 46 05 51 1c 0b 1f 12 06 09 0e 0e 05 63 04 0b 2a 23 00 00 00 02 0c 02 04 00 28 02 00 00 00 32 00 50 6f 77 65 72 20 4c 65 61 64 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Power Lead ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 07 00 01 00 2e 01 5c 08 13 1f 0c 06 09 0f 4b 01 4d 00 13 1f 0d 03 09 04 46 01 51 1c 0b 1f 12 06 09 0e 0e 00 63 04 0b 2a 23 00 00 00 02 0c 02 04 00 28 02 00 00 00 32 00 50 6f 77 65 72 20 4c 65 61 64 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PowerChord ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 08 00 03 07 20 01 52 00 06 1c 03 00 02 0a 1b 01 4c 00 02 15 05 04 03 0a 1b 00 46 0a 05 17 0f 00 08 0f 00 40 50 04 03 3a 1e 16 02 32 46 18 07 04 00 63 32 00 00 00 32 00 50 6f 77 65 72 43 68 6f 72 64 63 63 63 32 32 32 00 00 00 50 00 50 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RecLead ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 1f 00 06 0f 5c 01 48 0d 03 16 02 06 03 0d 00 01 5c 0d 06 15 09 09 0c 0d 00 05 63 0d 0b 12 09 09 08 0d 00 05 5f 0d 0b 0c 00 02 00 00 22 00 02 04 00 28 02 00 00 00 32 00 52 65 63 4c 65 61 64 20 20 20 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RecLead ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 13 1f 00 06 0f 5c 01 48 0d 03 16 02 06 03 0d 00 01 5c 0d 06 15 09 09 0c 0d 00 00 63 0d 0b 12 09 09 08 0d 00 00 5f 0d 0b 0c 00 02 00 00 22 00 02 04 00 28 02 00 00 00 32 00 52 65 63 4c 65 61 64 20 20 20 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 09 03 0d 0e 63 02 54 04 0e 1c 0a 02 0c 0b 00 03 41 1c 1c 1c 07 01 07 0e 00 05 5d 04 1b 1c 05 02 0a 0b 0a 05 5c 04 0d 03 23 20 00 00 32 00 04 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 31 63 63 63 32 32 32 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 09 03 0d 0e 63 01 54 04 0e 1c 0a 02 0c 0b 00 01 41 1c 1c 1c 07 01 07 0e 00 01 5d 04 1b 1c 05 02 0a 0b 0a 00 5c 04 0d 03 23 20 00 00 32 00 04 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 31 63 63 63 32 32 32 00 00 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 03 00 0a 0e 13 02 41 04 03 12 1f 00 0a 0f 00 03 46 08 02 12 1f 00 0a 0f 45 05 60 08 03 12 1f 00 0a 0f 01 05 60 08 03 1b 23 01 0a 00 62 0c 02 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 32 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 03 00 0a 0e 13 02 41 04 03 12 1f 00 0a 0f 00 01 46 08 02 12 1f 00 0a 0f 45 01 60 08 03 12 1f 00 0a 0f 01 00 60 08 03 1b 23 01 0a 00 62 0c 02 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 32 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0f 01 0c 0e 07 03 4e 00 1e 1c 1f 06 0f 0f 00 04 54 04 0c 1c 09 03 0f 0f 00 06 4e 04 03 1c 07 07 09 0b 0a 05 56 04 0d 29 23 20 00 00 32 18 04 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 33 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0f 01 0c 0e 07 01 4e 00 1e 1c 1f 06 0f 0f 00 01 54 04 0c 1c 09 03 0f 0f 00 01 4e 04 03 1c 07 07 09 0b 0a 00 56 04 0d 29 23 20 00 00 32 18 04 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 33 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0f 0a 0c 0e 00 03 41 1c 16 1c 1f 06 0f 0f 00 04 5b 00 0c 1c 09 03 0f 0f 00 05 4e 04 03 1c 07 07 09 0b 0a 05 60 04 0d 2c 23 20 00 00 32 18 04 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0f 0a 0c 0e 00 01 41 1c 16 1c 1f 06 0f 0f 00 01 5b 00 0c 1c 09 03 0f 0f 00 00 4e 04 03 1c 07 07 09 0b 0a 00 60 04 0d 2c 23 20 00 00 32 18 04 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 0f 06 07 0c 00 03 57 04 13 1c 1f 06 0f 0f 00 03 55 08 0c 1c 09 07 07 0c 00 05 5f 0a 0a 1c 07 07 09 0b 0a 05 5f 04 0d 2c 23 20 00 00 32 18 04 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 35 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 0f 06 07 0c 00 01 57 04 13 1c 1f 06 0f 0f 00 01 55 08 0c 1c 09 07 07 0c 00 00 5f 0a 0a 1c 07 07 09 0b 0a 00 5f 04 0d 2c 23 20 00 00 32 18 04 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 35 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 1f 04 07 0f 00 02 50 04 13 10 15 04 0f 0d 00 03 56 00 0c 10 1f 04 07 0f 00 05 63 0a 0a 18 04 03 09 0b 0a 05 63 04 15 24 23 20 00 00 32 18 04 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rock Guit6 ** Source: from a library of patches collected by Duane Bowker, author unknown 10 1f 04 07 0f 00 00 50 04 13 10 15 04 0f 0d 00 00 56 00 0c 10 1f 04 07 0f 00 00 63 0a 0a 18 04 03 09 0b 0a 00 63 04 15 24 23 20 00 00 32 18 04 04 00 63 32 00 00 00 32 00 52 6f 63 6b 20 47 75 69 74 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StadiumSol ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 17 1f 08 00 03 0f 20 01 4e 00 06 1f 03 00 02 0f 1b 01 48 0d 02 1f 05 04 03 0f 1b 00 3c 2a 05 1e 0f 00 09 0f 00 00 5c 04 03 3a 1f 16 05 00 62 18 07 0c 00 63 32 00 00 00 32 00 53 74 61 64 69 75 6d 53 6f 6c 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Solo 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 23 17 0f 00 0a 0c 00 00 35 0a 06 18 0e 00 0a 0c 00 00 3f 04 06 16 00 00 0a 0f 05 00 53 04 06 18 05 01 0a 0a 00 04 5c 04 00 3b 20 14 0a 00 42 0c 02 04 00 28 46 00 00 00 32 00 53 79 2e 53 6f 6c 6f 20 20 31 63 63 63 32 32 32 00 60 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Solo 1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 17 0f 00 0a 0c 00 00 35 0d 06 18 0e 00 0a 0c 00 00 3f 04 06 16 00 00 0a 0f 05 00 53 04 06 18 05 01 0a 0d 00 04 5c 04 00 3b 20 14 0a 00 42 0c 02 04 00 28 46 00 00 00 32 00 53 79 2e 53 6f 6c 6f 20 20 31 63 63 63 32 32 32 00 60 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Solo 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 24 1f 19 00 08 0b 00 00 49 04 06 1b 0a 00 0c 0c 00 02 63 08 00 1b 04 00 0c 0a 00 02 63 04 06 1f 04 00 0c 0e 00 02 63 04 06 3d 26 28 14 00 42 18 02 04 00 28 4b 00 00 00 32 00 53 79 2e 53 6f 6c 6f 20 20 32 63 63 63 32 32 32 00 20 00 50 00 40 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Solo 2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 19 00 08 0b 00 00 49 04 06 1b 0a 00 0c 0c 00 02 63 08 00 1b 04 00 0c 0d 00 02 63 04 06 1f 04 00 0c 0e 00 02 63 04 06 3d 26 28 14 00 42 18 02 04 00 28 4b 00 00 00 32 00 53 79 2e 53 6f 6c 6f 20 20 32 63 63 63 32 32 32 00 20 00 50 00 40 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Solo 3 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank A, voice 25 1f 0a 00 04 0a 07 01 4f 04 0b 1f 0a 00 07 0b 00 00 2d 04 0b 1f 0b 00 05 0b 00 00 4d 04 0b 16 1f 00 0a 0f 00 01 61 04 0b 3a 20 00 00 00 62 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 53 6f 6c 6f 20 20 33 63 63 63 32 32 32 00 30 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Solo 3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 00 04 0d 07 01 4f 04 0b 1f 0d 00 07 0b 00 00 2d 04 0b 1f 0b 00 05 0b 00 00 4d 04 0b 16 1f 00 0a 0f 00 01 61 04 0b 3a 20 00 00 00 62 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 53 6f 6c 6f 20 20 33 63 63 63 32 32 32 00 30 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Solo 4 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank A, voice 26 1f 0a 00 04 0a 07 01 4d 08 0b 1f 0a 00 07 0b 00 00 35 08 0b 1f 0b 00 05 0b 00 00 45 04 0b 17 1f 00 0a 0f 00 01 61 04 0b 3a 20 00 00 00 62 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 53 6f 6c 6f 20 20 34 63 63 63 32 32 32 00 30 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Solo 4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 00 04 0d 07 01 4d 08 0b 1f 0d 00 07 0b 00 00 35 08 0b 1f 0b 00 05 0b 00 00 45 04 0b 17 1f 00 0a 0f 00 01 61 04 0b 3a 20 00 00 00 62 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 53 6f 6c 6f 20 20 34 63 63 63 32 32 32 00 30 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sync Lead ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 22 13 12 00 09 0d 22 00 51 00 0b 1f 14 00 05 08 24 40 5c 00 03 1f 11 00 06 0c 2c 00 53 09 00 10 1f 00 0b 0f 00 00 5c 0a 0e 3a 22 11 0e 1e 5e 18 07 04 00 63 32 00 32 00 32 00 53 79 6e 63 20 4c 65 61 64 20 63 63 63 32 32 32 00 70 00 40 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: TwangGuitr ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1a 00 03 01 00 04 52 2f 16 1f 05 00 08 00 00 02 50 04 10 1f 0f 06 09 0a 00 02 3e 35 10 1f 09 07 09 0d 00 05 63 10 0b 32 1e 09 02 00 52 0c 04 04 00 63 19 00 10 00 32 00 54 77 61 6e 67 47 75 69 74 72 63 63 63 32 32 32 10 54 15 50 10 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: TwelveStrg ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 3 1f 0d 08 0a 08 00 03 42 24 06 1f 1f 05 0b 0b 36 03 63 07 0e 1f 0f 06 0b 0f 13 03 63 00 10 1f 13 07 09 0e 00 04 63 04 0e 3b 14 00 00 00 42 18 02 04 00 63 32 00 32 00 32 00 54 77 65 6c 76 65 53 74 72 67 63 63 63 32 32 32 00 00 00 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: slowtacky ** Description: a biting electric guitar sound ** Controls: KV ** Source: GLIB (Michael Kesti and others) 1f 0a 06 0b 02 00 05 5f 08 02 1f 02 03 0b 0e 43 05 5f 00 10 1f 12 06 08 0e 00 05 5f 00 00 16 04 08 08 0c 00 05 5f 04 03 33 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 73 6c 6f 77 74 61 63 6b 79 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: slowtacky+ ** Description: 'slowtacky' (from GLIB) with added sustain ** Controls: KV ** Source: Art Hitzeman, based on a voice from GLIB (Michael Kesti and others) 1f 0a 06 07 02 00 05 5f 08 02 1f 02 03 07 0e 43 05 5f 00 10 1f 12 06 07 0e 00 05 5f 00 00 16 04 08 06 0c 00 05 5f 04 03 33 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 73 6c 6f 77 74 61 63 6b 79 2b 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 9 * SUBCATEGORY Harp ** Name: Dbl Harp.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0f 09 04 0b 27 04 5f 0a 04 1c 0f 02 01 00 2d 04 5a 08 02 1c 0a 09 06 0a 1d 04 63 04 01 11 1f 04 03 0c 00 05 63 04 1d 6c 22 21 00 00 12 18 02 04 00 63 32 00 00 01 32 00 44 62 6c 20 48 61 72 70 2e 31 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Dbl Harp.1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 0f 09 04 0b 27 01 5f 0a 04 1c 0f 02 01 00 2d 01 5a 08 02 1c 0a 09 06 0a 1d 00 63 04 01 11 1f 04 03 0c 00 01 63 04 1d 6c 22 21 00 00 12 18 02 04 00 63 32 00 00 01 32 00 44 62 6c 20 48 61 72 70 2e 31 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Dbl Harp.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 1b 07 06 00 63 05 5d 13 0c 1f 12 0c 04 0f 5a 04 53 0d 0a 1c 0a 07 05 00 00 04 4c 08 01 18 12 09 05 0f 00 05 5d 04 0d 38 1b 29 01 00 32 18 02 04 00 63 32 00 00 01 32 00 44 62 6c 20 48 61 72 70 2e 32 63 63 63 32 32 32 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Dbl Harp.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 1b 07 06 00 63 01 5d 13 0c 1f 12 0c 04 0f 5a 01 53 0d 0a 1c 0a 07 05 00 00 00 4c 08 01 13 12 09 05 0f 00 00 5d 04 0d 38 1b 29 01 00 32 18 02 04 00 63 32 00 00 01 32 00 44 62 6c 20 48 61 72 70 2e 32 63 63 63 32 32 32 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 00 01 00 07 02 50 08 0e 1f 12 00 05 00 00 03 4d 0a 10 1e 08 00 04 00 0b 03 63 04 10 1b 08 00 04 00 10 03 63 04 16 34 23 00 00 00 32 18 02 04 00 28 62 00 00 00 32 00 48 61 72 70 20 20 20 20 20 2a 63 63 63 32 32 32 00 30 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank B, voice 24 10 13 0f 04 0a 00 03 3b 14 14 1f 0e 08 07 09 00 02 42 05 0e 18 13 0d 09 0c 00 02 3c 13 08 1f 11 07 04 0f 00 42 63 04 10 3a 1b 00 00 00 52 1d 04 04 00 63 32 00 00 00 32 00 48 61 72 70 20 20 20 20 20 20 63 63 63 32 32 32 08 00 00 21 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 13 17 0e 0a 03 0e 00 04 56 04 08 17 0e 0a 03 0e 00 04 56 04 0e 1f 0c 07 05 0e 00 03 63 04 08 1f 0c 07 05 0e 00 03 63 04 0e 04 1b 15 0a 01 32 18 02 04 00 63 63 00 00 00 32 00 48 61 72 70 20 20 20 20 20 31 63 63 63 32 32 32 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp 1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 17 0e 0a 03 0e 00 04 56 04 08 17 0e 0a 03 0e 00 04 56 04 0e 1f 0c 07 05 0e 00 03 63 04 08 1f 0c 07 05 0e 00 03 63 04 0e 04 1b 15 0d 01 32 18 02 04 00 63 63 00 00 00 32 00 48 61 72 70 20 20 20 20 20 31 63 63 63 32 32 32 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 14 17 0b 0b 01 06 0a 42 3b 04 00 1c 13 00 01 00 0a 41 49 08 06 1f 13 00 01 00 00 42 43 16 06 19 0f 0b 05 0e 00 43 63 04 08 3a 14 00 06 08 4a 18 02 04 00 28 55 00 00 00 32 00 48 61 72 70 20 20 20 20 20 32 63 63 63 32 32 32 00 00 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp.....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 07 05 0a 07 0e 00 02 56 13 0c 1f 09 05 03 00 52 02 53 08 0b 1f 13 07 06 00 36 05 4b 10 03 19 07 00 04 00 1d 04 5c 04 0e 3b 1b 00 02 00 32 18 02 04 00 63 32 00 00 01 32 00 48 61 72 70 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 30 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp.....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 07 05 0a 07 0e 00 00 56 13 0c 1f 09 05 03 00 52 00 53 08 0b 1f 13 07 06 00 36 02 4b 10 03 19 07 00 04 00 1d 01 5c 04 0e 3b 1b 00 02 00 32 18 02 04 00 63 32 00 00 01 32 00 48 61 72 70 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 30 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp.....2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0f 09 03 0b 2d 03 53 0a 1c 1c 0d 0d 03 0a 2d 04 4d 04 0a 1c 0a 0a 04 0a 32 04 47 0a 09 1c 09 0d 04 0a 00 04 62 04 0d 58 22 21 00 00 12 18 02 04 00 63 32 00 00 01 32 00 48 61 72 70 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 40 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp.....2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 0f 09 03 0b 2d 00 53 0a 1c 1c 0d 0d 03 0a 2d 01 4d 04 0a 1c 0a 0a 04 0a 32 01 47 0a 09 1c 09 0d 04 0a 00 00 62 04 0d 58 22 21 00 00 12 18 02 04 00 63 32 00 00 01 32 00 48 61 72 70 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 40 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp....FM ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 10 06 06 00 63 01 5a 08 0b 1a 18 06 06 0f 5a 04 5b 04 00 1c 1f 1f 06 0f 00 04 4c 04 0d 13 06 00 06 0f 00 04 63 04 02 25 1b 23 0b 00 42 18 02 04 00 63 32 00 00 01 32 00 48 61 72 70 2e 2e 2e 2e 46 4d 63 63 63 32 32 32 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harp....FM ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 10 06 06 00 63 01 5a 08 0b 1a 18 06 06 0f 5a 01 5b 04 00 1c 1f 1f 06 0f 00 00 4c 04 0d 13 06 00 06 0f 00 00 63 04 02 25 1b 23 0b 00 42 18 02 04 00 63 32 00 00 01 32 00 48 61 72 70 2e 2e 2e 2e 46 4d 63 63 63 32 32 32 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Koto ** Name: Fuzzy Koto ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 8 16 02 00 04 06 09 01 4f 08 00 19 0e 01 04 00 20 01 43 19 00 1f 12 10 06 07 00 00 54 3d 03 16 0d 06 06 02 1e 03 61 08 0e 3a 20 01 14 00 42 0c 02 04 02 63 63 63 00 00 32 00 46 75 7a 7a 79 20 4b 6f 74 6f 63 63 63 32 32 32 00 00 00 00 0b 50 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Koto * ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 15 00 05 00 0f 02 56 0d 13 1c 04 03 03 0b 0d 01 4f 04 1e 1c 07 09 05 0d 23 01 53 0d 18 1a 14 08 04 0d 00 03 63 04 10 02 1a 08 1d 00 32 18 02 04 00 28 63 00 00 00 36 00 4b 6f 74 6f 20 20 20 20 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Koto ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 17 1f 12 0c 06 0b 1c 03 57 16 0b 1f 1f 08 01 0f 0b 01 47 04 0b 1f 1f 06 04 0f 25 01 4b 0a 0b 19 1f 0a 06 0f 00 02 5a 08 0b 00 1e 00 00 00 62 18 02 04 00 28 32 00 00 00 32 00 4b 6f 74 6f 20 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Koto ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 0c 06 0b 1c 03 57 16 0b 1f 1f 08 01 0f 0b 01 47 04 0b 1f 1f 06 04 0f 25 01 4b 0a 0b 19 1f 0a 06 0f 00 02 5a 08 0b 00 1e 00 00 00 62 18 02 04 00 28 32 00 00 00 32 00 4b 6f 74 6f 20 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Koto.....1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 09 07 0c 11 02 52 0d 16 1b 1f 1d 05 0d 00 02 63 04 10 1c 0d 05 05 0c 19 05 63 04 16 1f 11 08 05 0f 00 46 5a 00 10 1c 1f 00 00 00 62 18 02 04 00 63 32 00 32 00 32 00 4b 6f 74 6f 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Koto.....1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 09 07 0c 11 00 52 0d 16 1b 1f 1d 05 0d 00 00 63 04 10 1c 0d 05 05 0c 19 00 63 04 16 1f 11 08 05 0f 00 40 5a 00 10 1c 1f 00 00 00 62 18 02 04 00 63 32 00 32 00 32 00 4b 6f 74 6f 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Koto.....2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 09 07 0c 11 41 47 0d 13 1b 1f 1d 05 0d 00 43 63 0a 13 1c 0d 05 05 0c 19 45 63 04 13 1f 11 08 05 0f 00 45 5a 04 13 5c 1e 00 11 14 26 18 02 04 00 63 32 00 32 00 32 00 4b 6f 74 6f 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Koto.....2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 09 07 0c 11 40 47 0d 13 1b 1f 1d 05 0d 00 40 63 0a 13 1c 0d 05 05 0c 19 40 63 04 13 1f 11 08 05 0f 00 40 5a 04 13 5c 1e 00 11 14 26 18 02 04 00 63 32 00 32 00 32 00 4b 6f 74 6f 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Koto.....3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 09 07 0c 11 42 47 0d 13 1f 0a 09 07 0c 11 43 4a 0d 13 1c 0d 05 05 0c 19 45 63 04 13 1c 0d 05 05 0c 19 47 5a 04 13 5c 1e 00 11 14 26 18 02 04 00 63 32 00 32 00 32 00 4b 6f 74 6f 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Koto.....3 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 09 07 0c 11 40 47 0d 13 1f 0a 09 07 0c 11 40 4a 0d 13 1c 0d 05 05 0c 19 40 63 04 13 1c 0d 05 05 0c 19 40 5a 04 13 5c 1e 00 11 14 26 18 02 04 00 63 32 00 32 00 32 00 4b 6f 74 6f 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Lute ** Name: OrntLute.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 07 04 05 08 00 04 52 08 0b 0c 07 1f 04 07 00 42 3e 00 0c 19 07 1f 04 07 00 04 63 04 0c 18 07 09 05 0e 00 45 63 04 0b 74 34 00 00 0a 06 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 4c 75 74 65 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntLute.1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 07 04 05 08 00 00 52 08 0b 0c 07 1f 04 07 00 40 3e 00 0c 19 07 1f 04 07 00 00 63 04 0c 18 07 09 05 0e 00 40 63 04 0b 74 34 00 00 0a 06 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 4c 75 74 65 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntLute.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 03 00 05 0f 00 42 59 00 0b 1f 0c 1f 05 08 00 43 46 00 0a 0c 0c 1f 04 07 00 44 63 00 0c 15 0c 09 05 0c 00 46 61 04 0b 6c 34 00 00 0a 06 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 4c 75 74 65 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntLute.2 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 03 00 05 0f 00 40 59 00 0b 1f 0c 1f 05 08 00 40 46 00 0a 0c 0c 1f 04 07 00 40 63 00 0c 15 0c 09 05 0c 00 40 61 04 0b 6c 34 00 00 0a 06 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 4c 75 74 65 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Mandolin ** Name: Mandolin.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 19 0d 05 0c 02 04 62 2d 1b 1f 09 06 02 0b 00 04 45 0a 0b 17 0c 09 07 00 00 06 63 04 0a 16 0c 09 07 0b 00 05 63 04 1c 74 2c 63 04 00 22 18 02 04 00 63 32 00 32 00 32 00 4d 61 6e 64 6f 6c 69 6e 2e 31 63 63 63 32 32 32 00 00 00 30 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mandolin.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 19 0d 05 0c 02 02 62 2d 1b 1f 09 06 02 0b 00 02 45 0a 0b 17 0c 09 07 00 00 00 63 04 0a 16 0c 09 07 0b 00 00 63 04 1c 74 2c 63 04 00 22 18 02 04 00 63 32 00 32 00 32 00 4d 61 6e 64 6f 6c 69 6e 2e 31 63 63 63 32 32 32 00 00 00 30 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mandolin.2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 18 0d 05 0c 02 02 3f 2d 1b 1f 0c 06 04 0b 00 05 37 0a 0b 1f 0d 07 05 0f 00 05 5f 04 0a 16 0c 09 05 0b 00 05 5e 04 1c 3c 46 10 01 00 20 18 02 04 00 63 32 00 32 00 32 00 4d 61 6e 64 6f 6c 69 6e 2e 32 63 63 63 32 32 32 00 40 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mandolin.2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 18 0d 05 0c 02 01 3f 2d 1b 1f 0c 06 04 0b 00 01 37 0a 0b 1f 0d 07 05 0f 00 00 5f 04 0a 16 0c 09 05 0b 00 00 5e 04 1c 3c 46 10 01 00 20 18 02 04 00 63 32 00 32 00 32 00 4d 61 6e 64 6f 6c 69 6e 2e 32 63 63 63 32 32 32 00 40 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Sitar ** Name: Sitar * ** Controls: KV, BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 00 00 04 03 1e 0c 0b 01 0f 00 02 55 16 09 11 15 00 02 0f 00 03 5b 04 08 1f 18 06 04 0f 00 01 63 04 0b 03 1d 00 00 00 62 18 01 04 00 28 0d 00 00 00 33 00 53 69 74 61 72 20 20 20 20 2a 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sitar ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 29 17 0e 09 01 0e 02 00 47 04 1b 1f 06 03 01 05 00 00 4b 08 16 1f 06 04 01 07 00 00 5e 04 16 1f 05 08 01 07 00 01 5f 08 18 3b 21 00 05 00 42 18 02 04 00 63 32 00 32 00 32 00 53 69 74 61 72 20 20 20 20 20 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sitar....1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 07 06 01 0d 52 03 51 16 0a 1f 0c 09 01 0e 27 02 58 04 0b 1f 06 06 04 0e 11 06 5b 04 13 1f 18 09 04 0e 00 05 49 37 0b 04 1c 00 00 00 22 18 01 04 00 63 00 63 00 00 32 00 53 69 74 61 72 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sitar....1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 07 06 01 0d 52 00 51 16 0a 1f 0c 09 01 0e 27 00 58 04 0b 1f 06 06 04 0e 11 00 5b 04 13 1f 18 09 04 0e 00 00 49 37 0b 04 1c 00 00 00 22 18 01 04 00 63 00 63 00 00 32 00 53 69 74 61 72 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sitar....2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 17 0e 09 04 0e 02 01 43 13 13 1f 06 03 03 05 00 02 3b 1c 16 1f 06 04 02 07 00 05 52 04 16 1f 05 08 01 07 00 05 60 00 18 13 21 00 05 00 42 18 02 04 00 63 32 00 32 00 32 00 53 69 74 61 72 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sitar....2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 17 0e 09 04 0e 02 00 43 13 13 1f 06 03 03 05 00 00 3b 1c 16 1f 06 04 02 07 00 00 52 04 16 1f 05 08 01 07 00 00 60 00 18 13 21 00 05 00 42 18 02 04 00 63 32 00 32 00 32 00 53 69 74 61 72 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sitar....3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 12 07 02 05 02 02 43 19 0c 1b 1c 0a 04 08 00 02 5b 1c 02 0f 0c 0a 02 0e 00 05 52 13 1c 1f 12 06 05 0f 00 05 60 00 0b 5a 2c 63 04 00 22 18 02 04 00 63 32 00 32 00 32 00 53 69 74 61 72 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sitar....3 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 12 07 02 05 02 00 43 19 0c 1b 1c 0a 04 08 00 00 5b 1c 02 0f 0c 0a 02 0e 00 00 52 13 1c 1f 12 06 05 0f 00 00 60 00 0b 5a 2c 63 04 00 22 18 02 04 00 63 32 00 32 00 32 00 53 69 74 61 72 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sitar.Vib ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 12 07 04 04 02 03 43 13 0b 1b 1d 09 03 09 00 02 3b 1c 06 0f 0c 09 05 0c 00 05 52 04 1e 1f 12 05 05 0f 00 07 60 00 00 13 21 00 05 00 42 18 02 04 00 63 32 00 32 00 32 00 53 69 74 61 72 2e 56 69 62 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sitar.Vib ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 12 07 04 04 02 00 43 13 0b 1b 1d 09 03 09 00 00 3b 1c 06 0f 0c 09 05 0c 00 00 52 04 1e 1f 12 05 05 0f 00 00 60 00 00 13 21 00 05 00 42 18 02 04 00 63 32 00 32 00 32 00 53 69 74 61 72 2e 56 69 62 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Sitar ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 31 1e 12 1f 03 09 00 00 41 3c 03 1d 0f 02 01 0f 00 00 5b 00 03 1e 0f 1f 02 0f 3b 01 57 02 03 1e 1f 0c 04 0f 00 02 5d 10 03 3a 17 00 03 00 5a 18 02 04 00 63 32 00 00 00 32 00 53 79 2e 53 69 74 61 72 20 20 63 63 63 32 32 32 00 10 00 00 00 76 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Viola ** Name: Viola 5th ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 06 00 06 0f 0a 01 4c 0a 15 0f 07 00 06 0e 32 04 4e 08 0b 0b 07 06 07 0f 0a 06 57 13 0c 0d 06 06 08 0d 00 03 5f 08 03 74 22 23 0b 00 12 18 04 04 00 63 32 63 00 00 32 00 56 69 6f 6c 61 20 35 74 68 20 63 63 63 32 32 32 00 40 00 20 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Viola 5th ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 06 00 06 0f 0a 01 54 0a 15 10 07 00 06 0e 32 01 4e 0d 0b 0a 07 06 07 0f 0a 00 60 0a 0c 0d 06 06 08 0d 00 00 5f 04 03 74 22 23 0b 00 12 18 04 04 00 63 32 63 00 00 32 00 56 69 6f 6c 61 20 35 74 68 20 63 63 63 32 32 32 00 00 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Viola....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 06 03 08 0e 00 41 4c 08 0d 1b 0d 07 07 0d 0a 44 43 19 04 0d 06 01 07 0f 00 45 60 08 03 0f 06 09 09 0d 00 45 60 08 04 7c 20 38 32 00 26 0c 04 04 00 63 32 63 00 00 32 00 56 69 6f 6c 61 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 40 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Viola....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 06 03 08 0e 00 41 4c 08 0d 1f 0d 07 07 0d 0a 41 43 19 04 0d 06 01 07 0f 00 40 60 08 03 0d 06 09 09 0d 00 41 60 13 04 7c 20 38 0e 00 26 0c 04 04 00 63 32 63 00 00 32 00 56 69 6f 6c 61 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Viola....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 04 06 07 0d 00 42 4e 04 03 0e 09 0d 07 03 0a 45 4f 08 14 0c 07 05 06 04 00 46 5c 04 03 0d 06 06 05 0d 00 43 63 04 04 35 1e 2e 26 00 36 0c 04 04 00 63 32 63 00 00 32 00 56 69 6f 6c 61 2e 2e 2e 2e 32 63 63 63 32 32 32 00 10 00 10 00 40 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Viola....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 04 06 07 0d 00 42 53 04 03 0e 09 0d 07 03 0a 41 4f 0d 14 0f 15 07 06 0d 00 40 63 04 03 0f 06 06 05 0d 00 41 63 04 04 34 1c 2e 1e 00 36 0c 04 04 00 63 32 63 00 00 32 00 56 69 6f 6c 61 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ViolaSynth ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 06 02 07 0f 00 03 4a 04 0b 0f 07 09 07 07 00 03 63 00 03 0e 09 10 0d 07 00 06 5c 04 03 0f 07 09 07 07 00 05 63 04 03 7d 23 0f 0b 00 42 18 02 04 00 63 63 00 00 00 32 00 56 69 6f 6c 61 53 79 6e 74 68 63 63 63 32 32 32 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ViolaSynth ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 06 02 07 0f 00 01 4a 04 0b 0f 07 09 07 07 00 00 63 00 03 0e 09 10 0d 07 00 00 5c 04 03 0f 07 09 07 07 00 00 63 04 03 7d 23 0f 0b 00 42 18 02 04 00 63 63 00 00 00 32 00 56 69 6f 6c 61 53 79 6e 74 68 63 63 63 32 32 32 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 10 * SUBCATEGORY Violin ** Name: SoloViolin ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 20 1b 05 00 07 0e 0c 01 4e 08 04 19 0b 06 06 0f 49 02 3b 0d 03 1c 11 10 0d 07 00 03 4b 35 03 0b 0a 00 06 0f 02 02 63 08 13 3a 1e 06 06 00 52 0c 04 04 00 63 32 00 00 00 32 00 53 6f 6c 6f 56 69 6f 6c 69 6e 63 63 63 32 32 32 00 00 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sradiv. * ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 0f 09 0f 2a 05 31 20 05 12 09 02 06 0f 10 00 51 08 0b 1f 0e 07 03 0a 14 02 4d 19 08 0e 05 05 09 0e 00 06 63 08 0a 00 20 00 0e 00 42 0c 02 04 00 28 63 00 00 00 31 00 53 72 61 64 69 76 2e 20 20 2a 63 63 63 32 32 32 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin 1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 5 16 07 00 06 0e 00 41 4e 04 0b 15 08 00 03 0b 00 02 3a 04 0b 19 08 00 08 0c 10 05 5f 0a 14 0e 0e 00 08 0a 00 02 60 08 0b 3a 20 0a 08 00 62 18 02 05 00 63 0a 50 32 00 32 00 56 69 6f 6c 69 6e 20 20 20 31 63 62 63 30 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin 1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 16 07 00 06 0e 00 41 4e 04 0b 15 08 00 03 0b 00 02 3a 04 0b 19 08 00 08 0c 10 05 5f 0a 14 0e 0e 00 08 0d 00 02 60 08 0b 3a 20 0a 08 00 62 18 02 05 00 63 0a 50 32 00 32 00 56 69 6f 6c 69 6e 20 20 20 31 63 62 63 30 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 6 1f 02 00 04 0e 0f 00 45 04 0b 0f 0a 00 04 0a 00 02 62 04 0b 0a 04 00 07 0f 09 02 5c 08 0b 0c 09 00 08 0a 08 02 58 0a 0b 3c 1f 13 3d 00 42 18 02 04 00 63 32 00 00 00 32 00 56 69 6f 6c 69 6e 20 20 20 32 63 54 63 2e 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin 2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 02 00 04 0e 0f 00 45 04 0b 0f 0a 00 04 0d 00 02 62 04 0b 0d 04 00 07 0f 09 02 5c 08 0b 0c 09 00 08 0d 08 02 58 0d 0b 3c 1f 13 3d 00 42 18 02 04 00 63 32 00 00 00 32 00 56 69 6f 6c 69 6e 20 20 20 32 63 54 63 2e 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 06 03 02 0f 0a 42 4e 04 10 0c 00 00 07 0e 00 7c 4b 04 04 0b 03 06 05 0e 0a 45 61 04 0d 0c 00 00 07 0e 00 7f 63 04 06 6c 1e 14 18 00 36 18 04 04 00 63 32 63 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 30 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 06 03 02 0f 0a 40 4c 04 16 15 00 00 06 0f 0a 43 55 04 05 0e 05 06 05 0e 0a 40 63 04 0b 0f 00 00 06 0e 00 40 63 08 03 6c 1c 00 18 00 36 18 04 04 00 63 32 63 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 03 00 03 0e 00 28 50 04 05 0b 0d 00 03 0e 00 3d 49 0a 14 0a 08 03 07 0f 13 34 63 04 0b 0a 0c 03 06 0f 13 3f 63 04 09 24 1c 15 0e 00 42 18 02 04 00 63 15 27 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 32 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 05 00 03 0e 00 00 50 08 06 0f 02 00 03 0e 00 01 3b 19 14 0c 04 03 07 0f 13 01 63 08 08 0a 01 03 06 0f 13 01 63 00 0b 2c 25 2c 0e 00 22 0c 02 04 00 63 15 27 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 06 07 01 0a 14 01 4e 04 15 0d 08 05 02 08 14 07 44 04 1c 0b 08 08 04 06 0a 03 60 08 0a 0c 03 03 04 0f 0a 05 63 04 0b 6c 1f 1c 13 12 42 18 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 33 63 63 63 32 32 32 00 20 00 20 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 04 02 01 0e 14 02 5c 08 05 1f 0f 08 02 0d 14 01 42 19 04 0d 06 03 02 0d 0a 00 60 08 0a 10 05 03 08 0e 0a 02 63 08 0b 64 23 00 07 00 32 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 50 00 00 00 20 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 04 05 06 07 2a 02 58 04 08 0a 04 10 05 0d 14 06 37 08 0d 08 04 09 05 06 00 07 63 04 13 0a 06 06 07 0e 0a 05 63 04 0a 6c 1f 19 2e 00 32 18 04 04 00 63 32 00 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 34 63 63 63 32 32 32 00 20 02 10 00 00 06 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...4 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 11 00 00 04 0e 2a 01 53 08 08 10 04 10 05 0d 14 01 37 19 0d 0c 07 06 04 0f 00 00 63 08 13 0d 06 06 07 0e 0a 00 63 04 0a 6c 23 00 0b 00 12 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 34 63 63 63 32 32 32 00 00 02 10 00 00 06 10 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 05 04 06 0f 14 05 50 08 00 11 0c 07 06 0a 14 04 50 08 14 0a 04 04 07 0f 00 04 63 08 14 0c 07 04 07 0f 00 03 63 08 13 6c 22 27 34 32 12 0c 04 04 00 63 32 00 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 10 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 04 06 0f 14 02 53 08 00 1f 0d 07 06 0f 14 02 50 08 14 0c 04 04 07 0f 00 01 63 13 14 0c 04 04 07 0f 00 00 63 13 13 74 24 27 00 00 12 0c 06 04 00 63 32 63 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...6 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 05 04 06 0f 14 04 53 08 00 11 0d 05 06 0f 14 03 53 08 14 0a 0b 04 07 0b 00 04 63 08 12 0a 0a 03 07 0f 00 04 63 08 16 6c 1a 27 19 19 42 0c 06 04 00 63 32 63 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 36 63 63 63 32 32 32 00 30 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 04 06 0f 14 02 53 08 00 1f 0d 07 06 0f 14 01 53 13 14 0c 04 04 07 0f 00 00 63 13 14 0c 04 04 07 0f 00 00 63 0d 13 74 24 27 00 00 12 0c 06 04 00 63 32 63 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 05 0e 2a 01 50 08 0b 0e 04 04 05 08 14 03 55 08 0a 0c 07 06 07 0f 00 04 62 08 0c 0a 08 09 08 0a 0a 04 63 08 04 6c 24 27 00 00 22 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 37 63 63 63 32 32 32 00 30 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 05 0e 2a 01 57 08 0e 1f 0c 05 05 0d 14 01 4b 19 0c 0c 07 06 07 0f 00 00 62 0d 0c 0f 06 06 08 06 0a 00 63 08 03 6c 24 27 00 00 12 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 37 63 63 63 32 32 32 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 05 04 06 07 2a 01 52 08 0e 15 07 07 05 05 14 01 51 08 03 0c 06 08 07 0a 0a 00 63 08 0d 0a 05 0a 08 07 0a 01 63 08 02 24 23 27 00 00 12 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 38 63 63 63 32 32 32 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...8 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 04 06 0f 2a 01 5b 08 0e 1f 0d 10 06 0e 14 01 51 19 03 0c 06 06 08 0e 0a 00 63 0d 0b 0f 05 06 08 0c 0a 01 63 08 02 24 23 27 00 00 12 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...9 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 05 00 06 0f 32 02 57 08 0e 1f 0c 0c 0a 0e 14 03 49 0d 05 0c 06 06 08 0e 0a 00 60 08 04 0c 06 06 08 0e 0a 06 63 08 04 6c 1d 17 40 00 22 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 39 63 63 63 32 32 32 00 30 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin...9 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 00 06 0f 32 02 5e 08 0e 1f 0c 0c 0a 0e 14 01 4a 13 05 0c 06 06 08 0e 0a 00 60 08 04 0c 06 06 08 0e 0a 01 5b 19 04 6c 24 27 00 00 22 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 2e 39 63 63 63 32 32 32 00 30 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..10 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 03 00 04 0c 2a 01 50 08 0c 11 02 19 05 08 14 01 57 08 0d 0b 09 06 06 0f 00 06 51 08 0b 0b 05 06 08 06 0a 05 61 08 0b 7c 1b 08 1f 00 32 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 30 63 63 63 32 32 32 00 00 00 30 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..10 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0a 03 00 04 0c 2a 01 50 08 0c 10 02 19 05 08 14 01 57 08 0d 0a 09 06 06 0f 00 00 5f 08 0b 0e 05 06 08 06 0a 00 61 08 0b 7c 20 08 0c 00 32 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..11 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 05 00 03 0e 32 01 4f 08 0d 0e 06 00 05 01 14 06 32 0d 05 0a 06 06 07 0e 00 00 61 08 0a 0b 05 00 08 0d 0a 06 60 08 04 3c 20 2c 05 00 32 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 31 63 63 63 32 32 32 00 00 00 40 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..11 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 05 00 03 0e 32 01 4f 08 0d 12 06 00 05 01 14 01 3e 0a 05 0a 06 06 07 0e 00 00 61 08 0a 10 07 00 08 0d 0a 00 60 08 04 3c 20 2c 05 00 32 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 31 63 63 63 32 32 32 00 00 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..12 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 11 05 04 05 0e 32 02 4a 08 0b 16 0a 06 08 0e 0a 02 41 08 03 0a 06 05 09 0e 00 04 63 08 0c 0f 08 02 08 0e 0a 04 63 08 05 6c 21 27 38 02 12 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 32 63 63 63 32 32 32 00 20 00 50 00 10 00 40 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..12 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 04 05 0e 32 02 52 08 0b 18 0a 06 08 0e 0a 02 4d 08 03 0f 06 05 09 0e 00 00 63 13 0c 10 08 02 08 0e 0a 00 63 08 05 6c 24 27 00 00 12 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 20 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..13 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 06 02 06 0e 0c 02 42 08 0d 16 07 08 06 0f 49 03 4c 08 0d 11 11 0e 06 07 00 03 3c 19 0b 0b 0d 00 06 0f 02 03 5a 08 11 3a 21 27 07 00 52 0c 02 04 00 63 63 00 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 33 63 63 63 32 32 32 00 00 00 50 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..13 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 06 02 06 0e 0c 01 53 08 0c 16 07 08 06 0f 49 01 4c 0d 0b 1a 11 0e 06 07 00 00 5b 1f 0b 0c 0d 00 06 0f 02 00 5a 08 13 22 21 27 07 00 52 0c 02 04 00 63 63 00 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 33 63 63 63 32 32 32 00 00 00 50 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..14 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 06 04 06 0e 01 01 58 08 05 13 0d 10 07 0e 14 01 4d 08 04 0d 06 03 07 0e 0a 04 53 08 0a 0b 05 06 08 0c 0a 05 63 08 03 5c 23 27 05 00 32 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 34 63 63 63 32 32 32 00 60 00 20 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..14 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 04 06 0e 01 01 59 08 05 1f 0d 10 07 0e 14 01 4a 08 04 0e 06 03 07 0e 0a 00 63 08 0a 0f 05 06 08 0c 0a 00 63 08 03 6c 23 27 05 00 32 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 34 63 63 63 32 32 32 00 40 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..15 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 06 04 06 0e 01 01 4e 08 06 12 11 10 06 0e 14 04 4e 19 04 0d 06 03 07 0e 0a 04 60 08 0a 0b 04 05 07 0d 0a 04 5f 0d 03 6c 21 12 0f 00 42 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 35 63 63 63 32 32 32 00 20 00 20 00 00 00 30 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..15 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 04 06 0e 01 01 4e 08 06 1d 11 10 06 0e 14 01 4e 19 04 0e 06 03 07 0e 0a 00 60 08 0a 10 04 05 07 0d 0a 01 5f 0d 03 6c 23 00 09 00 42 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 35 63 63 63 32 32 32 00 00 00 00 00 00 00 30 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..16 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 06 02 06 0e 0c 01 54 08 04 16 07 08 06 0f 49 02 2e 1f 06 1a 11 0e 06 07 00 05 46 08 03 0b 0a 03 05 0e 00 05 56 08 0c 2a 21 27 07 00 52 0c 02 04 00 63 63 00 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 36 63 63 63 32 32 32 00 00 0a 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..16 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 06 02 06 0e 0c 01 54 08 04 16 07 08 06 0f 49 02 2e 1f 06 1a 11 0e 06 07 00 00 46 08 03 0b 0a 03 05 0e 00 00 56 08 0c 2a 21 27 07 00 52 0c 02 04 00 63 63 00 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 36 63 63 63 32 32 32 00 00 0a 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..17 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 06 00 03 0e 00 01 48 08 0e 0d 05 00 03 0e 00 01 44 08 0e 0a 08 03 07 0f 13 05 5b 08 0b 0c 04 03 07 0f 13 06 63 08 0b 64 2c 0a 08 00 32 0c 02 04 00 63 15 27 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 37 63 63 63 32 32 32 00 10 00 30 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..17 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 06 00 03 0e 00 01 48 08 0e 0d 05 00 03 0e 00 01 44 0a 0e 0a 04 03 07 0f 13 00 5b 0d 0b 0c 04 03 07 0f 13 00 63 08 0b 64 2c 0a 08 00 32 0c 02 04 00 63 15 27 00 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 37 63 63 63 32 32 32 00 10 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..18 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 00 00 06 0e 2a 04 4f 08 0c 19 0f 10 06 0d 0a 01 4e 08 0b 0d 07 06 07 0f 00 04 5e 08 0b 0e 06 06 08 0e 0a 05 60 08 04 74 20 1a 1b 00 21 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 38 63 63 63 32 32 32 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..18 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 06 0e 2a 01 4f 0d 0c 1f 0f 10 06 0d 0a 01 4e 08 0b 0d 07 06 07 0f 00 00 5e 0d 0b 10 06 06 08 0e 0a 01 60 08 04 74 23 00 0b 00 22 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 38 63 63 63 32 32 32 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..19 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 06 05 06 0e 1e 02 51 08 14 18 03 07 06 08 14 03 51 08 04 0f 06 06 09 0f 0a 04 47 13 0b 0c 05 06 08 0c 0a 0c 5f 08 03 52 21 27 21 00 21 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 39 63 63 63 32 32 32 00 30 00 20 00 20 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violin..19 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 05 06 0e 1e 02 51 08 14 1f 0d 10 06 0e 14 01 51 08 04 0f 06 06 09 0f 0a 00 47 13 0b 0f 05 06 08 0c 0a 01 5f 08 03 52 24 27 01 00 22 0c 06 04 00 63 32 00 32 00 32 00 56 69 6f 6c 69 6e 2e 2e 31 39 63 63 63 32 32 32 00 00 00 30 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: VIOLINS ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 00 00 04 0e 42 02 44 08 00 16 08 05 07 0d 19 03 49 08 0b 0f 08 10 07 0b 01 04 4d 08 0e 0b 03 02 08 0f 13 05 5a 08 0a 68 20 19 2e 00 32 0c 0c 04 00 63 32 32 00 00 32 00 20 56 49 4f 4c 49 4e 53 20 20 63 63 63 32 32 32 00 40 00 20 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: VIOLINS ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 04 0e 42 00 47 08 00 1e 08 05 07 0d 19 00 53 08 0b 1f 0f 10 07 0e 01 04 49 19 0e 0f 03 02 08 0f 13 00 51 08 0a 68 23 00 0b 00 32 0c 0c 04 00 63 32 32 00 00 32 00 20 56 49 4f 4c 49 4e 53 20 20 63 63 63 32 32 32 00 00 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violins..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 00 00 04 06 3c 01 4c 08 01 12 0f 06 06 06 09 02 40 0d 04 0b 07 06 06 06 14 04 63 08 02 0b 09 00 05 06 28 04 62 08 03 2c 1e 18 3b 00 32 0c 0c 04 00 63 32 32 00 00 32 00 56 69 6f 6c 69 6e 73 2e 2e 31 63 63 63 32 32 32 00 10 00 40 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violins..1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 00 00 04 06 3c 00 4c 08 01 0f 0f 06 06 06 09 00 40 19 04 0c 07 06 06 06 14 00 63 08 02 12 09 00 05 06 28 00 62 08 03 2c 21 0a 0f 00 32 00 0c 04 00 63 32 32 00 00 32 00 56 69 6f 6c 69 6e 73 2e 2e 31 63 63 63 32 32 32 00 10 00 40 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violins..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 06 00 07 0d 21 02 4e 08 05 13 0c 0c 05 0d 32 02 45 0d 04 0b 06 06 07 0e 14 03 5d 08 04 0b 07 00 07 0d 1e 05 61 08 04 2c 20 2c 14 00 42 0c 0c 04 00 63 32 32 00 00 32 00 56 69 6f 6c 69 6e 73 2e 2e 32 63 63 63 32 32 32 00 10 00 40 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violins..2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 06 00 07 0d 21 00 4e 08 05 13 0c 0c 05 0d 32 00 45 19 04 0f 06 06 07 0e 14 00 5d 08 04 0e 07 00 07 0d 1e 00 61 08 04 2c 20 2c 14 00 42 0c 0c 04 00 63 32 32 00 00 32 00 56 69 6f 6c 69 6e 73 2e 2e 32 63 63 63 32 32 32 00 20 00 20 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violins..3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 06 00 07 0d 21 01 46 08 05 12 0c 0c 05 0d 32 02 43 0d 04 0b 06 06 06 0e 28 05 59 08 03 0b 07 00 07 0d 1e 04 61 08 04 6c 1e 27 34 00 32 0c 0c 04 00 63 32 32 00 00 32 00 56 69 6f 6c 69 6e 73 2e 2e 33 63 63 63 32 32 32 00 00 00 40 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violins..3 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 06 00 07 0d 21 00 46 08 05 1f 0c 0c 05 0d 32 00 43 13 04 0c 06 06 06 0e 28 00 59 08 03 10 07 00 07 0d 1e 00 61 08 04 6c 23 27 0c 00 32 0c 0c 04 00 63 32 32 00 00 32 00 56 69 6f 6c 69 6e 73 2e 2e 33 63 63 63 32 32 32 00 00 00 40 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violins..4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 07 05 05 0e 41 01 50 08 05 0f 0c 0c 04 0c 33 02 57 19 05 0a 06 00 06 0e 14 04 5b 08 01 0b 05 01 07 0e 11 04 52 08 00 6c 1f 1c 25 00 32 0c 03 04 01 63 32 32 00 00 32 00 56 69 6f 6c 69 6e 73 2e 2e 34 63 63 63 32 32 32 00 40 00 30 00 00 00 20 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Violins..4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 04 00 03 0e 41 01 57 08 05 0f 0c 0c 04 0c 33 02 57 19 05 0a 06 00 06 0e 14 00 5b 08 01 0d 05 01 07 0e 11 00 52 08 00 6c 25 14 0f 00 32 0c 03 04 01 63 32 32 00 00 32 00 56 69 6f 6c 69 6e 73 2e 2e 34 63 63 63 32 32 32 00 30 00 30 00 00 00 20 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Other ** Name: 8 Stringer ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 00 01 0e 00 01 3f 04 03 1f 0c 06 09 0e 00 02 56 0a 1b 1f 0c 02 08 0e 00 05 41 1c 0b 1f 05 06 09 0e 00 04 63 04 13 22 21 13 00 00 02 00 02 04 00 63 32 00 32 00 32 00 38 20 53 74 72 69 6e 67 65 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: 8 Stringer ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 00 01 0e 00 01 3f 04 03 1f 0c 06 09 0e 00 01 56 0a 1b 1f 0c 02 08 0e 00 01 41 1c 0b 1f 05 06 09 0e 00 00 63 04 13 22 21 13 00 00 02 00 02 04 00 63 32 00 32 00 32 00 38 20 53 74 72 69 6e 67 65 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0f 09 03 0c 2e 03 63 24 1a 1c 0f 09 06 0c 00 03 4c 13 0c 1c 0a 06 04 0c 00 04 59 08 0a 0d 1c 06 06 0c 00 05 5d 0d 14 4d 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0f 09 03 0c 2e 00 63 24 1a 1c 0f 09 06 0c 00 00 4c 13 0c 1c 0a 06 04 0c 00 00 59 08 0a 0d 1c 06 06 0c 00 00 5d 0d 14 4d 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 18 15 05 0d 2e 03 57 13 1b 1c 09 09 04 0d 00 03 55 04 09 1c 09 09 05 0d 00 05 53 0d 0e 1c 0d 0f 05 0d 00 06 62 04 0c 75 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 18 15 05 0d 2e 00 57 13 1b 1c 09 09 04 0d 00 00 55 04 09 1c 09 09 05 0d 00 00 53 0d 0e 1c 0d 0f 05 0d 00 00 62 04 0c 75 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 09 03 04 0c 00 04 3a 19 1b 1c 1c 09 04 0f 00 04 52 04 0b 1c 09 03 06 0c 00 05 53 04 16 1c 1f 09 05 0f 00 06 62 04 14 65 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.3 ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 09 03 04 0c 00 00 3a 19 1b 1c 1c 09 04 0f 00 00 52 04 0b 1c 09 03 06 0c 00 00 53 04 16 1c 1f 09 05 0f 00 00 62 04 14 65 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1b 16 08 0d 00 03 63 0f 13 1f 0a 0a 07 0e 00 03 3e 04 0b 1b 13 06 0c 07 00 06 47 16 0b 1f 0a 06 06 0d 00 05 5d 04 0b 4a 1f 00 00 00 12 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.4 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1b 16 08 0d 00 00 63 0f 13 1f 0a 0a 07 0e 00 00 3e 04 0b 1b 13 06 0c 07 00 00 47 16 0b 1f 0a 06 06 0d 00 00 5d 04 0b 4a 1f 00 00 00 12 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.5 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 0c 09 05 0c 00 04 3d 1f 08 10 0c 03 09 0c 00 03 3e 08 0a 1f 15 06 05 0d 00 46 63 13 08 1f 15 06 05 0d 00 06 63 04 0e 1c 15 21 11 27 26 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.5 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 0c 09 05 0c 00 00 3d 1f 08 10 0c 03 09 0c 00 00 3e 08 0a 1f 15 06 05 0d 00 40 63 13 08 1f 15 06 05 0d 00 00 63 04 0e 1c 15 21 11 27 26 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.6 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 10 0d 03 0a 00 04 3b 0d 08 0f 0f 0d 03 0a 00 44 3c 0d 0a 1c 05 03 07 0d 00 05 47 08 08 1c 05 03 07 0d 00 07 5d 08 0e 5c 23 3c 40 60 16 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.6 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 10 0d 03 0a 00 00 3b 0d 08 0f 0f 0d 03 0a 00 40 3c 0d 0a 1c 05 03 07 0d 00 00 47 08 08 1c 05 03 07 0d 00 00 5d 08 0e 5c 23 3c 40 60 16 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.7 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 04 02 0d 00 43 4c 04 00 13 0c 12 09 0f 00 43 3c 04 02 15 12 1f 09 0f 00 45 63 04 00 10 05 06 09 0e 00 45 5d 04 06 25 23 5a 01 02 46 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.7 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 04 02 0d 00 40 4c 04 00 13 0c 12 09 0f 00 40 3c 04 02 15 12 1f 09 0f 00 40 63 04 00 10 05 06 09 0e 00 40 5d 04 06 25 23 5a 01 02 46 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 09 06 08 00 03 63 04 03 1c 1e 00 07 08 00 04 54 04 0b 1b 15 09 04 08 00 05 5d 08 0b 1c 19 09 05 08 00 06 63 08 0b 7a 1f 00 00 00 12 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.8 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 09 06 08 00 00 63 04 03 1c 1e 00 07 08 00 00 54 04 0b 1b 15 09 04 08 00 00 5d 08 0b 1c 19 09 05 08 00 00 63 08 0b 7a 1f 00 00 00 12 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.9 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 16 09 0f 0b 00 43 63 04 15 1c 09 06 03 0b 00 44 54 04 11 1f 09 04 0f 0e 00 45 5a 08 13 1c 13 09 06 0b 00 47 63 08 0b 6c 19 2e 42 0f 36 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst.9 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 16 09 0f 0b 00 40 63 04 15 1c 09 06 03 0b 00 40 54 04 11 1f 09 04 0f 0e 00 40 5a 08 13 1c 13 09 06 0b 00 40 63 08 0b 6c 19 2e 42 0f 36 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 2e 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst10 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 16 18 0c 0b 00 41 47 0d 03 0f 0f 0d 06 0b 00 44 3c 0d 03 0d 0c 07 04 0d 00 44 48 00 03 1c 12 07 07 0e 00 46 63 04 03 5a 19 2e 42 0f 36 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst10 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 16 18 0c 0b 00 40 47 0d 03 0f 0f 0d 06 0b 00 40 3c 0d 03 0d 0c 07 04 0d 00 40 48 00 03 1c 12 07 07 0e 00 40 63 04 03 5a 19 2e 42 0f 36 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst11 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 18 0f 08 06 00 02 63 1c 03 1c 0c 1f 09 0a 00 42 4f 08 13 1f 06 0f 08 0f 00 44 5c 04 08 1c 0a 09 09 0a 00 05 63 04 0e 2c 28 00 00 3f 26 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 31 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntInst11 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 18 0f 08 06 00 00 63 1c 03 1c 0c 1f 09 0a 00 40 4f 08 13 1f 06 0f 08 0f 00 40 5c 04 08 1c 0a 09 09 0a 00 00 63 04 0e 2c 28 00 00 3f 26 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 49 6e 73 74 31 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Syamisen ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 18 1a 0e 0f 06 06 0a 04 54 0a 13 17 12 12 07 0c 0a 04 59 10 13 18 06 00 04 0e 00 02 40 04 13 17 0f 09 06 0a 00 03 63 08 13 5b 18 02 20 00 42 18 02 04 00 63 32 00 32 00 32 00 53 79 61 6d 69 73 65 6e 20 20 63 61 63 31 32 32 00 20 00 30 00 70 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Syamisen ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 0e 0f 06 06 0a 04 54 0a 13 17 12 12 07 0c 0a 04 59 10 13 18 06 00 04 0e 00 02 40 04 13 17 0f 09 06 0d 00 03 63 08 13 5b 18 02 20 00 42 18 02 04 00 63 32 00 32 00 32 00 53 79 61 6d 69 73 65 6e 20 20 63 61 63 31 32 32 00 20 00 30 00 70 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Zither ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 8 1f 0b 06 0a 0d 1e 01 57 10 0b 1f 1f 00 03 0f 22 01 51 04 03 1f 0c 03 0a 0a 14 01 36 08 03 1f 18 06 08 0f 00 02 63 04 03 30 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 5a 69 74 68 65 72 20 20 20 20 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 11 * CATEGORY KEYBOARDS * SUBCATEGORY Clavichord ** Name: Clav Lead ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 00 09 0c 0f 03 4b 24 15 1e 1b 00 09 0d 0c 05 56 00 01 1d 01 00 0c 00 2e 04 53 00 14 1e 1c 00 09 0c 00 04 63 00 15 3a 23 00 08 00 22 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 20 4c 65 61 64 20 63 63 63 32 32 32 00 00 00 40 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav Lead ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 00 09 0c 0f 01 4b 24 15 1e 1b 00 09 0d 0c 01 56 00 01 1d 01 00 0c 00 2e 01 53 00 14 1e 1c 00 09 0c 00 00 63 00 15 3a 23 00 08 00 22 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 20 4c 65 61 64 20 63 63 63 32 32 32 00 00 00 40 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav Perc ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 0f 04 09 0c 0f 03 5b 00 0a 1a 0f 04 09 0d 0c 04 63 00 13 1f 0a 04 0c 00 2e 06 58 00 0e 19 0f 05 09 0c 00 05 63 04 10 3a 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 20 50 65 72 63 20 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav Perc ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 0f 04 09 0c 0f 01 5b 00 0a 1a 0f 04 09 0d 0c 01 63 00 13 1f 0a 04 0c 00 2e 01 58 00 0e 19 0f 05 09 0c 00 00 63 04 10 3a 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 20 50 65 72 63 20 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav Synth ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 04 09 0c 0f 03 5e 0d 16 1e 04 04 09 0d 0c 04 5f 00 03 1e 03 04 0c 00 2e 04 55 00 13 1e 15 05 09 0c 00 06 63 0d 13 3a 23 1a 0f 00 32 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 20 53 79 6e 74 68 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav Synth ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 04 09 0c 0f 01 5e 0d 16 1e 04 04 09 0d 0c 01 5f 00 03 1e 03 04 0c 00 2e 01 55 00 13 1e 15 05 09 0c 00 00 63 0d 13 3a 23 1a 0f 00 32 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 20 53 79 6e 74 68 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0b 08 0f 09 10 01 42 13 0b 1f 0c 08 05 0f 00 04 5c 00 03 1f 10 05 06 0f 5e 04 52 00 13 1c 14 08 0c 0e 00 03 63 04 0b 28 23 00 00 00 62 18 02 04 00 63 32 00 32 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0b 08 0f 09 10 01 42 13 0b 1f 0c 08 05 0f 00 02 5c 00 03 1f 10 05 06 0f 5e 02 52 00 13 1c 14 08 0c 0e 00 00 63 04 0b 28 23 00 00 00 62 18 02 04 00 63 32 00 32 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 05 09 0c 0f 01 43 0d 15 1e 1b 01 09 0d 0c 03 5f 00 01 1d 01 01 0c 00 2e 04 51 16 14 1e 1c 05 09 0c 00 03 63 00 13 1b 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 05 09 0c 0f 01 43 0d 15 1e 1b 01 09 0d 0c 01 5f 00 01 1d 01 01 0c 00 2e 01 51 16 14 1e 1c 05 09 0c 00 00 63 00 13 1b 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 05 01 0c 20 01 54 04 15 17 1a 01 01 0d 09 03 54 04 04 1d 01 01 01 00 32 03 44 1f 0c 18 1b 05 07 0c 02 03 63 08 13 3a 23 00 00 00 02 18 05 04 00 63 32 32 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 10 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 05 01 0c 20 01 54 04 15 17 1a 01 01 0d 09 01 54 04 04 1d 01 01 01 00 32 01 44 1f 0c 18 1b 05 07 0c 02 00 63 08 13 3a 23 00 00 00 02 18 05 04 00 63 32 32 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 10 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 15 0a 01 00 20 01 61 00 15 17 09 0f 01 00 09 04 53 00 02 1d 01 0b 01 00 32 03 3e 1f 16 19 1b 06 09 0e 02 04 62 08 09 3a 23 00 00 00 02 18 02 04 00 63 32 32 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 15 0a 01 00 20 01 61 00 15 17 09 0f 01 00 09 02 53 00 02 1d 01 0b 01 00 32 01 3e 1f 16 19 1b 06 09 0e 02 00 62 08 09 3a 23 00 00 00 02 18 02 04 00 63 32 32 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 10 07 08 0c 00 01 51 16 1d 19 19 06 09 0e 00 03 57 00 0a 1c 10 07 08 0c 00 03 63 00 1b 19 19 06 09 0e 00 03 62 08 09 3c 1e 00 00 00 22 18 02 04 00 63 32 32 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 10 07 08 0c 00 01 51 16 1d 19 19 06 09 0e 00 01 57 00 0a 1c 10 07 08 0c 00 01 63 00 1b 19 19 06 09 0e 00 00 62 08 09 3c 1e 00 00 00 22 18 02 04 00 63 32 32 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 10 07 05 0c 00 01 4e 39 1d 19 19 06 09 0e 00 03 5a 00 0a 1c 05 07 05 0c 00 03 5d 0d 1b 19 19 06 09 0e 00 06 62 08 09 3c 1e 00 00 00 22 18 02 04 00 63 32 32 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 10 07 05 0c 00 01 4e 39 1d 19 19 06 09 0e 00 01 5a 00 0a 1c 05 07 05 0c 00 01 5d 0d 1b 19 19 06 09 0e 00 00 62 08 09 3c 1e 00 00 00 22 18 02 04 00 63 32 32 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 01 02 04 07 35 00 35 24 0d 17 09 0f 01 00 09 04 53 00 02 1d 01 0b 01 00 32 04 39 08 16 19 1b 06 09 0e 02 06 63 00 09 02 23 00 00 00 02 18 02 04 00 63 32 32 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 40 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 01 02 04 07 35 00 35 24 0d 17 09 0f 01 00 09 01 53 00 02 1d 01 0b 01 00 32 00 39 08 16 19 1b 06 09 0e 02 01 63 00 09 02 23 00 00 00 02 18 02 04 00 63 32 32 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 40 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 05 09 0c 0f 01 4c 16 15 1e 1b 01 09 0d 0c 05 53 00 01 1d 01 01 0c 00 2e 05 59 00 14 1e 1c 05 09 0c 00 05 63 08 13 1a 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....8 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 05 09 0c 0f 01 4c 16 15 1e 1b 01 09 0d 0c 01 53 00 01 1d 01 01 0c 00 2e 01 59 00 14 1e 1c 05 09 0c 00 00 63 08 13 1a 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....9 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 05 09 0c 0f 03 52 0d 15 1e 1b 01 09 0d 0c 05 63 04 01 1d 01 01 0c 00 2e 04 62 04 14 1e 1c 05 09 0c 00 05 63 04 13 1c 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 39 63 63 63 32 32 32 00 00 00 40 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clav.....9 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 05 09 0c 0f 00 52 0d 15 1e 1b 01 09 0d 0c 01 63 04 01 1d 01 01 0c 00 2e 01 62 04 14 1e 1c 05 09 0c 00 01 63 04 13 1c 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 43 6c 61 76 2e 2e 2e 2e 2e 39 63 63 63 32 32 32 00 00 00 40 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clavi ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 16 1f 01 00 0f 00 00 04 4d 13 10 1f 0c 01 01 0a 00 02 54 00 09 1f 06 01 01 0e 23 03 4f 10 0e 1f 00 0a 0f 0f 00 03 58 08 0a 3a 1c 00 00 00 52 18 02 04 00 63 32 00 32 00 32 00 43 6c 61 76 69 20 20 20 20 20 63 63 63 32 32 32 00 20 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Clavi ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 01 00 0f 00 00 04 4d 13 10 1f 0c 01 01 0d 00 02 54 00 09 1f 06 01 01 0e 23 03 4f 10 0e 1f 00 0a 0f 0f 00 03 58 08 0d 3a 1c 00 00 00 52 18 02 04 00 63 32 00 32 00 32 00 43 6c 61 76 69 20 20 20 20 20 63 63 63 32 32 32 00 20 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: EZ Clav ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 26 1f 0b 08 0f 09 10 04 42 2d 13 1f 09 08 05 0f 00 02 49 00 03 1f 12 05 06 0f 10 03 46 10 03 1f 14 08 0c 0e 00 02 63 04 0b 30 1b 00 00 00 52 18 04 04 00 63 32 00 00 00 32 00 45 5a 20 43 6c 61 76 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fuzz Clavi ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 27 1e 1c 05 09 0c 0f 01 56 24 15 1e 1b 01 09 0d 0c 04 61 00 01 1d 01 01 0c 00 2e 02 4d 0d 14 1e 1c 05 09 0c 00 01 63 0a 13 33 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 46 75 7a 7a 20 43 6c 61 76 69 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Klavinet * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 06 06 06 0e 00 00 52 04 0e 1d 07 09 07 0e 00 04 4d 04 00 1d 09 06 06 0e 10 02 50 1f 16 1b 0a 07 0a 0e 00 02 63 08 0b 02 23 00 00 00 32 0c 02 04 00 28 63 00 00 00 32 00 4b 6c 61 76 69 6e 65 74 20 2a 63 63 63 32 32 32 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Clav ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1c 05 01 0c 20 02 4e 2a 15 17 1a 01 01 0d 09 02 4b 04 04 1d 01 01 01 00 32 04 39 04 0c 18 1b 05 07 0c 02 05 63 08 13 3a 23 00 00 00 02 18 05 04 00 63 32 32 00 00 32 00 4f 72 6e 74 20 43 6c 61 76 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Clav ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1c 05 01 0c 20 00 4e 2a 15 17 1a 01 01 0d 09 00 4b 04 04 1d 01 01 01 00 32 00 39 04 0c 18 1b 05 07 0c 02 00 63 08 13 3a 23 00 00 00 02 18 05 04 00 63 32 32 00 00 32 00 4f 72 6e 74 20 43 6c 61 76 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Clav ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1e 1c 05 00 0c 20 00 4e 2a 15 17 1a 01 00 0d 09 00 4b 04 04 1d 01 01 00 00 32 00 39 04 0c 18 1b 05 07 0c 02 00 63 08 13 3a 23 00 00 00 02 18 05 04 00 63 32 32 00 00 32 00 4f 72 6e 74 20 43 6c 61 76 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rimbaclav ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 10 00 05 00 00 00 4c 28 03 1f 12 00 04 00 00 00 43 16 03 1f 0d 00 05 00 00 00 45 16 03 1f 07 00 06 08 00 00 63 04 03 03 23 00 00 00 62 0c 07 04 00 63 63 00 00 00 32 00 52 69 6d 62 61 63 6c 61 76 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SplashClav ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 21 13 18 14 0a 0c 00 00 61 04 03 10 02 01 0a 0c 00 00 5c 1c 03 1c 0b 04 0a 0c 00 00 41 28 03 1b 0e 10 0a 0c 00 03 63 04 03 3a 55 00 39 00 42 0c 04 04 00 28 32 00 00 00 32 00 53 70 6c 61 73 68 43 6c 61 76 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synclav. * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 09 07 05 0f 00 02 51 0d 0b 1f 0c 06 02 00 00 00 57 10 0e 17 06 05 05 0d 00 00 63 04 08 1f 09 0f 06 00 00 03 5a 19 0e 2c 23 00 00 00 32 0c 02 04 00 28 63 00 00 00 32 00 53 79 6e 63 6c 61 76 2e 20 2a 63 63 63 32 32 32 00 40 00 00 00 30 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Thin Clav ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 25 1f 0b 07 0f 09 00 04 49 13 13 1f 08 07 05 0e 00 02 58 00 0c 1f 06 04 06 0f 23 03 46 10 0a 1f 07 08 0c 0d 00 02 63 08 0b 02 1c 00 00 00 52 18 04 04 00 63 32 00 32 00 32 00 54 68 69 6e 20 43 6c 61 76 20 63 63 63 32 32 32 00 70 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Harpsichord ** Name: Harpschrd2 ** Description: DX11 factory preset 'Harpsichrd' with slower decay ** Source: Art Hitzeman 1a 13 00 01 0d 0a 00 5b 0a 11 0f 10 00 01 0d 00 00 5d 00 1d 17 08 06 04 0c 00 00 4c 04 16 17 08 06 05 0c 00 00 58 0d 14 1c 1f 00 00 00 52 18 02 04 00 63 32 32 00 00 32 00 48 61 72 70 73 63 68 72 64 32 63 63 63 32 32 32 00 40 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harpschrd3 ** Description: 'Harpschrd2' with minor changes ** Source: Art Hitzeman 1a 13 00 01 0d 0a 00 5b 05 11 0f 10 00 01 0d 00 00 5d 00 1d 17 08 06 04 0c 00 00 4c 00 16 17 08 06 05 0c 00 00 58 0d 14 1c 1f 00 00 00 52 18 02 04 00 63 32 32 00 00 32 00 48 61 72 70 73 63 68 72 64 33 63 63 63 32 32 32 00 41 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harpsichrd ** Source: Yamaha DX11 factory preset, bank B, voice 15 1a 13 00 01 0a 0a 00 5b 0a 11 0f 10 00 01 0a 00 00 5d 00 1d 17 08 06 07 0c 00 00 4c 04 16 17 08 06 07 0c 00 00 58 0a 14 1c 1f 00 00 00 52 18 02 04 00 63 32 32 00 00 32 00 48 61 72 70 73 69 63 68 72 64 63 63 63 32 32 32 00 40 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harpsichrd ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 13 00 01 0d 0a 00 5b 0a 11 0f 10 00 01 0d 00 00 5d 00 1d 17 08 06 07 0c 00 00 4c 04 16 17 08 06 07 0c 00 00 58 0d 14 1c 1f 00 00 00 52 18 02 04 00 63 32 32 00 00 32 00 48 61 72 70 73 69 63 68 72 64 63 63 63 32 32 32 00 40 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hrpsicord1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1d 01 06 0c 29 00 57 04 1b 1f 19 03 04 0d 00 03 5b 04 03 1f 1f 05 09 0d 00 06 3f 0d 13 1f 1a 01 07 0d 00 05 63 0d 1b 32 23 00 00 00 62 18 00 04 00 63 32 00 00 00 32 00 48 72 70 73 69 63 6f 72 64 31 63 63 63 32 32 32 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hrpsicord1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1d 01 06 0c 29 01 57 04 1b 1f 19 03 04 0d 00 00 5b 04 03 1f 1f 05 09 0d 00 00 3f 0d 13 1f 1a 01 07 0d 00 02 63 0d 1b 32 23 00 00 00 62 18 00 04 00 63 32 00 00 00 32 00 48 72 70 73 69 63 6f 72 64 31 63 63 63 32 32 32 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hrpsicord2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 11 00 01 0d 00 00 5b 04 1c 1c 18 00 01 0d 00 00 5b 00 0c 17 08 05 06 0c 00 05 60 00 11 1c 18 05 06 0d 00 04 60 0d 12 0c 23 00 00 00 02 24 00 04 00 63 32 00 00 00 32 00 48 72 70 73 69 63 6f 72 64 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hrpsicord2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 11 00 01 0d 00 00 5b 04 1c 1c 18 00 01 0d 00 00 5b 00 0c 17 08 05 06 0c 00 01 60 00 11 1c 18 05 06 0d 00 01 60 0d 12 0c 23 00 00 00 02 24 00 04 00 63 32 00 00 00 32 00 48 72 70 73 69 63 6f 72 64 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hrpsicord3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1d 01 01 0d 0f 02 5a 00 1b 1f 17 03 01 0d 00 02 57 04 03 1f 1c 05 09 0d 00 05 42 13 0b 1f 1c 01 05 0d 00 06 63 0d 1b 2a 23 00 00 00 62 18 00 04 00 63 32 00 00 00 32 00 48 72 70 73 69 63 6f 72 64 33 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hrpsicord3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1d 01 01 0d 0f 02 5a 00 1b 1f 17 03 01 0d 00 00 57 04 03 1f 1c 05 09 0d 00 00 42 13 0b 1f 1c 01 05 0d 00 01 63 0d 1b 2a 23 00 00 00 62 18 00 04 00 63 32 00 00 00 32 00 48 72 70 73 69 63 6f 72 64 33 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LiteHarpsi ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 28 1f 1d 00 01 0c 00 00 54 04 1b 1f 13 01 01 0f 00 01 4c 04 03 1f 1f 01 09 0f 21 01 4d 19 13 1f 1e 06 07 0d 00 00 5d 0d 13 32 1c 00 00 00 62 18 02 04 00 63 32 00 00 00 32 00 4c 69 74 65 48 61 72 70 73 69 63 63 63 32 32 32 00 00 30 00 30 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RichHarpsi ** Source: Yamaha TX81Z factory preset, bank A, voice 29 1a 13 00 01 0d 00 00 5b 0a 11 0f 10 00 01 0d 00 00 5d 00 1d 17 08 06 05 0c 00 00 5e 04 16 17 08 06 05 0c 00 00 56 0d 14 1c 23 00 00 00 02 18 04 04 00 63 32 32 00 00 32 00 52 69 63 68 48 61 72 70 73 69 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 12 * SUBCATEGORY Organ1 ** Name: 16 8 4 2 F ** Source: Yamaha TX81Z factory preset, bank A, voice 18 1f 1f 00 0f 0f 00 00 5a 19 06 1f 1f 00 0f 0f 00 00 5a 08 03 1f 1f 00 0f 0f 00 00 5a 0d 04 1f 1f 00 0f 0f 00 00 5a 04 00 07 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 31 36 20 38 20 34 20 32 20 46 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <6 Tease> ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 20 1f 00 00 0a 0f 1b 00 3f 0d 03 1f 00 00 0a 0f 00 00 63 08 03 1f 00 00 0a 0f 00 00 5e 0d 03 1f 00 00 0a 0f 00 00 63 04 03 3e 23 00 27 00 42 0c 07 04 00 63 63 00 63 00 32 00 3c 36 20 54 65 61 73 65 3e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnalogOrgn ** Controls: MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 24 1f 1b 00 0f 0d 09 00 57 05 06 1f 1c 00 0f 0d 09 00 57 04 01 1e 01 00 0f 0d 0a 00 5f 05 06 1e 01 00 0f 0d 0a 00 5f 04 01 04 1c 15 07 00 52 05 04 04 00 63 32 00 00 00 32 00 41 6e 61 6c 6f 67 4f 72 67 6e 63 63 63 32 32 32 00 01 00 08 00 01 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: B3 Organ.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 06 0d 0f 00 03 55 2a 04 1f 13 06 0d 0f 00 05 63 08 04 1f 18 06 0d 0f 00 04 62 0d 04 1f 06 06 0d 0f 00 05 63 00 04 07 28 00 0a 2c 12 0c 07 04 00 63 63 00 00 00 32 00 42 33 20 4f 72 67 61 6e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: B3 Organ.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 06 0d 0f 00 00 55 2a 04 1f 13 06 0d 0f 00 01 63 08 04 1f 18 06 0d 0f 00 01 62 0d 04 1f 06 06 0d 0f 00 01 63 00 04 07 28 00 0a 2c 12 0c 07 04 00 63 63 00 00 00 32 00 42 33 20 4f 72 67 61 6e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: B3 Organ.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 16 05 0d 0f 00 02 3e 08 04 1f 1f 1f 0d 0f 00 04 63 0d 04 1f 1f 02 0d 0f 00 05 63 04 04 1f 1f 07 0d 0f 00 03 63 08 04 04 28 00 0a 2c 22 18 07 04 00 63 63 00 00 00 32 00 42 33 20 4f 72 67 61 6e 2e 32 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: B3 Organ.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 16 05 0d 0f 00 00 3e 08 04 1f 1f 1f 0d 0f 00 00 63 0d 04 1f 1f 02 0d 0f 00 01 63 04 04 1f 1f 07 0d 0f 00 00 63 08 04 04 28 00 0a 2c 22 18 07 04 00 63 63 00 00 00 32 00 42 33 20 4f 72 67 61 6e 2e 32 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: B3 Organ.3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 10 04 0d 0f 00 02 52 08 04 1f 1f 04 0d 0f 00 04 63 08 04 1f 1f 03 0d 0f 0a 03 63 08 14 1f 1f 03 0d 0f 00 04 63 04 04 06 21 00 00 00 22 0c 07 04 00 63 63 00 00 00 32 00 42 33 20 4f 72 67 61 6e 2e 33 63 63 63 32 32 32 00 00 00 30 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: B3 Organ.3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 10 04 0d 0f 00 00 52 08 04 1f 1f 04 0d 0f 00 01 63 08 04 1f 1f 03 0d 0f 0a 01 63 08 14 1f 1f 03 0d 0f 00 01 63 04 04 06 21 00 00 00 22 0c 07 04 00 63 63 00 00 00 32 00 42 33 20 4f 72 67 61 6e 2e 33 63 63 63 32 32 32 00 00 00 30 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Big Church ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 23 16 00 00 0a 0f 00 01 47 1f 03 18 00 00 05 0f 5b 02 61 00 10 0f 00 00 08 0f 00 00 63 08 10 0c 00 00 05 0f 00 00 63 00 16 3c 1d 00 00 00 42 0c 04 04 00 63 32 00 00 00 32 00 42 69 67 20 43 68 75 72 63 68 63 63 63 32 32 32 00 00 00 70 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ChurchOrg* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 1f 00 0a 0f 00 42 47 1f 0e 10 1f 00 08 0f 00 40 60 0d 0e 10 1f 00 07 0f 00 40 63 08 08 0b 1f 00 05 0f 14 40 63 04 10 3e 0c 0f 00 00 3a 0c 02 04 00 28 00 1b 00 00 32 00 43 68 75 72 63 68 4f 72 67 2a 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ClosedFlut ** Description: A Closed Flute organ stop ** Source: Art Hitzeman 12 1f 00 07 0f 19 00 59 04 03 10 1f 00 06 0f 19 00 43 10 03 17 1f 00 07 0f 19 00 5a 0a 03 0e 1f 00 06 0f 19 00 20 16 03 07 23 01 00 00 62 18 02 04 00 28 32 00 00 00 32 00 43 6c 6f 73 65 64 46 6c 75 74 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cornopean ** Description: An organ stop ** Controls: MW (LFO pitch) ** Source: Art Hitzeman 12 1f 00 07 0f 19 00 58 00 03 10 1f 00 06 0f 19 00 5a 04 03 17 1f 00 07 0f 19 00 5a 04 03 0e 1f 00 06 0f 19 00 5a 04 03 47 23 01 06 00 41 18 02 04 00 28 32 00 00 00 32 00 43 6f 72 6e 6f 70 65 61 6e 20 63 63 63 32 32 32 00 00 00 40 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DistortOrg ** Controls: KV, MW (LFO pitch), BC (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 00 08 0f 00 27 42 03 1a 10 1b 00 08 06 0f 00 51 08 10 15 0a 00 08 0f 1b 1b 52 04 1e 12 1b 00 0b 0e 00 02 63 05 00 3b 23 00 01 00 52 11 02 05 00 63 32 00 05 00 32 3c 44 69 73 74 6f 72 74 4f 72 67 63 63 63 32 32 32 20 26 10 04 10 78 00 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Drawbar1 * ** Source: from a library of patches collected by Rod Lemcke, author unknown 17 1f 00 09 0f 32 40 5a 19 03 17 1f 00 09 0f 1e 40 63 0a 03 17 1f 00 09 0f 28 40 51 0d 03 17 1f 00 09 0f 14 40 5d 00 03 07 23 00 00 00 1a 18 02 04 00 28 63 30 00 00 32 00 44 72 61 77 62 61 72 31 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Drawbar2 * ** Source: from a library of patches collected by Rod Lemcke, author unknown 17 1f 00 09 0f 2f 40 4b 13 03 17 1f 00 09 0f 19 40 53 08 03 17 1f 00 09 0f 2c 40 51 10 03 17 1f 00 09 0f 0a 40 63 04 03 07 23 00 00 00 1a 18 02 04 00 28 63 30 00 00 32 00 44 72 61 77 62 61 72 32 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Dyno * ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0e 0f 0e 09 00 05 49 2b 08 1f 0f 05 07 0f 18 42 5a 00 06 1f 12 06 08 0d 00 02 63 00 06 1f 12 06 08 0f 00 02 63 04 00 34 14 00 01 02 4e 18 02 04 00 63 32 00 32 00 32 00 44 79 6e 6f 20 20 20 20 20 2a 63 63 63 32 32 32 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DynoWurlie ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 14 1f 1c 04 07 0a 00 03 4e 25 1e 16 07 03 07 0b 00 03 52 0d 10 1f 13 08 06 08 00 02 5c 3c 0e 1f 04 03 07 0e 00 02 63 04 13 7c 18 03 10 0c 32 0c 02 04 00 63 32 00 00 00 32 00 44 79 6e 6f 57 75 72 6c 69 65 63 63 63 32 32 32 1b 70 00 00 09 0f 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 1f 03 0f 0f 26 05 49 04 03 1f 1f 03 0f 0f 14 05 5a 08 03 1f 1f 03 0f 0f 50 05 59 0a 03 1f 1f 03 0f 0f 19 04 5b 00 03 76 23 0e 16 00 22 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 31 63 63 63 32 32 32 00 20 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..1 ** Source: from a library of patches collected by Duane Bowker, author unknown 15 1f 03 0f 0f 26 00 49 04 03 1f 1f 03 0f 0f 14 00 5a 08 03 1f 1f 03 0f 0f 50 00 59 0a 03 1f 1f 03 0f 0f 19 00 5b 00 03 76 23 0e 16 00 22 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 31 63 63 63 32 32 32 00 20 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 1f 03 0f 0f 26 05 2a 19 02 1f 1f 03 0f 0f 14 05 5a 0d 04 1f 1f 03 0f 0f 50 05 59 0d 01 1f 1f 03 0f 0f 19 05 63 0d 02 75 1c 00 19 00 22 00 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..2 ** Source: from a library of patches collected by Duane Bowker, author unknown 15 1f 03 0f 0f 26 00 2a 19 02 1f 1f 03 0f 0f 14 00 5a 0d 04 1f 1f 03 0f 0f 50 00 59 0d 01 1f 1f 03 0f 0f 19 00 63 0d 02 75 1c 00 19 00 22 00 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 16 03 08 0f 00 06 4c 0d 02 1f 1f 03 0c 0f 14 05 5c 0d 04 1f 1f 1f 0c 0f 00 05 63 04 01 1f 1f 02 0c 0f 19 05 63 04 02 06 23 00 0e 00 32 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..3 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 16 03 08 0f 00 00 4c 0d 02 1f 1f 03 0c 0f 14 00 5c 0d 04 1f 1f 1f 0c 0f 00 00 63 04 01 1f 1f 02 0c 0f 19 00 63 04 02 06 23 00 0e 00 32 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 16 00 08 0f 00 44 55 08 03 1f 1f 00 0c 0f 14 03 5c 0a 00 1f 1f 00 0c 0f 00 06 50 08 06 1f 1f 00 0c 0f 19 05 63 00 06 46 0e 00 47 4e 16 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 34 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..4 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 16 00 08 0f 00 40 55 08 03 1f 1f 00 0c 0f 14 00 5c 0a 00 1f 1f 00 0c 0f 00 00 50 08 06 1f 1f 00 0c 0f 19 00 63 00 06 46 0e 00 47 4e 16 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 34 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 16 00 08 0f 00 05 5a 04 0e 1b 1f 00 0c 0f 14 02 56 08 00 1f 1f 00 0c 0f 00 06 57 08 03 17 1f 00 0c 0f 19 05 63 00 06 47 0e 00 47 00 22 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 35 63 63 63 32 32 32 00 00 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..5 ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 16 00 08 0f 00 00 5a 04 0e 1b 1f 00 0c 0f 14 00 56 08 00 1f 1f 00 0c 0f 00 00 57 08 03 17 1f 00 0c 0f 19 00 63 00 06 47 0e 00 47 00 22 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 35 63 63 63 32 32 32 00 00 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0c 0f 00 04 41 08 0b 1f 1f 07 0c 0f 14 05 5c 08 03 1b 12 1f 0c 0f 00 05 50 0d 03 1c 1f 00 0c 0f 19 05 63 00 03 05 00 00 00 00 02 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..6 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 0c 0f 00 00 41 08 0b 1f 1f 07 0c 0f 14 00 5c 08 03 1b 12 1f 0c 0f 00 00 50 0d 03 1c 1f 00 0c 0f 19 00 63 00 03 05 00 00 00 00 02 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 03 0d 0f 00 03 51 00 08 1f 18 15 0a 0f 14 03 63 04 05 1b 12 06 0c 0f 00 06 63 08 06 1c 1f 06 0c 0f 19 05 63 04 06 07 21 00 0d 00 32 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 37 63 63 63 32 32 32 00 00 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..7 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 03 0d 0f 00 00 51 00 08 1f 18 15 0a 0f 14 00 63 04 05 1b 12 06 0c 0f 00 00 63 08 06 1c 1f 06 0c 0f 19 00 63 04 06 07 21 00 0d 00 32 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 37 63 63 63 32 32 32 00 00 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 03 0d 0f 63 05 63 13 0b 1f 18 15 0a 0f 14 05 63 04 03 1b 12 06 0c 0f 00 04 63 04 03 1c 1f 06 0c 0f 19 05 63 00 03 07 27 00 0d 00 42 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..8 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 03 0d 0f 63 00 63 13 0b 1f 18 15 0a 0f 14 00 63 04 03 1b 12 06 0c 0f 00 00 63 04 03 1c 1f 06 0c 0f 19 00 63 00 03 07 27 00 0d 00 42 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..9 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 00 09 0f 00 03 41 16 03 1f 1f 00 0a 0f 00 05 47 04 03 1f 1f 00 09 0f 00 05 14 0a 03 1f 1f 00 09 0f 00 05 5a 04 03 40 23 00 06 00 52 18 04 04 00 28 32 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 39 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ..9 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 00 09 0f 00 00 41 16 03 1f 1f 00 0a 0f 00 00 47 04 03 1f 1f 00 09 0f 00 00 14 0a 03 1f 1f 00 09 0f 00 00 5a 04 03 40 23 00 06 00 52 18 04 04 00 28 32 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 2e 39 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ.10 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 04 0d 0f 63 03 46 0a 03 1f 1f 04 0c 0f 00 05 63 08 03 1f 1f 03 0d 0f 63 05 63 04 03 1f 1f 01 0d 0f 00 05 63 04 03 2d 2e 00 22 00 12 0c 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 31 30 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ.10 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 04 0d 0f 63 00 46 0a 03 1f 1f 04 0c 0f 00 00 63 08 03 1f 1f 03 0d 0f 63 00 63 04 03 1f 1f 01 0d 0f 00 00 63 04 03 2d 2e 00 22 00 12 0c 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 31 30 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ.11 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 00 09 0f 63 03 4d 04 03 1f 1f 00 0a 0f 10 06 41 04 02 1f 1f 00 09 0f 00 05 35 04 03 1f 1f 00 09 0f 01 05 5a 04 04 43 23 00 06 00 52 18 04 04 00 28 32 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 31 31 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ.11 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 00 09 0f 63 00 4d 04 03 1f 1f 00 0a 0f 10 00 41 04 02 1f 1f 00 09 0f 00 00 35 04 03 1f 1f 00 09 0f 01 00 5a 04 04 43 23 00 06 00 52 18 04 04 00 28 32 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 31 31 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ.12 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 03 0d 0f 63 04 44 0a 0b 04 15 03 07 0f 14 05 63 00 03 1b 12 06 0c 0f 00 05 63 04 03 19 15 0a 09 0f 19 05 5d 04 04 44 1d 00 0d 00 32 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 31 32 63 63 63 32 32 32 00 00 00 10 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ.12 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 03 0d 0f 63 00 44 0a 0b 04 15 03 07 0f 14 00 63 00 03 1b 12 06 0c 0f 00 00 63 04 03 19 15 0a 09 0f 19 00 5d 04 04 44 1d 00 0d 00 32 18 00 04 00 63 00 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 31 32 63 63 63 32 32 32 00 00 00 10 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ.13 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 00 09 0f 00 04 2d 08 03 1f 1f 00 0a 0f 00 05 47 00 03 1a 1f 02 09 0f 00 05 2a 0a 03 1a 1f 02 09 0f 00 05 5f 04 03 42 23 00 06 00 42 18 04 04 00 28 32 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 31 33 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Organ.13 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 00 09 0f 00 00 2d 08 03 1f 1f 00 0a 0f 00 00 47 00 03 1a 1f 02 09 0f 00 00 2a 0a 03 1a 1f 02 09 0f 00 00 5f 04 03 42 23 00 06 00 42 18 04 04 00 28 32 00 00 00 32 00 45 20 4f 72 67 61 6e 2e 31 33 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Organ 1 ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 26 1f 0b 01 0f 08 18 40 5f 0a 0e 1f 1f 00 0e 0f 00 40 5f 04 06 1f 1f 00 0e 0f 00 40 63 05 00 1f 1f 00 0e 0f 00 40 5f 00 08 1f 20 00 03 00 5a 18 02 04 00 63 32 32 00 00 32 00 45 2e 4f 72 67 61 6e 20 20 31 63 63 63 32 32 32 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Organ 2 ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 27 1f 0a 00 0b 0a 20 40 43 04 03 1f 1f 00 0c 0f 1a 00 5f 08 06 1f 09 00 0c 0f 08 00 5d 04 03 1f 1f 00 0c 0f 00 00 5f 0a 00 26 23 00 01 2a 42 0c 02 04 00 28 32 00 00 00 32 00 45 2e 4f 72 67 61 6e 20 20 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Organ 2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0d 00 0b 0d 20 40 43 04 03 1f 1f 00 0c 0f 1a 00 5f 08 06 1f 09 00 0c 0f 08 00 5d 04 03 1f 1f 00 0c 0f 00 00 5f 0a 00 26 23 00 01 2a 42 0c 02 04 00 28 32 00 00 00 32 00 45 2e 4f 72 67 61 6e 20 20 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Organ 3 ** Source: Yamaha DX11 factory preset, bank C, voice 28 1f 14 00 0f 0a 00 40 5e 0a 01 1f 1f 00 0f 0a 00 40 5c 04 00 1f 1f 00 0f 0a 00 40 63 08 05 1f 1f 00 0f 0a 00 40 63 04 06 2f 21 00 00 15 4a 18 02 04 00 28 32 00 00 00 32 00 45 2e 4f 72 67 61 6e 20 20 33 63 63 63 32 32 32 00 00 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Organ 3 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 14 00 0f 0d 00 40 5e 0a 01 1f 1f 00 0f 0d 00 40 5c 04 00 1f 1f 00 0f 0d 00 40 63 08 05 1f 1f 00 0f 0d 00 40 63 04 06 2f 21 00 00 15 4a 18 02 04 00 28 32 00 00 00 32 00 45 2e 4f 72 67 61 6e 20 20 33 63 63 63 32 32 32 00 00 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Organ 4 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha DX11 factory preset, bank C, voice 29 14 00 00 06 0f 1b 00 55 08 00 15 00 00 06 0f 00 00 56 08 06 18 01 00 07 0f 00 00 5c 08 00 17 01 00 06 0f 00 00 63 04 06 3f 23 00 19 00 52 0c 02 04 00 63 32 00 63 00 32 00 45 2e 4f 72 67 61 6e 20 20 34 63 63 63 32 32 32 00 00 00 40 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Farcheeza ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 21 1f 00 00 0b 0f 1b 00 5a 13 03 1f 00 00 0b 0f 00 00 63 08 03 1f 00 00 0b 0f 00 00 63 0d 03 1f 00 00 0b 0f 00 00 63 04 03 3f 23 00 12 00 42 0c 07 04 00 63 32 00 63 00 32 00 46 61 72 63 68 65 65 7a 61 20 63 63 63 32 32 32 00 40 00 50 00 50 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond * ** Controls: MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 00 0c 00 1c 40 61 0e 03 1c 19 00 0b 0f 14 40 63 04 06 1c 10 00 0c 0f 15 40 63 08 00 1c 06 00 0c 0f 14 40 63 00 03 2f 25 00 03 0b 3a 11 02 04 00 28 0b 40 00 00 32 00 48 61 6d 6d 6f 6e 64 20 20 2a 63 63 63 32 32 32 00 03 00 08 00 04 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 13 ** Name: Hammond..1 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 02 04 10 0a 05 00 22 52 04 13 1f 08 00 0d 0c 12 26 3d 0d 03 04 1f 00 0d 0f 0d 5d 46 04 1b 16 1f 00 0d 0f 00 7c 5f 04 03 41 23 0e 10 10 46 18 05 04 00 63 63 63 00 00 32 00 48 61 6d 6d 6f 6e 64 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..1 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 02 04 10 0a 05 00 22 52 04 13 1f 08 00 0d 0c 12 21 3d 0d 03 04 1f 00 0d 0f 0d 59 46 04 1b 16 1f 00 0d 0f 00 78 5f 04 03 41 23 0e 10 10 46 18 05 04 00 63 63 63 00 00 32 00 48 61 6d 6d 6f 6e 64 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 1f 00 0a 0b 19 03 4a 04 03 1f 1a 00 0d 0c 52 03 46 04 1a 1f 1f 00 0d 0f 19 06 46 04 12 1b 1e 00 09 0f 63 05 5b 08 03 03 25 00 07 00 31 0c 02 04 00 63 32 00 00 01 32 00 48 61 6d 6d 6f 6e 64 2e 2e 32 63 63 63 32 32 32 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 1f 00 0a 0b 19 00 4a 04 03 1f 1a 00 0d 0c 52 00 46 04 1a 1f 1f 00 0d 0f 19 00 46 04 12 1b 1e 00 09 0f 63 00 5b 08 03 03 25 00 07 00 31 0c 02 04 00 63 32 00 00 01 32 00 48 61 6d 6d 6f 6e 64 2e 2e 32 63 63 63 32 32 32 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0e 05 08 06 00 03 47 04 03 1f 1f 04 07 0e 00 04 5b 08 01 19 1f 00 07 0f 00 05 4e 0a 05 1f 1f 00 07 0e 00 45 63 04 04 0d 0e 00 14 18 3a 0c 02 04 00 63 32 00 32 00 32 00 48 61 6d 6d 6f 6e 64 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..3 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 05 08 06 00 00 47 04 03 1f 1f 04 07 0e 00 00 5b 08 01 19 1f 00 07 0f 00 00 4e 0a 05 1f 1f 00 07 0e 00 40 63 04 04 0d 0e 00 14 18 3a 0c 02 04 00 63 32 00 32 00 32 00 48 61 6d 6d 6f 6e 64 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..4 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 05 01 03 0d 2d 03 53 0a 13 1f 05 00 05 0b 2d 04 47 04 13 1f 08 00 03 0c 43 06 5c 19 1b 1c 06 04 04 07 00 05 63 04 13 3f 23 00 23 00 32 0c 06 04 00 63 32 00 32 00 32 00 48 61 6d 6d 6f 6e 64 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..4 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 01 03 0d 2d 00 53 0a 13 1f 05 00 05 0b 2d 01 47 04 13 1f 08 00 03 0c 43 01 5c 19 1b 1c 06 04 04 07 00 00 63 04 13 3f 23 00 23 00 32 0c 06 04 00 63 32 00 32 00 32 00 48 61 6d 6d 6f 6e 64 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0a 00 10 04 00 00 45 32 04 03 1d 0c 00 0f 0f 01 45 40 04 03 1a 17 00 0f 0f 51 05 56 08 0b 1e 12 00 0f 0f 10 04 63 08 0b 0c 1d 00 47 55 36 0c 0c 04 00 63 32 00 00 00 32 00 48 61 6d 6d 6f 6e 64 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..5 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0a 00 10 04 00 00 40 32 04 03 1d 0c 00 0f 0f 01 40 40 04 03 1a 17 00 0f 0f 51 00 56 08 0b 1e 12 00 0f 0f 10 00 63 08 0b 0c 1d 00 47 55 36 0c 0c 04 00 63 32 00 00 00 32 00 48 61 6d 6d 6f 6e 64 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 1f 00 0a 0b 19 03 54 04 13 1f 1a 00 0d 0c 52 05 50 04 0a 1f 1f 00 0d 0f 19 05 43 0a 1a 1b 1e 00 09 0f 63 05 63 04 03 0b 1f 00 0f 00 32 0c 02 04 00 63 32 00 00 01 32 00 48 61 6d 6d 6f 6e 64 2e 2e 36 63 63 63 32 32 32 00 30 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..6 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 1f 00 0a 0b 19 00 54 04 13 1f 1a 00 0d 0c 52 00 50 04 0a 1f 1f 00 0d 0f 19 00 43 0a 1a 1b 1e 00 09 0f 63 00 63 04 03 0b 1f 00 0f 00 32 0c 02 04 00 63 32 00 00 01 32 00 48 61 6d 6d 6f 6e 64 2e 2e 36 63 63 63 32 32 32 00 30 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..7 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 17 0d 10 04 00 00 45 46 0a 03 1d 0c 00 0f 0f 01 45 40 08 03 1a 17 00 0f 0f 51 04 55 0d 0b 1e 12 00 0f 0f 10 05 63 04 0b 04 21 00 15 23 26 0c 0c 04 00 63 32 00 00 00 32 00 48 61 6d 6d 6f 6e 64 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hammond..7 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 17 0d 10 04 00 00 40 46 0a 03 1d 0c 00 0f 0f 01 40 40 08 03 1a 17 00 0f 0f 51 00 55 0d 0b 1e 12 00 0f 0f 10 00 63 04 0b 04 21 00 15 23 26 0c 0c 04 00 63 32 00 00 00 32 00 48 61 6d 6d 6f 6e 64 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HamondLead ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 16 04 0a 0f 00 04 58 00 00 12 15 04 0a 0f 00 05 63 00 08 12 15 04 0a 0f 00 06 63 00 0e 12 15 04 0a 0f 00 04 63 00 0b 06 24 0f 35 00 32 18 02 04 00 63 63 00 00 00 32 00 48 61 6d 6f 6e 64 4c 65 61 64 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HamondLead ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 16 04 0a 0f 00 01 58 00 00 12 15 04 0a 0f 00 00 63 00 08 12 15 04 0a 0f 00 00 63 00 0e 12 15 04 0a 0f 00 00 63 00 0b 06 24 0f 35 00 32 18 02 04 00 63 63 00 00 00 32 00 48 61 6d 6f 6e 64 4c 65 61 64 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hmnd LS f1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 09 07 0f 0f 00 03 63 0a 05 1b 18 10 0f 0f 00 44 63 08 01 1c 18 07 0f 0f 0a 03 63 08 05 1c 18 07 0f 0f 00 05 63 04 02 2f 30 00 34 63 16 0c 07 04 00 63 63 00 00 00 32 00 48 6d 6e 64 20 4c 53 20 66 31 63 63 63 32 32 32 00 00 00 40 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hmnd LS f1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 09 07 0f 0f 00 00 63 0a 05 1b 18 10 0f 0f 00 40 63 08 01 1c 18 07 0f 0f 0a 00 63 08 05 1c 18 07 0f 0f 00 00 63 04 02 2f 30 00 34 63 16 0c 07 04 00 63 63 00 00 00 32 00 48 6d 6e 64 20 4c 53 20 66 31 63 63 63 32 32 32 00 00 00 40 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hmnd LS f2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 18 07 0d 0f 00 03 63 04 05 1b 07 07 0d 0f 00 04 60 04 01 1c 18 07 0d 0f 0a 04 5f 04 05 1c 18 07 0d 0f 00 05 60 00 02 07 1b 00 0f 63 36 18 07 04 00 63 63 00 00 00 32 00 48 6d 6e 64 20 4c 53 20 66 32 63 63 63 32 32 32 00 00 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hmnd LS f2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 18 07 0d 0f 00 00 63 04 05 1b 07 07 0d 0f 00 00 60 04 01 1c 18 07 0d 0f 0a 00 5f 04 05 1c 18 07 0d 0f 00 00 60 00 02 07 1b 00 0f 63 36 18 07 04 00 63 63 00 00 00 32 00 48 6d 6e 64 20 4c 53 20 66 32 63 63 63 32 32 32 00 00 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hmnd LS s1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 07 01 0d 0f 00 05 5c 04 05 1b 07 07 0d 0f 00 05 5f 08 01 1c 18 07 0d 0f 0a 05 5f 08 05 1c 18 07 0d 0f 00 05 5f 04 02 27 30 00 0e 00 22 18 07 04 00 63 63 00 00 00 32 00 48 6d 6e 64 20 4c 53 20 73 31 63 63 63 32 32 32 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hmnd LS s1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 07 01 0d 0f 00 00 5c 04 05 1b 07 07 0d 0f 00 00 5f 08 01 1c 18 07 0d 0f 0a 00 5f 08 05 1c 18 07 0d 0f 00 00 5f 04 02 27 30 00 0e 00 22 18 07 04 00 63 63 00 00 00 32 00 48 6d 6e 64 20 4c 53 20 73 31 63 63 63 32 32 32 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hornopean ** Description: A loud, obnoxious organ stop ** Controls: MW (LFO pitch) ** Source: Art Hitzeman 12 1f 00 07 0f 19 00 58 00 03 10 1f 00 06 0f 19 00 5a 04 03 17 1f 00 07 0f 19 00 5a 04 03 0e 1f 00 06 0f 19 00 5a 04 03 45 00 01 04 00 62 18 02 04 00 28 32 00 00 00 32 00 48 6f 72 6e 6f 70 65 61 6e 20 63 63 63 32 32 32 00 00 00 70 00 60 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jazz Organ ** Description: An organ with jazz potential ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 0a 00 0b 0f 00 00 52 0a 06 1f 1f 00 0a 0f 00 00 5b 04 00 1f 12 0e 0a 0f 00 00 58 13 06 1f 1f 00 0a 0f 00 40 4f 04 06 1f 23 00 02 00 4e 0c 07 04 00 63 32 32 32 00 32 00 4a 61 7a 7a 20 4f 72 67 61 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Leslie * ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 1f 07 0c 0f 42 40 59 33 03 1b 1f 00 0b 0f 00 40 63 0a 00 1b 1f 00 0b 0f 00 40 56 1c 06 1b 1f 00 0b 0f 00 40 5c 04 06 1f 22 00 05 17 66 05 02 04 00 28 00 37 00 00 32 00 4c 65 73 6c 69 65 20 20 20 2a 63 63 63 32 32 32 00 07 00 10 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Organ 1 ** Description: An electric organ ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: GLIB (Michael Kesti and others) 1f 0a 01 0b 00 00 7d 5f 0a 06 1f 1f 01 0a 0f 00 7f 63 08 00 1f 1f 01 0a 0f 00 7f 63 0a 06 1f 1f 01 0a 0f 00 7e 63 04 06 1f 27 00 14 1c 36 0c 04 04 00 63 32 32 00 00 32 00 4f 72 67 61 6e 20 20 20 20 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Organ 2 ** Description: An electric organ ** Source: GLIB (Michael Kesti and others) 1f 0f 00 06 0f 00 00 40 00 03 1f 0f 00 09 0f 00 00 46 04 03 1f 0f 00 06 0f 00 00 5a 00 03 1f 0f 00 09 0f 00 00 5a 04 03 04 00 00 00 00 02 18 00 04 00 63 32 32 32 00 32 00 4f 72 67 61 6e 20 32 20 20 20 63 63 63 32 32 32 00 00 00 10 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Organ 3 ** Description: An electric organ ** Source: GLIB (Michael Kesti and others) 1f 0f 00 06 0f 4e 00 50 00 03 1f 0f 00 09 0f 36 00 46 04 03 1f 0f 00 06 0f 00 00 5a 00 03 1f 0f 00 09 0f 00 00 5a 04 03 04 00 00 00 00 02 18 00 04 00 63 32 32 32 00 32 00 4f 72 67 61 6e 20 33 20 20 20 63 63 63 32 32 32 00 00 00 10 00 10 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Organ ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 06 07 00 00 03 4f 3d 0b 1d 07 07 08 0b 00 02 63 04 0b 1d 07 07 08 0b 00 05 63 04 0e 1d 07 07 08 0b 00 04 5a 04 0b 2e 63 21 00 00 40 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 20 4f 72 67 61 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Organ ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 06 07 00 00 00 4f 3d 0b 1d 07 07 08 0b 00 00 63 04 0b 1d 07 07 08 0b 00 00 63 04 0e 1d 07 07 08 0b 00 00 5a 04 0b 2e 63 21 00 00 40 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 20 4f 72 67 61 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Organ2 ** Name: P Organ..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 1f 00 06 0f 00 0d 63 13 03 1a 1f 00 06 0f 00 0d 63 08 03 1a 1f 00 06 0f 00 0d 63 0a 03 1a 1f 00 06 0f 00 0d 63 04 03 2f 21 19 1d 00 52 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 1f 00 06 0f 00 08 63 13 03 1a 1f 00 06 0f 00 08 63 08 03 1a 1f 00 06 0f 00 08 63 0a 03 1a 1f 00 06 0f 00 08 63 04 03 2f 21 19 1d 00 52 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 1f 00 06 0f 00 04 45 19 03 1a 1f 00 06 0f 00 06 50 04 03 1a 1f 00 06 0f 00 05 63 04 03 1a 1f 00 06 0f 00 05 63 00 03 34 21 19 1d 00 52 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 1f 00 06 0f 00 01 45 19 03 1a 1f 00 06 0f 00 01 50 04 03 1a 1f 00 06 0f 00 00 63 04 03 1a 1f 00 06 0f 00 00 63 00 03 34 21 19 1d 00 52 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 1f 13 08 0e 00 03 43 19 03 1a 1f 00 06 0f 00 03 44 04 03 10 07 07 09 0f 00 05 63 08 03 1a 1f 00 06 0f 00 05 63 08 03 34 21 19 1d 00 52 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 33 63 63 63 32 32 32 00 30 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..3 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 14 1f 13 08 0e 00 00 43 19 03 1a 1f 00 06 0f 00 00 44 04 03 10 07 07 09 0f 00 00 63 08 03 1a 1f 00 06 0f 00 00 63 08 03 34 21 19 1d 00 52 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 33 63 63 63 32 32 32 00 30 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 1f 00 08 0f 00 05 50 00 03 16 1f 00 07 0f 00 06 4f 04 03 0d 1f 00 06 0f 00 06 63 0d 03 15 1f 00 07 0f 00 05 63 04 03 04 23 41 00 00 12 18 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 34 63 63 63 32 32 32 00 60 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 1f 00 08 0f 00 01 50 00 03 16 1f 00 07 0f 00 01 4f 04 03 0d 1f 00 06 0f 00 01 63 0d 03 15 1f 00 07 0f 00 00 63 04 03 04 23 41 00 00 12 18 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 34 63 63 63 32 32 32 00 60 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 05 06 0f 00 02 4c 0d 03 1d 1f 05 08 0f 00 04 5a 04 02 13 1f 07 07 0f 00 05 63 08 03 18 07 06 0a 0f 00 05 63 08 04 04 23 41 00 00 12 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 35 63 63 63 32 32 32 00 40 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 05 06 0f 00 01 4c 0d 03 1d 1f 05 08 0f 00 01 5a 04 02 13 1f 07 07 0f 00 00 63 08 03 18 07 06 0a 0f 00 00 63 08 04 04 23 41 00 00 12 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 35 63 63 63 32 32 32 00 40 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 06 03 06 0f 00 05 4c 1f 03 12 03 03 08 0f 00 05 63 08 02 12 07 04 07 0f 00 05 63 08 03 16 03 04 0a 0f 00 05 63 04 04 2e 23 41 00 00 12 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 36 63 63 63 32 32 32 00 10 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..6 ** Source: from a library of patches collected by Duane Bowker, author unknown 16 06 03 06 0f 00 00 4c 1f 03 12 03 03 08 0f 00 00 63 08 02 12 07 04 07 0f 00 00 63 08 03 16 03 04 0a 0f 00 00 63 04 04 2e 23 41 00 00 12 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 36 63 63 63 32 32 32 00 10 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 07 03 07 0f 00 05 4a 1f 03 1f 05 03 09 0f 00 06 54 00 02 12 05 04 08 0f 00 06 63 08 03 0d 05 04 0a 0f 00 05 63 00 04 04 23 41 00 00 12 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 37 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 07 03 07 0f 00 01 4a 1f 03 1f 05 03 09 0f 00 01 54 00 02 12 05 04 08 0f 00 00 63 08 03 0d 05 04 0a 0f 00 00 63 00 04 04 23 41 00 00 12 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 37 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..8 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 1f 00 09 0f 00 06 4c 2a 0b 1a 1f 00 06 0f 00 06 5b 00 04 13 1f 00 0a 0f 00 05 63 08 03 10 1f 00 06 0f 00 45 63 00 04 24 21 19 16 23 46 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 38 63 63 63 32 32 32 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..8 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 1f 00 09 0f 00 01 4c 2a 0b 1a 1f 00 06 0f 00 01 5b 00 04 13 1f 00 0a 0f 00 00 63 08 03 10 1f 00 06 0f 00 40 63 00 04 24 21 19 16 23 46 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 38 63 63 63 32 32 32 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..9 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 06 03 07 0f 00 06 44 13 03 12 03 03 08 0f 00 06 57 08 02 12 07 04 09 0f 00 05 63 0d 03 10 03 04 0a 0f 00 05 63 04 04 2c 23 41 00 00 12 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 39 63 63 63 32 32 32 00 00 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ..9 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 06 03 07 0f 00 01 44 13 03 12 03 03 08 0f 00 01 57 08 02 12 07 04 09 0f 00 00 63 0d 03 10 03 04 0a 0f 00 00 63 04 04 2c 23 41 00 00 12 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 2e 39 63 63 63 32 32 32 00 00 00 20 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.10 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 00 00 0a 0f 3e 46 4e 0d 06 10 00 00 0a 0f 63 05 63 08 06 18 00 00 0a 0f 00 06 63 08 06 15 00 00 0a 0f 00 05 63 04 01 06 23 00 00 38 16 18 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 30 63 63 63 32 32 32 00 00 00 00 00 60 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.10 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 00 00 0a 0f 3e 41 4e 0d 06 10 00 00 0a 0f 63 00 63 08 06 18 00 00 0a 0f 00 01 63 08 06 15 00 00 0a 0f 00 00 63 04 01 06 23 00 00 38 16 18 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 30 63 63 63 32 32 32 00 00 00 00 00 60 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.11 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 00 00 0a 0f 00 03 47 08 02 18 00 00 05 0f 5b 04 63 00 00 0f 00 00 08 0f 00 05 63 08 10 0e 00 00 05 0f 00 05 63 00 16 3c 23 00 00 00 02 0c 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 31 63 63 63 32 32 32 00 00 00 70 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.11 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 00 00 0a 0f 00 01 47 08 02 18 00 00 05 0f 5b 01 63 00 00 0f 00 00 08 0f 00 00 63 08 10 0e 00 00 05 0f 00 00 63 00 16 3c 23 00 00 00 02 0c 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 31 63 63 63 32 32 32 00 00 00 70 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.12 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 07 01 0a 0f 00 03 56 13 05 1b 07 01 07 0f 5b 05 4f 04 02 12 07 01 09 0f 00 03 61 08 12 0d 07 07 05 0f 00 05 63 00 14 2c 23 00 00 00 02 0c 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.12 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 07 01 0a 0f 00 01 56 13 05 1b 07 01 07 0f 5b 00 4f 04 02 12 07 01 09 0f 00 00 61 08 12 0d 07 07 05 0f 00 00 63 00 14 2c 23 00 00 00 02 0c 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.13 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 07 00 0a 0f 43 03 4f 13 05 1f 07 00 07 03 5b 04 52 04 02 12 07 01 09 0f 2e 05 5f 08 02 0d 07 00 05 0f 00 05 63 00 04 2c 23 00 00 00 02 0c 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 33 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.13 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 07 00 0a 0f 43 01 4f 13 05 1f 07 00 07 03 5b 01 52 04 02 12 07 01 09 0f 2e 00 5f 08 02 0d 07 00 05 0f 00 00 63 00 04 2c 23 00 00 00 02 0c 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 33 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.14 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 07 00 07 0f 43 04 59 0d 05 15 04 00 08 0f 15 06 57 00 02 12 07 01 08 0f 2e 04 62 04 02 11 03 00 06 0f 00 05 63 00 04 2c 23 00 16 00 22 18 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 34 63 63 63 32 32 32 00 00 00 50 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.14 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 07 00 07 0f 43 00 59 0d 05 15 04 00 08 0f 15 01 57 00 02 12 07 01 08 0f 2e 00 62 04 02 11 03 00 06 0f 00 00 63 00 04 2c 23 00 16 00 22 18 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 34 63 63 63 32 32 32 00 00 00 50 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.15 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 04 00 07 0f 43 03 58 19 0c 12 04 00 08 0f 5b 04 5c 04 0a 0f 04 00 08 0f 2e 05 63 08 02 0f 04 00 06 0f 00 05 63 00 03 2c 23 00 00 00 02 18 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 35 63 63 63 32 32 32 00 30 00 30 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.15 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 13 04 00 07 0f 43 01 58 19 0c 12 04 00 08 0f 5b 01 5c 04 0a 0f 04 00 08 0f 2e 00 63 08 02 0f 04 00 06 0f 00 00 63 00 03 2c 23 00 00 00 02 18 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 35 63 63 63 32 32 32 00 30 00 30 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.16 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 04 00 07 0f 43 04 47 19 0c 12 04 00 08 0f 5b 03 4d 04 0a 0f 04 00 08 0f 2e 06 63 0d 02 0f 04 00 06 0f 00 05 63 00 03 2e 23 00 00 00 02 18 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.16 ** Source: from a library of patches collected by Duane Bowker, author unknown 13 04 00 07 0f 43 00 47 19 0c 12 04 00 08 0f 5b 00 4d 04 0a 0f 04 00 08 0f 2e 00 63 0d 02 0f 04 00 06 0f 00 00 63 00 03 2e 23 00 00 00 02 18 00 04 00 63 00 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.17 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 1f 00 06 0f 00 06 51 13 03 1a 1f 00 06 0f 00 06 45 08 03 1a 1f 00 06 0f 00 07 50 0a 03 1a 1f 00 06 0f 00 05 63 04 03 29 21 19 1d 00 52 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 37 63 63 63 32 32 32 00 00 00 10 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P Organ.17 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 1f 00 06 0f 00 01 51 13 03 1a 1f 00 06 0f 00 01 45 08 03 1a 1f 00 06 0f 00 02 50 0a 03 1a 1f 00 06 0f 00 00 63 04 03 29 21 19 1d 00 52 0c 07 04 00 63 63 00 00 00 32 00 50 20 4f 72 67 61 6e 2e 31 37 63 63 63 32 32 32 00 00 00 10 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P.Organ 1 ** Source: Yamaha DX11 factory preset, bank C, voice 30 1e 1f 00 06 0f 0a 00 4c 00 0b 0a 1f 00 0a 0f 19 00 4c 10 0e 14 1f 00 0a 0f 00 00 5a 04 0b 13 1f 00 0a 0f 19 00 4c 0a 08 16 46 00 00 00 22 18 02 04 00 28 32 00 00 00 32 00 50 2e 4f 72 67 61 6e 20 20 31 63 63 63 32 32 32 00 20 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P.Organ 1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1f 00 06 0f 0a 00 4c 00 0b 0d 1f 00 0a 0f 19 00 4c 10 0e 14 1f 00 0a 0f 00 00 5a 04 0b 13 1f 00 0a 0f 19 00 4c 0a 08 16 46 00 00 00 22 18 02 04 00 28 32 00 00 00 32 00 50 2e 4f 72 67 61 6e 20 20 31 63 63 63 32 32 32 00 20 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P.Organ 2 ** Source: Yamaha DX11 factory preset, bank C, voice 31 12 1f 00 09 0f 00 00 5e 04 03 10 1f 00 08 0f 00 00 60 08 05 17 1f 00 09 0f 00 00 5f 0a 06 0e 1f 00 08 0f 00 00 5e 00 00 37 23 01 00 00 62 18 02 04 00 28 32 00 00 00 32 00 50 2e 4f 72 67 61 6e 20 20 32 63 63 63 32 32 32 00 20 00 30 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P.Organ 2 ** Source: from a library of patches collected by Duane Bowker, author unknown 12 1f 00 09 0f 00 00 5e 04 03 10 1f 00 08 0f 00 00 60 08 05 17 1f 00 09 0f 00 00 5f 0d 06 0e 1f 00 08 0f 00 00 5e 00 00 37 23 01 00 00 62 18 02 04 00 28 32 00 00 00 32 00 50 2e 4f 72 67 61 6e 20 20 32 63 63 63 32 32 32 00 20 00 30 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P.Organ 1' ** Description: DX11 factory preset 'P.Organ 1' with slower decay ** Source: Art Hitzeman 1e 1f 00 05 0f 0a 00 4c 00 0b 0a 1f 00 06 0f 19 00 4c 10 0e 14 1f 00 06 0f 00 00 5a 04 0b 13 1f 00 05 0f 19 00 4c 0a 08 16 46 00 00 00 22 18 02 04 00 28 32 00 00 00 32 00 50 2e 4f 72 67 61 6e 20 31 27 63 63 63 32 32 32 00 20 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P.Organ 2' ** Description: DX11 factory preset 'P.Organ 2' with slower decay ** Source: Art Hitzeman 12 1f 00 07 0f 00 00 5e 04 03 10 1f 00 06 0f 00 00 60 08 05 17 1f 00 07 0f 00 00 5f 0d 06 0e 1f 00 06 0f 00 00 5e 00 00 37 23 01 00 00 62 18 02 04 00 28 32 00 00 00 32 00 50 2e 4f 72 67 61 6e 20 32 27 63 63 63 32 32 32 00 20 00 30 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P.Organ 3 ** Description: A pipe organ ** Source: Art Hitzeman 12 1f 00 06 0f 00 00 5e 04 03 10 1f 00 05 0f 09 00 60 08 15 17 1f 00 07 0f 40 00 5f 0a 0e 0e 1f 00 05 0f 00 00 5e 00 10 37 23 01 00 00 62 18 02 04 00 28 32 00 00 00 32 00 50 2e 4f 72 67 61 6e 20 33 20 63 63 63 32 32 32 00 30 00 40 00 00 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P.Organ 4 ** Description: Another pipe organ ** Source: Art Hitzeman 12 1f 00 07 0f 00 00 54 04 03 10 1f 00 06 0f 63 00 63 04 03 17 1f 00 07 0f 00 00 55 08 06 0e 1f 00 06 0f 00 00 55 00 00 2f 23 01 00 00 62 18 02 04 00 28 32 00 00 00 32 00 50 2e 4f 72 67 61 6e 20 34 20 63 63 63 32 32 32 00 10 00 10 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P.Organ 5 ** Description: Yet another pipe organ ** Source: Art Hitzeman 12 1f 00 07 0f 00 00 5e 00 03 10 1f 00 06 0f 00 00 60 04 05 17 1f 00 07 0f 00 00 5f 08 06 0e 1f 00 06 0f 00 00 5e 00 00 37 23 01 00 00 62 18 02 04 00 28 32 00 00 00 32 00 50 2e 4f 72 67 61 6e 20 35 20 63 63 63 32 32 32 00 00 00 40 00 40 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PercOrgan ** Controls: MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 17 1f 0b 01 0b 08 18 40 5f 0a 0e 1f 1f 00 0a 0f 00 40 5f 04 00 1f 1f 00 0a 0f 00 40 63 05 06 1f 1f 00 0a 0f 00 40 5f 00 08 1f 20 00 03 00 5a 18 04 04 00 63 32 32 00 00 32 00 50 65 72 63 4f 72 67 61 6e 20 63 63 63 32 32 32 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pipe Organ ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 16 00 00 0a 0f 00 00 63 19 03 11 00 00 0a 0f 00 00 63 0d 03 17 00 00 0a 0f 00 00 63 04 03 17 00 00 0a 0f 00 00 63 00 03 07 1e 00 00 00 42 18 04 04 00 28 32 00 00 00 32 00 50 69 70 65 20 4f 72 67 61 6e 63 63 63 32 32 32 00 70 00 70 00 40 00 50 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PumpOrgan ** Controls: MW (LFO amplitude) ** Source: Yamaha TX81Z factory preset, bank A, voice 19 1f 1f 00 06 0f 1e 00 4d 17 03 1f 1f 00 05 0f 00 00 50 05 04 1f 1f 00 05 0f 00 00 23 04 06 0d 1f 00 09 0f 00 40 5a 04 03 38 14 00 00 0e 3e 11 04 04 00 63 32 0a 00 00 32 00 50 75 6d 70 4f 72 67 61 6e 20 63 63 63 32 32 32 00 25 00 31 00 18 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RevrbOrgan ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 03 00 05 0e 23 03 4e 05 05 0e 03 00 05 0e 23 03 4e 04 03 0b 1f 00 05 0f 00 01 63 05 06 0c 1f 00 05 0f 00 01 63 04 02 04 19 06 04 00 52 05 02 04 00 63 0f 00 0c 00 32 00 52 65 76 72 62 4f 72 67 61 6e 63 63 63 32 32 32 00 01 00 08 00 01 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Small Pipe ** Source: Yamaha TX81Z factory preset, bank A, voice 22 16 00 00 0a 0f 3e 00 4b 19 06 11 00 00 0a 0f 63 00 57 0d 06 17 00 00 0a 0f 00 00 58 04 06 17 00 00 0a 0f 00 00 63 00 01 07 1e 00 00 00 42 18 04 04 00 63 63 00 00 00 32 00 53 6d 61 6c 6c 20 50 69 70 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Organ 1 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: Yamaha DX11 factory preset, bank A, voice 21 1f 12 00 07 0a 14 02 50 0a 0b 1f 14 00 09 0e 00 42 55 08 13 1f 14 00 09 06 00 03 63 0a 03 1f 1f 00 08 0f 00 02 63 04 0b 06 1e 0a 11 1a 5e 18 02 04 00 63 32 23 00 00 32 00 53 79 2e 4f 72 67 61 6e 20 31 63 63 63 32 32 32 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Organ 1 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 00 07 0d 14 02 50 0d 0b 1f 14 00 09 0e 00 42 55 08 13 1f 14 00 09 06 00 03 63 0d 03 1f 1f 00 08 0f 00 02 63 04 0b 06 1e 0d 11 1a 5e 18 02 04 00 63 32 23 00 00 32 00 53 79 2e 4f 72 67 61 6e 20 31 63 63 63 32 32 32 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Organ 2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 22 1b 00 00 0f 0f 1b 00 24 0a 03 1e 1c 00 04 01 00 00 44 08 02 1f 00 00 0a 0b 00 00 49 0a 04 1f 06 00 09 0f 00 00 53 04 04 3a 23 00 27 00 42 18 02 04 00 63 63 00 63 00 32 00 53 79 2e 4f 72 67 61 6e 20 32 63 63 63 32 32 32 00 00 00 20 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Organ 2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 00 00 0f 0f 1b 00 24 0d 03 1e 1c 00 04 01 00 00 44 08 02 1f 00 00 0a 0b 00 00 49 0d 04 1f 06 00 09 0f 00 00 53 04 04 3a 23 00 27 00 42 18 02 04 00 63 63 00 63 00 32 00 53 79 2e 4f 72 67 61 6e 20 32 63 63 63 32 32 32 00 00 00 20 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tocc Organ ** Description: A pipe organ intended for Bach's Toccata & Fugue in D Minor ** (the piece the villain always plays in those horror movies) ** Source: Art Hitzeman 12 1f 00 06 0f 3c 00 5e 04 03 10 1f 00 06 0f 32 00 60 08 05 17 1f 00 07 0f 32 00 5f 0d 06 0e 1f 00 06 0f 3c 00 5e 00 00 37 23 01 00 00 62 0c 02 04 00 28 32 00 00 00 32 00 54 6f 63 63 20 4f 72 67 61 6e 63 63 63 32 32 32 00 20 00 30 00 20 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: VelocOrgan ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 07 00 0f 0d 32 07 2b 11 00 1c 08 00 0f 0d 41 07 49 12 04 19 08 00 0f 0d 41 07 5d 05 06 1c 1f 00 0a 0e 00 03 63 04 09 03 1d 15 05 00 52 05 02 04 00 63 23 00 00 00 32 00 56 65 6c 6f 63 4f 72 67 61 6e 63 63 63 32 32 32 00 08 00 04 00 11 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wirlitzer* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 07 06 07 0e 12 06 4a 08 08 1d 07 06 07 0e 00 06 54 00 0b 1d 06 06 07 0e 00 00 63 00 0e 1d 06 06 07 0e 00 02 63 04 08 34 23 00 00 00 32 18 02 04 00 28 63 00 00 00 32 00 57 69 72 6c 69 74 7a 65 72 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 14 * SUBCATEGORY Piano1 ** Name: AC Piano.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 03 02 04 04 63 06 58 0a 13 16 09 06 06 0d 00 04 4d 04 0b 1b 06 01 04 04 63 06 63 04 0b 16 09 06 06 0d 00 07 62 04 0b 2c 23 00 00 00 02 0c 02 04 00 63 32 00 32 00 32 00 41 43 20 50 69 61 6e 6f 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 03 02 04 04 63 01 58 0a 13 16 09 06 06 0d 00 01 4d 04 0b 1b 06 01 04 04 63 00 63 04 0b 16 09 06 06 0d 00 00 62 04 0b 2c 23 00 00 00 02 0c 02 04 00 63 32 00 32 00 32 00 41 43 20 50 69 61 6e 6f 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 05 02 04 04 63 04 3b 19 13 14 05 04 06 0d 00 05 50 04 1b 14 02 02 06 0d 00 05 3d 10 0b 15 07 06 06 0c 00 06 63 04 0b 22 23 00 00 00 02 0c 02 04 00 63 32 00 32 00 32 00 41 43 20 50 69 61 6e 6f 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 05 02 04 04 63 01 3b 19 13 14 05 04 06 0d 00 01 50 04 1b 14 02 02 06 0d 00 01 3d 10 0b 15 07 06 06 0c 00 00 63 04 0b 22 23 00 00 00 02 0c 02 04 00 63 32 00 32 00 32 00 41 43 20 50 69 61 6e 6f 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 01 01 03 00 1b 05 49 04 0d 14 06 05 04 09 42 06 53 04 0d 12 06 0b 03 00 42 05 44 10 09 18 06 05 05 09 01 46 60 04 0a 1a 23 0a 00 06 56 18 04 04 00 63 32 00 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 01 01 03 00 1b 02 49 04 0d 14 06 05 04 09 42 01 53 04 0d 12 06 0b 03 00 42 01 44 10 09 18 06 05 05 09 01 40 60 04 0a 1a 23 0a 00 06 56 18 04 04 00 63 32 00 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 01 01 05 00 32 44 4c 04 0d 19 09 09 05 0f 01 04 53 04 06 18 06 05 06 09 16 45 62 04 09 19 09 09 05 0f 01 45 63 04 00 2c 19 00 00 00 66 18 01 04 00 63 00 63 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 34 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 01 01 05 00 32 41 4c 04 0d 19 09 09 05 0f 01 01 53 04 06 18 06 05 06 09 16 40 62 04 09 19 09 09 05 0f 01 40 63 04 00 2c 19 00 00 00 66 18 01 04 00 63 00 63 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 34 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 07 00 07 00 00 04 49 00 08 19 1f 00 06 0f 00 05 4b 00 06 18 09 00 07 00 00 05 43 00 00 1a 05 06 05 0a 01 06 63 04 0e 03 00 00 00 00 62 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 07 00 07 00 00 01 49 00 08 19 1f 00 06 0f 00 01 4b 00 06 18 09 00 07 00 00 00 43 00 00 1a 05 06 05 0a 01 01 63 04 0e 03 00 00 00 00 62 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 03 05 0d 2d 05 49 04 03 1f 05 01 05 0f 63 05 4c 0a 03 1f 04 02 05 0e 63 05 47 04 0b 1c 1e 06 06 0f 00 06 5d 04 13 3a 23 00 00 00 62 0c 00 04 00 63 00 00 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 08 03 05 0d 2d 01 49 04 03 1f 05 01 05 0f 63 01 4c 0a 03 1f 04 02 05 0e 63 01 47 04 0b 1c 1e 06 06 0f 00 00 5d 04 13 3a 23 00 00 00 62 0c 00 04 00 63 00 00 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.7 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 08 00 03 00 00 04 3a 08 0c 14 06 07 04 09 23 05 48 04 15 14 07 00 03 00 00 06 36 0a 02 18 08 06 05 09 0f 06 5d 04 0b 7a 21 3c 0a 00 32 18 06 04 00 63 32 00 32 00 32 00 41 43 20 50 69 61 6e 6f 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.7 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 16 08 00 03 00 00 00 3a 08 0c 14 06 07 04 09 23 00 48 04 15 14 07 00 03 00 00 00 36 0a 02 18 08 06 05 09 0f 00 5d 04 0b 7a 21 3c 0a 00 32 18 06 04 00 63 32 00 32 00 32 00 41 43 20 50 69 61 6e 6f 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 09 01 03 0e 32 43 4f 0a 11 16 09 01 03 0e 32 1d 5d 04 16 18 07 05 05 0d 00 45 63 04 0b 18 07 05 05 0d 00 75 60 04 0b 24 19 00 00 00 66 18 01 04 00 63 00 63 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.8 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 09 01 03 0e 32 41 4f 0a 11 16 09 01 03 0e 32 01 5d 04 16 18 07 05 05 0d 00 40 63 04 0b 18 07 05 05 0d 00 40 60 04 0b 24 19 00 00 00 66 18 01 04 00 63 00 63 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.9 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 09 07 07 0d 00 45 3a 10 0b 1c 09 07 07 0d 00 05 4a 0a 0c 1c 09 07 07 0d 00 43 4b 04 0a 1c 09 07 07 0d 00 45 63 04 0b 23 19 00 00 00 66 18 01 04 00 63 00 63 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 39 63 63 63 32 32 32 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano.9 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 09 07 07 0d 00 42 3a 10 0b 1c 09 07 07 0d 00 02 4a 0a 0c 1c 09 07 07 0d 00 40 4b 04 0a 1c 09 07 07 0d 00 41 63 04 0b 23 19 00 00 00 66 18 01 04 00 63 00 63 00 00 32 00 41 43 20 50 69 61 6e 6f 2e 39 63 63 63 32 32 32 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano10 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 01 01 03 00 34 04 4b 04 0d 16 01 01 04 0c 42 04 52 00 0b 18 05 01 03 00 63 06 45 16 10 14 08 01 06 0c 00 05 63 04 13 32 23 00 00 00 02 18 00 04 00 63 32 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano10 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 18 01 01 03 00 34 01 4b 04 0d 16 01 01 04 0c 42 01 52 00 0b 18 05 01 03 00 63 01 45 16 10 14 08 01 06 0c 00 00 63 04 13 32 23 00 00 00 02 18 00 04 00 63 32 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano11 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 03 05 0d 00 04 49 04 0d 1f 04 02 05 0e 63 04 4a 0a 01 1f 05 04 05 0d 63 06 50 10 04 1c 1e 04 06 0f 00 05 5c 04 13 2a 23 00 00 00 62 0c 00 04 00 63 00 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 31 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano11 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 08 03 05 0d 00 01 49 04 0d 1f 04 02 05 0e 63 01 4a 0a 01 1f 05 04 05 0d 63 01 50 10 04 1c 1e 04 06 0f 00 00 5c 04 13 2a 23 00 00 00 62 0c 00 04 00 63 00 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 31 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano12 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 0c 09 09 06 52 45 46 0d 03 1b 05 06 09 07 27 04 41 04 13 1c 06 06 07 0c 11 45 4e 04 0b 1c 07 07 07 09 00 46 60 04 0b 1b 19 00 00 00 66 18 01 04 00 63 00 63 00 00 32 00 41 43 20 50 69 61 6e 6f 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano12 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 18 0c 09 09 06 52 42 46 0d 03 1b 05 06 09 07 27 01 41 04 13 1c 06 06 07 0c 11 40 4e 04 0b 1c 07 07 07 09 00 40 60 04 0b 1b 19 00 00 00 66 18 01 04 00 63 00 63 00 00 32 00 41 43 20 50 69 61 6e 6f 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano13 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 09 00 07 0d 00 04 46 0a 0b 19 09 00 06 0d 00 04 49 04 0c 18 09 00 07 0d 00 06 4c 04 0b 1a 09 09 07 0d 00 05 63 04 0b 2a 00 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 33 63 63 63 32 32 32 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano13 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 09 00 07 0d 00 01 46 0a 0b 19 09 00 06 0d 00 01 49 04 0c 18 09 00 07 0d 00 01 4c 04 0b 1a 09 09 07 0d 00 00 63 04 0b 2a 00 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 33 63 63 63 32 32 32 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano14 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 09 06 07 0d 00 02 38 0a 0b 1b 09 06 07 0d 00 04 46 10 0c 1b 09 06 07 0d 00 05 4c 04 0b 1b 09 06 07 0d 00 05 5d 04 0b 2a 00 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 34 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano14 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 09 06 07 0d 00 01 38 0a 0b 1b 09 06 07 0d 00 02 46 10 0c 1b 09 06 07 0d 00 01 4c 04 0b 1b 09 06 07 0d 00 00 5d 04 0b 2a 00 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 34 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano15 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 06 00 06 0e 00 05 2f 19 0e 13 04 04 06 0d 00 05 43 04 0a 13 04 04 06 0d 00 07 3e 0d 0c 15 07 06 06 0c 00 06 63 04 0b 2a 00 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano15 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 06 00 06 0e 00 01 2f 19 0e 13 04 04 06 0d 00 01 43 04 0a 13 04 04 06 0d 00 01 3e 0d 0c 15 07 06 06 0c 00 01 63 04 0b 2a 00 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano16 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 0d 05 07 0d 00 04 4c 0a 0c 13 04 03 06 0d 13 04 4e 04 0a 13 04 04 06 0d 2b 06 2d 1c 0b 15 07 06 06 0c 00 05 5d 04 0c 2a 00 00 00 00 02 0c 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano16 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 18 0d 05 07 0d 00 01 4c 0a 0c 13 04 03 06 0d 13 01 4e 04 0a 13 04 04 06 0d 2b 01 2d 1c 0b 15 07 06 06 0c 00 00 5d 04 0c 2a 00 00 00 00 02 0c 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano17 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 18 05 01 0a 01 05 47 16 0a 1f 0c 01 04 0d 13 05 4e 04 0b 1f 0c 01 04 0d 13 06 50 04 0c 17 07 06 08 0d 00 06 5e 04 0b 28 00 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 37 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano17 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 18 05 01 0a 01 01 47 16 0a 1f 0c 01 04 0d 13 01 4e 04 0b 1f 0c 01 04 0d 13 01 50 04 0c 17 07 06 08 0d 00 00 5e 04 0b 28 00 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 37 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano18 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 05 05 0c 01 02 51 0a 0d 19 06 15 09 0a 13 05 41 04 0a 19 08 05 05 09 00 06 46 10 0c 19 07 05 05 09 00 06 5d 04 0c 02 00 00 00 00 02 0c 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 38 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano18 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 05 05 0c 01 01 51 0a 0d 19 06 15 09 0a 13 01 41 04 0a 19 08 05 05 09 00 01 46 10 0c 19 07 05 05 09 00 00 5d 04 0c 02 00 00 00 00 02 0c 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 38 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano19 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 09 06 0c 01 05 43 16 05 19 06 06 09 0a 13 06 50 04 04 19 06 06 09 0a 13 05 4e 04 02 19 09 06 07 0d 00 06 5f 04 0a 22 00 00 00 00 02 0c 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano19 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 09 06 0c 01 01 43 16 05 19 06 06 09 0a 13 02 50 04 04 19 06 06 09 0a 13 01 4e 04 02 19 09 06 07 0d 00 00 5f 04 0a 22 00 00 00 00 02 0c 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 31 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano20 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 09 09 07 0d 01 05 3d 04 0b 19 09 09 07 0d 01 05 4b 0a 04 19 09 09 07 0d 01 05 41 10 03 19 09 06 07 0d 01 06 5f 04 0b 2a 00 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 32 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano20 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 09 09 07 0d 01 01 3d 04 0b 19 09 09 07 0d 01 01 4b 0a 04 19 09 09 07 0d 01 01 41 10 03 19 09 06 07 0d 01 00 5f 04 0b 2a 00 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 41 43 20 50 69 61 6e 6f 32 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano21 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 01 01 03 00 1b 05 47 04 0d 14 06 05 04 09 42 05 48 13 0d 12 06 0b 03 00 42 05 4b 0d 09 18 06 05 05 09 0a 44 60 04 0a 1a 23 0a 00 06 56 18 04 04 00 63 32 00 00 00 32 00 41 43 20 50 69 61 6e 6f 32 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AC Piano21 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 01 01 03 00 1b 01 47 04 0d 14 06 05 04 09 42 01 48 13 0d 12 06 0b 03 00 42 01 4b 0d 09 18 06 05 05 09 0a 40 60 04 0a 1a 23 0a 00 06 56 18 04 04 00 63 32 00 00 00 32 00 41 43 20 50 69 61 6e 6f 32 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BabyGrand* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 10 06 07 0e 00 01 44 04 0c 1e 0c 06 03 0e 2b 02 4c 04 0c 1e 09 07 06 0e 11 03 4f 10 09 1c 0a 06 07 0e 00 02 63 04 08 1a 23 00 00 00 62 0c 02 04 00 28 32 00 00 00 32 00 42 61 62 79 47 72 61 6e 64 2a 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bad Road * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 0f 18 07 02 11 02 38 1e 03 17 06 06 0a 0f 0c 05 57 04 0e 18 07 0d 0a 03 0e 45 63 04 06 17 06 06 07 0f 00 43 63 04 08 04 23 00 00 00 0e 0c 02 04 00 28 00 19 00 00 32 00 42 61 64 20 52 6f 61 64 20 2a 63 63 63 32 32 32 00 18 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bel/Piano* ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 1f 04 00 00 02 58 09 05 1c 09 1f 04 00 00 05 57 05 06 1c 09 1f 04 00 00 42 63 04 0b 1c 09 1f 04 00 00 43 63 00 08 04 17 00 08 0c 3a 11 02 04 00 28 63 00 00 00 32 00 42 65 6c 2f 50 69 61 6e 6f 2a 63 63 63 32 32 32 00 32 00 01 00 08 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BellPiano1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1b 0a 05 0a 00 03 63 2d 13 1f 1b 0a 06 0f 00 05 51 08 03 1f 09 07 05 0f 00 05 63 08 03 1f 09 0c 04 0f 00 46 5e 04 03 3c 18 52 00 51 26 0c 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 50 69 61 6e 6f 31 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BellPiano1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1b 0a 05 0a 00 01 63 2d 13 1f 1b 0a 06 0f 00 01 51 08 03 1f 09 07 05 0f 00 00 63 08 03 1f 09 0c 04 0f 00 40 5e 04 03 3c 18 52 00 51 26 0c 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 50 69 61 6e 6f 31 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BellPiano2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1b 0a 05 0a 00 04 63 08 13 1f 1b 0a 06 0f 00 04 63 19 03 1f 09 07 05 0f 00 05 63 08 03 1f 09 0c 04 0f 00 47 5e 08 03 3e 18 52 00 51 26 0c 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 50 69 61 6e 6f 32 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BellPiano2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1b 0a 05 0a 00 02 63 08 13 1f 1b 0a 06 0f 00 00 63 19 03 1f 09 07 05 0f 00 00 63 08 03 1f 09 0c 04 0f 00 40 5e 08 03 3e 18 52 00 51 26 0c 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 50 69 61 6e 6f 32 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BritePiano ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1d 04 0c 04 00 34 01 4d 04 0d 1f 07 03 04 0c 5e 03 4d 0d 0b 18 05 03 04 00 3d 03 4e 16 18 1f 08 03 04 0d 00 02 63 04 13 3a 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 42 72 69 74 65 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Buz'dPiano ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 04 0c 06 00 34 01 4f 04 0d 1f 07 03 06 0c 5e 04 4d 0d 13 18 05 03 06 00 3d 01 47 16 18 1f 08 03 06 0d 00 01 63 04 13 3a 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 42 75 7a 27 64 50 69 61 6e 6f 63 63 63 32 32 32 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ChibyPiano ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 15 0c 06 00 21 02 49 16 15 1f 07 03 06 0c 1b 04 47 0d 0b 18 05 03 06 00 10 02 4a 16 18 1f 08 03 06 0d 00 02 63 04 13 3a 1d 00 00 00 52 0c 02 04 00 63 32 00 00 00 32 00 43 68 69 62 79 50 69 61 6e 6f 63 63 63 32 32 32 00 70 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ChorusMeEP ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 0e 0f 0e 09 00 03 53 2a 0e 1f 00 00 07 0a 30 43 55 00 08 1f 12 06 08 0d 00 03 53 04 00 1c 04 00 07 0a 00 02 63 04 16 34 14 00 01 02 26 18 02 05 00 63 32 00 32 00 32 00 43 68 6f 72 75 73 4d 65 45 50 63 63 63 32 32 32 00 70 00 10 00 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: CrsPopPno ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 08 08 0b 63 03 4f 04 10 1f 1f 08 08 0b 63 03 4f 05 16 1f 19 05 06 0f 00 05 63 04 10 1f 19 05 06 0f 00 05 62 05 16 04 1a 12 00 00 4a 05 02 04 00 62 32 00 25 00 32 00 43 72 73 50 6f 70 50 6e 6f 20 63 63 63 32 32 32 16 48 14 41 00 58 00 51 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DX7 EP ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 1 1f 0f 00 0a 07 00 44 49 2d 06 1e 06 00 06 00 10 43 59 04 00 1b 07 0a 09 07 14 43 4f 04 03 1e 07 00 09 00 14 42 63 04 06 1c 16 00 04 0a 4e 18 02 04 00 28 32 00 00 00 32 00 44 58 37 20 45 50 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Deep Grd ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 3 18 01 01 03 00 15 03 47 04 0d 16 01 01 05 0c 46 03 5b 00 13 18 05 01 03 00 4a 04 36 10 08 14 08 01 05 0c 01 03 63 04 13 32 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 44 65 65 70 20 47 72 64 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DirtoyPno ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 0c 05 0c 1c 04 4e 13 13 1f 06 01 05 0e 00 46 55 30 0b 16 17 08 04 0d 00 04 63 00 0b 1f 09 09 05 0f 00 05 49 19 08 04 1d 11 05 00 4e 18 02 04 00 63 32 33 1e 33 32 00 44 69 72 74 6f 79 50 6e 6f 20 63 63 63 32 32 32 0e 70 04 2d 00 60 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DynomiteEP ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 13 1f 0e 0f 0e 09 00 03 43 2d 0e 1f 0f 05 07 0f 16 43 5a 00 00 1f 12 06 08 0d 00 03 63 04 00 1f 12 06 08 0f 00 02 63 04 06 34 14 00 01 02 4e 18 02 04 00 63 32 00 32 00 32 00 44 79 6e 6f 6d 69 74 65 45 50 63 63 63 32 32 32 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Piano2 ** Name: E. Piano 3 ** Description: An electric piano ** Controls: KV, MW (LFO pitch) ** Source: Art Hitzeman 16 1d 12 03 0c 00 42 42 16 04 1c 0e 03 03 0f 14 43 4f 04 16 1b 13 01 01 0f 4d 45 39 19 10 19 07 04 05 05 0a 42 63 04 13 19 1e 00 06 02 42 18 02 04 00 28 32 00 00 00 32 00 45 2e 20 50 69 61 6e 6f 20 33 63 63 63 32 32 32 03 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 00 05 00 28 02 53 04 0b 1f 08 00 07 00 00 06 4e 0a 03 19 07 00 0f 00 00 04 5f 04 08 19 05 15 08 00 00 06 5f 04 10 2c 19 00 03 00 62 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 00 05 00 28 01 53 04 0b 1f 08 00 07 00 00 01 4e 0a 03 19 07 00 0f 00 00 00 5f 04 08 19 05 15 08 00 00 00 5f 04 10 2c 19 00 03 00 62 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 12 05 00 00 03 4e 16 0c 1f 07 0a 06 00 00 05 3d 1c 0a 1f 06 00 05 00 00 06 5f 04 08 1b 06 0a 06 00 00 05 5c 04 0e 2c 14 00 05 00 62 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 32 63 63 63 32 32 32 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 12 05 00 00 02 4e 16 0c 1f 07 0a 06 00 00 02 3d 1c 0a 1f 06 00 05 00 00 00 5f 04 08 1b 06 0a 06 00 00 00 5c 04 0e 2c 14 00 05 00 62 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 32 63 63 63 32 32 32 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 09 03 09 0f 63 05 59 0a 03 1a 08 02 05 0c 29 04 55 08 0b 1f 0b 09 06 0f 00 04 5d 08 13 1d 08 06 08 0f 01 07 5f 04 0b 34 14 00 03 00 52 18 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 33 63 63 63 32 32 32 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 09 03 09 0f 63 02 59 0a 03 1a 08 02 05 0c 29 01 55 08 0b 1f 0b 09 06 0f 00 00 5d 08 13 1d 08 06 08 0f 01 00 5f 04 0b 34 14 00 03 00 52 18 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 33 63 63 63 32 32 32 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..4 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 09 03 07 0f 63 04 56 04 03 1a 08 02 03 0c 29 05 57 0a 0b 1f 0b 09 04 0f 00 06 5b 04 13 1d 08 06 06 0f 01 06 61 08 0b 2c 14 00 03 00 52 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 34 63 63 63 32 32 32 00 20 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..4 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 09 03 07 0f 63 01 56 04 03 1a 08 02 03 0c 29 01 57 0a 0b 1f 0b 09 04 0f 00 00 5b 04 13 1d 08 06 06 0f 01 00 61 08 0b 2c 14 00 03 00 52 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 34 63 63 63 32 32 32 00 20 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 09 05 05 0e 63 04 4a 19 13 19 07 04 09 00 00 04 4b 0a 06 1f 08 05 05 00 19 06 60 00 00 1d 08 01 06 0f 19 45 5c 00 02 0c 0f 00 00 20 66 18 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 35 63 63 63 32 32 32 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 09 05 05 0e 63 01 4a 19 13 19 07 04 09 00 00 01 4b 0a 06 1f 08 05 05 00 19 00 60 00 00 1d 08 01 06 0f 19 40 5c 00 02 0c 0f 00 00 20 66 18 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 35 63 63 63 32 32 32 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 09 06 09 0d 39 04 56 04 0e 1c 0f 06 0c 0c 23 46 5d 04 1a 1c 06 06 0f 0d 1e 45 60 04 00 1c 07 07 0b 0c 0b 46 62 04 06 2c 22 21 00 00 36 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 2e 36 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 09 06 09 0d 39 01 56 04 0e 1c 0f 06 0c 0c 23 42 5d 04 1a 1c 06 06 0f 0d 1e 40 60 04 00 1c 07 07 0b 0c 0b 40 62 04 06 2c 22 21 00 00 36 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 2e 36 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0f 07 0c 0f 63 04 55 04 0d 18 0c 0d 07 0c 30 04 4d 0a 0d 1f 06 06 06 0f 00 46 63 04 09 1f 0a 06 07 0f 01 06 5f 04 0c 24 17 00 00 27 0a 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 37 63 63 63 32 32 32 00 00 00 30 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 0f 07 0c 0f 63 01 55 04 0d 18 0c 0d 07 0c 30 01 4d 0a 0d 1f 06 06 06 0f 00 41 63 04 09 1f 0a 06 07 0f 01 00 5f 04 0c 24 17 00 00 27 0a 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 37 63 63 63 32 32 32 00 00 00 30 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 07 06 07 18 04 5b 04 03 1f 0a 07 06 0a 0c 04 63 04 0c 1f 06 0d 06 07 0e 46 63 04 0b 1f 04 0f 06 09 0a 06 63 04 0b 0d 1f 00 00 20 0a 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 38 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..8 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 07 06 07 18 01 5b 04 03 1f 0a 07 06 0a 0c 00 63 04 0c 1f 06 0d 06 07 0e 40 63 04 0b 1f 04 0f 06 09 0a 00 63 04 0b 0d 1f 00 00 20 0a 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 38 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..9 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 06 06 07 0e 63 06 46 10 04 1f 0f 06 03 0f 28 04 4c 04 0e 18 09 06 06 0e 00 05 63 04 03 1c 0f 06 06 0f 0a 07 5e 04 05 2c 63 00 00 00 02 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 39 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano..9 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 06 06 07 0e 63 01 46 10 04 1f 0f 06 03 0f 28 01 4c 04 0e 18 09 06 06 0e 00 00 63 04 03 1c 0f 06 06 0f 0a 00 5e 04 05 2c 63 00 00 00 02 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 2e 39 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.10 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 09 05 0d 1e 04 58 04 04 18 16 0d 08 0e 28 04 3c 22 06 18 09 06 07 0e 00 06 63 04 03 1c 0f 06 06 0f 0a 06 62 04 05 2c 63 00 00 00 02 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.10 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 09 05 0d 1e 01 58 04 04 18 16 0d 08 0e 28 01 3c 22 06 18 09 06 07 0e 00 01 63 04 03 1c 0f 06 06 0f 0a 00 62 04 05 2c 63 00 00 00 02 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.11 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 06 03 07 0b 4b 05 4f 04 1b 1b 19 06 06 0d 63 06 51 2a 1b 16 09 06 06 0e 00 05 61 04 03 1c 0f 04 06 0f 0a 06 5d 04 03 3c 63 00 00 00 02 18 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 31 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.11 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 06 03 07 0b 4b 01 4f 04 1b 1b 19 06 06 0d 63 01 51 2a 1b 16 09 06 06 0e 00 01 61 04 03 1c 0f 04 06 0f 0a 00 5d 04 03 3c 63 00 00 00 02 18 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 31 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.12 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 01 09 09 03 4b 05 52 10 1c 1c 18 06 02 0a 63 05 63 08 13 1b 14 09 04 0e 00 05 5a 04 05 1c 0f 06 06 0f 0a 06 62 00 03 24 00 00 00 00 02 18 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 31 32 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.12 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 01 09 09 03 4b 01 52 10 1c 1c 18 06 02 0a 63 01 63 08 13 1b 14 09 04 0e 00 00 5a 04 05 1c 0f 06 06 0f 0a 00 62 00 03 24 00 00 00 00 02 18 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 31 32 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.13 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 07 03 07 0d 63 46 52 04 1e 1e 16 0a 05 0b 63 45 58 24 0a 1c 06 06 0f 0d 1e 44 5c 04 08 1c 07 07 0b 0c 0b 05 63 04 06 3c 22 00 00 1a 0a 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.13 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 07 03 07 0d 63 42 52 04 1e 1e 16 0a 05 0b 63 41 58 24 0a 1c 06 06 0f 0d 1e 40 5c 04 08 1c 07 07 0b 0c 0b 01 63 04 06 3c 22 00 00 1a 0a 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.14 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 09 07 07 0b 2c 44 41 1f 0b 1e 09 07 07 0b 63 46 5a 2a 13 18 09 07 07 0d 00 44 61 04 03 1b 09 07 07 09 0a 06 62 04 0b 0c 22 00 00 1a 0a 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.14 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 18 09 07 07 0b 2c 42 41 1f 0b 1e 09 07 07 0b 63 42 5a 2a 13 18 09 07 07 0d 00 41 61 04 03 1b 09 07 07 09 0a 01 62 04 0b 0c 22 00 00 1a 0a 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.15 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 09 07 07 0b 55 45 39 04 13 1e 09 07 07 0b 63 45 47 1f 13 1f 18 07 07 0d 63 44 4f 08 1b 1b 09 07 07 09 0a 06 62 04 03 32 22 00 00 1a 0a 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 35 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.15 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 18 09 07 07 0b 55 42 39 04 13 1e 09 07 07 0b 63 41 47 1f 13 1f 18 07 07 0d 63 41 4f 08 1b 1b 09 07 07 09 0a 01 62 04 03 32 22 00 00 1a 0a 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 35 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.16 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 11 07 07 0b 55 04 34 16 13 1e 09 07 07 0b 63 05 35 08 13 1f 09 07 07 0d 63 04 32 08 13 1b 07 07 07 0b 0a 45 5a 04 03 70 22 00 00 27 06 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 36 63 63 63 32 32 32 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.16 ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 11 07 07 0b 55 00 34 16 13 1e 09 07 07 0b 63 00 35 08 13 1f 09 07 07 0d 63 00 32 08 13 1b 07 07 07 0b 0a 40 5a 04 03 70 22 00 00 27 06 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 36 63 63 63 32 32 32 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.17 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 06 08 0d 14 06 57 16 15 1c 0f 06 0c 0c 23 46 5a 04 09 1c 06 06 0f 0d 1e 46 63 04 01 1c 04 04 0b 02 0b 45 5f 04 05 2c 22 21 00 00 36 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.17 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 06 08 0d 14 04 57 16 15 1c 0f 06 0c 0c 23 41 5a 04 09 1c 06 06 0f 0d 1e 41 63 04 01 1c 04 04 0b 02 0b 40 5f 04 05 2c 22 21 00 00 36 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.18 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 06 04 01 0b 14 04 50 19 1d 16 0f 06 0c 0c 2d 44 44 04 01 18 06 06 0f 0d 1e 46 63 08 01 19 07 07 0b 0c 0b 45 60 04 05 0c 22 21 00 00 36 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 38 63 63 63 32 32 32 00 00 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.18 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 06 04 01 0b 14 01 50 19 1d 16 0f 06 0c 0c 2d 41 44 04 01 18 06 06 0f 0d 1e 41 63 08 01 19 07 07 0b 0c 0b 40 60 04 05 0c 22 21 00 00 36 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 38 63 63 63 32 32 32 00 00 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.19 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 06 07 0f 32 04 37 2a 15 18 09 06 0a 0f 24 46 58 04 15 1f 09 06 09 0f 1e 46 63 04 05 1f 0c 06 07 0f 0b 46 61 00 03 04 22 21 00 00 36 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 39 63 63 63 32 32 32 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.19 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 06 07 0f 32 01 37 2a 15 18 09 06 0a 0f 24 41 58 04 15 1f 09 06 09 0f 1e 41 63 04 05 1f 0c 06 07 0f 0b 40 61 00 03 04 22 21 00 00 36 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 31 39 63 63 63 32 32 32 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.20 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 06 07 0f 32 07 39 1f 15 18 09 06 0a 0f 24 44 51 04 15 1f 09 06 09 0f 1e 45 5f 08 05 1f 0c 06 07 0f 0b 46 5f 04 03 0e 22 21 00 00 36 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 32 30 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.20 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 06 07 0f 32 02 39 1f 15 18 09 06 0a 0f 24 40 51 04 15 1f 09 06 09 0f 1e 40 5f 08 05 1f 0c 06 07 0f 0b 40 5f 04 03 0e 22 21 00 00 36 18 00 04 00 63 63 00 00 00 32 00 45 20 50 69 61 6e 6f 2e 32 30 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.21 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 08 00 07 00 0d 01 3b 2a 0b 1f 08 00 07 00 00 01 59 04 03 1f 08 00 0a 00 00 00 60 04 00 1f 08 11 07 00 00 00 5e 04 00 64 18 00 04 00 62 0c 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 32 31 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.22 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 00 06 00 0d 01 46 16 0b 1f 08 00 07 00 2d 01 5a 04 0b 1f 11 00 0f 00 00 04 63 08 00 1f 08 15 08 01 00 04 63 04 08 1c 1b 00 04 00 52 18 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 32 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E Piano.22 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 00 06 00 0d 01 46 16 0b 1f 08 00 07 00 2d 01 5a 04 0b 1f 11 00 0f 00 00 00 63 08 00 1f 08 15 08 01 00 00 63 04 08 1c 1b 00 04 00 52 18 01 04 00 63 32 00 32 00 32 00 45 20 50 69 61 6e 6f 2e 32 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Piano 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 3 1f 1f 11 0f 0f 00 46 43 31 10 1d 0b 12 06 0e 00 42 2a 28 12 1d 04 01 06 0e 00 44 45 04 0c 1d 0c 04 08 0e 14 43 63 04 16 03 10 00 08 19 5a 18 02 04 00 28 32 00 00 00 32 00 45 2e 50 69 61 6e 6f 20 20 31 63 63 63 32 32 32 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Piano 2 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 4 1f 13 0a 09 04 00 06 49 0f 03 1f 1f 00 07 0f 0b 01 49 00 03 1f 0a 08 07 0c 0c 02 31 04 03 18 1f 09 09 0f 06 02 60 00 03 02 23 00 00 00 62 24 02 04 00 28 32 00 00 00 32 00 45 2e 50 69 61 6e 6f 20 20 32 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: E.Piano 2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 0d 09 04 00 06 49 0f 03 1f 1f 00 07 0f 0b 01 49 00 03 1f 0d 08 07 0c 0c 02 31 04 03 18 1f 09 09 0f 06 02 60 00 03 02 23 00 00 00 62 24 02 04 00 28 32 00 00 00 32 00 45 2e 50 69 61 6e 6f 20 20 32 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: El.Piano * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 06 07 0e 14 03 50 04 0e 1f 0f 0a 0b 0b 00 03 42 2d 0b 1f 1e 06 07 0e 00 02 63 03 08 1f 07 07 0a 0b 00 02 63 04 0b 04 15 00 0a 00 32 05 02 04 00 28 63 00 00 00 32 00 45 6c 2e 50 69 61 6e 6f 20 2a 63 63 63 32 32 32 00 18 00 08 00 06 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Elec Grand ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 5 1f 08 03 05 0d 00 02 44 04 0e 1f 04 02 05 0e 63 02 52 0a 00 1f 05 04 05 0d 63 02 42 10 05 1c 1e 06 06 0f 00 03 63 04 13 3a 23 00 00 00 62 0c 02 04 00 63 00 00 00 00 32 00 45 6c 65 63 20 47 72 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ElecPiano4 ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 16 0d 12 07 00 00 42 25 22 04 1c 1f 03 05 0f 14 43 51 04 16 1b 13 01 02 0f 63 45 3b 19 18 19 07 04 06 05 0d 42 63 04 13 02 1e 00 06 02 42 18 02 04 00 28 32 00 00 00 32 00 45 6c 65 63 50 69 61 6e 6f 34 63 63 63 32 32 32 03 50 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ElecPiano5 ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 0f 00 09 07 00 41 47 2d 06 0f 06 00 05 00 00 40 3e 04 00 1b 07 0a 08 07 00 42 63 04 03 1e 07 00 08 00 00 42 63 09 06 3c 17 00 00 1d 3a 18 02 04 00 28 32 00 00 00 32 00 45 6c 65 63 50 69 61 6e 6f 35 63 63 63 32 32 32 00 00 00 00 00 00 00 02 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ElecPiano6 ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 0f 00 09 07 00 41 52 2d 00 1f 0f 00 09 07 00 40 37 24 06 1b 07 0a 08 07 00 42 63 03 00 1b 07 0a 08 07 00 42 63 02 06 3c 17 00 00 1d 3a 18 02 04 00 28 32 00 00 00 32 00 45 6c 65 63 50 69 61 6e 6f 36 63 63 63 32 32 32 00 00 00 00 00 01 00 02 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Electracou ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 18 0b 0a 0f 0d 28 02 4a 0a 0b 1a 12 00 0a 0f 07 00 4e 04 0b 17 0f 0e 05 09 11 01 4e 0a 0b 1b 13 09 06 0e 00 00 63 04 0b 36 1e 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 45 6c 65 63 74 72 61 63 6f 75 63 63 63 32 32 32 00 30 00 00 00 70 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ElectroPno ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 11 1f 06 04 07 0c 00 04 36 2a 10 1f 04 03 04 0d 00 03 54 04 16 1f 0d 08 07 0c 00 44 63 04 0e 18 04 03 06 0d 00 43 63 04 13 04 14 08 02 1b 6a 0c 02 04 00 63 32 00 00 00 32 00 45 6c 65 63 74 72 6f 50 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FadePiano ** Description: Grand 2 with slowly fading sustain ** Controls: KV ** Source: Art Hitzeman 1f 0e 04 03 09 29 07 4c 04 00 16 04 02 03 0d 63 02 4c 09 16 1f 10 03 03 0a 63 02 4a 1c 15 17 04 01 05 0f 00 03 63 04 16 3a 1f 08 20 02 02 18 02 04 00 28 63 00 00 00 32 00 46 61 64 65 50 69 61 6e 6f 20 63 63 63 32 32 32 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FingersGo! ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 01 04 03 00 1a 01 48 04 1d 16 07 03 04 0b 4c 04 52 0d 13 18 05 03 03 00 41 02 47 1c 18 1a 06 03 06 0b 00 02 61 04 13 3a 23 00 00 00 02 18 02 04 00 63 32 00 00 00 32 00 46 69 6e 67 65 72 73 47 6f 21 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FlatFlange ** Description: a fuzzy, twangy electric piano ** Controls: KV ** Source: GLIB (Michael Kesti and others) 1f 0e 0f 0e 09 00 07 57 10 06 1f 11 03 0b 0a 43 02 53 08 13 1f 02 05 08 0e 00 04 63 0a 03 1f 1f 08 08 0a 00 00 5a 04 03 00 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 46 6c 61 74 46 6c 61 6e 67 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fuzz Piano ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 6 1f 11 0b 0f 00 00 04 63 15 16 1f 0b 00 0b 0f 58 07 51 04 06 1f 09 04 09 0a 5f 07 47 2a 05 1f 07 04 0a 0d 00 03 63 04 00 5a 1b 32 02 00 62 0c 04 04 00 63 16 00 00 00 32 00 46 75 7a 7a 20 50 69 61 6e 6f 63 63 63 32 32 32 08 70 30 50 00 40 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FuzzPiano ** Description: an electric piano with guitarish twang ** Controls: KV ** Source: GLIB (Michael Kesti and others) 1f 0e 0f 0e 09 00 03 4b 24 06 1f 11 03 0b 0e 43 03 51 08 13 1f 09 05 08 0e 00 03 59 0a 03 1f 12 08 08 0a 00 03 62 04 03 31 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 46 75 7a 7a 50 69 61 6e 6f 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GaragePno ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 15 1f 03 0b 00 05 53 1e 03 1f 09 04 04 0c 0f 06 47 03 16 1f 04 09 04 0c 0f 06 47 05 10 1f 1f 0a 06 0f 00 05 63 00 0b 03 1d 00 03 00 52 18 02 04 00 63 1e 00 00 00 32 00 47 61 72 61 67 65 50 6e 6f 20 63 63 63 32 32 32 08 0d 00 56 00 51 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GlassPiano ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 05 0f 00 03 4b 04 02 1f 1f 00 0f 0f 00 05 42 24 06 0c 09 05 05 0f 00 43 63 04 03 0c 0f 08 07 07 00 06 58 2a 04 6c 26 56 00 12 17 18 04 04 00 28 32 00 00 00 32 00 47 6c 61 73 73 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Grand 2 ** Description: DX11 factory preset 'Grand PF' with slower decay ** Controls: KV, MW (LFO pitch) ** Source: Art Hitzeman 1f 07 01 03 0e 2b 03 4c 04 08 16 03 02 04 0c 63 02 4c 09 16 1f 07 03 04 0a 63 02 4a 1c 15 17 16 06 04 0e 00 03 63 04 16 3a 1f 00 04 00 42 18 02 04 00 28 32 00 00 00 32 00 47 72 61 6e 64 20 32 20 20 20 63 63 63 32 32 32 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Grand 3 ** Description: 'Grand 2' with more treble ** Controls: KV, MW (LFO pitch) ** Source: Art Hitzeman 1f 07 01 03 0e 21 03 4c 04 08 16 03 02 04 0c 46 02 4c 09 16 1f 07 03 04 0a 46 02 4a 1c 15 17 16 06 04 0e 00 03 63 04 16 3a 1f 00 04 00 42 18 02 04 00 28 32 00 00 00 32 00 47 72 61 6e 64 20 33 20 20 20 63 63 63 32 32 32 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Grand Low ** Description: Grand 2 an octave lower ** Controls: KV, MW (LFO pitch) ** Source: Art Hitzeman 1f 07 01 03 0e 2b 03 4c 04 08 16 03 02 04 0c 63 02 4c 09 16 1f 07 03 04 0a 63 02 4a 1c 15 17 16 06 04 0e 00 03 63 04 16 3a 1f 00 04 00 42 0c 02 04 00 28 32 00 00 00 32 00 47 72 61 6e 64 20 4c 6f 77 20 63 63 63 32 32 32 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Grand PF ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 5 1f 07 01 06 0e 2b 03 4c 04 08 16 03 02 06 0c 63 02 4c 09 16 1f 07 03 07 0a 63 02 4a 1c 15 17 16 06 08 0e 00 03 63 04 16 3a 1f 00 04 00 42 18 02 04 00 28 32 00 00 00 32 00 47 72 61 6e 64 20 50 46 20 20 63 63 63 32 32 32 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GrandLow2 ** Description: (similar to Grand low) ** Controls: KV, MW (LFO pitch) ** Source: Art Hitzeman 1f 07 01 03 0e 2b 03 4c 04 08 16 03 02 04 0c 63 02 4c 09 16 1f 07 03 04 0a 63 02 4a 1c 15 17 06 03 04 0e 00 03 63 04 16 3a 1f 00 04 00 42 0c 02 04 00 28 32 00 00 00 32 00 47 72 61 6e 64 4c 6f 77 32 20 63 63 63 32 32 32 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GrandLow3 ** Description: Grand Low with slower decay ** Controls: KV, MW (LFO pitch) ** Source: Art Hitzeman 1f 07 01 03 0e 2b 03 4c 04 08 16 03 02 04 0c 63 02 4c 0e 16 1f 07 03 04 0a 63 02 4a 28 15 17 16 06 04 0e 00 03 63 04 16 3a 1f 00 04 00 42 05 02 04 00 28 32 00 00 00 32 00 47 72 61 6e 64 4c 6f 77 33 20 63 63 63 32 32 32 00 08 00 03 00 08 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GrandPiano ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 1 1d 04 0c 06 00 34 01 4d 04 0d 1f 07 03 06 0c 5e 04 4d 0d 0b 18 05 03 06 00 3d 01 4a 16 18 1f 08 03 06 0d 00 01 63 04 13 3a 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 47 72 61 6e 64 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HappyPiano ** Description: a cute electric piano ** Controls: KV ** Source: GLIB (Michael Kesti and others) 1f 0e 0f 0e 09 00 03 4b 24 06 1f 11 03 0b 0e 43 03 51 08 13 1f 12 05 08 0e 00 03 59 0a 03 1f 12 08 08 0a 00 03 62 04 03 34 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 48 61 70 70 79 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HarpsPiano ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 03 19 06 00 13 02 43 04 13 1f 00 0c 06 0f 25 04 55 1f 0b 14 1f 1f 06 0b 21 02 3d 16 1b 1f 1e 00 07 0f 00 02 63 04 13 62 20 00 00 00 42 18 02 04 00 63 0e 00 00 00 32 00 48 61 72 70 73 50 69 61 6e 6f 63 63 63 32 32 32 00 60 00 50 00 10 00 30 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hi-LowBell ** Description: yet another electric piano ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 1b 0f 0e 0a 00 03 4e 1f 06 1f 09 00 0b 00 43 03 4d 04 13 1f 09 05 08 0e 00 03 59 08 03 1f 12 08 08 0a 00 03 62 00 03 3c 1c 1d 10 00 62 18 01 04 00 63 32 00 32 00 32 00 48 69 2d 4c 6f 77 42 65 6c 6c 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: High&Long$ ** Description: a synthetic electric piano sound ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 0a 00 0e 09 00 03 58 24 06 1f 0e 03 0b 0e 43 03 55 04 13 1f 12 06 08 0e 00 02 4b 04 03 1f 12 05 08 0a 00 03 62 04 03 04 1c 14 06 54 66 18 01 04 00 63 32 00 32 00 32 00 48 69 67 68 26 4c 6f 6e 67 24 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HonkeyTonk ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 4 18 01 01 03 00 00 02 30 17 0e 16 03 01 04 0c 3a 02 4e 05 16 18 1b 04 03 0d 00 03 3d 1d 0d 14 08 01 05 0c 00 03 63 03 10 20 23 00 00 00 02 11 02 04 00 63 32 00 00 00 32 00 48 6f 6e 6b 65 79 54 6f 6e 6b 63 63 63 32 32 32 00 42 00 01 00 0b 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ivory * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 03 05 0d 00 02 48 04 16 1f 04 02 05 0e 42 05 47 0a 08 1f 05 04 05 0d 51 02 48 10 0d 16 1e 06 06 0f 00 03 63 04 13 02 19 00 00 00 22 0c 02 04 00 63 00 00 00 00 32 00 49 76 6f 72 79 20 20 20 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: JazzyPiano ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 07 01 06 0e 2b 03 35 04 08 16 03 02 06 0c 4b 02 59 09 16 1f 07 03 07 0a 5b 01 4d 19 15 17 16 06 08 0e 00 03 63 04 16 3a 1f 00 04 00 42 18 02 04 00 28 32 00 00 00 32 00 4a 61 7a 7a 79 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MetalWhip ** Description: electric piano / electric guitar mix ** Controls: KV ** Source: GLIB (Michael Kesti and others) 1f 0e 0f 0e 09 00 03 62 10 06 1f 11 03 0b 0e 43 03 51 08 13 1f 09 05 08 0e 00 03 59 0a 03 1f 12 08 08 0a 00 03 62 04 03 31 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 4d 65 74 61 6c 57 68 69 70 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: NewElectro ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 12 1f 0e 0f 0e 09 00 03 29 2d 06 1f 0f 05 0b 0e 4e 02 5a 04 0b 1f 12 06 08 0e 00 03 60 04 03 1f 12 06 08 0e 00 04 63 04 0b 34 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 4e 65 77 45 6c 65 63 74 72 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: NewElectro ** Description: still another electric piano ** Controls: KV ** Source: GLIB (Michael Kesti and others) 1f 0e 0f 0e 09 00 06 4b 24 06 1f 0e 03 0b 0e 43 02 55 04 13 1f 12 06 08 0e 00 04 4e 04 03 1f 12 08 08 0a 00 03 62 04 03 34 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 4e 65 77 45 6c 65 63 74 72 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: NewElectro ** Controls: KV ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 0e 0f 0e 09 00 07 4b 24 06 1f 0e 03 0b 0e 43 07 55 04 13 1f 12 06 08 0e 00 07 4e 04 03 1f 12 08 08 0a 00 07 62 04 03 34 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 4e 65 77 45 6c 65 63 74 72 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Old Rose ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 2 19 12 05 09 0a 25 41 54 04 05 19 0a 0a 09 0c 25 04 45 08 13 1f 13 00 09 03 00 05 56 02 03 1a 1f 05 09 0f 00 43 60 04 09 32 19 00 00 00 5e 18 02 04 00 28 09 0f 00 00 32 00 4f 6c 64 20 52 6f 73 65 20 20 63 63 63 32 32 32 00 30 00 50 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Old Rose ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 12 05 09 0d 25 41 54 04 05 19 0d 0a 09 0c 25 04 45 08 13 1f 13 00 09 03 00 05 56 02 03 1a 1f 05 09 0f 00 43 60 04 09 32 19 00 00 00 5e 18 02 04 00 28 09 0f 00 00 32 00 4f 6c 64 20 52 6f 73 65 20 20 63 63 63 32 32 32 00 30 00 50 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrganPiano ** Description: Doesn't sound like either ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 1b 0f 0e 0a 00 02 4e 0a 06 1f 09 00 0b 00 43 02 4d 04 13 1f 09 05 08 0e 00 02 59 08 03 1f 12 08 08 0a 00 02 62 04 03 3c 1c 1d 10 00 62 18 01 04 00 63 32 00 32 00 32 00 4f 72 67 61 6e 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrganPiano ** Description: a meek, organ-like electric piano ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 1b 0f 0e 0a 00 03 4e 0a 06 1f 09 00 0b 00 43 03 4d 04 13 1f 09 05 08 0e 00 02 59 08 03 1f 12 08 08 0a 00 02 62 04 03 3c 1c 1d 10 00 62 18 01 04 00 63 32 00 32 00 32 00 4f 72 67 61 6e 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Piano ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 09 00 04 0a 00 02 47 04 05 19 09 04 07 06 00 02 3b 0d 03 1e 16 02 06 0f 00 05 63 08 04 1e 15 08 05 0d 00 05 5a 3e 0a 2c 1e 17 00 00 22 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 20 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Piano ** Source: from a library of patches collected by Duane Bowker, author unknown 16 09 00 04 0a 00 00 47 04 05 19 09 04 07 06 00 00 3b 0d 03 1e 16 02 06 0f 00 00 63 08 04 1e 15 08 05 0d 00 00 5a 3e 0a 2c 1e 17 00 00 22 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 20 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PetalPiano ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 08 07 06 06 57 07 43 04 05 13 06 07 06 0a 00 00 3b 00 09 1d 08 07 06 0a 00 00 57 00 00 13 05 07 06 03 00 02 63 00 0d 01 1c 08 04 00 32 24 02 04 00 63 32 00 00 00 32 00 50 65 74 61 6c 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 10 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PhaseGrand ** Description: another electric piano ** Controls: KV ** Source: GLIB (Michael Kesti and others) 1d 17 02 0f 0a 52 00 47 0a 16 15 03 00 07 0e 35 04 4e 04 16 16 0f 00 02 0f 4f 03 56 04 10 13 1d 01 03 0a 0c 05 63 04 1b 33 23 00 00 00 62 18 02 04 00 63 32 00 32 00 32 00 50 68 61 73 65 47 72 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PianGuitar ** Description: Cross between a piano and a guitar, sort of ** Controls: KV ** Source: Art Hitzeman 1f 12 08 02 0b 39 02 4e 04 0b 1e 06 02 04 09 22 04 56 09 13 1f 0c 03 05 05 27 05 46 1c 16 1e 05 03 05 09 00 02 5d 04 13 02 20 00 00 00 62 18 02 04 00 28 32 00 00 00 32 00 50 69 61 6e 47 75 69 74 61 72 63 63 63 32 32 32 00 00 00 02 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rag Piano ** Description: A piano for those Scott Joplin pieces ** Controls: KV, MW (LFO pitch) ** Source: Art Hitzeman 1f 07 01 03 0e 2b 03 4c 04 08 16 03 02 04 0c 63 02 4c 09 16 1f 07 03 04 0a 63 02 4a 23 15 17 16 06 04 0e 19 03 63 04 16 0a 1f 00 04 00 42 13 02 04 00 28 32 00 00 00 32 00 52 61 67 20 50 69 61 6e 6f 20 63 63 63 33 33 33 00 05 00 0c 00 05 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RatRhodes ** Controls: KV, MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 08 08 0a 2b 47 63 00 06 1f 18 02 06 0c 00 01 59 2c 0b 1f 19 04 05 0f 04 46 63 00 13 1f 15 0a 05 0d 07 07 57 0f 10 04 14 16 07 0c 4a 18 02 04 00 63 32 33 2f 27 32 00 52 61 74 52 68 6f 64 65 73 20 63 63 63 32 32 32 00 30 0c 58 00 00 00 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Reed Piano ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 16 1f 0d 10 0d 0c 00 04 27 08 13 1f 1f 00 09 0f 24 43 42 04 16 1f 06 07 0a 0d 17 03 57 08 10 1f 09 02 07 0e 00 03 63 04 13 3b 17 00 00 11 5e 0c 04 04 00 63 32 00 32 00 32 00 52 65 65 64 20 50 69 61 6e 6f 63 63 63 32 32 32 00 03 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Piano3 ** Name: Rhodes...1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 0f 04 0f 00 01 2b 3f 06 1f 12 05 07 0e 4e 03 58 04 0b 1f 12 06 08 0e 52 05 4a 08 0b 1f 12 06 08 0e 00 44 63 04 0b 38 14 00 00 10 66 0c 00 04 00 63 00 00 00 00 32 00 52 68 6f 64 65 73 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 0f 04 0f 00 01 2b 3f 06 1f 12 05 07 0e 4e 02 58 04 0b 1f 12 06 08 0e 52 01 4a 08 0b 1f 12 06 08 0e 00 40 63 04 0b 38 14 00 00 10 66 0c 00 04 00 63 00 00 00 00 32 00 52 68 6f 64 65 73 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 04 00 08 0c 00 01 57 04 1b 1c 09 06 08 0c 4e 01 37 2a 0b 1c 04 00 08 0c 52 05 60 08 13 1c 07 07 08 0b 00 05 63 04 03 04 22 21 00 00 02 18 00 04 00 63 00 00 00 00 32 00 52 68 6f 64 65 73 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 04 00 08 0c 00 01 57 04 1b 1c 09 06 08 0c 4e 01 37 2a 0b 1c 04 00 08 0c 52 00 60 08 13 1c 07 07 08 0b 00 00 63 04 03 04 22 21 00 00 02 18 00 04 00 63 00 00 00 00 32 00 52 68 6f 64 65 73 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 04 00 08 0c 00 02 47 04 03 1c 15 0c 08 0c 4e 03 63 31 0b 1c 04 00 08 0c 00 05 5f 08 03 1c 1f 07 04 0b 00 05 62 04 0b 34 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 04 00 08 0c 00 02 47 04 03 1c 15 0c 08 0c 4e 01 63 31 0b 1c 04 00 08 0c 00 01 5f 08 03 1c 1f 07 04 0b 00 00 62 04 0b 34 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 04 00 08 0c 00 03 48 08 03 1c 15 0c 08 0c 4e 03 58 31 0b 1c 04 00 08 0c 00 05 5a 08 03 1c 1f 07 04 0b 00 04 62 08 0b 36 14 00 00 00 62 0c 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 04 00 08 0c 00 03 48 08 03 1c 15 0c 08 0c 4e 01 58 31 0b 1c 04 00 08 0c 00 00 5a 08 03 1c 1f 07 04 0b 00 00 62 08 0b 36 14 00 00 00 62 0c 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 09 06 07 05 00 43 52 04 0e 1c 09 05 0b 0a 4e 45 61 2a 0b 1c 06 06 07 0c 00 44 5f 04 08 1c 07 07 07 0b 00 45 63 04 0e 34 13 00 00 24 66 18 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 09 06 07 05 00 42 52 04 0e 1c 09 05 0b 0a 4e 45 61 2a 0b 1c 06 06 07 0c 00 40 5f 04 08 1c 07 07 07 0b 00 42 63 04 0e 34 13 00 00 24 66 18 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 03 03 02 0d 00 43 51 04 1b 1f 0d 0a 06 0d 4e 43 55 24 0b 1f 06 08 07 0d 00 45 5a 04 03 1f 0f 09 07 0d 00 45 61 04 0b 26 13 00 00 24 66 18 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 03 03 02 0d 00 41 51 04 1b 1f 0d 0a 06 0d 4e 41 55 24 0b 1f 06 08 07 0d 00 40 5a 04 03 1f 0f 09 07 0d 00 40 61 04 0b 26 13 00 00 24 66 18 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...7 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 0a 09 05 01 00 01 3d 13 16 19 0a 09 05 02 00 02 63 00 0c 19 07 09 05 07 13 04 63 0d 0e 19 07 09 05 07 13 44 63 04 0b 7e 14 0a 00 39 06 0c 02 04 00 63 15 27 00 00 32 00 52 68 6f 64 65 73 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...7 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 19 0a 09 05 01 00 01 3d 13 16 19 0a 09 05 02 00 00 63 00 0c 19 07 09 05 07 13 00 63 0d 0e 19 07 09 05 07 13 40 63 04 0b 7e 14 0a 00 39 06 0c 02 04 00 63 15 27 00 00 32 00 52 68 6f 64 65 73 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...8 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 09 00 07 0d 00 02 4c 04 1e 1c 0f 0a 05 0b 5c 03 5f 24 14 1c 06 06 07 0d 13 05 5b 04 06 1c 0f 09 07 0b 13 45 5f 04 03 7e 14 0a 00 39 06 0c 02 04 00 63 15 27 00 00 32 00 52 68 6f 64 65 73 2e 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...8 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 09 00 07 0d 00 00 4c 04 1e 1c 0f 0a 05 0b 5c 01 5f 24 14 1c 06 06 07 0d 13 00 5b 04 06 1c 0f 09 07 0b 13 40 5f 04 03 7e 14 0a 00 39 06 0c 02 04 00 63 15 27 00 00 32 00 52 68 6f 64 65 73 2e 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...9 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 04 00 08 0c 00 01 54 04 1b 1c 09 06 08 0c 4e 03 42 2a 0b 1c 04 00 08 0c 52 06 63 04 13 1c 07 07 08 0b 00 46 63 04 03 06 19 21 00 2c 06 18 00 04 00 63 00 00 00 00 32 00 52 68 6f 64 65 73 2e 2e 2e 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes...9 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 04 00 08 0c 00 01 54 04 1b 1c 09 06 08 0c 4e 00 42 2a 0b 1c 04 00 08 0c 52 00 63 04 13 1c 07 07 08 0b 00 40 63 04 03 06 19 21 00 2c 06 18 00 04 00 63 00 00 00 00 32 00 52 68 6f 64 65 73 2e 2e 2e 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes..10 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 04 00 08 0c 00 02 5b 04 1b 1c 09 06 08 0c 4e 03 57 24 1b 1c 04 00 08 0c 52 04 63 04 13 1c 07 07 08 0b 00 44 63 04 03 07 19 21 00 2c 06 18 00 04 00 63 00 00 00 00 32 00 52 68 6f 64 65 73 2e 2e 31 30 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes..10 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 04 00 08 0c 00 02 5b 04 1b 1c 09 06 08 0c 4e 00 57 24 1b 1c 04 00 08 0c 52 00 63 04 13 1c 07 07 08 0b 00 40 63 04 03 07 19 21 00 2c 06 18 00 04 00 63 00 00 00 00 32 00 52 68 6f 64 65 73 2e 2e 31 30 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes..11 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 0f 04 0f 00 03 34 0d 0e 1f 12 05 0b 0e 4e 05 45 19 03 1f 12 06 08 0e 52 04 45 04 03 1f 12 06 08 0e 00 06 63 04 03 3c 22 21 00 00 02 0c 00 04 00 63 00 00 00 00 32 00 52 68 6f 64 65 73 2e 2e 31 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes..11 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 0f 04 0f 00 01 34 0d 0e 1f 12 05 0b 0e 4e 01 45 19 03 1f 12 06 08 0e 52 00 45 04 03 1f 12 06 08 0e 00 00 63 04 03 3c 22 21 00 00 02 0c 00 04 00 63 00 00 00 00 32 00 52 68 6f 64 65 73 2e 2e 31 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes..12 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 04 00 08 0c 00 44 59 08 06 1a 15 0c 08 0c 4e 45 62 24 0b 1a 04 00 08 0c 00 44 63 04 00 1a 1f 07 04 0b 00 46 62 04 0b 37 13 00 00 24 66 18 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes..12 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 04 00 08 0c 00 41 59 08 06 1a 15 0c 08 0c 4e 40 62 24 0b 1a 04 00 08 0c 00 40 63 04 00 1a 1f 07 04 0b 00 40 62 04 0b 37 13 00 00 24 66 18 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes..13 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 03 02 02 0d 00 44 3d 22 1b 1f 0d 07 06 0d 32 46 45 1f 13 1f 06 04 07 0d 00 45 36 04 03 1f 0f 05 07 0d 00 45 5f 04 03 03 13 00 00 24 66 18 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 31 33 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rhodes..13 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 03 02 02 0d 00 41 3d 22 1b 1f 0d 07 06 0d 32 41 45 1f 13 1f 06 04 07 0d 00 41 36 04 03 1f 0f 05 07 0d 00 40 5f 04 03 03 13 00 00 24 66 18 01 04 00 63 32 00 32 00 32 00 52 68 6f 64 65 73 2e 2e 31 33 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SkoolPiano ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 7 1d 01 04 03 00 1a 01 48 04 0d 16 01 03 04 0c 63 04 4e 0d 13 18 04 03 03 00 56 01 44 16 18 1a 07 03 06 0d 00 01 61 04 13 3a 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 53 6b 6f 6f 6c 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpacePiano ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 06 02 07 0d 00 05 3a 3a 13 1b 19 03 08 0b 00 45 63 2b 13 16 06 04 0c 0d 00 44 62 04 0b 15 09 04 07 00 00 45 61 04 13 2c 19 00 00 51 66 18 04 04 00 28 32 00 00 00 32 00 53 70 61 63 65 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SquareFlat ** Description: a buzzy electric piano ** Controls: KV ** Source: GLIB (Michael Kesti and others) 1f 1f 00 0e 0f 00 03 5c 04 06 1f 0e 03 0b 0e 43 03 55 04 13 1f 12 06 08 0e 00 03 58 04 00 1f 12 08 08 0a 00 03 63 04 03 34 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 53 71 75 61 72 65 46 6c 61 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sus Piano ** Description: Grand 2 with infinitely long sustain ** Controls: KV ** Source: Art Hitzeman 1f 0e 03 03 09 29 07 4c 04 00 16 04 01 03 0d 63 02 4c 09 16 1f 10 02 03 0a 63 02 4a 1c 15 17 04 00 05 0f 00 03 63 04 16 3a 1f 08 20 02 02 18 02 04 00 28 63 00 00 00 32 00 53 75 73 20 50 69 61 6e 6f 20 63 63 63 32 32 32 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Thump Pno ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 8 1f 11 0b 0f 00 00 03 41 05 16 1f 0b 04 04 0f 55 02 55 04 16 1f 09 04 09 0e 3f 02 45 10 15 1f 09 07 07 0c 00 03 63 04 08 52 1d 13 00 00 52 0c 02 04 00 63 16 00 00 00 32 00 54 68 75 6d 70 20 50 6e 6f 20 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Toy Piano ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 24 1f 0f 0a 07 0c 27 02 50 0a 00 1f 0e 0a 09 0a 1a 02 4f 15 06 1d 10 0e 07 0e 00 05 54 10 08 1d 10 0a 08 0a 00 01 60 08 0e 14 1a 00 00 00 62 24 02 04 00 28 32 00 00 00 32 00 54 6f 79 20 50 69 61 6e 6f 20 63 63 63 32 32 32 00 07 00 07 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Toy Piano ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 0d 07 0c 27 02 50 0a 00 1f 0e 0a 09 0d 1a 02 4f 15 06 1d 10 0e 07 0e 00 05 54 10 08 1d 10 0d 08 0d 00 01 60 08 0e 14 1a 00 00 00 62 24 02 04 00 28 32 00 00 00 32 00 54 6f 79 20 50 69 61 6e 6f 20 63 63 63 32 32 32 00 07 00 07 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Upright * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 14 0a 0f 00 00 02 5e 0c 1b 17 0b 0a 0b 00 58 03 4e 0a 1c 1b 0a 10 01 00 5e 01 50 00 0b 16 08 0a 06 00 1f 02 63 05 13 03 19 19 3c 00 42 05 02 04 00 28 63 00 00 00 32 00 55 70 72 69 67 68 74 20 20 2a 63 63 63 32 32 32 08 7b 00 00 00 14 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Upright ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 6 16 1d 12 07 0c 00 42 42 07 04 1c 0e 03 05 0f 14 43 4f 04 16 1b 13 01 02 0f 63 45 39 19 18 19 07 04 06 05 0a 42 63 04 13 1a 1e 00 06 02 42 18 02 04 00 28 32 00 00 00 32 00 55 70 72 69 67 68 74 20 20 20 63 63 63 32 32 32 0b 02 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Upright ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 16 1d 12 07 0c 00 42 42 07 04 1c 0e 03 05 0f 14 43 4f 04 16 1b 13 01 02 0f 63 45 39 19 18 19 07 04 06 05 0d 42 63 04 13 1a 1e 00 06 02 42 18 02 04 00 28 32 00 00 00 32 00 55 70 72 69 67 68 74 20 20 20 63 63 63 32 32 32 0b 02 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: UprightBas ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 07 01 00 08 0d 16 02 4c 00 10 1f 08 05 07 0d 00 03 55 00 0e 1f 1c 03 07 0e 13 02 50 00 10 1f 0e 05 06 0d 00 03 63 00 0b 3a 1b 0f 0a 00 52 0c 04 05 00 63 4b 00 12 00 32 00 55 70 72 69 67 68 74 42 61 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Uprt piano ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 2 13 02 01 04 0c 48 04 41 10 19 13 02 01 04 0c 45 03 59 04 10 13 02 01 04 0c 48 03 4a 10 1d 13 02 01 04 0c 00 05 63 04 1b 3a 23 00 00 00 12 18 04 04 00 63 32 00 00 00 32 00 55 70 72 74 20 70 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WiredPiano ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 04 03 09 1a 01 4a 04 1d 17 07 03 04 0b 0a 04 45 0d 13 19 05 03 03 0a 41 02 46 22 18 1a 06 03 06 0b 00 02 61 04 13 3a 23 00 00 00 02 0c 02 04 00 63 32 00 00 00 32 00 57 69 72 65 64 50 69 61 6e 6f 63 63 63 32 32 32 10 00 00 00 20 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wood Piano ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 15 1f 0c 07 08 03 00 04 4d 04 03 1f 05 07 08 0f 00 04 50 04 0b 1f 09 00 08 00 00 04 63 00 08 1f 09 00 08 00 00 03 63 00 0b 34 19 00 07 00 52 24 01 04 00 63 32 00 32 00 32 00 57 6f 6f 64 20 50 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: britegrand ** Description: another electric piano ** Controls: KV ** Source: GLIB (Michael Kesti and others) 1d 17 02 0f 0a 07 00 4e 0a 16 15 03 00 07 0e 35 04 4e 04 16 16 0f 00 02 0f 4f 03 56 04 10 13 1d 01 03 0a 0c 05 63 04 1b 33 23 00 00 00 62 18 02 04 00 63 32 00 32 00 32 00 62 72 69 74 65 67 72 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: callipiano ** Description: an electric piano with tremolo sustain ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 11 0f 0e 0a 00 03 52 0a 06 1f 01 00 0b 00 43 03 5a 04 13 1f 03 05 08 0f 00 03 5a 0a 03 1f 12 08 08 0a 00 03 63 04 03 14 1c 1d 07 00 62 18 01 04 00 63 32 00 32 00 32 00 63 61 6c 6c 69 70 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: flatfunk ** Description: a percussive electric piano sound ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 11 0f 0e 0a 00 04 52 13 06 1f 01 00 0b 00 43 04 5a 04 13 1f 05 05 08 0e 00 04 5a 00 03 1f 12 08 08 0a 00 04 63 04 03 10 1c 1d 10 00 62 18 01 04 00 63 32 00 32 00 32 00 66 6c 61 74 66 75 6e 6b 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: fuzzbass ** Description: a very fuzzy bass electric piano ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 05 00 0e 0a 00 03 59 00 06 1f 09 00 0b 00 43 03 4d 04 13 1f 09 05 08 0e 00 03 5c 04 03 1f 12 08 08 0a 00 03 62 04 03 3c 1c 1d 10 00 52 18 01 04 00 63 32 00 32 00 32 00 66 75 7a 7a 62 61 73 73 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: metlmarimb ** Description: a metallic electric piano ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 0e 01 0e 0c 00 03 5a 0a 06 1f 09 00 0b 00 43 03 4d 04 13 1f 09 05 08 0e 00 03 59 08 03 1f 12 08 08 0a 00 03 62 00 03 3c 1c 1d 06 00 62 18 01 04 00 63 32 00 32 00 32 00 6d 65 74 6c 6d 61 72 69 6d 62 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: mildfilter ** Description: a variation on SquareFlat ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 02 03 06 00 0e 03 54 08 1b 1f 0b 00 09 0c 00 00 3c 04 04 1f 06 00 09 01 00 00 4b 04 03 1f 1c 04 09 0a 00 03 63 08 03 38 1e 09 04 00 62 0c 02 05 00 63 32 00 32 00 32 00 6d 69 6c 64 66 69 6c 74 65 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: phaseharp ** Description: yet another electric piano ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 07 00 0e 0a 00 07 62 0a 06 1f 09 00 0b 00 43 07 4d 04 13 1f 09 05 08 0e 00 01 59 08 03 1f 12 08 08 0a 00 00 62 04 03 3c 1c 1d 10 00 52 18 01 04 00 63 32 00 32 00 32 00 70 68 61 73 65 68 61 72 70 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: pizzapiano ** Description: yet another electric piano ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 13 10 00 0e 0a 00 03 54 08 06 1f 09 00 0b 00 43 03 4d 04 13 1f 09 05 08 0e 00 03 59 08 03 1f 12 08 08 0a 00 03 62 04 03 3c 1c 1d 10 00 52 18 01 04 00 63 32 00 32 00 32 00 70 69 7a 7a 61 70 69 61 6e 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: stringback ** Description: an electric piano with interesting decay ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 06 05 00 0e 0a 00 03 52 08 06 19 02 00 0b 00 43 03 57 0a 13 1f 09 05 08 0e 00 03 59 08 03 1f 12 08 08 0a 00 03 62 04 03 3c 1c 1d 10 00 52 18 01 04 00 63 32 00 32 00 32 00 73 74 72 69 6e 67 62 61 63 6b 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 17 * SUBCATEGORY Synthesizer1 ** Name: AnlgSynt.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 05 06 07 04 0a 23 04 52 08 0b 06 03 0c 06 0d 00 05 44 10 0c 12 01 06 06 0e 00 05 63 04 05 06 06 09 06 0b 00 05 60 04 02 74 2a 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.1 ** Source: from a library of patches collected by Duane Bowker, author unknown 05 06 07 04 0a 23 00 52 08 0b 06 03 0c 06 0d 00 00 44 10 0c 12 01 06 06 0e 00 00 63 04 05 06 06 09 06 0b 00 00 60 04 02 74 2a 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 02 07 0c 04 5c 06 56 04 03 0f 03 03 08 0e 00 04 5f 04 06 1f 1f 00 0f 0f 00 06 5f 04 03 1f 12 01 09 0e 00 05 5f 04 03 7c 2a 00 00 00 32 00 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 32 63 63 63 32 32 32 00 20 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 02 07 0c 04 5c 01 56 04 03 0f 03 03 08 0e 00 01 5f 04 06 1f 1f 00 0f 0f 00 00 5f 04 03 1f 12 01 09 0e 00 00 5f 04 03 7c 2a 00 00 00 32 00 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 32 63 63 63 32 32 32 00 20 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 02 07 0c 04 5c 03 59 04 03 0f 03 03 08 0e 00 05 63 04 06 1f 1f 00 0f 0f 00 07 63 04 03 1f 12 01 09 0e 00 06 5f 04 03 74 2a 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 33 63 63 63 32 32 32 00 10 00 10 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 02 07 0c 04 5c 01 59 04 03 0f 03 03 08 0e 00 02 63 04 06 1f 1f 00 0f 0f 00 00 63 04 03 1f 12 01 09 0e 00 00 5f 04 03 74 2a 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 33 63 63 63 32 32 32 00 10 00 10 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 09 07 09 0a 5c 04 51 08 05 12 04 03 09 0c 00 05 51 08 00 1f 12 07 09 0e 00 05 63 04 05 1b 09 0c 09 0a 00 06 54 04 00 7c 2a 00 00 00 42 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 34 63 63 63 32 32 32 00 10 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 09 07 09 0a 5c 01 51 08 05 12 04 03 09 0c 00 01 51 08 00 1f 12 07 09 0e 00 00 63 04 05 1b 09 0c 09 0a 00 00 54 04 00 7c 2a 00 00 00 42 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 34 63 63 63 32 32 32 00 10 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0f 0f 09 0d 00 04 52 08 02 1e 04 06 0c 09 00 06 4d 0a 03 1f 09 07 09 0e 00 05 61 04 04 1e 09 07 0a 0e 00 06 60 04 03 34 23 00 02 00 32 0c 02 04 00 63 63 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 35 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 0f 0f 09 0d 00 01 52 08 02 1e 04 06 0c 09 00 01 4d 0a 03 1f 09 07 09 0e 00 00 61 04 04 1e 09 07 0a 0e 00 00 60 04 03 34 23 00 02 00 32 0c 02 04 00 63 63 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 35 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 02 07 0c 04 5c 05 53 04 03 1f 0f 03 08 0f 00 04 54 04 05 1f 1f 00 0f 0f 00 06 63 04 03 1f 09 01 09 09 00 05 5f 04 01 6c 23 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 36 63 63 63 32 32 32 00 00 00 20 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 02 07 0c 04 5c 02 53 04 03 1f 0f 03 08 0f 00 01 54 04 05 1f 1f 00 0f 0f 00 00 63 04 03 1f 09 01 09 09 00 00 5f 04 01 6c 23 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 36 63 63 63 32 32 32 00 00 00 20 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 03 08 05 5c 05 5d 04 00 1f 0a 06 08 07 00 05 52 04 06 18 07 04 08 05 00 05 63 04 00 1f 15 06 08 0e 00 46 62 04 08 2c 20 00 00 47 36 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 37 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 03 08 05 5c 02 5d 04 00 1f 0a 06 08 07 00 01 52 04 06 18 07 04 08 05 00 00 63 04 00 1f 15 06 08 0e 00 40 62 04 08 2c 20 00 00 47 36 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 37 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 0c 07 09 07 00 04 58 08 06 12 0c 07 09 0a 00 06 5b 04 03 1f 16 00 09 0d 00 06 56 04 06 1f 16 00 09 0d 00 06 62 08 03 2c 20 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.8 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 0c 07 09 07 00 01 58 08 06 12 0c 07 09 0a 00 01 5b 04 03 1f 16 00 09 0d 00 00 56 04 06 1f 16 00 09 0d 00 00 62 08 03 2c 20 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.9 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0c 09 03 0c 00 03 5a 08 06 0f 0c 09 04 0c 00 04 59 04 02 1f 15 05 03 0d 00 05 56 04 0e 1f 15 05 05 0d 00 47 62 04 0e 2c 26 21 11 00 22 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 39 63 63 63 32 32 32 00 40 00 20 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt.9 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0c 09 03 0c 00 01 5a 08 06 0f 0c 09 04 0c 00 01 59 04 02 1f 15 05 03 0d 00 00 56 04 0e 1f 15 05 05 0d 00 40 62 04 0e 2c 26 21 11 00 22 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 2e 39 63 63 63 32 32 32 00 40 00 20 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt10 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0d 12 03 06 00 03 52 04 00 0f 04 12 09 0c 00 06 4a 10 0a 0f 15 06 03 0e 00 05 63 04 08 1f 15 06 05 0d 00 45 62 04 0e 3c 26 21 20 00 52 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 30 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt10 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0d 12 03 06 00 01 52 04 00 0f 04 12 09 0c 00 01 4a 10 0a 0f 15 06 03 0e 00 00 63 04 08 1f 15 06 05 0d 00 40 62 04 0e 3c 26 21 20 00 52 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 30 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt11 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 03 12 07 06 00 04 50 04 00 10 0d 12 07 06 00 05 56 04 02 11 06 06 08 0e 00 06 63 04 08 10 15 06 08 0e 00 06 62 04 0e 3c 26 1c 20 00 42 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 31 63 63 63 32 32 32 00 00 00 40 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt11 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 03 12 07 06 00 02 50 04 00 10 0d 12 07 06 00 01 56 04 02 11 06 06 08 0e 00 00 63 04 08 10 15 06 08 0e 00 00 62 04 0e 3c 26 1c 20 00 42 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 31 63 63 63 32 32 32 00 00 00 40 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt12 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 03 12 07 06 00 04 57 08 00 07 04 04 07 0d 00 04 56 0d 02 10 05 06 08 0e 00 07 63 08 08 19 04 03 07 0f 00 06 62 04 04 2c 26 21 20 00 22 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt12 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 03 12 07 06 00 02 57 08 00 07 04 04 07 0d 00 03 56 0d 02 10 05 06 08 0e 00 00 63 08 08 19 04 03 07 0f 00 00 62 04 04 2c 26 21 20 00 22 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt13 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 06 0a 0f 00 03 59 00 0b 1f 0c 06 0a 0b 00 04 63 00 02 1f 0c 05 0a 0b 00 04 63 00 04 1f 16 02 0a 0f 00 06 62 00 02 2d 25 00 00 00 32 18 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 33 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt13 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 06 0a 0f 00 01 59 00 0b 1f 0c 06 0a 0b 00 00 63 00 02 1f 0c 05 0a 0b 00 00 63 00 04 1f 16 02 0a 0f 00 00 62 00 02 2d 25 00 00 00 32 18 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 33 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt14 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 00 06 07 1b 03 58 04 09 1f 06 00 04 09 13 04 54 0d 0d 1f 07 00 07 07 0b 05 63 08 09 1f 06 00 04 09 00 05 62 04 0d 2c 1c 00 05 00 22 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 34 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt14 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 00 06 07 1b 01 58 04 09 1f 06 00 04 09 13 02 54 0d 0d 1f 07 00 07 07 0b 00 63 08 09 1f 06 00 04 09 00 00 62 04 0d 2c 1c 00 05 00 22 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 34 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt15 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 01 08 03 1b 03 63 00 09 1f 15 06 09 0f 00 05 57 04 01 1f 15 06 09 0f 00 04 63 04 01 1f 15 06 09 0f 00 06 62 00 01 2c 1c 00 05 00 22 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 35 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AnlgSynt15 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 01 08 03 1b 01 63 00 09 1f 15 06 09 0f 00 01 57 04 01 1f 15 06 09 0f 00 00 63 04 01 1f 15 06 09 0f 00 00 62 00 01 2c 1c 00 05 00 22 0c 02 04 00 28 02 00 00 00 32 00 41 6e 6c 67 53 79 6e 74 31 35 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ariesynth1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 01 05 09 00 03 55 00 03 1f 03 00 05 06 2d 03 59 0d 0b 1f 08 00 04 0c 43 05 63 13 0b 1c 06 04 04 07 00 05 63 08 0b 6d 21 00 38 00 22 0c 06 04 00 63 32 00 32 00 32 00 41 72 69 65 73 79 6e 74 68 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ariesynth1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 08 01 05 09 00 01 55 00 03 1f 03 00 05 06 2d 02 59 0d 0b 1f 08 00 04 0c 43 00 63 13 0b 1c 06 04 04 07 00 00 63 08 0b 6d 21 00 38 00 22 0c 06 04 00 63 32 00 32 00 32 00 41 72 69 65 73 79 6e 74 68 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ariesynth2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 01 05 09 00 03 53 00 03 1f 03 00 05 06 2d 05 52 00 13 1f 08 00 04 0c 43 05 55 00 0b 1c 06 04 05 07 00 07 63 04 0b 7a 21 00 38 00 22 0c 06 04 00 63 32 00 32 00 32 00 41 72 69 65 73 79 6e 74 68 32 63 63 63 32 32 32 00 00 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ariesynth2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 08 01 05 09 00 01 53 00 03 1f 03 00 05 06 2d 01 52 00 13 1f 08 00 04 0c 43 00 55 00 0b 1c 06 04 05 07 00 00 63 04 0b 7a 21 00 38 00 22 0c 06 04 00 63 32 00 32 00 32 00 41 72 69 65 73 79 6e 74 68 32 63 63 63 32 32 32 00 00 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: A.R.P. * ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 08 06 0d 00 00 4c 04 03 1f 07 00 04 0c 00 00 42 04 03 1f 04 00 02 0d 00 00 42 04 03 1a 0c 00 08 0e 00 00 63 04 03 3a 21 00 00 00 52 0c 02 0d 1f 28 63 00 00 00 32 00 20 20 41 2e 52 2e 50 2e 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP 2500 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 16 00 05 0e 0a 04 62 00 01 1f 0d 10 05 00 0e 05 3f 10 12 1f 0d 10 05 00 0c 04 3a 0d 15 10 1f 00 0b 0f 00 0d 63 04 04 6d 14 00 36 00 22 18 04 04 01 63 19 33 00 00 32 00 41 52 50 20 32 35 30 30 20 20 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP 2500 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 16 00 05 0e 0a 01 62 00 01 1f 0d 10 05 00 0e 01 3f 10 12 1f 0d 10 05 00 0c 01 3a 0d 15 10 1f 00 0b 0f 00 00 63 04 04 6d 14 00 36 00 22 18 04 04 01 63 19 33 00 00 32 00 41 52 50 20 32 35 30 30 20 20 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP 2600.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 08 02 03 03 00 05 60 04 04 10 05 06 08 03 00 25 63 0a 03 10 05 05 08 03 00 1d 63 0a 04 10 05 05 08 03 00 3d 63 08 03 2d 23 00 00 00 42 0c 05 04 01 63 32 00 00 00 32 00 41 52 50 20 32 36 30 30 2e 31 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP 2600.1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 08 02 03 03 00 02 60 04 04 10 05 06 08 03 00 20 63 0a 03 10 05 05 08 03 00 18 63 0a 04 10 05 05 08 03 00 38 63 08 03 2d 23 00 00 00 42 0c 05 04 01 63 32 00 00 00 32 00 41 52 50 20 32 36 30 30 2e 31 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP 2600.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 16 00 05 0e 0a 03 63 04 01 1f 0d 10 05 00 0e 05 3c 10 12 1f 0d 10 05 00 0c 05 35 13 15 10 1f 00 0b 0f 00 05 63 04 04 7f 14 00 36 00 22 18 04 04 00 63 19 33 00 00 32 00 41 52 50 20 32 36 30 30 2e 32 63 63 63 32 32 32 00 50 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP 2600.2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 16 00 05 0e 0a 00 63 04 01 1f 0d 10 05 00 0e 00 3c 10 12 1f 0d 10 05 00 0c 00 35 13 15 10 1f 00 0b 0f 00 00 63 04 04 7f 14 00 36 00 22 18 04 04 00 63 19 33 00 00 32 00 41 52 50 20 32 36 30 30 2e 32 63 63 63 32 32 32 00 50 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP Axxe.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 08 00 05 0f 14 04 5c 00 11 18 08 00 05 0f 14 03 5c 04 11 18 08 00 05 0f 14 06 61 00 15 18 08 00 05 0f 14 05 63 04 15 6c 22 00 0b 00 62 0c 07 0d 01 63 63 63 32 00 32 00 41 52 50 20 41 78 78 65 2e 31 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP Axxe.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 08 00 05 0f 14 01 5c 00 11 18 08 00 05 0f 14 01 5c 04 11 18 08 00 05 0f 14 00 61 00 15 18 08 00 05 0f 14 00 63 04 15 6c 22 00 0b 00 62 0c 07 0d 01 63 63 63 32 00 32 00 41 52 50 20 41 78 78 65 2e 31 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP Axxe.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 05 02 07 09 00 05 59 08 03 15 12 03 04 0d 00 04 54 08 01 18 07 05 07 0d 00 05 63 04 06 0f 04 03 07 0d 00 06 63 04 06 64 1a 00 00 00 12 0c 06 0d 02 28 32 00 00 00 32 00 41 52 50 20 41 78 78 65 2e 32 63 63 63 32 32 32 00 00 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP Axxe.2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 05 02 07 09 00 02 59 08 03 15 12 03 04 0d 00 02 54 08 01 18 07 05 07 0d 00 00 63 04 06 0f 04 03 07 0d 00 00 63 04 06 64 1a 00 00 00 12 0c 06 0d 02 28 32 00 00 00 32 00 41 52 50 20 41 78 78 65 2e 32 63 63 63 32 32 32 00 00 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP Omni.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 00 05 0e 00 44 4e 08 02 0d 07 0e 06 06 0e 45 63 0d 04 0c 1f 00 06 0f 52 46 63 19 04 0b 1f 06 08 0f 0a 46 63 08 03 35 23 08 10 06 56 0c 07 04 00 63 63 00 00 00 32 00 41 52 50 20 4f 6d 6e 69 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP Omni.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 00 05 0e 00 42 4e 08 02 0d 07 0e 06 06 0e 42 63 0d 04 0c 1f 00 06 0f 52 40 63 19 04 0b 1f 06 08 0f 0a 40 63 08 03 35 23 08 10 06 56 0c 07 04 00 63 63 00 00 00 32 00 41 52 50 20 4f 6d 6e 69 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP Omni.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 03 1e 05 04 56 05 50 0d 06 0b 03 1e 05 04 23 03 4b 08 0d 0a 1f 01 08 0f 15 05 57 08 08 0a 1f 00 05 0f 25 06 5b 08 01 54 22 05 0c 00 52 0c 0c 04 00 63 32 00 00 00 32 00 41 52 50 20 4f 6d 6e 69 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ARP Omni.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 03 1e 05 04 56 01 50 0d 06 0b 03 1e 05 04 23 01 4b 08 0d 0a 1f 01 08 0f 15 00 57 08 08 0a 1f 00 05 0f 25 00 5b 08 01 54 22 05 0c 00 52 0c 0c 04 00 63 32 00 00 00 32 00 41 52 50 20 4f 6d 6e 69 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Axel F ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 19 05 01 04 03 2c 00 48 04 0b 11 01 05 04 0c 3c 02 42 08 0b 19 04 05 05 0a 20 01 48 1c 0c 11 13 05 04 0f 00 02 63 04 13 3a 1f 00 04 00 42 18 02 04 00 28 32 00 00 00 32 00 41 78 65 6c 20 46 20 20 20 20 63 63 63 32 32 32 00 00 00 40 00 00 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Axel F...1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 0d 02 06 0e 00 04 58 04 00 15 04 14 06 0c 00 05 5f 04 0a 13 15 03 06 0e 00 05 63 04 08 15 15 06 06 0d 00 05 62 04 0e 34 26 21 20 47 12 0c 00 04 00 63 32 00 32 00 32 00 41 78 65 6c 20 46 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Axel F...1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 0d 02 06 0e 00 01 58 04 00 15 04 14 06 0c 00 01 5f 04 0a 13 15 03 06 0e 00 00 63 04 08 15 15 06 06 0d 00 00 62 04 0e 34 26 21 20 47 12 0c 00 04 00 63 32 00 32 00 32 00 41 78 65 6c 20 46 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Axel F...2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 15 02 06 0e 00 03 5a 04 08 15 04 14 06 0c 00 04 5f 04 0a 15 15 03 06 0e 00 06 63 04 08 15 15 06 06 0d 00 46 62 04 0e 2c 26 21 20 2b 16 0c 00 04 00 63 32 00 32 00 32 00 41 78 65 6c 20 46 2e 2e 2e 32 63 63 63 32 32 32 00 10 00 20 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Axel F...2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 15 02 06 0e 00 01 5a 04 08 15 04 14 06 0c 00 01 5f 04 0a 15 15 03 06 0e 00 00 63 04 08 15 15 06 06 0d 00 40 62 04 0e 2c 26 21 20 2b 16 0c 00 04 00 63 32 00 32 00 32 00 41 78 65 6c 20 46 2e 2e 2e 32 63 63 63 32 32 32 00 10 00 20 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BadSample* ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 15 00 01 00 00 00 43 3d 00 14 1f 00 01 0d 00 00 4a 0a 00 1f 0b 00 01 00 00 00 46 13 06 14 1b 0b 07 0f 00 01 63 04 0b 00 63 00 17 63 7f 0c 02 04 00 28 63 00 00 00 32 00 42 61 64 53 61 6d 70 6c 65 2a 63 63 63 32 32 32 09 40 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Beta.....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 05 09 00 07 0d 20 45 54 08 0a 0d 08 01 05 0d 00 46 4e 00 0a 1c 00 00 04 0f 00 46 2f 19 04 10 05 00 05 0f 00 45 54 08 0c 3a 23 00 05 30 62 00 05 04 00 63 32 00 00 00 32 00 42 65 74 61 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Beta.....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 09 00 07 0d 20 43 58 08 0a 0d 03 00 05 07 00 44 56 04 02 1c 00 00 04 0f 00 46 45 0d 04 10 04 00 05 07 00 45 56 08 04 2b 23 00 05 30 62 00 05 04 00 63 32 00 00 00 32 00 42 65 74 61 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BigWash ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 0e 05 00 18 02 54 13 10 1f 19 08 0a 0c 00 03 5c 13 0c 1f 05 00 02 00 5e 04 52 24 11 1f 10 08 07 0e 00 04 63 0d 0b 02 1a 16 03 00 52 00 02 04 00 63 28 00 1e 00 32 00 42 69 67 57 61 73 68 20 20 20 63 63 63 32 32 32 00 20 20 30 10 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chroma...1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 1f 07 09 00 03 44 2a 03 15 16 03 04 0d 00 06 55 0d 01 18 19 07 07 0d 00 03 63 04 06 0f 04 03 07 0d 00 05 63 04 06 64 1a 00 00 00 12 0c 04 04 00 28 32 00 00 00 32 00 43 68 72 6f 6d 61 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chroma...2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 00 09 0a 00 02 5c 08 04 1f 13 1f 01 07 00 03 55 10 03 13 15 06 09 0d 00 06 59 04 03 1f 09 0b 06 0c 00 04 63 04 0b 2c 21 17 02 0d 32 18 04 04 00 28 32 00 00 00 32 00 43 68 72 6f 6d 61 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chroma...3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 06 03 08 0f 00 04 54 04 13 1f 0a 00 09 07 00 04 41 2a 1b 15 06 03 09 0f 00 04 5c 04 13 1f 0a 00 09 0d 00 05 63 04 0b 34 23 00 00 00 12 18 04 04 00 28 32 00 00 00 32 00 43 68 72 6f 6d 61 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Crazy 6502 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 09 04 07 0f 00 43 48 0a 08 10 09 04 07 0f 00 44 63 0d 0e 10 09 04 07 0f 00 45 63 04 08 0f 03 01 07 0f 00 45 63 04 08 05 2c 00 63 63 37 24 04 04 00 63 01 63 00 00 32 00 43 72 61 7a 79 20 36 35 30 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Crazy 6502 ** Controls: MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 09 04 07 0f 00 40 48 0a 08 10 09 04 07 0f 00 40 63 0d 0e 10 09 04 07 0f 00 40 63 04 08 0f 03 01 07 0f 00 40 63 04 08 05 2c 00 63 63 37 24 04 04 00 63 01 63 00 00 32 00 43 72 61 7a 79 20 36 35 30 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DigiAnnie ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 32 1f 0c 05 05 0c 17 02 53 0a 08 1f 11 02 05 0d 1e 02 45 04 00 1f 13 0e 05 0d 32 01 3f 1f 06 1f 1f 08 06 0f 00 02 61 04 16 39 1d 00 00 00 52 0c 04 04 00 63 32 00 00 00 32 00 44 69 67 69 41 6e 6e 69 65 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> DS 1 <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 1f 00 02 0f 00 05 58 04 1e 17 1f 01 02 0f 0a 04 49 0d 03 19 1f 00 03 0f 08 06 63 00 16 12 1f 00 01 0f 14 06 63 00 1b 34 00 00 00 00 02 0c 07 04 00 63 63 00 32 00 32 00 3c 3e 20 44 53 20 31 20 3c 3e 63 63 63 32 32 32 00 00 00 30 00 00 00 20 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> DS 1 <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 1f 00 02 0f 00 02 58 04 1e 17 1f 01 02 0f 0a 01 49 0d 03 19 1f 00 03 0f 08 00 63 00 16 12 1f 00 01 0f 14 00 63 00 1b 34 00 00 00 00 02 0c 07 04 00 63 63 00 32 00 32 00 3c 3e 20 44 53 20 31 20 3c 3e 63 63 63 32 32 32 00 00 00 30 00 00 00 20 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: < DS 11 > ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 1f 00 02 0f 00 04 4f 04 1e 17 1f 01 02 0f 0a 04 3b 04 03 19 1f 00 03 0f 08 04 63 00 16 12 1f 00 01 0f 14 07 63 10 1b 34 20 00 0a 00 02 0c 07 04 00 63 63 00 32 00 32 00 3c 20 44 53 20 31 31 20 3e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: < DS 11 > ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 1f 00 02 0f 00 00 4f 04 1e 17 1f 01 02 0f 0a 00 3b 04 03 19 1f 00 03 0f 08 00 63 00 16 12 1f 00 01 0f 14 00 63 10 1b 34 20 00 0a 00 02 0c 07 04 00 63 63 00 32 00 32 00 3c 20 44 53 20 31 31 20 3e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: EML Synth1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 00 01 04 07 00 05 56 04 0e 0f 07 01 05 0b 00 06 52 04 08 13 07 00 06 0e 00 04 60 00 03 13 05 00 06 0e 00 06 62 04 0b 7c 19 00 0c 00 42 18 07 04 00 63 32 63 32 00 32 00 45 4d 4c 20 53 79 6e 74 68 31 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: EML Synth1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 00 01 04 07 00 02 56 04 0e 0f 07 01 05 0b 00 02 52 04 08 13 07 00 06 0e 00 00 60 00 03 13 05 00 06 0e 00 00 62 04 0b 7c 19 00 0c 00 42 18 07 04 00 63 32 63 32 00 32 00 45 4d 4c 20 53 79 6e 74 68 31 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: EML Synth2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 00 01 04 07 00 03 54 04 0e 0f 07 01 05 0b 00 02 5b 08 10 13 07 00 06 0e 00 05 60 00 03 13 05 00 06 0e 00 05 62 08 0b 7c 19 00 0c 00 42 18 07 04 00 63 32 63 32 00 32 00 45 4d 4c 20 53 79 6e 74 68 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: EML Synth2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 00 01 04 07 00 02 54 04 0e 0f 07 01 05 0b 00 01 5b 08 10 13 07 00 06 0e 00 00 60 00 03 13 05 00 06 0e 00 00 62 08 0b 7c 19 00 0c 00 42 18 07 04 00 63 32 63 32 00 32 00 45 4d 4c 20 53 79 6e 74 68 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Explorer.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 09 07 07 0d 00 02 5a 00 09 0f 00 03 06 0d 00 03 63 04 05 15 09 07 07 0d 00 07 63 04 0a 0f 00 03 06 0d 00 06 63 08 09 7e 23 30 00 00 22 18 04 04 00 63 01 63 00 00 32 00 45 78 70 6c 6f 72 65 72 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Explorer.1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 09 07 07 0d 00 02 5a 00 09 0f 00 03 06 0d 00 00 63 04 05 15 09 07 07 0d 00 00 63 04 0a 0f 00 03 06 0d 00 00 63 08 09 7e 23 30 00 00 22 18 04 04 00 63 01 63 00 00 32 00 45 78 70 6c 6f 72 65 72 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Explorer.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 09 07 07 0d 00 05 63 19 09 0f 00 03 06 0d 00 04 52 08 00 15 09 07 07 0d 00 06 41 00 0e 13 00 03 06 0d 00 05 63 04 0b 77 17 00 0b 00 22 18 04 04 00 63 01 63 00 00 32 00 45 78 70 6c 6f 72 65 72 2e 32 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Explorer.2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 09 07 07 0d 00 00 63 19 09 0f 00 03 06 0d 00 00 52 08 00 15 09 07 07 0d 00 00 41 00 0e 13 00 03 06 0d 00 00 63 04 0b 77 17 00 0b 00 22 18 04 04 00 63 01 63 00 00 32 00 45 78 70 6c 6f 72 65 72 2e 32 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Explorer.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 09 07 07 0d 00 02 51 08 0c 0f 00 04 06 0d 00 04 63 08 0a 15 09 07 07 0d 00 04 63 04 0d 0f 00 04 06 0d 00 06 63 0d 08 7e 17 00 0b 00 22 18 04 04 00 63 01 63 00 00 32 00 45 78 70 6c 6f 72 65 72 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Explorer.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 09 07 07 0d 00 02 51 08 0c 0f 00 04 06 0d 00 00 63 08 0a 15 09 07 07 0d 00 00 63 04 0d 0f 00 04 06 0d 00 00 63 0d 08 7e 17 00 0b 00 22 18 04 04 00 63 01 63 00 00 32 00 45 78 70 6c 6f 72 65 72 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FMMelodica ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0c 00 0a 0d 20 44 50 0a 09 11 0c 00 0a 0d 20 44 50 0a 03 11 05 00 0a 0f 00 43 63 04 01 11 05 00 09 0f 00 43 63 04 0b 44 21 15 03 05 5e 11 04 04 00 63 1e 55 00 00 32 00 46 4d 4d 65 6c 6f 64 69 63 61 63 63 63 32 32 32 00 40 00 40 00 28 00 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gamma....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 06 00 02 0e 14 03 5c 04 06 08 1f 00 02 0f 00 44 47 08 13 06 1f 00 04 0f 4e 04 3c 0d 03 10 06 00 05 0e 00 06 5f 08 0b 72 21 1d 0e 1a 56 0c 08 04 00 63 32 00 00 00 32 00 47 61 6d 6d 61 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gamma....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 06 00 02 0e 14 05 52 08 06 0c 1f 00 02 0f 00 45 3b 08 13 0a 1f 00 04 0f 4e 05 4c 10 03 10 06 00 05 0e 00 06 5f 04 0b 6a 21 1d 0e 1a 56 0c 08 04 01 63 32 00 00 00 32 00 47 61 6d 6d 61 2e 2e 2e 2e 32 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 18 ** Name: GleeSynth ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 03 09 06 0e 0a 03 52 08 14 0c 04 07 06 0c 00 04 42 19 08 15 04 02 05 0e 00 05 63 08 06 0b 15 0c 06 0f 00 45 5e 19 08 2c 26 21 08 47 26 00 02 04 00 28 02 00 00 00 32 00 47 6c 65 65 53 79 6e 74 68 20 63 63 63 32 32 32 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GleeSynth ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 03 09 06 0e 0a 01 52 08 14 0c 04 07 06 0c 00 01 42 19 08 15 04 02 05 0e 00 00 63 08 06 0b 15 0c 06 0f 00 40 5e 19 08 2c 26 21 08 47 26 00 02 04 00 28 02 00 00 00 32 00 47 6c 65 65 53 79 6e 74 68 20 63 63 63 32 32 32 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HardSync ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 11 00 00 05 0c 00 00 44 05 06 1b 05 00 0a 0e 00 01 4f 0a 0a 18 07 00 08 0e 3b 02 43 20 09 15 0a 00 0b 0f 00 01 63 04 0b 72 1d 08 00 00 52 05 04 04 01 63 41 00 00 00 32 00 48 61 72 64 53 79 6e 63 20 20 63 63 63 32 32 32 00 51 00 50 00 71 00 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Helix....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 08 1f 00 03 0f 63 04 58 08 03 08 0e 00 05 01 00 04 63 04 02 07 04 0c 06 07 3c 05 61 0d 0c 08 1f 01 05 0f 00 07 62 00 13 75 22 00 14 00 32 0c 04 04 00 63 01 63 00 00 32 00 48 65 6c 69 78 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Helix....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 08 1f 00 03 0f 63 02 58 08 03 08 0e 00 05 01 00 00 63 04 02 07 04 0c 06 07 3c 00 61 0d 0c 08 1f 01 05 0f 00 00 62 00 13 75 22 00 14 00 32 0c 04 04 00 63 01 63 00 00 32 00 48 65 6c 69 78 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Helix....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 08 1f 00 03 0f 63 05 5f 08 03 08 07 00 05 08 00 06 53 00 02 07 04 0c 06 07 3c 05 61 0d 0c 08 1f 01 05 0f 00 06 63 04 13 6c 22 00 14 00 32 0c 04 04 00 63 01 63 00 00 32 00 48 65 6c 69 78 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Helix....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 08 1f 00 03 0f 63 02 5f 08 03 08 07 00 05 08 00 01 53 00 02 07 04 0c 06 07 3c 00 61 0d 0c 08 1f 01 05 0f 00 00 63 04 13 6c 22 00 14 00 32 0c 04 04 00 63 01 63 00 00 32 00 48 65 6c 69 78 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: \ ISAO \ ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 17 1b 01 0e 00 42 39 04 0b 0f 00 03 05 0d 00 03 2e 08 0b 0f 00 03 05 0d 00 45 63 08 0b 10 00 03 05 0d 00 06 63 04 0b 2c 1b 00 3c 63 26 18 02 04 01 63 32 00 32 00 32 00 20 5c 20 49 53 41 4f 20 5c 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: \ ISAO \ ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 17 1b 01 0e 00 40 39 04 0b 0f 00 03 05 0d 00 00 2e 08 0b 0f 00 03 05 0d 00 40 63 08 0b 10 00 03 05 0d 00 00 63 04 0b 2c 1b 00 3c 63 26 18 02 04 01 63 32 00 32 00 32 00 20 5c 20 49 53 41 4f 20 5c 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: \ ISAO \ ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 18 17 1b 00 0e 00 40 39 04 0b 0f 00 03 05 0d 00 00 2e 08 0b 0f 00 03 05 0d 00 40 63 08 0b 10 00 03 05 0d 00 00 63 04 0b 2c 1b 00 3c 63 26 18 02 04 01 63 32 00 32 00 32 00 20 5c 20 49 53 41 4f 20 5c 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: JP 4.....1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 17 05 01 05 08 00 04 51 0a 03 18 09 00 05 0b 2d 04 60 00 13 14 08 00 03 0c 43 05 63 04 1b 18 08 04 04 07 00 06 63 04 13 7c 21 00 38 00 22 0c 06 04 00 63 32 00 32 00 32 00 4a 50 20 34 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: JP 4.....1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 17 05 01 05 08 00 01 51 0a 03 18 09 00 05 0b 2d 01 60 00 13 14 08 00 03 0c 43 00 63 04 1b 18 08 04 04 07 00 00 63 04 13 7c 21 00 38 00 22 0c 06 04 00 63 32 00 32 00 32 00 4a 50 20 34 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: JP 4.....2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 08 03 05 0f 00 03 54 04 06 16 0a 0a 05 0d 00 05 54 0d 03 16 09 01 06 0f 00 07 63 04 00 16 15 01 06 0d 00 07 59 00 05 3c 25 49 0f 00 32 0c 0c 04 00 63 00 00 00 00 32 00 4a 50 20 34 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: JP 4.....2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 08 03 05 0f 00 01 54 04 06 16 0a 0a 05 0d 00 03 54 0d 03 16 09 01 06 0f 00 04 63 04 00 16 15 01 06 0d 00 00 59 00 05 3c 25 49 0f 00 32 0c 0c 04 00 63 00 00 00 00 32 00 4a 50 20 34 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Juno Juice ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 06 0a 05 03 0f 00 02 4e 03 02 1f 0e 00 03 0f 00 01 50 05 01 15 0e 00 07 0f 00 02 63 03 01 16 0e 00 07 0f 00 02 63 05 06 3c 20 00 00 00 52 05 02 04 00 63 32 00 00 00 32 00 4a 75 6e 6f 20 4a 75 69 63 65 63 63 63 32 32 32 00 06 00 31 00 56 00 11 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Juno Who? ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 00 03 0f 00 01 4a 03 02 1f 0e 00 03 0f 00 01 50 05 01 15 0e 00 07 0f 00 02 63 03 01 16 0e 00 07 0f 00 02 63 05 06 3c 20 00 00 00 52 05 02 04 00 63 32 00 00 00 32 00 4a 75 6e 6f 20 57 68 6f 3f 20 63 63 63 32 32 32 00 26 00 31 00 56 00 11 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jupiter * ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 04 0f 35 00 3d 22 02 1f 00 00 04 0f 19 00 41 0d 05 1f 00 00 04 0f 3b 00 41 18 06 0c 0a 00 05 0e 00 40 63 0d 0c 01 1e 0c 0f 05 56 00 02 04 00 28 63 00 00 00 32 00 4a 75 70 69 74 65 72 20 20 2a 63 63 63 32 32 32 00 0f 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: \ Kitaro.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 1f 04 07 0f 00 02 4f 00 06 17 0c 06 09 0f 00 01 5a 00 16 14 1f 04 09 0f 00 06 63 04 06 18 1f 00 0d 0f 00 06 56 04 03 7c 29 00 05 00 22 18 05 04 00 63 63 63 00 00 32 00 5c 20 4b 69 74 61 72 6f 2e 31 63 63 63 32 32 32 00 00 00 30 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: \ Kitaro.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 1f 04 07 0f 00 02 4f 00 06 17 0c 06 09 0f 00 01 5a 00 16 14 1f 04 09 0f 00 00 63 04 06 18 1f 00 0d 0f 00 00 56 04 03 7c 29 00 05 00 22 18 05 04 00 63 63 63 00 00 32 00 5c 20 4b 69 74 61 72 6f 2e 31 63 63 63 32 32 32 00 00 00 30 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: \ Kitaro.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 09 09 0f 00 3c 52 0a 02 08 04 03 09 0f 00 1d 55 04 02 1f 0f 09 09 0f 00 1d 63 04 04 1f 0f 09 09 0f 00 3e 63 04 04 64 1f 1c 14 00 32 18 02 04 00 63 32 00 00 00 32 00 5c 20 4b 69 74 61 72 6f 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: \ Kitaro.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 09 09 0f 00 3a 52 0a 02 08 04 03 09 0f 00 1a 55 04 02 1f 0f 09 09 0f 00 18 63 04 04 1f 0f 09 09 0f 00 38 63 04 04 64 1f 1c 14 00 32 18 02 04 00 63 32 00 00 00 32 00 5c 20 4b 69 74 61 72 6f 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Korg 770.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 1f 07 02 0f 47 04 35 04 05 0a 1f 02 02 0f 00 45 43 04 02 0a 1f 02 04 0f 4e 05 55 04 0c 0a 1f 02 05 0f 00 06 60 08 1b 63 21 1d 0e 1a 56 0c 08 04 0a 63 32 00 00 00 32 00 4b 6f 72 67 20 37 37 30 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Korg 770.1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 1f 07 02 0f 47 00 35 04 05 0a 1f 02 02 0f 00 40 43 04 02 0a 1f 02 04 0f 4e 00 55 04 0c 0a 1f 02 05 0f 00 00 60 08 1b 63 21 1d 0e 1a 56 0c 08 04 0a 63 32 00 00 00 32 00 4b 6f 72 67 20 37 37 30 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Korg 770.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 1f 07 02 0f 47 03 48 04 05 0f 1f 00 08 0f 00 46 62 04 02 0f 1f 00 08 0f 00 05 63 04 04 0f 1f 00 08 0f 00 04 60 08 03 7d 21 1d 0e 1a 56 0c 06 0d 02 63 32 00 00 00 32 00 4b 6f 72 67 20 37 37 30 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Korg 770.2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 1f 07 02 0f 47 00 48 04 05 0f 1f 00 08 0f 00 40 62 04 02 0f 1f 00 08 0f 00 00 63 04 04 0f 1f 00 08 0f 00 00 60 08 03 7d 21 1d 0e 1a 56 0c 06 0d 02 63 32 00 00 00 32 00 4b 6f 72 67 20 37 37 30 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Korg 770.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 0a 07 09 0e 4d 03 4f 04 03 12 1f 00 0a 0f 00 05 39 04 03 12 1f 00 0a 0f 4b 05 38 04 03 12 1f 00 0a 0f 01 07 5f 04 03 3b 1e 05 0a 00 62 0c 02 04 00 63 32 00 00 00 32 00 4b 6f 72 67 20 37 37 30 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Korg 770.3 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 0a 07 09 0e 4d 00 4f 04 03 12 1f 00 0a 0f 00 00 39 04 03 12 1f 00 0a 0f 4b 00 38 04 03 12 1f 00 0a 0f 01 00 5f 04 03 3b 1e 05 0a 00 62 0c 02 04 00 63 32 00 00 00 32 00 4b 6f 72 67 20 37 37 30 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Synthesizer2 ** Name: Lambda...1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 06 04 04 0f 00 04 41 08 01 1f 0e 05 05 09 00 07 4a 13 04 09 06 00 05 0f 00 06 5f 08 06 1f 11 08 05 0f 00 06 61 04 00 44 23 2f 19 00 32 18 07 04 00 63 32 63 32 00 32 00 4c 61 6d 62 64 61 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lambda...1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 06 04 04 0f 00 01 41 08 01 1f 0e 05 05 09 00 02 4a 13 04 09 06 00 05 0f 00 00 5f 08 06 1f 11 08 05 0f 00 00 61 04 00 44 23 2f 19 00 32 18 07 04 00 63 32 63 32 00 32 00 4c 61 6d 62 64 61 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lambda...2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 0c 09 05 0d 00 03 45 10 03 1f 0b 0a 08 0c 00 04 54 04 03 0b 0c 09 05 0d 00 06 5f 04 03 1f 04 09 08 0f 00 06 5f 00 03 74 23 00 00 00 32 18 07 04 00 63 32 63 32 00 32 00 4c 61 6d 62 64 61 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lambda...2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 0c 09 05 0d 00 01 45 10 03 1f 0b 0a 08 0c 00 01 54 04 03 0b 0c 09 05 0d 00 00 5f 04 03 1f 04 09 08 0f 00 00 5f 00 03 74 23 00 00 00 32 18 07 04 00 63 32 63 32 00 32 00 4c 61 6d 62 64 61 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lambda...3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 07 10 03 09 0f 00 05 47 0d 00 1f 14 05 05 09 00 04 4d 13 16 07 10 03 09 0f 00 05 5c 0d 03 1f 14 08 05 0f 00 05 63 04 02 44 23 2f 19 00 32 18 07 04 00 63 32 63 32 00 32 00 4c 61 6d 62 64 61 2e 2e 2e 33 63 63 63 32 32 32 00 40 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lambda...3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 07 10 03 09 0f 00 02 47 0d 00 1f 14 05 05 09 00 01 4d 13 16 07 10 03 09 0f 00 00 5c 0d 03 1f 14 08 05 0f 00 00 63 04 02 44 23 2f 19 00 32 18 07 04 00 63 32 63 32 00 32 00 4c 61 6d 62 64 61 2e 2e 2e 33 63 63 63 32 32 32 00 40 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lambda...4 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 07 10 03 0d 0f 00 03 4c 08 00 1f 14 05 04 09 00 03 4d 0d 16 07 10 03 0e 0f 00 06 5c 08 03 1f 14 08 04 0f 00 03 63 08 02 44 23 2f 19 00 32 18 07 04 00 63 32 63 32 00 32 00 4c 61 6d 62 64 61 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lambda...4 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 07 10 03 0d 0f 00 01 4c 08 00 1f 14 05 04 09 00 01 4d 0d 16 07 10 03 0e 0f 00 00 5c 08 03 1f 14 08 04 0f 00 00 63 08 02 44 23 2f 19 00 32 18 07 04 00 63 32 63 32 00 32 00 4c 61 6d 62 64 61 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lambda...5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 05 03 03 05 08 00 03 55 08 00 18 06 09 05 09 00 04 51 04 06 05 10 03 05 0f 00 07 63 08 03 18 06 09 05 09 00 06 63 04 03 24 21 23 0f 00 12 18 05 04 00 63 63 63 00 00 32 00 4c 61 6d 62 64 61 2e 2e 2e 35 63 63 63 32 32 32 00 20 00 10 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lambda...5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 05 03 03 05 08 00 02 55 08 00 18 06 09 05 09 00 01 51 04 06 05 10 03 05 0f 00 00 63 08 03 18 06 09 05 09 00 00 63 04 03 24 21 23 0f 00 12 18 05 04 00 63 63 63 00 00 32 00 4c 61 6d 62 64 61 2e 2e 2e 35 63 63 63 32 32 32 00 20 00 10 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 07 0f 00 03 54 08 03 07 0d 04 0a 0f 00 04 59 0a 03 1f 03 03 09 0c 00 06 62 0a 03 0f 0d 05 0f 0f 00 05 63 0a 03 34 23 15 05 63 26 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 31 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 07 0f 00 01 54 08 03 07 0d 04 0a 0f 00 01 59 0a 03 1f 03 03 09 0c 00 00 62 0a 03 0f 0d 05 0f 0f 00 00 63 0a 03 34 23 15 05 63 26 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 31 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 15 06 0a 0e 00 04 4b 28 0b 1b 0a 09 0f 0e 00 04 5c 04 0b 1f 03 0a 09 0c 00 06 62 08 13 18 09 06 0a 0e 00 05 63 04 03 2c 23 15 05 63 26 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 32 63 63 63 32 32 32 00 00 00 50 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 15 06 0a 0e 00 01 4b 28 0b 1b 0a 09 0f 0e 00 01 5c 04 0b 1f 03 0a 09 0c 00 00 62 08 13 18 09 06 0a 0e 00 00 63 04 03 2c 23 15 05 63 26 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 32 63 63 63 32 32 32 00 00 00 50 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 03 07 0f 0f 00 03 45 04 06 1f 1f 0a 0f 0f 00 04 5b 00 0c 1f 04 00 0f 0f 00 06 5f 04 03 1f 1f 00 0f 0f 00 05 63 04 05 6c 23 00 00 00 32 18 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 33 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 03 07 0f 0f 00 01 45 04 06 1f 1f 0a 0f 0f 00 02 5b 00 0c 1f 04 00 0f 0f 00 00 5f 04 03 1f 1f 00 0f 0f 00 00 63 04 05 6c 23 00 00 00 32 18 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 33 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 03 07 0f 0f 2e 03 5a 04 06 1c 1f 03 0f 0f 2c 04 5b 00 0c 1f 04 00 0f 0f 00 05 5f 04 03 16 1f 00 0f 0f 00 06 63 04 05 6c 23 00 00 00 32 18 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 34 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 03 07 0f 0f 2e 01 5a 04 06 1c 1f 03 0f 0f 2c 01 5b 00 0c 1f 04 00 0f 0f 00 00 5f 04 03 16 1f 00 0f 0f 00 00 63 04 05 6c 23 00 00 00 32 18 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 34 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 15 06 0a 0e 2a 03 4a 22 06 1b 09 09 0f 0d 3a 04 63 04 0c 18 09 04 0a 0e 00 06 63 04 03 18 09 06 0a 0e 01 06 5e 0d 12 6c 23 00 00 00 32 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 35 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 15 06 0a 0e 2a 01 4a 22 06 1b 09 09 0f 0d 3a 00 63 04 0c 18 09 04 0a 0e 00 00 63 04 03 18 09 06 0a 0e 01 00 5e 0d 12 6c 23 00 00 00 32 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 35 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 09 03 06 07 2a 05 62 04 03 18 10 06 07 0b 3a 05 63 04 0b 15 1f 1c 0f 00 00 05 63 04 03 15 06 06 0a 0c 01 05 63 04 03 6c 23 00 00 00 42 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 36 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 09 03 06 07 2a 01 62 04 03 18 10 06 07 0b 3a 00 63 04 0b 15 1f 1c 0f 00 00 00 63 04 03 15 06 06 0a 0c 01 00 63 04 03 6c 23 00 00 00 42 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 36 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 0f 07 06 09 42 03 57 0d 02 18 0b 09 07 0b 32 06 63 04 03 18 1f 00 0f 0f 00 05 63 04 05 1f 06 06 0a 0c 01 05 63 04 03 5c 23 00 00 00 42 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 37 63 63 63 32 32 32 00 00 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 18 0f 07 06 09 42 01 57 0d 02 18 0b 09 07 0b 32 01 63 04 03 18 1f 00 0f 0f 00 01 63 04 05 1f 06 06 0a 0c 01 00 63 04 03 5c 23 00 00 00 42 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 37 63 63 63 32 32 32 00 00 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 07 05 09 0f 42 03 56 08 06 1f 16 00 08 0e 00 05 63 04 03 1f 16 00 08 0e 00 04 63 04 03 1f 16 00 08 0e 00 06 63 04 03 6d 25 00 00 00 32 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 38 63 63 63 32 32 32 00 20 00 20 00 00 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LeadSynth8 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 07 05 09 0f 42 02 56 08 06 1f 16 00 08 0e 00 00 63 04 03 1f 16 00 08 0e 00 00 63 04 03 1f 16 00 08 0e 00 00 63 04 03 6d 25 00 00 00 32 0c 02 04 00 63 63 00 00 00 32 00 4c 65 61 64 53 79 6e 74 68 38 63 63 63 32 32 32 00 20 00 20 00 00 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Luna.....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 05 07 00 05 0e 00 44 4c 04 00 05 07 00 05 0e 0e 45 52 04 06 0c 1f 00 06 0f 00 05 63 04 00 0c 1f 00 07 0f 0a 06 63 08 06 34 22 0e 13 08 56 18 07 04 00 63 63 00 00 00 32 00 4c 75 6e 61 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Luna.....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 08 07 00 05 0e 00 04 51 00 00 0c 03 00 05 08 0e 04 40 13 06 0c 1f 00 06 0f 00 05 60 04 00 0c 1f 00 07 0f 0a 06 5e 00 06 2c 22 00 0f 00 42 18 07 04 00 63 63 00 00 00 32 00 4c 75 6e 61 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Luna.....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 05 07 00 05 0e 00 44 4b 08 00 0c 07 00 05 0e 0e 45 52 0a 06 05 1f 00 06 0f 00 05 63 08 00 0c 1f 00 07 0f 0a 06 4b 0a 06 34 22 0e 13 08 56 18 07 04 00 63 63 00 00 00 32 00 4c 75 6e 61 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lyrisyn ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 20 10 06 00 05 0e 00 01 50 0a 04 1f 04 00 05 0f 00 01 51 04 02 1f 0b 00 0a 0f 00 01 35 08 03 10 1f 00 0a 0f 00 02 63 04 03 30 1e 00 00 00 52 18 04 04 00 63 32 00 00 00 32 00 4c 79 72 69 73 79 6e 20 20 20 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MaxiKorg.1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 07 0f 63 00 53 04 06 1f 1f 00 07 0f 00 00 63 00 02 1f 1f 00 07 0f 3c 00 61 08 04 1f 1f 00 07 0f 00 00 62 04 03 75 22 00 14 00 32 0c 06 0d 01 63 01 63 00 00 32 00 4d 61 78 69 4b 6f 72 67 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MaxiKorg.2 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 08 0f 00 03 54 00 06 17 1f 00 07 0f 00 06 4b 04 02 1f 1f 00 08 0f 00 04 61 08 04 15 1f 00 08 0f 00 45 62 08 03 74 1e 00 0e 35 56 18 06 05 00 63 01 63 00 00 32 00 4d 61 78 69 4b 6f 72 67 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MaxiKorg.2 ** Controls: MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 08 0f 00 00 54 00 06 17 1f 00 07 0f 00 00 4b 04 02 1f 1f 00 08 0f 00 00 61 08 04 15 1f 00 08 0f 00 40 62 08 03 74 1e 00 0e 35 56 18 06 05 00 63 01 63 00 00 32 00 4d 61 78 69 4b 6f 72 67 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MicroMoog1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 03 01 07 0d 27 05 4b 0a 0d 14 05 01 05 0f 00 05 56 04 0c 15 04 00 0a 0d 11 06 50 08 09 17 05 00 0a 02 27 06 5a 08 05 61 24 00 04 00 42 0c 02 0d 11 63 63 00 00 00 32 00 4d 69 63 72 6f 4d 6f 6f 67 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MicroMoog1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 14 03 01 07 0d 27 02 4b 0a 0d 14 05 01 05 0f 00 02 56 04 0c 15 04 00 0a 0d 11 00 50 08 09 17 05 00 0a 02 27 00 5a 08 05 61 24 00 04 00 42 0c 02 0d 11 63 63 00 00 00 32 00 4d 69 63 72 6f 4d 6f 6f 67 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MicroMoog2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 01 01 05 0d 27 02 4d 00 03 14 02 01 05 0f 00 04 4e 00 03 15 01 00 05 0d 11 05 58 08 03 17 02 00 05 02 27 07 63 08 03 78 24 00 04 00 42 0c 02 0c 11 63 63 00 00 00 32 00 4d 69 63 72 6f 4d 6f 6f 67 32 63 63 63 32 32 32 00 00 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MicroMoog2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 14 01 01 05 0d 27 01 4d 00 03 14 02 01 05 0f 00 01 4e 00 03 15 01 00 05 0d 11 01 58 08 03 17 02 00 05 02 27 01 63 08 03 78 24 00 04 00 42 0c 02 0c 11 63 63 00 00 00 32 00 4d 69 63 72 6f 4d 6f 6f 67 32 63 63 63 32 32 32 00 00 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MiniMoog * ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 09 00 01 0d 00 00 4d 04 06 0f 0d 00 01 0b 00 00 45 04 00 0b 04 00 01 0d 00 00 44 04 03 0f 0c 00 07 0e 00 00 63 04 03 3a 21 00 00 00 52 11 02 04 00 28 63 00 00 00 32 00 4d 69 6e 69 4d 6f 6f 67 20 2a 63 63 63 32 32 32 00 08 00 08 00 08 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MiniMoog.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 1b 06 0f 0f 00 01 5c 00 02 1f 0a 03 0f 0e 00 02 41 04 0c 1f 09 04 0f 0f 00 06 62 04 14 1c 10 04 0c 0e 00 05 63 04 0b 6c 23 00 00 00 32 0c 02 04 00 63 63 00 00 00 32 00 4d 69 6e 69 4d 6f 6f 67 2e 31 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MiniMoog.1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 1b 06 0f 0f 00 01 5c 00 02 1f 0a 03 0f 0e 00 01 41 04 0c 1f 09 04 0f 0f 00 00 62 04 14 1c 10 04 0c 0e 00 00 63 04 0b 6c 23 00 00 00 32 0c 02 04 00 63 63 00 00 00 32 00 4d 69 6e 69 4d 6f 6f 67 2e 31 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MiniMoog.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0a 03 0f 0e 00 06 58 08 14 1c 1b 06 0f 0f 00 04 5c 08 0e 1c 10 04 0c 0f 00 07 62 04 0b 1f 09 09 0f 0f 00 05 63 04 14 74 23 00 00 00 32 0c 02 04 00 63 63 00 00 00 32 00 4d 69 6e 69 4d 6f 6f 67 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MiniMoog.2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 0a 03 0f 0e 00 01 58 08 14 1c 1b 06 0f 0f 00 01 5c 08 0e 1c 10 04 0c 0f 00 00 62 04 0b 1f 09 09 0f 0f 00 00 63 04 14 74 23 00 00 00 32 0c 02 04 00 63 63 00 00 00 32 00 4d 69 6e 69 4d 6f 6f 67 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MiniMoog.3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0a 03 0f 0e 00 03 5e 08 14 1b 0a 03 0f 07 00 05 60 00 06 1c 10 04 0c 0f 00 05 62 04 0b 1c 10 04 0c 0f 00 06 63 04 0c 6c 23 00 00 00 32 0c 02 04 04 63 63 00 00 00 32 00 4d 69 6e 69 4d 6f 6f 67 2e 33 63 63 63 32 32 32 00 20 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MiniMoog.3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 0a 03 0f 0e 00 03 5e 08 14 1b 0a 03 0f 07 00 01 60 00 06 1c 10 04 0c 0f 00 00 62 04 0b 1c 10 04 0c 0f 00 00 63 04 0c 6c 23 00 00 00 32 0c 02 04 04 63 63 00 00 00 32 00 4d 69 6e 69 4d 6f 6f 67 2e 33 63 63 63 32 32 32 00 20 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mirage * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 08 00 05 0f 45 01 45 08 0b 1f 00 00 04 0f 30 00 4d 08 00 11 08 03 05 0f 26 07 5b 13 16 10 0a 08 06 0f 00 02 63 08 0b 03 1e 00 0a 00 32 00 02 04 00 28 63 00 00 00 32 00 4d 69 72 61 67 65 20 20 20 2a 63 63 63 32 32 32 00 20 00 70 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MoogBass * ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 00 06 0c 00 00 4e 05 06 1f 18 00 04 0c 0f 00 50 03 00 1f 04 00 02 0d 23 00 48 04 06 17 0c 00 08 0e 19 00 63 04 03 3a 21 00 00 00 52 0c 02 0c 00 28 63 00 00 00 32 00 4d 6f 6f 67 42 61 73 73 20 2a 63 63 63 32 32 32 00 11 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MultiMoog1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 07 00 05 0e 00 04 56 00 00 1f 03 00 05 08 0e 04 42 13 06 1f 1f 00 06 0f 00 07 56 04 00 1f 1f 00 07 0f 0a 06 5e 00 06 33 22 00 0f 00 42 18 07 04 00 63 63 00 00 00 32 00 4d 75 6c 74 69 4d 6f 6f 67 31 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MultiMoog1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 07 00 05 0e 00 02 56 00 00 1f 03 00 05 08 0e 01 42 13 06 1f 1f 00 06 0f 00 01 56 04 00 1f 1f 00 07 0f 0a 00 5e 00 06 33 22 00 0f 00 42 18 07 04 00 63 63 00 00 00 32 00 4d 75 6c 74 69 4d 6f 6f 67 31 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MultiMoog2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 07 00 05 0e 00 45 53 08 00 1f 07 00 05 0e 0e 46 44 0a 06 1f 1f 00 06 0f 00 04 54 08 00 1f 1f 00 07 0f 0a 06 63 0a 06 32 22 0e 13 08 56 18 07 04 00 63 63 00 00 00 32 00 4d 75 6c 74 69 4d 6f 6f 67 32 63 63 63 32 32 32 00 00 00 30 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MultiMoog2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 07 00 05 0e 00 42 53 08 00 1f 07 00 05 0e 0e 42 44 0a 06 1f 1f 00 06 0f 00 01 54 08 00 1f 1f 00 07 0f 0a 00 63 0a 06 32 22 0e 13 08 56 18 07 04 00 63 63 00 00 00 32 00 4d 75 6c 74 69 4d 6f 6f 67 32 63 63 63 32 32 32 00 00 00 30 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: No!NotJump ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 0a 00 03 0f 29 00 44 03 02 0f 0e 00 03 0f 42 01 5f 05 01 1d 0e 00 07 0f 00 02 63 03 04 13 0e 00 07 0f 00 02 63 05 06 3c 20 00 00 00 52 05 02 04 00 63 32 00 00 00 32 00 4e 6f 21 4e 6f 74 4a 75 6d 70 63 63 63 32 32 32 00 56 00 31 00 46 00 11 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> OB 1 <> ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 1f 00 02 0f 00 04 56 00 1e 17 1f 01 02 0f 0a 04 45 04 03 19 1f 00 03 0f 08 06 58 08 16 12 1f 00 01 0f 14 05 5b 04 1b 2a 00 00 00 00 02 0c 07 0c 01 63 63 00 32 00 32 00 3c 3e 20 4f 42 20 31 20 3c 3e 63 63 63 32 32 32 00 30 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: <> OB 1 <> ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 1f 00 02 0f 00 01 56 00 1e 17 1f 01 02 0f 0a 01 45 04 03 19 1f 00 03 0f 08 01 58 08 16 12 1f 00 01 0f 14 00 5b 04 1b 2a 00 00 00 00 02 0c 07 0c 01 63 63 00 32 00 32 00 3c 3e 20 4f 42 20 31 20 3c 3e 63 63 63 32 32 32 00 30 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OB-8 * ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 08 00 06 0e 0c 40 49 03 06 12 08 00 03 0e 00 00 4d 04 03 11 1f 00 06 0f 0a 00 63 03 0e 0c 1f 00 07 0f 00 00 63 04 0b 3c 23 00 00 00 5a 05 02 04 00 28 63 00 00 00 32 63 20 20 4f 42 2d 38 20 20 20 2a 63 63 63 32 32 32 00 06 00 08 00 06 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oberheim.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 0f 0f 00 04 5c 00 16 1d 0c 04 0f 0e 00 04 5e 00 02 1f 0c 04 0f 0e 00 05 63 00 04 1f 0c 04 0f 0e 00 05 63 04 03 2c 00 00 13 00 42 18 02 04 00 28 02 00 00 00 32 00 4f 62 65 72 68 65 69 6d 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oberheim.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 0f 0f 00 01 5c 00 16 1d 0c 04 0f 0e 00 01 5e 00 02 1f 0c 04 0f 0e 00 00 63 00 04 1f 0c 04 0f 0e 00 00 63 04 03 2c 00 00 13 00 42 18 02 04 00 28 02 00 00 00 32 00 4f 62 65 72 68 65 69 6d 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oberheim.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 02 04 04 0e 05 03 58 00 06 12 09 04 04 0f 00 04 4c 00 00 0b 0c 04 04 0e 00 05 63 00 03 11 0b 03 06 0f 00 05 63 00 03 2c 23 00 13 00 42 18 02 04 00 28 02 00 00 00 32 00 4f 62 65 72 68 65 69 6d 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oberheim.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 02 04 04 0e 05 02 58 00 06 12 09 04 04 0f 00 01 4c 00 00 0b 0c 04 04 0e 00 00 63 00 03 11 0b 03 06 0f 00 00 63 00 03 2c 23 00 13 00 42 18 02 04 00 28 02 00 00 00 32 00 4f 62 65 72 68 65 69 6d 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oberheim.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 0c 04 05 0e 05 03 57 00 00 0c 0c 04 04 0e 05 04 52 00 06 0a 0c 04 05 0e 00 05 63 04 06 0a 1f 00 05 0f 00 05 63 00 06 3c 17 00 20 00 42 18 02 04 00 28 02 00 00 00 32 00 4f 62 65 72 68 65 69 6d 2e 33 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Oberheim.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 0c 04 05 0e 05 02 57 00 00 0c 0c 04 04 0e 05 01 52 00 06 0a 0c 04 05 0e 00 00 63 04 06 0a 1f 00 05 0f 00 00 63 00 06 3c 17 00 20 00 42 18 02 04 00 28 02 00 00 00 32 00 4f 62 65 72 68 65 69 6d 2e 33 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Synthesizer3 ** Name: Odyssey..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 1f 04 09 0f 00 04 5c 00 0d 19 03 01 09 00 00 06 53 00 04 1f 16 03 09 0d 00 04 63 00 0a 19 06 06 09 06 00 05 63 00 0b 6c 1e 00 0a 00 32 18 04 04 00 63 01 63 00 00 32 00 4f 64 79 73 73 65 79 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Odyssey..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 16 1f 04 09 0f 00 01 5c 00 0d 19 03 01 09 00 00 02 53 00 04 1f 16 03 09 0d 00 00 63 00 0a 19 06 06 09 06 00 00 63 00 0b 6c 1e 00 0a 00 32 18 04 04 00 63 01 63 00 00 32 00 4f 64 79 73 73 65 79 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Odyssey..2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 02 01 05 0f 00 3b 5b 00 00 1b 13 01 0d 0a 00 1d 63 00 03 1f 05 06 05 0f 00 1d 63 00 00 1f 04 03 0c 0f 00 3e 63 00 0b 2c 00 00 00 00 32 18 02 04 00 63 32 00 00 00 32 00 4f 64 79 73 73 65 79 2e 2e 32 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Odyssey..2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 02 01 05 0f 00 39 5b 00 00 1b 13 01 0d 0a 00 1a 63 00 03 1f 05 06 05 0f 00 18 63 00 00 1f 04 03 0c 0f 00 38 63 00 0b 2c 00 00 00 00 32 18 02 04 00 63 32 00 00 00 32 00 4f 64 79 73 73 65 79 2e 2e 32 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Odyssey..3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 05 04 06 0e 1e 3d 56 0a 0d 1f 09 00 06 00 00 1d 5d 00 05 1b 02 04 06 0e 1e 1d 63 00 0b 1f 00 00 07 0f 00 3d 63 00 0c 2c 1d 00 00 00 42 18 02 04 00 63 32 00 00 00 32 00 4f 64 79 73 73 65 79 2e 2e 33 63 63 63 32 32 32 00 20 00 20 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Odyssey..3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 05 04 06 0e 1e 3a 56 0a 0d 1f 09 00 06 00 00 19 5d 00 05 1b 02 04 06 0e 1e 18 63 00 0b 1f 00 00 07 0f 00 38 63 00 0c 2c 1d 00 00 00 42 18 02 04 00 63 32 00 00 00 32 00 4f 64 79 73 73 65 79 2e 2e 33 63 63 63 32 32 32 00 20 00 20 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OhB1Kanobe ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 1f 00 0a 0f 00 04 55 00 0b 10 1f 00 0a 0f 00 05 55 00 0b 12 0d 09 0a 0e 15 07 62 08 0a 12 0d 09 0a 0e 15 05 63 08 0b 5c 25 00 4b 00 12 0c 02 04 00 28 02 00 00 00 32 00 4f 68 42 31 4b 61 6e 6f 62 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OhB1Kanobe ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 1f 00 0a 0f 00 01 55 00 0b 10 1f 00 0a 0f 00 01 55 00 0b 12 0d 09 0a 0e 15 00 62 08 0a 12 0d 09 0a 0e 15 00 63 08 0b 5c 25 00 4b 00 12 0c 02 04 00 28 02 00 00 00 32 00 4f 68 42 31 4b 61 6e 6f 62 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Omega....1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 08 09 10 05 08 00 03 52 10 03 1f 05 00 05 0b 2d 04 4e 08 13 1f 08 00 03 0c 43 06 47 10 1b 1c 06 04 04 07 00 06 63 04 13 7a 21 00 38 00 22 0c 06 04 00 63 32 00 32 00 32 00 4f 6d 65 67 61 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Omega....2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 03 00 0a 09 37 04 5a 08 0c 1f 1f 0d 0a 0f 39 46 63 08 01 18 04 1c 0a 0a 63 05 63 0d 02 1f 1f 00 0a 0f 00 06 62 04 06 15 46 28 0a 1a 56 0c 07 04 00 63 19 00 32 00 32 00 4f 6d 65 67 61 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PCM Duck * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 07 00 0f 0d 00 00 5c 00 08 0d 1f 00 06 0f 16 00 57 00 15 0b 06 03 0f 0e 0e 07 46 1c 10 1c 14 08 0d 0e 00 02 63 04 03 03 23 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 50 43 4d 20 44 75 63 6b 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PianoSynth ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 06 02 07 04 0c 1f 0a 46 04 0a 12 1f 00 03 0f 63 04 61 04 13 03 06 16 03 03 63 05 5a 0a 13 14 05 12 03 03 00 05 5e 08 0d 7b 23 0f 0e 00 42 0c 02 04 00 63 32 00 00 00 32 00 50 69 61 6e 6f 53 79 6e 74 68 63 63 63 32 32 32 00 20 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PianoSynth ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 06 02 07 04 0c 1f 0a 46 04 0a 12 1f 00 03 0f 63 01 61 04 13 03 06 16 03 03 63 01 5a 0a 13 14 05 12 03 03 00 00 5e 08 0d 7b 23 0f 0e 00 42 0c 02 04 00 63 32 00 00 00 32 00 50 69 61 6e 6f 53 79 6e 74 68 63 63 63 32 32 32 00 20 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PlkAnalog ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 14 02 01 06 00 06 51 0a 0e 10 1f 02 05 0f 00 04 63 03 08 0b 1f 00 08 0f 00 03 63 05 0e 1a 1f 00 05 0f 00 04 60 04 0b 3d 1f 0e 00 00 46 11 02 04 00 28 32 00 12 00 32 00 50 6c 6b 41 6e 61 6c 6f 67 20 63 63 63 32 32 32 04 30 00 56 00 31 07 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Poly 61 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 07 03 04 07 0a 04 5a 08 03 1f 03 0c 06 00 00 05 60 08 0b 12 10 0c 06 0e 00 05 61 08 01 12 12 0c 06 0e 00 45 5f 08 0b 35 1f 0b 11 1b 26 18 02 04 00 28 02 00 00 00 32 00 50 6f 6c 79 20 36 31 20 20 20 63 63 63 32 32 32 00 00 00 10 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Poly 61 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 07 03 04 07 0a 02 5a 08 03 1f 03 0c 06 00 00 00 60 08 0b 12 10 0c 06 0e 00 00 61 08 01 12 12 0c 06 0e 00 40 5f 08 0b 35 1f 0b 11 1b 26 18 02 04 00 28 02 00 00 00 32 00 50 6f 6c 79 20 36 31 20 20 20 63 63 63 32 32 32 00 00 00 10 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PolyFusion ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0a 07 00 05 0e 00 44 56 04 00 08 07 00 05 0e 0e 44 51 04 06 0e 1f 00 06 0f 00 04 63 04 00 0c 1f 00 07 0f 0a 06 63 08 06 34 22 0e 13 08 56 18 07 04 00 63 63 00 00 00 32 00 50 6f 6c 79 46 75 73 69 6f 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PolyFusion ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0a 07 00 05 0e 00 41 56 04 00 08 07 00 05 0e 0e 41 51 04 06 0e 1f 00 06 0f 00 00 63 04 00 0c 1f 00 07 0f 0a 00 63 08 06 34 22 0e 13 08 56 18 07 04 00 63 63 00 00 00 32 00 50 6f 6c 79 46 75 73 69 6f 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PolyMoog.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 03 02 04 01 0a 05 59 08 0b 1f 0a 0c 06 00 00 44 50 19 13 10 03 03 06 0e 00 06 63 0d 01 12 03 01 06 0f 00 45 63 08 0b 2c 1f 0b 11 1b 26 0c 02 04 00 28 02 00 00 00 32 00 50 6f 6c 79 4d 6f 6f 67 2e 31 63 63 63 32 32 32 00 20 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PolyMoog.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 03 02 04 01 0a 02 59 08 0b 1f 0a 0c 06 00 00 41 50 19 13 10 03 03 06 0e 00 01 63 0d 01 12 03 01 06 0f 00 40 63 08 0b 2c 1f 0b 11 1b 26 0c 02 04 00 28 02 00 00 00 32 00 50 6f 6c 79 4d 6f 6f 67 2e 31 63 63 63 32 32 32 00 20 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PolyMoog.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 1f 00 03 0f 63 03 59 08 0e 0e 1f 01 05 0f 00 04 63 08 0c 0a 04 0c 06 07 3c 06 63 0d 12 0e 1f 01 05 0f 00 05 63 04 0a 75 22 00 14 00 32 00 04 04 00 63 01 63 00 00 32 00 50 6f 6c 79 4d 6f 6f 67 2e 32 63 63 63 32 32 32 00 20 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PolyMoog.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 1f 00 03 0f 63 01 59 08 0e 0e 1f 01 05 0f 00 00 63 08 0c 0a 04 0c 06 07 3c 00 63 0d 12 0e 1f 01 05 0f 00 00 63 04 0a 75 22 00 14 00 32 00 04 04 00 63 01 63 00 00 32 00 50 6f 6c 79 4d 6f 6f 67 2e 32 63 63 63 32 32 32 00 20 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: P.P.G * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 05 07 0f 22 03 4c 07 02 12 04 02 04 0d 08 00 59 00 05 1d 10 06 01 0d 0c 00 2f 3d 06 1f 0a 00 07 0f 09 02 63 04 03 02 29 00 1f 00 32 0c 02 04 00 28 63 00 00 00 32 00 20 50 2e 50 2e 47 20 20 20 2a 63 63 63 32 32 32 00 07 00 00 00 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Prophet * ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 0a 00 05 0b 00 00 4f 03 00 1f 0b 00 01 0a 00 00 4a 03 05 1b 16 00 05 0d 00 00 63 03 06 1c 16 00 06 0d 00 00 63 05 00 34 1f 00 06 00 32 05 02 04 00 28 63 00 00 00 32 00 50 72 6f 70 68 65 74 20 20 2a 63 63 63 32 32 32 00 06 00 06 00 06 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Prophet..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 03 04 0a 06 00 04 4b 04 00 13 07 04 0a 0c 00 04 46 04 06 16 07 04 0a 0c 00 05 22 16 03 17 07 04 0a 0c 00 06 63 04 03 2b 22 21 00 00 32 18 04 05 00 28 32 00 00 00 32 00 50 72 6f 70 68 65 74 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Prophet..1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 13 03 04 0a 06 00 02 4b 04 00 13 07 04 0a 0c 00 01 46 04 06 16 07 04 0a 0c 00 01 22 16 03 17 07 04 0a 0c 00 00 63 04 03 2b 22 21 00 00 32 18 04 05 00 28 32 00 00 00 32 00 50 72 6f 70 68 65 74 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Prophet..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 03 02 07 0f 00 04 4e 04 00 1b 0c 0f 07 0f 19 06 49 0d 03 1b 06 04 07 0d 00 05 63 04 0b 1b 03 03 07 0d 00 07 63 04 15 74 23 00 12 00 12 18 04 05 00 28 32 00 00 00 32 00 50 72 6f 70 68 65 74 2e 2e 32 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Prophet..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 03 02 07 0f 00 02 4e 04 00 1b 0c 0f 07 0f 19 02 49 0d 03 1b 06 04 07 0d 00 00 63 04 0b 1b 03 03 07 0d 00 00 63 04 15 74 23 00 12 00 12 18 04 05 00 28 32 00 00 00 32 00 50 72 6f 70 68 65 74 2e 2e 32 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Prophet..3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 04 12 08 0c 19 05 4f 04 00 0e 04 12 08 0c 19 06 4c 04 03 10 03 06 08 0e 00 06 63 04 0b 10 03 04 08 0e 00 06 63 04 0d 3c 26 1d 23 00 22 18 04 05 00 28 32 00 00 00 32 00 50 72 6f 70 68 65 74 2e 2e 33 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Prophet..3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 04 12 08 0c 19 01 4f 04 00 0e 04 12 08 0c 19 01 4c 04 03 10 03 06 08 0e 00 00 63 04 0b 10 03 04 08 0e 00 00 63 04 0d 3c 26 1d 23 00 22 18 04 05 00 28 32 00 00 00 32 00 50 72 6f 70 68 65 74 2e 2e 33 63 63 63 32 32 32 00 00 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PS 3100..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 08 1f 07 02 0f 47 04 54 0d 05 08 1f 02 02 0f 00 44 53 08 02 07 1f 02 04 0f 4e 06 4a 00 0c 07 1f 02 05 0f 00 05 5f 08 1b 63 21 1d 0e 1a 56 0c 08 04 0a 63 32 00 00 00 32 00 50 53 20 33 31 30 30 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PS 3100..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 08 1f 07 02 0f 47 01 54 0d 05 08 1f 02 02 0f 00 41 53 08 02 07 1f 02 04 0f 4e 01 4a 00 0c 07 1f 02 05 0f 00 00 5f 08 1b 63 21 1d 0e 1a 56 0c 08 04 0a 63 32 00 00 00 32 00 50 53 20 33 31 30 30 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PS 3100..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 1f 07 02 0f 47 04 53 0a 06 07 1f 02 02 0f 00 44 4e 04 04 0d 1f 02 04 0f 4e 06 48 0d 0a 07 1f 02 05 0f 00 05 62 04 19 61 21 1d 0e 1a 56 0c 08 04 0f 63 32 00 00 00 32 00 50 53 20 33 31 30 30 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PS 3100..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 1f 07 02 0f 47 01 53 0a 06 07 1f 02 02 0f 00 41 4e 04 04 0d 1f 02 04 0f 4e 01 48 0d 0a 07 1f 02 05 0f 00 00 62 04 19 61 21 1d 0e 1a 56 0c 08 04 0f 63 32 00 00 00 32 00 50 53 20 33 31 30 30 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PS 3300..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 07 01 02 0f 47 03 54 08 05 0c 08 01 02 0f 00 44 50 08 02 0b 07 01 04 0f 4e 06 4f 00 0c 0c 06 02 05 0f 00 06 5f 08 0b 63 21 1d 0e 1a 56 0c 08 04 0a 63 32 00 00 00 32 00 50 53 20 33 33 30 30 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PS 3300..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 07 01 02 0f 47 01 54 08 05 0c 08 01 02 0f 00 41 50 08 02 0b 07 01 04 0f 4e 01 4f 00 0c 0c 06 02 05 0f 00 00 5f 08 0b 63 21 1d 0e 1a 56 0c 08 04 0a 63 32 00 00 00 32 00 50 53 20 33 33 30 30 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PS 3300..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 05 01 02 0d 47 03 4d 08 06 0f 05 00 02 0f 00 43 4a 04 04 07 06 01 04 0f 4e 04 4e 08 02 07 04 01 05 0d 00 05 62 00 09 61 21 1d 0e 1a 56 0c 08 04 0f 63 32 00 00 00 32 00 50 53 20 33 33 30 30 2e 2e 32 63 63 63 32 32 32 00 00 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PS 3300..2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 05 01 02 0d 47 00 4d 08 06 0f 05 00 02 0f 00 40 4a 04 04 07 06 01 04 0f 4e 00 4e 08 02 07 04 01 05 0d 00 00 62 00 09 61 21 1d 0e 1a 56 0c 08 04 0f 63 32 00 00 00 32 00 50 53 20 33 33 30 30 2e 2e 32 63 63 63 32 32 32 00 00 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Quadra...1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 07 03 05 0f 00 05 3c 13 06 15 09 04 04 0f 00 04 4a 08 00 13 1f 00 06 0f 00 05 5f 00 06 13 0c 07 06 0f 00 05 61 00 03 6c 17 00 0e 00 42 18 07 04 00 63 32 63 32 00 32 00 51 75 61 64 72 61 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Quadra...2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 09 04 04 0f 00 04 4c 0a 00 0f 07 02 05 0e 00 06 50 00 06 13 0c 07 06 0f 00 04 5f 00 03 13 1f 00 06 0f 00 05 61 08 06 74 17 00 0b 00 22 18 07 04 00 63 32 63 32 00 32 00 51 75 61 64 72 61 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ResoSynth1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 1f 00 02 0f 00 03 51 08 1e 17 1f 01 02 0f 0a 04 43 04 03 19 1f 00 03 0f 08 06 55 08 16 12 1f 00 01 0f 14 05 5b 04 1b 2a 00 00 00 00 02 0c 07 04 00 63 63 00 32 00 32 00 52 65 73 6f 53 79 6e 74 68 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ResoSynth2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 1f 00 02 0f 00 03 50 00 1e 17 1f 01 02 0f 0a 04 41 04 03 19 1f 00 03 0f 08 04 63 00 16 12 1f 00 01 0f 14 05 63 04 1b 3c 00 00 00 00 02 18 07 04 00 63 63 00 32 00 32 00 52 65 73 6f 53 79 6e 74 68 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RMI......1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 09 03 07 0d 00 04 4e 08 0b 18 07 03 08 0d 00 05 59 04 0b 19 07 03 08 0c 00 06 62 04 13 1b 08 04 08 0e 00 06 63 04 0b 24 20 00 00 00 02 18 04 04 00 28 32 00 00 00 32 00 52 4d 49 2e 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RMI......2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 09 03 07 0d 00 03 51 08 0b 18 07 03 08 0d 00 04 52 08 13 19 07 03 08 0c 00 06 62 04 13 1b 08 04 08 0e 00 06 62 00 0b 24 20 00 00 00 02 18 04 04 00 28 32 00 00 00 32 00 52 4d 49 2e 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SH 7.....1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 08 00 04 07 00 0c 57 00 09 17 1f 01 04 0f 0a 0b 3e 10 01 19 1f 00 04 0f 08 05 61 00 15 12 1f 00 04 0f 14 05 63 04 1d 2c 02 00 0a 00 02 0c 07 0d 05 63 63 63 32 00 32 00 53 48 20 37 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SH 7.....1 ** Source: from a library of patches collected by Duane Bowker, author unknown 10 08 00 04 07 00 08 57 00 09 17 1f 01 04 0f 0a 08 3e 10 01 19 1f 00 04 0f 08 00 61 00 15 12 1f 00 04 0f 14 00 63 04 1d 2c 02 00 0a 00 02 0c 07 0d 05 63 63 63 32 00 32 00 53 48 20 37 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SH 7.....2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 08 00 05 07 00 05 57 00 11 17 1f 01 05 0f 0a 04 5c 00 11 19 1f 00 05 0f 08 04 61 04 15 12 1f 00 05 0f 14 07 63 04 15 2c 02 00 0a 00 02 0c 07 0d 01 63 63 63 32 00 32 00 53 48 20 37 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SH 7.....2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 10 08 00 05 07 00 02 57 00 11 17 1f 01 05 0f 0a 01 5c 00 11 19 1f 00 05 0f 08 00 61 04 15 12 1f 00 05 0f 14 00 63 04 15 2c 02 00 0a 00 02 0c 07 0d 01 63 63 63 32 00 32 00 53 48 20 37 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sigma....1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 10 05 08 00 04 4e 10 03 1f 05 00 05 0b 2d 06 5a 04 13 1f 08 00 03 0c 43 04 55 0d 1b 1c 06 04 04 07 00 07 63 04 14 7c 21 00 38 00 22 0c 06 04 00 63 32 00 32 00 32 00 53 69 67 6d 61 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sigma....2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 08 03 05 0f 00 03 57 04 06 16 0a 0a 05 0d 00 07 4b 3f 03 16 09 01 06 0f 00 06 63 04 00 16 15 01 06 0d 00 06 59 13 05 34 25 49 0f 00 32 00 0c 04 00 63 00 00 00 00 32 00 53 69 67 6d 61 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sigma....3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 10 05 08 00 03 5a 08 03 1f 03 00 05 06 2d 06 41 13 13 1f 08 00 03 0c 43 04 55 08 1b 1c 06 04 04 07 00 05 63 08 13 6c 21 00 38 00 22 0c 06 04 00 63 32 00 32 00 32 00 53 69 67 6d 61 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Soft Synth ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 08 00 06 09 00 3a 27 12 06 18 08 00 06 0b 63 02 54 13 00 1b 10 00 06 0b 00 01 63 09 06 1f 0a 00 06 0a 23 01 63 0a 00 05 1f 06 09 00 42 05 02 04 00 63 63 00 00 00 32 00 53 6f 66 74 20 53 79 6e 74 68 63 63 63 32 32 32 00 54 00 30 00 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Softsynth* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 00 03 0d 00 03 54 09 00 1f 0f 06 04 0c 18 00 5c 2d 0d 0f 1f 06 05 0f 00 00 63 09 00 0f 1f 06 05 0f 00 00 63 0a 05 3d 23 00 00 00 62 05 02 04 00 28 32 00 00 00 32 00 53 6f 66 74 73 79 6e 74 68 2a 63 63 63 32 32 32 00 02 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SoloSynth ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 1f 05 03 0e 28 03 50 08 0a 1f 1d 07 04 0c 11 02 50 00 0a 1f 1d 0c 04 0c 28 02 41 00 0b 1a 01 01 05 0d 00 01 62 00 0c 01 1e 00 00 00 52 24 02 04 00 28 32 00 00 00 32 00 53 6f 6c 6f 53 79 6e 74 68 20 63 52 63 32 32 32 00 00 00 10 00 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpiralSynt ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 09 06 01 0e 00 03 60 00 13 16 0c 06 05 0e 03 05 51 04 0c 16 0f 06 07 0e 00 05 62 04 0c 16 09 06 07 0f 00 05 63 04 0a 64 23 00 00 00 02 18 04 04 00 63 01 63 00 00 32 00 53 70 69 72 61 6c 53 79 6e 74 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpiralSynt ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 09 06 01 0e 00 01 60 00 13 16 0c 06 05 0e 03 01 51 04 0c 16 0f 06 07 0e 00 00 62 04 0c 16 09 06 07 0f 00 00 63 04 0a 64 23 00 00 00 02 18 04 04 00 63 01 63 00 00 32 00 53 70 69 72 61 6c 53 79 6e 74 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sweeper ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 00 0a 0b 00 3b 5b 00 0d 1f 16 00 0a 0d 00 1d 46 00 03 1f 16 00 0a 0d 00 1d 63 00 03 1f 16 00 0a 0d 00 3d 63 00 03 35 25 00 00 00 22 18 02 04 00 63 32 00 00 00 32 00 53 77 65 65 70 65 72 20 20 20 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sweeper ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 00 0a 0b 00 3a 5b 00 0d 1f 16 00 0a 0d 00 19 46 00 03 1f 16 00 0a 0d 00 18 63 00 03 1f 16 00 0a 0d 00 38 63 00 03 35 25 00 00 00 22 18 02 04 00 63 32 00 00 00 32 00 53 77 65 65 70 65 72 20 20 20 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SweepSynt* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 11 0d 01 09 00 04 4e 0a 04 05 03 00 01 0d 00 02 50 04 02 11 11 00 07 09 00 01 5b 08 06 0e 07 00 04 0d 00 02 63 04 08 34 1f 00 00 00 32 18 02 04 00 28 63 00 00 00 32 00 53 77 65 65 70 53 79 6e 74 2a 63 63 63 32 32 32 00 03 00 40 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SweetSynth ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 14 09 00 03 0f 29 00 12 08 02 0d 0d 00 03 0f 42 01 2d 13 01 1b 0d 00 07 0f 00 02 33 04 04 11 0d 00 07 0f 00 02 63 13 06 00 20 00 00 00 52 05 02 04 00 63 32 00 00 00 32 00 53 77 65 65 74 53 79 6e 74 68 63 63 63 32 32 32 00 50 00 30 00 58 00 10 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.AftrTch ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 32 1f 1f 00 03 0f 00 28 46 0a 00 1f 1f 00 03 0f 00 28 44 09 06 0b 1f 00 05 0f 1e 08 56 0a 00 0b 1f 00 05 0f 1e 08 56 0a 06 3c 23 00 2e 00 42 05 02 04 00 63 32 00 00 00 32 00 53 79 2e 41 66 74 72 54 63 68 63 63 63 32 32 32 00 30 00 62 00 10 00 30 00 00 00 00 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.AftrTch ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 03 0f 00 28 46 0a 00 1f 1f 00 03 0f 00 28 44 09 06 0b 1f 00 05 0f 1e 08 56 0a 00 0b 1f 00 05 0f 1e 08 56 0a 06 3c 23 00 2e 00 42 05 02 04 00 63 32 00 00 00 32 00 53 79 2e 41 66 74 72 54 63 68 63 63 63 32 32 32 00 30 00 62 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Decay 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 29 1f 08 00 04 09 14 02 49 12 06 18 08 00 04 0b 14 02 51 13 00 1b 10 00 04 0b 00 01 60 09 06 1f 0a 00 04 0a 00 01 60 0a 00 3d 1f 06 09 00 52 05 02 04 00 63 32 00 00 00 32 00 53 79 2e 44 65 63 61 79 20 31 63 63 63 32 32 32 00 54 00 30 00 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Decay 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 30 1f 0c 08 0f 0c 22 02 3e 35 11 1f 11 01 04 0e 1a 04 4e 05 16 1f 0b 07 0a 0c 00 43 57 04 0e 1f 07 04 08 0e 00 43 60 05 11 3b 1c 08 02 1b 56 11 02 04 00 63 32 00 00 00 32 00 53 79 2e 44 65 63 61 79 20 32 63 63 63 32 32 32 00 7f 00 01 00 18 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Decay 2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 08 0f 0c 22 02 3e 35 11 1f 11 01 04 0e 1a 04 4e 05 16 1f 0b 07 0d 0c 00 43 57 04 0e 1f 07 04 08 0e 00 43 60 05 11 3b 1c 08 02 1b 56 11 02 04 00 63 32 00 00 00 32 00 53 79 2e 44 65 63 61 79 20 32 63 63 63 32 32 32 00 7f 00 01 00 18 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synballs ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank D, voice 31 1f 1f 00 01 0e 00 41 62 36 03 19 16 00 01 0b 00 41 5f 05 0b 15 1c 06 01 0b 00 41 58 39 13 1f 1f 05 02 08 00 41 63 18 13 29 00 02 00 03 52 12 05 04 00 63 32 00 00 00 32 00 53 79 6e 62 61 6c 6c 73 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 20 ** Name: Synth....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 16 07 05 0d 00 04 4b 2d 0b 17 0f 00 06 0d 00 05 57 04 03 16 13 07 06 0d 01 05 63 04 13 0d 09 06 06 0d 00 06 63 04 0b 74 26 21 09 00 42 18 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 16 16 07 05 0d 00 01 4b 2d 0b 17 0f 00 06 0d 00 01 57 04 03 16 13 07 06 0d 01 00 63 04 13 0d 09 06 06 0d 00 00 63 04 0b 74 26 21 09 00 42 18 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 09 03 0d 00 42 50 22 02 1f 0f 00 06 0d 00 44 4e 08 08 0c 0a 07 04 0d 01 45 63 04 12 1f 0a 06 05 0d 00 46 63 08 08 5c 26 23 06 1b 36 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 09 03 0d 00 43 50 22 02 1f 0f 00 06 0d 00 41 4e 08 08 0c 0a 07 04 0d 01 40 63 04 12 1f 0a 06 05 0d 00 40 63 08 08 5c 26 23 06 1b 36 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 0a 07 09 0e 00 43 59 08 06 0d 0a 07 09 0e 00 44 52 04 00 1f 1d 07 09 0e 00 46 63 04 03 1f 1d 07 09 0e 00 45 62 04 03 24 24 32 13 02 36 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 0a 07 09 0e 00 41 59 08 06 0d 0a 07 09 0e 00 41 52 04 00 1f 1d 07 09 0e 00 40 63 04 03 1f 1d 07 09 0e 00 40 62 04 03 24 24 32 13 02 36 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 0f 01 0f 00 05 50 3f 03 1f 06 07 0f 0e 00 05 4d 04 00 18 10 0f 0e 0c 00 06 53 04 03 1f 06 07 0f 0e 00 06 5d 04 03 22 28 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 0f 01 0f 00 02 50 3f 03 1f 06 07 0f 0e 00 02 4d 04 00 18 10 0f 0e 0c 00 01 53 04 03 1f 06 07 0f 0e 00 00 5d 04 03 22 28 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 14 08 0f 00 00 43 63 10 13 1b 03 06 08 0e 00 44 4c 04 02 1f 15 07 0f 00 00 45 5f 0d 06 0e 03 06 08 09 00 45 63 04 0e 34 26 21 20 2a 16 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 14 08 0f 00 00 40 63 10 13 1b 03 06 08 0e 00 41 4c 04 02 1f 15 07 0f 00 00 40 5f 0d 06 0e 03 06 08 09 00 40 63 04 0e 34 26 21 20 2a 16 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 0c 07 0c 0a 00 04 55 04 03 1e 0c 07 07 0a 00 05 55 04 03 1f 16 00 0f 0d 00 05 63 04 03 1f 16 07 07 0d 00 06 62 04 0b 34 26 00 00 00 22 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 10 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 10 0c 07 0c 0a 00 01 55 04 03 1e 0c 07 07 0a 00 01 55 04 03 1f 16 00 0f 0d 00 00 63 04 03 1f 16 07 07 0d 00 00 62 04 0b 34 26 00 00 00 22 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 10 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 09 06 04 00 04 5e 00 05 1f 0c 09 06 0e 00 04 5b 00 02 1f 0c 09 06 0e 00 04 63 00 04 1f 0c 09 06 0e 00 05 63 04 02 2c 25 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synth....7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0d 09 06 04 00 01 5e 00 05 1f 0c 09 06 0e 00 01 5b 00 02 1f 0c 09 06 0e 00 00 63 00 04 1f 0c 09 06 0e 00 00 63 04 02 2c 25 00 00 00 32 0c 02 04 00 28 02 00 00 00 32 00 53 79 6e 74 68 2e 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Synthish * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 08 00 01 0c 00 01 52 08 06 03 0d 00 01 0f 00 01 49 05 18 10 08 00 04 0d 00 01 63 08 0e 0b 0a 00 04 0e 00 00 63 05 08 34 20 05 19 0f 32 08 02 04 00 28 32 00 00 00 32 00 53 79 6e 74 68 69 73 68 20 2a 63 63 63 32 32 32 00 09 00 0d 00 09 00 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthRise1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 03 0f 00 03 51 00 06 1f 1f 00 03 0f 00 05 5e 00 08 0f 1f 00 04 0f 00 05 5d 08 06 0f 1f 00 05 0f 00 05 5c 08 00 7c 23 00 00 00 02 18 05 04 00 63 63 63 00 00 32 00 53 79 6e 74 68 52 69 73 65 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthRise1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 03 0f 00 01 51 00 06 1f 1f 00 03 0f 00 01 5e 00 08 0f 1f 00 04 0f 00 00 5d 08 06 0f 1f 00 05 0f 00 00 5c 08 00 7c 23 00 00 00 02 18 05 04 00 63 63 63 00 00 32 00 53 79 6e 74 68 52 69 73 65 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthRise2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 1f 00 04 0d 00 05 5f 00 06 1f 1f 00 04 0f 00 05 61 00 08 0f 1f 00 05 0f 00 06 5f 08 06 0f 1f 00 05 0f 00 05 58 08 00 74 23 00 00 00 02 18 05 04 00 63 63 63 00 00 32 00 53 79 6e 74 68 52 69 73 65 32 63 63 63 32 32 32 00 20 00 40 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthRise2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 1f 00 04 0d 00 01 5f 00 06 1f 1f 00 04 0f 00 02 61 00 08 0f 1f 00 05 0f 00 00 5f 08 06 0f 1f 00 05 0f 00 00 58 08 00 74 23 00 00 00 02 18 05 04 00 63 63 63 00 00 32 00 53 79 6e 74 68 52 69 73 65 32 63 63 63 32 32 32 00 20 00 40 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthyPad1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 11 11 06 07 0f 1d 02 62 03 06 0b 08 07 07 0f 16 41 62 05 04 11 07 08 07 0f 1d 02 62 05 00 0e 08 04 05 0f 14 43 63 03 02 7f 22 00 0f 00 36 05 02 04 03 63 32 00 00 00 32 00 53 79 6e 74 68 79 50 61 64 31 63 63 63 32 32 32 00 16 00 51 00 11 00 56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System 15 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 09 04 09 0e 00 05 54 08 06 16 09 04 09 0e 00 04 5a 00 06 16 09 04 09 0e 00 06 5c 04 03 16 09 04 09 0e 00 05 63 00 03 74 22 00 06 00 32 0c 02 04 00 28 02 00 00 00 32 00 53 79 73 74 65 6d 20 31 35 20 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System 15 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 16 09 04 09 0e 00 01 54 08 06 16 09 04 09 0e 00 01 5a 00 06 16 09 04 09 0e 00 00 5c 04 03 16 09 04 09 0e 00 00 63 00 03 74 22 00 06 00 32 0c 02 04 00 28 02 00 00 00 32 00 53 79 73 74 65 6d 20 31 35 20 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System35.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 07 01 00 04 0c 00 04 5f 00 09 0b 07 00 04 0f 00 04 50 00 04 0e 03 01 04 0f 00 05 63 04 0b 13 02 00 04 0f 00 05 63 04 06 54 17 00 0b 00 32 18 04 04 00 63 01 63 00 00 32 00 53 79 73 74 65 6d 33 35 2e 31 63 63 63 32 32 32 00 10 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System35.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 07 01 00 04 0c 00 01 5f 00 09 0b 07 00 04 0f 00 01 50 00 04 0e 03 01 04 0f 00 00 63 04 0b 13 02 00 04 0f 00 00 63 04 06 54 17 00 0b 00 32 18 04 04 00 63 01 63 00 00 32 00 53 79 73 74 65 6d 33 35 2e 31 63 63 63 32 32 32 00 10 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System35.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 04 03 08 0d 00 04 50 04 01 15 04 03 08 0d 00 04 4c 08 03 12 04 03 08 0d 00 04 62 08 03 0a 04 03 08 0d 00 05 5d 08 06 7c 17 00 0b 00 32 0c 04 04 00 63 01 63 00 00 32 00 53 79 73 74 65 6d 33 35 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System35.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 04 03 08 0d 00 02 50 04 01 15 04 03 08 0d 00 01 4c 08 03 12 04 03 08 0d 00 00 62 08 03 0a 04 03 08 0d 00 00 5d 08 06 7c 17 00 0b 00 32 0c 04 04 00 63 01 63 00 00 32 00 53 79 73 74 65 6d 33 35 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System35.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 01 00 06 0c 00 02 51 00 09 15 04 00 07 0f 00 04 4e 00 06 13 0c 02 06 0f 00 06 63 00 0b 13 03 01 06 0f 00 05 63 00 16 74 17 00 0b 00 32 18 04 04 00 63 01 63 00 00 32 00 53 79 73 74 65 6d 33 35 2e 33 63 63 63 32 32 32 00 00 00 40 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System35.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 01 00 06 0c 00 01 51 00 09 15 04 00 07 0f 00 02 4e 00 06 13 0c 02 06 0f 00 00 63 00 0b 13 03 01 06 0f 00 00 63 00 16 74 17 00 0b 00 32 18 04 04 00 63 01 63 00 00 32 00 53 79 73 74 65 6d 33 35 2e 33 63 63 63 32 32 32 00 00 00 40 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System55.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 09 00 05 0f 00 03 5b 00 00 15 03 00 06 05 00 05 60 00 0e 13 1f 00 06 0f 00 06 63 04 03 13 0c 00 06 0e 00 05 63 04 03 6c 17 00 0b 00 22 18 05 04 00 63 63 63 00 00 32 00 53 79 73 74 65 6d 35 35 2e 31 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System55.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 09 09 00 05 0f 00 01 5b 00 00 15 03 00 06 05 00 01 60 00 0e 13 1f 00 06 0f 00 00 63 04 03 13 0c 00 06 0e 00 00 63 04 03 6c 17 00 0b 00 22 18 05 04 00 63 63 63 00 00 32 00 53 79 73 74 65 6d 35 35 2e 31 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System55.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 09 00 05 0f 00 02 5c 00 06 15 03 00 06 05 00 05 58 08 0e 13 1f 00 06 0f 00 04 63 00 02 13 0c 00 06 0e 00 05 63 00 02 74 17 00 0b 00 22 18 05 04 00 63 63 63 00 00 32 00 53 79 73 74 65 6d 35 35 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: System55.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 09 09 00 05 0f 00 02 5c 00 06 15 03 00 06 05 00 01 58 08 0e 13 1f 00 06 0f 00 00 63 00 02 13 0c 00 06 0e 00 00 63 00 02 74 17 00 0b 00 22 18 05 04 00 63 63 63 00 00 32 00 53 79 73 74 65 6d 35 35 2e 32 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tamboura * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0a 07 07 01 00 00 00 49 0a 0b 1f 1f 02 03 0f 00 01 56 00 0b 07 08 04 03 00 00 01 45 24 0b 1e 0d 05 04 0d 00 01 63 04 0b 3b 23 00 00 00 32 18 02 04 00 28 63 00 00 00 32 00 54 61 6d 62 6f 75 72 61 20 2a 63 63 63 32 32 32 00 20 00 00 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tamboura.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 1f 00 03 0f 00 01 50 00 03 1f 1f 00 02 0f 00 03 5f 04 03 19 18 00 02 0f 00 05 45 05 03 1f 1f 00 03 0f 00 06 5a 04 03 7d 23 00 00 00 32 18 02 04 00 63 63 00 00 00 32 00 54 61 6d 62 6f 75 72 61 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tamboura.1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 1f 00 03 0f 00 00 50 00 03 1f 1f 00 02 0f 00 00 5f 04 03 19 18 00 02 0f 00 00 45 05 03 1f 1f 00 03 0f 00 00 5a 04 03 7d 23 00 00 00 32 18 02 04 00 63 63 00 00 00 32 00 54 61 6d 62 6f 75 72 61 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tamboura.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 11 07 03 0f 00 02 54 00 03 1f 1f 00 04 0f 00 03 63 00 03 19 18 00 03 0f 00 05 45 04 03 1f 1f 00 04 0f 00 05 5b 04 03 45 23 00 00 00 32 18 02 04 00 63 63 00 00 00 32 00 54 61 6d 62 6f 75 72 61 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tamboura.2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 11 07 03 0f 00 00 54 00 03 1f 1f 00 04 0f 00 00 63 00 03 19 18 00 03 0f 00 00 45 04 03 1f 1f 00 04 0f 00 00 5b 04 03 45 23 00 00 00 32 18 02 04 00 63 63 00 00 00 32 00 54 61 6d 62 6f 75 72 61 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tamboura.3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 1f 00 03 0f 00 02 47 00 03 1f 1f 00 03 0f 00 02 43 00 03 19 18 00 03 0f 00 03 5b 04 03 1f 1f 00 03 0f 00 06 5b 04 03 6c 23 00 00 00 32 18 02 04 00 63 63 00 00 00 32 00 54 61 6d 62 6f 75 72 61 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tamboura.3 ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 1f 00 03 0f 00 00 47 00 03 1f 1f 00 03 0f 00 00 43 00 03 19 18 00 03 0f 00 00 5b 04 03 1f 1f 00 03 0f 00 00 5b 04 03 6c 23 00 00 00 32 18 02 04 00 63 63 00 00 00 32 00 54 61 6d 62 6f 75 72 61 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Time X * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 06 00 00 01 00 08 00 5a 00 06 1f 00 00 01 00 00 00 48 00 05 1f 00 00 01 00 00 02 58 00 01 12 06 00 03 00 00 00 63 00 0b 02 23 00 00 00 32 24 02 04 00 28 63 00 00 00 32 00 54 69 6d 65 20 58 20 20 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: TrdrpSynth ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 17 04 00 06 09 00 3a 1e 12 06 10 04 00 06 0b 63 02 54 1c 00 13 0c 00 06 0b 00 01 63 09 06 17 06 00 06 0a 23 01 63 13 00 05 1f 06 09 00 42 05 02 04 00 63 63 00 00 00 32 00 54 72 64 72 70 53 79 6e 74 68 63 63 63 32 32 32 00 54 00 30 00 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Voyage * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 17 09 00 01 00 19 00 5b 00 08 17 05 00 03 00 00 00 5c 00 0e 15 07 00 04 00 07 02 63 04 08 15 07 00 04 00 06 01 63 03 0b 14 20 00 1c 00 42 11 02 04 00 28 63 00 00 00 32 00 56 6f 79 61 67 65 20 20 20 2a 63 63 63 32 32 32 00 44 00 04 00 08 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Walt/Wendy ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0a 09 07 04 0e 00 04 47 16 03 1e 0f 00 05 0d 00 05 4d 08 09 0a 09 07 04 0e 00 04 62 04 0b 09 10 03 04 0f 00 05 63 08 0e 74 26 00 02 00 63 0c 02 04 00 63 63 00 00 00 32 00 57 61 6c 74 2f 57 65 6e 64 79 63 63 63 32 32 32 00 00 00 30 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Walt/Wendy ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0a 09 07 04 0e 00 01 47 16 03 1e 0f 00 05 0d 00 02 4d 08 09 0a 09 07 04 0e 00 00 62 04 0b 09 10 03 04 0f 00 00 63 08 0e 74 26 00 02 00 63 0c 02 04 00 63 63 00 00 00 32 00 57 61 6c 74 2f 57 65 6e 64 79 63 63 63 32 32 32 00 00 00 30 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wapper...1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 1c 15 01 0e 00 04 52 0a 13 10 00 03 08 0d 00 06 58 04 0b 10 00 03 09 0d 00 06 5f 0a 08 10 00 03 08 0d 00 05 63 08 0b 34 25 29 26 63 12 0c 04 04 00 63 01 63 00 00 32 00 57 61 70 70 65 72 2e 2e 2e 31 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wapper...1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 1c 15 01 0e 00 01 52 0a 13 10 00 03 08 0d 00 02 58 04 0b 10 00 03 09 0d 00 00 5f 0a 08 10 00 03 08 0d 00 00 63 08 0b 34 25 29 26 63 12 0c 04 04 00 63 01 63 00 00 32 00 57 61 70 70 65 72 2e 2e 2e 31 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wapper...2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 1c 0a 01 0e 00 05 49 2a 0b 10 00 03 08 0d 00 05 3e 10 0b 10 00 03 09 0d 00 05 5f 04 08 10 00 03 08 0d 00 05 63 04 0b 2c 25 2a 26 63 12 18 04 04 00 63 01 63 00 00 32 00 57 61 70 70 65 72 2e 2e 2e 32 63 63 63 32 32 32 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wapper...2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 1c 0a 01 0e 00 01 49 2a 0b 10 00 03 08 0d 00 01 3e 10 0b 10 00 03 09 0d 00 00 5f 04 08 10 00 03 08 0d 00 00 63 04 0b 2c 25 2a 26 63 12 18 04 04 00 63 01 63 00 00 32 00 57 61 70 70 65 72 2e 2e 2e 32 63 63 63 32 32 32 00 30 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WarmSquare ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 21 1f 06 00 03 0f 00 01 4f 03 06 1f 0e 00 03 0f 00 01 50 05 00 12 0e 00 07 0f 00 02 63 03 00 16 0e 00 07 0f 00 02 63 05 06 34 20 00 00 00 52 05 07 0c 00 63 32 00 00 00 32 00 57 61 72 6d 53 71 75 61 72 65 63 63 63 32 32 32 00 26 00 51 00 06 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wendy : ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 0a 06 08 0c 00 03 53 04 03 10 07 06 0a 00 00 05 51 04 03 12 07 06 09 0c 00 05 63 04 13 12 09 04 07 0e 00 06 63 04 03 24 23 00 05 00 42 0c 02 04 00 28 02 00 00 00 32 00 20 57 65 6e 64 79 20 20 3a 20 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wendy : ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 0a 06 08 0c 00 01 53 04 03 10 07 06 0a 00 00 01 51 04 03 12 07 06 09 0c 00 00 63 04 13 12 09 04 07 0e 00 00 63 04 03 24 23 00 05 00 42 0c 02 04 00 28 02 00 00 00 32 00 20 57 65 6e 64 79 20 20 3a 20 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wendy C ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 09 04 04 0f 00 04 31 1f 06 08 00 00 04 0f 00 04 44 08 0b 0e 09 04 04 0f 00 06 62 08 02 08 00 00 05 0f 00 06 62 08 0b 74 2b 00 0b 00 32 0c 02 04 00 28 02 00 00 00 32 00 57 65 6e 64 79 20 43 20 20 20 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wendy C ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 09 04 04 0f 00 01 31 1f 06 08 00 00 04 0f 00 01 44 08 0b 0e 09 04 04 0f 00 00 62 08 02 08 00 00 05 0f 00 00 62 08 0b 74 2b 00 0b 00 32 0c 02 04 00 28 02 00 00 00 32 00 57 65 6e 64 79 20 43 20 20 20 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Whos Upset ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 43 58 04 03 1f 1f 00 0f 0f 00 43 63 04 03 1f 1f 00 0f 0f 00 45 63 04 03 1f 1f 00 0f 0f 00 47 63 04 03 5d 2e 3a 45 63 77 18 04 04 00 63 01 63 00 00 32 00 57 68 6f 73 20 55 70 73 65 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Whos Upset ** Controls: MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 0f 0f 00 40 58 04 03 1f 1f 00 0f 0f 00 40 63 04 03 1f 1f 00 0f 0f 00 40 63 04 03 1f 1f 00 0f 0f 00 40 63 04 03 5d 2e 3a 45 63 77 18 04 04 00 63 01 63 00 00 32 00 57 68 6f 73 20 55 70 73 65 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: X-Pander * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 08 00 06 0d 00 03 52 03 03 0e 08 00 01 0f 00 00 43 04 03 15 0e 00 06 0f 00 00 63 03 0b 0b 1f 00 06 0f 00 00 63 04 0b 3c 21 00 00 00 52 05 02 04 00 28 32 00 00 00 32 00 58 2d 50 61 6e 64 65 72 20 2a 63 63 63 32 32 32 00 06 00 08 00 06 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xuereb * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 08 00 03 0f 00 00 46 3f 1d 1f 00 00 04 0f 00 00 48 08 00 11 07 12 02 00 00 07 4a 3f 1e 0f 04 09 06 0f 00 02 63 08 0e 33 20 00 14 00 32 00 02 04 00 28 63 00 00 00 32 00 58 75 65 72 65 62 20 20 20 2a 63 63 63 32 32 32 00 0f 00 00 00 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 21 * CATEGORY VOICES * SUBCATEGORY Breath ** Name: BaadBreath ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 1 1f 00 00 04 0f 00 40 63 3c 00 0b 08 07 07 0f 05 00 63 04 06 0f 05 03 07 0f 08 00 3b 05 06 0e 08 04 05 0f 00 03 63 03 00 7e 1d 06 0f 00 4a 11 00 04 00 63 32 00 00 00 32 00 42 61 61 64 42 72 65 61 74 68 63 63 63 32 32 32 06 30 00 38 00 01 00 46 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BaadBreath ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 04 0f 00 41 63 3c 00 0c 09 04 07 0f 3e 03 63 06 06 10 07 03 06 0f 0c 03 4d 05 01 0f 08 04 05 0f 33 04 63 05 00 7e 1d 06 0f 29 4e 11 00 04 00 63 32 00 00 00 32 00 42 61 61 64 42 72 65 61 74 68 63 63 63 32 32 32 0e 30 00 30 00 42 01 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BaadBreath ** Controls: KV, MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 00 00 04 0f 00 40 63 3c 00 0c 09 04 07 0f 3e 00 63 06 06 10 07 03 06 0f 0c 01 4d 05 01 0f 08 04 05 0f 33 01 63 05 00 7e 1d 06 0f 29 4e 11 00 04 00 63 32 00 00 00 32 00 42 61 61 64 42 72 65 61 74 68 63 63 63 32 32 32 0e 30 00 30 00 42 01 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BadBreath2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 04 0f 00 40 63 3c 00 0a 08 07 07 0f 15 00 63 04 06 0a 05 03 07 0f 13 00 32 05 06 0a 08 04 05 0f 19 03 63 03 00 7e 1d 06 0f 00 4a 11 00 04 02 63 32 00 00 00 32 00 42 61 64 42 72 65 61 74 68 32 63 63 63 32 32 32 06 10 00 38 00 71 00 46 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BreathOrgn ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 32 0d 0b 00 01 0c 00 02 2c 3d 03 0d 07 00 01 0c 00 02 1f 3c 03 0d 1f 00 05 0f 00 02 63 05 06 0d 1f 00 05 0f 00 02 63 04 00 3c 63 00 2f 00 53 11 04 04 00 63 32 00 00 00 32 00 42 72 65 61 74 68 4f 72 67 6e 63 63 63 32 32 32 04 30 05 50 00 11 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BreathyEns ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 00 00 03 0f 00 40 63 3c 00 0b 08 07 06 0f 05 00 63 08 06 0f 05 03 06 0f 08 00 2b 08 06 10 04 04 09 0f 00 03 63 08 00 7e 1d 06 0f 00 4a 0c 01 04 00 63 32 00 00 00 32 00 42 72 65 61 74 68 79 45 6e 73 63 63 63 32 32 32 06 30 00 10 00 71 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Choir ** Name: ChoiChoir* ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 19 00 01 0f 08 00 46 04 06 1f 19 00 01 0f 4a 02 4d 04 06 0b 1f 00 06 0f 00 00 63 08 00 09 19 00 07 0f 17 01 4d 20 00 04 1e 1f 3f 00 42 0c 02 04 00 28 32 00 00 00 32 00 43 68 6f 69 43 68 6f 69 72 2a 63 63 63 32 32 32 00 00 00 20 00 00 0c 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 0b 0c 0c 0d 00 00 37 0a 03 0c 03 09 09 0f 00 07 32 08 04 0c 06 00 09 0f 00 07 45 08 01 08 08 09 07 0d 00 07 60 04 00 4a 20 21 35 00 22 18 07 04 00 63 32 63 32 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 31 63 63 63 32 32 32 20 00 10 10 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....1 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 0b 0c 0c 0d 00 00 37 0a 03 0c 03 09 09 0f 00 00 32 08 04 0c 06 00 09 0f 00 00 45 08 01 08 08 09 07 0d 00 00 60 04 00 4a 20 21 35 00 22 18 07 04 00 63 32 63 32 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 31 63 63 63 32 32 32 20 00 10 10 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 08 0c 0c 0d 00 03 35 0a 03 0c 1f 09 09 0f 00 06 35 08 03 0c 06 00 09 0f 00 05 63 08 03 0c 08 09 07 0d 00 05 60 04 00 4c 20 21 35 00 22 18 07 04 00 63 32 63 32 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 08 0c 0c 0d 00 00 35 0a 03 0c 1f 09 09 0f 00 00 35 08 03 0c 06 00 09 0f 00 00 63 08 03 0c 08 09 07 0d 00 00 60 04 00 4c 20 21 35 00 22 18 07 04 00 63 32 63 32 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 15 1f 07 0f 00 02 3a 0d 02 10 16 09 07 0f 00 06 44 08 04 0b 16 09 07 0f 00 06 5f 08 01 0f 0f 04 07 0f 00 05 5f 04 02 44 1f 23 21 00 22 0c 07 04 00 63 32 63 32 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 15 1f 07 0f 00 00 3a 0d 02 10 16 09 07 0f 00 01 44 08 04 0b 16 09 07 0f 00 00 5f 08 01 0f 0f 04 07 0f 00 00 5f 04 02 44 1f 23 21 00 22 0c 07 04 00 63 32 63 32 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 07 00 04 04 0a 61 00 39 19 03 09 1f 03 07 0f 63 04 3a 04 00 07 00 06 06 09 58 05 34 13 03 09 1f 01 06 0f 29 45 58 04 06 21 20 2d 00 00 06 24 02 04 00 63 32 00 00 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 30 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....4 ** Source: from a library of patches collected by Duane Bowker, author unknown 07 00 04 04 0a 61 00 39 19 03 09 1f 03 07 0f 63 00 3a 04 00 07 00 06 06 09 58 00 34 13 03 09 1f 01 06 0f 29 40 58 04 06 21 20 2d 00 00 06 24 02 04 00 63 32 00 00 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 30 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 12 04 04 0a 3a 03 36 19 06 0b 1f 03 0a 0f 56 05 44 04 00 15 0e 06 0a 09 63 06 3b 16 00 0e 1f 01 09 0f 00 06 61 04 06 29 21 13 09 00 62 18 02 04 00 63 32 00 00 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....5 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 12 04 04 0a 3a 00 36 19 06 0b 1f 03 0a 0f 56 00 44 04 00 15 0e 06 0a 09 63 00 3b 16 00 0e 1f 01 09 0f 00 00 61 04 06 29 21 13 09 00 62 18 02 04 00 63 32 00 00 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 15 04 04 0a 3a 03 33 10 06 0c 1f 03 0a 0f 56 04 39 04 00 0d 0e 06 0a 09 63 06 48 08 00 0c 1f 01 09 0f 00 05 57 04 06 51 21 16 08 00 62 18 02 04 00 63 32 00 00 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 36 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choir....6 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 15 04 04 0a 3a 00 33 10 06 0c 1f 03 0a 0f 56 00 39 04 00 0d 0e 06 0a 09 63 00 48 08 00 0c 1f 01 09 0f 00 00 57 04 06 51 21 16 08 00 62 18 02 04 00 63 32 00 00 00 32 00 43 68 6f 69 72 2e 2e 2e 2e 36 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choral ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 0b 0d 00 02 0f 0a 40 32 04 00 0a 0d 00 02 0f 0a 40 32 00 06 0c 0b 00 04 0f 1e 42 63 08 00 0a 0d 00 04 0f 14 42 63 04 06 3c 1e 0a 11 04 4a 18 02 04 00 28 32 00 00 00 32 00 43 68 6f 72 61 6c 20 20 20 20 63 56 63 29 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus ** Controls: MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 0d 0e 00 02 0f 28 00 32 00 03 0d 0e 00 02 0f 00 00 4e 00 0b 0d 0e 00 04 0f 00 00 63 04 0b 0d 0e 00 04 0f 58 00 63 08 0b 06 1d 00 17 00 22 18 05 04 00 28 32 0d 00 00 32 00 43 68 6f 72 75 73 20 20 20 20 63 47 63 2f 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...1 ** Controls: KV, MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 16 02 02 0f 23 42 2f 04 04 06 07 04 04 07 32 46 40 04 03 0a 06 09 09 0f 45 45 39 04 02 0a 06 07 06 0f 0a 46 5d 04 00 2b 23 23 0b 02 46 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 31 63 63 63 32 32 32 0b 09 00 10 00 00 00 10 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...1 ** Controls: MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 16 02 02 0f 23 40 2f 04 04 06 07 04 04 07 32 40 40 04 03 0a 06 09 09 0f 45 40 39 04 02 0a 06 07 06 0f 0a 40 5d 04 00 2b 23 23 0b 02 46 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 31 63 63 63 32 32 32 0b 09 00 10 00 00 00 10 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...2 ** Controls: KV, MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 16 05 02 0f 23 43 3a 0d 05 07 07 07 04 07 32 45 43 04 03 09 06 08 09 0f 45 45 40 04 02 0f 06 07 09 0f 0a 45 5f 04 00 3b 23 23 0b 02 46 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...2 ** Controls: MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 16 05 02 0f 23 40 3a 0d 05 07 07 07 04 07 32 40 43 04 03 09 06 08 09 0f 45 40 40 04 02 0f 06 07 09 0f 0a 40 5f 04 00 3b 23 23 0b 02 46 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...3 ** Controls: KV, MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 09 05 05 0f 23 44 49 08 05 06 07 07 04 07 32 46 4d 08 04 0d 06 08 09 0f 45 45 57 08 02 0d 07 07 09 0f 0a 45 5f 04 00 2e 23 23 0b 02 46 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...3 ** Controls: KV, MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 09 05 05 0f 23 41 49 08 05 06 07 07 04 07 32 40 4d 08 04 0d 06 08 09 0f 45 40 57 08 02 0d 07 07 09 0f 0a 40 5f 04 00 2e 23 23 0b 02 46 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...4 ** Controls: KV, MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 07 05 0a 07 0e 2d 43 51 04 0b 0a 04 05 05 09 3c 45 5b 04 05 0d 06 08 09 0f 59 46 60 04 04 0a 05 08 07 0e 0a 46 61 04 0e 06 1b 0f 48 22 16 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 34 63 63 63 32 32 32 00 10 00 10 00 10 00 10 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...4 ** Controls: MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 07 05 0a 07 0e 2d 40 51 04 0b 0a 04 05 05 09 3c 40 5b 04 05 0d 06 08 09 0f 59 40 60 04 04 0a 05 08 07 0e 0a 40 61 04 0e 06 1b 0f 48 22 16 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 34 63 63 63 32 32 32 00 10 00 10 00 10 00 10 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...5 ** Controls: KV, MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 07 08 08 0d 2d 44 41 08 04 0c 08 04 06 0b 3c 46 63 04 04 0a 07 05 08 0f 59 46 63 00 01 09 04 08 07 0f 0a 47 61 04 05 0e 1b 0f 4f 26 16 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...5 ** Controls: MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 09 07 08 08 0d 2d 40 41 08 04 0c 08 04 06 0b 3c 40 63 04 04 0a 07 05 08 0f 59 40 63 00 01 09 04 08 07 0f 0a 40 61 04 05 0e 1b 0f 4f 26 16 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 30 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...6 ** Controls: KV, MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 0a 06 08 0d 3e 45 4b 04 03 0c 06 05 07 0d 32 45 63 08 03 0a 0a 03 07 0f 43 45 63 0a 0b 0c 07 06 08 0f 0a 46 63 04 03 0e 1b 0f 4f 3d 16 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 36 63 63 63 32 32 32 00 20 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...6 ** Controls: MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 0a 06 08 0d 3e 40 4b 04 03 0c 06 05 07 0d 32 40 63 08 03 0a 0a 03 07 0f 43 40 63 0a 0b 0c 07 06 08 0f 0a 40 63 04 03 0e 1b 0f 4f 3d 16 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 36 63 63 63 32 32 32 00 20 00 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...7 ** Controls: MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 06 07 05 07 0d 17 40 42 04 03 09 03 04 07 0d 32 40 55 0a 03 08 05 04 07 0f 2a 40 5b 08 06 08 08 05 07 0f 1e 40 5b 04 05 1e 1b 0f 4f 3d 36 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 30 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...8 ** Controls: KV, MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 06 05 06 06 0c 17 40 39 0a 03 09 05 03 07 0e 32 45 5b 04 03 09 04 06 06 0d 2a 47 5f 04 03 09 06 02 09 0d 1e 46 5d 0a 03 0e 1b 0f 4f 3d 16 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 38 63 63 63 32 32 32 00 10 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chorus...8 ** Controls: MW (LFO pitch), BC (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 06 05 06 06 0c 17 40 39 0a 03 09 05 03 07 0e 32 40 5b 04 03 09 04 06 06 0d 2a 40 5f 04 03 09 06 02 09 0d 1e 40 5d 0a 03 0e 1b 0f 4f 3d 16 18 02 04 00 63 32 00 00 01 32 00 43 68 6f 72 75 73 2e 2e 2e 38 63 63 63 32 32 32 00 10 00 30 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY FVoice ** Name: F Voice..1 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 04 05 0a 00 46 52 04 01 15 18 18 07 0c 00 06 4b 04 02 0c 08 04 09 0e 00 46 5b 04 06 0c 08 04 09 0e 00 06 5e 04 00 74 27 1b 30 08 26 18 07 04 00 63 32 63 32 00 32 00 46 20 56 6f 69 63 65 2e 2e 31 63 63 63 32 32 32 00 30 00 30 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: F Voice..1 ** Controls: MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 04 05 0a 00 40 52 04 01 15 18 18 07 0c 00 00 4b 04 02 0c 08 04 09 0e 00 40 5b 04 06 0c 08 04 09 0e 00 00 5e 04 00 74 27 1b 30 08 26 18 07 04 00 63 32 63 32 00 32 00 46 20 56 6f 69 63 65 2e 2e 31 63 63 63 32 32 32 00 30 00 30 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: F Voice..2 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 16 0f 03 0e 00 45 32 3d 01 06 07 0f 03 09 00 04 44 04 03 0c 06 0a 07 0e 00 45 1a 04 06 0e 08 04 09 0e 00 06 57 04 00 23 27 22 5b 02 16 18 07 04 00 63 32 63 32 00 32 00 46 20 56 6f 69 63 65 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: F Voice..2 ** Controls: MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 16 0f 03 0e 00 40 32 3d 01 06 07 0f 03 09 00 00 44 04 03 0c 06 0a 07 0e 00 40 1a 04 06 0e 08 04 09 0e 00 00 57 04 00 23 27 22 5b 02 16 18 07 04 00 63 32 63 32 00 32 00 46 20 56 6f 69 63 65 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: F Voice..3 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 1f 0e 06 0f 28 05 63 04 00 0c 19 06 07 0f 3c 06 63 08 05 0d 0c 0a 07 0f 0f 06 63 08 06 0d 19 06 07 0f 00 46 61 04 01 27 23 00 15 28 16 18 07 04 00 63 32 63 32 00 32 00 46 20 56 6f 69 63 65 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: F Voice..3 ** Controls: MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 1f 0e 06 0f 28 00 63 04 00 0c 19 06 07 0f 3c 00 63 08 05 0d 0c 0a 07 0f 0f 00 63 08 06 0d 19 06 07 0f 00 40 61 04 01 27 23 00 15 28 16 18 07 04 00 63 32 63 32 00 32 00 46 20 56 6f 69 63 65 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: F Voice..4 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 15 03 05 0f 01 44 47 00 03 0f 06 06 07 0e 00 40 43 04 04 12 15 04 05 0f 00 46 37 08 03 12 06 04 09 0f 00 45 58 04 04 3c 23 3a 0a 02 46 24 07 04 00 63 32 63 32 00 32 00 46 20 56 6f 69 63 65 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: F Voice..4 ** Controls: MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 15 03 05 0f 01 40 47 00 03 0f 06 06 07 0e 00 40 43 04 04 12 15 04 05 0f 00 40 37 08 03 12 06 04 09 0f 00 40 58 04 04 3c 23 3a 0a 02 46 24 07 04 00 63 32 63 32 00 32 00 46 20 56 6f 69 63 65 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Fem.Voice* ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 17 00 07 0f 00 00 2d 0d 06 0e 17 00 06 0f 00 00 2f 04 00 0b 0d 00 07 0a 00 02 63 08 00 13 0f 00 07 0f 00 02 63 04 06 24 1e 03 25 00 52 18 02 04 00 28 63 00 00 00 32 00 46 65 6d 2e 56 6f 69 63 65 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY MVoice ** Name: M Voice..1 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 18 03 04 0e 00 45 49 04 0a 0d 05 02 07 0f 00 46 3d 10 0a 0c 06 04 07 0f 00 46 63 08 0a 0d 18 02 07 0f 00 46 5f 08 04 04 1e 21 33 26 16 0c 04 04 00 63 01 63 00 00 32 00 4d 20 56 6f 69 63 65 2e 2e 31 63 63 63 32 32 32 00 00 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: M Voice..1 ** Controls: MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 18 03 04 0e 00 40 49 04 0a 0d 05 02 07 0f 00 40 3d 10 0a 0c 06 04 07 0f 00 40 63 08 0a 0d 18 02 07 0f 00 40 5f 08 04 04 1e 21 33 26 16 0c 04 04 00 63 01 63 00 00 32 00 4d 20 56 6f 69 63 65 2e 2e 31 63 63 63 32 32 32 00 00 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: M Voice..2 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 18 03 04 0f 00 05 47 04 0b 1f 18 03 04 0f 00 06 4a 04 01 0f 18 03 07 0f 00 46 60 08 0b 0f 18 03 07 0f 00 46 63 08 04 24 1d 0c 29 26 26 0c 04 04 00 63 01 63 00 00 32 00 4d 20 56 6f 69 63 65 2e 2e 32 63 63 63 32 32 32 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: M Voice..2 ** Controls: MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 18 03 04 0f 00 00 47 04 0b 1f 18 03 04 0f 00 00 4a 04 01 0f 18 03 07 0f 00 40 60 08 0b 0f 18 03 07 0f 00 40 63 08 04 24 1d 0c 29 26 26 0c 04 04 00 63 01 63 00 00 32 00 4d 20 56 6f 69 63 65 2e 2e 32 63 63 63 32 32 32 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: M Voice..3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 18 02 04 0a 00 45 5a 04 0c 12 07 0f 04 03 0a 45 2a 14 0b 09 03 0f 07 09 00 45 58 04 0c 08 00 0f 08 0c 0a 46 5f 0a 0d 24 1e 21 21 0c 16 0c 04 04 00 63 32 00 00 00 32 00 4d 20 56 6f 69 63 65 2e 2e 33 63 63 63 32 32 32 10 10 2b 07 00 10 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: M Voice..3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 18 02 04 0a 00 40 5a 04 0c 12 07 0f 04 03 0a 41 2a 14 0b 09 03 0f 07 09 00 40 58 04 0c 08 00 0f 08 0c 0a 41 5f 0a 0d 24 1e 21 21 0c 16 0c 04 04 00 63 32 00 00 00 32 00 4d 20 56 6f 69 63 65 2e 2e 33 63 63 63 32 32 32 10 10 2b 07 00 10 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: M Voice..4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 05 07 02 03 00 06 46 04 0c 0d 0c 06 05 0f 0a 06 34 19 0c 0c 06 0f 07 0d 00 06 5c 04 0e 0a 00 0f 08 0c 0a 05 5a 04 09 54 21 14 38 00 22 0c 04 04 00 63 32 00 00 00 32 00 4d 20 56 6f 69 63 65 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: M Voice..4 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 05 07 02 03 00 00 46 04 0c 0d 0c 06 05 0f 0a 00 34 19 0c 0c 06 0f 07 0d 00 00 5c 04 0e 0a 00 0f 08 0c 0a 00 5a 04 09 54 21 14 38 00 22 0c 04 04 00 63 32 00 00 00 32 00 4d 20 56 6f 69 63 65 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mal.Voice* ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 19 00 04 0f 13 00 4d 04 06 12 06 00 02 0f 00 00 47 04 05 0b 19 00 07 0f 00 00 63 08 00 0a 19 00 07 0f 00 00 4e 28 03 04 20 00 30 26 42 0c 02 04 00 28 63 00 00 00 32 00 4d 61 6c 2e 56 6f 69 63 65 2a 63 63 63 32 32 32 00 00 00 00 00 00 0c 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Male Voice ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 04 0f 2c 00 32 3f 03 0b 02 03 01 0f 0d 00 3c 10 00 0f 02 04 01 0f 23 00 3b 00 06 0e 00 01 06 0f 08 03 5f 0d 03 4b 0a 07 05 01 62 0c 05 04 00 63 03 00 00 00 32 00 4d 61 6c 65 20 56 6f 69 63 65 63 63 63 32 32 32 00 1f 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MaleVoices ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 04 0f 2c 00 32 1c 03 0b 02 03 01 0f 0d 00 3c 05 00 0f 02 04 01 0f 23 00 3b 00 06 0e 00 01 06 0f 08 03 5f 04 03 4b 1a 00 05 01 62 11 00 04 00 63 32 00 00 00 32 00 4d 61 6c 65 56 6f 69 63 65 73 63 63 63 32 32 32 00 11 00 31 00 04 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Other ** Name: Quartet..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 05 07 00 05 0e 00 45 52 00 00 0c 07 00 05 0e 0e 45 56 08 06 0c 1f 00 06 0f 00 05 63 04 00 0c 1f 00 07 0f 0a 06 63 08 06 2c 22 0e 13 08 56 18 07 04 00 63 63 00 00 00 32 00 51 75 61 72 74 65 74 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Quartet..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 05 07 00 05 0e 00 41 52 00 00 0c 07 00 05 0e 0e 41 56 08 06 0c 1f 00 06 0f 00 00 63 04 00 0c 1f 00 07 0f 0a 00 63 08 06 2c 22 0e 13 08 56 18 07 04 00 63 63 00 00 00 32 00 51 75 61 72 74 65 74 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Quartet..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 07 00 05 0e 00 44 51 00 00 0e 07 00 05 0e 0e 45 55 04 06 0e 1f 00 06 0f 00 06 63 08 00 0c 1f 00 07 0f 0a 06 63 08 06 34 22 0e 13 08 56 18 07 04 00 63 63 00 00 00 32 00 51 75 61 72 74 65 74 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Quartet..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 07 00 05 0e 00 41 51 00 00 0e 07 00 05 0e 0e 41 55 04 06 0e 1f 00 06 0f 00 00 63 08 00 0c 1f 00 07 0f 0a 00 63 08 06 34 22 0e 13 08 56 18 07 04 00 63 63 00 00 00 32 00 51 75 61 72 74 65 74 2e 2e 32 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Ensem.1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 7 14 06 00 04 07 14 02 49 12 06 0e 08 02 04 09 14 02 59 13 00 0e 09 02 04 0b 00 01 63 09 06 0e 09 02 04 0a 00 01 5e 0a 00 3d 21 08 0c 00 52 05 02 04 00 63 32 00 00 00 32 00 53 79 2e 45 6e 73 65 6d 2e 31 63 63 63 32 32 32 00 54 00 30 00 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Ensem.2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 8 1f 17 09 03 0c 14 02 48 3b 03 0a 04 02 05 07 14 01 63 13 06 1f 07 02 05 0b 00 01 63 13 06 0a 03 01 05 0b 00 01 63 0a 00 3e 21 0c 16 00 52 05 02 04 00 63 32 00 00 00 32 00 53 79 2e 45 6e 73 65 6d 2e 32 63 63 63 32 32 32 00 60 00 10 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Ensem.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 17 09 03 0c 14 02 48 3b 03 0d 04 02 05 07 14 01 63 13 06 1f 07 02 05 0b 00 01 63 13 06 0d 03 01 05 0b 00 01 63 0a 00 3e 21 0c 16 00 52 05 02 04 00 63 32 00 00 00 32 00 53 79 2e 45 6e 73 65 6d 2e 32 63 63 63 32 32 32 00 60 00 10 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Ensem.3 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 9 1f 1f 05 02 0f 27 02 50 0a 06 1f 1f 05 02 0f 00 02 43 0a 00 1a 1f 07 04 0f 00 06 56 10 06 1a 1f 07 04 0f 00 01 5e 04 00 14 63 00 0c 00 63 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 45 6e 73 65 6d 2e 33 63 63 63 32 32 32 00 00 00 30 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Ensem.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 05 02 0f 27 02 50 0a 06 1f 1f 05 02 0f 00 02 43 0d 00 1a 1f 07 04 0f 00 06 56 10 06 1a 1f 07 04 0f 00 01 5e 04 00 14 63 00 0c 00 63 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 45 6e 73 65 6d 2e 33 63 63 63 32 32 32 00 00 00 30 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Ensem.4 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 10 08 04 00 04 0f 14 01 3e 13 06 07 07 00 04 0f 14 01 51 0a 02 10 1f 00 04 0f 14 01 63 09 04 1b 09 00 04 0b 14 01 5f 13 00 3c 1f 01 14 00 52 05 02 04 00 63 32 00 00 00 32 00 53 79 2e 45 6e 73 65 6d 2e 34 63 63 63 32 32 32 00 00 00 30 00 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Ensem.5 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 11 0e 02 00 01 0a 13 00 44 04 03 0b 06 00 05 0e 00 03 63 00 06 0e 06 00 05 0e 00 03 5b 08 00 0e 06 00 05 0e 00 03 63 04 00 35 1c 0b 1b 00 32 18 02 04 00 28 63 00 00 00 32 00 53 79 2e 45 6e 73 65 6d 2e 35 63 63 63 32 32 32 00 50 00 10 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Ensem.5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 02 00 01 0d 13 00 44 04 03 0b 06 00 05 0e 00 03 63 00 06 0e 06 00 05 0e 00 03 5b 08 00 0e 06 00 05 0e 00 03 63 04 00 35 1c 0b 1b 00 32 18 02 04 00 28 63 00 00 00 32 00 53 79 2e 45 6e 73 65 6d 2e 35 63 63 63 32 32 32 00 50 00 10 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Voice 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 27 0c 0e 00 04 0f 0a 40 41 04 06 0a 0e 00 04 0f 0a 40 41 04 00 0a 0c 00 06 0f 0a 42 63 04 06 0a 0e 00 06 0f 0a 42 63 08 00 2c 1e 0a 50 04 4a 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 56 6f 69 63 65 20 31 63 56 63 2e 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Voice 1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 0e 00 04 0f 0a 40 41 04 06 0d 0e 00 04 0f 0a 40 41 04 00 0d 0c 00 06 0f 0a 42 63 04 06 0d 0e 00 06 0f 0a 42 63 08 00 2c 1e 0a 50 04 4a 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 56 6f 69 63 65 20 31 63 56 63 2e 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Voice 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 28 12 02 01 04 0f 00 00 37 04 0c 0f 02 01 02 0b 00 00 31 04 0b 0e 02 01 05 0a 0f 02 63 04 0b 0e 02 01 05 0a 0f 02 63 08 08 34 1e 12 2c 00 42 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 56 6f 69 63 65 20 32 63 3e 63 2f 32 32 00 00 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Voice 2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 02 01 04 0f 00 00 37 04 0c 0f 02 01 02 0b 00 00 31 04 0b 0e 02 01 05 0d 0f 02 63 04 0b 0e 02 01 05 0d 0f 02 63 08 08 34 1e 12 2c 00 42 18 02 04 00 28 32 00 00 00 32 00 53 79 2e 56 6f 69 63 65 20 32 63 3e 63 2f 32 32 00 00 00 20 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Syntvoice* ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 00 00 22 03 0b 17 00 06 0f 00 01 52 04 01 0c 0d 00 07 0b 00 02 42 08 06 11 0f 00 07 0f 00 01 63 04 03 3b 1e 0b 21 00 42 18 02 04 00 28 63 00 00 00 32 00 53 79 6e 74 76 6f 69 63 65 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: VocalNuts ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 2 1f 00 00 04 0f 00 40 47 2f 00 0e 08 07 07 0f 05 00 4c 00 06 13 1c 0d 0c 06 08 00 63 05 06 14 11 0d 0b 00 00 03 63 04 00 7a 10 00 51 00 68 17 00 04 00 63 32 00 00 00 32 00 56 6f 63 61 6c 4e 75 74 73 20 63 63 63 32 32 32 06 30 00 30 00 01 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: > Voices < ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0e 00 07 0f 63 03 45 04 04 0a 1f 00 03 0f 00 01 42 04 04 09 1f 00 07 0f 00 06 44 1f 02 08 0e 00 06 0f 00 46 63 04 05 3c 23 2a 23 00 36 18 02 04 00 28 41 00 00 00 32 00 3e 20 56 6f 69 63 65 73 20 3c 63 63 63 32 32 32 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: > Voices < ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0e 00 07 0f 63 00 45 04 04 0a 1f 00 03 0f 00 00 42 04 04 09 1f 00 07 0f 00 00 44 1f 02 08 0e 00 06 0f 00 40 63 04 05 3c 23 2a 23 00 36 18 02 04 00 28 41 00 00 00 32 00 3e 20 56 6f 69 63 65 73 20 3c 63 63 63 32 32 32 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Voices ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 27 1f 00 00 04 0f 00 00 39 20 03 0b 02 03 01 0f 05 00 40 05 00 0f 02 04 01 0f 08 00 35 03 06 0e 00 01 06 0f 00 03 63 04 03 7b 1a 00 05 01 62 11 00 04 00 63 32 00 00 00 32 00 56 6f 69 63 65 73 20 20 20 20 63 63 63 32 32 32 00 01 00 01 00 06 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Whisper ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 00 01 0e 00 40 5c 39 00 0b 08 00 04 0d 26 02 63 05 04 10 0a 00 06 0c 00 03 41 3e 06 0b 08 00 04 0d 00 02 61 03 08 3e 4f 00 44 63 30 11 04 04 00 63 15 00 00 00 32 00 57 68 69 73 70 65 72 20 20 20 63 63 63 32 32 32 00 2f 00 31 00 60 00 36 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wind Voice ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 24 06 1d 01 02 0f 00 01 06 17 00 00 00 0c 02 0f 2e 01 53 19 0e 17 01 00 02 0a 00 02 4b 08 08 0b 01 06 05 0f 01 02 5c 08 0e 01 63 00 52 00 53 18 07 04 00 63 63 00 00 00 32 00 57 69 6e 64 20 56 6f 69 63 65 63 63 63 32 32 32 00 34 00 70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 22 * CATEGORY PERCUSSION * SUBCATEGORY Bell ** Name: Agogo * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 07 0f 00 01 49 08 06 1f 1f 00 07 0f 00 00 41 0c 00 1a 0f 00 07 00 00 02 63 04 06 1a 0f 00 07 00 00 03 63 04 00 44 12 00 00 00 32 24 02 04 00 28 63 00 00 00 32 00 41 67 6f 67 6f 20 20 20 20 2a 63 63 63 32 32 32 00 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Agogo Bell ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 11 1f 12 19 03 0c 06 02 57 16 0c 1f 13 19 04 0c 11 02 59 3c 09 1f 11 10 06 0e 00 01 63 08 0a 1f 11 0f 06 0a 00 02 63 08 0a 7c 13 00 06 00 62 18 04 04 00 63 32 00 00 00 32 00 41 67 6f 67 6f 20 42 65 6c 6c 63 63 63 32 32 32 00 20 0a 1d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Agogo Bell ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 19 03 0c 06 02 57 16 0c 1f 13 19 04 0c 11 02 59 3c 09 1f 11 10 06 0e 00 01 63 08 0d 1f 11 0f 06 0d 00 02 63 08 0a 7c 13 00 06 00 62 18 04 04 00 63 32 00 00 00 32 00 41 67 6f 67 6f 20 42 65 6c 6c 63 63 63 32 32 32 00 20 0d 1d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Analobell* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 17 0a 00 04 00 38 00 54 10 0d 1f 03 08 03 00 4c 00 51 09 08 17 06 08 03 00 14 05 63 10 0b 1d 0a 08 03 00 0e 02 63 0a 0e 04 23 00 00 00 32 02 02 04 00 28 63 00 00 00 32 00 41 6e 61 6c 6f 62 65 6c 6c 2a 63 63 63 32 32 32 00 04 00 18 00 04 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bangkok ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 1a 00 05 07 00 01 58 09 08 1e 10 00 03 04 00 03 57 08 15 1c 08 05 05 0b 00 00 62 04 0e 1a 0c 05 04 04 00 00 60 0a 00 0c 1e 00 00 00 42 1a 03 04 00 63 32 26 2a 00 32 00 42 61 6e 67 6b 6f 6b 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Bells ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 05 00 02 07 00 01 50 00 1b 1f 10 0c 03 0f 00 01 4e 10 0b 1f 0b 00 05 0e 00 05 62 04 03 1b 0f 0c 03 0f 00 05 4f 04 0c 2c 18 00 00 00 22 0c 04 04 00 28 32 00 00 00 32 00 42 61 73 73 20 42 65 6c 6c 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Bells ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 00 02 07 00 00 50 00 1b 1f 10 0c 03 0f 00 00 4e 10 0b 1f 0b 00 05 0e 00 00 62 04 03 1b 0f 0c 03 0f 00 00 4f 04 0c 2c 18 00 00 00 22 0c 04 04 00 28 32 00 00 00 32 00 42 61 73 73 20 42 65 6c 6c 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 31 1f 16 07 03 0a 00 01 4c 16 06 1f 10 0a 04 08 00 01 44 0a 00 19 09 00 04 00 00 03 60 04 06 18 09 00 06 00 00 02 5f 00 00 3c 1e 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 42 65 6c 6c 20 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 16 07 03 0a 00 01 4c 16 06 1f 10 0a 04 08 00 01 44 0d 00 19 09 00 04 00 00 03 60 04 06 18 09 00 06 00 00 02 5f 00 00 3c 1e 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 42 65 6c 6c 20 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell Ensem ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 05 00 04 04 50 04 51 20 0e 1e 0a 01 04 02 50 04 53 1f 08 1e 1d 0a 04 0f 00 02 5c 05 0e 1f 1f 0a 04 0f 00 03 5c 04 08 04 1a 00 00 00 52 1d 02 04 00 63 32 00 32 00 32 00 42 65 6c 6c 20 45 6e 73 65 6d 63 63 63 32 32 32 00 30 00 46 00 01 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell Pad ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 30 12 0b 0f 04 07 00 40 43 1c 00 1f 08 07 07 0f 05 00 63 05 00 0e 07 02 07 0f 08 03 5c 00 06 15 08 04 05 0f 00 03 63 03 06 7e 1f 00 06 00 4a 1d 02 04 00 63 32 00 00 00 32 00 42 65 6c 6c 20 50 61 64 20 20 63 63 63 32 32 32 06 30 00 51 00 04 00 56 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 04 01 02 0d 00 01 52 0d 03 1b 0f 00 03 0e 58 04 57 0d 08 1d 0f 03 03 0e 57 05 63 08 03 1e 02 03 03 0e 00 03 63 08 0e 34 0c 00 06 00 42 00 02 04 00 63 11 06 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 04 01 02 0d 00 01 52 0d 03 1b 0f 00 03 0e 58 02 57 0d 08 1d 0f 03 03 0e 57 01 63 08 03 1e 02 03 03 0e 00 00 63 08 0e 34 0c 00 06 00 42 00 02 04 00 63 11 06 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 04 00 05 00 62 04 38 2a 16 1f 07 00 04 01 00 02 4a 2a 00 1f 07 00 05 00 00 06 63 04 0b 1f 08 00 05 00 00 05 60 0d 08 3c 14 00 00 00 62 0c 02 04 00 63 32 00 32 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 04 00 05 00 62 01 38 2a 16 1f 07 00 04 01 00 01 4a 2a 00 1f 07 00 05 00 00 00 63 04 0b 1f 08 00 05 00 00 00 60 0d 08 3c 14 00 00 00 62 0c 02 04 00 63 32 00 32 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 10 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 04 01 01 0b 16 02 4e 05 03 15 0e 00 01 0d 00 03 54 10 16 1c 06 04 02 07 00 04 63 08 16 18 18 03 01 0e 00 04 62 04 10 24 09 00 04 00 42 24 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 04 01 01 0b 16 01 4e 05 03 15 0e 00 01 0d 00 01 54 10 16 1c 06 04 02 07 00 00 63 08 16 18 18 03 01 0e 00 01 62 04 10 24 09 00 04 00 42 24 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 30 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....4 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 0a 17 05 01 00 02 4e 2d 06 19 0a 1f 03 02 00 03 41 2d 04 19 07 12 04 07 13 05 63 0a 06 19 07 0f 03 07 13 47 63 0a 03 7c 14 0a 00 39 06 0c 02 04 00 63 15 27 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....4 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 19 0a 17 05 01 00 01 4e 2d 06 19 0a 1f 03 02 00 01 41 2d 04 19 07 12 04 07 13 01 63 0a 06 19 07 0f 03 07 13 40 63 0a 03 7c 14 0a 00 39 06 0c 02 04 00 63 15 27 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....5 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 0a 02 03 01 00 04 51 2d 06 19 0a 1f 05 02 00 03 48 08 04 19 07 12 04 07 13 06 62 0d 06 19 07 07 04 07 13 45 63 04 03 76 14 0a 00 39 06 0c 02 04 00 63 15 27 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....5 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 19 0a 02 03 01 00 01 51 2d 06 19 0a 1f 05 02 00 01 48 08 04 19 07 12 04 07 13 01 62 0d 06 19 07 07 04 07 13 40 63 04 03 76 14 0a 00 39 06 0c 02 04 00 63 15 27 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1a 0e 04 00 00 05 4e 2d 13 1f 0a 00 05 00 00 04 49 08 0b 1f 1f 00 04 0f 00 05 49 0d 0b 1f 06 00 04 00 00 06 63 04 03 41 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1a 0e 04 00 00 01 4e 2d 13 1f 0a 00 05 00 00 01 49 08 0b 1f 1f 00 04 0f 00 01 49 0d 0b 1f 06 00 04 00 00 00 63 04 03 41 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1a 0e 04 00 00 04 4e 28 03 1f 0a 00 05 00 00 04 49 16 03 1f 1f 00 04 0f 00 04 49 16 03 1f 06 00 06 00 00 06 63 04 03 42 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1a 0e 04 00 00 01 4e 28 03 1f 0a 00 05 00 00 02 49 16 03 1f 1f 00 04 0f 00 01 49 16 03 1f 06 00 06 00 00 00 63 04 03 42 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....8 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 1f 04 00 00 05 4b 19 03 1f 00 00 02 0f 00 04 4d 16 1b 1f 0d 1f 04 00 00 06 63 04 03 1f 0a 07 04 00 00 05 63 04 03 7c 5c 00 01 63 32 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....8 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0d 1f 04 00 00 01 4b 19 03 1f 00 00 02 0f 00 01 4d 16 1b 1f 0d 1f 04 00 00 01 63 04 03 1f 0a 07 04 00 00 01 63 04 03 7c 5c 00 01 63 32 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....9 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 15 09 03 0a 00 03 63 34 13 12 1f 0a 05 0f 00 43 63 0d 03 12 1f 0a 05 0f 00 45 63 19 03 12 08 0a 05 0f 00 46 63 04 03 45 1a 00 00 39 66 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 39 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell.....9 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 15 09 03 0a 00 00 63 34 13 12 1f 0a 05 0f 00 40 63 0d 03 12 1f 0a 05 0f 00 40 63 19 03 12 08 0a 05 0f 00 40 63 04 03 45 1a 00 00 39 66 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 2e 39 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell....10 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 06 04 0a 00 04 3f 24 13 1f 0c 06 03 0f 00 04 3f 16 03 1f 06 07 03 0c 00 45 48 0d 03 18 04 09 04 0c 00 06 63 08 03 43 1a 00 00 1c 66 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 31 30 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell....10 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0d 06 04 0a 00 01 3f 24 13 1f 0c 06 03 0f 00 01 3f 16 03 1f 06 07 03 0c 00 41 48 0d 03 18 04 09 04 0c 00 01 63 08 03 43 1a 00 00 1c 66 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 31 30 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell....11 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 05 0e 04 0f 00 02 48 1c 03 1f 0b 00 05 0a 00 04 5c 24 1b 1f 08 00 04 0a 00 05 5d 08 03 1f 10 00 04 0b 00 05 63 08 03 7e 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 31 31 63 63 63 32 32 32 00 00 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bell....11 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 0e 04 0f 00 02 48 1c 03 1f 0b 00 05 0a 00 00 5c 24 1b 1f 08 00 04 0a 00 00 5d 08 03 1f 10 00 04 0b 00 00 63 08 03 7e 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 42 65 6c 6c 2e 2e 2e 2e 31 31 63 63 63 32 32 32 00 00 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BelleBell* ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 08 06 02 0d 16 02 5c 07 0e 18 08 06 02 0d 15 02 5b 08 08 1f 0a 08 03 0e 0d 01 61 00 00 1c 0a 08 04 0e 0b 01 63 00 06 04 19 00 24 00 32 1d 01 04 00 28 63 00 00 00 30 00 42 65 6c 6c 65 42 65 6c 6c 2a 63 63 63 32 32 32 00 09 00 0b 00 04 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bells ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0c 0d 04 05 00 00 57 11 03 1f 0c 0d 04 05 00 40 5f 04 03 1f 0c 0d 04 05 00 38 61 36 03 1f 0c 0d 04 05 00 00 63 14 03 47 1d 00 03 13 5e 30 04 04 00 63 32 00 00 00 32 00 42 65 6c 6c 73 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BellyStuff ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 0b 0f 04 07 00 40 43 1c 00 13 0c 07 07 0b 05 00 58 05 00 1f 07 02 07 0f 08 03 5c 00 06 13 0c 04 05 0a 00 03 57 03 06 7e 1f 00 06 00 4a 1d 02 04 00 63 32 00 00 00 32 00 42 65 6c 6c 79 53 74 75 66 66 63 63 63 32 32 32 06 70 00 61 00 04 00 66 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Big Bell.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 04 01 02 0d 00 03 51 0c 03 1b 0f 00 03 0e 58 03 55 1a 08 1d 0f 03 03 0e 57 07 63 00 03 1e 02 03 03 0e 00 06 63 04 0e 74 0c 00 06 00 63 00 02 04 00 63 11 06 00 00 32 00 42 69 67 20 42 65 6c 6c 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Big Bell.1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 04 01 02 0d 00 01 51 0c 03 1b 0f 00 03 0e 58 03 55 1a 08 1d 0f 03 03 0e 57 04 63 00 03 1e 02 03 03 0e 00 00 63 04 0e 74 0c 00 06 00 63 00 02 04 00 63 11 06 00 00 32 00 42 69 67 20 42 65 6c 6c 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Big Bell.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 04 01 02 0d 00 04 52 15 03 1b 0f 00 03 0e 58 04 57 12 08 1d 0f 03 03 0e 57 05 63 0d 03 1e 02 03 03 0e 00 05 63 04 0e 74 0c 00 06 00 63 00 02 04 00 63 11 06 00 00 32 00 42 69 67 20 42 65 6c 6c 2e 32 63 63 63 32 32 32 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Big Bell.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 04 01 02 0d 00 01 52 15 03 1b 0f 00 03 0e 58 02 57 12 08 1d 0f 03 03 0e 57 02 63 0d 03 1e 02 03 03 0e 00 00 63 04 0e 74 0c 00 06 00 63 00 02 04 00 63 11 06 00 00 32 00 42 69 67 20 42 65 6c 6c 2e 32 63 63 63 32 32 32 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Big Bell.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 04 01 02 0d 00 01 51 18 03 1b 0f 00 03 0e 58 03 5a 00 08 1d 0f 03 03 0e 57 06 63 08 03 1e 02 03 03 0e 00 05 63 00 0e 74 0c 00 06 00 63 00 02 04 00 63 11 06 00 00 32 00 42 69 67 20 42 65 6c 6c 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Big Bell.3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 04 01 02 0d 00 01 51 18 03 1b 0f 00 03 0e 58 03 5a 00 08 1d 0f 03 03 0e 57 04 63 08 03 1e 02 03 03 0e 00 00 63 00 0e 74 0c 00 06 00 63 00 02 04 00 63 11 06 00 00 32 00 42 69 67 20 42 65 6c 6c 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BowedBell ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 6 0b 04 05 04 0f 00 42 55 22 06 0a 07 04 04 0f 00 02 47 0a 00 0b 1f 02 04 0f 00 02 63 0d 06 0b 1f 02 05 0f 00 02 62 04 00 1c 1e 06 11 09 5a 0c 04 04 00 63 28 00 00 00 32 00 42 6f 77 65 64 42 65 6c 6c 20 63 63 63 32 32 32 00 10 00 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrightBell ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 0e 05 02 0d 27 02 63 16 06 1f 0e 05 02 0e 1a 02 58 16 00 1a 1f 07 04 0f 00 06 60 08 0e 1a 1f 07 04 0f 00 01 5e 08 08 14 1a 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 42 72 69 67 68 74 42 65 6c 6c 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Brthbells ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 16 1c 08 00 03 0e 00 01 55 12 03 1c 07 00 01 0e 2e 01 58 27 0b 0e 04 00 02 0a 00 02 63 04 0a 0d 09 00 02 0c 01 02 63 10 0b 04 54 00 47 00 53 18 07 04 00 63 32 00 00 00 32 00 42 72 74 68 62 65 6c 6c 73 20 63 63 63 32 32 32 00 04 00 40 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Carnival! ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 19 1f 1f 01 0f 0f 22 00 2f 04 08 18 10 1c 0e 01 24 40 40 05 06 1f 04 10 0f 0b 2c 00 2b 00 00 17 1f 09 08 0f 00 00 54 04 0e 3a 63 3f 31 2c 5e 0c 07 04 00 63 63 00 32 00 32 00 43 61 72 6e 69 76 61 6c 21 20 63 63 63 32 32 32 00 00 00 10 00 07 00 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ChurchBell ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 09 1f 03 04 00 01 5b 3d 03 1c 09 1f 04 04 00 04 53 05 00 1c 09 1f 04 04 00 07 5a 04 03 1c 09 1f 03 04 00 07 63 04 03 2c 19 2a 0e 00 10 18 04 04 00 28 32 00 00 00 32 00 43 68 75 72 63 68 42 65 6c 6c 63 63 63 32 32 32 08 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ChurchBell ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 09 1f 03 04 00 01 5b 3d 03 1c 09 1f 04 04 00 01 53 05 00 1c 09 1f 04 04 00 00 5a 04 03 1c 09 1f 03 04 00 00 63 04 03 2c 19 2a 0e 00 10 18 04 04 00 28 32 00 00 00 32 00 43 68 75 72 63 68 42 65 6c 6c 63 63 63 32 32 32 08 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ClickChime ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 19 16 00 03 07 00 01 43 00 03 18 18 03 03 03 22 00 60 00 0c 16 07 06 03 06 00 01 3c 08 03 1a 05 07 03 00 00 01 63 04 0b 39 23 00 08 00 42 18 04 04 00 63 32 00 00 00 32 00 43 6c 69 63 6b 43 68 69 6d 65 63 63 63 32 32 32 00 31 00 03 00 11 00 01 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cloches * ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 04 1f 04 00 00 00 4b 0f 0a 1f 05 1b 04 00 00 00 49 10 0c 1e 09 16 03 00 00 00 63 03 08 1f 09 0a 03 00 00 00 63 04 0e 04 23 00 00 00 62 11 02 04 00 28 32 00 00 00 32 00 43 6c 6f 63 68 65 73 20 20 2a 63 63 63 32 32 32 00 06 00 04 00 06 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cow Bell ** Source: Yamaha DX11 factory preset, bank D, voice 10 1f 0a 1f 03 0c 00 00 43 20 03 1f 16 00 04 0b 00 00 4f 24 03 1f 12 10 08 07 00 00 5f 34 03 1f 12 12 08 0f 00 00 63 38 03 3c 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 43 6f 77 20 42 65 6c 6c 20 20 63 63 63 32 32 32 0b 07 0c 04 0b 0e 09 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cow-Belle* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 17 1f 09 00 00 00 51 38 03 1f 14 1f 0a 00 00 00 47 10 03 1a 0e 1f 09 00 00 03 63 04 13 1a 10 1f 08 00 00 02 63 04 0b 04 23 00 00 00 62 18 01 04 00 28 32 00 00 00 32 00 43 6f 77 2d 42 65 6c 6c 65 2a 63 63 63 32 32 32 0b 23 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gong.....1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 07 06 05 02 1f 04 41 04 0b 1f 1f 04 04 0f 00 05 4a 1c 0b 16 07 06 05 02 1f 06 51 0c 0b 1f 1f 04 04 0f 00 06 63 04 0b 6a 23 00 00 00 52 0c 04 04 00 63 32 00 00 00 32 00 47 6f 6e 67 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gong.....1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 07 06 05 02 1f 01 41 04 0b 1f 1f 04 04 0f 00 01 4a 1c 0b 16 07 06 05 02 1f 01 51 0c 0b 1f 1f 04 04 0f 00 00 63 04 0b 6a 23 00 00 00 52 0c 04 04 00 63 32 00 00 00 32 00 47 6f 6e 67 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gong.....2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 02 0c 06 03 0c 1f 05 3b 2b 0b 0c 09 0e 03 03 00 03 4d 1c 0b 18 09 07 03 03 1f 07 50 0c 0b 18 09 05 06 0e 00 45 63 04 0b 72 19 00 00 40 56 0c 04 04 00 63 32 00 00 00 32 00 47 6f 6e 67 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gong.....2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 02 0c 06 03 0c 1f 01 3b 2b 0b 0c 09 0e 03 03 00 01 4d 1c 0b 18 09 07 03 03 1f 01 50 0c 0b 18 09 05 06 0e 00 40 63 04 0b 72 19 00 00 40 56 0c 04 04 00 63 32 00 00 00 32 00 47 6f 6e 67 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gong.....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 1f 02 02 0f 1f 04 35 03 0b 07 1f 02 02 0f 00 04 46 02 0b 13 1f 02 02 0f 1f 06 4a 03 0b 1f 1f 03 03 0e 00 45 63 00 0b 72 0b 00 0d 40 66 0c 04 04 00 63 32 00 00 00 32 00 47 6f 6e 67 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gong.....3 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 19 1f 02 02 0f 1f 00 35 03 0b 07 1f 02 02 0f 00 00 46 02 0b 13 1f 02 02 0f 1f 00 4a 03 0b 1f 1f 03 03 0e 00 40 63 00 0b 72 0b 00 0d 40 56 0c 04 04 00 63 32 00 00 00 32 00 47 6f 6e 67 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gong Ageng ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 04 01 01 0b 16 04 43 05 03 07 04 00 01 0d 00 04 47 05 16 10 06 04 02 07 00 03 63 04 16 18 18 03 01 0e 00 03 5e 04 10 04 52 00 07 00 62 18 04 04 00 28 32 00 00 00 32 00 47 6f 6e 67 20 41 67 65 6e 67 63 63 63 32 32 32 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gongs * ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 04 0f 00 03 53 05 0b 1f 1f 00 03 0f 00 00 55 01 0b 1f 0d 00 02 0a 00 01 45 0a 0b 16 09 0f 03 0e 00 02 5a 00 0b 6b 63 20 50 00 73 18 03 04 00 28 63 00 00 00 29 00 47 6f 6e 67 73 20 20 20 20 2a 63 63 63 32 32 32 00 70 00 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HiTine81Z ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 10 1f 0d 09 0f 0c 27 02 3d 35 10 1f 12 02 04 0e 19 04 56 05 16 1f 0c 08 0d 0c 00 43 60 04 0e 1f 08 04 07 0e 00 43 63 05 10 3c 14 08 02 1b 56 11 02 04 00 63 32 00 00 00 32 00 48 69 54 69 6e 65 38 31 5a 20 63 63 63 32 32 32 00 7f 00 01 00 18 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HumorBells ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 06 0a 04 0d 00 03 44 16 03 1f 06 06 04 0d 00 04 56 19 0b 13 06 06 05 0d 00 06 62 04 0b 1f 06 06 04 0d 00 05 57 04 0a 7d 24 23 00 00 02 0c 02 04 00 63 63 00 00 00 32 00 48 75 6d 6f 72 42 65 6c 6c 73 63 63 63 32 32 32 00 10 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HumorBells ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 06 0a 04 0d 00 01 44 16 03 1f 06 06 04 0d 00 00 56 19 0b 13 06 06 05 0d 00 00 62 04 0b 1f 06 06 04 0d 00 00 57 04 0a 7d 24 23 00 00 02 0c 02 04 00 63 63 00 00 00 32 00 48 75 6d 6f 72 42 65 6c 6c 73 63 63 63 32 32 32 00 10 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: KeyBell ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 0d 04 09 00 43 3f 2d 06 1f 0e 0d 04 09 00 43 3f 2d 00 1f 09 08 04 0d 00 04 5f 05 0e 1f 09 08 04 0d 00 04 63 04 08 04 1f 0b 04 1a 5d 1d 02 04 00 63 0a 50 00 00 32 00 4b 65 79 42 65 6c 6c 20 20 20 63 63 63 32 32 32 00 70 00 60 00 01 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Large Ben ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 04 01 02 0d 00 04 54 0c 03 1b 0f 00 03 0e 58 06 63 1f 08 1d 0f 03 03 0e 57 06 63 0c 03 1e 02 03 03 0e 00 05 63 05 0e 74 0c 00 06 00 63 00 02 04 00 63 11 06 00 00 32 00 4c 61 72 67 65 20 42 65 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Large Ben ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 04 01 02 0d 00 01 54 0c 03 1b 0f 00 03 0e 58 03 63 1f 08 1d 0f 03 03 0e 57 04 63 0c 03 1e 02 03 03 0e 00 00 63 05 0e 74 0c 00 06 00 63 00 02 04 00 63 11 06 00 00 32 00 4c 61 72 67 65 20 42 65 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LastTwango ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 0a 07 08 03 00 03 58 16 03 13 1c 09 04 09 00 03 63 00 1b 16 0c 02 08 04 00 04 63 00 1b 13 09 06 09 0e 00 05 63 0d 1b 12 23 00 00 00 02 18 04 04 00 63 01 63 00 00 32 00 4c 61 73 74 54 77 61 6e 67 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: LastTwango ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 0a 07 08 03 00 00 58 16 03 13 1c 09 04 09 00 00 63 00 1b 16 0c 02 08 04 00 00 63 00 1b 13 09 06 09 0e 00 00 63 0d 1b 12 23 00 00 00 02 18 04 04 00 63 01 63 00 00 32 00 4c 61 73 74 54 77 61 6e 67 6f 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 23 ** Name: LoTine81Z ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank A, voice 9 1f 09 09 0f 0c 27 02 35 37 10 1f 04 03 04 0d 19 04 56 05 16 1f 0f 08 0d 0c 00 43 60 04 0e 1f 0c 03 07 0e 00 43 63 05 10 3c 14 08 02 1b 56 05 02 04 00 63 32 00 00 00 32 00 4c 6f 54 69 6e 65 38 31 5a 20 63 63 63 32 32 32 00 7f 00 01 00 18 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MellowSqar ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 23 1f 06 00 03 0f 00 02 4f 09 00 1f 0e 00 03 0f 00 02 55 0a 06 15 12 00 07 0f 00 01 63 05 06 14 0e 00 07 0f 00 01 63 04 00 34 20 00 00 00 52 05 07 0c 00 63 32 00 00 00 32 00 4d 65 6c 6c 6f 77 53 71 61 72 63 63 63 32 32 32 00 02 00 00 00 01 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Moooo Bell ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 10 12 06 05 00 03 4a 05 13 1f 10 12 06 05 00 05 47 0d 03 1f 10 12 06 05 00 05 63 08 03 1f 10 12 08 05 00 06 63 0a 03 2c 23 00 00 00 62 18 05 04 00 63 32 00 00 00 32 00 4d 6f 6f 6f 6f 20 42 65 6c 6c 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Moooo Bell ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 10 12 06 05 00 00 4a 05 13 1f 10 12 06 05 00 00 47 0d 03 1f 10 12 06 05 00 00 63 08 03 1f 10 12 08 05 00 00 63 0a 03 2c 23 00 00 00 62 18 05 04 00 63 32 00 00 00 32 00 4d 6f 6f 6f 6f 20 42 65 6c 6c 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mr.Rogers ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 0f 00 06 01 06 04 50 18 0b 1f 0d 00 06 01 03 04 4f 1b 09 1f 0a 00 05 01 00 02 63 04 0e 1f 0c 06 04 0b 00 03 63 04 00 04 19 00 00 00 52 23 02 04 00 63 32 00 32 00 32 00 4d 72 2e 52 6f 67 65 72 73 20 63 63 63 32 32 32 00 00 00 13 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OhMy!Bells ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 03 0f 00 40 49 10 08 1f 1f 00 03 0f 00 40 49 10 0e 11 1f 05 03 0f 63 00 63 13 10 11 1f 05 03 0f 63 00 63 13 16 04 0c 00 00 0f 00 0c 02 04 00 63 32 00 00 00 32 00 4f 68 4d 79 21 42 65 6c 6c 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntCowBel ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 10 12 06 05 00 02 4f 17 13 1f 10 12 06 05 00 02 46 08 03 1f 10 12 06 05 00 05 49 04 03 1f 10 12 08 05 00 04 63 04 03 28 23 00 00 00 62 18 05 04 00 63 32 00 00 00 32 00 4f 72 6e 74 43 6f 77 42 65 6c 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntCowBel ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 10 12 06 05 00 00 4f 17 13 1f 10 12 06 05 00 00 46 08 03 1f 10 12 06 05 00 00 49 04 03 1f 10 12 08 05 00 00 63 04 03 28 23 00 00 00 62 18 05 04 00 63 32 00 00 00 32 00 4f 72 6e 74 43 6f 77 42 65 6c 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PluckBell ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 07 04 0d 00 43 63 35 08 1f 16 0f 07 0e 00 04 63 00 0e 1f 1f 0f 07 0e 00 05 63 10 0e 1f 16 0a 06 0e 00 03 63 00 08 04 43 00 04 29 6f 18 04 04 00 63 00 00 00 00 32 00 50 6c 75 63 6b 42 65 6c 6c 20 63 63 63 32 32 32 26 60 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpaceBell1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 03 03 0f 0a 03 63 0d 03 1f 11 0c 04 0f 00 05 55 08 0b 1f 00 00 04 0f 00 05 51 08 13 09 0d 09 06 0f 00 06 63 08 0b 46 1f 00 0e 00 02 00 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 42 65 6c 6c 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpaceBell1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 03 03 0f 0a 00 63 0d 03 1f 11 0c 04 0f 00 00 55 08 0b 1f 00 00 04 0f 00 00 51 08 13 09 0d 09 06 0f 00 00 63 08 0b 46 1f 00 0e 00 02 00 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 42 65 6c 6c 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpaceBell2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 07 01 09 0a 43 55 04 03 19 1f 0a 06 0f 00 44 63 3d 0b 1f 1f 0a 06 0f 00 45 63 38 13 18 0f 0a 06 0f 00 45 63 2a 0b 65 16 00 09 04 36 00 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 42 65 6c 6c 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpaceBell2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 07 01 09 0a 40 55 04 03 19 1f 0a 06 0f 00 40 63 3d 0b 1f 1f 0a 06 0f 00 40 63 38 13 18 0f 0a 06 0f 00 40 63 2a 0b 65 16 00 09 04 36 00 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 42 65 6c 6c 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpaceBell2 ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 0f 07 00 09 0a 40 55 04 03 19 1f 0a 06 0f 00 40 63 3d 0b 1f 1f 0a 06 0f 00 40 63 38 13 18 0f 0a 06 0f 00 40 63 2a 0b 65 16 00 09 04 36 00 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 42 65 6c 6c 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SundayBell ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 04 01 01 0b 16 04 4f 22 01 15 0e 00 01 0d 00 06 53 05 13 1c 06 04 02 07 00 06 63 16 11 18 18 03 01 0e 00 05 5e 04 12 24 07 00 04 00 62 0c 04 04 00 28 32 00 00 00 32 00 53 75 6e 64 61 79 42 65 6c 6c 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SundayBell ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 04 01 01 0b 16 01 4f 22 01 15 0e 00 01 0d 00 01 53 05 13 1c 06 04 02 07 00 01 63 16 11 18 18 03 01 0e 00 00 5e 04 12 24 07 00 04 00 62 0c 04 04 00 28 32 00 00 00 32 00 53 75 6e 64 61 79 42 65 6c 6c 63 63 63 32 32 32 00 00 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Perc. 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 12 1f 11 06 01 08 46 01 4d 3f 03 1f 15 05 01 0c 1e 01 4c 3c 03 1f 0b 06 05 08 00 01 63 0a 06 1f 0f 06 05 0a 00 01 63 04 00 3c 22 00 0a 00 52 11 02 04 00 63 32 00 00 00 32 00 53 79 2e 50 65 72 63 2e 20 31 63 63 63 32 32 32 00 30 00 20 00 00 00 28 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Perc. 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank A, voice 13 1f 08 00 01 00 00 00 32 13 05 1f 07 00 01 00 00 00 39 28 01 1a 08 00 04 00 00 02 5c 00 05 1b 09 00 05 00 00 02 5f 08 01 3c 20 00 12 00 42 18 02 04 00 63 32 00 00 00 32 00 53 79 2e 50 65 72 63 2e 20 32 63 63 63 32 32 32 00 10 00 10 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Perc. 3 ** Source: Yamaha DX11 factory preset, bank A, voice 14 1f 14 00 04 00 1e 00 4d 08 00 1f 1a 08 01 04 00 00 3c 04 06 1a 16 0c 09 0c 00 00 5b 04 10 16 15 07 07 0e 00 40 5e 04 16 3c 1f 00 00 00 52 18 02 04 00 63 32 00 32 00 32 00 53 79 2e 50 65 72 63 2e 20 33 63 63 63 30 32 32 00 00 00 60 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sy.Perc. 4 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank A, voice 15 1f 05 00 05 04 1e 04 51 20 0e 1e 0a 01 05 02 1e 04 53 1f 08 1e 1d 0a 05 0f 00 02 5c 05 0e 1f 1f 0a 05 0f 00 03 5c 04 08 04 1a 00 00 00 52 1d 02 04 00 63 32 00 32 00 32 00 53 79 2e 50 65 72 63 2e 20 34 63 63 63 32 32 32 00 30 00 46 00 01 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthBell ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 06 04 0e 00 05 54 0d 06 1f 06 06 04 0e 00 05 63 08 04 1f 06 06 04 0e 00 05 5b 08 02 1f 06 06 04 0e 00 05 63 00 03 05 23 00 00 00 03 00 04 04 00 63 01 63 00 00 32 00 53 79 6e 74 68 42 65 6c 6c 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthBell ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 06 04 0e 00 02 54 0d 06 1f 06 06 04 0e 00 00 63 08 04 1f 06 06 04 0e 00 00 5b 08 02 1f 06 06 04 0e 00 00 63 00 03 05 23 00 00 00 03 00 04 04 00 63 01 63 00 00 32 00 53 79 6e 74 68 42 65 6c 6c 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tube Bell ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 23 1f 0e 05 02 0a 27 02 50 16 06 1f 0e 05 02 0e 1a 02 43 16 00 1a 1f 07 04 0f 00 06 60 08 0e 1a 1f 07 04 0f 00 01 5e 08 08 14 1a 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 54 75 62 65 20 42 65 6c 6c 20 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tube Bell ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 05 02 0d 27 02 50 16 06 1f 0e 05 02 0e 1a 02 43 16 00 1a 1f 07 04 0f 00 06 60 08 0e 1a 1f 07 04 0f 00 01 5e 08 08 14 1a 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 54 75 62 65 20 42 65 6c 6c 20 63 63 63 32 32 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tube Bells ** Controls: MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 26 1a 04 01 01 0b 16 00 47 05 03 15 0e 00 01 0d 00 00 4c 05 16 1c 06 04 02 07 00 00 63 04 16 18 18 03 01 0e 00 00 5e 04 10 24 07 00 04 00 62 24 04 04 00 63 32 00 00 00 32 00 54 75 62 65 20 42 65 6c 6c 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Block ** Name: "Air"imba ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 20 1f 03 1a 0f 0f 00 00 3e 3d 05 1f 00 1f 03 00 00 01 40 05 00 1f 13 0b 03 0b 00 01 51 28 06 1f 11 0a 06 0f 00 00 5c 2c 06 3a 63 00 63 00 63 0e 07 04 00 63 32 00 00 00 32 00 22 41 69 72 22 69 6d 62 61 20 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: "Air"imba ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 03 1a 0f 0f 00 00 3e 3d 05 1f 00 1f 03 00 00 01 40 05 00 1f 13 0b 03 0b 00 01 51 28 06 1f 11 0d 06 0f 00 00 5c 2c 06 3a 63 00 63 00 63 0e 07 04 00 63 32 00 00 00 32 00 22 41 69 72 22 69 6d 62 61 20 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Balafon * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 17 18 0c 03 22 07 4e 18 03 1d 12 1a 08 04 23 04 4f 0e 00 1c 08 09 04 09 24 02 63 03 11 1f 0b 18 06 00 14 02 63 04 10 04 23 00 00 00 32 11 02 04 00 28 00 00 00 00 32 00 42 61 6c 61 66 6f 6e 20 20 2a 63 63 63 32 32 32 0c 08 00 09 00 06 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BamboBlock ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 22 17 19 15 08 0c 02 00 57 07 0c 1f 17 15 0a 0b 02 00 55 0c 02 1a 12 16 0a 0b 01 00 5c 04 0a 1f 10 10 09 0a 00 00 5a 04 04 34 52 00 07 00 62 18 04 04 00 28 32 00 00 00 32 00 42 61 6d 62 6f 42 6c 6f 63 6b 63 63 63 32 32 32 00 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BamboBlock ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 17 19 15 08 0c 02 00 57 07 0c 1f 17 15 0a 0b 02 00 55 0c 02 1a 12 16 0a 0b 01 00 5c 04 0a 1f 10 10 09 0d 00 00 5a 04 04 34 52 00 07 00 62 18 04 04 00 28 32 00 00 00 32 00 42 61 6d 62 6f 42 6c 6f 63 6b 63 63 63 32 32 32 00 01 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BamboChiff ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 12 0f 0f 0c 52 03 51 16 0a 12 1f 12 0f 0f 27 02 5e 22 0b 12 0f 0c 0f 0c 11 07 5b 08 13 13 1f 09 0f 0f 00 06 63 04 03 44 23 00 02 00 12 18 01 04 00 63 00 63 00 00 32 00 42 61 6d 62 6f 43 68 69 66 66 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BamboChiff ** Source: from a library of patches collected by Duane Bowker, author unknown 15 12 0f 0f 0c 52 00 51 16 0a 12 1f 12 0f 0f 27 00 5e 22 0b 12 0f 0c 0f 0c 11 00 5b 08 13 13 1f 09 0f 0f 00 00 63 04 03 44 23 00 02 00 12 18 01 04 00 63 00 63 00 00 32 00 42 61 6d 62 6f 43 68 69 66 66 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BamboMar.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 07 0f 00 02 02 4c 19 1c 12 12 06 05 09 00 02 4d 22 1a 1f 0a 06 06 0c 00 04 63 13 1c 12 0a 07 06 00 00 05 63 0a 1b 5c 2c 63 04 00 22 00 02 04 00 63 32 00 32 00 32 00 42 61 6d 62 6f 4d 61 72 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BamboMar.1 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 07 0f 00 02 00 4c 19 1c 12 12 06 05 09 00 00 4d 22 1a 1f 0a 06 06 0c 00 00 63 13 1c 12 0a 07 06 00 00 00 63 0a 1b 5c 2c 63 04 00 22 00 02 04 00 63 32 00 32 00 32 00 42 61 6d 62 6f 4d 61 72 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BamboMar.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 0d 0a 0a 0c 02 04 4d 10 1c 12 12 06 05 09 00 05 46 16 12 1b 0a 0a 06 0c 00 05 63 04 1c 12 0a 07 06 00 00 05 63 04 1b 5c 15 00 00 00 02 00 02 04 00 63 32 00 32 00 32 00 42 61 6d 62 6f 4d 61 72 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BamboMar.2 ** Source: from a library of patches collected by Duane Bowker, author unknown 16 0d 0a 0a 0c 02 00 4d 10 1c 12 12 06 05 09 00 00 46 16 12 1b 0a 0a 06 0c 00 00 63 04 1c 12 0a 07 06 00 00 00 63 04 1b 5c 15 00 00 00 02 00 02 04 00 63 32 00 32 00 32 00 42 61 6d 62 6f 4d 61 72 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BamboMar.3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 0d 0a 0a 0c 02 01 48 19 1c 12 12 06 05 09 00 04 46 16 1a 1b 0a 0a 06 0c 00 05 63 04 1c 12 0a 07 06 00 00 05 63 04 1b 7c 15 00 00 00 02 00 02 04 00 63 32 00 32 00 32 00 42 61 6d 62 6f 4d 61 72 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BamboMar.3 ** Source: from a library of patches collected by Duane Bowker, author unknown 16 0d 0a 0a 0c 02 00 48 19 1c 12 12 06 05 09 00 00 46 16 1a 1b 0a 0a 06 0c 00 00 63 04 1c 12 0a 07 06 00 00 00 63 04 1b 7c 15 00 00 00 02 00 02 04 00 63 32 00 32 00 32 00 42 61 6d 62 6f 4d 61 72 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bamboo * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 06 1f 03 00 46 02 3c 00 10 1c 12 0f 05 04 00 00 5b 01 13 17 1b 0f 05 04 00 03 50 04 13 1a 0a 1f 05 00 00 02 63 00 0d 2a 17 0c 31 00 32 0c 02 04 00 28 63 00 00 00 32 00 42 61 6d 62 6f 6f 20 20 20 2a 63 63 63 32 32 32 00 10 00 14 00 71 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Castanet ** Source: Yamaha DX11 factory preset, bank D, voice 13 19 16 1f 03 00 00 00 52 20 06 18 16 1f 03 00 00 00 52 27 00 1f 15 00 0c 00 00 00 63 00 00 1f 15 15 0c 00 00 00 63 00 06 1c 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 43 61 73 74 61 6e 65 74 20 20 63 63 63 32 32 32 0c 18 0c 00 08 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Claves * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 00 00 04 03 1f 1d 0d 07 08 00 01 3d 00 03 1f 16 0f 0b 07 00 01 46 0d 03 1f 1c 11 08 0f 00 01 63 04 03 03 23 00 00 00 32 24 02 04 00 28 63 00 00 00 32 00 43 6c 61 76 65 73 20 20 20 2a 63 63 63 32 32 32 00 00 0e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MarimbLead ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 18 10 01 0c 63 44 62 22 0b 1f 18 10 01 0c 63 45 59 04 0b 1f 06 06 07 0c 0e 46 5e 04 0b 1f 06 06 07 0c 0e 45 5e 04 0b 7c 32 23 00 52 40 18 04 04 00 63 01 63 00 00 32 00 4d 61 72 69 6d 62 4c 65 61 64 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MarimbLead ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 18 10 01 0c 63 42 62 22 0b 1f 18 10 01 0c 63 41 59 04 0b 1f 06 06 07 0c 0e 40 5e 04 0b 1f 06 06 07 0c 0e 40 5e 04 0b 7c 32 23 00 52 40 18 04 04 00 63 01 63 00 00 32 00 4d 61 72 69 6d 62 4c 65 61 64 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 03 12 09 0f 00 03 47 10 06 1f 00 15 05 0f 00 00 3f 08 00 1f 0f 00 05 00 00 01 60 04 08 1f 0d 00 06 00 00 05 63 04 0e 3c 23 00 00 00 62 18 04 04 00 00 32 00 00 00 32 00 4d 61 72 69 6d 62 61 20 20 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 19 1f 16 0e 07 05 15 06 52 0f 0b 1a 18 0c 07 05 0c 02 52 0a 0b 17 1f 0a 07 0f 00 02 62 08 0b 16 1f 0a 05 0f 00 01 62 00 0b 04 23 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 4d 61 72 69 6d 62 61 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 16 0e 07 05 15 06 52 0f 0b 1a 18 0c 07 05 0c 02 52 0a 0b 17 1f 0d 07 0f 00 02 62 08 0b 16 1f 0a 05 0f 00 01 62 00 0b 04 23 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 4d 61 72 69 6d 62 61 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 0e 07 0f 48 04 49 17 1b 1f 0f 10 06 08 63 05 4f 1a 13 1f 1f 12 07 0f 3b 05 4b 34 1b 1f 1f 0a 06 0f 00 04 63 0d 13 02 1e 09 00 00 52 00 04 04 00 63 28 00 00 00 32 00 4d 61 72 69 6d 62 61 20 20 20 63 63 63 32 32 32 00 0b 00 03 08 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1b 00 06 00 00 05 63 28 03 1f 12 00 06 00 00 05 45 13 03 1f 0d 00 06 00 00 05 44 16 03 1f 0c 00 06 00 00 06 63 04 03 02 23 00 00 00 62 0c 07 04 00 63 63 00 00 00 32 00 4d 61 72 69 6d 62 61 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1b 00 06 00 00 03 63 28 03 1f 12 00 06 00 00 03 45 13 03 1f 0d 00 06 00 00 02 44 16 03 1f 0c 00 06 00 00 00 63 04 03 02 23 00 00 00 62 0c 07 04 00 63 63 00 00 00 32 00 4d 61 72 69 6d 62 61 2e 2e 31 63 63 63 32 32 32 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 19 00 07 00 00 05 5b 1f 08 1f 11 00 07 00 00 04 50 13 0e 1f 0a 00 07 00 11 06 63 04 0e 1f 0a 00 07 00 11 05 63 04 00 2c 15 00 00 00 52 0c 02 05 00 63 32 00 32 00 32 00 4d 61 72 69 6d 62 61 2e 2e 32 63 63 63 32 32 32 00 30 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 19 00 07 00 00 03 5b 1f 08 1f 11 00 07 00 00 02 50 13 0e 1f 0a 00 07 00 11 00 63 04 0e 1f 0a 00 07 00 11 00 63 04 00 2c 15 00 00 00 52 0c 02 05 00 63 32 00 32 00 32 00 4d 61 72 69 6d 62 61 2e 2e 32 63 63 63 32 32 32 00 30 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 19 00 07 00 00 04 4c 19 08 1f 11 00 07 00 00 03 49 16 0e 1f 0a 00 07 00 11 06 63 04 0e 1f 0a 00 07 00 11 06 62 04 00 2c 15 00 00 00 52 00 02 05 00 63 32 00 32 00 32 00 4d 61 72 69 6d 62 61 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..3 ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 19 00 07 00 00 00 4c 19 08 1f 11 00 07 00 00 00 49 16 0e 1f 0a 00 07 00 11 00 63 04 0e 1f 0a 00 07 00 11 00 62 04 00 2c 15 00 00 00 52 00 02 05 00 63 32 00 32 00 32 00 4d 61 72 69 6d 62 61 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 0d 05 03 00 00 04 58 0a 18 1f 11 00 07 00 00 04 50 10 0e 1f 0a 00 07 00 11 05 63 04 0e 1f 0a 00 07 00 11 06 62 04 00 2c 15 00 00 00 52 0c 02 05 00 63 32 00 32 00 32 00 4d 61 72 69 6d 62 61 2e 2e 34 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 0d 05 03 00 00 02 58 0a 18 1f 11 00 07 00 00 01 50 10 0e 1f 0a 00 07 00 11 00 63 04 0e 1f 0a 00 07 00 11 00 62 04 00 2c 15 00 00 00 52 0c 02 05 00 63 32 00 32 00 32 00 4d 61 72 69 6d 62 61 2e 2e 34 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1b 00 06 00 00 05 5d 28 03 1f 12 00 06 00 00 05 4d 00 03 1f 0d 00 06 00 00 07 46 10 03 1f 0c 00 06 00 00 07 61 00 03 02 23 00 00 00 62 0c 07 04 00 63 63 00 00 00 32 00 4d 61 72 69 6d 62 61 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1b 00 06 00 00 02 5d 28 03 1f 12 00 06 00 00 02 4d 00 03 1f 0d 00 06 00 00 04 46 10 03 1f 0c 00 06 00 00 01 61 00 03 02 23 00 00 00 62 0c 07 04 00 63 63 00 00 00 32 00 4d 61 72 69 6d 62 61 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 02 06 04 00 05 62 19 03 1f 12 00 06 00 00 05 4b 04 03 1f 0d 00 06 00 00 06 44 16 03 1f 0c 00 06 00 00 06 63 04 03 02 23 00 00 00 62 0c 07 04 00 63 63 00 00 00 32 00 4d 61 72 69 6d 62 61 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..6 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 02 06 04 00 02 62 19 03 1f 12 00 06 00 00 01 4b 04 03 1f 0d 00 06 00 00 01 44 16 03 1f 0c 00 06 00 00 00 63 04 03 02 23 00 00 00 62 0c 07 04 00 63 63 00 00 00 32 00 4d 61 72 69 6d 62 61 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 02 06 03 00 05 63 19 03 1f 12 00 06 00 00 03 4f 0a 03 1f 0d 00 06 00 00 06 42 04 03 1f 0c 00 06 00 00 05 63 08 03 03 23 00 00 00 62 0c 07 04 00 63 63 00 00 00 32 00 4d 61 72 69 6d 62 61 2e 2e 37 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Marimba..7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 02 06 03 00 02 63 19 03 1f 12 00 06 00 00 01 4f 0a 03 1f 0d 00 06 00 00 01 42 04 03 1f 0c 00 06 00 00 00 63 08 03 03 23 00 00 00 62 0c 07 04 00 63 63 00 00 00 32 00 4d 61 72 69 6d 62 61 2e 2e 37 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Block ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 0c 0f 0c 00 41 51 0a 1b 1f 10 09 07 0d 2e 42 40 04 13 1f 10 09 04 0d 00 44 63 04 1b 1f 10 12 05 0d 01 46 62 04 13 7e 1e 28 24 0f 27 18 07 04 00 63 32 00 00 00 32 00 4f 72 6e 74 20 42 6c 6f 63 6b 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Block ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 0c 0f 0c 00 40 51 0a 1b 1f 10 09 07 0d 2e 40 40 04 13 1f 10 09 04 0d 00 40 63 04 1b 1f 10 12 05 0d 01 40 62 04 13 7e 1e 28 24 0f 27 18 07 04 00 63 32 00 00 00 32 00 4f 72 6e 74 20 42 6c 6f 63 6b 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynGameran ** Source: Yamaha DX11 factory preset, bank D, voice 17 0a 14 00 01 0b 00 40 2d 11 03 14 03 00 0f 0e 2e 00 41 27 0c 1f 1b 11 05 0a 00 40 3f 0a 0a 1f 0f 00 0b 00 01 00 5a 10 0b 38 3a 00 63 50 5f 0b 05 04 00 63 00 00 00 00 32 00 53 79 6e 47 61 6d 65 72 61 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynGameran ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 14 00 01 0b 00 40 2d 11 03 14 03 00 0f 0e 2e 00 41 27 0c 1f 1b 11 05 0a 00 40 3f 0d 0a 1f 0f 00 0b 00 01 00 5a 10 0b 38 3a 00 63 50 5f 0b 05 04 00 63 00 00 00 00 32 00 53 79 6e 47 61 6d 65 72 61 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthMarib ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 18 10 01 0c 63 43 5d 38 0b 0a 01 07 07 0f 00 45 52 00 03 1f 09 0f 07 0c 0e 45 63 04 0b 0f 02 07 07 0e 00 46 63 04 03 7c 32 23 00 52 40 18 04 04 00 63 01 63 00 00 32 00 53 79 6e 74 68 4d 61 72 69 62 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthMarib ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 18 10 01 0c 63 41 5d 38 0b 0a 01 07 07 0f 00 41 52 00 03 1f 09 0f 07 0c 0e 40 63 04 0b 0f 02 07 07 0e 00 41 63 04 03 7c 32 23 00 52 40 18 04 04 00 63 01 63 00 00 32 00 53 79 6e 74 68 4d 61 72 69 62 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tablas * ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 1c 17 09 0a 00 03 63 0d 0b 19 1f 1b 09 06 00 00 63 00 0b 1f 05 00 09 0f 00 00 63 3c 0b 13 10 11 06 0a 00 03 63 00 0b 02 18 00 26 00 32 18 0c 04 00 28 63 00 00 00 13 00 54 61 62 6c 61 73 20 20 20 2a 63 63 63 32 32 32 00 06 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wood Block ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 12 1f 18 01 08 00 00 02 55 07 0b 1f 18 12 0a 0c 00 02 55 0a 0b 1f 15 13 0a 0a 00 01 63 04 0b 1f 12 13 09 00 00 01 63 04 0b 2c 52 00 07 00 62 24 04 04 00 28 32 00 00 00 32 00 57 6f 6f 64 20 42 6c 6f 63 6b 63 63 63 32 32 32 00 11 00 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wood Block ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 18 01 08 00 00 02 55 07 0b 1f 18 12 0a 0c 00 02 55 0a 0b 1f 15 13 0a 0d 00 01 63 04 0b 1f 12 13 09 00 00 01 63 04 0b 2c 52 00 07 00 62 24 04 04 00 28 32 00 00 00 32 00 57 6f 6f 64 20 42 6c 6f 63 6b 63 63 63 32 32 32 00 11 00 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WoodBlock* ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 17 00 08 00 00 00 4b 0b 0b 1f 1f 16 07 0f 20 03 49 0f 0b 1a 0f 00 07 00 00 02 63 03 0b 1f 1f 0c 06 0f 00 01 63 05 13 44 0e 00 63 00 42 11 02 04 00 28 63 00 00 00 32 00 57 6f 6f 64 42 6c 6f 63 6b 2a 63 63 63 32 32 32 00 0a 00 49 00 06 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WoodBlock1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 0f 0c 0a 00 00 4d 0c 13 1f 10 12 09 0d 00 00 45 04 0b 1f 16 10 08 0c 00 05 63 04 13 1f 10 12 09 0d 00 05 63 04 0b 44 30 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 57 6f 6f 64 42 6c 6f 63 6b 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WoodBlock1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 0f 0c 0a 00 00 4d 0c 13 1f 10 12 09 0d 00 00 45 04 0b 1f 16 10 08 0c 00 00 63 04 13 1f 10 12 09 0d 00 00 63 04 0b 44 30 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 57 6f 6f 64 42 6c 6f 63 6b 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WoodBlock2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 0f 0c 0a 00 01 4d 0c 13 1f 10 12 09 0d 00 02 37 04 0b 1f 16 10 08 0c 00 04 42 04 13 1f 10 12 09 0d 00 05 63 04 0b 42 30 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 57 6f 6f 64 42 6c 6f 63 6b 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WoodBlock2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 0f 0c 0a 00 00 4d 0c 13 1f 10 12 09 0d 00 00 37 04 0b 1f 16 10 08 0c 00 00 42 04 13 1f 10 12 09 0d 00 00 63 04 0b 42 30 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 57 6f 6f 64 42 6c 6f 63 6b 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WoodBlock3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 0f 0c 0a 00 00 44 0c 13 1f 10 12 09 0d 00 01 4b 05 0b 1f 16 10 08 0c 00 05 48 07 13 1f 10 12 09 0d 00 05 63 04 0b 42 30 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 57 6f 6f 64 42 6c 6f 63 6b 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WoodBlock3 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 0f 0c 0a 00 00 44 0c 13 1f 10 12 09 0d 00 00 4b 05 0b 1f 16 10 08 0c 00 00 48 07 13 1f 10 12 09 0d 00 00 63 04 0b 42 30 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 57 6f 6f 64 42 6c 6f 63 6b 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 24 * SUBCATEGORY Chime ** Name: BriteCelst ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 31 1f 0c 00 06 01 0a 04 50 20 0e 1f 0c 00 06 01 0a 04 50 1f 08 1f 0b 09 05 0e 00 02 63 05 0e 1f 0b 09 05 0e 00 03 63 04 08 04 1a 00 00 00 52 1d 02 04 00 63 32 00 32 00 32 00 42 72 69 74 65 43 65 6c 73 74 63 63 63 32 32 32 00 20 00 26 00 01 00 08 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Celeste ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank A, voice 30 19 1c 00 06 01 00 04 52 07 08 1f 10 00 06 01 00 04 46 16 0e 1f 0a 00 06 01 00 02 63 04 0e 1f 0d 00 04 01 00 03 63 04 00 04 19 00 00 00 52 24 02 04 00 63 32 00 32 00 32 00 43 65 6c 65 73 74 65 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Celeste..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 1c 00 06 01 00 03 51 2e 10 1f 10 00 06 01 00 05 3c 19 0e 1f 0a 00 06 01 00 05 63 04 0e 1f 0d 00 04 01 00 05 63 04 00 04 19 00 00 00 52 24 00 04 00 63 32 00 32 00 32 00 43 65 6c 65 73 74 65 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Celeste..1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 1c 00 06 01 00 01 51 2e 10 1f 10 00 06 01 00 01 3c 19 0e 1f 0a 00 06 01 00 01 63 04 0e 1f 0d 00 04 01 00 01 63 04 00 04 19 00 00 00 52 24 00 04 00 63 32 00 32 00 32 00 43 65 6c 65 73 74 65 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Celeste..2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 15 12 06 00 00 01 3b 1f 10 1f 10 00 06 01 00 05 3b 08 0e 1f 0a 00 06 01 00 04 63 0d 0e 1f 0d 00 04 01 00 06 63 04 00 04 19 00 00 00 52 24 00 04 00 63 32 00 32 00 32 00 43 65 6c 65 73 74 65 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Celeste..2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 15 12 06 00 00 01 3b 1f 10 1f 10 00 06 01 00 02 3b 08 0e 1f 0a 00 06 01 00 01 63 0d 0e 1f 0d 00 04 01 00 01 63 04 00 04 19 00 00 00 52 24 00 04 00 63 32 00 32 00 32 00 43 65 6c 65 73 74 65 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chimes * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 07 02 0d 12 02 5c 10 09 1f 1f 09 02 0d 11 01 56 11 09 19 1f 07 03 0d 00 42 53 08 0c 17 1f 07 03 0d 00 40 63 08 0d 6c 29 00 00 00 0b 24 02 04 00 28 00 36 00 00 32 00 43 68 69 6d 65 73 20 20 20 2a 63 63 63 32 32 32 00 00 00 30 00 08 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chiming ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 02 01 00 00 43 3d 2d 03 1f 05 07 02 0d 00 43 4d 16 06 1f 18 00 02 00 00 07 4c 3d 03 1f 08 0a 05 0d 00 04 63 04 0b 02 19 02 01 16 5a 11 02 04 00 63 28 00 00 00 32 00 43 68 69 6d 69 6e 67 20 20 20 63 63 63 32 32 32 00 60 10 07 00 40 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Glocken ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank B, voice 22 1f 1d 00 06 00 00 07 63 0a 03 1f 0a 00 06 00 0a 07 63 1f 03 1f 0c 00 05 00 0f 07 63 08 03 1f 0e 01 06 00 00 07 5b 2d 03 3d 21 00 0a 00 32 18 02 04 00 28 32 00 00 00 32 00 47 6c 6f 63 6b 65 6e 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Glocken ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1d 00 06 00 00 07 63 0a 03 1f 0d 00 06 00 0a 07 63 1f 03 1f 0c 00 05 00 0f 07 63 08 03 1f 0e 01 06 00 00 07 5b 2d 03 3d 21 00 0d 00 32 18 02 04 00 28 32 00 00 00 32 00 47 6c 6f 63 6b 65 6e 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Glocken..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 04 05 0a 23 02 4d 2a 06 1f 0e 05 02 09 00 04 3d 04 00 18 11 08 05 0f 0a 05 63 04 0e 1f 11 08 05 0f 05 43 63 0d 00 26 23 00 00 00 3e 18 05 04 00 63 63 28 00 00 32 00 47 6c 6f 63 6b 65 6e 2e 2e 31 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Glocken..1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 04 05 0a 23 02 4d 2a 06 1f 0e 05 02 09 00 01 3d 04 00 18 11 08 05 0f 0a 01 63 04 0e 1f 11 08 05 0f 05 41 63 0d 00 26 23 00 00 00 3e 18 05 04 00 63 63 28 00 00 32 00 47 6c 6f 63 6b 65 6e 2e 2e 31 63 63 63 32 32 32 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Glocken..2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 04 05 0a 23 02 4c 2d 0e 1f 0e 05 02 09 00 06 39 19 00 1e 12 0a 05 0c 0a 05 63 04 0e 1f 11 08 05 0f 05 46 63 04 00 24 23 00 00 00 3e 24 05 04 00 63 63 28 00 00 32 00 47 6c 6f 63 6b 65 6e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Glocken..2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 04 05 0a 23 02 4c 2d 0e 1f 0e 05 02 09 00 01 39 19 00 1e 12 0a 05 0c 0a 01 63 04 0e 1f 11 08 05 0f 05 41 63 04 00 24 23 00 00 00 3e 24 05 04 00 63 63 28 00 00 32 00 47 6c 6f 63 6b 65 6e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GoodVibes* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 0f 07 0d 00 47 3a 2a 16 19 1a 0c 07 0d 00 44 41 0a 10 14 1a 07 07 0d 00 41 63 04 08 14 1a 07 07 0d 00 41 63 04 0e 04 1a 00 08 00 0e 18 02 04 00 28 17 3b 00 00 32 00 47 6f 6f 64 56 69 62 65 73 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GreatVibes ** Controls: KV, MW (LFO amplitude) ** Source: Yamaha TX81Z factory preset, bank C, voice 28 1f 14 07 07 0d 00 43 63 04 06 1f 10 15 06 00 00 43 63 2a 0e 1f 14 0b 07 0d 00 43 63 0d 06 1f 14 07 07 0d 00 43 63 04 00 07 16 00 00 2a 66 0c 02 04 00 63 08 32 00 00 32 00 47 72 65 61 74 56 69 62 65 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mod Chime ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 08 0f 00 00 45 5b 3f 03 0f 08 0d 06 09 00 44 63 0d 01 1f 1f 0b 06 0f 00 46 63 3e 03 0e 08 0d 06 09 00 45 63 08 05 5e 19 13 07 5c 36 00 0c 04 00 63 00 00 00 00 32 00 4d 6f 64 20 43 68 69 6d 65 20 63 63 63 32 32 32 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mod Chime ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0f 08 0f 00 00 42 5b 3f 03 0f 08 0d 06 09 00 42 63 0d 01 1f 1f 0b 06 0f 00 42 63 3e 03 0e 08 0d 06 09 00 40 63 08 05 5e 19 13 07 5c 36 00 0c 04 00 63 00 00 00 00 32 00 4d 6f 64 20 43 68 69 6d 65 20 63 63 63 32 32 32 00 20 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthChime ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 00 1f 10 04 0d 0a 44 63 0d 0d 0d 0a 1f 03 01 0a 45 4c 08 09 1f 09 08 04 02 0a 45 60 1f 0c 10 05 09 06 03 0a 45 63 08 0c 4c 23 00 03 0a 4a 0c 04 04 00 63 32 00 00 00 32 00 53 79 6e 74 68 43 68 69 6d 65 63 63 63 32 32 32 00 20 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthChime ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 00 1f 10 04 0d 0a 41 63 0d 0d 0d 0a 1f 03 01 0a 42 4c 08 09 1f 09 08 04 02 0a 40 60 1f 0c 10 05 09 06 03 0a 40 63 08 0c 4c 23 00 03 0a 4a 0c 04 04 00 63 32 00 00 00 32 00 53 79 6e 74 68 43 68 69 6d 65 63 63 63 32 32 32 00 20 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Triangle.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 0c 04 0e 00 05 39 22 13 1f 1f 0c 04 0e 00 03 40 08 03 1f 1f 0c 04 0f 00 05 63 2f 03 1f 1f 0c 04 0f 00 05 63 08 03 45 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 54 72 69 61 6e 67 6c 65 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Triangle.1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 0c 04 0e 00 00 39 22 13 1f 1f 0c 04 0e 00 00 40 08 03 1f 1f 0c 04 0f 00 00 63 2f 03 1f 1f 0c 04 0f 00 00 63 08 03 45 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 54 72 69 61 6e 67 6c 65 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Triangle.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 0c 04 0e 00 03 30 22 13 1f 0d 1f 04 00 00 04 61 13 03 1f 1f 0c 04 0f 00 06 63 2a 03 1f 1f 0c 04 0f 00 06 63 08 03 46 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 54 72 69 61 6e 67 6c 65 2e 32 63 63 63 32 32 32 0a 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Triangle.2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 0c 04 0e 00 00 30 22 13 1f 0d 1f 04 00 00 00 61 13 03 1f 1f 0c 04 0f 00 00 63 2a 03 1f 1f 0c 04 0f 00 00 63 08 03 46 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 54 72 69 61 6e 67 6c 65 2e 32 63 63 63 32 32 32 0a 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vib Lead.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 03 07 09 0e 00 04 51 08 1b 16 0a 07 09 0f 00 04 63 04 1b 17 0a 07 09 0f 00 07 63 08 1b 18 16 07 09 0f 00 05 63 04 1b 2d 27 00 00 00 42 18 04 04 00 28 32 00 00 00 32 00 56 69 62 20 4c 65 61 64 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vib Lead.1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 03 07 09 0e 00 02 51 08 1b 16 0a 07 09 0f 00 01 63 04 1b 17 0a 07 09 0f 00 00 63 08 1b 18 16 07 09 0f 00 00 63 04 1b 2d 27 00 00 00 42 18 04 04 00 28 32 00 00 00 32 00 56 69 62 20 4c 65 61 64 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vib Lead.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 03 07 09 0e 00 04 51 08 0b 1f 0f 0a 0c 0b 00 05 3e 2a 13 17 0a 07 09 0f 00 05 63 04 1b 1f 07 07 0a 0e 00 06 63 04 0b 2c 27 00 00 00 42 18 04 04 00 28 32 00 00 00 32 00 56 69 62 20 4c 65 61 64 2e 32 63 63 63 32 32 32 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vib Lead.2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 15 03 07 09 0e 00 01 51 08 0b 1f 0f 0a 0c 0b 00 01 3e 2a 13 17 0a 07 09 0f 00 00 63 04 1b 1f 07 07 0a 0e 00 00 63 04 0b 2c 27 00 00 00 42 18 04 04 00 28 32 00 00 00 32 00 56 69 62 20 4c 65 61 64 2e 32 63 63 63 32 32 32 00 20 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe. ** Controls: KV, MW (LFO amplitude) ** Source: Yamaha DX11 factory preset, bank B, voice 21 1f 0f 0e 0a 0a 00 02 36 24 00 1f 0e 08 06 09 00 02 36 10 06 1f 11 05 04 0f 00 02 61 04 16 1f 11 05 04 0f 00 42 61 04 10 04 1f 00 00 1f 3e 18 02 04 00 63 32 1e 00 00 32 00 56 69 62 65 2e 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 0d 0a 0a 0d 00 03 3b 19 12 1b 16 00 0c 07 00 05 57 16 13 1f 0a 0a 0d 0c 00 02 60 04 0b 1f 09 07 06 0c 00 05 5e 04 0b 7c 15 00 00 00 22 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 0d 0a 0a 0d 00 01 3b 19 12 1b 16 00 0c 07 00 01 57 16 13 1f 0a 0a 0d 0c 00 00 60 04 0b 1f 09 07 06 0c 00 00 5e 04 0b 7c 15 00 00 00 22 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....2 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 14 07 07 0d 00 44 63 08 06 1f 10 15 06 00 00 45 63 24 0e 1f 14 0b 07 0d 00 44 63 0d 06 1f 14 07 07 0d 00 47 63 04 00 07 16 00 00 2a 66 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....2 ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 14 07 07 0d 00 40 63 08 06 1f 10 15 06 00 00 40 63 24 0e 1f 14 0b 07 0d 00 40 63 0d 06 1f 14 07 07 0d 00 40 63 04 00 07 16 00 00 2a 66 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....3 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 14 07 07 0d 00 42 63 0d 16 1f 10 15 06 00 00 44 63 16 0e 1f 14 0b 07 0d 00 45 63 04 06 1f 14 07 07 0d 00 47 63 04 00 07 16 00 00 2a 66 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....3 ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 14 07 07 0d 00 40 63 0d 16 1f 10 15 06 00 00 40 63 16 0e 1f 14 0b 07 0d 00 40 63 04 06 1f 14 07 07 0d 00 40 63 04 00 07 16 00 00 2a 66 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 06 1f 09 04 00 43 3a 24 16 1b 09 03 06 0d 00 45 63 0d 0e 1b 09 03 06 0d 00 44 63 04 0e 1f 15 06 08 0c 00 47 63 04 00 6d 21 00 00 63 02 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 06 1f 09 04 00 41 3a 24 16 1b 09 03 06 0d 00 42 63 0d 0e 1b 09 03 06 0d 00 41 63 04 0e 1f 15 06 08 0c 00 40 63 04 00 6d 21 00 00 63 02 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 06 1f 09 04 00 43 39 10 1e 1b 09 03 06 0d 00 44 5b 16 1e 1b 09 03 06 0d 00 44 63 04 1e 1f 15 06 08 0c 00 46 63 04 08 6d 21 00 00 63 02 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....5 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 12 06 1f 09 04 00 41 39 10 1e 1b 09 03 06 0d 00 40 5b 16 1e 1b 09 03 06 0d 00 40 63 04 1e 1f 15 06 08 0c 00 40 63 04 08 6d 21 00 00 63 02 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....6 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 14 07 07 0d 00 44 63 0d 06 1f 10 15 06 00 00 45 63 24 0e 1f 14 0b 07 0d 00 45 63 0d 06 1f 14 07 07 0d 00 47 63 08 00 07 16 00 00 2a 66 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 36 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibe.....6 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 14 07 07 0d 00 41 63 0d 06 1f 10 15 06 00 00 41 63 24 0e 1f 14 0b 07 0d 00 41 63 0d 06 1f 14 07 07 0d 00 41 63 08 00 07 16 00 00 2a 66 0c 02 04 00 63 08 32 00 00 32 00 56 69 62 65 2e 2e 2e 2e 2e 36 63 63 63 32 32 32 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Vibesyn * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 0a 1f 05 00 15 05 54 0a 0b 1f 14 12 01 00 00 07 59 13 0c 1c 1b 0b 06 0e 0b 00 63 04 16 1b 1a 0a 05 0e 0b 03 63 04 10 3c 23 00 00 00 42 0c 02 04 00 28 63 00 00 00 32 00 56 69 62 65 73 79 6e 20 20 2a 63 63 63 32 32 32 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WindChime1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 09 03 0b 00 43 52 2d 03 06 0f 09 02 0b 00 44 63 22 03 1f 12 09 02 0b 00 44 63 22 03 1f 12 09 04 0b 00 47 62 22 03 06 2d 00 05 06 37 18 04 04 00 28 32 00 00 00 32 00 57 69 6e 64 43 68 69 6d 65 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WindChime1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 09 03 0b 00 40 52 2d 03 06 0f 09 02 0b 00 40 63 22 03 1f 12 09 02 0b 00 40 63 22 03 1f 12 09 04 0b 00 40 62 22 03 06 2d 00 05 06 37 18 04 04 00 28 32 00 00 00 32 00 57 69 6e 64 43 68 69 6d 65 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WindChime2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0c 00 03 0e 00 02 50 19 03 1c 12 00 02 0e 2e 03 50 27 0b 0e 10 00 02 0a 00 05 59 09 0a 11 0d 00 03 0c 01 06 62 22 0b 0e 4e 00 38 00 53 0c 07 04 00 63 32 00 00 00 32 00 57 69 6e 64 43 68 69 6d 65 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WindChime2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0c 00 03 0e 00 00 50 19 03 1c 12 00 02 0e 2e 00 50 27 0b 0e 10 00 02 0a 00 00 59 09 0a 11 0d 00 03 0c 01 00 62 22 0b 0e 4e 00 38 00 53 0c 07 04 00 63 32 00 00 00 32 00 57 69 6e 64 43 68 69 6d 65 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone* ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0e 0e 06 0e 11 01 4b 10 16 1f 12 0d 05 0e 00 41 5a 04 13 19 10 0e 05 0d 20 42 4b 08 16 1f 11 0c 05 0e 00 41 63 04 10 75 29 2c 00 36 09 18 02 04 00 28 00 31 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank C, voice 27 1f 16 00 09 05 00 01 5a 08 03 1f 17 10 05 05 00 01 56 10 13 1f 1f 12 07 0f 00 01 63 04 03 1f 1f 0f 07 0f 00 02 63 04 03 2a 23 00 00 00 62 18 04 04 00 63 32 00 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 20 63 63 63 32 32 32 00 00 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 20 1f 1f 16 09 0f 26 02 41 1f 13 1f 1f 16 09 0f 00 03 50 19 0b 1f 1f 0f 07 0f 00 01 62 04 0b 1f 1f 0f 07 0f 00 02 62 0a 13 04 23 00 00 00 62 24 02 04 00 28 32 00 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 16 09 0f 26 02 41 1f 13 1f 1f 16 09 0f 00 03 50 19 0b 1f 1f 0f 07 0f 00 01 62 04 0b 1f 1f 0f 07 0f 00 02 62 0d 13 04 23 00 00 00 62 24 02 04 00 28 32 00 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 07 07 0e 00 04 53 13 1e 1b 10 06 06 0e 00 05 56 04 16 1f 0d 07 07 0e 00 07 63 0c 1b 1b 10 06 06 0e 00 05 63 00 0b 75 23 00 00 00 32 24 02 04 00 63 11 06 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone1 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0d 07 07 0e 00 01 53 13 1e 1b 10 06 06 0e 00 02 56 04 16 1f 0d 07 07 0e 00 04 63 0c 1b 1b 10 06 06 0e 00 00 63 00 0b 75 23 00 00 00 32 24 02 04 00 63 11 06 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 31 63 63 63 32 32 32 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 07 07 0d 14 03 4f 0d 12 1f 12 07 04 0d 19 05 5d 08 1e 1f 0c 06 07 0d 00 07 63 04 09 1f 0a 06 07 0d 00 05 63 04 0b 74 23 00 00 00 32 24 02 04 00 63 11 06 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone2 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 07 07 0d 14 01 4f 0d 12 1f 12 07 04 0d 19 03 5d 08 1e 1f 0c 06 07 0d 00 04 63 04 09 1f 0a 06 07 0d 00 00 63 04 0b 74 23 00 00 00 32 24 02 04 00 63 11 06 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 0d 05 05 0c 14 03 4f 0d 12 1d 16 00 0c 07 19 03 5b 2a 1e 1f 0c 06 07 0d 00 07 63 04 11 1f 09 07 06 0c 00 06 63 04 0b 74 23 00 00 00 32 24 02 04 00 63 11 06 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 33 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone3 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 0d 05 05 0c 14 01 4f 0d 12 1d 16 00 0c 07 19 01 5b 2a 1e 1f 0c 06 07 0d 00 04 63 04 11 1f 09 07 06 0c 00 00 63 04 0b 74 23 00 00 00 32 24 02 04 00 63 11 06 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 33 63 63 63 32 32 32 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 07 04 0e 00 04 40 08 1e 1b 10 06 05 0e 00 06 54 08 0e 1f 0d 07 05 0e 00 07 63 0c 1b 1b 10 06 05 0e 00 06 63 00 0b 75 23 00 00 00 32 24 02 04 00 63 11 06 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 34 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone4 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0d 07 04 0e 00 01 40 08 1e 1b 10 06 05 0e 00 03 54 08 0e 1f 0d 07 05 0e 00 04 63 0c 1b 1b 10 06 05 0e 00 00 63 00 0b 75 23 00 00 00 32 24 02 04 00 63 11 06 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 34 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 04 05 0a 00 04 41 28 01 1f 0e 05 05 09 00 04 43 13 0c 1f 1f 00 08 0f 00 05 3e 00 05 1f 11 08 05 0f 00 05 63 04 0a 4d 23 00 19 00 32 24 02 04 00 63 32 00 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 35 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 04 05 0a 00 01 41 28 01 1f 0e 05 05 09 00 01 43 13 0c 1f 1f 00 08 0f 00 01 3e 00 05 1f 11 08 05 0f 00 00 63 04 0a 4d 23 00 19 00 32 24 02 04 00 63 32 00 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 35 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0e 09 07 0a 00 03 52 30 19 1f 0e 05 06 09 00 04 42 08 0c 1f 1f 00 09 0f 00 04 3b 0d 0d 1f 11 08 06 0f 00 04 63 04 0a 4d 23 00 19 00 32 24 02 04 00 63 32 00 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone6 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 09 07 0a 00 01 52 30 19 1f 0e 05 06 09 00 01 42 08 0c 1f 1f 00 09 0f 00 00 3b 0d 0d 1f 11 08 06 0f 00 00 63 04 0a 4d 23 00 19 00 32 24 02 04 00 63 32 00 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0e 09 07 0a 00 05 55 13 19 12 12 05 06 09 00 06 5b 04 1c 1f 1f 00 09 0f 00 04 5f 04 1d 1f 11 08 06 0f 00 07 63 00 0a 4c 23 00 00 00 32 24 02 04 00 63 32 00 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone7 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0e 09 07 0a 00 01 55 13 19 12 12 05 06 09 00 01 5b 04 1c 1f 1f 00 09 0f 00 01 5f 04 1d 1f 11 08 06 0f 00 00 63 00 0a 4c 23 00 00 00 32 24 02 04 00 63 32 00 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 16 0d 05 05 0d 00 04 4f 13 12 1f 15 00 0c 07 00 05 63 2a 0e 1f 0a 05 06 0c 00 07 63 04 0b 1c 0f 06 06 0c 00 06 63 04 13 74 23 00 00 00 32 24 02 04 00 63 11 06 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Xylophone8 ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 16 0d 05 05 0d 00 01 4f 13 12 1f 15 00 0c 07 00 03 63 2a 0e 1f 0a 05 06 0c 00 04 63 04 0b 1c 0f 06 06 0c 00 00 63 04 13 74 23 00 00 00 32 24 02 04 00 63 11 06 00 00 32 00 58 79 6c 6f 70 68 6f 6e 65 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Cymbal ** Name: Chin.Cymb* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 01 00 0b 00 44 04 00 1e 00 01 01 00 1f 41 4e 0a 00 1f 00 00 01 00 00 01 56 14 06 1d 0b 1f 04 00 0a 04 63 00 06 7b 1f 0d 0e 1e 0e 18 0c 04 00 63 32 00 32 00 32 00 43 68 69 6e 2e 43 79 6d 62 2a 63 63 63 32 32 32 01 73 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cymbal...1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0e 00 40 59 35 03 19 16 00 01 0b 00 40 4e 03 0b 15 1c 06 01 0b 00 40 57 3b 13 1f 1f 05 02 08 00 45 63 1b 13 31 00 02 00 03 52 12 05 04 00 63 32 00 00 00 32 00 43 79 6d 62 61 6c 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cymbal...1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0e 00 40 59 36 03 19 16 00 01 0b 00 40 4e 03 0b 15 1c 06 01 0b 00 40 57 3b 13 1f 1f 05 02 08 00 40 63 1b 13 31 00 02 00 03 52 12 05 04 00 63 32 00 00 00 32 00 43 79 6d 62 61 6c 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cymbal...1 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0e 00 40 59 36 03 19 16 00 01 0b 00 40 4e 03 0b 15 1c 06 00 0b 00 40 57 3b 13 1f 1f 05 02 08 00 40 63 1b 13 31 00 02 00 03 52 12 05 04 00 63 32 00 00 00 32 00 43 79 6d 62 61 6c 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cymbal...2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0e 00 40 59 24 03 19 16 00 01 0b 00 40 55 1e 0b 15 1c 06 01 0b 00 40 56 19 13 1f 1f 05 02 08 00 44 63 1b 13 31 00 02 00 03 52 12 05 04 00 63 32 00 00 00 32 00 43 79 6d 62 61 6c 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cymbal...2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0e 00 40 59 24 03 19 16 00 01 0b 00 40 55 1e 0b 15 1c 06 01 0b 00 40 56 16 13 1f 1f 05 02 08 00 40 63 1b 13 31 00 02 00 03 52 12 05 04 00 63 32 00 00 00 32 00 43 79 6d 62 61 6c 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cymbal...2 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0e 00 40 59 24 03 19 16 00 01 0b 00 40 55 1e 0b 15 1c 06 00 0b 00 40 56 16 13 1f 1f 05 02 08 00 40 63 1b 13 31 00 02 00 03 52 12 05 04 00 63 32 00 00 00 32 00 43 79 6d 62 61 6c 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cymbal...3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0e 00 40 5b 37 03 19 16 00 01 0b 00 41 55 17 0b 15 1c 06 01 0b 00 40 5b 20 13 1f 1f 05 02 08 00 44 63 15 13 33 00 02 00 03 52 12 05 04 00 63 32 00 00 00 32 00 43 79 6d 62 61 6c 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cymbal...3 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0e 00 40 5b 37 03 19 16 00 01 0b 00 40 55 17 0b 15 1c 06 01 0b 00 40 5b 20 13 1f 1f 05 02 08 00 40 63 15 13 33 00 02 00 03 52 12 05 04 00 63 32 00 00 00 32 00 43 79 6d 62 61 6c 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cymbal...3 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0e 00 40 5b 37 03 19 16 00 01 0b 00 40 55 17 0b 15 1c 06 00 0b 00 40 5b 20 13 1f 1f 05 02 08 00 40 63 15 13 33 00 02 00 03 52 12 05 04 00 63 32 00 00 00 32 00 43 79 6d 62 61 6c 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 25 * SUBCATEGORY Drum1 ** Name: Bass/Snare ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 05 0f 00 00 63 32 03 1f 12 00 05 07 00 00 4d 00 03 1f 1f 10 07 0f 63 05 63 28 13 1f 1f 0b 08 0f 00 05 63 00 03 34 23 00 00 00 62 00 04 04 00 63 32 00 00 00 32 00 42 61 73 73 2f 53 6e 61 72 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass/Snare ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 05 0f 00 00 63 32 03 1f 12 00 05 07 00 00 4d 00 03 1f 1f 10 07 0f 63 00 63 28 13 1f 1f 0b 08 0f 00 00 63 00 03 34 23 00 00 00 62 00 04 04 00 63 32 00 00 00 32 00 42 61 73 73 2f 53 6e 61 72 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Drum ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank D, voice 25 1f 1f 13 03 00 00 00 5f 2d 06 1f 12 13 02 00 00 01 52 00 02 1f 14 13 03 00 00 02 56 1c 05 1e 14 13 0f 00 00 00 63 00 03 3b 63 00 00 00 03 0d 07 04 00 63 32 00 00 00 32 00 42 61 73 73 20 44 72 75 6d 20 63 63 63 32 32 32 08 00 00 00 08 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Drum1 ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 1 1f 0f 1f 07 00 00 00 4e 08 03 1f 18 00 06 08 00 00 5f 04 03 1f 15 08 07 0a 00 00 49 01 03 1f 0b 12 0c 0e 00 00 5b 08 03 03 50 00 4a 00 72 00 04 04 00 28 32 00 00 00 32 00 42 61 73 73 20 44 72 75 6d 31 63 63 63 32 32 32 00 00 00 00 00 00 08 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Drum2 ** Source: Yamaha DX11 factory preset, bank D, voice 2 1f 19 07 07 0b 00 00 46 20 01 1f 18 00 0b 0a 00 00 47 04 01 1f 15 00 0c 0a 00 00 45 00 05 1f 0a 15 0a 0f 00 00 63 15 05 31 23 00 00 00 62 00 04 04 00 28 32 00 00 00 32 00 42 61 73 73 20 44 72 75 6d 32 63 63 63 32 32 32 09 03 08 05 08 05 08 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bass Drum2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 19 07 07 0b 00 00 46 20 01 1f 18 00 0b 0d 00 00 47 04 01 1f 15 00 0c 0d 00 00 45 00 05 1f 0d 15 0a 0f 00 00 63 15 05 31 23 00 00 00 62 00 04 04 00 28 32 00 00 00 32 00 42 61 73 73 20 44 72 75 6d 32 63 63 63 32 32 32 09 03 08 05 08 05 08 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassDrum.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1b 19 0f 0e 00 00 55 03 03 18 10 19 0c 0d 00 01 63 03 13 1b 12 10 08 0e 00 06 63 00 13 1f 0f 12 09 0f 00 05 63 02 13 35 00 00 00 00 02 0c 06 04 00 63 32 00 32 00 32 00 42 61 73 73 44 72 75 6d 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassDrum.1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1b 19 0f 0e 00 00 55 03 03 18 10 19 0c 0d 00 00 63 03 13 1b 12 10 08 0e 00 00 63 00 13 1f 0f 12 09 0f 00 00 63 02 13 35 00 00 00 00 02 0c 06 04 00 63 32 00 32 00 32 00 42 61 73 73 44 72 75 6d 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassDrum.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1b 19 0f 0e 00 00 4e 03 03 18 1b 19 0c 0d 00 02 46 03 13 1b 12 10 08 0e 00 05 63 00 13 1f 0f 12 09 0f 00 03 63 00 13 34 00 00 00 00 02 0c 06 04 00 63 32 00 32 00 32 00 42 61 73 73 44 72 75 6d 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassDrum.2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1b 19 0f 0e 00 00 4e 03 03 18 1b 19 0c 0d 00 00 46 03 13 1b 12 10 08 0e 00 00 63 00 13 1f 0f 12 09 0f 00 00 63 00 13 34 00 00 00 00 02 0c 06 04 00 63 32 00 32 00 32 00 42 61 73 73 44 72 75 6d 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassDrum.3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1b 19 0f 0e 00 00 48 04 03 18 1b 19 0c 0d 00 01 4e 00 13 1b 12 10 08 0e 00 02 55 02 13 1f 0f 12 09 0f 00 03 63 00 13 32 00 00 00 00 02 0c 06 04 00 63 32 00 32 00 32 00 42 61 73 73 44 72 75 6d 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BassDrum.3 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1b 19 0f 0e 00 00 48 04 03 18 1b 19 0c 0d 00 00 4e 00 13 1b 12 10 08 0e 00 00 55 02 13 1f 0f 12 09 0f 00 00 63 00 13 32 00 00 00 00 02 0c 06 04 00 63 32 00 32 00 32 00 42 61 73 73 44 72 75 6d 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BoConga ** Controls: KV ** Source: Yamaha DX11 factory preset, bank D, voice 15 19 15 1f 0e 00 05 03 52 00 00 18 11 09 06 04 1e 02 42 04 16 18 0f 00 07 00 02 00 63 04 10 1c 0c 14 06 0b 00 00 63 01 13 04 23 00 00 00 62 1d 04 04 00 28 32 00 00 00 32 00 42 6f 43 6f 6e 67 61 20 20 20 63 11 2a 32 32 32 00 02 00 57 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Congas * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 1d 17 09 09 00 03 63 00 0b 19 1f 1b 09 06 00 00 63 00 0b 1f 05 00 09 0b 00 00 63 28 0b 13 10 11 06 0a 00 03 63 00 0b 02 18 00 26 00 32 18 02 04 00 28 63 00 00 00 32 00 43 6f 6e 67 61 73 20 20 20 2a 63 63 63 32 32 32 00 06 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DrmSpring1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 19 12 0f 0b 2e 03 4c 04 1a 1f 1b 1f 0f 0b 00 04 63 08 10 1f 1b 1f 0f 0b 00 05 63 08 0a 1f 18 07 07 0f 00 06 63 04 0b 32 22 0a 09 00 02 0c 02 04 00 63 32 00 32 00 32 00 44 72 6d 53 70 72 69 6e 67 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DrmSpring1 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 19 12 0f 0b 2e 00 4c 04 1a 1f 1b 1f 0f 0b 00 00 63 08 10 1f 1b 1f 0f 0b 00 00 63 08 0a 1f 18 07 07 0f 00 00 63 04 0b 32 22 0a 09 00 02 0c 02 04 00 63 32 00 32 00 32 00 44 72 6d 53 70 72 69 6e 67 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DrmSpring2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 19 12 0f 0b 2e 03 63 02 1a 1f 1b 1f 0f 0b 00 05 60 07 10 1f 1b 1f 0f 0b 00 06 60 09 0a 1f 18 07 07 0f 00 07 63 02 0b 3a 22 0a 19 00 02 0c 02 04 00 63 32 00 32 00 32 00 44 72 6d 53 70 72 69 6e 67 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DrmSpring2 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 19 12 0f 0b 2e 00 63 02 1a 1f 1b 1f 0f 0b 00 00 60 07 10 1f 1b 1f 0f 0b 00 00 60 09 0a 1f 18 07 07 0f 00 00 63 02 0b 3a 22 0a 19 00 02 0c 02 04 00 63 32 00 32 00 32 00 44 72 6d 53 70 72 69 6e 67 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DrumString ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 06 08 01 04 0d 63 02 51 1e 05 1f 0a 03 06 0d 00 03 52 08 0b 15 0a 04 07 0d 00 06 62 09 04 1f 04 08 07 00 00 05 63 08 06 2c 21 23 0f 00 12 00 04 04 00 28 32 00 00 00 32 00 44 72 75 6d 53 74 72 69 6e 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: DrumString ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 06 08 01 04 0d 63 00 51 1e 05 1f 0a 03 06 0d 00 00 52 08 0b 15 0a 04 07 0d 00 00 62 09 04 1f 04 08 07 00 00 00 63 08 06 2c 21 23 0f 00 12 00 04 04 00 28 32 00 00 00 32 00 44 72 75 6d 53 74 72 69 6e 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Efem Toms ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 32 1f 12 13 03 05 00 00 5f 3d 06 1f 10 0b 02 05 00 01 52 05 02 1f 0f 0e 03 06 00 01 56 28 05 1e 09 0d 06 09 00 00 63 06 03 3c 63 00 63 00 63 20 07 04 00 63 32 00 00 00 32 00 45 66 65 6d 20 54 6f 6d 73 20 63 63 63 32 32 32 08 00 00 00 08 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: El.Snare * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0e 08 02 00 00 01 57 0d 0b 1f 03 00 01 00 00 00 5f 00 0b 1b 04 0a 01 07 00 01 54 18 0b 1f 0e 04 07 00 00 00 63 00 0b 03 23 00 00 00 32 00 02 04 00 28 63 00 00 00 32 00 45 6c 2e 53 6e 61 72 65 20 2a 63 63 63 32 32 32 00 00 08 00 08 04 08 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: El.Snare2* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0e 02 05 00 00 00 57 0c 0b 1f 03 00 03 07 00 00 44 04 0b 1f 02 01 01 00 00 00 51 20 0b 1f 10 00 08 00 00 01 63 0a 0b 13 23 00 00 00 32 00 02 04 00 28 63 00 00 00 32 00 45 6c 2e 53 6e 61 72 65 32 2a 63 63 63 32 32 32 00 01 08 10 08 54 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FM Hi-Hats ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank D, voice 24 1f 0c 00 05 0f 00 00 63 3b 03 1f 09 00 04 0f 00 38 4b 16 03 1f 0c 00 06 0f 00 00 56 16 03 1f 0d 1f 0f 04 00 02 63 08 03 3b 00 00 00 00 62 18 0c 04 00 63 32 00 00 00 32 00 46 4d 20 48 69 2d 48 61 74 73 63 63 63 32 32 32 00 70 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hand Drum ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 30 1f 16 00 03 07 00 01 50 00 03 1f 18 03 03 03 22 00 63 00 14 1d 07 06 03 06 00 01 3c 08 03 1f 05 07 03 00 00 01 63 04 1b 39 23 00 08 00 42 18 04 04 00 63 32 00 00 00 32 00 48 61 6e 64 20 44 72 75 6d 20 63 63 63 32 32 32 00 01 00 03 00 01 00 01 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hand Drum1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 15 00 07 09 00 00 53 01 13 1f 18 03 04 0d 00 01 3b 03 13 1d 1a 06 04 04 00 06 41 09 03 1f 00 07 03 0f 00 03 63 03 1b 29 23 00 00 00 62 18 05 04 00 63 00 00 00 00 32 00 48 61 6e 64 20 44 72 75 6d 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hand Drum1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 15 00 07 09 00 00 53 01 13 1f 18 03 04 0d 00 00 3b 03 13 1d 1a 06 04 04 00 00 41 09 03 1f 00 07 03 0f 00 00 63 03 1b 29 23 00 00 00 62 18 05 04 00 63 00 00 00 00 32 00 48 61 6e 64 20 44 72 75 6d 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hand Drum2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 15 00 07 09 00 00 57 01 13 1f 18 03 04 0d 00 00 3b 04 13 1d 1a 06 04 04 00 00 41 01 03 1f 00 07 08 0f 00 05 63 01 1b 29 23 00 00 00 62 18 05 04 00 63 00 00 00 00 32 00 48 61 6e 64 20 44 72 75 6d 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hand Drum2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 15 00 07 09 00 00 57 01 13 1f 18 03 04 0d 00 00 3b 04 13 1d 1a 06 04 04 00 00 41 01 03 1f 00 07 08 0f 00 00 63 01 1b 29 23 00 00 00 62 18 05 04 00 63 00 00 00 00 32 00 48 61 6e 64 20 44 72 75 6d 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: "Hi!" Hat! ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 9 1f 06 06 0f 0f 00 00 63 20 1b 1f 09 1f 0f 0f 00 00 63 24 1b 1f 0a 00 0f 00 00 00 63 20 1b 1f 0a 00 0f 00 00 00 59 30 1b 3c 63 00 55 46 62 18 04 04 00 28 32 00 00 00 32 00 22 48 69 21 22 20 48 61 74 21 63 63 63 32 32 32 0e 0f 0e 08 0e 0c 0b 0b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hi-Hat * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0c 00 05 0f 00 00 63 3b 03 1f 09 00 04 0f 00 38 4e 16 03 1f 0c 00 06 0f 00 00 5e 16 03 1f 13 1f 0f 00 00 02 63 08 03 3b 00 00 00 00 62 18 0c 04 00 63 32 00 00 00 32 00 48 69 2d 48 61 74 20 20 20 2a 63 63 63 32 32 32 00 70 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Java Jive ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 26 15 13 0d 03 0d 1e 02 50 18 0e 15 14 09 04 0c 1e 02 56 11 08 1b 1f 0f 07 0f 00 01 63 04 06 1a 1f 0e 06 0f 00 02 63 04 00 7c 0b 00 08 00 62 18 04 04 00 63 32 00 00 00 32 00 4a 61 76 61 20 4a 69 76 65 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: KickDrum * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 16 00 05 03 00 00 5f 02 03 1f 11 01 08 00 00 00 53 04 13 1f 19 13 05 04 00 02 5f 00 1b 1f 1d 10 08 0f 00 00 63 10 0b 38 23 00 00 00 62 0c 04 0c 00 63 32 00 00 00 32 00 4b 69 63 6b 44 72 75 6d 20 2a 63 63 63 32 32 32 0d 30 08 00 08 20 08 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Log Drum.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 18 0f 06 04 00 41 1d 08 1b 1f 18 0c 05 00 00 41 51 00 03 18 06 0f 06 04 00 45 63 00 13 1b 0f 0d 06 00 00 44 63 00 0b 2c 24 00 00 50 02 18 04 04 00 28 32 00 00 00 32 00 4c 6f 67 20 44 72 75 6d 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Log Drum.1 ** Source: from a library of patches collected by Duane Bowker, author unknown 13 18 0f 06 04 00 40 1d 08 1b 1f 18 0c 05 00 00 40 51 00 03 18 06 0f 06 04 00 40 63 00 13 1b 0f 0d 06 00 00 40 63 00 0b 2c 24 00 00 50 02 18 04 04 00 28 32 00 00 00 32 00 4c 6f 67 20 44 72 75 6d 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Log Drum.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 18 0f 06 04 00 40 5d 05 1b 1f 18 0c 05 00 00 41 49 03 0b 18 06 0f 06 04 00 44 63 01 13 1b 0f 0d 06 00 00 44 63 01 0b 2c 24 00 00 50 02 18 04 04 00 28 32 00 00 00 32 00 4c 6f 67 20 44 72 75 6d 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Log Drum.2 ** Source: from a library of patches collected by Duane Bowker, author unknown 13 18 0f 06 04 00 40 5d 05 1b 1f 18 0c 05 00 00 40 49 03 0b 18 06 0f 06 04 00 40 63 01 13 1b 0f 0d 06 00 00 40 63 01 0b 2c 24 00 00 50 02 18 04 04 00 28 32 00 00 00 32 00 4c 6f 67 20 44 72 75 6d 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MiamiToms* ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 00 03 00 21 01 48 07 0b 1f 0a 00 02 00 04 00 46 02 0b 1f 02 00 04 00 00 01 40 00 0b 1f 0d 0f 06 0d 00 02 63 00 0b 68 04 00 45 00 62 18 02 04 00 28 63 00 00 00 2a 00 4d 69 61 6d 69 54 6f 6d 73 2a 63 63 63 32 32 32 00 30 00 52 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 19 18 0d 0d 00 02 62 03 0b 18 0f 0c 09 00 00 02 5a 08 13 1d 15 15 0f 0f 00 03 63 07 03 19 0f 18 07 04 00 05 62 04 0b 7c 23 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 19 18 0d 0d 00 00 62 03 0b 18 0f 0c 09 00 00 00 5a 08 13 1d 15 15 0f 0f 00 00 63 07 03 19 0f 18 07 04 00 00 62 04 0b 7c 23 00 00 00 02 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 01 07 04 00 00 02 49 02 0b 1f 16 00 03 00 00 42 56 00 0c 1f 09 06 04 0b 00 03 63 00 13 1f 0c 1f 06 00 00 05 62 00 13 2c 28 00 10 2e 26 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 01 07 04 00 00 00 49 02 0b 1f 16 00 03 00 00 40 56 00 0c 1f 09 06 04 0b 00 00 63 00 13 1f 0c 1f 06 00 00 00 62 00 13 2c 28 00 10 2e 26 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 01 07 04 00 00 04 48 02 0b 1f 16 00 03 00 00 04 55 00 0c 1f 09 06 04 0b 00 04 3e 03 13 1f 0c 1f 06 00 00 05 62 00 13 2c 0b 00 10 00 22 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..3 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 01 07 04 00 00 00 48 02 0b 1f 16 00 03 00 00 00 55 00 0c 1f 09 06 04 0b 00 00 3e 03 13 1f 0c 1f 06 00 00 00 62 00 13 2c 0b 00 10 00 22 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 09 06 00 00 02 4b 01 0b 1f 16 00 03 00 00 03 38 20 0c 1f 0b 09 06 0b 00 05 59 01 13 1f 0c 1f 06 00 00 06 62 08 13 2c 0b 00 10 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..4 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 09 06 00 00 00 4b 01 0b 1f 16 00 03 00 00 00 38 20 0c 1f 0b 09 06 0b 00 00 59 01 13 1f 0c 1f 06 00 00 00 62 08 13 2c 0b 00 10 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..5 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 12 07 0e 00 03 43 31 1b 1f 10 0c 06 0e 00 03 48 08 0c 1f 09 16 0a 0d 00 04 61 04 0b 1b 0c 0c 06 0d 00 04 62 04 0b 2c 0b 00 10 00 22 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..5 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0d 12 07 0e 00 00 43 31 1b 1f 10 0c 06 0e 00 00 48 08 0c 1f 09 16 0a 0d 00 00 61 04 0b 1b 0c 0c 06 0d 00 00 62 04 0b 2c 0b 00 10 00 22 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0a 05 05 0c 00 03 4d 04 1b 00 0f 00 01 09 00 02 61 09 03 1c 09 09 07 0d 00 04 63 00 13 1c 09 09 07 0c 00 07 62 00 0b 4c 23 00 00 00 22 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..6 ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 0a 05 05 0c 00 00 4d 04 1b 00 0f 00 01 09 00 00 61 09 03 1c 09 09 07 0d 00 00 63 00 13 1c 09 09 07 0c 00 00 62 00 0b 4c 23 00 00 00 22 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..6 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 0f 0a 05 05 0c 00 00 4d 04 1b 00 0f 00 00 09 00 00 61 09 03 1c 09 09 07 0d 00 00 63 00 13 1c 09 09 07 0c 00 00 62 00 0b 4c 23 00 00 00 22 18 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..7 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 09 06 00 00 05 48 02 13 1f 16 00 03 00 00 03 53 01 12 1f 0b 09 06 0b 00 03 57 03 13 1f 0c 1f 06 00 00 05 62 00 13 2c 0b 00 10 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..7 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 09 06 00 00 00 48 02 13 1f 16 00 03 00 00 00 53 01 12 1f 0b 09 06 0b 00 00 57 03 13 1f 0c 1f 06 00 00 00 62 00 13 2c 0b 00 10 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..8 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 09 06 00 00 02 48 02 13 1f 16 00 03 00 00 03 53 01 12 1f 0b 09 06 0b 00 06 57 05 13 1f 0c 1f 06 00 00 04 62 00 13 32 0b 00 10 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..8 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 09 06 00 00 00 48 02 13 1f 16 00 03 00 00 00 53 01 12 1f 0b 09 06 0b 00 00 57 05 13 1f 0c 1f 06 00 00 00 62 00 13 32 0b 00 10 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..9 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0b 09 06 00 00 03 30 08 1b 1f 16 00 03 00 00 02 44 01 12 1f 1a 09 06 0b 00 05 5f 05 13 1f 0c 1f 06 00 00 04 62 00 13 32 0b 00 10 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm..9 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0b 09 06 00 00 00 30 08 1b 1f 16 00 03 00 00 00 44 01 12 1f 1a 09 06 0b 00 00 5f 05 13 1f 0c 1f 06 00 00 00 62 00 13 32 0b 00 10 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 2e 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm.10 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0b 09 06 00 00 03 30 03 1b 18 0f 18 07 05 00 04 44 09 12 1f 1a 09 06 0b 00 03 37 07 13 18 0f 0c 08 00 00 06 62 01 13 33 0b 00 10 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntDrm.10 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0b 09 06 00 00 00 30 03 1b 18 0f 18 07 05 00 00 44 09 12 1f 1a 09 06 0b 00 00 37 07 13 18 0f 0c 08 00 00 00 62 01 13 33 0b 00 10 00 22 0c 02 04 00 63 63 00 00 00 32 00 4f 72 6e 74 44 72 6d 2e 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntEchoDr ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 18 0f 06 0f 63 02 5b 08 1b 0f 0a 03 07 0d 00 04 48 00 03 19 06 0f 06 04 00 06 63 00 13 1c 05 03 07 0d 00 05 5d 00 0b 2c 26 1c 0e 00 70 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 45 63 68 6f 44 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntEchoDr ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 18 0f 06 0f 63 00 5b 08 1b 0f 0a 03 07 0d 00 00 48 00 03 19 06 0f 06 04 00 00 63 00 13 1c 05 03 07 0d 00 00 5d 00 0b 2c 26 1c 0e 00 70 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 45 63 68 6f 44 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Percussive ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 05 04 0f 00 00 5a 0b 03 1f 12 0c 05 08 46 00 5f 03 03 1f 12 09 06 0a 00 05 4e 00 03 1f 1f 0c 06 0f 00 05 5a 07 0b 3c 23 00 00 00 62 18 04 04 00 63 32 00 00 00 32 00 50 65 72 63 75 73 73 69 76 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Percussive ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 05 04 0f 00 00 5a 0b 03 1f 12 0c 05 08 46 00 5f 01 03 1f 12 09 06 0a 00 00 4e 00 03 1f 1f 0c 06 0f 00 00 5a 07 0b 3c 23 00 00 00 62 18 04 04 00 63 32 00 00 00 32 00 50 65 72 63 75 73 73 69 76 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Simmons1 * ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 00 02 00 00 00 63 01 03 1f 00 00 02 00 00 00 42 00 03 1f 09 00 04 00 00 00 53 0c 03 1f 10 00 07 00 00 00 63 00 0b 41 02 00 5f 00 72 18 02 04 00 28 63 00 00 00 32 00 53 69 6d 6d 6f 6e 73 31 20 2a 63 63 63 32 32 32 00 01 00 23 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Simmons2 * ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0d 00 02 00 00 00 63 01 03 1f 00 00 05 00 00 00 3c 00 03 1f 09 00 01 00 00 00 4b 0c 03 1f 0f 00 06 00 00 00 63 00 0b 41 02 00 5f 00 72 18 02 04 00 28 63 00 00 00 32 00 53 69 6d 6d 6f 6e 73 32 20 2a 63 63 63 32 32 32 00 01 00 23 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Skankin' ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 19 08 0a 03 00 03 63 0b 10 1f 13 05 0b 05 00 03 58 00 0b 1f 19 06 0b 05 00 03 63 0a 0e 1f 0f 11 09 07 00 04 63 04 03 3a 14 00 00 00 42 18 02 04 00 63 32 00 32 00 32 00 53 6b 61 6e 6b 69 6e 27 20 20 63 63 63 32 32 32 00 30 20 30 00 20 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare * ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 01 00 00 00 63 32 03 1f 0e 00 01 00 00 00 59 08 13 1f 00 00 01 00 00 00 54 08 1b 1f 1a 10 08 0f 00 00 63 14 0b 3a 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 20 20 20 20 2a 63 63 63 32 32 32 0e 30 08 0a 00 20 08 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare 1 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 3 1f 18 00 01 0f 00 00 5a 38 06 1f 15 09 03 0b 00 00 63 00 00 1f 0a 12 08 0e 00 02 63 00 00 1f 0f 10 08 0a 00 02 63 3c 06 7c 63 00 63 00 73 18 04 04 00 28 32 00 00 00 32 00 53 6e 61 72 65 20 20 20 20 31 63 63 63 32 32 32 0f 0e 01 07 08 00 09 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare 1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 18 00 01 0f 00 00 5a 38 06 1f 15 09 03 0b 00 00 63 00 00 1f 0d 12 08 0e 00 02 63 00 00 1f 0f 10 08 0d 00 02 63 3c 06 7c 63 00 63 00 73 18 04 04 00 28 32 00 00 00 32 00 53 6e 61 72 65 20 20 20 20 31 63 63 63 32 32 32 0f 0e 01 07 08 00 09 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare 2 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 4 1f 1a 00 05 0f 00 40 61 3c 1e 1f 15 09 04 0b 00 44 4e 2c 18 1b 13 13 09 0e 00 42 63 30 00 1f 13 13 07 0e 00 42 63 2c 06 7c 63 00 30 0f 6a 0c 04 04 00 28 32 00 00 00 32 00 53 6e 61 72 65 20 20 20 20 32 63 63 63 32 32 32 0c 09 08 01 08 0c 08 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare 1 ** Source: Yamaha TX81Z factory preset, bank D, voice 28 1f 1f 00 05 0f 00 00 62 32 03 1f 14 08 05 08 00 00 5f 00 0b 1f 1f 10 08 0f 0c 00 63 0b 1b 1f 1f 0f 08 0f 00 00 63 05 0b 3b 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 20 31 20 20 20 63 63 63 32 32 32 08 00 00 02 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare 2 ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank D, voice 29 1f 00 00 05 0f 00 00 63 32 03 1f 14 08 05 08 00 01 63 02 0b 1f 04 10 08 0f 00 00 63 0b 0b 1f 1f 0f 08 0f 00 00 63 05 0b 3c 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 20 32 20 20 20 63 63 63 32 32 32 07 60 00 00 00 70 08 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare....1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0f 00 00 60 0b 00 1f 1a 0a 05 0a 00 00 58 06 03 13 1f 11 08 0f 00 05 5e 04 03 1f 1f 10 07 0f 00 04 57 00 03 3c 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare....1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 00 00 60 0b 00 1f 1a 0a 05 0a 00 00 58 06 03 13 1f 11 08 0f 00 00 5e 04 03 1f 1f 10 07 0f 00 00 57 00 03 3c 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare....1 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0f 00 00 60 0b 00 1f 1a 0a 05 0a 00 00 58 06 03 13 1f 11 08 0f 00 00 5e 04 03 1f 1f 10 07 0f 00 00 57 00 03 3c 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare....2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0f 00 00 63 3f 03 1f 1a 0a 05 0a 00 00 4f 06 03 13 16 11 08 0d 00 05 63 06 03 1f 1f 10 08 0f 00 05 63 02 03 28 23 00 00 00 52 0c 04 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare....2 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 00 00 63 3f 03 1f 1a 0a 05 0a 00 00 4f 06 03 13 16 11 08 0d 00 00 63 06 03 1f 1f 10 08 0f 00 00 63 02 03 28 23 00 00 00 52 0c 04 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare....2 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0f 00 00 63 3f 03 1f 1a 0a 05 0a 00 00 4f 06 03 13 16 11 08 0d 00 00 63 06 03 1f 1f 10 08 0f 00 00 63 02 03 28 23 00 00 00 52 0c 04 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare....3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 05 0f 00 00 63 32 03 1f 15 0f 05 09 14 00 21 06 03 1f 1f 12 08 0f 00 04 53 00 0b 1f 1f 10 08 0f 00 04 63 01 03 3b 23 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare....3 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 05 0f 00 00 63 32 03 1f 15 0f 05 09 14 00 21 06 03 1f 1f 12 08 0f 00 00 53 00 0b 1f 1f 10 08 0f 00 00 63 01 03 3b 23 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare....4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 05 0f 00 01 63 32 03 1f 13 10 05 05 14 01 47 3f 13 1f 1f 12 08 0f 00 06 63 00 0b 1f 1f 0f 08 0f 00 05 43 3f 03 3c 23 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare....4 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 05 0f 00 00 63 32 03 1f 13 10 05 05 14 00 47 3f 13 1f 1f 12 08 0f 00 00 63 00 0b 1f 1f 0f 08 0f 00 00 43 3f 03 3c 23 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SnareDrm.* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 1a 00 01 0f 00 00 63 1c 03 1f 14 00 01 00 00 00 51 1c 03 1f 1f 0e 07 0f 00 02 63 00 0b 1f 12 0f 06 00 00 02 63 24 03 3c 23 00 00 00 62 24 02 04 00 28 32 00 00 00 32 00 53 6e 61 72 65 44 72 6d 2e 2a 63 63 63 32 32 32 08 0e 08 0d 08 02 09 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Snare&Tom ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 05 0f 00 00 63 32 03 1f 10 08 05 08 00 01 63 02 0b 1f 04 10 08 0f 00 00 63 0b 0b 1f 1f 0f 00 0f 2e 00 63 05 0b 3c 23 24 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 53 6e 61 72 65 26 54 6f 6d 20 63 63 63 32 32 32 07 30 00 00 00 70 08 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Steel Drum ** Controls: KV ** Source: Yamaha DX11 factory preset, bank B, voice 32 14 10 10 06 0a 1a 03 27 30 03 13 08 00 04 0a 1a 01 42 04 01 15 0c 0e 06 0c 00 02 4c 0b 03 18 0c 0f 05 0f 00 01 5f 04 0a 01 20 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 53 74 65 65 6c 20 44 72 75 6d 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Steel Drum ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 14 10 10 06 0a 1a 03 27 30 03 13 08 00 04 0d 1a 01 42 04 01 15 0c 0e 06 0c 00 02 4c 0b 03 18 0c 0f 05 0f 00 01 5f 04 0d 01 20 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 53 74 65 65 6c 20 44 72 75 6d 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SteelDrum ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 04 03 0a 1a 07 44 05 08 0d 08 0c 05 0b 32 06 40 13 0b 0e 05 03 04 0a 00 04 28 1a 0e 15 08 0c 06 0c 00 05 63 08 0c 02 10 00 09 00 52 0c 02 04 00 63 32 00 00 00 32 00 53 74 65 65 6c 44 72 75 6d 20 63 63 63 32 32 32 10 37 00 10 00 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SteelDrum1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 09 03 0e 00 03 54 0a 11 1b 0a 0d 06 0e 2d 03 63 04 15 1b 0a 0d 06 0e 43 06 5f 04 15 1f 0c 0d 06 0e 00 05 63 04 0e 66 23 00 00 00 32 0c 06 04 00 63 32 00 32 00 32 00 53 74 65 65 6c 44 72 75 6d 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SteelDrum1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 09 03 0e 00 00 54 0a 11 1b 0a 0d 06 0e 2d 00 63 04 15 1b 0a 0d 06 0e 43 00 5f 04 15 1f 0c 0d 06 0e 00 00 63 04 0e 66 23 00 00 00 32 0c 06 04 00 63 32 00 32 00 32 00 53 74 65 65 6c 44 72 75 6d 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SteelDrum2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 09 03 0e 00 42 54 07 11 1b 0a 0d 06 0e 2d 43 63 08 15 1b 0a 0d 06 0e 43 46 5f 04 15 1f 0c 0d 06 0e 00 44 63 04 0e 2c 19 00 0a 15 26 0c 06 04 00 63 32 00 32 00 32 00 53 74 65 65 6c 44 72 75 6d 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SteelDrum2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 09 03 0e 00 40 54 07 11 1b 0a 0d 06 0e 2d 40 63 08 15 1b 0a 0d 06 0e 43 40 5f 04 15 1f 0c 0d 06 0e 00 40 63 04 0e 2c 19 00 0a 15 26 0c 06 04 00 63 32 00 32 00 32 00 53 74 65 65 6c 44 72 75 6d 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SteelDrum3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 10 00 03 00 00 01 49 0b 0b 14 0a 00 04 00 00 03 3c 06 03 14 0a 00 04 00 00 05 3f 04 03 14 0a 00 06 00 00 05 63 04 03 3a 23 00 00 00 62 18 05 04 00 63 63 00 00 00 32 00 53 74 65 65 6c 44 72 75 6d 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SteelDrum3 ** Source: from a library of patches collected by Duane Bowker, author unknown 14 10 00 03 00 00 00 49 0b 0b 14 0a 00 04 00 00 00 3c 06 03 14 0a 00 04 00 00 00 3f 04 03 14 0a 00 06 00 00 00 63 04 03 3a 23 00 00 00 62 18 05 04 00 63 63 00 00 00 32 00 53 74 65 65 6c 44 72 75 6d 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SteelDrum4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 10 00 03 00 00 02 42 0b 03 14 0a 00 04 00 00 03 42 06 03 14 0a 00 04 00 00 06 2f 06 03 14 0a 00 06 00 00 06 63 04 03 3a 23 00 00 00 62 18 05 04 00 63 63 00 00 00 32 00 53 74 65 65 6c 44 72 75 6d 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SteelDrum4 ** Source: from a library of patches collected by Duane Bowker, author unknown 14 10 00 03 00 00 00 42 0b 03 14 0a 00 04 00 00 00 42 06 03 14 0a 00 04 00 00 00 2f 06 03 14 0a 00 06 00 00 00 63 04 03 3a 23 00 00 00 62 18 05 04 00 63 63 00 00 00 32 00 53 74 65 65 6c 44 72 75 6d 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SyBon ** Controls: KV ** Source: Yamaha DX11 factory preset, bank D, voice 14 19 15 1f 02 00 4b 04 5f 00 1e 18 11 09 06 04 1e 04 5d 00 11 17 0b 04 04 0a 32 00 59 04 18 1b 12 0a 05 0a 00 00 5d 00 16 03 23 00 00 00 62 0c 04 04 00 28 32 00 00 00 32 00 53 79 42 6f 6e 20 20 20 20 20 63 19 19 32 15 15 00 02 00 07 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SyBon ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 19 15 1f 02 00 4b 04 5f 00 1e 18 11 09 06 04 1e 04 5d 00 11 17 0b 04 04 0d 32 00 59 04 18 1b 12 0d 05 0d 00 00 5d 00 16 03 23 00 00 00 62 0c 04 04 00 28 32 00 00 00 32 00 53 79 42 6f 6e 20 20 20 20 20 63 19 19 32 15 15 00 02 00 07 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Drum2 ** Name: SynthDrm.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 1f 0a 0f 00 02 3c 0f 03 18 18 16 0b 04 2d 03 55 04 05 15 15 0c 0a 00 43 05 63 13 03 15 13 0c 0b 00 00 04 63 08 03 7c 3f 00 63 00 60 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.1 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 1f 0a 0f 00 00 3c 0f 03 18 18 16 0b 04 2d 00 55 04 05 15 15 0c 0a 00 43 00 63 13 03 15 13 0c 0b 00 00 00 63 08 03 7c 3f 00 63 00 60 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 1f 0a 0f 00 01 5d 3f 03 18 18 16 0b 04 2d 01 55 04 15 15 15 0c 0a 00 43 04 63 09 0b 15 13 0c 0b 00 00 05 63 00 0b 7c 3f 00 63 00 60 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 1f 0a 0f 00 00 5d 3f 03 18 18 16 0b 04 2d 00 55 04 15 15 15 0c 0a 00 43 00 63 09 0b 15 13 0c 0b 00 00 00 63 00 0b 7c 3f 00 63 00 60 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.3 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 1f 0a 0f 00 02 63 04 03 1f 18 16 0b 04 2d 00 55 04 15 15 15 0c 0a 00 43 03 63 04 0b 1c 13 0c 0b 00 00 05 63 00 0b 7c 3f 00 63 00 60 18 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.3 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 1f 0a 0f 00 00 63 04 03 1f 18 16 0b 04 2d 00 55 04 15 15 15 0c 0a 00 43 00 63 04 0b 1c 13 0c 0b 00 00 00 63 00 0b 7c 3f 00 63 00 60 18 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 1f 0a 0f 00 01 63 30 03 19 0c 07 04 0f 2d 01 63 01 03 15 15 0c 0a 00 43 05 63 04 0b 1f 12 09 07 00 00 06 63 00 03 24 1e 63 00 00 73 18 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.4 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 1f 0a 0f 00 00 63 30 03 19 0c 07 04 0f 2d 00 63 01 03 15 15 0c 0a 00 43 00 63 04 0b 1f 12 09 07 00 00 00 63 00 03 24 1e 63 00 00 73 18 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 1f 0a 0f 00 03 63 2c 1b 1f 1f 07 05 0f 2d 03 5c 00 0b 1f 1f 00 0f 0f 43 04 63 00 0b 1f 11 09 05 00 00 06 63 00 03 24 1e 63 00 00 73 18 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.5 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 1f 0a 0f 00 00 63 2c 1b 1f 1f 07 05 0f 2d 00 5c 00 0b 1f 1f 00 0f 0f 43 00 63 00 0b 1f 11 09 05 00 00 00 63 00 03 24 1e 63 00 00 73 18 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.6 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 1f 0a 0f 00 02 5d 01 03 18 18 16 0b 04 2d 03 55 01 15 15 15 0c 0a 00 43 05 63 00 0b 15 13 0c 0b 00 00 45 63 00 0b 5a 17 00 00 2a 66 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.6 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 1f 0a 0f 00 00 5d 01 03 18 18 16 0b 04 2d 00 55 01 15 15 15 0c 0a 00 43 00 63 00 0b 15 13 0c 0b 00 00 40 63 00 0b 5a 17 00 00 2a 66 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.7 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 1f 0a 0f 00 02 5d 3f 03 18 18 16 0b 04 2d 03 55 01 15 15 15 0c 0a 00 43 05 63 04 0b 1c 13 0c 0b 00 00 45 63 04 0b 5b 17 00 00 2a 66 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.7 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 1f 0a 0f 00 00 5d 3f 03 18 18 16 0b 04 2d 00 55 01 15 15 15 0c 0a 00 43 00 63 04 0b 1c 13 0c 0b 00 00 40 63 04 0b 5b 17 00 00 2a 66 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 37 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.8 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 07 02 00 00 02 19 3f 03 19 19 09 07 00 2d 02 63 04 15 1f 1f 06 06 0c 43 03 63 07 0b 10 10 12 09 0c 00 45 63 00 0b 5a 17 00 00 2a 66 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.8 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 07 02 00 00 00 19 3f 03 19 19 09 07 00 2d 00 63 04 15 1f 1f 06 06 0c 43 00 63 07 0b 10 10 12 09 0c 00 40 63 00 0b 5a 17 00 00 2a 66 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 38 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.9 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 1f 02 0a 00 01 39 3d 03 1f 13 1f 06 00 2d 02 63 04 13 1f 0c 00 06 00 43 03 63 00 0b 1f 10 1f 09 06 00 45 63 00 0b 5c 17 00 00 2a 66 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm.9 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 1f 02 0a 00 00 39 3d 03 1f 13 1f 06 00 2d 00 63 04 13 1f 0c 00 06 00 43 00 63 00 0b 1f 10 1f 09 06 00 40 63 00 0b 5c 17 00 00 2a 66 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 2e 39 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm10 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 1f 02 0a 00 01 39 3a 03 1f 13 1f 06 00 2d 02 63 01 13 1f 0c 00 06 00 43 05 63 01 0b 1f 10 1f 09 06 00 06 63 05 0b 64 08 00 63 00 60 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm10 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 1f 02 0a 00 00 39 3a 03 1f 13 1f 06 00 2d 00 63 01 13 1f 0c 00 06 00 43 00 63 01 0b 1f 10 1f 09 06 00 00 63 05 0b 64 08 00 63 00 60 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 31 30 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm11 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 15 00 07 09 00 02 4a 00 13 1f 1f 10 04 0a 00 03 54 01 13 1d 1a 06 07 04 00 05 63 05 03 1f 10 1f 05 00 00 05 63 00 0b 2c 23 00 00 00 62 18 05 04 00 63 00 00 00 00 32 00 53 79 6e 74 68 44 72 6d 31 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm11 ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 15 00 07 09 00 00 4a 00 13 1f 1f 10 04 0a 00 00 54 01 13 1d 1a 06 07 04 00 00 63 05 03 1f 10 1f 05 00 00 00 63 00 0b 2c 23 00 00 00 62 18 05 04 00 63 00 00 00 00 32 00 53 79 6e 74 68 44 72 6d 31 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm12 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 1f 19 06 0f 00 02 2d 34 1b 1f 14 1f 06 00 2d 02 63 04 13 1f 0b 00 06 00 43 44 63 00 0b 1f 10 1f 09 06 00 46 63 07 0b 5c 24 1c 02 4d 76 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm12 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 1f 19 06 0f 00 00 2d 34 1b 1f 14 1f 06 00 2d 00 63 04 13 1f 0b 00 06 00 43 40 63 00 0b 1f 10 1f 09 06 00 40 63 07 0b 5c 24 1c 02 4d 76 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 31 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm13 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 1f 0a 0f 00 04 61 3f 03 18 18 16 0b 04 2d 04 5f 06 05 15 15 0c 0a 00 43 04 63 07 03 15 13 0c 0b 00 00 06 63 01 03 7c 3f 00 63 00 60 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 31 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm13 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 1f 0a 0f 00 00 61 3f 03 18 18 16 0b 04 2d 00 5f 06 05 15 15 0c 0a 00 43 00 63 07 03 15 13 0c 0b 00 00 00 63 01 03 7c 3f 00 63 00 60 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 31 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm14 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 1f 0a 0f 00 03 61 3f 03 18 18 16 0b 04 2d 03 56 01 05 15 15 0c 0a 00 43 03 42 07 03 15 13 0c 0b 00 00 04 63 01 03 78 3f 00 63 00 60 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 31 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm14 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 1f 0a 0f 00 00 61 3f 03 18 18 16 0b 04 2d 00 56 01 05 15 15 0c 0a 00 43 00 42 07 03 15 13 0c 0b 00 00 00 63 01 03 78 3f 00 63 00 60 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 31 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm15 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 18 1f 0a 0f 00 03 63 08 03 13 18 0e 0b 0a 2d 03 35 00 05 1f 18 0d 0a 09 43 04 5e 04 03 15 11 0c 0b 00 00 05 63 00 03 7b 23 00 00 00 32 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 31 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm15 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 18 1f 0a 0f 00 00 63 08 03 13 18 0e 0b 0a 2d 00 35 00 05 1f 18 0d 0a 09 43 00 5e 04 03 15 11 0c 0b 00 00 00 63 00 03 7b 23 00 00 00 32 0c 06 04 00 63 32 00 32 00 32 00 53 79 6e 74 68 44 72 6d 31 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm16 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 1e 08 01 00 63 02 63 0a 0b 14 07 16 0f 00 00 02 51 00 1b 1f 0c 1f 06 00 00 05 61 01 03 1c 0f 07 07 07 3e 04 63 03 0b 7c 00 00 00 00 73 0c 02 04 00 63 62 00 00 00 32 00 53 79 6e 74 68 44 72 6d 31 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SynthDrm16 ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 1e 08 01 00 63 00 63 0a 0b 14 07 16 0f 00 00 00 51 00 1b 1f 0c 1f 06 00 00 00 61 01 03 1c 0f 07 07 07 3e 00 63 03 0b 7c 00 00 00 00 73 0c 02 04 00 63 62 00 00 00 32 00 53 79 6e 74 68 44 72 6d 31 36 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tibet Drum ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0f 09 01 0c 32 02 59 0a 15 1c 0f 1f 02 0a 1e 03 34 10 0a 1c 09 0d 02 0a 32 05 5d 04 0a 1c 09 0d 02 0a 1e 06 61 04 0e 24 20 2c 14 00 42 0c 0c 04 00 63 32 32 00 00 32 00 54 69 62 65 74 20 44 72 75 6d 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tibet Drum ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0f 09 01 0c 32 00 59 0a 15 1c 0f 1f 02 0a 1e 00 34 10 0a 1c 09 0d 02 0a 32 00 5d 04 0a 1c 09 0d 02 0a 1e 00 61 04 0e 24 20 2c 14 00 42 0c 0c 04 00 63 32 32 00 00 32 00 54 69 62 65 74 20 44 72 75 6d 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timbale..1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 07 03 05 0f 00 00 37 02 06 15 09 04 04 0f 00 00 47 01 00 13 1f 00 06 0f 00 04 5f 03 06 1f 0c 07 06 00 00 04 63 00 13 3c 0b 00 10 00 22 18 07 04 00 63 32 63 32 00 32 00 54 69 6d 62 61 6c 65 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timbale..1 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 07 03 05 0f 00 00 37 02 06 15 09 04 04 0f 00 00 47 01 00 13 1f 00 06 0f 00 00 5f 03 06 1f 0c 07 06 00 00 00 63 00 13 3c 0b 00 10 00 22 18 07 04 00 63 32 63 32 00 32 00 54 69 6d 62 61 6c 65 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timbale..2 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 01 07 04 00 00 00 3d 02 06 1f 18 09 03 0a 00 00 53 01 08 1f 09 06 04 0b 00 05 57 03 06 1f 0c 07 06 00 00 05 63 00 13 2c 0b 00 10 00 22 18 07 04 00 63 32 63 32 00 32 00 54 69 6d 62 61 6c 65 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timbale..2 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 01 07 04 00 00 00 3d 02 06 1f 18 09 03 0a 00 00 53 01 08 1f 09 06 04 0b 00 00 57 03 06 1f 0c 07 06 00 00 00 63 00 13 2c 0b 00 10 00 22 18 07 04 00 63 32 63 32 00 32 00 54 69 6d 62 61 6c 65 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timbales * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 02 00 13 00 50 04 0b 1f 0d 00 07 00 0c 00 55 01 03 1f 11 00 09 00 32 00 3c 2b 03 1f 0e 00 06 00 00 06 63 08 03 41 02 00 5f 00 72 00 02 04 00 28 63 00 00 00 32 00 54 69 6d 62 61 6c 65 73 20 2a 63 63 63 32 32 32 00 1a 00 22 03 04 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timpani * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 08 03 00 00 07 4f 00 00 1f 11 06 03 0b 36 02 4b 00 08 1f 12 0c 04 0f 00 05 4f 01 0e 1d 0b 00 04 00 00 00 63 00 0b 02 07 00 00 00 62 18 0c 04 00 28 50 00 00 00 32 00 54 69 6d 70 61 6e 69 20 20 2a 63 63 63 32 32 32 00 32 00 02 10 22 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timpani ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 23 1d 12 00 03 00 00 00 41 3c 03 1f 0f 07 04 0f 00 00 3c 00 03 1f 0f 11 03 0c 3b 01 52 02 03 1f 11 0c 04 0f 00 02 63 00 03 3a 15 00 03 00 6a 0c 07 04 00 63 32 00 00 00 32 00 54 69 6d 70 61 6e 69 20 20 20 63 63 63 32 32 32 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timpani..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 12 00 03 00 00 00 37 00 03 1f 13 0c 04 0b 00 00 48 00 03 1f 07 04 02 0c 3b 05 3f 02 03 1f 0c 0c 04 0f 00 06 63 00 03 2a 15 00 03 00 6a 0c 07 04 00 63 32 00 00 00 32 00 54 69 6d 70 61 6e 69 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timpani..1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 12 00 03 00 00 00 37 00 03 1f 13 0c 04 0b 00 00 48 00 03 1f 07 04 02 0c 3b 00 3f 02 03 1f 0c 0c 04 0f 00 00 63 00 03 2a 15 00 03 00 6a 0c 07 04 00 63 32 00 00 00 32 00 54 69 6d 70 61 6e 69 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timpani..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 12 00 02 00 00 00 3d 02 0b 1f 1f 08 04 0f 00 00 37 00 03 1f 0f 07 03 0c 3b 05 4f 00 03 1f 1f 0c 04 0f 00 05 63 02 0b 3a 15 00 03 00 6a 00 07 04 00 63 32 00 00 00 32 00 54 69 6d 70 61 6e 69 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Timpani..2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 12 00 02 00 00 00 3d 02 0b 1f 1f 08 04 0f 00 00 37 00 03 1f 0f 07 03 0c 3b 00 4f 00 03 1f 1f 0c 04 0f 00 00 63 02 0b 3a 15 00 03 00 6a 00 07 04 00 63 32 00 00 00 32 00 54 69 6d 70 61 6e 69 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom 1 ** Source: Yamaha DX11 factory preset, bank D, voice 5 1f 05 0a 08 0e 00 00 5d 08 03 1f 0c 10 07 04 00 00 34 18 03 1f 0e 0a 06 00 00 00 51 20 03 1f 10 0a 07 0f 00 00 5e 04 03 01 23 00 00 00 62 00 04 04 00 28 32 00 00 00 32 00 54 6f 6d 20 20 20 20 20 20 31 63 17 23 32 2e 2e 08 05 08 04 09 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom 1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 05 0d 08 0e 00 00 5d 08 03 1f 0c 10 07 04 00 00 34 18 03 1f 0e 0a 06 00 00 00 51 20 03 1f 10 0d 07 0f 00 00 5e 04 03 01 23 00 00 00 62 00 04 04 00 28 32 00 00 00 32 00 54 6f 6d 20 20 20 20 20 20 31 63 17 23 32 2e 2e 08 05 08 04 09 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom 2 ** Controls: KV ** Source: Yamaha DX11 factory preset, bank D, voice 6 1f 1f 00 01 0f 00 00 63 00 03 1f 1f 00 01 0f 13 01 37 04 0b 1f 18 11 03 06 00 01 5e 04 0b 1f 1f 0b 06 0f 00 01 63 00 0b 3b 23 00 00 00 62 18 04 04 00 28 32 00 00 00 32 00 54 6f 6d 20 20 20 20 20 20 32 63 25 26 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom 3 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 7 1f 1f 00 01 0f 00 00 46 1b 03 1f 1f 00 01 0f 13 01 37 04 0b 1f 18 05 03 06 00 01 5e 03 0b 1f 14 0b 06 0e 00 01 63 00 0b 3b 63 00 63 00 63 18 04 04 00 28 32 00 00 00 32 00 54 6f 6d 20 20 20 20 20 20 33 63 25 26 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom 4 ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 8 1f 1f 00 01 0f 00 00 46 06 03 1f 1f 00 01 0f 13 01 37 00 0b 1f 18 05 03 06 00 01 5e 00 0b 1f 0b 1f 0a 0a 00 01 63 00 0b 23 63 00 63 00 63 18 04 04 00 28 32 00 00 00 32 00 54 6f 6d 20 20 20 20 20 20 34 63 25 26 32 2c 2c 00 00 00 02 00 52 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom 4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 00 00 46 06 03 1f 1f 00 01 0f 13 01 37 00 0b 1f 18 05 03 06 00 01 5e 00 0b 1f 0b 1f 0a 0d 00 01 63 00 0b 23 63 00 63 00 63 18 04 04 00 28 32 00 00 00 32 00 54 6f 6d 20 20 20 20 20 20 34 63 25 26 32 2c 2c 00 00 00 02 00 52 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom Tom..1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 1f 17 0f 0f 00 00 00 04 03 15 1f 1b 0f 0f 00 00 5d 02 06 18 1f 10 0f 0f 00 06 00 04 06 13 16 0d 0a 0f 00 04 63 00 08 03 23 00 00 00 62 18 0c 04 00 63 32 00 00 00 32 00 54 6f 6d 20 54 6f 6d 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom Tom..1 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 1f 17 0f 0f 00 00 00 04 03 15 1f 1b 0f 0f 00 00 5d 02 06 18 1f 10 0f 0f 00 00 00 04 06 13 16 0d 0a 0f 00 00 63 00 08 03 23 00 00 00 62 18 0c 04 00 63 32 00 00 00 32 00 54 6f 6d 20 54 6f 6d 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom Tom..2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 1f 1a 0f 0f 00 00 63 07 03 15 1f 1b 0f 0f 00 00 5d 02 06 18 1f 10 0f 0f 00 05 47 02 06 13 16 0d 0a 0f 00 05 63 00 08 03 23 00 00 00 62 18 0c 04 00 63 32 00 00 00 32 00 54 6f 6d 20 54 6f 6d 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom Tom..2 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 1f 1a 0f 0f 00 00 63 07 03 15 1f 1b 0f 0f 00 00 5d 02 06 18 1f 10 0f 0f 00 00 47 02 06 13 16 0d 0a 0f 00 00 63 00 08 03 23 00 00 00 62 18 0c 04 00 63 32 00 00 00 32 00 54 6f 6d 20 54 6f 6d 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom Tom..3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 18 1f 1a 0f 0f 00 00 63 01 03 15 1f 1b 0f 0f 00 00 63 01 16 18 1f 10 0f 0f 00 05 63 02 06 13 16 0d 0a 0f 00 05 63 01 10 04 23 00 00 00 62 18 0c 04 00 63 32 00 00 00 32 00 54 6f 6d 20 54 6f 6d 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom Tom..3 ** Source: from a library of patches collected by Duane Bowker, author unknown 18 1f 1a 0f 0f 00 00 63 01 03 15 1f 1b 0f 0f 00 00 63 01 16 18 1f 10 0f 0f 00 00 63 02 06 13 16 0d 0a 0f 00 00 63 01 10 04 23 00 00 00 62 18 0c 04 00 63 32 00 00 00 32 00 54 6f 6d 20 54 6f 6d 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom Tom..4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 0c 0a 0a 00 00 4d 0a 00 1f 13 0a 05 0d 32 00 4e 0c 03 1f 0d 0c 0a 0d 1f 05 59 10 03 1f 1c 0f 07 0f 00 05 63 0a 03 2c 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 54 6f 6d 20 54 6f 6d 2e 2e 34 63 63 63 32 32 32 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom Tom..4 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 0c 0a 0a 00 00 4d 02 00 1f 13 0a 05 0d 32 00 4e 0c 03 1f 0d 0c 0a 0d 1f 00 59 0e 03 1f 1c 0f 07 0f 00 00 63 09 03 2c 23 00 00 00 62 0c 04 04 00 63 32 00 00 00 32 00 54 6f 6d 20 54 6f 6d 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tom-Pany ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 16 1f 10 00 01 00 35 00 50 00 00 1f 16 04 03 09 36 00 63 00 00 13 0b 03 02 09 3b 00 3c 03 06 1d 0c 00 06 00 00 00 5a 00 06 1a 07 00 51 00 52 0c 04 04 00 28 32 00 00 00 32 00 54 6f 6d 2d 50 61 6e 79 20 20 63 40 43 32 18 16 00 50 00 03 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wood Drum ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 1f 07 0f 00 00 63 3f 03 18 1f 1f 07 0f 46 01 63 00 03 1f 1f 1f 06 0f 00 06 63 00 03 1b 1f 0f 06 0f 00 04 63 00 03 04 23 00 00 00 62 18 04 04 00 63 32 00 00 00 32 00 57 6f 6f 64 20 44 72 75 6d 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wood Drum ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 1f 07 0f 00 00 63 3f 03 18 1f 1f 07 0f 46 00 63 00 03 1f 1f 1f 06 0f 00 00 63 00 03 1b 1f 0f 06 0f 00 00 63 00 03 04 23 00 00 00 62 18 04 04 00 63 32 00 00 00 32 00 57 6f 6f 64 20 44 72 75 6d 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: XyloSpring ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 16 15 0f 05 2e 04 63 04 06 10 0c 0c 03 09 00 03 3f 01 01 1f 16 0f 06 03 00 06 63 08 03 1f 09 09 06 0c 00 06 62 00 02 7c 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 58 79 6c 6f 53 70 72 69 6e 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: XyloSpring ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 16 15 0f 05 2e 00 63 04 06 10 0c 0c 03 09 00 00 3f 01 01 1f 16 0f 06 03 00 00 63 08 03 1f 09 09 06 0c 00 00 62 00 02 7c 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 58 79 6c 6f 53 70 72 69 6e 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Other ** Name: Cabassa * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 15 00 0f 00 00 00 63 1c 03 1f 0d 00 07 00 00 41 63 24 03 1f 0d 00 07 00 00 42 63 38 03 1f 0d 00 06 00 00 42 63 24 03 7d 51 00 00 63 7f 30 02 04 00 28 63 00 00 00 32 00 43 61 62 61 73 73 61 20 20 2a 63 63 63 32 32 32 0d 00 0d 50 0c 00 0d 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Maracas * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 15 00 09 00 00 00 62 1c 03 1f 0d 00 07 00 00 41 63 3c 03 1f 0d 00 06 00 00 42 63 24 03 1f 0d 00 07 00 00 42 63 3c 03 7d 59 00 00 63 7f 30 02 04 00 28 63 00 00 00 32 00 4d 61 72 61 63 61 73 20 20 2a 63 63 63 32 32 32 0e 00 0d 43 0e 00 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Metalimba ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank D, voice 4 1f 17 00 05 05 1c 02 55 28 0b 1f 10 00 04 08 0a 02 4d 15 0e 1f 0b 00 05 08 0c 02 4d 16 08 1f 0e 00 06 00 00 01 63 04 0b 02 23 00 00 00 62 0c 07 04 00 63 63 00 00 00 32 00 4d 65 74 61 6c 69 6d 62 61 20 63 63 63 32 32 32 00 60 00 01 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tambourin* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 00 0f 00 00 01 63 04 03 1f 00 00 01 0f 00 01 59 24 03 1f 12 00 01 0f 00 00 45 04 03 18 10 1f 07 00 00 03 63 18 03 32 23 00 00 00 32 00 02 04 00 28 63 00 00 00 32 00 54 61 6d 62 6f 75 72 69 6e 2a 63 63 63 32 32 32 0b 05 0e 09 08 00 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 27 * CATEGORY NOISES * SUBCATEGORY Noises1 ** Name: Airplane.1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 04 02 08 0f 00 45 4d 04 03 13 12 11 03 0c 03 45 63 00 03 0f 04 03 08 0f 00 46 63 04 03 0c 12 03 03 0f 02 46 5f 00 03 0c 63 00 00 63 27 18 02 04 00 63 63 00 00 00 32 00 41 69 72 70 6c 61 6e 65 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Airplane.1 ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 04 02 08 0f 00 40 4d 04 03 13 12 11 03 0c 03 40 63 00 03 0f 04 03 08 0f 00 40 63 04 03 0c 12 03 03 0f 02 40 5f 00 03 0c 63 00 00 63 27 18 02 04 00 63 63 00 00 00 32 00 41 69 72 70 6c 61 6e 65 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Airplane.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 04 02 08 0f 00 44 56 09 03 13 12 11 03 0c 03 44 63 09 03 0f 04 03 08 0f 00 44 63 04 03 0c 12 03 03 0f 02 47 5f 00 03 0c 63 00 00 63 27 18 02 04 00 63 63 00 00 00 32 00 41 69 72 70 6c 61 6e 65 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Airplane.2 ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 04 02 08 0f 00 40 56 09 03 13 12 11 03 0c 03 40 63 09 03 0f 04 03 08 0f 00 40 63 04 03 0c 12 03 03 0f 02 40 5f 00 03 0c 63 00 00 63 27 18 02 04 00 63 63 00 00 00 32 00 41 69 72 70 6c 61 6e 65 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Alarm Call ** Controls: MW (LFO amplitude) ** Source: Yamaha TX81Z factory preset, bank D, voice 18 1f 1f 00 03 0f 00 00 2e 3f 00 1f 1f 00 04 0f 00 00 47 11 06 1f 1f 00 06 0f 00 40 3a 0b 00 1f 1f 00 06 0f 00 40 5c 3f 06 22 37 00 00 28 6d 18 04 04 00 63 01 63 00 00 32 00 41 6c 61 72 6d 20 43 61 6c 6c 63 63 63 32 32 32 00 40 0e 50 00 00 0a 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Alien 3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 05 02 04 0e 00 00 4d 08 01 19 07 04 05 0f 08 00 4c 06 00 0f 0a 00 07 0e 06 02 63 05 05 14 15 03 05 0e 00 01 62 06 00 24 26 03 05 00 46 05 03 04 00 63 2b 00 00 00 32 00 41 6c 69 65 6e 20 33 20 20 20 63 63 63 32 32 32 01 31 00 21 00 23 00 45 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Alien Chat ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0a 0c 01 06 0e 00 43 28 04 05 16 10 1f 05 0d 00 44 2c 04 02 16 0f 1f 06 0d 00 45 46 0a 06 15 04 1f 06 0e 00 45 63 08 03 42 32 00 00 63 05 18 02 04 00 28 02 00 00 00 32 00 41 6c 69 65 6e 20 43 68 61 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Alien Chat ** Source: from a library of patches collected by Duane Bowker, author unknown 0a 0c 01 06 0e 00 40 28 04 05 16 10 1f 05 0d 00 40 2c 04 02 16 0f 1f 06 0d 00 40 46 0a 06 15 04 1f 06 0e 00 40 63 08 03 42 32 00 00 63 05 18 02 04 00 28 02 00 00 00 32 00 41 6c 69 65 6e 20 43 68 61 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Artofnoiz* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 0a 08 02 07 00 05 5e 0d 0b 1f 03 00 01 00 00 00 63 00 0b 1b 04 0a 01 07 00 03 57 18 0b 1f 13 04 06 0f 00 00 63 00 13 03 23 00 00 00 32 00 02 04 00 28 63 00 00 00 32 00 41 72 74 6f 66 6e 6f 69 7a 2a 63 63 63 32 32 32 00 00 08 00 08 04 08 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AutoDecent ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 07 1b 0d 0d 0f 00 03 63 11 00 0c 1b 0d 0d 0f 00 04 63 13 03 0a 1b 0d 0d 0f 00 04 63 12 06 1f 1b 0d 0d 0f 00 06 63 19 03 47 25 00 00 00 32 00 02 04 00 28 02 00 00 00 32 00 41 75 74 6f 44 65 63 65 6e 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AutoDecent ** Source: from a library of patches collected by Duane Bowker, author unknown 07 1b 0d 0d 0f 00 00 63 11 00 0c 1b 0d 0d 0f 00 00 63 13 03 0a 1b 0d 0d 0f 00 00 63 12 06 1f 1b 0d 0d 0f 00 00 63 19 03 47 25 00 00 00 32 00 02 04 00 28 02 00 00 00 32 00 41 75 74 6f 44 65 63 65 6e 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AutoStorm1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 07 01 03 05 00 03 30 0a 00 14 0b 04 01 09 00 04 4d 15 00 14 06 01 03 05 00 04 53 00 00 0f 09 00 01 0c 00 05 63 00 00 2b 63 00 40 00 73 00 05 04 00 63 63 63 00 00 32 00 41 75 74 6f 53 74 6f 72 6d 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AutoStorm1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 07 01 03 05 00 00 30 0a 00 14 0b 04 01 09 00 00 4d 15 00 14 06 01 03 05 00 00 53 00 00 0f 09 00 01 0c 00 00 63 00 00 2b 63 00 40 00 73 00 05 04 00 63 63 63 00 00 32 00 41 75 74 6f 53 74 6f 72 6d 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AutoStorm2 ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 07 09 03 05 00 43 33 0d 00 12 0b 09 01 09 00 43 4d 15 00 18 06 09 03 05 00 44 53 00 00 13 09 09 01 0c 00 45 63 00 00 5b 50 00 63 11 77 00 05 04 00 63 63 63 00 00 32 00 41 75 74 6f 53 74 6f 72 6d 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: AutoStorm2 ** Controls: MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 07 09 03 05 00 40 33 0d 00 12 0b 09 01 09 00 40 4d 15 00 18 06 09 03 05 00 40 53 00 00 13 09 09 01 0c 00 40 63 00 00 5b 50 00 63 11 77 00 05 04 00 63 63 63 00 00 32 00 41 75 74 6f 53 74 6f 72 6d 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Big Belch ** Controls: KV, MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 0c 0b 0b 00 44 42 0d 03 1f 1b 07 09 0c 00 43 54 0d 1b 1f 1c 0c 0b 0c 00 44 63 0d 03 1f 1b 07 09 0f 00 45 63 00 1b 74 59 00 63 23 75 0c 07 04 00 63 32 01 32 00 32 00 42 69 67 20 42 65 6c 63 68 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Big Belch ** Controls: MW (LFO pitch), MW (LFO amplitude), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 0c 0b 0b 00 40 42 0d 03 1f 1b 07 09 0c 00 40 54 0d 1b 1f 1c 0c 0b 0c 00 40 63 0d 03 1f 1b 07 09 0f 00 40 63 00 1b 74 59 00 63 23 75 0c 07 04 00 63 32 01 32 00 32 00 42 69 67 20 42 65 6c 63 68 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Birds * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 0f 0f 00 00 00 0c 03 1c 00 00 01 0f 00 00 55 20 03 1b 12 00 01 0f 00 00 63 00 03 10 15 00 09 0e 00 03 63 00 03 02 23 00 00 00 32 00 02 04 00 28 63 00 00 00 32 00 42 69 72 64 73 20 20 20 20 2a 63 63 63 32 32 32 0f 09 0e 09 08 01 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Birds ** Controls: MW (LFO pitch), MW (LFO amplitude), BC (voice pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 12 1f 1f 00 07 0f 28 00 51 01 03 1f 1f 00 0f 0f 28 00 4b 01 03 13 1f 00 0e 0f 0a 40 63 3f 03 1f 1f 00 0f 0f 0a 40 63 3f 03 04 22 00 50 5e 6e 0c 0c 04 00 63 63 63 00 00 33 00 42 69 72 64 73 20 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BlendOrgan ** Description: Strange, loud, obnoxious sound ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 00 00 0a 0f 09 00 45 0e 03 19 00 00 08 0f 23 00 61 07 08 13 00 00 08 0f 00 00 5f 0b 01 12 00 00 05 0f 00 00 63 02 0d 3c 1f 00 23 00 42 0c 06 04 00 63 3d 00 1d 00 32 00 42 6c 65 6e 64 4f 72 67 61 6e 63 63 63 32 32 32 00 00 00 20 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Blow Up ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 04 04 06 02 00 3b 62 00 03 0f 06 04 06 0d 00 1d 63 00 1b 13 15 15 06 09 00 1a 63 04 1b 0f 04 04 09 0c 00 3f 63 0d 03 3a 00 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 42 6c 6f 77 20 55 70 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Blow Up ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 04 04 06 02 00 38 62 00 03 0f 06 04 06 0d 00 18 63 00 1b 13 15 15 06 09 00 18 63 04 1b 0f 04 04 09 0c 00 38 63 0d 03 3a 00 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 42 6c 6f 77 20 55 70 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Boingggggg ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 1b 03 00 00 42 4b 1d 15 1f 07 0c 04 00 00 44 32 16 15 1f 07 1c 03 00 00 44 3a 38 13 1f 07 0c 0d 00 00 45 63 08 10 2b 11 00 49 5a 74 00 02 04 00 63 63 00 00 00 32 00 42 6f 69 6e 67 67 67 67 67 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Boingggggg ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 1b 03 00 00 40 4b 1d 15 1f 07 0c 04 00 00 40 32 16 15 1f 07 1c 03 00 00 40 3a 38 13 1f 07 0c 0d 00 00 40 63 08 10 2b 11 00 49 5a 74 00 02 04 00 63 63 00 00 00 32 00 42 6f 69 6e 67 67 67 67 67 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrokWindow ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 06 06 06 00 03 3f 10 03 1f 0d 00 05 0b 00 03 50 31 03 1f 1f 00 08 0f 00 05 39 3f 03 1f 1f 0f 07 0f 00 06 63 10 03 2a 34 00 37 49 73 1a 05 04 00 63 32 00 00 00 32 00 42 72 6f 6b 57 69 6e 64 6f 77 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BrokWindow ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 06 06 06 00 00 3f 10 03 1f 0d 00 05 0b 00 00 50 31 03 1f 1f 00 08 0f 00 00 39 3f 03 1f 1f 0f 07 0f 00 00 63 10 03 2a 34 00 37 49 73 1a 05 04 00 63 32 00 00 00 32 00 42 72 6f 6b 57 69 6e 64 6f 77 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bubbly ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 17 06 00 04 0e 0a 04 3a 01 00 1f 1f 1f 0f 0f 00 04 00 04 00 1f 1f 1f 0f 0f 00 43 41 01 0b 18 0d 0d 06 00 00 45 63 0a 13 7b 32 00 63 43 39 18 02 04 00 28 02 00 00 00 32 00 42 75 62 62 6c 79 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Bubbly ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 17 06 00 04 0e 0a 00 3a 01 00 1f 1f 1f 0f 0f 00 00 00 04 00 1f 1f 1f 0f 0f 00 40 41 01 0b 18 0d 0d 06 00 00 40 63 0a 13 7b 32 00 63 43 39 18 02 04 00 28 02 00 00 00 32 00 42 75 62 62 6c 79 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Busy * ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 00 00 04 03 1f 1f 00 0f 0f 00 40 5f 34 03 1f 1f 00 0f 0f 00 40 3a 20 03 1f 1f 00 0f 0f 00 40 63 2c 03 46 13 46 00 63 4d 18 04 04 00 28 32 00 00 00 32 00 42 75 73 79 20 20 20 20 20 2a 63 63 63 32 32 32 00 00 09 0a 09 07 09 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Busy.....1 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 05 0f 00 43 32 04 08 1f 1f 00 05 0f 00 43 34 00 0e 1f 1f 00 05 0f 00 46 40 08 08 1f 1f 00 05 0f 00 45 63 00 0e 02 0f 00 00 5a 0d 18 04 04 00 63 01 63 00 00 32 00 42 75 73 79 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Busy.....1 ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 05 0f 00 40 32 04 08 1f 1f 00 05 0f 00 40 34 00 0e 1f 1f 00 05 0f 00 40 40 08 08 1f 1f 00 05 0f 00 40 63 00 0e 02 0f 00 00 5a 0d 18 04 04 00 63 01 63 00 00 32 00 42 75 73 79 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Busy.....2 ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 05 0f 00 40 63 04 08 1f 1f 00 05 0f 00 40 63 00 0e 1f 1f 00 05 0f 00 40 63 08 08 1f 1f 00 05 0f 00 40 63 00 0e 07 0f 00 00 63 0d 18 04 04 00 63 01 63 00 00 32 00 42 75 73 79 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choo Choo ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 10 11 08 0f 00 03 48 04 15 1c 1f 1f 06 0f 00 04 63 18 1d 1c 10 03 06 0f 00 05 63 08 1b 10 0c 03 09 0f 00 46 63 03 10 7a 63 00 10 54 06 18 02 04 00 63 63 00 00 00 32 00 43 68 6f 6f 20 43 68 6f 6f 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Choo Choo ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 10 11 08 0f 00 00 48 04 15 1c 1f 1f 06 0f 00 00 63 18 1d 1c 10 03 06 0f 00 00 63 08 1b 10 0c 03 09 0f 00 40 63 03 10 7a 63 00 10 54 06 18 02 04 00 63 63 00 00 00 32 00 43 68 6f 6f 20 43 68 6f 6f 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chopper..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0f 0a 43 63 3f 03 1f 1f 00 01 0f 00 43 63 29 03 1f 1f 00 03 0f 00 45 63 35 03 0b 0b 00 07 0f 00 45 63 00 03 38 2a 00 42 5a 64 18 02 04 00 28 02 00 00 00 32 00 43 68 6f 70 70 65 72 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chopper..1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 0a 40 63 3f 03 1f 1f 00 01 0f 00 40 63 29 03 1f 1f 00 03 0f 00 40 63 35 03 0b 0b 00 07 0f 00 40 63 00 03 38 2a 00 42 5a 64 18 02 04 00 28 02 00 00 00 32 00 43 68 6f 70 70 65 72 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chopper..1 ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0f 0a 40 63 3f 03 1f 1f 00 00 0f 00 40 63 29 03 1f 1f 00 03 0f 00 40 63 35 03 0b 0b 00 07 0f 00 40 63 00 03 38 2a 00 42 5a 64 18 02 04 00 28 02 00 00 00 32 00 43 68 6f 70 70 65 72 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chopper..2 ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 02 0f 00 44 50 0a 04 1f 1f 00 03 0f 00 46 56 05 02 1f 1f 00 03 0f 00 44 4e 08 04 1f 1f 00 03 0f 00 46 5f 02 04 6b 33 00 00 63 7c 0c 07 04 00 63 32 01 32 00 32 00 43 68 6f 70 70 65 72 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Chopper..2 ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 02 0f 00 40 50 0a 04 1f 1f 00 03 0f 00 40 56 05 02 1f 1f 00 03 0f 00 40 4e 08 04 1f 1f 00 03 0f 00 40 5f 02 04 6b 33 00 00 63 7c 0c 07 04 00 63 32 01 32 00 32 00 43 68 6f 70 70 65 72 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cricket * ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0f 00 00 5a 2c 03 1f 1f 00 01 0f 00 00 5a 28 03 1c 1f 00 04 0f 00 40 52 30 03 1d 1f 00 04 0f 00 40 52 34 03 24 20 00 00 63 0d 18 04 04 00 28 32 00 00 00 32 00 43 72 69 63 6b 65 74 20 20 2a 63 63 63 32 32 32 08 0a 08 02 0d 0f 0d 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Diving Fly ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0d 1f 00 08 0f 00 44 36 06 03 0d 1f 00 03 0f 00 44 63 04 03 0d 1f 00 04 0f 00 46 63 05 03 0d 1f 00 03 0f 00 47 63 04 03 4d 63 00 00 31 7e 24 05 04 00 63 63 63 00 00 32 00 44 69 76 69 6e 67 20 46 6c 79 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Diving Fly ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 0d 1f 00 08 0f 00 40 36 06 03 0d 1f 00 03 0f 00 40 63 04 03 0d 1f 00 04 0f 00 40 63 05 03 0d 1f 00 03 0f 00 40 63 04 03 4d 63 00 00 31 7e 24 05 04 00 63 63 63 00 00 32 00 44 69 76 69 6e 67 20 46 6c 79 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Dynamite ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 09 02 05 00 3c 4f 04 03 1f 03 0d 04 00 00 1c 38 04 03 1b 0a 0d 03 0f 00 1d 49 3f 03 1f 03 0d 04 00 00 3e 5a 04 03 3a 55 00 2d 00 72 00 02 04 00 63 32 00 00 00 32 00 44 79 6e 61 6d 69 74 65 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Dynamite ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 08 09 02 05 00 38 4f 04 03 1f 03 0d 04 00 00 18 38 04 03 1b 0a 0d 03 0f 00 18 49 3f 03 1f 03 0d 04 00 00 38 5a 04 03 3a 55 00 2d 00 72 00 02 04 00 63 32 00 00 00 32 00 44 79 6e 61 6d 69 74 65 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: EarthQuake ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 02 09 05 00 00 3c 56 00 00 0f 06 12 03 0c 00 1c 3a 04 1b 13 15 15 03 09 00 1c 3e 00 1b 0f 04 0a 07 0e 00 3f 5d 04 03 3a 00 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 45 61 72 74 68 51 75 61 6b 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: EarthQuake ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 02 09 05 00 00 38 56 00 00 0f 06 12 03 0c 00 18 3a 04 1b 13 15 15 03 09 00 18 3e 00 1b 0f 04 0a 07 0e 00 38 5d 04 03 3a 00 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 45 61 72 74 68 51 75 61 6b 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Earthquake ** Controls: KV, MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 28 17 0c 05 01 0e 03 43 4c 0a 0b 15 06 06 01 0e 00 40 56 07 0b 10 0b 05 01 0a 00 40 4c 0a 03 17 07 06 04 0f 00 41 5d 00 0b 38 63 00 0c 00 67 00 04 04 00 28 32 00 00 00 32 00 45 61 72 74 68 71 75 61 6b 65 63 63 12 32 32 00 00 0a 00 01 00 03 00 00 00 00 00 00 00 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Earthquake ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 17 0c 05 01 0e 03 43 4c 0d 0b 15 06 06 01 0e 00 40 56 07 0b 10 0b 05 01 0d 00 40 4c 0a 03 17 07 06 04 0f 00 41 5d 00 0b 38 63 00 0c 00 67 00 04 04 00 28 32 00 00 00 32 00 45 61 72 74 68 71 75 61 6b 65 63 63 12 32 32 00 00 0d 00 01 00 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Eng Siren ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 05 0f 23 04 2b 08 03 1f 1f 00 02 0f 00 04 4d 08 03 1f 1f 00 05 0f 0a 05 5d 04 0b 15 1f 00 05 0f 05 06 63 04 03 04 0d 00 63 00 61 18 05 04 00 63 63 28 00 00 32 00 45 6e 67 20 53 69 72 65 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Eng Siren ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 05 0f 23 00 2b 08 03 1f 1f 00 02 0f 00 00 4d 08 03 1f 1f 00 05 0f 0a 00 5d 04 0b 15 1f 00 05 0f 05 00 63 04 03 04 0d 00 63 00 61 18 05 04 00 63 63 28 00 00 32 00 45 6e 67 20 53 69 72 65 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Excuse Me ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1b 03 07 04 0e 4d 04 5b 04 03 12 1f 09 0a 0f 00 04 43 08 03 12 1f 0b 0a 0f 4b 05 55 08 03 12 1f 08 0a 0f 01 05 5f 04 03 23 00 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 45 78 63 75 73 65 20 4d 65 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Excuse Me ** Source: from a library of patches collected by Duane Bowker, author unknown 1b 03 07 04 0e 4d 00 5b 04 03 12 1f 09 0a 0f 00 00 43 08 03 12 1f 0b 0a 0f 4b 00 55 08 03 12 1f 08 0a 0f 01 00 5f 04 03 23 00 00 00 00 02 00 02 04 00 63 32 00 00 00 32 00 45 78 63 75 73 65 20 4d 65 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Exo/Bird * ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 00 00 04 03 13 08 08 04 0f 00 00 51 0a 03 0d 03 01 04 0f 00 00 5b 04 03 11 0d 1f 07 00 00 00 63 00 03 00 23 00 00 00 62 0e 04 04 00 28 32 00 00 00 32 00 45 78 6f 2f 42 69 72 64 20 2a 63 63 63 32 32 32 00 00 00 00 08 02 08 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Explosion ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 09 04 07 0f 00 43 5f 10 08 10 09 04 07 0f 00 44 63 0d 0e 10 09 04 07 0f 00 43 4e 06 08 10 09 04 07 0f 00 46 2c 04 08 05 47 00 63 63 37 18 04 04 00 63 01 63 00 00 32 00 45 78 70 6c 6f 73 69 6f 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Explosion ** Controls: MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 09 04 07 0f 00 40 5f 10 08 10 09 04 07 0f 00 40 63 0d 0e 10 09 04 07 0f 00 40 4e 06 08 10 09 04 07 0f 00 40 2c 04 08 05 47 00 63 63 37 18 04 04 00 63 01 63 00 00 32 00 45 78 70 6c 6f 73 69 6f 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FaucetDrip ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 08 1f 05 0d 0a 05 39 00 03 1f 0a 1f 0a 0f 00 06 63 04 03 1f 09 1f 0a 0f 00 02 63 08 03 1f 0e 1f 0a 0d 00 05 63 04 03 7d 39 00 63 00 70 18 02 04 00 28 02 00 00 00 32 00 46 61 75 63 65 74 44 72 69 70 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FaucetDrip ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 08 1f 05 0d 0a 00 39 00 03 1f 0a 1f 0a 0f 00 00 63 04 03 1f 09 1f 0a 0f 00 00 63 08 03 1f 0e 1f 0a 0d 00 00 63 04 03 7d 39 00 63 00 70 18 02 04 00 28 02 00 00 00 32 00 46 61 75 63 65 74 44 72 69 70 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Feedback * ** Description: A raspy sound ** Controls: MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 00 07 0f 30 00 40 13 01 1f 14 05 08 0b 00 00 61 04 00 1f 1f 00 07 0f 05 00 49 04 04 1c 1f 01 08 0e 00 00 63 13 06 39 1d 14 06 00 62 24 0c 04 00 28 32 00 00 00 36 00 46 65 65 64 62 61 63 6b 20 2a 63 63 63 32 32 32 00 00 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Flight Sim ** Source: Yamaha TX81Z factory preset, bank D, voice 15 1f 1f 00 04 0f 00 00 49 04 03 1f 1f 00 01 0f 32 00 59 04 03 1f 1f 00 05 0f 3b 40 43 10 03 0e 1f 00 06 0f 00 00 5c 0d 03 3a 39 00 00 63 6a 00 0c 0c 63 63 32 00 00 00 32 00 46 6c 69 67 68 74 20 53 69 6d 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 2d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gizmo * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 0d 05 06 0f 1e 01 3f 10 06 1a 1a 05 05 0a 36 01 63 07 0e 0f 1a 04 05 0f 13 01 63 00 10 1f 13 07 07 0f 00 01 63 04 0e 3b 14 00 00 00 42 18 02 04 00 63 32 00 32 00 32 00 47 69 7a 6d 6f 20 20 20 20 2a 63 63 63 32 32 32 00 00 00 27 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GrandPrix* ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0f 00 00 43 04 00 1f 1f 00 01 0f 00 00 4b 04 00 1f 1f 00 01 0f 00 00 4e 04 06 1f 1f 00 07 0f 00 00 63 04 03 00 63 00 63 00 73 00 0c 0c 00 28 63 00 00 00 32 00 47 72 61 6e 64 50 72 69 78 2a 63 63 63 32 32 32 00 0c 00 20 00 04 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GuiRoach:: ** Controls: KV ** Source: Yamaha DX11 factory preset, bank D, voice 25 0f 00 0b 0a 0a 00 40 5b 2d 03 1f 00 00 03 0f 00 40 61 00 03 1f 00 00 03 0f 00 40 4c 2d 03 0e 0b 0a 0f 0e 00 43 5c 00 03 3a 61 00 00 34 0a 18 04 04 00 28 32 00 00 00 32 00 47 75 69 52 6f 61 63 68 3a 3a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GuiRoach:: ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 00 0b 0a 0d 00 40 5b 2d 03 1f 00 00 03 0f 00 40 61 00 03 1f 00 00 03 0f 00 40 4c 2d 03 0e 0b 0d 0f 0e 00 43 5c 00 03 3a 61 00 00 34 0d 18 04 04 00 28 32 00 00 00 32 00 47 75 69 52 6f 61 63 68 3a 3a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gurgle ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 10 1f 11 00 01 0f 47 00 44 06 06 19 0b 01 01 0f 00 40 55 00 05 01 10 00 06 0f 18 00 63 08 00 02 03 00 06 0f 00 00 63 0c 01 3c 27 36 1b 44 7f 0c 07 04 00 63 63 00 32 00 32 00 47 75 72 67 6c 65 20 20 20 20 63 63 63 32 32 32 1b 00 00 00 08 70 08 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hard Rain ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 04 00 06 0f 2e 03 63 1c 02 1f 06 16 06 0f 00 04 30 31 0b 1f 15 00 06 0f 00 05 63 04 0a 12 04 00 09 0f 00 04 5a 04 06 3a 21 00 63 00 02 00 02 04 00 63 32 00 32 00 32 00 48 61 72 64 20 52 61 69 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hard Rain ** Source: from a library of patches collected by Duane Bowker, author unknown 12 04 00 06 0f 2e 00 63 1c 02 1f 06 16 06 0f 00 00 30 31 0b 1f 15 00 06 0f 00 00 63 04 0a 12 04 00 09 0f 00 00 5a 04 06 3a 21 00 63 00 02 00 02 04 00 63 32 00 32 00 32 00 48 61 72 64 20 52 61 69 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harley Hog ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 43 5b 09 13 1f 1f 00 02 0f 00 44 13 08 0e 1f 1f 06 09 0f 00 45 5a 00 0b 1f 1f 00 04 0f 00 45 51 05 03 7a 63 00 63 63 34 00 00 04 00 63 32 00 32 00 32 00 48 61 72 6c 65 79 20 48 6f 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Harley Hog ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 0f 0f 00 40 5b 09 13 1f 1f 00 02 0f 00 40 13 08 0e 1f 1f 06 09 0f 00 40 5a 00 0b 1f 1f 00 04 0f 00 40 51 05 03 7a 63 00 63 63 34 00 00 04 00 63 32 00 32 00 32 00 48 61 72 6c 65 79 20 48 6f 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Heart Beat ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 18 0f 0f 00 03 63 04 03 15 12 12 08 0c 00 04 34 04 03 07 13 12 08 0f 00 04 63 00 1b 15 13 12 08 0c 00 05 63 00 03 44 00 00 00 00 02 0c 02 04 00 28 02 00 00 00 32 00 48 65 61 72 74 20 42 65 61 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Heart Beat ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 18 0f 0f 00 00 63 04 03 15 12 12 08 0c 00 00 34 04 03 07 13 12 08 0f 00 00 63 00 1b 15 13 12 08 0c 00 00 63 00 03 44 00 00 00 00 02 0c 02 04 00 28 02 00 00 00 32 00 48 65 61 72 74 20 42 65 61 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Helicopter ** Controls: MW (LFO amplitude) ** Source: Yamaha TX81Z factory preset, bank D, voice 14 0d 1f 00 01 0f 28 40 5c 00 03 1f 1f 00 01 0f 28 40 62 00 03 0e 1f 00 04 0f 0a 40 63 29 03 0e 1f 00 04 0f 0a 00 63 16 03 3c 33 00 00 5b 6e 18 0c 04 00 63 63 63 00 00 32 00 48 65 6c 69 63 6f 70 74 65 72 63 63 63 32 32 32 00 00 00 20 08 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hi Alarm ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 04 0f 00 04 31 10 00 1f 1f 00 04 0f 00 04 43 13 06 1f 1f 00 04 0f 00 44 37 0a 00 1f 1f 00 06 0f 00 45 63 1f 06 22 38 00 00 32 6d 18 04 04 00 63 01 63 00 00 32 00 48 69 20 41 6c 61 72 6d 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hi Alarm ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 04 0f 00 00 31 10 00 1f 1f 00 04 0f 00 00 43 13 06 1f 1f 00 04 0f 00 40 37 0a 00 1f 1f 00 06 0f 00 40 63 1f 06 22 38 00 00 32 6d 18 04 04 00 63 01 63 00 00 32 00 48 69 20 41 6c 61 72 6d 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Hole in 1 ** Source: Yamaha TX81Z factory preset, bank D, voice 11 1f 14 00 01 0b 00 40 54 11 03 15 07 00 0f 0e 2e 00 33 27 0b 14 0c 11 05 0a 00 40 58 0d 0a 1f 12 10 0b 00 01 00 62 10 0b 3c 3a 00 63 50 5f 18 05 04 00 63 00 00 00 00 32 00 48 6f 6c 65 20 69 6e 20 31 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: House Fly ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 05 06 04 0c 00 04 4b 1a 03 07 0c 06 04 0c 00 04 3e 1a 06 1f 1f 00 0f 0f 00 05 3f 00 03 1f 1f 00 0f 0f 00 07 63 00 03 7c 2f 00 00 00 31 00 05 04 00 63 63 63 00 00 32 00 48 6f 75 73 65 20 46 6c 79 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: House Fly ** Source: from a library of patches collected by Duane Bowker, author unknown 09 05 06 04 0c 00 00 4b 1a 03 07 0c 06 04 0c 00 00 3e 1a 06 1f 1f 00 0f 0f 00 00 3f 00 03 1f 1f 00 0f 0f 00 00 63 00 03 7c 2f 00 00 00 31 00 05 04 00 63 63 63 00 00 32 00 48 6f 75 73 65 20 46 6c 79 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HowlAtMoon ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 0a 06 0c 0f 0a 02 33 0f 03 12 10 06 0a 0d 00 04 3d 1c 0b 0f 09 03 09 0e 00 04 52 0d 0b 0e 0c 09 0a 0f 00 05 5d 0c 0c 3c 19 2a 00 00 22 00 02 04 00 28 02 00 00 00 32 00 48 6f 77 6c 41 74 4d 6f 6f 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HowlAtMoon ** Source: from a library of patches collected by Duane Bowker, author unknown 12 0a 06 0c 0f 0a 00 33 0f 03 12 10 06 0a 0d 00 00 3d 1c 0b 0f 09 03 09 0e 00 00 52 0d 0b 0e 0c 09 0a 0f 00 00 5d 0c 0c 3c 19 2a 00 00 22 00 02 04 00 28 02 00 00 00 32 00 48 6f 77 6c 41 74 4d 6f 6f 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lil'Birdie ** Controls: KV, MW (LFO pitch), BC (LFO pitch), BC (EG amplitude), BC (voice pitch), FC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 08 0d 17 07 0f 5d 19 09 2e 0b 02 09 0a 08 05 25 1f 63 0c 1c 0c 1d 0c 0d 07 0c 0a 4b 34 0c 1f 04 0e 02 0b 2c 00 1e 0c 08 04 63 43 20 4a 42 18 0b 00 26 51 29 1f 51 2f 25 1b 4c 69 6c 27 42 69 72 64 69 65 00 00 00 00 00 00 2a 29 02 5c 34 18 39 48 04 15 1c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lo Alarm ** Controls: KV, MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 03 0f 00 03 31 03 00 1f 1f 00 04 0f 00 04 43 18 06 1f 1f 00 06 0f 00 45 37 07 00 1f 1f 00 06 0f 00 44 63 07 06 52 38 00 00 27 65 00 04 04 00 63 01 63 00 00 32 00 4c 6f 20 41 6c 61 72 6d 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Lo Alarm ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 03 0f 00 00 31 03 00 1f 1f 00 04 0f 00 00 43 18 06 1f 1f 00 06 0f 00 40 37 07 00 1f 1f 00 06 0f 00 40 63 07 06 52 38 00 00 27 65 00 04 04 00 63 01 63 00 00 32 00 4c 6f 20 41 6c 61 72 6d 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MT:Apr'88 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 04 00 02 0f 0f 00 44 04 03 1f 09 03 02 0f 15 01 3a 08 00 1f 00 00 0b 0f 0c 02 44 07 06 1a 0a 00 05 0f 05 01 5c 08 13 3c 18 06 10 00 5a 0c 04 04 00 63 32 00 00 00 32 00 4d 54 3a 41 70 72 27 38 38 20 63 63 63 32 32 32 20 00 30 30 10 04 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MachineGun ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1a 15 12 09 0f 00 40 63 00 00 19 09 08 09 0f 00 40 54 08 03 1f 15 1f 0a 0f 00 45 63 06 03 19 15 03 09 0f 00 45 63 00 0b 7c 40 00 63 63 74 18 02 04 00 28 02 00 00 00 32 00 4d 61 63 68 69 6e 65 47 75 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MachineGun ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1a 15 12 09 0f 00 40 63 00 00 19 09 08 09 0f 00 40 54 08 03 1f 15 1f 0a 0f 00 40 63 06 03 19 15 03 09 0f 00 40 63 00 0b 7c 40 00 63 63 74 18 02 04 00 28 02 00 00 00 32 00 4d 61 63 68 69 6e 65 47 75 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MalibuNite ** Controls: MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 13 1f 1f 00 01 0f 00 00 63 3f 03 1f 1f 00 01 0f 00 00 63 29 03 1f 1f 00 03 0f 00 00 63 35 03 0b 16 00 07 0f 00 40 5b 00 03 38 05 00 42 5a 6e 00 04 04 00 63 32 00 00 00 32 00 4d 61 6c 69 62 75 4e 69 74 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Med Musak ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 07 11 00 01 09 00 45 4f 00 00 01 08 00 01 09 05 01 62 0a 03 01 07 00 01 08 08 41 62 23 06 01 08 00 01 0a 00 43 63 00 00 3f 21 00 35 1c 6f 14 0c 04 1e 63 32 00 00 00 32 00 4d 65 64 20 4d 75 73 61 6b 20 63 63 63 32 32 32 30 37 20 1f 20 21 00 22 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mouse-Tom ** Source: Yamaha DX11 factory preset, bank D, voice 18 1f 1f 0c 07 0f 00 00 5c 32 03 1f 14 1f 09 01 00 00 63 00 0b 1f 1f 1f 0f 0f 0c 00 3e 0b 1b 1f 1f 0e 0f 0f 00 00 5c 05 0b 3b 23 00 00 00 62 24 04 04 00 63 32 00 00 00 32 00 4d 6f 75 73 65 2d 54 6f 6d 20 63 63 63 32 32 32 08 00 00 03 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Mr.Mr.Noiz ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 13 03 00 00 00 5f 3d 06 14 12 13 02 00 00 01 52 05 02 17 14 13 03 00 00 01 56 28 05 16 0b 13 0f 09 00 00 63 06 03 3b 63 00 63 00 73 10 07 04 00 63 32 00 00 00 32 00 4d 72 2e 4d 72 2e 4e 6f 69 7a 63 63 63 32 32 32 08 00 00 00 08 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Noise Shot ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 27 1f 1f 13 03 00 00 00 5f 3d 06 1f 12 13 02 00 00 01 52 05 02 1f 14 13 03 00 00 01 56 28 05 1e 0b 13 0f 09 00 00 63 06 03 3b 63 00 63 00 73 23 07 04 00 63 32 00 00 00 32 00 4e 6f 69 73 65 20 53 68 6f 74 63 63 63 32 32 32 08 00 00 00 08 00 00 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ofthewall* ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 08 00 07 00 00 00 63 00 03 08 1f 00 09 0f 00 00 43 00 05 0b 1f 00 05 0f 00 01 63 0a 06 0c 1f 00 06 0f 00 02 63 04 00 64 4f 2e 63 63 62 18 02 04 00 28 63 00 00 00 32 00 4f 66 74 68 65 77 61 6c 6c 2a 63 63 63 32 32 32 08 20 08 30 00 50 00 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OldClock ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 02 04 0f 00 43 3c 21 0e 1f 19 08 03 0f 63 07 63 17 08 1f 1f 0a 04 0a 5a 07 63 0a 0a 1f 1f 07 03 0f 00 04 63 0a 08 3b 0e 00 00 00 56 0f 04 04 00 63 37 00 00 00 32 00 4f 6c 64 43 6c 6f 63 6b 20 20 63 63 63 32 32 32 04 00 08 08 08 1b 07 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OutOfTune ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0f 03 09 06 0e 0a 03 3c 00 03 1f 03 03 06 0d 00 04 60 04 02 1f 04 02 06 0d 00 04 62 0a 03 1f 02 02 06 0d 00 06 63 04 03 3d 10 00 59 00 52 18 02 04 00 28 02 00 00 00 32 00 4f 75 74 4f 66 54 75 6e 65 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OutOfTune ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0f 03 09 06 0e 0a 00 3c 00 03 1f 03 03 06 0d 00 00 60 04 02 1f 04 02 06 0d 00 00 62 0a 03 1f 02 02 06 0d 00 00 63 04 03 3d 10 00 59 00 52 18 02 04 00 28 02 00 00 00 32 00 4f 75 74 4f 66 54 75 6e 65 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Passing By ** Source: Yamaha DX11 factory preset, bank D, voice 27 1f 1f 00 01 0f 00 00 55 04 03 1f 1f 00 01 0f 00 00 52 0a 03 02 1f 1f 06 0f 00 00 5a 04 0b 02 1f 1f 06 0f 00 00 5a 04 0b 7c 0a 00 00 00 71 0c 04 04 00 28 32 00 00 00 32 00 50 61 73 73 69 6e 67 20 42 79 4a 02 1b 32 00 00 00 10 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Passing By ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 00 00 55 04 03 1f 1f 00 01 0f 00 00 52 0a 03 02 1f 1f 06 0f 00 00 5a 04 0b 02 1f 1f 06 0f 00 00 5a 04 0b 7c 0d 00 00 00 71 0c 04 04 00 28 32 00 00 00 32 00 50 61 73 73 69 6e 67 20 42 79 4a 02 1b 32 00 00 00 10 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pull Over ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 44 2b 08 03 1f 1f 00 09 0f 00 44 4a 08 03 10 1f 00 0f 0f 00 05 5d 04 03 15 1f 00 09 0f 00 05 63 04 03 04 15 00 63 63 76 24 02 04 00 28 02 00 00 00 32 00 50 75 6c 6c 20 4f 76 65 72 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pull Over ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 0f 0f 00 40 2b 08 03 1f 1f 00 09 0f 00 40 4a 08 03 10 1f 00 0f 0f 00 00 5d 04 03 15 1f 00 09 0f 00 00 63 04 03 04 15 00 63 63 76 24 02 04 00 28 02 00 00 00 32 00 50 75 6c 6c 20 4f 76 65 72 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: R2-D2 * ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 00 00 04 03 0d 1f 00 09 0f 00 00 00 04 03 1d 1f 00 09 0f 00 00 63 3f 03 1d 1f 00 09 0f 00 00 63 31 03 44 2e 00 63 00 73 24 0c 04 00 28 63 00 00 00 32 00 52 32 2d 44 32 20 20 20 20 2a 63 63 63 32 32 32 00 00 00 00 00 50 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Racing Car ** Controls: MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 19 1f 1f 00 02 0f 00 00 47 00 03 1f 1f 00 01 0f 00 00 35 04 03 1f 1f 00 03 0f 00 00 5b 01 03 15 1f 00 03 0f 00 00 63 04 03 3a 05 00 3c 00 62 18 0c 04 00 63 32 00 00 00 32 00 52 61 63 69 6e 67 20 43 61 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RADIATION? ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 31 03 03 00 01 0f 00 40 51 28 03 15 09 00 03 0f 00 00 61 0c 03 0b 05 00 02 0f 00 40 33 34 03 12 18 00 06 0e 00 40 5a 30 03 7b 32 00 06 56 25 00 04 04 00 28 32 00 00 00 32 00 52 41 44 49 41 54 49 4f 4e 3f 63 63 63 32 32 32 09 09 08 02 0b 0e 08 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rain Storm ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 03 0f 22 04 63 0c 08 1f 00 00 05 0f 24 44 2c 08 00 1f 00 00 03 0f 00 05 63 04 06 1f 00 00 04 0f 00 04 5a 01 0e 3a 55 00 2d 00 7e 00 04 04 00 63 32 00 32 00 32 00 52 61 69 6e 20 53 74 6f 72 6d 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Rain Storm ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 03 0f 22 00 63 0c 08 1f 00 00 05 0f 24 40 2c 08 00 1f 00 00 03 0f 00 00 63 04 06 1f 00 00 04 0f 00 00 5a 01 0e 3a 55 00 2d 00 7e 00 04 04 00 63 32 00 32 00 32 00 52 61 69 6e 20 53 74 6f 72 6d 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Refs.Wisl* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1d 15 00 08 0d 00 00 4b 34 03 12 0c 00 06 0f 00 00 5d 04 03 1f 1f 00 05 0f 00 00 00 04 0b 0f 0c 00 08 0f 00 01 63 20 03 12 51 00 00 00 72 18 07 04 00 28 63 00 00 00 32 00 52 65 66 73 2e 57 69 73 6c 2a 63 63 63 32 32 32 0d 35 08 0d 00 00 0c 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Repeater.1 ** Source: from a library of patches collected by Duane Bowker, author unknown 13 18 06 0a 0f 00 78 5f 00 04 16 04 06 07 09 00 58 42 04 06 16 04 06 07 09 00 58 63 04 00 16 04 06 07 09 00 78 63 04 03 6c 29 00 00 63 34 18 02 04 00 63 32 00 00 00 32 00 52 65 70 65 61 74 65 72 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Repeater.2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 18 06 0a 0f 00 7b 47 10 04 16 04 06 07 09 00 5b 37 10 06 16 04 06 07 09 00 5d 61 04 00 16 04 06 07 09 00 7c 59 10 03 3a 29 3d 54 63 74 18 02 04 00 63 32 00 00 00 32 00 52 65 70 65 61 74 65 72 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Repeater.2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 13 18 06 0a 0f 00 78 47 10 04 16 04 06 07 09 00 58 37 10 06 16 04 06 07 09 00 58 61 04 00 16 04 06 07 09 00 78 59 10 03 3a 29 3d 54 63 74 18 02 04 00 63 32 00 00 00 32 00 52 65 70 65 61 74 65 72 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Repeater.3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 13 0f 0a 0f 0f 00 79 58 16 04 16 09 06 0f 0f 00 5b 49 10 06 16 09 06 0f 0f 00 5d 61 04 00 16 09 06 0f 0f 00 7d 59 04 03 7a 33 00 00 63 34 18 02 04 00 63 32 00 00 00 32 00 52 65 70 65 61 74 65 72 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Repeater.3 ** Source: from a library of patches collected by Duane Bowker, author unknown 13 0f 0a 0f 0f 00 78 58 16 04 16 09 06 0f 0f 00 58 49 10 06 16 09 06 0f 0f 00 58 61 04 00 16 09 06 0f 0f 00 78 59 04 03 7a 33 00 00 63 34 18 02 04 00 63 32 00 00 00 32 00 52 65 70 65 61 74 65 72 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Repeater.4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 13 07 07 00 7b 47 08 03 1f 06 1f 04 00 00 5b 4c 04 04 15 06 0c 06 07 00 5d 63 04 00 1f 06 1f 04 00 00 7d 63 04 06 6c 40 00 00 63 34 18 02 04 00 63 32 00 00 00 32 00 52 65 70 65 61 74 65 72 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Repeater.4 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 13 07 07 00 78 47 08 03 1f 06 1f 04 00 00 58 4c 04 04 15 06 0c 06 07 00 58 63 04 00 1f 06 1f 04 00 00 78 63 04 06 6c 40 00 00 63 34 18 02 04 00 63 32 00 00 00 32 00 52 65 70 65 61 74 65 72 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Repeater.5 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1b 04 07 0f 00 7b 52 08 03 03 1b 04 07 0f 00 5c 56 04 03 13 1b 04 07 0f 00 5d 51 08 03 1b 0f 04 07 0e 00 7e 63 04 03 45 25 00 00 63 35 18 02 04 00 63 32 00 00 00 32 00 52 65 70 65 61 74 65 72 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Repeater.5 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1b 04 07 0f 00 78 52 08 03 03 1b 04 07 0f 00 58 56 04 03 13 1b 04 07 0f 00 58 51 08 03 1b 0f 04 07 0e 00 78 63 04 03 45 25 00 00 63 35 18 02 04 00 63 32 00 00 00 32 00 52 65 70 65 61 74 65 72 2e 35 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * SUBCATEGORY Noises2 ** Name: Ringing..1 ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 1f 00 03 0f 00 00 34 1a 03 1f 1f 00 02 0f 00 00 3e 19 03 1f 1f 00 02 0f 00 00 39 1a 03 16 1f 00 06 0f 00 40 63 19 03 52 58 00 00 32 65 00 05 04 00 63 63 63 00 00 32 00 52 69 6e 67 69 6e 67 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ringing..2 ** Controls: MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 1f 00 03 0f 00 00 1c 00 03 1f 1f 00 02 0f 00 00 3e 1f 03 1f 1f 00 02 0f 00 00 39 04 03 16 1f 00 06 0f 00 40 63 1f 03 52 3b 00 00 3c 65 00 05 04 00 63 63 63 00 00 32 00 52 69 6e 67 69 6e 67 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RiseDecent ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0a 06 06 04 0e 00 44 43 0d 03 0c 06 06 04 0e 00 44 63 08 03 1f 06 06 04 0e 00 46 5b 04 03 1f 06 06 04 0e 00 45 63 00 03 7d 1e 00 48 3c 67 18 02 04 00 28 02 00 00 00 32 00 52 69 73 65 44 65 63 65 6e 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RiseDecent ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0a 06 06 04 0e 00 40 43 0d 03 0c 06 06 04 0e 00 40 63 08 03 1f 06 06 04 0e 00 40 5b 04 03 1f 06 06 04 0e 00 40 63 00 03 7d 1e 00 48 3c 67 18 02 04 00 28 02 00 00 00 32 00 52 69 73 65 44 65 63 65 6e 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RubberBand ** Description: An interesting use of portamento ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: GLIB (Michael Kesti and others) 1f 17 03 06 0f 52 07 5c 08 03 1f 1f 05 09 0b 00 06 63 10 03 1f 09 00 09 00 00 03 62 08 03 1f 0b 04 09 0b 00 07 63 04 03 02 1e 09 04 00 62 0c 07 02 03 63 32 00 32 00 32 00 52 75 62 62 65 72 42 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RubberBand ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 0c 04 02 01 09 08 01 43 04 0b 11 07 02 07 09 00 01 50 04 03 12 07 02 07 09 00 01 35 04 0b 0f 08 02 07 09 36 02 63 04 0b 30 1f 1c 13 00 52 18 07 04 00 63 32 00 32 00 32 00 52 75 62 62 65 72 42 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RubberBand ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 02 03 06 00 52 03 50 04 03 1f 0b 00 09 0c 00 00 4e 04 03 1f 06 00 09 01 00 00 5f 04 03 0b 09 04 09 0a 00 03 63 08 03 3c 1e 09 04 00 62 0c 07 04 00 63 32 00 32 00 32 00 52 75 62 62 65 72 42 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RubberBand ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 1f 00 00 0f 00 00 31 04 03 1f 1f 00 00 0f 21 00 3c 04 06 1f 1f 00 00 0f 1b 00 42 04 00 0b 1f 00 06 0f 00 00 60 04 03 3a 20 00 00 00 62 18 0c 04 00 63 32 00 32 00 32 00 52 75 62 62 65 72 42 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RubberBand ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 1f 00 00 0f 00 00 32 08 03 1f 1f 00 00 0f 21 00 4e 04 06 1f 1f 00 00 0f 1b 00 40 04 00 0b 1f 00 06 0f 00 00 5a 04 03 3a 20 00 00 00 62 18 0c 04 00 63 32 00 32 00 32 00 52 75 62 62 65 72 42 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RubberBand ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 02 03 06 00 0a 43 52 08 1b 1f 0a 00 09 0c 00 04 63 08 03 1f 08 02 09 0a 00 05 60 04 03 1f 1c 04 09 0c 00 07 63 04 03 3c 1e 09 03 00 62 18 07 04 00 63 32 00 32 00 32 00 52 75 62 62 65 72 42 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RubberBand ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 17 03 06 0f 52 07 5c 08 03 1f 1f 05 09 0b 00 06 63 10 03 1f 09 00 09 00 00 03 62 08 03 1f 0b 04 09 0b 00 07 63 04 03 02 1e 09 04 00 62 0c 07 0a 03 63 32 00 32 00 32 00 52 75 62 62 65 72 42 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RubberBand ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 02 03 06 00 52 03 50 04 03 1f 0b 00 09 0c 00 00 4e 04 04 1f 06 00 09 01 00 00 5f 04 03 1f 1c 04 09 0a 00 03 63 08 03 3c 1e 09 04 00 62 0c 02 04 00 63 32 00 32 00 32 00 52 75 62 62 65 72 42 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RubberBand ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 02 03 06 00 52 03 50 04 03 1f 0b 00 09 0c 00 00 4e 04 04 1f 06 00 09 01 00 00 5f 04 03 1f 1c 04 09 0a 00 03 63 08 03 3c 1e 09 04 00 62 0c 02 0a 07 63 32 00 32 00 32 00 52 75 62 62 65 72 42 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: RubberBand ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: DX100 GLIB patch (Michael Kesti and others) 1f 02 03 06 00 0e 03 55 04 1b 1f 0b 00 09 0c 00 00 4e 04 04 1f 06 00 09 01 00 00 5f 04 03 1f 1c 04 09 0a 00 03 63 08 03 3c 1e 09 04 00 62 0c 02 05 00 63 32 00 32 00 32 00 52 75 62 62 65 72 42 61 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SCM Writer ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0f 0a 44 63 3f 03 1f 0d 06 04 0f 00 44 55 20 1b 1f 1f 00 03 0f 00 44 63 35 03 1f 0f 09 07 00 00 44 63 3f 1b 3b 05 00 00 58 66 18 02 04 00 28 02 00 00 00 32 00 53 43 4d 20 57 72 69 74 65 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SCM Writer ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 0a 40 63 3f 03 1f 0d 06 04 0f 00 40 55 20 1b 1f 1f 00 03 0f 00 40 63 35 03 1f 0f 09 07 00 00 40 63 3f 1b 3b 05 00 00 58 66 18 02 04 00 28 02 00 00 00 32 00 53 43 4d 20 57 72 69 74 65 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SCM Writer ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0f 0a 40 63 3f 03 1f 0d 06 04 0f 00 40 55 20 1b 1f 1f 00 03 0f 00 40 63 35 03 1f 0f 09 07 00 00 40 63 3f 1b 3b 05 00 00 58 66 18 02 04 00 28 02 00 00 00 32 00 53 43 4d 20 57 72 69 74 65 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Saron Gam ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1d 09 05 06 0a 00 05 50 19 0e 1f 0c 06 06 0c 00 05 5d 06 16 1f 0d 08 03 0f 00 05 5e 11 1e 1f 11 07 06 0f 00 02 63 04 10 37 52 00 07 00 62 18 04 04 00 28 32 00 00 00 32 00 53 61 72 6f 6e 20 47 61 6d 20 63 63 63 32 32 32 00 0f 00 0c 00 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Shore Wave ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 10 0f 00 0d 0d 00 00 63 20 03 17 0e 00 0d 0f 00 00 63 20 03 17 0f 00 0a 0f 00 00 63 3c 03 03 09 00 0f 00 00 00 52 3f 03 02 23 00 00 00 62 30 04 04 00 28 32 00 00 00 32 00 53 68 6f 72 65 20 57 61 76 65 63 63 63 32 32 32 0c 06 00 12 0b 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Shore Wv2 ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 10 0f 00 07 0d 00 00 63 20 03 17 0e 00 07 0f 00 00 63 20 03 17 0f 00 04 0f 00 00 63 3c 03 03 05 00 09 00 00 00 52 3f 03 02 23 00 00 00 62 30 04 04 00 28 32 00 00 00 32 00 53 68 6f 72 65 20 57 76 32 20 63 63 63 32 32 32 0c 06 00 12 0b 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sick Siren ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0e 1f 00 0f 0f 00 43 2b 08 03 0e 1f 00 09 0f 00 45 4a 08 03 10 1f 00 0f 0f 00 45 5d 04 03 15 1f 00 09 0f 00 45 63 04 03 04 0d 00 00 63 64 24 02 04 00 28 02 00 00 00 32 00 53 69 63 6b 20 53 69 72 65 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sick Siren ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 1f 00 0f 0f 00 40 2b 08 03 0e 1f 00 09 0f 00 40 4a 08 03 10 1f 00 0f 0f 00 40 5d 04 03 15 1f 00 09 0f 00 40 63 04 03 04 0d 00 00 63 64 24 02 04 00 28 02 00 00 00 32 00 53 69 63 6b 20 53 69 72 65 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space BUG? ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 26 1f 1f 00 01 0f 06 00 63 01 1b 1f 1f 00 0f 0f 02 40 63 01 1b 0f 15 00 04 0f 00 40 56 00 1b 19 1f 00 0a 0f 63 40 53 00 1b 7c 1f 00 2a 3c 3c 00 04 04 00 28 32 00 00 00 32 00 53 70 61 63 65 20 42 55 47 3f 63 63 63 32 32 32 0f 06 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space BUG? ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 06 00 63 01 1b 1f 1f 00 0f 0f 02 40 63 01 1b 0f 15 00 04 0f 00 40 56 00 1b 19 1f 00 0d 0f 63 40 53 00 1b 7c 1f 00 2a 3c 3c 00 04 04 00 28 32 00 00 00 32 00 53 70 61 63 65 20 42 55 47 3f 63 63 63 32 32 32 0f 06 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space Gong ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 30 16 0a 0c 06 0f 03 00 53 04 08 0e 09 03 02 0f 03 00 59 07 0e 0a 06 05 04 0f 00 40 5a 00 00 14 08 05 04 08 00 40 5a 00 06 3c 0a 08 2d 3c 36 1b 04 04 00 28 32 00 00 00 32 00 53 70 61 63 65 20 47 6f 6e 67 5e 63 12 29 32 15 00 16 00 20 00 15 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space Gong ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 16 0d 0c 06 0f 03 00 53 04 08 0e 09 03 02 0f 03 00 59 07 0e 0a 06 05 04 0f 00 40 5a 00 00 14 08 05 04 08 00 40 5a 00 06 3c 0a 08 2d 3c 36 1b 04 04 00 28 32 00 00 00 32 00 53 70 61 63 65 20 47 6f 6e 67 5e 63 12 29 32 15 00 16 00 20 00 15 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space Gun ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 08 1f 05 0d 0a 02 39 00 03 1f 0a 1f 0a 0f 00 05 63 04 03 1f 09 1f 0a 0f 00 04 63 08 03 1f 04 1f 0a 0d 00 04 63 04 03 7d 39 00 63 00 70 18 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 20 47 75 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space Gun ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 08 1f 05 0d 0a 00 39 00 03 1f 0a 1f 0a 0f 00 00 63 04 03 1f 09 1f 0a 0f 00 00 63 08 03 1f 04 1f 0a 0d 00 00 63 04 03 7d 39 00 63 00 70 18 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 20 47 75 6e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space Talk ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 21 05 1f 01 01 0f 60 44 63 0b 03 06 1f 03 01 0f 02 42 63 06 03 0b 1f 04 01 0f 03 42 63 09 03 08 1f 02 09 0f 01 42 63 04 04 05 32 00 63 00 6d 18 05 04 3c 63 32 00 00 00 32 00 53 70 61 63 65 20 54 61 6c 6b 63 63 63 32 32 32 00 70 00 40 00 60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space Vibe ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 22 19 0f 0f 07 0f 05 00 62 0d 00 19 00 0f 07 0f 05 01 63 04 00 19 00 0f 07 0f 05 02 63 0d 06 1f 00 06 02 0f 05 01 63 04 06 47 23 00 4b 00 71 1e 0c 04 00 63 32 00 00 00 32 00 53 70 61 63 65 20 56 69 62 65 63 63 63 32 32 32 00 10 00 00 00 50 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space....1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 04 0c 00 03 0f 00 03 57 2d 06 04 09 00 03 0f 00 03 59 04 01 06 09 00 04 0f 00 03 63 04 05 06 09 00 04 0f 00 04 63 00 01 6c 23 21 00 00 02 18 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space....1 ** Source: from a library of patches collected by Duane Bowker, author unknown 04 0c 00 03 0f 00 00 57 2d 06 04 09 00 03 0f 00 00 59 04 01 06 09 00 04 0f 00 00 63 04 05 06 09 00 04 0f 00 00 63 00 01 6c 23 21 00 00 02 18 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 07 0f 00 41 57 04 03 03 1f 00 04 0f 00 43 59 1c 05 09 1f 00 06 0f 00 43 63 04 03 0c 1f 00 04 0f 00 45 63 13 00 2c 05 00 2f 63 14 18 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space....2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 07 0f 00 40 57 04 03 03 1f 00 04 0f 00 40 59 1c 05 09 1f 00 06 0f 00 40 63 04 03 0c 1f 00 04 0f 00 40 63 13 00 2c 05 00 2f 63 14 18 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space....3 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 19 12 06 06 00 00 42 63 04 13 04 09 1f 04 04 00 43 4a 0b 04 15 15 09 0a 0f 00 43 5d 04 03 1c 09 04 04 04 00 44 5f 04 00 0c 2b 41 0b 5a 76 18 02 04 00 63 63 00 00 00 32 00 53 70 61 63 65 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space....3 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 19 12 06 06 00 00 40 63 04 13 04 09 1f 04 04 00 40 4a 0b 04 15 15 09 0a 0f 00 40 5d 04 03 1c 09 04 04 04 00 40 5f 04 00 0c 2b 41 0b 5a 76 18 02 04 00 63 63 00 00 00 32 00 53 70 61 63 65 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space....4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 15 04 00 00 02 46 13 0c 06 04 07 04 00 00 04 46 04 0b 14 1d 15 06 00 00 04 63 08 0c 19 03 0c 04 00 00 04 5f 04 0b 4c 63 00 63 00 72 18 02 04 00 63 63 00 00 00 32 00 53 70 61 63 65 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Space....4 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 15 04 00 00 00 46 13 0c 06 04 07 04 00 00 00 46 04 0b 14 1d 15 06 00 00 00 63 08 0c 19 03 0c 04 00 00 00 5f 04 0b 4c 63 00 63 00 72 18 02 04 00 63 63 00 00 00 32 00 53 70 61 63 65 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpaceBirds ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 06 00 0a 0e 0a 43 39 0d 00 1f 1f 00 09 0f 00 45 63 29 03 1f 0b 00 0a 0f 00 44 36 08 06 0b 0b 00 0b 0f 00 45 63 00 03 07 25 00 42 5a 6e 00 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 42 69 72 64 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpaceBirds ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 06 00 0a 0e 0a 40 39 0d 00 1f 1f 00 09 0f 00 40 63 29 03 1f 0b 00 0a 0f 00 40 36 08 06 0b 0b 00 0b 0f 00 40 63 00 03 07 25 00 42 5a 6e 00 02 04 00 28 02 00 00 00 32 00 53 70 61 63 65 42 69 72 64 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Spc Midiot ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank D, voice 9 04 07 00 09 0d 22 00 4b 3d 08 14 14 00 05 0b 24 41 52 08 00 15 03 00 06 06 00 01 60 14 06 05 15 00 08 0f 00 04 63 00 06 7b 1f 0d 0e 1e 0e 18 0c 04 00 63 32 00 32 00 32 00 53 70 63 20 4d 69 64 69 6f 74 63 63 63 32 32 32 08 70 00 00 00 10 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SteelPan * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 14 0c 07 04 0f 27 05 52 0a 08 14 07 08 03 0f 2b 03 52 09 0e 16 07 09 06 0f 00 02 63 04 0e 11 07 09 05 0f 00 02 63 04 16 04 19 00 0a 63 52 18 02 04 00 28 32 00 00 00 32 00 53 74 65 65 6c 50 61 6e 20 2a 63 63 63 32 32 32 00 01 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Storm Wind ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 17 12 08 01 03 05 00 01 43 0a 00 14 0b 04 01 09 00 01 60 11 00 14 07 01 03 05 00 01 63 00 00 0f 06 00 01 0c 00 02 63 00 00 33 63 00 63 00 73 00 05 04 00 63 63 63 00 00 32 00 53 74 6f 72 6d 20 57 69 6e 64 63 63 63 32 32 32 00 50 00 10 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: TAP TAP<<< ** Source: Yamaha DX11 factory preset, bank D, voice 29 12 09 00 01 0f 06 00 3a 00 1b 12 09 00 01 0f 02 00 61 00 1b 17 08 00 01 0f 00 00 33 00 1b 15 15 00 0a 0f 00 40 53 3e 1b 3a 1c 00 00 63 6e 00 04 04 00 28 63 00 00 00 32 00 54 41 50 20 54 41 50 3c 3c 3c 63 63 63 32 32 32 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: TAP TAP<<< ** Source: from a library of patches collected by Duane Bowker, author unknown 12 09 00 01 0f 06 00 3a 00 1b 12 09 00 01 0f 02 00 61 00 1b 17 08 00 01 0f 00 00 33 00 1b 15 15 00 0d 0f 00 40 53 3e 1b 3a 1c 00 00 63 6e 00 04 04 00 28 63 00 00 00 32 00 54 41 50 20 54 41 50 3c 3c 3c 63 63 63 32 32 32 00 00 00 00 00 00 00 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Telephone* ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 0f 0f 00 40 4b 30 03 1f 1f 00 06 0f 00 40 48 28 03 1f 1f 00 06 0f 00 00 63 30 03 1f 1f 00 06 0f 00 00 63 24 03 7c 3a 00 00 63 09 18 04 04 00 28 32 00 00 00 32 00 54 65 6c 65 70 68 6f 6e 65 2a 63 63 63 32 32 32 0c 0a 0c 05 0b 16 0e 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Terror! ** Controls: MW (LFO pitch) ** Source: Yamaha DX11 factory preset, bank D, voice 23 0c 1f 00 03 0f 20 00 2e 31 03 1f 1f 00 03 0f 00 00 3b 08 06 0a 1f 00 03 0f 2a 00 34 08 00 12 1f 00 04 0f 00 00 54 00 03 03 63 00 23 00 63 18 04 04 00 28 32 00 00 00 32 00 54 65 72 72 6f 72 21 20 20 20 63 24 11 2d 32 25 04 6f 00 20 00 40 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Terror! ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 1f 00 03 0f 20 00 2e 31 03 1f 1f 00 03 0f 00 00 3b 08 06 0d 1f 00 03 0f 2a 00 34 08 00 12 1f 00 04 0f 00 00 54 00 03 03 63 00 23 00 63 18 04 04 00 28 32 00 00 00 32 00 54 65 72 72 6f 72 21 20 20 20 63 24 11 2d 32 25 04 6f 00 20 00 40 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Traffic..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 02 0f 00 02 42 02 03 1f 1f 00 01 0f 00 04 32 0a 03 1f 1f 00 03 0f 00 05 46 01 03 15 1f 00 03 0f 00 05 63 04 03 3a 47 00 3c 00 62 18 0c 04 0e 63 32 00 00 00 32 00 54 72 61 66 66 69 63 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Traffic..1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 02 0f 00 00 42 02 03 1f 1f 00 01 0f 00 00 32 0a 03 1f 1f 00 03 0f 00 00 46 01 03 15 1f 00 03 0f 00 00 63 04 03 3a 47 00 3c 00 62 18 0c 04 0e 63 32 00 00 00 32 00 54 72 61 66 66 69 63 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Traffic..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 02 0f 00 04 48 06 03 1f 1f 00 01 0f 00 05 32 07 03 1f 1f 00 03 0f 00 05 46 00 03 15 1f 00 03 0f 00 05 63 04 03 3a 15 00 5f 00 73 18 0c 04 0e 63 32 00 00 00 32 00 54 72 61 66 66 69 63 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Traffic..2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 02 0f 00 00 48 06 03 1f 1f 00 01 0f 00 00 32 07 03 1f 1f 00 03 0f 00 00 46 00 03 15 1f 00 03 0f 00 00 63 04 03 3a 15 00 5f 00 73 18 0c 04 0e 63 32 00 00 00 32 00 54 72 61 66 66 69 63 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tug Boat ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 15 16 00 01 0f 0a 03 00 06 03 0f 0d 00 01 0f 00 04 3b 08 03 1f 07 00 03 0c 00 06 3c 06 03 13 16 00 07 0f 00 06 63 04 03 7a 0c 00 00 00 40 18 02 04 00 28 02 00 00 00 32 00 54 75 67 20 42 6f 61 74 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tug Boat ** Source: from a library of patches collected by Duane Bowker, author unknown 15 16 00 01 0f 0a 00 00 06 03 0f 0d 00 01 0f 00 00 3b 08 03 1f 07 00 03 0c 00 00 3c 06 03 13 16 00 07 0f 00 00 63 04 03 7a 0c 00 00 00 40 18 02 04 00 28 02 00 00 00 32 00 54 75 67 20 42 6f 61 74 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Tug Boat ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 15 16 00 00 0f 0a 00 00 06 03 0f 0d 00 00 0f 00 00 3b 08 03 1f 07 00 03 0c 00 00 3c 06 03 13 16 00 07 0f 00 00 63 04 03 7a 0c 00 00 00 40 18 02 04 00 28 02 00 00 00 32 00 54 75 67 20 42 6f 61 74 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Typwriter* ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 05 00 07 00 00 00 3e 00 03 1f 1f 00 03 0f 00 00 4d 24 03 1f 1f 00 07 0f 00 00 59 18 03 1f 11 00 09 00 00 41 63 38 03 3b 4a 00 00 00 0d 18 02 04 00 28 63 00 00 00 32 00 54 79 70 77 72 69 74 65 72 2a 63 63 63 32 32 32 08 70 0c 02 08 04 09 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: UFO Decent ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 06 00 05 0e 0a 43 39 0d 00 1f 1f 00 01 0f 00 43 63 29 03 1f 0b 00 0a 0f 00 45 36 08 06 0b 0b 00 07 0f 00 45 63 00 03 38 05 00 42 5a 6e 18 02 04 00 28 02 00 00 00 32 00 55 46 4f 20 44 65 63 65 6e 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: UFO Decent ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 10 06 00 05 0e 0a 40 39 0d 00 1f 1f 00 01 0f 00 40 63 29 03 1f 0b 00 0a 0f 00 40 36 08 06 0b 0b 00 07 0f 00 40 63 00 03 38 05 00 42 5a 6e 18 02 04 00 28 02 00 00 00 32 00 55 46 4f 20 44 65 63 65 6e 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: UFO Decent ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 10 06 00 05 0e 0a 40 39 0d 00 1f 1f 00 00 0f 00 40 63 29 03 1f 0b 00 0a 0f 00 40 36 08 06 0b 0b 00 07 0f 00 40 63 00 03 38 05 00 42 5a 6e 18 02 04 00 28 02 00 00 00 32 00 55 46 4f 20 44 65 63 65 6e 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Uzzie ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 06 08 07 0f 0a 43 63 3f 03 1f 04 00 06 0f 00 45 63 29 03 1f 09 00 06 0f 00 45 47 35 03 1f 0a 00 07 0f 00 45 63 00 03 12 44 00 00 63 04 18 02 04 00 28 02 00 00 00 32 00 20 20 55 7a 7a 69 65 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Uzzie ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 08 07 0f 0a 40 63 3f 03 1f 04 00 06 0f 00 40 63 29 03 1f 09 00 06 0f 00 40 47 35 03 1f 0a 00 07 0f 00 40 63 00 03 12 44 00 00 63 04 18 02 04 00 28 02 00 00 00 32 00 20 20 55 7a 7a 69 65 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Water * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 06 00 06 04 0f 00 40 44 18 00 08 08 07 07 0f 00 00 63 04 06 06 07 08 07 0f 00 00 5a 05 06 09 08 04 05 0f 00 03 63 03 00 7f 43 00 63 63 7b 18 02 04 00 63 32 00 00 00 32 00 57 61 74 65 72 20 20 20 20 2a 63 63 63 32 32 32 00 08 00 38 00 01 00 46 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0f 0a 43 63 3f 03 1f 1f 00 01 0f 00 43 63 29 03 1f 1f 00 03 0f 00 45 63 35 03 0b 0b 00 07 0f 00 45 63 00 03 38 06 00 42 5a 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....1 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 0a 40 63 3f 03 1f 1f 00 01 0f 00 40 63 29 03 1f 1f 00 03 0f 00 40 63 35 03 0b 0b 00 07 0f 00 40 63 00 03 38 06 00 42 5a 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....1 ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0f 0a 40 63 3f 03 1f 1f 00 00 0f 00 40 63 29 03 1f 1f 00 03 0f 00 40 63 35 03 0b 0b 00 07 0f 00 40 63 00 03 38 06 00 42 5a 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0f 0a 43 63 3f 03 1f 1f 00 01 0f 00 43 63 29 03 1f 1f 00 03 0f 00 45 63 35 03 04 0b 00 07 0f 00 45 63 00 03 38 04 00 42 5a 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 0a 40 63 3f 03 1f 1f 00 01 0f 00 40 63 29 03 1f 1f 00 03 0f 00 40 63 35 03 04 0b 00 07 0f 00 40 63 00 03 38 04 00 42 5a 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....2 ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0f 0a 40 63 3f 03 1f 1f 00 00 0f 00 40 63 29 03 1f 1f 00 03 0f 00 40 63 35 03 04 0b 00 07 0f 00 40 63 00 03 38 04 00 42 5a 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0f 0a 45 63 3f 03 1f 1f 00 01 0f 00 44 63 29 03 1f 1f 00 03 0f 00 45 63 35 03 08 0b 00 07 0f 00 45 63 00 03 38 05 00 00 58 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....3 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 0a 40 63 3f 03 1f 1f 00 01 0f 00 40 63 29 03 1f 1f 00 03 0f 00 40 63 35 03 08 0b 00 07 0f 00 40 63 00 03 38 05 00 00 58 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....3 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0f 0a 40 63 3f 03 1f 1f 00 00 0f 00 40 63 29 03 1f 1f 00 03 0f 00 40 63 35 03 08 0b 00 07 0f 00 40 63 00 03 38 05 00 00 58 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 33 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....4 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 1f 00 01 0f 0a 43 63 3f 03 1f 1f 00 01 0f 00 45 00 29 03 0a 1f 00 03 0f 00 46 63 35 03 0b 0b 00 07 0f 00 46 01 00 03 3e 05 00 42 55 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....4 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 00 01 0f 0a 02 63 08 03 1f 1f 00 01 0f 00 04 63 0a 03 1f 1f 00 03 0f 00 04 63 08 03 0c 0a 09 07 0e 00 05 63 00 03 38 23 00 00 00 62 00 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....4 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 1f 00 01 0f 0a 40 63 3f 03 1f 1f 00 01 0f 00 40 00 29 03 0a 1f 00 03 0f 0f 40 63 35 03 0b 0b 00 07 0f 00 40 01 00 03 3e 05 00 42 55 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....4 ** Controls: MW (LFO pitch) ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1e 1f 00 00 0f 0a 40 63 3f 03 1f 1f 00 00 0f 00 40 00 29 03 0a 1f 00 03 0f 0f 40 63 35 03 0b 0b 00 07 0f 00 40 01 00 03 3e 05 00 42 55 66 18 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....4 ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 00 01 0f 0a 00 63 08 03 1f 1f 00 01 0f 00 00 63 0a 03 1f 1f 00 03 0f 00 00 63 08 03 0c 0a 09 07 0e 00 00 63 00 03 38 23 00 00 00 62 00 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Waves....4 ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 1f 1f 00 00 0f 0a 00 63 08 03 1f 1f 00 00 0f 00 00 63 0a 03 1f 1f 00 03 0f 00 00 63 08 03 0c 0a 09 07 0e 00 00 63 00 03 38 23 00 00 00 62 00 02 04 00 28 02 00 00 00 32 00 57 61 76 65 73 2e 2e 2e 2e 34 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Wind * ** Controls: KV, MW (LFO pitch), BC (voice pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 13 01 00 00 00 63 21 06 1f 05 13 01 00 00 00 00 00 02 1f 00 13 01 00 00 00 40 28 05 06 08 00 02 0e 00 01 63 05 03 3b 63 00 63 00 73 24 0c 04 00 63 32 00 00 00 53 00 57 69 6e 64 20 20 20 20 20 2a 63 63 63 32 32 32 08 00 00 00 0c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: >>WOW<< ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank D, voice 7 1f 0c 0d 08 0a 00 01 45 04 03 1f 10 00 04 0f 00 00 4c 00 03 0f 04 02 0a 0a 00 01 55 00 03 1f 1f 00 0c 0f 00 02 63 04 03 39 23 00 00 00 62 0c 02 04 00 63 32 00 32 00 32 00 3e 3e 57 4f 57 3c 3c 20 20 20 63 63 63 32 32 32 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WaterGlass ** Controls: KV ** Source: Yamaha TX81Z factory preset, bank D, voice 5 1f 1f 00 05 0f 00 00 41 32 03 1f 14 10 05 05 01 02 63 07 03 1f 0b 12 08 0f 00 01 63 0c 03 1f 1f 0f 08 0f 00 01 63 04 03 39 23 00 00 00 62 18 07 04 00 63 32 00 00 00 32 00 57 61 74 65 72 47 6c 61 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WeirdAxe * ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0f 0d 09 0f 00 00 5f 3c 03 1f 0f 0d 09 0f 00 02 63 38 03 1d 0f 0d 07 0f 00 01 63 1a 03 1f 0f 0d 07 0f 00 01 63 00 03 04 23 00 00 00 32 24 02 04 00 28 63 00 00 00 32 00 57 65 69 72 64 41 78 65 20 2a 63 63 63 32 32 32 0d 03 0d 31 00 04 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: White Blow ** Source: Yamaha DX11 factory preset, bank D, voice 32 18 13 00 0f 0a 00 00 3f 20 03 1f 12 00 0a 0f 00 00 56 20 03 1f 13 00 0c 0f 00 00 5c 3c 03 0b 0a 0a 0b 0a 00 00 5a 3f 03 02 23 00 00 00 62 30 04 04 00 28 32 00 00 00 32 00 57 68 69 74 65 20 42 6c 6f 77 63 63 63 32 32 32 0c 06 00 02 0b 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: White Blow ** Source: from a library of patches collected by Duane Bowker, author unknown 18 13 00 0f 0d 00 00 3f 20 03 1f 12 00 0d 0f 00 00 56 20 03 1f 13 00 0c 0f 00 00 5c 3c 03 0b 0d 0a 0b 0d 00 00 5a 3f 03 02 23 00 00 00 62 30 04 04 00 28 32 00 00 00 32 00 57 68 69 74 65 20 42 6c 6f 77 63 63 63 32 32 32 0c 06 00 02 0b 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 --Bank 29 * CATEGORY OTHER * SUBCATEGORY Other ** Name: 120 VAC ** Description: A guitar-like sound ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 07 07 06 0f 00 3d 5e 04 0c 1f 07 07 05 0f 00 1d 46 28 01 1f 07 07 07 0f 00 1d 63 10 0c 1f 07 0a 05 0f 00 3d 5d 04 01 24 1c 11 02 00 42 18 02 04 00 63 32 00 00 00 32 00 31 32 30 20 56 41 43 20 20 20 63 63 63 32 32 32 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: 120 VAC ** Description: A guitar-like sound ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 07 07 06 0f 00 3a 5e 04 0c 1f 07 07 05 0f 00 19 46 28 01 1f 07 07 07 0f 00 19 63 10 0c 1f 07 0a 05 0f 00 38 5d 04 01 24 1c 11 02 00 42 18 02 04 00 63 32 00 00 00 32 00 31 32 30 20 56 41 43 20 20 20 63 63 63 32 32 32 00 00 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Amazon ** Description: A jungle sound ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 13 1f 03 05 00 06 4b 13 13 13 1f 1f 05 05 00 06 4a 10 13 1f 1f 01 05 0f 00 03 63 03 0d 1f 1f 01 05 0f 00 03 63 05 09 2c 1c 09 07 00 52 05 04 04 00 63 28 00 0f 00 32 00 41 6d 61 7a 6f 6e 20 20 20 20 63 63 63 32 32 32 00 10 00 6d 00 16 00 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ArcoGlass* ** Description: A glass harp ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 00 05 00 0a 01 47 12 0e 1f 09 00 05 00 0a 01 47 0d 0e 0b 0a 0a 05 00 09 40 63 00 06 0b 0a 0a 05 00 09 40 63 00 00 2c 15 01 16 0c 3a 24 02 04 00 28 37 00 00 00 32 00 41 72 63 6f 47 6c 61 73 73 2a 63 63 63 32 32 32 00 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Aviary ** Description: A glassy sound ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1f 1f 0f 0f 0a 00 00 1e 03 1f 1f 0b 00 0f 00 00 2f 1a 03 0c 1f 0d 07 0f 00 00 48 03 03 0c 0f 06 05 0f 00 00 50 03 03 7c 23 00 00 00 32 18 02 04 00 28 02 00 00 00 32 00 41 76 69 61 72 79 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Aviary ** Description: A glassy sound ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 1f 0f 0f 0a 00 00 1e 03 1f 1f 0b 01 0f 00 00 2f 1a 03 0c 1f 0d 07 0f 00 00 48 03 03 0c 0f 06 05 0f 00 00 50 03 03 7c 23 00 00 00 32 18 02 04 00 28 02 00 00 00 32 00 41 76 69 61 72 79 20 20 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: BC1 GlassV ** Controls: KV, MW (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 03 03 06 0f 00 3b 4c 19 0b 0c 0c 05 05 0d 40 04 59 10 10 0b 0a 06 06 0c 27 06 63 08 08 09 06 06 06 0d 00 06 5c 04 13 0c 1e 18 03 00 62 18 07 04 00 63 32 01 00 00 32 63 42 43 31 20 47 6c 61 73 73 56 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Botl.Shff* ** Description: Blowing across the top of a bottle ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 13 11 0a 0d 00 01 55 13 09 1f 19 00 07 0d 00 01 4f 0a 00 1f 11 0d 09 09 00 02 5c 09 06 0e 08 00 07 0e 00 42 63 05 0b 3b 1c 00 00 00 5e 05 02 04 00 63 10 2e 00 00 32 00 42 6f 74 6c 2e 53 68 66 66 2a 63 63 63 32 32 32 00 00 00 00 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Cairo * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 08 06 07 0e 14 00 53 04 0e 1f 0f 0a 0b 0b 00 00 3d 38 0b 1f 1e 06 07 0e 00 02 63 03 08 1f 07 07 0a 0b 00 02 63 04 0b 04 15 00 0a 00 32 05 02 04 00 28 63 00 00 00 32 00 43 61 69 72 6f 20 20 20 20 2a 63 63 63 32 32 32 00 18 00 06 00 06 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Celestial ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 04 0f 00 18 41 39 06 0b 02 03 04 00 00 00 50 05 06 0f 02 04 03 0a 00 08 55 03 04 0e 00 01 06 00 00 02 63 04 00 7e 1a 00 05 01 62 11 0c 05 00 63 32 00 00 00 32 00 43 65 6c 65 73 74 69 61 6c 20 63 63 63 32 32 32 00 21 00 11 00 46 00 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ChimeStrig ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 09 04 03 08 00 04 57 0a 03 1f 1f 00 03 0f 00 04 5e 00 06 1f 09 04 04 08 00 04 5b 1f 02 0f 1f 00 04 0f 00 06 5e 08 06 2c 23 00 00 00 02 18 04 04 00 28 32 00 00 00 32 00 43 68 69 6d 65 53 74 72 69 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ChimeStrig ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 09 04 03 08 00 02 57 0a 03 1f 1f 00 03 0f 00 01 5e 00 06 1f 09 04 04 08 00 00 5b 1f 02 0f 1f 00 04 0f 00 00 5e 08 06 2c 23 00 00 00 02 18 04 04 00 28 32 00 00 00 32 00 43 68 69 6d 65 53 74 72 69 67 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Deep Space ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1b 07 07 00 0a 44 54 13 03 1f 0a 07 06 00 00 45 52 04 13 1f 12 07 06 00 00 46 53 10 03 10 0a 0a 04 00 00 46 63 04 03 07 2a 48 01 4b 06 18 02 04 00 28 02 00 00 00 32 00 44 65 65 70 20 53 70 61 63 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Deep Space ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1b 07 07 00 0a 40 54 13 03 1f 0a 07 06 00 00 40 52 04 13 1f 12 07 06 00 00 40 53 10 03 10 0a 0a 04 00 00 40 63 04 03 07 2a 48 01 4b 06 18 02 04 00 28 02 00 00 00 32 00 44 65 65 70 20 53 70 61 63 65 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: EleChamber ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 06 02 03 0e 00 02 3f 08 02 0b 03 03 05 08 00 04 47 08 00 0a 08 03 07 0f 00 05 63 08 06 09 10 03 05 0f 00 05 63 08 05 5c 21 23 09 00 32 0c 05 04 00 63 63 63 00 00 32 00 45 6c 65 43 68 61 6d 62 65 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: EleChamber ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 06 02 03 0e 00 00 3f 08 02 0b 03 03 05 08 00 00 47 08 00 0a 08 03 07 0f 00 01 63 08 06 09 10 03 05 0f 00 01 63 08 05 5c 21 23 09 00 32 0c 05 04 00 63 63 63 00 00 32 00 45 6c 65 43 68 61 6d 62 65 72 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Enc3rdKind ** Controls: KV, MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 00 06 00 00 44 63 00 03 1f 0d 00 06 00 00 43 63 04 03 1f 0c 00 06 00 00 45 63 04 03 1f 0a 00 06 0a 00 45 63 04 03 47 14 4f 1e 02 15 18 04 04 00 63 01 63 00 00 32 00 45 6e 63 33 72 64 4b 69 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Enc3rdKind ** Controls: MW (LFO pitch), MW (LFO amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 00 06 00 00 40 63 00 03 1f 0d 00 06 00 00 40 63 04 03 1f 0c 00 06 00 00 40 63 04 03 1f 0a 00 06 0a 00 40 63 04 03 47 14 4f 1e 02 15 18 04 04 00 63 01 63 00 00 32 00 45 6e 63 33 72 64 4b 69 6e 64 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Etherial ** Controls: MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 1f 00 03 0f 00 00 42 13 03 1f 1f 00 03 0f 00 00 3c 13 03 14 1f 00 05 0f 1e 00 56 0a 03 14 1f 00 05 0f 1e 00 56 1c 03 3c 23 00 32 00 42 05 02 04 00 63 63 00 00 00 32 00 45 74 68 65 72 69 61 6c 20 20 63 63 63 32 32 32 00 30 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Floot Base ** Description: Almost a flute, and down a fifth ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 18 1f 16 0f 0f 00 00 63 05 00 18 07 00 0f 07 1b 00 45 03 0c 1f 1f 15 0f 0f 03 00 63 1e 05 0f 14 00 0f 0f 05 01 5f 04 02 3b 20 00 00 00 62 0c 02 05 00 63 13 63 00 00 32 00 46 6c 6f 6f 74 20 42 61 73 65 63 63 63 32 32 32 00 72 00 36 00 04 00 08 06 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Full Ranks ** Source: Unknown (downloaded from TX81Z patch directory on ucsd.edu) 16 00 00 0a 0f 00 00 39 1f 02 18 00 00 05 0f 5b 00 5b 00 10 0f 00 00 08 0f 00 00 63 08 10 0c 00 00 05 0f 00 00 63 00 16 3c 23 00 00 00 02 0c 00 04 00 63 00 00 00 00 32 00 46 75 6c 6c 20 52 61 6e 6b 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: FuzzyPad ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 06 06 04 0f 00 40 40 05 00 14 08 07 05 0f 05 00 47 04 00 0f 07 03 07 0e 08 03 63 04 06 13 08 04 05 0f 00 03 60 04 06 64 1f 00 06 00 4a 05 02 04 00 63 32 00 00 00 32 00 46 75 7a 7a 79 50 61 64 20 20 63 63 63 32 32 32 06 31 00 38 00 08 00 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Gender Gam ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 15 13 05 06 0a 00 04 54 19 0e 1f 0e 04 06 0c 00 04 4a 06 0e 18 13 03 08 0c 00 04 59 10 06 1f 12 03 06 0d 00 01 63 04 00 37 52 00 07 00 62 18 04 04 00 28 32 00 00 00 32 00 47 65 6e 64 65 72 20 47 61 6d 63 63 63 32 32 32 00 1e 00 0b 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GlassHarp1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1c 0a 00 04 00 00 02 50 15 02 1f 0a 00 05 00 00 03 4e 0d 02 0f 0c 0a 06 00 00 03 63 00 02 0f 0c 00 06 0f 00 04 63 00 04 2e 63 00 00 00 33 1b 04 04 00 63 01 63 00 00 32 00 47 6c 61 73 73 48 61 72 70 31 63 63 63 32 32 32 0f 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GlassHarp1 ** Source: from a library of patches collected by Duane Bowker, author unknown 1c 0a 00 04 00 00 00 50 15 02 1f 0a 00 05 00 00 00 4e 0d 02 0f 0c 0a 06 00 00 00 63 00 02 0f 0c 00 06 0f 00 00 63 00 04 2e 63 00 00 00 33 1b 04 04 00 63 01 63 00 00 32 00 47 6c 61 73 73 48 61 72 70 31 63 63 63 32 32 32 0f 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GlassHarp2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 03 03 06 0f 00 3c 40 19 0b 0c 0c 05 05 0d 40 04 4f 10 10 0b 0a 06 06 0c 27 05 63 08 08 09 06 06 06 0d 00 05 5c 04 13 0c 1e 18 03 00 62 18 07 04 00 63 32 01 00 00 32 00 47 6c 61 73 73 48 61 72 70 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GlassHarp2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 09 03 03 06 0f 00 38 40 19 0b 0c 0c 05 05 0d 40 00 4f 10 10 0b 0a 06 06 0c 27 00 63 08 08 09 06 06 06 0d 00 00 5c 04 13 0c 1e 18 03 00 62 18 07 04 00 63 32 01 00 00 32 00 47 6c 61 73 73 48 61 72 70 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GlassHarp3 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 07 04 04 0a 00 04 3e 2a 03 07 07 04 04 09 00 04 27 2d 05 0c 06 07 07 03 00 04 63 10 06 0a 08 05 05 0c 00 04 63 10 00 2e 63 00 00 00 23 18 04 04 00 63 01 63 00 00 32 00 47 6c 61 73 73 48 61 72 70 33 63 63 63 32 32 32 0f 07 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: GlassHarp3 ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 07 04 04 0a 00 00 3e 2a 03 07 07 04 04 09 00 00 27 2d 05 0c 06 07 07 03 00 00 63 10 06 0a 08 05 05 0c 00 00 63 10 00 2e 63 00 00 00 33 18 04 04 00 63 01 63 00 00 32 00 47 6c 61 73 73 48 61 72 70 33 63 63 63 32 32 32 0f 07 00 20 00 00 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: HarmoPad ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank B, voice 28 0a 1f 02 05 0f 27 43 4d 05 06 0a 1f 02 05 0f 27 03 4f 04 00 0b 1f 00 04 0f 00 01 63 05 0e 0b 1f 00 04 0f 00 01 63 04 08 04 1c 0e 1c 00 36 05 04 04 00 28 32 00 00 00 32 00 48 61 72 6d 6f 50 61 64 20 20 63 63 63 32 32 32 04 41 00 48 00 31 07 38 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Impression ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 15 04 07 0f 00 06 50 00 0b 09 15 04 07 0f 00 06 43 00 0b 16 07 04 07 0f 00 05 63 00 03 16 07 04 07 0f 00 05 63 00 03 14 20 3b 02 00 02 18 04 04 00 63 01 63 00 00 32 00 49 6d 70 72 65 73 73 69 6f 6e 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Impression ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 09 15 04 07 0f 00 02 50 00 0b 09 15 04 07 0f 00 01 43 00 0b 16 07 04 07 0f 00 00 63 00 03 16 07 04 07 0f 00 00 63 00 03 14 20 3b 02 00 02 18 04 04 00 63 01 63 00 00 32 00 49 6d 70 72 65 73 73 69 6f 6e 63 63 63 32 32 32 00 00 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jump.....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 03 01 06 0e 00 03 54 04 03 12 04 01 04 0e 00 05 4d 08 03 15 03 02 09 0e 00 05 63 04 03 15 0c 03 09 0e 00 05 63 04 01 34 26 02 0d 00 32 0c 04 04 00 28 32 00 00 00 32 00 4a 75 6d 70 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jump.....1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 03 01 06 0e 00 01 54 04 03 12 04 01 04 0e 00 02 4d 08 03 15 03 02 09 0e 00 00 63 04 03 15 0c 03 09 0e 00 00 63 04 01 34 26 02 0d 00 32 0c 04 04 00 28 32 00 00 00 32 00 4a 75 6d 70 2e 2e 2e 2e 2e 31 63 63 63 32 32 32 00 00 00 10 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jump.....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 03 07 09 0e 00 03 57 04 03 1f 16 18 09 0c 00 05 63 08 1b 1f 16 00 09 0e 00 05 63 04 03 1f 16 00 09 0c 00 05 63 08 03 2c 26 21 20 00 12 18 04 04 00 28 32 00 00 00 32 00 4a 75 6d 70 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Jump.....2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 12 03 07 09 0e 00 01 57 04 03 1f 16 18 09 0c 00 02 63 08 1b 1f 16 00 09 0e 00 00 63 04 03 1f 16 00 09 0c 00 00 63 08 03 2c 26 21 20 00 12 18 04 04 00 28 32 00 00 00 32 00 4a 75 6d 70 2e 2e 2e 2e 2e 32 63 63 63 32 32 32 00 10 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: KrstlChoir ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank D, voice 3 1f 00 06 04 0f 00 40 43 18 00 0b 08 07 07 0f 05 00 63 04 06 0f 07 08 07 0f 08 00 59 05 06 0e 08 04 05 0f 00 03 63 03 00 7e 22 00 0f 00 4a 1d 02 04 00 63 32 00 00 00 32 00 4b 72 73 74 6c 43 68 6f 69 72 63 63 63 32 32 32 00 38 00 38 00 01 00 46 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: KrystlPad1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 11 06 07 0f 00 45 4f 1a 00 0b 08 07 07 0f 05 01 62 04 03 0f 07 08 07 0f 08 01 62 05 06 0e 08 04 05 0f 00 03 63 03 00 7f 22 00 0f 00 26 11 02 04 00 63 32 00 00 00 32 00 4b 72 79 73 74 6c 50 61 64 31 63 63 63 32 32 32 00 38 00 18 00 21 00 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: MilkBottle ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0c 1f 06 05 01 00 03 3f 09 08 13 1c 03 05 0b 00 02 53 09 0e 1c 1f 01 05 0f 00 03 46 0a 08 0c 09 01 08 0d 00 04 63 04 00 33 1a 09 07 1f 52 05 02 04 00 63 19 1f 10 1e 32 00 4d 69 6c 6b 42 6f 74 74 6c 65 63 63 63 32 32 32 20 33 00 02 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Movement.1 ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0a 09 03 06 0b 00 43 42 2a 10 1f 09 05 03 0f 00 43 63 04 04 1f 09 05 06 0b 00 44 63 19 0a 1f 09 06 06 0f 00 46 63 04 05 64 18 39 09 19 35 00 00 04 00 63 32 00 32 00 32 00 4d 6f 76 65 6d 65 6e 74 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Movement.1 ** Controls: MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0a 09 03 06 0b 00 40 42 2a 10 1f 09 05 03 0f 00 40 63 04 04 1f 09 05 06 0b 00 40 63 19 0a 1f 09 06 06 0f 00 40 63 04 05 64 18 39 09 19 35 00 00 04 00 63 32 00 32 00 32 00 4d 6f 76 65 6d 65 6e 74 2e 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Movement.2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0b 09 00 06 0e 00 43 3e 1c 18 1f 09 00 03 0f 00 44 53 08 01 1f 09 01 06 0e 00 43 63 08 02 1f 09 00 06 0f 00 45 63 08 15 7c 25 00 00 27 37 00 00 04 00 63 32 00 32 00 32 00 4d 6f 76 65 6d 65 6e 74 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Movement.2 ** Source: from a library of patches collected by Duane Bowker, author unknown 0b 09 00 06 0e 00 40 3e 1c 18 1f 09 00 03 0f 00 40 53 08 01 1f 09 01 06 0e 00 40 63 08 02 1f 09 00 06 0f 00 40 63 08 15 7c 25 00 00 27 37 00 00 04 00 63 32 00 32 00 32 00 4d 6f 76 65 6d 65 6e 74 2e 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Musicbox * ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 1a 01 01 09 00 00 4b 1f 06 1f 1f 1f 0f 00 00 00 5e 30 03 1f 1c 0a 04 0b 00 00 63 08 08 1b 16 1f 0f 00 00 00 52 30 03 04 04 00 00 00 52 18 02 04 00 28 63 00 00 00 32 00 4d 75 73 69 63 62 6f 78 20 2a 63 63 63 32 32 32 00 10 0c 0f 00 00 0a 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Obelisk ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 02 1d 00 04 0f 00 18 41 39 06 02 02 03 04 00 00 00 50 05 06 02 02 04 03 0a 00 08 55 03 04 02 0d 01 06 00 00 02 63 04 00 7f 1a 00 05 01 62 11 0c 05 00 63 32 00 00 00 32 00 4f 62 65 6c 69 73 6b 20 20 20 63 63 63 32 32 32 00 21 00 11 00 46 00 48 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt E.T. ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 03 00 06 04 00 42 50 2a 05 1f 05 00 06 04 00 42 47 34 03 1f 03 00 06 04 00 45 5d 22 04 1f 04 00 06 04 00 44 63 04 03 6c 48 2a 0a 27 36 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 20 45 2e 54 2e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt E.T. ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 03 00 06 04 00 40 50 2a 05 1f 05 00 06 04 00 40 47 34 03 1f 03 00 06 04 00 40 5d 22 04 1f 04 00 06 04 00 40 63 04 03 6c 48 2a 0a 27 36 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 20 45 2e 54 2e 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Quest ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 08 1f 09 05 00 03 63 3f 1b 12 1f 0e 0f 0f 00 04 63 0d 03 0a 1f 09 0f 0f 00 04 5e 19 03 1f 06 03 0f 0f 00 05 61 04 03 3f 15 00 15 23 12 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 20 51 75 65 73 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Ornt Quest ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 09 08 1f 09 05 00 00 63 3f 1b 12 1f 0e 0f 0f 00 00 63 0d 03 0a 1f 09 0f 0f 00 00 5e 19 03 1f 06 03 0f 0f 00 00 61 04 03 3f 15 00 15 23 12 18 04 04 00 28 32 00 00 00 32 00 4f 72 6e 74 20 51 75 65 73 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntFlight ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 12 06 00 04 00 2e 02 46 0d 1a 1f 13 1d 09 0a 00 02 63 34 04 12 02 00 04 00 2e 05 63 08 1a 13 03 15 09 0d 00 04 5d 08 03 7c 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 46 6c 69 67 68 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntFlight ** Source: from a library of patches collected by Duane Bowker, author unknown 12 06 00 04 00 2e 00 46 0d 1a 1f 13 1d 09 0a 00 00 63 34 04 12 02 00 04 00 2e 00 63 08 1a 13 03 15 09 0d 00 00 5d 08 03 7c 23 00 00 00 32 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 46 6c 69 67 68 74 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntSound1 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 03 02 05 00 00 02 40 0a 03 1f 0a 03 05 00 00 03 54 08 03 1f 12 04 05 00 00 03 63 0a 03 06 05 00 05 00 00 05 63 04 06 77 38 44 00 3f 12 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 53 6f 75 6e 64 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntSound1 ** Source: from a library of patches collected by Duane Bowker, author unknown 09 03 02 05 00 00 00 40 0a 03 1f 0a 03 05 00 00 00 54 08 03 1f 12 04 05 00 00 00 63 0a 03 06 05 00 05 00 00 00 63 04 06 77 38 44 00 3f 12 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 53 6f 75 6e 64 31 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntSound2 ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 09 03 09 04 0c 00 02 40 08 03 09 0d 03 02 0c 00 02 54 04 03 16 0f 04 05 0c 00 05 63 04 03 16 06 01 07 0c 00 05 63 04 06 74 38 44 00 3f 31 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 53 6f 75 6e 64 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: OrntSound2 ** Source: from a library of patches collected by Duane Bowker, author unknown 09 03 09 04 0c 00 00 40 08 03 09 0d 03 02 0c 00 00 54 04 03 16 0f 04 05 0c 00 00 63 04 03 16 06 01 07 0c 00 00 63 04 06 74 38 44 00 3f 31 0c 02 04 00 63 32 00 32 00 32 00 4f 72 6e 74 53 6f 75 6e 64 32 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: ParisDream ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 0b 06 0b 00 02 4b 1f 01 13 0b 0c 07 0a 00 02 63 16 01 1f 09 0c 06 0a 00 02 54 0a 0d 0e 0f 0c 07 0d 00 00 63 16 05 3e 1a 00 05 00 42 0c 01 04 00 63 32 00 00 00 32 00 50 61 72 69 73 44 72 65 61 6d 63 63 63 32 32 32 00 38 00 10 00 08 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PianoVoice ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 0a 0e 07 06 63 04 4d 1f 03 0a 1f 00 03 0f 00 04 4c 08 03 1f 07 07 05 07 00 05 63 04 03 08 0e 00 06 0f 00 45 63 04 03 7c 23 00 0e 00 36 18 02 04 00 28 02 00 00 00 32 00 50 69 61 6e 6f 56 6f 69 63 65 63 63 63 32 32 32 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PianoVoice ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 0a 0e 07 06 63 03 4d 1f 03 0a 1f 00 03 0f 00 02 4c 08 03 1f 07 07 05 07 00 00 63 04 03 08 0e 00 06 0f 00 40 63 04 03 7c 23 00 0e 00 36 18 02 04 00 28 02 00 00 00 32 00 50 69 61 6e 6f 56 6f 69 63 65 63 63 63 32 32 32 00 00 00 00 00 00 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PianoStrng ** Controls: KV, MW (LFO pitch) ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 1c 00 04 0f 1e 02 43 09 06 1f 1c 00 04 0f 1e 02 43 0a 00 1f 1f 00 05 0f 1e 01 63 09 06 1f 1f 00 05 0f 1e 01 63 0a 00 3c 1f 06 0a 00 52 05 02 04 00 63 32 00 00 00 32 00 50 69 61 6e 6f 53 74 72 6e 67 63 63 63 32 32 32 00 02 00 00 02 32 00 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PlasticHit ** Controls: KV, MW (LFO pitch) ** Source: Yamaha TX81Z factory preset, bank C, voice 31 1f 14 0e 07 04 00 04 5b 30 16 1f 12 04 0a 06 00 04 61 04 10 1f 0f 03 05 0b 00 02 5e 04 10 1f 0b 03 06 0b 00 02 63 04 16 03 1b 0b 05 00 52 0c 04 04 00 32 32 00 00 00 32 00 50 6c 61 73 74 69 63 48 69 74 63 63 63 32 32 32 00 30 00 50 00 50 07 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pno String ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 07 07 05 07 00 04 43 2a 02 18 03 03 05 08 00 04 58 08 00 1f 08 06 06 07 00 07 63 04 0e 09 10 03 05 0f 00 06 5e 08 05 2c 21 23 0f 00 12 0c 05 04 00 63 63 63 00 00 32 00 50 6e 6f 20 53 74 72 69 6e 67 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Pno String ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 07 07 05 07 00 03 43 2a 02 18 03 03 05 08 00 01 58 08 00 1f 08 06 06 07 00 00 63 04 0e 09 10 03 05 0f 00 00 5e 08 05 2c 21 23 0f 00 12 0c 05 04 00 63 63 63 00 00 32 00 50 6e 6f 20 53 74 72 69 6e 67 63 63 63 32 32 32 00 00 00 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PnoClouds ** Controls: KV, MW (LFO pitch), BC (LFO pitch), BC (EG amplitude) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 1f 08 05 0f 17 45 47 05 02 06 0b 01 04 0e 00 24 4e 1a 00 1f 1f 08 05 0f 00 04 63 04 03 07 07 01 04 0e 00 24 4e 1a 06 06 1c 0e 09 2e 4a 05 02 04 00 63 32 00 07 00 32 39 50 6e 6f 43 6c 6f 75 64 73 20 63 63 63 32 32 32 06 41 00 57 00 78 00 56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: PopPercuss ** Description: guitar / horn / electric piano mix ** Controls: KV ** Source: GLIB (Michael Kesti and others) 1f 0c 06 0b 06 00 04 56 04 02 1f 02 03 0b 0e 43 04 61 04 10 1f 12 06 08 0e 00 04 60 08 00 1a 17 08 08 0c 00 03 63 04 03 33 14 00 00 00 62 18 01 04 00 63 32 00 32 00 32 00 50 6f 70 50 65 72 63 75 73 73 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Sawsy Pad1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 0e 08 06 07 0e 1d 01 63 03 06 0e 08 07 07 0f 16 41 62 05 00 0e 08 05 07 0f 1d 01 62 05 00 0e 08 04 05 0f 14 41 63 04 06 7f 22 00 0f 00 36 05 02 05 03 63 32 00 00 00 32 00 53 61 77 73 79 20 50 61 64 31 63 63 63 32 32 32 00 36 00 31 00 31 00 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Solstice ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 0a 1b 0d 06 0f 00 04 31 12 00 0d 1b 0d 06 0f 00 04 43 13 06 0c 1b 0d 06 0f 00 04 37 12 00 1f 1b 0d 06 0f 00 04 63 19 03 47 23 13 00 00 32 0c 04 04 00 63 01 63 00 00 32 00 53 6f 6c 73 74 69 63 65 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Solstice ** Source: from a library of patches collected by Duane Bowker, author unknown 0a 1b 0d 06 0f 00 00 31 12 00 0d 1b 0d 06 0f 00 00 43 13 06 0c 1b 0d 06 0f 00 00 37 12 00 1f 1b 0d 06 0f 00 00 63 19 03 47 23 13 00 00 32 0c 04 04 00 63 01 63 00 00 32 00 53 6f 6c 73 74 69 63 65 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpoonChoir ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1e 0b 0f 04 07 00 40 43 1c 00 13 0c 07 07 0b 05 00 54 05 00 1f 07 02 07 0f 08 03 5c 00 06 13 0c 04 05 0a 00 03 57 03 06 7c 1f 00 06 00 4a 11 02 04 00 63 32 00 00 00 32 00 53 70 6f 6f 6e 43 68 6f 69 72 63 63 63 32 32 32 06 70 00 71 00 04 00 56 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpreadThin ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 10 0a 06 08 0c 00 02 59 08 0b 18 12 06 0a 00 00 05 5a 04 0b 15 07 06 08 0c 00 04 63 04 0b 15 1d 06 0f 0f 00 05 63 04 03 24 1e 00 00 00 22 00 05 04 00 63 63 63 00 00 32 00 53 70 72 65 61 64 54 68 69 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: SpreadThin ** Controls: KV ** Source: from a library of patches collected by Duane Bowker, author unknown 10 0a 06 08 0c 00 01 59 08 0b 18 12 06 0a 00 00 01 5a 04 0b 15 07 06 08 0c 00 00 63 04 0b 15 1d 06 0f 0f 00 00 63 04 03 24 1e 00 00 00 22 00 05 04 00 63 63 63 00 00 32 00 53 70 72 65 61 64 54 68 69 6e 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: St Nick..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 01 0f 00 04 4c 00 02 1f 1f 0c 05 0f 00 03 53 2a 0b 0c 0c 06 05 0f 00 04 63 00 04 1f 0c 06 05 0f 00 07 63 08 0b 3c 1b 28 10 00 22 18 04 04 00 28 32 00 00 00 32 00 53 74 20 4e 69 63 6b 2e 2e 31 63 63 63 32 32 32 00 20 00 00 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: St Nick..1 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 01 0f 00 01 4c 00 02 1f 1f 0c 05 0f 00 00 53 2a 0b 0c 0c 06 05 0f 00 00 63 00 04 1f 0c 06 05 0f 00 01 63 08 0b 3c 1b 28 10 00 22 18 04 04 00 28 32 00 00 00 32 00 53 74 20 4e 69 63 6b 2e 2e 31 63 63 63 32 32 32 00 20 00 00 00 40 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: St Nick..2 ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 00 00 01 0f 00 02 4c 04 00 19 1f 0c 05 0f 00 03 63 0a 03 0c 0c 06 05 0f 00 04 5a 04 06 19 0c 06 05 0f 00 06 63 04 03 36 22 28 2b 00 12 18 04 04 00 28 32 00 00 00 32 00 53 74 20 4e 69 63 6b 2e 2e 32 63 63 63 32 32 32 00 20 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: St Nick..2 ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 00 00 01 0f 00 00 4c 04 00 19 1f 0c 05 0f 00 00 63 0a 03 0c 0c 06 05 0f 00 00 5a 04 06 19 0c 06 05 0f 00 00 63 04 03 36 22 28 2b 00 12 18 04 04 00 28 32 00 00 00 32 00 53 74 20 4e 69 63 6b 2e 2e 32 63 63 63 32 32 32 00 20 00 10 00 10 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StonedCycl ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 04 0f 07 06 0f 00 05 4d 08 00 04 0d 07 06 0f 00 04 63 04 06 04 0e 07 06 0f 00 06 38 00 00 05 0e 07 06 0f 00 06 63 04 03 7a 63 63 06 00 53 00 04 04 00 63 01 63 00 00 32 00 53 74 6f 6e 65 64 43 79 63 6c 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: StonedCycl ** Controls: MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 04 0f 07 06 0f 00 00 4d 08 00 04 0d 07 06 0f 00 00 63 04 06 04 0e 07 06 0f 00 00 38 00 00 05 0e 07 06 0f 00 00 63 04 03 7a 63 63 06 00 53 00 04 04 00 63 01 63 00 00 32 00 53 74 6f 6e 65 64 43 79 63 6c 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Texture * ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 0c 0f 0d 07 0f 00 00 63 00 00 1b 00 0d 07 0f 00 01 63 04 00 0f 00 0d 07 0f 00 02 63 0d 06 1f 00 0d 07 0f 00 01 63 04 06 6f 32 00 63 00 33 18 0c 04 00 63 32 00 00 00 32 00 54 65 78 74 75 72 65 20 20 2a 63 63 63 32 32 32 00 50 00 40 00 60 00 50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: That Girl* ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Rod Lemcke, author unknown 1e 02 07 05 00 00 02 5e 05 19 1e 07 0a 08 0b 00 05 59 38 18 1e 06 0a 07 00 00 02 63 03 0c 1b 06 1f 0b 00 00 02 60 04 13 04 1d 12 1e 00 32 05 02 04 00 28 63 00 00 00 32 00 54 68 61 74 20 47 69 72 6c 2a 63 63 63 32 32 32 00 01 00 06 00 06 00 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Up We Go ** Controls: KV ** Source: from a library of patches collected by Rod Lemcke, author unknown 1f 12 0c 06 0e 00 04 5f 04 03 1f 0f 0c 04 0e 00 02 50 07 0b 1b 03 01 02 0e 00 05 51 0e 03 1f 09 0f 06 0d 00 06 63 04 03 44 23 00 00 00 32 18 04 04 00 63 01 63 00 00 32 00 55 70 20 57 65 20 47 6f 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: Up We Go ** Source: from a library of patches collected by Duane Bowker, author unknown 1f 12 0c 06 0e 00 00 5f 04 03 1f 0f 0c 04 0e 00 00 50 07 0b 1b 03 01 02 0e 00 00 51 0e 03 1f 09 0f 06 0d 00 00 63 04 03 44 23 00 00 00 32 18 04 04 00 63 01 63 00 00 32 00 55 70 20 57 65 20 47 6f 20 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WineGlass* ** Controls: KV, MW (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 03 05 05 04 00 40 01 35 24 08 08 07 01 04 0c 40 41 34 19 0e 08 09 00 04 0c 32 44 63 08 1e 03 01 00 04 0c 32 42 5b 08 18 04 08 00 08 22 3e 18 04 04 00 63 32 00 00 00 32 60 57 69 6e 65 47 6c 61 73 73 2a 63 63 63 32 32 32 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: WireWaves ** Controls: KV, MW (LFO pitch), BC (LFO pitch) ** Source: from a library of patches collected by Duane Bowker, author unknown 14 19 03 05 00 00 07 5d 18 12 19 09 00 05 00 1f 06 3e 2d 16 12 07 07 05 0c 00 05 63 0b 08 10 09 07 05 0d 00 05 63 0d 08 34 1a 09 05 00 52 00 02 04 00 63 19 00 10 00 32 00 57 69 72 65 57 61 76 65 73 20 63 63 63 32 32 32 20 32 1d 74 00 69 00 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ** Name: XyloSynth ** Controls: KV ** Source: by Steve Michelson, from the patch library of his dx11lib librarian 1f 16 0e 07 07 15 06 63 24 0b 1a 18 0c 07 05 0c 02 63 0a 0b 17 1f 0d 07 0f 00 02 63 0a 0b 16 1f 0a 05 0f 00 01 63 04 0b 3e 23 00 00 00 52 18 02 04 00 28 32 00 00 00 32 00 58 79 6c 6f 53 79 6e 74 68 20 63 63 63 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 muse-4.0.0/src/synti/deicsonze/CMakeLists.txt000066400000000000000000000050631404105311000211350ustar00rootroot00000000000000#============================================================================= # MusE # Linux Music Editor # $Id:$ # # Copyright (C) 1999-2011 by Werner Schweer and others # # 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; if not, write to the # Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #============================================================================= ## ## Expand Qt macros in source files ## QT5_WRAP_CPP ( deicsonze_mocs deicsonzegui.h ) ## ## UI files ## file (GLOB deicsonze_ui_files deicsonzegui.ui ) QT5_WRAP_UI ( deicsonze_uis ${deicsonze_ui_files} ) ## ## Resource files ## QT5_ADD_RESOURCES (deicsonze_qrc_files deicsonze.qrc ) ## ## List of source files to compile ## file (GLOB deicsonze_source_files deicsonze.cpp deicsonzepreset.cpp deicsonzeplugin.cpp deicsonzefilter.cpp deicsonzegui.cpp ) ## ## Define target ## add_library ( deicsonze SHARED ${deicsonze_source_files} ${deicsonze_mocs} ${deicsonze_uis} ${deicsonze_qrc_files} ) ## ## Append to the list of translations ## set (FILES_TO_TRANSLATE ${FILES_TO_TRANSLATE} ${deicsonze_source_files} ${deicsonze_ui_files} CACHE INTERNAL "" ) ## ## Compilation flags and target name ## # - tell cmake to name target deicsonze.so instead of # libdeicsonze.so # - use precompiled header files # set_target_properties ( deicsonze PROPERTIES PREFIX "" #COMPILE_FLAGS "-O2 -include" COMPILE_FLAGS "-fvisibility=hidden" ) ## ## Linkage ## target_link_libraries(deicsonze al awl mpevent_module simpler_plugin simpler_plugingui synti ${QT_LIBRARIES} ) ## ## Install location ## install( TARGETS deicsonze DESTINATION ${MusE_SYNTHI_DIR} ) install( FILES SutulaBank.dei DESTINATION ${MusE_SHARE_DIR}/presets/deicsonze ) install( FILES README TODO DESTINATION ${MusE_DOC_DIR}/deicsonze ) muse-4.0.0/src/synti/deicsonze/README000066400000000000000000000027661404105311000172640ustar00rootroot00000000000000DeicsOnze Copyright (C) 2005-2006 by Nil Geisweiller ------- License ------- DeicsOnze is distributed under the terms of the GNU General Public License (see COPYING file). ---------- Descripion ---------- DeicsOnze is a virtual synthesizer plugin for MusE which reproduces the FM synthesis of the YAMAHA DX11 synthesizer/TX81Z sound module. ------- Remarks ------- It contains over 2000 presets archived by Bryan Sutula belonging to the public domain. If by any chance you find some of them that are under copyright please contact me to take them off. Not every function is implemented but it's already sounding like the original (even better). Pitch bend, modulation and some others are not implemented yet. The files with the extension .fig in the directory pics have been made with xfig. ----------- Compilation ----------- DeicsOnze is compiled with the optimization -O3. This permits it to be twice faster than without optimization, however the compilation time is longer (10 minutes on my Athlon XP-M 2000) and may not pass on any machine. If not remove -O3 in the file CMakeLists.txt. ---- Help ---- The last features (portamento and pitch envelope) have been implemented without DX11 only with the documentation. If you have a DX11 or TX81Z I need you! Please record your sound with different values of portamento and pitch envelope and send it to me so I can adjust the paramenter of DeicsOnze to fit the real DX11. ------- Contact ------- Nil Geisweiller a-lin[NO_SPAM_PLEASE]@users.sourceforge.net muse-4.0.0/src/synti/deicsonze/SutulaBank.dei000066400000000000000000222063731404105311000211440ustar00rootroot00000000000000 Sutula Bank ORCHESTRA 0 Orchestra 0 FIFTH 6 TRIANGL 33 35 15 0 off 1 0 off off off off 0 0 0 0 6 2 5 2 2 2 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 2 -3 3 -1 9 24 17 9 16 3 7 3 15 8 11 11 3 3 3 3 5 5 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 78 0 0 0 0 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 EleOrchtra 0 SEVENTH 7 TRIANGL 33 33 7 0 off 3 0 off off off off 0 0 0 0 0 0 1 1 1.49 2.99 3 3 no no no no 9 10 8 8 W4 W2 W1 W1 -3 3 -3 -3 8 7 15 16 31 31 31 31 15 15 15 15 0 0 0 0 5 5 5 2 VOF VOF VOF VOF 99 99 99 50 50 50 97 97 99 75 1 1 1 1 0 11 0 0 -19 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Orchestra* 1 FIFTH 4 TRIANGL 32 44 20 0 off 4 0 off off off off 0 0 0 0 5 2 5 1 2 2 6 2 no no no no 8 8 8 8 W1 W1 W1 W1 3 -1 -1 2 31 16 14 16 21 12 21 12 15 15 15 15 6 3 6 3 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 97 62 93 71 1 1 1 1 30 50 30 50 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 OrntOrkest 2 Symphony 1 FIFTH 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 6 4 5 4 2 0.5 2 1 no no no no 8 8 8 8 W1 W3 W1 W1 3 3 -1 0 15 31 15 31 31 31 31 31 15 15 15 15 0 0 0 0 4 3 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 91 89 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sympathy.1 0 FIFTH 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 6 3 2 2 2 1 no no no no 8 8 8 8 W1 W3 W1 W1 3 3 -1 0 15 31 15 31 31 31 31 31 15 15 15 15 0 0 0 0 4 3 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 94 85 91 82 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sympathy.2 1 FIFTH 7 TRIANGL 35 14 14 0 off 3 0 off off off off 0 0 0 0 0 0 0 0 1.5 1.5 1.51 1.51 no no no no 16 16 14 14 W1 W2 W1 W1 -3 3 0 0 7 13 12 15 4 4 4 4 14 14 14 14 3 0 3 3 5 5 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 99 74 0 0 0 0 12 29 14 22 -7 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Sympho * 2 FIFTH 7 TRIANGL 31 12 16 0 off 4 3 on off on on 7 0 7 1 0 0 0 1 1.51 1.5 1.5 1.5 no no no no 14 16 16 16 W1 W4 W1 W1 -3 3 0 0 10 17 12 21 4 23 8 4 14 15 15 14 3 3 3 0 5 4 5 1 VOF V12 VOF VOF 99 99 99 50 50 50 99 69 99 77 0 0 0 0 0 0 0 0 -19 POLY 2 FULL 0 POR 40 0 0 12 7 0 0 50 25 0 0 0 0 0 Symphony * 3 FIFTH 4 TRIANGL 38 24 15 0 on 2 1 off off off off 0 0 0 0 5 4 4 4 2 2 4 6 no no no no 8 8 8 8 W3 W3 W2 W2 0 0 0 -1 12 14 24 25 13 9 9 19 13 14 14 14 0 0 5 7 5 5 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 75 89 0 0 1 2 0 0 10 10 -12 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Symphony.1 4 FIFTH 6 TRIANGL 34 24 15 0 off 2 0 off off off off 0 0 0 0 2 3 5 1 4 8 2 2 no no no no 8 8 8 8 W1 W1 W2 W2 -3 -1 -2 2 31 25 12 25 10 19 10 5 11 14 15 14 6 6 0 0 4 4 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 77 77 98 81 0 1 0 0 0 10 10 65 -12 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Symphony.2 5 FIFTH 5 TRIANGL 30 0 6 0 off 3 0 off off off off 0 0 0 0 6 3 3 3 2 2 2 1 no no no no 8 8 8 8 W7 W3 W1 W1 -2 -1 0 -2 18 15 13 28 8 12 10 5 13 14 14 14 3 1 0 0 6 5 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 88 57 93 76 0 0 0 0 0 10 10 41 -12 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Symphony.3 6 FIFTH 6 TRIANGL 30 11 9 0 on 3 0 off off off off 0 0 0 0 3 3 5 2 2 6 2 2 no no no no 8 8 8 8 W2 W3 W3 W4 -2 2 0 1 12 23 21 27 10 12 13 26 15 15 14 13 0 0 5 5 5 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 99 73 0 0 1 1 10 10 52 52 -24 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Symphony.4 7 SIXTH 6 TRIANGL 30 26 12 0 off 2 0 off off off off 0 0 0 0 3 0 3 3 2 4 3 2 no no no no 8 8 8 8 W2 W5 W3 W5 -2 -1 1 1 12 12 13 29 9 10 11 3 9 9 9 10 8 8 6 6 7 6 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 97 72 0 1 0 2 21 10 26 41 -24 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Symphony.5 8 SIXTH 6 TRIANGL 30 26 12 0 off 2 0 off off off off 0 0 0 0 0 0 0 0 2 2 3 2 no no no no 8 8 8 8 W2 W2 W3 W5 -2 -1 1 1 12 12 13 29 9 10 11 3 14 14 15 15 0 1 6 6 4 5 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 94 74 79 72 0 1 0 2 21 10 26 41 -24 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Symphony.5* 9 FIFTH 4 TRIANGL 29 25 61 37 off 2 1 on on on on 0 5 0 0 4 5 0 0 4 8 2 2 no no no no 8 8 8 8 W4 W3 W1 W4 2 1 -3 -1 23 26 11 15 10 5 10 10 15 14 15 5 0 0 3 2 6 7 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 91 80 86 76 0 0 2 1 20 10 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Symphony.6 10 FIFTH 4 TRIANGL 28 0 42 11 off 2 1 on on on on 0 0 0 0 0 1 0 0 2 6 2 2 no no no no 8 8 8 8 W1 W2 W1 W2 2 1 -3 -1 16 10 19 9 10 5 8 10 15 14 15 5 0 0 2 2 6 7 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 91 53 86 76 0 0 2 1 20 10 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Symphony.6* 11 FIFTH 5 TRIANGL 28 0 15 0 off 4 0 off off off off 0 0 0 0 4 4 3 3 2 2 2 1 no no no no 8 8 8 8 W4 W2 W1 W5 -2 -1 1 -3 18 18 14 15 16 12 16 12 14 14 14 14 0 4 0 4 5 4 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 81 77 95 81 0 1 0 1 0 10 73 73 0 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Symphony.7 12 WIND 1 Brass 0 SEVENTH 5 TRIANGL 31 14 16 0 off 5 0 off off off off 0 0 0 0 4 4 4 4 1 1 2 2 no no no no 8 8 8 8 W2 W3 W1 W1 0 0 0 0 13 13 13 13 31 31 31 7 15 15 15 14 0 0 0 0 7 7 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 95 85 0 0 0 1 10 10 0 15 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBrass1 0 SIXTH 5 TRIANGL 31 0 5 0 off 6 0 off off off off 0 0 0 0 6 4 6 2 1 1 1 1 no no no no 8 8 8 8 W4 W3 W4 W1 0 0 0 0 15 15 16 13 5 5 31 4 15 15 15 12 1 1 0 3 8 8 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 86 85 83 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBrass2 1 SEVENTH 7 TRIANGL 31 14 16 0 off 5 0 off off off off 0 0 0 0 5 2 5 4 2 1 2 1 no no no no 8 8 8 8 W1 W3 W5 W3 3 1 1 -2 15 13 13 13 31 31 31 7 15 15 15 14 0 0 0 0 7 7 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 95 77 0 0 0 0 10 10 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBrass3 2 SIXTH 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 3 5 5 1 1 1 1 no no no no 8 8 8 8 W4 W4 W1 W4 3 3 1 -1 15 15 16 13 3 3 31 4 1 1 5 15 4 4 0 0 8 8 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 85 85 83 0 0 0 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBrass4 3 FIFTH 6 TRIANGL 35 0 5 0 off 3 0 off off off off 0 0 0 0 3 3 3 2 1 1 1 1 no no no no 8 8 8 8 W5 W3 W4 W1 3 3 0 0 16 21 22 18 27 16 7 10 12 9 15 12 6 6 6 6 7 7 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 73 99 83 0 0 0 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBrass5 4 FOURTH 4 TRIANGL 30 37 37 0 off 3 0 off off off off 0 0 0 0 5 2 6 2 1 1 1 1 no no no no 8 8 8 8 W3 W3 W1 W3 3 2 1 -2 17 21 15 18 10 9 7 10 12 11 15 12 5 6 6 6 7 6 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 82 76 83 0 1 0 1 15 30 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBrass6 5 SIXTH 4 TRIANGL 34 36 15 0 off 4 0 off off off off 0 0 0 0 5 2 5 2 1 1 1 1 no no no no 8 8 8 8 W2 W5 W1 W3 3 2 3 1 16 16 22 20 31 31 31 10 13 13 14 12 6 9 5 3 14 14 13 13 VOF VOF VOF VOF 99 99 99 50 50 50 98 99 99 88 0 0 0 1 0 0 0 0 -12 POLY 2 FINGER 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AnlgBrass7 6 SEVENTH 4 TRIANGL 40 0 0 0 off 3 0 off off off off 0 0 0 0 3 3 3 1 0.5 0.5 1 0.5 no no no no 8 8 8 8 W3 W3 W1 W1 2 1 0 -1 31 31 31 31 24 10 24 10 15 15 15 15 6 6 6 6 13 13 13 13 VOF VOF VOF VOF 99 99 99 50 50 50 94 94 94 97 0 0 0 1 0 0 0 0 0 POLY 2 FINGER 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AnlgBrass8 7 FIFTH 7 TRIANGL 43 0 0 0 off 3 0 off off off off 0 0 0 0 3 3 4 1 1 1 1 1 no no no no 8 8 8 8 W3 W2 W1 W1 0 3 3 0 31 18 31 31 6 12 22 10 12 10 12 15 4 2 8 6 13 15 15 13 VOF VOF VOF VOF 99 99 99 50 50 50 94 81 99 81 0 1 0 1 0 10 0 0 -12 POLY 2 FINGER 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AnlgBrass9 8 FIFTH 6 TRIANGL 28 21 6 0 off 5 0 off off off off 0 0 0 0 4 1 4 2 1.5 1.5 1.49 1.49 no no no no 16 16 9 9 W1 W4 W1 W1 -2 -1 2 2 16 15 16 15 5 14 31 31 14 14 15 15 0 0 0 4 7 8 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 97 83 1 0 0 1 0 0 0 0 -7 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AtackBrass 9 THIRD 7 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 15 13 31 13 31 14 31 31 15 0 15 15 0 0 0 0 8 15 15 8 VOF VOF VOF VOF 99 99 99 50 50 50 90 71 0 67 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Back Brass 10 FOURTH 7 TRIANGL 35 1 10 0 off 6 0 off off off off 0 0 0 7 5 4 4 4 2 2 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 0 0 18 18 18 27 31 31 31 3 15 15 15 14 0 0 0 0 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 95 75 79 71 0 0 0 0 1 0 69 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 99 0 0 0 0 0 BC1 Feed 11 THIRD 7 TRIANGL 32 5 3 0 off 6 0 off off off off 5 4 4 7 6 4 5 3 1 1 3.14 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 14 9 10 11 31 31 31 9 15 15 15 14 0 0 0 0 9 8 15 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 61 33 74 1 0 0 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 99 0 0 0 0 0 BC1 Horns 12 FIFTH 7 TRIANGL 32 6 3 0 off 5 0 off off off off 0 0 0 0 1 1 1 1 2 1 1 1 no no no no 8 8 8 8 W1 W4 W1 W1 1 -1 -1 -2 19 16 20 16 31 11 31 9 15 10 15 10 0 0 0 0 10 9 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 98 82 98 80 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass 1 13 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -2 3 -3 3 13 13 13 13 13 13 13 13 11 15 11 13 0 0 0 0 8 10 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 99 71 1 1 1 1 0 45 0 1 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass 1* 14 FIFTH 7 TRIANGL 32 6 3 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 0.5 0.5 1 1 no no no no 8 8 8 8 W2 W4 W1 W1 -1 -3 3 1 18 10 21 17 31 8 31 10 15 10 15 10 0 0 0 0 9 8 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 98 83 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass 2 15 THIRD 6 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 -3 3 3 20 31 20 25 31 4 11 5 15 10 15 14 0 0 15 0 10 5 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 55 80 81 0 0 0 0 0 0 0 10 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass 3 16 SIXTH 7 TRIANGL 35 20 10 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 21 21 21 15 31 31 31 10 15 15 15 13 0 0 0 0 10 10 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 92 92 92 73 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass 4 17 THIRD 7 TRIANGL 35 27 10 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 3.14 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 21 17 31 17 31 17 16 14 15 12 5 14 0 0 0 0 11 9 15 5 VOF VOF VOF VOF 99 99 99 50 50 50 96 70 52 74 1 0 1 0 0 0 32 10 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass 5 18 FOURTH 7 TRIANGL 35 15 16 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 5 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 21 31 19 15 12 15 11 0 17 0 0 5 3 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 90 79 70 85 0 0 0 0 0 30 30 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass 6 19 SECOND 7 TRIANGL 35 15 16 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 2 1 1 11 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 25 31 31 31 31 11 17 17 15 15 14 11 0 0 0 19 5 3 3 12 VOF VOF VOF VOF 99 99 99 50 50 50 90 57 93 72 0 0 0 0 0 30 30 15 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass 7 20 FIFTH 5 TRIANGL 38 33 0 71 off 1 0 off off off off 0 0 0 0 5 4 6 2 2 2 3 3 no no no no 8 8 8 8 W2 W3 W1 W1 3 3 -1 -1 19 19 19 16 5 5 21 6 11 11 11 11 6 6 6 6 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 99 85 0 0 0 0 10 10 10 33 -12 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass 5th 21 FIFTH 7 TRIANGL 31 45 1 0 off 4 0 off off off off 0 0 0 0 4 3 5 2 1 1 1 1 no no no no 8 8 8 8 W1 W2 W2 W3 -3 1 -1 3 19 8 20 8 7 9 8 7 12 14 13 12 6 5 6 6 8 9 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 95 96 90 88 0 0 0 0 0 71 0 39 0 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass....1 22 FOURTH 7 TRIANGL 35 45 5 0 off 2 0 off off off off 0 0 0 0 6 5 6 1 1 1 1 1 no no no no 8 8 8 8 W4 W4 W1 W6 2 1 2 1 17 14 15 20 7 10 10 27 12 0 12 9 6 4 6 6 8 10 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 78 81 1 1 0 0 0 0 0 39 0 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass....2 23 FOURTH 7 TRIANGL 35 45 5 0 off 2 0 off off off off 0 0 0 0 1 1 2 1 1 1 0.5 1 no no no no 8 8 8 8 W4 W4 W1 W6 2 1 2 1 17 14 15 20 7 10 10 27 12 0 12 9 6 4 6 6 8 10 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 78 81 1 1 0 0 0 0 0 39 0 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass....2* 24 FOURTH 6 TRIANGL 35 12 15 0 off 3 0 off off off off 0 0 0 0 5 5 5 5 1 1 1 1 no no no no 8 8 8 8 W2 W5 W3 W3 1 -3 1 2 16 12 15 21 7 10 10 27 12 0 12 9 6 4 6 6 8 10 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 73 89 1 1 0 0 0 0 0 39 0 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Brass....3 25 FIFTH 4 TRIANGL 36 35 0 43 on 0 1 on on on off 0 0 0 0 6 5 4 3 4 8 1 1 no no no no 8 8 8 8 W1 W1 W3 W2 -1 0 0 0 31 31 20 17 6 6 7 4 13 13 14 15 6 6 5 4 6 5 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 83 82 97 80 0 0 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 BrassChime 26 FIFTH 3 TRIANGL 14 0 13 0 on 1 0 off off off off 0 0 0 0 4 3 4 2 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W3 W4 W3 W1 -1 -3 -1 3 14 21 16 12 12 19 31 17 14 15 15 15 7 5 4 3 6 7 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 96 68 97 71 0 0 0 1 20 60 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BrassEns.1 27 SIXTH 6 TRIANGL 38 0 0 38 off 1 1 off off on off 0 0 0 0 4 1 5 1 1 1 1 1 no no no no 8 8 8 8 W1 W7 W5 W4 2 -2 -2 2 20 14 19 15 12 19 31 17 14 15 15 15 7 4 4 3 6 7 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 96 98 79 0 0 0 1 20 43 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BrassEns.2 28 FIFTH 4 TRIANGL 32 51 11 0 on 2 0 off off off off 0 0 0 0 5 2 4 1 1 1 1 1 no no no no 8 8 8 8 W1 W3 W1 W4 2 -3 2 -1 15 14 12 14 12 9 31 5 14 15 15 15 4 4 4 3 5 6 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 96 69 92 87 0 0 0 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BrassEns.3 29 FOURTH 4 TRIANGL 32 51 11 0 on 3 0 off off off off 0 0 0 0 4 2 5 2 1 1 1 1 no no no no 8 8 8 8 W3 W3 W1 W1 1 -1 2 -2 17 20 12 31 5 8 12 15 14 15 15 12 4 4 9 3 7 9 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 63 58 0 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BrassEns.4 30 FIFTH 7 TRIANGL 32 6 3 0 off 5 0 off off off off 0 7 0 7 1 1 1 1 0.5 0.5 1 1 no no no no 8 8 8 8 W2 W4 W1 W1 -1 -3 3 1 14 9 14 13 22 5 22 10 15 13 15 13 0 0 0 0 6 5 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 98 83 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 BrassSynth 31 FOURTH 6 TRIANGL 35 0 25 0 on 3 0 off off off off 0 0 0 0 6 3 6 2 1 1 1 1 no no no no 8 8 8 8 W3 W3 W1 W2 -3 0 0 3 17 16 15 21 7 10 10 31 12 0 12 9 6 5 6 6 8 10 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 96 82 77 84 0 0 0 0 0 0 90 0 0 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Brass.Vibe 32 FIFTH 6 TRIANGL 23 0 11 0 off 5 0 off off off off 0 0 0 0 0 0 0 1 1.5 1.5 1.5 1.5 no no no no 16 16 16 16 W1 W1 W1 W1 0 -3 3 3 16 13 18 14 12 8 31 8 15 15 15 15 0 0 0 0 5 4 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 99 81 0 0 0 0 13 39 9 25 -19 POLY 2 FULL 0 POR 40 0 0 30 0 0 0 50 0 0 0 0 0 0 Brassy * 33 FIFTH 7 SQUARE 29 0 0 8 off 0 3 off off on on 0 0 0 0 2 0 2 0 1 0.5 2 1 no no no no 8 8 8 8 W1 W1 W1 W3 3 3 -3 -3 13 13 15 14 14 14 12 14 15 15 15 15 0 0 0 0 4 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 50 99 78 1 0 1 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 13 0 0 50 0 0 0 0 0 0 Chariots 34 FIFTH 7 TRIANGL 28 6 6 0 off 5 0 off off off off 0 0 0 0 2 2 2 2 1.5 1.5 1.49 1.49 no no no no 16 16 9 9 W4 W4 W1 W1 -3 -3 3 3 14 14 14 14 18 8 18 8 15 13 15 13 0 0 0 0 8 9 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 99 78 0 0 0 0 0 0 0 0 -19 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 ChorusBras 35 FIFTH 7 TRIANGL 30 45 12 0 off 4 0 off off off off 0 0 0 0 2 0 2 0 6 2.99 2.99 3 no no no no 8 10 10 8 W4 W2 W1 W4 1 3 3 -3 13 13 11 28 11 20 11 12 15 15 15 15 0 0 0 0 5 4 5 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 99 70 0 0 0 0 0 47 0 0 -19 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Ensemble * 36 FIFTH 7 TRIANGL 33 8 12 0 off 5 0 off off off off 0 0 0 0 1 1 1 2 3 6 2.99 9 no no no no 8 8 10 8 W3 W4 W3 W5 -3 -3 3 3 15 11 10 20 6 4 7 6 11 7 10 10 1 2 2 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 94 82 99 61 0 0 0 0 0 20 0 20 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Ensemble 1 37 FIFTH 7 TRIANGL 33 8 12 0 off 5 0 off off off off 0 0 0 0 1 1 1 2 3 6 2.99 9 no no no no 8 8 10 8 W3 W4 W5 W6 -3 -3 3 3 17 11 12 20 6 4 7 6 11 8 11 8 1 2 2 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 94 80 99 61 0 0 0 0 0 20 0 20 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Ensemble 2 38 SIXTH 7 TRIANGL 29 15 8 0 off 5 0 off off off off 0 0 0 0 2 2 2 3 1 1 1 1 no no no no 8 8 8 8 W1 W4 W1 W1 3 0 2 2 17 15 16 15 31 31 31 31 15 15 15 15 0 9 0 8 8 9 8 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 79 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 7 Full Brass 39 THIRD 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 25 31 31 20 31 31 31 31 15 15 15 15 0 0 0 0 10 15 15 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 58 52 71 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Hard Brass 40 THIRD 7 TRIANGL 30 9 2 0 off 6 0 off off off off 0 0 0 0 2 2 3 1 0.5 0.5 1.57 0.5 no no no no 8 8 8 8 W1 W3 W4 W1 0 0 0 0 15 16 16 10 24 8 15 7 15 10 5 14 0 0 0 0 10 10 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 64 77 1 1 1 1 0 28 9 10 0 POLY 2 FULL 0 POR 40 0 0 19 0 0 0 50 0 0 0 0 0 0 Horn 41 SIXTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 3 5 2 1 1 2 1 no no no no 8 8 8 8 W1 W3 W1 W3 3 3 1 1 15 14 16 13 31 31 31 31 15 15 15 15 0 0 0 11 8 8 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 91 81 90 85 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Horn.....1 42 THIRD 4 TRIANGL 35 0 4 0 off 3 0 off off off off 0 0 0 0 6 5 5 1 1 1 8.48 2 no no no no 8 8 8 8 W1 W4 W1 W4 -2 0 1 3 16 15 22 15 7 8 16 10 12 10 0 9 6 6 6 6 9 8 10 7 VOF VOF VOF VOF 99 99 99 50 50 50 96 73 70 80 0 0 2 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Horn.....2 43 FOURTH 5 TRIANGL 35 0 8 0 off 4 0 off off off off 0 0 0 0 6 6 4 1 1 1 1 1 no no no no 8 8 8 8 W1 W3 W1 W2 -1 -1 1 2 16 15 15 11 6 9 9 31 11 5 4 15 4 5 6 6 8 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 96 84 73 78 0 3 2 0 0 66 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Horn.....3 44 SIXTH 5 TRIANGL 32 19 9 0 off 3 0 off off off off 0 0 0 0 6 4 4 3 1 1 1 1 no no no no 8 8 8 8 W3 W2 W1 W6 1 1 -1 3 18 16 15 18 22 10 10 7 11 12 15 12 2 4 3 3 10 10 10 8 VOF VOF VOF VOF 99 99 99 50 50 50 97 99 99 81 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Horn.....4 45 FOURTH 4 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 3 6 4 1.51 1.5 1.5 1.5 no no no no 14 16 16 16 W1 W4 W1 W1 2 2 -2 -2 17 17 21 18 6 9 4 7 15 13 15 15 5 8 1 8 9 9 12 10 VOF VOF VOF VOF 99 99 99 50 50 50 90 83 60 73 0 1 0 0 0 0 30 0 -7 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Horn.....5 46 SIXTH 6 TRIANGL 35 0 8 0 on 4 0 off off off off 0 0 0 0 5 4 6 2 1 2 4 2 no no no no 8 8 8 8 W3 W4 W1 W1 1 1 -1 3 16 17 21 18 6 8 4 7 15 13 15 15 5 6 1 2 9 9 12 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 87 79 0 1 0 0 0 0 30 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Horn.....6 47 FOURTH 5 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 5 4 6 1 1 1 5 2 no no no no 8 8 8 8 W2 W2 W1 W1 -2 0 1 3 21 19 15 16 7 8 3 10 12 10 9 9 6 6 6 6 9 8 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 71 62 78 0 0 0 0 0 0 85 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Horn.....7 48 SIXTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 7 0 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 0 0 15 14 16 14 31 31 31 31 15 15 15 15 19 4 5 8 8 8 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 70 75 86 70 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Horns short 49 SIXTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 0 0 15 14 16 13 31 31 31 31 15 15 15 15 0 0 0 11 8 8 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 91 72 91 70 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Horns 50 SIXTH 7 TRIANGL 29 0 0 0 off 5 0 off off off off 5 4 4 4 2 2 3 2 1.5 1.49 1.5 1.5 no no no no 16 9 16 16 W1 W1 W1 W1 -3 3 -3 0 16 15 17 14 31 31 31 9 15 15 15 14 0 0 0 0 9 8 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 96 82 99 81 0 0 0 0 0 0 0 0 -19 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 99 0 0 0 0 0 Horns BC 51 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 -3 3 22 11 17 11 4 3 1 3 13 13 13 13 1 1 1 1 5 7 5 7 VOF VOF VOF VOF 99 99 99 50 50 50 60 93 99 73 2 1 2 0 0 0 0 31 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 JonesBrass 52 THIRD 0 TRIANGL 28 15 16 0 off 4 0 off off off off 0 0 0 0 5 3 5 0 1 2 1 1 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 0 1 16 29 29 16 3 18 10 4 15 12 12 13 1 19 9 1 9 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 96 65 66 59 0 0 0 0 0 0 90 0 0 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 LightBrass 53 FIFTH 6 TRIANGL 28 15 11 0 off 4 0 off off off off 0 0 0 0 6 5 6 2 1 1 1 1 no no no no 8 8 8 8 W3 W3 W1 W1 2 3 -3 -3 16 14 15 14 18 7 15 17 15 12 15 13 0 2 1 2 7 7 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 90 99 83 0 0 0 0 0 0 90 0 0 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 5 MelloBrass 54 FIFTH 7 TRIANGL 32 6 3 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 0.5 0.5 1 1 no no no no 8 8 8 8 W2 W4 W1 W1 -1 -3 3 1 23 18 26 22 31 10 31 15 15 13 15 13 0 0 0 0 9 8 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 99 82 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 MufflBrass 55 THIRD 7 TRIANGL 32 62 6 0 on 2 0 off off off off 0 0 0 0 6 4 5 3 1 1 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 16 18 15 21 9 9 9 9 13 13 13 13 9 9 9 9 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 66 75 1 1 2 1 35 13 99 63 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Ornt Brass 56 FIFTH 4 TRIANGL 35 0 0 0 on 2 0 off off off off 0 0 0 0 5 6 5 4 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W3 2 0 -1 0 27 27 21 24 6 6 7 18 8 8 15 15 6 6 6 6 7 7 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 92 93 1 1 0 1 0 0 0 10 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 PianoBrass1 57 FIFTH 7 TRIANGL 35 52 3 0 off 6 0 off off off off 0 0 0 0 6 6 3 4 2 4 1 1 no no no no 8 8 8 8 W2 W2 W1 W1 0 0 0 3 31 31 10 8 6 5 31 31 15 11 15 15 4 0 0 0 6 15 9 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 99 78 2 3 3 0 0 99 99 16 -12 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 PianoBrass2 58 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 14 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 -3 3 22 22 17 11 4 12 1 3 13 11 13 13 1 8 1 1 5 4 5 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 53 99 72 2 2 2 1 0 52 0 31 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 PianoBrass3 59 FIRST 7 TRIANGL 32 20 31 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 3 no no no no 8 8 8 8 W1 W6 W1 W4 0 0 0 3 27 26 19 16 31 31 31 15 15 15 15 12 0 0 0 0 11 3 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 72 73 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 POWERBRASS 60 FIRST 7 TRIANGL 32 20 31 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 27 26 19 16 31 31 31 15 15 15 15 12 0 0 0 0 11 3 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 72 73 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 PowerBrass 61 FOURTH 6 TRIANGL 35 1 13 0 on 3 0 off off off off 0 0 0 0 5 5 5 2 1 1 4 1 no no no no 8 8 8 8 W3 W3 W3 W5 0 1 0 0 20 12 21 15 7 10 31 31 12 10 11 15 3 4 0 0 9 9 10 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 84 69 91 0 1 0 0 0 0 76 99 0 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SplatBrass 62 THIRD 7 TRIANGL 31 25 7 0 off 5 0 off off off off 5 3 4 5 2 2 3 2 1 1 3.14 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 0 0 0 15 16 31 12 31 31 15 5 15 15 6 14 0 0 0 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 72 82 1 0 0 1 0 0 35 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 99 0 0 0 0 0 SpitBoneBC 63 FIFTH 7 TRIANGL 33 30 4 0 off 6 0 off off off off 0 0 0 0 1 1 1 1 1 1 1 1 no no no no 8 8 8 8 W2 W4 W1 W1 3 3 -3 -3 24 18 24 18 31 11 31 10 15 14 15 14 0 0 0 0 11 9 11 9 VOF VOF VOF VOF 99 95 99 50 50 50 98 82 98 83 0 1 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Brass 1 64 FIFTH 5 TRIANGL 33 0 0 0 off 6 0 off off off off 0 0 0 0 1 1 1 1 1 1 1 1 no no no no 8 8 8 8 W8 W4 W8 W4 3 3 -3 -3 24 18 24 18 31 12 31 12 15 6 15 6 0 0 0 0 11 9 11 9 VOF VOF VOF VOF 99 99 99 50 50 50 98 90 98 85 0 1 0 1 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Brass 2 65 THIRD 7 TRIANGL 31 0 0 0 off 6 0 off off off off 0 0 0 0 1 0 0 1 1 1 1 1 no no no no 8 8 8 8 W1 W7 W7 W4 0 0 0 0 17 18 15 15 31 10 11 8 15 11 11 14 0 0 0 0 10 7 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 97 45 77 82 1 1 1 1 0 0 0 7 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Brass 3 66 FIFTH 7 TRIANGL 28 28 10 0 off 5 0 off off off off 0 0 0 0 0 2 0 1 1 1 1 1 no no no no 8 8 8 8 W4 W2 W4 W2 -3 -3 3 3 17 15 17 15 9 15 9 15 12 14 12 14 0 0 0 0 10 1 10 1 VOF VOF VOF VOF 99 96 99 50 50 50 99 76 99 76 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Brass 4 67 FIFTH 5 TRIANGL 34 33 0 16 off 3 0 off off off off 0 0 0 0 5 4 6 2 1 2 1 1 no no no no 8 8 8 8 W3 W3 W1 W3 0 1 0 0 21 19 21 18 6 14 6 6 12 10 11 10 8 9 6 3 9 10 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 97 83 99 91 3 1 1 1 0 0 10 33 0 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 TightBrass 68 FIFTH 7 TRIANGL 35 0 0 0 off 5 0 off off off off 0 0 0 0 3 0 0 1 2 2 1 1 no no no no 8 8 8 8 W1 W7 W1 W1 2 3 -2 0 23 21 25 20 14 15 17 14 14 12 15 14 0 0 0 0 10 10 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 99 84 0 1 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Tpt.Sect.* 69 FIFTH 7 TRIANGL 31 14 28 0 off 3 0 off off off on 0 0 0 0 1 3 1 2 1.5 1.5 1.49 1.49 no no no no 16 16 9 9 W2 W4 W2 W4 -3 -3 3 3 10 7 17 31 31 31 31 31 15 15 15 15 0 2 0 2 4 5 7 5 VOF VOF VOF VOF 99 99 99 50 50 50 96 79 99 77 1 0 1 0 12 39 0 27 -7 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Tpt&Woodwd 70 FOURTH 5 TRIANGL 29 30 53 0 off 1 0 off off off off 0 0 0 0 3 2 5 2 1 3.14 1 2 no no no no 8 8 8 8 W2 W3 W1 W3 -1 0 -1 2 12 15 18 14 7 16 12 13 11 9 15 15 5 6 5 3 7 7 8 9 VOF VOF VOF VOF 99 99 99 50 50 50 95 65 76 73 0 0 0 0 0 0 69 35 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 6 WindEns..1 71 FOURTH 5 TRIANGL 29 30 53 0 off 1 0 off off off off 0 0 0 0 4 2 4 2 1 1 0.5 0.5 no no no no 8 8 8 8 W1 W2 W4 W2 -1 0 -1 2 13 22 13 31 7 16 12 13 11 9 15 15 5 6 7 3 7 7 8 9 VOF VOF VOF VOF 99 99 99 50 50 50 95 71 76 80 0 0 0 0 0 0 69 35 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 WindEns..2 72 FIFTH 7 TRIANGL 32 6 4 0 off 5 0 off off off off 0 0 0 0 3 1 3 1 2 2 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 3 2 -2 -3 14 31 16 18 31 31 31 8 15 15 15 14 0 0 0 0 11 7 12 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 86 76 1 0 0 1 0 0 26 54 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Wood Wind 73 FIFTH 7 TRIANGL 28 29 16 0 off 5 0 off off off off 0 0 0 0 5 5 5 5 1 1 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 19 18 9 9 5 10 0 14 10 8 0 5 0 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 77 89 89 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 pianohorn 74 Accordion 1 THIRD 0 TRIANGL 35 0 0 0 off 5 2 off off on on 0 0 2 2 0 0 0 0 2 1 6 0.5 no no no no 8 8 8 8 W1 W1 W2 W7 0 -1 1 0 15 19 31 15 4 4 4 4 2 2 2 2 3 3 3 3 10 7 7 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 56 64 0 0 0 0 16 0 35 16 0 POLY 2 FULL 0 POR 40 0 0 0 0 0 0 50 99 0 0 0 0 0 Accordion* 0 FIFTH 3 TRIANGL 28 10 0 0 off 6 0 off off off off 0 0 0 0 2 0 0 2 2 1 3 1 no no no no 8 8 8 8 W6 W2 W2 W8 3 3 -3 -3 14 31 14 20 31 31 31 18 15 15 15 15 0 0 0 0 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 92 70 83 84 1 1 1 1 6 0 14 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Accordion 1 FIFTH 5 TRIANGL 18 0 3 0 off 6 0 off off off off 0 0 0 0 6 6 6 2 1 4 2 8 no no no no 8 8 8 8 W3 W1 W1 W3 3 -3 1 0 14 31 18 31 31 4 5 5 15 15 13 15 0 0 3 3 10 5 10 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 73 99 62 0 0 0 0 0 0 99 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Accordion1 2 THIRD 7 TRIANGL 18 0 3 0 off 6 0 off off off off 0 0 0 0 6 5 5 1 1 4 2 3 no no no no 8 8 8 8 W3 W1 W1 W1 3 -3 3 -3 14 31 31 16 31 4 11 6 15 15 15 14 0 0 0 0 10 5 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 93 55 54 65 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Accordion2 3 THIRD 7 TRIANGL 18 0 3 0 off 6 0 off off off off 0 0 0 0 6 4 4 3 1 4 4 9 no no no no 8 8 8 8 W3 W3 W1 W1 3 -3 3 -3 14 31 31 16 31 4 11 6 15 15 15 14 0 0 0 0 10 5 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 92 55 54 62 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Accordion3 4 THIRD 7 TRIANGL 18 0 3 0 off 6 0 off off off off 0 0 0 0 6 6 5 2 2 2 2 4 no no no no 8 8 8 8 W1 W2 W1 W2 3 -3 3 -3 14 31 31 16 31 4 11 6 15 15 15 14 0 0 0 0 10 5 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 92 61 54 62 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Accordion4 5 SECOND 7 TRIANGL 25 0 3 0 off 5 0 off off off off 0 0 0 0 1 1 1 1 1 4 2 3 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 3 -3 14 31 31 16 31 4 11 6 15 15 15 14 0 0 0 0 10 5 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 90 73 70 74 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Squeezebox 6 Bag 2 FIFTH 4 TRIANGL 38 60 6 0 off 3 0 off off off off 0 0 0 0 6 5 6 5 3 1 1 3 no no no no 8 8 8 8 W4 W3 W1 W1 0 0 0 0 19 23 19 24 7 10 7 10 15 13 12 12 6 4 6 4 10 2 10 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 96 87 1 0 1 0 0 0 0 0 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bag Pipe 0 Bassoon 3 FIRST 7 TRIANGL 31 8 7 10 off 5 3 off on on on 0 0 0 0 2 2 2 1 2 0.5 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 15 25 16 16 31 31 31 23 15 15 15 15 0 0 0 0 9 7 1 6 VOF VOF VOF VOF 99 99 99 50 50 50 91 78 75 68 0 0 0 0 0 53 0 0 -12 POLY 2 FULL 0 POR 99 0 0 35 30 50 0 50 0 0 0 0 0 0 Bassoon 0 FIFTH 3 TRIANGL 37 41 0 0 off 1 0 off off off off 0 0 0 0 5 5 4 5 1 1 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W4 1 0 0 0 18 18 18 18 13 13 13 0 14 13 13 13 9 9 9 3 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 98 80 0 0 0 0 0 65 67 71 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bassoon..1 1 FIRST 4 TRIANGL 32 12 6 0 off 5 0 off off off off 0 0 0 0 4 5 4 2 2 0.5 1 1 no no no no 8 8 8 8 W1 W1 W3 W1 0 0 0 0 15 24 17 17 31 31 31 22 15 15 15 15 0 0 0 0 8 8 1 6 VOF VOF VOF VOF 99 99 99 50 50 50 88 83 69 56 0 0 0 0 0 71 0 0 -12 POLY 0 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bassoon..2 2 SIXTH 6 TRIANGL 39 33 0 38 off 3 1 off off on off 0 0 0 0 3 3 5 1 2 4 1 1 no no no no 8 8 8 8 W3 W1 W3 W1 0 0 0 0 18 19 19 18 24 6 24 24 15 15 15 15 6 6 6 6 9 9 9 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 69 0 0 0 0 0 71 0 99 -12 POLY 0 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bassoon..3 4 FIFTH 3 TRIANGL 39 33 0 38 off 2 1 off off on off 0 0 0 0 5 3 5 1 1 2 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 18 19 19 12 12 13 6 14 13 14 14 6 6 9 3 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 78 1 1 0 0 0 71 0 99 -12 POLY 0 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bassoon..4 5 SIXTH 1 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 5 6 2 3 0.5 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 2 3 16 22 15 22 31 16 6 8 15 15 15 7 0 0 0 5 10 4 11 8 VOF VOF VOF VOF 99 99 99 50 50 50 96 85 99 89 1 0 0 1 0 70 0 25 -12 POLY 7 FULL 1 POR 99 0 0 50 0 30 10 50 99 0 0 0 0 0 BassoonSyn 6 Clarinet 4 THIRD 0 TRIANGL 37 0 0 41 off 1 1 on off off off 0 0 0 0 5 5 4 1 1 2 4 2 no no no no 8 8 8 8 W1 W1 W1 W1 1 0 0 1 16 31 31 19 31 27 17 18 15 14 12 13 0 6 0 0 11 4 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 85 63 82 71 1 0 0 1 0 65 90 34 -24 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bass Clair 0 THIRD 7 TRIANGL 34 20 10 24 off 5 3 off off on off 0 0 0 0 5 5 5 1 1 1 5 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 16 12 31 19 31 20 17 18 15 14 12 13 0 0 0 0 11 5 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 85 59 79 76 0 0 0 1 0 60 90 34 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 5 Clairnet.1 1 FIFTH 7 TRIANGL 36 39 0 0 on 1 0 off off off off 0 0 0 0 5 2 5 1 2 4 4 4 no no no no 8 8 8 8 W1 W1 W1 W4 0 1 0 2 15 31 16 30 6 6 12 12 14 15 14 15 6 4 12 15 7 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 84 51 1 0 0 0 40 92 71 65 -24 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Clairnet.2 2 SEVENTH 7 TRIANGL 36 2 31 81 off 1 1 on on off off 0 0 0 0 5 4 5 1 0.5 1 0.5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 31 18 31 31 31 31 31 15 15 15 15 5 4 5 6 7 6 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 81 1 0 0 0 14 52 14 54 12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Clairnet.3 3 FIRST 4 TRIANGL 20 0 0 16 off 6 1 on off off off 0 0 0 0 5 4 4 3 1 2 4 6 no no no no 8 8 8 8 W1 W1 W1 W4 0 0 0 3 31 11 19 31 7 6 6 7 7 7 7 7 7 7 7 7 4 4 3 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 74 54 71 1 1 1 1 0 0 0 0 0 POLY 1 FULL 0 POR 99 0 0 0 0 50 0 50 0 0 0 0 0 0 ClaraPiano 4 THIRD 0 TRIANGL 31 20 7 16 off 5 3 off off on off 0 0 0 0 2 2 3 2 1 2 3 2 no no no no 8 8 8 8 W1 W3 W3 W1 0 0 0 0 17 14 19 18 31 20 17 2 15 14 11 14 0 0 0 0 9 5 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 89 72 86 81 1 1 1 1 0 0 0 9 -12 POLY 2 FULL 0 POR 99 0 0 50 30 0 0 50 0 0 0 0 0 0 Clarinet 5 THIRD 7 TRIANGL 30 10 3 5 off 6 3 off off off off 0 0 0 0 2 3 3 1 1 2 5 2 no no no no 8 8 8 8 W1 W4 W1 W1 0 0 0 0 17 21 16 21 31 15 16 10 15 10 14 14 0 0 0 0 10 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 92 84 65 75 1 2 1 0 0 27 31 47 0 POLY 2 FULL 0 POR 40 0 0 26 15 0 0 50 0 0 0 0 0 0 Clarinet 1 6 THIRD 4 TRIANGL 30 10 3 5 off 6 3 off off off off 0 0 0 0 2 2 1 2 1 2 3 2 no no no no 8 8 8 8 W1 W6 W3 W1 0 0 0 0 17 15 18 31 31 15 18 31 15 10 10 15 0 0 0 0 11 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 94 76 74 75 1 2 1 0 0 82 58 15 -12 POLY 2 FULL 0 POR 40 0 0 26 15 0 0 50 0 0 0 0 0 0 Clarinet 2 7 FIFTH 5 TRIANGL 38 7 3 0 off 4 0 off off off off 0 0 0 0 5 4 4 2 1 2 0.5 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 0 0 1 16 16 15 15 4 4 16 11 15 13 12 13 3 3 3 6 9 8 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 90 68 90 71 1 0 0 1 0 60 4 34 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 HolowClair 8 SECOND 7 TRIANGL 29 0 0 0 off 5 0 off off off off 0 0 0 0 1 3 2 2 1 4 7 2 no no no no 8 8 8 8 W1 W4 W2 W8 0 0 -3 2 15 14 14 7 8 7 5 6 13 12 11 15 0 1 1 1 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 69 77 55 2 1 3 2 0 27 16 33 0 POLY 2 FULL 0 POR 99 0 0 50 0 22 0 50 0 0 0 0 0 4 SynClarnet 9 EnglishHorn 5 FIFTH 2 TRIANGL 29 60 0 36 off 2 1 on off off off 6 4 3 0 5 6 6 2 3 2 2 2 no no no no 8 8 8 8 W1 W3 W1 W3 0 0 0 0 18 31 16 20 7 10 10 10 13 12 11 12 6 4 4 4 10 2 10 2 VOF VOF VOF VOF 99 99 99 50 50 50 96 71 99 75 0 0 0 0 10 58 92 82 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 English Hn 0 FlugelHorn 6 THIRD 7 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 2 1 2 1 1 1 2.82 1 no no no no 8 8 48 8 W1 W1 W1 W1 0 0 0 3 14 16 21 14 7 10 17 10 13 0 6 13 1 0 0 0 8 10 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 61 79 77 1 1 1 1 0 13 5 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Flugel * 0 THIRD 5 TRIANGL 35 25 4 0 off 5 0 off off off off 0 0 0 0 6 4 6 1 1 1 1.73 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 1 1 0 16 15 31 12 31 31 15 5 15 15 0 0 0 0 0 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 93 60 55 81 1 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 FlugelHorn 1 Flute 7 FOURTH 0 TRIANGL 30 36 7 0 off 5 2 on on on on 0 4 5 2 2 3 3 2 1.5 3 2.99 8.98 no no no no 16 8 10 14 W2 W4 W4 W2 0 -3 3 -2 13 5 4 2 31 6 5 2 15 13 12 15 0 1 1 1 4 2 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 77 60 1 0 0 0 0 38 18 0 -7 POLY 2 FULL 0 POR 99 0 0 21 0 8 0 50 14 0 0 0 0 0 AdditivFlt 0 FOURTH 5 TRIANGL 35 41 7 54 off 1 1 off off on off 0 0 0 0 4 4 5 0 1 1 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 13 12 22 15 18 19 31 15 15 15 15 15 4 7 4 5 9 7 1 6 VOF VOF VOF VOF 99 99 99 50 50 50 83 60 61 40 0 0 0 0 0 71 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Alto Flute 1 FOURTH 5 TRIANGL 35 41 7 54 off 1 1 off off on off 0 0 0 0 5 4 4 3 1 0.5 0.5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 13 12 22 15 18 19 31 15 15 15 15 15 4 7 4 5 9 7 1 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 60 65 50 0 0 0 0 0 71 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bass Flute1 2 FOURTH 5 TRIANGL 35 41 7 54 off 1 1 off off on off 0 0 0 0 0 0 0 0 0.5 0.5 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 13 12 22 15 18 19 31 15 15 15 15 15 4 7 4 5 9 7 1 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 60 65 50 0 0 0 0 0 71 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bass Flute2 3 THIRD 0 TRIANGL 29 24 6 25 off 4 0 off off off off 0 0 0 0 4 6 2 3 1 1.76 1.12 1 no no no no 8 12 10 8 W1 W1 W1 W1 -1 1 -3 3 15 29 29 16 3 18 10 3 15 14 13 15 1 18 9 2 9 7 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 89 79 1 1 1 1 0 0 0 25 0 POLY 2 FULL 0 POR 40 0 0 49 0 41 18 50 0 0 0 0 0 0 Flute * 4 FOURTH 7 TRIANGL 30 7 7 13 off 5 3 on on on on 0 0 0 0 2 3 2 2 1 1 1 1.73 no no no no 8 8 8 8 W1 W1 W1 W4 0 0 0 0 15 25 18 16 31 31 25 22 15 15 15 7 0 0 0 0 9 7 1 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 71 58 73 1 0 0 0 0 11 13 0 12 POLY 2 FULL 0 POR 99 0 0 30 64 50 0 50 0 0 0 0 0 0 Flute 5 FOURTH 7 TRIANGL 31 8 32 2 off 4 2 on on on on 0 0 0 0 3 2 2 7 2 2 3 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 3 15 11 17 17 4 4 16 14 15 10 10 9 0 0 2 3 9 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 90 77 76 57 1 2 1 1 0 24 27 14 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Flute 1 6 FOURTH 7 TRIANGL 31 8 32 2 off 4 2 on on on on 0 0 0 0 3 2 2 7 2 2 4 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 3 15 11 17 17 4 4 16 14 15 13 10 9 0 0 2 3 9 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 90 77 76 57 1 2 1 1 0 24 27 14 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Flute 1* 7 FIFTH 7 TRIANGL 31 8 32 2 off 4 2 on on on on 0 0 0 0 3 2 3 1 2.99 2.99 3 6 no no no no 10 10 8 8 W1 W1 W1 W1 3 3 -3 -3 15 19 15 19 0 4 0 4 15 14 15 14 0 0 0 0 9 5 9 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 99 53 1 2 1 2 0 3 0 3 -19 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Flute 2 8 FOURTH 5 TRIANGL 30 20 45 99 off 3 0 off off off off 0 0 0 0 4 4 5 4 2 2 2 2 no no no no 8 8 8 8 W3 W4 W1 W3 0 -1 1 0 17 22 11 27 0 21 0 25 13 13 13 13 3 18 3 0 9 1 9 1 VOF VOF VOF VOF 99 99 99 50 50 50 90 86 53 93 0 0 0 0 20 89 70 69 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 6 Flute....1 9 THIRD 0 TRIANGL 28 22 42 0 off 3 1 off on on off 0 0 0 0 4 4 5 2 1 1 1 1 no no no no 8 8 8 8 W1 W3 W5 W1 0 0 0 0 15 18 31 27 0 21 0 25 13 13 13 13 3 18 3 0 9 1 9 1 VOF VOF VOF VOF 99 99 99 50 50 50 91 88 60 75 0 0 0 0 20 89 70 69 12 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Flute....2 10 THIRD 1 TRIANGL 32 20 53 0 off 1 1 off on on off 0 0 0 0 5 3 5 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 13 15 31 27 0 13 0 25 13 15 13 13 3 15 3 0 9 9 9 1 VOF VOF VOF VOF 99 99 99 50 50 50 91 41 51 65 0 0 0 0 20 0 70 69 12 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Flute....3 11 THIRD 7 TRIANGL 31 9 15 30 off 5 3 off on on off 0 0 0 0 4 5 5 3 1 1 2.05 2 no no no no 8 8 11 8 W1 W2 W1 W2 3 -3 3 -3 16 31 25 27 26 18 19 18 15 14 6 13 0 0 0 0 8 5 5 9 VOF VOF VOF VOF 99 99 99 50 50 50 93 52 83 59 1 0 0 1 0 46 0 34 0 POLY 4 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Flute....4 12 THIRD 7 TRIANGL 31 9 15 30 off 5 3 off on on off 0 0 0 0 0 2 1 1 1 4 5.65 4 no no no no 8 8 8 8 W1 W2 W1 W2 3 -3 3 -3 16 31 25 27 26 18 19 18 15 14 6 13 0 0 0 0 8 5 5 9 VOF VOF VOF VOF 99 99 99 50 50 50 93 52 83 59 1 0 0 1 0 46 0 34 0 POLY 4 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Flute....4* 13 FOURTH 7 TRIANGL 35 4 7 0 off 5 0 off off off off 0 0 0 0 6 5 4 5 1 2 1 2.82 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 16 21 16 16 31 31 31 23 15 15 15 0 0 0 0 0 9 7 1 6 VOF VOF VOF VOF 99 99 99 50 50 50 87 69 59 54 0 0 0 0 0 71 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 5 Flute....5 14 FOURTH 7 TRIANGL 32 17 18 51 off 5 1 off off on off 0 0 0 0 2 1 1 0 1 1 2 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 15 31 31 19 31 20 20 30 15 14 1 14 0 0 0 0 11 5 15 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 77 99 1 0 0 1 0 36 0 0 0 POLY 4 FULL 0 POR 99 0 0 55 14 0 0 50 0 0 0 0 0 0 Jazz Flute 15 THIRD 0 TRIANGL 30 13 8 26 off 5 3 off on off off 0 0 0 0 2 2 3 2 1 2 5.65 4 no no no no 8 8 8 8 W1 W1 W3 W1 3 -3 3 -3 16 31 25 27 31 20 20 18 15 14 6 13 0 0 0 0 8 5 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 95 64 80 66 1 2 0 1 0 0 0 20 0 POLY 4 FULL 0 POR 99 0 0 50 35 0 0 50 0 0 0 0 0 0 Pan Floot 16 FIFTH 7 TRIANGL 46 14 9 0 on 3 0 off off off off 0 0 0 0 6 4 6 5 3 3 0.5 6.28 no no no no 8 8 8 8 W1 W1 W1 W1 3 1 -1 -1 16 31 18 18 6 6 6 21 14 9 13 15 2 3 3 15 9 7 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 58 70 0 0 1 0 0 0 0 46 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Pan Flute1 17 FOURTH 7 TRIANGL 31 13 14 30 off 5 3 off on off off 0 0 0 0 5 4 5 1 1 2 1 5 no no no no 8 8 8 8 W2 W1 W1 W1 3 -3 3 -3 16 31 25 27 31 20 20 18 15 14 7 13 0 0 0 0 8 5 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 96 46 55 70 1 0 0 1 0 36 0 34 0 POLY 4 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Pan Flute2 18 FIFTH 0 SHOLD 99 0 20 55 off 4 1 off off off on 0 0 0 0 0 0 0 0 1 1 1 4 no no no no 8 8 8 8 W1 W4 W1 W4 -1 0 0 0 16 17 16 17 4 23 4 23 7 6 7 6 4 1 4 1 10 1 10 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 53 59 99 0 0 3 0 17 0 17 0 12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 6 Pan Flute3 19 FIFTH 0 SHOLD 99 0 12 19 off 6 3 off off off off 0 0 0 0 1 3 2 0 1 4 1 8.04 no no no no 8 8 8 10 W2 W1 W1 W1 0 0 -1 -3 15 31 12 21 31 31 25 25 15 15 15 15 4 17 10 7 7 15 8 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 89 55 0 0 0 0 0 0 0 59 0 POLY 2 FULL 0 POR 59 0 0 31 0 0 0 49 0 0 0 0 0 0 PanFlute * 20 FIFTH 0 TRIANGL 35 41 7 54 off 1 1 off off on off 0 0 0 0 5 5 6 7 1 1 1 5 no no no no 8 8 8 8 W3 W5 W3 W3 -2 1 0 2 19 31 21 31 21 31 31 18 10 15 15 10 21 29 0 18 9 7 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 99 90 0 0 0 0 0 71 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Perc Flute1 21 FIFTH 0 TRIANGL 35 41 7 54 off 1 1 off off on off 0 0 0 0 0 0 1 1 1 1 1 5 no no no no 8 8 8 8 W3 W5 W1 W1 -2 1 0 2 19 31 21 31 21 31 31 18 10 15 15 10 21 29 0 18 9 7 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 99 90 0 0 0 0 0 71 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Perc Flute2 22 FOURTH 0 TRIANGL 28 4 11 0 on 5 2 on on off off 0 0 0 0 2 2 2 2 1.5 3 2.99 6 no no no no 16 8 10 8 W1 W1 W1 W1 0 -3 3 -2 16 31 31 31 8 25 20 23 14 13 11 13 0 0 9 17 6 1 9 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 93 85 1 0 0 1 0 0 0 0 -19 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Perc Flute3 23 FOURTH 5 TRIANGL 35 18 10 53 off 1 1 off off on off 0 0 0 0 5 3 5 3 2 2 1 2.05 no no no no 8 8 8 11 W1 W4 W1 W1 0 0 0 0 15 15 16 16 31 31 31 23 15 15 15 0 0 0 0 0 9 7 1 6 VOF VOF VOF VOF 99 99 99 50 50 50 94 59 44 52 0 0 0 0 0 71 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Soft Flute 24 SEVENTH 5 TRIANGL 33 0 5 0 off 4 0 off off off off 0 0 0 0 6 3 5 3 1 2 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 0 0 18 21 7 24 1 28 1 18 13 13 13 14 3 21 3 21 10 10 10 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 88 1 1 1 1 0 0 0 2 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Song Flute 25 FIFTH 2 SHOLD 99 0 12 0 off 3 0 off off off off 0 0 0 0 1 2 4 3 1 5 7 10.99 no no no no 8 8 8 8 W1 W3 W1 W5 0 0 0 0 13 18 13 18 24 24 14 24 14 13 7 11 3 0 4 1 4 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 94 75 99 88 2 2 2 0 0 0 0 22 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SynthFlute 26 FrenchHorn 8 FIFTH 6 TRIANGL 22 0 0 0 off 3 0 off off off off 0 0 0 0 0 2 1 2 0.5 0.5 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 -3 22 10 14 12 13 8 31 8 15 15 15 15 0 5 0 3 6 7 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 99 84 0 0 0 0 0 10 29 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Fr.Horn * 0 SECOND 7 TRIANGL 26 25 8 10 off 4 2 on on on on 0 0 0 0 2 2 3 2 1 1 3.14 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 -3 3 -3 12 14 31 8 31 31 14 5 15 15 0 0 0 0 0 0 6 6 6 6 VOF V24 VOF VOF 99 99 99 50 50 50 97 67 59 71 1 0 0 1 0 0 14 0 -12 POLY 4 FULL 0 POR 99 0 0 50 30 0 0 50 0 0 0 0 0 0 FrenchHorn 1 THIRD 7 TRIANGL 37 0 4 0 off 2 0 off off off off 0 0 0 0 6 3 5 1 1 1 3.14 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 1 1 0 18 15 13 15 31 31 15 5 15 15 0 0 0 0 0 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 54 57 75 1 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 FrnchHorn1 2 FOURTH 3 TRIANGL 37 0 10 0 off 3 0 off off off off 0 0 0 0 5 4 5 5 1 1 4 1 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 0 0 18 15 13 15 31 31 15 5 15 15 0 0 0 0 0 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 95 72 64 78 1 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 FrnchHorn2 3 Harmonica 9 FIFTH 4 TRIANGL 41 0 0 0 on 2 0 off off off off 0 0 0 0 5 4 6 3 1 1 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 0 29 18 16 16 31 12 31 13 15 15 15 15 0 6 4 1 13 9 12 10 VOF VOF VOF VOF 99 99 99 50 50 50 78 79 99 78 0 1 0 0 0 0 0 10 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Harmonic.1 0 FIFTH 5 TRIANGL 41 0 0 0 on 2 0 off off off off 0 0 0 0 6 5 5 2 1 0.5 1 1 no no no no 8 8 8 8 W2 W1 W1 W1 0 3 0 0 29 18 16 11 31 12 31 13 15 15 15 15 0 6 4 1 13 9 12 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 90 87 0 1 1 1 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Harmonic.2 1 SECOND 3 TRIANGL 31 33 0 37 off 3 2 on off on off 0 0 4 0 0 0 0 1 1 0.5 5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 0 0 13 12 14 13 5 8 9 13 15 12 15 13 1 0 1 1 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 60 66 0 0 0 0 0 0 0 0 12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 41 0 0 0 0 0 Harmonica* 2 FIRST 0 TRIANGL 35 15 2 27 off 5 3 off on on on 0 0 0 0 3 1 2 1 3 1 3 1 no no no no 8 8 8 8 W2 W6 W6 W6 1 -1 1 2 13 12 12 16 11 11 14 9 14 14 13 13 1 2 2 2 9 3 8 6 VOF V48 VOF VOF 99 99 99 50 50 50 99 79 75 62 2 2 3 2 0 0 0 0 0 POLY 4 FINGER 0 POR 99 0 0 40 80 50 0 50 0 0 0 0 0 0 Harmonica1 3 SEVENTH 0 TRIANGL 29 4 1 39 off 5 1 on on on off 0 0 0 0 1 1 1 2 1 1 1 3 no no no no 8 8 8 8 W5 W6 W4 W5 0 0 0 0 10 12 12 14 31 31 31 31 15 15 15 15 0 0 0 0 10 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 90 95 92 75 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Harmonica2 4 FIFTH 0 TRIANGL 31 0 11 0 off 4 1 on on on on 7 3 3 7 4 3 5 1 2 1 1 7.07 no no no no 8 8 8 8 W1 W2 W1 W2 0 0 0 -3 13 31 15 31 31 31 16 31 15 15 15 15 0 0 9 0 8 3 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 97 41 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Harmonica3 5 FIFTH 0 SAWUP 29 60 4 0 off 0 0 off off off off 7 3 3 7 5 4 6 2 1 1 1 14.13 no no no no 8 8 8 8 W1 W3 W3 W2 0 0 0 -3 13 16 15 31 31 10 16 9 15 6 15 10 0 5 9 31 8 10 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 91 47 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Harmonica4 6 SEVENTH 0 TRIANGL 31 0 11 0 off 4 1 on on on on 7 3 3 7 5 2 5 2 1 0.5 2 2 no no no no 8 8 8 8 W4 W4 W1 W1 0 0 0 -3 13 31 15 31 31 31 16 31 15 15 15 15 0 0 9 0 8 7 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 90 42 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Harmonica5 7 Oboe 10 THIRD 4 TRIANGL 35 11 8 10 off 5 1 on on on on 7 0 7 0 0 0 0 0 2 0.5 3 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 9 15 14 11 15 13 24 17 15 15 15 15 0 0 0 0 9 8 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 84 64 80 99 3 3 3 3 0 18 99 99 0 POLY 7 FULL 0 POR 99 0 0 50 0 24 30 50 99 0 0 0 0 0 BC1 Oboe 0 FIFTH 0 TRIANGL 34 20 25 27 off 3 2 off on off on 0 0 0 0 1 2 1 3 3 2 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 -3 -3 15 31 16 28 7 10 7 10 15 13 15 12 0 0 0 0 10 2 10 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 58 99 51 1 0 1 0 9 34 8 35 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Oboe * 1 THIRD 0 TRIANGL 31 17 8 14 off 5 3 off on off off 0 0 0 0 3 1 2 1 2 0.5 2 0.5 no no no no 8 8 8 8 W1 W2 W3 W3 0 0 0 0 15 18 16 16 26 20 10 18 14 14 13 13 0 0 0 0 10 5 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 89 91 75 81 1 0 3 0 6 35 48 5 12 POLY 2 FULL 0 POR 99 0 0 40 25 0 0 50 0 0 0 0 0 0 Oboe1 2 FOURTH 0 TRIANGL 31 10 15 0 off 5 0 off off off off 0 0 0 0 3 1 1 2 3 1 2 3 no no no no 8 8 8 8 W1 W2 W4 W3 0 0 0 0 18 31 21 20 31 31 20 18 15 15 10 10 0 0 0 0 11 3 5 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 78 60 83 1 1 1 1 6 0 9 10 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Oboe2 3 SIXTH 1 TRIANGL 34 7 12 67 off 5 0 off off off off 0 0 0 0 4 3 4 2 1 1 5 2 no no no no 8 8 8 8 W1 W1 W1 W1 -1 0 1 3 16 16 15 27 4 4 4 4 12 12 12 12 6 6 6 6 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 91 77 80 77 0 0 0 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Oboe.....1 4 FIFTH 6 TRIANGL 34 7 12 67 off 5 0 off off off off 0 0 0 0 4 4 5 1 2 1 2 4 no no no no 8 8 8 8 W1 W1 W1 W1 -1 0 1 3 16 16 15 27 4 4 4 4 12 12 12 12 6 6 6 6 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 69 95 70 0 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Oboe.....2 5 FIFTH 6 TRIANGL 34 7 12 67 off 5 0 off off off off 0 0 0 0 0 0 0 1 3 1 2 4 no no no no 8 8 8 8 W1 W1 W1 W1 -1 0 1 3 16 16 15 27 4 4 4 4 12 12 12 12 6 6 6 6 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 69 95 70 0 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Oboe.....2* 6 THIRD 6 TRIANGL 35 37 8 30 on 5 1 on on on on 0 0 0 0 4 3 5 1 3 0.5 3 0.5 no no no no 8 8 8 8 W1 W1 W4 W1 0 1 0 1 9 15 13 12 15 13 24 17 15 15 0 15 0 0 0 0 9 8 8 9 VOF VOF VOF VOF 99 99 99 50 50 50 80 71 73 85 3 3 3 1 0 18 99 99 0 POLY 2 FINGER 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Oboe.....3 7 THIRD 0 TRIANGL 34 10 17 30 on 3 1 off on on off 0 0 0 0 4 2 4 4 3 14 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 22 20 30 31 6 8 12 16 11 11 11 11 1 7 4 2 9 7 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 96 53 80 73 0 1 1 1 10 66 66 27 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Ornt Oboe 8 Ocarina 11 FIFTH 7 TRIANGL 46 39 7 54 off 2 0 off off off off 0 0 0 0 7 5 6 4 4 4 4 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 0 0 18 19 19 31 3 3 29 3 14 3 15 15 31 15 0 24 9 6 10 3 VOF VOF VOF VOF 99 99 99 50 50 50 90 49 93 99 2 2 1 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Ocarina 0 Piccolo 12 FIFTH 7 TRIANGL 33 13 14 33 off 4 2 off on off on 0 0 0 0 1 0 2 0 6 6 5.99 11.89 no no no no 8 8 12 15 W1 W1 W1 W2 -2 3 -3 3 16 12 14 25 19 18 19 18 15 15 15 15 0 0 0 0 9 5 9 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 60 99 49 1 0 1 0 11 19 10 44 -7 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Piccolo * 0 SEVENTH 0 TRIANGL 40 41 0 0 off 1 0 off off off off 0 0 0 0 5 6 6 6 2 2 2 5.65 no no no no 8 8 8 8 W1 W2 W1 W3 0 0 0 0 16 21 15 31 6 3 4 21 9 8 9 9 4 4 5 4 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 50 0 0 0 0 0 71 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Piccolo..1 1 SEVENTH 0 TRIANGL 40 34 7 0 off 2 0 off off off off 0 0 0 0 5 4 5 0 2 2 2 2 no no no no 8 8 8 8 W1 W3 W3 W1 0 0 0 0 18 18 18 31 18 4 9 31 14 14 14 14 15 12 9 31 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 44 0 0 0 0 0 71 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Piccolo..2 2 SEVENTH 0 TRIANGL 40 34 7 0 off 2 0 off off off off 0 0 0 0 0 1 3 0 1 4 4 1 no no no no 8 8 8 8 W1 W3 W3 W1 0 0 0 0 18 18 18 31 18 4 9 31 14 14 14 14 15 12 9 31 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 44 0 0 0 0 0 71 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Piccolo..2* 3 SEVENTH 0 TRIANGL 40 41 0 0 off 1 0 off off off off 0 0 0 0 4 4 4 4 4 4 4 8 no no no no 8 8 8 8 W2 W1 W3 W1 0 0 0 0 18 18 18 18 5 5 5 5 14 14 14 14 5 5 5 5 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 88 80 82 74 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Piccolo..3 4 Recorder 13 FIFTH 6 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 4 5 3 1 2 1 7 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 18 18 19 22 22 18 24 6 7 13 12 15 18 0 0 9 10 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 94 81 99 78 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 OrntRecord 0 THIRD 7 TRIANGL 27 4 15 6 off 4 1 on on on on 0 0 0 0 3 2 4 4 1 3 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 0 3 17 20 20 20 8 0 31 10 14 15 15 10 0 0 15 0 11 3 8 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 45 50 54 0 0 0 0 2 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 67 0 0 0 50 0 0 0 0 0 0 Recorder1 1 THIRD 7 TRIANGL 27 4 15 6 off 4 1 on on on on 0 0 0 0 3 2 4 4 1 4 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 0 3 17 20 20 20 8 0 31 10 14 15 15 10 0 0 15 0 11 3 8 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 45 50 54 0 0 0 0 2 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 67 0 0 0 50 0 0 0 0 0 0 Recorder2 2 SEVENTH 5 TRIANGL 37 42 38 54 off 1 1 off on off off 0 0 0 0 6 4 5 1 1 1 1 2 no no no no 8 8 8 8 W4 W1 W1 W1 0 0 0 0 15 15 15 24 0 0 0 31 13 13 13 14 3 3 3 22 10 10 10 1 VOF VOF VOF VOF 99 99 99 50 50 50 97 97 97 93 1 1 1 3 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Recorder.1 3 SIXTH 0 TRIANGL 32 42 21 67 on 2 1 off off on off 0 0 0 0 5 5 5 3 2 2 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 15 13 13 24 18 10 21 31 12 0 12 14 7 6 7 16 9 10 9 1 VOF VOF VOF VOF 99 99 99 50 50 50 95 99 99 0 0 0 0 3 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Recorder.2 4 SEVENTH 6 TRIANGL 32 42 21 67 on 2 1 off off on off 0 0 0 0 5 6 5 1 2 2 1 2 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 0 0 16 19 16 24 3 29 3 28 13 14 13 14 3 21 3 21 10 12 10 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 63 1 0 1 3 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Recorder.3 5 SEVENTH 6 TRIANGL 32 42 21 67 on 2 1 off off on off 0 0 0 0 7 5 6 1 1 2 1 2 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 0 0 16 19 16 24 3 29 3 28 13 14 13 14 3 21 3 21 10 12 10 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 63 1 0 1 3 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Recorder.4 6 SEVENTH 5 TRIANGL 33 42 0 0 off 1 0 off off off off 0 0 0 0 1 1 1 1 0.5 1 1 2 no no no no 8 8 8 8 W2 W1 W1 W4 0 0 0 0 16 16 15 24 31 15 9 29 15 15 4 15 3 10 10 21 9 10 12 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 98 94 94 1 1 1 2 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Recorder.5 7 Saxophone 14 FIRST 7 TRIANGL 35 0 0 0 on 4 0 off off off off 0 0 0 0 0 3 1 0 0.5 2 5.65 0.5 no no no no 8 8 8 8 W3 W2 W1 W1 0 0 0 0 15 12 17 15 31 31 31 31 15 15 15 15 0 0 0 0 8 9 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 68 55 82 0 3 0 3 0 58 0 49 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Alto Sax 0 FIRST 5 TRIANGL 35 0 0 0 off 4 0 off off off off 7 4 3 4 5 5 5 5 1 0.5 5.65 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 1 1 15 12 17 15 31 31 31 31 15 15 15 15 0 0 0 0 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 53 99 0 3 0 3 0 58 0 49 0 POLY 5 FULL 0 POR 99 0 0 50 0 10 86 50 99 0 0 0 0 0 BC1 AltSax 1 THIRD 7 TRIANGL 33 0 0 0 off 5 0 off off off off 7 4 3 4 0 0 0 0 1 0.5 5.65 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 3 0 16 16 16 16 31 31 31 31 15 15 15 15 0 0 0 0 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 48 68 0 0 0 0 0 42 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 99 0 0 0 0 0 <BC1> Sax 2 THIRD 7 TRIANGL 30 23 12 0 off 5 0 off off off off 7 0 0 0 3 1 1 1 2 1 12.72 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 0 -2 16 29 29 29 31 31 15 4 15 15 10 14 0 0 0 0 10 3 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 70 81 1 1 1 1 0 17 0 0 0 MONO 2 FINGER 15 POR 99 0 0 50 1 0 0 50 99 0 0 0 0 0 BCSexyPhon 3 THIRD 7 TRIANGL 30 9 2 0 off 6 0 off off off off 0 3 3 7 2 2 2 1 0.5 0.5 5 0.5 no no no no 8 8 8 8 W1 W4 W4 W3 0 -3 0 3 22 31 31 31 27 11 18 10 15 10 13 14 0 0 0 0 10 10 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 76 82 1 1 1 1 0 28 9 13 0 POLY 2 FULL 0 POR 40 0 0 11 0 0 0 50 0 0 0 0 0 0 ElectroSax 4 FIFTH 7 TRIANGL 41 0 0 0 on 3 0 off off off off 0 0 0 0 6 5 5 4 1 1 1 2 no no no no 8 8 8 8 W2 W1 W2 W2 0 3 0 0 24 18 27 31 31 12 31 13 15 15 15 15 0 6 4 0 12 9 12 10 VOF VOF VOF VOF 99 99 99 50 50 50 98 92 92 64 0 0 0 0 0 0 0 12 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 GreatDXled 5 THIRD 7 TRIANGL 30 17 11 0 off 5 0 off off off off 0 0 0 0 2 2 3 1 2 1 11 1 no no no no 8 8 8 8 W3 W4 W2 W4 0 0 0 0 16 15 16 17 31 31 14 4 15 15 12 14 0 0 0 0 10 3 8 6 VOF V24 V24 V12 99 99 99 50 50 50 99 80 68 76 1 1 2 1 0 52 99 51 -12 POLY 4 FINGER 0 POR 99 0 0 50 2 50 0 50 0 0 0 0 0 0 MelloTenor 6 THIRD 7 TRIANGL 30 9 9 0 off 5 2 on on on on 0 0 0 0 2 3 2 2 1 0.5 7.25 0.5 no no no no 8 8 12 8 W3 W3 W6 W2 0 0 3 0 16 17 17 16 31 31 9 31 15 15 13 15 0 0 0 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 43 74 1 0 0 0 0 10 0 0 12 POLY 4 FULL 0 POR 99 0 0 50 49 0 0 50 0 0 0 0 0 0 RaspAlto 7 THIRD 6 TRIANGL 32 0 0 0 off 5 0 off off off off 0 0 0 0 1 4 4 3 1 1 3 1 no no no no 8 8 8 8 W2 W2 W7 W1 0 0 0 0 20 16 18 20 31 12 19 8 15 10 11 10 0 0 0 0 10 9 6 8 VOF VOF VOF VOF 99 99 99 50 50 50 95 69 80 73 1 1 1 1 0 33 27 23 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sax 1 8 THIRD 6 TRIANGL 32 0 0 0 off 5 0 off off off off 0 0 0 0 1 4 4 3 2 1 4.24 1 no no no no 8 8 8 8 W6 W2 W7 W1 0 0 0 0 18 17 21 16 19 15 19 8 10 10 9 10 0 0 0 0 10 9 6 8 VOF VOF VOF VOF 99 99 99 50 50 50 95 78 65 73 1 1 1 1 0 15 0 44 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sax 2 9 FIRST 5 TRIANGL 35 0 0 0 off 4 0 off off off off 7 4 3 0 6 6 5 3 1 0.5 6 0.5 no no no no 8 8 8 8 W2 W2 W1 W1 0 0 1 1 17 10 10 16 31 31 31 6 15 15 15 3 0 0 0 18 8 8 8 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 41 99 0 3 0 1 0 58 0 49 0 POLY 5 FULL 1 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sax Synth 10 SECOND 7 TRIANGL 30 17 11 0 off 4 2 off off on on 0 0 4 4 2 2 3 1 2 1 11 1 no no no no 8 8 8 8 W3 W4 W2 W4 0 0 0 0 24 15 12 10 31 31 14 4 15 15 12 14 0 0 0 0 10 3 8 6 VOF VOF V12 VOF 99 99 99 50 50 50 99 84 77 69 1 1 1 1 0 54 15 29 -12 POLY 2 FINGER 0 POR 99 0 0 50 4 0 0 50 35 0 0 0 0 0 Saxophone* 11 FOURTH 7 TRIANGL 34 11 7 0 on 4 0 off off off off 7 4 3 0 4 2 5 1 1 1 5.99 0.5 no no no no 8 8 12 8 W2 W3 W5 W1 0 0 3 0 17 18 16 31 3 7 4 3 15 15 15 15 3 4 2 1 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 88 74 61 55 0 0 0 0 0 42 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 5 Saxophone1 12 FOURTH 7 TRIANGL 33 0 0 0 off 5 0 off off off off 7 4 3 0 3 2 3 0 1 1 5.99 0.87 no no no no 8 8 12 8 W2 W5 W2 W1 0 0 3 0 17 14 17 19 5 31 31 30 14 13 14 15 0 0 0 0 8 9 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 94 75 59 46 0 0 0 0 0 42 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Saxophone2 13 FOURTH 7 TRIANGL 33 0 0 0 off 5 0 off off off off 7 4 3 0 0 2 3 0 1 0.5 5.65 0.87 no no no no 8 8 8 8 W1 W5 W1 W1 0 0 3 0 17 14 17 19 5 31 31 30 14 13 14 15 0 0 0 0 8 9 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 94 75 59 55 0 0 0 0 0 42 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Saxophone3 14 FOURTH 7 TRIANGL 33 0 0 0 off 5 0 off off off off 7 4 3 0 5 3 5 0 1 1 5 1 no no no no 8 8 11 8 W2 W2 W5 W1 0 0 3 0 16 15 31 20 30 31 3 3 14 13 14 15 0 0 0 1 8 9 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 61 65 0 0 0 0 0 75 61 25 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Saxophone4 15 FOURTH 7 TRIANGL 33 0 0 0 off 5 0 off off off off 7 4 3 0 1 2 2 0 1 0.5 6.92 0.5 no no no no 8 8 8 8 W2 W2 W5 W1 0 0 3 0 16 15 31 20 30 31 3 3 14 13 14 15 0 0 0 1 8 9 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 61 65 0 0 0 0 0 75 61 25 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Saxophone5 16 FIRST 7 TRIANGL 35 0 0 0 on 4 0 off off off off 7 4 3 0 4 5 4 2 0.5 0.5 5.99 1 no no no no 8 8 12 8 W1 W4 W1 W1 0 0 1 1 15 12 17 15 31 31 31 31 15 15 15 15 0 0 0 0 8 7 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 61 82 0 3 0 3 0 58 0 49 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 Saxophone6 17 FIRST 7 TRIANGL 35 0 0 0 on 4 0 off off off off 7 4 3 0 0 2 1 2 1 1 5.65 0.5 no no no no 8 8 8 8 W1 W4 W1 W1 0 0 1 1 15 12 17 15 31 31 31 31 15 15 15 15 0 0 0 0 8 7 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 61 82 0 3 0 3 0 58 0 49 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 Saxophone7 18 THIRD 6 TRIANGL 30 17 10 0 off 5 0 off off off off 0 0 0 0 4 4 5 3 1 0.5 7.22 0.5 no no no no 8 8 10 8 W4 W4 W5 W2 0 0 0 0 17 17 17 16 31 31 15 15 15 14 14 15 0 0 0 0 9 8 8 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 47 79 0 1 0 0 0 20 0 10 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 5 Saxophone8 19 FOURTH 7 TRIANGL 33 5 9 0 on 5 0 off off off off 7 4 3 0 4 4 4 3 1 1 5 1 no no no no 8 8 8 8 W1 W5 W3 W5 0 0 0 0 17 12 15 19 30 31 31 30 14 13 14 15 0 0 0 0 8 9 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 57 70 0 0 0 0 0 42 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Saxophone9 20 FOURTH 7 TRIANGL 33 5 9 0 on 5 0 off off off off 7 4 3 0 0 1 1 0 1 0.5 1 1 no no no no 8 8 8 8 W1 W4 W3 W1 0 0 0 0 17 12 15 19 30 31 31 30 14 13 14 15 0 0 0 0 8 9 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 57 70 0 0 0 0 0 42 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Saxophone10 21 THIRD 0 TRIANGL 30 9 9 0 off 5 2 on on on on 0 0 0 0 4 2 2 2 1 0.5 7 1 no no no no 8 8 8 8 W2 W3 W6 W1 0 0 3 0 17 19 17 16 31 31 9 31 15 15 8 15 0 0 0 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 88 30 44 1 0 0 0 0 38 45 12 12 POLY 4 FULL 0 POR 99 0 0 50 49 18 0 50 0 0 0 0 0 0 SopranoSax 22 Trombone 15 THIRD 7 TRIANGL 39 0 41 0 off 2 0 off off off off 0 0 0 0 5 3 5 1 1 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W1 W2 W1 0 0 0 0 18 21 22 15 31 31 25 31 15 15 15 15 0 0 18 0 8 8 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 94 44 84 74 0 0 1 0 0 0 1 0 -12 POLY 6 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Mute Trmbn 0 THIRD 7 TRIANGL 29 0 0 0 off 5 0 off off off off 0 0 0 0 0 2 3 3 1 1 3.18 1 no no no no 8 8 11 8 W1 W1 W1 W1 0 3 0 3 14 12 31 11 7 10 8 10 13 13 13 13 0 0 0 0 8 6 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 42 78 1 1 1 1 0 0 20 0 -12 POLY 5 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Trombone * 1 THIRD 7 TRIANGL 32 9 3 0 off 5 0 off off off off 0 0 0 0 2 2 3 2 0.5 0.5 1.57 0.5 no no no no 8 8 8 8 W1 W2 W2 W1 0 0 0 0 15 16 16 14 24 12 17 11 15 10 5 14 0 0 0 0 11 10 10 9 VOF VOF VOF VOF 99 82 99 50 50 50 99 71 71 80 1 1 1 1 0 28 9 15 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Trombone1 2 THIRD 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 3.14 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 0 0 0 19 15 31 15 31 31 14 31 15 15 0 15 0 0 0 0 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 90 46 82 66 0 0 2 0 0 0 1 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Trombone2 3 THIRD 6 TRIANGL 39 0 41 0 off 2 0 off off off off 0 0 0 0 6 4 5 1 1 1 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 21 20 15 31 31 14 31 15 15 0 15 0 0 0 0 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 94 54 87 82 0 0 2 0 0 0 1 0 -12 POLY 6 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Trombone.1 4 THIRD 7 TRIANGL 39 0 41 0 off 2 0 off off off off 0 0 0 0 4 4 6 1 1 1 3.14 0.5 no no no no 8 8 8 8 W1 W2 W2 W1 0 0 0 0 18 21 15 15 31 31 16 31 15 15 6 15 0 0 5 0 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 93 43 80 79 0 0 2 0 0 0 1 0 -12 POLY 6 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Trombone.2 5 Trumpet 16 THIRD 0 TRIANGL 35 25 6 0 off 5 0 off off off off 0 0 0 0 7 5 4 5 1.5 1.49 3.12 1.5 no no no no 16 9 10 16 W1 W4 W1 W4 0 3 1 -3 16 16 15 16 31 31 16 5 15 15 0 11 0 0 0 0 7 7 12 8 VOF VOF V24 VOF 99 99 99 50 50 50 96 84 45 88 1 1 1 1 0 15 26 15 -7 POLY 4 FULL 0 POR 99 0 0 29 0 8 39 50 0 0 0 0 0 0 2 Trumpets 0 THIRD 7 TRIANGL 35 19 4 0 off 6 0 off off off off 4 4 7 7 0 0 0 0 1 1 3.14 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 14 16 16 31 31 31 31 15 15 15 15 0 0 0 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 60 36 72 1 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 99 0 0 0 0 0 BC1Trumpet 1 THIRD 5 TRIANGL 35 19 4 0 off 6 0 off off off off 4 4 7 7 6 5 5 5 1 1 3.14 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 14 16 16 31 31 31 31 15 15 15 15 0 0 0 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 32 73 1 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 3 3 50 99 0 0 0 0 0 BC1 Trumpt 2 FIFTH 7 TRIANGL 31 0 6 0 on 4 2 off off off on 0 0 0 0 3 0 3 0 1.5 1.5 1.5 1.49 no no no no 16 16 16 97 W5 W4 W1 W1 3 -3 3 -3 15 12 15 31 8 8 7 6 15 15 14 15 4 7 3 6 5 5 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 99 72 0 0 0 0 0 5 8 0 -7 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 FanfarTpts 3 THIRD 3 TRIANGL 34 35 5 0 off 2 0 off off off off 0 0 0 0 6 4 5 1 2 2 3 2 no no no no 8 8 8 8 W1 W1 W1 W3 1 1 1 -3 18 21 15 15 7 27 16 10 15 10 9 13 9 7 7 8 9 8 7 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 54 76 1 2 2 0 0 0 2 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Mute Trmpt 4 FOURTH 7 TRIANGL 36 0 12 0 on 5 0 off off off off 0 0 0 0 4 3 5 2 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 18 19 17 16 16 22 5 15 13 9 13 6 6 6 6 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 74 50 1 1 2 0 1 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Ornt Trmpt 5 SIXTH 7 TRIANGL 99 0 14 0 off 4 2 off off off on 2 5 5 2 2 2 2 3 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -1 0 1 -3 21 21 21 16 31 31 31 9 15 15 15 14 0 0 0 0 9 9 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 82 0 0 0 2 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 99 0 72 0 50 0 0 0 0 0 0 Trumpet * 6 THIRD 0 TRIANGL 28 25 0 0 off 5 3 on off off off 0 0 0 0 2 3 2 2 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 0 0 0 16 25 18 12 31 31 11 5 15 15 15 15 0 0 0 0 9 7 1 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 71 58 73 1 0 0 0 0 11 13 0 0 POLY 4 FULL 0 POR 99 0 0 11 0 0 25 50 99 0 0 0 0 5 Trumpet 7 THIRD 7 TRIANGL 35 19 4 0 off 6 0 off off off off 4 4 7 7 0 0 0 0 1 1 3.14 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 14 16 16 31 31 31 31 15 15 15 15 0 0 0 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 60 36 72 1 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Trumpet 8 THIRD 7 TRIANGL 31 9 4 0 off 6 0 off off off off 0 0 0 0 2 2 3 1 1 1 1.57 1 no no no no 8 8 8 8 W6 W4 W4 W1 0 0 0 0 20 19 16 18 24 14 15 16 15 10 5 10 0 0 0 0 11 10 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 64 78 1 1 1 1 0 28 9 10 0 POLY 2 FULL 0 POR 40 0 0 19 0 0 0 50 0 0 0 0 0 0 Trumpet 1 9 THIRD 7 TRIANGL 32 20 26 0 off 3 0 off off off off 0 0 0 0 4 1 7 1 1 1 0.87 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 19 12 12 16 9 14 12 14 15 14 0 14 0 0 0 0 10 3 9 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 64 51 78 0 0 0 0 18 40 0 0 0 POLY 2 FULL 0 POR 99 0 0 60 0 0 0 50 0 0 0 0 0 0 Trumpet 2 10 THIRD 7 TRIANGL 32 20 26 0 off 3 0 off off off off 0 0 0 0 4 1 7 1 1 1 0.87 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 17 10 10 14 8 13 11 13 15 14 0 14 0 0 0 0 10 3 9 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 64 51 78 0 0 0 0 18 40 0 0 12 POLY 2 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Trumpet 3 11 THIRD 7 TRIANGL 29 30 15 0 on 4 0 off off off off 0 0 0 0 7 6 6 2 1 2.05 1 1 no no no no 8 11 8 8 W3 W1 W3 W1 1 0 0 -1 18 15 15 15 7 15 7 10 13 0 11 15 5 5 9 7 10 10 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 66 80 0 2 1 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Trumpet..1 12 THIRD 7 TRIANGL 29 30 15 0 on 4 0 off off off off 0 0 0 0 1 1 0 2 1 1.73 1 1 no no no no 8 8 8 8 W3 W1 W3 W1 1 0 0 -1 18 15 15 15 7 15 7 10 13 0 11 15 5 5 9 7 10 10 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 66 80 0 2 1 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Trumpet..1* 13 THIRD 7 TRIANGL 35 30 23 16 off 3 1 off off off on 0 0 0 0 6 4 6 1 1 1 3.14 1 no no no no 8 8 8 8 W3 W1 W1 W1 1 0 0 -1 18 15 15 15 7 15 16 10 13 0 14 15 5 5 6 7 10 10 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 70 78 0 2 1 1 0 0 2 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Trumpet..2 14 THIRD 5 TRIANGL 35 0 0 0 off 2 0 off off off off 0 0 0 0 6 4 6 2 1 1 4 1 no no no no 8 8 8 8 W3 W3 W1 W1 1 2 -2 -1 19 21 15 15 7 28 16 10 13 12 14 13 6 6 6 6 9 8 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 73 89 0 2 1 1 0 0 2 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Trumpet..3 15 THIRD 7 TRIANGL 34 0 8 46 off 4 1 off off off on 0 0 0 0 7 3 6 2 1 1 5 1 no no no no 8 8 8 8 W1 W4 W1 W1 1 0 -1 -1 19 21 15 15 7 28 3 10 13 12 9 13 6 6 6 6 9 8 7 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 49 83 0 2 1 1 0 0 2 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Trumpet..4 16 THIRD 5 TRIANGL 34 35 5 0 off 2 0 off off off off 0 0 0 0 6 5 6 2 1 1 1.73 1 no no no no 8 8 8 8 W2 W3 W1 W1 1 0 -1 -1 21 21 15 12 7 28 3 10 13 12 9 13 6 6 6 6 9 8 7 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 52 77 0 2 1 1 0 0 2 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Trumpet..5 17 THIRD 7 TRIANGL 30 25 5 0 off 5 0 off off off off 4 4 7 7 2 2 3 2 1 1 2.9 1 no no no no 8 8 9 8 W1 W6 W6 W1 0 0 0 0 18 14 15 15 31 31 26 31 15 15 13 15 0 0 11 4 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 93 75 54 81 1 0 0 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Trumpet81Z 18 FOURTH 7 TRIANGL 29 3 15 1 off 4 0 off off off off 0 0 0 0 2 1 0 0 1.5 1.49 1.51 3.23 no no no no 16 9 14 9 W1 W2 W1 W7 0 -3 3 3 19 15 17 19 31 31 14 0 15 15 9 15 1 3 6 0 8 10 10 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 93 54 58 0 0 0 0 0 5 8 0 -7 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 7 TrumptSolo 19 THIRD 7 TRIANGL 30 19 4 0 off 4 0 off off off off 4 4 7 7 2 2 3 2 1 1 3.14 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 14 16 15 31 31 31 31 15 15 15 15 0 0 11 4 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 47 82 1 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 0 0 0 0 0 0 0 Trumpet V 20 Tuba 17 FOURTH 5 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 6 4 5 3 1 0.5 0.5 0.5 no no no no 8 8 8 8 W4 W1 W3 W1 0 0 0 0 14 20 15 31 15 13 16 15 15 9 9 13 3 2 3 5 8 7 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 72 76 0 0 0 0 20 89 70 69 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 << Tuba >> 0 THIRD 7 TRIANGL 31 0 0 0 off 5 0 off off off off 0 0 0 0 2 1 1 1 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W2 W2 W1 0 0 0 0 16 16 16 15 10 10 12 15 10 10 11 9 0 0 0 0 11 6 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 69 82 0 0 0 0 14 0 0 21 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Tuba 1 Whistle 18 SIXTH 0 TRIANGL 32 22 21 67 off 5 0 off off off off 0 0 0 0 0 0 0 0 2 2 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 3 -1 -3 0 12 11 10 31 15 16 15 31 14 14 0 15 0 0 28 0 9 9 9 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 0 0 0 0 0 0 0 0 0 0 POLY 12 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Whistle * 0 SEVENTH 7 TRIANGL 37 11 38 99 off 3 1 off on on off 0 0 0 0 6 4 5 3 2 2 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 19 19 19 19 6 6 6 28 13 13 13 14 3 3 3 21 10 10 10 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 0 0 0 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Whistle..1 1 EIGHTH 2 TRIANGL 34 0 20 0 on 3 0 off off off off 0 0 0 0 4 3 4 5 4 4 4 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 0 2 7 8 7 8 31 31 31 31 15 15 15 15 2 2 2 7 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 60 98 3 0 1 0 0 0 78 71 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 6 Whistle..2 2 FIFTH 7 TRIANGL 35 9 11 0 off 4 0 off off off off 0 0 0 0 6 4 6 3 2 1 2 2 no no no no 8 8 8 8 W3 W3 W1 W1 0 0 0 0 13 25 15 31 8 16 9 15 14 8 15 0 0 0 0 0 9 9 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 66 93 66 0 0 0 0 26 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 51 0 0 0 0 0 0 Whistle..3 3 FIFTH 7 TRIANGL 35 3 15 0 off 6 0 off off off off 0 0 0 0 2 0 2 4 2 0.71 2 1.73 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 13 25 15 31 8 16 9 15 14 8 15 0 0 0 0 0 9 9 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 94 56 78 78 0 0 0 0 26 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 51 0 0 0 0 0 0 Whistling 4 STRINGS 2 Strings 0 FIFTH 5 TRIANGL 38 33 32 71 off 1 0 off off off off 0 0 0 0 4 0 5 4 1 1 1 1 no no no no 8 8 8 8 W1 W2 W2 W5 0 1 -3 -3 13 14 12 13 4 5 10 10 12 12 14 14 18 18 0 0 8 8 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 79 99 81 0 0 0 1 20 0 7 40 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 AnlgStr..1 0 FIFTH 6 TRIANGL 38 33 17 71 on 3 0 off off off off 0 0 0 0 3 1 4 2 1 1 1 1 no no no no 8 8 8 8 W2 W4 W3 W6 -1 1 -3 -3 11 13 13 14 9 12 9 10 14 14 15 14 0 1 0 0 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 77 91 73 0 0 0 1 20 0 5 30 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 AnlgStr..2 1 FIFTH 5 TRIANGL 38 33 22 59 off 2 0 off off off off 0 0 0 0 4 2 4 2 2 2 2 4 no no no no 8 8 8 8 W4 W1 W1 W3 0 1 0 0 11 14 13 13 10 10 10 9 13 14 15 14 1 1 2 3 5 4 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 93 75 89 57 1 0 0 1 20 0 10 40 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 AnlgStr..3 2 SIXTH 5 TRIANGL 34 10 10 8 off 2 1 off on off on 0 0 0 0 3 4 5 2 1.32 2 4 4 no no no no 15 8 8 8 W2 W3 W1 W4 1 1 1 -1 11 13 12 31 31 7 31 19 15 6 15 14 6 14 0 0 8 6 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 83 68 0 0 0 0 10 14 82 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AnlgStr..4 3 FIFTH 2 TRIANGL 32 15 10 0 on 5 0 off off off off 0 0 0 0 3 1 3 4 1 3 2 1 no no no no 8 8 8 8 W2 W5 W2 W1 -1 2 -3 3 9 12 9 12 31 3 31 3 15 4 15 4 0 30 1 30 5 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 91 73 87 72 0 1 1 0 37 35 21 86 -12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgStr..5 4 FIFTH 2 TRIANGL 32 15 10 0 on 5 0 off off off off 0 0 0 0 4 2 3 4 2 4 2 10 no no no no 8 8 8 8 W3 W3 W4 W6 -2 2 -3 3 10 13 10 13 31 3 31 3 15 4 15 4 0 30 1 30 5 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 93 72 91 65 0 1 1 0 37 35 21 86 -12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgStr..6 5 FIFTH 5 TRIANGL 32 15 10 0 on 5 0 off off off off 0 0 0 0 4 2 4 4 2 2 2 10 no no no no 8 8 8 8 W2 W1 W1 W1 -2 2 -3 3 11 14 13 15 4 11 21 15 15 14 15 13 0 1 3 4 5 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 91 75 87 58 0 0 0 0 37 35 21 86 -12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgStr..7 6 SIXTH 5 TRIANGL 34 14 12 8 off 5 1 off on off on 0 0 0 0 4 3 5 2 2 2 8 2 no no no no 8 8 8 8 W4 W4 W2 W7 -3 -1 0 -2 10 13 12 19 3 4 7 11 15 13 15 13 6 2 6 5 8 6 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 86 68 0 0 0 2 10 14 82 10 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AnlgStr..8 7 FIFTH 4 TRIANGL 30 14 12 8 off 5 1 off on off on 0 0 0 0 4 1 4 2 2 2 2 1 no no no no 8 8 8 8 W1 W6 W1 W4 1 3 -1 0 10 13 10 14 3 12 9 11 15 14 15 14 0 0 6 5 6 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 93 60 94 66 0 1 0 0 10 14 82 0 -24 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 5 AnlgStr..9 8 FIFTH 4 TRIANGL 34 14 12 8 off 5 1 off on off on 0 0 0 0 0 0 0 0 2 6 1 4 no no no no 8 8 8 8 W1 W2 W1 W4 1 3 -1 0 12 13 10 14 3 12 9 11 15 14 15 14 0 0 6 5 6 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 93 60 94 66 0 1 0 0 10 14 82 0 -24 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 5 AnlgStr..9* 9 FIFTH 2 TRIANGL 23 19 41 0 on 3 0 off off off off 0 0 0 0 3 2 4 3 1 1 1 1 no no no no 8 8 8 8 W1 W4 W2 W1 3 3 3 -3 9 12 9 12 31 3 31 3 15 4 15 4 0 30 1 30 5 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 96 72 95 76 0 1 1 0 37 35 21 86 -12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgStr.10 10 FIFTH 2 TRIANGL 23 19 41 0 on 3 0 off off off off 0 0 0 0 0 0 0 0 0.5 2 0.5 0.5 no no no no 8 8 8 8 W1 W4 W2 W1 3 3 3 -3 9 12 9 12 31 3 31 3 15 4 15 4 0 30 1 30 5 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 96 72 95 76 0 1 1 0 37 35 21 86 -12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgStr.10* 11 FIFTH 4 TRIANGL 33 25 14 0 off 2 0 off off off off 0 0 0 0 3 3 5 3 1 4 2 1 no no no no 8 8 8 8 W3 W4 W1 W1 0 -3 3 -3 12 16 12 11 31 3 11 11 15 4 14 12 0 30 1 2 5 5 7 5 VOF VOF VOF VOF 99 99 99 50 50 50 96 72 92 73 0 0 0 1 37 88 38 35 12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgStr.11 12 FIFTH 4 TRIANGL 32 15 10 0 on 5 0 off off off off 0 7 0 0 6 4 5 4 1 2 2 4 no no no no 8 8 8 8 W1 W1 W1 W1 -2 2 -3 3 9 12 9 12 31 3 31 3 15 4 15 4 0 30 1 30 5 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 91 82 87 83 0 1 1 0 37 35 21 86 -12 POLY 12 FULL 0 POR 99 0 0 50 0 5 9 50 99 0 0 0 0 0 BC1 String 13 FIFTH 5 TRIANGL 33 30 15 0 off 2 0 off off off off 0 0 0 0 4 2 4 2 2 4 2 2 no no no no 8 8 8 8 W2 W5 W2 W1 3 1 2 -3 12 23 12 24 8 12 11 3 11 15 15 8 0 7 4 2 6 1 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 91 61 97 74 0 0 0 1 37 63 18 45 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 DualStr..1 14 FIFTH 5 TRIANGL 33 30 15 0 off 2 0 off off off off 0 0 0 0 0 0 0 0 1 9 2 2 no no no no 8 8 8 8 W2 W5 W2 W1 3 1 2 -3 13 31 9 24 8 12 16 3 11 15 15 8 0 7 4 2 6 1 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 91 61 97 74 0 0 0 1 37 63 18 45 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 DualStr..1* 15 FIFTH 6 TRIANGL 33 30 15 0 on 3 0 off off off off 0 0 0 0 4 2 4 3 2 1 2 2 no no no no 8 8 8 8 W4 W4 W2 W1 2 -2 2 0 10 22 9 24 8 12 16 3 11 15 15 8 0 7 4 2 6 1 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 96 73 89 70 0 0 0 1 37 63 18 45 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 4 DualStr..2 16 FIFTH 6 TRIANGL 33 30 15 0 on 3 0 off off off off 0 0 0 0 0 0 0 0 2 2 1 6 no no no no 8 8 8 8 W4 W4 W2 W1 2 -2 2 0 10 22 9 24 8 12 16 3 11 15 15 8 0 7 4 2 6 1 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 96 73 89 70 0 0 0 1 37 63 18 45 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 4 DualStr..2* 17 FIFTH 5 TRIANGL 33 30 15 0 off 2 0 off off off off 0 0 0 0 4 3 3 3 2 6 2 2 no no no no 8 8 8 8 W3 W2 W1 W2 3 -3 0 0 10 21 10 21 7 10 16 5 11 15 15 8 1 1 1 1 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 91 56 89 80 0 0 0 0 37 63 18 45 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 DualStr..3 18 FIFTH 4 TRIANGL 33 35 15 0 off 1 0 off off off off 0 0 0 0 7 4 4 4 2 2 4 4 no no no no 8 8 8 8 W4 W1 W1 W1 2 -3 3 -1 9 24 15 22 16 3 4 22 15 8 15 14 3 3 3 3 5 5 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 95 91 99 88 0 0 0 0 0 0 0 0 -24 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 ELKA Strg1 19 FIFTH 7 TRIANGL 35 25 20 0 on 4 0 off off off off 0 0 0 0 6 4 5 3 1 1 2 2 no no no no 8 8 8 8 W2 W4 W3 W1 2 -3 3 -1 11 20 11 21 16 3 4 22 15 8 15 14 3 3 3 3 5 5 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 75 1 0 0 1 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 ELKA Strg2 20 FIFTH 7 TRIANGL 31 6 9 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 3 3 2.99 2.99 no no no no 8 8 10 10 W2 W4 W4 W1 -3 -3 3 3 10 10 8 11 31 7 31 4 15 15 15 15 0 0 0 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 73 0 0 0 0 0 20 0 20 -19 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 5 HiString 1 21 FIFTH 7 TRIANGL 31 6 9 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 3 3 2.99 2.99 no no no no 8 8 10 10 W2 W4 W4 W1 -3 -3 3 3 20 25 21 23 31 7 31 4 15 15 15 15 0 0 0 0 5 4 5 4 VOF V24 VOF V12 99 99 99 50 50 50 99 81 99 73 0 0 0 0 0 20 0 20 -19 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 5 HiString 2 22 FIFTH 0 TRIANGL 28 0 0 0 off 4 0 off off off off 0 0 0 0 1 0 1 0 19.65 2 2 1 yes no no no 31 8 8 8 W2 W1 W1 W4 0 0 -3 3 30 26 26 29 18 13 12 0 0 0 0 0 31 31 31 31 9 15 5 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 50 99 63 2 3 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Kalimba * 23 FIFTH 7 TRIANGL 29 6 10 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 1.5 1.5 2.99 1.49 no no no no 16 16 10 9 W2 W4 W4 W1 -3 -3 3 3 8 10 9 11 31 7 31 4 15 15 15 15 0 0 0 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 73 0 0 0 0 0 20 0 20 -19 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 5 LowString 24 THIRD 7 TRIANGL 21 0 0 0 on 2 0 off off off off 0 0 0 0 5 2 5 2 1 0.5 7 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 27 31 10 9 21 25 12 12 13 13 4 10 6 22 6 7 12 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 54 82 1 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 OrntPluck1 25 FIFTH 7 TRIANGL 21 0 0 0 on 2 0 off off off off 0 0 0 0 4 3 6 2 1 15 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 22 24 24 24 15 12 12 7 12 12 12 15 22 15 15 25 6 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 99 99 1 2 1 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 OrntPluck2 26 SEVENTH 7 TRIANGL 35 0 10 0 off 3 0 off off off off 0 0 0 0 1 1 1 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 29 28 31 17 29 18 31 9 10 0 10 14 17 0 8 15 15 11 10 VOF VOF VOF VOF 99 99 99 50 50 50 90 99 99 60 0 0 0 0 0 0 0 9 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Pizz. 1 27 SECOND 7 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 1 0 1 1 1 1 0.5 1 no no no no 8 8 8 8 W1 W1 W4 W1 0 0 0 0 31 31 31 29 19 31 31 16 0 10 0 15 17 22 31 10 15 10 15 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 81 65 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Pizz. 2 28 SECOND 0 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 2 1 1 4 1 1 1 1 no no no no 8 8 8 8 W1 W1 W4 W1 0 3 -3 0 30 31 31 30 23 31 13 19 11 12 0 0 16 7 0 21 7 10 10 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 91 88 0 0 0 0 0 0 0 43 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Pizzicato* 29 SECOND 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 1 1 1 1 1 1 1 1 no no no no 8 8 8 8 W1 W1 W4 W1 0 3 0 0 31 31 31 27 16 31 13 24 0 12 0 10 0 0 0 21 6 7 8 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 91 97 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 Pizzicato 30 FIFTH 7 TRIANGL 29 8 10 0 off 5 0 off off off off 0 0 0 0 1 1 1 1 3 1.5 2.99 1.49 no no no no 8 16 10 9 W4 W3 W4 W4 -3 -3 3 3 9 10 9 11 31 7 31 4 15 15 15 15 0 0 0 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 73 0 0 0 0 0 0 0 0 -19 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 4 ReverbStrg 31 FIFTH 7 TRIANGL 33 6 12 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 1.5 1.5 1.51 1.51 no no no no 16 16 14 14 W2 W4 W4 W2 1 1 -2 -2 9 10 9 11 31 7 31 4 15 15 15 15 0 0 0 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 72 0 0 0 0 0 20 0 20 -19 POLY 4 FULL 0 POR 99 0 0 50 0 12 0 50 0 0 0 0 0 6 StrgPad A 32 FIFTH 7 TRIANGL 31 6 19 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 1.51 1.51 1.5 1.49 no no no no 14 14 16 9 W2 W4 W4 W2 -3 -3 3 -1 8 10 9 11 31 7 31 4 15 15 15 15 0 0 0 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 99 71 0 0 0 0 0 20 0 20 -19 POLY 4 FULL 0 POR 99 0 0 50 0 17 0 50 0 0 0 0 0 5 StrgPad B 33 FIFTH 7 TRIANGL 28 0 11 0 off 4 2 off on off on 0 0 0 2 2 0 2 0 3 3 2.99 2.99 no no no no 8 8 10 10 W1 W1 W1 W1 3 -3 -3 3 9 31 9 31 7 0 7 0 14 14 14 14 0 0 0 0 6 4 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 99 69 2 0 2 0 32 10 0 10 -19 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 16 0 0 0 0 0 Stringed * 34 FIFTH 4 TRIANGL 33 35 12 0 off 2 0 off off off off 3 4 3 2 3 4 3 2 1 7 2 1 no no no no 8 8 8 8 W2 W3 W3 W4 -1 -3 0 0 11 10 11 24 7 12 16 3 13 15 15 8 5 7 4 2 5 3 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 92 57 97 66 0 0 0 1 37 99 18 65 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 StringEns1 35 FIFTH 4 TRIANGL 33 35 12 0 off 2 0 off off off off 0 0 0 0 0 0 0 0 2 8 1 1 no no no no 8 8 8 8 W2 W1 W1 W3 -1 -3 0 0 11 22 11 24 7 12 16 3 13 15 15 8 5 7 4 2 5 3 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 92 63 97 66 0 0 0 1 37 99 18 65 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 StringEns1* 36 FIFTH 5 TRIANGL 33 35 12 0 off 2 0 off off off off 0 0 0 0 3 2 3 2 2 2 4 2 no no no no 8 8 8 8 W2 W7 W1 W3 -1 -3 0 0 11 22 9 24 8 12 16 1 13 15 15 8 5 7 4 2 6 6 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 94 59 96 79 0 0 0 1 37 99 18 65 -12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 StringEns2 37 FIFTH 5 TRIANGL 33 35 12 0 off 2 0 off off off off 0 0 0 0 0 0 0 0 2 6 4 1 no no no no 8 8 8 8 W2 W7 W1 W1 -1 -3 0 0 11 22 9 24 8 12 16 1 13 15 15 8 5 7 4 2 6 6 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 94 59 96 79 0 0 0 1 37 99 18 65 -12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 StringEns2* 38 FIFTH 5 SQUARE 33 46 30 9 off 2 0 off off off off 0 0 0 0 3 2 3 2 2 2 1 1 no no no no 8 8 8 8 W2 W2 W2 W5 3 3 1 3 10 10 15 31 6 6 9 21 5 5 15 14 26 26 7 10 8 8 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 52 99 77 1 1 0 1 40 40 30 10 0 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 StringEns3 39 FIFTH 5 TRIANGL 28 0 15 0 off 3 0 off off off off 0 0 0 0 4 2 4 1 4 2 2 2 no no no no 8 8 8 8 W2 W4 W3 W3 2 2 0 -3 11 19 11 16 16 9 16 16 14 14 14 14 0 0 0 6 7 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 95 68 99 80 0 0 0 0 20 20 20 57 0 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 StringEns4 40 FIFTH 5 TRIANGL 33 0 15 0 off 3 0 off off off off 0 0 0 0 0 0 0 0 2 2 2 1 no no no no 8 8 8 8 W2 W1 W1 W1 2 2 0 -3 15 19 15 16 16 9 16 16 14 14 14 14 0 0 0 6 7 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 95 68 99 80 0 0 0 0 20 20 20 57 0 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 StringEns4* 41 FIFTH 4 TRIANGL 33 10 14 0 off 3 0 off off off off 0 0 0 0 3 2 4 3 1 1 2 1 no no no no 8 8 8 8 W2 W2 W2 W5 -1 0 1 3 11 11 11 12 11 9 9 10 14 14 12 13 0 0 6 0 5 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 96 57 94 68 0 1 0 0 10 10 12 57 0 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 StringEns5 42 FIFTH 4 TRIANGL 33 10 14 0 off 3 0 off off off off 0 0 0 0 0 0 0 0 2 1 2 2 no no no no 8 8 8 8 W2 W2 W2 W5 -1 0 1 3 12 11 15 19 11 9 9 15 14 14 12 13 0 0 6 0 5 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 96 57 94 68 0 1 0 0 10 10 12 57 0 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 StringEns5* 43 FIFTH 5 TRIANGL 33 10 9 0 off 2 0 off off off off 0 0 0 0 3 3 2 1 4 6 2 2 no no no no 8 8 8 8 W3 W2 W2 W5 -1 0 1 3 11 21 11 21 23 15 9 15 14 13 12 13 0 0 6 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 72 98 80 0 1 0 0 10 10 12 57 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 StringEns6 44 FIFTH 5 TRIANGL 33 10 9 0 off 2 0 off off off off 0 0 0 0 0 0 0 0 4 6 2 2 no no no no 8 8 8 8 W3 W2 W2 W5 -1 0 1 3 16 21 16 21 23 15 9 15 14 13 12 13 0 0 6 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 72 98 80 0 1 0 0 10 10 12 57 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 StringEns6* 45 FIFTH 4 TRIANGL 33 10 9 0 off 2 0 off off off off 0 0 0 0 4 3 3 1 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W2 W1 W3 W1 2 1 -3 -3 11 21 12 13 23 15 9 15 14 13 12 13 0 0 6 0 5 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 98 71 97 83 0 0 0 0 5 10 6 40 0 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 StringEns7 46 FIFTH 4 TRIANGL 33 10 9 0 off 2 0 off off off off 0 0 0 0 3 3 5 1 2 2 2 2 no no no no 8 8 8 8 W2 W2 W1 W3 2 1 -3 -3 11 13 11 15 23 15 9 4 14 13 12 13 0 0 6 9 5 5 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 98 68 99 62 0 0 0 1 5 10 6 40 -24 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 StringEns8 47 FIFTH 6 TRIANGL 38 32 32 0 off 1 0 off off off off 0 0 0 0 5 5 5 3 1 8 2 2 no no no no 8 8 8 8 W4 W4 W1 W1 -3 -3 -3 0 12 10 15 28 21 2 21 13 14 9 15 14 6 0 6 0 8 7 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 95 90 98 88 1 1 1 0 0 0 0 10 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 StringLead 48 THIRD 7 TRIANGL 33 14 42 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 1 6 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -2 0 1 10 12 7 17 31 31 31 11 15 15 15 10 5 8 4 5 6 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 87 53 70 67 1 1 1 1 0 34 20 23 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Strings 49 THIRD 7 TRIANGL 33 14 42 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 3 6 3 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 -2 0 1 12 12 14 10 0 0 0 0 15 15 15 15 6 0 0 5 6 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 76 52 70 67 1 1 1 1 0 34 20 23 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Strings* 50 FIFTH 7 TRIANGL 31 6 10 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 3 3 2.99 2.99 no no no no 8 8 10 10 W4 W1 W4 W1 -3 -3 3 3 12 31 12 31 31 4 31 4 15 15 15 15 0 0 0 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 99 70 0 0 0 0 30 30 30 30 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Strings 1 51 FIFTH 7 TRIANGL 31 0 26 0 off 5 0 off off off off 0 0 0 0 1 1 1 2 3 6 2.99 9 no no no no 8 8 10 8 W3 W3 W4 W2 -3 -3 3 3 10 10 12 20 4 4 5 5 12 9 12 9 1 2 2 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 99 61 0 0 0 0 0 1 0 20 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Strings 2 52 FIFTH 7 TRIANGL 31 6 10 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 3 3 2.99 2.99 no no no no 8 8 10 10 W8 W1 W4 W1 -3 -3 3 3 18 31 12 31 31 4 31 4 15 15 15 15 0 0 0 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 99 70 0 0 0 0 30 30 30 30 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Strings 3 53 FIFTH 7 TRIANGL 35 0 46 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 3 2.99 3 3 no no no no 8 10 8 8 W4 W7 W2 W4 3 3 -3 -3 11 31 11 31 31 31 31 31 15 15 15 15 0 0 0 0 5 3 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 86 68 86 70 0 0 0 0 30 0 30 0 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Syn.Str 1 54 FIFTH 7 TRIANGL 32 0 75 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 3 2.99 3 6 no no no no 8 10 8 8 W5 W7 W4 W8 3 3 -3 -3 12 31 12 31 31 31 31 31 15 15 15 15 0 0 0 0 5 3 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 83 59 91 49 0 0 0 0 30 0 30 0 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Syn.Str 2 55 THIRD 7 TRIANGL 29 6 15 0 off 5 0 off off off off 0 0 0 0 1 1 2 0 2 2 2 1 no no no no 8 8 8 8 W2 W4 W1 W1 0 -3 3 0 26 31 31 31 10 9 0 4 15 15 15 15 0 3 0 0 5 2 11 2 VOF V12 V48 V24 99 99 99 50 50 50 92 58 68 68 2 0 0 0 5 21 12 15 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 SynString 56 FIFTH 5 TRIANGL 21 0 0 0 on 0 0 off off off off 7 3 3 7 5 6 4 2 1 7 1 3 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 -3 -1 27 27 31 22 9 19 10 13 12 7 12 13 10 0 10 10 7 12 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 79 1 1 1 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 T Pluck..1 57 THIRD 6 TRIANGL 21 0 0 0 on 0 0 off off off off 7 3 3 7 5 5 6 3 1 0.5 7 2 no no no no 8 8 8 8 W1 W2 W3 W1 0 0 -3 -1 30 27 31 22 10 19 10 13 12 7 12 13 9 0 10 10 6 12 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 79 83 2 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 T Pluck..2 58 FIFTH 5 TRIANGL 34 33 0 0 on 1 0 off off off off 7 3 3 7 5 5 6 3 0.5 10 1 3 no no no no 8 8 8 8 W1 W4 W1 W1 0 -2 -1 3 19 21 28 29 7 13 9 15 14 10 10 12 9 31 15 9 5 3 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 99 83 2 3 1 1 0 18 25 21 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 T Pluck..3 59 FIFTH 7 TRIANGL 29 6 16 0 off 5 0 off off off off 0 0 0 0 3 0 3 0 1.5 1.5 2.99 1.49 no no no no 16 16 10 9 W2 W4 W4 W1 -3 -3 3 3 10 12 11 13 31 8 31 5 15 15 15 15 0 0 0 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 64 0 0 0 0 0 20 0 20 -7 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 5 WarmString 60 FIFTH 7 TRIANGL 41 0 0 0 on 2 0 off off off off 0 0 0 0 5 4 6 3 2 2 2 4 no no no no 8 8 8 8 W1 W4 W1 W1 0 0 0 0 10 13 6 31 4 4 0 0 15 15 15 15 2 2 0 3 6 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 96 61 99 1 1 2 0 92 41 76 86 -24 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 WindString 61 FIRST 5 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 3 3 3 3 1 1 2 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 -3 -1 11 31 31 8 9 2 12 3 11 14 10 11 4 4 2 6 6 3 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 78 88 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 phantstrng 62 Banjo 1 FOURTH 7 SQUARE 58 0 0 0 on 0 2 on off off off 0 0 0 0 1 0 2 1 3 1 8 1 no no no no 8 8 8 8 W1 W3 W3 W1 0 3 -3 2 28 31 31 30 11 18 10 12 9 15 10 12 10 0 9 0 6 1 1 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 88 78 81 2 2 3 2 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 0 66 0 0 50 0 0 0 0 0 0 Banjo * 0 FOURTH 7 TRIANGL 29 0 16 0 off 3 0 off off off off 1 0 0 0 3 0 0 0 3 1 2 9 no no no no 8 8 8 8 W5 W4 W1 W8 -3 3 3 2 31 31 31 31 14 31 7 11 10 15 15 15 10 0 17 8 6 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 64 72 2 2 2 2 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Banjo 1 THIRD 5 TRIANGL 6 0 1 0 off 3 0 off off off off 0 0 0 0 6 5 6 2 1 1 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 2 31 31 31 30 13 2 27 23 9 15 12 12 11 3 8 0 6 1 1 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 66 90 2 1 1 1 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Banjo....1 2 FIFTH 5 TRIANGL 6 0 1 0 off 3 0 off off off off 0 0 0 0 5 6 4 2 1 6 2 1 no no no no 8 8 8 8 W1 W2 W4 W1 -3 3 3 2 31 22 31 30 13 9 27 23 9 0 12 12 11 3 8 0 6 5 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 62 91 2 2 1 1 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Banjo....2 3 THIRD 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 3 0 2 1 3 1 8 1 no no no no 8 8 8 8 W1 W2 W1 W1 -3 3 3 2 31 31 31 30 13 11 27 23 9 15 12 12 11 3 8 0 6 1 1 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 69 95 2 3 3 2 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Old Banjo 4 BassGuitar 2 THIRD 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 1 1 3 0.5 0.5 0.5 1 no no no no 8 8 8 8 W2 W1 W1 W1 0 -3 3 -3 31 31 31 31 31 9 12 13 15 10 11 12 0 0 0 0 12 9 10 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 74 81 0 0 0 1 0 0 0 30 -12 POLY 4 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 AnalogBass 0 THIRD 3 TRIANGL 34 53 10 0 on 7 0 off off off off 0 0 0 0 5 1 6 1 1 1 5 0.5 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 0 0 31 31 21 31 7 15 21 12 13 13 11 10 10 11 8 8 9 3 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 54 97 2 1 1 2 0 12 46 15 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBass.1 1 THIRD 3 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 2 5 1 1 1 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 28 24 28 15 27 9 27 15 12 13 12 4 17 15 12 9 3 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 50 95 2 1 1 0 0 12 46 15 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBass.2 2 THIRD 1 TRIANGL 38 28 2 80 off 3 1 off on on off 0 0 0 0 6 2 5 1 1 1 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -1 0 31 31 31 31 7 21 10 12 15 13 4 12 5 18 10 12 10 5 3 12 VOF VOF VOF VOF 99 99 99 50 50 50 95 87 70 81 2 1 1 0 0 12 46 15 -24 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBass.3 3 THIRD 3 TRIANGL 38 28 2 80 off 3 1 off on on off 0 0 0 0 4 1 5 1 1 1 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -1 0 31 31 31 31 7 21 10 12 15 13 4 12 9 18 10 12 10 5 3 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 70 90 1 2 2 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBass.4 4 THIRD 3 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 2 6 2 0.5 0.5 9 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 28 27 30 18 28 12 31 15 9 4 15 6 10 8 8 9 4 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 70 96 0 0 0 0 0 12 46 15 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBass.5 6 FIFTH 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 3 5 1 0.5 0.5 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 28 27 30 31 31 31 31 15 15 14 12 6 10 8 8 9 4 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 99 90 0 0 0 0 0 12 46 15 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBass.6 7 FIFTH 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 2 5 1 1 0.5 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 -1 -1 3 3 31 28 27 30 31 31 31 31 12 12 15 12 9 9 7 8 10 10 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 12 46 15 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBass.7 8 FIFTH 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 6 1 6 1 1 0.5 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 -1 -1 3 3 28 28 28 28 7 12 7 7 15 15 15 15 7 8 7 7 9 10 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 99 82 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBass.8 9 FIFTH 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 3 5 3 0.5 0.5 0.5 1 no no no no 8 8 8 8 W1 W1 W1 W1 -1 -1 3 3 28 28 28 31 31 15 31 18 15 2 15 2 9 31 6 31 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 94 99 50 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnlgBass9 10 FIFTH 6 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 5 3 4 2 0.5 0.5 8 8 no no no no 8 8 8 8 W2 W2 W1 W1 0 0 0 0 29 25 29 29 18 27 12 18 14 12 13 4 6 9 2 7 9 4 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 66 97 0 0 0 3 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 AnlgCut.BS 11 SIXTH 6 TRIANGL 8 0 11 0 on 2 0 off off off off 0 0 0 0 6 3 7 3 1 1 1 1 no no no no 8 8 8 8 W1 W2 W3 W1 0 1 0 0 31 31 31 31 6 28 27 8 12 4 15 6 18 15 31 2 7 7 7 5 VOF VOF VOF VOF 99 99 99 50 50 50 92 95 93 94 1 2 1 0 0 25 0 20 -24 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 AnlgFunkBS 12 SIXTH 4 TRIANGL 8 0 11 0 on 2 0 off off off off 0 0 0 0 6 4 5 2 1 1 2 1 no no no no 8 8 8 8 W2 W2 W1 W1 -2 3 2 3 28 24 13 21 15 31 31 12 12 15 15 11 7 31 8 1 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 98 99 0 0 1 0 0 25 0 20 -24 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 AnlgSplat 13 SECOND 7 TRIANGL 28 26 3 19 off 6 1 on on on on 0 0 0 7 7 7 6 3 1 3 0.87 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 0 31 21 16 31 11 11 8 25 14 9 5 0 6 0 2 0 10 10 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 42 99 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Bass 1 14 FOURTH 7 TRIANGL 28 26 3 19 off 6 1 on on on on 0 0 0 7 7 7 2 3 1 0.5 0.5 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 0 31 21 31 31 11 11 8 25 14 9 5 0 6 0 2 0 10 10 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 78 78 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Bass 2 15 FOURTH 7 TRIANGL 28 26 3 19 off 6 1 on on on on 0 0 0 7 7 7 2 3 1 3 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 0 31 21 31 31 11 11 8 25 14 9 5 0 6 0 2 0 10 10 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 78 92 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Bass 3 16 SECOND 0 TRIANGL 28 26 3 19 off 6 1 on on on on 0 0 0 7 7 7 2 3 1 3 8 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 0 31 21 4 31 11 11 8 25 14 9 5 0 6 0 2 0 10 10 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 88 92 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Bass 4 17 FIFTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 3 2 7 0.5 0.5 0.5 7 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 7 31 7 15 10 15 10 10 5 0 5 7 6 4 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 99 78 2 0 2 0 0 0 0 15 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Bass 5 18 FIFTH 4 TRIANGL 38 0 0 0 on 3 0 off off off off 0 0 0 0 5 0 4 0 1 1 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 21 31 31 13 22 15 15 10 13 31 31 12 6 15 15 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 89 3 3 2 3 0 0 99 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bass Pluck 19 FOURTH 6 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 6 0 3 1 0.5 0.5 5 9 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 25 27 28 18 28 12 26 14 9 4 4 6 9 2 7 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 79 81 3 2 2 3 14 0 99 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 <<Bass.0>> 20 FIFTH 7 TRIANGL 35 32 0 41 on 2 1 on off off off 0 0 0 0 3 1 3 1 0.5 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 25 24 21 22 7 31 24 9 0 15 14 0 6 12 6 6 7 6 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 98 74 99 76 2 1 1 2 14 0 99 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 <<Bass.1>> 21 SECOND 2 TRIANGL 33 19 0 0 off 0 0 off off off off 0 0 0 0 5 1 3 1 1 1 4 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 25 30 26 25 18 9 9 6 15 0 8 14 6 4 10 2 6 8 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 77 74 2 2 2 2 99 99 99 99 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 <<Bass.2>> 22 THIRD 4 TRIANGL 33 19 0 0 off 0 0 off off off off 0 0 0 0 5 1 5 1 1 1 3 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 -3 3 25 25 24 25 14 14 6 6 15 15 10 14 7 7 8 1 6 6 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 65 90 2 2 2 2 99 99 99 99 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 <<Bass.3>> 23 THIRD 5 TRIANGL 99 0 0 15 off 0 1 on on on on 0 0 0 0 5 1 4 1 1 4 0.5 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 18 28 12 26 14 9 4 4 6 9 2 7 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 78 63 3 2 2 2 14 0 99 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 <<Bass.4>> 24 FIFTH 4 TRIANGL 38 0 0 0 on 3 0 off off off off 0 0 0 0 5 1 3 1 1 2 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 25 25 25 21 12 18 15 22 14 12 13 13 4 4 6 6 10 10 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 99 95 2 2 2 3 14 35 99 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 <<Bass.5>> 25 FOURTH 4 TRIANGL 33 19 0 0 off 0 0 off off off off 0 0 0 0 5 1 2 1 1 1 3 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 -3 3 26 27 30 25 15 13 10 6 14 14 8 14 6 2 12 1 5 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 73 69 2 2 2 2 99 99 99 99 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 <<Bass.6>> 26 THIRD 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 4 1 4 1 2 1 1 7 no no no no 8 8 8 8 W1 W1 W1 W1 0 -2 1 2 30 30 29 30 28 27 1 28 12 13 0 12 5 1 1 5 9 9 12 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 85 83 2 0 2 3 0 12 46 15 -24 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 <<Bass.7>> 27 FIFTH 3 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 1 3 2 2 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -2 1 2 30 18 29 30 28 12 1 28 12 10 0 12 5 7 1 10 9 7 12 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 99 99 0 0 0 0 0 12 46 15 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 <<Bass.8>> 28 FOURTH 3 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 1 3 1 1 1 1 6 no no no no 8 8 8 8 W1 W1 W1 W1 0 -2 1 2 30 24 29 30 28 23 1 28 12 13 10 12 6 3 3 6 9 9 12 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 77 62 2 0 2 2 0 12 46 15 -24 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 <<Bass.9>> 29 FIRST 5 TRIANGL 35 0 25 0 on 3 0 off off off off 0 0 0 0 5 3 5 2 1 10 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 -1 1 2 -2 31 31 31 31 12 18 12 24 15 15 15 15 11 18 2 4 10 8 8 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 84 78 1 0 1 3 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Bass....FM 30 THIRD 7 SHOLD 99 0 0 0 off 7 0 off off off off 0 0 0 0 7 3 3 7 1 0.5 0.5 3 no no no no 8 8 8 8 W1 W1 W4 W5 0 -3 3 -3 31 31 31 31 31 5 6 4 15 14 13 14 2 1 2 7 8 10 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 83 0 1 1 1 0 63 50 0 -12 POLY 4 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 BombBass 31 THIRD 3 TRIANGL 33 19 7 0 off 6 0 off off off off 0 0 0 0 5 1 4 1 0.5 9 6 8 no no no no 8 8 8 8 W1 W2 W1 W1 0 1 0 0 31 24 27 28 18 28 12 18 15 9 13 4 6 9 9 7 7 4 7 8 VOF VOF VOF VOF 99 99 99 50 50 50 95 87 59 86 0 3 2 1 0 99 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 CutupBass 32 FOURTH 7 TRIANGL 28 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 6 3 0.5 0.5 7 0.5 no no no no 8 8 8 8 W1 W2 W2 W1 0 0 0 0 31 31 19 31 9 8 10 1 4 4 4 4 3 3 5 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 60 76 1 2 3 3 0 0 43 0 -12 POLY 2 FINGER 0 POR 99 0 0 75 0 0 0 50 0 0 0 0 0 0 E.Bass 1 33 FIRST 7 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 3 3 1 0.5 5 15 no no no no 8 8 8 8 W1 W4 W1 W1 0 0 0 0 29 30 31 31 12 8 11 15 15 15 15 15 10 9 10 16 9 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 65 67 0 0 0 0 40 35 0 0 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 E.Bass 2 34 FIRST 4 TRIANGL 26 16 20 21 off 4 1 on on on on 0 0 0 0 2 1 0 2 0.5 0.5 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 3 3 25 31 31 6 5 5 5 6 0 14 14 10 8 6 6 7 7 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 51 86 2 2 3 3 0 4 4 6 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 E.Bass 3 35 THIRD 7 TRIANGL 28 0 0 0 off 5 0 off off off off 0 0 0 0 1 0 7 3 1 0.5 9 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 3 3 31 31 31 31 9 1 9 1 9 0 12 4 0 0 6 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 70 61 0 2 3 3 0 0 27 0 -12 POLY 4 FINGER 0 POR 99 0 0 75 0 0 0 50 0 0 0 0 0 0 ElecBass 1 36 THIRD 7 TRIANGL 34 0 0 0 off 6 0 off off off off 0 0 0 0 2 0 3 3 0.5 0.5 1 13 no no no no 8 8 8 8 W1 W2 W1 W2 -3 3 3 3 31 31 31 31 6 6 10 14 2 2 2 2 0 0 0 0 8 7 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 82 75 0 0 2 2 0 59 59 49 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 ElecBass 2 37 FOURTH 7 TRIANGL 30 5 10 0 off 6 0 off off off off 0 0 0 0 6 5 5 5 1 2 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 18 18 13 31 31 31 10 15 15 15 14 0 0 0 7 10 10 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 95 62 95 84 0 0 0 0 1 0 75 77 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Fat Lead.1 38 THIRD 4 TRIANGL 36 0 4 0 on 4 0 off off off off 0 0 0 0 5 4 6 4 0.5 0.5 2 6 no no no no 8 8 8 8 W2 W1 W1 W1 0 2 0 2 22 17 22 11 31 31 4 3 15 15 13 13 0 6 0 1 10 5 10 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 81 73 83 0 1 1 1 39 0 17 39 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Fat Lead.2 39 SECOND 4 TRIANGL 36 0 4 0 on 4 0 off off off off 0 0 0 0 7 4 5 4 1 1 2 5 no no no no 8 8 8 8 W1 W2 W3 W1 0 2 0 2 22 17 22 11 31 31 4 3 15 15 13 13 0 6 0 1 10 5 10 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 86 78 63 0 1 1 1 39 0 17 39 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Fat Lead.3 40 FOURTH 4 TRIANGL 36 38 52 0 on 4 0 off off off off 0 0 0 0 5 4 7 5 0.5 0.5 0.5 1 no no no no 8 8 8 8 W2 W2 W1 W1 0 2 0 2 22 17 22 11 31 31 4 3 15 15 13 13 0 6 0 1 10 5 10 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 99 77 85 0 1 1 1 39 0 17 39 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Fat Lead.4 41 FIRST 7 TRIANGL 30 9 4 0 off 6 0 off off off off 0 0 0 0 3 0 0 3 2 1 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 0 0 31 31 31 31 28 11 6 2 10 12 1 0 4 0 0 3 9 9 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 95 84 0 0 0 3 0 0 0 14 -12 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 filtrsweep 42 THIRD 2 TRIANGL 33 19 0 0 off 0 0 off off off off 0 0 0 0 5 2 6 1 1 1 6 1 no no no no 8 8 8 8 W1 W1 W1 W1 -1 1 -1 1 31 31 31 31 18 18 12 6 14 14 14 14 6 6 2 0 9 9 8 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 69 82 3 3 2 0 0 0 0 0 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 FlangeBass 43 SIXTH 5 TRIANGL 23 33 9 0 off 5 0 off off off off 0 0 0 0 1 0 0 3 1.51 0.75 1.49 0.75 no no no no 14 12 9 12 W1 W1 W1 W1 -3 0 3 -3 17 17 17 10 6 8 6 8 15 15 15 15 3 3 3 2 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 83 1 1 1 1 0 0 0 21 -19 POLY 2 FULL 0 POR 40 0 0 49 0 0 0 50 0 0 0 0 0 0 Fretless * 44 THIRD 7 TRIANGL 33 19 7 0 off 6 0 off off off off 0 0 0 0 5 1 4 2 0.5 3 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 0 25 27 30 8 18 13 13 12 15 14 8 0 5 2 12 12 6 8 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 96 66 74 66 2 2 2 1 99 99 99 99 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Fretless.1 45 THIRD 5 TRIANGL 33 19 7 0 off 6 0 off off off off 0 0 0 0 7 3 5 2 3 1 5 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 0 25 27 30 31 18 13 13 6 15 14 8 0 5 2 12 0 6 5 6 1 VOF VOF VOF VOF 99 99 99 50 50 50 96 70 62 64 2 2 2 3 99 99 99 0 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Fretless.2 46 THIRD 5 SAWUP 21 0 11 0 off 0 0 off off off off 0 0 0 0 5 2 5 1 1 1 3 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 -2 3 1 31 18 24 30 19 31 0 28 12 15 0 12 9 2 0 10 10 7 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 71 75 0 2 0 0 0 0 0 15 -24 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 FunkyBass1 47 THIRD 6 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 4 2 5 0 1 1 4 2 no no no no 8 8 8 8 W1 W1 W1 W1 1 -2 -1 2 31 18 18 30 19 31 31 28 12 15 15 12 9 31 12 10 10 7 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 64 80 0 2 2 0 0 0 0 15 -24 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 FunkyBass2 48 FIRST 7 TRIANGL 25 8 6 0 off 6 0 off off off off 0 0 0 0 1 0 1 0 0.5 0.5 0.5 1 no no no no 8 8 8 8 W1 W1 W1 W7 0 3 -3 0 17 16 21 7 8 11 11 2 11 12 11 15 0 0 15 0 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 81 67 0 1 1 0 0 0 0 0 -12 POLY 4 FINGER 20 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Jaco Bass 49 SECOND 1 TRIANGL 25 8 6 0 off 6 0 off off off off 0 0 0 0 1 0 1 1 0.5 0.5 0.5 2 no no no no 8 8 8 8 W1 W2 W2 W1 0 3 -3 0 15 15 18 31 8 10 11 6 11 12 11 2 0 0 15 5 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 81 70 0 1 1 0 0 0 0 0 -12 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 0 0 0 0 0 0 0 Jaco Bass2 50 FOURTH 7 TRIANGL 28 0 0 0 off 5 1 off off off off 0 0 0 0 1 0 7 3 1 0.5 1 2 no no no no 8 8 8 8 W1 W4 W2 W4 0 -3 3 3 31 31 31 29 9 1 17 10 9 0 13 4 0 0 6 0 9 8 8 8 VOF V12 V12 V24 99 99 99 50 50 50 99 74 65 70 0 2 3 3 0 0 27 0 -12 POLY 4 FINGER 0 POR 99 0 0 75 0 0 0 50 0 0 0 0 0 0 JazzWalker 51 FOURTH 0 TRIANGL 27 0 0 0 on 5 0 off off off off 0 0 0 0 1 0 7 5 1 0.5 9 6 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 0 0 31 31 28 31 20 31 31 31 13 15 15 15 10 0 13 19 10 11 15 15 VOF VOF V48 VOF 99 99 99 50 50 50 99 68 70 99 1 2 2 2 13 0 52 0 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Kai Bass * 52 SECOND 7 TRIANGL 25 0 7 0 off 5 0 off off off off 0 0 0 0 0 1 1 3 0.5 0.5 1 1 no no no no 8 8 8 32 W1 W1 W5 W1 0 0 -3 0 31 31 31 31 9 9 17 16 0 0 0 3 0 0 0 7 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 71 79 1 1 1 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 LatelyBass 53 THIRD 7 TRIANGL 28 0 0 0 off 5 0 off off off off 0 0 0 0 1 0 7 3 1 0.5 1 3 no no no no 8 8 8 8 W1 W1 W6 W1 0 -3 3 3 31 31 31 31 9 1 9 1 15 7 15 10 0 0 6 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 74 67 0 2 3 3 0 0 27 0 -12 MONO 4 FINGER 0 POR 99 0 0 75 0 0 0 50 0 0 0 0 0 0 MonophBass 54 FIFTH 7 TRIANGL 29 0 7 0 off 5 0 off off off off 0 0 0 0 0 2 0 6 0.5 0.5 0.5 0.5 no no no no 8 8 8 32 W2 W6 W2 W8 3 0 -3 0 31 31 31 15 9 9 16 10 15 13 10 3 0 0 0 7 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 96 79 1 1 1 0 0 0 0 0 -12 MONO 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 MoogerBass 55 THIRD 0 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 3 2 6 1 1 5 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 -2 0 0 28 28 28 28 22 16 16 3 14 0 10 13 4 10 10 10 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 98 72 79 85 0 0 0 0 0 0 0 46 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntBass.1 56 FIFTH 0 SQUARE 46 0 17 0 on 1 0 off off off off 0 0 0 0 5 3 5 2 1 5 1 7 no no no no 8 8 8 8 W1 W1 W1 W1 1 -2 0 0 28 28 28 28 19 15 21 15 14 0 10 6 10 10 10 10 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 65 99 77 0 0 0 0 0 0 0 46 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntBass.2 57 FOURTH 0 TRIANGL 29 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 3 5 0.5 0.5 0.5 5 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 0 0 15 31 24 23 29 28 21 12 14 13 9 4 4 4 10 9 8 1 4 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 79 88 3 3 2 2 20 0 0 24 -12 POLY 2 FULL 0 POR 40 0 0 83 0 0 0 50 0 0 0 0 0 0 Precision* 58 SECOND 7 TRIANGL 25 0 7 0 off 5 0 off off off off 0 0 0 0 0 1 1 3 0.5 0.5 1 1 no no no no 8 8 8 32 W2 W2 W4 W2 0 0 -3 0 31 31 31 31 9 9 17 14 15 13 11 3 0 0 0 7 8 8 8 8 VOF VOF VOF V48 99 99 99 50 50 50 94 82 85 76 1 1 1 0 0 0 0 0 -12 MONO 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 ResoMonoBs 59 THIRD 4 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 5 1 4 1 0.5 0.5 6 0.5 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 0 0 31 31 31 31 18 18 12 18 14 14 3 4 6 6 2 7 9 9 8 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 88 91 3 3 2 3 14 0 99 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Roto Bass 60 SECOND 7 TRIANGL 25 0 9 0 off 5 0 off off on off 0 0 0 0 0 0 0 0 0.5 0.5 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -3 0 31 31 25 31 5 5 9 14 12 12 0 5 5 5 0 5 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 71 65 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SUPER BASS 61 FIFTH 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 3 5 3 2 1 1 1 no no no no 8 8 8 8 W2 W1 W2 W1 0 0 1 -1 25 19 20 18 3 9 2 8 14 12 13 10 15 6 15 8 10 6 9 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 99 83 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Science.BS 62 FIRST 0 TRIANGL 28 0 0 0 off 5 0 off off off off 0 0 0 0 1 0 6 7 1 0.5 9 7.85 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 3 3 31 31 31 31 9 1 9 15 9 0 12 6 0 0 6 10 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 75 90 0 2 3 3 0 0 27 0 -12 POLY 4 FINGER 0 POR 99 0 0 80 0 60 0 50 0 0 0 0 0 0 SlapBass 63 FOURTH 7 TRIANGL 25 0 9 0 off 5 0 off off off off 0 0 0 0 6 1 4 1 1 10 3 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -3 0 30 24 30 31 16 12 16 8 12 0 12 8 7 31 7 0 9 9 9 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 76 70 1 2 1 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SlapBass.1 64 THIRD 5 TRIANGL 25 0 9 0 off 5 0 off off off off 0 0 0 0 6 1 3 1 1 1 1 5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -3 0 30 22 20 24 16 0 10 28 12 9 15 12 7 0 5 9 9 10 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 82 68 1 2 2 1 0 0 0 0 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SlapBass.2 65 FOURTH 7 TRIANGL 31 0 0 0 off 4 0 off off off off 0 0 0 0 4 3 3 3 1 0.5 0.5 12.11 no no no no 8 8 8 48 W1 W8 W1 W7 3 0 -3 3 31 31 31 31 19 15 19 15 14 14 11 8 6 5 6 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 99 93 96 75 1 1 2 0 0 78 28 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SqncrBass 66 FOURTH 7 TRIANGL 31 0 0 0 off 4 0 off off off off 0 0 0 0 4 3 3 3 1 0.5 0.5 12.11 no no no no 8 8 8 48 W1 W4 W2 W4 3 0 -3 3 31 31 31 31 19 15 19 15 15 15 11 8 6 5 6 15 8 11 8 14 VOF V48 VOF VOF 99 99 99 50 50 50 99 97 82 66 1 1 2 0 0 57 28 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SqncrBass2 67 THIRD 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 1 4 1 1 1 3 7 1 no no no no 8 8 8 8 W1 W1 W2 W1 0 0 -3 2 31 31 20 31 30 0 31 3 15 15 11 0 0 18 23 16 7 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 93 99 87 73 2 1 3 2 0 94 61 52 -24 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Bass 1 68 SIXTH 7 TRIANGL 27 66 25 0 off 5 0 off off off off 0 0 0 0 0 0 0 2 0.5 0.5 0.5 1 no no no no 8 8 8 8 W1 W6 W3 W2 -3 -2 3 3 31 31 31 29 10 10 10 14 0 0 0 9 0 0 0 0 10 10 10 6 VOF VOF VOF VOF 99 99 99 50 50 50 97 97 97 81 2 0 0 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Bass 2 69 FIRST 0 TRIANGL 31 0 0 0 off 6 0 off off off off 0 0 0 0 0 2 2 2 0.5 0.5 0.5 5 no no no no 8 8 8 8 W1 W2 W6 W2 0 0 3 0 30 30 31 31 9 4 12 18 0 9 5 7 3 2 3 12 7 7 7 2 VOF VOF VOF VOF 99 99 99 50 50 50 87 85 70 89 2 2 2 2 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Bass 3 70 THIRD 7 TRIANGL 28 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 5 3 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W2 W2 W1 0 0 0 0 24 26 21 26 9 9 11 11 4 4 4 4 3 3 5 0 9 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 88 78 1 2 3 2 0 0 10 0 -12 POLY 2 FINGER 0 POR 99 0 0 75 0 0 0 50 0 0 0 0 0 0 Sy.Bass 4 71 THIRD 0 TRIANGL 32 0 0 0 off 6 0 off off off off 0 0 0 0 2 4 5 2 0.5 0.5 0.5 5 no no no no 8 8 8 8 W6 W2 W6 W2 0 0 3 0 30 30 31 31 5 6 12 18 9 9 5 11 3 2 3 8 7 7 7 2 VOF VOF VOF VOF 99 99 99 50 50 50 93 86 70 89 2 2 2 2 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Bass 5 72 THIRD 0 TRIANGL 32 0 0 0 off 6 0 off off off off 0 0 0 0 2 4 5 2 0.5 0.5 0.5 5 no no no no 8 8 8 8 W6 W2 W6 W2 0 0 3 0 30 30 31 31 5 6 12 18 9 9 5 11 3 2 3 8 5 4 5 2 VOF VOF VOF VOF 99 99 99 50 50 50 93 86 70 89 2 2 2 2 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Bass 6 73 THIRD 0 TRIANGL 32 0 0 0 off 6 0 off off off off 0 0 0 0 2 4 5 2 0.5 0.5 0.5 10 no no no no 8 8 8 8 W6 W2 W6 W2 0 0 3 0 30 30 31 31 5 6 12 18 9 9 5 11 3 2 3 8 5 4 5 2 VOF VOF VOF VOF 99 99 99 50 50 50 93 86 70 70 2 2 2 2 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Bass 7 74 FOURTH 7 TRIANGL 31 0 0 0 off 4 0 off off off off 0 0 0 0 4 3 3 3 1 0.5 0.5 10.59 no no no yes 8 8 8 92 W1 W1 W8 W7 3 0 -3 3 31 31 31 31 19 15 19 15 15 14 13 9 6 5 6 15 8 14 5 14 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 70 0 1 0 2 0 13 28 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynFunkBas 75 FOURTH 7 TRIANGL 31 0 0 0 off 4 0 off off off off 0 0 0 0 4 3 3 3 1 0.5 0.5 10.59 no no no yes 8 8 8 76 W1 W2 W4 W2 3 0 -3 3 31 31 31 31 19 15 19 13 15 14 13 6 6 5 6 25 8 7 5 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 84 60 0 1 1 2 0 26 42 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynFunkBs2 76 THIRD 0 TRIANGL 35 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 0 3 0.5 0.5 2 16 no no no no 8 8 8 12 W1 W1 W2 W1 0 3 -3 -3 21 21 25 25 2 11 30 30 14 14 14 14 7 9 9 19 6 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 74 83 1 0 0 0 16 10 23 45 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 TeknoBass* 77 FIRST 2 TRIANGL 31 0 0 0 off 5 0 off off off off 0 0 0 0 1 1 1 2 1 1 3 1 no no no no 8 8 8 32 W1 W1 W1 W4 0 0 0 0 26 31 28 23 21 17 17 21 15 15 15 11 10 11 11 12 8 4 2 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 75 99 1 1 1 1 0 39 49 44 -24 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Wood Bass 78 FIRST 2 TRIANGL 31 0 0 0 off 5 0 off off off off 0 0 0 0 1 1 1 2 1 1 3 1 no no no no 8 8 8 32 W1 W1 W1 W4 0 0 0 0 26 31 28 23 21 17 17 21 15 15 15 11 10 11 11 12 5 4 2 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 75 99 1 1 1 1 0 39 49 44 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 WoodBass2 79 Cello 3 THIRD 5 TRIANGL 26 7 19 0 off 5 0 off off off off 0 0 0 0 1 1 2 0 1 1 5 1 no no no no 8 8 8 8 W1 W2 W2 W1 0 -2 3 -3 12 13 18 28 10 10 11 9 15 14 9 14 0 0 0 0 6 6 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 73 80 1 1 1 0 0 9 44 53 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 BowCello 0 THIRD 6 TRIANGL 29 6 15 0 off 5 0 off off off off 0 0 0 0 1 1 2 0 2 2 7 1 no no no no 8 8 8 8 W1 W5 W1 W2 0 -3 3 3 11 21 17 28 10 9 16 4 15 13 9 14 0 3 0 0 5 4 11 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 58 81 72 2 0 0 0 5 21 12 15 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 5 BoxCello* 1 THIRD 6 TRIANGL 29 6 15 0 off 5 0 off off off off 0 0 0 0 1 1 2 0 2 2 7 1 no no no no 8 8 8 8 W1 W5 W1 W2 0 -3 3 3 11 15 17 28 10 18 16 4 15 13 9 14 0 1 0 0 5 4 11 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 58 81 72 2 0 0 0 5 21 12 15 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 5 BoxCello2 2 FIFTH 7 TRIANGL 31 25 21 0 off 3 0 off off off off 0 0 0 0 1 0 1 0 6 2 2 2 no no no no 8 8 8 8 W1 W2 W1 W2 -1 0 2 0 12 16 12 31 5 25 9 0 14 14 13 15 0 0 0 0 6 3 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 92 97 99 72 2 1 2 1 77 43 0 77 -24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Cello * 3 THIRD 7 TRIANGL 31 18 16 0 off 5 0 off off off on 0 0 0 0 2 2 5 1 0.5 0.5 2 0.5 no no no no 8 8 8 8 W1 W1 W4 W4 1 0 1 0 14 21 25 22 11 11 8 7 14 14 12 15 0 0 0 0 7 3 8 6 VOF VOF VOF VOF 99 86 99 50 50 50 99 53 95 76 1 1 2 1 0 0 16 0 0 POLY 2 FINGER 0 POR 99 0 0 40 80 50 0 50 0 0 0 0 0 0 Cello 1 4 THIRD 6 TRIANGL 29 22 44 0 off 3 0 off off off off 0 0 0 0 2 0 1 1 1 1 5 1 no no no no 8 8 8 8 W1 W1 W1 W3 3 -3 -3 3 10 23 30 29 6 12 14 8 15 15 15 10 0 0 0 0 7 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 62 78 0 0 0 0 0 0 37 18 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Cello 2 5 THIRD 6 TRIANGL 29 22 44 0 off 3 0 off off off off 0 0 0 0 2 0 1 1 1 1 5 1 no no no no 8 8 8 8 W1 W1 W1 W3 3 -3 -3 3 13 23 30 29 6 12 14 8 15 15 15 13 0 0 0 0 7 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 62 78 0 0 0 0 0 0 37 18 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Cello 2* 6 FOURTH 3 TRIANGL 27 27 32 0 on 3 0 off off off off 0 0 0 0 4 1 4 2 1 1 3 8 no no no no 8 8 8 8 W1 W1 W1 W1 -1 0 0 1 12 21 17 16 9 16 21 21 12 12 15 13 4 7 6 10 6 7 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 93 80 76 1 0 1 1 0 25 60 25 -24 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 4 Cello....1 7 THIRD 6 TRIANGL 28 46 30 0 off 4 0 off off off off 0 0 0 0 5 1 5 1 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W1 W5 W2 1 -1 1 -1 11 30 20 19 3 4 31 9 2 8 3 14 31 16 11 4 6 5 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 96 73 99 83 1 1 1 2 16 82 82 2 0 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Cello....2 8 DoubleBass 4 FIFTH 7 TRIANGL 30 23 15 0 off 4 0 off off off off 0 0 0 0 5 2 1 1 1 1 1 1 no no no no 8 8 8 8 W1 W2 W1 W1 -3 3 3 -3 13 30 15 27 8 5 9 21 14 14 15 13 0 0 0 0 5 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 86 1 1 0 1 0 51 0 19 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 ArcoBass * 0 THIRD 4 TRIANGL 28 6 4 6 on 4 3 off off on off 0 0 0 0 0 2 2 2 0.5 2 4 0.5 no no no no 16 16 8 112 W2 W6 W4 W1 -2 -1 3 1 16 11 3 9 5 5 8 9 13 15 8 10 3 4 3 3 6 3 5 5 VOF V48 VOF VOF 99 99 99 50 50 50 97 74 90 99 0 0 1 0 0 0 56 34 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BassBeast 1 FIFTH 2 TRIANGL 38 0 0 0 on 3 0 off off off off 0 0 0 0 5 1 6 1 0.5 0.5 0.5 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 21 31 21 13 22 13 22 10 13 10 13 12 6 12 6 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 64 2 3 2 3 99 0 99 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 BassPluck2 2 THIRD 6 TRIANGL 26 8 13 0 off 5 0 off off off off 0 0 0 0 1 1 2 0 1 3 8.97 1 no no no no 8 8 11 8 W1 W1 W1 W2 0 -3 3 0 10 13 18 28 10 5 7 2 15 14 9 14 0 0 0 0 5 6 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 71 78 1 1 1 0 0 99 99 99 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 5 DoubleBass 3 Guitar 5 FIFTH 1 TRIANGL 34 33 0 0 off 3 1 on on on off 0 0 0 0 6 3 5 2 2 4 2 11 no no no no 8 8 8 8 W1 W1 W1 W1 3 -1 -3 1 28 13 22 27 7 15 31 15 12 12 15 12 6 9 7 9 6 2 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 95 60 88 79 1 1 1 3 0 0 10 57 -12 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 12 String1 0 FIFTH 2 TRIANGL 35 70 0 1 off 3 2 on on on on 0 0 0 0 4 2 5 2 1 1 1 12 no no no no 8 8 8 8 W1 W3 W1 W1 3 -1 -3 1 28 26 27 27 7 4 9 9 14 11 12 12 3 1 6 4 7 13 14 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 78 81 2 3 2 3 0 0 10 43 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 12 String2 1 FIFTH 5 TRIANGL 30 23 0 0 off 2 0 off off off off 0 0 0 0 6 5 5 3 3 3 2 2 no no no no 8 8 8 8 W2 W1 W1 W1 -1 2 0 -1 31 25 25 25 21 9 12 6 13 6 6 6 7 7 7 7 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 99 88 0 1 0 1 0 12 46 15 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 5th Lead.1 2 FIFTH 5 TRIANGL 30 23 0 0 off 2 0 off off off off 0 0 0 0 5 5 5 3 3 3 2 2 no no no no 8 8 8 8 W1 W1 W2 W1 -1 2 0 -1 31 31 31 31 9 9 31 19 12 12 15 12 9 9 0 0 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 76 0 0 0 1 0 0 64 32 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 5th Lead.2 3 SECOND 6 TRIANGL 35 0 0 0 off 5 0 off off off off 0 0 0 0 4 5 5 3 2 1 3 2 no no no no 8 8 8 8 W3 W1 W1 W1 0 0 0 0 27 31 31 23 21 12 8 5 14 15 15 14 6 8 17 4 6 5 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 64 69 2 0 0 2 35 67 0 35 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AC Guitar1 4 FIRST 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 3 3 4 2 1 1 3 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 31 21 14 14 15 15 14 6 8 17 4 10 5 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 53 75 1 0 0 2 0 67 0 35 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AC Guitar2 5 FIFTH 4 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 4 3 4 3 2 3 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 31 26 14 15 15 13 15 8 0 6 0 14 11 13 15 VOF VOF VOF VOF 99 99 99 50 50 50 87 81 99 76 1 2 0 3 0 67 0 35 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AC Guitar3 6 FIFTH 6 TRIANGL 34 51 0 1 off 3 1 on on on on 0 0 0 0 5 1 5 1 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 26 29 27 4 21 10 15 15 15 15 15 4 6 9 12 14 11 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 99 83 1 2 2 2 0 43 0 35 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AC Guitar4 7 FIRST 7 TRIANGL 31 0 3 0 off 5 0 off off off off 0 0 0 0 5 1 0 4 1 3 3 9 no no no no 8 8 8 8 W1 W2 W4 W4 0 -3 3 0 31 28 31 31 18 6 10 9 15 7 6 12 8 0 0 0 8 4 2 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 69 72 1 0 1 1 0 55 61 39 -12 POLY 3 FULL 0 POR 99 0 0 50 1 50 0 50 0 0 0 0 0 0 AllThatJaz 8 THIRD 7 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 2 2 2 3 1 3 10 1 no no no no 8 8 8 8 W5 W3 W5 W1 1 -1 0 -1 27 28 31 31 12 10 11 31 10 10 11 10 8 7 7 6 9 7 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 61 69 75 1 1 1 1 0 40 40 40 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 A.Guitar 9 THIRD 7 TRIANGL 35 28 8 0 off 4 0 off off off off 0 0 0 0 2 0 1 1 1 1 0.5 3 no no no no 8 8 8 8 W2 W3 W7 W1 -3 -1 1 3 24 31 31 31 31 31 31 31 15 15 15 15 5 3 4 4 11 3 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 85 86 83 70 0 1 0 2 0 8 8 12 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 E.Guitar 10 THIRD 7 TRIANGL 35 28 8 0 off 4 0 off off off off 0 0 0 0 2 0 1 1 1 1 0.5 4 no no no no 8 8 8 8 W2 W3 W7 W1 -3 -1 1 3 24 31 31 31 31 31 31 31 15 15 15 15 5 3 4 4 11 3 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 85 86 83 70 0 1 0 2 0 8 8 12 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 E.Guitar* 11 SIXTH 6 TRIANGL 37 0 0 0 on 3 0 off off off off 0 0 0 0 5 5 5 6 1 1 1 2 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 0 3 31 31 31 31 22 22 22 15 14 14 14 13 0 0 0 9 5 5 8 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 87 0 0 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 4 Echo Lead 12 THIRD 7 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 2 2 2 3 1 3 10 1 no no no no 8 8 8 8 W1 W2 W8 W1 1 -1 0 -1 26 26 31 31 12 9 11 31 10 10 11 15 6 7 4 5 8 7 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 73 69 79 1 1 1 1 0 40 40 40 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 F.Guitar 13 THIRD 7 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 2 2 2 3 1 4 10 1 no no no no 8 8 8 8 W1 W2 W8 W1 1 -1 0 -1 26 26 31 31 12 9 11 31 13 13 11 15 6 7 4 5 8 7 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 73 69 79 1 1 1 1 0 40 40 40 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 F.Guitar* 14 SECOND 6 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 6 2 1 1 3 3 no no no no 8 8 8 8 W1 W1 W2 W4 0 0 0 0 23 31 23 24 19 18 15 11 14 15 9 10 9 0 14 10 8 5 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 64 82 82 1 1 1 1 0 7 17 40 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Flamenco 15 FIFTH 7 TRIANGL 35 0 0 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 1.5 1.5 1 1 no no no no 16 16 8 8 W1 W8 W1 W1 -3 3 3 -3 27 31 27 31 30 10 31 8 15 11 15 12 0 0 0 0 9 2 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 84 88 99 74 0 0 0 0 17 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Funkfifth* 16 THIRD 7 TRIANGL 20 0 0 0 off 4 0 off off off off 0 0 0 0 4 3 3 3 1 0.5 3 5 no no no no 8 8 8 8 W1 W1 W1 W4 0 0 3 -3 31 31 31 31 15 12 25 25 7 5 5 3 17 5 6 8 9 11 11 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 99 99 0 1 2 3 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 7 Funky Pick 17 FIFTH 5 TRIANGL 35 0 0 1 off 0 1 on on on on 0 0 0 0 6 4 5 3 1 1 0.5 0.5 no no no no 8 8 8 8 W1 W2 W1 W1 3 -3 3 -3 29 28 31 31 10 10 12 5 13 12 13 13 6 4 2 6 7 3 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 88 98 99 2 2 2 1 21 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 FuzzyLead 18 THIRD 6 TRIANGL 25 0 0 0 off 3 0 off off off off 0 0 0 0 2 1 1 3 1 3 10 2 no no no no 8 8 14 8 W1 W4 W3 W1 1 -1 -3 -1 31 26 26 31 11 11 11 31 11 7 7 10 11 4 4 11 6 5 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 50 55 1 1 1 1 30 60 0 99 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Guitar 19 FOURTH 7 TRIANGL 20 0 0 0 off 4 0 off off off off 0 0 0 0 4 3 3 3 1 0.5 0.5 15 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 -3 3 31 31 31 31 19 15 18 14 14 15 14 15 7 4 6 14 8 15 12 15 VOF VOF VOF VOF 99 99 99 50 50 50 98 91 85 72 1 0 2 0 6 37 28 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Guitar #1 20 FIFTH 0 TRIANGL 34 33 0 0 off 0 0 off off off off 7 3 3 7 7 7 4 4 1 12 1 1 no no no no 8 8 8 8 W1 W1 W2 W1 0 0 0 0 28 28 28 28 7 21 4 4 11 12 12 0 7 6 0 12 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 99 75 1 2 1 1 0 0 30 47 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 HardHabits 21 THIRD 6 TRIANGL 31 0 3 0 off 5 0 off off off off 0 0 0 0 3 2 2 2 1 3 3 11 no no no no 8 8 8 8 W1 W1 W3 W2 0 0 3 0 19 19 19 31 18 6 10 9 15 0 0 0 8 0 0 0 8 8 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 97 59 69 56 1 0 1 1 0 55 61 39 -12 POLY 3 FULL 0 POR 99 0 0 50 1 50 0 50 0 0 0 0 0 0 HawaiiGitr 22 FIRST 6 TRIANGL 32 39 6 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.5 0.5 0.5 1 no no no no 8 8 8 8 W1 W2 W5 W1 0 0 0 0 31 31 31 17 31 31 12 6 15 15 13 10 0 0 0 8 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 90 80 78 77 0 0 0 0 0 0 0 47 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 HeavyLead 23 THIRD 7 TRIANGL 31 22 5 0 off 6 0 off off off off 0 0 0 0 0 1 0 1 1 4 3 0.5 no no no no 8 8 8 8 W1 W2 W2 W1 0 -1 2 3 31 31 21 31 15 3 5 8 15 15 15 15 0 0 4 0 8 2 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 92 76 91 88 0 0 0 0 0 27 27 32 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 HeavyMetal 24 FIFTH 6 TRIANGL 41 0 0 0 on 2 0 off off off off 0 0 0 0 5 4 5 4 1 1 1 2 no no no no 8 8 8 8 W2 W2 W1 W1 0 3 3 3 24 18 20 27 31 12 31 31 15 15 15 15 0 6 0 0 13 9 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 86 90 98 71 0 1 0 0 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 HollowLead 25 FIFTH 0 TRIANGL 25 0 1 3 off 1 1 on on on on 0 0 0 0 6 5 5 3 1 3 1 3 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 0 0 22 27 24 25 9 9 9 9 9 11 13 11 7 7 7 7 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 91 99 95 0 1 3 2 10 99 99 85 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Jazz Guit1 26 FIFTH 0 TRIANGL 35 0 5 0 off 2 0 off off off off 0 0 0 0 4 3 5 3 1 2 1 16.96 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 -3 0 22 28 23 31 26 7 26 17 14 14 13 0 9 12 7 31 9 9 7 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 81 96 1 1 0 0 0 0 79 46 -12 POLY 3 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Jazz Guit2 27 FIFTH 0 TRIANGL 35 0 5 0 off 2 0 off off off off 0 0 0 0 6 3 4 4 1 2 1 14 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 -3 0 22 27 24 25 25 17 25 25 14 12 13 13 9 12 7 8 9 9 7 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 81 94 1 1 0 3 0 0 79 46 -12 POLY 3 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Jazz Guit3 28 FIFTH 7 TRIANGL 35 0 1 4 off 3 2 on on on on 0 0 0 0 6 5 4 4 1 3 3 14 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 22 27 24 31 9 9 9 18 9 11 13 0 7 7 7 31 7 7 7 15 VOF VOF VOF VOF 99 99 99 50 50 50 95 92 99 91 0 1 3 0 10 73 45 99 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Jazz Guit4 29 FIFTH 7 TRIANGL 45 0 0 0 on 2 0 off off off off 0 0 0 0 5 4 6 3 1 5 1 1 no no no no 8 8 8 8 W2 W5 W1 W1 0 0 -1 0 27 27 27 27 24 10 24 10 15 12 15 15 6 4 6 4 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 98 83 2 2 2 2 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 JonesLead 30 FIFTH 7 TRIANGL 15 29 35 61 off 2 1 on off off off 0 0 0 0 6 6 5 4 1 1 1 1 no no no no 8 8 8 8 W2 W1 W1 W1 3 -3 -3 -3 14 13 31 21 3 1 21 15 14 12 14 14 2 6 7 7 6 8 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 88 99 82 1 2 1 2 0 25 0 25 -12 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 KickLead 31 FIFTH 7 TRIANGL 28 0 22 0 on 3 0 off off off off 0 0 0 0 1 0 1 2 1 1 1 6 no no no no 8 8 8 8 W1 W1 W1 W6 0 0 0 0 31 31 31 31 17 22 16 20 14 14 14 14 9 10 9 31 8 8 9 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 99 78 3 3 3 1 0 30 0 10 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Mute Gtr 32 FIFTH 5 TRIANGL 35 0 0 0 on 0 0 off off off off 0 0 0 0 5 4 5 4 1 1 1 1 no no no no 8 8 8 8 W1 W5 W5 W1 3 -2 -3 -3 15 15 31 21 21 4 21 13 12 12 15 15 6 17 8 9 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 99 97 1 1 1 1 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 NastyLead1 33 FIFTH 5 TRIANGL 38 0 3 0 on 4 0 off off off off 0 0 0 0 5 5 5 4 5 1 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 3 -2 -3 -3 15 15 31 21 22 14 21 13 14 7 15 15 6 5 8 9 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 98 99 98 0 0 0 0 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 NastyLead2 34 FIFTH 7 TRIANGL 35 0 27 0 off 2 0 off off off off 0 0 0 0 5 3 5 1 1 4 4 10 no no no no 8 8 8 8 W1 W3 W2 W1 0 1 0 0 21 25 29 28 3 30 10 10 15 9 3 9 6 9 4 4 9 9 12 12 VOF VOF VOF VOF 99 99 99 50 50 50 92 81 93 72 1 3 3 1 10 0 0 99 -24 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Nuln Guit3 37 FIFTH 7 TRIANGL 35 0 27 0 off 2 0 off off off off 0 0 0 0 4 2 4 2 1 1 7 9 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 24 20 21 28 4 21 10 15 14 15 15 11 4 3 9 12 6 7 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 67 74 1 2 0 2 0 39 0 35 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Nyln Guit1 35 SECOND 4 TRIANGL 35 0 27 0 off 2 0 off off off off 0 0 0 0 4 5 5 2 1 25.95 1 5 no no no no 8 8 8 8 W1 W2 W1 W1 0 1 0 0 24 28 31 28 4 30 25 15 4 4 4 5 3 3 3 12 7 7 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 66 96 79 1 2 3 2 0 0 0 35 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Nyln Guit2 36 FOURTH 7 TRIANGL 31 0 0 0 off 3 0 off off off off 0 0 0 0 3 2 4 3 1.5 1.51 4.5 4.5 no no no no 16 14 16 16 W1 W1 W1 W1 -1 -2 3 0 21 28 27 31 19 31 5 13 14 15 10 11 8 3 7 8 5 5 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 54 76 77 1 1 2 2 17 44 41 47 -7 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Nylon * 38 FIRST 5 TRIANGL 31 0 3 0 off 5 0 off off off off 0 0 0 0 3 2 2 2 1 3 3 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 3 0 31 28 31 31 18 6 10 9 15 0 0 0 8 0 0 0 8 8 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 97 71 77 81 1 0 1 1 0 55 61 39 -12 POLY 3 FULL 0 POR 99 0 0 50 1 50 0 50 0 0 0 0 0 0 NylonGuit 39 FIFTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 2 5 2 2 0.5 10 8 no no no no 8 8 8 8 W1 W1 W1 W1 2 0 0 0 27 27 27 27 18 12 18 12 13 13 13 13 7 7 7 7 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 98 79 91 80 2 3 2 3 0 99 0 99 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntGuitar 40 SEVENTH 6 TRIANGL 34 0 0 0 on 2 0 off off off off 0 0 0 0 4 3 1 2 1 3 2 1 no no no no 8 8 8 8 W1 W2 W1 W1 -1 -2 2 3 24 28 31 19 12 9 9 15 15 14 15 14 9 10 9 9 7 8 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 99 81 0 0 0 0 0 0 0 57 0 POLY 3 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Ped Stl..1 41 SEVENTH 6 TRIANGL 34 0 0 0 on 2 0 off off off off 0 0 0 0 4 2 5 3 1 2 1 6 no no no no 8 8 8 8 W1 W4 W1 W1 -1 -2 2 3 14 28 31 19 12 9 9 15 15 14 15 14 7 10 9 9 8 8 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 69 1 0 0 0 0 0 0 57 0 POLY 3 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Ped Stl..2 42 SIXTH 7 TRIANGL 34 0 0 0 on 2 0 off off off off 0 0 0 0 6 3 3 2 1 2 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 -1 -2 2 3 14 28 31 19 12 9 9 15 15 14 15 14 7 10 9 9 8 8 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 60 1 0 0 0 0 0 0 57 0 POLY 3 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Ped Stl..3 43 FOURTH 0 TRIANGL 29 22 9 0 off 5 0 off off off off 0 0 0 0 6 2 2 2 1 1 3 12 no no no no 8 8 8 8 W2 W4 W1 W5 0 1 0 0 27 31 27 31 9 16 6 16 13 14 13 0 6 2 5 0 11 10 11 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 93 90 60 1 1 1 1 0 55 61 39 0 POLY 2 FULL 0 POR 99 0 0 25 0 10 0 50 0 0 0 0 0 0 PedalSteel 44 SECOND 2 TRIANGL 35 53 0 16 off 4 1 on off off on 0 0 0 0 5 6 6 4 1 0.5 1 2 no no no no 8 8 8 8 W4 W1 W2 W1 0 0 0 0 31 31 31 26 31 8 31 13 15 12 15 11 0 0 0 0 13 13 13 10 VOF VOF VOF VOF 99 99 99 50 50 50 94 64 83 85 0 1 3 3 0 5 13 6 0 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 PercLead.1 45 SIXTH 4 TRIANGL 35 53 0 16 off 4 1 on off off on 0 0 0 0 4 4 6 6 1 3 1 1 no no no no 8 8 8 8 W2 W3 W1 W1 0 0 0 0 31 31 31 26 31 8 31 13 15 12 15 11 0 0 0 0 13 13 13 10 VOF VOF VOF VOF 99 99 99 50 50 50 87 94 82 99 0 1 3 3 0 5 13 6 0 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 PercLead.2 46 FOURTH 7 TRIANGL 29 0 0 0 off 4 0 off off off off 0 0 0 0 4 3 3 3 1 0.5 0.5 15 no no no no 8 8 8 8 W1 W2 W2 W4 3 -3 -3 3 31 22 28 26 19 15 18 14 14 15 14 15 7 4 6 14 8 15 12 15 VOF VOF VOF V48 99 99 99 50 50 50 98 96 90 66 1 0 1 0 6 32 20 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 PhasedGuit 47 THIRD 0 TRIANGL 30 10 9 0 off 3 0 off off off off 0 0 0 0 3 2 2 3 1 3 3 13 no no no yes 8 8 8 32 W1 W1 W1 W1 1 -3 2 0 29 29 29 26 10 8 13 30 10 10 12 0 12 31 8 31 5 5 2 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 83 90 0 1 2 0 0 33 24 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Piknylon * 48 THIRD 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 6 5 3 1 0.5 9 2 no no no no 8 8 8 8 W1 W2 W2 W1 0 0 0 0 31 31 31 31 18 12 13 7 14 15 4 0 6 6 3 0 9 9 9 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 81 92 1 2 1 2 14 75 70 46 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Power Lead 49 THIRD 7 TRIANGL 30 22 2 50 off 4 1 on off off off 0 0 0 0 0 1 0 1 1 0.5 3 0.5 no no no no 8 8 8 8 W1 W6 W6 W1 0 -1 2 3 23 28 21 26 15 3 5 8 15 10 10 7 0 0 4 0 8 2 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 80 76 70 82 0 0 0 0 0 27 27 32 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 PowerChord 50 FIFTH 1 TRIANGL 0 2 0 0 off 2 0 off off off off 0 0 0 0 5 1 5 1 4 4 4 4 no no no no 8 8 8 8 W2 W3 W1 W1 0 3 0 0 18 22 21 19 9 2 9 31 13 13 13 15 9 6 9 0 8 3 12 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 92 99 72 1 0 1 0 0 0 0 92 -24 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 RecLead 51 FOURTH 0 TRIANGL 35 32 0 0 off 3 0 off off off off 0 0 0 0 5 3 5 2 1 9 1 1 no no no no 8 8 8 8 W3 W1 W1 W1 2 1 0 3 28 28 28 28 5 10 7 9 11 11 14 14 2 2 1 3 10 12 7 13 VOF VOF VOF VOF 99 99 99 50 50 50 92 65 93 84 1 3 3 1 10 0 0 99 -24 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Rock Guit1 52 FOURTH 3 TRIANGL 35 1 10 0 off 6 0 off off off off 0 0 0 0 5 3 5 2 2 2 2 1 no no no no 8 8 8 8 W1 W2 W1 W2 0 -1 0 0 18 18 18 27 31 31 31 3 15 15 15 14 0 0 0 0 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 96 70 96 65 0 0 0 0 1 0 69 19 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Rock Guit2 53 SECOND 5 TRIANGL 35 32 0 0 off 3 0 off off off off 0 0 0 0 5 4 6 3 1 1 1 0.5 no no no no 8 8 8 8 W1 W2 W1 W1 2 1 0 3 28 28 28 28 7 31 9 15 11 15 15 14 7 6 3 1 9 15 15 12 VOF VOF VOF VOF 99 99 99 50 50 50 86 84 78 78 1 1 0 3 10 0 0 7 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Rock Guit3 54 FIFTH 5 TRIANGL 35 32 0 0 off 3 0 off off off off 0 0 0 0 5 4 5 3 1 0.5 1 9 no no no no 8 8 8 8 W1 W1 W1 W1 2 1 0 3 28 28 28 28 7 31 9 15 11 15 15 14 7 6 3 10 9 15 15 12 VOF VOF VOF VOF 99 99 99 50 50 50 96 91 78 65 1 1 0 2 10 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Rock Guit4 55 FIFTH 5 TRIANGL 35 32 0 0 off 3 0 off off off off 0 0 0 0 5 3 5 3 1 2 3 1 no no no no 8 8 8 8 W1 W2 W1 W3 2 1 -1 0 28 28 28 21 7 31 9 15 11 15 12 12 7 6 7 6 9 15 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 85 95 87 1 1 1 2 10 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Rock Guit5 56 FIFTH 4 TRIANGL 35 32 0 0 off 3 0 off off off off 0 0 0 0 5 3 5 2 1 0.5 3 1 no no no no 8 8 8 8 W1 W1 W1 W1 2 1 -1 0 24 16 16 16 4 21 31 31 11 13 15 15 3 4 4 4 9 15 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 99 80 2 1 1 2 10 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Rock Guit6 57 THIRD 7 TRIANGL 31 22 5 0 off 6 0 off off off off 0 0 0 0 0 1 0 1 1 4 14 0.5 no no no no 8 8 8 8 W1 W2 W2 W1 0 -1 2 3 30 31 31 31 15 3 5 8 15 15 15 15 0 0 4 0 9 2 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 92 72 60 78 0 0 0 0 0 27 27 32 0 MONO 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 StadiumSol 58 FOURTH 7 TRIANGL 32 20 10 0 off 4 0 off off off off 0 0 0 0 4 0 0 0 1 1 1 3 no no no no 8 8 8 8 W1 W4 W4 W7 -3 3 3 3 24 24 22 23 5 14 0 15 10 12 15 12 1 0 0 0 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 92 63 83 53 0 0 0 0 0 0 5 0 -12 POLY 2 FULL 0 POR 40 0 0 70 0 0 0 50 0 0 0 0 0 0 Sy.Solo 1 59 SIXTH 7 TRIANGL 38 40 20 0 off 4 0 off off off off 0 0 0 0 2 2 2 0 1 2 1 1 no no no no 8 8 8 8 W3 W6 W5 W3 3 -3 3 3 31 27 27 31 4 10 4 25 14 12 10 11 0 0 0 0 12 12 12 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 73 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 75 0 0 0 50 0 0 0 0 0 0 Sy.Solo 2 60 THIRD 7 TRIANGL 32 0 0 0 off 6 0 off off off off 0 0 0 0 1 0 0 1 1 1 1 1 no no no no 8 8 8 8 W1 W4 W4 W4 0 0 0 0 22 31 31 31 31 10 11 10 15 11 11 10 0 0 0 0 10 7 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 97 45 77 79 1 1 1 1 0 0 0 7 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Solo 3 61 THIRD 7 TRIANGL 32 0 0 0 off 6 0 off off off off 0 0 0 0 1 0 0 1 1 2 1 2 no no no no 8 8 8 8 W1 W4 W4 W4 0 0 0 0 23 31 31 31 31 10 11 10 15 11 11 10 0 0 0 0 10 7 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 97 53 69 77 1 1 1 1 0 0 0 7 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Solo 4 62 THIRD 7 TRIANGL 34 17 14 30 off 5 3 off on off off 0 0 0 0 0 0 0 0 3 0.5 2.99 0.5 no no no no 8 8 10 8 W1 W5 W1 W8 3 0 -3 0 16 31 31 19 31 20 17 18 15 8 12 13 0 0 0 0 11 5 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 92 92 83 81 1 0 0 1 0 36 44 34 0 POLY 7 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Sync Lead 63 THIRD 6 TRIANGL 30 9 2 0 off 5 0 off off off off 0 0 0 0 5 2 2 4 5 1 19.03 16 no no no no 8 80 10 12 W1 W6 W5 W6 0 -3 -3 3 31 31 31 31 9 5 15 26 13 0 10 1 7 0 6 0 9 8 9 3 VOF V48 V48 V48 99 99 99 50 50 50 99 80 62 82 1 2 2 2 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 25 0 16 0 50 0 0 0 0 0 0 TwangGuitr 64 FOURTH 7 TRIANGL 20 0 0 0 off 4 0 off off off off 0 0 0 0 4 3 3 3 1 2.48 0.5 12 no no no no 8 15 8 8 W1 W4 W1 W1 3 3 -3 3 31 31 31 31 19 31 15 13 14 11 15 8 7 5 6 8 9 11 11 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 66 1 1 2 0 0 54 19 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 TwelveStrg 65 FOURTH 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 5 5 5 5 1 0.5 0.5 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 -3 -1 22 31 31 31 4 2 18 10 12 14 14 2 8 3 6 6 8 11 8 11 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 95 95 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 slowtacky 66 FOURTH 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 5 5 5 5 1 0.5 0.5 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 -3 -1 22 31 31 31 4 2 18 10 12 14 14 2 8 3 6 6 6 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 95 95 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 slowtacky+ 67 Harp 6 FIFTH 5 TRIANGL 34 33 0 0 on 1 0 off off off off 0 0 0 0 5 4 4 4 1 2 1 3 no no no no 8 8 8 8 W1 W2 W1 W3 2 -1 -2 1 17 28 28 27 31 15 10 15 12 0 10 11 4 2 9 9 3 1 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 99 95 3 0 0 0 0 45 29 39 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Dbl Harp.1 0 FIRST 7 TRIANGL 27 41 1 0 off 3 0 off off off off 0 0 0 0 5 4 4 5 1 4 2 6 no no no no 8 8 8 8 W1 W4 W1 W2 2 -1 -2 1 24 31 28 28 18 18 10 27 15 15 0 0 9 12 7 7 5 4 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 93 83 76 93 1 1 0 1 0 90 0 99 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Dbl Harp.2 1 FIFTH 6 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 3 3 3 2 1 3 1 2 no no no no 8 8 8 8 W1 W5 W1 W4 3 -3 -3 3 27 31 30 31 8 18 8 9 0 0 0 0 0 0 0 0 4 5 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 99 80 2 2 2 1 16 0 11 7 0 POLY 2 FULL 0 POR 40 0 0 98 0 0 0 50 0 0 0 0 0 0 Harp * 2 THIRD 7 TRIANGL 27 0 0 0 off 5 0 on off off off 0 0 0 0 2 2 2 3 1.5 1.49 6 6.28 no no no yes 16 9 8 8 W1 W3 W1 W1 -3 3 -3 1 31 31 24 16 17 14 19 19 15 9 12 10 7 8 13 15 4 7 9 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 60 59 2 1 1 2 0 0 0 0 5 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Harp 3 FIFTH 0 TRIANGL 27 21 10 1 off 3 0 off off off off 0 0 0 0 3 4 3 4 1 1 1 1 no no no no 8 8 8 8 W1 W4 W1 W4 3 3 -3 -3 31 23 31 23 12 14 12 14 14 14 14 14 7 10 7 10 5 3 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 99 86 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Harp 1 4 THIRD 7 TRIANGL 20 0 6 8 off 4 2 on on on on 0 0 0 0 3 1 2 2 1 2 7 1 no no no no 8 8 8 8 W1 W3 W3 W1 -3 3 3 -3 25 28 31 23 15 19 19 11 14 0 0 6 11 0 0 11 5 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 67 59 1 0 0 0 0 10 0 10 0 POLY 2 FULL 0 POR 40 0 0 85 0 0 0 50 0 0 0 0 0 0 Harp 2 5 FOURTH 7 TRIANGL 27 0 2 0 off 3 0 off off off off 0 0 0 0 4 2 5 2 1 2 5 6 no no no no 8 8 8 8 W1 W4 W5 W1 3 0 0 1 25 31 31 7 7 9 19 5 0 0 0 14 0 5 7 10 4 3 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 92 83 75 86 1 1 0 1 29 82 54 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Harp.....1 6 FIRST 3 TRIANGL 34 33 0 0 on 1 0 off off off off 0 0 0 0 4 4 4 3 1 1 3 3 no no no no 8 8 8 8 W1 W1 W3 W5 2 -1 -2 1 28 28 28 27 9 13 10 15 10 10 10 11 13 13 10 9 4 3 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 98 77 71 83 1 1 1 3 0 45 50 45 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Harp.....2 7 SIXTH 4 TRIANGL 27 35 11 0 off 4 0 off off off off 0 0 0 0 4 4 4 1 1 1 1 2 no no no no 8 8 8 8 W1 W1 W2 W2 -1 -3 2 0 19 26 28 28 6 24 31 16 15 15 15 0 0 6 31 6 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 76 90 0 0 1 1 0 90 0 99 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Harp....FM 8 Koto 7 THIRD 7 TRIANGL 32 1 20 0 off 4 0 off off off off 0 0 0 0 3 1 0 1 2 8 23.55 2 no no yes no 8 8 48 8 W1 W1 W6 W1 3 -3 0 -3 22 25 31 22 13 14 18 2 2 0 7 6 6 1 16 0 6 4 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 97 67 84 79 1 0 0 0 30 32 0 9 -12 POLY 2 FULL 2 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 5 Fuzzy Koto 0 THIRD 0 TRIANGL 26 8 29 0 off 3 0 off off off off 0 0 0 0 3 1 1 2 1 1 4 4 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 -3 0 26 28 28 29 20 4 7 21 13 11 13 0 8 3 9 0 4 3 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 83 86 2 3 3 2 0 13 35 15 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 54 0 0 0 0 0 0 Koto * 1 FIRST 0 TRIANGL 30 0 0 0 off 6 0 off off off off 0 0 0 0 2 1 1 3 2 1 3 7 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 25 31 31 31 31 31 31 18 15 15 15 11 10 8 6 12 6 1 4 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 71 75 87 1 1 1 1 0 11 37 28 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Koto 2 FIFTH 3 TRIANGL 31 0 0 0 off 6 0 on off off off 0 0 0 0 6 2 5 2 0.5 1 1 4 no no no no 8 8 8 8 W1 W1 W1 W1 -3 -3 3 3 31 27 28 31 17 31 13 10 15 13 12 12 8 29 5 9 5 5 5 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 99 99 82 2 2 2 2 0 0 25 17 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Koto.....1 3 FIFTH 3 TRIANGL 30 0 17 20 on 2 1 on on on on 0 0 0 0 5 3 5 1 1 3 1 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 27 28 31 17 31 13 10 15 13 12 12 8 29 5 9 5 5 5 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 99 99 71 2 2 2 2 0 0 25 17 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Koto.....2 4 FIFTH 3 TRIANGL 30 0 17 20 on 2 1 on on on on 0 0 0 0 7 3 5 2 1 4 1 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 31 28 31 13 10 13 10 12 12 12 12 5 9 5 9 5 7 5 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 74 99 71 2 2 2 2 25 17 25 17 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Koto.....3 5 Lute 8 FIFTH 6 TRIANGL 52 0 0 10 on 0 1 on on off off 0 0 0 0 5 2 4 4 1 0.5 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 1 0 24 12 25 31 7 7 7 7 14 7 7 8 9 31 31 4 5 4 4 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 99 82 1 1 1 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntLute.1 0 FIFTH 5 TRIANGL 52 0 0 10 on 0 1 on on on on 0 0 0 0 6 3 4 2 1 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 1 0 21 31 12 24 12 12 12 3 12 8 7 15 9 31 31 0 5 5 4 5 VOF VOF VOF VOF 99 99 99 50 50 50 97 70 99 89 1 1 1 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntLute.2 1 Mandolin 9 FIFTH 6 TRIANGL 44 99 4 0 on 2 0 off off off off 0 0 0 0 5 4 6 4 1 3 1 15 no no no no 8 8 8 8 W3 W4 W1 W1 1 0 -1 0 22 31 23 31 12 9 12 25 11 11 0 12 9 6 9 13 7 2 7 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 69 99 98 3 1 1 3 0 0 0 2 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Mandolin.1 0 FIFTH 7 SAWUP 70 16 1 0 off 2 0 off off off off 0 0 0 0 5 5 5 2 1 3 1 15 no no no no 8 8 8 8 W1 W3 W1 W5 1 0 -1 0 22 31 31 31 12 12 13 24 11 11 15 12 9 6 7 13 5 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 94 55 95 63 3 1 1 3 0 0 0 2 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Mandolin.2 1 Sitar 10 FOURTH 0 TRIANGL 29 0 0 0 off 6 0 off off off off 0 0 0 0 1 2 3 0 1 7 1 1 no no no no 8 8 8 8 W1 W2 W1 W1 0 -2 -3 0 31 30 17 31 24 12 21 31 15 15 15 15 6 11 0 0 4 1 2 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 91 0 1 1 1 0 0 0 0 0 0 POLY 1 FULL 0 POR 40 0 0 13 0 0 0 51 0 0 0 0 0 0 Sitar * 0 FOURTH 7 TRIANGL 33 0 5 0 off 4 0 off off off off 0 0 0 0 1 0 0 0 2 2 1 1 no no no no 8 8 8 8 W2 W2 W1 W1 -3 3 3 0 31 31 31 23 5 6 6 14 7 5 7 14 8 3 4 9 1 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 95 75 94 71 3 2 2 3 0 0 0 2 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Sitar 1 FIFTH 0 TRIANGL 28 0 0 0 off 2 0 off off off off 0 0 0 0 5 2 6 3 19.78 1 1 7 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 -1 31 31 31 31 24 12 6 7 14 14 14 13 9 9 6 6 4 1 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 73 88 91 81 1 1 2 1 0 39 17 82 0 POLY 1 FULL 0 POR 99 0 0 0 99 0 0 50 0 0 0 0 0 0 Sitar....1 2 FOURTH 2 TRIANGL 33 0 5 0 off 4 0 off off off off 0 0 0 0 5 2 5 1 0.5 9 1 6 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 0 31 31 31 23 5 6 6 14 7 5 7 14 8 3 4 9 1 3 2 4 VOF VOF VOF VOF 99 99 99 50 50 50 96 59 82 67 3 2 2 2 0 0 0 2 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Sitar....2 3 THIRD 3 TRIANGL 44 99 4 0 on 2 0 off off off off 0 0 0 0 5 2 5 2 0.5 9 6 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 1 1 31 27 15 29 18 28 12 18 15 8 14 5 6 10 10 7 5 4 2 2 VOF VOF VOF VOF 99 99 99 50 50 50 96 91 82 67 1 0 3 1 0 0 0 2 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Sitar....3 4 FOURTH 2 TRIANGL 33 0 5 0 off 4 0 off off off off 0 0 0 0 7 2 5 3 0.5 9 1 6 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 0 31 27 15 29 18 29 12 18 15 9 12 4 5 9 9 7 5 3 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 96 59 82 67 0 0 3 1 0 0 0 2 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Sitar.Vib 5 THIRD 7 TRIANGL 23 0 3 0 off 5 2 off off off off 0 0 0 0 2 0 1 0 5 0.5 1.37 22.49 no no no no 8 8 14 8 W1 W1 W8 W2 0 0 0 0 30 29 30 30 31 15 15 18 15 15 15 9 12 2 31 31 4 1 2 3 VOF VOF VOF VOF 99 99 99 50 50 50 93 91 87 65 0 0 0 0 0 0 59 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Sitar 6 Viola 11 FIFTH 6 TRIANGL 34 35 11 0 on 1 0 off off off off 0 0 0 0 3 4 6 1 2 2 6 3 no no no no 8 8 8 8 W3 W3 W3 W5 0 0 1 2 13 15 11 25 6 7 7 6 13 14 15 15 6 0 6 0 8 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 78 87 76 0 1 1 2 0 50 10 10 0 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Viola 5th 0 FIFTH 6 TRIANGL 34 35 11 0 on 1 0 off off off off 0 0 0 0 0 1 0 1 1 4 3 3 no no no no 8 8 8 8 W3 W2 W1 W1 0 0 1 2 13 16 10 29 6 7 7 6 13 14 15 15 6 0 6 0 8 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 78 96 84 0 1 1 2 0 50 10 10 0 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Viola 5th* 1 FIFTH 7 TRIANGL 32 56 50 0 on 2 1 on on on on 0 0 0 0 5 4 5 1 2 8 2 2 no no no no 8 8 8 8 W3 W5 W1 W1 1 1 0 2 15 27 13 21 6 13 6 6 13 13 15 14 9 7 1 3 9 7 7 8 VOF VOF VOF VOF 99 99 99 50 50 50 96 67 96 76 0 0 0 1 0 10 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Viola....1 2 SIXTH 6 TRIANGL 30 46 38 0 off 3 1 on on on on 0 0 0 0 3 5 6 2 1 2 1 1 no no no no 8 8 8 8 W1 W2 W5 W2 1 1 0 0 13 14 12 28 6 9 7 4 13 3 4 13 6 13 5 6 5 7 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 92 78 0 2 0 0 0 10 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 6 Viola....2 3 FIFTH 6 TRIANGL 28 46 30 0 off 3 1 on on on on 0 0 0 0 1 1 0 2 1 4 1 1 no no no no 8 8 8 8 W1 W2 W2 W1 1 1 0 0 15 14 15 31 6 9 21 4 13 3 13 13 6 13 7 6 5 7 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 99 83 0 2 0 0 0 10 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 6 Viola....2* 4 SIXTH 7 TRIANGL 35 15 11 0 on 4 0 off off off off 0 0 0 0 5 3 6 3 1 0.5 1 1 no no no no 8 8 8 8 W1 W3 W2 W1 0 0 0 0 15 15 14 27 7 7 9 6 7 7 7 15 9 9 16 2 7 7 13 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 92 74 0 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 ViolaSynth 5 Violin 12 THIRD 7 TRIANGL 30 6 6 0 off 5 0 off off off off 0 0 0 0 2 2 3 1 2 4 18.84 2 no no no no 8 8 8 8 W1 W6 W1 W1 0 0 0 1 11 25 28 27 10 11 17 5 15 15 7 14 0 6 16 0 6 6 13 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 75 78 2 0 0 0 2 73 0 12 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 SoloViolin 0 FIRST 0 TRIANGL 32 0 14 0 off 4 0 off off off off 0 0 0 0 6 0 2 5 2 2 8 10.38 no no no yes 8 8 8 64 W1 W1 W1 W1 -1 0 -3 2 14 18 31 31 5 9 14 13 14 15 10 15 5 2 7 15 9 6 3 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 77 49 1 1 1 0 0 16 20 42 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 49 0 0 0 0 0 0 Sradiv. * 1 THIRD 7 TRIANGL 32 10 8 0 off 6 0 off off off on 0 0 0 0 2 2 5 1 2 1 3 1 no no no no 8 8 8 8 W1 W1 W4 W1 0 0 1 0 14 21 25 22 14 8 8 7 10 11 12 14 0 0 0 0 8 3 8 6 VOF VOF VOF VOF 99 98 99 50 50 50 96 58 95 78 1 1 2 1 0 0 16 0 0 POLY 2 FINGER 0 POR 99 0 0 10 80 50 0 50 0 0 0 0 0 0 Violin 1 2 FIFTH 7 TRIANGL 31 19 61 0 off 4 0 off off off off 0 0 0 0 2 2 2 0 3 1 2 1 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 12 15 10 31 9 10 4 2 10 10 15 14 0 0 0 0 8 4 7 4 VOF VOF VOF VOF 99 84 99 50 50 50 88 98 92 69 1 1 1 1 8 0 9 15 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Violin 2 3 FIFTH 7 TRIANGL 31 19 61 0 off 4 0 off off off off 0 0 0 0 2 2 2 0 4 1 2 1 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 12 15 13 31 9 10 4 2 13 13 15 14 0 0 0 0 8 4 7 4 VOF VOF VOF VOF 99 84 99 50 50 50 88 98 92 69 1 1 1 1 8 0 9 15 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Violin 2* 4 FIFTH 5 TRIANGL 30 20 24 0 on 3 1 on on on on 7 7 0 0 7 4 5 2 1 1 1 1 no no no no 8 8 8 8 W3 W1 W4 W1 3 1 2 -3 12 12 11 11 0 0 3 6 14 14 14 15 0 0 6 3 7 7 5 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 97 78 0 0 1 2 0 0 10 10 0 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Violin...1 5 FIFTH 5 TRIANGL 28 0 24 0 on 3 1 on on on on 0 0 0 0 0 3 0 0 2 1 1 1 no no no no 8 8 8 8 W1 W2 W1 W1 0 2 0 3 15 21 14 18 0 0 5 6 14 15 14 15 0 0 6 3 6 6 5 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 76 0 0 1 2 0 10 10 10 0 POLY 4 FULL 0 POR 99 0 0 50 99 0 0 50 0 0 0 0 0 0 Violin...1* 6 FIFTH 4 TRIANGL 28 21 14 0 off 4 0 off off off off 7 7 6 5 7 5 4 0 1 3 1 1 no no no no 8 8 8 8 W1 W1 W1 W3 -2 1 0 2 10 11 10 21 12 13 8 3 15 14 15 14 3 0 3 0 6 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 80 1 2 1 0 19 0 19 0 0 POLY 2 FULL 0 POR 99 0 0 21 39 0 0 50 0 0 0 0 0 0 Violin...2 7 FIFTH 5 TRIANGL 37 44 14 0 off 2 0 off off off off 0 0 0 0 1 1 1 0 0.5 8 2 2 yes no no no 8 8 8 8 W1 W3 W1 W1 0 1 -3 3 10 15 12 15 1 2 4 5 15 14 15 14 3 0 3 0 6 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 99 80 1 2 1 0 19 0 19 0 -12 POLY 2 FULL 0 POR 99 0 0 21 39 0 0 50 0 0 0 0 0 0 Violin...2* 8 FIFTH 5 TRIANGL 31 28 19 18 on 4 0 off off off off 0 0 0 0 5 7 3 1 1 1 2 1 no no no no 8 8 8 8 W4 W3 W2 W3 0 1 -1 2 12 13 11 14 3 8 8 6 15 8 6 10 3 5 8 7 4 2 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 68 96 78 1 3 1 2 10 20 10 20 0 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Violin...3 9 FIFTH 4 TRIANGL 35 0 7 0 on 3 0 off off off off 0 0 0 0 2 1 0 2 2 8 2 2 no no no no 8 8 8 8 W3 W6 W1 W1 0 1 -1 2 16 31 13 31 5 15 6 4 14 13 13 14 3 8 3 2 8 2 2 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 96 92 1 0 1 0 10 20 10 20 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 5 Violin...3* 10 FIFTH 5 TRIANGL 31 25 46 0 on 3 0 off off off off 0 0 0 0 5 6 7 2 1 2 1 1 no no no no 96 32 8 8 W6 W2 W1 W3 -1 2 0 -3 10 10 8 18 6 4 4 4 14 13 6 7 6 16 9 5 7 5 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 55 99 88 1 1 2 1 10 20 0 42 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Violin...4 11 FIFTH 5 TRIANGL 35 0 11 0 on 1 0 off off off off 0 0 0 0 0 1 0 1 1 8 2 2 no no no no 96 32 8 8 W2 W2 W1 W1 -1 2 0 -3 13 16 12 17 6 4 7 0 14 13 15 14 6 16 6 0 7 5 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 55 99 83 1 1 2 1 10 20 0 42 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 3 Violin...4* 12 FIFTH 5 TRIANGL 34 39 52 50 on 1 0 off off off off 0 0 0 0 3 4 4 5 2 2 2 2 no no no no 8 8 8 8 W4 W2 W2 W1 0 1 1 -3 12 17 10 16 7 12 4 5 15 10 15 15 4 7 4 4 7 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 80 2 2 2 0 0 20 0 20 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Violin...5 13 FIFTH 6 TRIANGL 36 39 0 0 on 1 0 off off off off 0 0 0 0 0 2 1 2 6 2 6 2 no no no no 8 8 8 8 W2 W2 W1 W1 0 1 1 -3 12 31 12 31 4 13 4 5 15 15 15 15 4 7 4 4 7 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 83 2 2 2 0 0 20 0 20 -12 POLY 6 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Violin...5* 14 FIFTH 5 TRIANGL 26 39 25 25 on 4 0 off off off off 0 0 0 0 4 3 4 4 2 2 2 2 no no no no 8 8 8 8 W1 W3 W4 W4 3 1 -1 -3 10 17 10 18 10 13 11 5 15 15 11 15 3 5 4 4 7 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 99 83 2 2 2 0 0 20 0 20 -12 POLY 6 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Violin...6 15 FIFTH 6 TRIANGL 36 39 0 0 on 1 0 off off off off 0 0 0 0 0 1 0 2 4 6 6 2 no no no no 8 8 8 8 W1 W1 W4 W1 0 1 1 -3 12 31 12 31 4 13 4 5 15 15 15 15 4 7 4 4 7 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 99 83 2 2 2 0 0 20 0 20 -12 POLY 6 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Violin...6* 16 FIFTH 5 TRIANGL 36 39 0 0 on 2 0 off off off off 0 0 0 0 4 3 4 1 2 2 2 2 no no no no 8 8 8 8 W1 W4 W4 W4 1 -1 1 0 10 14 12 31 8 4 7 0 10 8 15 14 9 4 6 0 8 5 7 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 98 80 0 1 1 1 10 20 0 42 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Violin...7 17 FIFTH 4 TRIANGL 35 39 0 0 off 1 0 off off off off 0 0 0 0 1 1 0 1 2 2 2 2 no no no no 8 8 8 8 W1 W1 W4 W3 -1 0 2 3 10 21 12 20 5 7 6 5 7 5 10 7 10 7 8 4 8 5 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 82 0 0 1 1 10 20 10 42 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Violin...8 18 FIFTH 4 TRIANGL 35 39 0 0 off 1 0 off off off off 0 0 0 0 1 1 0 1 2 8 4 2 no no no no 8 8 8 8 W1 W1 W1 W1 -1 0 0 3 15 31 12 31 5 13 6 5 12 14 14 15 6 16 6 4 8 6 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 91 0 0 1 1 10 20 10 42 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Violin...8* 19 FIFTH 5 TRIANGL 29 23 64 0 on 2 0 off off off off 0 0 0 0 6 3 0 2 2 4 2 2 no no no no 8 8 8 8 W4 W3 W1 W4 1 2 1 3 12 31 12 31 6 12 6 5 14 14 14 15 6 12 6 0 8 10 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 96 87 0 0 0 1 10 20 10 50 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Violin...9 20 FIFTH 5 TRIANGL 36 39 0 0 on 2 0 off off off off 0 0 0 0 1 1 0 2 8 6 2 2 no no no no 8 8 8 8 W2 W1 W1 W4 1 2 1 3 12 31 12 31 6 12 6 5 14 14 14 15 6 12 6 0 8 10 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 91 74 96 94 0 0 0 1 10 20 10 50 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Violin...9* 21 FIFTH 7 TRIANGL 27 8 31 0 on 3 0 off off off off 0 0 0 0 5 1 6 1 2 2 2 2 no no no no 8 8 8 8 W1 W4 W1 W1 0 2 0 1 11 17 11 18 5 2 9 3 6 8 15 12 6 25 6 0 8 5 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 97 87 81 80 1 1 1 1 10 20 0 42 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 5 Violin..10 22 FIFTH 7 TRIANGL 32 8 12 0 on 3 0 off off off off 0 0 0 0 0 1 0 1 2 2 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 0 1 14 16 10 10 5 2 9 3 6 8 15 12 6 25 6 0 8 5 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 97 87 95 80 1 1 1 1 10 20 0 42 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 5 Violin..10* 23 FIFTH 7 TRIANGL 32 44 5 0 off 3 0 off off off off 0 0 0 0 6 6 0 1 2 4 2 2 no no no no 8 8 8 8 W3 W5 W1 W1 1 2 -1 2 11 14 10 24 5 6 6 5 13 1 14 14 0 0 6 0 8 5 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 96 50 97 79 0 0 1 1 10 20 0 50 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Violin..11 24 FIFTH 5 TRIANGL 33 39 56 2 on 1 0 off off off off 0 0 0 0 4 2 4 2 2 2 2 2 no no no no 8 8 8 8 W5 W6 W2 W3 2 0 1 0 15 22 10 17 8 10 6 5 14 14 14 14 2 6 5 4 8 8 9 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 99 74 0 0 1 1 10 10 0 50 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 5 Violin..12 26 FIFTH 5 TRIANGL 36 39 0 0 on 1 0 off off off off 0 0 0 0 0 2 0 2 2 2 6 2 no no no no 8 8 8 8 W3 W1 W1 W1 2 0 1 0 16 24 15 31 8 10 6 5 14 14 14 14 2 6 5 4 8 8 9 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 99 82 0 0 1 1 10 10 0 50 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 5 Violin..12* 27 THIRD 7 TRIANGL 33 39 7 0 off 5 0 off off off off 0 0 0 0 3 3 3 2 2 2 8 2 no no no no 8 8 8 8 W2 W6 W1 W1 -2 2 0 2 11 22 17 22 13 7 17 6 15 15 7 14 0 8 14 2 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 76 60 66 2 1 1 1 2 73 0 12 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Violin..13 28 THIRD 4 TRIANGL 33 39 7 0 off 5 0 off off off off 0 0 0 0 0 1 0 1 2 4 10 2 no no no no 8 8 8 8 W2 W6 W1 W1 0 0 0 1 12 22 26 29 13 7 17 6 15 15 7 14 0 8 14 2 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 76 91 83 2 1 1 1 2 73 0 12 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Violin..13* 29 FIFTH 3 TRIANGL 35 39 5 0 on 3 0 off off off off 0 0 0 0 5 1 4 1 2 2 2 2 no no no no 8 8 8 8 W1 W3 W1 W7 0 1 -1 2 11 19 13 15 5 13 6 6 12 14 14 14 6 16 3 4 8 7 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 83 88 0 0 1 0 10 20 10 1 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 6 Violin..14 30 FIFTH 5 TRIANGL 35 39 5 0 on 3 0 off off off off 0 0 0 0 0 1 0 1 2 2 2 2 no no no no 8 8 8 8 W1 W1 W1 W5 0 1 -1 2 15 31 14 31 5 13 6 6 12 14 14 14 6 16 3 4 8 7 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 99 89 0 0 1 0 10 20 10 1 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 6 Violin..14* 31 FIFTH 5 TRIANGL 33 18 15 0 on 4 0 off off off off 0 0 0 0 4 4 4 1 4 8 2 2 no no no no 8 8 8 8 W4 W3 W1 W3 0 1 -1 3 11 18 13 26 4 17 6 6 13 14 14 14 5 16 3 4 7 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 78 96 78 0 0 1 0 10 20 10 1 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 6 Violin..15 32 FIFTH 5 TRIANGL 35 0 9 0 on 4 0 off off off off 0 0 0 0 1 1 0 1 4 8 2 2 no no no no 8 8 8 8 W4 W1 W1 W1 0 1 -1 3 16 29 14 31 4 17 6 6 13 14 14 14 5 16 3 4 7 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 78 96 78 0 0 1 0 10 20 10 1 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 6 Violin..15* 33 THIRD 5 TRIANGL 33 39 7 0 off 5 0 off off off off 0 0 0 0 5 2 5 1 2 10.5 2 2 no yes no no 8 40 8 8 W1 W1 W1 W1 1 3 0 1 11 22 26 28 10 7 17 6 14 15 7 14 3 8 14 2 5 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 86 46 70 84 1 0 0 0 0 73 0 12 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Violin..16 34 FIFTH 4 TRIANGL 44 10 8 0 on 3 0 off off off off 0 0 0 0 6 1 5 1 2 2 2 2 no no no no 8 8 8 8 W2 W4 W2 W2 0 3 0 3 12 13 10 24 4 5 8 6 15 14 15 14 3 0 3 0 7 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 68 91 72 1 1 1 1 19 0 19 0 -12 POLY 2 FULL 0 POR 99 0 0 21 39 0 0 50 0 0 0 0 0 0 Violin..17 35 FIFTH 4 TRIANGL 44 10 8 0 on 3 0 off off off off 0 0 0 0 0 1 0 1 2 3 4 2 no no no no 8 8 8 8 W2 W4 W1 W2 0 3 0 3 12 13 10 24 4 5 4 6 15 14 15 14 3 0 3 0 7 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 68 91 72 1 1 1 1 19 0 19 0 -12 POLY 2 FULL 0 POR 99 0 0 21 39 0 0 50 0 0 0 0 0 0 Violin..17* 36 FIFTH 6 SQUARE 32 26 27 0 on 2 0 off off off off 0 0 0 0 5 1 4 4 2 2 2 2 no no no no 8 8 8 8 W1 W1 W2 W2 1 0 0 1 14 25 13 29 6 15 7 0 14 13 15 14 6 16 6 0 8 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 96 78 94 79 0 1 1 1 10 10 0 42 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Violin..18 37 FIFTH 6 TRIANGL 35 0 11 0 on 2 0 off off off off 0 0 0 0 1 1 0 1 2 2 4 4 no no no no 8 8 8 8 W1 W1 W2 W2 1 0 0 1 16 31 13 31 6 15 7 0 14 13 15 14 6 16 6 0 8 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 96 78 94 79 0 1 1 1 10 10 0 42 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Violin..18* 38 THIRD 2 SQUARE 33 39 33 0 on 2 0 off off off off 1 0 0 0 4 3 4 2 2 2 6 2 no no no no 8 8 8 8 W1 W3 W3 W4 0 1 0 1 12 24 15 21 5 3 6 6 12 8 15 14 6 7 6 5 8 6 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 81 71 81 0 0 1 2 10 20 10 30 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 6 Violin..19 39 THIRD 2 TRIANGL 36 39 1 0 on 2 0 off off off off 0 0 0 0 1 1 0 2 2 2 6 2 no no no no 8 8 8 8 W1 W4 W2 W1 0 1 0 1 15 31 15 31 5 13 6 6 12 14 15 14 6 16 6 5 8 6 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 81 71 81 0 0 1 2 10 20 10 30 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 6 Violin..19* 40 FIRST 5 TRIANGL 32 25 46 0 on 3 0 off off off off 0 0 0 0 5 3 4 2 2 2 2 2 no no no no 8 8 8 8 W4 W3 W4 W5 -1 0 3 -3 11 22 15 27 3 8 8 0 15 13 11 14 2 5 16 0 8 7 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 90 73 77 68 1 1 1 0 19 25 1 66 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 VIOLINS 41 FIRST 5 TRIANGL 35 0 11 0 on 3 0 off off off off 0 0 0 0 0 0 4 0 2 2 8 2 no no no no 8 8 8 8 W1 W3 W5 W1 -1 0 3 -3 15 30 31 31 3 8 15 0 15 13 14 14 2 5 16 0 8 7 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 81 83 73 71 1 1 1 0 19 25 1 66 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 VIOLINS * 42 FIFTH 5 TRIANGL 30 24 59 0 off 3 0 off off off off 0 0 0 0 4 2 4 1 2 4 2 2 no no no no 8 8 8 8 W4 W5 W2 W2 0 1 -1 -2 11 18 11 19 9 15 7 0 6 6 6 6 0 6 6 0 5 6 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 64 99 76 0 0 0 0 40 9 20 60 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Violins..1 43 FIFTH 5 TRIANGL 33 10 15 0 off 3 0 off off off off 0 0 0 0 0 0 0 0 2 8 2 2 no no no no 8 8 8 8 W4 W5 W2 W2 0 1 -1 -2 18 15 12 16 9 15 7 0 6 6 6 6 0 6 6 0 5 6 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 64 99 76 0 0 0 0 40 9 20 60 -24 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Violins..1* 44 FIFTH 5 TRIANGL 32 44 20 0 off 4 0 off off off off 0 0 0 0 5 2 3 2 2 4 2 2 no no no no 8 8 8 8 W4 W5 W2 W2 1 1 1 2 11 19 11 16 7 12 6 6 13 13 14 13 0 12 6 0 7 5 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 97 69 93 78 0 0 0 0 30 50 20 33 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Violins..2 45 FIFTH 5 TRIANGL 32 44 20 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 2 8 2 2 no no no no 8 8 8 8 W2 W3 W2 W3 1 1 1 2 14 19 15 16 7 12 6 6 13 13 14 13 0 12 6 0 7 5 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 97 69 93 78 0 0 0 0 30 50 20 33 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Violins..2* 46 FIFTH 5 TRIANGL 30 39 52 0 on 3 0 off off off off 0 0 0 0 4 2 5 1 2 4 2 2 no no no no 8 8 8 8 W4 W5 W3 W1 1 1 0 2 11 18 11 18 7 12 6 6 13 13 14 13 0 12 6 0 7 5 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 97 67 89 70 0 0 0 0 30 50 40 33 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Violins..3 47 FIFTH 5 TRIANGL 35 39 12 0 on 3 0 off off off off 0 0 0 0 0 0 0 0 2 6 2 2 no no no no 8 8 8 8 W5 W5 W3 W1 1 1 0 2 16 31 12 24 7 12 6 6 13 13 14 13 0 12 6 0 7 5 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 97 67 89 70 0 0 0 0 30 50 40 33 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Violins..3* 48 FIFTH 5 TRIANGL 31 28 37 0 on 3 0 off off off off 0 0 0 0 4 2 4 1 2 8 2 2 no no no no 8 8 8 8 W3 W4 W1 W5 -3 2 -2 2 11 15 10 21 5 12 6 7 14 12 14 14 1 12 0 5 7 4 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 82 87 91 80 0 0 0 0 17 51 20 65 -12 POLY 3 FULL 1 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 5 Violins..4 49 FIFTH 5 TRIANGL 37 20 15 0 on 3 0 off off off off 0 0 0 0 0 2 0 1 2 8 2 2 no no no no 8 8 8 8 W3 W4 W1 W4 -3 2 -2 2 13 15 10 15 5 12 6 4 14 12 14 14 1 12 0 0 7 4 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 82 87 91 87 0 0 0 0 17 51 20 65 -12 POLY 3 FULL 1 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 5 Violins..4* 50 Other 13 THIRD 4 TRIANGL 33 19 0 0 off 0 0 off off off off 0 0 0 0 4 2 5 1 1 3 9 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 5 12 12 6 14 14 14 14 6 6 2 0 9 9 8 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 65 63 2 3 1 0 0 0 0 0 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 8 Stringer 0 SIXTH 1 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 3 4 3 4 6 2 12 no no no no 8 8 8 8 W1 W1 W1 W1 1 1 -1 -1 13 28 28 28 28 15 10 15 12 12 12 12 6 9 6 9 6 6 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 93 76 89 99 2 1 1 3 0 0 0 46 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst.1 1 SIXTH 6 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 3 5 3 1 1 4 6 no no no no 8 8 8 8 W1 W1 W1 W1 1 -2 3 0 28 28 28 28 13 9 9 24 13 13 13 13 15 9 9 21 5 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 98 85 83 87 1 1 1 3 0 0 0 46 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst.2 2 SIXTH 4 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 4 5 4 1 1 1 8 no no no no 8 8 8 8 W1 W1 W1 W1 1 0 3 0 28 28 28 28 31 28 9 9 15 15 12 12 9 9 3 3 5 4 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 82 83 58 2 1 2 3 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst.3 3 THIRD 1 TRIANGL 31 0 0 0 on 1 0 off off off off 0 0 0 0 5 3 6 3 1 1 7 4.71 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 27 31 10 10 19 27 13 14 7 13 6 10 6 22 6 7 12 8 VOF VOF VOF VOF 99 99 99 50 50 50 93 62 71 99 1 1 1 2 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst.4 4 FIFTH 3 TRIANGL 21 33 17 39 off 2 1 off off on off 0 0 0 0 6 3 6 4 1 2 6 10 no no no no 8 8 8 8 W1 W1 W1 W1 3 -1 -3 -3 31 16 31 16 21 12 21 12 13 12 13 12 6 3 6 9 5 9 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 99 61 1 1 1 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst.5 5 FIFTH 3 TRIANGL 35 60 64 96 on 1 1 off on off off 0 0 0 0 7 4 5 4 2 4 2 4 no no no no 8 8 8 8 W1 W1 W1 W1 3 -1 -3 -3 28 15 28 15 5 15 5 16 13 10 13 10 3 13 3 13 7 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 93 60 71 59 1 1 1 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst.6 6 SIXTH 4 TRIANGL 35 90 1 2 off 4 1 on on on on 0 0 0 0 5 3 5 3 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 -1 -3 -3 16 19 21 31 5 12 18 9 14 15 15 13 6 18 31 4 9 9 9 2 VOF VOF VOF VOF 99 99 99 50 50 50 93 60 99 76 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst.7 7 THIRD 7 TRIANGL 31 0 0 0 on 1 0 off off off off 0 0 0 0 6 4 5 3 2 1 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 28 27 31 25 30 21 31 8 8 8 8 9 0 9 9 5 7 4 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 93 99 1 1 1 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst.8 8 FIFTH 5 TRIANGL 25 46 66 15 on 3 1 on on on on 0 0 0 0 7 4 5 3 2 1 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -2 0 2 28 28 31 31 19 9 9 22 11 11 14 11 9 6 4 9 6 3 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 90 99 1 2 2 2 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst.9 9 THIRD 3 TRIANGL 25 46 66 15 on 3 1 on on on on 0 0 0 0 6 4 4 1 1 4 0.5 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 15 13 24 18 15 12 22 14 11 13 11 7 13 7 24 7 6 4 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 60 72 71 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst10 10 FIFTH 5 TRIANGL 40 0 0 63 off 2 1 off on on off 0 0 0 0 5 2 4 2 1 2 1 9 no no no no 8 8 8 8 W1 W1 W1 W1 3 0 -3 0 28 28 31 31 10 12 6 24 10 10 15 6 9 31 15 15 9 9 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 92 99 1 2 1 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntInst11 11 FOURTH 3 TRIANGL 24 2 32 0 on 4 0 off off off off 0 0 0 0 3 4 2 4 2 5 1 3 no no no no 8 8 8 8 W4 W4 W8 W3 0 0 0 0 23 23 24 26 15 18 6 14 10 12 14 6 9 18 0 15 6 7 4 6 VOF VOF VOF VOF 99 97 99 50 50 50 99 89 64 84 2 2 2 2 0 10 0 10 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Syamisen 12 FIRST 6 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 1 1 1 1 1 2 5 no no no no 8 8 8 8 W1 W1 W1 W4 0 0 0 0 31 31 31 31 24 31 12 11 15 15 10 13 6 0 3 6 8 3 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 54 87 0 0 0 1 0 34 20 30 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Zither 13 KEYBOARDS 3 Clavichord 0 THIRD 7 TRIANGL 35 0 8 0 off 2 0 off off off off 0 0 0 0 4 5 4 3 0.5 0.5 0.5 12 no no no no 8 8 8 8 W2 W5 W2 W1 2 -2 1 2 30 30 29 30 28 27 1 28 12 13 0 12 0 0 0 0 9 9 12 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 83 75 2 0 2 2 0 12 46 15 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Clav Lead 0 THIRD 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 6 3 1 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W2 W2 W1 -3 0 3 -1 25 26 31 26 15 15 10 15 12 13 0 12 5 4 4 4 9 9 12 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 88 91 2 2 1 1 0 12 46 15 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Clav Perc 1 THIRD 7 TRIANGL 35 26 15 0 off 3 0 off off off off 0 0 0 0 6 4 4 3 4 0.5 0.5 4 no no no no 8 8 8 8 W2 W2 W1 W1 0 0 0 3 30 30 30 30 21 4 3 28 12 13 0 12 5 4 4 4 9 9 12 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 85 94 2 0 2 2 0 12 46 15 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Clav Synth 2 FIRST 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 3 4 4 1 1 0.5 0.5 6 no no no no 8 8 8 8 W1 W2 W1 W2 0 0 0 0 28 31 31 31 20 12 16 11 14 15 15 9 8 8 5 8 12 5 6 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 82 66 1 0 2 1 0 0 94 16 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Clav.....1 3 FOURTH 3 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 3 3 4 1 0.5 0.5 7 4 no no no no 8 8 8 8 W1 W1 W3 W1 0 -2 1 2 30 30 29 30 28 27 1 28 12 13 0 12 5 1 1 5 9 9 12 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 81 67 2 0 2 2 0 12 46 15 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Clav.....2 4 THIRD 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 3 3 3 1 2 1 10 1 no no no no 8 8 8 8 W2 W1 W1 W2 0 1 1 2 24 23 29 30 27 26 1 28 12 13 0 12 5 1 1 5 7 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 68 84 2 0 1 2 2 9 50 32 0 POLY 5 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Clav.....3 5 THIRD 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 4 4 3 1 2 0.5 10 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 -2 -1 3 2 25 23 29 30 27 9 1 21 14 0 0 0 6 15 11 10 9 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 98 83 62 97 1 0 2 2 2 9 50 32 0 POLY 2 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Clav.....4 6 FIFTH 7 TRIANGL 30 0 0 0 off 2 0 off off off off 0 0 0 0 3 3 3 1 2 0.5 0.5 7 no no no no 8 8 8 8 W2 W1 W3 W1 -2 -1 0 2 25 25 28 28 25 25 16 16 14 14 12 12 6 6 7 7 9 9 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 87 99 81 1 1 3 3 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Clav.....5 7 FIFTH 7 TRIANGL 30 0 0 0 off 2 0 off off off off 0 0 0 0 6 3 3 1 2 0.5 4 20.76 no no no no 8 8 8 8 W2 W1 W1 W1 -2 -1 0 2 25 25 28 28 25 25 5 16 14 14 12 12 6 6 7 7 9 9 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 98 90 93 78 1 1 3 3 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Clav.....6 8 THIRD 0 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 6 4 4 0 0.5 0.5 2 12 no no no no 8 8 8 8 W2 W5 W1 W1 -2 -1 3 2 25 23 29 28 27 9 1 1 14 0 0 7 6 15 11 2 9 1 1 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 57 53 1 0 2 1 2 9 50 53 0 POLY 2 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Clav.....7 9 THIRD 3 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 5 5 1 2 0.5 0.5 7 no no no no 8 8 8 8 W2 W1 W1 W1 0 -2 1 2 30 30 29 30 28 27 1 28 12 13 0 12 5 1 1 5 9 9 12 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 89 76 2 0 2 2 0 12 46 15 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Clav.....8 10 FIFTH 3 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 5 4 3 1 1 1 4 no no no no 8 8 8 8 W1 W5 W5 W1 0 -2 1 2 30 30 29 30 28 27 1 28 12 13 0 12 5 1 1 5 9 9 12 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 98 82 2 0 2 2 0 12 46 15 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Clav.....9 11 THIRD 7 TRIANGL 28 0 0 0 off 5 0 off off off off 0 0 0 0 3 2 3 4 2 0.5 5 6 no no no no 8 8 8 8 W1 W3 W5 W3 -1 -2 3 -3 31 31 31 31 0 12 6 1 15 10 14 0 10 1 1 0 15 1 1 15 VOF VOF VOF VOF 99 99 99 50 50 50 88 84 79 77 1 1 1 2 0 0 35 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Clavi 12 FIRST 6 TRIANGL 27 0 0 0 off 5 0 off off off off 0 0 0 0 2 2 3 4 1 0.5 5 15 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 0 0 31 31 31 31 20 9 18 11 14 15 15 9 8 8 5 8 12 5 6 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 70 66 1 0 0 2 0 0 16 16 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 EZ Clav 13 FOURTH 6 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 1 4 2 1 3 0.5 4 12 no no no no 8 8 8 8 W1 W1 W1 W1 0 -2 1 2 30 30 29 30 28 27 1 28 12 13 0 12 5 1 1 5 9 9 12 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 77 86 2 0 2 2 0 12 46 15 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Fuzz Clavi 14 THIRD 0 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 2 4 2 0 2 1 10 1 no no no no 8 8 8 8 W1 W1 W4 W4 0 -3 3 3 27 29 29 29 10 7 9 6 14 14 14 14 7 9 6 6 10 7 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 80 82 1 0 2 1 0 0 16 0 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Klavinet * 15 THIRD 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 2 4 2 2 1 1 14 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 1 2 24 23 29 30 27 26 1 28 12 13 0 12 5 1 1 5 7 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 57 78 2 0 1 2 2 9 50 32 0 POLY 5 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Ornt Clav 16 FOURTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 7 7 13 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 7 18 13 16 8 0 0 0 0 0 0 0 6 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 69 76 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Rimbaclav 17 THIRD 7 TRIANGL 85 0 57 0 off 4 0 off off off off 0 0 0 0 3 0 0 0 1 9 13 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 27 16 28 19 14 2 11 24 12 12 12 12 16 1 4 20 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 65 97 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SplashClav 18 FIFTH 5 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 3 0 0 2 8 5 1 4 no no no no 8 8 8 8 W1 W1 W4 W5 3 3 -3 0 31 31 23 26 9 12 6 9 0 0 13 15 15 6 5 7 6 2 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 90 87 99 81 1 1 1 1 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 4 Synclav. * 19 THIRD 0 TRIANGL 28 0 0 0 off 5 0 off off off off 0 0 0 0 2 2 3 4 2 0.5 5 6 no no no no 8 8 8 8 W1 W3 W2 W8 0 1 -1 0 31 31 31 31 7 8 6 11 13 14 15 9 8 7 4 7 12 5 6 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 88 70 73 1 1 1 2 0 0 35 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Thin Clav 20 Harpsichord 1 FIFTH 3 TRIANGL 31 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 4 0.5 1 3 no no no no 8 8 8 8 W4 W1 W1 W5 1 2 3 -2 23 15 23 26 8 16 8 19 12 13 12 13 6 0 6 0 5 1 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 88 93 76 91 2 3 2 2 0 0 0 10 0 POLY 2 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Harpschrd1 0 FIFTH 3 TRIANGL 31 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 4 0.5 0.5 1.49 no no no no 8 8 8 9 W4 W1 W1 W5 1 2 3 -2 23 15 23 26 8 16 8 19 12 13 12 13 6 0 6 0 5 1 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 88 93 76 91 2 3 2 2 0 0 0 10 0 POLY 2 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Harpschrd2 1 FIFTH 3 TRIANGL 31 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 3 0.5 1 3 no no no no 8 8 8 8 W4 W1 W1 W5 1 2 3 -2 23 15 23 26 8 16 8 19 12 10 12 10 6 0 6 0 7 1 7 1 VOF VOF VOF VOF 99 99 99 50 50 50 88 93 76 91 2 3 2 2 0 0 0 10 0 POLY 2 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Harpschrd3 2 FIFTH 3 TRIANGL 31 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 4 0.5 1 3 no no no no 8 8 8 8 W4 W1 W1 W5 1 2 3 -2 23 15 23 26 8 16 8 19 12 13 12 13 6 0 6 0 7 1 7 1 VOF VOF VOF VOF 99 99 99 50 50 50 88 93 76 91 2 3 2 2 0 0 0 10 0 POLY 2 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Harpschrd4 3 THIRD 6 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 3 6 0 4 1 4 1 no no no no 8 8 8 8 W1 W4 W1 W3 0 0 0 0 31 31 31 31 26 25 31 29 13 13 13 12 1 3 5 1 7 4 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 63 87 3 0 2 3 0 0 0 41 0 POLY 0 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Hrpsicord1 4 FIFTH 1 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 4 0 5 0 4 0.5 0.5 1 no no no no 8 8 8 8 W1 W1 W1 W1 -1 1 -2 1 28 28 23 29 24 24 8 17 13 13 12 13 5 0 5 0 6 1 6 1 VOF VOF VOF VOF 99 99 99 50 50 50 96 91 96 91 2 1 2 3 0 0 0 0 12 POLY 0 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Hrpsicord2 5 THIRD 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 6 2 5 2 4 1 6 0.5 no no no no 8 8 8 8 W2 W2 W1 W1 0 0 0 0 31 31 31 31 28 23 28 29 13 13 13 13 1 3 5 1 5 1 9 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 66 90 3 0 1 3 0 0 0 15 0 POLY 0 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Hrpsicord3 6 THIRD 6 TRIANGL 28 0 0 0 off 6 0 off off off off 0 0 0 0 0 1 1 0 4 1 8 1 no no no no 8 8 8 8 W4 W1 W2 W1 0 0 0 0 31 31 31 31 30 19 31 29 13 15 15 12 6 1 1 0 7 1 9 1 VOF V12 V12 VOF 99 99 99 50 50 50 93 76 77 84 2 0 2 3 0 0 33 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 LiteHarpsi 7 FIFTH 3 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 0 0 0 0 4 0.5 1 3 no no no no 8 8 8 8 W1 W1 W1 W1 1 2 3 -2 23 15 23 26 8 16 8 19 12 13 12 13 6 0 6 0 5 1 5 1 VOF VOF VOF VOF 99 99 99 50 50 50 86 93 94 91 2 3 2 2 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 RichHarpsi 8 Organ 2 EIGHTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 2 4 8 no no no no 8 8 8 8 W1 W1 W1 W1 -3 0 1 3 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 90 90 90 90 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 16 8 4 2 F 0 SEVENTH 7 TRIANGL 35 0 39 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 1 2 4 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 0 0 0 0 15 15 15 15 0 0 0 0 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 94 63 0 0 0 0 0 0 0 27 -12 POLY 7 FULL 0 POR 99 0 0 99 0 99 0 50 0 0 0 0 0 0 <6 Tease> 1 FIFTH 0 TRIANGL 28 21 7 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 1.5 1.5 1.49 1.49 no no no no 16 16 9 9 W1 W1 W1 W1 -2 -2 3 3 30 31 30 31 1 28 1 27 13 13 13 13 0 0 0 0 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 95 87 95 87 0 0 0 0 10 9 10 9 -19 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AnalogOrgn 2 EIGHTH 0 TRIANGL 40 0 10 44 off 1 0 off off off off 0 0 0 0 5 5 4 3 0.5 2 4 14 no no no no 8 8 8 8 W2 W3 W1 W1 1 1 1 1 31 31 31 31 6 19 24 15 15 15 15 15 6 6 6 6 13 13 13 13 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 98 85 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 B3 Organ.1 3 FIFTH 0 TRIANGL 40 0 10 44 off 2 0 off off off off 0 0 0 0 3 4 5 2 2 4 1 2 no no no no 8 8 8 8 W1 W2 W3 W1 1 1 1 1 31 31 31 31 31 31 31 22 15 15 15 15 7 31 2 5 13 13 13 13 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 62 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 B3 Organ.2 4 SEVENTH 0 TRIANGL 33 0 0 0 off 2 0 off off off off 0 0 0 0 4 4 3 2 1 2 2 2 no no no no 8 8 8 8 W3 W4 W1 W1 1 1 1 1 31 31 31 31 31 31 31 16 15 15 15 15 3 4 3 4 13 13 13 13 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 82 0 0 2 0 0 0 10 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 B3 Organ.3 5 FIFTH 7 TRIANGL 29 0 0 0 off 4 0 off off off off 0 0 0 0 0 2 0 1 0.5 0.5 2 10 no no no no 8 8 8 8 W1 W8 W4 W1 3 -3 -3 0 12 24 15 22 0 0 0 0 15 15 15 15 0 0 0 0 5 5 8 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 99 71 2 2 2 0 0 91 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Big Church 6 SEVENTH 7 TRIANGL 12 15 0 0 off 3 2 on on on on 0 0 0 0 0 0 0 2 1 4 2 10 no no no no 8 8 8 8 W2 W2 W1 W1 -3 3 -3 3 11 16 16 22 31 31 31 31 15 15 15 15 0 0 0 0 5 8 7 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 96 99 71 2 1 1 1 20 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 0 27 0 0 50 0 0 0 0 0 0 ChurchOrg* 7 EIGHTH 0 TRIANGL 35 1 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 7 5 3 1 no no no no 8 8 8 8 W1 W1 W1 W2 0 0 0 0 14 16 23 18 31 31 31 31 15 15 15 15 0 0 0 0 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 32 67 90 89 0 0 0 0 25 25 25 25 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 ClosedFlut 8 EIGHTH 0 SQUARE 35 1 6 0 on 4 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 0.5 no no no no 8 8 8 8 W1 W5 W7 W1 0 0 0 0 14 16 23 18 31 31 31 31 15 15 15 15 0 0 0 0 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 90 90 88 0 0 0 0 25 25 25 25 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Cornopean 9 FOURTH 7 TRIANGL 35 0 1 0 off 5 0 off off off off 0 0 3 4 2 0 3 7 1.49 2.25 1.5 1.51 no no no no 9 12 16 14 W2 W1 W8 W3 -3 -3 3 -1 18 16 21 31 27 27 10 10 14 6 15 15 0 0 0 0 11 8 8 8 VOF V48 V48 V24 99 99 99 50 50 50 99 81 82 66 0 2 3 3 0 15 27 0 -7 POLY 2 FINGER 0 POR 99 0 0 50 0 5 0 50 60 0 0 0 0 0 DistortOrg 10 EIGHTH 0 TRIANGL 35 0 0 0 off 1 2 on on on on 0 0 0 0 0 0 0 0 0.5 3 4 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 23 23 23 23 31 31 31 31 15 15 15 15 0 0 0 0 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 93 99 81 90 0 0 0 0 20 30 40 50 0 POLY 2 FULL 0 POR 40 0 0 99 48 0 0 50 0 0 0 0 0 0 Drawbar1 * 11 EIGHTH 0 TRIANGL 35 0 0 0 off 1 2 on on on on 0 0 0 0 0 0 0 0 1 2 5 6 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 23 23 23 23 31 31 31 31 15 15 15 15 0 0 0 0 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 81 75 0 0 0 0 10 25 44 47 0 POLY 2 FULL 0 POR 40 0 0 99 48 0 0 50 0 0 0 0 0 0 Drawbar2 * 12 FIFTH 6 TRIANGL 20 0 1 2 off 4 3 off on off off 0 0 0 0 2 2 2 5 1 0.5 0.5 14.1 no no no no 8 8 8 8 W1 W1 W1 W8 -3 3 3 -3 31 31 31 31 18 15 18 14 15 15 13 9 6 5 6 15 8 7 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 99 73 0 0 0 1 0 24 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Dyno * 13 FIFTH 7 TRIANGL 24 3 16 12 on 3 0 off off off off 0 0 0 0 2 3 2 3 1 4 24.11 12.11 no no yes yes 8 8 31 48 W1 W1 W1 W8 0 -3 3 3 31 22 31 31 4 7 19 28 14 11 8 10 3 3 8 4 7 7 6 7 VOF VOF VOF V48 99 99 99 50 50 50 99 82 92 78 2 2 1 3 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 4 DynoWurlie 14 SEVENTH 6 TRIANGL 35 14 22 0 on 2 0 off off off off 0 0 0 0 4 5 5 5 0.5 2 3 1 no no no no 8 8 8 8 W3 W1 W1 W3 0 0 0 0 31 31 31 21 31 31 31 31 15 15 15 15 3 3 3 3 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 91 90 89 73 0 0 0 0 25 20 80 38 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 E Organ..1 15 SIXTH 6 TRIANGL 28 0 25 0 on 2 0 off off off off 0 0 0 0 5 5 5 5 4 4 4 8 no no no no 8 8 8 8 W3 W3 W1 W1 -1 1 -2 -1 31 31 31 21 31 31 31 31 15 15 15 15 3 3 3 3 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 89 42 0 0 0 0 25 20 80 38 -24 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 E Organ..2 16 SEVENTH 0 TRIANGL 35 0 14 0 off 3 0 off off off off 0 0 0 0 5 5 5 6 1 4 1 4 no no no no 8 8 8 8 W2 W1 W4 W1 -1 1 -2 -1 31 31 31 24 31 31 31 22 15 15 15 15 2 3 31 3 12 12 12 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 99 76 0 0 0 0 25 20 0 0 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 E Organ..3 17 SEVENTH 0 TRIANGL 14 0 71 78 on 1 1 off off off on 0 0 0 0 5 3 6 4 0.5 3 2 2 no no no no 8 8 8 8 W3 W3 W1 W1 3 -3 3 0 31 31 31 31 31 31 31 22 15 15 15 15 0 0 0 0 12 12 12 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 80 85 0 0 0 0 25 20 0 0 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 E Organ..4 18 EIGHTH 0 TRIANGL 14 0 71 0 on 2 0 off off off off 0 0 0 0 5 2 6 5 0.5 2 2 1 no no no no 8 8 8 8 W4 W4 W1 W1 3 -3 0 3 23 27 31 30 31 31 31 22 15 15 15 15 0 0 0 0 12 12 12 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 87 90 0 0 0 1 25 20 0 0 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 E Organ..5 19 SIXTH 0 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 5 5 5 4 0.5 2 4 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 31 27 31 31 31 18 31 15 15 15 15 0 7 31 0 12 12 12 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 80 65 0 0 0 1 25 20 0 0 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 E Organ..6 20 EIGHTH 0 TRIANGL 33 0 13 0 off 3 0 off off off off 0 0 0 0 5 3 6 3 1 1 2 0.5 no no no no 8 8 8 8 W4 W3 W1 W1 3 2 3 -3 28 31 27 31 31 24 18 31 15 15 15 15 6 21 6 3 12 10 12 13 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 81 0 0 0 1 25 20 0 0 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 E Organ..7 21 EIGHTH 0 TRIANGL 39 0 13 0 off 4 0 off off off off 0 0 0 0 5 5 4 5 0.5 1 1 6 no no no no 8 8 8 8 W3 W1 W3 W1 0 0 0 0 28 31 27 31 31 24 18 31 15 15 15 15 6 21 6 3 12 10 12 13 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 1 25 20 0 99 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 E Organ..8 22 FIRST 0 TRIANGL 35 0 6 0 on 5 0 off off off off 0 0 0 0 5 5 5 3 1 1 3 7 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 31 31 31 31 31 31 31 12 15 15 15 15 0 0 0 0 9 10 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 90 71 20 65 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 E Organ..9 23 SIXTH 5 TRIANGL 46 0 34 0 off 1 0 off off off off 0 0 0 0 5 5 5 3 1 2 1 3 no no no no 8 8 8 8 W1 W1 W3 W1 0 0 0 0 31 31 31 31 31 31 31 31 15 15 15 15 1 4 3 4 13 12 13 13 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 70 0 0 0 0 0 0 99 99 -12 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 E Organ.10 24 FOURTH 0 TRIANGL 35 0 6 0 on 5 0 off off off off 0 0 0 0 5 6 5 3 1 1 1 1 no no no no 8 8 8 8 W1 W2 W1 W3 1 -1 0 0 31 31 31 31 31 31 31 12 15 15 15 15 0 0 0 0 9 10 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 90 65 53 77 0 0 0 0 1 16 0 99 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 E Organ.11 25 FIFTH 0 TRIANGL 29 0 13 0 on 3 0 off off off off 0 0 0 0 5 5 5 4 1 0.5 1 3 no no no no 8 8 8 8 W1 W2 W5 W1 1 0 0 0 25 4 27 31 21 21 18 31 15 15 15 15 10 3 6 3 9 7 12 13 VOF VOF VOF VOF 99 99 99 50 50 50 93 99 99 68 0 0 0 1 25 20 0 99 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 E Organ.12 26 THIRD 0 TRIANGL 35 0 6 0 on 4 0 off off off off 0 0 0 0 5 5 5 4 1 0.5 3 2 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 26 31 26 31 31 31 31 12 15 15 15 15 2 0 2 0 9 10 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 95 71 42 45 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 E Organ.13 27 EIGHTH 3 TRIANGL 32 0 3 0 off 5 2 on on on on 0 0 0 0 0 0 0 0 0.5 1 1.49 3 no no no no 8 8 9 8 W1 W1 W1 W1 -3 3 -3 3 31 31 31 31 31 31 31 11 15 15 15 8 0 0 0 1 14 14 14 15 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 99 95 1 0 0 1 0 0 0 24 0 POLY 2 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 E.Organ 1 28 SEVENTH 4 TRIANGL 35 0 1 42 off 4 0 off off off on 0 0 0 0 0 0 0 0 3 2 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 0 0 31 31 31 31 31 31 9 10 15 15 15 10 0 0 0 0 12 12 12 11 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 93 67 0 0 0 0 0 26 8 32 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 E.Organ 2 29 EIGHTH 5 TRIANGL 33 0 0 21 off 4 2 on on on on 0 0 0 0 0 0 0 0 1 1 2 3 no no no no 8 8 8 8 W3 W1 W2 W1 3 -3 2 -2 31 31 31 31 31 31 31 20 10 10 10 10 0 0 0 0 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 99 94 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 E.Organ 3 30 EIGHTH 7 TRIANGL 35 0 25 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 1 2 2 2 no no no no 8 8 8 8 W3 W5 W2 W1 3 3 -3 -3 23 21 24 20 1 0 1 0 15 15 15 15 0 0 0 0 6 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 92 85 0 0 0 0 0 0 0 27 -12 POLY 2 FULL 0 POR 99 0 0 50 0 99 0 50 0 0 0 0 0 0 E.Organ 4 31 EIGHTH 7 TRIANGL 35 0 18 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 1 2 4 6 no no no no 8 8 8 8 W2 W6 W6 W5 0 0 0 0 31 31 31 31 0 0 0 0 15 15 15 15 0 0 0 0 11 11 11 11 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 90 0 0 0 0 0 0 0 27 -12 POLY 7 FULL 0 POR 99 0 0 50 0 99 0 50 0 0 0 0 0 0 Farcheeza 32 EIGHTH 5 TRIANGL 37 0 3 11 off 3 2 on on on on 0 0 0 0 0 0 0 0 0.75 1.5 2.25 4.49 no no no no 12 16 12 11 W1 W1 W1 W1 0 3 -3 0 28 28 28 31 6 25 16 15 15 15 15 0 0 0 0 0 12 11 12 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 97 0 0 0 0 20 20 21 28 -7 POLY 2 FULL 0 POR 40 0 0 11 64 0 0 50 0 0 0 0 0 0 Hammond * 33 SECOND 0 TRIANGL 35 14 16 16 on 4 1 on off on off 7 4 3 4 4 6 5 2 1 4 1 1 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 0 0 22 31 4 2 31 8 31 4 15 12 15 5 0 0 0 16 13 13 13 10 VOF VOF VOF VOF 99 99 99 50 50 50 95 61 70 82 0 0 3 2 0 18 13 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 Hammond..1 34 FOURTH 0 SQUARE 37 0 7 0 off 3 0 off off off off 0 0 0 0 5 3 6 3 2 1 1 1 no no no no 8 8 8 8 W1 W4 W1 W3 0 -1 -1 0 27 31 31 27 30 26 31 31 15 12 15 11 0 0 0 0 9 13 13 10 VOF VOF VOF VOF 99 99 99 50 50 50 91 70 70 74 0 3 2 0 99 82 25 25 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Hammond..2 35 SIXTH 1 TRIANGL 14 0 20 24 off 3 2 on off off off 0 0 0 0 5 4 5 3 1 2 3 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 -2 2 0 31 31 25 31 31 31 31 14 14 14 15 6 0 4 0 5 7 7 7 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 78 71 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Hammond..3 36 EIGHTH 7 TRIANGL 35 0 35 0 off 3 0 off off off off 0 0 0 0 5 4 6 3 1 1 8 3 no no no no 8 8 8 8 W2 W1 W5 W1 0 0 0 0 28 31 31 31 6 5 8 5 7 11 12 13 4 0 0 1 4 5 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 92 83 2 2 3 2 0 45 67 45 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Hammond..4 37 FIFTH 1 TRIANGL 29 0 71 85 off 3 1 off on off on 0 0 0 0 4 5 5 5 2 1 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 30 29 26 10 18 12 23 0 15 15 15 0 0 0 0 16 15 15 15 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 64 86 50 1 0 1 0 16 1 81 0 -12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Hammond..5 38 FOURTH 1 TRIANGL 31 0 15 0 off 3 0 off off off off 0 0 0 0 5 5 5 3 1 1 3 1 no no no no 8 8 8 8 W1 W3 W1 W4 0 -1 -1 0 27 31 31 27 30 26 31 31 15 12 15 11 0 0 0 0 9 13 13 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 67 84 0 1 3 2 99 82 25 25 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Hammond..6 39 FIFTH 0 TRIANGL 33 0 21 35 off 2 1 off on off on 0 0 0 0 5 5 4 5 1 2 4 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 30 29 26 23 18 12 23 13 15 15 15 0 0 0 0 16 15 15 15 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 64 85 70 1 0 1 0 16 1 81 0 -12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Hammond..7 40 SEVENTH 0 TRIANGL 36 15 53 0 off 3 0 off off off off 0 0 0 0 4 5 6 4 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W2 W2 W1 W1 0 -3 3 -3 18 18 18 31 21 21 21 22 15 15 15 15 4 4 4 4 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 88 1 1 1 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 HamondLead 41 EIGHTH 5 TRIANGL 48 0 52 99 off 1 1 off on off off 0 0 0 0 5 4 3 3 1 2 2 3 no no no no 8 8 8 8 W2 W5 W1 W1 -1 -2 2 2 28 27 28 28 24 24 24 9 15 15 15 15 7 16 7 7 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 10 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Hmnd LS f1 42 EIGHTH 0 TRIANGL 27 0 15 99 off 3 1 off off off off 0 0 0 0 5 4 4 3 0.5 1 1 1 no no no no 8 8 8 8 W4 W4 W1 W1 -1 -2 2 2 28 27 28 28 24 7 24 24 15 15 15 15 7 7 7 7 13 13 13 13 VOF VOF VOF VOF 99 99 99 50 50 50 96 96 95 99 0 0 0 0 0 0 10 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Hmnd LS f2 43 EIGHTH 4 TRIANGL 48 0 14 0 off 2 0 off off off off 0 0 0 0 5 5 5 5 1 2 2 1 no no no no 8 8 8 8 W1 W1 W4 W3 -1 -2 2 2 28 27 28 28 24 7 24 7 15 15 15 15 7 7 7 1 13 13 13 13 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 95 92 0 0 0 0 0 0 10 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Hmnd LS s1 44 SIXTH 0 TRIANGL 0 1 4 0 on 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 0.5 no no no no 8 8 8 8 W2 W8 W7 W1 0 0 0 0 14 16 23 18 31 31 31 31 15 15 15 15 0 0 0 0 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 90 90 88 0 0 0 0 25 25 25 25 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Hornopean 45 EIGHTH 3 TRIANGL 35 0 2 0 off 4 3 on off off off 0 0 0 0 0 0 0 0 1 1 6 3 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 3 3 31 31 31 31 31 31 18 10 15 15 15 15 0 0 14 0 10 10 10 11 VOF VOF VOF VOF 99 99 99 50 50 50 79 91 88 82 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 50 50 50 0 50 0 0 0 0 0 0 Jazz Organ 46 EIGHTH 3 TRIANGL 34 0 5 23 off 6 1 on on on on 0 0 0 0 0 0 0 0 1.5 3 9 18.05 no no no no 16 8 8 15 W1 W2 W1 W1 3 -3 3 0 27 27 27 27 31 31 31 31 15 15 15 15 0 0 0 7 11 11 11 12 VOF VOF VOF VOF 99 99 99 50 50 50 92 99 86 89 0 0 0 0 0 0 0 66 -19 POLY 2 FULL 0 POR 40 0 0 0 55 0 0 50 0 0 0 0 0 0 Leslie * 47 EIGHTH 3 TRIANGL 39 0 20 28 off 3 1 on on on on 7 7 7 7 6 7 7 5 1 2 3 3 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 3 3 31 31 31 31 31 31 31 10 15 15 15 0 1 1 1 1 10 10 10 11 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 95 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Organ 1 48 FIFTH 0 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 0 0 0 0 1 1 0.5 0.5 no no no no 8 8 8 8 W5 W2 W1 W1 0 0 0 0 31 31 31 31 15 15 15 15 15 15 15 15 0 0 0 0 9 9 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 70 90 64 0 0 0 0 0 0 0 0 0 POLY 0 FULL 0 POR 99 0 0 50 50 50 0 50 0 0 0 0 0 0 Organ 2 49 FIFTH 0 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 0 0 0 0 1 1 0.5 0.5 no no no no 8 8 8 8 W5 W2 W2 W1 0 0 0 0 31 31 31 31 15 15 15 15 15 15 15 15 0 0 0 0 9 9 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 70 90 80 0 0 0 0 0 54 0 78 0 POLY 0 FULL 0 POR 99 0 0 50 50 50 0 50 0 0 0 0 0 0 Organ 3 50 SEVENTH 5 SAWUP 99 33 0 0 off 4 0 off off off off 0 0 0 0 4 2 5 3 1 1 1 23.55 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 3 0 29 29 29 24 7 7 7 24 11 11 11 0 7 7 7 6 8 8 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 99 99 79 1 1 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Ornt Organ 51 EIGHTH 5 TRIANGL 33 25 29 0 off 5 0 off off off off 1 1 1 1 5 5 5 5 1 2 3 6 no no no no 8 8 8 8 W2 W1 W3 W1 0 0 0 0 26 26 26 26 31 31 31 31 15 15 15 15 0 0 0 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 P Organ..1 52 FIFTH 6 TRIANGL 33 25 29 0 off 5 0 off off off off 0 0 0 0 5 6 5 4 0.5 1 1 8 no no no no 8 8 8 8 W2 W3 W3 W1 0 0 0 0 26 26 26 26 31 31 31 31 15 15 15 15 0 0 0 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 69 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 P Organ..2 53 FIFTH 6 TRIANGL 33 25 29 0 off 5 0 off off off off 0 0 0 0 5 3 5 3 2 1 2 8 no no no no 8 8 8 8 W1 W2 W3 W4 0 0 0 0 26 26 16 20 31 31 7 31 15 15 15 14 0 0 7 19 6 6 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 68 99 67 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 P Organ..3 54 FIFTH 0 TRIANGL 35 65 0 0 off 1 0 off off off off 0 0 0 0 5 6 6 5 1 1 4 0.5 no no no no 8 8 8 8 W1 W3 W2 W7 0 0 0 0 21 22 13 21 31 31 31 31 15 15 15 15 0 0 0 0 7 7 6 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 99 80 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 P Organ..4 55 FIFTH 0 TRIANGL 35 65 0 0 off 1 0 off off off off 0 0 0 0 5 4 5 2 2 1 2 4 no no no no 8 8 8 8 W3 W2 W1 W5 1 -1 0 0 24 29 19 31 7 31 31 31 15 15 15 15 6 5 7 5 10 8 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 99 76 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 P Organ..5 56 SEVENTH 5 TRIANGL 35 65 0 0 off 1 0 off off off off 0 0 0 0 5 5 5 5 1 2 2 10 no no no no 8 8 8 8 W3 W1 W1 W2 1 -1 0 0 22 18 18 22 3 3 7 6 15 15 15 15 4 3 4 3 10 8 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 76 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 P Organ..6 57 FIFTH 0 TRIANGL 35 65 0 0 off 1 0 off off off off 0 0 0 0 5 6 6 5 0.5 0.5 2 10 no no no no 8 8 8 8 W1 W3 W1 W1 1 -1 0 0 13 31 18 22 5 5 5 7 15 15 15 15 4 3 4 3 10 9 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 74 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 P Organ..7 58 FIFTH 4 TRIANGL 33 25 22 35 off 4 1 on off off off 0 0 0 0 5 6 5 6 0.5 0.5 2 14 no no no no 8 8 8 8 W1 W1 W2 W2 1 1 0 0 16 26 19 26 31 31 31 31 15 15 15 15 0 0 0 0 6 6 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 99 76 0 0 0 1 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 P Organ..8 59 FIFTH 5 TRIANGL 35 65 0 0 off 1 0 off off off off 0 0 0 0 5 6 5 6 1 2 4 6 no no no no 8 8 8 8 W4 W3 W1 W1 1 -1 0 0 16 18 18 22 3 3 7 6 15 15 15 15 4 3 4 3 10 8 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 99 68 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 P Organ..9 60 SEVENTH 0 TRIANGL 35 0 0 56 off 1 1 off off off on 0 0 0 0 5 5 6 6 1 2 2 4 no no no no 8 8 8 8 W2 W1 W7 W1 -2 3 3 3 21 16 24 25 0 0 0 0 15 15 15 15 0 0 0 0 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 78 0 0 0 0 0 99 0 62 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 P Organ.10 61 FIFTH 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 5 3 0.5 0.5 2 2 no no no no 8 8 8 8 W1 W8 W1 W1 3 -3 -3 -1 14 24 15 22 0 0 0 0 15 15 15 15 0 0 0 0 5 5 8 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 71 2 0 2 0 0 91 0 0 -12 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 5 P Organ.11 62 FIFTH 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 5 3 3 0.5 1 2 6 no no no no 8 8 8 8 W1 W1 W1 W1 1 -1 -1 2 13 27 18 22 7 7 7 7 15 15 15 15 7 1 1 1 5 7 9 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 97 86 2 0 2 0 0 91 0 0 -12 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 P Organ.12 63 FIFTH 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 5 3 0.5 1 2 6 no no no no 8 8 8 8 W2 W2 W1 W1 1 -1 -1 2 13 31 18 21 7 7 7 7 15 3 15 15 0 0 1 0 5 7 9 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 95 79 0 0 0 0 0 91 46 67 -12 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 P Organ.13 64 FIFTH 5 TRIANGL 35 0 22 0 off 2 0 off off off off 0 0 0 0 5 6 4 4 0.5 0.5 1 4 no no no no 8 8 8 8 W2 W6 W1 W1 1 -1 -1 2 17 21 18 21 3 4 7 7 15 15 15 15 0 0 1 0 6 8 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 98 89 0 0 0 0 0 21 46 67 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 P Organ.14 65 FIFTH 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 5 3 0.5 1 2 8 no no no no 8 8 8 8 W2 W4 W3 W4 0 -1 -1 1 15 18 15 19 4 4 4 4 15 15 15 15 0 0 0 0 6 8 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 99 88 0 1 0 1 0 91 46 67 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 P Organ.15 66 SEVENTH 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 3 6 4 0.5 1 4 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 -1 1 15 18 15 19 4 4 4 4 15 15 15 15 0 0 0 0 6 8 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 99 71 0 1 0 1 0 91 46 67 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 P Organ.16 67 SECOND 5 TRIANGL 33 25 29 0 off 5 0 off off off off 0 0 0 0 5 6 7 6 1 2 3 6 no no no no 8 8 8 8 W1 W2 W5 W1 0 0 0 0 26 26 26 26 31 31 31 31 15 15 15 15 0 0 0 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 69 80 81 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 P Organ.17 68 SEVENTH 2 TRIANGL 70 0 0 0 off 2 0 off off off off 0 0 0 0 0 0 0 0 3 5 1 0.5 no no no no 8 8 8 8 W3 W3 W1 W3 -3 3 0 0 19 10 20 30 31 31 31 31 15 15 15 15 0 0 0 0 10 10 10 6 VOF VOF VOF VOF 99 99 99 50 50 50 76 76 90 76 1 1 1 1 25 25 0 10 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 P.Organ 1 69 EIGHTH 6 TRIANGL 35 1 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.5 2 3 1 no no no no 8 8 8 8 W5 W4 W3 W3 -3 2 3 0 14 16 23 18 31 31 31 31 15 15 15 15 0 0 0 0 8 8 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 94 96 95 94 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 P.Organ 2 70 SEVENTH 2 TRIANGL 70 0 0 0 off 2 0 off off off off 0 0 0 0 0 0 0 0 3 5 1 0.5 no no no no 8 8 8 8 W3 W3 W1 W3 -3 3 0 0 19 10 20 30 31 31 31 31 15 15 15 15 0 0 0 0 5 6 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 76 76 90 76 1 1 1 1 25 25 0 10 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 P.Organ 1* 71 EIGHTH 6 TRIANGL 35 1 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.5 2 4 1 no no no no 8 8 8 8 W5 W4 W3 W3 -3 2 3 0 14 16 23 18 31 31 31 31 15 15 15 15 0 0 0 0 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 94 96 95 94 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 P.Organ 2* 72 EIGHTH 6 TRIANGL 35 1 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.5 2 3 1 no no no no 8 8 8 8 W6 W5 W1 W4 -3 2 3 0 14 16 23 18 31 31 31 31 15 15 15 15 0 0 0 0 5 5 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 94 96 95 94 2 2 1 0 0 9 64 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 P.Organ 3 73 EIGHTH 5 TRIANGL 35 1 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.5 1 2 1 no no no no 8 8 8 8 W5 W2 W3 W2 -3 0 3 0 14 16 23 18 31 31 31 31 15 15 15 15 0 0 0 0 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 85 99 85 84 0 0 0 0 0 99 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 P.Organ 4 74 EIGHTH 6 TRIANGL 35 1 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.5 1 2 0.5 no no no no 8 8 8 8 W5 W5 W5 W1 -3 2 3 0 14 16 23 18 31 31 31 31 15 15 15 15 0 0 0 0 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 94 96 95 94 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 P.Organ 5 75 EIGHTH 3 TRIANGL 32 0 3 0 off 5 2 on on on on 0 0 0 0 0 0 0 0 0.5 1 1.49 3 no no no no 8 8 9 8 W1 W1 W1 W1 -3 -3 3 3 31 31 31 31 31 31 31 11 15 15 15 8 0 0 0 1 10 10 10 11 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 99 95 1 0 0 1 0 0 0 24 0 POLY 4 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 PercOrgan 76 EIGHTH 0 TRIANGL 30 0 0 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 0.5 4 1 8 no no no no 8 8 8 8 W6 W8 W5 W8 0 0 0 0 23 17 23 22 0 0 0 0 15 15 15 15 0 0 0 0 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 5 Pipe Organ 77 FIRST 7 TRIANGL 20 0 0 14 off 3 3 on off off off 0 0 0 0 0 0 0 0 1.5 1.49 1.5 7.49 no no no no 16 9 16 13 W2 W4 W2 W3 0 1 3 0 13 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 9 5 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 80 35 77 0 0 0 0 0 0 0 30 -7 POLY 4 FULL 0 POR 99 0 0 50 10 0 0 50 0 0 0 0 0 0 PumpOrgan 78 FIFTH 0 TRIANGL 25 6 4 0 off 5 0 off off off off 0 0 0 0 1 3 1 3 1.5 1.5 1.49 1.49 no no no no 16 16 9 9 W1 W1 W1 W1 -1 0 3 2 12 14 11 13 31 3 31 3 15 14 15 14 0 0 0 0 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 99 78 0 0 0 0 0 35 0 35 -19 POLY 2 FULL 0 POR 99 0 0 15 0 12 0 50 0 0 0 0 0 0 RevrbOrgan 79 EIGHTH 0 TRIANGL 30 0 0 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 0.5 4 1 8 no no no no 8 8 8 8 W1 W1 W1 W1 -2 3 3 3 23 17 23 22 0 0 0 0 15 15 15 15 0 0 0 0 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 88 75 0 0 0 0 0 99 0 62 0 POLY 4 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Small Pipe 80 SEVENTH 0 TRIANGL 30 10 17 26 off 5 3 off on off off 0 0 0 0 2 2 3 2 1 2 3 3 no no no no 8 8 8 8 W1 W1 W5 W1 0 0 0 0 31 31 31 31 31 20 20 18 15 14 6 10 0 0 0 0 8 9 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 80 1 2 0 1 0 0 0 20 0 POLY 2 FULL 0 POR 99 0 0 50 35 0 0 50 0 0 0 0 0 0 Sy.Organ 1 81 SEVENTH 0 TRIANGL 30 13 17 26 off 5 3 off on off off 0 0 0 0 2 2 3 2 1 2 4 4 no no no no 8 8 8 8 W1 W1 W5 W1 0 0 0 0 31 31 31 31 31 20 20 18 15 14 6 13 0 0 0 0 8 9 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 80 1 2 0 1 0 0 0 20 0 POLY 2 FULL 0 POR 99 0 0 50 35 0 0 50 0 0 0 0 0 0 Sy.Organ 1* 82 THIRD 7 TRIANGL 35 0 39 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 1 2 3 3 no no no no 8 8 8 8 W2 W3 W5 W1 1 -1 1 0 31 30 31 27 6 28 0 0 15 1 11 15 0 0 0 0 9 4 10 15 VOF VOF VOF VOF 99 99 99 50 50 50 83 68 73 36 0 0 0 0 0 0 0 27 0 POLY 2 FULL 0 POR 99 0 0 99 0 99 0 50 0 0 0 0 0 0 Sy.Organ 2 83 EIGHTH 6 TRIANGL 35 1 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.5 2 4 1 no no no no 8 8 8 8 W5 W4 W3 W3 -3 2 3 0 14 16 23 18 31 31 31 31 15 15 15 15 0 0 0 0 6 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 94 96 95 94 0 0 0 0 60 50 50 60 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Tocc Organ 84 FOURTH 0 TRIANGL 29 21 5 0 off 5 0 off off off off 0 0 0 0 3 7 7 7 1.5 5.99 1.49 6.05 no no no no 16 12 9 16 W1 W1 W2 W1 -2 1 3 -3 28 28 25 30 31 8 8 7 14 13 13 13 0 0 0 0 10 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 93 43 1 0 0 0 0 65 65 50 -19 POLY 2 FULL 0 POR 99 0 0 35 0 0 0 50 0 0 0 0 0 0 VelocOrgan 85 FIFTH 6 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 2 6 0 6 1 0.5 0.5 2 no no no no 8 8 8 8 W1 W1 W1 W1 -3 0 3 -3 29 29 29 27 6 7 6 7 14 14 14 14 6 6 6 6 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 74 1 1 1 1 0 0 0 18 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Wirlitzer* 86 Piano 3 FIFTH 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 7 4 6 6 1 1 1 3 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 0 0 22 22 27 27 9 9 6 3 13 13 4 4 6 6 1 2 6 6 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 77 99 88 1 1 1 2 0 0 99 99 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 AC Piano.1 0 THIRD 4 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 6 5 5 4 1 1 5 8 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 21 20 20 18 7 5 2 5 12 13 13 4 6 4 2 2 6 6 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 61 59 1 3 1 2 0 0 0 99 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 AC Piano.2 1 THIRD 3 TRIANGL 35 10 0 6 off 5 1 on off off off 0 0 0 0 6 6 5 5 1 1 5 1 no no no no 8 8 8 8 W1 W1 W1 W1 -1 2 -2 2 24 20 18 21 6 6 6 1 9 9 0 0 5 5 11 1 5 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 96 83 68 73 1 1 1 1 1 66 66 27 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AC Piano.3 2 FIFTH 5 TRIANGL 25 0 0 0 off 6 1 on off on on 0 0 0 0 5 4 5 4 1 1 1 1 no no no no 8 8 8 8 W1 W4 W1 W1 -3 3 -2 2 25 25 24 21 9 9 6 1 15 15 9 0 9 9 5 1 5 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 98 76 0 0 1 1 1 1 22 50 0 POLY 1 FULL 0 POR 99 0 0 0 99 0 0 50 0 0 0 0 0 0 AC Piano.4 3 FOURTH 0 TRIANGL 0 0 0 0 off 6 0 off off off off 0 0 0 0 6 5 5 4 1 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 -3 -3 26 25 24 25 5 31 9 7 10 15 0 0 6 0 0 0 5 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 67 73 1 0 0 1 1 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AC Piano.5 4 THIRD 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 6 5 5 5 1 3 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 31 31 31 30 5 4 8 15 15 14 13 6 1 2 3 6 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 93 76 71 73 2 0 1 0 0 99 99 45 -12 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 AC Piano.6 5 THIRD 7 TRIANGL 33 60 10 0 on 3 0 off off off off 0 0 0 0 6 5 6 4 1 1 3 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 -1 1 24 20 20 22 8 6 7 8 9 9 0 0 6 7 0 0 5 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 93 72 54 58 1 2 0 1 15 35 0 0 0 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 AC Piano.7 6 FIFTH 4 TRIANGL 25 0 0 0 off 6 1 on off on on 6 3 0 0 5 5 5 3 1 1 1 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 -2 24 22 24 22 7 9 7 9 13 14 13 14 5 1 5 1 5 3 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 96 93 99 79 1 2 1 2 0 50 0 50 0 POLY 1 FULL 0 POR 99 0 0 0 99 0 0 50 0 0 0 0 0 0 AC Piano.8 7 FOURTH 4 TRIANGL 25 0 0 0 off 6 1 on off on on 0 0 0 0 5 5 3 5 1 3 1 5 no no no no 8 8 8 8 W1 W4 W1 W3 0 1 -1 0 28 28 28 28 9 9 9 9 13 13 13 13 7 7 7 7 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 75 58 1 1 1 1 0 0 0 0 0 POLY 1 FULL 0 POR 99 0 0 0 99 0 0 50 0 0 0 0 0 0 AC Piano.9 8 THIRD 6 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 6 4 1 0.5 7 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -3 2 20 22 24 24 8 1 5 1 12 12 0 0 1 1 1 1 6 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 69 75 2 1 2 1 0 66 99 52 0 POLY 0 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AC Piano10 9 THIRD 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 4 6 4 1 3 5 1 no no no no 8 8 8 8 W1 W1 W2 W1 0 -2 1 2 28 31 31 31 30 4 5 8 15 14 13 13 4 2 4 3 6 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 92 74 80 73 2 0 0 1 0 99 99 0 -12 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 AC Piano11 10 FOURTH 3 TRIANGL 25 0 0 0 off 6 1 on off on on 0 0 0 0 6 4 5 5 1 1 1 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 27 28 24 7 5 6 12 9 7 12 6 7 6 6 9 7 9 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 96 65 78 70 1 2 1 0 0 39 17 82 0 POLY 1 FULL 0 POR 99 0 0 0 99 0 0 50 0 0 0 0 0 0 AC Piano12 11 THIRD 5 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 6 4 1 1 1 3 no no no no 8 8 8 8 W1 W3 W1 W3 0 1 0 0 26 25 24 25 9 9 9 9 13 13 13 13 9 0 0 0 7 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 76 70 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AC Piano13 12 THIRD 5 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 5 2 1 5 1 3 no no no no 8 8 8 8 W1 W3 W1 W1 0 1 0 0 27 27 27 27 9 9 9 9 13 13 13 13 6 6 6 6 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 93 70 76 56 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AC Piano14 13 THIRD 5 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 6 5 7 5 1 1 4 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 1 3 21 19 19 28 7 4 4 6 12 13 13 14 6 4 4 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 62 47 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AC Piano15 14 THIRD 5 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 6 4 1 1 9 3 no no no no 8 8 8 8 W1 W1 W1 W1 1 -1 0 1 21 19 19 24 7 4 4 13 12 13 13 13 6 3 4 5 6 6 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 93 78 45 76 1 1 1 1 0 19 43 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AC Piano16 15 FIRST 5 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 6 5 6 5 1 1 1 7 no no no no 8 8 8 8 W1 W1 W4 W1 0 0 1 -1 23 31 31 28 7 12 12 24 13 13 13 10 6 1 1 5 8 4 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 94 78 80 71 1 1 1 1 0 19 19 1 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AC Piano17 16 THIRD 0 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 6 5 6 2 1 1 5 3 no no no no 8 8 8 8 W1 W2 W1 W1 1 -1 1 2 25 25 25 31 7 6 8 9 9 10 9 12 5 21 5 5 5 9 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 93 65 70 81 1 1 1 1 0 19 0 1 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AC Piano18 17 THIRD 4 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 6 6 5 5 1 1 1 7 no no no no 8 8 8 8 W1 W1 W1 W1 -1 1 -1 2 25 25 25 31 9 6 6 10 13 10 10 12 6 6 6 9 7 9 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 80 78 67 1 0 0 0 0 19 19 1 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AC Piano19 18 THIRD 5 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 6 5 5 5 1 3 5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 0 0 25 25 25 25 9 9 9 9 13 13 13 13 6 9 9 9 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 75 65 61 1 0 0 1 1 1 1 1 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AC Piano20 19 THIRD 3 TRIANGL 35 10 0 6 off 5 1 on off off off 0 0 0 0 4 5 5 5 1 6 4 1 no no no no 8 8 8 8 W1 W1 W1 W1 -1 2 -2 2 24 20 18 21 6 6 6 1 9 9 0 0 5 5 11 1 5 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 96 72 75 71 1 1 1 1 10 66 66 27 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 AC Piano21 20 THIRD 3 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 2 3 1 1 1 5 1 no no no no 8 8 8 8 W1 W1 W1 W3 -3 1 -2 1 28 30 30 30 10 12 9 16 14 14 14 14 6 6 7 6 7 3 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 79 68 1 1 1 1 0 43 17 0 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 BabyGrand* 21 FIFTH 0 TRIANGL 35 0 0 0 off 0 3 on off on off 0 0 0 0 3 5 5 2 1 1 1 10.57 no no no no 8 8 8 16 W1 W2 W1 W2 -3 3 3 0 23 23 24 20 6 6 7 15 15 15 3 2 6 6 13 24 7 10 10 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 99 56 1 1 0 0 0 12 14 17 -12 POLY 2 FULL 0 POR 40 0 0 0 25 0 0 50 0 0 0 0 0 0 Bad Road * 22 FIFTH 0 TRIANGL 23 0 8 12 off 3 2 on off on off 0 0 0 0 3 5 2 2 0.75 1.49 1.5 2.99 no no no no 12 9 16 10 W1 W1 W1 W4 -3 3 0 2 28 28 28 31 9 9 9 9 0 0 0 0 31 31 31 31 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 99 88 1 0 1 0 0 0 0 0 -7 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Bel/Piano* 23 FIFTH 7 TRIANGL 24 82 0 81 off 2 1 on off off off 0 0 0 0 6 5 5 3 1 2 2 15 no no no no 8 8 8 8 W1 W2 W1 W2 0 0 0 0 31 31 31 31 9 27 9 27 15 15 15 10 12 10 7 10 4 6 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 94 81 99 99 0 0 0 2 0 0 0 0 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 BellPiano1 24 SEVENTH 7 TRIANGL 24 82 0 81 off 2 1 on off off off 0 0 0 0 7 4 5 4 2 8 2 2 no no no no 8 8 8 8 W1 W2 W2 W1 0 0 0 0 31 31 31 31 9 27 9 27 15 15 15 10 12 10 7 10 4 6 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 94 99 99 99 0 0 0 2 0 0 0 0 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 BellPiano2 25 THIRD 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 2 3 3 1 1 4 7 1 no no no no 8 8 8 8 W1 W1 W2 W1 0 0 -3 2 31 31 24 29 8 7 5 4 13 12 0 0 3 3 3 12 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 78 77 2 1 3 1 0 94 61 52 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BritePiano 26 THIRD 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 1 4 1 1 1 4 7 1 no no no no 8 8 8 8 W1 W2 W2 W2 0 0 -3 2 31 31 24 29 8 7 5 4 13 12 0 0 3 3 3 12 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 71 79 2 2 3 1 0 94 61 52 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Buz'dPiano 27 THIRD 7 TRIANGL 29 0 0 0 off 5 0 off off off off 0 0 0 0 2 4 2 2 1 4 7 7 no no no no 8 8 8 8 W1 W1 W2 W8 0 0 -3 2 31 31 24 29 8 7 5 21 13 12 0 0 3 3 3 12 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 74 73 2 1 3 2 0 27 16 33 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 ChibyPiano 28 FIFTH 6 TRIANGL 20 0 1 2 off 2 1 off on off off 0 0 0 0 2 3 3 3 1 0.5 1 14 no no no no 8 8 8 8 W1 W2 W1 W8 3 -3 -3 3 28 31 31 30 4 0 18 14 10 10 13 9 0 0 6 15 7 7 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 83 83 2 1 0 1 0 48 0 0 0 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 7 ChorusMeEP 29 FIFTH 0 TRIANGL 26 18 0 0 off 4 2 off off off off 0 0 0 0 5 3 5 3 1.49 1.49 1.5 1.5 no no no no 9 65 16 104 W6 W5 W6 W5 3 3 -3 -3 31 31 31 31 25 31 25 31 15 11 15 11 5 8 5 8 6 8 6 8 VOF V48 VOF V48 99 99 99 50 50 50 98 79 99 79 2 2 2 2 0 99 0 99 -19 POLY 2 FULL 0 POR 98 0 0 50 0 37 0 50 0 0 0 0 0 0 CrsPopPno 30 FIFTH 3 TRIANGL 22 0 4 10 off 4 3 on on on on 0 0 0 0 2 3 3 4 1 1 1 15 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 0 3 30 30 27 31 7 6 7 15 0 0 7 7 0 0 10 0 9 6 9 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 79 73 0 0 0 0 20 16 20 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 DX7 EP 31 THIRD 6 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 3 3 4 3 1 0.5 5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -3 2 20 22 24 24 8 1 5 1 12 12 0 0 1 1 1 1 5 5 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 54 71 2 2 1 1 1 70 74 21 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Deep Grd 32 FIFTH 0 TRIANGL 29 17 5 0 off 4 3 off on off off 0 0 0 0 5 6 4 4 8 16.97 0.5 6 no no no yes 8 77 8 96 W5 W3 W7 W8 -3 0 0 0 31 31 22 31 9 6 23 12 15 14 13 12 9 1 8 12 5 5 4 5 VOF VOF VOF VOF 99 99 99 50 50 50 73 85 99 78 1 1 1 2 0 0 0 28 0 POLY 2 FULL 0 POR 99 0 0 50 51 30 51 50 0 0 0 0 0 0 DirtoyPno 33 FIFTH 6 TRIANGL 20 0 1 2 off 4 3 off on off off 0 0 0 0 2 3 3 3 1 0.5 1 15 no no no no 8 8 8 8 W1 W1 W1 W8 3 -3 -3 3 31 31 31 31 18 15 18 14 15 15 13 9 6 5 6 15 8 7 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 99 67 0 0 0 1 0 22 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 DynomiteEP 34 FIFTH 5 TRIANGL 25 0 3 0 off 6 0 off off off off 0 0 0 0 6 6 4 2 1 3 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 0 -3 0 25 31 25 31 5 8 7 9 0 0 0 0 21 0 0 0 8 7 15 5 VOF VOF VOF VOF 99 99 99 50 50 50 95 78 95 83 2 0 1 1 0 0 0 40 -12 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano..1 35 FIFTH 5 TRIANGL 20 0 5 0 off 6 0 off off off off 0 0 0 0 5 5 6 3 1 9 1 7 no no no no 8 8 8 8 W1 W3 W1 W2 3 -1 -3 1 27 31 31 31 6 7 6 12 0 0 0 0 10 10 0 18 6 6 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 92 61 95 78 1 1 1 1 0 0 0 0 -12 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano..2 36 FIFTH 6 TRIANGL 20 0 3 0 off 5 0 off off off off 0 0 0 0 7 4 4 5 1 2 2 3 no no no no 8 8 8 8 W1 W4 W1 W3 0 0 0 0 29 26 31 29 8 8 11 9 15 12 15 15 6 2 9 3 8 5 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 95 85 93 89 1 1 2 0 1 41 0 99 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano..3 37 FIFTH 5 TRIANGL 20 0 3 0 off 5 0 off off off off 0 0 0 0 6 5 6 4 2 3 1 1 no no no no 8 8 8 8 W1 W3 W3 W3 0 0 0 0 29 26 31 29 8 8 11 9 15 12 15 15 6 2 9 3 6 3 4 7 VOF VOF VOF VOF 99 99 99 50 50 50 97 87 91 86 1 1 2 0 1 41 0 99 -12 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano..4 38 FIFTH 1 TRIANGL 15 0 0 32 off 6 1 on off off off 0 0 0 0 5 4 6 4 0.5 3 0.5 8 no no no no 8 8 8 8 W1 W3 W1 W2 -1 3 -3 0 29 25 31 28 8 7 8 9 15 0 0 14 1 4 5 5 6 9 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 92 75 96 74 0 0 0 2 25 0 25 99 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano..5 39 FIFTH 5 TRIANGL 34 33 0 0 off 3 1 on on on off 0 0 0 0 6 6 5 4 1 1 1 1 no no no no 8 8 8 8 W2 W2 W1 W1 3 -1 -3 3 28 28 28 28 7 15 6 9 12 12 13 13 7 6 6 6 11 12 15 9 VOF VOF VOF VOF 99 99 99 50 50 50 98 93 96 86 0 3 0 1 11 35 30 57 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 E Piano..6 40 FIFTH 4 TRIANGL 23 0 0 39 off 0 2 off off on off 0 0 0 0 6 4 6 4 1 3 1 1 no no no no 8 8 8 8 W2 W4 W2 W1 1 2 -2 2 31 24 31 27 10 12 6 15 15 12 15 15 6 13 6 7 7 7 6 12 VOF VOF VOF VOF 99 99 99 50 50 50 95 77 99 85 1 1 1 1 1 48 0 99 -12 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano..7 41 SIXTH 1 TRIANGL 31 0 0 32 off 0 2 off off on off 0 0 0 0 6 4 6 4 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W4 0 1 0 0 31 31 31 31 4 10 6 10 9 10 7 7 15 7 13 7 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 91 1 1 1 0 10 12 14 24 -12 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano..8 42 FIFTH 5 TRIANGL 99 0 0 0 off 0 0 off off off off 0 0 0 0 7 4 5 6 1 1 1 5 no no no no 8 8 8 8 W2 W3 W1 W1 2 3 0 1 28 31 24 28 15 15 9 6 15 15 14 14 6 6 6 6 6 3 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 94 76 99 70 0 1 0 0 10 40 0 99 -12 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano..9 43 FIFTH 5 TRIANGL 99 0 0 0 off 0 0 off off off off 0 0 0 0 6 4 6 4 1 11 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 2 3 0 1 28 24 24 31 15 22 9 19 15 14 14 13 6 13 6 9 6 8 7 5 VOF VOF VOF VOF 99 99 99 50 50 50 98 60 99 88 0 0 0 0 10 40 0 30 -12 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano.10 44 FIFTH 7 TRIANGL 99 0 0 0 off 0 0 off off off off 0 0 0 0 6 6 5 5 1 14 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 27 22 22 15 25 9 6 15 13 14 11 4 6 6 3 6 6 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 93 81 97 79 0 3 0 3 10 99 0 75 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano.11 45 FIFTH 4 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 6 5 5 5 0.5 2 1 5 no no no no 8 8 8 8 W1 W1 W1 W3 0 0 2 1 28 28 27 27 15 24 20 1 15 10 14 3 6 6 9 9 6 2 4 9 VOF VOF VOF VOF 99 99 99 50 50 50 98 99 90 82 0 2 0 3 10 99 0 75 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano.12 46 FIFTH 7 TRIANGL 34 0 0 26 off 0 2 off on on on 0 0 0 0 5 5 4 6 1 12 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 -1 -3 3 28 30 28 22 7 22 6 7 12 11 13 13 7 10 6 3 11 5 15 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 88 92 82 0 1 1 3 11 99 30 99 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 E Piano.13 47 FIFTH 1 TRIANGL 34 0 0 26 off 0 2 off on on on 0 0 0 0 6 6 4 4 1 14 1 10 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 27 30 24 24 9 9 9 9 9 11 13 11 7 7 7 7 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 90 97 65 1 2 0 1 10 99 0 44 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 E Piano.14 48 THIRD 6 TRIANGL 34 0 0 26 off 0 2 off on on on 0 0 0 0 6 5 4 5 1 10 2 1 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 27 30 31 24 9 9 24 9 9 11 13 11 7 7 7 7 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 71 79 57 0 2 3 2 10 99 99 85 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 E Piano.15 49 FIRST 6 TRIANGL 34 0 0 39 on 0 1 on off off off 0 0 0 0 5 5 4 4 1 2 2 7 no no no no 8 8 8 8 W1 W1 W3 W2 0 0 0 0 27 30 31 26 7 9 9 17 11 11 13 11 7 7 7 7 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 53 50 52 0 2 2 2 10 99 99 85 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 E Piano.16 50 FIFTH 5 TRIANGL 34 33 0 0 off 3 1 on on on off 0 0 0 0 5 6 6 6 1 1 1 7 no no no no 8 8 8 8 W1 W1 W1 W1 2 -2 -2 2 28 28 28 31 4 15 6 6 2 12 13 13 4 6 6 6 11 12 15 8 VOF VOF VOF VOF 99 99 99 50 50 50 95 90 99 87 0 1 0 2 11 35 30 20 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 E Piano.17 51 FIFTH 1 TRIANGL 34 33 0 0 off 3 1 on on on off 0 0 0 0 5 4 6 4 1 1 2 8 no no no no 8 8 8 8 W3 W2 W1 W1 2 -2 -2 2 25 22 24 22 7 15 6 6 12 12 13 11 7 6 6 4 11 12 15 1 VOF VOF VOF VOF 99 99 99 50 50 50 96 68 99 80 0 0 0 3 11 45 30 20 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 E Piano.18 52 FIFTH 0 TRIANGL 34 33 0 0 off 3 1 on on on off 0 0 0 0 6 6 6 4 0.5 1 1 14 no no no no 8 8 8 8 W1 W4 W1 W4 0 2 2 2 31 24 31 31 12 9 9 12 15 15 15 15 6 6 6 6 7 10 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 97 88 99 55 0 2 0 2 11 36 30 50 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 E Piano.19 53 SEVENTH 1 TRIANGL 34 33 0 0 off 3 1 on on on off 0 0 0 0 6 4 5 7 1 1 2 10 no no no no 8 8 8 8 W2 W1 W2 W1 0 2 2 2 31 24 31 31 12 9 9 12 15 15 15 15 6 6 6 6 7 10 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 81 95 57 0 2 0 2 11 36 30 50 0 POLY 0 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 E Piano.20 54 FIFTH 4 TRIANGL 24 0 4 0 on 6 0 off off off off 0 0 0 0 0 1 0 1 1 1 1 14 no no no no 8 8 8 8 W1 W2 W1 W2 -3 0 -3 0 31 31 31 31 8 8 8 8 0 0 0 0 17 0 0 0 7 7 10 7 VOF VOF VOF VOF 99 99 99 50 50 50 94 89 96 59 0 0 0 1 0 0 0 13 -12 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano.21 55 FIFTH 3 TRIANGL 27 0 4 0 off 5 0 off off off off 0 0 0 0 4 1 4 1 1 1 2 7 no no no no 8 8 8 8 W1 W2 W1 W1 -3 0 -3 0 31 31 31 31 8 8 17 18 1 0 0 0 21 0 0 0 8 7 15 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 99 70 1 1 0 1 0 45 0 13 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 E Piano.22 56 FOURTH 0 TRIANGL 16 0 8 25 off 5 2 on on on on 0 0 0 0 3 2 4 6 1 13 1 16.96 no no no no 8 8 8 8 W1 W1 W1 W3 3 -1 1 -3 29 29 29 31 12 11 4 31 14 14 14 15 4 18 1 17 8 6 6 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 42 69 67 2 2 1 2 20 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 E.Piano 1 57 THIRD 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 1 2 6 0.5 0.5 1 4.71 no no no no 8 8 8 8 W1 W1 W1 W2 0 0 0 0 24 31 31 31 31 31 10 19 15 15 12 4 9 0 8 10 9 7 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 96 73 49 73 0 0 0 0 6 11 12 0 12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 E.Piano 2 58 SECOND 3 TRIANGL 30 0 6 2 off 4 0 on on on on 0 0 0 0 2 3 5 2 1 1 8 7 no no no no 8 8 8 48 W1 W2 W1 W1 0 3 -3 1 25 28 27 22 7 14 19 29 5 15 15 12 4 3 1 18 5 3 1 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 57 66 2 2 2 0 10 20 77 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 E. Piano 3 59 FIFTH 0 TRIANGL 21 0 10 0 off 3 0 off off off off 0 0 0 0 2 3 2 3 1.5 15.5 1.51 1.5 no no no no 16 16 14 16 W1 W1 W1 W2 0 0 -3 3 31 31 31 31 7 15 30 8 11 11 14 14 7 10 6 6 10 11 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 99 80 1 1 1 1 0 0 0 20 -19 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 El.Piano * 60 THIRD 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 3 2 2 2 1 3 5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 2 3 28 31 31 31 30 4 5 8 15 14 13 13 6 2 4 3 6 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 66 68 2 0 0 1 0 99 99 0 -12 POLY 2 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Elec Grand 61 THIRD 0 TRIANGL 30 0 6 2 off 4 0 on on on on 0 0 0 0 2 3 5 2 1 1 8 11 no no no no 8 8 8 48 W1 W2 W1 W6 0 3 -3 1 25 28 27 22 7 31 19 13 5 15 15 0 4 3 1 18 6 5 2 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 59 37 2 2 3 0 13 20 99 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 ElecPiano4 62 FIFTH 7 TRIANGL 23 0 0 29 off 3 2 on on on on 0 0 0 0 2 0 2 1 2.99 1 1 15 no no no no 10 8 8 8 W1 W1 W1 W1 3 -3 0 3 30 15 27 31 7 6 7 15 0 0 7 7 0 0 10 0 8 5 8 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 99 71 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 5 ElecPiano5 63 FIFTH 7 TRIANGL 23 0 0 29 off 3 2 on on on on 0 0 0 0 2 0 2 1 0.98 12 0.97 15 no no no no 10 8 9 8 W1 W1 W1 W1 3 3 -3 -3 27 31 27 31 7 15 7 15 7 7 7 7 10 0 10 0 8 9 8 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 55 99 82 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 5 ElecPiano6 64 SEVENTH 6 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 1 2 1 1 3 3 no no no no 8 8 8 8 W1 W1 W8 W4 0 0 0 0 27 26 23 24 19 18 15 11 14 15 9 13 9 0 14 10 6 10 5 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 78 74 1 1 1 1 0 7 17 40 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 5 Electracou 65 FIFTH 0 TRIANGL 20 8 2 27 off 6 2 on off on off 0 0 0 0 3 3 4 4 1 1 1 14 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 3 -3 24 31 31 31 4 4 13 6 13 13 12 12 3 3 8 4 6 4 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 54 2 2 1 2 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 ElectroPno 66 THIRD 7 TRIANGL 31 8 32 2 off 0 0 off off off off 0 0 0 0 3 2 2 7 1 2.99 9 1 no no no no 8 10 8 8 W1 W1 W1 W1 3 3 2 -3 23 22 31 31 4 4 16 14 15 13 10 9 1 2 3 4 5 3 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 74 76 2 2 2 0 0 99 99 41 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 FadePiano 67 THIRD 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 2 4 2 1 1 4 9 1 no no no no 8 8 8 8 W1 W1 W4 W1 0 0 -3 2 26 22 24 29 6 7 5 1 11 11 0 0 3 3 3 4 6 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 97 82 71 72 2 2 3 3 0 76 65 26 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 FingersGo! 68 FIRST 0 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 0 2 4 7 1 2 3 5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 31 17 2 14 10 10 14 9 8 3 5 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 90 83 99 87 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 FlatFlange 69 THIRD 3 TRIANGL 27 50 2 0 on 6 0 off off off off 0 0 0 0 3 7 7 4 1 1 14 6.92 no no no yes 8 8 8 8 W7 W6 W5 W8 -3 3 2 3 31 31 31 31 7 11 9 17 13 15 10 0 4 0 4 11 10 11 9 15 VOF V12 VOF VOF 99 99 99 50 50 50 99 81 71 99 0 0 0 2 0 88 95 0 -12 POLY 4 FULL 0 POR 99 0 0 22 0 0 0 50 0 0 0 0 0 0 Fuzz Piano 70 SECOND 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 3 3 3 3 1 2 3 12 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 17 9 14 10 14 14 9 8 3 5 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 81 89 75 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 FuzzPiano 71 FOURTH 0 TRIANGL 29 0 3 0 off 5 0 off off off off 0 0 0 0 5 6 6 5 0.5 1.51 1.49 11.01 no no no yes 8 14 9 21 W1 W6 W6 W1 0 3 -3 0 31 31 31 31 31 9 4 21 15 12 12 11 10 4 9 31 6 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 71 83 1 2 2 0 0 15 15 0 0 POLY 2 FULL 0 POR 99 0 0 30 0 0 0 50 0 0 0 0 0 0 GaragePno 72 FIFTH 5 SHOLD 38 86 0 18 on 1 1 off off on off 0 0 0 0 6 5 3 3 14 12 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 1 3 0 -1 12 31 12 31 15 31 9 31 7 15 15 15 8 0 5 0 7 15 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 88 66 99 75 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 GlassPiano 73 THIRD 7 TRIANGL 31 0 4 0 off 4 0 off off off off 0 0 0 0 3 2 2 3 1 2.99 9 1 no no no no 8 10 8 8 W1 W1 W1 W1 3 3 2 -3 23 22 31 31 22 3 7 7 14 12 10 14 6 2 3 1 4 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 74 76 2 2 2 1 0 99 99 43 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Grand 2 74 THIRD 7 TRIANGL 31 0 4 0 off 4 0 off off off off 0 0 0 0 3 2 2 3 1 2.99 9 1 no no no no 8 10 8 8 W1 W1 W1 W1 3 3 2 -3 23 22 31 31 22 3 7 7 14 12 10 14 6 2 3 1 4 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 74 76 2 2 2 1 0 70 70 33 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Grand 3 75 THIRD 7 TRIANGL 31 0 4 0 off 4 0 off off off off 0 0 0 0 3 2 2 3 1 2.99 9 1 no no no no 8 10 8 8 W1 W1 W1 W1 3 3 2 -3 23 22 31 31 22 3 7 7 14 12 10 14 6 2 3 1 4 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 74 76 2 2 2 1 0 99 99 43 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Grand Low 76 THIRD 7 TRIANGL 31 0 4 0 off 4 0 off off off off 0 0 0 0 3 2 2 3 1 2.99 9 1 no no no no 8 10 8 8 W1 W1 W1 W1 3 3 2 -3 23 22 31 31 22 3 7 7 14 12 10 14 6 2 3 1 8 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 74 76 2 2 2 1 0 99 99 43 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Grand PF 77 THIRD 7 TRIANGL 31 0 4 0 off 4 0 off off off off 0 0 0 0 3 2 2 3 1 2.99 9 1 no no no no 8 10 8 8 W1 W1 W1 W1 3 3 2 -3 23 22 31 31 6 3 7 7 14 12 10 14 3 2 3 1 4 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 74 76 2 2 2 1 0 99 99 43 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 GrandLow2 78 THIRD 7 TRIANGL 31 0 4 0 off 4 0 off off off off 0 0 0 0 3 2 2 3 1.5 4.49 13.5 1.5 no no no no 16 11 16 16 W1 W1 W1 W1 3 3 2 -3 23 22 31 31 22 3 7 7 14 12 10 14 6 2 3 1 4 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 74 76 2 2 2 1 0 99 99 43 -19 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 GrandLow3 79 THIRD 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 1 4 1 1 1 4 7 1 no no no no 8 8 8 8 W1 W1 W2 W1 0 0 -3 2 31 31 24 29 8 7 5 4 13 12 0 0 3 3 3 12 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 74 77 2 1 3 1 0 94 61 52 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 GrandPiano 80 FIFTH 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 3 3 3 3 1 2 3 12 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 17 18 14 10 14 14 9 8 3 5 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 81 89 75 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 HappyPiano 81 THIRD 4 TRIANGL 32 0 0 0 on 4 0 off off off off 0 0 0 0 2 4 2 2 1 10 7 1 no no no no 8 8 8 8 W4 W6 W2 W7 0 0 0 0 31 31 20 31 30 0 31 3 15 15 11 0 0 12 31 25 7 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 61 67 2 1 3 2 0 37 33 19 0 POLY 2 FULL 0 POR 99 0 0 14 0 0 0 50 0 0 0 0 0 5 HarpsPiano 82 FIFTH 7 TRIANGL 28 29 16 0 off 6 0 off off off off 0 0 0 0 3 3 3 3 0.5 1 2 10 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 9 9 27 10 0 14 10 8 0 5 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 77 89 78 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Hi-LowBell 83 FIFTH 0 TRIANGL 28 20 6 84 off 6 1 off off off off 0 0 0 0 3 3 2 3 1 1 1 12 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 14 18 10 10 14 14 9 5 3 6 0 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 85 75 88 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 High&Long$ 84 FIRST 4 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 3 2 3 2 1.51 1.49 10.49 7.22 no no no no 14 9 19 10 W1 W1 W1 W5 -3 3 2 3 20 22 24 24 8 3 27 1 12 12 13 0 1 1 4 1 5 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 61 48 2 2 1 1 0 58 0 0 -7 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 HonkeyTonk 85 THIRD 0 TRIANGL 25 0 0 0 off 2 0 off off off off 0 0 0 0 3 5 2 2 1 3 5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 2 3 22 31 31 31 30 4 5 8 15 14 13 13 6 2 4 3 6 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 72 72 2 1 1 2 0 66 81 0 -12 POLY 2 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 5 Ivory * 86 THIRD 7 TRIANGL 31 0 4 0 off 4 0 off off off off 0 0 0 0 3 2 1 3 1 2.99 8 1 no no no no 8 10 8 8 W1 W1 W1 W1 3 3 2 -3 23 22 31 31 22 3 7 7 14 12 10 14 6 2 3 1 8 6 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 77 53 2 2 2 1 0 75 91 43 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 JazzyPiano 87 SECOND 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 3 3 3 3 1 2 3 5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 17 9 14 10 14 14 9 8 3 5 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 81 89 98 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 MetalWhip 88 FIFTH 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 4 2 3 3 1 1 1 15 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 15 18 14 14 14 14 9 6 5 6 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 96 41 1 1 0 0 0 78 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 NewElectro 89 FIFTH 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 3 2 4 6 1 1 1 12 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 14 18 14 10 14 14 9 8 3 6 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 85 78 75 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 NewElectro* 90 THIRD 6 TRIANGL 25 0 0 0 off 5 3 on off off on 0 0 0 0 3 4 5 1 1 2 0.78 1 no no no no 8 8 8 8 W1 W6 W3 W4 -2 0 0 2 26 25 31 25 31 10 19 18 15 12 3 10 5 10 0 5 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 96 69 86 84 1 2 0 0 0 37 0 37 0 POLY 2 FULL 0 POR 40 0 0 9 15 0 0 50 0 0 0 0 0 0 Old Rose 91 FIFTH 7 TRIANGL 28 29 16 0 off 6 0 off off off off 0 0 0 0 2 2 2 2 1 1 2 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 9 9 27 10 0 14 10 8 0 5 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 77 89 78 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrganPiano 92 FIFTH 5 TRIANGL 30 23 0 0 off 2 0 off off off off 0 0 0 0 5 2 5 2 24.22 4 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 -1 0 1 2 30 25 30 22 21 9 22 9 13 6 15 10 8 4 2 0 5 7 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 90 59 99 71 1 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Ornt Piano 93 SECOND 0 TRIANGL 28 8 4 0 off 3 0 off off off off 0 0 0 0 2 0 0 7 0.5 0.5 0.5 1 no no no no 8 8 8 8 W1 W1 W2 W1 2 -2 -3 2 19 19 29 29 5 6 8 8 3 10 10 6 7 7 7 7 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 87 67 1 1 0 0 0 0 0 87 12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 PetalPiano 94 FOURTH 6 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 4 3 0 1 1 1 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -3 3 19 21 22 29 29 3 15 23 10 14 15 10 1 0 0 2 3 7 2 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 86 71 3 2 2 2 12 53 79 82 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 PhaseGrand 95 THIRD 0 TRIANGL 32 0 0 0 off 6 0 off off off off 0 0 0 0 2 4 5 2 1 2.99 9 1 no no no no 8 10 8 8 W2 W1 W1 W1 0 0 3 0 30 30 31 31 5 6 12 18 9 9 5 11 3 2 3 8 5 4 5 2 VOF VOF VOF VOF 99 99 99 50 50 50 93 86 70 78 2 2 2 1 0 34 39 57 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 PianGuitar 96 THIRD 1 TRIANGL 31 0 4 0 off 4 0 off off off off 0 0 0 0 3 2 2 3 1.31 3.87 11.72 1.31 no no no no 13 20 13 13 W1 W1 W1 W1 3 3 2 -3 23 22 31 31 22 3 7 7 14 12 10 14 6 2 3 1 4 4 4 3 VOF VOF VOF VOF 99 99 99 51 50 50 99 76 74 76 2 2 2 1 25 99 99 43 -5 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Rag Piano 97 FOURTH 7 TRIANGL 23 0 0 17 off 5 3 off on off off 0 0 0 0 3 3 3 4 1 1 2 2.18 no no no no 8 8 8 11 W1 W1 W4 W1 0 3 -3 0 31 31 31 31 9 31 6 13 14 15 13 12 2 0 7 16 7 9 10 13 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 87 39 2 2 2 2 0 36 23 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Reed Piano 98 THIRD 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 1 4 1 1 1 4 7 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -3 2 26 22 24 29 7 1 4 1 13 12 0 0 3 3 3 4 6 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 97 78 68 72 2 2 3 1 0 99 86 26 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 SkoolPiano 99 FIFTH 5 TRIANGL 25 0 0 81 off 6 1 on on on off 0 0 0 0 5 5 4 5 1 14.1 1 21.2 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 0 0 21 27 22 21 9 25 6 6 0 11 13 13 4 3 4 2 7 8 12 7 VOF VOF VOF VOF 99 99 99 50 50 50 97 99 98 58 2 2 1 2 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SpacePiano 100 FIFTH 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 3 3 3 3 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -3 3 31 31 31 31 18 14 18 31 10 14 14 15 8 3 6 0 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 88 92 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SquareFlat 101 THIRD 7 TRIANGL 31 8 32 2 off 0 0 off off off off 0 0 0 0 3 2 2 7 1 2.99 9 1 no no no no 8 10 8 8 W1 W1 W1 W1 3 3 2 -3 23 22 31 31 4 4 16 14 15 13 10 9 0 1 2 3 5 3 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 74 76 2 2 2 0 0 99 99 41 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Sus Piano 102 THIRD 2 TRIANGL 29 19 0 0 on 5 0 off off off off 0 0 0 0 3 2 2 3 1 1 5 1.41 no no no no 8 8 8 8 W1 W2 W1 W1 -3 3 2 3 31 31 31 31 9 11 9 17 12 15 14 0 7 4 4 11 7 4 9 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 69 65 1 2 2 2 0 85 63 0 -12 POLY 2 FULL 0 POR 99 0 0 22 0 0 0 50 0 0 0 0 0 0 Thump Pno 103 FIFTH 2 TRIANGL 26 0 0 0 off 6 0 off off off off 0 0 0 0 1 2 5 2 2 7.67 5.5 3.43 no no no no 8 15 16 15 W1 W1 W1 W1 3 3 -3 -3 29 31 29 31 16 14 16 15 10 10 14 12 10 10 14 10 8 9 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 96 79 84 80 1 0 1 0 0 26 0 39 12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Toy Piano 104 FIFTH 2 TRIANGL 26 0 0 0 off 6 0 off off off off 0 0 0 0 1 2 5 2 2 7.67 5.5 3.43 no no no no 8 15 16 15 W1 W1 W1 W1 3 3 -3 -3 29 31 29 31 16 14 16 15 13 13 14 12 13 10 14 13 8 9 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 96 79 84 80 1 0 1 0 0 26 0 39 12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Toy Piano* 105 FOURTH 0 TRIANGL 25 25 60 0 off 4 0 off off off off 0 0 0 0 2 3 1 2 1.49 3 0.75 4.64 no no no yes 9 8 12 19 W1 W1 W2 W8 0 1 0 0 22 23 27 31 8 11 10 20 0 0 0 0 10 10 16 10 6 11 1 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 80 94 2 3 1 3 31 88 94 0 -19 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Upright * 106 THIRD 3 TRIANGL 30 0 6 2 off 4 0 on on on on 0 0 0 0 2 3 5 2 1 1 8 1.94 no no no yes 8 8 8 50 W1 W2 W1 W1 0 3 -3 1 25 28 27 22 7 14 19 29 5 15 15 12 4 3 1 18 6 5 2 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 57 66 2 2 3 0 10 20 99 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Upright 107 THIRD 7 TRIANGL 27 15 10 0 off 5 0 off off off off 0 0 0 0 3 3 2 2 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -3 -3 31 31 31 7 14 8 28 1 13 13 14 13 5 5 3 0 6 7 7 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 80 76 1 1 2 2 0 0 19 22 -12 POLY 4 FINGER 0 POR 99 0 0 75 0 18 0 50 0 0 0 0 0 0 UprightBas 108 THIRD 7 TRIANGL 35 0 0 0 off 1 0 off off off off 0 0 0 0 5 3 3 4 1 1 5 5 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 2 -2 19 19 19 19 2 2 2 2 12 12 12 12 1 1 1 1 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 74 65 3 2 3 3 0 69 72 72 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Uprt piano 109 THIRD 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 2 4 2 1 1 4 11 1 no no no no 8 8 8 8 W1 W1 W3 W1 0 0 -3 2 26 23 25 31 6 7 5 0 11 11 10 9 3 3 3 4 6 4 3 3 VOF VOF V24 V48 99 99 99 50 50 50 97 69 70 74 2 2 3 3 0 10 65 26 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 WiredPiano 110 FIFTH 6 TRIANGL 25 0 7 0 off 5 0 off off off off 0 0 0 0 3 4 4 4 0.5 1 0.5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -3 0 31 31 31 31 9 5 9 12 0 15 0 3 0 7 0 7 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 77 1 1 1 0 0 0 0 0 12 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Wood Piano 111 FOURTH 6 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 4 3 0 1 1 1 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -3 3 19 21 22 29 29 3 15 23 10 14 15 10 1 0 0 2 3 7 2 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 86 78 3 2 2 2 12 53 79 7 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 britegrand 112 FIFTH 2 TRIANGL 28 29 7 0 off 6 0 off off off off 0 0 0 0 3 3 3 3 1 1 3 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 1 3 17 10 0 15 10 8 0 5 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 90 82 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 callipiano 113 FIRST 2 TRIANGL 28 29 16 0 off 6 0 off off off off 0 0 0 0 4 4 4 4 1 1 0.5 6 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 1 5 17 10 0 14 10 8 0 5 15 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 90 82 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 flatfunk 114 FIFTH 7 TRIANGL 28 29 16 0 off 5 0 off off off off 0 0 0 0 3 3 3 3 1 1 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 9 9 5 10 0 14 10 8 0 5 0 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 77 92 89 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 fuzzbass 115 FIFTH 7 TRIANGL 28 29 6 0 off 6 0 off off off off 0 0 0 0 3 3 3 3 0.5 1 2 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 9 9 14 10 0 14 12 8 0 5 1 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 77 89 90 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 metlmarimb 116 FIRST 7 TRIANGL 30 9 4 0 off 6 0 off off off off 0 0 0 0 3 0 0 3 2 1 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 0 0 31 31 31 31 28 11 6 2 10 12 1 0 4 0 0 3 9 9 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 60 75 84 0 0 0 3 0 0 0 14 -12 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 mildfilter 117 FIFTH 7 TRIANGL 28 29 16 0 off 5 0 off off off off 0 0 0 0 0 7 1 7 1 1 2 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 9 9 7 10 0 14 10 8 0 5 0 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 77 89 98 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 phaseharp 118 FIFTH 7 TRIANGL 28 29 16 0 off 5 0 off off off off 0 0 0 0 3 3 3 3 1 1 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 19 18 9 9 16 10 0 14 10 8 0 5 0 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 77 89 84 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 pizzapiano 119 FIFTH 7 TRIANGL 28 29 16 0 off 5 0 off off off off 0 0 0 0 3 3 3 3 1 3 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 25 31 6 18 2 9 5 10 0 14 10 8 0 5 0 8 11 8 14 VOF VOF VOF VOF 99 99 99 50 50 50 98 87 89 82 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 stringback 120 Rhodes 4 FIRST 7 TRIANGL 20 0 0 16 off 6 1 on off off off 0 0 0 0 4 3 5 1 1 1 2 25.95 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 3 31 31 31 31 18 18 18 31 14 14 14 15 6 5 6 15 8 7 8 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 88 74 43 1 1 1 0 0 78 82 0 -12 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Rhodes...1 0 FIFTH 0 TRIANGL 34 33 0 0 off 0 0 off off off off 0 0 0 0 5 1 5 1 1 14 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 28 28 28 7 9 4 4 11 12 12 12 7 6 0 0 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 55 96 87 0 1 2 3 0 78 82 0 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Rhodes...2 1 FIFTH 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 5 3 5 2 1 16.96 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 28 28 28 31 21 4 4 11 12 12 12 7 12 0 0 4 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 99 95 71 1 1 0 0 0 78 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Rhodes...3 2 SEVENTH 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 4 3 5 3 2 16.96 2 2 no no no no 8 8 8 8 W2 W2 W1 W1 0 0 0 0 28 28 28 28 31 21 4 4 11 12 12 12 7 12 0 0 4 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 88 90 72 1 1 0 0 0 78 0 0 -12 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Rhodes...4 3 FIFTH 6 TRIANGL 19 0 0 36 off 6 1 on on on on 0 0 0 0 5 5 4 3 1 14 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 0 -3 3 28 28 28 28 7 9 6 9 11 10 12 5 7 5 6 6 7 11 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 95 82 1 1 1 1 0 78 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Rhodes...5 4 SEVENTH 4 TRIANGL 19 0 0 36 off 6 1 on on on on 0 0 0 0 5 3 5 3 1 12 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 15 13 6 3 13 13 13 13 9 10 8 3 7 6 7 2 VOF VOF VOF VOF 99 99 99 50 50 50 97 85 90 81 1 1 0 3 0 78 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Rhodes...6 5 SEVENTH 7 TRIANGL 20 10 0 57 on 0 1 on off off off 0 0 0 0 4 2 4 1 1 0.5 4 6 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 3 3 25 25 25 25 7 10 7 10 7 2 7 1 9 9 9 9 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 61 1 1 1 2 19 0 19 0 -12 POLY 2 FULL 0 POR 99 0 0 21 39 0 0 50 0 0 0 0 0 0 Rhodes...7 6 SEVENTH 7 TRIANGL 20 10 0 57 on 0 1 on off off off 0 0 0 0 5 3 5 2 1 12 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 3 3 28 28 28 28 15 15 6 9 11 11 13 13 9 10 6 0 7 5 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 91 76 0 2 0 3 19 92 19 0 -12 POLY 2 FULL 0 POR 99 0 0 21 39 0 0 50 0 0 0 0 0 0 Rhodes...8 7 SEVENTH 0 TRIANGL 25 33 0 44 off 0 1 on off off off 0 0 0 0 6 3 6 1 1 14 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 28 28 28 7 9 4 4 11 12 12 12 7 6 0 0 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 99 84 0 1 2 3 0 78 82 0 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Rhodes...9 8 EIGHTH 0 TRIANGL 25 33 0 44 off 0 1 on off off off 0 0 0 0 4 3 4 2 1 12 1 1 no no no no 8 8 8 8 W1 W2 W1 W3 0 0 0 0 28 28 28 28 7 9 4 4 11 12 12 12 7 6 0 0 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 99 91 0 3 2 3 0 78 82 0 0 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Rhodes..10 9 FIFTH 7 TRIANGL 34 33 0 0 off 0 0 off off off off 0 0 0 0 6 5 4 3 1 8 1 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 3 31 31 31 31 18 18 18 31 14 14 14 15 6 5 6 15 8 11 8 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 69 69 52 0 0 0 1 0 78 82 0 -12 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Rhodes..11 10 EIGHTH 6 TRIANGL 19 0 0 36 off 6 1 on on on on 0 0 0 0 6 5 4 4 1 12 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -3 3 26 26 26 26 31 21 4 4 11 12 12 12 7 12 0 0 4 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 98 99 89 1 1 0 0 0 78 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Rhodes..12 11 FOURTH 0 TRIANGL 19 0 0 36 off 6 1 on on on on 0 0 0 0 5 6 5 4 1 10 1 11 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 0 0 31 31 31 31 15 13 6 3 13 13 13 13 5 7 4 2 7 6 7 2 VOF VOF VOF VOF 99 99 99 50 50 50 95 69 54 61 0 2 0 3 0 50 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Rhodes..13 12 FIFTH 0 TRIANGL 20 22 7 12 off 4 2 off off on on 0 0 0 0 7 1 6 7 5.49 14.91 0.5 0.5 no yes no no 16 72 8 8 W5 W6 W1 W4 -3 0 0 3 31 31 31 31 21 24 25 9 13 12 15 10 10 2 4 8 5 6 5 8 VOF VOF VOF VOF 99 99 99 50 50 50 87 89 99 99 2 1 2 0 7 0 4 43 0 POLY 2 FULL 0 POR 99 0 0 50 51 47 39 50 0 0 0 0 0 0 RatRhodes 13 Synthesizer1 5 FIFTH 6 TRIANGL 42 0 0 0 on 3 0 off off off off 0 0 0 0 5 5 5 4 1 5 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 -1 1 2 0 6 6 18 5 6 3 1 6 11 13 14 10 9 12 6 7 6 6 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 96 68 99 82 0 1 0 1 0 0 0 35 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt.1 0 FIFTH 7 TRIANGL 42 0 0 0 on 3 0 off off off off 0 0 0 0 5 4 6 6 1 1 1 1 no no no no 8 8 8 8 W2 W2 W1 W3 0 3 0 0 31 15 31 31 18 3 31 2 14 14 15 4 1 3 0 7 9 8 15 12 VOF VOF VOF VOF 99 99 99 50 50 50 95 95 95 86 0 0 0 0 0 0 0 92 -24 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt.2 1 FIFTH 6 TRIANGL 42 0 0 0 on 3 0 off off off off 0 0 0 0 6 5 7 3 1 1 1 1 no no no no 8 8 8 8 W3 W2 W3 W2 0 3 0 0 31 15 31 31 18 3 31 2 14 14 15 4 1 3 0 7 9 8 15 12 VOF VOF VOF VOF 99 99 99 50 50 50 95 99 99 89 0 0 0 0 0 0 0 92 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt.3 2 FIFTH 7 TRIANGL 42 0 0 0 on 4 0 off off off off 0 0 0 0 6 5 5 4 1 2 1 2 no no no no 8 8 8 8 W2 W2 W1 W2 -3 -3 2 2 27 18 31 18 9 4 18 9 10 12 14 10 12 3 7 7 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 84 81 99 81 0 0 0 0 0 0 0 92 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt.4 3 FIFTH 6 TRIANGL 35 0 2 0 off 3 0 off off off off 0 0 0 0 6 6 5 4 1 3 1 2 no no no no 8 8 8 8 W2 W1 W3 W1 0 0 1 -1 30 30 31 27 9 4 9 15 14 9 14 13 7 6 7 15 10 12 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 96 77 97 82 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 AnlgSynt.5 4 FIFTH 5 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 4 6 5 1 1 1 1 no no no no 8 8 8 8 W2 W3 W3 W1 -2 2 0 0 31 31 31 31 9 15 31 2 9 15 15 4 1 3 0 7 9 8 15 12 VOF VOF VOF VOF 99 99 99 50 50 50 95 84 99 83 0 0 0 0 0 0 0 92 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt.6 5 FIFTH 5 TRIANGL 32 0 0 71 off 3 1 on off off off 0 0 0 0 6 5 5 5 1 1 1 1 no no no no 8 8 8 8 W2 W1 W3 W1 -3 3 -3 -3 31 31 24 31 21 10 7 6 14 7 5 5 6 6 4 3 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 82 99 93 1 0 0 0 0 0 0 92 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt.7 6 FIFTH 5 TRIANGL 32 0 0 0 off 3 0 off off off off 0 0 0 0 6 6 6 4 2 1 1 2 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 3 3 31 18 31 18 22 12 22 12 13 10 13 7 0 7 0 7 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 98 91 86 88 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt.8 7 FIFTH 5 TRIANGL 38 33 17 0 off 2 0 on off off off 0 0 0 0 7 4 5 3 1 1 1 2 no no no no 8 8 8 8 W2 W3 W2 W5 3 -1 3 3 31 15 31 15 21 12 21 12 13 12 13 12 5 9 5 9 5 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 98 89 86 90 1 0 1 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt.9 8 FIFTH 7 TRIANGL 38 33 32 0 off 5 0 on off off off 0 0 0 0 5 6 5 3 1 5 1 1 no no no no 8 8 8 8 W1 W1 W2 W1 3 -1 -3 -3 31 15 15 15 21 4 21 13 13 12 14 6 6 18 6 18 5 9 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 98 74 99 82 1 1 1 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt10 9 FIFTH 7 TRIANGL 38 28 32 0 off 4 0 off off off off 0 0 0 0 6 5 6 4 1 1 1 1 no no no no 8 8 8 8 W2 W5 W1 W1 3 -1 -3 -3 16 16 17 19 21 13 6 3 14 6 14 6 6 18 6 18 8 7 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 86 99 80 1 0 1 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt11 10 FIFTH 5 TRIANGL 38 33 32 0 off 2 0 off off off off 0 0 0 0 6 4 7 4 1 4 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 1 -1 -3 -3 25 7 16 16 4 4 5 3 15 13 14 6 3 4 6 18 7 7 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 86 99 87 0 0 1 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt12 11 SIXTH 5 TRIANGL 37 0 0 0 off 3 0 off off off off 0 0 0 0 6 4 4 3 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W2 W1 W2 W1 -1 -1 1 0 31 31 31 31 22 12 12 9 15 11 11 15 2 6 5 6 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 98 99 99 89 0 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt13 12 FIFTH 5 TRIANGL 28 0 5 0 off 2 0 off off off off 0 0 0 0 5 4 5 3 1 4 2 1 no no no no 8 8 8 8 W2 W2 W1 W1 2 2 -2 -2 31 31 31 31 6 6 7 6 9 9 7 7 0 0 0 0 4 4 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 84 99 88 1 1 1 1 0 19 11 27 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt14 13 FIFTH 5 TRIANGL 28 0 5 0 off 2 0 off off off off 0 0 0 0 6 5 4 3 0.5 1 1 0.5 no no no no 8 8 8 8 W2 W1 W1 W1 -2 -2 -2 -2 31 31 31 31 21 21 21 9 15 15 15 3 6 6 6 1 9 9 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 87 99 99 0 0 0 1 0 0 0 27 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AnlgSynt15 14 SIXTH 5 TRIANGL 33 0 56 0 on 2 0 off off off off 0 0 0 0 5 3 5 3 2 4 6 0.5 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 0 0 28 31 31 31 6 3 8 8 7 6 12 9 4 0 0 1 4 5 4 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 99 85 1 1 1 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Ariesynth1 15 THIRD 7 TRIANGL 33 0 56 0 on 2 0 off off off off 0 0 0 0 7 5 5 3 1 0.5 0.5 0.5 no no no no 8 8 8 8 W2 W2 W2 W1 0 0 0 0 28 31 31 31 6 3 8 8 7 6 12 9 4 0 0 1 5 5 4 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 85 83 1 2 1 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Ariesynth2 16 THIRD 7 TRIANGL 33 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 26 31 31 31 12 7 4 13 14 12 13 13 0 0 0 8 8 4 2 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 66 76 0 0 0 0 0 0 0 0 -12 MONO 2 FINGER 31 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 A.R.P. * 17 SIXTH 5 TRIANGL 20 0 54 0 on 2 0 off off off off 1 0 0 0 5 5 4 4 1 5 4 0.5 no no no no 8 8 8 8 W1 W3 W1 W1 1 -1 2 -2 16 31 31 29 31 13 13 22 15 0 0 14 0 16 16 0 11 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 63 58 98 0 2 2 0 0 14 12 10 0 POLY 4 FULL 1 POR 99 0 0 25 51 0 0 50 0 0 0 0 0 0 ARP 2500 18 SIXTH 5 TRIANGL 35 0 0 0 off 4 0 off off off off 7 4 3 0 5 5 5 5 2 3 3 1 no no no no 8 8 8 8 W1 W2 W2 W1 0 0 1 1 16 16 16 22 5 5 5 8 3 3 3 3 5 6 5 2 8 8 8 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 96 0 0 0 0 0 0 0 0 -12 POLY 5 FULL 1 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 ARP 2600.1 19 EIGHTH 7 TRIANGL 20 0 54 0 on 2 0 off off off off 0 0 0 0 5 5 5 3 1 5 6 1 no no no no 8 8 8 8 W2 W1 W1 W6 1 -1 2 -2 16 31 31 29 31 13 13 22 15 0 0 14 0 16 16 0 11 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 60 53 99 0 2 2 0 0 14 12 10 0 POLY 4 FULL 0 POR 99 0 0 25 51 0 0 50 0 0 0 0 0 0 ARP 2600.2 20 FIFTH 5 TRIANGL 34 0 11 0 on 6 0 off off off off 0 0 0 0 5 3 6 4 1 1 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W2 2 -2 2 -2 24 24 24 24 8 8 8 8 15 15 15 15 0 0 0 0 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 97 92 2 2 2 2 20 20 20 20 -12 MONO 7 FINGER 1 POR 99 0 0 99 99 50 0 50 0 0 0 0 0 0 ARP Axxe.1 21 FIFTH 4 TRIANGL 26 0 0 0 on 1 0 off off off off 0 0 0 0 6 4 5 5 1 2 1 2 no no no no 8 8 8 8 W1 W2 W4 W1 3 -2 3 0 15 21 24 25 4 18 7 5 13 13 13 9 3 3 5 2 7 4 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 89 0 0 0 0 0 0 0 0 -12 MONO 6 FINGER 2 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 ARP Axxe.2 22 SIXTH 6 TRIANGL 35 8 16 6 off 5 1 on on on on 0 0 0 0 6 5 6 4 2 4 8 2 no no no no 8 8 8 8 W2 W2 W1 W1 0 1 1 -1 11 13 12 31 31 7 31 19 15 6 15 14 6 14 0 0 8 6 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 78 0 0 0 0 10 14 82 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 ARP Omni.1 23 FIFTH 2 TRIANGL 34 5 12 0 on 5 0 off off off off 0 0 0 0 6 3 5 5 2 2 2 4 no no no no 8 8 8 8 W2 W1 W1 W1 -2 2 -3 3 10 11 10 11 31 3 31 3 15 4 15 4 0 30 1 30 5 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 91 75 87 80 0 1 1 0 37 35 21 86 -12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 ARP Omni.2 24 THIRD 7 TRIANGL 31 0 4 0 off 4 0 off off off off 0 0 0 0 2 2 1 0 1 2 9 1 no no no no 8 8 8 8 W7 W5 W1 W1 0 0 1 0 17 17 25 25 19 1 4 5 15 12 10 3 5 5 5 1 4 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 72 72 2 1 1 1 0 60 32 44 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Axel F 25 FIFTH 6 TRIANGL 38 33 32 71 off 1 0 off off off off 0 0 0 0 5 5 5 4 1 1 1 1 no no no no 8 8 8 8 W1 W2 W1 W1 3 -1 -3 -3 21 21 19 21 21 4 21 13 13 12 14 14 6 20 3 2 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 95 99 88 1 1 1 0 0 0 0 0 -12 POLY 0 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Axel F...1 26 FIFTH 5 TRIANGL 38 33 32 43 off 1 1 on off off off 0 0 0 0 6 4 6 3 1 1 1 1 no no no no 8 8 8 8 W2 W3 W4 W2 3 -1 -3 -3 21 21 21 24 21 4 21 21 13 12 14 14 6 20 3 2 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 95 99 90 1 1 1 1 0 0 0 0 -12 POLY 0 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Axel F...2 27 FIRST 0 SHOLD 99 0 23 99 off 7 3 off off off off 0 0 0 0 1 0 0 0 1 3 6 23.55 no no no yes 8 8 8 16 W5 W1 W1 W5 0 -3 3 -3 20 20 31 31 27 31 11 21 15 13 0 0 11 0 0 0 7 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 70 67 1 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 BadSample* 28 THIRD 7 TRIANGL 35 0 5 48 off 6 0 on on on on 0 0 0 0 5 6 6 5 2 0.5 8 2 no no no no 8 8 8 8 W2 W2 W1 W1 1 -1 1 -1 16 13 28 5 5 8 0 9 15 13 15 13 0 1 0 0 5 5 4 7 VOF VOF VOF VOF 99 99 99 50 50 50 84 78 47 84 1 1 0 1 0 0 0 32 -24 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Beta.....1 29 FOURTH 5 TRIANGL 35 0 5 48 off 6 0 on on on on 0 0 0 0 5 4 6 3 2 1 4 2 no no no no 8 8 8 8 W2 W2 W1 W1 1 -1 1 -1 16 13 28 9 4 3 0 9 7 7 15 13 0 0 0 0 5 5 4 7 VOF VOF VOF VOF 99 99 99 50 50 50 86 86 69 88 0 0 0 1 0 0 0 32 -24 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Beta.....2 30 THIRD 0 TRIANGL 26 22 3 0 off 5 0 off off off off 0 0 0 0 4 3 4 2 4 6 12 6 no no no no 8 8 8 8 W2 W4 W4 W3 0 1 -2 -3 31 31 31 31 16 25 5 0 14 12 0 0 8 8 0 14 7 10 2 5 VOF V24 V48 VOF 99 99 99 50 50 50 99 92 82 84 1 1 2 2 0 0 94 24 -24 POLY 2 FULL 0 POR 99 0 0 40 0 30 0 50 0 0 0 0 0 0 BigWash 31 FIFTH 4 TRIANGL 26 0 0 0 on 1 0 off off off off 0 0 0 0 5 6 3 3 1 4 1 14 no no no no 8 8 8 8 W1 W2 W1 W1 3 -2 3 0 15 21 24 31 4 22 25 15 13 13 13 9 3 3 7 31 7 4 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 68 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Chroma...1 32 FIFTH 5 TRIANGL 33 23 2 13 off 3 0 off off off off 0 0 0 0 4 3 6 2 1 5 1 2 no no no no 8 8 8 8 W2 W2 W1 W1 0 0 0 1 31 31 19 31 9 19 21 19 12 7 13 10 11 31 6 0 6 1 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 89 92 1 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Chroma...2 33 FIFTH 6 TRIANGL 35 0 0 0 off 1 0 off off off off 0 0 0 0 5 4 4 4 1 14 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 21 27 10 10 6 6 13 7 15 15 0 0 3 3 9 9 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 92 84 1 3 2 2 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Chroma...3 34 SIXTH 0 SHOLD 44 0 99 99 off 3 1 on on on on 0 0 0 0 5 4 5 3 1 4 1 3 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 -3 -3 15 16 16 16 3 9 9 9 15 15 15 15 1 4 4 4 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 72 1 1 1 1 0 0 0 0 12 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Crazy 6502 35 SECOND 7 TRIANGL 29 0 0 0 off 5 0 off off off off 0 0 0 0 2 2 1 2 1 1 10 3 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 3 -3 31 31 31 31 31 17 19 12 15 13 13 12 8 2 14 5 6 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 97 69 63 83 2 0 0 1 0 30 50 23 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 DigiAnnie 36 FIFTH 6 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 6 4 6 5 0.5 4 0.5 1 no no no no 8 8 8 8 W3 W4 W1 W1 0 0 3 3 18 23 25 13 31 31 31 31 15 15 15 15 0 1 0 0 1 2 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 88 3 0 2 3 20 10 8 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 50 0 50 0 0 0 0 0 5 <> DS 1 <> 37 FIFTH 6 TRIANGL 32 0 10 0 off 0 0 off off off off 0 0 0 0 7 4 4 4 5 1 0.5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 3 3 18 23 25 13 31 31 31 31 15 15 15 15 0 1 0 0 1 2 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 99 79 3 0 2 3 20 10 8 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 50 0 50 0 0 0 0 0 0 < DS 11 > 38 FIFTH 7 TRIANGL 25 0 12 0 on 4 0 off off off off 0 0 0 0 6 6 4 5 1 1 0.5 1 no no no no 8 8 8 8 W1 W1 W3 W1 0 -3 0 3 19 15 19 21 5 7 7 0 14 11 14 7 0 1 0 1 6 5 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 82 96 86 1 1 0 1 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 EML Synth1 39 FIFTH 7 TRIANGL 25 0 12 0 on 4 0 off off off off 0 0 0 0 5 2 5 3 2 2 0.5 1 no no no no 8 8 8 8 W1 W2 W1 W1 0 -3 0 3 19 15 19 21 5 7 7 0 14 11 14 7 0 1 0 1 6 5 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 91 96 84 1 2 0 1 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 EML Synth2 40 SEVENTH 7 TRIANGL 35 48 0 0 on 2 0 off off off off 0 0 0 0 6 3 7 2 2 1 1 0.5 no no no no 8 8 8 8 W1 W2 W1 W1 -2 2 -1 -2 15 15 21 21 0 0 9 9 13 13 13 13 3 3 7 7 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 90 1 0 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Explorer.1 41 EIGHTH 6 TRIANGL 23 0 11 0 on 2 0 off off off off 0 0 0 0 5 4 6 5 1 2 0.5 8 no no no no 8 8 8 8 W1 W2 W3 W1 0 -3 3 -2 19 15 21 21 0 0 9 9 13 13 13 13 3 3 7 7 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 65 99 1 0 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Explorer.2 42 SEVENTH 7 TRIANGL 23 0 11 0 on 2 0 off off off off 0 0 0 0 6 4 4 2 4 2 1 2 no no no no 8 8 8 8 W2 W1 W1 W1 -3 -1 2 1 15 15 21 21 0 0 9 9 13 13 13 13 4 4 7 7 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 81 1 1 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Explorer.3 43 FIFTH 0 TRIANGL 33 21 3 5 on 5 3 on on on on 0 0 0 0 3 4 3 4 1.5 3 1.5 3 no no no no 16 8 16 8 W3 W5 W3 W5 0 0 -2 -2 17 17 17 15 5 12 5 12 15 13 15 13 0 0 0 0 9 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 80 1 0 0 1 0 32 0 32 -7 POLY 4 FULL 0 POR 99 0 0 30 85 0 0 50 0 0 0 0 0 0 FMMelodica 44 THIRD 6 TRIANGL 33 29 14 26 on 5 1 off on off off 0 0 0 0 6 4 4 3 2 2 4 1 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 3 16 8 6 9 6 31 31 6 14 15 15 14 0 0 0 0 5 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 95 71 60 92 1 2 0 0 0 0 78 20 -12 POLY 8 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Gamma....1 45 THIRD 5 TRIANGL 33 29 14 26 on 5 1 off on off off 0 0 0 0 6 5 5 5 1 2 5 2 no no no no 8 8 8 8 W1 W2 W1 W3 0 0 0 3 16 12 10 9 6 31 31 6 14 15 15 14 0 0 0 0 5 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 95 59 76 82 1 2 0 0 0 0 78 20 -12 POLY 8 FULL 1 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Gamma....2 46 FIFTH 5 TRIANGL 38 33 8 71 off 2 1 on off off off 0 0 0 0 5 4 5 3 8 8 2 2 no no no no 8 8 8 8 W1 W1 W2 W2 -3 -3 3 1 11 12 21 13 21 4 4 3 15 12 14 14 12 7 2 9 6 6 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 94 66 99 82 1 1 0 2 0 0 0 10 -24 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 GleeSynth 47 THIRD 6 TRIANGL 29 8 0 0 on 5 0 off off off off 0 0 0 0 1 1 2 0 1.5 3 10.48 1.49 no no no no 16 8 9 9 W5 W6 W8 W6 0 -1 -2 3 21 27 24 17 10 5 7 0 15 14 14 12 0 0 0 0 11 10 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 67 68 1 1 1 0 0 0 59 0 -19 POLY 4 FULL 1 POR 99 0 0 65 0 0 0 50 0 0 0 0 0 0 HardSync 48 SIXTH 6 TRIANGL 34 0 20 0 on 3 0 off off off off 0 0 0 0 7 4 5 4 0.5 1 4 2 no no no no 8 8 8 8 W1 W2 W1 W1 0 -1 1 0 8 8 7 8 31 14 4 31 15 1 7 15 1 0 12 0 5 5 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 98 99 97 88 2 0 1 0 0 0 60 99 -12 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Helix....1 49 FIFTH 5 TRIANGL 34 0 20 0 on 3 0 off off off off 0 0 0 0 6 6 5 5 1 0.5 4 2 no no no no 8 8 8 8 W1 W3 W2 W1 0 -1 1 0 8 8 7 8 31 7 4 31 15 8 7 15 1 0 12 0 5 5 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 97 95 2 0 1 0 0 0 60 99 -12 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Helix....2 50 FIFTH 5 TRIANGL 27 0 60 99 off 2 1 off off on on 0 0 0 0 6 3 5 2 1 2 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 16 15 15 24 0 0 0 23 13 13 13 14 3 3 3 27 5 5 5 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 46 99 57 1 1 1 1 0 0 0 0 0 POLY 2 FULL 1 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 \ ISAO \ 51 FIFTH 7 TRIANGL 33 0 56 0 on 2 0 off off off off 0 0 0 0 6 4 5 4 1 0.5 1 3 no no no no 8 8 8 8 W2 W2 W1 W1 0 0 0 0 24 24 20 23 8 9 8 5 7 11 12 8 4 0 0 1 4 5 3 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 96 99 81 2 2 3 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 JP 4.....1 52 FIFTH 7 TRIANGL 37 73 15 0 off 3 0 off off off off 0 0 0 0 7 5 7 3 0.5 4 1 1 no no no no 8 8 8 8 W1 W1 W3 W1 2 0 -3 3 22 22 22 22 21 10 9 8 13 13 15 15 1 10 1 3 6 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 89 84 99 84 0 0 0 0 0 0 0 0 -12 POLY 12 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 JP 4.....2 53 FIFTH 7 TRIANGL 32 0 0 0 off 5 0 off off off off 0 0 0 0 2 1 2 2 1.49 1.49 1.51 1.51 no no no no 9 9 14 14 W2 W4 W6 W1 3 -2 -2 -1 22 31 21 6 14 14 14 10 15 15 15 15 0 0 0 5 7 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 78 0 0 0 0 0 0 0 0 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 Juno Juice 54 FIFTH 7 TRIANGL 32 0 0 0 off 5 0 off off off off 0 0 0 0 2 1 2 1 1.49 1.49 1.51 1.51 no no no no 9 9 14 14 W2 W4 W6 W3 3 -2 -2 -1 22 31 21 31 14 14 14 10 15 15 15 15 0 0 0 0 7 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 74 0 0 0 0 0 0 0 0 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 Juno Who? 55 SECOND 0 TRIANGL 30 12 15 5 off 5 1 on off off off 0 0 0 0 0 0 0 0 4 4 7.94 11.93 no no no no 8 8 9 23 W1 W1 W1 W1 1 2 3 -1 12 31 31 31 10 0 0 0 14 15 15 15 0 0 0 0 5 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 65 61 1 0 0 0 0 25 59 53 -24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Jupiter * 56 FIFTH 7 TRIANGL 41 0 5 0 on 2 0 off off off off 0 0 0 0 6 1 6 2 1 0.5 1 0.5 no no no no 8 8 8 8 W4 W4 W4 W1 0 3 3 3 24 23 20 27 31 12 31 31 15 15 15 15 0 6 4 4 13 9 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 86 90 99 79 0 2 0 0 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 \ Kitaro.1 57 FIFTH 4 TRIANGL 31 28 20 0 on 3 0 off off off off 7 3 3 7 6 5 5 4 1 1 1 3 no no no no 8 8 8 8 W1 W1 W1 W1 1 -1 1 -1 31 8 31 31 15 4 15 15 15 15 15 15 9 3 9 9 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 82 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 \ Kitaro.2 58 FOURTH 4 TRIANGL 33 29 14 26 on 5 1 off on off off 0 0 0 0 6 5 5 4 2 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 1 2 10 10 10 15 31 31 31 31 15 15 15 15 2 2 2 7 5 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 96 67 85 53 3 0 1 0 0 0 78 71 -12 POLY 8 FULL 10 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Korg 770.1 59 SIXTH 7 TRIANGL 33 29 14 26 on 5 1 off on off off 0 0 0 0 4 6 5 3 2 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 1 2 15 15 15 15 31 31 31 31 15 15 15 15 0 0 0 7 8 8 8 2 VOF VOF VOF VOF 99 99 99 50 50 50 96 98 99 72 0 0 0 0 0 0 0 71 -12 MONO 6 FINGER 2 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Korg 770.2 60 FOURTH 7 TRIANGL 30 5 10 0 off 6 0 off off off off 0 0 0 0 7 5 5 3 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 18 18 13 31 31 31 10 15 15 15 14 0 0 0 7 10 10 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 95 57 56 79 0 0 0 0 1 0 75 77 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Korg 770.3 61 FIFTH 0 TRIANGL 35 47 25 0 on 3 0 off off off off 0 0 0 0 6 7 6 4 1 6 2 2 no no no no 8 8 8 8 W1 W2 W3 W1 -3 1 3 -2 31 31 9 11 17 14 6 6 15 9 15 15 8 5 0 4 5 5 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 97 74 95 65 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Lambda...1 62 FIFTH 6 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 4 6 3 0.5 1 1 5 no no no no 8 8 8 8 W1 W2 W2 W1 0 0 0 0 31 31 11 11 4 11 12 12 15 12 13 13 9 10 9 9 8 8 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 95 84 95 69 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Lambda...2 63 FIFTH 0 TRIANGL 35 47 25 0 on 3 0 off off off off 0 0 0 0 5 4 5 5 1 6 4 4 no no no no 8 8 8 8 W1 W2 W3 W5 -1 3 0 -3 31 31 7 7 20 20 16 16 15 9 15 15 8 5 3 3 5 5 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 92 71 0 2 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Lambda...3 64 FIFTH 0 TRIANGL 35 47 25 0 on 3 0 off off off off 0 0 0 0 3 3 6 3 2 4 2 2 no no no no 8 8 8 8 W1 W2 W3 W1 -1 3 0 -3 31 31 7 7 20 20 16 16 15 9 15 15 8 5 3 3 4 4 14 13 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 92 76 0 2 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Lambda...4 65 FIFTH 4 TRIANGL 33 35 15 0 off 1 0 off off off off 0 0 0 0 6 4 7 3 1 1 2 2 no no no no 8 8 8 8 W2 W2 W3 W3 0 3 0 -3 24 24 5 5 6 6 16 3 9 9 15 8 9 9 3 3 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 85 0 0 0 0 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 Lambda...5 66 FIFTH 6 TRIANGL 35 21 5 99 off 2 1 off off off off 0 0 0 0 5 4 6 3 3 3 3 2 no no no no 8 8 8 8 W1 W4 W1 W1 0 0 0 0 15 7 31 31 13 13 3 31 15 15 12 15 5 4 3 0 15 10 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 98 84 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 LeadSynth1 67 FIFTH 5 TRIANGL 35 21 5 99 off 2 1 off off off off 0 0 0 0 5 4 6 4 1 1 2 13 no no no no 8 8 8 8 W1 W6 W3 W1 0 0 0 0 24 27 31 18 9 10 3 21 14 14 12 14 6 9 10 6 10 15 9 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 98 75 0 1 2 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 LeadSynth2 68 FIFTH 5 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 4 6 3 1 0.5 1 1 no no no no 8 8 8 8 W2 W2 W1 W1 2 1 0 3 31 31 31 31 31 31 4 3 15 15 15 15 0 10 0 7 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 95 69 0 1 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 LeadSynth3 69 FIFTH 5 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 4 5 3 1 0.5 1 1 no no no no 8 8 8 8 W1 W2 W1 W1 2 1 0 3 22 28 31 31 31 31 4 3 15 15 15 15 0 3 0 7 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 95 90 0 1 0 0 0 44 0 46 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 LeadSynth4 70 FIFTH 5 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 4 6 3 4 1 1 11 no no no no 8 8 8 8 W2 W1 W1 W1 -1 1 0 3 24 27 24 22 9 9 9 21 14 13 14 14 6 9 4 6 10 15 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 94 99 99 74 2 1 0 0 1 58 0 42 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 LeadSynth5 71 FIFTH 5 TRIANGL 35 0 0 0 on 4 0 off off off off 0 0 0 0 5 5 5 5 1 1 1 1 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 0 0 21 24 21 25 6 16 31 9 12 11 0 7 6 6 28 3 10 7 15 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 98 0 1 0 0 1 58 0 42 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 LeadSynth6 72 FIFTH 3 TRIANGL 35 0 0 0 on 4 0 off off off off 0 0 0 0 5 6 5 3 1 1 1 4 no no no no 8 8 8 8 W2 W4 W1 W1 0 0 2 -1 31 24 24 24 6 11 31 15 12 11 15 9 6 9 0 7 10 7 15 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 87 0 0 0 0 1 50 0 66 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 LeadSynth7 73 SIXTH 5 TRIANGL 37 0 0 0 on 3 0 off off off off 0 0 0 0 6 5 4 3 1 1 1 2 no no no no 8 8 8 8 W7 W3 W1 W3 0 0 0 3 31 31 31 30 22 22 22 7 14 14 14 15 0 0 0 5 8 8 8 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 86 0 0 0 0 0 0 0 66 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 LeadSynth8 74 FIFTH 6 TRIANGL 34 14 19 8 off 5 1 off on off on 0 0 0 0 6 5 5 4 2 1 1 1 no no no no 8 8 8 8 W1 W2 W2 W2 3 3 -3 -3 12 5 12 5 31 7 31 7 15 14 15 14 0 0 0 0 7 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 99 76 0 0 0 0 10 14 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Luna.....1 75 FIFTH 5 TRIANGL 34 0 15 0 off 4 0 off off off off 0 0 0 0 6 4 5 4 0.5 6 1 0.5 no no no no 8 8 8 8 W1 W2 W1 W2 3 3 -3 -3 12 12 12 8 31 3 31 7 15 8 15 14 0 0 0 0 7 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 94 64 96 81 0 0 0 0 10 14 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Luna.....2 76 FIFTH 6 TRIANGL 34 14 19 8 off 5 1 off on off on 0 0 0 0 6 5 5 4 3 3 2 2 no no no no 8 8 8 8 W1 W2 W1 W1 3 3 -3 -3 12 12 5 5 31 7 31 7 15 14 15 14 0 0 0 0 7 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 75 82 99 75 0 0 0 0 10 14 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Luna.....3 77 FIRST 6 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 2 1 1 1 1 1 2 3 no no no no 8 8 8 8 W1 W1 W1 W2 0 -1 0 1 16 31 31 16 31 4 11 6 15 15 15 14 0 0 0 0 10 5 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 53 80 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Lyrisyn 78 SIXTH 6 TRIANGL 34 0 20 0 on 3 0 off off off off 0 0 0 0 0 0 0 0 1 0.5 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 1 3 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 99 97 83 0 0 0 0 0 0 60 99 -12 MONO 6 FINGER 1 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 MaxiKorg.1 79 FIFTH 6 TRIANGL 30 0 14 53 on 5 1 on off off off 0 0 0 0 5 6 4 3 2 1 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 1 3 21 23 31 31 31 31 31 31 15 15 15 15 0 0 0 0 8 7 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 98 75 97 84 0 0 0 0 0 0 0 0 0 POLY 6 FINGER 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 MaxiKorg.2 80 SECOND 4 TRIANGL 36 0 4 0 on 4 0 off off off off 0 0 0 0 6 5 6 5 2 1 2 3 no no no no 8 8 8 8 W1 W1 W1 W1 2 1 -2 2 23 20 21 20 5 5 4 3 2 15 13 13 0 1 0 1 10 5 10 7 VOF VOF VOF VOF 99 99 99 50 50 50 90 86 80 75 0 1 1 1 39 0 17 39 -12 MONO 2 FINGER 17 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 MicroMoog1 81 FIRST 7 TRIANGL 36 0 4 0 on 4 0 off off off off 0 0 0 0 7 4 5 2 2 0.5 2 0.5 no no no no 8 8 8 8 W3 W2 W1 W1 0 0 0 0 23 20 21 20 2 2 1 1 2 15 13 13 0 1 0 1 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 88 77 0 0 0 0 39 0 17 39 -12 MONO 2 FULL 17 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 MicroMoog2 82 THIRD 7 TRIANGL 33 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 1.5 1.5 1.5 1.5 no no no no 16 16 16 16 W1 W1 W1 W1 0 -3 0 3 15 15 11 14 12 13 4 9 14 11 13 13 0 0 0 0 7 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 69 68 77 0 0 0 0 0 0 0 0 -7 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 MiniMoog * 83 FIFTH 5 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 2 6 1 1 1 1 0.5 no no no no 8 8 8 8 W2 W1 W2 W1 0 1 1 -1 28 31 31 27 16 10 9 27 14 14 15 15 4 3 4 6 12 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 98 92 1 1 2 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 MiniMoog.1 84 FIFTH 6 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 4 7 6 1 2 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 1 3 0 1 31 28 28 27 9 27 16 10 15 15 15 14 9 6 4 3 15 15 12 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 98 88 2 1 1 2 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 MiniMoog.2 85 FIFTH 5 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 5 5 3 1 0.5 1 2 no no no no 8 8 8 8 W2 W2 W1 W3 1 3 0 1 28 27 28 27 16 10 16 10 15 7 15 14 4 3 4 3 12 15 12 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 96 98 94 1 0 1 2 0 0 0 0 -12 POLY 2 FULL 4 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 MiniMoog.3 86 FOURTH 0 TRIANGL 30 0 10 0 off 3 0 off off off off 0 0 0 0 2 0 7 1 2 2 6 2 no no no no 8 8 8 8 W1 W8 W4 W3 0 -3 3 0 16 31 17 19 10 0 8 8 15 15 15 15 8 0 3 0 6 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 91 69 1 0 2 1 0 48 38 69 -24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Mirage * 87 THIRD 7 TRIANGL 33 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 1 1.51 1 1.49 no no no no 8 14 8 9 W1 W1 W1 W2 0 -3 3 3 23 31 31 31 12 24 4 9 14 12 13 12 0 0 0 0 8 4 2 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 72 78 0 0 0 0 25 15 35 0 -12 MONO 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 MoogBass * 88 FOURTH 6 TRIANGL 34 0 15 0 off 4 0 off off off off 0 0 0 0 6 4 7 4 0.5 6 1 0.5 no no no no 8 8 8 8 W2 W3 W1 W1 3 3 -3 -3 31 31 31 26 31 3 31 7 15 8 15 14 0 0 0 0 7 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 94 66 86 86 0 0 0 0 10 14 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 MultiMoog1 89 THIRD 6 TRIANGL 34 14 19 8 off 5 1 off on off on 0 0 0 0 6 6 4 5 3 3 2 2 no no no no 8 8 8 8 W3 W4 W1 W1 3 3 -3 -3 31 31 31 31 31 7 31 7 15 14 15 14 0 0 0 0 7 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 68 84 83 0 0 0 0 10 14 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 MultiMoog2 90 FIFTH 7 TRIANGL 32 0 0 0 off 5 0 off off off off 0 0 0 0 2 1 2 0 1.49 1.49 1.51 1.51 no no no no 9 9 14 14 W2 W4 W5 W6 3 -2 1 -1 19 15 29 22 14 14 14 10 15 15 15 15 0 0 0 0 7 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 99 68 0 0 0 0 0 66 0 41 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 No!NotJump 91 THIRD 5 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 6 4 1 1 2 0.5 no no no no 8 8 8 8 W3 W1 W1 W4 0 0 3 3 18 23 25 13 31 31 31 31 15 15 15 15 0 1 0 0 1 2 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 91 69 88 86 3 0 2 3 20 10 8 0 -12 MONO 7 FULL 1 POR 99 0 0 99 0 50 0 50 0 0 0 0 0 0 <> OB 1 <> 92 FIFTH 7 TRIANGL 35 0 0 0 off 5 2 off off off on 0 0 0 0 0 0 0 0 1.5 1.5 1.51 1.51 no no no no 16 16 14 14 W1 W1 W1 W1 0 0 3 3 12 18 17 19 31 8 31 8 15 14 15 14 0 0 0 0 7 3 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 99 73 1 0 1 0 0 0 10 12 -19 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 99 0 0 0 0 0 OB-8 * 93 FIFTH 5 TRIANGL 0 0 19 0 off 4 0 off off off off 0 0 0 0 5 4 5 4 1 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W3 W1 W1 0 -1 1 3 31 29 31 31 12 12 12 0 14 14 14 15 4 4 4 0 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 94 99 92 0 0 0 2 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Oberheim.1 94 FIFTH 5 TRIANGL 35 0 19 0 off 4 0 off off off off 0 0 0 0 5 4 5 3 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 0 3 17 18 11 11 11 9 12 2 15 15 14 14 3 4 4 4 6 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 99 88 0 0 0 0 0 0 0 5 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Oberheim.2 95 FIFTH 7 TRIANGL 23 0 32 0 off 4 0 off off off off 0 0 0 0 5 4 5 3 0.5 0.5 1 0.5 no no no no 8 8 8 8 W1 W2 W1 W1 3 3 3 -3 10 12 10 13 31 12 12 12 15 14 14 14 0 4 4 4 5 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 99 87 0 0 0 0 0 5 0 5 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Oberheim.3 96 Synthesizer2 6 FIFTH 5 TRIANGL 30 0 10 0 on 3 0 off off off off 0 0 0 0 5 6 4 4 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W2 W3 W2 W1 0 1 -1 2 25 25 31 22 6 3 22 31 6 0 13 15 6 1 3 4 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 99 92 1 0 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Odyssey..1 0 FIFTH 5 TRIANGL 0 0 0 0 off 3 0 off off off off 7 3 3 7 6 5 5 3 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W2 W1 W2 0 0 -3 -3 31 27 31 31 4 19 5 2 15 10 15 15 3 1 6 1 12 13 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 91 1 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Odyssey..2 1 FIFTH 5 TRIANGL 29 0 0 0 off 4 0 off off off off 7 3 3 7 5 5 5 5 0.5 0.5 0.5 3 no no no no 8 8 8 8 W2 W3 W3 W3 1 2 0 2 31 31 27 27 0 9 2 5 15 0 14 14 0 0 4 4 7 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 93 99 86 1 0 1 1 0 0 30 30 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Odyssey..3 2 FIFTH 3 TRIANGL 37 0 75 0 on 1 0 off off off off 0 0 0 0 5 5 7 4 2 0.5 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -1 0 18 16 18 16 13 31 13 31 14 15 14 15 9 0 9 0 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 98 85 1 1 1 1 21 0 21 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 OhB1Kanobe 3 THIRD 7 TRIANGL 33 0 56 0 on 2 0 off off off off 0 0 0 0 6 4 6 3 1 2 5 5 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 0 0 28 31 31 8 6 5 8 9 7 11 12 8 4 0 0 16 4 5 3 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 71 82 2 2 3 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Omega....1 4 SIXTH 2 TRIANGL 70 40 10 26 off 5 1 off on off off 0 0 0 0 6 6 5 4 1 2 4 2 no no no no 8 8 8 8 W2 W1 W1 W1 3 -2 -1 1 31 31 24 31 31 31 4 3 15 15 10 9 0 13 28 0 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 98 99 99 90 0 0 0 1 0 57 99 55 -12 POLY 7 FULL 0 POR 99 0 0 25 0 50 0 50 0 0 0 0 0 0 Omega....2 5 FOURTH 0 TRIANGL 35 0 0 0 off 5 0 off off off off 0 0 0 0 2 0 7 0 1 0.5 9 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 -3 -3 28 13 11 14 20 31 6 7 14 15 14 13 8 0 3 0 13 6 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 70 92 0 2 2 1 0 22 14 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 PCM Duck * 6 FOURTH 7 TRIANGL 35 15 14 0 on 4 0 off off off off 0 0 0 1 5 4 5 2 2 1 3 1 no no no no 8 8 8 8 W2 W1 W1 W3 2 0 0 -1 20 18 3 6 5 31 6 2 3 15 3 12 18 0 22 7 3 3 3 4 VOF VOF VOF VOF 99 99 99 50 50 50 94 97 90 70 1 2 2 1 0 99 99 31 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 PianoSynth 7 SIXTH 7 TRIANGL 31 14 0 0 off 4 1 off off off off 0 0 0 0 4 4 3 6 1.5 1.51 1.49 3 no no no no 120 14 9 64 W5 W6 W4 W4 0 -3 3 3 26 16 11 31 31 31 31 20 15 15 15 6 0 2 0 2 5 5 8 1 VOF VOF VOF VOF 99 99 99 50 50 50 96 99 99 81 1 1 1 1 0 0 0 0 -7 POLY 2 FULL 0 POR 40 0 0 50 0 18 0 50 0 0 0 0 0 0 PlkAnalog 8 SIXTH 6 TRIANGL 31 11 17 27 off 2 1 on off off off 0 0 0 0 5 5 5 4 2 2 2 2 no no no no 8 8 8 8 W2 W2 W5 W1 0 0 -2 0 18 31 18 19 18 3 16 7 14 0 14 7 12 12 12 3 6 6 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 95 96 97 90 1 1 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Poly 61 9 FIFTH 6 TRIANGL 34 14 19 8 off 5 1 off on off on 0 0 0 0 6 4 4 4 2 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 -3 -3 12 8 14 10 31 7 31 7 15 14 15 14 0 0 0 0 7 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 86 0 0 0 0 10 14 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 PolyFusion 10 FIFTH 5 TRIANGL 31 11 17 27 off 2 1 on on off off 0 0 0 0 5 4 6 5 2 8 4 2 no no no no 8 8 8 8 W2 W3 W1 W3 0 0 -2 0 18 31 16 31 3 10 3 3 15 0 14 1 1 12 3 2 6 6 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 89 1 2 0 1 0 0 0 10 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 PolyMoog.1 11 SIXTH 6 TRIANGL 34 0 20 0 on 3 0 off off off off 0 0 0 0 5 4 6 3 1 2 4 2 no no no no 8 8 8 8 W2 W1 W2 W3 -1 1 -1 3 14 14 10 12 31 31 4 31 15 15 7 15 1 1 12 0 5 5 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 89 1 1 2 1 0 0 60 99 -24 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 PolyMoog.2 12 THIRD 0 TRIANGL 41 0 31 0 off 3 0 off off off off 0 0 0 0 2 0 0 3 1 0.5 25.02 2.48 no no no no 8 8 23 15 W1 W1 W4 W1 0 2 3 -1 31 18 29 31 10 4 16 10 15 13 13 15 0 2 6 5 7 4 1 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 47 76 0 0 0 0 9 8 12 34 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 P.P.G * 13 FIFTH 6 TRIANGL 31 0 6 0 off 3 0 off off off off 0 0 0 0 0 0 0 0 1.49 1.51 1.51 1.51 no no no no 9 14 14 14 W1 W1 W1 W1 -3 2 3 -3 28 31 27 18 22 11 22 10 13 10 13 11 0 0 0 0 6 1 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 99 79 0 0 0 0 0 0 0 0 -19 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Prophet * 14 FOURTH 5 TRIANGL 34 33 0 0 off 3 0 off off off off 0 0 0 0 6 4 5 4 1 1 7 1 no no no no 8 8 8 8 W2 W2 W1 W1 0 3 0 -3 23 19 22 19 7 7 7 3 12 12 12 6 4 4 4 4 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 34 75 0 0 0 0 0 0 0 0 0 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Prophet..1 15 FIFTH 6 TRIANGL 35 0 18 0 on 1 0 off off off off 0 0 0 0 7 6 5 4 1 4 1 1 no no no no 8 8 8 8 W1 W1 W1 W2 2 0 0 -3 27 27 27 27 3 12 6 3 13 15 13 15 3 15 4 2 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 78 2 0 1 0 0 25 0 0 0 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Prophet..2 16 FIFTH 7 TRIANGL 38 29 35 0 off 2 0 off off off off 0 0 0 0 6 6 6 5 1 1 1 1 no no no no 8 8 8 8 W3 W3 W1 W1 2 0 0 -3 16 14 16 16 3 4 3 4 14 12 14 12 4 18 6 18 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 99 79 1 0 1 0 0 25 0 25 0 POLY 4 FINGER 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Prophet..3 17 FOURTH 4 TRIANGL 33 29 14 26 on 5 1 off on off off 0 0 0 0 5 4 6 4 2 2 0.5 4 no no no no 8 8 8 8 W2 W3 W3 W1 0 -1 1 2 7 8 7 8 31 31 31 31 15 15 15 15 2 2 2 7 5 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 95 83 74 84 3 0 1 0 0 0 78 71 -12 POLY 8 FULL 10 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 PS 3100..1 18 SECOND 4 TRIANGL 33 29 14 26 on 5 1 off on off off 0 0 0 0 5 4 6 4 1 1 4 3 no no no no 8 8 8 8 W2 W2 W1 W1 -2 1 -1 3 7 7 13 13 31 31 31 31 15 15 15 15 2 2 2 7 5 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 98 78 72 83 3 0 1 0 0 0 78 71 -12 POLY 8 FULL 15 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 PS 3100..2 19 FOURTH 4 TRIANGL 33 29 14 26 on 5 1 off on off off 0 0 0 0 6 4 6 3 2 2 0.5 2 no no no no 8 8 8 8 W1 W3 W1 W1 0 -1 1 2 12 12 11 13 6 8 7 7 15 15 15 15 2 1 1 1 5 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 95 80 79 84 1 0 1 0 0 0 78 71 -12 POLY 8 FULL 10 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 PS 3300..1 20 SECOND 4 TRIANGL 33 29 14 26 on 5 1 off on off off 0 0 0 0 5 3 4 3 0.5 1 2 2 no no no no 8 8 8 8 W2 W4 W1 W1 -2 1 -1 3 7 15 7 13 4 5 6 5 13 15 15 13 1 0 1 1 5 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 98 74 78 77 1 0 0 0 0 0 78 71 -12 POLY 8 FULL 15 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 PS 3300..2 21 FIFTH 5 TRIANGL 23 0 14 0 on 4 0 off off off off 0 0 0 0 5 4 5 5 0.5 2 0.5 6 no no no no 8 8 8 8 W2 W2 W1 W1 0 -3 3 3 19 21 19 15 12 9 31 7 15 15 15 15 7 4 0 3 6 4 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 97 74 95 60 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Quadra...1 22 FIFTH 6 TRIANGL 23 0 11 0 on 2 0 off off off off 0 0 0 0 5 6 4 4 2 0.5 0.5 3 no no no no 8 8 8 8 W2 W1 W2 W1 3 3 0 -3 19 15 19 21 31 7 12 9 15 14 15 15 0 2 7 4 6 5 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 97 80 95 76 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Quadra...2 23 THIRD 5 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 6 3 1 1 2 2 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 3 3 18 23 25 13 31 31 31 31 15 15 15 15 0 1 0 0 1 2 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 91 67 85 81 3 0 2 3 20 10 8 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 50 0 50 0 0 0 0 0 0 ResoSynth1 24 FIFTH 7 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 4 3 1 1 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 3 3 18 23 25 13 31 31 31 31 15 15 15 15 0 1 0 0 1 2 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 99 80 3 0 2 3 20 10 8 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 50 0 50 0 0 0 0 0 0 ResoSynth2 25 FIFTH 4 TRIANGL 32 0 0 0 off 0 0 off off off off 0 0 0 0 6 5 6 4 1 1 1 2 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 27 24 25 27 8 7 7 9 14 13 12 13 4 3 3 3 8 8 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 98 78 1 1 2 1 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 RMI......1 26 FIFTH 4 TRIANGL 32 0 0 0 off 0 0 off off off off 0 0 0 0 6 4 6 3 0.5 2 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 27 24 25 27 8 7 7 9 14 13 12 13 4 3 3 3 8 8 8 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 82 98 81 1 2 2 1 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 RMI......2 27 FIFTH 5 TRIANGL 2 0 10 0 off 0 0 off off off off 0 1 0 1 5 3 5 4 1 5 0.5 0.5 no no no no 8 8 8 8 W2 W1 W2 W1 2 -2 2 -2 18 23 25 16 31 31 31 8 15 15 15 7 0 1 0 0 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 97 87 3 0 2 1 20 10 8 0 -12 MONO 7 FINGER 5 POR 99 0 0 99 99 50 0 50 0 0 0 0 0 0 SH 7.....1 28 FIFTH 5 TRIANGL 2 0 10 0 off 0 0 off off off off 0 0 0 0 7 4 4 5 1 0.5 1 0.5 no no no no 8 8 8 8 W2 W2 W1 W1 2 -2 2 -2 18 23 25 16 31 31 31 8 15 15 15 7 0 1 0 0 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 97 87 2 2 2 2 20 10 8 0 -12 MONO 7 FINGER 1 POR 99 0 0 99 99 50 0 50 0 0 0 0 0 0 SH 7.....2 29 FIFTH 7 TRIANGL 33 0 56 0 on 2 0 off off off off 0 0 0 0 7 6 4 4 1 1 4 5 no no no no 8 8 8 8 W1 W3 W1 W1 1 0 0 0 28 31 31 31 6 5 8 9 7 11 12 8 4 0 0 16 4 5 3 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 85 78 2 2 3 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Sigma....1 30 FIFTH 6 TRIANGL 37 73 15 0 off 3 0 off off off off 0 0 0 0 6 7 6 3 6 25.95 1 1 no no no no 8 8 8 8 W2 W2 W1 W1 2 0 -3 3 22 22 22 22 21 10 9 8 13 13 15 15 1 10 1 3 6 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 89 75 99 87 0 0 0 0 0 0 0 0 -24 POLY 12 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Sigma....2 31 FIFTH 5 TRIANGL 33 0 56 0 on 2 0 off off off off 0 0 0 0 5 6 4 3 2 6 2 2 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 28 31 31 31 6 3 8 9 7 6 12 8 4 0 0 16 4 5 3 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 85 90 2 2 3 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Sigma....3 32 SIXTH 0 TRIANGL 31 6 9 0 off 4 0 off off off off 0 0 0 7 1 2 1 2 3 6 2.99 5.99 no no no no 8 8 10 12 W2 W4 W4 W6 -3 -3 3 3 31 24 27 31 10 8 16 8 10 11 11 9 0 0 0 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 39 0 0 0 0 35 99 0 0 -19 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Soft Synth 33 SIXTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 3 3 15 2.99 2.99 no no no no 8 8 10 10 W1 W1 W1 W1 2 2 -3 -3 15 31 15 31 31 15 31 15 15 12 15 13 6 6 6 0 5 4 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 99 84 0 1 0 0 0 24 0 0 -19 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Softsynth* 34 SECOND 0 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 2 3 0.5 0.5 0.62 2 no no no no 8 8 10 8 W1 W2 W8 W1 1 -1 0 -1 26 31 31 31 1 29 29 31 13 12 12 14 1 7 12 5 5 4 4 3 VOF VOF VOF VOF 99 82 99 50 50 50 98 80 65 80 1 1 1 1 0 17 40 40 12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SoloSynth 35 FIFTH 4 TRIANGL 35 0 0 0 on 0 0 off off off off 0 0 0 0 5 5 5 3 1 1 1 0.5 no no no no 8 8 8 8 W2 W2 W1 W1 -1 1 1 0 22 22 22 18 9 12 15 9 15 14 14 14 6 6 6 6 7 5 7 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 98 96 1 1 1 2 0 3 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 SpiralSynt 36 SIXTH 6 TRIANGL 37 0 0 0 off 2 0 off off off off 7 3 3 7 5 5 5 3 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W2 W2 W1 W1 0 0 0 2 31 31 31 31 22 22 22 6 13 13 13 11 0 0 0 0 10 10 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 99 91 0 0 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sweeper 37 FIFTH 6 TRIANGL 31 0 0 0 off 3 0 off off off off 0 0 0 0 2 2 1 4 1 1 2 3.18 no no no no 8 8 8 11 W1 W5 W1 W1 -3 -1 3 1 14 5 17 15 7 3 17 17 13 13 9 9 0 0 0 13 4 1 7 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 91 78 1 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 5 SweepSynt* 38 FIRST 0 TRIANGL 32 0 0 0 off 5 0 off off off off 0 0 0 0 2 1 2 0 6 6 1.5 2 no no no no 8 8 16 8 W2 W4 W6 W6 3 -2 1 -1 17 13 27 20 13 13 13 9 15 15 15 15 0 0 0 0 7 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 45 51 18 0 0 0 0 0 66 0 41 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 SweetSynth 39 FIFTH 7 TRIANGL 35 0 46 0 off 4 0 off off off off 1 5 1 5 0 0 0 0 3 2.99 3 3 no no no no 8 10 8 8 W4 W7 W2 W4 3 3 -3 -3 11 31 11 31 31 31 31 31 15 15 15 15 0 0 0 0 5 3 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 86 68 86 70 0 0 0 0 30 0 30 0 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.AftrTch 40 SIXTH 7 TRIANGL 31 6 9 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 3 6 2.99 5.99 no no no no 8 8 10 12 W2 W4 W4 W6 -3 -3 3 3 31 24 27 31 10 8 16 8 10 11 11 9 0 0 0 0 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 96 81 96 73 0 0 0 0 0 20 0 20 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Decay 1 41 FOURTH 7 TRIANGL 28 8 2 27 off 5 1 on off on off 0 0 0 0 3 4 3 2 1.49 1.49 1.5 20.31 no no no no 9 9 16 23 W1 W1 W2 W8 -2 3 3 -2 31 31 31 31 7 17 11 12 14 14 12 12 4 1 7 8 8 4 10 15 VOF VOF VOF VOF 99 99 99 50 50 50 96 78 87 62 2 2 1 2 0 26 0 34 -7 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Decay 2 42 SECOND 5 TRIANGL 0 2 0 3 off 5 0 on on on on 0 0 0 0 1 1 1 1 7.85 1.41 20.76 19.03 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 25 21 31 31 22 28 31 8 11 11 14 5 0 6 0 2 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 88 98 2 1 2 0 0 0 0 0 -6 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Synballs 43 FIFTH 6 TRIANGL 38 33 9 0 on 4 0 off off off off 0 0 0 0 6 5 5 4 1 1 1 15 no no no no 8 8 8 8 W2 W1 W3 W1 0 0 0 0 13 23 22 22 9 15 19 22 13 13 13 13 6 0 7 7 6 6 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 99 75 1 0 2 1 0 0 1 0 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Synth....1 44 FIFTH 3 TRIANGL 38 35 6 27 on 3 1 on on on on 0 0 0 0 6 4 5 2 2 2 1 11 no no no no 8 8 8 8 W1 W3 W1 W1 -3 -3 -1 -1 31 31 12 31 10 15 10 10 13 13 13 13 6 0 7 9 5 6 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 99 80 1 1 2 0 0 0 1 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Synth....2 45 FIFTH 4 TRIANGL 36 50 19 2 off 3 1 on on on on 0 0 0 0 5 4 6 3 1 1 1 2 no no no no 8 8 8 8 W1 W1 W3 W1 0 -3 0 3 31 13 31 13 29 10 29 10 14 14 14 14 7 7 7 7 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 98 82 99 89 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Synth....3 46 THIRD 4 TRIANGL 40 0 0 0 off 3 0 off off off off 0 0 0 0 6 5 6 5 1 1 1 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 0 0 31 31 24 31 6 6 16 31 14 14 12 15 7 7 15 15 15 15 14 1 VOF VOF VOF VOF 99 99 99 50 50 50 93 77 83 80 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Synth....4 47 FIFTH 6 TRIANGL 38 33 32 42 off 1 1 on on on on 0 0 0 0 5 4 5 3 1 1 4 5 no no no no 8 8 8 8 W2 W1 W1 W1 3 -1 3 0 14 27 31 31 3 3 21 20 9 14 0 0 6 6 7 8 8 8 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 95 99 1 0 0 2 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Synth....5 48 FIFTH 6 TRIANGL 38 0 0 0 off 2 0 off off off off 0 0 0 0 6 5 5 4 1 1 1 1 no no no no 8 8 8 8 W2 W2 W4 W1 0 0 0 0 31 30 31 16 22 12 22 12 13 10 13 10 7 7 0 7 7 7 15 12 VOF VOF VOF VOF 99 99 99 50 50 50 98 85 99 85 1 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Synth....6 49 FIFTH 5 TRIANGL 37 0 0 0 off 3 0 off off off off 0 0 0 0 5 4 4 4 1 0.5 0.5 0.5 no no no no 8 8 8 8 W2 W1 W2 W1 -1 -1 1 2 31 31 31 31 12 12 12 13 14 14 14 4 9 9 9 9 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 99 94 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Synth....7 50 FIFTH 6 TRIANGL 32 5 25 15 off 3 0 off off off off 0 0 0 0 0 1 1 1 2.55 2.55 2.56 2.56 no no no no 21 21 17 17 W1 W1 W1 W1 -3 -3 3 3 11 3 16 18 10 13 8 8 14 15 13 12 0 0 0 0 4 1 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 82 1 3 1 0 0 0 0 0 -16 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Synthish * 51 FIFTH 7 TRIANGL 35 0 0 0 on 0 0 off off off off 0 0 0 0 5 5 5 3 2 0.5 2 0.5 no no no no 8 8 8 8 W2 W2 W1 W1 -3 -3 3 3 15 31 15 31 31 31 31 31 15 15 15 15 0 0 0 0 5 3 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 92 94 93 81 0 1 0 0 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 SynthRise1 52 FIFTH 6 TRIANGL 35 0 0 0 on 0 0 off off off off 0 0 0 0 5 5 6 5 2 0.5 2 0.5 no no no no 8 8 8 8 W2 W5 W3 W3 -3 -3 3 3 15 31 15 15 31 31 31 31 15 15 15 13 0 0 0 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 88 97 95 95 0 1 0 0 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 SynthRise2 53 EIGHTH 7 TRIANGL 34 0 15 0 on 3 1 on on off off 0 0 0 0 3 1 2 2 1.51 1.49 1.49 1.51 no no no no 14 9 9 14 W6 W6 W2 W2 -1 1 -3 3 14 11 17 17 8 8 7 17 15 15 15 15 4 7 8 6 5 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 98 98 98 0 0 0 0 20 22 29 29 -19 POLY 2 FULL 3 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 SynthyPad1 54 FIFTH 6 TRIANGL 34 0 6 0 on 3 0 off off off off 0 0 0 0 5 4 6 5 0.5 0.5 1 2 no no no no 8 8 8 8 W2 W1 W2 W1 0 3 0 3 22 22 22 22 9 9 9 9 14 14 14 14 4 4 4 4 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 92 84 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 System 15 55 FIFTH 2 TRIANGL 23 0 11 0 on 3 0 off off off off 0 0 0 0 5 4 5 4 1 0.5 1 0.5 no no no no 8 8 8 8 W2 W2 W1 W2 3 1 0 -2 19 11 14 7 2 7 3 1 15 15 15 12 0 0 1 0 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 95 0 0 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 System35.1 56 FIFTH 7 TRIANGL 23 0 11 0 on 3 0 off off off off 0 0 0 0 5 4 4 4 2 2 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 0 0 -2 10 21 18 21 4 4 4 4 13 13 13 13 3 3 3 3 8 8 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 93 76 98 80 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 System35.2 57 FIFTH 6 TRIANGL 23 0 11 0 on 3 0 off off off off 0 0 0 0 5 4 6 2 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W5 W5 W1 W1 3 3 0 -2 19 21 19 15 3 4 12 1 15 15 15 12 1 0 2 0 6 7 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 99 81 2 0 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 System35.3 58 FIFTH 5 TRIANGL 23 0 11 0 on 2 0 off off off off 0 0 0 0 5 5 6 3 1 0.5 1 0.5 no no no no 8 8 8 8 W2 W1 W2 W1 0 3 0 -3 19 21 19 9 12 3 31 9 14 5 15 15 0 0 0 0 6 6 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 96 99 91 0 1 0 0 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 System55.1 59 FIFTH 6 TRIANGL 23 0 11 0 on 2 0 off off off off 0 0 0 0 5 5 4 2 0.5 2 0.5 0.5 no no no no 8 8 8 8 W1 W2 W1 W1 -1 3 -1 3 19 21 19 9 12 3 31 9 14 5 15 15 0 0 0 0 6 6 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 88 99 92 0 1 0 0 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 System55.2 60 FOURTH 7 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 1 1 1 0 1 0.5 12 3 no no no no 8 8 8 8 W3 W1 W1 W3 0 0 0 0 30 31 7 10 13 31 8 7 13 15 0 0 5 2 4 7 4 3 3 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 69 73 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Tamboura * 61 SIXTH 7 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 3 5 1 1 1 1.41 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 25 29 31 31 24 31 15 15 15 15 0 0 0 0 3 2 2 3 VOF VOF VOF VOF 99 99 99 50 50 50 90 95 69 80 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Tamboura.1 62 SIXTH 0 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 3 5 2 1 0.5 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 25 29 31 31 24 17 15 15 15 15 0 0 0 7 4 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 91 99 69 84 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Tamboura.2 63 FIFTH 5 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 2 3 2 1 0.5 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 25 29 31 31 24 31 15 15 15 15 0 0 0 0 3 3 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 91 67 91 71 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Tamboura.3 64 THIRD 0 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 0 0 2 0 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 -2 3 18 31 31 6 6 0 0 0 0 0 0 0 0 0 0 0 3 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 88 90 1 0 0 0 0 0 0 8 12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Time X * 65 SIXTH 0 TRIANGL 31 6 9 0 off 4 0 off off off off 0 0 0 7 1 2 1 2 6 9 2.99 5.99 no no no no 8 8 10 12 W2 W4 W4 W6 -3 -3 3 3 23 16 19 23 6 4 12 4 10 11 11 9 0 0 0 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 30 0 0 0 0 35 99 0 0 -19 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 TrdrpSynth 66 FIFTH 2 TRIANGL 32 0 28 0 off 4 0 off off off off 0 0 0 0 1 0 2 0 1.51 0.75 1.5 0.75 no no no no 14 12 16 12 W1 W1 W1 W5 0 3 -3 -3 21 23 21 23 7 5 7 9 0 0 0 0 0 0 0 0 4 3 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 99 91 1 1 1 1 6 0 7 25 -7 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Voyage * 67 FIFTH 6 SHOLD 38 0 2 0 on 6 0 off off off off 0 0 0 0 5 5 4 4 2 2 1 7 no no no no 8 8 8 8 W2 W4 W2 W1 3 -2 0 0 9 30 10 10 16 15 9 9 15 13 14 14 3 0 7 7 4 5 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 98 71 1 1 1 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Walt/Wendy 68 FIFTH 6 TRIANGL 37 41 38 99 off 1 0 off off off off 0 0 0 0 5 6 6 4 2 1 3 3 no no no no 8 8 8 8 W1 W2 W1 W2 0 0 -3 0 16 16 16 18 0 0 0 28 13 13 13 14 3 3 3 21 8 8 9 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 88 95 82 1 1 1 2 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Wapper...1 69 FIFTH 5 TRIANGL 37 42 38 99 off 1 0 off off off off 0 0 0 0 5 5 5 5 1 5 1 14 no no no no 8 8 8 8 W1 W1 W2 W4 0 0 -3 0 16 16 16 18 0 0 0 28 13 13 13 14 3 3 3 10 8 8 9 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 95 73 1 1 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Wapper...2 70 FIFTH 6 TRIANGL 32 0 0 0 off 5 0 off off off off 0 0 0 0 2 1 2 1 1.49 1.49 1.51 1.51 no no no no 9 9 14 14 W1 W6 W1 W3 3 -3 -3 3 22 31 18 31 14 14 14 6 15 15 15 15 0 0 0 0 7 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 79 0 0 0 0 0 0 0 0 -19 MONO 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 WarmSquare 71 FIFTH 4 TRIANGL 35 0 5 0 off 4 0 off off off off 0 0 0 0 6 5 5 3 1 1 1 1 no no no no 8 8 8 8 W2 W1 W1 W1 0 0 0 0 18 16 18 14 9 7 7 10 14 0 12 12 4 6 6 6 7 10 9 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 83 0 0 2 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Wendy : 72 FIFTH 6 TRIANGL 43 0 11 0 on 3 0 off off off off 0 0 0 0 6 4 6 4 2 2 2 10 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 -1 3 8 8 14 12 0 0 9 9 15 15 15 15 0 0 4 4 5 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 68 98 49 1 1 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Wendy C 73 SIXTH 3 SHOLD 46 58 69 99 on 7 1 on on on on 0 0 0 0 7 3 5 3 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 88 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Whos Upset 74 FIFTH 7 TRIANGL 33 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 0 3 1.5 1.5 1.51 1.51 no no no no 16 16 14 14 W1 W1 W1 W1 0 0 0 0 11 14 21 18 31 8 14 8 15 15 15 13 0 0 0 0 6 1 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 99 82 1 0 1 0 0 0 0 0 -19 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 X-Pander * 75 FOURTH 6 TRIANGL 32 0 20 0 off 3 0 off off off off 0 0 0 0 2 0 7 0 2 2 27.57 27.57 no no no no 8 8 23 23 W1 W1 W1 W1 3 -3 3 2 15 31 17 19 4 0 7 8 15 15 0 15 9 0 18 0 6 4 2 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 74 70 1 0 3 3 0 0 0 0 -24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Xuereb * 76 VOICES 4 Breath 0 SEVENTH 7 TRIANGL 29 6 15 0 on 4 2 off off off on 0 0 0 0 3 0 0 0 1.51 1.5 1.49 22.49 no no no no 14 16 9 96 W5 W4 W1 W4 -3 3 3 -3 14 11 15 31 8 8 5 0 15 15 15 15 4 7 3 0 5 7 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 59 99 0 0 0 0 0 5 8 0 -7 POLY 0 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BadBreath1 0 SEVENTH 7 TRIANGL 29 6 15 41 on 4 3 off off off on 0 0 0 0 4 3 3 1 1.58 1.57 1.58 22.49 no no no yes 18 8 10 96 W5 W4 W5 W4 -3 3 -2 -3 15 12 16 31 8 9 7 0 15 15 15 15 4 4 3 0 5 7 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 77 99 0 0 0 0 51 62 12 0 -7 POLY 0 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BadBreath2 1 SEVENTH 7 TRIANGL 29 6 15 0 on 4 2 off off off on 0 0 0 0 3 0 0 0 1.51 1.5 1.49 22.49 no no no no 14 16 9 96 W5 W4 W8 W2 -3 3 3 -3 10 10 10 31 8 8 5 0 15 15 15 15 4 7 3 0 5 7 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 50 99 0 0 0 0 25 21 19 0 -7 POLY 0 FULL 2 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 3 BadBreath3 2 FIFTH 7 SHOLD 99 0 47 0 off 5 0 off off off off 0 0 0 0 2 2 2 2 1.5 22.49 1.49 23.55 no no no no 16 80 9 64 W2 W6 W2 W4 -3 0 3 0 13 13 13 13 31 7 31 11 15 12 15 12 0 0 0 0 5 1 5 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 31 99 44 0 0 0 0 0 0 0 0 -7 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BreathOrgn 3 SEVENTH 7 TRIANGL 29 6 15 0 on 4 2 off off off on 0 0 0 0 3 0 0 0 2 2 2.06 22.49 no no no no 8 8 9 96 W5 W2 W8 W4 -3 3 3 -3 16 11 15 31 4 8 5 0 15 15 15 15 4 7 3 0 9 6 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 43 99 0 0 0 0 0 5 8 0 -12 POLY 1 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BreathyEns 4 Choir 1 FIFTH 0 TRIANGL 30 31 63 0 off 4 0 off off off off 0 0 0 0 1 2 0 0 11.13 1 2 1 yes no no no 71 8 8 8 W1 W3 W1 W1 -3 3 -3 3 9 31 11 31 25 25 31 25 15 15 15 15 0 0 0 0 7 1 6 1 VOF VOF VOF VOF 99 99 99 50 50 50 77 77 99 70 0 0 0 0 23 74 0 8 -12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 ChoiChoir* 0 THIRD 1 TRIANGL 32 33 53 0 on 2 0 off off off off 0 0 0 0 7 7 7 0 1 2 2 3 no no no no 8 8 8 8 W1 W2 W1 W1 -3 1 -2 0 8 12 12 12 8 3 6 11 13 15 15 13 9 9 0 12 7 9 9 12 VOF V48 VOF V24 99 99 99 50 50 50 96 50 69 55 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 5 Choir....1 1 FIFTH 1 TRIANGL 32 33 53 0 on 2 0 off off off off 0 0 0 0 5 6 5 3 1 2 2 3 no no no no 8 8 8 8 W1 W1 W1 W1 -3 0 0 0 12 12 12 12 8 31 6 8 13 15 15 13 9 9 0 12 7 9 9 12 VOF VOF VOF VOF 99 99 99 50 50 50 96 53 99 53 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Choir....2 2 FIFTH 0 TRIANGL 31 35 33 0 on 2 0 off off off off 0 0 0 0 5 6 6 2 1 2 2 4 no no no no 8 8 8 8 W1 W4 W1 W1 -1 1 -2 -1 15 16 11 16 15 22 22 21 15 15 15 15 4 9 9 31 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 95 68 95 58 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Choir....3 3 SECOND 4 TRIANGL 32 45 0 0 off 0 1 on off off off 0 0 0 0 5 4 5 0 1 1 6 8 no no no no 8 8 8 8 W1 W1 W4 W1 3 -3 0 0 9 9 7 7 31 31 0 0 15 15 9 10 1 3 6 4 6 7 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 88 58 52 57 0 0 0 0 41 99 88 97 12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 4 Choir....4 4 SECOND 5 TRIANGL 33 19 9 0 off 6 0 off off off off 0 0 0 0 6 5 6 3 1 1 7 8 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 -3 3 14 11 21 19 31 31 14 18 15 15 9 10 1 3 6 4 9 10 10 4 VOF VOF VOF VOF 99 99 99 50 50 50 97 68 59 54 0 0 0 0 0 86 99 58 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Choir....5 5 SECOND 2 TRIANGL 33 22 8 0 on 6 0 off off off off 0 0 0 0 5 4 6 3 1 1 2 5 no no no no 8 8 8 8 W1 W1 W1 W2 3 -3 -3 3 12 12 13 18 31 31 14 21 15 15 9 10 1 3 6 4 9 10 10 4 VOF VOF VOF VOF 99 99 99 50 50 50 87 57 72 51 0 0 0 0 0 86 99 58 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Choir....6 6 FIFTH 7 TRIANGL 30 10 17 4 off 4 2 on on on on 0 0 0 0 2 0 2 0 1 0.5 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 -3 -3 10 10 12 11 13 13 11 13 15 15 15 15 0 0 0 0 4 2 4 2 VOF VOF VOF VOF 99 86 99 50 50 50 99 50 99 50 0 0 0 0 20 10 30 10 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Choral 7 SEVENTH 0 TRIANGL 29 0 23 0 off 2 0 off off off off 0 0 0 0 0 0 0 0 2 0.5 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 13 13 13 13 14 14 14 14 15 15 15 15 0 0 0 0 4 2 4 2 VOF VOF VOF VOF 99 71 99 50 50 50 99 78 99 50 1 1 1 0 88 0 0 40 0 POLY 5 FULL 0 POR 40 0 0 50 13 0 0 50 0 0 0 0 0 0 Chorus 8 FOURTH 5 TRIANGL 35 35 11 2 off 4 1 on on on on 0 0 0 0 6 6 5 2 1 1 1 1.56 no no no yes 8 8 8 57 W2 W2 W1 W1 -3 0 -1 1 10 6 10 31 6 7 6 22 15 7 15 15 7 4 9 2 6 4 9 2 VOF VOF VOF VOF 99 99 99 50 50 50 93 64 57 47 0 0 0 0 10 50 69 35 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 6 Chorus...1 9 FOURTH 7 TRIANGL 35 35 11 2 off 4 1 on on on on 0 0 0 0 5 5 5 3 1 1 1 4 no no no no 8 8 8 8 W1 W1 W1 W1 -3 0 -1 2 15 7 9 14 6 7 6 22 15 7 15 15 7 7 8 5 9 4 9 2 VOF VOF VOF VOF 99 99 99 50 50 50 95 67 64 58 0 0 0 0 10 50 69 35 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Chorus...2 10 SEVENTH 5 TRIANGL 35 35 11 2 off 4 1 on on on on 0 0 0 0 5 6 5 4 1 2 2 2 no no no no 8 8 8 8 W2 W1 W1 W1 -3 1 -1 2 13 6 13 11 7 7 6 9 15 7 15 15 7 7 8 5 9 4 9 5 VOF VOF VOF VOF 99 99 99 50 50 50 95 77 87 73 0 0 0 0 10 50 69 35 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Chorus...3 11 SEVENTH 0 TRIANGL 27 15 72 34 off 1 1 on on on on 0 0 0 0 6 5 6 3 1 1 1 1 no no no no 8 8 8 8 W2 W2 W2 W2 3 2 1 0 10 10 13 7 5 4 6 5 14 9 15 14 8 5 8 10 7 5 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 97 91 96 81 1 0 0 1 10 60 89 45 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 6 Chorus...4 12 SEVENTH 1 TRIANGL 27 15 79 38 off 1 1 on on on on 0 0 0 0 7 6 6 4 1 1 0.5 2 no no no no 8 8 8 8 W4 W4 W1 W1 2 1 -2 1 9 12 10 9 4 8 7 7 15 11 15 13 8 4 5 8 7 6 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 97 99 99 65 0 0 0 0 10 60 89 45 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Chorus...5 13 SEVENTH 1 TRIANGL 27 15 79 61 off 1 1 on on on on 0 0 0 0 6 5 5 5 1 2 3 1 no no no no 8 8 8 8 W1 W1 W1 W3 0 0 0 0 12 12 10 12 7 6 10 10 15 13 15 13 6 5 3 6 8 7 7 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 75 0 0 1 0 10 50 67 62 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 6 Chorus...6 14 SEVENTH 3 TRIANGL 27 15 79 61 off 3 1 on on on on 0 0 0 0 0 0 0 0 1 3 2 1 no no no no 8 8 8 8 W1 W1 W4 W1 2 0 3 0 8 9 8 6 8 3 5 7 15 13 15 13 5 4 4 5 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 91 85 91 66 0 0 0 0 30 50 42 23 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 6 Chorus...7 15 SEVENTH 1 TRIANGL 27 15 79 61 off 1 1 on on on on 0 0 0 0 6 5 7 0 3 1 1 3 no no no no 8 8 8 8 W1 W4 W4 W2 0 0 0 0 9 9 9 6 6 5 4 5 13 14 13 12 2 3 6 6 9 7 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 93 91 95 57 0 0 0 0 30 50 42 23 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 1 50 0 0 0 0 0 0 Chorus...8 16 FVoice 2 FIFTH 6 TRIANGL 39 27 48 8 on 2 1 off off on on 0 0 0 0 6 6 6 6 1 1 1 1 no no no no 8 8 8 8 W1 W4 W1 W4 -3 -1 3 -2 12 21 12 31 8 24 8 19 14 12 14 10 4 24 4 4 9 7 9 5 VOF VOF VOF VOF 99 99 99 50 50 50 94 75 91 82 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 4 F Voice..1 0 FOURTH 4 TRIANGL 39 34 91 2 off 1 1 off off on on 0 0 0 0 6 4 5 5 1 1 1 23.55 no no no no 8 8 8 8 W1 W1 W1 W1 -3 0 3 -2 14 6 12 13 8 7 6 22 14 9 14 14 4 15 10 15 9 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 87 68 26 50 0 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 F Voice..2 1 EIGHTH 4 TRIANGL 35 0 21 40 off 1 1 on off off off 0 0 0 0 6 6 6 5 1 2 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 -2 2 3 -3 13 12 13 15 25 25 12 31 15 15 15 15 6 6 10 14 7 7 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 97 99 99 99 0 0 0 0 0 60 15 40 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 4 F Voice..3 2 FIFTH 7 TRIANGL 35 58 10 2 off 4 1 on on on on 0 0 0 0 5 0 6 4 1 1 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 1 1 0 0 18 15 18 18 6 6 21 21 15 14 15 15 4 6 4 3 9 7 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 88 67 55 71 0 0 0 0 0 0 0 1 12 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 F Voice..4 3 FIFTH 4 TRIANGL 30 3 37 0 off 5 0 off off off off 0 0 0 0 2 0 2 0 1 1 2 4 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 -3 3 19 14 11 24 15 23 13 23 15 15 10 15 0 0 0 0 7 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 47 99 45 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Fem.Voice* 4 MVoice 3 FIFTH 0 TRIANGL 30 33 51 38 off 1 1 on on on on 0 0 0 0 6 6 6 5 2 5 2 1 no no no no 8 8 8 8 W1 W5 W2 W1 1 -1 -1 -1 13 13 12 31 24 5 6 24 15 15 15 14 2 2 4 3 7 7 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 95 61 99 73 0 1 1 1 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 M Voice..1 0 FIFTH 4 TRIANGL 29 12 41 38 off 2 1 on off on off 0 0 0 0 6 6 6 5 2 1 2 1 no no no no 8 8 8 8 W1 W3 W1 W2 1 -2 0 0 15 31 15 31 24 24 24 24 15 15 15 15 3 3 3 3 7 4 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 96 71 0 0 1 1 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 M Voice..2 1 FIFTH 4 TRIANGL 30 33 33 12 off 1 1 on on on on 0 0 0 0 6 5 5 5 3 6.96 1 1 no yes no no 8 55 8 8 W1 W1 W2 W2 2 0 1 1 8 18 9 31 0 7 3 24 12 3 9 10 15 15 15 2 8 4 7 4 VOF V24 VOF V48 99 99 99 50 50 50 95 42 88 90 1 1 1 1 10 10 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 5 M Voice..3 2 FIFTH 2 TRIANGL 33 20 56 0 on 2 0 off off off off 0 0 0 0 5 6 6 6 1 8 1 1 no no no no 8 8 8 8 W1 W1 W2 W1 -2 1 3 1 10 13 12 12 0 12 6 5 12 15 13 3 15 6 15 7 8 5 7 2 VOF VOF VOF VOF 99 99 99 50 50 50 90 52 92 70 1 1 1 1 10 10 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 M Voice..4 3 FIFTH 0 TRIANGL 32 0 48 38 off 4 0 off off off off 0 0 0 0 0 0 0 0 13.5 1 2 1 yes no no no 72 8 8 8 W1 W1 W1 W1 0 2 -3 3 10 18 11 31 25 6 25 25 15 15 15 15 0 0 0 0 7 2 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 78 71 99 77 0 0 0 0 0 0 0 19 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Mal.Voice* 4 FOURTH 1 TRIANGL 10 7 5 1 on 6 0 off off off off 0 0 0 0 3 0 0 0 4 5 0.5 27.57 no no no no 8 8 8 23 W1 W4 W1 W2 0 -3 3 0 14 11 15 31 0 2 2 0 15 15 15 15 1 3 4 0 6 1 1 4 VOF VOF VOF VOF 99 99 99 50 50 50 95 60 59 50 0 0 0 0 8 13 35 44 -12 POLY 5 FULL 0 POR 99 0 0 3 0 0 0 50 0 0 0 0 0 0 Male Voice 5 FOURTH 1 TRIANGL 26 0 5 1 on 6 0 off off off off 0 0 0 0 3 0 0 0 1.5 1.49 0.75 9.06 no no no no 16 9 12 9 W1 W4 W1 W2 0 -3 3 0 14 11 15 31 0 2 2 0 15 15 15 15 1 3 4 0 6 1 1 4 VOF VOF VOF VOF 99 99 99 50 50 50 95 60 59 50 0 0 0 0 8 13 35 44 -7 POLY 0 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 MaleVoices 6 Other 4 FIFTH 5 TRIANGL 34 14 19 8 off 5 1 off on off on 0 0 0 0 6 5 5 5 2 2 1 0.5 no no no no 8 8 8 8 W2 W1 W1 W1 3 3 -3 -3 12 12 12 5 31 7 31 7 15 14 15 14 0 0 0 0 7 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 99 82 0 0 0 0 10 14 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Quartet..1 0 FIFTH 6 TRIANGL 34 14 19 8 off 5 1 off on off on 0 0 0 0 6 5 6 4 2 1 2 0.5 no no no no 8 8 8 8 W1 W3 W1 W1 3 3 -3 -3 12 14 14 14 31 7 31 7 15 14 15 14 0 0 0 0 7 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 81 0 0 0 0 10 14 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Quartet..2 1 SIXTH 7 TRIANGL 33 8 12 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 3 6 2.99 5.99 no no no no 8 8 10 12 W2 W4 W4 W6 -3 -3 3 3 14 14 14 20 9 8 9 6 10 9 11 7 2 2 2 0 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 94 89 99 73 0 0 0 0 0 20 0 20 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Ensem.1 2 SEVENTH 7 TRIANGL 33 12 22 0 off 5 0 off off off off 0 0 0 0 1 1 1 2 3 6 6 21.98 no no no no 8 8 8 8 W4 W2 W1 W7 -3 3 3 0 10 10 31 31 3 4 7 23 11 7 11 12 1 2 2 9 5 5 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 72 0 0 0 0 0 20 0 20 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Ensem.2 3 FIFTH 2 SHOLD 99 0 12 0 off 6 0 off off off off 0 0 0 0 1 2 6 2 1 3 5 3 no no no no 8 8 8 8 W1 W4 W5 W1 -3 -3 3 3 26 31 26 31 31 31 31 31 15 15 15 15 7 5 7 5 4 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 94 67 86 80 0 0 0 0 0 0 0 39 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Ensem.3 4 FIFTH 7 TRIANGL 31 1 20 0 off 5 0 off off off off 0 0 0 0 1 1 1 1 6 3 2.99 6 no no no no 8 8 10 8 W2 W4 W4 W1 -3 -1 1 3 27 7 16 8 9 7 31 4 11 15 15 15 0 0 0 0 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 95 81 99 62 0 0 0 0 20 20 20 20 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Ensem.4 5 SIXTH 6 TRIANGL 28 11 27 0 off 3 0 off off off off 0 0 0 0 3 3 3 0 1 0.5 2 1 no no no no 8 8 8 8 W3 W2 W3 W6 -3 3 -3 0 14 11 14 14 6 6 6 2 14 14 14 10 0 0 0 0 5 5 5 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 91 68 0 0 0 0 0 0 0 19 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Sy.Ensem.5 6 FIFTH 5 TRIANGL 30 10 80 4 off 4 2 on on on on 0 0 0 0 2 0 2 0 2 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 -3 3 3 10 10 10 12 14 14 12 14 15 15 15 15 0 0 0 0 6 4 6 4 VOF VOF VOF VOF 99 86 99 50 50 50 99 65 99 65 0 0 0 0 10 10 10 10 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Voice 1 7 FIFTH 5 TRIANGL 30 10 80 4 off 4 2 on on on on 0 0 0 0 2 0 2 0 2 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 -3 3 3 13 13 13 12 14 14 12 14 15 15 15 15 0 0 0 0 6 4 6 4 VOF VOF VOF VOF 99 86 99 50 50 50 99 65 99 65 0 0 0 0 10 10 10 10 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Voice 1* 8 FIFTH 6 TRIANGL 30 18 44 0 off 4 0 off off off off 0 0 0 0 2 0 2 0 2 1 1 1 no no no no 8 8 8 8 W1 W3 W4 W1 -3 0 0 1 14 15 14 18 2 2 2 2 10 11 10 15 1 1 1 1 5 2 5 4 VOF VOF VOF VOF 99 62 99 50 50 50 99 49 99 55 1 1 1 1 15 0 15 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Voice 2 9 FOURTH 7 TRIANGL 30 11 33 0 off 4 0 off off off off 0 0 0 0 1 1 2 0 1 1 2 11 no no no no 8 8 8 8 W1 W1 W1 W1 0 -2 3 0 17 11 12 31 15 23 13 31 15 15 11 15 0 0 0 0 7 6 7 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 66 0 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Syntvoice* 10 THIRD 7 SAWUP 16 0 81 0 on 6 2 off off off on 0 0 0 0 3 0 0 0 1 0.5 1.49 15.57 no no no no 8 8 9 96 W2 W4 W1 W4 -3 3 3 -3 20 14 19 31 17 8 28 0 0 15 6 15 13 7 13 0 11 7 12 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 99 71 0 0 0 0 0 5 8 0 -1 POLY 0 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 VocalNuts 11 FIFTH 7 TRIANGL 35 42 35 0 off 3 1 on off off off 0 0 0 0 6 1 6 3 1 1 10 1 no no no no 8 8 8 8 W1 W1 W1 W1 2 1 -1 1 8 10 9 15 14 31 31 14 15 15 15 15 0 0 0 0 6 3 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 68 69 0 0 0 0 0 0 0 99 0 POLY 2 FULL 0 POR 40 0 0 65 0 0 0 50 0 0 0 0 0 5 > Voices < 12 FOURTH 7 TRIANGL 26 0 5 1 on 6 0 off off off off 0 0 0 0 3 0 0 0 1.5 1.49 1.51 10.48 no no no no 16 9 14 9 W1 W1 W1 W1 0 -3 3 0 14 11 15 31 0 2 2 0 15 15 15 15 1 3 4 0 6 1 1 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 64 53 57 0 0 0 0 0 5 8 0 -7 POLY 0 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Voices 13 SEVENTH 7 SAWUP 79 0 68 99 off 3 0 off off off on 0 0 0 0 2 2 3 0 1.51 1.49 24.22 22.38 no no no no 14 9 8 23 W4 W4 W7 W3 -3 1 3 -3 11 11 16 31 8 8 10 14 13 13 12 14 0 0 0 0 4 4 6 1 VOF VOF VOF VOF 99 99 99 50 50 50 97 99 65 92 1 0 0 0 0 38 0 0 -7 POLY 4 FULL 0 POR 99 0 0 21 0 0 0 50 0 0 0 0 0 0 Whisper 14 SECOND 0 SHOLD 99 0 82 0 off 5 0 off off off off 0 0 0 0 2 1 2 1 2 8 2 7.4 no no no no 8 8 8 12 W1 W8 W1 W4 3 3 -3 -3 11 0 23 6 1 0 1 29 15 15 10 15 6 12 0 1 5 2 2 2 VOF VOF VOF VOF 99 99 99 50 50 50 92 83 75 6 1 1 1 0 1 46 0 0 0 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Wind Voice 15 PERCUSSION 5 Bell 0 FIFTH 0 TRIANGL 18 0 0 0 on 3 0 off off off off 0 0 0 0 3 0 2 1 1 3.46 1 2.93 no no no no 8 8 8 23 W1 W1 W1 W5 -3 -3 3 3 26 31 26 31 15 31 15 31 0 15 0 15 0 0 0 0 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 99 73 0 0 0 0 0 0 0 0 12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Agogo * 0 FIFTH 7 TRIANGL 19 0 6 0 on 6 0 off off off off 0 0 0 0 2 2 1 2 2 13.89 2 7 no yes no no 8 45 8 8 W1 W2 W1 W3 -1 -2 -1 1 31 31 31 31 17 19 17 18 10 12 14 12 15 25 16 25 6 4 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 99 87 1 1 1 1 0 17 0 6 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Agogo Bell 1 FIFTH 0 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 2 0 5 0 3.5 3.52 5.25 5.25 no no no no 16 16 12 12 W1 W2 W1 W1 3 -3 0 2 29 31 23 23 10 3 6 10 0 0 0 0 8 8 8 0 3 3 3 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 84 1 1 1 1 14 76 20 56 -22 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Analobell* 2 FIFTH 1 TRIANGL 30 0 0 0 off 4 0 off off off off 0 0 0 0 0 3 0 1 3 2 1 2.82 no no no no 8 8 8 8 W1 W1 W1 W1 -3 2 3 -3 26 30 28 25 12 16 8 26 4 4 11 7 5 0 5 0 4 3 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 96 87 98 88 0 2 1 1 0 0 0 0 2 POLY 3 FULL 0 POR 99 0 0 50 38 42 0 50 0 0 0 0 0 0 Bangkok 3 FIFTH 5 TRIANGL 24 0 0 0 off 2 0 off off off off 0 0 0 0 5 1 5 1 1 5 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 1 0 0 0 27 31 31 31 15 16 11 5 15 15 14 7 12 12 0 0 3 3 5 2 VOF VOF VOF VOF 99 99 99 50 50 50 79 78 98 80 1 1 0 3 0 0 0 0 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bass Bells 4 FIFTH 7 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 2 1 3 1 0.5 3 1 7 no no no no 8 8 8 8 W1 W1 W1 W1 -3 -3 3 3 24 31 25 31 9 16 9 22 0 8 0 10 0 10 0 7 6 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 95 68 96 76 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bell 5 FIFTH 7 TRIANGL 30 0 0 0 off 5 0 off off off off 0 0 0 0 2 1 3 1 0.5 4 1 7 no no no no 8 8 8 8 W1 W1 W1 W1 -3 -3 3 3 24 31 25 31 9 16 9 22 0 8 0 10 0 10 0 7 6 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 95 68 96 76 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bell* 6 FIFTH 0 TRIANGL 26 0 0 0 off 5 0 off off off off 0 0 0 0 3 4 2 4 1.5 10.37 1.49 10.38 no no no no 16 14 9 8 W1 W5 W1 W4 -3 -3 3 3 31 30 30 31 31 10 29 5 15 2 15 4 10 1 10 0 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 92 83 92 81 1 1 1 1 0 80 0 80 5 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bell Ensem 7 SEVENTH 7 TRIANGL 31 0 6 0 on 4 2 off off off on 0 0 0 0 3 0 3 0 1.51 1.49 0.75 9 no no no no 14 9 12 96 W6 W6 W1 W4 3 -3 3 -3 21 31 14 18 8 8 7 11 15 15 15 7 4 7 2 15 5 7 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 92 67 0 0 0 0 0 5 8 0 5 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 Bell Pad 8 FIFTH 6 TRIANGL 12 0 6 0 off 4 0 off off off off 0 0 0 0 3 4 5 1 2 4 2 4 no no no no 8 8 8 8 W2 W3 W1 W1 3 -3 0 0 30 27 29 29 2 15 15 4 14 14 14 13 3 0 3 1 3 3 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 99 82 1 1 0 0 0 88 87 0 -24 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 Bell.....1 9 FIFTH 7 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 5 2 6 4 4 14 1 14 no no no no 8 8 8 8 W1 W2 W3 W1 -3 -3 0 3 31 31 31 27 8 7 7 4 0 1 0 0 0 0 0 0 5 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 96 74 99 56 1 0 1 2 0 0 0 98 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Bell.....2 10 FIFTH 4 TRIANGL 9 0 4 0 off 4 0 off off off off 0 0 0 0 4 3 4 2 1 5 2 1.41 no no no no 8 8 8 8 W1 W2 W1 W4 -3 3 3 0 24 21 28 26 24 14 6 4 14 13 7 11 3 0 4 1 1 1 2 1 VOF VOF VOF VOF 99 99 99 50 50 50 98 84 99 78 2 2 2 0 0 0 0 22 12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bell.....3 11 FIFTH 7 TRIANGL 20 10 0 57 on 0 1 on off off off 0 0 0 0 7 3 5 2 3 15 3 15 no no no no 8 8 8 8 W1 W5 W1 W1 0 1 3 3 25 25 25 25 7 10 7 10 7 2 7 1 15 31 18 23 3 3 4 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 99 78 0 0 0 0 19 0 19 0 -12 POLY 2 FULL 0 POR 99 0 0 21 39 0 0 50 0 0 0 0 0 0 Bell.....4 12 SEVENTH 6 TRIANGL 20 10 0 57 on 0 1 on off off off 0 0 0 0 5 3 6 4 1 2 4 15 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 3 3 25 25 25 25 7 10 7 10 7 2 7 1 7 31 18 2 4 5 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 98 81 0 0 0 0 19 0 19 0 -12 POLY 2 FULL 0 POR 99 0 0 21 39 0 0 50 0 0 0 0 0 0 Bell.....5 13 SECOND 0 TRIANGL 35 0 0 0 on 6 0 off off off off 0 0 0 0 6 4 5 5 1 2 4 15 no no no no 8 8 8 8 W2 W1 W3 W1 0 0 0 0 31 31 31 31 6 10 31 26 0 0 15 0 0 0 0 14 4 5 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 73 78 0 1 1 2 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bell.....6 14 THIRD 0 TRIANGL 35 0 0 0 on 6 0 off off off off 0 0 0 0 6 4 4 4 1 7 7 13 no no no no 8 8 8 8 W2 W1 W2 W1 0 0 0 0 31 31 31 31 6 10 31 26 0 0 15 0 0 0 0 14 6 5 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 73 78 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bell.....7 15 FIFTH 7 TRIANGL 92 0 1 99 on 3 0 off off off off 0 0 0 0 5 4 6 5 1 7 1 8 no no no no 8 8 8 8 W1 W1 W4 W1 0 0 0 0 31 31 31 31 10 0 13 13 0 15 0 0 7 0 31 31 4 2 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 99 75 0 3 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bell.....8 16 SIXTH 0 TRIANGL 26 0 0 57 on 6 1 on on on off 0 0 0 0 6 3 5 3 1 4 8 18.37 no no no no 8 8 8 8 W2 W1 W3 W1 0 0 0 0 18 18 18 24 8 31 31 21 15 15 15 10 10 10 10 9 5 5 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 2 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bell.....9 17 FOURTH 0 TRIANGL 26 0 0 28 on 6 1 off off on off 0 0 0 0 6 4 5 4 2 7 4 12 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 0 0 24 31 31 31 4 12 6 13 12 15 12 10 9 6 7 6 4 3 3 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 63 72 63 0 0 0 2 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bell....10 18 SEVENTH 7 TRIANGL 35 0 0 0 on 6 0 off off off off 0 0 0 0 5 4 5 2 2 12 2 9 no no no no 8 8 8 8 W2 W2 W2 W1 0 0 0 0 31 31 31 31 16 11 8 5 11 10 10 15 0 0 0 14 4 5 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 93 72 0 3 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bell....11 19 FIFTH 0 TRIANGL 25 0 36 0 off 3 0 off off off off 0 0 0 0 1 2 1 2 0.75 2.68 0.75 2.7 no no no no 12 19 12 17 W1 W1 W1 W1 3 -3 -3 3 28 24 31 26 10 8 10 8 14 13 14 13 8 6 8 6 4 2 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 97 92 0 1 0 1 11 21 13 22 5 POLY 1 FULL 0 POR 40 0 0 99 0 0 0 48 0 0 0 0 0 0 BelleBell* 20 EIGHTH 0 TRIANGL 29 0 3 19 on 5 3 off on off off 0 0 7 0 0 0 0 0 6.28 1 19.03 5.19 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 12 12 12 12 5 5 5 5 13 13 13 13 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 97 87 0 0 0 0 0 0 0 0 24 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Bells 21 SEVENTH 7 TRIANGL 31 0 6 0 on 4 2 off off off on 0 0 0 0 3 0 3 0 1.51 1.49 0.75 9 no no no no 14 9 12 96 W7 W7 W1 W8 3 -3 3 -3 19 19 31 30 12 12 7 11 10 11 15 7 4 7 2 15 5 7 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 87 88 92 67 0 0 0 0 0 5 8 0 5 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 BellyStuff 22 FIFTH 6 SHOLD 12 0 6 0 on 6 0 off off off off 0 0 0 0 6 3 7 3 1 8.48 0.5 3.46 no no no no 8 8 8 8 W1 W2 W1 W1 3 -3 0 0 30 27 29 29 2 15 15 4 14 14 14 13 3 0 3 1 3 3 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 81 1 1 0 0 0 88 87 0 -24 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 Big Bell.1 23 FIFTH 6 SHOLD 12 0 6 0 on 6 0 off off off off 0 0 0 0 5 4 5 4 1 5.65 4 6.92 no no no no 8 8 8 8 W1 W1 W2 W3 3 -3 0 0 30 27 29 29 2 15 15 4 14 14 14 13 3 0 3 1 3 3 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 87 99 82 1 1 0 0 0 88 87 0 -24 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 Big Bell.2 24 FIFTH 6 SHOLD 12 0 6 0 on 6 0 off off off off 0 0 0 0 5 3 6 1 0.5 0.5 2 7.85 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 0 0 30 27 29 29 2 15 15 4 14 14 14 13 3 0 3 1 3 3 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 99 81 1 1 0 0 0 88 87 0 -24 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 Big Bell.3 25 FIFTH 3 TRIANGL 30 6 17 9 off 5 2 off off off on 0 0 0 0 2 2 2 2 1 3 4 11 no no no no 8 8 8 8 W1 W1 W1 W2 -3 -3 3 3 11 10 11 11 31 7 31 4 15 15 15 15 2 4 2 5 5 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 71 99 85 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 40 0 0 0 50 0 0 0 0 0 4 BowedBell 26 FIFTH 2 TRIANGL 26 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 6 2 2 7 2 7 no no no no 8 8 8 8 W1 W1 W1 W4 -3 -3 3 3 26 31 26 31 31 14 31 14 15 14 15 13 7 5 7 5 4 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 94 88 96 99 1 0 1 0 0 26 0 39 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 BrightBell 27 FIFTH 0 SHOLD 84 0 71 0 off 5 0 off off off off 0 0 0 0 2 1 2 1 5 12.72 1 5.99 no no no no 8 8 8 12 W1 W5 W5 W1 0 0 -1 0 13 28 14 28 9 7 4 8 12 14 10 14 0 0 0 0 2 1 2 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 88 99 85 1 1 1 0 1 46 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Brthbells 28 THIRD 7 TRIANGL 99 63 49 44 off 5 3 off on off off 0 0 0 0 0 0 0 0 1.93 1.41 0.93 1 no no no no 23 8 15 8 W1 W2 W1 W1 3 3 -3 -3 23 24 31 31 31 16 4 31 15 1 11 15 9 28 16 1 8 14 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 84 64 43 47 1 0 0 1 0 36 44 34 -12 POLY 7 FULL 0 POR 99 0 0 99 0 50 0 50 0 0 0 0 0 0 Carnival! 29 FIFTH 5 SAWUP 25 42 14 0 off 1 0 off off off off 0 0 0 0 7 4 7 1 1 1.41 1 23.55 no no no yes 8 8 8 8 W2 W1 W2 W1 0 -3 0 0 28 28 28 28 9 9 9 9 4 4 4 4 31 31 31 31 3 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 90 91 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 ChurchBell 30 SECOND 7 TRIANGL 35 0 8 0 off 4 0 off off off off 0 0 0 0 1 0 1 1 1.06 0.68 2.06 0.56 no no no no 9 11 9 9 W1 W1 W2 W4 0 1 0 0 26 24 22 25 5 24 7 22 0 3 6 7 7 3 6 0 3 3 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 96 60 67 1 1 0 0 0 34 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 3 ClickChime 31 FIFTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1.5 5.25 1.51 5.29 no no no no 16 12 14 14 W1 W1 W1 W1 3 1 -3 -1 31 31 30 30 9 5 9 4 0 0 0 0 10 27 22 31 3 4 3 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 75 1 1 1 1 0 0 0 0 -7 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Cloches * 32 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 21.88 12.25 19.56 11.13 yes yes yes yes 31 68 62 55 W1 W1 W1 W1 0 0 0 0 31 31 31 31 18 22 18 10 15 11 7 12 18 0 16 31 8 4 8 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 95 67 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Cow Bell 33 FIFTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 0 3 0 1 5 1 20.7 no no no yes 8 8 8 51 W1 W5 W1 W3 0 0 0 0 26 31 26 31 16 20 14 23 0 0 0 0 31 31 31 31 8 10 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 99 81 1 0 2 0 0 0 0 0 0 POLY 1 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Cow-Belle* 34 THIRD 5 TRIANGL 35 0 0 0 on 5 0 off off off off 0 0 0 0 6 5 6 4 1 9 3.46 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 22 22 31 31 7 7 15 15 2 2 4 4 6 6 4 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 81 65 1 1 1 1 0 0 31 31 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Gong.....1 35 THIRD 6 TRIANGL 25 0 0 64 on 5 1 on off off off 0 0 0 0 5 3 7 5 1 9 3.46 14.1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 24 12 24 2 9 9 9 12 14 3 3 12 5 14 7 6 6 3 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 80 59 1 1 1 1 0 0 31 31 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Gong.....2 36 THIRD 6 TRIANGL 11 0 13 64 on 6 1 on off off off 0 0 0 0 5 4 6 4 0.5 0.78 0.87 0.87 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 7 19 25 31 31 31 31 14 15 15 15 3 2 2 2 3 2 2 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 74 53 1 1 1 1 0 0 31 31 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Gong.....3 37 FIFTH 0 TRIANGL 82 0 7 0 off 6 0 off off off off 0 0 0 0 3 4 3 4 1 1.41 1 2.29 no no no no 8 8 8 18 W1 W1 W1 W1 -3 3 3 0 24 7 16 11 24 4 6 4 14 13 7 11 3 0 4 1 1 1 2 1 VOF VOF VOF VOF 99 99 99 50 50 50 94 71 99 67 2 2 2 0 0 0 0 22 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Gong Ageng 38 FOURTH 5 SHOLD 99 32 80 0 on 7 0 off off off off 0 0 0 0 2 0 1 3 0.5 0.79 3 1.41 no no no no 8 9 8 8 W1 W5 W1 W8 0 0 0 0 22 31 31 31 9 31 13 31 14 15 10 15 15 0 0 0 3 3 2 4 VOF VOF VOF VOF 99 99 99 50 50 50 90 85 69 83 1 1 1 1 0 0 0 0 0 POLY 3 FULL 0 POR 40 0 0 99 0 0 0 41 0 0 0 0 0 0 Gongs * 39 FIFTH 7 TRIANGL 20 8 2 27 off 5 1 on off on off 0 0 0 0 3 4 3 2 1.49 1.49 1.5 20.31 no no no no 9 9 16 23 W1 W1 W2 W8 -3 3 3 -3 31 31 31 31 8 18 12 13 14 14 12 12 4 2 8 9 7 4 13 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 96 61 2 2 1 2 0 25 0 39 -7 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 HiTine81Z 40 SIXTH 7 TRIANGL 36 35 0 0 on 0 0 off off off off 0 0 0 0 5 4 6 3 1 8 1 7 no no no no 8 8 8 8 W1 W3 W3 W2 -1 0 0 0 31 31 19 12 6 6 6 6 13 13 13 13 6 6 6 10 4 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 87 86 98 68 1 1 1 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 HumorBells 41 FIFTH 0 SQUARE 31 11 4 26 off 5 3 off on off on 0 0 0 0 4 3 4 3 1.5 15 1.49 15 no no no no 16 8 9 8 W1 W7 W1 W8 -3 -3 3 3 31 31 31 31 9 14 9 14 13 9 13 9 8 13 8 13 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 63 95 63 1 0 1 0 0 0 0 0 5 POLY 2 FULL 0 POR 99 0 0 10 80 0 0 50 0 0 0 0 0 0 KeyBell 42 FIFTH 6 SHOLD 12 0 6 0 on 6 0 off off off off 0 0 0 0 5 6 6 4 1.41 10 3.46 3.46 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 0 0 30 27 29 29 2 15 15 4 14 14 14 13 3 0 3 1 3 3 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 84 1 1 0 0 0 88 87 0 -24 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 Large Ben 43 THIRD 2 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 3 4 3 4 0.5 0.5 7 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 19 19 22 29 9 28 12 10 14 9 4 3 6 9 2 7 9 4 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 88 3 3 3 0 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 LastTwango 44 FIFTH 7 TRIANGL 20 8 2 27 off 5 1 on off on off 0 0 0 0 3 4 3 2 1.49 1.49 1.5 21.06 no no no no 9 9 16 23 W1 W1 W2 W8 -3 3 3 -3 31 31 31 31 12 4 15 9 14 13 12 12 3 3 8 9 7 4 13 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 96 53 2 2 1 2 0 25 0 39 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 LoTine81Z 45 FIFTH 6 TRIANGL 32 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 1.5 3 1.49 2.99 no no no no 16 8 9 10 W1 W1 W1 W1 -3 3 3 -3 20 31 21 31 14 14 18 6 15 15 15 15 0 0 0 0 7 3 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 79 0 0 0 0 0 0 0 0 -19 MONO 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 MellowSqar 46 FIFTH 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 6 5 5 3 3 4 2 1.41 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 16 16 16 16 5 5 5 5 18 18 18 18 8 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 99 74 0 0 0 2 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Moooo Bell 47 FIFTH 0 TRIANGL 25 0 0 0 off 5 0 off off off off 0 0 0 0 3 4 2 4 1.31 8.97 1 7.85 no no no no 13 11 8 8 W1 W2 W1 W1 -3 -2 3 0 31 31 31 29 12 13 10 15 11 1 1 1 6 0 0 0 4 6 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 99 80 0 1 1 1 0 3 0 6 11 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Mr.Rogers 48 FIFTH 0 SAWUP 12 0 0 15 off 0 0 off on off on 0 0 0 0 0 0 0 0 6 5 6 5 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 -3 -3 17 31 17 31 31 31 31 31 15 15 15 15 5 0 5 0 3 3 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 73 2 1 2 1 99 0 99 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 OhMy!Bells 49 FIRST 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 4 2 5 2 1 2 1 7.07 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 16 16 16 16 5 5 5 5 18 18 18 18 8 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 73 79 0 0 0 2 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 OrntCowBel 50 FIFTH 0 SHOLD 67 0 4 41 off 6 3 off off off on 0 0 0 0 3 4 5 3 0.5 0.5 5 18.84 no no no no 8 8 8 96 W1 W4 W1 W7 -3 3 3 -3 31 31 31 31 22 22 31 31 14 14 14 13 10 15 15 7 6 7 7 4 VOF VOF VOF V24 99 99 99 50 50 50 99 99 99 99 1 1 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 PluckBell 51 SEVENTH 0 TRIANGL 31 0 14 0 on 0 0 off off off off 0 0 0 0 6 5 5 3 2 2 2 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 9 31 31 31 13 17 0 0 15 15 15 15 9 12 0 3 6 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 81 99 1 1 2 0 0 0 0 10 -24 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 SpaceBell1 52 SIXTH 4 TRIANGL 22 0 9 4 on 3 1 on on on on 0 0 0 0 5 4 5 3 14 23.55 20.41 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 24 25 31 31 15 31 31 15 15 15 15 9 10 10 10 7 6 6 6 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 85 1 1 2 0 0 0 0 10 -24 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 SpaceBell2 53 FIFTH 4 TRIANGL 7 0 4 0 off 6 0 off off off off 0 0 0 0 5 6 6 4 1 1.41 7 11 no no no no 8 8 8 8 W2 W1 W2 W1 -1 0 -2 -2 24 21 28 26 24 14 6 4 14 13 7 11 3 0 4 1 1 1 2 1 VOF VOF VOF VOF 99 99 99 50 50 50 94 83 99 79 2 2 2 0 0 0 0 22 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SundayBell 54 FIFTH 7 TRIANGL 34 0 10 0 off 5 0 off off off off 0 0 0 0 1 1 1 1 1.5 22.49 3 25.95 no no no no 16 8 8 8 W3 W3 W1 W4 -3 0 3 0 31 31 31 31 15 21 11 17 10 12 8 8 6 5 6 6 5 1 5 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 99 77 0 0 0 0 0 30 0 70 -7 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Perc. 1 55 FIFTH 7 TRIANGL 32 0 18 0 off 4 0 off off off off 0 0 0 0 2 0 2 0 2 13 0.5 6 no no no no 8 8 8 8 W3 W2 W1 W2 -2 -2 2 2 27 31 26 31 9 7 8 8 0 0 0 0 0 0 0 0 5 1 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 95 57 92 50 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sy.Perc. 2 56 FIFTH 7 TRIANGL 31 0 0 0 off 5 0 on off off off 0 0 0 0 0 0 0 0 1 1 1 2 no no no no 8 8 8 8 W1 W7 W6 W1 3 3 -3 -3 22 31 26 31 21 26 22 20 14 4 12 0 7 8 12 0 7 1 9 4 VOF VOF VOF VOF 99 99 99 50 50 50 94 60 91 77 2 0 2 0 0 0 0 30 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Sy.Perc. 3 57 FIFTH 0 TRIANGL 26 0 0 0 off 5 0 off off off off 0 0 0 0 3 4 2 4 1.5 10.37 1.49 10.38 no no no no 16 14 9 8 W1 W5 W1 W4 -3 -3 3 3 31 30 30 31 31 10 29 5 15 2 15 4 10 1 10 0 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 92 83 92 81 1 1 1 1 0 30 0 30 5 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Sy.Perc. 4 58 SIXTH 0 SHOLD 35 0 0 0 off 0 0 off off off off 0 0 0 0 5 5 5 5 0.5 2 2 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 -1 3 31 31 31 31 6 6 6 6 14 14 14 14 6 6 6 6 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 91 84 0 0 0 0 0 0 0 0 -24 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 SynthBell 59 FIFTH 2 TRIANGL 26 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 6 2 2 7 2 7 no no no no 8 8 8 8 W1 W1 W1 W4 -3 -3 3 3 26 31 26 31 31 14 31 14 15 14 15 10 7 5 7 5 4 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 94 67 96 80 1 0 1 0 0 26 0 39 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Tube Bell 60 FIFTH 4 TRIANGL 7 0 4 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1.41 1 1.41 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 0 24 21 28 26 24 14 6 4 14 13 7 11 3 0 4 1 1 1 2 1 VOF VOF VOF VOF 99 99 99 50 50 50 94 76 99 71 2 2 2 0 0 0 0 22 12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Tube Bells 61 Block 1 THIRD 7 SHOLD 99 0 99 0 off 6 0 off off off off 0 0 0 0 0 1 1 0 14.13 1.41 13 23.55 no no no no 8 8 8 8 W1 W2 W1 W1 3 -3 3 2 31 31 31 31 17 0 19 3 15 0 11 15 10 31 11 26 6 3 3 15 VOF VOF VOF VOF 99 99 99 50 50 50 92 64 81 62 0 0 0 0 0 0 0 0 -10 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 "Air"imba 0 FIFTH 0 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 2 4 2 7 1.5 5.02 1.51 8.63 no no no yes 16 17 14 72 W1 W1 W1 W1 -3 -3 -2 0 31 29 28 31 11 18 8 23 0 4 9 3 24 26 9 24 6 8 4 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 99 78 2 0 2 0 20 35 36 34 -7 POLY 2 FULL 0 POR 40 0 0 0 0 0 0 50 0 0 0 0 0 0 Balafon * 1 FIFTH 6 TRIANGL 82 0 7 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 3.89 1 1.83 no no no no 8 12 8 9 W1 W1 W1 W1 1 -1 -1 1 31 31 26 23 16 23 18 25 10 11 11 12 16 21 22 21 9 10 10 8 VOF VOF VOF VOF 99 99 99 50 50 50 90 85 92 87 0 0 1 1 0 2 1 2 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 BamboBlock 2 FIFTH 0 TRIANGL 35 0 2 0 on 1 0 off off off off 0 0 0 0 6 2 7 3 1 11 2 7 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 -1 19 18 18 21 31 31 15 18 15 15 12 12 9 18 12 15 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 94 91 81 0 1 2 1 0 39 17 82 0 POLY 1 FULL 0 POR 99 0 0 0 99 0 0 50 0 0 0 0 0 0 BamboChiff 3 FIFTH 3 TRIANGL 44 99 4 0 on 2 0 off off off off 0 0 0 0 5 2 4 2 3 11 6 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 1 1 18 18 31 31 10 18 10 18 0 9 12 0 7 6 6 7 6 5 6 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 99 76 3 3 3 3 0 0 0 2 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 BamboMar.1 4 FIFTH 3 TRIANGL 21 0 0 0 on 0 0 off off off off 0 0 0 0 5 5 5 4 1 7 1 5 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 1 1 18 18 27 22 10 18 10 13 0 9 12 12 7 6 10 10 6 5 6 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 99 77 3 2 3 3 0 0 0 2 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 BamboMar.2 5 FIFTH 7 TRIANGL 21 0 0 0 on 0 0 off off off off 0 0 0 0 5 4 5 1 1 7 1 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 1 1 18 18 27 22 10 18 10 13 0 9 12 12 7 6 10 10 6 5 6 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 99 72 3 3 3 3 0 0 0 2 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 BamboMar.3 6 THIRD 5 TRIANGL 23 12 49 0 off 3 0 off off off off 0 0 0 0 2 0 3 2 0.5 1.05 1.06 0.5 no no no no 8 12 9 8 W1 W2 W8 W2 2 0 0 -3 26 28 23 26 10 18 27 6 0 4 4 0 31 15 15 31 5 5 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 80 60 1 2 2 2 0 0 0 70 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Bamboo * 7 FIFTH 3 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.5 12.72 0.5 11.24 yes yes yes yes 8 64 8 72 W1 W1 W1 W2 3 -3 -3 3 31 24 31 25 21 22 21 22 0 0 0 0 21 31 0 31 12 3 12 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 99 82 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Castanet 8 FOURTH 0 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 1 1 1 0 1 0.5 4 1 no yes no no 8 96 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 28 29 22 31 15 8 7 15 17 13 15 0 8 7 11 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 61 70 0 0 0 0 0 0 0 0 0 12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Claves * 9 FIFTH 7 SAWUP 50 35 0 82 on 4 0 on on on on 0 0 0 0 5 5 6 4 1 1 1 11 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 31 31 31 31 6 24 6 24 12 12 12 12 6 16 6 16 7 1 7 1 VOF VOF VOF VOF 99 99 99 50 50 50 94 89 94 98 1 1 1 1 14 99 14 99 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 MarimbLead 10 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 0 1 3 1 2 1 5 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 -3 3 31 31 31 31 13 0 15 3 0 15 0 15 0 21 0 18 6 5 5 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 63 96 71 1 0 1 0 0 0 0 0 0 POLY 4 FULL 0 POR 0 0 0 50 0 0 0 50 0 0 0 0 0 0 Marimba * 11 FIFTH 0 TRIANGL 35 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 2 6 0.5 3 2 4.71 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 22 26 23 31 31 24 31 22 15 5 15 5 10 12 10 14 5 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 82 98 82 1 1 1 1 0 12 0 21 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Marimba 12 THIRD 0 TRIANGL 30 9 0 0 off 5 0 off off off off 0 0 0 0 4 5 5 4 4 8.72 18.37 8.01 no no yes no 8 11 8 19 W1 W1 W6 W1 0 0 0 0 31 31 31 31 31 15 31 18 15 8 15 15 10 16 18 14 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 75 73 2 2 3 3 0 99 59 72 -24 POLY 4 FULL 0 POR 99 0 0 40 0 0 0 50 0 0 0 0 0 0 Marimba* 13 THIRD 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 6 5 5 5 1 6 7 13 no no no no 8 8 8 8 W1 W3 W1 W1 0 0 0 0 31 31 31 31 12 18 13 27 0 0 0 0 0 0 0 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 69 68 99 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Marimba..1 14 FIFTH 5 TRIANGL 21 0 0 0 off 5 0 off off off off 0 0 0 0 5 4 6 5 1 6 1 10 no no no no 8 8 8 8 W1 W1 W1 W4 -3 3 3 -3 31 31 31 27 10 17 10 25 0 0 0 0 0 0 0 0 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 91 0 1 1 1 17 0 17 0 -12 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 5 Marimba..2 15 FIFTH 5 TRIANGL 21 0 0 0 off 5 0 off off off off 0 0 0 0 6 3 6 4 1 7 1 8 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 -3 31 31 31 27 10 17 10 25 0 0 0 0 0 0 0 0 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 73 99 76 0 1 1 1 17 0 17 0 -24 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Marimba..3 16 FIFTH 5 TRIANGL 21 0 0 0 off 5 0 off off off off 0 0 0 0 6 4 5 4 1 5 1 3 no no no no 8 8 8 8 W1 W2 W1 W1 -3 3 3 -3 31 31 31 22 10 17 10 13 0 0 0 0 0 0 0 5 7 7 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 98 80 99 88 0 1 1 3 17 0 17 0 -12 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Marimba..4 17 THIRD 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 7 5 7 5 0.5 0.5 5 13 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 12 18 13 27 0 0 0 0 0 0 0 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 97 77 70 93 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Marimba..5 18 THIRD 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 6 5 6 5 1 1 7 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 12 18 13 31 0 0 0 4 0 0 0 2 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 68 98 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Marimba..6 19 FOURTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 3 6 5 2 3 1 8 no no no no 8 8 8 8 W1 W2 W1 W1 0 0 0 0 31 31 31 31 12 18 13 31 0 0 0 3 0 0 0 2 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 66 99 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Marimba..7 20 SEVENTH 7 SHOLD 30 40 36 15 on 2 1 on on on on 0 0 0 0 6 2 4 1 1 1 1 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 24 16 16 16 24 13 13 13 12 18 9 9 12 5 7 4 15 VOF VOF VOF VOF 99 99 99 50 50 50 98 64 99 81 2 2 3 3 1 46 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Ornt Block 21 FIRST 7 SHOLD 58 0 99 80 off 5 3 off off on on 0 0 0 0 0 0 0 0 5 12.72 3 5.19 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 -1 0 31 20 31 10 15 3 27 20 0 14 10 11 0 0 17 0 11 15 5 1 VOF VOF VOF VOF 99 99 99 50 50 50 90 65 63 45 1 1 1 0 1 46 0 0 -13 POLY 5 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 SynGameran 22 FIFTH 7 SAWUP 50 35 0 82 on 4 0 on on on on 0 0 0 0 6 5 5 3 1 0.5 1 20.41 no no no no 8 8 8 8 W2 W2 W1 W1 0 0 0 0 15 10 31 31 2 1 9 24 14 15 12 12 7 7 15 16 7 7 7 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 99 93 0 0 1 1 0 0 14 99 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 SynthMarib 23 THIRD 0 TRIANGL 24 0 38 0 off 3 0 off off off off 0 0 0 0 3 0 0 3 0.5 0.5 22.49 4.37 no no yes no 8 8 8 14 W1 W1 W1 W1 0 0 0 0 19 25 31 25 16 31 5 28 10 6 15 10 17 27 0 23 6 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 1 1 1 1 0 0 0 0 0 POLY 12 FULL 0 POR 40 0 0 99 0 0 0 19 0 0 0 0 0 0 Tablas * 24 FIFTH 5 TRIANGL 82 0 7 0 off 6 0 off off off off 0 0 0 0 1 2 1 2 1 3.62 1 1.83 no no no no 8 18 8 9 W1 W1 W1 W2 0 0 0 0 31 31 31 31 18 24 21 24 0 12 10 0 19 18 19 1 9 10 10 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 85 1 1 1 1 0 0 0 0 12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Wood Block 25 FIFTH 0 TRIANGL 14 0 99 0 on 4 0 off off off off 0 0 0 0 1 3 2 0 1.49 5.59 1.51 4.12 no no no no 9 17 14 18 W1 W5 W1 W1 0 0 0 0 31 31 26 31 31 31 15 23 15 15 0 0 12 22 0 0 6 7 7 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 75 2 1 1 1 0 32 0 0 -7 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 WoodBlock* 26 FIFTH 0 TRIANGL 48 0 0 0 on 6 0 off off off off 0 0 0 0 5 0 5 0 1 1 1 3.46 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 16 16 22 18 13 13 12 10 18 18 16 15 9 9 8 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 69 99 77 1 1 2 2 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 WoodBlock1 27 THIRD 0 TRIANGL 48 0 0 0 on 6 0 off off off off 0 0 0 0 5 2 4 1 1 1 1 3.46 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 16 16 22 18 13 13 12 10 18 18 16 15 9 9 8 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 55 66 77 1 1 2 2 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 WoodBlock2 28 THIRD 0 TRIANGL 48 0 0 0 on 6 0 off off off off 0 0 0 0 5 1 5 0 1 1.41 1.73 3.46 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 16 16 22 18 13 13 12 10 18 18 16 15 9 9 8 12 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 72 68 1 1 2 2 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 WoodBlock3 29 Chime 2 FIFTH 0 TRIANGL 26 0 0 0 off 5 0 off off off off 0 0 0 0 3 4 2 4 1.5 10.37 1.49 10.38 no no no no 16 14 9 8 W1 W3 W1 W3 -3 -3 3 3 31 31 31 31 11 12 11 12 14 1 14 1 9 0 9 0 5 6 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 99 80 1 1 1 1 0 10 0 10 5 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 4 BriteCelst 0 FIFTH 0 TRIANGL 25 0 0 0 off 5 0 off off off off 0 0 0 0 3 4 2 4 1 7 1 1.73 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 -3 31 31 31 25 13 16 10 28 1 1 1 1 0 0 0 0 4 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 99 82 0 1 1 1 0 0 0 0 12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Celeste 1 FIFTH 0 TRIANGL 25 0 0 0 off 5 0 off off off off 0 0 0 0 5 5 5 3 1 8 1 15.55 no no no no 8 8 8 8 W4 W1 W1 W1 -3 3 3 -3 31 31 31 25 13 16 10 28 1 1 1 1 0 0 0 0 4 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 60 99 81 0 1 1 2 0 0 0 0 12 POLY 0 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Celeste..1 2 FIFTH 0 TRIANGL 25 0 0 0 off 5 0 off off off off 0 0 0 0 6 5 4 1 1 2 4 10 no no no no 8 8 8 8 W1 W1 W4 W1 -3 3 3 -3 31 31 31 31 13 16 10 21 1 1 1 0 0 0 0 18 4 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 99 59 0 1 1 2 0 0 0 0 12 POLY 0 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Celeste..2 3 FIFTH 5 SHOLD 41 0 0 0 on 0 2 on off on off 0 0 0 0 0 1 2 2 2.5 5.19 2.5 5 no no no no 16 8 16 8 W1 W4 W1 W1 2 -2 1 -2 23 31 25 31 31 31 31 31 13 13 13 13 7 9 7 7 3 2 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 83 92 1 1 1 1 0 17 0 18 12 POLY 2 FULL 0 POR 40 0 0 0 54 0 0 50 0 0 0 0 0 0 Chimes * 4 THIRD 0 TRIANGL 25 2 1 22 off 5 2 off on off on 0 0 0 0 4 3 7 3 1.5 7.43 23.55 15 no no no no 16 15 8 8 W1 W1 W5 W7 0 3 0 0 31 31 31 31 8 5 24 14 13 13 0 0 10 7 0 2 5 2 2 1 VOF V48 VOF VOF 99 99 99 50 50 50 99 77 76 61 1 0 0 0 0 0 0 0 -7 POLY 2 FULL 0 POR 99 0 0 40 0 0 0 50 0 0 0 0 0 0 Chiming 5 SIXTH 7 TRIANGL 33 0 10 0 off 3 0 off off off off 0 0 0 0 7 7 7 7 15 10 2 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 14 10 12 29 0 0 0 0 1 0 0 0 6 6 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 91 99 99 99 0 0 0 0 0 10 15 0 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Glocken 6 SEVENTH 4 TRIANGL 35 0 0 0 off 3 3 on off off off 0 0 0 0 3 4 5 2 4 1 1 14 no no no no 8 8 8 8 W1 W4 W1 W1 -3 -3 3 3 31 31 24 31 17 14 17 19 15 9 15 10 8 5 8 4 5 2 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 61 99 77 0 0 1 0 5 0 10 35 0 POLY 5 FULL 0 POR 99 0 0 99 40 0 0 50 0 0 0 0 0 0 Glocken..1 7 FIFTH 4 TRIANGL 35 0 0 0 off 3 3 on off off off 0 0 0 0 6 6 5 2 1 8 1 15 no no no no 8 8 8 8 W1 W1 W1 W1 -3 -3 3 3 31 31 30 31 17 14 18 19 15 9 12 10 8 5 10 4 5 2 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 57 99 76 0 0 1 1 5 0 10 35 12 POLY 5 FULL 0 POR 99 0 0 99 40 0 0 50 0 0 0 0 0 0 Glocken..2 8 FIFTH 0 TRIANGL 26 0 8 0 off 0 3 on on on on 0 0 0 0 1 4 1 7 1 3 1 14 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 -3 3 20 25 20 31 26 26 26 15 13 13 13 13 7 12 7 15 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 65 99 58 1 2 1 2 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 23 59 0 0 50 0 0 0 0 0 0 GoodVibes* 9 EIGHTH 0 TRIANGL 22 0 0 42 off 6 1 on on on on 0 0 0 0 3 3 3 3 1 14 4 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 3 31 31 31 31 20 16 20 20 13 0 13 13 7 21 11 7 7 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 1 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 8 50 0 0 50 0 0 0 0 0 0 GreatVibes 10 SEVENTH 3 TRIANGL 25 19 7 92 on 3 1 on on on on 0 0 0 0 5 4 6 5 2 4 24.22 25.95 no no no no 8 8 8 8 W1 W1 W2 W3 2 -2 0 0 14 15 31 31 8 8 31 15 9 9 15 0 13 13 11 8 6 6 6 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 91 0 0 0 0 0 0 0 0 -24 POLY 12 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Mod Chime 11 FIFTH 1 TRIANGL 35 0 3 10 on 4 2 on on on on 0 0 0 0 5 5 5 4 2 2 10 4 no no no no 8 8 8 8 W1 W2 W2 W3 1 -2 1 2 16 13 31 0 5 10 9 31 3 1 2 13 9 31 8 16 6 3 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 96 99 1 1 1 1 10 10 10 10 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 SynthChime 12 SIXTH 0 TRIANGL 35 0 0 0 on 6 0 off off off off 0 0 0 0 5 3 5 5 2 2 15.57 11 yes no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 31 31 31 15 14 15 14 12 12 12 12 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 64 99 57 0 0 0 2 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Triangle.1 13 SEVENTH 0 TRIANGL 35 0 0 0 on 6 0 off off off off 0 0 0 0 6 4 6 3 2 6 14 11 no yes no yes 8 8 8 32 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 13 31 31 15 0 15 14 12 31 12 12 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 99 48 0 0 0 2 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Triangle.2 14 SIXTH 5 TRIANGL 39 0 0 0 off 4 0 off off off off 0 0 0 0 5 4 7 4 1 1 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 24 22 23 21 22 10 10 3 15 15 15 14 7 7 7 7 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 81 3 3 3 3 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Vib Lead.1 15 FIFTH 5 TRIANGL 39 0 0 0 off 4 0 off off off off 0 0 0 0 6 5 5 4 1 14 1 2 no no no no 8 8 8 8 W1 W3 W1 W3 0 0 0 0 31 31 23 21 7 15 10 3 14 11 15 14 7 10 7 7 10 12 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 99 81 1 2 3 1 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Vib Lead.2 16 FIFTH 0 TRIANGL 31 0 0 31 off 3 3 on off off off 0 0 0 0 2 2 2 2 1 5 1 12 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 -3 31 31 31 31 17 14 17 15 15 9 15 10 5 8 5 14 4 6 4 10 VOF VOF VOF VOF 99 99 99 50 50 50 97 54 97 54 2 0 2 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 30 0 0 50 0 0 0 0 0 0 Vibe. 17 FIFTH 7 TRIANGL 21 0 0 0 on 2 0 off off off off 0 0 0 0 5 5 2 3 1 7 1 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 -1 31 27 31 22 9 22 10 13 12 7 12 13 7 0 10 10 6 12 13 10 VOF VOF VOF VOF 99 99 99 50 50 50 94 87 96 59 1 2 1 2 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 8 50 0 0 50 0 0 0 0 0 0 Vibe.....1 18 EIGHTH 0 TRIANGL 22 0 0 42 off 6 1 on on on on 0 0 0 0 7 5 4 4 1 12 4 2 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 3 31 31 31 31 20 16 20 20 13 0 13 13 7 21 11 7 7 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 1 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 8 50 0 0 50 0 0 0 0 0 0 Vibe.....2 19 EIGHTH 0 TRIANGL 22 0 0 42 off 6 1 on on on on 0 0 0 0 7 4 5 2 1 7 1 4 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 3 31 31 31 31 20 16 20 20 13 0 13 13 7 21 11 7 7 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 1 0 2 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 8 50 0 0 50 0 0 0 0 0 0 Vibe.....3 20 SIXTH 5 TRIANGL 33 0 0 99 on 0 0 on on on on 0 0 0 0 7 5 4 3 1 4 1 12 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 3 31 27 27 18 21 9 9 6 12 13 13 4 6 3 3 31 8 6 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 58 0 1 1 2 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 8 50 0 0 50 0 0 0 0 0 0 Vibe.....4 21 SIXTH 5 TRIANGL 33 0 0 99 on 0 0 on on on on 0 0 0 0 6 4 4 3 1 7 1 5 no no no no 8 8 8 8 W2 W1 W3 W1 -3 3 3 3 31 27 27 18 21 9 9 6 12 13 13 4 6 3 3 31 8 6 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 99 57 1 3 3 3 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 8 50 0 0 50 0 0 0 0 0 0 Vibe.....5 22 EIGHTH 0 TRIANGL 22 0 0 42 off 6 1 on on on on 0 0 0 0 7 5 5 4 2 12 4 4 no no no no 8 8 8 8 W1 W2 W1 W3 -3 3 3 3 31 31 31 31 20 16 20 20 13 0 13 13 7 21 11 7 7 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 1 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 8 50 0 0 50 0 0 0 0 0 0 Vibe.....6 23 FIFTH 7 TRIANGL 35 0 0 0 off 4 0 off off off off 0 0 0 0 3 7 0 5 1 6 1 3 no no no no 8 8 8 8 W1 W1 W1 W2 -3 1 3 0 27 31 28 27 26 20 27 10 14 0 14 0 10 18 11 31 5 1 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 99 84 2 1 2 1 11 0 11 21 -12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Vibesyn * 24 SEVENTH 0 SHOLD 45 0 5 6 off 3 1 on on on on 0 0 0 0 7 4 4 3 11 11 11 15 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 6 31 31 18 15 18 18 11 11 11 11 9 9 9 9 4 2 2 3 VOF VOF VOF VOF 99 99 99 50 50 50 98 99 99 82 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 WindChime1 25 SEVENTH 1 SHOLD 78 0 56 0 off 5 0 off off off off 0 0 0 0 6 3 5 2 11 12.72 2.82 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -1 0 17 28 14 28 13 18 16 12 12 14 10 14 0 0 0 0 3 2 2 3 VOF VOF VOF VOF 99 99 99 50 50 50 98 80 89 80 1 1 1 0 1 46 0 0 -12 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 WindChime2 26 SIXTH 6 SQUARE 41 44 0 54 on 0 2 on on on off 0 0 0 0 1 1 2 1 1 1 2 5 no no no no 8 8 8 8 W1 W1 W1 W1 -3 0 3 3 31 31 25 31 17 18 16 14 14 14 13 14 12 13 14 14 5 5 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 75 75 2 2 2 2 0 0 32 17 0 POLY 2 FULL 0 POR 40 0 0 0 49 0 0 50 0 0 0 0 0 0 Xylophone.1 27 THIRD 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 1 1 1 1 5 1 2 no no no no 8 8 8 8 W1 W7 W1 W1 0 0 0 0 31 31 31 31 31 23 31 22 15 5 15 5 15 16 18 0 7 5 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 99 90 0 2 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Xylophone.2 28 FIFTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 3 1 2 3 8 1 10 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 31 31 31 15 15 15 15 15 22 15 22 7 9 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 98 80 98 65 2 1 1 2 0 0 0 38 12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Xylophone.3 29 FIFTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 3 1 2 4 8 1 10 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 31 31 31 15 15 15 15 15 22 15 22 7 9 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 98 80 98 65 2 1 1 2 0 0 0 38 12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Xylophone.4 30 SIXTH 6 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 5 7 4 0.5 1 3.46 6 no no no no 8 8 8 8 W1 W2 W1 W1 0 3 0 3 27 27 31 31 16 16 13 13 14 14 14 14 6 6 7 7 6 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 99 83 1 2 3 3 0 0 0 0 12 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 Xylophone1 31 FIFTH 6 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 5 7 3 1 2 1 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -2 -1 31 31 31 31 10 18 12 18 13 13 13 13 6 7 6 7 7 4 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 93 99 79 1 3 1 2 0 25 0 20 12 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 Xylophone2 32 FIFTH 6 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 3 7 3 1 14 1 4 no no no no 8 8 8 8 W1 W2 W1 W2 0 3 -2 -1 31 29 31 22 9 22 12 13 12 7 13 12 7 0 6 5 6 12 7 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 99 79 1 3 2 2 0 25 0 20 12 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 Xylophone3 33 SIXTH 6 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 6 7 4 0.5 2 3.46 2 no no no no 8 8 8 8 W2 W1 W1 W1 0 3 0 3 27 27 31 31 16 16 13 13 14 14 14 14 6 6 7 7 5 5 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 64 1 1 3 3 0 0 0 0 12 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 Xylophone4 34 SIXTH 1 TRIANGL 35 0 25 0 on 3 0 off off off off 0 0 0 0 5 4 5 4 1 6 0.5 13 no no no no 8 8 8 8 W1 W2 W2 W1 -1 1 2 -2 31 31 31 31 17 14 31 19 15 9 15 10 8 5 0 4 5 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 62 65 1 1 0 0 0 0 0 0 12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Xylophone5 35 SIXTH 1 TRIANGL 35 0 25 0 on 3 0 off off off off 0 0 0 0 4 4 4 3 1 2 4 15.7 no no no no 8 8 8 8 W1 W1 W1 W1 -1 1 2 -2 31 31 31 31 17 14 31 14 15 9 15 10 8 5 0 9 6 6 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 59 82 1 1 1 3 0 0 0 0 12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Xylophone6 36 FIFTH 1 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 7 6 4 5 0.5 1 1 6 no no no no 8 8 8 8 W1 W1 W1 W1 -1 1 2 -2 31 18 31 31 17 18 31 14 15 9 15 10 8 5 0 9 6 6 9 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 95 85 1 3 3 3 0 0 0 0 12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Xylophone7 37 FIFTH 6 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 5 7 4 1 14 1 6 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 -1 28 31 31 22 15 21 10 13 12 7 12 13 6 0 5 5 6 12 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 79 2 1 1 2 0 0 0 0 12 POLY 2 FULL 0 POR 99 0 0 17 6 0 0 50 0 0 0 0 0 0 Xylophone8 38 Cymbal 3 FOURTH 7 TRIANGL 31 13 14 30 on 0 3 off on off off 0 0 0 0 4 1 1 0 0.5 3 6.28 1.18 no no no no 8 8 8 19 W1 W1 W2 W8 3 -3 3 -3 29 30 31 31 11 0 0 0 0 0 0 0 31 1 0 0 4 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 86 68 0 0 0 0 10 31 0 11 0 POLY 12 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Chin.Cymb* 0 SECOND 6 TRIANGL 0 2 0 3 off 5 0 on on on on 0 0 0 0 5 0 0 0 8.65 0.87 21.98 18.84 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 25 21 31 31 22 28 31 8 11 11 14 5 0 6 0 2 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 87 89 2 1 2 0 0 0 0 0 -6 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Cymbal...1 1 SECOND 6 TRIANGL 0 2 0 3 off 5 0 on on on on 0 0 0 0 0 0 0 0 8.65 0.87 21.98 19.03 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 25 21 31 31 22 28 31 8 11 11 14 5 0 6 0 2 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 87 89 2 1 2 0 0 0 0 0 -6 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Cymbal...1* 2 SECOND 6 TRIANGL 0 2 0 3 off 5 0 on on on on 0 0 0 0 4 0 0 0 8.65 10.13 8 12 no no no no 8 11 8 8 W1 W1 W1 W1 0 0 0 0 31 25 21 31 31 22 28 31 8 11 11 14 5 0 6 0 2 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 86 89 2 1 2 0 0 0 0 0 -6 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Cymbal...2 4 SECOND 6 TRIANGL 0 2 0 3 off 5 0 on on on on 0 0 0 0 0 0 0 0 8.65 9.89 7 12 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 25 21 31 31 22 28 31 8 11 11 14 5 0 6 0 2 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 86 89 2 1 2 0 0 0 0 0 -6 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Cymbal...2* 5 FOURTH 6 TRIANGL 0 2 0 3 off 5 0 on on on on 0 0 0 0 4 1 0 0 6.92 7.07 10.38 19.78 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 25 21 31 31 22 28 31 8 11 11 14 5 0 6 0 2 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 91 91 2 1 2 0 0 0 0 0 -6 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Cymbal...3 7 Drum 4 FIFTH 6 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 0 5 0 0.5 0.5 13 17.27 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 18 31 31 15 7 15 15 11 0 16 0 8 5 7 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 99 99 0 0 2 0 0 0 99 0 -24 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Bass/Snare 0 FOURTH 7 SHOLD 99 0 0 0 off 0 0 off off off off 0 0 0 0 0 1 2 0 0.5 0.5 9 15 no no yes yes 8 8 8 8 W1 W1 W1 W1 0 -1 2 3 30 31 31 31 20 18 20 31 0 0 0 0 19 19 19 19 15 2 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 86 95 0 0 0 0 0 0 0 0 -11 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 Bass Drum 1 FOURTH 0 TRIANGL 80 0 74 0 off 7 0 off off off off 0 0 0 0 0 0 0 0 2.31 1 0.71 2 yes no no no 13 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 11 24 21 15 14 8 10 0 18 0 8 31 12 6 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 91 95 73 78 0 0 0 0 0 0 0 0 -24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bass Drum1 2 SECOND 6 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 7.02 1.31 0.81 10.7 yes yes yes yes 9 13 13 19 W1 W1 W1 W1 2 -2 2 -2 31 31 31 31 10 24 21 25 15 10 10 11 21 0 0 7 10 11 12 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 69 70 0 0 0 0 0 0 0 0 -24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Bass Drum2 3 SIXTH 6 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 5 1 6 0 0.78 0.87 0.5 0.87 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 24 27 31 15 16 18 27 15 13 14 14 18 25 16 25 9 12 8 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 85 2 2 2 0 0 0 0 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 BassDrum.1 5 FIFTH 6 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 3 2 5 0 0.5 0.87 0.5 0.87 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 24 27 31 15 27 18 27 15 13 14 14 18 25 16 25 9 12 8 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 99 78 2 2 2 0 0 0 0 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 BassDrum.2 6 THIRD 6 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 3 1 2 0 0.5 0.5 0.78 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 24 27 31 15 27 18 27 15 13 14 14 18 25 16 25 9 12 8 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 85 72 2 2 2 0 0 0 0 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 BassDrum.3 7 FIFTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 2 0 3 0.71 1.43 1 0.62 no no no no 8 15 8 10 W1 W6 W2 W1 0 3 -3 -3 28 24 24 25 12 17 15 21 11 4 0 0 20 9 0 31 6 6 7 14 VOF VOF VOF VOF 99 17 42 50 50 50 99 66 99 82 2 2 2 0 0 30 2 5 5 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 BoConga 8 THIRD 0 TRIANGL 24 0 38 0 off 3 0 off off off off 0 0 0 0 3 0 0 3 0.5 0.5 13 0.87 no no yes no 8 8 8 14 W1 W1 W1 W1 0 0 0 0 19 25 31 25 16 31 5 29 10 6 11 9 17 27 0 23 6 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Congas * 9 THIRD 6 TRIANGL 34 10 9 0 off 0 0 off off off off 0 0 0 0 6 4 5 3 1 2 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 -1 -1 31 31 31 24 24 27 27 25 15 11 11 11 7 31 31 18 7 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 76 1 2 1 3 0 0 0 46 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 DrmSpring1 10 THIRD 7 TRIANGL 34 10 25 0 off 0 0 off off off off 0 0 0 0 7 5 6 3 0.78 1.73 2.82 0.78 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 -1 -1 31 31 31 24 24 27 27 25 15 11 11 11 7 31 31 18 7 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 96 96 99 1 2 1 3 0 0 0 46 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 DrmSpring2 11 FIFTH 5 TRIANGL 33 35 15 0 off 1 0 off off off off 0 0 0 0 5 3 6 2 2 2 2.82 9.89 no no no no 8 8 8 8 W1 W1 W1 W1 3 0 1 2 31 31 21 6 4 10 10 8 0 13 13 13 8 3 4 1 7 6 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 98 81 0 1 0 0 0 0 0 99 -24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 DrumString 12 FIFTH 7 SHOLD 99 0 99 0 off 6 0 off off off off 0 0 0 0 0 1 1 0 1.57 1.41 13 23.55 no no yes yes 8 8 8 8 W1 W1 W1 W1 0 -1 2 3 30 31 31 31 9 16 15 18 9 5 6 5 13 11 14 19 6 2 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 86 95 0 0 0 0 0 0 0 0 8 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 7 Efem Toms 13 FOURTH 0 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 0 0 1 1 0.62 0.5 8.24 4 yes yes yes no 10 8 12 8 W2 W1 W1 W1 0 0 0 0 31 31 27 31 14 3 4 14 0 0 7 0 4 0 10 8 7 1 1 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 84 87 1 1 1 1 0 0 0 0 -24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 El.Snare * 14 FOURTH 2 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 1 0 0 0 3 1 10.81 3.56 no yes yes no 8 8 12 9 W2 W2 W6 W1 0 0 0 0 31 31 31 31 16 3 2 14 0 7 0 0 0 0 1 2 8 3 1 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 68 81 87 1 1 1 1 0 0 0 0 -24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 El.Snare2* 15 FOURTH 7 TRIANGL 0 0 0 0 off 6 0 off off off off 0 7 0 0 2 0 0 0 2 7 7 21.98 no no no no 8 8 8 8 W1 W4 W1 W8 0 0 0 0 31 31 31 31 13 9 12 12 4 15 15 15 31 0 0 0 15 4 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 86 99 0 0 0 0 0 0 0 0 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 FM Hi-Hats 16 SECOND 7 TRIANGL 35 0 8 0 off 4 0 off off off off 0 0 0 0 1 0 1 1 1.06 0.68 2.06 0.56 no no no no 9 11 9 9 W1 W1 W1 W1 0 1 0 0 31 31 29 31 5 24 7 22 0 3 6 7 7 3 6 0 3 3 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 60 80 3 2 0 0 0 34 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 3 Hand Drum 17 SECOND 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 3 1 6 0 0.87 0.87 2.82 0.71 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 29 26 0 24 26 21 15 13 4 9 7 3 6 0 3 4 4 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 65 83 3 2 0 2 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Hand Drum1 18 SECOND 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 0 0 0 0.71 1 0.71 0.71 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 29 26 0 24 26 21 15 13 4 9 7 3 6 0 8 4 4 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 65 87 3 2 0 2 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Hand Drum2 19 FIFTH 7 TRIANGL 99 0 85 70 off 6 0 off off off off 0 0 0 0 0 0 0 0 16.77 12.5 11.67 12 yes yes yes yes 59 104 108 111 W1 W1 W1 W1 0 0 0 0 31 31 31 31 10 9 10 6 0 15 0 15 0 31 0 6 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 89 99 99 99 3 3 3 3 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 "Hi!" Hat! 20 FOURTH 7 TRIANGL 0 0 0 0 off 6 0 off off off off 0 7 0 0 2 0 0 0 2 7 7 21.98 no no no no 8 8 8 8 W1 W4 W1 W8 0 0 0 0 31 31 31 31 19 9 12 12 0 15 15 15 31 0 0 0 15 4 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 94 99 0 0 0 0 0 0 0 0 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Hi-Hat * 21 FIFTH 7 TRIANGL 11 0 8 0 on 6 0 off off off off 0 0 0 0 2 2 1 2 1 5.19 1 7.85 no no no no 8 8 8 8 W1 W1 W1 W1 -3 -3 3 3 26 21 27 21 31 20 31 19 15 12 15 13 14 9 15 13 6 4 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 99 80 0 1 0 1 0 30 0 30 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Java Jive 22 FIRST 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 2 0 5.62 1 0.5 0.78 yes yes yes yes 18 8 8 80 W1 W1 W3 W4 0 0 0 0 31 31 31 31 29 17 25 22 15 0 4 3 16 1 19 0 8 8 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 95 95 1 2 3 0 0 0 0 0 -12 MONO 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 KickDrum * 23 FIFTH 5 TRIANGL 36 0 0 80 off 0 0 on on on on 0 0 0 0 4 1 5 1 0.5 0.5 0.5 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 27 31 24 19 15 24 6 24 0 0 4 4 13 12 15 15 6 5 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 29 1 0 2 3 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Log Drum.1 24 FIFTH 5 TRIANGL 36 0 0 80 off 0 0 on on on on 0 0 0 0 4 1 4 0 0.71 0.87 0.71 1.41 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 27 31 24 19 15 24 6 24 0 0 4 4 13 12 15 15 6 5 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 73 99 93 1 1 2 3 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Log Drum.2 25 FIRST 5 TRIANGL 4 0 69 0 on 6 0 off off off off 0 0 0 0 2 0 1 1 0.5 0.98 0.68 1.73 no no no no 8 10 11 8 W1 W6 W1 W4 0 0 0 0 31 31 31 31 13 10 2 13 13 0 0 0 15 0 0 0 6 2 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 70 64 72 1 1 1 1 0 4 0 33 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 42 0 0 0 0 0 0 MiamiToms* 26 FIFTH 7 TRIANGL 35 0 0 0 on 0 0 off off off off 0 0 0 0 5 2 3 2 1 2 1.73 0.87 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 25 24 29 27 15 15 21 25 4 0 15 13 24 12 21 24 7 9 15 13 VOF VOF VOF VOF 99 99 99 50 50 50 98 90 99 98 1 2 0 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 OrntDrm..1 27 FIFTH 5 TRIANGL 40 0 16 46 off 2 1 off on off off 0 0 0 0 5 2 3 2 0.5 0.5 0.5 0.78 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 0 0 31 31 31 31 12 22 9 1 0 0 11 0 31 0 6 7 6 3 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 86 99 73 2 1 2 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 OrntDrm..2 28 FIFTH 5 TRIANGL 11 0 16 0 off 2 0 off off off off 0 0 0 0 5 4 4 4 0.5 0.5 0.87 0.78 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 0 0 31 31 31 31 12 22 9 1 0 0 11 0 31 0 6 7 6 3 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 85 62 72 2 1 2 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 OrntDrm..3 29 FIFTH 5 TRIANGL 11 0 16 0 off 2 0 off off off off 0 0 0 0 6 3 5 2 2 10.38 0.71 0.71 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 0 0 31 31 31 31 12 22 11 10 0 0 11 0 31 0 9 9 6 3 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 56 89 75 2 1 2 1 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 OrntDrm..4 30 FIFTH 5 TRIANGL 11 0 16 0 off 2 0 off off off off 0 0 0 0 4 3 4 3 1 2 1 16.96 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 0 0 27 31 31 31 12 16 9 13 13 14 13 14 12 12 22 18 6 6 10 7 VOF VOF VOF VOF 99 99 99 50 50 50 98 72 97 67 1 1 1 3 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 OrntDrm..5 31 FIFTH 1 TRIANGL 35 0 0 0 on 2 0 off off off off 0 0 0 0 7 2 4 3 0.5 2.82 0.5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 0 28 15 9 15 9 10 12 9 13 12 9 0 9 5 7 1 7 5 VOF VOF VOF VOF 99 99 99 50 50 50 98 97 99 77 1 0 2 3 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 OrntDrm..6 32 FIFTH 5 TRIANGL 11 0 16 0 off 2 0 off off off off 0 0 0 0 5 3 3 5 0.5 0.71 0.87 0.78 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 0 0 31 31 31 31 12 22 11 10 0 0 11 0 31 0 9 9 6 3 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 83 87 72 2 2 2 2 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 OrntDrm..7 33 THIRD 6 TRIANGL 11 0 16 0 off 2 0 off off off off 0 0 0 0 4 3 6 2 0.5 0.71 1.41 0.78 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 0 0 31 31 31 31 12 22 11 10 0 0 11 0 31 0 9 9 6 3 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 83 87 72 2 2 2 2 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 OrntDrm..8 34 THIRD 6 TRIANGL 11 0 16 0 off 2 0 off off off off 0 0 0 0 4 2 5 3 0.5 0.71 1.41 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 0 0 31 31 31 31 12 22 26 11 0 0 11 0 31 0 9 9 6 3 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 68 95 48 2 2 2 3 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 OrntDrm..9 35 FOURTH 6 TRIANGL 11 0 16 0 off 2 0 off off off off 0 0 0 0 6 4 3 3 0.71 2.82 1.73 0.87 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 0 0 24 24 31 31 15 15 26 11 0 5 11 0 12 24 9 9 8 7 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 98 68 55 48 2 2 2 3 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 OrntDrm.10 36 FIFTH 5 SAWUP 38 28 14 0 off 7 0 off off off off 0 0 0 0 5 4 6 2 0.5 0.5 0.5 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 15 25 18 5 10 6 24 13 13 4 15 3 3 15 15 7 7 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 93 72 99 91 1 0 2 3 0 0 0 99 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntEchoDr 37 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 0 5 0 1.73 0.87 0.5 3.14 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 18 18 31 15 8 10 15 12 12 9 5 6 5 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 90 95 78 90 1 0 0 0 0 70 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Percussive 38 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1.73 0.71 0.5 3.14 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 18 18 31 15 8 10 15 12 12 9 5 6 5 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 90 95 78 90 1 0 0 0 0 70 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Percussive* 39 SECOND 0 TRIANGL 2 0 95 0 on 7 0 off off off off 0 0 0 0 0 0 0 0 0.5 0.68 3.46 0.79 no no no no 8 11 8 9 W1 W3 W5 W1 0 0 0 0 31 31 31 31 16 0 9 13 0 0 0 0 0 0 0 0 7 2 4 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 83 99 1 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Simmons1 * 40 SECOND 0 TRIANGL 2 0 95 0 on 7 0 off off off off 0 0 0 0 0 0 0 0 0.5 0.68 3.46 0.79 no no no no 8 11 8 9 W1 W3 W5 W1 0 0 0 0 31 31 31 31 15 0 9 13 0 0 0 0 0 0 0 0 6 5 1 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 60 75 99 1 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Simmons2 * 41 THIRD 7 TRIANGL 20 0 0 0 off 4 0 off off off off 0 0 0 0 4 3 3 3 1 0.5 3 3.14 no no no no 8 8 8 8 W1 W4 W3 W4 0 0 3 -3 31 31 31 31 15 19 25 25 7 5 5 3 17 5 6 8 9 11 11 10 VOF V24 VOF VOF 99 99 99 50 50 50 99 88 99 99 0 1 1 2 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 7 Skankin' 42 THIRD 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 6.86 2.62 2 17.27 yes yes no yes 14 18 8 96 W1 W1 W3 W4 0 0 0 0 31 31 31 31 26 14 0 0 15 0 0 0 16 0 0 0 8 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 84 99 1 2 3 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Snare * 43 FIFTH 7 SHOLD 99 0 99 0 on 7 0 off off off off 0 0 0 0 2 0 2 0 22.81 0.93 0.5 21.78 yes no yes yes 19 23 8 126 W1 W1 W1 W1 3 -3 -3 3 31 31 31 31 15 21 10 24 10 11 14 15 16 9 18 0 8 3 8 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 90 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Snare 1 44 FIFTH 7 TRIANGL 99 0 48 15 on 6 2 on on on on 0 0 0 0 2 4 2 0 14.22 14.22 16.87 13.46 yes yes yes yes 9 9 20 73 W1 W1 W1 W1 3 -3 -3 3 31 31 27 31 19 21 19 26 14 11 14 15 19 9 19 0 7 4 9 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 99 97 0 3 0 3 0 0 0 0 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Snare 2 45 FOURTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1.41 0.62 3.14 17.27 yes no no yes 8 10 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 20 31 31 15 8 15 15 15 8 16 0 8 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 99 98 1 1 3 0 0 0 12 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Snare 1 46 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 1 0 0 1.41 0.78 3.14 17.27 yes no no no 8 8 8 112 W1 W1 W8 W7 0 0 0 0 31 31 31 31 31 20 4 0 15 8 15 15 15 8 16 0 8 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 1 1 1 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 7 Snare 2 47 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 4 0 5 0 0.5 1.57 1 3.14 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 -3 31 31 19 31 31 26 31 31 15 10 15 15 16 10 17 0 7 5 8 1 VOF VOF VOF VOF 99 99 99 50 50 50 87 88 94 96 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Snare....1 48 FIRST 5 TRIANGL 35 0 0 0 off 5 0 off off off off 0 0 0 0 5 0 5 0 0.78 1.57 1.57 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 19 31 31 26 22 31 15 10 13 15 16 10 17 0 8 5 8 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 99 99 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Snare....2 49 FOURTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 4 0 4 0 0.71 1.57 0.5 17.27 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 21 31 31 15 9 15 15 16 15 18 0 8 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 33 83 99 0 0 1 0 0 20 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Snare....3 50 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 1 6 1 25.95 25.95 0.5 17.27 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 19 31 31 15 5 15 15 15 16 18 0 8 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 67 71 99 99 0 2 1 0 0 20 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Snare....4 51 FIFTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 0 2 0 12.06 9.81 0.62 9.87 yes yes yes yes 17 21 10 22 W1 W1 W1 W1 0 0 0 0 31 31 31 16 18 20 31 26 0 0 15 15 15 0 14 0 6 1 7 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 99 99 0 0 1 0 0 0 0 0 12 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SnareDrm.* 52 FIFTH 7 TRIANGL 35 36 0 0 off 6 0 off off off off 0 0 0 0 0 1 0 0 1.41 0.78 3.14 17.27 yes no no no 8 8 8 112 W1 W1 W8 W4 0 0 0 0 31 31 31 31 31 16 4 0 15 8 15 15 15 8 16 0 0 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 1 1 1 0 46 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 7 Snare&Tom 53 SECOND 0 TRIANGL 32 0 0 0 off 5 0 off off off off 0 0 0 0 1 1 2 3 1 1 3.14 15.7 no no no no 8 8 8 8 W1 W1 W1 W1 -1 -2 0 0 24 19 21 20 12 8 12 16 15 10 12 10 15 0 14 16 5 4 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 66 76 39 1 0 0 0 0 26 0 26 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Steel Drum 54 THIRD 0 TRIANGL 16 0 9 0 off 5 0 off off off off 0 0 0 0 5 6 4 7 2 6 8.98 2.02 no no no no 8 8 14 15 W1 W2 W3 W4 1 0 3 -3 21 13 14 31 8 8 5 9 12 11 10 10 12 12 3 4 6 5 4 3 VOF VOF VOF V48 99 99 99 50 50 50 99 64 40 68 1 1 1 1 0 50 0 26 -12 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 SteelDrum 55 SEVENTH 4 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 3 6 3 1 1 1 3 no no no no 8 8 8 8 W1 W1 W1 W1 3 2 2 -2 31 27 27 31 12 10 10 6 14 14 14 14 13 13 13 9 6 6 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 95 84 1 2 2 2 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SteelDrum1 56 FIFTH 5 TRIANGL 25 0 10 21 off 2 1 on on on on 0 0 0 0 4 3 6 2 1 2 1 1.73 no no no no 8 8 8 8 W1 W1 W1 W1 3 2 2 -2 31 27 27 31 12 10 10 6 14 14 14 14 13 13 13 9 6 6 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 95 84 1 2 2 2 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SteelDrum2 57 THIRD 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 3 5 1 1 1.57 1 3.14 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 20 20 20 20 10 10 10 16 0 0 0 0 0 0 0 0 6 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 60 63 73 0 0 0 1 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 SteelDrum3 58 THIRD 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 6 3 6 2 1 1.57 1.57 3.14 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 20 20 20 20 10 10 10 16 0 0 0 0 0 0 0 0 6 4 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 47 66 0 0 0 0 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 SteelDrum4 59 FOURTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 4 0 4 0.5 0.93 1 0.62 no no no no 8 15 8 10 W1 W1 W5 W1 3 -2 -3 3 27 24 23 25 18 17 11 21 10 4 10 0 10 9 4 31 5 6 4 2 VOF VOF VOF VOF 99 25 25 21 50 50 93 93 89 95 2 2 3 3 0 30 50 75 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SyBon 60 FOURTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 4 0 4 0.5 0.93 1 0.62 no no no no 8 15 8 10 W1 W1 W5 W1 3 -2 -3 3 27 24 23 25 18 17 11 21 13 4 13 0 13 9 4 31 5 6 4 2 VOF VOF VOF VOF 99 25 25 21 50 50 93 93 89 95 2 2 3 3 0 30 50 75 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SyBon* 61 FIFTH 7 SAWUP 63 0 99 0 on 6 0 off off off off 0 0 0 0 4 3 5 2 2 1 6 4.71 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 0 0 21 24 21 24 19 24 21 24 0 4 0 15 12 22 12 31 11 11 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 60 0 0 0 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm.1 62 FIFTH 7 SAWUP 63 0 99 0 on 6 0 off off off off 0 0 0 0 5 1 4 1 0.5 1 2.82 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 0 0 21 24 21 24 19 24 21 24 0 4 0 15 12 22 12 31 11 11 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 93 1 2 1 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm.2 63 FIFTH 7 SAWUP 63 0 99 0 on 6 0 off off off off 0 0 0 0 5 0 3 2 0.5 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 0 0 28 31 21 24 19 24 21 24 0 4 0 15 12 22 12 31 11 11 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 99 1 2 1 0 0 45 67 0 0 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm.3 64 FIFTH 4 SHOLD 30 99 0 0 off 7 0 off off off off 0 0 0 0 6 1 5 1 0.5 0.71 1 15.7 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 25 21 24 18 12 21 24 0 15 0 15 9 7 12 31 7 4 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 1 0 0 45 67 0 0 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm.4 65 FIFTH 4 SHOLD 30 99 0 0 off 7 0 off off off off 0 0 0 0 6 3 4 3 0.5 0.5 0.5 14.13 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 24 17 31 31 24 0 15 15 15 9 7 0 31 5 5 15 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 92 99 99 0 1 1 3 0 45 67 0 0 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm.5 66 THIRD 3 TRIANGL 23 0 0 42 on 6 1 on off off off 0 0 0 0 5 3 5 2 0.5 0.71 0.5 0.71 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 0 0 21 24 21 24 19 24 21 24 0 4 0 15 12 22 12 31 11 11 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 93 1 2 1 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm.6 67 FOURTH 3 TRIANGL 23 0 0 42 on 6 1 on off off off 0 0 0 0 5 3 5 2 1 0.71 1 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 0 0 28 24 21 24 19 24 21 24 0 4 0 15 12 22 12 31 11 11 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 93 1 2 1 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm.7 68 THIRD 3 TRIANGL 23 0 0 42 on 6 1 on off off off 0 0 0 0 5 2 3 2 0.5 1 1.73 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 0 0 16 25 31 31 16 25 31 6 12 0 12 0 18 9 6 7 9 7 6 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 25 1 2 1 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm.8 69 FIFTH 3 TRIANGL 23 0 0 42 on 6 1 on off off off 0 0 0 0 5 2 3 1 0.5 1 0.5 23.55 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 16 19 12 31 6 0 0 10 31 31 0 31 9 6 6 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 57 1 2 1 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm.9 70 FIFTH 4 SAWUP 8 0 99 0 on 6 0 off off off off 0 0 0 0 6 2 5 1 1.41 0.71 0.71 21.2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 16 19 12 31 6 0 0 10 31 31 0 31 9 6 6 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 57 1 2 1 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm10 71 FIFTH 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 3 5 2 0.5 0.71 1.41 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 29 26 16 31 26 21 0 10 4 9 31 16 6 0 5 4 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 74 1 2 0 2 0 0 0 0 0 POLY 5 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 SynthDrm11 72 FIFTH 3 TRIANGL 36 28 2 77 on 7 1 on off on off 0 0 0 0 6 2 4 2 1.73 1 0.5 18.37 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 18 16 20 11 31 6 0 0 15 31 31 0 25 9 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 45 1 2 1 3 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm12 73 FIFTH 7 SAWUP 63 0 99 0 on 6 0 off off off off 0 0 0 0 6 4 4 4 0.71 1.57 1.73 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 0 0 21 24 21 24 19 24 21 24 0 4 0 15 12 22 12 31 11 11 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 99 97 0 0 0 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm13 74 FIRST 7 SAWUP 63 0 99 0 on 6 0 off off off off 0 0 0 0 4 3 3 3 0.71 0.71 1.73 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 0 0 21 24 21 24 19 24 21 24 0 4 0 15 12 22 12 31 11 11 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 66 97 0 0 0 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm14 75 FOURTH 7 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 5 3 4 3 0.5 0.5 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 2 0 0 21 19 31 24 17 24 24 24 0 10 9 15 12 14 13 31 11 11 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 53 94 99 0 0 0 0 0 45 67 0 -12 POLY 6 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 SynthDrm15 76 FIFTH 7 SHOLD 0 0 0 0 on 7 0 off off off off 0 0 0 0 4 2 5 2 0.87 0.5 0.71 3 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 28 20 31 28 15 7 12 30 7 0 0 0 7 22 31 8 7 15 6 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 97 99 1 3 0 1 62 0 0 99 -12 POLY 2 FULL 0 POR 99 0 0 98 0 0 0 50 0 0 0 0 0 0 SynthDrm16 77 FIFTH 4 TRIANGL 32 44 20 0 off 4 0 off off off off 0 0 0 0 6 3 5 2 1 5 1 3 no no no no 8 8 8 8 W1 W1 W1 W1 3 -1 -1 2 28 28 28 28 9 15 9 15 10 10 10 12 13 31 13 9 2 2 2 1 VOF VOF VOF VOF 99 99 99 50 50 50 97 52 93 89 1 1 1 2 30 30 50 50 -12 POLY 12 FULL 0 POR 99 0 0 50 50 0 0 50 0 0 0 0 0 0 Tibet Drum 78 FIFTH 7 TRIANGL 11 0 16 0 off 2 0 off off off off 0 0 0 0 4 0 4 0 0.5 0.71 0.87 0.78 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 3 3 31 21 19 15 12 9 31 7 0 15 15 15 7 4 0 3 6 4 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 95 55 2 0 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Timbale..1 79 FIFTH 5 TRIANGL 11 0 16 0 off 2 0 off off off off 0 0 0 0 5 0 5 0 0.5 0.71 0.87 0.78 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 3 3 31 31 31 31 12 24 9 1 0 10 11 0 7 9 6 7 6 3 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 87 61 2 1 0 0 0 0 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 99 50 0 50 0 0 0 0 0 0 Timbale..2 80 SECOND 0 TRIANGL 2 0 95 0 on 7 0 off off off off 0 0 0 0 6 0 0 0 2 0.88 14.45 1.62 no no no no 32 10 52 18 W1 W3 W1 W2 0 0 0 0 31 31 31 31 14 13 17 0 0 0 0 0 0 0 0 0 6 7 9 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 60 80 0 0 0 1 0 12 50 19 -24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Timbales * 81 THIRD 0 TRIANGL 7 0 0 0 off 6 0 off off off off 0 0 0 0 0 2 5 7 0.5 0.62 0.88 0.62 no no no no 8 10 10 10 W1 W1 W3 W4 0 -3 3 -3 29 31 31 31 11 17 18 15 0 11 15 0 0 6 12 8 4 3 4 3 VOF VOF V48 VOF 99 99 99 50 50 50 99 75 79 79 1 1 1 0 0 54 0 0 0 POLY 12 FULL 0 POR 40 0 0 80 0 0 0 50 0 0 0 0 0 0 Timpani * 82 THIRD 7 TRIANGL 21 0 3 0 off 6 2 off off off off 0 0 0 0 2 0 1 0 0.5 0.5 0.78 22.49 no no no yes 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 29 17 15 15 18 15 15 12 0 12 7 17 0 4 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 60 82 65 0 0 0 0 0 0 59 0 -12 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Timpani 83 THIRD 5 TRIANGL 21 0 3 0 off 6 2 off off off off 0 0 0 0 6 0 5 0 0.5 0.5 0.78 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 29 12 19 7 18 15 11 12 0 12 12 4 0 4 4 2 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 63 55 0 0 0 0 0 0 59 0 -12 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Timpani..1 84 THIRD 7 TRIANGL 21 0 3 0 off 6 2 off off off off 0 0 0 0 5 0 5 0 0.78 0.5 0.5 0.78 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 29 31 31 15 18 15 15 12 0 12 8 7 0 4 4 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 55 79 61 1 0 0 1 0 0 59 0 -24 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Timpani..2 85 SECOND 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 8.24 11.46 2.31 no yes yes yes 8 12 26 13 W1 W1 W1 W1 0 0 0 0 31 31 31 31 16 12 14 5 15 4 0 14 10 16 10 10 7 7 6 8 VOF VOF VOF VOF 99 23 35 46 50 50 94 52 81 93 0 0 0 0 0 0 0 0 -24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Tom 1 86 FOURTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 1 1 1 0 0.5 1 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 31 24 31 15 15 6 15 11 0 17 0 6 1 3 1 VOF VOF VOF VOF 99 37 38 0 50 50 99 55 94 99 1 1 1 0 0 19 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Tom 2 87 FOURTH 7 SHOLD 99 0 99 0 off 6 0 off off off off 0 0 0 0 1 1 1 0 0.5 1 0.87 8.65 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 20 31 24 31 14 15 6 15 11 0 5 0 6 1 3 1 VOF VOF VOF VOF 99 37 38 0 50 50 99 55 94 70 1 1 1 0 0 19 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Tom 3 88 FOURTH 4 SHOLD 99 0 99 0 off 6 0 off off off off 0 0 0 0 1 1 1 0 0.5 0.62 0.62 1.57 no no no no 8 10 10 8 W1 W1 W6 W1 0 0 0 0 31 31 31 31 11 31 24 31 10 15 6 15 31 0 5 0 10 1 3 1 VOF VOF VOF VOF 99 37 38 44 50 50 99 55 94 70 1 1 1 0 0 19 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Tom 4 89 FOURTH 4 SHOLD 99 0 99 0 off 6 0 off off off off 0 0 0 0 1 1 1 0 0.5 0.62 0.62 1.57 no no no no 8 10 10 8 W1 W1 W6 W1 0 0 0 0 31 31 31 31 11 31 24 31 13 15 6 15 31 0 5 0 10 1 3 1 VOF VOF VOF VOF 99 37 38 44 50 50 99 55 94 70 1 1 1 0 0 19 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Tom 4* 90 FOURTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 4 0 6 0 0.5 0.78 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 0 19 21 24 24 22 31 31 31 15 15 15 15 13 27 16 23 10 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 93 0 0 1 0 0 0 0 0 0 0 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Tom Tom..1 91 FOURTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 0 5 0 0.5 0.78 0.78 1.73 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 0 19 21 24 24 22 31 31 31 15 15 15 15 13 27 16 26 10 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 93 71 99 1 0 0 0 0 0 0 0 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Tom Tom..2 92 FIFTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 0 5 0 0.71 0.71 0.78 0.71 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 0 19 21 24 24 22 31 31 31 15 15 15 15 13 27 16 26 10 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 2 2 0 0 0 0 0 0 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Tom Tom..3 93 FIFTH 5 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 0 5 0 3 4 5 3 no no no no 8 13 8 8 W1 W1 W1 W1 0 0 0 -3 31 31 31 31 28 19 13 19 15 13 13 10 15 10 12 12 7 5 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 89 77 0 0 0 0 0 50 31 0 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Tom Tom..4 94 THIRD 3 TRIANGL 7 0 81 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 0.5 0.68 0.87 0.5 no no no no 8 11 8 8 W1 W1 W6 W6 3 -3 3 -3 29 31 19 31 12 22 11 16 0 9 9 0 0 4 3 0 6 3 2 1 VOF VOF VOF VOF 99 64 67 22 50 50 90 99 60 80 0 0 0 0 0 54 59 53 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Tom-Pany 95 FIFTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 4 1 6 0 0.5 0.5 0.5 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 27 24 31 31 31 31 31 31 15 15 15 15 15 31 31 31 6 7 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 70 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Wood Drum 96 FIFTH 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.5 0.5 0.5 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 27 24 31 31 31 31 31 31 15 15 15 15 15 31 31 31 6 7 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 70 0 0 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Wood Drum* 97 FIFTH 7 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 3 6 4 0.5 0.71 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 -1 -2 0 3 31 16 31 31 9 12 22 22 12 9 3 5 9 12 15 21 6 3 6 15 VOF VOF VOF VOF 99 99 99 50 50 50 98 63 99 99 0 0 0 0 0 0 0 46 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 XyloSpring 98 FIRST 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 1 no no no no 255 255 255 255 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 31 31 31 15 15 15 15 11 0 0 0 15 15 15 15 VOF VOF VOF VOF 93 72 99 67 6 0 90 0 0 0 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 SUS 40 0 0 50 0 0 0 0 0 0 0 0 0 0 Bass Drum3 4 Other 6 SIXTH 7 SHOLD 81 0 0 99 on 7 3 on on on off 0 0 0 0 2 1 2 0 12 12 20.41 9 yes yes yes yes 80 80 64 80 W2 W6 W1 W1 0 0 0 0 31 31 31 31 13 13 13 21 0 0 0 0 0 0 0 0 6 7 7 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 0 24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Cabassa * 0 SIXTH 7 SHOLD 89 0 0 99 on 7 3 on on on off 0 0 0 0 2 1 2 0 22.49 22.81 12 9 yes yes yes yes 80 83 96 96 W1 W5 W1 W1 0 0 0 0 31 31 31 31 13 13 13 21 0 0 0 0 0 0 0 0 7 7 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 98 0 0 0 0 0 0 0 0 24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Maracas * 1 THIRD 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 1 2 2 2 1 7.02 7 13 no no no no 8 9 8 8 W2 W1 W1 W7 0 3 -3 0 31 31 31 31 14 16 11 23 0 8 8 5 0 0 0 0 6 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 77 85 1 1 1 1 0 10 12 28 -12 POLY 7 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Metalimba 2 THIRD 6 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 3 1 0 1 7.85 12.56 1 1.31 yes yes yes yes 16 105 8 53 W1 W1 W1 W1 0 0 0 0 24 31 31 31 16 0 18 19 0 15 15 0 31 0 0 0 7 1 1 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 69 99 0 0 0 0 0 0 0 0 -24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Tambourin* 3 NOISES 6 Noises 0 FIFTH 1 SHOLD 99 0 0 99 off 2 1 on on on on 0 0 0 0 6 5 6 5 0.5 0.5 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 12 19 15 15 18 18 4 4 15 12 15 15 3 17 3 2 3 3 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 95 99 99 77 0 0 0 0 2 3 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Airplane.1 0 FIFTH 1 SHOLD 99 0 0 99 off 2 1 on on on on 0 0 0 0 7 4 4 4 0.5 2.82 1 2.82 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 12 19 15 15 18 18 4 4 15 12 15 15 3 17 3 2 3 3 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 95 99 99 86 0 0 0 0 2 3 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Airplane.2 1 THIRD 4 SQUARE 55 0 0 40 off 6 3 on off on off 0 0 0 0 0 0 0 0 27.57 5.19 3.14 25.95 yes yes no no 47 96 8 8 W1 W6 W1 W5 3 3 -3 -3 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 6 4 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 92 71 58 46 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Alarm Call 2 FIFTH 4 TRIANGL 38 3 5 0 off 4 1 off off off off 0 0 0 0 1 0 2 0 2.06 1.66 1.67 2.06 no no no no 13 9 11 17 W5 W3 W3 W4 -3 -3 2 -2 20 25 15 30 21 7 10 5 14 15 14 14 3 4 0 2 5 5 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 98 76 99 77 0 0 0 0 0 8 6 0 -19 POLY 3 FULL 0 POR 99 0 0 43 0 0 0 50 0 0 0 0 0 0 Alien 3 3 THIRD 0 SQUARE 50 0 0 99 on 0 1 on on on on 0 0 0 0 5 4 5 3 2 1 3 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 3 2 21 22 22 10 4 16 15 12 14 13 13 14 31 31 31 1 6 5 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 44 70 40 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Alien Chat 4 FOURTH 0 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 0 0 3 5 0.62 0.5 8.24 4 yes yes yes no 10 8 12 8 W2 W1 W1 W1 0 0 0 0 31 31 27 29 19 3 4 10 15 0 7 7 4 0 10 8 6 1 1 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 87 94 2 1 1 1 0 0 0 0 -24 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Artofnoiz* 5 EIGHTH 0 TRIANGL 37 0 0 0 on 3 0 off off off off 0 0 0 0 6 4 4 3 8 6 5.65 5.19 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 3 -3 31 12 10 7 27 27 27 27 15 15 15 15 13 13 13 13 13 13 13 13 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 0 -24 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 AutoDecent 6 FOURTH 5 SHOLD 99 0 64 0 off 7 0 off off off off 0 0 0 0 5 4 4 3 0.5 6.92 0.5 3 no no no no 8 8 8 8 W1 W1 W1 W1 -3 -3 -3 -3 15 20 20 18 9 11 6 7 12 9 5 5 0 4 1 1 1 1 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 83 48 0 0 0 0 0 0 0 0 -24 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 AutoStorm1 7 FOURTH 3 SHOLD 80 0 99 17 on 7 1 on on on on 0 0 0 0 5 3 4 3 0.5 6.92 0.5 4 no no no no 8 8 8 8 W1 W1 W1 W1 -3 -3 -3 -3 19 18 24 15 9 11 6 7 12 9 5 5 9 9 9 9 1 1 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 83 51 0 0 0 0 0 0 0 0 -24 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 AutoStorm2 8 FIFTH 6 SQUARE 89 0 99 35 on 7 1 on on on on 0 0 0 0 5 3 4 4 0.5 4 4 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 27 27 28 31 15 12 12 11 7 7 12 12 9 9 11 11 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 66 3 3 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 50 1 50 0 50 0 0 0 0 0 0 Big Belch 9 FIFTH 0 TRIANGL 34 0 80 94 off 6 3 on off on off 0 0 0 0 0 0 0 0 25.95 0.71 25.95 0.71 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 19 31 31 31 31 31 15 15 15 15 0 0 0 0 15 15 14 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 99 81 0 0 0 0 10 40 10 40 -12 POLY 12 FULL 0 POR 99 0 0 99 99 0 0 51 0 0 0 0 0 0 Birds 10 FIFTH 7 TRIANGL 31 0 35 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 0.78 1.73 3.14 4.24 no no no no 8 8 8 8 W1 W3 W2 W1 2 -3 -2 0 18 25 19 30 0 0 0 0 15 15 15 15 0 0 0 0 5 8 8 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 95 69 1 1 0 0 0 35 0 9 -12 POLY 6 FULL 0 POR 99 0 0 61 0 29 0 50 0 0 0 0 0 0 BlendOrgan 11 THIRD 7 TRIANGL 0 0 0 0 off 0 0 off off off off 7 3 3 7 7 5 2 3 4 0.5 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 15 15 19 15 4 6 21 4 12 13 9 2 4 4 21 4 9 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 98 0 3 3 0 0 0 0 0 -24 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Blow Up 12 FOURTH 5 SAWUP 17 0 73 90 off 7 1 on on on on 0 0 0 0 5 4 4 2 2 7 20.41 9.42 no no no no 8 8 8 8 W1 W1 W1 W1 -3 2 0 2 31 31 31 31 7 7 7 10 0 0 0 0 12 12 28 27 13 4 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 50 58 75 2 2 2 2 0 0 0 0 -24 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Boingggggg 13 THIRD 5 SHOLD 52 0 55 73 off 7 0 off off off off 0 0 0 0 6 3 5 3 5 16.96 25.95 5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 13 31 19 15 11 15 6 15 0 0 6 7 5 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 57 63 0 0 0 0 0 0 0 0 2 POLY 5 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 BrokWindow 14 FOURTH 7 SQUARE 50 0 99 67 on 3 2 on off on off 0 0 0 0 5 4 3 4 3 1 0.71 0.71 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 0 -3 24 31 31 23 13 31 31 6 0 15 15 14 13 31 31 0 6 15 15 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 0 65 58 2 0 1 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Bubbly 15 SEVENTH 0 SQUARE 19 70 0 99 on 4 3 on on on off 0 0 0 0 0 0 0 0 14.22 19.21 11.13 1 yes yes yes no 17 26 23 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 95 58 0 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Busy * 16 THIRD 0 SQUARE 15 0 0 90 off 0 3 on on on on 0 0 0 0 5 3 6 3 0.5 0.5 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 -3 -3 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 52 64 50 1 1 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Busy.....1 17 EIGHTH 0 SQUARE 15 0 0 99 off 0 3 on on on on 0 0 0 0 0 0 0 0 0.5 0.5 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 -3 -3 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 1 1 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Busy.....2 18 THIRD 7 TRIANGL 99 0 16 84 on 0 1 on off off off 0 0 0 0 6 4 5 3 0.87 7.85 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 2 0 2 16 28 28 26 12 31 16 16 15 15 15 15 3 31 3 17 9 6 6 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 72 2 3 3 2 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Choo Choo 19 FIRST 7 SAWUP 42 0 66 90 off 6 1 on on on on 0 0 0 0 5 3 5 3 0.5 13.84 18.84 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 11 31 31 31 11 31 31 31 15 15 15 15 0 0 0 0 7 1 3 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Chopper..1 20 FOURTH 5 SAWUP 51 0 0 99 on 7 3 on on on on 0 0 0 0 6 6 4 4 0.78 1.41 2 3 no no no no 8 8 8 8 W1 W1 W1 W1 1 -1 1 1 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 3 3 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 95 86 78 80 0 0 0 0 0 0 0 0 -12 POLY 7 FULL 0 POR 99 0 0 50 1 50 0 50 0 0 0 0 0 0 Chopper..2 21 FIFTH 4 SQUARE 32 0 0 99 off 0 3 on off on off 0 0 0 0 0 0 0 0 19.47 13.12 17.17 15.11 yes yes yes yes 93 10 95 18 W1 W1 W1 W1 0 0 0 0 29 31 28 31 31 31 31 31 15 15 15 15 0 0 0 0 4 1 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 82 90 82 90 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Cricket * 22 SIXTH 1 TRIANGL 99 0 0 49 on 7 3 on on on on 0 0 0 0 7 4 6 4 1 1 1.41 1.57 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 13 13 13 13 31 31 31 31 15 15 15 15 0 0 0 0 3 3 4 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 54 0 0 0 0 0 0 0 0 12 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 Diving Fly 23 THIRD 7 TRIANGL 85 0 45 0 off 7 0 off off off off 7 3 3 7 6 4 5 4 1 1 25.95 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 27 31 3 3 10 8 0 0 15 5 13 13 13 9 4 4 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 90 56 73 79 0 0 0 0 0 0 0 0 -24 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Dynamite 24 THIRD 7 TRIANGL 0 0 0 0 off 0 0 off off off off 7 3 3 7 7 4 4 4 1 1 0.5 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 -3 15 15 19 15 4 6 21 2 14 12 9 0 10 18 21 9 7 3 3 5 VOF VOF VOF VOF 99 99 99 50 50 50 93 58 62 86 0 3 3 0 0 0 0 0 -24 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 EarthQuake 25 FIRST 7 SHOLD 99 0 12 0 off 6 1 on on on on 0 0 0 0 1 0 0 3 0.5 1.83 3.18 3.62 no no no no 8 9 11 18 W1 W1 W1 W1 0 0 0 0 23 21 16 23 7 6 11 12 15 14 10 14 6 6 5 5 4 1 1 1 VOF VOF VOF VOF 99 99 18 0 50 50 93 86 76 76 1 1 0 1 0 0 0 3 -24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Earthquake 26 FIRST 7 SHOLD 99 0 12 0 off 6 1 on on on on 0 0 0 0 1 0 0 3 0.5 1.83 3.18 4.81 no no no no 8 9 11 21 W1 W1 W1 W1 0 0 0 0 23 21 16 23 7 6 11 12 15 14 13 14 6 6 5 5 4 1 1 1 VOF VOF VOF VOF 99 99 18 0 50 50 93 86 76 76 1 1 0 1 0 0 0 3 -24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Earthquake* 27 FIFTH 0 SQUARE 13 0 99 0 off 6 0 off off off off 0 0 0 0 6 4 5 4 1 2 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 21 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 5 2 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 93 43 0 0 1 0 5 0 10 35 0 POLY 5 FULL 0 POR 99 0 0 99 40 0 0 50 0 0 0 0 0 0 Eng Siren 28 FOURTH 4 TRIANGL 0 0 0 0 off 0 0 off off off off 0 0 0 0 5 4 5 4 1 2 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 18 18 18 27 31 31 31 3 15 15 15 14 8 9 11 7 10 10 10 4 VOF VOF VOF VOF 99 99 99 50 50 50 95 67 85 91 0 0 0 0 1 0 75 77 -24 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Excuse Me 29 FIRST 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.68 3 1.12 1 yes no yes no 11 8 10 8 W1 W1 W1 W1 0 0 0 0 17 19 13 31 13 8 3 31 0 15 15 15 31 8 1 0 7 4 4 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 81 91 0 0 0 0 0 0 0 0 0 -10 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Exo/Bird * 30 SIXTH 0 SHOLD 71 0 99 99 off 3 1 on on on on 0 0 0 0 6 4 3 3 1 4 1.57 5 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 -3 -3 16 16 16 16 9 9 9 9 15 15 15 15 4 4 4 4 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 44 99 78 95 1 1 1 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Explosion 31 SIXTH 7 SAWUP 57 0 99 0 on 7 0 off off off off 0 0 0 0 5 6 2 5 1 1 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 28 14 10 9 8 13 15 15 13 31 31 31 31 10 10 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 57 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 FaucetDrip 32 SECOND 7 TRIANGL 29 20 6 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 6 1 1.25 6 no no no no 8 8 12 8 W1 W1 W1 W1 3 -3 1 -2 28 31 31 31 31 20 31 10 14 11 15 15 1 5 0 0 8 8 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 73 64 0 0 0 0 0 0 5 48 12 POLY 12 FULL 0 POR 40 0 0 50 0 0 0 54 0 0 0 0 0 0 Feedback * 33 THIRD 7 TRIANGL 57 0 0 99 off 6 2 off off on off 0 0 0 0 0 0 0 0 4 1 5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 14 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 6 1 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 92 89 67 73 0 0 0 0 0 50 59 0 -24 MONO 12 FULL 99 POR 99 45 0 50 0 0 0 50 0 0 0 0 0 0 Flight Sim 34 FOURTH 7 TRIANGL 20 0 0 0 off 4 0 off off off off 0 0 0 0 1 1 1 1 1 2.48 0.5 5 no no no no 8 15 8 8 W1 W3 W4 W1 3 3 -3 3 31 26 15 15 19 26 26 13 15 10 15 15 7 5 4 5 7 5 5 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 63 1 1 2 0 0 54 19 30 0 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Gizmo * 35 FIRST 0 SHOLD 99 0 99 0 off 7 0 off off off off 0 0 0 0 0 0 0 0 1 1 1.25 1.75 no no no no 8 8 12 20 W3 W3 W1 W1 0 -3 3 -3 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 7 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 75 78 67 0 0 0 0 0 0 0 0 -24 MONO 12 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 GrandPrix* 36 THIRD 7 TRIANGL 97 0 0 52 off 0 2 on on on on 0 0 0 0 3 0 0 0 0.5 0.5 15 15 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 14 31 31 15 11 0 0 0 14 15 15 10 10 0 0 11 15 3 3 10 VOF VOF VOF VOF 99 99 99 50 50 50 92 97 76 91 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 GuiRoach:: 37 FIFTH 7 SHOLD 39 54 27 68 off 7 3 off on off off 0 0 0 0 0 0 0 0 3.46 0.5 2 1.57 yes no yes yes 8 8 8 48 W2 W1 W8 W1 -2 2 -3 3 2 25 1 31 3 11 16 17 15 15 15 15 0 1 0 0 6 1 6 1 VOF VOF VOF V48 99 99 99 50 50 50 99 85 99 68 0 0 0 0 0 0 24 71 -12 POLY 7 FULL 0 POR 99 0 0 99 0 50 0 50 0 0 0 0 0 0 Gurgle 38 THIRD 7 TRIANGL 33 0 99 0 off 0 0 off off off off 0 0 0 0 4 4 5 3 1 16.96 1 9 no no no no 8 8 8 8 W1 W1 W1 W1 3 0 -1 -1 18 31 31 18 4 6 21 4 15 15 15 15 0 22 0 0 9 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 90 48 99 99 0 1 1 0 0 0 0 46 -24 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Hard Rain 39 THIRD 7 SAWUP 99 0 99 99 on 3 1 on on on on 0 0 0 0 5 4 5 3 1.41 2 0.5 2.82 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 0 31 31 31 31 31 31 31 31 15 15 15 15 0 0 6 0 4 2 9 15 VOF VOF VOF VOF 99 99 99 50 50 50 81 19 90 91 0 1 1 2 0 0 0 0 -24 POLY 0 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Harley Hog 40 FIFTH 0 TRIANGL 0 0 0 0 on 0 0 off off off off 0 0 0 0 5 4 4 3 0.5 1 0.5 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 21 21 7 31 19 18 19 31 12 12 15 15 18 18 18 24 8 8 8 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 52 99 99 0 0 3 0 0 0 0 0 -12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Heart Beat 41 FIFTH 7 TRIANGL 51 0 0 91 off 6 3 off on on on 0 0 0 0 0 0 0 0 7 0.5 13.84 0.5 yes no yes no 8 8 8 8 W1 W3 W1 W1 0 0 0 0 14 31 14 13 31 31 31 31 15 15 15 15 0 0 0 0 4 1 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 98 99 92 0 0 0 0 10 40 10 40 0 POLY 12 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 Helicopter 42 THIRD 4 SQUARE 56 0 0 50 off 6 3 on off on off 0 0 0 0 5 4 4 4 10 6 3 5 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 -3 -3 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 6 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 55 49 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Hi Alarm 43 FIFTH 7 SHOLD 58 0 99 80 off 5 3 off off on on 0 0 0 0 0 0 0 0 5 12.72 4 5.19 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 -1 0 31 21 20 31 18 7 12 20 0 14 10 11 16 0 17 0 11 15 5 1 VOF VOF VOF VOF 99 99 99 50 50 50 98 51 88 84 1 1 1 0 1 46 0 0 0 POLY 5 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Hole in 1 44 FIFTH 7 SQUARE 47 0 0 0 on 3 0 off off off off 0 0 0 0 7 4 5 4 0.5 8.48 0.5 8.48 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 0 0 31 7 31 9 31 12 31 5 15 12 15 12 0 6 0 6 15 4 15 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 63 75 0 0 0 0 0 0 0 0 -24 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 House Fly 45 FIFTH 7 TRIANGL 25 42 0 0 off 2 0 off off off off 0 0 0 0 5 4 4 2 3.46 9 4 4.71 no no no no 8 8 8 8 W1 W1 W1 W1 1 0 0 0 14 18 15 18 12 16 9 10 15 13 14 15 9 6 3 6 10 10 9 12 VOF VOF VOF VOF 99 99 99 50 50 50 93 61 82 51 1 1 1 0 0 0 0 10 -24 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 HowlAtMoon 46 FIFTH 0 TRIANGL 99 67 32 74 off 4 0 off off off off 0 3 1 3 0 7 2 1 4.32 4.75 19.03 16.3 yes no no yes 24 44 72 41 W5 W6 W2 W3 -3 1 1 0 31 2 12 8 4 9 29 13 11 5 7 15 14 10 12 23 2 8 13 7 V12 VOF V12 V24 0 0 0 0 50 50 30 99 75 9 1 3 1 1 44 37 12 93 0 POLY 11 FULL 38 SUS 81 21 28 41 31 81 47 37 27 0 0 0 0 4 Lil'Birdie 47 THIRD 2 SQUARE 56 0 0 39 on 6 1 on off on off 0 0 0 0 4 4 5 3 1.73 7.85 1.73 0.87 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 -3 -3 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 6 4 6 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 55 49 0 0 0 0 0 0 0 0 -24 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Lo Alarm 48 FIFTH 7 TRIANGL 24 6 16 0 off 5 2 off off off off 0 0 0 0 1 1 2 0 2 2 2.16 1 no no no no 8 8 12 8 W2 W4 W1 W1 0 -3 3 0 26 31 31 31 10 9 0 4 15 15 15 15 0 3 0 0 5 2 11 2 VOF V12 V48 V24 99 99 99 50 50 50 92 58 68 68 2 0 0 0 5 21 12 15 -12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 MT:Apr'88 49 FIFTH 7 SAWUP 64 0 99 99 on 7 1 on on on on 0 0 0 0 5 0 5 0 0.5 2 1.57 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 -3 25 25 31 26 21 9 21 21 15 15 15 15 3 8 31 18 9 9 10 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 99 1 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 MachineGun 50 FIRST 7 TRIANGL 5 0 66 90 off 6 3 on off off off 0 0 0 0 0 0 0 0 0.5 13.84 18.84 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 11 31 31 31 22 31 31 31 15 15 15 15 0 0 0 0 7 1 3 1 VOF VOF VOF VOF 99 99 99 50 50 50 91 99 99 99 0 0 0 0 0 0 0 0 -24 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 MalibuNite 51 EIGHTH 7 SHOLD 33 0 53 28 off 6 3 on off on on 0 0 0 0 3 1 1 5 0.62 3.93 11.36 0.93 no no no no 10 23 9 15 W3 W2 W3 W4 -3 0 3 -3 1 1 1 7 8 8 7 17 10 9 8 9 0 0 0 0 1 1 1 1 VOF V24 V24 V12 99 99 99 50 50 50 99 98 98 79 0 0 0 0 0 5 8 0 -4 POLY 12 FULL 30 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 1 Med Musak 52 FOURTH 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1.41 0.68 3.14 17.27 yes no no yes 8 11 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 20 31 31 15 1 15 15 14 31 31 12 15 9 15 7 VOF VOF VOF VOF 99 99 99 50 50 50 92 99 62 92 1 1 3 0 0 0 12 0 12 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Mouse-Tom 53 FOURTH 7 SHOLD 99 0 99 0 off 7 0 off off off off 0 0 0 0 0 1 1 0 1.57 1.41 13 23.55 no no yes yes 8 8 8 8 W1 W1 W1 W1 0 -1 2 3 22 20 23 31 11 18 20 31 9 0 0 0 19 19 19 19 15 2 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 86 95 0 0 0 0 0 0 0 0 -8 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 Mr.Mr.Noiz 54 FOURTH 7 SHOLD 99 0 99 0 off 7 0 off off off off 0 0 0 0 0 1 1 0 1.57 1.41 13 23.55 no no yes yes 8 8 8 8 W1 W1 W1 W1 0 -1 2 3 30 31 31 31 11 18 20 31 9 0 0 0 19 19 19 19 15 2 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 86 95 0 0 0 0 0 0 0 0 11 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 6 Noise Shot 55 FIFTH 4 TRIANGL 79 46 99 99 on 6 0 off off off off 0 0 0 0 2 0 1 0 1.5 0.5 3 0.5 no yes no yes 16 8 8 8 W2 W4 W6 W3 -3 2 3 0 12 8 11 12 31 31 31 8 15 15 15 0 0 0 0 0 6 9 5 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 99 99 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Ofthewall* 56 FOURTH 7 TRIANGL 14 0 0 0 off 5 1 off off off on 0 0 0 0 4 7 7 3 3.5 7.75 3.68 10.99 no yes yes no 120 16 19 64 W1 W1 W2 W1 -3 -3 -1 3 31 31 31 31 31 25 31 31 15 15 10 15 7 8 10 2 3 3 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 60 1 1 1 1 0 99 90 0 -9 POLY 4 FULL 0 POR 99 0 0 55 0 0 0 50 0 0 0 0 0 0 OldClock 57 SIXTH 7 TRIANGL 16 0 89 0 off 5 0 off off off off 0 0 0 0 6 4 4 3 1 1 3 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 -1 0 0 31 31 31 15 2 3 4 3 13 13 13 14 2 3 2 9 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 96 98 60 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 OutOfTune 58 FIFTH 7 SQUARE 10 0 0 0 on 7 0 off off off off 0 0 0 0 0 0 0 0 1 3 1 1 no no no no 8 8 8 8 W2 W2 W2 W2 0 0 0 0 2 31 2 31 31 31 31 31 15 15 15 15 31 0 31 0 6 1 6 1 VOF VOF VOF VOF 74 2 27 0 50 50 90 82 90 85 1 0 1 0 0 0 0 0 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Passing By 59 FIFTH 0 TRIANGL 21 0 99 99 off 7 1 off on off on 0 0 0 0 5 4 5 4 1 2 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 21 31 16 31 31 31 31 31 15 15 15 15 0 0 0 0 9 9 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 93 43 0 0 0 0 0 0 0 0 12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Pull Over 60 FIFTH 0 SHOLD 46 0 99 0 on 7 0 off off off off 0 0 0 0 0 0 0 0 16.96 1 25.95 1 no no no no 8 8 8 8 W7 W1 W6 W1 0 0 0 0 29 13 29 31 31 31 31 31 15 15 15 15 0 0 0 0 9 9 9 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 0 99 0 0 0 0 0 0 0 0 0 12 POLY 12 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 R2-D2 * 61 THIRD 7 TRIANGL 5 0 60 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 0.71 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 21 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 3 1 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 53 91 71 0 0 0 0 0 0 0 0 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Racing Car 62 FOURTH 7 SQUARE 50 0 6 86 on 2 1 on off on on 0 0 0 0 0 0 0 0 16.87 3.67 19.56 13.56 yes yes yes yes 20 10 62 25 W1 W1 W1 W1 0 0 0 0 18 21 11 3 24 9 5 3 14 15 15 15 0 0 0 0 6 3 2 1 VOF VOF VOF VOF 99 99 99 50 50 50 90 97 51 81 0 0 0 0 0 0 0 0 -24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 RADIATION? 63 THIRD 7 TRIANGL 85 0 45 0 off 7 3 off on off off 0 0 0 0 4 4 5 4 0.71 2 1 3.46 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 3 -3 31 31 31 31 0 0 0 0 15 15 15 15 0 0 0 0 4 5 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 90 44 99 99 1 0 0 1 0 36 0 34 -24 POLY 4 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Rain Storm 64 THIRD 2 TRIANGL 81 0 0 0 off 7 0 off off off off 0 0 0 0 1 0 0 0 10.59 1.81 1 18.77 yes yes no yes 66 21 8 85 W1 W1 W1 W4 0 0 0 0 15 18 31 29 12 12 31 21 15 15 15 13 0 0 0 0 8 6 5 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 93 0 75 0 0 1 0 0 0 0 0 0 POLY 7 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Refs.Wisl* 65 FIFTH 5 SAWUP 41 0 0 99 on 3 1 on on on on 7 3 3 7 0 0 0 0 1 1 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -3 1 22 22 22 19 4 4 4 24 9 9 9 15 6 6 6 6 7 7 7 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 66 99 95 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Repeater.1 66 THIRD 7 SAWUP 41 61 84 99 off 7 1 on on on on 7 3 3 7 4 3 5 3 5 5 1 5 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -3 1 22 22 22 19 4 4 4 24 9 9 9 15 6 6 6 6 7 7 7 10 VOF VOF VOF VOF 99 99 99 50 50 50 89 55 97 71 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Repeater.2 67 THIRD 7 SAWUP 51 0 0 99 on 3 1 on on on on 7 3 3 7 5 3 5 1 1 5 1 7 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -3 1 22 22 22 19 9 9 9 15 15 15 15 15 6 6 6 10 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 89 73 97 88 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Repeater.3 68 FIFTH 5 SAWUP 64 0 0 99 on 3 1 on on on on 7 3 3 7 5 3 5 3 1 1 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 3 1 -3 0 31 31 21 31 6 6 6 6 0 0 7 7 31 31 12 19 4 4 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 99 71 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Repeater.4 69 SIXTH 0 SQUARE 37 0 0 99 on 3 1 on on on on 7 3 3 7 6 4 5 3 1 1 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 27 3 19 31 15 27 27 27 14 15 15 15 4 4 4 4 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 86 81 82 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Repeater.5 70 THIRD 2 SQUARE 88 0 0 50 on 6 1 on off off off 0 0 0 0 0 0 0 0 8 8 8.48 8.48 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 22 31 31 21 31 31 31 31 15 15 15 15 0 0 0 0 6 2 2 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 57 52 0 0 0 0 0 0 0 0 -24 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 Ringing..1 71 THIRD 2 SQUARE 59 0 0 60 on 6 1 on off off off 0 0 0 0 0 0 0 0 10 10 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 22 31 31 21 31 31 31 31 15 15 15 15 0 0 0 0 6 2 2 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 62 57 28 0 0 0 0 0 0 0 0 -24 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 Ringing..2 72 SIXTH 7 SHOLD 30 0 72 60 on 6 1 on on on on 0 0 0 0 5 4 6 4 0.5 2 1 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 12 31 10 6 6 6 6 14 14 14 14 6 6 6 6 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 91 67 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 RiseDecent 73 THIRD 0 TRIANGL 30 9 4 0 off 6 0 off off off off 0 0 0 0 7 6 3 7 1 5 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 11 31 9 23 11 11 0 15 4 5 0 3 9 9 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 98 92 0 0 0 0 0 0 0 82 -12 POLY 7 FULL 3 SUS 99 0 0 50 0 50 0 50 0 0 0 0 0 0 RubberBand1 74 FIRST 6 TRIANGL 31 28 19 0 off 5 0 off off off off 0 0 0 0 2 1 1 1 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 15 17 18 12 8 7 7 4 9 9 9 9 2 2 2 2 7 7 7 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 53 67 1 0 1 1 54 0 0 8 0 POLY 7 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 RubberBand2 75 FIFTH 7 TRIANGL 30 9 4 0 off 6 0 off off off off 0 0 0 0 3 0 0 3 2 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 11 31 31 31 9 11 6 2 10 12 1 0 4 0 0 3 9 9 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 95 80 0 0 0 0 0 0 0 82 -12 POLY 7 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 RubberBand3 76 THIRD 7 TRIANGL 32 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -3 0 11 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 6 0 0 0 VOF VOF VOF VOF 99 99 99 50 50 50 96 60 66 49 0 0 0 0 0 33 27 0 0 POLY 12 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 RubberBand4 77 THIRD 7 TRIANGL 32 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 1 1 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -3 0 11 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 6 0 0 0 VOF VOF VOF VOF 99 99 99 50 50 50 90 78 64 50 0 0 0 0 0 33 27 0 0 POLY 12 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 RubberBand5 78 FIFTH 7 TRIANGL 30 9 3 0 off 6 0 off off off on 0 0 0 0 7 4 5 3 1 2 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 28 10 8 2 12 12 10 0 4 0 2 3 9 9 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 96 82 0 0 0 3 0 0 0 10 0 POLY 7 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 RubberBand6 79 THIRD 0 TRIANGL 30 9 4 0 off 6 0 off off off off 0 0 0 0 7 6 3 7 1 5 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 11 31 9 23 11 11 0 15 4 5 0 3 9 9 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 98 92 0 0 0 0 0 0 0 82 -12 MONO 7 FULL 3 SUS 99 0 0 50 0 50 0 50 0 0 0 0 0 0 RubberBand7 80 FIFTH 7 TRIANGL 30 9 4 0 off 6 0 off off off off 0 0 0 0 3 0 0 3 2 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 0 0 31 31 31 31 28 11 6 2 10 12 1 0 4 0 0 3 9 9 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 95 80 0 0 0 0 0 0 0 82 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 RubberBand8 81 FIFTH 7 TRIANGL 30 9 4 0 off 6 0 off off off off 0 0 0 0 3 0 0 3 2 1 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 0 0 31 31 31 31 28 11 6 2 10 12 1 0 4 0 0 3 9 9 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 95 85 0 0 0 3 0 0 0 14 -12 POLY 2 FINGER 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 RubberBand9 82 FOURTH 7 TRIANGL 5 0 0 88 off 6 1 on on on on 0 0 0 0 4 4 4 4 25.95 10.38 18.84 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 15 13 31 31 0 15 15 15 9 6 0 0 7 4 3 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 85 99 99 3 3 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 SCM Writer 83 EIGHTH 6 TRIANGL 82 0 7 0 off 6 0 off off off off 0 0 0 0 2 5 5 5 1 2.74 5.4 8.93 no no no no 8 20 10 23 W1 W1 W2 W1 -3 3 3 3 31 31 31 29 17 12 13 9 15 12 15 10 7 6 8 5 6 6 3 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 93 94 80 2 2 3 1 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Saron Gam 84 THIRD 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 25.95 10.59 22.92 11.02 no no yes yes 8 10 52 70 W1 W2 W1 W1 0 0 0 0 3 23 23 16 9 14 15 15 0 15 15 13 0 0 0 0 15 13 10 13 VOF VOF VOF VOF 99 99 99 50 50 50 82 99 99 99 0 0 0 0 0 0 0 0 24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Shore Wave 85 THIRD 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 25.95 10.59 22.92 11.02 no no yes yes 8 10 52 70 W1 W2 W1 W1 0 0 0 0 3 23 23 16 5 14 15 15 0 15 15 13 0 0 0 0 9 7 4 7 VOF VOF VOF VOF 99 99 99 50 50 50 82 99 99 99 0 0 0 0 0 0 0 0 24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Shore Wv2 86 FIFTH 0 SAWUP 13 0 0 99 off 6 1 on on on on 0 0 0 0 5 5 5 3 1 2 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 21 14 16 14 31 31 31 31 15 15 15 15 0 0 0 0 9 9 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 93 43 0 0 0 0 0 0 0 0 12 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Sick Siren 87 FIFTH 7 SAWUP 31 0 42 60 on 3 3 on on on off 0 0 0 0 0 0 0 0 0.5 1.32 0.5 1.23 no no no yes 8 15 8 118 W1 W1 W1 W1 0 0 0 0 25 31 15 31 31 31 21 31 15 15 15 15 0 0 0 0 10 15 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 83 99 86 99 3 3 3 3 99 2 0 6 -24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Space BUG? 88 FIFTH 7 TRIANGL 10 8 45 60 off 3 1 on off on off 0 0 0 0 0 0 0 0 0.5 1.73 0.81 1.37 no no no no 8 8 13 14 W3 W3 W2 W2 3 3 -3 -3 20 14 10 22 8 9 6 10 8 15 15 15 5 3 5 12 4 2 4 6 VOF VOF VOF VOF 94 99 18 21 50 50 90 89 90 83 0 1 0 1 0 3 0 3 3 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Space Gong 89 SIXTH 7 SAWUP 57 0 99 0 on 7 0 off off off off 0 0 0 0 4 5 4 2 1 1 2 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 28 4 10 9 8 13 15 15 13 31 31 31 31 10 10 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 57 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Space Gun 90 SIXTH 0 SQUARE 50 0 99 0 off 6 3 on on on on 0 0 0 0 2 2 2 4 1 1.57 2.82 3.14 no no no no 8 8 8 8 W1 W5 W7 W8 1 0 0 0 8 6 11 5 31 31 31 31 15 15 15 15 2 3 4 1 9 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 1 2 3 96 0 POLY 5 FULL 60 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Space Talk 91 EIGHTH 0 SQUARE 35 0 75 0 on 7 0 off off off off 0 0 0 0 1 1 2 0 1 1 4 4 no no no no 8 8 8 8 W6 W1 W6 W2 3 -3 3 -3 31 25 25 25 0 0 0 15 15 15 15 15 6 15 15 15 2 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 98 0 0 0 0 5 5 5 5 6 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Space Vibe 92 FIFTH 5 TRIANGL 35 33 0 0 on 0 0 off off off off 0 0 0 0 4 3 3 3 0.5 1 1 15 no no no no 8 8 8 8 W1 W1 W1 W1 -2 -2 2 3 6 4 6 4 9 9 9 12 15 15 15 15 0 0 0 0 4 3 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 99 87 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Space....1 93 FIFTH 5 SAWUP 5 0 47 99 off 1 1 on on on on 0 0 0 0 5 3 3 1 6 9 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 2 0 0 12 3 9 31 31 31 31 31 15 15 15 15 0 0 0 0 4 4 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 89 99 87 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Space....2 94 FIFTH 1 TRIANGL 43 65 11 90 off 7 1 on on on on 0 0 0 0 4 3 3 2 1 3.14 1 1 no no no no 8 8 8 8 W1 W1 W1 W1 -3 1 0 0 28 4 21 25 9 9 21 18 4 4 15 0 4 31 9 6 4 4 10 6 VOF VOF VOF VOF 99 99 99 50 50 50 95 74 93 99 0 0 0 2 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Space....3 95 FIFTH 1 TRIANGL 99 0 99 0 on 7 0 off off off off 0 0 0 0 4 4 4 2 1 1 2 6 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 1 1 25 6 20 31 3 4 29 9 0 0 0 0 12 7 21 21 4 4 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 95 70 99 70 1 1 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Space....4 96 EIGHTH 0 TRIANGL 37 0 66 90 off 6 3 on on on on 0 0 0 0 5 5 4 3 0.5 13.84 2 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 3 -3 11 31 31 16 11 31 11 6 15 15 15 14 0 0 0 0 11 9 10 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 54 57 0 0 0 0 0 0 0 10 -24 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 SpaceBirds 97 FOURTH 7 TRIANGL 31 13 14 30 on 0 3 off on off off 0 0 0 0 4 1 1 0 0.5 2 6.28 23.55 no no no yes 8 8 8 8 W1 W1 W2 W8 3 -3 3 -3 5 20 21 4 21 20 3 7 15 11 6 13 0 0 0 0 8 5 6 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 96 75 0 0 0 1 0 36 0 34 0 POLY 12 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 5 Spc Midiot 98 FIFTH 0 TRIANGL 25 0 10 99 off 5 0 off off off off 0 0 0 0 2 3 2 5 1 2.99 1 3.06 no no no no 8 10 8 9 W1 W1 W1 W1 3 3 3 -3 17 20 22 20 7 7 7 12 15 15 15 15 9 8 9 7 5 3 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 99 82 2 1 1 1 0 43 0 39 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 SteelPan * 99 FOURTH 6 SHOLD 99 0 99 0 off 7 0 off off off off 0 0 0 0 2 1 1 1 0.5 5.19 0.5 3 no no no no 8 8 8 8 W5 W2 W1 W6 -3 -3 -3 -3 15 20 20 18 6 11 7 8 12 9 5 5 0 4 1 1 1 1 3 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 96 99 67 0 0 0 0 0 0 0 0 -24 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 Storm Wind 100 THIRD 7 TRIANGL 28 0 0 99 off 6 3 on off off off 0 0 0 0 0 0 0 0 25.19 0.5 0.5 0.5 no no no no 17 8 8 8 W1 W1 W1 W1 0 0 0 0 21 18 23 18 21 9 8 9 15 15 15 15 0 0 0 0 10 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 83 97 51 58 3 3 3 3 0 2 0 6 -24 POLY 4 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 TAP TAP<<< 101 FIFTH 7 SQUARE 58 0 0 99 on 0 2 off on off on 0 0 0 0 0 0 0 0 12.5 13.31 16.28 16.68 yes yes yes yes 104 69 54 74 W1 W1 W2 W1 0 0 0 0 31 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 6 6 6 15 VOF VOF VOF VOF 99 99 99 50 50 50 99 72 99 75 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Telephone* 102 FOURTH 0 SHOLD 99 0 35 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 0.5 2 2 18.24 yes no no no 8 8 8 79 W1 W3 W5 W7 0 3 -3 0 18 31 10 12 31 31 31 31 15 15 15 15 0 0 0 0 4 3 3 3 VOF VOF VOF VOF 99 36 17 37 50 50 84 59 52 46 0 0 0 0 0 0 42 32 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Terror! 103 THIRD 7 TRIANGL 71 0 60 0 off 6 0 off off off off 0 0 0 0 5 4 5 2 1 3 0.71 0.78 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 21 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 3 1 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 50 70 66 0 0 0 0 0 0 0 0 0 POLY 12 FULL 14 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Traffic..1 104 THIRD 7 SHOLD 21 0 95 0 off 7 0 off off off off 0 0 0 0 5 5 5 4 1 1.73 0.5 1.57 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 21 31 31 31 31 31 31 31 15 15 15 15 0 0 0 0 3 1 3 2 VOF VOF VOF VOF 99 99 99 50 50 50 99 50 70 72 0 0 0 0 0 0 0 0 0 POLY 12 FULL 14 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Traffic..2 105 THIRD 7 SAWUP 12 0 0 0 on 4 0 off off off off 0 0 0 0 6 4 6 3 1 2 1.57 1.57 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 19 15 31 21 22 13 7 22 15 15 12 15 0 0 0 0 7 1 3 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 59 60 0 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Tug Boat 106 FOURTH 7 SQUARE 74 0 0 0 off 0 3 on off off off 0 0 0 0 1 0 0 0 20.8 12.12 8.24 0.5 yes yes yes yes 20 66 12 8 W1 W1 W1 W8 0 0 0 0 31 31 31 31 17 31 31 5 0 15 15 0 0 0 0 0 9 3 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 89 62 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Typwriter* 107 FIRST 7 TRIANGL 5 0 66 90 off 6 3 on on on on 0 0 0 0 5 3 5 3 0.5 13.84 2 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 3 -3 11 31 31 16 11 31 11 6 15 15 15 14 0 0 0 0 7 1 10 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 54 57 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 UFO Decent 108 THIRD 2 SAWUP 68 0 0 99 off 0 1 on on on on 0 0 0 0 5 5 5 3 0.5 13.84 18.84 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 10 4 9 6 15 15 15 15 0 0 0 8 7 6 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 71 99 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Uzzie 109 EIGHTH 7 SHOLD 67 0 99 99 on 7 2 off off off on 0 0 0 0 3 0 0 0 1.51 1.5 1.49 8.63 no no no no 14 16 9 16 W5 W4 W1 W1 -3 3 3 -3 9 8 6 6 8 8 7 0 15 15 15 15 4 7 8 6 5 7 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 90 68 0 0 0 0 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Water * 110 FIRST 7 TRIANGL 6 0 66 90 off 6 1 on on on on 0 0 0 0 5 3 5 3 0.5 13.84 18.84 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 11 31 31 31 11 31 31 31 15 15 15 15 0 0 0 0 7 1 3 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Waves....1 111 FIRST 7 TRIANGL 4 0 66 90 off 6 1 on on on on 0 0 0 0 5 3 5 3 0.5 13.84 18.84 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 4 31 31 31 11 31 31 31 15 15 15 15 0 0 0 0 7 1 3 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Waves....2 112 FIRST 7 TRIANGL 5 0 0 88 off 6 1 on on on on 0 0 0 0 5 4 5 5 0.5 13.84 18.84 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 8 31 31 31 11 31 31 31 15 15 15 15 0 0 0 0 7 1 3 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Waves....3 113 SEVENTH 7 TRIANGL 5 0 66 85 off 6 1 on on on on 0 0 0 0 6 5 6 3 0.5 13.84 18.84 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 11 31 10 30 11 31 31 31 15 15 15 15 0 0 0 0 7 1 3 1 VOF VOF VOF VOF 99 99 99 50 50 50 1 0 99 99 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Waves....4 114 FIRST 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 5 4 4 2 0.5 3 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 12 31 31 31 10 31 31 31 14 15 15 15 9 0 0 0 7 1 3 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 10 -24 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Waves....5 115 FOURTH 7 SHOLD 99 0 99 0 off 7 0 off off off off 0 0 0 0 1 0 0 0 1.41 0.5 13 10.99 no no yes yes 8 8 64 8 W1 W1 W1 W1 0 -1 2 3 6 31 31 31 8 5 0 0 14 0 0 0 0 19 19 19 2 1 1 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 0 64 99 0 0 0 0 0 0 0 0 12 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 83 0 0 0 0 0 0 Wind * 116 SECOND 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 2 0 1 1 1 0.5 0.5 1 no no no no 8 8 8 8 W1 W1 W2 W1 0 0 0 0 31 31 15 31 31 16 4 12 15 15 10 10 0 0 2 13 12 4 10 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 85 69 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 >>WOW<< 117 SECOND 7 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 1 2 1 0 1 1.73 3.46 17.27 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 31 20 11 31 15 5 15 15 15 16 18 0 8 5 8 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 65 0 0 0 0 0 1 0 0 0 POLY 7 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 7 WaterGlass 118 FIFTH 0 TRIANGL 35 0 0 0 off 3 0 off off off off 0 0 0 0 1 2 1 0 0.5 20.5 8.81 22.81 no yes no yes 8 81 12 83 W3 W4 W1 W1 0 0 0 0 31 31 29 31 15 15 15 15 15 15 15 15 13 13 13 13 7 9 7 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 95 0 0 0 0 0 0 0 0 12 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 WeirdAxe * 119 THIRD 0 TRIANGL 35 0 0 0 off 6 0 off off off off 0 0 0 0 0 0 0 0 25.95 10.59 22.92 11.02 no no yes yes 8 10 52 70 W1 W1 W1 W1 0 0 0 0 11 31 31 24 10 18 19 19 10 15 15 10 10 0 0 0 11 10 12 15 VOF VOF VOF VOF 99 99 99 50 50 50 90 86 92 63 0 0 0 0 0 0 0 0 24 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 White Blow 120 OTHER 7 Other 0 FIFTH 4 TRIANGL 28 17 2 0 off 4 0 off off off off 7 3 3 7 5 5 5 5 1 13 5 1 no no no no 8 8 8 8 W1 W5 W1 W1 -2 -2 1 1 31 31 31 31 7 7 7 7 15 15 15 15 10 7 7 7 5 5 7 6 VOF VOF VOF VOF 99 99 99 50 50 50 93 70 99 94 0 0 1 1 0 0 0 0 0 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 120 VAC 0 FIFTH 5 TRIANGL 28 9 7 0 off 5 0 off off off off 0 0 0 0 3 6 3 6 1.49 5.81 1.51 6 no no no no 9 21 14 8 W2 W7 W2 W2 -2 0 2 0 31 19 31 31 31 31 31 19 15 5 15 5 1 31 1 31 5 5 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 99 75 1 2 1 2 0 0 0 0 -19 POLY 4 FULL 0 POR 99 0 0 40 0 15 0 50 0 0 0 0 0 0 Amazon 1 FIFTH 5 TRIANGL 21 1 22 12 off 3 2 on off on off 0 0 0 0 0 1 0 1 0.5 4 0.5 6.16 no no no no 8 8 8 14 W1 W1 W1 W1 -3 3 3 3 11 31 11 31 10 9 10 9 0 0 0 0 10 0 10 0 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 99 71 0 1 0 1 9 10 9 10 12 POLY 2 FULL 0 POR 40 0 0 55 0 0 0 50 0 0 0 0 0 0 ArcoGlass* 2 FIFTH 7 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 0 0 0 0 0.87 8.48 0.87 9.89 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 12 31 12 31 15 31 31 31 15 15 15 15 6 11 13 31 5 0 7 15 VOF VOF VOF VOF 99 99 99 50 50 50 80 47 72 0 0 0 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Aviary 3 FIFTH 1 TRIANGL 30 24 3 0 off 6 0 off off off off 0 0 0 7 6 4 6 3 1 5 2 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 -3 0 9 12 11 9 6 12 10 3 13 13 12 15 6 5 6 3 6 5 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 92 89 99 76 2 2 1 1 0 64 39 0 0 POLY 7 FULL 0 POR 99 0 0 50 1 0 0 50 99 0 0 0 0 0 BC1 GlassV 4 FOURTH 7 TRIANGL 28 0 0 0 off 5 3 on off off off 0 0 0 0 2 1 2 1 1.49 3 2.99 6 no no no no 9 8 10 8 W1 W1 W1 W1 0 -3 3 -2 14 31 31 31 8 25 17 19 14 13 9 13 0 0 13 17 7 7 9 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 92 85 1 0 0 1 0 0 0 0 -19 POLY 2 FULL 0 POR 99 0 0 16 46 0 0 50 0 0 0 0 0 0 Botl.Shff* 5 FIFTH 0 TRIANGL 21 0 10 0 off 3 0 off off off off 0 0 0 0 2 0 2 0 1.5 20.99 1.51 1.5 no no no no 16 14 14 16 W1 W1 W1 W2 0 0 -3 3 31 31 31 31 7 15 30 8 11 11 14 14 7 10 6 6 10 11 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 61 99 83 1 1 1 1 0 0 0 20 -19 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Cairo * 6 SEVENTH 7 TRIANGL 26 0 5 1 on 6 0 off off off off 0 0 1 3 2 0 0 0 1.5 1.49 1.51 20.86 no no no no 16 9 14 9 W5 W2 W5 W3 -3 3 1 3 14 11 15 31 0 2 2 0 0 0 10 15 1 3 4 0 6 4 3 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 85 65 0 0 0 0 0 0 0 0 -7 POLY 12 FINGER 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Celestial 7 FIFTH 5 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 6 4 4 4 2 0.5 10 3 no no no no 8 8 8 8 W1 W1 W1 W1 3 3 -1 0 15 31 31 31 31 31 9 9 15 15 8 8 0 0 4 4 4 3 4 3 VOF VOF VOF VOF 99 99 99 50 50 50 94 94 91 87 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 ChimeStrig 8 EIGHTH 0 TRIANGL 42 72 1 75 off 0 1 on on on on 0 0 0 0 6 5 6 4 1 1 5 6 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 16 31 31 31 10 10 18 27 0 0 0 0 10 7 7 7 4 6 6 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 82 83 84 0 2 0 0 0 0 0 10 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 Deep Space 9 FIFTH 3 TRIANGL 33 35 9 0 on 3 0 off off off off 0 0 0 0 5 4 5 2 2 2 2 2 no no no no 8 8 8 8 W1 W1 W1 W1 2 -3 3 -1 9 11 10 12 16 3 8 6 15 8 15 14 3 3 3 2 5 5 7 3 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 99 63 0 0 0 0 0 0 0 0 -12 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 EleChamber 10 EIGHTH 0 SQUARE 20 79 30 2 on 1 1 on on on on 0 0 0 0 5 3 5 4 1 1 1 0.5 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 31 31 10 13 12 10 10 0 0 0 0 0 0 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Enc3rdKind 11 FIFTH 7 TRIANGL 35 0 50 0 off 4 0 off off off off 0 0 0 0 0 0 0 0 9 6 3 6 no no no no 8 8 8 8 W1 W3 W1 W4 0 0 0 0 20 31 20 31 31 31 31 31 15 15 15 15 0 0 0 0 5 3 5 3 VOF VOF VOF VOF 99 99 99 50 50 50 86 60 86 66 0 0 0 0 30 0 30 0 -19 POLY 2 FULL 0 POR 99 0 0 99 0 0 0 50 0 0 0 0 0 0 Etherial 12 FOURTH 7 TRIANGL 32 0 0 0 off 6 0 off off off off 0 0 0 0 1 0 0 0 1.5 1.51 10.22 1.58 no no no no 16 14 12 10 W1 W4 W1 W8 -1 1 2 -3 15 24 31 24 20 7 31 31 15 7 15 15 0 0 21 22 15 15 15 15 VOF VOF VOF VOF 99 99 99 50 50 50 95 69 99 99 0 1 0 0 5 27 3 0 -12 POLY 2 FINGER 0 POR 99 0 0 19 99 0 0 50 0 0 0 0 0 6 Floot Base 13 FIFTH 7 TRIANGL 35 0 0 0 off 0 0 off off off off 0 0 0 0 0 0 0 0 0.5 0.5 2 10 no no no no 8 8 8 8 W1 W1 W1 W1 3 -3 -3 -1 12 24 15 22 0 0 0 0 15 15 15 15 0 0 0 0 5 5 8 10 VOF VOF VOF VOF 99 99 99 50 50 50 99 91 99 57 2 2 2 0 0 91 0 0 -12 POLY 0 FULL 0 POR 99 0 0 0 0 0 0 50 0 0 0 0 0 0 Full Ranks 14 FIFTH 4 TRIANGL 31 0 6 0 on 4 2 off off off on 0 0 0 0 3 0 3 0 1.5 1.5 1.5 1.49 no no no no 16 16 16 97 W4 W4 W1 W4 3 -3 3 -3 19 20 15 31 8 8 7 6 15 15 14 15 4 7 3 6 5 5 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 96 71 99 64 0 0 0 0 0 5 8 0 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 FuzzyPad 15 EIGHTH 6 TRIANGL 82 0 7 0 off 6 0 off off off off 0 0 0 0 1 4 4 4 1 2.64 5.12 8.87 no no no no 8 19 10 22 W1 W1 W1 W2 -3 3 3 3 31 31 24 21 18 14 19 19 13 12 12 10 3 4 3 5 6 6 8 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 74 89 84 0 1 0 1 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Gender Gam 16 SEVENTH 5 SHOLD 99 0 0 0 off 3 0 off off off off 0 0 0 0 4 3 3 2 0.5 4 0.5 6.92 no no no yes 8 8 8 112 W1 W2 W1 W1 1 -1 -1 -1 15 31 15 28 12 10 12 10 15 0 0 0 0 0 10 0 6 5 6 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 78 99 80 0 0 0 0 0 0 0 0 3 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 GlassHarp1 17 FIFTH 1 TRIANGL 30 24 3 0 off 6 0 off off off off 0 0 0 7 5 4 5 4 1 5 2 8 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 -3 0 9 12 11 9 6 12 10 3 13 13 12 15 6 5 6 3 6 5 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 92 79 99 64 2 2 1 1 0 64 39 0 0 POLY 7 FULL 0 POR 99 0 0 50 1 0 0 50 0 0 0 0 0 0 GlassHarp2 18 SEVENTH 5 SHOLD 99 0 0 0 off 2 0 off off off off 0 0 0 0 4 4 4 4 5 15 5 14.43 no no no yes 8 8 8 119 W3 W3 W1 W1 -3 2 3 0 10 7 12 12 8 7 6 7 12 9 3 10 5 4 7 4 5 4 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 39 99 62 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 GlassHarp3 19 FIFTH 0 TRIANGL 28 14 28 0 off 3 1 off off off on 0 0 0 0 1 3 1 3 1.5 1.5 1.49 1.49 no no no no 120 16 9 65 W4 W5 W4 W5 -3 -3 3 3 11 10 11 10 31 31 31 31 15 15 15 15 0 2 0 2 4 5 4 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 79 99 77 1 0 1 0 0 39 0 39 -19 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 4 HarmoPad 20 FIFTH 2 TRIANGL 32 59 2 0 off 0 0 off off off off 0 0 0 0 5 6 5 6 0.5 0.5 0.5 0.5 no no no no 8 8 8 8 W1 W2 W2 W1 0 0 0 0 22 9 22 9 7 21 7 21 15 15 15 15 4 4 4 4 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 99 80 0 1 0 1 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Impression 21 FIFTH 6 TRIANGL 38 2 13 0 off 3 0 off off off off 0 0 0 0 5 5 5 3 1 2 1 1 no no no no 8 8 8 8 W2 W2 W1 W1 -2 0 0 0 21 18 21 18 12 4 3 3 14 14 14 14 3 1 2 1 9 4 9 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 77 99 84 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Jump.....1 22 FIFTH 5 TRIANGL 38 33 32 0 off 1 0 off off off off 0 0 0 0 5 5 5 3 2 2 1 1 no no no no 8 8 8 8 W1 W4 W1 W2 0 0 0 0 31 31 31 18 22 22 22 3 12 12 14 14 0 24 0 7 9 9 9 9 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 87 0 3 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Jump.....2 23 SEVENTH 7 TRIANGL 34 0 15 0 on 4 2 off off off on 0 0 0 0 3 0 0 0 1.51 1.5 1.49 8.63 no no no no 14 16 9 16 W5 W4 W1 W4 -3 3 3 -3 14 11 15 31 8 8 7 0 15 15 15 15 4 7 8 6 5 7 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 89 67 0 0 0 0 0 5 8 0 5 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 KrstlChoir 24 EIGHTH 7 TRIANGL 34 0 15 0 on 2 1 off off off on 0 0 0 0 3 1 1 5 1.51 1.5 1.49 9.16 no no no no 14 16 9 16 W3 W2 W3 W4 -3 0 3 -3 14 11 15 31 8 8 7 17 15 15 15 15 4 7 8 6 5 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 98 98 79 0 0 0 0 0 5 8 0 -7 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 KrystlPad1 25 FOURTH 6 TRIANGL 26 9 7 31 off 5 0 off off off off 0 0 0 0 4 2 3 3 1.5 2.99 3 3.08 no no no no 16 10 8 11 W1 W1 W1 W4 -3 3 -3 -3 12 19 28 12 9 28 31 31 13 11 15 1 1 3 1 6 8 5 5 5 VOF VOF VOF V24 99 99 99 50 50 50 99 83 70 63 0 1 1 1 0 0 0 0 -19 POLY 2 FULL 0 POR 99 0 0 25 31 16 30 50 0 0 0 0 0 0 MilkBottle 26 FIFTH 4 SQUARE 24 57 9 25 on 3 1 on on on on 0 0 0 0 6 3 4 3 1 1 8 14 no no no no 8 8 8 8 W1 W1 W1 W1 2 1 -1 -3 31 31 31 10 9 9 9 9 15 15 11 11 6 5 5 3 6 3 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 66 0 0 1 2 0 0 0 0 -24 POLY 0 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Movement.1 27 FIFTH 7 SHOLD 37 0 0 39 on 3 1 on on on on 0 0 0 0 5 4 3 3 2 2 2 9 no no no no 8 8 8 8 W1 W1 W1 W1 2 -2 -1 -3 31 31 31 11 9 9 9 9 15 15 14 14 0 0 1 0 6 3 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 99 62 2 0 0 3 0 0 0 0 -24 POLY 0 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 Movement.2 28 FIFTH 0 TRIANGL 4 0 0 0 off 5 0 off off off off 0 0 0 0 0 0 0 0 15.89 17.17 2 10 yes yes no no 34 79 8 8 W1 W1 W1 W2 0 0 -3 3 27 31 31 31 22 31 28 26 0 0 11 9 31 31 10 1 15 15 4 1 VOF VOF VOF VOF 99 99 99 50 50 50 82 94 99 75 0 0 1 0 0 0 0 0 0 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 Musicbox * 29 EIGHTH 7 TRIANGL 26 0 5 1 on 6 0 off off off off 0 0 1 3 2 0 0 0 1.5 1.49 1.51 20.86 no no no no 16 9 14 9 W5 W2 W5 W3 -3 3 1 3 2 2 2 2 13 2 2 29 0 0 10 15 1 3 4 0 6 4 3 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 85 65 0 0 0 0 0 0 0 0 -7 POLY 12 FINGER 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Obelisk 30 FIFTH 5 TRIANGL 72 42 10 39 on 3 1 on on on on 0 0 0 0 4 2 5 2 1 18.37 11 14 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 1 2 31 31 31 31 4 5 3 3 4 4 4 4 0 0 0 0 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 71 93 80 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Ornt E.T. 31 EIGHTH 7 TRIANGL 21 0 21 35 off 1 0 off off off off 0 0 0 0 5 4 4 3 1 4 8 25.95 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 18 10 9 6 31 31 8 15 15 15 5 3 14 9 31 15 15 15 9 VOF VOF VOF VOF 99 99 99 50 50 50 97 99 94 99 0 0 0 3 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 Ornt Quest 32 FIFTH 7 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 4 2 5 2 2 18.37 2 4 no no no no 8 8 8 8 W1 W1 W1 W1 0 1 -1 -1 19 31 18 18 3 19 2 6 13 10 0 0 21 29 0 0 9 9 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 93 99 99 70 0 0 3 3 0 0 46 46 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntFlight 33 EIGHTH 6 TRIANGL 56 68 0 63 on 1 0 off off off off 0 0 0 0 5 3 3 2 1 2 3 3 no no no no 8 8 8 8 W1 W1 W1 W1 3 0 0 0 6 31 31 9 5 10 18 3 0 0 0 0 0 3 4 2 5 5 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 64 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntSound1 34 FIFTH 6 SQUARE 56 68 0 63 on 3 0 off off off off 0 0 0 0 5 2 5 2 1 1 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 3 0 0 0 22 9 22 9 6 13 15 3 12 12 12 12 1 3 4 9 7 2 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 84 99 64 0 0 0 0 0 0 0 0 -12 POLY 2 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 OrntSound2 35 SEVENTH 7 TRIANGL 26 0 5 0 off 4 0 off off off off 0 0 0 0 0 2 2 2 7 7 3.5 10.5 no no no no 8 8 16 16 W4 W2 W1 W4 2 -2 2 -2 14 19 31 31 15 11 9 10 13 10 10 11 12 12 12 11 7 7 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 84 75 0 0 1 0 0 0 0 0 -12 POLY 1 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 ParisDream 36 FIFTH 7 TRIANGL 35 0 14 0 on 3 1 on off off off 0 0 0 0 5 4 5 4 1 2 1 10 no no no no 8 8 8 8 W4 W1 W1 W1 0 0 0 0 8 10 31 31 14 31 7 10 15 15 7 6 0 0 7 14 6 3 5 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 76 99 77 0 0 0 0 0 0 0 99 0 POLY 2 FULL 0 POR 40 0 0 2 0 0 0 50 0 0 0 0 0 0 PianoVoice 37 FIFTH 7 TRIANGL 31 6 10 0 off 5 0 off off off off 0 0 0 0 1 2 1 2 3 3 2.99 2.99 no no no no 8 8 34 10 W4 W1 W4 W1 -3 -3 3 3 31 31 31 31 31 28 31 28 15 15 15 15 0 0 0 0 5 4 5 4 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 99 67 0 0 0 0 30 30 30 30 -19 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 PianoStrng 38 FOURTH 0 TRIANGL 27 11 5 0 off 5 0 off off off off 0 0 0 0 2 4 2 4 1 1 1 15.7 no no no no 112 8 8 8 W5 W6 W6 W4 3 -3 -3 3 31 31 31 31 11 18 15 20 11 6 11 4 3 4 3 14 6 10 5 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 94 91 2 2 2 2 0 0 0 0 -12 POLY 4 FULL 0 POR 50 0 0 50 0 0 0 50 0 0 0 0 0 0 PlasticHit 39 FIFTH 5 TRIANGL 33 35 15 0 off 1 0 off off off off 0 0 0 0 6 4 7 4 2 2 1 14 no no no no 8 8 8 8 W2 W1 W1 W1 2 -3 3 -1 9 24 31 31 16 3 8 7 15 8 7 7 3 3 6 7 5 5 6 5 VOF VOF VOF VOF 99 99 99 50 50 50 94 88 99 67 0 0 1 0 0 0 0 0 -12 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 Pno String 40 SEVENTH 0 TRIANGL 28 14 9 46 off 4 2 off off off on 4 4 0 0 4 4 4 5 8.98 9.07 1.5 1.49 no no no no 14 15 16 97 W6 W6 W8 W5 3 -3 0 -1 7 6 31 31 7 11 31 31 14 14 15 15 1 1 8 8 4 4 5 5 VOF VOF VOF VOF 99 99 99 50 50 50 78 78 99 71 0 0 0 0 0 0 0 23 -19 POLY 2 FULL 0 POR 99 0 0 50 0 7 0 50 57 0 0 0 0 0 PnoClouds 41 FOURTH 6 TRIANGL 20 0 0 0 off 6 0 off off off off 0 0 0 0 3 4 4 4 1 1 2 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 -3 -3 -1 26 31 31 31 23 2 18 12 12 14 14 6 8 3 6 6 8 11 8 11 VOF VOF VOF VOF 99 99 99 50 50 50 99 97 96 86 0 2 0 0 0 67 0 0 0 POLY 1 FULL 0 POR 99 0 0 50 0 50 0 50 0 0 0 0 0 0 PopPercuss 42 EIGHTH 7 TRIANGL 34 0 15 0 on 3 1 on on off off 0 0 0 0 1 1 1 1 1.5 1.49 1.49 1.51 no no no no 16 9 9 14 W4 W4 W4 W4 3 -3 -3 3 14 14 14 14 8 8 8 8 15 15 15 14 4 7 5 6 5 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 98 98 99 0 0 0 0 20 22 29 29 -19 POLY 2 FINGER 3 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Sawsy Pad1 43 EIGHTH 0 TRIANGL 35 19 0 0 on 3 0 off off off off 0 0 0 0 4 4 4 4 8 6 5.65 5.65 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -3 -3 31 13 12 10 27 27 27 27 15 15 15 15 13 13 13 13 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 67 55 49 0 0 0 0 0 0 0 0 -12 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Solstice 44 FIFTH 7 TRIANGL 31 0 6 0 on 4 2 off off off on 0 0 0 0 3 0 3 0 1.51 1.49 0.75 9 no no no no 14 9 12 96 W6 W8 W1 W8 3 -3 3 -3 19 19 31 30 12 12 7 11 10 11 15 7 4 7 2 15 5 7 7 4 VOF VOF VOF VOF 99 99 99 50 50 50 87 84 92 67 0 0 0 0 0 5 8 0 -7 POLY 2 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 SpoonChoir 45 FIFTH 4 TRIANGL 30 0 0 0 off 2 0 off off off off 0 0 0 0 5 5 4 2 1 1 1 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 21 24 21 16 29 18 7 10 15 0 12 12 6 6 6 6 15 10 8 8 VOF VOF VOF VOF 99 99 99 50 50 50 99 90 99 89 0 1 1 1 0 0 0 0 -24 POLY 5 FULL 0 POR 99 0 0 99 99 0 0 50 0 0 0 0 0 0 SpreadThin 46 FIFTH 7 TRIANGL 27 40 16 0 off 2 0 off off off off 0 0 0 0 7 3 4 4 2 14 0.5 0.5 no no no no 8 8 8 8 W2 W1 W5 W3 0 0 1 -1 31 31 12 31 12 31 12 0 15 15 15 15 6 12 6 0 5 5 5 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 83 99 76 1 1 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 St Nick..1 47 SEVENTH 6 TRIANGL 34 40 43 0 off 1 0 off off off off 0 0 0 0 6 3 4 2 1 3 1 1 no no no no 8 8 8 8 W2 W2 W2 W3 0 0 3 -3 25 25 12 31 12 31 12 0 15 15 15 15 6 12 6 0 5 5 5 1 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 90 76 0 0 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 St Nick..2 48 THIRD 7 SHOLD 99 99 6 0 on 5 0 off off off off 0 0 0 0 6 4 6 5 1 1 0.5 2 no no no no 8 8 8 8 W1 W1 W1 W1 0 3 -3 -3 5 4 4 4 14 13 14 15 15 15 15 15 7 7 7 7 6 6 6 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 56 77 0 0 0 0 0 0 0 0 -24 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 StonedCycl 49 EIGHTH 5 SHOLD 50 0 99 0 on 3 0 off off off off 0 0 0 0 1 1 2 0 1 1 4 0.5 no no no no 8 8 8 8 W6 W5 W7 W6 3 -3 3 -3 31 27 15 12 0 0 0 15 15 15 15 15 13 13 13 13 7 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 0 0 0 0 0 0 0 0 0 POLY 12 FULL 0 POR 99 0 0 50 0 0 0 50 0 0 0 0 0 0 Texture * 50 FIFTH 0 TRIANGL 29 18 30 0 off 3 0 off off off off 0 0 0 0 2 5 2 2 1.5 20.99 1.51 1.49 no no no no 16 14 14 9 W1 W1 W1 W1 0 -3 1 -2 27 30 30 30 6 7 6 2 0 11 0 0 31 10 10 7 11 8 7 5 VOF VOF VOF VOF 99 99 99 50 50 50 96 89 99 94 2 3 1 3 0 0 0 0 -19 POLY 2 FULL 0 POR 40 0 0 99 0 0 0 50 0 0 0 0 0 0 That Girl* 51 FIFTH 0 TRIANGL 35 0 0 0 on 3 0 off off off off 0 0 0 0 6 2 5 4 1 1.73 4.24 1 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 31 31 27 31 9 15 3 18 13 14 14 14 15 12 1 12 6 4 2 6 VOF VOF VOF VOF 99 99 99 50 50 50 99 80 81 95 0 1 0 0 0 0 0 0 0 POLY 4 FULL 0 POR 99 0 0 1 99 0 0 50 0 0 0 0 0 0 Up We Go 52 FIFTH 0 TRIANGL 8 0 8 34 off 3 3 on on on off 0 0 0 0 2 1 4 1 2 8 2 12 no no no no 8 8 8 8 W1 W1 W1 W1 -3 3 3 -3 3 8 8 3 1 7 9 5 12 12 12 0 0 1 0 5 4 4 4 4 VOF VOF VOF VOF 99 99 99 50 50 50 91 52 99 53 3 1 3 1 50 64 50 64 0 POLY 4 FULL 0 POR 99 0 0 50 0 0 0 50 96 0 0 0 0 5 WineGlass* 53 FIFTH 6 TRIANGL 26 9 5 0 off 5 0 off off off off 0 0 0 0 5 6 5 7 4 15.25 4.02 8.04 no yes no no 8 84 17 10 W5 W8 W7 W4 -3 3 -3 -1 16 25 18 20 9 9 7 25 13 0 12 0 7 0 7 3 5 5 5 5 VOF V48 VOF V24 99 99 99 50 50 50 99 62 99 93 1 2 1 2 0 31 0 0 -24 POLY 2 FULL 0 POR 99 0 0 25 0 16 0 50 0 0 0 0 0 0 WireWaves 54 SEVENTH 7 TRIANGL 35 0 0 0 off 5 0 off off off off 0 0 0 0 1 2 2 6 1 3 3 12 no no no no 8 8 8 8 W1 W1 W1 W1 0 0 0 0 22 26 23 31 31 24 31 22 15 5 15 7 10 12 13 14 5 7 7 7 VOF VOF VOF VOF 99 99 99 50 50 50 99 99 99 99 1 1 1 1 0 12 0 21 0 POLY 2 FULL 0 POR 40 0 0 50 0 0 0 50 0 0 0 0 0 0 XyloSynth 55 muse-4.0.0/src/synti/deicsonze/TODO000066400000000000000000000007461404105311000170700ustar00rootroot00000000000000- Fix sync - Eg Rate Scaling - Foot Control, Modulation Wheel, Breath Control, After Touch - analogue reverb - Change presetName subcategoryName category with SysEx - Internal restructure, private vs public, add comment, etc - Optimize the code - Remember the last directory - Pan per voices - Load BUMP preset - calibrate portamento and pitch envelope to fit real DX11 - make deicsonze loadable as standalone by mus - It seems that there is still a bug on the note on, on some presets muse-4.0.0/src/synti/deicsonze/common_defs.h000066400000000000000000000024221404105311000210330ustar00rootroot00000000000000//========================================================= // MusE // Linux Music Editor // $Id: ./synti/deicsonze/common_defs.h $ // // Copyright (C) 1999-2011 by Werner Schweer and others // // 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; 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // //========================================================= #ifndef __DEICSONZE_UNIQUE_ID_H #define __DEICSONZE_UNIQUE_ID_H // Make sure this number is unique among all the MESS synths (including ticksynth) and DSSI, VST, LV2 and other host synths. // 127 is reserved for special MusE system messages. #define DEICSONZE_UNIQUE_ID 5 //#define DEICSONZE_DEBUG #endif muse-4.0.0/src/synti/deicsonze/deicsonze.cpp000066400000000000000000004726061404105311000210770ustar00rootroot00000000000000//=========================================================================== // // DeicsOnze an emulator of the YAMAHA DX11 synthesizer // // Version 0.5.5 // // // // // Copyright (c) 2004-2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #include #include #include #include "muse/midi_consts.h" #include "libsynti/mess.h" #include "deicsonze.h" #include "libsimpleplugin/simpler_plugin.h" #include "midictrl_consts.h" #define ABS(x) (x>=0?x:-x) float DeicsOnze::waveTable[NBRWAVES][RESOLUTION]; int DeicsOnze::useCount = 0; QString DEI_hostConfigPath; QString DEI_hostCachePath; QString DEI_globalLibPath; QString DEI_sharePath; unsigned int DEI_segmentSize; int DEI_sampleRate; bool DEI_useDenormalBias; float DEI_denormalBias; //--------------------------------------------------------- // DeicsOnze //--------------------------------------------------------- DeicsOnze::DeicsOnze() : Mess(2) { MusESimplePlugin::SS_initPlugins(DEI_hostCachePath); if (useCount++ == 0) { // create sinus wave table, W1 for(int i = 0; i < RESOLUTION; i++) waveTable[W1][i] = (float)(sin((i * 2.0 * M_PI) / (double)RESOLUTION)); // create sinus*abs(sinus) wave table, W2 for(int i = 0; i < RESOLUTION; i++){ double t = (i * 2.0 * M_PI) / (double)RESOLUTION; waveTable[W2][i] = (float)(ABS(sin(t))*sin(t));} // create halfsinus_ wave table, W3 for(int i = 0; i < RESOLUTION; i++) waveTable[W3][i] = (float) (ihide(); // to avoid flicker during MusE startup _gui->setWindowTitle(QString("DeicsOnze")); //FX MusESimplePlugin::Plugin* p; p = MusESimplePlugin::plugins.find("freeverb", "freeverb1"); _pluginIReverb = NULL; if(p) initPluginReverb(p); _pluginIChorus = NULL; p = MusESimplePlugin::plugins.find("doublechorus", "doublechorus1"); if(p) initPluginChorus(p); _pluginIDelay = NULL; p = MusESimplePlugin::plugins.find("pandelay", "pandelay"); if(p) initPluginDelay(p); //Filter _dryFilter = new LowFilter(); _chorusFilter = new LowFilter(); _reverbFilter = new LowFilter(); _delayFilter = new LowFilter(); _initialPreset = new Preset(new Subcategory(new Category(NULL, "NONE", 0), "NONE", 0), 0); for(int c = 0; c < NBRCHANNELS; c++) { _preset[c]=_initialPreset; setPreset(c); } //Load configuration QString defaultConf = DEI_hostConfigPath + QString("/" DEICSONZESTR ".dco"); FILE* f; f = fopen(defaultConf.toLatin1().data(), "r"); if(f) { fclose(f); loadConfiguration(defaultConf); } //load Set _set=new Set("Initial Bank"); if(_isInitSet) loadSet(_initSetPath); //loadSutulaPresets(); //update display gui //update mastervol unsigned char dataMasterVol[2]; dataMasterVol[0]=SYSEX_MASTERVOL; dataMasterVol[1]=getMasterVol(); MusECore::MidiPlayEvent evSysexMasterVol(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataMasterVol, 2); _gui->writeEvent(evSysexMasterVol); //update return fx unsigned char *dataReverbRet = new unsigned char[2]; dataReverbRet[0]=SYSEX_REVERBRETURN; dataReverbRet[1]=(unsigned char)getReverbReturn(); MusECore::MidiPlayEvent evReverbRet(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataReverbRet, 2); _gui->writeEvent(evReverbRet); unsigned char *dataChorusRet = new unsigned char[2]; dataChorusRet[0]=SYSEX_CHORUSRETURN; dataChorusRet[1]=(unsigned char)getChorusReturn(); MusECore::MidiPlayEvent evChorusRet(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataChorusRet, 2); _gui->writeEvent(evChorusRet); unsigned char *dataDelayRet = new unsigned char[2]; dataDelayRet[0]=SYSEX_DELAYRETURN; dataDelayRet[1]=(unsigned char)getDelayReturn(); //printf("DELAY RET = %d, REVERB RET = %d\n", //getDelayReturn(), getReverbReturn()); MusECore::MidiPlayEvent evDelayRet(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataDelayRet, 2); _gui->writeEvent(evDelayRet); //update font size unsigned char *dataFontSize = new unsigned char[2]; dataFontSize[0]=SYSEX_FONTSIZE; dataFontSize[1]=(unsigned char)_global.fontSize; MusECore::MidiPlayEvent evFontSize(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataFontSize, 2); _gui->writeEvent(evFontSize); //display load preset unsigned char dataUpdateGuiSet[1]; dataUpdateGuiSet[0]=SYSEX_UPDATESETGUI; MusECore::MidiPlayEvent evSysexUpdateGuiSet(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataUpdateGuiSet, 1); _gui->writeEvent(evSysexUpdateGuiSet); } //--------------------------------------------------------- // ~DeicsOnze //--------------------------------------------------------- DeicsOnze::~DeicsOnze() { if(_gui) delete _gui; if(_pluginIReverb) delete _pluginIReverb; if(_pluginIChorus) delete _pluginIChorus; if(_pluginIDelay) delete _pluginIDelay; //if (--useCount == 0) //delete[] sine_table; //dealloc temp buffers chorus and reverb for(int i = 0; i < NBRFXINPUTS; i++) free(tempInputChorus[i]); free(tempInputChorus); for(int i = 0; i < NBRFXOUTPUTS; i++) free(tempOutputChorus[i]); free(tempOutputChorus); for(int i = 0; i < NBRFXINPUTS; i++) free(tempInputReverb[i]); free(tempInputReverb); for(int i = 0; i < NBRFXOUTPUTS; i++) free(tempOutputReverb[i]); free(tempOutputReverb); for(int i = 0; i < NBRFXINPUTS; i++) free(tempInputDelay[i]); free(tempInputDelay); for(int i = 0; i < NBRFXOUTPUTS; i++) free(tempOutputDelay[i]); free(tempOutputDelay); if (initBuffer) delete [] initBuffer; } int DeicsOnze::oldMidiStateHeader(const unsigned char** data) const { static unsigned char const d[2] = {MUSE_SYNTH_SYSEX_MFG_ID, DEICSONZE_UNIQUE_ID}; *data = &d[0]; return 2; } //--------------------------------------------------------- // getSinusWaveTable //--------------------------------------------------------- float* DeicsOnze::getSinusWaveTable() { return waveTable[W1]; } //--------------------------------------------------------- // guiVisible //--------------------------------------------------------- bool DeicsOnze::nativeGuiVisible() const { return _gui->isVisible(); } //--------------------------------------------------------- // showGui //--------------------------------------------------------- void DeicsOnze::showNativeGui(bool val) { _gui->setVisible(val); } //--------------------------------------------------------- // getNativeGeometry //--------------------------------------------------------- void DeicsOnze::getNativeGeometry(int* x, int* y, int* w, int* h) const { QPoint pos(_gui->pos()); QSize size(_gui->size()); *x = pos.x(); *y = pos.y(); *w = size.width(); *h = size.height(); } void DeicsOnze::setSampleRate(int sr) { Mess::setSampleRate(sr); _dryFilter->setSamplerate(sr); _chorusFilter->setSamplerate(sr); _reverbFilter->setSamplerate(sr); _delayFilter->setSamplerate(sr); setQuality(_global.quality); } //--------------------------------------------------------- // setNativeGeometry //--------------------------------------------------------- void DeicsOnze::setNativeGeometry(int x, int y, int w, int h) { _gui->resize(QSize(w, h)); _gui->move(QPoint(x, y)); } //--------------------------------------------------------- // initCtrls //--------------------------------------------------------- void DeicsOnze::initCtrls() { int i=0; for(int k=0; kfindPreset(hbank, lbank, prog); } Subcategory* DeicsOnze::findSubcategory(int hbank, int lbank) const { return _set->findSubcategory(hbank, lbank); } Category* DeicsOnze::findCategory(int hbank) const { return _set->findCategory(hbank); } //--------------------------------------------------------- // isPitchEnv // return true iff all levels are in the middle //--------------------------------------------------------- inline bool isPitchEnv(PitchEg* pe) { return(pe->pl1 != 50 || pe->pl2 != 50 || pe->pl3 != 50); } //--------------------------------------------------------- // getPitchEnvCoefInct // returns the coefInct according to level pl //--------------------------------------------------------- inline double getPitchEnvCoefInct(int pl) { /* pl = 0 <--> -4oct, pl = 50 <--> 0oct, pl = 100 <--> 4oct y = a * exp((pl - 50)/b) 1.0 = a*exp(0) ==> a = 1.0 8.0 = exp(50/b) ==> log 8.0 = 50/b ==> b = 50/log(8.0) */ double b = 50.0/log(8.0); return exp((pl-50.0)/b); } //--------------------------------------------------------- // getPitchEnvCoefInctInct //--------------------------------------------------------- inline double getPitchEnvCoefInctInct(int pl1, int pl2, int pr, double sr) { //TODO : depending on the sampleRate int a = pr; double c = 1.0 + COEFPITCHENV*((double)(a*a)+1.0); double inctInct = exp(log(c)*48000.0/sr); if(pl1pl2) return(1.0/inctInct); else return 1.0; } //--------------------------------------------------------- // existsKeyOn //--------------------------------------------------------- bool DeicsOnze::existsKeyOn(int ch) { return !_global.channel[ch].lastVoiceKeyOn.empty(); } //--------------------------------------------------------- // note2Amp // return the Amp of a note depending on the level scaling //--------------------------------------------------------- inline double note2Amp(double note, int ls) { if(ls==0) return(1.0); else return((noteMAXNBRVOICES?MAXNBRVOICES:(nv<1?1:nv)); //we assume that any voices //that is not included in the active voices is properly initialized for(int v=nv; v<_global.channel[c].nbrVoices; v++) initVoice(c, v); _global.channel[c].nbrVoices=nv; } //---------------------------------------------------------------- // setMasterVol //---------------------------------------------------------------- void DeicsOnze::setMasterVol(int mv) { _global.masterVolume=level2amp(mv); //watch out that MAXMASTERVOLUME==255 } //---------------------------------------------------------------- // setChannelEnable //---------------------------------------------------------------- void DeicsOnze::setChannelEnable(int c, bool e) { _global.channel[c].isEnable = e; setLfo(c); } //---------------------------------------------------------------- // setChannelVol //---------------------------------------------------------------- void DeicsOnze::setChannelVol(int c, int v) { _global.channel[c].volume = v; } void DeicsOnze::applyChannelAmp(int c) { _global.channel[c].ampLeft = level2amp(_global.channel[c].volume) * ((double)(MAXCHANNELPAN - _global.channel[c].pan) /(double)(2*MAXCHANNELPAN)); _global.channel[c].ampRight = level2amp(_global.channel[c].volume) * ((double)(MAXCHANNELPAN + _global.channel[c].pan) /(double)(2*MAXCHANNELPAN)); } //---------------------------------------------------------------- // setChannelPan //---------------------------------------------------------------- void DeicsOnze::setChannelPan(int c, int p) { _global.channel[c].pan = p; } //---------------------------------------------------------------- // setChannelDetune //---------------------------------------------------------------- void DeicsOnze::setChannelDetune(int c, int p) { _global.channel[c].detune = p; } //---------------------------------------------------------------- // setChannelBrightness //---------------------------------------------------------------- void DeicsOnze::setChannelBrightness(int c, int b) { _global.channel[c].brightness = b; } //---------------------------------------------------------------- // setChannelModulation //---------------------------------------------------------------- void DeicsOnze::setChannelModulation(int c, int m) { _global.channel[c].modulation = m; } //---------------------------------------------------------------- // setChannelAttack //---------------------------------------------------------------- void DeicsOnze::setChannelAttack(int c, int a) { _global.channel[c].attack = a; } //---------------------------------------------------------------- // setChannelRelease //---------------------------------------------------------------- void DeicsOnze::setChannelRelease(int c, int r) { _global.channel[c].release = r; } //---------------------------------------------------------------- // setChannelReverb //---------------------------------------------------------------- void DeicsOnze::setChannelReverb(int c, int r) { _global.channel[c].reverbAmount = (float)lowlevel2amp(r); } //---------------------------------------------------------------- // setChannelChorus //---------------------------------------------------------------- void DeicsOnze::setChannelChorus(int c, int val) { _global.channel[c].chorusAmount = (float)lowlevel2amp(val); } //---------------------------------------------------------------- // setChannelDelay //---------------------------------------------------------------- void DeicsOnze::setChannelDelay(int c, int val) { _global.channel[c].delayAmount = (float)lowlevel2amp(val); } //---------------------------------------------------------------- // setChorusReturn //---------------------------------------------------------------- void DeicsOnze::setChorusReturn(int val) { _global.chorusReturn = 2.0*(float)level2amp(val); //beware MAXFXRETURN==255 } //---------------------------------------------------------------- // setReverbReturn //---------------------------------------------------------------- void DeicsOnze::setReverbReturn(int val) { _global.reverbReturn = 2.0*(float)level2amp(val); //beware MAXFXRETURN==255 } //---------------------------------------------------------------- // setDelayReturn //---------------------------------------------------------------- void DeicsOnze::setDelayReturn(int val) { _global.delayReturn = 2.0*(float)level2amp(val); //beware MAXFXRETURN==255 } //---------------------------------------------------------------- // getNbrVoices //---------------------------------------------------------------- int DeicsOnze::getNbrVoices(int c) const { return(_global.channel[c].nbrVoices); } //---------------------------------------------------------------- // getMasterVol //---------------------------------------------------------------- int DeicsOnze::getMasterVol(void) const { return(amp2level(_global.masterVolume)); } //---------------------------------------------------------------- // getFilter //---------------------------------------------------------------- bool DeicsOnze::getFilter(void) const { return _global.filter; } //---------------------------------------------------------------- // getChannelEnable //---------------------------------------------------------------- bool DeicsOnze::getChannelEnable(int c) const { return _global.channel[c].isEnable; } //---------------------------------------------------------------- // getChannelVol //---------------------------------------------------------------- int DeicsOnze::getChannelVol(int c) const { //TODO : to see if correct //return((int)(MAX(_global.channel[c].ampLeft, _global.channel[c].ampRight) //*(double)MAXCHANNELVOLUME)); return(_global.channel[c].volume); } //---------------------------------------------------------------- // getChannelPan //---------------------------------------------------------------- int DeicsOnze::getChannelPan(int c) const { return(_global.channel[c].pan); } //---------------------------------------------------------------- // setChannelDetune //---------------------------------------------------------------- int DeicsOnze::getChannelDetune(int c) const { return _global.channel[c].detune; } //---------------------------------------------------------------- // getChannelBrightness //---------------------------------------------------------------- int DeicsOnze::getChannelBrightness(int c) const { return(_global.channel[c].brightness); } //---------------------------------------------------------------- // getChannelModulation //---------------------------------------------------------------- int DeicsOnze::getChannelModulation(int c) const { return(_global.channel[c].modulation); } //---------------------------------------------------------------- // getChannelAttack //---------------------------------------------------------------- int DeicsOnze::getChannelAttack(int c) const { return(_global.channel[c].attack); } //---------------------------------------------------------------- // getChannelRelease //---------------------------------------------------------------- int DeicsOnze::getChannelRelease(int c) const { return(_global.channel[c].release); } //---------------------------------------------------------------- // getChannelReverb //---------------------------------------------------------------- int DeicsOnze::getChannelReverb(int c) const { return(amp2lowlevel(_global.channel[c].reverbAmount)); } //---------------------------------------------------------------- // getChannelChorus //---------------------------------------------------------------- int DeicsOnze::getChannelChorus(int c) const { return(amp2lowlevel(_global.channel[c].chorusAmount)); } //---------------------------------------------------------------- // getChannelDelay //---------------------------------------------------------------- int DeicsOnze::getChannelDelay(int c) const { return(amp2lowlevel(_global.channel[c].delayAmount)); } //---------------------------------------------------------------- // getChorusReturn //---------------------------------------------------------------- int DeicsOnze::getChorusReturn() const { return(amp2level(_global.chorusReturn/2.0)); } //---------------------------------------------------------------- // getReverbReturn //---------------------------------------------------------------- int DeicsOnze::getReverbReturn() const { return(amp2level(_global.reverbReturn/2.0)); } //---------------------------------------------------------------- // getReverbReturn //---------------------------------------------------------------- int DeicsOnze::getDelayReturn() const { return(amp2level(_global.delayReturn/2.0)); } //---------------------------------------------------------------- // setLfo //---------------------------------------------------------------- void DeicsOnze::setLfo(int c/*channel*/) { double x; x=(double)_preset[c]->lfo.speed; // lfoSpeed to Hz, obtained by fitting the actual curve by a polynomial _global.channel[c].lfoFreq = -1.9389e-08*x*x*x*x*x+2.8826e-06*x*x*x*x-9.0316e-05*x*x*x +4.7453e-03*x*x-1.2295e-02*x+7.0347e-02;//a revoir //Pitch LFO _global.channel[c].lfoMaxIndex = (_global.channel[c].lfoFreq==0?0:(int)((1.0/_global.channel[c].lfoFreq) *(double)_global.deiSampleRate)); double totalpDepth = ((double)_preset[c]->lfo.pModDepth + (((double)_global.channel[c].modulation)/127.0) * ((double)(MAXPMODDEPTH - _preset[c]->lfo.pModDepth)) )/(double)MAXPMODDEPTH; _global.channel[c].lfoPitch = totalpDepth * (COEFPLFO(_preset[c]->sensitivity.pitch)); //Amplitude LFO double totalaDepth = ((double)_preset[c]->lfo.aModDepth + (((double)_global.channel[c].modulation)/127.0) * ((double)(MAXAMODDEPTH - _preset[c]->lfo.aModDepth)) )/(double)MAXAMODDEPTH; _global.channel[c].lfoMaxAmp = totalaDepth * (COEFALFO(_preset[c]->sensitivity.amplitude)); //index is considered on the half of the frequency of the LFO _global.channel[c].lfoDelayMaxIndex = delay2Time(_preset[c]->lfo.delay)*_global.channel[c].lfoFreq*2; _global.channel[c].lfoDelayInct = (double)(RESOLUTION/4)/_global.channel[c].lfoDelayMaxIndex; //update the actuall values controlling the modulation now if(_global.channel[c].lfoDelayIndex<(double)(RESOLUTION/4)) { double delayCoef = (double)waveTable[W2][(int)_global.channel[c].lfoDelayIndex]; _global.channel[c].lfoMaxCoefInct = exp((log(2.0)/12.0)*_global.channel[c].lfoPitch*delayCoef); _global.channel[c].lfoCoefInctInct = exp((log(2.0)/12.0)*((2*_global.channel[c].lfoPitch*delayCoef) /_global.channel[c].lfoMaxIndex)); _global.channel[c].lfoMaxDAmp = delayCoef*_global.channel[c].lfoMaxAmp; } else if(_global.channel[c].delayPassed) { _global.channel[c].lfoMaxCoefInct = exp((log(2.0)/12.0)*_global.channel[c].lfoPitch); _global.channel[c].lfoCoefInctInct= exp((log(2.0)/12.0)*((2*_global.channel[c].lfoPitch) /_global.channel[c].lfoMaxIndex)); _global.channel[c].lfoMaxDAmp=_global.channel[c].lfoMaxAmp; } } //----------------------------------------------------------------- // setOutLevel //----------------------------------------------------------------- void DeicsOnze::setOutLevel(int c, int k) { for(int v=0; v<_global.channel[c].nbrVoices; v++) { if(_global.channel[c].voices[v].op[k].envState!=OFF) { _global.channel[c].voices[v].op[k].amp = outLevel2Amp(_preset[c]->outLevel[k]) * _global.channel[c].voices[v].op[k].ampVeloNote * brightness2Amp(c, k); } } } void DeicsOnze::setOutLevel(int c) { for(int k=0; keg[k].ar==0?0: (double)(RESOLUTION/4)/(envAR2s(_preset[c]->eg[k].ar) *_global.deiSampleRate)) *coefAttack(_global.channel[c].attack); } void DeicsOnze::setEnvAttack(int c, int k) { for(int v=0; v<_global.channel[c].nbrVoices; v++) setEnvAttack(c, v, k); } void DeicsOnze::setEnvAttack(int c) { for(int k=0; keg[k].rr, _global.deiSampleRate, _global.channel[c].release); } void DeicsOnze::setEnvRelease(int c, int k) { for(int v=0; v<_global.channel[c].nbrVoices; v++) setEnvRelease(c, v, k); } void DeicsOnze::setEnvRelease(int c) { for(int k=0; kpitchEg)) { if(_global.channel[c].voices[v].pitchEnvCoefInct > _global.channel[c].voices[v].pitchEnvCoefInctPhase1) { _global.channel[c].voices[v].pitchEnvCoefInctInct = getPitchEnvCoefInctInct(1, 0, _preset[c]->pitchEg.pr3, _global.deiSampleRate); _global.channel[c].voices[v].pitchEnvState = RELEASE_PE; } else if(_global.channel[c].voices[v].pitchEnvCoefInct < _global.channel[c].voices[v].pitchEnvCoefInctPhase1) { _global.channel[c].voices[v].pitchEnvCoefInctInct = getPitchEnvCoefInctInct(0, 1, _preset[c]->pitchEg.pr3, _global.deiSampleRate); _global.channel[c].voices[v].pitchEnvState = RELEASE_PE; } else { _global.channel[c].voices[v].pitchEnvCoefInctInct = 1.0; _global.channel[c].voices[v].pitchEnvState = OFF_PE; } } } //----------------------------------------------------------------- // setQuality //----------------------------------------------------------------- void DeicsOnze::setQuality(Quality q) { _global.quality = q; switch(q) { case high : _global.qualityCounterTop = 1; break; case middle : _global.qualityCounterTop = 2; break; case low : _global.qualityCounterTop = 4; break; case ultralow : _global.qualityCounterTop = 6; break; default : printf("Error switch setQuality : out of value\n"); break; } //calculate _global.deiSampleRate _global.deiSampleRate = (double)sampleRate() / (double)_global.qualityCounterTop; _global.qualityCounter = 0; //update lfo to consider the new samplerate for(int c = 0; c < 16; c++) if(_global.channel[c].isEnable) setLfo(c); //update the cutoffs of the filters _dryFilter->setCutoff(_global.deiSampleRate/4.0); _reverbFilter->setCutoff(_global.deiSampleRate/4.0); _chorusFilter->setCutoff(_global.deiSampleRate/4.0); _delayFilter->setCutoff(_global.deiSampleRate/4.0); } //----------------------------------------------------------------- // setFilter //----------------------------------------------------------------- void DeicsOnze::setFilter(bool f) { _global.filter = f; } //----------------------------------------------------------------- // brightness2Amp //----------------------------------------------------------------- double DeicsOnze::brightness2Amp(int c, int k) { if( (k==1 && (_preset[c]->algorithm!=SIXTH || _preset[c]->algorithm!=SEVENTH || _preset[c]->algorithm!=EIGHTH)) || (k==2 && (_preset[c]->algorithm==FIRST || _preset[c]->algorithm==SECOND || _preset[c]->algorithm==THIRD || _preset[c]->algorithm==FOURTH)) || (k==3 && (_preset[c]->algorithm!=EIGHTH)) ) { double x = 2.0*(double)_global.channel[c].brightness / (double)MAXFINEBRIGHTNESS; double square_x = x*x; return(square_x*x); } else return(1.0); } //----------------------------------------------------------------- // setFeedback //----------------------------------------------------------------- void DeicsOnze::setFeedback(int c) { _global.channel[c].feedbackAmp = COEFFEEDBACK*exp(log(2)*(double)(_preset[c]->feedback-MAXFEEDBACK)); } //----------------------------------------------------------------- // setPreset //----------------------------------------------------------------- void DeicsOnze::setPreset(int c) { setFeedback(c); setLfo(c); setEnvAttack(c); setEnvRelease(c); setOutLevel(c); } inline double coarseFine2Ratio(int c,int f) { double tab[64][16]= { {0.50,0.56,0.62,0.68,0.75,0.81,0.87,0.93,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}, {0.71,0.79,0.88,0.96,1.05,1.14,1.23,1.32,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}, {0.78,0.88,0.98,1.07,1.17,1.27,1.37,1.47,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}, {0.87,0.97,1.08,1.18,1.29,1.40,1.51,1.62,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0}, {1.00,1.06,1.12,1.18,1.25,1.31,1.37,1.43,1.50,1.56,1.62,1.68,1.75,1.81,1.87,1.93}, {1.41,1.49,1.58,1.67,1.76,1.85,1.93,2.02,2.11,2.20,2.29,2.37,2.46,2.55,2.64,2.73}, {1.57,1.66,1.76,1.86,1.96,2.06,2.15,2.25,2.35,2.45,2.55,2.64,2.74,2.84,2.94,3.04}, {1.73,1.83,1.94,2.05,2.16,2.27,2.37,2.48,2.59,2.70,2.81,2.91,3.02,3.13,3.24,3.35}, {2.00,2.06,2.12,2.18,2.25,2.31,2.37,2.43,2.50,2.56,2.62,2.68,2.75,2.81,2.87,2.93}, {2.82,2.90,2.99,3.08,3.17,3.26,3.34,3.43,3.52,3.61,3.70,3.78,3.87,3.96,4.05,3.14}, {3.00,3.06,3.12,3.18,3.25,3.31,3.37,3.43,3.50,3.56,3.62,3.68,3.75,3.81,3.87,3.93} , {3.14,3.23,3.33,3.43,3.53,3.63,3.72,3.82,3.92,4.02,4.12,4.21,4.31,4.41,4.51,4.61}, {3.46,3.56,3.67,3.78,3.89,4.00,4.10,4.21,4.32,4.43,4.54,4.64,4.75,4.86,4.97,5.08}, {4.00,4.06,4.12,4.18,4.25,4.31,4.37,4.43,4.50,4.56,4.62,4.68,4.75,4.81,4.87,4.93}, {4.24,4.31,4.40,4.49,4.58,4.67,4.75,4.84,4.93,5.02,5.11,5.19,5.28,5.37,5.46,5.55}, {4.71,4.80,4.90,5.00,5.10,5.20,5.29,5.39,5.49,5.59,5.69,5.78,5.88,5.98,6.08,6.18}, {5.00,5.06,5.12,5.18,5.25,5.31,5.37,5.43,5.50,5.56,5.62,5.68,5.75,5.81,5.87,5.93}, {5.19,5.29,5.40,5.51,5.62,5.73,5.83,5.94,6.05,6.16,6.27,6.37,6.48,6.59,6.70,6.81}, {5.65,5.72,5.81,5.90,5.99,6.08,6.16,6.25,6.34,6.43,6.52,6.60,6.69,6.78,6.87,6.96}, {6.00,6.06,6.12,6.18,6.25,6.31,6.37,6.43,6.50,6.56,6.62,6.68,6.75,6.81,6.87,6.93}, {6.28,6.37,6.47,6.57,6.67,6.77,6.86,6.96,7.06,7.16,7.26,7.35,7.45,7.55,7.65,7.75}, {6.92,7.02,7.13,7.24,7.35,7.46,7.56,7.67,7.78,7.89,8.00,8.10,8.21,8.32,8.43,8.54}, {7.00,7.06,7.12,7.18,7.25,7.31,7.37,7.43,7.50,7.56,7.62,7.68,7.75,7.81,7.87,7.93}, {7.07,7.13,7.22,7.31,7.40,7.49,7.57,7.66,7.75,7.84,7.93,8.01,8.10,8.19,8.28,8.37}, {7.85,7.94,8.04,8.14,8.24,8.34,8.43,8.53,8.63,8.73,8.83,8.92,9.02,9.12,9.22,9.32}, {8.00,8.06,8.12,8.18,8.25,8.31,8.37,8.43,8.50,8.56,8.62,8.68,8.75,8.81,8.87,8.93}, {8.48,8.54,8.63,8.72,8.81,8.90,8.98,9.07,9.16,9.25,9.34,9.42,9.51,9.60,9.69,9.78}, {8.65,8.75,8.86,8.97,9.08,9.19,9.29,9.40,9.51,9.62,9.73,9.83,9.94,10.05,10.16,10.27}, {9.00,9.06,9.12,9.18,9.25,9.31,9.37,9.43,9.50,9.56,9.62,9.68,9.75,9.81,9.87,9.93}, {9.42,9.51,9.61,9.71,9.81,9.91,10.00,10.10,10.20,10.30,10.40,10.49,10.59,10.69,10.79,10.89}, {9.89,9.95,10.04,10.13,10.22,10.31,10.39,10.48,10.57,10.66,10.75,10.83,10.92,11.01,11.10,11.19}, {10.00,10.06,10.12,10.18,10.25,10.31,10.37,10.43,10.50,10.56,10.62,10.68,10.75,10.81,10.87,10.93}, {10.38,10.48,10.59,10.70,10.81,10.92,11.02,11.13,11.24,11.35,11.46,11.56,11.67,11.78,11.89,12.00}, {10.99,11.08,11.18,11.28,11.38,11.48,11.57,11.67,11.77,11.87,11.97,12.06,12.16,12.26,12.36,12.46}, {11.00,11.06,11.12,11.18,11.25,11.31,11.37,11.43,11.50,11.56,11.62,11.68,11.75,11.81,11.87,11.93}, {11.30,11.36,11.45,11.54,11.63,11.72,11.80,11.89,11.98,12.07,12.16,12.24,12.33,12.42,12.51,12.60}, {12.00,12.06,12.12,12.18,12.25,12.31,12.37,12.43,12.50,12.56,12.62,12.68,12.75,12.81,12.87,12.93}, {12.11,12.21,12.32,12.43,12.54,12.65,12.75,12.86,12.97,13.08,13.19,13.29,13.40,13.51,13.62,13.73}, {12.56,12.65,12.75,12.85,12.95,13.05,13.14,13.24,13.34,13.44,13.54,13.63,13.73,13.83,13.93,14.03}, {12.72,12.77,12.86,12.95,13.04,13.13,13.21,13.30,13.39,13.48,13.57,13.65,13.74,13.83,13.92,14.01}, {13.00,13.06,13.12,13.18,13.25,13.31,13.37,13.43,13.50,13.56,13.62,13.68,13.75,13.81,13.87,13.93}, {13.84,13.94,14.05,14.16,14.27,14.38,14.48,14.59,14.70,14.81,14.92,15.02,15.13,15.24,15.35,15.46}, {14.00,14.06,14.12,14.18,14.25,14.31,14.37,14.43,14.50,14.56,14.62,14.68,14.75,14.81,14.87,14.93}, {14.10,14.18,14.27,14.36,14.45,14.54,14.62,14.71,14.80,14.89,14.98,15.06,15.15,15.24,15.33,15.42}, {14.13,14.22,14.32,14.42,14.52,14.62,14.71,14.81,14.91,15.01,15.11,15.20,15.30,15.40,15.50,15.60}, {15.00,15.06,15.12,15.18,15.25,15.31,15.37,15.43,15.50,15.56,15.62,15.68,15.75,15.81,15.87,15.93}, {15.55,15.59,15.68,15.77,15.86,15.95,16.03,16.12,16.21,16.30,16.39,16.47,16.56,16.65,16.74,16.83}, {15.57,15.67,15.78,15.89,16.00,16.11,16.21,16.32,16.43,16.54,16.65,16.75,16.86,16.97,17.08,17.19}, {15.70,15.79,15.89,15.99,16.09,16.19,16.28,16.38,16.48,16.58,16.68,16.77,16.87,16.97,17.07,17.17}, {16.96,17.00,17.09,17.18,17.27,17.36,17.44,17.53,17.62,17.71,17.80,17.88,17.97,18.06,18.15,18.24}, {17.27,17.36,17.46,17.56,17.66,17.76,17.85,17.95,18.05,18.15,18.25,18.34,18.44,18.54,18.64,18.74}, {17.30,17.40,17.51,17.62,17.73,17.84,17.94,18.05,18.16,18.27,18.38,18.48,18.59,18.70,18.81,18.92}, {18.37,18.41,18.50,18.59,18.68,18.77,18.85,18.94,19.03,19.12,19.21,19.29,19.38,19.47,19.56,19.65}, {18.84,18.93,19.03,19.13,19.23,19.33,19.42,19.52,19.62,19.72,19.82,19.91,20.01,20.11,20.21,20.31}, {19.03,19.13,19.24,19.35,19.46,19.57,19.67,19.78,19.89,20.00,20.11,20.21,20.32,20.43,20.54,20.65}, {19.78,19.82,19.91,20.00,20.09,20.18,20.26,20.35,20.44,20.53,20.62,20.70,20.79,20.88,20.97,21.06}, {20.41,20.50,20.60,20.70,20.80,20.90,20.99,21.09,21.19,21.29,21.39,21.48,21.58,21.68,21.78,21.88}, {20.76,20.86,20.97,21.08,21.19,21.30,21.40,21.51,21.62,21.73,21.84,21.94,22.05,22.16,22.27,22.38}, {21.20,21.23,21.32,21.41,21.50,21.59,21.67,21.76,21.85,21.94,22.03,22.11,22.20,22.29,22.38,22.47}, {21.98,22.07,22.17,22.17,22.37,22.47,22.56,22.66,22.76,22.86,22.96,23.05,23.15,23.25,23.35,23.45}, {22.49,22.59,22.70,22.81,22.92,23.03,23.13,13.24,13.35,13.46,13.57,13.67,13.78,13.89,24.00,24.11}, {23.55,23.64,23.74,23.84,23.94,24.04,24.13,24.23,24.33,24.43,24.53,24.62,24.72,24.82,24.92,25.02}, {24.22,24.32,24.43,24.54,24.65,24.76,24.86,24.97,25.08,25.19,25.30,25.40,25.51,25.62,25.73,25.84}, {25.95,26.05,26.16,26.27,26.38,26.49,26.59,26.70,26.81,26.92,27.03,27.13,27.24,27.35,27.46,27.57} }; return(tab[c][f]); } //--------------------------------------------------------------- // loadSet //--------------------------------------------------------------- void DeicsOnze::loadSet(QString fileName) { // read the XML file and create DOM tree if(!fileName.isEmpty()) { QFile deicsonzeFile(fileName); if(!deicsonzeFile.open(QIODevice::ReadOnly)) { printf("Critical Error Cannot open file %s\n", fileName.toLatin1().data()); return; } QDomDocument domTree; if (!domTree.setContent(&deicsonzeFile )) { printf("Critical Error Parsing error for file %s\n", fileName.toLatin1().data()); deicsonzeFile.close(); return; } deicsonzeFile.close(); QDomNode node = domTree.documentElement(); while (!node.isNull()) { QDomElement e = node.toElement(); if (e.isNull()) continue; if (e.tagName() == "deicsOnzeSet") { QString version = e.attribute(QString("version")); if (version == "1.0") { for(int c = 0; c_categoryVector.empty()) delete(*_set->_categoryVector.begin()); _set->readSet(node.firstChild()); //display load preset unsigned char dataUpdateGuiSet[1]; dataUpdateGuiSet[0]=SYSEX_UPDATESETGUI; MusECore::MidiPlayEvent evSysexUpdateGuiSet(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataUpdateGuiSet, 1); _gui->writeEvent(evSysexUpdateGuiSet); } else printf("unsupported *.dei file version %s\n", version.toLatin1().constData()); } else printf("DeicsOnze: %s not supported\n", e.tagName().toLatin1().constData()); node = node.nextSibling(); } } } //--------------------------------------------------------------- // loadSutulaPreset //--------------------------------------------------------------- void DeicsOnze::loadSutulaPresets() { FILE* file; int v; int crs[NBROP], fin[NBROP]; //coarse ratio, fine ratio char s[500]; char sname[LENGTHNAME+1]; char scategory[LENGTHCATEGORY+1]; char ssubcategory[LENGTHSUBCATEGORY+1]; int k; int nhBank, nlBank, nPreset; Preset* presetTemp; Subcategory* subcategoryTemp = NULL; Category* categoryTemp = NULL; if(!_set) _set=new Set("Sutula Bank"); nhBank=0; nlBank=0; nPreset=0; //QString presetPath(INSTPREFIX); //presetPath += "/share/" PACKAGEVERSION "/presets/deicsonze/ARCH_ALIN"; QString presetPath("/home/a-lin/sources/svnMusEDev/lmuse/muse/synti/deicsonze/ARCH_ALIN"); file = fopen (presetPath.toLatin1().constData(), "rt"); if (file == NULL) { printf("can't open "); printf("%s", presetPath.toLatin1().constData()); printf("\n"); } else { while(fgets(s, 500, file) && !strstr(s, "** Source:")) { if (strstr(s,"* CATEGORY")) { sscanf(s, "* CATEGORY %s", scategory); categoryTemp=new Category(_set, scategory,0); } if (strstr(s,"* SUBCATEGORY")) { sscanf(s, "* SUBCATEGORY %s", ssubcategory); subcategoryTemp=new Subcategory(categoryTemp,ssubcategory,0); nlBank++; } } while(!feof(file)) { presetTemp=new Preset(subcategoryTemp); // Fill the preset //OP.4 to OP.1 for(int kaka=(NBROP-1); kaka>=0; kaka--) { k=(kaka==2?1:(kaka==1?2:kaka)); fscanf(file, "%x", &v);//0 presetTemp->eg[k].ar=v; fscanf(file, "%x", &v);//1 presetTemp->eg[k].d1r=v; fscanf(file, "%x", &v);//2 presetTemp->eg[k].d2r=v; fscanf(file, "%x", &v);//3 presetTemp->eg[k].rr=v; fscanf(file, "%x", &v);//4 presetTemp->eg[k].d1l=v; fscanf(file, "%x", &v);//5 presetTemp->scaling.level[k]=v; fscanf(file, "%x", &v);//6 presetTemp->sensitivity.keyVelocity[k]= v & 0x7; presetTemp->sensitivity.egBias[k]= (v & 0x38)>>3; presetTemp->sensitivity.ampOn[k]= (v & 0x40)>>6; fscanf(file, "%x", &v);//7 presetTemp->outLevel[k]=v; fscanf(file, "%x", &v);//8 crs[k]=v; fscanf(file, "%x", &v);//9 presetTemp->detune[k]=(v & 0x7)-3; presetTemp->scaling.rate[k]=(v & 0x18)>>3; } fscanf(file, "%x", &v);//40 presetTemp->algorithm= ((v & 0x7)==0?FIRST: ((v & 0x7)==1?SECOND: ((v & 0x7)==2?THIRD: ((v & 0x7)==3?FOURTH: ((v & 0x7)==4?FIFTH: ((v & 0x7)==5?SIXTH: ((v & 0x7)==6?SEVENTH:EIGHTH))))))); presetTemp->feedback=(v & 0x38)>>3; presetTemp->lfo.sync=(v & 0x40)>>6; fscanf(file, "%x", &v);//41 presetTemp->lfo.speed=v; fscanf(file, "%x", &v);//42 presetTemp->lfo.delay=v; fscanf(file, "%x", &v);//43 presetTemp->lfo.pModDepth=v; fscanf(file, "%x", &v);//44 presetTemp->lfo.aModDepth=v; fscanf(file, "%x", &v);//45 presetTemp->lfo.wave= ((v & 0x3)==0?SAWUP: ((v & 0x3)==1?SQUARE: ((v & 0x3)==2?TRIANGL:SHOLD))); presetTemp->sensitivity.amplitude=(v & 0xc)>>2; presetTemp->sensitivity.pitch=(v & 0x70)>>4; fscanf(file, "%x", &v);//46 presetTemp->function.transpose=v-24; fscanf(file, "%x", &v);//47 presetTemp->function.pBendRange=v; fscanf(file, "%x", &v);//48 presetTemp->function.portamento= ((v & 0x1)==0?FULL:FINGER); presetTemp->function.footSw= ((v & 0x4)==0?SUS:POR); presetTemp->function.mode= ((v & 0x8)==0?POLY:MONO); fscanf(file, "%x", &v);//49 presetTemp->function.portamentoTime=v; fscanf(file, "%x", &v);//50 presetTemp->function.fcVolume=v; fscanf(file, "%x", &v);//51 presetTemp->function.mwPitch=v; fscanf(file, "%x", &v);//52 presetTemp->function.mwAmplitude=v; fscanf(file, "%x", &v);//53 presetTemp->function.bcPitch=v; fscanf(file, "%x", &v);//54 presetTemp->function.bcAmplitude=v; fscanf(file, "%x", &v);//55 presetTemp->function.bcPitchBias=v; fscanf(file, "%x", &v);//56 presetTemp->function.bcEgBias=v; for(int l=0; l<10; l++) { fscanf(file, "%x", &v);//57 to 66 sname[l]=(char)v; } sname[10]='\0'; presetTemp->name=sname; fscanf(file, "%x", &v);//67 presetTemp->pitchEg.pr1=v; fscanf(file, "%x", &v);//68 presetTemp->pitchEg.pr2=v; fscanf(file, "%x", &v);//69 presetTemp->pitchEg.pr3=v; fscanf(file, "%x", &v);//70 presetTemp->pitchEg.pl1=v; fscanf(file, "%x", &v);//71 presetTemp->pitchEg.pl1=v; fscanf(file, "%x", &v);//72 presetTemp->pitchEg.pl1=v; for(int kaka=(NBROP-1); kaka>=0; kaka--) { k=(kaka==2?1:(kaka==1?2:kaka)); fscanf(file, "%x", &v);//73, 75, 77, 79 presetTemp->frequency[k].isFix=(v & 0x8)>>3; presetTemp->frequency[k].freq=((v & 0x7)==0?8:(v & 0x7)*16); presetTemp->eg[k].egShift= (((v & 0x30)>>4)==0?VOF: (((v & 0x30)>>4)==1?V48: (((v & 0x30)>>4)==2?V24:V12))); fscanf(file, "%x", &v);//74, 76, 78, 80 fin[k]=v & 0xF; presetTemp->frequency[k].freq+=fin[k]; presetTemp->frequency[k].ratio= coarseFine2Ratio(crs[k],fin[k]); presetTemp->oscWave[k]= (((v & 0x70)>>4)==0?W1: (((v & 0x70)>>4)==1?W2: (((v & 0x70)>>4)==2?W3: (((v & 0x70)>>4)==3?W4: (((v & 0x70)>>4)==4?W5: (((v & 0x70)>>4)==5?W6: (((v & 0x70)>>4)==6?W7:W8))))))); } fscanf(file, "%x", &v);//81 presetTemp->function.reverbRate=v; fscanf(file, "%x", &v);//82 presetTemp->function.fcPitch=v; fscanf(file, "%x", &v);//83 presetTemp->function.fcAmplitude=v; //presetTemp->globalDetune=0; presetTemp->prog=nPreset; // End of filling the preset nPreset++; while(fgets(s, 500, file) && !strstr(s, "** Source:")) { if (strstr(s,"* CATEGORY")) { sscanf(s, "* CATEGORY %s", scategory); nhBank++; categoryTemp=new Category(_set,scategory,nhBank); nlBank=0; } if (strstr(s,"* SUBCATEGORY")) { sscanf(s, "* SUBCATEGORY %s", ssubcategory); subcategoryTemp=new Subcategory(categoryTemp,ssubcategory,nlBank); nlBank++; nPreset=0; } } } } fclose(file); } //--------------------------------------------------------- // minVolu2Voice // return the number of the voice which is the least aloud // and is not is the ATTACK state //--------------------------------------------------------- int DeicsOnze::minVolu2Voice(int c) { int minVoice=0; double min=MAXVOLUME; for(int i=0; i<_global.channel[c].nbrVoices; i++) { min=((min>_global.channel[c].voices[i].volume && _global.channel[c].voices[i].op[0].envState!=ATTACK && _global.channel[c].voices[i].op[1].envState!=ATTACK && _global.channel[c].voices[i].op[2].envState!=ATTACK && _global.channel[c].voices[i].op[3].envState!=ATTACK)? _global.channel[c].voices[i].volume:min); minVoice=(min==_global.channel[c].voices[i].volume?i:minVoice); } return minVoice; } //--------------------------------------------------------- // noteOff2Voice // return the number of one off voice, MAXNBRVOICES otherwise //--------------------------------------------------------- int DeicsOnze::noteOff2Voice(int c) { int offVoice=MAXNBRVOICES; for(int i=0; i<_global.channel[c].nbrVoices; i++) offVoice = (_global.channel[c].voices[i].isOn || _global.channel[c].voices[i].keyOn? offVoice:i); return offVoice; } //--------------------------------------------------------- // pitchOn2Voice // return the number of the voice which has the input // pitch and is keyOn //--------------------------------------------------------- int DeicsOnze::pitchOn2Voice(int c, int pitch) { int pitchVoice=MAXNBRVOICES; for(int i=0; i<_global.channel[c].nbrVoices; i++) { if(_global.channel[c].voices[i].pitch== pitch && _global.channel[c].voices[i].keyOn && !_global.channel[c].voices[i].isSustained) { pitchVoice = i; return pitchVoice; } } return pitchVoice; } //--------------------------------------------------------- // getAttractor //--------------------------------------------------------- inline double getAttractor(int portamentoTime, double sr) { /* some explanations c(48000) = c > 1 f_sr(0) = 1000, f_sr(t) = 2000 f_sr*2(0) = 1000, f_sr*2(t*2) = 2000 f_sr(t) = exp(t*ln(c(sr))) * 1000 2000 = exp(t*ln(c(48000))) * 1000 2000 = exp(t*2*ln(c(48000*2))) * 1000 t*ln(c(48000)) = t*2*ln(c(48000*2)) c(48000*m) = exp(ln(c)/m) sr = 48000*m */ double c; c = 1.0 + COEFPORTA/(double)(portamentoTime*portamentoTime); return(exp(log(c)*48000.0/sr)); } //--------------------------------------------------------- // pitch2freq //--------------------------------------------------------- inline double pitch2freq(double p) { return(LOWERNOTEFREQ*exp(p*log(2.0)/12.0)); } //--------------------------------------------------------- // lfoUpdate // update the coefficient which multiplies the current inct // in order to // get the right current frequency with respect to the lfo // update the coefficient which multiplies the amplitude. //--------------------------------------------------------- inline void lfoUpdate(Preset* p, Channel* p_c, float* wt) { double delayCoef; //Manage LFO delay if(!p_c->delayPassed) { if(p_c->lfoIndex==0 || p_c->lfoIndex==p_c->lfoMaxIndex/2) { if(p_c->lfoDelayIndex<(double)(RESOLUTION/4)) { delayCoef=(double)wt[(int)p_c->lfoDelayIndex]; p_c->lfoMaxCoefInct=exp((log(2.0)/12.0)*p_c->lfoPitch*delayCoef); p_c->lfoCoefInctInct= exp((log(2.0)/12.0)*((2*p_c->lfoPitch*delayCoef)/p_c->lfoMaxIndex)); p_c->lfoDelayIndex+=p_c->lfoDelayInct; p_c->lfoMaxDAmp=delayCoef*p_c->lfoMaxAmp; } else { p_c->lfoMaxCoefInct=exp((log(2.0)/12.0)*p_c->lfoPitch); p_c->lfoCoefInctInct= exp((log(2.0)/12.0)*((2*p_c->lfoPitch)/p_c->lfoMaxIndex)); p_c->delayPassed=true; p_c->lfoMaxDAmp=p_c->lfoMaxAmp; } } } switch(p->lfo.wave) { case SAWUP : if(p_c->lfoIndex==0) { p_c->lfoCoefInct=1.0/(p_c->lfoMaxCoefInct); p_c->lfoCoefAmp=p_c->lfoMaxDAmp/(double)p_c->lfoMaxIndex; p_c->lfoAmp=1.0; } else { p_c->lfoCoefInct*=p_c->lfoCoefInctInct; p_c->lfoAmp-=p_c->lfoCoefAmp; } break; case SQUARE : if(p_c->lfoIndex==0) { p_c->lfoCoefInct=p_c->lfoMaxCoefInct; p_c->lfoAmp=1.0; } if(p_c->lfoIndex==(p_c->lfoMaxIndex/2)) { p_c->lfoCoefInct=1.0/p_c->lfoMaxCoefInct; p_c->lfoAmp=1.0-p_c->lfoMaxDAmp; } break; case TRIANGL : if(p_c->lfoIndex==0) { p_c->lfoCoefInct=1.0; p_c->lfoCoefAmp=p_c->lfoMaxDAmp /(double)(p_c->lfoMaxIndex/2); p_c->lfoAmp=1.0-p_c->lfoMaxDAmp/2.0; } else if(p_c->lfoIndex<(p_c->lfoMaxIndex/4)) { p_c->lfoCoefInct*=p_c->lfoCoefInctInct; p_c->lfoAmp-=p_c->lfoCoefAmp; } else if(p_c->lfoIndex<((3*p_c->lfoMaxIndex)/4)) { p_c->lfoCoefInct/=p_c->lfoCoefInctInct; p_c->lfoAmp+=p_c->lfoCoefAmp; } else if(p_c->lfoIndexlfoMaxIndex) { p_c->lfoCoefInct*=p_c->lfoCoefInctInct; p_c->lfoAmp-=p_c->lfoCoefAmp; } break; case SHOLD : if(p_c->lfoIndex==0||p_c->lfoIndex==(p_c->lfoMaxIndex/2)) { double r;//uniform random between -1.0 and 1.0 r = (double)(2*rand()-RAND_MAX)/(double)RAND_MAX; p_c->lfoCoefInct=(r>=0.0?1.0+r*(p_c->lfoMaxCoefInct-1.0) :1.0/(1.0-r*(p_c->lfoMaxCoefInct-1.0))); p_c->lfoAmp=1.0-(r/2.0+0.5)*p_c->lfoMaxDAmp; } break; default : printf("Error : lfo wave does not exist\n"); break; } p_c->lfoIndex=(p_c->lfoIndexlfoMaxIndex?p_c->lfoIndex+1:0); } //--------------------------------------------------------- // portamento update //--------------------------------------------------------- inline void portamentoUpdate(Channel* p_c, Voice* p_v) { double inctTemp; bool allTargetReached; if(p_v->hasAttractor) { allTargetReached = true; for(int k = 0; kop[k].inct < p_v->op[k].targetInct) { inctTemp = p_v->op[k].inct * p_v->attractor; if(inctTemp < p_v->op[k].targetInct) { allTargetReached = false; p_v->op[k].inct = inctTemp; } else p_v->op[k].inct = p_v->op[k].targetInct; } else if(p_v->op[k].inct > p_v->op[k].targetInct) { inctTemp = p_v->op[k].inct / p_v->attractor; if(inctTemp > p_v->op[k].targetInct) { allTargetReached = false; p_v->op[k].inct = inctTemp; } else p_v->op[k].inct = p_v->op[k].targetInct; } p_c->lastInc[k] = p_v->op[k].inct; } if(allTargetReached) p_v->hasAttractor = false; } } //--------------------------------------------------------- // pitchEnvelopeUpdate //--------------------------------------------------------- inline void pitchEnvelopeUpdate(Voice* v, PitchEg* pe, double sr) { if(v->pitchEnvState != OFF_PE) { switch(v->pitchEnvState) { case PHASE1 : if( //change to phase2 (v->pitchEnvCoefInctInct == 1.0) || (v->pitchEnvCoefInctInct > 1.0 && v->pitchEnvCoefInct > v->pitchEnvCoefInctPhase2) || (v->pitchEnvCoefInctInct < 1.0 && v->pitchEnvCoefInct < v->pitchEnvCoefInctPhase2) ) { v->pitchEnvState = PHASE2; v->pitchEnvCoefInct = getPitchEnvCoefInct(pe->pl2); v->pitchEnvCoefInctInct = getPitchEnvCoefInctInct(pe->pl2, pe->pl3, pe->pr2, sr); } else v->pitchEnvCoefInct *= v->pitchEnvCoefInctInct; break; case PHASE2 : if( //change to off (temporarely) (v->pitchEnvCoefInctInct == 1.0) || (v->pitchEnvCoefInctInct > 1.0 && v->pitchEnvCoefInct > v->pitchEnvCoefInctPhase3) || (v->pitchEnvCoefInctInct < 1.0 && v->pitchEnvCoefInct < v->pitchEnvCoefInctPhase3) ) { v->pitchEnvState = OFF_PE; v->pitchEnvCoefInct = getPitchEnvCoefInct(pe->pl3); v->pitchEnvCoefInctInct = 1.0; } else v->pitchEnvCoefInct *= v->pitchEnvCoefInctInct; break; case RELEASE_PE : if( //change to release2 (v->pitchEnvCoefInctInct == 1.0) || (v->pitchEnvCoefInctInct > 1.0 && v->pitchEnvCoefInct > v->pitchEnvCoefInctPhase1) || (v->pitchEnvCoefInctInct < 1.0 && v->pitchEnvCoefInct < v->pitchEnvCoefInctPhase1) ) { v->pitchEnvState = OFF_PE; v->pitchEnvCoefInct = getPitchEnvCoefInct(pe->pl1); v->pitchEnvCoefInctInct = 1.0; } else v->pitchEnvCoefInct *= v->pitchEnvCoefInctInct; break; case OFF_PE : //do nothing, should not appear anyway break; default : printf("Error switch pitchEnvelopeUpdate, no such case\n"); break; } } } //--------------------------------------------------------- // outLevel2Amp, Amp for amplitude //between 0.0 and 2.0 or more // 100->2.0, 90->1.0, 80->0.5 ... //--------------------------------------------------------- inline double outLevel2Amp(int ol) { double a; double b; a = log(2)/10.0; b = -a*DB0LEVEL; return exp(a*(double)ol+b); } //--------------------------------------------------------- // lowlevel2amp, // 127->0dB->1.0, 0->-25dB->0 //--------------------------------------------------------- inline double lowlevel2amp(int l) { double a, b, c, db; if(l==0) return 0.0; else { a = DB_MIN/127.0; b = -DB_MIN; db = a*l+b; c = -log(2)/3; return exp(-c*db); } } //--------------------------------------------------------- // level2amp, // 255->0dB->1.0, 0->-25dB->0 //--------------------------------------------------------- inline double level2amp(int l) { double a, b, c, db; if(l==0) return 0.0; else { a = DB_MIN/255.0; b = -DB_MIN; db = a*l+b; c = -log(2.0)/3.0; return exp(-c*db); } } //--------------------------------------------------------- // amp2level // 1.0->0dB->255, 0->-25dB->0 //--------------------------------------------------------- inline int amp2level(double amp){ double a, b, c; a = 255.0/DB_MIN; b = 255.0; c = log(2.0)/3.0; return (int)(a*(log(amp)/c)+b); } //--------------------------------------------------------- // amp2lowlevel // 1.0->0dB->127, 0->-25dB->0 //--------------------------------------------------------- inline int amp2lowlevel(double amp){ double a, b, c; a = 127.0/DB_MIN; b = 127.0; c = log(2.0)/3.0; return (int)(a*(log(amp)/c)+b); } //--------------------------------------------------------- // velo2RAmp, AmpR between 0.0 and 1.0 // return an amplitude ratio with respect to _preset->sensitivity.keyVelocity //--------------------------------------------------------- inline double velo2AmpR(int velo, int kvs) { double lev; lev = exp(-log(2)*kvs); return (lev+(1.0-lev)*((double)velo/(double)MAXVELO)); } //--------------------------------------------------------- // envAR2s // return the time in second of the ATTACK duration //--------------------------------------------------------- inline double envAR2s(int ar) { //determined using the fitting feature of gnuplot return 10.4423*exp(-0.353767*ar); } //--------------------------------------------------------- // envD1R2coef // return the coefficient for the exponential decrease // with respect to d1r and sampleRate, sr //--------------------------------------------------------- inline double envD1R2coef(int d1r, double sr) { double dt;//such that amp(t+dt)=amp(t)/2 double alpha;//such that amp(t)=exp(alpha*t) if(d1r==0) return 1.0; else { //dt has been determined with the fitting function of gnuplot dt=9.80715*exp(-0.356053*(double)d1r); //amp(0)=1 //amp(t+dt)=amp(t)/2 //amp(t)=exp(alpha*t) //amp(t+mt) //following the above equational system we found : alpha=-log(2)/dt; return exp(alpha/sr); } } //--------------------------------------------------------- // coefRelease // convert the release value to a coef for coefVLevel //--------------------------------------------------------- inline double coefRelease(unsigned char release) { double x = COEFGRELEASE*(double)release/(double)MIDRELEASE+1.0-COEFGRELEASE; double square_x = x*x; return(1.0/(square_x*x)); } //--------------------------------------------------------- // envRR2coef // return the coefficient for the exponential decrease // with respect to rr and sampleRate, sr //--------------------------------------------------------- inline double envRR2coef(int rr, double sr, unsigned char release) { double dt;//such that amp(t+dt)=amp(t)/2 double alpha;//such that amp(t)=exp(alpha*t) //dt has been determined with the fitting function of gnuplot dt=7.06636*exp(-0.697606*(double)rr); dt*=coefRelease(release); //printf("demi life = %e\n", dt); //amp(0)=1 //amp(t+dt)=amp(t)/2 //amp(t)=exp(alpha*t) //amp(t+mt) //following the above equational system we found : alpha=-log(2)/dt; return exp(alpha/sr); } //--------------------------------------------------------- // coefAttack // convert the attack value to a coef for envInct //--------------------------------------------------------- inline double coefAttack(unsigned char attack) { double x = COEFGATTACK*(double)attack/(double)MIDATTACK + 1.0-COEFGATTACK; double square_x = x*x; return(square_x*square_x*x); } //--------------------------------------------------------- // env2RAmp // return the amplitude ratio with respect to an envelope and an // envelope state, making evoluate the envelope // sr is the sample rate and st the sine_table //--------------------------------------------------------- inline double env2AmpR(double sr, float* wt, Eg eg, OpVoice* p_opVoice) { switch(p_opVoice->envState) { case ATTACK: p_opVoice->envIndex+=p_opVoice->envInct; if (p_opVoice->envIndex<(RESOLUTION/4)) { p_opVoice->envLevel=wt[(int)p_opVoice->envIndex]; } else { p_opVoice->envState=DECAY; p_opVoice->envLevel=1.0; p_opVoice->coefVLevel=envD1R2coef(eg.d1r, sr); } return p_opVoice->envLevel; break; case DECAY: if (p_opVoice->envLevel>((double)eg.d1l/(double)MAXD1L)+COEFERRDECSUS) { p_opVoice->envLevel*=p_opVoice->coefVLevel; } else { p_opVoice->envState=SUSTAIN; p_opVoice->envLevel=((double)eg.d1l/(double)MAXD1L); p_opVoice->coefVLevel=envD1R2coef(eg.d2r, sr);//probably the same } return p_opVoice->envLevel; break; case SUSTAIN: if (p_opVoice->envLevel>COEFERRSUSREL) { p_opVoice->envLevel*=p_opVoice->coefVLevel; } else { p_opVoice->envState=OFF; p_opVoice->envLevel=0.0; } return p_opVoice->envLevel; break; case RELEASE: if (p_opVoice->envLevel > COEFERRSUSREL) { p_opVoice->envLevel*=p_opVoice->coefVLevel; } else { p_opVoice->envState=OFF; p_opVoice->envLevel=0.0; } return p_opVoice->envLevel; break; case OFF: return 0.0; break; default: printf("Error case envelopeState"); break; } return p_opVoice->envLevel; } //--------------------------------------------------------- // programSelect //--------------------------------------------------------- void DeicsOnze::programSelect(int c, int hbank, int lbank, int prog) { Preset* foundPreset; foundPreset=findPreset(hbank, lbank, prog); if (foundPreset) _preset[c]=foundPreset; else { _preset[c]=_initialPreset; _preset[c]->prog=prog; _preset[c]->_subcategory->_lbank=lbank; //TODO : real link _preset[c]->_subcategory->_category->_hbank=hbank; } setPreset(c); } //--------------------------------------------------------- // setModulation //--------------------------------------------------------- void DeicsOnze::setModulation(int c, int val) { _global.channel[c].modulation = (unsigned char) val; setLfo(c); } //--------------------------------------------------------- // setPitchBendCoef //--------------------------------------------------------- void DeicsOnze::setPitchBendCoef(int c, int val) { _global.channel[c].pitchBendCoef = exp(log(2)*((double)_preset[c]->function.pBendRange /(double)MAXPBENDRANGE) *((double)val/(double)MAXPITCHBENDVALUE)); } //--------------------------------------------------------- // setSustain //--------------------------------------------------------- void DeicsOnze::setSustain(int c, int val) { _global.channel[c].sustain=(val>64); if(!_global.channel[c].sustain) for(int i=0; i<_global.channel[c].nbrVoices; i++) if(_global.channel[c].voices[i].isSustained) { for(int j=0; jwriteEvent(evNbrVoices); }*/ //channelNum /* if(qdEl.tagName()==CHANNELNUMSTR) { _global.channelNum = (qdEl.text()==ALLSTR?-1:qdEl.text().toInt()-1); unsigned char *dataChannelNum = new unsigned char[2]; dataChannelNum[0]=SYSEX_CHANNELNUM; dataChannelNum[1]=(unsigned char)_global.channelNum; MusECore::MidiPlayEvent evChannelNum(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataChannelNum, 2); _gui->writeEvent(evChannelNum); }*/ //quality if(qdEl.tagName()==QUALITYSTR) { _global.quality = (qdEl.text()==HIGHSTR?high: (qdEl.text()==MIDDLESTR?middle: (qdEl.text()==LOWSTR?low:ultralow))); setQuality(_global.quality); unsigned char *dataQuality = new unsigned char[2]; dataQuality[0]=SYSEX_QUALITY; dataQuality[1]=(unsigned char)_global.quality; MusECore::MidiPlayEvent evQuality(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataQuality, 2); _gui->writeEvent(evQuality); } //filter if(qdEl.tagName()==FILTERSTR) { setFilter(qdEl.text()==YESSTRDEI?true:false); unsigned char *dataFilter = new unsigned char[2]; dataFilter[0]=SYSEX_FILTER; dataFilter[1]=(unsigned char)getFilter(); MusECore::MidiPlayEvent evFilter(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataFilter, 2); _gui->writeEvent(evFilter); } //font size if(qdEl.tagName()==FONTSIZESTR) { _global.fontSize = qdEl.text().toInt(); unsigned char *dataFontSize = new unsigned char[2]; dataFontSize[0]=SYSEX_FONTSIZE; dataFontSize[1]=(unsigned char)_global.fontSize; MusECore::MidiPlayEvent evFontSize(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataFontSize, 2); _gui->writeEvent(evFontSize); } //saveConfig if(qdEl.tagName()==SAVECONFIGSTR) { _saveConfig = (qdEl.text()==YESSTRDEI?true:false); unsigned char *dataSaveConfig = new unsigned char[2]; dataSaveConfig[0]=SYSEX_SAVECONFIG; dataSaveConfig[1]=(unsigned char)_saveConfig; MusECore::MidiPlayEvent evSaveConfig(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataSaveConfig, 2); _gui->writeEvent(evSaveConfig); } //saveOnlyUsed if(qdEl.tagName()==SAVEONLYUSEDSTR) { _saveOnlyUsed = (qdEl.text()==YESSTRDEI?true:false); unsigned char *dataSaveOnlyUsed = new unsigned char[2]; dataSaveOnlyUsed[0]=SYSEX_SAVEONLYUSED; dataSaveOnlyUsed[1]=(unsigned char)_saveOnlyUsed; MusECore::MidiPlayEvent evSaveOnlyUsed(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataSaveOnlyUsed, 2); _gui->writeEvent(evSaveOnlyUsed); } //colors if(qdEl.tagName()==TEXTCOLORSTR) textColor = readColor(qdn); if(qdEl.tagName()==BACKGROUNDCOLORSTR) backgroundColor = readColor(qdn); if(qdEl.tagName()==EDITTEXTCOLORSTR) editTextColor = readColor(qdn); if(qdEl.tagName()==EDITBACKGROUNDCOLORSTR) editBackgroundColor = readColor(qdn); //must insert load image, later //load init set if(qdEl.tagName()==ISINITSETSTR) { _isInitSet = (qdEl.text()==YESSTRDEI?true:false); unsigned char *dataIsInitSet = new unsigned char[2]; dataIsInitSet[0]=SYSEX_ISINITSET; dataIsInitSet[1]=(unsigned char)_isInitSet; MusECore::MidiPlayEvent evIsInitSet(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataIsInitSet, 2); _gui->writeEvent(evIsInitSet); } if(qdEl.tagName()==INITSETPATHSTR) { _initSetPath = qdEl.text(); unsigned char *dataInitSetPath = new unsigned char[1+MAXSTRLENGTHINITSETPATH]; dataInitSetPath[0]=SYSEX_INITSETPATH; strncpy((char*)&dataInitSetPath[1], _initSetPath.toLatin1().constData(), MAXSTRLENGTHINITSETPATH); MusECore::MidiPlayEvent evInitSetPath(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataInitSetPath, 1+MAXSTRLENGTHINITSETPATH); _gui->writeEvent(evInitSetPath); } //load background pix if(qdEl.tagName()==ISBACKGROUNDPIXSTR) { _isBackgroundPix = (qdEl.text()==YESSTRDEI?true:false); unsigned char *dataIsBackgroundPix = new unsigned char[2]; dataIsBackgroundPix[0]=SYSEX_ISBACKGROUNDPIX; dataIsBackgroundPix[1]=(unsigned char)_isBackgroundPix; MusECore::MidiPlayEvent evIsBackgroundPix(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataIsBackgroundPix, 2); _gui->writeEvent(evIsBackgroundPix); } if(qdEl.tagName()==BACKGROUNDPIXPATHSTR) { _backgroundPixPath = qdEl.text(); unsigned char *dataBackgroundPixPath = new unsigned char[1+MAXSTRLENGTHBACKGROUNDPIXPATH]; dataBackgroundPixPath[0]=SYSEX_BACKGROUNDPIXPATH; strncpy((char*)&dataBackgroundPixPath[1], _backgroundPixPath.toLatin1().constData(), MAXSTRLENGTHBACKGROUNDPIXPATH); MusECore::MidiPlayEvent evBackgroundPixPath(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataBackgroundPixPath, 1+MAXSTRLENGTHBACKGROUNDPIXPATH); _gui->writeEvent(evBackgroundPixPath); } qdn = qdn.nextSibling(); } //send colors unsigned char dataColorGui[COLORSYSEXLENGTH+1]; dataColorGui[0]=SYSEX_COLORGUI; dataColorGui[1]=(unsigned char)textColor.red(); dataColorGui[2]=(unsigned char)textColor.green(); dataColorGui[3]=(unsigned char)textColor.blue(); dataColorGui[4]=(unsigned char)backgroundColor.red(); dataColorGui[5]=(unsigned char)backgroundColor.green(); dataColorGui[6]=(unsigned char)backgroundColor.blue(); dataColorGui[7]=(unsigned char)editTextColor.red(); dataColorGui[8]=(unsigned char)editTextColor.green(); dataColorGui[9]=(unsigned char)editTextColor.blue(); dataColorGui[10]=(unsigned char)editBackgroundColor.red(); dataColorGui[11]=(unsigned char)editBackgroundColor.green(); dataColorGui[12]=(unsigned char)editBackgroundColor.blue(); MusECore::MidiPlayEvent evSysexColor(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataColorGui, COLORSYSEXLENGTH+1); _gui->writeEvent(evSysexColor); } //----------------------------------------------------------- // loadConfiguration //----------------------------------------------------------- void DeicsOnze::loadConfiguration(QString fileName) { // read the XML file and create DOM tree if(!fileName.isEmpty()) { QFile confFile(fileName); if(!confFile.open(QIODevice::ReadOnly)) { printf("Critical Error. Cannot open file %s\n", fileName.toLatin1().data()); return; } QDomDocument domTree; if (!domTree.setContent(&confFile )) { printf("Critical Error. Parsing error for file %s\n", fileName.toLatin1().data()); confFile.close(); return; } confFile.close(); QDomNode node = domTree.documentElement(); while (!node.isNull()) { QDomElement e = node.toElement(); if (e.isNull()) continue; if (e.tagName() == DEICSONZECONFIGURATIONSTR) { QString version = e.attribute(QString("version")); if (version == "1.0") { readConfiguration(node.firstChild()); } else printf("unsupported *.dco file version %s\n", version.toLatin1().constData()); } else printf("DeicsOnze: %s not supported\n", e.tagName().toLatin1().constData()); node = node.nextSibling(); } } } //--------------------------------------------------------- // writeConfiguration //--------------------------------------------------------- void DeicsOnze::writeConfiguration(AL::Xml* xml) { QString str; xml->stag("deicsOnzeConfiguation version=\"1.0\""); //xml->intTag(NBRVOICESSTR, (int)_global.nbrVoices); //xml->strTag(CHANNELNUMSTR, (_global.channelNum==-1?ALLSTR: // str.setNum(_global.channelNum+1))); xml->tag(QUALITYSTR, QString((_global.quality==high?HIGHSTR: (_global.quality==middle?MIDDLESTR: (_global.quality==low?LOWSTR:ULTRALOWSTR))))); xml->tag(FILTERSTR, QString(getFilter()==true?YESSTRDEI:NOSTRDEI)); xml->tag(FONTSIZESTR, _global.fontSize); xml->tag(SAVECONFIGSTR, QString((_saveConfig?YESSTRDEI:NOSTRDEI))); xml->tag(SAVEONLYUSEDSTR, QString((_saveOnlyUsed?YESSTRDEI:NOSTRDEI))); xml->tag(TEXTCOLORSTR, reinterpret_cast(*_gui->tColor)); xml->tag(BACKGROUNDCOLORSTR, reinterpret_cast(*_gui->bColor)); xml->tag(EDITTEXTCOLORSTR, reinterpret_cast(*_gui->etColor)); xml->tag(EDITBACKGROUNDCOLORSTR, reinterpret_cast(*_gui->ebColor)); xml->tag(ISINITSETSTR, QString((_isInitSet?YESSTRDEI:NOSTRDEI))); xml->tag(INITSETPATHSTR, QString(_initSetPath)); xml->tag(ISBACKGROUNDPIXSTR, QString((_isBackgroundPix?YESSTRDEI:NOSTRDEI))); xml->tag(BACKGROUNDPIXPATHSTR, _backgroundPixPath); xml->etag(DEICSONZECONFIGURATIONSTR); } //--------------------------------------------------------- // getInitBuffer //--------------------------------------------------------- void DeicsOnze::setupInitBuffer(int len) { if (len > initLen) { if (initBuffer) delete [] initBuffer; initBuffer = new unsigned char[len]; initLen = len; } } //--------------------------------------------------------- // getInitData //--------------------------------------------------------- void DeicsOnze::getInitData(int* length, const unsigned char** data) { //write the set in a temporary file and in a QByteArray QTemporaryFile file; file.open(); AL::Xml* xml=new AL::Xml(&file); xml->header(); _set->writeSet(xml, _saveOnlyUsed); file.reset(); //seek the start of the file QByteArray ba = file.readAll(); file.close(); //compress the QByteArray at default rate QByteArray baComp = qCompress(ba); int version = SYSEX_INIT_DATA_VERSION; //SYSEX_INIT_DATA_VERSION_2; // Not quite ready for ver 2 yet. Need delay wet/dry gui control. //save the set //*length = (version == SYSEX_INIT_DATA_VERSION_2 ? NUM_CONFIGLENGTH : NUM_DELAY_WET_DRY_MIX) // HACK *length = NUM_CONFIGLENGTH + (_pluginIReverb?sizeof(float)*_pluginIReverb->plugin()->parameter():0) + (_pluginIChorus?sizeof(float)*_pluginIChorus->plugin()->parameter():0) + baComp.size(); setupInitBuffer(*length); //save init data initBuffer[0]=MUSE_SYNTH_SYSEX_MFG_ID; initBuffer[1]=DEICSONZE_UNIQUE_ID; initBuffer[2]=SYSEX_INIT_DATA; initBuffer[3]=version; //save global data initBuffer[NUM_MASTERVOL] = (unsigned char) getMasterVol(); for(int c = 0; c < NBRCHANNELS; c++) { initBuffer[NUM_CHANNEL_ENABLE + c] = (unsigned char) getChannelEnable(c); initBuffer[NUM_CHANNEL_VOL + c] = (unsigned char) getChannelVol(c); initBuffer[NUM_CHANNEL_PAN + c] = (unsigned char) getChannelPan(c); int b = getChannelBrightness(c); initBuffer[NUM_CHANNEL_BRIGHTNESS + 2*c] = (unsigned char) (b%256); initBuffer[NUM_CHANNEL_BRIGHTNESS + 2*c + 1] = (unsigned char) (b/256); initBuffer[NUM_CHANNEL_MODULATION + c] = (unsigned char) getChannelModulation(c); initBuffer[NUM_CHANNEL_DETUNE + c] = (unsigned char) getChannelDetune(c) + MAXCHANNELDETUNE; initBuffer[NUM_CHANNEL_ATTACK + c] = (unsigned char) getChannelAttack(c); initBuffer[NUM_CHANNEL_RELEASE + c] = (unsigned char) getChannelRelease(c); initBuffer[NUM_CHANNEL_REVERB + c] = (unsigned char) getChannelReverb(c); initBuffer[NUM_CHANNEL_CHORUS + c] = (unsigned char) getChannelChorus(c); initBuffer[NUM_CHANNEL_DELAY + c] = (unsigned char) getChannelDelay(c); initBuffer[NUM_CURRENTPROG + c] = (unsigned char) _preset[c]->prog; initBuffer[NUM_CURRENTLBANK + c] = (unsigned char) _preset[c]->_subcategory->_lbank; initBuffer[NUM_CURRENTHBANK + c] = (unsigned char) _preset[c]->_subcategory->_category->_hbank; initBuffer[NUM_NBRVOICES + c] = (unsigned char) getNbrVoices(c); } initBuffer[NUM_SAVEONLYUSED]=(unsigned char) _saveOnlyUsed; initBuffer[NUM_SAVECONFIG]=(unsigned char) _saveConfig; //save config data if(_saveConfig) { initBuffer[NUM_QUALITY]=(unsigned char)_global.quality; initBuffer[NUM_FILTER]=(unsigned char)getFilter(); initBuffer[NUM_FONTSIZE]=(unsigned char)_global.fontSize; initBuffer[NUM_RED_TEXT]=(unsigned char)_gui->tColor->red(); initBuffer[NUM_GREEN_TEXT]=(unsigned char)_gui->tColor->green(); initBuffer[NUM_BLUE_TEXT]=(unsigned char)_gui->tColor->blue(); initBuffer[NUM_RED_BACKGROUND]=(unsigned char)_gui->bColor->red(); initBuffer[NUM_GREEN_BACKGROUND]=(unsigned char)_gui->bColor->green(); initBuffer[NUM_BLUE_BACKGROUND]=(unsigned char)_gui->bColor->blue(); initBuffer[NUM_RED_EDITTEXT]=(unsigned char)_gui->etColor->red(); initBuffer[NUM_GREEN_EDITTEXT]=(unsigned char)_gui->etColor->green(); initBuffer[NUM_BLUE_EDITTEXT]=(unsigned char)_gui->etColor->blue(); initBuffer[NUM_RED_EDITBACKGROUND]=(unsigned char)_gui->ebColor->red(); initBuffer[NUM_GREEN_EDITBACKGROUND]=(unsigned char)_gui->ebColor->green(); initBuffer[NUM_BLUE_EDITBACKGROUND]=(unsigned char)_gui->ebColor->blue(); initBuffer[NUM_ISINITSET]=(unsigned char)_isInitSet; strncpy((char*)&initBuffer[NUM_INITSETPATH], _initSetPath.toLatin1().constData(), MAXSTRLENGTHINITSETPATH); initBuffer[NUM_ISBACKGROUNDPIX]=(unsigned char)_isBackgroundPix; strncpy((char*)&initBuffer[NUM_BACKGROUNDPIXPATH], _backgroundPixPath.toLatin1().constData(), MAXSTRLENGTHBACKGROUNDPIXPATH); } //FX //reverb initBuffer[NUM_IS_REVERB_ON]=(unsigned char)_global.isReverbActivated; initBuffer[NUM_REVERB_RETURN]=(unsigned char)getReverbReturn(); initBuffer[NUM_REVERB_PARAM_NBR]= (_pluginIReverb?(unsigned char)_pluginIReverb->plugin()->parameter() : 0); strncpy((char*)&initBuffer[NUM_REVERB_LIB], (_pluginIReverb? _pluginIReverb->plugin()->lib().toLatin1().constData() : "\0"), MAXSTRLENGTHFXLIB); strncpy((char*)&initBuffer[NUM_REVERB_LABEL], (_pluginIReverb? _pluginIReverb->plugin()->label().toLatin1().constData() : "\0"), MAXSTRLENGTHFXLABEL); //chorus initBuffer[NUM_IS_CHORUS_ON]=(unsigned char)_global.isChorusActivated; initBuffer[NUM_CHORUS_RETURN]=(unsigned char)getChorusReturn(); initBuffer[NUM_CHORUS_PARAM_NBR]= (_pluginIChorus?(unsigned char)_pluginIChorus->plugin()->parameter() : 0); strncpy((char*)&initBuffer[NUM_CHORUS_LIB], (_pluginIChorus? _pluginIChorus->plugin()->lib().toLatin1().constData() : "\0"), MAXSTRLENGTHFXLIB); strncpy((char*)&initBuffer[NUM_CHORUS_LABEL], (_pluginIChorus? _pluginIChorus->plugin()->label().toLatin1().constData() : "\0"), MAXSTRLENGTHFXLABEL); //delay initBuffer[NUM_IS_DELAY_ON]=(unsigned char)_global.isDelayActivated; initBuffer[NUM_DELAY_RETURN]=(unsigned char)getDelayReturn(); //save FX parameters //reverb for(int i = 0; i < (int)initBuffer[NUM_REVERB_PARAM_NBR]; i++) { float val = getReverbParam(i); //memcpy(&initBuffer[(version == SYSEX_INIT_DATA_VERSION_2 ? NUM_CONFIGLENGTH : NUM_DELAY_WET_DRY_MIX) // HACK memcpy(&initBuffer[NUM_CONFIGLENGTH + sizeof(float)*i], &val, sizeof(float)); } //chorus for(int i = 0; i < (int)initBuffer[NUM_CHORUS_PARAM_NBR]; i++) { float val = getChorusParam(i); //memcpy(&initBuffer[(version == SYSEX_INIT_DATA_VERSION_2 ? NUM_CONFIGLENGTH : NUM_DELAY_WET_DRY_MIX) // HACK memcpy(&initBuffer[NUM_CONFIGLENGTH + sizeof(float)*(int)initBuffer[NUM_REVERB_PARAM_NBR] + sizeof(float)*i], &val, sizeof(float)); } //delay float delayfloat; delayfloat = getDelayBPM(); memcpy(&initBuffer[NUM_DELAY_BPM], &delayfloat, 4); delayfloat = getDelayBeatRatio(); memcpy(&initBuffer[NUM_DELAY_BEATRATIO], &delayfloat, sizeof(float)); delayfloat = getDelayFeedback(); memcpy(&initBuffer[NUM_DELAY_FEEDBACK], &delayfloat, sizeof(float)); delayfloat = getDelayLFOFreq(); memcpy(&initBuffer[NUM_DELAY_LFO_FREQ], &delayfloat, sizeof(float)); delayfloat = getDelayLFODepth(); memcpy(&initBuffer[NUM_DELAY_LFO_DEPTH], &delayfloat, sizeof(float)); //if(version == SYSEX_INIT_DATA_VERSION_2) //{ // delayfloat = getDelayDryWet(); // memcpy(&initBuffer[NUM_DELAY_WET_DRY_MIX], &delayfloat, sizeof(float)); //} //save set data int offset = //(version == SYSEX_INIT_DATA_VERSION_2 ? NUM_CONFIGLENGTH : NUM_DELAY_WET_DRY_MIX) // HACK NUM_CONFIGLENGTH + sizeof(float)*(int)initBuffer[NUM_REVERB_PARAM_NBR] + sizeof(float)*(int)initBuffer[NUM_CHORUS_PARAM_NBR]; for(int i = offset; i < *length; i++) initBuffer[i]=(unsigned char)baComp.at(i - offset); *data=initBuffer; } //--------------------------------------------------------- // parseInitData //--------------------------------------------------------- void DeicsOnze::parseInitData(int length, const unsigned char* data) { //if(data[3]!=SYSEX_INIT_DATA_VERSION || data[3]!=SYSEX_INIT_DATA_VERSION_2) if(data[3]!=SYSEX_INIT_DATA_VERSION) { printf("Error: DeicsOnze::parseInitData: unknown sysex init data version:%d\n", data[3]); return; } //load global parameters //master volume setMasterVol(data[NUM_MASTERVOL]); unsigned char *dataMasterVol = new unsigned char[2]; dataMasterVol[0]=SYSEX_MASTERVOL; dataMasterVol[1]=(unsigned char) getMasterVol(); MusECore::MidiPlayEvent evMasterVol(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataMasterVol, 2); _gui->writeEvent(evMasterVol); //channel configuration for(int c = 0; c < NBRCHANNELS; c++) { //isEnable setChannelEnable(c, data[NUM_CHANNEL_ENABLE + c]); MusECore::MidiPlayEvent evChEnable(0, 0, c, MusECore::ME_CONTROLLER, CTRL_CHANNELENABLE, data[NUM_CHANNEL_ENABLE + c]); _gui->writeEvent(evChEnable); //nbrVoices setNbrVoices(c, data[NUM_NBRVOICES + c]); MusECore::MidiPlayEvent evNbrVoices(0, 0, c,MusECore::ME_CONTROLLER,CTRL_NBRVOICES, data[NUM_NBRVOICES + c]); _gui->writeEvent(evNbrVoices); //channel volume setChannelVol(c, data[NUM_CHANNEL_VOL + c]); MusECore::MidiPlayEvent evChVol(0, 0, c, MusECore::ME_CONTROLLER, CTRL_CHANNELVOLUME, data[NUM_CHANNEL_VOL + c]); _gui->writeEvent(evChVol); //channel pan setChannelPan(c, data[NUM_CHANNEL_PAN + c]); MusECore::MidiPlayEvent evChPan(0, 0, c, MusECore::ME_CONTROLLER, CTRL_CHANNELPAN, data[NUM_CHANNEL_PAN + c]); _gui->writeEvent(evChPan); if(getChannelEnable(c)) applyChannelAmp(c); //channel detune setChannelDetune(c, data[NUM_CHANNEL_DETUNE + c]-MAXCHANNELDETUNE); MusECore::MidiPlayEvent evChDetune(0, 0, c, MusECore::ME_CONTROLLER, CTRL_CHANNELDETUNE, data[NUM_CHANNEL_DETUNE + c]-MAXCHANNELDETUNE); _gui->writeEvent(evChDetune); //channel brightness setChannelBrightness(c, data[NUM_CHANNEL_BRIGHTNESS + 2*c] + data[NUM_CHANNEL_BRIGHTNESS + 2*c + 1] * 256); MusECore::MidiPlayEvent evChBrightness(0, 0, c, MusECore::ME_CONTROLLER, CTRL_FINEBRIGHTNESS, getChannelBrightness(c)); _gui->writeEvent(evChBrightness); //channel modulation setChannelModulation(c, data[NUM_CHANNEL_MODULATION + c]); MusECore::MidiPlayEvent evChMod(0, 0, c, MusECore::ME_CONTROLLER, MusECore::CTRL_MODULATION, data[NUM_CHANNEL_MODULATION + c]); _gui->writeEvent(evChMod); //channel attack setChannelAttack(c, data[NUM_CHANNEL_ATTACK + c]); MusECore::MidiPlayEvent evChAttack(0, 0, c, MusECore::ME_CONTROLLER, MusECore::CTRL_ATTACK_TIME, data[NUM_CHANNEL_ATTACK + c]); _gui->writeEvent(evChAttack); //channel release setChannelRelease(c, data[NUM_CHANNEL_RELEASE + c]); MusECore::MidiPlayEvent evChRelease(0, 0, c, MusECore::ME_CONTROLLER, MusECore::CTRL_RELEASE_TIME, data[NUM_CHANNEL_RELEASE + c]); _gui->writeEvent(evChRelease); //channel reverb setChannelReverb(c, data[NUM_CHANNEL_REVERB + c]); MusECore::MidiPlayEvent evChReverb(0, 0, c, MusECore::ME_CONTROLLER, MusECore::CTRL_REVERB_SEND, data[NUM_CHANNEL_REVERB + c]); _gui->writeEvent(evChReverb); //channel chorus setChannelChorus(c, data[NUM_CHANNEL_CHORUS + c]); MusECore::MidiPlayEvent evChChorus(0, 0, c, MusECore::ME_CONTROLLER, MusECore::CTRL_CHORUS_SEND, data[NUM_CHANNEL_CHORUS + c]); _gui->writeEvent(evChChorus); //channel delay setChannelDelay(c, data[NUM_CHANNEL_DELAY + c]); MusECore::MidiPlayEvent evChDelay(0, 0, c, MusECore::ME_CONTROLLER, MusECore::CTRL_VARIATION_SEND, data[NUM_CHANNEL_DELAY + c]); _gui->writeEvent(evChDelay); } //load configuration _saveConfig = (bool)data[NUM_SAVECONFIG]; unsigned char *dataSaveConfig = new unsigned char[2]; dataSaveConfig[0]=SYSEX_SAVECONFIG; dataSaveConfig[1]=(unsigned char)_saveConfig; MusECore::MidiPlayEvent evSaveConfig(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataSaveConfig, 2); _gui->writeEvent(evSaveConfig); if(_saveConfig) { //saveOnlyUsed _saveOnlyUsed = (bool)data[NUM_SAVEONLYUSED]; unsigned char *dataSaveOnlyUsed = new unsigned char[2]; dataSaveOnlyUsed[0]=SYSEX_SAVEONLYUSED; dataSaveOnlyUsed[1]=(unsigned char)_saveOnlyUsed; MusECore::MidiPlayEvent evSaveOnlyUsed(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataSaveOnlyUsed, 2); _gui->writeEvent(evSaveOnlyUsed); //colors unsigned char dataColorGui[COLORSYSEXLENGTH+1]; dataColorGui[0]=SYSEX_COLORGUI; for (int i=0; iwriteEvent(evSysexColor); //quality unsigned char dataQuality[2]; dataQuality[0]=SYSEX_QUALITY; dataQuality[1]=data[NUM_QUALITY]; setQuality((Quality)data[NUM_QUALITY]); MusECore::MidiPlayEvent evQuality(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataQuality, 2); _gui->writeEvent(evQuality); //filter unsigned char dataFilter[2]; dataFilter[0]=SYSEX_FILTER; dataFilter[1]=data[NUM_FILTER]; setFilter((bool)data[NUM_FILTER]); MusECore::MidiPlayEvent evFilter(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataFilter, 2); _gui->writeEvent(evFilter); //font size unsigned char dataFontSize[2]; dataFontSize[0]=SYSEX_FONTSIZE; dataFontSize[1]=data[NUM_FONTSIZE]; MusECore::MidiPlayEvent evFontSize(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataFontSize, 2); _gui->writeEvent(evFontSize); //load init set unsigned char dataIsInitSet[2]; dataIsInitSet[0]=SYSEX_ISINITSET; dataIsInitSet[1]=data[NUM_ISINITSET]; MusECore::MidiPlayEvent evIsInitSet(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataIsInitSet, 2); _gui->writeEvent(evIsInitSet); unsigned char dataInitSetPath[1+MAXSTRLENGTHINITSETPATH]; dataInitSetPath[0]=SYSEX_INITSETPATH; for(int a = 0; a < MAXSTRLENGTHINITSETPATH; a++) dataInitSetPath[a+1] = data[a+NUM_INITSETPATH]; MusECore::MidiPlayEvent evInitSetPath(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataInitSetPath, 1+MAXSTRLENGTHINITSETPATH); _gui->writeEvent(evInitSetPath); //load background pix unsigned char dataIsBackgroundPix[2]; dataIsBackgroundPix[0]=SYSEX_ISBACKGROUNDPIX; dataIsBackgroundPix[1]=data[NUM_ISBACKGROUNDPIX]; MusECore::MidiPlayEvent evIsBackgroundPix(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataIsBackgroundPix, 2); _gui->writeEvent(evIsBackgroundPix); unsigned char dataBackgroundPixPath[1+MAXSTRLENGTHBACKGROUNDPIXPATH]; dataBackgroundPixPath[0]=SYSEX_BACKGROUNDPIXPATH; for(int a = 0; a < MAXSTRLENGTHBACKGROUNDPIXPATH; a++) dataBackgroundPixPath[a+1] = data[a+NUM_BACKGROUNDPIXPATH]; MusECore::MidiPlayEvent evBackgroundPixPath(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataBackgroundPixPath, 1+MAXSTRLENGTHBACKGROUNDPIXPATH); _gui->writeEvent(evBackgroundPixPath); } else _gui->saveConfigCheckBox->setChecked(false); //load FX //reverb _global.isReverbActivated = (bool)data[NUM_IS_REVERB_ON]; unsigned char *dataReverbAct = new unsigned char[2]; dataReverbAct[0]=SYSEX_REVERBACTIV; dataReverbAct[1]=(unsigned char)_global.isReverbActivated; MusECore::MidiPlayEvent evReverbAct(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataReverbAct, 2); _gui->writeEvent(evReverbAct); setReverbReturn((int)data[NUM_REVERB_RETURN]); unsigned char *dataReverbRet = new unsigned char[2]; dataReverbRet[0]=SYSEX_REVERBRETURN; dataReverbRet[1]=(unsigned char)getReverbReturn(); MusECore::MidiPlayEvent evReverbRet(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataReverbRet, 2); _gui->writeEvent(evReverbRet); MusESimplePlugin::Plugin* p; p = MusESimplePlugin::plugins.find((const char*)&data[NUM_REVERB_LIB], (const char*)&data[NUM_REVERB_LABEL]); if(p) { initPluginReverb(p); for(int i = 0; i < (int)_pluginIReverb->plugin()->parameter(); i++) { float val; //memcpy(&val, &data[(data[3] == SYSEX_INIT_DATA_VERSION_2 ? NUM_CONFIGLENGTH : NUM_DELAY_WET_DRY_MIX) // HACK memcpy(&val, &data[NUM_CONFIGLENGTH + sizeof(float)*i], sizeof(float)); _pluginIReverb->setParam(i, val); } char dataBuildRev; dataBuildRev = SYSEX_BUILDGUIREVERB; MusECore::MidiPlayEvent evSysexBuildRev(0, 0, MusECore::ME_SYSEX, (const unsigned char*)&dataBuildRev, 1); _gui->writeEvent(evSysexBuildRev); } else _pluginIReverb = NULL; //chorus _global.isChorusActivated = (bool)data[NUM_IS_CHORUS_ON]; unsigned char *dataChorusAct = new unsigned char[2]; dataChorusAct[0]=SYSEX_CHORUSACTIV; dataChorusAct[1]=(unsigned char)_global.isChorusActivated; MusECore::MidiPlayEvent evChorusAct(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataChorusAct, 2); _gui->writeEvent(evChorusAct); setChorusReturn((int)data[NUM_CHORUS_RETURN]); unsigned char *dataChorusRet = new unsigned char[2]; dataChorusRet[0]=SYSEX_CHORUSRETURN; dataChorusRet[1]=(unsigned char)getChorusReturn(); MusECore::MidiPlayEvent evChorusRet(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataChorusRet, 2); _gui->writeEvent(evChorusRet); p = MusESimplePlugin::plugins.find((const char*)&data[NUM_CHORUS_LIB], (const char*)&data[NUM_CHORUS_LABEL]); if(p) { initPluginChorus(p); for(int i = 0; i < (int)_pluginIChorus->plugin()->parameter(); i++) { float val; //memcpy(&val, &data[(data[3] == SYSEX_INIT_DATA_VERSION_2 ? NUM_CONFIGLENGTH : NUM_DELAY_WET_DRY_MIX) // HACK memcpy(&val, &data[NUM_CONFIGLENGTH + sizeof(float)*(int)data[NUM_REVERB_PARAM_NBR] + sizeof(float)*i], sizeof(float)); _pluginIChorus->setParam(i, val); } char dataBuildCho; dataBuildCho = SYSEX_BUILDGUICHORUS; MusECore::MidiPlayEvent evSysexBuildCho(0, 0, MusECore::ME_SYSEX, (const unsigned char*)&dataBuildCho, 1); _gui->writeEvent(evSysexBuildCho); } else _pluginIChorus = NULL; //delay _global.isDelayActivated = (bool)data[NUM_IS_DELAY_ON]; unsigned char *dataDelayAct = new unsigned char[2]; dataDelayAct[0]=SYSEX_DELAYACTIV; dataDelayAct[1]=(unsigned char)_global.isDelayActivated; MusECore::MidiPlayEvent evDelayAct(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataDelayAct, 2); _gui->writeEvent(evDelayAct); setDelayReturn((int)data[NUM_DELAY_RETURN]); unsigned char *dataDelayRet = new unsigned char[2]; dataDelayRet[0]=SYSEX_DELAYRETURN; dataDelayRet[1]=(unsigned char)getDelayReturn(); MusECore::MidiPlayEvent evDelayRet(0, 0, MusECore::ME_SYSEX,(const unsigned char*)dataDelayRet, 2); _gui->writeEvent(evDelayRet); //initPluginDelay(MusEGlobal::plugins.find("pandelay", "pandelay")); float delayfloat; memcpy(&delayfloat, &data[NUM_DELAY_BPM], sizeof(float)); //setDelayBPM(delayfloat); if(_pluginIDelay) _pluginIDelay->setParam(0, delayfloat); char dataDelayBPM[sizeof(float)+1]; dataDelayBPM[0] = SYSEX_DELAYBPM; memcpy(&dataDelayBPM[1], &delayfloat, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayBPM(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataDelayBPM, sizeof(float)+1); _gui->writeEvent(evSysexDelayBPM); memcpy(&delayfloat, &data[NUM_DELAY_BEATRATIO], sizeof(float)); //setDelayBeatRatio(delayfloat); if(_pluginIDelay) _pluginIDelay->setParam(1, delayfloat); char dataDelayBeatRatio[sizeof(float)+1]; dataDelayBeatRatio[0] = SYSEX_DELAYBEATRATIO; memcpy(&dataDelayBeatRatio[1], &delayfloat, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayBeatRatio(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataDelayBeatRatio, sizeof(float)+1); _gui->writeEvent(evSysexDelayBeatRatio); memcpy(&delayfloat, &data[NUM_DELAY_FEEDBACK], sizeof(float)); //setDelayFeedback(delayfloat); if(_pluginIDelay) _pluginIDelay->setParam(2, delayfloat); char dataDelayFeedback[sizeof(float)+1]; dataDelayFeedback[0] = SYSEX_DELAYFEEDBACK; memcpy(&dataDelayFeedback[1], &delayfloat, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayFeedback(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataDelayFeedback, sizeof(float)+1); _gui->writeEvent(evSysexDelayFeedback); memcpy(&delayfloat, &data[NUM_DELAY_LFO_FREQ], sizeof(float)); //setDelayLFOFreq(delayfloat); if(_pluginIDelay) _pluginIDelay->setParam(3, delayfloat); char dataDelayLFOFreq[sizeof(float)+1]; dataDelayLFOFreq[0] = SYSEX_DELAYLFOFREQ; memcpy(&dataDelayLFOFreq[1], &delayfloat, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayLFOFreq(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataDelayLFOFreq, sizeof(float)+1); _gui->writeEvent(evSysexDelayLFOFreq); memcpy(&delayfloat, &data[NUM_DELAY_LFO_DEPTH], sizeof(float)); //setDelayLFODepth(delayfloat); if(_pluginIDelay) _pluginIDelay->setParam(4, delayfloat); char dataDelayLFODepth[sizeof(float)+1]; dataDelayLFODepth[0] = SYSEX_DELAYLFODEPTH; memcpy(&dataDelayLFODepth[1], &delayfloat, sizeof(float)); MusECore::MidiPlayEvent evSysexDelayLFODepth(0, 0, MusECore::ME_SYSEX, (const unsigned char*)dataDelayLFODepth, sizeof(float)+1); _gui->writeEvent(evSysexDelayLFODepth); // if(data[3] == SYSEX_INIT_DATA_VERSION_2) // { // memcpy(&delayfloat, &data[NUM_DELAY_WET_DRY_MIX], sizeof(float)); // //setDelayLFODepth(delayfloat); // if(_pluginIDelay) _pluginIDelay->putParam(5, delayfloat); // char dataDelayWetDryMix[sizeof(float)+1]; // dataDelayWetDryMix[0] = SYSEX_DELAYWETDRYMIX; // memcpy(&dataDelayWetDryMix[1], &delayfloat, sizeof(float)); // MusECore::MidiPlayEvent evSysexDelayWetDryMix(0, 0, MusECore::ME_SYSEX, // (const unsigned char*)dataDelayWetDryMix, // sizeof(float)+1); // _gui->writeEvent(evSysexDelayWetDryMix); // } //load the set compressed int offset = //(data[3] == SYSEX_INIT_DATA_VERSION_2 ? NUM_CONFIGLENGTH : NUM_DELAY_WET_DRY_MIX) // HACK NUM_CONFIGLENGTH + sizeof(float)*(int)data[NUM_REVERB_PARAM_NBR] + sizeof(float)*(int)data[NUM_CHORUS_PARAM_NBR]; QByteArray baComp = QByteArray((const char*)&data[offset], length-offset); //uncompress the set QByteArray baUncomp = qUncompress(baComp); //save the set in a temporary file and // read the XML file and create DOM tree QTemporaryFile file; file.open(); file.write(baUncomp); QDomDocument domTree; file.reset(); //seek the start of the file domTree.setContent(&file); file.close(); QDomNode node = domTree.documentElement(); while (!node.isNull()) { QDomElement e = node.toElement(); if (e.isNull()) continue; if (e.tagName() == "deicsOnzeSet") { QString version = e.attribute(QString("version")); if (version == "1.0") { for(int c = 0; c < NBRCHANNELS; c++) _preset[c]=_initialPreset; //read the set if((bool)data[NUM_SAVEONLYUSED]) { //printf("Mini\n"); //updateSaveOnlyUsed(true); } else { //printf("Huge\n"); while(!_set->_categoryVector.empty()) delete(*_set->_categoryVector.begin()); //updateSaveOnlyUsed(false); } _set->readSet(node.firstChild()); //display load preset //setSet(); } else printf("Wrong set version : %s\n", version.toLatin1().constData()); } node = node.nextSibling(); } //send sysex to the gui to load the set (actually not because it doesn't //work -the code is just zapped in the middle???-, so it is done above //int dL=2+baUncomp.size(); int dL = 2; char dataSend[dL]; dataSend[0]=SYSEX_LOADSET; dataSend[1]=data[NUM_SAVEONLYUSED]; //for(int i=2; iwriteEvent(evSysex); //select programs per channel for(int c = 0; c < NBRCHANNELS; c++) { int hbank=(int)data[NUM_CURRENTHBANK+c]; int lbank=(int)data[NUM_CURRENTLBANK+c]; int prog=(int)data[NUM_CURRENTPROG+c]; programSelect(c, hbank, lbank, prog); int val=prog+(lbank<<8)+(hbank<<16); MusECore::MidiPlayEvent evProgSel(0, 0, c, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, val); _gui->writeEvent(evProgSel); } } //--------------------------------------------------------- // sysex //--------------------------------------------------------- bool DeicsOnze::sysex(int length, const unsigned char* data) { sysex(length, data, false); return false; } bool DeicsOnze::sysex(int length, const unsigned char* data, bool fromGui) { if(length < 3 || data[0] != MUSE_SYNTH_SYSEX_MFG_ID || data[1] != DEICSONZE_UNIQUE_ID) { #ifdef DEICSONZE_DEBUG printf("MusE DeicsOnze: Unknown sysex header\n"); #endif return false; } int l = length - 2; const unsigned char* d = data + 2; int cmd=d[0]; int index; float f; switch(cmd) { case SYSEX_INIT_DATA: parseInitData(length, data); //parseInitData(l, d); break; case SYSEX_MASTERVOL: setMasterVol((int)d[1]); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; //case SYSEX_CHANNELNUM: //_global.channelNum = (char)d[1]; //if(!fromGui) { // MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); // _gui->writeEvent(evSysex); //} //break; case SYSEX_QUALITY: setQuality((Quality)d[1]); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_FILTER: setFilter((bool)d[1]); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_FONTSIZE: _global.fontSize = (int)d[1]; if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_SAVECONFIG: _saveConfig = (bool)d[1]; if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_SAVEONLYUSED: _saveOnlyUsed = (bool)d[1]; if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_ISINITSET: _isInitSet = (bool)d[1]; if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_INITSETPATH: _initSetPath = (char*)&d[1]; if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_ISBACKGROUNDPIX: _isBackgroundPix = (bool)d[1]; if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_BACKGROUNDPIXPATH: _backgroundPixPath = (char*)&d[1]; if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_PANIC: resetVoices(); break; case SYSEX_CHORUSACTIV: _global.isChorusActivated = (bool)d[1]; if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_CHORUSPARAM: index = (int)d[1]; memcpy(&f, &d[2], sizeof(float)); setChorusParam(index, f); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_REVERBACTIV: _global.isReverbActivated = (bool)d[1]; if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_REVERBPARAM: index = (int)d[1]; memcpy(&f, &d[2], sizeof(float)); setReverbParam(index, f); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_DELAYACTIV: _global.isDelayActivated = (bool)d[1]; if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_CHORUSRETURN: setChorusReturn((int)d[1]); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_REVERBRETURN: setReverbReturn((int)d[1]); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_DELAYRETURN: setDelayReturn((int)d[1]); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_SELECTREVERB: MusESimplePlugin::Plugin* pluginReverb; memcpy(&pluginReverb, &d[1], sizeof(MusESimplePlugin::Plugin*)); initPluginReverb(pluginReverb); break; case SYSEX_SELECTCHORUS: MusESimplePlugin::Plugin* pluginChorus; memcpy(&pluginChorus, &d[1], sizeof(MusESimplePlugin::Plugin*)); initPluginChorus(pluginChorus); break; case SYSEX_DELAYBPM: memcpy(&f, &d[1], sizeof(float)); setDelayBPM(f); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_DELAYBEATRATIO: memcpy(&f, &d[1], sizeof(float)); setDelayBeatRatio(f); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_DELAYFEEDBACK: memcpy(&f, &d[1], sizeof(float)); setDelayFeedback(f); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_DELAYLFOFREQ: memcpy(&f, &d[1], sizeof(float)); setDelayLFOFreq(f); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_DELAYLFODEPTH: memcpy(&f, &d[1], sizeof(float)); setDelayLFODepth(f); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; case SYSEX_DELAYWETDRYMIX: memcpy(&f, &d[1], sizeof(float)); setDelayDryWet(f); if(!fromGui) { MusECore::MidiPlayEvent evSysex(0, 0, MusECore::ME_SYSEX, d, l); _gui->writeEvent(evSysex); } break; default: break; } return false; } //--------------------------------------------------------- // setController //--------------------------------------------------------- bool DeicsOnze::setController(int channel, int id, int val) { setController(channel, id, val, false); return false; } bool DeicsOnze::setController(int ch, int ctrl, int val, bool fromGui) { int deiPan, k=0; if(_global.channel[ch].isEnable || ctrl==CTRL_CHANNELENABLE) { if(ctrl>=CTRL_AR && ctrlCTRL_PL3 && ctrlsetIsUsed(true); _preset[ch]->eg[k].ar=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_AR+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_D1R: _preset[ch]->setIsUsed(true); _preset[ch]->eg[k].d1r=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_D1R+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_D2R: _preset[ch]->setIsUsed(true); _preset[ch]->eg[k].d2r=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_D2R+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_RR: _preset[ch]->setIsUsed(true); _preset[ch]->eg[k].rr=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_RR+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_D1L: _preset[ch]->setIsUsed(true); _preset[ch]->eg[k].d1l=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_D1L+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_LS: _preset[ch]->setIsUsed(true); _preset[ch]->scaling.level[k]=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_LS+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_RS: _preset[ch]->setIsUsed(true); _preset[ch]->scaling.rate[k]=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_RS+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_EBS: _preset[ch]->setIsUsed(true); _preset[ch]->sensitivity.egBias[k]=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_EBS+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_AME: _preset[ch]->setIsUsed(true); _preset[ch]->sensitivity.ampOn[k]=val==1; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_AME+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_KVS: _preset[ch]->setIsUsed(true); _preset[ch]->sensitivity.keyVelocity[k]=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_KVS+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_OUT: _preset[ch]->setIsUsed(true); _preset[ch]->outLevel[k]=val; setOutLevel(k); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_OUT+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_RATIO: _preset[ch]->setIsUsed(true); _preset[ch]->frequency[k].ratio=((double)val)/100.0; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER, CTRL_RATIO+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_DET: _preset[ch]->setIsUsed(true); _preset[ch]->detune[k]=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_DET+k*DECAPAR1,val); _gui->writeEvent(ev); } break; case CTRL_ALG: _preset[ch]->setIsUsed(true); _preset[ch]->algorithm=(Algorithm)val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_ALG,val); _gui->writeEvent(ev); } break; case CTRL_FEEDBACK: _preset[ch]->setIsUsed(true); _preset[ch]->feedback=val; setFeedback(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_FEEDBACK,val); _gui->writeEvent(ev); } break; case CTRL_SPEED: _preset[ch]->setIsUsed(true); _preset[ch]->lfo.speed=val; setLfo(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_SPEED,val); _gui->writeEvent(ev); } break; case CTRL_DELAY: _preset[ch]->setIsUsed(true); _preset[ch]->lfo.delay=val; setLfo(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_DELAY,val); _gui->writeEvent(ev); } break; case CTRL_PMODDEPTH: _preset[ch]->setIsUsed(true); _preset[ch]->lfo.pModDepth=val; setLfo(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PMODDEPTH,val); _gui->writeEvent(ev); } break; case CTRL_AMODDEPTH: _preset[ch]->setIsUsed(true); _preset[ch]->lfo.aModDepth=val; setLfo(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_AMODDEPTH,val); _gui->writeEvent(ev); } break; case CTRL_SYNC: _preset[ch]->setIsUsed(true); _preset[ch]->lfo.sync=val==1; setLfo(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_SYNC,val); _gui->writeEvent(ev); } break; case CTRL_WAVE: _preset[ch]->setIsUsed(true); _preset[ch]->lfo.wave=(Wave)val; setLfo(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_WAVE,val); _gui->writeEvent(ev); } break; case CTRL_PMODSENS: _preset[ch]->setIsUsed(true); _preset[ch]->sensitivity.pitch=val; setLfo(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PMODSENS,val); _gui->writeEvent(ev); } break; case CTRL_AMS: _preset[ch]->setIsUsed(true); _preset[ch]->sensitivity.amplitude=val; setLfo(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_AMS,val); _gui->writeEvent(ev); } break; case CTRL_TRANSPOSE: _preset[ch]->setIsUsed(true); _preset[ch]->function.transpose=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_TRANSPOSE,val); _gui->writeEvent(ev); } break; case CTRL_POLYMODE: _preset[ch]->setIsUsed(true); _preset[ch]->function.mode=(Mode)val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_POLYMODE,val); _gui->writeEvent(ev); } break; case CTRL_PBENDRANGE: _preset[ch]->setIsUsed(true); _preset[ch]->function.pBendRange=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PBENDRANGE,val); _gui->writeEvent(ev); } break; case CTRL_PORTAMODE: _preset[ch]->setIsUsed(true); _preset[ch]->function.portamento=(Portamento)val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PORTAMODE,val); _gui->writeEvent(ev); } break; case CTRL_PORTATIME: _preset[ch]->setIsUsed(true); _preset[ch]->function.portamentoTime=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PORTATIME,val); _gui->writeEvent(ev); } break; case CTRL_FCVOLUME: _preset[ch]->setIsUsed(true); _preset[ch]->function.fcVolume=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_FCVOLUME,val); _gui->writeEvent(ev); } break; case CTRL_FSW: _preset[ch]->setIsUsed(true); _preset[ch]->function.footSw=(FootSw)val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_FSW,val); _gui->writeEvent(ev); } break; case CTRL_MWPITCH: _preset[ch]->setIsUsed(true); _preset[ch]->function.mwPitch=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_MWPITCH,val); _gui->writeEvent(ev); } break; case CTRL_MWAMPLITUDE: _preset[ch]->setIsUsed(true); _preset[ch]->function.mwAmplitude=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_MWAMPLITUDE,val); _gui->writeEvent(ev); } break; case CTRL_BCPITCH: _preset[ch]->setIsUsed(true); _preset[ch]->function.bcPitch=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_BCPITCH,val); _gui->writeEvent(ev); } break; case CTRL_BCAMPLITUDE: _preset[ch]->setIsUsed(true); _preset[ch]->function.bcAmplitude=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_BCAMPLITUDE,val); _gui->writeEvent(ev); } break; case CTRL_BCPITCHBIAS: _preset[ch]->setIsUsed(true); _preset[ch]->function.bcPitchBias=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_BCPITCHBIAS,val); _gui->writeEvent(ev); } break; case CTRL_BCEGBIAS: _preset[ch]->setIsUsed(true); _preset[ch]->function.bcEgBias=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_BCEGBIAS,val); _gui->writeEvent(ev); } break; case CTRL_ATPITCH: _preset[ch]->setIsUsed(true); _preset[ch]->function.atPitch=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_ATPITCH,val); _gui->writeEvent(ev); } break; case CTRL_ATAMPLITUDE: _preset[ch]->setIsUsed(true); _preset[ch]->function.atAmplitude=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_ATAMPLITUDE,val); _gui->writeEvent(ev); } break; case CTRL_ATPITCHBIAS: _preset[ch]->setIsUsed(true); _preset[ch]->function.atPitchBias=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_ATPITCHBIAS,val); _gui->writeEvent(ev); } break; case CTRL_ATEGBIAS: _preset[ch]->setIsUsed(true); _preset[ch]->function.atEgBias=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_ATEGBIAS,val); _gui->writeEvent(ev); } break; case CTRL_PR1: _preset[ch]->setIsUsed(true); _preset[ch]->pitchEg.pr1=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PR1,val); _gui->writeEvent(ev); } break; case CTRL_PR2: _preset[ch]->setIsUsed(true); _preset[ch]->pitchEg.pr2=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PR2,val); _gui->writeEvent(ev); } break; case CTRL_PR3: _preset[ch]->setIsUsed(true); _preset[ch]->pitchEg.pr3=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PR3,val); _gui->writeEvent(ev); } break; case CTRL_PL1: _preset[ch]->setIsUsed(true); _preset[ch]->pitchEg.pl1=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PL1,val); _gui->writeEvent(ev); } break; case CTRL_PL2: _preset[ch]->setIsUsed(true); _preset[ch]->pitchEg.pl2=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PL2,val); _gui->writeEvent(ev); } break; case CTRL_PL3: _preset[ch]->setIsUsed(true); _preset[ch]->pitchEg.pl3=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_PL3,val); _gui->writeEvent(ev); } break; case CTRL_FIX: _preset[ch]->setIsUsed(true); _preset[ch]->frequency[k].isFix=val==1; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_FIX+k*DECAPAR2,val); _gui->writeEvent(ev); } break; case CTRL_FIXRANGE: _preset[ch]->setIsUsed(true); _preset[ch]->frequency[k].freq=((double)val)/100.0; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER, CTRL_FIXRANGE+k*DECAPAR2,val); _gui->writeEvent(ev); } break; case CTRL_OSW: _preset[ch]->setIsUsed(true); _preset[ch]->oscWave[k]=(OscWave)val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_OSW+k*DECAPAR2,val); _gui->writeEvent(ev); } break; case CTRL_SHFT: _preset[ch]->setIsUsed(true); _preset[ch]->eg[k].egShift=(egShiftValue)val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_SHFT+k*DECAPAR2,val); _gui->writeEvent(ev); } break; case CTRL_REVERBRATE: _preset[ch]->setIsUsed(true); _preset[ch]->function.reverbRate=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_REVERBRATE,val); _gui->writeEvent(ev); } break; case CTRL_FCPITCH: _preset[ch]->setIsUsed(true); _preset[ch]->function.fcPitch=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_FCPITCH,val); _gui->writeEvent(ev); } break; case CTRL_FCAMPLITUDE: _preset[ch]->setIsUsed(true); _preset[ch]->function.fcAmplitude=val; if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_FCAMPLITUDE,val); _gui->writeEvent(ev); } break; case CTRL_CHANNELENABLE: setChannelEnable(ch, (bool)val); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_CHANNELENABLE,val); _gui->writeEvent(ev); } break; case CTRL_CHANNELDETUNE: _preset[ch]->setIsUsed(true); setChannelDetune(ch, val); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_CHANNELDETUNE,val); _gui->writeEvent(ev); } break; case CTRL_CHANNELVOLUME: setChannelVol(ch, val); applyChannelAmp(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_CHANNELVOLUME,val); _gui->writeEvent(ev); } break; case CTRL_NBRVOICES: setNbrVoices(ch, val); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, CTRL_NBRVOICES, val); _gui->writeEvent(ev); } break; case MusECore::CTRL_PROGRAM: { int hbank = (val & 0xff0000) >> 16; int lbank = (val & 0xff00) >> 8; int prog = val & 0x7f; if (hbank > 127) // map "dont care" to 0 hbank = 0; if (lbank > 127) lbank = 0; programSelect(ch, hbank, lbank, prog); _preset[ch]->setIsUsed(true);//TODO : not sure to put that if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, MusECore::CTRL_PROGRAM, val); _gui->writeEvent(ev); } } break; case MusECore::CTRL_MODULATION: setModulation(ch, val); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, MusECore::CTRL_MODULATION, val); _gui->writeEvent(ev); } break; case MusECore::CTRL_PITCH: setPitchBendCoef(ch, val); break; case MusECore::CTRL_PANPOT: _preset[ch]->setIsUsed(true); deiPan = val*2*MAXCHANNELPAN/127-MAXCHANNELPAN; setChannelPan(ch, deiPan); applyChannelAmp(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, CTRL_CHANNELPAN, deiPan); _gui->writeEvent(ev); } break; case CTRL_CHANNELPAN: _preset[ch]->setIsUsed(true); setChannelPan(ch, val); applyChannelAmp(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, CTRL_CHANNELPAN, val); _gui->writeEvent(ev); } break; case CTRL_FINEBRIGHTNESS: _preset[ch]->setIsUsed(true); setChannelBrightness(ch, val); setOutLevel(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, CTRL_FINEBRIGHTNESS, val); _gui->writeEvent(ev); } break; case MusECore::CTRL_BRIGHTNESS: _preset[ch]->setIsUsed(true); setChannelBrightness(ch, val*(MIDFINEBRIGHTNESS/MIDBRIGHTNESS)); setOutLevel(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch,MusECore::ME_CONTROLLER,CTRL_FINEBRIGHTNESS,getChannelBrightness(ch)); _gui->writeEvent(ev); } break; case MusECore::CTRL_ATTACK_TIME: _preset[ch]->setIsUsed(true); setChannelAttack(ch, val); setEnvAttack(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, MusECore::CTRL_ATTACK_TIME, val); _gui->writeEvent(ev); } break; case MusECore::CTRL_RELEASE_TIME: _preset[ch]->setIsUsed(true); setChannelRelease(ch, val); setEnvRelease(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, MusECore::CTRL_RELEASE_TIME, val); _gui->writeEvent(ev); } break; case MusECore::CTRL_REVERB_SEND: setChannelReverb(ch, val); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, MusECore::CTRL_REVERB_SEND, val); _gui->writeEvent(ev); } break; case MusECore::CTRL_CHORUS_SEND: setChannelChorus(ch, val); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, MusECore::CTRL_CHORUS_SEND, val); _gui->writeEvent(ev); } break; case MusECore::CTRL_VARIATION_SEND: setChannelDelay(ch, val); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, MusECore::CTRL_VARIATION_SEND, val); _gui->writeEvent(ev); } break; case MusECore::CTRL_SUSTAIN: setSustain(ch, val); break; case MusECore::CTRL_VOLUME: setChannelVol(ch, val*(MAXCHANNELVOLUME/127)); applyChannelAmp(ch); if(!fromGui) { MusECore::MidiPlayEvent ev(0, 0, ch, MusECore::ME_CONTROLLER, CTRL_CHANNELVOLUME, getChannelVol(ch)); _gui->writeEvent(ev); } break; case MusECore::CTRL_ALL_SOUNDS_OFF: resetVoices(); default: break; } } return false; } //--------------------------------------------------------- // getPatchName //--------------------------------------------------------- const char* DeicsOnze::getPatchName(int ch, int val, bool) const { if(_global.channel[ch].isEnable) { Preset* p_preset; int hbank = (val & 0xff0000) >> 16; int lbank = (val & 0xff00) >> 8; if (hbank > 127) hbank = 0; if (lbank > 127) lbank = 0; if (lbank == 127) // drum HACK lbank = 128; int prog = val & 0x7f; const char* tempName="INITVOICE"; p_preset=_set->findPreset(hbank, lbank, prog); if (p_preset) tempName=const_cast(p_preset->name.c_str()); return tempName; } //return " "; // ?? return ""; } //--------------------------------------------------------- // getPatchInfo //--------------------------------------------------------- const MidiPatch* DeicsOnze::getPatchInfo(int /*ch*/, const MidiPatch* p) const { Preset* preset = NULL; Subcategory* sub = NULL; Category* cat = NULL; if(p) { _patch.hbank = p->hbank; _patch.lbank = p->lbank; _patch.prog = p->prog; switch(p->typ) { case MP_TYPE_HBANK : sub = findSubcategory(_patch.hbank, _patch.lbank); if(sub) { _patch.name = sub->_subcategoryName.c_str(); _patch.typ = MP_TYPE_LBANK; return &_patch; } else { if(_patch.lbank + 1 < LBANK_NBR) { _patch.lbank++; return getPatchInfo(0, &_patch); } else { _patch.prog = PROG_NBR - 1; //hack to go faster _patch.typ = 0; return getPatchInfo(0, &_patch); } } break; case MP_TYPE_LBANK : preset = findPreset(_patch.hbank, _patch.lbank, _patch.prog); _patch.typ = 0; if(preset) { _patch.name = preset->name.c_str(); return &_patch; } else return getPatchInfo(0, &_patch); break; default : if(_patch.prog + 1 < PROG_NBR) { _patch.prog++; preset = findPreset(_patch.hbank, _patch.lbank, _patch.prog); if(preset) { _patch.name = preset->name.c_str(); return &_patch; } else return getPatchInfo(0, &_patch); } else { _patch.prog = 0; if(_patch.lbank + 1 < LBANK_NBR) { _patch.lbank++; _patch.typ = MP_TYPE_HBANK; return getPatchInfo(0, &_patch); } else { _patch.lbank = 0; if(_patch.hbank + 1 < HBANK_NBR) { _patch.hbank++; _patch.typ = MP_TYPE_HBANK; cat = findCategory(_patch.hbank); if(cat) { _patch.name = cat->_categoryName.c_str(); return &_patch; } return getPatchInfo(0, &_patch); } else return NULL; } } } } else { _patch.typ = MP_TYPE_HBANK; _patch.hbank = 0; _patch.lbank = 0; _patch.prog = 0; cat = findCategory(_patch.hbank); if(cat) { _patch.name = cat->_categoryName.c_str(); return &_patch; } else { _patch.hbank++; return getPatchInfo(0, &_patch); } } } //--------------------------------------------------------- // getControllerInfo /*! \fn SimpleSynth::getControllerInfo \brief Called from host to collect info about which controllers the synth supports \param index current controller number \param name pointer where name is stored \param controller int pointer where muse controller number is stored \param min int pointer where controller min value is stored \param max int pointer where controller max value is stored \return 0 when done, otherwise return next desired controller index */ //--------------------------------------------------------- int DeicsOnze::getControllerInfo(int index, const char** name, int* controller, int* min, int* max, int* initval) const { if (index >= nbrCtrl) { return 0; } *name = _ctrl[index].name.c_str(); *controller = _ctrl[index].num; *min = _ctrl[index].min; *max = _ctrl[index].max; *initval = 0; // p4.0.27 FIXME NOTE TODO return (index +1); } //--------------------------------------------------------- // playNote // process note on //--------------------------------------------------------- bool DeicsOnze::playNote(int ch, int pitch, int velo) { int newVoice; int nO2V; int p2V; double tempTargetFreq; if(_global.channel[ch].isEnable) { if(velo==0) {//Note off p2V=pitchOn2Voice(ch, pitch); //printf("Note Off : pitchOn2Voice = %d\n", p2V); if(p2V<_global.channel[ch].nbrVoices) { if(_global.channel[ch].sustain) _global.channel[ch].voices[p2V].isSustained = true; else { _global.channel[ch].voices[p2V].keyOn = false; _global.channel[ch].lastVoiceKeyOff = p2V; _global.channel[ch].lastVoiceKeyOn.remove(p2V); if(_preset[ch]->function.mode == MONO && existsKeyOn(ch) && _global.channel[ch].voices[p2V].isOn) { newVoice = _global.channel[ch].lastVoiceKeyOn.back(); //portamento if(_preset[ch]->function.portamentoTime!=0) { _global.channel[ch].voices[newVoice].hasAttractor = true; _global.channel[ch].voices[newVoice].attractor = getAttractor(_preset[ch]->function.portamentoTime, _global.deiSampleRate); } else _global.channel[ch].voices[newVoice].hasAttractor = false; //feedback _global.channel[ch].voices[newVoice].sampleFeedback = _global.channel[ch].voices[p2V].sampleFeedback; //on/off _global.channel[ch].voices[p2V].isOn = false; _global.channel[ch].voices[newVoice].isOn = true; //per op for(int i = 0; i < NBROP; i++) { _global.channel[ch].voices[newVoice].op[i].index = _global.channel[ch].voices[p2V].op[i].index; _global.channel[ch].voices[newVoice].op[i].envState = _global.channel[ch].voices[p2V].op[i].envState; _global.channel[ch].voices[newVoice].op[i].envIndex = _global.channel[ch].voices[p2V].op[i].envIndex; _global.channel[ch].voices[newVoice].op[i].envInct = _global.channel[ch].voices[p2V].op[i].envInct; _global.channel[ch].voices[newVoice].op[i].envLevel = _global.channel[ch].voices[p2V].op[i].envLevel; _global.channel[ch].voices[newVoice].op[i].coefVLevel = _global.channel[ch].voices[p2V].op[i].coefVLevel; if(_global.channel[ch].voices[newVoice].hasAttractor) _global.channel[ch].voices[newVoice].op[i].inct = _global.channel[ch].voices[p2V].op[i].inct; } } else { setPitchEnvRelease(ch, p2V); for(int i=0; ifunction.portamentoTime!=0 && _global.channel[ch].isLastNote && ((_preset[ch]->function.portamento==FULL) || (_preset[ch]->function.portamento==FINGER && existsKeyOn(ch)))) { _global.channel[ch].voices[newVoice].hasAttractor = true; _global.channel[ch].voices[newVoice].attractor = getAttractor(_preset[ch]->function.portamentoTime, _global.deiSampleRate); } else _global.channel[ch].voices[newVoice].hasAttractor = false; if(_preset[ch]->lfo.sync) _global.channel[ch].lfoIndex=0; _global.channel[ch].lfoDelayIndex = (_preset[ch]->lfo.delay==0?(double)(RESOLUTION/4):0.0); _global.channel[ch].delayPassed = false; //-------------- //PITCH ENVELOPE //-------------- if(isPitchEnv(&_preset[ch]->pitchEg)) { _global.channel[ch].voices[newVoice].pitchEnvState = PHASE1; _global.channel[ch].voices[newVoice].pitchEnvCoefInctPhase1 = getPitchEnvCoefInct(_preset[ch]->pitchEg.pl1); _global.channel[ch].voices[newVoice].pitchEnvCoefInctPhase2 = getPitchEnvCoefInct(_preset[ch]->pitchEg.pl2); _global.channel[ch].voices[newVoice].pitchEnvCoefInctPhase3 = getPitchEnvCoefInct(_preset[ch]->pitchEg.pl3); _global.channel[ch].voices[newVoice].pitchEnvCoefInct = _global.channel[ch].voices[newVoice].pitchEnvCoefInctPhase1; _global.channel[ch].voices[newVoice].pitchEnvCoefInctInct = getPitchEnvCoefInctInct(_preset[ch]->pitchEg.pl1, _preset[ch]->pitchEg.pl2, _preset[ch]->pitchEg.pr1, _global.deiSampleRate); } else { _global.channel[ch].voices[newVoice].pitchEnvState = OFF_PE; _global.channel[ch].voices[newVoice].pitchEnvCoefInct = 1.0; } //per operator for(int i=0; isensitivity.keyVelocity[i]) *note2Amp((double) (pitch+_preset[ch]->function.transpose), _preset[ch]->scaling.level[i]); _global.channel[ch].voices[newVoice].op[i].amp = outLevel2Amp(_preset[ch]->outLevel[i]) *_global.channel[ch].voices[newVoice].op[i].ampVeloNote * brightness2Amp(ch, i); //---------------- //INDEX & ENVELOPE //---------------- //if index get 0.0, it means that the offset is 0 if(existsKeyOn(ch)) { int lastVoice = _global.channel[ch].lastVoiceKeyOn.back(); if(_preset[ch]->function.mode == MONO) { _global.channel[ch].voices[newVoice].op[i].index = _global.channel[ch].voices[lastVoice].op[i].index; _global.channel[ch].voices[newVoice].sampleFeedback = _global.channel[ch].voices[lastVoice].sampleFeedback; _global.channel[ch].voices[newVoice].op[i].envState = _global.channel[ch].voices[lastVoice].op[i].envState; _global.channel[ch].voices[newVoice].op[i].envIndex = _global.channel[ch].voices[lastVoice].op[i].envIndex; _global.channel[ch].voices[newVoice].op[i].envInct = _global.channel[ch].voices[lastVoice].op[i].envInct; _global.channel[ch].voices[newVoice].op[i].envLevel = _global.channel[ch].voices[lastVoice].op[i].envLevel; _global.channel[ch].voices[newVoice].op[i].coefVLevel = _global.channel[ch].voices[lastVoice].op[i].coefVLevel; _global.channel[ch].voices[lastVoice].isOn = false; } else { _global.channel[ch].voices[newVoice].op[i].index = 0.0; _global.channel[ch].voices[newVoice].sampleFeedback = 0.0; _global.channel[ch].voices[newVoice].op[i].envState = ATTACK; _global.channel[ch].voices[newVoice].op[i].envIndex = 0.0; setEnvAttack(ch, newVoice, i); } } else { _global.channel[ch].voices[newVoice].op[i].index = 0.0; _global.channel[ch].voices[newVoice].sampleFeedback = 0.0; _global.channel[ch].voices[newVoice].op[i].envState = ATTACK; _global.channel[ch].voices[newVoice].op[i].envIndex = 0.0; setEnvAttack(ch, newVoice, i); if(_preset[ch]->function.mode == MONO && _global.channel[ch].isLastNote) { _global.channel[ch].voices[_global.channel[ch].lastVoiceKeyOff] .isOn = false; } } //---- //FREQ //---- //the frequence for each operator is calculated //and is used later to calculate inct tempTargetFreq = (pitch2freq((double)getChannelDetune(ch) /(double)MAXCHANNELDETUNE) /LOWERNOTEFREQ)* (_preset[ch]->frequency[i].isFix? _preset[ch]->frequency[i].freq: (_preset[ch]->frequency[i].ratio *pitch2freq((double)(pitch+_preset[ch]->function.transpose) +(double)_preset[ch]->detune[i]*COEFDETUNE))); //---- //INCT //---- //compute inct _global.channel[ch].voices[newVoice].op[i].targetInct = (double)RESOLUTION / ( _global.deiSampleRate / tempTargetFreq ); if(_global.channel[ch].voices[newVoice].hasAttractor && !_preset[ch]->frequency[i].isFix) _global.channel[ch].voices[newVoice].op[i].inct = _global.channel[ch].lastInc[i]; else _global.channel[ch].voices[newVoice].op[i].inct = _global.channel[ch].voices[newVoice].op[i].targetInct; } //-------------------- //some initializations //-------------------- _global.channel[ch].voices[newVoice].keyOn = true; _global.channel[ch].voices[newVoice].isSustained = false; _global.channel[ch].voices[newVoice].isOn = true; _global.channel[ch].voices[newVoice].pitch = pitch; _global.channel[ch].isLastNote = true; _global.channel[ch].lastVoiceKeyOn.push_back(newVoice); for(int k = 0; k < NBROP; k++) _global.channel[ch].lastInc[k] = _global.channel[ch].voices[newVoice].op[k].inct; return false; } } return false; } //--------------------------------------------------------- // plusMod // add two doubles modulo RESOLUTION //--------------------------------------------------------- inline double plusMod(double x, double y) { double res; res=x+y; if (res>=0) while (res >= (double)RESOLUTION) res-=(double)RESOLUTION; else while (res < 0) res+=(double)RESOLUTION; return res; } //--------------------------------------------------------- // processMessages // Called from host always, even if output path is unconnected. //--------------------------------------------------------- void DeicsOnze::processMessages() { //Process messages from the gui while (_gui->fifoSize()) { MusECore::MidiPlayEvent ev = _gui->readEvent(); if (ev.type() == MusECore::ME_SYSEX) { sysex(ev.len(), ev.data(), true); sendEvent(ev); } else if (ev.type() == MusECore::ME_CONTROLLER) { setController(ev.channel(), ev.dataA(), ev.dataB(), true); sendEvent(ev); } } } //--------------------------------------------------------- // write // synthesize n samples into buffer+offset //--------------------------------------------------------- void DeicsOnze::process(unsigned pos, float** buffer, int offset, int n) { /* //Process messages from the gui while (_gui->fifoSize()) { MusECore::MidiPlayEvent ev = _gui->readEvent(); if (ev.type() == MusECore::ME_SYSEX) { sysex(ev.len(), ev.data(), true); sendEvent(ev); } else if (ev.type() == MusECore::ME_CONTROLLER) { setController(ev.channel(), ev.dataA(), ev.dataB(), true); sendEvent(ev); } } */ float* leftOutput = buffer[0] + offset; float* rightOutput = buffer[1] + offset; float sample[MAXNBRVOICES]; float tempLeftOutput; float tempRightOutput; float tempChannelOutput; float tempChannelLeftOutput; float tempChannelRightOutput; float tempIncChannel; //for optimization float sampleOp[NBROP]; for(int i = 0; i < NBROP; i++) sampleOp[i] = 0.0; float ampOp[NBROP]; for(int i = 0; i < n; i++) { if(_global.qualityCounter == 0) { tempLeftOutput = 0.0; tempRightOutput = 0.0; _global.lastInputLeftChorusSample = 0.0; _global.lastInputRightChorusSample = 0.0; _global.lastInputLeftReverbSample = 0.0; _global.lastInputRightReverbSample = 0.0; _global.lastInputLeftDelaySample = 0.0; _global.lastInputRightDelaySample = 0.0; //per channel for(int c = 0; c < NBRCHANNELS; c++) { tempChannelOutput = 0.0; if(_global.channel[c].isEnable) { //lfo, trick : we use the first quater of the wave W2 lfoUpdate(_preset[c], &_global.channel[c], waveTable[W2]); //optimization tempIncChannel = _global.channel[c].lfoCoefInct * _global.channel[c].pitchBendCoef; //per voice for(int j=0; j<_global.channel[c].nbrVoices; j++) { if (_global.channel[c].voices[j].isOn) { //portamento portamentoUpdate(&_global.channel[c], &_global.channel[c].voices[j]); //pitch envelope pitchEnvelopeUpdate(&_global.channel[c].voices[j], &_preset[c]->pitchEg, _global.deiSampleRate); //per op for(int k=0; ksensitivity.ampOn[k]? _global.channel[c].lfoAmp:1.0) *env2AmpR(_global.deiSampleRate, waveTable[W2], _preset[c]->eg[k], &_global.channel[c].voices[j].op[k]); } switch(_preset[c]->algorithm) { case FIRST : sampleOp[3]=ampOp[3] *waveTable[_preset[c]->oscWave[3]] [(int)plusMod(_global.channel[c].voices[j].op[3].index, (float)RESOLUTION *_global.channel[c].voices[j].sampleFeedback)]; sampleOp[2]=ampOp[2] *waveTable[_preset[c]->oscWave[2]] [(int)plusMod(_global.channel[c].voices[j].op[2].index, (float)RESOLUTION*sampleOp[3])]; sampleOp[1]=ampOp[1] *waveTable[_preset[c]->oscWave[1]] [(int)plusMod(_global.channel[c].voices[j].op[1].index, (float)RESOLUTION*sampleOp[2])]; sampleOp[0]=ampOp[0] *waveTable[_preset[c]->oscWave[0]] [(int)plusMod(_global.channel[c].voices[j].op[0].index, (float)RESOLUTION*sampleOp[1])]; sample[j]=sampleOp[0];///COEFLEVEL; _global.channel[c].voices[j].isOn = (_global.channel[c].voices[j].op[0].envState!=OFF); break; case SECOND : sampleOp[3]=ampOp[3] *waveTable[_preset[c]->oscWave[3]] [(int)plusMod(_global.channel[c].voices[j].op[3].index, (float)RESOLUTION *_global.channel[c].voices[j].sampleFeedback)]; sampleOp[2]=ampOp[2] *waveTable[_preset[c]->oscWave[2]] [(int)_global.channel[c].voices[j].op[2].index]; sampleOp[1]=ampOp[1] *waveTable[_preset[c]->oscWave[1]] [(int)plusMod(_global.channel[c].voices[j].op[1].index, (float)RESOLUTION *(sampleOp[2]+sampleOp[3])/2.0)]; sampleOp[0]=ampOp[0] *waveTable[_preset[c]->oscWave[0]] [(int)plusMod(_global.channel[c].voices[j].op[0].index, (float)RESOLUTION *sampleOp[1])]; sample[j]=sampleOp[0];///COEFLEVEL; _global.channel[c].voices[j].isOn = (_global.channel[c].voices[j].op[0].envState!=OFF); break; case THIRD : sampleOp[3]=ampOp[3] *waveTable[_preset[c]->oscWave[3]] [(int)plusMod(_global.channel[c].voices[j].op[3].index, (float)RESOLUTION *_global.channel[c].voices[j].sampleFeedback)]; sampleOp[2]=ampOp[2] *waveTable[_preset[c]->oscWave[2]] [(int)_global.channel[c].voices[j].op[2].index]; sampleOp[1]=ampOp[1] *waveTable[_preset[c]->oscWave[1]] [(int)plusMod(_global.channel[c].voices[j].op[1].index, (float)RESOLUTION*sampleOp[2])]; sampleOp[0]=ampOp[0] *waveTable[_preset[c]->oscWave[0]] [(int)plusMod(_global.channel[c].voices[j].op[0].index, (float)RESOLUTION *(sampleOp[3]+sampleOp[1])/2.0)]; sample[j]=sampleOp[0];///COEFLEVEL; _global.channel[c].voices[j].isOn = (_global.channel[c].voices[j].op[0].envState!=OFF); break; case FOURTH : sampleOp[3]=ampOp[3] *waveTable[_preset[c]->oscWave[3]] [(int)plusMod(_global.channel[c].voices[j].op[3].index, (float)RESOLUTION *_global.channel[c].voices[j].sampleFeedback)]; sampleOp[2]=ampOp[2] *waveTable[_preset[c]->oscWave[2]] [(int)plusMod(_global.channel[c].voices[j].op[2].index, (float)RESOLUTION *sampleOp[3])]; sampleOp[1]=ampOp[1] *waveTable[_preset[c]->oscWave[1]] [(int)_global.channel[c].voices[j].op[1].index]; sampleOp[0]=ampOp[0] *waveTable[_preset[c]->oscWave[0]] [(int)plusMod(_global.channel[c].voices[j].op[0].index, (float)RESOLUTION *(sampleOp[1]+sampleOp[2])/2.0)]; sample[j]=sampleOp[0];///COEFLEVEL; _global.channel[c].voices[j].isOn = (_global.channel[c].voices[j].op[0].envState!=OFF); break; case FIFTH : sampleOp[3]=ampOp[3] *waveTable[_preset[c]->oscWave[3]] [(int)plusMod(_global.channel[c].voices[j].op[3].index, (float)RESOLUTION *_global.channel[c].voices[j].sampleFeedback)]; sampleOp[2]=ampOp[2] *waveTable[_preset[c]->oscWave[2]] [(int)plusMod(_global.channel[c].voices[j].op[2].index, (float)RESOLUTION*sampleOp[3])]; sampleOp[1]=ampOp[1] *waveTable[_preset[c]->oscWave[1]] [(int)_global.channel[c].voices[j].op[1].index]; sampleOp[0]=ampOp[0] *waveTable[_preset[c]->oscWave[0]] [(int)plusMod(_global.channel[c].voices[j].op[0].index, (float)RESOLUTION*sampleOp[1])]; sample[j]=(sampleOp[0]+sampleOp[2])/2.0;///COEFLEVEL; _global.channel[c].voices[j].isOn = (_global.channel[c].voices[j].op[0].envState!=OFF ||_global.channel[c].voices[j].op[2].envState!=OFF); break; case SIXTH : sampleOp[3]=ampOp[3] *waveTable[_preset[c]->oscWave[3]] [(int)plusMod(_global.channel[c].voices[j].op[3].index, (float)RESOLUTION *_global.channel[c].voices[j].sampleFeedback)]; sampleOp[2]=ampOp[2] *waveTable[_preset[c]->oscWave[2]] [(int)plusMod(_global.channel[c].voices[j].op[2].index, (float)RESOLUTION*sampleOp[3])]; sampleOp[1]=ampOp[1] *waveTable[_preset[c]->oscWave[1]] [(int)plusMod(_global.channel[c].voices[j].op[1].index, (float)RESOLUTION*sampleOp[3])]; sampleOp[0]=ampOp[0] *waveTable[_preset[c]->oscWave[0]] [(int)plusMod(_global.channel[c].voices[j].op[0].index, (float)RESOLUTION*sampleOp[3])]; sample[j]=(sampleOp[0]+sampleOp[1]+sampleOp[2])/3.0; _global.channel[c].voices[j].isOn = (_global.channel[c].voices[j].op[0].envState!=OFF); break; case SEVENTH : sampleOp[3]=ampOp[3] *waveTable[_preset[c]->oscWave[3]] [(int)plusMod(_global.channel[c].voices[j].op[3].index, (float)RESOLUTION *_global.channel[c].voices[j].sampleFeedback)]; sampleOp[2]=ampOp[2] *waveTable[_preset[c]->oscWave[2]] [(int)plusMod(_global.channel[c].voices[j].op[2].index, (float)RESOLUTION*sampleOp[3])]; sampleOp[1]=ampOp[1] *waveTable[_preset[c]->oscWave[1]] [(int)_global.channel[c].voices[j].op[1].index]; sampleOp[0]=ampOp[0]*waveTable[_preset[c]->oscWave[0]] [(int)_global.channel[c].voices[j].op[0].index]; sample[j]=(sampleOp[0]+sampleOp[1]+sampleOp[2])/3.0; _global.channel[c].voices[j].isOn = (_global.channel[c].voices[j].op[0].envState!=OFF); break; case EIGHTH : sampleOp[3]=ampOp[3] *waveTable[_preset[c]->oscWave[3]] [(int)plusMod(_global.channel[c].voices[j].op[3].index, (float)RESOLUTION *_global.channel[c].voices[j].sampleFeedback)]; sampleOp[2]=ampOp[2] *waveTable[_preset[c]->oscWave[2]] [(int)_global.channel[c].voices[j].op[2].index]; sampleOp[1]=ampOp[1] *waveTable[_preset[c]->oscWave[1]] [(int)_global.channel[c].voices[j].op[1].index]; sampleOp[0]=ampOp[0] *waveTable[_preset[c]->oscWave[0]] [(int)_global.channel[c].voices[j].op[0].index]; sample[j]= (sampleOp[0]+sampleOp[1]+sampleOp[2]+sampleOp[3]) /4.0; _global.channel[c].voices[j].isOn = (_global.channel[c].voices[j].op[0].envState!=OFF || _global.channel[c].voices[j].op[1].envState!=OFF || _global.channel[c].voices[j].op[2].envState!=OFF || _global.channel[c].voices[j].op[3].envState!=OFF); break; default : printf("Error : No algorithm"); break; } _global.channel[c].voices[j].volume= ampOp[0]+ampOp[1]+ampOp[2]+ampOp[3]; _global.channel[c].voices[j].sampleFeedback = sampleOp[3]*_global.channel[c].feedbackAmp; tempChannelOutput += sample[j]; } } //printf("left out = %f, temp out = %f, left amp = %f\n", //tempLeftOutput, tempChannelOutput, _global.channel[c].ampLeft); tempChannelLeftOutput = tempChannelOutput*_global.channel[c].ampLeft; tempChannelRightOutput=tempChannelOutput*_global.channel[c].ampRight; if(_global.isChorusActivated) { _global.lastInputLeftChorusSample += tempChannelLeftOutput * _global.channel[c].chorusAmount; _global.lastInputRightChorusSample += tempChannelRightOutput * _global.channel[c].chorusAmount; } if(_global.isReverbActivated) { _global.lastInputLeftReverbSample += tempChannelLeftOutput * _global.channel[c].reverbAmount; _global.lastInputRightReverbSample += tempChannelRightOutput * _global.channel[c].reverbAmount; } if(_global.isDelayActivated) { _global.lastInputLeftDelaySample += tempChannelLeftOutput * _global.channel[c].delayAmount; _global.lastInputRightDelaySample += tempChannelRightOutput * _global.channel[c].delayAmount; } tempLeftOutput += tempChannelLeftOutput; tempRightOutput += tempChannelRightOutput; } } _global.lastLeftSample = tempLeftOutput * _global.masterVolume; _global.lastRightSample = tempRightOutput * _global.masterVolume; } leftOutput[i] += _global.lastLeftSample; rightOutput[i] += _global.lastRightSample; if(_global.isChorusActivated) { tempInputChorus[0][i] = _global.lastInputLeftChorusSample; tempInputChorus[1][i] = _global.lastInputRightChorusSample; } if(_global.isReverbActivated) { tempInputReverb[0][i] = _global.lastInputLeftReverbSample; tempInputReverb[1][i] = _global.lastInputRightReverbSample; } if(_global.isDelayActivated) { tempInputDelay[0][i] = _global.lastInputLeftDelaySample; tempInputDelay[1][i] = _global.lastInputRightDelaySample; } _global.qualityCounter++; _global.qualityCounter %= _global.qualityCounterTop; } //apply Filter if(_global.filter) _dryFilter->process(leftOutput, rightOutput, n); //Chorus if(_pluginIChorus) { if(_global.isChorusActivated) { //apply Filter if(_global.filter) _chorusFilter->process(tempOutputChorus[0], tempOutputChorus[1], n); //apply Chorus _pluginIChorus->apply(pos, n, 2, tempInputChorus, tempOutputChorus); for(int i = 0; i < n; i++) { leftOutput[i] += tempOutputChorus[0][i] * _global.chorusReturn * _global.masterVolume; rightOutput[i] += tempOutputChorus[1][i] * _global.chorusReturn * _global.masterVolume; } } else _pluginIChorus->apply(pos, n, 0, 0, 0); // Just process controls only, not audio (do not 'run'). Tim. } //Reverb if(_pluginIReverb) { if(_global.isReverbActivated) { //apply Filter if(_global.filter) _reverbFilter->process(tempOutputReverb[0], tempOutputReverb[1], n); //apply Reverb _pluginIReverb->apply(pos, n, 2, tempInputReverb, tempOutputReverb); for(int i = 0; i < n; i++) { leftOutput[i] += tempOutputReverb[0][i] * _global.reverbReturn * _global.masterVolume; rightOutput[i] += tempOutputReverb[1][i] * _global.reverbReturn * _global.masterVolume; } } else _pluginIReverb->apply(pos, n, 0, 0, 0); // Just process controls only, not audio (do not 'run'). Tim. } //Delay if(_pluginIDelay) { if(_global.isDelayActivated) { //apply Filter if(_global.filter) _delayFilter->process(tempOutputDelay[0], tempOutputDelay[1], n); //apply Delay _pluginIDelay->apply(pos, n, 2, tempInputDelay, tempOutputDelay); for(int i = 0; i < n; i++) { leftOutput[i] += tempOutputDelay[0][i] * _global.delayReturn * _global.masterVolume; rightOutput[i] += tempOutputDelay[1][i] * _global.delayReturn * _global.masterVolume; } } else _pluginIDelay->apply(pos, n, 0, 0, 0); // Just process controls only, not audio (do not 'run'). Tim. } } //--------------------------------------------------------- // inst //--------------------------------------------------------- class QWidget; static Mess* instantiate(unsigned long long /*parentWinId*/, const char* /*name*/, const MessConfig* config) { DEI_hostConfigPath = QString(config->_configPath); DEI_hostCachePath = QString(config->_cachePath); DEI_globalLibPath = QString(config->_globalLibPath); DEI_sharePath = QString(config->_globalSharePath); DEI_segmentSize = config->_segmentSize; DEI_sampleRate = config->_sampleRate; DEI_useDenormalBias = config->_useDenormalBias; DEI_denormalBias = config->_denormalBias; DeicsOnze* deicsonze = new DeicsOnze(); deicsonze->setSampleRate(config->_sampleRate); return deicsonze; } extern "C" { static MESS descriptor = { "DeicsOnze", "DeicsOnze FM DX11/TX81Z emulator", "0.5.5", // version string MESS_MAJOR_VERSION, MESS_MINOR_VERSION, instantiate }; // We must compile with -fvisibility=hidden to avoid namespace // conflicts with global variables. // Only visible symbol is "mess_descriptor". // (TODO: all plugins should be compiled this way) __attribute__ ((visibility("default"))) const MESS* mess_descriptor() { return &descriptor; } } muse-4.0.0/src/synti/deicsonze/deicsonze.h000066400000000000000000000464471404105311000205440ustar00rootroot00000000000000//=========================================================================== // // DeicsOnze an emulator of the YAMAHA DX11 synthesizer // // Version 0.5.5 // // // // // Copyright (c) 2004-2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #ifndef __DEICSONZE_H #define __DEICSONZE_H #include #include "common_defs.h" #include "deicsonzepreset.h" #include "deicsonzegui.h" #include "deicsonzeplugin.h" #include "deicsonzefilter.h" #include "libsynti/mess.h" #include "libsimpleplugin/simpler_plugin.h" #define DEICSONZESTR "deicsonze" #define MAXPITCHBENDVALUE 8191 #define RESOLUTION 96000 #define MAXFXBUFFERSIZE 48000 #define NBRFXINPUTS 2 #define NBRFXOUTPUTS 2 #define NBRCTRLS 127 #define NBRPRESETS 128 #define LOWERNOTEFREQ 8.176 #define DB0LEVEL 90 #define LENGTHNAME 20 #define LENGTHCATEGORY 20 #define LENGTHSUBCATEGORY 20 #define MAXVELO 127 #define MAXVOLUME 100.0 #define MAXSTRLENGTHINITSETPATH 256 #define MAXSTRLENGTHBACKGROUNDPIXPATH 256 #define MAXSTRLENGTHFXLIB 256 #define MAXSTRLENGTHFXLABEL 256 #define DB_MIN 25.0 //coef determined by ear to sound like the YAMAHA DX11 #define COEFFEEDBACK 0.3 #define COEFPLFO(x) (x==0?0.0:(x==1?0.06:(x==2?0.12:(x==3?0.25:(x==4?0.5:(x==5?0.9:(x==6?3.9:7.9))))))) //return pitch amplitude with respect to sensitivity pitch #define COEFALFO(x) (x==0?0.0:(x==1?0.4:(x==2?0.9:1.0))) #define MAX(x,y) (x2.0, 90->1.0, 80->0.5 ... //--------------------------------------------------------- inline double outLevel2Amp(int ol); //--------------------------------------------------------- // level2amp, // 255->0dB->1.0, 0->-27dB->0 //--------------------------------------------------------- inline double level2amp(int l); //--------------------------------------------------------- // amp2level // 1.0->0dB->255, 0->-27dB->0 //--------------------------------------------------------- inline int amp2level(double amp); //--------------------------------------------------------- // amp2lowlevel // 1.0->0dB->127, 0->-27dB->0 //--------------------------------------------------------- inline int amp2lowlevel(double amp); //--------------------------------------------------------- // lowlevel2amp, // 127->0dB->1.0, 0->-27dB->0 //--------------------------------------------------------- inline double lowlevel2amp(int l); //--------------------------------------------------------- // envAR2s // return the time in second of the ATTACK duration //--------------------------------------------------------- inline double envAR2s(int ar); //--------------------------------------------------------- // coefAttack // convert the attack value to a coef for envInct //--------------------------------------------------------- inline double coefAttack(unsigned char attack); //--------------------------------------------------------- // envRR2coef // return the coefficient for the exponential decrease // with respect to rr and sampleRate, sr //--------------------------------------------------------- inline double envRR2coef(int rr, double sr, unsigned char release); //-------------------------------------------------------- // DeicsOnzeCtrl //-------------------------------------------------------- struct DeicsOnzeCtlr { std::string name; int num; int min, max; }; //--------------------------------------------------------- // EnvState //--------------------------------------------------------- enum EnvState{ ATTACK, DECAY, SUSTAIN, RELEASE, OFF }; //--------------------------------------------------------- // OpVoice //--------------------------------------------------------- struct OpVoice { double index; double inct; double targetInct; //used if portamento double amp; //between 0 and 1 double ampVeloNote; //keeps the ratio amplitude from velo2AmpR and note2Amp //in order to change independently the output level //after pressing the note EnvState envState; double envIndex; double envInct; double envLevel; double coefVLevel; }; //--------------------------------------------------------- // PitchEnvState //--------------------------------------------------------- enum PitchEnvState{ PHASE1, PHASE2, RELEASE_PE, OFF_PE }; //--------------------------------------------------------- // Voice //--------------------------------------------------------- struct Voice { bool hasAttractor;//true iff the voice has an attractor (portamento occurring) double attractor; //contains some coefficient for portamento TODO PitchEnvState pitchEnvState; double pitchEnvCoefInct; double pitchEnvCoefInctPhase1; double pitchEnvCoefInctPhase2; double pitchEnvCoefInctPhase3; double pitchEnvCoefInctRelease; double pitchEnvCoefInctInct; bool isOn; bool keyOn; bool isSustained; int pitch; //number of the note double volume; OpVoice op[NBROP]; float sampleFeedback; }; //--------------------------------------------------------- // Channel //--------------------------------------------------------- struct Channel { bool isEnable; float ampLeft; float ampRight; int volume; //0 to 255 int pan; //TODO -63 +64 or -127 +128 int modulation;//0 to 127 int detune;//-31 to 31 int brightness; //0 to 4095 int attack; //0 to 127 int release; //0 to 127 float feedbackAmp; float lfoFreq; float lfoPitch; float lfoMaxCoefInct; float lfoCoefInct; float lfoCoefInctInct; unsigned int lfoIndex; unsigned int lfoMaxIndex; float lfoMaxAmp; float lfoMaxDAmp; float lfoAmp; float lfoCoefAmp; double lfoDelayIndex; double lfoDelayInct; double lfoDelayMaxIndex; bool delayPassed; bool sustain; double pitchBendCoef;//speed coef to read the sample unsigned char nbrVoices; Voice voices[MAXNBRVOICES]; double lastInc[NBROP]; std::list lastVoiceKeyOn; //stack of the voice number int lastVoiceKeyOff; bool isLastNote; //FX float chorusAmount; //between 0.0 and 1.0 float reverbAmount; //between 0.0 and 1.0 float delayAmount; //between 0.0 and 1.0 }; //--------------------------------------------------------- // Global //--------------------------------------------------------- enum Quality { high, middle, low, ultralow }; struct Global { float masterVolume; Quality quality; //high, middle, low int qualityCounter; //counter to skip some sample depending on quality int qualityCounterTop; //number of sample - 1 to skip double deiSampleRate; //depending on quality deicsOnze sample rate varies bool filter; //low passe filter used when the sampling is low int fontSize; float lastLeftSample; float lastRightSample; float lastInputLeftChorusSample; float lastInputRightChorusSample; float lastInputLeftReverbSample; float lastInputRightReverbSample; float lastInputLeftDelaySample; float lastInputRightDelaySample; Channel channel[NBRCHANNELS]; bool isChorusActivated; float chorusReturn; bool isReverbActivated; float reverbReturn; bool isDelayActivated; float delayReturn; }; //--------------------------------------------------------- // DeicsOnze : DX11 emulator //--------------------------------------------------------- class DeicsOnze : public Mess { DeicsOnzeGui* _gui; unsigned char* initBuffer; int initLen; static int useCount; static float waveTable[NBRWAVES][RESOLUTION]; private: void parseInitData(int length, const unsigned char* data); void loadConfiguration(QString fileName); void setupInitBuffer(int len); public: float** tempInputChorus; float** tempOutputChorus; float** tempInputReverb; float** tempOutputReverb; float** tempInputDelay; float** tempOutputDelay; float* getSinusWaveTable(); int nbrCtrl; QString _initSetPath; bool _isInitSet; QString _backgroundPixPath; bool _isBackgroundPix; bool _saveOnlyUsed; bool _saveConfig; DeicsOnzeCtlr _ctrl[NBRCTRLS]; Global _global; Preset* _preset[NBRCHANNELS]; Preset* _initialPreset; //FX MusESimplePlugin::PluginI* _pluginIReverb; MusESimplePlugin::PluginI* _pluginIChorus; MusESimplePlugin::PluginI* _pluginIDelay; void initPluginReverb(MusESimplePlugin::Plugin*); void initPluginChorus(MusESimplePlugin::Plugin*); void initPluginDelay(MusESimplePlugin::Plugin*); void setReverbParam(int i, float val); float getReverbParam(int i) const; void setChorusParam(int i, float val); float getChorusParam(int i) const; void setDelayParam(int i, float val); float getDelayParam(int i) const; void setDelayBPM(float val); void setDelayBeatRatio(float val); void setDelayFeedback(float val); void setDelayLFOFreq(float val); void setDelayLFODepth(float val); void setDelayDryWet(float val); float getDelayDryWet() const; float getDelayBPM() const; float getDelayBeatRatio() const; float getDelayFeedback() const; float getDelayLFOFreq() const; float getDelayLFODepth() const; //Filter LowFilter* _dryFilter; LowFilter* _chorusFilter; LowFilter* _reverbFilter; LowFilter* _delayFilter; mutable MidiPatch _patch; mutable int _numPatchProg; //used by getPatchInfo //preset tree Set* _set; void setSampleRate(int sr); Preset* findPreset(int hbank, int lbank, int prog) const; Subcategory* findSubcategory(int hbank, int lbank) const; Category* findCategory(int hbank) const; void initCtrls(); void initGlobal(); void initChannels(); void initChannel(int c); void resetVoices(); //when panic is pressed void initVoice(int c, int v); void initVoices(int c); void setPreset(int c); void setFeedback(int c); void setLfo(int c); void setOutLevel(int c, int k); //set the output level of the op k void setOutLevel(int c); //do the same for all operators void setEnvAttack(int c, int v, int k); //set envInct of voice v and op k void setEnvAttack(int c, int k); //do the same for all voices of operator k void setEnvAttack(int c); //do the same for all voices all operators void setEnvRelease(int c, int v, int k); //set coefVLevel of voice v and op k void setEnvRelease(int c, int k); //do the same for all voices of operator k void setEnvRelease(int c); //do the same for all voices all operators void setPitchEnvRelease(int c, int v); void setQuality(Quality q); void setFilter(bool f); double brightness2Amp(int c, int k); //get the brightness of the operator k void loadSutulaPresets(); void loadSet(QString s); int noteOff2Voice(int c); //return the first free voice int minVolu2Voice(int c); int pitchOn2Voice(int c, int pitch); void programSelect(int c, int hbank, int lbank, int prog); bool existsKeyOn(int ch); void setNbrVoices(int c, int nv); void setMasterVol(int v); void setChannelEnable(int c, bool e); void setChannelVol(int c, int v); void setChannelPan(int c, int v); void applyChannelAmp(int c); void setChannelDetune(int c, int d); void setChannelBrightness(int c, int b); void setChannelModulation(int c, int m); void setChannelAttack(int c, int a); void setChannelRelease(int c, int r); void setChannelReverb(int c, int r); void setChannelChorus(int c, int val); void setChannelDelay(int c, int val); void setChorusReturn(int val); void setReverbReturn(int val); void setDelayReturn(int val); bool getChannelEnable(int c) const; int getNbrVoices(int c) const; int getMasterVol(void) const; bool getFilter(void) const; int getChannelVol(int c) const; int getChannelPan(int c) const; int getChannelDetune(int c) const; int getChannelBrightness(int c) const; int getChannelModulation(int c) const; int getChannelAttack(int c) const; int getChannelRelease(int c) const; int getChannelReverb(int c) const; int getChannelChorus(int c) const; int getChannelDelay(int c) const; int getChorusReturn(void) const; int getReverbReturn(void) const; int getDelayReturn(void) const; void setPitchBendCoef(int c, int val); void setModulation(int c, int val); //TODO check between setChannelModulation void setSustain(int c, int val); void readConfiguration(QDomNode qdn); void writeConfiguration(AL::Xml* xml); bool setController(int ch, int ctrl, int val, bool fromGui); virtual bool setController(int ch, int ctrl, int val); bool sysex(int length, const unsigned char* data, bool fromGui); virtual bool sysex(int l, const unsigned char* d); virtual const char* getPatchName(int ch, int number, bool) const; virtual const MidiPatch* getPatchInfo(int, const MidiPatch *) const; virtual int getControllerInfo(int arg1, const char** arg2, int* arg3, int* arg4, int* arg5, int* arg6) const; virtual void getInitData(int* length, const unsigned char** data); // This is only a kludge required to support old songs' midistates. Do not use in any new synth. virtual int oldMidiStateHeader(const unsigned char** data) const; virtual bool playNote(int channel, int pitch, int velo); virtual void processMessages(); virtual void process(unsigned pos, float** buffer, int offset, int n); // GUI interface routines virtual bool hasNativeGui() const { return true; } virtual bool nativeGuiVisible() const; virtual void showNativeGui(bool); virtual void getNativeGeometry(int* x, int* y, int* w, int* h) const; virtual void setNativeGeometry(int, int, int, int); DeicsOnze(); virtual ~DeicsOnze(); }; extern QString DEI_hostConfigPath; extern QString DEI_hostCachePath; extern QString DEI_sharePath; extern unsigned int DEI_segmentSize; extern int DEI_sampleRate; extern bool DEI_useDenormalBias; extern float DEI_denormalBias; #endif /* __DEICSONZE_H */ muse-4.0.0/src/synti/deicsonze/deicsonze.qrc000066400000000000000000000013651404105311000210700ustar00rootroot00000000000000 pics/SHold.png pics/algo1.png pics/algo2.png pics/algo3.png pics/algo4.png pics/algo5.png pics/algo6.png pics/algo7.png pics/algo8.png pics/sawUp.png pics/square.png pics/triangl.png pics/wave1.png pics/wave2.png pics/wave3.png pics/wave4.png pics/wave5.png pics/wave6.png pics/wave7.png pics/wave8.png muse-4.0.0/src/synti/deicsonze/deicsonzefilter.cpp000066400000000000000000000035011404105311000222650ustar00rootroot00000000000000//=========================================================================== // // DeicsOnze an emulator of the YAMAHA DX11 synthesizer // // Version 0.5.5 // // deicsonzefilter.cpp // // // Copyright (c) 2004-2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #include "deicsonzefilter.h" LowFilter::LowFilter() { _li = 0.0; _ri = 0.0; _lo = 0.0; _ro = 0.0; } void LowFilter::setSamplerate(int sr) { _samplerate = sr; } void LowFilter::setCutoff(double cut) { _cutoff = cut; float w = 2.0 * (float)_samplerate; float fCut = _cutoff * 2.0 * M_PI; float norm = 1.0 / (fCut + w); _a = fCut * norm; _b = (w - fCut) * norm; } void LowFilter::process(float* leftSamples, float* rightSamples, unsigned n) { float cl, cr; for(unsigned i = 0; i < n; i++) { cl = leftSamples[i]; cr = rightSamples[i]; leftSamples[i] = _a * (cl + _li) + _b * _lo; rightSamples[i] = _a * (cr + _ri) + _b * _ro; _li = cl; _ri = cr; _lo = leftSamples[i]; _ro = rightSamples[i]; } } muse-4.0.0/src/synti/deicsonze/deicsonzefilter.h000066400000000000000000000032471404105311000217410ustar00rootroot00000000000000//=========================================================================== // // DeicsOnze an emulator of the YAMAHA DX11 synthesizer // // Version 0.5.5 // // deicsonzefilter.h // // // Copyright (c) 2004-2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #ifndef __DEICSONZEFILTER_H #define __DEICSONZEFILTER_H #include "muse_math.h" class LowFilter { private: int _samplerate; double _cutoff; //frequency cutoff float _a; float _b; float _li; //last left input sample float _ri; //last right input sample float _lo; //last left output sample float _ro; //last right output sample public: LowFilter(); ~LowFilter() {} void setSamplerate(int sr); void setCutoff(double cut); //int getSamplerate(); //double getCutoff(); void process(float* leftSamples, float* RightSamples, unsigned n); }; #endif /* __DEICSONZEFILTER_H */ muse-4.0.0/src/synti/deicsonze/deicsonzegui.cpp000066400000000000000000005067701404105311000216040ustar00rootroot00000000000000//=========================================================================== // // DeicsOnze an emulator of the YAMAHA DX11 synthesizer // // Version 0.5.5 // // deicsonzegui.cpp // // // Copyright (c) 2004-2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #include #include #include #include #include #include #include #include "muse/midi_consts.h" #include "midictrl_consts.h" #include "common_defs.h" #include "deicsonzegui.h" #include "libsimpleplugin/simpler_plugin.h" #include "libsimpleplugin/simpler_plugingui.h" //#include "plugins/pandelay/pandelaymodel.h" namespace MusEGui { class PluginDialog; } DeicsOnzeGui::DeicsOnzeGui(DeicsOnze* deicsOnze) : QDialog(0), MessGui() { setupUi(this); _deicsOnze = deicsOnze; lastDir = QDir::currentPath(); _currentChannel = 0; //FX _chorusSuperWidget = NULL; _reverbSuperWidget = NULL; tColor = new TCOLOR; bColor = new BCOLOR; etColor = new ETCOLOR; ebColor = new EBCOLOR; curColor = new QColor(0, 0, 0); pitchEnvelopeGraph = new QFramePitchEnvelope(pitchEnvFrame, this); envelopeGraph[0] = new QFrameEnvelope(envelope1Frame, this, 0); envelopeGraph[1] = new QFrameEnvelope(envelope2Frame, this, 1); envelopeGraph[2] = new QFrameEnvelope(envelope3Frame, this, 2); envelopeGraph[3] = new QFrameEnvelope(envelope4Frame, this, 3); //change/enable channel connect(ChannelCheckBox, SIGNAL(toggled(bool)), this, SLOT(setEnabledChannel(bool))); connect(ChannelNumSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setChangeChannel(int))); //MasterVolume connect(masterVolKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setMasterVolKnob(double))); //Panic connect(panicButton, SIGNAL(pressed()), this, SLOT(setPanic())); //reset Ctrls connect(resCtrlButton, SIGNAL(pressed()), this, SLOT(setResCtrl())); //FX connect(chorusActivCheckBox, SIGNAL(toggled(bool)), this, SLOT(setChorusActiv(bool))); connect(chChorusSlider, SIGNAL(valueChanged(int)), this, SLOT(setChannelChorus(int))); connect(returnChorusSlider, SIGNAL(valueChanged(int)), this, SLOT(setChorusReturn(int))); connect(selectLadspaChorusPushButton, SIGNAL(pressed()), this, SLOT(setSelectChorusPlugin())); /*connect(panChorus1Knob, SIGNAL(valueChanged(double, int)), this, SLOT(setPanChorus1(double))); connect(LFOFreqChorus1Knob, SIGNAL(valueChanged(double, int)), this, SLOT(setLFOFreqChorus1(double))); connect(depthChorus1Knob, SIGNAL(valueChanged(double, int)), this, SLOT(setDepthChorus1(double))); connect(panChorus2Knob, SIGNAL(valueChanged(double, int)), this, SLOT(setPanChorus2(double))); connect(LFOFreqChorus2Knob, SIGNAL(valueChanged(double, int)), this, SLOT(setLFOFreqChorus2(double))); connect(depthChorus2Knob, SIGNAL(valueChanged(double, int)), this, SLOT(setDepthChorus2(double)));*/ connect(reverbActivCheckBox, SIGNAL(toggled(bool)), this, SLOT(setReverbActiv(bool))); connect(chReverbSlider, SIGNAL(valueChanged(int)), this, SLOT(setChannelReverb(int))); connect(returnReverbSlider, SIGNAL(valueChanged(int)), this, SLOT(setReverbReturn(int))); connect(selectLadspaReverbPushButton, SIGNAL(pressed()), this, SLOT(setSelectReverbPlugin())); //Quick edit connect(channelVolumeKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setChannelVolKnob(double))); connect(channelPanKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setChannelPan(double))); connect(brightnessKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setBrightnessKnob(double))); connect(modulationKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setModulationKnob(double))); connect(detuneKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setDetuneKnob(double))); connect(attackKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setAttackKnob(double))); connect(releaseKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setReleaseKnob(double))); //nbr of voices connect(nbrVoicesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setNbrVoices(int))); //quality connect(qualityComboBox, SIGNAL(activated(const QString&)), this, SLOT(setQuality(const QString&))); connect(filterCheckBox, SIGNAL(toggled(bool)), this, SLOT(setFilter(bool))); //change font size connect(fontSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFontSize(int))); //load save configuration connect(saveConfPushButton, SIGNAL(pressed()), this, SLOT(saveConfiguration())); connect(loadConfPushButton, SIGNAL(pressed()), this, SLOT(loadConfiguration())); connect(saveDefaultPushButton, SIGNAL(pressed()), this, SLOT(saveDefaultConfiguration())); //load init set connect(initSetCheckBox, SIGNAL(toggled(bool)), this, SLOT(setIsInitSet(bool))); connect(initSetPathLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setInitSetPath(const QString&))); connect(initSetBrowsePushButton, SIGNAL(pressed()), this, SLOT(setBrowseInitSetPath())); //load background pix connect(imageCheckBox, SIGNAL(toggled(bool)), this, SLOT(setIsBackgroundPix(bool))); connect(imagePathLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setBackgroundPixPath(const QString&))); connect(imageBrowsePushButton, SIGNAL(pressed()), this, SLOT(setBrowseBackgroundPixPath())); //Midi in channel //connect(MidiInChComboBox, SIGNAL(activated(int)), // this, SLOT(setMidiInCh(int))); //Save mode ratio button connect(minSaveRadioButton, SIGNAL(toggled(bool)), this, SLOT(setSaveOnlyUsed(bool))); connect(hugeSaveRadioButton, SIGNAL(toggled(bool)), this, SLOT(setSaveOnlyUsedComp(bool))); connect(saveConfigCheckBox, SIGNAL(toggled(bool)), this, SLOT(setSaveConfig(bool))); //Colors connect(redSlider, SIGNAL(valueChanged(int)), this, SLOT(setRedColor(int))); connect(greenSlider, SIGNAL(valueChanged(int)), this, SLOT(setGreenColor(int))); connect(blueSlider, SIGNAL(valueChanged(int)), this, SLOT(setBlueColor(int))); connect(colorListBox, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, SLOT(setRGBSliders(QListWidgetItem*))); //PopupMenu Category Subcategory Preset connect(categoryListView, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(categoryPopupMenu(const QPoint&))); connect(subcategoryListView, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(subcategoryPopupMenu(const QPoint&))); connect(presetListView, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(presetPopupMenu(const QPoint &))); //Preset and bank connect(presetLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setPresetName(const QString&))); connect(subcategoryLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setSubcategoryName(const QString&))); connect(categoryLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(setCategoryName(const QString&))); connect(hbankSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setHBank(int))); connect(lbankSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setLBank(int))); connect(progSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setProg(int))); //Global //connect(channelPanSlider, SIGNAL(valueChanged(int)), //this, SLOT(setChannelPan(int))); connect(feedbackSlider, SIGNAL(valueChanged(int)), this, SLOT(setFeedback(int))); connect(LFOWaveComboBox, SIGNAL(activated(int)), this, SLOT(setLfoWave(int))); connect(LFOSpeedSlider, SIGNAL(valueChanged(int)), this, SLOT(setLfoSpeed(int))); connect(LFODelaySlider, SIGNAL(valueChanged(int)), this, SLOT(setLfoDelay(int))); connect(PModDepthSlider, SIGNAL(valueChanged(int)), this, SLOT(setLfoPModDepth(int))); connect(PModSensSlider, SIGNAL(valueChanged(int)), this, SLOT(setLfoPitchSens(int))); connect(AModDepthSlider, SIGNAL(valueChanged(int)), this, SLOT(setLfoAModDepth(int))); connect(AModSensSlider, SIGNAL(valueChanged(int)), this, SLOT(setLfoAmpSens(int))); connect(transposeSlider, SIGNAL(valueChanged(int)), this, SLOT(setTranspose(int))); //connect(channelDetuneSlider, SIGNAL(valueChanged(int)), // this, SLOT(setChannelDetune(int))); connect(algorithmComboBox, SIGNAL(activated(int)), this, SLOT(setAlgorithm(int))); connect(pitchBendRangeSlider, SIGNAL(valueChanged(int)), this, SLOT(setPitchBendRange(int))); //Pitch Envelope connect(PL1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setPL1(int))); connect(PL2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setPL2(int))); connect(PL3SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setPL3(int))); connect(PR1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setPR1(int))); connect(PR2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setPR2(int))); connect(PR3SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setPR3(int))); //Function connect(fcVolumeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFcVolume(int))); connect(fcPitchSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFcPitch(int))); connect(fcAmplitudeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFcAmplitude(int))); connect(mwPitchSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setMwPitch(int))); connect(mwAmplitudeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setMwAmplitude(int))); connect(bcPitchSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setBcPitch(int))); connect(bcAmplitudeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setBcAmplitude(int))); connect(bcPitchBiasSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setBcPitchBias(int))); connect(bcEgBiasSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setBcEgBias(int))); connect(atPitchSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAtPitch(int))); connect(atAmplitudeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAtAmplitude(int))); connect(atPitchBiasSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAtPitchBias(int))); connect(atEgBiasSpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAtEgBias(int))); //connect(reverbSpinBox, SIGNAL(valueChanged(int)), // this, SLOT(setReverbRate(int))); connect(polyMonoComboBox, SIGNAL(activated(int)), this, SLOT(setPolyMode(int))); connect(PortFingerFullComboBox, SIGNAL(activated(int)), this, SLOT(setPortFingerFull(int))); connect(PortamentoTimeSlider, SIGNAL(valueChanged(int)), this, SLOT(setPortaTime(int))); //envelope connect(AR1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAR1(int))); connect(D1R1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD1R1(int))); connect(D1L1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD1L1(int))); connect(D2R1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD2R1(int))); connect(RR1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setRR1(int))); connect(AR2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAR2(int))); connect(D1R2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD1R2(int))); connect(D1L2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD1L2(int))); connect(D2R2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD2R2(int))); connect(RR2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setRR2(int))); connect(AR3SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAR3(int))); connect(D1R3SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD1R3(int))); connect(D1L3SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD1L3(int))); connect(D2R3SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD2R3(int))); connect(RR3SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setRR3(int))); connect(AR4SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setAR4(int))); connect(D1R4SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD1R4(int))); connect(D1L4SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD1L4(int))); connect(D2R4SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setD2R4(int))); connect(RR4SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setRR4(int))); //Scaling connect(LS1Slider, SIGNAL(valueChanged(int)), this, SLOT(setLS1(int))); connect(RS1Slider, SIGNAL(valueChanged(int)), this, SLOT(setRS1(int))); connect(LS2Slider, SIGNAL(valueChanged(int)), this, SLOT(setLS2(int))); connect(RS2Slider, SIGNAL(valueChanged(int)), this, SLOT(setRS2(int))); connect(LS3Slider, SIGNAL(valueChanged(int)), this, SLOT(setLS3(int))); connect(RS3Slider, SIGNAL(valueChanged(int)), this, SLOT(setRS3(int))); connect(LS4Slider, SIGNAL(valueChanged(int)), this, SLOT(setLS4(int))); connect(RS4Slider, SIGNAL(valueChanged(int)), this, SLOT(setRS4(int))); //Volume connect(OUT1Slider, SIGNAL(valueChanged(int)), this, SLOT(setVol1(int))); connect(OUT2Slider, SIGNAL(valueChanged(int)), this, SLOT(setVol2(int))); connect(OUT3Slider, SIGNAL(valueChanged(int)), this, SLOT(setVol3(int))); connect(OUT4Slider, SIGNAL(valueChanged(int)), this, SLOT(setVol4(int))); //Ratio and Frequency connect(CoarseRatio1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setCoarseRatio1(int))); connect(FineRatio1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFineRatio1(int))); connect(Freq1SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFreq1(int))); connect(Fix1CheckBox, SIGNAL(toggled(bool)), this, SLOT(setFix1(bool))); connect(CoarseRatio2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setCoarseRatio2(int))); connect(FineRatio2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFineRatio2(int))); connect(Freq2SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFreq2(int))); connect(Fix2CheckBox, SIGNAL(toggled(bool)), this, SLOT(setFix2(bool))); connect(CoarseRatio3SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setCoarseRatio3(int))); connect(FineRatio3SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFineRatio3(int))); connect(Freq3SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFreq3(int))); connect(Fix3CheckBox, SIGNAL(toggled(bool)), this, SLOT(setFix3(bool))); connect(CoarseRatio4SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setCoarseRatio4(int))); connect(FineRatio4SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFineRatio4(int))); connect(Freq4SpinBox, SIGNAL(valueChanged(int)), this, SLOT(setFreq4(int))); connect(Fix4CheckBox, SIGNAL(toggled(bool)), this, SLOT(setFix4(bool))); //Sensitivity connect(AME1CheckBox, SIGNAL(toggled(bool)), this, SLOT(setAME1(bool))); connect(EBS1Slider, SIGNAL(valueChanged(int)), this, SLOT(setEBS1(int))); connect(KVS1Slider, SIGNAL(valueChanged(int)), this, SLOT(setKVS1(int))); connect(AME2CheckBox, SIGNAL(toggled(bool)), this, SLOT(setAME2(bool))); connect(EBS2Slider, SIGNAL(valueChanged(int)), this, SLOT(setEBS2(int))); connect(KVS2Slider, SIGNAL(valueChanged(int)), this, SLOT(setKVS2(int))); connect(AME3CheckBox, SIGNAL(toggled(bool)), this, SLOT(setAME3(bool))); connect(EBS3Slider, SIGNAL(valueChanged(int)), this, SLOT(setEBS3(int))); connect(KVS3Slider, SIGNAL(valueChanged(int)), this, SLOT(setKVS3(int))); connect(AME4CheckBox, SIGNAL(toggled(bool)), this, SLOT(setAME4(bool))); connect(EBS4Slider, SIGNAL(valueChanged(int)), this, SLOT(setEBS4(int))); connect(KVS4Slider, SIGNAL(valueChanged(int)), this, SLOT(setKVS4(int))); //detune connect(DET1Slider, SIGNAL(valueChanged(int)), this, SLOT(setDET1(int))); connect(DET2Slider, SIGNAL(valueChanged(int)), this, SLOT(setDET2(int))); connect(DET3Slider, SIGNAL(valueChanged(int)), this, SLOT(setDET3(int))); connect(DET4Slider, SIGNAL(valueChanged(int)), this, SLOT(setDET4(int))); //WaveForm connect(WaveForm1ComboBox, SIGNAL(activated(int)), this, SLOT(setWaveForm1(int))); connect(WaveForm2ComboBox, SIGNAL(activated(int)), this, SLOT(setWaveForm2(int))); connect(WaveForm3ComboBox, SIGNAL(activated(int)), this, SLOT(setWaveForm3(int))); connect(WaveForm4ComboBox, SIGNAL(activated(int)), this, SLOT(setWaveForm4(int))); //PanDelay connect(delayActivCheckBox, SIGNAL(toggled(bool)), this, SLOT(setActivDelay(bool))); connect(delayReturnSlider, SIGNAL(valueChanged(int)), this, SLOT(setDelayReturn(int))); connect(chDelaySlider, SIGNAL(valueChanged(int)), this, SLOT(setChannelDelay(int))); connect(delayBPMFloatentry, SIGNAL(valueChanged(double, int)), this, SLOT(setDelayBPM(double))); connect(delayBPMKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setDelayBPM(double))); connect(delayBeatRatioFloatentry, SIGNAL(valueChanged(double, int)), this, SLOT(setDelayBeatRatio(double))); connect(delayBeatRatioKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setDelayBeatRatio(double))); connect(delayFeedbackFloatentry, SIGNAL(valueChanged(double, int)), this, SLOT(setDelayFeedback(double))); connect(delayFeedbackKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setDelayFeedback(double))); connect(delayPanLFOFreqFloatentry, SIGNAL(valueChanged(double, int)), this, SLOT(setDelayPanLFOFreq(double))); connect(delayPanLFOFreqKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setDelayPanLFOFreq(double))); delayPanLFOFreqKnob->setMinLogValue(0.1); delayPanLFOFreqKnob->setMaxLogValue(10.0); connect(delayPanLFODepthFloatentry, SIGNAL(valueChanged(double, int)), this, SLOT(setDelayPanLFODepth(double))); connect(delayPanLFODepthKnob, SIGNAL(valueChanged(double, int)), this, SLOT(setDelayPanLFODepth(double))); //category subcategory preset connect(categoryListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(setCategory(QTreeWidgetItem*))); connect(categoryListView, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(setCategory(QTreeWidgetItem*))); connect(subcategoryListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(setSubcategory(QTreeWidgetItem*))); connect(subcategoryListView, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(setSubcategory(QTreeWidgetItem*))); connect(presetListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), this, SLOT(setPreset(QTreeWidgetItem*))); connect(presetListView, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(setPreset(QTreeWidgetItem*))); connect(this->getGuiSignal(),SIGNAL(wakeup()),this,SLOT(readMessage())); updateInitSetPath(DEI_sharePath + QString("/presets/deicsonze/SutulaBank.dei")); // Tim. updateBackgroundPixPath(DEI_sharePath + QString("/wallpapers/paper2.jpg")); // Tim. updateBackgroundPixCheckBox(false); setTextColor(reinterpret_cast(*tColor)); setBackgroundColor(reinterpret_cast(*bColor)); setEditTextColor(reinterpret_cast(*etColor)); setEditBackgroundColor(reinterpret_cast(*ebColor)); //select the first item in the color list colorListBox->setCurrentItem(colorListBox->item(0)); //color the colorFrame with the color of the text QPalette p = colorFrame->palette(); p.setColor(QPalette::Window, (reinterpret_cast(*tColor))); colorFrame->setPalette(p); //update maaster volume //updateMasterVolume(INITMASTERVOL); //update Quick edit updateQuickEdit(); //updatePreset(); _enabledPreset = true; setEnabledPreset(false); } //----------------------------------------------------------- // setEnabledChannel //----------------------------------------------------------- void DeicsOnzeGui::setEnabledChannel(bool e) { sendController(_currentChannel, CTRL_CHANNELENABLE, (int)e); updateEnabledChannel(e); } //----------------------------------------------------------- // setUpdateEnabledChannelCheckBox //----------------------------------------------------------- void DeicsOnzeGui::updateChannelCheckBox(bool b) { ChannelCheckBox->blockSignals(true); ChannelCheckBox->setChecked(b); ChannelCheckBox->blockSignals(false); } //----------------------------------------------------------- // setChangeChannel //----------------------------------------------------------- void DeicsOnzeGui::setChangeChannel(int c) { _currentChannel = c-1; updateChannelEnable(_deicsOnze->getChannelEnable(_currentChannel)); updateNbrVoices(_deicsOnze->getNbrVoices(_currentChannel)); //update quick edit updateQuickEdit(); //update preset int p, l, h; _deicsOnze->_preset[_currentChannel]->getHBankLBankProg(&h, &l, &p); updateSelectPreset(h, l, p); updatePreset(); } //----------------------------------------------------------- // setPanic //----------------------------------------------------------- void DeicsOnzeGui::setPanic() { unsigned char message[3]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_PANIC; sendSysex(message, 3); } //----------------------------------------------------------- // setResCtrl //----------------------------------------------------------- void DeicsOnzeGui::setResCtrl() { //Detune updateChannelDetune(0); sendController(_currentChannel, CTRL_CHANNELDETUNE, 0); //Brightness updateBrightness(MIDFINEBRIGHTNESS); sendController(_currentChannel, CTRL_FINEBRIGHTNESS, MIDFINEBRIGHTNESS); //Attack updateAttack(MIDATTACK); sendController(_currentChannel, MusECore::CTRL_ATTACK_TIME, MIDATTACK); //Release updateRelease(MIDRELEASE); sendController(_currentChannel, MusECore::CTRL_RELEASE_TIME, MIDRELEASE); } //----------------------------------------------------------- // setNbrVoices //----------------------------------------------------------- void DeicsOnzeGui::setNbrVoices(int nv) { sendController(_currentChannel, CTRL_NBRVOICES, nv); } //---------------------------------------------------------- // setMidiInCh //---------------------------------------------------------- //void DeicsOnzeGui::setMidiInCh(int m) { // unsigned char message[4]; // message[0]=MUSE_SYNTH_SYSEX_MFG_ID; // message[1]=DEICSONZE_UNIQUE_ID; // message[2]=SYSEX_CHANNELNUM; // message[3]=(unsigned char)(m-1); // sendSysex(message, 4); //} //----------------------------------------------------------- // saveConfiguration //----------------------------------------------------------- void DeicsOnzeGui::saveConfiguration() { QString filename = QFileDialog::getSaveFileName( this, tr("Save configuration"), lastDir, QString("*.dco")); if(!filename.isEmpty()) { QFileInfo fi(filename); lastDir = fi.path(); if(!filename.endsWith(".dco")) filename+=".dco"; QFile f(filename); f.open(QIODevice::WriteOnly); AL::Xml* xml = new AL::Xml(&f); xml->header(); _deicsOnze->writeConfiguration(xml); f.close(); } } //----------------------------------------------------------- // saveDefaultConfiguration //----------------------------------------------------------- void DeicsOnzeGui::saveDefaultConfiguration() { QString filename = DEI_hostConfigPath + QString("/" DEICSONZESTR ".dco"); if(!filename.isEmpty()) { QFile f(filename); f.open(QIODevice::WriteOnly); AL::Xml* xml = new AL::Xml(&f); xml->header(); _deicsOnze->writeConfiguration(xml); f.close(); } } //----------------------------------------------------------- // loadConfiguration //----------------------------------------------------------- void DeicsOnzeGui::loadConfiguration(QString fileName) { // read the XML file and create DOM tree if(!fileName.isEmpty()) { QFile confFile(fileName); if(!confFile.open(QIODevice::ReadOnly)) { QMessageBox::critical(0, tr("Critical Error"), tr("Cannot open file %1").arg(fileName)); return; } QDomDocument domTree; if (!domTree.setContent(&confFile )) { QMessageBox::critical (0, tr("Critical Error"), tr("Parsing error for file %1").arg(fileName)); confFile.close(); return; } confFile.close(); QDomNode node = domTree.documentElement(); while (!node.isNull()) { QDomElement e = node.toElement(); if (e.isNull()) continue; if (e.tagName() == DEICSONZECONFIGURATIONSTR) { QString version = e.attribute(QString("version")); if (version == "1.0") { _deicsOnze->readConfiguration(node.firstChild()); } else printf("unsupported *.dco file version %s\n", version.toLatin1().constData()); } else printf("DeicsOnze: %s not supported\n", e.tagName().toLatin1().constData()); node = node.nextSibling(); } } } //----------------------------------------------------------- // loadConfiguration //----------------------------------------------------------- void DeicsOnzeGui::loadConfiguration() { QString fileName = QFileDialog::getOpenFileName( this, tr("Load category dialog"), lastDir, QString("*.dco")); QFileInfo fi(fileName); lastDir = fi.path(); loadConfiguration(fileName); } //----------------------------------------------------------- // setQuality //----------------------------------------------------------- void DeicsOnzeGui::setQuality(const QString& q) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_QUALITY; message[3]=(unsigned char)(q=="High"? high:(q=="Middle"? middle:(q=="Low"?low:ultralow))); sendSysex(message, 4); } //----------------------------------------------------------- // setFilter //----------------------------------------------------------- void DeicsOnzeGui::setFilter(bool f) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_FILTER; message[3]=(unsigned char)f; sendSysex(message, 4); } //----------------------------------------------------------- // setFontSize //----------------------------------------------------------- void DeicsOnzeGui::setFontSize(int fs) { applyFontSize(fs); unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_FONTSIZE; message[3]=(unsigned char)fs; sendSysex(message, 4); } //----------------------------------------------------------- // setSaveOnlyUsed //----------------------------------------------------------- void DeicsOnzeGui::setSaveOnlyUsed(bool sou) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_SAVEONLYUSED; message[3]=(unsigned char)sou; sendSysex(message, 4); updateSaveOnlyUsed(sou); } void DeicsOnzeGui::setSaveOnlyUsedComp(bool souc) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_SAVEONLYUSED; message[3]=(unsigned char)!souc; sendSysex(message, 4); updateSaveOnlyUsed(!souc); } //----------------------------------------------------------- // setSaveConfig //----------------------------------------------------------- void DeicsOnzeGui::setSaveConfig(bool ssc) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_SAVECONFIG; message[3]=(unsigned char)ssc; sendSysex(message, 4); } //----------------------------------------------------------- // setColor //----------------------------------------------------------- void DeicsOnzeGui::setRedColor(int r) { QListWidgetItem* i=colorListBox->selectedItems().at(0); if(i){ curColor->setRgb(r, curColor->green(), curColor->blue()); if(i->text()==QString("Text")) { tColor->setRgb(curColor->rgb()); setTextColor(reinterpret_cast(*curColor)); } else if(i->text()==QString("Background")) { bColor->setRgb(curColor->rgb()); setBackgroundColor(reinterpret_cast(*curColor)); } else if(i->text()==QString("Edit Text")) { etColor->setRgb(curColor->rgb()); setEditTextColor(reinterpret_cast(*curColor)); } else if(i->text()==QString("Edit Background")) { ebColor->setRgb(curColor->rgb()); setEditBackgroundColor(reinterpret_cast(*curColor)); } QPalette p = colorFrame->palette(); p.setColor(QPalette::Window, (reinterpret_cast(*curColor))); colorFrame->setPalette(p); } } void DeicsOnzeGui::setGreenColor(int g) { QListWidgetItem* i=colorListBox->selectedItems().at(0); if(i) { curColor->setRgb(curColor->red(), g, curColor->blue()); if(i->text()==QString("Text")) { tColor->setRgb(curColor->rgb()); setTextColor(reinterpret_cast(*curColor)); } else if(i->text()==QString("Background")) { bColor->setRgb(curColor->rgb()); setBackgroundColor(reinterpret_cast(*curColor)); } else if(i->text()==QString("Edit Text")) { etColor->setRgb(curColor->rgb()); setEditTextColor(reinterpret_cast(*curColor)); } else if(i->text()==QString("Edit Background")) { ebColor->setRgb(curColor->rgb()); setEditBackgroundColor(reinterpret_cast(*curColor)); } QPalette p = colorFrame->palette(); p.setColor(QPalette::Window, (reinterpret_cast(*curColor))); colorFrame->setPalette(p); } } void DeicsOnzeGui::setBlueColor(int b) { QListWidgetItem* i=colorListBox->selectedItems().at(0); if(i) { curColor->setRgb(curColor->red(), curColor->green(), b); if(i->text()==QString("Text")) { tColor->setRgb(curColor->rgb()); setTextColor(reinterpret_cast(*curColor)); } else if(i->text()==QString("Background")) { bColor->setRgb(curColor->rgb()); setBackgroundColor(reinterpret_cast(*curColor)); } else if(i->text()==QString("Edit Text")) { etColor->setRgb(curColor->rgb()); setEditTextColor(reinterpret_cast(*curColor)); } else if(i->text()==QString("Edit Background")) { ebColor->setRgb(curColor->rgb()); setEditBackgroundColor(reinterpret_cast(*curColor)); } QPalette p = colorFrame->palette(); p.setColor(QPalette::Window, (reinterpret_cast(*curColor))); colorFrame->setPalette(p); } } void DeicsOnzeGui::setRGBSliders(QListWidgetItem* i) { if(i->text()==QString("Text")) curColor->setRgb(tColor->red(), tColor->green(), tColor->blue()); else if(i->text()==QString("Background")) curColor->setRgb(bColor->red(), bColor->green(), bColor->blue()); else if(i->text()==QString("Edit Text")) curColor->setRgb(etColor->red(), etColor->green(), etColor->blue()); else if(i->text()==QString("Edit Background")) curColor->setRgb(ebColor->red(), ebColor->green(), ebColor->blue()); redSlider->blockSignals(true); redSlider->setValue(curColor->red()); redSlider->blockSignals(false); redSpinBox->blockSignals(true); redSpinBox->setValue(curColor->red()); redSpinBox->blockSignals(false); greenSlider->blockSignals(true); greenSlider->setValue(curColor->green()); greenSlider->blockSignals(false); greenSpinBox->blockSignals(true); greenSpinBox->setValue(curColor->green()); greenSpinBox->blockSignals(false); blueSlider->blockSignals(true); blueSlider->setValue(curColor->blue()); blueSlider->blockSignals(false); blueSpinBox->blockSignals(true); blueSpinBox->setValue(curColor->blue()); blueSpinBox->blockSignals(false); QPalette p = colorFrame->palette(); p.setColor(QPalette::Window, (reinterpret_cast(*curColor))); colorFrame->setPalette(p); } void DeicsOnzeGui::setTextColor(const QColor & c) { QPalette p = this->palette(); p.setColor(QPalette::WindowText, c); this->setPalette(p); channelCtrlGroupBox->setPalette(p); //channelPanGroupBox->setPalette(p); FeedbackGroupBox->setPalette(p); LFOGroupBox->setPalette(p); ModulationMatrixGroupBox->setPalette(p); FeedbackGroupBox->setPalette(p); pitchEnvGroupBox->setPalette(p); Frequency1groupBox->setPalette(p); OUT1groupBox->setPalette(p); Env1GroupBox->setPalette(p); Scaling1GroupBox->setPalette(p); DetWaveEGS1GroupBox->setPalette(p); sensitivity1groupBox->setPalette(p); Frequency2groupBox->setPalette(p); OUT2groupBox->setPalette(p); Env2GroupBox->setPalette(p); Scaling2GroupBox->setPalette(p); DetWaveEGS2GroupBox->setPalette(p); sensitivity2groupBox->setPalette(p); Frequency3groupBox->setPalette(p); OUT3groupBox->setPalette(p); Env3GroupBox->setPalette(p); Scaling3GroupBox->setPalette(p); DetWaveEGS3GroupBox->setPalette(p); sensitivity3groupBox->setPalette(p); Frequency4groupBox->setPalette(p); OUT4groupBox->setPalette(p); Env4GroupBox->setPalette(p); Scaling4GroupBox->setPalette(p); DetWaveEGS4GroupBox->setPalette(p); sensitivity4groupBox->setPalette(p); transposeGroupBox->setPalette(p); //detuneGroupBox->setPalette(p); //footSWGroupBox->setPalette(p); pitchBendRangeGroupBox->setPalette(p); //reverbGroupBox->setPalette(p); modeGroupBox->setPalette(p); portamentoGroupBox->setPalette(p); colorGroupBox->setPalette(p); pathGroupBox->setPalette(p); qualityGroupBox->setPalette(p); saveModeButtonGroup->setPalette(p); fileGroupBox->setPalette(p); onReverbGroupBox->setPalette(p); selectLadspaReverbGroupBox->setPalette(p); channelReverbGroupBox->setPalette(p); parametersReverbGroupBox->setPalette(p); onChorusGroupBox->setPalette(p); selectLadspaChorusGroupBox->setPalette(p); channelChorusGroupBox->setPalette(p); parametersChorusGroupBox->setPalette(p); fontSizeGroupBox->setPalette(p); delayBPMGroupBox->setPalette(p); delayBeatRatioGroupBox->setPalette(p); delayFeedbackGroupBox->setPalette(p); delayPanLFOGroupBox->setPalette(p); delayPanDepthGroupBox->setPalette(p); delayReturnGroupBox->setPalette(p); channelDelayGroupBox->setPalette(p); } void DeicsOnzeGui::setBackgroundColor(const QColor & c) { if(imageCheckBox->checkState()==Qt::Unchecked) { QPalette p = this->palette(); p.setColor(QPalette::Window, c); this->setPalette(p); } } void DeicsOnzeGui::setEditTextColor(const QColor & c) { QPalette p = this->palette(); p.setColor(QPalette::Text, c); this->setPalette(p); channelCtrlGroupBox->setPalette(p); //channelPanGroupBox->setPalette(p); FeedbackGroupBox->setPalette(p); LFOGroupBox->setPalette(p); ModulationMatrixGroupBox->setPalette(p); FeedbackGroupBox->setPalette(p); pitchEnvGroupBox->setPalette(p); Frequency1groupBox->setPalette(p); OUT1groupBox->setPalette(p); Env1GroupBox->setPalette(p); Scaling1GroupBox->setPalette(p); DetWaveEGS1GroupBox->setPalette(p); sensitivity1groupBox->setPalette(p); Frequency2groupBox->setPalette(p); OUT2groupBox->setPalette(p); Env2GroupBox->setPalette(p); Scaling2GroupBox->setPalette(p); DetWaveEGS2GroupBox->setPalette(p); sensitivity2groupBox->setPalette(p); Frequency3groupBox->setPalette(p); OUT3groupBox->setPalette(p); Env3GroupBox->setPalette(p); Scaling3GroupBox->setPalette(p); DetWaveEGS3GroupBox->setPalette(p); sensitivity3groupBox->setPalette(p); Frequency4groupBox->setPalette(p); OUT4groupBox->setPalette(p); Env4GroupBox->setPalette(p); Scaling4GroupBox->setPalette(p); DetWaveEGS4GroupBox->setPalette(p); sensitivity4groupBox->setPalette(p); transposeGroupBox->setPalette(p); //detuneGroupBox->setPalette(p); //footSWGroupBox->setPalette(p); pitchBendRangeGroupBox->setPalette(p); //reverbGroupBox->setPalette(p); modeGroupBox->setPalette(p); portamentoGroupBox->setPalette(p); colorGroupBox->setPalette(p); pathGroupBox->setPalette(p); qualityGroupBox->setPalette(p); saveModeButtonGroup->setPalette(p); fileGroupBox->setPalette(p); masterVolKnob->setScaleValueColor(c); channelVolumeKnob->setScaleValueColor(c); channelPanKnob->setScaleValueColor(c); brightnessKnob->setScaleValueColor(c); modulationKnob->setScaleValueColor(c); detuneKnob->setScaleValueColor(c); attackKnob->setScaleValueColor(c); releaseKnob->setScaleValueColor(c); channelChorusGroupBox->setPalette(p); selectLadspaChorusGroupBox->setPalette(p); parametersChorusGroupBox->setPalette(p); for(int i=0; i < (int)_chorusSliderVector.size(); i++) if(_chorusSliderVector[i]) _chorusSliderVector[i]->setScaleValueColor(c); channelReverbGroupBox->setPalette(p); selectLadspaReverbGroupBox->setPalette(p); parametersReverbGroupBox->setPalette(p); for(int i=0; i < (int)_reverbSliderVector.size(); i++) if(_reverbSliderVector[i]) _reverbSliderVector[i]->setScaleValueColor(c); channelDelayGroupBox->setPalette(p); delayBPMKnob->setScaleValueColor(c); delayBPMGroupBox->setPalette(p); delayBeatRatioKnob->setScaleValueColor(c); delayBeatRatioGroupBox->setPalette(p); delayFeedbackKnob->setScaleValueColor(c); delayFeedbackGroupBox->setPalette(p); delayPanLFOFreqKnob->setScaleValueColor(c); delayPanLFOGroupBox->setPalette(p); delayPanLFODepthKnob->setScaleValueColor(c); delayPanDepthGroupBox->setPalette(p); fontSizeGroupBox->setPalette(p); } void DeicsOnzeGui::setEditBackgroundColor(const QColor & c) { QPalette p = this->palette(); p.setColor(QPalette::Base, c); this->setPalette(p); channelCtrlGroupBox->setPalette(p); //channelPanGroupBox->setPalette(p); FeedbackGroupBox->setPalette(p); LFOGroupBox->setPalette(p); ModulationMatrixGroupBox->setPalette(p); FeedbackGroupBox->setPalette(p); pitchEnvGroupBox->setPalette(p); Frequency1groupBox->setPalette(p); OUT1groupBox->setPalette(p); Env1GroupBox->setPalette(p); Scaling1GroupBox->setPalette(p); DetWaveEGS1GroupBox->setPalette(p); sensitivity1groupBox->setPalette(p); Frequency2groupBox->setPalette(p); OUT2groupBox->setPalette(p); Env2GroupBox->setPalette(p); Scaling2GroupBox->setPalette(p); DetWaveEGS2GroupBox->setPalette(p); sensitivity2groupBox->setPalette(p); Frequency3groupBox->setPalette(p); OUT3groupBox->setPalette(p); Env3GroupBox->setPalette(p); Scaling3GroupBox->setPalette(p); DetWaveEGS3GroupBox->setPalette(p); sensitivity3groupBox->setPalette(p); Frequency4groupBox->setPalette(p); OUT4groupBox->setPalette(p); Env4GroupBox->setPalette(p); Scaling4GroupBox->setPalette(p); DetWaveEGS4GroupBox->setPalette(p); sensitivity4groupBox->setPalette(p); transposeGroupBox->setPalette(p); //detuneGroupBox->setPalette(p); //footSWGroupBox->setPalette(p); pitchBendRangeGroupBox->setPalette(p); //reverbGroupBox->setPalette(p); modeGroupBox->setPalette(p); portamentoGroupBox->setPalette(p); colorGroupBox->setPalette(p); pathGroupBox->setPalette(p); qualityGroupBox->setPalette(p); saveModeButtonGroup->setPalette(p); fileGroupBox->setPalette(p); p = pitchEnvFrame->palette(); p.setColor(QPalette::Window, c); pitchEnvFrame->setPalette(p); p = envelope1Frame->palette(); p.setColor(QPalette::Window, c); envelope1Frame->setPalette(p); p = envelope2Frame->palette(); p.setColor(QPalette::Window, c); envelope2Frame->setPalette(p); p = envelope3Frame->palette(); p.setColor(QPalette::Window, c); envelope3Frame->setPalette(p); p = envelope4Frame->palette(); p.setColor(QPalette::Window, c); envelope4Frame->setPalette(p); masterVolKnob->setScaleColor(c); channelVolumeKnob->setScaleColor(c); channelPanKnob->setScaleColor(c); brightnessKnob->setScaleColor(c); modulationKnob->setScaleColor(c); detuneKnob->setScaleColor(c); attackKnob->setScaleColor(c); releaseKnob->setScaleColor(c); channelChorusGroupBox->setPalette(p); selectLadspaChorusGroupBox->setPalette(p); parametersChorusGroupBox->setPalette(p); for(int i=0; i < (int)_chorusSliderVector.size(); i++) if(_chorusSliderVector[i]) _chorusSliderVector[i]->setScaleColor(c); channelReverbGroupBox->setPalette(p); selectLadspaReverbGroupBox->setPalette(p); parametersReverbGroupBox->setPalette(p); for(int i=0; i < (int)_reverbSliderVector.size(); i++) if(_reverbSliderVector[i]) _reverbSliderVector[i]->setScaleColor(c); channelDelayGroupBox->setPalette(p); delayBPMKnob->setScaleColor(c); delayBPMGroupBox->setPalette(p); delayBeatRatioKnob->setScaleColor(c); delayBeatRatioGroupBox->setPalette(p); delayFeedbackKnob->setScaleColor(c); delayFeedbackGroupBox->setPalette(p); delayPanLFOFreqKnob->setScaleColor(c); delayPanLFOGroupBox->setPalette(p); delayPanLFODepthKnob->setScaleColor(c); delayPanDepthGroupBox->setPalette(p); fontSizeGroupBox->setPalette(p); } //----------------------------------------------------------- // QFramePitchEnvelope //----------------------------------------------------------- void QFramePitchEnvelope::paintEvent(QPaintEvent* /*e*/) { QPainter paint(this); QPen pen; pen.setColor(*(_deicsOnzeGui->etColor)); //if the size of pitchEnvFrame is different than QFramePitchEnvelope resize //and update the envelope if(_deicsOnzeGui->pitchEnvFrame->width()!=width() || _deicsOnzeGui->pitchEnvFrame->height()!=height()) { resize(_deicsOnzeGui->pitchEnvFrame->width(), _deicsOnzeGui->pitchEnvFrame->height()); //update the positions of the envelope PitchEg* pe = &_deicsOnzeGui->_deicsOnze ->_preset[_deicsOnzeGui->_currentChannel]->pitchEg; env2Points(pe->pl1, pe->pl2, pe->pl3, pe->pr1, pe->pr2, pe->pr3); } //Draw the verticale line on the release time pen.setWidth(1); pen.setStyle(Qt::DotLine); paint.setPen(pen); paint.drawLine(P2linkP3.x(), height(), P2linkP3.x(), 0); //Draw the horisontal line for the center of the pitch pen.setStyle(Qt::DashDotLine); paint.setPen(pen); paint.drawLine(0, height()/2-DOTWIDTH/2, width(), height()/2-DOTWIDTH/2); //Draw the pitch envelope pen.setWidth(PENWIDTH); pen.setStyle(Qt::SolidLine); paint.setPen(pen); paint.drawRect(startlinkP1.x()-DOTWIDTH/2, startlinkP1.y()-DOTWIDTH/2, DOTWIDTH, DOTWIDTH); paint.drawRect(P1linkP2.x()-DOTWIDTH/2, P1linkP2.y()-DOTWIDTH/2, DOTWIDTH, DOTWIDTH); paint.drawRect(P2linkP3.x()-DOTWIDTH/2, P2linkP3.y()-DOTWIDTH/2, DOTWIDTH, DOTWIDTH); paint.drawRect(P3linkEnd.x()-DOTWIDTH/2, P3linkEnd.y()-DOTWIDTH/2, DOTWIDTH, DOTWIDTH); paint.drawLine(startlinkP1,P1linkP2); paint.drawLine(P1linkP2, P2linkP3); paint.drawLine(P2linkP3, P3linkEnd); } void QFramePitchEnvelope::mousePressEvent(QMouseEvent * e) { //startlinkP1 if(e->x()x()>startlinkP1.x()-DRAGWIDTH && e->y()y()>startlinkP1.y()-DRAGWIDTH) isStartlinkP1Edit=true; //P1linkP2 if(e->x()x()>P1linkP2.x()-DRAGWIDTH && e->y()y()>P1linkP2.y()-DRAGWIDTH) isP1linkP2Edit=true; //P2linkP3 if(e->x()x()>P2linkP3.x()-DRAGWIDTH && e->y()y()>P2linkP3.y()-DRAGWIDTH) isP2linkP3Edit=true; //P3linkEnd if(e->x()x()>P3linkEnd.x()-DRAGWIDTH && e->y()y()>P3linkEnd.y()-DRAGWIDTH) isP3linkEndEdit=true; } void QFramePitchEnvelope::mouseReleaseEvent(QMouseEvent* /*e*/) { isStartlinkP1Edit=false; isP1linkP2Edit=false; isP2linkP3Edit=false; isP3linkEndEdit=false; } void QFramePitchEnvelope::mouseMoveEvent(QMouseEvent* e) { if(isStartlinkP1Edit) { if(e->y()>startlinkP1.y()) _deicsOnzeGui->PL1SpinBox->stepDown(); if(e->y()PL1SpinBox->stepUp(); } if(isP1linkP2Edit) { if(e->x()>P1linkP2.x()) _deicsOnzeGui->PR1SpinBox->stepDown(); if(e->x()PR1SpinBox->stepUp(); if(e->y()>P1linkP2.y()) _deicsOnzeGui->PL2SpinBox->stepDown(); if(e->y()PL2SpinBox->stepUp(); } if(isP2linkP3Edit) { if(e->x()>P2linkP3.x()) _deicsOnzeGui->PR2SpinBox->stepDown(); if(e->x()PR2SpinBox->stepUp(); if(e->y()>P2linkP3.y()) _deicsOnzeGui->PL3SpinBox->stepDown(); if(e->y()PL3SpinBox->stepUp(); } if(isP3linkEndEdit) { if(e->x()>P3linkEnd.x()) _deicsOnzeGui->PR3SpinBox->stepDown(); if(e->x()PR3SpinBox->stepUp(); if(e->y()>P3linkEnd.y()) _deicsOnzeGui->PL1SpinBox->stepDown(); if(e->y()PL1SpinBox->stepUp(); } } //----------------------------------------------------------- // env2Points // assigns the right coordinates to the points // to draw the pitch envelope //----------------------------------------------------------- void QFramePitchEnvelope::env2Points(int pl1, int pl2, int pl3, int pr1, int pr2, int pr3) { startlinkP1.setY(YOFFSET+MAXPHEIGHT -PENWIDTH-((PL1HEIGHT-PENWIDTH)*pl1)/MAXPL); startlinkP1.setX(XOFFSET); P1linkP2.setY(YOFFSET+MAXPHEIGHT-PENWIDTH-((PL2HEIGHT-PENWIDTH)*pl2)/MAXPL); P1linkP2.setX(XOFFSET+WALLWIDTH+PR1WIDTH-(PR1WIDTH*pr1)/MAXPR); P2linkP3.setY(YOFFSET+MAXPHEIGHT-PENWIDTH-((PL3HEIGHT-PENWIDTH)*pl3)/MAXPL); P2linkP3.setX(P1linkP2.x()+WALLWIDTH+PR2WIDTH-(PR2WIDTH*pr2)/MAXPR); P3linkEnd.setY(YOFFSET+MAXPHEIGHT-PENWIDTH-((PL1HEIGHT-PENWIDTH)*pl1)/MAXPL); P3linkEnd.setX(P2linkP3.x()+WALLWIDTH+PR3WIDTH-(PR3WIDTH*pr3)/MAXPR); } //----------------------------------------------------------- // QFrameEnvelope //----------------------------------------------------------- void QFrameEnvelope::paintEvent(QPaintEvent* /*e*/) { QPainter paint(this); QPen pen; pen.setColor(*(_deicsOnzeGui->etColor)); //if the size of pitchEnvFrame is different than QFramePitchEnvelope resize //and update the envelope int op = _deicsOnzeGui->deicsOnzeTabWidget->currentIndex()-2; //-2 because of the presetsTab and globalTab Eg* eg=&(_deicsOnzeGui->_deicsOnze-> _preset[_deicsOnzeGui->_currentChannel]->eg[op]); switch(op) { case 0 : if(_deicsOnzeGui->envelope1Frame->width()!=width() || _deicsOnzeGui->envelope1Frame->height()!=height()) { resize(_deicsOnzeGui->envelope1Frame->width(), _deicsOnzeGui->envelope1Frame->height()); //update the positions of the envelope env2Points(eg->ar, eg->d1r, eg->d1l, eg->d2r, eg->rr); } break; case 1 : if(_deicsOnzeGui->envelope2Frame->width()!=width() || _deicsOnzeGui->envelope2Frame->height()!=height()) { resize(_deicsOnzeGui->envelope2Frame->width(), _deicsOnzeGui->envelope2Frame->height()); //update the positions of the envelope env2Points(eg->ar, eg->d1r, eg->d1l, eg->d2r, eg->rr); } break; case 2 : if(_deicsOnzeGui->envelope3Frame->width()!=width() || _deicsOnzeGui->envelope3Frame->height()!=height()) { resize(_deicsOnzeGui->envelope3Frame->width(), _deicsOnzeGui->envelope3Frame->height()); //update the positions of the envelope env2Points(eg->ar, eg->d1r, eg->d1l, eg->d2r, eg->rr); } break; case 3 : if(_deicsOnzeGui->envelope4Frame->width()!=width() || _deicsOnzeGui->envelope4Frame->height()!=height()) { resize(_deicsOnzeGui->envelope4Frame->width(), _deicsOnzeGui->envelope4Frame->height()); //update the positions of the envelope env2Points(eg->ar, eg->d1r, eg->d1l, eg->d2r, eg->rr); } break; default : printf("QFrameEnvelope::paintEvent switch case error\n"); break; } //Draw the vertical line of the release note pen.setWidth(1); pen.setStyle(Qt::DotLine); paint.setPen(pen); paint.drawLine(D2linkRR.x(), MAXHEIGHT, D2linkRR.x(), YOFFSET); //Draw the volume envelope pen.setWidth(PENWIDTH); pen.setStyle(Qt::SolidLine); paint.setPen(pen); paint.drawRect(startlinkAR.x()-DOTWIDTH/2, startlinkAR.y()-DOTWIDTH/2, DOTWIDTH, DOTWIDTH); paint.drawRect(ARlinkD1.x()-DOTWIDTH/2, ARlinkD1.y()-DOTWIDTH/2, DOTWIDTH, DOTWIDTH); paint.drawRect(D1linkD2.x()-DOTWIDTH/2, D1linkD2.y()-DOTWIDTH/2, DOTWIDTH, DOTWIDTH); paint.drawRect(D2linkRR.x()-DOTWIDTH/2, D2linkRR.y()-DOTWIDTH/2, DOTWIDTH, DOTWIDTH); paint.drawRect(RRlinkEnd.x()-DOTWIDTH/2, RRlinkEnd.y()-DOTWIDTH/2, DOTWIDTH, DOTWIDTH); paint.drawLine(startlinkAR,ARlinkD1); paint.drawLine(ARlinkD1, D1linkD2); paint.drawLine(D1linkD2, D2linkRR); paint.drawLine(D2linkRR, RRlinkEnd); } void QFrameEnvelope::mousePressEvent(QMouseEvent * e) { //ARlinkD1 if(e->x()x()>ARlinkD1.x()-DRAGWIDTH && e->y()y()>ARlinkD1.y()-DRAGWIDTH) isARlinkD1Edit=true; //D1linkD2 if(e->x()x()>D1linkD2.x()-DRAGWIDTH && e->y()y()>D1linkD2.y()-DRAGWIDTH) isD1linkD2Edit=true; //D2linkRR if(e->x()x()>D2linkRR.x()-DRAGWIDTH && e->y()y()>D2linkRR.y()-DRAGWIDTH) isD2linkRREdit=true; //RRlinkEnd if(e->x()x()>RRlinkEnd.x()-DRAGWIDTH && e->y()y()>RRlinkEnd.y()-DRAGWIDTH) isRRlinkEndEdit=true; } void QFrameEnvelope::mouseReleaseEvent(QMouseEvent* /*e*/) { isARlinkD1Edit=false; isD1linkD2Edit=false; isD2linkRREdit=false; isRRlinkEndEdit=false; } void QFrameEnvelope::mouseMoveEvent(QMouseEvent* e) { if(isARlinkD1Edit) { switch(op) { case 0 : if(e->x()>ARlinkD1.x()) _deicsOnzeGui->AR1SpinBox->stepDown(); if(e->x()AR1SpinBox->stepUp(); break; case 1 : if(e->x()>ARlinkD1.x()) _deicsOnzeGui->AR2SpinBox->stepDown(); if(e->x()AR2SpinBox->stepUp(); break; case 2 : if(e->x()>ARlinkD1.x()) _deicsOnzeGui->AR3SpinBox->stepDown(); if(e->x()AR3SpinBox->stepUp(); break; case 3 : if(e->x()>ARlinkD1.x()) _deicsOnzeGui->AR4SpinBox->stepDown(); if(e->x()AR4SpinBox->stepUp(); break; default : break; } } if(isD1linkD2Edit) { switch(op) { case 0 : if(e->x()>D1linkD2.x()) _deicsOnzeGui->D1R1SpinBox->stepDown(); if(e->x()D1R1SpinBox->stepUp(); if(e->y()>D1linkD2.y()) _deicsOnzeGui->D1L1SpinBox->stepDown(); if(e->y()D1L1SpinBox->stepUp(); break; case 1 : if(e->x()>D1linkD2.x()) _deicsOnzeGui->D1R2SpinBox->stepDown(); if(e->x()D1R2SpinBox->stepUp(); if(e->y()>D1linkD2.y()) _deicsOnzeGui->D1L2SpinBox->stepDown(); if(e->y()D1L2SpinBox->stepUp(); break; case 2 : if(e->x()>D1linkD2.x()) _deicsOnzeGui->D1R3SpinBox->stepDown(); if(e->x()D1R3SpinBox->stepUp(); if(e->y()>D1linkD2.y()) _deicsOnzeGui->D1L3SpinBox->stepDown(); if(e->y()D1L3SpinBox->stepUp(); break; case 3 : if(e->x()>D1linkD2.x()) _deicsOnzeGui->D1R4SpinBox->stepDown(); if(e->x()D1R4SpinBox->stepUp(); if(e->y()>D1linkD2.y()) _deicsOnzeGui->D1L4SpinBox->stepDown(); if(e->y()D1L4SpinBox->stepUp(); break; default : break; } } if(isD2linkRREdit) { switch(op) { case 0 : if(e->x()>D2linkRR.x() /*&& e->y()D2R1SpinBox->stepDown(); if(e->x()y()>D2linkRR.y()*/) _deicsOnzeGui->D2R1SpinBox->stepUp(); break; case 1 : if(e->x()>D2linkRR.x() /*&& e->y()D2R2SpinBox->stepDown(); if(e->x()y()>D2linkRR.y()*/) _deicsOnzeGui->D2R2SpinBox->stepUp(); break; case 2 : if(e->x()>D2linkRR.x() /*&& e->y()D2R3SpinBox->stepDown(); if(e->x()y()>D2linkRR.y()*/) _deicsOnzeGui->D2R3SpinBox->stepUp(); break; case 3 : if(e->x()>D2linkRR.x() /*&& e->y()D2R4SpinBox->stepDown(); if(e->x()y()>D2linkRR.y()*/) _deicsOnzeGui->D2R4SpinBox->stepUp(); break; default : break; } } if(isRRlinkEndEdit) { switch(op) { case 0 : if(e->x()>RRlinkEnd.x()) _deicsOnzeGui->RR1SpinBox->stepDown(); if(e->x()RR1SpinBox->stepUp(); break; case 1 : if(e->x()>RRlinkEnd.x()) _deicsOnzeGui->RR2SpinBox->stepDown(); if(e->x()RR2SpinBox->stepUp(); break; case 2 : if(e->x()>RRlinkEnd.x()) _deicsOnzeGui->RR3SpinBox->stepDown(); if(e->x()RR3SpinBox->stepUp(); break; case 3 : if(e->x()>RRlinkEnd.x()) _deicsOnzeGui->RR4SpinBox->stepDown(); if(e->x()RR4SpinBox->stepUp(); break; default : break; } } } //----------------------------------------------------------- // env2Points // assigns the right coordinates to the points // to draw the envelope //----------------------------------------------------------- void QFrameEnvelope::env2Points(int ar, int d1r, int d1l, int d2r, int rr) { startlinkAR.setY(MAXHEIGHT-PENWIDTH); startlinkAR.setX(PENWIDTH); ARlinkD1.setY(PENWIDTH); ARlinkD1.setX(PENWIDTH+ARWIDTH-(ARWIDTH*ar)/MAXAR); D1linkD2.setY(PENWIDTH+ (D1LHEIGHT-2*PENWIDTH-((D1LHEIGHT-2*PENWIDTH)*d1l)/MAXD1L)); D1linkD2.setX(ARlinkD1.x()+D1RWIDTH-(D1RWIDTH*d1r)/MAXD1R); D2linkRR.setY(D1linkD2.y() +((D1LHEIGHT-2*PENWIDTH-D1linkD2.y())*d2r)/MAXD2R); D2linkRR.setX(D1linkD2.x()+D2RWIDTH-(D2RWIDTH*d2r)/MAXD2R); RRlinkEnd.setY(MAXHEIGHT-PENWIDTH); RRlinkEnd.setX(D2linkRR.x() +(RRWIDTH-PENWIDTH-((RRWIDTH-PENWIDTH)*rr)/MAXRR)); } //----------------------------------------------------------- // processEvent(const MidiEvent&); //----------------------------------------------------------- void DeicsOnzeGui::processEvent(const MusECore::MidiPlayEvent& ev) { //Controller if (ev.type() == MusECore::ME_CONTROLLER) { //printf("MusECore::ME_CONTROLLER\n"); int id=ev.dataA(); int ch=ev.channel(); int val=ev.dataB(); if(ch == _currentChannel || id == CTRL_CHANNELENABLE) { switch(id) { case CTRL_AR: updateAR(0, val); break; case CTRL_D1R: updateD1R(0, val); break; case CTRL_D2R: updateD2R(0, val); break; case CTRL_RR: updateRR(0, val); break; case CTRL_D1L: updateD1L(0, val); break; case CTRL_LS: updateLS(0, val); break; case CTRL_RS: updateRS(0, val); break; case CTRL_EBS: updateEBS(0, val); break; case CTRL_AME: updateAME(0, val==1); break; case CTRL_KVS: updateKVS(0, val); break; case CTRL_OUT: updateOUT(0, val); break; case CTRL_RATIO: updateRATIO(0, val); break; case CTRL_DET: updateDET(0, val); break; case CTRL_AR+DECAPAR1: updateAR(1, val); break; case CTRL_D1R+DECAPAR1: updateD1R(1, val); break; case CTRL_D2R+DECAPAR1: updateD2R(1, val); break; case CTRL_RR+DECAPAR1: updateRR(1, val); break; case CTRL_D1L+DECAPAR1: updateD1L(1, val); break; case CTRL_LS+DECAPAR1: updateLS(1, val); break; case CTRL_RS+DECAPAR1: updateRS(1, val); break; case CTRL_EBS+DECAPAR1: updateEBS(1, val); break; case CTRL_AME+DECAPAR1: updateAME(1, val==1); break; case CTRL_KVS+DECAPAR1: updateKVS(1, val); break; case CTRL_OUT+DECAPAR1: updateOUT(1, val); break; case CTRL_RATIO+DECAPAR1: updateRATIO(1, val); break; case CTRL_DET+DECAPAR1: updateDET(1, val); break; case CTRL_AR+2*DECAPAR1: updateAR(2, val); break; case CTRL_D1R+2*DECAPAR1: updateD1R(2, val); break; case CTRL_D2R+2*DECAPAR1: updateD2R(2, val); break; case CTRL_RR+2*DECAPAR1: updateRR(2, val); break; case CTRL_D1L+2*DECAPAR1: updateD1L(2, val); break; case CTRL_LS+2*DECAPAR1: updateLS(2, val); break; case CTRL_RS+2*DECAPAR1: updateRS(2, val); break; case CTRL_EBS+2*DECAPAR1: updateEBS(2, val); break; case CTRL_AME+2*DECAPAR1: updateAME(2, val==1); break; case CTRL_KVS+2*DECAPAR1: updateKVS(2, val); break; case CTRL_OUT+2*DECAPAR1: updateOUT(2, val); break; case CTRL_RATIO+2*DECAPAR1: updateRATIO(2, val); break; case CTRL_DET+2*DECAPAR1: updateDET(2, val); break; case CTRL_AR+3*DECAPAR1: updateAR(3, val); break; case CTRL_D1R+3*DECAPAR1: updateD1R(3, val); break; case CTRL_D2R+3*DECAPAR1: updateD2R(3, val); break; case CTRL_RR+3*DECAPAR1: updateRR(3, val); break; case CTRL_D1L+3*DECAPAR1: updateD1L(3, val); break; case CTRL_LS+3*DECAPAR1: updateLS(3, val); break; case CTRL_RS+3*DECAPAR1: updateRS(3, val); break; case CTRL_EBS+3*DECAPAR1: updateEBS(3, val); break; case CTRL_AME+3*DECAPAR1: updateAME(3, val==1); break; case CTRL_KVS+3*DECAPAR1: updateKVS(3, val); break; case CTRL_OUT+3*DECAPAR1: updateOUT(3, val); break; case CTRL_RATIO+3*DECAPAR1: updateRATIO(3, val); break; case CTRL_DET+3*DECAPAR1: updateDET(3, val); break; case CTRL_ALG: updateALG(val); break; case CTRL_FEEDBACK: updateFEEDBACK(val); break; case CTRL_SPEED: updateSPEED(val); break; case CTRL_DELAY: updateDELAY(val); break; case CTRL_PMODDEPTH: updatePMODDEPTH(val); break; case CTRL_AMODDEPTH: updateAMODDEPTH(val); break; case CTRL_SYNC: updateSYNC(val==1); break; case CTRL_WAVE: updateWAVE(val); break; case CTRL_PMODSENS: updatePMODSENS(val); break; case CTRL_AMS: updateAMS(val); break; case CTRL_TRANSPOSE: updateTRANSPOSE(val); break; case CTRL_POLYMODE: updatePOLYMODE(val); break; case CTRL_PBENDRANGE: updatePBENDRANGE(val); break; case CTRL_PORTAMODE: updatePORTAMODE(val); break; case CTRL_PORTATIME: updatePORTATIME(val); break; case CTRL_FCVOLUME: updateFcVolume(val); break; case CTRL_FSW: break; case CTRL_MWPITCH: updateMwPitch(val); break; case CTRL_MWAMPLITUDE: updateMwAmplitude(val); break; case CTRL_BCPITCH: updateBcPitch(val); break; case CTRL_BCAMPLITUDE: updateBcAmplitude(val); break; case CTRL_BCPITCHBIAS: updateBcPitchBias(val); break; case CTRL_BCEGBIAS: updateBcEgBias(val); break; case CTRL_PR1: updatePR1(val); break; case CTRL_PR2: updatePR2(val); break; case CTRL_PR3: updatePR3(val); break; case CTRL_PL1: updatePL1(val); break; case CTRL_PL2: updatePL2(val); break; case CTRL_PL3: updatePL3(val); break; case CTRL_FIX: updateFIX(0, val==1); break; case CTRL_FIXRANGE: updateFIXRANGE(0, val); break; case CTRL_OSW: updateOSW(0, val); break; case CTRL_SHFT: updateSHFT(0, val); break; case CTRL_FIX+DECAPAR2: updateFIX(1, val==1); break; case CTRL_FIXRANGE+DECAPAR2: updateFIXRANGE(1, val); break; case CTRL_OSW+DECAPAR2: updateOSW(1, val); break; case CTRL_SHFT+DECAPAR2: updateSHFT(1, val); break; case CTRL_FIX+2*DECAPAR2: updateFIX(2, val==1); break; case CTRL_FIXRANGE+2*DECAPAR2: updateFIXRANGE(2, val); break; case CTRL_OSW+2*DECAPAR2: updateOSW(2, val); break; case CTRL_SHFT+2*DECAPAR2: updateSHFT(2, val); break; case CTRL_FIX+3*DECAPAR2: updateFIX(3, val==1); break; case CTRL_FIXRANGE+3*DECAPAR2: updateFIXRANGE(3, val); break; case CTRL_OSW+3*DECAPAR2: updateOSW(3, val); break; case CTRL_SHFT+3*DECAPAR2: updateSHFT(3, val); break; case CTRL_REVERBRATE: /*updateReverbRate(val);*/ break; case CTRL_FCPITCH: updateFcPitch(val); break; case CTRL_FCAMPLITUDE: updateFcAmplitude(val); break; case CTRL_CHANNELENABLE: if(ch == _currentChannel) updateChannelEnable(val); break; case CTRL_CHANNELDETUNE: updateChannelDetune(val); break; case CTRL_CHANNELPAN: updateChannelPan(val); break; case CTRL_CHANNELVOLUME: updateChannelVolume(val); break; case CTRL_NBRVOICES: updateNbrVoices(val); break; case CTRL_FINEBRIGHTNESS: updateBrightness(val); break; case MusECore::CTRL_ATTACK_TIME: updateAttack(val); break; case MusECore::CTRL_RELEASE_TIME: updateRelease(val); break; case MusECore::CTRL_CHORUS_SEND: updateChannelChorus(val); break; case MusECore::CTRL_REVERB_SEND: updateChannelReverb(val); break; case MusECore::CTRL_VARIATION_SEND: updateChannelDelay(val); break; case MusECore::CTRL_MODULATION: updateModulation(val); break; case MusECore::CTRL_PROGRAM : int hbank = (val & 0xff0000) >> 16; int lbank = (val & 0xff00) >> 8; if (hbank > 127) // map "dont care" to 0 hbank = 0; if (lbank > 127) lbank = 0; int prog = val & 0x7f; //printf("GUI program : ch = %d, hbank = %d, lbank = %d, prog = %d\n", // ch, hbank, lbank, prog); //change the _deicsonze preset //to update the right preset _deicsOnze->programSelect(ch, hbank, lbank, prog); //only display _deicsonze preset updateSelectPreset(hbank, lbank, prog); updatePreset(); break; } } } // Sysexes else if (ev.type() == MusECore::ME_SYSEX) { //printf("MusECore::ME_SYSEX\n"); const unsigned char* data = ev.constData(); const int cmd = *data; float f; switch (cmd) { case SYSEX_CHORUSACTIV : updateChorusActiv((bool)data[1]); break; case SYSEX_CHORUSRETURN : updateChorusReturn((int)data[1]); break; case SYSEX_REVERBACTIV : updateReverbActiv((bool)data[1]); break; case SYSEX_REVERBRETURN : updateReverbReturn((int)data[1]); break; /*case SYSEX_CHORUS1PAN : updatePanChorus1((int)data[1]); break; case SYSEX_CHORUS1LFOFREQ : updateLFOFreqChorus1((int)data[1]); break; case SYSEX_CHORUS1DEPTH : updateDepthChorus1((int)data[1]); break; case SYSEX_CHORUS2PAN : updatePanChorus2((int)data[1]); break; case SYSEX_CHORUS2LFOFREQ : updateLFOFreqChorus2((int)data[1]); break; case SYSEX_CHORUS2DEPTH : updateDepthChorus2((int)data[1]); break;*/ case SYSEX_DELAYACTIV : updateDelayActiv((bool)data[1]); break; case SYSEX_DELAYRETURN : updateDelayReturn((int)data[1]); break; case SYSEX_DELAYBPM : memcpy(&f, &data[1], sizeof(float)); updateDelayBPM(f); break; case SYSEX_DELAYBEATRATIO : memcpy(&f, &data[1], sizeof(float)); updateDelayBeatRatio(f); break; case SYSEX_DELAYFEEDBACK : memcpy(&f, &data[1], sizeof(float)); updateDelayFeedback(f); break; case SYSEX_DELAYLFOFREQ : memcpy(&f, &data[1], sizeof(float)); updateDelayPanLFOFreq(f); break; case SYSEX_DELAYLFODEPTH : memcpy(&f, &data[1], sizeof(float)); updateDelayPanLFODepth(f); break; case SYSEX_DELAYWETDRYMIX : memcpy(&f, &data[1], sizeof(float)); //updateDelayPanDryWet(f); break; case SYSEX_QUALITY : updateQuality((int)data[1]); break; case SYSEX_FILTER : updateFilter((bool)data[1]); break; case SYSEX_FONTSIZE : updateFontSize((int)data[1]); applyFontSize((int)data[1]); break; case SYSEX_MASTERVOL : updateMasterVolume((int)data[1]); break; case SYSEX_SAVECONFIG : updateSaveConfig((bool)data[1]); break; case SYSEX_SAVEONLYUSED : updateSaveOnlyUsed((bool)data[1]); break; case SYSEX_COLORGUI : tColor->setRgb(data[1], data[2], data[3]); bColor->setRgb(data[4], data[5], data[6]); etColor->setRgb(data[7], data[8], data[9]); ebColor->setRgb(data[10], data[11], data[12]); setTextColor(reinterpret_cast(*tColor)); setBackgroundColor(reinterpret_cast(*bColor)); setEditTextColor(reinterpret_cast(*etColor)); setEditBackgroundColor(reinterpret_cast(*ebColor)); setRGBSliders(colorListBox->currentItem()); break; case SYSEX_ISINITSET : updateInitSetCheckBox((bool)data[1]); break; case SYSEX_INITSETPATH : updateInitSetPath(QString((char*)&data[1])); break; case SYSEX_ISBACKGROUNDPIX : updateBackgroundPixCheckBox((bool)data[1]); if((bool)data[1]) applyBackgroundPix(); break; case SYSEX_BACKGROUNDPIXPATH : updateBackgroundPixPath(QString((char*)&data[1])); break; case SYSEX_UPDATESETGUI : setSet(); subcategoryListView->clear(); presetListView->clear(); updateCategoryName("NONE", false); hbankSpinBox->setEnabled(false); updatePreset(); updateSubcategoryName("NONE", false); progSpinBox->setEnabled(false); updatePresetName("INITVOICE", false); break; case SYSEX_BUILDGUIREVERB : buildGuiReverb(); break; case SYSEX_BUILDGUICHORUS : buildGuiChorus(); break; case SYSEX_LOADSET : //printf("LoadSet\n"); // read the XML file and create DOM tree /*QString filename = (const char*) (data+2); QFile deicsonzeFile(filename); deicsonzeFile.open(IO_ReadOnly); QDomDocument domTree; domTree.setContent(&deicsonzeFile); deicsonzeFile.close(); QDomNode node = domTree.documentElement(); printf("After XML\n"); while (!node.isNull()) { QDomElement e = node.toElement(); if (e.isNull()) continue; if (e.tagName() == "deicsOnzeSet") { QString version = e.attribute(QString("version")); if (version == "1.0") { _deicsOnze->_preset=_deicsOnze->_initialPreset;*/ //read the set if((bool)data[1]) { //printf("Mini\n"); updateSaveOnlyUsed(true); } else { //printf("Huge\n"); //while(!_deicsOnze->_set->_categoryVector.empty()) // delete(*_deicsOnze->_set->_categoryVector.begin()); updateSaveOnlyUsed(false); } //_deicsOnze->_set->readSet(node.firstChild()); //display load preset setSet(); /*} else printf("Wrong set version : %s\n", version.toLatin1().constData()); } node = node.nextSibling(); } break; // delete the temporary file created QString rmfile; rmfile="rm "; rmfile+=filename; system(rmfile); printf("Finit\n");*/ } } } /*! \fn SimpleSynthGui::readMessage() */ void DeicsOnzeGui::readMessage() { MessGui::readMessage(); } //----------------------------------------------------------- // num3Digits(int n) //----------------------------------------------------------- QString DeicsOnzeGui::num3Digits(int n) { QString sn=QString::number(n); return(sn.length()==1?"00"+sn:(sn.length()==2?"0"+sn:sn)); } //----------------------------------------------------------- // deleteSet //----------------------------------------------------------- void DeicsOnzeGui::deleteSetDialog() { //TODO : maybe to put this in sysex to deicsonze.cpp for(int c = 0; c < NBRCHANNELS; c++) _deicsOnze->_preset[c]=_deicsOnze->_initialPreset; while(!_deicsOnze->_set->_categoryVector.empty()) delete(*_deicsOnze->_set->_categoryVector.begin()); setSet(); //_currentQLVCategory = NULL; presetListView->clear(); subcategoryListView->clear(); updateCategoryName("NONE", false); hbankSpinBox->setEnabled(false); updateSubcategoryName("NONE", false); lbankSpinBox->setEnabled(false); updatePresetName("INITVOICE", false); progSpinBox->setEnabled(false); updatePreset(); } //----------------------------------------------------------- // loadSetDialog //----------------------------------------------------------- void DeicsOnzeGui::loadSetDialog() { QString fileName = QFileDialog::getOpenFileName( this, tr("Load set dialog"), lastDir, QString("*.dei") ); // read the XML file and create DOM tree if(!fileName.isEmpty()) { QFileInfo fi(fileName); lastDir = fi.path(); QFile deicsonzeFile(fileName); if(!deicsonzeFile.open(QIODevice::ReadOnly)) { QMessageBox::critical(0, tr("Critical Error"), tr("Cannot open file %1").arg(fileName)); return; } QDomDocument domTree; if (!domTree.setContent(&deicsonzeFile )) { QMessageBox::critical (0, tr("Critical Error"), tr("Parsing error for file %1").arg(fileName)); deicsonzeFile.close(); return; } deicsonzeFile.close(); QDomNode node = domTree.documentElement(); while (!node.isNull()) { QDomElement e = node.toElement(); if (e.isNull()) continue; if (e.tagName() == "deicsOnzeSet") { QString version = e.attribute(QString("version")); if (version == "1.0") { for(int c = 0; c < NBRCHANNELS; c++) _deicsOnze->_preset[c]=_deicsOnze->_initialPreset; while(!_deicsOnze->_set->_categoryVector.empty()) delete(*_deicsOnze->_set->_categoryVector.begin()); _deicsOnze->_set->readSet(node.firstChild()); //display load preset setSet(); subcategoryListView->clear(); presetListView->clear(); updateCategoryName("NONE", false); hbankSpinBox->setEnabled(false); updatePreset(); updateSubcategoryName("NONE", false); progSpinBox->setEnabled(false); updatePresetName("INITVOICE", false); } else printf("unsupported *.dei file version %s\n", version.toLatin1().constData()); } else printf("DeicsOnze: %s not supported\n", e.tagName().toLatin1().constData()); node = node.nextSibling(); } } } //----------------------------------------------------------- // saveSetDialog //----------------------------------------------------------- void DeicsOnzeGui::saveSetDialog() { QString filename = QFileDialog::getSaveFileName( this, tr("Save set dialog"), lastDir, QString("*.dei")); if(!filename.isEmpty()) { QFileInfo fi(filename); lastDir = fi.path(); if(!filename.endsWith(".dei")) filename+=".dei"; QFile f(filename); f.open(QIODevice::WriteOnly); AL::Xml* xml = new AL::Xml(&f); xml->header(); _deicsOnze->_set->writeSet(xml, false); f.close(); } } //----------------------------------------------------------- // popupMenuCategory //----------------------------------------------------------- void DeicsOnzeGui::categoryPopupMenu(const QPoint&) { QTreeWidgetItem* cat = categoryListView->currentItem(); QMenu *categoryMenu = new QMenu; categoryMenu->addAction(tr("New category"), this, SLOT(newCategoryDialog())); QAction* deleteItem = categoryMenu->addAction(tr("Delete category"), this, SLOT(deleteCategoryDialog())); categoryMenu->addAction(tr("Load category"), this, SLOT(loadCategoryDialog())); QAction* saveItem = categoryMenu->addAction(tr("Save category"), this, SLOT(saveCategoryDialog())); if(!cat || !cat->isSelected()) { deleteItem->setEnabled(false); saveItem->setEnabled(false); } categoryMenu->addSeparator(); categoryMenu->addAction(tr("Load set"), this, SLOT(loadSetDialog()));; categoryMenu->addAction(tr("Save set"), this, SLOT(saveSetDialog()));; categoryMenu->addAction(tr("Delete set"), this, SLOT(deleteSetDialog()));; categoryMenu->exec(QCursor::pos()); delete categoryMenu; // Tim. } void DeicsOnzeGui::subcategoryPopupMenu(const QPoint&) { QTreeWidgetItem* sub = subcategoryListView->currentItem(); QMenu *subcategoryMenu = new QMenu; QAction* newItem = subcategoryMenu->addAction(tr("New subcategory"), this, SLOT(newSubcategoryDialog())); QAction* deleteItem = subcategoryMenu->addAction(tr("Delete subcategory"), this, SLOT(deleteSubcategoryDialog())); QAction* loadItem = subcategoryMenu->addAction(tr("Load subcategory"), this, SLOT(loadSubcategoryDialog())); QAction* saveItem = subcategoryMenu->addAction(tr("Save subcategory"), this, SLOT(saveSubcategoryDialog())); if(!sub || !sub->isSelected()) { deleteItem->setEnabled(false); saveItem->setEnabled(false); } if(!categoryListView->currentItem() || !categoryListView->currentItem()->isSelected()) { newItem->setEnabled(false); loadItem->setEnabled(false); } subcategoryMenu->exec(QCursor::pos()); delete subcategoryMenu; // Tim. } void DeicsOnzeGui::presetPopupMenu(const QPoint&) { QTreeWidgetItem* pre = presetListView->currentItem(); QMenu *presetMenu = new QMenu; QAction* newItem = presetMenu->addAction(tr("New preset"), this, SLOT(newPresetDialog())); QAction* deleteItem = presetMenu->addAction(tr("Delete preset"), this, SLOT(deletePresetDialog())); QAction* loadItem = presetMenu->addAction(tr("Load preset"), this, SLOT(loadPresetDialog())); QAction* saveItem = presetMenu->addAction(tr("Save preset"), this, SLOT(savePresetDialog())); if(!pre || !pre->isSelected()) { deleteItem->setEnabled(false); saveItem->setEnabled(false); } if(!subcategoryListView->currentItem() || !subcategoryListView->currentItem()->isSelected()){ newItem->setEnabled(false); loadItem->setEnabled(false); } presetMenu->exec(QCursor::pos()); delete presetMenu; // Tim. } //----------------------------------------------------------- // newCategoryDialog //----------------------------------------------------------- void DeicsOnzeGui::newCategoryDialog() { int nhbank=_deicsOnze->_set->firstFreeHBank(); if(nhbank==-1) QMessageBox::information(this, tr("No more category supported"), tr("You can not add more categories"), QMessageBox::Ok); else { new Category(_deicsOnze->_set, "NEWCAT", nhbank); setSet(); QTreeWidgetItem* ci= categoryListView->findItems(num3Digits(nhbank+1), Qt::MatchExactly).at(0); ci->setSelected(true); categoryListView->setCurrentItem(ci); setCategory(ci); categoryListView->scrollToItem(ci); } } //----------------------------------------------------------- // deleteCategoryDialog //----------------------------------------------------------- void DeicsOnzeGui::deleteCategoryDialog() { QTreeCategory* cat = (QTreeCategory*) categoryListView->currentItem(); if(cat && cat->isSelected()) { if(!QMessageBox::question( this, tr("Delete category"), tr("Do you really want to delete %1 ?") .arg(cat->_category->_categoryName.c_str()), tr("&Yes"), tr("&No"), QString(), 0, 1 )) { for(int c = 0; c < NBRCHANNELS; c++) _deicsOnze->_preset[c]=_deicsOnze->_initialPreset; delete(cat->_category); delete(cat); subcategoryListView->clear(); presetListView->clear(); updateCategoryName("NONE", false); hbankSpinBox->setEnabled(false); updatePreset(); updateSubcategoryName("NONE", false); progSpinBox->setEnabled(false); updatePresetName("INITVOICE", false); } } else QMessageBox::warning(this, tr("No category selected"), tr("You must first select a category.")); } //----------------------------------------------------------- // loadCategoryDialog //----------------------------------------------------------- void DeicsOnzeGui::loadCategoryDialog() { QString buffstr; QString fileName = QFileDialog::getOpenFileName( this, tr("Load category dialog"), lastDir, QString("*.dec")); // read the XML file and create DOM tree if(!fileName.isEmpty()) { QFileInfo fi(fileName); lastDir = fi.path(); QFile deicsonzeFile(fileName); if(!deicsonzeFile.open(QIODevice::ReadOnly)) { QMessageBox::critical(0, tr("Critical Error"), tr("Cannot open file %1").arg(fileName)); return; } QDomDocument domTree; if (!domTree.setContent(&deicsonzeFile )) { QMessageBox::critical (0, tr("Critical Error"), tr("Parsing error for file %1").arg(fileName)); deicsonzeFile.close(); return; } deicsonzeFile.close(); QDomNode node = domTree.documentElement(); while (!node.isNull()) { QDomElement e = node.toElement(); if (e.isNull()) continue; if (e.tagName() == "deicsOnzeCategory") { QString version = e.attribute(QString("version")); if (version == "1.0") { Category* lCategory = new Category(); lCategory->readCategory(node.firstChild()); if (!_deicsOnze->_set->isFreeHBank(lCategory->_hbank)) { if(!QMessageBox::question( this, tr("Replace or add"), tr("%1 is supposed to be affected to the hbank number %2, but there is already one on this slot.\n Do you want to replace it or to add it in the next free slot ?") .arg((lCategory->_categoryName).c_str()) .arg(buffstr.setNum(lCategory->_hbank+1)), tr("&Replace"), tr("&Add"), QString(), 0, 1 )) { delete(_deicsOnze->_set ->findCategory(lCategory->_hbank)); lCategory->linkSet(_deicsOnze->_set); } else { int ffhb=_deicsOnze->_set->firstFreeHBank(); if(ffhb==-1) QMessageBox::warning (this, tr("Download error"), tr("There is no more free category slot.")); else lCategory->_hbank=ffhb; lCategory->linkSet(_deicsOnze->_set); } } else lCategory->linkSet(_deicsOnze->_set); //display category setSet(); } else printf("unsupported *.dec file version %s\n", version.toLatin1().constData()); } else printf("DeicsOnze: %s not supported\n", e.tagName().toLatin1().constData()); node = node.nextSibling(); } } } //----------------------------------------------------------- // saveCategoryDialog //----------------------------------------------------------- void DeicsOnzeGui::saveCategoryDialog() { QTreeCategory* cat = (QTreeCategory*) categoryListView->currentItem(); if(cat) { QString filename = QFileDialog::getSaveFileName( this, tr("Save category dialog"), lastDir, QString("*.dec")); if(!filename.isEmpty()) { QFileInfo fi(filename); lastDir = fi.path(); if(!filename.endsWith(".dec")) filename+=".dec"; QFile f(filename); f.open(QIODevice::WriteOnly); AL::Xml* xml = new AL::Xml(&f); xml->header(); cat->_category->writeCategory(xml, false); f.close(); } } else QMessageBox::warning(this, tr("No category selected"), tr("You must first select a category.")); } //----------------------------------------------------------- // newSubcategoryDialog //----------------------------------------------------------- void DeicsOnzeGui::newSubcategoryDialog() { QTreeCategory* cat = (QTreeCategory*) categoryListView->currentItem(); if(cat && cat->isSelected()) { int nlbank=cat->_category->firstFreeLBank(); if(nlbank==-1) QMessageBox::information(this, tr("No more subcategory supported"), tr("You can not add more subcategories"), QMessageBox::Ok); else { new Subcategory(cat->_category, "NEWSUB", nlbank); setCategory(cat); QTreeWidgetItem* si= subcategoryListView->findItems(num3Digits(nlbank+1), Qt::MatchExactly).at(0); si->setSelected(true); subcategoryListView->setCurrentItem(si); setSubcategory(si); subcategoryListView->scrollToItem(si); } } } //----------------------------------------------------------- // deleteSubcategoryDialog //----------------------------------------------------------- void DeicsOnzeGui::deleteSubcategoryDialog() { QTreeSubcategory* sub = (QTreeSubcategory*) subcategoryListView->currentItem(); if(sub && sub->isSelected()) { if(!QMessageBox::question( this, tr("Delete subcategory"), tr("Do you really want to delete %1 ?") .arg(sub->_subcategory ->_subcategoryName.c_str()), tr("&Yes"), tr("&No"), QString(), 0, 1 )) { for(int c = 0; c < NBRCHANNELS; c++) _deicsOnze->_preset[c]=_deicsOnze->_initialPreset; delete(sub->_subcategory); delete(sub); presetListView->clear(); //subcategoryLineEdit->setEnabled(false); updateSubcategoryName("NONE", false); lbankSpinBox->setEnabled(false); updatePreset(); } } else QMessageBox::warning(this, tr("No subcategory selected"), tr("You must first select a subcategory.")); } //----------------------------------------------------------- // loadSubcategoryDialog //----------------------------------------------------------- void DeicsOnzeGui::loadSubcategoryDialog() { QTreeCategory* cat = (QTreeCategory*) categoryListView->currentItem(); QString buffstr; QString fileName = QFileDialog::getOpenFileName( this, tr("Load subcategory dialog"), lastDir, QString("*.des")); // read the XML file and create DOM tree if(!fileName.isEmpty()) { QFileInfo fi(fileName); lastDir = fi.path(); QFile deicsonzeFile(fileName); if(!deicsonzeFile.open(QIODevice::ReadOnly)) { QMessageBox::critical(0, tr("Critical Error"), tr("Cannot open file %1").arg(fileName)); return; } QDomDocument domTree; if (!domTree.setContent(&deicsonzeFile )) { QMessageBox::critical (0, tr("Critical Error"), tr("Parsing error for file %1").arg(fileName)); deicsonzeFile.close(); return; } deicsonzeFile.close(); QDomNode node = domTree.documentElement(); while (!node.isNull()) { QDomElement e = node.toElement(); if (e.isNull()) continue; if (e.tagName() == "deicsOnzeSubcategory") { QString version = e.attribute(QString("version")); if (version == "1.0") { Subcategory* lSubcategory = new Subcategory(); lSubcategory->readSubcategory(node.firstChild()); if (!cat->_category->isFreeLBank(lSubcategory->_lbank)) { if(!QMessageBox::question( this, tr("Replace or add"), tr("%1 is supposed to be affected to the lbank number %2, but there is already one on this slot.\n Do you want to replace it or to add it in the next free slot ?") .arg((lSubcategory->_subcategoryName) .c_str()) .arg(buffstr.setNum(lSubcategory->_lbank+1)), tr("&Replace"), tr("&Add"), QString(), 0, 1 )) { delete(cat->_category->findSubcategory(lSubcategory->_lbank)); lSubcategory->linkCategory(cat->_category); } else { int fflb=cat->_category->firstFreeLBank(); if(fflb==-1) QMessageBox::warning (this, tr("Download error"), tr("There is no more free subcategory slot.")); else lSubcategory->_lbank=fflb; lSubcategory->linkCategory(cat->_category); } } else lSubcategory->linkCategory(cat->_category); //display subcategory setCategory(cat); } else printf("unsupported *.des file version %s\n", version.toLatin1().constData()); } else printf("DeicsOnze: %s not supported\n", e.tagName().toLatin1().constData()); node = node.nextSibling(); } } } //----------------------------------------------------------- // saveSubcategoryDialog //----------------------------------------------------------- void DeicsOnzeGui::saveSubcategoryDialog() { QTreeSubcategory* sub = (QTreeSubcategory*) subcategoryListView->currentItem(); if(sub) { QString filename = QFileDialog::getSaveFileName( this, tr("Save subcategory dialog"), lastDir, QString("*.des")); if(!filename.isEmpty()) { QFileInfo fi(filename); lastDir = fi.path(); if(!filename.endsWith(".des")) filename+=".des"; QFile f(filename); f.open(QIODevice::WriteOnly); AL::Xml* xml = new AL::Xml(&f); xml->header(); sub->_subcategory->writeSubcategory(xml, false); f.close(); } } else QMessageBox::warning(this, tr("No subcategory selected"), tr("You must first select a subcategory.")); } //----------------------------------------------------------- // newPresetDialog //----------------------------------------------------------- void DeicsOnzeGui::newPresetDialog() { QTreeSubcategory* sub = (QTreeSubcategory*) subcategoryListView->currentItem(); if(sub && sub->isSelected()) { int nprog=sub->_subcategory->firstFreeProg(); if(nprog==-1) QMessageBox::information(this, tr("No more preset supported"), tr("You can not add more presets"), QMessageBox::Ok); else { new Preset(sub->_subcategory, nprog); setSubcategory(sub); QTreeWidgetItem* pi= presetListView->findItems(num3Digits(nprog+1), Qt::MatchExactly).at(0); pi->setSelected(true); presetListView->setCurrentItem(pi); setPreset(pi); presetListView->scrollToItem(pi); } } } //----------------------------------------------------------- // deletePresetDialog //----------------------------------------------------------- void DeicsOnzeGui::deletePresetDialog() { QTreePreset* pre = (QTreePreset*) presetListView->currentItem(); if(pre) { if(pre->isSelected()) { if(!QMessageBox::question( this, tr("Delete preset"), tr("Do you really want to delete %1 ?") .arg(pre->_preset->name.c_str()), tr("&Yes"), tr("&No"), QString(), 0, 1 )) { for(int c = 0; c < NBRCHANNELS; c++) _deicsOnze->_preset[c]=_deicsOnze->_initialPreset; delete(pre->_preset); delete(pre); presetLineEdit->setEnabled(false); progSpinBox->setEnabled(false); updatePreset(); } } else QMessageBox::warning(this, tr("No preset selected"), tr("You must first select a preset.")); } else QMessageBox::warning(this, tr("No preset selected"), tr("You must first select a preset.")); } //----------------------------------------------------------- // loadPresetDialog //----------------------------------------------------------- void DeicsOnzeGui::loadPresetDialog() { QTreeSubcategory* sub = (QTreeSubcategory*) subcategoryListView->currentItem(); QString buffstr; QString fileName = QFileDialog::getOpenFileName( this, tr("Load preset dialog"), lastDir, QString("*.dep")); // read the XML file and create DOM tree if(!fileName.isEmpty()) { QFileInfo fi(fileName); lastDir = fi.path(); QFile deicsonzeFile(fileName); if(!deicsonzeFile.open(QIODevice::ReadOnly)) { QMessageBox::critical(0, tr("Critical Error"), tr("Cannot open file %1").arg(fileName)); return; } QDomDocument domTree; if (!domTree.setContent(&deicsonzeFile )) { QMessageBox::critical (0, tr("Critical Error"), tr("Parsing error for file %1").arg(fileName)); deicsonzeFile.close(); return; } deicsonzeFile.close(); QDomNode node = domTree.documentElement(); while (!node.isNull()) { QDomElement e = node.toElement(); if(e.isNull()) continue; if(e.tagName() == "deicsOnzePreset") { QString version = e.attribute(QString("version")); if (version == "1.0") { Preset* lPreset = new Preset(); lPreset->readPreset(node.firstChild()); if(!sub->_subcategory->isFreeProg(lPreset->prog)) { if(!QMessageBox::question( this, tr("Replace or add"), tr("%1 is supposed to be affected to the prog number %2, but there is already one on this slot.\n Do you want to replace it or to add it in the next free slot ?") .arg((lPreset->name).c_str()) .arg(buffstr.setNum(lPreset->prog+1)), tr("&Replace"), tr("&Add"), QString(), 0, 1 )) { delete(sub->_subcategory->findPreset(lPreset->prog)); lPreset->linkSubcategory(sub->_subcategory); } else { int ffp=sub->_subcategory->firstFreeProg(); if(ffp==-1) QMessageBox::warning (this, tr("Download error"), tr("There is no more free preset slot.")); else lPreset->prog=ffp; lPreset->linkSubcategory(sub->_subcategory); } } else lPreset->linkSubcategory(sub->_subcategory); //display preset setSubcategory(sub->_subcategory); } else printf("unsupported *.dep file version %s\n", version.toLatin1().constData()); } else printf("DeicsOnze: %s not supported\n", e.tagName().toLatin1().constData()); node = node.nextSibling(); } } } //----------------------------------------------------------- // savePresetDialog //----------------------------------------------------------- void DeicsOnzeGui::savePresetDialog() { QTreePreset* pre = (QTreePreset*) presetListView->currentItem(); if(pre) { QString filename = QFileDialog::getSaveFileName( this, tr("Save preset dialog"), lastDir, QString("*.dep")); if(!filename.isEmpty()) { QFileInfo fi(filename); lastDir = fi.path(); if(!filename.endsWith(".dep")) filename+=".dep"; QFile f(filename); f.open(QIODevice::WriteOnly); AL::Xml* xml = new AL::Xml(&f); xml->header(); pre->_preset->writePreset(xml, false); f.close(); } } else QMessageBox::warning(this, tr("No preset selected"), tr("You must first select a preset.")); } //----------------------------------------------------------- // Preset and bank //----------------------------------------------------------- void DeicsOnzeGui::setPresetName(const QString& n) { QTreeWidgetItem* pre = presetListView->currentItem(); if(pre) { //TODO : must be changed with SysEx _deicsOnze->_preset[_currentChannel]->name = n.toLatin1().data(); pre->setText(1,n.toLatin1().data()); } } void DeicsOnzeGui::setSubcategoryName(const QString& s) { QTreeSubcategory* sub = (QTreeSubcategory*) subcategoryListView->currentItem(); if(sub) {//must be changed with SysEx sub->_subcategory->_subcategoryName=s.toLatin1().data(); sub->setText(1, s.toLatin1().data()); } } void DeicsOnzeGui::setCategoryName(const QString& c) { QTreeCategory* cat = (QTreeCategory*) categoryListView->currentItem(); if(cat) {//must be changed with SysEx cat->_category->_categoryName=c.toLatin1().data(); cat->setText(1, c.toLatin1().data()); } } void DeicsOnzeGui::setHBank(int hb) { QTreeCategory* cat = (QTreeCategory*) categoryListView->currentItem(); if(cat) {//must be changed with SysEx if(!_deicsOnze->_set->isFreeHBank(hb-1)) { QTreeWidgetItem* qcat; qcat=categoryListView->findItems(num3Digits(hb), Qt::MatchExactly).at(0); ((QTreeCategory*)qcat)->_category->_hbank= cat->_category->_hbank; qcat->setText(0, num3Digits(((QTreeCategory*)qcat) ->_category->_hbank+1)); } cat->_category->_hbank=hb-1; cat->setText(0,num3Digits(hb)); categoryListView->sortItems(0, Qt::AscendingOrder); categoryListView->scrollToItem(cat); } } void DeicsOnzeGui::setLBank(int lb) {//must be changed with SysEx QTreeSubcategory* sub = (QTreeSubcategory*) subcategoryListView->currentItem(); if(sub) { Category* currentCat=sub->_subcategory->_category; if(!currentCat->isFreeLBank(lb-1)) { QTreeWidgetItem* qsub; qsub=subcategoryListView->findItems(num3Digits(lb), Qt::MatchExactly).at(0); ((QTreeSubcategory*)qsub)->_subcategory->_lbank= sub->_subcategory->_lbank; qsub->setText(0, num3Digits(((QTreeSubcategory*)qsub) ->_subcategory->_lbank+1)); } sub->_subcategory->_lbank=lb-1; sub->setText(0,num3Digits(lb)); subcategoryListView->sortItems(0, Qt::AscendingOrder); subcategoryListView->scrollToItem(sub); } } void DeicsOnzeGui::setProg(int pr) {//must be changed with SysEx QTreePreset* pre = (QTreePreset*) presetListView->currentItem(); if(pre) { Subcategory* currentSub=pre->_preset->_subcategory; if(!currentSub->isFreeProg(pr-1)) { QTreeWidgetItem* qpre; qpre=presetListView->findItems(num3Digits(pr), Qt::MatchExactly).at(0); ((QTreePreset*)qpre)->_preset->prog=pre->_preset->prog; qpre->setText(0, num3Digits(((QTreePreset*)qpre)->_preset->prog+1)); } pre->_preset->prog=pr-1; pre->setText(0,num3Digits(pr)); presetListView->sortItems(0,Qt::AscendingOrder); presetListView->scrollToItem(pre); } } //----------------------------------------------------------- // load init set //----------------------------------------------------------- void DeicsOnzeGui::setIsInitSet(bool b) { initSetPathLineEdit->setEnabled(b); initSetBrowsePushButton->setEnabled(b); unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_ISINITSET; message[3]=(unsigned char)b; sendSysex(message, 4); } void DeicsOnzeGui::setInitSetPath(const QString& s) { unsigned char message[4+MAXSTRLENGTHINITSETPATH]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_INITSETPATH; strncpy((char*)&message[3], s.toLatin1().data(), MAXSTRLENGTHINITSETPATH); sendSysex(message, 3+MAXSTRLENGTHINITSETPATH); } void DeicsOnzeGui::setBrowseInitSetPath() { QString fileName = QFileDialog::getOpenFileName( this, tr("Browse set dialog"), lastDir, QString("*.dei")); if(!fileName.isEmpty()) { QFileInfo fi(fileName); lastDir = fi.path(); updateInitSetPath(fileName); setInitSetPath(fileName); } } //----------------------------------------------------------- // load background pix //----------------------------------------------------------- void DeicsOnzeGui::setIsBackgroundPix(bool b) { if(b && !imagePathLineEdit->text().isEmpty()) applyBackgroundPix(); else setBackgroundColor(reinterpret_cast(*bColor)); imagePathLineEdit->setEnabled(b); imageBrowsePushButton->setEnabled(b); unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_ISBACKGROUNDPIX; message[3]=(unsigned char)b; sendSysex(message, 4); } void DeicsOnzeGui::setBackgroundPixPath(const QString& s) { applyBackgroundPix(); unsigned char message[4+MAXSTRLENGTHBACKGROUNDPIXPATH]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_BACKGROUNDPIXPATH; strncpy((char*)&message[3], s.toLatin1().data(), MAXSTRLENGTHBACKGROUNDPIXPATH); sendSysex(message, 3+MAXSTRLENGTHBACKGROUNDPIXPATH); } void DeicsOnzeGui::setBrowseBackgroundPixPath() { QString fileName = QFileDialog::getOpenFileName( this, tr("Browse image dialog"), lastDir, QString("*.jpg *.png")); if(!fileName.isEmpty()) { QFileInfo fi(fileName); lastDir = fi.path(); updateBackgroundPixPath(fileName); setBackgroundPixPath(fileName); } } //----------------------------------------------------------- // FX //----------------------------------------------------------- void DeicsOnzeGui::setChorusActiv(bool a) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_CHORUSACTIV; message[3]=(unsigned char)a; sendSysex(message, 4); } void DeicsOnzeGui::setChannelChorus(int c) { sendController(_currentChannel, MusECore::CTRL_CHORUS_SEND, c); } void DeicsOnzeGui::setChorusReturn(int val) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_CHORUSRETURN; message[3]=(unsigned char)val; sendSysex(message, 4); } void DeicsOnzeGui::setSelectChorusPlugin() { MusESimplePlugin::Plugin* pluginChorus = MusESimplePlugin::SimplerPluginChooser::getPlugin(this); if(pluginChorus) { unsigned char message[3+sizeof(MusESimplePlugin::Plugin*)]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_SELECTCHORUS; memcpy(&message[3], &pluginChorus, sizeof(MusESimplePlugin::Plugin*)); sendSysex(message, 3+sizeof(MusESimplePlugin::Plugin*)); } } /*void DeicsOnzeGui::setPanChorus1(double i) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_CHORUS1PAN; message[3]=(unsigned char)(i*(double)MAXCHORUSPARAM); sendSysex(message, 4); } void DeicsOnzeGui::setLFOFreqChorus1(double i) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_CHORUS1LFOFREQ; message[3]=(unsigned char)(i*(double)MAXCHORUSPARAM); sendSysex(message, 4); } void DeicsOnzeGui::setDepthChorus1(double i) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_CHORUS1DEPTH; message[3]=(unsigned char)(i*(double)MAXCHORUSPARAM); sendSysex(message, 4); } void DeicsOnzeGui::setPanChorus2(double i) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_CHORUS2PAN; message[3]=(unsigned char)(i*(double)MAXCHORUSPARAM); sendSysex(message, 4); } void DeicsOnzeGui::setLFOFreqChorus2(double i) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_CHORUS2LFOFREQ; message[3]=(unsigned char)(i*(double)MAXCHORUSPARAM); sendSysex(message, 4); } void DeicsOnzeGui::setDepthChorus2(double i) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_CHORUS2DEPTH; message[3]=(unsigned char)(i*(double)MAXCHORUSPARAM); sendSysex(message, 4); }*/ void DeicsOnzeGui::setReverbActiv(bool a) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_REVERBACTIV; message[3]=(unsigned char)a; sendSysex(message, 4); } void DeicsOnzeGui::setChannelReverb(int r) { sendController(_currentChannel, MusECore::CTRL_REVERB_SEND, r); } void DeicsOnzeGui::setReverbReturn(int val) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_REVERBRETURN; message[3]=(unsigned char)val; sendSysex(message, 4); } void DeicsOnzeGui::setSelectReverbPlugin() { MusESimplePlugin::Plugin* pluginReverb = MusESimplePlugin::SimplerPluginChooser::getPlugin(this); if(pluginReverb) { unsigned char message[3+sizeof(MusESimplePlugin::Plugin*)]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_SELECTREVERB; memcpy(&message[3], &pluginReverb, sizeof(MusESimplePlugin::Plugin*)); sendSysex(message, 3+sizeof(MusESimplePlugin::Plugin*)); } } //----------------------------------------------------------- // Quick Edit //----------------------------------------------------------- void DeicsOnzeGui::setChannelVolKnob(double val) { sendController(_currentChannel, CTRL_CHANNELVOLUME, (int)(val*(double)MAXCHANNELVOLUME)); } void DeicsOnzeGui::setBrightnessKnob(double val) { sendController(_currentChannel, CTRL_FINEBRIGHTNESS, (int)(val*(double)MAXFINEBRIGHTNESS)); } void DeicsOnzeGui::setModulationKnob(double val) { sendController(_currentChannel, MusECore::CTRL_MODULATION, (int)(val*(double)MAXMODULATION)); } void DeicsOnzeGui::setDetuneKnob(double val) { //channelDetuneSlider->setValue((int)((2.0*val-1.0)*(double)MAXCHANNELDETUNE)); setChannelDetune((int)((2.0*val-1.0)*(double)MAXCHANNELDETUNE)); } void DeicsOnzeGui::setAttackKnob(double val) { sendController(_currentChannel, MusECore::CTRL_ATTACK_TIME, (int)(val*(double)MAXATTACK)); } void DeicsOnzeGui::setReleaseKnob(double val) { sendController(_currentChannel, MusECore::CTRL_RELEASE_TIME, (int)(val*(double)MAXRELEASE)); } //----------------------------------------------------------- // Global control //----------------------------------------------------------- void DeicsOnzeGui::setChannelPan(double mv) { sendController(_currentChannel, CTRL_CHANNELPAN, (int)((mv-0.5)*2*(double)MAXCHANNELPAN)); } void DeicsOnzeGui::setMasterVolKnob(double mv) { setMasterVol((int)(mv*(double)MAXMASTERVOLUME)); } void DeicsOnzeGui::setMasterVol(int mv) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_MASTERVOL; message[3]=(unsigned char)mv; sendSysex(message, 4); } void DeicsOnzeGui::setFeedback(int f) {sendController(_currentChannel, CTRL_FEEDBACK, f);} void DeicsOnzeGui::setLfoWave(int lw) {sendController(_currentChannel, CTRL_WAVE, lw);} void DeicsOnzeGui::setLfoSpeed(int ls) {sendController(_currentChannel, CTRL_SPEED, ls);} void DeicsOnzeGui::setLfoDelay(int ld) {sendController(_currentChannel, CTRL_DELAY, ld);} void DeicsOnzeGui::setLfoPModDepth(int lpmd) { sendController(_currentChannel, CTRL_PMODDEPTH, lpmd); } void DeicsOnzeGui::setLfoPitchSens(int lps) { sendController(_currentChannel, CTRL_PMODSENS, lps); } void DeicsOnzeGui::setLfoAModDepth(int lamd) { sendController(_currentChannel, CTRL_AMODDEPTH, lamd); } void DeicsOnzeGui::setLfoAmpSens(int las) {sendController(_currentChannel, CTRL_AMS, las);} void DeicsOnzeGui::setTranspose(int t) {sendController(_currentChannel, CTRL_TRANSPOSE, t);} void DeicsOnzeGui::setChannelDetune(int d) { sendController(_currentChannel, CTRL_CHANNELDETUNE, d); updateChannelDetuneKnob(d); } void DeicsOnzeGui::setAlgorithm(int a) { sendController(_currentChannel, CTRL_ALG, (int) (a==0?FIRST: (a==1?SECOND: (a==2?THIRD: (a==3?FOURTH: (a==4?FIFTH: (a==5?SIXTH: (a==6?SEVENTH:EIGHTH)))))))); } void DeicsOnzeGui::setPitchBendRange(int pbr) { sendController(_currentChannel, CTRL_PBENDRANGE, pbr); } //--------------------------------------------------------------- // Pitch Envelope //--------------------------------------------------------------- void DeicsOnzeGui::setPL1(int val) { PitchEg* pe=&_deicsOnze->_preset[_currentChannel]->pitchEg; pitchEnvelopeGraph->env2Points(val, pe->pl2, pe->pl3, pe->pr1, pe->pr2, pe->pr3); pitchEnvelopeGraph->updateEnv(); sendController(_currentChannel, CTRL_PL1, val); } void DeicsOnzeGui::setPL2(int val) { PitchEg* pe=&_deicsOnze->_preset[_currentChannel]->pitchEg; pitchEnvelopeGraph->env2Points(pe->pl1, val, pe->pl3, pe->pr1, pe->pr2, pe->pr3); pitchEnvelopeGraph->updateEnv(); sendController(_currentChannel, CTRL_PL2, val); } void DeicsOnzeGui::setPL3(int val) { PitchEg* pe=&_deicsOnze->_preset[_currentChannel]->pitchEg; pitchEnvelopeGraph->env2Points(pe->pl1, pe->pl2, val, pe->pr1, pe->pr2, pe->pr3); pitchEnvelopeGraph->updateEnv(); sendController(_currentChannel, CTRL_PL3, val); } void DeicsOnzeGui::setPR1(int val) { PitchEg* pe=&_deicsOnze->_preset[_currentChannel]->pitchEg; pitchEnvelopeGraph->env2Points(pe->pl1, pe->pl2, pe->pl3, val, pe->pr2, pe->pr3); pitchEnvelopeGraph->updateEnv(); sendController(_currentChannel, CTRL_PR1, val); } void DeicsOnzeGui::setPR2(int val) { PitchEg* pe=&_deicsOnze->_preset[_currentChannel]->pitchEg; pitchEnvelopeGraph->env2Points(pe->pl1, pe->pl2, pe->pl3, pe->pr1, val, pe->pr3); pitchEnvelopeGraph->updateEnv(); sendController(_currentChannel, CTRL_PR2, val); } void DeicsOnzeGui::setPR3(int val) { PitchEg* pe=&_deicsOnze->_preset[_currentChannel]->pitchEg; pitchEnvelopeGraph->env2Points(pe->pl1, pe->pl2, pe->pl3, pe->pr1, pe->pr2, val); pitchEnvelopeGraph->updateEnv(); sendController(_currentChannel, CTRL_PR3, val); } //--------------------------------------------------------------- // Function //--------------------------------------------------------------- void DeicsOnzeGui::setFcVolume(int val){sendController(_currentChannel, CTRL_FCVOLUME, val);} void DeicsOnzeGui::setFcPitch(int val){sendController(_currentChannel, CTRL_FCPITCH, val);} void DeicsOnzeGui::setFcAmplitude(int val) { sendController(_currentChannel, CTRL_FCAMPLITUDE, val); } void DeicsOnzeGui::setMwPitch(int val){sendController(_currentChannel, CTRL_MWPITCH, val);} void DeicsOnzeGui::setMwAmplitude(int val) { sendController(_currentChannel, CTRL_MWAMPLITUDE, val); } void DeicsOnzeGui::setBcPitch(int val){sendController(_currentChannel, CTRL_BCPITCH, val);} void DeicsOnzeGui::setBcAmplitude(int val) { sendController(_currentChannel, CTRL_BCAMPLITUDE, val); } void DeicsOnzeGui::setBcPitchBias(int val) { sendController(_currentChannel, CTRL_BCPITCHBIAS, val);} void DeicsOnzeGui::setBcEgBias(int val) { sendController(_currentChannel, CTRL_BCEGBIAS, val); } void DeicsOnzeGui::setAtPitch(int val){sendController(_currentChannel, CTRL_ATPITCH, val);} void DeicsOnzeGui::setAtAmplitude(int val) { sendController(_currentChannel, CTRL_ATAMPLITUDE, val); } void DeicsOnzeGui::setAtPitchBias(int val) { sendController(_currentChannel, CTRL_ATPITCHBIAS, val);} void DeicsOnzeGui::setAtEgBias(int val) { sendController(_currentChannel, CTRL_ATEGBIAS, val); } void DeicsOnzeGui::setReverbRate(int val) { sendController(_currentChannel, CTRL_REVERBRATE, val); } void DeicsOnzeGui::setPolyMode(int val) { sendController(_currentChannel, CTRL_POLYMODE, val); } void DeicsOnzeGui::setPortFingerFull(int val) { sendController(_currentChannel, CTRL_PORTAMODE, val); } void DeicsOnzeGui::setPortaTime(int val) { sendController(_currentChannel, CTRL_PORTATIME, val); } //--------------------------------------------------------------- // envelope controle //--------------------------------------------------------------- void DeicsOnzeGui::setAR1(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[0]); //printf("ar : %d, d1r : %d, d1l : %d, d2r : %d, rr : %d\n", // val, _eg->d1r, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[0]->env2Points(val, _eg->d1r, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[0]->updateEnv(); sendController(_currentChannel, CTRL_AR, val); } void DeicsOnzeGui::setD1R1(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[0]); envelopeGraph[0]->env2Points(_eg->ar, val, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[0]->updateEnv(); sendController(_currentChannel, CTRL_D1R, val); } void DeicsOnzeGui::setD1L1(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[0]); envelopeGraph[0]->env2Points(_eg->ar, _eg->d1r, val, _eg->d2r, _eg->rr); envelopeGraph[0]->updateEnv(); sendController(_currentChannel, CTRL_D1L, val); } void DeicsOnzeGui::setD2R1(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[0]); envelopeGraph[0]->env2Points(_eg->ar, _eg->d1r, _eg->d1l, val, _eg->rr); envelopeGraph[0]->updateEnv(); sendController(_currentChannel, CTRL_D2R, val); } void DeicsOnzeGui::setRR1(int val){ Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[0]); envelopeGraph[0]->env2Points(_eg->ar, _eg->d1r, _eg->d1l, _eg->d2r, val); envelopeGraph[0]->updateEnv(); sendController(_currentChannel, CTRL_RR, val); } void DeicsOnzeGui::setAR2(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[1]); envelopeGraph[1]->env2Points(val, _eg->d1r, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[1]->updateEnv(); sendController(_currentChannel, CTRL_AR+DECAPAR1, val); } void DeicsOnzeGui::setD1R2(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[1]); envelopeGraph[1]->env2Points(_eg->ar, val, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[1]->updateEnv(); sendController(_currentChannel, CTRL_D1R+DECAPAR1, val); } void DeicsOnzeGui::setD1L2(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[1]); envelopeGraph[1]->env2Points(_eg->ar, _eg->d1r, val, _eg->d2r, _eg->rr); envelopeGraph[1]->updateEnv(); sendController(_currentChannel, CTRL_D1L+DECAPAR1, val); } void DeicsOnzeGui::setD2R2(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[1]); envelopeGraph[1]->env2Points(_eg->ar, _eg->d1r, _eg->d1l, val, _eg->rr); envelopeGraph[1]->updateEnv(); sendController(_currentChannel, CTRL_D2R+DECAPAR1, val); } void DeicsOnzeGui::setRR2(int val){ Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[1]); envelopeGraph[1]->env2Points(_eg->ar, _eg->d1r, _eg->d1l, _eg->d2r, val); envelopeGraph[1]->updateEnv(); sendController(_currentChannel, CTRL_RR+DECAPAR1, val); } void DeicsOnzeGui::setAR3(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[2]); envelopeGraph[2]->env2Points(val, _eg->d1r, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[2]->updateEnv(); sendController(_currentChannel, CTRL_AR+2*DECAPAR1, val); } void DeicsOnzeGui::setD1R3(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[2]); envelopeGraph[2]->env2Points(_eg->ar, val, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[2]->updateEnv(); sendController(_currentChannel, CTRL_D1R+2*DECAPAR1, val); } void DeicsOnzeGui::setD1L3(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[2]); envelopeGraph[2]->env2Points(_eg->ar, _eg->d1r, val, _eg->d2r, _eg->rr); envelopeGraph[2]->updateEnv(); sendController(_currentChannel, CTRL_D1L+2*DECAPAR1, val); } void DeicsOnzeGui::setD2R3(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[2]); envelopeGraph[2]->env2Points(_eg->ar, _eg->d1r, _eg->d1l, val, _eg->rr); envelopeGraph[2]->updateEnv(); sendController(_currentChannel, CTRL_D2R+2*DECAPAR1, val); } void DeicsOnzeGui::setRR3(int val){ Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[2]); envelopeGraph[2]->env2Points(_eg->ar, _eg->d1r, _eg->d1l, _eg->d2r, val); envelopeGraph[2]->updateEnv(); sendController(_currentChannel, CTRL_RR+2*DECAPAR1, val); } void DeicsOnzeGui::setAR4(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[3]); envelopeGraph[3]->env2Points(val, _eg->d1r, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[3]->updateEnv(); sendController(_currentChannel, CTRL_AR+3*DECAPAR1, val); } void DeicsOnzeGui::setD1R4(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[3]); envelopeGraph[3]->env2Points(_eg->ar, val, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[3]->updateEnv(); sendController(_currentChannel, CTRL_D1R+3*DECAPAR1, val); } void DeicsOnzeGui::setD1L4(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[3]); envelopeGraph[3]->env2Points(_eg->ar, _eg->d1r, val, _eg->d2r, _eg->rr); envelopeGraph[3]->updateEnv(); sendController(_currentChannel, CTRL_D1L+3*DECAPAR1, val); } void DeicsOnzeGui::setD2R4(int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[3]); envelopeGraph[3]->env2Points(_eg->ar, _eg->d1r, _eg->d1l, val, _eg->rr); envelopeGraph[3]->updateEnv(); sendController(_currentChannel, CTRL_D2R+3*DECAPAR1, val); } void DeicsOnzeGui::setRR4(int val){ Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[3]); envelopeGraph[3]->env2Points(_eg->ar, _eg->d1r, _eg->d1l, _eg->d2r, val); envelopeGraph[3]->updateEnv(); sendController(_currentChannel, CTRL_RR+3*DECAPAR1, val); } //-------------------------------------------------------------- // set Scaling //-------------------------------------------------------------- void DeicsOnzeGui::setLS1(int val){sendController(_currentChannel, CTRL_LS, val);} void DeicsOnzeGui::setRS1(int val){sendController(_currentChannel, CTRL_RS, val);} void DeicsOnzeGui::setLS2(int val){sendController(_currentChannel, CTRL_LS+DECAPAR1, val);} void DeicsOnzeGui::setRS2(int val){sendController(_currentChannel, CTRL_RS+DECAPAR1, val);} void DeicsOnzeGui::setLS3(int val){sendController(_currentChannel, CTRL_LS+2*DECAPAR1, val);} void DeicsOnzeGui::setRS3(int val){sendController(_currentChannel, CTRL_RS+2*DECAPAR1, val);} void DeicsOnzeGui::setLS4(int val){sendController(_currentChannel, CTRL_LS+3*DECAPAR1, val);} void DeicsOnzeGui::setRS4(int val){sendController(_currentChannel, CTRL_RS+3*DECAPAR1, val);} //-------------------------------------------------------------- // set Volume //-------------------------------------------------------------- void DeicsOnzeGui::setVol1(int val){sendController(_currentChannel, CTRL_OUT, val);} void DeicsOnzeGui::setVol2(int val){sendController(_currentChannel, CTRL_OUT+DECAPAR1, val);} void DeicsOnzeGui::setVol3(int val){sendController(_currentChannel,CTRL_OUT+2*DECAPAR1,val);} void DeicsOnzeGui::setVol4(int val){sendController(_currentChannel,CTRL_OUT+3*DECAPAR1,val);} //-------------------------------------------------------------- // set Ratio and Frequency //-------------------------------------------------------------- void DeicsOnzeGui::setCoarseRatio1(int val) { sendController(_currentChannel, CTRL_RATIO, val*100+FineRatio1SpinBox->value()); } void DeicsOnzeGui::setFineRatio1(int val) { sendController(_currentChannel, CTRL_RATIO, val+CoarseRatio1SpinBox->value()*100); } void DeicsOnzeGui::setFreq1(int val) { sendController(_currentChannel,CTRL_FIXRANGE,val*100);} void DeicsOnzeGui::setFix1(bool f) { sendController(_currentChannel, CTRL_FIX, (f==false?0:1));} void DeicsOnzeGui::setCoarseRatio2(int val) { sendController(_currentChannel, CTRL_RATIO+DECAPAR1, val*100+FineRatio2SpinBox->value()); } void DeicsOnzeGui::setFineRatio2(int val) { sendController(_currentChannel,CTRL_RATIO+DECAPAR1,val+CoarseRatio2SpinBox->value()*100); } void DeicsOnzeGui::setFreq2(int val) { sendController(_currentChannel,CTRL_FIXRANGE+DECAPAR2,val*100);} void DeicsOnzeGui::setFix2(bool f) { sendController(_currentChannel, CTRL_FIX+DECAPAR2, (f==false?0:1));} void DeicsOnzeGui::setCoarseRatio3(int val) { sendController(_currentChannel,CTRL_RATIO+2*DECAPAR1,val*100+FineRatio3SpinBox->value()); } void DeicsOnzeGui::setFineRatio3(int val) { sendController(_currentChannel,CTRL_RATIO+2*DECAPAR1, val+CoarseRatio3SpinBox->value()*100); } void DeicsOnzeGui::setFreq3(int val) { sendController(_currentChannel,CTRL_FIXRANGE+2*DECAPAR2,val*100);} void DeicsOnzeGui::setFix3(bool f) { sendController(_currentChannel, CTRL_FIX+2*DECAPAR2, (f==false?0:1));} void DeicsOnzeGui::setCoarseRatio4(int val) { sendController(_currentChannel,CTRL_RATIO+3*DECAPAR1,val*100+FineRatio4SpinBox->value()); } void DeicsOnzeGui::setFineRatio4(int val) { sendController(_currentChannel,CTRL_RATIO+3*DECAPAR1, val+CoarseRatio4SpinBox->value()*100); } void DeicsOnzeGui::setFreq4(int val) { sendController(_currentChannel,CTRL_FIXRANGE+3*DECAPAR2,val*100);} void DeicsOnzeGui::setFix4(bool f) { sendController(_currentChannel, CTRL_FIX+3*DECAPAR2, (f==false?0:1));} //-------------------------------------------------------------- // set Sensitivity //-------------------------------------------------------------- void DeicsOnzeGui::setAME1(bool val) {sendController(_currentChannel, CTRL_AME, val);} void DeicsOnzeGui::setEBS1(int val) {sendController(_currentChannel, CTRL_EBS, val);} void DeicsOnzeGui::setKVS1(int val) {sendController(_currentChannel, CTRL_KVS, val);} void DeicsOnzeGui::setAME2(bool val) {sendController(_currentChannel,CTRL_AME+DECAPAR1,val);} void DeicsOnzeGui::setEBS2(int val) {sendController(_currentChannel,CTRL_EBS+DECAPAR1,val);} void DeicsOnzeGui::setKVS2(int val) {sendController(_currentChannel,CTRL_KVS+DECAPAR1,val);} void DeicsOnzeGui::setAME3(bool val) { sendController(_currentChannel,CTRL_AME+2*DECAPAR1,val);} void DeicsOnzeGui::setEBS3(int val) { sendController(_currentChannel, CTRL_EBS+2*DECAPAR1, val);} void DeicsOnzeGui::setKVS3(int val) { sendController(_currentChannel, CTRL_KVS+2*DECAPAR1, val);} void DeicsOnzeGui::setAME4(bool val) { sendController(_currentChannel, CTRL_AME+3*DECAPAR1, val);} void DeicsOnzeGui::setEBS4(int val) { sendController(_currentChannel, CTRL_EBS+3*DECAPAR1, val);} void DeicsOnzeGui::setKVS4(int val) { sendController(_currentChannel, CTRL_KVS+3*DECAPAR1, val);} //-------------------------------------------------------------- // set detune //-------------------------------------------------------------- void DeicsOnzeGui::setDET1(int val){sendController(_currentChannel, CTRL_DET, val);} void DeicsOnzeGui::setDET2(int val){sendController(_currentChannel, CTRL_DET+DECAPAR1, val);} void DeicsOnzeGui::setDET3(int val){sendController(_currentChannel,CTRL_DET+2*DECAPAR1,val);} void DeicsOnzeGui::setDET4(int val){sendController(_currentChannel,CTRL_DET+3*DECAPAR1,val);} //-------------------------------------------------------------- // set WaveForm //-------------------------------------------------------------- void DeicsOnzeGui::setWaveForm1(int w) { sendController(_currentChannel, CTRL_OSW, w); } void DeicsOnzeGui::setWaveForm2(int w) { sendController(_currentChannel, CTRL_OSW+DECAPAR2, w); } void DeicsOnzeGui::setWaveForm3(int w) { sendController(_currentChannel, CTRL_OSW+2*DECAPAR2, w); } void DeicsOnzeGui::setWaveForm4(int w) { sendController(_currentChannel, CTRL_OSW+3*DECAPAR2, w); } //-------------------------------------------------------------- // set delay //-------------------------------------------------------------- void DeicsOnzeGui::setActivDelay(bool a) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_DELAYACTIV; message[3]=(unsigned char)a; sendSysex(message, 4); } void DeicsOnzeGui::setDelayReturn(int r) { unsigned char message[4]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_DELAYRETURN; message[3]=(unsigned char)r; sendSysex(message, 4); } void DeicsOnzeGui::setChannelDelay(int d) { sendController(_currentChannel, MusECore::CTRL_VARIATION_SEND, (unsigned char)d); } void DeicsOnzeGui::setDelayBPM(double t) { //int it = (int)(((t - MINDELAYTIME) / (MAXDELAYTIME - MINDELAYTIME))*255.0); unsigned char message[sizeof(float)+3]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_DELAYBPM; float f = (float)t; memcpy(&message[3], &f, sizeof(float)); message[3]=(unsigned char)f; sendSysex(message, sizeof(float)+3); //updateDelayTime(it); } void DeicsOnzeGui::setDelayBeatRatio(double t) { unsigned char message[sizeof(float)+3]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_DELAYBEATRATIO; float f = (float)t; memcpy(&message[3], &f, sizeof(float)); message[3]=(unsigned char)f; sendSysex(message, sizeof(float)+3); } void DeicsOnzeGui::setDelayFeedback(double t) { //int idf = (int)(f*128.0+128.0); unsigned char message[sizeof(float)+3]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_DELAYFEEDBACK; float f = (float)t; memcpy(&message[3], &f, sizeof(float)); sendSysex(message, sizeof(float)+3); //updateDelayFeedback(idf); } void DeicsOnzeGui::setDelayPanLFOFreq(double pf) { //int ipf = (int)(((pf - MINFREQ) / (MAXFREQ - MINFREQ))*255.0); unsigned char message[sizeof(float)+3]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_DELAYLFOFREQ; float f = (float)pf; memcpy(&message[3], &f, sizeof(float)); sendSysex(message, sizeof(float)+3); //updateDelayPanLFOFreq(ipf); } void DeicsOnzeGui::setDelayPanLFODepth(double pd) { //int ipd = (int)(pd*255.0); unsigned char message[sizeof(float)+3]; message[0]=MUSE_SYNTH_SYSEX_MFG_ID; message[1]=DEICSONZE_UNIQUE_ID; message[2]=SYSEX_DELAYLFODEPTH; float f = (float)pd; memcpy(&message[3], &f, sizeof(float)); sendSysex(message, sizeof(float)+3); //updateDelayPanLFODepth(ipd); } //-------------------------------------------------------------- // setSet // Display the set, that is the category list //-------------------------------------------------------------- void DeicsOnzeGui::setSet() { categoryListView->clear(); hbankSpinBox->setEnabled(false); categoryLineEdit->setEnabled(false); for(std::vector::iterator i=_deicsOnze->_set->_categoryVector.begin(); i!=_deicsOnze->_set->_categoryVector.end(); i++) (void) new QTreeCategory (categoryListView, num3Digits((*i)->_hbank+1), (*i)->_categoryName.c_str(), *i); categoryListView->resizeColumnToContents(0); categoryListView->sortItems(0,Qt::AscendingOrder); } //-------------------------------------------------------------- // setCategory //-------------------------------------------------------------- void DeicsOnzeGui::setCategory(Category* cat) { subcategoryListView->clear(); lbankSpinBox->setEnabled(false); subcategoryLineEdit->setEnabled(false); for(std::vector::iterator i=cat->_subcategoryVector.begin(); i!=cat->_subcategoryVector.end(); i++) (void) new QTreeSubcategory(subcategoryListView, num3Digits((*i)->_lbank+1), (*i)->_subcategoryName.c_str(),*i); subcategoryListView->resizeColumnToContents(0); subcategoryListView->sortItems(0,Qt::AscendingOrder); } void DeicsOnzeGui::setCategory(QTreeWidgetItem* cat) { QTreeCategory* ccat = (QTreeCategory*) categoryListView->currentItem(); if(cat) { categoryLineEdit->setEnabled(true); hbankSpinBox->setEnabled(true); categoryLineEdit ->setText(((QTreeCategory*)cat)->_category->_categoryName.c_str()); hbankSpinBox->setValue(((QTreeCategory*)cat)->_category->_hbank+1); ccat=(QTreeCategory*)cat; setCategory(ccat->_category); } } //-------------------------------------------------------------- // setSubcategory //-------------------------------------------------------------- void DeicsOnzeGui::setSubcategory(Subcategory* sub) { presetListView->clear(); progSpinBox->setEnabled(false); presetLineEdit->setEnabled(false); for(std::vector::iterator i=sub->_presetVector.begin(); i!=sub->_presetVector.end(); i++) (void) new QTreePreset(presetListView, num3Digits((*i)->prog+1), (*i)->name.c_str(),*i); presetListView->resizeColumnToContents(0); presetListView->sortItems(0,Qt::AscendingOrder); } void DeicsOnzeGui::setSubcategory(QTreeWidgetItem* sub) { QTreeSubcategory* csub = (QTreeSubcategory*) subcategoryListView->currentItem(); if(sub) { subcategoryLineEdit->setEnabled(true); lbankSpinBox->setEnabled(true); subcategoryLineEdit->setText(((QTreeSubcategory*)sub) ->_subcategory->_subcategoryName.c_str()); lbankSpinBox->setValue(((QTreeSubcategory*)sub) ->_subcategory->_lbank+1); csub=(QTreeSubcategory*)sub; setSubcategory(csub->_subcategory); } } //-------------------------------------------------------------- // setPreset //-------------------------------------------------------------- void DeicsOnzeGui::setPreset(QTreeWidgetItem* pre) { if(pre) { QTreePreset* cpre=(QTreePreset*)pre; updatePresetName(cpre->_preset->name.c_str(), true); updateProg(cpre->_preset->prog, true); int prog = cpre->_preset->prog; int lbank = cpre->_preset->_subcategory->_lbank; int hbank = cpre->_preset->_subcategory->_category->_hbank; setEnabledPreset(true); updatePreset(cpre->_preset); sendController(_currentChannel, MusECore::CTRL_PROGRAM, (hbank<<16)+(lbank<<8)+prog); } } //-------------------------------------------------------------- // update the gui //-------------------------------------------------------------- void DeicsOnzeGui::setEnabledPreset(bool b) { if(_enabledPreset!=b) { FeedbackGroupBox->setEnabled(b); LFOGroupBox->setEnabled(b); pitchEnvGroupBox->setEnabled(b); ModulationMatrixGroupBox->setEnabled(b); Op1Tab->setEnabled(b); Op2Tab->setEnabled(b); Op3Tab->setEnabled(b); Op4Tab->setEnabled(b); FunctionsTab->setEnabled(b); chorusTab->setEnabled(b); reverbTab->setEnabled(b); _enabledPreset=b; } } void DeicsOnzeGui::updateChannelEnable(bool e) { updateChannelCheckBox(e); updateEnabledChannel(e); } void DeicsOnzeGui::updateEnabledChannel(bool e) { numberVoicesLabel->setEnabled(e); nbrVoicesSpinBox->setEnabled(e); channelCtrlGroupBox->setEnabled(e); deicsOnzeTabWidget->setEnabled(e); } void DeicsOnzeGui::updateNbrVoices(int val) { nbrVoicesSpinBox->blockSignals(true); nbrVoicesSpinBox->setValue(val); nbrVoicesSpinBox->blockSignals(false); } void DeicsOnzeGui::updateMasterVolume(int val) { double d = (double)val/(double)MAXMASTERVOLUME; //printf("D = %d, %f\n", val, d); masterVolKnob->blockSignals(true); masterVolKnob->setValue(d); masterVolKnob->blockSignals(false); } //void DeicsOnzeGui::updateMidiInCh(int val) { //MidiInChComboBox->blockSignals(true); //MidiInChComboBox->setCurrentIndex(val); //MidiInChComboBox->blockSignals(false); //} void DeicsOnzeGui::updateQuality(int val) { qualityComboBox->blockSignals(true); qualityComboBox->setCurrentIndex(val); qualityComboBox->blockSignals(false); } void DeicsOnzeGui::updateFilter(bool f) { filterCheckBox->blockSignals(true); filterCheckBox->setChecked(f); filterCheckBox->blockSignals(false); } void DeicsOnzeGui::updateFontSize(int val) { fontSizeSpinBox->blockSignals(true); fontSizeSpinBox->setValue(val); fontSizeSpinBox->blockSignals(false); } //FX void DeicsOnzeGui::updateChorusActiv(bool a) { chorusActivCheckBox->blockSignals(true); chorusActivCheckBox->setChecked(a); chorusActivCheckBox->blockSignals(false); } void DeicsOnzeGui::updateChannelChorus(int c) { chChorusSlider->blockSignals(true); chChorusSlider->setValue(c); chChorusSlider->blockSignals(false); chChorusSpinBox->blockSignals(true); chChorusSpinBox->setValue(c); chChorusSpinBox->blockSignals(false); } void DeicsOnzeGui::updateChorusReturn(int r) { returnChorusSlider->blockSignals(true); returnChorusSlider->setValue(r); returnChorusSlider->blockSignals(false); } /*void DeicsOnzeGui::updatePanChorus1(int c) { panChorus1Knob->blockSignals(true); panChorus1Knob->setValue((double)c/(double)MAXCHORUSPARAM); panChorus1Knob->blockSignals(false); } void DeicsOnzeGui::updateLFOFreqChorus1(int c) { LFOFreqChorus1Knob->blockSignals(true); LFOFreqChorus1Knob->setValue((double)c/(double)MAXCHORUSPARAM); LFOFreqChorus1Knob->blockSignals(false); } void DeicsOnzeGui::updateDepthChorus1(int c) { depthChorus1Knob->blockSignals(true); depthChorus1Knob->setValue((double)c/(double)MAXCHORUSPARAM); depthChorus1Knob->blockSignals(false); } void DeicsOnzeGui::updatePanChorus2(int c) { panChorus2Knob->blockSignals(true); panChorus2Knob->setValue((double)c/(double)MAXCHORUSPARAM); panChorus2Knob->blockSignals(false); } void DeicsOnzeGui::updateLFOFreqChorus2(int c) { LFOFreqChorus2Knob->blockSignals(true); LFOFreqChorus2Knob->setValue((double)c/(double)MAXCHORUSPARAM); LFOFreqChorus2Knob->blockSignals(false); } void DeicsOnzeGui::updateDepthChorus2(int c) { depthChorus2Knob->blockSignals(true); depthChorus2Knob->setValue((double)c/(double)MAXCHORUSPARAM); depthChorus2Knob->blockSignals(false); }*/ void DeicsOnzeGui::updateReverbActiv(bool a) { reverbActivCheckBox->blockSignals(true); reverbActivCheckBox->setChecked(a); reverbActivCheckBox->blockSignals(false); } void DeicsOnzeGui::updateChannelReverb(int r) { chReverbSlider->blockSignals(true); chReverbSlider->setValue(r); chReverbSlider->blockSignals(false); chReverbSpinBox->blockSignals(true); chReverbSpinBox->setValue(r); chReverbSpinBox->blockSignals(false); } void DeicsOnzeGui::updateReverbReturn(int r) { returnReverbSlider->blockSignals(true); returnReverbSlider->setValue(r); returnReverbSlider->blockSignals(false); } void DeicsOnzeGui::updateLadspaReverbLineEdit(QString s) { selectLadspaReverbLineEdit->blockSignals(true); selectLadspaReverbLineEdit->setText(s); selectLadspaReverbLineEdit->blockSignals(false); } void DeicsOnzeGui::updateLadspaChorusLineEdit(QString s) { selectLadspaChorusLineEdit->blockSignals(true); selectLadspaChorusLineEdit->setText(s); selectLadspaChorusLineEdit->blockSignals(false); } void DeicsOnzeGui::updateDelayActiv(bool a) { delayActivCheckBox->blockSignals(true); delayActivCheckBox->setChecked(a); delayActivCheckBox->blockSignals(false); } void DeicsOnzeGui::updateChannelDelay(int r) { chDelaySlider->blockSignals(true); chDelaySlider->setValue(r); chDelaySlider->blockSignals(false); chDelaySpinBox->blockSignals(true); chDelaySpinBox->setValue(r); chDelaySpinBox->blockSignals(false); } void DeicsOnzeGui::updateDelayReturn(int r) { delayReturnSlider->blockSignals(true); delayReturnSlider->setValue(r); delayReturnSlider->blockSignals(false); } void DeicsOnzeGui::updateDelayPanLFOFreq(float plf) { delayPanLFOFreqKnob->blockSignals(true); delayPanLFOFreqKnob->setValue((double)plf); delayPanLFOFreqKnob->blockSignals(false); delayPanLFOFreqFloatentry->blockSignals(true); delayPanLFOFreqFloatentry->setValue((double)plf); delayPanLFOFreqFloatentry->blockSignals(false); } void DeicsOnzeGui::updateDelayBPM(float dt) { delayBPMKnob->blockSignals(true); delayBPMKnob->setValue((double)dt); delayBPMKnob->blockSignals(false); delayBPMFloatentry->blockSignals(true); delayBPMFloatentry->setValue((double)dt); delayBPMFloatentry->blockSignals(false); } void DeicsOnzeGui::updateDelayBeatRatio(float dt) { delayBeatRatioKnob->blockSignals(true); delayBeatRatioKnob->setValue((double)dt); delayBeatRatioKnob->blockSignals(false); delayBeatRatioFloatentry->blockSignals(true); delayBeatRatioFloatentry->setValue((double)dt); delayBeatRatioFloatentry->blockSignals(false); } void DeicsOnzeGui::updateDelayFeedback(float df) { delayFeedbackKnob->blockSignals(true); delayFeedbackKnob->setValue((double)df); delayFeedbackKnob->blockSignals(false); delayFeedbackFloatentry->blockSignals(true); delayFeedbackFloatentry->setValue((double)df); delayFeedbackFloatentry->blockSignals(false); } void DeicsOnzeGui::updateDelayPanLFODepth(float dpd) { delayPanLFODepthKnob->blockSignals(true); delayPanLFODepthKnob->setValue((double)dpd); delayPanLFODepthKnob->blockSignals(false); delayPanLFODepthFloatentry->blockSignals(true); delayPanLFODepthFloatentry->setValue((double)dpd); delayPanLFODepthFloatentry->blockSignals(false); } void DeicsOnzeGui::applyFontSize(int fs) { QFont f = font(); f.setPointSize(fs); setFont(f); } void DeicsOnzeGui::updateSaveConfig(bool usc) { saveConfigCheckBox->blockSignals(true); saveConfigCheckBox->setChecked(usc); saveConfigCheckBox->blockSignals(false); } void DeicsOnzeGui::updateSaveOnlyUsed(bool usou) { hugeSaveRadioButton->blockSignals(true); hugeSaveRadioButton->setChecked(!usou); hugeSaveRadioButton->blockSignals(false); minSaveRadioButton->blockSignals(true); minSaveRadioButton->setChecked(usou); minSaveRadioButton->blockSignals(false); } //Pitch Envelope void DeicsOnzeGui::updatePL1(int val) { PL1SpinBox->blockSignals(true); PL1SpinBox->setValue(val); PL1SpinBox->blockSignals(false); } void DeicsOnzeGui::updatePL2(int val) { PL2SpinBox->blockSignals(true); PL2SpinBox->setValue(val); PL2SpinBox->blockSignals(false); } void DeicsOnzeGui::updatePL3(int val) { PL3SpinBox->blockSignals(true); PL3SpinBox->setValue(val); PL3SpinBox->blockSignals(false); } void DeicsOnzeGui::updatePR1(int val) { PR1SpinBox->blockSignals(true); PR1SpinBox->setValue(val); PR1SpinBox->blockSignals(false); } void DeicsOnzeGui::updatePR2(int val) { PR2SpinBox->blockSignals(true); PR2SpinBox->setValue(val); PR2SpinBox->blockSignals(false); } void DeicsOnzeGui::updatePR3(int val) { PR3SpinBox->blockSignals(true); PR3SpinBox->setValue(val); PR3SpinBox->blockSignals(false); } //Function void DeicsOnzeGui::updateFcVolume(int val) { fcVolumeSpinBox->blockSignals(true); fcVolumeSpinBox->setValue(val); fcVolumeSpinBox->blockSignals(false); fcVolumeSlider->blockSignals(true); fcVolumeSlider->setValue(val); fcVolumeSlider->blockSignals(false); } void DeicsOnzeGui::updateFcPitch(int val) { fcPitchSpinBox->blockSignals(true); fcPitchSpinBox->setValue(val); fcPitchSpinBox->blockSignals(false); fcPitchSlider->blockSignals(true); fcPitchSlider->setValue(val); fcPitchSlider->blockSignals(false); } void DeicsOnzeGui::updateFcAmplitude(int val) { fcAmplitudeSpinBox->blockSignals(true); fcAmplitudeSpinBox->setValue(val); fcAmplitudeSpinBox->blockSignals(false); fcAmplitudeSlider->blockSignals(true); fcAmplitudeSlider->setValue(val); fcAmplitudeSlider->blockSignals(false); } void DeicsOnzeGui::updateMwPitch(int val) { mwPitchSpinBox->blockSignals(true); mwPitchSpinBox->setValue(val); mwPitchSpinBox->blockSignals(false); mwPitchSlider->blockSignals(true); mwPitchSlider->setValue(val); mwPitchSlider->blockSignals(false); } void DeicsOnzeGui::updateMwAmplitude(int val) { mwAmplitudeSpinBox->blockSignals(true); mwAmplitudeSpinBox->setValue(val); mwAmplitudeSpinBox->blockSignals(false); mwAmplitudeSlider->blockSignals(true); mwAmplitudeSlider->setValue(val); mwAmplitudeSlider->blockSignals(false); } void DeicsOnzeGui::updateBcPitch(int val) { bcPitchSpinBox->blockSignals(true); bcPitchSpinBox->setValue(val); bcPitchSpinBox->blockSignals(false); bcPitchSlider->blockSignals(true); bcPitchSlider->setValue(val); bcPitchSlider->blockSignals(false); } void DeicsOnzeGui::updateBcAmplitude(int val) { bcAmplitudeSpinBox->blockSignals(true); bcAmplitudeSpinBox->setValue(val); bcAmplitudeSpinBox->blockSignals(false); bcAmplitudeSlider->blockSignals(true); bcAmplitudeSlider->setValue(val); bcAmplitudeSlider->blockSignals(false); } void DeicsOnzeGui::updateBcPitchBias(int val) { bcPitchBiasSpinBox->blockSignals(true); bcPitchBiasSpinBox->setValue(val); bcPitchBiasSpinBox->blockSignals(false); bcPitchBiasSlider->blockSignals(true); bcPitchBiasSlider->setValue(val); bcPitchBiasSlider->blockSignals(false); } void DeicsOnzeGui::updateBcEgBias(int val) { bcEgBiasSpinBox->blockSignals(true); bcEgBiasSpinBox->setValue(val); bcEgBiasSpinBox->blockSignals(false); bcEgBiasSlider->blockSignals(true); bcEgBiasSlider->setValue(val); bcEgBiasSlider->blockSignals(false); } void DeicsOnzeGui::updateAtPitch(int val) { atPitchSpinBox->blockSignals(true); atPitchSpinBox->setValue(val); atPitchSpinBox->blockSignals(false); atPitchSlider->blockSignals(true); atPitchSlider->setValue(val); atPitchSlider->blockSignals(false); } void DeicsOnzeGui::updateAtAmplitude(int val) { atAmplitudeSpinBox->blockSignals(true); atAmplitudeSpinBox->setValue(val); atAmplitudeSpinBox->blockSignals(false); atAmplitudeSlider->blockSignals(true); atAmplitudeSlider->setValue(val); atAmplitudeSlider->blockSignals(false); } void DeicsOnzeGui::updateAtPitchBias(int val) { atPitchBiasSpinBox->blockSignals(true); atPitchBiasSpinBox->setValue(val); atPitchBiasSpinBox->blockSignals(false); atPitchBiasSlider->blockSignals(true); atPitchBiasSlider->setValue(val); atPitchBiasSlider->blockSignals(false); } void DeicsOnzeGui::updateAtEgBias(int val) { atEgBiasSpinBox->blockSignals(true); atEgBiasSpinBox->setValue(val); atEgBiasSpinBox->blockSignals(false); atEgBiasSlider->blockSignals(true); atEgBiasSlider->setValue(val); atEgBiasSlider->blockSignals(false); } //void DeicsOnzeGui::updateReverbRate(int val) { //reverbSpinBox->blockSignals(true); //reverbSpinBox->setValue(val); //reverbSpinBox->blockSignals(false); //reverbSlider->blockSignals(true); //reverbSlider->setValue(val); //reverbSlider->blockSignals(false); //} //Envelope void DeicsOnzeGui::updateAR(int op, int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[op]); envelopeGraph[op]->env2Points(val, _eg->d1r, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[op]->updateEnv(); switch(op) { case 0: AR1SpinBox->blockSignals(true); AR1SpinBox->setValue(val); AR1SpinBox->blockSignals(false); break; case 1: AR2SpinBox->blockSignals(true); AR2SpinBox->setValue(val); AR2SpinBox->blockSignals(false); break; case 2: AR3SpinBox->blockSignals(true); AR3SpinBox->setValue(val); AR3SpinBox->blockSignals(false); break; case 3: AR4SpinBox->blockSignals(true); AR4SpinBox->setValue(val); AR4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateAR : Error switch\n"); } } void DeicsOnzeGui::updateD1R(int op, int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[op]); envelopeGraph[op]->env2Points(_eg->ar, val, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[op]->updateEnv(); switch(op) { case 0: D1R1SpinBox->blockSignals(true); D1R1SpinBox->setValue(val); D1R1SpinBox->blockSignals(false); break; case 1: D1R2SpinBox->blockSignals(true); D1R2SpinBox->setValue(val); D1R2SpinBox->blockSignals(false); break; case 2: D1R3SpinBox->blockSignals(true); D1R3SpinBox->setValue(val); D1R3SpinBox->blockSignals(false); break; case 3: D1R4SpinBox->blockSignals(true); D1R4SpinBox->setValue(val); D1R4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateD1R : Error switch\n"); } } void DeicsOnzeGui::updateD2R(int op, int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[op]); envelopeGraph[op]->env2Points(_eg->ar, _eg->d1r, _eg->d1l, val, _eg->rr); envelopeGraph[op]->updateEnv(); switch(op) { case 0: D2R1SpinBox->blockSignals(true); D2R1SpinBox->setValue(val); D2R1SpinBox->blockSignals(false); break; case 1: D2R2SpinBox->blockSignals(true); D2R2SpinBox->setValue(val); D2R2SpinBox->blockSignals(false); break; case 2: D2R3SpinBox->blockSignals(true); D2R3SpinBox->setValue(val); D2R3SpinBox->blockSignals(false); break; case 3: D2R4SpinBox->blockSignals(true); D2R4SpinBox->setValue(val); D2R4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateD2R : Error switch\n"); } } void DeicsOnzeGui::updateRR(int op, int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[op]); envelopeGraph[op]->env2Points(_eg->ar, _eg->d1r, _eg->d1l, _eg->d2r, val); envelopeGraph[op]->updateEnv(); switch(op) { case 0: RR1SpinBox->blockSignals(true); RR1SpinBox->setValue(val); RR1SpinBox->blockSignals(false); break; case 1: RR2SpinBox->blockSignals(true); RR2SpinBox->setValue(val); RR2SpinBox->blockSignals(false); break; case 2: RR3SpinBox->blockSignals(true); RR3SpinBox->setValue(val); RR3SpinBox->blockSignals(false); break; case 3: RR4SpinBox->blockSignals(true); RR4SpinBox->setValue(val); RR4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateRR : Error switch\n"); } } void DeicsOnzeGui::updateD1L(int op, int val) { Eg* _eg=&(_deicsOnze->_preset[_currentChannel]->eg[op]); envelopeGraph[op]->env2Points(_eg->ar, _eg->d1r, val, _eg->d2r, _eg->rr); envelopeGraph[op]->updateEnv(); switch(op) { case 0: D1L1SpinBox->blockSignals(true); D1L1SpinBox->setValue(val); D1L1SpinBox->blockSignals(false); break; case 1: D1L2SpinBox->blockSignals(true); D1L2SpinBox->setValue(val); D1L2SpinBox->blockSignals(false); break; case 2: D1L3SpinBox->blockSignals(true); D1L3SpinBox->setValue(val); D1L3SpinBox->blockSignals(false); break; case 3: D1L4SpinBox->blockSignals(true); D1L4SpinBox->setValue(val); D1L4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateD1L : Error switch\n"); } } void DeicsOnzeGui::updateLS(int op, int val) { switch(op) { case 0: LS1Slider->blockSignals(true); LS1Slider->setValue(val); LS1Slider->blockSignals(false); LS1SpinBox->blockSignals(true); LS1SpinBox->setValue(val); LS1SpinBox->blockSignals(false); break; case 1: LS2Slider->blockSignals(true); LS2Slider->setValue(val); LS2Slider->blockSignals(false); LS2SpinBox->blockSignals(true); LS2SpinBox->setValue(val); LS2SpinBox->blockSignals(false); break; case 2: LS3Slider->blockSignals(true); LS3Slider->setValue(val); LS3Slider->blockSignals(false); LS3SpinBox->blockSignals(true); LS3SpinBox->setValue(val); LS3SpinBox->blockSignals(false); break; case 3: LS4Slider->blockSignals(true); LS4Slider->setValue(val); LS4Slider->blockSignals(false); LS4SpinBox->blockSignals(true); LS4SpinBox->setValue(val); LS4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateLS : Error switch\n"); } } void DeicsOnzeGui::updateRS(int op, int val) { switch(op) { case 0: RS1Slider->blockSignals(true); RS1Slider->setValue(val); RS1Slider->blockSignals(false); RS1SpinBox->blockSignals(true); RS1SpinBox->setValue(val); RS1SpinBox->blockSignals(false); break; case 1: RS2Slider->blockSignals(true); RS2Slider->setValue(val); RS2Slider->blockSignals(false); RS2SpinBox->blockSignals(true); RS2SpinBox->setValue(val); RS2SpinBox->blockSignals(false); break; case 2: RS3Slider->blockSignals(true); RS3Slider->setValue(val); RS3Slider->blockSignals(false); RS3SpinBox->blockSignals(true); RS3SpinBox->setValue(val); RS3SpinBox->blockSignals(false); break; case 3: RS4Slider->blockSignals(true); RS4Slider->setValue(val); RS4Slider->blockSignals(false); RS4SpinBox->blockSignals(true); RS4SpinBox->setValue(val); RS4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateRS : Error switch\n"); } } void DeicsOnzeGui::updateEBS(int op, int val) { switch(op) { case 0: EBS1Slider->blockSignals(true); EBS1Slider->setValue(val); EBS1Slider->blockSignals(false); EBS1SpinBox->blockSignals(true); EBS1SpinBox->setValue(val); EBS1SpinBox->blockSignals(false); break; case 1: EBS2Slider->blockSignals(true); EBS2Slider->setValue(val); EBS2Slider->blockSignals(false); EBS2SpinBox->blockSignals(true); EBS2SpinBox->setValue(val); EBS2SpinBox->blockSignals(false); break; case 2: EBS3Slider->blockSignals(true); EBS3Slider->setValue(val); EBS3Slider->blockSignals(false); EBS3SpinBox->blockSignals(true); EBS3SpinBox->setValue(val); EBS3SpinBox->blockSignals(false); break; case 3: EBS4Slider->blockSignals(true); EBS4Slider->setValue(val); EBS4Slider->blockSignals(false); EBS4SpinBox->blockSignals(true); EBS4SpinBox->setValue(val); EBS4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateEBS : Error switch\n"); } } void DeicsOnzeGui::updateAME(int op, bool val) { switch(op) { case 0: AME1CheckBox->blockSignals(true); AME1CheckBox->setChecked(val); AME1CheckBox->blockSignals(false); break; case 1: AME2CheckBox->blockSignals(true); AME2CheckBox->setChecked(val); AME2CheckBox->blockSignals(false); break; case 2: AME3CheckBox->blockSignals(true); AME3CheckBox->setChecked(val); AME3CheckBox->blockSignals(false); break; case 3: AME4CheckBox->blockSignals(true); AME4CheckBox->setChecked(val); AME4CheckBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateAME : Error switch\n"); } } void DeicsOnzeGui::updateKVS(int op, int val) { switch(op) { case 0: KVS1Slider->blockSignals(true); KVS1Slider->setValue(val); KVS1Slider->blockSignals(false); KVS1SpinBox->blockSignals(true); KVS1SpinBox->setValue(val); KVS1SpinBox->blockSignals(false); break; case 1: KVS2Slider->blockSignals(true); KVS2Slider->setValue(val); KVS2Slider->blockSignals(false); KVS2SpinBox->blockSignals(true); KVS2SpinBox->setValue(val); KVS2SpinBox->blockSignals(false); break; case 2: KVS3Slider->blockSignals(true); KVS3Slider->setValue(val); KVS3Slider->blockSignals(false); KVS3SpinBox->blockSignals(true); KVS3SpinBox->setValue(val); KVS3SpinBox->blockSignals(false); break; case 3: KVS4Slider->blockSignals(true); KVS4Slider->setValue(val); KVS4Slider->blockSignals(false); KVS4SpinBox->blockSignals(true); KVS4SpinBox->setValue(val); KVS4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateKVS : Error switch\n"); } } void DeicsOnzeGui::updateOUT(int op, int val) { switch(op) { case 0: OUT1Slider->blockSignals(true); OUT1Slider->setValue(val); OUT1Slider->blockSignals(false); OUT1SpinBox->blockSignals(true); OUT1SpinBox->setValue(val); OUT1SpinBox->blockSignals(false); break; case 1: OUT2Slider->blockSignals(true); OUT2Slider->setValue(val); OUT2Slider->blockSignals(false); OUT2SpinBox->blockSignals(true); OUT2SpinBox->setValue(val); OUT2SpinBox->blockSignals(false); break; case 2: OUT3Slider->blockSignals(true); OUT3Slider->setValue(val); OUT3Slider->blockSignals(false); OUT3SpinBox->blockSignals(true); OUT3SpinBox->setValue(val); OUT3SpinBox->blockSignals(false); break; case 3: OUT4Slider->blockSignals(true); OUT4Slider->setValue(val); OUT4Slider->blockSignals(false); OUT4SpinBox->blockSignals(true); OUT4SpinBox->setValue(val); OUT4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateOUT : Error switch\n"); } } void DeicsOnzeGui::updateRATIO(int op, int val) { switch(op) { case 0: CoarseRatio1SpinBox->blockSignals(true); CoarseRatio1SpinBox->setValue(val/100); CoarseRatio1SpinBox->blockSignals(false); FineRatio1SpinBox->blockSignals(true); FineRatio1SpinBox->setValue(val%100); FineRatio1SpinBox->blockSignals(false); break; case 1: CoarseRatio2SpinBox->blockSignals(true); CoarseRatio2SpinBox->setValue(val/100); CoarseRatio2SpinBox->blockSignals(false); FineRatio2SpinBox->blockSignals(true); FineRatio2SpinBox->setValue(val%100); FineRatio2SpinBox->blockSignals(false); break; case 2: CoarseRatio3SpinBox->blockSignals(true); CoarseRatio3SpinBox->setValue(val/100); CoarseRatio3SpinBox->blockSignals(false); FineRatio3SpinBox->blockSignals(true); FineRatio3SpinBox->setValue(val%100); FineRatio3SpinBox->blockSignals(false); break; case 3: CoarseRatio4SpinBox->blockSignals(true); CoarseRatio4SpinBox->setValue(val/100); CoarseRatio4SpinBox->blockSignals(false); FineRatio4SpinBox->blockSignals(true); FineRatio4SpinBox->setValue(val%100); FineRatio4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateRATIO : Error switch\n"); } } void DeicsOnzeGui::updateDET(int op, int val) { switch(op) { case 0: DET1Slider->blockSignals(true); DET1Slider->setValue(val); DET1Slider->blockSignals(false); DET1SpinBox->blockSignals(true); DET1SpinBox->setValue(val); DET1SpinBox->blockSignals(false); break; case 1: DET2Slider->blockSignals(true); DET2Slider->setValue(val); DET2Slider->blockSignals(false); DET2SpinBox->blockSignals(true); DET2SpinBox->setValue(val); DET2SpinBox->blockSignals(false); break; case 2: DET3Slider->blockSignals(true); DET3Slider->setValue(val); DET3Slider->blockSignals(false); DET3SpinBox->blockSignals(true); DET3SpinBox->setValue(val); DET3SpinBox->blockSignals(false); break; case 3: DET4Slider->blockSignals(true); DET4Slider->setValue(val); DET4Slider->blockSignals(false); DET4SpinBox->blockSignals(true); DET4SpinBox->setValue(val); DET4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateDET : Error switch\n"); } } void DeicsOnzeGui::updateALG(int val) { algorithmComboBox->blockSignals(true); algorithmComboBox->setCurrentIndex(val); algorithmComboBox->blockSignals(false); } void DeicsOnzeGui::updateFEEDBACK(int val) { feedbackSlider->blockSignals(true); feedbackSlider->setValue(val); feedbackSlider->blockSignals(false); feedbackSpinBox->blockSignals(true); feedbackSpinBox->setValue(val); feedbackSpinBox->blockSignals(false); } void DeicsOnzeGui::updateSPEED(int val) { LFOSpeedSlider->blockSignals(true); LFOSpeedSlider->setValue(val); LFOSpeedSlider->blockSignals(false); LFOSpeedSpinBox->blockSignals(true); LFOSpeedSpinBox->setValue(val); LFOSpeedSpinBox->blockSignals(false); } void DeicsOnzeGui::updateDELAY(int val) { LFODelaySlider->blockSignals(true); LFODelaySlider->setValue(val); LFODelaySlider->blockSignals(false); LFODelaySpinBox->blockSignals(true); LFODelaySpinBox->setValue(val); LFODelaySpinBox->blockSignals(false); } void DeicsOnzeGui::updatePMODDEPTH(int val) { PModDepthSlider->blockSignals(true); PModDepthSlider->setValue(val); PModDepthSlider->blockSignals(false); PModDepthSpinBox->blockSignals(true); PModDepthSpinBox->setValue(val); PModDepthSpinBox->blockSignals(false); } void DeicsOnzeGui::updateAMODDEPTH(int val) { AModDepthSlider->blockSignals(true); AModDepthSlider->setValue(val); AModDepthSlider->blockSignals(false); AModDepthSpinBox->blockSignals(true); AModDepthSpinBox->setValue(val); AModDepthSpinBox->blockSignals(false); } void DeicsOnzeGui::updateSYNC(bool val) { LFOSyncCheckBox->blockSignals(true); LFOSyncCheckBox->setChecked(val); LFOSyncCheckBox->blockSignals(false); } void DeicsOnzeGui::updateWAVE(int val) { LFOWaveComboBox->blockSignals(true); LFOWaveComboBox->setCurrentIndex(val); LFOWaveComboBox->blockSignals(false); } void DeicsOnzeGui::updatePMODSENS(int val) { PModSensSlider->blockSignals(true); PModSensSlider->setValue(val); PModSensSlider->blockSignals(false); PModSensSpinBox->blockSignals(true); PModSensSpinBox->setValue(val); PModSensSpinBox->blockSignals(false); } void DeicsOnzeGui::updateAMS(int val) { AModSensSlider->blockSignals(true); AModSensSlider->setValue(val); AModSensSlider->blockSignals(false); AModSensSpinBox->blockSignals(true); AModSensSpinBox->setValue(val); AModSensSpinBox->blockSignals(false); } void DeicsOnzeGui::updateTRANSPOSE(int val) { transposeSlider->blockSignals(true); transposeSlider->setValue(val); transposeSlider->blockSignals(false); transposeSpinBox->blockSignals(true); transposeSpinBox->setValue(val); transposeSpinBox->blockSignals(false); } void DeicsOnzeGui::updatePOLYMODE(int val) { polyMonoComboBox->blockSignals(true); polyMonoComboBox->setCurrentIndex(val); polyMonoComboBox->blockSignals(false); } void DeicsOnzeGui::updatePBENDRANGE(int val) { pitchBendRangeSlider->blockSignals(true); pitchBendRangeSlider->setValue(val); pitchBendRangeSlider->blockSignals(false); pitchBendRangeSpinBox->blockSignals(true); pitchBendRangeSpinBox->setValue(val); pitchBendRangeSpinBox->blockSignals(false); } void DeicsOnzeGui::updatePORTAMODE(int val) { PortFingerFullComboBox->blockSignals(true); PortFingerFullComboBox->setCurrentIndex(val); PortFingerFullComboBox->blockSignals(false); } void DeicsOnzeGui::updatePORTATIME(int val) { PortamentoTimeSlider->blockSignals(true); PortamentoTimeSlider->setValue(val); PortamentoTimeSlider->blockSignals(false); PortamentoTimeSpinBox->blockSignals(true); PortamentoTimeSpinBox->setValue(val); PortamentoTimeSpinBox->blockSignals(false); } void DeicsOnzeGui::updateFIX(int op, bool val) { switch(op) { case 0: Fix1CheckBox->blockSignals(true); Fix1CheckBox->setChecked(val); Fix1CheckBox->blockSignals(false); FineRatio1SpinBox->blockSignals(true); FineRatio1SpinBox->setEnabled(!val); FineRatio1SpinBox->blockSignals(false); CoarseRatio1SpinBox->blockSignals(true); CoarseRatio1SpinBox->setEnabled(!val); CoarseRatio1SpinBox->blockSignals(false); break; case 1: Fix2CheckBox->blockSignals(true); Fix2CheckBox->setChecked(val); Fix2CheckBox->blockSignals(false); FineRatio2SpinBox->blockSignals(true); FineRatio2SpinBox->setEnabled(!val); FineRatio2SpinBox->blockSignals(false); CoarseRatio2SpinBox->blockSignals(true); CoarseRatio2SpinBox->setEnabled(!val); CoarseRatio2SpinBox->blockSignals(false); break; case 2: Fix3CheckBox->blockSignals(true); Fix3CheckBox->setChecked(val); Fix3CheckBox->blockSignals(false); FineRatio3SpinBox->blockSignals(true); FineRatio3SpinBox->setEnabled(!val); FineRatio3SpinBox->blockSignals(false); CoarseRatio3SpinBox->blockSignals(true); CoarseRatio3SpinBox->setEnabled(!val); CoarseRatio3SpinBox->blockSignals(false); break; case 3: Fix4CheckBox->blockSignals(true); Fix4CheckBox->setChecked(val); Fix4CheckBox->blockSignals(false); FineRatio4SpinBox->blockSignals(true); FineRatio4SpinBox->setEnabled(!val); FineRatio4SpinBox->blockSignals(false); CoarseRatio4SpinBox->blockSignals(true); CoarseRatio4SpinBox->setEnabled(!val); CoarseRatio4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateFIX : error switch\n"); } } void DeicsOnzeGui::updateFIXRANGE(int op, int val) { switch(op) { case 0: Freq1SpinBox->blockSignals(true); Freq1SpinBox->setValue(val/100); //val/100 because it is still a coarse display Freq1SpinBox->blockSignals(false); break; case 1: Freq2SpinBox->blockSignals(true); Freq2SpinBox->setValue(val/100); Freq2SpinBox->blockSignals(false); break; case 2: Freq3SpinBox->blockSignals(true); Freq3SpinBox->setValue(val/100); Freq3SpinBox->blockSignals(false); break; case 3: Freq4SpinBox->blockSignals(true); Freq4SpinBox->setValue(val/100); Freq4SpinBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateFIXRANGE : error switch\n"); } } void DeicsOnzeGui::updateOSW(int op, int val) { switch(op) { case 0: WaveForm1ComboBox->blockSignals(true); WaveForm1ComboBox->setCurrentIndex(val); WaveForm1ComboBox->blockSignals(false); break; case 1: WaveForm2ComboBox->blockSignals(true); WaveForm2ComboBox->setCurrentIndex(val); WaveForm2ComboBox->blockSignals(false); break; case 2: WaveForm3ComboBox->blockSignals(true); WaveForm3ComboBox->setCurrentIndex(val); WaveForm3ComboBox->blockSignals(false); break; case 3: WaveForm4ComboBox->blockSignals(true); WaveForm4ComboBox->setCurrentIndex(val); WaveForm4ComboBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateOSW : Error switch\n"); } } void DeicsOnzeGui::updateSHFT(int op, int val) { switch(op) { case 0: EGS1ComboBox->blockSignals(true); EGS1ComboBox->setCurrentIndex(val); EGS1ComboBox->blockSignals(false); break; case 1: EGS2ComboBox->blockSignals(true); EGS2ComboBox->setCurrentIndex(val); EGS2ComboBox->blockSignals(false); break; case 2: EGS3ComboBox->blockSignals(true); EGS3ComboBox->setCurrentIndex(val); EGS3ComboBox->blockSignals(false); break; case 3: EGS4ComboBox->blockSignals(true); EGS4ComboBox->setCurrentIndex(val); EGS4ComboBox->blockSignals(false); break; default: printf("DeicsOnzeGui::updateSHFT : Error switch\n"); } } void DeicsOnzeGui::updateChannelDetune(int val) { updateChannelDetuneKnob(val); //updateChannelDetuneSlider(val); } void DeicsOnzeGui::updateChannelDetuneKnob(int val) { detuneKnob->blockSignals(true); detuneKnob->setValue((((double)val)/((double)MAXCHANNELDETUNE))/2.0+0.5); detuneKnob->blockSignals(false); } //void DeicsOnzeGui::updateChannelDetuneSlider(int val) { //channelDetuneSlider->blockSignals(true); //channelDetuneSlider->setValue(val); //channelDetuneSlider->blockSignals(false); //channelDetuneSpinBox->blockSignals(true); //channelDetuneSpinBox->setValue(val); //channelDetuneSpinBox->blockSignals(false); //} void DeicsOnzeGui::updateChannelVolume(int val) { channelVolumeKnob->blockSignals(true); channelVolumeKnob->setValue(((double)val)/(double)MAXCHANNELVOLUME); channelVolumeKnob->blockSignals(false); } void DeicsOnzeGui::updateCategoryName(QString cn, bool enable) { categoryLineEdit->setEnabled(enable); categoryLineEdit->blockSignals(true); categoryLineEdit->setText(cn); categoryLineEdit->blockSignals(false); } void DeicsOnzeGui::updateSubcategoryName(QString sn, bool enable) { subcategoryLineEdit->setEnabled(enable); subcategoryLineEdit->blockSignals(true); subcategoryLineEdit->setText(sn); subcategoryLineEdit->blockSignals(false); } void DeicsOnzeGui::updatePresetName(QString pn) { //presetNameLineEdit->blockSignals(true); //presetNameLineEdit->setText(pn); //presetNameLineEdit->blockSignals(false); //presetNameLabel->setText(pn); presetLineEdit->blockSignals(true); presetLineEdit->setText(pn); presetLineEdit->blockSignals(false); } void DeicsOnzeGui::updatePresetName(QString pn, bool enable) { presetLineEdit->setEnabled(enable); //presetNameLineEdit->setEnabled(enable); updatePresetName(pn); } void DeicsOnzeGui::updateHBank(int n, bool enable) { hbankSpinBox->setEnabled(enable); hbankSpinBox->blockSignals(true); hbankSpinBox->setValue(n+1); hbankSpinBox->blockSignals(false); } void DeicsOnzeGui::updateLBank(int n, bool enable) { lbankSpinBox->setEnabled(enable); lbankSpinBox->blockSignals(true); lbankSpinBox->setValue(n+1); lbankSpinBox->blockSignals(false); } void DeicsOnzeGui::updateProg(int n, bool enable) { progSpinBox->setEnabled(enable); progSpinBox->blockSignals(true); progSpinBox->setValue(n+1); progSpinBox->blockSignals(false); } void DeicsOnzeGui::updateInitSetCheckBox(bool b) { initSetCheckBox->blockSignals(true); initSetCheckBox->setChecked(b); initSetCheckBox->blockSignals(false); initSetPathLineEdit->setEnabled(b); initSetBrowsePushButton-> setEnabled(b); } void DeicsOnzeGui::updateInitSetPath(QString s) { initSetPathLineEdit->blockSignals(true); initSetPathLineEdit->setText(s); initSetPathLineEdit->blockSignals(false); } void DeicsOnzeGui::updateBackgroundPixCheckBox(bool b) { imageCheckBox->blockSignals(true); imageCheckBox->setChecked(b); imageCheckBox->blockSignals(false); imagePathLineEdit->setEnabled(b); imageBrowsePushButton-> setEnabled(b); } void DeicsOnzeGui::updateBackgroundPixPath(QString s) { imagePathLineEdit->blockSignals(true); imagePathLineEdit->setText(s); imagePathLineEdit->blockSignals(false); } void DeicsOnzeGui::applyBackgroundPix() { #ifdef DEICSONZE_DEBUG printf("applyBackgroundPix\n"); #endif QPalette p = this->palette(); QPixmap pixmap = QPixmap(imagePathLineEdit->text()); p.setBrush((this)->backgroundRole(), QBrush(pixmap)); (this)->setPalette(p); } void DeicsOnzeGui::updateChannelPan(int val) { channelPanKnob->blockSignals(true); channelPanKnob->setValue((((double)val/(double)MAXCHANNELPAN)+1.0)/2.0); channelPanKnob->blockSignals(false); //channelPanSpinBox->blockSignals(true); //channelPanSpinBox->setValue(val); //channelPanSpinBox->blockSignals(false); } void DeicsOnzeGui::updateBrightness(int val) { brightnessKnob->blockSignals(true); brightnessKnob->setValue((double)val/((double)MAXFINEBRIGHTNESS)); brightnessKnob->blockSignals(false); } void DeicsOnzeGui::updateModulation(int val) { modulationKnob->blockSignals(true); modulationKnob->setValue((double)val/((double)MAXMODULATION)); modulationKnob->blockSignals(false); } void DeicsOnzeGui::updateAttack(int val) { attackKnob->blockSignals(true); attackKnob->setValue((double)val/((double)MAXATTACK)); attackKnob->blockSignals(false); } void DeicsOnzeGui::updateRelease(int val) { releaseKnob->blockSignals(true); releaseKnob->setValue((double)val/((double)MAXRELEASE)); releaseKnob->blockSignals(false); } void DeicsOnzeGui::updateQuickEdit() { updateChannelVolume(_deicsOnze->getChannelVol(_currentChannel)); updateChannelPan(_deicsOnze->getChannelPan(_currentChannel)); updateBrightness(_deicsOnze->getChannelBrightness(_currentChannel)); updateModulation(_deicsOnze->getChannelModulation(_currentChannel)); updateChannelDetune(_deicsOnze->getChannelDetune(_currentChannel)); updateAttack(_deicsOnze->getChannelAttack(_currentChannel)); updateRelease(_deicsOnze->getChannelRelease(_currentChannel)); updateChannelReverb(_deicsOnze->getChannelReverb(_currentChannel)); updateChannelChorus(_deicsOnze->getChannelChorus(_currentChannel)); updateChannelDelay(_deicsOnze->getChannelDelay(_currentChannel)); } //-------------------------------------------------------------- // updatePreset //-------------------------------------------------------------- void DeicsOnzeGui::updatePreset(Preset* p) { //TODO : why updateMasterVolume //updateMasterVolume(_deicsOnze->getMasterVol()); //to change updatePresetName(p->name.c_str()); updateFEEDBACK(p->feedback); updateWAVE((int)p->lfo.wave); updateSPEED(p->lfo.speed); updateDELAY(p->lfo.delay); updatePMODDEPTH(p->lfo.pModDepth); updatePMODSENS(p->sensitivity.pitch); updateAMODDEPTH(p->lfo.aModDepth); updateAMS(p->sensitivity.amplitude); updateTRANSPOSE(p->function.transpose); updateALG((int)p->algorithm); updatePBENDRANGE(p->function.pBendRange); //pitch envelope PitchEg* pe=&(p->pitchEg); pitchEnvelopeGraph ->env2Points(pe->pl1, pe->pl2, pe->pl3, pe->pr1, pe->pr2, pe->pr3); pitchEnvelopeGraph->updateEnv(); updatePL1(pe->pl1); updatePL2(pe->pl2); updatePL3(pe->pl3); updatePR1(pe->pr1); updatePR2(pe->pr2); updatePR3(pe->pr3); //function updateFcVolume(p->function.fcVolume); updateFcPitch(p->function.fcPitch); updateFcAmplitude(p->function.fcAmplitude); updateMwPitch(p->function.mwPitch); updateMwAmplitude(p->function.mwAmplitude); updateBcPitch(p->function.bcPitch); updateBcAmplitude(p->function.bcAmplitude); updateBcPitchBias(p->function.bcPitchBias); updateBcEgBias(p->function.bcEgBias); updateAtPitch(p->function.atPitch); updateAtAmplitude(p->function.atAmplitude); updateAtPitchBias(p->function.atPitchBias); updateAtEgBias(p->function.atEgBias); //updateReverbRate(p->function.reverbRate); updatePOLYMODE((int)p->function.mode); updatePORTAMODE((int)p->function.portamento); updatePORTATIME((int)p->function.portamentoTime); for(int k=0; keg[k]); envelopeGraph[k] ->env2Points(_eg->ar, _eg->d1r, _eg->d1l, _eg->d2r, _eg->rr); envelopeGraph[k]->updateEnv(); updateAR(k, _eg->ar); updateD1R(k, _eg->d1r); updateD1L(k, _eg->d1l); updateD2R(k, _eg->d2r); updateRR(k, _eg->rr); //scaling updateLS(k, p->scaling.level[k]); updateRS(k, p->scaling.rate[k]); //Volume updateOUT(k, p->outLevel[k]); //Ratio and Frequency updateRATIO(k, (int)(100*p->frequency[k].ratio)); updateFIXRANGE(k, (int)(100*p->frequency[k].freq)); updateFIX(k, p->frequency[k].isFix); //Sensitivity updateAME(k, p->sensitivity.ampOn[k]); updateEBS(k, p->sensitivity.egBias[k]); updateKVS(k, p->sensitivity.keyVelocity[k]); //detune updateDET(k, p->detune[k]); //Waveform updateOSW(k, (int)p->oscWave[0]); } } /*void DeicsOnzeGui::updateCurrentChannel() { updateBrightness(_deicsOnze->_global.channel[_currentChannel].brightness); updateModulation(_deicsOnze->_global.channel[_currentChannel].modulation); updateChannelDetune(_deicsOnze->_global.channel[_currentChannel].detune); updateAttack(_deicsOnze->_global.channel[_currentChannel].attack); updateRelease(_deicsOnze->_global.channel[_currentChannel].release); }*/ void DeicsOnzeGui::updatePreset() { updatePreset(_deicsOnze->_preset[_currentChannel]); } void DeicsOnzeGui::updateSelectPreset(int hbank, int lbank, int prog) { //QTreeWidgetItem* cat = categoryListView->currentItem(); //QTreeWidgetItem* sub = subcategoryListView->currentItem(); //QTreeWidgetItem* pre = presetListView->currentItem(); //select category, subcategory, preset //category QList qlcat = categoryListView->findItems(num3Digits(hbank+1), Qt::MatchExactly); QTreeWidgetItem* qcat = qlcat.empty()? NULL:qlcat.at(0); //if the category is different than the last one then select the new one //if(!cat || !qcat || qcat!= cat) { if(qcat) { qcat->setSelected(true); categoryListView->setCurrentItem(qcat); categoryListView->scrollToItem(qcat); setEnabledPreset(true); } else { updateCategoryName(QString("NONE"), false); updateHBank(hbank, false); categoryListView->clearSelection(); subcategoryListView->clear(); setEnabledPreset(false); } //} //subcategory //if(cat) { QList qlsub = subcategoryListView->findItems(num3Digits(lbank+1), Qt::MatchExactly); QTreeWidgetItem* qsub = qlsub.empty()? NULL:qlsub.at(0); // if(!sub || qsub!=sub) { if(qsub) { qsub->setSelected(true); subcategoryListView->setCurrentItem(qsub); subcategoryListView->scrollToItem(qsub); setEnabledPreset(true); } else { updateSubcategoryName(QString("NONE"), false); updateLBank(lbank, false); subcategoryListView->clearSelection(); presetListView->clear(); setEnabledPreset(false); } // } //} //else { // updateSubcategoryName(QString("NONE"), false); // updateLBank(lbank, false); // subcategoryListView->clearSelection(); // presetListView->clear(); // setEnabledPreset(false); //} //preset //if(sub) { QList qlpre = presetListView->findItems(num3Digits(prog+1), Qt::MatchExactly); QTreeWidgetItem* qpre = qlpre.empty()? NULL:qlpre.at(0); if(qpre) { presetListView->blockSignals(true); qpre->setSelected(true); presetListView->setCurrentItem(qpre); presetListView->blockSignals(false); presetListView->scrollToItem(qpre); updatePresetName(qpre->text(1), true); updateProg(prog, true); //pre=(QTreePreset*) qpre; setEnabledPreset(true); } else { updatePresetName(QString("INITVOICE"), false); updateProg(prog, false); presetListView->clearSelection(); setEnabledPreset(false); } //} //else { // updatePresetName(QString("INITVOICE"), false); // updateProg(prog, false); // presetListView->clearSelection(); // setEnabledPreset(false); //} } muse-4.0.0/src/synti/deicsonze/deicsonzegui.h000066400000000000000000000427521404105311000212440ustar00rootroot00000000000000//=========================================================================== // // DeicsOnze an emulator of the YAMAHA DX11 synthesizer // // Version 0.5.5 // // deicsonzegui.h // // // Copyright (c) 2004-2006 Nil Geisweiller // // // // 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; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA // 02111-1301, USA or point your web browser to http://www.gnu.org. //=========================================================================== #ifndef __DEICSONZEGUI_H #define __DEICSONZEGUI_H #include "awl/slider.h" using Awl::Slider; #include "deicsonze.h" #include "deicsonzepreset.h" #include "ui_deicsonzegui.h" #include "libsynti/gui.h" #include "awl/floatentry.h" ///#include "awl/slider.h" #include "awl/checkbox.h" using Awl::FloatEntry; ///using Awl::Slider; using Awl::CheckBox; //Envelope Gui constants #define XOFFSET 2 #define YOFFSET 2 #define PENWIDTH 2 #define DOTWIDTH 3 #define DRAGWIDTH 6 //size of the mousetracking threshold //pitch envelope constants #define WALLWIDTH 6 #define PR1WIDTH (width()/3-9) #define PR2WIDTH PR1WIDTH #define PR3WIDTH PR1WIDTH #define PL1HEIGHT (height()-4) #define PL2HEIGHT PL1HEIGHT #define PL3HEIGHT PL1HEIGHT #define MAXPWIDTH PR1WIDTH+WALLWIDTH+PR2WIDTH+WALLWIDTH+PR3WIDTH+WALLWIDTH+PR1WIDTH #define MAXPHEIGHT PL1HEIGHT #define STEPVALUE 10 //amplitude envelope constants #define ARWIDTH (width()/4-1) #define D1RWIDTH ARWIDTH #define D1LHEIGHT (height()-2) #define D2RWIDTH ARWIDTH #define RRWIDTH ARWIDTH #define MAXWIDTH ARWIDTH+D1RWIDTH+D1RWIDTH+RRWIDTH #define MAXHEIGHT D1LHEIGHT //COLOR #define TCOLOR QColor(0, 0, 0) //text color #define BCOLOR QColor(210, 180, 90) //background color #define ETCOLOR QColor(0, 150, 0) //edit text color #define EBCOLOR QColor(255, 255, 30) //edit background color class DeicsOnze; class QFramePitchEnvelope; class QFrameEnvelope; class QTreeCategory:public QTreeWidgetItem { public: Category* _category; QTreeCategory(QTreeWidget* p, QString shbank, QString l, Category* c) :QTreeWidgetItem(p) { setText(0, shbank); setText(1, l); _category=c; }; }; class QTreeSubcategory:public QTreeWidgetItem { public: Subcategory* _subcategory; QTreeSubcategory(QTreeWidget* p, QString slbank, QString l, Subcategory* s) :QTreeWidgetItem(p) { setText(0, slbank); setText(1, l); _subcategory=s; }; }; class QTreePreset:public QTreeWidgetItem { public: Preset* _preset; QTreePreset(QTreeWidget* pa, QString sprog, QString l, Preset* p) :QTreeWidgetItem(pa) { setText(0, sprog); setText(1, l); _preset=p; }; }; //--------------------------------------------------------- // DeicsOnzeGui //--------------------------------------------------------- class DeicsOnzeGui : public QDialog, public Ui::DeicsOnzeGuiBase, public MessGui { Q_OBJECT bool _enabledPreset; QFramePitchEnvelope* pitchEnvelopeGraph; QFrameEnvelope* envelopeGraph[NBROP]; QWidget* _chorusSuperWidget; QWidget* _reverbSuperWidget; std::vector _chorusSliderVector; std::vector _chorusFloatEntryVector; std::vector _chorusCheckBoxVector; std::vector _reverbSliderVector; std::vector _reverbFloatEntryVector; std::vector _reverbCheckBoxVector; QString lastDir; private slots: void readMessage(); void setEnabledChannel(bool); void setChangeChannel(int); void setPanic(); void setResCtrl(); void setNbrVoices(int); void setSaveOnlyUsed(bool); void setSaveOnlyUsedComp(bool); void setSaveConfig(bool); // void setMidiInCh(int); //to change void setQuality(const QString&); void setFilter(bool); void setFontSize(int); void saveConfiguration(); void saveDefaultConfiguration(); void loadConfiguration(); void loadConfiguration(QString s); //load init set void setIsInitSet(bool); void setInitSetPath(const QString&); void setBrowseInitSetPath(); //load init set void setIsBackgroundPix(bool); void setBackgroundPixPath(const QString&); void setBrowseBackgroundPixPath(); //FX void setChorusActiv(bool a); void setChannelChorus(int c); void setChorusReturn(int al); void setSelectChorusPlugin(); void setReverbCheckBox(double v, int i); void setChorusCheckBox(double v, int i); void setReverbActiv(bool a); void setChannelReverb(int r); void setReverbReturn(int val); void setSelectReverbPlugin(); void setReverbFloatEntry(double v, int i); void setReverbSlider(double v, int i); void setChorusFloatEntry(double v, int i); void setChorusSlider(double v, int i); //quick edit void setChannelVolKnob(double val); void setChannelPan(double val); void setBrightnessKnob(double val); void setModulationKnob(double val); void setDetuneKnob(double val); void setAttackKnob(double val); void setReleaseKnob(double val); //Color void setRedColor(int); void setGreenColor(int); void setBlueColor(int); void setRGBSliders(QListWidgetItem*); void setTextColor(const QColor &); void setBackgroundColor(const QColor &); void setEditTextColor(const QColor &); void setEditBackgroundColor(const QColor &); //New Delete Load Save void deleteSetDialog(); void loadSetDialog(); void saveSetDialog(); void deleteCategoryDialog(); void newCategoryDialog(); void loadCategoryDialog(); void saveCategoryDialog(); void deleteSubcategoryDialog(); void newSubcategoryDialog(); void loadSubcategoryDialog(); void saveSubcategoryDialog(); void newPresetDialog(); void loadPresetDialog(); void deletePresetDialog(); void savePresetDialog(); //popupMenu void categoryPopupMenu(const QPoint&); void subcategoryPopupMenu(const QPoint&); void presetPopupMenu(const QPoint&); //Preset and bank void setPresetName(const QString&); void setSubcategoryName(const QString&); void setCategoryName(const QString&); void setHBank(int); void setLBank(int); void setProg(int); //Global void setMasterVolKnob(double); void setMasterVol(int); void setFeedback(int); void setLfoWave(int); void setLfoSpeed(int); void setLfoDelay(int); void setLfoPModDepth(int); void setLfoPitchSens(int); void setLfoAModDepth(int); void setLfoAmpSens(int); void setTranspose(int); void setChannelDetune(int); void setAlgorithm(int); void setPitchBendRange(int); //Pitch Envelope void setPL1(int); void setPL2(int); void setPL3(int); void setPR1(int); void setPR2(int); void setPR3(int); //Function void setFcVolume(int); void setFcPitch(int); void setFcAmplitude(int); void setMwPitch(int); void setMwAmplitude(int); void setBcPitch(int); void setBcAmplitude(int); void setBcPitchBias(int); void setBcEgBias(int); void setAtPitch(int); void setAtAmplitude(int); void setAtPitchBias(int); void setAtEgBias(int); void setReverbRate(int); void setPolyMode(int); void setPortFingerFull(int); void setPortaTime(int); //envelope void setAR1(int val); void setD1R1(int val); void setD1L1(int val); void setD2R1(int val); void setRR1(int val); void setAR2(int val); void setD1R2(int val); void setD1L2(int val); void setD2R2(int val); void setRR2(int val); void setAR3(int val); void setD1R3(int val); void setD1L3(int val); void setD2R3(int val); void setRR3(int val); void setAR4(int val); void setD1R4(int val); void setD1L4(int val); void setD2R4(int val); void setRR4(int val); //scaling void setLS1(int val); void setRS1(int val); void setLS2(int val); void setRS2(int val); void setLS3(int val); void setRS3(int val); void setLS4(int val); void setRS4(int val); //vol void setVol1(int val); void setVol2(int val); void setVol3(int val); void setVol4(int val); //Ratio and Frequency void setCoarseRatio1(int val); void setFineRatio1(int val); void setFreq1(int val); void setFix1(bool f); void setCoarseRatio2(int val); void setFineRatio2(int val); void setFreq2(int val); void setFix2(bool f); void setCoarseRatio3(int val); void setFineRatio3(int val); void setFreq3(int val); void setFix3(bool f); void setCoarseRatio4(int val); void setFineRatio4(int val); void setFreq4(int val); void setFix4(bool f); //Sensitivity void setAME1(bool val); void setEBS1(int val); void setKVS1(int val); void setAME2(bool val); void setEBS2(int val); void setKVS2(int val); void setAME3(bool val); void setEBS3(int val); void setKVS3(int val); void setAME4(bool val); void setEBS4(int val); void setKVS4(int val); //detune void setDET1(int val); void setDET2(int val); void setDET3(int val); void setDET4(int val); //WaveForm void setWaveForm1(int); void setWaveForm2(int); void setWaveForm3(int); void setWaveForm4(int); //Delay void setActivDelay(bool); void setDelayReturn(int); void setChannelDelay(int); //void setDelayTime(int); void setDelayBPM(double); void setDelayBeatRatio(double); //void setDelayFeedback(int); void setDelayFeedback(double); //void setDelayPanLFOFreq(int); void setDelayPanLFOFreq(double); //void setDelayPanLFODepth(int); void setDelayPanLFODepth(double); //category subcategory preset void setSet(void); //display the set, that is the category list void setCategory(Category*); void setCategory(QTreeWidgetItem*); void setSubcategory(Subcategory*); void setSubcategory(QTreeWidgetItem*); void setPreset(QTreeWidgetItem*); public: virtual void processEvent(const MusECore::MidiPlayEvent&); void updateSelectPreset(int hbank, int lbank, int prog); //update the gui void setEnabledPreset(bool b); void updateChannelCheckBox(bool b); void updateEnabledChannel(bool e);//put enabled the display concerning channel and preset void updateChannelEnable(bool e);//update channel enable void updateMasterVolume(int val); void updateNbrVoices(int val); //void updateMidiInCh(int val); //to change void updateQuality(int val); void updateFilter(bool f); void updateFontSize(int fs); void applyFontSize(int fs); void updateSaveOnlyUsed(bool); void updateSaveConfig(bool); //FX void updateChorusActiv(bool a); void updateChannelChorus(int c); void updateChorusReturn(int r); void updateReverbActiv(bool a); void updateChannelReverb(int r); void updateReverbReturn(int r); void updateLadspaReverbLineEdit(QString s); void updateLadspaChorusLineEdit(QString s); void updateDelayActiv(bool a); void updateChannelDelay(int r); void updateDelayReturn(int r); void updateDelayPanLFOFreq(float plf); void updateDelayBPM(float dt); void updateDelayBeatRatio(float dt); void updateDelayFeedback(float df); void updateDelayPanLFODepth(float dpd); void addPluginCheckBox(int index, QString text, bool toggled, QWidget* parent, QGridLayout* grid, bool isReverb); void addPluginIntSlider(int index, QString text, double min, double max, double val, QWidget* parent, QGridLayout* grid, bool isReverb); void addPluginSlider(int index, QString text, bool isLog, double min, double max, double val, QWidget* parent, QGridLayout* grid, bool isReverb); void buildGuiReverb(); void buildGuiChorus(); void updateReverbSlider(double v, int i); void updateReverbFloatEntry(double v, int i); void updateChorusSlider(double v, int i); void updateChorusFloatEntry(double v, int i); //update load init set void updateInitSetCheckBox(bool); void updateInitSetPath(QString); //update background pix void updateBackgroundPixCheckBox(bool); void updateBackgroundPixPath(QString); void applyBackgroundPix(); //update quick edit void updateChannelPan(int val); void updateBrightness(int val); void updateModulation(int val); void updateAttack(int val); void updateRelease(int val); void updateQuickEdit(); //update pitch envelope void updatePL1(int val); void updatePL2(int val); void updatePL3(int val); void updatePR1(int val); void updatePR2(int val); void updatePR3(int val); //update function void updateFcVolume(int val); void updateFcPitch(int val); void updateFcAmplitude(int val); void updateMwPitch(int val); void updateMwAmplitude(int val); void updateBcPitch(int val); void updateBcAmplitude(int val); void updateBcPitchBias(int val); void updateBcEgBias(int val); void updateAtPitch(int val); void updateAtAmplitude(int val); void updateAtPitchBias(int val); void updateAtEgBias(int val); //void updateReverbRate(int val); //update envelope void updateAR(int op, int val); void updateD1R(int op, int val); void updateD2R(int op, int val); void updateRR(int op, int val); void updateD1L(int op, int val); //update scale void updateLS(int op, int val); void updateRS(int op, int val); void updateEBS(int op, int val); void updateAME(int op, bool val); void updateKVS(int op, int val); void updateOUT(int op, int val); void updateRATIO(int op, int val); void updateDET(int op, int val); //update global void updateALG(int val); void updateFEEDBACK(int val); void updateSPEED(int val); void updateDELAY(int val); void updatePMODDEPTH(int val); void updateAMODDEPTH(int val); void updateSYNC(bool val); void updateWAVE(int val); void updatePMODSENS(int val); void updateAMS(int val); void updateTRANSPOSE(int val); void updatePOLYMODE(int val); void updatePBENDRANGE(int val); void updatePORTAMODE(int val); void updatePORTATIME(int val); void updateFIX(int op, bool val); void updateFIXRANGE(int op, int val); void updateOSW(int op, int val); void updateSHFT(int op, int val); void updateChannelDetune(int val); void updateChannelDetuneKnob(int val); //void updateChannelDetuneSlider(int val); void updateChannelVolume(int val); void updateCategoryName(QString cn, bool enable); void updateSubcategoryName(QString sn, bool enable); void updatePresetName(QString pn, bool enable); void updatePresetName(QString pn); void updateHBank(int hbank, bool enable); void updateLBank(int lbank, bool enable); void updateProg(int prog, bool enable); void updatePreset(Preset* p); void updatePreset(void); //update gui following the current preset //void updateCurrentChannel(); //update gui channel attributes QString num3Digits(int); DeicsOnzeGui(DeicsOnze*); int _currentChannel; QColor* tColor; //text color QColor* bColor; //background color QColor* etColor;//edit text color QColor* ebColor;//edit background color QColor* curColor;//current color DeicsOnze* _deicsOnze; }; class QFramePitchEnvelope:private QFrame { QPoint startlinkP1, //first point P1linkP2, //point linking P1 to P2 P2linkP3, //point linking P2 to P3 P3linkEnd; //point linking P3 to End bool isStartlinkP1Edit; bool isP1linkP2Edit; bool isP2linkP3Edit; bool isP3linkEndEdit; public: DeicsOnzeGui* _deicsOnzeGui; QFramePitchEnvelope(QWidget* parent, DeicsOnzeGui* dog):QFrame(parent){ _deicsOnzeGui = dog; isStartlinkP1Edit=false; isP1linkP2Edit=false; isP2linkP3Edit=false; isP3linkEndEdit=false; }; void env2Points(int pl1, int pl2, int pl3, int pr1, int pr2, int pr3); void updateEnv(void) {update();}; protected: virtual void paintEvent(QPaintEvent* e); virtual void mouseMoveEvent(QMouseEvent* e); virtual void mousePressEvent(QMouseEvent * e); virtual void mouseReleaseEvent(QMouseEvent * e); }; class QFrameEnvelope:private QFrame { unsigned char op; //operator number, between 0 and 3 QPoint startlinkAR, //first point ARlinkD1, //point linking AR to D1 D1linkD2, //point linking D1 to D2 D2linkRR, //point linking D2 to RR RRlinkEnd; //last point bool isARlinkD1Edit; bool isD1linkD2Edit; bool isD2linkRREdit; bool isRRlinkEndEdit; public: DeicsOnzeGui* _deicsOnzeGui; QFrameEnvelope(QWidget* parent, DeicsOnzeGui* dog, unsigned char k):QFrame(parent){ _deicsOnzeGui = dog; isARlinkD1Edit=false; isD1linkD2Edit=false; isD2linkRREdit=false; isRRlinkEndEdit=false; op = k; //setGeometry(XOFFSET, YOFFSET, XOFFSET+MAXWIDTH, MAXHEIGHT); //setMouseTracking(true); }; void env2Points(int ar, int d1r, int d1l, int d2r, int rr); void updateEnv(void) {update();}; protected: void paintEvent(QPaintEvent* e); void mouseMoveEvent(QMouseEvent* e); void mousePressEvent(QMouseEvent * e); void mouseReleaseEvent(QMouseEvent * e); }; #endif /* __DEICSONZEGUI_H */ muse-4.0.0/src/synti/deicsonze/deicsonzegui.ui000066400000000000000000010577441404105311000214420ustar00rootroot00000000000000 Nil Geisweiller DeicsOnzeGuiBase 0 0 728 568 DeicsOnze false false 9 6 0 0 ArrowCursor false QTabWidget::Rounded 0 &Preset 9 6 false Program numerous 1 128 false INITVOICE 12 Qt::CustomContextMenu false LBank Subcategory false Bank numerous 1 128 false NONE 12 false NONE 12 false Bank numerous 1 128 Qt::CustomContextMenu Qt::LeftToRight false QFrame::StyledPanel QFrame::Sunken 1 0 false true 2 HBank Category Qt::CustomContextMenu false Prog Preset Sans Serif 8 50 false false false false QFrame::NoFrame QFrame::Plain DeicsOnze v0.5.5 Copyright (c) 2004-2006 Nil Geisweiller. Published under GPL licence. &Global 9 6 Pitch Envelope 10 6 QFrame::Box QFrame::Sunken PL3 QFrame::Box QFrame::Sunken PL2 QFrame::Box QFrame::Sunken PL1 QFrame::Box QFrame::Sunken PR1 QFrame::Box QFrame::Sunken PR2 QFrame::Box QFrame::Sunken PR3 PointingHandCursor true QFrame::StyledPanel QFrame::Sunken Pitch modulation depth 99 2 50 Pitch modulation depth 99 2 50 Pitch modulation depth 99 2 50 Pitch modulation depth 99 3 0 Pitch modulation depth 99 3 0 Pitch modulation depth 99 3 0 LFO 10 6 PointingHandCursor LFO Sync Pitch modulation sensitivity 7 PointingHandCursor Pitch Modulation Sensitivity 7 1 1 Qt::Horizontal PointingHandCursor LFO Delay 99 1 1 Qt::Horizontal LFO delay 99 LFO speed 99 Amplitude modulation depth 99 Pitch modulation depth 99 Amplitude modulation sensitivity 7 PointingHandCursor Amplitude Modulation Sensitivity 3 1 1 Qt::Horizontal QFrame::Box QFrame::Sunken AMS PointingHandCursor LFO Waveform 38 16 :/pics/sawUp.png:/pics/sawUp.png :/pics/square.png:/pics/square.png :/pics/triangl.png:/pics/triangl.png :/pics/SHold.png:/pics/SHold.png PointingHandCursor Pitch Modulation Depth 99 1 1 Qt::Horizontal QFrame::Box QFrame::Sunken PMD PointingHandCursor LFO Speed 99 1 1 Qt::Horizontal PointingHandCursor Pitch Modulation Depth 99 1 1 Qt::Horizontal QFrame::Box QFrame::Sunken AMD QFrame::Box QFrame::Sunken Speed QFrame::Box QFrame::Sunken Delay QFrame::Box QFrame::Sunken PMS true Modulation Matrix 10 6 PointingHandCursor Modulation Matrix <b>Algorithm 1</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 2</b> : <i>Op 1</i> modulated by <i>Op 2</i> modulated by both <i>Op 3</i> and <i>Op 4</i><br> <b>Algorithm 3</b> : <i>Op 1</i> modulated by both <i>Op 4</i> and <i>Op 2</i> modulated by <i>Op 3</i><br> <b>Algorithm 4</b> : <i>Op 1</i> modulated by both <i>Op 2</i> and <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 5</b> : (<i>Op 1</i> modulated by <i>Op 2</i>) add to (<i>Op 3</i> modulated by <i>Op 4</i>) <br> <b>Algorithm 6</b> : addition of the three <i>Op 1, 2, 3</i> all modulated by <i>Op 4</i><br> <b>Algorithm 7</b> : addition of the three <i>Op 1, 2, 3</i> with <i>Op 3</i> modulated by <i>Op 4</i><br> <b>Algorithm 8</b> : addition of the four <i>Op 1, 2, 3, 4</i> 160 32 :/pics/algo1.png:/pics/algo1.png :/pics/algo3.png:/pics/algo3.png :/pics/algo3.png:/pics/algo3.png :/pics/algo4.png:/pics/algo4.png :/pics/algo5.png:/pics/algo5.png :/pics/algo6.png:/pics/algo6.png :/pics/algo7.png:/pics/algo7.png :/pics/algo8.png:/pics/algo8.png Op4 Feedback 6 10 PointingHandCursor Feedback level of the operator 4 7 1 1 Qt::Horizontal Feedback level of the operator 4 7 Transpose 6 10 PointingHandCursor Transpose -24 24 1 Qt::Horizontal Transpose -24 99 Op &1 9 6 Scaling 1 10 6 QFrame::Box QFrame::Sunken LS1 QFrame::Box QFrame::Sunken RS1 PointingHandCursor Rate Scaling Attack Rate of the operator 1 3 1 1 0 Qt::Vertical PointingHandCursor Level Scaling Attack Rate of the operator 1 99 1 1 0 Qt::Vertical Level Scaling 99 Rate Scaling 3 Amplitude Envelope 1 10 6 QFrame::Box QFrame::Sunken RR1 QFrame::Box QFrame::Sunken D1R1 QFrame::Box QFrame::Sunken D1L1 QFrame::Box QFrame::Sunken D2R1 Release Rate 15 2° Decay Rate 31 1° Decay Level 15 1° Decay Rate 31 Attack Rate 31 QFrame::Box QFrame::Sunken AR1 0 0 PointingHandCursor true QFrame::StyledPanel QFrame::Sunken Detune, OSCWave, EGShift 1 10 6 QFrame::Box QFrame::Sunken DET1 Detune -3 3 PointingHandCursor Detune Attack Rate of the operator 1 -3 3 1 1 0 Qt::Horizontal false PointingHandCursor EG Shift 96dB 48dB 24dB 12dB PointingHandCursor Wave form Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> 48 16 :/pics/wave1.png:/pics/wave1.png :/pics/wave2.png:/pics/wave2.png :/pics/wave3.png:/pics/wave3.png :/pics/wave4.png:/pics/wave4.png :/pics/wave5.png:/pics/wave5.png :/pics/wave6.png:/pics/wave6.png :/pics/wave7.png:/pics/wave7.png :/pics/wave8.png:/pics/wave8.png Sensitivity 1 10 6 QFrame::Box QFrame::Sunken KVS1 PointingHandCursor Amplitude Modulation Enable AME1 Keyboard Velocity Sensitivity 7 PointingHandCursor false Key Velocity Sensitivity 7 1 1 Qt::Horizontal QFrame::Box QFrame::Sunken EBS1 PointingHandCursor EG Bias Sensitivity 7 1 1 Qt::Horizontal Eg Bias Sensitivity 7 Frequency 1 6 10 QFrame::Box QFrame::Sunken Coarse 1 Coarse Ratio QFrame::Box QFrame::Sunken Fine 1 Fine Ratio QFrame::Box QFrame::Sunken Freq 1 false Fixed Frequency 32000 true PointingHandCursor Toggle Fix Frequency FIX OUT 1 6 10 Output Volume 99 PointingHandCursor Volume 99 1 1 0 Qt::Vertical Op &2 9 6 Amplitude Envelope 2 10 6 Attack Rate 31 QFrame::Box QFrame::Sunken D1R2 QFrame::Box QFrame::Sunken D1L2 QFrame::Box QFrame::Sunken D2R2 QFrame::Box QFrame::Sunken RR2 QFrame::Box QFrame::Sunken AR2 Release Rate 15 2° Decay Rate 31 1° Decay Level 15 1° Decay Rate 31 0 0 PointingHandCursor true QFrame::StyledPanel QFrame::Sunken Frequency 2 6 10 QFrame::Box QFrame::Sunken Coarse 2 Coarse Ratio QFrame::Box QFrame::Sunken Fine 2 Fine Ratio QFrame::Box QFrame::Sunken Freq 2 false Fixed Frequency 32000 true PointingHandCursor Toggle Fix Frequency FIX Scaling 2 10 6 QFrame::Box QFrame::Sunken LS2 QFrame::Box QFrame::Sunken RS2 PointingHandCursor Level Scaling Attack Rate of the operator 1 99 1 0 Qt::Vertical 1 PointingHandCursor Rate Scaling Attack Rate of the operator 1 3 1 0 Qt::Vertical Level Scaling 99 Rate Scaling 3 OUT 2 6 10 Output Volume 99 PointingHandCursor Volume 99 1 0 Qt::Vertical Detune, OSCWave, EGShift 2 10 6 QFrame::Box QFrame::Sunken DET2 Detune -3 3 PointingHandCursor Detune Attack Rate of the operator 1 -3 3 1 0 Qt::Horizontal false PointingHandCursor EG Shift 96dB 48dB 24dB 12dB PointingHandCursor Wave form Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> 48 16 :/pics/wave1.png:/pics/wave1.png :/pics/wave2.png:/pics/wave2.png :/pics/wave3.png:/pics/wave3.png :/pics/wave4.png:/pics/wave4.png :/pics/wave5.png:/pics/wave5.png :/pics/wave6.png:/pics/wave6.png :/pics/wave7.png:/pics/wave7.png :/pics/wave8.png:/pics/wave8.png Sensitivity 2 10 6 QFrame::Box QFrame::Sunken EBS2 QFrame::Box QFrame::Sunken KVS2 PointingHandCursor Amplitude Modulation Enable AME2 PointingHandCursor false Key Velocity Sensitivity 7 1 Qt::Horizontal Keyboard Velocity Sensitivity 7 PointingHandCursor EG Bias Sensitivity 7 1 Qt::Horizontal Eg Bias Sensitivity 7 Op &3 9 6 Amplitude Envelope 3 10 6 Attack Rate 31 QFrame::Box QFrame::Sunken D1R3 QFrame::Box QFrame::Sunken D1L3 QFrame::Box QFrame::Sunken D2R3 QFrame::Box QFrame::Sunken RR3 QFrame::Box QFrame::Sunken AR3 Release Rate 15 2° Decay Rate 31 1° Decay Level 15 1° Decay Rate 31 0 0 PointingHandCursor true QFrame::StyledPanel QFrame::Sunken Scaling 3 10 6 QFrame::Box QFrame::Sunken LS3 QFrame::Box QFrame::Sunken RS3 PointingHandCursor Level Scaling Attack Rate of the operator 1 99 1 0 Qt::Vertical PointingHandCursor Rate Scaling Attack Rate of the operator 1 3 1 0 Qt::Vertical Level Scaling 99 Rate Scaling 3 OUT 3 6 10 Output Volume 99 PointingHandCursor Volume 99 1 0 Qt::Vertical Frequency 3 6 10 QFrame::Box QFrame::Sunken Coarse 3 ArrowCursor Coarse Ratio QFrame::Box QFrame::Sunken Fine 3 ArrowCursor Fine Ratio QFrame::Box QFrame::Sunken Freq 3 false Fixed Frequency 32000 true PointingHandCursor Toggle Fix Frequency FIX Detune, OSCWave, EGShift 3 10 6 QFrame::Box QFrame::Sunken DET3 Detune -3 3 PointingHandCursor Detune Attack Rate of the operator 1 -3 3 1 0 Qt::Horizontal false PointingHandCursor EG Shift 96dB 48dB 24dB 12dB PointingHandCursor Wave form Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> 48 16 :/pics/wave1.png:/pics/wave1.png :/pics/wave2.png:/pics/wave2.png :/pics/wave3.png:/pics/wave3.png :/pics/wave4.png:/pics/wave4.png :/pics/wave5.png:/pics/wave5.png :/pics/wave6.png:/pics/wave6.png :/pics/wave7.png:/pics/wave7.png :/pics/wave8.png:/pics/wave8.png Sensitivity 3 10 6 QFrame::Box QFrame::Sunken EBS3 QFrame::Box QFrame::Sunken KVS3 PointingHandCursor Amplitude Modulation Enable AME3 PointingHandCursor false Key Velocity Sensitivity 7 1 Qt::Horizontal Keyboard Velocity Sensitivity 7 PointingHandCursor EG Bias Sensitivity 7 1 Qt::Horizontal Eg Bias Sensitivity 7 Op &4 9 6 amplitude Envelope 4 10 6 Attack Rate 31 QFrame::Box QFrame::Sunken AR4 QFrame::Box QFrame::Sunken D1R4 QFrame::Box QFrame::Sunken D1L4 QFrame::Box QFrame::Sunken D2R4 QFrame::Box QFrame::Sunken RR4 Release Rate 15 2° Decay Rate 31 1° Decay Level 15 1° Decay Rate 31 0 0 PointingHandCursor true QFrame::StyledPanel QFrame::Sunken Frequency 4 6 10 QFrame::Box QFrame::Sunken Coarse 4 Coarse Ratio QFrame::Box QFrame::Sunken Fine 4 Fine Ratio QFrame::Box QFrame::Sunken Freq 4 false Fixed Frequency 32000 true PointingHandCursor Toggle Fix Frequency FIX Scaling 4 10 6 QFrame::Box QFrame::Sunken LS4 QFrame::Box QFrame::Sunken RS4 PointingHandCursor Level Scaling Attack Rate of the operator 1 99 1 0 Qt::Vertical PointingHandCursor Rate Scaling Attack Rate of the operator 1 3 1 0 Qt::Vertical Level Scaling 99 Rate Scaling 3 OUT 4 6 10 Output Volume 99 PointingHandCursor Volume 99 1 0 Qt::Vertical Detune, OSCWave, EGShift 4 10 6 QFrame::Box QFrame::Sunken DET4 Detune -3 3 PointingHandCursor Detune Attack Rate of the operator 1 -3 3 1 0 Qt::Horizontal false PointingHandCursor EG Shift 96dB 48dB 24dB 12dB PointingHandCursor Wave form Wave form 1 = <i>sin(<b>t</b>)</i><br> Wave form 2 = <i>sin(<b>t</b>)*abs(sin(<b>t</b>))</i><br> Wave form 3 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>) else 0</i><br> Wave form 4 = <i>if <b>t</b>&#060 pi then sin(<b>t</b>)*abs(sin(<b>t</b>)) else 0</i><br> Wave form 5 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>) else 0</i><br> Wave form 6 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 7 = <i>if <b>t</b>&#060 pi then abs(sin(2*<b>t</b>)) else 0</i><br> Wave form 8 = <i>if <b>t</b>&#060 pi then sin(2*<b>t</b>)*sin(2*<b>t</b>) else 0</i> 48 16 :/pics/wave1.png:/pics/wave1.png :/pics/wave2.png:/pics/wave2.png :/pics/wave3.png:/pics/wave3.png :/pics/wave4.png:/pics/wave4.png :/pics/wave5.png:/pics/wave5.png :/pics/wave6.png:/pics/wave6.png :/pics/wave7.png:/pics/wave7.png :/pics/wave8.png:/pics/wave8.png Sensitivity 4 10 6 QFrame::Box QFrame::Sunken EBS4 QFrame::Box QFrame::Sunken KVS4 PointingHandCursor Amplitude Modulation Enable AME4 PointingHandCursor false Key Velocity Sensitivity 7 1 Qt::Horizontal Keyboard Velocity Sensitivity 7 PointingHandCursor EG Bias Sensitivity 7 1 Qt::Horizontal Eg Bias Sensitivity 7 &Func 9 6 16777215 80 Delay Pan Depth 9 6 48 16777215 16777215 80 Delay Pan LFO Freq 9 6 1.137891684385393 0.100000000000000 10.000000000000000 true 48 16777215 0.100000000000000 10.000000000000000 16777215 80 Delay Ch Send Level 9 6 127 PointingHandCursor Channel Chorus 127 Qt::Horizontal 16777215 80 Delay Feedback 9 6 0.000000000000000 -1.000000000000000 48 16777215 -1.000000000000000 16777215 80 Delay On/Off, Return Level 9 6 PointingHandCursor On 255 128 Qt::Horizontal 16777215 80 Delay Beat Ratio 9 6 1.000000000000000 0.125000000000000 2.000000000000000 48 16777215 0.010000000000000 2.000000000000000 16777215 80 Delay BPM 9 6 100.000000000000000 60.000000000000000 255.000000000000000 48 16777215 6 60.000000000000000 255.000000000000000 0 Foot Control 9 6 Pitch Bend Range 99 Pitch Bend Range 99 Pitch Bend Range 99 QFrame::Box QFrame::Sunken Pitch PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal QFrame::Box QFrame::Sunken Amplitude QFrame::Box QFrame::Sunken Volume PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal Modulation Wheel 9 6 Pitch Bend Range 99 PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal Pitch Bend Range 99 PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal QFrame::Box QFrame::Sunken Amplitude QFrame::Box QFrame::Sunken Pitch Breath Control 9 6 PointingHandCursor Pitch Bend Range 0 50 1 0 Qt::Horizontal Pitch Bend Range 50 Pitch Bend Range 99 Pitch Bend Range 99 Pitch Bend Range 99 QFrame::Box QFrame::Sunken Pitch QFrame::Box QFrame::Sunken Pitch Bias QFrame::Box QFrame::Sunken Envelope Bias QFrame::Box QFrame::Sunken Amplitude PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal After Touch 9 6 QFrame::Box QFrame::Sunken Pitch Pitch Bend Range 99 PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal Pitch Bend Range 50 PointingHandCursor Pitch Bend Range 0 50 1 0 Qt::Horizontal Pitch Bend Range 99 Pitch Bend Range 99 QFrame::Box QFrame::Sunken Pitch Bias QFrame::Box QFrame::Sunken Amplitude QFrame::Box QFrame::Sunken Envelope Bias PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal PointingHandCursor Pitch Bend Range 0 99 1 0 Qt::Horizontal Phony Mode 6 10 true PointingHandCursor POLY MONO Pitch Bend Range 6 10 PointingHandCursor Pitch Bend Range 0 12 1 0 Qt::Horizontal Pitch Bend Range 12 Potamento 6 10 true PointingHandCursor Portamento Mode FINGER FULL QFrame::Box QFrame::Sunken PT true PointingHandCursor Portamento Time 0 99 1 0 Qt::Horizontal true Portamento Time 99 C&horus 9 6 Chorus Parameters 9 6 16777215 80 Channel send level 9 6 127 PointingHandCursor Channel Chorus 127 Qt::Horizontal 16777215 80 On/Off and Return level 9 6 PointingHandCursor On 255 128 Qt::Horizontal 16777215 80 Select LADSPA plugin 9 6 Change plugin true &Reverb 9 6 16777215 80 On/Off and Return level 9 6 PointingHandCursor On 255 128 Qt::Horizontal 16777215 80 Select LADSPA plugin 9 6 Change plugin true 16777215 80 Channel send level 9 6 127 PointingHandCursor Channel Chorus 127 Qt::Horizontal Reverb Parameters &Config 9 6 Font Size 9 6 1 32 9 Quality 6 9 true High Middle Low Ultra low true Filter Save Mode (into the song) 6 10 PointingHandCursor Save only the used presets true PointingHandCursor Save the entire set PointingHandCursor Save the configuration true Configuration File 9 6 true Save... true Load... Save as default Colors 9 6 0 0 0 20 true QFrame::StyledPanel QFrame::Sunken Text Background Edit Text Edit Background Red 255 PointingHandCursor Blue 255 Qt::Horizontal PointingHandCursor Green 255 Qt::Horizontal PointingHandCursor Red Qt::LeftToRight 255 Qt::Horizontal QSlider::NoTicks Blue 255 Green 255 QFrame::Box QFrame::Sunken Blue QFrame::Box QFrame::Sunken Green QFrame::Box QFrame::Sunken Red Set Path 9 6 Image in the background : false 128 false Browse... 128 Browse... Load the set at the initialization : true 1000 20 0 0 0 207 207 207 237 237 237 231 231 231 121 125 121 166 166 166 0 0 0 255 255 255 255 0 0 255 255 255 207 207 207 0 0 0 0 0 0 255 255 255 0 0 255 255 0 255 231 231 231 0 0 0 207 207 207 237 237 237 231 231 231 121 125 121 166 166 166 0 0 0 255 255 255 255 0 0 255 255 255 207 207 207 0 0 0 0 0 0 255 255 255 0 0 255 255 0 255 231 231 231 121 125 121 207 207 207 237 237 237 231 231 231 121 125 121 166 166 166 121 125 121 255 255 255 121 125 121 207 207 207 207 207 207 0 0 0 121 125 121 207 207 207 0 0 255 255 0 255 231 231 231 Sans Serif 10 75 false true false false Set Brightness, Detune, Attack and Release of the current channel to default Res. Ctrl 1000 20 0 0 0 207 207 207 237 237 237 231 231 231 121 125 121 166 166 166 0 0 0 255 255 255 255 0 0 255 255 255 207 207 207 0 0 0 0 0 0 255 255 255 0 0 255 255 0 255 231 231 231 0 0 0 207 207 207 237 237 237 231 231 231 121 125 121 166 166 166 0 0 0 255 255 255 255 0 0 255 255 255 207 207 207 0 0 0 0 0 0 255 255 255 0 0 255 255 0 255 231 231 231 121 125 121 207 207 207 237 237 237 231 231 231 121 125 121 166 166 166 121 125 121 255 255 255 121 125 121 207 207 207 207 207 207 0 0 0 121 125 121 207 207 207 0 0 255 255 0 255 231 231 231 Sans Serif 10 75 false true false false Cut all notes off Panic! 9 6 true Number of Voices 1 64 8 QFrame::StyledPanel Number of voices Qt::AlignCenter Enable true 1 16 QFrame::StyledPanel Channel Qt::AlignCenter QFrame::StyledPanel Vol Qt::AlignCenter 40 40 -28.144332654385916 0.000000000000000 1.000000000000000 0.100000000000000 0.200000000000000 false 96 16 Channel Ctrl 9 6 PointingHandCursor 0.000000000000000 1.000000000000000 0.100000000000000 0.200000000000000 false QFrame::StyledPanel Release Qt::AlignCenter 32 32 PointingHandCursor QFrame::StyledPanel Attack Qt::AlignCenter 32 32 PointingHandCursor QFrame::StyledPanel Detune Qt::AlignCenter 32 32 PointingHandCursor QFrame::StyledPanel Brightness Qt::AlignCenter 32 32 PointingHandCursor QFrame::StyledPanel Modulation Qt::AlignCenter 32 32 PointingHandCursor QFrame::StyledPanel Pan Qt::AlignCenter QFrame::StyledPanel Volume Qt::AlignCenter 32 32 PointingHandCursor 1.000000000000000 0.100000000000000 0.200000000000000 270 6 Awl::Knob QWidget